From 9eb21cf6c7fd0e0a056403b74436514b5ce60460 Mon Sep 17 00:00:00 2001
From: thedevnull <daniellage18@gmail.com>
Date: Tue, 11 Jan 2011 23:54:37 -0200
Subject: [PATCH] bots
 Signed-off-by: thedevnull <daniellage18@gmail.com>

---
 sql/Bots/CMakeLists.txt                            |    4 +
 sql/Bots/characters_bots.sql                       |    6 +
 sql/Bots/world_bots.sql                            |  245 ++
 sql/Bots/world_bots_update.sql                     |   13 +
 sql/CMakeLists.txt                                 |    1 +
 src/server/game/AI/Bots/PlayerbotAI.cpp            | 4498 ++++++++++++++++++++
 src/server/game/AI/Bots/PlayerbotAI.h              |  284 ++
 src/server/game/AI/Bots/PlayerbotClassAI.cpp       |  592 +++
 src/server/game/AI/Bots/PlayerbotClassAI.h         |  162 +
 src/server/game/AI/Bots/PlayerbotDeathKnightAI.cpp |  325 ++
 src/server/game/AI/Bots/PlayerbotDeathKnightAI.h   |   60 +
 src/server/game/AI/Bots/PlayerbotDruidAI.cpp       |  674 +++
 src/server/game/AI/Bots/PlayerbotDruidAI.h         |   70 +
 src/server/game/AI/Bots/PlayerbotHunterAI.cpp      |  561 +++
 src/server/game/AI/Bots/PlayerbotHunterAI.h        |   68 +
 src/server/game/AI/Bots/PlayerbotMageAI.cpp        |  384 ++
 src/server/game/AI/Bots/PlayerbotMageAI.h          |   69 +
 src/server/game/AI/Bots/PlayerbotPaladinAI.cpp     |  535 +++
 src/server/game/AI/Bots/PlayerbotPaladinAI.h       |   70 +
 src/server/game/AI/Bots/PlayerbotPriestAI.cpp      |  400 ++
 src/server/game/AI/Bots/PlayerbotPriestAI.h        |   59 +
 src/server/game/AI/Bots/PlayerbotRogueAI.cpp       |  266 ++
 src/server/game/AI/Bots/PlayerbotRogueAI.h         |   41 +
 src/server/game/AI/Bots/PlayerbotShamanAI.cpp      |  555 +++
 src/server/game/AI/Bots/PlayerbotShamanAI.h        |   75 +
 src/server/game/AI/Bots/PlayerbotWarlockAI.cpp     |  409 ++
 src/server/game/AI/Bots/PlayerbotWarlockAI.h       |   55 +
 src/server/game/AI/Bots/PlayerbotWarriorAI.cpp     |  400 ++
 src/server/game/AI/Bots/PlayerbotWarriorAI.h       |   57 +
 src/server/game/AI/CoreAI/PetAI.cpp                |    9 +
 src/server/game/CMakeLists.txt                     |    2 +
 src/server/game/Chat/Chat.cpp                      |    3 +
 src/server/game/Chat/Chat.h                        |    6 +
 src/server/game/Chat/Commands/Level0.cpp           |  181 +
 src/server/game/Chat/Commands/Level1.cpp           |    6 +-
 src/server/game/Entities/Creature/Creature.cpp     |    3 +
 src/server/game/Entities/Creature/Creature.h       |   15 +
 src/server/game/Entities/Creature/GossipDef.h      |    1 +
 .../game/Entities/Creature/TemporarySummon.cpp     |    8 +
 src/server/game/Entities/Object/Object.cpp         |   27 +-
 src/server/game/Entities/Player/Player.cpp         | 1279 ++++++-
 src/server/game/Entities/Player/Player.h           |  113 +-
 src/server/game/Entities/Unit/Unit.cpp             |   11 +
 src/server/game/Groups/Group.cpp                   |  150 +-
 src/server/game/Groups/Group.h                     |   19 +
 src/server/game/Maps/Map.cpp                       |   21 +-
 src/server/game/Quests/QuestDef.h                  |    4 +-
 src/server/game/Scripting/ScriptLoader.cpp         |   23 +
 src/server/game/Scripting/ScriptMgr.cpp            |   20 +-
 src/server/game/Scripting/ScriptMgr.h              |    3 +
 .../Server/Protocol/Handlers/CharacterHandler.cpp  |  178 +
 .../game/Server/Protocol/Handlers/ChatHandler.cpp  |   33 +-
 .../game/Server/Protocol/Handlers/GroupHandler.cpp |   33 +-
 .../game/Server/Protocol/Handlers/NPCHandler.cpp   |    6 +-
 .../game/Server/Protocol/Handlers/QuestHandler.cpp |    1 +
 src/server/game/Server/WorldSession.cpp            |  120 +-
 src/server/game/Server/WorldSession.h              |   13 +
 src/server/game/Weather/Weather.cpp                |    2 +-
 src/server/game/World/World.cpp                    |    2 +
 src/server/game/World/World.h                      |    1 +
 src/server/scripts/Bots/CMakeLists.txt             |   26 +
 src/server/scripts/Bots/bot_ai.cpp                 |  566 +++
 src/server/scripts/Bots/bot_ai.h                   |   88 +
 src/server/scripts/Bots/bot_druid_ai.cpp           |  563 +++
 src/server/scripts/Bots/bot_druid_ai.h             |   63 +
 src/server/scripts/Bots/bot_hunter_ai.cpp          |  303 ++
 src/server/scripts/Bots/bot_hunter_ai.h            |   26 +
 src/server/scripts/Bots/bot_mage_ai.cpp            |  524 +++
 src/server/scripts/Bots/bot_mage_ai.h              |  124 +
 src/server/scripts/Bots/bot_paladin_ai.cpp         |  276 ++
 src/server/scripts/Bots/bot_paladin_ai.h           |   62 +
 src/server/scripts/Bots/bot_priest_ai.cpp          |  276 ++
 src/server/scripts/Bots/bot_priest_ai.h            |   33 +
 src/server/scripts/Bots/bot_rogue_ai.cpp           |  313 ++
 src/server/scripts/Bots/bot_rogue_ai.h             |   30 +
 src/server/scripts/Bots/bot_shaman_ai.cpp          |  385 ++
 src/server/scripts/Bots/bot_shaman_ai.h            |   48 +
 src/server/scripts/Bots/bot_warlock_ai.cpp         |  292 ++
 src/server/scripts/Bots/bot_warlock_ai.h           |   41 +
 src/server/scripts/Bots/bot_warrior_ai.cpp         |  501 +++
 src/server/scripts/Bots/bot_warrior_ai.h           |   87 +
 src/server/scripts/Bots/script_bot_giver.cpp       |  234 +
 src/server/scripts/CMakeLists.txt                  |    1 +
 src/server/shared/Common.h                         |    4 +
 src/server/worldserver/worldserver.conf.dist       |   36 +
 85 files changed, 18093 insertions(+), 40 deletions(-)
 create mode 100644 sql/Bots/CMakeLists.txt
 create mode 100644 sql/Bots/characters_bots.sql
 create mode 100644 sql/Bots/world_bots.sql
 create mode 100644 sql/Bots/world_bots_update.sql
 create mode 100644 src/server/game/AI/Bots/PlayerbotAI.cpp
 create mode 100644 src/server/game/AI/Bots/PlayerbotAI.h
 create mode 100644 src/server/game/AI/Bots/PlayerbotClassAI.cpp
 create mode 100644 src/server/game/AI/Bots/PlayerbotClassAI.h
 create mode 100644 src/server/game/AI/Bots/PlayerbotDeathKnightAI.cpp
 create mode 100644 src/server/game/AI/Bots/PlayerbotDeathKnightAI.h
 create mode 100644 src/server/game/AI/Bots/PlayerbotDruidAI.cpp
 create mode 100644 src/server/game/AI/Bots/PlayerbotDruidAI.h
 create mode 100644 src/server/game/AI/Bots/PlayerbotHunterAI.cpp
 create mode 100644 src/server/game/AI/Bots/PlayerbotHunterAI.h
 create mode 100644 src/server/game/AI/Bots/PlayerbotMageAI.cpp
 create mode 100644 src/server/game/AI/Bots/PlayerbotMageAI.h
 create mode 100644 src/server/game/AI/Bots/PlayerbotPaladinAI.cpp
 create mode 100644 src/server/game/AI/Bots/PlayerbotPaladinAI.h
 create mode 100644 src/server/game/AI/Bots/PlayerbotPriestAI.cpp
 create mode 100644 src/server/game/AI/Bots/PlayerbotPriestAI.h
 create mode 100644 src/server/game/AI/Bots/PlayerbotRogueAI.cpp
 create mode 100644 src/server/game/AI/Bots/PlayerbotRogueAI.h
 create mode 100644 src/server/game/AI/Bots/PlayerbotShamanAI.cpp
 create mode 100644 src/server/game/AI/Bots/PlayerbotShamanAI.h
 create mode 100644 src/server/game/AI/Bots/PlayerbotWarlockAI.cpp
 create mode 100644 src/server/game/AI/Bots/PlayerbotWarlockAI.h
 create mode 100644 src/server/game/AI/Bots/PlayerbotWarriorAI.cpp
 create mode 100644 src/server/game/AI/Bots/PlayerbotWarriorAI.h
 create mode 100644 src/server/scripts/Bots/CMakeLists.txt
 create mode 100644 src/server/scripts/Bots/bot_ai.cpp
 create mode 100644 src/server/scripts/Bots/bot_ai.h
 create mode 100644 src/server/scripts/Bots/bot_druid_ai.cpp
 create mode 100644 src/server/scripts/Bots/bot_druid_ai.h
 create mode 100644 src/server/scripts/Bots/bot_hunter_ai.cpp
 create mode 100644 src/server/scripts/Bots/bot_hunter_ai.h
 create mode 100644 src/server/scripts/Bots/bot_mage_ai.cpp
 create mode 100644 src/server/scripts/Bots/bot_mage_ai.h
 create mode 100644 src/server/scripts/Bots/bot_paladin_ai.cpp
 create mode 100644 src/server/scripts/Bots/bot_paladin_ai.h
 create mode 100644 src/server/scripts/Bots/bot_priest_ai.cpp
 create mode 100644 src/server/scripts/Bots/bot_priest_ai.h
 create mode 100644 src/server/scripts/Bots/bot_rogue_ai.cpp
 create mode 100644 src/server/scripts/Bots/bot_rogue_ai.h
 create mode 100644 src/server/scripts/Bots/bot_shaman_ai.cpp
 create mode 100644 src/server/scripts/Bots/bot_shaman_ai.h
 create mode 100644 src/server/scripts/Bots/bot_warlock_ai.cpp
 create mode 100644 src/server/scripts/Bots/bot_warlock_ai.h
 create mode 100644 src/server/scripts/Bots/bot_warrior_ai.cpp
 create mode 100644 src/server/scripts/Bots/bot_warrior_ai.h
 create mode 100644 src/server/scripts/Bots/script_bot_giver.cpp

diff --git a/sql/Bots/CMakeLists.txt b/sql/Bots/CMakeLists.txt
new file mode 100644
index 0000000..a615f58
--- /dev/null
+++ b/sql/Bots/CMakeLists.txt
@@ -0,0 +1,4 @@
+INSTALL(FILES
+world_bots.sql
+characters_bots.sql
+DESTINATION share/trinity/sql/Bots)
diff --git a/sql/Bots/characters_bots.sql b/sql/Bots/characters_bots.sql
new file mode 100644
index 0000000..5277449
--- /dev/null
+++ b/sql/Bots/characters_bots.sql
@@ -0,0 +1,6 @@
+CREATE TABLE IF NOT EXISTS `character_npcbot` (
+  `owner` int(11) default NULL,
+  `entry` int(11) default NULL,
+  `race` tinyint(4) default NULL,
+  `class` tinyint(4) default NULL
+)  ENGINE=InnoDB DEFAULT CHARSET=utf8;
diff --git a/sql/Bots/world_bots.sql b/sql/Bots/world_bots.sql
new file mode 100644
index 0000000..da400b1
--- /dev/null
+++ b/sql/Bots/world_bots.sql
@@ -0,0 +1,245 @@
+
+-- Add command as level 0
+DELETE FROM command where name='bot';
+INSERT INTO command (name, security, help) VALUES ('bot',0,'Syntax: .bot $subcommand $Name');
+
+delete from `creature_template` where entry >= 60000 && entry < 60239;
+
+insert  into `creature_template`(`entry`,`difficulty_entry_1`,`difficulty_entry_2`,`difficulty_entry_3`,`KillCredit1`,`KillCredit2`,`modelid1`,`modelid2`,`modelid3`,`modelid4`,`name`,`subname`,`IconName`,`gossip_menu_id`,`minlevel`,`maxlevel`,`exp`,`faction_A`,`faction_H`,`npcflag`,`speed_walk`,`speed_run`,`scale`,`rank`,`mindmg`,`maxdmg`,`dmgschool`,`attackpower`,`dmg_multiplier`,`baseattacktime`,`rangeattacktime`,`unit_class`,`unit_flags`,`dynamicflags`,`family`,`trainer_type`,`trainer_spell`,`trainer_class`,`trainer_race`,`minrangedmg`,`maxrangedmg`,`rangedattackpower`,`type`,`type_flags`,`lootid`,`pickpocketloot`,`skinloot`,`resistance1`,`resistance2`,`resistance3`,`resistance4`,`resistance5`,`resistance6`,`spell1`,`spell2`,`spell3`,`spell4`,`spell5`,`spell6`,`spell7`,`spell8`,`PetSpellDataId`,`VehicleId`,`mingold`,`maxgold`,`AIName`,`MovementType`,`InhabitType`,`Health_mod`,`Mana_mod`,`Armor_mod`,`RacialLeader`,`questItem1`,`questItem2`,`questItem3`,`questItem4`,`questItem5`,`questItem6`,`movementId`,`RegenHealth`,`equipment_id`,`mechanic_immune_mask`,`flags_extra`,`ScriptName`,`WDBVerified`) values
+(60000,0,0,0,0,0,169,0,169,0,'Recruitment Officer','','',0,80,80,0,35,35,1,1.4,1.14286,1,0,228,298,0,1837,1,2000,0,1,0,0,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,300,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'',0,3,1,1,1,0,0,0,0,0,0,0,0,1,0,0,0,'script_bot_giver',0),
+(60001,0,0,0,0,0,5001,0,5001,0,'Khelden','Mage Bot','',0,80,80,2,12,12,1,0.93,1.14286,1,0,2,5,0,23,1,1500,0,8,4608,0,0,0,0,8,1,8.624,11.858,100,7,1,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,0,0,0,0,0,0,0,0,0,127,838,0,'mage_bot',0),
+(60002,0,0,0,0,0,1294,0,1294,0,'Zaldimar','Mage Bot','',0,80,80,2,12,12,1,0.98,1.14286,1,0,5,10,0,54,1,1500,0,8,4608,0,0,0,0,8,1,21.5072,29.5724,100,7,1,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,0,0,0,0,0,0,0,0,0,127,85,0,'mage_bot',0),
+(60003,0,0,0,0,0,1484,0,1484,0,'Maginor','Mage Bot','',0,80,80,2,12,12,1,1.26,1.14286,1,0,42,88,0,451,1,1500,0,8,4608,0,0,0,0,8,1,74.448,102.366,100,7,1,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,0,0,0,0,0,0,0,0,0,127,96,0,'mage_bot',0),
+(60004,0,0,0,0,0,3344,0,3344,0,'Anetta','Priest Bot','',0,80,80,2,12,12,1,0.93,1.14286,1,0,2,5,0,23,1,1500,0,8,4608,0,0,0,0,5,1,8.624,11.858,100,7,1,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,0,0,0,0,0,0,0,0,0,127,838,0,'priest_bot',0),
+(60005,0,0,0,0,0,1495,0,1495,0,'Laurena','Priest Bot','',0,80,80,2,12,12,1,1.26,1.14286,1,0,42,88,0,451,1,1500,0,8,4608,0,0,0,0,5,1,74.448,102.366,100,7,1,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,0,0,0,0,0,0,0,0,0,127,94,0,'priest_bot',0),
+(60006,0,0,0,0,0,1295,0,1295,0,'Josetta','Priest Bot','',0,80,80,2,12,12,1,0.98,1.14286,1,0,5,10,0,54,1,1500,0,8,4608,0,0,0,0,5,1,21.5072,29.5724,100,7,1,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,0,0,0,0,0,0,0,0,0,127,85,0,'priest_bot',0),
+(60007,0,0,0,0,0,3345,0,3345,0,'Drusilla','Warlock Bot','',0,80,80,2,12,12,1,0.93,1.14286,1,0,2,5,0,23,1,1500,0,8,4608,0,0,0,0,9,1,8.624,11.858,100,7,1,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,0,0,0,0,0,0,0,0,0,1491,838,0,'warlock_bot',0),
+(60008,0,0,0,0,0,1930,0,1930,0,'Alamar','Warlock Bot','',0,80,80,2,875,875,1,1.07,1.14286,1,0,2,5,0,23,1,1960,2156,8,4608,0,0,0,0,9,7,8.624,11.858,100,7,1,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,0,0,0,0,0,0,0,0,0,1491,838,0,'warlock_bot',0),
+(60009,0,0,0,0,0,1469,0,1469,0,'Demisette','Warlock Bot','',0,80,80,2,12,12,1,1.26,1.14286,1,0,42,88,0,451,1,1500,0,8,4608,0,0,0,0,9,1,74.448,102.366,100,7,1,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,0,0,0,0,0,0,0,0,0,1491,110,0,'warlock_bot',0),
+(60010,0,0,0,0,0,12749,0,12749,0,'Nalesette','Hunter Bot','',0,80,80,2,80,80,1,1.1,1.14286,1,0,23,48,0,247,1,1500,1500,2,4608,0,0,3,0,3,4,56.672,77.924,100,7,1,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,0,0,0,0,0,0,0,0,0,1186,319,0,'hunter_bot',0),
+(60011,0,0,0,0,0,3401,0,3401,0,'Branstock','Priest Bot','',0,80,80,2,55,55,1,0.93,1.14286,1,0,2,5,0,23,1,1960,2156,8,4608,0,0,0,0,5,3,8.624,11.858,100,7,1,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,0,0,0,0,0,0,0,0,0,127,838,0,'priest_bot',0),
+(60012,0,0,0,0,0,3395,0,3395,0,'Thorgas','Hunter Bot','',0,80,80,2,55,55,1,0.93,1.14286,1,0,2,5,0,23,1,1960,2156,2,4608,0,0,0,0,3,3,8.624,11.858,100,7,1,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,0,0,0,0,0,0,0,0,0,1186,10,0,'hunter_bot',0),
+(60013,0,0,0,0,0,3343,0,3343,0,'Llane','Warrior Bot','',0,80,80,2,12,12,1,0.93,1.14286,1,0,2,65,0,364,1,1500,0,1,4608,0,0,0,0,1,1,8.624,11.858,100,7,1,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,0,0,0,0,0,0,0,0,0,1362,702,0,'warrior_bot',0),
+(60014,0,0,0,0,0,3399,0,3399,0,'Thran','Warrior Bot','',0,80,80,2,55,55,1,0.93,1.14286,1,0,2,55,0,267,1,1960,2156,1,4608,0,0,0,0,1,3,8.624,11.858,100,7,1,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,0,0,0,0,0,0,0,0,0,1022,702,0,'warrior_bot',0),
+(60015,0,0,0,0,0,1300,0,1300,0,'Lyria','Warrior Bot','',0,80,80,2,12,12,1,0.97,1.14286,1,0,5,90,0,543,1,1500,0,1,4608,0,0,0,0,1,1,19.9584,27.4428,100,7,1,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,0,0,0,0,0,0,0,0,0,110,647,0,'warrior_bot',0),
+(60016,0,0,0,0,0,3351,0,3351,0,'Jorik','Rogue Bot','',0,80,80,2,12,12,1,0.93,1.14286,1,0,2,75,0,1489,1,1500,0,4,4608,0,0,0,0,4,1,8.624,11.858,100,7,1,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,0,0,0,0,0,0,0,0,0,1204,397,0,'rogue_bot',0),
+(60017,0,0,0,0,0,3407,0,3407,0,'Solm','Rogue Bot','',0,80,80,2,55,55,1,0.93,1.14286,1,0,2,74,0,1489,1,1960,2156,4,4608,0,0,0,0,4,3,8.624,11.858,100,7,1,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,0,0,0,0,0,0,0,0,0,1204,397,0,'rogue_bot',0),
+(60018,0,0,0,0,0,1297,0,1297,0,'Keryn','Rogue Bot','',0,80,80,2,12,12,1,0.97,1.14286,1,0,5,53,0,1489,1,2000,0,4,4608,0,0,0,0,4,1,18.392,25.289,100,7,1,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,0,0,0,0,0,0,0,0,0,1206,730,0,'rogue_bot',0),
+(60019,0,0,0,0,0,1507,0,1507,0,'Osborne','Rogue Bot','',0,80,80,2,12,12,1,1.26,1.14286,1,0,42,88,0,1489,1,1500,0,4,4608,0,0,0,0,4,1,74.448,102.366,100,7,1,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,0,0,0,0,0,0,0,0,0,1204,392,0,'rogue_bot',0),
+(60020,0,0,0,0,0,3346,0,3346,0,'Sammuel','Paladin Bot','',0,80,80,2,12,12,1,0.93,1.14286,1,0,2,5,0,23,1,1500,0,2,4608,0,0,0,0,2,1,8.624,11.858,100,7,1,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,0,0,0,0,0,0,0,0,0,204,429,0,'paladin_bot',0),
+(60021,0,0,0,0,0,3393,0,3393,0,'Bob','Paladin Bot','',0,80,80,2,55,55,1,0.93,1.14286,1,0,2,5,0,23,1,1960,2156,2,4608,0,0,0,0,2,3,8.624,11.858,100,7,1,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,0,0,0,0,0,0,0,0,0,205,429,0,'paladin_bot',0),
+(60022,0,0,0,0,0,1299,0,1299,0,'Wilhelm','Paladin Bot','',0,80,80,2,12,12,1,0.97,1.14286,1,0,5,9,0,45,1,1500,0,2,4608,0,0,0,0,2,1,18.392,25.289,100,7,1,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,0,0,0,0,0,0,0,0,0,204,426,0,'paladin_bot',0),
+(60023,0,0,0,0,0,1499,0,1499,0,'Brisombre','Paladin Bot','',0,80,80,2,12,12,1,1.26,1.14286,1,0,42,88,0,451,1,1500,0,2,4608,0,0,0,0,2,1,74.448,102.366,100,7,1,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,0,0,0,0,0,0,0,0,0,205,105,0,'paladin_bot',0),
+(60024,0,0,0,0,0,10216,0,10216,0,'Marry','Mage Bot','',0,80,80,2,875,875,1,1.07,1.14286,1,0,2,5,0,23,1,1960,2156,8,4608,0,0,0,0,8,7,8.624,11.858,100,7,1,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,3,0,0,0,0,0,0,0,0,127,838,0,'mage_bot',0),
+(60025,0,0,0,0,0,4552,0,4552,0,'Haromm','Shaman Bot','',0,80,80,2,29,29,1,1.05,1.14286,1,0,32,200,0,345,1,2000,0,2,4608,0,0,0,0,7,2,66.44,91.355,100,7,1,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,0,0,0,0,0,0,0,0,0,93,1691,0,'shaman_bot',0),
+(60026,0,0,0,0,0,4567,0,4567,0,'Kartosh','Warlock Bot','',0,80,80,2,29,29,1,1.05,1.14286,1,0,32,67,0,345,1,2000,0,8,4608,0,0,0,0,9,2,66.44,91.355,100,7,1,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,0,0,0,0,0,0,0,0,0,1491,110,0,'warlock_bot',0),
+(60027,0,0,0,0,0,3429,0,3429,0,'MaxanAnvol','Priest Bot','',0,80,80,2,55,55,1,0.96,1.14286,1,0,3,9,0,42,1,1500,0,8,4608,0,0,0,0,5,3,16.808,23.111,100,7,1,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,0,0,0,0,0,0,0,0,0,127,1315,0,'priest_bot',0),
+(60028,0,0,0,0,0,10215,0,10215,0,'Magis','Mage Bot','',0,80,80,2,875,875,1,1.1,1.14286,1,0,3,9,0,42,1,1500,0,8,4608,0,0,0,0,8,7,16.808,23.111,100,7,1,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,0,0,0,0,0,0,0,0,0,127,0,0,'mage_bot',0),
+(60029,0,0,0,0,0,3431,0,3431,0,'GranVivehache','Warrior Bot','',0,80,80,2,55,55,1,0.96,1.14286,1,0,3,75,0,422,1,1500,0,1,4608,0,0,0,0,1,3,16.808,23.111,100,7,1,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,0,0,0,0,0,0,0,0,0,394,1309,0,'warrior_bot',0),
+(60030,0,0,0,0,0,1622,0,1622,0,'Azar','Paladin Bot','',0,80,80,2,55,55,1,1.26,1.14286,1,0,42,88,0,451,1,2000,0,2,4608,0,0,0,0,2,3,74.448,102.366,100,7,1,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,0,0,0,0,0,0,0,0,0,204,105,0,'paladin_bot',0),
+(60031,0,0,0,0,0,3436,0,3436,0,'Hogral','Rogue Bot','',0,80,80,2,55,55,1,0.97,1.14286,1,0,5,56,0,1489,1,2000,0,4,4608,0,0,0,0,4,3,19.9584,27.4428,100,7,1,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,0,0,0,0,0,0,0,0,0,1204,397,0,'rogue_bot',0),
+(60032,0,0,0,0,0,3053,0,3053,0,'Kelstrum','Warrior Bot','',0,80,80,2,55,55,1,1.17,1.14286,1,0,27,57,0,294,1,1500,0,1,4608,0,0,0,0,1,3,61.776,84.942,100,7,1,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,0,0,0,0,0,0,0,0,0,1022,1853,0,'warrior_bot',0),
+(60033,0,0,0,0,0,1578,0,1578,0,'Dannal','Warrior Bot','',0,80,80,2,68,68,1,0.9,1.14286,1,0,2,67,0,364,1,2000,0,1,4608,0,0,0,0,1,5,8.624,11.858,100,7,1,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,0,0,0,0,0,0,0,0,0,1362,639,0,'warrior_bot',0),
+(60034,0,0,0,0,0,1579,0,1579,0,'SombreDuesten','Priest Bot','',0,80,80,2,68,68,1,0.9,1.14286,1,0,2,5,0,23,1,2000,0,8,4608,0,0,0,0,5,5,8.624,11.858,100,7,1,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,0,0,0,0,0,0,0,0,0,127,604,0,'priest_bot',0),
+(60035,0,0,0,0,0,1592,0,1592,0,'Isabella','Mage Bot','',0,80,80,2,68,68,1,0.9,1.14286,1,0,2,5,0,23,1,2000,0,8,4608,0,0,0,0,8,5,8.624,11.858,100,7,1,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,0,0,0,0,0,0,0,0,0,127,1022,0,'mage_bot',0),
+(60036,0,0,0,0,0,1581,0,1581,0,'Maximillion','Warlock Bot','',0,80,80,2,68,68,1,0.9,1.14286,1,0,2,5,0,23,1,2000,0,8,4608,0,0,0,0,9,5,8.624,11.858,100,7,1,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,0,0,0,0,0,0,0,0,0,1491,143,0,'warlock_bot',0),
+(60037,0,0,0,0,0,1604,0,1604,0,'Rupert','Warlock Bot','',0,80,80,2,68,68,1,1.1,1.14286,1,0,6,13,0,65,1,2000,0,8,4608,0,0,0,0,9,5,24.552,33.759,100,7,1,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,0,0,0,0,0,0,0,0,0,1491,1022,0,'warlock_bot',0),
+(60038,0,0,0,0,0,1600,0,1600,0,'Cain','Mage Bot','',0,80,80,2,68,68,1,1.1,1.14286,1,0,6,12,0,59,1,2000,0,8,4608,0,0,0,0,8,5,23.0384,31.6778,100,7,1,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,0,0,0,0,0,0,0,0,0,127,173,0,'mage_bot',0),
+(60039,0,0,0,0,0,1602,0,1602,0,'SombreBeryl','Priest Bot','',0,80,80,2,68,68,1,1.1,1.14286,1,0,7,14,0,75,1,2000,0,8,4608,0,0,0,0,5,5,27.5264,37.8488,100,7,1,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,0,0,0,0,0,0,0,0,0,127,604,0,'priest_bot',0),
+(60041,0,0,0,0,0,10548,0,10548,0,'Milituus','Mage Bot','',0,80,80,2,55,55,1,1.35,1.14286,1,0,27,57,0,294,1,2000,0,8,4608,0,0,0,0,8,3,61.776,84.942,100,7,1,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,0,0,0,0,0,0,0,0,0,127,96,0,'mage_bot',0),
+(60042,0,0,0,0,0,2810,0,2810,0,'Lexington','Mage Bot','',0,80,80,2,68,68,1,1.1,1.14286,1,0,19,40,0,205,1,2000,0,8,4608,0,0,0,0,8,5,51.128,70.301,100,7,1,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,0,0,0,0,0,0,0,0,0,127,96,0,'mage_bot',0),
+(60043,0,0,0,0,0,2123,0,2123,0,'Siln','Shaman Bot','',0,80,80,2,104,104,1,1.2,1.14286,1,0,32,200,0,345,1,2000,0,2,4608,0,0,0,0,7,6,66.44,91.355,100,7,1,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,0,0,0,0,0,0,0,0,0,93,586,0,'shaman_bot',0),
+(60044,0,0,0,0,0,19598,0,19598,0,'Umbrua','Shaman Bot','',0,80,80,2,1640,1640,1,1.125,1.14286,1,0,176,200,0,1235,1,2000,0,2,4608,0,0,0,0,7,11,0,0,100,7,1,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,0,0,0,0,0,0,0,0,0,93,1627,0,'shaman_bot',0),
+(60045,0,0,0,0,0,2102,0,2102,0,'Tigor','Shaman Bot','',0,80,80,2,104,104,1,1.2,1.14286,1,0,23,200,0,247,1,2000,0,2,4608,0,0,0,0,7,6,56.672,77.924,100,7,1,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,0,0,0,0,0,0,0,0,0,93,145,0,'shaman_bot',0),
+(60046,0,0,0,0,0,2082,0,2082,0,'Beram','Shaman Bot','',0,80,80,2,104,104,1,1.1,1.14286,1,0,42,200,0,451,1,2000,0,2,4608,0,0,0,0,7,6,74.448,102.366,100,7,1,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,0,0,0,0,0,0,0,0,0,93,157,0,'shaman_bot',0),
+(60047,0,0,0,0,0,2106,0,2106,0,'Turak','Druid Bot','',0,80,80,2,104,104,1,1.2,1.14286,1,0,32,200,0,345,1,2000,0,2,4608,0,0,0,0,11,6,66.44,91.355,100,7,1,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,0,0,0,0,0,0,0,0,0,93,1069,0,'druid_bot',0),
+(60048,0,0,0,0,0,2121,0,2121,0,'Sheal','Druid Bot','',0,80,80,2,104,104,1,1.1,1.14286,1,0,42,200,0,1034,1,2000,0,2,4608,0,0,0,0,11,6,74.448,102.366,100,7,1,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,0,0,0,0,0,0,0,0,0,93,1252,0,'druid_bot',0),
+(60049,0,0,0,0,0,2115,0,2115,0,'Kym','Druid Bot','',0,80,80,2,104,104,1,1.2,1.14286,1,0,123,248,0,1034,1,2000,0,2,4608,0,0,0,0,11,6,56.672,77.924,100,7,1,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,0,0,0,0,0,0,0,0,0,93,1034,0,'druid_bot',0),
+(60050,0,0,0,0,0,2112,0,2112,0,'Kary','Hunter Bot','',0,80,80,2,104,104,1,1.2,1.14286,1,0,32,67,0,345,1,2000,1200,2,4608,0,0,0,0,3,6,66.44,91.355,100,7,1,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,0,0,0,0,0,0,0,0,0,1186,920,0,'hunter_bot',0),
+(60051,0,0,0,0,0,2087,0,2087,0,'Holt','Hunter Bot','',0,80,80,2,104,104,1,1.1,1.14286,1,0,42,88,0,451,1,2000,1000,2,4608,0,0,0,0,3,6,74.448,102.366,100,7,1,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,0,0,0,0,0,0,0,0,0,1186,613,0,'hunter_bot',0),
+(60052,0,0,0,0,0,2105,0,2105,0,'Urek','Hunter Bot','',0,80,80,2,104,104,1,1.2,1.14286,1,0,23,48,0,247,1,2000,1300,2,4608,0,0,0,0,3,6,56.672,77.924,100,7,1,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,0,0,0,0,0,0,0,0,0,1186,6,0,'hunter_bot',0),
+(60053,0,0,0,0,0,2103,0,2103,0,'Torm','Warrior Bot','',0,80,80,2,104,104,1,1.2,1.14286,1,0,32,67,0,345,1,2000,0,1,4608,0,0,0,0,1,6,66.44,91.355,100,7,1,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,0,0,0,0,0,0,0,0,0,1362,916,0,'warrior_bot',0),
+(60054,0,0,0,0,0,2096,0,2096,0,'Sark','Warrior Bot','',0,80,80,2,104,104,1,1.1,1.14286,1,0,42,88,0,451,1,2000,0,1,4608,0,0,0,0,1,6,74.448,102.366,100,7,1,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,0,0,0,0,0,0,0,0,0,1022,133,0,'warrior_bot',0),
+(60055,0,0,0,0,0,17211,0,17211,0,'Kerra','Warrior Bot','',0,80,80,2,104,104,1,1.2,1.14286,1,0,23,48,0,247,1,2000,0,1,4608,0,0,0,0,1,6,56.672,77.924,100,7,1,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,0,0,0,0,0,0,0,0,0,491,18,0,'warrior_bot',0),
+(60056,0,0,0,0,0,2139,0,2139,0,'Miles Welsh','Priest Bot','',0,80,80,2,68,68,1,1.2,1.14286,1,0,23,48,0,247,1,2000,0,8,4608,0,0,0,0,5,5,56.672,77.924,100,7,1,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,0,0,0,0,0,0,0,0,0,127,40,0,'priest_bot',0),
+(60057,0,0,0,0,0,2138,0,2138,0,'Malakai','Priest Bot','',0,80,80,2,68,68,1,1.2,1.14286,1,0,32,67,0,345,1,2000,0,8,4608,0,0,0,0,5,5,66.44,91.355,100,7,1,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,0,0,0,0,0,0,0,0,0,127,65,0,'priest_bot',0),
+(60058,0,0,0,0,0,2137,0,2137,0,'Cobb','Priest Bot','',0,80,80,2,68,68,1,1.1,1.14286,1,0,42,88,0,451,1,2000,0,8,4608,0,0,0,0,5,5,74.448,102.366,100,7,1,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,0,0,0,0,0,0,0,0,0,127,1096,0,'priest_bot',0),
+(60059,0,0,0,0,0,2134,0,2134,0,'Shymm','Mage Bot','',0,80,80,2,68,68,1,1.1,1.14286,1,0,42,88,0,451,1,2000,0,8,4608,0,0,0,0,8,5,74.448,102.366,100,7,1,0,0,0,0,0,0,0,0,0,0,143,145,0,0,0,0,0,0,0,0,0,'0',0,3,1,1,1,0,0,0,0,0,0,0,0,0,127,108,0,'mage_bot',0),
+(60060,0,0,0,0,0,6058,0,6058,0,'Ursyn','Mage Bot','',0,80,80,2,68,68,1,1.2,1.14286,1,0,23,48,0,247,1,2000,0,8,4608,0,0,0,0,8,5,56.672,77.924,100,7,1,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,0,0,0,0,0,0,0,0,0,127,96,0,'mage_bot',0),
+(60061,0,0,0,0,0,2135,0,2135,0,'Thurston','Mage Bot','',0,80,80,2,68,68,1,1.2,1.14286,1,0,32,67,0,345,1,2000,0,8,4608,0,0,0,0,8,5,66.44,91.355,100,7,1,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,0,0,0,0,0,0,0,0,0,127,129,0,'mage_bot',0),
+(60062,0,0,0,0,0,3793,0,3793,0,'Harutt','Warrior Bot','',0,80,80,2,104,104,1,0.97,1.14286,1,0,40,99,0,637,1,2000,0,1,4608,0,0,0,0,1,6,16.808,23.111,100,7,1,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,0,0,0,0,0,0,0,0,0,394,1977,0,'warrior_bot',0),
+(60063,0,0,0,0,0,3819,0,3819,0,'Gart','Druid Bot','',0,80,80,2,104,104,1,0.97,1.14286,1,0,3,200,0,1034,1,2000,0,2,4608,0,0,0,0,11,6,15.2064,20.9088,100,7,8,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,0,0,0,0,0,0,0,0,0,93,1073,0,'druid_bot',0),
+(60064,0,0,0,0,0,3810,0,3810,0,'Lanka','Hunter Bot','',0,80,80,2,104,104,1,0.97,1.14286,1,0,5,9,0,45,1,2000,1460,2,4608,0,0,0,0,3,6,18.392,25.289,100,7,1,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,0,0,0,0,0,0,0,0,0,1186,138,0,'hunter_bot',0),
+(60065,0,0,0,0,0,10180,0,10180,0,'Meela','Shaman Bot','',0,80,80,2,104,104,1,0.97,1.14286,1,0,3,200,0,42,1,2000,0,2,4608,0,0,0,0,7,6,16.808,23.111,100,7,1,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,0,0,0,0,0,0,0,0,0,93,157,0,'shaman_bot',0),
+(60066,0,0,0,0,0,3794,0,3794,0,'Krang','Warrior Bot','',0,80,80,2,104,104,1,0.98,1.14286,1,0,40,68,0,575,1,2000,0,1,4608,0,0,0,0,1,6,23.0384,31.6778,100,7,1,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,0,0,0,0,0,0,0,0,0,1362,1977,0,'warrior_bot',0),
+(60067,0,0,0,0,0,10734,0,10734,0,'Gennia','Druid Bot','',0,80,80,2,104,104,1,0.98,1.14286,1,0,5,200,0,1034,1,2000,0,2,4608,0,0,0,0,11,6,19.9584,27.4428,100,7,1,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,0,0,0,0,0,0,0,0,0,93,1069,0,'druid_bot',0),
+(60068,0,0,0,0,0,3811,0,3811,0,'Yaw','Hunter Bot','',0,80,80,2,104,104,1,0.97,1.14286,1,0,5,9,0,45,1,2000,1200,2,4608,0,0,0,0,3,6,18.392,25.289,100,7,1,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,0,0,0,0,0,0,0,0,0,1186,127,0,'hunter_bot',0),
+(60069,0,0,0,0,0,3816,0,3816,0,'Narm','Shaman Bot','',0,80,80,2,104,104,1,0.98,1.14286,1,0,5,200,0,54,1,2000,0,2,4608,0,0,0,0,7,6,21.5072,29.5724,100,7,1,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,0,0,0,0,0,0,0,0,0,93,446,0,'shaman_bot',0),
+(60070,0,0,0,0,0,1880,0,1880,0,'Frang','Warrior Bot','',0,80,80,2,29,29,1,1.1,1.14286,1,0,30,99,0,670,1,2000,2090,1,4608,0,0,0,0,1,2,18.392,25.289,100,7,1,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,0,0,0,0,0,0,0,0,0,1022,1977,0,'warrior_bot',0),
+(60071,0,0,0,0,0,1882,0,1882,0,'Jenshan','Hunter Bot','',0,80,80,2,126,126,1,0.95,1.14286,1,0,2,7,0,33,1,2000,2123,2,4608,0,0,0,0,3,8,13.5872,18.6824,100,7,1,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,0,0,0,0,0,0,0,0,0,1186,193,0,'hunter_bot',0),
+(60072,0,0,0,0,0,1884,0,1884,0,'Nartok','Warlock Bot','',0,80,80,2,29,29,1,0.96,1.14286,1,0,3,9,0,38,1,2000,2112,8,4608,0,0,0,0,9,2,15.2064,20.9088,100,7,1,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,0,0,0,0,0,0,0,0,0,1491,1022,0,'warlock_bot',0),
+(60073,0,0,0,0,0,1878,0,1878,0,'Shikrik','Shaman Bot','',0,80,80,2,29,29,1,1.1,1.14286,1,0,3,200,0,42,1,2000,2101,2,4608,0,0,0,0,7,2,16.808,23.111,100,7,1,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,0,0,0,0,0,0,0,0,0,93,604,0,'shaman_bot',0),
+(60074,0,0,0,0,0,3743,0,3743,0,'Tarshaw','Warrior Bot','',0,80,80,2,29,29,1,1.1,1.14286,1,0,25,53,0,273,1,2000,1738,1,4608,0,0,0,0,1,2,59.7872,82.2074,100,7,1,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,0,0,0,0,0,0,0,0,0,1362,642,0,'warrior_bot',0),
+(60075,0,0,0,0,0,3744,0,3744,0,'Thotar','Hunter Bot','',0,80,80,2,29,29,1,1.1,1.14286,1,0,6,15,0,72,1,2000,2035,2,4608,0,0,0,0,3,2,26.048,35.816,100,7,1,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,0,0,0,0,0,0,0,0,0,1186,920,0,'hunter_bot',0),
+(60076,0,0,0,0,0,3745,0,3745,0,'Dhugru','Warlock Bot','',0,80,80,2,29,29,1,1.1,1.14286,1,0,21,43,0,220,1,2000,1804,8,4608,0,0,0,0,9,2,53.3984,73.4228,100,7,1,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,0,0,0,0,0,0,0,0,0,1491,838,0,'warlock_bot',0),
+(60077,0,0,0,0,0,3746,0,3746,0,'Swart','Shaman Bot','',0,80,80,2,29,29,1,1.1,1.14286,1,0,6,200,0,65,1,2000,2046,2,4608,0,0,0,0,7,2,24.552,33.759,100,7,1,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,0,0,0,0,0,0,0,0,0,93,861,0,'shaman_bot',0),
+(60078,0,0,0,0,0,1324,0,1324,0,'Groldar','Warlock Bot','',0,80,80,2,29,29,1,1.1,1.14286,1,0,32,67,0,345,1,2000,0,8,4608,0,0,0,0,9,2,66.44,91.355,100,7,1,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,0,0,0,0,0,0,0,0,0,1491,173,0,'warlock_bot',0),
+(60079,0,0,0,0,0,1325,0,1325,0,'Mirket','Warlock Bot','',0,80,80,2,29,29,1,1.1,1.14286,1,0,32,67,0,345,1,2000,0,8,4608,0,0,0,0,9,2,66.44,91.355,100,7,1,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,0,0,0,0,0,0,0,0,0,1491,173,0,'warlock_bot',0),
+(60080,0,0,0,0,0,1326,0,1326,0,'Zevrost','Warlock Bot','',0,80,80,2,29,29,1,1.1,1.14286,1,0,32,67,0,345,1,2000,0,8,4608,0,0,0,0,9,2,66.44,91.355,100,7,1,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,0,0,0,0,0,0,0,0,0,1491,173,0,'warlock_bot',0),
+(60081,0,0,0,0,0,1360,0,1360,0,'Kardris','Shaman Bot','',0,80,80,2,29,29,1,1.1,1.14286,1,0,42,200,0,451,1,2000,0,2,4608,0,0,0,0,7,2,74.448,102.366,100,7,1,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,0,0,0,0,0,0,0,0,0,93,586,0,'shaman_bot',0),
+(60082,0,0,0,0,0,1373,0,1373,0,'Ormak','Hunter Bot','',0,80,80,2,29,29,1,1.1,1.14286,1,0,42,88,0,451,1,2000,1551,2,4608,0,0,0,0,3,2,74.448,102.366,100,7,1,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,0,0,0,0,0,0,0,0,0,1186,8,0,'hunter_bot',0),
+(60083,0,0,0,0,0,1374,0,1374,0,'Grezz','Warrior Bot','',0,80,80,2,29,29,1,1.1,1.14286,1,0,42,88,0,451,1,2000,0,1,4608,0,0,0,0,1,2,74.448,102.366,100,7,1,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,0,0,0,0,0,0,0,0,0,110,1865,0,'warrior_bot',0),
+(60084,0,0,0,0,0,1375,0,1375,0,'Sorek','Warrior Bot','',0,80,80,2,29,29,1,1.1,1.14286,1,0,32,67,0,345,1,2000,0,1,4608,0,0,0,0,1,2,66.44,91.355,100,7,1,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,0,0,0,0,0,0,0,0,0,1362,725,0,'warrior_bot',0),
+(60085,0,0,0,0,0,4231,0,4231,0,'Siantsu','Shaman Bot','',0,80,80,2,29,29,1,1.1,1.14286,1,0,23,200,0,247,1,2000,0,2,4608,0,0,0,0,7,2,56.672,77.924,100,7,1,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,0,0,0,0,0,0,0,0,0,93,170,0,'shaman_bot',0),
+(60086,0,0,0,0,0,4239,0,4239,0,'Xorjuul','Hunter Bot','',0,80,80,2,29,29,1,1.1,1.14286,1,0,32,67,0,345,1,2000,1661,2,4608,0,0,0,0,3,2,66.44,91.355,100,7,1,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,0,0,0,0,0,0,0,0,0,1186,613,0,'hunter_bot',0),
+(60087,0,0,0,0,0,4241,0,4241,0,'Siandur','Hunter Bot','',0,80,80,2,29,29,1,1.1,1.14286,1,0,23,48,0,247,1,2000,1771,2,4608,0,0,0,0,3,2,56.672,77.924,100,7,1,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,0,0,0,0,0,0,0,0,0,1186,613,0,'hunter_bot',0),
+(60088,0,0,0,0,0,4242,0,4242,0,'Zelmak','Warrior Bot','',0,80,80,2,29,29,1,1.1,1.14286,1,0,23,48,0,247,1,2000,0,1,4608,0,0,0,0,1,2,56.672,77.924,100,7,1,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,0,0,0,0,0,0,0,0,0,1022,1867,0,'warrior_bot',0),
+(60089,0,0,0,0,0,7915,0,7915,0,'ClaudeErksine','Hunter Bot','',0,80,80,2,55,55,1,1.14,1.14286,1,0,23,48,0,247,1,1610,1771,2,4608,0,0,3,0,3,3,56.672,77.924,100,7,1,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,0,0,0,0,0,0,0,0,0,1186,0,0,'hunter_bot',0),
+(60090,0,0,0,0,0,1721,0,1721,0,'Alyissia','Warrior Bot','',0,80,80,2,80,80,1,0.97,1.14286,1,0,50,87,0,322,1,2000,0,1,4608,0,0,0,0,1,4,18.392,25.289,100,7,1,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,0,0,0,0,0,0,0,0,0,394,639,0,'warrior_bot',0),
+(60091,0,0,0,0,0,1725,0,1725,0,'FrahunMurmombre','Rogue Bot','',0,80,80,2,80,80,1,0.97,1.14286,1,0,5,43,0,1489,1,2000,0,4,4608,0,0,0,0,4,4,18.392,25.289,100,7,1,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,0,0,0,0,0,0,0,0,0,1206,220,0,'rogue_bot',0),
+(60092,0,0,0,0,0,1733,0,1733,0,'Shanda','Priest Bot','',0,80,80,2,80,80,1,0.96,1.14286,1,0,3,9,0,42,1,2000,0,8,4608,0,0,0,0,5,4,16.808,23.111,100,7,1,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,0,0,0,0,0,0,0,0,0,127,604,0,'priest_bot',0),
+(60093,0,0,0,0,0,1732,0,1732,0,'Mardant','Druid Bot','',0,80,80,2,80,80,1,0.97,1.14286,1,0,5,367,0,45,1,2000,0,2,4608,0,0,0,0,11,4,18.392,25.289,100,7,1,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,0,0,0,0,0,0,0,0,0,93,114,0,'druid_bot',0),
+(60094,0,0,0,0,0,1707,0,1707,0,'Kyra','Warrior Bot','',0,80,80,2,80,80,1,1.01,1.14286,1,0,25,87,0,422,1,2000,0,1,4608,0,0,0,0,1,4,30.4304,41.8418,100,7,1,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,0,0,0,0,0,0,0,0,0,1362,1913,0,'warrior_bot',0),
+(60095,0,0,0,0,0,1704,0,1704,0,'Jannok','Rogue Bot','',0,80,80,2,80,80,1,1.02,1.14286,1,0,9,73,0,1489,1,2000,0,4,4608,0,0,0,0,4,4,31.856,43.802,100,7,1,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,0,0,0,0,0,0,0,0,0,1204,628,0,'rogue_bot',0),
+(60096,0,0,0,0,0,1708,0,1708,0,'Laurna','Priest Bot','',0,80,80,2,80,80,1,1.03,1.14286,1,0,9,20,0,100,1,2000,0,8,4608,0,0,0,0,5,4,33.264,45.738,100,7,1,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,0,0,0,0,0,0,0,0,0,127,94,0,'priest_bot',0),
+(60097,0,0,0,0,0,1706,0,1706,0,'Kal','Druid Bot','',0,80,80,2,80,80,1,1.03,1.14286,1,0,10,214,0,107,1,2000,0,2,4608,0,0,0,0,11,4,34.6544,47.6498,100,7,1,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,0,0,0,0,0,0,0,0,0,93,85,0,'druid_bot',0),
+(60098,0,0,0,0,0,4296,0,4296,0,'Harruk','Hunter Bot','',0,80,80,2,29,29,1,1.1,1.14286,1,0,23,48,0,247,1,2000,1771,2,4608,0,0,3,0,3,2,56.672,77.924,100,7,1,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,0,0,0,0,0,0,0,0,0,1186,344,0,'hunter_bot',0),
+(60099,0,0,0,0,0,4299,0,4299,0,'Reban','Hunter bot','',0,80,80,2,104,104,1,1.2,1.14286,1,0,23,48,0,247,1,2000,1100,2,4608,0,0,3,0,3,6,56.672,77.924,100,7,1,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,0,0,0,0,0,0,0,0,0,1186,865,0,'hunter_bot',0),
+(60100,0,0,0,0,0,4304,0,4304,0,'Bolyun','Hunter Bot','',0,80,80,2,80,80,1,1.05,1.14286,1,0,23,48,0,247,1,2000,1235,2,4608,0,0,3,0,3,4,56.672,77.924,100,7,1,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,0,0,0,0,0,0,0,0,0,1186,0,0,'hunter_bot',0),
+(60101,0,0,0,0,0,1897,0,1897,0,'Taijin','Priest Bot','',0,80,80,2,126,126,1,1.1,1.14286,1,0,8,16,0,84,1,2000,2013,8,4608,0,0,0,0,5,8,28.9872,39.8574,100,7,1,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,0,0,0,0,0,0,0,0,0,127,94,0,'priest_bot',0),
+(60102,0,0,0,0,0,4068,0,4068,0,'Kenjai','Priest Bot','',0,80,80,2,126,126,1,1.1,1.14286,1,0,3,9,0,42,1,2000,2101,8,4608,0,0,0,0,5,8,16.808,23.111,100,7,1,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,0,0,0,0,0,0,0,0,0,127,458,0,'priest_bot',0),
+(60103,0,0,0,0,0,2066,0,2066,0,'Danlaar','Hunter Bot','',0,80,80,2,80,80,1,1.05,1.14286,1,0,19,40,0,205,1,2000,1382,2,4608,0,0,0,0,3,4,51.128,70.301,100,7,1,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,0,0,0,0,0,0,0,0,0,1186,1,0,'hunter_bot',0),
+(60104,0,0,0,0,0,2196,0,2196,0,'Ariasta','Warrior Bot','',0,80,80,2,80,80,1,1.26,1.14286,1,0,42,88,0,451,1,2000,0,1,4608,0,0,0,0,1,4,74.448,102.366,100,7,1,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,0,0,0,0,0,0,0,0,0,110,822,0,'warrior_bot',0),
+(60105,0,0,0,0,0,2198,0,2198,0,'Sildanair','Warrior Bot','',0,80,80,2,80,80,1,1.14,1.14286,1,0,23,48,0,247,1,2000,0,1,4608,0,0,0,0,1,4,56.672,77.924,100,7,1,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,0,0,0,0,0,0,0,0,0,1362,281,0,'warrior_bot',0),
+(60106,0,0,0,0,0,2200,0,2200,0,'Astarii','Priest Bot','',0,80,80,2,80,80,1,1.26,1.14286,1,0,42,88,0,451,1,2000,0,8,4608,0,0,0,0,5,4,74.448,102.366,100,7,1,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,0,0,0,0,0,0,0,0,0,127,94,0,'priest_bot',0),
+(60107,0,0,0,0,0,2201,0,2201,0,'Jandria','Priest Bot','',0,80,80,2,80,80,1,1.2,1.14286,1,0,32,67,0,345,1,2000,0,8,4608,0,0,0,0,5,4,66.44,91.355,100,7,1,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,0,0,0,0,0,0,0,0,0,127,173,0,'priest_bot',0),
+(60108,0,0,0,0,0,2202,0,2202,0,'Lariia','Priest Bot','',0,80,80,2,80,80,1,1.14,1.14286,1,0,23,48,0,247,1,2000,0,8,4608,0,0,0,0,5,4,56.672,77.924,100,7,1,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,0,0,0,0,0,0,0,0,0,127,838,0,'priest_bot',0),
+(60109,0,0,0,0,0,2231,0,2231,0,'Syurna','Rogue Bot','',0,80,80,2,80,80,1,1.14,1.14286,1,0,23,48,0,1489,1,2000,0,4,4608,0,0,0,0,4,4,56.672,77.924,100,7,1,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,0,0,0,0,0,0,0,0,0,1204,220,0,'rogue_bot',0),
+(60110,0,0,0,0,0,7669,0,7669,0,'Elissa','Mage Bot','',0,80,80,2,80,80,1,1.11,1.14286,1,0,19,40,0,205,1,2000,0,8,4608,0,0,0,0,8,4,51.128,70.301,100,7,1,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,0,0,0,0,0,0,0,0,0,127,96,0,'mage_bot',0),
+(60111,0,0,0,0,0,2252,0,2252,0,'Erion','Rogue Bot','',0,80,80,2,80,80,1,1.26,1.14286,1,0,42,88,0,1489,1,2000,0,4,4608,0,0,0,0,4,4,74.448,102.366,100,7,1,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,0,0,0,0,0,0,0,0,0,1206,277,0,'rogue_bot',0),
+(60112,0,0,0,0,0,2243,0,2243,0,'Anishar','Rogue Bot','',0,80,80,2,80,80,1,1.2,1.14286,1,0,32,67,0,1489,1,2000,0,4,4608,0,0,0,0,4,4,66.44,91.355,100,7,1,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,0,0,0,0,0,0,0,0,0,1204,1913,0,'rogue_bot',0),
+(60113,0,0,0,0,0,2250,0,2250,0,'Denatharion','Druid Bot','',0,80,80,2,80,80,1,1.2,1.14286,1,0,32,267,0,345,1,2000,0,2,4608,0,0,0,0,11,4,66.44,91.355,100,7,1,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,0,0,0,0,0,0,0,0,0,93,258,0,'druid_bot',0),
+(60114,0,0,0,0,0,2255,0,2255,0,'Fylerian','Druid Bot','',0,80,80,2,80,80,1,1.14,1.14286,1,0,23,348,0,247,1,2000,0,2,4608,0,0,0,0,11,4,56.672,77.924,100,7,1,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,0,0,0,0,0,0,0,0,0,93,358,0,'druid_bot',0),
+(60115,0,0,0,0,0,2416,0,2416,0,'Caelyb','Hunter Bot','',0,80,80,2,80,80,1,1.05,1.14286,1,0,23,48,0,247,1,2000,1186,2,4608,0,0,3,0,3,4,56.672,77.924,100,7,1,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,0,0,0,0,0,0,0,0,0,1186,1073,0,'hunter_bot',0),
+(60116,0,0,0,0,0,2675,0,2675,0,'Kaal','Warlock Bot','',0,80,80,2,68,68,1,1.1,1.14286,1,0,42,88,0,451,1,2000,0,8,4608,0,0,0,0,9,5,74.448,102.366,100,7,1,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,0,0,0,0,0,0,0,0,0,1491,110,0,'warlock_bot',0),
+(60117,0,0,0,0,0,16800,0,16800,0,'Lana','Warlock Bot','',0,80,80,2,68,68,1,1.1,1.14286,1,0,32,67,0,345,1,2000,0,8,4608,0,0,0,0,9,5,66.44,91.355,100,7,1,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,0,0,0,0,0,0,0,0,0,1491,1784,0,'warlock_bot',0),
+(60118,0,0,0,0,0,2646,0,2646,0,'Richard','Warlock Bot','',0,80,80,2,68,68,1,1.1,1.14286,1,0,23,48,0,247,1,2000,0,8,4608,0,0,0,0,9,5,56.672,77.924,100,7,1,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,0,0,0,0,0,0,0,0,0,1491,1217,0,'warlock_bot',0),
+(60119,0,0,0,0,0,10214,0,10214,0,'Kaelystia','Mage Bot','',0,80,80,2,68,68,1,1.1,1.14286,1,0,42,88,0,451,1,2000,0,8,4608,0,0,0,0,8,5,74.448,102.366,100,6,1,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,0,0,0,0,0,0,0,0,0,127,96,8388624,'mage_bot',0),
+(60120,0,0,0,0,0,2644,0,2644,0,'Pierce','Mage Bot','',0,80,80,2,68,68,1,1.1,1.14286,1,0,32,67,0,345,1,2000,0,8,4608,0,0,0,0,8,5,66.44,91.355,100,7,1,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,0,0,0,0,0,0,0,0,0,127,173,0,'mage_bot',0),
+(60121,0,0,0,0,0,2657,0,2657,0,'Anastasia','Mage Bot','',0,80,80,2,68,68,1,1.1,1.14286,1,0,23,48,0,247,1,2000,0,8,4608,0,0,0,0,8,5,56.672,77.924,100,7,1,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,0,0,0,0,0,0,0,0,0,127,173,0,'mage_bot',0),
+(60122,0,0,0,0,0,2620,0,2620,0,'Chris','Warrior Bot','',0,80,80,2,68,68,1,1.1,1.14286,1,0,42,88,0,451,1,2000,0,1,4608,0,0,0,0,1,5,74.448,102.366,100,7,1,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,0,0,0,0,0,0,0,0,0,1022,801,0,'warrior_bot',0),
+(60123,0,0,0,0,0,2658,0,2658,0,'Angela','Warrior Bot','',0,80,80,2,68,68,1,1.1,1.14286,1,0,32,67,0,345,1,2000,0,1,4608,0,0,0,0,1,5,66.44,91.355,100,7,1,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,0,0,0,0,0,0,0,0,0,1362,1881,0,'warrior_bot',0),
+(60124,0,0,0,0,0,2614,0,2614,0,'Baltus','Warrior Bot','',0,80,80,2,68,68,1,1.1,1.14286,1,0,23,48,0,247,1,2000,0,1,4608,0,0,0,0,1,5,56.672,77.924,100,7,1,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,0,0,0,0,0,0,0,0,0,110,1882,0,'warrior_bot',0),
+(60125,0,0,0,0,0,3054,0,3054,0,'Kelv','Warrior Bot','',0,80,80,2,55,55,1,1.17,1.14286,1,0,27,57,0,294,1,1560,1716,1,4608,0,0,0,0,1,3,61.776,84.942,100,7,1,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,0,0,0,0,0,0,0,0,0,1362,136,0,'warrior_bot',0),
+(60126,0,0,0,0,0,3055,0,3055,0,'Bilban','Warrior Bot','',0,80,80,2,875,875,1,1.35,1.14286,1,0,27,57,0,294,1,1500,0,1,4608,0,0,0,0,1,7,61.776,84.942,100,7,1,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,0,0,0,0,0,0,0,0,0,394,675,0,'warrior_bot',0),
+(60127,0,0,0,0,0,3056,0,3056,0,'Daera','Hunter Bot','',0,80,80,2,55,55,1,1.26,1.14286,1,0,42,88,0,451,1,1410,1551,2,4608,0,0,0,0,3,3,74.448,102.366,100,7,1,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,0,0,0,0,0,0,0,0,0,1186,613,0,'hunter_bot',0),
+(60128,0,0,0,0,0,3072,0,3072,0,'Olmin','Hunter Bot','',0,80,80,2,55,55,1,1.2,1.14286,1,0,32,67,0,345,1,2000,1033,2,4608,0,0,0,0,3,3,66.44,91.355,100,7,1,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,0,0,0,0,0,0,0,0,0,1186,10,0,'hunter_bot',0),
+(60129,0,0,0,0,0,3073,0,3073,0,'Regnus','Hunter Bot','',0,80,80,2,55,55,1,1.14,1.14286,1,0,23,48,0,247,1,2000,1012,2,4608,0,0,0,0,3,3,56.672,77.924,100,7,1,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,0,0,0,0,0,0,0,0,0,1186,10,0,'hunter_bot',0),
+(60130,0,0,0,0,0,3086,0,3086,0,'Theodrus','Priest Bot','',0,80,80,2,55,55,1,1.26,1.14286,1,0,42,88,0,451,1,2000,0,8,4608,0,0,0,0,5,3,74.448,102.366,100,7,1,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,0,0,0,0,0,0,0,0,0,127,94,0,'priest_bot',0),
+(60131,0,0,0,0,0,3066,0,3066,0,'Braenna','Priest Bot','',0,80,80,2,55,55,1,1.2,1.14286,1,0,32,67,0,345,1,2000,0,8,4608,0,0,0,0,5,3,66.44,91.355,100,7,1,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,0,0,0,0,0,0,0,0,0,127,173,0,'priest_bot',0),
+(60132,0,0,0,0,0,3085,0,3085,0,'Toldren','Priest Bot','',0,80,80,2,55,55,1,1.14,1.14286,1,0,23,48,0,247,1,2000,0,8,4608,0,0,0,0,5,3,56.672,77.924,100,7,1,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,0,0,0,0,0,0,0,0,0,127,838,0,'priest_bot',0),
+(60134,0,0,0,0,0,3108,0,3108,0,'Bink','Mage Bot','',0,80,80,2,875,875,1,1.31,1.14286,1,0,23,48,0,247,1,2000,0,8,4608,0,0,0,0,8,7,56.672,77.924,100,7,1,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,0,0,0,0,0,0,0,0,0,127,838,0,'mage_bot',0),
+(60135,0,0,0,0,0,10214,0,10214,0,'Juli','Mage Bot','',0,80,80,2,875,875,1,1.38,1.14286,1,0,32,67,0,345,1,2000,0,8,4608,0,0,0,0,8,7,66.44,91.355,100,7,1,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,0,0,0,0,0,0,0,0,0,127,173,0,'mage_bot',0),
+(60136,0,0,0,0,0,3109,0,3109,0,'Nittegousse','Mage Bot','',0,80,80,2,55,55,1,1.45,1.14286,1,0,42,88,0,451,1,2000,0,8,4608,0,0,0,0,8,3,74.448,102.366,100,7,1,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,0,0,0,0,0,0,0,0,0,127,96,0,'mage_bot',0),
+(60137,0,0,0,0,0,3089,0,3089,0,'Valgar','Paladin Bot','',0,80,80,2,55,55,1,1.26,1.14286,1,0,42,88,0,451,1,2000,0,2,4608,0,0,0,0,2,3,74.448,102.366,100,7,1,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,0,0,0,0,0,0,0,0,0,205,105,0,'paladin_bot',0),
+(60138,0,0,0,0,0,3088,0,3088,0,'Beldruk','Paladin Bot','',0,80,80,2,55,55,1,1.2,1.14286,1,0,32,67,0,345,1,2000,0,2,4608,0,0,0,0,2,3,66.44,91.355,100,7,1,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,0,0,0,0,0,0,0,0,0,204,799,0,'paladin_bot',0),
+(60139,0,0,0,0,0,3087,0,3087,0,'Brandur','Paladin Bot','',0,80,80,2,55,55,1,1.2,1.14286,1,0,32,67,0,345,1,2000,0,2,4608,0,0,0,0,2,3,66.44,91.355,100,7,1,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,0,0,0,0,0,0,0,0,0,205,799,0,'paladin_bot',0),
+(60140,0,0,0,0,0,3101,0,3101,0,'Hulfdan','Rogue Bot','',0,80,80,2,55,55,1,1.26,1.14286,1,0,42,88,0,1489,1,2000,0,4,4608,0,0,0,0,4,3,74.448,102.366,100,7,1,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,0,0,0,0,0,0,0,0,0,1206,1886,0,'rogue_bot',0),
+(60141,0,0,0,0,0,3100,0,3100,0,'Ormyr','Rogue Bot','',0,80,80,2,55,55,1,1.2,1.14286,1,0,32,67,0,1489,1,2000,0,4,4608,0,0,0,0,4,3,66.44,91.355,100,7,1,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,0,0,0,0,0,0,0,0,0,1204,220,0,'rogue_bot',0),
+(60142,0,0,0,0,0,3113,0,3113,0,'Phenwick','Rogue Bot','',0,80,80,2,875,875,1,1.31,1.14286,1,0,23,48,0,1489,1,2000,0,4,4608,0,0,0,0,4,7,56.672,77.924,100,7,1,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,0,0,0,0,0,0,0,0,0,1206,220,0,'rogue_bot',0),
+(60143,0,0,0,0,0,3115,0,3115,0,'Coeurdechardon','Warlock Bot','',0,80,80,2,55,55,1,1.31,1.14286,1,0,23,48,0,247,1,2000,0,8,4608,0,0,0,0,9,3,56.672,77.924,100,7,1,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,0,0,0,0,0,0,0,0,0,1491,838,0,'warlock_bot',0),
+(60144,0,0,0,0,0,3116,0,3116,0,'Eglantin','Warlock Bot','',0,80,80,2,875,875,1,1.38,1.14286,1,0,32,67,0,345,1,2000,0,8,4608,0,0,0,0,9,7,66.44,91.355,100,7,1,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,0,0,0,0,0,0,0,0,0,1491,173,0,'warlock_bot',0),
+(60145,0,0,0,0,0,3122,0,3122,0,'Alexander','Warlock Bot','',0,80,80,2,55,55,1,1.26,1.14286,1,0,42,88,0,451,1,2000,0,8,4608,0,0,0,0,9,3,74.448,102.366,100,7,1,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,0,0,0,0,0,0,0,0,0,1491,110,0,'warlock_bot',0),
+(60146,0,0,0,0,0,3280,0,3280,0,'Wu','Warrior Bot','',0,80,80,2,12,12,1,1.2,1.14286,1,0,32,67,0,345,1,1500,0,1,4608,0,0,0,0,1,1,66.44,91.355,100,7,1,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,0,0,0,0,0,0,0,0,0,1022,727,0,'warrior_bot',0),
+(60147,0,0,0,0,0,3287,0,3287,0,'Ilsa','Warrior Bot','',0,80,80,2,12,12,1,1.14,1.14286,1,0,23,48,0,247,1,1500,0,1,4608,0,0,0,0,1,1,56.672,77.924,100,7,1,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,0,0,0,0,0,0,0,0,0,1362,613,0,'warrior_bot',0),
+(60148,0,0,0,0,0,3283,0,3283,0,'Joshua','Priest Bot','',0,80,80,2,12,12,1,1.2,1.14286,1,0,32,67,0,345,1,2000,0,8,4608,0,0,0,0,5,1,66.44,91.355,100,7,1,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,0,0,0,0,0,0,0,0,0,127,173,0,'priest_bot',0),
+(60149,0,0,0,0,0,3284,0,3284,0,'Arthur','Paladin Bot','',0,80,80,2,12,12,1,1.14,1.14286,1,0,23,48,0,247,1,2000,0,2,4608,0,0,0,0,2,1,56.672,77.924,100,7,1,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,0,0,0,0,0,0,0,0,0,204,799,0,'paladin_bot',0),
+(60150,0,0,0,0,0,3289,0,3289,0,'Katherine','Paladin Bot','',0,80,80,2,12,12,1,1.2,1.14286,1,0,32,67,0,345,1,2000,0,2,4608,0,0,0,0,2,1,66.44,91.355,100,7,1,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,0,0,0,0,0,0,0,0,0,205,799,0,'paladin_bot',0),
+(60151,0,0,0,0,0,3291,0,3291,0,'Deline','Warlock Bot','',0,80,80,2,12,12,1,1.14,1.14286,1,0,23,48,0,247,1,2000,0,8,4608,0,0,0,0,9,1,56.672,77.924,100,7,1,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,0,0,0,0,0,0,0,0,0,1491,838,0,'warlock_bot',0),
+(60152,0,0,0,0,0,3286,0,3286,0,'Sandahl','Warlock Bot','',0,80,80,2,12,12,1,1.2,1.14286,1,0,32,67,0,345,1,2000,0,8,4608,0,0,0,0,9,1,66.44,91.355,100,7,1,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,0,0,0,0,0,0,0,0,0,1491,173,0,'warlock_bot',0),
+(60153,0,0,0,0,0,3292,0,3292,0,'Jennea','Mage Bot','',0,80,80,2,12,12,1,1.14,1.14286,1,0,23,48,0,247,1,2000,0,8,4608,0,0,0,0,8,1,56.672,77.924,100,7,1,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,0,0,0,0,0,0,0,0,0,127,0,0,'mage_bot',0),
+(60154,0,0,0,0,0,19803,0,19803,0,'Elsharin','Mage Bot','',0,80,80,2,12,12,1,1.2,1.14286,1,0,32,67,0,345,1,2000,0,8,4608,0,0,0,0,8,1,66.44,91.355,100,7,1,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,0,0,0,0,0,0,0,0,0,127,173,0,'mage_bot',0),
+(60155,0,0,0,0,0,3299,0,3299,0,'Kaerbrus','Hunter Bot','',0,80,80,2,80,80,1,1.1,1.14286,1,0,39,80,0,418,1,2000,1263,2,4608,0,0,0,0,3,4,72.2304,99.3168,100,7,1,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,0,0,0,0,0,0,0,0,0,1186,613,0,'hunter_bot',0),
+(60156,0,0,0,0,0,3300,0,3300,0,'Sheldras','Druid Bot','',0,80,80,2,12,12,1,1.26,1.14286,1,0,42,288,0,451,1,2000,0,2,4608,0,0,0,0,11,4,74.448,102.366,100,7,1,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,0,0,0,0,0,0,0,0,0,93,1071,0,'druid_bot',0),
+(60157,0,0,0,0,0,3301,0,3301,0,'Theridran','Druid Bot','',0,80,80,2,80,80,1,1.2,1.14286,1,0,32,467,0,345,1,2000,0,2,4608,0,0,0,0,11,4,66.44,91.355,100,7,1,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,0,0,0,0,0,0,0,0,0,93,258,0,'druid_bot',0),
+(60158,0,0,0,0,0,3312,0,3312,0,'Einris','Hunter Bot','',0,80,80,2,12,12,1,1.26,1.14286,1,0,42,88,0,451,1,2000,1157,2,4608,0,0,0,0,3,1,74.448,102.366,100,7,1,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,0,0,0,0,0,0,0,0,0,1186,613,0,'hunter_bot',0),
+(60159,0,0,0,0,0,3309,0,3309,0,'Ulfir','Hunter Bot','',0,80,80,2,12,12,1,1.2,1.14286,1,0,32,67,0,345,1,2000,1379,2,4608,0,0,0,0,3,1,66.44,91.355,100,7,1,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,0,0,0,0,0,0,0,0,0,1186,10,0,'hunter_bot',0),
+(60160,0,0,0,0,0,3310,0,3310,0,'Thorfin','Hunter Bot','',0,80,80,2,12,12,1,1.14,1.14286,1,0,23,48,0,247,1,2000,1175,2,4608,0,0,0,0,3,1,56.672,77.924,100,7,1,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,0,0,0,0,0,0,0,0,0,1186,298,0,'hunter_bot',0),
+(60161,0,0,0,0,0,10171,0,10171,0,'UnThuwa','Mage Bot','',0,80,80,2,126,126,1,1.1,1.14286,1,0,6,12,0,59,1,2000,2057,8,4608,0,0,0,0,8,8,23.0384,31.6778,100,7,1,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,0,0,0,0,0,0,0,0,0,127,143,0,'mage_bot',0),
+(60162,0,0,0,0,0,4524,0,4524,0,'Pephredo','Mage Bot','',0,80,80,2,29,29,1,1.1,1.14286,1,0,42,88,0,451,1,2000,1551,8,4608,0,0,0,0,8,2,74.448,102.366,100,7,1,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,0,0,0,0,0,0,0,0,0,127,559,0,'mage_bot',0),
+(60163,0,0,0,0,0,4522,0,4522,0,'Enyo','Mage Bot','',0,80,80,2,29,29,1,1.1,1.14286,1,0,32,67,0,345,1,2000,1661,8,4608,0,0,0,0,8,2,66.44,91.355,100,7,1,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,0,0,0,0,0,0,0,0,0,127,99,0,'mage_bot',0),
+(60164,0,0,0,0,0,4526,0,4526,0,'Mai','Mage Bot','',0,80,80,2,126,126,1,1.1,1.14286,1,0,3,4,0,26,1,2000,2101,8,4608,0,0,0,0,8,8,16.808,23.111,100,7,1,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,0,0,0,0,0,0,0,0,0,127,143,0,'mage_bot',0),
+(60165,0,0,0,0,0,4523,0,4523,0,'Deino','Mage Bot','',0,80,80,2,29,29,1,1.1,1.14286,1,0,23,48,0,247,1,2000,1771,8,4608,0,0,0,0,8,2,56.672,77.924,100,7,1,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,0,0,0,0,0,0,0,0,0,127,21,0,'mage_bot',0),
+(60166,0,0,0,0,0,4665,0,4665,0,'Birgitte','Mage Bot','',0,80,80,2,68,68,1,1.1,1.14286,1,0,27,57,0,294,1,1000,0,8,4608,0,0,0,0,8,5,61.776,84.942,100,7,1,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,0,0,0,0,0,0,0,0,0,127,173,0,'mage_bot',0),
+(60167,0,0,0,0,0,12849,0,12849,0,'Thuul','Mage Bot','',0,80,80,2,29,29,1,1.1,1.14286,1,0,27,57,0,294,1,1000,1716,8,4608,0,0,0,0,8,2,61.776,84.942,100,7,1,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,0,0,0,0,0,0,0,0,0,127,173,0,'mage_bot',0),
+(60168,0,0,0,0,0,4690,0,4690,0,'Zayus','Priest Bot','',0,80,80,2,29,29,1,1.1,1.14286,1,0,62,131,0,677,1,2000,1551,8,4608,0,0,0,0,5,2,74.448,102.366,100,7,1,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,0,0,0,0,0,0,0,0,0,127,143,0,'priest_bot',0),
+(60169,0,0,0,0,0,10473,0,10473,0,'Xyera','Priest Bot','',0,80,80,2,29,29,1,1.1,1.14286,1,0,32,67,0,345,1,2000,1661,8,4608,0,0,0,0,5,2,66.44,91.355,100,7,1,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,0,0,0,0,0,0,0,0,0,127,228,0,'priest_bot',0),
+(60170,0,0,0,0,0,4711,0,4711,0,'Urkyo','Priest Bot','',0,80,80,2,29,29,1,1.1,1.14286,1,0,23,48,0,247,1,2000,1771,8,4608,0,0,0,0,5,2,56.672,77.924,100,7,1,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,0,0,0,0,0,0,0,0,0,127,85,0,'priest_bot',0),
+(60171,0,0,0,0,0,6060,0,6060,0,'Uthelnay','Mage Bot','',0,80,80,2,126,126,1,1.1,1.14286,1,0,23,48,0,247,1,2000,1771,8,4608,0,0,0,0,8,8,56.672,77.924,100,7,1,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,0,0,0,0,0,0,0,0,0,127,1071,0,'mage_bot',0),
+(60172,0,0,0,0,0,6072,0,6072,0,'Dink','Mage Bot','',0,80,80,2,875,875,1,1.31,1.14286,1,0,23,48,0,247,1,2000,0,8,4608,0,0,0,0,8,7,56.672,77.924,100,7,1,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,0,0,0,0,0,0,0,0,0,127,838,0,'mage_bot',0),
+(60173,0,0,0,0,0,6071,0,6071,0,'Darnath','Warrior Bot','',0,80,80,2,80,80,1,1.23,1.14286,1,0,36,77,0,394,1,2000,0,1,4608,0,0,0,0,1,4,70.664,97.163,100,7,1,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,0,0,0,0,0,0,0,0,0,394,277,0,'warrior_bot',0),
+(60174,0,0,0,0,0,7356,0,7356,0,'Karman','Paladin Bot','',0,80,80,2,894,894,1,1.25,1.14286,1,0,27,57,0,294,1,2000,0,2,4608,0,0,0,0,2,1,61.776,84.942,100,7,1,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,0,0,0,0,0,0,0,0,0,204,117,0,'paladin_bot',0),
+(60175,0,0,0,0,0,11037,0,11037,0,'Evencane','Warrior Bot','',0,80,80,2,894,894,1,1.25,1.14286,1,0,27,57,0,294,1,2000,0,1,4608,0,0,0,0,1,1,61.776,84.942,100,7,1,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,0,0,0,0,0,0,0,0,0,110,639,0,'warrior_bot',0),
+(60176,0,0,0,0,0,7357,0,7357,0,'Jannos','Druid Bot','',0,80,80,2,104,104,1,1.1,1.14286,1,0,32,200,0,1034,1,2000,0,2,4608,0,0,0,0,11,6,66.44,91.355,100,7,1,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,0,0,0,0,0,0,0,0,0,93,119,0,'druid_bot',0),
+(60177,0,0,0,0,0,7538,0,7538,0,'Alenndaar','Hunter Bot','',0,80,80,2,1076,1076,1,1.05,1.14286,1,0,14,28,0,143,1,2000,1012,2,4608,0,0,0,0,3,4,41.3424,56.8458,100,7,1,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,0,0,0,0,0,0,0,0,0,1186,886,0,'hunter_bot',0),
+(60178,0,0,0,0,0,10738,0,10738,0,'Golhine','Druid Bot','',0,80,80,2,80,80,1,1.1,1.14286,1,0,42,288,0,451,1,2000,0,2,4608,0,0,0,0,11,4,74.448,102.366,100,7,1,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,0,0,0,0,0,0,0,0,0,93,1252,0,'druid_bot',0),
+(60179,0,0,0,0,0,9337,0,9337,0,'Hesuwa','Hunter Bot','',0,80,80,2,104,104,1,1.2,1.14286,1,0,23,48,0,247,1,2000,1178,2,4608,0,0,3,0,3,6,56.672,77.924,100,7,1,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,0,0,0,0,0,0,0,0,0,1186,6,0,'hunter_bot',0),
+(60180,0,0,0,0,0,9336,0,9336,0,'Xao\'tsu','Hunter Bot','',0,80,80,2,29,29,1,1.1,1.14286,1,0,23,48,0,247,1,2000,1771,2,4608,0,0,3,0,3,2,56.672,77.924,100,7,1,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,0,0,0,0,0,0,0,0,0,1186,886,0,'hunter_bot',0),
+(60181,0,0,0,0,0,9338,0,9338,0,'Belia','Hunter Bot','',0,80,80,2,55,55,1,1.14,1.14286,1,0,23,48,0,247,1,2000,1426,2,4608,0,0,3,0,3,3,56.672,77.924,100,7,1,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,0,0,0,0,0,0,0,0,0,1186,1081,0,'hunter_bot',0),
+(60182,0,0,0,0,0,10245,0,10245,0,'Dargh','Hunter Bot','',0,80,80,2,55,55,1,1.05,1.14286,1,0,12,25,0,128,1,1760,1936,2,4608,0,0,0,0,3,3,38.72,53.24,100,7,1,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,0,0,0,0,0,0,0,0,0,1186,19,0,'hunter_bot',0),
+(60183,0,0,0,0,0,11044,0,11044,0,'Meideros','Priest Bot','',0,80,80,2,80,80,1,1.08,1.14286,1,0,16,32,0,164,1,2000,0,8,4608,0,0,0,0,5,4,45.144,62.073,100,7,1,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,0,0,0,0,0,0,0,0,0,127,0,0,'priest_bot',0),
+(60184,0,0,0,0,0,11048,0,11048,0,'Presse','Priest Bot','',0,80,80,2,1076,1076,1,1.26,1.14286,1,0,42,88,0,451,1,2000,0,8,4608,0,0,0,0,5,4,74.448,102.366,100,7,1,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,0,0,0,0,0,0,0,0,0,127,0,0,'priest_bot',0),
+(60185,0,0,0,0,0,11053,0,11053,0,'Rohan','Priest Bot','',0,80,80,2,122,122,1,1.26,1.14286,1,0,42,88,0,451,1,2000,0,8,4608,0,0,0,0,5,3,74.448,102.366,100,7,1,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,0,0,0,0,0,0,0,0,0,127,0,0,'priest_bot',0),
+(60186,0,0,0,0,0,12053,0,12053,0,'Loganaar','Druid Bot','',0,80,80,2,994,994,1,1.1,1.14286,1,0,33,269,0,353,1,2000,0,2,4608,0,0,0,0,11,4,67.32,92.565,100,7,1,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,0,0,0,0,0,0,0,0,0,93,1252,0,'druid_bot',0),
+(60187,0,0,0,0,0,13171,0,13171,0,'Romano','Rogue Bot','',0,80,80,2,12,12,1,1.26,1.14286,1,0,42,88,0,1489,1,2000,0,4,4608,0,0,0,0,4,1,74.448,102.366,100,7,1,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,0,0,0,0,0,0,0,0,0,1204,734,0,'rogue_bot',0),
+(60188,0,0,0,0,0,13341,0,13341,0,'Sagorne','Shaman Bot','',0,80,80,2,104,104,1,1.1,1.14286,1,0,32,200,0,345,1,2000,0,2,4608,0,0,0,0,7,6,66.44,91.355,100,7,1,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,0,0,0,0,0,0,0,0,0,93,458,0,'shaman_bot',0),
+(60189,0,0,0,0,0,15522,0,15522,0,'Julia','Mage Bot','',0,80,80,2,1604,1604,1,1.125,1.14286,1,0,2,5,0,23,1,1500,0,8,4608,0,0,0,0,8,10,23.4783,39.2769,100,7,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'0',0,1,1,1,1,0,0,0,0,0,0,0,0,0,127,1022,0,'mage_bot',0),
+(60190,0,0,0,0,0,15511,0,15511,0,'Jesthenis','Paladin Bot','',0,80,80,2,1604,1604,1,1.125,1.14286,1,0,2,5,0,23,1,1500,0,2,4608,0,0,0,0,2,10,23.4783,32.7308,100,7,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'0',0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,1229,0,'paladin_bot',0),
+(60191,0,0,0,0,0,15524,0,15524,0,'Invocateur','Warlock Bot','',0,80,80,2,1604,1604,1,1.125,1.14286,1,0,2,5,0,23,1,1500,0,8,4608,0,0,0,0,9,10,23.4783,39.2769,100,7,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'0',0,1,1,1,1,0,0,0,0,0,0,0,0,0,1491,1455,0,'warlock_bot',0),
+(60192,0,0,0,0,0,15518,0,15518,0,'Matrone','Priest Bot','',0,80,80,2,1604,1604,1,1.125,1.14286,1,0,2,5,0,23,1,1500,0,8,4608,0,0,0,0,5,10,23.4783,39.2769,100,7,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'0',0,1,1,1,1,0,0,0,0,0,0,0,0,0,127,1306,0,'priest_bot',0),
+(60193,0,0,0,0,0,2659,0,2659,0,'Eclaireur','Rogue Bot','',0,80,80,2,68,68,1,1.1,1.14286,1,0,62,99,0,1489,1,2000,0,4,4608,0,0,0,0,4,5,28.1739,39.2769,100,7,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'0',0,1,1,1,1,0,0,0,0,0,0,0,0,0,1204,0,0,'rogue_bot',0),
+(60194,0,0,0,0,0,15520,0,15520,0,'Sallina','Hunter Bot','',0,80,80,2,1604,1604,1,1.125,1.14286,1,0,2,5,0,23,1,1500,1543,2,4608,0,0,0,0,3,10,23.4783,39.2769,100,7,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'0',0,1,1,1,1,0,0,0,0,0,0,0,0,0,1186,27,0,'hunter_bot',0),
+(60195,0,0,0,0,0,16685,0,16685,0,'Noellene','Paladin Bot','',0,80,80,2,1604,1604,1,1.125,1.14286,1,0,2,7,0,33,1,1500,0,2,4608,0,0,0,0,2,10,56.3478,78.5538,100,7,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'0',0,1,1,1,1,0,0,0,0,0,0,0,0,0,204,725,0,'paladin_bot',0),
+(60196,0,0,0,0,0,16707,0,16707,0,'Ponaris','Priest Bot','',0,80,80,2,1604,1604,1,1.125,1.14286,1,0,2,7,0,33,1,1500,0,8,4608,0,0,0,0,5,10,0,0,100,7,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'0',0,1,1,1,1,0,0,0,0,0,0,0,0,0,127,1096,0,'priest_bot',0),
+(60197,0,0,0,0,0,16222,0,16222,0,'Keilnei','Hunter Bot','',0,80,80,2,1638,1638,1,1.125,1.14286,1,0,5,18,0,79,1,2000,1180,2,4608,0,0,0,0,3,11,28.1739,39.2769,100,7,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'0',0,1,1,1,1,0,0,0,0,0,0,0,0,0,1186,1619,0,'hunter_bot',0),
+(60198,0,0,0,0,0,16223,0,16223,0,'Valaatu','Mage Bot','',0,80,80,2,1638,1638,1,1.125,1.14286,1,0,5,18,0,79,1,2000,0,8,4608,0,0,0,0,8,11,28.1739,39.2769,100,7,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'0',0,1,1,1,1,0,0,0,0,0,0,0,0,0,127,1624,0,'mage_bot',0),
+(60199,0,0,0,0,0,16224,0,16224,0,'Aurelon','Paladin Bot','',0,80,80,2,1638,1638,1,1.125,1.14286,1,0,5,18,0,79,1,2000,0,2,4608,0,0,0,0,2,11,28.1739,39.2769,100,7,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'0',0,1,1,1,1,0,0,0,0,0,0,0,0,0,205,1610,0,'paladin_bot',0),
+(60200,0,0,0,0,0,16225,0,16225,0,'Zalduun','Priest Bot','',0,80,80,2,1638,1638,1,1.25,1.14286,1,0,5,18,0,79,1,2000,0,8,4608,0,0,0,0,5,11,28.1739,39.2769,100,7,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'0',0,1,1,1,1,0,0,0,0,0,0,0,0,0,127,1626,0,'priest_bot',0),
+(60201,0,0,0,0,0,16226,0,16226,0,'Kore','Warrior Bot','',0,80,80,2,1638,1638,1,1.125,1.14286,1,0,5,54,0,322,1,2000,0,1,4608,0,0,0,0,1,11,28.1739,39.2769,100,7,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'0',0,1,1,1,1,0,0,0,0,0,0,0,0,0,1022,1618,0,'warrior_bot',0),
+(60202,0,0,0,0,0,16787,0,16787,0,'Alamma','Warlock Bot','',0,80,80,2,1604,1604,1,1.125,1.14286,1,0,14,53,0,236,1,2000,0,8,4608,0,0,0,0,9,10,0,0,100,7,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'0',0,1,1,1,1,0,0,0,0,0,0,0,0,0,1491,0,0,'warlock_bot',0),
+(60203,0,0,0,0,0,16800,0,16800,0,'Talionia','Warlock Bot','',0,80,80,2,1604,1604,1,1.125,1.14286,1,0,12,42,0,185,1,2000,0,8,4608,0,0,0,0,9,10,0,0,100,7,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'0',0,1,1,1,1,0,0,0,0,0,0,0,0,0,1491,0,0,'warlock_bot',0),
+(60204,0,0,0,0,0,16831,0,16831,0,'Zanien','Hunter Bot','',0,80,80,2,1604,1604,1,1.125,1.14286,1,0,18,66,0,290,1,2000,1180,2,4608,0,0,0,0,9,10,0,0,100,7,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'0',0,1,1,1,1,0,0,0,0,0,0,0,0,0,1186,0,0,'hunter_bot',0),
+(60205,0,0,0,0,0,16781,0,16781,0,'Zaedana','Mage Bot','',0,80,80,2,1604,1604,1,1.125,1.14286,1,0,12,42,0,185,1,2000,0,8,4608,0,0,0,0,8,10,0,0,100,7,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'0',0,1,1,1,1,0,0,0,0,0,0,0,0,0,127,0,0,'mage_bot',0),
+(60206,0,0,0,0,0,16824,0,16824,0,'Quithas','Mage Bot','',0,80,80,2,1604,1604,1,1.125,1.14286,1,0,14,53,0,236,1,2000,0,8,4608,0,0,0,0,8,10,0,0,100,7,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'0',0,1,1,1,1,0,0,0,0,0,0,0,0,0,127,0,0,'mage_bot',0),
+(60207,0,0,0,0,0,16739,0,16739,0,'Harene','Druid Bot','',0,80,80,2,1604,1604,1,1.125,1.14286,1,0,18,366,0,290,1,2000,0,2,4608,0,0,0,0,11,6,0,0,100,7,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'0',0,1,1,1,1,0,0,0,0,0,0,0,0,0,93,0,0,'druid_bot',0),
+(60208,0,0,0,0,0,16778,0,16778,0,'Tana','Hunter Bot','',0,80,80,2,1604,1604,1,1.125,1.14286,1,0,12,42,0,185,1,2000,1168,2,4608,0,0,0,0,3,10,0,0,100,7,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'0',0,1,1,1,1,0,0,0,0,0,0,0,0,0,1186,0,0,'hunter_bot',0),
+(60209,0,0,0,0,0,16816,0,16816,0,'Oninath','Hunter Bot','',0,80,80,2,1604,1604,1,1.125,1.14286,1,0,14,53,0,236,1,1000,1084,2,4608,0,0,0,0,3,10,0,0,100,7,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'0',0,1,1,1,1,0,0,0,0,0,0,0,0,0,1186,0,0,'hunter_bot',0),
+(60210,0,0,0,0,0,16829,0,16829,0,'Bachi','Paladin Bot','',0,80,80,2,1604,1604,1,0.93,1.14286,1,0,38,68,0,367,1,2000,0,2,4608,0,0,0,0,2,10,8.624,11.858,100,7,1,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,0,0,0,0,0,0,0,0,0,204,0,0,'paladin_bot',0),
+(60211,0,0,0,0,0,16767,0,16767,0,'Zelanis','Rogue Bot','',0,80,80,2,1604,1604,1,1.125,1.14286,1,0,62,80,0,1489,1,2000,0,4,4608,0,0,0,0,4,10,0,0,100,7,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'0',0,1,1,1,1,0,0,0,0,0,0,0,0,0,1206,0,0,'rogue_bot',0),
+(60212,0,0,0,0,0,16798,0,16798,0,'Elara','Rogue Bot','',0,80,80,2,1604,1604,1,1.125,1.14286,1,0,52,77,0,1489,1,2000,0,4,4608,0,0,0,0,4,10,0,0,100,7,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'0',0,1,1,1,1,0,0,0,0,0,0,0,0,0,1204,0,0,'rogue_bot',0),
+(60213,0,0,0,0,0,16858,0,16858,0,'Shalannius','Druid Bot','',0,80,80,2,1638,1638,1,1.125,1.14286,1,0,100,300,0,290,1,2000,0,2,4608,0,0,0,0,11,6,0,0,100,7,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'0',0,1,1,1,1,0,0,0,0,0,0,0,0,0,93,0,0,'druid_bot',0),
+(60214,0,0,0,0,0,17434,0,17434,0,'Deremiis','Hunter Bot','',0,80,80,2,1638,1638,1,1.125,1.14286,1,0,14,53,0,236,1,2000,1076,2,4608,0,0,0,0,3,11,0,0,100,7,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'0',0,1,1,1,1,0,0,0,0,0,0,0,0,0,1186,0,0,'hunter_bot',0),
+(60215,0,0,0,0,0,17247,0,17247,0,'Caedmos','Priest Bot','',0,80,80,2,1638,1638,1,1.125,1.14286,1,0,18,66,0,290,1,2000,0,8,4608,0,0,0,0,5,11,0,0,100,7,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'0',0,1,1,1,1,0,0,0,0,0,0,0,0,0,127,0,0,'priest_bot',0),
+(60216,0,0,0,0,0,17225,0,17225,0,'Baatun','Paladin Bot','',0,80,80,2,1638,1638,1,1.125,1.14286,1,0,18,66,0,290,1,2000,0,2,4608,0,0,0,0,2,11,0,0,100,7,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'0',0,1,1,1,1,0,0,0,0,0,0,0,0,0,205,0,0,'paladin_bot',0),
+(60217,0,0,0,0,0,17212,0,17212,0,'Ahonan','Warrior Bot','',0,80,80,2,1638,1638,1,1.125,1.14286,1,0,12,42,0,453,1,2000,0,1,4608,0,0,0,0,1,11,0,0,100,7,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'0',0,1,1,1,1,0,0,0,0,0,0,0,0,0,394,0,0,'warrior_bot',0),
+(60218,0,0,0,0,0,17598,0,17598,0,'Firmanvaar','Shaman Bot','',0,80,80,2,1638,1638,1,1.125,1.14286,1,0,5,200,0,79,1,2000,0,2,4608,0,0,0,0,7,11,28.1739,39.2769,100,7,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'0',0,1,1,1,1,0,0,0,0,0,0,0,0,0,93,1622,0,'shaman_bot',0),
+(60219,0,0,0,0,0,16860,0,16860,0,'Actron','Hunter Bot','',0,80,80,2,1638,1638,1,1.125,1.14286,1,0,2,5,0,23,1,2000,1125,2,4608,0,0,0,0,3,11,46.9565,65.4615,100,7,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'0',0,1,1,1,1,0,0,0,0,0,0,0,0,0,1186,0,0,'hunter_bot',0),
+(60220,0,0,0,0,0,17213,0,17213,0,'Behomat','Warrior Bot','',0,80,80,2,1638,1638,1,1.125,1.14286,1,0,18,66,0,290,1,2000,0,1,4608,0,0,0,0,1,11,0,0,100,7,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'0',0,1,1,1,1,0,0,0,0,0,0,0,0,0,1362,0,0,'warrior_bot',0),
+(60221,0,0,0,0,0,17600,0,17600,0,'Nobundo','Shaman Bot','',0,80,80,2,1638,1638,1,1.125,1.14286,1,0,21,200,0,345,1,2000,0,2,4608,0,0,0,0,7,11,346.02,481.06,100,7,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'0',0,1,1,1,1,0,0,0,0,0,0,0,0,0,93,85,0,'shaman_bot',0),
+(60222,0,0,0,0,0,17599,0,17599,0,'Tuluun','Shaman Bot','',0,80,80,2,1638,1638,1,1.125,1.14286,1,0,41,200,0,393,1,2000,0,2,4608,0,0,0,0,7,11,70.4348,98.1923,100,7,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'0',0,1,1,1,1,0,0,0,0,0,0,0,0,0,93,1086,0,'shaman_bot',0),
+(60223,0,0,0,0,0,16914,0,16914,0,'Sulaa','Shaman Bot','',0,80,80,2,1638,1638,1,1.125,1.14286,1,0,14,200,0,236,1,2000,0,2,4608,0,0,0,0,7,11,0,0,100,7,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'0',0,1,1,1,1,0,0,0,0,0,0,0,0,0,93,85,0,'shaman_bot',0),
+(60224,0,0,0,0,0,17215,0,17215,0,'Ruada','Warrior Bot','',0,80,80,2,1638,1638,1,1.125,1.14286,1,0,2,76,0,643,1,2000,0,1,4608,0,0,0,0,1,11,0,0,100,7,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'0',0,1,1,1,1,0,0,0,0,0,0,0,0,0,1022,797,0,'warrior_bot',0),
+(60225,0,0,0,0,0,17233,0,17233,0,'Semid','Mage Bot','',0,80,80,2,1638,1638,1,1.125,1.14286,1,0,2,5,0,23,1,2000,0,8,4608,0,0,0,0,8,11,0,0,100,7,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'0',0,1,1,1,1,0,0,0,0,0,0,0,0,0,127,1304,0,'mage_bot',0),
+(60226,0,0,0,0,0,17232,0,17232,0,'Guvan','Priest Bot','',0,80,80,2,1638,1638,1,1.125,1.14286,1,0,2,5,0,26,1,2000,0,8,4608,0,0,0,0,5,11,0,0,100,7,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'0',0,1,1,1,1,0,0,0,0,0,0,0,0,0,127,94,0,'priest_bot',0),
+(60227,0,0,0,0,0,17234,0,17234,0,'Tullas','Paladin Bot','',0,80,80,2,1638,1638,1,1.125,1.14286,1,0,2,5,0,23,1,2000,0,2,4608,0,0,0,0,2,11,0,0,100,7,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'0',0,1,1,1,1,0,0,0,0,0,0,0,0,0,204,422,0,'paladin_bot',0),
+(60228,0,0,0,0,0,17488,0,17488,0,'Killac','Hunter bot','',0,80,80,2,1638,1638,1,1.125,1.14286,1,0,12,42,0,185,1,2000,0,2,4608,0,0,0,0,3,11,0,0,100,7,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'0',0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,'hunter_bot',0),
+(60229,0,0,0,0,0,17226,0,17226,0,'Jol','Paladin Bot','',0,80,80,2,1638,1638,1,1.125,1.14286,1,0,14,53,0,236,1,2000,0,2,4608,0,0,0,0,2,11,0,0,100,7,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'0',0,1,1,1,1,0,0,0,0,0,0,0,0,0,205,0,0,'paladin_bot',0),
+(60230,0,0,0,0,0,17248,0,17248,0,'Fallat','Priest Bot','',0,80,80,2,1638,1638,1,1.125,1.14286,1,0,14,53,0,236,1,2000,0,8,4608,0,0,0,0,5,11,0,0,100,7,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'0',0,1,1,1,1,0,0,0,0,0,0,0,0,0,127,0,0,'priest_bot',0),
+(60231,0,0,0,0,0,17243,0,17243,0,'Harnan','Mage Bot','',0,80,80,2,1638,1638,1,1.125,1.14286,1,0,14,53,0,236,1,2000,0,8,4608,0,0,0,0,8,11,0,0,100,7,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'0',0,1,1,1,1,0,0,0,0,0,0,0,0,0,127,0,0,'mage_bot',0),
+(60232,0,0,0,0,0,17241,0,17241,0,'Bati','Mage Bot','',0,80,80,2,1638,1638,1,1.125,1.14286,1,0,12,42,0,185,1,2000,0,8,4608,0,0,0,0,8,11,0,0,100,7,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'0',0,1,1,1,1,0,0,0,0,0,0,0,0,0,127,0,0,'mage_bot',0),
+(60233,0,0,0,0,0,17792,0,17792,0,'Hobahken','Shaman Bot','',0,80,80,2,1638,1638,1,1.08,1.14286,1,0,18,200,0,290,1,2000,0,2,4608,0,0,0,0,7,11,0,0,100,7,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'0',0,1,1,1,1,0,0,0,0,0,0,0,0,0,93,85,0,'shaman_bot',0),
+(60234,0,0,0,0,0,6820,0,6820,0,'Gurrag','Shaman Bot','',0,80,80,2,1638,1638,1,1.08,1.14286,1,0,12,200,0,185,1,2000,0,2,4608,0,0,0,0,7,11,0,0,100,7,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'0',0,1,1,1,1,0,0,0,0,0,0,0,0,0,93,85,0,'shaman_bot',0),
+(60235,0,0,0,0,0,19596,0,19596,0,'Auberose','Paladin Bot','',0,80,80,2,1602,1602,1,1.1,1.14286,1,0,176,176,0,367,1,2000,0,2,4608,0,0,0,0,2,10,0,0,100,7,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'0',0,1,1,1,1,0,0,0,0,0,0,0,0,0,897,1552,0,'paladin_bot',0),
+(60236,0,0,0,0,0,10335,10335,10335,10335,'Afina','Priest Bot','',0,80,80,2,35,35,1,0.95,1.14286,1,0,12,25,0,128,1,10000,0,8,4608,0,0,0,0,5,2,40,53,100,7,1,0,0,0,100,100,100,100,100,100,0,0,0,0,0,0,0,0,0,0,0,0,'',0,3,1,1,1,0,0,0,0,0,0,0,0,1,1370,0,0,'priest_bot',0),
+(60237,0,0,0,0,0,1132,0,1132,0,'Voidwalker',NULL,NULL,0,80,80,2,14,14,0,1.1,1.14286,1,0,50,50,0,100,1,2000,0,1,0,0,16,0,0,1,0,23.0384,31.6778,100,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'PetAI',0,1,1.1,0.77,1,0,0,0,0,0,0,0,0,1,0,0,0,'',0),
+(60238,0,0,0,0,0,1105,0,0,0,'Hunter\'s Pet',NULL,NULL,0,80,80,0,14,14,1,1.1,1.14286,1,0,87,117,0,214,1,2000,0,1,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,'PetAI',0,3,1,1,1,0,0,0,0,0,0,0,149,1,0,0,0,'',0);
diff --git a/sql/Bots/world_bots_update.sql b/sql/Bots/world_bots_update.sql
new file mode 100644
index 0000000..e312de0
--- /dev/null
+++ b/sql/Bots/world_bots_update.sql
@@ -0,0 +1,13 @@
+-- OPTIONAL UPDATE
+-- Updated values I thought should be closer to the bots values at least for my server.
+-- You can create your own values to be in line with your own server if these are not acceptable.
+
+UPDATE `creature_template` SET mindmg:=150, maxdmg:=350, attackpower:=300, baseattacktime:=2000, rangeattacktime:=2000, minrangedmg:=0, maxrangedmg:=0, rangedattackpower:=0, speed_walk:=1.5, speed_run:=1.5, resistance1:=100, resistance2:=100, resistance3:=100, resistance4:=100, resistance5:=100, resistance6:=100, InhabitType:=3, health_mod:=2, mana_mod:=2.7, armor_mod:=2, mechanic_immune_mask:=0, flags_extra:=0, AIName='' where entry >= 60000 && entry < 60239 and subname='Druid Bot';
+UPDATE `creature_template` SET mindmg:=700, maxdmg:=900, attackpower:=2700, baseattacktime:=2000, rangeattacktime:=2000, minrangedmg:=950, maxrangedmg:=1050, rangedattackpower:=3000, speed_walk:=1.5, speed_run:=1.5, resistance1:=100, resistance2:=100, resistance3:=100, resistance4:=100, resistance5:=100, resistance6:=100, InhabitType:=3, health_mod:=2, mana_mod:=2.7, armor_mod:=2, mechanic_immune_mask:=0, flags_extra:=0, AIName='' where entry >= 60000 && entry < 60239 and subname='Hunter Bot';
+UPDATE `creature_template` SET mindmg:=125, maxdmg:=350, attackpower:=70, baseattacktime:=2000, rangeattacktime:=2000, minrangedmg:=500, maxrangedmg:=900, rangedattackpower:=50, resistance1:=100, speed_walk:=1.5, speed_run:=1.5, resistance2:=100, resistance3:=100, resistance4:=100, resistance5:=100, resistance6:=100, InhabitType:=3, health_mod:=2, mana_mod:=2.7, armor_mod:=2, mechanic_immune_mask:=0, flags_extra:=0, AIName='' where entry >= 60000 && entry < 60239 and subname='Mage Bot';
+UPDATE `creature_template` SET mindmg:=200, maxdmg:=400, attackpower:=550, baseattacktime:=20000, rangeattacktime:=2000, minrangedmg:=0, maxrangedmg:=0, rangedattackpower:=80, resistance1:=100, speed_walk:=1.5, speed_run:=1.5, resistance2:=100, resistance3:=100, resistance4:=100, resistance5:=100, resistance6:=100, InhabitType:=3, health_mod:=2, mana_mod:=2.7, armor_mod:=2, mechanic_immune_mask:=0, flags_extra:=0, AIName='' where entry >= 60000 && entry < 60239 and subname='Paladin Bot';
+UPDATE `creature_template` SET mindmg:=100, maxdmg:=300, attackpower:=60, baseattacktime:=2000, rangeattacktime:=2000, minrangedmg:=300, maxrangedmg:=600, rangedattackpower:=50, resistance1:=100, speed_walk:=1.5, speed_run:=1.5, resistance2:=100, resistance3:=100, resistance4:=100, resistance5:=100, resistance6:=100, InhabitType:=3, health_mod:=2, mana_mod:=2.7, armor_mod:=2, mechanic_immune_mask:=0, flags_extra:=0, AIName='' where entry >= 60000 && entry < 60239 and subname='Priest Bot';
+UPDATE `creature_template` SET mindmg:=1200, maxdmg:=1500, attackpower:=4500, baseattacktime:=2000, rangeattacktime:=2000, minrangedmg:=900, maxrangedmg:=1100, rangedattackpower:=4100, speed_walk:=1.5, speed_run:=1.5, resistance1:=100, resistance2:=100, resistance3:=100, resistance4:=100, resistance5:=100, resistance6:=100, InhabitType:=3, health_mod:=2, mana_mod:=2.7, armor_mod:=2, mechanic_immune_mask:=0, flags_extra:=0, AIName='' where entry >= 60000 && entry < 60239 and subname='Rogue Bot';
+UPDATE `creature_template` SET mindmg:=150, maxdmg:=350, attackpower:=350, baseattacktime:=2000, rangeattacktime:=2000, minrangedmg:=0, maxrangedmg:=0, rangedattackpower:=70, speed_walk:=1.5, speed_run:=1.5, resistance1:=100, resistance2:=100, resistance3:=100, resistance4:=100, resistance5:=100, resistance6:=100, InhabitType:=3, health_mod:=2, mana_mod:=2.7, armor_mod:=2, mechanic_immune_mask:=0, flags_extra:=0, AIName='' where entry >= 60000 && entry < 60239 and subname='Shaman Bot';
+UPDATE `creature_template` SET mindmg:=225, maxdmg:=375, attackpower:=90, baseattacktime:=2000, rangeattacktime:=2000, minrangedmg:=500, maxrangedmg:=900, rangedattackpower:=60, speed_walk:=1.5, speed_run:=1.5, resistance1:=100, resistance2:=100, resistance3:=100, resistance4:=100, resistance5:=100, resistance6:=100, InhabitType:=3, health_mod:=2, mana_mod:=2.7, armor_mod:=2, mechanic_immune_mask:=0, flags_extra:=0, AIName='' where entry >= 60000 && entry < 60239 and subname='Warlock Bot';
+UPDATE `creature_template` SET mindmg:=800, maxdmg:=1000, attackpower:=4000, baseattacktime:=2000, rangeattacktime:=2000, minrangedmg:=425, maxrangedmg:=625, rangedattackpower:=250, speed_walk:=1.5, speed_run:=1.5, resistance1:=100, resistance2:=100, resistance3:=100, resistance4:=100, resistance5:=100, resistance6:=100, InhabitType:=3, health_mod:=2, mana_mod:=2.7, armor_mod:=2, mechanic_immune_mask:=0, flags_extra:=0, AIName='' where entry >= 60000 && entry < 60239 and subname='Warrior Bot';
diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt
index e1b0921..7cd8acf 100644
--- a/sql/CMakeLists.txt
+++ b/sql/CMakeLists.txt
@@ -15,6 +15,7 @@ if( WITH_SQL )
       scripts
       base
       create
+      Bots
     DESTINATION
       shared/trinity/sql
   )
diff --git a/src/server/game/AI/Bots/PlayerbotAI.cpp b/src/server/game/AI/Bots/PlayerbotAI.cpp
new file mode 100644
index 0000000..1b944d7
--- /dev/null
+++ b/src/server/game/AI/Bots/PlayerbotAI.cpp
@@ -0,0 +1,4498 @@
+#include "Common.h"
+#include "Database/DatabaseEnv.h"
+#include "DBCStores.h"
+#include "World.h"
+#include "SpellMgr.h"
+#include "PlayerbotAI.h"
+#include "PlayerbotPriestAI.h"
+#include "PlayerbotWarriorAI.h"
+#include "PlayerbotShamanAI.h"
+#include "PlayerbotRogueAI.h"
+#include "PlayerbotPaladinAI.h"
+#include "PlayerbotMageAI.h"
+#include "PlayerbotDruidAI.h"
+#include "PlayerbotWarlockAI.h"
+#include "PlayerbotHunterAI.h"
+#include "PlayerbotDeathKnightAI.h"
+#include "Player.h"
+#include "ObjectMgr.h"
+#include "Chat.h"
+#include "WorldPacket.h"
+#include "Spell.h"
+#include "Unit.h"
+#include "Group.h"
+#include "Player.h"
+#include "SpellAuras.h"
+#include "SpellAuraEffects.h"
+#include "SharedDefines.h"
+#include "GossipDef.h"
+#include "Config.h"
+#include <ctime>
+
+bool ChatHandler::BotHandleQuestAdd(const char *args)
+{
+    Player* player = getSelectedPlayer();
+    if (!player)
+    {
+        SendSysMessage(LANG_NO_CHAR_SELECTED);
+        SetSentErrorMessage(true);
+        return false;
+    }
+
+    // .addquest #entry'
+    // number or [name] Shift-click form |color|Hquest:quest_id:quest_level|h[name]|h|r
+    char* cId = extractKeyFromLink((char*)args,"Hquest");
+    if (!cId)
+        return false;
+
+    uint32 entry = atol(cId);
+
+    Quest const* pQuest = sObjectMgr->GetQuestTemplate(entry);
+
+    if (!pQuest)
+    {
+        PSendSysMessage(LANG_COMMAND_QUEST_NOTFOUND,entry);
+        SetSentErrorMessage(true);
+        return false;
+    }
+
+    // check item starting quest (it can work incorrectly if added without item in inventory)
+    for (uint32 id = 0; id < sItemStorage.MaxEntry; id++)
+    {
+        ItemPrototype const *pProto = sItemStorage.LookupEntry<ItemPrototype>(id);
+        if (!pProto)
+            continue;
+
+        if (pProto->StartQuest == entry)
+        {
+            PSendSysMessage(LANG_COMMAND_QUEST_STARTFROMITEM, entry, pProto->ItemId);
+            SetSentErrorMessage(true);
+            return false;
+        }
+    }
+
+    // ok, normal (creature/GO starting) quest
+    if (player->CanAddQuest(pQuest, true))
+    {
+        player->AddQuest(pQuest, NULL);
+
+        if (player->CanCompleteQuest(entry))
+            player->CompleteQuest(entry);
+    }
+
+    return true;
+}
+
+bool ChatHandler::BotHandleQuestRemove(const char *args)
+{
+    Player* player = getSelectedPlayer();
+    if (!player)
+    {
+        SendSysMessage(LANG_NO_CHAR_SELECTED);
+        SetSentErrorMessage(true);
+        return false;
+    }
+
+    // .removequest #entry'
+    // number or [name] Shift-click form |color|Hquest:quest_id:quest_level|h[name]|h|r
+    char* cId = extractKeyFromLink((char*)args,"Hquest");
+    if (!cId)
+        return false;
+
+    uint32 entry = atol(cId);
+
+    Quest const* pQuest = sObjectMgr->GetQuestTemplate(entry);
+
+    if (!pQuest)
+    {
+        PSendSysMessage(LANG_COMMAND_QUEST_NOTFOUND, entry);
+        SetSentErrorMessage(true);
+        return false;
+    }
+
+    // remove all quest entries for 'entry' from quest log
+    for (uint8 slot = 0; slot < MAX_QUEST_LOG_SIZE; ++slot)
+    {
+        uint32 quest = player->GetQuestSlotQuestId(slot);
+        if (quest == entry)
+        {
+            player->SetQuestSlot(slot,0);
+
+            // we ignore unequippable quest items in this case, its' still be equipped
+            player->TakeQuestSourceItem(quest, false);
+        }
+    }
+
+    // set quest status to not started (will updated in DB at next save)
+    player->SetQuestStatus(entry, QUEST_STATUS_NONE);
+
+    SendSysMessage(LANG_COMMAND_QUEST_REMOVED);
+    return true;
+}
+
+
+/*
+* Packets often compress the GUID (global unique identifier)
+* This function extracts the guid from the packet and decompresses it.
+* The first word (8 bits) in the packet represents how many words in the following packet(s) are part of
+* the guid and what weight they hold. I call it the mask. For example) if mask is 01001001,
+* there will be only 3 words. The first word is shifted to the left 0 times,
+* the second is shifted 3 times, and the third is shifted 6.
+*/
+uint64 extractGuid(WorldPacket &packet)
+{
+    uint8 mask; packet >> mask;
+    uint64 guid = 0;
+    uint8 bit = 0;
+    uint8 testMask = 1;
+    while(true)
+    {
+        if(mask & testMask)
+        {
+            uint8 word; packet >> word;
+            guid += (word << bit);
+        }
+        if(bit == 7) break;
+        ++bit;
+        testMask <<= 1;
+    }
+    return guid;
+}
+
+//ChatHandler already implements some useful commands the master can call on bots
+//These commands are protected inside the ChatHandler class so this class provides access to the commands
+//we'd like to call on our bots
+class PlayerbotChatHandler : protected ChatHandler
+{
+    public:
+        explicit PlayerbotChatHandler(Player *pMasterPlayer) : ChatHandler(pMasterPlayer){}
+
+        bool revive(const Player &botPlayer){ return HandleReviveCommand(botPlayer.GetName()); }
+        bool teleport(const Player &botPlayer){ return HandleSummonCommand(botPlayer.GetName()); }
+        bool teleport(Player &botPlayer, const WorldLocation &loc){ return botPlayer.TeleportTo(loc,TELE_TO_GM_MODE); }
+        bool uninvite(const char *str){ return HandlePlayerbotCommand(str); }
+        void sysmessage(const char *str){ SendSysMessage(str); }
+        bool acceptQuest(const char *str){ return BotHandleQuestAdd(str); }
+        bool abandonQuest(const char *str) { return BotHandleQuestRemove(str); }
+};
+
+PlayerbotAI::PlayerbotAI(Player *const master, Player *const bot): m_master(master), m_bot(bot),
+m_ignoreAIUpdatesUntilTime(0), m_combatOrder(ORDERS_NONE), m_ScenarioType(SCENARIO_PVEEASY),
+m_TimeDoneEating(0), m_TimeDoneDrinking(0), m_CurrentlyCastingSpellId(0), m_IsFollowingMaster(true),
+m_spellIdCommand(0), m_targetGuidCommand(0), m_classAI(0), isLooting(false), m_TimeRessurect(0),
+m_FeastSpamTimer(0)
+{
+
+    //If the player have a group, it's possible to add the bot.
+    if(master->GetGroup())
+    {
+        Group *m_group = master->GetGroup();
+        bool inGroup = false;
+        Group::MemberSlotList members = m_group->GetMemberSlots();
+
+        if(!m_group->IsFull() ||
+            m_group->IsMember(bot->GetGUID()) )
+        {
+            //check that bot is not already in the group, ie from a server crash
+            Group::MemberSlotList const &groupSlot = master->GetGroup()->GetMemberSlots();
+            for(Group::member_citerator itr = groupSlot.begin(); itr != groupSlot.end(); itr++)
+            {
+                Player *tPlayer = master->GetObjPlayer(itr->guid);
+                if(itr->guid == bot->GetGUID()) inGroup = true;
+            }
+            if(!inGroup) m_group->AddMember(bot->GetGUID(), bot->GetName());
+        } else {
+            //group is full so can't add bot
+            bot->Say("Group is full!", LANG_UNIVERSAL);
+        }
+    } else {
+        Group *m_group = new Group;
+        if(!m_group->Create(master->GetGUID(), master->GetName()))
+        {
+            delete m_group;
+            return;
+        }
+        sObjectMgr->AddGroup(m_group);
+        if(!m_group->IsFull()) m_group->AddMember(bot->GetGUID(), bot->GetName());
+    }
+
+    //get class specific AI
+    switch(m_bot->getClass())
+    {
+        case CLASS_PRIEST:
+            m_classAI = (PlayerbotClassAI *)new PlayerbotPriestAI(master, m_bot, this);
+            break;
+        case CLASS_WARRIOR:
+            m_classAI = (PlayerbotClassAI *)new PlayerbotWarriorAI(master, m_bot, this);
+            break;
+        case CLASS_SHAMAN:
+            m_classAI = (PlayerbotClassAI *)new PlayerbotShamanAI(master, m_bot, this);
+            break;
+        case CLASS_ROGUE:
+            m_classAI = (PlayerbotClassAI *)new PlayerbotRogueAI(master, m_bot, this);
+            break;
+        case CLASS_PALADIN:
+            m_classAI = (PlayerbotClassAI *)new PlayerbotPaladinAI(master, m_bot, this);
+            break;
+        case CLASS_MAGE:
+            m_classAI = (PlayerbotClassAI *)new PlayerbotMageAI(master, m_bot, this);
+            break;
+        case CLASS_DRUID:
+            m_classAI = (PlayerbotClassAI *)new PlayerbotDruidAI(master, m_bot, this);
+            break;
+        case CLASS_WARLOCK:
+            m_classAI = (PlayerbotClassAI *)new PlayerbotWarlockAI(master, m_bot, this);
+            break;
+        case CLASS_HUNTER:
+            m_classAI = (PlayerbotClassAI *)new PlayerbotHunterAI(master, m_bot, this);
+            break;
+        case CLASS_DEATH_KNIGHT:
+            m_classAI = (PlayerbotClassAI *)new PlayerbotDeathKnightAI(master, m_bot, this);
+            break;
+    }
+
+    //load config variables
+    m_followDistanceMin = sConfig->GetFloatDefault("Bot.FollowDistanceMin", 0.5f);
+    m_followDistanceMax = sConfig->GetFloatDefault("Bot.FollowDistanceMax", 3.0f);
+    m_playerBotsFly = sConfig->GetIntDefault("Bot.PlayerBotsFly", 0);
+
+    SetQuestNeedItems();
+    m_needEmblemList.clear();
+    m_needEmblemList[29434] = 200; // Badge of Justice
+    m_needEmblemList[40752] = 200; // Emblem of Heroism
+    m_needEmblemList[40753] = 200; // Emblem of Valor
+    m_needEmblemList[45624] = 200; // Emblem of Conquest
+    m_needEmblemList[47241] = 200; // Emblem of Triumph
+    m_needEmblemList[49426] = 200; // Emblem of Frost
+    m_needEmblemList[44990] = 200; // Champion's Seal
+    HandleCommand("help", *m_master);
+}
+PlayerbotAI::~PlayerbotAI(){}
+
+//finds spell ID for matching substring args
+//in priority of full text match, spells not taking reagents, and highest rank
+uint32 PlayerbotAI::getSpellId(const char *args, bool master) const
+{
+    if(!*args) return 0;
+
+    std::string namepart = args;
+    std::wstring wnamepart;
+
+    if(!Utf8toWStr(namepart, wnamepart)) return 0;
+
+    //converting string that we try to find to lower case
+    wstrToLower(wnamepart);
+
+    int loc = 0;
+    if(master) loc = m_master->GetSession()->GetSessionDbcLocale();
+    else loc = m_bot->GetSession()->GetSessionDbcLocale();
+
+    uint32 foundSpellId = 0;
+    bool foundExactMatch = false;
+    bool foundMatchUsesNoReagents = false;
+
+    for(PlayerSpellMap::iterator itr = m_bot->GetSpellMap().begin(); itr != m_bot->GetSpellMap().end(); ++itr)
+    {
+        uint32 spellId = itr->first;
+
+        if(itr->second->state == PLAYERSPELL_REMOVED || itr->second->disabled || IsPassiveSpell(spellId)) continue;
+
+        const SpellEntry *pSpellInfo = sSpellStore.LookupEntry(spellId);
+        if(!pSpellInfo) continue;
+
+        const std::string name = pSpellInfo->SpellName[loc];
+        if(name.empty() || !Utf8FitTo(name, wnamepart)) continue;
+
+        bool isExactMatch = (name.length() == wnamepart.length()) ? true : false;
+        bool usesNoReagents = (pSpellInfo->Reagent[0] <=  0) ? true : false;
+
+        //if we already found a spell
+        bool useThisSpell = true;
+        if(foundSpellId > 0)
+        {
+            if(isExactMatch && !foundExactMatch){}
+            else if(usesNoReagents && !foundMatchUsesNoReagents){}
+            else if(spellId > foundSpellId){}
+            else useThisSpell = false;
+        }
+        if(useThisSpell)
+        {
+            foundSpellId = spellId;
+            foundExactMatch = isExactMatch;
+            foundMatchUsesNoReagents = usesNoReagents;
+        }
+    }
+    return foundSpellId;
+}
+
+uint32 PlayerbotAI::getSpellIdExact(const char *args, bool includePassive, bool master)
+{
+    if(!*args) return 0;
+    std::string namepart = args;
+    int loc = 0;
+    if(master) loc = m_master->GetSession()->GetSessionDbcLocale();
+    else loc = m_bot->GetSession()->GetSessionDbcLocale();
+    uint32 foundSpellId = (uint32) 0;
+    bool foundMatchUsesNoReagents = false;
+
+    for(PlayerSpellMap::iterator itr = m_bot->GetSpellMap().begin(); itr != m_bot->GetSpellMap().end(); ++itr)
+    {
+        uint32 spellId = itr->first;
+        if(itr->second->state == PLAYERSPELL_REMOVED || itr->second->disabled || ( !includePassive && IsPassiveSpell(spellId))) continue;
+        const SpellEntry *pSpellInfo = sSpellStore.LookupEntry(spellId);
+        if(!pSpellInfo) continue;
+        if(pSpellInfo->Effect[0] == SPELL_EFFECT_LEARN_SPELL) continue; //This is a learn spell
+
+        const std::string name = pSpellInfo->SpellName[loc];
+        if(name.empty()) continue;
+        if(strcmp(name.c_str(),namepart.c_str())) continue;
+        if(pSpellInfo->Reagent[0] <=  0 && !foundMatchUsesNoReagents){ foundSpellId = spellId; foundMatchUsesNoReagents = true; }
+        else if(spellId > foundSpellId) { foundSpellId = spellId; }
+    }
+    //sLog->outDebug("PBot Class %u - Found in Search - [%u/%s]", m_bot->getClass(), foundSpellId, namepart.c_str());
+    if (foundSpellId > 70000) { sLog->outDebug("CRITICAL: PBot Class %u - Weird Spell in Search - [%u/%s]", m_bot->getClass(), foundSpellId, namepart.c_str()); }
+    return foundSpellId;
+}
+
+// finds quest ID for matching substring args
+uint32 PlayerbotAI::getQuestId(const char* args, bool remove) const
+{
+    if (!*args)
+        return 0;
+
+    std::string namepart = args;
+    std::wstring wnamepart;
+
+    if (!Utf8toWStr(namepart,wnamepart))
+        return 0;
+
+    // converting string that we try to find to lower case
+    wstrToLower(wnamepart);
+    uint32 questId = 0;
+    uint32 foundQuestId = 0;
+    bool foundExactMatch = false;
+    if (!m_questsSeen.empty() && !remove)
+    {
+        for (BotQuestsSeen::const_iterator iter = m_questsSeen.begin(); iter != m_questsSeen.end(); ++iter)
+        {
+            uint32 questId = iter->first;
+            const std::string name = iter->second;
+            if (name.empty() || !Utf8FitTo(name, wnamepart))
+                continue;
+            bool isExactMatch = (name.length() == wnamepart.length()) ? true : false;
+            // if we already found a quest
+            bool useThisQuest = true;
+            if (foundQuestId > 0)
+            {
+                if (isExactMatch && ! foundExactMatch)
+                {
+                }
+                else if (questId > foundQuestId)
+                {
+                }
+                else
+                    useThisQuest = false;
+            }
+            if (useThisQuest)
+            {
+                foundQuestId = questId;
+                foundExactMatch = isExactMatch;
+            }
+        }
+    }
+    else if (remove)
+    {
+        for (QuestStatusMap::iterator iter=m_bot->getQuestStatusMap().begin(); iter!=m_bot->getQuestStatusMap().end(); ++iter)
+        {
+            const Quest *qInfo = sObjectMgr->GetQuestTemplate(iter->first);
+            if (!qInfo) continue;
+
+            uint32 questId = qInfo->GetQuestId();
+            const std::string name = qInfo->GetTitle();
+            if (name.empty() || !Utf8FitTo(name, wnamepart))
+                continue;
+
+            bool isExactMatch = (name.length() == wnamepart.length()) ? true : false;
+            // if we already found a quest
+            bool useThisQuest = true;
+            if (foundQuestId > 0)
+            {
+                if (isExactMatch && ! foundExactMatch)
+                {
+                }
+                else if (questId > foundQuestId)
+                {
+                }
+                else
+                    useThisQuest = false;
+            }
+            if (useThisQuest)
+            {
+                foundQuestId = questId;
+                foundExactMatch = isExactMatch;
+            }
+        }
+    }
+    return foundQuestId;
+}
+
+
+/*
+* Send a list of equipment that is in bot's inventor that is currently unequipped.
+* This is called when the master is inspecting the bot.
+*/
+void PlayerbotAI::SendNotEquipList(Player &player)
+{
+    //find all unequipped items and put them in
+    //a vector of dynamically created lists where the vector index is from 0-18
+    //and the list contains Item *that can be equipped to that slot
+    //Note: each dynamically created list in the vector must be deleted at end
+    //so NO EARLY RETURNS!
+    //see enum EquipmentSlots in Player.h to see what equipment slot each index in vector
+    //is assigned to. (The first is EQUIPMENT_SLOT_HEAD = 0, and last is EQUIPMENT_SLOT_TABARD = 18)
+
+    std::list<Item *> *equip[19];
+    for(uint8 i = 0; i < 19; ++i) equip[i] = NULL;
+
+    //list out items in main backpack
+    for(uint8 slot = INVENTORY_SLOT_ITEM_START; slot < INVENTORY_SLOT_ITEM_END; slot++)
+    {
+        Item *const pItem = m_bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot);
+        if(!pItem)
+            continue;
+
+        uint16 dest;
+        uint8 msg = m_bot->CanEquipItem(NULL_SLOT, dest, pItem, !pItem->IsBag());
+        if(msg != EQUIP_ERR_OK) continue;
+
+        //the dest looks like it includes the old loc in the 8 higher bits
+        //so casting it to a uint8 strips them
+        uint8 equipSlot = uint8(dest);
+        if(!(equipSlot >= 0 && equipSlot < 19)) continue;
+
+        //create a list if one doesn't already exist
+        if(equip[equipSlot] == NULL)
+            equip[equipSlot] = new std::list<Item *>;
+
+        std::list<Item *> *itemListForEqSlot = equip[equipSlot];
+
+        itemListForEqSlot->push_back(pItem);
+    }
+
+    //list out items in other removable backpacks
+    for(uint8 bag = INVENTORY_SLOT_BAG_START; bag < INVENTORY_SLOT_BAG_END; ++bag)
+    {
+        const Bag *const pBag = (Bag *)m_bot->GetItemByPos(INVENTORY_SLOT_BAG_0, bag);
+        if(pBag)
+        {
+            for(uint8 slot = 0; slot < pBag->GetBagSize(); ++slot)
+            {
+                Item *const pItem = m_bot->GetItemByPos(bag, slot);
+                if(!pItem)
+                    continue;
+
+                uint16 equipSlot;
+                uint8 msg = m_bot->CanEquipItem(NULL_SLOT, equipSlot, pItem, !pItem->IsBag());
+                if(msg != EQUIP_ERR_OK)
+                    continue;
+                if(!(equipSlot >= 0 && equipSlot < 19))
+                    continue;
+
+                //create a list if one doesn't already exist
+                if(equip[equipSlot] == NULL)
+                    equip[equipSlot] = new std::list<Item *>;
+
+                std::list<Item *> *itemListForEqSlot = equip[equipSlot];
+                itemListForEqSlot->push_back(pItem);
+            }
+        }
+    }
+
+    TellMaster("Here's all the items in my inventory that I can equip:");
+    ChatHandler ch(m_master);
+
+    const std::string descr[] = { "head", "neck", "shoulders", "body", "chest",
+    "waist", "legs", "feet", "wrists", "hands", "finger1", "finger2",
+    "trinket1", "trinket2", "back", "mainhand", "offhand", "ranged", "tabard" };
+
+    //now send client all items that can be equipped by slot
+    for(uint8 equipSlot = 0; equipSlot < 19; ++equipSlot)
+    {
+        if(equip[equipSlot] == NULL) continue;
+        std::list<Item *> *itemListForEqSlot = equip[equipSlot];
+        std::ostringstream out;
+        out << descr[equipSlot] << ": ";
+        for(std::list<Item *>::iterator it = itemListForEqSlot->begin(); it != itemListForEqSlot->end(); ++it)
+        {
+            const ItemPrototype *const pItemProto = (*it)->GetProto();
+            out << " |cffffffff|Hitem:" << pItemProto->ItemId
+            << ":0:0:0:0:0:0:0" << "|h[" << pItemProto->Name1
+            << "]|h|r";
+        }
+        ch.SendSysMessage(out.str().c_str());
+        delete itemListForEqSlot; //delete list of Item *
+    }
+}
+
+void PlayerbotAI::HandleMasterOutgoingPacket(const WorldPacket &packet, WorldSession &masterSession)
+{
+    /*
+    const char *oc = LookupOpcodeName(packet.GetOpcode());
+
+    std::ostringstream out;
+    out << "HandleMasterOutgoingPacket: " << oc;
+    sLog->outError(out.str().c_str());
+    */
+}
+
+void PlayerbotAI::HandleMasterIncomingPacket(const WorldPacket &packet, WorldSession &masterSession)
+{
+    switch(packet.GetOpcode())
+    {
+		case CMSG_SET_SELECTION:
+		{
+			//sLog->outError("cmsg_set_selection");
+			return;
+		}
+
+        //If master inspects one of his bots, give the master useful info in chat window
+        //such as inventory that can be equipped
+        case CMSG_INSPECT:
+        {
+            WorldPacket p(packet);
+            p.rpos(0); //reset reader
+            uint64 guid; p >> guid;
+            Player *const bot = masterSession.GetPlayerBot(guid);
+            if(!bot) return;
+            bot->GetPlayerbotAI()->SendNotEquipList(*bot);
+        }
+
+        case CMSG_PUSHQUESTTOPARTY:
+        {
+            WorldPacket p(packet);
+            p.rpos(0); //reset reader
+            uint32 quest; p >> quest;
+            Player *pPlayer = masterSession.GetPlayer();
+            Quest const *pQuest = sObjectMgr->GetQuestTemplate(quest);
+
+            if(pQuest)
+                for(PlayerBotMap::const_iterator it = masterSession.GetPlayerBotsBegin(); it != masterSession.GetPlayerBotsEnd(); ++it)
+                {
+                    Player *const bot = it->second;
+                    uint64 guid = it->first;
+                    uint32 unk1 = 0;
+
+                    WorldPacket data(MSG_QUEST_PUSH_RESULT, (8+4+4));
+                    //data << guid;
+                    data << pPlayer->GetGUID();
+                    data << quest;
+                    data << unk1;
+
+                    bot->GetSession()->HandleQuestgiverAcceptQuestOpcode(data);
+                    bot->GetPlayerbotAI()->SetQuestNeedItems();
+                }
+            return;
+        }
+
+        //handle emotes from the master
+        //case CMSG_EMOTE:
+        case CMSG_TEXT_EMOTE:
+        {
+            WorldPacket p(packet);
+            p.rpos(0); //reset reader
+            uint32 emoteNum;
+//            uint64 guid;
+            p >> emoteNum;
+
+            switch(emoteNum)
+            {
+                case TEXTEMOTE_BONK:
+                {
+                    Player *const pPlayer = masterSession.GetPlayerBot(masterSession.GetPlayer()->GetSelection());
+                    if(!pPlayer || !pPlayer->GetPlayerbotAI()) return;
+                    PlayerbotAI *const pBot = pPlayer->GetPlayerbotAI();
+
+                    ChatHandler ch(masterSession.GetPlayer());
+
+                    {
+                    std::ostringstream out;
+                    out << "clock(): " << (getMSTime())
+                    << " m_ignoreAIUpdatesUntilTime: " << (pBot->m_ignoreAIUpdatesUntilTime);
+                    ch.SendSysMessage(out.str().c_str());
+                    }{
+                    std::ostringstream out;
+                    out << "m_TimeDoneEating: " << pBot->m_TimeDoneEating
+                    << " m_TimeDoneDrinking: " << pBot->m_TimeDoneDrinking;
+                    ch.SendSysMessage(out.str().c_str());
+                    }{
+                    std::ostringstream out;
+                    out << "m_CurrentlyCastingSpellId: " << pBot->m_CurrentlyCastingSpellId;
+                    ch.SendSysMessage(out.str().c_str());
+                    }{
+                    std::ostringstream out;
+                    out << "m_IsFollowingMaster: " << pBot->m_IsFollowingMaster;
+                    ch.SendSysMessage(out.str().c_str());
+                    }{
+                    std::ostringstream out;
+                    out << "IsBeingTeleported(): " << pBot->m_bot->IsBeingTeleported();
+                    ch.SendSysMessage(out.str().c_str());
+                    }{
+                    std::ostringstream out;
+                    bool tradeActive = (pBot->m_bot->GetTrader()) ? true : false;
+                    out << "tradeActive: " << tradeActive;
+                    ch.SendSysMessage(out.str().c_str());
+                    }{
+                    std::ostringstream out;
+                    out << "IsCharmed(): " << pBot->m_bot->isCharmed();
+                    ch.SendSysMessage(out.str().c_str());
+                    }{
+                    std::ostringstream out;
+                    out << "IsInCombat(): " << pBot->m_bot->isInCombat();
+                    ch.SendSysMessage(out.str().c_str());
+                    }{
+                    std::ostringstream out;
+                    out << "isLooting: " << pBot->isLooting;
+                    ch.SendSysMessage(out.str().c_str());
+                    }
+                    {
+                    std::ostringstream out;
+                    out << "isPulling: " << pBot->GetClassAI()->isPulling();
+                    ch.SendSysMessage(out.str().c_str());
+                    }
+                    return;
+                }
+
+                case TEXTEMOTE_EAT:
+                case TEXTEMOTE_DRINK:
+                {
+                    Player *const bot = masterSession.GetPlayerBot(masterSession.GetPlayer()->GetSelection());
+                    if(bot) bot->GetPlayerbotAI()->Stay();
+                    else {
+                        for(PlayerBotMap::const_iterator it = masterSession.GetPlayerBotsBegin(); it != masterSession.GetPlayerBotsEnd(); ++it)
+                        {
+                            Player *const bot = it->second;
+                            bot->GetPlayerbotAI()->Feast();
+                        }
+                    }
+                    return;
+                }
+
+                //emote to stay
+                case TEXTEMOTE_STAND:
+                {
+                    Player *const bot = masterSession.GetPlayerBot(masterSession.GetPlayer()->GetSelection());
+                    if(bot) bot->GetPlayerbotAI()->Stay();
+                    else {
+                        for(PlayerBotMap::const_iterator it = masterSession.GetPlayerBotsBegin(); it != masterSession.GetPlayerBotsEnd(); ++it)
+                        {
+                            Player *const bot = it->second;
+                            bot->GetPlayerbotAI()->Stay();
+                        }
+                    }
+                    return;
+                }
+
+                //324 is the followme emote (not defined in enum)
+                //if master has bot selected then only bot follows, else all bots follow
+                case 324:
+                case TEXTEMOTE_WAVE:
+                {
+                    Player *const bot = masterSession.GetPlayerBot(masterSession.GetPlayer()->GetSelection());
+                    if(bot)
+                    {
+                        bot->GetPlayerbotAI()->Follow(*masterSession.GetPlayer());
+                        bot->GetPlayerbotAI()->SetLooting(false);
+                    } else {
+                        for(PlayerBotMap::const_iterator it = masterSession.GetPlayerBotsBegin(); it != masterSession.GetPlayerBotsEnd(); ++it)
+                        {
+                            Player *const bot = it->second;
+                            bot->GetPlayerbotAI()->Follow(*masterSession.GetPlayer());
+                            bot->GetPlayerbotAI()->SetLooting(false);
+                        }
+                    }
+                    return;
+                }
+
+                default: return;
+            }
+        } //end CMSG_TEXT_EMOTE
+
+        case CMSG_GROUP_UNINVITE:
+        {
+            WorldPacket p(packet);
+            p.rpos(0); //reset reader
+            std::string member; p >> member;
+            p.clear();
+
+            WorldPacket data(CMSG_GROUP_UNINVITE, 1);
+            PlayerbotChatHandler ch(masterSession.GetPlayer());
+            std::ostringstream out;
+            out << "remove " << member;
+            ch.uninvite(out.str().c_str());
+            return;
+        }
+
+        case CMSG_REPAIR_ITEM:
+        {
+            WorldPacket p(packet);
+            p.rpos(0); //reset reader
+            uint64 npcGUID;
+            p >> npcGUID;
+
+            Object *const pNpc = ObjectAccessor::GetObjectByTypeMask(*masterSession.GetPlayer(), npcGUID, TYPEMASK_UNIT | TYPEMASK_GAMEOBJECT);
+            if(!pNpc)
+                return;
+
+            //for all master's bots
+            for(PlayerBotMap::const_iterator it = masterSession.GetPlayerBotsBegin(); it != masterSession.GetPlayerBotsEnd(); ++it)
+            {
+                Player *const bot = it->second;
+                if(!bot->IsInMap((WorldObject*) pNpc))
+                {
+                    bot->GetPlayerbotAI()->TellMaster("I'm too far away to repair items!");
+                    continue;
+                } else {
+                    bot->GetPlayerbotAI()->TellMaster("Repairing my items.");
+                    bot->DurabilityRepairAll(false, 0.0, false);
+                }
+
+            }
+            return;
+        }
+
+        case CMSG_ACTIVATETAXIEXPRESS:
+        {
+            WorldPacket incP(packet);
+            //for all master's bots
+            for(PlayerBotMap::const_iterator it = masterSession.GetPlayerBotsBegin(); it != masterSession.GetPlayerBotsEnd(); ++it)
+            {
+
+                Player *const bot = it->second;
+                if (!bot->GetPlayerbotAI()->CanBotsFly()) return;
+
+                if(!bot->IsInMap((WorldObject*) masterSession.GetPlayer()))
+                {
+                    bot->GetPlayerbotAI()->TellMaster("I'm too far away to fly!");
+                    continue;
+                } else {
+                    WorldPacket p;
+                   // p << guid << _totalcost << node_count;
+                    bot->GetPlayerbotAI()->Stay();  // clear any movement
+                    incP.rpos(0);
+                    bot->GetSession()->HandleActivateTaxiExpressOpcode(incP);
+                }
+
+            }
+            return;
+        }
+
+        case CMSG_ACTIVATETAXI:
+        {
+            WorldPacket incP(packet);
+            //for all master's bots
+            for(PlayerBotMap::const_iterator it = masterSession.GetPlayerBotsBegin(); it != masterSession.GetPlayerBotsEnd(); ++it)
+            {
+                Player *const bot = it->second;
+                if (!bot->GetPlayerbotAI()->CanBotsFly()) return;
+
+                if(!bot->IsInMap((WorldObject*) masterSession.GetPlayer()))
+                {
+                    bot->GetPlayerbotAI()->TellMaster("I'm too far away to fly!");
+                    continue;
+                } else {
+                    WorldPacket p;
+                   // p << guid << nodes[0] << nodes[1];
+                    bot->GetPlayerbotAI()->Stay();  // clear any movement
+                    incP.rpos(0);
+                    bot->GetSession()->HandleActivateTaxiOpcode(incP);
+                }
+
+            }
+            return;
+        }
+
+        // when landing from a flight path
+        case CMSG_MOVE_SPLINE_DONE:
+        {
+            WorldPacket p(packet);
+            //p.rpos(0);  // reset reader
+
+            //for all master's bots
+            for(PlayerBotMap::const_iterator it = masterSession.GetPlayerBotsBegin(); it != masterSession.GetPlayerBotsEnd(); ++it)
+            {
+                Player *const bot = it->second;
+
+                if(!bot->IsInMap((WorldObject*) masterSession.GetPlayer()))
+                {
+                    bot->GetPlayerbotAI()->TellMaster("I'm too far away to land!");
+                    continue;
+                } else {
+                    p.rpos(0);  // reset reader
+                    p.appendPackGUID(bot->GetGUID());
+                    bot->GetSession()->HandleMoveSplineDoneOpcode(p);
+                    uint32 sourcenode = bot->m_taxi.GetTaxiSource();
+                    uint32 mountDisplayId = sObjectMgr->GetTaxiMountDisplayId(sourcenode, bot->GetTeam());
+
+                    if (mountDisplayId==0) {
+                        bot->CleanupAfterTaxiFlight();
+                        bot->GetPlayerbotAI()->Follow(*masterSession.GetPlayer());
+                    }
+                }
+
+            }
+            return;
+        }
+        case CMSG_LOOT:
+            {
+                WorldPacket p(packet);
+                p.rpos(0); // reset reader
+                uint64 cGUID;
+                p >> cGUID;
+
+                Player *m_master = masterSession.GetPlayer();
+                Creature *cToLoot = m_master->GetMap()->GetCreature(cGUID);
+                if (!cToLoot)
+                    return;
+                /* for all master's bots */
+                for (PlayerBotMap::const_iterator it = masterSession.GetPlayerBotsBegin();
+                    it != masterSession.GetPlayerBotsEnd(); ++it)
+                {
+                    Player* const bot = it->second;
+                    if (!bot->IsInMap((WorldObject*) cToLoot))
+                    {
+                        bot->GetPlayerbotAI()->TellMaster("I'm too far away to check for needed Quest Items!");
+                        continue;
+                    }
+                    else
+                    {
+                        //bot->GetPlayerbotAI()->TellMaster("Checking for needed Quest Items.");
+                        bot->GetPlayerbotAI()->AddLootGUID(cGUID);
+                        bot->GetPlayerbotAI()->DoLoot();
+                    }
+                }
+                return;
+
+            }
+            break;
+
+        case CMSG_GAMEOBJ_USE:
+            {
+                WorldPacket p(packet);
+                p.rpos(0); // reset reader
+                uint64 objGUID;
+                p >> objGUID;
+
+                Player *m_master = masterSession.GetPlayer();
+                GameObject *obj = m_master->GetMap()->GetGameObject( objGUID );
+                if( !obj )
+                    return;
+
+               for(PlayerBotMap::const_iterator it = masterSession.GetPlayerBotsBegin(); it != masterSession.GetPlayerBotsEnd(); ++it)
+                {
+                    Player* const bot = it->second;
+//sLog->outError ("gameobject type = %u", obj->GetGoType());
+                    if( obj->GetGoType() == GAMEOBJECT_TYPE_QUESTGIVER )
+                    {
+                        bot->GetPlayerbotAI()->TurnInQuests( obj );
+                    }
+                    // add other go types here, i.e.:
+                    // GAMEOBJECT_TYPE_CHEST - loot quest items of chest
+                }
+            }
+            break;
+
+        case CMSG_GAMEOBJ_REPORT_USE:
+            {
+                WorldPacket p(packet);
+                p.rpos(0); // reset reader
+                uint64 objGUID;
+                p >> objGUID;
+
+                Player *m_master = masterSession.GetPlayer();
+                GameObject *obj = m_master->GetMap()->GetGameObject( objGUID );
+                if( !obj )
+                    return;
+
+                //Object* const pNpc = ObjectAccessor::GetObjectByTypeMask(*masterSession.GetPlayer(), npcGUID, TYPEMASK_UNIT|TYPEMASK_GAMEOBJECT);
+                //if (!pNpc) return;
+            /* for all master's bots */
+            for (PlayerBotMap::const_iterator it = masterSession.GetPlayerBotsBegin();
+                it != masterSession.GetPlayerBotsEnd(); ++it)
+            {
+                Player* const bot = it->second;
+                if (!bot->IsInMap((WorldObject*) obj))
+                {
+                    bot->GetPlayerbotAI()->TellMaster("I'm too far away to check for needed Quest Items!");
+                    continue;
+                }
+                else
+                {
+                    //bot->GetPlayerbotAI()->TellMaster("Checking for needed Quest Items.");
+                    bot->GetPlayerbotAI()->AddLootGUID(objGUID);
+                    bot->GetPlayerbotAI()->DoLoot();
+                }
+            }
+            return;
+        }
+
+        //if master talks to an NPC
+        case CMSG_GOSSIP_HELLO:
+        case CMSG_QUESTGIVER_HELLO:
+            {
+                WorldPacket p(packet);
+                p.rpos(0); //reset reader
+                uint64 npcGUID;
+                p >> npcGUID;
+
+                Player *m_master = masterSession.GetPlayer();
+                //Object *const pNpc = ObjectAccessor::GetObjectByTypeMask(*masterSession.GetPlayer(), npcGUID, TYPEMASK_UNIT | TYPEMASK_GAMEOBJECT);
+                WorldObject* pNpc = ObjectAccessor::GetWorldObject( *m_master, npcGUID );
+                if(!pNpc)
+                    return;
+
+                // if its a flight master
+                if(pNpc->HasFlag( UNIT_NPC_FLAGS, UNIT_NPC_FLAG_FLIGHTMASTER ))
+                {
+                    for(PlayerBotMap::const_iterator it = masterSession.GetPlayerBotsBegin(); it != masterSession.GetPlayerBotsEnd(); ++it)
+                    {
+                        Player *const bot = it->second;
+                        if (bot->GetSession()->SendLearnNewTaxiNode((Creature*)pNpc))
+                            bot->GetPlayerbotAI()->TellMaster("Learned a new path.");
+                    }
+                    return;
+                }
+
+                // for all master's bots
+                for(PlayerBotMap::const_iterator it = masterSession.GetPlayerBotsBegin(); it != masterSession.GetPlayerBotsEnd(); ++it)
+                {
+                    Player* const bot = it->second;
+                    bot->GetPlayerbotAI()->TurnInQuests( pNpc );
+                    bot->GetPlayerbotAI()->SetQuestNeedItems();
+
+                    bot->TalkedToCreature(pNpc->GetEntry(), pNpc->GetGUID());
+                }
+
+                return;
+        }
+
+        case CMSG_QUESTGIVER_STATUS_MULTIPLE_QUERY:
+        {
+            WorldPacket p(packet);
+            p.rpos(0); // reset reader
+           if (!masterSession.GetPlayer()->GetSelection()) return;
+
+            //for all master's bots
+            for(PlayerBotMap::const_iterator it = masterSession.GetPlayerBotsBegin(); it != masterSession.GetPlayerBotsEnd(); ++it)
+            {
+                Player* const bot = it->second;
+                p.rpos(0); // reset reader
+                bot->GetSession()->HandleQuestgiverStatusMultipleQuery(p);
+            }
+
+
+        return;
+        }
+
+        // if master accepts a quest, bots should also try to accept quest
+        case CMSG_QUESTGIVER_ACCEPT_QUEST:
+        {
+            WorldPacket p(packet);
+            p.rpos(0); // reset reader
+            uint64 guid;
+            uint32 quest;
+            p >> guid >> quest;
+            Quest const* qInfo = sObjectMgr->GetQuestTemplate(quest);
+            if (qInfo)
+            {
+                //for all master's bots
+                for(PlayerBotMap::const_iterator it = masterSession.GetPlayerBotsBegin(); it != masterSession.GetPlayerBotsEnd(); ++it)
+                {
+                    Player* const bot = it->second;
+
+                    if (bot->GetQuestStatus(quest) == QUEST_STATUS_COMPLETE)
+                        bot->GetPlayerbotAI()->TellMaster("I already completed that quest.");
+                    else if (! bot->CanTakeQuest(qInfo, false))
+                    {
+                        if (! bot->SatisfyQuestStatus(qInfo, false))
+                            bot->GetPlayerbotAI()->TellMaster("I already have that quest.");
+                        else
+                            bot->GetPlayerbotAI()->TellMaster("I can't take that quest.");
+                    }
+                    else if (! bot->SatisfyQuestLog(false))
+                        bot->GetPlayerbotAI()->TellMaster("My quest log is full.");
+                    else if (! bot->CanAddQuest(qInfo, false))
+                        bot->GetPlayerbotAI()->TellMaster("I can't take that quest because it requires that I take items, but my bags are full!");
+
+                    else
+                    {
+                        p.rpos(0); // reset reader
+                        bot->GetSession()->HandleQuestgiverAcceptQuestOpcode(p);
+                        bot->GetPlayerbotAI()->TellMaster("Got the quest.");
+                        bot->GetPlayerbotAI()->SetQuestNeedItems();
+                    }
+                }
+            }
+
+            return;
+        }
+
+        case CMSG_LIST_INVENTORY:
+            {
+                uint64 npcGUID;
+                WorldPacket p1(packet);
+                p1.rpos(0); /* reset reader */
+                p1 >> npcGUID;
+
+                Object* const pNpc = ObjectAccessor::GetObjectByTypeMask(*masterSession.GetPlayer(), npcGUID, TYPEMASK_UNIT|TYPEMASK_GAMEOBJECT);
+                if (!pNpc) return;
+
+                /* for all master's bots */
+                for (PlayerBotMap::const_iterator it = masterSession.GetPlayerBotsBegin();
+                    it != masterSession.GetPlayerBotsEnd(); ++it)
+                {
+                    Player* const bot = it->second;
+                    if (!bot->IsInMap((WorldObject*) pNpc))
+                    {
+                        bot->GetPlayerbotAI()->TellMaster("I'm too far away to sell items!");
+                        continue;
+                    }
+                    else
+                    {
+                        uint32 TotalCost = 0;
+                        uint32 TotalSold = 0;
+                        std::ostringstream report;
+                        std::ostringstream canSell;
+                        canSell << "Items that are not trash and can be sold: ";
+                        // list out items in main backpack
+                        for (uint8 slot = INVENTORY_SLOT_ITEM_START; slot < INVENTORY_SLOT_ITEM_END; slot++)
+                        {
+                            Item* const item = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot);
+                            if (!item)
+                                continue;
+
+                            if (item->CanBeTraded() && item->GetProto()->Quality == ITEM_QUALITY_POOR)
+                            {
+                                int32 cost = item->GetCount() * item->GetProto()->SellPrice;
+                                bot->ModifyMoney(cost);
+                                bot->MoveItemFromInventory(item->GetBagSlot(), item->GetSlot(), true);
+                                bot->AddItemToBuyBackSlot(item);
+
+                                TotalSold = TotalSold + 1;
+                                TotalCost = TotalCost + cost;
+
+                                report << "Sold " << item->GetCount() << "x";
+                                report << " |cffffffff|Hitem:" << item->GetProto()->ItemId << ":0:0:0:0:0:0:0" << "|h[" << item->GetProto()->Name1 << "]|h|r";
+                                report << " for ";
+
+                                uint32 gold = uint32(cost / 10000);
+                                cost -= (gold * 10000);
+                                uint32 silver = uint32(cost / 100);
+                                cost -= (silver * 100);
+
+                                if (gold > 0)
+                                {
+                                    report << gold << "|TInterface\\Icons\\INV_Misc_Coin_01:16|t";
+                                }
+                                if (silver > 0)
+                                {
+                                    report << silver << "|TInterface\\Icons\\INV_Misc_Coin_03:16|t";
+                                }
+                                report << cost << "|TInterface\\Icons\\INV_Misc_Coin_05:16|t\n";
+                            }
+                            else if (item->GetProto()->SellPrice > 0)
+                            {
+                                canSell << "|cffffffff|Hitem:" << item->GetProto()->ItemId << ":0:0:0:0:0:0:0" << "|h[" << item->GetProto()->Name1 << "]|h|r ";
+                            }
+                        }
+
+                        for (uint8 bag = INVENTORY_SLOT_BAG_START; bag < INVENTORY_SLOT_BAG_END;++bag)
+                        {
+                            const Bag* const pBag = (Bag*) bot->GetItemByPos(INVENTORY_SLOT_BAG_0, bag);
+                            if (pBag)
+                            {
+                                for (uint8 slot = 0; slot < pBag->GetBagSize(); ++slot)
+                                {
+                                    Item* const item = bot->GetItemByPos(bag, slot);
+                                    if (!item)
+                                        continue;
+
+                                    if (item->CanBeTraded() && item->GetProto()->Quality == ITEM_QUALITY_POOR)
+                                    {
+                                        int32 cost = item->GetCount() * item->GetProto()->SellPrice;
+                                        bot->ModifyMoney(cost);
+                                        bot->MoveItemFromInventory(item->GetBagSlot(), item->GetSlot(), true);
+                                        bot->AddItemToBuyBackSlot(item);
+
+                                        TotalSold = TotalSold + 1;
+                                        TotalCost = TotalCost + cost;
+
+                                        report << "Sold " << item->GetCount() << "x";
+                                        report << " |cffffffff|Hitem:" << item->GetProto()->ItemId << ":0:0:0:0:0:0:0" << "|h[" << item->GetProto()->Name1 << "]|h|r";
+                                        report << " for ";
+
+                                        uint32 gold = uint32(cost / 10000);
+                                        cost -= (gold * 10000);
+                                        uint32 silver = uint32(cost / 100);
+                                        cost -= (silver * 100);
+                                        if (gold > 0)
+                                        {
+                                            report << gold << "|TInterface\\Icons\\INV_Misc_Coin_01:16|t";
+                                        }
+                                        if (silver > 0)
+                                        {
+                                            report << silver << "|TInterface\\Icons\\INV_Misc_Coin_03:16|t";
+                                        }
+                                        report << cost << "|TInterface\\Icons\\INV_Misc_Coin_05:16|t\n";
+                                    }
+                                    else if (item->GetProto()->SellPrice > 0)
+                                    {
+                                        canSell << "|cffffffff|Hitem:" << item->GetProto()->ItemId << ":0:0:0:0:0:0:0" << "|h[" << item->GetProto()->Name1 << "]|h|r ";
+                                    }
+                                }
+                            }
+                        }
+                        if (TotalSold > 0) {
+                            report << "Sold total " << TotalSold << " item(s) for ";
+                            uint32 gold = uint32(TotalCost / 10000);
+                            TotalCost -= (gold * 10000);
+                            uint32 silver = uint32(TotalCost / 100);
+                            TotalCost -= (silver * 100);
+                            if (gold > 0)
+                            {
+                                report << gold << "|TInterface\\Icons\\INV_Misc_Coin_01:16|t";
+                            }
+                            if (silver > 0)
+                            {
+                                report << silver << "|TInterface\\Icons\\INV_Misc_Coin_03:16|t";
+                            }
+                            report << TotalCost << "|TInterface\\Icons\\INV_Misc_Coin_05:16|t";
+                            bot->GetPlayerbotAI()->TellMaster(report.str());
+                        }
+                        bot->GetPlayerbotAI()->TellMaster(canSell.str());
+                    }
+                }
+                return;
+
+            }
+
+        case CMSG_AREATRIGGER:
+        {
+            uint32 Trigger_ID;
+            WorldPacket p1(packet);
+            p1.rpos(0); /* reset reader */
+            p1 >> Trigger_ID;
+
+            /* for all master's bots */
+            for (PlayerBotMap::const_iterator it = masterSession.GetPlayerBotsBegin();
+                it != masterSession.GetPlayerBotsEnd(); ++it)
+            {
+                Player* const bot = it->second;
+
+                uint32 quest_id = sObjectMgr->GetQuestForAreaTrigger( Trigger_ID );
+                // The conditions that intentionally left unchecked are:
+                // Bot is alive or not
+                // Bot is in the trigger area or not
+                if( quest_id && bot->IsActiveQuest(quest_id) )
+                {
+                    Quest const* pQuest = sObjectMgr->GetQuestTemplate(quest_id);
+                    if( pQuest )
+                    {
+                        if(bot->GetQuestStatus(quest_id) == QUEST_STATUS_INCOMPLETE)
+                        {
+                            bot->AreaExploredOrEventHappens( quest_id );
+                            bot->GetPlayerbotAI()->TellMaster("Quest area explored");
+                        }
+                    }
+                }
+
+                if(sObjectMgr->IsTavernAreaTrigger(Trigger_ID))
+                {
+                    // set resting flag we are in the inn
+                    bot->SetFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING);
+                    bot->InnEnter(time(NULL), bot->GetMapId() , bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ());
+                    bot->SetRestType(REST_TYPE_IN_TAVERN);
+
+                    if(sWorld->IsFFAPvPRealm())
+                        bot->RemoveByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_FFA_PVP);
+                }
+            }
+            return;
+        }
+
+
+        default:
+        {
+     /*       const char *oc = LookupOpcodeName(packet.GetOpcode());
+            ChatHandler ch(masterSession.GetPlayer());
+     //       ch.SendSysMessage(oc);
+
+            std::ostringstream out;
+            out << "HandleMasterIncomingPacket: " << oc;
+            sLog->outError(out.str().c_str());
+       */
+            return;
+        }
+
+    }
+}
+
+//handle outgoing packets the server would send to the client
+void PlayerbotAI::HandleBotOutgoingPacket(const WorldPacket &packet)
+{
+    switch(packet.GetOpcode())
+    {
+         case SMSG_TRADE_STATUS_EXTENDED:
+         {
+             //m_bot->GetSession()->SendTradeStatus(TRADE_STATUS_TRADE_ACCEPT);
+             return;
+         }
+
+
+        case SMSG_DUEL_WINNER:
+            m_bot->HandleEmoteCommand(EMOTE_ONESHOT_APPLAUD);
+            return;
+
+        case SMSG_DUEL_COMPLETE:
+            SetIgnoreUpdateTime(4);
+            m_combatOrder = ORDERS_NONE;
+            m_ScenarioType = SCENARIO_PVEEASY;
+            m_bot->GetMotionMaster()->Clear(true);
+            return;
+
+        case SMSG_DUEL_OUTOFBOUNDS:
+            m_bot->HandleEmoteCommand(EMOTE_ONESHOT_CHICKEN);
+            return;
+
+        case SMSG_DUEL_REQUESTED:
+        {
+            SetIgnoreUpdateTime(0);
+            WorldPacket p(packet);
+            uint64 flagGuid; p >> flagGuid;
+            uint64 playerGuid; p >> playerGuid;
+            Player *const pPlayer = ObjectAccessor::FindPlayer(playerGuid);
+            if(canObeyCommandFrom(*pPlayer))
+            {
+                m_bot->GetMotionMaster()->Clear(true);
+                WorldPacket *const packet = new WorldPacket(CMSG_DUEL_ACCEPTED, 8);
+                *packet << flagGuid;
+                m_bot->GetSession()->QueuePacket(packet); //queue the packet to get around race condition
+
+                //follow target in casting range
+                float angle = rand_norm()*M_PI; //Generates random float between 0 and 3.14
+                float dist = (float)(urand((m_followDistanceMin*10), (m_followDistanceMax*10))/10); //Using urand to get a random float is stupid. It takes uint32, not float.
+                m_bot->GetMotionMaster()->Clear(true);
+                m_bot->GetMotionMaster()->MoveFollow(pPlayer, dist, angle);
+
+                m_bot->SetSelection(playerGuid);
+                SetIgnoreUpdateTime(4);
+                m_combatOrder = ORDERS_KILL;
+                m_ScenarioType = SCENARIO_DUEL;
+            }
+            return;
+        }
+
+        case SMSG_INVENTORY_CHANGE_FAILURE:
+            TellMaster("I can't use that.");
+            return;
+
+        case SMSG_SPELL_DELAYED:
+        {
+            //sLog->outDebug("Bot [%u] SMSG_SPELL_DELAYED",m_bot->GetGUIDLow());
+            WorldPacket p(packet);
+            //uint64 casterGuid = extractGuid(p); //somehow the caster guid is corrupt
+            //if(casterGuid != m_bot->GetGUID()) return;
+            //uint32 delayTime; p >> delayTime;
+            //sLog->outDebug("Bot [%u] caster [%u] Spell Delayed [%u]",m_bot->GetGUIDLow(), casterGuid, delayTime);
+            //m_ignoreAIUpdatesUntilTime +=  ((((float)delayTime) / 1000.0f ) + 0.1f) * CLOCKS_PER_SEC;
+            if(m_CurrentlyCastingSpellId > 0)
+            {
+                m_ignoreAIUpdatesUntilTime += 0.5f * 1000; //Until this is handled correctly, assume, delay is the default 0.5 secs
+            }
+            return;
+        }
+
+        case SMSG_SPELL_FAILURE:
+        {
+            WorldPacket p(packet);
+            uint64 casterGuid = extractGuid(p);
+            if(casterGuid != m_bot->GetGUID()) return;
+            uint32 spellId; p >> spellId;
+            if(m_CurrentlyCastingSpellId == spellId)
+            {
+                SetIgnoreUpdateTime(1);
+                m_CurrentlyCastingSpellId = 0;
+            }
+            return;
+        }
+
+        //if a change in speed was detected for the master
+        //make sure we have the same mount status
+        case SMSG_FORCE_RUN_SPEED_CHANGE:
+        {
+            WorldPacket p(packet);
+            uint64 guid = extractGuid(p);
+            //uint64 guid; p >> guid;
+            Player *tPlayer = sObjectMgr->GetPlayer(guid);
+            if(!tPlayer) return;
+            if (!m_master || !m_bot) return;
+            if(guid == m_bot->GetGUID()) return;
+            if(guid == m_master->GetGUID()) {
+                m_bot->GetPlayerbotAI()->UseMount();
+                SetIgnoreUpdateTime(2);
+            }
+            return;
+        }
+
+        //handle flying acknowledgement
+        case SMSG_MOVE_SET_CAN_FLY:
+        {
+            WorldPacket p(packet);
+            uint64 guid = extractGuid(p);
+            if(guid != m_bot->GetGUID()) return;
+            m_bot->AddUnitMovementFlag(MOVEMENTFLAG_FLYING);
+            //SetSpeed(MOVE_RUN, m_master->GetSpeed(MOVE_FLIGHT) + 0.1f, true);
+            return;
+        }
+
+        //handle dismount flying acknowledgement
+        case SMSG_MOVE_UNSET_CAN_FLY:
+        {
+            WorldPacket p(packet);
+            uint64 guid = extractGuid(p);
+            if(guid != m_bot->GetGUID()) return;
+            m_bot->RemoveUnitMovementFlag(MOVEMENTFLAG_FLYING);
+            //SetSpeed(MOVE_RUN, m_master->GetSpeedRate(MOVE_RUN), true);
+            return;
+        }
+
+        //If the leader role was given to the bot automatically give it to the master
+        //if the master is in the group, otherwise leave group
+        case SMSG_GROUP_SET_LEADER:
+        {
+            WorldPacket p(packet);
+            std::string name; p >> name;
+            if(m_bot->GetGroup() && name == m_bot->GetName())
+            {
+                if(m_bot->GetGroup()->IsMember(m_master->GetGUID()))
+                {
+                    p.resize(8);
+                    p << m_master->GetGUID();
+                    m_bot->GetSession()->HandleGroupSetLeaderOpcode(p);
+                } else {
+                    p.clear(); //not really needed
+                    m_bot->GetSession()->HandleGroupDisbandOpcode(p); //packet not used
+                }
+            }
+            return;
+        }
+
+        //If the master leaves the group, then the bot leaves too
+        case SMSG_PARTY_COMMAND_RESULT:
+        {
+            WorldPacket p(packet);
+            uint32 operation; p >> operation;
+            std::string member; p >> member;
+            uint32 result; p >> result;
+            p.clear();
+            if(operation == PARTY_OP_LEAVE && member == m_master->GetName()) m_bot->GetSession()->HandleGroupDisbandOpcode(p); //packet not used
+            return;
+        }
+
+        //Automatically accept rez. Useful when bot dies, and a druid does a battle rez.
+        case SMSG_RESURRECT_REQUEST:
+        {
+            WorldPacket p, incP(packet);
+            uint8 status = 1;
+            uint64 rezzer; incP >> rezzer;
+            p << rezzer;
+            p << status;
+            m_bot->GetPlayerbotAI()->SetLooting(false);
+            m_bot->GetSession()->HandleResurrectResponseOpcode(p);
+            m_IsFollowingMaster = true;
+            m_TimeRessurect = 0;
+            return;
+        }
+
+        //Handle Group invites (auto accept if master is in group, otherwise decline & send message)
+        case SMSG_GROUP_INVITE:
+        {
+            if(m_bot->GetGroupInvite())
+            {
+                const Group *const grp = m_bot->GetGroupInvite();
+                if(!grp) return;
+                Player *const inviter = sObjectMgr->GetPlayer(grp->GetLeaderGUID());
+                if(!inviter) return;
+                WorldPacket p;
+                if(!canObeyCommandFrom(*inviter))
+                {
+                    std::string buf = "I can't accept your invite unless you first invite my master ";
+                    buf += m_master->GetName();
+                    buf += ".";
+                    SendWhisper(buf, *inviter);
+                    m_bot->GetSession()->HandleGroupDeclineOpcode(p); //packet not used
+                } else
+                    m_bot->GetSession()->HandleGroupAcceptOpcode(p); //packet not used
+            }
+            return;
+        }
+
+        //Handle when another player opens the trade window with the bot
+        //also sends list of tradable items bot can trade if bot is allowed to obey commands from
+        case SMSG_TRADE_STATUS:
+        {
+            if(m_bot->GetTrader() == NULL) break;
+            WorldPacket p(packet);
+            uint32 status; p >> status;
+            p.clear();
+
+            if(status == 4) { // TRADE_STATUS_TRADE_ACCEPT
+				if (!m_bot->GetTradeData()->IsAccepted() || !m_bot->GetTrader()->GetTradeData()->IsAccepted()) {
+                    m_bot->GetSession()->HandleAcceptTradeOpcode(p); //packet not used
+                }
+
+            } else if(status == 1) // TRADE_STATUS_BEGIN_TRADE
+            {
+                m_bot->GetSession()->HandleBeginTradeOpcode(p); //packet not used
+
+                //if(!canObeyCommandFrom(*(m_bot->GetTrader())))
+                //{
+                    //SendWhisper("I'm not allowed to trade you any of my items, but you are free to give me money or items.", *(m_bot->GetTrader()));
+                    //return;
+                //}
+
+                //list out items available for trade
+                std::ostringstream out;
+
+                //list out items in main backpack
+                for(uint8 slot = INVENTORY_SLOT_ITEM_START; slot < INVENTORY_SLOT_ITEM_END; slot++)
+                {
+                    const Item *const pItem = m_bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot);
+                    if(pItem && pItem->CanBeTraded())
+                    {
+                        const ItemPrototype *const pItemProto = pItem->GetProto();
+                        std::string name = pItemProto->Name1;
+
+                        out << " |cffffffff|Hitem:" << pItemProto->ItemId << ":0:0:0:0:0:0:0" << "|h[" << name << "]|h|r";
+                        if(pItem->GetCount() > 1)
+                            out << "x" << pItem->GetCount() << ' ';
+                    }
+                }
+                //list out items in other removable backpacks
+                for(uint8 bag = INVENTORY_SLOT_BAG_START; bag < INVENTORY_SLOT_BAG_END; ++bag)
+                {
+                    const Bag *const pBag = (Bag *)m_bot->GetItemByPos(INVENTORY_SLOT_BAG_0, bag);
+                    if(pBag)
+                    {
+                        for(uint8 slot = 0; slot < pBag->GetBagSize(); ++slot)
+                        {
+                            const Item *const pItem = m_bot->GetItemByPos(bag, slot);
+                            if(pItem && pItem->CanBeTraded())
+                            {
+                                const ItemPrototype *const pItemProto = pItem->GetProto();
+                                const std::string name = pItemProto->Name1;
+
+                                //item link format: http://www.wowwiki.com/ItemString
+                                //itemId, enchantId, jewelId1, jewelId2, jewelId3, jewelId4, suffixId, uniqueId
+                                out << " |cffffffff|Hitem:" << pItemProto->ItemId << ":0:0:0:0:0:0:0" << "|h[" << name << "]|h|r";
+                                if(pItem->GetCount() > 1)
+                                    out << "x" << pItem->GetCount() << ' ';
+                            }
+                        }
+                    }
+                }
+
+                //calculate how much money bot has
+                uint32 copper = m_bot->GetMoney();
+                uint32 gold = uint32(copper / 10000);
+                copper -= (gold * 10000);
+                uint32 silver = uint32(copper / 100);
+                copper -= (silver * 100);
+
+                //send bot the message
+                std::ostringstream whisper;
+                whisper << "I have |cff00ff00" << gold
+                << "|r|cfffffc00g|r|cff00ff00" << silver
+                << "|r|cffcdcdcds|r|cff00ff00" << copper
+                << "|r|cffffd333c|r" << " and the following items:";
+                m_bot->GetPlayerbotAI()->SendWhisper(whisper.str(), *(m_bot->GetTrader()));
+                ChatHandler ch(m_bot->GetTrader());
+                ch.SendSysMessage(out.str().c_str());
+            }
+            return;
+        }
+
+        case SMSG_SPELL_GO:
+        {
+            WorldPacket p(packet);
+            uint64 castItemGuid = extractGuid(p);
+            uint64 casterGuid = extractGuid(p);
+            if(casterGuid != m_bot->GetGUID()) return;
+
+            uint32 spellId; p >> spellId;
+            uint16 castFlags; p >> castFlags;
+            uint32 msTime; p >> msTime;
+            uint8 numHit; p >> numHit;
+            if(m_CurrentlyCastingSpellId == spellId)
+            {
+                Spell *const pSpell = m_bot->FindCurrentSpellBySpellId(spellId);
+                if(!pSpell) return;
+                if(pSpell->IsChannelActive() || pSpell->IsAutoRepeat())
+                    SetIgnoreUpdateTime( (((float)GetSpellDuration(pSpell->m_spellInfo) / 1000.0f) + 1.0f) );
+                else if(pSpell->IsAutoRepeat())
+                    SetIgnoreUpdateTime(6);
+                else {
+                    SetIgnoreUpdateTime(0.5f);
+                    m_CurrentlyCastingSpellId = 0;
+                }
+            }
+            return;
+        }
+
+        case SMSG_TEXT_EMOTE:
+        {
+            WorldPacket p(packet);
+            p.rpos(0); //reset reader
+            uint32 text_emote;
+            uint64 guid;
+            p >> guid;
+            p >> text_emote;
+
+            switch(text_emote)
+            {
+                case TEXTEMOTE_BOW:
+                {
+                    //Buff anyone who bows before me. Useful for players not in bot's group
+                    Player *pPlayer = sObjectMgr->GetPlayer(guid);
+
+                    Player *const bot =sObjectMgr->GetPlayer(pPlayer->GetSelection());
+
+                    if(bot && bot->GetGUID()==m_bot->GetGUID() &&
+                       bot->GetPlayerbotAI()->GetClassAI())
+                    {
+                        bot->GetPlayerbotAI()->GetClassAI()->BuffPlayer(pPlayer);
+                    }
+                    return;
+                }
+
+                default:
+                    m_bot->HandleEmoteCommand(text_emote);
+                    return;
+            }
+            return;
+        }
+
+        case MSG_MOVE_TELEPORT_ACK:
+            HandleTeleportAck();
+            return;
+
+        case SMSG_QUESTGIVER_STATUS_MULTIPLE:
+        {
+            return;
+        }
+
+        // used to communicate between bots
+        case SMSG_MESSAGECHAT:
+        {
+            WorldPacket p(packet);
+            p.rpos(0); //reset reader
+            uint8 msgtype;
+            uint32 language;
+            uint64 guid;
+            uint32 language2;
+            uint64 guid2;
+            uint32 textlen;
+            std::string msg;
+
+            p>>msgtype; p>>language; p>>guid; p>>language2; p>>guid2; p>>textlen;
+            p>>msg;
+
+            Player * fromPlayer = sObjectMgr->GetPlayer(guid);
+            if (fromPlayer == NULL) break;
+            const std::string text = msg;
+            HandleCommand(text, *fromPlayer);
+        }
+
+        case SMSG_MONSTER_MOVE:
+        case SMSG_UPDATE_WORLD_STATE:
+        case SMSG_COMPRESSED_UPDATE_OBJECT:
+        case MSG_MOVE_SET_FACING:
+        case MSG_MOVE_STOP:
+        case MSG_MOVE_HEARTBEAT:
+        case MSG_MOVE_STOP_STRAFE:
+        case MSG_MOVE_START_STRAFE_LEFT:
+        case SMSG_UPDATE_OBJECT:
+        case MSG_MOVE_START_FORWARD:
+        case SMSG_WEATHER:
+        case SMSG_POWER_UPDATE:
+        case SMSG_TIME_SYNC_REQ:
+        case SMSG_STANDSTATE_UPDATE:
+        case SMSG_PERIODICAURALOG:
+        case SMSG_AURA_UPDATE:
+        return;
+
+/*
+        default:
+        const char *oc = LookupOpcodeName(packet.GetOpcode());
+        TellMaster(oc);
+        sLog->outError("SMSG opcode: %s", oc);
+  */
+    }
+}
+void PlayerbotAI::HandleTeleportAck()
+{
+    SetIgnoreUpdateTime(6);
+    m_bot->GetMotionMaster()->Clear(true);
+    if(m_bot->IsBeingTeleportedNear())
+    {
+        WorldPacket p = WorldPacket(MSG_MOVE_TELEPORT_ACK, 8 + 4 + 4);
+        p.appendPackGUID(m_bot->GetGUID());
+        p << (uint32) 0; //supposed to be flags? not used currently
+        p << (uint32) time(0); //time - not currently used
+        m_bot->GetSession()->HandleMoveTeleportAck(p);
+    }
+    else if(m_bot->IsBeingTeleportedFar())
+        m_bot->GetSession()->HandleMoveWorldportAckOpcode();
+}
+
+uint8 PlayerbotAI::GetHealthPercent(const Unit &target) const
+{
+    return(static_cast<float>(target.GetHealth()) / target.GetMaxHealth()) * 100;
+}
+
+uint8 PlayerbotAI::GetHealthPercent() const
+{
+    return GetHealthPercent(*m_bot);
+}
+
+uint8 PlayerbotAI::GetManaPercent(const Unit &target) const
+{
+    return(static_cast<float>(target.GetPower(POWER_MANA)) / target.GetMaxPower(POWER_MANA)) * 100;
+}
+
+uint8 PlayerbotAI::GetManaPercent() const
+{
+    return GetManaPercent(*m_bot);
+}
+
+uint8 PlayerbotAI::GetBaseManaPercent(const Unit &target) const
+{
+    if(target.GetPower(POWER_MANA) >= target.GetCreateMana())
+        return(100);
+    else
+        return(static_cast<float>(target.GetPower(POWER_MANA)) / target.GetCreateMana()) * 100;
+}
+
+uint8 PlayerbotAI::GetBaseManaPercent() const
+{
+    return GetBaseManaPercent(*m_bot);
+}
+
+uint8 PlayerbotAI::GetRageAmount(const Unit &target) const
+{
+    return(static_cast<float>(target.GetPower(POWER_RAGE)));
+}
+
+uint8 PlayerbotAI::GetRageAmount() const
+{
+    return GetRageAmount(*m_bot);
+}
+
+uint8 PlayerbotAI::GetEnergyAmount(const Unit &target) const
+{
+    return(static_cast<float>(target.GetPower(POWER_ENERGY)));
+}
+
+uint8 PlayerbotAI::GetEnergyAmount() const
+{
+    return GetEnergyAmount(*m_bot);
+}
+
+uint8 PlayerbotAI::GetRunicPower(const Unit &target) const
+{
+    return(static_cast<float>(target.GetPower(POWER_RUNIC_POWER)));
+}
+
+uint8 PlayerbotAI::GetRunicPower() const
+{
+    return GetRunicPower(*m_bot);
+}
+
+
+typedef std::pair<uint32, uint8> spellEffectPair;
+typedef std::multimap< spellEffectPair, Aura*> AuraMap;
+
+bool PlayerbotAI::HasAura(uint32 spellId, const Unit *player) const
+{
+    for(Unit::AuraMap::const_iterator iter = player->GetOwnedAuras().begin(); iter != player->GetOwnedAuras().end(); ++iter)
+    {
+        if(iter->second->GetId() == spellId) return true;
+    }
+    return false;
+}
+bool PlayerbotAI::HasAura(const char *spellName) const
+{
+    return HasAura(spellName, m_bot);
+}
+bool PlayerbotAI::HasAura(const char *spellName, const Unit *player) const
+{
+    uint32 spellId = getSpellId(spellName);
+    return(spellId) ? HasAura(spellId, player) : false;
+}
+
+void PlayerbotAI::UseMount() const
+{
+
+    if(m_master->IsMounted() && ! m_bot->IsMounted())
+    {
+// sLog->outError ("PlayerbotAI::UseMount: %s is mounted but %s is not", m_master->GetName(), m_bot->GetName());
+        //Player Part
+        int32 master_speed1 = 0;
+        int32 master_speed2 = 0;
+        if(!m_master->GetAuraEffectsByType(SPELL_AURA_MOUNTED).empty())
+        {
+            master_speed1 = m_master->GetAuraEffectsByType(SPELL_AURA_MOUNTED).front()->GetSpellProto()->EffectBasePoints[1];
+            master_speed2 = m_master->GetAuraEffectsByType(SPELL_AURA_MOUNTED).front()->GetSpellProto()->EffectBasePoints[2];
+        }
+//sLog->outError ("master_speed1 = %d", master_speed1);
+//sLog->outError ("master_speed2 = %d", master_speed2);
+        //Bot Part
+        uint32 spellMount = 0;
+        for(PlayerSpellMap::iterator itr = m_bot->GetSpellMap().begin(); itr != m_bot->GetSpellMap().end(); ++itr)
+        {
+            uint32 spellId = itr->first;
+            if(itr->second->state == PLAYERSPELL_REMOVED || itr->second->disabled || IsPassiveSpell(spellId))
+                continue;
+            const SpellEntry *pSpellInfo = sSpellStore.LookupEntry(spellId);
+            if(!pSpellInfo)
+                continue;
+            if(pSpellInfo->EffectApplyAuraName[0] == SPELL_AURA_MOUNTED)
+            {
+                if((pSpellInfo->EffectApplyAuraName[1] == SPELL_AURA_MOD_INCREASE_MOUNTED_SPEED)
+                    && (pSpellInfo->EffectApplyAuraName[2] == SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED))
+                {
+                    if((pSpellInfo->EffectBasePoints[1] == master_speed1)
+                        && (pSpellInfo->EffectBasePoints[2] == master_speed2))
+                    {
+                        spellMount = spellId;
+                        break;
+                    }
+                }
+                else if((pSpellInfo->EffectApplyAuraName[2] == SPELL_AURA_MOD_INCREASE_MOUNTED_SPEED)
+                    && (pSpellInfo->EffectApplyAuraName[1] == SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED))
+                {
+                    if((pSpellInfo->EffectBasePoints[2] == master_speed2)
+                        && (pSpellInfo->EffectBasePoints[1] == master_speed1))
+                    {
+                        spellMount = spellId;
+                        break;
+                    }
+                }
+                else if(pSpellInfo->EffectApplyAuraName[1] == SPELL_AURA_MOD_INCREASE_MOUNTED_SPEED)
+                {
+                    if(pSpellInfo->EffectBasePoints[1] == master_speed1 && master_speed2 <= 0) { spellMount = spellId; break; } //Has no secondary mount aura
+                    else if (spellMount == 0) { spellMount = spellId; } // default to first mount in case it doesnt have correct version
+                }
+            }
+        }
+//sLog->outError ("spellMount = %u", spellMount);
+        if(spellMount > 0) m_bot->GetPlayerbotAI()->CastSpell(spellMount, m_bot);
+
+    }
+    else if(!m_master->IsMounted() && m_bot->IsMounted())
+    {
+        WorldPacket emptyPacket;
+        m_bot->GetSession()->HandleCancelMountAuraOpcode(emptyPacket);
+    }
+} //end UseMount
+
+Item *PlayerbotAI::FindFood() const
+{
+    // list out items in main backpack
+    for (uint8 slot=INVENTORY_SLOT_ITEM_START; slot < INVENTORY_SLOT_ITEM_END; slot++)
+    {
+        Item* const pItem = m_bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot);
+        if (pItem)
+        {
+            const ItemPrototype* const pItemProto = pItem->GetProto();
+            if (! pItemProto || m_bot->CanUseItem(pItemProto)!=EQUIP_ERR_OK) continue;
+            if (pItemProto->Class==ITEM_CLASS_CONSUMABLE &&
+                (pItemProto->SubClass==ITEM_SUBCLASS_FOOD ||
+                pItemProto->SubClass==ITEM_SUBCLASS_POTION ||
+                pItemProto->SubClass==ITEM_SUBCLASS_ELIXIR))
+            {
+                // if is FOOD
+                if (pItemProto->Spells[0].SpellCategory == SPELL_CATEGORY_FOOD)
+                    return pItem;
+            }
+        }
+    }
+    // list out items in other removable backpacks
+    for (uint8 bag = INVENTORY_SLOT_BAG_START; bag < INVENTORY_SLOT_BAG_END; ++bag)
+    {
+        const Bag* const pBag = (Bag*) m_bot->GetItemByPos(INVENTORY_SLOT_BAG_0, bag);
+        if (pBag)
+        {
+            for (uint8 slot = 0; slot < pBag->GetBagSize(); ++slot)
+            {
+                Item* const pItem = m_bot->GetItemByPos(bag, slot);
+                if (pItem)
+                {
+                    const ItemPrototype* const pItemProto = pItem->GetProto();
+                    if (! pItemProto || m_bot->CanUseItem(pItemProto)!=EQUIP_ERR_OK) continue;
+                    if (pItemProto->Class==ITEM_CLASS_CONSUMABLE &&
+                        (pItemProto->SubClass==ITEM_SUBCLASS_FOOD ||
+                        pItemProto->SubClass==ITEM_SUBCLASS_POTION ||
+                        pItemProto->SubClass==ITEM_SUBCLASS_ELIXIR))
+                    {
+                        // if is FOOD
+                        if (pItemProto->Spells[0].SpellCategory == SPELL_CATEGORY_FOOD)
+                            return pItem;
+                    }
+                }
+            }
+        }
+    }
+    return NULL;
+}
+
+Item *PlayerbotAI::FindDrink() const
+{
+    // list out items in main backpack
+    for (uint8 slot=INVENTORY_SLOT_ITEM_START; slot < INVENTORY_SLOT_ITEM_END; slot++)
+    {
+        Item* const pItem = m_bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot);
+        if (pItem)
+        {
+            const ItemPrototype* const pItemProto = pItem->GetProto();
+            if (! pItemProto || m_bot->CanUseItem(pItemProto)!=EQUIP_ERR_OK) continue;
+            if (pItemProto->Class==ITEM_CLASS_CONSUMABLE &&
+                (pItemProto->SubClass==ITEM_SUBCLASS_FOOD ||
+                pItemProto->SubClass==ITEM_SUBCLASS_POTION ||
+                pItemProto->SubClass==ITEM_SUBCLASS_ELIXIR))
+            {
+                if (pItemProto->Spells[0].SpellCategory == SPELL_CATEGORY_DRINK ||
+                    pItemProto->Spells[0].SpellCategory == 4)
+                    return pItem;
+            }
+        }
+    }
+    // list out items in other removable backpacks
+    for (uint8 bag = INVENTORY_SLOT_BAG_START; bag < INVENTORY_SLOT_BAG_END; ++bag)
+    {
+        const Bag* const pBag = (Bag*) m_bot->GetItemByPos(INVENTORY_SLOT_BAG_0, bag);
+        if (pBag)
+        {
+            for (uint8 slot = 0; slot < pBag->GetBagSize(); ++slot)
+            {
+                Item* const pItem = m_bot->GetItemByPos(bag, slot);
+                if (pItem)
+                {
+                    const ItemPrototype* const pItemProto = pItem->GetProto();
+
+                    if (! pItemProto || m_bot->CanUseItem(pItemProto)!=EQUIP_ERR_OK) continue;
+                    if (pItemProto->Class==ITEM_CLASS_CONSUMABLE &&
+                        (pItemProto->SubClass==ITEM_SUBCLASS_FOOD ||
+                        pItemProto->SubClass==ITEM_SUBCLASS_POTION ||
+                        pItemProto->SubClass==ITEM_SUBCLASS_ELIXIR))
+                    {
+                        // if is WATER
+                        if (pItemProto->Spells[0].SpellCategory == SPELL_CATEGORY_DRINK ||
+                            pItemProto->Spells[0].SpellCategory == 4)
+                            return pItem;
+                    }
+                }
+            }
+        }
+    }
+    return NULL;
+}
+
+
+Item *PlayerbotAI::FindPotion() const
+{
+    // list out items in main backpack
+    for (uint8 slot=INVENTORY_SLOT_ITEM_START; slot < INVENTORY_SLOT_ITEM_END; slot++)
+    {
+        Item* const pItem = m_bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot);
+        if (pItem)
+        {
+            const ItemPrototype* const pItemProto = pItem->GetProto();
+            if (! pItemProto || m_bot->CanUseItem(pItemProto)!=EQUIP_ERR_OK) continue;
+            if (pItemProto->IsPotion())
+            {
+                    return pItem;
+            }
+        }
+    }
+    // list out items in other removable backpacks
+    for (uint8 bag = INVENTORY_SLOT_BAG_START; bag < INVENTORY_SLOT_BAG_END; ++bag)
+    {
+        const Bag* const pBag = (Bag*) m_bot->GetItemByPos(INVENTORY_SLOT_BAG_0, bag);
+        if (pBag)
+        {
+            for (uint8 slot = 0; slot < pBag->GetBagSize(); ++slot)
+            {
+                Item* const pItem = m_bot->GetItemByPos(bag, slot);
+                if (pItem)
+                {
+                    const ItemPrototype* const pItemProto = pItem->GetProto();
+                    if (! pItemProto || m_bot->CanUseItem(pItemProto)!=EQUIP_ERR_OK) continue;
+                    if (pItemProto->IsPotion())
+                    {
+                            return pItem;
+                    }
+                }
+            }
+        }
+    }
+    return NULL;
+}
+
+
+
+Item *PlayerbotAI::FindBandage() const
+{
+    //list out items in main backpack
+    for(uint8 slot=INVENTORY_SLOT_ITEM_START; slot < INVENTORY_SLOT_ITEM_END; slot++)
+    {
+        Item *const pItem = m_bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot);
+        if(pItem)
+        {
+            const ItemPrototype *const pItemProto = pItem->GetProto();
+            if(!pItemProto || m_bot->CanUseItem(pItemProto)!=EQUIP_ERR_OK) continue;
+            if(pItemProto->Class == ITEM_CLASS_CONSUMABLE && pItemProto->SubClass == ITEM_SUBCLASS_BANDAGE) return pItem;
+        }
+    }
+    //list out items in other removable backpacks
+    for(uint8 bag = INVENTORY_SLOT_BAG_START; bag < INVENTORY_SLOT_BAG_END; ++bag)
+    {
+        const Bag *const pBag = (Bag *)m_bot->GetItemByPos(INVENTORY_SLOT_BAG_0, bag);
+        if(pBag)
+        {
+            for(uint8 slot = 0; slot < pBag->GetBagSize(); ++slot)
+            {
+                Item *const pItem = m_bot->GetItemByPos(bag, slot);
+                if(pItem)
+                {
+                    const ItemPrototype *const pItemProto = pItem->GetProto();
+                    if(!pItemProto || m_bot->CanUseItem(pItemProto)!=EQUIP_ERR_OK) continue;
+                    if(pItemProto->Class == ITEM_CLASS_CONSUMABLE && pItemProto->SubClass == ITEM_SUBCLASS_BANDAGE) return pItem;
+                }
+            }
+        }
+    }
+    return NULL;
+}
+
+// finds poison starting from the front
+Item *PlayerbotAI::FindPoisonForward() const
+{
+    //list out items in main backpack
+    for(uint8 slot=INVENTORY_SLOT_ITEM_START; slot < INVENTORY_SLOT_ITEM_END; slot++)
+    {
+        Item *const pItem = m_bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot);
+        if(pItem)
+        {
+            const ItemPrototype *const pItemProto = pItem->GetProto();
+            if(!pItemProto || m_bot->CanUseItem(pItemProto)!=EQUIP_ERR_OK) continue;
+            if(pItemProto->Class == ITEM_CLASS_CONSUMABLE && pItemProto->SubClass == ITEM_SUBCLASS_CONSUMABLE_OTHER) return pItem;
+        }
+    }
+    //list out items in other removable backpacks
+    for(uint8 bag = INVENTORY_SLOT_BAG_START; bag < INVENTORY_SLOT_BAG_END; ++bag)
+    {
+        const Bag *const pBag = (Bag *)m_bot->GetItemByPos(INVENTORY_SLOT_BAG_0, bag);
+        if(pBag && pBag->IsBag())
+        {
+            for(uint8 slot = 0; slot < pBag->GetBagSize(); ++slot)
+            {
+                Item *const pItem = m_bot->GetItemByPos(bag, slot);
+                if(pItem)
+                {
+                    const ItemPrototype *const pItemProto = pItem->GetProto();
+                    if(!pItemProto || m_bot->CanUseItem(pItemProto)!=EQUIP_ERR_OK) continue;
+                    if(pItemProto->Class == ITEM_CLASS_CONSUMABLE && pItemProto->SubClass == ITEM_SUBCLASS_CONSUMABLE_OTHER) return pItem;
+                }
+            }
+        }
+    }
+    return NULL;
+}
+
+// finds poison starting from the back
+Item *PlayerbotAI::FindPoisonBackward() const
+{
+    //list out items in main backpack
+    for(uint8 slot=INVENTORY_SLOT_ITEM_END; slot > INVENTORY_SLOT_ITEM_START; slot--)
+    {
+        Item *const pItem = m_bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot);
+        if(pItem)
+        {
+            const ItemPrototype *const pItemProto = pItem->GetProto();
+            if(!pItemProto || m_bot->CanUseItem(pItemProto)!=EQUIP_ERR_OK) continue;
+            if(pItemProto->Class == ITEM_CLASS_GLYPH) continue;
+            if(pItemProto->Class == ITEM_CLASS_CONSUMABLE && pItemProto->SubClass == ITEM_SUBCLASS_CONSUMABLE_OTHER) return pItem;
+        }
+    }
+    //list out items in other removable backpacks
+    for(uint8 bag = INVENTORY_SLOT_BAG_END; bag > INVENTORY_SLOT_BAG_START; --bag)
+    {
+        const Bag *const pBag = (Bag *)m_bot->GetItemByPos(INVENTORY_SLOT_BAG_0, bag);
+        if(pBag && pBag->IsBag())
+        {
+            for(uint8 slot = pBag->GetBagSize(); slot > 0  ; --slot)
+            {
+                Item *const pItem = m_bot->GetItemByPos(bag, slot);
+                if(pItem)
+                {
+                    const ItemPrototype *const pItemProto = pItem->GetProto();
+                    if(!pItemProto || m_bot->CanUseItem(pItemProto)!=EQUIP_ERR_OK) continue;
+                    if(pItemProto->Class == ITEM_CLASS_GLYPH) continue;
+                    if(pItemProto->Class == ITEM_CLASS_CONSUMABLE && pItemProto->SubClass == ITEM_SUBCLASS_CONSUMABLE_OTHER) return pItem;
+                }
+            }
+        }
+    }
+    return NULL;
+}
+
+void PlayerbotAI::InterruptCurrentCastingSpell()
+{
+    //TellMaster("I'm interrupting my current spell!");
+    WorldPacket *const packet = new WorldPacket(CMSG_CANCEL_CAST, 5);
+    uint8 counter = 1;
+    *packet << counter;
+    *packet << m_CurrentlyCastingSpellId;
+    m_CurrentlyCastingSpellId = 0;
+    m_bot->GetSession()->QueuePacket(packet);
+}
+
+void PlayerbotAI::Feast()
+{
+    //stand up if we are done feasting
+    if(!(m_bot->GetHealth() < m_bot->GetMaxHealth() || (m_bot->getPowerType() == POWER_MANA && m_bot->GetPower(POWER_MANA) < m_bot->GetMaxPower(POWER_MANA))))
+    {
+        m_TimeDoneDrinking = time(0) - 1;
+        m_TimeDoneEating = time(0) - 1;
+        m_bot->SetStandState(UNIT_STAND_STATE_STAND);
+        return;
+    }
+
+    //wait 3 seconds before checking if we need to drink more or eat more
+    time_t currentTime = time(0);
+    SetIgnoreUpdateTime(3);
+
+    //should we drink another
+    if(m_bot->getPowerType() == POWER_MANA && currentTime > m_TimeDoneDrinking && ((static_cast<float>(m_bot->GetPower(POWER_MANA)) / m_bot->GetMaxPower(POWER_MANA)) < 0.8))
+    {
+        Item *pItem = FindDrink();
+        if(pItem != NULL && !m_bot->HasSpellCooldown(pItem->GetSpell()))
+        {
+            UseItem(*pItem);
+            m_TimeDoneDrinking = currentTime + 30;
+            return;
+        } else {
+
+            // find a mage
+            if (m_FeastSpamTimer > 0) --m_FeastSpamTimer;
+            else {
+                Player *mage = GetClassAI()->FindMage(m_bot);
+                if (mage != NULL) {
+                    SendWhisper("I could use a drink.", *mage);
+                }
+                TellMaster("I need water.");
+                m_FeastSpamTimer=100;
+            }
+        }
+    }
+
+    //should we eat another
+    if(currentTime > m_TimeDoneEating && currentTime > m_TimeDoneDrinking  && ((static_cast<float>(m_bot->GetHealth()) / m_bot->GetMaxHealth()) < 0.8))
+    {
+        Item *pItem = FindFood();
+        if(pItem != NULL && !m_bot->HasSpellCooldown(pItem->GetSpell()))
+        {
+            UseItem(*pItem);
+            m_TimeDoneEating = currentTime + 30;
+            return;
+        }
+        //TellMaster("I need food."); //Disabled, tends to be horribly spammy.
+    }
+
+    //if we are no longer eating or drinking
+    //because we are out of items or we are above 80% in both stats
+    if(currentTime > m_TimeDoneEating && currentTime > m_TimeDoneDrinking)
+    {
+        //TellMaster("I'm ready, let's go.");
+        m_bot->SetStandState(UNIT_STAND_STATE_STAND);
+    }
+}
+
+Unit *PlayerbotAI::getNextTarget(Unit *victim)
+{
+    Unit *target = NULL;
+    AttackerSet m_attackers = victim->getAttackers();
+    if(!m_attackers.empty())
+    {
+        for(AttackerSet::iterator iter = m_attackers.begin(); iter != m_attackers.end(); ++iter)
+        {
+            if(*iter && m_bot->GetDistance((*iter)) < 30)
+            {
+                target = *iter;
+                break;
+            } //end if
+        } //end for
+    }
+    return target;
+} //end getNextTarget
+
+//intelligently sets a reasonable combat order for this bot
+//based on its class / level / etc
+void PlayerbotAI::GetCombatOrders()
+{
+    if(m_bot->isDead() || isLooting) return;
+    Unit *thingToAttack=0;
+
+    // check raid targets icons
+    if (!thingToAttack)
+    {
+        Group *group = m_bot->GetGroup();
+        uint64 targetGUID = group->GetTargetWithIconByGroup (m_bot->GetGUID());
+        if (targetGUID>0)
+        {
+            thingToAttack = ObjectAccessor::GetUnit(*m_master, targetGUID);
+            if (!thingToAttack || thingToAttack->isDead() || !m_bot->IsHostileTo(thingToAttack)) thingToAttack=0;
+//else sLog->outError ("%s is attacking %s", m_bot->GetName(), thingToAttack->GetName());
+        }
+    }
+
+    //check if someone wants to attack master or me
+    if (!thingToAttack) thingToAttack = getNextTarget(m_master);
+
+    if(!thingToAttack)
+        thingToAttack = getNextTarget(m_bot);
+
+    //check master's target
+    if(!thingToAttack)
+    {
+        Unit *const pTarget = ObjectAccessor::GetUnit(*m_master, m_master->GetSelection());
+        if(pTarget && pTarget->isInCombat() && pTarget->IsHostileTo(m_master))
+            thingToAttack = pTarget;
+    }
+
+    //last try to find something to attack
+    if(!thingToAttack)
+    {
+        Unit *pUnit = NULL;
+        Trinity::NearestHostileUnitInAttackDistanceCheck u_check((Creature*)m_bot, 30.0);
+        Trinity::UnitLastSearcher<Trinity::NearestHostileUnitInAttackDistanceCheck> searcher(m_bot, pUnit, u_check);
+        m_bot->VisitNearbyObject(30, searcher);
+        if(pUnit != NULL && pUnit->isAlive() && pUnit->IsHostileToPlayers()) thingToAttack = pUnit;
+    }
+
+
+    //if the thing to attack is a world invisible trigger, ex Glyph in UBRS,
+    //default to master's current victim
+    if(!thingToAttack || thingToAttack->GetUInt32Value(UNIT_FIELD_DISPLAYID) == 11686) thingToAttack = m_master->getVictim();
+
+    //if the thing to attack is an invisible trigger ex vazruden in Hellfire Ramparts,
+    //default to master's current victim
+    if(!thingToAttack || !thingToAttack->IsVisible()) thingToAttack = m_master->getVictim();
+
+    // if the thing to attack is not attackable
+    if (!thingToAttack || thingToAttack->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE)) thingToAttack = NULL;
+
+    // override all others if ordered to pull
+    if (m_bot->GetPlayerbotAI()->GetClassAI()->isPulling()) {
+        thingToAttack = ObjectAccessor::GetUnit(*m_master,m_master->GetSelection());
+    }
+
+    if(!thingToAttack)
+    {
+        if(GetClassAI() && !m_bot->isInCombat()) (GetClassAI())->DoNonCombatActions();
+        return;
+    }
+
+    //wait till it gets closer
+    //if(m_bot->GetDistance(thingToAttack) > 30) return;
+
+    //if thing to attack is in a duel, then ignore and don't call updateAI for 6 seconds
+    //this method never gets called when the bot is in a duel and this code
+    //prevents bot from helping
+    if(thingToAttack->GetTypeId() == TYPEID_PLAYER && ((Player*)(thingToAttack))->duel)
+    {
+       SetIgnoreUpdateTime(6);
+        return;
+    }
+
+    m_bot->SetSelection(thingToAttack->GetGUID());
+    SetIgnoreUpdateTime(1);
+    m_combatOrder = ORDERS_KILL;
+
+    if(m_bot->getStandState() != UNIT_STAND_STATE_STAND)
+        m_bot->SetStandState(UNIT_STAND_STATE_STAND);
+    m_bot->Attack(thingToAttack, true);
+
+    if(thingToAttack->GetTypeId() != TYPEID_PLAYER)
+    {
+        //add thingToAttack to loot list
+        CreatureInfo const *cInfo = ((Creature *)thingToAttack)->GetCreatureInfo();
+        if(cInfo && cInfo->lootid) m_lootCreature.push_back(thingToAttack->GetGUID());
+    }
+
+    return;
+}
+
+
+void PlayerbotAI::DoNextCombatManeuver()
+{
+    if(isLooting) return;
+
+    Unit *const pTarget = ObjectAccessor::GetUnit(*m_bot, m_bot->GetSelection());
+
+    //if current order doesn't make sense anymore
+    //clear our orders so we can get orders in next update
+    if((!pTarget || pTarget->isDead() || !pTarget->IsInWorld() ||
+        !m_bot->IsHostileTo(pTarget) || pTarget->IsPolymorphed() || m_bot->isDead()
+        || ( !m_master->isInCombat() && !m_bot->isInCombat() && !pTarget->isInCombat()) // The mob probably is in evade mode, stop combat..
+        //|| pTarget->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE)
+        ) &&
+        !m_bot->GetPlayerbotAI()->GetClassAI()->isPulling() )
+    {
+        m_combatOrder = ORDERS_NONE;
+        m_bot->SetSelection(0);
+        m_bot->GetMotionMaster()->Clear(true);
+        m_bot->InterruptNonMeleeSpells(true);
+//sLog->outError ("current target doesn't make sense so following");
+        Follow(*m_master);
+        return;
+    }
+
+    if(GetClassAI())
+    {
+        if(m_bot->HasUnitState(UNIT_STAT_CASTING))
+        {
+            return;
+        }
+
+        GetClassAI()->DoNextCombatManeuver(pTarget);
+    }
+}
+
+//this is where the AI should go
+//GetRandomContactPoint
+//GetPower, GetMaxPower
+//HasSpellCooldown
+//IsAffectedBySpellmod
+//isMoving
+//HasUnitState(FLAG) FLAG like: UNIT_STAT_ROOT, UNIT_STAT_CONFUSED, UNIT_STAT_STUNNED
+//hasAuraType
+
+void PlayerbotAI::UpdateAI(const uint32 p_time)
+{
+    time_t currentTime = time(0);
+    uint32 currentClock = getMSTime();
+    m_bot->UpdateZone(m_bot->GetZoneId(), m_bot->GetAreaId());
+
+    if (m_playerBotsFly==0 && m_master->isInFlight())
+    {
+        if (m_IsFollowingMaster)
+        {
+            const WorldLocation fakeloc = WorldLocation(35, -0.873190f, 52.920242f, -27.550674f, 1.655620f);
+            PlayerbotChatHandler ch(m_master);
+            if (! ch.teleport(*m_bot, fakeloc))
+            {
+                ch.sysmessage(".. could not be teleported ..");
+                return;
+            }
+            m_bot->SendUpdateToPlayer(m_master);
+       }
+        Stay();
+        return;
+    }
+
+    if(m_TimeRessurect == 0 && m_bot->isDead())
+    {
+        m_IsFollowingMaster = false;
+        m_TimeRessurect = currentTime + 30;
+        return;
+    }
+    else if(m_TimeRessurect > currentTime && m_bot->isDead())
+    {
+        return;
+    }
+    else if((!m_TimeRessurect == 0) && m_TimeRessurect <= currentTime && m_bot->isDead())
+    {
+        m_IsFollowingMaster = true;
+        m_TimeRessurect = 0;
+    }
+    if(((int64)m_ignoreAIUpdatesUntilTime - (int64)currentClock) > (int64) 30000) { SetIgnoreUpdateTime(2); return; } // Fix Timer overflow and AI freeze (max limit 30 secs)
+    if(currentClock < m_ignoreAIUpdatesUntilTime || m_bot->IsBeingTeleported() || m_bot->GetTrader()) return;
+
+
+    //default updates occur every 1.5 seconds
+    SetIgnoreUpdateTime(1.5);
+
+    // prevent cheating
+    if (!m_bot->GetGroup())
+    {
+        m_master->GetSession()->LogoutPlayerBot(m_bot->GetGUID(), false);
+        return;
+    }
+
+    if(m_bot->isDead()) isLooting = false;
+
+    /*
+     * combat checks
+     */
+    if(m_master <= 0 || ((m_master->isInCombat() || m_bot->isInCombat()) && m_bot->isDead())) return; //You're DEAD, stop thinking.
+
+    //if we are casting a spell then interrupt it
+    //make sure any actions that cast a spell set a proper m_ignoreAIUpdatesUntilTime!
+    Spell *const pSpell = GetCurrentSpell();
+    if(pSpell && !(pSpell->IsChannelActive() || pSpell->IsAutoRepeat())) InterruptCurrentCastingSpell();
+
+    //direct cast command from master
+    else if(m_spellIdCommand != 0)
+    {
+        Unit *pTarget = ObjectAccessor::GetUnit(*m_bot, m_targetGuidCommand);
+        if(pTarget != NULL) CastSpell(m_spellIdCommand, pTarget);
+        m_spellIdCommand = 0;
+        m_targetGuidCommand = 0;
+    }
+
+    else if(m_combatOrder != ORDERS_NONE) DoNextCombatManeuver(); //handle combat
+
+    else if (m_bot->GetPlayerbotAI()->GetClassAI()->isPulling())
+    {
+        GetCombatOrders();
+        return;
+    }
+
+    //if master is in combat and bot is not, automatically assist master
+    //NOTE: combat orders are also set via incoming packets to bot or outgoing packets from master
+    else if(m_master->isInCombat() && (!m_bot->isInCombat() || m_combatOrder == ORDERS_NONE) || m_master->isDead()) GetCombatOrders();
+
+    //if bot is in combat but master is not, attack
+    else if(m_bot->isInCombat()) GetCombatOrders();
+
+    // if bot is not in combat, but main tank is
+    else if (!m_bot->isInCombat()) {
+       Unit *tank=m_classAI->FindMainTankInRaid(m_bot);
+       if (tank!=NULL && tank->isInCombat()) GetCombatOrders();
+
+    }
+
+    /*
+     * Non combat checks
+     */
+
+    //are we sitting, if so feast if possible
+//    if(m_bot->getStandState() == UNIT_STAND_STATE_SIT) {
+//sLog->outError ("%s - sitting so feast", m_bot->GetName());
+//
+//    }
+
+    //if commanded to follow master and not already following master then follow master
+    if(!m_bot->isInCombat() && m_IsFollowingMaster && m_bot->GetMotionMaster()->GetCurrentMovementGeneratorType() == IDLE_MOTION_TYPE)
+    {
+        Follow(*m_master);
+
+    //do class specific non combat actions
+	} else if(!m_bot->isInCombat() && GetClassAI()) {
+        (GetClassAI())->DoNonCombatActions();
+    } if(!m_master->isInCombat())
+        DoLoot();
+
+
+    if (m_master->getStandState() == UNIT_STAND_STATE_SIT)
+    {
+        m_bot->SetStandState(UNIT_STAND_STATE_SIT);
+        m_bot->SendUpdateToPlayer(m_master);
+        Feast();
+    }
+    else if (m_TimeDoneDrinking < time(0) && m_TimeDoneEating < time(0)) //Do no interrupt if bot is eating/drinking
+    {
+        m_bot->SetStandState(UNIT_STAND_STATE_STAND);
+        m_bot->SendUpdateToPlayer(m_master);
+    }
+
+
+    //try to catch if he is falling through the world.  This happens
+    //when zoning in/out of an instance
+    if(m_IsFollowingMaster && m_bot->GetMapId() != m_master->GetMapId() ||
+    //m_bot->GetZoneId() != m_master->GetZoneId() ||
+    (abs(abs(m_bot->GetPositionX()) - abs(m_master->GetPositionX())) > 90) ||
+    (abs(abs(m_bot->GetPositionY()) - abs(m_master->GetPositionY())) > 90) ||
+    (abs(abs(m_bot->GetPositionZ()) - abs(m_master->GetPositionZ())) > 50))
+    {
+//sLog->outError ("%s: %s is too far away so following", m_bot->GetName(), m_master->GetName());
+        Follow(*m_master);
+    }
+}
+
+
+void PlayerbotAI::KilledMonster(uint32 entry, uint64 guid)
+{
+   // isLooting = true;
+
+    if(m_master->isAlive() && m_IsFollowingMaster && !m_master->isInCombat())
+    {
+        if(!DoLoot())
+        {
+            float angle = rand_norm()*M_PI; //Generates random float between 0 and 3.14
+            float dist = (float)(urand((m_followDistanceMin*10), (m_followDistanceMax*10))/10);
+
+            m_bot->GetMotionMaster()->Clear(true);
+            m_bot->GetMotionMaster()->MoveFollow(m_master, dist, angle);
+        }
+    }
+
+    // reset main tank every time we finish combat, just in case the
+    // original main tank died and got set to next tank.
+    //m_classAI->SetMainTank(NULL);
+}
+
+Spell *PlayerbotAI::GetCurrentSpell() const
+{
+    if(m_CurrentlyCastingSpellId == 0) return NULL;
+    Spell *const pSpell = m_bot->FindCurrentSpellBySpellId(m_CurrentlyCastingSpellId);
+    return pSpell;
+}
+
+void PlayerbotAI::TellMaster(const std::string &text)
+{
+    SendWhisper(text, *m_master);
+}
+
+bool PlayerbotAI::CanBotsFly()
+{
+    if (m_playerBotsFly==0) return false;
+    else return true;
+}
+
+void PlayerbotAI::SendWhisper(const std::string &text, Player &player)
+{
+    WorldPacket data(SMSG_MESSAGECHAT, 200);
+
+    data << uint8(CHAT_MSG_WHISPER_INFORM);
+    data << uint32(LANG_UNIVERSAL);
+    data << uint64(player.GetGUID());
+    data << uint32(LANG_UNIVERSAL);                               //language 2.1.0 ?
+    data << uint64(player.GetGUID());
+    data << uint32(text.length() + 1);
+    data << text;
+    data << uint8(player.chatTag());
+
+    player.GetSession()->SendPacket(&data);
+}
+
+bool PlayerbotAI::canObeyCommandFrom(const Player &player) const
+{
+    return player.GetSession()->GetAccountId() == m_master->GetSession()->GetAccountId();
+}
+
+void PlayerbotAI::SetInFront(const Unit *obj)
+{
+    if(!m_bot->HasInArc(M_PI, obj))
+    {
+        m_bot->SetInFront(obj);
+        m_bot->SendUpdateToPlayer(m_master);
+    }
+}
+
+bool PlayerbotAI::CastSpell(const char *args)
+{
+    uint32 spellId = getSpellId(args);
+    return(spellId) ? CastSpell(spellId) : false;
+}
+
+bool PlayerbotAI::CastSpell(uint32 spellId, Unit *target, bool checkFirst, bool castExistingAura, bool skipFriendlyCheck, bool skipEquipStanceCheck, bool triggered)
+{
+    if (!spellId) return false;
+
+    if (!m_bot->HasSpell(spellId)) {
+        return false;
+    }
+
+    const SpellEntry * pSpellInfo = sSpellStore.LookupEntry(spellId);
+    if (!pSpellInfo)
+    {
+        sLog->outDebug("CRITICAL: PBot Class %u - Non-existing Spell in Cast - [%u]", m_bot->getClass(), spellId);
+        std::stringstream ss;
+        ss << "Missing spell entry in CastSpell - ";
+        ss << spellId;
+        TellMaster(ss.str());
+        SetIgnoreUpdateTime(1);
+        return false;
+    }
+    return CastSpell(pSpellInfo, target, checkFirst, castExistingAura, skipFriendlyCheck, skipEquipStanceCheck, triggered);
+}
+
+bool PlayerbotAI::CastSpell(const SpellEntry * pSpellInfo, Unit *target, bool checkFirst, bool castExistingAura, bool skipFriendlyCheck, bool skipEquipStanceCheck, bool triggered)
+{
+    if(!m_bot->isAlive()) return false;
+    if(!pSpellInfo)
+    {
+        sLog->outError ("%s: Missing spell entry in CastSpell Direct", m_bot->GetName());
+        TellMaster("Missing spell entry in CastSpell");
+        SetIgnoreUpdateTime(1);
+        return false;
+    }
+    uint32 spellId = pSpellInfo->Id;
+    uint64 oldSel = m_bot->GetSelection();
+
+    // Auto Targeting
+    if (!target)
+    {
+        //NEGATIVE SPELL
+        if (sSpellMgr->GetSpellCustomAttr(spellId) & SPELL_ATTR0_CU_NEGATIVE)
+        {
+            if (m_bot->GetSelection() <= 0) return false;
+            else
+            {
+                target = ObjectAccessor::GetUnit(*m_bot, m_bot->GetSelection());
+                if (!target) return false;
+            }
+        }
+        else { target = m_bot; }
+    }
+
+    //Make the Checks
+
+    if (!triggered && checkFirst && !CanCast(pSpellInfo, target, castExistingAura, skipFriendlyCheck, skipEquipStanceCheck) ) { return false; }
+    if ( m_bot->GetSelection() != target->GetGUID() ) { m_bot->SetSelection(target->GetGUID()); } //if target is different than selection apply it
+
+    m_bot->CastSpell(target, pSpellInfo, triggered); //CAST THE SPELL
+    if ( m_bot->GetSelection() != oldSel ) { m_bot->SetSelection(oldSel); } // Restore if target changed to cast
+
+    // Check if the casting started..
+    Spell *const pSpell = m_bot->FindCurrentSpellBySpellId(spellId);
+    if(!pSpell) return false;
+
+    // Trigger Pseudo Global Cooldown and consider casttime
+    float GCD = 1.5f;
+    if (m_bot->getPowerType() == POWER_ENERGY) GCD = 1;
+    float psCastTime = ((float)pSpell->GetCastTime()) / 1000.0f;
+    if (psCastTime - GCD > -0.3f) GCD = 0.3f; //Global cooldown won't be an issiue for casts (0.3 secs is for safe next cast)
+    else { GCD -= psCastTime; } //Remaining GCD after cast..
+    //float psRecoveryTime = GetSpellRecoveryTime(pSpellInfo) / 1000;
+    //sLog->outDebug("Bot [%u] Start Spell [%u] Cast Time [%f]", m_bot->GetGUIDLow(), pSpellInfo->Id, psCastTime);
+    m_CurrentlyCastingSpellId = spellId;
+    //SetIgnoreUpdateTime(psCastTime + GCD);
+    SetIgnoreUpdateTime(psCastTime > GCD ? psCastTime : GCD);
+    return true;
+}
+
+bool PlayerbotAI::CanCast(uint32 spellId, Unit *target, bool castExistingAura, bool skipFriendlyCheck, bool skipEquipStanceCheck)
+{
+     //if spellId == 0, it means that the bot is not high enough level to
+    //have learned the spell
+    if (!spellId) return false;
+    const SpellEntry * pSpellInfo = sSpellStore.LookupEntry(spellId);
+    if (!pSpellInfo)
+    {
+        sLog->outDebug("CRITICAL: PBot Class %u - Non-existing Spell in Cast - [%u]", m_bot->getClass(), spellId);
+        std::stringstream ss;
+        ss << "Missing spell entry in CastSpell - ";
+        ss << spellId;
+        TellMaster(ss.str());
+        SetIgnoreUpdateTime(1);
+        return false;
+    }
+    return CanCast(pSpellInfo, target, castExistingAura, skipFriendlyCheck, skipEquipStanceCheck);
+}
+
+bool PlayerbotAI::CanCast(const SpellEntry * pSpellInfo, Unit *target, bool castExistingAura, bool skipFriendlyCheck, bool skipEquipStanceCheck)
+{
+    if (!pSpellInfo)
+    {
+        sLog->outDebug("CRITICAL: PBot Class %u - Non-existing Spell in CastCheck - Direct SpellEntry", m_bot->getClass());
+        TellMaster("Missing spell entry in CastSpell");
+        SetIgnoreUpdateTime(1);
+        return false;
+    }
+    uint32 spellId = pSpellInfo->Id;
+
+    // Auto Targeting
+    if (!target)
+    {
+        //NEGATIVE SPELL
+        if (sSpellMgr->GetSpellCustomAttr(spellId) & SPELL_ATTR0_CU_NEGATIVE)
+        {
+            if (m_bot->GetSelection() <= 0) return false;
+            else
+            {
+                target = ObjectAccessor::GetUnit(*m_bot, m_bot->GetSelection());
+                if (!target) return false;
+            }
+        }
+        else { target = m_bot; }
+    }
+
+    if (!m_bot->isAlive()) return false;
+    if (m_bot->HasSpellCooldown(spellId)) return false;
+    if (m_bot->HasUnitState(UNIT_STAT_CASTING)) return false;
+    if (m_bot->IsMounted()) return false;
+
+    //cast existing aura over again?
+    if (!castExistingAura && target->HasAura(spellId, m_bot->GetGUID())) return false;
+
+    //Stances-forms and equipment REQs
+    if (!skipEquipStanceCheck)
+    {
+        uint32 formMask = (GetForm() ? 1 << (GetPlayerBot()->GetShapeshiftForm() - 1) : 0);
+        //sLog->outDebug("DEBUG: Spell [%u] - Form [%X] - Need Form [%X] - Not Form [%X]", pSpellInfo->Id, formMask, pSpellInfo->Stances, pSpellInfo->StancesNot );
+        if (pSpellInfo->Stances & formMask) { return true; }
+        if (pSpellInfo->StancesNot && pSpellInfo->StancesNot & formMask) { return false; }
+        if (!m_bot->HasItemFitToSpellRequirements(pSpellInfo)) return false;
+    }
+
+    //Power Costs
+    const SpellSchoolMask pSpellSchool = GetSpellSchoolMask(pSpellInfo);
+    uint32 pPowerCost = CalculatePowerCost(pSpellInfo, m_bot, pSpellSchool);
+
+    if (skipEquipStanceCheck) { if (m_bot->GetPower((Powers)pSpellInfo->powerType) < pPowerCost) return false; } //Power check for Required PowerType (After changind stance, powertype may change, for druids)
+    else { if (m_bot->GetPower(m_bot->getPowerType()) < pPowerCost) return false; } //Power check for Current m_bot Power Type
+
+    //Distance / movement checks
+    const SpellRangeEntry * pSpellRange = sSpellRangeStore.LookupEntry(pSpellInfo->rangeIndex);
+    float curDistance = m_bot->GetDistance(target);
+    if (GetSpellCastTime(pSpellInfo) > 0 && m_bot->isMoving()) return false; //Cannot cast while moving
+
+    //The target is immune or not?
+    if (target->GetTypeId() != TYPEID_PLAYER) { if ( ((Creature*)target)->IsImmunedToSpell(pSpellInfo)) { return false; } }
+    else { if (target->IsImmunedToSpell(pSpellInfo)) { return false; } }
+
+    //target reaction checks (Has problems with dual effect spells like death coil/holy shock)
+    if (skipFriendlyCheck)
+    {
+        if (pSpellRange->maxRangeHostile != 0) { if ( pSpellRange->maxRangeHostile < curDistance || pSpellRange->minRangeHostile > curDistance ) { return false; } } //Assume hostile spell
+    }
+    else if (IsPositiveSpell(spellId))
+    {
+        if(! m_bot->IsFriendlyTo(target)) { return false; }
+        else if (pSpellRange->maxRangeFriend != 0) { if (pSpellRange->maxRangeFriend < curDistance || pSpellRange->minRangeFriend > curDistance) { return false; } }
+        else if (curDistance > MELEE_RANGE) return false;
+    }
+    else
+    {
+        if (m_bot->IsFriendlyTo(target))  return false;
+        if (!m_bot->HasInArc(M_PI,target)) return false; //target is not in front
+        if (pSpellRange->maxRangeHostile != 0) { if ( pSpellRange->maxRangeHostile < curDistance || pSpellRange->minRangeHostile > curDistance ) { return false; } }
+        else if (curDistance > MELEE_RANGE) return false; //Out of range - Melee Range
+    }
+
+    return true;
+}
+
+uint8 PlayerbotAI::GetForm(Unit *pPlayer)
+{
+    if (!pPlayer) pPlayer = m_bot;
+    return (pPlayer->GetUInt32Value(UNIT_FIELD_BYTES_2) & 0xFF000000) >> (4 * 6);
+}
+
+//extracts all item ids in format below
+//I decided to roll my own extractor rather then use the one in ChatHandler
+//because this one works on a const string, and it handles multiple links
+//|color|linkType:key:something1:...:somethingN|h[name]|h|r
+void PlayerbotAI::extractItemIds(const std::string &text, std::list<uint32> &itemIds) const
+{
+    uint8 pos = 0;
+    while(true)
+    {
+        int i = text.find("Hitem:", pos);
+        if(i == -1) break;
+        pos = i + 6;
+        int endPos = text.find(':', pos);
+        if(endPos == -1) break;
+        std::string idC = text.substr(pos, endPos - pos);
+        uint32 id = atol(idC.c_str());
+        pos = endPos;
+        if(id) itemIds.push_back(id);
+    }
+}
+
+bool PlayerbotAI::extractGOinfo(const std::string& text, uint32 &guid, uint32 &entry, int &mapid, float &x, float &y, float &z) const
+{
+
+   //    Link format
+   //    |cFFFFFF00|Hfound:" << guid << ':'  << entry << ':' << x << ':' << y << ':' << z  << ':' << mapid << ':' <<  "|h[" << gInfo->name << "]|h|r";
+
+     //    |cFFFFFF00|Hfound:5093:1731:-9295:-270:81.874:0:|h[Copper Vein]|h|r
+  uint8 pos = 0;
+      // extract GO guid
+       int i = text.find("Hfound:", pos); // base H = 11
+       if (i == -1) // break if error
+            return false;
+
+       pos = i + 7; //start of window in text 11 + 7 = 18
+      int endPos = text.find(':', pos); // end of window in text 22
+      if (endPos == -1) //break if error
+           return false;
+       std::string guidC = text.substr(pos, endPos - pos); // get string within window i.e guid 22 - 18 =  4
+       guid = atol(guidC.c_str()); // convert ascii to long int
+     // extract GO entry
+       pos = endPos + 1;
+      endPos = text.find(':', pos); // end of window in text
+       if (endPos == -1) //break if error
+           return false;
+      std::string entryC = text.substr(pos, endPos - pos); // get string within window i.e entry
+        entry = atol(entryC.c_str()); // convert ascii to float
+     // extract GO x
+       pos = endPos + 1;
+      endPos = text.find(':', pos); // end of window in text
+       if (endPos == -1) //break if error
+           return false;
+       std::string xC = text.substr(pos, endPos - pos); // get string within window i.e x
+
+        x = atof(xC.c_str()); // convert ascii to float
+      // extract GO y
+       pos = endPos + 1;
+      endPos = text.find(':', pos); // end of window in text
+       if (endPos == -1) //break if error
+           return false;
+      std::string yC = text.substr(pos, endPos - pos); // get string within window i.e y
+      y = atof(yC.c_str()); // convert ascii to float
+     // extract GO z
+
+        pos = endPos + 1;
+       endPos = text.find(':', pos); // end of window in text
+        if (endPos == -1) //break if error
+           return false;
+
+       std::string zC = text.substr(pos, endPos - pos); // get string within window i.e z
+
+        z = atof(zC.c_str()); // convert ascii to float
+
+      //extract GO mapid
+       pos = endPos + 1;
+       endPos = text.find(':', pos); // end of window in text
+       if (endPos == -1) //break if error
+           return false;
+      std::string mapidC = text.substr(pos, endPos - pos); // get string within window i.e mapid
+       mapid = atoi(mapidC.c_str()); // convert ascii to int
+        pos = endPos; // end
+      return true;
+}
+
+//extracts currency in #g#s#c format
+uint32 PlayerbotAI::extractMoney(const std::string &text) const
+{
+    //if user specified money in ##g##s##c format
+    std::string acum = "";
+    uint32 copper = 0;
+    for(uint8 i = 0; i < text.length(); i++)
+    {
+        if(text[i] == 'g')
+        {
+            copper += (atol(acum.c_str()) * 100 * 100);
+            acum = "";
+        }
+        else if(text[i] == 'c')
+        {
+            copper += atol(acum.c_str());
+            acum = "";
+        }
+        else if(text[i] == 's')
+        {
+            copper += (atol(acum.c_str()) * 100);
+            acum = "";
+        }
+        else if(text[i] == ' ')
+        {
+            break;
+        }
+        else if(text[i] >= 48 && text[i] <= 57)
+        {
+            acum += text[i];
+        } else {
+            copper = 0;
+            break;
+        }
+    }
+    return copper;
+}
+
+// finds items in equipment and adds Item* to foundItemList
+// also removes found item IDs from itemIdSearchList when found
+void PlayerbotAI::findItemsInEquip(std::list<uint32>& itemIdSearchList, std::list<Item*>& foundItemList) const
+{
+    for( uint8 slot=EQUIPMENT_SLOT_START; itemIdSearchList.size()>0 && slot<EQUIPMENT_SLOT_END; slot++ ) {
+        Item* const pItem = m_bot->GetItemByPos( INVENTORY_SLOT_BAG_0, slot );
+        if( !pItem )
+            continue;
+
+        for (std::list<uint32>::iterator it = itemIdSearchList.begin(); it != itemIdSearchList.end(); ++it)
+        {
+            if (pItem->GetProto()->ItemId != *it)
+                continue;
+
+            foundItemList.push_back(pItem);
+            itemIdSearchList.erase(it);
+            break;
+        }
+    }
+}
+
+
+//finds items in inventory and adds Item *to foundItemList
+//also removes found item IDs from itemIdSearchList when found
+void PlayerbotAI::findItemsInInv(std::list<uint32>& itemIdSearchList, std::list<Item*>& foundItemList) const
+{
+
+    //look for items in main bag
+    for(uint8 slot = INVENTORY_SLOT_ITEM_START; itemIdSearchList.size() > 0 && slot < INVENTORY_SLOT_ITEM_END; ++slot)
+    {
+        Item *const pItem = m_bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot);
+        if(!pItem) continue;
+        for(std::list<uint32>::iterator it = itemIdSearchList.begin(); it != itemIdSearchList.end(); ++it)
+        {
+            if(pItem->GetProto()->ItemId != *it) continue;
+            foundItemList.push_back(pItem);
+            itemIdSearchList.erase(it);
+            break;
+        }
+    }
+
+    //for all for items in other bags
+    for(uint8 bag = INVENTORY_SLOT_BAG_START; itemIdSearchList.size() > 0 && bag < INVENTORY_SLOT_BAG_END; ++bag)
+    {
+        Bag *const pBag = (Bag *)m_bot->GetItemByPos(INVENTORY_SLOT_BAG_0, bag);
+        if(!pBag) continue;
+        for(uint8 slot = 0; itemIdSearchList.size() > 0 && slot < pBag->GetBagSize(); ++slot)
+        {
+            Item *const pItem = m_bot->GetItemByPos(bag, slot);
+            if(!pItem) continue;
+            for(std::list<uint32>::iterator it = itemIdSearchList.begin(); it != itemIdSearchList.end(); ++it)
+            {
+                if(pItem->GetProto()->ItemId != *it) continue;
+                foundItemList.push_back(pItem);
+                itemIdSearchList.erase(it);
+                break;
+            }
+        }
+    }
+}
+
+bool PlayerbotAI::HasPick()
+{
+    QueryResult result;
+
+    // list out equiped items
+    for( uint8 slot = EQUIPMENT_SLOT_START; slot < EQUIPMENT_SLOT_END; slot++)
+    {
+        Item* const pItem = m_bot->GetItemByPos( INVENTORY_SLOT_BAG_0, slot );
+        if (pItem )
+        {
+            const ItemPrototype* const pItemProto = pItem->GetProto();
+
+                if (!pItemProto )
+                    continue;
+
+            result = WorldDatabase.PQuery("SELECT TotemCategory FROM item_template WHERE entry = '%i'", pItemProto->ItemId);
+            if (result)
+            {
+                Field *fields = result->Fetch();
+                uint32 tc = fields[0].GetUInt32();
+                // sLog->outDebug("HasPick %u",tc);
+                if(tc ==  165 || tc == 167) // pick = 165, hammer = 162 or hammer pick = 167
+                    return true;
+            }
+        }
+    }
+
+    // list out items in backpack
+    for (uint8 slot = INVENTORY_SLOT_ITEM_START; slot < INVENTORY_SLOT_ITEM_END; slot++)
+    {
+        // sLog->outDebug("[%s's]backpack slot = %u",m_bot->GetName(),slot); // 23 to 38 = 16
+        Item* const pItem = m_bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot); // 255, 23 to 38
+        if (pItem)
+        {
+            const ItemPrototype* const pItemProto = pItem->GetProto();
+            if (!pItemProto )
+                continue;
+            result = WorldDatabase.PQuery("SELECT TotemCategory FROM item_template WHERE entry = '%i'", pItemProto->ItemId);
+            if (result)
+            {
+                Field *fields = result->Fetch();
+                uint32 tc = fields[0].GetUInt32();
+                // sLog->outDebug("HasPick %u",tc);
+                if(tc ==  165 || tc == 167) // pick = 165, hammer = 162 or hammer pick = 167
+                    return true;
+            }
+        }
+    }
+
+    // list out items in other removable backpacks
+    for (uint8 bag = INVENTORY_SLOT_BAG_START; bag < INVENTORY_SLOT_BAG_END; ++bag) // 20 to 23 = 4
+    {
+        const Bag* const pBag = (Bag*) m_bot->GetItemByPos(INVENTORY_SLOT_BAG_0, bag); // 255, 20 to 23
+        if (pBag)
+        {
+            for (uint8 slot = 0; slot < pBag->GetBagSize(); ++slot)
+            {
+                // sLog->outDebug("[%s's]bag[%u] slot = %u",m_bot->GetName(),bag,slot); // 1 to bagsize = ?
+                Item* const pItem = m_bot->GetItemByPos(bag, slot); // 20 to 23, 1 to bagsize
+                if (pItem)
+                {
+                    const ItemPrototype* const pItemProto = pItem->GetProto();
+
+                    if (!pItemProto )
+                        continue;
+
+                    result = WorldDatabase.PQuery("SELECT TotemCategory FROM item_template WHERE entry = '%i'", pItemProto->ItemId);
+                    if (result)
+                    {
+
+                        Field *fields = result->Fetch();
+                        uint32 tc = fields[0].GetUInt32();
+                        // sLog->outDebug("HasPick %u",tc);
+                        if(tc ==  165 || tc == 167)
+                            return true;
+                    }
+                }
+            }
+        }
+    }
+    std::ostringstream out;
+    out << "|cffffffffI do not have a pick!";
+    TellMaster( out.str().c_str() );
+    return false;
+}
+
+
+//submits packet to use an item
+void PlayerbotAI::PoisonWeapon(Item &item, uint32 _spellId, uint32 _target, EquipmentSlots weaponSlot)
+{
+    uint8 bagIndex = item.GetBagSlot();
+    uint8 slot = item.GetSlot();
+    uint8 cast_count = 1;
+    uint32 spellid = _spellId;
+    uint64 item_guid = item.GetGUID();
+    uint32 glyphIndex = 0; //??
+    uint8 unk_flags = 0; //not 0x02
+
+    uint32 target = _target;
+    uint32 targetItemGUID = 0;
+    uint8 x = 0;
+    Item *weapon=NULL;
+    if (_spellId>0){
+
+        targetItemGUID = 16;
+        x = 135;
+        cast_count = 7;
+        weapon = GetPlayerBot()->GetItemByPos( INVENTORY_SLOT_BAG_0, weaponSlot );
+
+    }
+
+    WorldPacket *const packet = new WorldPacket(CMSG_USE_ITEM, 1 + 1 + 1 + 4 + 8 + 4 + 1 + 4);
+    *packet << bagIndex << slot << cast_count << spellid << item_guid << glyphIndex << unk_flags << target;
+    if (weapon) packet->appendPackGUID(weapon->GetGUID());
+
+    m_bot->GetSession()->QueuePacket(packet); //queue the packet to get around race condition
+
+} // end PoisonWeapon
+
+
+
+//submits packet to use an item
+void PlayerbotAI::UseItem(Item &item)
+{
+    uint8 bagIndex = item.GetBagSlot();
+    uint8 slot = item.GetSlot();
+    uint8 cast_count = 1;
+    uint32 spellid = 0; //only used in combat
+    uint64 item_guid = item.GetGUID();
+    uint32 glyphIndex = 0; //??
+    uint8 unk_flags = 0; //not 0x02
+
+    //create target data
+    //note other targets are possible but not supported at the moment
+    //see SpellCastTargets::read in Spell.cpp to see other options
+    //for setting target
+
+    uint32 target = TARGET_FLAG_SELF;
+
+    WorldPacket *const packet = new WorldPacket(CMSG_USE_ITEM, 1 + 1 + 1 + 4 + 8 + 4 + 1);
+    *packet << bagIndex << slot << cast_count << spellid << item_guid << glyphIndex << unk_flags << target;
+    m_bot->GetSession()->QueuePacket(packet); //queue the packet to get aroundrace condition
+
+} // end UseItem
+
+
+//submits packet to use an item
+void PlayerbotAI::EquipItem(Item &item)
+{
+    uint8 bagIndex = item.GetBagSlot();
+    uint8 slot = item.GetSlot();
+
+    WorldPacket *const packet = new WorldPacket(CMSG_AUTOEQUIP_ITEM, 2);
+    *packet << bagIndex << slot;
+    m_bot->GetSession()->QueuePacket(packet);
+}
+
+// submits packet to trade an item (trade window must already be open)
+// default slot is -1 which means trade slots 0 to 5. if slot is set
+// to TRADE_SLOT_NONTRADED (which is slot 6) item will be shown in the
+// 'Will not be traded' slot.
+bool PlayerbotAI::TradeItem(const Item& item, int8 slot)
+{
+    sLog->outDebug( "[PlayerbotAI::TradeItem]: slot=%d, hasTrader=%d, itemInTrade=%d, itemTradeable=%d",
+        slot,
+        (m_bot->GetTrader()?1:0),
+        (item.IsInTrade()?1:0),
+        (item.CanBeTraded()?1:0)
+        );
+
+    if (!m_bot->GetTrader() || item.IsInTrade())
+        return false;
+
+    int8 tradeSlot = -1;
+
+    if( (slot>=0 && slot<TRADE_SLOT_COUNT) /*&& m_bot->GetItemPosByTradeSlot(slot)==NULL_SLOT */) {
+        tradeSlot = slot;
+    } else if (!item.CanBeTraded())
+    {
+        tradeSlot = (uint8) TRADE_SLOT_NONTRADED;
+    }
+    else
+    {
+        for( uint8 i=0; i<TRADE_SLOT_TRADED_COUNT; ++i )
+        {
+			if (m_bot->GetTradeData()->GetItem(TradeSlots(i)) == NULL){
+                tradeSlot = (uint8)i;
+                break;
+			}
+        }
+    }
+
+    if( tradeSlot == -1 ) return false;
+
+    WorldPacket* const packet = new WorldPacket(CMSG_SET_TRADE_ITEM, 3);
+    *packet << (uint8) tradeSlot << (uint8) item.GetBagSlot()
+            << (uint8) item.GetSlot();
+    m_bot->GetSession()->QueuePacket(packet);
+
+    return true;
+}
+
+
+//submits packet to trade copper (trade window must be open)
+bool PlayerbotAI::TradeCopper(uint32 copper)
+{
+    if(copper > 0)
+    {
+        WorldPacket *const packet = new WorldPacket(CMSG_SET_TRADE_GOLD, 4);
+        *packet << copper;
+        m_bot->GetSession()->QueuePacket(packet);
+        return true;
+    }
+    return false;
+}
+
+void PlayerbotAI::Stay()
+{
+    if (!m_IsFollowingMaster)
+        return;
+
+    m_IsFollowingMaster = false;
+    m_bot->GetMotionMaster()->Clear(true);
+    m_bot->HandleEmoteCommand(EMOTE_ONESHOT_SALUTE);
+}
+
+
+bool PlayerbotAI::Follow(Player &player)
+{
+    if(m_master <= 0 || ((m_master->isInCombat() || m_bot->isInCombat()) && m_bot->isDead())) return false; //You're DEAD, stop thinking.
+    if(m_master->isDead()) return false;
+    if(m_master->IsBeingTeleported() || m_master->isInFlight()) return false;
+
+    if(m_bot->getStandState() == UNIT_STAND_STATE_SIT && (m_TimeDoneDrinking < time(0) && m_TimeDoneEating < time(0))) return false; //Do no interrupt if bot is eating/drinking
+
+    m_IsFollowingMaster = true;
+
+    if(!m_bot->IsStandState()) {
+        m_bot->SetStandState(UNIT_STAND_STATE_STAND);
+        m_bot->SendUpdateToPlayer(m_master);
+    }
+
+    if(!m_bot->isInCombat())
+    {
+        //if bot is dead and master is alive, revive bot
+        if(m_master->isAlive() && !m_bot->isAlive())
+        {
+            SetIgnoreUpdateTime(6);
+            isLooting = false;
+            PlayerbotChatHandler ch(m_master);
+            if(!ch.revive(*m_bot))
+            {
+                ch.sysmessage(".. could not be revived ..");
+                return false;
+            }
+        }
+
+        if(!m_bot->isDead() && !m_bot->IsBeingTeleported() && !player.isDead() &&
+            (m_bot->GetMapId() != player.GetMapId()
+            || m_bot->GetZoneId() != player.GetZoneId()
+            || m_bot->GetAreaId() != player.GetAreaId()
+            || m_bot->GetPhaseMask() != player.GetPhaseMask())
+            || m_bot->GetDistance(player) > 255)
+            {
+                SetIgnoreUpdateTime(6);
+                isLooting = false;
+                PlayerbotChatHandler ch(m_master);
+                if(!ch.teleport(*m_bot))
+                {
+                    ch.sysmessage(".. could not be teleported ..");
+                    return false;
+                }
+        }
+    }
+
+    if(m_bot->isAlive() && !isLooting)
+    {
+        float angle = M_PI/2 + rand_norm()*M_PI ; //Generates random float between 90 and 270 degrees
+        float dist = (float)(urand((m_followDistanceMin*10), (m_followDistanceMax*10))/10); // Using urand to get a random float is stupid.
+        m_bot->GetMotionMaster()->Clear(true);
+        m_bot->GetMotionMaster()->MoveFollow(&player, dist, angle);
+
+        return true;
+    }
+    return false;
+}
+
+//handle commands sent through chat channels
+void PlayerbotAI::HandleCommand(const std::string &text, Player &fromPlayer)
+{
+    //ignore any messages from Addons
+    if(text.empty() ||
+        text.find("X-Perl") != std::wstring::npos ||
+        text.find("HealBot") != std::wstring::npos ||
+        text.find("LOOT_OPENED") != std::wstring::npos ||
+        text.find("CTRA") != std::wstring::npos)
+        return;
+
+    //if message is not from a player in the masters account auto reply and ignore
+    //if(!canObeyCommandFrom(fromPlayer))
+    //{
+        //std::string msg = "I can't talk to you. Please speak to my master ";
+        //msg += m_master->GetName();
+        //msg += ".";
+        //m_bot->SendWhisper(msg, *fromPlayer);
+        //m_bot->HandleEmoteCommand(EMOTE_ONESHOT_NO);
+    //}
+
+
+    else if (text == "I could use a drink." &&
+             m_bot->getClass() == CLASS_MAGE)
+    {
+            Item const*pItem = FindDrink();
+            if (pItem != NULL) {
+                WorldPacket *p = new WorldPacket(CMSG_INITIATE_TRADE, 8);
+                *p << fromPlayer.GetGUID();
+                m_bot->GetSession()-> HandleInitiateTradeOpcode(*p);
+                SendWhisper ("Here is a tasty treat for you", fromPlayer);
+
+            }
+    }
+    // accept food/drink from mage
+    else if (text == "Here is a tasty treat for you")
+    {
+        m_bot->Say ("Thank you for this treat", LANG_UNIVERSAL);
+        SendWhisper ("Thank you for this treat.", fromPlayer);
+    }
+    // trade opened so send items over
+    else if (text == "Thank you for this treat")
+    {
+        if (m_bot->getClass()!=CLASS_MAGE) return;
+
+        Item const*pItem = FindDrink();
+        if (pItem==NULL) return;
+
+        bool trade = TradeItem (*pItem,2);
+        if (trade) {
+            m_bot->Say("Enjoy the refreshing drink.", LANG_UNIVERSAL);
+
+			WorldPacket* const packet = new WorldPacket(CMSG_ACCEPT_TRADE, 3);
+			m_bot->GetSession()->QueuePacket(packet);  // packet is not used
+        } else {
+            m_bot->Say ("I cannot trade with you.", LANG_UNIVERSAL);
+        }
+    }
+
+    // if in the middle of a trade, and player asks for an item/money
+    else if (m_bot->GetTrader() && m_bot->GetTrader()->GetGUID() == fromPlayer.GetGUID() &&
+        fromPlayer.GetPlayerbotAI() == NULL)
+    {
+        uint32 copper = extractMoney(text);
+        if (copper > 0)
+            TradeCopper(copper);
+
+        std::list<uint32> itemIds;
+        extractItemIds(text, itemIds);
+        if (itemIds.size() == 0)
+            SendWhisper("Show me what item you want by shift clicking the item in the chat window.", fromPlayer);
+        else
+        {
+            std::list<Item*> itemList;
+            findItemsInInv(itemIds, itemList);
+            findItemsInEquip(itemIds, itemList);
+            for (std::list<Item*>::iterator it = itemList.begin(); it != itemList.end(); ++it)
+                TradeItem(**it);
+        }
+    }
+
+
+
+    else if(text == "follow" || text == "come")
+        Follow(*m_master);
+
+    else if(text == "stay" || text == "stop")
+        Stay();
+
+    //handle cast command
+    else if(text.size() > 2 && text.substr(0, 2) == "c " ||
+        text.size() > 5 && text.substr(0, 5) == "cast ")
+        {
+            uint32 spellId = 0;
+            std::string spellStr = text.substr(text.find(" ") + 1);
+
+            if(spellStr.find("Hspell:"))
+            {
+                spellStr = spellStr.substr(spellStr.find("|h[") + 3);
+                spellStr = spellStr.substr(0, spellStr.find("]"));
+            } else
+                spellId = (uint32)atol(spellStr.c_str());
+
+            //try and get spell ID by name
+            if(spellId == 0) spellId = getSpellId(spellStr.c_str(), true);
+
+            uint64 castOnGuid = fromPlayer.GetSelection();
+            if(castOnGuid == 0) castOnGuid = m_bot->GetGUID();
+            if(spellId != 0)
+            {
+                m_spellIdCommand = spellId;
+                m_targetGuidCommand = castOnGuid;
+            }
+    }
+
+    //use items
+    else if(text.size() > 2 && text.substr(0, 2) == "u " ||
+    text.size() > 4 && text.substr(0, 4) == "use ")
+    {
+        std::list<uint32> itemIds;
+        std::list<Item *> itemList;
+        extractItemIds(text, itemIds);
+        findItemsInInv(itemIds, itemList);
+        for(std::list<Item *>::iterator it = itemList.begin(); it != itemList.end(); ++it) UseItem(**it);
+    }
+
+    // poison mainhand weapon
+    else if(text.size() > 2 && text.substr(0, 2) == "p " ||
+    text.size() >= 8 && text.substr(0, 8) == "poison m")
+    {
+
+        Item *poison = FindPoisonForward();
+        if(poison == NULL) {
+            std::string msg = "No poison found for mainhand.";
+            SendWhisper(msg, fromPlayer);
+            return;
+        }
+
+        PoisonWeapon(*poison, poison->GetProto()->Spells[0].SpellId, TARGET_FLAG_ITEM, EQUIPMENT_SLOT_MAINHAND);
+    }
+
+    // poison offhand weapon
+    else if(text.size() >= 8 && text.substr(0, 8) == "poison o")
+    {
+
+        Item *poison = FindPoisonBackward();
+        if(poison == NULL) {
+            std::string msg = "No poison found for offhand.";
+            SendWhisper(msg, fromPlayer);
+            return;
+        }
+
+        PoisonWeapon(*poison, poison->GetProto()->Spells[0].SpellId, TARGET_FLAG_ITEM, EQUIPMENT_SLOT_OFFHAND);
+    }
+
+    // npcbot commands
+    else if(text.size() >= 8 && text.substr(0, 8) == "npcbot a")
+    {
+        if(m_bot->HaveBot()) {
+            SendWhisper("I already have a bot.", fromPlayer);
+            return;
+        }
+
+        std::string text1 = text.substr(text.find(" ") + 1);
+        std::string botClass = text1.substr(text1.find(" ") + 1);
+
+
+        if (botClass == "priest") m_bot->CreateNPCBot(CLASS_PRIEST);
+        else if (botClass == "warrior") m_bot->CreateNPCBot(CLASS_WARRIOR);
+        else if (botClass == "druid") m_bot->CreateNPCBot(CLASS_DRUID);
+        else if (botClass == "paladin") m_bot->CreateNPCBot(CLASS_PALADIN);
+        else if (botClass == "hunter") m_bot->CreateNPCBot(CLASS_HUNTER);
+        else if (botClass == "mage") m_bot->CreateNPCBot(CLASS_MAGE);
+        else if (botClass == "warlock") m_bot->CreateNPCBot(CLASS_WARLOCK);
+        else if (botClass == "shaman") m_bot->CreateNPCBot(CLASS_SHAMAN);
+        else if (botClass == "rogue") m_bot->CreateNPCBot(CLASS_ROGUE);
+        else {
+             SendWhisper("Unknown class", fromPlayer);
+             return;
+        }
+
+       // m_master->CreateNPCBot(CLASS_PALADIN);
+    }
+    else if(text.size() >= 8 && text.substr(0, 8) == "npcbot d")
+    {
+        if(m_bot->HaveBot())
+            m_bot->SetBotMustDie();
+    }
+
+    //equip items
+    else if(text.size() > 2 && text.substr(0, 2) == "e " ||
+    text.size() > 6 && text.substr(0, 6) == "equip ")
+    {
+        std::list<uint32> itemIds;
+        std::list<Item *> itemList;
+        extractItemIds(text, itemIds);
+        findItemsInInv(itemIds, itemList);
+         for(std::list<Item *>::iterator it = itemList.begin(); it != itemList.end(); ++it) EquipItem(**it);
+    }
+
+    else if(text == "spells")
+    {
+        int loc = m_master->GetSession()->GetSessionDbcLocale();
+
+        std::ostringstream posOut;
+        std::ostringstream negOut;
+
+        const std::string ignoreList = ",Opening,Closing,Stuck,Remove Insignia,Opening - No Text,Grovel,Duel,Honorless Target,";
+        std::string alreadySeenList = ",";
+
+        for(PlayerSpellMap::iterator itr = m_bot->GetSpellMap().begin(); itr != m_bot->GetSpellMap().end(); ++itr)
+        {
+            const uint32 spellId = itr->first;
+
+            if(itr->second->state == PLAYERSPELL_REMOVED || itr->second->disabled || IsPassiveSpell(spellId))
+                continue;
+
+            const SpellEntry *const pSpellInfo = sSpellStore.LookupEntry(spellId);
+            if(!pSpellInfo)
+                continue;
+
+            //|| name.find("Teleport") != -1
+
+            std::string comp = ",";
+            comp.append(pSpellInfo->SpellName[loc]);
+            comp.append(",");
+
+            if(!(ignoreList.find(comp) == std::string::npos &&
+                alreadySeenList.find(comp) == std::string::npos))
+                continue;
+
+            alreadySeenList += pSpellInfo->SpellName[loc];
+            alreadySeenList += ",";
+
+            if(IsPositiveSpell(spellId))
+                posOut << " |cffffffff|Hspell:" << spellId << "|h[" << pSpellInfo->SpellName[loc] << "]|h|r";
+            else
+                negOut << " |cffffffff|Hspell:" << spellId << "|h[" << pSpellInfo->SpellName[loc] << "]|h|r";
+        }
+
+        ChatHandler ch(&fromPlayer);
+        SendWhisper("Here's my non-attack spells:", fromPlayer);
+        ch.SendSysMessage(posOut.str().c_str());
+        SendWhisper("Here's my attack spells:", fromPlayer);
+        ch.SendSysMessage(negOut.str().c_str());
+    }
+
+    else if (text.size() > 13 && text.substr(0,13) == "accept quest ")
+    {
+        uint32 questId = 0;
+        std::string questStr = text.substr(text.find(" ") + 1);
+        questStr = questStr.substr(questStr.find(" ") + 1);
+        std::string questStrLink = questStr;
+        if (questStr.find("Hquest:"))
+        {
+            questStr = questStr.substr(questStr.find("|h[") + 3);
+            questStr = questStr.substr(0, questStr.find("]"));
+        }
+        else
+            questId = (uint32)atol(questStr.c_str());
+
+        // try and get quest ID by name
+        if (questId == 0)
+            questId = getQuestId(questStr.c_str(), 0);
+        std::ostringstream out;
+        out << "Quest " << questId << " " << questStr.c_str();
+
+        PlayerbotChatHandler ch(m_master);
+        uint64 oldSel = 0;
+        if (m_master->GetSelection())
+            oldSel = m_master->GetSelection();
+        m_master->SetSelection(m_bot->GetGUID());
+        if (questId != 0 && ch.acceptQuest(questStrLink.c_str()))
+        {
+            out << " accepted.";
+            TellMaster(out.str());
+        }
+        else
+        {
+            out << " not accepted.";
+            TellMaster(out.str());
+        }
+        if (oldSel > 0)
+            m_master->SetSelection(oldSel);
+    }
+
+    else if (text.size() > 14 && text.substr(0,14) == "abandon quest ")
+    {
+        uint32 questId = 0;
+        std::string questStr = text.substr(text.find(" ") + 1);
+        questStr = questStr.substr(questStr.find(" ") + 1);
+        std::string questStrLink = questStr;
+        if (questStr.find("Hquest:"))
+        {
+            questStr = questStr.substr(questStr.find("|h[") + 3);
+            questStr = questStr.substr(0, questStr.find("]"));
+        }
+        else
+            questId = (uint32)atol(questStr.c_str());
+
+        // try and get quest ID by name
+        if (questId == 0)
+            questId = getQuestId(questStr.c_str(), 1);
+        std::ostringstream out;
+        out << "Quest " << questId << " " << questStr.c_str();
+
+        PlayerbotChatHandler ch(m_master);
+        uint64 oldSel = 0;
+        if (m_master->GetSelection())
+            oldSel = m_master->GetSelection();
+        m_master->SetSelection(m_bot->GetGUID());
+        if (questId != 0 && ch.abandonQuest(questStrLink.c_str()))
+        {
+            out << " abandoned.";
+            TellMaster(out.str());
+        }
+               else
+        {
+            out << " not abandoned.";
+            TellMaster(out.str());
+        }
+        if (oldSel > 0)
+            m_master->SetSelection(oldSel);
+    }
+
+    else if (text.size() == 1 && text.substr(0,1) == "q" ||
+        text == "quests")
+    {
+        std::ostringstream out;
+        for (QuestStatusMap::iterator iter=m_bot->getQuestStatusMap().begin(); iter!=m_bot->getQuestStatusMap().end(); ++iter)
+        {
+            const Quest *qInfo = sObjectMgr->GetQuestTemplate(iter->first);
+            if (!qInfo)
+                continue;
+            QuestStatusData *qData = &iter->second;
+
+            uint32 questId = qInfo->GetQuestId();
+            const std::string name = qInfo->GetTitle();
+            if (name.empty())
+                continue;
+            //out << qData->m_status << " ";
+            if (qData->m_status == QUEST_STATUS_NONE)
+            {
+                //out << "Quest " << " |cffffffff|Hquest:" << questId << "|h[" << name << "]|h|r" << " ";
+                //out << " no status" << "\n";
+                continue;
+            }
+            else if (qData->m_status == QUEST_STATUS_COMPLETE)
+            {
+                out << "Quest " << " |cffffffff|Hquest:" << questId << "|h[" << name << "]|h|r" << " ";
+                out << " complete" << "\n";
+            }
+            else if (qData->m_status == QUEST_STATUS_UNAVAILABLE)
+            {
+                //out << "Quest " << " |cffffffff|Hquest:" << questId << "|h[" << name << "]|h|r" << " ";
+                //out << " unavailable" << "\n";
+                continue;
+            }
+            else if (qData->m_status == QUEST_STATUS_INCOMPLETE)
+            {
+                out << "Quest " << " |cffffffff|Hquest:" << questId << "|h[" << name << "]|h|r" << " ";
+                out << " incomplete" << "\n";
+            }
+            else if (qData->m_status == QUEST_STATUS_AVAILABLE)
+            {
+                //out << "Quest " << " |cffffffff|Hquest:" << questId << "|h[" << name << "]|h|r" << " ";
+                //out << " available" << "\n";
+                continue;
+            }
+            else if (qData->m_status == QUEST_STATUS_FAILED)
+            {
+                out << "Quest " << " |cffffffff|Hquest:" << questId << "|h[" << name << "]|h|r" << " ";
+                out << " failed" << "\n";
+            }
+            else
+            {
+                //out << "Quest " << " |cffffffff|Hquest:" << questId << "|h[" << name << "]|h|r" << " ";
+                //out << " unknown" << "\n";
+                continue;
+            }
+        }
+        if (!out.str().empty())
+            TellMaster(out.str());
+    }
+
+    else if (text == "train")
+    {
+         Unit *unit = m_master->GetSelectedUnit();
+
+         if (!unit)
+         {
+             TellMaster("Please select the trainer which I should learn  from!");
+             return;
+         }
+         if (!unit->isTrainer())
+         {
+             TellMaster("This is not a trainer!");
+             return;
+         }
+
+         Creature *creature =  m_bot->GetMap()->GetCreature(m_master->GetSelection());
+         if(!creature->isCanTrainingOf(m_bot, false))
+         {
+             TellMaster("This trainer can not train me anything at  all!");
+             return;
+         }
+
+         CreatureInfo const *ci = creature->GetCreatureInfo();
+
+         if (!ci)
+         {
+             TellMaster("This trainer can not train me anything at  all!");
+             return;
+         }
+
+         TrainerSpellData const* trainer_spells =  creature->GetTrainerSpells();
+         if(!trainer_spells)
+         {
+             TellMaster("No training spells can be found from this  trainer");
+             return;
+         }
+         // reputation discount
+         float fDiscountMod =  m_bot->GetReputationPriceDiscount(creature);
+         bool can_learn_primary_prof =  m_bot->GetFreePrimaryProfessionPoints() > 0;
+
+         std::ostringstream msg;
+         msg << "I had learnt the following spells:\n";
+         uint32 totalCost = 0;
+         uint32 totalSpellLearnt = 0;
+         int loc = m_master->GetSession()->GetSessionDbcLocale();
+         for (TrainerSpellMap::const_iterator itr =  trainer_spells->spellList.begin(); itr !=  trainer_spells->spellList.end(); ++itr)
+         {
+             TrainerSpell const* tSpell = &itr->second;
+
+             bool valid = true;
+             bool primary_prof_first_rank = false;
+             for (uint8 i = 0; i < MAX_SPELL_EFFECTS ; ++i)
+             {
+                 if (!tSpell->learnedSpell[i])
+                     continue;
+                  if(!m_bot->IsSpellFitByClassAndRace(tSpell->learnedSpell[i]))
+                 {
+                     valid = false;
+                     break;
+                 }
+                 if  (sSpellMgr->IsPrimaryProfessionFirstRankSpell(tSpell->learnedSpell[i]))
+                     primary_prof_first_rank = true;
+             }
+             if (!valid)
+                 continue;
+
+             TrainerSpellState state =  m_bot->GetTrainerSpellState(tSpell);
+             if (state != TRAINER_SPELL_GREEN)
+             {
+                 continue;
+             }
+             //data << uint32(primary_prof_first_rank &&  can_learn_primary_prof ? 1 : 0);
+             uint32 spellId = tSpell->spell;
+             const SpellEntry *const pSpellInfo =  sSpellStore.LookupEntry(spellId);
+             if (!pSpellInfo)
+             {
+                 continue;
+             }
+             uint32 cost = (uint32) (floor(tSpell->spellCost *  fDiscountMod));
+
+             // check money requirement
+             if(m_bot->GetMoney() < cost )
+                 continue;
+
+             m_bot->ModifyMoney( -int32(cost) );
+
+             // learn explicitly or cast explicitly
+             if(tSpell->IsCastable())
+                 //FIXME: prof. spell entry in trainer list not marked  gray until list re-open.
+                 m_bot->CastSpell(m_bot,tSpell->spell,true);
+             else
+                 m_bot->learnSpell(spellId,false);
+             totalSpellLearnt++;
+             totalCost = totalCost + cost;
+
+             msg << " |cffffffff|Hspell:" << spellId <<  "|h[" << pSpellInfo->SpellName[loc] << "]|h|r" <<  ", ";
+             uint32 gold = uint32(cost / 10000);
+             cost -= (gold * 10000);
+             uint32 silver = uint32(cost / 100);
+             cost -= (silver * 100);
+             if (gold > 0)
+             {
+                 msg << gold <<  "|TInterface\\Icons\\INV_Misc_Coin_01:16|t";
+             }
+             if (silver > 0)
+             {
+                 msg << silver <<  "|TInterface\\Icons\\INV_Misc_Coin_03:16|t";
+             }
+             msg << cost <<  "|TInterface\\Icons\\INV_Misc_Coin_05:16|t\n";
+         }
+
+         uint32 gold = uint32(totalCost / 10000);
+         totalCost -= (gold * 10000);
+         uint32 silver = uint32(totalCost / 100);
+         totalCost -= (silver * 100);
+         msg << "Total of " << totalSpellLearnt << "  spell(s) learnt, ";
+         if (gold > 0)
+         {
+             msg << gold <<  "|TInterface\\Icons\\INV_Misc_Coin_01:16|t";
+         }
+         if (silver > 0)
+         {
+             msg << silver <<  "|TInterface\\Icons\\INV_Misc_Coin_03:16|t";
+         }
+         msg << totalCost <<  "|TInterface\\Icons\\INV_Misc_Coin_05:16|t spent.";
+
+         TellMaster(msg.str());
+
+         m_bot->GetPlayerbotAI()->GetClassAI()->LoadSpells();
+
+    }
+    else if (text == "train list")
+    {
+         Unit *unit = m_master->GetSelectedUnit();
+
+         if (!unit)
+         {
+             TellMaster("Please select the trainer which I should learn  from!");
+             return;
+         }
+         if (!unit->isTrainer())
+         {
+             TellMaster("This is not a trainer!");
+             return;
+         }
+
+         Creature *creature =  m_bot->GetMap()->GetCreature(m_master->GetSelection());
+         if(!creature->isCanTrainingOf(m_bot, false))
+         {
+             TellMaster("This trainer can not train me anything at  all!");
+             return;
+         }
+
+         CreatureInfo const *ci = creature->GetCreatureInfo();
+
+         if (!ci)
+         {
+             TellMaster("This trainer can not train me anything at  all!");
+             return;
+         }
+
+         TrainerSpellData const* trainer_spells =  creature->GetTrainerSpells();
+         if(!trainer_spells)
+         {
+             TellMaster("No training spells can be found from this  trainer");
+             return;
+         }
+         // reputation discount
+         float fDiscountMod =  m_bot->GetReputationPriceDiscount(creature);
+         bool can_learn_primary_prof =  m_bot->GetFreePrimaryProfessionPoints() > 0;
+
+         std::ostringstream msg;
+         msg << "The spells I can learn and their costs are:\n";
+         uint32 totalCost = 0;
+         int loc = m_master->GetSession()->GetSessionDbcLocale();
+         for (TrainerSpellMap::const_iterator itr =  trainer_spells->spellList.begin(); itr !=  trainer_spells->spellList.end(); ++itr)
+         {
+             TrainerSpell const* tSpell = &itr->second;
+
+             bool valid = true;
+             bool primary_prof_first_rank = false;
+             for (uint8 i = 0; i < MAX_SPELL_EFFECTS ; ++i)
+             {
+                 if (!tSpell->learnedSpell[i])
+                     continue;
+                  if(!m_bot->IsSpellFitByClassAndRace(tSpell->learnedSpell[i]))
+                 {
+                     valid = false;
+                     break;
+                 }
+                 if  (sSpellMgr->IsPrimaryProfessionFirstRankSpell(tSpell->learnedSpell[i]))
+                     primary_prof_first_rank = true;
+             }
+             if (!valid)
+                 continue;
+
+             TrainerSpellState state =  m_bot->GetTrainerSpellState(tSpell);
+             if (state != TRAINER_SPELL_GREEN)
+             {
+                 continue;
+             }
+             //data << uint32(primary_prof_first_rank &&  can_learn_primary_prof ? 1 : 0);
+             uint32 spellId = tSpell->spell;
+             const SpellEntry *const pSpellInfo =  sSpellStore.LookupEntry(spellId);
+             if (!pSpellInfo)
+             {
+                 continue;
+             }
+             uint32 cost = (uint32) (floor(tSpell->spellCost *  fDiscountMod));
+             totalCost = totalCost + cost;
+
+             uint32 gold = uint32(cost / 10000);
+             cost -= (gold * 10000);
+             uint32 silver = uint32(cost / 100);
+             cost -= (silver * 100);
+             msg << " |cffffffff|Hspell:" << spellId <<  "|h[" << pSpellInfo->SpellName[loc] << "]|h|r" <<  ", ";
+             if (gold > 0)
+             {
+                 msg << gold <<  "|TInterface\\Icons\\INV_Misc_Coin_01:16|t";
+             }
+             if (silver > 0)
+             {
+                 msg << silver <<  "|TInterface\\Icons\\INV_Misc_Coin_03:16|t";
+             }
+             msg << cost <<  "|TInterface\\Icons\\INV_Misc_Coin_05:16|t\n";
+         }
+         uint32 moneyDiff = m_bot->GetMoney() - totalCost;
+         if (moneyDiff >= 0)
+         {
+             uint32 gold = uint32(moneyDiff / 10000);
+             moneyDiff -= (gold * 10000);
+             uint32 silver = uint32(moneyDiff / 100);
+             moneyDiff -= (silver * 100);
+             if (gold > 0)
+             {
+                 msg << gold <<  "|TInterface\\Icons\\INV_Misc_Coin_01:16|t";
+             }
+             if (silver > 0)
+             {
+                 msg << silver <<  "|TInterface\\Icons\\INV_Misc_Coin_03:16|t";
+             }
+             msg << moneyDiff <<  "|TInterface\\Icons\\INV_Misc_Coin_05:16|t left.";
+             }
+         else
+         {
+             moneyDiff = moneyDiff * (-1);
+             uint32 gold = uint32(moneyDiff / 10000);
+             moneyDiff -= (gold * 10000);
+             uint32 silver = uint32(moneyDiff / 100);
+             moneyDiff -= (silver * 100);
+             msg << "I need ";
+             if (gold > 0)
+             {
+                 msg << gold <<  "|TInterface\\Icons\\INV_Misc_Coin_01:16|t";
+             }
+             if (silver > 0)
+             {
+                 msg << silver <<  "|TInterface\\Icons\\INV_Misc_Coin_03:16|t";
+             }
+             msg << moneyDiff <<  "|TInterface\\Icons\\INV_Misc_Coin_05:16|t more to learn all the  spells!";
+         }
+         TellMaster(msg.str());
+
+    }
+ else if(text.size() >= 4 && text.substr(0, 4) == "sell")
+    {
+        Unit *unit = m_master->GetSelectedUnit();
+
+        if (!unit)
+        {
+            TellMaster("Please show me who I should trade with!");
+            return;
+        }
+        if (!unit->isVendor())
+        {
+            TellMaster("This person does not want to trade with me!");
+            return;
+        }
+        if (!m_bot->IsInMap((WorldObject*) unit))
+        {
+            TellMaster("I'm too far away to sell items!");
+            return;
+        }
+        uint32 TotalCost = 0;
+        uint32 TotalSold = 0;
+        std::ostringstream report;
+
+        std::list<uint32> itemIds;
+        std::list<Item *> itemList;
+        extractItemIds(text, itemIds);
+        findItemsInInv(itemIds, itemList);
+        for(std::list<Item *>::iterator it = itemList.begin(); it != itemList.end(); ++it)
+        {
+            if ((**it).GetProto()->SellPrice > 0)
+            {
+                int32 cost = (**it).GetCount() * (**it).GetProto()->SellPrice;
+                m_bot->ModifyMoney(cost);
+                m_bot->MoveItemFromInventory((**it).GetBagSlot(), (**it).GetSlot(), true);
+
+                TotalSold = TotalSold + 1;
+                TotalCost = TotalCost + cost;
+
+                if ((**it).GetCount() > 0) {
+                    report << "Sold " << (**it).GetCount() << "x";
+                    report << " |cffffffff|Hitem:" << (**it).GetProto()->ItemId << ":0:0:0:0:0:0:0" << "|h[" << (**it).GetProto()->Name1 << "]|h|r";
+                    report << " for ";
+
+                    uint32 gold = uint32(cost / 10000);
+                    cost -= (gold * 10000);
+                    uint32 silver = uint32(cost / 100);
+                    cost -= (silver * 100);
+
+                    if (gold > 0)
+                    {
+                        report << gold << "|TInterface\\Icons\\INV_Misc_Coin_01:16|t";
+                    }
+                    if (silver > 0)
+                    {
+                        report << silver << "|TInterface\\Icons\\INV_Misc_Coin_03:16|t";
+                    }
+                    report << cost << "|TInterface\\Icons\\INV_Misc_Coin_05:16|t\n";
+                }
+            }
+        }
+        if (TotalSold > 0) {
+            report << "Sold total " << TotalSold << " item(s) for ";
+
+            uint32 gold = uint32(TotalCost / 10000);
+            TotalCost -= (gold * 10000);
+            uint32 silver = uint32(TotalCost / 100);
+            TotalCost -= (silver * 100);
+
+            if (gold > 0)
+            {
+                report << gold << "|TInterface\\Icons\\INV_Misc_Coin_01:16|t";
+            }
+            if (silver > 0)
+            {
+                report << silver << "|TInterface\\Icons\\INV_Misc_Coin_03:16|t";
+            }
+            report << TotalCost << "|TInterface\\Icons\\INV_Misc_Coin_05:16|t.";
+            TellMaster(report.str());
+        }
+    }
+
+ else if (text.size() > 2 && text.substr(0, 2) == "g " || text.size() > 4 && text.substr(0, 4) == "get ")
+ {
+     uint32 guid;
+     float x,y,z;
+     uint32 entry;
+     int mapid;
+     if (extractGOinfo(text, guid, entry, mapid, x, y, z))
+     {
+         sLog->outDebug("find: guid : %u entry : %u x : (%f) y : (%f) z : (%f) mapid : %d",guid, entry, x, y, z, mapid);
+         m_lootCurrent = MAKE_NEW_GUID(guid, entry, HIGHGUID_GAMEOBJECT);
+         GameObject *go = m_bot->GetMap()->GetGameObject(m_lootCurrent);
+         if (!go)
+         {
+             m_bot->Say("I can't find it.", LANG_UNIVERSAL);
+             m_lootCurrent = 0;
+             return;
+         }
+
+         if ( !go->isSpawned() ) {
+             m_bot->Say("It is not there anymore.", LANG_UNIVERSAL);
+             return;
+         }
+
+         m_bot->UpdateGroundPositionZ(x,y,z);
+         m_bot->GetMotionMaster()->MovePoint( mapid, x, y, z );
+         m_bot->SetPosition(x, y, z, m_bot->GetOrientation());
+         m_bot->SendLoot( m_lootCurrent, LOOT_CORPSE );
+         Loot *loot = &go->loot;
+         uint32 lootNum = loot->GetMaxSlotInLootFor( m_bot );
+
+         sLog->outDebug( "[PlayerbotAI]: GetGOType %u - %s looting: '%s' got %d items", go->GetGoType(), m_bot->GetName(), go->GetGOInfo()->name, loot->GetMaxSlotInLootFor( m_bot ));
+         for ( uint32 l=0; l<lootNum; l++ )
+         {
+             QuestItem *qitem=0, *ffaitem=0, *conditem=0;
+             LootItem *item = loot->LootItemInSlot( l, m_bot, &qitem, &ffaitem, &conditem );
+             if ( !item )
+                 continue;
+
+             if ( !qitem && item->is_blocked )
+             {
+                 m_bot->SendLootRelease( m_lootCurrent );
+                 continue;
+             }
+
+             if ( m_needItemList[item->itemid]>0 )
+             {
+                 ItemPosCountVec dest;
+                 if ( m_bot->CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, item->itemid, item->count ) == EQUIP_ERR_OK )
+                 {
+                     Item * newitem = m_bot->StoreNewItem( dest, item->itemid, true, item->randomPropertyId);
+                     if ( qitem )
+                     {
+                         qitem->is_looted = true;
+                         if ( item->freeforall || loot->GetPlayerQuestItems().size() == 1 )
+                             m_bot->SendNotifyLootItemRemoved( l );
+                         else
+                             loot->NotifyQuestItemRemoved( qitem->index );
+                     }
+                     else
+                     {
+                         if ( ffaitem )
+                         {
+                             ffaitem->is_looted=true;
+                             m_bot->SendNotifyLootItemRemoved( l );
+                         }
+                         else
+                         {
+                             if ( conditem )
+                                 conditem->is_looted=true;
+                             loot->NotifyItemRemoved( l );
+                         }
+                     }
+                     if (!item->freeforall)
+                         item->is_looted = true;
+                     --loot->unlootedCount;
+                     m_bot->SendNewItem( newitem, uint32(item->count), false, false, true );
+
+                     m_bot->GetAchievementMgr().UpdateAchievementCriteria( ACHIEVEMENT_CRITERIA_TYPE_LOOT_ITEM, item->itemid, item->count );
+                 }
+             }
+             uint32 lockId = go->GetGOInfo()->GetLockId();
+             LockEntry const *lockInfo = sLockStore.LookupEntry(lockId);
+             if(lockInfo)
+             {
+                 uint32 skillId = SkillByLockType(LockType(lockInfo->Index[0]));
+                 switch(skillId)
+                 {
+                 case SKILL_MINING:
+                     if (m_bot->HasSkill(SKILL_MINING) && HasPick()) // Has skill & suitable pick
+                     {
+                         ItemPosCountVec dest;
+                         if ( m_bot->CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, item->itemid, item->count) == EQUIP_ERR_OK )
+                         {
+                             Item* pItem = m_bot->StoreNewItem (dest,item->itemid,true,item->randomPropertyId);
+                             uint32 reqSkillValue = lockInfo->Skill[0];
+                             uint32 SkillValue = m_bot->GetPureSkillValue(SKILL_MINING);
+                             if (SkillValue >= reqSkillValue)
+                             {
+                                 m_bot->SendNewItem(pItem, uint32(item->count), false, false, true);
+                                 m_bot->UpdateGatherSkill(SKILL_MINING, SkillValue, reqSkillValue);
+                                 --loot->unlootedCount;
+                             }
+                         }
+                     }
+                     break;
+                 case SKILL_HERBALISM:
+                     if (m_bot->HasSkill(SKILL_HERBALISM)) // Has skill
+                     {
+                         ItemPosCountVec dest;
+                         if ( m_bot->CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, item->itemid, item->count) == EQUIP_ERR_OK )
+
+                         {
+                             Item* pItem = m_bot->StoreNewItem (dest,item->itemid,true,item->randomPropertyId);
+                             uint32 reqSkillValue = lockInfo->Skill[0];
+                             uint32 SkillValue = m_bot->GetPureSkillValue(SKILL_HERBALISM);
+                             if (SkillValue >= reqSkillValue)
+                             {
+                                 m_bot->SendNewItem(pItem, uint32(item->count), false, false, true);
+                                 m_bot->UpdateGatherSkill(SKILL_HERBALISM, SkillValue, reqSkillValue);
+                                 --loot->unlootedCount;
+                             }
+                         }
+                     }
+                     break;
+                 }
+             }
+         }
+         // release loot
+         m_bot->GetSession()->DoLootRelease( m_lootCurrent );
+
+         // clear movement target, take next target on next update
+         m_bot->GetMotionMaster()->Clear();
+         m_bot->GetMotionMaster()->MoveIdle();
+         sLog->outDebug( "[PlayerbotAI]: %s looted target 0x%08X", m_bot->GetName(), m_lootCurrent );
+         SetQuestNeedItems();
+     }
+     else
+         SendWhisper("I have no info on that object", fromPlayer);
+ }
+
+ else if (text == "survey")
+ {
+      float distance = 100.0f;
+      uint32 count = 0;
+      std::ostringstream detectout;
+
+      QueryResult result = WorldDatabase.PQuery("SELECT guid, id, position_x, position_y, position_z, map, "
+        "(POW(position_x - '%f', 2) + POW(position_y - '%f', 2) + POW(position_z - '%f', 2)) AS order_ "
+        "FROM gameobject WHERE map='%u' AND (POW(position_x - '%f', 2) + POW(position_y - '%f', 2) + POW(position_z - '%f', 2)) <= '%f' ORDER BY order_",
+         m_bot->GetPositionX(), m_bot->GetPositionY(), m_bot->GetPositionZ(),
+         m_bot->GetMapId(), m_bot->GetPositionX(), m_bot->GetPositionY(), m_bot->GetPositionZ(), distance*distance);
+
+      if (result)
+      {
+         do
+         {
+             Field *fields = result->Fetch();
+             uint32 guid = fields[0].GetUInt32();
+             uint32 entry = fields[1].GetUInt32();
+             float x = fields[2].GetFloat();
+             float y = fields[3].GetFloat();
+             float z = fields[4].GetFloat();
+             int mapid = fields[5].GetUInt16();
+
+             GameObjectInfo const * gInfo = ObjectMgr::GetGameObjectInfo(entry);
+
+             if(!gInfo)
+                 continue;
+
+             uint64 objGuid = MAKE_NEW_GUID(guid, entry, HIGHGUID_GAMEOBJECT);
+             GameObject *go = m_bot->GetMap()->GetGameObject(objGuid);
+             if ( !go || (go && !go->isSpawned()) ) continue;
+
+             if(count < 12) // count, limits number of links
+                  detectout << "|cFFFFFF00|Hfound:" << guid << ":" << entry << ":" << x << ":" << y << ":" << z  << ":" << mapid  << ":" <<  "|h[" << gInfo->name << "]|h|r";
+             ++count;
+         } while (result->NextRow());
+
+      }
+      SendWhisper(detectout.str().c_str(), fromPlayer);
+    }
+
+    else if (text == "pull")
+    {
+        m_bot->GetPlayerbotAI()->GetClassAI()->Pull();
+    }
+    else if(text == "help")
+    {
+        std::string msg;
+        if (m_bot->getClass() == CLASS_ROGUE)
+            msg = "The commands I respond to are \n follow, stay, (c)ast <spellname>, spells, (e)quip, (u)se, \nnpcbot (a)dd, npcbot (d)elete <class>,\n(q)uests, accept quest <hlink>, abandon quest <hlink>\npoison [main | off].\ntrain list";
+        else
+            msg = "The commands I respond to are \n follow, stay, (c)ast <spellname>, spells, (e)quip, (u)se, \nnpcbot (a)dd, npcbot (d)elete <class>,\n(q)uests, accept quest <hlink>, abandon quest <hlink>\ntrain list.";
+
+        SendWhisper(msg, fromPlayer);
+        m_bot->HandleEmoteCommand(EMOTE_ONESHOT_TALK);
+    }
+      else
+    {
+        // if this looks like an item link, reward item it completed quest and talking to NPC
+        std::list<uint32> itemIds;
+        extractItemIds(text, itemIds);
+        if (!itemIds.empty()) {
+            uint32 itemId = itemIds.front();
+            bool wasRewarded = false;
+            uint64 questRewarderGUID = m_bot->GetSelection();
+            Object* const pNpc = ObjectAccessor::GetObjectByTypeMask(*m_bot, questRewarderGUID, TYPEMASK_UNIT|TYPEMASK_GAMEOBJECT);
+            if (!pNpc)
+                return;
+
+            QuestMenu& questMenu = m_bot->PlayerTalkClass->GetQuestMenu();
+            for (uint32 iI = 0; !wasRewarded && iI < questMenu.MenuItemCount(); ++iI)
+            {
+                QuestMenuItem const& qItem = questMenu.GetItem(iI);
+
+                uint32 questID = qItem.m_qId;
+                Quest const* pQuest = sObjectMgr->GetQuestTemplate(questID);
+                QuestStatus status = m_bot->GetQuestStatus(questID);
+
+                // if quest is complete, turn it in
+                if (status == QUEST_STATUS_COMPLETE &&
+                    ! m_bot->GetQuestRewardStatus(questID) &&
+                    pQuest->GetRewChoiceItemsCount() > 1 &&
+                    m_bot->CanRewardQuest(pQuest, false))
+                {
+                    for (uint8 rewardIdx=0; !wasRewarded && rewardIdx < pQuest->GetRewChoiceItemsCount(); ++rewardIdx)
+                    {
+                        ItemPrototype const * const pRewardItem = sObjectMgr->GetItemPrototype(pQuest->RewChoiceItemId[rewardIdx]);
+                        if (itemId == pRewardItem->ItemId)
+                        {
+                            m_bot->RewardQuest(pQuest, rewardIdx, pNpc, false);
+
+                            std::string questTitle  = pQuest->GetTitle();
+                            m_bot->GetPlayerbotAI()->QuestLocalization(questTitle, questID);
+                            std::string itemName = pRewardItem->Name1;
+                            m_bot->GetPlayerbotAI()->ItemLocalization(itemName, pRewardItem->ItemId);
+
+                            std::ostringstream out;
+                            out << "|cffffffff|Hitem:" << pRewardItem->ItemId << ":0:0:0:0:0:0:0" << "|h[" << itemName << "]|h|r rewarded";
+                            SendWhisper(out.str(), fromPlayer);
+                            wasRewarded = true;
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+}
+
+void PlayerbotAI::SetLooting(bool looting)
+{
+    isLooting = looting;
+}
+
+void PlayerbotAI::AddLootGUID(uint64 guid) {
+    m_lootCreature.push_back(guid);
+}
+
+bool PlayerbotAI::DoLoot()
+{
+    if(!m_lootCurrent && m_lootCreature.empty())
+    {
+        sLog->outDebug("[PlayerbotAI]: %s reset loot list / go back to idle", m_bot->GetName());
+//        SetQuestNeedItems();
+        isLooting = false;
+        return false;
+    }
+
+    if(m_bot->isDead())
+    {
+        isLooting = false;
+        return false;
+    }
+
+    if(!m_lootCurrent)
+    {
+        m_lootCurrent = m_lootCreature.front();
+
+        if(!m_lootCurrent)
+        {
+            //sLog->outError("PlayerbotAI::DoLoot() error location #1, please report this error immediately!");
+            return false;
+        }
+
+        Creature *c = m_bot->GetMap()->GetCreature(m_lootCurrent);
+        GameObject *o = m_bot->GetMap()->GetGameObject( m_lootCurrent );
+
+        if(!c && !o)
+        {
+            m_lootCurrent = 0;
+            isLooting = false;
+            return false;
+        }
+        if(c && c->isAlive())
+        {
+            m_lootCurrent = 0;
+            isLooting = false;
+            return false; //not dead yet
+        }
+
+        sLog->outDebug("[PlayerbotAI]: %s got loot target 0x%08X", m_bot->GetName(), m_lootCurrent);
+        Position pos;
+
+        WorldObject *object;
+        if (c) object = c;
+        else object = o;
+
+        if(m_bot->IsWithinDistInMap(object, INTERACTION_DISTANCE * 3)){ //Verify if the bot it close to a loot.
+
+            m_lootCreature.pop_front();
+            object->GetPosition(&pos);
+            m_bot->GetMotionMaster()->MovePoint(object->GetMapId(), (const Position &)(pos));
+
+        } else { //Rotate the loot to very if one is not near the bot.
+
+            m_lootobjtemp = m_lootCreature.front();
+            m_lootCreature.pop_front();
+            m_lootCreature.push_back(m_lootobjtemp);
+            m_lootCurrent = 0;
+        }
+        return true;
+
+    } else {
+
+        Creature *c = m_bot->GetMap()->GetCreature(m_lootCurrent);
+        GameObject *o = m_bot->GetMap()->GetGameObject( m_lootCurrent );
+        if(!c && !o)
+        {
+            m_lootCurrent = 0;
+            isLooting = false;
+            return false;
+        }
+        if(c && c->isAlive())
+        {
+            m_lootCurrent = 0;
+            isLooting = false;
+            return false; //not dead yet
+        }
+
+        WorldObject *object;
+        if (c) object = c;
+        else object = o;
+
+        if(m_bot->IsWithinDistInMap(object, INTERACTION_DISTANCE))
+        {
+            //check for needed items
+            m_bot->SendLoot(m_lootCurrent, LOOT_CORPSE);
+
+            Loot *loot;
+            if (c)
+                loot = &c->loot;
+            else
+                loot = &o->loot;
+
+            assert(loot);
+
+            uint32 lootNum = loot->GetMaxSlotInLootFor(m_bot);
+//sLog->outError("[PlayerbotAI]: %s loot target 0x%08X got %d items", m_bot->GetName(), m_lootCurrent, loot->GetMaxSlotInLootFor(m_bot));
+
+            for(uint32 l = 0; l < lootNum; ++l)
+            {
+                QuestItem *qitem = 0, *ffaitem = 0, *conditem = 0;
+                LootItem *item = loot->LootItemInSlot(l, m_bot, &qitem, &ffaitem, &conditem);
+                if(!item) continue;
+                if(!qitem && item->is_blocked)
+                {
+                    m_bot->SendLootRelease(m_bot->GetLootGUID());
+                    continue;
+                }
+
+                if(m_needItemList[item->itemid] > 0)
+                {
+//sLog->outError("[PlayerbotAI]: %s LOOT needed item 0x%04X", m_bot->GetName(), item->itemid);
+                    ItemPosCountVec dest;
+                    if(m_bot->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, item->itemid, item->count) == EQUIP_ERR_OK)
+                    {
+                        Item *newitem = m_bot->StoreNewItem(dest, item->itemid,true, item->randomPropertyId);
+                        if(qitem)
+                        {
+                            qitem->is_looted = true;
+                            if(item->freeforall ||loot->GetPlayerQuestItems().size() == 1)
+                                m_bot->SendNotifyLootItemRemoved(l);
+                            else
+                                loot->NotifyQuestItemRemoved(qitem->index);
+                        }
+                        else if(ffaitem)
+                        {
+                            ffaitem->is_looted = true;
+                            m_bot->SendNotifyLootItemRemoved(l);
+                        } else {
+                            if(conditem) conditem->is_looted = true;
+                            loot->NotifyItemRemoved(l);
+                        }
+                        if(!item->freeforall) item->is_looted = true;
+                        --(loot->unlootedCount);
+                        m_bot->SendNewItem(newitem, uint32(item->count), false,false, true);
+                        m_bot->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LOOT_ITEM, item->itemid, item->count);
+
+                        std::ostringstream out;
+                        PlayerbotChatHandler ch(m_master);
+                        out << m_bot->GetName() << " needs " << m_needItemList[item->itemid]-1 << " more.";
+                        ch.sysmessage(out.str().c_str());
+                        m_ignoreAIUpdatesUntilTime = time(0);
+                    }
+                }
+            }
+            //release loot
+            if(uint64 lguid = m_bot->GetLootGUID() && m_bot->GetSession())
+                m_bot->GetSession()->DoLootRelease(lguid);
+            else if(!m_bot->GetSession())
+                sLog->outDebug("[PlayerbotAI]: %s has no session. Cannot releaseloot!", m_bot->GetName());
+
+            //clear movement target, take next target on next update
+            m_bot->GetMotionMaster()->Clear();
+            m_bot->GetMotionMaster()->MoveIdle();
+            sLog->outDebug("[PlayerbotAI]: %s looted target 0x%08X", m_bot->GetName(), m_lootCurrent);
+            m_lootCurrent = 0;
+            m_ignoreAIUpdatesUntilTime = time(0);
+            isLooting = false;
+        } else { //keep moving till we get there
+            Position pos;
+
+            if (c) {
+                c->GetPosition(&pos);
+                m_bot->GetMotionMaster()->MovePoint(c->GetMapId(), (const Position &)(pos));
+            } else {
+                o->GetPosition(&pos);
+                m_bot->GetMotionMaster()->MovePoint(o->GetMapId(), (const Position &)(pos));
+            }
+
+        }
+    }
+    return false;
+} //end DoLoot
+
+void PlayerbotAI::SetQuestNeedItems()
+{
+    //reset values first
+    m_needItemList.clear();
+    m_lootCreature.clear();
+    m_lootCurrent = 0;
+
+    //run through accepted quests, get quest infoand data
+    for(QuestStatusMap::iterator iter=m_bot->getQuestStatusMap().begin(); iter!=m_bot->getQuestStatusMap().end(); ++iter)
+    {
+        const Quest *qInfo = sObjectMgr->GetQuestTemplate(iter->first);
+        if(!qInfo) continue;
+        QuestStatusData *qData = &iter->second;
+
+        //only check quest if it is incomplete
+        if(qData->m_status != QUEST_STATUS_INCOMPLETE) continue;
+
+        //check for items we not have enough of
+        for(uint8 i = 0; i < QUEST_OBJECTIVES_COUNT; i++)
+        {
+            if(!qInfo->ReqItemCount[i] || (qInfo->ReqItemCount[i]-qData->m_itemcount[i]) <= 0) continue;
+            m_needItemList[qInfo->ReqItemId[i]] = (qInfo->ReqItemCount[i]-qData->m_itemcount[i]);
+        }
+    }
+}//end SetQuestNeedItems
+
+//Localization support
+void PlayerbotAI::ItemLocalization(std::string &itemName, const uint32 itemID) const
+{
+    int loc = m_master->GetSession()->GetSessionDbLocaleIndex();
+    std::wstring wnamepart;
+    ItemLocale const *pItemInfo = sObjectMgr->GetItemLocale(itemID);
+    if(pItemInfo)
+    {
+        if(pItemInfo->Name.size() > loc && !pItemInfo->Name[loc].empty())
+        {
+            const std::string name = pItemInfo->Name[loc];
+            if(Utf8FitTo(name, wnamepart))
+                itemName = name.c_str();
+        }
+    }
+}
+
+void PlayerbotAI::QuestLocalization(std::string &questTitle, const uint32 questID) const
+{
+    int loc = m_master->GetSession()->GetSessionDbLocaleIndex();
+    std::wstring wnamepart;
+    QuestLocale const *pQuestInfo = sObjectMgr->GetQuestLocale(questID);
+    if(pQuestInfo)
+    {
+        if(pQuestInfo->Title.size() > loc && !pQuestInfo->Title[loc].empty())
+        {
+            const std::string title = pQuestInfo->Title[loc];
+            if(Utf8FitTo(title, wnamepart))
+                questTitle = title.c_str();
+        }
+    }
+}
+
+void PlayerbotAI::TurnInQuests( WorldObject *pNpc )
+{
+
+    uint64 npcGUID = pNpc->GetGUID();
+    if (!m_bot->IsInMap((WorldObject*) pNpc))
+        m_bot->GetPlayerbotAI()->TellMaster("hey you are turning in quests without me!");
+    else
+    {
+        m_bot->SetSelection(npcGUID);
+
+        // auto complete every completed quest this NPC has
+        m_bot->PrepareQuestMenu(npcGUID);
+        QuestMenu& questMenu = m_bot->PlayerTalkClass->GetQuestMenu();
+        for (uint32 iI = 0; iI < questMenu.MenuItemCount(); ++iI)
+        {
+            QuestMenuItem const& qItem = questMenu.GetItem(iI);
+            uint32 questID = qItem.m_qId;
+            Quest const* pQuest = sObjectMgr->GetQuestTemplate(questID);
+
+            std::ostringstream out;
+            std::string questTitle  = pQuest->GetTitle();
+            m_bot->GetPlayerbotAI()->QuestLocalization(questTitle, questID);
+
+            QuestStatus status = m_bot->GetQuestStatus(questID);
+            // if quest is complete, turn it in
+            if (status == QUEST_STATUS_COMPLETE)
+            {
+                // if bot hasn't already turned quest in
+                if (! m_bot->GetQuestRewardStatus(questID))
+                {
+                    // auto reward quest if no choice in reward
+                    if (pQuest->GetRewChoiceItemsCount() == 0)
+                    {
+                        if (m_bot->CanRewardQuest(pQuest, false))
+                        {
+                            m_bot->RewardQuest(pQuest, 0, pNpc, false);
+                            out << "Quest complete: |cff808080|Hquest:" << questID << ':' << pQuest->GetQuestLevel() << "|h[" << questTitle << "]|h|r";
+                        }
+                        else
+                        {
+                            out << "|cffff0000Unable to turn quest in:|r |cff808080|Hquest:" << questID << ':' << pQuest->GetQuestLevel() << "|h[" << questTitle << "]|h|r";
+                        }
+                    }
+
+                    // auto reward quest if one item as reward
+                    else if (pQuest->GetRewChoiceItemsCount() == 1)
+                    {
+                        int rewardIdx = 0;
+                        ItemPrototype const *pRewardItem = sObjectMgr->GetItemPrototype(pQuest->RewChoiceItemId[rewardIdx]);
+                        std::string itemName = pRewardItem->Name1;
+                        m_bot->GetPlayerbotAI()->ItemLocalization(itemName, pRewardItem->ItemId);
+                        if (m_bot->CanRewardQuest(pQuest, rewardIdx, false))
+                        {
+                            m_bot->RewardQuest(pQuest, rewardIdx, pNpc, true);
+
+                            std::string itemName = pRewardItem->Name1;
+                            m_bot->GetPlayerbotAI()->ItemLocalization(itemName, pRewardItem->ItemId);
+
+                            out << "Quest complete: "
+                                << " |cff808080|Hquest:" << questID << ':' << pQuest->GetQuestLevel()
+                                << "|h[" << questTitle << "]|h|r reward: |cffffffff|Hitem:"
+                                << pRewardItem->ItemId << ":0:0:0:0:0:0:0" << "|h[" << itemName << "]|h|r";
+                        }
+                        else
+                        {
+                            out << "|cffff0000Unable to turn quest in:|r "
+                                << "|cff808080|Hquest:" << questID << ':'
+                                << pQuest->GetQuestLevel() << "|h[" << questTitle << "]|h|r"
+                                << " reward: |cffffffff|Hitem:"
+                                << pRewardItem->ItemId << ":0:0:0:0:0:0:0" << "|h[" << itemName << "]|h|r";
+                        }
+                    }
+
+                    // else multiple rewards - let master pick
+                    else {
+                        out << "What reward should I take for |cff808080|Hquest:" << questID << ':' << pQuest->GetQuestLevel()
+                            << "|h[" << questTitle << "]|h|r? ";
+                        for (uint8 i=0; i < pQuest->GetRewChoiceItemsCount(); ++i)
+                        {
+                            ItemPrototype const * const pRewardItem = sObjectMgr->GetItemPrototype(pQuest->RewChoiceItemId[i]);
+                            std::string itemName = pRewardItem->Name1;
+                            m_bot->GetPlayerbotAI()->ItemLocalization(itemName, pRewardItem->ItemId);
+                            out << "|cffffffff|Hitem:" << pRewardItem->ItemId << ":0:0:0:0:0:0:0" << "|h[" << itemName << "]|h|r";
+                        }
+                    }
+                }
+            }
+
+            else if (status == QUEST_STATUS_INCOMPLETE) {
+                out << "|cffff0000Quest incomplete:|r "
+                    << " |cff808080|Hquest:" << questID << ':' << pQuest->GetQuestLevel() << "|h[" << questTitle << "]|h|r";
+            }
+
+//            else if (status == QUEST_STATUS_AVAILABLE){
+  //              out << "|cff00ff00Quest available:|r "
+//                    << " |cff808080|Hquest:" << questID << ':' << pQuest->GetQuestLevel() << "|h[" << questTitle << "]|h|r";
+//            }
+
+            if (! out.str().empty())
+                m_bot->GetPlayerbotAI()->TellMaster(out.str());
+        }
+    }
+} // TurnInQuests
+
+
+ void PlayerbotAI::SetCombatOrder (CombatOrderType orders)
+ {
+    m_combatOrder = orders;
+ }
diff --git a/src/server/game/AI/Bots/PlayerbotAI.h b/src/server/game/AI/Bots/PlayerbotAI.h
new file mode 100644
index 0000000..e40d6ad
--- /dev/null
+++ b/src/server/game/AI/Bots/PlayerbotAI.h
@@ -0,0 +1,284 @@
+#ifndef _PLAYERBOTAI_H
+#define _PLAYERBOTAI_H
+
+#include "Common.h"
+
+
+class WorldPacket;
+class Player;
+class Unit;
+class Object;
+class Item;
+class PlayerbotClassAI;
+
+enum ScenarioType
+{
+    SCENARIO_PVEEASY,
+    SCENARIO_PVEHARD,
+    SCENARIO_DUEL,
+    SCENARIO_PVPEASY,
+    SCENARIO_PVPHARD
+};
+
+//masters orders that should be obeyed by the AI during the updteAI routine
+//the master will auto set the target of the bot
+enum CombatOrderType
+{
+    ORDERS_NONE,
+    ORDERS_KILL,
+    ORDERS_CC,
+    ORDERS_HEAL,
+    ORDERS_TANK,
+    ORDERS_PROTECT,
+    ORDERS_REGEN
+};
+
+typedef std::set<Unit *> AttackerInfoList;
+
+class PlayerbotAI
+{
+public:
+    //******* Stuff the outside world calls ****************************
+    PlayerbotAI(Player *const master, Player *const bot);
+    virtual ~PlayerbotAI();
+
+    //This is called from Unit.cpp and is called every second (I think)
+    void UpdateAI(const uint32 p_time);
+
+    //This is called from ChatHandler.cpp when there is an incoming message to the bot
+    //from a whisper or from the party channel
+    void HandleCommand(const std::string &text, Player &fromPlayer);
+
+    //This is called by WorldSession.pm
+    //It provides a view of packets normally sent to the client.
+    //Since there is no client at the other end, the packets are dropped of course.
+    //For a list of opcodes that can be caught see Opcodes.cpp (SMSG_* opcodes only)
+    void HandleBotOutgoingPacket(const WorldPacket &packet);
+
+    //This is called whenever the master sends a packet to the server.
+    //These packets can be viewed, but not edited.
+    //It allows bot creators to craft AI in response to a master's actions.
+    //For a list of opcodes that can be caught see Opcodes.cpp (CMSG_* opcodes only)
+    //Notice: that this is static which means it is called once for all bots of the master.
+    static void HandleMasterIncomingPacket(const WorldPacket &packet, WorldSession &masterSession);
+    static void HandleMasterOutgoingPacket(const WorldPacket &packet, WorldSession &masterSession);
+
+    //Returns what kind of situation we are in so the AI can react accordingly
+    ScenarioType GetScenarioType(){ return m_ScenarioType; }
+
+    PlayerbotClassAI *GetClassAI(){ return m_classAI; }
+
+    //protected:
+
+    //******* Utilities ***************************************************
+
+    //finds spell ID for matching substring args
+    //in priority of full text match, spells not taking reagents, and highest rank
+    uint32 getSpellId(const char *args, bool master=false) const;
+    //Main PlayerBot spell finding function Returns ONLY exact matches including Upper/Lower case differentiation.
+    uint32 getSpellIdExact(const char *args, bool includePassive=false, bool master=false);
+
+    // finds quest ID for matching substring args
+    uint32 getQuestId(const char* args, bool remove) const;
+
+    //extracts item ids from links
+    void extractItemIds(const std::string &text, std::list<uint32> &itemIds) const;
+
+    //extracts currency from a string as #g#s#c and returns the total in copper
+    uint32 extractMoney(const std::string &text) const;
+
+    // extracts gameobject info from link
+    bool extractGOinfo(const std::string& text, uint32 &guid,  uint32 &entry, int &mapid, float &x, float &y, float &z) const;
+
+    // finds items in equipment and adds Item* to foundItemList
+    // also removes found item IDs from itemIdSearchList when found
+    void findItemsInEquip(std::list<uint32>& itemIdSearchList, std::list<Item*>& foundItemList) const;
+
+    //finds items in bots inventory and adds them to foundItemList, removes found items from itemIdSearchList
+    void findItemsInInv(std::list<uint32>& itemIdSearchList, std::list<Item*>& foundItemList) const;
+
+    //currently bots only obey commands from the master
+    bool canObeyCommandFrom(const Player &player) const;
+
+    //get current casting spell (will return NULL if no spell!)
+    Spell *GetCurrentSpell() const;
+
+    bool HasAura(uint32 spellId, const Unit *player) const;
+    bool HasAura(const char *spellName, const Unit *player) const;
+    bool HasAura(const char *spellName) const;
+    void HandleTeleportAck();
+
+    bool HasPick();
+
+    uint8 GetHealthPercent(const Unit &target) const;
+    uint8 GetHealthPercent() const;
+    uint8 GetBaseManaPercent(const Unit &target) const;
+    uint8 GetBaseManaPercent() const;
+    uint8 GetManaPercent(const Unit &target) const;
+    uint8 GetManaPercent() const;
+    uint8 GetRageAmount(const Unit &target) const;
+    uint8 GetRageAmount() const;
+    uint8 GetEnergyAmount(const Unit &target) const;
+    uint8 GetEnergyAmount() const;
+    uint8 GetRunicPower(const Unit &target) const;
+    uint8 GetRunicPower() const;
+
+    Item *FindFood() const;
+    Item *FindDrink() const;
+    Item *FindPotion() const;
+    Item *FindBandage() const;
+    Item *FindPoisonForward() const;    // finds poison starting from the front
+    Item *FindPoisonBackward() const;   // finds poison starting from the back
+
+    void UseMount() const;
+
+    //******* Actions ****************************************
+    //Your handlers can call these actions to make the bot do things.
+    void TellMaster(const std::string &text);
+    void SendWhisper(const std::string &text, Player &player);
+    bool CastSpell(const char *args);
+    //Player bots main spell cast function, if checkFirst > performs canCast() first, if castExistingAura > performs the cast even if the aura exists on target,
+    //if skipFriendlyCheck > do not perform spell positive/negative and target friendly/hostile checks (Useful for dual purpose spells like holy shock)
+    virtual bool CastSpell(uint32 spellId, Unit *target=NULL, bool checkFirst=true, bool castExistingAura=false, bool skipFriendlyCheck=false, bool skipEquipStanceCheck=false, bool triggered=false);
+    virtual bool CastSpell(const SpellEntry * pSpellInfo, Unit *target=NULL, bool checkFirst=true, bool castExistingAura=false, bool skipFriendlyCheck=false, bool skipEquipStanceCheck=false, bool triggered=false);
+    //Simple Checks to determine if the bot can cast the spell or not...
+    //Mana/Stance/EquipmentRequirement/Distance/TargetInFront/OverwriteOrStackExistingAura/FriendlyFire checks
+    virtual bool CanCast(uint32 spellId, Unit *target=NULL,bool castExistingAura=false, bool skipFriendlyCheck=false, bool skipEquipStanceCheck=false);
+    virtual bool CanCast(const SpellEntry * pSpellInfo, Unit *target=NULL,bool castExistingAura=false, bool skipFriendlyCheck=false, bool skipEquipStanceCheck=false);
+    //Gets the current form/stance of player
+    uint8 GetForm(Unit *pPlayer=NULL);
+    void UseItem(Item &item);
+    void PoisonWeapon(Item &item, uint32 spellid=0, uint32 target=0, EquipmentSlots weapon=EQUIPMENT_SLOT_MAINHAND);
+    void EquipItem(Item &item);
+    void Stay();
+    bool Follow(Player &player);
+    void SendNotEquipList(Player &player);
+    void Feast();
+    void SetLooting(bool looting);
+    void InterruptCurrentCastingSpell();
+    void GetCombatOrders();
+    void DoNextCombatManeuver();
+    void KilledMonster(uint32 entry, uint64 guid);
+    void ItemLocalization(std::string &itemName, const uint32 itemID) const;
+    void QuestLocalization(std::string &questTitle, const uint32 questID) const;
+
+    uint32 GetAttackerCount(){ return m_attackerInfo.size(); }
+	void SetIgnoreUpdateTime(float t){m_ignoreAIUpdatesUntilTime=getMSTime() + (t * 1000); };
+
+    Player *GetPlayerBot(){ return m_bot; }
+    void SetInFront(const Unit *obj);
+
+    bool CanBotsFly();  // take the flight path?
+    uint32 GetStartMapID() { return m_startMapID; };
+    uint32 GetStartZoneID() { return m_startZoneID; };
+    uint32 GetStartAreaID() { return m_startAreaID; };
+    uint32 GetStartPhase() { return m_startPhase; };
+    uint32 GetStartDifficulty() { return m_startDifficulty; };
+    uint32 GetStartInstanceID() { return m_startInstanceID; };
+    float GetStartX() { return m_startX; };
+    float GetStartY() { return m_startY; };
+    float GetStartZ() { return m_startZ; };
+    float GetStartO() { return m_startO; };
+
+    void SetStartMapID(uint32 mapID) { m_startMapID = mapID; };
+    void SetStartZoneID(uint32 zoneID) { m_startZoneID = zoneID; };
+    void SetStartAreaID(uint32 areaID) { m_startAreaID = areaID; };
+    void SetStartPhase(uint32 phase) { m_startPhase = phase; };
+    void SetStartDifficulty(uint32 difficulty) { m_startDifficulty = difficulty; };
+    void SetStartInstanceID(uint32 instanceID) { m_startInstanceID = instanceID; };
+    void SetStartX(float x) { m_startX = x; };
+    void SetStartY(float y) { m_startY = y; };
+    void SetStartZ(float z) { m_startZ = z; };
+    void SetStartO(float o) { m_startO = o; };
+
+    void AddLootGUID(uint64 guid);
+    void SetCombatOrder (CombatOrderType orders);
+
+private:
+
+    //****** Closed Actions ********************************
+    //These actions may only be called at special times.
+    //Trade methods are only applicable when the trade window is open
+    //and are only called from within HandleCommand.
+    // submits packet to trade an item (trade window must already be open)
+
+    // default slot is -1 which means trade slots 0 to 5. if slot is set
+    // to TRADE_SLOT_NONTRADED (which is slot 6) item will be shown in the
+    // 'Will not be traded' slot.
+    bool TradeItem(const Item& item, int8 slot=-1);
+
+    bool TradeCopper(uint32 copper);
+
+    //it is safe to keep these back reference pointers because m_bot
+    //owns the "this" object and m_master owns m_bot. The owner always cleans up.
+    Player *const m_master;
+    Player *const m_bot;
+    PlayerbotClassAI *m_classAI;
+
+    //ignores AI updates until time specified
+    //no need to waste CPU cycles during casting etc
+    uint32 m_ignoreAIUpdatesUntilTime;
+
+    CombatOrderType m_combatOrder;
+
+    ScenarioType m_ScenarioType;
+    typedef std::set<Unit *> AttackerSet;
+
+    time_t m_TimeDoneEating;
+    time_t m_TimeDoneDrinking;
+    time_t m_TimeRessurect;
+    uint32 m_CurrentlyCastingSpellId;
+    bool m_IsFollowingMaster;
+
+    //if master commands bot to do something, store here until updateAI
+    //can do it
+    uint32 m_spellIdCommand;
+    uint64 m_targetGuidCommand;
+
+    //finds who to attack next
+    Unit *getNextTarget(Unit *victim);
+
+    /* -- Loot routines by runsttren */
+    bool DoLoot();
+    void SetQuestNeedItems();
+
+    void TurnInQuests( WorldObject *questgiver );
+
+    typedef std::map<uint32, uint32> BotNeedItem;
+    typedef std::list<uint64> BotLootCreature;
+    typedef std::map<uint32, std::string> BotQuestsSeen;
+
+    //list of items needed to fullfill quests
+    BotNeedItem m_needItemList;
+
+    //list of items needed to fullfill quests
+    BotNeedItem m_needEmblemList;
+
+    //list of quests recently seen that we can accept
+    BotQuestsSeen m_questsSeen;
+
+    //list of creatures we recently attacked and want to loot
+    BotLootCreature m_lootCreature; //list of creatures
+    uint64 m_lootCurrent; //current remains of interest
+    uint64 m_lootobjtemp;
+    bool isLooting;
+    AttackerInfoList m_attackerInfo;
+
+    float m_followDistanceMin, m_followDistanceMax;
+    int m_playerBotsFly;
+
+    uint32 m_startMapID;
+    uint32 m_startZoneID;
+    uint32 m_startAreaID;
+    uint32 m_startPhase;
+    uint32 m_startDifficulty;
+    uint32 m_startInstanceID;
+    float m_startX;
+    float m_startY;
+    float m_startZ;
+    float m_startO;
+
+    uint32 m_FeastSpamTimer;
+};
+
+#endif
diff --git a/src/server/game/AI/Bots/PlayerbotClassAI.cpp b/src/server/game/AI/Bots/PlayerbotClassAI.cpp
new file mode 100644
index 0000000..6ac9f55
--- /dev/null
+++ b/src/server/game/AI/Bots/PlayerbotClassAI.cpp
@@ -0,0 +1,592 @@
+/*
+Name :    PlayerbotClassAI.cpp
+Notes:    Does not really work with peldor's own classbot AIs
+        Contains many improvements and hacks to overcome some difficulites
+Known
+Problems:    - Contains hardcoded values,  for an example check group heal, individual heal decision
+            - ai->getSpellIdExact func, *although works more accurately* is probably slower and hackish
+            - FindMainTankRaid func, includes a db query making it a resource hog
+            - canCast func, does not check for every possible problem, can cause AI stuck.. Should be inside PlayerbotAI class
+            - castSpell func is redundant and should be placed in PlayerbotAI class, sets private variable m_ai->m_CurrentlyCastingSpellId which is made public as a hack..
+
+Authors : SwaLLoweD
+Version : 0.40
+*/
+
+#include "PlayerbotClassAI.h"
+#include "Common.h"
+#include "Spell.h"
+#include "Group.h"
+
+PlayerbotClassAI::PlayerbotClassAI(Player *const master, Player *const bot, PlayerbotAI *const ai): m_master(master), m_bot(bot), m_ai(ai), rezSpamTimer(0)
+{
+    threatThreshold = 75;            // Threat % threshold for dps to lower tps
+    offensiveSpellThreshold = 70;    // Mana % threshold for healers to use offensive spells
+
+    // first aid
+    RECENTLY_BANDAGED = 11196; // first aid check
+
+    // RACIALS
+    R_ARCANE_TORRENT = ai->getSpellIdExact("Arcane Torrent");
+    R_BERSERKING = ai->getSpellIdExact("Berserking");
+    R_BLOOD_FURY = ai->getSpellIdExact("Blood Fury");
+    R_CANNIBALIZE = ai->getSpellIdExact("Cannibalize");
+    R_ESCAPE_ARTIST = ai->getSpellIdExact("Escape Artist");
+    R_EVERY_MAN_FOR_HIMSELF = ai->getSpellIdExact("Every Man for Himself");
+    R_GIFT_OF_NAARU = ai->getSpellIdExact("Gift of the Naaru");
+    R_SHADOWMELD = ai->getSpellIdExact("Shadowmeld");
+    R_STONEFORM = ai->getSpellIdExact("Stoneform");
+    R_WAR_STOMP = ai->getSpellIdExact("War Stomp");
+    R_WILL_OF_FORSAKEN = ai->getSpellIdExact("Will of the Forsaken");
+
+    mainTank = NULL;
+    m_pulling = false;
+}
+PlayerbotClassAI::~PlayerbotClassAI(){}
+
+void PlayerbotClassAI::DoNextCombatManeuver(Unit *){}
+
+void PlayerbotClassAI::DoNonCombatActions(){}
+
+void PlayerbotClassAI::LoadSpells(){}
+
+void PlayerbotClassAI::Pull(){}
+
+bool PlayerbotClassAI::BuffPlayer(Unit *target){ return false; }
+
+bool PlayerbotClassAI::FindMount(){ return true; }
+
+bool PlayerbotClassAI::Unmount(){ return true; }
+
+bool PlayerbotClassAI::HealTarget (Unit *target, uint8 hp){ return false; }
+
+bool PlayerbotClassAI::HealGroup (Unit *target, uint8 hp, uint8 &countNeedHeal){ return false; }
+
+bool PlayerbotClassAI::CureTarget (Unit *target){ return false; }
+
+bool PlayerbotClassAI::RezTarget (Unit *target){ return false; }
+
+bool PlayerbotClassAI::IsMounted(){ return m_bot->IsMounted(); }
+
+bool PlayerbotClassAI::CastSpell(uint32 spellId, Unit *target, bool checkFirst, bool castExistingAura, bool skipFriendlyCheck, bool skipEquipStanceCheck, bool triggered)
+{return m_ai->CastSpell(spellId, target, checkFirst, castExistingAura, skipFriendlyCheck, skipEquipStanceCheck, triggered); }
+bool PlayerbotClassAI::CastSpell(const SpellEntry * pSpellInfo, Unit *target, bool checkFirst, bool castExistingAura, bool skipFriendlyCheck, bool skipEquipStanceCheck, bool triggered)
+{return m_ai->CastSpell(pSpellInfo, target, checkFirst, castExistingAura, skipFriendlyCheck, skipEquipStanceCheck, triggered);}
+
+bool PlayerbotClassAI::CanCast(uint32 spellId, Unit *target, bool castExistingAura, bool skipFriendlyCheck, bool skipEquipStanceCheck)
+{return m_ai->CanCast(spellId, target, castExistingAura, skipFriendlyCheck, skipEquipStanceCheck);}
+
+bool PlayerbotClassAI::CanCast(const SpellEntry * pSpellInfo, Unit *target, bool castExistingAura, bool skipFriendlyCheck, bool skipEquipStanceCheck)
+{return m_ai->CanCast(pSpellInfo, target, castExistingAura, skipFriendlyCheck, skipEquipStanceCheck);}
+
+bool PlayerbotClassAI::listAuras(Unit *u)
+{
+    int loc = 0;
+    Unit *target = u;
+    typedef std::pair<uint32, uint8> spellEffectPair;
+    typedef std::multimap< spellEffectPair, Aura*> AuraMap;
+    Unit::AuraMap &vAuras = target->GetOwnedAuras();
+    for(Unit::AuraMap::const_iterator itr = vAuras.begin(); itr!=vAuras.end(); ++itr)
+    {
+        //SpellEntry const *spellInfo = (*itr).second->GetSpellProto();
+        const SpellEntry *spellInfo = itr->second->GetSpellProto();
+        const std::string name = spellInfo->SpellName[loc];
+        sLog->outDebug("aura = %u %s", spellInfo->Id, name.c_str());
+    }
+    return true;
+};//end listAuras
+
+bool PlayerbotClassAI::HasAuraName (Unit *unit, uint32 spellId, uint64 casterGuid)
+{
+    const SpellEntry *const pSpellInfo = GetSpellStore()->LookupEntry (spellId);
+    if(!pSpellInfo) return false;
+    int loc = m_bot->GetSession()->GetSessionDbcLocale();
+    const std::string  name = pSpellInfo->SpellName[loc];
+    if(name.length() == 0) return false;
+    return HasAuraName(unit, name, casterGuid);
+}
+
+bool PlayerbotClassAI::HasAuraName (Unit *target, std::string spell, uint64 casterGuid)
+{
+    int loc = m_bot->GetSession()->GetSessionDbcLocale();
+    typedef std::pair<uint32, uint8>spellEffectPair;
+    typedef std::multimap<spellEffectPair, Aura*>AuraMap;
+
+    Unit::AuraMap &vAuras = target->GetOwnedAuras();
+    for(Unit::AuraMap::const_iterator itr = vAuras.begin(); itr!=vAuras.end(); ++itr)
+    {
+        //SpellEntry const *spellInfo = (*itr).second->GetSpellProto();
+        const SpellEntry *spellInfo = itr->second->GetSpellProto();
+        const std::string name = spellInfo->SpellName[loc];
+        if(!spell.compare(name))
+        //if(!strcmp(name.c_str(),spell.c_str()))
+        {
+            if(casterGuid == 0) //don't care who casted it
+                return true;
+            else if(casterGuid == itr->second->GetCasterGUID()) //only if correct caster casted it
+                return true;
+        }
+    }
+    return false;
+};
+
+bool PlayerbotClassAI::castDispel (uint32 dispelSpell, Unit *dTarget, bool checkFirst, bool castExistingAura, bool skipFriendlyCheck, bool skipEquipStanceCheck)
+{
+    if (dispelSpell == 0 || !dTarget ) return false;
+    //if (!canCast(dispelSpell, dTarget, true)) return false; //Needless cpu cycles wasted, usually a playerbot can cast a dispell
+    const SpellEntry *dSpell = GetSpellStore()->LookupEntry(dispelSpell);
+    if (!dSpell) return false;
+
+    for (uint8 i = 0 ; i < MAX_SPELL_EFFECTS ; ++i)
+    {
+        if (dSpell->Effect[i] != (uint32)SPELL_EFFECT_DISPEL) continue;
+        uint32 dispel_type = dSpell->EffectMiscValue[i];
+        uint32 dispelMask  = GetDispellMask(DispelType(dispel_type));
+        Unit::AuraMap const& auras = dTarget->GetOwnedAuras();
+        for (Unit::AuraMap::const_iterator itr = auras.begin(); itr != auras.end(); itr++)
+        {
+            Aura * aura = itr->second;
+            AuraApplication * aurApp = aura->GetApplicationOfTarget(dTarget->GetGUID());
+            if (!aurApp)
+                continue;
+
+            if ((1<<aura->GetSpellProto()->Dispel) & dispelMask)
+            {
+                if(aura->GetSpellProto()->Dispel == DISPEL_MAGIC)
+                {
+                    bool positive = aurApp->IsPositive() ? (!(aura->GetSpellProto()->AttributesEx & SPELL_ATTR1_NEGATIVE)) : false;
+
+                    // do not remove positive auras if friendly target
+                    //               negative auras if non-friendly target
+                    if(positive == dTarget->IsFriendlyTo(GetPlayerBot()))
+                        continue;
+                }
+                // If there is a successfull match return, else continue searching.
+                if (CastSpell(dSpell, dTarget, checkFirst, castExistingAura, skipFriendlyCheck, skipEquipStanceCheck)) { return true; }
+            }
+        }
+    }
+    return false;
+}
+
+bool PlayerbotClassAI::castSelfCCBreakers (uint32 castList[])
+{
+    uint32 dispelSpell = 0;
+    Player *dTarget = GetPlayerBot();
+
+
+            /*dispelSpell = (uint32) R_ESCAPE_ARTIST; // this is script effect,
+            Unit::AuraMap const& auras = dTarget->GetOwnedAuras();
+            for (Unit::AuraMap::const_iterator itr = auras.begin(); itr != auras.end(); itr++)
+            {
+                Aura * aura = itr->second;
+                AuraApplication * aurApp = aura->GetApplicationOfTarget(dTarget->GetGUID());
+                if (!aurApp)
+                    continue;
+
+                if ( ( aura->GetSpellProto()->Mechanic == MECHANIC_SNARE ) || ( aura->GetSpellProto()->Mechanic == MECHANIC_ROOT ) )
+                {
+                    if(aura->GetSpellProto()->Dispel == DISPEL_MAGIC)
+                    {
+                        bool positive = aurApp->IsPositive() ? (!(aura->GetSpellProto()->AttributesEx & SPELL_ATTR_EX_NEGATIVE)) : false;
+
+                        // do not remove positive auras if friendly target
+                        //               negative auras if non-friendly target
+                        if(positive == dTarget->IsFriendlyTo(caster))
+                            continue;
+                    }
+                    return castSpell(dispelSpell, dTarget);
+                }
+            }
+            return false; */
+
+        // racial abilities
+    /* if( GetPlayerBot()->getRace() == RACE_BLOODELF && !pTarget->HasAura( ARCANE_TORRENT,0 ) && castSpell( ARCANE_TORRENT,pTarget ) ) {
+         //GetPlayerBot()->Say("Arcane Torrent!", LANG_UNIVERSAL);
+    } else if( GetPlayerBot()->getRace() == RACE_HUMAN && (GetPlayerBot()->HasUnitState( UNIT_STAT_STUNNED ) || GetPlayerBot()->HasAuraType( SPELL_AURA_MOD_FEAR ) || GetPlayerBot()->HasAuraType( SPELL_AURA_MOD_DECREASE_SPEED ) || GetPlayerBot()->HasAuraType( SPELL_AURA_MOD_CHARM )) && castSpell( EVERY_MAN_FOR_HIMSELF, GetPlayerBot() ) ) {
+        //GetPlayerBot()->Say("EVERY MAN FOR HIMSELF!", LANG_UNIVERSAL);
+    } else if( GetPlayerBot()->getRace() == RACE_UNDEAD_PLAYER && (GetPlayerBot()->HasAuraType( SPELL_AURA_MOD_FEAR ) || GetPlayerBot()->HasAuraType( SPELL_AURA_MOD_CHARM )) && castSpell( WILL_OF_THE_FORSAKEN, GetPlayerBot() ) ) {
+       // GetPlayerBot()->Say("WILL OF THE FORSAKEN!", LANG_UNIVERSAL);
+    } else if( GetPlayerBot()->getRace() == RACE_DWARF && GetPlayerBot()->HasAuraState( AURA_STATE_DEADLY_POISON ) && castSpell( STONEFORM, GetPlayerBot() ) ) {
+        //GetPlayerBot()->Say("STONEFORM!", LANG_UNIVERSAL);
+    } else if( GetPlayerBot()->getRace() == RACE_GNOME && (GetPlayerBot()->HasUnitState( UNIT_STAT_STUNNED ) || GetPlayerBot()->HasAuraType( SPELL_AURA_MOD_DECREASE_SPEED )) && castSpell( ESCAPE_ARTIST, GetPlayerBot() ) ) {
+       // GetPlayerBot()->Say("ESCAPE ARTIST!", LANG_UNIVERSAL);
+    } */
+
+    for (uint8 j = 0; j <  sizeof (castList); j++)
+    {
+        dispelSpell = castList[j];
+        if (dispelSpell == 0 || !dTarget->HasSpell(dispelSpell) || !CanCast(dispelSpell, dTarget, true)) continue;
+        SpellEntry const *dSpell = GetSpellStore()->LookupEntry(dispelSpell);
+        if (!dSpell) continue;
+
+        for (uint8 i = 0 ; i < MAX_SPELL_EFFECTS ; ++i)
+        {
+            if (dSpell->Effect[i] != (uint32)SPELL_EFFECT_DISPEL && dSpell->Effect[i] != (uint32)SPELL_EFFECT_APPLY_AURA) continue;
+            if (dSpell->Effect[i] == (uint32)SPELL_EFFECT_APPLY_AURA && (
+                (dSpell->EffectApplyAuraName[i] != (uint32) SPELL_AURA_MECHANIC_IMMUNITY) ||
+                (dSpell->EffectApplyAuraName[i] != (uint32) SPELL_AURA_DISPEL_IMMUNITY)
+                )) continue;
+
+            Unit::AuraMap const& auras = dTarget->GetOwnedAuras();
+            for (Unit::AuraMap::const_iterator itr = auras.begin(); itr != auras.end(); itr++)
+            {
+                Aura * aura = itr->second;
+                AuraApplication * aurApp = aura->GetApplicationOfTarget(dTarget->GetGUID());
+                if (!aurApp) continue;
+
+                if (aura->GetSpellProto() && (
+                    (dSpell->Effect[i] == (uint32)SPELL_EFFECT_DISPEL  && ((1<<aura->GetSpellProto()->Dispel) & GetDispellMask(DispelType(dSpell->EffectMiscValue[i]))) )
+                    || (dSpell->EffectApplyAuraName[i] == (uint32) SPELL_AURA_MECHANIC_IMMUNITY && ( GetAllSpellMechanicMask(aura->GetSpellProto()) & ( 1 << dSpell->EffectMiscValue[i]) ) )
+                    || (dSpell->EffectApplyAuraName[i] == (uint32) SPELL_AURA_DISPEL_IMMUNITY && ( (1<<aura->GetSpellProto()->Dispel) & GetDispellMask(DispelType(dSpell->EffectMiscValue[i])) ) )
+                    ) )
+                {
+                    if(aura->GetSpellProto()->Dispel == DISPEL_MAGIC)
+                    {
+                        bool positive = aurApp->IsPositive() ? (!(aura->GetSpellProto()->AttributesEx & SPELL_ATTR1_NEGATIVE)) : false;
+                        if(positive)continue;
+                    }
+                    return CastSpell(dispelSpell, dTarget, false);
+                }
+            }
+        }
+    }
+    return false;
+}
+
+bool PlayerbotClassAI::DoSupportRaid(Player *gPlayer, float radius, bool dResurrect, bool dGroupHeal, bool dHeal, bool dCure, bool dBuff)
+{
+    bool needHeal = false;
+    if (dGroupHeal || dHeal)
+    {
+        uint8 cntNeedHeal = 0;
+        uint8 raidHPPercent = GetHealthPercentRaid(gPlayer, cntNeedHeal);
+        if (dGroupHeal && raidHPPercent <=90 && cntNeedHeal > 1)
+        {
+            if (HealGroup(gPlayer, raidHPPercent, cntNeedHeal)) return true;
+        }
+        if (raidHPPercent < 60 ) needHeal = true;
+    }
+    //std::list<Unit*> unitList;
+    //gPlayer->GetRaidMember(unitList,30);
+    Group *pGroup = gPlayer->GetGroup();
+    if (!pGroup) return false;
+    for (GroupReference *itr = pGroup->GetFirstMember(); itr != NULL; itr = itr->next())
+    {
+        Unit* tPlayer = itr->getSource();
+        if(!tPlayer || gPlayer->IsHostileTo(tPlayer)) continue;
+        if(GetPlayerBot()->GetAreaId() != tPlayer->GetAreaId()) continue;
+        if(!m_bot->IsWithinDistInMap(tPlayer, radius)) { continue; }
+        if(tPlayer->isDead()) // May be we can rez
+        {
+            if(!dResurrect) continue;
+            if(needHeal) continue; //First heal others needing heal
+            if(tPlayer->GetGUID() == GetPlayerBot()->GetGUID()) continue;
+            if(tPlayer->IsNonMeleeSpellCasted(true)) continue; //Already rez
+            if(RezTarget(tPlayer)) { return true; }
+            else continue;
+        }
+        if (dHeal)
+        {
+            uint8 tarHPPercent = tPlayer->GetHealth()*100 / tPlayer->GetMaxHealth();
+            if (tarHPPercent < 100 && HealTarget(tPlayer, tarHPPercent)) return true;
+        }
+        if (needHeal && dHeal) continue; //First heal others needing heal
+        if (dCure && CureTarget(tPlayer)) return true;
+        if (dBuff && BuffPlayer(tPlayer)) return true;
+    }
+    return false;
+}
+
+bool PlayerbotClassAI::TakePosition(Unit *followTarget, BotRole bRole, float bDist, float bMinDist, float bMaxDist, float bAngle, Unit *faceTarget)
+{
+    bool doFollow = true;
+    bool omitAngle = false;
+    bool angleIsAutoSet = false;
+    if (!bAngle) angleIsAutoSet = true;
+    if (bAngle < 0) bAngle += 2 * M_PI;
+    //if (bAngle > 2 * M_PI) bAngle -= 2 * M_PI; //Do not send values higher than 2 PI, lower than -2 PI
+    bool rval = false;
+    if (followTarget == NULL) { followTarget = GetMaster(); if (followTarget == NULL) { return false; } }
+    if (faceTarget == NULL) { faceTarget = followTarget; }
+    if (bRole == BOT_ROLE_NONE) { bRole = ( (m_role == BOT_ROLE_NONE) ? BOT_ROLE_DPS_MELEE : m_role);  }
+    //Default values
+    Unit *pVictim = followTarget->getVictim();
+    if (pVictim && pVictim->GetGUID() == m_bot->GetGUID()) //if target is attacking me
+    {
+        if (bRole == BOT_ROLE_TANK || bRole == BOT_ROLE_OFFTANK || bRole == BOT_ROLE_DPS_MELEE)
+        {
+            //Move to target
+            if (!bDist || bDist > 0.7f) bDist = 0.7f;
+            if (bMinDist < 0 || bMinDist > 1) bMinDist = 0;
+            if (bMaxDist <= 0 || bMaxDist > MELEE_RANGE) bMaxDist = MELEE_RANGE;
+            bAngle = 0;
+        }
+        else {doFollow = false;} //Do not move, creature will come
+    }
+    else
+    {
+        // calculating distance to follow
+        switch (bRole)
+        {
+            case BOT_ROLE_TANK:
+            case BOT_ROLE_OFFTANK:
+                if (!bDist) { bDist = 0.7f; bMinDist = 0; bMaxDist = MELEE_RANGE; bAngle = 0;}
+                break;
+            case BOT_ROLE_HEALER:
+            case BOT_ROLE_SUPPORT:
+                if (!bDist) { bDist = urand(12, 14); bMinDist = 10; bMaxDist = 18; bAngle = ((urand(0,1) * 90 ) + urand(110,160)) * M_PI / 180; }
+                break;
+            case BOT_ROLE_DPS_RANGED:
+                if (!bDist) { bDist = urand(18, 24); bMinDist = 10;  bMaxDist = 26; bAngle = ((urand(0,1) * 90 ) + urand(110,160)) * M_PI / 180; }
+                break;
+            default:
+                if (!bDist) { bDist = 0.7f; bMinDist = 0.1f; bMaxDist = MELEE_RANGE; bAngle = ((urand(0,1) * 90 ) + urand(110,160)) * M_PI / 180; }
+                break;
+        }
+    }
+    //Do not try to go behind if ranged and creature is not boss like
+    if (bDist > MELEE_RANGE && followTarget->GetTypeId() != TYPEID_PLAYER)
+    {
+        const CreatureInfo *cInfo = ((Creature*) followTarget)->GetCreatureInfo();
+        if (!cInfo || cInfo->rank != 3) { omitAngle = true; }
+    }
+
+    //Move
+    if (doFollow)
+    {
+        float curDist = m_bot->GetDistance(followTarget);
+        if (m_pulling ||
+            (!m_bot->isMoving() &&
+            ((curDist > bMaxDist || curDist < bMinDist)  //Outside range boundries
+            || (!omitAngle && ((!followTarget->HasInArc(M_PI,m_bot)) ^ (bAngle > 0.5f * M_PI && bAngle < 1.5f * M_PI)))) )//is at right position front/behind?
+            )
+        {
+            //m_bot->GetMotionMaster()->Clear();
+            //sLog->outError("Bot[%u] is moving, curDist[%f], bDist[%f], bminDist[%f], bMaxDist[%f], bAngle[%f], InFront[%u]", m_bot->GetGUIDLow(), curDist, bDist,bMinDist, bMaxDist, bAngle, followTarget->HasInArc(M_PI,m_bot));
+            if (angleIsAutoSet && omitAngle) { m_bot->GetMotionMaster()->MoveChase(followTarget, bDist); }
+            else { m_bot->GetMotionMaster()->MoveChase(followTarget, bDist, bAngle); }
+            rval |= true;
+        }
+    }
+    //Face your faceTarget
+    if (!m_bot->HasInArc(M_PI/16, faceTarget) && !m_bot->isMoving() ) { m_bot->SetFacingToObject(faceTarget); rval |= true; }
+    return rval;
+}
+
+uint8 PlayerbotClassAI::GetThreatPercent(Unit *pTarget, Unit *pFrom)
+{
+    uint8 tPercent = 0;
+    Unit *pVictim = pTarget->getVictim();
+    if (!pVictim) return 100; //Not Attacking anyone yet, somehow..
+    if (!pFrom) { pFrom = m_bot; }
+    if (pVictim->GetGUID() == pFrom->GetGUID()) return 100; //I'm already being attacked, too late for alert, kill it..
+    //if (m_tank->GetGUID() == m_bot->GetGUID()) {} //If I am not tank and there is a target
+
+    ThreatManager &pthreatManager = pTarget->getThreatManager();
+    float maxThreat = pthreatManager.getThreat(pTarget->getVictim()) ;
+    if (maxThreat <= 0) { return 100; } //0 threat
+    float curThreat = pthreatManager.getThreat(pFrom);
+    return (curThreat * 100 / maxThreat);
+}
+//Gets if the unit is under attack by # of attackers
+bool PlayerbotClassAI::isUnderAttack(Unit *pAttacked,const uint8 &minNumberOfAttackers)
+{
+    if (!pAttacked) { pAttacked = m_bot; if (!pAttacked) { return false; } }
+    Unit::AttackerSet fAttackerSet = pAttacked->getAttackers();
+    if (fAttackerSet.size() >= minNumberOfAttackers) { return true; }
+    return false;
+}
+
+//Gets the first found attacker of Unit
+Unit *PlayerbotClassAI::GetAttackerOf(Unit *pAttacked)
+{
+    if (!pAttacked) { pAttacked = m_bot; if (!pAttacked) { return NULL; } }
+    Unit::AttackerSet fAttackerSet = pAttacked->getAttackers();
+    if (fAttackerSet.size() <= 0) { return NULL; }
+    return (*fAttackerSet.begin());
+}
+//Gets the first found attacker of Unit if not nearestToAttacked > finds the one nearest to bot
+Unit *PlayerbotClassAI::GetNearestAttackerOf(Unit *pAttacked, bool nearestToAttacked)
+{
+    if (!pAttacked) { pAttacked = m_bot; if (!pAttacked) return NULL;}
+
+    Unit::AttackerSet fAttackerSet = pAttacked->getAttackers();
+    if (fAttackerSet.size() <= 0) { return NULL; }
+
+    Unit *nearestTo = m_bot;
+    if (nearestToAttacked) { nearestTo = pAttacked; }
+
+    Unit *curAtt = NULL;
+    float minDist = 30;
+
+
+    for (Unit::AttackerSet::const_iterator itr = fAttackerSet.begin(); itr != fAttackerSet.end(); ++itr)
+    {
+        Unit *tAtt = (*itr);
+        if (!tAtt) break; // Something is wrong.. How can a non existing mob attack?
+        if (tAtt->isDead()) break;
+        if (m_bot->GetDistance(tAtt) >= minDist) continue; //Get the nearest one
+        curAtt = tAtt;
+        minDist = tAtt->GetDistance(nearestTo);
+    }
+    return curAtt;
+
+}
+
+uint8 PlayerbotClassAI::GetHealthPercentRaid(Player *gPlayer, uint8 &countNeedHealing)
+{
+    uint8 validMemberCount=0;
+    uint16 totalHPPercent=0;
+    std::list<Unit*> unitList;
+    gPlayer->GetRaidMember(unitList,30);
+    if(!unitList.empty()){
+      for (std::list<Unit*>::iterator itr = unitList.begin() ; itr!=unitList.end();++itr) {
+        //Player *tPlayer = GetPlayerBot()->GetObjPlayer((*itr)->GetGUID());
+        Unit *tPlayer = sObjectMgr->GetPlayer((*itr)->GetGUID());
+        if(tPlayer == NULL) continue;
+        if(tPlayer->isDead()) continue;
+        if(GetPlayerBot()->GetAreaId() != tPlayer->GetAreaId()) continue;
+        //if(tPlayer->GetGUID() == GetPlayerBot()->GetGUID()) continue;
+        if(GetPlayerBot()->GetDistance(tPlayer) > 30) continue;
+        uint8 fndHPPercent =  tPlayer->GetHealth()*100 / tPlayer->GetMaxHealth();
+        totalHPPercent+=fndHPPercent;
+        validMemberCount++;
+        if (fndHPPercent < 100) countNeedHealing++;
+
+        //const std::string myname = GetPlayerBot()->GetName();
+        //const std::string hisname = tPlayer->GetName();
+        //sLog->outDebug("me = %s, checked= %s %u [%u / %u]", myname.c_str(), hisname.c_str(), fndHPPercent, tPlayer->GetHealth(), tPlayer->GetMaxHealth());
+
+      }
+    }
+    if (validMemberCount == 0) return 100;
+    return totalHPPercent / validMemberCount;
+}
+
+Unit *PlayerbotClassAI::DoSelectLowestHpFriendly(float range, uint32 MinHPDiff)
+{
+    Unit *pUnit = NULL;
+    Trinity::MostHPMissingInRange u_check(GetPlayerBot(), range, MinHPDiff);
+    Trinity::UnitLastSearcher<Trinity::MostHPMissingInRange> searcher(GetPlayerBot(), pUnit, u_check);
+
+    GetPlayerBot()->VisitNearbyObject(range, searcher);
+
+    return pUnit;
+}
+
+void PlayerbotClassAI::SetMainTank(Unit *tank)
+{
+    mainTank = tank;
+}
+
+// is Resource heavy, do not spam or use heavily in loop
+Unit *PlayerbotClassAI::FindMainTankInRaid(Player *gPlayer)
+{
+    // check if original main tank is still alive. No point regetting main
+    // tank b/c chances are slim that it will not get reset in the middle of a fight.
+    // But if main tank dies, try to find next best canidate
+    if (mainTank!=NULL && mainTank->isAlive()) {
+        return mainTank;
+    }
+
+    if (!gPlayer) return NULL;
+    Group *pGroup = gPlayer->GetGroup();
+    if (!pGroup) return NULL;
+    uint64 pLeaderGuid = pGroup->GetLeaderGUID();
+
+    Unit *pPlayer = NULL;
+
+    // Check if set in raid
+    if (pGroup->isRaidGroup())
+    {
+        QueryResult result = CharacterDatabase.PQuery("SELECT memberGuid FROM group_member WHERE memberFlags='%u' AND guid = '%u'",MEMBER_FLAG_MAINTANK, pGroup->GetGUID());
+        if(result)
+        {
+            uint64 pGuid = MAKE_NEW_GUID(result->Fetch()->GetInt32(),0,HIGHGUID_PLAYER);
+            pPlayer = sObjectMgr->GetPlayer(pGuid);
+            if (pPlayer && pGroup->IsMember(pGuid) && pPlayer->isAlive()){
+                mainTank = pPlayer;
+                return pPlayer;
+            }
+        }
+    }
+
+
+    // if could not find tank try assuming
+    // Assume the one with highest health is the main tank
+    uint32 maxhpfound=0;
+    std::list<Unit*> unitList;
+    gPlayer->GetRaidMember(unitList,30);
+    if (!unitList.empty()){
+      for (std::list<Unit*>::iterator itr = unitList.begin() ; itr!=unitList.end();++itr) {
+        //Player *tPlayer = GetPlayerBot()->GetObjPlayer((*itr)->GetGUID());
+        Unit *tPlayer = sObjectMgr->GetPlayer((*itr)->GetGUID());
+        if (tPlayer == NULL) continue;
+        if (tPlayer->isDead()) continue;
+        if (GetPlayerBot()->GetAreaId() != tPlayer->GetAreaId()) continue;
+        //if(tPlayer->GetGUID() == GetPlayerBot()->GetGUID()) continue;
+        if (GetPlayerBot()->GetDistance(tPlayer) > 50) continue;
+        if (tPlayer->GetMaxHealth() > maxhpfound) { maxhpfound = tPlayer->GetMaxHealth(); pPlayer=tPlayer; }
+        // Also check pets
+        if ( (tPlayer->getClass() == (uint8) CLASS_HUNTER || tPlayer->getClass() == (uint8) CLASS_WARLOCK) && IS_PET_GUID(tPlayer->GetPetGUID()) )
+        {
+            Pet* tpet = ObjectAccessor::GetPet(*tPlayer, tPlayer->GetPetGUID());
+            if (!tpet || !tpet->IsInWorld() || !tpet->isDead()) continue;
+            if (tpet->GetArmor() > tPlayer->GetArmor()) //Probably a tanking capable pet..
+            {
+                if (tpet->GetMaxHealth() > maxhpfound) { maxhpfound = tpet->GetMaxHealth(); pPlayer=tpet; }
+                else if (tPlayer->GetGUID() == pPlayer->GetGUID()) {pPlayer = tpet;} //set pet as tank instead of owner
+            }
+        }
+      }
+    }
+
+    mainTank = pPlayer;
+    return pPlayer;
+}
+
+Unit *PlayerbotClassAI::FindMainAssistInRaid(Player *gPlayer)
+{
+    if (!gPlayer) return NULL;
+    Group *pGroup = gPlayer->GetGroup();
+    if (!pGroup) return NULL;
+    uint64 pLeaderGuid = pGroup->GetLeaderGUID();
+
+
+    Unit *pPlayer = NULL;
+
+    // Check if set in raid
+    if (pGroup->isRaidGroup())
+    {
+        QueryResult result = CharacterDatabase.PQuery("SELECT memberGuid FROM group_member WHERE memberFlags='%u' AND guid = '%u'",MEMBER_FLAG_MAINASSIST, pGroup->GetGUID());
+          if(result)
+        {
+            uint64 pGuid = MAKE_NEW_GUID(result->Fetch()->GetInt32(),0,HIGHGUID_PLAYER);
+            pPlayer = sObjectMgr->GetPlayer(pGuid);
+            if (pPlayer && pGroup->IsMember(pGuid) && pPlayer->isAlive()){
+                return pPlayer;
+            }
+        }
+    }
+
+    // default to main tank
+    return FindMainTankInRaid(gPlayer);
+}
+
+Player * PlayerbotClassAI::FindMage(Player *gPlayer)
+{
+    Group::MemberSlotList const &groupSlot = gPlayer->GetGroup()->GetMemberSlots();
+    for(Group::member_citerator itr = groupSlot.begin(); itr != groupSlot.end(); itr++)
+    {
+        Player *tPlayer = sObjectMgr->GetPlayer(itr->guid);
+
+        if(tPlayer == NULL) continue;
+        if(tPlayer->GetGUID() == GetPlayerBot()->GetGUID()) continue;
+        if(GetPlayerBot()->GetAreaId() != gPlayer->GetAreaId()) continue;
+        if(GetPlayerBot()->GetDistance(tPlayer) > 30) continue;
+
+        if (tPlayer->getClass() == CLASS_MAGE) return tPlayer;
+    }
+    return NULL;
+}
diff --git a/src/server/game/AI/Bots/PlayerbotClassAI.h b/src/server/game/AI/Bots/PlayerbotClassAI.h
new file mode 100644
index 0000000..5537b1d
--- /dev/null
+++ b/src/server/game/AI/Bots/PlayerbotClassAI.h
@@ -0,0 +1,162 @@
+#ifndef _PLAYERBOTCLASSAI_H
+#define _PLAYERBOTCLASSAI_H
+
+#include "Common.h"
+#include "World.h"
+#include "SpellMgr.h"
+#include "Player.h"
+#include "ObjectMgr.h"
+#include "WorldPacket.h"
+#include "Unit.h"
+#include "SharedDefines.h"
+#include "PlayerbotAI.h"
+#include "SpellAuras.h"
+#include "Cell.h"
+#include "CellImpl.h"
+#include "GridNotifiers.h"
+#include "GridNotifiersImpl.h"
+
+
+class Player;
+class PlayerbotAI;
+class Aura;
+
+        enum BotRole
+        {
+            BOT_ROLE_NONE,
+            BOT_ROLE_TANK,
+            BOT_ROLE_OFFTANK,
+            BOT_ROLE_DPS_RANGED,
+            BOT_ROLE_DPS_MELEE,
+            BOT_ROLE_SUPPORT,
+            BOT_ROLE_HEALER
+        };
+
+class PlayerbotClassAI
+{
+    public:
+        PlayerbotClassAI(Player *const master, Player *const bot, PlayerbotAI *const ai);
+        virtual ~PlayerbotClassAI();
+
+        //all combat actions go here
+        virtual void DoNextCombatManeuver(Unit *);
+
+        //all non combat actions go here, ex buffs, heals, rezzes
+        virtual void DoNonCombatActions();
+
+        //buff a specific player, usually a real PC who is not in group
+        virtual bool BuffPlayer(Unit *target);
+
+        //Heals the target based off its HP
+        virtual bool HealTarget (Unit *target, uint8 hp);
+
+        //Heals the group based off its HP
+        virtual bool HealGroup (Unit *target, uint8 hp, uint8 &countNeedHeal);
+
+        //Cure the target
+        virtual bool CureTarget (Unit *target);
+
+        //Resurrect the target (OBSOLETE - Check individual ClassAIs instead)
+        virtual bool RezTarget(Unit *target);
+
+        //find any specific mount spells, ie druids = cat, shaman = ghost wolf etc (OBSOLETE)
+        virtual bool FindMount();
+
+        virtual bool Unmount();
+
+        virtual bool IsMounted();
+
+        virtual void LoadSpells();
+
+        virtual void Pull();
+
+        //Utilities
+        Player *GetMaster (){ return m_master; }
+        Player *GetPlayerBot(){ return m_bot; }
+        PlayerbotAI *GetAI (){ return m_ai; }
+
+        bool isPulling() { return m_pulling; }
+        bool TakePosition(Unit *followTarget, BotRole bRole=BOT_ROLE_NONE, float bDist=0, float bMinDist=0, float bMaxDist=0, float bAngle=0, Unit *faceTarget=NULL);
+        //Gets the threat done by bot / threat max (percent) to the target.
+        uint8 GetThreatPercent(Unit *pTarget, Unit *pFrom = NULL);
+        //Gets if the unit is under attack by # of attackers
+        bool isUnderAttack(Unit *pAttacked=NULL,const uint8 &minNumberOfAttackers=1);
+        //Gets the first found attacker of Unit
+        Unit *GetAttackerOf(Unit *pAttacked=NULL);
+        //Gets the nearest attacker of Unit if not nearestToAttacked > finds the one nearest to bot
+        Unit *GetNearestAttackerOf(Unit *pAttacked=NULL, bool nearestToAttacked=false);
+        //Calculates Average Raid Health condition as Percentage, ref value is the Count of units need healing..
+        uint8 GetHealthPercentRaid(Player *gPlayer, uint8 &countNeedHealing);
+
+        // Called when the main tank is set from raid ui
+        void SetMainTank (Unit *tank);
+
+        //Finds the possible MainTank in Raid including Hunter/Warlock pets.. Makes the assumption based on - max maxHealth..
+        Unit *FindMainTankInRaid(Player *gPlayer);
+
+        //Finds the possible MainAssist in Raid.  Defaults to Main Tank if it cannot find one.
+        Unit *FindMainAssistInRaid(Player *gPlayer);
+
+        Player *FindMage(Player *gPlayer);
+        //Finds the lowest hp creature around that is friendly with the caster.
+        Unit *DoSelectLowestHpFriendly(float range, uint32 MinHPDiff);
+
+
+        protected:
+        bool CastSpell(uint32 spellId, Unit *target=NULL, bool checkFirst=true, bool castExistingAura=false, bool skipFriendlyCheck=false, bool skipEquipStanceCheck=false, bool triggered=false);
+        bool CastSpell(const SpellEntry * pSpellInfo, Unit *target=NULL, bool checkFirst=true, bool castExistingAura=false, bool skipFriendlyCheck=false, bool skipEquipStanceCheck=false, bool triggered=false);
+        bool CanCast(uint32 spellId, Unit *target=NULL,bool castExistingAura=false, bool skipFriendlyCheck=false, bool skipEquipStanceCheck=false);
+        bool CanCast(const SpellEntry * pSpellInfo, Unit *target=NULL,bool castExistingAura=false, bool skipFriendlyCheck=false, bool skipEquipStanceCheck=false);
+
+        //Debug method to list the auras currently active.
+        //Use to find what spells were casted
+        bool listAuras(Unit *unit);
+
+        //More generalized method than HasAura().  It looks for
+        //any rank of the spell and it doesn't care which
+        //spell effect you want.  If it has the spell aura than
+        //it returns true
+        bool HasAuraName(Unit *unit, std::string spell, uint64 casterGuid=0);
+        bool HasAuraName(Unit *unit, uint32 spellId, uint64 casterGuid=0);
+
+        //The following functions return true only a match is found and the bot successfully casted a spell to resolve the problem
+        //If the result is false, either a match is not found, or the ClassAI could not cast or refused to cast a spell for some reason..
+
+        //Combination of all Healer roles, scans the party and decides if group healing > individual healing > Rez > curing > buffing is needed
+        //and directs any matches found to individual ClassAIs
+        //Main Raid scan function for Healer/Support types..
+        bool DoSupportRaid(Player *gPlayer, float radius=30, bool dResurrect=true, bool dGroupHeal=true, bool dHeal=true, bool dCure=true, bool dBuff=true);
+        //Find matching debuffs on target to provided Spell, and call castSpell() with provided parameters
+        bool castDispel (uint32 dispelSpell, Unit *dTarget, bool checkFirst=true, bool castExistingAura=false, bool skipFriendlyCheck=false, bool skipEquipStanceCheck=false);
+        //Cast matching debuffs on self with probided SpellId list.. Mainly Used for Racial spells.. List is used to prevent extra loops for each spell..
+        bool castSelfCCBreakers (uint32 castList[]);
+
+
+
+        typedef std::set<Unit *> AttackerSet;
+
+        uint8 rezSpamTimer;
+        uint32 foodDrinkSpamTimer;
+        static const uint32 foodDrinkSpamCount = 100;
+
+        BotRole m_role;
+        bool m_pulling;
+        uint32 threatThreshold, offensiveSpellThreshold;
+
+        // RACIAL SPELLS
+        uint32 R_ARCANE_TORRENT, R_BERSERKING, R_BLOOD_FURY, R_CANNIBALIZE, R_ESCAPE_ARTIST, R_EVERY_MAN_FOR_HIMSELF, R_GIFT_OF_NAARU, R_SHADOWMELD, R_STONEFORM, R_WAR_STOMP, R_WILL_OF_FORSAKEN;
+        // first aid
+        uint32 RECENTLY_BANDAGED;
+        uint32 SHOOT;
+
+    private:
+        Player *m_master;
+        Player *m_bot;
+        PlayerbotAI *m_ai;
+        Unit *mainTank;
+
+
+
+};
+
+#endif
diff --git a/src/server/game/AI/Bots/PlayerbotDeathKnightAI.cpp b/src/server/game/AI/Bots/PlayerbotDeathKnightAI.cpp
new file mode 100644
index 0000000..a3836f4
--- /dev/null
+++ b/src/server/game/AI/Bots/PlayerbotDeathKnightAI.cpp
@@ -0,0 +1,325 @@
+/*
+Name : PlayerbotDeathknightAI.cpp
+Complete: maybe around 65%
+
+Limitations:    - Talent build decision is made by key talent spells, which makes them viable only after level 50-ish.. Until then default behaviour is Blood dps/offtank type
+                - Tanking bots should taunt if any group member is under attack, currently only saves master
+                - Situations needing Death grip casting : limited / non-existant..
+
+Authors : SwaLLoweD, rrtn
+Version : 0.40
+*/
+
+#include "PlayerbotDeathKnightAI.h"
+
+class PlayerbotAI;
+PlayerbotDeathKnightAI::PlayerbotDeathKnightAI(Player* const master, Player* const bot, PlayerbotAI* const ai): PlayerbotClassAI(master, bot, ai)
+{
+    foodDrinkSpamTimer = 0;
+    LoadSpells();
+}
+
+PlayerbotDeathKnightAI::~PlayerbotDeathKnightAI() {}
+
+void PlayerbotDeathKnightAI::LoadSpells(){
+    PlayerbotAI *ai = GetAI();
+    if (!ai) return;
+    #pragma region SpellId Fill
+    // Unholy
+    PLAGUE_STRIKE = ai->getSpellIdExact("Plague Strike");
+    DEATH_STRIKE = ai->getSpellIdExact("Death Strike");
+    SCOURGE_STRIKE  = ai->getSpellIdExact("Scourge Strike");
+
+    // Frost
+    ICY_TOUCH = ai->getSpellIdExact("Icy Touch");
+    OBLITERATE = ai->getSpellIdExact("Obliterate");
+
+    // Blood
+    BLOOD_STRIKE = ai->getSpellIdExact("Blood Strike");
+    HEART_STRIKE  = ai->getSpellIdExact("Heart Strike");
+    RUNE_TAP = ai->getSpellIdExact("Rune Tap");
+    DARK_COMMAND = ai->getSpellIdExact("Dark Command");
+
+    // AOE
+    HOWLING_BLAST = ai->getSpellIdExact("Howling Blast");
+    BLOOD_BOIL = ai->getSpellIdExact("Blood Boil");
+    PESTILENCE = ai->getSpellIdExact("Pestilence");
+    CORPSE_EXPLOSION = ai->getSpellIdExact("Corpse Explosion");
+    DEATH_AND_DECAY = ai->getSpellIdExact("Death and Decay");
+
+    // Rune attacks
+    FROST_STRIKE = ai->getSpellIdExact("Frost Strike");
+    DEATH_COIL = ai->getSpellIdExact("Death Coil");
+    RUNE_STRIKE = ai->getSpellIdExact("Rune Strike");
+
+    // CC Interrupt
+    DEATH_GRIP = ai->getSpellIdExact("Death Grip");
+    CHAINS_OF_ICE = ai->getSpellIdExact("Chains of Ice");
+    MIND_FREEZE = ai->getSpellIdExact("Mind Freeze");
+    HUNGERING_COLD = ai->getSpellIdExact("Hungering Cold");
+    STRANGULATE = ai->getSpellIdExact("Strangulate");
+
+    // Debuffs
+    FROST_FEVER = 55095; //ai->getSpellIdExact("Frost Fever",true);
+    BLOOD_PLAGUE = 55078; //ai->getSpellIdExact("Blood Plague",true);
+    CRYPT_FEVER = ai->getSpellIdExact("Crypt Fever",true);
+    EBON_PLAGUE = ai->getSpellIdExact("Ebon Plague",true);
+    MARK_OF_BLOOD = ai->getSpellIdExact("Mark of Blood");
+
+    // Buffs
+    HORN_OF_WINTER = ai->getSpellIdExact("Horn of Winter");
+    BONE_SHIELD = ai->getSpellIdExact("Bone Shield");
+    VAMPIRIC_BLOOD = ai->getSpellIdExact("Vampiric Blood");
+    HYSTERIA = ai->getSpellIdExact("Hysteria");
+    UNBREAKABLE_ARMOR = ai->getSpellIdExact("Unbreakable Armor");
+    ANTI_MAGIC_SHELL = ai->getSpellIdExact("Anti Magic Shell");
+    ANTI_MAGIC_ZONE = ai->getSpellIdExact("Anti Magic Zone");
+    ICEBOUND_FORTITUDE = ai->getSpellIdExact("Icebound Fortitude");
+    EMPOWER_WEAPON = ai->getSpellIdExact("Empower Rune Weapon");
+    LICHBORNE = ai->getSpellIdExact("Lichborne");
+
+    // Summons
+    RAISE_DEAD = ai->getSpellIdExact("Raise Dead");
+    ARMY_OF_THE_DEAD = ai->getSpellIdExact("Army of the Dead");
+    SUMMON_GARGOYLE = ai->getSpellIdExact("Summon Gargoyle");
+    GHOUL_FRENZY = ai->getSpellIdExact("Ghoul Frenzy");
+    DEATH_PACT = ai->getSpellIdExact("Death Pact");
+    DANCING_WEAPON = ai->getSpellIdExact("Dancing Rune Weapon");
+
+    // Presences
+    BLOOD_PRESENCE = ai->getSpellIdExact("Blood Presence");
+    FROST_PRESENCE = ai->getSpellIdExact("Frost Presence");
+    UNHOLY_PRESENCE  = ai->getSpellIdExact("Unholy Presence");
+
+    // Talent
+    TALENT_BLOOD = HEART_STRIKE;
+    TALENT_FROST = FROST_STRIKE;
+    TALENT_UNHOLY = SCOURGE_STRIKE;
+
+    uint8 talentCounter = 0;
+    if (TALENT_BLOOD) talentCounter++;
+    if (TALENT_FROST) talentCounter++;
+    if (TALENT_UNHOLY) talentCounter++;
+    if (talentCounter > 1) { TALENT_BLOOD = 0; TALENT_FROST = 0; TALENT_UNHOLY = 0; } //Unreliable Talent detection.
+    #pragma endregion
+}
+void PlayerbotDeathKnightAI::DoNextCombatManeuver(Unit *pTarget)
+{
+    if (!pTarget || pTarget->isDead()) return;
+    PlayerbotAI *ai = GetAI();
+    if (!ai) return;
+    Player *m_bot = GetPlayerBot();
+    if (!m_bot || m_bot->isDead()) return;
+    Unit *pVictim = pTarget->getVictim();
+    Unit *m_tank = FindMainTankInRaid(GetMaster());
+    if (!m_tank && m_bot->GetGroup() && GetMaster()->GetGroup() != m_bot->GetGroup()) { FindMainTankInRaid(m_bot); }
+    if (!m_tank) { m_tank = m_bot; }
+    uint32 masterHP = GetMaster()->GetHealth()*100 / GetMaster()->GetMaxHealth();
+    float pDist = m_bot->GetDistance(pTarget);
+    uint8 pThreat = GetThreatPercent(pTarget);
+
+    Pet *pet = m_bot->GetPet();
+    std::ostringstream out;
+
+
+    if (!m_pulling)
+    {
+        m_role = BOT_ROLE_DPS_MELEE;
+        #pragma region Choose Role/Presence
+
+        // Choose Presence
+        if (m_tank->GetGUID() == m_bot->GetGUID()) // Hey! I am Main Tank
+        {
+            if (CastSpell(FROST_PRESENCE,m_bot)) { m_role = BOT_ROLE_TANK; return; }
+        }
+        else if (isUnderAttack()) // I am under attack
+        {
+            if (pVictim && pVictim->GetGUID() == m_bot->GetGUID() && pDist <= 2)  {} // My target is almost up to me, no need to search
+            else //Have to select nearest target
+            {
+                Unit *curAtt = GetNearestAttackerOf(m_bot);
+                if (curAtt && curAtt->GetGUID() != pTarget->GetGUID())
+                {
+                    m_bot->SetSelection(curAtt->GetGUID());
+                    //ai->AddLootGUID(curAtt->GetGUID());
+                    DoNextCombatManeuver(curAtt); //Restart new update to get variables fixed..
+                    return;
+                }
+            }
+            //my target is attacking me
+            //if (m_bot->getRace() == (uint8) RACE_NIGHTELF && CastSpell(R_SHADOWMELD,m_bot) ) { return; }
+            if (CastSpell(FROST_PRESENCE,m_bot)) { m_role = BOT_ROLE_OFFTANK; return; }
+        }
+        else if (TALENT_UNHOLY)
+        {
+            if (CastSpell(UNHOLY_PRESENCE,m_bot)) return;
+        }
+        else if (CastSpell(BLOOD_PRESENCE,m_bot)) return;
+        #pragma endregion
+    }
+
+    // Cast CC breakers if any match found  (does not work yet)
+    // uint32 ccSpells[6] = { LICHBORNE, ICEBOUND_FORTITUDE, R_ESCAPE_ARTIST, R_EVERY_MAN_FOR_HIMSELF, R_WILL_OF_FORSAKEN, R_STONEFORM };
+    // if (castSelfCCBreakers(ccSpells)) { } //most of them dont have gcd
+
+    TakePosition(pTarget);
+
+
+   if (m_pulling) {
+        if (GetAI()->CastSpell(DEATH_GRIP,pTarget)) {
+            m_pulling = false;
+            GetAI()->SetCombatOrder(ORDERS_NONE);
+            GetAI()->Follow(*GetMaster());
+            GetAI()->SetIgnoreUpdateTime(2);
+
+            if (m_bot->GetPet()) pet->SetReactState (REACT_DEFENSIVE);
+         }
+          return;
+    }
+
+    // If there's a cast stop
+    if(m_bot->HasUnitState(UNIT_STAT_CASTING)) { return; }
+
+    #pragma region Buff Heal Interrupt
+    //Buff UP
+    if (CastSpell(HORN_OF_WINTER,m_bot)) { return; }
+    if (CastSpell(BONE_SHIELD,m_bot)) { return; }
+
+    //HEAL UP && PROTECT UP
+    if (ai->GetHealthPercent() < 80 && ai->GetHealthPercent() > 20 && CastSpell(VAMPIRIC_BLOOD,m_bot)) { }  //NO GCD
+    if (ai->GetHealthPercent() < 65 && CastSpell(RUNE_TAP,m_bot)) { } //NO GCD
+    if (CanCast(DEATH_STRIKE,pTarget,true) && ai->GetHealthPercent() < 90 &&
+        (pTarget->HasAura(FROST_FEVER,m_bot->GetGUID()) ||pTarget->HasAura(BLOOD_PLAGUE,m_bot->GetGUID()) )
+        && CastSpell(DEATH_STRIKE,pTarget,false) ) {return;}
+    if (m_bot->getRace() == (uint8) RACE_DWARF && ai->GetHealthPercent() < 75 && CastSpell(R_STONEFORM,m_bot)) { } //no gcd
+    if (m_bot->getRace() == (uint8) RACE_DRAENEI && ai->GetHealthPercent() < 55 && CastSpell(R_GIFT_OF_NAARU,m_bot)) { return; } //no Gcd, but has cast
+    if (pet && ai->GetHealthPercent() < 50 && CastSpell(DEATH_PACT,m_bot)) { return; }
+    if (pet && ai->GetHealthPercent() < 60 && CastSpell(MARK_OF_BLOOD,pTarget)) { return; }
+    if (ai->GetHealthPercent() < 65 && CastSpell(ICEBOUND_FORTITUDE,m_bot)) { } //No GCD
+    if (ai->GetHealthPercent() < 65 && CastSpell(UNBREAKABLE_ARMOR,m_bot)) { return; }
+
+    //Break spells being cast
+    if (pTarget->IsNonMeleeSpellCasted(true))
+    {
+        if (CastSpell(MIND_FREEZE,pTarget)) {} // No GCD
+        if (CastSpell(STRANGULATE,pTarget)) { return; }
+        if (m_bot->getRace() == (uint8) RACE_BLOODELF && pDist < 8 && CastSpell(R_ARCANE_TORRENT, pTarget)) { } //no gcd
+        if (CastSpell(ANTI_MAGIC_ZONE,m_bot)) { return; }
+        if (CastSpell(ANTI_MAGIC_SHELL,m_bot)) {} //NO GCD
+    }
+
+    //Catch
+    if (pTarget->HasUnitMovementFlag(UNIT_FLAG_FLEEING))
+    {
+        if (CastSpell(DEATH_GRIP,pTarget)) return;
+        if (CastSpell(CHAINS_OF_ICE,pTarget)) return;
+    }
+    #pragma endregion
+
+    #pragma region Taunt / Threat
+    // if i am main tank, protect master by taunt
+    if(m_tank->GetGUID() == m_bot->GetGUID())
+    {
+        // Taunt if needed (Only for master)
+        Unit *curAtt = GetAttackerOf(GetMaster());
+        if (curAtt && CastSpell(DARK_COMMAND, curAtt))  { }     //No gcd
+        // My target is not attacking me, taunt..
+        if (pVictim && pVictim->GetGUID() != m_bot->GetGUID() && CastSpell(DARK_COMMAND, pTarget) )  { } // No gcd
+    }
+
+    // If not in Frost Presence slow down due to threat
+    if (pThreat > threatThreshold && !m_bot->HasAura(FROST_PRESENCE) && m_tank->GetGUID() != m_bot->GetGUID() && !isUnderAttack())
+    {
+        if (m_tank->getVictim() && m_tank->getVictim()->GetGUID() != pTarget->GetGUID()) // I am attacking wrong target!!
+        {
+            m_bot->SetSelection(m_tank->getVictim()->GetGUID());
+            return;
+        }
+        else { return; } //DK has no threat reducing spells, just slow down
+    }
+    #pragma endregion
+
+    #pragma region Dps
+    //Dps up
+    if (CastSpell(EMPOWER_WEAPON,m_bot)) {} //NO GCD
+    if (ai->GetHealthPercent() > 90 && CastSpell(HYSTERIA,m_bot)) {} //NO GCD
+    if (m_bot->getRace() == (uint8) RACE_TROLL && CastSpell(R_BERSERKING,m_bot)) {} //no GCD
+    if (m_bot->getRace() == (uint8) RACE_ORC && CastSpell(R_BLOOD_FURY,m_bot)) {} //no GCD
+
+    // Use up excess Runic Power
+    if (ai->GetRunicPower() > 60 && CastSpell(FROST_STRIKE,pTarget)) { return; }
+    else if (ai->GetRunicPower() > 60 && CastSpell(DEATH_COIL,pTarget,true,true,true)) { return; }
+    if ((isUnderAttack() || ai->GetRunicPower() > 70) && CastSpell(RUNE_STRIKE,pTarget)) {} //Next attack spell
+
+    // Build Diseases
+    if (!pTarget->HasAura(FROST_FEVER,m_bot->GetGUID()) && CastSpell(ICY_TOUCH,pTarget)) { return; }
+    if (!pTarget->HasAura(BLOOD_PLAGUE,m_bot->GetGUID()) && CastSpell(PLAGUE_STRIKE,pTarget)) { return; }
+
+    // Use AOEs summons
+    if (isUnderAttack(m_tank,4) && CastSpell(DEATH_AND_DECAY,pTarget)) { return; }
+    if (isUnderAttack(m_tank,4) && CastSpell(HOWLING_BLAST,pTarget)) { return; }
+    if (CanCast(PESTILENCE,pTarget,true) && isUnderAttack(m_tank,4) &&
+        (pTarget->HasAura(FROST_FEVER,m_bot->GetGUID()) && pTarget->HasAura(BLOOD_PLAGUE,m_bot->GetGUID()) )
+        && CastSpell(PESTILENCE,pTarget,false)) { return; }
+    if (CanCast(BLOOD_BOIL,pTarget,true) && isUnderAttack(m_tank,4) &&
+        (pTarget->HasAura(FROST_FEVER,m_bot->GetGUID()) || pTarget->HasAura(BLOOD_PLAGUE,m_bot->GetGUID()) )
+        && CastSpell(BLOOD_BOIL,pTarget,false)) { return; }
+    if (m_bot->getRace() == (uint8) RACE_TAUREN && pDist < 8 && CastSpell(R_WAR_STOMP, pTarget)) { return; } //no gcd but is cast
+    if (isUnderAttack(m_tank,6) && CastSpell(ARMY_OF_THE_DEAD,m_bot)) { return; }
+    if (isUnderAttack(m_tank,4) && CastSpell(SUMMON_GARGOYLE,pTarget)) { return; } //This should be somewhat different
+
+    // Use standard damage spells
+    if (CastSpell(HEART_STRIKE,pTarget,true,true)) { return; }
+    if (CastSpell(BLOOD_STRIKE,pTarget)) { return; }
+    if (TALENT_FROST && CastSpell(OBLITERATE,pTarget)) { return; }
+    else if (TALENT_UNHOLY && CastSpell(SCOURGE_STRIKE,pTarget)) { return; }
+    else if (CastSpell(DEATH_STRIKE,pTarget)) { return; }
+    #pragma endregion
+
+} // end DoNextCombatManeuver
+
+void PlayerbotDeathKnightAI::DoNonCombatActions()
+{
+    PlayerbotAI *ai = GetAI();
+    Player *m_bot = GetPlayerBot();
+    if (!m_bot || !ai || m_bot->isDead()) { return; }
+
+    //If Casting or Eating/Drinking return
+    if (m_bot->HasUnitState(UNIT_STAT_CASTING)) { return; }
+    if (m_bot->getStandState() == UNIT_STAND_STATE_SIT) { return; }
+
+    //Buff UP
+    if (CastSpell(HORN_OF_WINTER,m_bot)) { return; }
+    if (CastSpell(BONE_SHIELD,m_bot)) { return; }
+
+    //mana/hp check
+    if (m_bot->getRace() == (uint8) RACE_UNDEAD_PLAYER && ai->GetHealthPercent() < 75 && CastSpell(R_CANNIBALIZE,m_bot)) { return; }
+    if (m_bot->GetHealth() < m_bot->GetMaxHealth() && CastSpell(RUNE_TAP,m_bot)) { return; } //no gcd but lets give the others a time to heal
+    if (ai->GetHealthPercent() < 30) { ai->Feast(); }
+    //Item* fItem = ai->FindBandage();
+    /*if (pItem == NULL && fItem != NULL && !m_bot->HasAura(RECENTLY_BANDAGED, 0) && ai->GetHealthPercent() < 70)
+    {
+        ai->TellMaster("I could use first aid.");
+        ai->UseItem(*fItem);
+        ai->SetIgnoreUpdateTime(8);
+        return;
+    } */
+} // end DoNonCombatActions
+
+void PlayerbotDeathKnightAI::Pull()
+{
+    Unit* pTarget = ObjectAccessor::GetUnit(*GetMaster(), GetMaster()->GetSelection());
+    if (pTarget==NULL || pTarget->IsFriendlyTo(GetMaster()))
+    {
+        GetPlayerBot()->Say("Invalid target", LANG_UNIVERSAL);
+        m_pulling = false;
+        GetAI()->Follow(*GetMaster());
+        return;
+    }
+
+    m_role = BOT_ROLE_DPS_RANGED;
+    m_pulling = true;
+    GetAI()->SetIgnoreUpdateTime(0);
+
+    if (GetPlayerBot()->GetPet()) GetPlayerBot()->GetPet()->SetReactState (REACT_PASSIVE);
+}
diff --git a/src/server/game/AI/Bots/PlayerbotDeathKnightAI.h b/src/server/game/AI/Bots/PlayerbotDeathKnightAI.h
new file mode 100644
index 0000000..9c897bb
--- /dev/null
+++ b/src/server/game/AI/Bots/PlayerbotDeathKnightAI.h
@@ -0,0 +1,60 @@
+#ifndef _PLAYERDEATHKNIGHTAI_H
+#define _PLAYERDEATHKNIGHTAI_H
+
+#include "PlayerbotClassAI.h"
+
+//class Player;
+
+class PlayerbotDeathKnightAI : PlayerbotClassAI
+{
+    public:
+        PlayerbotDeathKnightAI(Player *const master, Player *const bot, PlayerbotAI *const ai);
+        virtual ~PlayerbotDeathKnightAI();
+
+        virtual void LoadSpells();
+
+        //all combat actions go here
+        void DoNextCombatManeuver(Unit *);
+
+        //all non combat actions go here, ex buffs, heals, rezzes
+        void DoNonCombatActions();
+
+        void Pull();
+
+    private:
+        // Unholy
+        uint32 PLAGUE_STRIKE, DEATH_STRIKE, SCOURGE_STRIKE;
+
+        // Frost
+        uint32 ICY_TOUCH, OBLITERATE;
+
+        // Blood
+        uint32 BLOOD_STRIKE, HEART_STRIKE, RUNE_TAP, DARK_COMMAND;
+
+        // AOE
+        uint32 HOWLING_BLAST, BLOOD_BOIL, PESTILENCE, CORPSE_EXPLOSION, DEATH_AND_DECAY;
+
+        // Rune attacks
+        uint32 FROST_STRIKE, DEATH_COIL, RUNE_STRIKE;
+
+        // CC Interrupt
+        uint32 DEATH_GRIP, CHAINS_OF_ICE, MIND_FREEZE, HUNGERING_COLD, STRANGULATE;
+
+        // Debuffs
+        uint32 FROST_FEVER, BLOOD_PLAGUE, CRYPT_FEVER, EBON_PLAGUE, MARK_OF_BLOOD;
+
+        // Buffs
+        uint32 HORN_OF_WINTER, BONE_SHIELD, VAMPIRIC_BLOOD, HYSTERIA, UNBREAKABLE_ARMOR, ANTI_MAGIC_SHELL, ANTI_MAGIC_ZONE, ICEBOUND_FORTITUDE, EMPOWER_WEAPON, LICHBORNE;
+
+        // Summons
+        uint32 RAISE_DEAD, ARMY_OF_THE_DEAD, SUMMON_GARGOYLE, GHOUL_FRENZY, DEATH_PACT, DANCING_WEAPON;
+
+        // Presences
+        uint32 BLOOD_PRESENCE, FROST_PRESENCE, UNHOLY_PRESENCE;
+
+        // Talent
+        uint32 TALENT_BLOOD, TALENT_FROST, TALENT_UNHOLY;
+
+};
+
+#endif
diff --git a/src/server/game/AI/Bots/PlayerbotDruidAI.cpp b/src/server/game/AI/Bots/PlayerbotDruidAI.cpp
new file mode 100644
index 0000000..5fc5130
--- /dev/null
+++ b/src/server/game/AI/Bots/PlayerbotDruidAI.cpp
@@ -0,0 +1,674 @@
+/*
+Name : PlayerbotDruidAI.cpp
+Complete: maybe around 70%
+
+Limitations:    - Talent build decision is made by key talent spells, which makes them viable only after level 50-ish.. Until then default behaviour is a combination of Feral/balance type..
+                - Tanking bots should taunt if any group member is under attack, currently only saves master
+                - Tree of life form transition is late and may never occur, due to healing bots attacking priority at full mana.
+                - Boomkin's support roles are not fully covered.. For example -> off healing
+                - Situations needing Abolish Disease casting : limited / non-existant..
+
+Authors : SwaLLoweD, rrtn, Natsukawa
+Version : 0.40
+*/
+#include "PlayerbotDruidAI.h"
+
+class PlayerbotAI;
+
+PlayerbotDruidAI::PlayerbotDruidAI(Player *const master, Player *const bot, PlayerbotAI *const ai): PlayerbotClassAI(master, bot, ai)
+{
+    foodDrinkSpamTimer = 0;
+    LoadSpells();
+}
+
+PlayerbotDruidAI::~PlayerbotDruidAI(){}
+
+void PlayerbotDruidAI::LoadSpells() {
+    PlayerbotAI *ai = GetAI();
+    if (!ai) return;
+#pragma region SpellId Fill
+    // Balance Spells
+    MOONFIRE = ai->getSpellIdExact("Moonfire"); //attacks
+    WRATH = ai->getSpellIdExact("Wrath");
+    STARFIRE = ai->getSpellIdExact("Starfire");
+    STARFALL = ai->getSpellIdExact("Starfall");
+    FORCE_OF_NATURE = ai->getSpellIdExact("Force of Nature");
+    TYPHOON = ai->getSpellIdExact("Typhoon");
+    HURRICANE = ai->getSpellIdExact("Hurricane");
+    INSECT_SWARM = ai->getSpellIdExact("Insect Swarm");
+
+    CYCLONE = ai->getSpellIdExact("Cyclone");
+    ROOTS = ai->getSpellIdExact("Entangling Roots");
+    NATURES_GRASP = ai->getSpellIdExact("Nature's Grasp");
+
+    HIBERNATE = ai->getSpellIdExact("Hibernate");
+    FAERIE_FIRE = ai->getSpellIdExact("Faerie Fire");
+
+
+    // Bear Form Spells
+    MAUL = ai->getSpellIdExact("Maul");
+    BASH = ai->getSpellIdExact("Bash");
+    LACERATE = ai->getSpellIdExact("Lacerate");
+    MANGLE_BEAR = ai->getSpellIdExact("Mangle (Bear)");
+    SWIPE_BEAR = ai->getSpellIdExact("Swipe (Bear)");
+
+    DEMORALIZING_ROAR = ai->getSpellIdExact("Demoralizing Roar");
+    GROWL = ai->getSpellIdExact("Growl");
+    CHALLENGING_ROAR = ai->getSpellIdExact("Challenging Roar");
+
+    ENRAGE = ai->getSpellIdExact("Enrage");
+    FERAL_CHARGE_BEAR = ai->getSpellIdExact("Feral Charge - Bear");
+    FRENZIED_REGENERATION = ai->getSpellIdExact("Frenzied Regeneration");
+
+
+    //Cat Attack type's
+    RAKE = ai->getSpellIdExact("Rake"); //40 energy
+    CLAW = ai->getSpellIdExact("Claw"); //45
+    MANGLE_CAT = ai->getSpellIdExact("Mangle (Cat)"); //45
+    SHRED = ai->getSpellIdExact("Shred");
+
+    RIP = ai->getSpellIdExact("Rip"); //30
+    FEROCIOUS_BITE = ai->getSpellIdExact("Ferocious Bite"); //35
+    SAVAGE_ROAR = ai->getSpellIdExact("Savage Roar");
+    MAIM = ai->getSpellIdExact("Maim"); //35
+
+    FERAL_CHARGE_CAT = ai->getSpellIdExact("Feral Charge - Cat");
+    COWER = ai->getSpellIdExact("Cower"); //20
+    TIGERS_FURY = ai->getSpellIdExact("Tiger's Fury");
+
+    // Feral General
+    BERSERK = ai->getSpellIdExact("Berserk");
+    FAERIE_FIRE_FERAL = ai->getSpellIdExact("Faerie Fire (Feral)"); //debuffs
+
+    //buffs
+    MARK_OF_THE_WILD = ai->getSpellIdExact("Mark of the Wild"); //buffs
+    GIFT_OF_THE_WILD = ai->getSpellIdExact("Gift of the Wild");
+    THORNS = ai->getSpellIdExact("Thorns");
+    SURVIVAL_INSTINCTS = ai->getSpellIdExact("Survival Instincts");
+
+    // Restoration Spells
+    LIFEBLOOM = ai->getSpellIdExact("Lifebloom");
+    REJUVENATION = ai->getSpellIdExact("Rejuvenation"); //heals
+    REGROWTH = ai->getSpellIdExact("Regrowth");
+    NOURISH = ai->getSpellIdExact("Nourish");
+    SWIFTMEND = ai->getSpellIdExact("Swiftmend");
+    HEALING_TOUCH = ai->getSpellIdExact("Healing Touch");
+    INNERVATE = ai->getSpellIdExact("Innervate");
+    WILD_GROWTH = ai->getSpellIdExact("Wild Growth");
+    TRANQUILITY = ai->getSpellIdExact("Tranquility");
+    NATURES_SWIFTNESS = ai->getSpellIdExact("Nature's Swiftness");
+
+    CURE_POISON = ai->getSpellIdExact("Abolish Poison");
+    if (!CURE_POISON) CURE_POISON = ai->getSpellIdExact("Cure Poison");
+
+    REBIRTH    = ai->getSpellIdExact("Rebirth");
+    REVIVE = ai->getSpellIdExact("Revive");
+
+    BARKSKIN = ai->getSpellIdExact("Barkskin");
+
+    //Druid Forms
+    BEAR_FORM = ai->getSpellIdExact("Dire Bear Form");
+    if (!BEAR_FORM) BEAR_FORM = ai->getSpellIdExact("Bear Form");
+    CAT_FORM = ai->getSpellIdExact("Cat Form");
+    MOONKIN_FORM = ai->getSpellIdExact("Moonkin Form");
+    TREE_OF_LIFE_FORM = ai->getSpellIdExact("Tree of Life"); //33891;//learning spell has higher id..
+    AQUATIC_FORM = ai->getSpellIdExact("Aquatic Form");
+    TRAVEL_FORM = ai->getSpellIdExact("Travel Form");
+    FLIGHT_FORM = ai->getSpellIdExact("Swift Flight Form");
+    if (!FLIGHT_FORM) FLIGHT_FORM = ai->getSpellIdExact("Flight Form");
+
+
+    TALENT_BALANCE    = MOONKIN_FORM;
+    TALENT_RESTO    = SWIFTMEND;
+    TALENT_FERAL    = MANGLE_CAT;
+
+    uint8 talentCounter = 0;
+    if (TALENT_BALANCE) talentCounter++;
+    if (TALENT_FERAL) talentCounter++;
+    if (TALENT_RESTO) talentCounter++;
+    if (talentCounter > 1) { TALENT_BALANCE = 0; TALENT_RESTO = 0; TALENT_FERAL = 0; } //Unreliable Talent detection.
+#pragma endregion
+}
+
+void PlayerbotDruidAI::DoNextCombatManeuver(Unit *pTarget)
+{
+    if (!pTarget || pTarget->isDead()) return;
+    PlayerbotAI *ai = GetAI();
+    if (!ai) return;
+    Player *m_bot = GetPlayerBot();
+    if (!m_bot || m_bot->isDead()) return;
+    Unit *pVictim = pTarget->getVictim();
+    Unit *m_tank = FindMainTankInRaid(GetMaster());
+    if (!m_tank && m_bot->GetGroup() && GetMaster()->GetGroup() != m_bot->GetGroup()) { FindMainTankInRaid(m_bot); }
+    if (!m_tank) { m_tank = m_bot; }
+    uint32 masterHP = GetMaster()->GetHealth()*100 / GetMaster()->GetMaxHealth();
+    float pDist = m_bot->GetDistance(pTarget);
+    uint8 pThreat = GetThreatPercent(pTarget);
+
+    uint8 reqHeal = 0;
+    uint8 OwnPartyHP = GetHealthPercentRaid(m_bot, reqHeal);
+
+
+    #pragma region Select behaviour
+    if (m_tank->GetGUID() == m_bot->GetGUID()) // Hey! I am Main Tank
+    {
+        if (TALENT_FERAL && BEAR_FORM) { m_role = BOT_ROLE_TANK; } //Just Keep Tanking dont even change forms for healing
+        else
+        {
+            if (TALENT_BALANCE) {
+                if ((ai->GetHealthPercent() <= 40 || masterHP <30 ) && (ai->GetManaPercent() >= 40)) { m_role = BOT_ROLE_SUPPORT; }
+                else if (OwnPartyHP < 20 && ai->GetManaPercent() >= 30) { m_role = BOT_ROLE_SUPPORT; }
+                else if (ai->GetManaPercent() < 25 ) { m_role = BOT_ROLE_TANK; }
+                else { m_role = BOT_ROLE_DPS_RANGED; }
+            }
+            else //I am both healer and tank?? Hmm
+            {
+                if ((ai->GetHealthPercent() <= 70 || masterHP <70 ) && (ai->GetManaPercent() >= 50)) { m_role = BOT_ROLE_SUPPORT; }
+                else if (OwnPartyHP < 20 && ai->GetManaPercent() >= 30) { m_role = BOT_ROLE_SUPPORT; }
+                else if (ai->GetManaPercent() < 15 ) { m_role = BOT_ROLE_TANK; }
+                else { m_role = BOT_ROLE_DPS_RANGED; }
+            }
+        }
+    }
+    else if (isUnderAttack() && !( ai->GetForm() == FORM_MOONKIN || ai->GetForm() == FORM_TREE)  ) // if i am under attack
+    {
+        // Keep being in Cat Form if you can reduce threat
+        if (ai->GetForm() == FORM_CAT && CastSpell(COWER,pTarget)) {return; }
+        else if (TALENT_RESTO && ai->GetManaPercent() > 10 ) { m_role = BOT_ROLE_SUPPORT; }
+        else { m_role = BOT_ROLE_OFFTANK; }
+    }
+    else if (TALENT_FERAL && CAT_FORM) { // If has any feral forms at all
+        if ((ai->GetHealthPercent() <= 40 || masterHP <40 ) && (ai->GetManaPercent() >= 40)) { m_role = BOT_ROLE_SUPPORT; }
+        else if (OwnPartyHP < 30 && ai->GetManaPercent() >= 30) { m_role = BOT_ROLE_SUPPORT; }
+        else{ m_role = BOT_ROLE_DPS_MELEE; }
+    }
+    else if (TALENT_BALANCE) {
+        if ((ai->GetHealthPercent() <= 50 || masterHP <40 ) && (ai->GetManaPercent() >= 10)) { m_role = BOT_ROLE_SUPPORT; }
+        else if (OwnPartyHP < 40 && ai->GetManaPercent() >= 30) { m_role = BOT_ROLE_SUPPORT; }
+        else { m_role = BOT_ROLE_DPS_RANGED; }
+    }
+    else if (TALENT_RESTO)    { m_role = BOT_ROLE_SUPPORT; }
+    else
+    {
+        // Unknown build or low level : Do not change forms rapidly..
+        if ( (ai->GetManaPercent() < 30 && BEAR_FORM) || ( (ai->GetForm() == FORM_CAT || ai->GetForm() == FORM_DIREBEAR || ai->GetForm() == FORM_BEAR) && ai->GetManaPercent() < 70 )  ) m_role = BOT_ROLE_DPS_MELEE;
+        else { m_role = BOT_ROLE_DPS_RANGED; }
+    }
+
+    if (!isUnderAttack() && m_tank->GetGUID() != m_bot->GetGUID())
+    {
+        // Select Attacking target
+        if (pVictim && pVictim->GetGUID() == m_bot->GetGUID() && pDist <= 2) {} //if my target is attacking me continue
+        else
+        {
+            Unit *curAtt = GetNearestAttackerOf(m_bot);
+            if (curAtt && curAtt->GetGUID() != pTarget->GetGUID())
+            {
+                m_bot->SetSelection(curAtt->GetGUID());
+                //ai->AddLootGUID(curAtt->GetGUID());
+                DoNextCombatManeuver(curAtt); //Restart new update to get variables fixed..
+                return;
+            }
+        }
+        //my target is attacking me
+    }
+    #pragma endregion
+
+    // If there's a cast stop
+    if (m_bot->HasUnitState(UNIT_STAT_CASTING)) return;
+    // Return to normal form from non combat forms
+    if (ai->GetForm() == FORM_NONE || ai->GetForm() == FORM_CAT || ai->GetForm() == FORM_TREE || ai->GetForm() == FORM_MOONKIN || ai->GetForm() == FORM_DIREBEAR || ai->GetForm() == FORM_BEAR )  { } //Those are valid incombat auras
+    else if (ai->GetForm() != FORM_NONE && ChangeForm(1)) { } //return to caster form
+
+    switch(m_role)
+    {
+        #pragma region BOT_ROLE_DPS_MELEE
+        case BOT_ROLE_DPS_MELEE:
+            //ai->TellMaster("DruidCombat");
+
+            // Do caster form stuff
+            if (ai->GetForm() == FORM_NONE)
+            {
+                //We have little mana probably cant change form
+                if (ai->GetManaPercent() < 20 && CastSpell (INNERVATE, m_bot) ) { return; }
+                else if(m_bot->getRace() == (uint8) RACE_TAUREN && pDist < 8 && CastSpell(R_WAR_STOMP, pTarget)) {  return;}
+                else if(DoSupportRaid(GetMaster(),false,false,false)) return;
+                else if(m_bot->GetGroup() && GetMaster()->GetGroup() != m_bot->GetGroup() && DoSupportRaid(m_bot,false,false,false)) { return; }
+            }
+
+            if (CAT_FORM) { if (ChangeForm(CAT_FORM)) { return; } }
+            else if (BEAR_FORM) { if (ChangeForm(BEAR_FORM)) { return; } }
+            else if (ai->GetForm() != FORM_NONE && ChangeForm(1)) {  } //Normal Form
+
+            TakePosition(pTarget);
+            break;
+        #pragma endregion
+
+        #pragma region BOT_ROLE_TANK / BOT_ROLE_OFFTANK
+        case BOT_ROLE_OFFTANK:
+        case BOT_ROLE_TANK: // It is a tank druid or a defending druid
+
+            // Do what you must before getting attacked...
+            if (ai->GetForm() == FORM_NONE)
+            {
+                // Non tank stuff to avoid
+                if (m_tank->GetGUID() != m_bot->GetGUID())
+                {
+                    if (ROOTS && !pTarget->HasAura(CYCLONE) && !pTarget->HasAura(HIBERNATE) && CastSpell(ROOTS, pTarget)) { return; }
+                    if (CYCLONE && pDist > 5 && !pTarget->HasAura(ROOTS) && !pTarget->HasAura(HIBERNATE) && CastSpell(CYCLONE, pTarget)) { return; }
+                    if (HIBERNATE && pTarget->GetCreatureType() == (uint32) CREATURE_TYPE_BEAST && !pTarget->HasAura(ROOTS) && !pTarget->HasAura(CYCLONE) && CastSpell(HIBERNATE, pTarget)) { return; }
+                    //if (m_bot->getRace() == (uint8) RACE_NIGHTELF && isUnderAttack() && CastSpell(R_SHADOWMELD, m_bot)) { return; }
+                }
+                // Things to do wheter Tank or not
+                if (m_bot->getRace() == (uint8) RACE_TAUREN && pDist < 8 && CastSpell(R_WAR_STOMP, pTarget)) { return; } //no gcd
+                if (ai->GetManaPercent() < 20 && CastSpell (INNERVATE, m_bot) ) { return; } //We have little mana probably cant change form
+            }
+            TakePosition(pTarget);
+
+            if (ChangeForm(BEAR_FORM)) { return; }
+
+            // if i am main tank, protect master by taunt
+            if(m_tank->GetGUID() == m_bot->GetGUID())
+            {
+                // Taunt if needed (Only for master)
+                Unit *curAtt = GetAttackerOf(GetMaster());
+                if (curAtt)
+                {
+                    if (isUnderAttack(GetMaster(),2) && CastSpell(CHALLENGING_ROAR, curAtt)) { return; }
+                    if (CastSpell(GROWL, curAtt))  { return; }
+                }
+                // My target is not attacking me, taunt..
+                if (pVictim && pVictim->GetGUID() != m_bot->GetGUID() && CastSpell(GROWL, pTarget) )  { return; }
+            }
+            break;
+        #pragma endregion
+
+        #pragma region BOT_ROLE_DPS_RANGED
+        case BOT_ROLE_DPS_RANGED:
+            if ( ai->GetManaPercent() < 20 && CastSpell (INNERVATE, m_bot)) { return; }
+
+            // Do caster form stuff
+            if (ai->GetForm() == FORM_NONE)
+            {
+                if(DoSupportRaid(GetMaster())) return;
+                else if(m_bot->GetGroup() && GetMaster()->GetGroup() != m_bot->GetGroup() && DoSupportRaid(m_bot)) { return; }
+            }
+
+            if (MOONKIN_FORM) { if (ChangeForm(MOONKIN_FORM)) { return; } }
+            else if (ai->GetForm() != FORM_NONE && ChangeForm(1)) { } //Normal Form
+
+            TakePosition(pTarget);
+
+            // BUFF UP
+            if(DoSupportRaid(GetMaster(),false,false,false)) return;
+            else if(m_bot->GetGroup() && GetMaster()->GetGroup() != m_bot->GetGroup() && DoSupportRaid(m_bot,false,false,false)) { return; }
+
+            break;
+        #pragma endregion
+
+        #pragma region BOT_ROLE_SUPPORT
+        case BOT_ROLE_SUPPORT:
+            if ( ai->GetManaPercent() < 20 && CastSpell (INNERVATE,m_bot)) { return; }
+            //Get to tree form only if you will no longer cast attack spells
+            if( TREE_OF_LIFE_FORM && (ai->GetManaPercent() < offensiveSpellThreshold || isUnderAttack()) )
+            {
+                 if (ChangeForm(TREE_OF_LIFE_FORM)) { return; }
+            }
+            else if (ai->GetForm() != FORM_NONE && ChangeForm(1)) { }  //Normal Form no gcd
+
+            TakePosition(pTarget);
+
+            //RezGroup(REBIRTH, GetMaster());
+            if (DoSupportRaid(GetMaster())) { return; }
+            if (m_bot->GetGroup() && GetMaster()->GetGroup() != m_bot->GetGroup() && DoSupportRaid(m_bot)) { return; }
+            //heal pets and bots
+            Unit *target = DoSelectLowestHpFriendly(30, 1000);
+            if(target && target->isAlive() && HealTarget(target, target->GetHealth()*100 / target->GetMaxHealth()) ) { return; }
+
+            break;
+        #pragma endregion
+    }
+
+    #pragma region DruidCommon
+    // Common Dps and protection routine
+    if (ai->GetHealthPercent() <= 70 && CastSpell(BARKSKIN,m_bot)) { return; }
+    if (isUnderAttack() && CastSpell(NATURES_GRASP,m_bot)) { return; }
+
+    if (ai->GetForm() == FORM_CAT)
+    {
+        // If at threat limit, use Cower to reduce threat
+        if (pThreat > threatThreshold && m_tank->GetGUID() != m_bot->GetGUID() && !isUnderAttack())
+        {
+            if (m_tank->getVictim() && m_tank->getVictim()->GetGUID() != pTarget->GetGUID()) // I am attacking wrong target!!
+            {
+                m_bot->SetSelection(m_tank->getVictim()->GetGUID());
+                return;
+            }
+            else
+            {
+                if (CastSpell(COWER,pTarget)) { return; } //Lets see if we can manage
+                else { return; } //use no spells and wait threat to be reduced
+            }
+        }
+        if (CastSpell(FERAL_CHARGE_CAT,pTarget)) { return; }
+        if (m_bot->GetComboPoints() >= 1 && pTarget->IsNonMeleeSpellCasted(true) && CastSpell(MAIM, pTarget)) { return; }
+
+        if (CastSpell(BERSERK, m_bot)) { return; }
+        if (ai->GetHealthPercent() <= 75 && CastSpell(SURVIVAL_INSTINCTS, m_bot)) { return; }
+        if (isUnderAttack() && CastSpell(NATURES_GRASP, m_bot)) { return; }
+        if (CastSpell(FAERIE_FIRE_FERAL, pTarget)) { return; }
+
+        if (m_bot->GetComboPoints() < 5)
+        {
+            if (CastSpell(RAKE, pTarget)) { return; }
+            if (CastSpell(MANGLE_CAT, pTarget)) { return; }
+            if (!pTarget->HasInArc(M_PI,m_bot) && CastSpell(SHRED, pTarget)) { return; }
+            if (ai->GetEnergyAmount() > 65 && CastSpell(MANGLE_CAT, pTarget)) { return; } //Spam mangle if cannot cast shred
+            if (ai->GetEnergyAmount() > 65 && CastSpell(CLAW, pTarget) ) { return; } //Spam Claw if there is no mangle
+            // if (CanCast(COWER, pTarget) && CastSpell(COWER, pTarget)) { return; } //if still nothing, use COWER to reduce threat
+        }
+        else
+        {
+            if (CastSpell(SAVAGE_ROAR)) { return; }
+            if (CastSpell(RIP, pTarget)) { return; }
+            if (ai->GetEnergyAmount() >= 65 && CastSpell(FEROCIOUS_BITE, pTarget)) { return; } //maxhit for feracious bite
+        }
+        if (CastSpell(TIGERS_FURY, m_bot)) { return; } //if nothing is ready yet, use tigers fury
+    }
+    else if (ai->GetForm() == FORM_DIREBEAR || ai->GetForm() == FORM_BEAR)
+    {
+        // If at threat limit, stop
+        if (pThreat > threatThreshold && m_tank->GetGUID() != m_bot->GetGUID() && !isUnderAttack() )
+        {
+            //Change to tank's target
+            if (m_tank->getVictim() && m_tank->getVictim()->GetGUID() != pTarget->GetGUID()) { m_bot->SetSelection(m_tank->getVictim()->GetGUID()); }
+            return; //use no spells and wait threat to be reduced
+        }
+        if (CastSpell(FERAL_CHARGE_BEAR,pTarget)) { return; }
+        if (CastSpell(BASH, pTarget,true,true)) { return; } //Need check for immunity
+        if (CastSpell(BERSERK, m_bot)) { return; }
+        if (CastSpell(DEMORALIZING_ROAR, pTarget)) { return; }
+        if (ai->GetHealthPercent() > 90 && ai->GetRageAmount() < 50 && CastSpell(ENRAGE, m_bot)) { return; }
+        if (ai->GetHealthPercent() <= 75 && CastSpell(SURVIVAL_INSTINCTS, m_bot)) { return; }
+        if ( ( ai->GetHealthPercent() <= 30 || (ai->GetHealthPercent() < 85 && m_tank->GetGUID() != m_bot->GetGUID())  )
+            && CastSpell(FRENZIED_REGENERATION)) { return; }
+        if (CastSpell(FAERIE_FIRE_FERAL, pTarget)) { return; }
+        if (CastSpell(MANGLE_BEAR, pTarget)) { return; }
+        if ((ai->GetRageAmount() > 70 || m_tank->GetGUID() == m_bot->GetGUID()) && CastSpell(SWIPE_BEAR, pTarget)) { return; }
+        if (ai->GetRageAmount() > 50 && CastSpell(MAUL, pTarget)) {} // Low Priority, Next Attack effect
+        if (ai->GetRageAmount() > 60 && CastSpell(LACERATE, pTarget)) { return; } //Currently applies only 1
+    }
+    else
+    {
+        //Defensive stuff
+        if (m_tank->GetGUID() != m_bot->GetGUID() && pVictim && pVictim->GetGUID() == m_bot->GetGUID() )
+        {
+            if (ROOTS && !pTarget->HasAura(CYCLONE) && !pTarget->HasAura(HIBERNATE) && CastSpell(ROOTS, pTarget)) { return; }
+            if (CYCLONE && pDist > 5 && !pTarget->HasAura(ROOTS) && !pTarget->HasAura(HIBERNATE) && CastSpell(CYCLONE, pTarget)) { return; }
+            if (HIBERNATE && pTarget->GetCreatureType() == (uint32) CREATURE_TYPE_BEAST && !pTarget->HasAura(ROOTS) && !pTarget->HasAura(CYCLONE) && CastSpell(HIBERNATE, pTarget)) { return; }
+            //if (m_bot->getRace() == (uint8) RACE_NIGHTELF && isUnderAttack() && CastSpell(R_SHADOWMELD, m_bot)) { return; }
+            if (m_bot->getRace() == (uint8) RACE_TAUREN && pDist < 8 && CastSpell(R_WAR_STOMP, pTarget)) { return; }
+        }
+
+        if (CastSpell(FAERIE_FIRE, pTarget)) { return; }
+
+        // If at threat limit, stop
+        if (pThreat > threatThreshold && m_tank->GetGUID() != m_bot->GetGUID() && !isUnderAttack() )
+        {
+            //Change to tank's target
+            if (m_tank->getVictim() && m_tank->getVictim()->GetGUID() != pTarget->GetGUID()) { m_bot->SetSelection(m_tank->getVictim()->GetGUID()); }
+            return; //use no spells and wait threat to be reduced
+        }
+        // Continue attacking if theres excess mana (for healers)
+        if (m_role == BOT_ROLE_SUPPORT && ai->GetManaPercent() < offensiveSpellThreshold) { return; }
+        if (m_role != BOT_ROLE_SUPPORT && CastSpell(NATURES_SWIFTNESS, m_bot)) { } //only balance no gcd
+
+        if (m_bot->HasAura(NATURES_SWIFTNESS) && CastSpell(STARFIRE, pTarget)) { return; }
+        if (CastSpell(INSECT_SWARM, pTarget)) { return; }
+        if (CastSpell(TYPHOON, pTarget)) { return; }
+        if (isUnderAttack(m_tank,4) && CastSpell(HURRICANE, pTarget)) { ai->SetIgnoreUpdateTime(8); return; }
+        if (isUnderAttack(m_tank,5) && CastSpell(FORCE_OF_NATURE, m_bot)) { return; }
+        if (isUnderAttack(m_tank,4) && CastSpell(STARFALL, pTarget)) { return; }
+        if (CastSpell(MOONFIRE, pTarget)) { return; }
+        if (CastSpell(WRATH, pTarget)) { return; }
+        if (CastSpell(STARFIRE, pTarget)) { return; }
+    }
+
+    // If there is nothing else to do buff UP
+    if (m_role == BOT_ROLE_DPS_MELEE) //Those already healed and buffed or should never buff in combat
+    {
+        if (DoSupportRaid(GetMaster(),false,false,false)) { return; }
+        if (m_bot->GetGroup() && GetMaster()->GetGroup() != m_bot->GetGroup() && DoSupportRaid(m_bot,false,false,false)) { return; }
+    }
+
+
+    // drink potion if support / healer (Other builds simply overuse mana and waste mana pots)
+    if(ai->GetManaPercent() < 5 && (m_role == BOT_ROLE_SUPPORT || m_role == BOT_ROLE_HEALER) )
+    {
+        Item *pItem = ai->FindPotion();
+        if(pItem != NULL)
+        {
+            if (pItem->GetSpell() && m_bot->HasSpellCooldown(pItem->GetSpell()) ) { return; } //pot is in cooldown
+            ai->UseItem(*pItem);
+        }
+    }
+    #pragma endregion
+} //end DoNextCombatManeuver
+
+void PlayerbotDruidAI::DoNonCombatActions()
+{
+    PlayerbotAI *ai = GetAI();
+    Player *m_bot = GetPlayerBot();
+    if (!m_bot || !ai || m_bot->isDead()) { return; }
+
+    //If Casting or Eating/Drinking return
+    if (m_bot->HasUnitState(UNIT_STAT_CASTING)) { return; }
+    if (m_bot->getStandState() == UNIT_STAND_STATE_SIT) { return; }
+
+    //buff and heal raid
+    if (DoSupportRaid(GetMaster())) { return; }
+    if (m_bot->GetGroup() && GetMaster()->GetGroup() != m_bot->GetGroup() && DoSupportRaid(m_bot)) { return; }
+
+    //heal pets and bots
+    Unit *target = DoSelectLowestHpFriendly(30, 1000);
+    if (target && target->isAlive() && HealTarget(target, target->GetHealth()*100 / target->GetMaxHealth())) { return; }
+
+    //mana/hp check
+    //Don't bother with eating, if low on hp, just let it heal themself
+    if (m_bot->getRace() == (uint8) RACE_UNDEAD_PLAYER && ai->GetHealthPercent() < 75 && CastSpell(R_CANNIBALIZE,m_bot)) { return; }
+    if (ai->GetManaPercent() < 10 && CastSpell (INNERVATE, m_bot)) { return; } //Need mana fast
+    if (m_bot->GetHealth() < m_bot->GetMaxHealth() &&
+        (ai->GetForm() != FORM_CAT && ai->GetForm() != FORM_MOONKIN && ai->GetForm() != FORM_DIREBEAR && ai->GetForm() != FORM_BEAR)
+        && CastSpell(REGROWTH,m_bot)) { return; }
+    if (ai->GetManaPercent() < 50) { ai->Feast(); }
+
+} //end DoNonCombatActions
+
+bool PlayerbotDruidAI::BuffPlayer(Unit *target)
+{
+    PlayerbotAI *ai = GetAI();
+    Player *m_bot = GetPlayerBot();
+    if (!m_bot || !ai || m_bot->isDead()) { return false; }
+
+    if(!target || target->isDead()) { return false; }
+
+    if (CanCast(THORNS,target,0,0,1) && !HasAuraName(target, THORNS)) {
+        // Decide if it is worth to change form
+        if( /*m_bot->HasAura(MOONKIN_FORM) ||*/ m_bot->HasAura(CAT_FORM) || m_bot->HasAura(BEAR_FORM))
+        {
+            if(GetAI()->GetManaPercent() >= 80 ) { ChangeForm(1); }
+            else { return false; }
+        }
+        return CastSpell(THORNS, target, false);
+    }
+    if (CanCast(MARK_OF_THE_WILD,target,0,0,1) && !HasAuraName(target, GIFT_OF_THE_WILD) && !HasAuraName(target, MARK_OF_THE_WILD)) {
+        // Decide if it is worth to change form
+        if(/*m_bot->HasAura(MOONKIN_FORM) ||*/ m_bot->HasAura(CAT_FORM) || m_bot->HasAura(BEAR_FORM))
+        {
+            if(GetAI()->GetManaPercent() >= 70 ) { ChangeForm(1); }
+            else return false;
+        }
+        return CastSpell(MARK_OF_THE_WILD, target, false);
+    }
+    return false;
+}
+
+bool PlayerbotDruidAI::HealTarget(Unit *target, uint8 hp)
+{
+    PlayerbotAI *ai = GetAI();
+    Player *m_bot = GetPlayerBot();
+    if (!m_bot || !ai || m_bot->isDead()) { return false; }
+    if (!target || target->isDead()) { return false; }
+
+    // Decide if it is worth to change form
+    if (ai->GetForm() == FORM_MOONKIN || ai->GetForm() == FORM_CAT || ai->GetForm() == FORM_DIREBEAR || ai->GetForm() == FORM_BEAR)
+    {
+        if (hp < 75 && GetAI()->GetManaPercent() >= 70 ) { ChangeForm(1); }
+        else if (hp < 40 && GetAI()->GetManaPercent() >= 50) { ChangeForm(1); }
+        else if (hp < 25 && GetAI()->GetManaPercent() >= 30) { ChangeForm(1); }
+        else return false;
+    }
+
+    // if(m_bot->HasAura(TRAVEL_FORM)) ChangeForm(1);
+
+    if(hp < 60 && m_bot->HasAura(NATURES_SWIFTNESS) && CastSpell(HEALING_TOUCH, target)) { return true; }
+    if(hp < 90 && CastSpell(LIFEBLOOM, target)) { return true; }
+    if(hp < 80 && CastSpell(REJUVENATION, target)) { return true; }
+    if(hp < 60 && CastSpell(REGROWTH, target)) { return true; }
+    if(hp < 70 && CanCast(NOURISH,target) &&
+        (HasAuraName(target,REJUVENATION,m_bot->GetGUID()) || HasAuraName(target,LIFEBLOOM,m_bot->GetGUID()) || HasAuraName(target,REGROWTH,m_bot->GetGUID()))
+        ) { return CastSpell(NOURISH, target, false); }
+    if(hp < 50 && CanCast(SWIFTMEND,target) &&
+        (HasAuraName(target,REJUVENATION,m_bot->GetGUID()) || HasAuraName(target,REGROWTH,m_bot->GetGUID()))
+        ) { return CastSpell(SWIFTMEND, target, false); }
+    if(hp < 40 && m_bot->isInCombat() && CastSpell(NATURES_SWIFTNESS, m_bot)) { } // NO gcd
+    if(hp < 40 && CastSpell(HEALING_TOUCH, target)) { return true; }
+    return false;
+} //end HealTarget
+
+bool PlayerbotDruidAI::HealGroup(Unit *target, uint8 hp, uint8 &countNeedHeal)
+{
+    PlayerbotAI *ai = GetAI();
+    Player *m_bot = GetPlayerBot();
+    if (!m_bot || !ai || m_bot->isDead()) { return false; }
+    if (!target || target->isDead()) { return false; }
+
+    if (countNeedHeal < 2) { return false; }
+
+    // Decide if it is worth to change form
+    if (ai->GetForm() == FORM_MOONKIN || ai->GetForm() == FORM_CAT || ai->GetForm() == FORM_DIREBEAR || ai->GetForm() == FORM_BEAR)
+    {
+        if (hp > 70) { return false; }
+        if (!CanCast(TRANQUILITY,target,0,0,1) && !WILD_GROWTH) { return false; }
+        if (!WILD_GROWTH && hp > 35) { return false; }
+        if (hp < 65 && GetAI()->GetManaPercent() >= 70 ) { ChangeForm(1); }
+        else if (hp < 40 && GetAI()->GetManaPercent() >= 50) { ChangeForm(1); }
+        else if (hp < 25 && GetAI()->GetManaPercent() >= 30) { ChangeForm(1); }
+        else return false;
+    }
+
+    if (hp < 36 && m_bot->isInCombat() && CanCast(TRANQUILITY,target))
+    {
+            bool sc = CastSpell(TRANQUILITY, target, false);
+            if (sc) GetAI()->SetIgnoreUpdateTime(10);
+            return sc;
+    }
+    if (hp < 75 && CastSpell(WILD_GROWTH,target)) { return true; }
+    return false;
+}
+
+bool PlayerbotDruidAI::CureTarget(Unit *target)
+{
+    PlayerbotAI *ai = GetAI();
+    Player *m_bot = GetPlayerBot();
+    if (!m_bot || !ai || m_bot->isDead()) { return false; }
+    if (!target || target->isDead()) { return false; }
+    // Decide if it is worth to change form (they cange forms even if theres nothing to cure..)
+    if (ai->GetForm() == FORM_CAT || ai->GetForm() == FORM_DIREBEAR || ai->GetForm() == FORM_BEAR)
+    {
+        //if(GetAI()->GetManaPercent() >= 80 ) { ChangeForm(1); }
+        //else { return false; }
+        return false;
+    }
+    if (castDispel(CURE_POISON, target)) { return true; }
+    // if(HasAuraName(target, "Venom Spit") || HasAuraName(target, "Poison")) return CastSpell(CURE_POISON, target);
+
+    return false;
+} //end HealTarget
+
+bool PlayerbotDruidAI::RezTarget (Unit *target)
+{
+    if(!target || target->isAlive()) return false;
+    Player *m_bot = GetPlayerBot();
+    if (target->IsNonMeleeSpellCasted(true)) { return false; } //Already resurrected
+
+    if (m_bot->isInCombat())
+    {
+        if (!CanCast(REBIRTH,target)) return false;
+        Unit *m_tank = FindMainTankInRaid(m_bot);
+        if (!m_tank) m_tank = m_bot;
+        if (target->GetGUID() != m_tank->GetGUID() &&
+            (target->getClass() != (uint8) CLASS_PRIEST || target->getClass() != (uint8) CLASS_DRUID || target->getClass() != (uint8) CLASS_PALADIN) ) return false;
+        std::string msg = "Rezzing ";
+        msg += target->GetName();
+       // msg += " with ";
+       // msg += *REZZSpell->SpellName;
+        GetPlayerBot()->Say(msg, LANG_UNIVERSAL);
+        return CastSpell(REBIRTH, target);
+    }
+    else
+    {
+        if (!CanCast(REVIVE,target)) return false;
+        std::string msg = "Rezzing ";
+        msg += target->GetName();
+       // msg += " with ";
+       // msg += *REZZSpell->SpellName;
+        GetPlayerBot()->Say(msg, LANG_UNIVERSAL);
+        return CastSpell(REVIVE, target);
+    }
+    return false;
+}
+
+/*bool PlayerbotDruidAI::FindMount() {
+    if (TRAVEL_FORM) {
+        if (GetPlayerBot())    CastSpell(TRAVEL_FORM, GetPlayerBot());
+        return true;
+    } else return false;
+}
+
+bool PlayerbotDruidAI::Unmount() {
+    GetPlayerBot()->RemoveAurasDueToSpell(TRAVEL_FORM);
+    return true;
+}
+
+bool PlayerbotDruidAI::IsMounted() {
+    return GetPlayerBot()->IsMounted() || HasAuraName(GetPlayerBot(), TRAVEL_FORM);
+} */
+
+bool PlayerbotDruidAI::ChangeForm(uint32 form)
+{
+    PlayerbotAI *ai = GetAI();
+    Player *m_bot = GetPlayerBot();
+    if (!m_bot || !ai || m_bot->isDead()) { return false; }
+    if (!form) return false;
+
+    if (form == 1 && ai->GetForm() == FORM_NONE) return false;
+
+    if (form != 1)
+    {
+        if (!CanCast(form,m_bot,0,0,1)) return false;
+        if (m_bot->HasAura(form)) { return false; }
+    }
+
+    if (ai->GetForm() == FORM_TREE) m_bot->RemoveAurasDueToSpell(TREE_OF_LIFE_FORM);
+    else if (ai->GetForm() == FORM_CAT) m_bot->RemoveAurasDueToSpell(CAT_FORM);
+    else if (ai->GetForm() == FORM_MOONKIN) m_bot->RemoveAurasDueToSpell(MOONKIN_FORM);
+    else if (ai->GetForm() == FORM_DIREBEAR || ai->GetForm() == FORM_BEAR) m_bot->RemoveAurasDueToSpell(BEAR_FORM);
+    else if (ai->GetForm() == FORM_TRAVEL) m_bot->RemoveAurasDueToSpell(TRAVEL_FORM);
+    else if (ai->GetForm() == FORM_FLIGHT || ai->GetForm() == FORM_FLIGHT_EPIC)    m_bot->RemoveAurasDueToSpell(FLIGHT_FORM);
+    else if (ai->GetForm() == FORM_AQUA) m_bot->RemoveAurasDueToSpell(AQUATIC_FORM);
+
+    if (form == 1) { return true; }
+
+    return CastSpell(form,m_bot,false);
+}
diff --git a/src/server/game/AI/Bots/PlayerbotDruidAI.h b/src/server/game/AI/Bots/PlayerbotDruidAI.h
new file mode 100644
index 0000000..35f4d54
--- /dev/null
+++ b/src/server/game/AI/Bots/PlayerbotDruidAI.h
@@ -0,0 +1,70 @@
+#ifndef _PLAYERBOTDRUIDAI_H
+#define _PLAYERBOTDRUIDAI_H
+
+#include "PlayerbotClassAI.h"
+
+class PlayerbotDruidAI : PlayerbotClassAI
+{
+    public:
+        PlayerbotDruidAI(Player *const master, Player *const bot, PlayerbotAI *const ai);
+        virtual ~PlayerbotDruidAI();
+
+        virtual void LoadSpells();
+
+        //all combat actions go here
+        void DoNextCombatManeuver(Unit *);
+
+        //all non combat actions go here, ex buffs, heals, rezzes
+        void DoNonCombatActions();
+
+        //buff a specific player, usually a real PC who is not in group
+        bool BuffPlayer(Unit *target);
+
+        //Heals the target based off its HP
+        bool HealTarget(Unit *target, uint8 hp);
+
+        bool HealGroup(Unit *target, uint8 hp, uint8 &countNeedHeal);
+
+        //Cures the target
+        bool CureTarget(Unit *target);
+
+        bool RezTarget(Unit *target);
+
+        // find any specific mount spells, ie druids=cat, shaman=ghost wolf etc
+        /* virtual bool FindMount();
+        virtual bool Unmount();
+        virtual bool IsMounted(); */
+
+        //Change Form
+        bool ChangeForm(uint32 form);
+
+    private:
+
+        // BALANCE Attacks
+        uint32 MOONFIRE, WRATH, STARFALL, STARFIRE, TYPHOON, HURRICANE, FORCE_OF_NATURE, INSECT_SWARM, CYCLONE, ROOTS, NATURES_GRASP, HIBERNATE, FAERIE_FIRE;
+
+        // RESTORATION Spells
+        uint32 LIFEBLOOM, REJUVENATION, REGROWTH, NOURISH, SWIFTMEND, HEALING_TOUCH, NATURES_SWIFTNESS, INNERVATE, WILD_GROWTH, TRANQUILITY, REBIRTH, REVIVE, CURE_POISON, BARKSKIN;
+
+        // BEAR SPELLS
+        uint32 MAUL, BASH, LACERATE, MANGLE_BEAR, SWIPE_BEAR, DEMORALIZING_ROAR, GROWL, CHALLENGING_ROAR , ENRAGE, FERAL_CHARGE_BEAR, FRENZIED_REGENERATION;
+
+        // CAT SPELLS
+        uint32 CLAW, RAKE, SHRED, MANGLE_CAT, RIP, FEROCIOUS_BITE, SAVAGE_ROAR, MAIM, FERAL_CHARGE_CAT, COWER, TIGERS_FURY;
+
+        // FERAL General
+        uint32 BERSERK, FAERIE_FIRE_FERAL;
+
+        // BUFFS
+        uint32 MARK_OF_THE_WILD, GIFT_OF_THE_WILD, THORNS, SURVIVAL_INSTINCTS;
+
+        // FORMS
+        uint32 CAT_FORM, BEAR_FORM, MOONKIN_FORM, TREE_OF_LIFE_FORM, TRAVEL_FORM, FLIGHT_FORM, AQUATIC_FORM;
+
+        // Key TALENT SPELLS
+        uint32 TALENT_BALANCE, TALENT_RESTO, TALENT_FERAL;
+
+};
+
+
+#endif
diff --git a/src/server/game/AI/Bots/PlayerbotHunterAI.cpp b/src/server/game/AI/Bots/PlayerbotHunterAI.cpp
new file mode 100644
index 0000000..06c5bc9
--- /dev/null
+++ b/src/server/game/AI/Bots/PlayerbotHunterAI.cpp
@@ -0,0 +1,561 @@
+/*
+Name : PlayerbotHunterAI.cpp
+Complete: maybe around 70%
+
+Limitations:    - Talent build decision is made by key talent spells, which makes them viable only after level 50-ish.. Behaviour does not change though..
+                - AI always assumes pet is the tank if there are no higher hp people in group than the hunter..
+                - Possible threat build / reduce race between pet and hunter if attacking to same target.. Needs checking
+                - Possible target changing loop between pet and hunter if attacking to same target and getting aggro repeatedly.. Needs checking
+                - Disarm and Nature resist aspect, Disengage, Scorpid sting are not used right now..
+
+Authors : SwaLLoweD
+Version : 0.40
+*/
+
+#include "PlayerbotHunterAI.h"
+
+
+class PlayerbotAI;
+
+PlayerbotHunterAI::PlayerbotHunterAI(Player* const master, Player* const bot, PlayerbotAI* const ai): PlayerbotClassAI(master, bot, ai)
+{
+    m_petSummonFailed = false;
+    LoadSpells();
+}
+
+PlayerbotHunterAI::~PlayerbotHunterAI() {}
+
+void PlayerbotHunterAI::LoadSpells() {
+    PlayerbotAI *ai = GetAI();
+    if (!ai) return;
+    #pragma region SpellId Fill
+    // PET CONTROL
+    PET_SUMMON = ai->getSpellIdExact("Call Pet");
+    PET_DISMISS = ai->getSpellIdExact("Dismiss Pet");
+    PET_REVIVE = ai->getSpellIdExact("Revive Pet");
+    PET_MEND = ai->getSpellIdExact("Mend Pet");
+    PET_FEED = 1539; //ai->getSpellIdExact("Feed Pet");
+    KILL_COMMAND = ai->getSpellIdExact("Kill Command");
+    INTIMIDATION = ai->getSpellIdExact("Intimidation");
+    BESTIAL_WRATH = ai->getSpellIdExact("Bestial Wrath");
+
+    // PET SPELL (master does not have these spells anymore)
+    GROWL = ai->getSpellIdExact("Growl");
+    COWER = ai->getSpellIdExact("Cower");
+    BAD_ATTITUDE = ai->getSpellIdExact("Bad Attitude");
+    SONIC_BLAST = ai->getSpellIdExact("Sonic Blast");
+    NETHER_SHOCK = ai->getSpellIdExact("Nether Shock");
+    DEMORALIZING_SCREECH = ai->getSpellIdExact("Demoralizing Screech");
+
+    // RANGED ATTACK
+    AUTO_SHOT = ai->getSpellIdExact("Auto Shot");
+    ARCANE_SHOT = ai->getSpellIdExact("Arcane Shot");
+    EXPLOSIVE_SHOT = ai->getSpellIdExact("Explosive Shot");
+    STEADY_SHOT = ai->getSpellIdExact("Steady Shot");
+    AIMED_SHOT = ai->getSpellIdExact("Aimed Shot");
+    SCATTER_SHOT = ai->getSpellIdExact("Scatter Shot");
+    KILL_SHOT = ai->getSpellIdExact("Kill Shot");
+    CHIMERA_SHOT = ai->getSpellIdExact("Chimera Shot");
+    CONCUSSIVE_SHOT = ai->getSpellIdExact("Concussive Shot");
+    DISTRACTING_SHOT = ai->getSpellIdExact("Distracting Shot");
+    SILENCING_SHOT = ai->getSpellIdExact("Silencing Shot");
+
+    // STINGS
+    SERPENT_STING = ai->getSpellIdExact("Serpent Sting");
+    SCORPID_STING = ai->getSpellIdExact("Scorpid Sting");
+    WYVERN_STING = ai->getSpellIdExact("Wyvern Sting");
+    VIPER_STING = ai->getSpellIdExact("Viper Sting");
+
+    // DEBUFF
+    HUNTERS_MARK = ai->getSpellIdExact("Hunter's Mark");
+    SCARE_BEAST = ai->getSpellIdExact("Scare Beast");
+
+    //AOE
+    VOLLEY = ai->getSpellIdExact("Volley");
+    MULTI_SHOT = ai->getSpellIdExact("Multi Shot");
+
+    //MELEE
+    RAPTOR_STRIKE = ai->getSpellIdExact("Raptor Strike");
+    WING_CLIP = ai->getSpellIdExact("Wing Clip");
+    MONGOOSE_BITE = ai->getSpellIdExact("Mongoose Bite");
+    COUNTERATTACK = ai->getSpellIdExact("Counterattack");
+
+    //TRAP
+    FREEZING_TRAP = ai->getSpellIdExact("Freezing Trap");
+    IMMOLATION_TRAP = ai->getSpellIdExact("Immolation Trap");
+    FROST_TRAP = ai->getSpellIdExact("Frost Trap");
+    EXPLOSIVE_TRAP = ai->getSpellIdExact("Explosive Trap");
+    SNAKE_TRAP = ai->getSpellIdExact("Snake Trap");
+    ARCANE_TRAP = ai->getSpellIdExact("Arcane Trap");
+    FREEZING_ARROW = ai->getSpellIdExact("Freezing Arrow");
+    BLACK_ARROW = ai->getSpellIdExact("Black Arrow");
+
+    //BUFF
+    TRUESHOT_AURA = ai->getSpellIdExact("Trueshot Aura");
+    DETERRENCE = ai->getSpellIdExact("Deterrence");
+    FEIGN_DEATH = ai->getSpellIdExact("Feign Death");
+    DISENGAGE = ai->getSpellIdExact("Disengage");
+    RAPID_FIRE = ai->getSpellIdExact("Rapid Fire");
+    READINESS = ai->getSpellIdExact("Readiness");
+    MISDIRECTION = ai->getSpellIdExact("Misdirection");
+
+    //ASPECT
+    ASPECT_OF_THE_HAWK = ai->getSpellIdExact("Aspect of the Dragonhawk");
+    ASPECT_OF_THE_MONKEY = ASPECT_OF_THE_HAWK;
+    if (!ASPECT_OF_THE_HAWK) ASPECT_OF_THE_HAWK = ai->getSpellIdExact("Aspect of the Hawk");
+    if (!ASPECT_OF_THE_MONKEY) ASPECT_OF_THE_MONKEY = ai->getSpellIdExact("Aspect of the Monkey");
+    ASPECT_OF_THE_VIPER = ai->getSpellIdExact("Aspect of the Viper");
+
+    TALENT_MM = TRUESHOT_AURA;
+    TALENT_BM = BESTIAL_WRATH;
+    TALENT_SURVIVAL = WYVERN_STING;
+
+    uint8 talentCounter = 0;
+    if (TALENT_MM) talentCounter++;
+    if (TALENT_BM) talentCounter++;
+    if (TALENT_SURVIVAL) talentCounter++;
+    if (talentCounter > 1) { TALENT_MM = 0; TALENT_BM = 0; TALENT_SURVIVAL = 0; } //Unreliable Talent detection.
+
+    #pragma endregion
+}
+
+bool PlayerbotHunterAI::HasPet(Player* bot)
+{
+    QueryResult result = CharacterDatabase.PQuery("SELECT * FROM character_pet WHERE owner = '%u' AND (slot = '%u' OR slot = '%u')",bot->GetGUIDLow(),PET_SAVE_AS_CURRENT,PET_SAVE_NOT_IN_SLOT);
+
+    if(result)
+        return true; //hunter has current pet
+    else
+        return false; //hunter either has no pet or stabled
+}// end HasPet
+
+void PlayerbotHunterAI::DoNextCombatManeuver(Unit *pTarget)
+{
+    if (!pTarget || pTarget->isDead()) return;
+    PlayerbotAI *ai = GetAI();
+    if (!ai) return;
+    Player *m_bot = GetPlayerBot();
+    if (!m_bot || m_bot->isDead()) return;
+    Unit *pVictim = pTarget->getVictim();
+    Unit *m_tank = FindMainTankInRaid(GetMaster());
+    if (!m_tank && m_bot->GetGroup() && GetMaster()->GetGroup() != m_bot->GetGroup()) { FindMainTankInRaid(m_bot); }
+    if (!m_tank) { m_tank = m_bot; }
+    uint32 masterHP = GetMaster()->GetHealth()*100 / GetMaster()->GetMaxHealth();
+    float pDist = m_bot->GetDistance(pTarget);
+    uint8 pThreat = GetThreatPercent(pTarget);
+
+    Pet *pet = m_bot->GetPet();
+    if (m_tank->GetGUID() == m_bot->GetGUID() && pet && pet->isAlive() && pet->isInCombat()) { m_tank = pet; }
+    uint8 petThreat = 0;
+    if (pet) { GetThreatPercent(pTarget,pet); }
+
+ //   switch (ai->GetScenarioType())
+//    {
+//        case PlayerbotAI::SCENARIO_DUEL:
+ //           ai->CastSpell(RAPTOR_STRIKE);
+ //           return;
+//    }
+
+    // ------- Non Duel combat ----------
+
+
+    #pragma region Choose Target
+    // Choose Target
+    if (isUnderAttack()) // I am under attack
+    {
+        if (pVictim && pVictim->GetGUID() == m_bot->GetGUID() && pDist <= 2) {  } // My target is almost up to me, no need to search
+        else //Have to select nearest target
+        {
+            Unit *curAtt = GetNearestAttackerOf(m_bot);
+            if (curAtt && curAtt->GetGUID() != pTarget->GetGUID())
+            {
+                m_bot->SetSelection(curAtt->GetGUID());
+                //ai->AddLootGUID(curAtt->GetGUID());
+                DoNextCombatManeuver(curAtt); //Restart new update to get variables fixed..
+                return;
+            }
+        }
+        //my target is attacking me
+    }
+
+    #pragma endregion
+
+    #pragma region Pet Actions
+    // Pet's own Actions
+    if( pet && pet->isAlive() )
+    {
+        // Setup pet
+        if (pet->GetCharmInfo()->IsAtStay()) {pet->GetCharmInfo()->SetCommandState(COMMAND_FOLLOW); }
+
+        //Heal pet
+        if ( ( ((float)pet->GetHealth()/(float)pet->GetMaxHealth()) < 0.5f )
+        && ( PET_MEND>0 && !pet->getDeathState() != ALIVE && pVictim != m_bot
+        && CastSpell(PET_MEND,m_bot) )) { return; }
+
+        // Set pet to attack hunter's attacker > its own attackers > hunter's target
+        if (!pet->getVictim()) { pet->AI()->AttackStart(pTarget); }
+        else if (isUnderAttack(m_bot)) { pet->AI()->AttackStart(pTarget); }  //Always help hunter if she's under attack
+        else if (pet->getVictim()->GetGUID() != pTarget->GetGUID() && !isUnderAttack(pet)) { pet->AI()->AttackStart(pTarget); }
+        else if (isUnderAttack(pet)) // Pet is under attack and hunter has no attackers
+        {
+            if ( pet->getVictim()->getVictim() && pet->getVictim()->getVictim()->GetGUID() == pet->GetGUID() && pDist <= 2) {  } // My target is almost up to me, no need to search
+            else //Have to select nearest target
+            {
+                Unit *curAtt = GetNearestAttackerOf(pet,true);
+                if (curAtt && (!pet->getVictim() || curAtt->GetGUID() != pet->getVictim()->GetGUID()))
+                {
+                    pet->AI()->AttackStart(curAtt); //Attack nearest attacker
+                }
+            }
+            //Actions to do under attack (Always tank it, and try to kill it, until someone (!= hunter) takes aggro back)
+            //Hunter should help her pet whether main tank or not, unless she's being attacked (BEWARE Targeting Loop possibility)
+            if (pet->getVictim() && !isUnderAttack(m_bot) && pet->getVictim()->GetGUID() != pTarget->GetGUID())
+            {
+                m_bot->SetSelection(pet->getVictim()->GetGUID());
+                DoNextCombatManeuver(pet->getVictim()); //Restart new update to get variables fixed..
+                return;
+            }
+
+        }
+        // Pet tanking behaviour
+        if (pet->GetGUID() == m_tank->GetGUID() || isUnderAttack(m_bot) || isUnderAttack(pet))
+        {
+            if (GROWL) pet->GetCharmInfo()->SetSpellAutocast(GROWL,true); //Autocast growl
+            if (BAD_ATTITUDE) pet->GetCharmInfo()->SetSpellAutocast(BAD_ATTITUDE,true);
+            if (COWER) pet->GetCharmInfo()->SetSpellAutocast(COWER,false);
+            if (CastSpell(INTIMIDATION,m_bot)) { return; }
+        }
+        else
+        {
+            if (GROWL) pet->GetCharmInfo()->SetSpellAutocast(GROWL,false); //Do not try to get aggro
+            if (BAD_ATTITUDE) pet->GetCharmInfo()->SetSpellAutocast(BAD_ATTITUDE,false);
+            if (COWER) pet->GetCharmInfo()->SetSpellAutocast(COWER,true); //Autocast cower
+        }
+        // NORMAL PET dps attacks
+        if (petThreat < threatThreshold || pet->GetGUID() == m_tank->GetGUID() || isUnderAttack(m_bot))
+        {
+            if (CastSpell(KILL_COMMAND,m_bot)) { }
+            else if (CastSpell(BESTIAL_WRATH,m_bot)) { }
+        }
+        // NETHERSHOCK DEMORALIZINGSCREECH
+    }
+    #pragma endregion
+
+    // If there's a cast stop
+    if(m_bot->HasUnitState(UNIT_STAT_CASTING)) return;
+
+    // Cast CC breakers if any match found  (does not work yet)
+    // uint32 ccSpells[4] = { R_ESCAPE_ARTIST, R_EVERY_MAN_FOR_HIMSELF, R_WILL_OF_FORSAKEN, R_STONEFORM };
+    // if (castSelfCCBreakers(ccSpells)) { } //most of them dont have gcd
+
+    #pragma region Evasive manuevers
+    // Do evasive manuevers if under attack
+    if (isUnderAttack())
+    {
+        if (m_tank->GetGUID() == m_bot->GetGUID()) { } // i am tank and my pet is probably dead, so i have to face the attackers
+        else if (CastSpell(FEIGN_DEATH,m_bot)) { return; } //avoid attack
+        //else if (m_bot->getRace() == (uint8) RACE_NIGHTELF && CastSpell(R_SHADOWMELD,m_bot) ) { return; }
+        else if (CastSpell(CONCUSSIVE_SHOT,pTarget)) { return; }
+        else if (CastSpell(WYVERN_STING,pTarget)) { return; }
+        else if (CastSpell(SCATTER_SHOT,pTarget)) { return; }
+        else if (CastSpell(FREEZING_ARROW,pTarget)) { return; }
+        else if (CastSpell(MISDIRECTION,m_tank)) { return; }
+        else if (m_bot->getRace() == (uint8) RACE_TAUREN && pDist < 8 && CastSpell(R_WAR_STOMP, pTarget) ) { return; } //no gcd but is cast
+        else if (pTarget->GetCreatureType() == (uint32) CREATURE_TYPE_BEAST && CastSpell(SCARE_BEAST,pTarget)) { return; }
+        else if (pDist <= 2 && CastSpell(FREEZING_TRAP,pTarget)) { return; }
+    }
+    #pragma endregion
+
+    //Select combat mode
+    m_role = BOT_ROLE_DPS_RANGED;
+    if ((isUnderAttack()  && pDist <= ATTACK_DISTANCE) || !m_bot->GetUInt32Value(PLAYER_AMMO_ID) ) { m_role = BOT_ROLE_DPS_MELEE; }
+
+    TakePosition(pTarget);
+
+    #pragma region Buff / Protect
+    //Buff UP
+    if (m_bot->getRace() == (uint8) RACE_TROLL && CastSpell(R_BERSERKING,m_bot) ) {  } //no GCD
+    if (m_bot->getRace() == (uint8) RACE_ORC && CastSpell(R_BLOOD_FURY,m_bot) ) { } //no GCD
+    if (CastSpell(TRUESHOT_AURA, m_bot)) { return; }
+    if (CastSpell(RAPID_FIRE,m_bot)) { return; }
+    if (CastSpell(HUNTERS_MARK,pTarget)) { return; }
+    if ((ai->GetHealthPercent() < 80 || ai->GetManaPercent() < 60 ) && CastSpell(READINESS,m_bot)) { } //no gcd
+
+
+    //Protect yourself if needed
+    if (m_bot->getRace() == (uint8) RACE_DWARF && ai->GetHealthPercent() < 75 && CastSpell(R_STONEFORM,m_bot) ) { } //no gcd
+    if (ai->GetHealthPercent() < 20 && CastSpell(DETERRENCE,m_bot)) {} //No GCD
+    if (m_bot->getRace() == (uint8) RACE_DRAENEI && ai->GetHealthPercent() < 55 && CastSpell(R_GIFT_OF_NAARU,m_bot)) { return;  }
+
+    //Break Spells
+    if (m_bot->getRace() == (uint8) RACE_BLOODELF && pDist < 8 && ( pTarget->IsNonMeleeSpellCasted(true) || ai->GetManaPercent() < 20 ) && CastSpell(R_ARCANE_TORRENT, pTarget) ) { } //no gcd
+    if (pTarget->IsNonMeleeSpellCasted(true) && CastSpell(SILENCING_SHOT, pTarget) ) { return; }
+    if (pTarget->IsNonMeleeSpellCasted(true) && CastSpell(SCATTER_SHOT, pTarget) ) { return; }
+
+    //Catch
+    if (pTarget->HasUnitMovementFlag(UNIT_FLAG_FLEEING))
+    {
+        if (CastSpell(WING_CLIP,pTarget)) return;
+        if (CastSpell(CONCUSSIVE_SHOT,pTarget)) return;
+        if (CastSpell(SCATTER_SHOT, pTarget) ) { return; }
+    }
+    #pragma endregion
+
+    //Do combat
+    switch (m_role)
+    {
+        #pragma region BOT_ROLE_DPS_MELEE
+        case BOT_ROLE_DPS_MELEE:
+            if (AUTO_SHOT) { m_bot->InterruptNonMeleeSpells( true, AUTO_SHOT ); } //Stop autoshot
+            if (CastSpell(ASPECT_OF_THE_MONKEY,m_bot)) { return; } //Get Monkey aspect
+
+            if (m_bot->getRace() == (uint8) RACE_TAUREN && pDist < 8 && CastSpell(R_WAR_STOMP, pTarget)) { return; } //no gcd but is cast
+
+            // Threat control
+            if (pThreat < threatThreshold || m_tank->GetGUID() == m_bot->GetGUID() || m_bot->HasAura(MISDIRECTION) ) { } //Continue attack
+            else
+            {
+                if (pet && isUnderAttack(pet) && pet->getVictim() && pet->getVictim()->GetGUID() != pTarget->GetGUID()) //Should be helping pet
+                {
+                    m_bot->SetSelection(pet->getVictim()->GetGUID());
+                    return;
+                }
+                else if (m_tank->getVictim() && m_tank->getVictim()->GetGUID() != pTarget->GetGUID()) // I am attacking wrong target!!
+                {
+                    m_bot->SetSelection(m_tank->getVictim()->GetGUID());
+                    return;
+                }
+                else if (CastSpell(FEIGN_DEATH,m_bot)) { return; }
+                else { return; } // No more threat reducing spells, just slow down
+            }
+
+            if (CastSpell(RAPTOR_STRIKE,pTarget,true,true)) {} //No gcd
+            if (CastSpell(MONGOOSE_BITE,pTarget,true,true)) { return; } // Cannot be sure if casted or not
+            else if (CastSpell(COUNTERATTACK,pTarget,true,true)) { return; } // Cannot be sure if casted or not
+            if (CastSpell(WING_CLIP,pTarget)) { return; }
+            if (isUnderAttack(m_tank,6) && CastSpell(SNAKE_TRAP,m_bot)) { return; }
+            if (isUnderAttack(m_tank,4) && CastSpell(EXPLOSIVE_TRAP,m_bot)) { return; }
+            if (CastSpell(IMMOLATION_TRAP,m_bot)) { return; }
+            break;
+        #pragma endregion
+
+        #pragma region BOT_ROLE_DPS_RANGED
+        case BOT_ROLE_DPS_RANGED:
+            if (m_pulling) {
+                if (GetAI()->CastSpell(CONCUSSIVE_SHOT,pTarget) ||
+                    GetAI()->CastSpell(AUTO_SHOT,pTarget)) {
+                    m_pulling = false;
+                    GetAI()->SetCombatOrder(ORDERS_NONE);
+                    GetAI()->Follow(*GetMaster());
+                    GetAI()->SetIgnoreUpdateTime(2);
+
+                    if(HasPet(GetPlayerBot()))
+                        m_bot->GetPet()->SetReactState(REACT_DEFENSIVE);
+                }
+                return;
+            }
+            if (AUTO_SHOT && !m_bot->FindCurrentSpellBySpellId(AUTO_SHOT)) { ai->CastSpell(AUTO_SHOT,pTarget); } //Start autoshot
+            if (!(ai->GetManaPercent() < 85 && m_bot->HasAura(ASPECT_OF_THE_VIPER)) && CastSpell(ASPECT_OF_THE_HAWK,m_bot)) { return; } //Get Hawk aspect
+            if ((ai->GetManaPercent() < 25) && CastSpell(ASPECT_OF_THE_VIPER,m_bot,true,false,true)) { return; } //Build up mana
+
+            // if i am main tank, protect master by taunt
+            if(m_tank->GetGUID() == m_bot->GetGUID())
+            {
+                // Taunt if needed (Only for master)
+                Unit *curAtt = GetAttackerOf(GetMaster());
+                if (curAtt && CastSpell(DISTRACTING_SHOT, curAtt))  { return; }
+                // My target is not attacking me, taunt..
+                if (pVictim && pVictim->GetGUID() != m_bot->GetGUID() && CastSpell(DISTRACTING_SHOT, pTarget) )  { return; }
+            }
+            // If i am not tank, transfer threat to tank or pet..
+            else
+            {
+                if (CastSpell(MISDIRECTION,m_tank)) { return; }
+                if (pet && pet->isAlive() && CastSpell(MISDIRECTION,pet)) { return; }
+
+                // Threat control
+                if (pThreat < threatThreshold || m_bot->HasAura(MISDIRECTION) ) { } //Continue attack
+                else
+                {
+                    if (pet && isUnderAttack(pet) && pet->getVictim() && pet->getVictim()->GetGUID() != pTarget->GetGUID()) //Should be helping pet
+                    {
+                        m_bot->SetSelection(pet->getVictim()->GetGUID());
+                        return;
+                    }
+                    else if (m_tank->getVictim() && m_tank->getVictim()->GetGUID() != pTarget->GetGUID()) // I am attacking wrong target!!
+                    {
+                        m_bot->SetSelection(m_tank->getVictim()->GetGUID());
+                        return;
+                    }
+                    else if (CastSpell(FEIGN_DEATH,m_bot)) { return; }
+                    else { return; } // No more threat reducing spells, just slow down
+                }
+            }
+
+            // DO dps
+            if (ai->GetHealthPercent(*pTarget) < 20 && CastSpell(KILL_SHOT,pTarget)) { return; }
+            if (isUnderAttack(m_tank,4) && CastSpell(MULTI_SHOT,pTarget)) { return; }
+            if (isUnderAttack(m_tank,4) && CastSpell(VOLLEY,pTarget)) { GetAI()->SetIgnoreUpdateTime(7); return; }
+            if (CanCast(CHIMERA_SHOT,pTarget) &&
+                (pTarget->HasAura(VIPER_STING,m_bot->GetGUID()) || pTarget->HasAura(SERPENT_STING,m_bot->GetGUID()) )
+                && CastSpell(CHIMERA_SHOT,pTarget,false) ) { return; }
+            if (ai->GetManaPercent() < 60 && ai->GetManaPercent(*pTarget) > 4 && CastSpell(VIPER_STING,pTarget)) { return; }
+            if (!pTarget->HasAura(VIPER_STING,m_bot->GetGUID()) && CastSpell(SERPENT_STING,pTarget)) { return; }
+            if (CastSpell(ARCANE_SHOT,pTarget)) { return; }
+            if (CastSpell(BLACK_ARROW,pTarget)) { return; }
+            if (CastSpell(EXPLOSIVE_SHOT,pTarget)) { return; }
+            if (CastSpell(STEADY_SHOT,pTarget)) { return; }
+            break;
+        #pragma endregion
+    }
+
+    /*// drink potion if support / healer (Other builds simply overuse mana and waste mana pots)
+    if(ai->GetManaPercent() < 5 && (m_role == BOT_ROLE_SUPPORT || m_role == BOT_ROLE_HEALER) )
+    {
+        Item *pItem = ai->FindPotion();
+        if(pItem != NULL)
+        {
+            if (pItem->GetSpell() && m_bot->HasSpellCooldown(pItem->GetSpell()) ) { return; } //pot is in cooldown
+            ai->UseItem(*pItem);
+        }
+    }*/
+} // end DoNextCombatManeuver
+
+void PlayerbotHunterAI::DoNonCombatActions()
+{
+    PlayerbotAI *ai = GetAI();
+    Player *m_bot = GetPlayerBot();
+    if (!m_bot || !ai || m_bot->isDead()) { return; }
+
+    //If Casting or Eating/Drinking return
+    if (m_bot->HasUnitState(UNIT_STAT_CASTING)) { return; }
+    if (m_bot->getStandState() == UNIT_STAND_STATE_SIT) { return; }
+
+    // buff group
+    if (CastSpell(TRUESHOT_AURA, m_bot)) { return; }
+
+    //mana/hp check
+    //Don't bother with eating, if low on hp, just let it heal themself
+    if (m_bot->getRace() == (uint8) RACE_UNDEAD_PLAYER && ai->GetHealthPercent() < 75 && CastSpell(R_CANNIBALIZE,m_bot)) { return; }
+    if (ai->GetManaPercent() < 20 || ai->GetHealthPercent() < 30) { ai->Feast(); }
+
+    #pragma region Check Pet
+    // check for pet
+    if( PET_SUMMON>0 && !m_petSummonFailed && HasPet(m_bot) )
+    {
+        // we can summon pet, and no critical summon errors before
+        Pet *pet = m_bot->GetPet();
+        if( !pet )
+        {
+            // summon pet
+            if( PET_SUMMON>0 && ai->CastSpell(PET_SUMMON,m_bot) )
+                ai->TellMaster( "summoning pet." );
+            else
+            {
+                m_petSummonFailed = true;
+                ai->TellMaster( "summon pet failed!" );
+            }
+        }
+        else if( pet->getDeathState() != ALIVE )
+        {
+            // revive pet
+            if( PET_REVIVE>0 && ai->GetManaPercent()>=80 && ai->CastSpell(PET_REVIVE,m_bot) )
+                ai->TellMaster( "reviving pet." );
+        }
+        else if( ((float)pet->GetHealth()/(float)pet->GetMaxHealth()) < 0.5f )
+        {
+            // heal pet when health lower 50%
+            if( PET_MEND>0 && !pet->getDeathState() != ALIVE && !pet->HasAura(PET_MEND,0) && ai->GetManaPercent()>=13 && ai->CastSpell(PET_MEND,m_bot) )
+                ai->TellMaster( "healing pet." );
+        }
+        else if(pet->GetHappinessState() != HAPPY) // if pet is hungry
+        {
+            Unit *caster = (Unit*)m_bot;
+            // list out items in main backpack
+            for (uint8 slot = INVENTORY_SLOT_ITEM_START; slot < INVENTORY_SLOT_ITEM_END; slot++)
+            {
+                Item* const pItem = m_bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot);
+                if (pItem)
+                {
+                    const ItemPrototype* const pItemProto = pItem->GetProto();
+                    if (!pItemProto )
+                        continue;
+                    if(pet->HaveInDiet(pItemProto)) // is pItem in pets diet
+                    {
+                        //sLog->outDebug("Food for pet: %s",pItemProto->Name1);
+                        caster->CastSpell(caster,51284,true); // pet feed visual
+                        uint32 count = 1; // number of items used
+                        int32 benefit = pet->GetCurrentFoodBenefitLevel(pItemProto->ItemLevel); // nutritional value of food
+                        m_bot->DestroyItemCount(pItem,count,true); // remove item from inventory
+                        m_bot->CastCustomSpell(m_bot,PET_FEED,&benefit,NULL,NULL,true); // feed pet
+                        ai->TellMaster( "feeding pet." );
+                        ai->SetIgnoreUpdateTime(10);
+                        return;
+                    }
+                }
+            }
+            // list out items in other removable backpacks
+            for (uint8 bag = INVENTORY_SLOT_BAG_START; bag < INVENTORY_SLOT_BAG_END; ++bag)
+            {
+                const Bag* const pBag = (Bag*) m_bot->GetItemByPos(INVENTORY_SLOT_BAG_0, bag);
+                if (pBag)
+                {
+                    for (uint8 slot = 0; slot < pBag->GetBagSize(); ++slot)
+                    {
+                        Item* const pItem = m_bot->GetItemByPos(bag, slot);
+                        if (pItem)
+                        {
+                            const ItemPrototype* const pItemProto = pItem->GetProto();
+                            if (!pItemProto )
+                                continue;
+                            if(pet->HaveInDiet(pItemProto)) // is pItem in pets diet
+                            {
+                                //sLog->outDebug("Food for pet: %s",pItemProto->Name1);
+                                caster->CastSpell(caster,51284,true); // pet feed visual
+                                uint32 count = 1; // number of items used
+                                int32 benefit = pet->GetCurrentFoodBenefitLevel(pItemProto->ItemLevel); // nutritional value of food
+                                m_bot->DestroyItemCount(pItem,count,true); // remove item from inventory
+                                m_bot->CastCustomSpell(m_bot,PET_FEED,&benefit,NULL,NULL,true); // feed pet
+                                ai->TellMaster( "feeding pet." );
+                                ai->SetIgnoreUpdateTime(10);
+                                return;
+                            }
+                        }
+                    }
+                }
+            }
+            if( pet->HasAura(PET_MEND, 0) && !pet->HasAura(PET_FEED, 0))
+
+                ai->TellMaster( "..no pet food!" );
+                ai->SetIgnoreUpdateTime(7);
+        }
+    #pragma endregion
+    }
+} // end DoNonCombatActions
+
+void PlayerbotHunterAI::Pull()
+{
+    if (!AUTO_SHOT) return;
+
+    // check ammo
+    uint32 ammo_id = GetPlayerBot()->GetUInt32Value(PLAYER_AMMO_ID);
+    if (!ammo_id) {
+        GetPlayerBot()->Say("I'm out of ammo.", LANG_UNIVERSAL);
+        return;
+    }
+
+    Unit* pTarget = ObjectAccessor::GetUnit(*GetMaster(), GetMaster()->GetSelection());
+    if (pTarget==NULL || pTarget->IsFriendlyTo(GetMaster()))
+    {
+        GetPlayerBot()->Say("Invalid target", LANG_UNIVERSAL);
+        GetAI()->Follow(*GetMaster());
+        return;
+    }
+
+    m_role = BOT_ROLE_DPS_RANGED;
+    m_pulling = true;
+    GetAI()->SetIgnoreUpdateTime(0);
+
+    if(GetPlayerBot()->GetPet())
+        GetPlayerBot()->GetPet()->SetReactState(REACT_PASSIVE);
+}
diff --git a/src/server/game/AI/Bots/PlayerbotHunterAI.h b/src/server/game/AI/Bots/PlayerbotHunterAI.h
new file mode 100644
index 0000000..aae7a9f
--- /dev/null
+++ b/src/server/game/AI/Bots/PlayerbotHunterAI.h
@@ -0,0 +1,68 @@
+#ifndef _PLAYERHUNTERAI_H
+#define _PLAYERHUNTERAI_H
+
+#include "PlayerbotClassAI.h"
+
+//class Player;
+
+class PlayerbotHunterAI : PlayerbotClassAI
+{
+    public:
+        PlayerbotHunterAI(Player *const master, Player *const bot, PlayerbotAI *const ai);
+        virtual ~PlayerbotHunterAI();
+
+        virtual void LoadSpells();
+
+        //all combat actions go here
+        void DoNextCombatManeuver(Unit *);
+
+        //all non combat actions go here, ex buffs, heals, rezzes
+        void DoNonCombatActions();
+
+        bool HasPet(Player* bot);
+
+        virtual void Pull();
+
+        //buff a specific player, usually a real PC who is not in group
+        //void BuffPlayer(Player *target);
+
+    private:
+        //Hunter
+        bool m_petSummonFailed;
+        bool m_petFeedPetFailed;
+
+        // PET CONTROL
+        uint32 PET_SUMMON, PET_DISMISS, PET_REVIVE, PET_MEND, PET_FEED, KILL_COMMAND, INTIMIDATION, BESTIAL_WRATH;
+
+        // PET SPELL
+        uint32 GROWL, COWER, BAD_ATTITUDE, SONIC_BLAST, NETHER_SHOCK, DEMORALIZING_SCREECH;
+
+        // RANGED ATTACK
+        uint32 AUTO_SHOT, ARCANE_SHOT, EXPLOSIVE_SHOT, STEADY_SHOT, AIMED_SHOT, SCATTER_SHOT, KILL_SHOT, CHIMERA_SHOT, CONCUSSIVE_SHOT, DISTRACTING_SHOT, SILENCING_SHOT;
+
+        // STINGS
+        uint32 SERPENT_STING, SCORPID_STING, WYVERN_STING, VIPER_STING;
+
+        // DEBUFF
+        uint32 HUNTERS_MARK, SCARE_BEAST;
+
+        //AOE
+        uint32 VOLLEY, MULTI_SHOT;
+
+        //MELEE
+        uint32 RAPTOR_STRIKE, WING_CLIP, MONGOOSE_BITE, COUNTERATTACK;
+
+        //TRAP
+        uint32 FREEZING_TRAP, IMMOLATION_TRAP, FROST_TRAP, EXPLOSIVE_TRAP, SNAKE_TRAP, ARCANE_TRAP, FREEZING_ARROW, BLACK_ARROW;
+
+        //BUFF
+        uint32 TRUESHOT_AURA, DETERRENCE, FEIGN_DEATH, DISENGAGE, RAPID_FIRE, READINESS, MISDIRECTION;
+
+        //ASPECT
+        uint32 ASPECT_OF_THE_HAWK, ASPECT_OF_THE_MONKEY, ASPECT_OF_THE_VIPER;
+
+        uint32 TALENT_MM, TALENT_BM, TALENT_SURVIVAL;
+
+};
+
+#endif
diff --git a/src/server/game/AI/Bots/PlayerbotMageAI.cpp b/src/server/game/AI/Bots/PlayerbotMageAI.cpp
new file mode 100644
index 0000000..a3c510c
--- /dev/null
+++ b/src/server/game/AI/Bots/PlayerbotMageAI.cpp
@@ -0,0 +1,384 @@
+#include "PlayerbotMageAI.h"
+class PlayerbotAI;
+PlayerbotMageAI::PlayerbotMageAI(Player *const master, Player *const bot, PlayerbotAI *const ai): PlayerbotClassAI(master, bot, ai){\
+    foodDrinkSpamTimer = 0;
+    LoadSpells();
+}
+PlayerbotMageAI::~PlayerbotMageAI(){}
+
+void PlayerbotMageAI::LoadSpells() {
+    PlayerbotAI *ai = GetAI();
+    if (!ai) return;
+    #pragma region SpellId Fill
+    //arcane
+    ARCANE_MISSILES = ai->getSpellIdExact("Arcane Missiles");
+    ARCANE_EXPLOSION = ai->getSpellIdExact("Arcane Explosion");
+    ARCANE_BLAST = ai->getSpellIdExact("Arcane Blast");
+    ARCANE_BARRAGE = ai->getSpellIdExact("Arcane Barrage");
+
+
+    //fire
+    FIREBALL = ai->getSpellIdExact("Fireball");
+    FROSTFIRE_BOLT = ai->getSpellIdExact("Frostfire Bolt");
+    FIRE_BLAST = ai->getSpellIdExact("Fire Blast");
+    FLAMESTRIKE = ai->getSpellIdExact("Flamestrike");
+    BLAST_WAVE = ai->getSpellIdExact("Blastwave");
+    SCORCH = ai->getSpellIdExact("Scorch");
+    PYROBLAST = ai->getSpellIdExact("Pyroblast");
+    LIVING_BOMB = ai->getSpellIdExact("Living Bomb");
+
+
+    //cold
+    FROSTBOLT = ai->getSpellIdExact("Frostbolt");
+    FROST_NOVA = ai->getSpellIdExact("Frost Nova");
+    ICE_LANCE = ai->getSpellIdExact("Ice Lance");
+    BLIZZARD = ai->getSpellIdExact("Blizzard");
+    CONE_OF_COLD = ai->getSpellIdExact("Cone of Cold");
+
+    WATER_ELEMENTAL = ai->getSpellIdExact("Summon Water Elemental");
+
+
+    // buffs
+    FROST_ARMOR = ai->getSpellIdExact("Ice Armor");
+    if (!FROST_ARMOR) FROST_ARMOR = ai->getSpellIdExact("Frost Armor");
+    MAGE_ARMOR = ai->getSpellIdExact("Mage Armor");
+    MOLTEN_ARMOR = ai->getSpellIdExact("Molten Armor");
+    FIRE_WARD = ai->getSpellIdExact("Fire Ward");
+    FROST_WARD = ai->getSpellIdExact("Frost Ward");
+    MANA_SHIELD = ai->getSpellIdExact("Mana Shield");
+    ICE_BARRIER = ai->getSpellIdExact("Ice Barrier");
+    POM = ai->getSpellIdExact("Presence of Mind");
+    FOCUS_MAGIC = ai->getSpellIdExact("Focus Magic");
+    ARCANE_POWER = ai->getSpellIdExact("Arance Power");
+    COMBUSTION = ai->getSpellIdExact("Combustion");
+    ICY_VEINS = ai->getSpellIdExact("Icy Veins");
+
+    ARCANE_INTELLECT = ai->getSpellIdExact("Arcane Intellect");
+    ARCANE_BRILLIANCE = ai->getSpellIdExact("Arcane Brilliance");
+    DALARAN_INTELLECT = ai->getSpellIdExact("Dalaran Intellect");
+    DALARAN_BRILLIANCE = ai->getSpellIdExact("Dalaran Brilliance");
+    DAMPEN_MAGIC = ai->getSpellIdExact("Dampen Magic");
+    AMPLIFY_MAGIC = ai->getSpellIdExact("Amplify Magic");
+
+
+    //CC
+    POLYMORPH = ai->getSpellIdExact("Polymorph");
+    DRAGONS_BREATH = ai->getSpellIdExact("Dragon's Breath");
+    DEEP_FREEZE = ai->getSpellIdExact("Deep Freeze");
+
+
+    //other
+    CONJURE_REFRESHMENT = ai->getSpellIdExact("Conjure Refreshment");
+    CONJURE_WATER = ai->getSpellIdExact("Conjure Water");
+    CONJURE_FOOD = ai->getSpellIdExact("Conjure Food");
+    CONJURE_MANA_GEM = ai->getSpellIdExact("Conjure Mana Gem");
+    MIRROR_IMAGE = ai->getSpellIdExact("Mirror Image");
+    BLINK = ai->getSpellIdExact("Blink");
+    ICE_BLOCK = ai->getSpellIdExact("Ice Block");
+    INVISIBILITY = ai->getSpellIdExact("Invisibility");
+    EVOCATION = ai->getSpellIdExact("Evocation");
+    REMOVE_CURSE = ai->getSpellIdExact("Remove Curse");
+    COUNTER_SPELL = ai->getSpellIdExact("Counterspell");
+    SLOW = ai->getSpellIdExact("Slow");
+
+    //Special
+    P_BRAIN_FREEZE = 57761; //Brain Freeze proc
+    P_FIRESTARTER = 54741; //Firestarter proc
+    P_HOT_STREAK = 48108; //Hot Sreak proc
+    P_ARCANE_BLAST = 36032; //Arcane blast proc
+	P_MISSILE_BARRAGE = 54490; //Missle Barrage proc
+	P_FINGERS_OF_FROST = 44545; //Fingers of Frost proc
+	IMP_SCORCH = 12873; //IMP SCORCH
+
+    SHOOT = ai->getSpellIdExact("Shoot");
+
+    TALENT_ARCANE = ARCANE_BARRAGE;
+    TALENT_FIRE = COMBUSTION;
+    TALENT_FROST = ICE_BARRIER;
+
+    uint8 talentCounter = 0;
+    if (TALENT_ARCANE) talentCounter++;
+    if (TALENT_FIRE) talentCounter++;
+    if (TALENT_FROST) talentCounter++;
+    //if (talentCounter > 1) { TALENT_ARCANE = 0; TALENT_FIRE = 0; TALENT_FROST = 0; } //Unreliable Talent detection.
+    #pragma endregion
+}
+
+void PlayerbotMageAI::DoNextCombatManeuver(Unit *pTarget)
+{
+    if (!pTarget || pTarget->isDead()) return;
+    PlayerbotAI *ai = GetAI();
+    if (!ai) return;
+    Player *m_bot = GetPlayerBot();
+    if (!m_bot || m_bot->isDead()) return;
+    Unit *pVictim = pTarget->getVictim();
+    Unit *m_tank = FindMainTankInRaid(GetMaster());
+    if (!m_tank && m_bot->GetGroup() && GetMaster()->GetGroup() != m_bot->GetGroup()) { FindMainTankInRaid(m_bot); }
+    if (!m_tank) { m_tank = m_bot; }
+    uint32 masterHP = GetMaster()->GetHealth()*100 / GetMaster()->GetMaxHealth();
+    float pDist = m_bot->GetDistance(pTarget);
+    uint8 pThreat = GetThreatPercent(pTarget);
+
+    #pragma region Choose Actions
+    // Choose actions accoring to talents (MAGE is always ranged dps)
+    m_role = BOT_ROLE_DPS_RANGED;
+
+    // if i am under attack and if i am not tank or offtank: change target if needed
+    if (isUnderAttack())
+    {
+        // Keep hitting but reduce threat
+        //else if (m_bot->getRace() == (uint8) RACE_NIGHTELF && CastSpell(R_SHADOWMELD,m_bot)) { return; }
+            if (pVictim && pVictim->GetGUID() == m_bot->GetGUID() && pDist <= 2) {  } // My target is almost up to me, no need to search
+            else //Have to select nearest target
+            {
+                Unit *curAtt = GetNearestAttackerOf(m_bot);
+                if (curAtt && curAtt->GetGUID() != pTarget->GetGUID())
+                {
+                    m_bot->SetSelection(curAtt->GetGUID());
+                    DoNextCombatManeuver(curAtt); //Restart new update to get variables fixed..
+                    return;
+                }
+            }
+            //my target is attacking me
+    }
+    #pragma endregion
+
+    TakePosition(pTarget);
+    // If there's a cast stop
+    if (m_bot->HasUnitState(UNIT_STAT_CASTING)) { return; }
+
+    if (DoSupportRaid(m_bot,30,0,0,0,1,1)) { return; }
+
+    if (m_tank->GetGUID() != m_bot->GetGUID() && pVictim && pVictim->GetGUID() == m_bot->GetGUID() )
+    {
+        //if (CastSpell(INVISIBILITY, m_bot)) { return; }
+        if (ai->GetHealthPercent(*pTarget) > 50 && CastSpell(POLYMORPH)) { return; }
+        //if (m_bot->getRace() == (uint8) RACE_NIGHTELF && isUnderAttack() && CastSpell(R_SHADOWMELD, m_bot)) { return; }
+    }
+    if (isUnderAttack() && pDist > 5 && CastSpell(FROST_NOVA, pTarget)) { return; }
+    if (DEEP_FREEZE && pTarget->isFrozen() && CastSpell(DEEP_FREEZE,pTarget)) { return; }
+    if (isUnderAttack() && CastSpell(DRAGONS_BREATH, pTarget)) { return; }
+    if ((isUnderAttack() || ai->GetHealthPercent() < 75 && !HasAuraName(m_bot, MANA_SHIELD))  && ai->GetManaPercent() > 40 && CastSpell(MANA_SHIELD,m_bot)) { return; }
+    if (m_bot->getRace() == (uint8) RACE_DWARF && ai->GetHealthPercent() < 75 && CastSpell(R_STONEFORM,m_bot)) { } //no gcd
+    if (m_bot->getRace() == (uint8) RACE_DRAENEI && ai->GetHealthPercent() < 55 && CastSpell(R_GIFT_OF_NAARU,m_bot)) { return; } //no Gcd, but has cast
+    if (m_bot->getRace() == (uint8) RACE_TAUREN && pDist < 8 && CastSpell(R_WAR_STOMP, pTarget)) { return; } //no gcd but is cast
+    if ((ai->GetHealthPercent() < 65 || ai->GetManaPercent() < 5) && CastSpell(ICE_BLOCK,m_bot)) { return; }
+    if (isUnderAttack() && CastSpell(ICE_BARRIER, pTarget)) { return; }
+    if (ai->GetManaPercent() < 30 && CastSpell (EVOCATION, m_bot)) { return; }
+
+
+    //Break spells
+    if (m_bot->getRace() == (uint8) RACE_BLOODELF && pDist < 8 && (pTarget->IsNonMeleeSpellCasted(true) || ai->GetManaPercent() < 40) && CastSpell(R_ARCANE_TORRENT, pTarget)) { } //no gcd
+    else if (pThreat < threatThreshold && pTarget->IsNonMeleeSpellCasted(true) && CastSpell(COUNTER_SPELL, pTarget)) { return; } //High threat
+	if (!m_bot->HasAura(MOLTEN_ARMOR) && CastSpell(MOLTEN_ARMOR,m_bot)) { return; }
+
+    if (ai->GetHealthPercent(*pTarget) > 96) { return; } // dont dps too early
+
+    //Catch
+    if (pTarget->HasUnitMovementFlag(UNIT_FLAG_FLEEING))
+    {
+        if (CastSpell(FROST_NOVA,pTarget)) return;
+        if (CastSpell(FROSTBOLT,pTarget)) return;
+    }
+
+    // If at threat limit, try to reduce threat
+    if (pThreat > threatThreshold && m_tank->GetGUID() != m_bot->GetGUID() && !isUnderAttack())
+    {
+        if (m_tank->getVictim() && m_tank->getVictim()->GetGUID() != pTarget->GetGUID()) // I am attacking wrong target!!
+        {
+            m_bot->SetSelection(m_tank->getVictim()->GetGUID());
+            return;
+        }
+        else
+        {
+            if (CastSpell(INVISIBILITY,m_bot)) { return; } //Lets see if we can manage
+            else if (m_bot->FindCurrentSpellBySpellId(SHOOT)) { m_bot->InterruptNonMeleeSpells( true, SHOOT ); return; } //Disable wand
+            else { return; } //use no spells and wait threat to be reduced
+        }
+    }
+
+
+    // buff up
+    if (CastSpell(ICY_VEINS,m_bot)) {} //nogcd
+    if (m_bot->getRace() == (uint8) RACE_TROLL && CastSpell(R_BERSERKING,m_bot)) {} //no GCD
+    if (m_bot->getRace() == (uint8) RACE_ORC && CastSpell(R_BLOOD_FURY,m_bot)) {} //no GCD
+    if (CastSpell(POM,m_bot)) {} //nogcd
+
+	if (TALENT_ARCANE)
+	{
+		if (CastSpell(ARCANE_POWER,m_bot)) {} //nogcd
+		if (CastSpell(MIRROR_IMAGE,m_bot)) { return; }
+		//AOE
+		if (isUnderAttack(m_tank,5))
+		{
+			if (CastSpell(BLIZZARD,pTarget)) { return; }
+		}
+		//DPS
+		if (ARCANE_BLAST)
+        {
+            Aura *abaura = m_bot->GetAura(P_ARCANE_BLAST);
+            if (abaura && abaura->GetStackAmount() >= 3)
+			{
+				if (m_bot->HasAura(P_MISSILE_BARRAGE) && CastSpell(ARCANE_MISSILES,pTarget)) { return; }
+				else if (CastSpell(ARCANE_BARRAGE,pTarget)) { return; }
+			}
+        }
+        if (CastSpell(ARCANE_BARRAGE,pTarget) ) { return; }
+
+	}
+	if (TALENT_FIRE)
+	{
+		if (CastSpell(COMBUSTION,m_bot)) { } //nogcd
+		if (CastSpell(MIRROR_IMAGE,m_bot)) { return; }
+
+		//AOE
+		if (isUnderAttack(m_tank,5))
+		{
+			if (CastSpell(FLAMESTRIKE,pTarget)) { return; }
+			if (CastSpell(BLAST_WAVE,pTarget)) { return; }
+			if (CastSpell(LIVING_BOMB,pTarget)) { return; }
+			if (CastSpell(DRAGONS_BREATH,pTarget)) { return; }
+		}
+
+		//DPS
+		if (m_bot->HasAura(P_HOT_STREAK) && CastSpell(PYROBLAST,pTarget)) { return; }
+		if (!pTarget->HasAura(LIVING_BOMB,m_bot->GetGUID()) && CastSpell(LIVING_BOMB,pTarget)) { return; }
+		//if (!pTarget->HasAura(IMP_SCORCH) && CastSpell(SCORCH,pTarget)) { return; }
+		if (CastSpell(FIREBALL,pTarget)) { return; }
+	}
+	if (TALENT_FROST)
+	{
+		if (CastSpell(MIRROR_IMAGE,m_bot)) { return; }
+        if (CastSpell(WATER_ELEMENTAL,m_bot)) { return; }
+
+        uint64 pet_guid = m_bot->GetPetGUID();
+        if (pet_guid>0){
+            Pet* pet = ObjectAccessor::GetPet(*m_bot, pet_guid);
+            Unit *unit = ObjectAccessor::GetUnit(*m_bot, pet_guid);
+            if (unit!=NULL){
+				if (!unit->isInCombat()) {
+                    m_bot->GetSession()->HandlePetActionHelper(unit, pet_guid, COMMAND_ATTACK, ACT_COMMAND, pTarget->GetGUID());
+				}
+            }
+        }
+
+        //if (CastSpell(33395, pTarget)) // pet freeze spell
+        //    sLog->outError ("successfully casted freeze");
+
+        //AOE
+        if (isUnderAttack(m_tank,5))
+        {
+            if (CastSpell(BLIZZARD,pTarget)) { return; }
+        }
+
+        //DPS
+        if (m_bot->HasAura(P_FINGERS_OF_FROST) && CastSpell(DEEP_FREEZE,pTarget)) { return; }
+        if (m_bot->HasAura(P_BRAIN_FREEZE) && CastSpell(FROSTFIRE_BOLT,pTarget)) { return; }
+        if (CastSpell(FROSTBOLT,pTarget,true,true)) { return; }
+
+    }
+
+    // Defaults especialy for lower levels
+    if (m_bot->HasAura(P_BRAIN_FREEZE) && CastSpell(FIREBALL,pTarget,1,1)) { return; }
+        if (m_bot->HasAura(P_FIRESTARTER) && CastSpell(FLAMESTRIKE,pTarget,1,1)) { return; }
+        if (m_bot->HasAura(P_HOT_STREAK) && CastSpell(PYROBLAST,pTarget,1,1)) { return; }
+        if (m_bot->HasAura(POM) && (CastSpell(PYROBLAST,pTarget,1,1) || CastSpell(FIREBALL,pTarget,1,1) || CastSpell(FROSTBOLT,pTarget,1,1))) { return; }
+        if (pTarget->isFrozen() && CastSpell(ICE_LANCE,pTarget)) { return; }
+        if (m_bot->isMoving() && (CastSpell(FIRE_BLAST,pTarget,1,1) || CastSpell(ARCANE_BARRAGE,pTarget) || CastSpell(ICE_LANCE,pTarget))) { return; }
+        if (CastSpell(FIREBALL,pTarget)) { return; }
+        if (CastSpell(FROSTBOLT,pTarget)) { return; }
+        if (CastSpell(ARCANE_MISSILES,pTarget)) { return; }
+
+    // drink potion
+    if(ai->GetManaPercent() < 5 )
+    {
+        Item *pItem = ai->FindPotion();
+        if(pItem != NULL)
+        {
+            if (pItem->GetSpell() && m_bot->HasSpellCooldown(pItem->GetSpell()) ) { return; } //pot is in cooldown
+            ai->UseItem(*pItem);
+        }
+    }
+
+    // if we get down here, it means we are out of mana, so use wand
+    CastSpell(SHOOT, pTarget);
+
+} //end DoNextCombatManeuver
+void PlayerbotMageAI::DoNonCombatActions()
+{
+    PlayerbotAI *ai = GetAI();
+    Player *m_bot = GetPlayerBot();
+    if (!m_bot || !ai || m_bot->isDead()) { return; }
+
+    // make sure pet stays by your side
+    uint64 pet_guid = m_bot->GetPetGUID();
+    if (pet_guid>0){
+        Pet* pet = ObjectAccessor::GetPet(*m_bot, pet_guid);
+        Unit *unit = ObjectAccessor::GetUnit(*m_bot, pet_guid);
+        if (unit!=NULL){
+            m_bot->GetSession()->HandlePetActionHelper(unit, pet_guid, COMMAND_FOLLOW, ACT_COMMAND, 0);
+            m_bot->GetSession()->HandlePetActionHelper(unit, pet_guid, REACT_DEFENSIVE, ACT_REACTION, 0);
+        }
+    }
+
+
+    //If Casting or Eating/Drinking return
+    if (m_bot->HasUnitState(UNIT_STAT_CASTING)) { return; }
+    if (m_bot->getStandState() == UNIT_STAND_STATE_SIT) { return; }
+
+    //buff and heal raid
+    if (DoSupportRaid(m_bot,30,0,0,0,1,1)) { return; }
+
+    //Own Buffs
+    if (MOLTEN_ARMOR) { if ( CastSpell(MOLTEN_ARMOR,m_bot)) { return; } }
+    else if (CastSpell(MAGE_ARMOR,m_bot)) { return; }
+    if (CastSpell(COMBUSTION,m_bot)) { } //nogcd
+    if (!HasAuraName(m_bot, MANA_SHIELD)) CastSpell (MANA_SHIELD);
+
+    //conjure food & water
+    Item *pItem = ai->FindDrink();
+	if(pItem == NULL && ai->GetManaPercent() >= 48)
+    {
+        if (CastSpell(CONJURE_REFRESHMENT, m_bot)) { return; }
+        if (CastSpell(CONJURE_WATER, m_bot)) { return; }
+        return;
+    }
+    pItem = ai->FindFood();
+    if(pItem == NULL && ai->GetManaPercent() >= 48)
+    {
+        if (CastSpell(CONJURE_REFRESHMENT, m_bot)) { return; }
+        if (CastSpell(CONJURE_FOOD, m_bot)) { return; }
+        return;
+    }
+    //Conjure mana gem??
+
+    //mana/hp check
+    //Don't bother with eating, if low on hp, just let it heal themself
+    if (m_bot->getRace() == (uint8) RACE_UNDEAD_PLAYER && ai->GetHealthPercent() < 75 && CastSpell(R_CANNIBALIZE,m_bot)) { return; }
+    if (ai->GetManaPercent() < 50 && CastSpell (EVOCATION, m_bot)) { return; }
+    if (ai->GetManaPercent() < 50 || ai->GetHealthPercent() < 50) { ai->Feast(); }
+} //end DoNonCombatActions
+
+
+bool PlayerbotMageAI::BuffPlayer(Unit *target)
+{
+    if (!target || target->isDead()) return false;
+
+    if (target->getClass() == CLASS_WARRIOR || target->getClass() == CLASS_DEATH_KNIGHT || target->getClass() == CLASS_ROGUE) return false;
+
+    if (!HasAuraName(target, ARCANE_INTELLECT) && !HasAuraName(target, ARCANE_BRILLIANCE) && !HasAuraName(target, DALARAN_INTELLECT) && !HasAuraName(target, DALARAN_BRILLIANCE))
+    {
+        if (CastSpell(ARCANE_BRILLIANCE, target)) return true;
+        else if (CastSpell (ARCANE_INTELLECT, target)) return true;
+    }
+    return false;
+}
+bool PlayerbotMageAI::CureTarget(Unit *target)
+{
+	//Cures the target
+    Player *m_bot = GetPlayerBot();
+
+    if(!target || target->isDead()) { return false; }
+    if (castDispel(DISPEL_CURSE, target)) return true;
+    return false;
+}
diff --git a/src/server/game/AI/Bots/PlayerbotMageAI.h b/src/server/game/AI/Bots/PlayerbotMageAI.h
new file mode 100644
index 0000000..307e519
--- /dev/null
+++ b/src/server/game/AI/Bots/PlayerbotMageAI.h
@@ -0,0 +1,69 @@
+#ifndef _PLAYERBOTMAGEAI_H
+#define _PLAYERBOTMAGEAI_H
+
+#include "PlayerbotClassAI.h"
+
+enum
+{
+    SPELL_SCORCH,
+    SPELL_POM,
+    SPELL_ARCANE_POWER,
+    SPELL_FIREBALL,
+    SPELL_MISSILES,
+    SPELL_FROSTBOLT
+};
+
+//class Player;
+
+class PlayerbotMageAI : PlayerbotClassAI
+{
+    public:
+        PlayerbotMageAI(Player *const master, Player *const bot, PlayerbotAI *const ai);
+        virtual ~PlayerbotMageAI();
+
+        virtual void LoadSpells();
+
+        //all combat actions go here
+        void DoNextCombatManeuver(Unit *);
+
+        //all non combat actions go here, ex buffs, heals, rezzes
+        void DoNonCombatActions();
+
+        //buff a specific player, usually a real PC who is not in group
+        bool BuffPlayer(Unit *target);
+
+        //Cures the target
+        bool CureTarget(Unit *target);
+
+        typedef std::set<Unit *> AttackerSet;
+    private:
+        //arcane
+        uint32 ARCANE_MISSILES, ARCANE_EXPLOSION, ARCANE_BLAST, ARCANE_BARRAGE;
+
+        //fire
+        uint32 FIREBALL, FROSTFIRE_BOLT, FIRE_BLAST, FLAMESTRIKE, BLAST_WAVE, SCORCH, PYROBLAST, LIVING_BOMB;
+
+        //cold
+        uint32 FROSTBOLT, FROST_NOVA, ICE_LANCE, BLIZZARD, CONE_OF_COLD, WATER_ELEMENTAL;
+
+        // buffs
+        uint32 FROST_ARMOR, ICE_ARMOR, MAGE_ARMOR, MOLTEN_ARMOR, FIRE_WARD, FROST_WARD, MANA_SHIELD, ICE_BARRIER, POM, FOCUS_MAGIC, ARCANE_POWER, COMBUSTION, ICY_VEINS,
+            ARCANE_INTELLECT, ARCANE_BRILLIANCE, DALARAN_INTELLECT, DALARAN_BRILLIANCE, DAMPEN_MAGIC, AMPLIFY_MAGIC;
+
+        //CC
+        uint32 POLYMORPH, DRAGONS_BREATH, DEEP_FREEZE;
+
+        //other
+        uint32 CONJURE_REFRESHMENT, CONJURE_WATER, CONJURE_FOOD, CONJURE_MANA_GEM, MIRROR_IMAGE, BLINK, ICE_BLOCK, INVISIBILITY, EVOCATION, REMOVE_CURSE, COUNTER_SPELL, SLOW, SHOOT;
+
+        //special
+        uint32 P_BRAIN_FREEZE, P_FIRESTARTER, P_HOT_STREAK, P_ARCANE_BLAST, P_MISSILE_BARRAGE, P_FINGERS_OF_FROST, IMP_SCORCH;
+
+
+        uint32 TALENT_ARCANE, TALENT_FIRE, TALENT_FROST;
+
+};
+
+
+
+#endif
diff --git a/src/server/game/AI/Bots/PlayerbotPaladinAI.cpp b/src/server/game/AI/Bots/PlayerbotPaladinAI.cpp
new file mode 100644
index 0000000..fd5bcab
--- /dev/null
+++ b/src/server/game/AI/Bots/PlayerbotPaladinAI.cpp
@@ -0,0 +1,535 @@
+#include "PlayerbotPaladinAI.h"
+
+class PlayerbotAI;
+PlayerbotPaladinAI::PlayerbotPaladinAI(Player *const master, Player *const bot, PlayerbotAI *const ai): PlayerbotClassAI(master, bot, ai)
+{
+    foodDrinkSpamTimer = 0;
+    LoadSpells();
+}
+PlayerbotPaladinAI::~PlayerbotPaladinAI(){}
+
+void PlayerbotPaladinAI::LoadSpells() {
+    PlayerbotAI *ai = GetAI();
+    if (!ai) return;
+    #pragma region SpellId Fill
+    //heals
+    FLASH_OF_LIGHT = ai->getSpellIdExact("Flash of Light");
+    HOLY_LIGHT = ai->getSpellIdExact("Holy Light");
+    HOLY_SHOCK = ai->getSpellIdExact("Holy Shock");
+    CLEANSE = ai->getSpellIdExact("Cleanse");
+    if (!CLEANSE) CLEANSE = ai->getSpellIdExact("Purify");
+    LOH = ai->getSpellIdExact("Lay on Hands");
+    SACRED_SHIELD = ai->getSpellIdExact("Sacred Shield");
+    BEACON_OF_LIGHT = ai->getSpellIdExact("Beacon of Light");
+    DIVINE_FAVOR = ai->getSpellIdExact("Divine Favor");
+    REDEMPTION = ai->getSpellIdExact("Redemption");
+
+    //Damages
+    JOL = ai->getSpellIdExact("Judgement of Light");
+    JOW = ai->getSpellIdExact("Judgement of Wisdom");
+	JOJ = ai->getSpellIdExact("Judgement of Justice");
+    HAMMER_OF_WRATH = ai->getSpellIdExact("Hammer of Wrath");
+    EXORCISM = ai->getSpellIdExact("Exorcism");
+    HOLY_WRATH = ai->getSpellIdExact("Holy Wrath");
+    CONSECRATION = ai->getSpellIdExact("Consecration");
+    AVENGERS_SHIELD = ai->getSpellIdExact("Avenger's Shield");
+    SHIELD_OF_RIGHTEOUSNESS = ai->getSpellIdExact("Shield of Righteousness");
+    HOTR = ai->getSpellIdExact("Hammer of the Righteous");
+    CRUSADER_STRIKE = ai->getSpellIdExact("Crusader Strike");
+    DIVINE_STORM = ai->getSpellIdExact("Divine Storm");
+
+    //CC
+    HAMMER_OF_JUSTICE = ai->getSpellIdExact("Hammer of Justice");
+    REPENTANCE = ai->getSpellIdExact("Repentance");
+
+    //Self buffs
+    SOL = ai->getSpellIdExact("Seal of Light");
+    SOW = ai->getSpellIdExact("Seal of Wisdom");
+    SOR = ai->getSpellIdExact("Seal of Righteousness");
+    SOC = ai->getSpellIdExact("Seal of Command");
+	SOV = ai->getSpellIdExact("Seal of Vengeance");
+	if (!SOV) SOV = ai->getSpellIdExact("Seal of Corruption");
+    DIVINE_PLEA = ai->getSpellIdExact("Divine Plea");
+    HOLY_SHIELD = ai->getSpellIdExact("Holy Shield");
+    RIGHTEOUS_FURY = ai->getSpellIdExact("Righteous Fury");
+    DIVINE_SHIELD = ai->getSpellIdExact("Divine Shield");
+    if (!DIVINE_SHIELD) DIVINE_SHIELD = ai->getSpellIdExact("Divine Protection");
+    AVENGING_WRATH = ai->getSpellIdExact("Avenging Wrath");
+
+    //AURAS
+    DEVOTION_AURA = ai->getSpellIdExact("Devotion Aura");
+    RETRIBUTION_AURA = ai->getSpellIdExact("Retribution Aura");
+    CONCENTRATION_AURA = ai->getSpellIdExact("Concentration Aura");
+    FIRE_AURA = ai->getSpellIdExact("Fire Resistance Aura");
+    FROST_AURA = ai->getSpellIdExact("Frost Resistance Aura");
+    SHADOW_AURA = ai->getSpellIdExact("Shadow Resistance Aura");
+    CRUSADER_AURA = ai->getSpellIdExact("Crusader Aura");
+
+    //Blessings
+    BOW = ai->getSpellIdExact("Blessing of Wisdom");
+    BOM = ai->getSpellIdExact("Blessing of Might");
+    BOS = ai->getSpellIdExact("Blessing of Sanctuary");
+    BOK = ai->getSpellIdExact("Blessing of Kings");
+    GBOW = ai->getSpellIdExact("Greater Blessing of Wisdom");
+    GBOM = ai->getSpellIdExact("Greater Blessing of Might");
+    GBOS = ai->getSpellIdExact("Greater Blessing of Sanctuary");
+    GBOK = ai->getSpellIdExact("Greater Blessing of Kings");
+
+    //Hands
+    HOF = ai->getSpellIdExact("Hand of Freedom");
+    HOR = ai->getSpellIdExact("Hand of Reckoning");
+    HOS = ai->getSpellIdExact("Hand of Salvation");
+    HOP = ai->getSpellIdExact("Hand of Protection");
+    DIVINE_SACRIFICE = ai->getSpellIdExact("Divine Sacrifice");
+
+    //Taunt
+    RIGHTEOUS_DEFENSE = ai->getSpellIdExact("Righteous Defense");
+
+    FORBEARANCE = 25771;
+	AOW = 53488;
+
+    TALENT_RETRI = CRUSADER_STRIKE;
+    TALENT_PROT = HOLY_SHIELD;
+    TALENT_HOLY = HOLY_SHOCK;
+
+    uint8 talentCounter = 0;
+    if (TALENT_RETRI) talentCounter++;
+    if (TALENT_PROT) talentCounter++;
+    if (TALENT_HOLY) talentCounter++;
+    //if (talentCounter > 1) { TALENT_RETRI = 0; TALENT_PROT = 0; TALENT_HOLY = 0; } //Unreliable Talent detection.
+
+    #pragma endregion
+}
+
+void PlayerbotPaladinAI::DoNextCombatManeuver(Unit *pTarget)
+{
+    if (!pTarget || pTarget->isDead()) return;
+    PlayerbotAI *ai = GetAI();
+    if (!ai) return;
+    Player *m_bot = GetPlayerBot();
+    if (!m_bot || m_bot->isDead()) return;
+    Unit *pVictim = pTarget->getVictim();
+    Unit *m_tank = FindMainTankInRaid(GetMaster());
+    if (!m_tank && m_bot->GetGroup() && GetMaster()->GetGroup() != m_bot->GetGroup()) { FindMainTankInRaid(m_bot); }
+    if (!m_tank) { m_tank = m_bot; }
+    uint32 masterHP = GetMaster()->GetHealth()*100 / GetMaster()->GetMaxHealth();
+    float pDist = m_bot->GetDistance(pTarget);
+    uint8 pThreat = GetThreatPercent(pTarget);
+
+    uint8 reqHeal = 0;
+    uint8 OwnPartyHP = GetHealthPercentRaid(m_bot, reqHeal);
+
+
+    // Fill mana if needed
+    if (m_bot->getRace() == (uint8) RACE_BLOODELF && pDist < 8 && ai->GetManaPercent() < 20 && CastSpell(R_ARCANE_TORRENT, pTarget)) { } //no gcd
+    if (ai->GetManaPercent() < 30 && CastSpell (DIVINE_PLEA, m_bot)) { return; }
+
+    // If hp is too low divine shield
+    if (ai->GetHealthPercent() < 20 && (!m_bot->HasAura(DIVINE_SHIELD) || !m_bot->HasAura(HOP) || !m_bot->HasAura(SACRED_SHIELD)))
+    {
+        if (!m_bot->HasAura(FORBEARANCE))
+        {
+            if (CastSpell(DIVINE_SHIELD,m_bot)) { return; }
+            if (CastSpell(HOP,m_bot)) { return; }
+        }
+        else if (CastSpell(SACRED_SHIELD,m_bot)) { return; }
+    }
+
+    // if i am under attack and if i am not tank or offtank: change target if needed
+    if (m_tank->GetGUID() != m_bot->GetGUID() && !TALENT_PROT && isUnderAttack() )
+    {
+        // Keep hitting but reduce threat
+        if (CastSpell(HOS,m_bot,true,true)) { }
+        //else if (m_bot->getRace() == (uint8) RACE_NIGHTELF && CastSpell(R_SHADOWMELD,m_bot)) { return; }
+        else //I cannot reduce threat so
+        {
+            if (pVictim && pVictim->GetGUID() == m_bot->GetGUID() && pDist <= 2) {  } // My target is almost up to me, no need to search
+            else //Have to select nearest target
+            {
+                Unit *curAtt = GetNearestAttackerOf(m_bot);
+                if (curAtt && curAtt->GetGUID() != pTarget->GetGUID())
+                {
+                    m_bot->SetSelection(curAtt->GetGUID());
+                    //ai->AddLootGUID(curAtt->GetGUID());
+                    DoNextCombatManeuver(curAtt); //Restart new update to get variables fixed..
+                    return;
+                }
+            }
+            //my target is attacking me
+        }
+    }
+
+
+    #pragma region Choose Actions
+    // Choose actions accoring to talents
+    if (m_tank->GetGUID() == m_bot->GetGUID()) // Hey! I am Main Tank
+    {
+        if (TALENT_PROT) { m_role=BOT_ROLE_TANK; } //Just Keep Tanking
+        else
+        {
+            if (TALENT_RETRI) {
+                if ((ai->GetHealthPercent() <= 40 || masterHP <40 ) && (ai->GetManaPercent() >= 40)) { m_role = BOT_ROLE_SUPPORT; }
+                else if (OwnPartyHP < 40 && ai->GetManaPercent() >= 30) { m_role = BOT_ROLE_SUPPORT; }
+                else { m_role = BOT_ROLE_TANK; } //have no shield but can tank if you think so
+            }
+            else if (TALENT_HOLY) //I am both healer and tank?? Hmm
+            {
+                if ((ai->GetHealthPercent() <= 70 || masterHP <70 ) && (ai->GetManaPercent() >= 50))m_role = BOT_ROLE_SUPPORT;
+                else if (OwnPartyHP < 20 && ai->GetManaPercent() >= 30) { m_role = BOT_ROLE_SUPPORT; }
+                else m_role = BOT_ROLE_TANK;
+            }
+            else { m_role = BOT_ROLE_TANK; } //Unknown build or low level
+        }
+    }
+    else if (TALENT_RETRI) {
+        if ((ai->GetHealthPercent() <= 40 || masterHP <40 ) && (ai->GetManaPercent() >= 40)) { m_role = BOT_ROLE_SUPPORT; }
+        else if (OwnPartyHP < 40 && ai->GetManaPercent() >= 30) {m_role = BOT_ROLE_SUPPORT;}
+        else { m_role = BOT_ROLE_DPS_MELEE; }
+    }
+    else if (TALENT_PROT) {
+        if ((ai->GetHealthPercent() <= 30 || masterHP <40 ) && (ai->GetManaPercent() >= 20)) { m_role = BOT_ROLE_SUPPORT; }
+        else if (OwnPartyHP < 40 && ai->GetManaPercent() >= 40) { m_role = BOT_ROLE_SUPPORT; }
+        else { m_role = BOT_ROLE_OFFTANK; }
+    }
+    else if (TALENT_HOLY) { m_role = BOT_ROLE_SUPPORT; }
+    else { m_role = BOT_ROLE_DPS_MELEE; } //Unknown build or low level.. Mainly attack
+
+	//takepos
+    if (m_role == BOT_ROLE_SUPPORT || m_role == BOT_ROLE_HEALER) TakePosition(pTarget,BOT_ROLE_DPS_MELEE,0.5f);
+    else TakePosition(pTarget,m_role);
+
+    // If there's a cast stop
+    if(m_bot->HasUnitState(UNIT_STAT_CASTING)) return;
+
+    Unit *target = DoSelectLowestHpFriendly(40, 1000);
+    switch(m_role)
+    {
+        #pragma region BOT_ROLE_SUPPORT
+        case BOT_ROLE_SUPPORT:
+
+            ChangeAura(CONCENTRATION_AURA);
+            if (!TALENT_PROT && m_tank->GetGUID() != m_bot->GetGUID()) m_bot->RemoveAurasDueToSpell(RIGHTEOUS_FURY);
+            // Choose Seal
+            if (SOW && ai->GetManaPercent() <= 30) {    if (CastSpell(SOW,m_bot)) { return; } }
+            else if (m_bot->HasAura(SOW) && ai->GetManaPercent() < 85) { } // Paladin was striving for mana, keep until he got most of his mana back
+            else if(SOL && ai->GetHealthPercent() < 40) { if(CastSpell(SOL,m_bot)) { return; } }
+            else if(CastSpell(SOR, m_bot)) { return; }
+
+            if (!m_bot->HasAura(FORBEARANCE) && CastSpell(AVENGING_WRATH,m_bot)) { } // no gcd
+
+            if (DoSupportRaid(m_bot)) { return; }
+            //heal pets and bots
+            if(target && target->isAlive() && HealTarget(target, target->GetHealth()*100 / target->GetMaxHealth()) ) { return; }
+
+            if (ai->GetManaPercent() <= 80 && CastSpell(JOW,pTarget,true,true)) { return; }
+
+            // Use Spells only if mana is sufficient..
+            if(ai->GetManaPercent() < offensiveSpellThreshold ) return;
+
+        break;
+        #pragma endregion
+
+        #pragma region BOT_ROLE_TANK / BOT_ROLE_OFFTANK
+        case BOT_ROLE_TANK:
+        case BOT_ROLE_OFFTANK:
+
+            ChangeAura(DEVOTION_AURA);
+            if (CastSpell(RIGHTEOUS_FURY,m_bot)) { return; }
+            // Choose Seal
+            if (SOW && ai->GetManaPercent() <= 30) { if (CastSpell(SOW,m_bot)) { return; } }
+            else if (m_bot->HasAura(SOW) && ai->GetManaPercent() < 85) { } // Paladin was striving for mana, keep until he got most of his mana back
+            else if (SOL && ai->GetHealthPercent() < 40) { if (CastSpell(SOL,m_bot)) { return; } }
+            else if (CastSpell(SOR,m_bot)) { return; }
+
+            // We are tank/offtank threat is not an issiue;
+            // Use taunts only if helping target is not main tank..
+            // Taunt if needed (Only for master)
+            if(GetMaster()->GetGUID() != m_tank->GetGUID())
+            {
+                // Taunt if needed (Only for master)
+                Unit *curAtt = GetAttackerOf(GetMaster());
+                if (curAtt)
+                {
+                    if (isUnderAttack(GetMaster(),2) && CastSpell(RIGHTEOUS_DEFENSE, GetMaster())) { return; }
+                    if (CastSpell(HOR, curAtt,true,true))  { } //No GCD
+                }
+            }
+            // My target is not attacking me, taunt..
+            if ( m_tank->GetGUID() == m_bot->GetGUID() && pVictim && pVictim->GetGUID() != m_bot->GetGUID() && CastSpell(HOR, pTarget,true,true) )  { } //NO GCD
+
+            // Tank specials
+            if (TALENT_PROT && ai->GetManaPercent() < 90 && CastSpell (DIVINE_PLEA, m_bot)) { return; } //Prot paladin always uses this..
+            if (pVictim && pVictim->GetGUID() == m_bot->GetGUID() && CastSpell(HOLY_SHIELD,m_bot)) { return; }
+            if (CastSpell(AVENGERS_SHIELD,pTarget,true,true)) { return; }
+            if (CastSpell(HOTR,pTarget,true,true)) { return; }
+            if (CastSpell(HOLY_WRATH,pTarget,true,true)){ return; }
+            if (CastSpell(CONSECRATION,pTarget)) { return; }
+            if (m_bot->getRace() == (uint8) RACE_DWARF && CastSpell(R_STONEFORM,m_bot)) { return; }
+
+            if (DoSupportRaid(m_bot)) { return; }
+            //heal pets and bots
+            if(target && target->isAlive() && HealTarget(target, target->GetHealth()*100 / target->GetMaxHealth()) ) { return; }
+
+
+        break;
+		#pragma endregion
+
+		#pragma region BOT_ROLE_DPS_MELEE
+		case BOT_ROLE_DPS_MELEE:
+
+			ChangeAura(RETRIBUTION_AURA);
+			if (!TALENT_PROT && m_tank->GetGUID() != m_bot->GetGUID()) m_bot->RemoveAurasDueToSpell(RIGHTEOUS_FURY);
+			if (CastSpell(SOV,m_bot)) { return; }
+
+			if (CastSpell (HAMMER_OF_JUSTICE, pTarget)) { return; }
+			if (!m_bot->HasAura(FORBEARANCE) && CastSpell(AVENGING_WRATH,m_bot)) {} //no gcd
+			if (CastSpell(JOW,pTarget)) { return; }
+			if (CastSpell(DIVINE_STORM, pTarget)) { return; }
+			if (CastSpell(CRUSADER_STRIKE, pTarget)) { return; }
+			if (GetAI()->GetHealthPercent(*pTarget)<20 && CastSpell(HAMMER_OF_WRATH, pTarget)) { return; }
+			if (CastSpell(CONSECRATION,pTarget)) { return; }
+			if (m_bot->HasAura(AOW) && CastSpell(EXORCISM,pTarget)) { return; }
+			if (CastSpell(HOLY_WRATH,pTarget)) { return; }
+
+		break;
+        #pragma endregion
+
+    }
+    #pragma region PaladinCommon
+    // Shared dps spells
+    if (pTarget->GetCreatureType() == (uint32) CREATURE_TYPE_HUMANOID && pTarget->IsNonMeleeSpellCasted(true) && CastSpell (REPENTANCE, pTarget)) { return; }
+    if (m_bot->getRace() == (uint8) RACE_BLOODELF && pDist < 8 && pTarget->IsNonMeleeSpellCasted(true) && CastSpell(R_ARCANE_TORRENT, pTarget)) { } //no gcd
+    if (m_bot->getRace() == (uint8) RACE_TROLL && CastSpell(R_BERSERKING,m_bot)) {} // no GCD
+    if (m_bot->getRace() == (uint8) RACE_ORC && CastSpell(R_BLOOD_FURY,m_bot)) {} // no GCD
+
+    // If at threat limit, stop
+    if(pThreat > threatThreshold && !TALENT_PROT && m_tank->GetGUID() != m_bot->GetGUID() && !isUnderAttack())
+    {
+        if (m_tank->getVictim() && m_tank->getVictim()->GetGUID() != pTarget->GetGUID()) // I am attacking wrong target!!
+        {
+            m_bot->SetSelection(m_tank->getVictim()->GetGUID());
+            return;
+        }
+        else
+        {
+            if (CastSpell(HOS,m_bot)) { return; } //Lets see if we can manage with HOS
+            else { return; } //use no spells and wait threat to be reduced
+        }
+    }
+    // Continue attacking if theres excess mana (for healers)
+    if (m_role == BOT_ROLE_SUPPORT && ai->GetManaPercent() < offensiveSpellThreshold) { return; }
+
+    if (GetAI()->GetHealthPercent(*pTarget)<20 && CastSpell(HAMMER_OF_WRATH, pTarget,true,true)) { return; } //no gcd but cast
+    if (CastSpell (HAMMER_OF_JUSTICE, pTarget)) { return; }
+    if (CanCast(JOW,pTarget,true) &&
+        ( ( ai->GetManaPercent() <= 70 && ai->GetHealthPercent() > 90)
+        || ( ai->GetManaPercent() <= 50 && ai->GetHealthPercent() > 75)
+        || ( ai->GetManaPercent() <= 20 && ai->GetHealthPercent() > 20) )
+        && CastSpell(JOW,pTarget,false)) { return; }
+    else if (CastSpell(JOL,pTarget),true,true) { return; }
+    if (CastSpell(SHIELD_OF_RIGHTEOUSNESS,pTarget,true,true)) { return; }
+    if (CastSpell (DIVINE_STORM, pTarget,true,true)) { return; }
+    if (CastSpell (CRUSADER_STRIKE, pTarget,true,true)) { return; }
+    if (m_bot->getRace() == (uint8) RACE_TAUREN && pDist < 8 && CastSpell(R_WAR_STOMP, pTarget)) { return; } //no GCD but cast
+    if (isUnderAttack(m_tank,4) && CastSpell(HOLY_WRATH,pTarget,true,true)) { return; }
+    if (isUnderAttack(m_tank,4) && CastSpell(CONSECRATION,pTarget)) { return; }
+    if (CastSpell(HOLY_SHOCK,pTarget,true,true)) { return; }
+    if (m_role != BOT_ROLE_SUPPORT && ai->GetManaPercent() > 60 && OwnPartyHP < 65 && DoSupportRaid(m_bot)) { return; } //if there is spare time and mana, do healz and other stuff..
+    else if (m_role != BOT_ROLE_SUPPORT && ai->GetManaPercent() > 30 && DoSupportRaid(m_bot,30,false,false,false,true,false)) { return; }
+    if (CastSpell(EXORCISM,pTarget,true,true)) { return; }
+
+
+    // drink potion if support / healer (Other builds simply overuse mana and waste mana pots)
+    if(ai->GetManaPercent() < 5 && (m_role == BOT_ROLE_SUPPORT || m_role == BOT_ROLE_HEALER) )
+    {
+        Item *pItem = ai->FindPotion();
+        if(pItem != NULL)
+        {
+            if (pItem->GetSpell() && m_bot->HasSpellCooldown(pItem->GetSpell()) ) { return; } //pot is in cooldown
+            ai->UseItem(*pItem);
+        }
+    }
+    #pragma endregion
+
+} //end DoNextCombatManeuver
+
+void PlayerbotPaladinAI::DoNonCombatActions()
+{
+    PlayerbotAI *ai = GetAI();
+    Player *m_bot = GetPlayerBot();
+    if (!m_bot || !ai || m_bot->isDead()) { return; }
+
+    //If Casting or Eating/Drinking return
+    if (m_bot->HasUnitState(UNIT_STAT_CASTING)) { return; }
+    if (m_bot->getStandState() == UNIT_STAND_STATE_SIT) { return; }
+
+    //buff and heal raid
+    if (DoSupportRaid(m_bot)) { return; }
+
+    //heal pets and bots
+    Unit *target = DoSelectLowestHpFriendly(40, 1000);
+    if (target && target->isAlive() && HealTarget(target, target->GetHealth()*100 / target->GetMaxHealth())) { return; }
+
+    //mana/hp check
+    //Don't bother with eating, if low on hp, just let it heal themself
+    if (m_bot->getRace() == (uint8) RACE_UNDEAD_PLAYER && ai->GetHealthPercent() < 75 && CastSpell(R_CANNIBALIZE,m_bot)) { return; }
+    if (m_bot->GetHealth() < m_bot->GetMaxHealth() && CastSpell(FLASH_OF_LIGHT,m_bot)) { return; }
+    if (ai->GetManaPercent() < 70) { ai->Feast(); }
+} //end DoNonCombatActions
+
+bool PlayerbotPaladinAI::HealTarget(Unit *target, uint8 hp)
+{
+    if(!target || target->isDead()) return false;
+    Player *m_bot = GetPlayerBot();
+
+    if(hp < 10 && m_bot->isInCombat() && CastSpell(LOH, target)) { return true; }
+    if(hp < 10 && m_bot->isInCombat() && CastSpell(SACRED_SHIELD,target)) { return true; }
+    if(hp < 15 && m_bot->isInCombat() && CastSpell(HOP,target)) { return true; }
+    if(hp < 20 && m_bot->isInCombat() && CastSpell(BEACON_OF_LIGHT,target)) { return true; }
+    if(hp < 30 && CastSpell(HOLY_SHOCK,target,true,true,true)) { return true; }
+    if(hp < 30 && m_bot->isInCombat() && CanCast(DIVINE_FAVOR,m_bot,true) && CanCast(HOLY_LIGHT,target,true) ) { CastSpell(DIVINE_FAVOR, m_bot,false); return CastSpell(HOLY_LIGHT,target,false); } //No gcd
+    if(hp < 30 && CastSpell(FLASH_OF_LIGHT,target,true,true)) { return true; }
+    if(hp < 40 && m_bot->getRace() == (uint8) RACE_DRAENEI && CastSpell(R_GIFT_OF_NAARU,target)) { return true; } // no GCD but has cast
+    if(hp < 65 && CastSpell(HOLY_LIGHT,target,true,true)) { return true; }
+    if(hp < 85 && CastSpell(FLASH_OF_LIGHT,target,true,true)) { return true; }
+    if(hp < 95 && m_bot->isInCombat() && CastSpell(BEACON_OF_LIGHT,target)) { return true; }
+
+    return false;
+} //end HealTarget
+
+bool PlayerbotPaladinAI::CureTarget(Unit *target)
+{
+    if (!target || target->isDead()) { return false; }
+    if (castDispel(CLEANSE, target)) { return true; }
+    return false;
+
+} //end CureTarget
+
+bool PlayerbotPaladinAI::BuffPlayer(Unit *target)
+{
+    if(!target || target->isDead()) return false;
+    Player *m_bot = GetPlayerBot();
+
+    // Check if target already has a blessing by me..
+    if (HasAuraName(target,BOW,m_bot->GetGUID()) ||
+    HasAuraName(target,BOK,m_bot->GetGUID()) ||
+    HasAuraName(target,BOM,m_bot->GetGUID()) ||
+    HasAuraName(target,BOS,m_bot->GetGUID()) ||
+    HasAuraName(target,GBOW,m_bot->GetGUID()) ||
+    HasAuraName(target,GBOK,m_bot->GetGUID()) ||
+    HasAuraName(target,GBOM,m_bot->GetGUID()) ||
+    HasAuraName(target,GBOS,m_bot->GetGUID())
+    ) return false;
+
+#pragma region Choose Buff > Class
+    switch(target->getClass())
+    {
+        case CLASS_MAGE:
+        case CLASS_WARLOCK:
+            if (CanCast(GBOW,target) && !HasAuraName(target,BOW) && !HasAuraName(target,GBOW) ) return CastSpell(GBOW,target,false);
+            else if (CanCast(GBOK,target) && !HasAuraName(target,BOK) && !HasAuraName(target,GBOK) ) return CastSpell(GBOK,target,false);
+            else if (CanCast(GBOS,target) && !HasAuraName(target,BOS) && !HasAuraName(target,GBOS) ) return CastSpell(GBOS,target,false);
+            break;
+        case CLASS_PRIEST:
+            if (CanCast(GBOK,target) && !HasAuraName(target,BOK) && !HasAuraName(target,GBOK) ) return CastSpell(GBOK,target,false);
+            else if (CanCast(GBOW,target) && !HasAuraName(target,BOW) && !HasAuraName(target,GBOW) ) return CastSpell(GBOW,target,false);
+            else if (CanCast(GBOS,target) && !HasAuraName(target,BOS) && !HasAuraName(target,GBOS) ) return CastSpell(GBOS,target,false);
+            break;
+        case CLASS_HUNTER:
+            if (CanCast(GBOM,target) && !HasAuraName(target,BOM) && !HasAuraName(target,GBOM) ) return CastSpell(GBOM,target,false);
+            else if (CanCast(GBOK,target) && !HasAuraName(target,BOK) && !HasAuraName(target,GBOK) ) return CastSpell(GBOK,target,false);
+            else if (CanCast(GBOW,target) && !HasAuraName(target,BOW) && !HasAuraName(target,GBOW) ) return CastSpell(GBOW,target,false);
+            else if (CanCast(GBOS,target) && !HasAuraName(target,BOS) && !HasAuraName(target,GBOS) ) return CastSpell(GBOS,target,false);
+            break;
+        case CLASS_ROGUE:
+            if (CanCast(GBOM,target) && !HasAuraName(target,BOM) && !HasAuraName(target,GBOM) ) return CastSpell(GBOM,target,false);
+            else if (CanCast(GBOK,target) && !HasAuraName(target,BOK) && !HasAuraName(target,GBOK) ) return CastSpell(GBOK,target,false);
+            else if (CanCast(GBOS,target) && !HasAuraName(target,BOS) && !HasAuraName(target,GBOS) ) return CastSpell(GBOS,target,false);
+            break;
+        case CLASS_WARRIOR:
+        case CLASS_DEATH_KNIGHT:
+            if (target->GetUnitDodgeChance() + target->GetUnitParryChance() > 40)
+            {
+                if (CanCast(GBOS,target) && !HasAuraName(target,BOS) && !HasAuraName(target,GBOS) ) return CastSpell(GBOS,target,false);
+                else if (CanCast(GBOK,target) && !HasAuraName(target,BOK) && !HasAuraName(target,GBOK) ) return CastSpell(GBOK,target,false);
+                else if (CanCast(GBOM,target) && !HasAuraName(target,BOM) && !HasAuraName(target,GBOM) ) return CastSpell(GBOM,target,false);
+            }
+            else
+            {
+                if (CanCast(GBOK,target) && !HasAuraName(target,BOK) && !HasAuraName(target,GBOK) ) return CastSpell(GBOK,target,false);
+                else if (CanCast(GBOM,target) && !HasAuraName(target,BOM) && !HasAuraName(target,GBOM) ) return CastSpell(GBOM,target,false);
+                else if (CanCast(GBOS,target) && !HasAuraName(target,BOS) && !HasAuraName(target,GBOS) ) return CastSpell(GBOS,target,false);
+            }
+            break;
+        case CLASS_DRUID:
+        case CLASS_SHAMAN:
+        case CLASS_PALADIN:
+            if (target->GetMaxPower(target->getPowerType()) > target->GetMaxHealth())
+            {
+                if (CanCast(GBOW,target) && !HasAuraName(target,BOW) && !HasAuraName(target,GBOW) ) return CastSpell(GBOW,target,false);
+                else if (CanCast(GBOK,target) && !HasAuraName(target,BOK) && !HasAuraName(target,GBOK) ) return CastSpell(GBOK,target,false);
+                else if (CanCast(GBOS,target) && !HasAuraName(target,BOS) && !HasAuraName(target,GBOS) ) return CastSpell(GBOS,target,false);
+                else if (CanCast(GBOM,target) && !HasAuraName(target,BOM) && !HasAuraName(target,GBOM) ) return CastSpell(GBOM,target,false);
+            }
+            else if (target->GetUnitDodgeChance() + target->GetUnitParryChance() > 40)
+            {
+                if (CanCast(GBOS,target) && !HasAuraName(target,BOS) && !HasAuraName(target,GBOS) ) return CastSpell(GBOS,target,false);
+                else if (CanCast(GBOK,target) && !HasAuraName(target,BOK) && !HasAuraName(target,GBOK) ) return CastSpell(GBOK,target,false);
+                else if (CanCast(GBOM,target) && !HasAuraName(target,BOM) && !HasAuraName(target,GBOM) ) return CastSpell(GBOM,target,false);
+                else if (CanCast(GBOW,target) && !HasAuraName(target,BOW) && !HasAuraName(target,GBOW) ) return CastSpell(GBOW,target,false);
+            }
+            else
+            {
+                if (CanCast(GBOK,target) && !HasAuraName(target,BOK) && !HasAuraName(target,GBOK) ) return CastSpell(GBOK,target,false);
+                else if (CanCast(GBOM,target) && !HasAuraName(target,BOM) && !HasAuraName(target,GBOM) ) return CastSpell(GBOM,target,false);
+                else if (CanCast(GBOW,target) && !HasAuraName(target,BOW) && !HasAuraName(target,GBOW) ) return CastSpell(GBOW,target,false);
+                else if (CanCast(GBOS,target) && !HasAuraName(target,BOS) && !HasAuraName(target,GBOS) ) return CastSpell(GBOS,target,false);
+            }
+            break;
+
+        default:
+                if (CanCast(GBOK,target) && !HasAuraName(target,BOK) && !HasAuraName(target,GBOK) ) return CastSpell(GBOK,target,false);
+                else if (CanCast(GBOM,target) && !HasAuraName(target,BOM) && !HasAuraName(target,GBOM) ) return CastSpell(GBOM,target,false);
+                else if (CanCast(GBOW,target) && !HasAuraName(target,BOW) && !HasAuraName(target,GBOW) ) return CastSpell(GBOW,target,false);
+                else if (CanCast(GBOS,target) && !HasAuraName(target,BOS) && !HasAuraName(target,GBOS) ) return CastSpell(GBOS,target,false);
+                break;
+    }
+#pragma endregion
+
+    return false;
+}
+
+bool PlayerbotPaladinAI::RezTarget (Unit *target)
+{
+    if(!target || target->isAlive()) return false;
+    Player *m_bot = GetPlayerBot();
+    if (target->IsNonMeleeSpellCasted(true)) { return false; } //Already resurrected
+    if (m_bot->isInCombat()) { return false; }
+
+    if (!CanCast(REDEMPTION,target)) return false;
+    std::string msg = "Rezzing ";
+    msg += target->GetName();
+    GetPlayerBot()->Say(msg, LANG_UNIVERSAL);
+    return CastSpell(REDEMPTION, target, false);
+}
+
+bool PlayerbotPaladinAI::ChangeAura(uint32 aura)
+{
+    Player *m_bot = GetPlayerBot();
+    if(!aura) return false;
+
+    if(!CanCast(aura,m_bot)) return false;
+
+    if(m_bot->HasAura(aura))
+    {
+        if (aura == DEVOTION_AURA)
+        {
+            if (ChangeAura(FIRE_AURA)) return true;
+            if (ChangeAura(FROST_AURA)) return true;
+            if (ChangeAura(SHADOW_AURA)) return true;
+            return true;
+        }
+        else return ChangeAura(DEVOTION_AURA);
+    }
+    return CastSpell(aura,m_bot,false);
+}
diff --git a/src/server/game/AI/Bots/PlayerbotPaladinAI.h b/src/server/game/AI/Bots/PlayerbotPaladinAI.h
new file mode 100644
index 0000000..72c6beb
--- /dev/null
+++ b/src/server/game/AI/Bots/PlayerbotPaladinAI.h
@@ -0,0 +1,70 @@
+#ifndef _PLAYERBOTPALADINAI_H
+#define _PLAYERBOTPALADINAI_H
+
+#include "PlayerbotClassAI.h"
+#include "SharedDefines.h"
+
+class PlayerbotPaladinAI : PlayerbotClassAI
+{
+    public:
+        PlayerbotPaladinAI(Player *const master, Player *const bot, PlayerbotAI *const ai);
+        virtual ~PlayerbotPaladinAI();
+
+        virtual void LoadSpells();
+
+        //all combat actions go here
+        void DoNextCombatManeuver(Unit *);
+
+        //all non combat actions go here, ex buffs, heals, rezzes
+        void DoNonCombatActions();
+
+        //buff a specific player, usually a real PC who is not in group
+        bool BuffPlayer(Unit *target);
+
+        //Heals the target based off its HP
+        bool HealTarget(Unit *target, uint8 hp);
+
+        //Cures the target
+        bool CureTarget(Unit *target);
+
+        bool RezTarget (Unit *target);
+
+        bool ChangeAura(uint32 aura);
+
+    private:
+        //heals
+        uint32 FLASH_OF_LIGHT, HOLY_LIGHT, HOLY_SHOCK, REZZ, CLEANSE, LOH, SACRED_SHIELD, BEACON_OF_LIGHT, DIVINE_FAVOR, REDEMPTION;
+
+        //Damages
+        uint32 JOL, JOW, JOJ, HAMMER_OF_WRATH, EXORCISM, HOLY_WRATH, CONSECRATION, AVENGERS_SHIELD, SHIELD_OF_RIGHTEOUSNESS, HOTR, CRUSADER_STRIKE, DIVINE_STORM;
+
+        //CC
+        uint32 HAMMER_OF_JUSTICE, REPENTANCE;
+
+        //Self buffs
+        uint32 SOL, SOW, SOR, SOC, SOV, DIVINE_PLEA, HOLY_SHIELD, RIGHTEOUS_FURY, DIVINE_SHIELD, AVENGING_WRATH;
+
+        //AURAS
+        uint32 DEVOTION_AURA, RETRIBUTION_AURA, CONCENTRATION_AURA, FIRE_AURA, FROST_AURA, SHADOW_AURA, CRUSADER_AURA ;
+
+        //Blessings
+        uint32 BOW, BOM, BOS, BOK, GBOW, GBOM, GBOS, GBOK;
+
+        //Hands
+        uint32 HOF, HOR, HOS, HOP, DIVINE_SACRIFICE;
+
+        //Taunt
+        uint32 RIGHTEOUS_DEFENSE;
+
+        uint32 FORBEARANCE;
+
+        uint32 TALENT_HOLY, TALENT_PROT, TALENT_RETRI;
+
+		//procs
+		uint32 AOW;
+
+};
+
+
+
+#endif
diff --git a/src/server/game/AI/Bots/PlayerbotPriestAI.cpp b/src/server/game/AI/Bots/PlayerbotPriestAI.cpp
new file mode 100644
index 0000000..729316f
--- /dev/null
+++ b/src/server/game/AI/Bots/PlayerbotPriestAI.cpp
@@ -0,0 +1,400 @@
+/*
+Name : PlayerbotPriest.cpp
+Complete: maybe around 55%
+
+Limitations:    - Talent build decision is made by key talent spells, which makes them viable only after level 50-ish.. Behaviour does not change though..
+                - Holy And Disc builds do not cast any offensive spells requiring cast time..(To compensate for the fact that Healing decision is not that intelligent)
+                - Priest breaks her own CCs.. Need a check for bots to not attack CC ed mobs..
+                - Wand usage is not very smooth..
+
+
+Authors : SwaLLoweD
+Version : 0.40
+*/
+#include "PlayerbotPriestAI.h"
+
+class PlayerbotAI;
+PlayerbotPriestAI::PlayerbotPriestAI(Player *const master, Player *const bot, PlayerbotAI *const ai): PlayerbotClassAI(master, bot, ai)
+{
+    foodDrinkSpamTimer = 0;
+    LoadSpells();
+}
+PlayerbotPriestAI::~PlayerbotPriestAI(){}
+
+void PlayerbotPriestAI::LoadSpells() {
+    PlayerbotAI *ai = GetAI();
+    if (!ai) return;
+    #pragma region SpellId Fill
+    //heals
+    RENEW = ai->getSpellIdExact("Renew");
+    FLASH_HEAL = ai->getSpellIdExact("Flash Heal");
+    if (!FLASH_HEAL) FLASH_HEAL = ai->getSpellIdExact("Lesser Heal");
+    HEAL = ai->getSpellIdExact("Greater Heal");
+    if (!HEAL) HEAL = ai->getSpellIdExact("Heal");
+    if (!HEAL) HEAL = FLASH_HEAL;
+    BINDING_HEAL = ai->getSpellIdExact("Binding Heal");
+    PO_MENDING = ai->getSpellIdExact("Prayer of Mending");
+    DESPERATE_PRAYER = ai->getSpellIdExact("Desperate Prayer");
+    PO_HEALING = ai->getSpellIdExact("Prayer of Healing");
+    CIRCLE_OF_HEALING = ai->getSpellIdExact("Circle of Healing");
+    DIVINE_HYMN = ai->getSpellIdExact("Divine Hymn");
+    RESURRECTION = ai->getSpellIdExact("Resurrection");
+    HYMN_OF_HOPE = ai->getSpellIdExact("Hymn of Hope");
+    CURE_DISEASE = ai->getSpellIdExact("Abolish Disease");
+    if (!CURE_DISEASE) CURE_DISEASE = ai->getSpellIdExact("Abolish Disease");
+    DISPEL_MAGIC = ai->getSpellIdExact("Dispel Magic");
+    MASS_DISPEL = ai->getSpellIdExact("Mass Dispel");
+
+    //Holy Offensive
+    SMITE = ai->getSpellIdExact("Smite");
+    HOLY_FIRE = ai->getSpellIdExact("Holy Fire");
+    PENANCE = ai->getSpellIdExact("Penance");
+    HOLY_NOVA = ai->getSpellIdExact("Holy Nova");
+
+    //Shadow Offensive
+    MIND_BLAST = ai->getSpellIdExact("Mind Blast");
+    SW_PAIN = ai->getSpellIdExact("Shadow Word: Pain");
+    DEVOURING_PLAGUE = ai->getSpellIdExact("Devouring Plague");
+    MIND_FLAY = ai->getSpellIdExact("Mind Flay");
+    VAMPIRIC_EMBRACE = ai->getSpellIdExact("Vampiric Embrace");
+    VAMPIRIC_TOUCH = ai->getSpellIdExact("Vampiric Touch");
+    SW_DEATH = ai->getSpellIdExact("Shadow Word: Death");
+    MIND_SEAR = ai->getSpellIdExact("Mind Sear");
+    MANA_BURN = ai->getSpellIdExact("Mana Burn");
+    SHADOWFIEND = ai->getSpellIdExact("Shadowfiend");
+
+    //CC - Breaker
+    PSYCHIC_SCREAM = ai->getSpellIdExact("Psychic Scream");
+    PSYCHIC_HORROR = ai->getSpellIdExact("Psychic Horror");
+    MIND_SOOTHE = ai->getSpellIdExact("Mind Soothe");
+    SHACKLE_UNDEAD = ai->getSpellIdExact("Shackle Undead");
+    SILENCE = ai->getSpellIdExact("Silence");
+    MIND_CONTROL = ai->getSpellIdExact("Mind Control");
+
+    //buffs
+    PW_SHIELD = ai->getSpellIdExact("Power Word: Shield");
+    INNER_FIRE = ai->getSpellIdExact("Inner Fire");
+    GUARDIAN_SPIRIT = ai->getSpellIdExact("Guardian Spirit");
+    FADE = ai->getSpellIdExact("Fade");
+    INNER_FOCUS = ai->getSpellIdExact("Inner Focus");
+    POWER_INFUSION = ai->getSpellIdExact("Power Infusion");
+    PAIN_SUPPRESSION = ai->getSpellIdExact("Pain Suppression");
+    SHADOWFORM = ai->getSpellIdExact("Shadowform");
+    DISPERSION = ai->getSpellIdExact("Dispersion");
+    LIGHTWELL = ai->getSpellIdExact("Lightwell");
+
+    PW_FORTITUDE = ai->getSpellIdExact("Power Word: Fortitude");
+    DIVINE_SPIRIT = ai->getSpellIdExact("Divine Spirit");
+    SHADOW_PROTECTION = ai->getSpellIdExact("Shadow Protection");
+    PO_FORTITUDE = ai->getSpellIdExact("Prayer of Fortitude");
+    PO_SPIRIT = ai->getSpellIdExact("Prayer of Spirit");
+    PO_SHADOW_PROTECTION = ai->getSpellIdExact("Prayer of Shadow Protection");
+    FEAR_WARD = ai->getSpellIdExact("Fear Ward");
+
+    SHOOT = ai->getSpellIdExact("Shoot");
+
+    TALENT_DISC = PAIN_SUPPRESSION;
+    TALENT_HOLY = CIRCLE_OF_HEALING;
+    TALENT_SHADOW = SHADOWFORM;
+
+    uint8 talentCounter = 0;
+    if (TALENT_DISC) talentCounter++;
+    if (TALENT_HOLY) talentCounter++;
+    if (TALENT_SHADOW) talentCounter++;
+    if (talentCounter > 1) { TALENT_DISC = 0; TALENT_HOLY = 0; TALENT_SHADOW = 0; } //Unreliable Talent detection.
+    #pragma endregion
+}
+
+void PlayerbotPriestAI::DoNextCombatManeuver(Unit *pTarget)
+{
+    if (!pTarget || pTarget->isDead()) return;
+    PlayerbotAI *ai = GetAI();
+    if (!ai) return;
+    Player *m_bot = GetPlayerBot();
+    if (!m_bot || m_bot->isDead()) return;
+    Unit *pVictim = pTarget->getVictim();
+    Unit *m_tank = FindMainTankInRaid(GetMaster());
+    if (!m_tank && m_bot->GetGroup() && GetMaster()->GetGroup() != m_bot->GetGroup()) { FindMainTankInRaid(m_bot); }
+    if (!m_tank) { m_tank = m_bot; }
+    uint32 masterHP = GetMaster()->GetHealth()*100 / GetMaster()->GetMaxHealth();
+    float pDist = m_bot->GetDistance(pTarget);
+    uint8 pThreat = GetThreatPercent(pTarget);
+
+    uint8 reqHeal = 0;
+    uint8 OwnPartyHP = GetHealthPercentRaid(m_bot, reqHeal);
+
+    /*
+    switch(ai->GetScenarioType())
+    {
+        case SCENARIO_DUEL:
+            (ai->HasAura(SCREAM, pTarget) && ai->GetHealthPercent() < 60 && CastSpell(HEAL)) ||
+            CastSpell(PAIN) ||
+            (ai->GetHealthPercent() < 80 && CastSpell(RENEW)) ||
+            (m_bot->GetDistance(pTarget) <= 5 && CastSpell(SCREAM)) ||
+            CastSpell(MIND_BLAST) ||
+            (ai->GetHealthPercent() < 20 && CastSpell(FLASH_HEAL)) ||
+            CastSpell(SMITE);
+            return;
+    }*/
+
+
+    //------- Non Duel combat ----------
+
+
+    // Cast CC breakers if any match found (include any dispels first)  does not work yet
+    //uint32 ccSpells[6] = { DISPEL_MAGIC, CURE_DISEASE, DISPERSION, R_ESCAPE_ARTIST, R_EVERY_MAN_FOR_HIMSELF, R_WILL_OF_FORSAKEN, R_STONEFORM };
+    //if (ai->GetManaPercent() < 35) { ccSpells[0] = 0; ccSpells[1] = 0; } //We dont have any mana to waste...
+    //if (castSelfCCBreakers(ccSpells)) {  } // Most of them don't trigger gcd
+
+
+
+    #pragma region Choose Actions
+    // Choose actions accoring to talents
+    if (m_tank->GetGUID() == m_bot->GetGUID()) // Hey! I am Main Tank
+    {
+        m_role = BOT_ROLE_DPS_RANGED;
+    }
+    else if (TALENT_SHADOW) {
+        if ((ai->GetHealthPercent() <= 40 || masterHP <40 ) && (ai->GetManaPercent() >= 30)) { m_role = BOT_ROLE_SUPPORT; }
+        else if (OwnPartyHP < 40 && ai->GetManaPercent() >= 40) {m_role = BOT_ROLE_SUPPORT;}
+        else { m_role = BOT_ROLE_DPS_RANGED; }
+    }
+    else { m_role = BOT_ROLE_SUPPORT; } //Unknown build or low level.. Mainly attack
+
+    // if i am under attack and if i am not tank or offtank: change target if needed
+    if (m_tank->GetGUID() != m_bot->GetGUID() && isUnderAttack() )
+    {
+        // Keep hitting but reduce threat
+        if (CastSpell(FADE,m_bot)) { return; }
+        else if (CastSpell(PAIN_SUPPRESSION,m_bot)) { return; }
+        //else if (m_bot->getRace() == (uint8) RACE_NIGHTELF && CastSpell(R_SHADOWMELD,m_bot)) { return; }
+        else //I cannot reduce threat so
+        {
+            if (pVictim && pVictim->GetGUID() == m_bot->GetGUID() && pDist <= 2) {  } // My target is almost up to me, no need to search
+            else //Have to select nearest target
+            {
+                Unit *curAtt = GetNearestAttackerOf(m_bot);
+                if (curAtt && curAtt->GetGUID() != pTarget->GetGUID())
+                {
+                    m_bot->SetSelection(curAtt->GetGUID());
+                    //ai->AddLootGUID(curAtt->GetGUID());
+                    DoNextCombatManeuver(curAtt); //Restart new update to get variables fixed..
+                    return;
+                }
+            }
+            //my target is attacking me
+        }
+    }
+    #pragma endregion
+
+    TakePosition(pTarget);
+    // If there's a cast stop
+    if (m_bot->HasUnitState(UNIT_STAT_CASTING)) { return; }
+
+
+    if (m_role == BOT_ROLE_DPS_RANGED && CastSpell(SHADOWFORM,m_bot)) { return; }
+    if (m_role == BOT_ROLE_SUPPORT && ai->GetForm() == FORM_SHADOW) { m_bot->RemoveAurasDueToSpell(SHADOWFORM); }
+
+    //Buff
+    if (CastSpell(INNER_FIRE,m_bot)) { } //nogcd
+    if (CastSpell(POWER_INFUSION,m_bot)) { return; }
+    if (m_bot->getRace() == (uint8) RACE_TROLL && CastSpell(R_BERSERKING,m_bot)) {} //no GCD
+    if (m_bot->getRace() == (uint8) RACE_ORC && CastSpell(R_BLOOD_FURY,m_bot)) {} //no GCD
+
+
+    if (ai->GetForm() != FORM_SHADOW)
+    {
+        if (PO_MENDING && ai->GetHealthPercent(*m_tank) < 90 && !HasAuraName(m_tank, "Prayer of Mending") && CastSpell(PO_MENDING,m_tank)) { return; } //MEND tank first
+        if (DoSupportRaid(m_bot)) { return; }
+        //heal pets and bots
+        Unit *target = DoSelectLowestHpFriendly(30, 1000);
+        if(target && target->isAlive() && HealTarget(target, target->GetHealth()*100 / target->GetMaxHealth()) ) { return; }
+    }
+    if (ai->GetForm() == FORM_SPIRITOFREDEMPTION) { return; } //You're dead..
+
+
+    //PROTECT
+    if (m_tank->GetGUID() != m_bot->GetGUID() && pVictim && pVictim->GetGUID() == m_bot->GetGUID() )
+    {
+        if (PSYCHIC_HORROR && CastSpell(PSYCHIC_HORROR, pTarget)) { return; }
+        if (PSYCHIC_SCREAM && CastSpell(PSYCHIC_SCREAM, pTarget)) { return; }
+        if (SHACKLE_UNDEAD && pTarget->GetCreatureType() == (uint32) CREATURE_TYPE_UNDEAD && CastSpell(SHACKLE_UNDEAD, pTarget)) { return; }
+        //if (m_bot->getRace() == (uint8) RACE_NIGHTELF && isUnderAttack() && CastSpell(R_SHADOWMELD, m_bot)) { return; }
+    }
+    if (ai->GetHealthPercent() < 20 && CastSpell(DESPERATE_PRAYER)) { return; }
+    if (ai->GetHealthPercent() < 30 && CastSpell(PW_SHIELD)) { return; }
+    if (m_bot->getRace() == (uint8) RACE_DWARF && ai->GetHealthPercent() < 75 && CastSpell(R_STONEFORM,m_bot)) { } //no gcd
+    if (ai->GetHealthPercent() < 60 && CastSpell(PAIN_SUPPRESSION,m_bot)) { return; }
+    if (ai->GetHealthPercent() < 50 && ai->GetManaPercent() < 10 && CastSpell(DISPERSION,m_bot)) { return; }
+    if (ai->GetManaPercent() < 10 && CastSpell (HYMN_OF_HOPE, m_bot)) { return; }
+    if (m_bot->getRace() == (uint8) RACE_TAUREN && pDist < 8 && CastSpell(R_WAR_STOMP, pTarget)) { return; } //no gcd but is cast
+
+
+    //Break Spells
+    if (m_bot->getRace() == (uint8) RACE_BLOODELF && pDist < 8 && (pTarget->IsNonMeleeSpellCasted(true) || ai->GetManaPercent() < 40) && CastSpell(R_ARCANE_TORRENT, pTarget)) { } //no gcd
+    else if (pTarget->IsNonMeleeSpellCasted(true) && CastSpell(SILENCE, pTarget)) { return; }
+
+
+    // If at threat limit, try to reduce threat
+    if (pThreat > threatThreshold && m_tank->GetGUID() != m_bot->GetGUID() && !isUnderAttack())
+    {
+        if (m_tank->getVictim() && m_tank->getVictim()->GetGUID() != pTarget->GetGUID()) // I am attacking wrong target!!
+        {
+            m_bot->SetSelection(m_tank->getVictim()->GetGUID());
+            return;
+        }
+        else
+        {
+            if (CastSpell(PAIN_SUPPRESSION,m_bot)) { return; } //Lets see if we can manage
+            else if (CastSpell(FADE,m_bot)) { return; }
+            else if (m_bot->FindCurrentSpellBySpellId(SHOOT)) { m_bot->InterruptNonMeleeSpells( true, SHOOT ); return; } //Disable wand
+            else { return; } //use no spells and wait threat to be reduced
+        }
+    }
+
+    //WAND
+    if (ai->GetManaPercent() < 5 ||
+        (m_role != BOT_ROLE_DPS_RANGED && SHOOT && !m_bot->FindCurrentSpellBySpellId(SHOOT) && ai->CastSpell(SHOOT,pTarget) )
+        ) { return; } //Start autoshot
+
+    // Continue spell attacking if theres excess mana (for healers)
+    if (m_role == BOT_ROLE_SUPPORT && ai->GetManaPercent() < offensiveSpellThreshold) { return; }
+
+    if (ai->GetHealthPercent(*pTarget) > 95) { return; } // dont dps too early
+
+    if (CastSpell(VAMPIRIC_EMBRACE,pTarget)) { return; }
+    if (CastSpell(VAMPIRIC_TOUCH,pTarget)) { return; }
+    if (CastSpell(DEVOURING_PLAGUE,pTarget)) { return; }
+    if (CastSpell(SW_PAIN,pTarget)) { return; }
+
+    if (ai->GetForm() == FORM_SHADOW)
+    {
+        if (castDispel(DISPEL_MAGIC, pTarget)) { return; } //Dispel buffs if any
+        if (CastSpell(MIND_BLAST,pTarget)) { return; }
+        if (CastSpell(MIND_FLAY,pTarget)) { return; }
+        if (isUnderAttack(m_tank,4) && CastSpell(MIND_SEAR,pTarget)) { return; }
+    }
+
+    if (ai->GetForm() == FORM_NONE && m_role == BOT_ROLE_DPS_RANGED)
+    {
+        if (CastSpell(PENANCE,pTarget)) { return; }
+        if (CastSpell(MIND_BLAST,pTarget)) { return; }
+        if (CastSpell(HOLY_FIRE,pTarget)) { return; }
+        if (CastSpell(SMITE,pTarget)) { return; }
+        if (isUnderAttack(m_tank,4) && CastSpell(MIND_SEAR,pTarget)) { return; }
+        if (isUnderAttack(m_tank,4) && CastSpell(HOLY_NOVA,pTarget)) { return; }
+    }
+
+     // drink potion if support / healer (Other builds simply overuse mana and waste mana pots)
+    if(ai->GetManaPercent() < 5 && (m_role == BOT_ROLE_SUPPORT || m_role == BOT_ROLE_HEALER) )
+    {
+        Item *pItem = ai->FindPotion();
+        if(pItem != NULL)
+        {
+            if (pItem->GetSpell() && m_bot->HasSpellCooldown(pItem->GetSpell()) ) { return; } //pot is in cooldown
+            ai->UseItem(*pItem);
+        }
+    }
+} //end DoNextCombatManeuver
+
+void PlayerbotPriestAI::DoNonCombatActions()
+{
+    PlayerbotAI *ai = GetAI();
+    Player *m_bot = GetPlayerBot();
+    if (!m_bot || !ai || m_bot->isDead()) { return; }
+
+    //If Casting or Eating/Drinking return
+    if (m_bot->HasUnitState(UNIT_STAT_CASTING)) { return; }
+    if (m_bot->getStandState() == UNIT_STAND_STATE_SIT) { return; }
+
+    //Own Buffs
+    if (CastSpell(INNER_FIRE,m_bot)) { return; }
+
+    //buff and heal raid
+    if (DoSupportRaid(m_bot)) { return; }
+
+    //heal pets and bots
+    Unit *target = DoSelectLowestHpFriendly(30, 1000);
+    if (target && target->isAlive() && HealTarget(target, target->GetHealth()*100 / target->GetMaxHealth())) { return; }
+
+    //mana/hp check
+    //Don't bother with eating, if low on hp, just let it heal themself
+    if (m_bot->getRace() == (uint8) RACE_UNDEAD_PLAYER && ai->GetHealthPercent() < 75 && CastSpell(R_CANNIBALIZE,m_bot)) { return; }
+    if (m_bot->GetHealth() < m_bot->GetMaxHealth() && CastSpell(FLASH_HEAL,m_bot)) { return; }
+    if (ai->GetManaPercent() < 50) { ai->Feast(); }
+
+} //end DoNonCombatActions
+
+
+bool PlayerbotPriestAI::HealTarget(Unit *target, uint8 hp)
+{
+    if(!target || target->isDead()) return false;
+    Player *m_bot = GetPlayerBot();
+
+    if (hp < 30 && m_bot->isInCombat() && CastSpell(GUARDIAN_SPIRIT,target)) { } //nogcd
+    if (hp < 30 && CastSpell(PENANCE,target,true,false,true)) { return true; } //Channeling Dual purpose
+    if (hp < 35 && m_bot->isInCombat() && CastSpell(PW_SHIELD,target)) { return true; } //Check weakened soul
+    if (hp < 80 && hp > 50 && GetAI()->GetHealthPercent() < 80 && CastSpell (BINDING_HEAL,target)) { return true; }
+    if (hp < 85 && CastSpell(RENEW,target)) { return true; }
+    if (hp < 40 && GetPlayerBot()->getRace() == (uint8) RACE_DRAENEI && CastSpell(R_GIFT_OF_NAARU,target)) { return true; } // no GCD, but has cast
+    if (hp < 55 && hp > 35 && CastSpell(HEAL,target)) { return true; }
+    if (hp < 75 && CastSpell(FLASH_HEAL,target)) { return true; }
+
+    return false;
+} //end HealTarget
+
+bool PlayerbotPriestAI::HealGroup (Unit *target, uint8 hp, uint8 &countNeedHeal)
+{
+    Player *m_bot = GetPlayerBot();
+    if (countNeedHeal < 2) { return false; }
+    Unit *rTarget = DoSelectLowestHpFriendly(30,500);
+    if (!rTarget || rTarget->isDead() || rTarget->GetHealth() * 100 / rTarget->GetMaxHealth() > 80 ) { return false; }
+
+    // if (hp < 75 && CastSpell(PO_MENDING, rTarget)) { return true; } //save this for tank
+    if (hp < 35 && m_bot->isInCombat() && CastSpell(DIVINE_HYMN,rTarget)) { /*GetAI()->SetIgnoreUpdateTime(9);*/ return true; }
+    if (hp < 70 && CastSpell(CIRCLE_OF_HEALING,rTarget)) { return true; }
+    if (hp < 75 && hp > 30 && countNeedHeal > 4 && CastSpell(PO_HEALING)) { return true; }
+    if (hp < 65 && CastSpell(HOLY_NOVA,rTarget,true,false,true)) { return true; }
+
+    return false;
+}
+
+//Cures the target
+bool PlayerbotPriestAI::CureTarget(Unit *target)
+{
+    Player *m_bot = GetPlayerBot();
+
+    if(!target || target->isDead()) { return false; }
+    if (castDispel(DISPEL_MAGIC, target,true,false,true)) return true;
+    if (castDispel(CURE_DISEASE, target)) return true;
+    // if(HasAuraName(target, "Venom Spit") || HasAuraName(target, "Poison")) return CastSpell(CURE_POISON, target);
+    return false;
+}
+
+bool PlayerbotPriestAI::RezTarget (Unit *target)
+{
+    if(!target || target->isAlive()) return false;
+    Player *m_bot = GetPlayerBot();
+    if (target->IsNonMeleeSpellCasted(true)) { return false; } //Already resurrected
+    if (m_bot->isInCombat()) { return false; }
+
+    if (!CanCast(RESURRECTION,target)) return false;
+    std::string msg = "Rezzing ";
+    msg += target->GetName();
+   // msg += " with ";
+   // msg += *REZZSpell->SpellName;
+    GetPlayerBot()->Say(msg, LANG_UNIVERSAL);
+    return CastSpell(RESURRECTION, target,false);
+}
+
+bool PlayerbotPriestAI::BuffPlayer(Unit *target)
+{
+    if(!target || target->isDead()) return false;
+
+    return (
+        (!HasAuraName(target, PW_FORTITUDE) && !HasAuraName(target, PO_FORTITUDE) && CastSpell (PW_FORTITUDE, target)) ||
+        (!HasAuraName(target, SHADOW_PROTECTION) && !HasAuraName(target, PO_SHADOW_PROTECTION) && CastSpell(SHADOW_PROTECTION, target)) ||
+        (!HasAuraName(target, DIVINE_SPIRIT) && !HasAuraName(target, PO_SPIRIT) && CastSpell (DIVINE_SPIRIT, target)) ||
+        (!HasAuraName(target, FEAR_WARD) && CastSpell (FEAR_WARD, target))
+         );
+}
diff --git a/src/server/game/AI/Bots/PlayerbotPriestAI.h b/src/server/game/AI/Bots/PlayerbotPriestAI.h
new file mode 100644
index 0000000..9670986
--- /dev/null
+++ b/src/server/game/AI/Bots/PlayerbotPriestAI.h
@@ -0,0 +1,59 @@
+#ifndef _PLAYERBOTPRIESTAI_H
+#define _PLAYERBOTPRIESTAI_H
+
+#include "PlayerbotClassAI.h"
+
+//class Player;
+
+class PlayerbotPriestAI : PlayerbotClassAI
+{
+public:
+    PlayerbotPriestAI(Player *const master, Player *const bot, PlayerbotAI *const ai);
+    virtual ~PlayerbotPriestAI();
+
+    virtual void LoadSpells();
+
+    //all combat actions go here
+    void DoNextCombatManeuver(Unit *);
+
+    //all non combat actions go here, ex buffs, heals, rezzes
+    void DoNonCombatActions();
+
+    //buff a specific player, usually a real PC who is not in group
+    bool BuffPlayer(Unit *target);
+
+    //Heals the target based off its HP
+    bool HealTarget(Unit *target, uint8 hp);
+
+    bool HealGroup (Unit *target, uint8 hp, uint8 &countNeedHeal);
+
+    //Cures the target
+    bool CureTarget(Unit *target);
+
+    bool RezTarget (Unit *target);
+
+private:
+    //heals
+    uint32 RENEW, FLASH_HEAL, HEAL, BINDING_HEAL, PO_MENDING, DESPERATE_PRAYER, PO_HEALING, CIRCLE_OF_HEALING, DIVINE_HYMN, RESURRECTION, HYMN_OF_HOPE, CURE_DISEASE, DISPEL_MAGIC, MASS_DISPEL;
+
+    //Holy Offensive
+    uint32 SMITE, HOLY_FIRE, PENANCE, HOLY_NOVA;
+
+    //Shadow Offensive
+    uint32 MIND_BLAST, SW_PAIN, DEVOURING_PLAGUE, MIND_FLAY, VAMPIRIC_EMBRACE, VAMPIRIC_TOUCH, SW_DEATH, MIND_SEAR, MANA_BURN, SHADOWFIEND;
+
+    //CC - Breaker
+    uint32 PSYCHIC_SCREAM, PSYCHIC_HORROR, MIND_SOOTHE, SHACKLE_UNDEAD, SILENCE, MIND_CONTROL;
+
+    //buffs
+    uint32 PW_SHIELD, INNER_FIRE, GUARDIAN_SPIRIT, FADE, INNER_FOCUS, POWER_INFUSION, PAIN_SUPPRESSION, SHADOWFORM, DISPERSION, LIGHTWELL,
+        PW_FORTITUDE, DIVINE_SPIRIT, SHADOW_PROTECTION, PO_FORTITUDE, PO_SPIRIT, PO_SHADOW_PROTECTION, FEAR_WARD;
+
+    uint32 SHOOT;
+
+    uint32 TALENT_DISC, TALENT_HOLY, TALENT_SHADOW;
+};
+
+
+
+#endif
diff --git a/src/server/game/AI/Bots/PlayerbotRogueAI.cpp b/src/server/game/AI/Bots/PlayerbotRogueAI.cpp
new file mode 100644
index 0000000..4dff2db
--- /dev/null
+++ b/src/server/game/AI/Bots/PlayerbotRogueAI.cpp
@@ -0,0 +1,266 @@
+#include "PlayerbotRogueAI.h"
+#include "Spell.h"
+class PlayerbotAI;
+PlayerbotRogueAI::PlayerbotRogueAI(Player *const master, Player *const bot, PlayerbotAI *const ai): PlayerbotClassAI(master, bot, ai)
+{
+    foodDrinkSpamTimer = 0;
+    LoadSpells();
+}
+PlayerbotRogueAI::~PlayerbotRogueAI(){}
+
+void PlayerbotRogueAI::LoadSpells() {
+    PlayerbotAI *ai = GetAI();
+    if (!ai) return;
+    #pragma region SpellId Fill
+    //Damage spells
+    BACKSTAB = ai->getSpellIdExact("Backstab");
+    SINISTER_STRIKE = ai->getSpellIdExact("Sinister Strike");
+    MUTILATE = ai->getSpellIdExact("Mutilate");
+    HEMORRHAGE = ai->getSpellIdExact("Hemorrhage");
+    GHOSTLY_STRIKE = ai->getSpellIdExact("Ghostly Strike");
+    RIPOSTE = ai->getSpellIdExact("Riposte");
+    SHIV = ai->getSpellIdExact("Shiv");
+    FAN_OF_KNIVES = ai->getSpellIdExact("Fan of Knives");
+
+    //Finishing Moves
+    EVISCERATE = ai->getSpellIdExact("Eviscerate");
+    RUPTURE = ai->getSpellIdExact("Rupture");
+    KIDNEY_SHOT = ai->getSpellIdExact("Kidney Shot");
+    ENVENOM = ai->getSpellIdExact("Envenom");
+    SLICE_AND_DICE = ai->getSpellIdExact("Slice and Dice");
+    EXPOSE_ARMOR = ai->getSpellIdExact("Expose Armor");
+    DEADLY_THROW = ai->getSpellIdExact("Deadly Throw");
+
+    //Buffs
+    STEALTH = ai->getSpellIdExact("Stealth");
+    VANISH = ai->getSpellIdExact("Vanish");
+    EVASION = ai->getSpellIdExact("Evasion");
+    CLOAK_OF_SHADOWS = ai->getSpellIdExact("Cloak of Shadows");
+    SPRINT = ai->getSpellIdExact("Sprint");
+    COLD_BLOOD = ai->getSpellIdExact("Cold Blood");
+    HUNGER_FOR_BLOOD = ai->getSpellIdExact("Hunger for Blood");
+    BLADE_FLURRY = ai->getSpellIdExact("Blade Flurry");
+    ADRENALINE_RUSH = ai->getSpellIdExact("Adrenaline Rush");
+    KILLING_SPREE = ai->getSpellIdExact("Killing Spree");
+    SHADOW_DANCE = ai->getSpellIdExact("Shadow Dance");
+
+    //Openers
+    CHEAP_SHOT = ai->getSpellIdExact("Cheap Shot");
+    GARROTE = ai->getSpellIdExact("Garrote");
+    AMBUSH = ai->getSpellIdExact("Ambush");
+
+    //Others
+    GOUGE = ai->getSpellIdExact("Gouge");
+    BLIND = ai->getSpellIdExact("Blind");
+    DISMANTLE = ai->getSpellIdExact("Dismantle");
+    SAP = ai->getSpellIdExact("Sap");
+    KICK = ai->getSpellIdExact("Kick");
+    PREPARATION = ai->getSpellIdExact("Preparation");
+    PREMEDITATION = ai->getSpellIdExact("Premeditation");
+    SHADOWSTEP = ai->getSpellIdExact("Shadowstep");
+    FEINT = ai->getSpellIdExact("Feint");
+    TRICKS_OF_THE_TRADE = ai->getSpellIdExact("Tricks of the Trade");
+
+    SHOOT = ai->getSpellIdExact("Shoot");
+    THROW = ai->getSpellIdExact("Throw");
+
+    TALENT_ASSASSINATION = MUTILATE;
+    TALENT_COMBAT = ADRENALINE_RUSH;
+    TALENT_SUBTELTY = PREMEDITATION;
+
+    //uint8 talentCounter = 0;
+    //if (TALENT_ASSASSINATION) talentCounter++;
+    //if (TALENT_COMBAT) talentCounter++;
+    //if (TALENT_SUBTELTY) talentCounter++;
+    //if (talentCounter > 1) { TALENT_ASSASSINATION = 0; TALENT_COMBAT = 0; TALENT_SUBTELTY = 0; } //Unreliable Talent detection.
+#pragma endregion
+}
+
+void PlayerbotRogueAI::DoNextCombatManeuver(Unit *pTarget)
+{
+    if (!pTarget || pTarget->isDead()) return;
+    PlayerbotAI *ai = GetAI();
+    if (!ai) return;
+    Player *m_bot = GetPlayerBot();
+    if (!m_bot || m_bot->isDead()) return;
+    Unit *pVictim = pTarget->getVictim();
+    Unit *m_tank = FindMainTankInRaid(GetMaster());
+    if (!m_tank && m_bot->GetGroup() && GetMaster()->GetGroup() != m_bot->GetGroup()) { FindMainTankInRaid(m_bot); }
+    if (!m_tank) { m_tank = m_bot; }
+    uint32 masterHP = GetMaster()->GetHealth()*100 / GetMaster()->GetMaxHealth();
+    float pDist = m_bot->GetDistance(pTarget);
+    uint8 pThreat = GetThreatPercent(pTarget);
+
+    #pragma region Choose Target
+    // Choose actions accoring to talents (ROGUE is always MELEE DPS)
+    m_role = BOT_ROLE_DPS_MELEE;
+    // if i am under attack and if i am not tank or offtank: change target if needed
+    if (m_tank->GetGUID() != m_bot->GetGUID() && isUnderAttack() )
+    {
+        // Keep hitting but reduce threat
+        if (CastSpell(TRICKS_OF_THE_TRADE,m_tank)) { return; }
+        else if (CastSpell(VANISH,m_bot)) { return; }
+        else //I cannot reduce threat so
+        {
+            if (pVictim && pVictim->GetGUID() == m_bot->GetGUID() && pDist <= 2) {  } // My target is almost up to me, no need to search
+            else //Have to select nearest target
+            {
+                Unit *curAtt = GetNearestAttackerOf(m_bot);
+                if (curAtt && curAtt->GetGUID() != pTarget->GetGUID())
+                {
+                    m_bot->SetSelection(curAtt->GetGUID());
+                    DoNextCombatManeuver(curAtt); //Restart new update to get variables fixed..
+                    return;
+                }
+            }
+            //my target is attacking me
+        }
+    }
+    #pragma endregion
+
+    // wait until we actually reach our target b4 we actually do anything
+    if (m_bot->GetDistance(pTarget)>10.0 &&
+        !m_bot->HasAura(STEALTH) &&
+        !m_bot->isInCombat() && CastSpell(STEALTH))
+    { return; }
+
+    TakePosition(pTarget);
+
+    // If there's a cast stop
+    if (m_bot->HasUnitState(UNIT_STAT_CASTING)) { return; }
+
+    // wait until we actually reach our target b4 we actually do anything
+    /*if (GetPlayerBot()->GetDistance(pTarget)>10.0 &&
+        !HasAuraName(GetPlayerBot(),STEALTH) &&
+        !GetPlayerBot()->isInCombat() && CastSpell(STEALTH))
+    { return; }*/
+
+    //Buff
+    if (CastSpell(PREMEDITATION,m_bot)) { return; }
+    if (CastSpell(COLD_BLOOD,m_bot)) { } //no gcd
+
+    //PROTECT UP
+    if (m_tank->GetGUID() != m_bot->GetGUID() && pVictim && pVictim->GetGUID() == m_bot->GetGUID() )
+    {
+        if (CastSpell(TRICKS_OF_THE_TRADE,m_tank)) { return; }
+        if (CastSpell(FEINT,m_bot)) { return; }
+        if (CastSpell(VANISH,m_bot)) { return; }
+    }
+    if (isUnderAttack() && pDist <= MELEE_RANGE && ai->GetHealthPercent() <= 85 && CastSpell(EVASION, m_bot)) { } //no GCD
+    if (ai->GetHealthPercent() <= 55 && CastSpell(CLOAK_OF_SHADOWS, m_bot)) { return; }
+    if (isUnderAttack() && ai->GetHealthPercent() <= 65 && CastSpell(GOUGE, m_bot)) { return; }
+    if (isUnderAttack() && ai->GetHealthPercent() <= 45 && CastSpell(BLIND, m_bot)) { return; }
+    if (m_bot->getRace() == (uint8) RACE_DWARF && ai->GetHealthPercent() < 75 && CastSpell(R_STONEFORM,m_bot)) { } //no gcd
+
+    //Break spells
+    if (m_bot->getRace() == (uint8) RACE_BLOODELF && pDist < 8 && (pTarget->IsNonMeleeSpellCasted(true) || ai->GetManaPercent() < 40) && CastSpell(R_ARCANE_TORRENT, pTarget)) { } //no gcd
+    else if (pTarget->IsNonMeleeSpellCasted(true) && CastSpell(KICK, pTarget)) { return; }
+    else if (pTarget->IsNonMeleeSpellCasted(true) && CastSpell(GOUGE, pTarget)) { return; }
+    else if (pTarget->IsNonMeleeSpellCasted(true) && m_bot->GetComboPoints() >= 1 && CastSpell(KIDNEY_SHOT, pTarget)) { return; }
+
+    //Transfer threat
+    if (m_tank->GetGUID() != m_bot->GetGUID() && CastSpell(TRICKS_OF_THE_TRADE,m_tank)) { return; }
+	if (m_bot->getRace() == (uint8) RACE_TROLL && CastSpell(R_BERSERKING,m_bot)) {} //no GCD
+    if (m_bot->getRace() == (uint8) RACE_ORC && CastSpell(R_BLOOD_FURY,m_bot)) {} //no GCD
+
+    // sometimes we lose attack
+    if (!m_bot->isInCombat()) {
+        m_bot->Attack(pTarget, true);
+    }
+
+	if(TALENT_ASSASSINATION)
+	{
+		if (!m_bot->HasAura(HUNGER_FOR_BLOOD) && CastSpell(HUNGER_FOR_BLOOD,m_bot)) { return; }
+		if (m_bot->GetComboPoints() < 5)
+		{
+		    if (CastSpell(MUTILATE, pTarget)) { return; }
+		}
+		else
+		{
+			if (!pTarget->HasAura(RUPTURE) && CastSpell(RUPTURE)) { return; }
+
+		}
+	}
+
+    if(TALENT_COMBAT)
+	{
+		if (CastSpell(BLADE_FLURRY,m_bot)) { return; }
+		if (ai->GetEnergyAmount() < 20 && CastSpell(ADRENALINE_RUSH,m_bot)) { return; }
+		if (!CastSpell(ADRENALINE_RUSH) && CastSpell(KILLING_SPREE,m_bot,1,0,1)) { return; }
+		if (m_bot->GetComboPoints() > 5)
+		{
+			if (!pTarget->HasAura(RUPTURE) && CastSpell(RUPTURE)) { return; }
+		}
+	}
+
+    if(TALENT_SUBTELTY)
+	{
+		if (CastSpell(PREMEDITATION,m_bot)) {}
+		if (CastSpell(SHADOW_DANCE,m_bot)) {}
+		if (!CastSpell(SHADOW_DANCE,m_bot) && CastSpell(PREPARATION,m_bot)) { return; }
+		if (m_bot->GetComboPoints() < 5)
+		{
+			if (m_bot->HasAura(SHADOW_DANCE) && !pTarget->HasInArc(M_PI,m_bot)) { if (CastSpell(AMBUSH, pTarget)) { return; } }
+            if (CastSpell(GHOSTLY_STRIKE, pTarget)) { return; }
+		}
+		else
+		{
+			if (!pTarget->HasAura(RUPTURE) && CastSpell(RUPTURE)) { return; }
+			if (!m_bot->HasAura(SLICE_AND_DICE) && CastSpell(SLICE_AND_DICE)) { return; }
+			if (CastSpell(SHADOWSTEP,pTarget)) { }
+		}
+	}
+
+    // defaults if not high enough do specialized attacks
+    if (m_bot->GetComboPoints() < 5) {
+        if (!pTarget->HasInArc(M_PI,m_bot)) { if (CastSpell(BACKSTAB, pTarget)) { return; } }
+        if (!MUTILATE &&CastSpell(SINISTER_STRIKE,pTarget)) { return; } // Dont cast if we have mutilate, save energy for it
+    } else {
+        if (!m_bot->HasAura(SLICE_AND_DICE) && CastSpell(SLICE_AND_DICE)) {  return; }
+		if (CastSpell(ENVENOM,pTarget)) { return; }
+        if (CastSpell(EVISCERATE,pTarget)) { return; }
+    }
+} //end DoNextCombatManeuver
+
+
+void PlayerbotRogueAI::DoNonCombatActions()
+{
+    PlayerbotAI *ai = GetAI();
+    Player *m_bot = GetPlayerBot();
+    if (!m_bot || !ai || m_bot->isDead()) { return; }
+
+    //If Casting or Eating/Drinking return
+    if (m_bot->HasUnitState(UNIT_STAT_CASTING)) { return; }
+    if (m_bot->getStandState() == UNIT_STAND_STATE_SIT) { return; }
+
+    //Buff Up
+    if (ChangeWeaponEnchants()) { return; }
+} //end DoNonCombatActions
+
+bool PlayerbotRogueAI::ChangeWeaponEnchants()
+{
+    PlayerbotAI *ai = GetAI();
+    Player *m_bot = GetPlayerBot();
+    if (!m_bot || !ai || m_bot->isDead()) { return false; }
+
+    Item *weap;
+    Item *poison;
+
+    weap = m_bot->GetWeaponForAttack(BASE_ATTACK);
+    if (weap && !weap->GetEnchantmentId(TEMP_ENCHANTMENT_SLOT))
+    {
+        poison = GetAI()->FindPoisonForward();
+        if(poison == NULL) return false;
+        GetAI()->PoisonWeapon(*poison, poison->GetProto()->Spells[0].SpellId, TARGET_FLAG_ITEM, EQUIPMENT_SLOT_MAINHAND);
+        return true;
+    }
+    weap = m_bot->GetWeaponForAttack(OFF_ATTACK);
+    if (weap && !weap->GetEnchantmentId(TEMP_ENCHANTMENT_SLOT))
+    {
+        poison = GetAI()->FindPoisonBackward();
+        if(poison == NULL) return false;
+        GetAI()->PoisonWeapon(*poison, poison->GetProto()->Spells[0].SpellId, TARGET_FLAG_ITEM, EQUIPMENT_SLOT_OFFHAND);
+        return true;
+    }
+    return false;
+}
diff --git a/src/server/game/AI/Bots/PlayerbotRogueAI.h b/src/server/game/AI/Bots/PlayerbotRogueAI.h
new file mode 100644
index 0000000..9c400f1
--- /dev/null
+++ b/src/server/game/AI/Bots/PlayerbotRogueAI.h
@@ -0,0 +1,41 @@
+#ifndef _PLAYERBOTROGUEAI_H
+#define _PLAYERBOTROGUEAI_H
+
+#include "PlayerbotClassAI.h"
+#include "TargetedMovementGenerator.h"
+
+class PlayerbotRogueAI : PlayerbotClassAI
+{
+    public:
+        PlayerbotRogueAI(Player *const master, Player *const bot, PlayerbotAI *const ai);
+        virtual ~PlayerbotRogueAI();
+
+        virtual void LoadSpells();
+
+        //all combat actions go here
+        void DoNextCombatManeuver(Unit *);
+
+        //all non combat actions go here, ex buffs, heals, REBIRTHes
+        void DoNonCombatActions();
+
+        //buff a specific player, usually a real PC who is not in group
+        bool ChangeWeaponEnchants();
+
+    private:
+        //Damage spells
+        uint32 BACKSTAB, SINISTER_STRIKE, MUTILATE, HEMORRHAGE, GHOSTLY_STRIKE, RIPOSTE, SHIV, FAN_OF_KNIVES;
+        //Finishing Moves
+        uint32 EVISCERATE, RUPTURE, KIDNEY_SHOT, ENVENOM, SLICE_AND_DICE, EXPOSE_ARMOR, DEADLY_THROW;
+        //Buffs
+        uint32 STEALTH, VANISH, EVASION, CLOAK_OF_SHADOWS, SPRINT, COLD_BLOOD, HUNGER_FOR_BLOOD, BLADE_FLURRY, ADRENALINE_RUSH, KILLING_SPREE, SHADOW_DANCE;
+        //Openers
+        uint32 CHEAP_SHOT, GARROTE, AMBUSH;
+        //Others
+        uint32 GOUGE, BLIND, DISMANTLE, SAP, KICK, PREPARATION, PREMEDITATION, SHADOWSTEP, FEINT, TRICKS_OF_THE_TRADE;
+
+        uint32 TALENT_ASSASSINATION, TALENT_COMBAT, TALENT_SUBTELTY;
+
+        uint32 THROW;
+
+};
+#endif
diff --git a/src/server/game/AI/Bots/PlayerbotShamanAI.cpp b/src/server/game/AI/Bots/PlayerbotShamanAI.cpp
new file mode 100644
index 0000000..201776e
--- /dev/null
+++ b/src/server/game/AI/Bots/PlayerbotShamanAI.cpp
@@ -0,0 +1,555 @@
+#include "PlayerbotShamanAI.h"
+class PlayerbotAI;
+PlayerbotShamanAI::PlayerbotShamanAI(Player *const master, Player *const bot, PlayerbotAI *const ai): PlayerbotClassAI(master, bot, ai)
+{
+    foodDrinkSpamTimer = 0;
+    LoadSpells();
+}
+PlayerbotShamanAI::~PlayerbotShamanAI(){}
+
+void PlayerbotShamanAI::LoadSpells() {
+    PlayerbotAI *ai = GetAI();
+    if (!ai) return;
+    #pragma region SpellId Fill
+    //totems
+    HEALING_STREAM_TOTEM = ai->getSpellIdExact("Healing Stream Totem");
+    MANA_SPRING_TOTEM = ai->getSpellIdExact("Mana Spring Totem");
+    MANA_TIDE_TOTEM = ai->getSpellIdExact("Mana Tide Totem");
+    CLEANSING_TOTEM = ai->getSpellIdExact("Cleansing Totem");
+    FIRE_RESISTANCE_TOTEM = ai->getSpellIdExact("Fire Resistance Totem");
+
+    WINDFURY_TOTEM = ai->getSpellIdExact("Windfury Totem");
+    WRATH_OF_AIR_TOTEM = ai->getSpellIdExact("Wrath of Air Totem");
+    GROUNDING_TOTEM = ai->getSpellIdExact("Grounding Totem");
+    NATURE_RESISTANCE_TOTEM = ai->getSpellIdExact("Nature Resistance Totem");
+
+    STRENGTH_OF_EARTH_TOTEM = ai->getSpellIdExact("Strength of Earth Totem");
+    EARTHBIND_TOTEM = ai->getSpellIdExact("Earthbind Totem");
+    STONESKIN_TOTEM = ai->getSpellIdExact("Stoneskin Totem");
+    STONECLAW_TOTEM = ai->getSpellIdExact("Stoneclaw Totem");
+    TREMOR_TOTEM = ai->getSpellIdExact("Tremor Totem");
+    EARTH_ELEMENTAL_TOTEM = ai->getSpellIdExact("Earth Elemental Totem");
+
+    FLAMETONGUE_TOTEM = ai->getSpellIdExact("Flametongue Totem");
+    TOTEM_OF_WRATH = ai->getSpellIdExact("Totem of Wrath");
+    SEARING_TOTEM = ai->getSpellIdExact("Searing Totem");
+    MAGMA_TOTEM = ai->getSpellIdExact("Magma Totem");
+    FIRE_ELEMENTAL_TOTEM = ai->getSpellIdExact("Fire Elemental Totem");
+    FROST_RESISTANCE_TOTEM = ai->getSpellIdExact("Frost Resistance Totem");
+
+    TOTEMIC_RECALL = ai->getSpellIdExact("Totemic Recall");
+    CALL_OF_THE_ELEMENTS = ai->getSpellIdExact("Call of the Elements");
+    CALL_OF_THE_ANCESTORS = ai->getSpellIdExact("Call of the Ancestors");
+    CALL_OF_THE_SPIRITS = ai->getSpellIdExact("Call of the Spirits");
+
+    //restoration
+    HEAL = ai->getSpellIdExact("Healing Wave");
+    LESSER_HEAL = ai->getSpellIdExact("Lesser Healing Wave");
+    CHAIN_HEAL = ai->getSpellIdExact("Chain Heal");
+    RIPTIDE = ai->getSpellIdExact("Riptide");
+    ANCESTRAL_SPIRIT = ai->getSpellIdExact("Ancestral Spirit");
+    CLEANSE_SPIRIT = ai->getSpellIdExact("Cleanse Spirit");
+    if (CLEANSE_SPIRIT) CLEANSE_SPIRIT = ai->getSpellIdExact("Cure Toxins");
+
+    //offensive spells
+    LIGHTNING_BOLT = ai->getSpellIdExact("Lightning Bolt");
+    CHAIN_LIGHTNING = ai->getSpellIdExact("Chain Lightning");
+    FIRE_NOVA = ai->getSpellIdExact("Fire Nova");
+    THUNDERSTORM = ai->getSpellIdExact("Thunderstorm");
+    LAVA_BURST = ai->getSpellIdExact("Lava Burst");
+    EARTH_SHOCK = ai->getSpellIdExact("Earth Shock");
+    WIND_SHEAR = ai->getSpellIdExact("Wind Shear");
+    FLAME_SHOCK = ai->getSpellIdExact("Flame Shock");
+    FROST_SHOCK = ai->getSpellIdExact("Frost Shock");
+    PURGE = ai->getSpellIdExact("Purge");
+    HEX  = ai->getSpellIdExact("Hex");
+
+    //buffs
+    LIGHTNING_SHIELD = ai->getSpellIdExact("Lightning Shield");
+    WATER_SHIELD = ai->getSpellIdExact("Water Shield");
+    EARTH_SHIELD = ai->getSpellIdExact("Earth Shield");
+    HEROISM = ai->getSpellIdExact("Heroism");
+    if (HEROISM) HEROISM = ai->getSpellIdExact("Bloodlust");
+    ELEMENTAL_MASTERY = ai->getSpellIdExact("Elemental Mastery");
+    NATURES_SWIFTNESS = ai->getSpellIdExact("Nature's Swiftness");
+
+    WINDFURY_WEAPON = ai->getSpellIdExact("Windfury Weapon");
+    FLAMETONGUE_WEAPON = ai->getSpellIdExact("Flametongue Weapon");
+    FROSTBRAND_WEAPON = ai->getSpellIdExact("Frostbrand Weapon");
+    ROCKBITER_WEAPON = ai->getSpellIdExact("Rockbiter Weapon");
+    EARTHLIVING_WEAPON = ai->getSpellIdExact("Earthliving Weapon");
+
+    WATER_BREATHING = ai->getSpellIdExact("Water Breathing");
+    WATER_WALKING = ai->getSpellIdExact("Water Walking");
+
+    //melee
+    LAVA_LASH = ai->getSpellIdExact("Lava Lash");
+    STORMSTRIKE = ai->getSpellIdExact("Stormstrike");
+    SHAMANISTIC_RAGE = ai->getSpellIdExact("Shamanistic Rage");
+    FERAL_SPIRIT = ai->getSpellIdExact("Feral Spirit");
+
+    GHOST_WOLF = ai->getSpellIdExact("Ghost Wolf");
+
+    EXHAUSTION = 57723; // heroism debuff
+    SATED = 57724; // bloodlust debuff
+    //MAELSTROM_WEAPON = 0; // We want the triggered aura, not the talent spell
+    uint32 mwtrigger = ai->getSpellIdExact("Maelstrom Weapon",true);
+    if (mwtrigger)
+    {
+        SpellEntry const *mwtSpell = GetSpellStore()->LookupEntry(mwtrigger);
+        if (mwtSpell && mwtSpell->EffectTriggerSpell[0] > 0) MAELSTROM_WEAPON = mwtSpell->EffectTriggerSpell[0];
+    }
+
+    TALENT_ELEMENTAL = ELEMENTAL_MASTERY;
+    TALENT_ENHANCEMENT = STORMSTRIKE;
+    TALENT_RESTO = EARTH_SHIELD;
+
+    uint8 talentCounter = 0;
+    if (TALENT_ELEMENTAL) talentCounter++;
+    if (TALENT_ENHANCEMENT) talentCounter++;
+    if (TALENT_RESTO) talentCounter++;
+    //if (talentCounter > 1) { TALENT_ELEMENTAL = 0; TALENT_ENHANCEMENT = 0; TALENT_RESTO = 0; } //Unreliable Talent detection.
+    #pragma endregion
+}
+void PlayerbotShamanAI::DoNextCombatManeuver(Unit *pTarget)
+{
+    if (!pTarget || pTarget->isDead()) return;
+    PlayerbotAI *ai = GetAI();
+    if (!ai) return;
+    Player *m_bot = GetPlayerBot();
+    if (!m_bot || m_bot->isDead()) return;
+    Unit *pVictim = pTarget->getVictim();
+    Unit *m_tank = FindMainTankInRaid(GetMaster());
+    if (!m_tank && m_bot->GetGroup() && GetMaster()->GetGroup() != m_bot->GetGroup()) { FindMainTankInRaid(m_bot); }
+    if (!m_tank) { m_tank = m_bot; }
+    uint32 masterHP = GetMaster()->GetHealth()*100 / GetMaster()->GetMaxHealth();
+    float pDist = m_bot->GetDistance(pTarget);
+    uint8 pThreat = GetThreatPercent(pTarget);
+
+    uint8 reqHeal = 0;
+    uint8 OwnPartyHP = GetHealthPercentRaid(m_bot, reqHeal);
+
+    switch(ai->GetScenarioType())
+    {
+        case SCENARIO_DUEL:
+            ((ai->GetHealthPercent() < 80 && CastSpell(LESSER_HEAL)) ||
+            CastSpell(LIGHTNING_BOLT, pTarget));
+            return;
+    }
+
+    // Cast CC breakers if any match found (include any dispels first)  does not work yet
+    //uint32 ccSpells[4] = { R_ESCAPE_ARTIST, R_EVERY_MAN_FOR_HIMSELF, R_WILL_OF_FORSAKEN, R_STONEFORM };
+    //if (ai->GetManaPercent() < 35) { ccSpells[0] = 0; ccSpells[1] = 0; } //We dont have any mana to waste...
+    //if (castSelfCCBreakers(ccSpells)) {  } // Most of them don't trigger gcd
+
+
+    #pragma region Choose Actions
+    // Choose actions accoring to talents
+    if (m_tank->GetGUID() == m_bot->GetGUID()) { m_role=BOT_ROLE_TANK; } // Hey! I am Main Tank
+    else if (TALENT_ENHANCEMENT) { m_role = BOT_ROLE_DPS_MELEE; }
+    else if (TALENT_ELEMENTAL) { m_role = BOT_ROLE_DPS_RANGED; }
+    else if (TALENT_RESTO) { m_role = BOT_ROLE_SUPPORT; }
+    else { m_role = BOT_ROLE_DPS_MELEE; } //Unknown build or low level.. Mainly attack
+
+    // if i am under attack and if i am not tank or offtank: change target if needed
+    if (m_tank->GetGUID() != m_bot->GetGUID() && isUnderAttack()  )
+    {
+            if (pVictim && pVictim->GetGUID() == m_bot->GetGUID() && pDist <= 2) {  } // My target is almost up to me, no need to search
+            else //Have to select nearest target
+            {
+                Unit *curAtt = GetNearestAttackerOf(m_bot);
+                if (curAtt && curAtt->GetGUID() != pTarget->GetGUID())
+                {
+                    m_bot->SetSelection(curAtt->GetGUID());
+                    //ai->AddLootGUID(curAtt->GetGUID());
+                    DoNextCombatManeuver(curAtt); //Restart new update to get variables fixed..
+                    return;
+                }
+            }
+            //my target is attacking me
+    }
+    #pragma endregion
+
+    // Choose Weapon Enchant
+    if (ChangeWeaponEnchants()) return;
+
+	if (TALENT_ELEMENTAL){ if (!m_bot->HasAura(WATER_SHIELD) && CastSpell(WATER_SHIELD,m_bot)) { return; }}
+	if (TALENT_ENHANCEMENT){ if (!m_bot->HasAura(LIGHTNING_SHIELD) && CastSpell(LIGHTNING_SHIELD,m_bot)) { return; }}
+	if (TALENT_RESTO){ if (!m_bot->HasAura(WATER_SHIELD) && CastSpell(WATER_SHIELD,m_bot)) { return; }}
+    // Choose shield
+	/*
+    if (EARTH_SHIELD && ai->GetHealthPercent() < 80 && isUnderAttack()) { if (CastSpell(EARTH_SHIELD,m_bot)) { return; } }
+    else if (WATER_SHIELD && ai->GetManaPercent() < 40) { if (CastSpell(WATER_SHIELD,m_bot)) { return; } }
+    else if (LIGHTNING_SHIELD &&
+        ( isUnderAttack() || m_tank->GetGUID() == m_bot->GetGUID() )  && !(m_bot->HasAura(WATER_SHIELD) && ai->GetManaPercent() < 80)
+        ) { if (CastSpell(LIGHTNING_SHIELD,m_bot)) { return; } }
+    else if (CastSpell(WATER_SHIELD,m_bot)) { return; }
+	*/
+    // If there's a cast stop
+    if(m_bot->HasUnitState(UNIT_STAT_CASTING)) return;
+
+    switch(m_role)
+    {
+        #pragma region BOT_ROLE_TANK / BOT_ROLE_OFFTANK
+        case BOT_ROLE_TANK:
+        case BOT_ROLE_OFFTANK:
+             if (!TALENT_ELEMENTAL && !TALENT_RESTO) { TakePosition(pTarget); }
+            else { TakePosition(pTarget,BOT_ROLE_DPS_RANGED); } // mob will come to you sooner or later no need to hurry
+
+            // Do support stuff
+            if (!m_bot->isMoving() && ChangeTotems(m_role)) { return; }
+            if (ai->GetManaPercent() > 70 && DoSupportRaid(m_bot)) { return; }
+
+            break;
+        #pragma endregion
+
+        #pragma region BOT_ROLE_DPS_MELEE
+        case BOT_ROLE_DPS_MELEE:
+            TakePosition(pTarget);
+            // Do support stuff
+            if (!m_bot->isMoving() && ChangeTotems(m_role)) { return; }
+            if (ai->GetManaPercent() > 40 && DoSupportRaid(m_bot)) { return; }
+            break;
+        #pragma endregion
+
+        #pragma region BOT_ROLE_DPS_RANGED
+        case BOT_ROLE_DPS_RANGED:
+            TakePosition(pTarget);
+            // Do support stuff
+            if (!m_bot->isMoving() && ChangeTotems(m_role)) { return; }
+            if (ai->GetManaPercent() > 40 && DoSupportRaid(m_bot)) { return; }
+            break;
+        #pragma endregion
+
+        #pragma region BOT_ROLE_SUPPORT
+        case BOT_ROLE_SUPPORT:
+            TakePosition(pTarget);
+            // Do support stuff
+            if (!m_bot->isMoving() && ChangeTotems(m_role)) { return; }
+            if (DoSupportRaid(m_bot)) { return; }
+            //heal pets and bots
+            Unit *target = DoSelectLowestHpFriendly(40, 1000);
+            if(target && target->isAlive() && HealTarget(target, target->GetHealth()*100 / target->GetMaxHealth()) ) { return; }
+
+            break;
+        #pragma endregion
+    }
+    #pragma region ShamanCommon
+
+
+    //Defensive Stuff
+    if (m_tank->GetGUID() != m_bot->GetGUID() && pVictim && pVictim->GetGUID() == m_bot->GetGUID() )
+    {
+        if (pDist > 5 && CastSpell(FROST_SHOCK, pTarget)) { return; }
+        if ((pTarget->GetCreatureType() == (uint32) CREATURE_TYPE_BEAST || pTarget->GetCreatureType() == (uint32) CREATURE_TYPE_HUMANOID) && CastSpell(HEX, pTarget)) { return; } // no gcd
+        if (CastSpell(WIND_SHEAR, pTarget)) { } // no gcd
+    }
+    if (m_bot->getRace() == (uint8) RACE_BLOODELF && pDist < 8 && pTarget->IsNonMeleeSpellCasted(true) && CastSpell(R_ARCANE_TORRENT, pTarget)) { } //no gcd
+    if (pTarget->IsNonMeleeSpellCasted(true) && CastSpell(WIND_SHEAR, pTarget)) { } //no gcd
+    if (m_bot->getRace() == (uint8) RACE_TAUREN && pVictim && pVictim->GetGUID() == m_bot->GetGUID() && pDist < 8 && CastSpell(R_WAR_STOMP, pTarget)) { return; }
+
+    //Catch
+    if (pTarget->HasUnitMovementFlag(UNIT_FLAG_FLEEING))
+    {
+        if (CastSpell(FROST_SHOCK,pTarget)) return;
+    }
+
+
+    //Buff and restores
+    if ( ( (ai->GetHealthPercent() < 60 && isUnderAttack()) ||
+        (ai->GetManaPercent() < 30) ) && CastSpell(SHAMANISTIC_RAGE, m_bot)) { return; }
+    if (m_bot->getRace() == (uint8) RACE_TROLL && CastSpell(R_BERSERKING,m_bot)) {} // no GCD
+    if (m_bot->getRace() == (uint8) RACE_ORC && CastSpell(R_BLOOD_FURY,m_bot)) {} // no GCD
+    if (!m_bot->HasAura(HEROISM) && !m_bot->HasAura(EXHAUSTION) && !m_bot->HasAura(SATED) && CastSpell(HEROISM,m_bot)) { return; }
+    if (m_role != BOT_ROLE_SUPPORT && CastSpell(NATURES_SWIFTNESS, m_bot)) { } //healers keep it for healing no gcd
+    else if (CastSpell(ELEMENTAL_MASTERY, m_bot)) { } //no gcd
+
+    // If at threat limit, use WIND_SHEAR to reduce threat
+    if (pThreat > threatThreshold && m_tank->GetGUID() != m_bot->GetGUID() && !isUnderAttack())
+    {
+        if (m_tank->getVictim() && m_tank->getVictim()->GetGUID() != pTarget->GetGUID()) // I am attacking wrong target!!
+        {
+            m_bot->SetSelection(m_tank->getVictim()->GetGUID());
+            return;
+        }
+        else
+        {
+            if (CastSpell(WIND_SHEAR,pTarget)) { return; } //Lets see if we can manage
+            else { return; } //use no spells and wait threat to be reduced
+        }
+    }
+
+
+	if (TALENT_ELEMENTAL)
+	{
+		if (CastSpell(ELEMENTAL_MASTERY, m_bot)) { } //no gcd
+		if (!pTarget->HasAura(FLAME_SHOCK,m_bot->GetGUID()) && CastSpell(FLAME_SHOCK,pTarget)) { return; }
+		if (CastSpell(LAVA_BURST,pTarget)) { return; }
+		if (CastSpell(CHAIN_LIGHTNING,pTarget)) { return; }
+		if (CastSpell(LIGHTNING_BOLT,pTarget)) { return; }
+	}
+
+    //dps
+    if (MAELSTROM_WEAPON)
+    {
+        Aura *maelaura = m_bot->GetAura(MAELSTROM_WEAPON);
+        if (maelaura && maelaura->GetStackAmount() == 5)
+        {
+            if ((isUnderAttack(m_tank,3) || m_tank->GetGUID() == m_bot->GetGUID()) && CastSpell(CHAIN_LIGHTNING,pTarget,true,true)) { return; }
+            if (CastSpell(LIGHTNING_BOLT,pTarget,true,true)) { return; }
+        }
+    }
+	if (CastSpell(FLAME_SHOCK,pTarget)) { return; }
+    if (CastSpell(STORMSTRIKE,pTarget,true,true)) { return; }
+
+    //if (!TALENT_ENHANCEMENT && CanCast(LAVA_BURST,pTarget,true) && pTarget->HasAura(FLAME_SHOCK,m_bot->GetGUID()) && CastSpell(LAVA_BURST,pTarget,false)) { return; }
+	if (CastSpell(FERAL_SPIRIT,m_bot)) { return; }
+    if (CanCast(EARTH_SHOCK,pTarget,true) && (pTarget->HasAura(STORMSTRIKE,m_bot->GetGUID()) || pTarget->HasAura(FLAME_SHOCK,m_bot->GetGUID()) ) && CastSpell(EARTH_SHOCK,pTarget)) { return; }
+    //if (CanCast(FLAME_SHOCK,pTarget) && CastSpell(FLAME_SHOCK,pTarget)) { return; }
+	if (CastSpell(LAVA_LASH,pTarget,true,true)) { return; }
+	if (CastSpell(FIRE_NOVA,pTarget)) { return; }
+    //if ((isUnderAttack(m_tank,4) || m_tank->GetGUID() == m_bot->GetGUID()) && CastSpell(FIRE_NOVA,pTarget)) { return; }
+    if (ai->GetManaPercent() > 60 && castDispel(PURGE,pTarget)) { return; } //PURGE but dont overpurge
+
+    #pragma endregion
+
+
+    // drink potion if support / healer (Other builds simply overuse mana and waste mana pots)
+    if(ai->GetManaPercent() < 5 && (m_role == BOT_ROLE_SUPPORT || m_role == BOT_ROLE_HEALER) )
+    {
+        Item *pItem = ai->FindPotion();
+        if(pItem != NULL)
+        {
+            if (pItem->GetSpell() && m_bot->HasSpellCooldown(pItem->GetSpell()) ) { return; } //pot is in cooldown
+            ai->UseItem(*pItem);
+        }
+    }
+
+
+} //end DoNextCombatManeuver
+
+void PlayerbotShamanAI::DoNonCombatActions()
+{
+    PlayerbotAI *ai = GetAI();
+    Player *m_bot = GetPlayerBot();
+    if (!m_bot || !ai || m_bot->isDead()) { return; }
+
+    //If Casting or Eating/Drinking return
+    if (m_bot->HasUnitState(UNIT_STAT_CASTING)) { return; }
+    if (m_bot->getStandState() == UNIT_STAND_STATE_SIT) { return; }
+
+    //buff and heal raid
+    if (DoSupportRaid(m_bot)) { return; }
+
+    //heal pets and bots
+    Unit *target = DoSelectLowestHpFriendly(40, 1000);
+    if (target && target->isAlive() && HealTarget(target, target->GetHealth()*100 / target->GetMaxHealth())) { return; }
+
+    //Buffs
+    if (ChangeWeaponEnchants()) { return; }
+    if (CastSpell(WATER_SHIELD,m_bot)) { return; }
+    if (CastSpell(EARTH_SHIELD,m_bot)) { return; }
+
+    //mana/hp check
+    //Don't bother with eating, if low on hp, just let it heal themself
+    if (m_bot->getRace() == (uint8) RACE_UNDEAD_PLAYER && ai->GetHealthPercent() < 75 && CastSpell(R_CANNIBALIZE,m_bot)) { return; }
+    if (m_bot->GetHealth() < m_bot->GetMaxHealth() && CastSpell(LESSER_HEAL,m_bot)) { return; }
+	if (ai->GetManaPercent() < 50) { ai->Feast(); }
+
+} //end DoNonCombatActions
+bool PlayerbotShamanAI::HealTarget(Unit *target, uint8 hp)
+{
+    if(!target || target->isDead()) return false;
+    Player *m_bot = GetPlayerBot();
+
+    if(hp < 30 && m_bot->isInCombat() && CastSpell(NATURES_SWIFTNESS, m_bot)) {} // NO gcd
+    if(hp < 60 && CanCast(HEAL,target,true) && m_bot->HasAura(NATURES_SWIFTNESS) && CastSpell(HEAL, target, false)) { return true; }
+    if(hp < 30 && CastSpell(LESSER_HEAL,target,true,true)) { return true; }
+    if(hp < 40 && m_bot->getRace() == (uint8) RACE_DRAENEI && CastSpell(R_GIFT_OF_NAARU,target)) {} // no GCD
+    if(hp < 65 && CanCast(EARTH_SHIELD,target) && !m_bot->HasAura(EARTH_SHIELD,m_bot->GetGUID()) && CastSpell(EARTH_SHIELD,target,false)) { return true; }
+    if(hp < 65 && CastSpell(HEAL,target,true,true)) { return true; }
+    if(hp < 85 && CastSpell(LESSER_HEAL,target,true,true)) { return true; }
+
+    return false;
+} //end HealTarget
+
+bool PlayerbotShamanAI::HealGroup (Unit *target, uint8 hp, uint8 &countNeedHeal)
+{
+    Player *m_bot = GetPlayerBot();
+    if (countNeedHeal < 2) { return false; }
+    Unit *rTarget = DoSelectLowestHpFriendly(30,500);
+    if (!rTarget || rTarget->isDead() || rTarget->GetHealth() * 100 / rTarget->GetMaxHealth() > 80 ) { return false; }
+
+    if (hp < 65 && RIPTIDE && rTarget->HasAura(RIPTIDE,m_bot->GetGUID()) && CastSpell(CHAIN_HEAL, rTarget)) { return true; }
+    if (hp < 85 && CastSpell(RIPTIDE, rTarget)) { return true; }
+    if (hp < 75 && CastSpell(CHAIN_HEAL, rTarget,true,true)) { return true; }
+
+    return false;
+}
+
+bool PlayerbotShamanAI::CureTarget(Unit *target)
+//Cures the target
+{
+    Player *m_bot = GetPlayerBot();
+
+    if(!target || target->isDead()) { return false; }
+    if (castDispel(CLEANSE_SPIRIT, target)) return true;
+
+    return false;
+}
+
+bool PlayerbotShamanAI::RezTarget (Unit *target)
+{
+    if(!target || target->isAlive()) return false;
+    Player *m_bot = GetPlayerBot();
+    if (target->IsNonMeleeSpellCasted(true)) { return false; } //Already resurrected
+    if (m_bot->isInCombat()) { return false; }
+
+    if (!CanCast(ANCESTRAL_SPIRIT,target)) return false;
+    std::string msg = "Rezzing ";
+    msg += target->GetName();
+
+    GetPlayerBot()->Say(msg, LANG_UNIVERSAL);
+    return CastSpell(ANCESTRAL_SPIRIT, target,false);
+}
+
+bool PlayerbotShamanAI::BuffPlayer(Unit *target)
+{
+    //std::string msg = "Mana totem, coming right up.";
+    //GetPlayerBot()->Say(msg, LANG_UNIVERSAL);
+    //if(!HasAuraName(GetPlayerBot(), "Mana Spring")) { CastSpell(MANA_SPRING_TOTEM, GetPlayerBot()); } return true;
+    return false;
+}
+#pragma region Change Totems
+bool PlayerbotShamanAI::ChangeTotems(uint32 mode)
+{
+    uint32 earth=0, fire=0, water=0, air=0;
+
+    PlayerbotAI *ai = GetAI();
+    if(!ai) return false;
+    Player *m_bot = GetPlayerBot();
+    if(!m_bot || m_bot->isDead()) return false;
+
+    Unit *pTarget = m_bot->GetSelectedUnit();
+    Unit *pVictim = NULL;
+    if (m_bot->GetSelectedUnit()->IsFriendlyTo(m_bot)) pTarget = NULL;
+    if (pTarget) pVictim = pTarget->getVictim();
+
+    //Defaults
+    if (!HasAuraName(m_bot,"Horn of Winter") )earth = STRENGTH_OF_EARTH_TOTEM;
+    if (!earth) earth = STONESKIN_TOTEM;
+    if (!earth) earth = EARTHBIND_TOTEM;
+    fire = TOTEM_OF_WRATH;
+    if (!fire) fire = FLAMETONGUE_TOTEM;
+    if (!fire) fire = SEARING_TOTEM;
+    water = MANA_SPRING_TOTEM;
+    if (!water) water = HEALING_STREAM_TOTEM;
+    if (TALENT_ELEMENTAL || TALENT_RESTO) air = WRATH_OF_AIR_TOTEM;
+    else air = WINDFURY_TOTEM;
+
+    //Target reactive stuff
+    if (pTarget)
+    {
+        if (GROUNDING_TOTEM && pTarget->IsNonMeleeSpellCasted(true)) air = GROUNDING_TOTEM;
+    }
+
+    if (STONESKIN_TOTEM && isUnderAttack()) earth = STONESKIN_TOTEM;
+
+    uint32 totz[4] = {earth, fire, water, air};
+
+    for (int i = 0; i < 4; i++)
+    {
+        if (!totz[i]) continue;
+        SpellEntry const *tSpell = GetSpellStore()->LookupEntry(totz[i]);
+        if (!tSpell) continue;
+        uint32 tEntry = (uint32) tSpell->EffectMiscValue[0];
+        if (!tEntry) continue;
+        CreatureInfo const *totemEntry = GetCreatureTemplateStore(tEntry);
+        if (!tEntry) continue;
+
+        if (CanCast(totz[i], m_bot) && !m_bot->FindNearestCreature(tEntry,30)) { return CastSpell(totz[i],m_bot,false); }
+    }
+    return false;
+}
+#pragma endregion
+#pragma region ChangeWeaponEnchants
+bool PlayerbotShamanAI::ChangeWeaponEnchants()
+{
+    uint32 mhEnch = 0, ohEnch = 0;
+
+    PlayerbotAI *ai = GetAI();
+    if(!ai) return false;
+    Player *m_bot = GetPlayerBot();
+    if(!m_bot || m_bot->isDead()) return false;
+
+
+    // Choose Weapon Enchant
+    if (TALENT_RESTO) { mhEnch = EARTHLIVING_WEAPON; }
+    else if (TALENT_ELEMENTAL){ mhEnch = FLAMETONGUE_WEAPON; }
+    else
+    {
+        if (WINDFURY_WEAPON)
+        {
+            mhEnch = WINDFURY_WEAPON;
+            if (m_bot->haveOffhandWeapon())
+            {
+                if (LAVA_LASH) ohEnch = FLAMETONGUE_WEAPON;
+                else ohEnch = WINDFURY_WEAPON;
+            }
+		}
+    else if (FLAMETONGUE_WEAPON)
+    {
+        mhEnch = FLAMETONGUE_WEAPON;
+        if (m_bot->haveOffhandWeapon()) ohEnch = FLAMETONGUE_WEAPON;
+    }
+
+    }
+
+    Item* weap;
+    uint32 enchant_id = 0;
+    SpellEntry const *tSpell;
+    bool castedsomething = false;
+
+    if (mhEnch)
+    {
+        weap = m_bot->GetWeaponForAttack(BASE_ATTACK);
+        if (weap)
+        {
+            tSpell = GetSpellStore()->LookupEntry(mhEnch);
+            if (tSpell && tSpell->EffectMiscValue[0] > 0)
+            {
+                enchant_id = (uint32) tSpell->EffectMiscValue[0];
+                if (enchant_id && weap->GetEnchantmentId(TEMP_ENCHANTMENT_SLOT) != enchant_id && sSpellItemEnchantmentStore.LookupEntry(enchant_id))
+                {
+                    m_bot->ApplyEnchantment(weap,TEMP_ENCHANTMENT_SLOT, false); //Remove old enchantment effect
+                    weap->SetEnchantment(TEMP_ENCHANTMENT_SLOT, enchant_id, 1800 * 1000, 0); //Add for 30 mins
+                    m_bot->ApplyEnchantment(weap, TEMP_ENCHANTMENT_SLOT, true); //Add new effect
+                    castedsomething = true;
+                }
+            }
+        }
+    }
+
+    if (ohEnch)
+    {
+        weap = m_bot->GetWeaponForAttack(OFF_ATTACK);
+        if (weap)
+        {
+            tSpell = GetSpellStore()->LookupEntry(ohEnch);
+            if (tSpell && tSpell->EffectMiscValue[0] > 0)
+            {
+                enchant_id = (uint32) tSpell->EffectMiscValue[0];
+                if (enchant_id && weap->GetEnchantmentId(TEMP_ENCHANTMENT_SLOT) != enchant_id && sSpellItemEnchantmentStore.LookupEntry(enchant_id))
+                {
+                    m_bot->ApplyEnchantment(weap,TEMP_ENCHANTMENT_SLOT, false); //Remove old enchantment effect
+                    weap->SetEnchantment(TEMP_ENCHANTMENT_SLOT, enchant_id, 1800 * 1000, 0); //Add for 30 mins
+                    m_bot->ApplyEnchantment(weap, TEMP_ENCHANTMENT_SLOT, true); //Add new effect
+                    castedsomething = true;
+                }
+            }
+        }
+    }
+    return castedsomething;
+
+}
+#pragma endregion
diff --git a/src/server/game/AI/Bots/PlayerbotShamanAI.h b/src/server/game/AI/Bots/PlayerbotShamanAI.h
new file mode 100644
index 0000000..e5fef23
--- /dev/null
+++ b/src/server/game/AI/Bots/PlayerbotShamanAI.h
@@ -0,0 +1,75 @@
+#ifndef _PLAYERBOTSHAMANAI_H
+#define _PLAYERBOTSHAMANAI_H
+
+#include "PlayerbotClassAI.h"
+
+class PlayerbotShamanAI : PlayerbotClassAI
+{
+    public:
+        PlayerbotShamanAI(Player *const master, Player *const bot, PlayerbotAI *const ai);
+        virtual ~PlayerbotShamanAI();
+
+        virtual void LoadSpells();
+
+        //all combat actions go here
+        void DoNextCombatManeuver(Unit *);
+
+        //all non combat actions go here, ex buffs, heals, REBIRTHes
+        void DoNonCombatActions();
+
+        //buff a specific player, usually a real PC who is not in group
+        bool BuffPlayer(Unit *target);
+
+        //Heals the target based off its HP
+        bool HealTarget(Unit *target, uint8 hp);
+
+        bool HealGroup (Unit *target, uint8 hp, uint8 &countNeedHeal);
+
+        //Cures the target
+        bool CureTarget(Unit *target);
+
+        bool RezTarget (Unit *target);
+
+        bool ChangeTotems(uint32 mode);
+
+        bool ChangeWeaponEnchants();
+
+        /*//find any specific mount spells, ie druids=cat, shaman=ghost wolf etc
+        virtual bool FindMount();
+        virtual bool Unmount();
+        virtual bool IsMounted(); */
+
+    private:
+
+        //totems
+        uint32 HEALING_STREAM_TOTEM, MANA_SPRING_TOTEM, MANA_TIDE_TOTEM, CLEANSING_TOTEM, FIRE_RESISTANCE_TOTEM; //water
+        uint32 WINDFURY_TOTEM, WRATH_OF_AIR_TOTEM, GROUNDING_TOTEM, NATURE_RESISTANCE_TOTEM;  //air
+        uint32 STRENGTH_OF_EARTH_TOTEM, EARTHBIND_TOTEM, STONESKIN_TOTEM, STONECLAW_TOTEM, TREMOR_TOTEM, EARTH_ELEMENTAL_TOTEM ; //earth
+        uint32 FLAMETONGUE_TOTEM, TOTEM_OF_WRATH, SEARING_TOTEM, MAGMA_TOTEM, FIRE_ELEMENTAL_TOTEM, FROST_RESISTANCE_TOTEM; //fire
+        uint32 TOTEMIC_RECALL, CALL_OF_THE_ELEMENTS, CALL_OF_THE_ANCESTORS, CALL_OF_THE_SPIRITS;
+
+        //restoration
+        uint32 HEAL, LESSER_HEAL, CHAIN_HEAL, RIPTIDE, ANCESTRAL_SPIRIT, CLEANSE_SPIRIT;
+
+        //offensive spells
+        uint32 LIGHTNING_BOLT, CHAIN_LIGHTNING, FIRE_NOVA, THUNDERSTORM, LAVA_BURST, EARTH_SHOCK, WIND_SHEAR, FLAME_SHOCK, FROST_SHOCK, PURGE, HEX ;
+
+        //buffs
+        uint32 LIGHTNING_SHIELD, WATER_SHIELD, EARTH_SHIELD, HEROISM, ELEMENTAL_MASTERY, NATURES_SWIFTNESS,
+            WINDFURY_WEAPON, FLAMETONGUE_WEAPON, FROSTBRAND_WEAPON, ROCKBITER_WEAPON, EARTHLIVING_WEAPON,
+            WATER_BREATHING, WATER_WALKING ;
+
+        //mellee
+        uint32 LAVA_LASH, STORMSTRIKE, SHAMANISTIC_RAGE, FERAL_SPIRIT;
+
+        uint32 GHOST_WOLF;
+
+        //special
+        uint32 EXHAUSTION, SATED, MAELSTROM_WEAPON;
+
+        uint32 TALENT_ELEMENTAL, TALENT_ENHANCEMENT, TALENT_RESTO;
+
+};
+
+
+#endif
diff --git a/src/server/game/AI/Bots/PlayerbotWarlockAI.cpp b/src/server/game/AI/Bots/PlayerbotWarlockAI.cpp
new file mode 100644
index 0000000..517c6e4
--- /dev/null
+++ b/src/server/game/AI/Bots/PlayerbotWarlockAI.cpp
@@ -0,0 +1,409 @@
+/*
+Name : PlayerbotWarlockAI.cpp
+Complete: maybe around 60%
+
+Limitations:    - Talent build decision is made by key talent spells, which makes them viable only after level 50-ish.. Behaviour does not change though..
+                - Curse checks are slow, later all curses should be looked up in one loop
+                - Need a function to lookup pet known spells for better pet handling
+                - Warlock do not summon other pets than Fel hunter/Imp
+
+Authors : SwaLLoweD
+Version : 0.40
+*/
+#include "PlayerbotWarlockAI.h"
+
+class PlayerbotAI;
+PlayerbotWarlockAI::PlayerbotWarlockAI(Player *const master, Player *const bot, PlayerbotAI *const ai): PlayerbotClassAI(master, bot, ai)
+{
+    foodDrinkSpamTimer = 0;
+    LoadSpells();
+}
+PlayerbotWarlockAI::~PlayerbotWarlockAI(){}
+
+void PlayerbotWarlockAI::LoadSpells() {
+    PlayerbotAI *ai = GetAI();
+    if (!ai) return;
+    #pragma region SpellId Fill
+    //CURSES
+    CURSE_OF_ELEMENTS = ai->getSpellIdExact("Curse of Elements");
+    CURSE_OF_WEAKNESS = ai->getSpellIdExact("Curse of Weakness");
+    CURSE_OF_AGONY = ai->getSpellIdExact("Curse of Agony");
+    CURSE_OF_RECKLESSNESS = ai->getSpellIdExact("Curse of Recklessness");
+    CURSE_OF_TONGUES = ai->getSpellIdExact("Curse of Tongues");
+    CURSE_OF_DOOM = ai->getSpellIdExact("Curse of Doom");
+    CURSE_OF_EXHAUSTION = ai->getSpellIdExact("Curse of Exhaustion");
+
+
+    //AFFLICTION
+    CORRUPTION = ai->getSpellIdExact("Corruption");
+    DRAIN_SOUL = ai->getSpellIdExact("Drain Soul");
+    DRAIN_LIFE = ai->getSpellIdExact("Drain Life");
+    DRAIN_MANA = ai->getSpellIdExact("Drain Mana");
+    SIPHON_LIFE = ai->getSpellIdExact("Siphon Life");
+    UNSTABLE_AFFLICTION = ai->getSpellIdExact("Unstable Affliction");
+    HAUNT = ai->getSpellIdExact("Haunt");
+    SEED_OF_CORRUPTION = ai->getSpellIdExact("Seed of Corruption");
+    DEATH_COIL = ai->getSpellIdExact("Death Coil");
+
+
+    //DESTRUCTION
+    SHADOW_BOLT = ai->getSpellIdExact("Shadow Bolt");
+    IMMOLATE = ai->getSpellIdExact("Immolate");
+    INCINERATE = ai->getSpellIdExact("Incinerate");
+    SEARING_PAIN = ai->getSpellIdExact("Searing Pain");
+    CONFLAGRATE = ai->getSpellIdExact("Conflagrate");
+    SOUL_FIRE = ai->getSpellIdExact("Soul Fire");
+    SHADOWBURN = ai->getSpellIdExact("Shadowburn");
+    SHADOWFURY = ai->getSpellIdExact("Shadowfury");
+    CHAOS_BOLT = ai->getSpellIdExact("Chaos Bolt");
+    SHADOWFLAME = ai->getSpellIdExact("Shadowflame");
+    RAIN_OF_FIRE = ai->getSpellIdExact("Rain of Fire");
+    HELLFIRE = ai->getSpellIdExact("Hellfire");
+
+
+    //DEMONOLOGY
+    DEMON_ARMOR = ai->getSpellIdExact("Demon Armor");
+    if (!DEMON_ARMOR) DEMON_ARMOR = ai->getSpellIdExact("Demon Skin");
+    FEL_ARMOR = ai->getSpellIdExact("Fel Armor");
+    SOULSHATTER = ai->getSpellIdExact("Soulshatter");
+    HEALTH_FUNNEL = ai->getSpellIdExact("Health Funnel");
+    DARK_PACT = ai->getSpellIdExact("Dark Pact");
+    SOUL_LINK = ai->getSpellIdExact("Soul Link");
+    DEMONIC_EMPOWERMENT = ai->getSpellIdExact("Demonic Empowerment");
+    METAMORPHOSIS = ai->getSpellIdExact("Metamorphosis"); //Original is learn spell
+    SUMMON_IMP = ai->getSpellIdExact("Summon Imp");
+    SUMMON_VOIDWALKER = ai->getSpellIdExact("Summon Voidwalker");
+    SUMMON_SUCCUBUS = ai->getSpellIdExact("Summon Succubus");
+    SUMMON_FELHUNTER = ai->getSpellIdExact("Summon Felhunter");
+    SUMMON_FELGUARD = ai->getSpellIdExact("Summon Felguard");
+
+
+    //CC
+    FEAR = ai->getSpellIdExact("Fear");
+    HOWL_OF_TERROR = ai->getSpellIdExact("Howl of Terror");
+    BANISH = ai->getSpellIdExact("Banish");
+    ENSLAVE_DEMON = ai->getSpellIdExact("Enslave Demon");
+
+
+    //Buff
+    UNENDING_BREATH = ai->getSpellIdExact("Unending Breath");
+    DETECT_INVISIBILITY = ai->getSpellIdExact("Detect Invisibility");
+    SHADOW_WARD = ai->getSpellIdExact("Shadow Ward");
+
+
+    //Other
+    LIFE_TAP = ai->getSpellIdExact("Life Tap");
+    CREATE_SOULSTONE = ai->getSpellIdExact("Create Soulstone");
+
+
+    SOUL_SHARD = 6265; //Soul Shard Item id
+    P_BACKLASH = 34936; //Backlash proc
+    P_NIGHTFALL= 17941; //Nightfall proc
+    SHOOT = ai->getSpellIdExact("Shoot");
+
+    TALENT_DEMONOLOGY = SUMMON_FELGUARD;
+    TALENT_AFFLICTION = UNSTABLE_AFFLICTION;
+    TALENT_DESTRUCTION = CONFLAGRATE;
+
+    uint8 talentCounter = 0;
+    if (TALENT_DEMONOLOGY) talentCounter++;
+    if (TALENT_AFFLICTION) talentCounter++;
+    if (TALENT_DESTRUCTION) talentCounter++;
+    if (talentCounter > 1) { TALENT_DEMONOLOGY = 0; TALENT_AFFLICTION = 0; TALENT_DESTRUCTION = 0; } //Unreliable Talent detection.
+    #pragma endregion
+}
+
+void PlayerbotWarlockAI::DoNextCombatManeuver(Unit *pTarget)
+{
+    if (!pTarget || pTarget->isDead()) return;
+    PlayerbotAI *ai = GetAI();
+    if (!ai) return;
+    Player *m_bot = GetPlayerBot();
+    if (!m_bot || m_bot->isDead()) return;
+    Unit *pVictim = pTarget->getVictim();
+    Unit *m_tank = FindMainTankInRaid(GetMaster());
+    if (!m_tank && m_bot->GetGroup() && GetMaster()->GetGroup() != m_bot->GetGroup()) { FindMainTankInRaid(m_bot); }
+    if (!m_tank) { m_tank = m_bot; }
+    uint32 masterHP = GetMaster()->GetHealth()*100 / GetMaster()->GetMaxHealth();
+    float pDist = m_bot->GetDistance(pTarget);
+    uint8 pThreat = GetThreatPercent(pTarget);
+
+    Pet *pet = m_bot->GetPet();
+    if (m_tank->GetGUID() == m_bot->GetGUID() && pet && pet->isAlive() && pet->isInCombat()) { m_tank = pet; }
+    uint8 petThreat = 0;
+    if (pet) { GetThreatPercent(pTarget,pet); }
+
+    /*switch(ai->GetScenarioType())
+    {
+        case SCENARIO_DUEL:
+            if(SHADOW_BOLT > 0) CastSpell(SHADOW_BOLT);
+            return;
+    }*/
+
+    //------- Non Duel combat ----------
+
+    //ai->Follow(*GetMaster()); //don't want to melee mob
+
+    #pragma region Choose Target
+    // Choose actions accoring to talents (WARLOCK is always ranged dps)
+    m_role = BOT_ROLE_DPS_RANGED;
+
+    // if i am under attack and if i am not tank or offtank: change target if needed
+    if (m_tank->GetGUID() != m_bot->GetGUID() && isUnderAttack() )
+    {
+        // Keep hitting but reduce threat
+        if (CastSpell(SOULSHATTER,m_bot)) { return; }
+        //else if (m_bot->getRace() == (uint8) RACE_NIGHTELF && CastSpell(R_SHADOWMELD,m_bot)) { return; }
+        else //I cannot reduce threat so
+        {
+            if (pVictim && pVictim->GetGUID() == m_bot->GetGUID() && pDist <= 2) {  } // My target is almost up to me, no need to search
+            else //Have to select nearest target
+            {
+                Unit *curAtt = GetNearestAttackerOf(m_bot);
+                if (curAtt && curAtt->GetGUID() != pTarget->GetGUID())
+                {
+                    m_bot->SetSelection(curAtt->GetGUID());
+                    //ai->AddLootGUID(curAtt->GetGUID());
+                    DoNextCombatManeuver(curAtt); //Restart new update to get variables fixed..
+                    return;
+                }
+            }
+            //my target is attacking me
+        }
+    }
+
+    #pragma endregion
+
+    #pragma region Pet Actions
+    // Pet's own Actions
+    if( pet && pet->isAlive() )
+    {
+        // Setup pet
+        if (pet->GetCharmInfo()->IsAtStay()) {pet->GetCharmInfo()->SetCommandState(COMMAND_FOLLOW); }
+        //if (pet->HasSpell(BLOOD_PACT) && ); //Cast Blood Pact
+
+        if ( ( ((float)pet->GetHealth()/(float)pet->GetMaxHealth()) < 0.5f )
+        && ( HEALTH_FUNNEL>0 && !pet->getDeathState() != ALIVE && pVictim != m_bot
+        && CastSpell(HEALTH_FUNNEL,m_bot) )) { return; } //Heal pet
+
+        // Set pet to attack warlock's attacker > its own attackers > warlock's target
+        if (!pet->getVictim()) { pet->AI()->AttackStart(pTarget); }
+        else if (isUnderAttack(m_bot)) { pet->AI()->AttackStart(pTarget); }  //Always help warlock if he's under attack
+        else if (pet->getVictim()->GetGUID() != pTarget->GetGUID() && !isUnderAttack(pet)) { pet->AI()->AttackStart(pTarget); }
+        else if (isUnderAttack(pet)) // Pet is under attack and warlock has no attackers
+        {
+            if ( pet->getVictim()->getVictim() && pet->getVictim()->getVictim()->GetGUID() == pet->GetGUID() && pDist <= 2) {  } // My target is almost up to me, no need to search
+            else //Have to select nearest target
+            {
+                Unit *curAtt = GetNearestAttackerOf(pet,true);
+                if (curAtt && (!pet->getVictim() || curAtt->GetGUID() != pet->getVictim()->GetGUID()))
+                {
+                    pet->AI()->AttackStart(curAtt); //Attack nearest attacker
+                }
+            }
+            //Actions to do under attack (Always tank it, and try to kill it, until someone (!= warlock) takes aggro back)
+            //Warlock should help her pet whether main tank or not, unless he's being attacked (BEWARE Targeting Loop possibility)
+            if (pet->getVictim() && !isUnderAttack(m_bot) && pet->getVictim()->GetGUID() != pTarget->GetGUID())
+            {
+                m_bot->SetSelection(pet->getVictim()->GetGUID());
+                DoNextCombatManeuver(pet->getVictim()); //Restart new update to get variables fixed..
+                return;
+            }
+
+        }
+        // Pet tanking behaviour
+        if (pet->GetGUID() == m_tank->GetGUID() || isUnderAttack(m_bot) || isUnderAttack(pet))
+        {
+            //need pet tanking spells
+            //if (GROWL) pet->GetCharmInfo()->SetSpellAutocast(GROWL,true); //Autocast growl
+        }
+        else
+        {
+            //if (GROWL) pet->GetCharmInfo()->SetSpellAutocast(GROWL,false); //Do not try to get aggro
+        }
+        // NORMAL PET dps attacks
+        if (petThreat < threatThreshold || pet->GetGUID() == m_tank->GetGUID() || isUnderAttack(m_bot))
+        {
+            //if (CastSpell(KILL_COMMAND,m_bot)) { }
+        }
+    }
+    #pragma endregion
+
+    TakePosition(pTarget);
+    // If there's a cast stop
+    if (m_bot->HasUnitState(UNIT_STAT_CASTING)) { return; }
+
+    //Buff
+    if (isUnderAttack()) { if (CastSpell (DEMON_ARMOR, m_bot)) { return; } }
+    else if (FEL_ARMOR) { if (CastSpell(FEL_ARMOR,m_bot)) { return; } }
+    else if (CastSpell(DEMON_ARMOR, m_bot)) { return; }
+    if (CastSpell(METAMORPHOSIS,m_bot)) { return; }
+    if (CastSpell(DEMONIC_EMPOWERMENT,m_bot)) { return; }
+
+    //Protect
+    if (m_tank->GetGUID() != m_bot->GetGUID() && pVictim && pVictim->GetGUID() == m_bot->GetGUID() )
+    {
+        if (CastSpell(SOULSHATTER, m_bot)) { return; }
+        if (pTarget->GetCreatureType() == CREATURE_TYPE_DEMON && CastSpell(BANISH,pTarget)) { return; }
+        //if (m_bot->getRace() == (uint8) RACE_NIGHTELF && isUnderAttack() && CastSpell(R_SHADOWMELD, m_bot)) { return; }
+    }
+    if (isUnderAttack() && CastSpell(FEAR, pTarget)) { return; }
+    if (isUnderAttack() && CastSpell(HOWL_OF_TERROR, pTarget)) { return; }
+    if (isUnderAttack() && CastSpell(SHADOWFURY, pTarget)) { return; }
+    if (m_bot->getRace() == (uint8) RACE_DWARF && ai->GetHealthPercent() < 75 && CastSpell(R_STONEFORM,m_bot)) { } //no gcd
+    if (m_bot->getRace() == (uint8) RACE_DRAENEI && ai->GetHealthPercent() < 55 && CastSpell(R_GIFT_OF_NAARU,m_bot)) { return; } //no Gcd, but has cast
+    if (m_bot->getRace() == (uint8) RACE_TAUREN && pDist < 8 && CastSpell(R_WAR_STOMP, pTarget)) { return; } //no gcd but is cast
+    //Void Walker shield?
+    if (ai->GetHealthPercent() < 70 && CastSpell(DEATH_COIL,pTarget)) { return; }
+    if (ai->GetHealthPercent() < 70 && CastSpell(DRAIN_LIFE,pTarget)) { return; }
+    if (ai->GetManaPercent() < 70 && ai->GetManaPercent(*pTarget) > 10 && CastSpell(DRAIN_MANA,pTarget)) { return; }
+    if (ai->GetManaPercent() < 50 && pet && ai->GetManaPercent(*pet) > 50 && CastSpell(DARK_PACT,pet,1,0,1)) { return; }
+    if (ai->GetManaPercent() < 30 && ai->GetHealthPercent() > 60 && CastSpell(LIFE_TAP,m_bot,1,0,1)) { return; }
+    //Use Health stone
+
+    //Break spells
+    if (m_bot->getRace() == (uint8) RACE_BLOODELF && pDist < 8 && (pTarget->IsNonMeleeSpellCasted(true) || ai->GetManaPercent() < 40) && CastSpell(R_ARCANE_TORRENT, pTarget)) { } //no gcd
+    else if (pTarget->IsNonMeleeSpellCasted(true) && CastSpell(CURSE_OF_TONGUES, pTarget)) { return; }
+
+    //Catch
+    if (pTarget->HasUnitMovementFlag(UNIT_FLAG_FLEEING))
+    {
+        if (CastSpell(CURSE_OF_EXHAUSTION,pTarget)) return;
+    }
+
+    // Threat control
+    if (pThreat < threatThreshold || m_tank->GetGUID() == m_bot->GetGUID() ) { } //Continue attack
+    else
+    {
+        if (pet && isUnderAttack(pet) && pet->getVictim() && pet->getVictim()->GetGUID() != pTarget->GetGUID()) //Should be helping pet
+        {
+            m_bot->SetSelection(pet->getVictim()->GetGUID());
+            return;
+        }
+        else if (m_tank->getVictim() && m_tank->getVictim()->GetGUID() != pTarget->GetGUID()) // I am attacking wrong target!!
+        {
+            m_bot->SetSelection(m_tank->getVictim()->GetGUID());
+            return;
+        }
+        else if (CastSpell(SOULSHATTER,m_bot)) { return; }
+        else if (m_bot->FindCurrentSpellBySpellId(SHOOT)) { m_bot->InterruptNonMeleeSpells( true, SHOOT ); return; } //Disable wand
+        else { return; } // No more threat reducing spells, just slow down
+    }
+
+
+
+    //Urgent DPS
+    if ((m_bot->HasAura(P_NIGHTFALL) || m_bot->HasAura(P_BACKLASH)) && CastSpell(SHADOW_BOLT,pTarget)) { return; }
+    if (INCINERATE && pTarget->HasAura(IMMOLATE,m_bot->GetGUID()) && CastSpell(INCINERATE,pTarget)) { return; }
+    if (CONFLAGRATE && (pTarget->HasAura(IMMOLATE,m_bot->GetGUID()) || pTarget->HasAura(SHADOWFLAME,m_bot->GetGUID())) && CastSpell(CONFLAGRATE,pTarget)) { return; }
+
+    if (ai->GetHealthPercent(*pTarget) < 2 && CastSpell(SHADOWBURN,pTarget)) { return; }
+    if (ai->GetHealthPercent(*pTarget) < 5 && m_bot->GetItemCount(SOUL_SHARD) < 20 && CastSpell(DRAIN_SOUL,pTarget)) { return; }
+
+    //Dps up
+    if (ai->GetHealthPercent(*pTarget) > 95) { return; } // dont dps too early
+
+    if (m_bot->getRace() == (uint8) RACE_TROLL && CastSpell(R_BERSERKING,m_bot)) {} //no GCD
+    if (m_bot->getRace() == (uint8) RACE_ORC && CastSpell(R_BLOOD_FURY,m_bot)) {} //no GCD
+    if (CastSpell(HAUNT,pTarget)) { return; }
+
+    //AOE
+    if (isUnderAttack(m_tank,4) && CastSpell(SHADOWFLAME,pTarget)) { return; }
+    if (isUnderAttack(m_tank,4) && CastSpell(RAIN_OF_FIRE,pTarget)) { return; }
+
+    //Dps Main
+    if(CURSE_OF_ELEMENTS) { if (!CheckCurse(pTarget) && CastSpell(CURSE_OF_ELEMENTS, pTarget)) { return; } } //curse of elements trumps any other curses
+    else if (CURSE_OF_AGONY) { if (!CheckCurse(pTarget) && CastSpell(CURSE_OF_AGONY, pTarget)) { return; } }
+
+    if (SEED_OF_CORRUPTION && isUnderAttack(m_tank,4)) { if (CastSpell(SEED_OF_CORRUPTION,pTarget)) { return;} }
+    else if (CastSpell(CORRUPTION,pTarget)) { return; }
+
+    if (CastSpell(CHAOS_BOLT,pTarget)) { return; }
+
+    if (UNSTABLE_AFFLICTION) { if (CastSpell(UNSTABLE_AFFLICTION,pTarget)) { return; } }
+    else if (CastSpell(IMMOLATE,pTarget)) { return; }
+
+    if (CastSpell(SHADOW_BOLT,pTarget)) { return; }
+
+
+
+
+    //Use healthstone??
+    // drink poition
+    if(ai->GetManaPercent() < 5 )
+    {
+        Item *pItem = ai->FindPotion();
+        if(pItem != NULL)
+        {
+            if (pItem->GetSpell() && m_bot->HasSpellCooldown(pItem->GetSpell()) ) { return; } //pot is in cooldown
+            ai->UseItem(*pItem);
+        }
+    }
+
+} //end DoNextCombatManeuver
+
+void PlayerbotWarlockAI::DoNonCombatActions()
+{
+    PlayerbotAI *ai = GetAI();
+    Player *m_bot = GetPlayerBot();
+    if (!m_bot || !ai || m_bot->isDead()) { return; }
+
+    //If Casting or Eating/Drinking return
+    if (m_bot->HasUnitState(UNIT_STAT_CASTING)) { return; }
+    if (m_bot->getStandState() == UNIT_STAND_STATE_SIT) { return; }
+
+    //buff and heal raid
+    if (DoSupportRaid(m_bot,30,0,0,0,0,1)) { return; }
+
+    //Own Buffs
+    if (CastSpell (FEL_ARMOR, m_bot)) { return; }
+    if (!FEL_ARMOR && CastSpell(DEMON_ARMOR, m_bot)) { return; }
+    if (SOUL_LINK && m_bot->GetPet() && !m_bot->HasAuraType(SPELL_AURA_SPLIT_DAMAGE_PCT) && CastSpell(SOUL_LINK,m_bot)) { return; }
+
+    if(m_bot->GetPet() == NULL) {
+        if (SUMMON_FELGUARD)
+            CastSpell(SUMMON_FELGUARD, m_bot);
+        else
+            CastSpell(SUMMON_IMP, m_bot);
+    } else {
+    m_bot->GetPet()->GetCharmInfo()->SetCommandState(COMMAND_FOLLOW);
+    m_bot->GetPet()->GetCharmInfo()->SetIsCommandAttack(false);
+    }
+
+    //Create Healthstone?
+
+    //mana/hp check
+    //Don't bother with eating, if low on hp, just let it heal themself
+    if (m_bot->getRace() == (uint8) RACE_UNDEAD_PLAYER && ai->GetHealthPercent() < 75 && CastSpell(R_CANNIBALIZE,m_bot)) { return; }
+    if (ai->GetManaPercent() < 50 && ai->GetHealthPercent() > 60 && CastSpell (LIFE_TAP, m_bot)) { return; }
+    if (ai->GetManaPercent() < 50 || ai->GetHealthPercent() < 50) { ai->Feast(); }
+
+
+} //end DoNonCombatActions
+
+bool PlayerbotWarlockAI::BuffPlayer(Unit *target)
+{
+    if (!target || target->isDead()) return false;
+
+    if (!HasAuraName(target, DETECT_INVISIBILITY) && CastSpell(DETECT_INVISIBILITY, target)) { return true; }
+    if (!HasAuraName(target, UNENDING_BREATH) && CastSpell(UNENDING_BREATH, target)) { return true; }
+    return false;
+}
+
+uint32 PlayerbotWarlockAI::CheckCurse(Unit *target)
+{
+    Player *m_bot = GetPlayerBot();
+    if (!m_bot || m_bot->isDead() || !target || target->isDead() ) { return 0; }
+    uint64 mGuid = m_bot->GetGUID();
+
+    if (CURSE_OF_ELEMENTS && target->HasAura(CURSE_OF_ELEMENTS,mGuid)) { return CURSE_OF_ELEMENTS; }
+    if (CURSE_OF_AGONY && target->HasAura(CURSE_OF_AGONY,mGuid)) { return CURSE_OF_AGONY; }
+    if (CURSE_OF_TONGUES && target->HasAura(CURSE_OF_TONGUES,mGuid)) { return CURSE_OF_TONGUES; }
+    if (CURSE_OF_WEAKNESS && target->HasAura(CURSE_OF_WEAKNESS,mGuid)) { return CURSE_OF_WEAKNESS; }
+    if (CURSE_OF_DOOM && target->HasAura(CURSE_OF_DOOM,mGuid)) { return CURSE_OF_DOOM; }
+    if (CURSE_OF_RECKLESSNESS && target->HasAura(CURSE_OF_RECKLESSNESS,mGuid)) { return CURSE_OF_RECKLESSNESS; }
+    if (CURSE_OF_EXHAUSTION && target->HasAura(CURSE_OF_EXHAUSTION,mGuid)) { return CURSE_OF_EXHAUSTION; }
+    return 0;
+}
+
+//void PlayerbotWarlockAI::BuffPlayer(Player *target){}
diff --git a/src/server/game/AI/Bots/PlayerbotWarlockAI.h b/src/server/game/AI/Bots/PlayerbotWarlockAI.h
new file mode 100644
index 0000000..f5a2cb0
--- /dev/null
+++ b/src/server/game/AI/Bots/PlayerbotWarlockAI.h
@@ -0,0 +1,55 @@
+#ifndef _PlayerbotWarlockAI_H
+#define _PlayerbotWarlockAI_H
+
+#include "PlayerbotClassAI.h"
+
+//class Player;
+
+class PlayerbotWarlockAI : PlayerbotClassAI
+{
+    public:
+        PlayerbotWarlockAI(Player *const master, Player *const bot, PlayerbotAI *const ai);
+        virtual ~PlayerbotWarlockAI();
+
+        virtual void LoadSpells();
+
+    //all combat actions go here
+    void DoNextCombatManeuver(Unit *);
+
+    //all non combat actions go here, ex buffs, heals, rezzes
+    void DoNonCombatActions();
+
+    //buff a specific player, usually a real PC who is not in group
+    bool BuffPlayer(Unit *target);
+
+    uint32 CheckCurse(Unit *target);
+
+  private:
+    //CURSES
+    uint32 CURSE_OF_ELEMENTS, CURSE_OF_WEAKNESS, CURSE_OF_AGONY, CURSE_OF_RECKLESSNESS, CURSE_OF_TONGUES, CURSE_OF_DOOM, CURSE_OF_EXHAUSTION;
+
+    //AFFLICTION
+    uint32 CORRUPTION, DRAIN_SOUL, DRAIN_LIFE, DRAIN_MANA, SIPHON_LIFE, UNSTABLE_AFFLICTION, HAUNT, SEED_OF_CORRUPTION, DEATH_COIL;
+
+    //DESTRUCTION
+    uint32 SHADOW_BOLT, IMMOLATE, INCINERATE, SEARING_PAIN, CONFLAGRATE, SOUL_FIRE, SHADOWBURN, SHADOWFURY, CHAOS_BOLT, SHADOWFLAME, RAIN_OF_FIRE, HELLFIRE;
+
+    //DEMONOLOGY
+    uint32 DEMON_ARMOR, FEL_ARMOR, SOULSHATTER, HEALTH_FUNNEL, DARK_PACT, SOUL_LINK, DEMONIC_EMPOWERMENT, METAMORPHOSIS, SUMMON_IMP, SUMMON_VOIDWALKER, SUMMON_SUCCUBUS, SUMMON_FELHUNTER, SUMMON_FELGUARD;
+
+    //CC
+    uint32 FEAR, HOWL_OF_TERROR, BANISH, ENSLAVE_DEMON;
+
+    //Buff
+    uint32 UNENDING_BREATH, DETECT_INVISIBILITY, SHADOW_WARD;
+
+    //Other
+    uint32 LIFE_TAP, CREATE_SOULSTONE;
+
+    //Special
+    uint32 SOUL_SHARD, P_BACKLASH, P_NIGHTFALL, SHOOT;
+
+    uint32 TALENT_DEMONOLOGY, TALENT_AFFLICTION, TALENT_DESTRUCTION;
+};
+
+#endif
diff --git a/src/server/game/AI/Bots/PlayerbotWarriorAI.cpp b/src/server/game/AI/Bots/PlayerbotWarriorAI.cpp
new file mode 100644
index 0000000..29ad3ad
--- /dev/null
+++ b/src/server/game/AI/Bots/PlayerbotWarriorAI.cpp
@@ -0,0 +1,400 @@
+/*
+Name : PlayerbotWarrior.cpp
+Complete: maybe around 75%
+
+Limitations:    - Talent build decision is made by key talent spells, which makes them viable only after level 50-ish.. Until then default behaviour is Blood dps/offtank type
+                - Tanking bots should taunt if any group member is under attack, currently only saves master
+                - Situations needing Intervene casting : limited / non-existant..
+                - Intervene / Piercing Howl / Hamstring are not used..
+
+Authors : SwaLLoweD
+Version : 0.40
+*/
+
+#include "PlayerbotWarriorAI.h"
+
+class PlayerbotAI;
+PlayerbotWarriorAI::PlayerbotWarriorAI(Player *const master, Player *const bot, PlayerbotAI *const ai): PlayerbotClassAI(master, bot, ai)
+{
+    foodDrinkSpamTimer = 0;
+    LoadSpells();
+}
+PlayerbotWarriorAI::~PlayerbotWarriorAI(){}
+
+void PlayerbotWarriorAI::LoadSpells() {
+    PlayerbotAI *ai = GetAI();
+    if (!ai) return;
+   #pragma region SpellId Fill
+    //Defensive Stance
+    SHIELD_WALL = ai->getSpellIdExact("Shield Wall");
+    REVENGE = ai->getSpellIdExact("Revenge");
+    SHIELD_BLOCK = ai->getSpellIdExact("Shield Block");
+    DISARM = ai->getSpellIdExact("Disarm");
+    INTERVENE = ai->getSpellIdExact("Intervene");
+
+    //Berserker Stance
+    RECKLESSNESS = ai->getSpellIdExact("Recklessness");
+    WHIRLWIND = ai->getSpellIdExact("Whirlwind");
+    PUMMEL = ai->getSpellIdExact("Pummel");
+    INTERCEPT = ai->getSpellIdExact("Intercept");
+
+    //Battle Stance
+    RETALIATION = ai->getSpellIdExact("Retaliation");
+    CHARGE = ai->getSpellIdExact("Charge");
+    OVERPOWER = ai->getSpellIdExact("Overpower");
+    SHATTERING_THROW = ai->getSpellIdExact("Shattering Throw");
+
+    //Mixed Attacks
+    REND = ai->getSpellIdExact("Rend");                                // 1 2
+    THUNDER_CLAP = ai->getSpellIdExact("Thunder Clap");
+    SPELL_REFLECTION = ai->getSpellIdExact("Spell Reflection");
+    SHIELD_BASH = ai->getSpellIdExact("Shield Bash");
+    EXECUTE = ai->getSpellIdExact("Execute");                        // 1 3
+    HAMSTRING = ai->getSpellIdExact("Hamstring");
+    SWEEPING_STRIKES = ai->getSpellIdExact("Sweeping Strikes");
+    VICTORY_RUSH = ai->getSpellIdExact("Victory Rush");
+
+
+    //General attacks
+    HEROIC_STRIKE = ai->getSpellIdExact("Heroic Strike");
+    MORTAL_STRIKE = ai->getSpellIdExact("Mortal Strike");
+    BLOODTHIRST = ai->getSpellIdExact("Bloodthirst");
+    SHIELD_SLAM = ai->getSpellIdExact("Shield Slam");
+    SHOCKWAVE = ai->getSpellIdExact("Shockwave");
+    SLAM = ai->getSpellIdExact("Slam");
+    CLEAVE = ai->getSpellIdExact("Cleave");
+    BLADESTORM = ai->getSpellIdExact("Bladestorm");
+    HEROIC_THROW = ai->getSpellIdExact("Heroic Throw");
+    CONCUSSION_BLOW = ai->getSpellIdExact("Concussion Blow");
+    SUNDER_ARMOR = ai->getSpellIdExact("Sunder Armor");
+    DEMORALIZING_SHOUT = ai->getSpellIdExact("Demoralizing Shout");
+    INTIMIDATING_SHOUT = ai->getSpellIdExact("Intimidating Shout");
+    PIERCING_HOWL = ai->getSpellIdExact("Piercing Howl");
+    DEVASTATE = ai->getSpellIdExact("Devastate");
+
+
+    //buffs
+    COMMANDING_SHOUT = ai->getSpellIdExact("Commanding Shout");
+    BATTLE_SHOUT = ai->getSpellIdExact("Battle Shout");
+    VIGILANCE = ai->getSpellIdExact("Vigilance");
+    BERSERKER_RAGE = ai->getSpellIdExact("Berserker Rage");
+    ENRAGED_REGENERATION = ai->getSpellIdExact("Enraged Regeneration");
+    BLOODRAGE = ai->getSpellIdExact("Bloodrage");
+    LAST_STAND = ai->getSpellIdExact("Last Stand");
+    HEROIC_FURY = ai->getSpellIdExact("Heroic Fury");
+    DEATH_WISH = ai->getSpellIdExact("Death Wish");
+
+
+    //Stances
+    DEFENSIVE_STANCE = ai->getSpellIdExact("Defensive Stance");
+    BATTLE_STANCE = ai->getSpellIdExact("Battle Stance");
+    BERSERKER_STANCE = ai->getSpellIdExact("Berserker Stance");
+
+
+    //Taunts
+    TAUNT = ai->getSpellIdExact("Taunt");
+    CHALLENGING_SHOUT = ai->getSpellIdExact("Challenging Shout");
+    MOCKING_BLOW = ai->getSpellIdExact("Mocking Blow");
+
+    //Special
+    SLAMM = 46916; //Instant Slam (Blood Surge)
+
+    TALENT_ARMS = MORTAL_STRIKE;
+    TALENT_FURY = BLOODTHIRST;
+    TALENT_PROT = DEVASTATE;
+
+    SHOOT = ai->getSpellIdExact("Shoot");
+
+    uint8 talentCounter = 0;
+    if (TALENT_ARMS) talentCounter++;
+    if (TALENT_FURY) talentCounter++;
+    if (TALENT_PROT) talentCounter++;
+    if (talentCounter > 1) { TALENT_ARMS = 0; TALENT_FURY = 0; TALENT_PROT = 0; } //Unreliable Talent detection.
+    #pragma endregion
+}
+
+void PlayerbotWarriorAI::DoNextCombatManeuver(Unit *pTarget)
+{
+    if (!pTarget || pTarget->isDead()) return;
+    PlayerbotAI *ai = GetAI();
+    if (!ai) return;
+    Player *m_bot = GetPlayerBot();
+    if (!m_bot || m_bot->isDead()) return;
+    Unit *pVictim = pTarget->getVictim();
+    Unit *m_tank = FindMainTankInRaid(GetMaster());
+    if (!m_tank && m_bot->GetGroup() && GetMaster()->GetGroup() != m_bot->GetGroup()) { FindMainTankInRaid(m_bot); }
+    if (!m_tank) { m_tank = m_bot; }
+    uint32 masterHP = GetMaster()->GetHealth()*100 / GetMaster()->GetMaxHealth();
+    float pDist = m_bot->GetDistance(pTarget);
+    uint8 pThreat = GetThreatPercent(pTarget);
+
+    if (!m_pulling){
+        #pragma region Choose Role / Stance
+
+        m_role = BOT_ROLE_DPS_MELEE;
+
+        // Choose Stance
+        if (m_tank->GetGUID() == m_bot->GetGUID()) // Hey! I am Main Tank
+        {
+            if (ChangeStance(DEFENSIVE_STANCE)) { m_role = BOT_ROLE_TANK; return; }  //m_bot->GetShield(true)
+        }
+        else if (isUnderAttack()) // I am under attack
+        {
+            if (pVictim && pVictim->GetGUID() == m_bot->GetGUID() && pDist <= 2)  {} // My target is almost up to me, no need to search
+            else //Have to select nearest target
+            {
+                Unit *curAtt = GetNearestAttackerOf(m_bot);
+                if (curAtt && curAtt->GetGUID() != pTarget->GetGUID())
+                {
+                    m_bot->SetSelection(curAtt->GetGUID());
+                    //ai->AddLootGUID(curAtt->GetGUID());
+                    DoNextCombatManeuver(curAtt); //Restart new update to get variables fixed..
+                    return;
+                }
+            }
+            //my target is attacking me
+            //if (m_bot->getRace() == (uint8) RACE_NIGHTELF && CanCast(R_SHADOWMELD,m_bot) && CastSpell(R_SHADOWMELD,m_bot) ) { return; }
+            if (m_bot->GetShield(true)) { if (ChangeStance(DEFENSIVE_STANCE)) { m_role = BOT_ROLE_OFFTANK; return; } }
+            else if (ChangeStance(BATTLE_STANCE)) { return; }
+        }
+        else if (ai->GetHealthPercent() > 90)
+        {
+            if (ChangeStance(BERSERKER_STANCE)) { return; }
+        }
+        else if (ai->GetForm() != FORM_BERSERKERSTANCE || ai->GetHealthPercent() < 70 ) { if (ChangeStance(BATTLE_STANCE)) { return; } }
+        #pragma endregion
+    }
+
+    // Cast CC breakers if any match found  (does not work yet)
+    // uint32 ccSpells[7] = { HEROIC_FURY, BERSERKER_RAGE, BLADESTORM, R_ESCAPE_ARTIST, R_EVERY_MAN_FOR_HIMSELF, R_WILL_OF_FORSAKEN, R_STONEFORM };
+    // if (castSelfCCBreakers(ccSpells)) { } //most of them dont have gcd
+
+    TakePosition(pTarget);
+
+    // If there's a cast stop
+    if(m_bot->HasUnitState(UNIT_STAT_CASTING)) { return; }
+
+    if (m_pulling) {
+        if (GetAI()->CastSpell(SHOOT,pTarget)) {
+            m_pulling = false;
+            GetAI()->SetCombatOrder(ORDERS_NONE);
+            GetAI()->Follow(*GetMaster());
+            GetAI()->SetIgnoreUpdateTime(2);
+         }
+          return;
+    }
+
+    #pragma region Buff Heal Interrupt
+    //Buff UP
+    if (!m_bot->HasAura(BATTLE_SHOUT) && CastSpell(BATTLE_SHOUT,m_bot)) { return; }
+    if (!m_bot->HasAura(BATTLE_SHOUT,m_bot->GetGUID()) && !m_bot->HasAura(COMMANDING_SHOUT) && CastSpell(COMMANDING_SHOUT,m_bot)) { return; }
+
+
+    //HEAL UP && PROTECT UP
+    if (ai->GetHealthPercent() <= 85 && CastSpell(SHIELD_BLOCK, m_bot)) { } //no GCD
+    if (ai->GetHealthPercent() <= 45 && CastSpell(SHIELD_WALL, m_bot)) { return; }
+    if (ai->GetHealthPercent() < 55 &&
+        (m_bot->HasAura(BERSERKER_RAGE) || m_bot->HasAura(BLOODRAGE) || m_bot->HasAura(DEATH_WISH)) //There are other spells that count as enrage
+        && CastSpell(ENRAGED_REGENERATION,m_bot)) { return; }
+    if (ai->GetHealthPercent() < 25 && CastSpell(INTIMIDATING_SHOUT, m_bot)) { return; }
+    if (ai->GetHealthPercent() <= 75 && CastSpell(LAST_STAND, m_bot)) { return; }
+    if (m_bot->getRace() == (uint8) RACE_DWARF && ai->GetHealthPercent() < 75 && CastSpell(R_STONEFORM,m_bot)) { } //no gcd
+    if (m_bot->getRace() == (uint8) RACE_DRAENEI && ai->GetHealthPercent() < 55 && CastSpell(R_GIFT_OF_NAARU,m_bot)) { return; } //no Gcd, but has cast
+
+    //Break spells being cast
+    if (pTarget->IsNonMeleeSpellCasted(true))
+    {
+        if (pVictim && pVictim->GetGUID() == m_bot->GetGUID() && CastSpell(SPELL_REFLECTION,pTarget)) { return; }
+        if (m_bot->HasAura(SPELL_REFLECTION))
+        {
+            if (CastSpell(SHIELD_BASH,pTarget)) {} // No GCD
+            else if (CastSpell(PUMMEL,pTarget)) { return; }
+            else if (m_bot->getRace() == (uint8) RACE_BLOODELF && pDist < 8 && CastSpell(R_ARCANE_TORRENT, pTarget)) { } //no gcd
+        }
+    }
+    #pragma endregion
+
+    #pragma region Taunt / Threat
+    // if i am main tank, protect master by taunt
+    if(m_tank->GetGUID() == m_bot->GetGUID())
+    {
+        // Taunt if needed (Only for master)
+        Unit *curAtt = GetAttackerOf(GetMaster());
+        if (curAtt)
+        {
+            if (isUnderAttack(GetMaster(),2) && CastSpell(CHALLENGING_SHOUT, curAtt)) { return; }
+            if (CastSpell(TAUNT, curAtt,true,true))  { return; }
+            if (CastSpell(VIGILANCE, GetMaster())) { return; }
+            if (CastSpell(TAUNT, curAtt))  { return; }
+            if (CastSpell(MOCKING_BLOW, curAtt)) { return; }
+        }
+        // My target is not attacking me, taunt..
+        if (pVictim && pVictim->GetGUID() != m_bot->GetGUID())
+        {
+            if (CastSpell(VIGILANCE, pVictim)) { return; }
+            if (CastSpell(TAUNT, pTarget))  { return; }
+            if (CastSpell(MOCKING_BLOW, pTarget)) { return; }
+        }
+    }
+
+    // If not in Defensive Stance slow down due to threat
+    if (pThreat > threatThreshold && ai->GetForm() != FORM_DEFENSIVESTANCE && m_tank->GetGUID() != m_bot->GetGUID() && !isUnderAttack() )
+    {
+        if (m_tank->getVictim() && m_tank->getVictim()->GetGUID() != pTarget->GetGUID()) // I am attacking wrong target!!
+        {
+            m_bot->SetSelection(m_tank->getVictim()->GetGUID());
+            return;
+        }
+        else { return; } //Warrior has no threat reducing spells, just slow down
+    }
+    #pragma endregion
+
+    #pragma region Dps
+
+    //Ranged Stuff (Openers)
+    if (CastSpell(CHARGE,pTarget)) { } //no GCD
+    else if (CastSpell(INTERCEPT,pTarget)) { } //no GCD
+    if (pDist > MELEE_RANGE && ai->GetForm() == FORM_DEFENSIVESTANCE && CastSpell(HEROIC_THROW,pTarget)) { return; } //High threat
+    if (pDist > MELEE_RANGE && CastSpell(SHATTERING_THROW,pTarget)) { return; }
+
+    //Catch
+    if (pTarget->HasUnitMovementFlag(UNIT_FLAG_FLEEING))
+    {
+        if (CastSpell(HAMSTRING,pTarget)) return;
+        if (CastSpell(PIERCING_HOWL,pTarget)) return;
+    }
+
+
+    //Dps up
+    if (ai->GetHealthPercent() > 90 && ai->GetRageAmount() < 20 && CastSpell(BLOODRAGE,m_bot))  { return; }
+    if (isUnderAttack() && CastSpell(RETALIATION,m_bot)) { return; }
+    if (ai->GetHealthPercent() > 90 && CastSpell(DEATH_WISH,m_bot)) { return; }
+    if (ai->GetHealthPercent() > 80 && CastSpell(RECKLESSNESS,m_bot)) { return; }
+    if (m_bot->getRace() == (uint8) RACE_TROLL && CastSpell(R_BERSERKING,m_bot)) {} //no GCD
+    if (m_bot->getRace() == (uint8) RACE_ORC && CastSpell(R_BLOOD_FURY,m_bot)) {} //no GCD
+
+    //Tank only stuff
+    if ((ai->GetForm() == FORM_DEFENSIVESTANCE || ai->GetRageAmount() > 85) && CastSpell(THUNDER_CLAP)) { return; } //High threat
+    if ((ai->GetForm() == FORM_DEFENSIVESTANCE || ai->GetRageAmount() > 75) && CastSpell(HEROIC_STRIKE)) {} //nogcd high threat
+
+    //Finishing Move / Timed moves
+    if (ai->GetHealthPercent(*pTarget) < 20 && CastSpell(EXECUTE,pTarget)) { return; }
+    if (CastSpell(VICTORY_RUSH,pTarget)) { return; }
+
+    //AOE
+    if (CastSpell(SHOCKWAVE,pTarget)) { return; }
+    if ((isUnderAttack(m_tank,3) || m_tank->GetGUID() == m_bot->GetGUID()) && CastSpell(CLEAVE,pTarget)) {} //no GCD
+    if (isUnderAttack(m_tank,3) && CastSpell(SWEEPING_STRIKES,m_bot)) {} //no GCD
+    if (isUnderAttack(m_tank,4) && CastSpell(BLADESTORM,m_bot)) { return; }
+    if (isUnderAttack(m_tank,4) && CastSpell(WHIRLWIND,pTarget)) { return; }
+
+    //Main dps
+    if (m_bot->HasAura(SLAMM) && CastSpell(SLAM,pTarget)) { return; }  //instant slam only
+    if (CastSpell(REVENGE,pTarget)) { return; } //Def stance only
+    if (CastSpell(OVERPOWER,pTarget)) { return; }
+    if (CastSpell(SHIELD_SLAM,pTarget)) { return; }
+    if (CastSpell(BLOODTHIRST,pTarget)) { return; }
+    if (CastSpell(MORTAL_STRIKE,pTarget)) { return; }
+
+
+    //Support/Debuff
+    if (CastSpell(DEMORALIZING_SHOUT,pTarget)) { return; }
+    if (DEVASTATE) { if (CastSpell(DEVASTATE,pTarget,1,1)) { return; } }  //High threat
+    else if (CastSpell(SUNDER_ARMOR)) { return; } //Only 1 - High threat
+    if (CastSpell(CONCUSSION_BLOW,pTarget)) { return; }
+    if (CastSpell(REND,pTarget)) { return; }
+    if (CastSpell(DISARM,pTarget)) { return; }
+    #pragma endregion
+
+} //end DoNextCombatManeuver
+
+void PlayerbotWarriorAI::DoNonCombatActions()
+{
+    PlayerbotAI *ai = GetAI();
+    Player *m_bot = GetPlayerBot();
+    if (!m_bot || !ai || m_bot->isDead()) { return; }
+
+    //If Casting or Eating/Drinking return
+    if (m_bot->HasUnitState(UNIT_STAT_CASTING)) { return; }
+    if (m_bot->getStandState() == UNIT_STAND_STATE_SIT) { return; }
+
+    //Buff Up
+    if (!m_bot->HasAura(BATTLE_SHOUT) && CastSpell(BATTLE_SHOUT,m_bot)) { return; }
+    if (!m_bot->HasAura(BATTLE_SHOUT,m_bot->GetGUID()) && !m_bot->HasAura(COMMANDING_SHOUT) && CastSpell(COMMANDING_SHOUT,m_bot)) { return; }
+
+    if (GetMaster()->isAlive() && CastSpell(VIGILANCE, GetMaster())) { return; }
+
+    //want to start off in battle stance so we can CHARGE
+    //if(ai->GetRageAmount() < 20 && ai->GetForm() != FORM_BATTLESTANCE && ChangeStance(BATTLE_STANCE)) { return; }
+
+    //mana/hp check
+    if (m_bot->getRace() == (uint8) RACE_UNDEAD_PLAYER && ai->GetHealthPercent() < 75 && CastSpell(R_CANNIBALIZE,m_bot)) { return; }
+    if (ai->GetHealthPercent() < 75) { ai->Feast(); }
+} //end DoNonCombatActions
+
+bool PlayerbotWarriorAI::ChangeStance(uint32 stance)
+{
+    if (stance == 0) return false;
+    if (CastSpell(stance, GetPlayerBot())) { return true; }
+    return false;
+}
+
+void PlayerbotWarriorAI::Pull()
+{
+    if (!SHOOT) return;
+
+    // check ammo
+    uint32 ammo_id = GetPlayerBot()->GetUInt32Value(PLAYER_AMMO_ID);
+    if (!ammo_id) {
+        GetPlayerBot()->Say("I'm out of ammo.", LANG_UNIVERSAL);
+        return;
+    }
+
+    Unit* pTarget = ObjectAccessor::GetUnit(*GetMaster(), GetMaster()->GetSelection());
+    if (pTarget==NULL || pTarget->IsFriendlyTo(GetMaster()))
+    {
+        GetPlayerBot()->Say("Invalid target", LANG_UNIVERSAL);
+        m_pulling = false;
+        GetAI()->Follow(*GetMaster());
+        return;
+    }
+
+    m_role = BOT_ROLE_DPS_RANGED;
+    m_pulling = true;
+    GetAI()->SetIgnoreUpdateTime(0);
+}
+
+/*
+void PlayerbotWarriorAI::BreakCC(const uint32 diff)
+{
+    if(pvpTrinket_cd < diff && GCD < diff)
+    {
+        if(m_creature->HasAuraType(SPELL_AURA_MOD_ROOT) ||
+        m_creature->HasAuraType(SPELL_AURA_MOD_CONFUSE) || //dragons breath/blind/poly
+        m_creature->HasAura(8983)                       || //Druid bash rank 3
+        m_creature->HasAura(27006)                      || //Druid pounce rank 4
+        m_creature->HasAura(33786)                      || //Druid cyclone
+        m_creature->HasAura(22570, 1)                   || //Druid maim
+        m_creature->HasAura(10308)                      || //Paladin hammer of justice rank 4
+        m_creature->HasAura(30414, 1)                   || //Warlock shadowfury rank 3
+        m_creature->HasAura(6215)                       || //Warlock fear rank 3 **REMOVE THIS & IMPLEMENT IN BERSERKER RAGE**
+        m_creature->HasAura(17928)                      || //Warlock howlofterror rank 3 **REMOVE THIS & IMPLEMENT IN BERSERKER RAGE**
+        m_creature->HasAura(10890)                      || //Priest psychic scream rank 4 **REMOVE THIS & IMPLEMENT IN BERSERKER RAGE**
+        m_creature->HasAura(14902)                      || //Rogue Cheap shot
+        m_creature->HasAura(8643)                       || //Rogue Kidney shot Rank 2
+        m_creature->HasAura(38764, 2)                   || //Rogue Gouge Rank 6 **REMOVE THIS & IMPLEMENT IN BERSERKER RAGE**
+        m_creature->HasAura(12809))                        //Warrior concussion blow
+        {
+            doCast(m_creature, PVPTRINKET); //I think it would be better to instead of applying individual spells that apply the
+            pvpTrinket_cd = PVPTRINKET_CD;  //effect SPELL_AURA_MOD_STUN, just add that type and start removing bad choices e.g. impact.
+        }
+
+        if(m_creature->HasAura(11297) && m_creature->GetDistance(m_creature->getVictim()) < 10)
+        {      //if warrior sapped and creature is less then 10 yards from war, cast pvp trinket and attempt to demo shout him out of stealth
+            doCast(m_creature, PVPTRINKET);
+            pvpTrinket_cd = PVPTRINKET_CD;
+            castDemoralizingShout = true;
+        }
+    }
+} //BreakCC
+*/
diff --git a/src/server/game/AI/Bots/PlayerbotWarriorAI.h b/src/server/game/AI/Bots/PlayerbotWarriorAI.h
new file mode 100644
index 0000000..87ab4a8
--- /dev/null
+++ b/src/server/game/AI/Bots/PlayerbotWarriorAI.h
@@ -0,0 +1,57 @@
+#ifndef _PLAYERBOTWARRIORAI_H
+#define _PLAYERBOTWARRIORAI_H
+
+#include "PlayerbotClassAI.h"
+
+class PlayerbotWarriorAI : PlayerbotClassAI
+{
+    public:
+        PlayerbotWarriorAI(Player *const master, Player *const bot, PlayerbotAI *const ai);
+        virtual ~PlayerbotWarriorAI();
+
+        virtual void LoadSpells();
+
+        //all combat actions go here
+        void DoNextCombatManeuver(Unit *);
+
+        //all non combat actions go here, ex buffs, heals, rezzes
+        void DoNonCombatActions();
+
+        virtual void Pull();
+
+    private:
+        //Defensive Stance
+        uint32 SHIELD_WALL, REVENGE, SHIELD_BLOCK, DISARM, INTERVENE;
+
+        //Berserker Stance
+        uint32 RECKLESSNESS, WHIRLWIND, PUMMEL, INTERCEPT;
+
+        //Battle Stance
+        uint32 RETALIATION, CHARGE, OVERPOWER, SHATTERING_THROW;
+
+        //Mixed Attacks                                              //1 3
+        uint32 REND, THUNDER_CLAP, SPELL_REFLECTION, SHIELD_BASH, EXECUTE, HAMSTRING, SWEEPING_STRIKES, VICTORY_RUSH;
+
+        //General attacks
+        uint32 HEROIC_STRIKE, MORTAL_STRIKE, BLOODTHIRST, SHIELD_SLAM, SHOCKWAVE, SLAM, CLEAVE, BLADESTORM, HEROIC_THROW, CONCUSSION_BLOW, SUNDER_ARMOR, DEMORALIZING_SHOUT, INTIMIDATING_SHOUT, PIERCING_HOWL, DEVASTATE;
+
+        //buffs
+        uint32 COMMANDING_SHOUT, BATTLE_SHOUT, VIGILANCE, BERSERKER_RAGE, ENRAGED_REGENERATION, BLOODRAGE, LAST_STAND, HEROIC_FURY, DEATH_WISH;
+
+        //Stances
+        uint32 DEFENSIVE_STANCE, BATTLE_STANCE, BERSERKER_STANCE;
+
+        //Taunts
+        uint32 TAUNT, CHALLENGING_SHOUT, MOCKING_BLOW;
+
+        //Special
+        uint32 SLAMM;
+
+        uint32 TALENT_ARMS, TALENT_FURY, TALENT_PROT;
+
+        bool ChangeStance(uint32 stance);
+
+
+};
+
+#endif
diff --git a/src/server/game/AI/CoreAI/PetAI.cpp b/src/server/game/AI/CoreAI/PetAI.cpp
index ac142db..81d9bfe 100755
--- a/src/server/game/AI/CoreAI/PetAI.cpp
+++ b/src/server/game/AI/CoreAI/PetAI.cpp
@@ -46,6 +46,7 @@ PetAI::PetAI(Creature *c) : CreatureAI(c), i_tracker(TIME_INTERVAL_LOOK)
 
 void PetAI::EnterEvadeMode()
 {
+    if(me->GetIAmABot() && me->GetBotAI()) me->GetBotAI()->EnterEvadeMode();
 }
 
 bool PetAI::_needToStop()
@@ -83,6 +84,12 @@ void PetAI::UpdateAI(const uint32 diff)
     if (!me->isAlive())
         return;
 
+    if(me->GetIAmABot())
+    {
+        //don't do anything if eating or drinking, otherwise call UpdateAI
+        if(!me->HasAura(10256) && !me->HasAura(1137) && me->GetBotAI()) me->GetBotAI()->UpdateAI(diff);
+    }
+
     Unit* owner = me->GetCharmerOrOwner();
 
     if (m_updateAlliesTimer <= diff)
@@ -295,6 +302,8 @@ void PetAI::KilledUnit(Unit *victim)
 
 void PetAI::AttackStart(Unit *target)
 {
+    if (me->GetCharmInfo() == NULL) return;
+
     // Overrides Unit::AttackStart to correctly evaluate Pet states
 
     // Check all pet states to decide if we can attack this target
diff --git a/src/server/game/CMakeLists.txt b/src/server/game/CMakeLists.txt
index 323a3ac..be5240c 100644
--- a/src/server/game/CMakeLists.txt
+++ b/src/server/game/CMakeLists.txt
@@ -107,6 +107,7 @@ include_directories(
   ${CMAKE_SOURCE_DIR}/dep/zlib
   ${CMAKE_SOURCE_DIR}/src/server/collision
   ${CMAKE_SOURCE_DIR}/src/server/collision/Management
+  ${CMAKE_SOURCE_DIR}/src/server/game/AI/Bots
   ${CMAKE_SOURCE_DIR}/src/server/shared
   ${CMAKE_SOURCE_DIR}/src/server/shared/Configuration
   ${CMAKE_SOURCE_DIR}/src/server/shared/Cryptography
@@ -127,6 +128,7 @@ include_directories(
   ${CMAKE_CURRENT_SOURCE_DIR}/Achievements
   ${CMAKE_CURRENT_SOURCE_DIR}/Addons
   ${CMAKE_CURRENT_SOURCE_DIR}/AI
+  ${CMAKE_CURRENT_SOURCE_DIR}/AI/Bots
   ${CMAKE_CURRENT_SOURCE_DIR}/AI/CoreAI
   ${CMAKE_CURRENT_SOURCE_DIR}/AI/EventAI
   ${CMAKE_CURRENT_SOURCE_DIR}/AI/ScriptedAI
diff --git a/src/server/game/Chat/Chat.cpp b/src/server/game/Chat/Chat.cpp
index 30fe5de..668da5e 100755
--- a/src/server/game/Chat/Chat.cpp
+++ b/src/server/game/Chat/Chat.cpp
@@ -437,6 +437,9 @@ ChatCommand * ChatHandler::getCommandTable()
         { "bindsight",      SEC_ADMINISTRATOR,  false, OldHandler<&ChatHandler::HandleBindSightCommand>,           "", NULL },
         { "unbindsight",    SEC_ADMINISTRATOR,  false, OldHandler<&ChatHandler::HandleUnbindSightCommand>,         "", NULL },
         { "playall",        SEC_GAMEMASTER,  false, OldHandler<&ChatHandler::HandlePlayAllCommand>,             "", NULL },
+        // Playerbot mod
+        { "bot",            SEC_ADMINISTRATOR,  false, OldHandler<&ChatHandler::HandlePlayerbotCommand>,             "", NULL },
+        { "maintank",       SEC_PLAYER,  false, OldHandler<&ChatHandler::HandlePlayerbotMainTankCommand>,             "", NULL },
         { NULL,             0,                  false, NULL,                                           "", NULL }
     };
 
diff --git a/src/server/game/Chat/Chat.h b/src/server/game/Chat/Chat.h
index 486987d..cb2d923 100755
--- a/src/server/game/Chat/Chat.h
+++ b/src/server/game/Chat/Chat.h
@@ -172,6 +172,8 @@ class ChatHandler
         bool HandleUnPossessCommand(const char* args);
         bool HandleBindSightCommand(const char* args);
         bool HandleUnbindSightCommand(const char* args);
+        bool HandlePlayerbotCommand(const char *args);
+        bool HandlePlayerbotMainTankCommand(const char *args);
 
         bool HandleGuildCreateCommand(const char* args);
         bool HandleGuildInviteCommand(const char* args);
@@ -346,6 +348,10 @@ class ChatHandler
         bool HandleQuestRemove(const char * args);
         bool HandleQuestComplete(const char * args);*/
 
+		bool BotHandleQuestAdd(const char *args);
+		bool BotHandleQuestRemove(const char *args);
+
+
         //bool HandleSet32Bit(const char* args);
         bool HandleSaveAllCommand(const char* args);
 
diff --git a/src/server/game/Chat/Commands/Level0.cpp b/src/server/game/Chat/Commands/Level0.cpp
index 5deed67..f3714b1 100755
--- a/src/server/game/Chat/Commands/Level0.cpp
+++ b/src/server/game/Chat/Commands/Level0.cpp
@@ -23,11 +23,13 @@
 #include "Opcodes.h"
 #include "Chat.h"
 #include "ObjectAccessor.h"
+#include "ObjectMgr.h"
 #include "Language.h"
 #include "AccountMgr.h"
 #include "SystemConfig.h"
 #include "revision.h"
 #include "Util.h"
+#include "Group.h"
 
 bool ChatHandler::HandleHelpCommand(const char* args)
 {
@@ -144,6 +146,185 @@ bool ChatHandler::HandleSaveCommand(const char* /*args*/)
     return true;
 }
 
+//Playerbot mod
+bool ChatHandler::HandlePlayerbotCommand(const char *args)
+{
+    if(!m_session)
+    {
+        PSendSysMessage("You may only add bots from an active session");
+        SetSentErrorMessage(true);
+        return false;
+    }
+
+    if(!*args)
+    {
+        PSendSysMessage("usage: add PLAYERNAME  or  remove PLAYERNAME");
+        SetSentErrorMessage(true);
+        return false;
+    }
+
+    char *cmd = strtok ((char*)args, " ");
+    char *charname = strtok (NULL, " ");
+    if(!cmd || !charname)
+    {
+        PSendSysMessage("usage: add PLAYERNAME  or  remove PLAYERNAME");
+        SetSentErrorMessage(true);
+        return false;
+    }
+
+    std::string cmdStr = cmd;
+    std::string charnameStr = charname;
+    uint64 guid;
+
+   if (charnameStr.compare("all") != 0)
+   {
+       if (!normalizePlayerName(charnameStr))
+           return false;
+
+       guid = sObjectMgr->GetPlayerGUIDByName(charnameStr.c_str());
+       if (guid == 0 || (guid == m_session->GetPlayer()->GetGUID()))
+       {
+           SendSysMessage(LANG_PLAYER_NOT_FOUND);
+           SetSentErrorMessage(true);
+           return false;
+       }
+
+       uint32 accountId = sObjectMgr->GetPlayerAccountIdByGUID(guid);
+       if (accountId != m_session->GetAccountId())
+       {
+           PSendSysMessage("You may only add bots from the same account.");
+           SetSentErrorMessage(true);
+           return false;
+       }
+   }
+
+    if (cmdStr.compare("add") == 0 || cmdStr.compare("login") == 0)
+    {
+        if (charnameStr.compare("all") == 0)
+        {
+            std::list<std::string> *names;
+            names=m_session->GetPlayer()->GetCharacterList();
+            std::list<std::string>::iterator iter,next;
+            for (iter = names->begin(); iter != names->end(); iter++)
+            {
+                std::stringstream arg;
+                arg << "add " << (*iter).c_str();
+                HandlePlayerbotCommand(arg.str().c_str());
+            }
+            PSendSysMessage("Bots added successfully.");
+            return true;
+        }
+        else
+        {
+            if(m_session->GetPlayerBot(guid) != NULL)
+            {
+                PSendSysMessage("Bot already exists in world.");
+                SetSentErrorMessage(true);
+                return false;
+            }
+            m_session->AddPlayerBot(guid);
+        }
+
+    }
+    else if (cmdStr.compare("remove") == 0 || cmdStr.compare("logout") == 0)
+    {
+        if (charnameStr.compare("all") == 0)
+        {
+            std::list<std::string> *names = new std::list<std::string>;
+            for (PlayerBotMap::const_iterator iter = m_session->GetPlayerBotsBegin(); iter != m_session->GetPlayerBotsEnd(); ++iter)
+            {
+                names->push_back(iter->second->GetName());
+            }
+            std::list<std::string>::iterator iter,next;
+            for (iter = names->begin(); iter != names->end(); iter++)
+            {
+                std::stringstream arg;
+                arg << "remove " << (*iter).c_str();
+                HandlePlayerbotCommand(arg.str().c_str());
+            }
+            return true;
+        }
+        else
+        {
+            if (m_session->GetPlayerBot(guid) == NULL)
+            {
+                PSendSysMessage("Bot can not be removed because bot does not exst in world.");
+                SetSentErrorMessage(true);
+                return false;
+            }
+            m_session->LogoutPlayerBot(guid, true);
+            PSendSysMessage("Bot removed successfully.");
+            return true;
+        }
+    }
+    return true;
+}
+
+bool ChatHandler::HandlePlayerbotMainTankCommand(const char *args)
+{
+    uint64 guid = 0;
+    uint64 pGuid = 0;
+    char *charname ;
+    Group *group = m_session->GetPlayer()->GetGroup();
+
+    if (group == NULL) {
+        PSendSysMessage("Must be in a group to set a main tank.");
+        SetSentErrorMessage(true);
+        return false;
+    }
+
+    QueryResult result = CharacterDatabase.PQuery("SELECT memberGuid FROM group_member WHERE memberFlags='%u' AND guid = '%u'",MEMBER_FLAG_MAINTANK, group->GetGUID());
+    if(result)
+    {
+        pGuid = MAKE_NEW_GUID(result->Fetch()->GetInt32(),0,HIGHGUID_PLAYER);
+    }
+
+    // if no arguments are passed in, just say who the current main tank is
+    if(!*args) {
+
+        if (pGuid>0) {
+            Player *pPlayer = sObjectMgr->GetPlayer(pGuid);
+
+            if (pPlayer  && pPlayer->isAlive()){
+                PSendSysMessage("Main tank is %s.", pPlayer->GetName());
+                return true;
+            }
+        }
+
+        PSendSysMessage("Currently there is no main tank. ");
+        return true;
+    } else {
+        charname = strtok ((char*)args, " ");
+        std::string charnameStr = charname;
+        guid = sObjectMgr->GetPlayerGUIDByName(charnameStr.c_str());
+
+        // clear if same player
+        if (pGuid==guid) {
+            group->SetMainTank(guid, false);
+            PSendSysMessage("Main tank has been cleared. ");
+            return true;
+        }
+
+        if (m_session->GetPlayer()->GetGroup()->IsMember(guid)) {
+            group->SetMainTank(pGuid,false); // clear old one
+            group->SetMainTank(guid, true);  // set new one
+            Player *pPlayer = sObjectMgr->GetPlayer(guid);
+            if (pPlayer->IsInWorld())
+                PSendSysMessage("Main tank is %s.", pPlayer->GetName());
+            else
+                PSendSysMessage("Player is not online.");
+
+        } else {
+            PSendSysMessage("Player is not in your group.");
+        }
+
+    }
+
+
+    return true;
+}
+
+
 /// Display the 'Message of the day' for the realm
 bool ChatHandler::HandleServerMotdCommand(const char* /*args*/)
 {
diff --git a/src/server/game/Chat/Commands/Level1.cpp b/src/server/game/Chat/Commands/Level1.cpp
index d5acf4b..f9b9eb2 100755
--- a/src/server/game/Chat/Commands/Level1.cpp
+++ b/src/server/game/Chat/Commands/Level1.cpp
@@ -270,9 +270,10 @@ bool ChatHandler::HandleSummonCommand(const char* args)
                 target->UnbindInstance(pMap->GetInstanceId(), target->GetDungeonDifficulty(), true);
 
             // we are in instance, and can summon only player in our group with us as lead
-            if (!m_session->GetPlayer()->GetGroup() || !target->GetGroup() ||
+            if(!target->IsPlayerbot() &&
+                (!m_session->GetPlayer()->GetGroup() || !target->GetGroup() ||
                 (target->GetGroup()->GetLeaderGUID() != m_session->GetPlayer()->GetGUID()) ||
-                (m_session->GetPlayer()->GetGroup()->GetLeaderGUID() != m_session->GetPlayer()->GetGUID()))
+                (m_session->GetPlayer()->GetGroup()->GetLeaderGUID() != m_session->GetPlayer()->GetGUID())))
                 // the last check is a bit excessive, but let it be, just in case
             {
                 PSendSysMessage(LANG_CANNOT_SUMMON_TO_INST,nameLink.c_str());
@@ -320,7 +321,6 @@ bool ChatHandler::HandleSummonCommand(const char* args)
             m_session->GetPlayer()->GetZoneId(),
             target_guid);
     }
-
     return true;
 }
 
diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp
index 13e3234..5c0e729 100755
--- a/src/server/game/Entities/Creature/Creature.cpp
+++ b/src/server/game/Entities/Creature/Creature.cpp
@@ -152,6 +152,9 @@ m_formation(NULL)
     for (uint8 i = 0; i < CREATURE_MAX_SPELLS; ++i)
         m_spells[i] = 0;
 
+    is_a_bot = false;
+    bot_AI = NULL;
+
     m_CreatureSpellCooldowns.clear();
     m_CreatureCategoryCooldowns.clear();
     m_GlobalCooldown = 0;
diff --git a/src/server/game/Entities/Creature/Creature.h b/src/server/game/Entities/Creature/Creature.h
index 5794167..a607fe1 100755
--- a/src/server/game/Entities/Creature/Creature.h
+++ b/src/server/game/Entities/Creature/Creature.h
@@ -481,6 +481,18 @@ class Creature : public Unit, public GridObject<Creature>
         void AI_SendMoveToPacket(float x, float y, float z, uint32 time, uint32 MovementFlags, uint8 type);
         CreatureAI * AI() const { return (CreatureAI*)i_AI; }
 
+        //
+        //Bot commands
+        //
+        void SetBotAI(CreatureAI *newAI)
+        {
+            bot_AI = newAI;
+        }
+        CreatureAI *GetBotAI(){ return bot_AI; }
+        void SetIAmABot(bool bot){ is_a_bot = bot; }
+        bool GetIAmABot(){ return is_a_bot; }
+
+
         uint32 GetShieldBlockValue() const                  //dunno mob block value
         {
             return (getLevel()/2 + uint32(GetStat(STAT_STRENGTH)/20));
@@ -723,6 +735,9 @@ class Creature : public Unit, public GridObject<Creature>
 
         bool isVisibleForInState(WorldObject const* seer) const;
     private:
+        bool is_a_bot;
+        CreatureAI *bot_AI;
+
         //WaypointMovementGenerator vars
         uint32 m_waypointID;
         uint32 m_path_id;
diff --git a/src/server/game/Entities/Creature/GossipDef.h b/src/server/game/Entities/Creature/GossipDef.h
index 2b6fe7d..e2e8f71 100755
--- a/src/server/game/Entities/Creature/GossipDef.h
+++ b/src/server/game/Entities/Creature/GossipDef.h
@@ -50,6 +50,7 @@ enum Gossip_Option
     GOSSIP_OPTION_UNLEARNPETTALENTS = 17,                   //UNIT_NPC_FLAG_TRAINER             (16) (bonus option for GOSSIP_OPTION_TRAINER)
     GOSSIP_OPTION_LEARNDUALSPEC     = 18,                   //UNIT_NPC_FLAG_TRAINER             (16) (bonus option for GOSSIP_OPTION_TRAINER)
     GOSSIP_OPTION_OUTDOORPVP        = 19,                   //added by code (option for outdoor pvp creatures)
+    GOSSIP_OPTION_BOT               = 20,
     GOSSIP_OPTION_MAX
 };
 
diff --git a/src/server/game/Entities/Creature/TemporarySummon.cpp b/src/server/game/Entities/Creature/TemporarySummon.cpp
index 1204a10..dc4a7f1 100755
--- a/src/server/game/Entities/Creature/TemporarySummon.cpp
+++ b/src/server/game/Entities/Creature/TemporarySummon.cpp
@@ -238,6 +238,14 @@ void TempSummon::UnSummon()
     if (owner && owner->GetTypeId() == TYPEID_UNIT && owner->ToCreature()->IsAIEnabled)
         owner->ToCreature()->AI()->SummonedCreatureDespawn(this);
 
+    if (owner &&
+        owner->GetTypeId() == TYPEID_PLAYER &&
+       ((Player*)owner)->HaveBot() &&
+       ((Player*)owner)->GetBot()->GetGUID()==this->GetGUID() &&
+       this->isDead()) {    // dont unsummon corpse if a bot
+        return;
+    }
+
     AddObjectToRemoveList();
 }
 
diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp
index ff08de3..4031e42 100755
--- a/src/server/game/Entities/Object/Object.cpp
+++ b/src/server/game/Entities/Object/Object.cpp
@@ -1568,9 +1568,16 @@ void WorldObject::GetRandomPoint(const Position &pos, float distance, float &ran
 
 void WorldObject::UpdateGroundPositionZ(float x, float y, float &z) const
 {
-    float new_z = GetBaseMap()->GetHeight(x,y,z,true);
-    if (new_z > INVALID_HEIGHT)
-        z = new_z+ 0.05f;                                   // just to be sure that we are not a few pixel under the surface
+    float map_z = GetBaseMap()->GetHeight(x,y,z,false);
+    float vmap_z = GetBaseMap()->GetHeight(x,y,z,true);
+
+    if(vmap_z > INVALID_HEIGHT)
+       z = vmap_z;    // add or subtract say 0.05f, to adjust bot hover height
+
+    if((map_z > vmap_z) && (map_z > z))
+       z = map_z;
+
+    Trinity::NormalizeMapCoord(z);
 }
 
 bool Position::IsPositionValid() const
@@ -2106,7 +2113,19 @@ TempSummon *Map::SummonCreature(uint32 entry, const Position &pos, SummonPropert
         case UNIT_MASK_SUMMON:    summon = new TempSummon (properties, summoner);  break;
         case UNIT_MASK_GUARDIAN:  summon = new Guardian   (properties, summoner);  break;
         case UNIT_MASK_PUPPET:    summon = new Puppet     (properties, summoner);  break;
-        case UNIT_MASK_TOTEM:     summon = new Totem      (properties, summoner);  break;
+        case UNIT_MASK_TOTEM:
+        {
+            if(summoner->isCharmed())
+            {
+                //If the caster is charmed, assume it is a Bot.  This might not always be
+                //the case, but oh well.  This will allow the affects of the totem
+                //(ex healing, stoneskin, etc, to affect the bot owner insteadof the
+                //bot. Thats ok, the bot is expendable  :-)
+                summon = new Totem      (properties, summoner->GetCharmer());  break;
+            } else {
+                summon = new Totem      (properties, summoner);  break;
+            }
+        }
         case UNIT_MASK_MINION:    summon = new Minion     (properties, summoner);  break;
         default:    return NULL;
     }
diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp
index 45fcef3..1fe3d90 100755
--- a/src/server/game/Entities/Player/Player.cpp
+++ b/src/server/game/Entities/Player/Player.cpp
@@ -69,6 +69,11 @@
 #include "LFGMgr.h"
 #include "CharacterDatabaseCleaner.h"
 #include <cmath>
+// Playerbot mod
+#include "CreatureAIFactory.h"
+#include "Config.h"
+#include "PlayerbotAI.h"
+#include "PlayerbotClassAI.h"
 
 #define ZONE_UPDATE_INTERVAL (1*IN_MILLISECONDS)
 
@@ -389,8 +394,11 @@ UpdateMask Player::updateVisualBits;
 #ifdef _MSC_VER
 #pragma warning(disable:4355)
 #endif
-Player::Player (WorldSession *session): Unit(), m_achievementMgr(this), m_reputationMgr(this)
+Player::Player (WorldSession *session): Unit(), m_achievementMgr(this), m_reputationMgr(this), m_MaxPlayerbots(9), m_bot_died(false)
 {
+    // Playerbot mod
+    m_playerbotAI = NULL;
+
 #ifdef _MSC_VER
 #pragma warning(default:4355)
 #endif
@@ -608,6 +616,22 @@ Player::Player (WorldSession *session): Unit(), m_achievementMgr(this), m_reputa
 
     m_ChampioningFaction = 0;
 
+    ///////////////////// Bot System ////////////////////////
+    m_botTimer = 0;
+    m_bot = NULL;
+    m_bot_form = 0;
+    m_bot_race = 0;
+    m_bot_class = 0;
+    m_bot_must_wait_for_spell_1 = 0;
+    m_bot_must_wait_for_spell_2 = 0;
+    m_bot_must_wait_for_spell_3 = 0;
+    m_bot_must_be_created = false;
+    m_bot_must_die = false;
+    m_bot_entry_must_be_created = 0;
+    m_bot_class_must_be_created = 0;
+    m_bot_race_must_be_created = 0;
+    m_bot_entry = 0;
+
     for (uint8 i = 0; i < MAX_POWERS; ++i)
         m_powerFraction[i] = 0;
 
@@ -652,6 +676,13 @@ Player::~Player ()
     delete m_declinedname;
     delete m_runes;
 
+    //Playerbot mod: remove AI if exists
+    if(m_playerbotAI != NULL)
+    {
+        delete m_playerbotAI;
+        m_playerbotAI = NULL;
+    }
+
     sWorld->DecreasePlayerCount();
 }
 
@@ -665,6 +696,27 @@ void Player::CleanupsBeforeDelete(bool finalCleanup)
     if (m_transport)
         m_transport->RemovePassenger(this);
 
+    if(GetGroup() && HaveBot())
+    {
+         Creature *m_bot = GetBot();
+         Group *m_group = GetGroup();
+
+         //removing bot from group
+         if(m_group->IsMember(m_bot->GetGUID()))
+         {
+             //deleting bot from group
+             if(m_group->RemoveMember(m_bot->GetGUID(), GROUP_REMOVEMETHOD_DEFAULT) < 1) // 99 means I'm a bot
+             {
+                 //no one left in group so deleting group
+                 delete m_group;
+                 sObjectMgr->RemoveGroup(m_group);
+             }
+         }
+         m_bot->SetCharmerGUID(0);
+         m_bot->RemoveFromWorld();
+         RemoveBot();
+    }
+
     // clean up player-instance binds, may unload some instance saves
     for (uint8 i = 0; i < MAX_DIFFICULTY; ++i)
         for (BoundInstancesMap::iterator itr = m_boundInstances[i].begin(); itr != m_boundInstances[i].end(); ++itr)
@@ -1463,6 +1515,22 @@ void Player::Update(uint32 p_time)
         RegenerateAll();
     }
 
+
+    //want to refresh bot even if we're dead so
+    //it can rez me
+    if(HaveBot() || GetBotMustBeCreated() || m_bot_died)
+        RefreshBot(p_time);
+
+
+    if(m_botTimer > 0)
+    {
+        if(p_time >= m_botTimer)
+            m_botTimer = 0;
+        else
+            m_botTimer -= p_time;
+    }
+
+
     if (m_deathState == JUST_DIED)
         KillPlayer();
 
@@ -1526,6 +1594,9 @@ void Player::Update(uint32 p_time)
     //because we don't want player's ghost teleported from graveyard
     if (IsHasDelayedTeleport() && isAlive())
         TeleportTo(m_teleport_dest, m_teleport_options);
+
+    //Playerbot mod: this was added as part of the Playerbot mod,
+    if(m_playerbotAI != NULL) m_playerbotAI->UpdateAI(p_time);
 }
 
 void Player::setDeathState(DeathState s)
@@ -1782,7 +1853,8 @@ void Player::SendTeleportPacket(Position &oldPos)
     WorldPacket data2(MSG_MOVE_TELEPORT, 38);
     data2.append(GetPackGUID());
     BuildMovementPacket(&data2);
-    Relocate(&oldPos);
+    // Relocate(&oldPos);
+    if (!this->IsPlayerbot()) Relocate(&oldPos);
     SendMessageToSet(&data2, false);
 }
 
@@ -1832,6 +1904,16 @@ bool Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientati
         return false;
     }
 
+    //Playerbot mod: if this user has bots, tell them to stop following master
+    //so they don't try to follow the master after the master teleports
+    for(PlayerBotMap::const_iterator itr = GetSession()->GetPlayerBotsBegin(); itr != GetSession()->GetPlayerBotsEnd(); ++itr)
+    {
+            Player *botPlayer = itr->second;
+            botPlayer->GetMotionMaster()->Clear();
+    }
+
+
+
     // preparing unsummon pet if lost (we must get pet before teleportation or will not find it later)
     Pet* pet = GetPet();
 
@@ -1881,6 +1963,33 @@ bool Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientati
         }
     }
 
+     //HACK ELSE CLIENT CRASH WHEN PLAYER IS TELEPORTED
+     if(GetGroup() && HaveBot())
+     {
+         //sLog->outError("Player::teleporting.. removing from group");
+
+         Group *m_group = GetGroup();
+         Creature *m_bot = GetBot();
+
+         //removing bot from group
+         if(m_group->IsMember(m_bot->GetGUID()))
+         {
+             //deleting bot from group
+             if(m_group->RemoveMember(m_bot->GetGUID(), GROUP_REMOVEMETHOD_DEFAULT) < 1) // 99 means I'm a bot
+             {
+                 //no one left in group so deleting group
+                 delete m_group;
+                 sObjectMgr->RemoveGroup(m_group);
+             }
+         }
+         m_bot->SetCharmerGUID(0);
+         //m_bot->RemoveFromWorld();
+         RemoveBot();
+         SetBotMustBeCreated(m_bot_entry, newbotrace, newbotclass);
+     }
+
+
+
     // The player was ported to another map and loses the duel immediately.
     // We have to perform this check before the teleport, otherwise the
     // ObjectAccessor won't find the flag.
@@ -2057,6 +2166,20 @@ bool Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientati
         else
             return false;
     }
+
+    //if I'm dead than need to remove bot manually
+    //This means I'm at the graveyard, but the bot or rest of the group
+    //finished off the mob
+    if(HaveBot() && m_bot->isAlive() && !isAlive())
+    {
+        m_bot->SetCharmerGUID(0);
+        m_bot->RemoveFromWorld();
+        RemoveBot();
+
+        //recreate it when you are alive again
+        SetBotMustBeCreated(m_bot_entry, newbotrace, newbotclass);
+    } //end if bot is alive and I'm not
+
     return true;
 }
 
@@ -2171,6 +2294,1094 @@ void Player::RemoveFromWorld()
     }
 }
 
+Player *Player::GetObjPlayer(uint64 guid)
+{
+    return sObjectMgr->GetPlayer(guid);
+}
+
+void Player::GetBotLevelInfo(uint32 race, uint32 class_,uint32 level, PlayerLevelInfo* info) const {
+    sObjectMgr->GetPlayerLevelInfo (race, class_, level, info);
+}
+
+void Player::RefreshBot(uint32 diff)
+{
+    if(m_botTimer != 0)
+        return;
+    uint32 refreshDelay = 0;
+
+    if(m_bot_died == true && !this->isInCombat() && isAlive())
+    {
+        //recreate bot because it died
+        CreateBot(m_bot_entry, newbotrace, newbotclass);
+        m_bot = GetBot();
+        m_bot_died = false;
+    }
+
+    if (isInFlight())
+    {
+        if (HaveBot())
+        {
+            if (GetGroup())
+            {
+                Group* m_group = GetGroup();
+                Creature* m_bot = GetBot();
+
+                // removing bot from group
+                if (m_group->IsMember(m_bot->GetGUID()))
+                {
+                    if (m_group->RemoveMember(m_bot->GetGUID(), GROUP_REMOVEMETHOD_DEFAULT) <= 1)
+                    {
+                        // deleting group since no one is left
+                        delete m_group;
+                        sObjectMgr->RemoveGroup(m_group);
+                    }
+                }
+            }
+            m_bot->SetCharmerGUID(0);
+            m_bot->RemoveFromWorld();
+            RemoveBot();
+
+            SetBotMustBeCreated(m_bot_entry, newbotrace, newbotclass);
+        }
+        return;
+    }
+
+    if(HaveBot())
+    {
+        Creature *m_bot = GetBot();
+
+        //BOT IS DEAD SUPPORT
+        if(GetGroup() && !m_bot->isAlive())
+        {
+            Group *m_group = GetGroup();
+
+            //respawn if master is not in combat and is alive
+            if(!this->isInCombat() && isAlive())
+            {
+                CreateBot(m_bot_entry, newbotrace, newbotclass);
+                m_bot = GetBot();
+            } else {
+                m_bot_died = true;
+            }
+        }
+        //BOT MUST DIE SUPPORT
+        else if(GetBotMustDie())
+        {
+            if(m_bot->isAlive()) {  // dont want to delete from group if dead
+                if(GetGroup())
+                {
+                    Group *m_group = GetGroup();
+
+                    //removing bot from group
+                    if(m_group->IsMember(m_bot->GetGUID()) && m_group->GetMembersCount() >= 2)
+                    {
+                        //deleting bot from group
+                        if(m_group->RemoveMember(m_bot->GetGUID(), GROUP_REMOVEMETHOD_DEFAULT) < 1) // 99 means I'm a bot
+                        {
+                            //no one left in group so deleting group
+                            delete m_group;
+                            sObjectMgr->RemoveGroup(m_group);
+                        }
+                    }
+                }
+
+                m_bot->SetReactState(REACT_PASSIVE);
+                m_bot->CombatStop();
+
+                m_bot->DeleteThreatList();
+                m_bot->SetCharmerGUID(0);
+                m_bot->RemoveFromWorld();
+                RemoveBot();
+
+                CharacterDatabase.PExecute("DELETE FROM character_npcbot WHERE owner='%u'", this->GetGUIDLow());
+            }
+        }
+    }
+
+    if(HaveBot() && GetBot()->isAlive())
+    {
+
+/*------------------------------------------*/
+    Creature *m_bot = GetBot();
+/*------------------------------------------*/
+
+    if(isInCombat())
+    {
+        if(m_bot->getVictim() > 0)
+        {
+            if(m_bot->getVictim()->IsPolymorphed())
+            {
+                    //m_bot->Say("SHEEP!", LANG_UNIVERSAL, NULL);
+                    m_bot->SetReactState(REACT_PASSIVE); //Don't attack sheep
+                    m_bot->CombatStop();
+                    return; //for now return because can't do anything, need to continue timer though somehow
+            } //end if polymorpth
+        } //end if getVictim > 0
+        else if(getVictim() > 0 && !getVictim()->IsPolymorphed())
+        {
+            if(GetBotClass() == CLASS_PRIEST || GetBotClass() == CLASS_MAGE || GetBotClass() == CLASS_WARLOCK)
+                m_bot->SetReactState(REACT_PASSIVE); //casters shouldn't melee
+            else
+                m_bot->SetReactState(REACT_DEFENSIVE);
+        }
+
+        //if I'm in combat but the bot is not, put bot in combat
+        //this fixes the case where group member gets initial aggro
+        //otherwise the bot wont fight unless I get hit.
+        if(!m_bot->isInCombat() && m_bot->GetReactState() != REACT_PASSIVE)
+        {
+            if(getVictim() > 0 && m_bot->GetDistance(getVictim())<30) {
+                m_bot->AI()->AttackStart(getVictim());
+                SetBotCommandState(COMMAND_ATTACK);
+                //m_bot->AI()->BotAttackStart(getVictim());
+            }
+        } else {
+//sLog->outError ("putting priest to follow master");
+  //          m_bot->GetMotionMaster()->MoveFollow(this, urand(5, 10), PET_FOLLOW_ANGLE);
+        }
+    } //end if isInCombat
+
+
+    //TELEPORT AND CHANGE ZONE/AREA SUPPORT
+    if(!isInFlight())
+    {
+        bool tp = false;
+        if(GetMapId() != m_bot->GetMapId())
+            tp = true;
+        else if(GetZoneId() != m_bot->GetZoneId())
+            tp = true;
+        //If bot and player not in the same area but around 25
+        else if((GetAreaId() != m_bot->GetAreaId()) &&
+        ((abs(m_bot->GetPositionX() - GetPositionX()) > 25) ||
+        (abs(m_bot->GetPositionY() - GetPositionY()) > 25)) ||
+        (abs(m_bot->GetPositionZ() - GetPositionZ()) > 25))
+        {
+            tp = true;
+        }
+
+        //If player change of zone/area
+        if(tp)
+        {
+            //HACK ELSE BOT IS DUPLICATED AND CLIENT CRASH
+            if(GetGroup() && GetBot())
+            {
+                Group *m_group = GetGroup();
+                Creature *m_bot = GetBot();
+
+                //removing bot from group
+                if(m_group->IsMember(m_bot->GetGUID()))
+                {
+                    //deleting bot from group
+                    if(m_group->RemoveMember(m_bot->GetGUID(), GROUP_REMOVEMETHOD_DEFAULT) < 1) // 99 means I'm a bot
+                    {
+                        //no one left in group so deleting group
+                        delete m_group;
+                        sObjectMgr->RemoveGroup(m_group);
+                    }
+                }
+            }
+
+            //SAVE INFO
+            uint32 entry = m_bot->GetEntry();
+
+            //DESPAWN
+            m_bot->SetCharmerGUID(0);
+            m_bot->RemoveFromWorld();
+            RemoveBot();
+
+            //RESPAWN
+            CreateBot(entry, newbotrace, newbotclass);
+            m_bot = GetBot();
+            return;
+        }
+    }
+
+        //FLYING MOUNT SUPPORT
+        if((IsMounted() && HasAuraType(SPELL_AURA_FLY)) || canFly() || IsFlying() || isInFlight())
+        {
+            if(m_bot->GetMountID() != 17759
+            && m_bot->GetMountID() != 17703
+            && m_bot->GetMountID() != 17718
+            && m_bot->GetMountID() != 17720
+            && m_bot->GetMountID() != 17721
+            && m_bot->GetMountID() != 17719)
+            {
+                int m_mount = 0;
+                int m_rand = rand()%100;
+                if(m_rand < 33)      m_mount = 1;
+                else if(m_rand > 64) m_mount = 2;
+                else                 m_mount = 3;
+
+                if((GetBotClass() == CLASS_DRUID) && (m_bot->getLevel() >= 70)) m_mount = 0;
+                if((GetBotClass() == CLASS_DRUID) && (m_bot->getLevel() >= 70)) m_mount = 0;
+
+                if(GetBotRace() == RACE_HUMAN || GetBotRace() == RACE_DWARF || GetBotRace() == RACE_NIGHTELF || GetBotRace() == RACE_GNOME || GetBotRace() == RACE_DRAENEI)
+                {
+                    switch(m_mount)
+                    {
+                        case 1: m_bot->Mount(17759); break;
+                        case 2: m_bot->Mount(17703); break;
+                        case 3: m_bot->Mount(17718); break;
+                        default: break;
+                    }
+                }
+                else if(GetBotRace() == RACE_ORC || GetBotRace() == RACE_UNDEAD_PLAYER || GetBotRace() == RACE_TAUREN || GetBotRace() == RACE_TROLL || GetBotRace() == RACE_BLOODELF)
+                {
+                    switch(m_mount)
+                    {
+                        case 1: m_bot->Mount(17720); break;
+                        case 2: m_bot->Mount(17721); break;
+                        case 3: m_bot->Mount(17719); break;
+                        default: break;
+                    }
+                }
+                //m_bot->SetSpeed(MOVE_RUN, GetSpeed(MOVE_WALK) - 0.1f, true);
+            }
+            if(isInFlight())
+            {
+                m_bot->HasUnitMovementFlag(MOVEMENTFLAG_FLYING);
+                m_bot->SetSpeed(MOVE_RUN, GetSpeed(MOVE_FLIGHT) + 0.1f, true);
+            }
+            else
+            {
+                m_bot->HasUnitMovementFlag(MOVEMENTFLAG_NONE);
+                m_bot->SetSpeed(MOVE_RUN, GetSpeed(MOVE_RUN) + 0.1f, true);
+            }
+        }
+        //MOUNT SUPPORT
+        else if(IsMounted() && !m_bot->IsMounted())
+        {
+            int m_mount = 0;
+            int m_rand = rand()%100;
+            if(m_rand < 33)      m_mount = 1;
+            else if(m_rand > 64) m_mount = 2;
+            else                 m_mount = 3;
+
+            if((GetBotClass() == CLASS_DRUID) && (m_bot->getLevel() < 60)) m_mount = 0;
+            if((GetBotClass() == CLASS_DRUID) && (m_bot->getLevel() < 60)) m_mount = 0;
+
+            switch(GetBotRace())
+            {
+                case RACE_HUMAN:
+                {
+                    if(getLevel() < 60)
+                    {
+                        switch(m_mount)
+                        {
+                            case 1: m_bot->Mount(2409); break;
+                            case 2: m_bot->Mount(2404); break;
+                            case 3: m_bot->Mount(2405); break;
+                            default: break;
+                        }
+                    }
+                    else
+                    {
+                        switch(m_mount)
+                        {
+                            case 1: m_bot->Mount(14338); break;
+                            case 2: m_bot->Mount(14583); break;
+                            case 3: m_bot->Mount(14582); break;
+                            default: break;
+                        }
+                    }
+                    break;
+                }
+                case RACE_ORC:
+                {
+                    if(getLevel() < 60)
+                    {
+                        switch(m_mount)
+                        {
+                            case 1: m_bot->Mount(247); break;
+                            case 2: m_bot->Mount(2327); break;
+                            case 3: m_bot->Mount(2328); break;
+                            default: break;
+                        }
+                    }
+                    else
+                    {
+                        switch(m_mount)
+                        {
+                            case 1: m_bot->Mount(14575); break;
+                            case 2: m_bot->Mount(14574); break;
+                            case 3: m_bot->Mount(14573); break;
+                            default: break;
+                        }
+                    }
+                    break;
+                }
+                case RACE_DWARF:
+                {
+                    if(getLevel() < 60)
+                    {
+                        switch(m_mount)
+                        {
+                            case 1: m_bot->Mount(2785); break;
+                            case 2: m_bot->Mount(2786); break;
+                            case 3: m_bot->Mount(2736); break;
+                            default: break;
+                        }
+                    }
+                    else
+                    {
+                        switch(m_mount)
+                        {
+                            case 1: m_bot->Mount(14347); break;
+                            case 2: m_bot->Mount(14576); break;
+                            case 3: m_bot->Mount(14346); break;
+                            default: break;
+                        }
+                    }
+                    break;
+                }
+                case RACE_NIGHTELF:
+                {
+                    if(getLevel() < 60)
+                    {
+                        switch(m_mount)
+                        {
+                            case 1: m_bot->Mount(6080); break;
+                            case 2: m_bot->Mount(6448); break;
+                            case 3: m_bot->Mount(6444); break;
+                            default: break;
+                        }
+                    }
+                    else
+                    {
+                        switch(m_mount)
+                        {
+                            case 1: m_bot->Mount(14632); break;
+                            case 2: m_bot->Mount(14332); break;
+                            case 3: m_bot->Mount(14331); break;
+                            default: break;
+                        }
+                    }
+                    break;
+                }
+                case RACE_UNDEAD_PLAYER:
+                {
+                    if(getLevel() < 60)
+                    {
+                        switch(m_mount)
+                        {
+                            case 1: m_bot->Mount(10670); break;
+                            case 2: m_bot->Mount(10671); break;
+                            case 3: m_bot->Mount(10672); break;
+                            default: break;
+                        }
+                    }
+                    else
+                    {
+                        switch(m_mount)
+                        {
+                            case 1: m_bot->Mount(10721); break;
+                            case 2: m_bot->Mount(10720); break;
+                            case 3: m_bot->Mount(10719); break;
+                            default: break;
+                        }
+                    }
+                    break;
+                }
+                case RACE_TAUREN:
+                {
+                    if(getLevel() < 60)
+                    {
+                        switch(m_mount)
+                        {
+                            case 1: m_bot->Mount(12246); break;
+                            case 2: m_bot->Mount(11641); break;
+                            case 3: m_bot->Mount(12245); break;
+                            default: break;
+                        }
+                    }
+                    else
+                    {
+                        switch(m_mount)
+                        {
+                            case 1: m_bot->Mount(14579); break;
+                            case 2: m_bot->Mount(14349); break;
+                            case 3: m_bot->Mount(14578); break;
+                            default: break;
+                        }
+                    }
+                    break;
+                }
+                case RACE_GNOME:
+                {
+                    if(getLevel() < 60)
+                    {
+                        switch(m_mount)
+                        {
+                            case 1: m_bot->Mount(9473); break;
+                            case 2: m_bot->Mount(10661); break;
+                            case 3: m_bot->Mount(6569); break;
+                            default: break;
+                        }
+                    }
+                    else
+                    {
+                        switch(m_mount)
+                        {
+                            case 1: m_bot->Mount(14376); break;
+                            case 2: m_bot->Mount(14374); break;
+                            case 3: m_bot->Mount(14377); break;
+                            default: break;
+                        }
+                    }
+                    break;
+                }
+                case RACE_TROLL:
+                {
+                    if(getLevel() < 60)
+                    {
+                        switch(m_mount)
+                        {
+                            case 1: m_bot->Mount(6472); break;
+                            case 2: m_bot->Mount(4806); break;
+                            case 3: m_bot->Mount(6473); break;
+                            default: break;
+                        }
+                    }
+                    else
+                    {
+                        switch(m_mount)
+                        {
+                            case 1: m_bot->Mount(14344); break;
+                            case 2: m_bot->Mount(14339); break;
+                            case 3: m_bot->Mount(14342); break;
+                            default: break;
+                        }
+                    }
+                    break;
+                }
+                case RACE_BLOODELF:
+                {
+                    if(getLevel() < 60)
+                    {
+                        switch(m_mount)
+                        {
+                            case 1: m_bot->Mount(18696); break;
+                            case 2: m_bot->Mount(19480); break;
+                            case 3: m_bot->Mount(19478); break;
+                            default: break;
+                        }
+                    }
+                    else
+                    {
+                        switch(m_mount)
+                        {
+                            case 1: m_bot->Mount(19484); break;
+                            case 2: m_bot->Mount(18697); break;
+                            case 3: m_bot->Mount(19482); break;
+                            default: break;
+                        }
+                    }
+                    break;
+                }
+                case RACE_DRAENEI:
+                {
+                    if(getLevel() < 60)
+                    {
+                        switch(m_mount)
+                        {
+                            case 1: m_bot->Mount(17063); break;
+                            case 2: m_bot->Mount(19870); break;
+                            case 3: m_bot->Mount(19869); break;
+                            default: break;
+                        }
+                    }
+                    else
+                    {
+                        switch(m_mount)
+                        {
+                            case 1: m_bot->Mount(19871); break;
+                            case 2: m_bot->Mount(19872); break;
+                            case 3: m_bot->Mount(19873); break;
+                            default: break;
+                        }
+                    }
+                    break;
+                }
+            }
+            m_bot->SetSpeed(MOVE_RUN, GetSpeed(MOVE_WALK) - 0.1f, true);
+        }
+        else if(!IsMounted()
+            && m_bot->IsMounted())
+        {
+            m_bot->Unmount();
+            CreatureInfo const *cinfo = sObjectMgr->GetCreatureTemplate(m_bot->GetEntry());
+            m_bot->SetSpeed(MOVE_RUN, cinfo->speed_run, true);
+            m_bot->HasUnitMovementFlag(MOVEMENTFLAG_NONE);
+        }
+
+        //if low on mana, take a drink (only check for classes with custom AI)
+        //because they are the only ones currently using mana
+        if(GetBotClass() == CLASS_SHAMAN || GetBotClass() == CLASS_DRUID ||
+        GetBotClass() == CLASS_PRIEST || GetBotClass() == CLASS_MAGE || GetBotClass() == CLASS_HUNTER ||
+        GetBotClass() == CLASS_WARLOCK || GetBotClass() == CLASS_PALADIN)
+        {
+            if(m_bot->GetPower(POWER_MANA)*100/m_bot->GetMaxPower(POWER_MANA) < 80 &&
+              !m_bot->HasAura(1137) &&
+              GetBotMustWaitForSpell3() <= 0 &&
+              !m_bot->isInCombat())
+            {
+                m_bot->CastSpell(m_bot, 1137, true);
+                SetBotMustWaitForSpell3(1000);
+                m_bot->SetStandState(1);
+                m_botTimer = 5000; //set longer delay so it wont stand up right away
+                return;
+            }
+        }
+
+        //if drinking, have to fake mana regen because charmed NPCs
+        //do not regen mana
+        if(m_bot->HasAura(1137))
+        {
+            uint32 addvalue = 0;
+            uint32 maxValue = m_bot->GetMaxPower(POWER_MANA);
+            uint32 curValue = m_bot->GetPower(POWER_MANA);
+
+            if(curValue <= maxValue)
+            {
+                addvalue = maxValue/20;
+                m_bot->ModifyPower(POWER_MANA, addvalue);
+                //return;
+            }
+        }
+
+        if(m_bot->HasAura(1137) && m_bot->GetPower(POWER_MANA) >= m_bot->GetMaxPower(POWER_MANA))
+            m_bot->RemoveAurasDueToSpell(1137);
+
+        //eat
+        if(m_bot->GetHealth()*100 / m_bot->GetMaxHealth() < 80 &&
+           !m_bot->HasAura(10256) &&
+           GetBotMustWaitForSpell3() <= 0 &&
+           !m_bot->isInCombat())
+        {
+            SetBotMustWaitForSpell3(1000);
+            m_bot->CastSpell(m_bot, 10256, true);
+            m_bot->SetStandState(1);
+            m_botTimer = 5000; //set longer delay so it wont stand up right away
+            return;
+        }
+
+        //if eating, have to fake regen because charmed NPCs
+        //do not regen
+        if(m_bot->HasAura(10256))
+        {
+            uint32 addvalue = 0;
+            uint32 maxValue = m_bot->GetMaxHealth();
+            uint32 curValue = m_bot->GetHealth();
+
+            if(curValue <= maxValue)
+            {
+                addvalue = maxValue/20;
+                m_bot->SetHealth(curValue + addvalue);
+                //return;
+            }
+        }
+
+        if(m_bot->GetHealth() == m_bot->GetMaxHealth() && m_bot->HasAura(10256))
+            m_bot->RemoveAurasDueToSpell(10256);
+
+        //if bot stands up for some reason, ie goes into combat,
+        //remove the food and drink affect
+        if(m_bot->isInCombat() /*|| !m_bot->IsStopped()*/ || m_bot->IsStandState())
+        {
+            if(m_bot->HasAura(10256)) m_bot->RemoveAurasDueToSpell(10256);
+            if(m_bot->HasAura(1137)) m_bot->RemoveAurasDueToSpell(1137);
+        }
+
+        //if done drinking and eating, stand up
+        if(!isInCombat() && m_bot->IsSitState() && !m_bot->HasAura(1137) && !m_bot->HasAura(10256))
+            m_bot->SetStandState(UNIT_STAND_STATE_STAND);
+
+        //SPELL AI CUSTOM SUPPORT
+        switch(GetBotClass())
+        {
+            case CLASS_DRUID: //DRUID FORM SUPPORT
+            {
+                if(!m_bot->isAlive()) break;
+
+                CreatureInfo const *cinfo = sObjectMgr->GetCreatureTemplate(m_bot->GetEntry());
+
+                uint32 m_old_bot_form = m_bot->GetDisplayId();
+                if((IsMounted() && HasAuraType(SPELL_AURA_FLY)) || canFly() || IsFlying() || isInFlight())
+                {
+                    //flight form
+					if((((IsMounted()) && (m_bot->getLevel() >= 70)) || (m_bot->GetShapeshiftForm() == FORM_FLIGHT) || (m_bot->GetShapeshiftForm() == FORM_FLIGHT_EPIC)))
+                    {
+                        if((GetBotRace() == RACE_NIGHTELF) && (m_bot->GetDisplayId() != 21243))
+                            m_bot->SetDisplayId(21243);
+                        if((GetBotRace() == RACE_TAUREN) && (m_bot->GetDisplayId() != 21244))
+                            m_bot->SetDisplayId(21244);
+                        m_bot->Unmount();
+                        SetBotForm(m_bot->GetDisplayId());
+                        SetBotMustWaitForSpell1(3000);
+                    }
+                }
+                else if((GetBotMustWaitForSpell1() == 0) && (m_bot->IsInWater()) && (!isInFlight()))
+                {
+                    //Removed this because it turns into a seal in Booty Bay
+                    //seal form
+/*
+                    if((GetBotRace() == RACE_NIGHTELF) && (m_bot->GetDisplayId() != 2428))
+                        m_bot->SetDisplayId(2428);
+                    if((GetBotRace() == RACE_TAUREN) && (m_bot->GetDisplayId() != 2428))
+                        m_bot->SetDisplayId(2428);
+                    SetBotForm(m_bot->GetDisplayId());
+                    SetBotMustWaitForSpell1(3000);
+*/
+                }
+                else if((GetBotMustWaitForSpell1() == 0) && (m_bot->isInCombat()) && (!m_bot->isInFlight()))
+                {
+                    //combat form is now handled in AI
+                }
+                else if((GetBotMustWaitForSpell1() == 0) && (((IsMounted()) && (m_bot->getLevel() < 60)) || (m_bot->GetShapeshiftForm() == FORM_TRAVEL)) && (!m_bot->isInFlight()))
+                {
+                    //travel form
+                    if((GetBotRace() == RACE_NIGHTELF) && (m_bot->GetDisplayId() != 632))
+                        m_bot->SetDisplayId(632);
+                    if((GetBotRace() == RACE_TAUREN) && (m_bot->GetDisplayId() != 632))
+                        m_bot->SetDisplayId(632);
+                    SetBotForm(m_bot->GetDisplayId());
+                    SetBotMustWaitForSpell1(3000);
+                }
+                else if((GetBotMustWaitForSpell1() == 0) && (m_bot->GetShapeshiftForm() == FORM_CAT) && (!m_bot->isInFlight()))
+                {
+                    //cat form
+                    if((GetBotRace() == RACE_NIGHTELF) && (m_bot->GetDisplayId() != 892))
+                        m_bot->SetDisplayId(892);
+                    if((GetBotRace() == RACE_TAUREN) && (m_bot->GetDisplayId() != 8571))
+                        m_bot->SetDisplayId(8571);
+                    SetBotForm(m_bot->GetDisplayId());
+                    //SetBotMustWaitForSpell1(3000);
+                }
+                else if((GetBotMustWaitForSpell1() == 0) && (m_bot->GetShapeshiftForm() == FORM_BEAR) && (!m_bot->isInFlight()))
+                {
+                    //bear form
+                    if((GetBotRace() == RACE_NIGHTELF) && (m_bot->GetDisplayId() != 2281)) m_bot->SetDisplayId(2281);
+                    if((GetBotRace() == RACE_TAUREN) && (m_bot->GetDisplayId() != 2289)) m_bot->SetDisplayId(2289);
+                    SetBotForm(m_bot->GetDisplayId());
+                    //SetBotMustWaitForSpell1(3000);
+                }
+
+                if(m_old_bot_form != GetBotForm())
+                {
+                    //change stats based on forms
+                    m_bot->SetSpeed(MOVE_SWIM, cinfo->speed_run, true); m_bot->SetSpeed(MOVE_RUN, cinfo->speed_run, true);
+                    if(GetBotForm() == 2428)
+                        m_bot->SetSpeed(MOVE_SWIM, cinfo->speed_run * 1.50f, true);
+                    else if(GetBotForm() == 632) //travel form
+                    {
+                        //m_bot->SetSpeed(MOVE_RUN, GetSpeed(MOVE_WALK) , true);
+                        m_bot->SetSpeed(MOVE_RUN, GetSpeed(MOVE_RUN) - 0.1f, true);
+                    }
+
+                }
+
+                //RESET FORMS
+                if((!m_bot->isInFlight()) && (!m_bot->IsInWater())  &&  (!m_bot->isInCombat())  && (!IsMounted()) && (m_bot->GetShapeshiftForm() != FORM_TRAVEL) && (m_bot->GetShapeshiftForm() != FORM_CAT))
+                {
+                    //don't reset if bear or cat because it costs too much mana
+                    if(m_bot->HasAura(9634) ||
+                        m_bot->HasAura(768) ||
+                        m_bot->HasAura(16591))
+                        return;
+
+                    if((GetBotRace() == 4) && (m_bot->GetDisplayId() != cinfo->Modelid1))
+                        m_bot->SetDisplayId(cinfo->Modelid1);
+                    if((GetBotRace() == 6) && (m_bot->GetDisplayId() != cinfo->Modelid3))
+                        m_bot->SetDisplayId(cinfo->Modelid3);
+                    m_bot->SetSpeed(MOVE_SWIM, cinfo->speed_run, true);
+                    m_bot->SetSpeed(MOVE_RUN, cinfo->speed_run, true);
+                    SetBotForm(m_bot->GetDisplayId());
+                }
+
+                //SPECIAL SPELL FOR DRUID
+                SetBotForm(m_bot->GetDisplayId());
+                break;
+            }
+
+        }//END SWITCH
+
+        if (!m_bot->isInCombat() &&
+            m_bot->GetCharmInfo()->GetCommandState() != COMMAND_STAY &&
+            GetDistance(m_bot) > 60 &&
+            !IsBeingTeleported())
+        {
+            m_bot->Relocate(this);
+            SetBotCommandState(COMMAND_FOLLOW);
+        }
+
+        if (getStandState() == UNIT_STAND_STATE_SIT)
+        {
+            m_bot->SetStandState(UNIT_STAND_STATE_SIT);
+        }
+        else
+        {
+            m_bot->SetStandState(UNIT_STAND_STATE_STAND);
+        }
+    }
+    else if(GetBotMustBeCreated() && isAlive())
+    {
+        CreateBot(m_bot_entry_must_be_created, m_bot_race_must_be_created, m_bot_class_must_be_created);
+    }
+
+    if(GetBotMustWaitForSpell1() > 0)
+        SetBotMustWaitForSpell1(GetBotMustWaitForSpell1() - refreshDelay > 0 ? refreshDelay : 10);
+
+    if(GetBotMustWaitForSpell2() > 0)
+        SetBotMustWaitForSpell2(GetBotMustWaitForSpell2() - refreshDelay > 0 ? refreshDelay : 10);
+
+    if(GetBotMustWaitForSpell3() > 0)
+        SetBotMustWaitForSpell3(GetBotMustWaitForSpell3() - refreshDelay > 0 ? refreshDelay : 10);
+
+    m_botTimer = refreshDelay;
+
+} //end Player::RefreshBot
+
+void Player::RemoveBot()
+{
+    if(m_botHasPet && m_botsPet != NULL)
+    {
+        m_botsPet->SetCharmerGUID(0);
+        m_botsPet->CombatStop();
+        m_botsPet->DeleteFromDB();
+        m_botsPet->CleanupsBeforeDelete();
+        m_botsPet->AddObjectToRemoveList();
+    }
+    m_botsPet = NULL;
+    m_botHasPet = false;
+
+    m_bot->CombatStop();
+    m_bot->DeleteFromDB();
+    m_bot->CleanupsBeforeDelete();
+    m_bot->SetIAmABot(false); //this HAS to come after CleanupsBeforeDelete
+    m_bot->AddObjectToRemoveList();
+
+    m_bot = NULL; m_bot_class = 0; m_bot_race = 0; m_bot_form = 0;
+    m_bot_must_wait_for_spell_1 = 0; m_bot_must_wait_for_spell_2 = 0;
+    m_bot_must_wait_for_spell_3 = 0; m_bot_must_be_created = false;
+
+    if(m_bot_ai)
+    {
+        //delete m_bot_ai;
+        m_bot_ai = NULL;
+    }
+    m_bot_must_die = false;
+
+
+} //end RemoveBot
+
+
+void Player::CreateBot(uint32 botentry, uint8 botrace, uint8 botclass)
+{
+    if(IsBeingTeleported()) return; //being teleported so don't create bot yet
+
+    if (this->isInCombat() || this->isDead()) return; // don't create while fighting or dead
+
+    if (m_bot != NULL) {
+        m_bot->SetHealth(m_bot->GetMaxHealth());
+        m_bot->setDeathState(ALIVE);
+        return;
+    }
+
+    Creature *newbot = SummonCreature(botentry, GetPositionX()-2, GetPositionY()-2, GetPositionZ(), GetOrientation(), TEMPSUMMON_MANUAL_DESPAWN, 0);
+
+    SetBot(newbot);
+    SetBotClass(botclass);
+    SetBotRace(botrace);
+
+    newbot->SetIAmABot(true);
+    newbot->SetCharmerGUID(GetGUID());
+    newbot->CombatStop();
+    newbot->DeleteThreatList();
+    newbot->AIM_Initialize();
+
+    CharmInfo *charmInfonewbot = newbot->InitCharmInfo();
+    newbot->setFaction(getFaction());
+    if(botclass == CLASS_PRIEST || botclass == CLASS_MAGE || botclass == CLASS_WARLOCK)
+        newbot->SetReactState(REACT_PASSIVE); //casters shouldn't melee
+    else
+        newbot->SetReactState(REACT_DEFENSIVE);
+    //charmInfonewbot->SetCommandState(COMMAND_FOLLOW);
+    SetBotCommandState(COMMAND_FOLLOW);;
+
+    //sLog->outError("Player::CreateBot: %s", newbot->GetName());
+    //sLog->outError("\thp = %d", newbot->GetMaxHealth());
+    //sLog->outError("\tmana = %d", newbot->GetMaxPower(POWER_MANA));
+    //sLog->outError("\tlevel = %d", newbot->getLevel());
+    //sLog->outError("\tentry = %d", botentry);
+    //sLog->outError("\trace = %d", botrace);
+    //sLog->outError("\tclass = %d", botclass);
+
+    //If the player have a group, it's possible to add the bot.
+    if(GetGroup())
+    {
+        Group *m_group = GetGroup();
+        if(!m_group->IsFull()) m_group->AddMember(newbot->GetGUID(), newbot->GetName());
+        else {
+            //group is full so can't add bot
+            //(m_bot->AI())->MonsterSay("Group is full.", LANG_UNIVERSAL, NULL);
+            SetBotMustDie();
+        }
+    }
+    else
+    {
+        Group *m_group = new Group;
+        if(!m_group->Create(GetGUID(), GetName()))
+        {
+            delete m_group;
+            return;
+        }
+        sObjectMgr->AddGroup(m_group);
+
+        if(!m_group->IsFull()) m_group->AddMember(newbot->GetGUID(), newbot->GetName());
+    }
+    m_bot_must_die = false;
+
+    //if not in group, go away
+    Group::MemberSlotList const &a =GetGroup()->GetMemberSlots();
+    if(GetGroup() == NULL || a.empty())
+    {
+        //m_creature->MonsterSay("Not in group.", LANG_UNIVERSAL, NULL);
+        SetBotMustDie();
+        return;
+    }
+
+    m_bot_entry = m_bot->GetEntry();
+    newbotclass = GetBotClass();
+    newbotrace = GetBotRace();
+
+    //Set the race. This is so when the druid shapeshifts
+    //it wont get an error about not finding correct race
+    GetBot()->SetByteValue(UNIT_FIELD_BYTES_0, 0, newbotrace);
+
+    if(GetBotAI()) GetBotAI()->JustRespawned();
+
+    SetBotMustWaitForSpell1(0);
+    SetBotMustWaitForSpell2(0);
+    SetBotMustWaitForSpell3(0); //eating and drinking
+
+   // m_bot->SelectLevel(sObjectMgr->GetCreatureTemplate(m_bot->GetEntry()));
+   // m_bot->SelectLevel(sObjectMgr->GetCreatureTemplate(m_bot->GetEntry()));
+    CreatureBaseStats const* stats = sObjectMgr->GetCreatureBaseStats(getLevel(), newbot->GetCreatureInfo()->unit_class);
+    newbot->SetArmor(stats->BaseArmor);
+    newbot->SetMaxHealth((getLevel() * newbot->GetMaxHealth()) / newbot->getLevel()); //set health based of level
+    newbot->SetMaxHealth(newbot->GetMaxHealth() - newbot->GetMaxHealth() * urand(1, 25) / 100); // insert some randomness
+    newbot->SetHealth(newbot->GetMaxHealth());
+
+    if(botclass != CLASS_WARRIOR && botclass != CLASS_ROGUE && botclass != CLASS_DEATH_KNIGHT)
+    {
+        newbot->SetPower(POWER_MANA, m_bot->GetMaxPower(POWER_MANA));
+        newbot->SetMaxPower(POWER_MANA, (getLevel() * m_bot->GetMaxPower(POWER_MANA)) / m_bot->getLevel()); //set mana based of level
+        newbot->SetMaxPower(POWER_MANA, m_bot->GetMaxPower(POWER_MANA) - newbot->GetMaxPower(POWER_MANA) * urand(1, 20) / 100); // insert some randomness
+    }
+
+    //newbot->SetFaction(getFaction());
+    newbot->setFaction(getFaction());
+
+    //remove any entries, just in case it didnt get cleaned up earlier
+	CharacterDatabase.PExecute("DELETE FROM character_npcbot WHERE owner = '%u'", this->GetGUIDLow());
+
+    //add the new bot
+    CharacterDatabase.PExecute("INSERT INTO character_npcbot (owner,entry,race,class) VALUES ('%u','%u','%u','%u')", this->GetGUIDLow(), botentry, botrace, botclass);
+
+    m_botsPet = NULL;
+    m_botHasPet = false;
+
+    newbot->SetLevel(getLevel());
+    newbot->AI()->Reset();
+
+    m_SaveOrgLocation = sConfig->GetIntDefault("Bot.SaveOrgLocation", 0);
+
+} //end Player::CreateBot
+
+Creature *Player::GetBotsPet(uint32 entry)
+{
+    Creature *pet = this->SummonCreature(entry, GetPositionX() + 10, GetPositionY() + 10, GetPositionZ(), 0, TEMPSUMMON_DEAD_DESPAWN, 0);
+
+    QueryResult result;
+
+    result = WorldDatabase.PQuery("SELECT hp, mana, armor, str, agi FROM pet_levelstats WHERE creature_entry = 1860 AND level=%u", this->getLevel());
+
+    if(result)
+    {
+        Field *fields = result->Fetch();
+        uint32 hp = fields[0].GetUInt32();
+        uint32 mana = fields[1].GetUInt32();
+        uint32 armor = fields[2].GetUInt32();
+        uint32 str = fields[3].GetUInt32();
+        uint32 agi = fields[4].GetUInt32();
+        //sLog->outError("hp = %u", hp);
+        //sLog->outError("mana = %u", mana);
+        //sLog->outError("str = %u", str);
+        //sLog->outError("agi = %u", agi);
+
+        pet->SetMaxHealth(hp);
+        pet->SetMaxPower(POWER_MANA, mana);
+        pet->SetArmor(armor);
+        pet->SetStat(STAT_STRENGTH, str);
+        pet->SetStat(STAT_AGILITY, agi);
+
+        //delete result;
+    }
+    pet->SetLevel(getLevel());
+
+    m_botHasPet = true;
+    m_botsPet = pet;
+
+    return pet;
+} //end GetBotsPet
+
+void Player::SetBotsPetDied()
+{
+    if(m_botHasPet && m_botsPet != NULL)
+    {
+        m_botsPet->SetCharmerGUID(0);
+        m_botsPet->CombatStop();
+        m_botsPet->DeleteFromDB();
+        m_botsPet->CleanupsBeforeDelete();
+        m_botsPet->AddObjectToRemoveList();
+    }
+
+    m_botsPet = NULL;
+    m_botHasPet = false;
+}
+
+//
+//This is called from script_bot_giver.cpp
+//
+uint8 Player::GetMaxPlayerBot()
+{
+    //load config variables
+    if(m_MaxPlayerbots > 9) m_MaxPlayerbots = sConfig->GetIntDefault("Bot.MaxPlayerbots", 9);
+
+    return m_MaxPlayerbots;
+
+}
+
+//
+//This is called from script_bot_giver.cpp
+//
+void Player::CreatePlayerBot(std::string name)
+{
+    uint64 guid = sObjectMgr->GetPlayerGUIDByName(name.c_str());
+    if(m_session->GetPlayerBot(guid) != NULL) return;
+    m_session->AddPlayerBot(guid);
+}
+
+//
+//This is called from script_bot_giver.cpp
+//
+std::list<std::string> *Player::GetCharacterList()
+{
+    std::string plName;
+    QueryResult results;
+
+    results = CharacterDatabase.PQuery("SELECT name FROM characters WHERE account='%u' AND online=0", m_session->GetAccountId());
+
+    if(!results) return NULL;
+
+    plName = (*results)[0].GetString();
+
+    std::list<std::string> *names = new std::list<std::string>;
+    do {
+        Field *fields = results->Fetch();
+        plName = fields[0].GetString();
+        if(plName.compare(GetName()) == 0) continue;
+        names->insert(names->end(), fields[0].GetString());
+    } while(results->NextRow());
+    return names;
+} //end GetCharacterList
+
+//Playerbot mod:
+void Player::SetPlayerbotAI(PlayerbotAI *ai)
+{
+    if(ai == NULL)
+    {
+        sLog->outError("Tried to assign playerbot AI to NULL; this is not supported!");
+        return;
+    }
+    if(GetPlayerbotAI() != NULL)
+    {
+        sLog->outError("Tried to reassign playerbot AI; this is not yet supported!");
+        return;
+    }
+    //assigning bot AI to normal players is not currently supported
+    if(!IsPlayerbot())
+    {
+        sLog->outError("Tried to set playerbot AI for a player that was not a bot.");
+        return;
+    }
+    m_playerbotAI = ai;
+
+    m_SaveOrgLocation = sConfig->GetIntDefault("Bot.SaveOrgLocation", 0);
+}
+
+
+
+//This is called from script_bot_giver.cpp
+//
+void Player::CreateNPCBot(uint8 bot_class)
+{
+    uint32 entry = 0;
+    uint32 bot_race = 0;
+
+    if(!this->HaveBot())
+    {
+        QueryResult result;
+
+        if(this->GetTeam() == ALLIANCE)
+        {
+            if(bot_class == CLASS_ROGUE) result = WorldDatabase.PQuery("SELECT entry,trainer_race FROM creature_template WHERE scriptname='rogue_bot' and trainer_class=%u and trainer_race IN(1,3,4,7,11)", bot_class);
+            else if(bot_class == CLASS_PRIEST) result = WorldDatabase.PQuery("SELECT entry,trainer_race FROM creature_template WHERE scriptname='priest_bot' and trainer_class=%u and trainer_race IN(1,3,4,7,11)", bot_class);
+            else if(bot_class == CLASS_DRUID) result = WorldDatabase.PQuery("SELECT entry,trainer_race FROM creature_template WHERE scriptname='druid_bot' and trainer_class=%u and trainer_race IN(1,3,4,7,11)", bot_class);
+            else if(bot_class == CLASS_SHAMAN) result = WorldDatabase.PQuery("SELECT entry,trainer_race FROM creature_template WHERE scriptname='shaman_bot' and trainer_class=%u and trainer_race IN(1,3,4,7,11)", bot_class);
+            else if(bot_class == CLASS_MAGE) result = WorldDatabase.PQuery("SELECT entry,trainer_race FROM creature_template WHERE scriptname='mage_bot' and trainer_class=%u and trainer_race IN(1,3,4,7,11)", bot_class);
+            else if(bot_class == CLASS_WARLOCK) result = WorldDatabase.PQuery("SELECT entry,trainer_race FROM creature_template WHERE scriptname='warlock_bot' and trainer_class=%u and trainer_race IN(1,3,4,7,11)", bot_class);
+            else if(bot_class == CLASS_WARRIOR) result = WorldDatabase.PQuery("SELECT entry,trainer_race FROM creature_template WHERE scriptname='warrior_bot' and trainer_class=%u and trainer_race IN(1,3,4,7,11)", bot_class);
+            else if(bot_class == CLASS_PALADIN) result = WorldDatabase.PQuery("SELECT entry,trainer_race FROM creature_template WHERE scriptname='paladin_bot' and trainer_class=%u and trainer_race IN(1,3,4,7,11)", bot_class);
+            else if(bot_class == CLASS_HUNTER) result = WorldDatabase.PQuery("SELECT entry,trainer_race FROM creature_template WHERE scriptname='hunter_bot' and trainer_class=%u and trainer_race IN(1,3,4,7,11)", bot_class);
+            else result = WorldDatabase.PQuery("SELECT entry,trainer_race FROM creature_template WHERE scriptname='script_bot' and trainer_class=%u and trainer_race IN(1,3,4,7,11)", bot_class);
+        }
+        else if(this->GetTeam() == HORDE)
+        {
+            if(bot_class == CLASS_ROGUE) result = WorldDatabase.PQuery("SELECT entry,trainer_race FROM creature_template WHERE scriptname='rogue_bot' and trainer_class=%u and trainer_race IN(2,5,6,8,10)", bot_class);
+            else if(bot_class == CLASS_PRIEST) result = WorldDatabase.PQuery("SELECT entry,trainer_race FROM creature_template WHERE scriptname='priest_bot' and trainer_class=%u and trainer_race IN(2,5,6,8,10)", bot_class);
+            else if(bot_class == CLASS_SHAMAN) result = WorldDatabase.PQuery("SELECT entry,trainer_race FROM creature_template WHERE scriptname='shaman_bot' and trainer_class=%u and trainer_race IN(2,5,6,8,10)", bot_class);
+            else if(bot_class == CLASS_MAGE) result = WorldDatabase.PQuery("SELECT entry,trainer_race FROM creature_template WHERE scriptname='mage_bot' and trainer_class=%u and trainer_race IN(2,5,6,8,10)", bot_class);
+            else if(bot_class == CLASS_WARLOCK) result = WorldDatabase.PQuery("SELECT entry,trainer_race FROM creature_template WHERE scriptname='warlock_bot' and trainer_class=%u and trainer_race IN(2,5,6,8,10)", bot_class);
+            else if(bot_class == CLASS_WARRIOR) result = WorldDatabase.PQuery("SELECT entry,trainer_race FROM creature_template WHERE scriptname='warrior_bot' and trainer_class=%u and trainer_race IN(2,5,6,8,10)", bot_class);
+            else if(bot_class == CLASS_DRUID) result = WorldDatabase.PQuery("SELECT entry,trainer_race FROM creature_template WHERE scriptname='druid_bot' and trainer_class=%u and trainer_race IN(2,5,6,8,10)", bot_class);
+            else if(bot_class == CLASS_PALADIN) result = WorldDatabase.PQuery("SELECT entry,trainer_race FROM creature_template WHERE scriptname='paladin_bot' and trainer_class=%u and trainer_race IN(2,5,6,8,10)", bot_class);
+            else if(bot_class == CLASS_HUNTER) result = WorldDatabase.PQuery("SELECT entry,trainer_race FROM creature_template WHERE scriptname='hunter_bot' and trainer_class=%u and trainer_race IN(2,5,6,8,10)", bot_class);
+            else result = WorldDatabase.PQuery("SELECT entry,trainer_race FROM creature_template WHERE scriptname='script_bot' and trainer_class=%u and trainer_race IN(2,5,6,8,10)", bot_class);
+        }
+
+        //randomly select one of NPCs
+        if(result)
+        {
+            uint64 m_rand = urand(1, result->GetRowCount());
+            uint64 tmp_rand = 0;
+            do
+            {
+                Field *fields = result->Fetch();
+                entry = fields[0].GetUInt32();
+                bot_race = fields[1].GetUInt32();
+                ++tmp_rand;
+                if(tmp_rand == m_rand)
+                    break;
+            } while(result->NextRow());
+           // delete result;
+        }
+    }
+
+    this->SetBotMustBeCreated(entry, bot_race, bot_class);
+} //end CreateNPCBot
+
+
 void Player::RegenerateAll()
 {
     //if (m_regenTimer <= 500)
@@ -2446,7 +3657,7 @@ Player::GetNPCIfCanInteractWith(uint64 guid, uint32 npcflagmask)
                     return NULL;
 
     // not too far
-    if (!unit->IsWithinDistInMap(this,INTERACTION_DISTANCE))
+    if (!unit->IsWithinDistInMap(this,INTERACTION_DISTANCE) && !IsPlayerbot())
         return NULL;
 
     return unit;
@@ -2634,6 +3845,31 @@ void Player::RemoveFromGroup(Group* group, uint64 guid, RemoveMethod method /* =
 {
     if (group)
     {
+		Player *_player = sObjectMgr->GetPlayer(guid);
+
+        if(_player!=NULL) {
+            WorldSession *session= _player->GetSession();
+            // Playerbot mod: if you remove yourself from a group, log out all playerbots
+
+            //save the map of playerbots first because if the map gets altered when
+            //a playerbot logs out which will corrupt the for loop
+            PlayerBotMap m_playerBots;
+            for(PlayerBotMap::const_iterator itr = session->GetPlayerBotsBegin(); itr != session->GetPlayerBotsEnd(); ++itr)
+            {
+                Player *bot = itr->second;
+                (m_playerBots)[itr->first] = bot;
+            }
+            for(PlayerBotMap::const_iterator itr2 = m_playerBots.begin(); itr2 != m_playerBots.end(); ++itr2)
+           {
+                Player *botPlayer = itr2->second;
+                if (!botPlayer) continue;
+
+                session->LogoutPlayerBot(botPlayer->GetGUID(),true);
+            }
+
+			if (!sObjectMgr->GetPlayer(guid)->GetGroup() || group->GetMembersCount()==0) return;
+        }
+
         if (group->RemoveMember(guid, method, kicker, reason) <= 1)
         {
             // group->Disband(); already disbanded in RemoveMember
@@ -6864,6 +8100,11 @@ bool Player::RewardHonor(Unit *uVictim, uint32 groupsize, int32 honor, bool pvpt
         {
             Player *pVictim = uVictim->ToPlayer();
 
+
+            if(pVictim->IsPlayerbot() && (!sWorld->getBoolConfig(CONFIG_HONOR_FROM_PLAYERBOTS) ||
+                pVictim->GetPlayerbotAI()->GetClassAI()->GetMaster() == this)) //Killing your own playerbots is not honorable!
+                return false;
+
             if (GetTeam() == pVictim->GetTeam() && !sWorld->IsFFAPvPRealm())
                 return false;
 
@@ -13264,7 +14505,6 @@ void Player::ApplyEnchantment(Item *item, EnchantmentSlot slot, bool apply, bool
             uint32 enchant_display_type = pEnchant->type[s];
             uint32 enchant_amount = pEnchant->amount[s];
             uint32 enchant_spell_id = pEnchant->spellid[s];
-
             switch(enchant_display_type)
             {
                 case ITEM_ENCHANTMENT_TYPE_NONE:
@@ -13888,7 +15128,6 @@ void Player::OnGossipSelect(WorldObject* pSource, uint32 gossipListId, uint32 me
             return;
         }
     }
-
     GossipMenuItemData pMenuData = gossipmenu.GetItemData(gossipListId);
 
     switch(gossipOptionId)
@@ -15519,6 +16758,9 @@ void Player::KilledMonsterCredit(uint32 entry, uint64 guid)
             }
         }
     }
+
+    //Playerbot mod
+    if(m_playerbotAI != NULL) m_playerbotAI->KilledMonster(entry, guid);
 }
 
 void Player::CastedCreatureOrGO(uint32 entry, uint64 guid, uint32 spell_id)
@@ -15644,6 +16886,7 @@ void Player::TalkedToCreature(uint32 entry, uint64 guid)
                             m_QuestStatusSave[questid] = true;
 
                             SendQuestUpdateAddCreatureOrGo(qInfo, guid, j, curTalkCount, addTalkCount);
+							if (IsPlayerbot()) this->GetPlayerbotAI()->TellMaster("Talked to quest guy.");
                         }
                         if (CanCompleteQuest(questid))
                             CompleteQuest(questid);
@@ -17971,6 +19214,18 @@ void Player::SaveToDB()
 
     if (!IsBeingTeleported())
     {
+        if (IsPlayerbot() && m_SaveOrgLocation == 1)
+        {
+            ss << m_playerbotAI->GetStartMapID() << ", "
+            << (uint32)m_playerbotAI->GetStartInstanceID() << ", "
+            << (uint32)m_playerbotAI->GetStartDifficulty() << ", "
+            << finiteAlways(m_playerbotAI->GetStartX()) << ", "
+            << finiteAlways(m_playerbotAI->GetStartY()) << ", "
+            << finiteAlways(m_playerbotAI->GetStartZ()) << ", "
+            << finiteAlways(m_playerbotAI->GetStartO()) << ", ";
+        }
+        else
+        {
         ss << GetMapId() << ", "
         << (uint32)GetInstanceId() << ", "
         << uint32(uint8(GetDungeonDifficulty()) | uint8(GetRaidDifficulty()) << 4) << ", "
@@ -17978,9 +19233,22 @@ void Player::SaveToDB()
         << finiteAlways(GetPositionY()) << ", "
         << finiteAlways(GetPositionZ()) << ", "
         << finiteAlways(GetOrientation()) << ", ";
+        }
     }
     else
     {
+        if (IsPlayerbot() && m_SaveOrgLocation == 1)
+        {
+            ss << m_playerbotAI->GetStartMapID() << ", "
+            << (uint32)m_playerbotAI->GetStartInstanceID() << ", "
+            << (uint32)m_playerbotAI->GetStartDifficulty() << ", "
+            << finiteAlways(m_playerbotAI->GetStartX()) << ", "
+            << finiteAlways(m_playerbotAI->GetStartY()) << ", "
+            << finiteAlways(m_playerbotAI->GetStartZ()) << ", "
+            << finiteAlways(m_playerbotAI->GetStartO()) << ", ";
+        }
+        else
+        {
         ss << GetTeleportDest().GetMapId() << ", "
         << (uint32)0 << ", "
         << uint32(uint8(GetDungeonDifficulty()) | uint8(GetRaidDifficulty()) << 4) << ", "
@@ -17988,6 +19256,7 @@ void Player::SaveToDB()
         << finiteAlways(GetTeleportDest().GetPositionY()) << ", "
         << finiteAlways(GetTeleportDest().GetPositionZ()) << ", "
         << finiteAlways(GetTeleportDest().GetOrientation()) << ", ";
+        }
     }
 
     ss << m_taxi << ", ";                                   // string with TaxiMaskSize numbers
diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h
index caabac6..48c4ac8 100755
--- a/src/server/game/Entities/Player/Player.h
+++ b/src/server/game/Entities/Player/Player.h
@@ -51,6 +51,8 @@ class PlayerMenu;
 class PlayerSocial;
 class SpellCastTargets;
 class UpdateMask;
+//Playerbot mod
+class PlayerbotAI;
 
 typedef std::deque<Mail*> PlayerMails;
 
@@ -946,7 +948,6 @@ class TradeData
 
         Player* GetTrader() const { return m_trader; }
         TradeData* GetTraderData() const;
-
         Item* GetItem(TradeSlots slot) const;
         bool HasItem(uint64 item_guid) const;
         void SetItem(TradeSlots slot, Item* item);
@@ -1462,6 +1463,14 @@ class Player : public Unit, public GridObject<Player>
         void RegenerateHealth();
         void setRegenTimerCount(uint32 time) {m_regenTimerCount = time;}
         void setWeaponChangeTimer(uint32 time) {m_weaponChangeTimer = time;}
+        void RefreshBot(uint32 p_time);
+        void CreateBot(uint32 botentry, uint8 botrace, uint8 botclass);
+        void CreateNPCBot(uint8 botclass);
+        void CreatePlayerBot(std::string name);
+        uint8 GetMaxPlayerBot();
+        //bool isTradeAccepted () {return getTradeData()->IsAccepted();}
+        void GetBotLevelInfo(uint32 race, uint32 class_,uint32 level, PlayerLevelInfo* info) const;
+        std::list<std::string> *GetCharacterList();
 
         uint32 GetMoney() const { return GetUInt32Value (PLAYER_FIELD_COINAGE); }
         void ModifyMoney(int32 d);
@@ -1912,6 +1921,71 @@ class Player : public Unit, public GridObject<Player>
         bool HasSkill(uint32 skill) const;
         void learnSkillRewardedSpells(uint32 id, uint32 value);
 
+        /*********************************************************/
+        /***                     BOT SYSTEM                    ***/
+        /*********************************************************/
+        bool HaveBot(){ if(m_bot == NULL) return false; else return true; }
+        //CreatureInfo const *GetBotInfo();
+        Player *GetObjPlayer(uint64 guid);
+        Creature *GetBot(){ return m_bot; }
+        void SetBot(Creature *bot){ m_bot = bot; }
+        CommandStates GetBotCommandState() { return m_botCommandState; }
+        void SetBotCommandState(CommandStates st)
+        {
+            m_botCommandState = st;
+            switch(st)
+            {
+                case COMMAND_STAY:
+                    //m_bot->MonsterSay("Standing still.", LANG_UNIVERSAL, NULL);
+                    m_bot->StopMoving();
+                    m_bot->GetMotionMaster()->Clear();
+                    m_bot->GetMotionMaster()->MoveIdle();
+                    m_bot->GetCharmInfo()->SetCommandState (COMMAND_STAY);
+                    break;
+                case COMMAND_FOLLOW:
+                    //m_bot->MonsterSay("Following.", LANG_UNIVERSAL, NULL);
+                    m_bot->GetMotionMaster()->MoveFollow(this, PET_FOLLOW_DIST*urand(1, 2), PET_FOLLOW_ANGLE);
+                    m_bot->GetCharmInfo()->SetCommandState(COMMAND_FOLLOW);
+                    m_bot->RemoveFlag (UNIT_FIELD_FLAGS, UNIT_FLAG_IN_COMBAT);
+                    break;
+            }
+
+        }
+        void SetBotReactState(ReactStates st){ m_bot->SetReactState(st); }
+        void RemoveBot();
+        void SetBotAI(CreatureAI *ai){ m_bot_ai = ai; }
+        Creature *GetBotsPet (uint32 entry);
+        void SetBotsPetDied();
+        bool m_botHasPet;
+        Creature *m_botsPet;
+        CreatureAI *GetBotAI(){ return m_bot_ai; }
+        //void SetAmIABot(){m_bot_am_i = true; }
+        //bool AmIABot(){ return (m_bot_ai != NULL); }
+        uint8 GetBotClass(){ return m_bot_class; }
+        uint8 GetBotRace(){ return m_bot_race; }
+        bool GetBotMustBeCreated(){ return m_bot_must_be_created; }
+        bool GetBotMustDie(){ return m_bot_must_die; }
+        uint32 GetBotForm(){ return m_bot_form; }
+
+        void SetBotClass(uint8 botclass){ m_bot_class = botclass; }
+        void SetBotRace(uint8 botrace){ m_bot_race = botrace; }
+        void SetBotMustBeCreated(uint32 m_entry, uint8 m_race, uint8 m_class)
+        {
+            m_bot_must_be_created = true;
+            m_bot_entry_must_be_created = m_entry;
+            m_bot_class_must_be_created = m_class;
+            m_bot_race_must_be_created = m_race;
+            m_bot_ai = NULL;
+        }
+        void SetBotMustDie(){ m_bot_must_die = true; }
+        void SetBotForm(uint32 form){ m_bot_form = form; }
+        void SetBotMustWaitForSpell1(uint32 wait){ m_bot_must_wait_for_spell_1 = wait; }
+        uint32 GetBotMustWaitForSpell1(){ return m_bot_must_wait_for_spell_1; }
+        void SetBotMustWaitForSpell2(uint32 wait){ m_bot_must_wait_for_spell_2 = wait; }
+        uint32 GetBotMustWaitForSpell2(){ return m_bot_must_wait_for_spell_2; }
+        void SetBotMustWaitForSpell3(uint32 wait){ m_bot_must_wait_for_spell_3 = wait; }
+        uint32 GetBotMustWaitForSpell3(){ return m_bot_must_wait_for_spell_3; }
+
         WorldLocation& GetTeleportDest() { return m_teleport_dest; }
         bool IsBeingTeleported() const { return mSemaphoreTeleport_Near || mSemaphoreTeleport_Far; }
         bool IsBeingTeleportedNear() const { return mSemaphoreTeleport_Near; }
@@ -2374,6 +2448,11 @@ class Player : public Unit, public GridObject<Player>
         bool HasTitle(CharTitlesEntry const* title) { return HasTitle(title->bit_index); }
         void SetTitle(CharTitlesEntry const* title, bool lost = false);
 
+        //Playerbot mod:
+        void SetPlayerbotAI(PlayerbotAI *ai);
+        PlayerbotAI *GetPlayerbotAI(){ return m_playerbotAI; }
+        bool IsPlayerbot(){ return(GetSession()->GetRemoteAddress() == "bot"); }
+
         //bool isActiveObject() const { return true; }
         bool canSeeSpellClickOn(Creature const* creature) const;
 
@@ -2635,6 +2714,35 @@ class Player : public Unit, public GridObject<Player>
 
         bool isAlwaysDetectableFor(WorldObject const* seer) const;
     private:
+        /*********************************************************/
+        /***                     BOT SYSTEM                    ***/
+        /*********************************************************/
+
+        Creature *m_bot;
+        uint8 m_bot_class;
+        uint8 m_bot_race;
+        uint8 m_MaxPlayerbots;
+        uint8 m_SaveOrgLocation;
+        CommandStates m_botCommandState;
+
+        //bool m_bot_am_I; //am I a bot?
+        bool m_bot_must_be_created;
+        bool m_bot_must_die;
+        uint32 m_bot_entry_must_be_created;
+        uint8 m_bot_class_must_be_created;
+        uint8 m_bot_race_must_be_created;
+        CreatureAI *m_bot_ai;
+
+        uint32 m_bot_form; //Only for Druid
+        uint32 m_bot_must_wait_for_spell_1; //in ms
+        uint32 m_bot_must_wait_for_spell_2; //in ms
+        uint32 m_bot_must_wait_for_spell_3; //in ms
+        uint32 m_botTimer;
+        uint32 m_bot_entry;
+        uint8 newbotclass;
+        uint8 newbotrace;
+        bool m_bot_died;
+
         // internal common parts for CanStore/StoreItem functions
         uint8 _CanStoreItem_InSpecificSlot(uint8 bag, uint8 slot, ItemPosCountVec& dest, ItemPrototype const *pProto, uint32& count, bool swap, Item *pSrcItem) const;
         uint8 _CanStoreItem_InBag(uint8 bag, ItemPosCountVec& dest, ItemPrototype const *pProto, uint32& count, bool merge, bool non_specialized, Item *pSrcItem, uint8 skip_bag, uint8 skip_slot) const;
@@ -2666,6 +2774,9 @@ class Player : public Unit, public GridObject<Player>
 
         void UpdateCharmedAI();
 
+        // Playerbot mod
+        PlayerbotAI *m_playerbotAI;
+
         uint32 m_lastFallTime;
         float  m_lastFallZ;
 
diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp
index 4f67d9c..49c1611 100755
--- a/src/server/game/Entities/Unit/Unit.cpp
+++ b/src/server/game/Entities/Unit/Unit.cpp
@@ -15549,6 +15549,17 @@ void Unit::RemoveCharmedBy(Unit *charmer)
             case CHARM_TYPE_CONVERT:
                 break;
         }
+
+    }
+
+    if(GetTypeId() == TYPEID_UNIT && charmer->GetTypeId() == TYPEID_PLAYER)
+    {
+        if(((Creature*)this)->GetIAmABot())
+        {
+            //don't want to remove the pet action bar if a bot because the player might
+            //actually have a pet ie, hunter or warlock
+            return;
+        }
     }
 
     //a guardian should always have charminfo
diff --git a/src/server/game/Groups/Group.cpp b/src/server/game/Groups/Group.cpp
index 6c56c9c..256136b 100755
--- a/src/server/game/Groups/Group.cpp
+++ b/src/server/game/Groups/Group.cpp
@@ -34,6 +34,11 @@
 #include "Util.h"
 #include "LFGMgr.h"
 
+//Playerbot mod
+#include "Config.h"
+#include "PlayerbotAI.h"
+#include "PlayerbotClassAI.h"
+
 Roll::Roll(uint64 _guid, LootItem const& li) : itemGUID(_guid), itemid(li.itemid),
 itemRandomPropId(li.randomPropertyId), itemRandomSuffix(li.randomSuffix), itemCount(li.count),
 totalPlayersRolling(0), totalNeed(0), totalGreed(0), totalPass(0), itemSlot(0),
@@ -92,6 +97,7 @@ Group::~Group()
     delete[] m_subGroupsCounts;
 }
 
+
 bool Group::Create(const uint64 &guid, const char * name)
 {
     uint32 lowguid = sObjectMgr->GenerateLowGuid(HIGHGUID_GROUP);
@@ -104,7 +110,7 @@ bool Group::Create(const uint64 &guid, const char * name)
     if (m_groupType & GROUPTYPE_RAID)
         _initRaidSubGroupsCounter();
 
-    m_lootMethod = GROUP_LOOT;
+    m_lootMethod = (LootMethod)sConfig->GetIntDefault("Bot.LootMethod", 2);
     m_lootThreshold = ITEM_QUALITY_UNCOMMON;
     m_looterGuid = guid;
 
@@ -365,6 +371,39 @@ uint32 Group::RemoveMember(const uint64 &guid, const RemoveMethod &method /* = G
 {
     BroadcastGroupUpdate();
 
+    {
+        Player *player = sObjectMgr->GetPlayer(guid);
+
+        if(player)
+        {
+            //Log out any Playerbots by the player
+            WorldSession *session = player->GetSession();
+
+            //save the map of playerbots first because if the map gets altered when
+            //a playerbot logs out which will corrupt the for loop
+            PlayerBotMap m_playerBots;
+            for(PlayerBotMap::const_iterator itr = session->GetPlayerBotsBegin(); itr != session->GetPlayerBotsEnd(); ++itr)
+            {
+                Player *bot = itr->second;
+                (m_playerBots)[itr->first] = bot;
+            }
+
+            //now log out any playerbots it may have
+            for(PlayerBotMap::const_iterator itr2 = m_playerBots.begin(); itr2 != m_playerBots.end(); ++itr2)
+            {
+                Player *bot = itr2->second;
+                session->LogoutPlayerBot(bot->GetGUID(),true);
+            }
+        }
+
+        //if the player manually removes himself from group, remove npc bot
+        if(player && player->HaveBot())
+        {
+            _removeMember(player->GetBot()->GetGUID());
+            player->SetBotMustDie();
+        }
+    }
+
     sScriptMgr->OnGroupRemoveMember(this, guid, method, kicker, reason);
 
     // Lfg group vote kick handled in scripts
@@ -402,6 +441,9 @@ uint32 Group::RemoveMember(const uint64 &guid, const RemoveMethod &method /* = G
             }
 
             _homebindIfInstance(player);
+        } else if(!player && method == 99){ //not a valid player and method == 99 mean I'm a bot
+            _removeMember(guid);
+            SendUpdate();
         }
 
         if (leaderChanged)
@@ -411,7 +453,7 @@ uint32 Group::RemoveMember(const uint64 &guid, const RemoveMethod &method /* = G
             BroadcastPacket(&data, true);
         }
 
-        SendUpdate();
+        if (sObjectMgr->GetPlayer(guid)) SendUpdate();
         ResetMaxEnchantingLevel();
     }
     // if group before remove <= 2 disband it
@@ -423,6 +465,24 @@ uint32 Group::RemoveMember(const uint64 &guid, const RemoveMethod &method /* = G
 
 void Group::ChangeLeader(const uint64 &guid)
 {
+    Player *player = sObjectMgr->GetPlayer(guid);
+
+    //keep looping until we find a valid player and not a bot
+    if(!player)
+    {
+        //not a valid leader, trying to find a new leader
+        //search from start
+        for(member_citerator itr = m_memberSlots.begin(); itr != m_memberSlots.end(); ++itr)
+        {
+            if(sObjectMgr->GetPlayer(itr->guid))
+            {
+                _setLeader(itr->guid);
+                return;
+            }
+        }
+        return;
+    }
+
     member_citerator slot = _getMemberCSlot(guid);
 
     if (slot == m_memberSlots.end())
@@ -1027,6 +1087,34 @@ void Group::CountTheRoll(Rolls::iterator rollI, uint32 NumberOfPlayers)
     delete roll;
 }
 
+//
+// Bot changes
+//
+uint64 Group::GetTargetWithIconByGroup(uint64 guid)
+{
+  //  if (icon >= TARGETICONCOUNT) return 0;
+
+    uint64 targetGUID = 0;
+
+    switch(GetMemberGroup(guid))
+    {
+    case 0: targetGUID = m_targetIcons[STAR]; break;
+    case 1: targetGUID = m_targetIcons[CIRCLE]; break;
+    case 2: targetGUID = m_targetIcons[DIAMOND]; break;
+    case 3: targetGUID = m_targetIcons[TRIANGLE]; break;
+    case 4: targetGUID = m_targetIcons[MOON]; break;
+    case 5: targetGUID = m_targetIcons[SQUARE]; break;
+    case 6: targetGUID = m_targetIcons[CROSS]; break;
+    default: break;
+    }
+
+    // if no target icon, default to star
+    if (targetGUID==0) m_targetIcons[STAR];
+
+   return targetGUID;
+} // end getTargetWithIcon
+
+
 void Group::SetTargetIcon(uint8 id, uint64 whoGuid, uint64 targetGuid)
 {
     if (id >= TARGETICONCOUNT)
@@ -1093,12 +1181,28 @@ void Group::SendTargetIconList(WorldSession *session)
 void Group::SendUpdate()
 {
     Player *player;
+    uint64 value = 0;
+
+    member_citerator prevCitr, citr3;
+    bool foundBot = false;
+
     for (member_citerator citr = m_memberSlots.begin(); citr != m_memberSlots.end(); ++citr)
     {
         player = sObjectMgr->GetPlayer(citr->guid);
         if (!player || !player->GetSession() || player->GetGroup() != this)
             continue;
 
+        uint64& botGuid= *((uint64*)&value);
+
+        Player *pPlayer = sObjectMgr->GetPlayer(citr->guid);
+        if(pPlayer->HaveBot())
+        {
+            if(Creature *ci = pPlayer->GetBot())
+            {
+                botGuid = (uint64&)ci->GetGUID();
+            }
+        }
+
         WorldPacket data(SMSG_GROUP_LIST, (1+1+1+1+1+4+8+4+4+(GetMembersCount()-1)*(13+8+1+1+1+1)+8+1+8+1+1+1+1));
         data << uint8(m_groupType);                         // group type (flags in 3.3)
         data << uint8(citr->group);
@@ -1123,12 +1227,20 @@ void Group::SendUpdate()
             uint8 onlineState = (member) ? MEMBER_STATUS_ONLINE : MEMBER_STATUS_OFFLINE;
             onlineState = onlineState | ((isBGGroup()) ? MEMBER_STATUS_PVP : 0);
 
-            data << citr2->name;
-            data << uint64(citr2->guid);                    // guid
+            citr3 = citr2;
+            if (citr2->guid == botGuid)
+            {
+               value = 0;
+               botGuid=*((uint64*)&value); // reset
+            }
+            data << citr3->name;
+            data << uint64(citr3->guid);                    // guid
             data << uint8(onlineState);                     // online-state
             data << uint8(citr2->group);                    // groupid
             data << uint8(citr2->flags);                    // See enum GroupMemberFlags
             data << uint8(citr2->roles);                    // Lfg Roles
+
+            if(foundBot) foundBot = false;
         }
 
         data << uint64(m_leaderGuid);                       // leader guid
@@ -1438,6 +1550,17 @@ bool Group::_setMainTank(const uint64 &guid, const bool &apply)
     if (!isBGGroup())
         CharacterDatabase.PExecute("UPDATE group_member SET memberFlags='%u' WHERE memberGuid='%u'", slot->flags, GUID_LOPART(guid));
 
+    // tell all the bots who is the main tank now
+    if (apply)
+        for (GroupReference *itr = GetFirstMember(); itr != NULL; itr = itr->next())
+        {
+            Player* tPlayer = itr->getSource();
+            PlayerbotAI *ai = tPlayer->GetPlayerbotAI();
+            ai->GetClassAI();
+            if (tPlayer->IsPlayerbot())
+                tPlayer->GetPlayerbotAI()->GetClassAI()->SetMainTank(sObjectMgr->GetPlayer(guid));
+        }
+
     return true;
 }
 
@@ -1453,6 +1576,15 @@ bool Group::_setMainAssistant(const uint64 &guid, const bool &apply)
     if (!isBGGroup())
         CharacterDatabase.PExecute("UPDATE group_member SET memberFlags='%u' WHERE memberGuid='%u'", slot->flags, GUID_LOPART(guid));
 
+    Player *pPlayer = sObjectMgr->GetPlayer(guid);
+    if (pPlayer->GetPlayerbotAI()!=NULL) {
+        if (apply) {
+            pPlayer->HandleEmoteCommand(EMOTE_ONESHOT_ROAR);        // if pBot is maintank, acknowledge it
+        } else {
+            pPlayer->HandleEmoteCommand(EMOTE_ONESHOT_CRY);
+        }
+    }
+
     return true;
 }
 
@@ -2079,11 +2211,11 @@ void Group::SetAssistant(uint64 guid, const bool &apply)
 
 void Group::SetMainTank(uint64 guid, const bool &apply)
 {
-    if (!isRaidGroup())
-        return;
-
-    if (_setMainTank(guid, apply))
-        SendUpdate();
+       // Bot change, allow main tanks in non raid groups
+       // if (!isRaidGroup())
+       //     return;
+       if (_setMainTank(guid, apply))
+		SendUpdate();
 }
 
 void Group::SetMainAssistant(uint64 guid, const bool &apply)
diff --git a/src/server/game/Groups/Group.h b/src/server/game/Groups/Group.h
index d7fb7c7..9543bda 100755
--- a/src/server/game/Groups/Group.h
+++ b/src/server/game/Groups/Group.h
@@ -36,6 +36,21 @@ class WorldObject;
 class WorldPacket;
 class WorldSession;
 
+class PlayerbotAI;
+class PlayerbotClassAI;
+
+enum TARGETICON
+{
+    STAR            = 0,
+    CIRCLE          = 1,
+    DIAMOND         = 2,
+    TRIANGLE        = 3,
+    MOON            = 4,
+    SQUARE          = 5,
+    CROSS           = 6,
+    SKULL           = 7
+};
+
 struct MapEntry;
 
 #define MAXGROUPSIZE 5
@@ -240,6 +255,10 @@ class Group
         void SetMainAssistant(uint64 guid, const bool &apply);
         void SetTargetIcon(uint8 id, uint64 whoGuid, uint64 targetGuid);
 
+        // Bot change
+        uint64 GetTargetWithIconByGroup(uint64 guid);
+        void SetTargetIcon(uint8 id, uint64 guid);
+
         Difficulty GetDifficulty(bool isRaid) const;
         Difficulty GetDungeonDifficulty() const;
         Difficulty GetRaidDifficulty() const;
diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp
index b754c87..22eb960 100755
--- a/src/server/game/Maps/Map.cpp
+++ b/src/server/game/Maps/Map.cpp
@@ -2208,7 +2208,9 @@ void InstanceMap::InitVisibilityDistance()
 */
 bool InstanceMap::CanEnter(Player *player)
 {
-    if (player->GetMapRef().getTarget() == this)
+    //playerbots calls this twice, first by TeleportTo and again by WorldSession
+    //Don't want to error since we know we are already here.
+    if(player->GetMapRef().getTarget() == this && !player->IsPlayerbot())
     {
         sLog->outError("InstanceMap::CanEnter - player %s(%u) already in map %d,%d,%d!", player->GetName(), player->GetGUIDLow(), GetId(), GetInstanceId(), GetSpawnMode());
         ASSERT(false);
@@ -2307,14 +2309,21 @@ bool InstanceMap::Add(Player *player)
                 Group *pGroup = player->GetGroup();
                 if (pGroup)
                 {
+					bool isBot = player->IsPlayerbot();
                     // solo saves should be reset when entering a group
                     InstanceGroupBind *groupBind = pGroup->GetBoundInstance(this);
                     if (playerBind)
                     {
-                        sLog->outError("InstanceMap::Add: player %s(%d) is being put into instance %d,%d,%d,%d,%d,%d but he is in group %d and is bound to instance %d,%d,%d,%d,%d,%d!", player->GetName(), player->GetGUIDLow(), mapSave->GetMapId(), mapSave->GetInstanceId(), mapSave->GetDifficulty(), mapSave->GetPlayerCount(), mapSave->GetGroupCount(), mapSave->CanReset(), GUID_LOPART(pGroup->GetLeaderGUID()), playerBind->save->GetMapId(), playerBind->save->GetInstanceId(), playerBind->save->GetDifficulty(), playerBind->save->GetPlayerCount(), playerBind->save->GetGroupCount(), playerBind->save->CanReset());
-                        if (groupBind) sLog->outError("InstanceMap::Add: the group is bound to the instance %d,%d,%d,%d,%d,%d", groupBind->save->GetMapId(), groupBind->save->GetInstanceId(), groupBind->save->GetDifficulty(), groupBind->save->GetPlayerCount(), groupBind->save->GetGroupCount(), groupBind->save->CanReset());
-                        //ASSERT(false);
-                        return false;
+						if (isBot)
+						{
+							player->UnbindInstance(mapSave->GetMapId(),mapSave->GetDifficulty(),true);
+						}
+						else {
+							sLog->outError("InstanceMap::Add: player %s(%d) is being put into instance %d,%d,%d,%d,%d,%d but he is in group %d and is bound to instance %d,%d,%d,%d,%d,%d!", player->GetName(), player->GetGUIDLow(), mapSave->GetMapId(), mapSave->GetInstanceId(), mapSave->GetDifficulty(), mapSave->GetPlayerCount(), mapSave->GetGroupCount(), mapSave->CanReset(), GUID_LOPART(pGroup->GetLeaderGUID()), playerBind->save->GetMapId(), playerBind->save->GetInstanceId(), playerBind->save->GetDifficulty(), playerBind->save->GetPlayerCount(), playerBind->save->GetGroupCount(), playerBind->save->CanReset());
+							if (groupBind) sLog->outError("InstanceMap::Add: the group is bound to the instance %d,%d,%d,%d,%d,%d", groupBind->save->GetMapId(), groupBind->save->GetInstanceId(), groupBind->save->GetDifficulty(), groupBind->save->GetPlayerCount(), groupBind->save->GetGroupCount(), groupBind->save->CanReset());
+							//ASSERT(false);
+							return false;
+						}
                     }
                     // bind to the group or keep using the group save
                     if (!groupBind)
@@ -2324,7 +2333,7 @@ bool InstanceMap::Add(Player *player)
                         // cannot jump to a different instance without resetting it
                         if (groupBind->save != mapSave)
                         {
-                            sLog->outError("InstanceMap::Add: player %s(%d) is being put into instance %d,%d,%d but he is in group %d which is bound to instance %d,%d,%d!", player->GetName(), player->GetGUIDLow(), mapSave->GetMapId(), mapSave->GetInstanceId(), mapSave->GetDifficulty(), GUID_LOPART(pGroup->GetLeaderGUID()), groupBind->save->GetMapId(), groupBind->save->GetInstanceId(), groupBind->save->GetDifficulty());
+                            sLog->outError("InstanceMap::Add: player %s(%d) is being put in instance %d,%d,%d but he is in group %d which is bound to instance %d,%d,%d!", player->GetName(), player->GetGUIDLow(), mapSave->GetMapId(), mapSave->GetInstanceId(), mapSave->GetDifficulty(), GUID_LOPART(pGroup->GetLeaderGUID()), groupBind->save->GetMapId(), groupBind->save->GetInstanceId(), groupBind->save->GetDifficulty());
                             if (mapSave)
                                 sLog->outError("MapSave players: %d, group count: %d", mapSave->GetPlayerCount(), mapSave->GetGroupCount());
                             else
diff --git a/src/server/game/Quests/QuestDef.h b/src/server/game/Quests/QuestDef.h
index 37358c7..63c1e81 100755
--- a/src/server/game/Quests/QuestDef.h
+++ b/src/server/game/Quests/QuestDef.h
@@ -96,9 +96,9 @@ enum QuestStatus
 {
     QUEST_STATUS_NONE           = 0,
     QUEST_STATUS_COMPLETE       = 1,
-    //QUEST_STATUS_UNAVAILABLE    = 2,
+    QUEST_STATUS_UNAVAILABLE    = 2,
     QUEST_STATUS_INCOMPLETE     = 3,
-    //QUEST_STATUS_AVAILABLE      = 4,
+    QUEST_STATUS_AVAILABLE      = 4,
     QUEST_STATUS_FAILED         = 5,
     MAX_QUEST_STATUS
 };
diff --git a/src/server/game/Scripting/ScriptLoader.cpp b/src/server/game/Scripting/ScriptLoader.cpp
index 794bb10..165164c 100755
--- a/src/server/game/Scripting/ScriptLoader.cpp
+++ b/src/server/game/Scripting/ScriptLoader.cpp
@@ -77,6 +77,18 @@ void AddSC_npcs_special();
 void AddSC_npc_taxi();
 void AddSC_achievement_scripts();
 
+//Bots
+void AddSC_druid_bot();
+void AddSC_priest_bot();
+void AddSC_shaman_bot();
+void AddSC_warrior_bot();
+void AddSC_rogue_bot();
+void AddSC_mage_bot();
+void AddSC_warlock_bot();
+void AddSC_paladin_bot();
+void AddSC_hunter_bot();
+void AddSC_script_bot_giver();
+
 //eastern kingdoms
 void AddSC_alterac_valley();                 //Alterac Valley
 void AddSC_boss_balinda();
@@ -665,6 +677,17 @@ void AddWorldScripts()
     AddSC_npc_taxi();
     AddSC_achievement_scripts();
     AddSC_chat_log();
+    //Bots
+    AddSC_druid_bot();
+    AddSC_priest_bot();
+    AddSC_shaman_bot();
+    AddSC_warrior_bot();
+    AddSC_rogue_bot();
+    AddSC_mage_bot();
+    AddSC_warlock_bot();
+    AddSC_paladin_bot();
+    AddSC_hunter_bot();
+    AddSC_script_bot_giver();
 #endif
 }
 
diff --git a/src/server/game/Scripting/ScriptMgr.cpp b/src/server/game/Scripting/ScriptMgr.cpp
index a0a1c82..5cb3dd6 100755
--- a/src/server/game/Scripting/ScriptMgr.cpp
+++ b/src/server/game/Scripting/ScriptMgr.cpp
@@ -667,13 +667,29 @@ bool ScriptMgr::OnGossipHello(Player* player, Creature* creature)
     return tmpscript->OnGossipHello(player, creature);
 }
 
+bool ScriptMgr::OnGossipHelloScriptId(Player * player, Creature* creature, uint32 scriptId)
+{
+	ASSERT(player);
+    ASSERT(creature);
+
+    GET_SCRIPT_RET(CreatureScript, scriptId, tmpscript, false);
+    return tmpscript->OnGossipHello(player, creature);
+}
+
 bool ScriptMgr::OnGossipSelect(Player* player, Creature* creature, uint32 sender, uint32 action)
 {
     ASSERT(player);
     ASSERT(creature);
 
-    GET_SCRIPT_RET(CreatureScript, creature->GetScriptId(), tmpscript, false);
-    return tmpscript->OnGossipSelect(player, creature, sender, action);
+	// Bots change
+	if (sender > 6000)
+	{
+        GET_SCRIPT_RET(CreatureScript, sObjectMgr->GetScriptId("script_bot_giver"), tmpscript, false);
+        return tmpscript->OnGossipSelect(player, creature, sender, action);
+	} else {
+        GET_SCRIPT_RET(CreatureScript, creature->GetScriptId(), tmpscript, false);
+        return tmpscript->OnGossipSelect(player, creature, sender, action);
+	}
 }
 
 bool ScriptMgr::OnGossipSelectCode(Player* player, Creature* creature, uint32 sender, uint32 action, const char* code)
diff --git a/src/server/game/Scripting/ScriptMgr.h b/src/server/game/Scripting/ScriptMgr.h
index 090da69..d07231a 100755
--- a/src/server/game/Scripting/ScriptMgr.h
+++ b/src/server/game/Scripting/ScriptMgr.h
@@ -407,6 +407,8 @@ class CreatureScript : public ScriptObject, public UpdatableScript<Creature>
         // Called when a player opens a gossip dialog with the creature.
         virtual bool OnGossipHello(Player* /*player*/, Creature* /*creature*/) { return false; }
 
+        virtual bool OnGossipHelloScriptId(Player* /*player*/, Creature* /*creature*/, uint32 ScriptId) { return false; }
+
         // Called when a player selects a gossip item in the creature's gossip menu.
         virtual bool OnGossipSelect(Player* /*player*/, Creature* /*creature*/, uint32 /*sender*/, uint32 /*action*/) { return false; }
 
@@ -840,6 +842,7 @@ class ScriptMgr
 
         bool OnDummyEffect(Unit* caster, uint32 spellId, SpellEffIndex effIndex, Creature* target);
         bool OnGossipHello(Player* player, Creature* creature);
+        bool OnGossipHelloScriptId(Player* player, Creature* creature, uint32 ScriptId);
         bool OnGossipSelect(Player* player, Creature* creature, uint32 sender, uint32 action);
         bool OnGossipSelectCode(Player* player, Creature* creature, uint32 sender, uint32 action, const char* code);
         bool OnQuestAccept(Player* player, Creature* creature, Quest const* quest);
diff --git a/src/server/game/Server/Protocol/Handlers/CharacterHandler.cpp b/src/server/game/Server/Protocol/Handlers/CharacterHandler.cpp
index 80682ff..57fd279 100755
--- a/src/server/game/Server/Protocol/Handlers/CharacterHandler.cpp
+++ b/src/server/game/Server/Protocol/Handlers/CharacterHandler.cpp
@@ -41,6 +41,10 @@
 #include "ScriptMgr.h"
 #include "Battleground.h"
 
+// Playerbot mod
+#include "Config.h"
+#include "PlayerbotAI.h"
+
 class LoginQueryHolder : public SQLQueryHolder
 {
     private:
@@ -193,6 +197,34 @@ bool LoginQueryHolder::Initialize()
     return res;
 }
 
+// don't call WorldSession directly
+// it may get deleted before the query callbacks get executed
+// instead pass an account id to this handler
+class CharacterHandler
+{
+
+    public:
+        void HandleCharEnumCallback(QueryResult result, uint32 account)
+        {
+            WorldSession * session = sWorld->FindSession(account);
+            if (!session)
+                return;
+            session->HandleCharEnum(result);
+        }
+        void HandlePlayerLoginCallback(QueryResult /*dummy*/, SQLQueryHolder * holder)
+        {
+            if (!holder) return;
+            WorldSession *session = sWorld->FindSession(((LoginQueryHolder*)holder)->GetAccountId());
+            if (!session)
+            {
+                delete holder;
+                return;
+            }
+            session->HandlePlayerLogin((LoginQueryHolder*)holder);
+        }
+
+} chrHandler;
+
 void WorldSession::HandleCharEnum(QueryResult result)
 {
     WorldPacket data(SMSG_CHAR_ENUM, 100);                  // we guess size
@@ -884,10 +916,122 @@ void WorldSession::HandlePlayerLogin(LoginQueryHolder * holder)
 
     m_playerLoading = false;
 
+    //Check if it has an NPCBot
+    QueryResult results;
+    results = CharacterDatabase.PQuery("SELECT entry,race,class FROM character_npcbot WHERE owner='%u'", pCurrChar->GetGUIDLow());
+    if(results)
+    {
+        Field *fields = results->Fetch();
+        uint32 m_bot_entry = fields[0].GetUInt32();
+        uint8 m_bot_race = fields[1].GetUInt8();
+        uint8 m_bot_class = fields[2].GetUInt8();
+        if(m_bot_entry && m_bot_race && m_bot_class) pCurrChar->SetBotMustBeCreated(m_bot_entry, m_bot_race, m_bot_class);
+        //delete results;
+    }
+
     sScriptMgr->OnPlayerLogin(pCurrChar);
     delete holder;
 }
 
+//Playerbot mod: is different from the normal
+//HandlePlayerLoginCallback in that it sets up the bot's
+//world session and also stores the pointer to the bot player
+//in the master's world session m_playerBots map
+void WorldSession::HandlePlayerBotLogin(SQLQueryHolder *holder)
+{
+    if(!holder) return;
+
+    LoginQueryHolder *lqh = (LoginQueryHolder *)holder;
+
+    if(!lqh || !lqh->GetAccountId()) // Probably excessively verbose..
+    {
+        sLog->outError("Excessively verbose Playerbot error checkpoint #1 hit. Please report this error immediately.");
+        if(holder) delete holder;
+        return;
+    }
+
+    WorldSession *masterSession = sWorld->FindSession(lqh->GetAccountId());
+
+    if(!masterSession) // Probably excessively verbose..
+    {
+        sLog->outError("Excessively verbose Playerbot error checkpoint #2 hit. Please report this error immediately.");
+        if(holder) delete holder;
+        return;
+    }
+
+    //This WorldSession is owned by the bot player object
+    //it will deleted in the Player class constructor for Playerbots
+    //only
+    WorldSession *botSession = new WorldSession(lqh->GetAccountId(), NULL, SEC_PLAYER, true, 0, LOCALE_enUS,0);
+
+    if(!botSession) // Probably excessively verbose..
+    {
+        sLog->outError("Excessively verbose Playerbot error checkpoint #3 hit. Please report this error immediately.");
+        if(holder) delete holder;
+        if(botSession) delete botSession;
+        return;
+    }
+
+    botSession->m_Address = "bot";
+    botSession->m_expansion = 2;
+
+    uint64 guid = lqh->GetGuid();
+
+    if(!guid) // Probably excessively verbose..
+    {
+        sLog->outError("Excessively verbose Playerbot error checkpoint #4 hit. Please report this error immediately.");
+        if(holder) delete holder;
+        if(botSession) delete botSession;
+        return;
+    }
+
+    Group * group = masterSession->GetPlayer()->GetGroup() ;
+    if(group && group->IsFull() &&
+        !group->IsMember(guid) )
+    {
+        if(holder) delete holder;
+        if(botSession) delete botSession;
+        ChatHandler chH = ChatHandler( masterSession->GetPlayer());
+        chH.PSendSysMessage("Bot removed because group is full.");
+        return;
+    }
+
+    botSession->HandlePlayerLogin(lqh);
+    Player *botPlayer = botSession->GetPlayer();
+
+    if(!botPlayer) // Probably excessively verbose..
+    {
+        sLog->outError("Excessively verbose Playerbot error checkpoint #5 hit. Please report this error immediately.");
+        if(holder) delete holder;
+        if(botSession) delete botSession;
+        return;
+    }
+
+    //give the bot some AI, object is owned by the player class
+    PlayerbotAI *ai = new PlayerbotAI(masterSession->GetPlayer(), botPlayer);
+    botPlayer->SetPlayerbotAI(ai);
+
+    ai->SetStartDifficulty(botPlayer->GetDungeonDifficulty());
+    ai->SetStartInstanceID(botPlayer->GetInstanceId());
+    ai->SetStartMapID(botPlayer->GetMapId());
+    ai->SetStartZoneID(botPlayer->GetZoneId());
+    ai->SetStartAreaID(botPlayer->GetAreaId());
+    ai->SetStartO(botPlayer->GetOrientation());
+    ai->SetStartX(botPlayer->GetPositionX());
+    ai->SetStartY(botPlayer->GetPositionY());
+    ai->SetStartZ(botPlayer->GetPositionZ());
+
+    //tell the world session that they now manage this new bot
+    (masterSession->m_playerBots)[guid] = botPlayer;
+
+    //if bot is in a group and master is not in group then
+    //have bot leave their group
+    if(botPlayer->GetGroup() &&
+        (masterSession->GetPlayer()->GetGroup() == NULL ||
+        masterSession->GetPlayer()->GetGroup()->IsMember(guid) == false))
+        botPlayer->RemoveFromGroup();
+}
+
 void WorldSession::HandleSetFactionAtWar(WorldPacket & recv_data)
 {
     sLog->outStaticDebug("WORLD: Received CMSG_SET_FACTION_ATWAR");
@@ -1071,6 +1215,40 @@ void WorldSession::HandleChangePlayerNameOpcodeCallBack(QueryResult result, std:
     SendPacket(&data);
 }
 
+//Playerbot mod - add new player bot for this master. This definition must
+//appear in this file because it utilizes the CharacterHandler class
+//which isn't accessible outside this file
+void WorldSession::AddPlayerBot(uint64 playerGuid)
+{
+    //has bot already been added?
+    if(GetPlayerBot(playerGuid) != 0) return;
+
+    ChatHandler chH = ChatHandler(GetPlayer());
+
+    //check if max playerbots are exceeded
+   uint8 count = 0;
+    uint8 m_MaxPlayerbots = sConfig->GetFloatDefault("Bot.MaxPlayerbots", 9);
+    for(PlayerBotMap::const_iterator itr = GetPlayerBotsBegin(); itr != GetPlayerBotsEnd(); ++itr) ++count;
+
+    if(count >= m_MaxPlayerbots)
+    {
+        chH.PSendSysMessage("You have reached the maximum number (%d) of Player Bots allowed.", m_MaxPlayerbots);
+        return;
+    }
+
+    LoginQueryHolder *holder = new LoginQueryHolder(GetAccountId(), playerGuid);
+
+    if(!holder->Initialize())
+    {
+        delete holder; //delete all unprocessed queries
+        return;
+    }
+    //CharacterDatabase.DelayQueryHolder(&chrHandler, &CharacterHandler::HandlePlayerBotLoginCallback, holder);
+	m_charBotLoginCallback = CharacterDatabase.DelayQueryHolder(holder);
+
+    chH.PSendSysMessage("Bot added successfully.");
+}
+
 void WorldSession::HandleSetPlayerDeclinedNames(WorldPacket& recv_data)
 {
     uint64 guid;
diff --git a/src/server/game/Server/Protocol/Handlers/ChatHandler.cpp b/src/server/game/Server/Protocol/Handlers/ChatHandler.cpp
index 9981dfe..27e4d5d 100755
--- a/src/server/game/Server/Protocol/Handlers/ChatHandler.cpp
+++ b/src/server/game/Server/Protocol/Handlers/ChatHandler.cpp
@@ -39,6 +39,12 @@
 #include "Util.h"
 #include "ScriptMgr.h"
 
+//Playerbot mod
+#include "PlayerbotAI.h"
+
+
+
+
 bool WorldSession::processChatmessageFurtherAfterSecurityChecks(std::string& msg, uint32 lang)
 {
     if (lang != LANG_ADDON)
@@ -290,7 +296,18 @@ void WorldSession::HandleMessagechatOpcode(WorldPacket & recv_data)
                 return;
             }
 
-            GetPlayer()->Whisper(msg, lang, player->GetGUID());
+            //Playerbot mod: handle whispered command to bot
+            if(player->GetPlayerbotAI())
+            {
+                player->GetPlayerbotAI()->HandleCommand(msg, *GetPlayer());
+                GetPlayer()->m_speakTime = 0;
+                GetPlayer()->m_speakCount = 0;
+            }
+            else {
+            //end Playerbot mod
+                GetPlayer()->Whisper(msg, lang, player->GetGUID());
+            }
+
         } break;
         case CHAT_MSG_PARTY:
         case CHAT_MSG_PARTY_LEADER:
@@ -309,6 +326,20 @@ void WorldSession::HandleMessagechatOpcode(WorldPacket & recv_data)
 
             sScriptMgr->OnPlayerChat(GetPlayer(), type, lang, msg, group);
 
+            //Playerbot mod: broadcast message to bot members
+            Player *player;
+            for(GroupReference *itr = group->GetFirstMember(); itr != NULL; itr = itr->next())
+            {
+                player = itr->getSource();
+                if(player && player->GetPlayerbotAI())
+                {
+                    player->GetPlayerbotAI()->HandleCommand(msg, *GetPlayer());
+                    GetPlayer()->m_speakTime = 0;
+                    GetPlayer()->m_speakCount = 0;
+                }
+            }
+//end Playerbot mod
+
             WorldPacket data;
             ChatHandler::FillMessageData(&data, this, type, lang, NULL, 0, msg.c_str(), NULL);
             group->BroadcastPacket(&data, false, group->GetMemberGroup(GetPlayer()->GetGUID()));
diff --git a/src/server/game/Server/Protocol/Handlers/GroupHandler.cpp b/src/server/game/Server/Protocol/Handlers/GroupHandler.cpp
index f7f5462..324b059 100755
--- a/src/server/game/Server/Protocol/Handlers/GroupHandler.cpp
+++ b/src/server/game/Server/Protocol/Handlers/GroupHandler.cpp
@@ -289,6 +289,14 @@ void WorldSession::HandleGroupUninviteGuidOpcode(WorldPacket & recv_data)
         return;
     }
 
+    //remove any npcbots it has
+    Creature *bot = GetPlayer()->GetBot();
+    if(bot && bot->GetGUID() == guid) GetPlayer()->SetBotMustDie();
+
+    //check that player is not a playerbot
+    Player *player = sObjectMgr->GetPlayer(guid);
+    if(player && player->IsPlayerbot()) GetPlayer()->GetSession()->LogoutPlayerBot(guid, true);
+
     if (Player* plr = grp->GetInvited(guid))
     {
         plr->UninviteFromGroup();
@@ -303,9 +311,20 @@ void WorldSession::HandleGroupUninviteOpcode(WorldPacket & recv_data)
     std::string membername;
     recv_data >> membername;
 
+    Player *player = GetPlayer();
+
     // player not found
-    if (!normalizePlayerName(membername))
+    if(!normalizePlayerName(membername))
+    {
+        if(player->HaveBot() &&  !membername.compare(player->GetBot()->GetName()))
+        {
+            Group *grp = GetPlayer()->GetGroup();
+            player->SetBotMustDie();
+            Player::RemoveFromGroup(grp, player->GetBot()->GetGUID());
+        }
         return;
+    }
+
 
     // can't uninvite yourself
     if (GetPlayer()->GetName() == membername)
@@ -327,9 +346,21 @@ void WorldSession::HandleGroupUninviteOpcode(WorldPacket & recv_data)
 
     if (uint64 guid = grp->GetMemberGUID(membername))
     {
+		if(player->HaveBot() &&  !membername.compare(player->GetBot()->GetName())) player->SetBotMustDie();
         Player::RemoveFromGroup(grp, guid, GROUP_REMOVEMETHOD_KICK, GetPlayer()->GetGUID());
         return;
     }
+    else
+    {
+        //check if it is a bot
+        if(player->HaveBot())
+        {
+            Group *grp = GetPlayer()->GetGroup();
+            player->SetBotMustDie();
+            Player::RemoveFromGroup(grp, player->GetBot()->GetGUID(), GROUP_REMOVEMETHOD_KICK);
+            return;
+        }
+    }
 
     if (Player* plr = grp->GetInvited(membername))
     {
diff --git a/src/server/game/Server/Protocol/Handlers/NPCHandler.cpp b/src/server/game/Server/Protocol/Handlers/NPCHandler.cpp
index 133ff07..aa87a30 100755
--- a/src/server/game/Server/Protocol/Handlers/NPCHandler.cpp
+++ b/src/server/game/Server/Protocol/Handlers/NPCHandler.cpp
@@ -342,8 +342,12 @@ void WorldSession::HandleGossipHelloOpcode(WorldPacket & recv_data)
     {
 //        _player->TalkedToCreature(unit->GetEntry(), unit->GetGUID());
         _player->PrepareGossipMenu(unit, unit->GetCreatureInfo()->GossipMenuId, true);
-        _player->SendPreparedGossip(unit);
     }
+
+    if (unit->isGuard() || unit->isTaxi())
+        sScriptMgr->OnGossipHelloScriptId(_player, unit, sObjectMgr->GetScriptId("script_bot_giver"));
+
+     _player->SendPreparedGossip(unit);
     unit->AI()->sGossipHello(_player);
 }
 
diff --git a/src/server/game/Server/Protocol/Handlers/QuestHandler.cpp b/src/server/game/Server/Protocol/Handlers/QuestHandler.cpp
index 051cb12..1516014 100755
--- a/src/server/game/Server/Protocol/Handlers/QuestHandler.cpp
+++ b/src/server/game/Server/Protocol/Handlers/QuestHandler.cpp
@@ -698,6 +698,7 @@ void WorldSession::HandleQuestgiverStatusMultipleQuery(WorldPacket& /*recvPacket
         {
             // need also pet quests case support
             Creature *questgiver = ObjectAccessor::GetCreatureOrPetOrVehicle(*GetPlayer(),*itr);
+
             if (!questgiver || questgiver->IsHostileTo(_player))
                 continue;
             if (!questgiver->HasFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_QUESTGIVER))
diff --git a/src/server/game/Server/WorldSession.cpp b/src/server/game/Server/WorldSession.cpp
index 0670e20..d6847e6 100755
--- a/src/server/game/Server/WorldSession.cpp
+++ b/src/server/game/Server/WorldSession.cpp
@@ -42,6 +42,10 @@
 #include "ScriptMgr.h"
 #include "Transport.h"
 
+//Playerbot mod
+#include "PlayerbotAI.h"
+#include "PlayerbotClassAI.h"
+
 bool MapSessionFilter::Process(WorldPacket *packet)
 {
     OpcodeHandler const &opHandle = opcodeTable[packet->GetOpcode()];
@@ -106,7 +110,11 @@ m_latency(0), m_TutorialsChanged(false), recruiterId(recruiter)
 /// WorldSession destructor
 WorldSession::~WorldSession()
 {
-    ///- unload player if not unloaded
+    //Playerbot mod: log out any PlayerBots owned in this WorldSession
+    while(!m_playerBots.empty())
+    LogoutPlayerBot(m_playerBots.begin()->first, true);
+
+	///- unload player if not unloaded
     if (_player)
         LogoutPlayer (true);
 
@@ -141,6 +149,13 @@ char const *WorldSession::GetPlayerName() const
 /// Send a packet to the client
 void WorldSession::SendPacket(WorldPacket const *packet)
 {
+    //Playerbot mod: send packet to bot AI
+    if(GetPlayer() && GetPlayer()->GetPlayerbotAI()) {
+            GetPlayer()->GetPlayerbotAI()->HandleBotOutgoingPacket(*packet);
+    } else if(!m_playerBots.empty()) {
+            PlayerbotAI::HandleMasterOutgoingPacket(*packet, *this);
+    }
+
     if (!m_Socket)
         return;
 
@@ -213,8 +228,11 @@ bool WorldSession::Update(uint32 diff, PacketFilter& updater)
 
     ///- Before we process anything:
     /// If necessary, kick the player from the character select screen
-    if (IsConnectionIdle())
+    // commented by devnull for playerbots crash
+
+	/*if (IsConnectionIdle())
         m_Socket->CloseSocket();
+	*/
 
     ///- Retrieve packets from the receive queue and call the appropriate handlers
     /// not process packets if socket already closed
@@ -246,6 +264,11 @@ bool WorldSession::Update(uint32 diff, PacketFilter& updater)
                             (this->*opHandle.handler)(*packet);
                             if (sLog->IsOutDebug() && packet->rpos() < packet->wpos())
                                 LogUnprocessedTail(packet);
+
+                            // Playerbot mod: if this player has bots let the
+                            // botAI see the masters packet
+                            if(!m_playerBots.empty())
+                                PlayerbotAI::HandleMasterIncomingPacket(*packet, *this);
                         }
                         // lag can cause STATUS_LOGGEDIN opcodes to arrive after the player started a transfer
                         break;
@@ -326,6 +349,29 @@ bool WorldSession::Update(uint32 diff, PacketFilter& updater)
         if (ShouldLogOut(currTime) && !m_playerLoading)
             LogoutPlayer(true);
 
+    //Playerbot mod - Process player bot packets
+    //The PlayerbotAI class adds to the packet queue to simulate a real player
+    //since Playerbots are known to the World obj only its master's
+    //WorldSession object we need to process all master's bot's packets.
+        for(PlayerBotMap::const_iterator itr = GetPlayerBotsBegin(); itr != GetPlayerBotsEnd(); ++itr)
+        {
+            Player *const botPlayer = itr->second;
+            WorldSession *const pBotWorldSession = botPlayer->GetSession();
+            if(botPlayer->IsBeingTeleportedFar())
+            {
+              pBotWorldSession->HandleMoveWorldportAckOpcode();
+          } else if(botPlayer->IsInWorld())
+          {
+              WorldPacket *packet;
+              while(pBotWorldSession->_recvQueue.next(packet))
+              {
+                  OpcodeHandler &opHandle = opcodeTable[packet->GetOpcode()];
+                  (pBotWorldSession->*opHandle.handler)(*packet);
+                  delete packet;
+              }
+          }
+        }
+
         ///- Cleanup socket pointer if need
         if (m_Socket && m_Socket->IsClosed())
         {
@@ -342,6 +388,25 @@ bool WorldSession::Update(uint32 diff, PacketFilter& updater)
 /// %Log the player out
 void WorldSession::LogoutPlayer(bool Save)
 {
+    if (!_player)
+    {
+        return;
+    }
+
+    if (_player->IsMounted()) _player->Unmount();
+
+     // in case it has a minion, kill it
+    if(_player->HaveBot())
+    {
+         _player->GetBot()->SetCharmerGUID(0);
+         _player->GetBot()->RemoveFromWorld();
+         _player->RemoveBot();
+    }
+
+     //Playerbot mod: log out all player bots owned by this toon
+     while(!m_playerBots.empty())
+     LogoutPlayerBot(m_playerBots.begin()->first, Save);
+
     // finish pending transfers before starting the logout
     while (_player && _player->IsBeingTeleportedFar())
         HandleMoveWorldportAckOpcode();
@@ -457,7 +522,7 @@ void WorldSession::LogoutPlayer(bool Save)
 
         // remove player from the group if he is:
         // a) in group; b) not in raid group; c) logging out normally (not being kicked or disconnected)
-        if (_player->GetGroup() && !_player->GetGroup()->isRaidGroup() && m_Socket)
+        if ((_player->GetGroup() && !_player->GetGroup()->isRaidGroup() && m_Socket) || (_player->IsPlayerbot() && _player->GetGroup()))
             _player->RemoveFromGroup();
 
         ///- Send update to group and reset stored max enchanting level
@@ -481,6 +546,7 @@ void WorldSession::LogoutPlayer(bool Save)
         _player->CleanupsBeforeDelete();
         sLog->outChar("Account: %d (IP: %s) Logout Character:[%s] (GUID: %u)", GetAccountId(), GetRemoteAddress().c_str(), _player->GetName() ,_player->GetGUIDLow());
         Map *_map = _player->GetMap();
+		uint32 guid = _player->GetGUIDLow();
         _map->Remove(_player, true);
         SetPlayer(NULL);                                    // deleted in Remove call
 
@@ -490,7 +556,9 @@ void WorldSession::LogoutPlayer(bool Save)
 
         ///- Since each account can only have one online character at any given time, ensure all characters for active account are marked as offline
         //No SQL injection as AccountId is uint32
-        CharacterDatabase.PExecute("UPDATE characters SET online = 0 WHERE account = '%u'", GetAccountId());
+        //CharacterDatabase.PExecute("UPDATE characters SET online = 0 WHERE account = '%u'",
+        //GetAccountId());
+        CharacterDatabase.PExecute("UPDATE characters SET online = 0 WHERE guid = '%u'", guid);
         sLog->outDebug("SESSION: Sent SMSG_LOGOUT_COMPLETE Message");
     }
 
@@ -592,6 +660,41 @@ void WorldSession::LoadGlobalAccountData()
     LoadAccountData(CharacterDatabase.Query(stmt), GLOBAL_CACHE_MASK);
 }
 
+//Playerbot mod: logs out a Playerbot.
+void WorldSession::LogoutPlayerBot(uint64 guid, bool Save)
+{
+    Player *pPlayerBot = GetPlayerBot(guid);
+
+    if(pPlayerBot) //log out any playbots I have
+    {
+        //if (pPlayerBot->IsMounted()) pPlayerBot->GetPlayerbotAI()->GetClassAI()->Unmount();
+
+        pPlayerBot->CombatStop();
+        if(pPlayerBot->HaveBot())
+            pPlayerBot->SetBotMustDie();
+
+        // remove from group
+        Group* m_group = pPlayerBot->GetGroup();
+        if (m_group) {
+            if (m_group->RemoveMember(pPlayerBot->GetGUID(),GROUP_REMOVEMETHOD_DEFAULT) <= 1) {
+                delete m_group;
+            }
+        }
+
+        WorldSession *pPlayerBotWorldSession = pPlayerBot->m_session;
+        m_playerBots.erase(guid); //deletes bot player ptr inside this WorldSession PlayerBotMap
+        pPlayerBotWorldSession->LogoutPlayer(Save); //this will delete the bot Player object and PlayerbotAI object
+        delete pPlayerBotWorldSession; //finally delete the bot's WorldSession
+    }
+}
+
+//Playerbot mod: Gets a player bot Player object for this WorldSession master
+Player *WorldSession::GetPlayerBot(uint64 playerGuid) const
+{
+    PlayerBotMap::const_iterator it = m_playerBots.find(playerGuid);
+    return(it == m_playerBots.end()) ? 0 : it->second;
+}
+
 void WorldSession::LoadAccountData(PreparedQueryResult result, uint32 mask)
 {
     for (uint32 i = 0; i < NUM_ACCOUNT_DATA_TYPES; ++i)
@@ -979,6 +1082,15 @@ void WorldSession::ProcessQueryCallbacks()
         m_charLoginCallback.cancel();
     }
 
+    //! HandlePlayerBotLogin
+    if (m_charBotLoginCallback.ready())
+    {
+        SQLQueryHolder* param;
+        m_charBotLoginCallback.get(param);
+        HandlePlayerBotLogin((SQLQueryHolder*)param);
+        m_charBotLoginCallback.cancel();
+    }
+
     //! HandleAddFriendOpcode
     if (m_addFriendCallback.IsReady())
     {
diff --git a/src/server/game/Server/WorldSession.h b/src/server/game/Server/WorldSession.h
index 4823d20..a9dae24 100755
--- a/src/server/game/Server/WorldSession.h
+++ b/src/server/game/Server/WorldSession.h
@@ -175,6 +175,9 @@ public:
     virtual bool Process(WorldPacket* packet);
 };
 
+//Playerbot mod
+typedef UNORDERED_MAP<uint64, Player*> PlayerBotMap;
+
 /// Player session in the World
 class WorldSession
 {
@@ -183,6 +186,14 @@ class WorldSession
         WorldSession(uint32 id, WorldSocket *sock, AccountTypes sec, uint8 expansion, time_t mute_time, LocaleConstant locale, uint32 recruiter);
         ~WorldSession();
 
+        //Playerbot mod
+        void AddPlayerBot(uint64 guid);
+        void LogoutPlayerBot(uint64 guid, bool Save);
+        Player *GetPlayerBot (uint64 guid) const;
+		PlayerBotMap m_playerBots;
+        PlayerBotMap::const_iterator GetPlayerBotsBegin() const { return m_playerBots.begin(); }
+        PlayerBotMap::const_iterator GetPlayerBotsEnd()   const { return m_playerBots.end();   }
+
         bool PlayerLoading() const { return m_playerLoading; }
         bool PlayerLogout() const { return m_playerLogout; }
         bool PlayerLogoutWithSave() const { return m_playerLogout && m_playerSave; }
@@ -373,6 +384,7 @@ class WorldSession
         void HandlePlayerLoginOpcode(WorldPacket& recvPacket);
         void HandleCharEnum(QueryResult result);
         void HandlePlayerLogin(LoginQueryHolder * holder);
+        void HandlePlayerBotLogin(SQLQueryHolder * holder);
         void HandleCharFactionOrRaceChange(WorldPacket& recv_data);
 
         // played time
@@ -875,6 +887,7 @@ class WorldSession
         QueryCallback<uint32> m_stableSwapCallback;
         QueryCallback<uint64> m_sendStabledPetCallback;
         QueryResultHolderFuture m_charLoginCallback;
+        QueryResultHolderFuture m_charBotLoginCallback;
 
     private:
         // private trade methods
diff --git a/src/server/game/Weather/Weather.cpp b/src/server/game/Weather/Weather.cpp
index b191b85..13903fe 100755
--- a/src/server/game/Weather/Weather.cpp
+++ b/src/server/game/Weather/Weather.cpp
@@ -33,7 +33,7 @@
 Weather::Weather(uint32 zone, WeatherData const* weatherChances)
     : m_zone(zone), m_weatherChances(weatherChances)
 {
-    m_timer.SetInterval(sWorld->getIntConfig(CONFIG_INTERVAL_CHANGEWEATHER));
+    m_timer.SetInterval(150000);
     m_type = WEATHER_TYPE_FINE;
     m_grade = 0;
 
diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp
index 0f99fc3..8b4ab36 100755
--- a/src/server/game/World/World.cpp
+++ b/src/server/game/World/World.cpp
@@ -1051,6 +1051,8 @@ void World::LoadConfigSettings(bool reload)
     if (m_int_configs[CONFIG_GUILD_BANK_EVENT_LOG_COUNT] > GUILD_BANKLOG_MAX_RECORDS)
         m_int_configs[CONFIG_GUILD_BANK_EVENT_LOG_COUNT] = GUILD_BANKLOG_MAX_RECORDS;
 
+    m_int_configs[CONFIG_HONOR_FROM_PLAYERBOTS] = sConfig->GetBoolDefault("Bot.HonorFromPlayerbots", false);
+
     //visibility on continents
     m_MaxVisibleDistanceOnContinents = sConfig->GetFloatDefault("Visibility.Distance.Continents", DEFAULT_VISIBILITY_DISTANCE);
     if (m_MaxVisibleDistanceOnContinents < 45*sWorld->getRate(RATE_CREATURE_AGGRO))
diff --git a/src/server/game/World/World.h b/src/server/game/World/World.h
index 87c37b0..3bf4599 100755
--- a/src/server/game/World/World.h
+++ b/src/server/game/World/World.h
@@ -91,6 +91,7 @@ enum WorldBoolConfigs
     CONFIG_ADDON_CHANNEL,
     CONFIG_ALLOW_PLAYER_COMMANDS,
     CONFIG_CLEAN_CHARACTER_DB,
+    CONFIG_HONOR_FROM_PLAYERBOTS,
     CONFIG_GRID_UNLOAD,
     CONFIG_STATS_SAVE_ONLY_ON_LOGOUT,
     CONFIG_ALLOW_TWO_SIDE_ACCOUNTS,
diff --git a/src/server/scripts/Bots/CMakeLists.txt b/src/server/scripts/Bots/CMakeLists.txt
new file mode 100644
index 0000000..f943a5e
--- /dev/null
+++ b/src/server/scripts/Bots/CMakeLists.txt
@@ -0,0 +1,26 @@
+set(scripts_STAT_SRCS
+  ${scripts_STAT_SRCS}
+  Bots/bot_ai.cpp
+  Bots/bot_druid_ai.cpp
+  Bots/bot_hunter_ai.cpp
+  Bots/bot_mage_ai.cpp
+  Bots/bot_paladin_ai.cpp
+  Bots/bot_priest_ai.cpp
+  Bots/bot_rogue_ai.cpp
+  Bots/bot_shaman_ai.cpp
+  Bots/bot_warlock_ai.cpp
+  Bots/bot_warrior_ai.cpp
+  Bots/script_bot_giver.cpp
+  Bots/bot_ai.h
+  Bots/bot_druid_ai.h
+  Bots/bot_hunter_ai.h
+  Bots/bot_mage_ai.h
+  Bots/bot_paladin_ai.h
+  Bots/bot_priest_ai.h
+  Bots/bot_rogue_ai.h
+  Bots/bot_shaman_ai.h
+  Bots/bot_warlock_ai.h
+  Bots/bot_warrior_ai.h
+)
+
+message("  -> Prepared: Bots")
diff --git a/src/server/scripts/Bots/bot_ai.cpp b/src/server/scripts/Bots/bot_ai.cpp
new file mode 100644
index 0000000..068fb6f
--- /dev/null
+++ b/src/server/scripts/Bots/bot_ai.cpp
@@ -0,0 +1,566 @@
+#include "ScriptPCH.h"
+#include "bot_ai.h"
+#include "Group.h"
+
+
+bot_ai::bot_ai(Creature *creature): ScriptedAI(creature)
+{
+    m_creature = creature;
+    prevCommandState = COMMAND_FOLLOW; // default
+}
+
+bot_ai::~bot_ai(){}
+
+bool bot_ai::CureTarget (Unit *target){ return true; }
+bool bot_ai::HealTarget (Unit *target, uint8 hp){ return true; }
+void bot_ai::BuffTarget (Unit *target) {}
+
+
+void bot_ai::BuffAndHealGroup(Player *gPlayer)
+{
+    if(m_creature->IsNonMeleeSpellCasted(true)) return; // if I'm already casting
+
+    std::list<Unit*> unitList;
+    gPlayer->GetRaidMember(unitList,30);
+    if(!unitList.empty()){
+        for (std::list<Unit*>::iterator itr = unitList.begin() ; itr!=unitList.end();++itr) {
+            Player *tPlayer = ((Player *)master)->GetObjPlayer((*itr)->GetGUID());
+            if(tPlayer == NULL) continue;
+            if(tPlayer->isDead()) continue;
+            if(m_creature->GetAreaId() != gPlayer->GetAreaId()) continue;
+            //if(tPlayer->GetGUID() == GetPlayerBot()->GetGUID()) continue;
+            //if(m_creature->GetDistance(tPlayer) > 30) continue;
+            (HealTarget(tPlayer, tPlayer->GetHealth()*100 / tPlayer->GetMaxHealth()));
+            BuffTarget(tPlayer);
+        }
+    }
+}
+
+void bot_ai::RezGroup(uint32 REZZ, Player *gPlayer)
+{
+    if (REZZ==0) return;
+    if(m_creature->IsNonMeleeSpellCasted(true)) return; // if I'm already casting
+
+    Group::MemberSlotList const &a =(gPlayer)->GetGroup()->GetMemberSlots();
+    for(Group::member_citerator itr = a.begin(); itr != a.end(); itr++)
+    {
+        Player *tPlayer = ((Player *)master)->GetObjPlayer(itr->guid);
+        if(tPlayer == NULL) continue;
+        if(tPlayer->isAlive()) continue;
+        if(m_creature->GetAreaId() != gPlayer->GetAreaId()) continue;
+        if(m_creature->GetDistance(tPlayer) > 30) continue;
+        if(tPlayer->IsNonMeleeSpellCasted(true)) continue; //someone rezzing it already
+        if(m_creature->IsNonMeleeSpellCasted(true)) continue; // if I'm already casting
+
+        doCast(tPlayer, REZZ);
+        return;
+    }
+
+}
+
+
+
+bool bot_ai::IAmDead()
+{
+    if(master <= 0 || !master->GetGroup() || ( (master->isInCombat() || m_creature->isInCombat()) && m_creature->isDead() )) return true; //You're DEAD, stop thinking.
+    return false;
+}
+
+void bot_ai::BotAttackStart(Unit *victim)
+{
+    AttackStart(victim);
+    m_creature->AttackerStateUpdate(victim);
+    m_creature->resetAttackTimer();
+    if (master->GetBotCommandState() != COMMAND_ATTACK)
+        prevCommandState = master->GetBotCommandState();
+    master->SetBotCommandState(COMMAND_ATTACK);
+}
+
+bool bot_ai::gettingAttacked(AttackerSet m_attackers)
+{
+    if(!m_attackers.empty())
+    {
+        for(AttackerSet::iterator iter = m_attackers.begin(); iter != m_attackers.end(); ++iter)
+        {
+            if(*iter && m_creature->GetDistance((*iter)) < 50 &&
+               !master->IsInRaidWith(*iter) &&
+               !master->IsInPartyWith(*iter) &&
+               //if the thing to attack is a world invisible trigger, ex Glyph in UBRS,
+               (*iter)->GetUInt32Value(UNIT_FIELD_DISPLAYID) != 11686)
+            {
+                BotAttackStart(*iter);
+                return true;
+            } //end if
+        } //end for
+    } // end if
+    return false;
+} //end gettingAttacked
+
+
+void bot_ai::ResetOrGetNextTarget()
+{
+    if (master->GetBotMustDie()) return;
+    uint64 targetGUID = 0;
+
+    // check if anyone has raid target
+    //targetGUID = getTargetWithIcon();
+    Group *group = master->GetGroup();
+    targetGUID = group->GetTargetWithIconByGroup (m_creature->GetGUID());
+
+
+    if (targetGUID && targetGUID!=master->GetGUID())
+    {
+
+        Unit * target = m_creature->GetCreature(*master, targetGUID);
+        if (target && target->isAlive() && target->IsHostileTo(master) && target->isInCombat() /*&& m_creature->IsWithinDist(target, 30)*/)
+        {
+            BotAttackStart(target);
+            return;
+        }
+    }
+
+    AttackerSet m_attackers = master->getAttackers();
+
+    //check if anyone is attacking master
+    if(gettingAttacked(m_attackers)) return;
+
+    //check if anyone is attacking me
+    m_attackers = m_creature->getAttackers();
+    if(gettingAttacked(m_attackers)) return;
+
+    //check if master has a victim
+    if(master->getVictim() && master->getVictim()->IsHostileTo(master))
+    {
+        if(m_creature->IsWithinDist(m_creature->getVictim(), 50))
+        {
+            BotAttackStart(master->getVictim());
+            return;
+        }
+    }
+
+    //lastly check a random victim, including bots, pets, etc
+    Unit *target = DoSelectLowestHpFriendly(30);
+    if(target != NULL && target->isAlive() && !target->IsHostileToPlayers())
+    {
+        m_attackers = target->getAttackers();
+        if(gettingAttacked(m_attackers)) {
+            return;
+        }
+
+    }
+
+    //if there is no one to attack, make sure we are following master
+    if(m_creature->getVictim() == NULL &&
+        m_creature->GetCharmInfo()->GetCommandState() != COMMAND_STAY &&
+        master->GetDistance(m_creature) > 20 &&
+        !master->IsBeingTeleported())
+    {
+        if (!master->isAlive())
+            master->SetBotCommandState(COMMAND_STAY);
+        else if (master->GetBotCommandState()==COMMAND_ATTACK)
+            master->SetBotCommandState(prevCommandState);
+
+
+        return;
+    }
+}
+
+std::string bot_ai::GetSpellName(uint32 spellId)
+{
+    if (spellId==0) return "";
+
+    int loc = master->GetSession()->GetSessionDbcLocale();
+    const SpellEntry *const pSpellInfo = GetSpellStore()->LookupEntry (spellId);
+    if (pSpellInfo != NULL) {
+        const std::string  name = pSpellInfo->SpellName[loc];
+        return name;
+    }
+    return "";
+} // end GetSpellName
+
+
+bool bot_ai::HasAuraIcon (Unit *unit, uint32 SpellIconID, uint64 casterGuid)
+{
+    int loc = master->GetSession()->GetSessionDbcLocale();;
+    if(unit == NULL) return false;
+    Unit *target = unit;
+    if(target->isDead()) return false;
+
+    Unit::AuraMap &vAuras = (Unit::AuraMap&)target->GetOwnedAuras();
+
+    //save the map of auras b/c it can crash if an aura goes away while looping
+    UNORDERED_MAP<uint64, Aura*> auraMap;
+    for(Unit::AuraMap::const_iterator iter = vAuras.begin(); iter!= vAuras.end(); ++iter)
+    {
+         Aura *aura = iter->second;
+        (auraMap)[iter->first] = aura;
+    }
+
+    // now search our new map
+    for(UNORDERED_MAP<uint64, Aura*>::iterator itr = auraMap.begin(); itr!= auraMap.end(); ++itr)
+    {
+        const SpellEntry *spellInfo = itr->second->GetSpellProto();
+        uint32 spelliconId = spellInfo->SpellIconID;
+//sLog->outError ("bot_ai.HasAuraICON: %s has icon %u",spellInfo->SpellName[master->GetSession()->GetSessionDbcLocale()],  spellInfo->SpellIconID);
+
+        if(spelliconId==SpellIconID)
+        {
+//sLog->outError ("bot_ai.HasAuraICON: %s has icon %u",spellInfo->SpellName[master->GetSession()->GetSessionDbcLocale()],  spellInfo->SpellIconID);
+            if(casterGuid == 0){ //don't care who casted it
+                return true;
+            } else if(casterGuid == itr->second->GetCasterGUID()){ //only if correct caster casted it
+                return true;
+            }
+        }
+    }
+    return false;
+}
+
+bool bot_ai::HasAuraName (Unit *unit, uint32 spellId, uint64 casterGuid)
+{
+    const SpellEntry *const pSpellInfo = GetSpellStore()->LookupEntry (spellId);
+
+    if(!pSpellInfo) return false;
+
+    int loc = master->GetSession()->GetSessionDbcLocale();
+    const std::string  name = pSpellInfo->SpellName[loc];
+    if(name.length() == 0) return false;
+
+    return HasAuraName(unit, name, casterGuid);
+}
+
+
+bool bot_ai::HasAuraName (Unit *unit, std::string spell, uint64 casterGuid)
+{
+    if (spell.length()==0) return false;
+
+    int loc = master->GetSession()->GetSessionDbcLocale();;
+    if(unit == NULL) return false;
+    Unit *target = unit;
+    if(target->isDead()) return false;
+
+    Unit::AuraMap &vAuras = (Unit::AuraMap&)target->GetOwnedAuras();
+
+    //save the map of auras b/c it can crash if an aura goes away while looping
+    UNORDERED_MAP<uint64, Aura*> auraMap;
+    for(Unit::AuraMap::const_iterator iter = vAuras.begin(); iter!= vAuras.end(); ++iter)
+    {
+         Aura *aura = iter->second;
+        (auraMap)[iter->first] = aura;
+    }
+
+    // now search our new map
+    for(UNORDERED_MAP<uint64, Aura*>::iterator itr = auraMap.begin(); itr!= auraMap.end(); ++itr)
+    {
+        const SpellEntry *spellInfo = itr->second->GetSpellProto();
+        const std::string name = spellInfo->SpellName[loc];
+        if(!spell.compare(name))
+        {
+            if(casterGuid == 0){ //don't care who casted it
+                return true;
+            } else if(casterGuid == itr->second->GetCasterGUID()){ //only if correct caster casted it
+                return true;
+            }
+        }
+    }
+
+    return false;
+}
+
+bool bot_ai::listAuras (Unit *unit)
+{
+    int loc = 0;
+    Unit *target = unit;
+    sLog->outError ("ListAuras for %s", unit->GetName());
+    Unit::AuraMap &vAuras = (Unit::AuraMap&)target->GetOwnedAuras();
+    for(Unit::AuraMap::const_iterator itr = vAuras.begin(); itr!=vAuras.end(); ++itr)
+    {
+        SpellEntry const *spellInfo = itr->second->GetSpellProto();
+        const std::string name = spellInfo->SpellName[loc];
+        sLog->outError("aura = %u %s", spellInfo->Id, name.c_str());
+    }
+    return false;
+}
+
+void bot_ai::doCast(Unit *victim, uint32 spellId, bool triggered)
+{
+    if(spellId == 0) return;
+    if (!isTimerReady(GC_Timer)) return;
+    if (m_creature->IsNonMeleeSpellCasted(true)) return;
+
+    m_creature->SetStandState(UNIT_STAND_STATE_STAND);
+    GC_Timer = 20;
+    DoCast(victim, spellId, triggered);
+} //end doCast
+
+
+bool bot_ai::isTimerReady (int32 timer)
+{
+    if(timer <= 0 && GC_Timer <= 0) return true;
+    else                            return false;
+} //end isTimerReady
+
+
+//Since a lot of spell's mana requirement is calculated
+//from the base mana, it will be wrong for NPCs because base mana
+//is also total mana.  So it uses up too much mana.  So just
+//arbitrary give 25% mana back.
+//TODO: calculate the correct cost based on the spell used
+void bot_ai::GiveManaBack(uint8 amount)
+{
+    uint32 maxPower =  m_creature->GetMaxPower(POWER_MANA);
+    uint32 x =  m_creature->GetPower(POWER_MANA) + maxPower*amount/100;
+    m_creature->SetPower(POWER_MANA, x>maxPower ? maxPower : x);
+}
+
+void bot_ai::CureGroup (Unit *pTarget)
+{
+    Group::MemberSlotList const &a =((Player*)pTarget)->GetGroup()->GetMemberSlots();
+    for(Group::member_citerator itr = a.begin(); itr != a.end(); itr++)
+    {
+        Player *tPlayer = ((Player *)master)->GetObjPlayer(itr->guid);
+        if(tPlayer == NULL) continue;
+        if(tPlayer->isDead()) continue;
+        if(m_creature->GetDistance(tPlayer) > 25) continue;
+        (CureTarget(tPlayer));
+    }
+
+}
+
+void bot_ai::Feast()
+{
+    uint8 myClass = m_creature->getClass();
+
+    //if low on mana, take a drink (only check for classes with custom AI)
+    //because they are the only ones currently using mana
+    if(myClass == CLASS_SHAMAN || myClass == CLASS_DRUID ||
+        myClass == CLASS_PRIEST || myClass == CLASS_MAGE ||
+        myClass == CLASS_WARLOCK || myClass == CLASS_PALADIN)
+    {
+        if(m_creature->GetPower(POWER_MANA)*100/m_creature->GetMaxPower(POWER_MANA) < 80 && !m_creature->HasAura(1137) && master->GetBotMustWaitForSpell3() <= 0 && !m_creature->isInCombat())
+        {
+            m_creature->CastSpell(m_creature, 1137, true);
+            master->SetBotMustWaitForSpell3(1000);
+            m_creature->SetStandState(1);
+//            m_creatureTimer = 5000; //set longer delay so it wont stand up right away
+            return;
+        }
+    }
+    //if drinking, have to fake mana regen because charmed NPCs
+    //do not regen mana
+    if(m_creature->HasAura(1137))
+    {
+        uint32 addvalue = 0;
+        uint32 maxValue = m_creature->GetMaxPower(POWER_MANA);
+        uint32 curValue = m_creature->GetPower(POWER_MANA);
+
+        if(curValue <= maxValue)
+        {
+            addvalue = maxValue/20;
+            m_creature->ModifyPower(POWER_MANA, addvalue);
+            //return;
+        }
+    }
+    if(m_creature->HasAura(1137) && m_creature->GetPower(POWER_MANA) >= m_creature->GetMaxPower(POWER_MANA))
+        m_creature->RemoveAurasDueToSpell(1137);
+
+    //eat
+    if(m_creature->GetHealth()*100 / m_creature->GetMaxHealth() < 80 && !m_creature->HasAura(10256) && master->GetBotMustWaitForSpell3() <= 0 && !m_creature->isInCombat())
+    {
+        master->SetBotMustWaitForSpell3(1000);
+        m_creature->CastSpell(m_creature, 10256, true);
+        m_creature->SetStandState(1);
+//        m_creatureTimer = 5000; //set longer delay so it wont stand up right away
+        return;
+    }
+
+    //if eating, have to fake regen because charmed NPCs
+    //do not regen
+    if(m_creature->HasAura(10256))
+    {
+        uint32 addvalue = 0;
+        uint32 maxValue = m_creature->GetMaxHealth();
+        uint32 curValue = m_creature->GetHealth();
+
+        if(curValue <= maxValue)
+        {
+            addvalue = maxValue/20;
+            m_creature->SetHealth(curValue + addvalue);
+            //return;
+        }
+    }
+
+    if(m_creature->GetHealth() == m_creature->GetMaxHealth() && m_creature->HasAura(10256))
+        m_creature->RemoveAurasDueToSpell(10256);
+} // end Feast
+
+void bot_ai::setStats(uint32 myclass, uint32 myrace, uint32 mylevel) {
+    if (myrace==0) return;
+
+    //sLog->outError ("bot_ai::setStats");
+    //DATABASE STATS
+    PlayerLevelInfo info;
+    master->GetBotLevelInfo(myrace,myclass,mylevel,&info);
+
+    PlayerClassLevelInfo classInfo;
+    // master->GetBotClassLevelInfo(m_creature->getClass(),m_creature->getLevel(),&classInfo);
+
+    m_creature->SetLevel(mylevel);
+    UnitMods unitMod = UNIT_MOD_ATTACK_POWER;
+
+
+    //player -> m_creature->UpdateAllStats
+    for (int i = STAT_STRENGTH; i < MAX_STATS; i++)
+    {
+        //float value = m_creature->GetTotalStatValue(Stats(i));
+        //m_creature->SetStat(Stats(i), (int32)value);
+        m_creature->SetStat(Stats(i), info.stats[i]);
+    }
+
+    m_creature->UpdateAttackPowerAndDamage();
+    float val2 = 0.0f;
+    float level = float(mylevel);
+
+
+    uint16 index = UNIT_FIELD_ATTACK_POWER;
+    uint16 index_mod = UNIT_FIELD_ATTACK_POWER_MODS;
+    uint16 index_mult = UNIT_FIELD_ATTACK_POWER_MULTIPLIER;
+
+    float mLevelMult = 0.0;
+    if (level>=40) mLevelMult = 1.5;
+    if (level>=50) mLevelMult = 2.0;
+    if (level>=60) mLevelMult = 3.0;
+    if (level>=70) mLevelMult = 4.0;
+    if (level>=80) mLevelMult = 5.0;
+
+    switch(myclass)
+    {
+    case CLASS_WARRIOR:       val2 = level * mLevelMult*4.0f + m_creature->GetStat(STAT_STRENGTH)*2.0f                   ; break;
+    case CLASS_DEATH_KNIGHT:  val2 = level * mLevelMult*4.0f + m_creature->GetStat(STAT_STRENGTH)*2.0f                   ; break;
+    case CLASS_PALADIN:       val2 = level * mLevelMult*3.0f + m_creature->GetStat(STAT_STRENGTH)*2.0f                    - 20.0f; break;
+    case CLASS_ROGUE:         val2 = level * mLevelMult*6.0f + m_creature->GetStat(STAT_STRENGTH) + m_creature->GetStat(STAT_AGILITY); break;
+    case CLASS_HUNTER:        val2 = level * mLevelMult*2.0f + m_creature->GetStat(STAT_STRENGTH) + m_creature->GetStat(STAT_AGILITY) - 20.0f; break;
+    case CLASS_SHAMAN:        val2 = level * mLevelMult*2.0f + m_creature->GetStat(STAT_STRENGTH)*2.0f                    - 20.0f; break;
+    case CLASS_DRUID:         val2 = m_creature->GetStat(STAT_STRENGTH)*2.0f - 20.0f; break;
+    case CLASS_MAGE:          val2 =              m_creature->GetStat(STAT_STRENGTH)  - 10.0f; break;
+    case CLASS_PRIEST:        val2 =              m_creature->GetStat(STAT_STRENGTH)  - 10.0f; break;
+    case CLASS_WARLOCK:       val2 =              m_creature->GetStat(STAT_STRENGTH)  - 10.0f; break;
+    }
+
+//sLog->outError ("val2 = %f", val2);
+//sLog->outError ("str = %f", m_creature->GetStat(STAT_STRENGTH));
+    //CreatureInfo const *cinfo = sObjectMgr->GetCreatureTemplate(m_creature->GetEntry());
+    //m_creature->SetBaseWeaponDamage(BASE_ATTACK, MAXDAMAGE, cinfo->maxdmg/mylevel);
+
+    //Custom weapon system
+    val2 = abs(val2 + (val2 * (level/80)));
+   // sLog->outError ("custom val2 = %f", val2);
+    m_creature->SetModifierValue(unitMod, BASE_VALUE, val2);
+    float base_attPower  = m_creature->GetModifierValue(unitMod, BASE_VALUE) * m_creature->GetModifierValue(unitMod, BASE_PCT) * mLevelMult;
+
+    float attPowerMod = m_creature->GetModifierValue(unitMod, TOTAL_VALUE);
+
+    float attPowerMultiplier = m_creature->GetModifierValue(unitMod, TOTAL_PCT) - 1.0f;
+    attPowerMod = attPowerMod ?  attPowerMod : 1;
+    attPowerMultiplier = attPowerMultiplier ?  attPowerMultiplier : 0.1f;
+
+    m_creature->SetUInt32Value(UNIT_FIELD_ATTACK_POWER, (uint32)base_attPower);           //UNIT_FIELD_(RANGED)_ATTACK_POWER field
+    m_creature->SetUInt32Value(UNIT_FIELD_ATTACK_POWER_MODS, (uint32)attPowerMod);         //UNIT_FIELD_(RANGED)_ATTACK_POWER_MODS field
+    m_creature->SetFloatValue(UNIT_FIELD_ATTACK_POWER_MULTIPLIER, attPowerMultiplier);          //UNIT_FIELD_(RANGED)_ATTACK_POWER_MULTIPLIER field
+
+    m_creature->UpdateDamagePhysical(BASE_ATTACK);
+
+    //sLog->outError ("udpating BASE ATTACK");
+    //sLog->outError ("mLevelMult = %f", mLevelMult);
+    //sLog->outError ("bot_ai - base_attPower = %f", base_attPower);
+    //sLog->outError ("actpower multiplier = %f", attPowerMultiplier);
+    //sLog->outError ("actpower val2 = %f", val2);
+    //sLog->outError ("\tmin damage = %f", uint32(val2 * attPowerMultiplier)+level);
+    //sLog->outError ("\tmax damange = %f",uint32(val2 * attPowerMultiplier)*2+level);
+    //sLog->outError ("\tmax damange 2 = %f",uint32(val2 * attPowerMultiplier)*mLevelMult);
+    m_creature->SetBaseWeaponDamage(BASE_ATTACK, MINDAMAGE, uint32(val2 * attPowerMultiplier)+level);
+    m_creature->SetBaseWeaponDamage(BASE_ATTACK, MAXDAMAGE, uint32(val2 * attPowerMultiplier)*2+level);
+
+    m_creature->UpdateDamagePhysical(BASE_ATTACK);
+    m_creature->SetFloatValue(UNIT_FIELD_MINRANGEDDAMAGE,uint32(val2 * attPowerMultiplier)+level);
+    m_creature->SetFloatValue(UNIT_FIELD_MAXRANGEDDAMAGE,uint32(val2 * attPowerMultiplier)*2+level);
+//sLog->outError ("\t%s - min damage after setting it = %f", m_creature->GetName(), m_creature->GetFloatValue(UNIT_FIELD_MINDAMAGE));
+//sLog->outError ("\t%s - MINDAMAGE after setting it = %f", m_creature->GetName(), m_creature->GetWeaponDamageRange(BASE_ATTACK, MINDAMAGE));
+
+
+    m_creature->SetModifierValue(UNIT_MOD_ATTACK_POWER, BASE_VALUE, uint32(val2 * attPowerMultiplier));
+    //player -> m_creature->UpdateAttackPowerAndDamage(true); // ---> STUPID CREATURE CAN NOT BE RANGED
+
+
+    float value = 0.0f;
+    unitMod = UNIT_MOD_ARMOR;
+
+    value  = m_creature->GetModifierValue(unitMod, BASE_VALUE);         // base armor (from items)
+    value *= m_creature->GetModifierValue(unitMod, BASE_PCT);           // armor percent from items
+    value += m_creature->GetStat(STAT_AGILITY) * 2.0f;                  // armor bonus from stats
+    value += m_creature->GetModifierValue(unitMod, TOTAL_VALUE);
+
+    value *= m_creature->GetModifierValue(unitMod, TOTAL_PCT);
+    m_creature->UpdateAttackPowerAndDamage();
+
+    //Custom armor system
+    value = value + (value * (level/100));
+
+   // m_creature->SetArmor(int32(value));
+    m_creature->SetModifierValue(UNIT_MOD_ARMOR, BASE_VALUE, value);
+    //m_creature->UpdateArmor();
+
+}
+
+void bot_ai::ReceiveEmote(Player *player, uint32 emote)
+{
+    if (emote == TEXTEMOTE_DANCE)
+        DoSay("I love to dance!",LANG_UNIVERSAL,m_creature->GetGUID(), player);
+
+    // debug to see what auras are on bot
+    if (emote == TEXTEMOTE_BONK)
+        listAuras(m_creature);
+
+    // debug to see what auras are on me
+    if (emote == TEXTEMOTE_SALUTE)
+        listAuras(player);
+
+    if (emote == TEXTEMOTE_STAND)
+    {
+        if (m_creature->GetCharmerGUID() != player->GetGUID())
+        {
+            m_creature->HandleEmoteCommand(TEXTEMOTE_RUDE);
+            return;
+        }
+        player->SetBotCommandState (COMMAND_STAY);
+        DoSay("Standing Still.",LANG_UNIVERSAL,m_creature->GetGUID(), player);
+    }
+
+    if (emote == TEXTEMOTE_WAVE)
+    {
+        if (m_creature->GetCharmerGUID() != player->GetGUID())
+        {
+            m_creature->HandleEmoteCommand(TEXTEMOTE_RUDE);
+            return;
+        }
+        player->SetBotCommandState (COMMAND_FOLLOW);
+        DoSay("Following!",LANG_UNIVERSAL,m_creature->GetGUID(), player);
+    }
+
+    // buff the requester
+    if (emote == TEXTEMOTE_BOW)
+    {
+        ReceiveBowEmote(player);
+    }
+
+} // end ReceiveEmote
+
+void bot_ai::ReceiveBowEmote(Player *player)
+{
+       // DoSay("I have no buffs for you!",LANG_UNIVERSAL,m_creature->GetGUID(), player);
+}
+
+void bot_ai::DoSay(const std::string& text, const uint32 language,uint64 receiver,Player *player)
+{
+
+}
diff --git a/src/server/scripts/Bots/bot_ai.h b/src/server/scripts/Bots/bot_ai.h
new file mode 100644
index 0000000..35a7e07
--- /dev/null
+++ b/src/server/scripts/Bots/bot_ai.h
@@ -0,0 +1,88 @@
+#ifndef _BOT_AI_H
+#define _BOT_AI_H
+
+#define SPELL_LEVEL ((int)m_creature->getLevel()/10)
+
+#define BANDAGE 27031
+#define MANAPOTION 28499
+#define REJUVEPOTION 28517
+#define HEALINGPOTION 28495
+#define POTIONCD 60000
+#define FIRSTAID 60000
+#define DRINK 1137
+
+#define master ((Player*)m_creature->GetCharmer())
+
+typedef std::set<Unit *> AttackerSet;
+
+class bot_ai : public ScriptedAI
+{
+    public:
+        bot_ai(Creature *c);
+
+        virtual ~bot_ai();
+
+        //Cure the target
+        virtual bool CureTarget (Unit *target);
+
+        //Heal the target
+        virtual bool HealTarget (Unit *target, uint8 hp);
+
+        // Buff target
+        virtual void BuffTarget(Unit *target);
+
+        virtual void ReceiveBowEmote(Player *player);
+
+
+        // Cycles through the group to heal/buff/rezz
+        void BuffAndHealGroup(Player *gPlayer);
+        void RezGroup(uint32 REZZ, Player *gPlayer);
+
+        //Debug method to list the auras currently active.
+        //Use to find what spells were casted
+        bool listAuras(Unit *unit);
+
+        //More generalized method than HasAura().  It looks for
+        //any rank of the spell and it doesn't care which
+        //spell effect you want.  If it has the spell aura than
+        //it returns true
+        bool HasAuraName(Unit *unit, std::string spell, uint64 casterGuid=0);
+        bool HasAuraName(Unit *unit, uint32 spellId, uint64 casterGuid=0);
+        bool HasAuraIcon (Unit *unit, uint32 SpellIconID, uint64 casterGuid=0);
+
+        std::string GetSpellName(uint32 spell_id);
+
+        void doCast(Unit *victim, uint32 spellId, bool triggered = false);
+        bool isTimerReady(int32 timer);
+
+        void ResetOrGetNextTarget();
+
+        void ReceiveEmote(Player *player, uint32 emote);
+
+        void DoSay(const std::string& text, const uint32 language,uint64 receiver,Player *player);
+
+        bool IAmDead();
+
+        void GiveManaBack(uint8 amount=25);
+
+        void CureGroup(Unit *pTarget);
+
+        void Feast();
+
+        void BotAttackStart(Unit *victim);
+
+        Creature *m_creature;
+
+        int32 GC_Timer; //global cooldown
+        uint32 FirstAid_cd;
+        uint32 Potion_cd;
+
+    private:
+        bool gettingAttacked(AttackerSet m_attackers);
+
+        CommandStates prevCommandState;
+
+    protected:
+        void setStats(uint32 myclass, uint32 myrace, uint32 mylevel);
+};
+#endif
diff --git a/src/server/scripts/Bots/bot_druid_ai.cpp b/src/server/scripts/Bots/bot_druid_ai.cpp
new file mode 100644
index 0000000..0f65931
--- /dev/null
+++ b/src/server/scripts/Bots/bot_druid_ai.cpp
@@ -0,0 +1,563 @@
+#include "ScriptPCH.h"
+#include "bot_druid_ai.h"
+#include "Group.h"
+
+class druid_bot : public CreatureScript
+{
+public:
+    druid_bot() : CreatureScript("druid_bot") { }
+
+    CreatureAI *GetAI(Creature *pCreature) const
+    {
+        return new bot_druid_ai(pCreature);
+    }
+
+	struct bot_druid_ai : public bot_ai
+	{
+		bot_druid_ai(Creature *c) : bot_ai(c)
+		{
+			Reset();
+		}
+
+		int32 GC_Timer; //global cooldown
+		int32 Heal_Timer;
+		int32 Regrowth_Timer;
+		int32 Self_Regrowth_Timer;
+		int32 Self_Rejuvenation_Timer;
+		int32 Self_Heal_Timer;
+		int32 Rejuvenation_Timer;
+		int32 Others_Heal_Timer;
+		int32 Oom_timer;
+		int32 Fade_Timer;
+		int32 Potion_Timer;
+		int32 Warstomp_Timer;
+		int32 Rez_Timer;
+
+		int32 Noggenfogger_Timer;
+
+		//Bear Timers
+		int32 Demoralizing_Roar_Timer;
+		int32 Swipe_Timer;
+
+		//Cat Timers
+		int32 Claw_Timer;
+		int32 Rake_Timer;
+		int32 Shred_Timer;
+		int32 Rip_Timer;
+		int32 Mangle_Cat_Timer;
+
+		//Balance Timers
+		int32 Moonfire_Timer;
+		int32 Starfire_Timer;
+		int32 Wrath_Timer;
+		int32 Fairie_Fire_Timer;
+
+		Unit *mobsTarget;
+		Unit *opponent;
+
+		void Reset()
+		{
+			GC_Timer = 0;
+			Potion_Timer = 0;
+			Heal_Timer = 0;
+			Regrowth_Timer = 0;
+			Self_Regrowth_Timer = 0;
+			Self_Rejuvenation_Timer = 0;
+			Self_Heal_Timer = 0;
+			Rejuvenation_Timer = 0;
+			Rez_Timer = 0;
+
+			Others_Heal_Timer = 0;
+			Oom_timer = 0;
+
+			Warstomp_Timer = 0;
+			Demoralizing_Roar_Timer = 10;
+			Swipe_Timer = 20;
+			Noggenfogger_Timer = 0;
+
+			Claw_Timer = 0;
+			Rake_Timer = 0;
+			Shred_Timer = 0;
+			Rip_Timer = 0;
+			Mangle_Cat_Timer = 0;
+
+			Moonfire_Timer = 30;
+			Starfire_Timer = 90;
+			Wrath_Timer = 150;
+			Fairie_Fire_Timer = 10;
+
+			opponent = NULL;
+
+			if (master) {
+				setStats(CLASS_DRUID, m_creature->getRace(), master->getLevel());
+		   }
+		}
+
+		void Aggro(Unit *who){}
+
+		void EnterEvadeMode(){}
+
+		void KilledUnit(Unit *){ master->SetBotCommandState(COMMAND_FOLLOW); }
+
+		//try to do a warstomp every time I get out of animal form
+		void warstomp(const uint32 diff)
+		{
+			if(master->GetBotRace() != RACE_TAUREN) return;
+
+			if(Warstomp_Timer <= 0)
+			{
+				if(opponent != NULL)
+				{
+					Warstomp_Timer = 1200; //2 minutes
+					doCast(opponent, SPELL_WARSTOMP, true);
+				}
+			}
+			else if(Warstomp_Timer >= 0)
+				--Warstomp_Timer;
+		} //end warstomp
+
+		void removeFeralForm(Player *m=0)
+		{
+			if(!m) m=master; //if m is set than use it, else use master
+
+			if(m_creature->HasAura(SPELL_BEAR_FORM, 0))
+			{
+				m_creature->RemoveAurasDueToSpell(SPELL_BEAR_FORM);
+				m_creature->RemoveAurasDueToSpell(SPELL_BEAR_FORM_MOD);
+				m->SetBotMustWaitForSpell1(3000);
+			}
+
+			if(m_creature->HasAura(SPELL_CAT_FORM, 0))
+			{
+				m_creature->RemoveAurasDueToSpell(SPELL_CAT_FORM);
+				m_creature->RemoveAurasDueToSpell(SPELL_CAT_FORM_MOD);
+				m->SetBotMustWaitForSpell1(3000);
+			}
+
+			m_creature->setPowerType(POWER_MANA);
+
+		} //end removeFeralForm
+
+		bool isTimerReady(int32 timer)
+		{
+			if(timer <= 0 && GC_Timer <= 0) return true;
+			else                            return false;
+		}
+
+		void decrementTimers()
+		{
+			if(GC_Timer >= 0)                --GC_Timer;
+			if(Demoralizing_Roar_Timer >= 0) --Demoralizing_Roar_Timer;
+			if(Swipe_Timer >= 0)             --Swipe_Timer;
+			if(Claw_Timer >= 0)              --Claw_Timer;
+			if(Rake_Timer >= 0)              --Rake_Timer;
+			if(Shred_Timer >= 0)             --Shred_Timer;
+			if(Rip_Timer >= 0)               --Rip_Timer;
+			if(Mangle_Cat_Timer >= 0)        --Mangle_Cat_Timer;
+			if(Moonfire_Timer >= 0)          --Moonfire_Timer;
+			if(Starfire_Timer >= 0)          --Starfire_Timer;
+			if(Wrath_Timer >= 0)             --Wrath_Timer;
+			if(Rip_Timer >= 0)               --Fairie_Fire_Timer;
+			if(Potion_Timer >= 0)            --Potion_Timer;
+			if(Rejuvenation_Timer >= 0)      --Rejuvenation_Timer;
+			if(Regrowth_Timer >= 0)          --Regrowth_Timer;
+			if(Heal_Timer > 0)               --Heal_Timer;
+			if(Self_Regrowth_Timer >= 0)     --Self_Regrowth_Timer;
+			if(Self_Rejuvenation_Timer >= 0) --Self_Rejuvenation_Timer;
+			if(Self_Heal_Timer >= 0)         --Self_Heal_Timer;
+			if(Others_Heal_Timer >= 0)       --Others_Heal_Timer;
+			if(Rez_Timer >= 0)               --Rez_Timer;
+			if(Noggenfogger_Timer >= 0)      --Noggenfogger_Timer;
+
+		}
+
+		void doCast(Unit *victim, uint32 spellId, bool triggered = false)
+		{
+			if(spellId == 0) return;
+
+			GC_Timer = 20;
+			m_creature->SetStandState(UNIT_STAND_STATE_STAND);
+			DoCast(victim, spellId, triggered);
+		} //end doCast
+
+		void doBearActions(const uint32 diff)
+		{
+			m_creature->setPowerType(POWER_RAGE);
+
+			if(isTimerReady(Demoralizing_Roar_Timer) && opponent != NULL)
+			{
+				Demoralizing_Roar_Timer = 150;
+				doCast(opponent, SPELL_DEMORALIZING_ROAR, true);
+			}
+
+			if(isTimerReady(Swipe_Timer) && opponent != NULL)
+			{
+				Swipe_Timer = 50;
+				doCast(opponent, SPELL_SWIPE, true);
+			}
+		}//end doBearActions
+
+
+
+		void doCatActions(/*Player *master, Creature *m_creature,*/ const uint32 diff)
+		{
+
+			m_creature->SetPower(POWER_ENERGY, 100);
+
+			if(isTimerReady(Claw_Timer) && opponent != NULL)
+			{
+				Claw_Timer = 70;
+				doCast(opponent, SPELL_CLAW, true);
+			}
+
+			if(isTimerReady(Mangle_Cat_Timer) && opponent != NULL)
+			{
+				Mangle_Cat_Timer = 70;
+				doCast(opponent, SPELL_MANGLE_CAT, true);
+			}
+
+			if(isTimerReady(Rake_Timer) && opponent != NULL)
+			{
+				Rake_Timer = 100;
+				doCast(opponent, SPELL_RAKE, true);
+			}
+
+			if(isTimerReady(Shred_Timer) && opponent != NULL)
+			{
+				Shred_Timer = 120;
+				doCast(opponent, SPELL_SHRED, true);
+			}
+
+			if(isTimerReady(Rip_Timer) && opponent != NULL)
+			{
+				Rip_Timer = 150;
+				doCast(opponent, SPELL_RIP, true);
+			}
+		} //end doCatActions
+
+
+		void doBalanceActions(const uint32 diff)
+		{
+			removeFeralForm();
+			m_creature->setPowerType (POWER_MANA);
+
+			if(isTimerReady(Moonfire_Timer) && opponent != NULL)
+			{
+				Moonfire_Timer = 150;
+				doCast(opponent, SPELL_MOONFIRE, true);
+			}
+
+			if(isTimerReady(Starfire_Timer) && opponent != NULL)
+			{
+				Starfire_Timer = 200;
+				doCast(opponent, SPELL_STARFIRE, true);
+			}
+
+			if(isTimerReady(Wrath_Timer) && opponent != NULL)
+			{
+				Wrath_Timer = 180;
+				doCast(opponent, SPELL_WRATH, true);
+			}
+
+			if(isTimerReady(Fairie_Fire_Timer) && opponent != NULL)
+			{
+				Fairie_Fire_Timer = 200;
+				doCast(opponent, SPELL_FAIRIE_FIRE, true);
+			}
+		} //end doBalanceActions
+
+
+		bool CureTarget(Unit *target)
+		{
+			if (HasAuraIcon(target, 68 /*"Venom Spit"*/)
+				//|| HasAuraName(target, "Venom Spit")
+				//|| HasAuraName(target, "Poison"))
+				&& !HasAuraName(target, SPELL_CURE_POISON))
+			{
+				removeFeralForm();
+				doCast(target, SPELL_CURE_POISON);
+			}
+			return true;
+		}
+
+		void UpdateAI(const uint32 diff)
+		{
+			decrementTimers();
+
+			if(IAmDead()) return;
+
+			if(m_creature->HasUnitState(UNIT_STAT_CASTING))
+				return;
+
+			//self buff
+			if(!m_creature->isInCombat())
+			{
+				if(!m_creature->HasAura(SPELL_THORNS))
+				{
+					removeFeralForm();
+					doCast(m_creature, SPELL_THORNS, true);
+				}
+			}
+
+			if(m_creature->GetPower(POWER_MANA) < 400 && isTimerReady(Potion_Timer))
+			{
+				 doCast(m_creature, REJUVEPOTION, true);
+				 //m_creature->MonsterSay("MANA POTION", LANG_UNIVERSAL, NULL);
+				 Potion_Timer = 150;
+				 Oom_timer = 0;
+			}
+			if(m_creature->GetPower(POWER_MANA)/m_creature->GetMaxPower(POWER_MANA) < 10)
+			{
+				if(Oom_timer == 0)
+				{
+					//m_creature->MonsterSay("OOM", LANG_UNIVERSAL, NULL);
+					Oom_timer = 1;
+				}
+			}
+
+			//Heal master
+			if((master->GetHealth()*100 / master->GetMaxHealth() < 90) && isTimerReady(Rejuvenation_Timer) && master->isAlive())
+			{
+				removeFeralForm();
+				warstomp(diff);
+				doCast(master, SPELL_REJUVENATION, true);
+				Rejuvenation_Timer = 300;
+				Heal_Timer = Heal_Timer + 30; //wait 3 seconds before casting a real heal
+				Regrowth_Timer = Regrowth_Timer + 20; //wait 2 seconds before casting a regrowth
+				//if(master->isInCombat() && master->getVictim() == NULL) return;
+				return;
+			}
+
+			if((master->GetHealth()*100 / master->GetMaxHealth() < 90) && isTimerReady(Regrowth_Timer) && master->isAlive())
+			{
+				removeFeralForm();
+				warstomp(diff);
+				doCast(master, SPELL_REGROWTH, true);
+				Regrowth_Timer = 200;
+				Heal_Timer = Heal_Timer + 20; //wait 2 seconds before casting a real heal
+				//if(master->isInCombat() && master->getVictim() == NULL) return;
+				return;
+			}
+
+			if((master->GetHealth()*100 / master->GetMaxHealth() < 75) && isTimerReady(Heal_Timer) && master->isAlive()
+			/* && CanCast(master, GetSpellStore()->LookupEntry(SPELL_HEALING_TOUCH))*/)
+			{
+				removeFeralForm();
+				warstomp(diff);
+				doCast(master, SPELL_HEALING_TOUCH, false);
+				Heal_Timer = 100;
+				return;
+			}
+
+			//
+			//Heal myself
+			//
+			if(m_creature->GetHealth()*100 / m_creature->GetMaxHealth() < 80 && !m_creature->HasAura(SPELL_REGROWTH, 1) && isTimerReady(Self_Regrowth_Timer))
+			{
+				removeFeralForm();
+				warstomp(diff);
+				doCast(m_creature, SPELL_REGROWTH, false);
+
+				Self_Regrowth_Timer = 120;
+				return;
+			}
+			if(m_creature->GetHealth()*100 / m_creature->GetMaxHealth() < 80 && !m_creature->HasAura(SPELL_REJUVENATION, 0) && isTimerReady(Self_Rejuvenation_Timer)
+			/* && CanCast(m_creature, GetSpellStore()->LookupEntry(SPELL_REGROWTH))*/)
+			{
+				removeFeralForm();
+				warstomp(diff);
+				doCast(m_creature, SPELL_REJUVENATION, false);
+
+				Self_Rejuvenation_Timer = 120;
+				return;
+			}
+			if((m_creature->GetHealth()*100 / m_creature->GetMaxHealth() < 75) && isTimerReady(Self_Heal_Timer)
+			/* && CanCast(master, GetSpellStore()->LookupEntry(SPELL_HEALING_TOUCH))*/)
+			{
+				removeFeralForm();
+				warstomp(diff);
+
+				doCast(m_creature, SPELL_HEALING_TOUCH, false);
+				Self_Heal_Timer = 100;
+				return;
+			}
+
+
+			//
+			//Heal others
+			//
+			//check group members, this doesn't check bots/pets.  They will be done later.  Preference
+			//goes to real players first.
+			//
+			Group::MemberSlotList const &a =((Player*)master)->GetGroup()->GetMemberSlots();
+			for(Group::member_citerator itr = a.begin(); itr != a.end(); itr++)
+			{
+				Player *tPlayer = ((Player *)master)->GetObjPlayer(itr->guid);
+				if(tPlayer == NULL) continue;
+
+				//healing others
+				if(tPlayer->isAlive() && isTimerReady(Others_Heal_Timer) && tPlayer->GetHealth()*100 / tPlayer->GetMaxHealth() < 75)
+				{
+					//sLog->outError("Druid_Bot: healing someoneelse: %s it has %d HP", tPlayer->GetName(), tPlayer->GetHealth());
+					doCast(tPlayer, SPELL_HEALING_TOUCH, false);
+					Others_Heal_Timer = 250;
+				}
+
+				//rezzes
+				if(tPlayer->isDead() && m_creature->getLevel() >= 20 &&
+				//CanCast(tPlayer, GetSpellStore()->LookupEntry(SPELL_REBIRTH)) &&
+				m_creature->GetDistance(tPlayer) < 40 && (isTimerReady(Rez_Timer)))
+				{
+					char *str = (char *)malloc(32);
+					sprintf(str, "Rezzing %s", tPlayer->GetName());
+					m_creature->MonsterSay(str, LANG_UNIVERSAL, NULL);
+					free(str);
+					doCast(tPlayer, SPELL_REBIRTH, false);
+					Rez_Timer = 1600;
+				}
+
+				//buff group
+				if(tPlayer->isAlive())
+				{
+					if(!HasAuraName(tPlayer, GetSpellName(SPELL_MARK_OF_THE_WILD)) && !HasAuraName(tPlayer, "Gift of the Wild") && isTimerReady(GC_Timer))
+					{
+						removeFeralForm();
+						doCast(tPlayer, SPELL_MARK_OF_THE_WILD);
+					}
+
+					if(!HasAuraName(tPlayer, SPELL_THORNS) && isTimerReady(GC_Timer))
+					{
+						removeFeralForm();
+						doCast(tPlayer, SPELL_THORNS);
+					}
+				}
+			}
+
+			if(isTimerReady(Noggenfogger_Timer))
+			{
+				uint64 m_rand = urand(1, 2);
+				switch(m_rand)
+				{
+					case 1:
+						doCast(m_creature, SPELL_NOGGENFOGGER_SKELETON, true);
+						break;
+					case 2:
+						// Don't change forms b/c it crashes in GetModelForForm().  It checks
+						// PLAYER_BYTES and since its an npc it does not have this value.
+						//doCast(m_creature, SPELL_NOGGENFOGGER_SMALL, true);
+						break;
+				}
+				Noggenfogger_Timer = 6000; //10 minutes
+			}
+
+			//The rest is combat stuff, so if not in combat just return
+			opponent = SelectUnit(SELECT_TARGET_TOPAGGRO, 0);
+			if(!opponent && !m_creature->getVictim())
+			{
+				CureGroup(master);
+				ResetOrGetNextTarget();
+				return;
+			}
+
+			//default value
+			float val2 = m_creature->GetStat(STAT_STRENGTH)*2.0f;
+
+			//only go Feral if we are "small" from the noggenfogger spell
+			if(m_creature->HasAura(SPELL_NOGGENFOGGER_SMALL))
+			{
+				//if the target is attacking us, we want to go bear
+				if(opponent && opponent->getVictim() &&
+				opponent->getVictim()->GetGUID() == m_creature->GetGUID())
+				{
+					//if we don't have bear yet
+					if(!m_creature->HasAura(SPELL_BEAR_FORM))
+					{
+						m_creature->RemoveAurasDueToSpell(SPELL_CAT_FORM); //remove cat
+						m_creature->RemoveAurasDueToSpell(SPELL_CAT_FORM_MOD);
+						doCast(m_creature, SPELL_BEAR_FORM, true);
+						doCast(m_creature, SPELL_BEAR_FORM_MOD, true);
+						master->SetBotMustWaitForSpell1(3000);
+						m_creature->setPowerType(POWER_RAGE);
+						m_creature->SetMaxPower(POWER_RAGE, 1000);
+						m_creature->SetPower(POWER_RAGE, 1000);
+						val2 = m_creature->getLevel()*3.0f + m_creature->GetStat(STAT_STRENGTH)*2.0f;
+
+						//update attack power based on form
+						val2 = val2 + (val2 * (m_creature->getLevel()/50));
+						m_creature->SetModifierValue(UNIT_MOD_ATTACK_POWER, BASE_VALUE, val2);
+						m_creature->UpdateDamagePhysical(BASE_ATTACK);
+					}
+					doBearActions(diff);
+				} else {
+					//if we don't have cat yet
+					if(!m_creature->HasAura(SPELL_CAT_FORM))
+					{
+						m_creature->RemoveAurasDueToSpell(SPELL_BEAR_FORM); //remove bear
+						m_creature->RemoveAurasDueToSpell(SPELL_BEAR_FORM_MOD);
+						doCast(m_creature, SPELL_CAT_FORM, true);
+						doCast(m_creature, SPELL_CAT_FORM_MOD, true);
+						master->SetBotMustWaitForSpell1(3000);
+						m_creature->setPowerType(POWER_ENERGY);
+						m_creature->SetMaxPower(POWER_ENERGY, 1000);
+						m_creature->SetPower(POWER_ENERGY, 1000);
+						m_creature->SetSpeed(MOVE_RUN, m_creature->GetSpeed(MOVE_RUN) - 0.1f, true);
+						val2 = m_creature->getLevel()*5.0f + m_creature->GetStat(STAT_STRENGTH)*2.0f + m_creature->GetStat(STAT_AGILITY) - 20.0f;
+
+						//update attack power based on form
+						val2 = val2 + (val2 * (m_creature->getLevel()/50));
+						m_creature->SetModifierValue(UNIT_MOD_ATTACK_POWER, BASE_VALUE, val2);
+						m_creature->UpdateDamagePhysical(BASE_ATTACK);
+					}
+					doCatActions(diff);
+				}
+			//else go Balance if we are a skeleton from noggenfogger
+			} else {
+				val2 = m_creature->GetStat(STAT_STRENGTH)*2.0f;
+				val2 = val2 + (val2 * (m_creature->getLevel()/50));
+				m_creature->SetModifierValue(UNIT_MOD_ATTACK_POWER, BASE_VALUE, val2);
+				m_creature->UpdateDamagePhysical(BASE_ATTACK);
+				doBalanceActions(diff);
+			}
+
+
+
+			//now try to heal bots and pets.  DoSelectLowestHpFriendly will get
+			//everyone in group including bots and pets.  Unfortunately it can
+			//not be triggered for % of lost HP, so we just set it to -1000.
+			//This means low level players wont be healed because they wont have
+			//enough HP.
+			Unit *target = DoSelectLowestHpFriendly(40, 1000);
+			if(target && isTimerReady(Others_Heal_Timer))
+			{
+				doCast(target, SPELL_HEALING_TOUCH, false);
+				Others_Heal_Timer = 50;
+			} else {
+				target = DoSelectLowestHpFriendly(40, 500);  //now try someone with less HP lost
+				if(target && isTimerReady(Others_Heal_Timer))
+				{
+					if(!target->HasAura(SPELL_REGROWTH, 1))
+					{
+						doCast(target, SPELL_REGROWTH, false);
+						Others_Heal_Timer = 100;
+					}
+				}
+			}
+
+			ScriptedAI::UpdateAI(diff);
+		}
+		void ReceiveBowEmote(Player *player)
+		{
+			((bot_druid_ai*)m_creature->AI())->removeFeralForm((Player*) m_creature->GetCharmer());
+			((bot_druid_ai*)m_creature->AI())->doCast(player, SPELL_THORNS, true );
+			((bot_druid_ai*)m_creature->AI())->doCast(player, SPELL_MARK_OF_THE_WILD, false );
+		}
+
+	}; //end druid_bot
+
+};
+
+
+void AddSC_druid_bot()
+{
+    new druid_bot();
+}
diff --git a/src/server/scripts/Bots/bot_druid_ai.h b/src/server/scripts/Bots/bot_druid_ai.h
new file mode 100644
index 0000000..d63ea7b
--- /dev/null
+++ b/src/server/scripts/Bots/bot_druid_ai.h
@@ -0,0 +1,63 @@
+
+#include "bot_ai.h"
+
+
+#define SPELL_MARK_OF_THE_WILD      SPELL_MARK_OF_THE_WILD_A[SPELL_LEVEL]
+#define SPELL_THORNS                SPELL_THORNS_A[SPELL_LEVEL]
+#define SPELL_HEALING_TOUCH         SPELL_HEALING_TOUCH_A[SPELL_LEVEL]
+#define SPELL_REGROWTH              SPELL_REGROWTH_A[SPELL_LEVEL]
+#define SPELL_REJUVENATION          SPELL_REJUVENATION_A[SPELL_LEVEL]
+#define SPELL_REBIRTH               SPELL_REBIRTH_A[SPELL_LEVEL]
+#define SPELL_CURE_POISON           SPELL_CURE_POISON_A[SPELL_LEVEL]
+
+
+//0#define SPELL_RESURRECTION       10881    //rank 4
+#define SPELL_WARSTOMP              20549    //racial ability
+
+//FERAL SPELLS
+#define SPELL_CAT_FORM              768
+#define SPELL_CAT_FORM_MOD          3025
+#define SPELL_BEAR_FORM             9634
+#define SPELL_BEAR_FORM_MOD         1178
+
+#define SPELL_DEMORALIZING_ROAR     SPELL_DEMORALIZING_ROAR_A[SPELL_LEVEL]
+#define SPELL_SWIPE                 SPELL_SWIPE_A[SPELL_LEVEL]
+
+#define SPELL_CLAW                  SPELL_CLAW_A[SPELL_LEVEL]
+#define SPELL_RAKE                  SPELL_RAKE_A[SPELL_LEVEL]
+#define SPELL_SHRED                 SPELL_SHRED_A[SPELL_LEVEL]
+#define SPELL_RIP                   SPELL_RIP_A[SPELL_LEVEL]
+#define SPELL_MANGLE_CAT            SPELL_MANGLE_CAT_A[SPELL_LEVEL]
+
+//BALANCE SPELLS
+#define SPELL_MOONFIRE              SPELL_MOONFIRE_A[SPELL_LEVEL]
+#define SPELL_STARFIRE              SPELL_STARFIRE_A[SPELL_LEVEL]
+#define SPELL_WRATH                 SPELL_WRATH_A[SPELL_LEVEL]
+#define SPELL_FAIRIE_FIRE           SPELL_FAIRIE_FIRE_A[SPELL_LEVEL]
+
+#define SPELL_NOGGENFOGGER_SMALL    16595
+#define SPELL_NOGGENFOGGER_SKELETON 16591
+
+
+uint32 SPELL_MARK_OF_THE_WILD_A[] = { 1126, 5232, 6756, 5234, 8907, 21849, 21850, 26991, 48470 };
+uint32 SPELL_THORNS_A[] = { 467, 782, 1075, 8914, 9756, 9910, 26992, 26992, 53307, 53307 };
+uint32 SPELL_HEALING_TOUCH_A[] = { 5185, 5187, 5189, 6778, 9758, 9889, 26978, 26979, 26979 };
+uint32 SPELL_REGROWTH_A[] = { 0, 8936, 8939, 8941, 9750, 9857, 9858, 26980, 96980, 48442, 48442 };
+uint32 SPELL_REJUVENATION_A[] = { 774, 1058, 2090, 2091, 3627, 9840, 26981, 26982, 48440, 48440 };
+uint32 SPELL_REBIRTH_A[] = { 0, 0, 2006, 2010, 10880, 10881, 20770, 25435, 25435 };
+uint32 SPELL_CURE_POISON_A[] = { 0, 8946, 8946, 14253, 14253, 14253, 14253, 14253, 14253 };
+
+
+uint32 SPELL_DEMORALIZING_ROAR_A[] = { 0, 99, 1735, 9490, 9747, 9898, 26998, 26998, 48559, 48560, 48560 };
+uint32 SPELL_SWIPE_A[] = { 0, 779, 780, 769, 9754, 9908, 26997, 26997, 26997 };
+
+uint32 SPELL_CLAW_A[] = { 0, 0, 1082, 3029, 5201, 9849, 9850, 27000, 48569, 48569 };
+uint32 SPELL_RAKE_A[] = { 0, 0, 1822, 1823, 1824, 9904, 27003, 27003, 48574, 48574 };
+uint32 SPELL_SHRED_A[] = { 0, 0, 5221, 6800, 8992, 9829, 9830, 27001, 27002, 27002 };
+uint32 SPELL_RIP_A[] = { 0, 0, 1079, 9492, 9493, 9752, 9894, 9896, 27008, 27008 };
+uint32 SPELL_MANGLE_CAT_A[] = { 0, 0, 0, 0, 0, 33982, 33983, 48565, 48566, 48566 };
+
+uint32 SPELL_MOONFIRE_A[] = { 8921, 8924, 8925, 8928, 8929, 9834, 26987, 26988, 26988 };
+uint32 SPELL_STARFIRE_A[] = { 0, 0, 2912, 8950, 8951, 9875, 25298, 26986, 26986 };
+uint32 SPELL_WRATH_A[] = { 5176, 5178, 5179, 5180, 8905, 9912, 26984, 26985, 48461, 48461 };
+uint32 SPELL_FAIRIE_FIRE_A[] = { 0, 16857, 16857, 17390, 17391, 17392, 27011, 27011, 48475, 48475 };
diff --git a/src/server/scripts/Bots/bot_hunter_ai.cpp b/src/server/scripts/Bots/bot_hunter_ai.cpp
new file mode 100644
index 0000000..6f98d9e
--- /dev/null
+++ b/src/server/scripts/Bots/bot_hunter_ai.cpp
@@ -0,0 +1,303 @@
+#include "ScriptPCH.h"
+#include "bot_hunter_ai.h"
+
+class hunter_bot : public CreatureScript
+{
+public:
+    hunter_bot() : CreatureScript("hunter_bot") { }
+
+    CreatureAI *GetAI(Creature *pCreature) const
+    {
+        return new hunter_botAI(pCreature);
+    }
+
+	struct hunter_botAI : public bot_ai
+	{
+		hunter_botAI(Creature *c) :bot_ai(c)
+		{
+			Reset();
+			pet = NULL;
+		}
+
+		bool oom_spam;
+
+		Unit *opponent;
+		Creature *pet;
+
+		int32 ArcaneShot_cd;
+		int32 ChimeraShot_Timer;
+		int32 SilencingShot_Timer;
+		int32 AimedShot_Timer;
+
+		void Reset()
+		{
+			oom_spam = false;
+
+			opponent = NULL;
+
+			GC_Timer = 0;
+			ArcaneShot_cd = 0;
+			ChimeraShot_Timer = 0;
+			SilencingShot_Timer = 0;
+			AimedShot_Timer = 0;
+
+			if (master) {
+				setStats(CLASS_HUNTER, m_creature->getRace(), master->getLevel());
+		   }
+		}
+
+	   void CreatePet()
+		{
+			pet = master->GetBotsPet(60238);
+
+			if(pet == NULL)
+				return;
+
+			pet->UpdateCharmAI();
+			pet->setFaction(m_creature->getFaction());
+			pet->SetReactState(REACT_DEFENSIVE);
+			pet->GetMotionMaster()->MoveFollow(m_creature, PET_FOLLOW_DIST*urand(1, 2),PET_FOLLOW_ANGLE);
+			CharmInfo *charmInfonewbot = pet->InitCharmInfo();
+			pet->GetCharmInfo()->SetCommandState(COMMAND_FOLLOW);
+			pet->UpdateStats(STAT_STRENGTH);
+			pet->UpdateStats(STAT_AGILITY);
+			pet->SetLevel(master->getLevel());
+
+			float val2 = master->getLevel()*4.0f + pet->GetStat(STAT_STRENGTH)*2.0f;
+
+			val2=100.0;
+			uint32 attPowerMultiplier=1;
+			pet->SetModifierValue(UNIT_MOD_ATTACK_POWER, BASE_VALUE, uint32(val2));
+			pet->UpdateAttackPowerAndDamage();
+			pet->SetBaseWeaponDamage(BASE_ATTACK, MINDAMAGE, uint32(val2 * attPowerMultiplier));
+			pet->SetBaseWeaponDamage(BASE_ATTACK, MAXDAMAGE, uint32(val2 * attPowerMultiplier)*2+master->getLevel());
+			pet->UpdateDamagePhysical(BASE_ATTACK);
+
+		}
+
+		void UpdateAI(const uint32 diff)
+		{
+
+			ReduceCD(diff);
+
+			if(IAmDead()) return;
+
+			if(!m_creature->isInCombat())
+			{
+				DoNonCombatActions();
+			}
+
+			if(pet && pet != NULL && pet->isDead())
+			{
+				master->SetBotsPetDied();
+				pet = NULL;
+			}
+
+			//if we think we have a pet, but master doesn't, it means we teleported
+			if(pet && master->m_botHasPet == false)
+			{
+				master->SetBotsPetDied();
+				pet = NULL;
+			}
+
+			DoNormalAttack(diff);
+			ScriptedAI::UpdateAI(diff);
+
+			//if low on health, drink a potion
+			if(m_creature->GetHealth() < m_creature->GetMaxHealth()*0.6 && isTimerReady(Potion_cd))
+			{
+				doCast(m_creature, HEALINGPOTION);
+				Potion_cd = 1500;
+			}
+
+			//if low on mana, drink a potion
+			 if(m_creature->GetPower(POWER_MANA) < m_creature->GetMaxPower(POWER_MANA)*0.2)
+			{
+				if(isTimerReady(Potion_cd))
+				{
+					doCast(m_creature, MANAPOTION);
+					//MonsterSay("MANA POTION", LANG_UNIVERSAL, NULL);
+					Potion_cd = 1500;
+				}
+			 }
+
+			opponent = SelectUnit(SELECT_TARGET_TOPAGGRO, 0);
+			if(!opponent && !m_creature->getVictim())
+			{
+				ResetOrGetNextTarget();
+
+				//to reduce the number of crashes, remove pet whenever we are not in combat
+				if(pet != NULL && pet->isAlive())
+				{
+					master->SetBotsPetDied();
+					pet = NULL;
+				}
+				return;
+			}
+
+
+			if(pet == NULL)
+				CreatePet();
+
+			if (pet && pet->isAlive() &&
+				!pet->isInCombat() &&
+				m_creature->getVictim()) {
+				pet->Attack (m_creature->getVictim(), true);
+				pet->GetMotionMaster()->MoveChase(m_creature->getVictim(), 1, 0);
+
+			}
+		}
+
+		void Aggro(Unit *who){}
+
+		void JustDied(Unit *Killer)
+		{
+			master->SetBotCommandState(COMMAND_FOLLOW);
+		}
+
+		void KilledUnit(Unit *)
+		{
+			((Player*)master)->SetBotCommandState(COMMAND_FOLLOW);
+		}
+
+
+		void AttackStart(Unit *u)
+		{
+			Aggro(u);
+			ScriptedAI::AttackStartCaster(u, 33);
+			m_creature->AddThreat(u, 0.001f);
+			u->AddThreat(m_creature, 0.001f);
+		}
+
+		void DoNormalAttack(const uint32 diff)
+		{
+			AttackerSet m_attackers = master->getAttackers();
+			if(opponent == NULL) return;
+			if(opponent->isDead()) return;
+
+
+			// try to get rid of enrage
+			if (TRANQ_SHOT && HasAuraName(opponent, "Enrage")) {
+				m_creature->InterruptNonMeleeSpells(true, AUTO_SHOT);
+			   // m_creature->MonsterSay("Tranquility shot!", LANG_UNIVERSAL, NULL);
+				doCast(opponent, TRANQ_SHOT);
+				GiveManaBack();
+			  //  doCast(opponent, AUTO_SHOT);
+			  //  return;
+			}
+
+			// silence it
+			if(SILENCING_SHOT && opponent->IsNonMeleeSpellCasted(true) && isTimerReady(SilencingShot_Timer))
+			{
+				doCast(opponent, SILENCING_SHOT);
+				SilencingShot_Timer = 200;
+				GiveManaBack();
+			  //  doCast(opponent, AUTO_SHOT);
+			  //  return;
+			}
+
+			// mark it
+			if (!HasAuraName(opponent, "Hunter's Mark")) {
+				doCast(opponent, HUNTERS_MARK);
+				GiveManaBack();
+			  //  doCast(opponent, AUTO_SHOT);
+			  //  return;
+			}
+
+			// sting it
+			if (SCORPID_STING && !opponent->HasAura(SCORPID_STING, m_creature->GetGUID())) {
+				m_creature->InterruptNonMeleeSpells(true, AUTO_SHOT);
+				doCast(opponent, SCORPID_STING);
+			   // m_creature->MonsterSay("Scorpid Sting!", LANG_UNIVERSAL, NULL);
+				GiveManaBack();
+			   // doCast(opponent, AUTO_SHOT);
+			   // return;
+			}
+
+			 if (CHIMERA_SHOT && isTimerReady(ChimeraShot_Timer)) {
+				m_creature->InterruptNonMeleeSpells(true, AUTO_SHOT);
+				doCast(opponent, CHIMERA_SHOT);
+				ChimeraShot_Timer = 100;
+			   // m_creature->MonsterSay("Chimera Sting!", LANG_UNIVERSAL, NULL);
+				GiveManaBack();
+			  //  doCast(opponent, AUTO_SHOT);
+			  //  return;
+			}
+
+			if(ARCANE_SHOT && isTimerReady(ArcaneShot_cd))
+			{
+				m_creature->InterruptNonMeleeSpells( true, AUTO_SHOT );
+				doCast(opponent, ARCANE_SHOT);
+			   // m_creature->MonsterSay("Arcane shot!", LANG_UNIVERSAL, NULL);
+				ArcaneShot_cd = 60;
+				GiveManaBack();
+			  //  doCast(opponent, AUTO_SHOT);
+			  //  return;
+			}
+
+			if(AIMED_SHOT && isTimerReady(AimedShot_Timer))
+			{
+				m_creature->InterruptNonMeleeSpells( true, AUTO_SHOT );
+				doCast(opponent, AIMED_SHOT);
+			   // m_creature->MonsterSay("Arcane shot!", LANG_UNIVERSAL, NULL);
+				AimedShot_Timer = 100;
+				GiveManaBack();
+			  //  doCast(opponent, AUTO_SHOT);
+			  //  return;
+			}
+
+
+			doCast(opponent, AUTO_SHOT);
+
+		}
+
+		void DoNonCombatActions()
+		{
+			if (ASPECT_OF_THE_WILD && !HasAuraName(m_creature, "Aspect of the Wild")) {
+				doCast(master, ASPECT_OF_THE_WILD);
+			}
+		}
+
+		void ReduceCD(const uint32 diff)
+		{
+			if(GC_Timer > 0)        --GC_Timer;
+			if(ArcaneShot_cd > 0)       --ArcaneShot_cd;
+			if(ChimeraShot_Timer > 0)       --ChimeraShot_Timer;
+			if(SilencingShot_Timer > 0)     --SilencingShot_Timer;
+			if(AimedShot_Timer > 0)         --AimedShot_Timer;
+		}
+
+		void ReceiveEmote(Player *player, uint32 emote)
+		{
+			//debug to see what auras are on bot
+			if(emote == TEXTEMOTE_BONK) ((hunter_botAI*)m_creature->AI())->listAuras(m_creature);
+
+			//debug to see what auras are on me
+			if(emote == TEXTEMOTE_SALUTE) ((hunter_botAI*)m_creature->AI())->listAuras(player);
+
+
+			if(emote == TEXTEMOTE_STAND)
+			{
+				if(m_creature->GetCharmerGUID() != player->GetGUID())
+				{
+					m_creature->HandleEmoteCommand(TEXTEMOTE_RUDE);
+					return;
+				}
+
+				player->SetBotCommandState (COMMAND_STAY);
+			}
+			if(emote == TEXTEMOTE_WAVE)
+			{
+				player->SetBotCommandState (COMMAND_FOLLOW);
+			}
+
+
+		}
+
+	};
+};
+
+void AddSC_hunter_bot()
+{
+    new hunter_bot();
+}
diff --git a/src/server/scripts/Bots/bot_hunter_ai.h b/src/server/scripts/Bots/bot_hunter_ai.h
new file mode 100644
index 0000000..23b9c3d
--- /dev/null
+++ b/src/server/scripts/Bots/bot_hunter_ai.h
@@ -0,0 +1,26 @@
+
+
+#include "bot_ai.h"
+
+
+#define AUTO_SHOT           75
+#define TRANQ_SHOT           TRANQ_SHOT_A[SPELL_LEVEL]
+#define SCORPID_STING        SCORPID_STING_A[SPELL_LEVEL]
+#define HUNTERS_MARK         HUNTERS_MARK_A[SPELL_LEVEL]
+
+#define ARCANE_SHOT          ARCANE_SHOT_A[SPELL_LEVEL]
+#define CHIMERA_SHOT         CHIMERA_SHOT_A[SPELL_LEVEL]
+#define AIMED_SHOT           AIMED_SHOT_A[SPELL_LEVEL]
+#define SILENCING_SHOT       SILENCING_SHOT_A[SPELL_LEVEL]
+#define ASPECT_OF_THE_WILD   ASPECT_OF_THE_WILD_A[SPELL_LEVEL]
+
+uint32 TRANQ_SHOT_A[] = { 0, 0, 19801, 19801, 19801, 19801, 19801, 19801, 19801, 19801 };
+uint32 HUNTERS_MARK_A[] = { 14325, 14325, 14325, 14325, 14325, 14325, 14325, 14325, 14325, 14325 };
+uint32 SCORPID_STING_A[] = { 0, 0, 0, 0, 0, 0, 3043, 3043, 3043, 3043 };
+
+uint32 ARCANE_SHOT_A[] = { 3044, 14281, 14282, 14284, 14285, 14286, 27019, 49044, 49045, 49045 };
+uint32 CHIMERA_SHOT_A[] = { 0, 0, 0, 0, 0, 0, 53209, 53209, 53209, 53209 };
+uint32 AIMED_SHOT_A[] = { 0, 0, 19434, 20900, 20902, 20903, 20904, 49049, 49050, 49050 };
+uint32 SILENCING_SHOT_A[] = { 0, 0, 0, 34490, 34490, 34490, 34490, 34490, 34490, 34490 };
+
+uint32 ASPECT_OF_THE_WILD_A[] = {0, 0, 0, 0, 20043, 20190, 27045, 49071, 49071 };
diff --git a/src/server/scripts/Bots/bot_mage_ai.cpp b/src/server/scripts/Bots/bot_mage_ai.cpp
new file mode 100644
index 0000000..8a680b5
--- /dev/null
+++ b/src/server/scripts/Bots/bot_mage_ai.cpp
@@ -0,0 +1,524 @@
+    /* ScriptData
+    SDName: pvp_mage
+    SD%Complete: 0
+    SDComment: Original PVP mage by Brats reworked by Azrael with the help of Machiavelli
+    SDCategory: Custom
+    EndScriptData */
+#include "ScriptPCH.h"
+#include "bot_mage_ai.h"
+#include "Group.h"
+
+
+class mage_bot : public CreatureScript
+{
+public:
+    mage_bot() : CreatureScript("mage_bot") { }
+
+    CreatureAI *GetAI(Creature *pCreature) const
+    {
+        return new mage_botAI(pCreature);
+    }
+
+	struct mage_botAI : public bot_ai
+	{
+		mage_botAI(Creature *c) :bot_ai(c)
+		{
+			//master = NULL;
+			Reset();
+		}
+
+		bool oom_spam;
+
+		uint8 state;
+		uint8 next_state;
+		uint32 next_state_timer;
+		Unit *opponent;
+
+		void Reset()
+		{
+			Blizzard_cd = 0;
+			FireBlast_cd = 0;
+			BlastWave_cd = 0;
+			CounterSpell_cd = 0;
+			FrostNova_cd = 0;
+			PoM_cd = 0;
+			Ward_cd = 0;
+			DragonBreath_cd = 0;
+			Blink_cd = 0;
+			Potion_cd = 0;
+			Combustion_cd = 0;
+			Evocation_cd = 0;
+			FirstAid_cd = 0;
+			GC_Timer = 0;
+			CastedArcaneIntellect = false;
+			CastedDampenMagic = false;
+			CastedArmor1 = false;
+			blink_timer = 10;
+			blink = false;
+			//wait = false;
+			oom_spam = false;
+
+			state = 1;
+			next_state = 0;
+			next_state_timer = 0;
+
+			opponent = NULL;
+
+			if (master) {
+				setStats(CLASS_DRUID, m_creature->getRace(), master->getLevel());
+		   }
+		}
+
+		void UpdateAI(const uint32 diff)
+		{
+			if(IAmDead()) return;
+
+			ReduceCD(diff);
+
+			if(!m_creature->isInCombat())
+			{
+				//buff master because master might be in different group
+				if(!HasAuraName(master, GetSpellName(ARCANEINTELLECT)) &&
+					master->isAlive() &&
+					isTimerReady(GC_Timer))
+						doCast(master, ARCANEINTELLECT, true);
+
+				//buff myself
+				if(!m_creature->HasAura(ARCANEINTELLECT) && isTimerReady(GC_Timer))
+					doCast(m_creature, ARCANEINTELLECT, true);
+
+				//check group members
+				Group::MemberSlotList const &a = ((Player*)master)->GetGroup()->GetMemberSlots();
+				for(Group::member_citerator itr = a.begin(); itr != a.end(); itr++)
+				{
+					Player *tPlayer = ((Player *)master)->GetObjPlayer(itr->guid);
+					if(tPlayer == NULL) continue;
+
+					//buff group
+					if(tPlayer->isAlive() &&
+						!m_creature->isInCombat() &&
+						isTimerReady(GC_Timer) &&
+						!HasAuraName(tPlayer, ARCANEINTELLECT))
+							doCast(tPlayer, ARCANEINTELLECT, true);
+				}
+
+				//no other buffs till full mana
+				if(m_creature->GetPower(POWER_MANA) == m_creature->GetMaxPower(POWER_MANA))
+				{
+					//sLog->outError("mana is %u, total mana is %u", m_creature->GetPower(POWER_MANA), m_creature->GetMaxPower(POWER_MANA));
+					//if(!HasAuraName(m_creature, "Mana Shield"))
+						//doCast(m_creature, MANASHIELD);
+
+					if(!HasAuraName(m_creature, DAMPENMAGIC) && isTimerReady(GC_Timer))
+					{
+						 doCast(m_creature, DAMPENMAGIC, true);
+						 GiveManaBack();
+						 CastedDampenMagic = true;
+						 //MonsterSay("DAMPEN MAGIC", LANG_UNIVERSAL, NULL);
+					}
+
+					if(!HasAuraName(m_creature, ICEARMOR) && isTimerReady(GC_Timer))
+					{
+						doCast(m_creature, ICEARMOR, true);
+						GiveManaBack();
+						//MonsterSay("ice armor", LANG_UNIVERSAL, NULL);
+						CastedArmor1 = true;
+					}
+				}
+			} //end if !isInCombat
+
+			m_creature->ApplySpellImmune(0, IMMUNITY_STATE, SPELL_AURA_MOD_TAUNT, true);
+			m_creature->ApplySpellImmune(0, IMMUNITY_EFFECT, SPELL_EFFECT_ATTACK_ME, true);
+			if(m_creature->GetHealth() < m_creature->GetMaxHealth()*0.5 && isTimerReady(Potion_cd))
+			{
+				doCast(m_creature, HEALINGPOTION);
+				Potion_cd = 1500;
+			}
+			if(m_creature->GetPower(POWER_MANA) < m_creature->GetMaxPower(POWER_MANA)*0.3 && isTimerReady(Evocation_cd))
+			{
+				doCast(m_creature, EVOCATION);
+				Evocation_cd = EVOCATION_CD;
+			}
+			if(m_creature->GetPower(POWER_MANA) < m_creature->GetMaxPower(POWER_MANA)*0.2)
+			{
+				if(isTimerReady(Potion_cd))
+				{
+					doCast(m_creature, MANAPOTION);
+					//MonsterSay("MANA POTION", LANG_UNIVERSAL, NULL);
+					Potion_cd = Potion_cd;
+				} else {
+					if(oom_spam == false)
+					{
+						//MonsterSay("OOM", LANG_UNIVERSAL, NULL);
+						oom_spam = true;
+					}
+					ScriptedAI::UpdateAI(diff);
+					//return; //can't do anything without mana
+				}
+			}
+			oom_spam = false;
+
+			ScriptedAI::UpdateAI(diff);
+
+			opponent = SelectUnit(SELECT_TARGET_TOPAGGRO, 0);
+			if(!opponent && !m_creature->getVictim())
+			{
+				ResetOrGetNextTarget();
+				return;
+			}
+
+			//Armour(diff);
+			CheckSpellSteal(diff);
+			DoNormalAttack(diff);
+			Counter(diff);
+		}
+
+		void Aggro(Unit *who){}
+
+		void Armour(const uint32 diff)
+		{
+			Unit *opponent = SelectUnit(SELECT_TARGET_TOPAGGRO, 0);
+
+			if(!CastedArmor1 && (opponent->getClass() == CLASS_MAGE || opponent->getClass() == CLASS_PRIEST || opponent->getClass() == CLASS_WARLOCK) && isTimerReady(GC_Timer))
+			{
+				doCast(m_creature, MAGEARMOR, true);
+				CastedArmor1 = true;
+				//MonsterSay("MAGE Armor", LANG_UNIVERSAL, NULL);
+			}
+			else if(!CastedArmor1 && isTimerReady(GC_Timer))
+			{
+				doCast(m_creature, ICEARMOR, true);
+				//MonsterSay("ice armor 2", LANG_UNIVERSAL, NULL);
+				CastedArmor1 = true;
+			} else {
+				doCast(m_creature, ICEARMOR, true);
+				//MonsterSay("ice armor 3", LANG_UNIVERSAL, NULL);
+				CastedArmor1 = true;
+				return;
+			}
+		} //end Armour
+
+
+		void JustDied(Unit *Killer)
+		{
+			master->SetBotCommandState(COMMAND_FOLLOW);
+			state = 1;
+			next_state = 0;
+			next_state_timer = 0;
+		}
+
+		void AttackStart(Unit *u)
+		{
+			Aggro(u);
+			ScriptedAI::AttackStartCaster(u, 31);
+			m_creature->AddThreat(u, 0.001f);
+		}
+
+		void KilledUnit(Unit *)
+		{
+			((Player*)master)->SetBotCommandState(COMMAND_FOLLOW);
+			state = 1;
+			next_state = 0;
+			next_state_timer = 0;
+		}
+
+		void Counter(const uint32 diff)
+		{
+			if(opponent == NULL) return;
+			if(opponent->isDead()) return;
+			if(opponent->IsNonMeleeSpellCasted(true) && isTimerReady(CounterSpell_cd))
+			{
+				doCast(opponent, COUNTERSPELL);
+				CounterSpell_cd = COUNTERSPELL_CD;
+			}
+		}
+
+		void CheckSpellSteal(const uint32 diff)
+		{
+			if(opponent == NULL) return;
+			if(opponent->isDead()) return;
+			if(   //Druids:
+				  HasAuraName(opponent, "Mark of the Wild") ||
+				  HasAuraName(opponent, "Rejuvenation") ||
+				  HasAuraName(opponent, "Regrowth") ||
+				  //opponent->HasAura(33763) || //lifebloom
+				  //Mage:
+				  //opponent->HasAura(12043) || //POM
+				  //Warlock:
+				  HasAuraName(opponent, "Demon Armor") ||
+				  HasAuraName(opponent, "Fel Armor") ||
+				  //Priest:
+				  HasAuraName(opponent, "Power Word: Shield") ||
+				  HasAuraName(opponent, "Power Word: Fortitude"))
+			{
+				if(isTimerReady(GC_Timer))
+					doCast(opponent, SPELLSTEAL);
+			}
+		}
+
+
+
+		void DoNormalAttack(const uint32 diff)
+		{
+			AttackerSet m_attackers = master->getAttackers();
+			if(opponent == NULL) return;
+			if(opponent->isDead()) return;
+			bool defensive = false;
+
+			//opponent is attacking me, go defensive, ie point blank spells
+			if(opponent->getVictim() && opponent->getVictim()->GetGUID() == m_creature->GetGUID())
+				defensive = true;
+
+			//sLog->outError("%s: state = %u, next_state = %u, next_state_time = %u", m_creature->GetName(), state, next_state, next_state_timer);
+
+			switch(state)
+			{
+				case 0:
+					if(next_state_timer <= 0)
+						state = next_state;
+					else
+						--next_state_timer;
+
+					break;
+				case 1:
+					if(opponent->GetHealth()*100 > 80)
+						//state = 2;
+						state = 6;
+					else
+						state = 5;
+					break;
+				case 2:
+					if(isTimerReady(GC_Timer))
+					{
+						//MonsterSay("POLYMORPH", LANG_UNIVERSAL, NULL);
+						//doCast(opponent, POLYMORPH);
+						//next_state_timer = 21;
+						next_state_timer = 2;
+						next_state = 3;
+						state = 0;
+					}
+				case 3:
+					if(opponent->HasAura(POLYMORPH) || m_creature->HasAura(POM))
+					{
+						next_state_timer = (m_creature->HasAura(POM)) ? 15 : 61;
+						int damage = 957 + rand()%(1215-957+1) + 1.15*BONUS_DAMAGE;
+						//m_creature->CastCustomSpell(opponent, PYROBLAST, &damage, NULL, NULL, false, NULL, NULL);
+						doCast(opponent, PYROBLAST);
+						//MonsterSay("PYROBLAST", LANG_UNIVERSAL, NULL);
+						//GC_Timer = 1500;
+						next_state = 4;
+						state = 0;
+					} else state = 5;
+					break;
+				case 4:
+					if(isTimerReady(PoM_cd))
+					{
+						int damage = 957 + rand()%(1215-957+1) + 1.15*BONUS_DAMAGE;
+						//m_creature->CastCustomSpell(opponent, PYROBLAST, &damage, NULL, NULL, false, NULL, NULL);
+						doCast(opponent, PYROBLAST);
+						//MonsterSay("PYROBLAST", LANG_UNIVERSAL, NULL);
+						PoM_cd = POM_CD;
+						next_state = 5;
+						state = 0;
+						next_state_timer = 5;
+					}
+					else state = 5;
+					break;
+				case 5:
+					if(m_creature->GetPower(POWER_MANA)*100/m_creature->GetMaxPower(POWER_MANA) < 25.0)
+						break;
+
+					if(m_creature->GetHealth() < m_creature->GetMaxHealth()*0.3 && isTimerReady(FirstAid_cd))
+					{
+						doCast(opponent, FROSTNOVA);
+						GiveManaBack();
+						FrostNova_cd = FROSTNOVA_CD;
+						//MonsterSay("FROSTNOVA", LANG_UNIVERSAL, NULL);
+						next_state = 8;
+						next_state_timer = 15;
+						state = 0;
+					}
+					if(LIVINGBOMB && isTimerReady(Living_Bomb_cd))
+					{
+						doCast(opponent, LIVINGBOMB);
+						GiveManaBack();
+						Living_Bomb_cd = LIVING_BOMB_CD;
+						//MonsterSay("LIVING BOMB", LANG_UNIVERSAL, NULL);
+						next_state = 7;
+						next_state_timer = 2;
+						state = 0;
+					}
+					else if(isTimerReady(Scorch_cd))
+					{
+						//MonsterSay("SCORCH", LANG_UNIVERSAL, NULL);
+						doCast(opponent, SCORCH);
+						Scorch_cd = SCORCH_CD;
+						next_state = 7;
+						next_state_timer = 5;
+						state = 0;
+					}
+					else if(isTimerReady(GC_Timer))
+					{
+						int damage = 377 + rand()%(407-377+1) + 0.2128*BONUS_DAMAGE;
+						//m_creature->CastCustomSpell(opponent, ARCANEEXPLOSION, &damage, NULL, NULL, false, NULL, NULL);
+						if(ARCANEEXPLOSION > 0 && defensive)
+							 doCast(opponent, ARCANEEXPLOSION);
+						else
+							 doCast(opponent, FIREBALL);
+						GiveManaBack();
+						//MonsterSay("Arcane Explosion", LANG_UNIVERSAL, NULL);
+						next_state = 7;
+						next_state_timer = 2;
+						state = 0;
+					}
+					/*if(isTimerReady(Combustion_cd))
+					{
+						doCast(m_creature, COMBUSTION);
+						Combustion_cd = COMBUSTION_CD;
+						//MonsterSay("COMBUSTION", LANG_UNIVERSAL, NULL);
+						next_state = 6;
+						next_state_timer = 15;
+						state = 0;
+					}
+					else if(BLIZZARD && isTimerReady (Blizzard_cd) && m_attackers.size() > 1)
+					{
+						doCast(opponent, 62576);
+						//GiveManaBack();
+						Blizzard_cd = BLIZZARD_CD;
+						//MonsterSay("Blizzard", LANG_UNIVERSAL, NULL);
+						//sLog->outError("blizzard - %u", BLIZZARD);
+						//GiveManaBack();
+						next_state = 6;
+						next_state_timer = 100;
+						state = 0;
+					}*/
+					else if(FIREBLAST && isTimerReady(FireBlast_cd) && !defensive)
+					{
+						int damage = 677 + rand()%(802-677+1) + 0.4286*BONUS_DAMAGE;
+						//m_creature->CastCustomSpell(opponent, FIREBLAST, &damage, NULL, NULL, false, NULL, NULL);
+						doCast(opponent, FIREBLAST);
+						GiveManaBack();
+						FireBlast_cd = FIREBLAST_CD;
+						//MonsterSay("FIREBLAST", LANG_UNIVERSAL, NULL);
+						next_state = 7;
+						next_state_timer = 3;
+						state = 0;
+					}
+					else if(BLASTWAVE && isTimerReady(BlastWave_cd) && defensive && !opponent->HasAura(BLASTWAVE))
+					{
+						int damage = 628 + rand()%(739-628+1) + 0.1357*BONUS_DAMAGE;
+						//m_creature->CastCustomSpell(opponent, BLASTWAVE, &damage, NULL, NULL, false, NULL, NULL);
+						doCast(opponent, BLASTWAVE);
+						GiveManaBack();
+						BlastWave_cd = BLASTWAVE_CD;
+						//MonsterSay("BLAST WAVE", LANG_UNIVERSAL, NULL);
+						next_state = 7;
+						next_state_timer = 2;
+						state = 0;
+					}
+					else if(DRAGONBREATH && isTimerReady(DragonBreath_cd) && defensive && !opponent->HasAura(DRAGONBREATH))
+					{
+						int damage = 693 + rand()%(806-693+1) + 0.1357*BONUS_DAMAGE;
+						//m_creature->CastCustomSpell(opponent, DRAGONBREATH, &damage, NULL, NULL, false, NULL, NULL);
+						doCast(opponent, DRAGONBREATH);
+						GiveManaBack();
+						//MonsterSay("Dragon Breath", LANG_UNIVERSAL, NULL);
+						DragonBreath_cd = DRAGONBREATH_CD;
+						next_state = 6;
+						next_state_timer = 2;
+						state = 0;
+					}
+
+					break;
+				case 6:
+					if(isTimerReady(GC_Timer))
+					{
+						int damage = 645 + rand()%(822-645+1) + 1.00*BONUS_DAMAGE;
+						//m_creature->CastCustomSpell(opponent, FIREBALL, &damage, NULL, NULL, false, NULL, NULL);
+						doCast(opponent, FIREBALL);
+						GiveManaBack();
+						next_state = 5;
+						//MonsterSay("Fireball", LANG_UNIVERSAL, NULL);
+						next_state_timer = 3;
+						state = 0;
+					}
+					break;
+				case 7:
+					if(//HasAuraName(opponent, "Ice Block") ||
+						  HasAuraName(opponent, ICEBLOCK) ||
+						  HasAuraName(opponent, "Divine Shield") ||
+						  opponent->HasAura(5573)) //divine protection
+					{
+						doCast(m_creature, BANDAGE);
+						FirstAid_cd = FirstAid_cd;
+						state = 9;
+					}
+					else state = 5;
+					break;
+				case 8:
+					doCast(opponent, BLINK);   //Needs some serious testing
+					//blink is not working, so comment out for now
+					//blink = true;
+					//m_creature->Say("BLINK", LANG_UNIVERSAL, NULL);
+					//GC_Timer = 1500;
+					//MonsterSay("Blink", LANG_UNIVERSAL, NULL);
+					next_state = 9;
+					next_state_timer = 5;
+					state = 0;
+					break;
+				case 9:
+					/*if(blink)
+					{
+						wait = true;
+						wait_timer = 60;
+						doCast(m_creature, BANDAGE);
+						FirstAid_cd = FirstAid_cd;
+						//m_creature->Say("BANDAGE", LANG_UNIVERSAL, NULL);
+						blink = false;
+						//GC_Timer = 1500;
+						next_state = 10;
+						next_state_timer = 35;
+					} else*/
+						next_state = 5;
+					state = 0;
+					break;
+				case 10:
+					if(!m_creature->HasAura(BANDAGE))
+						state = 5;
+					break;
+				default:
+					state = 1;
+			}
+		}
+
+		void ReduceCD(const uint32 diff)
+		{
+			if(Living_Bomb_cd > 0)  --Living_Bomb_cd;
+			if(FireBlast_cd > 0)    --FireBlast_cd;
+			if(Blizzard_cd > 0)     --Blizzard_cd;
+			if(BlastWave_cd > 0)    --BlastWave_cd;
+			if(CounterSpell_cd > 0) --CounterSpell_cd;
+			if(FrostNova_cd > 0)    --FrostNova_cd;
+			if(PoM_cd > 0)          --PoM_cd;
+			if(Ward_cd > 0)         --Ward_cd;
+			if(Scorch_cd > 0)       --Scorch_cd;
+			if(DragonBreath_cd > 0) --DragonBreath_cd;
+			if(Blink_cd > 0)        --Blink_cd;
+			if(Combustion_cd > 0)   --Combustion_cd;
+			if(Potion_cd > 0)       --Potion_cd;
+			if(Evocation_cd > 0)    --Evocation_cd;
+			if(FirstAid_cd >0)      --FirstAid_cd;
+			if(GC_Timer > 0)        --GC_Timer;
+		}
+
+		void ReceiveBowEmote(Player *player)
+		{
+			((mage_botAI*)m_creature->AI())->doCast(player, ARCANEINTELLECT, true );
+		}
+	};
+};
+
+void AddSC_mage_bot()
+{
+    new mage_bot();
+}
diff --git a/src/server/scripts/Bots/bot_mage_ai.h b/src/server/scripts/Bots/bot_mage_ai.h
new file mode 100644
index 0000000..af681b7
--- /dev/null
+++ b/src/server/scripts/Bots/bot_mage_ai.h
@@ -0,0 +1,124 @@
+
+#include "bot_ai.h"
+
+
+uint32 FireBlast_cd;
+uint32 BlastWave_cd;
+uint32 CounterSpell_cd;
+uint32 FrostNova_cd;
+uint32 PoM_cd;
+uint32 Ward_cd;
+uint32 Blizzard_cd;
+uint32 DragonBreath_cd;
+uint32 Blink_cd;
+uint32 Combustion_cd;
+uint32 Scorch_cd;
+uint32 Potion_cd;
+uint32 Evocation_cd;
+uint32 Living_Bomb_cd;
+uint32 FirstAid_cd;
+uint32 GC_Timer;
+uint32 blink_timer;
+//uint32 wait_timer;
+
+bool CastedDampenMagic;
+bool CastedArcaneIntellect;
+bool CastedArmor1;
+//bool wait;
+bool blink;
+
+//arcane spells cooldown
+#define ARCANETORRENT_CD 120
+#define EVOCATION_CD 4800
+#define COUNTERSPELL_CD 240
+#define POM_CD 1200
+#define BLINK_CD 1500
+
+//fire spells cooldown
+#define BLASTWAVE_CD 350
+#define DRAGONBREATH_CD 450
+#define WARD_CD 300
+#define FIREBLAST_CD 650 //8000 if not fire mage
+#define COMBUSTION_CD 1800
+#define SCORCH_CD 900
+#define LIVING_BOMB_CD 600
+
+//frost spells cooldown
+#define FROSTNOVA_CD 250
+#define CONEOFCOLD_CD 100
+#define ICEBLOCK_CD 3000
+#define BLIZZARD_CD 300
+
+//arcane spells
+#define DAMPENMAGIC     SPELL_DAMPENMAGIC_A[SPELL_LEVEL]
+#define MANASHIELD      SPELL_MANASHIELD_A[SPELL_LEVEL]
+#define MAGEARMOR       SPELL_MAGEARMOR_A[SPELL_LEVEL]
+#define SPELLSTEAL      SPELL_SPELLSTEAL_A[SPELL_LEVEL]
+#define ARCANEMISSILES  SPELL_ARCANEMISSILES_A[SPELL_LEVEL]
+#define ARCANEINTELLECT SPELL_ARCANEINTELLECT_A[SPELL_LEVEL]
+#define ARCANEEXPLOSION SPELL_ARCANEEXPLOSION_A[SPELL_LEVEL]
+#define POLYMORPH       SPELL_POLYMORPH_A[SPELL_LEVEL]
+#define COUNTERSPELL    2139
+#define EVOCATION       12051
+#define POM             12043
+#define REMCURSE        15729
+#define BLINK           38932
+#define ARCANETORRENT   28730
+
+
+
+//fire spells
+#define FIREBALL        SPELL_FIREBALL_A[SPELL_LEVEL]
+#define BLASTWAVE       SPELL_BLASTWAVE_A[SPELL_LEVEL]
+#define DRAGONBREATH    SPELL_DRAGONBREATH_A[SPELL_LEVEL]
+#define FIREBLAST       SPELL_FIREBLAST_A[SPELL_LEVEL]
+#define FIREWARD        SPELL_FIREWARD_A[SPELL_LEVEL]
+#define PYROBLAST       SPELL_PYROBLAST_A[SPELL_LEVEL]
+#define COMBUSTION      SPELL_COMBUSTION_A[SPELL_LEVEL]
+#define SCORCH          SPELL_SCORCH_A[SPELL_LEVEL]
+#define MOLTENARMOR     30482
+#define LIVINGBOMB      SPELL_LIVING_BOMB_A[SPELL_LEVEL]
+
+//frost spells
+#define FROSTNOVA       SPELL_FROSTNOVA_A[SPELL_LEVEL]
+#define FROSTWARD       SPELL_FROSTWARD_A[SPELL_LEVEL]
+#define CONEOFCOLD      SPELL_CONEOFCOLD_A[SPELL_LEVEL]
+#define ICEARMOR        SPELL_ICEARMOR_A[SPELL_LEVEL]
+#define ICEBLOCK        45438
+#define BLIZZARD        SPELL_BLIZZARD_A[SPELL_LEVEL]
+
+
+//others
+#define BONUS_DAMAGE 986 //Spell Bonus
+#define BANDAGE 27031
+#define MANAPOTION 28499
+#define REJUVEPOTION 28517
+#define HEALINGPOTION 28495
+
+//arcane spells
+uint32 SPELL_DAMPENMAGIC_A[] = { 0, 8450, 8451, 10173, 10173, 10174, 33944, 43015, 43015 };
+uint32 SPELL_MANASHIELD_A[] = { 0, 0, 1463, 8495, 10191, 10192, 27131, 43019, 43020 };
+uint32 SPELL_MAGEARMOR_A[] = { 0, 0, 0, 6117, 22782, 22783, 27125, 43023, 43024 };
+uint32 SPELL_SPELLSTEAL_A[] = { 0, 0, 0, 0, 0, 0, 0, 30449, 30449 };
+uint32 SPELL_ARCANEMISSILES_A[] = {5143, 5144, 5145, 8416, 8417, 10212, 27075, 42843, 42846 };
+uint32 SPELL_ARCANEINTELLECT_A[] = { 1459, 1460, 1461, 1461, 10156, 10157, 10157, 27126, 42995};
+uint32 SPELL_ARCANEEXPLOSION_A[] = { 0, 1449, 8437, 8439, 10201, 10202, 27080, 42990, 42921 };
+uint32 SPELL_POLYMORPH_A[] = { 0, 0, 12824, 12824, 12825, 12825, 28271, 28272, 61721 };
+
+//fire spells
+uint32 SPELL_FIREBALL_A[] = { 143, 145, 8400, 8401, 10148, 25306, 38692, 42834, 42834 };
+uint32 SPELL_BLASTWAVE_A[] = { 0, 0, 0, 11113, 13019, 13020, 13021, 42944, 42945 };
+uint32 SPELL_DRAGONBREATH_A[] = { 0, 0, 0, 0, 0, 31661, 31661, 31661, 31661 };
+uint32 SPELL_FIREBLAST_A[] = {2136, 2137, 2138, 8412, 10197, 10199, 10199, 10199, 10199 };
+uint32 SPELL_FIREWARD_A[] = { 0, 0, 543, 8457, 8458, 10223, 10225, 27128, 43010 };
+uint32 SPELL_COMBUSTION_A[] = { 0, 0, 0, 0, 11129, 11129, 11129, 11129, 11129 };
+uint32 SPELL_PYROBLAST_A[] = { 0, 0, 11366, 12522, 12525, 12526, 27132, 42890, 42891 };
+uint32 SPELL_SCORCH_A[] = { 0, 0, 2948, 8445, 10205, 10207, 27073, 27074, 42859 };
+uint32 SPELL_LIVING_BOMB_A[] = { 0, 0, 0, 0, 0, 44457, 44457, 44457, 44457 };
+
+//frost spells
+uint32 SPELL_FROSTNOVA_A[] = {0, 122, 865, 6131, 10230, 27088, 42917, 42917, 42917 };
+uint32 SPELL_FROSTWARD_A[] = { 0, 0, 6143, 8461, 8462, 10177, 28609, 32796, 43012 };
+uint32 SPELL_CONEOFCOLD_A[] = { 0, 0, 120, 8492, 10159, 10161, 27087, 42930, 42931 };
+uint32 SPELL_ICEARMOR_A[] = {0, 0, 0, 7302, 7320, 10219, 10220, 27124, 43008  };
+uint32 SPELL_BLIZZARD_A[] = { 0, 0, 10, 8427, 10185, 10186, 10187, 27085, 42940 };
diff --git a/src/server/scripts/Bots/bot_paladin_ai.cpp b/src/server/scripts/Bots/bot_paladin_ai.cpp
new file mode 100644
index 0000000..5a53215
--- /dev/null
+++ b/src/server/scripts/Bots/bot_paladin_ai.cpp
@@ -0,0 +1,276 @@
+#include "ScriptPCH.h"
+#include "bot_paladin_ai.h"
+
+
+class paladin_bot : public CreatureScript
+{
+public:
+    paladin_bot() : CreatureScript("paladin_bot") { }
+
+    CreatureAI *GetAI(Creature *pCreature) const
+    {
+        return new paladin_botAI(pCreature);
+    }
+
+	struct paladin_botAI : public bot_ai
+	{
+		paladin_botAI(Creature *c) :bot_ai(c)
+		{
+			Reset();
+		}
+
+		bool oom_spam;
+
+		Unit *opponent;
+
+		int32 LOH_Timer;
+		int32 HOJ_Timer;
+
+		void Reset()
+		{
+			oom_spam = false;
+
+			opponent = NULL;
+
+			GC_Timer = 0;
+			LOH_Timer = 0;
+			HOJ_Timer = 0;
+
+			if (master) {
+				setStats(CLASS_PALADIN, m_creature->getRace(), master->getLevel());
+		   }
+		}
+
+		bool CureTarget(Unit *target)
+		{
+			if (!isTimerReady(GC_Timer)) return false;
+			if (HasAuraName(target, "Withering Heat"))
+			{
+	//sLog->outError ("PaladingBotAI.CureTarget: curing %s of withering heat");
+				doCast(target, CLEANSE);
+			}
+			if (HasAuraName(target, "Ancient Dread"))
+			{
+	//sLog->outError ("PaladingBotAI.CureTarget: curing %s of Ancient Dread");
+				doCast(target, CLEANSE);
+			}
+					if (HasAuraName(target, "Ancient Dread"))
+			{
+	//sLog->outError ("PaladingBotAI.CureTarget: curing %s of Ancient Dread");
+				doCast(target, CLEANSE);
+			}
+			if (HasAuraName(target, "Arcane Buffet"))
+			{
+	//sLog->outError ("PaladingBotAI.CureTarget: curing %s of Arcane Buffet");
+				doCast(target, CLEANSE);
+			}
+			if (HasAuraName(target, "Shadow Buffet"))
+			{
+	//sLog->outError ("PaladingBotAI.CureTarget: curing %s of Shadow Buffet");
+				doCast(target, CLEANSE);
+			}
+			if (HasAuraName(target, "Flame Buffet"))
+			{
+	//sLog->outError ("PaladingBotAI.CureTarget: curing %s of Flame Buffet");
+				doCast(target, CLEANSE);
+			}
+			if (HasAuraName(target, "Frost Buffet"))
+			{
+	//sLog->outError ("PaladingBotAI.CureTarget: curing %s of Frost Buffet");
+				doCast(target, CLEANSE);
+			}
+			return true;
+		}
+
+		bool HealTarget(Unit *target, uint8 hp)
+		{
+			if (!isTimerReady(GC_Timer)) return false;
+			if (m_creature->IsNonMeleeSpellCasted(true)) return false;
+			if(!target || target->isDead()) return false;
+			if(hp < 25 && isTimerReady(LOH_Timer))
+			{
+				// 33% to cast loh, else just do a fast heal
+				uint64 m_rand = urand(1, 3);
+				switch(m_rand)
+				{
+					 case 1: {
+						std::string loh = "Lay of Hands on ";
+						loh += target->GetName();
+						loh += ".";
+
+						m_creature->MonsterSay(loh.c_str(), LANG_UNIVERSAL, NULL);
+
+						doCast(target, LAY_OF_HANDS);
+						LOH_Timer = 1600;
+						return true;
+					}
+					case 2:
+					case 3: {
+						doCast(target, FLASH_OF_LIGHT);
+						GiveManaBack(15);
+						return true;
+					}
+				}
+
+			}
+			if(hp < 60) { doCast(target, FLASH_OF_LIGHT); GiveManaBack(15); return true; }
+			if(hp < 80) { doCast(target, HOLY_LIGHT); GiveManaBack(20); return true; }
+
+			return true;
+		} //end HealTarget
+
+
+		void UpdateAI(const uint32 diff)
+		{
+			if(IAmDead()) return;
+
+			ReduceCD(diff);
+
+		   // opponent = master->getVictim() ? master->getVictim() : SelectUnit(SELECT_TARGET_TOPAGGRO, 0);
+			opponent = m_creature->getVictim();
+			if(!opponent )
+			{
+				ResetOrGetNextTarget();
+				DoNonCombatActions();
+				return;
+			}
+
+			m_creature->ApplySpellImmune(0, IMMUNITY_STATE, SPELL_AURA_MOD_TAUNT, true);
+			m_creature->ApplySpellImmune(0, IMMUNITY_EFFECT, SPELL_EFFECT_ATTACK_ME, true);
+			if(m_creature->GetHealth() < m_creature->GetMaxHealth()*0.5 && isTimerReady(Potion_cd))
+			{
+				doCast(m_creature, HEALINGPOTION);
+				Potion_cd = Potion_cd;
+			}
+
+			oom_spam = false;
+
+			//buff and heal master's group
+			if(master->GetGroup())
+			{
+				BuffAndHealGroup(master);
+				CureGroup(master);
+			}
+
+			// Heal myself
+			HealTarget (m_creature, m_creature->GetHealth()*100 / m_creature->GetMaxHealth());
+
+
+
+			DoNormalAttack(diff);
+			Counter(diff);
+
+			ScriptedAI::UpdateAI(diff);
+		}
+
+		void Aggro(Unit *who){
+		}
+
+		void Armour(const uint32 diff)
+		{
+
+		} //end Armour
+
+		void DoNonCombatActions()
+		{
+			if (m_creature->HasAura(DEVOTION_AURA)) Aura = DEVOTIONAURA;
+			else if (m_creature->HasAura(FIRE_RESISTANCE_AURA)) Aura = FIRERESISTANCEAURA;
+			else Aura = NOAURA;
+
+			//buff myself
+			if(!master->HasAura(DEVOTION_AURA) && isTimerReady(GC_Timer)) {
+				doCast(m_creature, DEVOTION_AURA, true);
+			}
+			else
+			// I already have devotion aura and its not mine, cast different aura
+			if (master->HasAura(DEVOTION_AURA) &&
+				!master->HasAura(DEVOTION_AURA, m_creature->GetGUID()) &&
+				Aura == NOAURA &&
+				isTimerReady(GC_Timer))
+				doCast(m_creature, FIRE_RESISTANCE_AURA, true);
+
+			if(!m_creature->HasAura(SEAL_OF_LIGHT) && isTimerReady(GC_Timer))
+				doCast(m_creature, SEAL_OF_LIGHT, true);
+
+			//buff and heal master's group
+			if(master->GetGroup())
+			{
+				RezGroup(REDEMPTION, master);
+				BuffAndHealGroup(master);
+				CureGroup(master);
+			}
+
+		}
+
+		void BuffTarget(Unit *target)
+		{
+			if(!target) return;
+			switch(target->getClass())
+			{
+			case CLASS_MAGE:
+			case CLASS_PRIEST:
+			case CLASS_WARLOCK:
+				if (!HasAuraName(target, GetSpellName(BLESSING_OF_WISDOM))) doCast(target, BLESSING_OF_WISDOM, true);
+				break;
+			case CLASS_PALADIN:
+				if (!HasAuraName(target, GetSpellName(BLESSING_OF_SANCTUARY))) doCast(target, BLESSING_OF_SANCTUARY, true);
+				break;
+			default:
+				if (!HasAuraName(target, GetSpellName(BLESSING_OF_KINGS))) doCast(target, BLESSING_OF_KINGS, true);
+				break;
+			}
+
+		}
+
+
+		void JustDied(Unit *Killer)
+		{
+			master->SetBotCommandState(COMMAND_FOLLOW);
+		}
+
+		void AttackStart(Unit *u)
+		{
+			Aggro(u);
+			ScriptedAI::AttackStart(u);
+		}
+
+		void KilledUnit(Unit *)
+		{
+			((Player*)master)->SetBotCommandState(COMMAND_FOLLOW);
+		}
+
+		void Counter(const uint32 diff)
+		{
+			if(opponent == NULL) return;
+			if(opponent->isDead()) return;
+			if(opponent->IsNonMeleeSpellCasted(true) && isTimerReady(HOJ_Timer))
+			{
+				doCast(opponent, HAMMER_OF_JUSTICE);
+				HOJ_Timer = 600;
+			}
+		}
+
+		void DoNormalAttack(const uint32 diff)
+		{
+			if(!opponent->HasAura(JUDGEMENT_OF_LIGHT) && isTimerReady(GC_Timer))
+				doCast(opponent, JUDGEMENT_OF_LIGHT, true);
+
+		}
+
+		void ReduceCD(const uint32 diff)
+		{
+			if(GC_Timer > 0)        --GC_Timer;
+			if(LOH_Timer > 0)       --LOH_Timer;
+			if(HOJ_Timer > 0)       --HOJ_Timer;
+		}
+		void ReceiveBowEmote(Player *player)
+		{
+			BuffTarget(player);
+		}
+	};
+};
+
+void AddSC_paladin_bot()
+{
+    new paladin_bot();
+}
diff --git a/src/server/scripts/Bots/bot_paladin_ai.h b/src/server/scripts/Bots/bot_paladin_ai.h
new file mode 100644
index 0000000..f6ccab8
--- /dev/null
+++ b/src/server/scripts/Bots/bot_paladin_ai.h
@@ -0,0 +1,62 @@
+
+#include "bot_ai.h"
+
+
+enum AURAS {
+    DEVOTIONAURA,
+    FIRERESISTANCEAURA,
+    NOAURA
+} ;
+
+AURAS Aura;
+
+
+// misc
+#define BANDAGE 27031
+#define MANAPOTION 28499
+#define REJUVEPOTION 28517
+#define HEALINGPOTION 28495
+
+// Heals
+#define FLASH_OF_LIGHT                FLASH_OF_LIGHT_A[SPELL_LEVEL]
+#define HOLY_LIGHT                    HOLY_LIGHT_A[SPELL_LEVEL]
+#define LAY_OF_HANDS                  LAY_OF_HANDS_A[SPELL_LEVEL]
+#define REDEMPTION                    REDEMPTION_A[SPELL_LEVEL]
+
+#define CLEANSE                         CLEANSE_A[SPELL_LEVEL]
+
+uint32 FLASH_OF_LIGHT_A[] = {0, 0, 19939, 19940, 19941, 19942, 27137, 48784, 48785, 48785};
+uint32 HOLY_LIGHT_A[] = {635, 647, 1026, 3472, 10328, 10329, 27135, 48781, 48782, 48782};
+uint32 LAY_OF_HANDS_A[] = {0, 633, 633, 2800, 2800, 10310, 10310, 27154, 48788, 48788};
+uint32 REDEMPTION_A[] = {0, 7328, 10322, 10324, 20772, 20772, 20773, 48949, 48950, 48950};
+uint32 CLEANSE_A[] = {0, 0, 0, 0, 4987, 4987, 4987, 4987, 4987};
+
+// Seals
+#define SEAL_OF_LIGHT	        SEAL_OF_LIGHT_A[SPELL_LEVEL]
+#define JUDGEMENT_OF_LIGHT      JUDGEMENT_OF_LIGHT_A[SPELL_LEVEL]
+
+uint32 SEAL_OF_LIGHT_A[] = {0, 0, 0, 20165, 20165, 20165, 20165, 20165, 20165, 20165};
+uint32 JUDGEMENT_OF_LIGHT_A[] = {0, 0, 0, 20185, 20185, 20185, 20185, 20185, 20185, 20185};
+
+// Blessings
+#define BLESSING_OF_WISDOM      BLESSING_OF_WISDOM_A[SPELL_LEVEL]
+#define BLESSING_OF_KINGS       BLESSING_OF_KINGS_A[SPELL_LEVEL]
+#define BLESSING_OF_SANCTUARY   BLESSING_OF_SANCTUARY_A[SPELL_LEVEL]
+
+uint32 BLESSING_OF_WISDOM_A[] = {0, 19742, 19850, 19852, 19853, 19854, 25290, 48935, 48936, 48936};
+uint32 BLESSING_OF_KINGS_A[] = {0, 0, 56525, 56525, 56525, 56525, 56525, 56525, 56525, 56525};
+uint32 BLESSING_OF_SANCTUARY_A[] = {0, 0, 0, 67480, 67480, 67480, 67480, 67480, 67480, 67480};
+
+
+// Auras
+#define FIRE_RESISTANCE_AURA      FIRE_RESISTANCE_AURA_A[SPELL_LEVEL]
+#define DEVOTION_AURA             DEVOITION_AURA_A[SPELL_LEVEL]
+
+uint32 FIRE_RESISTANCE_AURA_A[] = {0, 0, 0, 19891, 19899, 19899, 19900, 27153, 48947, 48947};
+uint32 DEVOITION_AURA_A[] = {465, 10290, 643, 10291, 1032, 10292, 10293, 48941, 48942, 48942};
+
+
+// Others
+#define HAMMER_OF_JUSTICE         HAMMER_OF_JUSTICE_A[SPELL_LEVEL]
+
+uint32 HAMMER_OF_JUSTICE_A[] = {0, 853, 5588, 5588, 5589, 10308, 10308, 37369, 37369};
diff --git a/src/server/scripts/Bots/bot_priest_ai.cpp b/src/server/scripts/Bots/bot_priest_ai.cpp
new file mode 100644
index 0000000..f6a6941
--- /dev/null
+++ b/src/server/scripts/Bots/bot_priest_ai.cpp
@@ -0,0 +1,276 @@
+#include "ScriptPCH.h"
+#include "bot_priest_ai.h"
+#include "Group.h"
+
+class priest_bot : public CreatureScript
+{
+public:
+    priest_bot() : CreatureScript("priest_bot") { }
+
+    CreatureAI *GetAI(Creature *pCreature) const
+    {
+        return new priest_botAI(pCreature);
+    }
+
+	struct priest_botAI : public bot_ai
+	{
+		priest_botAI(Creature *c) :bot_ai(c)
+		{
+			Reset();
+		}
+
+		int32 Heal_Timer;
+		int32 Renew_Timer;
+		int32 Self_Renew_Timer;
+		int32 PWS_Timer;
+		int32 Others_Heal_Timer;
+		int32 Oom_timer;
+		int32 Fade_Timer;
+		int32 Potion_Timer;
+		int32 Rez_Timer;
+
+		Unit *mobsTarget;
+
+		void Reset()
+		{
+			GC_Timer = 0;
+			Heal_Timer = 0;
+			Renew_Timer = 0;
+			Self_Renew_Timer = 0;
+			PWS_Timer = 0;
+			Others_Heal_Timer = 0;
+			Oom_timer = 0;
+			Fade_Timer = 0;
+			Potion_Timer = 0;
+			Rez_Timer = 0;
+
+			if (master) {
+				setStats(CLASS_PRIEST, m_creature->getRace(), master->getLevel());
+			}
+		}
+
+		void EnterEvadeMode(){ Oom_timer = 0; }
+
+		void Aggro(Unit *who){}
+
+		void AttackStart(Unit *u)
+		{
+			   m_creature->GetMotionMaster()->MoveFollow(master, urand(5, 10), PET_FOLLOW_ANGLE);
+
+		}
+
+		void KilledUnit(Unit *)
+		{
+			((Player*)master)->SetBotCommandState(COMMAND_FOLLOW);
+		}
+
+		void MoveInLineOfSight(Unit *target)
+		{
+			if (master==NULL || master==0) return;
+
+			if(target->GetGUID() == master->GetGUID()) return;
+
+			if(!target->IsFriendlyTo(master)) return;
+
+			if(target->isAlive() && ((target->GetHealth()*100) < 100))
+				m_creature->CastSpell(target, SPELL_HEAL, false);
+		}
+
+		bool isTimerReady(int32 timer)
+		{
+			if(timer <= 0 && GC_Timer <= 0) return true;
+			else                            return false;
+		} //end isTimerReady
+
+		void decrementTimers()
+		{
+			if(GC_Timer > 0)          --GC_Timer;
+			if(Heal_Timer > 0)        --Heal_Timer;
+			if(Others_Heal_Timer > 0) --Others_Heal_Timer;
+			if(Fade_Timer > 0)        --Fade_Timer;
+			if(Self_Renew_Timer > 0)  --Self_Renew_Timer;
+			if(Renew_Timer > 0)       --Renew_Timer;
+			if(PWS_Timer > 0)         --PWS_Timer;
+			if(Potion_Timer > 0)      --Potion_Timer;
+		} //end decrementTImers
+
+		void UpdateAI(const uint32 diff)
+		{
+			decrementTimers();
+
+			if(IAmDead()) return;
+
+			if(!m_creature->isInCombat())
+			{
+				DoNonCombatActions();
+			}
+
+			//buff and heal master's group
+			BuffAndHealGroup(master);
+
+
+			//check group members
+			Group::MemberSlotList const &a =((Player*)master)->GetGroup()->GetMemberSlots();
+			for(Group::member_citerator itr = a.begin(); itr != a.end(); itr++)
+			{
+				Player *tPlayer = ((Player *)master)->GetObjPlayer(itr->guid);
+				if(tPlayer == NULL) continue;
+			}
+
+			//if low on mana, drink a potion
+			if(m_creature->GetPower(POWER_MANA) < 400 && isTimerReady(Potion_Timer))
+			{
+				 doCast(m_creature, MANAPOTION, true);
+				 Potion_Timer = 1500;
+			}
+			//if after drinking a potion still low on mana
+			//let everyone know that you are oom.
+			if(m_creature->GetPower(POWER_MANA)/m_creature->GetMaxPower(POWER_MANA) < 10)
+			{
+				if(Oom_timer == 0)
+				{
+					//MonsterSay("OOM", LANG_UNIVERSAL, NULL);
+					Oom_timer = 1;
+				}
+			}
+
+			// Heal myself
+			if(m_creature->GetHealth()*100 / m_creature->GetMaxHealth() < 90)
+			{
+				if(Fade_Timer <= 0 && m_creature->isInCombat() &&
+					isTimerReady(Fade_Timer))
+				{
+					doCast(m_creature, SPELL_FADE);
+					Fade_Timer = 30;
+					return;
+				}
+
+
+				HealTarget (m_creature, m_creature->GetHealth()*100 / m_creature->GetMaxHealth());
+
+			}
+
+			//now try to heal bots and pets.  DoSelectLowestHpFriendly will get
+			//everyone in group including bots and pets.  Unfortunately it can
+			//not be triggered for % of lost HP, so we just set it to -1000.
+			//This means low level players wont be healed because they wont have
+			//enough HP.
+			if(isTimerReady(Others_Heal_Timer))
+			{
+				Unit *target;
+				if(target = DoSelectLowestHpFriendly(40, 1000))
+				{
+					doCast(target, SPELL_HEAL, false);
+					GiveManaBack();
+					Others_Heal_Timer = 50;
+				}
+				else if(target = DoSelectLowestHpFriendly(40, 500))
+				{
+					if(!target->HasAura(SPELL_RENEW, 0))
+					{
+						doCast(target, SPELL_RENEW, false);
+						GiveManaBack();
+						Others_Heal_Timer = 100;
+					}
+				}
+			} //end if isTimerReady(Others_Heal_Timer)
+		} //end UpdateAI
+
+		bool HealTarget(Unit *target, uint8 hp)
+		{
+		   if (!isTimerReady(GC_Timer)) return false;
+			if (m_creature->IsNonMeleeSpellCasted(true)) return false;
+			if(!target || target->isDead()) return false;
+		   // if(hp < 60) { doCast(target, FLASH_OF_LIGHT); GiveManaBack(15); return true; }
+		   // if(hp < 80) { doCast(target, HOLY_LIGHT); GiveManaBack(20); return true; }
+
+		   if((hp < 50) &&
+				isTimerReady(PWS_Timer) &&
+				!target->isDead())
+			{
+				doCast(target, SPELL_PW_SHIELD);
+				PWS_Timer = 120;
+				//free (buff);
+			}
+
+			if((hp < 90) &&
+				hp >75 &&
+				isTimerReady(Renew_Timer))
+			{
+				doCast(target, SPELL_RENEW, true);
+				GiveManaBack();
+				Renew_Timer = 90;
+				Heal_Timer = Heal_Timer + 5; //wait 5 seconds before casting a real heal
+				return true;
+			}
+
+			if((hp < 75) &&
+				isTimerReady(Heal_Timer) &&
+				!target->isDead())
+			{
+				doCast(target, SPELL_HEAL);
+				GiveManaBack();
+				Heal_Timer = 10;
+				return true;
+			}
+
+			return true;
+		}
+
+		void BuffTarget(Unit *target)
+		{
+			if (!HasAuraName(target, GetSpellName(SPELL_FORTITUDE))) {
+				doCast(target, SPELL_FORTITUDE, false);
+				GiveManaBack();
+			}
+			if (!HasAuraName(target, GetSpellName(SPELL_PRAYER_OF_SHADOW))) {
+				doCast(target, SPELL_PRAYER_OF_SHADOW, false);
+				GiveManaBack();
+			}
+		}
+
+		void DoNonCombatActions()
+		{
+			   //if eating or drinking don't do anything
+				if(m_creature->HasAura(10256) || m_creature->HasAura(1137)) return;
+
+				Feast();
+
+				//buff master
+				if(!HasAuraName(master, SPELL_PRAYER_OF_SHADOW, 0) && isTimerReady(GC_Timer)) doCast(master, SPELL_PRAYER_OF_SHADOW, true);
+				if(!HasAuraName(master, FEAR_WARD, 0) && isTimerReady(GC_Timer)) doCast(master, FEAR_WARD, true);
+				if(!HasAuraName(master, DIVINE_SPIRIT, 0) && isTimerReady(GC_Timer)) {
+					doCast(master, DIVINE_SPIRIT, true);
+					GiveManaBack();
+				}
+
+				//buff myself
+				if(!m_creature->HasAura(SPELL_INNER_FIRE, 0) && isTimerReady(GC_Timer)) doCast(m_creature, SPELL_INNER_FIRE, true);
+				if(!m_creature->HasAura(SPELL_FORTITUDE, 0) && isTimerReady(GC_Timer)) doCast(m_creature, SPELL_FORTITUDE, true);
+				//if(m_creature->getRace() == RACE_UNDEAD_PLAYER && !m_creature->HasAura(SPELL_TOUCH_OF_WEAKNESS, 0) && isTimerReady(GC_Timer)) doCast(m_creature, SPELL_TOUCH_OF_WEAKNESS);
+
+	//buff and heal master's group
+			if(master->GetGroup())
+			{
+				RezGroup(SPELL_RESURRECTION, master);
+				//BuffAndHealGroup(master);
+			   // CureGroup(master);
+			}
+
+
+		}
+
+		void ReceiveBowEmote(Player *player)
+		{
+			((priest_botAI*)m_creature->AI())->doCast(player, SPELL_FORTITUDE, true);
+			((priest_botAI*)m_creature->AI())->doCast(player, SPELL_PRAYER_OF_SHADOW, false);
+		}
+
+	}; //end priest_bot
+};
+
+
+void AddSC_priest_bot()
+{
+    new priest_bot();
+}
diff --git a/src/server/scripts/Bots/bot_priest_ai.h b/src/server/scripts/Bots/bot_priest_ai.h
new file mode 100644
index 0000000..09bf1bb
--- /dev/null
+++ b/src/server/scripts/Bots/bot_priest_ai.h
@@ -0,0 +1,33 @@
+#include "bot_ai.h"
+
+
+#define FEAR_WARD   6346
+
+#define SPELL_PW_SHIELD           SPELL_PW_SHIELD_A[SPELL_LEVEL]
+//#define SPELL_DIVINE_SPIRIT     14818
+
+#define SPELL_FORTITUDE           SPELL_FORTITUDE_A[SPELL_LEVEL]
+#define SPELL_HEAL                SPELL_HEAL_A[SPELL_LEVEL]
+#define SPELL_RENEW               SPELL_RENEW_A[SPELL_LEVEL]
+#define SPELL_FADE                SPELL_FADE_A[SPELL_LEVEL]
+#define SPELL_RESURRECTION        SPELL_RESURRECTION_A[SPELL_LEVEL]
+//define SPELL_VAMPIRIC_EMBRACE
+//#define SPELL_INNER_FOCUS       14751
+
+//BUFFS
+#define SPELL_INNER_FIRE          SPELL_INNER_FIRE_A[SPELL_LEVEL]
+#define SPELL_PRAYER_OF_SHADOW    SPELL_PRAYER_OF_SHADOW_A[SPELL_LEVEL]
+#define DIVINE_SPIRIT               DIVINE_SPIRIT_A[SPELL_LEVEL]
+//#define SPELL_TOUCH_OF_WEAKNESS 19265 //rank 5
+
+uint32 SPELL_FORTITUDE_A[] = { 1243, 1244, 1245, 2791, 10937, 10938, 25389, 48161, 48161 };
+uint32 SPELL_RENEW_A[] = { 8362, 11640, 6075, 6077, 10927, 10928, 25315, 48068, 48068 };
+uint32 SPELL_HEAL_A[] = { 29170, 29170, 2055, 6064, 10963, 10964, 10965, 25213, 25213 };
+uint32 SPELL_PW_SHIELD_A[] = { 17, 592, 3747, 6066, 10899, 10900, 10901, 25218, 25218 };
+uint32 SPELL_FADE_A[] = { 586, 586, 586, 586, 586, 586, 586, 586, 586 };
+uint32 SPELL_PRAYER_OF_SHADOW_A[] = { 0, 0, 0, 0, 0, 27683, 27683, 39374, 39374, 39374 };
+
+uint32 SPELL_INNER_FIRE_A[] = { 588, 7128, 602, 1006, 10951, 10952, 25431, 25431, 48040, 48040 };
+uint32 DIVINE_SPIRIT_A[] = {0, 0, 0, 14752, 14818, 14819, 27841, 25312, 48073, 48073};
+
+uint32 SPELL_RESURRECTION_A[] = { 2006, 2006, 2006, 2010, 10880, 10881, 20770, 25435, 25435 };
diff --git a/src/server/scripts/Bots/bot_rogue_ai.cpp b/src/server/scripts/Bots/bot_rogue_ai.cpp
new file mode 100644
index 0000000..916a991
--- /dev/null
+++ b/src/server/scripts/Bots/bot_rogue_ai.cpp
@@ -0,0 +1,313 @@
+#include "ScriptPCH.h"
+#include "bot_rogue_ai.h"
+
+class rogue_bot : public CreatureScript
+{
+public:
+    rogue_bot() : CreatureScript("rogue_bot") { }
+
+    CreatureAI *GetAI(Creature *pCreature) const
+    {
+        return new rogue_botAI(pCreature);
+    }
+
+struct rogue_botAI : public bot_ai
+{
+    rogue_botAI(Creature *c) : bot_ai(c)
+    {
+        Reset();
+    }
+
+    int32 GC_Timer;                // global cooldown
+    int32 BS_Timer;
+    int32 SinisterStrike_Timer;
+    int32 Eviscerate_Timer;
+    int32 SliceDice_Timer;
+    int32 Rupture_Timer;
+    int32 Kick_Timer;
+    int32 Poison_Timer;
+    int32 Potion_Timer;
+    int32 Shadowstep_Timer;
+    int32 Mutilate_Timer;
+
+    uint8 energy;
+    uint8 comboPoints;
+
+    Unit *opponent;
+
+    void Reset()
+    {
+        GC_Timer = 0;
+        BS_Timer = 50;
+        Mutilate_Timer = 0;
+        SinisterStrike_Timer = 30;
+        Eviscerate_Timer = 90;
+        SliceDice_Timer = 75;
+        Rupture_Timer = 80;
+        Kick_Timer = 20;
+        Poison_Timer = 15;
+        Potion_Timer=0;
+        Shadowstep_Timer = 0;
+        comboPoints = 0;
+        energy = 100;
+
+        opponent = NULL;
+
+        m_creature->setPowerType(POWER_ENERGY);
+        m_creature->SetMaxPower(POWER_ENERGY, 100);
+        m_creature->SetPower(POWER_ENERGY, 100);
+
+        if (master) {
+            //        if (!m_creature->HasAura(61331)) m_creature->AddAura(61331,m_creature);  // Aggression
+            //         if (!m_creature->HasAura(14137)) m_creature->AddAura(14137,m_creature);  // Lethality
+            //          if (!m_creature->HasAura(14166)) m_creature->AddAura(14166,m_creature);  // Improved Slice and Dice
+            //           if (!m_creature->HasAura(14164)) m_creature->AddAura(14164,m_creature);  // Improved Eviserate
+
+            setStats(CLASS_ROGUE, m_creature->getRace(), master->getLevel());
+
+            if (m_creature->getLevel()>70 && !m_creature->HasAura(37169)) m_creature->AddAura(37169,m_creature);  // Death Mantle item set
+        }
+
+    }
+
+    void EnterEvadeMode(){}
+
+    void Aggro(Unit *who){}
+
+    void MoveInLineOfSight(Unit *target){}
+
+    bool isTimerReady(int32 timer)
+    {
+        if(timer <= 0 && GC_Timer <= 0) return true;
+        else                            return false;
+    } //end isTimerReady
+
+    void doCast(Unit *victim, uint32 spellId, bool triggered = false)
+    {
+        if(spellId == 0) return;
+        m_creature->SetStandState(UNIT_STAND_STATE_STAND);
+        GC_Timer = 10;
+        DoCast(victim, spellId, triggered);
+    } //end doCast
+
+    void decrementTimers()
+    {
+        if(GC_Timer > 0)             --GC_Timer;
+        if(BS_Timer > 0)             --BS_Timer;
+        if(Kick_Timer > 0)           --Kick_Timer;
+        if(SliceDice_Timer > 0)      --SliceDice_Timer;
+        if(SinisterStrike_Timer > 0) --SinisterStrike_Timer;
+        if(Eviscerate_Timer > 0)     --Eviscerate_Timer;
+        if(Rupture_Timer > 0)        --Rupture_Timer;
+        if(Poison_Timer > 0)         --Poison_Timer;
+        if(Potion_Timer > 0)         --Potion_Timer;
+        if(Shadowstep_Timer > 0)     --Shadowstep_Timer;
+        if (Mutilate_Timer > 0)            Mutilate_Timer--;
+    } //end decrementTImers
+
+    void KilledUnit(Unit *)
+    {
+        ((Player*)master)->SetBotCommandState(COMMAND_FOLLOW);
+    }
+
+    void AttackStart(Unit *u)
+    {
+        Aggro(u);
+        ScriptedAI::AttackStart(u);
+    }
+
+    void UpdateAI(const uint32 diff)
+    {
+        decrementTimers();
+
+        if(IAmDead()) return;
+
+        opponent = SelectUnit(SELECT_TARGET_TOPAGGRO, 0);
+        if(!opponent && !m_creature->getVictim())
+        {
+            comboPoints = 0;
+            energy = 100;
+            ResetOrGetNextTarget();
+            return;
+        }
+
+        m_creature->ApplySpellImmune(0, IMMUNITY_STATE, SPELL_AURA_MOD_TAUNT, true);
+        m_creature->ApplySpellImmune(0, IMMUNITY_EFFECT, SPELL_EFFECT_ATTACK_ME, true);
+
+        energy += 5;
+        if(comboPoints > 5) comboPoints = 5;
+
+        //interrupt any spells
+        if(opponent->IsNonMeleeSpellCasted(true) && energy >= 25)
+        {
+            if(isTimerReady(Kick_Timer))
+            {
+                doCast(opponent, KICK);
+                Kick_Timer = 100;
+                energy -= 25;
+            }
+            else if(comboPoints > 0)
+            {
+                switch(comboPoints)
+                {
+                    case 1: doCast(opponent, KIDNEY_SHOT_1); --comboPoints; break;
+                    case 2: doCast(opponent, KIDNEY_SHOT_2); comboPoints-=2; break;
+                    default: doCast(opponent, KIDNEY_SHOT_3); comboPoints-=3; break;
+                }
+                energy -= 25;
+            }
+        }
+
+        if(!opponent->isInFrontInMap(m_creature, 5) && isTimerReady(BS_Timer))
+        {
+            doCast(opponent, BACKSTAB);
+            BS_Timer = 50;
+            energy -= 60;
+        }
+        else if(isTimerReady(Shadowstep_Timer) && energy > 10 && m_creature->GetDistance(opponent) < 25)
+        {
+            //doCast(opponent, SHADOWSTEP);
+            Shadowstep_Timer = 300;
+            energy -= 10;
+
+            //NPCs can't really shadowstep so fake it
+            float x = opponent->GetPositionX();
+            float y = opponent->GetPositionY();
+            float z = opponent->GetPositionZ();
+            float o = opponent->GetOrientation();
+
+            m_creature->Relocate(x-4, y-4, z, o);
+            m_creature->SendMonsterMoveWithSpeed(x-4, y-4, 1, 0);
+            doCast(opponent, BACKSTAB);
+            return;
+        }
+
+        if(isTimerReady(SliceDice_Timer) && comboPoints > 0 && !m_creature->HasAura(SLICE_DICE))
+        {
+            doCast(opponent, SLICE_DICE);
+
+            // since npcs don't use combo points, they can only case
+            // first level of spell.  So only remove 1 combo point
+            --comboPoints;
+
+            SliceDice_Timer = 75;
+            energy -= 25;
+        }
+
+        if (isTimerReady(Mutilate_Timer) &&
+            energy>60) {
+            // TODO: calculate correct dmg for mutilate (dont forget poison bonus)
+            // for now use same formula as evicerate
+            uint32 base_attPower = m_creature->GetUInt32Value(UNIT_FIELD_ATTACK_POWER);
+            //float minDmg = m_creature->GetFloatValue(UNIT_FIELD_MINDAMAGE);
+            float minDmg = m_creature->GetWeaponDamageRange(BASE_ATTACK, MINDAMAGE);
+            int damage = irand(int32(base_attPower*5*0.03f),int32(base_attPower*5*0.07f))+minDmg+m_creature->getLevel();
+
+            // compensate for lack of attack power
+            damage = damage*(rand()%4+1);
+
+            m_creature->CastCustomSpell(opponent, MUTILATE, &damage, NULL, NULL, false, NULL, NULL);
+
+            //doCast (m_creature, MUTILATE);
+            Mutilate_Timer = 75;
+            ++comboPoints;
+            energy -= 60;
+        }
+
+        if (isTimerReady(Eviscerate_Timer) && comboPoints>0) {
+            uint32 base_attPower = m_creature->GetUInt32Value(UNIT_FIELD_ATTACK_POWER);
+            //float minDmg = m_creature->GetFloatValue(UNIT_FIELD_MINDAMAGE);
+            float minDmg = m_creature->GetWeaponDamageRange(BASE_ATTACK, MINDAMAGE);
+            int damage = irand(int32(base_attPower*5*0.03f),int32(base_attPower*5*0.07f))+minDmg+m_creature->getLevel();
+//sLog->outError ("EVISCERTE: base_attPower = %u", base_attPower);
+//sLog->outError("\tminDmg = %f", minDmg);
+//sLog->outError("\tmaxDmg = %f", m_creature->GetWeaponDamageRange(BASE_ATTACK, MAXDAMAGE));
+//sLog->outError ("\tMINDAMAGE after setting it = %f", m_creature->GetWeaponDamageRange(BASE_ATTACK, MINDAMAGE));
+
+// compensate for lack of attack power
+//sLog->outError ("\tdamage before = %u", damage);
+            damage = damage*(rand()%4+1);
+//sLog->outError ("\tdamage after = %u", damage);
+            // Eviscerate and Envenom Bonus Damage (Deathmantle item set effect)
+            if(m_creature->HasAura(37169))
+                damage += comboPoints*40;
+
+            m_creature->CastCustomSpell(opponent, EVISCERATE, &damage, NULL, NULL, false, NULL, NULL);
+            //doCast(opponent, EVISCERATE);
+            comboPoints=0;
+
+            energy -= 30;
+            Eviscerate_Timer = 90;
+         //   return;
+        }
+
+        if(isTimerReady(SinisterStrike_Timer) && comboPoints < 5)
+        {
+            doCast(opponent, SINISTER_STRIKE);
+            //m_creature->Say("sinister strike", LANG_UNIVERSAL, NULL);
+            ++comboPoints;
+            SinisterStrike_Timer = 20;
+            energy -= 40;
+        }
+
+        if(isTimerReady(Rupture_Timer))
+        {
+            doCast(opponent, RUPTURE);
+            comboPoints = 0;
+            Rupture_Timer = 80;
+            energy -= 40;
+        }
+
+        /*if(!opponent->HasAuraType(SPELL_AURA_MOD_DISARM))
+            doCast(opponent, DISMANTLE);*/
+
+        /*
+            since npc can't really use poison, we'll pretend that we were able to poison his blades.
+        */
+        if(isTimerReady(Poison_Timer))
+        {
+            //Deadly Poison has 40% chance of proccing
+            switch(rand()%5)
+            {
+                case 0:
+                case 1:
+                case 2:
+                    break;
+                case 4:
+                case 5:
+                    if(DEADLY_POISON) DoCast(opponent, DEADLY_POISON, true); break;
+            }
+
+            //Wound Poison has 50% chance of procing
+            switch(rand()%2)
+            {
+                case 0:
+                     break;
+                case 1:
+                    if(WOUND_POISON) DoCast(opponent, WOUND_POISON, true); break;
+                    break;
+            }
+
+            Poison_Timer = 15;
+        }
+
+        //if low on health, drink a potion
+        if(m_creature->GetHealth() < m_creature->GetMaxHealth()*0.6 && isTimerReady(Potion_Timer))
+        {
+            doCast(m_creature, HEALINGPOTION);
+            Potion_Timer = 1500;
+        }
+
+        m_creature->SetPower(POWER_ENERGY, energy);
+
+        ScriptedAI::UpdateAI(diff);
+    } //end UpdateAI
+
+
+}; //end rogue_bot
+};
+
+void AddSC_rogue_bot()
+{
+    new rogue_bot();
+}
diff --git a/src/server/scripts/Bots/bot_rogue_ai.h b/src/server/scripts/Bots/bot_rogue_ai.h
new file mode 100644
index 0000000..c7a295a
--- /dev/null
+++ b/src/server/scripts/Bots/bot_rogue_ai.h
@@ -0,0 +1,30 @@
+#include "bot_ai.h"
+
+
+#define BACKSTAB        BACKSTAB_A[SPELL_LEVEL]
+#define SINISTER_STRIKE SINISTER_STRIKE_A[SPELL_LEVEL]
+#define SLICE_DICE      SLICE_DICE_A[SPELL_LEVEL]
+#define EVISCERATE      EVISCERATE_A[SPELL_LEVEL]
+#define KICK            KICK_A[SPELL_LEVEL]
+#define RUPTURE         RUPTURE_A[SPELL_LEVEL]
+#define WOUND_POISON    WOUND_POISON_A[SPELL_LEVEL]
+#define DEADLY_POISON   DEADLY_POISON_A[SPELL_LEVEL]
+#define DISMANTLE       DISMANTLE_A[SPELL_LEVEL]
+#define KIDNEY_SHOT_1   8643
+#define KIDNEY_SHOT_2   30832
+#define KIDNEY_SHOT_3   41389
+#define SHADOWSTEP      SHADOWSTEP_A[SPELL_LEVEL]
+#define MUTILATE        MUTILATE_A[SPELL_LEVEL]
+
+
+uint32 MUTILATE_A[] = {0, 0, 0, 0, 48664, 48664, 48664,48664, 48664};
+uint32 BACKSTAB_A[] = { 53, 2589, 2591, 8721, 11279, 11280, 11281, 25300, 25300 };
+uint32 SINISTER_STRIKE_A[] = { 1757, 1758, 1759, 8621, 11293, 11294, 26862, 48638, 48638 };
+uint32 SLICE_DICE_A[] = { 0, 5171, 5171, 5171, 6774, 6774, 6774, 6774, 6774, 6774 };
+uint32 EVISCERATE_A[]= { 11300, 11300, 11300, 11300, 11300, 11300, 11300, 11300, 11300 };
+uint32 KICK_A[] = { 0, 1766, 1767, 1767, 1768, 1769, 38768, 38768, 38768 };
+uint32 RUPTURE_A[] = { 0, 0, 1943, 8640, 11273, 11274, 11275, 26867, 48672, 48672 };
+uint32 WOUND_POISON_A[] = { 0, 0, 0, 13218, 13222, 13223, 13224, 27189, 57974, 57974 };
+uint32 DEADLY_POISON_A[] = { 0, 0, 0, 2818, 2819, 11354, 26968, 57969, 57969 };
+uint32 DISMANTLE_A[] = { 0, 0, 51722, 51722, 51722, 51722, 51722, 51722, 51722 };
+uint32 SHADOWSTEP_A[] = { 0, 0, 0, 0, 0, 36554, 36554, 36554, 36554 };
diff --git a/src/server/scripts/Bots/bot_shaman_ai.cpp b/src/server/scripts/Bots/bot_shaman_ai.cpp
new file mode 100644
index 0000000..398ca51
--- /dev/null
+++ b/src/server/scripts/Bots/bot_shaman_ai.cpp
@@ -0,0 +1,385 @@
+#include "ScriptPCH.h"
+#include "bot_shaman_ai.h"
+
+class shaman_bot : public CreatureScript
+{
+public:
+    shaman_bot() : CreatureScript("shaman_bot") { }
+
+    CreatureAI *GetAI(Creature *pCreature) const
+    {
+        return new shaman_botAI(pCreature);
+    }
+
+struct shaman_botAI : public bot_ai
+{
+    shaman_botAI(Creature *c) : bot_ai(c)
+    {
+        Reset();
+    }
+
+    int32 GC_Timer; //global cooldown
+    int32 Heal_Timer;
+    int32 Lesser_Healing_Timer;
+    int32 Self_Lesser_Healing_Timer;
+    int32 Flame_Shock_Timer;
+    int32 Earth_Shock_Timer;
+    int32 Lightning_Bolt_Timer;
+    int32 Others_Heal_Timer;
+    int32 Oom_timer;
+    int32 Potion_Timer;
+    int32 Rez_Timer;
+    int32 Earth_Totem_Timer;
+    int32 Water_Totem_Timer;
+    int32 Fire_Totem_Timer;
+    int32 Wind_Totem_Timer;
+
+    Unit *mobsTarget;
+    Unit *opponent;
+
+    void Reset()
+    {
+        GC_Timer = 0;
+        Heal_Timer = 0;
+        Lesser_Healing_Timer = 0;
+        Self_Lesser_Healing_Timer = 0;
+        Flame_Shock_Timer = 20;
+        Lightning_Bolt_Timer = 60;
+        Earth_Shock_Timer = 150;
+        Others_Heal_Timer = 0;
+        Oom_timer = 0;
+
+        Earth_Totem_Timer = 0;
+        Fire_Totem_Timer = 0;
+        Water_Totem_Timer = 0;
+        Wind_Totem_Timer = 0;
+
+        Potion_Timer = 0;
+        Rez_Timer = 0;
+
+        opponent = NULL;
+
+        if (master) {
+            setStats(CLASS_SHAMAN, m_creature->getRace(), master->getLevel());
+       }
+    }
+
+    bool isTimerReady(int32 timer)
+    {
+        if(timer <= 0 && GC_Timer <= 0) return true;
+        else                            return false;
+    } //end isTimerReady
+
+    void doCast(Unit *victim, uint32 spellId, bool triggered = false)
+    {
+        if(spellId == 0) return;
+        m_creature->SetStandState(UNIT_STAND_STATE_STAND);
+        GC_Timer = 40;
+        DoCast(victim, spellId, triggered);
+    } //end doCast
+
+    void decrementTimers()
+    {
+        if(GC_Timer > 0)             --GC_Timer;
+        if(Heal_Timer > 0)           --Heal_Timer;
+        if(Others_Heal_Timer > 0)    --Others_Heal_Timer;
+        if(Flame_Shock_Timer > 0)    --Flame_Shock_Timer;
+        if(Earth_Shock_Timer > 0)    --Earth_Shock_Timer;
+        if(Lightning_Bolt_Timer > 0) --Lightning_Bolt_Timer;
+        if(Rez_Timer > 0)            --Rez_Timer;
+        if(Potion_Timer > 0)         --Potion_Timer;
+        if(Earth_Totem_Timer > 0)    --Earth_Totem_Timer;
+        if(Fire_Totem_Timer > 0)     --Fire_Totem_Timer;
+        if(Water_Totem_Timer > 0)    --Water_Totem_Timer;
+        if(Wind_Totem_Timer > 0)     --Wind_Totem_Timer;
+    } //end decrementTImers
+
+    void KilledUnit(Unit *)
+    {
+        ((Player*)master)->SetBotCommandState(COMMAND_FOLLOW);
+    }
+
+    void Aggro(Unit *who){}
+
+    void EnterEvadeMode(){ Oom_timer = 0; }
+
+    void UpdateAI(const uint32 diff)
+    {
+        decrementTimers();
+
+        if(IAmDead()) return;
+
+        if(m_creature->GetPower(POWER_MANA) < 400 &&
+        isTimerReady(Potion_Timer))
+        {
+            doCast(m_creature, MANAPOTION);
+            Potion_Timer = 150;
+        }
+        if(m_creature->GetPower(POWER_MANA)/m_creature->GetMaxPower(POWER_MANA) < 10)
+        {
+            if(Oom_timer == 0)
+            {
+                //MonsterSay("OOM", LANG_UNIVERSAL, NULL);
+                Oom_timer = 1;
+            }
+        }
+
+        BuffAndHealGroup(master);
+
+        // Heal myself
+        HealTarget (m_creature, m_creature->GetHealth()*100 / m_creature->GetMaxHealth());
+
+        //the rest are combat so return if not fighting
+        opponent = SelectUnit(SELECT_TARGET_TOPAGGRO, 0);
+        if(!opponent && !m_creature->getVictim())
+        {
+            DoNonCombatActions();
+            ResetOrGetNextTarget();
+            return;
+        }
+
+        //Cast totems.
+        if(m_creature->isInCombat() &&
+        isTimerReady(Earth_Totem_Timer) &&
+        !master->HasAura(SPELL_STONESKIN_AURA, 0))
+        {
+            doCast(m_creature, SPELL_STONESKIN_TOTEM);
+            GiveManaBack();
+            Earth_Totem_Timer = 90;
+            return;
+        }
+
+        if(m_creature->isInCombat() &&
+        isTimerReady(Fire_Totem_Timer))
+        {
+            doCast(m_creature, SPELL_SEARING_TOTEM);
+            GiveManaBack();
+            Fire_Totem_Timer = 180;
+            return;
+        }
+
+        if(m_creature->isInCombat() &&
+        isTimerReady(Wind_Totem_Timer))
+        {
+            doCast(m_creature, SPELL_WINDFURY_TOTEM);
+            GiveManaBack();
+            Wind_Totem_Timer = 180;
+            return;
+        }
+
+        if(m_creature->isInCombat())
+        {
+            switch(master->getClass())
+            {
+                case CLASS_WARRIOR:
+                case CLASS_DEATH_KNIGHT:
+                case CLASS_ROGUE:
+                {
+                    if(isTimerReady(Water_Totem_Timer) &&
+                    !master->HasAura(SPELL_HEALINGSTREAM_AURA))
+                    {
+                        doCast(m_creature, SPELL_HEALINGSTREAM_TOTEM);
+                        Water_Totem_Timer = 90;
+                        GiveManaBack();
+                        return;
+                    }
+                    break;
+                }
+                default: //everyone else gets a mana totem
+                {
+                    if(isTimerReady (Water_Totem_Timer) &&
+                    !master->HasAura(SPELL_MANASPRING_AURA, 0))
+                    {
+                        doCast(m_creature, SPELL_MANASPRING_TOTEM);
+                        Water_Totem_Timer = 90;
+                        GiveManaBack();
+                        return;
+                    }
+                }
+            } //end switch
+
+        }
+
+
+        if(isTimerReady(Flame_Shock_Timer))
+        {
+            doCast(opponent, SPELL_FLAME_SHOCK);
+            Flame_Shock_Timer = 120;
+            return;
+        }
+
+        if(isTimerReady(Lightning_Bolt_Timer))
+        {
+            doCast(opponent, SPELL_LIGHTNING_BOLT);
+            Lightning_Bolt_Timer = 180;
+            return;
+        }
+
+        if(isTimerReady(Earth_Shock_Timer))
+        {
+            doCast(opponent, SPELL_EARTH_SHOCK);
+            Earth_Shock_Timer = 250;
+            return;
+        }
+
+        //now try to heal bots and pets.  DoSelectLowestHpFriendly will get
+        //everyone in group including bots and pets.  Unfortunately it can
+        //not be triggered for % of lost HP, so we just set it to -1000.
+        //This means low level players wont be healed because they wont have
+        //enough HP.
+        Unit *target = DoSelectLowestHpFriendly(40, 1000);
+        if(target)
+        {
+            if(CanCast(target, GetSpellStore()->LookupEntry (SPELL_CHAIN_HEAL)))
+            {
+                doCast(target, SPELL_CHAIN_HEAL, false);
+                Others_Heal_Timer = 50;
+            }
+        } else {
+            target = DoSelectLowestHpFriendly(40, 500); //now try someone with less HP lost
+            if(target)
+            {
+                if(CanCast(target, GetSpellStore()->LookupEntry (SPELL_CHAIN_HEAL)))
+                {
+                    doCast(target, SPELL_CHAIN_HEAL, false);
+                    Others_Heal_Timer = 100;
+                }
+            }
+        }
+
+        ScriptedAI::UpdateAI(diff);
+
+    } //end UpdateAI
+
+    void DoNonCombatActions()
+    {
+        Feast();
+
+        if(isTimerReady(GC_Timer) && !m_creature->HasAura(SPELL_LIGHTNING_SHIELD, 0))
+            doCast(m_creature, SPELL_LIGHTNING_SHIELD);
+
+        //Casts buffs
+       // if(!m_creature->isInCombat())
+       // {
+            //if(!m_creature->HasAura(SPELL_WINDFURY_WEAPON, 0)) doCast(m_creature, SPELL_WINDFURY_WEAPON);
+ //           if(isTimerReady(GC_Timer) && !m_creature->HasAura(SPELL_LIGHTNING_SHIELD, 0))
+   //             doCast(m_creature, SPELL_LIGHTNING_SHIELD);
+       // }
+
+
+        //Heal/rez others
+        //
+        //check group members, this doesn't check bots/pets.  They will be done later.  Preference
+        //goes to real players first.
+        //
+        //buff and heal group
+        if(master->GetGroup())
+        {
+            RezGroup(SPELL_SHAMAN_REZZ, master);
+            BuffAndHealGroup(master);
+           // CureGroup(master);
+        }
+
+
+        /*
+        Group::MemberSlotList const &a =((Player*)master)->GetGroup()->GetMemberSlots();
+        for(Group::member_citerator itr = a.begin(); itr != a.end(); itr++)
+        {
+            Player *tPlayer = ((Player *)master)->GetObjPlayer(itr->guid);
+            if(tPlayer == NULL) continue;
+            //healing others
+            if(tPlayer->isAlive() &&
+            isTimerReady(Others_Heal_Timer) &&
+            tPlayer->GetGUID() != master->GetGUID() &&
+            tPlayer->GetHealth()*100 / tPlayer->GetMaxHealth() < 75 &&
+            CanCast(tPlayer, GetSpellStore()->LookupEntry (SPELL_CHAIN_HEAL)))
+            {
+                doCast(tPlayer, SPELL_CHAIN_HEAL, false);
+                Others_Heal_Timer = 100;
+            }
+
+            //rezzes
+            if(tPlayer->isDead() &&
+            !m_creature->isInCombat() &&
+            //CanCast(tPlayer, GetSpellStore()->LookupEntry (SPELL_SHAMAN_REZZ)) &&
+            m_creature->GetDistance(tPlayer) < 40 &&
+            isTimerReady(Rez_Timer))
+            {
+                char *str = (char *)malloc(32);
+                sprintf(str, "Rezzing %s", tPlayer->GetName());
+                m_creature->MonsterSay(str, LANG_UNIVERSAL, NULL);
+                free(str);
+                doCast(tPlayer, SPELL_SHAMAN_REZZ, false);
+                Rez_Timer = 160;
+            }
+        }
+        */
+
+
+
+/*
+        if((master->GetHealth()*100 / master->GetMaxHealth() < 90) && Lesser_Healing_Timer <= 0)
+        {
+            doCast(master, SPELL_LESSER_HEALING);
+            Lesser_Healing_Timer = 90;
+            Heal_Timer = Heal_Timer + 5; //wait 5 seconds before casting a real heal
+            //if(master->isInCombat()) && master->getVictim() == NULL) return;
+            return;
+        } else if(Lesser_Healing_Timer >= 0) --Lesser_Healing_Timer;
+
+        if((master->GetHealth()*100 / master->GetMaxHealth() < 75) && isTimerReady(Heal_Timer))
+        {
+            doCast(master, SPELL_CHAIN_HEAL);
+            Heal_Timer = 10;
+        }
+
+        if(m_creature->GetHealth()*100 / m_creature->GetMaxHealth() < 90)
+        {
+            if(Self_Lesser_Healing_Timer <= 0)
+            {
+                doCast(m_creature, SPELL_LESSER_HEALING);
+                Self_Lesser_Healing_Timer = 90;
+                return;
+            } else if(Self_Lesser_Healing_Timer >= 0)
+                --Self_Lesser_Healing_Timer;
+        }
+*/
+        }
+
+    bool HealTarget(Unit *target, uint8 hp)
+    {
+        if (!isTimerReady(GC_Timer)) return false;
+        if (m_creature->IsNonMeleeSpellCasted(true)) return false;
+        if(!target || target->isDead()) return false;
+
+        if(hp < 90 && Lesser_Healing_Timer <= 0)
+        {
+            doCast(target, SPELL_LESSER_HEALING);
+            Lesser_Healing_Timer = 90;
+            Heal_Timer = Heal_Timer + 5; //wait 5 seconds before casting a real heal
+            //if(master->isInCombat()) && master->getVictim() == NULL) return;
+            return true;
+        } else if(Lesser_Healing_Timer >= 0) --Lesser_Healing_Timer;
+
+        if(hp < 75 && isTimerReady(Heal_Timer))
+        {
+            doCast(target, SPELL_CHAIN_HEAL);
+            Heal_Timer = 10;
+        }
+        return true;
+    } //end HealTarget
+
+    void ReceiveBowEmote(Player *player)
+    {
+        doCast(m_creature, SPELL_MANASPRING_TOTEM);
+    }
+
+
+}; //end shaman_bot
+};
+
+
+void AddSC_shaman_bot()
+{
+    new shaman_bot();
+}
diff --git a/src/server/scripts/Bots/bot_shaman_ai.h b/src/server/scripts/Bots/bot_shaman_ai.h
new file mode 100644
index 0000000..0574c5d
--- /dev/null
+++ b/src/server/scripts/Bots/bot_shaman_ai.h
@@ -0,0 +1,48 @@
+#include "bot_ai.h"
+
+#define SPELL_CHAIN_HEAL          SPELL_CHAIN_HEAL_A[SPELL_LEVEL]
+#define SPELL_LESSER_HEALING      SPELL_LESSER_HEALING_A[SPELL_LEVEL]
+
+#define SPELL_SHAMAN_REZZ         SPELL_SHAMAN_REZZ_A[SPELL_LEVEL]
+
+//Nukes
+#define SPELL_FLAME_SHOCK         SPELL_FLAME_SHOCK_A[SPELL_LEVEL]
+#define SPELL_LIGHTNING_BOLT      SPELL_LIGHTNING_BOLT_A[SPELL_LEVEL]
+#define SPELL_EARTH_SHOCK         SPELL_EARTH_SHOCK_A[SPELL_LEVEL]
+
+//BUFFS
+//#define SPELL_WINDFURY_WEAPON   10486 //rank 3
+#define SPELL_LIGHTNING_SHIELD    SPELL_LIGHTNING_SHIELD_A[SPELL_LEVEL]
+#define SPELL_STONESKIN_AURA      SPELL_STONESKIN_AURA_A[SPELL_LEVEL]
+#define SPELL_HEALINGSTREAM_AURA  SPELL_HEALINGSTREAM_AURA_A[SPELL_LEVEL]
+#define SPELL_MANASPRING_AURA     SPELL_MANASPRING_AURA_A[SPELL_LEVEL]
+
+//Totems
+#define SPELL_STONESKIN_TOTEM     SPELL_STONESKIN_TOTEM_A[SPELL_LEVEL]
+#define SPELL_HEALINGSTREAM_TOTEM SPELL_HEALINGSTREAM_TOTEM_A[SPELL_LEVEL]
+#define SPELL_MANASPRING_TOTEM    SPELL_MANASPRING_TOTEM_A[SPELL_LEVEL]
+#define SPELL_SEARING_TOTEM       SPELL_SEARING_TOTEM_A[SPELL_LEVEL]
+#define SPELL_WINDFURY_TOTEM      SPELL_WINDFURY_TOTEM_A[SPELL_LEVEL]
+
+uint32 SPELL_CHAIN_HEAL_A[] = { 0, 0, 0, 0, 1064, 10623, 25422, 25423, 55459, 55459 };
+uint32 SPELL_LESSER_HEALING_A[] = { 0, 0, 8004, 8010, 10466, 10467, 10468, 25420, 49275, 49275 };
+
+uint32 SPELL_LIGHTNING_BOLT_A[] = { 403, 548, 915, 6041, 10392, 15207, 25448, 45296, 45296 };
+uint32 SPELL_FLAME_SHOCK_A[] = { 0, 8050, 8052, 8053, 10447, 10448, 29228, 25457, 49232, 49232 };
+uint32 SPELL_EARTH_SHOCK_A[] = { 8042, 8045, 8046, 10412, 10413, 10414, 10414, 25454, 49230, 49230 };
+
+uint32 SPELL_LIGHTNING_SHIELD_A[] = {324, 325, 905, 945, 8134, 10431, 25469, 25472, 49280, 49280 };
+uint32 SPELL_SHAMAN_REZZ_A[] = {0, 2008, 20609, 20610, 20776, 20776, 20777, 25590, 49277, 49277 };
+
+uint32 SPELL_STONESKIN_AURA_A[] = { 8072, 8156, 8156, 10403, 10404, 10405, 25506, 58752, 58752, 58752 };
+uint32 SPELL_STONESKIN_TOTEM_A[] = { 8071, 8154, 8155, 10406, 10407, 10408, 25508, 25509, 25509, 25509 };
+
+uint32 SPELL_HEALINGSTREAM_AURA_A[] = { 0, 0, 5672, 6371, 6372, 10460, 10461, 25566, 58765, 58765 };
+uint32 SPELL_HEALINGSTREAM_TOTEM_A[] = { 0, 0, 5394, 6375, 6377, 10462, 10463, 25567, 58757, 58757 };
+
+uint32 SPELL_MANASPRING_AURA_A[] = { 0, 0, 5677, 10491, 10493, 10494, 25569, 25569, 58775, 58775 };
+uint32 SPELL_MANASPRING_TOTEM_A[] = { 0, 0, 5675, 10495, 10496, 10497, 25570, 25570, 58771, 58771 };
+
+uint32 SPELL_SEARING_TOTEM_A[] = { 0, 3599, 6363, 6364, 6365, 10437, 10438, 25533, 58699, 58699 };
+
+uint32 SPELL_WINDFURY_TOTEM_A[] = { 0, 0, 0, 0, 8512, 8512, 8512, 8512, 8512, 8512 };
diff --git a/src/server/scripts/Bots/bot_warlock_ai.cpp b/src/server/scripts/Bots/bot_warlock_ai.cpp
new file mode 100644
index 0000000..7619575
--- /dev/null
+++ b/src/server/scripts/Bots/bot_warlock_ai.cpp
@@ -0,0 +1,292 @@
+#include "ScriptPCH.h"
+#include "bot_warlock_ai.h"
+
+
+class warlock_bot : public CreatureScript
+{
+public:
+    warlock_bot() : CreatureScript("warlock_bot") { }
+
+    CreatureAI *GetAI(Creature *pCreature) const
+    {
+        return new warlock_botAI(pCreature);
+    }
+
+	struct warlock_botAI : public bot_ai
+	{
+		warlock_botAI(Creature *c) :bot_ai(c)
+		{
+			Reset();
+			pet = NULL;
+		}
+
+		bool oom_spam;
+
+		uint8 state;
+		uint8 next_state;
+		uint32 next_state_timer;
+
+		Creature *pet;
+		Unit *opponent;
+
+		void Reset()
+		{
+			FirstAid_cd = 0;
+			GC_Timer = 0;
+
+			conflagarate_cd = 0;
+			chaos_bolt_cd = 0;
+
+			oom_spam = false;
+
+			uint8 state = 1;
+			next_state = 0;
+			next_state_timer = 0;
+
+			opponent = NULL;
+
+			if (master) {
+				setStats(CLASS_WARLOCK, m_creature->getRace(), master->getLevel());
+
+				if (!m_creature->HasAura(56235)) m_creature->AddAura(56235,m_creature);  // Glyph of Conflagrate
+				if (!m_creature->HasAura(63302)) m_creature->AddAura(63302,m_creature);  // Glyph of Haunt
+				if (!m_creature->HasAura(17834)) m_creature->AddAura(17834,m_creature);  // Improved Immolation
+				if (!m_creature->HasAura(17814)) m_creature->AddAura(17814,m_creature);  // Improved Corruption
+
+			}
+		}
+
+		void CreatePet()
+		{
+			pet = master->GetBotsPet(60237);
+
+			if(pet == NULL)
+				return;
+
+			pet->UpdateCharmAI();
+			pet->setFaction(m_creature->getFaction());
+			pet->SetReactState(REACT_DEFENSIVE);
+			pet->GetMotionMaster()->MoveFollow(m_creature, PET_FOLLOW_DIST*urand(1, 2),PET_FOLLOW_ANGLE);
+			CharmInfo *charmInfonewbot = pet->InitCharmInfo();
+			pet->GetCharmInfo()->SetCommandState(COMMAND_FOLLOW);
+			pet->UpdateStats(STAT_STRENGTH);
+			pet->UpdateStats(STAT_AGILITY);
+			pet->SetLevel(master->getLevel());
+
+			float val2 = master->getLevel()*4.0f + pet->GetStat(STAT_STRENGTH)*2.0f;
+
+			val2=100.0;
+			uint32 attPowerMultiplier=1;
+			pet->SetModifierValue(UNIT_MOD_ATTACK_POWER, BASE_VALUE, uint32(val2));
+			pet->UpdateAttackPowerAndDamage();
+			pet->SetBaseWeaponDamage(BASE_ATTACK, MINDAMAGE, uint32(val2 * attPowerMultiplier));
+			pet->SetBaseWeaponDamage(BASE_ATTACK, MAXDAMAGE, uint32(val2 * attPowerMultiplier)*2+master->getLevel());
+			pet->UpdateDamagePhysical(BASE_ATTACK);
+
+		}
+
+		void UpdateAI(const uint32 diff)
+		{
+
+			ReduceCD();
+
+			if(IAmDead()) return;
+
+			if(pet && pet != NULL && pet->isDead())
+			{
+				master->SetBotsPetDied();
+				pet = NULL;
+			}
+
+			//if we think we have a pet, but master doesn't, it means we teleported
+			if(pet && master->m_botHasPet == false)
+			{
+				master->SetBotsPetDied();
+				pet = NULL;
+			}
+
+			m_creature->ApplySpellImmune(0, IMMUNITY_STATE, SPELL_AURA_MOD_TAUNT, true);
+			m_creature->ApplySpellImmune(0, IMMUNITY_EFFECT, SPELL_EFFECT_ATTACK_ME, true);
+
+			if(m_creature->GetHealth() < m_creature->GetMaxHealth()*0.3 && isTimerReady(Potion_cd))
+			{
+				doCast(m_creature, HEALINGPOTION);
+				Potion_cd = Potion_cd;
+			}
+			if(m_creature->GetPower(POWER_MANA) < m_creature->GetMaxPower(POWER_MANA)*0.2)
+			{
+				if(isTimerReady(Potion_cd))
+				{
+					doCast(m_creature, MANAPOTION);
+					//MonsterSay("MANA POTION", LANG_UNIVERSAL, NULL);
+					Potion_cd = Potion_cd;
+				} else {
+					if(oom_spam == false)
+					{
+						//MonsterSay("OOM", LANG_UNIVERSAL, NULL);
+						oom_spam = true;
+					}
+					ScriptedAI::UpdateAI(diff);
+					//return; //can't do anything without mana
+			   }
+			}
+			oom_spam = false;
+
+			ScriptedAI::UpdateAI(diff);
+
+			opponent = SelectUnit(SELECT_TARGET_TOPAGGRO, 0);
+			if(!opponent && !m_creature->getVictim())
+			{
+				ResetOrGetNextTarget();
+
+				//to reduce the number of crashes, remove pet whenever we are not in combat
+				if(pet != NULL && pet->isAlive())
+				{
+					master->SetBotsPetDied();
+					pet = NULL;
+				}
+
+				return;
+			}
+
+			if(pet == NULL)
+				CreatePet();
+
+			if (pet && pet->isAlive() &&
+				!pet->isInCombat() &&
+				m_creature->getVictim()) {
+				pet->Attack (m_creature->getVictim(), true);
+				pet->GetMotionMaster()->MoveChase(m_creature->getVictim(), 1, 0);
+			}
+
+			if(m_creature->HasUnitState(UNIT_STAT_CASTING))
+				return;
+
+			DoNormalAttack(diff);
+		}
+
+		void Aggro(Unit *who){}
+
+		void JustDied(Unit *Killer)
+		{
+			master->SetBotCommandState(COMMAND_FOLLOW);
+			state = 1;
+			next_state = 0;
+			next_state_timer = 0;
+			if(pet && pet != NULL)
+			{
+				master->SetBotsPetDied();
+				pet = NULL;
+			}
+		}
+
+		void AttackStart(Unit *u)
+		{
+			Aggro(u);
+			ScriptedAI::AttackStartCaster(u, 25);
+		}
+
+		void KilledUnit(Unit *)
+		{
+			((Player*)master)->SetBotCommandState(COMMAND_FOLLOW);
+			if(pet && pet->isAlive()) pet->GetMotionMaster()->MoveFollow(m_creature, PET_FOLLOW_DIST*urand(1, 2), PET_FOLLOW_ANGLE);
+		}
+
+		void DoNormalAttack(const uint32 diff)
+		{
+			AttackerSet m_attackers = master->getAttackers();
+			if(opponent == NULL) return;
+			if(opponent->isDead()) return;
+
+			//double check that pet didn't just die
+			if(pet && pet != NULL && pet->isDead())
+			{
+				master->SetBotsPetDied();
+				pet = NULL;
+			}
+
+			//send in the pet
+			if(pet && pet != NULL && pet->isAlive() && !pet->isInCombat()) pet->AI()->AttackStart(opponent);
+
+			if(!isTimerReady(GC_Timer)) return;
+
+			if(opponent->HasUnitMovementFlag(UNIT_FLAG_FLEEING))
+			{
+				//MonsterSay("Mob is fleeing!", LANG_UNIVERSAL, NULL);
+				return;
+			}
+
+
+			//if(RAIN_OF_FIRE && m_attackers.size() > 1)
+			//{
+				//doCast(opponent, RAIN_OF_FIRE);
+				//return;
+			//}
+			if(CURSE_OF_THE_ELEMENTS && !HasAuraName(opponent, CURSE_OF_THE_ELEMENTS))
+			{
+				doCast(opponent, CURSE_OF_THE_ELEMENTS);
+				GiveManaBack();
+				//return;
+			}
+
+			if(CORRUPTION && !HasAuraName (opponent, CORRUPTION, m_creature->GetGUID()))
+			{
+				doCast(opponent, CORRUPTION);
+				GiveManaBack();
+				//return;
+			}
+
+			if(HAUNT && !HasAuraName (opponent, HAUNT, m_creature->GetGUID()))
+			{
+				doCast(opponent, HAUNT);
+				GiveManaBack();
+				return;
+			}
+
+			if(UNSTABLE_AFFLICTION && !HasAuraName (opponent, UNSTABLE_AFFLICTION, m_creature->GetGUID()))
+			{
+				doCast(opponent, UNSTABLE_AFFLICTION);
+				GiveManaBack();
+				return;
+			}
+
+			if(!HasAuraName(opponent, IMMOLATE, m_creature->GetGUID()))
+			{
+				doCast(opponent, IMMOLATE);
+				GiveManaBack();
+				return;
+			} else if(CONFLAGRATE && isTimerReady(conflagarate_cd))
+			{
+				doCast(opponent, CONFLAGRATE);
+				conflagarate_cd = CONFLAGRATE_CD;
+				GiveManaBack();
+				return;
+			}
+
+			if(CHAOS_BOLT && isTimerReady(chaos_bolt_cd))
+			{
+				doCast(opponent, CHAOS_BOLT);
+				GiveManaBack();
+				chaos_bolt_cd = CHAOS_BOLT_CD;
+				return;
+			} else
+				doCast(opponent, SHADOW_BOLT);
+			GiveManaBack();
+		} //DoNormalAttack
+
+		void ReduceCD()
+		{
+			if(Potion_cd > 0)       --Potion_cd;
+			if(FirstAid_cd > 0)     --FirstAid_cd;
+			if(GC_Timer > 0)        --GC_Timer;
+
+			if(conflagarate_cd > 0) --conflagarate_cd;
+			if(chaos_bolt_cd > 0)   --chaos_bolt_cd;
+		}
+	};
+};
+
+void AddSC_warlock_bot()
+{
+    new warlock_bot();
+}
diff --git a/src/server/scripts/Bots/bot_warlock_ai.h b/src/server/scripts/Bots/bot_warlock_ai.h
new file mode 100644
index 0000000..f0dc1e2
--- /dev/null
+++ b/src/server/scripts/Bots/bot_warlock_ai.h
@@ -0,0 +1,41 @@
+#include "bot_ai.h"
+
+
+uint32 conflagarate_cd;
+uint32 chaos_bolt_cd;
+
+#define CONFLAGRATE_CD  100
+#define CHAOS_BOLT_CD   120
+#define RAIN_OF_FIRE_CD 300
+
+#define PET_VOIDWALKER 697
+
+//Curses
+#define CURSE_OF_THE_ELEMENTS SPELL_CURSE_OF_THE_ELEMENTS_A[SPELL_LEVEL]
+
+//DESTRUCTION
+#define SHADOW_BOLT         SPELL_SHADOW_BOLT_A[SPELL_LEVEL]
+#define IMMOLATE            SPELL_IMMOLATE_A[SPELL_LEVEL]
+#define CONFLAGRATE         SPELL_CONFLAGRATE_A[SPELL_LEVEL]
+#define CHAOS_BOLT          SPELL_CHAOS_BOLT_A[SPELL_LEVEL]
+#define RAIN_OF_FIRE        SPELL_RAIN_OF_FIRE_A[SPELL_LEVEL]
+
+//AFFLICTION
+#define HAUNT               SPELL_HAUNT_A[SPELL_LEVEL]
+#define CORRUPTION          SPELL_CORRUPTION_A[SPELL_LEVEL]
+#define UNSTABLE_AFFLICTION SPELL_UNSTABLE_AFFLICTION_A[SPELL_LEVEL]
+
+//curses
+uint32 SPELL_CURSE_OF_THE_ELEMENTS_A[] = { 0, 0, 0, 1490, 11721, 11721, 11722, 27728, 47865 };
+
+//destruction spells
+uint32 SPELL_SHADOW_BOLT_A[] = { 686, 705, 1088, 7641, 11659, 11660, 25307, 47808, 47809 };
+uint32 SPELL_IMMOLATE_A[] = { 348, 707, 1094, 2941, 11665, 11667, 25309, 47810, 47811 };
+uint32 SPELL_CONFLAGRATE_A[] = { 0, 0, 0, 0, 17962, 17962, 17962, 17962, 17962 };
+uint32 SPELL_CHAOS_BOLT_A[] = { 0, 0, 0, 0, 0, 50796, 50796, 50796, 50796 };
+uint32 SPELL_RAIN_OF_FIRE_A[] = { 0, 0, 5740, 6219, 11677, 11678, 11678, 27212, 27212 };
+
+//affliction spells
+uint32 SPELL_HAUNT_A[] = { 0, 0, 0, 0, 0, 0, 59164, 59164, 59164 };
+uint32 SPELL_CORRUPTION_A[] = { 172, 6222, 7648, 11671, 11672, 25311, 47812, 47835, 47836 };
+uint32 SPELL_UNSTABLE_AFFLICTION_A[] = { 0, 0, 0, 0, 0, 30404, 30405, 47843,  47843 };
diff --git a/src/server/scripts/Bots/bot_warrior_ai.cpp b/src/server/scripts/Bots/bot_warrior_ai.cpp
new file mode 100644
index 0000000..f424528
--- /dev/null
+++ b/src/server/scripts/Bots/bot_warrior_ai.cpp
@@ -0,0 +1,501 @@
+    /* ScriptData
+    SDName: pvp_warrior
+    SD%Complete: 0
+    SDComment: paytheo help from Gasilli
+    SDCategory: Custom
+    EndScriptData */
+#include "ScriptPCH.h"
+//#include "../../game/Player.h"
+#include "bot_warrior_ai.h"
+
+bool castDemoralizingShout;
+bool battleStance;
+bool defensiveStance;
+bool berserkerStance;
+
+class warrior_bot : public CreatureScript
+{
+public:
+    warrior_bot() : CreatureScript("warrior_bot") { }
+
+    CreatureAI *GetAI(Creature *pCreature) const
+    {
+        return new warrior_botAI(pCreature);
+    }
+
+	struct warrior_botAI : public bot_ai
+	{
+		warrior_botAI(Creature *c) : bot_ai(c)
+		{
+			Reset();
+		}
+
+		uint32 charge_cd;
+		uint32 deathWish_cd;
+		uint32 mortalStrike_cd;
+		uint32 overpower_cd;
+		uint32 retaliation_recklessness_shieldwall_cd;
+		uint32 berserkerRage_cd;
+		uint32 challengingShout_cd;
+		uint32 battleShout_cd;
+		uint32 intercept_cd;
+		uint32 intimidatingShout_cd;
+		uint32 pummel_cd;
+		uint32 whirlwind_cd;
+		uint32 bloodrage_cd;
+		uint32 disarm_cd;
+		uint32 intervene_cd;
+		uint32 shieldBash_cd;
+		uint32 spellReflection_cd;
+		uint32 potion_cd;
+		uint32 firstAid_cd;
+		uint32 pvpTrinket_cd;
+		uint32 taunt_cd;
+		uint32 sunder_cd;
+		uint32 rage;
+		uint32 yellRage;
+		uint32 GCD;
+		int32 Noggenfogger_Timer;
+
+
+		void Reset()
+		{
+			charge_cd = 0;
+			deathWish_cd = 0;
+			mortalStrike_cd = 0;
+			overpower_cd = 0;
+			retaliation_recklessness_shieldwall_cd = 0;
+			berserkerRage_cd = 0;
+			challengingShout_cd = 0;
+			battleShout_cd = 0;
+			intercept_cd = 0;
+			intimidatingShout_cd = 0;
+			pummel_cd = 0;
+			whirlwind_cd = 0;
+			bloodrage_cd = 0;
+			disarm_cd = 0;
+			intervene_cd = 0;
+			shieldBash_cd = 0;
+			spellReflection_cd = 0;
+			potion_cd = 0;
+			firstAid_cd = 0;
+			pvpTrinket_cd = 0;
+			taunt_cd = 0;
+			sunder_cd = 0;
+			rage = 0;
+			yellRage = 0;
+			GCD = 0;
+			castDemoralizingShout = false;
+			battleStance = true;
+			defensiveStance = false;
+			berserkerStance = false;
+
+			Noggenfogger_Timer = 0;
+			m_creature->setPowerType(POWER_RAGE);
+
+			if (master) {
+				setStats(CLASS_WARRIOR, m_creature->getRace(), master->getLevel());
+			}
+		}
+
+		void EnterEvadeMode(){}
+
+		void doCast(Unit *victim, uint32 spellId, bool triggered = false)
+		{
+			if(spellId == 0) return;
+
+			GCD = 2000;
+			DoCast(victim, spellId, triggered);
+		} //end doCast
+
+		bool isTimerReady(int32 timer, uint32 diff)
+		{
+			if(timer <= 0 && GCD < diff)    return true;
+			else                            return false;
+		}
+
+
+		void UpdateAI(const uint32 diff)
+		{
+			ReduceCD(diff);
+
+			if(IAmDead()) return;
+
+			//Use Noggenfogger potion if a tauren
+			if(master->GetBotRace() == RACE_TAUREN &&
+			isTimerReady(Noggenfogger_Timer, diff))
+			{
+				uint64 m_rand = urand(1, 2);
+				switch(m_rand)
+				{
+					case 1:
+							doCast(m_creature, SPELL_NOGGENFOGGER_SKELETON, true);
+							break;
+					case 2:
+							doCast(m_creature, SPELL_NOGGENFOGGER_SMALL, true);
+							break;
+				}
+				Noggenfogger_Timer = 6000; //10 minutes
+			}
+
+
+			Unit *opponent = SelectUnit(SELECT_TARGET_TOPAGGRO, 0);
+			if(!opponent && !m_creature->getVictim())
+			{
+				ResetOrGetNextTarget();
+				if(rage > 0)
+				{
+					--rage;
+					//m_creature->SetPower(POWER_RAGE, rage*10);
+				}
+				return;
+			}
+
+			rage = m_creature->GetPower(POWER_RAGE);
+
+			m_creature->ApplySpellImmune(0, IMMUNITY_STATE, SPELL_AURA_MOD_TAUNT, true);
+			m_creature->ApplySpellImmune(0, IMMUNITY_EFFECT, SPELL_EFFECT_ATTACK_ME, true);
+
+			if(m_creature->GetHealth() < m_creature->GetMaxHealth()*0.2 &&
+				potion_cd<diff && GCD < diff)
+			{
+				doCast(m_creature, HEALINGPOTION);
+				potion_cd = POTIONCD;
+			}
+
+			if(!m_creature->HasAura(BATTLESHOUT) && rage > 10 && GCD < diff &&
+				battleShout_cd<diff)
+			{
+				doCast(m_creature, BATTLESHOUT);
+				//rage -= 10;
+				battleShout_cd = BATTLESHOUT_CD;
+			}
+
+			//if(rage > 100)
+			//{
+				//rage = 0;
+			//}
+
+			if(bloodrage_cd < diff       &&
+				m_creature->isInCombat() &&
+				opponent                 &&
+				GCD < diff)
+			{
+				doCast(m_creature, BLOODRAGE);
+				bloodrage_cd = BLOODRAGE_CD;
+				//rage += 10;
+			}
+
+			if(!m_creature->SelectVictim() || !m_creature->getVictim())
+			{
+				ResetOrGetNextTarget();
+				return;
+			}
+
+			BreakCC(diff);
+			Attack(diff);
+
+			ScriptedAI::UpdateAI(diff);
+		}
+
+
+		void AttackStart(Unit *u)
+		{
+			Aggro(u);
+			ScriptedAI::AttackStart(u);
+		}
+
+		void Aggro(Unit *who)
+		{
+			//Unit *opponent = SelectUnit(SELECT_TARGET_TOPAGGRO, 0);
+			Unit *opponent = who;
+
+			if(!opponent) return;
+
+			if((m_creature->GetDistance(opponent) > 15) &&
+			(m_creature->GetDistance(opponent) < 25) &&
+			charge_cd <= 0)
+			{
+				doCast(opponent, CHARGE);
+				charge_cd = CHARGE_CD;
+			}
+
+			if(opponent->getClass() == CLASS_ROGUE ||
+			opponent->getClass() == CLASS_WARRIOR  ||
+			opponent->getClass() == CLASS_SHAMAN   ||
+			opponent->getClass() == CLASS_DRUID    ||
+			opponent->getClass() == CLASS_PALADIN  ||
+			opponent->getClass() == CLASS_HUNTER)
+				castDemoralizingShout = true;
+		} //end Aggro
+
+		void KilledUnit(Unit *Victim)
+		{
+			master->SetBotCommandState(COMMAND_FOLLOW);
+			//DoPlaySoundToSet(m_creature, 8852);
+			//m_creature->Yell("Haha! Im just getting warmed up!", LANG_UNIVERSAL, NULL);
+		}
+
+		void JustDied(Unit *Killer)
+		{
+			DoPlaySoundToSet(m_creature, 8860);
+			master->SetBotCommandState(COMMAND_FOLLOW);
+			//m_creature->Yell("An honorable.. death..", LANG_UNIVERSAL, NULL);
+		}
+
+		void BreakCC(const uint32 diff)
+		{
+			 if(pvpTrinket_cd < diff && GCD < diff)
+			 {
+				 if(m_creature->HasAuraType(SPELL_AURA_MOD_ROOT)  ||
+				 m_creature->HasAuraType(SPELL_AURA_MOD_CONFUSE)  || //dragons breath/blind/poly
+				 m_creature->HasAura(8983)                        || //Druid bash rank 3
+				 m_creature->HasAura(27006)                       || //Druid pounce rank 4
+				 m_creature->HasAura(33786)                       || //Druid cyclone
+				 m_creature->HasAura(22570, 1)                    || //Druid maim
+				 m_creature->HasAura(10308)                       || //Paladin hammer of justice rank 4
+				 m_creature->HasAura(30414, 1)                    || //Warlock shadowfury rank 3
+				 m_creature->HasAura(6215)                        || //Warlock fear rank 3 **REMOVE THIS & IMPLEMENT IN BERSERKER RAGE**
+				 m_creature->HasAura(17928)                       || //Warlock howlofterror rank 3 **REMOVE THIS & IMPLEMENT IN BERSERKER RAGE**
+				 m_creature->HasAura(10890)                       || //Priest psychic scream rank 4 **REMOVE THIS & IMPLEMENT IN BERSERKER RAGE**
+				 m_creature->HasAura(14902)                       || //Rogue Cheap shot
+				 m_creature->HasAura(8643)                        || //Rogue Kidney shot Rank 2
+				 m_creature->HasAura(38764, 2)                    || //Rogue Gouge Rank 6 **REMOVE THIS & IMPLEMENT IN BERSERKER RAGE**
+				 m_creature->HasAura(12809))                         //Warrior concussion blow
+				{
+					doCast(m_creature, PVPTRINKET); //I think it would be better to instead of applying individual spells that apply the
+					pvpTrinket_cd = PVPTRINKET_CD;  //effect SPELL_AURA_MOD_STUN, just add that type and start removing bad choices e.g. impact.
+				}
+
+				if(m_creature->HasAura(11297) && m_creature->GetDistance(m_creature->getVictim()) < 10)
+				{   //if warrior sapped and creature is less then 10 yards from warrior, cast pvp trinket and attempt to demoralizing shout him out of stealth
+					doCast(m_creature, PVPTRINKET);
+					pvpTrinket_cd = PVPTRINKET_CD;
+					castDemoralizingShout = true;
+				}
+			}
+		} //BreakCC
+
+		void Attack(const uint32 diff)
+		{
+			//Unit *opponent = SelectUnit(SELECT_TARGET_TOPAGGRO, 0);
+			Unit *opponent = m_creature->getVictim();
+			if(!opponent) return;
+
+			if((m_creature->GetDistance(opponent) > 13)      &&
+			(m_creature->GetDistance(opponent) < 25)         &&
+			!m_creature->HasAuraType(SPELL_AURA_MOD_ROOT)    &&
+			!m_creature->HasAuraType(SPELL_AURA_MOD_STUN)    &&
+			!m_creature->HasAuraType(SPELL_AURA_MOD_CONFUSE) &&
+			(intercept_cd < diff)                            &&
+			(rage > 10)                                      &&
+			(GCD < diff))
+			{
+				if(berserkerStance == true)
+				{
+					doCast(opponent, INTERCEPT, true);
+					intercept_cd = INTERCEPT_CD;
+					//rage -= 10;
+				} else stanceChange(3);
+			}
+
+			if(disarm_cd < diff                                           &&
+				(opponent->GetHealth()*100/opponent->GetMaxHealth()) < 80 &&
+				rage > 15                                                 &&
+				!HasAuraName(opponent, GetSpellName(DISARM))              &&
+				GCD < diff)
+			{
+				if(opponent->getClass() == CLASS_ROGUE  ||
+					opponent->getClass() == CLASS_WARRIOR   ||
+					opponent->getClass() == CLASS_SHAMAN    ||
+					opponent->getClass() == CLASS_PALADIN)
+				{
+					if(defensiveStance == true)
+					{
+						doCast(opponent, DISARM, true);
+						//rage -= 15;
+						disarm_cd = DISARM_CD;
+					} else stanceChange(2);
+				}
+			}
+
+			//opponent is not attacking me so try to taunt it
+			if(opponent->getVictim() &&
+			   opponent->getVictim()->GetGUID() != m_creature->GetGUID() &&
+			   taunt_cd < diff &&
+			   GCD < diff)
+			{
+				if(battleStance != true)
+				{
+					doCast(m_creature, DEFENSIVESTANCE, true);
+					defensiveStance = true;
+				}
+				doCast(opponent, TAUNT, true);
+				doCast(m_creature, TAUNT_VISUAL, true);
+				taunt_cd = TAUNT_CD;
+			}
+
+	/*        if(sunder_cd < diff && GCD < diff)
+			{
+	sLog->outError ("SUNDER");
+				doCast(opponent, SUNDER, true);
+				//rage -= 15;
+				sunder_cd = SUNDER_CD;
+			}
+	*/
+			if((opponent->GetHealth()*100/opponent->GetMaxHealth()) < 15 &&
+				rage > 15                                                    &&
+				GCD < diff)
+			{
+				if(battleStance == true || berserkerStance == true)
+				{
+					int damage = (rage*4 + (m_creature->getLevel()*10))/2;
+
+					m_creature->CastCustomSpell(opponent, EXECUTE, &damage, NULL, NULL, false, NULL, NULL);
+					//rage = 0;
+					//m_creature->SetPower(POWER_RAGE, 0);
+					GCD = 20;
+				} else stanceChange(5);
+			}
+
+			if(mortalStrike_cd < diff               &&
+			   rage > 15                               &&
+			   GCD < diff)
+			{
+				doCast(opponent, MORTALSTRIKE, true);
+				mortalStrike_cd = MORTALSTRIKE_CD;
+				//rage -= 15;
+			}
+
+			if(castDemoralizingShout == true         &&
+			   !opponent->HasAura(DEMORALIZINGSHOUT)    &&
+			   rage < 10 && GCD < diff)
+			{
+				doCast(opponent, DEMORALIZINGSHOUT, true);
+				//rage -= 10;
+				castDemoralizingShout = false;
+			}
+
+			if(opponent->IsNonMeleeSpellCasted(true) &&
+			   pummel_cd < diff                         &&
+			   rage > 10                                &&
+			   GCD < diff)
+			{
+				if(berserkerStance == true)
+				{
+					doCast(opponent, PUMMEL, true);
+					pummel_cd = PUMMEL_CD;
+					//rage -= 10;
+				} else stanceChange(3);
+			}
+
+			if(whirlwind_cd < diff                &&
+			   rage > 25                             &&
+			   GCD < diff)
+			{
+				if(berserkerStance == true)
+				{
+					doCast(opponent, WHIRLWIND, true);
+					whirlwind_cd = WHIRLWIND_CD;
+					//rage -= 25;
+				} else stanceChange(3);
+			}
+
+			if(!opponent->HasAura(REND)                 &&
+			   rage > 10                                &&
+			   GCD < diff)
+			{
+				if(battleStance == true || defensiveStance == true)
+				{
+					doCast(opponent, REND, true);
+					//rage -= 10;
+				} else stanceChange(1);
+			}
+
+			if(!opponent->HasAura(HAMSTRING, 1)         &&
+				rage > 10                               &&
+				GCD < diff)
+			{
+				if(battleStance == true ||
+				berserkerStance == true)
+				{
+					doCast(opponent, HAMSTRING, true);
+					//rage -= 10;
+				} else stanceChange(5);
+			}
+
+		}
+
+		void stanceChange(uint32 stance)
+		{
+		 //if(rage > 20)
+			 //rage = 20;
+
+			if(stance == 5)
+			{
+				switch(rand()%2)
+				{
+				case 0:
+					stance = 1; break;
+				case 1:
+					stance = 3; break;
+				}
+			}
+
+			battleStance = false;
+			defensiveStance = false;
+			berserkerStance = false;
+
+			switch(stance)
+			{
+			case 1:
+				doCast(m_creature, BATTLESTANCE);
+				battleStance = true;
+				break;
+			case 2:
+				doCast(m_creature, DEFENSIVESTANCE);
+				defensiveStance = true;
+				break;
+			case 3:
+				doCast(m_creature, BERSERKERSTANCE);
+				berserkerStance = true;
+				break;
+			}
+		}
+
+		void ReduceCD(const uint32 diff)
+		{
+			if(!(deathWish_cd < diff))                  deathWish_cd -= diff;
+			if(!(mortalStrike_cd < diff))               mortalStrike_cd -= diff;
+			if(!(overpower_cd < diff))                 overpower_cd -= diff;
+			if(!(retaliation_recklessness_shieldwall_cd < diff))  retaliation_recklessness_shieldwall_cd -= diff;
+			if(!(berserkerRage_cd < diff))              berserkerRage_cd -= diff;
+			if(!(intercept_cd < diff))                  intercept_cd -= diff;
+			if(!(intimidatingShout_cd < diff))          intimidatingShout_cd -= diff;
+			if(!(pummel_cd < diff))                     pummel_cd -= diff;
+			if(!(whirlwind_cd < diff))                  whirlwind_cd -= diff;
+			if(!(bloodrage_cd < diff))                  bloodrage_cd -= diff;
+			if(!(disarm_cd < diff))                     disarm_cd -= diff;
+			if(!(intervene_cd < diff))                  intervene_cd -= diff;
+			if(!(shieldBash_cd < diff))                 shieldBash_cd -= diff;
+			if(!(spellReflection_cd < diff))            spellReflection_cd -= diff;
+			if(!(potion_cd < diff))                     potion_cd -= diff;
+			if(!(yellRage < diff))                      yellRage -= diff;
+			if(!(firstAid_cd < diff))                   firstAid_cd -= diff;
+			if(!(taunt_cd < diff))                      taunt_cd -= diff;
+			if(!(sunder_cd < diff))                     sunder_cd -= diff;
+			if(!(battleShout_cd < diff))                battleShout_cd -= diff;
+			if(!(GCD < diff))                           GCD -= diff;
+			else                                        GCD = 0;
+
+			if(charge_cd > 0)                           --charge_cd; //this is treated different
+			if(Noggenfogger_Timer >= 0)                --Noggenfogger_Timer;
+
+		}
+	};
+};
+
+void AddSC_warrior_bot()
+{
+    new warrior_bot();
+}
diff --git a/src/server/scripts/Bots/bot_warrior_ai.h b/src/server/scripts/Bots/bot_warrior_ai.h
new file mode 100644
index 0000000..8d1e0ef
--- /dev/null
+++ b/src/server/scripts/Bots/bot_warrior_ai.h
@@ -0,0 +1,87 @@
+#include "bot_ai.h"
+
+
+//Cooldown/Timers
+#define CHARGE_CD 100
+#define DEATHWISH_CD 180000
+#define MORTALSTRIKE_CD 7000
+#define OVERPOWER_CD 5000
+#define RETALIATION_RECKLESSNESS_SHIELDWALL_CD 1800000
+#define BERSERKERRAGE_CD 30000
+#define INTERCEPT_CD 15000
+#define INTIMIDATINGSHOUT_CD 120000
+#define PUMMEL_CD 10000
+#define WHIRLWIND_CD 10000
+#define BLOODRAGE_CD 60000
+#define DISARM_CD 60000
+#define INTERVENE_CD 30000
+#define BATTLESHOUT_CD 30000
+#define SHIELDBASH_CD 12000
+#define SPELLREFLECTION_CD 10000
+#define PVPTRINKET_CD 120000
+#define TAUNT_CD 8000
+#define SUNDER_CD 8000
+
+//others
+#define PVPTRINKET 42292
+#define YELLRAGE_CD 5000
+
+#define SPELL_NOGGENFOGGER_SMALL    16595
+#define SPELL_NOGGENFOGGER_SKELETON    16591
+
+//spells
+#define TAUNT              355
+#define TAUNT_VISUAL       34105
+#define CHALLENGING_SHOUT   1161
+
+/*
+ *  Some spells NPCs can't seem to cast.  So for those that I couldn't
+ *  get to work, I used an NPC equivalent version.  Most of these don't
+ *  have different levels so it is one spell for all levels.  This
+ *  leads it to be over/under powered in some levels.  Oh well.
+ *  An example is BattleShout.
+ */
+
+//Defensive Stance
+#define DEFENSIVESTANCE     71
+#define DISARM              676 //DISARM_A[SPELL_LEVEL]
+#define BLOODRAGE           29131 //2687 original warrior spell
+
+
+//Berserker Stance
+#define BERSERKERSTANCE     7366 //2458 original warrior spell
+#define BERSERKERRAGE       18499
+
+//#define BATTLESHOUT       26043
+#define COMMANDINGSHOUT     469
+#define BATTLESHOUT         BATTLESHOUT_A[SPELL_LEVEL]
+//#define EXECUTE           EXECUTE_A[SPELL_LEVEL]
+#define EXECUTE             38959 //25236 original warrior spell
+#define WHIRLWIND           WHIRLDWIND_A[SPELL_LEVEL]
+//#define PUMMEL            PUMMEL_A[SPELL_LEVEL]
+#define PUMMEL              15615 //6554 original warrior spell
+//#define DEMORALIZINGSHOUT 29584 //25203 original warrior spell
+#define DEMORALIZINGSHOUT   29584 //25203 original warrior spell
+#define INTERCEPT           27577 //25275 original warrior spell
+
+
+//Battle Stance
+#define BATTLESTANCE        7165 //2457 original warrior one
+//#define CHARGE            CHARGE_A[SPELL_LEVEL]
+#define CHARGE              37511 //11578 original warrior one
+#define HAMSTRING           HAMSTRING_A[SPELL_LEVEL]
+#define REND                REND_A[SPELL_LEVEL]
+#define MORTALSTRIKE        MORTALSTRIKE_A[SPELL_LEVEL]
+#define SUNDER              SUNDER_A[SPELL_LEVEL]
+
+
+//uint32 BATTLESHOUT_A[] = { 6673, 5242, 6192, 11549, 11550, 11551, 25289, 2048, 47436 };
+uint32 BATTLESHOUT_A[] = { 9128, 9128, 9128, 27578, 27578, 26043, 26043, 26043, 26043 };
+uint32 REND_A[] = { 772, 6546, 6547, 6548, 11572, 11573, 11574, 25208, 46845 };
+uint32 CHARGE_A[] = { 100, 100, 6178, 11578, 11578, 11578, 11578, 11578, 11578 };
+uint32 HAMSTRING_A[] = { 1715, 1715, 1715, 7372, 7372, 7373, 7373, 25212, 25212 };
+uint32 EXECUTE_A[] = { 1715, 1715, 1715, 7372, 7372, 7373, 7373, 25212, 25212 };
+uint32 WHIRLDWIND_A[] = { 0, 0, 0, 0, 1680, 1680, 1680, 1680, 1680 };
+uint32 PUMMEL_A[] = { 0, 0, 0, 0, 6552, 6552, 6552, 6552, 6552 };
+uint32 MORTALSTRIKE_A[] = { 0, 0, 0, 0, 12294, 21552, 21553, 25248, 47485 };
+uint32 SUNDER_A[] = { 0, 7386, 7405, 8380, 11596, 11597, 25225, 47467, 47467 };
diff --git a/src/server/scripts/Bots/script_bot_giver.cpp b/src/server/scripts/Bots/script_bot_giver.cpp
new file mode 100644
index 0000000..cc4b3b5
--- /dev/null
+++ b/src/server/scripts/Bots/script_bot_giver.cpp
@@ -0,0 +1,234 @@
+
+#include "ScriptPCH.h"
+#include <cstring>
+#include "Group.h"
+
+//This function is called when the player opens the gossip menubool
+class script_bot_giver : public CreatureScript
+{
+	public:
+
+		script_bot_giver()
+            : CreatureScript("script_bot_giver")
+        {
+        }
+
+		bool OnGossipSelect(Player *player, Creature *creature, uint32 sender, uint32 action)
+		{
+			switch(sender)
+			{
+				case 6006: SendCreateNPCBotMenu(player, creature, action); break;
+				case 6001: SendCreateNPCBot(player, creature, action); break;
+				case 6002: SendCreatePlayerBotMenu(player, creature, action); break;
+				case 6003: SendCreatePlayerBot(player, creature, action); break;
+				case 6004: SendRemovePlayerBotMenu(player, creature, action); break;
+				case 6005: SendRemovePlayerBot(player, creature, action); break;
+			}
+			return true;
+		}
+
+		bool OnGossipHello(Player *player, Creature *creature)
+		{
+			WorldSession *session = player->GetSession();
+			uint8 count = 0;
+
+			for(PlayerBotMap::const_iterator itr = session->GetPlayerBotsBegin(); itr != session->GetPlayerBotsEnd(); ++itr)
+			{
+				if(count == 0)
+					player->ADD_GOSSIP_ITEM(0, "Abandon Your Player?", 6004, GOSSIP_ACTION_INFO_DEF + 100);
+
+				++count;
+			}
+
+			if(player->HaveBot())
+			{
+				player->ADD_GOSSIP_ITEM(0, "Abandon Your Minion?", 6001, GOSSIP_ACTION_INFO_DEF + 101);
+			} else
+				player->ADD_GOSSIP_ITEM(0, "Recruit a Minion", 6006, GOSSIP_ACTION_INFO_DEF + 2);
+
+			if(count < player->GetMaxPlayerBot()) player->ADD_GOSSIP_ITEM(0, "Recruit a Player", 6002, GOSSIP_ACTION_INFO_DEF + 1);
+
+			player->PlayerTalkClass->SendGossipMenu(907, creature->GetGUID());
+			return true;
+		}
+
+		void SendCreatePlayerBot(Player *player, Creature *creature, uint32 action)
+		{
+			std::list<std::string> *names;
+			names = player->GetCharacterList();
+			if(names == NULL || names->empty())
+			{
+				player->CLOSE_GOSSIP_MENU();
+				return;
+			}
+
+			int8 x = action - GOSSIP_ACTION_INFO_DEF - 1;
+
+			std::list<std::string>::iterator iter, next;
+			for(iter = names->begin(); iter != names->end(); iter++)
+			{
+				if (x==0) player->CreatePlayerBot((*iter).c_str());
+				else {
+					if(x == 1)
+					{
+						player->CreatePlayerBot((*iter).c_str());
+						break;
+					}
+					--x;
+				}
+			}
+
+			player->CLOSE_GOSSIP_MENU();
+		} //end SendCreatePlayerBot
+
+		void SendCreatePlayerBotMenu(Player *player, Creature *creature, uint32 action)
+		{
+			std::list<std::string> *names;
+			names = player->GetCharacterList();
+			if(names == NULL || names->empty())
+			{
+				player->CLOSE_GOSSIP_MENU();
+				return;
+			}
+
+            player->PlayerTalkClass->ClearMenus();
+			player->ADD_GOSSIP_ITEM(9, "ADD ALL" , 6003, GOSSIP_ACTION_INFO_DEF + 1);
+			int8 x = 2;
+
+			std::list<std::string>::iterator iter, next;
+			for(iter = names->begin(); iter != names->end(); iter++)
+			{
+				//sLog->outError("character : %s", (*iter).c_str());
+				player->ADD_GOSSIP_ITEM(9, (*iter).c_str() , 6003, GOSSIP_ACTION_INFO_DEF + x);
+				++x;
+			}
+			player->SEND_GOSSIP_MENU(907, creature->GetGUID());
+		} //end SendCreatePlayerBotMenu
+
+		void SendRemovePlayerBotAll(Player *player, Creature *creature) {
+			for (int8 x = 2; x<=10; x++ )
+			{
+				SendRemovePlayerBot (player, creature, GOSSIP_ACTION_INFO_DEF + 2);
+			}
+		}
+
+		void SendRemovePlayerBot(Player *player, Creature *creature, uint32 action)
+		{
+			int8 x = action - GOSSIP_ACTION_INFO_DEF - 1;
+
+			if (x == 0) {
+				SendRemovePlayerBotAll(player, creature);
+				return;
+			}
+
+			WorldSession *session = player->GetSession();
+			for(PlayerBotMap::const_iterator itr = session->GetPlayerBotsBegin(); itr != session->GetPlayerBotsEnd(); ++itr)
+			{
+				if(x == 1 && itr->second && itr->second->GetGroup())
+				{
+					Player *m_bot = itr->second;
+					Group *m_group = m_bot->GetGroup();
+
+					//removing bot from group
+					if(m_group->IsMember(m_bot->GetGUID()))
+					{
+						//deleting bot from group
+						if(m_group->RemoveMember(m_bot->GetGUID(), GROUP_REMOVEMETHOD_DEFAULT) < 1) // 99 means I'm a bot
+						{
+							//no one left in group so deleting group
+							delete m_group;
+							//sObjectMgr->RemoveGroup(m_group);
+						}
+					}
+					session->LogoutPlayerBot(m_bot->GetGUID(), true);
+					break;
+				}
+				--x;
+			}
+			player->CLOSE_GOSSIP_MENU();
+		} //end SendRemovePlayerBot
+
+		void SendRemovePlayerBotMenu(Player *player, Creature *creature, uint32 action)
+		{
+            player->PlayerTalkClass->ClearMenus();
+			player->ADD_GOSSIP_ITEM(9, "REMOVE ALL", 6005, GOSSIP_ACTION_INFO_DEF + 1);
+
+			uint8 x = 2;
+			WorldSession *session = player->GetSession();
+			for(PlayerBotMap::const_iterator itr = session->GetPlayerBotsBegin(); itr != session->GetPlayerBotsEnd(); ++itr)
+			{
+				Player *bot = itr->second;
+				player->ADD_GOSSIP_ITEM(9, bot->GetName(), 6005, GOSSIP_ACTION_INFO_DEF + x);
+				++x;
+			}
+			player->SEND_GOSSIP_MENU(907, creature->GetGUID());
+		} //end SendRemovePlayerBotMenu
+
+		void SendCreateNPCBot(Player *player, Creature *creature, uint32 action)
+		{
+			uint8 bot_class = 0;
+			if(action == GOSSIP_ACTION_INFO_DEF + 101) //abandon bot
+			{
+				if(player->HaveBot())
+					player->SetBotMustDie();
+				player->CLOSE_GOSSIP_MENU();
+				return;
+			}
+			else if(action == GOSSIP_ACTION_INFO_DEF + 1){ //playerbot
+				player->CLOSE_GOSSIP_MENU();
+				return;
+			}
+			else if(action == GOSSIP_ACTION_INFO_DEF + 2)
+				bot_class = CLASS_WARRIOR;
+			else if(action == GOSSIP_ACTION_INFO_DEF + 3)
+				bot_class = CLASS_HUNTER;
+			else if(action == GOSSIP_ACTION_INFO_DEF + 4)
+				bot_class = CLASS_PALADIN;
+			else if(action == GOSSIP_ACTION_INFO_DEF + 5)
+				bot_class = CLASS_SHAMAN;
+			else if(action == GOSSIP_ACTION_INFO_DEF + 6)
+				bot_class = CLASS_ROGUE;
+			else if(action == GOSSIP_ACTION_INFO_DEF + 7)
+				bot_class = CLASS_DRUID;
+			else if(action == GOSSIP_ACTION_INFO_DEF + 8)
+				bot_class = CLASS_MAGE;
+			else if(action == GOSSIP_ACTION_INFO_DEF + 9)
+				bot_class = CLASS_PRIEST;
+			else if(action == GOSSIP_ACTION_INFO_DEF + 10)
+				bot_class = CLASS_WARLOCK;
+			//else if(action == GOSSIP_ACTION_INFO_DEF + 11)
+				//bot_class = CLASS_DEATH_KNIGHT;
+
+			if(bot_class > 0)
+			{
+				//sLog->outError("script_bot_giver.SendCreateNPCBot class = %u", bot_class);
+				player->CreateNPCBot(bot_class);
+			}
+			//else
+				//creature->Say("Invalid selection.", LANG_UNIVERSAL, NULL);
+			player->CLOSE_GOSSIP_MENU();
+			return;
+		}
+
+		void SendCreateNPCBotMenu(Player *player, Creature *creature, uint32 action)
+		{
+            player->PlayerTalkClass->ClearMenus();
+			player->ADD_GOSSIP_ITEM(9, "Recruit a Warrior", 6001, GOSSIP_ACTION_INFO_DEF + 2);
+			player->ADD_GOSSIP_ITEM(9, "Recruit a Hunter", 6001, GOSSIP_ACTION_INFO_DEF + 3);
+			player->ADD_GOSSIP_ITEM(9, "Recruit a Paladin", 6001, GOSSIP_ACTION_INFO_DEF + 4);
+			player->ADD_GOSSIP_ITEM(9, "Recruit a Shaman", 6001, GOSSIP_ACTION_INFO_DEF + 5);
+			player->ADD_GOSSIP_ITEM(9, "Recruit a Rogue", 6001, GOSSIP_ACTION_INFO_DEF + 6);
+			player->ADD_GOSSIP_ITEM(3, "Recruit a Druid", 6001, GOSSIP_ACTION_INFO_DEF + 7);
+			player->ADD_GOSSIP_ITEM(3, "Recruit a Mage", 6001, GOSSIP_ACTION_INFO_DEF + 8);
+			player->ADD_GOSSIP_ITEM(3, "Recruit a Priest", 6001, GOSSIP_ACTION_INFO_DEF + 9);
+			player->ADD_GOSSIP_ITEM(3, "Recruit a Warlock", 6001, GOSSIP_ACTION_INFO_DEF + 10);
+			//player->ADD_GOSSIP_ITEM(9, "Recruit a Death Knight", 1, GOSSIP_ACTION_INFO_DEF + 11);
+			player->SEND_GOSSIP_MENU(907, creature->GetGUID());
+		} //end SendCreateNPCBotMenu
+};
+
+//This function is called when the player clicks an option on the gossip menu
+void AddSC_script_bot_giver()
+{
+    new script_bot_giver();
+}
diff --git a/src/server/scripts/CMakeLists.txt b/src/server/scripts/CMakeLists.txt
index 17d4bfc..95ec7b9 100644
--- a/src/server/scripts/CMakeLists.txt
+++ b/src/server/scripts/CMakeLists.txt
@@ -40,6 +40,7 @@ set(scripts_STAT_SRCS
 
 if(SCRIPTS)
   include(Custom/CMakeLists.txt)
+  include(Bots/CMakeLists.txt)
   include(World/CMakeLists.txt)
   include(OutdoorPvP/CMakeLists.txt)
   include(EasternKingdoms/CMakeLists.txt)
diff --git a/src/server/shared/Common.h b/src/server/shared/Common.h
index cefefa7..77a76dc 100755
--- a/src/server/shared/Common.h
+++ b/src/server/shared/Common.h
@@ -19,6 +19,10 @@
 #ifndef TRINITYCORE_COMMON_H
 #define TRINITYCORE_COMMON_H
 
+#ifndef PLAYERBOT_EXISTS
+#define PLAYERBOT_EXISTS
+#endif
+
 // config.h needs to be included 1st
 // TODO this thingy looks like hack ,but its not, need to
 // make separate header however, because It makes mess here.
diff --git a/src/server/worldserver/worldserver.conf.dist b/src/server/worldserver/worldserver.conf.dist
index 188d6d8..40d4631 100644
--- a/src/server/worldserver/worldserver.conf.dist
+++ b/src/server/worldserver/worldserver.conf.dist
@@ -2738,3 +2738,39 @@ LevelReq.Mail = 1
 
 #
 ###################################################################################################
+
+################################################################################
+# BOT CONFIGURATION
+#
+#    Bot.FollowDistanceMin
+#    Bot.FollowDistanceMax
+#        Min. and max. follow distance for bots
+#        Default: 0.5 / 2.0
+#    Bot.MaxPlayerBots
+#        Maximum number of Player Bots allowed per account
+#        Default: 9
+#    Bot.PlayerBotsFly
+#        If PlayerBots fly with you when you use the flight master
+#        Default: 0
+#        0 = false
+#        1 = true
+#    Bot.LootMethod
+#        Type of loot method
+#        Default: 2
+#        0 = Free for all
+#        1 = Round robin
+#        2 = Master loot
+#        3 = Group loot
+#        4 = Need before greed
+#    Bot.SaveOrgLocation
+#        Puts playerbots back to their original location after use
+#        Default: 0
+#        0 = false - keep playerbots where they were camped out
+#        1 = true - puts playerbots back to where they were originally summoned
+################################################################################
+Bot.FollowDistanceMin = 0.5
+Bot.FollowDistanceMax = 2.0
+Bot.MaxPlayerBots = 9
+Bot.PlayerBotsFly=0
+Bot.LootMethod=2
+Bot.SaveOrgLocation=0
-- 
1.7.3.1.msysgit.0