diff --git a/conf/playerbots.conf.dist b/conf/playerbots.conf.dist index 01a6a4600f6..6ea92b2d686 100644 --- a/conf/playerbots.conf.dist +++ b/conf/playerbots.conf.dist @@ -552,6 +552,17 @@ AiPlayerbot.AutoGearCommand = 1 # Default: 1 (enabled) AiPlayerbot.AutoGearCommandAltBots = 1 +# If 1 (enabled) chat command: autogear bis, gives bots BiS gear from the playerbots_bis_gear table +# whose item-level floor matches AutoGearScoreLimit (e.g. 290 = ICC, 245 = ToC, +# 125 = Kara, 78 = MC). See the AutoGearScoreLimit comment below for the full list. +# chat command: autogear bis x, (x must be positive integer) will give items based on value of x. +# If x is bigger than AutoGearScoreLimit, bis wont be given, if lower it will match x when giving items. +# Commands falls back to autogear when no tier matches or the table is empty for the bot's class/spec. +# Requires AutoGearQualityLimit = 4 and AutoGearCommand = 1. +# If AutoGearCommandAltBots = 1 it will be anbled for alt bots. +# Default: 0 (disabled) +AiPlayerbot.AutoGearBisCommand = 0 + # Equipment quality limitation for autogear command (1 = normal, 2 = uncommon, 3 = rare, 4 = epic, 5 = legendary) # Default: 3 (rare) AiPlayerbot.AutoGearQualityLimit = 3 @@ -1775,18 +1786,18 @@ AiPlayerbot.PremadeSpecLink.9.5.80 = -2032003311302-05230015220331351005031051 AiPlayerbot.PremadeSpecName.11.0 = balance pve AiPlayerbot.PremadeSpecGlyph.11.0 = 40916,43331,40921,43335,44922,40919 -AiPlayerbot.PremadeSpecLink.11.0.60 = 5022203105331003213005301231 -AiPlayerbot.PremadeSpecLink.11.0.80 = 5032203105331303213305301231--205003012 +AiPlayerbot.PremadeSpecLink.11.0.60 = 5032003125031003213304301231 +AiPlayerbot.PremadeSpecLink.11.0.80 = 5032003125331303213305301231--205003012 AiPlayerbot.PremadeSpecName.11.1 = bear pve AiPlayerbot.PremadeSpecGlyph.11.1 = 40897,43331,46372,43335,43332,40899 -AiPlayerbot.PremadeSpecLink.11.1.60 = -500232130322110353100301310501 +AiPlayerbot.PremadeSpecLink.11.1.60 = -503232132322010303120300013501 AiPlayerbot.PremadeSpecLink.11.1.80 = -503232132322010353120303013511-20350001 AiPlayerbot.PremadeSpecName.11.2 = resto pve -AiPlayerbot.PremadeSpecGlyph.11.2 = 40913,43331,40906,43335,43674,45602 -AiPlayerbot.PremadeSpecLink.11.2.60 = --230033312031500531050113051 -AiPlayerbot.PremadeSpecLink.11.2.80 = 05320131003--230023312131500531050313051 +AiPlayerbot.PremadeSpecGlyph.11.2 = 40906,43331,45602,43335,43674,45603 +AiPlayerbot.PremadeSpecLink.11.2.60 = --230033312031502331050313031 +AiPlayerbot.PremadeSpecLink.11.2.80 = 05320031--230033312031502431053313051 AiPlayerbot.PremadeSpecName.11.3 = cat pve -AiPlayerbot.PremadeSpecGlyph.11.3 = 40902,43331,40901,43335,43674,45604 +AiPlayerbot.PremadeSpecGlyph.11.3 = 40902,43331,40901,43674,43335,45604 AiPlayerbot.PremadeSpecLink.11.3.60 = -552202032322010053100030310501 AiPlayerbot.PremadeSpecLink.11.3.80 = -553202032322010053120030310511-203503012 AiPlayerbot.PremadeSpecName.11.4 = balance pvp @@ -2151,6 +2162,27 @@ AiPlayerbot.RandomClassSpecIndex.11.6 = 6 # #################################################################################################### +################################### +# # +# RAIDS # +# # +################################### + +#################################################################################################### +# +# +# + +# Enable buffs in ICC to make Heroic easier and more casual. Default is 1. +# 30% more damage, 40% damage reduction (tank bots), increased all resistances, reduced threat for +# non tank bots, increased threat for tank bots. +# Buffs will be applied on LDW, PP, Sindragosa and Lich King. +AiPlayerbot.EnableICCBuffs = 1 + +# +# +# +#################################################################################################### ################################### # # @@ -2433,10 +2465,4 @@ AiPlayerbot.GuildTaskKillTaskDistance = 200 AiPlayerbot.TargetPosRecalcDistance = 0.1 # Allow bots to be summoned near innkeepers -AiPlayerbot.SummonAtInnkeepersEnabled = 1 - -# Enable buffs in ICC to make Heroic easier and more casual. -# 30% more damage, 40% damage reduction (tank bots), increased all resistances, reduced threat for non tank bots, increased threat for tank bots. -# Buffs will be applied on PP, Sindragosa and Lich King - -AiPlayerbot.EnableICCBuffs = 1 +AiPlayerbot.SummonAtInnkeepersEnabled = 1 \ No newline at end of file diff --git a/data/sql/playerbots/updates/2026_04_28_00_playerbots_bis_gear.sql b/data/sql/playerbots/updates/2026_04_28_00_playerbots_bis_gear.sql new file mode 100644 index 00000000000..89c1c8cc0b0 --- /dev/null +++ b/data/sql/playerbots/updates/2026_04_28_00_playerbots_bis_gear.sql @@ -0,0 +1,7973 @@ +-- BiS gear table for the /p bis party-chat command . Unified across tiers. +-- Lookup keys: (class, tab, slot, faction, auto_gear_score_limit). +-- faction: 0=Both, 1=Alliance, 2=Horde. Faction-specific overrides Both. +-- auto_gear_score_limit: matched exactly against AiPlayerbot.AutoGearScoreLimit. +-- phase: free-text label for readability (e.g. 'Phase 5', 'Pre-Raid'). +-- Slot uses AzerothCore EquipmentSlots enum: +-- head=0, neck=1, shoulders=2, chest=4, waist=5, legs=6, feet=7, wrists=8, hands=9, +-- finger1=10, finger2=11, trinket1=12, trinket2=13, back=14, mainhand=15, offhand=16, ranged=17. +-- Druid Bear lives under sentinel tab=10 (resolved at runtime when bot has Tank strategy). + +DROP TABLE IF EXISTS `playerbots_bis`; +DROP TABLE IF EXISTS `playerbots_bis_gear`; +CREATE TABLE `playerbots_bis_gear` ( + `class` TINYINT UNSIGNED NOT NULL, + `tab` TINYINT UNSIGNED NOT NULL, + `slot` TINYINT UNSIGNED NOT NULL, + `faction` TINYINT UNSIGNED NOT NULL DEFAULT 0, + `auto_gear_score_limit` SMALLINT UNSIGNED NOT NULL, + `item_id` INT UNSIGNED NOT NULL, + `phase` VARCHAR(32) NOT NULL DEFAULT '', + `class_name` VARCHAR(16) NOT NULL, + `spec_name` VARCHAR(32) NOT NULL, + `slot_name` VARCHAR(16) NOT NULL, + `faction_name` VARCHAR(8) NOT NULL DEFAULT 'Both', + `item_name` VARCHAR(96) NOT NULL, + PRIMARY KEY (`class`, `tab`, `slot`, `faction`, `auto_gear_score_limit`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + + +-- ============================================================ +-- Warrior (1) +-- ============================================================ +-- Arms (tab 0) +-- ilvl 120 (Pre-Raid) +INSERT INTO `playerbots_bis_gear` VALUES +(1, 0, 0, 0, 120, 32087, 'Pre-Raid', 'Warrior', 'Arms', 'Head', 'Both', 'Mask of the Deceiver'), +(1, 0, 1, 0, 120, 29349, 'Pre-Raid', 'Warrior', 'Arms', 'Neck', 'Both', 'Adamantine Chain of the Unbroken'), +(1, 0, 2, 0, 120, 33173, 'Pre-Raid', 'Warrior', 'Arms', 'Shoulders', 'Both', 'Ragesteel Shoulders'), +(1, 0, 4, 0, 120, 23522, 'Pre-Raid', 'Warrior', 'Arms', 'Chest', 'Both', 'Ragesteel Breastplate'), +(1, 0, 5, 0, 120, 27985, 'Pre-Raid', 'Warrior', 'Arms', 'Waist', 'Both', 'Deathforge Girdle'), +(1, 0, 6, 0, 120, 30538, 'Pre-Raid', 'Warrior', 'Arms', 'Legs', 'Both', 'Midnight Legguards'), +(1, 0, 7, 0, 120, 25686, 'Pre-Raid', 'Warrior', 'Arms', 'Feet', 'Both', 'Fel Leather Boots'), +(1, 0, 8, 0, 120, 23537, 'Pre-Raid', 'Warrior', 'Arms', 'Wrists', 'Both', 'Black Felsteel Bracers'), +(1, 0, 9, 0, 120, 25685, 'Pre-Raid', 'Warrior', 'Arms', 'Hands', 'Both', 'Fel Leather Gloves'), +(1, 0, 10, 0, 120, 29379, 'Pre-Raid', 'Warrior', 'Arms', 'Finger1', 'Both', 'Ring of Arathi Warlords'), +(1, 0, 11, 0, 120, 30834, 'Pre-Raid', 'Warrior', 'Arms', 'Finger2', 'Both', 'Shapeshifter''s Signet'), +(1, 0, 12, 0, 120, 29383, 'Pre-Raid', 'Warrior', 'Arms', 'Trinket1', 'Both', 'Bloodlust Brooch'), +(1, 0, 13, 0, 120, 28034, 'Pre-Raid', 'Warrior', 'Arms', 'Trinket2', 'Both', 'Hourglass of the Unraveller'), +(1, 0, 14, 0, 120, 24259, 'Pre-Raid', 'Warrior', 'Arms', 'Back', 'Both', 'Vengeance Wrap'), +(1, 0, 15, 0, 120, 28438, 'Pre-Raid', 'Warrior', 'Arms', 'MainHand', 'Both', 'Dragonmaw'), +(1, 0, 16, 0, 120, 23542, 'Pre-Raid', 'Warrior', 'Arms', 'OffHand', 'Both', 'Fel Edged Battleaxe'), +(1, 0, 17, 0, 120, 30279, 'Pre-Raid', 'Warrior', 'Arms', 'Ranged', 'Both', 'Mama''s Insurance'); + +-- ilvl 125 (Phase 1) +INSERT INTO `playerbots_bis_gear` VALUES +(1, 0, 0, 0, 125, 29021, 'Phase 1', 'Warrior', 'Arms', 'Head', 'Both', 'Warbringer Battle-Helm'), +(1, 0, 1, 0, 125, 29349, 'Phase 1', 'Warrior', 'Arms', 'Neck', 'Both', 'Adamantine Chain of the Unbroken'), +(1, 0, 2, 0, 125, 30740, 'Phase 1', 'Warrior', 'Arms', 'Shoulders', 'Both', 'Ripfiend Shoulderplates'), +(1, 0, 4, 0, 125, 29019, 'Phase 1', 'Warrior', 'Arms', 'Chest', 'Both', 'Warbringer Breastplate'), +(1, 0, 5, 0, 125, 28779, 'Phase 1', 'Warrior', 'Arms', 'Waist', 'Both', 'Girdle of the Endless Pit'), +(1, 0, 6, 0, 125, 28741, 'Phase 1', 'Warrior', 'Arms', 'Legs', 'Both', 'Skulker''s Greaves'), +(1, 0, 7, 0, 125, 28608, 'Phase 1', 'Warrior', 'Arms', 'Feet', 'Both', 'Ironstriders of Urgency'), +(1, 0, 8, 0, 125, 28795, 'Phase 1', 'Warrior', 'Arms', 'Wrists', 'Both', 'Bladespire Warbands'), +(1, 0, 9, 0, 125, 28824, 'Phase 1', 'Warrior', 'Arms', 'Hands', 'Both', 'Gauntlets of Martial Perfection'), +(1, 0, 10, 0, 125, 28757, 'Phase 1', 'Warrior', 'Arms', 'Finger1', 'Both', 'Ring of a Thousand Marks'), +(1, 0, 11, 0, 125, 30834, 'Phase 1', 'Warrior', 'Arms', 'Finger2', 'Both', 'Shapeshifter''s Signet'), +(1, 0, 12, 0, 125, 29383, 'Phase 1', 'Warrior', 'Arms', 'Trinket1', 'Both', 'Bloodlust Brooch'), +(1, 0, 13, 0, 125, 28830, 'Phase 1', 'Warrior', 'Arms', 'Trinket2', 'Both', 'Dragonspine Trophy'), +(1, 0, 14, 0, 125, 24259, 'Phase 1', 'Warrior', 'Arms', 'Back', 'Both', 'Vengeance Wrap'), +(1, 0, 15, 0, 125, 28438, 'Phase 1', 'Warrior', 'Arms', 'MainHand', 'Both', 'Dragonmaw'), +(1, 0, 16, 0, 125, 31332, 'Phase 1', 'Warrior', 'Arms', 'OffHand', 'Both', 'Blinkstrike'), +(1, 0, 17, 0, 125, 28772, 'Phase 1', 'Warrior', 'Arms', 'Ranged', 'Both', 'Sunfury Bow of the Phoenix'); + +-- ilvl 141 (Phase 2) +INSERT INTO `playerbots_bis_gear` VALUES +(1, 0, 0, 0, 141, 30120, 'Phase 2', 'Warrior', 'Arms', 'Head', 'Both', 'Destroyer Battle-Helm'), +(1, 0, 1, 0, 141, 30022, 'Phase 2', 'Warrior', 'Arms', 'Neck', 'Both', 'Pendant of the Perilous'), +(1, 0, 2, 0, 141, 30122, 'Phase 2', 'Warrior', 'Arms', 'Shoulders', 'Both', 'Destroyer Shoulderblades'), +(1, 0, 4, 0, 141, 30118, 'Phase 2', 'Warrior', 'Arms', 'Chest', 'Both', 'Destroyer Breastplate'), +(1, 0, 5, 0, 141, 30106, 'Phase 2', 'Warrior', 'Arms', 'Waist', 'Both', 'Belt of One-Hundred Deaths'), +(1, 0, 6, 0, 141, 29995, 'Phase 2', 'Warrior', 'Arms', 'Legs', 'Both', 'Leggings of Murderous Intent'), +(1, 0, 7, 0, 141, 30081, 'Phase 2', 'Warrior', 'Arms', 'Feet', 'Both', 'Warboots of Obliteration'), +(1, 0, 8, 0, 141, 30057, 'Phase 2', 'Warrior', 'Arms', 'Wrists', 'Both', 'Bracers of Eradication'), +(1, 0, 9, 0, 141, 30119, 'Phase 2', 'Warrior', 'Arms', 'Hands', 'Both', 'Destroyer Gauntlets'), +(1, 0, 10, 0, 141, 29997, 'Phase 2', 'Warrior', 'Arms', 'Finger1', 'Both', 'Band of the Ranger-General'), +(1, 0, 11, 0, 141, 30834, 'Phase 2', 'Warrior', 'Arms', 'Finger2', 'Both', 'Shapeshifter''s Signet'), +(1, 0, 12, 0, 141, 29383, 'Phase 2', 'Warrior', 'Arms', 'Trinket1', 'Both', 'Bloodlust Brooch'), +(1, 0, 13, 0, 141, 28830, 'Phase 2', 'Warrior', 'Arms', 'Trinket2', 'Both', 'Dragonspine Trophy'), +(1, 0, 14, 0, 141, 24259, 'Phase 2', 'Warrior', 'Arms', 'Back', 'Both', 'Vengeance Wrap'), +(1, 0, 15, 0, 141, 28439, 'Phase 2', 'Warrior', 'Arms', 'MainHand', 'Both', 'Dragonstrike'), +(1, 0, 16, 0, 141, 30082, 'Phase 2', 'Warrior', 'Arms', 'OffHand', 'Both', 'Talon of Azshara'), +(1, 0, 17, 0, 141, 30105, 'Phase 2', 'Warrior', 'Arms', 'Ranged', 'Both', 'Serpent Spine Longbow'); + +-- ilvl 156 (Phase 3) +INSERT INTO `playerbots_bis_gear` VALUES +(1, 0, 0, 0, 156, 32235, 'Phase 3', 'Warrior', 'Arms', 'Head', 'Both', 'Cursed Vision of Sargeras'), +(1, 0, 1, 0, 156, 32591, 'Phase 3', 'Warrior', 'Arms', 'Neck', 'Both', 'Choker of Serrated Blades'), +(1, 0, 2, 0, 156, 30979, 'Phase 3', 'Warrior', 'Arms', 'Shoulders', 'Both', 'Onslaught Shoulderblades'), +(1, 0, 4, 0, 156, 30975, 'Phase 3', 'Warrior', 'Arms', 'Chest', 'Both', 'Onslaught Breastplate'), +(1, 0, 5, 0, 156, 30106, 'Phase 3', 'Warrior', 'Arms', 'Waist', 'Both', 'Belt of One-Hundred Deaths'), +(1, 0, 6, 0, 156, 32341, 'Phase 3', 'Warrior', 'Arms', 'Legs', 'Both', 'Leggings of Divine Retribution'), +(1, 0, 7, 0, 156, 32345, 'Phase 3', 'Warrior', 'Arms', 'Feet', 'Both', 'Dreadboots of the Legion'), +(1, 0, 8, 0, 156, 30863, 'Phase 3', 'Warrior', 'Arms', 'Wrists', 'Both', 'Deadly Cuffs'), +(1, 0, 9, 0, 156, 32278, 'Phase 3', 'Warrior', 'Arms', 'Hands', 'Both', 'Grips of Silent Justice'), +(1, 0, 10, 0, 156, 32497, 'Phase 3', 'Warrior', 'Arms', 'Finger1', 'Both', 'Stormrage Signet Ring'), +(1, 0, 11, 0, 156, 32335, 'Phase 3', 'Warrior', 'Arms', 'Finger2', 'Both', 'Unstoppable Aggressor''s Ring'), +(1, 0, 12, 0, 156, 28830, 'Phase 3', 'Warrior', 'Arms', 'Trinket1', 'Both', 'Dragonspine Trophy'), +(1, 0, 13, 0, 156, 32505, 'Phase 3', 'Warrior', 'Arms', 'Trinket2', 'Both', 'Madness of the Betrayer'), +(1, 0, 14, 0, 156, 32323, 'Phase 3', 'Warrior', 'Arms', 'Back', 'Both', 'Shadowmoon Destroyer''s Drape'), +(1, 0, 15, 0, 156, 32837, 'Phase 3', 'Warrior', 'Arms', 'MainHand', 'Both', 'Warglaive of Azzinoth'), +(1, 0, 16, 0, 156, 32838, 'Phase 3', 'Warrior', 'Arms', 'OffHand', 'Both', 'Warglaive of Azzinoth'), +(1, 0, 17, 0, 156, 32326, 'Phase 3', 'Warrior', 'Arms', 'Ranged', 'Both', 'Twisted Blades of Zarak'); + +-- ilvl 164 (Phase 4) +INSERT INTO `playerbots_bis_gear` VALUES +(1, 0, 0, 0, 164, 32235, 'Phase 4', 'Warrior', 'Arms', 'Head', 'Both', 'Cursed Vision of Sargeras'), +(1, 0, 1, 0, 164, 32591, 'Phase 4', 'Warrior', 'Arms', 'Neck', 'Both', 'Choker of Serrated Blades'), +(1, 0, 2, 0, 164, 30979, 'Phase 4', 'Warrior', 'Arms', 'Shoulders', 'Both', 'Onslaught Shoulderblades'), +(1, 0, 4, 0, 164, 30975, 'Phase 4', 'Warrior', 'Arms', 'Chest', 'Both', 'Onslaught Breastplate'), +(1, 0, 5, 0, 164, 30106, 'Phase 4', 'Warrior', 'Arms', 'Waist', 'Both', 'Belt of One-Hundred Deaths'), +(1, 0, 6, 0, 164, 32341, 'Phase 4', 'Warrior', 'Arms', 'Legs', 'Both', 'Leggings of Divine Retribution'), +(1, 0, 7, 0, 164, 32345, 'Phase 4', 'Warrior', 'Arms', 'Feet', 'Both', 'Dreadboots of the Legion'), +(1, 0, 8, 0, 164, 30863, 'Phase 4', 'Warrior', 'Arms', 'Wrists', 'Both', 'Deadly Cuffs'), +(1, 0, 9, 0, 164, 32278, 'Phase 4', 'Warrior', 'Arms', 'Hands', 'Both', 'Grips of Silent Justice'), +(1, 0, 10, 0, 164, 33496, 'Phase 4', 'Warrior', 'Arms', 'Finger1', 'Both', 'Signet of Primal Wrath'), +(1, 0, 11, 0, 164, 32497, 'Phase 4', 'Warrior', 'Arms', 'Finger2', 'Both', 'Stormrage Signet Ring'), +(1, 0, 12, 0, 164, 28830, 'Phase 4', 'Warrior', 'Arms', 'Trinket1', 'Both', 'Dragonspine Trophy'), +(1, 0, 13, 0, 164, 32505, 'Phase 4', 'Warrior', 'Arms', 'Trinket2', 'Both', 'Madness of the Betrayer'), +(1, 0, 14, 0, 164, 32323, 'Phase 4', 'Warrior', 'Arms', 'Back', 'Both', 'Shadowmoon Destroyer''s Drape'), +(1, 0, 15, 0, 164, 32837, 'Phase 4', 'Warrior', 'Arms', 'MainHand', 'Both', 'Warglaive of Azzinoth'), +(1, 0, 16, 0, 164, 32838, 'Phase 4', 'Warrior', 'Arms', 'OffHand', 'Both', 'Warglaive of Azzinoth'), +(1, 0, 17, 0, 164, 33474, 'Phase 4', 'Warrior', 'Arms', 'Ranged', 'Both', 'Ancient Amani Longbow'); + +-- ilvl 200 (Pre-Raid) +INSERT INTO `playerbots_bis_gear` VALUES +(1, 0, 0, 0, 200, 41386, 'Pre-Raid', 'Warrior', 'Arms', 'Head', 'Both', 'Spiked Titansteel Helm'), +(1, 0, 1, 0, 200, 42645, 'Pre-Raid', 'Warrior', 'Arms', 'Neck', 'Both', 'Titanium Impact Choker'), +(1, 0, 2, 0, 200, 37627, 'Pre-Raid', 'Warrior', 'Arms', 'Shoulders', 'Both', 'Snake Den Spaulders'), +(1, 0, 4, 0, 200, 37612, 'Pre-Raid', 'Warrior', 'Arms', 'Chest', 'Both', 'Bonegrinder Breastplate'), +(1, 0, 5, 0, 200, 37171, 'Pre-Raid', 'Warrior', 'Arms', 'Waist', 'Both', 'Flame-Bathed Steel Girdle'), +(1, 0, 6, 0, 200, 37193, 'Pre-Raid', 'Warrior', 'Arms', 'Legs', 'Both', 'Staggering Legplates'), +(1, 0, 7, 0, 200, 41391, 'Pre-Raid', 'Warrior', 'Arms', 'Feet', 'Both', 'Spiked Titansteel Treads'), +(1, 0, 8, 0, 200, 37668, 'Pre-Raid', 'Warrior', 'Arms', 'Wrists', 'Both', 'Bands of the Stoneforge'), +(1, 0, 9, 0, 200, 37363, 'Pre-Raid', 'Warrior', 'Arms', 'Hands', 'Both', 'Gauntlets of Dragon Wrath'), +(1, 0, 10, 0, 200, 37642, 'Pre-Raid', 'Warrior', 'Arms', 'Finger1', 'Both', 'Hemorrhaging Circle'), +(1, 0, 12, 0, 200, 40684, 'Pre-Raid', 'Warrior', 'Arms', 'Trinket1', 'Both', 'Mirror of Truth'), +(1, 0, 14, 0, 200, 43566, 'Pre-Raid', 'Warrior', 'Arms', 'Back', 'Both', 'Ice Striker''s Cloak'), +(1, 0, 15, 0, 200, 41257, 'Pre-Raid', 'Warrior', 'Arms', 'MainHand', 'Both', 'Titansteel Destroyer'); + +-- ilvl 224 (Phase 1) +INSERT INTO `playerbots_bis_gear` VALUES +(1, 0, 0, 0, 224, 40528, 'Phase 1', 'Warrior', 'Arms', 'Head', 'Both', 'Valorous Dreadnaught Helmet'), +(1, 0, 1, 0, 224, 44664, 'Phase 1', 'Warrior', 'Arms', 'Neck', 'Both', 'Favor of the Dragon Queen'), +(1, 0, 2, 0, 224, 40530, 'Phase 1', 'Warrior', 'Arms', 'Shoulders', 'Both', 'Valorous Dreadnaught Shoulderplates'), +(1, 0, 4, 0, 224, 40525, 'Phase 1', 'Warrior', 'Arms', 'Chest', 'Both', 'Valorous Dreadnaught Battleplate'), +(1, 0, 5, 0, 224, 40317, 'Phase 1', 'Warrior', 'Arms', 'Waist', 'Both', 'Girdle of Razuvious'), +(1, 0, 6, 0, 224, 40318, 'Phase 1', 'Warrior', 'Arms', 'Legs', 'Both', 'Legplates of Double Strikes'), +(1, 0, 7, 0, 224, 40206, 'Phase 1', 'Warrior', 'Arms', 'Feet', 'Both', 'Iron-Spring Jumpers'), +(1, 0, 8, 0, 224, 40733, 'Phase 1', 'Warrior', 'Arms', 'Wrists', 'Both', 'Wristbands of the Sentinel Huntress'), +(1, 0, 9, 0, 224, 40527, 'Phase 1', 'Warrior', 'Arms', 'Hands', 'Both', 'Valorous Dreadnaught Gauntlets'), +(1, 0, 10, 0, 224, 40075, 'Phase 1', 'Warrior', 'Arms', 'Finger1', 'Both', 'Ruthlessness'), +(1, 0, 12, 0, 224, 40256, 'Phase 1', 'Warrior', 'Arms', 'Trinket1', 'Both', 'Grim Toll'), +(1, 0, 14, 0, 224, 40250, 'Phase 1', 'Warrior', 'Arms', 'Back', 'Both', 'Aged Winter Cloak'); + +-- ilvl 245 (Phase 2) +INSERT INTO `playerbots_bis_gear` VALUES +(1, 0, 0, 0, 245, 46151, 'Phase 2', 'Warrior', 'Arms', 'Head', 'Both', 'Conqueror''s Siegebreaker Helmet'), +(1, 0, 1, 0, 245, 45459, 'Phase 2', 'Warrior', 'Arms', 'Neck', 'Both', 'Frigid Strength of Hodir'), +(1, 0, 2, 0, 245, 46149, 'Phase 2', 'Warrior', 'Arms', 'Shoulders', 'Both', 'Conqueror''s Siegebreaker Shoulderplates'), +(1, 0, 4, 0, 245, 46146, 'Phase 2', 'Warrior', 'Arms', 'Chest', 'Both', 'Conqueror''s Siegebreaker Battleplate'), +(1, 0, 5, 0, 245, 45241, 'Phase 2', 'Warrior', 'Arms', 'Waist', 'Both', 'Belt of Colossal Rage'), +(1, 0, 6, 0, 245, 45536, 'Phase 2', 'Warrior', 'Arms', 'Legs', 'Both', 'Legguards of Cunning Deception'), +(1, 0, 7, 0, 245, 45599, 'Phase 2', 'Warrior', 'Arms', 'Feet', 'Both', 'Sabatons of Lifeless Night'), +(1, 0, 8, 0, 245, 45663, 'Phase 2', 'Warrior', 'Arms', 'Wrists', 'Both', 'Armbands of Bedlam'), +(1, 0, 9, 0, 245, 46148, 'Phase 2', 'Warrior', 'Arms', 'Hands', 'Both', 'Conqueror''s Siegebreaker Gauntlets'), +(1, 0, 10, 0, 245, 45608, 'Phase 2', 'Warrior', 'Arms', 'Finger1', 'Both', 'Brann''s Signet Ring'), +(1, 0, 12, 0, 245, 46038, 'Phase 2', 'Warrior', 'Arms', 'Trinket1', 'Both', 'Dark Matter'), +(1, 0, 14, 0, 245, 46032, 'Phase 2', 'Warrior', 'Arms', 'Back', 'Both', 'Drape of the Faceless General'), +(1, 0, 17, 0, 245, 45296, 'Phase 2', 'Warrior', 'Arms', 'Ranged', 'Both', 'Twirling Blades'); + +-- ilvl 258 (Phase 3) +INSERT INTO `playerbots_bis_gear` VALUES +(1, 0, 0, 1, 258, 48383, 'Phase 3', 'Warrior', 'Arms', 'Head', 'Alliance', 'Wrynn''s Helmet of Triumph'), +(1, 0, 0, 2, 258, 48398, 'Phase 3', 'Warrior', 'Arms', 'Head', 'Horde', 'Hellscream''s Helmet of Triumph'), +(1, 0, 1, 1, 258, 47915, 'Phase 3', 'Warrior', 'Arms', 'Neck', 'Alliance', 'Collar of Ceaseless Torment'), +(1, 0, 1, 2, 258, 47988, 'Phase 3', 'Warrior', 'Arms', 'Neck', 'Horde', 'Collar of Unending Torment'), +(1, 0, 2, 1, 258, 48381, 'Phase 3', 'Warrior', 'Arms', 'Shoulders', 'Alliance', 'Wrynn''s Shoulderplates of Triumph'), +(1, 0, 2, 2, 258, 48400, 'Phase 3', 'Warrior', 'Arms', 'Shoulders', 'Horde', 'Hellscream''s Shoulderplates of Triumph'), +(1, 0, 4, 1, 258, 48385, 'Phase 3', 'Warrior', 'Arms', 'Chest', 'Alliance', 'Wrynn''s Battleplate of Triumph'), +(1, 0, 4, 2, 258, 48396, 'Phase 3', 'Warrior', 'Arms', 'Chest', 'Horde', 'Hellscream''s Battleplate of Triumph'), +(1, 0, 5, 1, 258, 47153, 'Phase 3', 'Warrior', 'Arms', 'Waist', 'Alliance', 'Belt of Deathly Dominion'), +(1, 0, 5, 2, 258, 47472, 'Phase 3', 'Warrior', 'Arms', 'Waist', 'Horde', 'Waistguard of Deathly Dominion'), +(1, 0, 6, 1, 258, 48382, 'Phase 3', 'Warrior', 'Arms', 'Legs', 'Alliance', 'Wrynn''s Legplates of Triumph'), +(1, 0, 6, 2, 258, 48399, 'Phase 3', 'Warrior', 'Arms', 'Legs', 'Horde', 'Hellscream''s Legplates of Triumph'), +(1, 0, 7, 1, 258, 47077, 'Phase 3', 'Warrior', 'Arms', 'Feet', 'Alliance', 'Treads of the Icewalker'), +(1, 0, 7, 2, 258, 47445, 'Phase 3', 'Warrior', 'Arms', 'Feet', 'Horde', 'Icewalker Treads'), +(1, 0, 8, 1, 258, 47074, 'Phase 3', 'Warrior', 'Arms', 'Wrists', 'Alliance', 'Bracers of the Untold Massacre'), +(1, 0, 8, 2, 258, 47442, 'Phase 3', 'Warrior', 'Arms', 'Wrists', 'Horde', 'Bracers of the Silent Massacre'), +(1, 0, 9, 1, 258, 47240, 'Phase 3', 'Warrior', 'Arms', 'Hands', 'Alliance', 'Gloves of Bitter Reprisal'), +(1, 0, 9, 2, 258, 47492, 'Phase 3', 'Warrior', 'Arms', 'Hands', 'Horde', 'Gauntlets of Bitter Reprisal'), +(1, 0, 10, 0, 258, 45608, 'Phase 3', 'Warrior', 'Arms', 'Finger1', 'Both', 'Brann''s Signet Ring'), +(1, 0, 12, 0, 258, 46038, 'Phase 3', 'Warrior', 'Arms', 'Trinket1', 'Both', 'Dark Matter'), +(1, 0, 14, 1, 258, 47545, 'Phase 3', 'Warrior', 'Arms', 'Back', 'Alliance', 'Vereesa''s Dexterity'), +(1, 0, 14, 2, 258, 47546, 'Phase 3', 'Warrior', 'Arms', 'Back', 'Horde', 'Sylvanas'' Cunning'), +(1, 0, 17, 1, 258, 46995, 'Phase 3', 'Warrior', 'Arms', 'Ranged', 'Alliance', 'Talonstrike'), +(1, 0, 17, 2, 258, 47428, 'Phase 3', 'Warrior', 'Arms', 'Ranged', 'Horde', 'Death''s Head Crossbow'); + +-- ilvl 264 (Phase 4) +INSERT INTO `playerbots_bis_gear` VALUES +(1, 0, 0, 0, 264, 51227, 'Phase 4', 'Warrior', 'Arms', 'Head', 'Both', 'Sanctified Ymirjar Lord''s Helmet'), +(1, 0, 1, 0, 264, 50728, 'Phase 4', 'Warrior', 'Arms', 'Neck', 'Both', 'Lana''thel''s Chain of Flagellation'), +(1, 0, 2, 0, 264, 51229, 'Phase 4', 'Warrior', 'Arms', 'Shoulders', 'Both', 'Sanctified Ymirjar Lord''s Shoulderplates'), +(1, 0, 4, 0, 264, 51225, 'Phase 4', 'Warrior', 'Arms', 'Chest', 'Both', 'Sanctified Ymirjar Lord''s Battleplate'), +(1, 0, 5, 0, 264, 50707, 'Phase 4', 'Warrior', 'Arms', 'Waist', 'Both', 'Astrylian''s Sutured Cinch'), +(1, 0, 6, 0, 264, 50645, 'Phase 4', 'Warrior', 'Arms', 'Legs', 'Both', 'Leggings of Northern Lights'), +(1, 0, 7, 0, 264, 50639, 'Phase 4', 'Warrior', 'Arms', 'Feet', 'Both', 'Blood-Soaked Saronite Stompers'), +(1, 0, 8, 0, 264, 50670, 'Phase 4', 'Warrior', 'Arms', 'Wrists', 'Both', 'Toskk''s Maximized Wristguards'), +(1, 0, 9, 0, 264, 51226, 'Phase 4', 'Warrior', 'Arms', 'Hands', 'Both', 'Sanctified Ymirjar Lord''s Gauntlets'), +(1, 0, 10, 0, 264, 50402, 'Phase 4', 'Warrior', 'Arms', 'Finger1', 'Both', 'Ashen Band of Endless Vengeance'), +(1, 0, 12, 0, 264, 50363, 'Phase 4', 'Warrior', 'Arms', 'Trinket1', 'Both', 'Deathbringer''s Will'), +(1, 0, 14, 1, 264, 47545, 'Phase 4', 'Warrior', 'Arms', 'Back', 'Alliance', 'Vereesa''s Dexterity'), +(1, 0, 14, 2, 264, 47546, 'Phase 4', 'Warrior', 'Arms', 'Back', 'Horde', 'Sylvanas'' Cunning'), +(1, 0, 17, 0, 264, 50733, 'Phase 4', 'Warrior', 'Arms', 'Ranged', 'Both', 'Fal''inrush, Defender of Quel''thalas'); + +-- ilvl 290 (Phase 5) +INSERT INTO `playerbots_bis_gear` VALUES +(1, 0, 0, 0, 290, 51227, 'Phase 5', 'Warrior', 'Arms', 'Head', 'Both', 'Sanctified Ymirjar Lord''s Helmet'), +(1, 0, 1, 0, 290, 54581, 'Phase 5', 'Warrior', 'Arms', 'Neck', 'Both', 'Penumbra Pendant'), +(1, 0, 2, 0, 290, 51229, 'Phase 5', 'Warrior', 'Arms', 'Shoulders', 'Both', 'Sanctified Ymirjar Lord''s Shoulderplates'), +(1, 0, 4, 0, 290, 51225, 'Phase 5', 'Warrior', 'Arms', 'Chest', 'Both', 'Sanctified Ymirjar Lord''s Battleplate'), +(1, 0, 5, 0, 290, 50620, 'Phase 5', 'Warrior', 'Arms', 'Waist', 'Both', 'Coldwraith Links'), +(1, 0, 6, 0, 290, 51228, 'Phase 5', 'Warrior', 'Arms', 'Legs', 'Both', 'Sanctified Ymirjar Lord''s Legplates'), +(1, 0, 7, 0, 290, 54578, 'Phase 5', 'Warrior', 'Arms', 'Feet', 'Both', 'Apocalypse''s Advance'), +(1, 0, 8, 0, 290, 50670, 'Phase 5', 'Warrior', 'Arms', 'Wrists', 'Both', 'Toskk''s Maximized Wristguards'), +(1, 0, 9, 0, 290, 50675, 'Phase 5', 'Warrior', 'Arms', 'Hands', 'Both', 'Aldriana''s Gloves of Secrecy'), +(1, 0, 10, 0, 290, 50657, 'Phase 5', 'Warrior', 'Arms', 'Finger1', 'Both', 'Skeleton Lord''s Circle'), +(1, 0, 11, 0, 290, 52572, 'Phase 5', 'Warrior', 'Arms', 'Finger2', 'Both', 'Ashen Band of Endless Might'), +(1, 0, 12, 0, 290, 50363, 'Phase 5', 'Warrior', 'Arms', 'Trinket1', 'Both', 'Deathbringer''s Will'), +(1, 0, 13, 0, 290, 54590, 'Phase 5', 'Warrior', 'Arms', 'Trinket2', 'Both', 'Sharpened Twilight Scale'), +(1, 0, 14, 0, 290, 47545, 'Phase 5', 'Warrior', 'Arms', 'Back', 'Both', 'Vereesa''s Dexterity'), +(1, 0, 15, 0, 290, 49623, 'Phase 5', 'Warrior', 'Arms', 'MainHand', 'Both', 'Shadowmourne'), +(1, 0, 17, 0, 290, 50733, 'Phase 5', 'Warrior', 'Arms', 'Ranged', 'Both', 'Fal''inrush, Defender of Quel''thalas'); + +-- Fury (tab 1) +-- ilvl 66 (Phase 1 (Pre-Raid)) +INSERT INTO `playerbots_bis_gear` VALUES +(1, 1, 0, 0, 66, 13404, 'Phase 1 (Pre-Raid)', 'Warrior', 'Fury', 'Head', 'Both', 'Mask of the Unforgiven'), +(1, 1, 1, 0, 66, 15411, 'Phase 1 (Pre-Raid)', 'Warrior', 'Fury', 'Neck', 'Both', 'Mark of Fordring'), +(1, 1, 2, 0, 66, 12927, 'Phase 1 (Pre-Raid)', 'Warrior', 'Fury', 'Shoulders', 'Both', 'Truestrike Shoulders'), +(1, 1, 4, 0, 66, 11726, 'Phase 1 (Pre-Raid)', 'Warrior', 'Fury', 'Chest', 'Both', 'Savage Gladiator Chain'), +(1, 1, 5, 0, 66, 13142, 'Phase 1 (Pre-Raid)', 'Warrior', 'Fury', 'Waist', 'Both', 'Brigam Girdle'), +(1, 1, 6, 0, 66, 14554, 'Phase 1 (Pre-Raid)', 'Warrior', 'Fury', 'Legs', 'Both', 'Cloudkeeper Legplates'), +(1, 1, 7, 0, 66, 14616, 'Phase 1 (Pre-Raid)', 'Warrior', 'Fury', 'Feet', 'Both', 'Bloodmail Boots'), +(1, 1, 8, 0, 66, 12936, 'Phase 1 (Pre-Raid)', 'Warrior', 'Fury', 'Wrists', 'Both', 'Battleborn Armbraces'), +(1, 1, 9, 0, 66, 14551, 'Phase 1 (Pre-Raid)', 'Warrior', 'Fury', 'Hands', 'Both', 'Edgemaster''s Handguards'), +(1, 1, 10, 0, 66, 17713, 'Phase 1 (Pre-Raid)', 'Warrior', 'Fury', 'Finger1', 'Both', 'Blackstone Ring'), +(1, 1, 11, 0, 66, 13098, 'Phase 1 (Pre-Raid)', 'Warrior', 'Fury', 'Finger2', 'Both', 'Painweaver Band'), +(1, 1, 12, 0, 66, 13965, 'Phase 1 (Pre-Raid)', 'Warrior', 'Fury', 'Trinket1', 'Both', 'Blackhand''s Breadth'), +(1, 1, 13, 0, 66, 11815, 'Phase 1 (Pre-Raid)', 'Warrior', 'Fury', 'Trinket2', 'Both', 'Hand of Justice'), +(1, 1, 14, 0, 66, 13340, 'Phase 1 (Pre-Raid)', 'Warrior', 'Fury', 'Back', 'Both', 'Cape of the Black Baron'), +(1, 1, 15, 0, 66, 12940, 'Phase 1 (Pre-Raid)', 'Warrior', 'Fury', 'MainHand', 'Both', 'Dal''Rend''s Sacred Charge'), +(1, 1, 16, 0, 66, 12939, 'Phase 1 (Pre-Raid)', 'Warrior', 'Fury', 'OffHand', 'Both', 'Dal''Rend''s Tribal Guardian'), +(1, 1, 17, 0, 66, 12651, 'Phase 1 (Pre-Raid)', 'Warrior', 'Fury', 'Ranged', 'Both', 'Blackcrow'); + +-- ilvl 76 (Phase 2 (Pre-Raid)) +INSERT INTO `playerbots_bis_gear` VALUES +(1, 1, 0, 0, 76, 13404, 'Phase 2 (Pre-Raid)', 'Warrior', 'Fury', 'Head', 'Both', 'Mask of the Unforgiven'), +(1, 1, 1, 0, 76, 15411, 'Phase 2 (Pre-Raid)', 'Warrior', 'Fury', 'Neck', 'Both', 'Mark of Fordring'), +(1, 1, 2, 0, 76, 12927, 'Phase 2 (Pre-Raid)', 'Warrior', 'Fury', 'Shoulders', 'Both', 'Truestrike Shoulders'), +(1, 1, 4, 0, 76, 11726, 'Phase 2 (Pre-Raid)', 'Warrior', 'Fury', 'Chest', 'Both', 'Savage Gladiator Chain'), +(1, 1, 5, 0, 76, 13142, 'Phase 2 (Pre-Raid)', 'Warrior', 'Fury', 'Waist', 'Both', 'Brigam Girdle'), +(1, 1, 6, 0, 76, 14554, 'Phase 2 (Pre-Raid)', 'Warrior', 'Fury', 'Legs', 'Both', 'Cloudkeeper Legplates'), +(1, 1, 7, 0, 76, 14616, 'Phase 2 (Pre-Raid)', 'Warrior', 'Fury', 'Feet', 'Both', 'Bloodmail Boots'), +(1, 1, 8, 0, 76, 12936, 'Phase 2 (Pre-Raid)', 'Warrior', 'Fury', 'Wrists', 'Both', 'Battleborn Armbraces'), +(1, 1, 9, 0, 76, 14551, 'Phase 2 (Pre-Raid)', 'Warrior', 'Fury', 'Hands', 'Both', 'Edgemaster''s Handguards'), +(1, 1, 10, 0, 76, 17713, 'Phase 2 (Pre-Raid)', 'Warrior', 'Fury', 'Finger1', 'Both', 'Blackstone Ring'), +(1, 1, 11, 0, 76, 13098, 'Phase 2 (Pre-Raid)', 'Warrior', 'Fury', 'Finger2', 'Both', 'Painweaver Band'), +(1, 1, 12, 0, 76, 13965, 'Phase 2 (Pre-Raid)', 'Warrior', 'Fury', 'Trinket1', 'Both', 'Blackhand''s Breadth'), +(1, 1, 13, 0, 76, 11815, 'Phase 2 (Pre-Raid)', 'Warrior', 'Fury', 'Trinket2', 'Both', 'Hand of Justice'), +(1, 1, 14, 0, 76, 13340, 'Phase 2 (Pre-Raid)', 'Warrior', 'Fury', 'Back', 'Both', 'Cape of the Black Baron'), +(1, 1, 15, 0, 76, 12940, 'Phase 2 (Pre-Raid)', 'Warrior', 'Fury', 'MainHand', 'Both', 'Dal''Rend''s Sacred Charge'), +(1, 1, 16, 0, 76, 12939, 'Phase 2 (Pre-Raid)', 'Warrior', 'Fury', 'OffHand', 'Both', 'Dal''Rend''s Tribal Guardian'), +(1, 1, 17, 0, 76, 18323, 'Phase 2 (Pre-Raid)', 'Warrior', 'Fury', 'Ranged', 'Both', 'Satyr''s Bow'); + +-- ilvl 78 (Phase 2) +INSERT INTO `playerbots_bis_gear` VALUES +(1, 1, 0, 0, 78, 12640, 'Phase 2', 'Warrior', 'Fury', 'Head', 'Both', 'Lionheart Helm'), +(1, 1, 1, 0, 78, 18404, 'Phase 2', 'Warrior', 'Fury', 'Neck', 'Both', 'Onyxia Tooth Pendant'), +(1, 1, 2, 0, 78, 12927, 'Phase 2', 'Warrior', 'Fury', 'Shoulders', 'Both', 'Truestrike Shoulders'), +(1, 1, 4, 0, 78, 11726, 'Phase 2', 'Warrior', 'Fury', 'Chest', 'Both', 'Savage Gladiator Chain'), +(1, 1, 5, 0, 78, 19137, 'Phase 2', 'Warrior', 'Fury', 'Waist', 'Both', 'Onslaught Girdle'), +(1, 1, 6, 0, 78, 14554, 'Phase 2', 'Warrior', 'Fury', 'Legs', 'Both', 'Cloudkeeper Legplates'), +(1, 1, 7, 0, 78, 14616, 'Phase 2', 'Warrior', 'Fury', 'Feet', 'Both', 'Bloodmail Boots'), +(1, 1, 8, 0, 78, 19146, 'Phase 2', 'Warrior', 'Fury', 'Wrists', 'Both', 'Wristguards of Stability'), +(1, 1, 9, 0, 78, 14551, 'Phase 2', 'Warrior', 'Fury', 'Hands', 'Both', 'Edgemaster''s Handguards'), +(1, 1, 10, 0, 78, 17063, 'Phase 2', 'Warrior', 'Fury', 'Finger1', 'Both', 'Band of Accuria'), +(1, 1, 11, 0, 78, 18821, 'Phase 2', 'Warrior', 'Fury', 'Finger2', 'Both', 'Quick Strike Ring'), +(1, 1, 12, 0, 78, 13965, 'Phase 2', 'Warrior', 'Fury', 'Trinket1', 'Both', 'Blackhand''s Breadth'), +(1, 1, 13, 0, 78, 11815, 'Phase 2', 'Warrior', 'Fury', 'Trinket2', 'Both', 'Hand of Justice'), +(1, 1, 14, 0, 78, 18541, 'Phase 2', 'Warrior', 'Fury', 'Back', 'Both', 'Puissant Cape'), +(1, 1, 15, 0, 78, 17075, 'Phase 2', 'Warrior', 'Fury', 'MainHand', 'Both', 'Vis''kag the Bloodletter'), +(1, 1, 16, 0, 78, 18832, 'Phase 2', 'Warrior', 'Fury', 'OffHand', 'Both', 'Brutality Blade'), +(1, 1, 17, 0, 78, 17069, 'Phase 2', 'Warrior', 'Fury', 'Ranged', 'Both', 'Striker''s Mark'); + +-- ilvl 83 (Phase 3) +INSERT INTO `playerbots_bis_gear` VALUES +(1, 1, 0, 0, 83, 12640, 'Phase 3', 'Warrior', 'Fury', 'Head', 'Both', 'Lionheart Helm'), +(1, 1, 1, 0, 83, 18404, 'Phase 3', 'Warrior', 'Fury', 'Neck', 'Both', 'Onyxia Tooth Pendant'), +(1, 1, 2, 0, 83, 19394, 'Phase 3', 'Warrior', 'Fury', 'Shoulders', 'Both', 'Drake Talon Pauldrons'), +(1, 1, 4, 0, 83, 11726, 'Phase 3', 'Warrior', 'Fury', 'Chest', 'Both', 'Savage Gladiator Chain'), +(1, 1, 5, 0, 83, 19137, 'Phase 3', 'Warrior', 'Fury', 'Waist', 'Both', 'Onslaught Girdle'), +(1, 1, 6, 0, 83, 19402, 'Phase 3', 'Warrior', 'Fury', 'Legs', 'Both', 'Legguards of the Fallen Crusader'), +(1, 1, 7, 0, 83, 19387, 'Phase 3', 'Warrior', 'Fury', 'Feet', 'Both', 'Chromatic Boots'), +(1, 1, 8, 0, 83, 19146, 'Phase 3', 'Warrior', 'Fury', 'Wrists', 'Both', 'Wristguards of Stability'), +(1, 1, 9, 0, 83, 14551, 'Phase 3', 'Warrior', 'Fury', 'Hands', 'Both', 'Edgemaster''s Handguards'), +(1, 1, 10, 0, 83, 17063, 'Phase 3', 'Warrior', 'Fury', 'Finger1', 'Both', 'Band of Accuria'), +(1, 1, 11, 0, 83, 19384, 'Phase 3', 'Warrior', 'Fury', 'Finger2', 'Both', 'Master Dragonslayer''s Ring'), +(1, 1, 12, 0, 83, 19406, 'Phase 3', 'Warrior', 'Fury', 'Trinket1', 'Both', 'Drake Fang Talisman'), +(1, 1, 13, 0, 83, 11815, 'Phase 3', 'Warrior', 'Fury', 'Trinket2', 'Both', 'Hand of Justice'), +(1, 1, 14, 0, 83, 18541, 'Phase 3', 'Warrior', 'Fury', 'Back', 'Both', 'Puissant Cape'), +(1, 1, 15, 0, 83, 19352, 'Phase 3', 'Warrior', 'Fury', 'MainHand', 'Both', 'Chromatically Tempered Sword'), +(1, 1, 16, 0, 83, 19351, 'Phase 3', 'Warrior', 'Fury', 'OffHand', 'Both', 'Maladath, Runed Blade of the Black Flight'), +(1, 1, 17, 0, 83, 17069, 'Phase 3', 'Warrior', 'Fury', 'Ranged', 'Both', 'Striker''s Mark'); + +-- ilvl 88 (Phase 5) +INSERT INTO `playerbots_bis_gear` VALUES +(1, 1, 0, 0, 88, 12640, 'Phase 5', 'Warrior', 'Fury', 'Head', 'Both', 'Lionheart Helm'), +(1, 1, 1, 0, 88, 18404, 'Phase 5', 'Warrior', 'Fury', 'Neck', 'Both', 'Onyxia Tooth Pendant'), +(1, 1, 2, 0, 88, 21330, 'Phase 5', 'Warrior', 'Fury', 'Shoulders', 'Both', 'Conqueror''s Spaulders'), +(1, 1, 4, 0, 88, 21814, 'Phase 5', 'Warrior', 'Fury', 'Chest', 'Both', 'Breastplate of Annihilation'), +(1, 1, 5, 0, 88, 19137, 'Phase 5', 'Warrior', 'Fury', 'Waist', 'Both', 'Onslaught Girdle'), +(1, 1, 6, 0, 88, 22385, 'Phase 5', 'Warrior', 'Fury', 'Legs', 'Both', 'Titanic Leggings'), +(1, 1, 7, 0, 88, 19387, 'Phase 5', 'Warrior', 'Fury', 'Feet', 'Both', 'Chromatic Boots'), +(1, 1, 8, 0, 88, 21618, 'Phase 5', 'Warrior', 'Fury', 'Wrists', 'Both', 'Hive Defiler Wristguards'), +(1, 1, 9, 0, 88, 14551, 'Phase 5', 'Warrior', 'Fury', 'Hands', 'Both', 'Edgemaster''s Handguards'), +(1, 1, 10, 0, 88, 17063, 'Phase 5', 'Warrior', 'Fury', 'Finger1', 'Both', 'Band of Accuria'), +(1, 1, 11, 0, 88, 19384, 'Phase 5', 'Warrior', 'Fury', 'Finger2', 'Both', 'Master Dragonslayer''s Ring'), +(1, 1, 12, 0, 88, 19406, 'Phase 5', 'Warrior', 'Fury', 'Trinket1', 'Both', 'Drake Fang Talisman'), +(1, 1, 13, 0, 88, 21670, 'Phase 5', 'Warrior', 'Fury', 'Trinket2', 'Both', 'Badge of the Swarmguard'), +(1, 1, 14, 0, 88, 18541, 'Phase 5', 'Warrior', 'Fury', 'Back', 'Both', 'Puissant Cape'), +(1, 1, 15, 0, 88, 21650, 'Phase 5', 'Warrior', 'Fury', 'MainHand', 'Both', 'Ancient Qiraji Ripper'), +(1, 1, 16, 0, 88, 21650, 'Phase 5', 'Warrior', 'Fury', 'OffHand', 'Both', 'Ancient Qiraji Ripper'), +(1, 1, 17, 0, 88, 17069, 'Phase 5', 'Warrior', 'Fury', 'Ranged', 'Both', 'Striker''s Mark'); + +-- ilvl 92 (Phase 6) +INSERT INTO `playerbots_bis_gear` VALUES +(1, 1, 0, 0, 92, 12640, 'Phase 6', 'Warrior', 'Fury', 'Head', 'Both', 'Lionheart Helm'), +(1, 1, 1, 0, 92, 18404, 'Phase 6', 'Warrior', 'Fury', 'Neck', 'Both', 'Onyxia Tooth Pendant'), +(1, 1, 2, 0, 92, 21330, 'Phase 6', 'Warrior', 'Fury', 'Shoulders', 'Both', 'Conqueror''s Spaulders'), +(1, 1, 4, 0, 92, 23000, 'Phase 6', 'Warrior', 'Fury', 'Chest', 'Both', 'Plated Abomination Ribcage'), +(1, 1, 5, 0, 92, 19137, 'Phase 6', 'Warrior', 'Fury', 'Waist', 'Both', 'Onslaught Girdle'), +(1, 1, 6, 0, 92, 22385, 'Phase 6', 'Warrior', 'Fury', 'Legs', 'Both', 'Titanic Leggings'), +(1, 1, 7, 0, 92, 19387, 'Phase 6', 'Warrior', 'Fury', 'Feet', 'Both', 'Chromatic Boots'), +(1, 1, 8, 0, 92, 22936, 'Phase 6', 'Warrior', 'Fury', 'Wrists', 'Both', 'Wristguards of Vengeance'), +(1, 1, 9, 0, 92, 21581, 'Phase 6', 'Warrior', 'Fury', 'Hands', 'Both', 'Gauntlets of Annihilation'), +(1, 1, 11, 0, 92, 19384, 'Phase 6', 'Warrior', 'Fury', 'Finger2', 'Both', 'Master Dragonslayer''s Ring'), +(1, 1, 12, 0, 92, 19406, 'Phase 6', 'Warrior', 'Fury', 'Trinket1', 'Both', 'Drake Fang Talisman'), +(1, 1, 13, 0, 92, 21670, 'Phase 6', 'Warrior', 'Fury', 'Trinket2', 'Both', 'Badge of the Swarmguard'), +(1, 1, 14, 0, 92, 23045, 'Phase 6', 'Warrior', 'Fury', 'Back', 'Both', 'Shroud of Dominion'), +(1, 1, 15, 0, 92, 23054, 'Phase 6', 'Warrior', 'Fury', 'MainHand', 'Both', 'Gressil, Dawn of Ruin'), +(1, 1, 16, 0, 92, 23577, 'Phase 6', 'Warrior', 'Fury', 'OffHand', 'Both', 'The Hungering Cold'), +(1, 1, 17, 0, 92, 17069, 'Phase 6', 'Warrior', 'Fury', 'Ranged', 'Both', 'Striker''s Mark'); + +-- ilvl 120 (Pre-Raid) +INSERT INTO `playerbots_bis_gear` VALUES +(1, 1, 0, 0, 120, 32087, 'Pre-Raid', 'Warrior', 'Fury', 'Head', 'Both', 'Mask of the Deceiver'), +(1, 1, 1, 0, 120, 29349, 'Pre-Raid', 'Warrior', 'Fury', 'Neck', 'Both', 'Adamantine Chain of the Unbroken'), +(1, 1, 2, 0, 120, 33173, 'Pre-Raid', 'Warrior', 'Fury', 'Shoulders', 'Both', 'Ragesteel Shoulders'), +(1, 1, 4, 0, 120, 23522, 'Pre-Raid', 'Warrior', 'Fury', 'Chest', 'Both', 'Ragesteel Breastplate'), +(1, 1, 5, 0, 120, 27985, 'Pre-Raid', 'Warrior', 'Fury', 'Waist', 'Both', 'Deathforge Girdle'), +(1, 1, 6, 0, 120, 30538, 'Pre-Raid', 'Warrior', 'Fury', 'Legs', 'Both', 'Midnight Legguards'), +(1, 1, 7, 0, 120, 25686, 'Pre-Raid', 'Warrior', 'Fury', 'Feet', 'Both', 'Fel Leather Boots'), +(1, 1, 8, 0, 120, 23537, 'Pre-Raid', 'Warrior', 'Fury', 'Wrists', 'Both', 'Black Felsteel Bracers'), +(1, 1, 9, 0, 120, 25685, 'Pre-Raid', 'Warrior', 'Fury', 'Hands', 'Both', 'Fel Leather Gloves'), +(1, 1, 10, 0, 120, 29379, 'Pre-Raid', 'Warrior', 'Fury', 'Finger1', 'Both', 'Ring of Arathi Warlords'), +(1, 1, 11, 0, 120, 30834, 'Pre-Raid', 'Warrior', 'Fury', 'Finger2', 'Both', 'Shapeshifter''s Signet'), +(1, 1, 12, 0, 120, 29383, 'Pre-Raid', 'Warrior', 'Fury', 'Trinket1', 'Both', 'Bloodlust Brooch'), +(1, 1, 13, 0, 120, 28034, 'Pre-Raid', 'Warrior', 'Fury', 'Trinket2', 'Both', 'Hourglass of the Unraveller'), +(1, 1, 14, 0, 120, 24259, 'Pre-Raid', 'Warrior', 'Fury', 'Back', 'Both', 'Vengeance Wrap'), +(1, 1, 15, 0, 120, 28438, 'Pre-Raid', 'Warrior', 'Fury', 'MainHand', 'Both', 'Dragonmaw'), +(1, 1, 16, 0, 120, 23542, 'Pre-Raid', 'Warrior', 'Fury', 'OffHand', 'Both', 'Fel Edged Battleaxe'), +(1, 1, 17, 0, 120, 30279, 'Pre-Raid', 'Warrior', 'Fury', 'Ranged', 'Both', 'Mama''s Insurance'); + +-- ilvl 125 (Phase 1) +INSERT INTO `playerbots_bis_gear` VALUES +(1, 1, 0, 0, 125, 29021, 'Phase 1', 'Warrior', 'Fury', 'Head', 'Both', 'Warbringer Battle-Helm'), +(1, 1, 1, 0, 125, 29349, 'Phase 1', 'Warrior', 'Fury', 'Neck', 'Both', 'Adamantine Chain of the Unbroken'), +(1, 1, 2, 0, 125, 29023, 'Phase 1', 'Warrior', 'Fury', 'Shoulders', 'Both', 'Warbringer Shoulderplates'), +(1, 1, 4, 0, 125, 29019, 'Phase 1', 'Warrior', 'Fury', 'Chest', 'Both', 'Warbringer Breastplate'), +(1, 1, 5, 0, 125, 28779, 'Phase 1', 'Warrior', 'Fury', 'Waist', 'Both', 'Girdle of the Endless Pit'), +(1, 1, 6, 0, 125, 28741, 'Phase 1', 'Warrior', 'Fury', 'Legs', 'Both', 'Skulker''s Greaves'), +(1, 1, 7, 0, 125, 28608, 'Phase 1', 'Warrior', 'Fury', 'Feet', 'Both', 'Ironstriders of Urgency'), +(1, 1, 8, 0, 125, 28795, 'Phase 1', 'Warrior', 'Fury', 'Wrists', 'Both', 'Bladespire Warbands'), +(1, 1, 9, 0, 125, 28824, 'Phase 1', 'Warrior', 'Fury', 'Hands', 'Both', 'Gauntlets of Martial Perfection'), +(1, 1, 10, 0, 125, 28757, 'Phase 1', 'Warrior', 'Fury', 'Finger1', 'Both', 'Ring of a Thousand Marks'), +(1, 1, 11, 0, 125, 30834, 'Phase 1', 'Warrior', 'Fury', 'Finger2', 'Both', 'Shapeshifter''s Signet'), +(1, 1, 12, 0, 125, 29383, 'Phase 1', 'Warrior', 'Fury', 'Trinket1', 'Both', 'Bloodlust Brooch'), +(1, 1, 13, 0, 125, 28830, 'Phase 1', 'Warrior', 'Fury', 'Trinket2', 'Both', 'Dragonspine Trophy'), +(1, 1, 14, 0, 125, 24259, 'Phase 1', 'Warrior', 'Fury', 'Back', 'Both', 'Vengeance Wrap'), +(1, 1, 15, 0, 125, 28438, 'Phase 1', 'Warrior', 'Fury', 'MainHand', 'Both', 'Dragonmaw'), +(1, 1, 16, 0, 125, 31332, 'Phase 1', 'Warrior', 'Fury', 'OffHand', 'Both', 'Blinkstrike'), +(1, 1, 17, 0, 125, 30724, 'Phase 1', 'Warrior', 'Fury', 'Ranged', 'Both', 'Barrel-Blade Longrifle'); + +-- ilvl 141 (Phase 2) +INSERT INTO `playerbots_bis_gear` VALUES +(1, 1, 0, 0, 141, 30120, 'Phase 2', 'Warrior', 'Fury', 'Head', 'Both', 'Destroyer Battle-Helm'), +(1, 1, 1, 0, 141, 30022, 'Phase 2', 'Warrior', 'Fury', 'Neck', 'Both', 'Pendant of the Perilous'), +(1, 1, 2, 0, 141, 30122, 'Phase 2', 'Warrior', 'Fury', 'Shoulders', 'Both', 'Destroyer Shoulderblades'), +(1, 1, 4, 0, 141, 30118, 'Phase 2', 'Warrior', 'Fury', 'Chest', 'Both', 'Destroyer Breastplate'), +(1, 1, 5, 0, 141, 30106, 'Phase 2', 'Warrior', 'Fury', 'Waist', 'Both', 'Belt of One-Hundred Deaths'), +(1, 1, 6, 0, 141, 29995, 'Phase 2', 'Warrior', 'Fury', 'Legs', 'Both', 'Leggings of Murderous Intent'), +(1, 1, 7, 0, 141, 30081, 'Phase 2', 'Warrior', 'Fury', 'Feet', 'Both', 'Warboots of Obliteration'), +(1, 1, 8, 0, 141, 30057, 'Phase 2', 'Warrior', 'Fury', 'Wrists', 'Both', 'Bracers of Eradication'), +(1, 1, 9, 0, 141, 30119, 'Phase 2', 'Warrior', 'Fury', 'Hands', 'Both', 'Destroyer Gauntlets'), +(1, 1, 10, 0, 141, 29997, 'Phase 2', 'Warrior', 'Fury', 'Finger1', 'Both', 'Band of the Ranger-General'), +(1, 1, 11, 0, 141, 28757, 'Phase 2', 'Warrior', 'Fury', 'Finger2', 'Both', 'Ring of a Thousand Marks'), +(1, 1, 12, 0, 141, 29383, 'Phase 2', 'Warrior', 'Fury', 'Trinket1', 'Both', 'Bloodlust Brooch'), +(1, 1, 13, 0, 141, 28830, 'Phase 2', 'Warrior', 'Fury', 'Trinket2', 'Both', 'Dragonspine Trophy'), +(1, 1, 14, 0, 141, 24259, 'Phase 2', 'Warrior', 'Fury', 'Back', 'Both', 'Vengeance Wrap'), +(1, 1, 15, 0, 141, 28439, 'Phase 2', 'Warrior', 'Fury', 'MainHand', 'Both', 'Dragonstrike'), +(1, 1, 16, 0, 141, 30082, 'Phase 2', 'Warrior', 'Fury', 'OffHand', 'Both', 'Talon of Azshara'), +(1, 1, 17, 0, 141, 30105, 'Phase 2', 'Warrior', 'Fury', 'Ranged', 'Both', 'Serpent Spine Longbow'); + +-- ilvl 200 (Pre-Raid) +INSERT INTO `playerbots_bis_gear` VALUES +(1, 1, 0, 0, 200, 41386, 'Pre-Raid', 'Warrior', 'Fury', 'Head', 'Both', 'Spiked Titansteel Helm'), +(1, 1, 1, 0, 200, 42645, 'Pre-Raid', 'Warrior', 'Fury', 'Neck', 'Both', 'Titanium Impact Choker'), +(1, 1, 2, 0, 200, 37627, 'Pre-Raid', 'Warrior', 'Fury', 'Shoulders', 'Both', 'Snake Den Spaulders'), +(1, 1, 4, 0, 200, 39606, 'Pre-Raid', 'Warrior', 'Fury', 'Chest', 'Both', 'Heroes'' Dreadnaught Battleplate'), +(1, 1, 5, 0, 200, 37826, 'Pre-Raid', 'Warrior', 'Fury', 'Waist', 'Both', 'The General''s Steel Girdle'), +(1, 1, 6, 0, 200, 39607, 'Pre-Raid', 'Warrior', 'Fury', 'Legs', 'Both', 'Heroes'' Dreadnaught Legplates'), +(1, 1, 7, 0, 200, 43402, 'Pre-Raid', 'Warrior', 'Fury', 'Feet', 'Both', 'The Obliterator Greaves'), +(1, 1, 8, 0, 200, 37891, 'Pre-Raid', 'Warrior', 'Fury', 'Wrists', 'Both', 'Cast Iron Shackles'), +(1, 1, 9, 0, 200, 39609, 'Pre-Raid', 'Warrior', 'Fury', 'Hands', 'Both', 'Heroes'' Dreadnaught Gauntlets'), +(1, 1, 10, 0, 200, 37151, 'Pre-Raid', 'Warrior', 'Fury', 'Finger1', 'Both', 'Band of Frosted Thorns'), +(1, 1, 12, 0, 200, 40684, 'Pre-Raid', 'Warrior', 'Fury', 'Trinket1', 'Both', 'Mirror of Truth'), +(1, 1, 14, 0, 200, 43566, 'Pre-Raid', 'Warrior', 'Fury', 'Back', 'Both', 'Ice Striker''s Cloak'), +(1, 1, 15, 0, 200, 41257, 'Pre-Raid', 'Warrior', 'Fury', 'MainHand', 'Both', 'Titansteel Destroyer'), +(1, 1, 17, 0, 200, 43284, 'Pre-Raid', 'Warrior', 'Fury', 'Ranged', 'Both', 'Amanitar Skullbow'); + +-- ilvl 224 (Phase 1) +INSERT INTO `playerbots_bis_gear` VALUES +(1, 1, 0, 0, 224, 44006, 'Phase 1', 'Warrior', 'Fury', 'Head', 'Both', 'Obsidian Greathelm'), +(1, 1, 1, 0, 224, 44664, 'Phase 1', 'Warrior', 'Fury', 'Neck', 'Both', 'Favor of the Dragon Queen'), +(1, 1, 2, 0, 224, 40530, 'Phase 1', 'Warrior', 'Fury', 'Shoulders', 'Both', 'Valorous Dreadnaught Shoulderplates'), +(1, 1, 4, 0, 224, 40539, 'Phase 1', 'Warrior', 'Fury', 'Chest', 'Both', 'Chestguard of the Recluse'), +(1, 1, 5, 0, 224, 40205, 'Phase 1', 'Warrior', 'Fury', 'Waist', 'Both', 'Stalk-Skin Belt'), +(1, 1, 6, 0, 224, 40529, 'Phase 1', 'Warrior', 'Fury', 'Legs', 'Both', 'Valorous Dreadnaught Legplates'), +(1, 1, 7, 0, 224, 40591, 'Phase 1', 'Warrior', 'Fury', 'Feet', 'Both', 'Melancholy Sabatons'), +(1, 1, 8, 0, 224, 39765, 'Phase 1', 'Warrior', 'Fury', 'Wrists', 'Both', 'Sinner''s Bindings'), +(1, 1, 9, 0, 224, 40541, 'Phase 1', 'Warrior', 'Fury', 'Hands', 'Both', 'Frosted Adroit Handguards'), +(1, 1, 10, 0, 224, 40717, 'Phase 1', 'Warrior', 'Fury', 'Finger1', 'Both', 'Ring of Invincibility'), +(1, 1, 12, 0, 224, 40256, 'Phase 1', 'Warrior', 'Fury', 'Trinket1', 'Both', 'Grim Toll'), +(1, 1, 14, 0, 224, 40403, 'Phase 1', 'Warrior', 'Fury', 'Back', 'Both', 'Drape of the Deadly Foe'), +(1, 1, 17, 0, 224, 40385, 'Phase 1', 'Warrior', 'Fury', 'Ranged', 'Both', 'Envoy of Mortality'); + +-- ilvl 245 (Phase 2) +INSERT INTO `playerbots_bis_gear` VALUES +(1, 1, 0, 0, 245, 46151, 'Phase 2', 'Warrior', 'Fury', 'Head', 'Both', 'Conqueror''s Siegebreaker Helmet'), +(1, 1, 1, 0, 245, 45517, 'Phase 2', 'Warrior', 'Fury', 'Neck', 'Both', 'Pendulum of Infinity'), +(1, 1, 2, 0, 245, 46037, 'Phase 2', 'Warrior', 'Fury', 'Shoulders', 'Both', 'Shoulderplates of the Celestial Watch'), +(1, 1, 4, 0, 245, 46146, 'Phase 2', 'Warrior', 'Fury', 'Chest', 'Both', 'Conqueror''s Siegebreaker Battleplate'), +(1, 1, 5, 0, 245, 46041, 'Phase 2', 'Warrior', 'Fury', 'Waist', 'Both', 'Starfall Girdle'), +(1, 1, 6, 0, 245, 45536, 'Phase 2', 'Warrior', 'Fury', 'Legs', 'Both', 'Legguards of Cunning Deception'), +(1, 1, 7, 0, 245, 45599, 'Phase 2', 'Warrior', 'Fury', 'Feet', 'Both', 'Sabatons of Lifeless Night'), +(1, 1, 9, 0, 245, 46148, 'Phase 2', 'Warrior', 'Fury', 'Hands', 'Both', 'Conqueror''s Siegebreaker Gauntlets'), +(1, 1, 10, 0, 245, 45608, 'Phase 2', 'Warrior', 'Fury', 'Finger1', 'Both', 'Brann''s Signet Ring'), +(1, 1, 12, 0, 245, 46038, 'Phase 2', 'Warrior', 'Fury', 'Trinket1', 'Both', 'Dark Matter'), +(1, 1, 14, 0, 245, 46032, 'Phase 2', 'Warrior', 'Fury', 'Back', 'Both', 'Drape of the Faceless General'), +(1, 1, 17, 0, 245, 45296, 'Phase 2', 'Warrior', 'Fury', 'Ranged', 'Both', 'Twirling Blades'); + +-- ilvl 258 (Phase 3) +INSERT INTO `playerbots_bis_gear` VALUES +(1, 1, 0, 1, 258, 48383, 'Phase 3', 'Warrior', 'Fury', 'Head', 'Alliance', 'Wrynn''s Helmet of Triumph'), +(1, 1, 0, 2, 258, 48398, 'Phase 3', 'Warrior', 'Fury', 'Head', 'Horde', 'Hellscream''s Helmet of Triumph'), +(1, 1, 1, 1, 258, 47060, 'Phase 3', 'Warrior', 'Fury', 'Neck', 'Alliance', 'Charge of the Demon Lord'), +(1, 1, 1, 2, 258, 47433, 'Phase 3', 'Warrior', 'Fury', 'Neck', 'Horde', 'Charge of the Eredar'), +(1, 1, 2, 1, 258, 48381, 'Phase 3', 'Warrior', 'Fury', 'Shoulders', 'Alliance', 'Wrynn''s Shoulderplates of Triumph'), +(1, 1, 2, 2, 258, 48400, 'Phase 3', 'Warrior', 'Fury', 'Shoulders', 'Horde', 'Hellscream''s Shoulderplates of Triumph'), +(1, 1, 4, 1, 258, 48385, 'Phase 3', 'Warrior', 'Fury', 'Chest', 'Alliance', 'Wrynn''s Battleplate of Triumph'), +(1, 1, 4, 2, 258, 48396, 'Phase 3', 'Warrior', 'Fury', 'Chest', 'Horde', 'Hellscream''s Battleplate of Triumph'), +(1, 1, 5, 1, 258, 47002, 'Phase 3', 'Warrior', 'Fury', 'Waist', 'Alliance', 'Bloodbath Belt'), +(1, 1, 5, 2, 258, 47429, 'Phase 3', 'Warrior', 'Fury', 'Waist', 'Horde', 'Bloodbath Girdle'), +(1, 1, 6, 1, 258, 48382, 'Phase 3', 'Warrior', 'Fury', 'Legs', 'Alliance', 'Wrynn''s Legplates of Triumph'), +(1, 1, 6, 2, 258, 48399, 'Phase 3', 'Warrior', 'Fury', 'Legs', 'Horde', 'Hellscream''s Legplates of Triumph'), +(1, 1, 7, 1, 258, 47154, 'Phase 3', 'Warrior', 'Fury', 'Feet', 'Alliance', 'Greaves of the 7th Legion'), +(1, 1, 7, 2, 258, 47473, 'Phase 3', 'Warrior', 'Fury', 'Feet', 'Horde', 'Greaves of the Saronite Citadel'), +(1, 1, 8, 1, 258, 47074, 'Phase 3', 'Warrior', 'Fury', 'Wrists', 'Alliance', 'Bracers of the Untold Massacre'), +(1, 1, 8, 2, 258, 47442, 'Phase 3', 'Warrior', 'Fury', 'Wrists', 'Horde', 'Bracers of the Silent Massacre'), +(1, 1, 9, 1, 258, 47240, 'Phase 3', 'Warrior', 'Fury', 'Hands', 'Alliance', 'Gloves of Bitter Reprisal'), +(1, 1, 9, 2, 258, 47492, 'Phase 3', 'Warrior', 'Fury', 'Hands', 'Horde', 'Gauntlets of Bitter Reprisal'), +(1, 1, 10, 1, 258, 47075, 'Phase 3', 'Warrior', 'Fury', 'Finger1', 'Alliance', 'Ring of Callous Aggression'), +(1, 1, 10, 2, 258, 47443, 'Phase 3', 'Warrior', 'Fury', 'Finger1', 'Horde', 'Band of Callous Aggression'), +(1, 1, 12, 1, 258, 47131, 'Phase 3', 'Warrior', 'Fury', 'Trinket1', 'Alliance', 'Death''s Verdict'), +(1, 1, 12, 2, 258, 47464, 'Phase 3', 'Warrior', 'Fury', 'Trinket1', 'Horde', 'Death''s Choice'), +(1, 1, 14, 1, 258, 47545, 'Phase 3', 'Warrior', 'Fury', 'Back', 'Alliance', 'Vereesa''s Dexterity'), +(1, 1, 14, 2, 258, 47546, 'Phase 3', 'Warrior', 'Fury', 'Back', 'Horde', 'Sylvanas'' Cunning'), +(1, 1, 17, 1, 258, 46995, 'Phase 3', 'Warrior', 'Fury', 'Ranged', 'Alliance', 'Talonstrike'), +(1, 1, 17, 2, 258, 47428, 'Phase 3', 'Warrior', 'Fury', 'Ranged', 'Horde', 'Death''s Head Crossbow'); + +-- ilvl 264 (Phase 4) +INSERT INTO `playerbots_bis_gear` VALUES +(1, 1, 0, 0, 264, 51227, 'Phase 4', 'Warrior', 'Fury', 'Head', 'Both', 'Sanctified Ymirjar Lord''s Helmet'), +(1, 1, 1, 0, 264, 50633, 'Phase 4', 'Warrior', 'Fury', 'Neck', 'Both', 'Sindragosa''s Cruel Claw'), +(1, 1, 2, 0, 264, 51229, 'Phase 4', 'Warrior', 'Fury', 'Shoulders', 'Both', 'Sanctified Ymirjar Lord''s Shoulderplates'), +(1, 1, 4, 0, 264, 51225, 'Phase 4', 'Warrior', 'Fury', 'Chest', 'Both', 'Sanctified Ymirjar Lord''s Battleplate'), +(1, 1, 5, 0, 264, 50620, 'Phase 4', 'Warrior', 'Fury', 'Waist', 'Both', 'Coldwraith Links'), +(1, 1, 6, 0, 264, 51228, 'Phase 4', 'Warrior', 'Fury', 'Legs', 'Both', 'Sanctified Ymirjar Lord''s Legplates'), +(1, 1, 7, 0, 264, 50639, 'Phase 4', 'Warrior', 'Fury', 'Feet', 'Both', 'Blood-Soaked Saronite Stompers'), +(1, 1, 8, 0, 264, 50670, 'Phase 4', 'Warrior', 'Fury', 'Wrists', 'Both', 'Toskk''s Maximized Wristguards'), +(1, 1, 9, 0, 264, 50675, 'Phase 4', 'Warrior', 'Fury', 'Hands', 'Both', 'Aldriana''s Gloves of Secrecy'), +(1, 1, 10, 1, 264, 47075, 'Phase 4', 'Warrior', 'Fury', 'Finger1', 'Alliance', 'Ring of Callous Aggression'), +(1, 1, 10, 2, 264, 47443, 'Phase 4', 'Warrior', 'Fury', 'Finger1', 'Horde', 'Band of Callous Aggression'), +(1, 1, 12, 0, 264, 50363, 'Phase 4', 'Warrior', 'Fury', 'Trinket1', 'Both', 'Deathbringer''s Will'), +(1, 1, 14, 0, 264, 50653, 'Phase 4', 'Warrior', 'Fury', 'Back', 'Both', 'Shadowvault Slayer''s Cloak'), +(1, 1, 17, 0, 264, 50733, 'Phase 4', 'Warrior', 'Fury', 'Ranged', 'Both', 'Fal''inrush, Defender of Quel''thalas'); + +-- ilvl 290 (Phase 5) +INSERT INTO `playerbots_bis_gear` VALUES +(1, 1, 0, 0, 290, 51227, 'Phase 5', 'Warrior', 'Fury', 'Head', 'Both', 'Sanctified Ymirjar Lord''s Helmet'), +(1, 1, 1, 0, 290, 54581, 'Phase 5', 'Warrior', 'Fury', 'Neck', 'Both', 'Penumbra Pendant'), +(1, 1, 2, 0, 290, 51229, 'Phase 5', 'Warrior', 'Fury', 'Shoulders', 'Both', 'Sanctified Ymirjar Lord''s Shoulderplates'), +(1, 1, 4, 0, 290, 51225, 'Phase 5', 'Warrior', 'Fury', 'Chest', 'Both', 'Sanctified Ymirjar Lord''s Battleplate'), +(1, 1, 5, 0, 290, 50620, 'Phase 5', 'Warrior', 'Fury', 'Waist', 'Both', 'Coldwraith Links'), +(1, 1, 6, 0, 290, 51228, 'Phase 5', 'Warrior', 'Fury', 'Legs', 'Both', 'Sanctified Ymirjar Lord''s Legplates'), +(1, 1, 7, 0, 290, 54578, 'Phase 5', 'Warrior', 'Fury', 'Feet', 'Both', 'Apocalypse''s Advance'), +(1, 1, 8, 0, 290, 50670, 'Phase 5', 'Warrior', 'Fury', 'Wrists', 'Both', 'Toskk''s Maximized Wristguards'), +(1, 1, 9, 0, 290, 50675, 'Phase 5', 'Warrior', 'Fury', 'Hands', 'Both', 'Aldriana''s Gloves of Secrecy'), +(1, 1, 10, 0, 290, 50657, 'Phase 5', 'Warrior', 'Fury', 'Finger1', 'Both', 'Skeleton Lord''s Circle'), +(1, 1, 11, 0, 290, 52572, 'Phase 5', 'Warrior', 'Fury', 'Finger2', 'Both', 'Ashen Band of Endless Might'), +(1, 1, 12, 0, 290, 50363, 'Phase 5', 'Warrior', 'Fury', 'Trinket1', 'Both', 'Deathbringer''s Will'), +(1, 1, 13, 0, 290, 54590, 'Phase 5', 'Warrior', 'Fury', 'Trinket2', 'Both', 'Sharpened Twilight Scale'), +(1, 1, 14, 0, 290, 47545, 'Phase 5', 'Warrior', 'Fury', 'Back', 'Both', 'Vereesa''s Dexterity'), +(1, 1, 15, 0, 290, 50603, 'Phase 5', 'Warrior', 'Fury', 'MainHand', 'Both', 'Cryptmaker'), +(1, 1, 16, 0, 290, 50603, 'Phase 5', 'Warrior', 'Fury', 'OffHand', 'Both', 'Cryptmaker'), +(1, 1, 17, 0, 290, 50733, 'Phase 5', 'Warrior', 'Fury', 'Ranged', 'Both', 'Fal''inrush, Defender of Quel''thalas'); + +-- Protection (tab 2) +-- ilvl 66 (Phase 1 (Pre-Raid)) +INSERT INTO `playerbots_bis_gear` VALUES +(1, 2, 0, 0, 66, 12952, 'Phase 1 (Pre-Raid)', 'Warrior', 'Protection', 'Head', 'Both', 'Gyth''s Skull'), +(1, 2, 1, 0, 66, 13091, 'Phase 1 (Pre-Raid)', 'Warrior', 'Protection', 'Neck', 'Both', 'Medallion of Grand Marshal Morris'), +(1, 2, 2, 0, 66, 14552, 'Phase 1 (Pre-Raid)', 'Warrior', 'Protection', 'Shoulders', 'Both', 'Stockade Pauldrons'), +(1, 2, 4, 0, 66, 14624, 'Phase 1 (Pre-Raid)', 'Warrior', 'Protection', 'Chest', 'Both', 'Deathbone Chestplate'), +(1, 2, 5, 0, 66, 14620, 'Phase 1 (Pre-Raid)', 'Warrior', 'Protection', 'Waist', 'Both', 'Deathbone Girdle'), +(1, 2, 6, 0, 66, 11927, 'Phase 1 (Pre-Raid)', 'Warrior', 'Protection', 'Legs', 'Both', 'Legplates of the Eternal Guardian'), +(1, 2, 7, 0, 66, 14621, 'Phase 1 (Pre-Raid)', 'Warrior', 'Protection', 'Feet', 'Both', 'Deathbone Sabatons'), +(1, 2, 8, 0, 66, 12550, 'Phase 1 (Pre-Raid)', 'Warrior', 'Protection', 'Wrists', 'Both', 'Runed Golem Shackles'), +(1, 2, 9, 0, 66, 13072, 'Phase 1 (Pre-Raid)', 'Warrior', 'Protection', 'Hands', 'Both', 'Stonegrip Gauntlets'), +(1, 2, 10, 0, 66, 11669, 'Phase 1 (Pre-Raid)', 'Warrior', 'Protection', 'Finger1', 'Both', 'Naglering'), +(1, 2, 11, 0, 66, 10795, 'Phase 1 (Pre-Raid)', 'Warrior', 'Protection', 'Finger2', 'Both', 'Drakeclaw Band'), +(1, 2, 13, 0, 66, 10779, 'Phase 1 (Pre-Raid)', 'Warrior', 'Protection', 'Trinket2', 'Both', 'Demon''s Blood'), +(1, 2, 14, 0, 66, 13397, 'Phase 1 (Pre-Raid)', 'Warrior', 'Protection', 'Back', 'Both', 'Stoneskin Gargoyle Cape'), +(1, 2, 15, 0, 66, 15806, 'Phase 1 (Pre-Raid)', 'Warrior', 'Protection', 'MainHand', 'Both', 'Mirah''s Song'), +(1, 2, 16, 0, 66, 12602, 'Phase 1 (Pre-Raid)', 'Warrior', 'Protection', 'OffHand', 'Both', 'Draconian Deflector'), +(1, 2, 17, 0, 66, 12651, 'Phase 1 (Pre-Raid)', 'Warrior', 'Protection', 'Ranged', 'Both', 'Blackcrow'); + +-- ilvl 76 (Phase 2 (Pre-Raid)) +INSERT INTO `playerbots_bis_gear` VALUES +(1, 2, 0, 0, 76, 12952, 'Phase 2 (Pre-Raid)', 'Warrior', 'Protection', 'Head', 'Both', 'Gyth''s Skull'), +(1, 2, 1, 0, 76, 13091, 'Phase 2 (Pre-Raid)', 'Warrior', 'Protection', 'Neck', 'Both', 'Medallion of Grand Marshal Morris'), +(1, 2, 2, 0, 76, 14552, 'Phase 2 (Pre-Raid)', 'Warrior', 'Protection', 'Shoulders', 'Both', 'Stockade Pauldrons'), +(1, 2, 4, 0, 76, 14624, 'Phase 2 (Pre-Raid)', 'Warrior', 'Protection', 'Chest', 'Both', 'Deathbone Chestplate'), +(1, 2, 5, 0, 76, 14620, 'Phase 2 (Pre-Raid)', 'Warrior', 'Protection', 'Waist', 'Both', 'Deathbone Girdle'), +(1, 2, 6, 0, 76, 11927, 'Phase 2 (Pre-Raid)', 'Warrior', 'Protection', 'Legs', 'Both', 'Legplates of the Eternal Guardian'), +(1, 2, 7, 0, 76, 14621, 'Phase 2 (Pre-Raid)', 'Warrior', 'Protection', 'Feet', 'Both', 'Deathbone Sabatons'), +(1, 2, 8, 0, 76, 18754, 'Phase 2 (Pre-Raid)', 'Warrior', 'Protection', 'Wrists', 'Both', 'Fel Hardened Bracers'), +(1, 2, 9, 0, 76, 13072, 'Phase 2 (Pre-Raid)', 'Warrior', 'Protection', 'Hands', 'Both', 'Stonegrip Gauntlets'), +(1, 2, 10, 0, 76, 11669, 'Phase 2 (Pre-Raid)', 'Warrior', 'Protection', 'Finger1', 'Both', 'Naglering'), +(1, 2, 11, 0, 76, 10795, 'Phase 2 (Pre-Raid)', 'Warrior', 'Protection', 'Finger2', 'Both', 'Drakeclaw Band'), +(1, 2, 13, 0, 76, 10779, 'Phase 2 (Pre-Raid)', 'Warrior', 'Protection', 'Trinket2', 'Both', 'Demon''s Blood'), +(1, 2, 14, 0, 76, 18495, 'Phase 2 (Pre-Raid)', 'Warrior', 'Protection', 'Back', 'Both', 'Redoubt Cloak'), +(1, 2, 15, 0, 76, 15806, 'Phase 2 (Pre-Raid)', 'Warrior', 'Protection', 'MainHand', 'Both', 'Mirah''s Song'), +(1, 2, 16, 0, 76, 12602, 'Phase 2 (Pre-Raid)', 'Warrior', 'Protection', 'OffHand', 'Both', 'Draconian Deflector'), +(1, 2, 17, 0, 76, 18323, 'Phase 2 (Pre-Raid)', 'Warrior', 'Protection', 'Ranged', 'Both', 'Satyr''s Bow'); + +-- ilvl 78 (Phase 2) +INSERT INTO `playerbots_bis_gear` VALUES +(1, 2, 0, 0, 78, 16963, 'Phase 2', 'Warrior', 'Protection', 'Head', 'Both', 'Helm of Wrath'), +(1, 2, 1, 0, 78, 17065, 'Phase 2', 'Warrior', 'Protection', 'Neck', 'Both', 'Medallion of Steadfast Might'), +(1, 2, 2, 0, 78, 16868, 'Phase 2', 'Warrior', 'Protection', 'Shoulders', 'Both', 'Pauldrons of Might'), +(1, 2, 4, 0, 78, 14624, 'Phase 2', 'Warrior', 'Protection', 'Chest', 'Both', 'Deathbone Chestplate'), +(1, 2, 5, 0, 78, 16864, 'Phase 2', 'Warrior', 'Protection', 'Waist', 'Both', 'Belt of Might'), +(1, 2, 6, 0, 78, 16962, 'Phase 2', 'Warrior', 'Protection', 'Legs', 'Both', 'Legplates of Wrath'), +(1, 2, 7, 0, 78, 16862, 'Phase 2', 'Warrior', 'Protection', 'Feet', 'Both', 'Sabatons of Might'), +(1, 2, 8, 0, 78, 16861, 'Phase 2', 'Warrior', 'Protection', 'Wrists', 'Both', 'Bracers of Might'), +(1, 2, 9, 0, 78, 13072, 'Phase 2', 'Warrior', 'Protection', 'Hands', 'Both', 'Stonegrip Gauntlets'), +(1, 2, 10, 0, 78, 11669, 'Phase 2', 'Warrior', 'Protection', 'Finger1', 'Both', 'Naglering'), +(1, 2, 11, 0, 78, 18879, 'Phase 2', 'Warrior', 'Protection', 'Finger2', 'Both', 'Heavy Dark Iron Ring'), +(1, 2, 13, 0, 78, 18406, 'Phase 2', 'Warrior', 'Protection', 'Trinket2', 'Both', 'Onyxia Blood Talisman'), +(1, 2, 14, 0, 78, 18495, 'Phase 2', 'Warrior', 'Protection', 'Back', 'Both', 'Redoubt Cloak'), +(1, 2, 15, 0, 78, 18203, 'Phase 2', 'Warrior', 'Protection', 'MainHand', 'Both', 'Eskhandar''s Right Claw'), +(1, 2, 16, 0, 78, 17066, 'Phase 2', 'Warrior', 'Protection', 'OffHand', 'Both', 'Drillborer Disk'), +(1, 2, 17, 0, 78, 18323, 'Phase 2', 'Warrior', 'Protection', 'Ranged', 'Both', 'Satyr''s Bow'); + +-- ilvl 83 (Phase 3) +INSERT INTO `playerbots_bis_gear` VALUES +(1, 2, 0, 0, 83, 16963, 'Phase 3', 'Warrior', 'Protection', 'Head', 'Both', 'Helm of Wrath'), +(1, 2, 1, 0, 83, 19383, 'Phase 3', 'Warrior', 'Protection', 'Neck', 'Both', 'Master Dragonslayer''s Medallion'), +(1, 2, 2, 0, 83, 16961, 'Phase 3', 'Warrior', 'Protection', 'Shoulders', 'Both', 'Pauldrons of Wrath'), +(1, 2, 4, 0, 83, 16966, 'Phase 3', 'Warrior', 'Protection', 'Chest', 'Both', 'Breastplate of Wrath'), +(1, 2, 5, 0, 83, 16960, 'Phase 3', 'Warrior', 'Protection', 'Waist', 'Both', 'Waistband of Wrath'), +(1, 2, 6, 0, 83, 16962, 'Phase 3', 'Warrior', 'Protection', 'Legs', 'Both', 'Legplates of Wrath'), +(1, 2, 7, 0, 83, 16965, 'Phase 3', 'Warrior', 'Protection', 'Feet', 'Both', 'Sabatons of Wrath'), +(1, 2, 8, 0, 83, 16959, 'Phase 3', 'Warrior', 'Protection', 'Wrists', 'Both', 'Bracelets of Wrath'), +(1, 2, 9, 0, 83, 16964, 'Phase 3', 'Warrior', 'Protection', 'Hands', 'Both', 'Gauntlets of Wrath'), +(1, 2, 10, 0, 83, 11669, 'Phase 3', 'Warrior', 'Protection', 'Finger1', 'Both', 'Naglering'), +(1, 2, 11, 0, 83, 18879, 'Phase 3', 'Warrior', 'Protection', 'Finger2', 'Both', 'Heavy Dark Iron Ring'), +(1, 2, 12, 0, 83, 19431, 'Phase 3', 'Warrior', 'Protection', 'Trinket1', 'Both', 'Styleen''s Impeding Scarab'), +(1, 2, 13, 0, 83, 18406, 'Phase 3', 'Warrior', 'Protection', 'Trinket2', 'Both', 'Onyxia Blood Talisman'), +(1, 2, 14, 0, 83, 18495, 'Phase 3', 'Warrior', 'Protection', 'Back', 'Both', 'Redoubt Cloak'), +(1, 2, 15, 0, 83, 19335, 'Phase 3', 'Warrior', 'Protection', 'MainHand', 'Both', 'Spineshatter'), +(1, 2, 17, 0, 83, 19368, 'Phase 3', 'Warrior', 'Protection', 'Ranged', 'Both', 'Dragonbreath Hand Cannon'); + +-- ilvl 88 (Phase 5) +INSERT INTO `playerbots_bis_gear` VALUES +(1, 2, 0, 0, 88, 16963, 'Phase 5', 'Warrior', 'Protection', 'Head', 'Both', 'Helm of Wrath'), +(1, 2, 1, 0, 88, 22732, 'Phase 5', 'Warrior', 'Protection', 'Neck', 'Both', 'Mark of C''Thun'), +(1, 2, 2, 0, 88, 21639, 'Phase 5', 'Warrior', 'Protection', 'Shoulders', 'Both', 'Pauldrons of the Unrelenting'), +(1, 2, 4, 0, 88, 16966, 'Phase 5', 'Warrior', 'Protection', 'Chest', 'Both', 'Breastplate of Wrath'), +(1, 2, 5, 0, 88, 21598, 'Phase 5', 'Warrior', 'Protection', 'Waist', 'Both', 'Royal Qiraji Belt'), +(1, 2, 6, 0, 88, 16962, 'Phase 5', 'Warrior', 'Protection', 'Legs', 'Both', 'Legplates of Wrath'), +(1, 2, 7, 0, 88, 16965, 'Phase 5', 'Warrior', 'Protection', 'Feet', 'Both', 'Sabatons of Wrath'), +(1, 2, 8, 0, 88, 16959, 'Phase 5', 'Warrior', 'Protection', 'Wrists', 'Both', 'Bracelets of Wrath'), +(1, 2, 9, 0, 88, 21674, 'Phase 5', 'Warrior', 'Protection', 'Hands', 'Both', 'Gauntlets of Steadfast Determination'), +(1, 2, 10, 0, 88, 21601, 'Phase 5', 'Warrior', 'Protection', 'Finger1', 'Both', 'Ring of Emperor Vek''lor'), +(1, 2, 11, 0, 88, 18879, 'Phase 5', 'Warrior', 'Protection', 'Finger2', 'Both', 'Heavy Dark Iron Ring'), +(1, 2, 12, 0, 88, 19431, 'Phase 5', 'Warrior', 'Protection', 'Trinket1', 'Both', 'Styleen''s Impeding Scarab'), +(1, 2, 13, 0, 88, 18406, 'Phase 5', 'Warrior', 'Protection', 'Trinket2', 'Both', 'Onyxia Blood Talisman'), +(1, 2, 14, 0, 88, 19888, 'Phase 5', 'Warrior', 'Protection', 'Back', 'Both', 'Overlord''s Embrace'), +(1, 2, 15, 0, 88, 19363, 'Phase 5', 'Warrior', 'Protection', 'MainHand', 'Both', 'Crul''shorukh, Edge of Chaos'), +(1, 2, 16, 0, 88, 21269, 'Phase 5', 'Warrior', 'Protection', 'OffHand', 'Both', 'Blessed Qiraji Bulwark'), +(1, 2, 17, 0, 88, 19368, 'Phase 5', 'Warrior', 'Protection', 'Ranged', 'Both', 'Dragonbreath Hand Cannon'); + +-- ilvl 92 (Phase 6) +INSERT INTO `playerbots_bis_gear` VALUES +(1, 2, 0, 0, 92, 22418, 'Phase 6', 'Warrior', 'Protection', 'Head', 'Both', 'Dreadnaught Helmet'), +(1, 2, 1, 0, 92, 22732, 'Phase 6', 'Warrior', 'Protection', 'Neck', 'Both', 'Mark of C''Thun'), +(1, 2, 2, 0, 92, 22419, 'Phase 6', 'Warrior', 'Protection', 'Shoulders', 'Both', 'Dreadnaught Pauldrons'), +(1, 2, 4, 0, 92, 22416, 'Phase 6', 'Warrior', 'Protection', 'Chest', 'Both', 'Dreadnaught Breastplate'), +(1, 2, 5, 0, 92, 22422, 'Phase 6', 'Warrior', 'Protection', 'Waist', 'Both', 'Dreadnaught Waistguard'), +(1, 2, 6, 0, 92, 22417, 'Phase 6', 'Warrior', 'Protection', 'Legs', 'Both', 'Dreadnaught Legplates'), +(1, 2, 7, 0, 92, 22420, 'Phase 6', 'Warrior', 'Protection', 'Feet', 'Both', 'Dreadnaught Sabatons'), +(1, 2, 8, 0, 92, 22423, 'Phase 6', 'Warrior', 'Protection', 'Wrists', 'Both', 'Dreadnaught Bracers'), +(1, 2, 9, 0, 92, 22421, 'Phase 6', 'Warrior', 'Protection', 'Hands', 'Both', 'Dreadnaught Gauntlets'), +(1, 2, 10, 0, 92, 21601, 'Phase 6', 'Warrior', 'Protection', 'Finger1', 'Both', 'Ring of Emperor Vek''lor'), +(1, 2, 11, 0, 92, 23059, 'Phase 6', 'Warrior', 'Protection', 'Finger2', 'Both', 'Ring of the Dreadnaught'), +(1, 2, 12, 0, 92, 19431, 'Phase 6', 'Warrior', 'Protection', 'Trinket1', 'Both', 'Styleen''s Impeding Scarab'), +(1, 2, 13, 0, 92, 19406, 'Phase 6', 'Warrior', 'Protection', 'Trinket2', 'Both', 'Drake Fang Talisman'), +(1, 2, 14, 0, 92, 22938, 'Phase 6', 'Warrior', 'Protection', 'Back', 'Both', 'Cryptfiend Silk Cloak'), +(1, 2, 15, 0, 92, 23577, 'Phase 6', 'Warrior', 'Protection', 'MainHand', 'Both', 'The Hungering Cold'), +(1, 2, 16, 0, 92, 23043, 'Phase 6', 'Warrior', 'Protection', 'OffHand', 'Both', 'The Face of Death'), +(1, 2, 17, 0, 92, 19368, 'Phase 6', 'Warrior', 'Protection', 'Ranged', 'Both', 'Dragonbreath Hand Cannon'); + +-- ilvl 120 (Pre-Raid) +INSERT INTO `playerbots_bis_gear` VALUES +(1, 2, 0, 0, 120, 32083, 'Pre-Raid', 'Warrior', 'Protection', 'Head', 'Both', 'Faceguard of Determination'), +(1, 2, 1, 0, 120, 29386, 'Pre-Raid', 'Warrior', 'Protection', 'Neck', 'Both', 'Necklace of the Juggernaut'), +(1, 2, 2, 0, 120, 27803, 'Pre-Raid', 'Warrior', 'Protection', 'Shoulders', 'Both', 'Shoulderguards of the Bold'), +(1, 2, 4, 0, 120, 28205, 'Pre-Raid', 'Warrior', 'Protection', 'Chest', 'Both', 'Breastplate of the Bold'), +(1, 2, 5, 0, 120, 31460, 'Pre-Raid', 'Warrior', 'Protection', 'Waist', 'Both', 'Sha''tari Vindicator''s Waistguard'), +(1, 2, 6, 0, 120, 29184, 'Pre-Raid', 'Warrior', 'Protection', 'Legs', 'Both', 'Timewarden''s Leggings'), +(1, 2, 7, 0, 120, 29239, 'Pre-Raid', 'Warrior', 'Protection', 'Feet', 'Both', 'Eaglecrest Warboots'), +(1, 2, 8, 0, 120, 29463, 'Pre-Raid', 'Warrior', 'Protection', 'Wrists', 'Both', 'Amber Bands of the Aggressor'), +(1, 2, 9, 0, 120, 27475, 'Pre-Raid', 'Warrior', 'Protection', 'Hands', 'Both', 'Gauntlets of the Bold'), +(1, 2, 10, 0, 120, 30834, 'Pre-Raid', 'Warrior', 'Protection', 'Finger1', 'Both', 'Shapeshifter''s Signet'), +(1, 2, 12, 0, 120, 29387, 'Pre-Raid', 'Warrior', 'Protection', 'Trinket1', 'Both', 'Gnomeregan Auto-Blocker 600'), +(1, 2, 13, 0, 120, 23836, 'Pre-Raid', 'Warrior', 'Protection', 'Trinket2', 'Both', 'Goblin Rocket Launcher'), +(1, 2, 14, 0, 120, 27804, 'Pre-Raid', 'Warrior', 'Protection', 'Back', 'Both', 'Devilshark Cape'), +(1, 2, 15, 0, 120, 28438, 'Pre-Raid', 'Warrior', 'Protection', 'MainHand', 'Both', 'Dragonmaw'), +(1, 2, 16, 0, 120, 29266, 'Pre-Raid', 'Warrior', 'Protection', 'OffHand', 'Both', 'Azure-Shield of Coldarra'), +(1, 2, 17, 0, 120, 32756, 'Pre-Raid', 'Warrior', 'Protection', 'Ranged', 'Both', 'Gyro-Balanced Khorium Destroyer'); + +-- ilvl 125 (Phase 1) +INSERT INTO `playerbots_bis_gear` VALUES +(1, 2, 0, 0, 125, 29011, 'Phase 1', 'Warrior', 'Protection', 'Head', 'Both', 'Warbringer Greathelm'), +(1, 2, 1, 0, 125, 28516, 'Phase 1', 'Warrior', 'Protection', 'Neck', 'Both', 'Barbed Choker of Discipline'), +(1, 2, 2, 0, 125, 29016, 'Phase 1', 'Warrior', 'Protection', 'Shoulders', 'Both', 'Warbringer Shoulderguards'), +(1, 2, 4, 0, 125, 29012, 'Phase 1', 'Warrior', 'Protection', 'Chest', 'Both', 'Warbringer Chestguard'), +(1, 2, 5, 0, 125, 28566, 'Phase 1', 'Warrior', 'Protection', 'Waist', 'Both', 'Crimson Girdle of the Indomitable'), +(1, 2, 6, 0, 125, 28621, 'Phase 1', 'Warrior', 'Protection', 'Legs', 'Both', 'Wrynn Dynasty Greaves'), +(1, 2, 7, 0, 125, 28747, 'Phase 1', 'Warrior', 'Protection', 'Feet', 'Both', 'Battlescar Boots'), +(1, 2, 8, 0, 125, 28502, 'Phase 1', 'Warrior', 'Protection', 'Wrists', 'Both', 'Vambraces of Courage'), +(1, 2, 9, 0, 125, 28518, 'Phase 1', 'Warrior', 'Protection', 'Hands', 'Both', 'Iron Gauntlets of the Maiden'), +(1, 2, 10, 0, 125, 29279, 'Phase 1', 'Warrior', 'Protection', 'Finger1', 'Both', 'Violet Signet of the Great Protector'), +(1, 2, 11, 0, 125, 30834, 'Phase 1', 'Warrior', 'Protection', 'Finger2', 'Both', 'Shapeshifter''s Signet'), +(1, 2, 12, 0, 125, 29387, 'Phase 1', 'Warrior', 'Protection', 'Trinket1', 'Both', 'Gnomeregan Auto-Blocker 600'), +(1, 2, 13, 0, 125, 23836, 'Phase 1', 'Warrior', 'Protection', 'Trinket2', 'Both', 'Goblin Rocket Launcher'), +(1, 2, 14, 0, 125, 28660, 'Phase 1', 'Warrior', 'Protection', 'Back', 'Both', 'Gilded Thorium Cloak'), +(1, 2, 15, 0, 125, 28438, 'Phase 1', 'Warrior', 'Protection', 'MainHand', 'Both', 'Dragonmaw'), +(1, 2, 16, 0, 125, 28825, 'Phase 1', 'Warrior', 'Protection', 'OffHand', 'Both', 'Aldori Legacy Defender'), +(1, 2, 17, 0, 125, 30724, 'Phase 1', 'Warrior', 'Protection', 'Ranged', 'Both', 'Barrel-Blade Longrifle'); + +-- ilvl 141 (Phase 2) +INSERT INTO `playerbots_bis_gear` VALUES +(1, 2, 0, 0, 141, 30115, 'Phase 2', 'Warrior', 'Protection', 'Head', 'Both', 'Destroyer Greathelm'), +(1, 2, 1, 0, 141, 33066, 'Phase 2', 'Warrior', 'Protection', 'Neck', 'Both', 'Veteran''s Pendant of Triumph'), +(1, 2, 2, 0, 141, 30117, 'Phase 2', 'Warrior', 'Protection', 'Shoulders', 'Both', 'Destroyer Shoulderguards'), +(1, 2, 4, 0, 141, 30113, 'Phase 2', 'Warrior', 'Protection', 'Chest', 'Both', 'Destroyer Chestguard'), +(1, 2, 5, 0, 141, 30106, 'Phase 2', 'Warrior', 'Protection', 'Waist', 'Both', 'Belt of One-Hundred Deaths'), +(1, 2, 6, 0, 141, 30116, 'Phase 2', 'Warrior', 'Protection', 'Legs', 'Both', 'Destroyer Legguards'), +(1, 2, 7, 0, 141, 32793, 'Phase 2', 'Warrior', 'Protection', 'Feet', 'Both', 'Veteran''s Plate Greaves'), +(1, 2, 8, 0, 141, 32818, 'Phase 2', 'Warrior', 'Protection', 'Wrists', 'Both', 'Veteran''s Plate Bracers'), +(1, 2, 9, 2, 141, 29998, 'Phase 2', 'Warrior', 'Protection', 'Hands', 'Horde', 'Royal Gauntlets of Silvermoon'), +(1, 2, 10, 0, 141, 30834, 'Phase 2', 'Warrior', 'Protection', 'Finger1', 'Both', 'Shapeshifter''s Signet'), +(1, 2, 11, 0, 141, 29283, 'Phase 2', 'Warrior', 'Protection', 'Finger2', 'Both', 'Violet Signet of the Master Assassin'), +(1, 2, 12, 0, 141, 23836, 'Phase 2', 'Warrior', 'Protection', 'Trinket1', 'Both', 'Goblin Rocket Launcher'), +(1, 2, 13, 0, 141, 28121, 'Phase 2', 'Warrior', 'Protection', 'Trinket2', 'Both', 'Icon of Unyielding Courage'), +(1, 2, 14, 0, 141, 29994, 'Phase 2', 'Warrior', 'Protection', 'Back', 'Both', 'Thalassian Wildercloak'), +(1, 2, 15, 0, 141, 28439, 'Phase 2', 'Warrior', 'Protection', 'MainHand', 'Both', 'Dragonstrike'), +(1, 2, 16, 0, 141, 28825, 'Phase 2', 'Warrior', 'Protection', 'OffHand', 'Both', 'Aldori Legacy Defender'), +(1, 2, 17, 0, 141, 32756, 'Phase 2', 'Warrior', 'Protection', 'Ranged', 'Both', 'Gyro-Balanced Khorium Destroyer'); + +-- ilvl 156 (Phase 3) +INSERT INTO `playerbots_bis_gear` VALUES +(1, 2, 0, 0, 156, 32521, 'Phase 3', 'Warrior', 'Protection', 'Head', 'Both', 'Faceplate of the Impenetrable'), +(1, 2, 1, 0, 156, 33066, 'Phase 3', 'Warrior', 'Protection', 'Neck', 'Both', 'Veteran''s Pendant of Triumph'), +(1, 2, 2, 0, 156, 33732, 'Phase 3', 'Warrior', 'Protection', 'Shoulders', 'Both', 'Vengeful Gladiator''s Plate Shoulders'), +(1, 2, 4, 0, 156, 33728, 'Phase 3', 'Warrior', 'Protection', 'Chest', 'Both', 'Vengeful Gladiator''s Plate Chestpiece'), +(1, 2, 5, 0, 156, 30106, 'Phase 3', 'Warrior', 'Protection', 'Waist', 'Both', 'Belt of One-Hundred Deaths'), +(1, 2, 6, 0, 156, 30978, 'Phase 3', 'Warrior', 'Protection', 'Legs', 'Both', 'Onslaught Legguards'), +(1, 2, 7, 0, 156, 33812, 'Phase 3', 'Warrior', 'Protection', 'Feet', 'Both', 'Vindicator''s Plate Greaves'), +(1, 2, 8, 0, 156, 33813, 'Phase 3', 'Warrior', 'Protection', 'Wrists', 'Both', 'Vindicator''s Plate Bracers'), +(1, 2, 10, 0, 156, 30834, 'Phase 3', 'Warrior', 'Protection', 'Finger1', 'Both', 'Shapeshifter''s Signet'), +(1, 2, 11, 0, 156, 33919, 'Phase 3', 'Warrior', 'Protection', 'Finger2', 'Both', 'Vindicator''s Band of Triumph'), +(1, 2, 12, 0, 156, 31858, 'Phase 3', 'Warrior', 'Protection', 'Trinket1', 'Both', 'Darkmoon Card: Vengeance'), +(1, 2, 13, 0, 156, 32501, 'Phase 3', 'Warrior', 'Protection', 'Trinket2', 'Both', 'Shadowmoon Insignia'), +(1, 2, 14, 0, 156, 34010, 'Phase 3', 'Warrior', 'Protection', 'Back', 'Both', 'Pepe''s Shroud of Pacification'), +(1, 2, 15, 0, 156, 32254, 'Phase 3', 'Warrior', 'Protection', 'MainHand', 'Both', 'The Brutalizer'), +(1, 2, 16, 0, 156, 32375, 'Phase 3', 'Warrior', 'Protection', 'OffHand', 'Both', 'Bulwark of Azzinoth'), +(1, 2, 17, 0, 156, 32253, 'Phase 3', 'Warrior', 'Protection', 'Ranged', 'Both', 'Legionkiller'); + +-- ilvl 164 (Phase 5) +INSERT INTO `playerbots_bis_gear` VALUES +(1, 2, 0, 0, 164, 35068, 'Phase 5', 'Warrior', 'Protection', 'Head', 'Both', 'Brutal Gladiator''s Plate Helm'), +(1, 2, 1, 0, 164, 34178, 'Phase 5', 'Warrior', 'Protection', 'Neck', 'Both', 'Collar of the Pit Lord'), +(1, 2, 2, 0, 164, 34388, 'Phase 5', 'Warrior', 'Protection', 'Shoulders', 'Both', 'Pauldrons of Berserking'), +(1, 2, 4, 0, 164, 35066, 'Phase 5', 'Warrior', 'Protection', 'Chest', 'Both', 'Brutal Gladiator''s Plate Chestpiece'), +(1, 2, 5, 0, 164, 34547, 'Phase 5', 'Warrior', 'Protection', 'Waist', 'Both', 'Onslaught Waistguard'), +(1, 2, 6, 0, 164, 34381, 'Phase 5', 'Warrior', 'Protection', 'Legs', 'Both', 'Felstrength Legplates'), +(1, 2, 7, 0, 164, 34568, 'Phase 5', 'Warrior', 'Protection', 'Feet', 'Both', 'Onslaught Boots'), +(1, 2, 8, 0, 164, 34442, 'Phase 5', 'Warrior', 'Protection', 'Wrists', 'Both', 'Onslaught Wristguards'), +(1, 2, 9, 0, 164, 34378, 'Phase 5', 'Warrior', 'Protection', 'Hands', 'Both', 'Hard Khorium Battlefists'), +(1, 2, 10, 0, 164, 35131, 'Phase 5', 'Warrior', 'Protection', 'Finger1', 'Both', 'Guardian''s Band of Triumph'), +(1, 2, 11, 0, 164, 34213, 'Phase 5', 'Warrior', 'Protection', 'Finger2', 'Both', 'Ring of Hardened Resolve'), +(1, 2, 12, 0, 164, 34473, 'Phase 5', 'Warrior', 'Protection', 'Trinket1', 'Both', 'Commendation of Kael''thas'), +(1, 2, 13, 0, 164, 31858, 'Phase 5', 'Warrior', 'Protection', 'Trinket2', 'Both', 'Darkmoon Card: Vengeance'), +(1, 2, 14, 0, 164, 34190, 'Phase 5', 'Warrior', 'Protection', 'Back', 'Both', 'Crimson Paragon''s Cover'), +(1, 2, 15, 0, 164, 34164, 'Phase 5', 'Warrior', 'Protection', 'MainHand', 'Both', 'Dragonscale-Encrusted Longblade'), +(1, 2, 16, 0, 164, 34185, 'Phase 5', 'Warrior', 'Protection', 'OffHand', 'Both', 'Sword Breaker''s Bulwark'), +(1, 2, 17, 0, 164, 32253, 'Phase 5', 'Warrior', 'Protection', 'Ranged', 'Both', 'Legionkiller'); + +-- ilvl 200 (Pre-Raid) +INSERT INTO `playerbots_bis_gear` VALUES +(1, 2, 0, 0, 200, 41387, 'Pre-Raid', 'Warrior', 'Protection', 'Head', 'Both', 'Tempered Titansteel Helm'), +(1, 2, 1, 0, 200, 40679, 'Pre-Raid', 'Warrior', 'Protection', 'Neck', 'Both', 'Chained Military Gorget'), +(1, 2, 2, 0, 200, 34389, 'Pre-Raid', 'Warrior', 'Protection', 'Shoulders', 'Both', 'Spaulders of the Thalassian Defender'), +(1, 2, 4, 0, 200, 39611, 'Pre-Raid', 'Warrior', 'Protection', 'Chest', 'Both', 'Heroes'' Dreadnaught Breastplate'), +(1, 2, 5, 0, 200, 37379, 'Pre-Raid', 'Warrior', 'Protection', 'Waist', 'Both', 'Skadi''s Iron Belt'), +(1, 2, 6, 0, 200, 43500, 'Pre-Raid', 'Warrior', 'Protection', 'Legs', 'Both', 'Bolstered Legplates'), +(1, 2, 7, 0, 200, 44201, 'Pre-Raid', 'Warrior', 'Protection', 'Feet', 'Both', 'Sabatons of Draconic Vigor'), +(1, 2, 8, 0, 200, 37620, 'Pre-Raid', 'Warrior', 'Protection', 'Wrists', 'Both', 'Bracers of the Herald'), +(1, 2, 9, 0, 200, 39622, 'Pre-Raid', 'Warrior', 'Protection', 'Hands', 'Both', 'Heroes'' Dreadnaught Handguards'), +(1, 2, 10, 0, 200, 34213, 'Pre-Raid', 'Warrior', 'Protection', 'Finger1', 'Both', 'Ring of Hardened Resolve'), +(1, 2, 12, 0, 200, 37220, 'Pre-Raid', 'Warrior', 'Protection', 'Trinket1', 'Both', 'Essence of Gossamer'), +(1, 2, 14, 0, 200, 43565, 'Pre-Raid', 'Warrior', 'Protection', 'Back', 'Both', 'Durable Nerubhide Cape'), +(1, 2, 15, 0, 200, 37401, 'Pre-Raid', 'Warrior', 'Protection', 'MainHand', 'Both', 'Red Sword of Courage'); + +-- ilvl 224 (Phase 1) +INSERT INTO `playerbots_bis_gear` VALUES +(1, 2, 0, 0, 224, 40546, 'Phase 1', 'Warrior', 'Protection', 'Head', 'Both', 'Valorous Dreadnaught Greathelm'), +(1, 2, 1, 0, 224, 44665, 'Phase 1', 'Warrior', 'Protection', 'Neck', 'Both', 'Nexus War Champion Beads'), +(1, 2, 2, 0, 224, 40548, 'Phase 1', 'Warrior', 'Protection', 'Shoulders', 'Both', 'Valorous Dreadnaught Pauldrons'), +(1, 2, 4, 0, 224, 40544, 'Phase 1', 'Warrior', 'Protection', 'Chest', 'Both', 'Valorous Dreadnaught Breastplate'), +(1, 2, 5, 0, 224, 39759, 'Phase 1', 'Warrior', 'Protection', 'Waist', 'Both', 'Ablative Chitin Girdle'), +(1, 2, 6, 0, 224, 40589, 'Phase 1', 'Warrior', 'Protection', 'Legs', 'Both', 'Legplates of Sovereignty'), +(1, 2, 7, 0, 224, 39717, 'Phase 1', 'Warrior', 'Protection', 'Feet', 'Both', 'Inexorable Sabatons'), +(1, 2, 8, 0, 224, 39764, 'Phase 1', 'Warrior', 'Protection', 'Wrists', 'Both', 'Bindings of the Hapless Prey'), +(1, 2, 9, 0, 224, 40545, 'Phase 1', 'Warrior', 'Protection', 'Hands', 'Both', 'Valorous Dreadnaught Handguards'), +(1, 2, 10, 0, 224, 40718, 'Phase 1', 'Warrior', 'Protection', 'Finger1', 'Both', 'Signet of the Impregnable Fortress'), +(1, 2, 12, 0, 224, 40257, 'Phase 1', 'Warrior', 'Protection', 'Trinket1', 'Both', 'Defender''s Code'), +(1, 2, 14, 0, 224, 40722, 'Phase 1', 'Warrior', 'Protection', 'Back', 'Both', 'Platinum Mesh Cloak'), +(1, 2, 15, 0, 224, 40402, 'Phase 1', 'Warrior', 'Protection', 'MainHand', 'Both', 'Last Laugh'); + +-- ilvl 245 (Phase 2) +INSERT INTO `playerbots_bis_gear` VALUES +(1, 2, 0, 0, 245, 46166, 'Phase 2', 'Warrior', 'Protection', 'Head', 'Both', 'Conqueror''s Siegebreaker Greathelm'), +(1, 2, 1, 0, 245, 45485, 'Phase 2', 'Warrior', 'Protection', 'Neck', 'Both', 'Bronze Pendant of the Vanir'), +(1, 2, 2, 0, 245, 46167, 'Phase 2', 'Warrior', 'Protection', 'Shoulders', 'Both', 'Conqueror''s Siegebreaker Pauldrons'), +(1, 2, 4, 0, 245, 46162, 'Phase 2', 'Warrior', 'Protection', 'Chest', 'Both', 'Conqueror''s Siegebreaker Breastplate'), +(1, 2, 5, 0, 245, 45139, 'Phase 2', 'Warrior', 'Protection', 'Waist', 'Both', 'Dragonslayer''s Brace'), +(1, 2, 6, 0, 245, 46169, 'Phase 2', 'Warrior', 'Protection', 'Legs', 'Both', 'Conqueror''s Siegebreaker Legguards'), +(1, 2, 7, 0, 245, 45542, 'Phase 2', 'Warrior', 'Protection', 'Feet', 'Both', 'Greaves of the Stonewarder'), +(1, 2, 8, 0, 245, 45111, 'Phase 2', 'Warrior', 'Protection', 'Wrists', 'Both', 'Mimiron''s Inferno Couplings'), +(1, 2, 9, 0, 245, 45487, 'Phase 2', 'Warrior', 'Protection', 'Hands', 'Both', 'Handguards of Revitalization'), +(1, 2, 10, 0, 245, 45471, 'Phase 2', 'Warrior', 'Protection', 'Finger1', 'Both', 'Fate''s Clutch'), +(1, 2, 12, 0, 245, 45158, 'Phase 2', 'Warrior', 'Protection', 'Trinket1', 'Both', 'Heart of Iron'), +(1, 2, 14, 0, 245, 45496, 'Phase 2', 'Warrior', 'Protection', 'Back', 'Both', 'Titanskin Cloak'), +(1, 2, 17, 0, 245, 45137, 'Phase 2', 'Warrior', 'Protection', 'Ranged', 'Both', 'Veranus'' Bane'); + +-- ilvl 258 (Phase 3) +INSERT INTO `playerbots_bis_gear` VALUES +(1, 2, 0, 0, 258, 48433, 'Phase 3', 'Warrior', 'Protection', 'Head', 'Both', 'Greathelm of Triumph'), +(1, 2, 2, 0, 258, 48455, 'Phase 3', 'Warrior', 'Protection', 'Shoulders', 'Both', 'Pauldrons of Triumph'), +(1, 2, 4, 1, 258, 46968, 'Phase 3', 'Warrior', 'Protection', 'Chest', 'Alliance', 'Chestplate of the Towering Monstrosity'), +(1, 2, 4, 2, 258, 47415, 'Phase 3', 'Warrior', 'Protection', 'Chest', 'Horde', 'Hauberk of the Towering Monstrosity'), +(1, 2, 5, 1, 258, 47076, 'Phase 3', 'Warrior', 'Protection', 'Waist', 'Alliance', 'Girdle of Bloodied Scars'), +(1, 2, 5, 2, 258, 47444, 'Phase 3', 'Warrior', 'Protection', 'Waist', 'Horde', 'Belt of Bloodied Scars'), +(1, 2, 6, 0, 258, 48447, 'Phase 3', 'Warrior', 'Protection', 'Legs', 'Both', 'Legguards of Triumph'), +(1, 2, 7, 1, 258, 47952, 'Phase 3', 'Warrior', 'Protection', 'Feet', 'Alliance', 'Sabatons of the Lingering Vortex & Dawnbreaker Greaves'), +(1, 2, 8, 1, 258, 47918, 'Phase 3', 'Warrior', 'Protection', 'Wrists', 'Alliance', 'Dreadscale Armguards & Bracers of the Shieldmaiden'), +(1, 2, 9, 0, 258, 48453, 'Phase 3', 'Warrior', 'Protection', 'Hands', 'Both', 'Handguards of Triumph'), +(1, 2, 10, 1, 258, 45471, 'Phase 3', 'Warrior', 'Protection', 'Finger1', 'Alliance', 'Fate''s Clutch'), +(1, 2, 12, 1, 258, 47216, 'Phase 3', 'Warrior', 'Protection', 'Trinket1', 'Alliance', 'The Black Heart'), +(1, 2, 14, 1, 258, 47549, 'Phase 3', 'Warrior', 'Protection', 'Back', 'Alliance', 'Magni''s Resolution'), +(1, 2, 14, 2, 258, 47550, 'Phase 3', 'Warrior', 'Protection', 'Back', 'Horde', 'Cairne''s Endurance'), +(1, 2, 17, 0, 258, 47660, 'Phase 3', 'Warrior', 'Protection', 'Ranged', 'Both', 'Blades of the Sable Cross'); + +-- ilvl 264 (Phase 4) +INSERT INTO `playerbots_bis_gear` VALUES +(1, 2, 0, 0, 264, 50640, 'Phase 4', 'Warrior', 'Protection', 'Head', 'Both', 'Broken Ram Skull Helm'), +(1, 2, 2, 0, 264, 51224, 'Phase 4', 'Warrior', 'Protection', 'Shoulders', 'Both', 'Sanctified Ymirjar Lord''s Pauldrons'), +(1, 2, 4, 0, 264, 51220, 'Phase 4', 'Warrior', 'Protection', 'Chest', 'Both', 'Sanctified Ymirjar Lord''s Breastplate'), +(1, 2, 5, 0, 264, 50691, 'Phase 4', 'Warrior', 'Protection', 'Waist', 'Both', 'Belt of Broken Bones'), +(1, 2, 6, 0, 264, 51223, 'Phase 4', 'Warrior', 'Protection', 'Legs', 'Both', 'Sanctified Ymirjar Lord''s Legguards'), +(1, 2, 7, 0, 264, 50625, 'Phase 4', 'Warrior', 'Protection', 'Feet', 'Both', 'Grinning Skull Greatboots'), +(1, 2, 8, 0, 264, 50611, 'Phase 4', 'Warrior', 'Protection', 'Wrists', 'Both', 'Bracers of Dark Reckoning'), +(1, 2, 9, 0, 264, 51222, 'Phase 4', 'Warrior', 'Protection', 'Hands', 'Both', 'Sanctified Ymirjar Lord''s Handguards'), +(1, 2, 10, 0, 264, 50622, 'Phase 4', 'Warrior', 'Protection', 'Finger1', 'Both', 'Devium''s Eternally Cold Ring'), +(1, 2, 12, 0, 264, 50364, 'Phase 4', 'Warrior', 'Protection', 'Trinket1', 'Both', 'Sindragosa''s Flawless Fang'), +(1, 2, 14, 0, 264, 50718, 'Phase 4', 'Warrior', 'Protection', 'Back', 'Both', 'Royal Crimson Cloak'), +(1, 2, 17, 0, 264, 51834, 'Phase 4', 'Warrior', 'Protection', 'Ranged', 'Both', 'Dreamhunter''s Carbine'); + +-- ilvl 290 (Phase 5) +INSERT INTO `playerbots_bis_gear` VALUES +(1, 2, 0, 0, 290, 50640, 'Phase 5', 'Warrior', 'Protection', 'Head', 'Both', 'Broken Ram Skull Helm'), +(1, 2, 1, 0, 290, 50682, 'Phase 5', 'Warrior', 'Protection', 'Neck', 'Both', 'Bile-Encrusted Medallion'), +(1, 2, 2, 0, 290, 51847, 'Phase 5', 'Warrior', 'Protection', 'Shoulders', 'Both', 'Spaulders of the Blood Princes'), +(1, 2, 4, 0, 290, 51220, 'Phase 5', 'Warrior', 'Protection', 'Chest', 'Both', 'Sanctified Ymirjar Lord''s Breastplate'), +(1, 2, 5, 0, 290, 50691, 'Phase 5', 'Warrior', 'Protection', 'Waist', 'Both', 'Belt of Broken Bones'), +(1, 2, 6, 0, 290, 50612, 'Phase 5', 'Warrior', 'Protection', 'Legs', 'Both', 'Legguards of Lost Hope'), +(1, 2, 7, 0, 290, 54579, 'Phase 5', 'Warrior', 'Protection', 'Feet', 'Both', 'Treads of Impending Resurrection'), +(1, 2, 8, 0, 290, 51901, 'Phase 5', 'Warrior', 'Protection', 'Wrists', 'Both', 'Gargoyle Spit Bracers'), +(1, 2, 9, 0, 290, 51222, 'Phase 5', 'Warrior', 'Protection', 'Hands', 'Both', 'Sanctified Ymirjar Lord''s Handguards'), +(1, 2, 10, 0, 290, 50622, 'Phase 5', 'Warrior', 'Protection', 'Finger1', 'Both', 'Devium''s Eternally Cold Ring'), +(1, 2, 11, 0, 290, 50404, 'Phase 5', 'Warrior', 'Protection', 'Finger2', 'Both', 'Ashen Band of Endless Courage'), +(1, 2, 12, 0, 290, 54591, 'Phase 5', 'Warrior', 'Protection', 'Trinket1', 'Both', 'Petrified Twilight Scale'), +(1, 2, 13, 0, 290, 50364, 'Phase 5', 'Warrior', 'Protection', 'Trinket2', 'Both', 'Sindragosa''s Flawless Fang'), +(1, 2, 14, 0, 290, 50466, 'Phase 5', 'Warrior', 'Protection', 'Back', 'Both', 'Sentinel''s Winter Cloak'), +(1, 2, 15, 0, 290, 50738, 'Phase 5', 'Warrior', 'Protection', 'MainHand', 'Both', 'Mithrios, Bronzebeard''s Legacy'), +(1, 2, 16, 0, 290, 50729, 'Phase 5', 'Warrior', 'Protection', 'OffHand', 'Both', 'Icecrown Glacial Wall'), +(1, 2, 17, 0, 290, 51834, 'Phase 5', 'Warrior', 'Protection', 'Ranged', 'Both', 'Dreamhunter''s Carbine'); + + +-- ============================================================ +-- Paladin (2) +-- ============================================================ +-- Holy (tab 0) +-- ilvl 66 (Phase 1 (Pre-Raid)) +INSERT INTO `playerbots_bis_gear` VALUES +(2, 0, 0, 0, 66, 12633, 'Phase 1 (Pre-Raid)', 'Paladin', 'Holy', 'Head', 'Both', 'Whitesoul Helm'), +(2, 0, 1, 0, 66, 18723, 'Phase 1 (Pre-Raid)', 'Paladin', 'Holy', 'Neck', 'Both', 'Animated Chain Necklace'), +(2, 0, 2, 0, 66, 18720, 'Phase 1 (Pre-Raid)', 'Paladin', 'Holy', 'Shoulders', 'Both', 'Shroud of the Nathrezim'), +(2, 0, 4, 0, 66, 13346, 'Phase 1 (Pre-Raid)', 'Paladin', 'Holy', 'Chest', 'Both', 'Robes of the Exalted'), +(2, 0, 5, 0, 66, 18702, 'Phase 1 (Pre-Raid)', 'Paladin', 'Holy', 'Waist', 'Both', 'Belt of the Ordained'), +(2, 0, 6, 0, 66, 11841, 'Phase 1 (Pre-Raid)', 'Paladin', 'Holy', 'Legs', 'Both', 'Senior Designer''s Pantaloons'), +(2, 0, 7, 0, 66, 13954, 'Phase 1 (Pre-Raid)', 'Paladin', 'Holy', 'Feet', 'Both', 'Verdant Footpads'), +(2, 0, 8, 0, 66, 13969, 'Phase 1 (Pre-Raid)', 'Paladin', 'Holy', 'Wrists', 'Both', 'Loomguard Armbraces'), +(2, 0, 9, 0, 66, 10787, 'Phase 1 (Pre-Raid)', 'Paladin', 'Holy', 'Hands', 'Both', 'Atal''ai Gloves'), +(2, 0, 10, 0, 66, 16058, 'Phase 1 (Pre-Raid)', 'Paladin', 'Holy', 'Finger1', 'Both', 'Fordring''s Seal'), +(2, 0, 11, 0, 66, 18103, 'Phase 1 (Pre-Raid)', 'Paladin', 'Holy', 'Finger2', 'Both', 'Band of Rumination'), +(2, 0, 12, 0, 66, 11819, 'Phase 1 (Pre-Raid)', 'Paladin', 'Holy', 'Trinket1', 'Both', 'Second Wind'), +(2, 0, 13, 0, 66, 12930, 'Phase 1 (Pre-Raid)', 'Paladin', 'Holy', 'Trinket2', 'Both', 'Briarwood Reed'), +(2, 0, 14, 0, 66, 13386, 'Phase 1 (Pre-Raid)', 'Paladin', 'Holy', 'Back', 'Both', 'Archivist Cape'), +(2, 0, 15, 0, 66, 11923, 'Phase 1 (Pre-Raid)', 'Paladin', 'Holy', 'MainHand', 'Both', 'The Hammer of Grace'), +(2, 0, 16, 0, 66, 11928, 'Phase 1 (Pre-Raid)', 'Paladin', 'Holy', 'OffHand', 'Both', 'Thaurissan''s Royal Scepter'); + +-- ilvl 76 (Phase 2 (Pre-Raid)) +INSERT INTO `playerbots_bis_gear` VALUES +(2, 0, 0, 0, 76, 18490, 'Phase 2 (Pre-Raid)', 'Paladin', 'Holy', 'Head', 'Both', 'Insightful Hood'), +(2, 0, 1, 0, 76, 18723, 'Phase 2 (Pre-Raid)', 'Paladin', 'Holy', 'Neck', 'Both', 'Animated Chain Necklace'), +(2, 0, 2, 0, 76, 18720, 'Phase 2 (Pre-Raid)', 'Paladin', 'Holy', 'Shoulders', 'Both', 'Shroud of the Nathrezim'), +(2, 0, 4, 0, 76, 13346, 'Phase 2 (Pre-Raid)', 'Paladin', 'Holy', 'Chest', 'Both', 'Robes of the Exalted'), +(2, 0, 5, 0, 76, 18702, 'Phase 2 (Pre-Raid)', 'Paladin', 'Holy', 'Waist', 'Both', 'Belt of the Ordained'), +(2, 0, 6, 0, 76, 18386, 'Phase 2 (Pre-Raid)', 'Paladin', 'Holy', 'Legs', 'Both', 'Padre''s Trousers'), +(2, 0, 7, 0, 76, 18507, 'Phase 2 (Pre-Raid)', 'Paladin', 'Holy', 'Feet', 'Both', 'Boots of the Full Moon'), +(2, 0, 8, 0, 76, 13969, 'Phase 2 (Pre-Raid)', 'Paladin', 'Holy', 'Wrists', 'Both', 'Loomguard Armbraces'), +(2, 0, 9, 0, 76, 18527, 'Phase 2 (Pre-Raid)', 'Paladin', 'Holy', 'Hands', 'Both', 'Harmonious Gauntlets'), +(2, 0, 10, 0, 76, 16058, 'Phase 2 (Pre-Raid)', 'Paladin', 'Holy', 'Finger1', 'Both', 'Fordring''s Seal'), +(2, 0, 11, 0, 76, 18103, 'Phase 2 (Pre-Raid)', 'Paladin', 'Holy', 'Finger2', 'Both', 'Band of Rumination'), +(2, 0, 12, 0, 76, 11819, 'Phase 2 (Pre-Raid)', 'Paladin', 'Holy', 'Trinket1', 'Both', 'Second Wind'), +(2, 0, 13, 0, 76, 12930, 'Phase 2 (Pre-Raid)', 'Paladin', 'Holy', 'Trinket2', 'Both', 'Briarwood Reed'), +(2, 0, 14, 0, 76, 18510, 'Phase 2 (Pre-Raid)', 'Paladin', 'Holy', 'Back', 'Both', 'Hide of the Wild'), +(2, 0, 15, 0, 76, 11923, 'Phase 2 (Pre-Raid)', 'Paladin', 'Holy', 'MainHand', 'Both', 'The Hammer of Grace'), +(2, 0, 16, 0, 76, 18523, 'Phase 2 (Pre-Raid)', 'Paladin', 'Holy', 'OffHand', 'Both', 'Brightly Glowing Stone'); + +-- ilvl 78 (Phase 2) +INSERT INTO `playerbots_bis_gear` VALUES +(2, 0, 0, 0, 78, 18490, 'Phase 2', 'Paladin', 'Holy', 'Head', 'Both', 'Insightful Hood'), +(2, 0, 1, 0, 78, 18723, 'Phase 2', 'Paladin', 'Holy', 'Neck', 'Both', 'Animated Chain Necklace'), +(2, 0, 2, 0, 78, 18810, 'Phase 2', 'Paladin', 'Holy', 'Shoulders', 'Both', 'Wild Growth Spaulders'), +(2, 0, 4, 0, 78, 19145, 'Phase 2', 'Paladin', 'Holy', 'Chest', 'Both', 'Robe of Volatile Power'), +(2, 0, 5, 0, 78, 19162, 'Phase 2', 'Paladin', 'Holy', 'Waist', 'Both', 'Corehound Belt'), +(2, 0, 6, 0, 78, 18875, 'Phase 2', 'Paladin', 'Holy', 'Legs', 'Both', 'Salamander Scale Pants'), +(2, 0, 7, 0, 78, 18507, 'Phase 2', 'Paladin', 'Holy', 'Feet', 'Both', 'Boots of the Full Moon'), +(2, 0, 8, 0, 78, 13969, 'Phase 2', 'Paladin', 'Holy', 'Wrists', 'Both', 'Loomguard Armbraces'), +(2, 0, 9, 0, 78, 18527, 'Phase 2', 'Paladin', 'Holy', 'Hands', 'Both', 'Harmonious Gauntlets'), +(2, 0, 10, 0, 78, 19140, 'Phase 2', 'Paladin', 'Holy', 'Finger1', 'Both', 'Cauterizing Band'), +(2, 0, 11, 0, 78, 19140, 'Phase 2', 'Paladin', 'Holy', 'Finger2', 'Both', 'Cauterizing Band'), +(2, 0, 12, 0, 78, 17064, 'Phase 2', 'Paladin', 'Holy', 'Trinket1', 'Both', 'Shard of the Scale'), +(2, 0, 13, 0, 78, 12930, 'Phase 2', 'Paladin', 'Holy', 'Trinket2', 'Both', 'Briarwood Reed'), +(2, 0, 14, 0, 78, 18510, 'Phase 2', 'Paladin', 'Holy', 'Back', 'Both', 'Hide of the Wild'), +(2, 0, 15, 0, 78, 17103, 'Phase 2', 'Paladin', 'Holy', 'MainHand', 'Both', 'Azuresong Mageblade'), +(2, 0, 16, 0, 78, 18523, 'Phase 2', 'Paladin', 'Holy', 'OffHand', 'Both', 'Brightly Glowing Stone'); + +-- ilvl 83 (Phase 3) +INSERT INTO `playerbots_bis_gear` VALUES +(2, 0, 0, 0, 83, 19375, 'Phase 3', 'Paladin', 'Holy', 'Head', 'Both', 'Mish''undare, Circlet of the Mind Flayer'), +(2, 0, 1, 0, 83, 18723, 'Phase 3', 'Paladin', 'Holy', 'Neck', 'Both', 'Animated Chain Necklace'), +(2, 0, 2, 0, 83, 18810, 'Phase 3', 'Paladin', 'Holy', 'Shoulders', 'Both', 'Wild Growth Spaulders'), +(2, 0, 4, 0, 83, 19145, 'Phase 3', 'Paladin', 'Holy', 'Chest', 'Both', 'Robe of Volatile Power'), +(2, 0, 5, 0, 83, 19162, 'Phase 3', 'Paladin', 'Holy', 'Waist', 'Both', 'Corehound Belt'), +(2, 0, 6, 0, 83, 19385, 'Phase 3', 'Paladin', 'Holy', 'Legs', 'Both', 'Empowered Leggings'), +(2, 0, 7, 0, 83, 19437, 'Phase 3', 'Paladin', 'Holy', 'Feet', 'Both', 'Boots of Pure Thought'), +(2, 0, 8, 0, 83, 13969, 'Phase 3', 'Paladin', 'Holy', 'Wrists', 'Both', 'Loomguard Armbraces'), +(2, 0, 9, 0, 83, 19390, 'Phase 3', 'Paladin', 'Holy', 'Hands', 'Both', 'Taut Dragonhide Gloves'), +(2, 0, 10, 0, 83, 19382, 'Phase 3', 'Paladin', 'Holy', 'Finger1', 'Both', 'Pure Elementium Band'), +(2, 0, 11, 0, 83, 19140, 'Phase 3', 'Paladin', 'Holy', 'Finger2', 'Both', 'Cauterizing Band'), +(2, 0, 12, 0, 83, 17064, 'Phase 3', 'Paladin', 'Holy', 'Trinket1', 'Both', 'Shard of the Scale'), +(2, 0, 13, 0, 83, 19395, 'Phase 3', 'Paladin', 'Holy', 'Trinket2', 'Both', 'Rejuvenating Gem'), +(2, 0, 14, 0, 83, 19430, 'Phase 3', 'Paladin', 'Holy', 'Back', 'Both', 'Shroud of Pure Thought'), +(2, 0, 15, 0, 83, 19360, 'Phase 3', 'Paladin', 'Holy', 'MainHand', 'Both', 'Lok''amir il Romathis'), +(2, 0, 16, 0, 83, 19312, 'Phase 3', 'Paladin', 'Holy', 'OffHand', 'Both', 'Lei of the Lifegiver'); + +-- ilvl 88 (Phase 5) +INSERT INTO `playerbots_bis_gear` VALUES +(2, 0, 0, 0, 88, 20628, 'Phase 5', 'Paladin', 'Holy', 'Head', 'Both', 'Deviate Growth Cap'), +(2, 0, 1, 0, 88, 21712, 'Phase 5', 'Paladin', 'Holy', 'Neck', 'Both', 'Amulet of the Fallen God'), +(2, 0, 2, 0, 88, 18810, 'Phase 5', 'Paladin', 'Holy', 'Shoulders', 'Both', 'Wild Growth Spaulders'), +(2, 0, 4, 0, 88, 21663, 'Phase 5', 'Paladin', 'Holy', 'Chest', 'Both', 'Robes of the Guardian Saint'), +(2, 0, 5, 0, 88, 21582, 'Phase 5', 'Paladin', 'Holy', 'Waist', 'Both', 'Grasp of the Old God'), +(2, 0, 6, 0, 88, 21667, 'Phase 5', 'Paladin', 'Holy', 'Legs', 'Both', 'Legplates of Blazing Light'), +(2, 0, 7, 0, 88, 19437, 'Phase 5', 'Paladin', 'Holy', 'Feet', 'Both', 'Boots of Pure Thought'), +(2, 0, 8, 0, 88, 21604, 'Phase 5', 'Paladin', 'Holy', 'Wrists', 'Both', 'Bracelets of Royal Redemption'), +(2, 0, 9, 0, 88, 20264, 'Phase 5', 'Paladin', 'Holy', 'Hands', 'Both', 'Peacekeeper Gauntlets'), +(2, 0, 10, 0, 88, 19382, 'Phase 5', 'Paladin', 'Holy', 'Finger1', 'Both', 'Pure Elementium Band'), +(2, 0, 11, 0, 88, 21620, 'Phase 5', 'Paladin', 'Holy', 'Finger2', 'Both', 'Ring of the Martyr'), +(2, 0, 12, 0, 88, 17064, 'Phase 5', 'Paladin', 'Holy', 'Trinket1', 'Both', 'Shard of the Scale'), +(2, 0, 13, 0, 88, 19395, 'Phase 5', 'Paladin', 'Holy', 'Trinket2', 'Both', 'Rejuvenating Gem'), +(2, 0, 14, 0, 88, 21583, 'Phase 5', 'Paladin', 'Holy', 'Back', 'Both', 'Cloak of Clarity'), +(2, 0, 15, 0, 88, 21839, 'Phase 5', 'Paladin', 'Holy', 'MainHand', 'Both', 'Scepter of the False Prophet'), +(2, 0, 16, 0, 88, 21666, 'Phase 5', 'Paladin', 'Holy', 'OffHand', 'Both', 'Sartura''s Might'), +(2, 0, 17, 0, 88, 22402, 'Phase 5', 'Paladin', 'Holy', 'Ranged', 'Both', 'Libram of Grace'); + +-- ilvl 92 (Phase 6) +INSERT INTO `playerbots_bis_gear` VALUES +(2, 0, 0, 0, 92, 20628, 'Phase 6', 'Paladin', 'Holy', 'Head', 'Both', 'Deviate Growth Cap'), +(2, 0, 1, 0, 92, 23057, 'Phase 6', 'Paladin', 'Holy', 'Neck', 'Both', 'Gem of Trapped Innocents'), +(2, 0, 2, 0, 92, 22429, 'Phase 6', 'Paladin', 'Holy', 'Shoulders', 'Both', 'Redemption Spaulders'), +(2, 0, 4, 0, 92, 22425, 'Phase 6', 'Paladin', 'Holy', 'Chest', 'Both', 'Redemption Tunic'), +(2, 0, 5, 0, 92, 21582, 'Phase 6', 'Paladin', 'Holy', 'Waist', 'Both', 'Grasp of the Old God'), +(2, 0, 6, 0, 92, 22427, 'Phase 6', 'Paladin', 'Holy', 'Legs', 'Both', 'Redemption Legguards'), +(2, 0, 7, 0, 92, 22430, 'Phase 6', 'Paladin', 'Holy', 'Feet', 'Both', 'Redemption Boots'), +(2, 0, 8, 0, 92, 21604, 'Phase 6', 'Paladin', 'Holy', 'Wrists', 'Both', 'Bracelets of Royal Redemption'), +(2, 0, 9, 0, 92, 20264, 'Phase 6', 'Paladin', 'Holy', 'Hands', 'Both', 'Peacekeeper Gauntlets'), +(2, 0, 10, 0, 92, 19382, 'Phase 6', 'Paladin', 'Holy', 'Finger1', 'Both', 'Pure Elementium Band'), +(2, 0, 11, 0, 92, 23066, 'Phase 6', 'Paladin', 'Holy', 'Finger2', 'Both', 'Ring of Redemption'), +(2, 0, 12, 0, 92, 23047, 'Phase 6', 'Paladin', 'Holy', 'Trinket1', 'Both', 'Eye of the Dead'), +(2, 0, 13, 0, 92, 19395, 'Phase 6', 'Paladin', 'Holy', 'Trinket2', 'Both', 'Rejuvenating Gem'), +(2, 0, 14, 0, 92, 23050, 'Phase 6', 'Paladin', 'Holy', 'Back', 'Both', 'Cloak of the Necropolis'), +(2, 0, 15, 0, 92, 23056, 'Phase 6', 'Paladin', 'Holy', 'MainHand', 'Both', 'Hammer of the Twisting Nether'), +(2, 0, 16, 0, 92, 23075, 'Phase 6', 'Paladin', 'Holy', 'OffHand', 'Both', 'Death''s Bargain'), +(2, 0, 17, 0, 92, 23006, 'Phase 6', 'Paladin', 'Holy', 'Ranged', 'Both', 'Libram of Light'); + +-- ilvl 120 (Pre-Raid) +INSERT INTO `playerbots_bis_gear` VALUES +(2, 0, 0, 0, 120, 32084, 'Pre-Raid', 'Paladin', 'Holy', 'Head', 'Both', 'Helmet of the Steadfast Champion'), +(2, 0, 1, 0, 120, 29374, 'Pre-Raid', 'Paladin', 'Holy', 'Neck', 'Both', 'Necklace of Eternal Hope'), +(2, 0, 2, 0, 120, 27775, 'Pre-Raid', 'Paladin', 'Holy', 'Shoulders', 'Both', 'Hallowed Pauldrons'), +(2, 0, 4, 0, 120, 28230, 'Pre-Raid', 'Paladin', 'Holy', 'Chest', 'Both', 'Hallowed Garments'), +(2, 0, 5, 0, 120, 24256, 'Pre-Raid', 'Paladin', 'Holy', 'Waist', 'Both', 'Girdle of Ruination'), +(2, 0, 6, 0, 120, 30541, 'Pre-Raid', 'Paladin', 'Holy', 'Legs', 'Both', 'Stormsong Kilt'), +(2, 0, 7, 0, 120, 27411, 'Pre-Raid', 'Paladin', 'Holy', 'Feet', 'Both', 'Slippers of Serenity'), +(2, 0, 8, 0, 120, 23539, 'Pre-Raid', 'Paladin', 'Holy', 'Wrists', 'Both', 'Blessed Bracers'), +(2, 0, 9, 0, 120, 27457, 'Pre-Raid', 'Paladin', 'Holy', 'Hands', 'Both', 'Life Bearer''s Gauntlets'), +(2, 0, 10, 0, 120, 29373, 'Pre-Raid', 'Paladin', 'Holy', 'Finger1', 'Both', 'Band of Halos'), +(2, 0, 10, 2, 120, 29168, 'Pre-Raid', 'Paladin', 'Holy', 'Finger1', 'Horde', 'Ancestral Band'), +(2, 0, 12, 0, 120, 29376, 'Pre-Raid', 'Paladin', 'Holy', 'Trinket1', 'Both', 'Essence of the Martyr'), +(2, 0, 13, 0, 120, 30841, 'Pre-Raid', 'Paladin', 'Holy', 'Trinket2', 'Both', 'Lower City Prayer Book'), +(2, 0, 14, 0, 120, 29354, 'Pre-Raid', 'Paladin', 'Holy', 'Back', 'Both', 'Light-Touched Stole of Altruism'), +(2, 0, 15, 0, 120, 23556, 'Pre-Raid', 'Paladin', 'Holy', 'MainHand', 'Both', 'Hand of Eternity'), +(2, 0, 16, 0, 120, 29267, 'Pre-Raid', 'Paladin', 'Holy', 'OffHand', 'Both', 'Light-Bearer''s Faith Shield'), +(2, 0, 17, 0, 120, 25644, 'Pre-Raid', 'Paladin', 'Holy', 'Ranged', 'Both', 'Blessed Book of Nagrand'); + +-- ilvl 125 (Phase 1) +INSERT INTO `playerbots_bis_gear` VALUES +(2, 0, 0, 0, 125, 29061, 'Phase 1', 'Paladin', 'Holy', 'Head', 'Both', 'Justicar Diadem'), +(2, 0, 1, 0, 125, 30726, 'Phase 1', 'Paladin', 'Holy', 'Neck', 'Both', 'Archaic Charm of Presence'), +(2, 0, 2, 0, 125, 29064, 'Phase 1', 'Paladin', 'Holy', 'Shoulders', 'Both', 'Justicar Pauldrons'), +(2, 0, 4, 0, 125, 29062, 'Phase 1', 'Paladin', 'Holy', 'Chest', 'Both', 'Justicar Chestpiece'), +(2, 0, 5, 0, 125, 28799, 'Phase 1', 'Paladin', 'Holy', 'Waist', 'Both', 'Belt of Divine Inspiration'), +(2, 0, 6, 0, 125, 30727, 'Phase 1', 'Paladin', 'Holy', 'Legs', 'Both', 'Gilded Trousers of Benediction'), +(2, 0, 7, 0, 125, 30737, 'Phase 1', 'Paladin', 'Holy', 'Feet', 'Both', 'Gold-Leaf Wildboots'), +(2, 0, 8, 0, 125, 28512, 'Phase 1', 'Paladin', 'Holy', 'Wrists', 'Both', 'Bracers of Justice'), +(2, 0, 9, 0, 125, 28505, 'Phase 1', 'Paladin', 'Holy', 'Hands', 'Both', 'Gauntlets of Renewed Hope'), +(2, 0, 10, 0, 125, 28790, 'Phase 1', 'Paladin', 'Holy', 'Finger1', 'Both', 'Naaru Lightwarden''s Band'), +(2, 0, 11, 0, 125, 30736, 'Phase 1', 'Paladin', 'Holy', 'Finger2', 'Both', 'Ring of Flowing Light'), +(2, 0, 12, 0, 125, 28590, 'Phase 1', 'Paladin', 'Holy', 'Trinket1', 'Both', 'Ribbon of Sacrifice'), +(2, 0, 13, 0, 125, 29376, 'Phase 1', 'Paladin', 'Holy', 'Trinket2', 'Both', 'Essence of the Martyr'), +(2, 0, 14, 0, 125, 28765, 'Phase 1', 'Paladin', 'Holy', 'Back', 'Both', 'Stainless Cloak of the Pure Hearted'), +(2, 0, 15, 0, 125, 28771, 'Phase 1', 'Paladin', 'Holy', 'MainHand', 'Both', 'Light''s Justice'), +(2, 0, 16, 0, 125, 29458, 'Phase 1', 'Paladin', 'Holy', 'OffHand', 'Both', 'Aegis of the Vindicator'), +(2, 0, 17, 0, 125, 28592, 'Phase 1', 'Paladin', 'Holy', 'Ranged', 'Both', 'Libram of Souls Redeemed'); + +-- ilvl 141 (Phase 2) +INSERT INTO `playerbots_bis_gear` VALUES +(2, 0, 0, 0, 141, 30136, 'Phase 2', 'Paladin', 'Holy', 'Head', 'Both', 'Crystalforge Greathelm'), +(2, 0, 1, 0, 141, 30018, 'Phase 2', 'Paladin', 'Holy', 'Neck', 'Both', 'Lord Sanguinar''s Claim'), +(2, 0, 2, 0, 141, 30138, 'Phase 2', 'Paladin', 'Holy', 'Shoulders', 'Both', 'Crystalforge Pauldrons'), +(2, 0, 4, 0, 141, 30134, 'Phase 2', 'Paladin', 'Holy', 'Chest', 'Both', 'Crystalforge Chestpiece'), +(2, 0, 5, 0, 141, 29965, 'Phase 2', 'Paladin', 'Holy', 'Waist', 'Both', 'Girdle of the Righteous Path'), +(2, 0, 6, 0, 141, 29991, 'Phase 2', 'Paladin', 'Holy', 'Legs', 'Both', 'Sunhawk Leggings'), +(2, 0, 7, 0, 141, 30027, 'Phase 2', 'Paladin', 'Holy', 'Feet', 'Both', 'Boots of Courage Unending'), +(2, 0, 8, 0, 141, 30047, 'Phase 2', 'Paladin', 'Holy', 'Wrists', 'Both', 'Blackfathom Warbands'), +(2, 0, 9, 0, 141, 30112, 'Phase 2', 'Paladin', 'Holy', 'Hands', 'Both', 'Glorious Gauntlets of Crestfall'), +(2, 0, 10, 0, 141, 28790, 'Phase 2', 'Paladin', 'Holy', 'Finger1', 'Both', 'Naaru Lightwarden''s Band'), +(2, 0, 11, 0, 141, 29920, 'Phase 2', 'Paladin', 'Holy', 'Finger2', 'Both', 'Phoenix-Ring of Rebirth'), +(2, 0, 12, 0, 141, 29376, 'Phase 2', 'Paladin', 'Holy', 'Trinket1', 'Both', 'Essence of the Martyr'), +(2, 0, 13, 0, 141, 28590, 'Phase 2', 'Paladin', 'Holy', 'Trinket2', 'Both', 'Ribbon of Sacrifice'), +(2, 0, 14, 0, 141, 29989, 'Phase 2', 'Paladin', 'Holy', 'Back', 'Both', 'Sunshower Light Cloak'), +(2, 0, 15, 0, 141, 30108, 'Phase 2', 'Paladin', 'Holy', 'MainHand', 'Both', 'Lightfathom Scepter'), +(2, 0, 16, 0, 141, 29458, 'Phase 2', 'Paladin', 'Holy', 'OffHand', 'Both', 'Aegis of the Vindicator'), +(2, 0, 17, 0, 141, 28592, 'Phase 2', 'Paladin', 'Holy', 'Ranged', 'Both', 'Libram of Souls Redeemed'); + +-- ilvl 156 (Phase 3) +INSERT INTO `playerbots_bis_gear` VALUES +(2, 0, 0, 0, 156, 30988, 'Phase 3', 'Paladin', 'Holy', 'Head', 'Both', 'Lightbringer Greathelm'), +(2, 0, 1, 0, 156, 32370, 'Phase 3', 'Paladin', 'Holy', 'Neck', 'Both', 'Nadina''s Pendant of Purity'), +(2, 0, 2, 0, 156, 30996, 'Phase 3', 'Paladin', 'Holy', 'Shoulders', 'Both', 'Lightbringer Pauldrons'), +(2, 0, 4, 0, 156, 30992, 'Phase 3', 'Paladin', 'Holy', 'Chest', 'Both', 'Lightbringer Chestpiece'), +(2, 0, 5, 0, 156, 30897, 'Phase 3', 'Paladin', 'Holy', 'Waist', 'Both', 'Girdle of Hope'), +(2, 0, 6, 0, 156, 30994, 'Phase 3', 'Paladin', 'Holy', 'Legs', 'Both', 'Lightbringer Leggings'), +(2, 0, 7, 0, 156, 32243, 'Phase 3', 'Paladin', 'Holy', 'Feet', 'Both', 'Pearl Inlaid Boots'), +(2, 0, 8, 0, 156, 30862, 'Phase 3', 'Paladin', 'Holy', 'Wrists', 'Both', 'Blessed Adamantite Bracers'), +(2, 0, 9, 0, 156, 30112, 'Phase 3', 'Paladin', 'Holy', 'Hands', 'Both', 'Glorious Gauntlets of Crestfall'), +(2, 0, 10, 0, 156, 32528, 'Phase 3', 'Paladin', 'Holy', 'Finger1', 'Both', 'Blessed Band of Karabor'), +(2, 0, 11, 0, 156, 32238, 'Phase 3', 'Paladin', 'Holy', 'Finger2', 'Both', 'Ring of Calming Waves'), +(2, 0, 12, 0, 156, 29376, 'Phase 3', 'Paladin', 'Holy', 'Trinket1', 'Both', 'Essence of the Martyr'), +(2, 0, 13, 0, 156, 32496, 'Phase 3', 'Paladin', 'Holy', 'Trinket2', 'Both', 'Memento of Tyrande'), +(2, 0, 14, 0, 156, 32524, 'Phase 3', 'Paladin', 'Holy', 'Back', 'Both', 'Shroud of the Highborne'), +(2, 0, 15, 0, 156, 32500, 'Phase 3', 'Paladin', 'Holy', 'MainHand', 'Both', 'Crystal Spire of Karabor'), +(2, 0, 16, 0, 156, 32255, 'Phase 3', 'Paladin', 'Holy', 'OffHand', 'Both', 'Felstone Bulwark'), +(2, 0, 17, 0, 156, 28592, 'Phase 3', 'Paladin', 'Holy', 'Ranged', 'Both', 'Libram of Souls Redeemed'); + +-- ilvl 164 (Phase 4) +INSERT INTO `playerbots_bis_gear` VALUES +(2, 0, 0, 0, 164, 30988, 'Phase 4', 'Paladin', 'Holy', 'Head', 'Both', 'Lightbringer Greathelm'), +(2, 0, 1, 0, 164, 33281, 'Phase 4', 'Paladin', 'Holy', 'Neck', 'Both', 'Brooch of Nature''s Mercy'), +(2, 0, 2, 0, 164, 30996, 'Phase 4', 'Paladin', 'Holy', 'Shoulders', 'Both', 'Lightbringer Pauldrons'), +(2, 0, 4, 0, 164, 30992, 'Phase 4', 'Paladin', 'Holy', 'Chest', 'Both', 'Lightbringer Chestpiece'), +(2, 0, 5, 0, 164, 30897, 'Phase 4', 'Paladin', 'Holy', 'Waist', 'Both', 'Girdle of Hope'), +(2, 0, 6, 0, 164, 30994, 'Phase 4', 'Paladin', 'Holy', 'Legs', 'Both', 'Lightbringer Leggings'), +(2, 0, 7, 0, 164, 33324, 'Phase 4', 'Paladin', 'Holy', 'Feet', 'Both', 'Treads of the Life Path'), +(2, 0, 8, 0, 164, 30862, 'Phase 4', 'Paladin', 'Holy', 'Wrists', 'Both', 'Blessed Adamantite Bracers'), +(2, 0, 9, 0, 164, 30112, 'Phase 4', 'Paladin', 'Holy', 'Hands', 'Both', 'Glorious Gauntlets of Crestfall'), +(2, 0, 10, 0, 164, 32528, 'Phase 4', 'Paladin', 'Holy', 'Finger1', 'Both', 'Blessed Band of Karabor'), +(2, 0, 11, 0, 164, 32238, 'Phase 4', 'Paladin', 'Holy', 'Finger2', 'Both', 'Ring of Calming Waves'), +(2, 0, 12, 0, 164, 29376, 'Phase 4', 'Paladin', 'Holy', 'Trinket1', 'Both', 'Essence of the Martyr'), +(2, 0, 13, 0, 164, 32496, 'Phase 4', 'Paladin', 'Holy', 'Trinket2', 'Both', 'Memento of Tyrande'), +(2, 0, 14, 0, 164, 32524, 'Phase 4', 'Paladin', 'Holy', 'Back', 'Both', 'Shroud of the Highborne'), +(2, 0, 15, 0, 164, 32500, 'Phase 4', 'Paladin', 'Holy', 'MainHand', 'Both', 'Crystal Spire of Karabor'), +(2, 0, 16, 0, 164, 32255, 'Phase 4', 'Paladin', 'Holy', 'OffHand', 'Both', 'Felstone Bulwark'), +(2, 0, 17, 0, 164, 28592, 'Phase 4', 'Paladin', 'Holy', 'Ranged', 'Both', 'Libram of Souls Redeemed'); + +-- ilvl 200 (Pre-Raid) +INSERT INTO `playerbots_bis_gear` VALUES +(2, 0, 0, 0, 200, 44949, 'Pre-Raid', 'Paladin', 'Holy', 'Head', 'Both', 'Unbreakable Healing Amplifiers'), +(2, 0, 1, 0, 200, 42647, 'Pre-Raid', 'Paladin', 'Holy', 'Neck', 'Both', 'Titanium Spellshock Necklace'), +(2, 0, 2, 0, 200, 37673, 'Pre-Raid', 'Paladin', 'Holy', 'Shoulders', 'Both', 'Dark Runic Mantle'), +(2, 0, 4, 0, 200, 39629, 'Pre-Raid', 'Paladin', 'Holy', 'Chest', 'Both', 'Heroes'' Redemption Tunic'), +(2, 0, 5, 0, 200, 40691, 'Pre-Raid', 'Paladin', 'Holy', 'Waist', 'Both', 'Magroth''s Meditative Cincture'), +(2, 0, 6, 0, 200, 37362, 'Pre-Raid', 'Paladin', 'Holy', 'Legs', 'Both', 'Leggings of Protective Auras'), +(2, 0, 7, 0, 200, 44202, 'Pre-Raid', 'Paladin', 'Holy', 'Feet', 'Both', 'Sandals of Crimson Fury'), +(2, 0, 8, 0, 200, 40741, 'Pre-Raid', 'Paladin', 'Holy', 'Wrists', 'Both', 'Cuffs of the Shadow Ascendant'), +(2, 0, 9, 0, 200, 39632, 'Pre-Raid', 'Paladin', 'Holy', 'Hands', 'Both', 'Heroes'' Redemption Gloves'), +(2, 0, 12, 0, 200, 44255, 'Pre-Raid', 'Paladin', 'Holy', 'Trinket1', 'Both', 'Darkmoon Card: Greatness'), +(2, 0, 14, 0, 200, 34242, 'Pre-Raid', 'Paladin', 'Holy', 'Back', 'Both', 'Tattered Cape of Antonidas'), +(2, 0, 15, 0, 200, 37169, 'Pre-Raid', 'Paladin', 'Holy', 'MainHand', 'Both', 'War Mace of Unrequited Love'), +(2, 0, 17, 0, 200, 40705, 'Pre-Raid', 'Paladin', 'Holy', 'Ranged', 'Both', 'Libram of Renewal'); + +-- ilvl 224 (Phase 1) +INSERT INTO `playerbots_bis_gear` VALUES +(2, 0, 0, 0, 224, 44007, 'Phase 1', 'Paladin', 'Holy', 'Head', 'Both', 'Headpiece of Reconciliation'), +(2, 0, 1, 0, 224, 44661, 'Phase 1', 'Paladin', 'Holy', 'Neck', 'Both', 'Wyrmrest Necklace of Power'), +(2, 0, 2, 0, 224, 40573, 'Phase 1', 'Paladin', 'Holy', 'Shoulders', 'Both', 'Valorous Redemption Spaulders'), +(2, 0, 4, 0, 224, 40569, 'Phase 1', 'Paladin', 'Holy', 'Chest', 'Both', 'Valorous Redemption Tunic'), +(2, 0, 5, 0, 224, 40561, 'Phase 1', 'Paladin', 'Holy', 'Waist', 'Both', 'Leash of Heedless Magic'), +(2, 0, 6, 0, 224, 40572, 'Phase 1', 'Paladin', 'Holy', 'Legs', 'Both', 'Valorous Redemption Greaves'), +(2, 0, 7, 0, 224, 40592, 'Phase 1', 'Paladin', 'Holy', 'Feet', 'Both', 'Boots of Healing Energies'), +(2, 0, 8, 0, 224, 40332, 'Phase 1', 'Paladin', 'Holy', 'Wrists', 'Both', 'Abetment Bracers'), +(2, 0, 9, 0, 224, 40570, 'Phase 1', 'Paladin', 'Holy', 'Hands', 'Both', 'Valorous Redemption Gloves'), +(2, 0, 12, 0, 224, 44255, 'Phase 1', 'Paladin', 'Holy', 'Trinket1', 'Both', 'Darkmoon Card: Greatness'), +(2, 0, 14, 0, 224, 44005, 'Phase 1', 'Paladin', 'Holy', 'Back', 'Both', 'Pennant Cloak'), +(2, 0, 17, 0, 224, 40705, 'Phase 1', 'Paladin', 'Holy', 'Ranged', 'Both', 'Libram of Renewal'); + +-- ilvl 245 (Phase 2) +INSERT INTO `playerbots_bis_gear` VALUES +(2, 0, 0, 0, 245, 46180, 'Phase 2', 'Paladin', 'Holy', 'Head', 'Both', 'Conqueror''s Aegis Headpiece'), +(2, 0, 1, 0, 245, 45443, 'Phase 2', 'Paladin', 'Holy', 'Neck', 'Both', 'Charm of Meticulous Timing'), +(2, 0, 2, 0, 245, 46182, 'Phase 2', 'Paladin', 'Holy', 'Shoulders', 'Both', 'Conqueror''s Aegis Spaulders'), +(2, 0, 4, 0, 245, 45445, 'Phase 2', 'Paladin', 'Holy', 'Chest', 'Both', 'Breastplate of the Devoted'), +(2, 0, 5, 0, 245, 45616, 'Phase 2', 'Paladin', 'Holy', 'Waist', 'Both', 'Star-beaded Clutch'), +(2, 0, 6, 0, 245, 46181, 'Phase 2', 'Paladin', 'Holy', 'Legs', 'Both', 'Conqueror''s Aegis Greaves'), +(2, 0, 7, 0, 245, 45537, 'Phase 2', 'Paladin', 'Holy', 'Feet', 'Both', 'Treads of the False Oracle'), +(2, 0, 8, 0, 245, 45460, 'Phase 2', 'Paladin', 'Holy', 'Wrists', 'Both', 'Bindings of Winter Gale'), +(2, 0, 9, 0, 245, 46155, 'Phase 2', 'Paladin', 'Holy', 'Hands', 'Both', 'Conqueror''s Aegis Gauntlets'), +(2, 0, 12, 0, 245, 46051, 'Phase 2', 'Paladin', 'Holy', 'Trinket1', 'Both', 'Meteorite Crystal'), +(2, 0, 14, 0, 245, 45486, 'Phase 2', 'Paladin', 'Holy', 'Back', 'Both', 'Drape of the Sullen Goddess'), +(2, 0, 15, 0, 245, 46017, 'Phase 2', 'Paladin', 'Holy', 'MainHand', 'Both', 'Val''anyr, Hammer of Ancient Kings'), +(2, 0, 17, 0, 245, 40705, 'Phase 2', 'Paladin', 'Holy', 'Ranged', 'Both', 'Libram of Renewal'); + +-- ilvl 258 (Phase 3) +INSERT INTO `playerbots_bis_gear` VALUES +(2, 0, 0, 0, 258, 48582, 'Phase 3', 'Paladin', 'Holy', 'Head', 'Both', 'Headpiece of Triumph'), +(2, 0, 2, 0, 258, 48580, 'Phase 3', 'Paladin', 'Holy', 'Shoulders', 'Both', 'Spaulders of Triumph'), +(2, 0, 4, 1, 258, 47147, 'Phase 3', 'Paladin', 'Holy', 'Chest', 'Alliance', 'Breastplate of the Frozen Lake'), +(2, 0, 4, 2, 258, 47471, 'Phase 3', 'Paladin', 'Holy', 'Chest', 'Horde', 'Chestplate of the Frozen Lake'), +(2, 0, 5, 1, 258, 47924, 'Phase 3', 'Paladin', 'Holy', 'Waist', 'Alliance', 'Belt of the Frozen Reach'), +(2, 0, 5, 2, 258, 47997, 'Phase 3', 'Paladin', 'Holy', 'Waist', 'Horde', 'Girdle of the Frozen Reach'), +(2, 0, 6, 1, 258, 47190, 'Phase 3', 'Paladin', 'Holy', 'Legs', 'Alliance', 'Legwraps of the Awakening'), +(2, 0, 6, 2, 258, 47479, 'Phase 3', 'Paladin', 'Holy', 'Legs', 'Horde', 'Leggings of the Awakening'), +(2, 0, 7, 1, 258, 46986, 'Phase 3', 'Paladin', 'Holy', 'Feet', 'Alliance', 'Boots of the Courageous'), +(2, 0, 7, 2, 258, 47424, 'Phase 3', 'Paladin', 'Holy', 'Feet', 'Horde', 'Sabatons of the Courageous'), +(2, 0, 8, 0, 258, 45460, 'Phase 3', 'Paladin', 'Holy', 'Wrists', 'Both', 'Bindings of Winter Gale'), +(2, 0, 9, 0, 258, 45665, 'Phase 3', 'Paladin', 'Holy', 'Hands', 'Both', 'Pharos Gloves'), +(2, 0, 10, 1, 258, 47224, 'Phase 3', 'Paladin', 'Holy', 'Finger1', 'Alliance', 'Ring of the Darkmender'), +(2, 0, 10, 2, 258, 47439, 'Phase 3', 'Paladin', 'Holy', 'Finger1', 'Horde', 'Circle of the Darkmender'), +(2, 0, 12, 0, 258, 46051, 'Phase 3', 'Paladin', 'Holy', 'Trinket1', 'Both', 'Meteorite Crystal'), +(2, 0, 14, 1, 258, 47552, 'Phase 3', 'Paladin', 'Holy', 'Back', 'Alliance', 'Jaina''s Radiance'), +(2, 0, 14, 2, 258, 47551, 'Phase 3', 'Paladin', 'Holy', 'Back', 'Horde', 'Aethas'' Intensity'); + +-- ilvl 264 (Phase 4) +INSERT INTO `playerbots_bis_gear` VALUES +(2, 0, 0, 0, 264, 51272, 'Phase 4', 'Paladin', 'Holy', 'Head', 'Both', 'Sanctified Lightsworn Headpiece'), +(2, 0, 2, 0, 264, 51273, 'Phase 4', 'Paladin', 'Holy', 'Shoulders', 'Both', 'Sanctified Lightsworn Spaulders'), +(2, 0, 4, 0, 264, 50680, 'Phase 4', 'Paladin', 'Holy', 'Chest', 'Both', 'Rot-Resistant Breastplate'), +(2, 0, 5, 0, 264, 50613, 'Phase 4', 'Paladin', 'Holy', 'Waist', 'Both', 'Crushing Coldwraith Belt'), +(2, 0, 6, 0, 264, 51928, 'Phase 4', 'Paladin', 'Holy', 'Legs', 'Both', 'Corrupted Silverplate Leggings'), +(2, 0, 7, 0, 264, 50699, 'Phase 4', 'Paladin', 'Holy', 'Feet', 'Both', 'Plague Scientist''s Boots'), +(2, 0, 8, 0, 264, 50721, 'Phase 4', 'Paladin', 'Holy', 'Wrists', 'Both', 'Crypt Keeper''s Bracers'), +(2, 0, 9, 0, 264, 50650, 'Phase 4', 'Paladin', 'Holy', 'Hands', 'Both', 'Fallen Lord''s Handguards'), +(2, 0, 10, 0, 264, 50400, 'Phase 4', 'Paladin', 'Holy', 'Finger1', 'Both', 'Ashen Band of Endless Wisdom'), +(2, 0, 12, 0, 264, 46051, 'Phase 4', 'Paladin', 'Holy', 'Trinket1', 'Both', 'Meteorite Crystal'), +(2, 0, 14, 0, 264, 50628, 'Phase 4', 'Paladin', 'Holy', 'Back', 'Both', 'Frostbinder''s Shredded Cape'); + +-- ilvl 290 (Phase 5) +INSERT INTO `playerbots_bis_gear` VALUES +(2, 0, 0, 0, 290, 51272, 'Phase 5', 'Paladin', 'Holy', 'Head', 'Both', 'Sanctified Lightsworn Headpiece'), +(2, 0, 1, 0, 290, 50182, 'Phase 5', 'Paladin', 'Holy', 'Neck', 'Both', 'Blood Queen''s Crimson Choker'), +(2, 0, 2, 0, 290, 51273, 'Phase 5', 'Paladin', 'Holy', 'Shoulders', 'Both', 'Sanctified Lightsworn Spaulders'), +(2, 0, 4, 0, 290, 50680, 'Phase 5', 'Paladin', 'Holy', 'Chest', 'Both', 'Rot-Resistant Breastplate'), +(2, 0, 5, 0, 290, 54587, 'Phase 5', 'Paladin', 'Holy', 'Waist', 'Both', 'Split Shape Belt'), +(2, 0, 6, 0, 290, 50694, 'Phase 5', 'Paladin', 'Holy', 'Legs', 'Both', 'Plaguebringer''s Stained Pants'), +(2, 0, 7, 0, 290, 54586, 'Phase 5', 'Paladin', 'Holy', 'Feet', 'Both', 'Foreshadow Steps'), +(2, 0, 8, 0, 290, 54582, 'Phase 5', 'Paladin', 'Holy', 'Wrists', 'Both', 'Bracers of Fiery Night'), +(2, 0, 9, 0, 290, 50650, 'Phase 5', 'Paladin', 'Holy', 'Hands', 'Both', 'Fallen Lord''s Handguards'), +(2, 0, 10, 0, 290, 50664, 'Phase 5', 'Paladin', 'Holy', 'Finger1', 'Both', 'Ring of Rapid Ascent'), +(2, 0, 11, 0, 290, 50400, 'Phase 5', 'Paladin', 'Holy', 'Finger2', 'Both', 'Ashen Band of Endless Wisdom'), +(2, 0, 12, 0, 290, 46051, 'Phase 5', 'Paladin', 'Holy', 'Trinket1', 'Both', 'Meteorite Crystal'), +(2, 0, 13, 0, 290, 48724, 'Phase 5', 'Paladin', 'Holy', 'Trinket2', 'Both', 'Talisman of Resurgence'), +(2, 0, 14, 0, 290, 54583, 'Phase 5', 'Paladin', 'Holy', 'Back', 'Both', 'Cloak of Burning Dusk'), +(2, 0, 15, 0, 290, 46017, 'Phase 5', 'Paladin', 'Holy', 'MainHand', 'Both', 'Val''anyr, Hammer of Ancient Kings'), +(2, 0, 16, 0, 290, 50616, 'Phase 5', 'Paladin', 'Holy', 'OffHand', 'Both', 'Bulwark of Smouldering Steel'), +(2, 0, 17, 0, 290, 40705, 'Phase 5', 'Paladin', 'Holy', 'Ranged', 'Both', 'Libram of Renewal'); + +-- Protection (tab 1) +-- ilvl 66 (Phase 1 (Pre-Raid)) +INSERT INTO `playerbots_bis_gear` VALUES +(2, 1, 0, 0, 66, 12952, 'Phase 1 (Pre-Raid)', 'Paladin', 'Protection', 'Head', 'Both', 'Gyth''s Skull'), +(2, 1, 1, 0, 66, 13091, 'Phase 1 (Pre-Raid)', 'Paladin', 'Protection', 'Neck', 'Both', 'Medallion of Grand Marshal Morris'), +(2, 1, 2, 0, 66, 14552, 'Phase 1 (Pre-Raid)', 'Paladin', 'Protection', 'Shoulders', 'Both', 'Stockade Pauldrons'), +(2, 1, 4, 0, 66, 14624, 'Phase 1 (Pre-Raid)', 'Paladin', 'Protection', 'Chest', 'Both', 'Deathbone Chestplate'), +(2, 1, 5, 0, 66, 14620, 'Phase 1 (Pre-Raid)', 'Paladin', 'Protection', 'Waist', 'Both', 'Deathbone Girdle'), +(2, 1, 6, 0, 66, 14623, 'Phase 1 (Pre-Raid)', 'Paladin', 'Protection', 'Legs', 'Both', 'Deathbone Legguards'), +(2, 1, 7, 0, 66, 14621, 'Phase 1 (Pre-Raid)', 'Paladin', 'Protection', 'Feet', 'Both', 'Deathbone Sabatons'), +(2, 1, 8, 0, 66, 12550, 'Phase 1 (Pre-Raid)', 'Paladin', 'Protection', 'Wrists', 'Both', 'Runed Golem Shackles'), +(2, 1, 9, 0, 66, 14622, 'Phase 1 (Pre-Raid)', 'Paladin', 'Protection', 'Hands', 'Both', 'Deathbone Gauntlets'), +(2, 1, 10, 0, 66, 11669, 'Phase 1 (Pre-Raid)', 'Paladin', 'Protection', 'Finger1', 'Both', 'Naglering'), +(2, 1, 11, 0, 66, 10795, 'Phase 1 (Pre-Raid)', 'Paladin', 'Protection', 'Finger2', 'Both', 'Drakeclaw Band'), +(2, 1, 13, 0, 66, 10779, 'Phase 1 (Pre-Raid)', 'Paladin', 'Protection', 'Trinket2', 'Both', 'Demon''s Blood'), +(2, 1, 14, 0, 66, 13397, 'Phase 1 (Pre-Raid)', 'Paladin', 'Protection', 'Back', 'Both', 'Stoneskin Gargoyle Cape'), +(2, 1, 15, 0, 66, 11784, 'Phase 1 (Pre-Raid)', 'Paladin', 'Protection', 'MainHand', 'Both', 'Arbiter''s Blade'), +(2, 1, 16, 0, 66, 12602, 'Phase 1 (Pre-Raid)', 'Paladin', 'Protection', 'OffHand', 'Both', 'Draconian Deflector'); + +-- ilvl 76 (Phase 2 (Pre-Raid)) +INSERT INTO `playerbots_bis_gear` VALUES +(2, 1, 0, 0, 76, 12952, 'Phase 2 (Pre-Raid)', 'Paladin', 'Protection', 'Head', 'Both', 'Gyth''s Skull'), +(2, 1, 1, 0, 76, 13091, 'Phase 2 (Pre-Raid)', 'Paladin', 'Protection', 'Neck', 'Both', 'Medallion of Grand Marshal Morris'), +(2, 1, 2, 0, 76, 14552, 'Phase 2 (Pre-Raid)', 'Paladin', 'Protection', 'Shoulders', 'Both', 'Stockade Pauldrons'), +(2, 1, 4, 0, 76, 14624, 'Phase 2 (Pre-Raid)', 'Paladin', 'Protection', 'Chest', 'Both', 'Deathbone Chestplate'), +(2, 1, 5, 0, 76, 14620, 'Phase 2 (Pre-Raid)', 'Paladin', 'Protection', 'Waist', 'Both', 'Deathbone Girdle'), +(2, 1, 6, 0, 76, 14623, 'Phase 2 (Pre-Raid)', 'Paladin', 'Protection', 'Legs', 'Both', 'Deathbone Legguards'), +(2, 1, 7, 0, 76, 14621, 'Phase 2 (Pre-Raid)', 'Paladin', 'Protection', 'Feet', 'Both', 'Deathbone Sabatons'), +(2, 1, 8, 0, 76, 18754, 'Phase 2 (Pre-Raid)', 'Paladin', 'Protection', 'Wrists', 'Both', 'Fel Hardened Bracers'), +(2, 1, 9, 0, 76, 14622, 'Phase 2 (Pre-Raid)', 'Paladin', 'Protection', 'Hands', 'Both', 'Deathbone Gauntlets'), +(2, 1, 10, 0, 76, 11669, 'Phase 2 (Pre-Raid)', 'Paladin', 'Protection', 'Finger1', 'Both', 'Naglering'), +(2, 1, 11, 0, 76, 10795, 'Phase 2 (Pre-Raid)', 'Paladin', 'Protection', 'Finger2', 'Both', 'Drakeclaw Band'), +(2, 1, 13, 0, 76, 10779, 'Phase 2 (Pre-Raid)', 'Paladin', 'Protection', 'Trinket2', 'Both', 'Demon''s Blood'), +(2, 1, 14, 0, 76, 18495, 'Phase 2 (Pre-Raid)', 'Paladin', 'Protection', 'Back', 'Both', 'Redoubt Cloak'), +(2, 1, 15, 0, 76, 18396, 'Phase 2 (Pre-Raid)', 'Paladin', 'Protection', 'MainHand', 'Both', 'Mind Carver'), +(2, 1, 16, 0, 76, 12602, 'Phase 2 (Pre-Raid)', 'Paladin', 'Protection', 'OffHand', 'Both', 'Draconian Deflector'); + +-- ilvl 78 (Phase 2) +INSERT INTO `playerbots_bis_gear` VALUES +(2, 1, 0, 0, 78, 12952, 'Phase 2', 'Paladin', 'Protection', 'Head', 'Both', 'Gyth''s Skull'), +(2, 1, 1, 0, 78, 17065, 'Phase 2', 'Paladin', 'Protection', 'Neck', 'Both', 'Medallion of Steadfast Might'), +(2, 1, 2, 0, 78, 14552, 'Phase 2', 'Paladin', 'Protection', 'Shoulders', 'Both', 'Stockade Pauldrons'), +(2, 1, 4, 0, 78, 14624, 'Phase 2', 'Paladin', 'Protection', 'Chest', 'Both', 'Deathbone Chestplate'), +(2, 1, 5, 0, 78, 14620, 'Phase 2', 'Paladin', 'Protection', 'Waist', 'Both', 'Deathbone Girdle'), +(2, 1, 6, 0, 78, 14623, 'Phase 2', 'Paladin', 'Protection', 'Legs', 'Both', 'Deathbone Legguards'), +(2, 1, 7, 0, 78, 18806, 'Phase 2', 'Paladin', 'Protection', 'Feet', 'Both', 'Core Forged Greaves'), +(2, 1, 8, 0, 78, 18754, 'Phase 2', 'Paladin', 'Protection', 'Wrists', 'Both', 'Fel Hardened Bracers'), +(2, 1, 9, 0, 78, 13072, 'Phase 2', 'Paladin', 'Protection', 'Hands', 'Both', 'Stonegrip Gauntlets'), +(2, 1, 10, 0, 78, 11669, 'Phase 2', 'Paladin', 'Protection', 'Finger1', 'Both', 'Naglering'), +(2, 1, 11, 0, 78, 18879, 'Phase 2', 'Paladin', 'Protection', 'Finger2', 'Both', 'Heavy Dark Iron Ring'), +(2, 1, 13, 0, 78, 18406, 'Phase 2', 'Paladin', 'Protection', 'Trinket2', 'Both', 'Onyxia Blood Talisman'), +(2, 1, 14, 0, 78, 18495, 'Phase 2', 'Paladin', 'Protection', 'Back', 'Both', 'Redoubt Cloak'), +(2, 1, 15, 0, 78, 17103, 'Phase 2', 'Paladin', 'Protection', 'MainHand', 'Both', 'Azuresong Mageblade'), +(2, 1, 16, 0, 78, 17066, 'Phase 2', 'Paladin', 'Protection', 'OffHand', 'Both', 'Drillborer Disk'); + +-- ilvl 83 (Phase 3) +INSERT INTO `playerbots_bis_gear` VALUES +(2, 1, 0, 0, 83, 12620, 'Phase 3', 'Paladin', 'Protection', 'Head', 'Both', 'Enchanted Thorium Helm'), +(2, 1, 1, 0, 83, 19383, 'Phase 3', 'Paladin', 'Protection', 'Neck', 'Both', 'Master Dragonslayer''s Medallion'), +(2, 1, 2, 0, 83, 14552, 'Phase 3', 'Paladin', 'Protection', 'Shoulders', 'Both', 'Stockade Pauldrons'), +(2, 1, 4, 0, 83, 12618, 'Phase 3', 'Paladin', 'Protection', 'Chest', 'Both', 'Enchanted Thorium Breastplate'), +(2, 1, 5, 0, 83, 14620, 'Phase 3', 'Paladin', 'Protection', 'Waist', 'Both', 'Deathbone Girdle'), +(2, 1, 6, 0, 83, 14623, 'Phase 3', 'Paladin', 'Protection', 'Legs', 'Both', 'Deathbone Legguards'), +(2, 1, 7, 0, 83, 18806, 'Phase 3', 'Paladin', 'Protection', 'Feet', 'Both', 'Core Forged Greaves'), +(2, 1, 8, 0, 83, 18754, 'Phase 3', 'Paladin', 'Protection', 'Wrists', 'Both', 'Fel Hardened Bracers'), +(2, 1, 9, 0, 83, 13072, 'Phase 3', 'Paladin', 'Protection', 'Hands', 'Both', 'Stonegrip Gauntlets'), +(2, 1, 10, 0, 83, 11669, 'Phase 3', 'Paladin', 'Protection', 'Finger1', 'Both', 'Naglering'), +(2, 1, 11, 0, 83, 18879, 'Phase 3', 'Paladin', 'Protection', 'Finger2', 'Both', 'Heavy Dark Iron Ring'), +(2, 1, 12, 0, 83, 19431, 'Phase 3', 'Paladin', 'Protection', 'Trinket1', 'Both', 'Styleen''s Impeding Scarab'), +(2, 1, 13, 0, 83, 18406, 'Phase 3', 'Paladin', 'Protection', 'Trinket2', 'Both', 'Onyxia Blood Talisman'), +(2, 1, 14, 0, 83, 18495, 'Phase 3', 'Paladin', 'Protection', 'Back', 'Both', 'Redoubt Cloak'), +(2, 1, 15, 0, 83, 19360, 'Phase 3', 'Paladin', 'Protection', 'MainHand', 'Both', 'Lok''amir il Romathis'); + +-- ilvl 88 (Phase 5) +INSERT INTO `playerbots_bis_gear` VALUES +(2, 1, 0, 0, 88, 21387, 'Phase 5', 'Paladin', 'Protection', 'Head', 'Both', 'Avenger''s Crown'), +(2, 1, 1, 0, 88, 22732, 'Phase 5', 'Paladin', 'Protection', 'Neck', 'Both', 'Mark of C''Thun'), +(2, 1, 2, 0, 88, 21639, 'Phase 5', 'Paladin', 'Protection', 'Shoulders', 'Both', 'Pauldrons of the Unrelenting'), +(2, 1, 4, 0, 88, 21389, 'Phase 5', 'Paladin', 'Protection', 'Chest', 'Both', 'Avenger''s Breastplate'), +(2, 1, 5, 0, 88, 21598, 'Phase 5', 'Paladin', 'Protection', 'Waist', 'Both', 'Royal Qiraji Belt'), +(2, 1, 6, 0, 88, 19855, 'Phase 5', 'Paladin', 'Protection', 'Legs', 'Both', 'Bloodsoaked Legplates'), +(2, 1, 7, 0, 88, 21706, 'Phase 5', 'Paladin', 'Protection', 'Feet', 'Both', 'Boots of the Unwavering Will'), +(2, 1, 8, 0, 88, 18754, 'Phase 5', 'Paladin', 'Protection', 'Wrists', 'Both', 'Fel Hardened Bracers'), +(2, 1, 9, 0, 88, 21674, 'Phase 5', 'Paladin', 'Protection', 'Hands', 'Both', 'Gauntlets of Steadfast Determination'), +(2, 1, 10, 0, 88, 21200, 'Phase 5', 'Paladin', 'Protection', 'Finger1', 'Both', 'Signet Ring of the Bronze Dragonflight'), +(2, 1, 11, 0, 88, 21601, 'Phase 5', 'Paladin', 'Protection', 'Finger2', 'Both', 'Ring of Emperor Vek''lor'), +(2, 1, 12, 0, 88, 19431, 'Phase 5', 'Paladin', 'Protection', 'Trinket1', 'Both', 'Styleen''s Impeding Scarab'), +(2, 1, 13, 0, 88, 18406, 'Phase 5', 'Paladin', 'Protection', 'Trinket2', 'Both', 'Onyxia Blood Talisman'), +(2, 1, 14, 0, 88, 19888, 'Phase 5', 'Paladin', 'Protection', 'Back', 'Both', 'Overlord''s Embrace'), +(2, 1, 15, 0, 88, 21622, 'Phase 5', 'Paladin', 'Protection', 'MainHand', 'Both', 'Sharpened Silithid Femur'), +(2, 1, 16, 0, 88, 21269, 'Phase 5', 'Paladin', 'Protection', 'OffHand', 'Both', 'Blessed Qiraji Bulwark'), +(2, 1, 17, 0, 88, 22401, 'Phase 5', 'Paladin', 'Protection', 'Ranged', 'Both', 'Libram of Hope'); + +-- ilvl 92 (Phase 6) +INSERT INTO `playerbots_bis_gear` VALUES +(2, 1, 0, 0, 92, 21387, 'Phase 6', 'Paladin', 'Protection', 'Head', 'Both', 'Avenger''s Crown'), +(2, 1, 1, 0, 92, 22732, 'Phase 6', 'Paladin', 'Protection', 'Neck', 'Both', 'Mark of C''Thun'), +(2, 1, 2, 0, 92, 21639, 'Phase 6', 'Paladin', 'Protection', 'Shoulders', 'Both', 'Pauldrons of the Unrelenting'), +(2, 1, 4, 0, 92, 21389, 'Phase 6', 'Paladin', 'Protection', 'Chest', 'Both', 'Avenger''s Breastplate'), +(2, 1, 5, 0, 92, 21598, 'Phase 6', 'Paladin', 'Protection', 'Waist', 'Both', 'Royal Qiraji Belt'), +(2, 1, 6, 0, 92, 19855, 'Phase 6', 'Paladin', 'Protection', 'Legs', 'Both', 'Bloodsoaked Legplates'), +(2, 1, 7, 0, 92, 21706, 'Phase 6', 'Paladin', 'Protection', 'Feet', 'Both', 'Boots of the Unwavering Will'), +(2, 1, 8, 0, 92, 18754, 'Phase 6', 'Paladin', 'Protection', 'Wrists', 'Both', 'Fel Hardened Bracers'), +(2, 1, 9, 0, 92, 21674, 'Phase 6', 'Paladin', 'Protection', 'Hands', 'Both', 'Gauntlets of Steadfast Determination'), +(2, 1, 10, 0, 92, 21200, 'Phase 6', 'Paladin', 'Protection', 'Finger1', 'Both', 'Signet Ring of the Bronze Dragonflight'), +(2, 1, 11, 0, 92, 21601, 'Phase 6', 'Paladin', 'Protection', 'Finger2', 'Both', 'Ring of Emperor Vek''lor'), +(2, 1, 12, 0, 92, 19431, 'Phase 6', 'Paladin', 'Protection', 'Trinket1', 'Both', 'Styleen''s Impeding Scarab'), +(2, 1, 13, 0, 92, 18406, 'Phase 6', 'Paladin', 'Protection', 'Trinket2', 'Both', 'Onyxia Blood Talisman'), +(2, 1, 14, 0, 92, 22938, 'Phase 6', 'Paladin', 'Protection', 'Back', 'Both', 'Cryptfiend Silk Cloak'), +(2, 1, 15, 0, 92, 22988, 'Phase 6', 'Paladin', 'Protection', 'MainHand', 'Both', 'The End of Dreams'), +(2, 1, 16, 0, 92, 22818, 'Phase 6', 'Paladin', 'Protection', 'OffHand', 'Both', 'The Plague Bearer'), +(2, 1, 17, 0, 92, 22401, 'Phase 6', 'Paladin', 'Protection', 'Ranged', 'Both', 'Libram of Hope'); + +-- ilvl 120 (Pre-Raid) +INSERT INTO `playerbots_bis_gear` VALUES +(2, 1, 0, 0, 120, 32083, 'Pre-Raid', 'Paladin', 'Protection', 'Head', 'Both', 'Faceguard of Determination'), +(2, 1, 1, 0, 120, 29386, 'Pre-Raid', 'Paladin', 'Protection', 'Neck', 'Both', 'Necklace of the Juggernaut'), +(2, 1, 2, 0, 120, 27739, 'Pre-Raid', 'Paladin', 'Protection', 'Shoulders', 'Both', 'Spaulders of the Righteous'), +(2, 1, 4, 0, 120, 28262, 'Pre-Raid', 'Paladin', 'Protection', 'Chest', 'Both', 'Jade-Skull Breastplate'), +(2, 1, 5, 0, 120, 29253, 'Pre-Raid', 'Paladin', 'Protection', 'Waist', 'Both', 'Girdle of Valorous Deeds'), +(2, 1, 6, 0, 120, 29184, 'Pre-Raid', 'Paladin', 'Protection', 'Legs', 'Both', 'Timewarden''s Leggings'), +(2, 1, 7, 0, 120, 29254, 'Pre-Raid', 'Paladin', 'Protection', 'Feet', 'Both', 'Boots of the Righteous Path'), +(2, 1, 8, 0, 120, 29252, 'Pre-Raid', 'Paladin', 'Protection', 'Wrists', 'Both', 'Bracers of Dignity'), +(2, 1, 9, 0, 120, 27535, 'Pre-Raid', 'Paladin', 'Protection', 'Hands', 'Both', 'Gauntlets of the Righteous'), +(2, 1, 10, 0, 120, 29323, 'Pre-Raid', 'Paladin', 'Protection', 'Finger1', 'Both', 'Andormu''s Tear'), +(2, 1, 11, 0, 120, 28407, 'Pre-Raid', 'Paladin', 'Protection', 'Finger2', 'Both', 'Elementium Band of the Sentry'), +(2, 1, 12, 0, 120, 27891, 'Pre-Raid', 'Paladin', 'Protection', 'Trinket1', 'Both', 'Adamantine Figure'), +(2, 1, 13, 0, 120, 27529, 'Pre-Raid', 'Paladin', 'Protection', 'Trinket2', 'Both', 'Figurine of the Colossus'), +(2, 1, 14, 0, 120, 27804, 'Pre-Raid', 'Paladin', 'Protection', 'Back', 'Both', 'Devilshark Cape'), +(2, 1, 15, 0, 120, 30832, 'Pre-Raid', 'Paladin', 'Protection', 'MainHand', 'Both', 'Gavel of Unearthed Secrets'), +(2, 1, 16, 0, 120, 29266, 'Pre-Raid', 'Paladin', 'Protection', 'OffHand', 'Both', 'Azure-Shield of Coldarra'), +(2, 1, 17, 0, 120, 29388, 'Pre-Raid', 'Paladin', 'Protection', 'Ranged', 'Both', 'Libram of Repentance'); + +-- ilvl 125 (Phase 1) +INSERT INTO `playerbots_bis_gear` VALUES +(2, 1, 0, 0, 125, 29068, 'Phase 1', 'Paladin', 'Protection', 'Head', 'Both', 'Justicar Faceguard'), +(2, 1, 1, 0, 125, 28516, 'Phase 1', 'Paladin', 'Protection', 'Neck', 'Both', 'Barbed Choker of Discipline'), +(2, 1, 2, 0, 125, 29070, 'Phase 1', 'Paladin', 'Protection', 'Shoulders', 'Both', 'Justicar Shoulderguards'), +(2, 1, 4, 0, 125, 29066, 'Phase 1', 'Paladin', 'Protection', 'Chest', 'Both', 'Justicar Chestguard'), +(2, 1, 5, 0, 125, 28566, 'Phase 1', 'Paladin', 'Protection', 'Waist', 'Both', 'Crimson Girdle of the Indomitable'), +(2, 1, 6, 0, 125, 29069, 'Phase 1', 'Paladin', 'Protection', 'Legs', 'Both', 'Justicar Legguards'), +(2, 1, 7, 0, 125, 30641, 'Phase 1', 'Paladin', 'Protection', 'Feet', 'Both', 'Boots of Elusion'), +(2, 1, 8, 0, 125, 23538, 'Phase 1', 'Paladin', 'Protection', 'Wrists', 'Both', 'Bracers of the Green Fortress'), +(2, 1, 9, 0, 125, 28518, 'Phase 1', 'Paladin', 'Protection', 'Hands', 'Both', 'Iron Gauntlets of the Maiden'), +(2, 1, 10, 0, 125, 28792, 'Phase 1', 'Paladin', 'Protection', 'Finger1', 'Both', 'A''dal''s Signet of Defense'), +(2, 1, 11, 0, 125, 29279, 'Phase 1', 'Paladin', 'Protection', 'Finger2', 'Both', 'Violet Signet of the Great Protector'), +(2, 1, 12, 0, 125, 28789, 'Phase 1', 'Paladin', 'Protection', 'Trinket1', 'Both', 'Eye of Magtheridon'), +(2, 1, 13, 0, 125, 29370, 'Phase 1', 'Paladin', 'Protection', 'Trinket2', 'Both', 'Icon of the Silver Crescent'), +(2, 1, 14, 0, 125, 28660, 'Phase 1', 'Paladin', 'Protection', 'Back', 'Both', 'Gilded Thorium Cloak'), +(2, 1, 15, 0, 125, 28802, 'Phase 1', 'Paladin', 'Protection', 'MainHand', 'Both', 'Bloodmaw Magus-Blade'), +(2, 1, 16, 0, 125, 28825, 'Phase 1', 'Paladin', 'Protection', 'OffHand', 'Both', 'Aldori Legacy Defender'), +(2, 1, 17, 0, 125, 29388, 'Phase 1', 'Paladin', 'Protection', 'Ranged', 'Both', 'Libram of Repentance'); + +-- ilvl 141 (Phase 2) +INSERT INTO `playerbots_bis_gear` VALUES +(2, 1, 0, 0, 141, 30125, 'Phase 2', 'Paladin', 'Protection', 'Head', 'Both', 'Crystalforge Faceguard'), +(2, 1, 1, 0, 141, 30007, 'Phase 2', 'Paladin', 'Protection', 'Neck', 'Both', 'The Darkener''s Grasp'), +(2, 1, 2, 0, 141, 29070, 'Phase 2', 'Paladin', 'Protection', 'Shoulders', 'Both', 'Justicar Shoulderguards'), +(2, 1, 4, 0, 141, 29066, 'Phase 2', 'Paladin', 'Protection', 'Chest', 'Both', 'Justicar Chestguard'), +(2, 1, 5, 0, 141, 30034, 'Phase 2', 'Paladin', 'Protection', 'Waist', 'Both', 'Belt of the Guardian'), +(2, 1, 6, 0, 141, 30126, 'Phase 2', 'Paladin', 'Protection', 'Legs', 'Both', 'Crystalforge Legguards'), +(2, 1, 7, 0, 141, 30033, 'Phase 2', 'Paladin', 'Protection', 'Feet', 'Both', 'Boots of the Protector'), +(2, 1, 8, 0, 141, 32515, 'Phase 2', 'Paladin', 'Protection', 'Wrists', 'Both', 'Wristguards of Determination'), +(2, 1, 9, 0, 141, 30124, 'Phase 2', 'Paladin', 'Protection', 'Hands', 'Both', 'Crystalforge Handguards'), +(2, 1, 10, 0, 141, 30083, 'Phase 2', 'Paladin', 'Protection', 'Finger1', 'Both', 'Ring of Sundered Souls'), +(2, 1, 11, 0, 141, 33054, 'Phase 2', 'Paladin', 'Protection', 'Finger2', 'Both', 'The Seal of Danzalar'), +(2, 1, 12, 0, 141, 28789, 'Phase 2', 'Paladin', 'Protection', 'Trinket1', 'Both', 'Eye of Magtheridon'), +(2, 1, 13, 0, 141, 29370, 'Phase 2', 'Paladin', 'Protection', 'Trinket2', 'Both', 'Icon of the Silver Crescent'), +(2, 1, 14, 0, 141, 29925, 'Phase 2', 'Paladin', 'Protection', 'Back', 'Both', 'Phoenix-Wing Cloak'), +(2, 1, 15, 0, 141, 32963, 'Phase 2', 'Paladin', 'Protection', 'MainHand', 'Both', 'Merciless Gladiator''s Gavel'), +(2, 1, 16, 0, 141, 33313, 'Phase 2', 'Paladin', 'Protection', 'OffHand', 'Both', 'Merciless Gladiator''s Barrier'), +(2, 1, 17, 0, 141, 29388, 'Phase 2', 'Paladin', 'Protection', 'Ranged', 'Both', 'Libram of Repentance'); + +-- ilvl 156 (Phase 3) +INSERT INTO `playerbots_bis_gear` VALUES +(2, 1, 0, 0, 156, 32521, 'Phase 3', 'Paladin', 'Protection', 'Head', 'Both', 'Faceplate of the Impenetrable'), +(2, 1, 1, 0, 156, 32362, 'Phase 3', 'Paladin', 'Protection', 'Neck', 'Both', 'Pendant of Titans'), +(2, 1, 2, 0, 156, 30998, 'Phase 3', 'Paladin', 'Protection', 'Shoulders', 'Both', 'Lightbringer Shoulderguards'), +(2, 1, 4, 0, 156, 30991, 'Phase 3', 'Paladin', 'Protection', 'Chest', 'Both', 'Lightbringer Chestguard'), +(2, 1, 5, 0, 156, 32342, 'Phase 3', 'Paladin', 'Protection', 'Waist', 'Both', 'Girdle of Mighty Resolve'), +(2, 1, 6, 0, 156, 30995, 'Phase 3', 'Paladin', 'Protection', 'Legs', 'Both', 'Lightbringer Legguards'), +(2, 1, 7, 0, 156, 32245, 'Phase 3', 'Paladin', 'Protection', 'Feet', 'Both', 'Tide-Stomper''s Greaves'), +(2, 1, 8, 0, 156, 32279, 'Phase 3', 'Paladin', 'Protection', 'Wrists', 'Both', 'The Seeker''s Wristguards'), +(2, 1, 9, 0, 156, 30985, 'Phase 3', 'Paladin', 'Protection', 'Hands', 'Both', 'Lightbringer Handguards'), +(2, 1, 10, 0, 156, 32261, 'Phase 3', 'Paladin', 'Protection', 'Finger1', 'Both', 'Band of the Abyssal Lord'), +(2, 1, 11, 0, 156, 33054, 'Phase 3', 'Paladin', 'Protection', 'Finger2', 'Both', 'The Seal of Danzalar'), +(2, 1, 12, 0, 156, 31858, 'Phase 3', 'Paladin', 'Protection', 'Trinket1', 'Both', 'Darkmoon Card: Vengeance'), +(2, 1, 13, 0, 156, 32501, 'Phase 3', 'Paladin', 'Protection', 'Trinket2', 'Both', 'Shadowmoon Insignia'), +(2, 1, 14, 0, 156, 34010, 'Phase 3', 'Paladin', 'Protection', 'Back', 'Both', 'Pepe''s Shroud of Pacification'), +(2, 1, 15, 0, 156, 30910, 'Phase 3', 'Paladin', 'Protection', 'MainHand', 'Both', 'Tempest of Chaos'), +(2, 1, 16, 0, 156, 32375, 'Phase 3', 'Paladin', 'Protection', 'OffHand', 'Both', 'Bulwark of Azzinoth'), +(2, 1, 17, 0, 156, 29388, 'Phase 3', 'Paladin', 'Protection', 'Ranged', 'Both', 'Libram of Repentance'); + +-- ilvl 164 (Phase 4) +INSERT INTO `playerbots_bis_gear` VALUES +(2, 1, 0, 0, 164, 32521, 'Phase 4', 'Paladin', 'Protection', 'Head', 'Both', 'Faceplate of the Impenetrable'), +(2, 1, 1, 0, 164, 32362, 'Phase 4', 'Paladin', 'Protection', 'Neck', 'Both', 'Pendant of Titans'), +(2, 1, 2, 0, 164, 30998, 'Phase 4', 'Paladin', 'Protection', 'Shoulders', 'Both', 'Lightbringer Shoulderguards'), +(2, 1, 4, 0, 164, 30991, 'Phase 4', 'Paladin', 'Protection', 'Chest', 'Both', 'Lightbringer Chestguard'), +(2, 1, 5, 0, 164, 32342, 'Phase 4', 'Paladin', 'Protection', 'Waist', 'Both', 'Girdle of Mighty Resolve'), +(2, 1, 6, 0, 164, 30995, 'Phase 4', 'Paladin', 'Protection', 'Legs', 'Both', 'Lightbringer Legguards'), +(2, 1, 7, 0, 164, 32245, 'Phase 4', 'Paladin', 'Protection', 'Feet', 'Both', 'Tide-Stomper''s Greaves'), +(2, 1, 8, 0, 164, 32279, 'Phase 4', 'Paladin', 'Protection', 'Wrists', 'Both', 'The Seeker''s Wristguards'), +(2, 1, 9, 0, 164, 30985, 'Phase 4', 'Paladin', 'Protection', 'Hands', 'Both', 'Lightbringer Handguards'), +(2, 1, 10, 0, 164, 32261, 'Phase 4', 'Paladin', 'Protection', 'Finger1', 'Both', 'Band of the Abyssal Lord'), +(2, 1, 11, 0, 164, 33054, 'Phase 4', 'Paladin', 'Protection', 'Finger2', 'Both', 'The Seal of Danzalar'), +(2, 1, 12, 0, 164, 31858, 'Phase 4', 'Paladin', 'Protection', 'Trinket1', 'Both', 'Darkmoon Card: Vengeance'), +(2, 1, 13, 0, 164, 32501, 'Phase 4', 'Paladin', 'Protection', 'Trinket2', 'Both', 'Shadowmoon Insignia'), +(2, 1, 14, 0, 164, 33593, 'Phase 4', 'Paladin', 'Protection', 'Back', 'Both', 'Slikk''s Cloak of Placation'), +(2, 1, 15, 0, 164, 30910, 'Phase 4', 'Paladin', 'Protection', 'MainHand', 'Both', 'Tempest of Chaos'), +(2, 1, 16, 0, 164, 32375, 'Phase 4', 'Paladin', 'Protection', 'OffHand', 'Both', 'Bulwark of Azzinoth'), +(2, 1, 17, 0, 164, 29388, 'Phase 4', 'Paladin', 'Protection', 'Ranged', 'Both', 'Libram of Repentance'); + +-- ilvl 200 (Pre-Raid) +INSERT INTO `playerbots_bis_gear` VALUES +(2, 1, 0, 0, 200, 41387, 'Pre-Raid', 'Paladin', 'Protection', 'Head', 'Both', 'Tempered Titansteel Helm'), +(2, 1, 1, 0, 200, 40679, 'Pre-Raid', 'Paladin', 'Protection', 'Neck', 'Both', 'Chained Military Gorget'), +(2, 1, 2, 0, 200, 37814, 'Pre-Raid', 'Paladin', 'Protection', 'Shoulders', 'Both', 'Iron Dwarf Smith Pauldrons'), +(2, 1, 4, 0, 200, 44198, 'Pre-Raid', 'Paladin', 'Protection', 'Chest', 'Both', 'Breastplate of the Solemn Council'), +(2, 1, 5, 0, 200, 37241, 'Pre-Raid', 'Paladin', 'Protection', 'Waist', 'Both', 'Ancient Aligned Girdle'), +(2, 1, 6, 0, 200, 37193, 'Pre-Raid', 'Paladin', 'Protection', 'Legs', 'Both', 'Staggering Legplates'), +(2, 1, 7, 0, 200, 44201, 'Pre-Raid', 'Paladin', 'Protection', 'Feet', 'Both', 'Sabatons of Draconic Vigor'), +(2, 1, 8, 0, 200, 37620, 'Pre-Raid', 'Paladin', 'Protection', 'Wrists', 'Both', 'Bracers of the Herald'), +(2, 1, 9, 0, 200, 37645, 'Pre-Raid', 'Paladin', 'Protection', 'Hands', 'Both', 'Horn-Tipped Gauntlets'), +(2, 1, 10, 0, 200, 36961, 'Pre-Raid', 'Paladin', 'Protection', 'Finger1', 'Both', 'Dragonflight Great-Ring'), +(2, 1, 12, 0, 200, 37220, 'Pre-Raid', 'Paladin', 'Protection', 'Trinket1', 'Both', 'Essence of Gossamer'), +(2, 1, 14, 0, 200, 43565, 'Pre-Raid', 'Paladin', 'Protection', 'Back', 'Both', 'Durable Nerubhide Cape'), +(2, 1, 15, 0, 200, 37401, 'Pre-Raid', 'Paladin', 'Protection', 'MainHand', 'Both', 'Red Sword of Courage'), +(2, 1, 17, 0, 200, 40707, 'Pre-Raid', 'Paladin', 'Protection', 'Ranged', 'Both', 'Libram of Obstruction'); + +-- ilvl 224 (Phase 1) +INSERT INTO `playerbots_bis_gear` VALUES +(2, 1, 0, 0, 224, 40581, 'Phase 1', 'Paladin', 'Protection', 'Head', 'Both', 'Valorous Redemption Faceguard'), +(2, 1, 1, 0, 224, 44665, 'Phase 1', 'Paladin', 'Protection', 'Neck', 'Both', 'Nexus War Champion Beads'), +(2, 1, 2, 0, 224, 40584, 'Phase 1', 'Paladin', 'Protection', 'Shoulders', 'Both', 'Valorous Redemption Shoulderguards'), +(2, 1, 4, 0, 224, 40579, 'Phase 1', 'Paladin', 'Protection', 'Chest', 'Both', 'Valorous Redemption Breastplate'), +(2, 1, 5, 0, 224, 39759, 'Phase 1', 'Paladin', 'Protection', 'Waist', 'Both', 'Ablative Chitin Girdle'), +(2, 1, 6, 0, 224, 40589, 'Phase 1', 'Paladin', 'Protection', 'Legs', 'Both', 'Legplates of Sovereignty'), +(2, 1, 7, 0, 224, 40297, 'Phase 1', 'Paladin', 'Protection', 'Feet', 'Both', 'Sabatons of Endurance'), +(2, 1, 8, 0, 224, 39764, 'Phase 1', 'Paladin', 'Protection', 'Wrists', 'Both', 'Bindings of the Hapless Prey'), +(2, 1, 9, 0, 224, 40580, 'Phase 1', 'Paladin', 'Protection', 'Hands', 'Both', 'Valorous Redemption Handguards'), +(2, 1, 10, 0, 224, 40107, 'Phase 1', 'Paladin', 'Protection', 'Finger1', 'Both', 'Sand-Worn Band'), +(2, 1, 12, 0, 224, 40372, 'Phase 1', 'Paladin', 'Protection', 'Trinket1', 'Both', 'Rune of Repulsion'), +(2, 1, 14, 0, 224, 40410, 'Phase 1', 'Paladin', 'Protection', 'Back', 'Both', 'Shadow of the Ghoul'), +(2, 1, 15, 0, 224, 40402, 'Phase 1', 'Paladin', 'Protection', 'MainHand', 'Both', 'Last Laugh'), +(2, 1, 17, 0, 224, 40337, 'Phase 1', 'Paladin', 'Protection', 'Ranged', 'Both', 'Libram of Resurgence'); + +-- ilvl 245 (Phase 2) +INSERT INTO `playerbots_bis_gear` VALUES +(2, 1, 0, 0, 245, 46175, 'Phase 2', 'Paladin', 'Protection', 'Head', 'Both', 'Conqueror''s Aegis Faceguard'), +(2, 1, 1, 0, 245, 45485, 'Phase 2', 'Paladin', 'Protection', 'Neck', 'Both', 'Bronze Pendant of the Vanir'), +(2, 1, 2, 0, 245, 46177, 'Phase 2', 'Paladin', 'Protection', 'Shoulders', 'Both', 'Conqueror''s Aegis Shoulderguards'), +(2, 1, 4, 0, 245, 46039, 'Phase 2', 'Paladin', 'Protection', 'Chest', 'Both', 'Breastplate of the Timeless'), +(2, 1, 5, 0, 245, 45825, 'Phase 2', 'Paladin', 'Protection', 'Waist', 'Both', 'Shieldwarder Girdle'), +(2, 1, 6, 0, 245, 45594, 'Phase 2', 'Paladin', 'Protection', 'Legs', 'Both', 'Legplates of the Endless Void'), +(2, 1, 7, 0, 245, 45988, 'Phase 2', 'Paladin', 'Protection', 'Feet', 'Both', 'Greaves of the Iron Army'), +(2, 1, 8, 0, 245, 45111, 'Phase 2', 'Paladin', 'Protection', 'Wrists', 'Both', 'Mimiron''s Inferno Couplings'), +(2, 1, 9, 0, 245, 45487, 'Phase 2', 'Paladin', 'Protection', 'Hands', 'Both', 'Handguards of Revitalization'), +(2, 1, 10, 0, 245, 45471, 'Phase 2', 'Paladin', 'Protection', 'Finger1', 'Both', 'Fate''s Clutch'), +(2, 1, 12, 0, 245, 45158, 'Phase 2', 'Paladin', 'Protection', 'Trinket1', 'Both', 'Heart of Iron'), +(2, 1, 14, 0, 245, 45496, 'Phase 2', 'Paladin', 'Protection', 'Back', 'Both', 'Titanskin Cloak'), +(2, 1, 17, 0, 245, 45145, 'Phase 2', 'Paladin', 'Protection', 'Ranged', 'Both', 'Libram of the Sacred Shield'); + +-- ilvl 258 (Phase 3) +INSERT INTO `playerbots_bis_gear` VALUES +(2, 1, 0, 0, 258, 48644, 'Phase 3', 'Paladin', 'Protection', 'Head', 'Both', 'Faceguard of Triumph'), +(2, 1, 2, 0, 258, 48646, 'Phase 3', 'Paladin', 'Protection', 'Shoulders', 'Both', 'Shoulderguards of Triumph'), +(2, 1, 4, 1, 258, 46968, 'Phase 3', 'Paladin', 'Protection', 'Chest', 'Alliance', 'Chestplate of the Towering Monstrosity'), +(2, 1, 4, 2, 258, 47415, 'Phase 3', 'Paladin', 'Protection', 'Chest', 'Horde', 'Hauberk of the Towering Monstrosity'), +(2, 1, 5, 1, 258, 47076, 'Phase 3', 'Paladin', 'Protection', 'Waist', 'Alliance', 'Girdle of Bloodied Scars'), +(2, 1, 5, 2, 258, 47444, 'Phase 3', 'Paladin', 'Protection', 'Waist', 'Horde', 'Belt of Bloodied Scars'), +(2, 1, 6, 0, 258, 48645, 'Phase 3', 'Paladin', 'Protection', 'Legs', 'Both', 'Legguards of Triumph'), +(2, 1, 7, 1, 258, 47003, 'Phase 3', 'Paladin', 'Protection', 'Feet', 'Alliance', 'Dawnbreaker Greaves'), +(2, 1, 7, 2, 258, 47430, 'Phase 3', 'Paladin', 'Protection', 'Feet', 'Horde', 'Dawnbreaker Sabatons'), +(2, 1, 8, 1, 258, 47918, 'Phase 3', 'Paladin', 'Protection', 'Wrists', 'Alliance', 'Dreadscale Armguards'), +(2, 1, 8, 2, 258, 47991, 'Phase 3', 'Paladin', 'Protection', 'Wrists', 'Horde', 'Dreadscale Bracers'), +(2, 1, 9, 0, 258, 48643, 'Phase 3', 'Paladin', 'Protection', 'Hands', 'Both', 'Handguards of Triumph'), +(2, 1, 10, 1, 258, 45471, 'Phase 3', 'Paladin', 'Protection', 'Finger1', 'Alliance', 'Fate''s Clutch'), +(2, 1, 12, 1, 258, 47216, 'Phase 3', 'Paladin', 'Protection', 'Trinket1', 'Alliance', 'The Black Heart'), +(2, 1, 14, 1, 258, 47549, 'Phase 3', 'Paladin', 'Protection', 'Back', 'Alliance', 'Magni''s Resolution'), +(2, 1, 14, 2, 258, 47550, 'Phase 3', 'Paladin', 'Protection', 'Back', 'Horde', 'Cairne''s Endurance'), +(2, 1, 17, 0, 258, 47664, 'Phase 3', 'Paladin', 'Protection', 'Ranged', 'Both', 'Libram of Defiance'); + +-- ilvl 264 (Phase 4) +INSERT INTO `playerbots_bis_gear` VALUES +(2, 1, 0, 0, 264, 50640, 'Phase 4', 'Paladin', 'Protection', 'Head', 'Both', 'Broken Ram Skull Helm'), +(2, 1, 1, 0, 264, 50627, 'Phase 4', 'Paladin', 'Protection', 'Neck', 'Both', 'Noose of Malachite'), +(2, 1, 2, 0, 264, 51269, 'Phase 4', 'Paladin', 'Protection', 'Shoulders', 'Both', 'Sanctified Lightsworn Shoulderguards'), +(2, 1, 4, 0, 264, 51265, 'Phase 4', 'Paladin', 'Protection', 'Chest', 'Both', 'Sanctified Lightsworn Chestguard'), +(2, 1, 5, 0, 264, 50691, 'Phase 4', 'Paladin', 'Protection', 'Waist', 'Both', 'Belt of Broken Bones'), +(2, 1, 6, 0, 264, 51268, 'Phase 4', 'Paladin', 'Protection', 'Legs', 'Both', 'Sanctified Lightsworn Legguards'), +(2, 1, 7, 0, 264, 50625, 'Phase 4', 'Paladin', 'Protection', 'Feet', 'Both', 'Grinning Skull Greatboots'), +(2, 1, 8, 0, 264, 50611, 'Phase 4', 'Paladin', 'Protection', 'Wrists', 'Both', 'Bracers of Dark Reckoning'), +(2, 1, 9, 0, 264, 51267, 'Phase 4', 'Paladin', 'Protection', 'Hands', 'Both', 'Sanctified Lightsworn Handguards'), +(2, 1, 10, 0, 264, 50404, 'Phase 4', 'Paladin', 'Protection', 'Finger1', 'Both', 'Ashen Band of Endless Courage'), +(2, 1, 12, 0, 264, 50349, 'Phase 4', 'Paladin', 'Protection', 'Trinket1', 'Both', 'Corpse Tongue Coin'), +(2, 1, 14, 0, 264, 50718, 'Phase 4', 'Paladin', 'Protection', 'Back', 'Both', 'Royal Crimson Cloak'), +(2, 1, 15, 0, 264, 50738, 'Phase 4', 'Paladin', 'Protection', 'MainHand', 'Both', 'Mithrios, Bronzebeard''s Legacy'), +(2, 1, 17, 0, 264, 50461, 'Phase 4', 'Paladin', 'Protection', 'Ranged', 'Both', 'Libram of the Eternal Tower'); + +-- ilvl 290 (Phase 5) +INSERT INTO `playerbots_bis_gear` VALUES +(2, 1, 0, 0, 290, 50640, 'Phase 5', 'Paladin', 'Protection', 'Head', 'Both', 'Broken Ram Skull Helm'), +(2, 1, 1, 0, 290, 50682, 'Phase 5', 'Paladin', 'Protection', 'Neck', 'Both', 'Bile-Encrusted Medallion'), +(2, 1, 2, 0, 290, 50660, 'Phase 5', 'Paladin', 'Protection', 'Shoulders', 'Both', 'Boneguard Commander''s Pauldrons'), +(2, 1, 4, 0, 290, 51265, 'Phase 5', 'Paladin', 'Protection', 'Chest', 'Both', 'Sanctified Lightsworn Chestguard'), +(2, 1, 5, 0, 290, 50991, 'Phase 5', 'Paladin', 'Protection', 'Waist', 'Both', 'Verdigris Chain Belt'), +(2, 1, 6, 0, 290, 49904, 'Phase 5', 'Paladin', 'Protection', 'Legs', 'Both', 'Pillars of Might'), +(2, 1, 7, 0, 290, 54579, 'Phase 5', 'Paladin', 'Protection', 'Feet', 'Both', 'Treads of Impending Resurrection'), +(2, 1, 8, 0, 290, 51901, 'Phase 5', 'Paladin', 'Protection', 'Wrists', 'Both', 'Gargoyle Spit Bracers'), +(2, 1, 9, 0, 290, 51267, 'Phase 5', 'Paladin', 'Protection', 'Hands', 'Both', 'Sanctified Lightsworn Handguards'), +(2, 1, 10, 0, 290, 50622, 'Phase 5', 'Paladin', 'Protection', 'Finger1', 'Both', 'Devium''s Eternally Cold Ring'), +(2, 1, 11, 0, 290, 50404, 'Phase 5', 'Paladin', 'Protection', 'Finger2', 'Both', 'Ashen Band of Endless Courage'), +(2, 1, 12, 0, 290, 54591, 'Phase 5', 'Paladin', 'Protection', 'Trinket1', 'Both', 'Petrified Twilight Scale'), +(2, 1, 13, 0, 290, 50364, 'Phase 5', 'Paladin', 'Protection', 'Trinket2', 'Both', 'Sindragosa''s Flawless Fang'), +(2, 1, 14, 0, 290, 50466, 'Phase 5', 'Paladin', 'Protection', 'Back', 'Both', 'Sentinel''s Winter Cloak'), +(2, 1, 15, 0, 290, 50738, 'Phase 5', 'Paladin', 'Protection', 'MainHand', 'Both', 'Mithrios, Bronzebeard''s Legacy'), +(2, 1, 16, 0, 290, 50729, 'Phase 5', 'Paladin', 'Protection', 'OffHand', 'Both', 'Icecrown Glacial Wall'), +(2, 1, 17, 0, 290, 50461, 'Phase 5', 'Paladin', 'Protection', 'Ranged', 'Both', 'Libram of the Eternal Tower'); + +-- Retribution (tab 2) +-- ilvl 66 (Phase 1 (Pre-Raid)) +INSERT INTO `playerbots_bis_gear` VALUES +(2, 2, 0, 0, 66, 13404, 'Phase 1 (Pre-Raid)', 'Paladin', 'Retribution', 'Head', 'Both', 'Mask of the Unforgiven'), +(2, 2, 1, 0, 66, 15411, 'Phase 1 (Pre-Raid)', 'Paladin', 'Retribution', 'Neck', 'Both', 'Mark of Fordring'), +(2, 2, 2, 0, 66, 12927, 'Phase 1 (Pre-Raid)', 'Paladin', 'Retribution', 'Shoulders', 'Both', 'Truestrike Shoulders'), +(2, 2, 4, 0, 66, 11726, 'Phase 1 (Pre-Raid)', 'Paladin', 'Retribution', 'Chest', 'Both', 'Savage Gladiator Chain'), +(2, 2, 5, 0, 66, 13959, 'Phase 1 (Pre-Raid)', 'Paladin', 'Retribution', 'Waist', 'Both', 'Omokk''s Girth Restrainer'), +(2, 2, 6, 0, 66, 14554, 'Phase 1 (Pre-Raid)', 'Paladin', 'Retribution', 'Legs', 'Both', 'Cloudkeeper Legplates'), +(2, 2, 7, 0, 66, 14616, 'Phase 1 (Pre-Raid)', 'Paladin', 'Retribution', 'Feet', 'Both', 'Bloodmail Boots'), +(2, 2, 8, 0, 66, 12936, 'Phase 1 (Pre-Raid)', 'Paladin', 'Retribution', 'Wrists', 'Both', 'Battleborn Armbraces'), +(2, 2, 9, 0, 66, 13957, 'Phase 1 (Pre-Raid)', 'Paladin', 'Retribution', 'Hands', 'Both', 'Gargoyle Slashers'), +(2, 2, 10, 0, 66, 13098, 'Phase 1 (Pre-Raid)', 'Paladin', 'Retribution', 'Finger1', 'Both', 'Painweaver Band'), +(2, 2, 11, 0, 66, 12548, 'Phase 1 (Pre-Raid)', 'Paladin', 'Retribution', 'Finger2', 'Both', 'Magni''s Will'), +(2, 2, 12, 0, 66, 13965, 'Phase 1 (Pre-Raid)', 'Paladin', 'Retribution', 'Trinket1', 'Both', 'Blackhand''s Breadth'), +(2, 2, 13, 0, 66, 11815, 'Phase 1 (Pre-Raid)', 'Paladin', 'Retribution', 'Trinket2', 'Both', 'Hand of Justice'), +(2, 2, 14, 0, 66, 13340, 'Phase 1 (Pre-Raid)', 'Paladin', 'Retribution', 'Back', 'Both', 'Cape of the Black Baron'), +(2, 2, 15, 0, 66, 12784, 'Phase 1 (Pre-Raid)', 'Paladin', 'Retribution', 'MainHand', 'Both', 'Arcanite Reaper'); + +-- ilvl 76 (Phase 2 (Pre-Raid)) +INSERT INTO `playerbots_bis_gear` VALUES +(2, 2, 0, 0, 76, 13404, 'Phase 2 (Pre-Raid)', 'Paladin', 'Retribution', 'Head', 'Both', 'Mask of the Unforgiven'), +(2, 2, 1, 0, 76, 15411, 'Phase 2 (Pre-Raid)', 'Paladin', 'Retribution', 'Neck', 'Both', 'Mark of Fordring'), +(2, 2, 2, 0, 76, 12927, 'Phase 2 (Pre-Raid)', 'Paladin', 'Retribution', 'Shoulders', 'Both', 'Truestrike Shoulders'), +(2, 2, 4, 0, 76, 11726, 'Phase 2 (Pre-Raid)', 'Paladin', 'Retribution', 'Chest', 'Both', 'Savage Gladiator Chain'), +(2, 2, 5, 0, 76, 13959, 'Phase 2 (Pre-Raid)', 'Paladin', 'Retribution', 'Waist', 'Both', 'Omokk''s Girth Restrainer'), +(2, 2, 6, 0, 76, 14554, 'Phase 2 (Pre-Raid)', 'Paladin', 'Retribution', 'Legs', 'Both', 'Cloudkeeper Legplates'), +(2, 2, 7, 0, 76, 14616, 'Phase 2 (Pre-Raid)', 'Paladin', 'Retribution', 'Feet', 'Both', 'Bloodmail Boots'), +(2, 2, 8, 0, 76, 12936, 'Phase 2 (Pre-Raid)', 'Paladin', 'Retribution', 'Wrists', 'Both', 'Battleborn Armbraces'), +(2, 2, 9, 0, 76, 13957, 'Phase 2 (Pre-Raid)', 'Paladin', 'Retribution', 'Hands', 'Both', 'Gargoyle Slashers'), +(2, 2, 10, 0, 76, 13098, 'Phase 2 (Pre-Raid)', 'Paladin', 'Retribution', 'Finger1', 'Both', 'Painweaver Band'), +(2, 2, 11, 0, 76, 12548, 'Phase 2 (Pre-Raid)', 'Paladin', 'Retribution', 'Finger2', 'Both', 'Magni''s Will'), +(2, 2, 12, 0, 76, 13965, 'Phase 2 (Pre-Raid)', 'Paladin', 'Retribution', 'Trinket1', 'Both', 'Blackhand''s Breadth'), +(2, 2, 13, 0, 76, 11815, 'Phase 2 (Pre-Raid)', 'Paladin', 'Retribution', 'Trinket2', 'Both', 'Hand of Justice'), +(2, 2, 14, 0, 76, 13340, 'Phase 2 (Pre-Raid)', 'Paladin', 'Retribution', 'Back', 'Both', 'Cape of the Black Baron'), +(2, 2, 15, 0, 76, 12784, 'Phase 2 (Pre-Raid)', 'Paladin', 'Retribution', 'MainHand', 'Both', 'Arcanite Reaper'); + +-- ilvl 78 (Phase 2) +INSERT INTO `playerbots_bis_gear` VALUES +(2, 2, 0, 0, 78, 12640, 'Phase 2', 'Paladin', 'Retribution', 'Head', 'Both', 'Lionheart Helm'), +(2, 2, 1, 0, 78, 18404, 'Phase 2', 'Paladin', 'Retribution', 'Neck', 'Both', 'Onyxia Tooth Pendant'), +(2, 2, 2, 0, 78, 12927, 'Phase 2', 'Paladin', 'Retribution', 'Shoulders', 'Both', 'Truestrike Shoulders'), +(2, 2, 4, 0, 78, 11726, 'Phase 2', 'Paladin', 'Retribution', 'Chest', 'Both', 'Savage Gladiator Chain'), +(2, 2, 5, 0, 78, 19137, 'Phase 2', 'Paladin', 'Retribution', 'Waist', 'Both', 'Onslaught Girdle'), +(2, 2, 6, 0, 78, 14554, 'Phase 2', 'Paladin', 'Retribution', 'Legs', 'Both', 'Cloudkeeper Legplates'), +(2, 2, 7, 0, 78, 14616, 'Phase 2', 'Paladin', 'Retribution', 'Feet', 'Both', 'Bloodmail Boots'), +(2, 2, 8, 0, 78, 19146, 'Phase 2', 'Paladin', 'Retribution', 'Wrists', 'Both', 'Wristguards of Stability'), +(2, 2, 9, 0, 78, 19143, 'Phase 2', 'Paladin', 'Retribution', 'Hands', 'Both', 'Flameguard Gauntlets'), +(2, 2, 10, 0, 78, 13098, 'Phase 2', 'Paladin', 'Retribution', 'Finger1', 'Both', 'Painweaver Band'), +(2, 2, 11, 0, 78, 18821, 'Phase 2', 'Paladin', 'Retribution', 'Finger2', 'Both', 'Quick Strike Ring'), +(2, 2, 12, 0, 78, 13965, 'Phase 2', 'Paladin', 'Retribution', 'Trinket1', 'Both', 'Blackhand''s Breadth'), +(2, 2, 13, 0, 78, 11815, 'Phase 2', 'Paladin', 'Retribution', 'Trinket2', 'Both', 'Hand of Justice'), +(2, 2, 14, 0, 78, 13340, 'Phase 2', 'Paladin', 'Retribution', 'Back', 'Both', 'Cape of the Black Baron'), +(2, 2, 15, 0, 78, 17076, 'Phase 2', 'Paladin', 'Retribution', 'MainHand', 'Both', 'Bonereaver''s Edge'); + +-- ilvl 83 (Phase 3) +INSERT INTO `playerbots_bis_gear` VALUES +(2, 2, 0, 0, 83, 12640, 'Phase 3', 'Paladin', 'Retribution', 'Head', 'Both', 'Lionheart Helm'), +(2, 2, 1, 0, 83, 18404, 'Phase 3', 'Paladin', 'Retribution', 'Neck', 'Both', 'Onyxia Tooth Pendant'), +(2, 2, 2, 0, 83, 19394, 'Phase 3', 'Paladin', 'Retribution', 'Shoulders', 'Both', 'Drake Talon Pauldrons'), +(2, 2, 4, 0, 83, 11726, 'Phase 3', 'Paladin', 'Retribution', 'Chest', 'Both', 'Savage Gladiator Chain'), +(2, 2, 5, 0, 83, 19137, 'Phase 3', 'Paladin', 'Retribution', 'Waist', 'Both', 'Onslaught Girdle'), +(2, 2, 6, 0, 83, 19402, 'Phase 3', 'Paladin', 'Retribution', 'Legs', 'Both', 'Legguards of the Fallen Crusader'), +(2, 2, 7, 0, 83, 19387, 'Phase 3', 'Paladin', 'Retribution', 'Feet', 'Both', 'Chromatic Boots'), +(2, 2, 8, 0, 83, 19146, 'Phase 3', 'Paladin', 'Retribution', 'Wrists', 'Both', 'Wristguards of Stability'), +(2, 2, 9, 0, 83, 19143, 'Phase 3', 'Paladin', 'Retribution', 'Hands', 'Both', 'Flameguard Gauntlets'), +(2, 2, 11, 0, 83, 19384, 'Phase 3', 'Paladin', 'Retribution', 'Finger2', 'Both', 'Master Dragonslayer''s Ring'), +(2, 2, 12, 0, 83, 13965, 'Phase 3', 'Paladin', 'Retribution', 'Trinket1', 'Both', 'Blackhand''s Breadth'), +(2, 2, 13, 0, 83, 11815, 'Phase 3', 'Paladin', 'Retribution', 'Trinket2', 'Both', 'Hand of Justice'), +(2, 2, 14, 0, 83, 19436, 'Phase 3', 'Paladin', 'Retribution', 'Back', 'Both', 'Cloak of Draconic Might'), +(2, 2, 15, 0, 83, 19334, 'Phase 3', 'Paladin', 'Retribution', 'MainHand', 'Both', 'The Untamed Blade'); + +-- ilvl 88 (Phase 5) +INSERT INTO `playerbots_bis_gear` VALUES +(2, 2, 0, 0, 88, 21387, 'Phase 5', 'Paladin', 'Retribution', 'Head', 'Both', 'Avenger''s Crown'), +(2, 2, 1, 0, 88, 18404, 'Phase 5', 'Paladin', 'Retribution', 'Neck', 'Both', 'Onyxia Tooth Pendant'), +(2, 2, 2, 0, 88, 21391, 'Phase 5', 'Paladin', 'Retribution', 'Shoulders', 'Both', 'Avenger''s Pauldrons'), +(2, 2, 4, 0, 88, 21389, 'Phase 5', 'Paladin', 'Retribution', 'Chest', 'Both', 'Avenger''s Breastplate'), +(2, 2, 5, 0, 88, 21463, 'Phase 5', 'Paladin', 'Retribution', 'Waist', 'Both', 'Ossirian''s Binding'), +(2, 2, 6, 0, 88, 21390, 'Phase 5', 'Paladin', 'Retribution', 'Legs', 'Both', 'Avenger''s Legguards'), +(2, 2, 7, 0, 88, 21388, 'Phase 5', 'Paladin', 'Retribution', 'Feet', 'Both', 'Avenger''s Greaves'), +(2, 2, 8, 0, 88, 21618, 'Phase 5', 'Paladin', 'Retribution', 'Wrists', 'Both', 'Hive Defiler Wristguards'), +(2, 2, 9, 0, 88, 21623, 'Phase 5', 'Paladin', 'Retribution', 'Hands', 'Both', 'Gauntlets of the Righteous Champion'), +(2, 2, 10, 0, 88, 17063, 'Phase 5', 'Paladin', 'Retribution', 'Finger1', 'Both', 'Band of Accuria'), +(2, 2, 11, 0, 88, 21205, 'Phase 5', 'Paladin', 'Retribution', 'Finger2', 'Both', 'Signet Ring of the Bronze Dragonflight'), +(2, 2, 12, 0, 88, 22321, 'Phase 5', 'Paladin', 'Retribution', 'Trinket1', 'Both', 'Heart of Wyrmthalak'), +(2, 2, 13, 0, 88, 19289, 'Phase 5', 'Paladin', 'Retribution', 'Trinket2', 'Both', 'Darkmoon Card: Maelstrom'), +(2, 2, 14, 0, 88, 21701, 'Phase 5', 'Paladin', 'Retribution', 'Back', 'Both', 'Cloak of Concentrated Hatred'), +(2, 2, 15, 0, 88, 21134, 'Phase 5', 'Paladin', 'Retribution', 'MainHand', 'Both', 'Dark Edge of Insanity'), +(2, 2, 17, 0, 88, 23203, 'Phase 5', 'Paladin', 'Retribution', 'Ranged', 'Both', 'Libram of Fervor'); + +-- ilvl 92 (Phase 6) +INSERT INTO `playerbots_bis_gear` VALUES +(2, 2, 0, 0, 92, 21387, 'Phase 6', 'Paladin', 'Retribution', 'Head', 'Both', 'Avenger''s Crown'), +(2, 2, 1, 0, 92, 18404, 'Phase 6', 'Paladin', 'Retribution', 'Neck', 'Both', 'Onyxia Tooth Pendant'), +(2, 2, 2, 0, 92, 21391, 'Phase 6', 'Paladin', 'Retribution', 'Shoulders', 'Both', 'Avenger''s Pauldrons'), +(2, 2, 4, 0, 92, 21389, 'Phase 6', 'Paladin', 'Retribution', 'Chest', 'Both', 'Avenger''s Breastplate'), +(2, 2, 5, 0, 92, 23219, 'Phase 6', 'Paladin', 'Retribution', 'Waist', 'Both', 'Girdle of the Mentor'), +(2, 2, 6, 0, 92, 21390, 'Phase 6', 'Paladin', 'Retribution', 'Legs', 'Both', 'Avenger''s Legguards'), +(2, 2, 7, 0, 92, 21388, 'Phase 6', 'Paladin', 'Retribution', 'Feet', 'Both', 'Avenger''s Greaves'), +(2, 2, 8, 0, 92, 22936, 'Phase 6', 'Paladin', 'Retribution', 'Wrists', 'Both', 'Wristguards of Vengeance'), +(2, 2, 9, 0, 92, 21623, 'Phase 6', 'Paladin', 'Retribution', 'Hands', 'Both', 'Gauntlets of the Righteous Champion'), +(2, 2, 11, 0, 92, 21205, 'Phase 6', 'Paladin', 'Retribution', 'Finger2', 'Both', 'Signet Ring of the Bronze Dragonflight'), +(2, 2, 12, 0, 92, 22321, 'Phase 6', 'Paladin', 'Retribution', 'Trinket1', 'Both', 'Heart of Wyrmthalak'), +(2, 2, 13, 0, 92, 19289, 'Phase 6', 'Paladin', 'Retribution', 'Trinket2', 'Both', 'Darkmoon Card: Maelstrom'), +(2, 2, 14, 0, 92, 23045, 'Phase 6', 'Paladin', 'Retribution', 'Back', 'Both', 'Shroud of Dominion'), +(2, 2, 15, 0, 92, 22691, 'Phase 6', 'Paladin', 'Retribution', 'MainHand', 'Both', 'Corrupted Ashbringer'), +(2, 2, 17, 0, 92, 23203, 'Phase 6', 'Paladin', 'Retribution', 'Ranged', 'Both', 'Libram of Fervor'); + +-- ilvl 200 (Pre-Raid) +INSERT INTO `playerbots_bis_gear` VALUES +(2, 2, 0, 0, 200, 41386, 'Pre-Raid', 'Paladin', 'Retribution', 'Head', 'Both', 'Spiked Titansteel Helm'), +(2, 2, 1, 0, 200, 42645, 'Pre-Raid', 'Paladin', 'Retribution', 'Neck', 'Both', 'Titanium Impact Choker'), +(2, 2, 2, 0, 200, 37627, 'Pre-Raid', 'Paladin', 'Retribution', 'Shoulders', 'Both', 'Snake Den Spaulders'), +(2, 2, 4, 0, 200, 37612, 'Pre-Raid', 'Paladin', 'Retribution', 'Chest', 'Both', 'Bonegrinder Breastplate'), +(2, 2, 5, 0, 200, 37178, 'Pre-Raid', 'Paladin', 'Retribution', 'Waist', 'Both', 'Strategist''s Belt'), +(2, 2, 6, 0, 200, 37193, 'Pre-Raid', 'Paladin', 'Retribution', 'Legs', 'Both', 'Staggering Legplates'), +(2, 2, 7, 0, 200, 43402, 'Pre-Raid', 'Paladin', 'Retribution', 'Feet', 'Both', 'The Obliterator Greaves'), +(2, 2, 8, 0, 200, 37668, 'Pre-Raid', 'Paladin', 'Retribution', 'Wrists', 'Both', 'Bands of the Stoneforge'), +(2, 2, 9, 0, 200, 37363, 'Pre-Raid', 'Paladin', 'Retribution', 'Hands', 'Both', 'Gauntlets of Dragon Wrath'), +(2, 2, 10, 0, 200, 37151, 'Pre-Raid', 'Paladin', 'Retribution', 'Finger1', 'Both', 'Band of Frosted Thorns'), +(2, 2, 12, 0, 200, 40684, 'Pre-Raid', 'Paladin', 'Retribution', 'Trinket1', 'Both', 'Mirror of Truth'), +(2, 2, 14, 0, 200, 43566, 'Pre-Raid', 'Paladin', 'Retribution', 'Back', 'Both', 'Ice Striker''s Cloak'), +(2, 2, 15, 0, 200, 41257, 'Pre-Raid', 'Paladin', 'Retribution', 'MainHand', 'Both', 'Titansteel Destroyer'), +(2, 2, 17, 0, 200, 40706, 'Pre-Raid', 'Paladin', 'Retribution', 'Ranged', 'Both', 'Libram of Reciprocation'); + +-- ilvl 224 (Phase 1) +INSERT INTO `playerbots_bis_gear` VALUES +(2, 2, 0, 0, 224, 44006, 'Phase 1', 'Paladin', 'Retribution', 'Head', 'Both', 'Obsidian Greathelm'), +(2, 2, 1, 0, 224, 44664, 'Phase 1', 'Paladin', 'Retribution', 'Neck', 'Both', 'Favor of the Dragon Queen'), +(2, 2, 2, 0, 224, 40578, 'Phase 1', 'Paladin', 'Retribution', 'Shoulders', 'Both', 'Valorous Redemption Shoulderplates'), +(2, 2, 4, 0, 224, 40574, 'Phase 1', 'Paladin', 'Retribution', 'Chest', 'Both', 'Valorous Redemption Chestpiece'), +(2, 2, 5, 0, 224, 40278, 'Phase 1', 'Paladin', 'Retribution', 'Waist', 'Both', 'Girdle of Chivalry'), +(2, 2, 6, 0, 224, 44011, 'Phase 1', 'Paladin', 'Retribution', 'Legs', 'Both', 'Leggings of the Honored'), +(2, 2, 7, 0, 224, 40591, 'Phase 1', 'Paladin', 'Retribution', 'Feet', 'Both', 'Melancholy Sabatons'), +(2, 2, 8, 0, 224, 40186, 'Phase 1', 'Paladin', 'Retribution', 'Wrists', 'Both', 'Thrusting Bands'), +(2, 2, 9, 0, 224, 40541, 'Phase 1', 'Paladin', 'Retribution', 'Hands', 'Both', 'Frosted Adroit Handguards'), +(2, 2, 10, 0, 224, 40075, 'Phase 1', 'Paladin', 'Retribution', 'Finger1', 'Both', 'Ruthlessness'), +(2, 2, 12, 0, 224, 42987, 'Phase 1', 'Paladin', 'Retribution', 'Trinket1', 'Both', 'Darkmoon Card: Greatness'), +(2, 2, 14, 0, 224, 40403, 'Phase 1', 'Paladin', 'Retribution', 'Back', 'Both', 'Drape of the Deadly Foe'), +(2, 2, 17, 0, 224, 40191, 'Phase 1', 'Paladin', 'Retribution', 'Ranged', 'Both', 'Libram of Radiance'); + +-- ilvl 245 (Phase 2) +INSERT INTO `playerbots_bis_gear` VALUES +(2, 2, 0, 0, 245, 45472, 'Phase 2', 'Paladin', 'Retribution', 'Head', 'Both', 'Warhelm of the Champion'), +(2, 2, 2, 0, 245, 45245, 'Phase 2', 'Paladin', 'Retribution', 'Shoulders', 'Both', 'Shoulderpads of the Intruder'), +(2, 2, 4, 0, 245, 45473, 'Phase 2', 'Paladin', 'Retribution', 'Chest', 'Both', 'Embrace of the Gladiator'), +(2, 2, 5, 0, 245, 46095, 'Phase 2', 'Paladin', 'Retribution', 'Waist', 'Both', 'Soul-Devouring Cinch'), +(2, 2, 6, 0, 245, 45134, 'Phase 2', 'Paladin', 'Retribution', 'Legs', 'Both', 'Plated Leggings of Ruination'), +(2, 2, 7, 0, 245, 45599, 'Phase 2', 'Paladin', 'Retribution', 'Feet', 'Both', 'Sabatons of Lifeless Night'), +(2, 2, 8, 0, 245, 45663, 'Phase 2', 'Paladin', 'Retribution', 'Wrists', 'Both', 'Armbands of Bedlam'), +(2, 2, 9, 0, 245, 45444, 'Phase 2', 'Paladin', 'Retribution', 'Hands', 'Both', 'Gloves of the Steady Hand'), +(2, 2, 10, 0, 245, 45608, 'Phase 2', 'Paladin', 'Retribution', 'Finger1', 'Both', 'Brann''s Signet Ring'), +(2, 2, 12, 0, 245, 45609, 'Phase 2', 'Paladin', 'Retribution', 'Trinket1', 'Both', 'Comet''s Trail'), +(2, 2, 14, 0, 245, 46032, 'Phase 2', 'Paladin', 'Retribution', 'Back', 'Both', 'Drape of the Faceless General'), +(2, 2, 17, 0, 245, 42853, 'Phase 2', 'Paladin', 'Retribution', 'Ranged', 'Both', 'Furious Gladiator''s Libram of Fortitude'); + +-- ilvl 258 (Phase 3) +INSERT INTO `playerbots_bis_gear` VALUES +(2, 2, 0, 0, 258, 48614, 'Phase 3', 'Paladin', 'Retribution', 'Head', 'Both', 'Helm of Triumph'), +(2, 2, 2, 0, 258, 48612, 'Phase 3', 'Paladin', 'Retribution', 'Shoulders', 'Both', 'Shoulderplates of Triumph'), +(2, 2, 4, 1, 258, 46965, 'Phase 3', 'Paladin', 'Retribution', 'Chest', 'Alliance', 'Breastplate of Cruel Intent'), +(2, 2, 4, 2, 258, 47412, 'Phase 3', 'Paladin', 'Retribution', 'Chest', 'Horde', 'Cuirass of Cruel Intent'), +(2, 2, 5, 1, 258, 47002, 'Phase 3', 'Paladin', 'Retribution', 'Waist', 'Alliance', 'Bloodbath Belt'), +(2, 2, 5, 2, 258, 47429, 'Phase 3', 'Paladin', 'Retribution', 'Waist', 'Horde', 'Bloodbath Girdle'), +(2, 2, 6, 1, 258, 47132, 'Phase 3', 'Paladin', 'Retribution', 'Legs', 'Alliance', 'Legguards of Ascension'), +(2, 2, 6, 2, 258, 47465, 'Phase 3', 'Paladin', 'Retribution', 'Legs', 'Horde', 'Legplates of Ascension'), +(2, 2, 7, 1, 258, 47154, 'Phase 3', 'Paladin', 'Retribution', 'Feet', 'Alliance', 'Greaves of the 7th Legion'), +(2, 2, 7, 2, 258, 47473, 'Phase 3', 'Paladin', 'Retribution', 'Feet', 'Horde', 'Greaves of the Saronite Citadel'), +(2, 2, 8, 1, 258, 47155, 'Phase 3', 'Paladin', 'Retribution', 'Wrists', 'Alliance', 'Bracers of Dark Determination'), +(2, 2, 8, 2, 258, 47474, 'Phase 3', 'Paladin', 'Retribution', 'Wrists', 'Horde', 'Armbands of Dark Determination'), +(2, 2, 9, 0, 258, 48615, 'Phase 3', 'Paladin', 'Retribution', 'Hands', 'Both', 'Gauntlets of Triumph'), +(2, 2, 10, 1, 258, 46966, 'Phase 3', 'Paladin', 'Retribution', 'Finger1', 'Alliance', 'Band of the Violent Temperment'), +(2, 2, 10, 2, 258, 47413, 'Phase 3', 'Paladin', 'Retribution', 'Finger1', 'Horde', 'Ring of the Violent Temperament'), +(2, 2, 12, 1, 258, 47131, 'Phase 3', 'Paladin', 'Retribution', 'Trinket1', 'Alliance', 'Death''s Verdict'), +(2, 2, 12, 2, 258, 47464, 'Phase 3', 'Paladin', 'Retribution', 'Trinket1', 'Horde', 'Death''s Choice'), +(2, 2, 14, 1, 258, 47547, 'Phase 3', 'Paladin', 'Retribution', 'Back', 'Alliance', 'Varian''s Furor'), +(2, 2, 14, 2, 258, 47548, 'Phase 3', 'Paladin', 'Retribution', 'Back', 'Horde', 'Garrosh''s Rage'), +(2, 2, 17, 0, 258, 47661, 'Phase 3', 'Paladin', 'Retribution', 'Ranged', 'Both', 'Libram of Valiance'); + +-- ilvl 264 (Phase 4) +INSERT INTO `playerbots_bis_gear` VALUES +(2, 2, 0, 0, 264, 51277, 'Phase 4', 'Paladin', 'Retribution', 'Head', 'Both', 'Sanctified Lightsworn Helmet'), +(2, 2, 1, 0, 264, 50633, 'Phase 4', 'Paladin', 'Retribution', 'Neck', 'Both', 'Sindragosa''s Cruel Claw'), +(2, 2, 2, 0, 264, 51279, 'Phase 4', 'Paladin', 'Retribution', 'Shoulders', 'Both', 'Sanctified Lightsworn Shoulderplates'), +(2, 2, 4, 0, 264, 51275, 'Phase 4', 'Paladin', 'Retribution', 'Chest', 'Both', 'Sanctified Lightsworn Battleplate'), +(2, 2, 5, 0, 264, 50707, 'Phase 4', 'Paladin', 'Retribution', 'Waist', 'Both', 'Astrylian''s Sutured Cinch'), +(2, 2, 6, 0, 264, 51278, 'Phase 4', 'Paladin', 'Retribution', 'Legs', 'Both', 'Sanctified Lightsworn Legplates'), +(2, 2, 7, 0, 264, 50607, 'Phase 4', 'Paladin', 'Retribution', 'Feet', 'Both', 'Frostbitten Fur Boots'), +(2, 2, 8, 0, 264, 50659, 'Phase 4', 'Paladin', 'Retribution', 'Wrists', 'Both', 'Polar Bear Claw Bracers'), +(2, 2, 9, 0, 264, 50690, 'Phase 4', 'Paladin', 'Retribution', 'Hands', 'Both', 'Fleshrending Gauntlets'), +(2, 2, 10, 0, 264, 50657, 'Phase 4', 'Paladin', 'Retribution', 'Finger1', 'Both', 'Skeleton Lord''s Circle'), +(2, 2, 12, 0, 264, 50706, 'Phase 4', 'Paladin', 'Retribution', 'Trinket1', 'Both', 'Tiny Abomination in a Jar'), +(2, 2, 14, 0, 264, 50653, 'Phase 4', 'Paladin', 'Retribution', 'Back', 'Both', 'Shadowvault Slayer''s Cloak'), +(2, 2, 15, 0, 264, 49623, 'Phase 4', 'Paladin', 'Retribution', 'MainHand', 'Both', 'Shadowmourne'), +(2, 2, 17, 0, 264, 50455, 'Phase 4', 'Paladin', 'Retribution', 'Ranged', 'Both', 'Libram of Three Truths'); + +-- ilvl 290 (Phase 5) +INSERT INTO `playerbots_bis_gear` VALUES +(2, 2, 0, 0, 290, 51277, 'Phase 5', 'Paladin', 'Retribution', 'Head', 'Both', 'Sanctified Lightsworn Helmet'), +(2, 2, 1, 0, 290, 54581, 'Phase 5', 'Paladin', 'Retribution', 'Neck', 'Both', 'Penumbra Pendant'), +(2, 2, 2, 0, 290, 51279, 'Phase 5', 'Paladin', 'Retribution', 'Shoulders', 'Both', 'Sanctified Lightsworn Shoulderplates'), +(2, 2, 4, 0, 290, 51275, 'Phase 5', 'Paladin', 'Retribution', 'Chest', 'Both', 'Sanctified Lightsworn Battleplate'), +(2, 2, 5, 0, 290, 50707, 'Phase 5', 'Paladin', 'Retribution', 'Waist', 'Both', 'Astrylian''s Sutured Cinch'), +(2, 2, 6, 0, 290, 51278, 'Phase 5', 'Paladin', 'Retribution', 'Legs', 'Both', 'Sanctified Lightsworn Legplates'), +(2, 2, 7, 0, 290, 54578, 'Phase 5', 'Paladin', 'Retribution', 'Feet', 'Both', 'Apocalypse''s Advance'), +(2, 2, 8, 0, 290, 54580, 'Phase 5', 'Paladin', 'Retribution', 'Wrists', 'Both', 'Umbrage Armbands'), +(2, 2, 9, 0, 290, 50690, 'Phase 5', 'Paladin', 'Retribution', 'Hands', 'Both', 'Fleshrending Gauntlets'), +(2, 2, 10, 0, 290, 50402, 'Phase 5', 'Paladin', 'Retribution', 'Finger1', 'Both', 'Ashen Band of Endless Vengeance'), +(2, 2, 11, 0, 290, 54576, 'Phase 5', 'Paladin', 'Retribution', 'Finger2', 'Both', 'Signet of Twilight'), +(2, 2, 12, 0, 290, 54590, 'Phase 5', 'Paladin', 'Retribution', 'Trinket1', 'Both', 'Sharpened Twilight Scale'), +(2, 2, 13, 0, 290, 50706, 'Phase 5', 'Paladin', 'Retribution', 'Trinket2', 'Both', 'Tiny Abomination in a Jar'), +(2, 2, 14, 0, 290, 50653, 'Phase 5', 'Paladin', 'Retribution', 'Back', 'Both', 'Shadowvault Slayer''s Cloak'), +(2, 2, 15, 0, 290, 49623, 'Phase 5', 'Paladin', 'Retribution', 'MainHand', 'Both', 'Shadowmourne'), +(2, 2, 17, 0, 290, 50455, 'Phase 5', 'Paladin', 'Retribution', 'Ranged', 'Both', 'Libram of Three Truths'); + + +-- ============================================================ +-- Hunter (3) +-- ============================================================ +-- Beast Mastery (tab 0) +-- ilvl 66 (Phase 1 (Pre-Raid)) +INSERT INTO `playerbots_bis_gear` VALUES +(3, 0, 0, 0, 66, 13404, 'Phase 1 (Pre-Raid)', 'Hunter', 'Beast Mastery', 'Head', 'Both', 'Mask of the Unforgiven'), +(3, 0, 1, 0, 66, 15411, 'Phase 1 (Pre-Raid)', 'Hunter', 'Beast Mastery', 'Neck', 'Both', 'Mark of Fordring'), +(3, 0, 2, 0, 66, 12927, 'Phase 1 (Pre-Raid)', 'Hunter', 'Beast Mastery', 'Shoulders', 'Both', 'Truestrike Shoulders'), +(3, 0, 4, 0, 66, 11726, 'Phase 1 (Pre-Raid)', 'Hunter', 'Beast Mastery', 'Chest', 'Both', 'Savage Gladiator Chain'), +(3, 0, 5, 0, 66, 14502, 'Phase 1 (Pre-Raid)', 'Hunter', 'Beast Mastery', 'Waist', 'Both', 'Frostbite Girdle'), +(3, 0, 6, 0, 66, 15062, 'Phase 1 (Pre-Raid)', 'Hunter', 'Beast Mastery', 'Legs', 'Both', 'Devilsaur Leggings'), +(3, 0, 7, 0, 66, 13967, 'Phase 1 (Pre-Raid)', 'Hunter', 'Beast Mastery', 'Feet', 'Both', 'Windreaver Greaves'), +(3, 0, 8, 0, 66, 13211, 'Phase 1 (Pre-Raid)', 'Hunter', 'Beast Mastery', 'Wrists', 'Both', 'Slashclaw Bracers'), +(3, 0, 9, 0, 66, 15063, 'Phase 1 (Pre-Raid)', 'Hunter', 'Beast Mastery', 'Hands', 'Both', 'Devilsaur Gauntlets'), +(3, 0, 10, 0, 66, 13098, 'Phase 1 (Pre-Raid)', 'Hunter', 'Beast Mastery', 'Finger1', 'Both', 'Painweaver Band'), +(3, 0, 11, 0, 66, 17713, 'Phase 1 (Pre-Raid)', 'Hunter', 'Beast Mastery', 'Finger2', 'Both', 'Blackstone Ring'), +(3, 0, 12, 0, 66, 13965, 'Phase 1 (Pre-Raid)', 'Hunter', 'Beast Mastery', 'Trinket1', 'Both', 'Blackhand''s Breadth'), +(3, 0, 13, 0, 66, 11815, 'Phase 1 (Pre-Raid)', 'Hunter', 'Beast Mastery', 'Trinket2', 'Both', 'Hand of Justice'), +(3, 0, 14, 0, 66, 13340, 'Phase 1 (Pre-Raid)', 'Hunter', 'Beast Mastery', 'Back', 'Both', 'Cape of the Black Baron'), +(3, 0, 15, 0, 66, 12940, 'Phase 1 (Pre-Raid)', 'Hunter', 'Beast Mastery', 'MainHand', 'Both', 'Dal''Rend''s Sacred Charge'), +(3, 0, 16, 0, 66, 12939, 'Phase 1 (Pre-Raid)', 'Hunter', 'Beast Mastery', 'OffHand', 'Both', 'Dal''Rend''s Tribal Guardian'), +(3, 0, 17, 0, 66, 18738, 'Phase 1 (Pre-Raid)', 'Hunter', 'Beast Mastery', 'Ranged', 'Both', 'Carapace Spine Crossbow'); + +-- ilvl 76 (Phase 2 (Pre-Raid)) +INSERT INTO `playerbots_bis_gear` VALUES +(3, 0, 0, 0, 76, 18421, 'Phase 2 (Pre-Raid)', 'Hunter', 'Beast Mastery', 'Head', 'Both', 'Backwood Helm'), +(3, 0, 1, 0, 76, 15411, 'Phase 2 (Pre-Raid)', 'Hunter', 'Beast Mastery', 'Neck', 'Both', 'Mark of Fordring'), +(3, 0, 2, 0, 76, 13358, 'Phase 2 (Pre-Raid)', 'Hunter', 'Beast Mastery', 'Shoulders', 'Both', 'Wyrmtongue Shoulders'), +(3, 0, 4, 0, 76, 11726, 'Phase 2 (Pre-Raid)', 'Hunter', 'Beast Mastery', 'Chest', 'Both', 'Savage Gladiator Chain'), +(3, 0, 5, 0, 76, 18393, 'Phase 2 (Pre-Raid)', 'Hunter', 'Beast Mastery', 'Waist', 'Both', 'Warpwood Binding'), +(3, 0, 6, 0, 76, 15062, 'Phase 2 (Pre-Raid)', 'Hunter', 'Beast Mastery', 'Legs', 'Both', 'Devilsaur Leggings'), +(3, 0, 7, 0, 76, 13967, 'Phase 2 (Pre-Raid)', 'Hunter', 'Beast Mastery', 'Feet', 'Both', 'Windreaver Greaves'), +(3, 0, 8, 0, 76, 13211, 'Phase 2 (Pre-Raid)', 'Hunter', 'Beast Mastery', 'Wrists', 'Both', 'Slashclaw Bracers'), +(3, 0, 9, 0, 76, 15063, 'Phase 2 (Pre-Raid)', 'Hunter', 'Beast Mastery', 'Hands', 'Both', 'Devilsaur Gauntlets'), +(3, 0, 10, 0, 76, 18500, 'Phase 2 (Pre-Raid)', 'Hunter', 'Beast Mastery', 'Finger1', 'Both', 'Tarnished Elven Ring'), +(3, 0, 11, 0, 76, 18500, 'Phase 2 (Pre-Raid)', 'Hunter', 'Beast Mastery', 'Finger2', 'Both', 'Tarnished Elven Ring'), +(3, 0, 12, 0, 76, 13965, 'Phase 2 (Pre-Raid)', 'Hunter', 'Beast Mastery', 'Trinket1', 'Both', 'Blackhand''s Breadth'), +(3, 0, 13, 0, 76, 18473, 'Phase 2 (Pre-Raid)', 'Hunter', 'Beast Mastery', 'Trinket2', 'Both', 'Royal Seal of Eldre''Thalas'), +(3, 0, 14, 0, 76, 13340, 'Phase 2 (Pre-Raid)', 'Hunter', 'Beast Mastery', 'Back', 'Both', 'Cape of the Black Baron'), +(3, 0, 15, 0, 76, 18520, 'Phase 2 (Pre-Raid)', 'Hunter', 'Beast Mastery', 'MainHand', 'Both', 'Barbarous Blade'), +(3, 0, 17, 0, 76, 18738, 'Phase 2 (Pre-Raid)', 'Hunter', 'Beast Mastery', 'Ranged', 'Both', 'Carapace Spine Crossbow'); + +-- ilvl 78 (Phase 2) +INSERT INTO `playerbots_bis_gear` VALUES +(3, 0, 0, 0, 78, 16846, 'Phase 2', 'Hunter', 'Beast Mastery', 'Head', 'Both', 'Giantstalker''s Helmet'), +(3, 0, 1, 0, 78, 18404, 'Phase 2', 'Hunter', 'Beast Mastery', 'Neck', 'Both', 'Onyxia Tooth Pendant'), +(3, 0, 2, 0, 78, 16848, 'Phase 2', 'Hunter', 'Beast Mastery', 'Shoulders', 'Both', 'Giantstalker''s Epaulets'), +(3, 0, 4, 0, 78, 16845, 'Phase 2', 'Hunter', 'Beast Mastery', 'Chest', 'Both', 'Giantstalker''s Breastplate'), +(3, 0, 5, 0, 78, 16851, 'Phase 2', 'Hunter', 'Beast Mastery', 'Waist', 'Both', 'Giantstalker''s Belt'), +(3, 0, 6, 0, 78, 16847, 'Phase 2', 'Hunter', 'Beast Mastery', 'Legs', 'Both', 'Giantstalker''s Leggings'), +(3, 0, 7, 0, 78, 16849, 'Phase 2', 'Hunter', 'Beast Mastery', 'Feet', 'Both', 'Giantstalker''s Boots'), +(3, 0, 8, 0, 78, 16850, 'Phase 2', 'Hunter', 'Beast Mastery', 'Wrists', 'Both', 'Giantstalker''s Bracers'), +(3, 0, 9, 0, 78, 16852, 'Phase 2', 'Hunter', 'Beast Mastery', 'Hands', 'Both', 'Giantstalker''s Gloves'), +(3, 0, 10, 0, 78, 17063, 'Phase 2', 'Hunter', 'Beast Mastery', 'Finger1', 'Both', 'Band of Accuria'), +(3, 0, 11, 0, 78, 18821, 'Phase 2', 'Hunter', 'Beast Mastery', 'Finger2', 'Both', 'Quick Strike Ring'), +(3, 0, 12, 0, 78, 13965, 'Phase 2', 'Hunter', 'Beast Mastery', 'Trinket1', 'Both', 'Blackhand''s Breadth'), +(3, 0, 13, 0, 78, 18473, 'Phase 2', 'Hunter', 'Beast Mastery', 'Trinket2', 'Both', 'Royal Seal of Eldre''Thalas'), +(3, 0, 14, 0, 78, 17102, 'Phase 2', 'Hunter', 'Beast Mastery', 'Back', 'Both', 'Cloak of the Shrouded Mists'), +(3, 0, 15, 0, 78, 18832, 'Phase 2', 'Hunter', 'Beast Mastery', 'MainHand', 'Both', 'Brutality Blade'), +(3, 0, 16, 0, 78, 18805, 'Phase 2', 'Hunter', 'Beast Mastery', 'OffHand', 'Both', 'Core Hound Tooth'), +(3, 0, 17, 0, 78, 18713, 'Phase 2', 'Hunter', 'Beast Mastery', 'Ranged', 'Both', 'Rhok''delar, Longbow of the Ancient Keepers'); + +-- ilvl 83 (Phase 3) +INSERT INTO `playerbots_bis_gear` VALUES +(3, 0, 0, 0, 83, 16939, 'Phase 3', 'Hunter', 'Beast Mastery', 'Head', 'Both', 'Dragonstalker''s Helm'), +(3, 0, 1, 0, 83, 19377, 'Phase 3', 'Hunter', 'Beast Mastery', 'Neck', 'Both', 'Prestor''s Talisman of Connivery'), +(3, 0, 2, 0, 83, 16937, 'Phase 3', 'Hunter', 'Beast Mastery', 'Shoulders', 'Both', 'Dragonstalker''s Spaulders'), +(3, 0, 4, 0, 83, 16942, 'Phase 3', 'Hunter', 'Beast Mastery', 'Chest', 'Both', 'Dragonstalker''s Breastplate'), +(3, 0, 5, 0, 83, 16936, 'Phase 3', 'Hunter', 'Beast Mastery', 'Waist', 'Both', 'Dragonstalker''s Belt'), +(3, 0, 6, 0, 83, 16938, 'Phase 3', 'Hunter', 'Beast Mastery', 'Legs', 'Both', 'Dragonstalker''s Legguards'), +(3, 0, 7, 0, 83, 16941, 'Phase 3', 'Hunter', 'Beast Mastery', 'Feet', 'Both', 'Dragonstalker''s Greaves'), +(3, 0, 8, 0, 83, 16935, 'Phase 3', 'Hunter', 'Beast Mastery', 'Wrists', 'Both', 'Dragonstalker''s Bracers'), +(3, 0, 9, 0, 83, 16940, 'Phase 3', 'Hunter', 'Beast Mastery', 'Hands', 'Both', 'Dragonstalker''s Gauntlets'), +(3, 0, 10, 0, 83, 19325, 'Phase 3', 'Hunter', 'Beast Mastery', 'Finger1', 'Both', 'Don Julio''s Band'), +(3, 0, 11, 0, 83, 18821, 'Phase 3', 'Hunter', 'Beast Mastery', 'Finger2', 'Both', 'Quick Strike Ring'), +(3, 0, 12, 0, 83, 13965, 'Phase 3', 'Hunter', 'Beast Mastery', 'Trinket1', 'Both', 'Blackhand''s Breadth'), +(3, 0, 13, 0, 83, 19406, 'Phase 3', 'Hunter', 'Beast Mastery', 'Trinket2', 'Both', 'Drake Fang Talisman'), +(3, 0, 14, 0, 83, 17102, 'Phase 3', 'Hunter', 'Beast Mastery', 'Back', 'Both', 'Cloak of the Shrouded Mists'), +(3, 0, 15, 0, 83, 18832, 'Phase 3', 'Hunter', 'Beast Mastery', 'MainHand', 'Both', 'Brutality Blade'), +(3, 0, 16, 0, 83, 18805, 'Phase 3', 'Hunter', 'Beast Mastery', 'OffHand', 'Both', 'Core Hound Tooth'), +(3, 0, 17, 0, 83, 19361, 'Phase 3', 'Hunter', 'Beast Mastery', 'Ranged', 'Both', 'Ashjre''thul, Crossbow of Smiting'); + +-- ilvl 88 (Phase 5) +INSERT INTO `playerbots_bis_gear` VALUES +(3, 0, 0, 0, 88, 16939, 'Phase 5', 'Hunter', 'Beast Mastery', 'Head', 'Both', 'Dragonstalker''s Helm'), +(3, 0, 1, 0, 88, 19377, 'Phase 5', 'Hunter', 'Beast Mastery', 'Neck', 'Both', 'Prestor''s Talisman of Connivery'), +(3, 0, 2, 0, 88, 16937, 'Phase 5', 'Hunter', 'Beast Mastery', 'Shoulders', 'Both', 'Dragonstalker''s Spaulders'), +(3, 0, 4, 0, 88, 16942, 'Phase 5', 'Hunter', 'Beast Mastery', 'Chest', 'Both', 'Dragonstalker''s Breastplate'), +(3, 0, 5, 0, 88, 16936, 'Phase 5', 'Hunter', 'Beast Mastery', 'Waist', 'Both', 'Dragonstalker''s Belt'), +(3, 0, 6, 0, 88, 16938, 'Phase 5', 'Hunter', 'Beast Mastery', 'Legs', 'Both', 'Dragonstalker''s Legguards'), +(3, 0, 7, 0, 88, 16941, 'Phase 5', 'Hunter', 'Beast Mastery', 'Feet', 'Both', 'Dragonstalker''s Greaves'), +(3, 0, 8, 0, 88, 16935, 'Phase 5', 'Hunter', 'Beast Mastery', 'Wrists', 'Both', 'Dragonstalker''s Bracers'), +(3, 0, 9, 0, 88, 16940, 'Phase 5', 'Hunter', 'Beast Mastery', 'Hands', 'Both', 'Dragonstalker''s Gauntlets'), +(3, 0, 10, 0, 88, 19325, 'Phase 5', 'Hunter', 'Beast Mastery', 'Finger1', 'Both', 'Don Julio''s Band'), +(3, 0, 11, 0, 88, 17063, 'Phase 5', 'Hunter', 'Beast Mastery', 'Finger2', 'Both', 'Band of Accuria'), +(3, 0, 12, 0, 88, 21670, 'Phase 5', 'Hunter', 'Beast Mastery', 'Trinket1', 'Both', 'Badge of the Swarmguard'), +(3, 0, 13, 0, 88, 23570, 'Phase 5', 'Hunter', 'Beast Mastery', 'Trinket2', 'Both', 'Jom Gabbar'), +(3, 0, 14, 0, 88, 21710, 'Phase 5', 'Hunter', 'Beast Mastery', 'Back', 'Both', 'Cloak of the Fallen God'), +(3, 0, 15, 0, 88, 21673, 'Phase 5', 'Hunter', 'Beast Mastery', 'MainHand', 'Both', 'Silithid Claw'), +(3, 0, 16, 0, 88, 19859, 'Phase 5', 'Hunter', 'Beast Mastery', 'OffHand', 'Both', 'Fang of the Faceless'), +(3, 0, 17, 0, 88, 19361, 'Phase 5', 'Hunter', 'Beast Mastery', 'Ranged', 'Both', 'Ashjre''thul, Crossbow of Smiting'); + +-- ilvl 92 (Phase 6) +INSERT INTO `playerbots_bis_gear` VALUES +(3, 0, 0, 0, 92, 22438, 'Phase 6', 'Hunter', 'Beast Mastery', 'Head', 'Both', 'Cryptstalker Headpiece'), +(3, 0, 1, 0, 92, 23053, 'Phase 6', 'Hunter', 'Beast Mastery', 'Neck', 'Both', 'Stormrage''s Talisman of Seething'), +(3, 0, 2, 0, 92, 22439, 'Phase 6', 'Hunter', 'Beast Mastery', 'Shoulders', 'Both', 'Cryptstalker Spaulders'), +(3, 0, 4, 0, 92, 22436, 'Phase 6', 'Hunter', 'Beast Mastery', 'Chest', 'Both', 'Cryptstalker Tunic'), +(3, 0, 5, 0, 92, 22442, 'Phase 6', 'Hunter', 'Beast Mastery', 'Waist', 'Both', 'Cryptstalker Girdle'), +(3, 0, 6, 0, 92, 22437, 'Phase 6', 'Hunter', 'Beast Mastery', 'Legs', 'Both', 'Cryptstalker Legguards'), +(3, 0, 7, 0, 92, 22440, 'Phase 6', 'Hunter', 'Beast Mastery', 'Feet', 'Both', 'Cryptstalker Boots'), +(3, 0, 8, 0, 92, 22443, 'Phase 6', 'Hunter', 'Beast Mastery', 'Wrists', 'Both', 'Cryptstalker Wristguards'), +(3, 0, 9, 0, 92, 16571, 'Phase 6', 'Hunter', 'Beast Mastery', 'Hands', 'Both', 'General''s Chain Gloves'), +(3, 0, 10, 0, 92, 23067, 'Phase 6', 'Hunter', 'Beast Mastery', 'Finger1', 'Both', 'Ring of the Cryptstalker'), +(3, 0, 11, 0, 92, 22961, 'Phase 6', 'Hunter', 'Beast Mastery', 'Finger2', 'Both', 'Band of Reanimation'), +(3, 0, 12, 0, 92, 21670, 'Phase 6', 'Hunter', 'Beast Mastery', 'Trinket1', 'Both', 'Badge of the Swarmguard'), +(3, 0, 13, 0, 92, 23041, 'Phase 6', 'Hunter', 'Beast Mastery', 'Trinket2', 'Both', 'Slayer''s Crest'), +(3, 0, 14, 0, 92, 23045, 'Phase 6', 'Hunter', 'Beast Mastery', 'Back', 'Both', 'Shroud of Dominion'), +(3, 0, 15, 0, 92, 22816, 'Phase 6', 'Hunter', 'Beast Mastery', 'MainHand', 'Both', 'Hatchet of Sundered Bone'), +(3, 0, 16, 0, 92, 22802, 'Phase 6', 'Hunter', 'Beast Mastery', 'OffHand', 'Both', 'Kingsfall'), +(3, 0, 17, 0, 92, 22812, 'Phase 6', 'Hunter', 'Beast Mastery', 'Ranged', 'Both', 'Nerubian Slavemaker'); + +-- ilvl 200 (Pre-Raid) +INSERT INTO `playerbots_bis_gear` VALUES +(3, 0, 0, 0, 200, 37188, 'Pre-Raid', 'Hunter', 'Beast Mastery', 'Head', 'Both', 'Plunderer''s Helmet'), +(3, 0, 1, 0, 200, 40678, 'Pre-Raid', 'Hunter', 'Beast Mastery', 'Neck', 'Both', 'Pendant of the Outcast Hero'), +(3, 0, 2, 0, 200, 37679, 'Pre-Raid', 'Hunter', 'Beast Mastery', 'Shoulders', 'Both', 'Spaulders of the Abomination'), +(3, 0, 4, 0, 200, 39579, 'Pre-Raid', 'Hunter', 'Beast Mastery', 'Chest', 'Both', 'Heroes'' Cryptstalker Tunic'), +(3, 0, 6, 0, 200, 37669, 'Pre-Raid', 'Hunter', 'Beast Mastery', 'Legs', 'Both', 'Leggings of the Stone Halls'), +(3, 0, 7, 0, 200, 44297, 'Pre-Raid', 'Hunter', 'Beast Mastery', 'Feet', 'Both', 'Boots of the Neverending Path'), +(3, 0, 8, 0, 200, 41224, 'Pre-Raid', 'Hunter', 'Beast Mastery', 'Wrists', 'Both', 'Deadly Gladiator''s Wristguards of Triumph'), +(3, 0, 9, 0, 200, 43734, 'Pre-Raid', 'Hunter', 'Beast Mastery', 'Hands', 'Both', 'Heroes'' Cryptstalker Handguards'), +(3, 0, 10, 0, 200, 40586, 'Pre-Raid', 'Hunter', 'Beast Mastery', 'Finger1', 'Both', 'Band of the Kirin Tor'), +(3, 0, 12, 0, 200, 44253, 'Pre-Raid', 'Hunter', 'Beast Mastery', 'Trinket1', 'Both', 'Darkmoon Card: Greatness'), +(3, 0, 14, 0, 200, 42068, 'Pre-Raid', 'Hunter', 'Beast Mastery', 'Back', 'Both', 'Deadly Gladiator''s Cloak of Victory'), +(3, 0, 15, 0, 200, 44249, 'Pre-Raid', 'Hunter', 'Beast Mastery', 'MainHand', 'Both', 'Runeblade of Demonstrable Power'), +(3, 0, 17, 0, 200, 44504, 'Pre-Raid', 'Hunter', 'Beast Mastery', 'Ranged', 'Both', 'Nesingwary 4000'); + +-- ilvl 224 (Phase 1) +INSERT INTO `playerbots_bis_gear` VALUES +(3, 0, 0, 0, 224, 40505, 'Phase 1', 'Hunter', 'Beast Mastery', 'Head', 'Both', 'Valorous Cryptstalker Headpiece'), +(3, 0, 1, 0, 224, 44664, 'Phase 1', 'Hunter', 'Beast Mastery', 'Neck', 'Both', 'Favor of the Dragon Queen'), +(3, 0, 2, 0, 224, 40507, 'Phase 1', 'Hunter', 'Beast Mastery', 'Shoulders', 'Both', 'Valorous Cryptstalker Spaulders'), +(3, 0, 4, 0, 224, 43998, 'Phase 1', 'Hunter', 'Beast Mastery', 'Chest', 'Both', 'Chestguard of Flagrant Prowess'), +(3, 0, 5, 0, 224, 39762, 'Phase 1', 'Hunter', 'Beast Mastery', 'Waist', 'Both', 'Torn Web Wrapping'), +(3, 0, 6, 0, 224, 40331, 'Phase 1', 'Hunter', 'Beast Mastery', 'Legs', 'Both', 'Leggings of Failed Escape'), +(3, 0, 7, 0, 224, 40549, 'Phase 1', 'Hunter', 'Beast Mastery', 'Feet', 'Both', 'Boots of the Renewed Flight'), +(3, 0, 8, 0, 224, 40282, 'Phase 1', 'Hunter', 'Beast Mastery', 'Wrists', 'Both', 'Slime Stream Bands'), +(3, 0, 9, 0, 224, 40541, 'Phase 1', 'Hunter', 'Beast Mastery', 'Hands', 'Both', 'Frosted Adroit Handguards'), +(3, 0, 10, 0, 224, 40474, 'Phase 1', 'Hunter', 'Beast Mastery', 'Finger1', 'Both', 'Surge Needle Ring'), +(3, 0, 12, 0, 224, 40431, 'Phase 1', 'Hunter', 'Beast Mastery', 'Trinket1', 'Both', 'Fury of the Five Flights'), +(3, 0, 14, 0, 224, 40403, 'Phase 1', 'Hunter', 'Beast Mastery', 'Back', 'Both', 'Drape of the Deadly Foe'), +(3, 0, 17, 0, 224, 40385, 'Phase 1', 'Hunter', 'Beast Mastery', 'Ranged', 'Both', 'Envoy of Mortality'); + +-- ilvl 245 (Phase 2) +INSERT INTO `playerbots_bis_gear` VALUES +(3, 0, 0, 0, 245, 45610, 'Phase 2', 'Hunter', 'Beast Mastery', 'Head', 'Both', 'Boundless Gaze'), +(3, 0, 1, 0, 245, 45517, 'Phase 2', 'Hunter', 'Beast Mastery', 'Neck', 'Both', 'Pendulum of Infinity'), +(3, 0, 2, 0, 245, 45300, 'Phase 2', 'Hunter', 'Beast Mastery', 'Shoulders', 'Both', 'Mantle of Fiery Vengeance'), +(3, 0, 4, 0, 245, 45473, 'Phase 2', 'Hunter', 'Beast Mastery', 'Chest', 'Both', 'Embrace of the Gladiator'), +(3, 0, 5, 0, 245, 45553, 'Phase 2', 'Hunter', 'Beast Mastery', 'Waist', 'Both', 'Belt of Dragons'), +(3, 0, 6, 0, 245, 45536, 'Phase 2', 'Hunter', 'Beast Mastery', 'Legs', 'Both', 'Legguards of Cunning Deception'), +(3, 0, 7, 0, 245, 45989, 'Phase 2', 'Hunter', 'Beast Mastery', 'Feet', 'Both', 'Tempered Mercury Greaves'), +(3, 0, 8, 0, 245, 45869, 'Phase 2', 'Hunter', 'Beast Mastery', 'Wrists', 'Both', 'Fluxing Energy Coils'), +(3, 0, 9, 0, 245, 45444, 'Phase 2', 'Hunter', 'Beast Mastery', 'Hands', 'Both', 'Gloves of the Steady Hand'), +(3, 0, 12, 0, 245, 46038, 'Phase 2', 'Hunter', 'Beast Mastery', 'Trinket1', 'Both', 'Dark Matter'), +(3, 0, 14, 0, 245, 46032, 'Phase 2', 'Hunter', 'Beast Mastery', 'Back', 'Both', 'Drape of the Faceless General'); + +-- ilvl 258 (Phase 3) +INSERT INTO `playerbots_bis_gear` VALUES +(3, 0, 0, 0, 258, 48262, 'Phase 3', 'Hunter', 'Beast Mastery', 'Head', 'Both', 'Windrunner''s Headpiece of Triumph'), +(3, 0, 1, 1, 258, 47060, 'Phase 3', 'Hunter', 'Beast Mastery', 'Neck', 'Alliance', 'Charge of the Demon Lord'), +(3, 0, 1, 2, 258, 47433, 'Phase 3', 'Hunter', 'Beast Mastery', 'Neck', 'Horde', 'Charge of the Eredar'), +(3, 0, 2, 0, 258, 48260, 'Phase 3', 'Hunter', 'Beast Mastery', 'Shoulders', 'Both', 'Windrunner''s Spaulders of Triumph'), +(3, 0, 4, 0, 258, 48264, 'Phase 3', 'Hunter', 'Beast Mastery', 'Chest', 'Both', 'Windrunner''s Tunic of Triumph'), +(3, 0, 5, 1, 258, 47153, 'Phase 3', 'Hunter', 'Beast Mastery', 'Waist', 'Alliance', 'Belt of Deathly Dominion'), +(3, 0, 5, 2, 258, 47472, 'Phase 3', 'Hunter', 'Beast Mastery', 'Waist', 'Horde', 'Waistguard of Deathly Dominion'), +(3, 0, 6, 1, 258, 47191, 'Phase 3', 'Hunter', 'Beast Mastery', 'Legs', 'Alliance', 'Legguards of the Lurking Threat'), +(3, 0, 6, 2, 258, 47480, 'Phase 3', 'Hunter', 'Beast Mastery', 'Legs', 'Horde', 'Leggings of the Lurking Threat'), +(3, 0, 7, 1, 258, 47109, 'Phase 3', 'Hunter', 'Beast Mastery', 'Feet', 'Alliance', 'Sabatons of Ruthless Judgment'), +(3, 0, 7, 2, 258, 47457, 'Phase 3', 'Hunter', 'Beast Mastery', 'Feet', 'Horde', 'Greaves of Ruthless Judgment'), +(3, 0, 8, 1, 258, 47074, 'Phase 3', 'Hunter', 'Beast Mastery', 'Wrists', 'Alliance', 'Bracers of the Untold Massacre'), +(3, 0, 8, 2, 258, 47442, 'Phase 3', 'Hunter', 'Beast Mastery', 'Wrists', 'Horde', 'Bracers of the Silent Massacre'), +(3, 0, 9, 0, 258, 48263, 'Phase 3', 'Hunter', 'Beast Mastery', 'Hands', 'Both', 'Windrunner''s Handguards of Triumph'), +(3, 0, 10, 1, 258, 47075, 'Phase 3', 'Hunter', 'Beast Mastery', 'Finger1', 'Alliance', 'Ring of Callous Aggression'), +(3, 0, 10, 2, 258, 47443, 'Phase 3', 'Hunter', 'Beast Mastery', 'Finger1', 'Horde', 'Band of Callous Aggression'), +(3, 0, 12, 2, 258, 45931, 'Phase 3', 'Hunter', 'Beast Mastery', 'Trinket1', 'Horde', 'Mjolnir Runestone'), +(3, 0, 14, 1, 258, 47545, 'Phase 3', 'Hunter', 'Beast Mastery', 'Back', 'Alliance', 'Vereesa''s Dexterity'), +(3, 0, 14, 2, 258, 47546, 'Phase 3', 'Hunter', 'Beast Mastery', 'Back', 'Horde', 'Sylvanas'' Cunning'), +(3, 0, 17, 1, 258, 47521, 'Phase 3', 'Hunter', 'Beast Mastery', 'Ranged', 'Alliance', 'BRK 1000'), +(3, 0, 17, 2, 258, 47523, 'Phase 3', 'Hunter', 'Beast Mastery', 'Ranged', 'Horde', 'Fezzik''s Autocannon'); + +-- ilvl 264 (Phase 4) +INSERT INTO `playerbots_bis_gear` VALUES +(3, 0, 0, 0, 264, 51286, 'Phase 4', 'Hunter', 'Beast Mastery', 'Head', 'Both', 'Sanctified Ahn''Kahar Blood Hunter''s Headpiece'), +(3, 0, 1, 0, 264, 50633, 'Phase 4', 'Hunter', 'Beast Mastery', 'Neck', 'Both', 'Sindragosa''s Cruel Claw'), +(3, 0, 2, 0, 264, 51288, 'Phase 4', 'Hunter', 'Beast Mastery', 'Shoulders', 'Both', 'Sanctified Ahn''Kahar Blood Hunter''s Spaulders'), +(3, 0, 4, 0, 264, 51289, 'Phase 4', 'Hunter', 'Beast Mastery', 'Chest', 'Both', 'Sanctified Ahn''Kahar Blood Hunter''s Tunic'), +(3, 0, 5, 0, 264, 50688, 'Phase 4', 'Hunter', 'Beast Mastery', 'Waist', 'Both', 'Nerub''ar Stalker''s Cord'), +(3, 0, 6, 0, 264, 50645, 'Phase 4', 'Hunter', 'Beast Mastery', 'Legs', 'Both', 'Leggings of Northern Lights'), +(3, 0, 7, 0, 264, 50607, 'Phase 4', 'Hunter', 'Beast Mastery', 'Feet', 'Both', 'Frostbitten Fur Boots'), +(3, 0, 8, 0, 264, 50655, 'Phase 4', 'Hunter', 'Beast Mastery', 'Wrists', 'Both', 'Scourge Hunter''s Vambraces'), +(3, 0, 9, 0, 264, 51285, 'Phase 4', 'Hunter', 'Beast Mastery', 'Hands', 'Both', 'Sanctified Ahn''Kahar Blood Hunter''s Handguards'), +(3, 0, 10, 0, 264, 50402, 'Phase 4', 'Hunter', 'Beast Mastery', 'Finger1', 'Both', 'Ashen Band of Endless Vengeance'), +(3, 0, 12, 0, 264, 50363, 'Phase 4', 'Hunter', 'Beast Mastery', 'Trinket1', 'Both', 'Deathbringer''s Will'), +(3, 0, 14, 1, 264, 47545, 'Phase 4', 'Hunter', 'Beast Mastery', 'Back', 'Alliance', 'Vereesa''s Dexterity'), +(3, 0, 14, 2, 264, 47546, 'Phase 4', 'Hunter', 'Beast Mastery', 'Back', 'Horde', 'Sylvanas'' Cunning'), +(3, 0, 17, 0, 264, 50733, 'Phase 4', 'Hunter', 'Beast Mastery', 'Ranged', 'Both', 'Fal''inrush, Defender of Quel''thalas'); + +-- Marksmanship (tab 1) +-- ilvl 66 (Phase 1 (Pre-Raid)) +INSERT INTO `playerbots_bis_gear` VALUES +(3, 1, 0, 0, 66, 13404, 'Phase 1 (Pre-Raid)', 'Hunter', 'Marksmanship', 'Head', 'Both', 'Mask of the Unforgiven'), +(3, 1, 1, 0, 66, 15411, 'Phase 1 (Pre-Raid)', 'Hunter', 'Marksmanship', 'Neck', 'Both', 'Mark of Fordring'), +(3, 1, 2, 0, 66, 12927, 'Phase 1 (Pre-Raid)', 'Hunter', 'Marksmanship', 'Shoulders', 'Both', 'Truestrike Shoulders'), +(3, 1, 4, 0, 66, 11726, 'Phase 1 (Pre-Raid)', 'Hunter', 'Marksmanship', 'Chest', 'Both', 'Savage Gladiator Chain'), +(3, 1, 5, 0, 66, 14502, 'Phase 1 (Pre-Raid)', 'Hunter', 'Marksmanship', 'Waist', 'Both', 'Frostbite Girdle'), +(3, 1, 6, 0, 66, 15062, 'Phase 1 (Pre-Raid)', 'Hunter', 'Marksmanship', 'Legs', 'Both', 'Devilsaur Leggings'), +(3, 1, 7, 0, 66, 13967, 'Phase 1 (Pre-Raid)', 'Hunter', 'Marksmanship', 'Feet', 'Both', 'Windreaver Greaves'), +(3, 1, 8, 0, 66, 13211, 'Phase 1 (Pre-Raid)', 'Hunter', 'Marksmanship', 'Wrists', 'Both', 'Slashclaw Bracers'), +(3, 1, 9, 0, 66, 15063, 'Phase 1 (Pre-Raid)', 'Hunter', 'Marksmanship', 'Hands', 'Both', 'Devilsaur Gauntlets'), +(3, 1, 10, 0, 66, 13098, 'Phase 1 (Pre-Raid)', 'Hunter', 'Marksmanship', 'Finger1', 'Both', 'Painweaver Band'), +(3, 1, 11, 0, 66, 17713, 'Phase 1 (Pre-Raid)', 'Hunter', 'Marksmanship', 'Finger2', 'Both', 'Blackstone Ring'), +(3, 1, 12, 0, 66, 13965, 'Phase 1 (Pre-Raid)', 'Hunter', 'Marksmanship', 'Trinket1', 'Both', 'Blackhand''s Breadth'), +(3, 1, 13, 0, 66, 11815, 'Phase 1 (Pre-Raid)', 'Hunter', 'Marksmanship', 'Trinket2', 'Both', 'Hand of Justice'), +(3, 1, 14, 0, 66, 13340, 'Phase 1 (Pre-Raid)', 'Hunter', 'Marksmanship', 'Back', 'Both', 'Cape of the Black Baron'), +(3, 1, 15, 0, 66, 12940, 'Phase 1 (Pre-Raid)', 'Hunter', 'Marksmanship', 'MainHand', 'Both', 'Dal''Rend''s Sacred Charge'), +(3, 1, 16, 0, 66, 12939, 'Phase 1 (Pre-Raid)', 'Hunter', 'Marksmanship', 'OffHand', 'Both', 'Dal''Rend''s Tribal Guardian'), +(3, 1, 17, 0, 66, 18738, 'Phase 1 (Pre-Raid)', 'Hunter', 'Marksmanship', 'Ranged', 'Both', 'Carapace Spine Crossbow'); + +-- ilvl 76 (Phase 2 (Pre-Raid)) +INSERT INTO `playerbots_bis_gear` VALUES +(3, 1, 0, 0, 76, 18421, 'Phase 2 (Pre-Raid)', 'Hunter', 'Marksmanship', 'Head', 'Both', 'Backwood Helm'), +(3, 1, 1, 0, 76, 15411, 'Phase 2 (Pre-Raid)', 'Hunter', 'Marksmanship', 'Neck', 'Both', 'Mark of Fordring'), +(3, 1, 2, 0, 76, 13358, 'Phase 2 (Pre-Raid)', 'Hunter', 'Marksmanship', 'Shoulders', 'Both', 'Wyrmtongue Shoulders'), +(3, 1, 4, 0, 76, 11726, 'Phase 2 (Pre-Raid)', 'Hunter', 'Marksmanship', 'Chest', 'Both', 'Savage Gladiator Chain'), +(3, 1, 5, 0, 76, 18393, 'Phase 2 (Pre-Raid)', 'Hunter', 'Marksmanship', 'Waist', 'Both', 'Warpwood Binding'), +(3, 1, 6, 0, 76, 15062, 'Phase 2 (Pre-Raid)', 'Hunter', 'Marksmanship', 'Legs', 'Both', 'Devilsaur Leggings'), +(3, 1, 7, 0, 76, 13967, 'Phase 2 (Pre-Raid)', 'Hunter', 'Marksmanship', 'Feet', 'Both', 'Windreaver Greaves'), +(3, 1, 8, 0, 76, 13211, 'Phase 2 (Pre-Raid)', 'Hunter', 'Marksmanship', 'Wrists', 'Both', 'Slashclaw Bracers'), +(3, 1, 9, 0, 76, 15063, 'Phase 2 (Pre-Raid)', 'Hunter', 'Marksmanship', 'Hands', 'Both', 'Devilsaur Gauntlets'), +(3, 1, 10, 0, 76, 18500, 'Phase 2 (Pre-Raid)', 'Hunter', 'Marksmanship', 'Finger1', 'Both', 'Tarnished Elven Ring'), +(3, 1, 11, 0, 76, 18500, 'Phase 2 (Pre-Raid)', 'Hunter', 'Marksmanship', 'Finger2', 'Both', 'Tarnished Elven Ring'), +(3, 1, 12, 0, 76, 13965, 'Phase 2 (Pre-Raid)', 'Hunter', 'Marksmanship', 'Trinket1', 'Both', 'Blackhand''s Breadth'), +(3, 1, 13, 0, 76, 18473, 'Phase 2 (Pre-Raid)', 'Hunter', 'Marksmanship', 'Trinket2', 'Both', 'Royal Seal of Eldre''Thalas'), +(3, 1, 14, 0, 76, 13340, 'Phase 2 (Pre-Raid)', 'Hunter', 'Marksmanship', 'Back', 'Both', 'Cape of the Black Baron'), +(3, 1, 15, 0, 76, 18520, 'Phase 2 (Pre-Raid)', 'Hunter', 'Marksmanship', 'MainHand', 'Both', 'Barbarous Blade'), +(3, 1, 17, 0, 76, 18738, 'Phase 2 (Pre-Raid)', 'Hunter', 'Marksmanship', 'Ranged', 'Both', 'Carapace Spine Crossbow'); + +-- ilvl 78 (Phase 2) +INSERT INTO `playerbots_bis_gear` VALUES +(3, 1, 0, 0, 78, 16846, 'Phase 2', 'Hunter', 'Marksmanship', 'Head', 'Both', 'Giantstalker''s Helmet'), +(3, 1, 1, 0, 78, 18404, 'Phase 2', 'Hunter', 'Marksmanship', 'Neck', 'Both', 'Onyxia Tooth Pendant'), +(3, 1, 2, 0, 78, 16848, 'Phase 2', 'Hunter', 'Marksmanship', 'Shoulders', 'Both', 'Giantstalker''s Epaulets'), +(3, 1, 4, 0, 78, 16845, 'Phase 2', 'Hunter', 'Marksmanship', 'Chest', 'Both', 'Giantstalker''s Breastplate'), +(3, 1, 5, 0, 78, 16851, 'Phase 2', 'Hunter', 'Marksmanship', 'Waist', 'Both', 'Giantstalker''s Belt'), +(3, 1, 6, 0, 78, 16847, 'Phase 2', 'Hunter', 'Marksmanship', 'Legs', 'Both', 'Giantstalker''s Leggings'), +(3, 1, 7, 0, 78, 16849, 'Phase 2', 'Hunter', 'Marksmanship', 'Feet', 'Both', 'Giantstalker''s Boots'), +(3, 1, 8, 0, 78, 16850, 'Phase 2', 'Hunter', 'Marksmanship', 'Wrists', 'Both', 'Giantstalker''s Bracers'), +(3, 1, 9, 0, 78, 16852, 'Phase 2', 'Hunter', 'Marksmanship', 'Hands', 'Both', 'Giantstalker''s Gloves'), +(3, 1, 10, 0, 78, 17063, 'Phase 2', 'Hunter', 'Marksmanship', 'Finger1', 'Both', 'Band of Accuria'), +(3, 1, 11, 0, 78, 18821, 'Phase 2', 'Hunter', 'Marksmanship', 'Finger2', 'Both', 'Quick Strike Ring'), +(3, 1, 12, 0, 78, 13965, 'Phase 2', 'Hunter', 'Marksmanship', 'Trinket1', 'Both', 'Blackhand''s Breadth'), +(3, 1, 13, 0, 78, 18473, 'Phase 2', 'Hunter', 'Marksmanship', 'Trinket2', 'Both', 'Royal Seal of Eldre''Thalas'), +(3, 1, 14, 0, 78, 17102, 'Phase 2', 'Hunter', 'Marksmanship', 'Back', 'Both', 'Cloak of the Shrouded Mists'), +(3, 1, 15, 0, 78, 18832, 'Phase 2', 'Hunter', 'Marksmanship', 'MainHand', 'Both', 'Brutality Blade'), +(3, 1, 16, 0, 78, 18805, 'Phase 2', 'Hunter', 'Marksmanship', 'OffHand', 'Both', 'Core Hound Tooth'), +(3, 1, 17, 0, 78, 18713, 'Phase 2', 'Hunter', 'Marksmanship', 'Ranged', 'Both', 'Rhok''delar, Longbow of the Ancient Keepers'); + +-- ilvl 83 (Phase 3) +INSERT INTO `playerbots_bis_gear` VALUES +(3, 1, 0, 0, 83, 16939, 'Phase 3', 'Hunter', 'Marksmanship', 'Head', 'Both', 'Dragonstalker''s Helm'), +(3, 1, 1, 0, 83, 19377, 'Phase 3', 'Hunter', 'Marksmanship', 'Neck', 'Both', 'Prestor''s Talisman of Connivery'), +(3, 1, 2, 0, 83, 16937, 'Phase 3', 'Hunter', 'Marksmanship', 'Shoulders', 'Both', 'Dragonstalker''s Spaulders'), +(3, 1, 4, 0, 83, 16942, 'Phase 3', 'Hunter', 'Marksmanship', 'Chest', 'Both', 'Dragonstalker''s Breastplate'), +(3, 1, 5, 0, 83, 16936, 'Phase 3', 'Hunter', 'Marksmanship', 'Waist', 'Both', 'Dragonstalker''s Belt'), +(3, 1, 6, 0, 83, 16938, 'Phase 3', 'Hunter', 'Marksmanship', 'Legs', 'Both', 'Dragonstalker''s Legguards'), +(3, 1, 7, 0, 83, 16941, 'Phase 3', 'Hunter', 'Marksmanship', 'Feet', 'Both', 'Dragonstalker''s Greaves'), +(3, 1, 8, 0, 83, 16935, 'Phase 3', 'Hunter', 'Marksmanship', 'Wrists', 'Both', 'Dragonstalker''s Bracers'), +(3, 1, 9, 0, 83, 16940, 'Phase 3', 'Hunter', 'Marksmanship', 'Hands', 'Both', 'Dragonstalker''s Gauntlets'), +(3, 1, 10, 0, 83, 19325, 'Phase 3', 'Hunter', 'Marksmanship', 'Finger1', 'Both', 'Don Julio''s Band'), +(3, 1, 11, 0, 83, 18821, 'Phase 3', 'Hunter', 'Marksmanship', 'Finger2', 'Both', 'Quick Strike Ring'), +(3, 1, 12, 0, 83, 13965, 'Phase 3', 'Hunter', 'Marksmanship', 'Trinket1', 'Both', 'Blackhand''s Breadth'), +(3, 1, 13, 0, 83, 19406, 'Phase 3', 'Hunter', 'Marksmanship', 'Trinket2', 'Both', 'Drake Fang Talisman'), +(3, 1, 14, 0, 83, 17102, 'Phase 3', 'Hunter', 'Marksmanship', 'Back', 'Both', 'Cloak of the Shrouded Mists'), +(3, 1, 15, 0, 83, 18832, 'Phase 3', 'Hunter', 'Marksmanship', 'MainHand', 'Both', 'Brutality Blade'), +(3, 1, 16, 0, 83, 18805, 'Phase 3', 'Hunter', 'Marksmanship', 'OffHand', 'Both', 'Core Hound Tooth'), +(3, 1, 17, 0, 83, 19361, 'Phase 3', 'Hunter', 'Marksmanship', 'Ranged', 'Both', 'Ashjre''thul, Crossbow of Smiting'); + +-- ilvl 88 (Phase 5) +INSERT INTO `playerbots_bis_gear` VALUES +(3, 1, 0, 0, 88, 16939, 'Phase 5', 'Hunter', 'Marksmanship', 'Head', 'Both', 'Dragonstalker''s Helm'), +(3, 1, 1, 0, 88, 19377, 'Phase 5', 'Hunter', 'Marksmanship', 'Neck', 'Both', 'Prestor''s Talisman of Connivery'), +(3, 1, 2, 0, 88, 16937, 'Phase 5', 'Hunter', 'Marksmanship', 'Shoulders', 'Both', 'Dragonstalker''s Spaulders'), +(3, 1, 4, 0, 88, 16942, 'Phase 5', 'Hunter', 'Marksmanship', 'Chest', 'Both', 'Dragonstalker''s Breastplate'), +(3, 1, 5, 0, 88, 16936, 'Phase 5', 'Hunter', 'Marksmanship', 'Waist', 'Both', 'Dragonstalker''s Belt'), +(3, 1, 6, 0, 88, 16938, 'Phase 5', 'Hunter', 'Marksmanship', 'Legs', 'Both', 'Dragonstalker''s Legguards'), +(3, 1, 7, 0, 88, 16941, 'Phase 5', 'Hunter', 'Marksmanship', 'Feet', 'Both', 'Dragonstalker''s Greaves'), +(3, 1, 8, 0, 88, 16935, 'Phase 5', 'Hunter', 'Marksmanship', 'Wrists', 'Both', 'Dragonstalker''s Bracers'), +(3, 1, 9, 0, 88, 16940, 'Phase 5', 'Hunter', 'Marksmanship', 'Hands', 'Both', 'Dragonstalker''s Gauntlets'), +(3, 1, 10, 0, 88, 19325, 'Phase 5', 'Hunter', 'Marksmanship', 'Finger1', 'Both', 'Don Julio''s Band'), +(3, 1, 11, 0, 88, 17063, 'Phase 5', 'Hunter', 'Marksmanship', 'Finger2', 'Both', 'Band of Accuria'), +(3, 1, 12, 0, 88, 21670, 'Phase 5', 'Hunter', 'Marksmanship', 'Trinket1', 'Both', 'Badge of the Swarmguard'), +(3, 1, 13, 0, 88, 23570, 'Phase 5', 'Hunter', 'Marksmanship', 'Trinket2', 'Both', 'Jom Gabbar'), +(3, 1, 14, 0, 88, 21710, 'Phase 5', 'Hunter', 'Marksmanship', 'Back', 'Both', 'Cloak of the Fallen God'), +(3, 1, 15, 0, 88, 21673, 'Phase 5', 'Hunter', 'Marksmanship', 'MainHand', 'Both', 'Silithid Claw'), +(3, 1, 16, 0, 88, 19859, 'Phase 5', 'Hunter', 'Marksmanship', 'OffHand', 'Both', 'Fang of the Faceless'), +(3, 1, 17, 0, 88, 19361, 'Phase 5', 'Hunter', 'Marksmanship', 'Ranged', 'Both', 'Ashjre''thul, Crossbow of Smiting'); + +-- ilvl 92 (Phase 6) +INSERT INTO `playerbots_bis_gear` VALUES +(3, 1, 0, 0, 92, 22438, 'Phase 6', 'Hunter', 'Marksmanship', 'Head', 'Both', 'Cryptstalker Headpiece'), +(3, 1, 1, 0, 92, 23053, 'Phase 6', 'Hunter', 'Marksmanship', 'Neck', 'Both', 'Stormrage''s Talisman of Seething'), +(3, 1, 2, 0, 92, 22439, 'Phase 6', 'Hunter', 'Marksmanship', 'Shoulders', 'Both', 'Cryptstalker Spaulders'), +(3, 1, 4, 0, 92, 22436, 'Phase 6', 'Hunter', 'Marksmanship', 'Chest', 'Both', 'Cryptstalker Tunic'), +(3, 1, 5, 0, 92, 22442, 'Phase 6', 'Hunter', 'Marksmanship', 'Waist', 'Both', 'Cryptstalker Girdle'), +(3, 1, 6, 0, 92, 22437, 'Phase 6', 'Hunter', 'Marksmanship', 'Legs', 'Both', 'Cryptstalker Legguards'), +(3, 1, 7, 0, 92, 22440, 'Phase 6', 'Hunter', 'Marksmanship', 'Feet', 'Both', 'Cryptstalker Boots'), +(3, 1, 8, 0, 92, 22443, 'Phase 6', 'Hunter', 'Marksmanship', 'Wrists', 'Both', 'Cryptstalker Wristguards'), +(3, 1, 9, 0, 92, 16571, 'Phase 6', 'Hunter', 'Marksmanship', 'Hands', 'Both', 'General''s Chain Gloves'), +(3, 1, 10, 0, 92, 23067, 'Phase 6', 'Hunter', 'Marksmanship', 'Finger1', 'Both', 'Ring of the Cryptstalker'), +(3, 1, 11, 0, 92, 22961, 'Phase 6', 'Hunter', 'Marksmanship', 'Finger2', 'Both', 'Band of Reanimation'), +(3, 1, 12, 0, 92, 21670, 'Phase 6', 'Hunter', 'Marksmanship', 'Trinket1', 'Both', 'Badge of the Swarmguard'), +(3, 1, 13, 0, 92, 23041, 'Phase 6', 'Hunter', 'Marksmanship', 'Trinket2', 'Both', 'Slayer''s Crest'), +(3, 1, 14, 0, 92, 23045, 'Phase 6', 'Hunter', 'Marksmanship', 'Back', 'Both', 'Shroud of Dominion'), +(3, 1, 15, 0, 92, 22816, 'Phase 6', 'Hunter', 'Marksmanship', 'MainHand', 'Both', 'Hatchet of Sundered Bone'), +(3, 1, 16, 0, 92, 22802, 'Phase 6', 'Hunter', 'Marksmanship', 'OffHand', 'Both', 'Kingsfall'), +(3, 1, 17, 0, 92, 22812, 'Phase 6', 'Hunter', 'Marksmanship', 'Ranged', 'Both', 'Nerubian Slavemaker'); + +-- ilvl 200 (Pre-Raid) +INSERT INTO `playerbots_bis_gear` VALUES +(3, 1, 0, 0, 200, 37188, 'Pre-Raid', 'Hunter', 'Marksmanship', 'Head', 'Both', 'Plunderer''s Helmet'), +(3, 1, 1, 0, 200, 42645, 'Pre-Raid', 'Hunter', 'Marksmanship', 'Neck', 'Both', 'Titanium Impact Choker'), +(3, 1, 2, 0, 200, 37679, 'Pre-Raid', 'Hunter', 'Marksmanship', 'Shoulders', 'Both', 'Spaulders of the Abomination'), +(3, 1, 4, 0, 200, 37144, 'Pre-Raid', 'Hunter', 'Marksmanship', 'Chest', 'Both', 'Hauberk of the Arcane Wraith'), +(3, 1, 5, 0, 200, 37407, 'Pre-Raid', 'Hunter', 'Marksmanship', 'Waist', 'Both', 'Sovereign''s Belt'), +(3, 1, 6, 0, 200, 37669, 'Pre-Raid', 'Hunter', 'Marksmanship', 'Legs', 'Both', 'Leggings of the Stone Halls'), +(3, 1, 7, 0, 200, 37167, 'Pre-Raid', 'Hunter', 'Marksmanship', 'Feet', 'Both', 'Dragon Slayer''s Sabatons'), +(3, 1, 8, 0, 200, 37170, 'Pre-Raid', 'Hunter', 'Marksmanship', 'Wrists', 'Both', 'Interwoven Scale Bracers'), +(3, 1, 9, 0, 200, 37886, 'Pre-Raid', 'Hunter', 'Marksmanship', 'Hands', 'Both', 'Handgrips of the Savage Emissary'), +(3, 1, 10, 0, 200, 42642, 'Pre-Raid', 'Hunter', 'Marksmanship', 'Finger1', 'Both', 'Titanium Impact Band'), +(3, 1, 12, 0, 200, 40684, 'Pre-Raid', 'Hunter', 'Marksmanship', 'Trinket1', 'Both', 'Mirror of Truth'), +(3, 1, 14, 0, 200, 43566, 'Pre-Raid', 'Hunter', 'Marksmanship', 'Back', 'Both', 'Ice Striker''s Cloak'), +(3, 1, 15, 0, 200, 44249, 'Pre-Raid', 'Hunter', 'Marksmanship', 'MainHand', 'Both', 'Runeblade of Demonstrable Power'); + +-- ilvl 224 (Phase 1) +INSERT INTO `playerbots_bis_gear` VALUES +(3, 1, 0, 0, 224, 40543, 'Phase 1', 'Hunter', 'Marksmanship', 'Head', 'Both', 'Blue Aspect Helm'), +(3, 1, 1, 0, 224, 44664, 'Phase 1', 'Hunter', 'Marksmanship', 'Neck', 'Both', 'Favor of the Dragon Queen'), +(3, 1, 2, 0, 224, 40507, 'Phase 1', 'Hunter', 'Marksmanship', 'Shoulders', 'Both', 'Valorous Cryptstalker Spaulders'), +(3, 1, 4, 0, 224, 40193, 'Phase 1', 'Hunter', 'Marksmanship', 'Chest', 'Both', 'Tunic of Masked Suffering'), +(3, 1, 5, 0, 224, 40275, 'Phase 1', 'Hunter', 'Marksmanship', 'Waist', 'Both', 'Depraved Linked Belt'), +(3, 1, 6, 0, 224, 40506, 'Phase 1', 'Hunter', 'Marksmanship', 'Legs', 'Both', 'Valorous Cryptstalker Legguards'), +(3, 1, 7, 0, 224, 40549, 'Phase 1', 'Hunter', 'Marksmanship', 'Feet', 'Both', 'Boots of the Renewed Flight'), +(3, 1, 8, 0, 224, 40282, 'Phase 1', 'Hunter', 'Marksmanship', 'Wrists', 'Both', 'Slime Stream Bands'), +(3, 1, 9, 0, 224, 40541, 'Phase 1', 'Hunter', 'Marksmanship', 'Hands', 'Both', 'Frosted Adroit Handguards'), +(3, 1, 10, 0, 224, 40074, 'Phase 1', 'Hunter', 'Marksmanship', 'Finger1', 'Both', 'Strong-Handed Ring'), +(3, 1, 12, 0, 224, 40684, 'Phase 1', 'Hunter', 'Marksmanship', 'Trinket1', 'Both', 'Mirror of Truth'), +(3, 1, 14, 0, 224, 40403, 'Phase 1', 'Hunter', 'Marksmanship', 'Back', 'Both', 'Drape of the Deadly Foe'); + +-- ilvl 245 (Phase 2) +INSERT INTO `playerbots_bis_gear` VALUES +(3, 1, 0, 0, 245, 46143, 'Phase 2', 'Hunter', 'Marksmanship', 'Head', 'Both', 'Conqueror''s Scourgestalker Headpiece'), +(3, 1, 1, 0, 245, 45517, 'Phase 2', 'Hunter', 'Marksmanship', 'Neck', 'Both', 'Pendulum of Infinity'), +(3, 1, 2, 0, 245, 46145, 'Phase 2', 'Hunter', 'Marksmanship', 'Shoulders', 'Both', 'Conqueror''s Scourgestalker Spaulders'), +(3, 1, 4, 0, 245, 46141, 'Phase 2', 'Hunter', 'Marksmanship', 'Chest', 'Both', 'Conqueror''s Scourgestalker Tunic'), +(3, 1, 5, 0, 245, 45467, 'Phase 2', 'Hunter', 'Marksmanship', 'Waist', 'Both', 'Belt of the Betrayed'), +(3, 1, 6, 0, 245, 46144, 'Phase 2', 'Hunter', 'Marksmanship', 'Legs', 'Both', 'Conqueror''s Scourgestalker Legguards'), +(3, 1, 7, 0, 245, 45244, 'Phase 2', 'Hunter', 'Marksmanship', 'Feet', 'Both', 'Greaves of Swift Vengeance'), +(3, 1, 8, 0, 245, 45454, 'Phase 2', 'Hunter', 'Marksmanship', 'Wrists', 'Both', 'Frost-bound Chain Bracers'), +(3, 1, 9, 0, 245, 45444, 'Phase 2', 'Hunter', 'Marksmanship', 'Hands', 'Both', 'Gloves of the Steady Hand'), +(3, 1, 10, 0, 245, 45456, 'Phase 2', 'Hunter', 'Marksmanship', 'Finger1', 'Both', 'Loop of the Agile'), +(3, 1, 12, 0, 245, 45931, 'Phase 2', 'Hunter', 'Marksmanship', 'Trinket1', 'Both', 'Mjolnir Runestone'), +(3, 1, 14, 0, 245, 46032, 'Phase 2', 'Hunter', 'Marksmanship', 'Back', 'Both', 'Drape of the Faceless General'), +(3, 1, 15, 0, 245, 45498, 'Phase 2', 'Hunter', 'Marksmanship', 'MainHand', 'Both', 'Lotrafen, Spear of the Damned'), +(3, 1, 17, 0, 245, 45570, 'Phase 2', 'Hunter', 'Marksmanship', 'Ranged', 'Both', 'Skyforge Crossbow'); + +-- ilvl 258 (Phase 3) +INSERT INTO `playerbots_bis_gear` VALUES +(3, 1, 0, 0, 258, 48262, 'Phase 3', 'Hunter', 'Marksmanship', 'Head', 'Both', 'Windrunner''s Headpiece of Triumph'), +(3, 1, 1, 1, 258, 47060, 'Phase 3', 'Hunter', 'Marksmanship', 'Neck', 'Alliance', 'Charge of the Demon Lord'), +(3, 1, 1, 2, 258, 47433, 'Phase 3', 'Hunter', 'Marksmanship', 'Neck', 'Horde', 'Charge of the Eredar'), +(3, 1, 2, 0, 258, 48260, 'Phase 3', 'Hunter', 'Marksmanship', 'Shoulders', 'Both', 'Windrunner''s Spaulders of Triumph'), +(3, 1, 4, 0, 258, 48264, 'Phase 3', 'Hunter', 'Marksmanship', 'Chest', 'Both', 'Windrunner''s Tunic of Triumph'), +(3, 1, 5, 1, 258, 47153, 'Phase 3', 'Hunter', 'Marksmanship', 'Waist', 'Alliance', 'Belt of Deathly Dominion'), +(3, 1, 5, 2, 258, 47472, 'Phase 3', 'Hunter', 'Marksmanship', 'Waist', 'Horde', 'Waistguard of Deathly Dominion'), +(3, 1, 6, 1, 258, 47191, 'Phase 3', 'Hunter', 'Marksmanship', 'Legs', 'Alliance', 'Legguards of the Lurking Threat'), +(3, 1, 6, 2, 258, 47480, 'Phase 3', 'Hunter', 'Marksmanship', 'Legs', 'Horde', 'Leggings of the Lurking Threat'), +(3, 1, 7, 1, 258, 47109, 'Phase 3', 'Hunter', 'Marksmanship', 'Feet', 'Alliance', 'Sabatons of Ruthless Judgment'), +(3, 1, 7, 2, 258, 47457, 'Phase 3', 'Hunter', 'Marksmanship', 'Feet', 'Horde', 'Greaves of Ruthless Judgment'), +(3, 1, 8, 1, 258, 47074, 'Phase 3', 'Hunter', 'Marksmanship', 'Wrists', 'Alliance', 'Bracers of the Untold Massacre'), +(3, 1, 8, 2, 258, 47442, 'Phase 3', 'Hunter', 'Marksmanship', 'Wrists', 'Horde', 'Bracers of the Silent Massacre'), +(3, 1, 9, 0, 258, 48263, 'Phase 3', 'Hunter', 'Marksmanship', 'Hands', 'Both', 'Windrunner''s Handguards of Triumph'), +(3, 1, 10, 1, 258, 47075, 'Phase 3', 'Hunter', 'Marksmanship', 'Finger1', 'Alliance', 'Ring of Callous Aggression'), +(3, 1, 10, 2, 258, 47443, 'Phase 3', 'Hunter', 'Marksmanship', 'Finger1', 'Horde', 'Band of Callous Aggression'), +(3, 1, 12, 2, 258, 45931, 'Phase 3', 'Hunter', 'Marksmanship', 'Trinket1', 'Horde', 'Mjolnir Runestone'), +(3, 1, 14, 1, 258, 47545, 'Phase 3', 'Hunter', 'Marksmanship', 'Back', 'Alliance', 'Vereesa''s Dexterity'), +(3, 1, 14, 2, 258, 47546, 'Phase 3', 'Hunter', 'Marksmanship', 'Back', 'Horde', 'Sylvanas'' Cunning'), +(3, 1, 17, 1, 258, 47521, 'Phase 3', 'Hunter', 'Marksmanship', 'Ranged', 'Alliance', 'BRK 1000'), +(3, 1, 17, 2, 258, 47523, 'Phase 3', 'Hunter', 'Marksmanship', 'Ranged', 'Horde', 'Fezzik''s Autocannon'); + +-- ilvl 264 (Phase 4) +INSERT INTO `playerbots_bis_gear` VALUES +(3, 1, 0, 0, 264, 51286, 'Phase 4', 'Hunter', 'Marksmanship', 'Head', 'Both', 'Sanctified Ahn''Kahar Blood Hunter''s Headpiece'), +(3, 1, 1, 0, 264, 50633, 'Phase 4', 'Hunter', 'Marksmanship', 'Neck', 'Both', 'Sindragosa''s Cruel Claw'), +(3, 1, 2, 0, 264, 51288, 'Phase 4', 'Hunter', 'Marksmanship', 'Shoulders', 'Both', 'Sanctified Ahn''Kahar Blood Hunter''s Spaulders'), +(3, 1, 4, 0, 264, 51289, 'Phase 4', 'Hunter', 'Marksmanship', 'Chest', 'Both', 'Sanctified Ahn''Kahar Blood Hunter''s Tunic'), +(3, 1, 5, 0, 264, 50688, 'Phase 4', 'Hunter', 'Marksmanship', 'Waist', 'Both', 'Nerub''ar Stalker''s Cord'), +(3, 1, 6, 0, 264, 50645, 'Phase 4', 'Hunter', 'Marksmanship', 'Legs', 'Both', 'Leggings of Northern Lights'), +(3, 1, 7, 0, 264, 50607, 'Phase 4', 'Hunter', 'Marksmanship', 'Feet', 'Both', 'Frostbitten Fur Boots'), +(3, 1, 8, 0, 264, 50655, 'Phase 4', 'Hunter', 'Marksmanship', 'Wrists', 'Both', 'Scourge Hunter''s Vambraces'), +(3, 1, 9, 0, 264, 51285, 'Phase 4', 'Hunter', 'Marksmanship', 'Hands', 'Both', 'Sanctified Ahn''Kahar Blood Hunter''s Handguards'), +(3, 1, 10, 0, 264, 50402, 'Phase 4', 'Hunter', 'Marksmanship', 'Finger1', 'Both', 'Ashen Band of Endless Vengeance'), +(3, 1, 12, 0, 264, 50363, 'Phase 4', 'Hunter', 'Marksmanship', 'Trinket1', 'Both', 'Deathbringer''s Will'), +(3, 1, 14, 1, 264, 47545, 'Phase 4', 'Hunter', 'Marksmanship', 'Back', 'Alliance', 'Vereesa''s Dexterity'), +(3, 1, 14, 2, 264, 47546, 'Phase 4', 'Hunter', 'Marksmanship', 'Back', 'Horde', 'Sylvanas'' Cunning'), +(3, 1, 15, 0, 264, 50735, 'Phase 4', 'Hunter', 'Marksmanship', 'MainHand', 'Both', 'Oathbinder, Charge of the Ranger-General'), +(3, 1, 17, 0, 264, 50733, 'Phase 4', 'Hunter', 'Marksmanship', 'Ranged', 'Both', 'Fal''inrush, Defender of Quel''thalas'); + +-- ilvl 290 (Phase 5) +INSERT INTO `playerbots_bis_gear` VALUES +(3, 1, 0, 0, 290, 51286, 'Phase 5', 'Hunter', 'Marksmanship', 'Head', 'Both', 'Sanctified Ahn''Kahar Blood Hunter''s Headpiece'), +(3, 1, 1, 0, 290, 50633, 'Phase 5', 'Hunter', 'Marksmanship', 'Neck', 'Both', 'Sindragosa''s Cruel Claw'), +(3, 1, 2, 0, 290, 51288, 'Phase 5', 'Hunter', 'Marksmanship', 'Shoulders', 'Both', 'Sanctified Ahn''Kahar Blood Hunter''s Spaulders'), +(3, 1, 4, 0, 290, 51289, 'Phase 5', 'Hunter', 'Marksmanship', 'Chest', 'Both', 'Sanctified Ahn''Kahar Blood Hunter''s Tunic'), +(3, 1, 5, 0, 290, 50688, 'Phase 5', 'Hunter', 'Marksmanship', 'Waist', 'Both', 'Nerub''ar Stalker''s Cord'), +(3, 1, 6, 0, 290, 50645, 'Phase 5', 'Hunter', 'Marksmanship', 'Legs', 'Both', 'Leggings of Northern Lights'), +(3, 1, 7, 0, 290, 54577, 'Phase 5', 'Hunter', 'Marksmanship', 'Feet', 'Both', 'Returning Footfalls'), +(3, 1, 8, 0, 290, 50655, 'Phase 5', 'Hunter', 'Marksmanship', 'Wrists', 'Both', 'Scourge Hunter''s Vambraces'), +(3, 1, 9, 0, 290, 51285, 'Phase 5', 'Hunter', 'Marksmanship', 'Hands', 'Both', 'Sanctified Ahn''Kahar Blood Hunter''s Handguards'), +(3, 1, 10, 0, 290, 54576, 'Phase 5', 'Hunter', 'Marksmanship', 'Finger1', 'Both', 'Signet of Twilight'), +(3, 1, 11, 0, 290, 50402, 'Phase 5', 'Hunter', 'Marksmanship', 'Finger2', 'Both', 'Ashen Band of Endless Vengeance'), +(3, 1, 12, 0, 290, 50363, 'Phase 5', 'Hunter', 'Marksmanship', 'Trinket1', 'Both', 'Deathbringer''s Will'), +(3, 1, 13, 0, 290, 54590, 'Phase 5', 'Hunter', 'Marksmanship', 'Trinket2', 'Both', 'Sharpened Twilight Scale'), +(3, 1, 14, 0, 290, 47545, 'Phase 5', 'Hunter', 'Marksmanship', 'Back', 'Both', 'Vereesa''s Dexterity'), +(3, 1, 15, 0, 290, 50735, 'Phase 5', 'Hunter', 'Marksmanship', 'MainHand', 'Both', 'Oathbinder, Charge of the Ranger-General'), +(3, 1, 17, 0, 290, 50733, 'Phase 5', 'Hunter', 'Marksmanship', 'Ranged', 'Both', 'Fal''inrush, Defender of Quel''thalas'); + +-- Survival (tab 2) +-- ilvl 66 (Phase 1 (Pre-Raid)) +INSERT INTO `playerbots_bis_gear` VALUES +(3, 2, 0, 0, 66, 13404, 'Phase 1 (Pre-Raid)', 'Hunter', 'Survival', 'Head', 'Both', 'Mask of the Unforgiven'), +(3, 2, 1, 0, 66, 15411, 'Phase 1 (Pre-Raid)', 'Hunter', 'Survival', 'Neck', 'Both', 'Mark of Fordring'), +(3, 2, 2, 0, 66, 12927, 'Phase 1 (Pre-Raid)', 'Hunter', 'Survival', 'Shoulders', 'Both', 'Truestrike Shoulders'), +(3, 2, 4, 0, 66, 11726, 'Phase 1 (Pre-Raid)', 'Hunter', 'Survival', 'Chest', 'Both', 'Savage Gladiator Chain'), +(3, 2, 5, 0, 66, 14502, 'Phase 1 (Pre-Raid)', 'Hunter', 'Survival', 'Waist', 'Both', 'Frostbite Girdle'), +(3, 2, 6, 0, 66, 15062, 'Phase 1 (Pre-Raid)', 'Hunter', 'Survival', 'Legs', 'Both', 'Devilsaur Leggings'), +(3, 2, 7, 0, 66, 13967, 'Phase 1 (Pre-Raid)', 'Hunter', 'Survival', 'Feet', 'Both', 'Windreaver Greaves'), +(3, 2, 8, 0, 66, 13211, 'Phase 1 (Pre-Raid)', 'Hunter', 'Survival', 'Wrists', 'Both', 'Slashclaw Bracers'), +(3, 2, 9, 0, 66, 15063, 'Phase 1 (Pre-Raid)', 'Hunter', 'Survival', 'Hands', 'Both', 'Devilsaur Gauntlets'), +(3, 2, 10, 0, 66, 13098, 'Phase 1 (Pre-Raid)', 'Hunter', 'Survival', 'Finger1', 'Both', 'Painweaver Band'), +(3, 2, 11, 0, 66, 17713, 'Phase 1 (Pre-Raid)', 'Hunter', 'Survival', 'Finger2', 'Both', 'Blackstone Ring'), +(3, 2, 12, 0, 66, 13965, 'Phase 1 (Pre-Raid)', 'Hunter', 'Survival', 'Trinket1', 'Both', 'Blackhand''s Breadth'), +(3, 2, 13, 0, 66, 11815, 'Phase 1 (Pre-Raid)', 'Hunter', 'Survival', 'Trinket2', 'Both', 'Hand of Justice'), +(3, 2, 14, 0, 66, 13340, 'Phase 1 (Pre-Raid)', 'Hunter', 'Survival', 'Back', 'Both', 'Cape of the Black Baron'), +(3, 2, 15, 0, 66, 12940, 'Phase 1 (Pre-Raid)', 'Hunter', 'Survival', 'MainHand', 'Both', 'Dal''Rend''s Sacred Charge'), +(3, 2, 16, 0, 66, 12939, 'Phase 1 (Pre-Raid)', 'Hunter', 'Survival', 'OffHand', 'Both', 'Dal''Rend''s Tribal Guardian'), +(3, 2, 17, 0, 66, 18738, 'Phase 1 (Pre-Raid)', 'Hunter', 'Survival', 'Ranged', 'Both', 'Carapace Spine Crossbow'); + +-- ilvl 76 (Phase 2 (Pre-Raid)) +INSERT INTO `playerbots_bis_gear` VALUES +(3, 2, 0, 0, 76, 18421, 'Phase 2 (Pre-Raid)', 'Hunter', 'Survival', 'Head', 'Both', 'Backwood Helm'), +(3, 2, 1, 0, 76, 15411, 'Phase 2 (Pre-Raid)', 'Hunter', 'Survival', 'Neck', 'Both', 'Mark of Fordring'), +(3, 2, 2, 0, 76, 13358, 'Phase 2 (Pre-Raid)', 'Hunter', 'Survival', 'Shoulders', 'Both', 'Wyrmtongue Shoulders'), +(3, 2, 4, 0, 76, 11726, 'Phase 2 (Pre-Raid)', 'Hunter', 'Survival', 'Chest', 'Both', 'Savage Gladiator Chain'), +(3, 2, 5, 0, 76, 18393, 'Phase 2 (Pre-Raid)', 'Hunter', 'Survival', 'Waist', 'Both', 'Warpwood Binding'), +(3, 2, 6, 0, 76, 15062, 'Phase 2 (Pre-Raid)', 'Hunter', 'Survival', 'Legs', 'Both', 'Devilsaur Leggings'), +(3, 2, 7, 0, 76, 13967, 'Phase 2 (Pre-Raid)', 'Hunter', 'Survival', 'Feet', 'Both', 'Windreaver Greaves'), +(3, 2, 8, 0, 76, 13211, 'Phase 2 (Pre-Raid)', 'Hunter', 'Survival', 'Wrists', 'Both', 'Slashclaw Bracers'), +(3, 2, 9, 0, 76, 15063, 'Phase 2 (Pre-Raid)', 'Hunter', 'Survival', 'Hands', 'Both', 'Devilsaur Gauntlets'), +(3, 2, 10, 0, 76, 18500, 'Phase 2 (Pre-Raid)', 'Hunter', 'Survival', 'Finger1', 'Both', 'Tarnished Elven Ring'), +(3, 2, 11, 0, 76, 18500, 'Phase 2 (Pre-Raid)', 'Hunter', 'Survival', 'Finger2', 'Both', 'Tarnished Elven Ring'), +(3, 2, 12, 0, 76, 13965, 'Phase 2 (Pre-Raid)', 'Hunter', 'Survival', 'Trinket1', 'Both', 'Blackhand''s Breadth'), +(3, 2, 13, 0, 76, 18473, 'Phase 2 (Pre-Raid)', 'Hunter', 'Survival', 'Trinket2', 'Both', 'Royal Seal of Eldre''Thalas'), +(3, 2, 14, 0, 76, 13340, 'Phase 2 (Pre-Raid)', 'Hunter', 'Survival', 'Back', 'Both', 'Cape of the Black Baron'), +(3, 2, 15, 0, 76, 18520, 'Phase 2 (Pre-Raid)', 'Hunter', 'Survival', 'MainHand', 'Both', 'Barbarous Blade'), +(3, 2, 17, 0, 76, 18738, 'Phase 2 (Pre-Raid)', 'Hunter', 'Survival', 'Ranged', 'Both', 'Carapace Spine Crossbow'); + +-- ilvl 78 (Phase 2) +INSERT INTO `playerbots_bis_gear` VALUES +(3, 2, 0, 0, 78, 16846, 'Phase 2', 'Hunter', 'Survival', 'Head', 'Both', 'Giantstalker''s Helmet'), +(3, 2, 1, 0, 78, 18404, 'Phase 2', 'Hunter', 'Survival', 'Neck', 'Both', 'Onyxia Tooth Pendant'), +(3, 2, 2, 0, 78, 16848, 'Phase 2', 'Hunter', 'Survival', 'Shoulders', 'Both', 'Giantstalker''s Epaulets'), +(3, 2, 4, 0, 78, 16845, 'Phase 2', 'Hunter', 'Survival', 'Chest', 'Both', 'Giantstalker''s Breastplate'), +(3, 2, 5, 0, 78, 16851, 'Phase 2', 'Hunter', 'Survival', 'Waist', 'Both', 'Giantstalker''s Belt'), +(3, 2, 6, 0, 78, 16847, 'Phase 2', 'Hunter', 'Survival', 'Legs', 'Both', 'Giantstalker''s Leggings'), +(3, 2, 7, 0, 78, 16849, 'Phase 2', 'Hunter', 'Survival', 'Feet', 'Both', 'Giantstalker''s Boots'), +(3, 2, 8, 0, 78, 16850, 'Phase 2', 'Hunter', 'Survival', 'Wrists', 'Both', 'Giantstalker''s Bracers'), +(3, 2, 9, 0, 78, 16852, 'Phase 2', 'Hunter', 'Survival', 'Hands', 'Both', 'Giantstalker''s Gloves'), +(3, 2, 10, 0, 78, 17063, 'Phase 2', 'Hunter', 'Survival', 'Finger1', 'Both', 'Band of Accuria'), +(3, 2, 11, 0, 78, 18821, 'Phase 2', 'Hunter', 'Survival', 'Finger2', 'Both', 'Quick Strike Ring'), +(3, 2, 12, 0, 78, 13965, 'Phase 2', 'Hunter', 'Survival', 'Trinket1', 'Both', 'Blackhand''s Breadth'), +(3, 2, 13, 0, 78, 18473, 'Phase 2', 'Hunter', 'Survival', 'Trinket2', 'Both', 'Royal Seal of Eldre''Thalas'), +(3, 2, 14, 0, 78, 17102, 'Phase 2', 'Hunter', 'Survival', 'Back', 'Both', 'Cloak of the Shrouded Mists'), +(3, 2, 15, 0, 78, 18832, 'Phase 2', 'Hunter', 'Survival', 'MainHand', 'Both', 'Brutality Blade'), +(3, 2, 16, 0, 78, 18805, 'Phase 2', 'Hunter', 'Survival', 'OffHand', 'Both', 'Core Hound Tooth'), +(3, 2, 17, 0, 78, 18713, 'Phase 2', 'Hunter', 'Survival', 'Ranged', 'Both', 'Rhok''delar, Longbow of the Ancient Keepers'); + +-- ilvl 83 (Phase 3) +INSERT INTO `playerbots_bis_gear` VALUES +(3, 2, 0, 0, 83, 16939, 'Phase 3', 'Hunter', 'Survival', 'Head', 'Both', 'Dragonstalker''s Helm'), +(3, 2, 1, 0, 83, 19377, 'Phase 3', 'Hunter', 'Survival', 'Neck', 'Both', 'Prestor''s Talisman of Connivery'), +(3, 2, 2, 0, 83, 16937, 'Phase 3', 'Hunter', 'Survival', 'Shoulders', 'Both', 'Dragonstalker''s Spaulders'), +(3, 2, 4, 0, 83, 16942, 'Phase 3', 'Hunter', 'Survival', 'Chest', 'Both', 'Dragonstalker''s Breastplate'), +(3, 2, 5, 0, 83, 16936, 'Phase 3', 'Hunter', 'Survival', 'Waist', 'Both', 'Dragonstalker''s Belt'), +(3, 2, 6, 0, 83, 16938, 'Phase 3', 'Hunter', 'Survival', 'Legs', 'Both', 'Dragonstalker''s Legguards'), +(3, 2, 7, 0, 83, 16941, 'Phase 3', 'Hunter', 'Survival', 'Feet', 'Both', 'Dragonstalker''s Greaves'), +(3, 2, 8, 0, 83, 16935, 'Phase 3', 'Hunter', 'Survival', 'Wrists', 'Both', 'Dragonstalker''s Bracers'), +(3, 2, 9, 0, 83, 16940, 'Phase 3', 'Hunter', 'Survival', 'Hands', 'Both', 'Dragonstalker''s Gauntlets'), +(3, 2, 10, 0, 83, 19325, 'Phase 3', 'Hunter', 'Survival', 'Finger1', 'Both', 'Don Julio''s Band'), +(3, 2, 11, 0, 83, 18821, 'Phase 3', 'Hunter', 'Survival', 'Finger2', 'Both', 'Quick Strike Ring'), +(3, 2, 12, 0, 83, 13965, 'Phase 3', 'Hunter', 'Survival', 'Trinket1', 'Both', 'Blackhand''s Breadth'), +(3, 2, 13, 0, 83, 19406, 'Phase 3', 'Hunter', 'Survival', 'Trinket2', 'Both', 'Drake Fang Talisman'), +(3, 2, 14, 0, 83, 17102, 'Phase 3', 'Hunter', 'Survival', 'Back', 'Both', 'Cloak of the Shrouded Mists'), +(3, 2, 15, 0, 83, 18832, 'Phase 3', 'Hunter', 'Survival', 'MainHand', 'Both', 'Brutality Blade'), +(3, 2, 16, 0, 83, 18805, 'Phase 3', 'Hunter', 'Survival', 'OffHand', 'Both', 'Core Hound Tooth'), +(3, 2, 17, 0, 83, 19361, 'Phase 3', 'Hunter', 'Survival', 'Ranged', 'Both', 'Ashjre''thul, Crossbow of Smiting'); + +-- ilvl 88 (Phase 5) +INSERT INTO `playerbots_bis_gear` VALUES +(3, 2, 0, 0, 88, 16939, 'Phase 5', 'Hunter', 'Survival', 'Head', 'Both', 'Dragonstalker''s Helm'), +(3, 2, 1, 0, 88, 19377, 'Phase 5', 'Hunter', 'Survival', 'Neck', 'Both', 'Prestor''s Talisman of Connivery'), +(3, 2, 2, 0, 88, 16937, 'Phase 5', 'Hunter', 'Survival', 'Shoulders', 'Both', 'Dragonstalker''s Spaulders'), +(3, 2, 4, 0, 88, 16942, 'Phase 5', 'Hunter', 'Survival', 'Chest', 'Both', 'Dragonstalker''s Breastplate'), +(3, 2, 5, 0, 88, 16936, 'Phase 5', 'Hunter', 'Survival', 'Waist', 'Both', 'Dragonstalker''s Belt'), +(3, 2, 6, 0, 88, 16938, 'Phase 5', 'Hunter', 'Survival', 'Legs', 'Both', 'Dragonstalker''s Legguards'), +(3, 2, 7, 0, 88, 16941, 'Phase 5', 'Hunter', 'Survival', 'Feet', 'Both', 'Dragonstalker''s Greaves'), +(3, 2, 8, 0, 88, 16935, 'Phase 5', 'Hunter', 'Survival', 'Wrists', 'Both', 'Dragonstalker''s Bracers'), +(3, 2, 9, 0, 88, 16940, 'Phase 5', 'Hunter', 'Survival', 'Hands', 'Both', 'Dragonstalker''s Gauntlets'), +(3, 2, 10, 0, 88, 19325, 'Phase 5', 'Hunter', 'Survival', 'Finger1', 'Both', 'Don Julio''s Band'), +(3, 2, 11, 0, 88, 17063, 'Phase 5', 'Hunter', 'Survival', 'Finger2', 'Both', 'Band of Accuria'), +(3, 2, 12, 0, 88, 21670, 'Phase 5', 'Hunter', 'Survival', 'Trinket1', 'Both', 'Badge of the Swarmguard'), +(3, 2, 13, 0, 88, 23570, 'Phase 5', 'Hunter', 'Survival', 'Trinket2', 'Both', 'Jom Gabbar'), +(3, 2, 14, 0, 88, 21710, 'Phase 5', 'Hunter', 'Survival', 'Back', 'Both', 'Cloak of the Fallen God'), +(3, 2, 15, 0, 88, 21673, 'Phase 5', 'Hunter', 'Survival', 'MainHand', 'Both', 'Silithid Claw'), +(3, 2, 16, 0, 88, 19859, 'Phase 5', 'Hunter', 'Survival', 'OffHand', 'Both', 'Fang of the Faceless'), +(3, 2, 17, 0, 88, 19361, 'Phase 5', 'Hunter', 'Survival', 'Ranged', 'Both', 'Ashjre''thul, Crossbow of Smiting'); + +-- ilvl 92 (Phase 6) +INSERT INTO `playerbots_bis_gear` VALUES +(3, 2, 0, 0, 92, 22438, 'Phase 6', 'Hunter', 'Survival', 'Head', 'Both', 'Cryptstalker Headpiece'), +(3, 2, 1, 0, 92, 23053, 'Phase 6', 'Hunter', 'Survival', 'Neck', 'Both', 'Stormrage''s Talisman of Seething'), +(3, 2, 2, 0, 92, 22439, 'Phase 6', 'Hunter', 'Survival', 'Shoulders', 'Both', 'Cryptstalker Spaulders'), +(3, 2, 4, 0, 92, 22436, 'Phase 6', 'Hunter', 'Survival', 'Chest', 'Both', 'Cryptstalker Tunic'), +(3, 2, 5, 0, 92, 22442, 'Phase 6', 'Hunter', 'Survival', 'Waist', 'Both', 'Cryptstalker Girdle'), +(3, 2, 6, 0, 92, 22437, 'Phase 6', 'Hunter', 'Survival', 'Legs', 'Both', 'Cryptstalker Legguards'), +(3, 2, 7, 0, 92, 22440, 'Phase 6', 'Hunter', 'Survival', 'Feet', 'Both', 'Cryptstalker Boots'), +(3, 2, 8, 0, 92, 22443, 'Phase 6', 'Hunter', 'Survival', 'Wrists', 'Both', 'Cryptstalker Wristguards'), +(3, 2, 9, 0, 92, 16571, 'Phase 6', 'Hunter', 'Survival', 'Hands', 'Both', 'General''s Chain Gloves'), +(3, 2, 10, 0, 92, 23067, 'Phase 6', 'Hunter', 'Survival', 'Finger1', 'Both', 'Ring of the Cryptstalker'), +(3, 2, 11, 0, 92, 22961, 'Phase 6', 'Hunter', 'Survival', 'Finger2', 'Both', 'Band of Reanimation'), +(3, 2, 12, 0, 92, 21670, 'Phase 6', 'Hunter', 'Survival', 'Trinket1', 'Both', 'Badge of the Swarmguard'), +(3, 2, 13, 0, 92, 23041, 'Phase 6', 'Hunter', 'Survival', 'Trinket2', 'Both', 'Slayer''s Crest'), +(3, 2, 14, 0, 92, 23045, 'Phase 6', 'Hunter', 'Survival', 'Back', 'Both', 'Shroud of Dominion'), +(3, 2, 15, 0, 92, 22816, 'Phase 6', 'Hunter', 'Survival', 'MainHand', 'Both', 'Hatchet of Sundered Bone'), +(3, 2, 16, 0, 92, 22802, 'Phase 6', 'Hunter', 'Survival', 'OffHand', 'Both', 'Kingsfall'), +(3, 2, 17, 0, 92, 22812, 'Phase 6', 'Hunter', 'Survival', 'Ranged', 'Both', 'Nerubian Slavemaker'); + +-- ilvl 200 (Pre-Raid) +INSERT INTO `playerbots_bis_gear` VALUES +(3, 2, 0, 0, 200, 42551, 'Pre-Raid', 'Hunter', 'Survival', 'Head', 'Both', 'Truesight Ice Blinders'), +(3, 2, 1, 0, 200, 40678, 'Pre-Raid', 'Hunter', 'Survival', 'Neck', 'Both', 'Pendant of the Outcast Hero'), +(3, 2, 2, 0, 200, 37679, 'Pre-Raid', 'Hunter', 'Survival', 'Shoulders', 'Both', 'Spaulders of the Abomination'), +(3, 2, 4, 0, 200, 44295, 'Pre-Raid', 'Hunter', 'Survival', 'Chest', 'Both', 'Polished Regimental Hauberk'), +(3, 2, 5, 0, 200, 40692, 'Pre-Raid', 'Hunter', 'Survival', 'Waist', 'Both', 'Vereesa''s Silver Chain Belt'), +(3, 2, 6, 0, 200, 37669, 'Pre-Raid', 'Hunter', 'Survival', 'Legs', 'Both', 'Leggings of the Stone Halls'), +(3, 2, 7, 0, 200, 44297, 'Pre-Raid', 'Hunter', 'Survival', 'Feet', 'Both', 'Boots of the Neverending Path'), +(3, 2, 8, 0, 200, 37170, 'Pre-Raid', 'Hunter', 'Survival', 'Wrists', 'Both', 'Interwoven Scale Bracers'), +(3, 2, 9, 0, 200, 37886, 'Pre-Raid', 'Hunter', 'Survival', 'Hands', 'Both', 'Handgrips of the Savage Emissary'), +(3, 2, 10, 0, 200, 37685, 'Pre-Raid', 'Hunter', 'Survival', 'Finger1', 'Both', 'Mobius Band'), +(3, 2, 12, 0, 200, 40684, 'Pre-Raid', 'Hunter', 'Survival', 'Trinket1', 'Both', 'Mirror of Truth'), +(3, 2, 14, 0, 200, 43406, 'Pre-Raid', 'Hunter', 'Survival', 'Back', 'Both', 'Cloak of the Gushing Wound'), +(3, 2, 15, 0, 200, 37883, 'Pre-Raid', 'Hunter', 'Survival', 'MainHand', 'Both', 'Staff of Trickery'), +(3, 2, 17, 0, 200, 37191, 'Pre-Raid', 'Hunter', 'Survival', 'Ranged', 'Both', 'Drake-Mounted Crossbow'); + +-- ilvl 224 (Phase 1) +INSERT INTO `playerbots_bis_gear` VALUES +(3, 2, 0, 0, 224, 40505, 'Phase 1', 'Hunter', 'Survival', 'Head', 'Both', 'Valorous Cryptstalker Headpiece'), +(3, 2, 1, 0, 224, 44664, 'Phase 1', 'Hunter', 'Survival', 'Neck', 'Both', 'Favor of the Dragon Queen'), +(3, 2, 2, 0, 224, 40507, 'Phase 1', 'Hunter', 'Survival', 'Shoulders', 'Both', 'Valorous Cryptstalker Spaulders'), +(3, 2, 4, 0, 224, 43998, 'Phase 1', 'Hunter', 'Survival', 'Chest', 'Both', 'Chestguard of Flagrant Prowess'), +(3, 2, 5, 0, 224, 39762, 'Phase 1', 'Hunter', 'Survival', 'Waist', 'Both', 'Torn Web Wrapping'), +(3, 2, 6, 0, 224, 40331, 'Phase 1', 'Hunter', 'Survival', 'Legs', 'Both', 'Leggings of Failed Escape'), +(3, 2, 7, 0, 224, 40549, 'Phase 1', 'Hunter', 'Survival', 'Feet', 'Both', 'Boots of the Renewed Flight'), +(3, 2, 8, 0, 224, 40282, 'Phase 1', 'Hunter', 'Survival', 'Wrists', 'Both', 'Slime Stream Bands'), +(3, 2, 9, 0, 224, 40541, 'Phase 1', 'Hunter', 'Survival', 'Hands', 'Both', 'Frosted Adroit Handguards'), +(3, 2, 10, 0, 224, 40074, 'Phase 1', 'Hunter', 'Survival', 'Finger1', 'Both', 'Strong-Handed Ring'), +(3, 2, 12, 0, 224, 40431, 'Phase 1', 'Hunter', 'Survival', 'Trinket1', 'Both', 'Fury of the Five Flights'), +(3, 2, 14, 0, 224, 40403, 'Phase 1', 'Hunter', 'Survival', 'Back', 'Both', 'Drape of the Deadly Foe'), +(3, 2, 15, 0, 224, 40388, 'Phase 1', 'Hunter', 'Survival', 'MainHand', 'Both', 'Journey''s End'), +(3, 2, 17, 0, 224, 40385, 'Phase 1', 'Hunter', 'Survival', 'Ranged', 'Both', 'Envoy of Mortality'); + +-- ilvl 245 (Phase 2) +INSERT INTO `playerbots_bis_gear` VALUES +(3, 2, 0, 0, 245, 45610, 'Phase 2', 'Hunter', 'Survival', 'Head', 'Both', 'Boundless Gaze'), +(3, 2, 1, 0, 245, 45517, 'Phase 2', 'Hunter', 'Survival', 'Neck', 'Both', 'Pendulum of Infinity'), +(3, 2, 2, 0, 245, 45300, 'Phase 2', 'Hunter', 'Survival', 'Shoulders', 'Both', 'Mantle of Fiery Vengeance'), +(3, 2, 4, 0, 245, 45473, 'Phase 2', 'Hunter', 'Survival', 'Chest', 'Both', 'Embrace of the Gladiator'), +(3, 2, 5, 0, 245, 46095, 'Phase 2', 'Hunter', 'Survival', 'Waist', 'Both', 'Soul-Devouring Cinch'), +(3, 2, 6, 0, 245, 45536, 'Phase 2', 'Hunter', 'Survival', 'Legs', 'Both', 'Legguards of Cunning Deception'), +(3, 2, 7, 0, 245, 45244, 'Phase 2', 'Hunter', 'Survival', 'Feet', 'Both', 'Greaves of Swift Vengeance'), +(3, 2, 8, 0, 245, 45869, 'Phase 2', 'Hunter', 'Survival', 'Wrists', 'Both', 'Fluxing Energy Coils'), +(3, 2, 9, 0, 245, 45444, 'Phase 2', 'Hunter', 'Survival', 'Hands', 'Both', 'Gloves of the Steady Hand'), +(3, 2, 12, 0, 245, 44253, 'Phase 2', 'Hunter', 'Survival', 'Trinket1', 'Both', 'Darkmoon Card: Greatness'), +(3, 2, 14, 0, 245, 46032, 'Phase 2', 'Hunter', 'Survival', 'Back', 'Both', 'Drape of the Faceless General'); + +-- ilvl 258 (Phase 3) +INSERT INTO `playerbots_bis_gear` VALUES +(3, 2, 0, 0, 258, 48262, 'Phase 3', 'Hunter', 'Survival', 'Head', 'Both', 'Windrunner''s Headpiece of Triumph'), +(3, 2, 1, 1, 258, 47060, 'Phase 3', 'Hunter', 'Survival', 'Neck', 'Alliance', 'Charge of the Demon Lord'), +(3, 2, 1, 2, 258, 47433, 'Phase 3', 'Hunter', 'Survival', 'Neck', 'Horde', 'Charge of the Eredar'), +(3, 2, 2, 0, 258, 48260, 'Phase 3', 'Hunter', 'Survival', 'Shoulders', 'Both', 'Windrunner''s Spaulders of Triumph'), +(3, 2, 4, 0, 258, 48264, 'Phase 3', 'Hunter', 'Survival', 'Chest', 'Both', 'Windrunner''s Tunic of Triumph'), +(3, 2, 5, 1, 258, 47153, 'Phase 3', 'Hunter', 'Survival', 'Waist', 'Alliance', 'Belt of Deathly Dominion'), +(3, 2, 5, 2, 258, 47472, 'Phase 3', 'Hunter', 'Survival', 'Waist', 'Horde', 'Waistguard of Deathly Dominion'), +(3, 2, 6, 1, 258, 47191, 'Phase 3', 'Hunter', 'Survival', 'Legs', 'Alliance', 'Legguards of the Lurking Threat'), +(3, 2, 6, 2, 258, 47480, 'Phase 3', 'Hunter', 'Survival', 'Legs', 'Horde', 'Leggings of the Lurking Threat'), +(3, 2, 7, 1, 258, 47109, 'Phase 3', 'Hunter', 'Survival', 'Feet', 'Alliance', 'Sabatons of Ruthless Judgment'), +(3, 2, 7, 2, 258, 47457, 'Phase 3', 'Hunter', 'Survival', 'Feet', 'Horde', 'Greaves of Ruthless Judgment'), +(3, 2, 8, 1, 258, 47074, 'Phase 3', 'Hunter', 'Survival', 'Wrists', 'Alliance', 'Bracers of the Untold Massacre'), +(3, 2, 8, 2, 258, 47442, 'Phase 3', 'Hunter', 'Survival', 'Wrists', 'Horde', 'Bracers of the Silent Massacre'), +(3, 2, 9, 0, 258, 48263, 'Phase 3', 'Hunter', 'Survival', 'Hands', 'Both', 'Windrunner''s Handguards of Triumph'), +(3, 2, 10, 1, 258, 47075, 'Phase 3', 'Hunter', 'Survival', 'Finger1', 'Alliance', 'Ring of Callous Aggression'), +(3, 2, 10, 2, 258, 47443, 'Phase 3', 'Hunter', 'Survival', 'Finger1', 'Horde', 'Band of Callous Aggression'), +(3, 2, 12, 2, 258, 44253, 'Phase 3', 'Hunter', 'Survival', 'Trinket1', 'Horde', 'Darkmoon Card: Greatness'), +(3, 2, 14, 1, 258, 47545, 'Phase 3', 'Hunter', 'Survival', 'Back', 'Alliance', 'Vereesa''s Dexterity'), +(3, 2, 14, 2, 258, 47546, 'Phase 3', 'Hunter', 'Survival', 'Back', 'Horde', 'Sylvanas'' Cunning'), +(3, 2, 17, 1, 258, 47521, 'Phase 3', 'Hunter', 'Survival', 'Ranged', 'Alliance', 'BRK 1000'), +(3, 2, 17, 2, 258, 47523, 'Phase 3', 'Hunter', 'Survival', 'Ranged', 'Horde', 'Fezzik''s Autocannon'); + +-- ilvl 264 (Phase 4) +INSERT INTO `playerbots_bis_gear` VALUES +(3, 2, 0, 0, 264, 51286, 'Phase 4', 'Hunter', 'Survival', 'Head', 'Both', 'Sanctified Ahn''Kahar Blood Hunter''s Headpiece'), +(3, 2, 1, 0, 264, 50633, 'Phase 4', 'Hunter', 'Survival', 'Neck', 'Both', 'Sindragosa''s Cruel Claw'), +(3, 2, 2, 0, 264, 51288, 'Phase 4', 'Hunter', 'Survival', 'Shoulders', 'Both', 'Sanctified Ahn''Kahar Blood Hunter''s Spaulders'), +(3, 2, 4, 0, 264, 51289, 'Phase 4', 'Hunter', 'Survival', 'Chest', 'Both', 'Sanctified Ahn''Kahar Blood Hunter''s Tunic'), +(3, 2, 5, 0, 264, 50688, 'Phase 4', 'Hunter', 'Survival', 'Waist', 'Both', 'Nerub''ar Stalker''s Cord'), +(3, 2, 6, 0, 264, 50645, 'Phase 4', 'Hunter', 'Survival', 'Legs', 'Both', 'Leggings of Northern Lights'), +(3, 2, 7, 0, 264, 50607, 'Phase 4', 'Hunter', 'Survival', 'Feet', 'Both', 'Frostbitten Fur Boots'), +(3, 2, 8, 0, 264, 50655, 'Phase 4', 'Hunter', 'Survival', 'Wrists', 'Both', 'Scourge Hunter''s Vambraces'), +(3, 2, 9, 0, 264, 51285, 'Phase 4', 'Hunter', 'Survival', 'Hands', 'Both', 'Sanctified Ahn''Kahar Blood Hunter''s Handguards'), +(3, 2, 10, 0, 264, 50402, 'Phase 4', 'Hunter', 'Survival', 'Finger1', 'Both', 'Ashen Band of Endless Vengeance'), +(3, 2, 12, 1, 264, 47131, 'Phase 4', 'Hunter', 'Survival', 'Trinket1', 'Alliance', 'Death''s Verdict'), +(3, 2, 12, 2, 264, 47464, 'Phase 4', 'Hunter', 'Survival', 'Trinket1', 'Horde', 'Death''s Choice'), +(3, 2, 14, 1, 264, 47545, 'Phase 4', 'Hunter', 'Survival', 'Back', 'Alliance', 'Vereesa''s Dexterity'), +(3, 2, 14, 2, 264, 47546, 'Phase 4', 'Hunter', 'Survival', 'Back', 'Horde', 'Sylvanas'' Cunning'), +(3, 2, 17, 0, 264, 50733, 'Phase 4', 'Hunter', 'Survival', 'Ranged', 'Both', 'Fal''inrush, Defender of Quel''thalas'); + + +-- ============================================================ +-- Rogue (4) +-- ============================================================ +-- Assassination (tab 0) +-- ilvl 120 (Pre-Raid) +INSERT INTO `playerbots_bis_gear` VALUES +(4, 0, 0, 0, 120, 32087, 'Pre-Raid', 'Rogue', 'Assassination', 'Head', 'Both', 'Mask of the Deceiver'), +(4, 0, 1, 0, 120, 29381, 'Pre-Raid', 'Rogue', 'Assassination', 'Neck', 'Both', 'Choker of Vile Intent'), +(4, 0, 2, 0, 120, 27797, 'Pre-Raid', 'Rogue', 'Assassination', 'Shoulders', 'Both', 'Wastewalker Shoulderpads'), +(4, 0, 4, 0, 120, 30730, 'Pre-Raid', 'Rogue', 'Assassination', 'Chest', 'Both', 'Terrorweave Tunic'), +(4, 0, 5, 0, 120, 29247, 'Pre-Raid', 'Rogue', 'Assassination', 'Waist', 'Both', 'Girdle of the Deathdealer'), +(4, 0, 6, 0, 120, 30538, 'Pre-Raid', 'Rogue', 'Assassination', 'Legs', 'Both', 'Midnight Legguards'), +(4, 0, 7, 0, 120, 25686, 'Pre-Raid', 'Rogue', 'Assassination', 'Feet', 'Both', 'Fel Leather Boots'), +(4, 0, 8, 0, 120, 29246, 'Pre-Raid', 'Rogue', 'Assassination', 'Wrists', 'Both', 'Nightfall Wristguards'), +(4, 0, 9, 0, 120, 27531, 'Pre-Raid', 'Rogue', 'Assassination', 'Hands', 'Both', 'Wastewalker Gloves'), +(4, 0, 10, 0, 120, 30738, 'Pre-Raid', 'Rogue', 'Assassination', 'Finger1', 'Both', 'Ring of Reciprocity'), +(4, 0, 12, 0, 120, 28288, 'Pre-Raid', 'Rogue', 'Assassination', 'Trinket1', 'Both', 'Abacus of Violent Odds'), +(4, 0, 13, 0, 120, 29383, 'Pre-Raid', 'Rogue', 'Assassination', 'Trinket2', 'Both', 'Bloodlust Brooch'), +(4, 0, 14, 0, 120, 24259, 'Pre-Raid', 'Rogue', 'Assassination', 'Back', 'Both', 'Vengeance Wrap'), +(4, 0, 15, 0, 120, 31331, 'Pre-Raid', 'Rogue', 'Assassination', 'MainHand', 'Both', 'The Night Blade'), +(4, 0, 16, 0, 120, 29346, 'Pre-Raid', 'Rogue', 'Assassination', 'OffHand', 'Both', 'Feltooth Eviscerator'), +(4, 0, 17, 2, 120, 29152, 'Pre-Raid', 'Rogue', 'Assassination', 'Ranged', 'Horde', 'Marksman''s Bow'); + +-- ilvl 125 (Phase 1) +INSERT INTO `playerbots_bis_gear` VALUES +(4, 0, 0, 0, 125, 29044, 'Phase 1', 'Rogue', 'Assassination', 'Head', 'Both', 'Netherblade Facemask'), +(4, 0, 1, 0, 125, 29381, 'Phase 1', 'Rogue', 'Assassination', 'Neck', 'Both', 'Choker of Vile Intent'), +(4, 0, 2, 0, 125, 27797, 'Phase 1', 'Rogue', 'Assassination', 'Shoulders', 'Both', 'Wastewalker Shoulderpads'), +(4, 0, 4, 0, 125, 29045, 'Phase 1', 'Rogue', 'Assassination', 'Chest', 'Both', 'Netherblade Chestpiece'), +(4, 0, 5, 0, 125, 29247, 'Phase 1', 'Rogue', 'Assassination', 'Waist', 'Both', 'Girdle of the Deathdealer'), +(4, 0, 6, 0, 125, 28741, 'Phase 1', 'Rogue', 'Assassination', 'Legs', 'Both', 'Skulker''s Greaves'), +(4, 0, 7, 0, 125, 28545, 'Phase 1', 'Rogue', 'Assassination', 'Feet', 'Both', 'Edgewalker Longboots'), +(4, 0, 8, 0, 125, 29246, 'Phase 1', 'Rogue', 'Assassination', 'Wrists', 'Both', 'Nightfall Wristguards'), +(4, 0, 9, 0, 125, 27531, 'Phase 1', 'Rogue', 'Assassination', 'Hands', 'Both', 'Wastewalker Gloves'), +(4, 0, 10, 0, 125, 28649, 'Phase 1', 'Rogue', 'Assassination', 'Finger1', 'Both', 'Garona''s Signet Ring'), +(4, 0, 11, 0, 125, 28757, 'Phase 1', 'Rogue', 'Assassination', 'Finger2', 'Both', 'Ring of a Thousand Marks'), +(4, 0, 12, 0, 125, 29383, 'Phase 1', 'Rogue', 'Assassination', 'Trinket1', 'Both', 'Bloodlust Brooch'), +(4, 0, 13, 0, 125, 28830, 'Phase 1', 'Rogue', 'Assassination', 'Trinket2', 'Both', 'Dragonspine Trophy'), +(4, 0, 14, 0, 125, 28672, 'Phase 1', 'Rogue', 'Assassination', 'Back', 'Both', 'Drape of the Dark Reavers'), +(4, 0, 15, 0, 125, 28312, 'Phase 1', 'Rogue', 'Assassination', 'MainHand', 'Both', 'Gladiator''s Shanker'), +(4, 0, 16, 0, 125, 29346, 'Phase 1', 'Rogue', 'Assassination', 'OffHand', 'Both', 'Feltooth Eviscerator'), +(4, 0, 17, 0, 125, 28772, 'Phase 1', 'Rogue', 'Assassination', 'Ranged', 'Both', 'Sunfury Bow of the Phoenix'); + +-- ilvl 200 (Pre-Raid) +INSERT INTO `playerbots_bis_gear` VALUES +(4, 0, 0, 0, 200, 42550, 'Pre-Raid', 'Rogue', 'Assassination', 'Head', 'Both', 'Weakness Spectralizers'), +(4, 0, 1, 0, 200, 40678, 'Pre-Raid', 'Rogue', 'Assassination', 'Neck', 'Both', 'Pendant of the Outcast Hero'), +(4, 0, 2, 0, 200, 43481, 'Pre-Raid', 'Rogue', 'Assassination', 'Shoulders', 'Both', 'Trollwoven Spaulders'), +(4, 0, 4, 0, 200, 39558, 'Pre-Raid', 'Rogue', 'Assassination', 'Chest', 'Both', 'Heroes'' Bonescythe Breastplate'), +(4, 0, 5, 0, 200, 40694, 'Pre-Raid', 'Rogue', 'Assassination', 'Waist', 'Both', 'Jorach''s Crocolisk Skin Belt'), +(4, 0, 8, 0, 200, 34448, 'Pre-Raid', 'Rogue', 'Assassination', 'Wrists', 'Both', 'Slayer''s Bracers'), +(4, 0, 9, 0, 200, 39560, 'Pre-Raid', 'Rogue', 'Assassination', 'Hands', 'Both', 'Heroes'' Bonescythe Gauntlets'), +(4, 0, 12, 0, 200, 40684, 'Pre-Raid', 'Rogue', 'Assassination', 'Trinket1', 'Both', 'Mirror of Truth'), +(4, 0, 14, 0, 200, 43566, 'Pre-Raid', 'Rogue', 'Assassination', 'Back', 'Both', 'Ice Striker''s Cloak'), +(4, 0, 15, 0, 200, 37856, 'Pre-Raid', 'Rogue', 'Assassination', 'MainHand', 'Both', 'Librarian''s Paper Cutter'), +(4, 0, 17, 0, 200, 44504, 'Pre-Raid', 'Rogue', 'Assassination', 'Ranged', 'Both', 'Nesingwary 4000'); + +-- ilvl 224 (Phase 1) +INSERT INTO `playerbots_bis_gear` VALUES +(4, 0, 0, 0, 224, 43729, 'Phase 1', 'Rogue', 'Assassination', 'Head', 'Both', 'Valorous Bonescythe Helmet'), +(4, 0, 1, 0, 224, 44664, 'Phase 1', 'Rogue', 'Assassination', 'Neck', 'Both', 'Favor of the Dragon Queen'), +(4, 0, 2, 0, 224, 40502, 'Phase 1', 'Rogue', 'Assassination', 'Shoulders', 'Both', 'Valorous Bonescythe Pauldrons'), +(4, 0, 4, 0, 224, 40539, 'Phase 1', 'Rogue', 'Assassination', 'Chest', 'Both', 'Chestguard of the Recluse'), +(4, 0, 5, 0, 224, 40205, 'Phase 1', 'Rogue', 'Assassination', 'Waist', 'Both', 'Stalk-Skin Belt'), +(4, 0, 6, 0, 224, 44011, 'Phase 1', 'Rogue', 'Assassination', 'Legs', 'Both', 'Leggings of the Honored'), +(4, 0, 7, 0, 224, 39701, 'Phase 1', 'Rogue', 'Assassination', 'Feet', 'Both', 'Dawnwalkers'), +(4, 0, 8, 0, 224, 39765, 'Phase 1', 'Rogue', 'Assassination', 'Wrists', 'Both', 'Sinner''s Bindings'), +(4, 0, 9, 0, 224, 40541, 'Phase 1', 'Rogue', 'Assassination', 'Hands', 'Both', 'Frosted Adroit Handguards'), +(4, 0, 10, 0, 224, 40474, 'Phase 1', 'Rogue', 'Assassination', 'Finger1', 'Both', 'Surge Needle Ring'), +(4, 0, 12, 0, 224, 44253, 'Phase 1', 'Rogue', 'Assassination', 'Trinket1', 'Both', 'Darkmoon Card: Greatness'), +(4, 0, 14, 0, 224, 40403, 'Phase 1', 'Rogue', 'Assassination', 'Back', 'Both', 'Drape of the Deadly Foe'), +(4, 0, 15, 0, 224, 39714, 'Phase 1', 'Rogue', 'Assassination', 'MainHand', 'Both', 'Webbed Death'), +(4, 0, 17, 0, 224, 40385, 'Phase 1', 'Rogue', 'Assassination', 'Ranged', 'Both', 'Envoy of Mortality'); + +-- ilvl 245 (Phase 2) +INSERT INTO `playerbots_bis_gear` VALUES +(4, 0, 0, 0, 245, 46125, 'Phase 2', 'Rogue', 'Assassination', 'Head', 'Both', 'Conqueror''s Terrorblade Helmet'), +(4, 0, 1, 0, 245, 45517, 'Phase 2', 'Rogue', 'Assassination', 'Neck', 'Both', 'Pendulum of Infinity'), +(4, 0, 2, 0, 245, 45245, 'Phase 2', 'Rogue', 'Assassination', 'Shoulders', 'Both', 'Shoulderpads of the Intruder'), +(4, 0, 4, 0, 245, 45473, 'Phase 2', 'Rogue', 'Assassination', 'Chest', 'Both', 'Embrace of the Gladiator'), +(4, 0, 5, 0, 245, 46095, 'Phase 2', 'Rogue', 'Assassination', 'Waist', 'Both', 'Soul-Devouring Cinch'), +(4, 0, 6, 0, 245, 45536, 'Phase 2', 'Rogue', 'Assassination', 'Legs', 'Both', 'Legguards of Cunning Deception'), +(4, 0, 7, 0, 245, 45564, 'Phase 2', 'Rogue', 'Assassination', 'Feet', 'Both', 'Footpads of Silence'), +(4, 0, 8, 0, 245, 45611, 'Phase 2', 'Rogue', 'Assassination', 'Wrists', 'Both', 'Solar Bindings'), +(4, 0, 9, 0, 245, 46124, 'Phase 2', 'Rogue', 'Assassination', 'Hands', 'Both', 'Conqueror''s Terrorblade Gauntlets'), +(4, 0, 12, 0, 245, 45609, 'Phase 2', 'Rogue', 'Assassination', 'Trinket1', 'Both', 'Comet''s Trail'), +(4, 0, 14, 0, 245, 45461, 'Phase 2', 'Rogue', 'Assassination', 'Back', 'Both', 'Drape of Icy Intent'), +(4, 0, 17, 0, 245, 45570, 'Phase 2', 'Rogue', 'Assassination', 'Ranged', 'Both', 'Skyforge Crossbow'); + +-- ilvl 258 (Phase 3) +INSERT INTO `playerbots_bis_gear` VALUES +(4, 0, 0, 0, 258, 48230, 'Phase 3', 'Rogue', 'Assassination', 'Head', 'Both', 'Helmet of Triumph'), +(4, 0, 2, 0, 258, 48228, 'Phase 3', 'Rogue', 'Assassination', 'Shoulders', 'Both', 'Pauldrons of Triumph'), +(4, 0, 4, 0, 258, 48232, 'Phase 3', 'Rogue', 'Assassination', 'Chest', 'Both', 'Breastplate of Triumph'), +(4, 0, 5, 1, 258, 47112, 'Phase 3', 'Rogue', 'Assassination', 'Waist', 'Alliance', 'Belt of the Merciless Killer'), +(4, 0, 5, 2, 258, 47460, 'Phase 3', 'Rogue', 'Assassination', 'Waist', 'Horde', 'Belt of the Pitiless Killer'), +(4, 0, 6, 1, 258, 46975, 'Phase 3', 'Rogue', 'Assassination', 'Legs', 'Alliance', 'Leggings of the Broken Beast'), +(4, 0, 6, 2, 258, 47420, 'Phase 3', 'Rogue', 'Assassination', 'Legs', 'Horde', 'Legwraps of the Broken Beast'), +(4, 0, 7, 1, 258, 47077, 'Phase 3', 'Rogue', 'Assassination', 'Feet', 'Alliance', 'Treads of the Icewalker'), +(4, 0, 7, 2, 258, 47445, 'Phase 3', 'Rogue', 'Assassination', 'Feet', 'Horde', 'Icewalker Treads'), +(4, 0, 8, 1, 258, 47155, 'Phase 3', 'Rogue', 'Assassination', 'Wrists', 'Alliance', 'Bracers of Dark Determination'), +(4, 0, 8, 2, 258, 47474, 'Phase 3', 'Rogue', 'Assassination', 'Wrists', 'Horde', 'Armbands of Dark Determination'), +(4, 0, 9, 0, 258, 48231, 'Phase 3', 'Rogue', 'Assassination', 'Hands', 'Both', 'Gauntlets of Triumph'), +(4, 0, 10, 1, 258, 47075, 'Phase 3', 'Rogue', 'Assassination', 'Finger1', 'Alliance', 'Ring of Callous Aggression'), +(4, 0, 10, 2, 258, 47443, 'Phase 3', 'Rogue', 'Assassination', 'Finger1', 'Horde', 'Band of Callous Aggression'), +(4, 0, 12, 1, 258, 47131, 'Phase 3', 'Rogue', 'Assassination', 'Trinket1', 'Alliance', 'Death''s Verdict'), +(4, 0, 12, 2, 258, 47464, 'Phase 3', 'Rogue', 'Assassination', 'Trinket1', 'Horde', 'Death''s Choice'), +(4, 0, 14, 1, 258, 47545, 'Phase 3', 'Rogue', 'Assassination', 'Back', 'Alliance', 'Vereesa''s Dexterity'), +(4, 0, 14, 2, 258, 47546, 'Phase 3', 'Rogue', 'Assassination', 'Back', 'Horde', 'Sylvanas'' Cunning'), +(4, 0, 17, 1, 258, 47521, 'Phase 3', 'Rogue', 'Assassination', 'Ranged', 'Alliance', 'BRK 1000'), +(4, 0, 17, 2, 258, 47523, 'Phase 3', 'Rogue', 'Assassination', 'Ranged', 'Horde', 'Fezzik''s Autocannon'); + +-- ilvl 264 (Phase 4) +INSERT INTO `playerbots_bis_gear` VALUES +(4, 0, 0, 0, 264, 51252, 'Phase 4', 'Rogue', 'Assassination', 'Head', 'Both', 'Sanctified Shadowblade Helmet'), +(4, 0, 2, 0, 264, 51254, 'Phase 4', 'Rogue', 'Assassination', 'Shoulders', 'Both', 'Sanctified Shadowblade Pauldrons'), +(4, 0, 4, 0, 264, 50656, 'Phase 4', 'Rogue', 'Assassination', 'Chest', 'Both', 'Ikfirus''s Sack of Wonder'), +(4, 0, 5, 0, 264, 50707, 'Phase 4', 'Rogue', 'Assassination', 'Waist', 'Both', 'Astrylian''s Sutured Cinch'), +(4, 0, 6, 0, 264, 51253, 'Phase 4', 'Rogue', 'Assassination', 'Legs', 'Both', 'Sanctified Shadowblade Legplates'), +(4, 0, 7, 0, 264, 50607, 'Phase 4', 'Rogue', 'Assassination', 'Feet', 'Both', 'Frostbitten Fur Boots'), +(4, 0, 8, 0, 264, 50670, 'Phase 4', 'Rogue', 'Assassination', 'Wrists', 'Both', 'Toskk''s Maximized Wristguards'), +(4, 0, 9, 0, 264, 51251, 'Phase 4', 'Rogue', 'Assassination', 'Hands', 'Both', 'Sanctified Shadowblade Gauntlets'), +(4, 0, 10, 0, 264, 50604, 'Phase 4', 'Rogue', 'Assassination', 'Finger1', 'Both', 'Band of the Bone Colossus'), +(4, 0, 12, 0, 264, 50363, 'Phase 4', 'Rogue', 'Assassination', 'Trinket1', 'Both', 'Deathbringer''s Will'), +(4, 0, 14, 0, 264, 50653, 'Phase 4', 'Rogue', 'Assassination', 'Back', 'Both', 'Shadowvault Slayer''s Cloak'), +(4, 0, 17, 0, 264, 50733, 'Phase 4', 'Rogue', 'Assassination', 'Ranged', 'Both', 'Fal''inrush, Defender of Quel''thalas'); + +-- ilvl 290 (Phase 5) +INSERT INTO `playerbots_bis_gear` VALUES +(4, 0, 0, 0, 290, 50713, 'Phase 5', 'Rogue', 'Assassination', 'Head', 'Both', 'Geistlord''s Punishment Sack'), +(4, 0, 1, 0, 290, 50633, 'Phase 5', 'Rogue', 'Assassination', 'Neck', 'Both', 'Sindragosa''s Cruel Claw'), +(4, 0, 2, 0, 290, 50646, 'Phase 5', 'Rogue', 'Assassination', 'Shoulders', 'Both', 'Cultist''s Bloodsoaked Spaulders'), +(4, 0, 4, 0, 290, 50656, 'Phase 5', 'Rogue', 'Assassination', 'Chest', 'Both', 'Ikfirus''s Sack of Wonder'), +(4, 0, 5, 0, 290, 47112, 'Phase 5', 'Rogue', 'Assassination', 'Waist', 'Both', 'Belt of the Merciless Killer'), +(4, 0, 6, 0, 290, 51253, 'Phase 5', 'Rogue', 'Assassination', 'Legs', 'Both', 'Sanctified Shadowblade Legplates'), +(4, 0, 7, 0, 290, 50607, 'Phase 5', 'Rogue', 'Assassination', 'Feet', 'Both', 'Frostbitten Fur Boots'), +(4, 0, 8, 0, 290, 54580, 'Phase 5', 'Rogue', 'Assassination', 'Wrists', 'Both', 'Umbrage Armbands'), +(4, 0, 9, 0, 290, 51251, 'Phase 5', 'Rogue', 'Assassination', 'Hands', 'Both', 'Sanctified Shadowblade Gauntlets'), +(4, 0, 10, 0, 290, 50402, 'Phase 5', 'Rogue', 'Assassination', 'Finger1', 'Both', 'Ashen Band of Endless Vengeance'), +(4, 0, 11, 0, 290, 54576, 'Phase 5', 'Rogue', 'Assassination', 'Finger2', 'Both', 'Signet of Twilight'), +(4, 0, 12, 0, 290, 54590, 'Phase 5', 'Rogue', 'Assassination', 'Trinket1', 'Both', 'Sharpened Twilight Scale'), +(4, 0, 13, 0, 290, 50706, 'Phase 5', 'Rogue', 'Assassination', 'Trinket2', 'Both', 'Tiny Abomination in a Jar'), +(4, 0, 14, 0, 290, 50653, 'Phase 5', 'Rogue', 'Assassination', 'Back', 'Both', 'Shadowvault Slayer''s Cloak'), +(4, 0, 15, 0, 290, 50736, 'Phase 5', 'Rogue', 'Assassination', 'MainHand', 'Both', 'Heaven''s Fall, Kryss of a Thousand Lies'), +(4, 0, 16, 0, 290, 50621, 'Phase 5', 'Rogue', 'Assassination', 'OffHand', 'Both', 'Lungbreaker'), +(4, 0, 17, 0, 290, 50733, 'Phase 5', 'Rogue', 'Assassination', 'Ranged', 'Both', 'Fal''inrush, Defender of Quel''thalas'); + +-- Combat (tab 1) +-- ilvl 66 (Phase 1 (Pre-Raid)) +INSERT INTO `playerbots_bis_gear` VALUES +(4, 1, 0, 0, 66, 13404, 'Phase 1 (Pre-Raid)', 'Rogue', 'Combat', 'Head', 'Both', 'Mask of the Unforgiven'), +(4, 1, 1, 0, 66, 15411, 'Phase 1 (Pre-Raid)', 'Rogue', 'Combat', 'Neck', 'Both', 'Mark of Fordring'), +(4, 1, 2, 0, 66, 12927, 'Phase 1 (Pre-Raid)', 'Rogue', 'Combat', 'Shoulders', 'Both', 'Truestrike Shoulders'), +(4, 1, 4, 0, 66, 14637, 'Phase 1 (Pre-Raid)', 'Rogue', 'Combat', 'Chest', 'Both', 'Cadaverous Armor'), +(4, 1, 5, 0, 66, 13252, 'Phase 1 (Pre-Raid)', 'Rogue', 'Combat', 'Waist', 'Both', 'Cloudrunner Girdle'), +(4, 1, 6, 0, 66, 15062, 'Phase 1 (Pre-Raid)', 'Rogue', 'Combat', 'Legs', 'Both', 'Devilsaur Leggings'), +(4, 1, 7, 0, 66, 12553, 'Phase 1 (Pre-Raid)', 'Rogue', 'Combat', 'Feet', 'Both', 'Swiftwalker Boots'), +(4, 1, 8, 0, 66, 13120, 'Phase 1 (Pre-Raid)', 'Rogue', 'Combat', 'Wrists', 'Both', 'Deepfury Bracers'), +(4, 1, 9, 0, 66, 15063, 'Phase 1 (Pre-Raid)', 'Rogue', 'Combat', 'Hands', 'Both', 'Devilsaur Gauntlets'), +(4, 1, 10, 0, 66, 13098, 'Phase 1 (Pre-Raid)', 'Rogue', 'Combat', 'Finger1', 'Both', 'Painweaver Band'), +(4, 1, 11, 0, 66, 17713, 'Phase 1 (Pre-Raid)', 'Rogue', 'Combat', 'Finger2', 'Both', 'Blackstone Ring'), +(4, 1, 12, 0, 66, 13965, 'Phase 1 (Pre-Raid)', 'Rogue', 'Combat', 'Trinket1', 'Both', 'Blackhand''s Breadth'), +(4, 1, 13, 0, 66, 11815, 'Phase 1 (Pre-Raid)', 'Rogue', 'Combat', 'Trinket2', 'Both', 'Hand of Justice'), +(4, 1, 14, 0, 66, 13340, 'Phase 1 (Pre-Raid)', 'Rogue', 'Combat', 'Back', 'Both', 'Cape of the Black Baron'), +(4, 1, 15, 0, 66, 12783, 'Phase 1 (Pre-Raid)', 'Rogue', 'Combat', 'MainHand', 'Both', 'Heartseeker'), +(4, 1, 16, 0, 66, 14555, 'Phase 1 (Pre-Raid)', 'Rogue', 'Combat', 'OffHand', 'Both', 'Alcor''s Sunrazor'), +(4, 1, 17, 0, 66, 12651, 'Phase 1 (Pre-Raid)', 'Rogue', 'Combat', 'Ranged', 'Both', 'Blackcrow'); + +-- ilvl 76 (Phase 2 (Pre-Raid)) +INSERT INTO `playerbots_bis_gear` VALUES +(4, 1, 0, 0, 76, 13404, 'Phase 2 (Pre-Raid)', 'Rogue', 'Combat', 'Head', 'Both', 'Mask of the Unforgiven'), +(4, 1, 1, 0, 76, 15411, 'Phase 2 (Pre-Raid)', 'Rogue', 'Combat', 'Neck', 'Both', 'Mark of Fordring'), +(4, 1, 2, 0, 76, 12927, 'Phase 2 (Pre-Raid)', 'Rogue', 'Combat', 'Shoulders', 'Both', 'Truestrike Shoulders'), +(4, 1, 4, 0, 76, 14637, 'Phase 2 (Pre-Raid)', 'Rogue', 'Combat', 'Chest', 'Both', 'Cadaverous Armor'), +(4, 1, 5, 0, 76, 18505, 'Phase 2 (Pre-Raid)', 'Rogue', 'Combat', 'Waist', 'Both', 'Mugger''s Belt'), +(4, 1, 6, 0, 76, 15062, 'Phase 2 (Pre-Raid)', 'Rogue', 'Combat', 'Legs', 'Both', 'Devilsaur Leggings'), +(4, 1, 7, 0, 76, 12553, 'Phase 2 (Pre-Raid)', 'Rogue', 'Combat', 'Feet', 'Both', 'Swiftwalker Boots'), +(4, 1, 8, 0, 76, 18375, 'Phase 2 (Pre-Raid)', 'Rogue', 'Combat', 'Wrists', 'Both', 'Bracers of the Eclipse'), +(4, 1, 9, 0, 76, 15063, 'Phase 2 (Pre-Raid)', 'Rogue', 'Combat', 'Hands', 'Both', 'Devilsaur Gauntlets'), +(4, 1, 10, 0, 76, 18500, 'Phase 2 (Pre-Raid)', 'Rogue', 'Combat', 'Finger1', 'Both', 'Tarnished Elven Ring'), +(4, 1, 11, 0, 76, 18500, 'Phase 2 (Pre-Raid)', 'Rogue', 'Combat', 'Finger2', 'Both', 'Tarnished Elven Ring'), +(4, 1, 12, 0, 76, 13965, 'Phase 2 (Pre-Raid)', 'Rogue', 'Combat', 'Trinket1', 'Both', 'Blackhand''s Breadth'), +(4, 1, 13, 0, 76, 11815, 'Phase 2 (Pre-Raid)', 'Rogue', 'Combat', 'Trinket2', 'Both', 'Hand of Justice'), +(4, 1, 14, 0, 76, 13340, 'Phase 2 (Pre-Raid)', 'Rogue', 'Combat', 'Back', 'Both', 'Cape of the Black Baron'), +(4, 1, 15, 0, 76, 12783, 'Phase 2 (Pre-Raid)', 'Rogue', 'Combat', 'MainHand', 'Both', 'Heartseeker'), +(4, 1, 16, 0, 76, 14555, 'Phase 2 (Pre-Raid)', 'Rogue', 'Combat', 'OffHand', 'Both', 'Alcor''s Sunrazor'), +(4, 1, 17, 0, 76, 18323, 'Phase 2 (Pre-Raid)', 'Rogue', 'Combat', 'Ranged', 'Both', 'Satyr''s Bow'); + +-- ilvl 78 (Phase 2) +INSERT INTO `playerbots_bis_gear` VALUES +(4, 1, 0, 0, 78, 16908, 'Phase 2', 'Rogue', 'Combat', 'Head', 'Both', 'Bloodfang Hood'), +(4, 1, 1, 0, 78, 18404, 'Phase 2', 'Rogue', 'Combat', 'Neck', 'Both', 'Onyxia Tooth Pendant'), +(4, 1, 2, 0, 78, 16823, 'Phase 2', 'Rogue', 'Combat', 'Shoulders', 'Both', 'Nightslayer Shoulder Pads'), +(4, 1, 4, 0, 78, 16820, 'Phase 2', 'Rogue', 'Combat', 'Chest', 'Both', 'Nightslayer Chestpiece'), +(4, 1, 5, 0, 78, 16827, 'Phase 2', 'Rogue', 'Combat', 'Waist', 'Both', 'Nightslayer Belt'), +(4, 1, 6, 0, 78, 16909, 'Phase 2', 'Rogue', 'Combat', 'Legs', 'Both', 'Bloodfang Pants'), +(4, 1, 7, 0, 78, 16824, 'Phase 2', 'Rogue', 'Combat', 'Feet', 'Both', 'Nightslayer Boots'), +(4, 1, 8, 0, 78, 18375, 'Phase 2', 'Rogue', 'Combat', 'Wrists', 'Both', 'Bracers of the Eclipse'), +(4, 1, 9, 0, 78, 18823, 'Phase 2', 'Rogue', 'Combat', 'Hands', 'Both', 'Aged Core Leather Gloves'), +(4, 1, 10, 0, 78, 17063, 'Phase 2', 'Rogue', 'Combat', 'Finger1', 'Both', 'Band of Accuria'), +(4, 1, 11, 0, 78, 18500, 'Phase 2', 'Rogue', 'Combat', 'Finger2', 'Both', 'Tarnished Elven Ring'), +(4, 1, 12, 0, 78, 13965, 'Phase 2', 'Rogue', 'Combat', 'Trinket1', 'Both', 'Blackhand''s Breadth'), +(4, 1, 13, 0, 78, 11815, 'Phase 2', 'Rogue', 'Combat', 'Trinket2', 'Both', 'Hand of Justice'), +(4, 1, 14, 0, 78, 13340, 'Phase 2', 'Rogue', 'Combat', 'Back', 'Both', 'Cape of the Black Baron'), +(4, 1, 15, 0, 78, 18816, 'Phase 2', 'Rogue', 'Combat', 'MainHand', 'Both', 'Perdition''s Blade'), +(4, 1, 16, 0, 78, 18805, 'Phase 2', 'Rogue', 'Combat', 'OffHand', 'Both', 'Core Hound Tooth'), +(4, 1, 17, 0, 78, 17069, 'Phase 2', 'Rogue', 'Combat', 'Ranged', 'Both', 'Striker''s Mark'); + +-- ilvl 83 (Phase 3) +INSERT INTO `playerbots_bis_gear` VALUES +(4, 1, 0, 0, 83, 16908, 'Phase 3', 'Rogue', 'Combat', 'Head', 'Both', 'Bloodfang Hood'), +(4, 1, 1, 0, 83, 19377, 'Phase 3', 'Rogue', 'Combat', 'Neck', 'Both', 'Prestor''s Talisman of Connivery'), +(4, 1, 2, 0, 83, 16823, 'Phase 3', 'Rogue', 'Combat', 'Shoulders', 'Both', 'Nightslayer Shoulder Pads'), +(4, 1, 4, 0, 83, 16905, 'Phase 3', 'Rogue', 'Combat', 'Chest', 'Both', 'Bloodfang Chestpiece'), +(4, 1, 5, 0, 83, 16910, 'Phase 3', 'Rogue', 'Combat', 'Waist', 'Both', 'Bloodfang Belt'), +(4, 1, 6, 0, 83, 16909, 'Phase 3', 'Rogue', 'Combat', 'Legs', 'Both', 'Bloodfang Pants'), +(4, 1, 7, 0, 83, 19381, 'Phase 3', 'Rogue', 'Combat', 'Feet', 'Both', 'Boots of the Shadow Flame'), +(4, 1, 8, 0, 83, 16911, 'Phase 3', 'Rogue', 'Combat', 'Wrists', 'Both', 'Bloodfang Bracers'), +(4, 1, 9, 0, 83, 18823, 'Phase 3', 'Rogue', 'Combat', 'Hands', 'Both', 'Aged Core Leather Gloves'), +(4, 1, 10, 0, 83, 17063, 'Phase 3', 'Rogue', 'Combat', 'Finger1', 'Both', 'Band of Accuria'), +(4, 1, 11, 0, 83, 19384, 'Phase 3', 'Rogue', 'Combat', 'Finger2', 'Both', 'Master Dragonslayer''s Ring'), +(4, 1, 12, 0, 83, 19406, 'Phase 3', 'Rogue', 'Combat', 'Trinket1', 'Both', 'Drake Fang Talisman'), +(4, 1, 13, 0, 83, 11815, 'Phase 3', 'Rogue', 'Combat', 'Trinket2', 'Both', 'Hand of Justice'), +(4, 1, 14, 0, 83, 19398, 'Phase 3', 'Rogue', 'Combat', 'Back', 'Both', 'Cloak of Firemaw'), +(4, 1, 15, 0, 83, 18816, 'Phase 3', 'Rogue', 'Combat', 'MainHand', 'Both', 'Perdition''s Blade'), +(4, 1, 16, 0, 83, 18805, 'Phase 3', 'Rogue', 'Combat', 'OffHand', 'Both', 'Core Hound Tooth'), +(4, 1, 17, 0, 83, 17069, 'Phase 3', 'Rogue', 'Combat', 'Ranged', 'Both', 'Striker''s Mark'); + +-- ilvl 88 (Phase 5) +INSERT INTO `playerbots_bis_gear` VALUES +(4, 1, 0, 0, 88, 21360, 'Phase 5', 'Rogue', 'Combat', 'Head', 'Both', 'Deathdealer''s Helm'), +(4, 1, 1, 0, 88, 19377, 'Phase 5', 'Rogue', 'Combat', 'Neck', 'Both', 'Prestor''s Talisman of Connivery'), +(4, 1, 2, 0, 88, 21361, 'Phase 5', 'Rogue', 'Combat', 'Shoulders', 'Both', 'Deathdealer''s Spaulders'), +(4, 1, 4, 0, 88, 21364, 'Phase 5', 'Rogue', 'Combat', 'Chest', 'Both', 'Deathdealer''s Vest'), +(4, 1, 5, 0, 88, 21586, 'Phase 5', 'Rogue', 'Combat', 'Waist', 'Both', 'Belt of Never-ending Agony'), +(4, 1, 6, 0, 88, 21362, 'Phase 5', 'Rogue', 'Combat', 'Legs', 'Both', 'Deathdealer''s Leggings'), +(4, 1, 7, 0, 88, 21359, 'Phase 5', 'Rogue', 'Combat', 'Feet', 'Both', 'Deathdealer''s Boots'), +(4, 1, 8, 0, 88, 21602, 'Phase 5', 'Rogue', 'Combat', 'Wrists', 'Both', 'Qiraji Execution Bracers'), +(4, 1, 9, 0, 88, 18823, 'Phase 5', 'Rogue', 'Combat', 'Hands', 'Both', 'Aged Core Leather Gloves'), +(4, 1, 10, 0, 88, 17063, 'Phase 5', 'Rogue', 'Combat', 'Finger1', 'Both', 'Band of Accuria'), +(4, 1, 11, 0, 88, 19384, 'Phase 5', 'Rogue', 'Combat', 'Finger2', 'Both', 'Master Dragonslayer''s Ring'), +(4, 1, 12, 0, 88, 19406, 'Phase 5', 'Rogue', 'Combat', 'Trinket1', 'Both', 'Drake Fang Talisman'), +(4, 1, 13, 0, 88, 23570, 'Phase 5', 'Rogue', 'Combat', 'Trinket2', 'Both', 'Jom Gabbar'), +(4, 1, 14, 0, 88, 21701, 'Phase 5', 'Rogue', 'Combat', 'Back', 'Both', 'Cloak of Concentrated Hatred'), +(4, 1, 15, 0, 88, 21126, 'Phase 5', 'Rogue', 'Combat', 'MainHand', 'Both', 'Death''s Sting'), +(4, 1, 16, 0, 88, 21244, 'Phase 5', 'Rogue', 'Combat', 'OffHand', 'Both', 'Blessed Qiraji Pugio'), +(4, 1, 17, 0, 88, 17069, 'Phase 5', 'Rogue', 'Combat', 'Ranged', 'Both', 'Striker''s Mark'); + +-- ilvl 92 (Phase 6) +INSERT INTO `playerbots_bis_gear` VALUES +(4, 1, 0, 0, 92, 22478, 'Phase 6', 'Rogue', 'Combat', 'Head', 'Both', 'Bonescythe Helmet'), +(4, 1, 1, 0, 92, 19377, 'Phase 6', 'Rogue', 'Combat', 'Neck', 'Both', 'Prestor''s Talisman of Connivery'), +(4, 1, 2, 0, 92, 22479, 'Phase 6', 'Rogue', 'Combat', 'Shoulders', 'Both', 'Bonescythe Pauldrons'), +(4, 1, 4, 0, 92, 22476, 'Phase 6', 'Rogue', 'Combat', 'Chest', 'Both', 'Bonescythe Breastplate'), +(4, 1, 5, 0, 92, 21586, 'Phase 6', 'Rogue', 'Combat', 'Waist', 'Both', 'Belt of Never-ending Agony'), +(4, 1, 6, 0, 92, 22477, 'Phase 6', 'Rogue', 'Combat', 'Legs', 'Both', 'Bonescythe Legplates'), +(4, 1, 7, 0, 92, 22480, 'Phase 6', 'Rogue', 'Combat', 'Feet', 'Both', 'Bonescythe Sabatons'), +(4, 1, 8, 0, 92, 22483, 'Phase 6', 'Rogue', 'Combat', 'Wrists', 'Both', 'Bonescythe Bracers'), +(4, 1, 9, 0, 92, 22481, 'Phase 6', 'Rogue', 'Combat', 'Hands', 'Both', 'Bonescythe Gauntlets'), +(4, 1, 10, 0, 92, 23060, 'Phase 6', 'Rogue', 'Combat', 'Finger1', 'Both', 'Bonescythe Ring'), +(4, 1, 12, 0, 92, 23041, 'Phase 6', 'Rogue', 'Combat', 'Trinket1', 'Both', 'Slayer''s Crest'), +(4, 1, 13, 0, 92, 22954, 'Phase 6', 'Rogue', 'Combat', 'Trinket2', 'Both', 'Kiss of the Spider'), +(4, 1, 14, 0, 92, 23045, 'Phase 6', 'Rogue', 'Combat', 'Back', 'Both', 'Shroud of Dominion'), +(4, 1, 15, 0, 92, 22802, 'Phase 6', 'Rogue', 'Combat', 'MainHand', 'Both', 'Kingsfall'), +(4, 1, 16, 0, 92, 21126, 'Phase 6', 'Rogue', 'Combat', 'OffHand', 'Both', 'Death''s Sting'), +(4, 1, 17, 0, 92, 22812, 'Phase 6', 'Rogue', 'Combat', 'Ranged', 'Both', 'Nerubian Slavemaker'); + +-- ilvl 120 (Pre-Raid) +INSERT INTO `playerbots_bis_gear` VALUES +(4, 1, 0, 0, 120, 32087, 'Pre-Raid', 'Rogue', 'Combat', 'Head', 'Both', 'Mask of the Deceiver'), +(4, 1, 1, 0, 120, 29381, 'Pre-Raid', 'Rogue', 'Combat', 'Neck', 'Both', 'Choker of Vile Intent'), +(4, 1, 2, 0, 120, 27797, 'Pre-Raid', 'Rogue', 'Combat', 'Shoulders', 'Both', 'Wastewalker Shoulderpads'), +(4, 1, 4, 0, 120, 30730, 'Pre-Raid', 'Rogue', 'Combat', 'Chest', 'Both', 'Terrorweave Tunic'), +(4, 1, 5, 0, 120, 29247, 'Pre-Raid', 'Rogue', 'Combat', 'Waist', 'Both', 'Girdle of the Deathdealer'), +(4, 1, 6, 0, 120, 30538, 'Pre-Raid', 'Rogue', 'Combat', 'Legs', 'Both', 'Midnight Legguards'), +(4, 1, 7, 0, 120, 25686, 'Pre-Raid', 'Rogue', 'Combat', 'Feet', 'Both', 'Fel Leather Boots'), +(4, 1, 8, 0, 120, 29246, 'Pre-Raid', 'Rogue', 'Combat', 'Wrists', 'Both', 'Nightfall Wristguards'), +(4, 1, 9, 0, 120, 27531, 'Pre-Raid', 'Rogue', 'Combat', 'Hands', 'Both', 'Wastewalker Gloves'), +(4, 1, 10, 0, 120, 30738, 'Pre-Raid', 'Rogue', 'Combat', 'Finger1', 'Both', 'Ring of Reciprocity'), +(4, 1, 12, 0, 120, 28288, 'Pre-Raid', 'Rogue', 'Combat', 'Trinket1', 'Both', 'Abacus of Violent Odds'), +(4, 1, 13, 0, 120, 29383, 'Pre-Raid', 'Rogue', 'Combat', 'Trinket2', 'Both', 'Bloodlust Brooch'), +(4, 1, 14, 0, 120, 24259, 'Pre-Raid', 'Rogue', 'Combat', 'Back', 'Both', 'Vengeance Wrap'), +(4, 1, 15, 0, 120, 31332, 'Pre-Raid', 'Rogue', 'Combat', 'MainHand', 'Both', 'Blinkstrike'), +(4, 1, 16, 0, 120, 28189, 'Pre-Raid', 'Rogue', 'Combat', 'OffHand', 'Both', 'Latro''s Shifting Sword'), +(4, 1, 17, 2, 120, 29152, 'Pre-Raid', 'Rogue', 'Combat', 'Ranged', 'Horde', 'Marksman''s Bow'); + +-- ilvl 125 (Phase 1) +INSERT INTO `playerbots_bis_gear` VALUES +(4, 1, 0, 0, 125, 29044, 'Phase 1', 'Rogue', 'Combat', 'Head', 'Both', 'Netherblade Facemask'), +(4, 1, 1, 0, 125, 29381, 'Phase 1', 'Rogue', 'Combat', 'Neck', 'Both', 'Choker of Vile Intent'), +(4, 1, 2, 0, 125, 27797, 'Phase 1', 'Rogue', 'Combat', 'Shoulders', 'Both', 'Wastewalker Shoulderpads'), +(4, 1, 4, 0, 125, 29045, 'Phase 1', 'Rogue', 'Combat', 'Chest', 'Both', 'Netherblade Chestpiece'), +(4, 1, 5, 0, 125, 29247, 'Phase 1', 'Rogue', 'Combat', 'Waist', 'Both', 'Girdle of the Deathdealer'), +(4, 1, 6, 0, 125, 28741, 'Phase 1', 'Rogue', 'Combat', 'Legs', 'Both', 'Skulker''s Greaves'), +(4, 1, 7, 0, 125, 28545, 'Phase 1', 'Rogue', 'Combat', 'Feet', 'Both', 'Edgewalker Longboots'), +(4, 1, 8, 0, 125, 29246, 'Phase 1', 'Rogue', 'Combat', 'Wrists', 'Both', 'Nightfall Wristguards'), +(4, 1, 9, 0, 125, 27531, 'Phase 1', 'Rogue', 'Combat', 'Hands', 'Both', 'Wastewalker Gloves'), +(4, 1, 10, 0, 125, 28649, 'Phase 1', 'Rogue', 'Combat', 'Finger1', 'Both', 'Garona''s Signet Ring'), +(4, 1, 11, 0, 125, 28757, 'Phase 1', 'Rogue', 'Combat', 'Finger2', 'Both', 'Ring of a Thousand Marks'), +(4, 1, 12, 0, 125, 29383, 'Phase 1', 'Rogue', 'Combat', 'Trinket1', 'Both', 'Bloodlust Brooch'), +(4, 1, 13, 0, 125, 28830, 'Phase 1', 'Rogue', 'Combat', 'Trinket2', 'Both', 'Dragonspine Trophy'), +(4, 1, 14, 0, 125, 28672, 'Phase 1', 'Rogue', 'Combat', 'Back', 'Both', 'Drape of the Dark Reavers'), +(4, 1, 15, 0, 125, 31332, 'Phase 1', 'Rogue', 'Combat', 'MainHand', 'Both', 'Blinkstrike'), +(4, 1, 16, 0, 125, 28307, 'Phase 1', 'Rogue', 'Combat', 'OffHand', 'Both', 'Gladiator''s Quickblade'), +(4, 1, 17, 0, 125, 28772, 'Phase 1', 'Rogue', 'Combat', 'Ranged', 'Both', 'Sunfury Bow of the Phoenix'); + +-- ilvl 200 (Pre-Raid) +INSERT INTO `playerbots_bis_gear` VALUES +(4, 1, 0, 0, 200, 42550, 'Pre-Raid', 'Rogue', 'Combat', 'Head', 'Both', 'Weakness Spectralizers'), +(4, 1, 1, 0, 200, 42645, 'Pre-Raid', 'Rogue', 'Combat', 'Neck', 'Both', 'Titanium Impact Choker'), +(4, 1, 2, 0, 200, 43481, 'Pre-Raid', 'Rogue', 'Combat', 'Shoulders', 'Both', 'Trollwoven Spaulders'), +(4, 1, 4, 0, 200, 39558, 'Pre-Raid', 'Rogue', 'Combat', 'Chest', 'Both', 'Heroes'' Bonescythe Breastplate'), +(4, 1, 5, 0, 200, 43484, 'Pre-Raid', 'Rogue', 'Combat', 'Waist', 'Both', 'Trollwoven Girdle'), +(4, 1, 6, 0, 200, 39564, 'Pre-Raid', 'Rogue', 'Combat', 'Legs', 'Both', 'Heroes'' Bonescythe Legplates'), +(4, 1, 7, 0, 200, 37666, 'Pre-Raid', 'Rogue', 'Combat', 'Feet', 'Both', 'Boots of the Whirling Mist'), +(4, 1, 8, 0, 200, 37853, 'Pre-Raid', 'Rogue', 'Combat', 'Wrists', 'Both', 'Advanced Tooled-Leather Bands'), +(4, 1, 9, 0, 200, 39560, 'Pre-Raid', 'Rogue', 'Combat', 'Hands', 'Both', 'Heroes'' Bonescythe Gauntlets'), +(4, 1, 10, 0, 200, 42642, 'Pre-Raid', 'Rogue', 'Combat', 'Finger1', 'Both', 'Titanium Impact Band'), +(4, 1, 12, 0, 200, 40684, 'Pre-Raid', 'Rogue', 'Combat', 'Trinket1', 'Both', 'Mirror of Truth'), +(4, 1, 14, 0, 200, 43566, 'Pre-Raid', 'Rogue', 'Combat', 'Back', 'Both', 'Ice Striker''s Cloak'), +(4, 1, 15, 0, 200, 37871, 'Pre-Raid', 'Rogue', 'Combat', 'MainHand', 'Both', 'The Key'), +(4, 1, 17, 0, 200, 43284, 'Pre-Raid', 'Rogue', 'Combat', 'Ranged', 'Both', 'Amanitar Skullbow'); + +-- ilvl 224 (Phase 1) +INSERT INTO `playerbots_bis_gear` VALUES +(4, 1, 0, 0, 224, 40499, 'Phase 1', 'Rogue', 'Combat', 'Head', 'Both', 'Valorous Bonescythe Helmet'), +(4, 1, 1, 0, 224, 44664, 'Phase 1', 'Rogue', 'Combat', 'Neck', 'Both', 'Favor of the Dragon Queen'), +(4, 1, 2, 0, 224, 40502, 'Phase 1', 'Rogue', 'Combat', 'Shoulders', 'Both', 'Valorous Bonescythe Pauldrons'), +(4, 1, 4, 0, 224, 40539, 'Phase 1', 'Rogue', 'Combat', 'Chest', 'Both', 'Chestguard of the Recluse'), +(4, 1, 5, 0, 224, 40205, 'Phase 1', 'Rogue', 'Combat', 'Waist', 'Both', 'Stalk-Skin Belt'), +(4, 1, 6, 0, 224, 44011, 'Phase 1', 'Rogue', 'Combat', 'Legs', 'Both', 'Leggings of the Honored'), +(4, 1, 7, 0, 224, 39701, 'Phase 1', 'Rogue', 'Combat', 'Feet', 'Both', 'Dawnwalkers'), +(4, 1, 8, 0, 224, 39765, 'Phase 1', 'Rogue', 'Combat', 'Wrists', 'Both', 'Sinner''s Bindings'), +(4, 1, 9, 0, 224, 40541, 'Phase 1', 'Rogue', 'Combat', 'Hands', 'Both', 'Frosted Adroit Handguards'), +(4, 1, 10, 0, 224, 40074, 'Phase 1', 'Rogue', 'Combat', 'Finger1', 'Both', 'Strong-Handed Ring'), +(4, 1, 12, 0, 224, 40684, 'Phase 1', 'Rogue', 'Combat', 'Trinket1', 'Both', 'Mirror of Truth'), +(4, 1, 14, 0, 224, 40403, 'Phase 1', 'Rogue', 'Combat', 'Back', 'Both', 'Drape of the Deadly Foe'), +(4, 1, 15, 0, 224, 40383, 'Phase 1', 'Rogue', 'Combat', 'MainHand', 'Both', 'Calamity''s Grasp'), +(4, 1, 17, 0, 224, 40385, 'Phase 1', 'Rogue', 'Combat', 'Ranged', 'Both', 'Envoy of Mortality'); + +-- ilvl 245 (Phase 2) +INSERT INTO `playerbots_bis_gear` VALUES +(4, 1, 0, 0, 245, 46125, 'Phase 2', 'Rogue', 'Combat', 'Head', 'Both', 'Conqueror''s Terrorblade Helmet'), +(4, 1, 1, 0, 245, 45517, 'Phase 2', 'Rogue', 'Combat', 'Neck', 'Both', 'Pendulum of Infinity'), +(4, 1, 2, 0, 245, 46127, 'Phase 2', 'Rogue', 'Combat', 'Shoulders', 'Both', 'Conqueror''s Terrorblade Pauldrons'), +(4, 1, 4, 0, 245, 45473, 'Phase 2', 'Rogue', 'Combat', 'Chest', 'Both', 'Embrace of the Gladiator'), +(4, 1, 5, 0, 245, 46095, 'Phase 2', 'Rogue', 'Combat', 'Waist', 'Both', 'Soul-Devouring Cinch'), +(4, 1, 6, 0, 245, 45536, 'Phase 2', 'Rogue', 'Combat', 'Legs', 'Both', 'Legguards of Cunning Deception'), +(4, 1, 7, 0, 245, 45564, 'Phase 2', 'Rogue', 'Combat', 'Feet', 'Both', 'Footpads of Silence'), +(4, 1, 8, 0, 245, 45611, 'Phase 2', 'Rogue', 'Combat', 'Wrists', 'Both', 'Solar Bindings'), +(4, 1, 9, 0, 245, 46124, 'Phase 2', 'Rogue', 'Combat', 'Hands', 'Both', 'Conqueror''s Terrorblade Gauntlets'), +(4, 1, 10, 0, 245, 45456, 'Phase 2', 'Rogue', 'Combat', 'Finger1', 'Both', 'Loop of the Agile'), +(4, 1, 12, 0, 245, 45609, 'Phase 2', 'Rogue', 'Combat', 'Trinket1', 'Both', 'Comet''s Trail'), +(4, 1, 14, 0, 245, 46032, 'Phase 2', 'Rogue', 'Combat', 'Back', 'Both', 'Drape of the Faceless General'), +(4, 1, 17, 0, 245, 45296, 'Phase 2', 'Rogue', 'Combat', 'Ranged', 'Both', 'Twirling Blades'); + +-- ilvl 258 (Phase 3) +INSERT INTO `playerbots_bis_gear` VALUES +(4, 1, 0, 0, 258, 48230, 'Phase 3', 'Rogue', 'Combat', 'Head', 'Both', 'Helmet of Triumph'), +(4, 1, 2, 0, 258, 48228, 'Phase 3', 'Rogue', 'Combat', 'Shoulders', 'Both', 'Pauldrons of Triumph'), +(4, 1, 4, 0, 258, 48232, 'Phase 3', 'Rogue', 'Combat', 'Chest', 'Both', 'Breastplate of Triumph'), +(4, 1, 5, 1, 258, 47112, 'Phase 3', 'Rogue', 'Combat', 'Waist', 'Alliance', 'Belt of the Merciless Killer'), +(4, 1, 5, 2, 258, 47460, 'Phase 3', 'Rogue', 'Combat', 'Waist', 'Horde', 'Belt of the Pitiless Killer'), +(4, 1, 6, 1, 258, 46975, 'Phase 3', 'Rogue', 'Combat', 'Legs', 'Alliance', 'Leggings of the Broken Beast'), +(4, 1, 6, 2, 258, 47420, 'Phase 3', 'Rogue', 'Combat', 'Legs', 'Horde', 'Legwraps of the Broken Beast'), +(4, 1, 7, 1, 258, 47077, 'Phase 3', 'Rogue', 'Combat', 'Feet', 'Alliance', 'Treads of the Icewalker'), +(4, 1, 7, 2, 258, 47445, 'Phase 3', 'Rogue', 'Combat', 'Feet', 'Horde', 'Icewalker Treads'), +(4, 1, 8, 1, 258, 47155, 'Phase 3', 'Rogue', 'Combat', 'Wrists', 'Alliance', 'Bracers of Dark Determination'), +(4, 1, 8, 2, 258, 47474, 'Phase 3', 'Rogue', 'Combat', 'Wrists', 'Horde', 'Armbands of Dark Determination'), +(4, 1, 9, 0, 258, 48231, 'Phase 3', 'Rogue', 'Combat', 'Hands', 'Both', 'Gauntlets of Triumph'), +(4, 1, 10, 1, 258, 47075, 'Phase 3', 'Rogue', 'Combat', 'Finger1', 'Alliance', 'Ring of Callous Aggression'), +(4, 1, 10, 2, 258, 47443, 'Phase 3', 'Rogue', 'Combat', 'Finger1', 'Horde', 'Band of Callous Aggression'), +(4, 1, 12, 1, 258, 47131, 'Phase 3', 'Rogue', 'Combat', 'Trinket1', 'Alliance', 'Death''s Verdict'), +(4, 1, 12, 2, 258, 47464, 'Phase 3', 'Rogue', 'Combat', 'Trinket1', 'Horde', 'Death''s Choice'), +(4, 1, 14, 1, 258, 47545, 'Phase 3', 'Rogue', 'Combat', 'Back', 'Alliance', 'Vereesa''s Dexterity'), +(4, 1, 14, 2, 258, 47546, 'Phase 3', 'Rogue', 'Combat', 'Back', 'Horde', 'Sylvanas'' Cunning'), +(4, 1, 17, 1, 258, 47521, 'Phase 3', 'Rogue', 'Combat', 'Ranged', 'Alliance', 'BRK 1000'), +(4, 1, 17, 2, 258, 47523, 'Phase 3', 'Rogue', 'Combat', 'Ranged', 'Horde', 'Fezzik''s Autocannon'); + +-- ilvl 264 (Phase 4) +INSERT INTO `playerbots_bis_gear` VALUES +(4, 1, 0, 0, 264, 51252, 'Phase 4', 'Rogue', 'Combat', 'Head', 'Both', 'Sanctified Shadowblade Helmet'), +(4, 1, 2, 0, 264, 51254, 'Phase 4', 'Rogue', 'Combat', 'Shoulders', 'Both', 'Sanctified Shadowblade Pauldrons'), +(4, 1, 4, 0, 264, 50656, 'Phase 4', 'Rogue', 'Combat', 'Chest', 'Both', 'Ikfirus''s Sack of Wonder'), +(4, 1, 5, 0, 264, 50707, 'Phase 4', 'Rogue', 'Combat', 'Waist', 'Both', 'Astrylian''s Sutured Cinch'), +(4, 1, 6, 0, 264, 50697, 'Phase 4', 'Rogue', 'Combat', 'Legs', 'Both', 'Gangrenous Leggings'), +(4, 1, 7, 0, 264, 50607, 'Phase 4', 'Rogue', 'Combat', 'Feet', 'Both', 'Frostbitten Fur Boots'), +(4, 1, 8, 0, 264, 50670, 'Phase 4', 'Rogue', 'Combat', 'Wrists', 'Both', 'Toskk''s Maximized Wristguards'), +(4, 1, 9, 0, 264, 50675, 'Phase 4', 'Rogue', 'Combat', 'Hands', 'Both', 'Aldriana''s Gloves of Secrecy'), +(4, 1, 10, 0, 264, 50618, 'Phase 4', 'Rogue', 'Combat', 'Finger1', 'Both', 'Frostbrood Sapphire Ring'), +(4, 1, 12, 0, 264, 50363, 'Phase 4', 'Rogue', 'Combat', 'Trinket1', 'Both', 'Deathbringer''s Will'), +(4, 1, 14, 1, 264, 47545, 'Phase 4', 'Rogue', 'Combat', 'Back', 'Alliance', 'Vereesa''s Dexterity'), +(4, 1, 14, 2, 264, 47546, 'Phase 4', 'Rogue', 'Combat', 'Back', 'Horde', 'Sylvanas'' Cunning'), +(4, 1, 17, 0, 264, 50733, 'Phase 4', 'Rogue', 'Combat', 'Ranged', 'Both', 'Fal''inrush, Defender of Quel''thalas'); + +-- ilvl 290 (Phase 5) +INSERT INTO `playerbots_bis_gear` VALUES +(4, 1, 0, 0, 290, 51252, 'Phase 5', 'Rogue', 'Combat', 'Head', 'Both', 'Sanctified Shadowblade Helmet'), +(4, 1, 1, 0, 290, 50633, 'Phase 5', 'Rogue', 'Combat', 'Neck', 'Both', 'Sindragosa''s Cruel Claw'), +(4, 1, 2, 0, 290, 51254, 'Phase 5', 'Rogue', 'Combat', 'Shoulders', 'Both', 'Sanctified Shadowblade Pauldrons'), +(4, 1, 4, 0, 290, 51250, 'Phase 5', 'Rogue', 'Combat', 'Chest', 'Both', 'Sanctified Shadowblade Breastplate'), +(4, 1, 5, 0, 290, 50707, 'Phase 5', 'Rogue', 'Combat', 'Waist', 'Both', 'Astrylian''s Sutured Cinch'), +(4, 1, 6, 0, 290, 51253, 'Phase 5', 'Rogue', 'Combat', 'Legs', 'Both', 'Sanctified Shadowblade Legplates'), +(4, 1, 7, 0, 290, 50607, 'Phase 5', 'Rogue', 'Combat', 'Feet', 'Both', 'Frostbitten Fur Boots'), +(4, 1, 8, 0, 290, 50670, 'Phase 5', 'Rogue', 'Combat', 'Wrists', 'Both', 'Toskk''s Maximized Wristguards'), +(4, 1, 9, 0, 290, 50675, 'Phase 5', 'Rogue', 'Combat', 'Hands', 'Both', 'Aldriana''s Gloves of Secrecy'), +(4, 1, 10, 0, 290, 50402, 'Phase 5', 'Rogue', 'Combat', 'Finger1', 'Both', 'Ashen Band of Endless Vengeance'), +(4, 1, 11, 0, 290, 50618, 'Phase 5', 'Rogue', 'Combat', 'Finger2', 'Both', 'Frostbrood Sapphire Ring'), +(4, 1, 12, 0, 290, 50363, 'Phase 5', 'Rogue', 'Combat', 'Trinket1', 'Both', 'Deathbringer''s Will'), +(4, 1, 13, 0, 290, 54590, 'Phase 5', 'Rogue', 'Combat', 'Trinket2', 'Both', 'Sharpened Twilight Scale'), +(4, 1, 14, 0, 290, 47545, 'Phase 5', 'Rogue', 'Combat', 'Back', 'Both', 'Vereesa''s Dexterity'), +(4, 1, 15, 0, 290, 50737, 'Phase 5', 'Rogue', 'Combat', 'MainHand', 'Both', 'Havoc''s Call, Blade of Lordaeron Kings'), +(4, 1, 16, 0, 290, 50654, 'Phase 5', 'Rogue', 'Combat', 'OffHand', 'Both', 'Scourgeborne Waraxe'), +(4, 1, 17, 0, 290, 50733, 'Phase 5', 'Rogue', 'Combat', 'Ranged', 'Both', 'Fal''inrush, Defender of Quel''thalas'); + +-- Subtlety (tab 2) +-- ilvl 120 (Pre-Raid) +INSERT INTO `playerbots_bis_gear` VALUES +(4, 2, 0, 0, 120, 32087, 'Pre-Raid', 'Rogue', 'Subtlety', 'Head', 'Both', 'Mask of the Deceiver'), +(4, 2, 1, 0, 120, 29381, 'Pre-Raid', 'Rogue', 'Subtlety', 'Neck', 'Both', 'Choker of Vile Intent'), +(4, 2, 2, 0, 120, 27797, 'Pre-Raid', 'Rogue', 'Subtlety', 'Shoulders', 'Both', 'Wastewalker Shoulderpads'), +(4, 2, 4, 0, 120, 30730, 'Pre-Raid', 'Rogue', 'Subtlety', 'Chest', 'Both', 'Terrorweave Tunic'), +(4, 2, 5, 0, 120, 29247, 'Pre-Raid', 'Rogue', 'Subtlety', 'Waist', 'Both', 'Girdle of the Deathdealer'), +(4, 2, 6, 0, 120, 30538, 'Pre-Raid', 'Rogue', 'Subtlety', 'Legs', 'Both', 'Midnight Legguards'), +(4, 2, 7, 0, 120, 25686, 'Pre-Raid', 'Rogue', 'Subtlety', 'Feet', 'Both', 'Fel Leather Boots'), +(4, 2, 8, 0, 120, 29246, 'Pre-Raid', 'Rogue', 'Subtlety', 'Wrists', 'Both', 'Nightfall Wristguards'), +(4, 2, 9, 0, 120, 27531, 'Pre-Raid', 'Rogue', 'Subtlety', 'Hands', 'Both', 'Wastewalker Gloves'), +(4, 2, 10, 0, 120, 30738, 'Pre-Raid', 'Rogue', 'Subtlety', 'Finger1', 'Both', 'Ring of Reciprocity'), +(4, 2, 12, 0, 120, 28288, 'Pre-Raid', 'Rogue', 'Subtlety', 'Trinket1', 'Both', 'Abacus of Violent Odds'), +(4, 2, 13, 0, 120, 29383, 'Pre-Raid', 'Rogue', 'Subtlety', 'Trinket2', 'Both', 'Bloodlust Brooch'), +(4, 2, 14, 0, 120, 24259, 'Pre-Raid', 'Rogue', 'Subtlety', 'Back', 'Both', 'Vengeance Wrap'), +(4, 2, 15, 0, 120, 31332, 'Pre-Raid', 'Rogue', 'Subtlety', 'MainHand', 'Both', 'Blinkstrike'), +(4, 2, 16, 0, 120, 28189, 'Pre-Raid', 'Rogue', 'Subtlety', 'OffHand', 'Both', 'Latro''s Shifting Sword'), +(4, 2, 17, 2, 120, 29152, 'Pre-Raid', 'Rogue', 'Subtlety', 'Ranged', 'Horde', 'Marksman''s Bow'); + +-- ilvl 125 (Phase 1) +INSERT INTO `playerbots_bis_gear` VALUES +(4, 2, 0, 0, 125, 29044, 'Phase 1', 'Rogue', 'Subtlety', 'Head', 'Both', 'Netherblade Facemask'), +(4, 2, 1, 0, 125, 29381, 'Phase 1', 'Rogue', 'Subtlety', 'Neck', 'Both', 'Choker of Vile Intent'), +(4, 2, 2, 0, 125, 27797, 'Phase 1', 'Rogue', 'Subtlety', 'Shoulders', 'Both', 'Wastewalker Shoulderpads'), +(4, 2, 4, 0, 125, 29045, 'Phase 1', 'Rogue', 'Subtlety', 'Chest', 'Both', 'Netherblade Chestpiece'), +(4, 2, 5, 0, 125, 29247, 'Phase 1', 'Rogue', 'Subtlety', 'Waist', 'Both', 'Girdle of the Deathdealer'), +(4, 2, 6, 0, 125, 28741, 'Phase 1', 'Rogue', 'Subtlety', 'Legs', 'Both', 'Skulker''s Greaves'), +(4, 2, 7, 0, 125, 28545, 'Phase 1', 'Rogue', 'Subtlety', 'Feet', 'Both', 'Edgewalker Longboots'), +(4, 2, 8, 0, 125, 29246, 'Phase 1', 'Rogue', 'Subtlety', 'Wrists', 'Both', 'Nightfall Wristguards'), +(4, 2, 9, 0, 125, 27531, 'Phase 1', 'Rogue', 'Subtlety', 'Hands', 'Both', 'Wastewalker Gloves'), +(4, 2, 10, 0, 125, 28649, 'Phase 1', 'Rogue', 'Subtlety', 'Finger1', 'Both', 'Garona''s Signet Ring'), +(4, 2, 11, 0, 125, 28757, 'Phase 1', 'Rogue', 'Subtlety', 'Finger2', 'Both', 'Ring of a Thousand Marks'), +(4, 2, 12, 0, 125, 29383, 'Phase 1', 'Rogue', 'Subtlety', 'Trinket1', 'Both', 'Bloodlust Brooch'), +(4, 2, 13, 0, 125, 28830, 'Phase 1', 'Rogue', 'Subtlety', 'Trinket2', 'Both', 'Dragonspine Trophy'), +(4, 2, 14, 0, 125, 28672, 'Phase 1', 'Rogue', 'Subtlety', 'Back', 'Both', 'Drape of the Dark Reavers'), +(4, 2, 15, 0, 125, 31332, 'Phase 1', 'Rogue', 'Subtlety', 'MainHand', 'Both', 'Blinkstrike'), +(4, 2, 16, 0, 125, 28307, 'Phase 1', 'Rogue', 'Subtlety', 'OffHand', 'Both', 'Gladiator''s Quickblade'), +(4, 2, 17, 0, 125, 28772, 'Phase 1', 'Rogue', 'Subtlety', 'Ranged', 'Both', 'Sunfury Bow of the Phoenix'); + + +-- ============================================================ +-- Priest (5) +-- ============================================================ +-- Discipline (tab 0) +-- ilvl 120 (Pre-Raid) +INSERT INTO `playerbots_bis_gear` VALUES +(5, 0, 0, 0, 120, 24264, 'Pre-Raid', 'Priest', 'Discipline', 'Head', 'Both', 'Whitemend Hood'), +(5, 0, 1, 0, 120, 30377, 'Pre-Raid', 'Priest', 'Discipline', 'Neck', 'Both', 'Karja''s Medallion'), +(5, 0, 2, 0, 120, 21874, 'Pre-Raid', 'Priest', 'Discipline', 'Shoulders', 'Both', 'Primal Mooncloth Shoulders'), +(5, 0, 4, 0, 120, 21875, 'Pre-Raid', 'Priest', 'Discipline', 'Chest', 'Both', 'Primal Mooncloth Robe'), +(5, 0, 5, 0, 120, 21873, 'Pre-Raid', 'Priest', 'Discipline', 'Waist', 'Both', 'Primal Mooncloth Belt'), +(5, 0, 6, 0, 120, 24261, 'Pre-Raid', 'Priest', 'Discipline', 'Legs', 'Both', 'Whitemend Pants'), +(5, 0, 7, 0, 120, 29251, 'Pre-Raid', 'Priest', 'Discipline', 'Feet', 'Both', 'Boots of the Pious'), +(5, 0, 8, 0, 120, 29249, 'Pre-Raid', 'Priest', 'Discipline', 'Wrists', 'Both', 'Bands of the Benevolent'), +(5, 0, 9, 0, 120, 24393, 'Pre-Raid', 'Priest', 'Discipline', 'Hands', 'Both', 'Bloody Surgeon''s Mitts'), +(5, 0, 10, 0, 120, 27780, 'Pre-Raid', 'Priest', 'Discipline', 'Finger1', 'Both', 'Ring of Fabled Hope'), +(5, 0, 10, 2, 120, 29168, 'Pre-Raid', 'Priest', 'Discipline', 'Finger1', 'Horde', 'Ancestral Band'), +(5, 0, 12, 0, 120, 19288, 'Pre-Raid', 'Priest', 'Discipline', 'Trinket1', 'Both', 'Darkmoon Card: Blue Dragon'), +(5, 0, 13, 0, 120, 29376, 'Pre-Raid', 'Priest', 'Discipline', 'Trinket2', 'Both', 'Essence of the Martyr'), +(5, 0, 14, 0, 120, 29354, 'Pre-Raid', 'Priest', 'Discipline', 'Back', 'Both', 'Light-Touched Stole of Altruism'), +(5, 0, 15, 0, 120, 29353, 'Pre-Raid', 'Priest', 'Discipline', 'MainHand', 'Both', 'Shockwave Truncheon'), +(5, 0, 16, 0, 120, 29170, 'Pre-Raid', 'Priest', 'Discipline', 'OffHand', 'Both', 'Windcaller''s Orb'), +(5, 0, 17, 0, 120, 27885, 'Pre-Raid', 'Priest', 'Discipline', 'Ranged', 'Both', 'Soul-Wand of the Aldor'); + +-- ilvl 125 (Phase 1) +INSERT INTO `playerbots_bis_gear` VALUES +(5, 0, 0, 0, 125, 29049, 'Phase 1', 'Priest', 'Discipline', 'Head', 'Both', 'Light-Collar of the Incarnate'), +(5, 0, 1, 0, 125, 30726, 'Phase 1', 'Priest', 'Discipline', 'Neck', 'Both', 'Archaic Charm of Presence'), +(5, 0, 2, 0, 125, 21874, 'Phase 1', 'Priest', 'Discipline', 'Shoulders', 'Both', 'Primal Mooncloth Shoulders'), +(5, 0, 4, 0, 125, 21875, 'Phase 1', 'Priest', 'Discipline', 'Chest', 'Both', 'Primal Mooncloth Robe'), +(5, 0, 5, 0, 125, 21873, 'Phase 1', 'Priest', 'Discipline', 'Waist', 'Both', 'Primal Mooncloth Belt'), +(5, 0, 6, 0, 125, 30727, 'Phase 1', 'Priest', 'Discipline', 'Legs', 'Both', 'Gilded Trousers of Benediction'), +(5, 0, 7, 0, 125, 28663, 'Phase 1', 'Priest', 'Discipline', 'Feet', 'Both', 'Boots of the Incorrupt'), +(5, 0, 8, 0, 125, 29249, 'Phase 1', 'Priest', 'Discipline', 'Wrists', 'Both', 'Bands of the Benevolent'), +(5, 0, 9, 0, 125, 28508, 'Phase 1', 'Priest', 'Discipline', 'Hands', 'Both', 'Gloves of Saintly Blessings'), +(5, 0, 10, 0, 125, 29290, 'Phase 1', 'Priest', 'Discipline', 'Finger1', 'Both', 'Violet Signet of the Grand Restorer'), +(5, 0, 11, 0, 125, 28763, 'Phase 1', 'Priest', 'Discipline', 'Finger2', 'Both', 'Jade Ring of the Everliving'), +(5, 0, 12, 0, 125, 29376, 'Phase 1', 'Priest', 'Discipline', 'Trinket1', 'Both', 'Essence of the Martyr'), +(5, 0, 13, 0, 125, 28823, 'Phase 1', 'Priest', 'Discipline', 'Trinket2', 'Both', 'Eye of Gruul'), +(5, 0, 14, 0, 125, 28765, 'Phase 1', 'Priest', 'Discipline', 'Back', 'Both', 'Stainless Cloak of the Pure Hearted'), +(5, 0, 15, 0, 125, 28771, 'Phase 1', 'Priest', 'Discipline', 'MainHand', 'Both', 'Light''s Justice'), +(5, 0, 16, 0, 125, 29170, 'Phase 1', 'Priest', 'Discipline', 'OffHand', 'Both', 'Windcaller''s Orb'), +(5, 0, 17, 0, 125, 28588, 'Phase 1', 'Priest', 'Discipline', 'Ranged', 'Both', 'Blue Diamond Witchwand'); + +-- ilvl 141 (Phase 2) +INSERT INTO `playerbots_bis_gear` VALUES +(5, 0, 0, 0, 141, 30152, 'Phase 2', 'Priest', 'Discipline', 'Head', 'Both', 'Cowl of the Avatar'), +(5, 0, 1, 0, 141, 30018, 'Phase 2', 'Priest', 'Discipline', 'Neck', 'Both', 'Lord Sanguinar''s Claim'), +(5, 0, 2, 0, 141, 30154, 'Phase 2', 'Priest', 'Discipline', 'Shoulders', 'Both', 'Mantle of the Avatar'), +(5, 0, 4, 0, 141, 30150, 'Phase 2', 'Priest', 'Discipline', 'Chest', 'Both', 'Vestments of the Avatar'), +(5, 0, 5, 0, 141, 30036, 'Phase 2', 'Priest', 'Discipline', 'Waist', 'Both', 'Belt of the Long Road'), +(5, 0, 6, 0, 141, 30727, 'Phase 2', 'Priest', 'Discipline', 'Legs', 'Both', 'Gilded Trousers of Benediction'), +(5, 0, 7, 0, 141, 30100, 'Phase 2', 'Priest', 'Discipline', 'Feet', 'Both', 'Soul-Strider Boots'), +(5, 0, 8, 0, 141, 32516, 'Phase 2', 'Priest', 'Discipline', 'Wrists', 'Both', 'Wraps of Purification'), +(5, 0, 9, 0, 141, 30151, 'Phase 2', 'Priest', 'Discipline', 'Hands', 'Both', 'Gloves of the Avatar'), +(5, 0, 10, 0, 141, 30110, 'Phase 2', 'Priest', 'Discipline', 'Finger1', 'Both', 'Coral Band of the Revived'), +(5, 0, 11, 0, 141, 29290, 'Phase 2', 'Priest', 'Discipline', 'Finger2', 'Both', 'Violet Signet of the Grand Restorer'), +(5, 0, 12, 0, 141, 29376, 'Phase 2', 'Priest', 'Discipline', 'Trinket1', 'Both', 'Essence of the Martyr'), +(5, 0, 13, 0, 141, 28823, 'Phase 2', 'Priest', 'Discipline', 'Trinket2', 'Both', 'Eye of Gruul'), +(5, 0, 14, 0, 141, 29989, 'Phase 2', 'Priest', 'Discipline', 'Back', 'Both', 'Sunshower Light Cloak'), +(5, 0, 15, 0, 141, 30108, 'Phase 2', 'Priest', 'Discipline', 'MainHand', 'Both', 'Lightfathom Scepter'), +(5, 0, 16, 0, 141, 29923, 'Phase 2', 'Priest', 'Discipline', 'OffHand', 'Both', 'Talisman of the Sun King'), +(5, 0, 17, 0, 141, 30080, 'Phase 2', 'Priest', 'Discipline', 'Ranged', 'Both', 'Luminescent Rod of the Naaru'); + +-- ilvl 156 (Phase 3) +INSERT INTO `playerbots_bis_gear` VALUES +(5, 0, 0, 0, 156, 31063, 'Phase 3', 'Priest', 'Discipline', 'Head', 'Both', 'Cowl of Absolution'), +(5, 0, 1, 0, 156, 32370, 'Phase 3', 'Priest', 'Discipline', 'Neck', 'Both', 'Nadina''s Pendant of Purity'), +(5, 0, 2, 0, 156, 31069, 'Phase 3', 'Priest', 'Discipline', 'Shoulders', 'Both', 'Mantle of Absolution'), +(5, 0, 4, 0, 156, 31066, 'Phase 3', 'Priest', 'Discipline', 'Chest', 'Both', 'Vestments of Absolution'), +(5, 0, 5, 0, 156, 32519, 'Phase 3', 'Priest', 'Discipline', 'Waist', 'Both', 'Belt of Divine Guidance'), +(5, 0, 6, 0, 156, 30912, 'Phase 3', 'Priest', 'Discipline', 'Legs', 'Both', 'Leggings of Eternity'), +(5, 0, 7, 0, 156, 32609, 'Phase 3', 'Priest', 'Discipline', 'Feet', 'Both', 'Boots of the Divine Light'), +(5, 0, 8, 0, 156, 30871, 'Phase 3', 'Priest', 'Discipline', 'Wrists', 'Both', 'Bracers of Martyrdom'), +(5, 0, 9, 0, 156, 31060, 'Phase 3', 'Priest', 'Discipline', 'Hands', 'Both', 'Gloves of Absolution'), +(5, 0, 10, 0, 156, 32528, 'Phase 3', 'Priest', 'Discipline', 'Finger1', 'Both', 'Blessed Band of Karabor'), +(5, 0, 11, 0, 156, 32528, 'Phase 3', 'Priest', 'Discipline', 'Finger2', 'Both', 'Blessed Band of Karabor'), +(5, 0, 12, 0, 156, 29376, 'Phase 3', 'Priest', 'Discipline', 'Trinket1', 'Both', 'Essence of the Martyr'), +(5, 0, 13, 0, 156, 32496, 'Phase 3', 'Priest', 'Discipline', 'Trinket2', 'Both', 'Memento of Tyrande'), +(5, 0, 14, 0, 156, 32524, 'Phase 3', 'Priest', 'Discipline', 'Back', 'Both', 'Shroud of the Highborne'), +(5, 0, 15, 0, 156, 32500, 'Phase 3', 'Priest', 'Discipline', 'MainHand', 'Both', 'Crystal Spire of Karabor'), +(5, 0, 16, 0, 156, 30911, 'Phase 3', 'Priest', 'Discipline', 'OffHand', 'Both', 'Scepter of Purification'), +(5, 0, 17, 0, 156, 32363, 'Phase 3', 'Priest', 'Discipline', 'Ranged', 'Both', 'Naaru-Blessed Life Rod'); + +-- ilvl 164 (Phase 4) +INSERT INTO `playerbots_bis_gear` VALUES +(5, 0, 0, 0, 164, 31063, 'Phase 4', 'Priest', 'Discipline', 'Head', 'Both', 'Cowl of Absolution'), +(5, 0, 1, 0, 164, 33281, 'Phase 4', 'Priest', 'Discipline', 'Neck', 'Both', 'Brooch of Nature''s Mercy'), +(5, 0, 2, 0, 164, 31069, 'Phase 4', 'Priest', 'Discipline', 'Shoulders', 'Both', 'Mantle of Absolution'), +(5, 0, 4, 0, 164, 31066, 'Phase 4', 'Priest', 'Discipline', 'Chest', 'Both', 'Vestments of Absolution'), +(5, 0, 5, 0, 164, 32519, 'Phase 4', 'Priest', 'Discipline', 'Waist', 'Both', 'Belt of Divine Guidance'), +(5, 0, 6, 0, 164, 30912, 'Phase 4', 'Priest', 'Discipline', 'Legs', 'Both', 'Leggings of Eternity'), +(5, 0, 7, 0, 164, 32609, 'Phase 4', 'Priest', 'Discipline', 'Feet', 'Both', 'Boots of the Divine Light'), +(5, 0, 8, 0, 164, 30871, 'Phase 4', 'Priest', 'Discipline', 'Wrists', 'Both', 'Bracers of Martyrdom'), +(5, 0, 9, 0, 164, 31060, 'Phase 4', 'Priest', 'Discipline', 'Hands', 'Both', 'Gloves of Absolution'), +(5, 0, 10, 0, 164, 32528, 'Phase 4', 'Priest', 'Discipline', 'Finger1', 'Both', 'Blessed Band of Karabor'), +(5, 0, 11, 0, 164, 30110, 'Phase 4', 'Priest', 'Discipline', 'Finger2', 'Both', 'Coral Band of the Revived'), +(5, 0, 12, 0, 164, 29376, 'Phase 4', 'Priest', 'Discipline', 'Trinket1', 'Both', 'Essence of the Martyr'), +(5, 0, 13, 0, 164, 32496, 'Phase 4', 'Priest', 'Discipline', 'Trinket2', 'Both', 'Memento of Tyrande'), +(5, 0, 14, 0, 164, 32524, 'Phase 4', 'Priest', 'Discipline', 'Back', 'Both', 'Shroud of the Highborne'), +(5, 0, 15, 0, 164, 32500, 'Phase 4', 'Priest', 'Discipline', 'MainHand', 'Both', 'Crystal Spire of Karabor'), +(5, 0, 16, 0, 164, 30911, 'Phase 4', 'Priest', 'Discipline', 'OffHand', 'Both', 'Scepter of Purification'), +(5, 0, 17, 0, 164, 32363, 'Phase 4', 'Priest', 'Discipline', 'Ranged', 'Both', 'Naaru-Blessed Life Rod'); + +-- ilvl 200 (Pre-Raid) +INSERT INTO `playerbots_bis_gear` VALUES +(5, 0, 0, 0, 200, 37294, 'Pre-Raid', 'Priest', 'Discipline', 'Head', 'Both', 'Crown of Unbridled Magic'), +(5, 0, 1, 0, 200, 40681, 'Pre-Raid', 'Priest', 'Discipline', 'Neck', 'Both', 'Lattice Choker of Light'), +(5, 0, 2, 0, 200, 37673, 'Pre-Raid', 'Priest', 'Discipline', 'Shoulders', 'Both', 'Dark Runic Mantle'), +(5, 0, 4, 0, 200, 44180, 'Pre-Raid', 'Priest', 'Discipline', 'Chest', 'Both', 'Robes of Crackling Flame'), +(5, 0, 5, 0, 200, 40697, 'Pre-Raid', 'Priest', 'Discipline', 'Waist', 'Both', 'Elegant Temple Gardens'' Girdle'), +(5, 0, 6, 0, 200, 37854, 'Pre-Raid', 'Priest', 'Discipline', 'Legs', 'Both', 'Woven Bracae Leggings'), +(5, 0, 7, 0, 200, 44202, 'Pre-Raid', 'Priest', 'Discipline', 'Feet', 'Both', 'Sandals of Crimson Fury'), +(5, 0, 8, 0, 200, 37361, 'Pre-Raid', 'Priest', 'Discipline', 'Wrists', 'Both', 'Cuffs of Winged Levitation'), +(5, 0, 9, 0, 200, 37172, 'Pre-Raid', 'Priest', 'Discipline', 'Hands', 'Both', 'Gloves of Glistening Runes'), +(5, 0, 10, 0, 200, 37694, 'Pre-Raid', 'Priest', 'Discipline', 'Finger1', 'Both', 'Band of Guile'), +(5, 0, 12, 0, 200, 37835, 'Pre-Raid', 'Priest', 'Discipline', 'Trinket1', 'Both', 'Je''Tze''s Bell'), +(5, 0, 14, 0, 200, 41610, 'Pre-Raid', 'Priest', 'Discipline', 'Back', 'Both', 'Deathchill Cloak'), +(5, 0, 15, 0, 200, 37169, 'Pre-Raid', 'Priest', 'Discipline', 'MainHand', 'Both', 'War Mace of Unrequited Love'), +(5, 0, 17, 0, 200, 37619, 'Pre-Raid', 'Priest', 'Discipline', 'Ranged', 'Both', 'Wand of Ahn''kahet'); + +-- ilvl 224 (Phase 1) +INSERT INTO `playerbots_bis_gear` VALUES +(5, 0, 0, 0, 224, 40447, 'Phase 1', 'Priest', 'Discipline', 'Head', 'Both', 'Valorous Crown of Faith'), +(5, 0, 1, 0, 224, 44661, 'Phase 1', 'Priest', 'Discipline', 'Neck', 'Both', 'Wyrmrest Necklace of Power'), +(5, 0, 2, 0, 224, 40450, 'Phase 1', 'Priest', 'Discipline', 'Shoulders', 'Both', 'Valorous Shoulderpads of Faith'), +(5, 0, 4, 0, 224, 44002, 'Phase 1', 'Priest', 'Discipline', 'Chest', 'Both', 'The Sanctum''s Flowing Vestments'), +(5, 0, 5, 0, 224, 40561, 'Phase 1', 'Priest', 'Discipline', 'Waist', 'Both', 'Leash of Heedless Magic'), +(5, 0, 6, 0, 224, 40448, 'Phase 1', 'Priest', 'Discipline', 'Legs', 'Both', 'Valorous Leggings of Faith'), +(5, 0, 7, 0, 224, 40558, 'Phase 1', 'Priest', 'Discipline', 'Feet', 'Both', 'Arcanic Tramplers'), +(5, 0, 8, 0, 224, 44008, 'Phase 1', 'Priest', 'Discipline', 'Wrists', 'Both', 'Unsullied Cuffs'), +(5, 0, 9, 0, 224, 40445, 'Phase 1', 'Priest', 'Discipline', 'Hands', 'Both', 'Valorous Gloves of Faith'), +(5, 0, 10, 0, 224, 40399, 'Phase 1', 'Priest', 'Discipline', 'Finger1', 'Both', 'Signet of Manifested Pain'), +(5, 0, 12, 0, 224, 37835, 'Phase 1', 'Priest', 'Discipline', 'Trinket1', 'Both', 'Je''Tze''s Bell'), +(5, 0, 14, 0, 224, 44005, 'Phase 1', 'Priest', 'Discipline', 'Back', 'Both', 'Pennant Cloak'), +(5, 0, 17, 0, 224, 39426, 'Phase 1', 'Priest', 'Discipline', 'Ranged', 'Both', 'Wand of the Archlich'); + +-- ilvl 245 (Phase 2) +INSERT INTO `playerbots_bis_gear` VALUES +(5, 0, 0, 0, 245, 46197, 'Phase 2', 'Priest', 'Discipline', 'Head', 'Both', 'Conqueror''s Cowl of Sanctification'), +(5, 0, 1, 0, 245, 45443, 'Phase 2', 'Priest', 'Discipline', 'Neck', 'Both', 'Charm of Meticulous Timing'), +(5, 0, 2, 0, 245, 46190, 'Phase 2', 'Priest', 'Discipline', 'Shoulders', 'Both', 'Conqueror''s Shoulderpads of Sanctification'), +(5, 0, 4, 0, 245, 46193, 'Phase 2', 'Priest', 'Discipline', 'Chest', 'Both', 'Conqueror''s Robe of Sanctification'), +(5, 0, 5, 0, 245, 45619, 'Phase 2', 'Priest', 'Discipline', 'Waist', 'Both', 'Starwatcher''s Binding'), +(5, 0, 6, 0, 245, 46195, 'Phase 2', 'Priest', 'Discipline', 'Legs', 'Both', 'Conqueror''s Leggings of Sanctification'), +(5, 0, 7, 0, 245, 45135, 'Phase 2', 'Priest', 'Discipline', 'Feet', 'Both', 'Boots of Fiery Resolution'), +(5, 0, 8, 0, 245, 45446, 'Phase 2', 'Priest', 'Discipline', 'Wrists', 'Both', 'Grasps of Reason'), +(5, 0, 9, 0, 245, 45520, 'Phase 2', 'Priest', 'Discipline', 'Hands', 'Both', 'Handwraps of the Vigilant'), +(5, 0, 12, 0, 245, 45535, 'Phase 2', 'Priest', 'Discipline', 'Trinket1', 'Both', 'Show of Faith'), +(5, 0, 14, 0, 245, 45486, 'Phase 2', 'Priest', 'Discipline', 'Back', 'Both', 'Drape of the Sullen Goddess'), +(5, 0, 15, 0, 245, 46017, 'Phase 2', 'Priest', 'Discipline', 'MainHand', 'Both', 'Val''anyr, Hammer of Ancient Kings'), +(5, 0, 17, 0, 245, 45294, 'Phase 2', 'Priest', 'Discipline', 'Ranged', 'Both', 'Petrified Ivy Sprig'); + +-- ilvl 258 (Phase 3) +INSERT INTO `playerbots_bis_gear` VALUES +(5, 0, 0, 0, 258, 46197, 'Phase 3', 'Priest', 'Discipline', 'Head', 'Both', 'Conqueror''s Cowl of Sanctification'), +(5, 0, 2, 0, 258, 46190, 'Phase 3', 'Priest', 'Discipline', 'Shoulders', 'Both', 'Conqueror''s Shoulderpads of Sanctification'), +(5, 0, 4, 0, 258, 48031, 'Phase 3', 'Priest', 'Discipline', 'Chest', 'Both', 'Robe of Triumph'), +(5, 0, 5, 1, 258, 47084, 'Phase 3', 'Priest', 'Discipline', 'Waist', 'Alliance', 'Cord of Biting Cold'), +(5, 0, 5, 2, 258, 47447, 'Phase 3', 'Priest', 'Discipline', 'Waist', 'Horde', 'Belt of Biting Cold'), +(5, 0, 6, 1, 258, 47189, 'Phase 3', 'Priest', 'Discipline', 'Legs', 'Alliance', 'Leggings of the Deepening Void'), +(5, 0, 6, 2, 258, 47478, 'Phase 3', 'Priest', 'Discipline', 'Legs', 'Horde', 'Breeches of the Deepening Void'), +(5, 0, 7, 1, 258, 47097, 'Phase 3', 'Priest', 'Discipline', 'Feet', 'Alliance', 'Boots of the Mourning Widow'), +(5, 0, 7, 2, 258, 47454, 'Phase 3', 'Priest', 'Discipline', 'Feet', 'Horde', 'Sandals of the Mourning Widow'), +(5, 0, 8, 1, 258, 47143, 'Phase 3', 'Priest', 'Discipline', 'Wrists', 'Alliance', 'Bindings of Dark Essence'), +(5, 0, 8, 2, 258, 47467, 'Phase 3', 'Priest', 'Discipline', 'Wrists', 'Horde', 'Dark Essence Bindings'), +(5, 0, 9, 0, 258, 46188, 'Phase 3', 'Priest', 'Discipline', 'Hands', 'Both', 'Conqueror''s Gloves of Sanctification'), +(5, 0, 10, 1, 258, 47237, 'Phase 3', 'Priest', 'Discipline', 'Finger1', 'Alliance', 'Band of Deplorable Violence'), +(5, 0, 10, 2, 258, 47489, 'Phase 3', 'Priest', 'Discipline', 'Finger1', 'Horde', 'Lurid Manifestation'), +(5, 0, 12, 1, 258, 47059, 'Phase 3', 'Priest', 'Discipline', 'Trinket1', 'Alliance', 'Solace of the Defeated'), +(5, 0, 12, 2, 258, 47432, 'Phase 3', 'Priest', 'Discipline', 'Trinket1', 'Horde', 'Solace of the Fallen'), +(5, 0, 14, 1, 258, 47552, 'Phase 3', 'Priest', 'Discipline', 'Back', 'Alliance', 'Jaina''s Radiance'), +(5, 0, 14, 2, 258, 47551, 'Phase 3', 'Priest', 'Discipline', 'Back', 'Horde', 'Aethas'' Intensity'), +(5, 0, 17, 0, 258, 45294, 'Phase 3', 'Priest', 'Discipline', 'Ranged', 'Both', 'Petrified Ivy Sprig'); + +-- ilvl 264 (Phase 4) +INSERT INTO `playerbots_bis_gear` VALUES +(5, 0, 0, 0, 264, 51261, 'Phase 4', 'Priest', 'Discipline', 'Head', 'Both', 'Sanctified Crimson Acolyte Hood'), +(5, 0, 2, 0, 264, 51264, 'Phase 4', 'Priest', 'Discipline', 'Shoulders', 'Both', 'Sanctified Crimson Acolyte Shoulderpads'), +(5, 0, 4, 0, 264, 51263, 'Phase 4', 'Priest', 'Discipline', 'Chest', 'Both', 'Sanctified Crimson Acolyte Robe'), +(5, 0, 5, 0, 264, 50702, 'Phase 4', 'Priest', 'Discipline', 'Waist', 'Both', 'Lingering Illness'), +(5, 0, 6, 0, 264, 51262, 'Phase 4', 'Priest', 'Discipline', 'Legs', 'Both', 'Sanctified Crimson Acolyte Leggings'), +(5, 0, 7, 0, 264, 50699, 'Phase 4', 'Priest', 'Discipline', 'Feet', 'Both', 'Plague Scientist''s Boots'), +(5, 0, 8, 0, 264, 50686, 'Phase 4', 'Priest', 'Discipline', 'Wrists', 'Both', 'Death Surgeon''s Sleeves'), +(5, 0, 9, 0, 264, 50722, 'Phase 4', 'Priest', 'Discipline', 'Hands', 'Both', 'San''layn Ritualist Gloves'), +(5, 0, 10, 0, 264, 50720, 'Phase 4', 'Priest', 'Discipline', 'Finger1', 'Both', 'Incarnadine Band of Mending'), +(5, 0, 12, 1, 264, 47059, 'Phase 4', 'Priest', 'Discipline', 'Trinket1', 'Alliance', 'Solace of the Defeated'), +(5, 0, 12, 2, 264, 47432, 'Phase 4', 'Priest', 'Discipline', 'Trinket1', 'Horde', 'Solace of the Fallen'), +(5, 0, 14, 0, 264, 50628, 'Phase 4', 'Priest', 'Discipline', 'Back', 'Both', 'Frostbinder''s Shredded Cape'), +(5, 0, 15, 0, 264, 50734, 'Phase 4', 'Priest', 'Discipline', 'MainHand', 'Both', 'Royal Scepter of Terenas II'), +(5, 0, 17, 0, 264, 50631, 'Phase 4', 'Priest', 'Discipline', 'Ranged', 'Both', 'Nightmare Ender'); + +-- ilvl 290 (Phase 5) +INSERT INTO `playerbots_bis_gear` VALUES +(5, 0, 0, 0, 290, 51261, 'Phase 5', 'Priest', 'Discipline', 'Head', 'Both', 'Sanctified Crimson Acolyte Hood'), +(5, 0, 1, 0, 290, 50182, 'Phase 5', 'Priest', 'Discipline', 'Neck', 'Both', 'Blood Queen''s Crimson Choker'), +(5, 0, 2, 0, 290, 51264, 'Phase 5', 'Priest', 'Discipline', 'Shoulders', 'Both', 'Sanctified Crimson Acolyte Shoulderpads'), +(5, 0, 4, 0, 290, 51263, 'Phase 5', 'Priest', 'Discipline', 'Chest', 'Both', 'Sanctified Crimson Acolyte Robe'), +(5, 0, 5, 0, 290, 50613, 'Phase 5', 'Priest', 'Discipline', 'Waist', 'Both', 'Crushing Coldwraith Belt'), +(5, 0, 6, 0, 290, 51262, 'Phase 5', 'Priest', 'Discipline', 'Legs', 'Both', 'Sanctified Crimson Acolyte Leggings'), +(5, 0, 7, 0, 290, 50699, 'Phase 5', 'Priest', 'Discipline', 'Feet', 'Both', 'Plague Scientist''s Boots'), +(5, 0, 8, 0, 290, 54582, 'Phase 5', 'Priest', 'Discipline', 'Wrists', 'Both', 'Bracers of Fiery Night'), +(5, 0, 9, 0, 290, 50722, 'Phase 5', 'Priest', 'Discipline', 'Hands', 'Both', 'San''layn Ritualist Gloves'), +(5, 0, 10, 0, 290, 50644, 'Phase 5', 'Priest', 'Discipline', 'Finger1', 'Both', 'Ring of Maddening Whispers'), +(5, 0, 11, 0, 290, 50636, 'Phase 5', 'Priest', 'Discipline', 'Finger2', 'Both', 'Memory of Malygos'), +(5, 0, 12, 0, 290, 50366, 'Phase 5', 'Priest', 'Discipline', 'Trinket1', 'Both', 'Althor''s Abacus'), +(5, 0, 13, 0, 290, 54589, 'Phase 5', 'Priest', 'Discipline', 'Trinket2', 'Both', 'Glowing Twilight Scale'), +(5, 0, 14, 0, 290, 54583, 'Phase 5', 'Priest', 'Discipline', 'Back', 'Both', 'Cloak of Burning Dusk'), +(5, 0, 15, 0, 290, 50734, 'Phase 5', 'Priest', 'Discipline', 'MainHand', 'Both', 'Royal Scepter of Terenas II'), +(5, 0, 16, 0, 290, 50719, 'Phase 5', 'Priest', 'Discipline', 'OffHand', 'Both', 'Shadow Silk Spindle'), +(5, 0, 17, 0, 290, 50631, 'Phase 5', 'Priest', 'Discipline', 'Ranged', 'Both', 'Nightmare Ender'); + +-- Holy (tab 1) +-- ilvl 66 (Phase 1 (Pre-Raid)) +INSERT INTO `playerbots_bis_gear` VALUES +(5, 1, 0, 0, 66, 13102, 'Phase 1 (Pre-Raid)', 'Priest', 'Holy', 'Head', 'Both', 'Cassandra''s Grace'), +(5, 1, 1, 0, 66, 18723, 'Phase 1 (Pre-Raid)', 'Priest', 'Holy', 'Neck', 'Both', 'Animated Chain Necklace'), +(5, 1, 2, 0, 66, 13013, 'Phase 1 (Pre-Raid)', 'Priest', 'Holy', 'Shoulders', 'Both', 'Elder Wizard''s Mantle'), +(5, 1, 4, 0, 66, 14154, 'Phase 1 (Pre-Raid)', 'Priest', 'Holy', 'Chest', 'Both', 'Truefaith Vestments'), +(5, 1, 5, 0, 66, 14143, 'Phase 1 (Pre-Raid)', 'Priest', 'Holy', 'Waist', 'Both', 'Ghostweave Belt'), +(5, 1, 6, 0, 66, 11841, 'Phase 1 (Pre-Raid)', 'Priest', 'Holy', 'Legs', 'Both', 'Senior Designer''s Pantaloons'), +(5, 1, 7, 0, 66, 11822, 'Phase 1 (Pre-Raid)', 'Priest', 'Holy', 'Feet', 'Both', 'Omnicast Boots'), +(5, 1, 8, 0, 66, 11766, 'Phase 1 (Pre-Raid)', 'Priest', 'Holy', 'Wrists', 'Both', 'Flameweave Cuffs'), +(5, 1, 9, 0, 66, 10787, 'Phase 1 (Pre-Raid)', 'Priest', 'Holy', 'Hands', 'Both', 'Atal''ai Gloves'), +(5, 1, 10, 0, 66, 16058, 'Phase 1 (Pre-Raid)', 'Priest', 'Holy', 'Finger1', 'Both', 'Fordring''s Seal'), +(5, 1, 11, 0, 66, 13178, 'Phase 1 (Pre-Raid)', 'Priest', 'Holy', 'Finger2', 'Both', 'Rosewine Circle'), +(5, 1, 12, 0, 66, 11819, 'Phase 1 (Pre-Raid)', 'Priest', 'Holy', 'Trinket1', 'Both', 'Second Wind'), +(5, 1, 13, 0, 66, 12930, 'Phase 1 (Pre-Raid)', 'Priest', 'Holy', 'Trinket2', 'Both', 'Briarwood Reed'), +(5, 1, 14, 0, 66, 13386, 'Phase 1 (Pre-Raid)', 'Priest', 'Holy', 'Back', 'Both', 'Archivist Cape'), +(5, 1, 15, 0, 66, 11923, 'Phase 1 (Pre-Raid)', 'Priest', 'Holy', 'MainHand', 'Both', 'The Hammer of Grace'), +(5, 1, 16, 0, 66, 11928, 'Phase 1 (Pre-Raid)', 'Priest', 'Holy', 'OffHand', 'Both', 'Thaurissan''s Royal Scepter'), +(5, 1, 17, 0, 66, 16997, 'Phase 1 (Pre-Raid)', 'Priest', 'Holy', 'Ranged', 'Both', 'Stormrager'); + +-- ilvl 76 (Phase 2 (Pre-Raid)) +INSERT INTO `playerbots_bis_gear` VALUES +(5, 1, 0, 0, 76, 13102, 'Phase 2 (Pre-Raid)', 'Priest', 'Holy', 'Head', 'Both', 'Cassandra''s Grace'), +(5, 1, 1, 0, 76, 18723, 'Phase 2 (Pre-Raid)', 'Priest', 'Holy', 'Neck', 'Both', 'Animated Chain Necklace'), +(5, 1, 2, 0, 76, 13013, 'Phase 2 (Pre-Raid)', 'Priest', 'Holy', 'Shoulders', 'Both', 'Elder Wizard''s Mantle'), +(5, 1, 4, 0, 76, 14154, 'Phase 2 (Pre-Raid)', 'Priest', 'Holy', 'Chest', 'Both', 'Truefaith Vestments'), +(5, 1, 5, 0, 76, 18327, 'Phase 2 (Pre-Raid)', 'Priest', 'Holy', 'Waist', 'Both', 'Whipvine Cord'), +(5, 1, 6, 0, 76, 18386, 'Phase 2 (Pre-Raid)', 'Priest', 'Holy', 'Legs', 'Both', 'Padre''s Trousers'), +(5, 1, 7, 0, 76, 18507, 'Phase 2 (Pre-Raid)', 'Priest', 'Holy', 'Feet', 'Both', 'Boots of the Full Moon'), +(5, 1, 8, 0, 76, 11766, 'Phase 2 (Pre-Raid)', 'Priest', 'Holy', 'Wrists', 'Both', 'Flameweave Cuffs'), +(5, 1, 9, 0, 76, 10787, 'Phase 2 (Pre-Raid)', 'Priest', 'Holy', 'Hands', 'Both', 'Atal''ai Gloves'), +(5, 1, 10, 0, 76, 16058, 'Phase 2 (Pre-Raid)', 'Priest', 'Holy', 'Finger1', 'Both', 'Fordring''s Seal'), +(5, 1, 11, 0, 76, 13178, 'Phase 2 (Pre-Raid)', 'Priest', 'Holy', 'Finger2', 'Both', 'Rosewine Circle'), +(5, 1, 12, 0, 76, 18469, 'Phase 2 (Pre-Raid)', 'Priest', 'Holy', 'Trinket1', 'Both', 'Royal Seal of Eldre''Thalas'), +(5, 1, 13, 0, 76, 18371, 'Phase 2 (Pre-Raid)', 'Priest', 'Holy', 'Trinket2', 'Both', 'Mindtap Talisman'), +(5, 1, 14, 0, 76, 18510, 'Phase 2 (Pre-Raid)', 'Priest', 'Holy', 'Back', 'Both', 'Hide of the Wild'), +(5, 1, 15, 0, 76, 11923, 'Phase 2 (Pre-Raid)', 'Priest', 'Holy', 'MainHand', 'Both', 'The Hammer of Grace'), +(5, 1, 16, 0, 76, 18523, 'Phase 2 (Pre-Raid)', 'Priest', 'Holy', 'OffHand', 'Both', 'Brightly Glowing Stone'), +(5, 1, 17, 0, 76, 18483, 'Phase 2 (Pre-Raid)', 'Priest', 'Holy', 'Ranged', 'Both', 'Mana Channeling Wand'); + +-- ilvl 78 (Phase 2) +INSERT INTO `playerbots_bis_gear` VALUES +(5, 1, 0, 0, 78, 16921, 'Phase 2', 'Priest', 'Holy', 'Head', 'Both', 'Halo of Transcendence'), +(5, 1, 1, 0, 78, 18723, 'Phase 2', 'Priest', 'Holy', 'Neck', 'Both', 'Animated Chain Necklace'), +(5, 1, 2, 0, 78, 16816, 'Phase 2', 'Priest', 'Holy', 'Shoulders', 'Both', 'Mantle of Prophecy'), +(5, 1, 4, 0, 78, 14154, 'Phase 2', 'Priest', 'Holy', 'Chest', 'Both', 'Truefaith Vestments'), +(5, 1, 5, 0, 78, 16817, 'Phase 2', 'Priest', 'Holy', 'Waist', 'Both', 'Girdle of Prophecy'), +(5, 1, 6, 0, 78, 16922, 'Phase 2', 'Priest', 'Holy', 'Legs', 'Both', 'Leggings of Transcendence'), +(5, 1, 7, 0, 78, 16811, 'Phase 2', 'Priest', 'Holy', 'Feet', 'Both', 'Boots of Prophecy'), +(5, 1, 8, 0, 78, 16819, 'Phase 2', 'Priest', 'Holy', 'Wrists', 'Both', 'Vambraces of Prophecy'), +(5, 1, 9, 0, 78, 16812, 'Phase 2', 'Priest', 'Holy', 'Hands', 'Both', 'Gloves of Prophecy'), +(5, 1, 10, 0, 78, 19140, 'Phase 2', 'Priest', 'Holy', 'Finger1', 'Both', 'Cauterizing Band'), +(5, 1, 11, 0, 78, 19140, 'Phase 2', 'Priest', 'Holy', 'Finger2', 'Both', 'Cauterizing Band'), +(5, 1, 12, 0, 78, 18469, 'Phase 2', 'Priest', 'Holy', 'Trinket1', 'Both', 'Royal Seal of Eldre''Thalas'), +(5, 1, 13, 0, 78, 17064, 'Phase 2', 'Priest', 'Holy', 'Trinket2', 'Both', 'Shard of the Scale'), +(5, 1, 14, 0, 78, 18510, 'Phase 2', 'Priest', 'Holy', 'Back', 'Both', 'Hide of the Wild'), +(5, 1, 15, 0, 78, 18608, 'Phase 2', 'Priest', 'Holy', 'MainHand', 'Both', 'Benediction'), +(5, 1, 17, 0, 78, 18483, 'Phase 2', 'Priest', 'Holy', 'Ranged', 'Both', 'Mana Channeling Wand'); + +-- ilvl 83 (Phase 3) +INSERT INTO `playerbots_bis_gear` VALUES +(5, 1, 0, 0, 83, 16921, 'Phase 3', 'Priest', 'Holy', 'Head', 'Both', 'Halo of Transcendence'), +(5, 1, 1, 0, 83, 18723, 'Phase 3', 'Priest', 'Holy', 'Neck', 'Both', 'Animated Chain Necklace'), +(5, 1, 2, 0, 83, 16924, 'Phase 3', 'Priest', 'Holy', 'Shoulders', 'Both', 'Pauldrons of Transcendence'), +(5, 1, 4, 0, 83, 16923, 'Phase 3', 'Priest', 'Holy', 'Chest', 'Both', 'Robes of Transcendence'), +(5, 1, 5, 0, 83, 16925, 'Phase 3', 'Priest', 'Holy', 'Waist', 'Both', 'Belt of Transcendence'), +(5, 1, 6, 0, 83, 16922, 'Phase 3', 'Priest', 'Holy', 'Legs', 'Both', 'Leggings of Transcendence'), +(5, 1, 7, 0, 83, 16919, 'Phase 3', 'Priest', 'Holy', 'Feet', 'Both', 'Boots of Transcendence'), +(5, 1, 8, 0, 83, 16926, 'Phase 3', 'Priest', 'Holy', 'Wrists', 'Both', 'Bindings of Transcendence'), +(5, 1, 9, 0, 83, 16920, 'Phase 3', 'Priest', 'Holy', 'Hands', 'Both', 'Handguards of Transcendence'), +(5, 1, 10, 0, 83, 19382, 'Phase 3', 'Priest', 'Holy', 'Finger1', 'Both', 'Pure Elementium Band'), +(5, 1, 11, 0, 83, 19140, 'Phase 3', 'Priest', 'Holy', 'Finger2', 'Both', 'Cauterizing Band'), +(5, 1, 12, 0, 83, 19395, 'Phase 3', 'Priest', 'Holy', 'Trinket1', 'Both', 'Rejuvenating Gem'), +(5, 1, 13, 0, 83, 17064, 'Phase 3', 'Priest', 'Holy', 'Trinket2', 'Both', 'Shard of the Scale'), +(5, 1, 14, 0, 83, 19430, 'Phase 3', 'Priest', 'Holy', 'Back', 'Both', 'Shroud of Pure Thought'), +(5, 1, 15, 0, 83, 18608, 'Phase 3', 'Priest', 'Holy', 'MainHand', 'Both', 'Benediction'), +(5, 1, 17, 0, 83, 19435, 'Phase 3', 'Priest', 'Holy', 'Ranged', 'Both', 'Essence Gatherer'); + +-- ilvl 88 (Phase 5) +INSERT INTO `playerbots_bis_gear` VALUES +(5, 1, 0, 0, 88, 21615, 'Phase 5', 'Priest', 'Holy', 'Head', 'Both', 'Don Rigoberto''s Lost Hat'), +(5, 1, 1, 0, 88, 21712, 'Phase 5', 'Priest', 'Holy', 'Neck', 'Both', 'Amulet of the Fallen God'), +(5, 1, 2, 0, 88, 16924, 'Phase 5', 'Priest', 'Holy', 'Shoulders', 'Both', 'Pauldrons of Transcendence'), +(5, 1, 4, 0, 88, 21663, 'Phase 5', 'Priest', 'Holy', 'Chest', 'Both', 'Robes of the Guardian Saint'), +(5, 1, 5, 0, 88, 21582, 'Phase 5', 'Priest', 'Holy', 'Waist', 'Both', 'Grasp of the Old God'), +(5, 1, 6, 0, 88, 16922, 'Phase 5', 'Priest', 'Holy', 'Legs', 'Both', 'Leggings of Transcendence'), +(5, 1, 7, 0, 88, 19437, 'Phase 5', 'Priest', 'Holy', 'Feet', 'Both', 'Boots of Pure Thought'), +(5, 1, 8, 0, 88, 16926, 'Phase 5', 'Priest', 'Holy', 'Wrists', 'Both', 'Bindings of Transcendence'), +(5, 1, 9, 0, 88, 21619, 'Phase 5', 'Priest', 'Holy', 'Hands', 'Both', 'Gloves of the Messiah'), +(5, 1, 10, 0, 88, 19382, 'Phase 5', 'Priest', 'Holy', 'Finger1', 'Both', 'Pure Elementium Band'), +(5, 1, 11, 0, 88, 21620, 'Phase 5', 'Priest', 'Holy', 'Finger2', 'Both', 'Ring of the Martyr'), +(5, 1, 12, 0, 88, 19395, 'Phase 5', 'Priest', 'Holy', 'Trinket1', 'Both', 'Rejuvenating Gem'), +(5, 1, 13, 0, 88, 17064, 'Phase 5', 'Priest', 'Holy', 'Trinket2', 'Both', 'Shard of the Scale'), +(5, 1, 14, 0, 88, 21583, 'Phase 5', 'Priest', 'Holy', 'Back', 'Both', 'Cloak of Clarity'), +(5, 1, 15, 0, 88, 21839, 'Phase 5', 'Priest', 'Holy', 'MainHand', 'Both', 'Scepter of the False Prophet'), +(5, 1, 16, 0, 88, 21666, 'Phase 5', 'Priest', 'Holy', 'OffHand', 'Both', 'Sartura''s Might'), +(5, 1, 17, 0, 88, 21801, 'Phase 5', 'Priest', 'Holy', 'Ranged', 'Both', 'Antenna of Invigoration'); + +-- ilvl 92 (Phase 6) +INSERT INTO `playerbots_bis_gear` VALUES +(5, 1, 0, 0, 92, 22514, 'Phase 6', 'Priest', 'Holy', 'Head', 'Both', 'Circlet of Faith'), +(5, 1, 1, 0, 92, 21712, 'Phase 6', 'Priest', 'Holy', 'Neck', 'Both', 'Amulet of the Fallen God'), +(5, 1, 2, 0, 92, 22515, 'Phase 6', 'Priest', 'Holy', 'Shoulders', 'Both', 'Shoulderpads of Faith'), +(5, 1, 4, 0, 92, 22512, 'Phase 6', 'Priest', 'Holy', 'Chest', 'Both', 'Robe of Faith'), +(5, 1, 5, 0, 92, 21582, 'Phase 6', 'Priest', 'Holy', 'Waist', 'Both', 'Grasp of the Old God'), +(5, 1, 6, 0, 92, 22513, 'Phase 6', 'Priest', 'Holy', 'Legs', 'Both', 'Leggings of Faith'), +(5, 1, 7, 0, 92, 22516, 'Phase 6', 'Priest', 'Holy', 'Feet', 'Both', 'Sandals of Faith'), +(5, 1, 8, 0, 92, 21604, 'Phase 6', 'Priest', 'Holy', 'Wrists', 'Both', 'Bracelets of Royal Redemption'), +(5, 1, 9, 0, 92, 22517, 'Phase 6', 'Priest', 'Holy', 'Hands', 'Both', 'Gloves of Faith'), +(5, 1, 10, 0, 92, 23061, 'Phase 6', 'Priest', 'Holy', 'Finger1', 'Both', 'Ring of Faith'), +(5, 1, 11, 0, 92, 22939, 'Phase 6', 'Priest', 'Holy', 'Finger2', 'Both', 'Band of Unanswered Prayers'), +(5, 1, 12, 0, 92, 23027, 'Phase 6', 'Priest', 'Holy', 'Trinket1', 'Both', 'Warmth of Forgiveness'), +(5, 1, 13, 0, 92, 23047, 'Phase 6', 'Priest', 'Holy', 'Trinket2', 'Both', 'Eye of the Dead'), +(5, 1, 14, 0, 92, 22960, 'Phase 6', 'Priest', 'Holy', 'Back', 'Both', 'Cloak of Suturing'), +(5, 1, 15, 0, 92, 23056, 'Phase 6', 'Priest', 'Holy', 'MainHand', 'Both', 'Hammer of the Twisting Nether'), +(5, 1, 16, 0, 92, 23048, 'Phase 6', 'Priest', 'Holy', 'OffHand', 'Both', 'Sapphiron''s Right Eye'), +(5, 1, 17, 0, 92, 23009, 'Phase 6', 'Priest', 'Holy', 'Ranged', 'Both', 'Wand of the Whispering Dead'); + +-- ilvl 120 (Pre-Raid) +INSERT INTO `playerbots_bis_gear` VALUES +(5, 1, 0, 0, 120, 24264, 'Pre-Raid', 'Priest', 'Holy', 'Head', 'Both', 'Whitemend Hood'), +(5, 1, 1, 0, 120, 30377, 'Pre-Raid', 'Priest', 'Holy', 'Neck', 'Both', 'Karja''s Medallion'), +(5, 1, 2, 0, 120, 21874, 'Pre-Raid', 'Priest', 'Holy', 'Shoulders', 'Both', 'Primal Mooncloth Shoulders'), +(5, 1, 4, 0, 120, 21875, 'Pre-Raid', 'Priest', 'Holy', 'Chest', 'Both', 'Primal Mooncloth Robe'), +(5, 1, 5, 0, 120, 21873, 'Pre-Raid', 'Priest', 'Holy', 'Waist', 'Both', 'Primal Mooncloth Belt'), +(5, 1, 6, 0, 120, 24261, 'Pre-Raid', 'Priest', 'Holy', 'Legs', 'Both', 'Whitemend Pants'), +(5, 1, 7, 0, 120, 29251, 'Pre-Raid', 'Priest', 'Holy', 'Feet', 'Both', 'Boots of the Pious'), +(5, 1, 8, 0, 120, 29249, 'Pre-Raid', 'Priest', 'Holy', 'Wrists', 'Both', 'Bands of the Benevolent'), +(5, 1, 9, 0, 120, 24393, 'Pre-Raid', 'Priest', 'Holy', 'Hands', 'Both', 'Bloody Surgeon''s Mitts'), +(5, 1, 10, 0, 120, 27780, 'Pre-Raid', 'Priest', 'Holy', 'Finger1', 'Both', 'Ring of Fabled Hope'), +(5, 1, 10, 2, 120, 29168, 'Pre-Raid', 'Priest', 'Holy', 'Finger1', 'Horde', 'Ancestral Band'), +(5, 1, 12, 0, 120, 19288, 'Pre-Raid', 'Priest', 'Holy', 'Trinket1', 'Both', 'Darkmoon Card: Blue Dragon'), +(5, 1, 13, 0, 120, 29376, 'Pre-Raid', 'Priest', 'Holy', 'Trinket2', 'Both', 'Essence of the Martyr'), +(5, 1, 14, 0, 120, 29354, 'Pre-Raid', 'Priest', 'Holy', 'Back', 'Both', 'Light-Touched Stole of Altruism'), +(5, 1, 15, 0, 120, 29353, 'Pre-Raid', 'Priest', 'Holy', 'MainHand', 'Both', 'Shockwave Truncheon'), +(5, 1, 16, 0, 120, 29170, 'Pre-Raid', 'Priest', 'Holy', 'OffHand', 'Both', 'Windcaller''s Orb'), +(5, 1, 17, 0, 120, 27885, 'Pre-Raid', 'Priest', 'Holy', 'Ranged', 'Both', 'Soul-Wand of the Aldor'); + +-- ilvl 125 (Phase 1) +INSERT INTO `playerbots_bis_gear` VALUES +(5, 1, 0, 0, 125, 29049, 'Phase 1', 'Priest', 'Holy', 'Head', 'Both', 'Light-Collar of the Incarnate'), +(5, 1, 1, 0, 125, 30726, 'Phase 1', 'Priest', 'Holy', 'Neck', 'Both', 'Archaic Charm of Presence'), +(5, 1, 2, 0, 125, 21874, 'Phase 1', 'Priest', 'Holy', 'Shoulders', 'Both', 'Primal Mooncloth Shoulders'), +(5, 1, 4, 0, 125, 21875, 'Phase 1', 'Priest', 'Holy', 'Chest', 'Both', 'Primal Mooncloth Robe'), +(5, 1, 5, 0, 125, 21873, 'Phase 1', 'Priest', 'Holy', 'Waist', 'Both', 'Primal Mooncloth Belt'), +(5, 1, 6, 0, 125, 30727, 'Phase 1', 'Priest', 'Holy', 'Legs', 'Both', 'Gilded Trousers of Benediction'), +(5, 1, 7, 0, 125, 28663, 'Phase 1', 'Priest', 'Holy', 'Feet', 'Both', 'Boots of the Incorrupt'), +(5, 1, 8, 0, 125, 29249, 'Phase 1', 'Priest', 'Holy', 'Wrists', 'Both', 'Bands of the Benevolent'), +(5, 1, 9, 0, 125, 28508, 'Phase 1', 'Priest', 'Holy', 'Hands', 'Both', 'Gloves of Saintly Blessings'), +(5, 1, 10, 0, 125, 29290, 'Phase 1', 'Priest', 'Holy', 'Finger1', 'Both', 'Violet Signet of the Grand Restorer'), +(5, 1, 11, 0, 125, 28763, 'Phase 1', 'Priest', 'Holy', 'Finger2', 'Both', 'Jade Ring of the Everliving'), +(5, 1, 12, 0, 125, 29376, 'Phase 1', 'Priest', 'Holy', 'Trinket1', 'Both', 'Essence of the Martyr'), +(5, 1, 13, 0, 125, 28823, 'Phase 1', 'Priest', 'Holy', 'Trinket2', 'Both', 'Eye of Gruul'), +(5, 1, 14, 0, 125, 28765, 'Phase 1', 'Priest', 'Holy', 'Back', 'Both', 'Stainless Cloak of the Pure Hearted'), +(5, 1, 15, 0, 125, 28771, 'Phase 1', 'Priest', 'Holy', 'MainHand', 'Both', 'Light''s Justice'), +(5, 1, 16, 0, 125, 29170, 'Phase 1', 'Priest', 'Holy', 'OffHand', 'Both', 'Windcaller''s Orb'), +(5, 1, 17, 0, 125, 28588, 'Phase 1', 'Priest', 'Holy', 'Ranged', 'Both', 'Blue Diamond Witchwand'); + +-- ilvl 141 (Phase 2) +INSERT INTO `playerbots_bis_gear` VALUES +(5, 1, 0, 0, 141, 30152, 'Phase 2', 'Priest', 'Holy', 'Head', 'Both', 'Cowl of the Avatar'), +(5, 1, 1, 0, 141, 30018, 'Phase 2', 'Priest', 'Holy', 'Neck', 'Both', 'Lord Sanguinar''s Claim'), +(5, 1, 2, 0, 141, 30154, 'Phase 2', 'Priest', 'Holy', 'Shoulders', 'Both', 'Mantle of the Avatar'), +(5, 1, 4, 0, 141, 30150, 'Phase 2', 'Priest', 'Holy', 'Chest', 'Both', 'Vestments of the Avatar'), +(5, 1, 5, 0, 141, 30036, 'Phase 2', 'Priest', 'Holy', 'Waist', 'Both', 'Belt of the Long Road'), +(5, 1, 6, 0, 141, 30727, 'Phase 2', 'Priest', 'Holy', 'Legs', 'Both', 'Gilded Trousers of Benediction'), +(5, 1, 7, 0, 141, 30100, 'Phase 2', 'Priest', 'Holy', 'Feet', 'Both', 'Soul-Strider Boots'), +(5, 1, 8, 0, 141, 32516, 'Phase 2', 'Priest', 'Holy', 'Wrists', 'Both', 'Wraps of Purification'), +(5, 1, 9, 0, 141, 30151, 'Phase 2', 'Priest', 'Holy', 'Hands', 'Both', 'Gloves of the Avatar'), +(5, 1, 10, 0, 141, 30110, 'Phase 2', 'Priest', 'Holy', 'Finger1', 'Both', 'Coral Band of the Revived'), +(5, 1, 11, 0, 141, 29290, 'Phase 2', 'Priest', 'Holy', 'Finger2', 'Both', 'Violet Signet of the Grand Restorer'), +(5, 1, 12, 0, 141, 29376, 'Phase 2', 'Priest', 'Holy', 'Trinket1', 'Both', 'Essence of the Martyr'), +(5, 1, 13, 0, 141, 28823, 'Phase 2', 'Priest', 'Holy', 'Trinket2', 'Both', 'Eye of Gruul'), +(5, 1, 14, 0, 141, 29989, 'Phase 2', 'Priest', 'Holy', 'Back', 'Both', 'Sunshower Light Cloak'), +(5, 1, 15, 0, 141, 30108, 'Phase 2', 'Priest', 'Holy', 'MainHand', 'Both', 'Lightfathom Scepter'), +(5, 1, 16, 0, 141, 29923, 'Phase 2', 'Priest', 'Holy', 'OffHand', 'Both', 'Talisman of the Sun King'), +(5, 1, 17, 0, 141, 30080, 'Phase 2', 'Priest', 'Holy', 'Ranged', 'Both', 'Luminescent Rod of the Naaru'); + +-- ilvl 156 (Phase 3) +INSERT INTO `playerbots_bis_gear` VALUES +(5, 1, 0, 0, 156, 31063, 'Phase 3', 'Priest', 'Holy', 'Head', 'Both', 'Cowl of Absolution'), +(5, 1, 1, 0, 156, 32370, 'Phase 3', 'Priest', 'Holy', 'Neck', 'Both', 'Nadina''s Pendant of Purity'), +(5, 1, 2, 0, 156, 31069, 'Phase 3', 'Priest', 'Holy', 'Shoulders', 'Both', 'Mantle of Absolution'), +(5, 1, 4, 0, 156, 31066, 'Phase 3', 'Priest', 'Holy', 'Chest', 'Both', 'Vestments of Absolution'), +(5, 1, 5, 0, 156, 32519, 'Phase 3', 'Priest', 'Holy', 'Waist', 'Both', 'Belt of Divine Guidance'), +(5, 1, 6, 0, 156, 30912, 'Phase 3', 'Priest', 'Holy', 'Legs', 'Both', 'Leggings of Eternity'), +(5, 1, 7, 0, 156, 32609, 'Phase 3', 'Priest', 'Holy', 'Feet', 'Both', 'Boots of the Divine Light'), +(5, 1, 8, 0, 156, 30871, 'Phase 3', 'Priest', 'Holy', 'Wrists', 'Both', 'Bracers of Martyrdom'), +(5, 1, 9, 0, 156, 31060, 'Phase 3', 'Priest', 'Holy', 'Hands', 'Both', 'Gloves of Absolution'), +(5, 1, 10, 0, 156, 32528, 'Phase 3', 'Priest', 'Holy', 'Finger1', 'Both', 'Blessed Band of Karabor'), +(5, 1, 11, 0, 156, 32528, 'Phase 3', 'Priest', 'Holy', 'Finger2', 'Both', 'Blessed Band of Karabor'), +(5, 1, 12, 0, 156, 29376, 'Phase 3', 'Priest', 'Holy', 'Trinket1', 'Both', 'Essence of the Martyr'), +(5, 1, 13, 0, 156, 32496, 'Phase 3', 'Priest', 'Holy', 'Trinket2', 'Both', 'Memento of Tyrande'), +(5, 1, 14, 0, 156, 32524, 'Phase 3', 'Priest', 'Holy', 'Back', 'Both', 'Shroud of the Highborne'), +(5, 1, 15, 0, 156, 32500, 'Phase 3', 'Priest', 'Holy', 'MainHand', 'Both', 'Crystal Spire of Karabor'), +(5, 1, 16, 0, 156, 30911, 'Phase 3', 'Priest', 'Holy', 'OffHand', 'Both', 'Scepter of Purification'), +(5, 1, 17, 0, 156, 32363, 'Phase 3', 'Priest', 'Holy', 'Ranged', 'Both', 'Naaru-Blessed Life Rod'); + +-- ilvl 164 (Phase 4) +INSERT INTO `playerbots_bis_gear` VALUES +(5, 1, 0, 0, 164, 31063, 'Phase 4', 'Priest', 'Holy', 'Head', 'Both', 'Cowl of Absolution'), +(5, 1, 1, 0, 164, 33281, 'Phase 4', 'Priest', 'Holy', 'Neck', 'Both', 'Brooch of Nature''s Mercy'), +(5, 1, 2, 0, 164, 31069, 'Phase 4', 'Priest', 'Holy', 'Shoulders', 'Both', 'Mantle of Absolution'), +(5, 1, 4, 0, 164, 31066, 'Phase 4', 'Priest', 'Holy', 'Chest', 'Both', 'Vestments of Absolution'), +(5, 1, 5, 0, 164, 32519, 'Phase 4', 'Priest', 'Holy', 'Waist', 'Both', 'Belt of Divine Guidance'), +(5, 1, 6, 0, 164, 30912, 'Phase 4', 'Priest', 'Holy', 'Legs', 'Both', 'Leggings of Eternity'), +(5, 1, 7, 0, 164, 32609, 'Phase 4', 'Priest', 'Holy', 'Feet', 'Both', 'Boots of the Divine Light'), +(5, 1, 8, 0, 164, 30871, 'Phase 4', 'Priest', 'Holy', 'Wrists', 'Both', 'Bracers of Martyrdom'), +(5, 1, 9, 0, 164, 31060, 'Phase 4', 'Priest', 'Holy', 'Hands', 'Both', 'Gloves of Absolution'), +(5, 1, 10, 0, 164, 32528, 'Phase 4', 'Priest', 'Holy', 'Finger1', 'Both', 'Blessed Band of Karabor'), +(5, 1, 11, 0, 164, 30110, 'Phase 4', 'Priest', 'Holy', 'Finger2', 'Both', 'Coral Band of the Revived'), +(5, 1, 12, 0, 164, 29376, 'Phase 4', 'Priest', 'Holy', 'Trinket1', 'Both', 'Essence of the Martyr'), +(5, 1, 13, 0, 164, 32496, 'Phase 4', 'Priest', 'Holy', 'Trinket2', 'Both', 'Memento of Tyrande'), +(5, 1, 14, 0, 164, 32524, 'Phase 4', 'Priest', 'Holy', 'Back', 'Both', 'Shroud of the Highborne'), +(5, 1, 15, 0, 164, 32500, 'Phase 4', 'Priest', 'Holy', 'MainHand', 'Both', 'Crystal Spire of Karabor'), +(5, 1, 16, 0, 164, 30911, 'Phase 4', 'Priest', 'Holy', 'OffHand', 'Both', 'Scepter of Purification'), +(5, 1, 17, 0, 164, 32363, 'Phase 4', 'Priest', 'Holy', 'Ranged', 'Both', 'Naaru-Blessed Life Rod'); + +-- ilvl 200 (Pre-Raid) +INSERT INTO `playerbots_bis_gear` VALUES +(5, 1, 0, 0, 200, 37294, 'Pre-Raid', 'Priest', 'Holy', 'Head', 'Both', 'Crown of Unbridled Magic'), +(5, 1, 1, 0, 200, 40681, 'Pre-Raid', 'Priest', 'Holy', 'Neck', 'Both', 'Lattice Choker of Light'), +(5, 1, 2, 0, 200, 37673, 'Pre-Raid', 'Priest', 'Holy', 'Shoulders', 'Both', 'Dark Runic Mantle'), +(5, 1, 4, 0, 200, 44180, 'Pre-Raid', 'Priest', 'Holy', 'Chest', 'Both', 'Robes of Crackling Flame'), +(5, 1, 5, 0, 200, 40697, 'Pre-Raid', 'Priest', 'Holy', 'Waist', 'Both', 'Elegant Temple Gardens'' Girdle'), +(5, 1, 6, 0, 200, 37854, 'Pre-Raid', 'Priest', 'Holy', 'Legs', 'Both', 'Woven Bracae Leggings'), +(5, 1, 7, 0, 200, 44202, 'Pre-Raid', 'Priest', 'Holy', 'Feet', 'Both', 'Sandals of Crimson Fury'), +(5, 1, 8, 0, 200, 37361, 'Pre-Raid', 'Priest', 'Holy', 'Wrists', 'Both', 'Cuffs of Winged Levitation'), +(5, 1, 9, 0, 200, 37172, 'Pre-Raid', 'Priest', 'Holy', 'Hands', 'Both', 'Gloves of Glistening Runes'), +(5, 1, 10, 0, 200, 37694, 'Pre-Raid', 'Priest', 'Holy', 'Finger1', 'Both', 'Band of Guile'), +(5, 1, 12, 0, 200, 37835, 'Pre-Raid', 'Priest', 'Holy', 'Trinket1', 'Both', 'Je''Tze''s Bell'), +(5, 1, 14, 0, 200, 41610, 'Pre-Raid', 'Priest', 'Holy', 'Back', 'Both', 'Deathchill Cloak'), +(5, 1, 15, 0, 200, 37169, 'Pre-Raid', 'Priest', 'Holy', 'MainHand', 'Both', 'War Mace of Unrequited Love'), +(5, 1, 17, 0, 200, 37619, 'Pre-Raid', 'Priest', 'Holy', 'Ranged', 'Both', 'Wand of Ahn''kahet'); + +-- ilvl 224 (Phase 1) +INSERT INTO `playerbots_bis_gear` VALUES +(5, 1, 0, 0, 224, 40447, 'Phase 1', 'Priest', 'Holy', 'Head', 'Both', 'Valorous Crown of Faith'), +(5, 1, 1, 0, 224, 44661, 'Phase 1', 'Priest', 'Holy', 'Neck', 'Both', 'Wyrmrest Necklace of Power'), +(5, 1, 2, 0, 224, 40450, 'Phase 1', 'Priest', 'Holy', 'Shoulders', 'Both', 'Valorous Shoulderpads of Faith'), +(5, 1, 4, 0, 224, 44002, 'Phase 1', 'Priest', 'Holy', 'Chest', 'Both', 'The Sanctum''s Flowing Vestments'), +(5, 1, 5, 0, 224, 40561, 'Phase 1', 'Priest', 'Holy', 'Waist', 'Both', 'Leash of Heedless Magic'), +(5, 1, 6, 0, 224, 40448, 'Phase 1', 'Priest', 'Holy', 'Legs', 'Both', 'Valorous Leggings of Faith'), +(5, 1, 7, 0, 224, 40558, 'Phase 1', 'Priest', 'Holy', 'Feet', 'Both', 'Arcanic Tramplers'), +(5, 1, 8, 0, 224, 44008, 'Phase 1', 'Priest', 'Holy', 'Wrists', 'Both', 'Unsullied Cuffs'), +(5, 1, 9, 0, 224, 40445, 'Phase 1', 'Priest', 'Holy', 'Hands', 'Both', 'Valorous Gloves of Faith'), +(5, 1, 10, 0, 224, 40399, 'Phase 1', 'Priest', 'Holy', 'Finger1', 'Both', 'Signet of Manifested Pain'), +(5, 1, 12, 0, 224, 37835, 'Phase 1', 'Priest', 'Holy', 'Trinket1', 'Both', 'Je''Tze''s Bell'), +(5, 1, 14, 0, 224, 44005, 'Phase 1', 'Priest', 'Holy', 'Back', 'Both', 'Pennant Cloak'), +(5, 1, 17, 0, 224, 39426, 'Phase 1', 'Priest', 'Holy', 'Ranged', 'Both', 'Wand of the Archlich'); + +-- ilvl 245 (Phase 2) +INSERT INTO `playerbots_bis_gear` VALUES +(5, 1, 0, 0, 245, 45497, 'Phase 2', 'Priest', 'Holy', 'Head', 'Both', 'Crown of Luminescence'), +(5, 1, 1, 0, 245, 45243, 'Phase 2', 'Priest', 'Holy', 'Neck', 'Both', 'Sapphire Amulet of Renewal'), +(5, 1, 2, 0, 245, 46068, 'Phase 2', 'Priest', 'Holy', 'Shoulders', 'Both', 'Amice of Inconceivable Horror'), +(5, 1, 4, 0, 245, 45240, 'Phase 2', 'Priest', 'Holy', 'Chest', 'Both', 'Raiments of the Iron Council'), +(5, 1, 5, 0, 245, 45619, 'Phase 2', 'Priest', 'Holy', 'Waist', 'Both', 'Starwatcher''s Binding'), +(5, 1, 6, 0, 245, 46195, 'Phase 2', 'Priest', 'Holy', 'Legs', 'Both', 'Conqueror''s Leggings of Sanctification'), +(5, 1, 7, 0, 245, 45135, 'Phase 2', 'Priest', 'Holy', 'Feet', 'Both', 'Boots of Fiery Resolution'), +(5, 1, 8, 0, 245, 45460, 'Phase 2', 'Priest', 'Holy', 'Wrists', 'Both', 'Bindings of Winter Gale'), +(5, 1, 9, 0, 245, 46188, 'Phase 2', 'Priest', 'Holy', 'Hands', 'Both', 'Conqueror''s Gloves of Sanctification'), +(5, 1, 12, 0, 245, 45535, 'Phase 2', 'Priest', 'Holy', 'Trinket1', 'Both', 'Show of Faith'), +(5, 1, 14, 0, 245, 45618, 'Phase 2', 'Priest', 'Holy', 'Back', 'Both', 'Sunglimmer Cloak'), +(5, 1, 15, 0, 245, 46017, 'Phase 2', 'Priest', 'Holy', 'MainHand', 'Both', 'Val''anyr, Hammer of Ancient Kings'), +(5, 1, 17, 0, 245, 45170, 'Phase 2', 'Priest', 'Holy', 'Ranged', 'Both', 'Scepter of Creation'); + +-- ilvl 258 (Phase 3) +INSERT INTO `playerbots_bis_gear` VALUES +(5, 1, 0, 0, 258, 48035, 'Phase 3', 'Priest', 'Holy', 'Head', 'Both', 'Cowl of Triumph'), +(5, 1, 2, 0, 258, 48029, 'Phase 3', 'Priest', 'Holy', 'Shoulders', 'Both', 'Shoulderpads of Triumph'), +(5, 1, 4, 1, 258, 46993, 'Phase 3', 'Priest', 'Holy', 'Chest', 'Alliance', 'Flowing Vestments of Ascent'), +(5, 1, 4, 2, 258, 47425, 'Phase 3', 'Priest', 'Holy', 'Chest', 'Horde', 'Flowing Robes of Ascent'), +(5, 1, 5, 1, 258, 46973, 'Phase 3', 'Priest', 'Holy', 'Waist', 'Alliance', 'Cord of the Tenebrous Mist'), +(5, 1, 5, 2, 258, 47419, 'Phase 3', 'Priest', 'Holy', 'Waist', 'Horde', 'Belt of the Tenebrous Mist'), +(5, 1, 6, 1, 258, 47062, 'Phase 3', 'Priest', 'Holy', 'Legs', 'Alliance', 'Leggings of the Soothing Touch'), +(5, 1, 6, 2, 258, 47435, 'Phase 3', 'Priest', 'Holy', 'Legs', 'Horde', 'Pants of the Soothing Touch'), +(5, 1, 7, 1, 258, 47097, 'Phase 3', 'Priest', 'Holy', 'Feet', 'Alliance', 'Boots of the Mourning Widow'), +(5, 1, 7, 2, 258, 47454, 'Phase 3', 'Priest', 'Holy', 'Feet', 'Horde', 'Sandals of the Mourning Widow'), +(5, 1, 8, 1, 258, 47208, 'Phase 3', 'Priest', 'Holy', 'Wrists', 'Alliance', 'Armbands of the Ashen Saint'), +(5, 1, 8, 2, 258, 47485, 'Phase 3', 'Priest', 'Holy', 'Wrists', 'Horde', 'Bindings of the Ashen Saint'), +(5, 1, 9, 0, 258, 45665, 'Phase 3', 'Priest', 'Holy', 'Hands', 'Both', 'Pharos Gloves'), +(5, 1, 10, 1, 258, 47224, 'Phase 3', 'Priest', 'Holy', 'Finger1', 'Alliance', 'Ring of the Darkmender'), +(5, 1, 10, 2, 258, 47439, 'Phase 3', 'Priest', 'Holy', 'Finger1', 'Horde', 'Circle of the Darkmender'), +(5, 1, 12, 1, 258, 47059, 'Phase 3', 'Priest', 'Holy', 'Trinket1', 'Alliance', 'Solace of the Defeated'), +(5, 1, 12, 2, 258, 47432, 'Phase 3', 'Priest', 'Holy', 'Trinket1', 'Horde', 'Solace of the Fallen'), +(5, 1, 14, 1, 258, 47552, 'Phase 3', 'Priest', 'Holy', 'Back', 'Alliance', 'Jaina''s Radiance'), +(5, 1, 14, 2, 258, 47551, 'Phase 3', 'Priest', 'Holy', 'Back', 'Horde', 'Aethas'' Intensity'); + +-- ilvl 264 (Phase 4) +INSERT INTO `playerbots_bis_gear` VALUES +(5, 1, 0, 0, 264, 51261, 'Phase 4', 'Priest', 'Holy', 'Head', 'Both', 'Sanctified Crimson Acolyte Hood'), +(5, 1, 2, 0, 264, 51264, 'Phase 4', 'Priest', 'Holy', 'Shoulders', 'Both', 'Sanctified Crimson Acolyte Shoulderpads'), +(5, 1, 4, 0, 264, 50717, 'Phase 4', 'Priest', 'Holy', 'Chest', 'Both', 'Sanguine Silk Robes'), +(5, 1, 5, 0, 264, 50613, 'Phase 4', 'Priest', 'Holy', 'Waist', 'Both', 'Crushing Coldwraith Belt'), +(5, 1, 6, 0, 264, 51262, 'Phase 4', 'Priest', 'Holy', 'Legs', 'Both', 'Sanctified Crimson Acolyte Leggings'), +(5, 1, 7, 0, 264, 50699, 'Phase 4', 'Priest', 'Holy', 'Feet', 'Both', 'Plague Scientist''s Boots'), +(5, 1, 8, 0, 264, 50686, 'Phase 4', 'Priest', 'Holy', 'Wrists', 'Both', 'Death Surgeon''s Sleeves'), +(5, 1, 9, 0, 264, 51260, 'Phase 4', 'Priest', 'Holy', 'Hands', 'Both', 'Sanctified Crimson Acolyte Gloves'), +(5, 1, 10, 0, 264, 50636, 'Phase 4', 'Priest', 'Holy', 'Finger1', 'Both', 'Memory of Malygos'), +(5, 1, 12, 1, 264, 47059, 'Phase 4', 'Priest', 'Holy', 'Trinket1', 'Alliance', 'Solace of the Defeated'), +(5, 1, 12, 2, 264, 47432, 'Phase 4', 'Priest', 'Holy', 'Trinket1', 'Horde', 'Solace of the Fallen'), +(5, 1, 14, 0, 264, 50668, 'Phase 4', 'Priest', 'Holy', 'Back', 'Both', 'Greatcloak of the Turned Champion'), +(5, 1, 15, 0, 264, 46017, 'Phase 4', 'Priest', 'Holy', 'MainHand', 'Both', 'Val''anyr, Hammer of Ancient Kings'), +(5, 1, 17, 0, 264, 50684, 'Phase 4', 'Priest', 'Holy', 'Ranged', 'Both', 'Corpse-Impaling Spike'); + +-- ilvl 290 (Phase 5) +INSERT INTO `playerbots_bis_gear` VALUES +(5, 1, 0, 0, 290, 51261, 'Phase 5', 'Priest', 'Holy', 'Head', 'Both', 'Sanctified Crimson Acolyte Hood'), +(5, 1, 1, 0, 290, 50609, 'Phase 5', 'Priest', 'Holy', 'Neck', 'Both', 'Bone Sentinel''s Amulet'), +(5, 1, 2, 0, 290, 51264, 'Phase 5', 'Priest', 'Holy', 'Shoulders', 'Both', 'Sanctified Crimson Acolyte Shoulderpads'), +(5, 1, 4, 0, 290, 50717, 'Phase 5', 'Priest', 'Holy', 'Chest', 'Both', 'Sanguine Silk Robes'), +(5, 1, 5, 0, 290, 50702, 'Phase 5', 'Priest', 'Holy', 'Waist', 'Both', 'Lingering Illness'), +(5, 1, 6, 0, 290, 51262, 'Phase 5', 'Priest', 'Holy', 'Legs', 'Both', 'Sanctified Crimson Acolyte Leggings'), +(5, 1, 7, 0, 290, 50699, 'Phase 5', 'Priest', 'Holy', 'Feet', 'Both', 'Plague Scientist''s Boots'), +(5, 1, 8, 0, 290, 54582, 'Phase 5', 'Priest', 'Holy', 'Wrists', 'Both', 'Bracers of Fiery Night'), +(5, 1, 9, 0, 290, 51260, 'Phase 5', 'Priest', 'Holy', 'Hands', 'Both', 'Sanctified Crimson Acolyte Gloves'), +(5, 1, 10, 0, 290, 50400, 'Phase 5', 'Priest', 'Holy', 'Finger1', 'Both', 'Ashen Band of Endless Wisdom'), +(5, 1, 11, 0, 290, 50636, 'Phase 5', 'Priest', 'Holy', 'Finger2', 'Both', 'Memory of Malygos'), +(5, 1, 12, 0, 290, 50366, 'Phase 5', 'Priest', 'Holy', 'Trinket1', 'Both', 'Althor''s Abacus'), +(5, 1, 13, 0, 290, 54589, 'Phase 5', 'Priest', 'Holy', 'Trinket2', 'Both', 'Glowing Twilight Scale'), +(5, 1, 14, 0, 290, 50668, 'Phase 5', 'Priest', 'Holy', 'Back', 'Both', 'Greatcloak of the Turned Champion'), +(5, 1, 15, 0, 290, 50731, 'Phase 5', 'Priest', 'Holy', 'MainHand', 'Both', 'Archus, Greatstaff of Antonidas'), +(5, 1, 17, 0, 290, 50631, 'Phase 5', 'Priest', 'Holy', 'Ranged', 'Both', 'Nightmare Ender'); + +-- Shadow (tab 2) +-- ilvl 66 (Phase 1 (Pre-Raid)) +INSERT INTO `playerbots_bis_gear` VALUES +(5, 2, 0, 1, 66, 10504, 'Phase 1 (Pre-Raid)', 'Priest', 'Shadow', 'Head', 'Alliance', 'Green Lens'), +(5, 2, 0, 2, 66, 10504, 'Phase 1 (Pre-Raid)', 'Priest', 'Shadow', 'Head', 'Horde', 'Green Lens'), +(5, 2, 1, 1, 66, 18691, 'Phase 1 (Pre-Raid)', 'Priest', 'Shadow', 'Neck', 'Alliance', 'Dark Advisor''s Pendant'), +(5, 2, 1, 2, 66, 18691, 'Phase 1 (Pre-Raid)', 'Priest', 'Shadow', 'Neck', 'Horde', 'Dark Advisor''s Pendant'), +(5, 2, 2, 1, 66, 14112, 'Phase 1 (Pre-Raid)', 'Priest', 'Shadow', 'Shoulders', 'Alliance', 'Felcloth Shoulders'), +(5, 2, 2, 2, 66, 14112, 'Phase 1 (Pre-Raid)', 'Priest', 'Shadow', 'Shoulders', 'Horde', 'Felcloth Shoulders'), +(5, 2, 4, 1, 66, 14136, 'Phase 1 (Pre-Raid)', 'Priest', 'Shadow', 'Chest', 'Alliance', 'Robe of Winter Night'), +(5, 2, 4, 2, 66, 14136, 'Phase 1 (Pre-Raid)', 'Priest', 'Shadow', 'Chest', 'Horde', 'Robe of Winter Night'), +(5, 2, 5, 1, 66, 11662, 'Phase 1 (Pre-Raid)', 'Priest', 'Shadow', 'Waist', 'Alliance', 'Ban''thok Sash'), +(5, 2, 5, 2, 66, 11662, 'Phase 1 (Pre-Raid)', 'Priest', 'Shadow', 'Waist', 'Horde', 'Ban''thok Sash'), +(5, 2, 6, 1, 66, 13170, 'Phase 1 (Pre-Raid)', 'Priest', 'Shadow', 'Legs', 'Alliance', 'Skyshroud Leggings'), +(5, 2, 6, 2, 66, 13170, 'Phase 1 (Pre-Raid)', 'Priest', 'Shadow', 'Legs', 'Horde', 'Skyshroud Leggings'), +(5, 2, 7, 1, 66, 18735, 'Phase 1 (Pre-Raid)', 'Priest', 'Shadow', 'Feet', 'Alliance', 'Maleki''s Footwraps'), +(5, 2, 7, 2, 66, 18735, 'Phase 1 (Pre-Raid)', 'Priest', 'Shadow', 'Feet', 'Horde', 'Maleki''s Footwraps'), +(5, 2, 8, 1, 66, 11766, 'Phase 1 (Pre-Raid)', 'Priest', 'Shadow', 'Wrists', 'Alliance', 'Flameweave Cuffs'), +(5, 2, 8, 2, 66, 11766, 'Phase 1 (Pre-Raid)', 'Priest', 'Shadow', 'Wrists', 'Horde', 'Flameweave Cuffs'), +(5, 2, 9, 1, 66, 13253, 'Phase 1 (Pre-Raid)', 'Priest', 'Shadow', 'Hands', 'Alliance', 'Hands of Power'), +(5, 2, 9, 2, 66, 13253, 'Phase 1 (Pre-Raid)', 'Priest', 'Shadow', 'Hands', 'Horde', 'Hands of Power'), +(5, 2, 10, 1, 66, 12543, 'Phase 1 (Pre-Raid)', 'Priest', 'Shadow', 'Finger1', 'Alliance', 'Songstone of Ironforge'), +(5, 2, 10, 2, 66, 12545, 'Phase 1 (Pre-Raid)', 'Priest', 'Shadow', 'Finger1', 'Horde', 'Eye of Orgrimmar'), +(5, 2, 11, 1, 66, 13001, 'Phase 1 (Pre-Raid)', 'Priest', 'Shadow', 'Finger2', 'Alliance', 'Maiden''s Circle'), +(5, 2, 11, 2, 66, 13001, 'Phase 1 (Pre-Raid)', 'Priest', 'Shadow', 'Finger2', 'Horde', 'Maiden''s Circle'), +(5, 2, 12, 1, 66, 12930, 'Phase 1 (Pre-Raid)', 'Priest', 'Shadow', 'Trinket1', 'Alliance', 'Briarwood Reed'), +(5, 2, 12, 2, 66, 12930, 'Phase 1 (Pre-Raid)', 'Priest', 'Shadow', 'Trinket1', 'Horde', 'Briarwood Reed'), +(5, 2, 13, 1, 66, 11819, 'Phase 1 (Pre-Raid)', 'Priest', 'Shadow', 'Trinket2', 'Alliance', 'Second Wind'), +(5, 2, 13, 2, 66, 11819, 'Phase 1 (Pre-Raid)', 'Priest', 'Shadow', 'Trinket2', 'Horde', 'Second Wind'), +(5, 2, 14, 1, 66, 13386, 'Phase 1 (Pre-Raid)', 'Priest', 'Shadow', 'Back', 'Alliance', 'Archivist Cape'), +(5, 2, 14, 2, 66, 13386, 'Phase 1 (Pre-Raid)', 'Priest', 'Shadow', 'Back', 'Horde', 'Archivist Cape'), +(5, 2, 15, 1, 66, 13349, 'Phase 1 (Pre-Raid)', 'Priest', 'Shadow', 'MainHand', 'Alliance', 'Scepter of the Unholy'), +(5, 2, 15, 2, 66, 13349, 'Phase 1 (Pre-Raid)', 'Priest', 'Shadow', 'MainHand', 'Horde', 'Scepter of the Unholy'), +(5, 2, 16, 1, 66, 10796, 'Phase 1 (Pre-Raid)', 'Priest', 'Shadow', 'OffHand', 'Alliance', 'Drakestone'), +(5, 2, 16, 2, 66, 10796, 'Phase 1 (Pre-Raid)', 'Priest', 'Shadow', 'OffHand', 'Horde', 'Drakestone'), +(5, 2, 17, 1, 66, 13396, 'Phase 1 (Pre-Raid)', 'Priest', 'Shadow', 'Ranged', 'Alliance', 'Skul''s Ghastly Touch'), +(5, 2, 17, 2, 66, 13396, 'Phase 1 (Pre-Raid)', 'Priest', 'Shadow', 'Ranged', 'Horde', 'Skul''s Ghastly Touch'); + +-- ilvl 76 (Phase 2 (Pre-Raid)) +INSERT INTO `playerbots_bis_gear` VALUES +(5, 2, 0, 1, 76, 10504, 'Phase 2 (Pre-Raid)', 'Priest', 'Shadow', 'Head', 'Alliance', 'Green Lens'), +(5, 2, 0, 2, 76, 10504, 'Phase 2 (Pre-Raid)', 'Priest', 'Shadow', 'Head', 'Horde', 'Green Lens'), +(5, 2, 1, 1, 76, 18691, 'Phase 2 (Pre-Raid)', 'Priest', 'Shadow', 'Neck', 'Alliance', 'Dark Advisor''s Pendant'), +(5, 2, 1, 2, 76, 18691, 'Phase 2 (Pre-Raid)', 'Priest', 'Shadow', 'Neck', 'Horde', 'Dark Advisor''s Pendant'), +(5, 2, 2, 1, 76, 14112, 'Phase 2 (Pre-Raid)', 'Priest', 'Shadow', 'Shoulders', 'Alliance', 'Felcloth Shoulders'), +(5, 2, 2, 2, 76, 14112, 'Phase 2 (Pre-Raid)', 'Priest', 'Shadow', 'Shoulders', 'Horde', 'Felcloth Shoulders'), +(5, 2, 4, 1, 76, 14136, 'Phase 2 (Pre-Raid)', 'Priest', 'Shadow', 'Chest', 'Alliance', 'Robe of Winter Night'), +(5, 2, 4, 2, 76, 14136, 'Phase 2 (Pre-Raid)', 'Priest', 'Shadow', 'Chest', 'Horde', 'Robe of Winter Night'), +(5, 2, 5, 1, 76, 11662, 'Phase 2 (Pre-Raid)', 'Priest', 'Shadow', 'Waist', 'Alliance', 'Ban''thok Sash'), +(5, 2, 5, 2, 76, 11662, 'Phase 2 (Pre-Raid)', 'Priest', 'Shadow', 'Waist', 'Horde', 'Ban''thok Sash'), +(5, 2, 6, 1, 76, 13170, 'Phase 2 (Pre-Raid)', 'Priest', 'Shadow', 'Legs', 'Alliance', 'Skyshroud Leggings'), +(5, 2, 6, 2, 76, 13170, 'Phase 2 (Pre-Raid)', 'Priest', 'Shadow', 'Legs', 'Horde', 'Skyshroud Leggings'), +(5, 2, 7, 1, 76, 18735, 'Phase 2 (Pre-Raid)', 'Priest', 'Shadow', 'Feet', 'Alliance', 'Maleki''s Footwraps'), +(5, 2, 7, 2, 76, 18735, 'Phase 2 (Pre-Raid)', 'Priest', 'Shadow', 'Feet', 'Horde', 'Maleki''s Footwraps'), +(5, 2, 8, 1, 76, 11766, 'Phase 2 (Pre-Raid)', 'Priest', 'Shadow', 'Wrists', 'Alliance', 'Flameweave Cuffs'), +(5, 2, 8, 2, 76, 11766, 'Phase 2 (Pre-Raid)', 'Priest', 'Shadow', 'Wrists', 'Horde', 'Flameweave Cuffs'), +(5, 2, 9, 1, 76, 18407, 'Phase 2 (Pre-Raid)', 'Priest', 'Shadow', 'Hands', 'Alliance', 'Felcloth Gloves'), +(5, 2, 9, 2, 76, 18407, 'Phase 2 (Pre-Raid)', 'Priest', 'Shadow', 'Hands', 'Horde', 'Felcloth Gloves'), +(5, 2, 10, 1, 76, 12543, 'Phase 2 (Pre-Raid)', 'Priest', 'Shadow', 'Finger1', 'Alliance', 'Songstone of Ironforge'), +(5, 2, 10, 2, 76, 12545, 'Phase 2 (Pre-Raid)', 'Priest', 'Shadow', 'Finger1', 'Horde', 'Eye of Orgrimmar'), +(5, 2, 11, 1, 76, 13001, 'Phase 2 (Pre-Raid)', 'Priest', 'Shadow', 'Finger2', 'Alliance', 'Maiden''s Circle'), +(5, 2, 11, 2, 76, 13001, 'Phase 2 (Pre-Raid)', 'Priest', 'Shadow', 'Finger2', 'Horde', 'Maiden''s Circle'), +(5, 2, 12, 1, 76, 12930, 'Phase 2 (Pre-Raid)', 'Priest', 'Shadow', 'Trinket1', 'Alliance', 'Briarwood Reed'), +(5, 2, 12, 2, 76, 12930, 'Phase 2 (Pre-Raid)', 'Priest', 'Shadow', 'Trinket1', 'Horde', 'Briarwood Reed'), +(5, 2, 13, 1, 76, 18371, 'Phase 2 (Pre-Raid)', 'Priest', 'Shadow', 'Trinket2', 'Alliance', 'Mindtap Talisman'), +(5, 2, 13, 2, 76, 18371, 'Phase 2 (Pre-Raid)', 'Priest', 'Shadow', 'Trinket2', 'Horde', 'Mindtap Talisman'), +(5, 2, 14, 1, 76, 13386, 'Phase 2 (Pre-Raid)', 'Priest', 'Shadow', 'Back', 'Alliance', 'Archivist Cape'), +(5, 2, 14, 2, 76, 13386, 'Phase 2 (Pre-Raid)', 'Priest', 'Shadow', 'Back', 'Horde', 'Archivist Cape'), +(5, 2, 15, 1, 76, 13349, 'Phase 2 (Pre-Raid)', 'Priest', 'Shadow', 'MainHand', 'Alliance', 'Scepter of the Unholy'), +(5, 2, 15, 2, 76, 13349, 'Phase 2 (Pre-Raid)', 'Priest', 'Shadow', 'MainHand', 'Horde', 'Scepter of the Unholy'), +(5, 2, 16, 1, 76, 10796, 'Phase 2 (Pre-Raid)', 'Priest', 'Shadow', 'OffHand', 'Alliance', 'Drakestone'), +(5, 2, 16, 2, 76, 10796, 'Phase 2 (Pre-Raid)', 'Priest', 'Shadow', 'OffHand', 'Horde', 'Drakestone'), +(5, 2, 17, 1, 76, 13396, 'Phase 2 (Pre-Raid)', 'Priest', 'Shadow', 'Ranged', 'Alliance', 'Skul''s Ghastly Touch'), +(5, 2, 17, 2, 76, 13396, 'Phase 2 (Pre-Raid)', 'Priest', 'Shadow', 'Ranged', 'Horde', 'Skul''s Ghastly Touch'); + +-- ilvl 78 (Phase 2) +INSERT INTO `playerbots_bis_gear` VALUES +(5, 2, 0, 0, 78, 10504, 'Phase 2', 'Priest', 'Shadow', 'Head', 'Both', 'Green Lens'), +(5, 2, 1, 0, 78, 18814, 'Phase 2', 'Priest', 'Shadow', 'Neck', 'Both', 'Choker of the Fire Lord'), +(5, 2, 2, 0, 78, 14112, 'Phase 2', 'Priest', 'Shadow', 'Shoulders', 'Both', 'Felcloth Shoulders'), +(5, 2, 4, 0, 78, 14136, 'Phase 2', 'Priest', 'Shadow', 'Chest', 'Both', 'Robe of Winter Night'), +(5, 2, 5, 0, 78, 18809, 'Phase 2', 'Priest', 'Shadow', 'Waist', 'Both', 'Sash of Whispered Secrets'), +(5, 2, 6, 0, 78, 19133, 'Phase 2', 'Priest', 'Shadow', 'Legs', 'Both', 'Fel Infused Leggings'), +(5, 2, 7, 0, 78, 19131, 'Phase 2', 'Priest', 'Shadow', 'Feet', 'Both', 'Snowblind Shoes'), +(5, 2, 8, 0, 78, 11766, 'Phase 2', 'Priest', 'Shadow', 'Wrists', 'Both', 'Flameweave Cuffs'), +(5, 2, 9, 0, 78, 18407, 'Phase 2', 'Priest', 'Shadow', 'Hands', 'Both', 'Felcloth Gloves'), +(5, 2, 10, 0, 78, 19147, 'Phase 2', 'Priest', 'Shadow', 'Finger1', 'Both', 'Ring of Spell Power'), +(5, 2, 11, 0, 78, 19147, 'Phase 2', 'Priest', 'Shadow', 'Finger2', 'Both', 'Ring of Spell Power'), +(5, 2, 12, 0, 78, 12930, 'Phase 2', 'Priest', 'Shadow', 'Trinket1', 'Both', 'Briarwood Reed'), +(5, 2, 13, 0, 78, 18820, 'Phase 2', 'Priest', 'Shadow', 'Trinket2', 'Both', 'Talisman of Ephemeral Power'), +(5, 2, 14, 0, 78, 13386, 'Phase 2', 'Priest', 'Shadow', 'Back', 'Both', 'Archivist Cape'), +(5, 2, 15, 0, 78, 18609, 'Phase 2', 'Priest', 'Shadow', 'MainHand', 'Both', 'Anathema'), +(5, 2, 17, 0, 78, 13396, 'Phase 2', 'Priest', 'Shadow', 'Ranged', 'Both', 'Skul''s Ghastly Touch'); + +-- ilvl 83 (Phase 3) +INSERT INTO `playerbots_bis_gear` VALUES +(5, 2, 0, 0, 83, 19375, 'Phase 3', 'Priest', 'Shadow', 'Head', 'Both', 'Mish''undare, Circlet of the Mind Flayer'), +(5, 2, 1, 0, 83, 18814, 'Phase 3', 'Priest', 'Shadow', 'Neck', 'Both', 'Choker of the Fire Lord'), +(5, 2, 2, 0, 83, 19370, 'Phase 3', 'Priest', 'Shadow', 'Shoulders', 'Both', 'Mantle of the Blackwing Cabal'), +(5, 2, 4, 0, 83, 14136, 'Phase 3', 'Priest', 'Shadow', 'Chest', 'Both', 'Robe of Winter Night'), +(5, 2, 5, 0, 83, 19400, 'Phase 3', 'Priest', 'Shadow', 'Waist', 'Both', 'Firemaw''s Clutch'), +(5, 2, 6, 0, 83, 19133, 'Phase 3', 'Priest', 'Shadow', 'Legs', 'Both', 'Fel Infused Leggings'), +(5, 2, 7, 0, 83, 19131, 'Phase 3', 'Priest', 'Shadow', 'Feet', 'Both', 'Snowblind Shoes'), +(5, 2, 8, 0, 83, 19374, 'Phase 3', 'Priest', 'Shadow', 'Wrists', 'Both', 'Bracers of Arcane Accuracy'), +(5, 2, 9, 0, 83, 19407, 'Phase 3', 'Priest', 'Shadow', 'Hands', 'Both', 'Ebony Flame Gloves'), +(5, 2, 11, 0, 83, 19434, 'Phase 3', 'Priest', 'Shadow', 'Finger2', 'Both', 'Band of Dark Dominion'), +(5, 2, 12, 0, 83, 19379, 'Phase 3', 'Priest', 'Shadow', 'Trinket1', 'Both', 'Neltharion''s Tear'), +(5, 2, 13, 0, 83, 18820, 'Phase 3', 'Priest', 'Shadow', 'Trinket2', 'Both', 'Talisman of Ephemeral Power'), +(5, 2, 14, 0, 83, 19378, 'Phase 3', 'Priest', 'Shadow', 'Back', 'Both', 'Cloak of the Brood Lord'), +(5, 2, 15, 0, 83, 19360, 'Phase 3', 'Priest', 'Shadow', 'MainHand', 'Both', 'Lok''amir il Romathis'), +(5, 2, 16, 0, 83, 19366, 'Phase 3', 'Priest', 'Shadow', 'OffHand', 'Both', 'Master Dragonslayer''s Orb'), +(5, 2, 17, 0, 83, 13396, 'Phase 3', 'Priest', 'Shadow', 'Ranged', 'Both', 'Skul''s Ghastly Touch'); + +-- ilvl 88 (Phase 5) +INSERT INTO `playerbots_bis_gear` VALUES +(5, 2, 0, 0, 88, 21348, 'Phase 5', 'Priest', 'Shadow', 'Head', 'Both', 'Tiara of the Oracle'), +(5, 2, 1, 0, 88, 18814, 'Phase 5', 'Priest', 'Shadow', 'Neck', 'Both', 'Choker of the Fire Lord'), +(5, 2, 2, 0, 88, 19370, 'Phase 5', 'Priest', 'Shadow', 'Shoulders', 'Both', 'Mantle of the Blackwing Cabal'), +(5, 2, 4, 0, 88, 21351, 'Phase 5', 'Priest', 'Shadow', 'Chest', 'Both', 'Vestments of the Oracle'), +(5, 2, 5, 0, 88, 19400, 'Phase 5', 'Priest', 'Shadow', 'Waist', 'Both', 'Firemaw''s Clutch'), +(5, 2, 6, 0, 88, 19133, 'Phase 5', 'Priest', 'Shadow', 'Legs', 'Both', 'Fel Infused Leggings'), +(5, 2, 7, 0, 88, 19131, 'Phase 5', 'Priest', 'Shadow', 'Feet', 'Both', 'Snowblind Shoes'), +(5, 2, 8, 0, 88, 21611, 'Phase 5', 'Priest', 'Shadow', 'Wrists', 'Both', 'Burrower Bracers'), +(5, 2, 9, 0, 88, 21585, 'Phase 5', 'Priest', 'Shadow', 'Hands', 'Both', 'Dark Storm Gauntlets'), +(5, 2, 10, 0, 88, 21709, 'Phase 5', 'Priest', 'Shadow', 'Finger1', 'Both', 'Ring of the Fallen God'), +(5, 2, 11, 0, 88, 21210, 'Phase 5', 'Priest', 'Shadow', 'Finger2', 'Both', 'Signet Ring of the Bronze Dragonflight'), +(5, 2, 12, 0, 88, 19379, 'Phase 5', 'Priest', 'Shadow', 'Trinket1', 'Both', 'Neltharion''s Tear'), +(5, 2, 13, 0, 88, 18820, 'Phase 5', 'Priest', 'Shadow', 'Trinket2', 'Both', 'Talisman of Ephemeral Power'), +(5, 2, 14, 0, 88, 22731, 'Phase 5', 'Priest', 'Shadow', 'Back', 'Both', 'Cloak of the Devoured'), +(5, 2, 15, 0, 88, 19360, 'Phase 5', 'Priest', 'Shadow', 'MainHand', 'Both', 'Lok''amir il Romathis'), +(5, 2, 16, 0, 88, 19366, 'Phase 5', 'Priest', 'Shadow', 'OffHand', 'Both', 'Master Dragonslayer''s Orb'), +(5, 2, 17, 0, 88, 21603, 'Phase 5', 'Priest', 'Shadow', 'Ranged', 'Both', 'Wand of Qiraji Nobility'); + +-- ilvl 92 (Phase 6) +INSERT INTO `playerbots_bis_gear` VALUES +(5, 2, 0, 0, 92, 23035, 'Phase 6', 'Priest', 'Shadow', 'Head', 'Both', 'Preceptor''s Hat'), +(5, 2, 1, 0, 92, 18814, 'Phase 6', 'Priest', 'Shadow', 'Neck', 'Both', 'Choker of the Fire Lord'), +(5, 2, 2, 0, 92, 22983, 'Phase 6', 'Priest', 'Shadow', 'Shoulders', 'Both', 'Rime Covered Mantle'), +(5, 2, 4, 0, 92, 23220, 'Phase 6', 'Priest', 'Shadow', 'Chest', 'Both', 'Crystal Webbed Robe'), +(5, 2, 5, 0, 92, 19400, 'Phase 6', 'Priest', 'Shadow', 'Waist', 'Both', 'Firemaw''s Clutch'), +(5, 2, 6, 0, 92, 19133, 'Phase 6', 'Priest', 'Shadow', 'Legs', 'Both', 'Fel Infused Leggings'), +(5, 2, 7, 0, 92, 19131, 'Phase 6', 'Priest', 'Shadow', 'Feet', 'Both', 'Snowblind Shoes'), +(5, 2, 8, 0, 92, 21611, 'Phase 6', 'Priest', 'Shadow', 'Wrists', 'Both', 'Burrower Bracers'), +(5, 2, 9, 0, 92, 21585, 'Phase 6', 'Priest', 'Shadow', 'Hands', 'Both', 'Dark Storm Gauntlets'), +(5, 2, 10, 0, 92, 21709, 'Phase 6', 'Priest', 'Shadow', 'Finger1', 'Both', 'Ring of the Fallen God'), +(5, 2, 11, 0, 92, 21210, 'Phase 6', 'Priest', 'Shadow', 'Finger2', 'Both', 'Signet Ring of the Bronze Dragonflight'), +(5, 2, 12, 0, 92, 19379, 'Phase 6', 'Priest', 'Shadow', 'Trinket1', 'Both', 'Neltharion''s Tear'), +(5, 2, 13, 0, 92, 23046, 'Phase 6', 'Priest', 'Shadow', 'Trinket2', 'Both', 'The Restrained Essence of Sapphiron'), +(5, 2, 14, 0, 92, 22731, 'Phase 6', 'Priest', 'Shadow', 'Back', 'Both', 'Cloak of the Devoured'), +(5, 2, 15, 0, 92, 22988, 'Phase 6', 'Priest', 'Shadow', 'MainHand', 'Both', 'The End of Dreams'), +(5, 2, 16, 0, 92, 23049, 'Phase 6', 'Priest', 'Shadow', 'OffHand', 'Both', 'Sapphiron''s Left Eye'), +(5, 2, 17, 0, 92, 21603, 'Phase 6', 'Priest', 'Shadow', 'Ranged', 'Both', 'Wand of Qiraji Nobility'); + +-- ilvl 200 (Fresh 80) +INSERT INTO `playerbots_bis_gear` VALUES +(5, 2, 0, 0, 200, 43995, 'Fresh 80', 'Priest', 'Shadow', 'Head', 'Both', 'Enamored Cowl'), +(5, 2, 1, 0, 200, 44658, 'Fresh 80', 'Priest', 'Shadow', 'Neck', 'Both', 'Chain of the Ancient Wyrm'), +(5, 2, 2, 0, 200, 40459, 'Fresh 80', 'Priest', 'Shadow', 'Shoulders', 'Both', 'Valorous Mantle of Faith'), +(5, 2, 4, 0, 200, 40526, 'Fresh 80', 'Priest', 'Shadow', 'Chest', 'Both', 'Gown of the Spell-Weaver'), +(5, 2, 5, 0, 200, 40696, 'Fresh 80', 'Priest', 'Shadow', 'Waist', 'Both', 'Plush Sash of Guzbah'), +(5, 2, 6, 0, 200, 40457, 'Fresh 80', 'Priest', 'Shadow', 'Legs', 'Both', 'Valorous Pants of Faith'), +(5, 2, 7, 0, 200, 40751, 'Fresh 80', 'Priest', 'Shadow', 'Feet', 'Both', 'Slippers of the Holy Light'), +(5, 2, 8, 0, 200, 40740, 'Fresh 80', 'Priest', 'Shadow', 'Wrists', 'Both', 'Wraps of the Astral Traveler'), +(5, 2, 9, 0, 200, 39530, 'Fresh 80', 'Priest', 'Shadow', 'Hands', 'Both', 'Heroes'' Handwraps of Faith'), +(5, 2, 10, 0, 200, 40719, 'Fresh 80', 'Priest', 'Shadow', 'Finger1', 'Both', 'Band of Channeled Magic'), +(5, 2, 12, 0, 200, 39229, 'Fresh 80', 'Priest', 'Shadow', 'Trinket1', 'Both', 'Embrace of the Spider'), +(5, 2, 14, 0, 200, 40723, 'Fresh 80', 'Priest', 'Shadow', 'Back', 'Both', 'Disguise of the Kumiho'), +(5, 2, 15, 0, 200, 39423, 'Fresh 80', 'Priest', 'Shadow', 'MainHand', 'Both', 'Hammer of the Astral Plane'), +(5, 2, 17, 0, 200, 39426, 'Fresh 80', 'Priest', 'Shadow', 'Ranged', 'Both', 'Wand of the Archlich'); + +-- ilvl 213 (Pre-Ulduar) +INSERT INTO `playerbots_bis_gear` VALUES +(5, 2, 0, 0, 213, 40562, 'Pre-Ulduar', 'Priest', 'Shadow', 'Head', 'Both', 'Hood of Rationality'), +(5, 2, 1, 0, 213, 44661, 'Pre-Ulduar', 'Priest', 'Shadow', 'Neck', 'Both', 'Wyrmrest Necklace of Power'), +(5, 2, 2, 0, 213, 40555, 'Pre-Ulduar', 'Priest', 'Shadow', 'Shoulders', 'Both', 'Mantle of Dissemination'), +(5, 2, 4, 0, 213, 40234, 'Pre-Ulduar', 'Priest', 'Shadow', 'Chest', 'Both', 'Heigan''s Putrid Vestments'), +(5, 2, 5, 0, 213, 40561, 'Pre-Ulduar', 'Priest', 'Shadow', 'Waist', 'Both', 'Leash of Heedless Magic'), +(5, 2, 6, 0, 213, 40560, 'Pre-Ulduar', 'Priest', 'Shadow', 'Legs', 'Both', 'Leggings of the Wanton Spellcaster'), +(5, 2, 7, 0, 213, 40558, 'Pre-Ulduar', 'Priest', 'Shadow', 'Feet', 'Both', 'Arcanic Tramplers'), +(5, 2, 8, 0, 213, 44008, 'Pre-Ulduar', 'Priest', 'Shadow', 'Wrists', 'Both', 'Unsullied Cuffs'), +(5, 2, 9, 0, 213, 40454, 'Pre-Ulduar', 'Priest', 'Shadow', 'Hands', 'Both', 'Valorous Handwraps of Faith'), +(5, 2, 10, 0, 213, 40719, 'Pre-Ulduar', 'Priest', 'Shadow', 'Finger1', 'Both', 'Band of Channeled Magic'), +(5, 2, 12, 0, 213, 40432, 'Pre-Ulduar', 'Priest', 'Shadow', 'Trinket1', 'Both', 'Illustration of the Dragon Soul'), +(5, 2, 14, 0, 213, 44005, 'Pre-Ulduar', 'Priest', 'Shadow', 'Back', 'Both', 'Pennant Cloak'), +(5, 2, 17, 0, 213, 39712, 'Pre-Ulduar', 'Priest', 'Shadow', 'Ranged', 'Both', 'Gemmed Wand of the Nerubians'); + +-- ilvl 245 (Phase 2) +INSERT INTO `playerbots_bis_gear` VALUES +(5, 2, 0, 0, 245, 46172, 'Phase 2', 'Priest', 'Shadow', 'Head', 'Both', 'Conqueror''s Circlet of Sanctification'), +(5, 2, 1, 0, 245, 45243, 'Phase 2', 'Priest', 'Shadow', 'Neck', 'Both', 'Sapphire Amulet of Renewal'), +(5, 2, 2, 0, 245, 46165, 'Phase 2', 'Priest', 'Shadow', 'Shoulders', 'Both', 'Conqueror''s Mantle of Sanctification'), +(5, 2, 4, 0, 245, 46168, 'Phase 2', 'Priest', 'Shadow', 'Chest', 'Both', 'Conqueror''s Raiments of Sanctification'), +(5, 2, 5, 0, 245, 45619, 'Phase 2', 'Priest', 'Shadow', 'Waist', 'Both', 'Starwatcher''s Binding'), +(5, 2, 6, 0, 245, 46170, 'Phase 2', 'Priest', 'Shadow', 'Legs', 'Both', 'Conqueror''s Pants of Sanctification'), +(5, 2, 7, 0, 245, 45135, 'Phase 2', 'Priest', 'Shadow', 'Feet', 'Both', 'Boots of Fiery Resolution'), +(5, 2, 8, 0, 245, 45446, 'Phase 2', 'Priest', 'Shadow', 'Wrists', 'Both', 'Grasps of Reason'), +(5, 2, 9, 0, 245, 45665, 'Phase 2', 'Priest', 'Shadow', 'Hands', 'Both', 'Pharos Gloves'), +(5, 2, 10, 0, 245, 45495, 'Phase 2', 'Priest', 'Shadow', 'Finger1', 'Both', 'Conductive Seal'), +(5, 2, 12, 0, 245, 45466, 'Phase 2', 'Priest', 'Shadow', 'Trinket1', 'Both', 'Scale of Fates'), +(5, 2, 14, 0, 245, 45242, 'Phase 2', 'Priest', 'Shadow', 'Back', 'Both', 'Drape of Mortal Downfall'), +(5, 2, 17, 0, 245, 45294, 'Phase 2', 'Priest', 'Shadow', 'Ranged', 'Both', 'Petrified Ivy Sprig'); + +-- ilvl 258 (Phase 3) +INSERT INTO `playerbots_bis_gear` VALUES +(5, 2, 0, 0, 258, 48085, 'Phase 3', 'Priest', 'Shadow', 'Head', 'Both', 'Circlet of Triumph'), +(5, 2, 1, 1, 258, 47144, 'Phase 3', 'Priest', 'Shadow', 'Neck', 'Alliance', 'Wail of the Val''kyr'), +(5, 2, 1, 2, 258, 47468, 'Phase 3', 'Priest', 'Shadow', 'Neck', 'Horde', 'Cry of the Val''kyr'), +(5, 2, 2, 1, 258, 48082, 'Phase 3', 'Priest', 'Shadow', 'Shoulders', 'Alliance', 'Velen''s Mantle of Triumph'), +(5, 2, 2, 2, 258, 48090, 'Phase 3', 'Priest', 'Shadow', 'Shoulders', 'Horde', 'Zabra''s Raiments of Triumph'), +(5, 2, 4, 0, 258, 48083, 'Phase 3', 'Priest', 'Shadow', 'Chest', 'Both', 'Raiments of Triumph'), +(5, 2, 5, 1, 258, 46973, 'Phase 3', 'Priest', 'Shadow', 'Waist', 'Alliance', 'Cord of the Tenebrous Mist'), +(5, 2, 5, 2, 258, 47419, 'Phase 3', 'Priest', 'Shadow', 'Waist', 'Horde', 'Belt of the Tenebrous Mist'), +(5, 2, 6, 0, 258, 48084, 'Phase 3', 'Priest', 'Shadow', 'Legs', 'Both', 'Pants of Triumph'), +(5, 2, 7, 1, 258, 47097, 'Phase 3', 'Priest', 'Shadow', 'Feet', 'Alliance', 'Boots of the Mourning Widow'), +(5, 2, 7, 2, 258, 47454, 'Phase 3', 'Priest', 'Shadow', 'Feet', 'Horde', 'Sandals of the Mourning Widow'), +(5, 2, 8, 1, 258, 47208, 'Phase 3', 'Priest', 'Shadow', 'Wrists', 'Alliance', 'Armbands of the Ashen Saint'), +(5, 2, 8, 2, 258, 47467, 'Phase 3', 'Priest', 'Shadow', 'Wrists', 'Horde', 'Dark Essence Bindings'), +(5, 2, 9, 1, 258, 45665, 'Phase 3', 'Priest', 'Shadow', 'Hands', 'Alliance', 'Pharos Gloves'), +(5, 2, 10, 1, 258, 46046, 'Phase 3', 'Priest', 'Shadow', 'Finger1', 'Alliance', 'Nebula Band'), +(5, 2, 10, 2, 258, 47237, 'Phase 3', 'Priest', 'Shadow', 'Finger1', 'Horde', 'Band of Deplorable Violence'), +(5, 2, 12, 2, 258, 45518, 'Phase 3', 'Priest', 'Shadow', 'Trinket1', 'Horde', 'Flare of the Heavens'), +(5, 2, 14, 1, 258, 47552, 'Phase 3', 'Priest', 'Shadow', 'Back', 'Alliance', 'Jaina''s Radiance'), +(5, 2, 14, 2, 258, 47551, 'Phase 3', 'Priest', 'Shadow', 'Back', 'Horde', 'Aethas'' Intensity'), +(5, 2, 17, 1, 258, 47922, 'Phase 3', 'Priest', 'Shadow', 'Ranged', 'Alliance', 'Rod of Imprisoned Souls'), +(5, 2, 17, 2, 258, 45294, 'Phase 3', 'Priest', 'Shadow', 'Ranged', 'Horde', 'Petrified Ivy Sprig'); + +-- ilvl 264 (Phase 4) +INSERT INTO `playerbots_bis_gear` VALUES +(5, 2, 0, 0, 264, 51255, 'Phase 4', 'Priest', 'Shadow', 'Head', 'Both', 'Sanctified Crimson Acolyte Cowl'), +(5, 2, 1, 0, 264, 50724, 'Phase 4', 'Priest', 'Shadow', 'Neck', 'Both', 'Blood Queen''s Crimson Choker'), +(5, 2, 2, 0, 264, 51257, 'Phase 4', 'Priest', 'Shadow', 'Shoulders', 'Both', 'Sanctified Crimson Acolyte Mantle'), +(5, 2, 4, 0, 264, 51259, 'Phase 4', 'Priest', 'Shadow', 'Chest', 'Both', 'Sanctified Crimson Acolyte Raiments'), +(5, 2, 5, 0, 264, 50613, 'Phase 4', 'Priest', 'Shadow', 'Waist', 'Both', 'Crushing Coldwraith Belt'), +(5, 2, 6, 0, 264, 50694, 'Phase 4', 'Priest', 'Shadow', 'Legs', 'Both', 'Plaguebringer''s Stained Pants'), +(5, 2, 7, 0, 264, 50699, 'Phase 4', 'Priest', 'Shadow', 'Feet', 'Both', 'Plague Scientist''s Boots'), +(5, 2, 8, 0, 264, 50651, 'Phase 4', 'Priest', 'Shadow', 'Wrists', 'Both', 'The Lady''s Brittle Bracers'), +(5, 2, 9, 0, 264, 51256, 'Phase 4', 'Priest', 'Shadow', 'Hands', 'Both', 'Sanctified Crimson Acolyte Handwraps'), +(5, 2, 10, 0, 264, 50398, 'Phase 4', 'Priest', 'Shadow', 'Finger1', 'Both', 'Ashen Band of Endless Destruction'), +(5, 2, 12, 0, 264, 50348, 'Phase 4', 'Priest', 'Shadow', 'Trinket1', 'Both', 'Dislodged Foreign Object'), +(5, 2, 14, 0, 264, 50628, 'Phase 4', 'Priest', 'Shadow', 'Back', 'Both', 'Frostbinder''s Shredded Cape'), +(5, 2, 15, 0, 264, 50734, 'Phase 4', 'Priest', 'Shadow', 'MainHand', 'Both', 'Royal Scepter of Terenas II'), +(5, 2, 17, 0, 264, 50684, 'Phase 4', 'Priest', 'Shadow', 'Ranged', 'Both', 'Corpse-Impaling Spike'); + +-- ilvl 290 (Phase 5) +INSERT INTO `playerbots_bis_gear` VALUES +(5, 2, 0, 0, 290, 51255, 'Phase 5', 'Priest', 'Shadow', 'Head', 'Both', 'Sanctified Crimson Acolyte Cowl'), +(5, 2, 1, 0, 290, 50182, 'Phase 5', 'Priest', 'Shadow', 'Neck', 'Both', 'Blood Queen''s Crimson Choker'), +(5, 2, 2, 0, 290, 51257, 'Phase 5', 'Priest', 'Shadow', 'Shoulders', 'Both', 'Sanctified Crimson Acolyte Mantle'), +(5, 2, 4, 0, 290, 51259, 'Phase 5', 'Priest', 'Shadow', 'Chest', 'Both', 'Sanctified Crimson Acolyte Raiments'), +(5, 2, 5, 0, 290, 50613, 'Phase 5', 'Priest', 'Shadow', 'Waist', 'Both', 'Crushing Coldwraith Belt'), +(5, 2, 6, 0, 290, 50694, 'Phase 5', 'Priest', 'Shadow', 'Legs', 'Both', 'Plaguebringer''s Stained Pants'), +(5, 2, 7, 0, 290, 50699, 'Phase 5', 'Priest', 'Shadow', 'Feet', 'Both', 'Plague Scientist''s Boots'), +(5, 2, 8, 0, 290, 54582, 'Phase 5', 'Priest', 'Shadow', 'Wrists', 'Both', 'Bracers of Fiery Night'), +(5, 2, 9, 0, 290, 51256, 'Phase 5', 'Priest', 'Shadow', 'Hands', 'Both', 'Sanctified Crimson Acolyte Handwraps'), +(5, 2, 10, 0, 290, 50664, 'Phase 5', 'Priest', 'Shadow', 'Finger1', 'Both', 'Ring of Rapid Ascent'), +(5, 2, 11, 0, 290, 50398, 'Phase 5', 'Priest', 'Shadow', 'Finger2', 'Both', 'Ashen Band of Endless Destruction'), +(5, 2, 12, 0, 290, 54588, 'Phase 5', 'Priest', 'Shadow', 'Trinket1', 'Both', 'Charred Twilight Scale'), +(5, 2, 13, 0, 290, 50348, 'Phase 5', 'Priest', 'Shadow', 'Trinket2', 'Both', 'Dislodged Foreign Object'), +(5, 2, 14, 0, 290, 54583, 'Phase 5', 'Priest', 'Shadow', 'Back', 'Both', 'Cloak of Burning Dusk'), +(5, 2, 15, 0, 290, 50734, 'Phase 5', 'Priest', 'Shadow', 'MainHand', 'Both', 'Royal Scepter of Terenas II'), +(5, 2, 16, 0, 290, 50719, 'Phase 5', 'Priest', 'Shadow', 'OffHand', 'Both', 'Shadow Silk Spindle'), +(5, 2, 17, 0, 290, 50684, 'Phase 5', 'Priest', 'Shadow', 'Ranged', 'Both', 'Corpse-Impaling Spike'); + + +-- ============================================================ +-- Death Knight (6) +-- ============================================================ +-- Blood Tank (tab 0) +-- ilvl 200 (Pre-Raid) +INSERT INTO `playerbots_bis_gear` VALUES +(6, 0, 0, 0, 200, 41387, 'Pre-Raid', 'Death Knight', 'Blood Tank', 'Head', 'Both', 'Tempered Titansteel Helm'), +(6, 0, 1, 0, 200, 42646, 'Pre-Raid', 'Death Knight', 'Blood Tank', 'Neck', 'Both', 'Titanium Earthguard Chain'), +(6, 0, 2, 0, 200, 37814, 'Pre-Raid', 'Death Knight', 'Blood Tank', 'Shoulders', 'Both', 'Iron Dwarf Smith Pauldrons'), +(6, 0, 4, 0, 200, 37735, 'Pre-Raid', 'Death Knight', 'Blood Tank', 'Chest', 'Both', 'Ziggurat Imprinted Chestguard'), +(6, 0, 5, 0, 200, 37241, 'Pre-Raid', 'Death Knight', 'Blood Tank', 'Waist', 'Both', 'Ancient Aligned Girdle'), +(6, 0, 6, 0, 200, 37193, 'Pre-Raid', 'Death Knight', 'Blood Tank', 'Legs', 'Both', 'Staggering Legplates'), +(6, 0, 7, 0, 200, 41392, 'Pre-Raid', 'Death Knight', 'Blood Tank', 'Feet', 'Both', 'Tempered Titansteel Treads'), +(6, 0, 8, 0, 200, 37620, 'Pre-Raid', 'Death Knight', 'Blood Tank', 'Wrists', 'Both', 'Bracers of the Herald'), +(6, 0, 9, 0, 200, 37645, 'Pre-Raid', 'Death Knight', 'Blood Tank', 'Hands', 'Both', 'Horn-Tipped Gauntlets'), +(6, 0, 10, 0, 200, 42643, 'Pre-Raid', 'Death Knight', 'Blood Tank', 'Finger1', 'Both', 'Titanium Earthguard Ring'), +(6, 0, 12, 0, 200, 37220, 'Pre-Raid', 'Death Knight', 'Blood Tank', 'Trinket1', 'Both', 'Essence of Gossamer'), +(6, 0, 14, 0, 200, 43565, 'Pre-Raid', 'Death Knight', 'Blood Tank', 'Back', 'Both', 'Durable Nerubhide Cape'), +(6, 0, 15, 0, 200, 41257, 'Pre-Raid', 'Death Knight', 'Blood Tank', 'MainHand', 'Both', 'Titansteel Destroyer'), +(6, 0, 17, 0, 200, 40714, 'Pre-Raid', 'Death Knight', 'Blood Tank', 'Ranged', 'Both', 'Sigil of the Unfaltering Knight'); + +-- ilvl 224 (Phase 1) +INSERT INTO `playerbots_bis_gear` VALUES +(6, 0, 0, 0, 224, 40565, 'Phase 1', 'Death Knight', 'Blood Tank', 'Head', 'Both', 'Valorous Scourgeborne Faceguard'), +(6, 0, 1, 0, 224, 40387, 'Phase 1', 'Death Knight', 'Blood Tank', 'Neck', 'Both', 'Boundless Ambition'), +(6, 0, 2, 0, 224, 40568, 'Phase 1', 'Death Knight', 'Blood Tank', 'Shoulders', 'Both', 'Valorous Scourgeborne Pauldrons'), +(6, 0, 4, 0, 224, 40559, 'Phase 1', 'Death Knight', 'Blood Tank', 'Chest', 'Both', 'Valorous Scourgeborne Chestguard'), +(6, 0, 5, 0, 224, 39759, 'Phase 1', 'Death Knight', 'Blood Tank', 'Waist', 'Both', 'Ablative Chitin Girdle'), +(6, 0, 6, 0, 224, 40589, 'Phase 1', 'Death Knight', 'Blood Tank', 'Legs', 'Both', 'Legplates of Sovereignty'), +(6, 0, 7, 0, 224, 40297, 'Phase 1', 'Death Knight', 'Blood Tank', 'Feet', 'Both', 'Sabatons of Endurance'), +(6, 0, 8, 0, 224, 40306, 'Phase 1', 'Death Knight', 'Blood Tank', 'Wrists', 'Both', 'Bracers of the Unholy Knight'), +(6, 0, 9, 0, 224, 40563, 'Phase 1', 'Death Knight', 'Blood Tank', 'Hands', 'Both', 'Valorous Scourgeborne Handguards'), +(6, 0, 10, 0, 224, 40107, 'Phase 1', 'Death Knight', 'Blood Tank', 'Finger1', 'Both', 'Sand-Worn Band'), +(6, 0, 12, 0, 224, 37220, 'Phase 1', 'Death Knight', 'Blood Tank', 'Trinket1', 'Both', 'Essence of Gossamer'), +(6, 0, 14, 0, 224, 40252, 'Phase 1', 'Death Knight', 'Blood Tank', 'Back', 'Both', 'Cloak of the Shadowed Sun'), +(6, 0, 15, 0, 224, 40406, 'Phase 1', 'Death Knight', 'Blood Tank', 'MainHand', 'Both', 'Inevitable Defeat'), +(6, 0, 17, 0, 224, 40714, 'Phase 1', 'Death Knight', 'Blood Tank', 'Ranged', 'Both', 'Sigil of the Unfaltering Knight'); + +-- ilvl 245 (Phase 2) +INSERT INTO `playerbots_bis_gear` VALUES +(6, 0, 0, 0, 245, 46120, 'Phase 2', 'Death Knight', 'Blood Tank', 'Head', 'Both', 'Conqueror''s Darkruned Faceguard'), +(6, 0, 1, 0, 245, 45485, 'Phase 2', 'Death Knight', 'Blood Tank', 'Neck', 'Both', 'Bronze Pendant of the Vanir'), +(6, 0, 2, 0, 245, 46122, 'Phase 2', 'Death Knight', 'Blood Tank', 'Shoulders', 'Both', 'Conqueror''s Darkruned Pauldrons'), +(6, 0, 4, 0, 245, 46039, 'Phase 2', 'Death Knight', 'Blood Tank', 'Chest', 'Both', 'Breastplate of the Timeless'), +(6, 0, 5, 0, 245, 45825, 'Phase 2', 'Death Knight', 'Blood Tank', 'Waist', 'Both', 'Shieldwarder Girdle'), +(6, 0, 6, 0, 245, 45594, 'Phase 2', 'Death Knight', 'Blood Tank', 'Legs', 'Both', 'Legplates of the Endless Void'), +(6, 0, 7, 0, 245, 45988, 'Phase 2', 'Death Knight', 'Blood Tank', 'Feet', 'Both', 'Greaves of the Iron Army'), +(6, 0, 8, 0, 245, 45111, 'Phase 2', 'Death Knight', 'Blood Tank', 'Wrists', 'Both', 'Mimiron''s Inferno Couplings'), +(6, 0, 9, 0, 245, 45487, 'Phase 2', 'Death Knight', 'Blood Tank', 'Hands', 'Both', 'Handguards of Revitalization'), +(6, 0, 10, 0, 245, 45471, 'Phase 2', 'Death Knight', 'Blood Tank', 'Finger1', 'Both', 'Fate''s Clutch'), +(6, 0, 12, 0, 245, 45158, 'Phase 2', 'Death Knight', 'Blood Tank', 'Trinket1', 'Both', 'Heart of Iron'), +(6, 0, 14, 0, 245, 45496, 'Phase 2', 'Death Knight', 'Blood Tank', 'Back', 'Both', 'Titanskin Cloak'), +(6, 0, 15, 0, 245, 45516, 'Phase 2', 'Death Knight', 'Blood', 'MainHand', 'Both', 'Voldrethar, Dark Blade of Oblivion'), +(6, 0, 17, 0, 245, 40207, 'Phase 2', 'Death Knight', 'Blood Tank', 'Ranged', 'Both', 'Sigil of Awareness'); + +-- ilvl 258 (Phase 3) +INSERT INTO `playerbots_bis_gear` VALUES +(6, 0, 0, 0, 258, 48545, 'Phase 3', 'Death Knight', 'Blood Tank', 'Head', 'Both', 'Faceguard of Triumph'), +(6, 0, 0, 1, 258, 48488, 'Phase 3', 'Death Knight', 'Blood', 'Head', 'Alliance', 'Thassarian''s Helmet of Triumph'), +(6, 0, 0, 2, 258, 48493, 'Phase 3', 'Death Knight', 'Blood', 'Head', 'Horde', 'Koltira''s Helmet of Triumph'), +(6, 0, 1, 1, 258, 47110, 'Phase 3', 'Death Knight', 'Blood', 'Neck', 'Alliance', 'The Executioner''s Malice'), +(6, 0, 1, 2, 258, 47458, 'Phase 3', 'Death Knight', 'Blood', 'Neck', 'Horde', 'The Executioner''s Vice'), +(6, 0, 2, 0, 258, 48543, 'Phase 3', 'Death Knight', 'Blood Tank', 'Shoulders', 'Both', 'Pauldrons of Triumph'), +(6, 0, 2, 1, 258, 48486, 'Phase 3', 'Death Knight', 'Blood', 'Shoulders', 'Alliance', 'Thassarian''s Shoulderplates of Triumph'), +(6, 0, 2, 2, 258, 48495, 'Phase 3', 'Death Knight', 'Blood', 'Shoulders', 'Horde', 'Koltira''s Shoulderplates of Triumph'), +(6, 0, 4, 1, 258, 46968, 'Phase 3', 'Death Knight', 'Blood Tank', 'Chest', 'Alliance', 'Chestplate of the Towering Monstrosity'), +(6, 0, 4, 2, 258, 47415, 'Phase 3', 'Death Knight', 'Blood Tank', 'Chest', 'Horde', 'Hauberk of the Towering Monstrosity'), +(6, 0, 5, 1, 258, 47076, 'Phase 3', 'Death Knight', 'Blood Tank', 'Waist', 'Alliance', 'Girdle of Bloodied Scars'), +(6, 0, 5, 2, 258, 47444, 'Phase 3', 'Death Knight', 'Blood Tank', 'Waist', 'Horde', 'Belt of Bloodied Scars'), +(6, 0, 6, 0, 258, 48544, 'Phase 3', 'Death Knight', 'Blood Tank', 'Legs', 'Both', 'Legguards of Triumph'), +(6, 0, 6, 1, 258, 48487, 'Phase 3', 'Death Knight', 'Blood', 'Legs', 'Alliance', 'Thassarian''s Legplates of Triumph'), +(6, 0, 6, 2, 258, 48494, 'Phase 3', 'Death Knight', 'Blood', 'Legs', 'Horde', 'Koltira''s Legplates of Triumph'), +(6, 0, 7, 0, 258, 45599, 'Phase 3', 'Death Knight', 'Blood', 'Feet', 'Both', 'Sabatons of Lifeless Night'), +(6, 0, 7, 1, 258, 47003, 'Phase 3', 'Death Knight', 'Blood Tank', 'Feet', 'Alliance', 'Dawnbreaker Greaves'), +(6, 0, 7, 2, 258, 47430, 'Phase 3', 'Death Knight', 'Blood Tank', 'Feet', 'Horde', 'Dawnbreaker Sabatons'), +(6, 0, 8, 0, 258, 45663, 'Phase 3', 'Death Knight', 'Blood', 'Wrists', 'Both', 'Armbands of Bedlam'), +(6, 0, 8, 1, 258, 47111, 'Phase 3', 'Death Knight', 'Blood Tank', 'Wrists', 'Alliance', 'Bracers of the Shieldmaiden'), +(6, 0, 8, 2, 258, 47459, 'Phase 3', 'Death Knight', 'Blood Tank', 'Wrists', 'Horde', 'Armguards of the Shieldmaiden'), +(6, 0, 9, 0, 258, 48546, 'Phase 3', 'Death Knight', 'Blood Tank', 'Hands', 'Both', 'Handguards of Triumph'), +(6, 0, 9, 1, 258, 47240, 'Phase 3', 'Death Knight', 'Blood', 'Hands', 'Alliance', 'Gloves of Bitter Reprisal'), +(6, 0, 9, 2, 258, 47492, 'Phase 3', 'Death Knight', 'Blood', 'Hands', 'Horde', 'Gauntlets of Bitter Reprisal'), +(6, 0, 10, 1, 258, 45471, 'Phase 3', 'Death Knight', 'Blood Tank', 'Finger1', 'Alliance', 'Fate''s Clutch'), +(6, 0, 10, 2, 258, 47413, 'Phase 3', 'Death Knight', 'Blood', 'Finger1', 'Horde', 'Ring of the Violent Temperament'), +(6, 0, 12, 0, 258, 45931, 'Phase 3', 'Death Knight', 'Blood', 'Trinket1', 'Both', 'Mjolnir Runestone'), +(6, 0, 12, 1, 258, 47216, 'Phase 3', 'Death Knight', 'Blood Tank', 'Trinket1', 'Alliance', 'The Black Heart'), +(6, 0, 14, 1, 258, 47549, 'Phase 3', 'Death Knight', 'Blood Tank', 'Back', 'Alliance', 'Magni''s Resolution'), +(6, 0, 14, 2, 258, 47550, 'Phase 3', 'Death Knight', 'Blood Tank', 'Back', 'Horde', 'Cairne''s Endurance'), +(6, 0, 15, 0, 258, 47156, 'Phase 3', 'Death Knight', 'Blood', 'MainHand', 'Both', 'Stormpike Cleaver'), +(6, 0, 15, 2, 258, 47475, 'Phase 3', 'Death Knight', 'Blood', 'MainHand', 'Horde', 'Hellscream Slicer'), +(6, 0, 17, 0, 258, 47672, 'Phase 3', 'Death Knight', 'Blood Tank', 'Ranged', 'Both', 'Sigil of Insolence'); + +-- ilvl 264 (Phase 4) +INSERT INTO `playerbots_bis_gear` VALUES +(6, 0, 0, 0, 264, 51306, 'Phase 4', 'Death Knight', 'Blood Tank', 'Head', 'Both', 'Sanctified Scourgelord Faceguard'), +(6, 0, 1, 0, 264, 54581, 'Phase 4', 'Death Knight', 'Blood', 'Neck', 'Both', 'Penumbra Pendant'), +(6, 0, 2, 0, 264, 51309, 'Phase 4', 'Death Knight', 'Blood Tank', 'Shoulders', 'Both', 'Sanctified Scourgelord Pauldrons'), +(6, 0, 4, 0, 264, 51305, 'Phase 4', 'Death Knight', 'Blood Tank', 'Chest', 'Both', 'Sanctified Scourgelord Chestguard'), +(6, 0, 5, 0, 264, 50691, 'Phase 4', 'Death Knight', 'Blood Tank', 'Waist', 'Both', 'Belt of Broken Bones'), +(6, 0, 6, 0, 264, 50612, 'Phase 4', 'Death Knight', 'Blood Tank', 'Legs', 'Both', 'Legguards of Lost Hope'), +(6, 0, 7, 0, 264, 50625, 'Phase 4', 'Death Knight', 'Blood Tank', 'Feet', 'Both', 'Grinning Skull Greatboots'), +(6, 0, 8, 0, 264, 50611, 'Phase 4', 'Death Knight', 'Blood Tank', 'Wrists', 'Both', 'Bracers of Dark Reckoning'), +(6, 0, 9, 0, 264, 51307, 'Phase 4', 'Death Knight', 'Blood Tank', 'Hands', 'Both', 'Sanctified Scourgelord Handguards'), +(6, 0, 10, 0, 264, 50622, 'Phase 4', 'Death Knight', 'Blood Tank', 'Finger1', 'Both', 'Devium''s Eternally Cold Ring'), +(6, 0, 12, 0, 264, 50364, 'Phase 4', 'Death Knight', 'Blood Tank', 'Trinket1', 'Both', 'Sindragosa''s Flawless Fang'), +(6, 0, 14, 0, 264, 50718, 'Phase 4', 'Death Knight', 'Blood Tank', 'Back', 'Both', 'Royal Crimson Cloak'), +(6, 0, 15, 0, 264, 49623, 'Phase 4', 'Death Knight', 'Blood', 'MainHand', 'Both', 'Shadowmourne'), +(6, 0, 17, 0, 264, 50462, 'Phase 4', 'Death Knight', 'Blood Tank', 'Ranged', 'Both', 'Sigil of the Bone Gryphon'); + +-- ilvl 290 (Phase 5) +INSERT INTO `playerbots_bis_gear` VALUES +(6, 0, 0, 0, 290, 51306, 'Phase 5', 'DeathKnight', 'BloodTank', 'Head', 'Both', 'Sanctified Scourgelord Faceguard'), +(6, 0, 1, 0, 290, 50682, 'Phase 5', 'DeathKnight', 'BloodTank', 'Neck', 'Both', 'Bile-Encrusted Medallion'), +(6, 0, 2, 0, 290, 51309, 'Phase 5', 'DeathKnight', 'BloodTank', 'Shoulders', 'Both', 'Sanctified Scourgelord Pauldrons'), +(6, 0, 4, 0, 290, 51305, 'Phase 5', 'DeathKnight', 'BloodTank', 'Chest', 'Both', 'Sanctified Scourgelord Chestguard'), +(6, 0, 5, 0, 290, 50991, 'Phase 5', 'DeathKnight', 'BloodTank', 'Waist', 'Both', 'Verdigris Chain Belt'), +(6, 0, 6, 0, 290, 50612, 'Phase 5', 'DeathKnight', 'BloodTank', 'Legs', 'Both', 'Legguards of Lost Hope'), +(6, 0, 7, 0, 290, 54579, 'Phase 5', 'DeathKnight', 'BloodTank', 'Feet', 'Both', 'Treads of Impending Resurrection'), +(6, 0, 8, 0, 290, 51901, 'Phase 5', 'DeathKnight', 'BloodTank', 'Wrists', 'Both', 'Gargoyle Spit Bracers'), +(6, 0, 9, 0, 290, 51307, 'Phase 5', 'DeathKnight', 'BloodTank', 'Hands', 'Both', 'Sanctified Scourgelord Handguards'), +(6, 0, 10, 0, 290, 50622, 'Phase 5', 'DeathKnight', 'BloodTank', 'Finger1', 'Both', 'Devium''s Eternally Cold Ring'), +(6, 0, 11, 0, 290, 50404, 'Phase 5', 'DeathKnight', 'BloodTank', 'Finger2', 'Both', 'Ashen Band of Endless Courage'), +(6, 0, 12, 0, 290, 54591, 'Phase 5', 'DeathKnight', 'BloodTank', 'Trinket1', 'Both', 'Petrified Twilight Scale'), +(6, 0, 13, 0, 290, 50364, 'Phase 5', 'DeathKnight', 'BloodTank', 'Trinket2', 'Both', 'Sindragosa''s Flawless Fang'), +(6, 0, 14, 0, 290, 50466, 'Phase 5', 'DeathKnight', 'BloodTank', 'Back', 'Both', 'Sentinel''s Winter Cloak'), +(6, 0, 15, 0, 290, 49623, 'Phase 5', 'DeathKnight', 'BloodTank', 'MainHand', 'Both', 'Shadowmourne'), +(6, 0, 17, 0, 290, 50462, 'Phase 5', 'DeathKnight', 'BloodTank', 'Ranged', 'Both', 'Sigil of the Bone Gryphon'); + +-- Frost (tab 1) +-- ilvl 200 (Pre-Raid) +INSERT INTO `playerbots_bis_gear` VALUES +(6, 1, 0, 0, 200, 41386, 'Pre-Raid', 'Death Knight', 'Frost', 'Head', 'Both', 'Spiked Titansteel Helm'), +(6, 1, 1, 0, 200, 42645, 'Pre-Raid', 'Death Knight', 'Frost', 'Neck', 'Both', 'Titanium Impact Choker'), +(6, 1, 2, 0, 200, 37627, 'Pre-Raid', 'Death Knight', 'Frost', 'Shoulders', 'Both', 'Snake Den Spaulders'), +(6, 1, 4, 0, 200, 37612, 'Pre-Raid', 'Death Knight', 'Frost', 'Chest', 'Both', 'Bonegrinder Breastplate'), +(6, 1, 5, 0, 200, 37171, 'Pre-Raid', 'Death Knight', 'Frost', 'Waist', 'Both', 'Flame-Bathed Steel Girdle'), +(6, 1, 6, 0, 200, 37193, 'Pre-Raid', 'Death Knight', 'Frost', 'Legs', 'Both', 'Staggering Legplates'), +(6, 1, 7, 0, 200, 41391, 'Pre-Raid', 'Death Knight', 'Frost', 'Feet', 'Both', 'Spiked Titansteel Treads'), +(6, 1, 8, 0, 200, 37668, 'Pre-Raid', 'Death Knight', 'Frost', 'Wrists', 'Both', 'Bands of the Stoneforge'), +(6, 1, 9, 0, 200, 37363, 'Pre-Raid', 'Death Knight', 'Frost', 'Hands', 'Both', 'Gauntlets of Dragon Wrath'), +(6, 1, 10, 0, 200, 37642, 'Pre-Raid', 'Death Knight', 'Frost', 'Finger1', 'Both', 'Hemorrhaging Circle'), +(6, 1, 12, 0, 200, 40684, 'Pre-Raid', 'Death Knight', 'Frost', 'Trinket1', 'Both', 'Mirror of Truth'), +(6, 1, 14, 0, 200, 43566, 'Pre-Raid', 'Death Knight', 'Frost', 'Back', 'Both', 'Ice Striker''s Cloak'), +(6, 1, 15, 0, 200, 41383, 'Pre-Raid', 'Death Knight', 'Frost', 'MainHand', 'Both', 'Titansteel Bonecrusher'), +(6, 1, 17, 0, 200, 40715, 'Pre-Raid', 'Death Knight', 'Frost', 'Ranged', 'Both', 'Sigil of Haunted Dreams'); + +-- ilvl 224 (Phase 1) +INSERT INTO `playerbots_bis_gear` VALUES +(6, 1, 0, 0, 224, 44006, 'Phase 1', 'Death Knight', 'Frost', 'Head', 'Both', 'Obsidian Greathelm'), +(6, 1, 1, 0, 224, 44664, 'Phase 1', 'Death Knight', 'Frost', 'Neck', 'Both', 'Favor of the Dragon Queen'), +(6, 1, 2, 0, 224, 40557, 'Phase 1', 'Death Knight', 'Frost', 'Shoulders', 'Both', 'Valorous Scourgeborne Shoulderplates'), +(6, 1, 4, 0, 224, 40550, 'Phase 1', 'Death Knight', 'Frost', 'Chest', 'Both', 'Valorous Scourgeborne Battleplate'), +(6, 1, 5, 0, 224, 40317, 'Phase 1', 'Death Knight', 'Frost', 'Waist', 'Both', 'Girdle of Razuvious'), +(6, 1, 6, 0, 224, 40556, 'Phase 1', 'Death Knight', 'Frost', 'Legs', 'Both', 'Valorous Scourgeborne Legplates'), +(6, 1, 7, 0, 224, 40591, 'Phase 1', 'Death Knight', 'Frost', 'Feet', 'Both', 'Melancholy Sabatons'), +(6, 1, 8, 0, 224, 40330, 'Phase 1', 'Death Knight', 'Frost', 'Wrists', 'Both', 'Bracers of Unrelenting Attack'), +(6, 1, 9, 0, 224, 40552, 'Phase 1', 'Death Knight', 'Frost', 'Hands', 'Both', 'Valorous Scourgeborne Gauntlets'), +(6, 1, 10, 0, 224, 40075, 'Phase 1', 'Death Knight', 'Frost', 'Finger1', 'Both', 'Ruthlessness'), +(6, 1, 12, 0, 224, 40256, 'Phase 1', 'Death Knight', 'Frost', 'Trinket1', 'Both', 'Grim Toll'), +(6, 1, 14, 0, 224, 40403, 'Phase 1', 'Death Knight', 'Frost', 'Back', 'Both', 'Drape of the Deadly Foe'), +(6, 1, 15, 0, 224, 40189, 'Phase 1', 'Death Knight', 'Frost', 'MainHand', 'Both', 'Angry Dread'), +(6, 1, 17, 0, 224, 40207, 'Phase 1', 'Death Knight', 'Frost', 'Ranged', 'Both', 'Sigil of Awareness'); + +-- ilvl 245 (Phase 2) +INSERT INTO `playerbots_bis_gear` VALUES +(6, 1, 0, 0, 245, 46115, 'Phase 2', 'Death Knight', 'Frost', 'Head', 'Both', 'Conqueror''s Darkruned Helmet'), +(6, 1, 1, 0, 245, 45459, 'Phase 2', 'Death Knight', 'Frost', 'Neck', 'Both', 'Frigid Strength of Hodir'), +(6, 1, 2, 0, 245, 46117, 'Phase 2', 'Death Knight', 'Frost', 'Shoulders', 'Both', 'Conqueror''s Darkruned Shoulderplates'), +(6, 1, 4, 0, 245, 46111, 'Phase 2', 'Death Knight', 'Frost', 'Chest', 'Both', 'Conqueror''s Darkruned Battleplate'), +(6, 1, 5, 0, 245, 45241, 'Phase 2', 'Death Knight', 'Frost', 'Waist', 'Both', 'Belt of Colossal Rage'), +(6, 1, 6, 0, 245, 45134, 'Phase 2', 'Death Knight', 'Frost', 'Legs', 'Both', 'Plated Leggings of Ruination'), +(6, 1, 7, 0, 245, 45599, 'Phase 2', 'Death Knight', 'Frost', 'Feet', 'Both', 'Sabatons of Lifeless Night'), +(6, 1, 8, 0, 245, 45663, 'Phase 2', 'Death Knight', 'Frost', 'Wrists', 'Both', 'Armbands of Bedlam'), +(6, 1, 9, 0, 245, 46113, 'Phase 2', 'Death Knight', 'Frost', 'Hands', 'Both', 'Conqueror''s Darkruned Gauntlets'), +(6, 1, 10, 0, 245, 45534, 'Phase 2', 'Death Knight', 'Frost', 'Finger1', 'Both', 'Seal of the Betrayed King'), +(6, 1, 12, 0, 245, 45931, 'Phase 2', 'Death Knight', 'Frost', 'Trinket1', 'Both', 'Mjolnir Runestone'), +(6, 1, 14, 0, 245, 46032, 'Phase 2', 'Death Knight', 'Frost', 'Back', 'Both', 'Drape of the Faceless General'), +(6, 1, 15, 0, 245, 46097, 'Phase 2', 'Death Knight', 'Frost', 'MainHand', 'Both', 'Caress of Insanity'), +(6, 1, 17, 0, 245, 40207, 'Phase 2', 'Death Knight', 'Frost', 'Ranged', 'Both', 'Sigil of Awareness'); + +-- ilvl 258 (Phase 3) +INSERT INTO `playerbots_bis_gear` VALUES +(6, 1, 0, 1, 258, 48488, 'Phase 3', 'Death Knight', 'Frost', 'Head', 'Alliance', 'Thassarian''s Helmet of Triumph'), +(6, 1, 0, 2, 258, 48493, 'Phase 3', 'Death Knight', 'Frost', 'Head', 'Horde', 'Koltira''s Helmet of Triumph'), +(6, 1, 1, 1, 258, 47110, 'Phase 3', 'Death Knight', 'Frost', 'Neck', 'Alliance', 'The Executioner''s Malice'), +(6, 1, 1, 2, 258, 47458, 'Phase 3', 'Death Knight', 'Frost', 'Neck', 'Horde', 'The Executioner''s Vice'), +(6, 1, 2, 1, 258, 48486, 'Phase 3', 'Death Knight', 'Frost', 'Shoulders', 'Alliance', 'Thassarian''s Shoulderplates of Triumph'), +(6, 1, 2, 2, 258, 48495, 'Phase 3', 'Death Knight', 'Frost', 'Shoulders', 'Horde', 'Koltira''s Shoulderplates of Triumph'), +(6, 1, 4, 1, 258, 48490, 'Phase 3', 'Death Knight', 'Frost', 'Chest', 'Alliance', 'Thassarian''s Battleplate of Triumph'), +(6, 1, 4, 2, 258, 48491, 'Phase 3', 'Death Knight', 'Frost', 'Chest', 'Horde', 'Koltira''s Battleplate of Triumph'), +(6, 1, 5, 0, 258, 45241, 'Phase 3', 'Death Knight', 'Frost', 'Waist', 'Both', 'Belt of Colossal Rage'), +(6, 1, 6, 1, 258, 48487, 'Phase 3', 'Death Knight', 'Frost', 'Legs', 'Alliance', 'Thassarian''s Legplates of Triumph'), +(6, 1, 6, 2, 258, 48494, 'Phase 3', 'Death Knight', 'Frost', 'Legs', 'Horde', 'Koltira''s Legplates of Triumph'), +(6, 1, 7, 0, 258, 45599, 'Phase 3', 'Death Knight', 'Frost', 'Feet', 'Both', 'Sabatons of Lifeless Night'), +(6, 1, 8, 0, 258, 45663, 'Phase 3', 'Death Knight', 'Frost', 'Wrists', 'Both', 'Armbands of Bedlam'), +(6, 1, 9, 1, 258, 47240, 'Phase 3', 'Death Knight', 'Frost', 'Hands', 'Alliance', 'Gloves of Bitter Reprisal'), +(6, 1, 9, 2, 258, 47492, 'Phase 3', 'Death Knight', 'Frost', 'Hands', 'Horde', 'Gauntlets of Bitter Reprisal'), +(6, 1, 10, 1, 258, 46966, 'Phase 3', 'Death Knight', 'Frost', 'Finger1', 'Alliance', 'Band of the Violent Temperment'), +(6, 1, 10, 2, 258, 47413, 'Phase 3', 'Death Knight', 'Frost', 'Finger1', 'Horde', 'Ring of the Violent Temperament'), +(6, 1, 12, 0, 258, 45931, 'Phase 3', 'Death Knight', 'Frost', 'Trinket1', 'Both', 'Mjolnir Runestone'), +(6, 1, 14, 1, 258, 47547, 'Phase 3', 'Death Knight', 'Frost', 'Back', 'Alliance', 'Varian''s Furor'), +(6, 1, 14, 2, 258, 47548, 'Phase 3', 'Death Knight', 'Frost', 'Back', 'Horde', 'Garrosh''s Rage'), +(6, 1, 15, 0, 258, 47156, 'Phase 3', 'Death Knight', 'Frost', 'MainHand', 'Both', 'Stormpike Cleaver'), +(6, 1, 15, 2, 258, 47475, 'Phase 3', 'Death Knight', 'Frost', 'MainHand', 'Horde', 'Hellscream Slicer'), +(6, 1, 17, 0, 258, 40207, 'Phase 3', 'Death Knight', 'Frost', 'Ranged', 'Both', 'Sigil of Awareness'); + +-- ilvl 264 (Phase 4) +INSERT INTO `playerbots_bis_gear` VALUES +(6, 1, 0, 0, 264, 51312, 'Phase 4', 'Death Knight', 'Frost', 'Head', 'Both', 'Sanctified Scourgelord Helmet'), +(6, 1, 1, 0, 264, 50728, 'Phase 4', 'Death Knight', 'Frost', 'Neck', 'Both', 'Lana''thel''s Chain of Flagellation'), +(6, 1, 2, 0, 264, 51314, 'Phase 4', 'Death Knight', 'Frost', 'Shoulders', 'Both', 'Sanctified Scourgelord Shoulderplates'), +(6, 1, 4, 0, 264, 51310, 'Phase 4', 'Death Knight', 'Frost', 'Chest', 'Both', 'Sanctified Scourgelord Battleplate'), +(6, 1, 5, 0, 264, 50620, 'Phase 4', 'Death Knight', 'Frost', 'Waist', 'Both', 'Coldwraith Links'), +(6, 1, 6, 0, 264, 51817, 'Phase 4', 'Death Knight', 'Frost', 'Legs', 'Both', 'Legplates of Aetheric Strife'), +(6, 1, 7, 0, 264, 50639, 'Phase 4', 'Death Knight', 'Frost', 'Feet', 'Both', 'Blood-Soaked Saronite Stompers'), +(6, 1, 8, 0, 264, 50659, 'Phase 4', 'Death Knight', 'Frost', 'Wrists', 'Both', 'Polar Bear Claw Bracers'), +(6, 1, 9, 0, 264, 51311, 'Phase 4', 'Death Knight', 'Frost', 'Hands', 'Both', 'Sanctified Scourgelord Gauntlets'), +(6, 1, 10, 0, 264, 52572, 'Phase 4', 'Death Knight', 'Frost', 'Finger1', 'Both', 'Ashen Band of Endless Might'), +(6, 1, 12, 0, 264, 50363, 'Phase 4', 'Death Knight', 'Frost', 'Trinket1', 'Both', 'Deathbringer''s Will'), +(6, 1, 14, 1, 264, 47547, 'Phase 4', 'Death Knight', 'Frost', 'Back', 'Alliance', 'Varian''s Furor'), +(6, 1, 14, 2, 264, 47548, 'Phase 4', 'Death Knight', 'Frost', 'Back', 'Horde', 'Garrosh''s Rage'), +(6, 1, 15, 0, 264, 50737, 'Phase 4', 'Death Knight', 'Frost', 'MainHand', 'Both', 'Havoc''s Call, Blade of Lordaeron Kings'), +(6, 1, 17, 0, 264, 40207, 'Phase 4', 'Death Knight', 'Frost', 'Ranged', 'Both', 'Sigil of Awareness'); + +-- ilvl 290 (Phase 5) +INSERT INTO `playerbots_bis_gear` VALUES +(6, 1, 0, 0, 290, 51312, 'Phase 5', 'DeathKnight', 'Frost', 'Head', 'Both', 'Sanctified Scourgelord Helmet'), +(6, 1, 1, 0, 290, 54581, 'Phase 5', 'DeathKnight', 'Frost', 'Neck', 'Both', 'Penumbra Pendant'), +(6, 1, 2, 0, 290, 51314, 'Phase 5', 'DeathKnight', 'Frost', 'Shoulders', 'Both', 'Sanctified Scourgelord Shoulderplates'), +(6, 1, 4, 0, 290, 51310, 'Phase 5', 'DeathKnight', 'Frost', 'Chest', 'Both', 'Sanctified Scourgelord Battleplate'), +(6, 1, 5, 0, 290, 50620, 'Phase 5', 'DeathKnight', 'Frost', 'Waist', 'Both', 'Coldwraith Links'), +(6, 1, 6, 0, 290, 51313, 'Phase 5', 'DeathKnight', 'Frost', 'Legs', 'Both', 'Sanctified Scourgelord Legplates'), +(6, 1, 7, 0, 290, 54578, 'Phase 5', 'DeathKnight', 'Frost', 'Feet', 'Both', 'Apocalypse''s Advance'), +(6, 1, 8, 0, 290, 50670, 'Phase 5', 'DeathKnight', 'Frost', 'Wrists', 'Both', 'Toskk''s Maximized Wristguards'), +(6, 1, 9, 0, 290, 50690, 'Phase 5', 'DeathKnight', 'Frost', 'Hands', 'Both', 'Fleshrending Gauntlets'), +(6, 1, 10, 0, 290, 50693, 'Phase 5', 'DeathKnight', 'Frost', 'Finger1', 'Both', 'Might of Blight'), +(6, 1, 11, 0, 290, 52572, 'Phase 5', 'DeathKnight', 'Frost', 'Finger2', 'Both', 'Ashen Band of Endless Might'), +(6, 1, 12, 0, 290, 54590, 'Phase 5', 'DeathKnight', 'Frost', 'Trinket1', 'Both', 'Sharpened Twilight Scale'), +(6, 1, 13, 0, 290, 50363, 'Phase 5', 'DeathKnight', 'Frost', 'Trinket2', 'Both', 'Deathbringer''s Will'), +(6, 1, 14, 0, 290, 50677, 'Phase 5', 'DeathKnight', 'Frost', 'Back', 'Both', 'Winding Sheet'), +(6, 1, 15, 0, 290, 50737, 'Phase 5', 'DeathKnight', 'Frost', 'MainHand', 'Both', 'Havoc''s Call, Blade of Lordaeron Kings'), +(6, 1, 16, 0, 290, 50737, 'Phase 5', 'DeathKnight', 'Frost', 'OffHand', 'Both', 'Havoc''s Call, Blade of Lordaeron Kings'), +(6, 1, 17, 0, 290, 50459, 'Phase 5', 'DeathKnight', 'Frost', 'Ranged', 'Both', 'Sigil of the Hanged Man'); + +-- Unholy (tab 2) +-- ilvl 200 (Pre-Raid) +INSERT INTO `playerbots_bis_gear` VALUES +(6, 2, 0, 0, 200, 41386, 'Pre-Raid', 'Death Knight', 'Unholy', 'Head', 'Both', 'Spiked Titansteel Helm'), +(6, 2, 1, 0, 200, 42645, 'Pre-Raid', 'Death Knight', 'Unholy', 'Neck', 'Both', 'Titanium Impact Choker'), +(6, 2, 2, 0, 200, 37627, 'Pre-Raid', 'Death Knight', 'Unholy', 'Shoulders', 'Both', 'Snake Den Spaulders'), +(6, 2, 4, 0, 200, 39617, 'Pre-Raid', 'Death Knight', 'Unholy', 'Chest', 'Both', 'Heroes'' Scourgeborne Battleplate'), +(6, 2, 5, 0, 200, 37178, 'Pre-Raid', 'Death Knight', 'Unholy', 'Waist', 'Both', 'Strategist''s Belt'), +(6, 2, 6, 0, 200, 39620, 'Pre-Raid', 'Death Knight', 'Unholy', 'Legs', 'Both', 'Heroes'' Scourgeborne Legplates'), +(6, 2, 7, 0, 200, 43402, 'Pre-Raid', 'Death Knight', 'Unholy', 'Feet', 'Both', 'The Obliterator Greaves'), +(6, 2, 8, 0, 200, 37668, 'Pre-Raid', 'Death Knight', 'Unholy', 'Wrists', 'Both', 'Bands of the Stoneforge'), +(6, 2, 9, 0, 200, 39618, 'Pre-Raid', 'Death Knight', 'Unholy', 'Hands', 'Both', 'Heroes'' Scourgeborne Gauntlets'), +(6, 2, 10, 0, 200, 37151, 'Pre-Raid', 'Death Knight', 'Unholy', 'Finger1', 'Both', 'Band of Frosted Thorns'), +(6, 2, 12, 0, 200, 40684, 'Pre-Raid', 'Death Knight', 'Unholy', 'Trinket1', 'Both', 'Mirror of Truth'), +(6, 2, 14, 0, 200, 43566, 'Pre-Raid', 'Death Knight', 'Unholy', 'Back', 'Both', 'Ice Striker''s Cloak'), +(6, 2, 15, 0, 200, 41257, 'Pre-Raid', 'Death Knight', 'Unholy', 'MainHand', 'Both', 'Titansteel Destroyer'), +(6, 2, 17, 0, 200, 40715, 'Pre-Raid', 'Death Knight', 'Unholy', 'Ranged', 'Both', 'Sigil of Haunted Dreams'); + +-- ilvl 224 (Phase 1) +INSERT INTO `playerbots_bis_gear` VALUES +(6, 2, 0, 0, 224, 44006, 'Phase 1', 'Death Knight', 'Unholy', 'Head', 'Both', 'Obsidian Greathelm'), +(6, 2, 1, 0, 224, 44664, 'Phase 1', 'Death Knight', 'Unholy', 'Neck', 'Both', 'Favor of the Dragon Queen'), +(6, 2, 2, 0, 224, 40557, 'Phase 1', 'Death Knight', 'Unholy', 'Shoulders', 'Both', 'Valorous Scourgeborne Shoulderplates'), +(6, 2, 4, 0, 224, 40550, 'Phase 1', 'Death Knight', 'Unholy', 'Chest', 'Both', 'Valorous Scourgeborne Battleplate'), +(6, 2, 5, 0, 224, 40317, 'Phase 1', 'Death Knight', 'Unholy', 'Waist', 'Both', 'Girdle of Razuvious'), +(6, 2, 6, 0, 224, 40556, 'Phase 1', 'Death Knight', 'Unholy', 'Legs', 'Both', 'Valorous Scourgeborne Legplates'), +(6, 2, 7, 0, 224, 40206, 'Phase 1', 'Death Knight', 'Unholy', 'Feet', 'Both', 'Iron-Spring Jumpers'), +(6, 2, 8, 0, 224, 40330, 'Phase 1', 'Death Knight', 'Unholy', 'Wrists', 'Both', 'Bracers of Unrelenting Attack'), +(6, 2, 9, 0, 224, 40552, 'Phase 1', 'Death Knight', 'Unholy', 'Hands', 'Both', 'Valorous Scourgeborne Gauntlets'), +(6, 2, 10, 0, 224, 40074, 'Phase 1', 'Death Knight', 'Unholy', 'Finger1', 'Both', 'Strong-Handed Ring'), +(6, 2, 12, 0, 224, 42987, 'Phase 1', 'Death Knight', 'Unholy', 'Trinket1', 'Both', 'Darkmoon Card: Greatness'), +(6, 2, 14, 0, 224, 40250, 'Phase 1', 'Death Knight', 'Unholy', 'Back', 'Both', 'Aged Winter Cloak'), +(6, 2, 17, 0, 224, 40207, 'Phase 1', 'Death Knight', 'Unholy', 'Ranged', 'Both', 'Sigil of Awareness'); + +-- ilvl 245 (Phase 2) +INSERT INTO `playerbots_bis_gear` VALUES +(6, 2, 0, 1, 245, 51312, 'Phase 2', 'Death Knight', 'Unholy', 'Head', 'Alliance', 'Sanctified Scourgelord Helmet'), +(6, 2, 0, 2, 245, 45472, 'Phase 2', 'Death Knight', 'Unholy', 'Head', 'Horde', 'Warhelm of the Champion'), +(6, 2, 1, 1, 245, 54581, 'Phase 2', 'Death Knight', 'Unholy', 'Neck', 'Alliance', 'Penumbra Pendant'), +(6, 2, 1, 2, 245, 46040, 'Phase 2', 'Death Knight', 'Unholy', 'Neck', 'Horde', 'Strength of the Heavens'), +(6, 2, 2, 1, 245, 51314, 'Phase 2', 'Death Knight', 'Unholy', 'Shoulders', 'Alliance', 'Sanctified Scourgelord Shoulderplates'), +(6, 2, 2, 2, 245, 46117, 'Phase 2', 'Death Knight', 'Unholy', 'Shoulders', 'Horde', 'Conqueror''s Darkruned Shoulderplates'), +(6, 2, 4, 1, 245, 51310, 'Phase 2', 'Death Knight', 'Unholy', 'Chest', 'Alliance', 'Sanctified Scourgelord Battleplate'), +(6, 2, 4, 2, 245, 46111, 'Phase 2', 'Death Knight', 'Unholy', 'Chest', 'Horde', 'Conqueror''s Darkruned Battleplate'), +(6, 2, 5, 1, 245, 50620, 'Phase 2', 'Death Knight', 'Unholy', 'Waist', 'Alliance', 'Coldwraith Links'), +(6, 2, 5, 2, 245, 45241, 'Phase 2', 'Death Knight', 'Unholy', 'Waist', 'Horde', 'Belt of Colossal Rage'), +(6, 2, 6, 1, 245, 50624, 'Phase 2', 'Death Knight', 'Unholy', 'Legs', 'Alliance', 'Scourge Reaver''s Legplates'), +(6, 2, 6, 2, 245, 46116, 'Phase 2', 'Death Knight', 'Unholy', 'Legs', 'Horde', 'Conqueror''s Darkruned Legplates'), +(6, 2, 7, 1, 245, 54578, 'Phase 2', 'Death Knight', 'Unholy', 'Feet', 'Alliance', 'Apocalypse''s Advance'), +(6, 2, 7, 2, 245, 45599, 'Phase 2', 'Death Knight', 'Unholy', 'Feet', 'Horde', 'Sabatons of Lifeless Night'), +(6, 2, 8, 1, 245, 51842, 'Phase 2', 'Death Knight', 'Unholy', 'Wrists', 'Alliance', 'Collar of Haughty Disdain'), +(6, 2, 8, 2, 245, 45663, 'Phase 2', 'Death Knight', 'Unholy', 'Wrists', 'Horde', 'Armbands of Bedlam'), +(6, 2, 9, 1, 245, 51311, 'Phase 2', 'Death Knight', 'Unholy', 'Hands', 'Alliance', 'Sanctified Scourgelord Gauntlets'), +(6, 2, 9, 2, 245, 46113, 'Phase 2', 'Death Knight', 'Unholy', 'Hands', 'Horde', 'Conqueror''s Darkruned Gauntlets'), +(6, 2, 10, 1, 245, 50693, 'Phase 2', 'Death Knight', 'Unholy', 'Finger1', 'Alliance', 'Might of Blight'), +(6, 2, 10, 2, 245, 45469, 'Phase 2', 'Death Knight', 'Unholy', 'Finger1', 'Horde', 'Sif''s Promise'), +(6, 2, 12, 1, 245, 54590, 'Phase 2', 'Death Knight', 'Unholy', 'Trinket1', 'Alliance', 'Sharpened Twilight Scale'), +(6, 2, 12, 2, 245, 45609, 'Phase 2', 'Death Knight', 'Unholy', 'Trinket1', 'Horde', 'Comet''s Trail'), +(6, 2, 14, 1, 245, 50677, 'Phase 2', 'Death Knight', 'Unholy', 'Back', 'Alliance', 'Winding Sheet'), +(6, 2, 14, 2, 245, 46032, 'Phase 2', 'Death Knight', 'Unholy', 'Back', 'Horde', 'Drape of the Faceless General'), +(6, 2, 17, 1, 245, 50459, 'Phase 2', 'Death Knight', 'Unholy', 'Ranged', 'Alliance', 'Sigil of the Hanged Man'), +(6, 2, 17, 2, 245, 45254, 'Phase 2', 'Death Knight', 'Unholy', 'Ranged', 'Horde', 'Sigil of the Vengeful Heart'); + +-- ilvl 258 (Phase 3) +INSERT INTO `playerbots_bis_gear` VALUES +(6, 2, 0, 0, 258, 45472, 'Phase 3', 'Death Knight', 'Unholy', 'Head', 'Both', 'Warhelm of the Champion'), +(6, 2, 1, 0, 258, 46040, 'Phase 3', 'Death Knight', 'Unholy', 'Neck', 'Both', 'Strength of the Heavens'), +(6, 2, 2, 0, 258, 46117, 'Phase 3', 'Death Knight', 'Unholy', 'Shoulders', 'Both', 'Conqueror''s Darkruned Shoulderplates'), +(6, 2, 4, 0, 258, 45712, 'Phase 3', 'Death Knight', 'Unholy', 'Chest', 'Both', 'Chestplate of Titanic Fury'), +(6, 2, 5, 0, 258, 45241, 'Phase 3', 'Death Knight', 'Unholy', 'Waist', 'Both', 'Belt of Colossal Rage'), +(6, 2, 6, 0, 258, 46116, 'Phase 3', 'Death Knight', 'Unholy', 'Legs', 'Both', 'Conqueror''s Darkruned Legplates'), +(6, 2, 7, 0, 258, 45599, 'Phase 3', 'Death Knight', 'Unholy', 'Feet', 'Both', 'Sabatons of Lifeless Night'), +(6, 2, 8, 0, 258, 45663, 'Phase 3', 'Death Knight', 'Unholy', 'Wrists', 'Both', 'Armbands of Bedlam'), +(6, 2, 9, 0, 258, 46113, 'Phase 3', 'Death Knight', 'Unholy', 'Hands', 'Both', 'Conqueror''s Darkruned Gauntlets'), +(6, 2, 10, 0, 258, 45250, 'Phase 3', 'Death Knight', 'Unholy', 'Finger1', 'Both', 'Crazed Construct Ring'), +(6, 2, 12, 0, 258, 40531, 'Phase 3', 'Death Knight', 'Unholy', 'Trinket1', 'Both', 'Mark of Norgannon'), +(6, 2, 14, 0, 258, 45588, 'Phase 3', 'Death Knight', 'Unholy', 'Back', 'Both', 'Drape of the Skyborn'), +(6, 2, 17, 0, 258, 45254, 'Phase 3', 'Death Knight', 'Unholy', 'Ranged', 'Both', 'Sigil of the Vengeful Heart'); + +-- ilvl 264 (Phase 4) +INSERT INTO `playerbots_bis_gear` VALUES +(6, 2, 0, 1, 264, 48488, 'Phase 4', 'Death Knight', 'Unholy', 'Head', 'Alliance', 'Thassarian''s Helmet of Triumph'), +(6, 2, 0, 2, 264, 48493, 'Phase 4', 'Death Knight', 'Unholy', 'Head', 'Horde', 'Koltira''s Helmet of Triumph'), +(6, 2, 1, 1, 264, 47110, 'Phase 4', 'Death Knight', 'Unholy', 'Neck', 'Alliance', 'The Executioner''s Malice'), +(6, 2, 1, 2, 264, 47458, 'Phase 4', 'Death Knight', 'Unholy', 'Neck', 'Horde', 'The Executioner''s Vice'), +(6, 2, 2, 1, 264, 48486, 'Phase 4', 'Death Knight', 'Unholy', 'Shoulders', 'Alliance', 'Thassarian''s Shoulderplates of Triumph'), +(6, 2, 2, 2, 264, 48495, 'Phase 4', 'Death Knight', 'Unholy', 'Shoulders', 'Horde', 'Koltira''s Shoulderplates of Triumph'), +(6, 2, 4, 1, 264, 48490, 'Phase 4', 'Death Knight', 'Unholy', 'Chest', 'Alliance', 'Thassarian''s Battleplate of Triumph'), +(6, 2, 4, 2, 264, 48491, 'Phase 4', 'Death Knight', 'Unholy', 'Chest', 'Horde', 'Koltira''s Battleplate of Triumph'), +(6, 2, 5, 1, 264, 47002, 'Phase 4', 'Death Knight', 'Unholy', 'Waist', 'Alliance', 'Bloodbath Belt'), +(6, 2, 5, 2, 264, 47429, 'Phase 4', 'Death Knight', 'Unholy', 'Waist', 'Horde', 'Bloodbath Girdle'), +(6, 2, 6, 1, 264, 47132, 'Phase 4', 'Death Knight', 'Unholy', 'Legs', 'Alliance', 'Legguards of Ascension'), +(6, 2, 6, 2, 264, 47465, 'Phase 4', 'Death Knight', 'Unholy', 'Legs', 'Horde', 'Legplates of Ascension'), +(6, 2, 7, 0, 264, 45599, 'Phase 4', 'Death Knight', 'Unholy', 'Feet', 'Both', 'Sabatons of Lifeless Night'), +(6, 2, 8, 0, 264, 45663, 'Phase 4', 'Death Knight', 'Unholy', 'Wrists', 'Both', 'Armbands of Bedlam'), +(6, 2, 9, 1, 264, 48489, 'Phase 4', 'Death Knight', 'Unholy', 'Hands', 'Alliance', 'Thassarian''s Gauntlets of Triumph'), +(6, 2, 9, 2, 264, 48492, 'Phase 4', 'Death Knight', 'Unholy', 'Hands', 'Horde', 'Koltira''s Gauntlets of Triumph'), +(6, 2, 10, 1, 264, 47920, 'Phase 4', 'Death Knight', 'Unholy', 'Finger1', 'Alliance', 'Carnivorous Band'), +(6, 2, 10, 2, 264, 47993, 'Phase 4', 'Death Knight', 'Unholy', 'Finger1', 'Horde', 'Gormok''s Band'), +(6, 2, 12, 1, 264, 47131, 'Phase 4', 'Death Knight', 'Unholy', 'Trinket1', 'Alliance', 'Death''s Verdict'), +(6, 2, 12, 2, 264, 47464, 'Phase 4', 'Death Knight', 'Unholy', 'Trinket1', 'Horde', 'Death''s Choice'), +(6, 2, 14, 1, 264, 47547, 'Phase 4', 'Death Knight', 'Unholy', 'Back', 'Alliance', 'Varian''s Furor'), +(6, 2, 14, 2, 264, 47548, 'Phase 4', 'Death Knight', 'Unholy', 'Back', 'Horde', 'Garrosh''s Rage'), +(6, 2, 17, 0, 264, 47673, 'Phase 4', 'Death Knight', 'Unholy', 'Ranged', 'Both', 'Sigil of Virulence'); + +-- ilvl 290 (Phase 5) +INSERT INTO `playerbots_bis_gear` VALUES +(6, 2, 0, 0, 290, 51312, 'Phase 5', 'DeathKnight', 'Unholy', 'Head', 'Both', 'Sanctified Scourgelord Helmet'), +(6, 2, 1, 0, 290, 54581, 'Phase 5', 'DeathKnight', 'Unholy', 'Neck', 'Both', 'Penumbra Pendant'), +(6, 2, 2, 0, 290, 51314, 'Phase 5', 'DeathKnight', 'Unholy', 'Shoulders', 'Both', 'Sanctified Scourgelord Shoulderplates'), +(6, 2, 4, 0, 290, 51310, 'Phase 5', 'DeathKnight', 'Unholy', 'Chest', 'Both', 'Sanctified Scourgelord Battleplate'), +(6, 2, 5, 0, 290, 50620, 'Phase 5', 'DeathKnight', 'Unholy', 'Waist', 'Both', 'Coldwraith Links'), +(6, 2, 6, 0, 290, 51313, 'Phase 5', 'DeathKnight', 'Unholy', 'Legs', 'Both', 'Sanctified Scourgelord Legplates'), +(6, 2, 7, 0, 290, 54578, 'Phase 5', 'DeathKnight', 'Unholy', 'Feet', 'Both', 'Apocalypse''s Advance'), +(6, 2, 8, 0, 290, 50670, 'Phase 5', 'DeathKnight', 'Unholy', 'Wrists', 'Both', 'Toskk''s Maximized Wristguards'), +(6, 2, 9, 0, 290, 50690, 'Phase 5', 'DeathKnight', 'Unholy', 'Hands', 'Both', 'Fleshrending Gauntlets'), +(6, 2, 10, 0, 290, 50693, 'Phase 5', 'DeathKnight', 'Unholy', 'Finger1', 'Both', 'Might of Blight'), +(6, 2, 11, 0, 290, 52572, 'Phase 5', 'DeathKnight', 'Unholy', 'Finger2', 'Both', 'Ashen Band of Endless Might'), +(6, 2, 12, 0, 290, 54590, 'Phase 5', 'DeathKnight', 'Unholy', 'Trinket1', 'Both', 'Sharpened Twilight Scale'), +(6, 2, 13, 0, 290, 50363, 'Phase 5', 'DeathKnight', 'Unholy', 'Trinket2', 'Both', 'Deathbringer''s Will'), +(6, 2, 14, 0, 290, 50677, 'Phase 5', 'DeathKnight', 'Unholy', 'Back', 'Both', 'Winding Sheet'), +(6, 2, 15, 0, 290, 49623, 'Phase 5', 'DeathKnight', 'Unholy', 'MainHand', 'Both', 'Shadowmourne'), +(6, 2, 17, 0, 290, 50459, 'Phase 5', 'DeathKnight', 'Unholy', 'Ranged', 'Both', 'Sigil of the Hanged Man'); + + +-- ============================================================ +-- Shaman (7) +-- ============================================================ +-- Elemental (tab 0) +-- ilvl 66 (Phase 1 (Pre-Raid)) +INSERT INTO `playerbots_bis_gear` VALUES +(7, 0, 0, 0, 66, 10504, 'Phase 1 (Pre-Raid)', 'Shaman', 'Elemental', 'Head', 'Both', 'Green Lens'), +(7, 0, 1, 0, 66, 12103, 'Phase 1 (Pre-Raid)', 'Shaman', 'Elemental', 'Neck', 'Both', 'Star of Mystaria'), +(7, 0, 2, 0, 66, 13013, 'Phase 1 (Pre-Raid)', 'Shaman', 'Elemental', 'Shoulders', 'Both', 'Elder Wizard''s Mantle'), +(7, 0, 4, 0, 66, 11924, 'Phase 1 (Pre-Raid)', 'Shaman', 'Elemental', 'Chest', 'Both', 'Robes of the Royal Crown'), +(7, 0, 5, 0, 66, 11662, 'Phase 1 (Pre-Raid)', 'Shaman', 'Elemental', 'Waist', 'Both', 'Ban''thok Sash'), +(7, 0, 6, 0, 66, 13170, 'Phase 1 (Pre-Raid)', 'Shaman', 'Elemental', 'Legs', 'Both', 'Skyshroud Leggings'), +(7, 0, 7, 0, 66, 13954, 'Phase 1 (Pre-Raid)', 'Shaman', 'Elemental', 'Feet', 'Both', 'Verdant Footpads'), +(7, 0, 8, 0, 66, 11765, 'Phase 1 (Pre-Raid)', 'Shaman', 'Elemental', 'Wrists', 'Both', 'Pyremail Wristguards'), +(7, 0, 9, 0, 66, 13253, 'Phase 1 (Pre-Raid)', 'Shaman', 'Elemental', 'Hands', 'Both', 'Hands of Power'), +(7, 0, 10, 0, 66, 12545, 'Phase 1 (Pre-Raid)', 'Shaman', 'Elemental', 'Finger1', 'Both', 'Eye of Orgrimmar'), +(7, 0, 11, 0, 66, 13001, 'Phase 1 (Pre-Raid)', 'Shaman', 'Elemental', 'Finger2', 'Both', 'Maiden''s Circle'), +(7, 0, 12, 0, 66, 12930, 'Phase 1 (Pre-Raid)', 'Shaman', 'Elemental', 'Trinket1', 'Both', 'Briarwood Reed'), +(7, 0, 13, 0, 66, 13968, 'Phase 1 (Pre-Raid)', 'Shaman', 'Elemental', 'Trinket2', 'Both', 'Eye of the Beast'), +(7, 0, 14, 0, 66, 15421, 'Phase 1 (Pre-Raid)', 'Shaman', 'Elemental', 'Back', 'Both', 'Shroud of the Exile'), +(7, 0, 15, 0, 66, 13964, 'Phase 1 (Pre-Raid)', 'Shaman', 'Elemental', 'MainHand', 'Both', 'Witchblade'), +(7, 0, 16, 0, 66, 11904, 'Phase 1 (Pre-Raid)', 'Shaman', 'Elemental', 'OffHand', 'Both', 'Spirit of Aquementas'); + +-- ilvl 76 (Phase 2 (Pre-Raid)) +INSERT INTO `playerbots_bis_gear` VALUES +(7, 0, 0, 0, 76, 10504, 'Phase 2 (Pre-Raid)', 'Shaman', 'Elemental', 'Head', 'Both', 'Green Lens'), +(7, 0, 1, 0, 76, 12103, 'Phase 2 (Pre-Raid)', 'Shaman', 'Elemental', 'Neck', 'Both', 'Star of Mystaria'), +(7, 0, 2, 0, 76, 23260, 'Phase 2 (Pre-Raid)', 'Shaman', 'Elemental', 'Shoulders', 'Both', 'Champion''s Mail Pauldrons'), +(7, 0, 4, 0, 76, 18385, 'Phase 2 (Pre-Raid)', 'Shaman', 'Elemental', 'Chest', 'Both', 'Robe of Everlasting Night'), +(7, 0, 5, 0, 76, 11662, 'Phase 2 (Pre-Raid)', 'Shaman', 'Elemental', 'Waist', 'Both', 'Ban''thok Sash'), +(7, 0, 6, 0, 76, 13170, 'Phase 2 (Pre-Raid)', 'Shaman', 'Elemental', 'Legs', 'Both', 'Skyshroud Leggings'), +(7, 0, 7, 0, 76, 18322, 'Phase 2 (Pre-Raid)', 'Shaman', 'Elemental', 'Feet', 'Both', 'Waterspout Boots'), +(7, 0, 8, 0, 76, 11765, 'Phase 2 (Pre-Raid)', 'Shaman', 'Elemental', 'Wrists', 'Both', 'Pyremail Wristguards'), +(7, 0, 9, 0, 76, 13253, 'Phase 2 (Pre-Raid)', 'Shaman', 'Elemental', 'Hands', 'Both', 'Hands of Power'), +(7, 0, 10, 0, 76, 12545, 'Phase 2 (Pre-Raid)', 'Shaman', 'Elemental', 'Finger1', 'Both', 'Eye of Orgrimmar'), +(7, 0, 11, 0, 76, 13001, 'Phase 2 (Pre-Raid)', 'Shaman', 'Elemental', 'Finger2', 'Both', 'Maiden''s Circle'), +(7, 0, 12, 0, 76, 12930, 'Phase 2 (Pre-Raid)', 'Shaman', 'Elemental', 'Trinket1', 'Both', 'Briarwood Reed'), +(7, 0, 13, 0, 76, 13968, 'Phase 2 (Pre-Raid)', 'Shaman', 'Elemental', 'Trinket2', 'Both', 'Eye of the Beast'), +(7, 0, 14, 0, 76, 18496, 'Phase 2 (Pre-Raid)', 'Shaman', 'Elemental', 'Back', 'Both', 'Heliotrope Cloak'), +(7, 0, 15, 0, 76, 18534, 'Phase 2 (Pre-Raid)', 'Shaman', 'Elemental', 'MainHand', 'Both', 'Rod of the Ogre Magi'); + +-- ilvl 78 (Phase 2) +INSERT INTO `playerbots_bis_gear` VALUES +(7, 0, 0, 0, 78, 10504, 'Phase 2', 'Shaman', 'Elemental', 'Head', 'Both', 'Green Lens'), +(7, 0, 1, 0, 78, 18814, 'Phase 2', 'Shaman', 'Elemental', 'Neck', 'Both', 'Choker of the Fire Lord'), +(7, 0, 2, 0, 78, 18829, 'Phase 2', 'Shaman', 'Elemental', 'Shoulders', 'Both', 'Deep Earth Spaulders'), +(7, 0, 4, 0, 78, 19145, 'Phase 2', 'Shaman', 'Elemental', 'Chest', 'Both', 'Robe of Volatile Power'), +(7, 0, 5, 0, 78, 19136, 'Phase 2', 'Shaman', 'Elemental', 'Waist', 'Both', 'Mana Igniting Cord'), +(7, 0, 6, 0, 78, 16946, 'Phase 2', 'Shaman', 'Elemental', 'Legs', 'Both', 'Legplates of Ten Storms'), +(7, 0, 7, 0, 78, 18322, 'Phase 2', 'Shaman', 'Elemental', 'Feet', 'Both', 'Waterspout Boots'), +(7, 0, 8, 0, 78, 11765, 'Phase 2', 'Shaman', 'Elemental', 'Wrists', 'Both', 'Pyremail Wristguards'), +(7, 0, 9, 0, 78, 16839, 'Phase 2', 'Shaman', 'Elemental', 'Hands', 'Both', 'Earthfury Gauntlets'), +(7, 0, 10, 0, 78, 19147, 'Phase 2', 'Shaman', 'Elemental', 'Finger1', 'Both', 'Ring of Spell Power'), +(7, 0, 11, 0, 78, 19147, 'Phase 2', 'Shaman', 'Elemental', 'Finger2', 'Both', 'Ring of Spell Power'), +(7, 0, 12, 0, 78, 12930, 'Phase 2', 'Shaman', 'Elemental', 'Trinket1', 'Both', 'Briarwood Reed'), +(7, 0, 13, 0, 78, 18820, 'Phase 2', 'Shaman', 'Elemental', 'Trinket2', 'Both', 'Talisman of Ephemeral Power'), +(7, 0, 14, 0, 78, 18496, 'Phase 2', 'Shaman', 'Elemental', 'Back', 'Both', 'Heliotrope Cloak'), +(7, 0, 15, 0, 78, 18842, 'Phase 2', 'Shaman', 'Elemental', 'MainHand', 'Both', 'Staff of Dominance'); + +-- ilvl 83 (Phase 3) +INSERT INTO `playerbots_bis_gear` VALUES +(7, 0, 0, 0, 83, 19375, 'Phase 3', 'Shaman', 'Elemental', 'Head', 'Both', 'Mish''undare, Circlet of the Mind Flayer'), +(7, 0, 1, 0, 83, 18814, 'Phase 3', 'Shaman', 'Elemental', 'Neck', 'Both', 'Choker of the Fire Lord'), +(7, 0, 2, 0, 83, 18829, 'Phase 3', 'Shaman', 'Elemental', 'Shoulders', 'Both', 'Deep Earth Spaulders'), +(7, 0, 4, 0, 83, 19145, 'Phase 3', 'Shaman', 'Elemental', 'Chest', 'Both', 'Robe of Volatile Power'), +(7, 0, 5, 0, 83, 19400, 'Phase 3', 'Shaman', 'Elemental', 'Waist', 'Both', 'Firemaw''s Clutch'), +(7, 0, 6, 0, 83, 16946, 'Phase 3', 'Shaman', 'Elemental', 'Legs', 'Both', 'Legplates of Ten Storms'), +(7, 0, 7, 0, 83, 18322, 'Phase 3', 'Shaman', 'Elemental', 'Feet', 'Both', 'Waterspout Boots'), +(7, 0, 8, 0, 83, 19374, 'Phase 3', 'Shaman', 'Elemental', 'Wrists', 'Both', 'Bracers of Arcane Accuracy'), +(7, 0, 9, 0, 83, 16839, 'Phase 3', 'Shaman', 'Elemental', 'Hands', 'Both', 'Earthfury Gauntlets'), +(7, 0, 10, 0, 83, 19397, 'Phase 3', 'Shaman', 'Elemental', 'Finger1', 'Both', 'Ring of Blackrock'), +(7, 0, 12, 0, 83, 19379, 'Phase 3', 'Shaman', 'Elemental', 'Trinket1', 'Both', 'Neltharion''s Tear'), +(7, 0, 13, 0, 83, 19344, 'Phase 3', 'Shaman', 'Elemental', 'Trinket2', 'Both', 'Natural Alignment Crystal'), +(7, 0, 14, 0, 83, 19378, 'Phase 3', 'Shaman', 'Elemental', 'Back', 'Both', 'Cloak of the Brood Lord'), +(7, 0, 15, 0, 83, 19360, 'Phase 3', 'Shaman', 'Elemental', 'MainHand', 'Both', 'Lok''amir il Romathis'), +(7, 0, 16, 0, 83, 19366, 'Phase 3', 'Shaman', 'Elemental', 'OffHand', 'Both', 'Master Dragonslayer''s Orb'); + +-- ilvl 88 (Phase 5) +INSERT INTO `playerbots_bis_gear` VALUES +(7, 0, 0, 0, 88, 19375, 'Phase 5', 'Shaman', 'Elemental', 'Head', 'Both', 'Mish''undare, Circlet of the Mind Flayer'), +(7, 0, 1, 0, 88, 21608, 'Phase 5', 'Shaman', 'Elemental', 'Neck', 'Both', 'Amulet of Vek''nilash'), +(7, 0, 2, 0, 88, 21376, 'Phase 5', 'Shaman', 'Elemental', 'Shoulders', 'Both', 'Stormcaller''s Pauldrons'), +(7, 0, 4, 0, 88, 21671, 'Phase 5', 'Shaman', 'Elemental', 'Chest', 'Both', 'Robes of the Battleguard'), +(7, 0, 5, 0, 88, 22730, 'Phase 5', 'Shaman', 'Elemental', 'Waist', 'Both', 'Eyestalk Waist Cord'), +(7, 0, 6, 0, 88, 21375, 'Phase 5', 'Shaman', 'Elemental', 'Legs', 'Both', 'Stormcaller''s Leggings'), +(7, 0, 7, 0, 88, 21373, 'Phase 5', 'Shaman', 'Elemental', 'Feet', 'Both', 'Stormcaller''s Footguards'), +(7, 0, 8, 0, 88, 21464, 'Phase 5', 'Shaman', 'Elemental', 'Wrists', 'Both', 'Shackles of the Unscarred'), +(7, 0, 9, 0, 88, 21585, 'Phase 5', 'Shaman', 'Elemental', 'Hands', 'Both', 'Dark Storm Gauntlets'), +(7, 0, 10, 0, 88, 21707, 'Phase 5', 'Shaman', 'Elemental', 'Finger1', 'Both', 'Ring of Swarming Thought'), +(7, 0, 11, 0, 88, 21709, 'Phase 5', 'Shaman', 'Elemental', 'Finger2', 'Both', 'Ring of the Fallen God'), +(7, 0, 12, 0, 88, 19379, 'Phase 5', 'Shaman', 'Elemental', 'Trinket1', 'Both', 'Neltharion''s Tear'), +(7, 0, 13, 0, 88, 19344, 'Phase 5', 'Shaman', 'Elemental', 'Trinket2', 'Both', 'Natural Alignment Crystal'), +(7, 0, 14, 0, 88, 22731, 'Phase 5', 'Shaman', 'Elemental', 'Back', 'Both', 'Cloak of the Devoured'), +(7, 0, 15, 0, 88, 19360, 'Phase 5', 'Shaman', 'Elemental', 'MainHand', 'Both', 'Lok''amir il Romathis'), +(7, 0, 16, 0, 88, 19366, 'Phase 5', 'Shaman', 'Elemental', 'OffHand', 'Both', 'Master Dragonslayer''s Orb'), +(7, 0, 17, 0, 88, 23199, 'Phase 5', 'Shaman', 'Elemental', 'Ranged', 'Both', 'Totem of the Storm'); + +-- ilvl 92 (Phase 6) +INSERT INTO `playerbots_bis_gear` VALUES +(7, 0, 0, 0, 92, 19375, 'Phase 6', 'Shaman', 'Elemental', 'Head', 'Both', 'Mish''undare, Circlet of the Mind Flayer'), +(7, 0, 1, 0, 92, 22943, 'Phase 6', 'Shaman', 'Elemental', 'Neck', 'Both', 'Malice Stone Pendant'), +(7, 0, 2, 0, 92, 21376, 'Phase 6', 'Shaman', 'Elemental', 'Shoulders', 'Both', 'Stormcaller''s Pauldrons'), +(7, 0, 4, 0, 92, 21671, 'Phase 6', 'Shaman', 'Elemental', 'Chest', 'Both', 'Robes of the Battleguard'), +(7, 0, 5, 0, 92, 22730, 'Phase 6', 'Shaman', 'Elemental', 'Waist', 'Both', 'Eyestalk Waist Cord'), +(7, 0, 6, 0, 92, 21375, 'Phase 6', 'Shaman', 'Elemental', 'Legs', 'Both', 'Stormcaller''s Leggings'), +(7, 0, 7, 0, 92, 21373, 'Phase 6', 'Shaman', 'Elemental', 'Feet', 'Both', 'Stormcaller''s Footguards'), +(7, 0, 8, 0, 92, 21464, 'Phase 6', 'Shaman', 'Elemental', 'Wrists', 'Both', 'Shackles of the Unscarred'), +(7, 0, 9, 0, 92, 21585, 'Phase 6', 'Shaman', 'Elemental', 'Hands', 'Both', 'Dark Storm Gauntlets'), +(7, 0, 10, 0, 92, 21707, 'Phase 6', 'Shaman', 'Elemental', 'Finger1', 'Both', 'Ring of Swarming Thought'), +(7, 0, 11, 0, 92, 21709, 'Phase 6', 'Shaman', 'Elemental', 'Finger2', 'Both', 'Ring of the Fallen God'), +(7, 0, 12, 0, 92, 19379, 'Phase 6', 'Shaman', 'Elemental', 'Trinket1', 'Both', 'Neltharion''s Tear'), +(7, 0, 13, 0, 92, 23046, 'Phase 6', 'Shaman', 'Elemental', 'Trinket2', 'Both', 'The Restrained Essence of Sapphiron'), +(7, 0, 14, 0, 92, 23050, 'Phase 6', 'Shaman', 'Elemental', 'Back', 'Both', 'Cloak of the Necropolis'), +(7, 0, 15, 0, 92, 22988, 'Phase 6', 'Shaman', 'Elemental', 'MainHand', 'Both', 'The End of Dreams'), +(7, 0, 16, 0, 92, 23049, 'Phase 6', 'Shaman', 'Elemental', 'OffHand', 'Both', 'Sapphiron''s Left Eye'), +(7, 0, 17, 0, 92, 23199, 'Phase 6', 'Shaman', 'Elemental', 'Ranged', 'Both', 'Totem of the Storm'); + +-- ilvl 120 (Pre-Raid) +INSERT INTO `playerbots_bis_gear` VALUES +(7, 0, 0, 0, 120, 32086, 'Pre-Raid', 'Shaman', 'Elemental', 'Head', 'Both', 'Storm Master''s Helmet'), +(7, 0, 1, 0, 120, 31692, 'Pre-Raid', 'Shaman', 'Elemental', 'Neck', 'Both', 'Natasha''s Ember Necklace'), +(7, 0, 2, 0, 120, 27796, 'Pre-Raid', 'Shaman', 'Elemental', 'Shoulders', 'Both', 'Mana-Etched Spaulders'), +(7, 0, 4, 0, 120, 29519, 'Pre-Raid', 'Shaman', 'Elemental', 'Chest', 'Both', 'Netherstrike Breastplate'), +(7, 0, 5, 0, 120, 29520, 'Pre-Raid', 'Shaman', 'Elemental', 'Waist', 'Both', 'Netherstrike Belt'), +(7, 0, 6, 0, 120, 24262, 'Pre-Raid', 'Shaman', 'Elemental', 'Legs', 'Both', 'Spellstrike Pants'), +(7, 0, 7, 0, 120, 28406, 'Pre-Raid', 'Shaman', 'Elemental', 'Feet', 'Both', 'Sigil-Laced Boots'), +(7, 0, 8, 0, 120, 29521, 'Pre-Raid', 'Shaman', 'Elemental', 'Wrists', 'Both', 'Netherstrike Bracers'), +(7, 0, 9, 0, 120, 27465, 'Pre-Raid', 'Shaman', 'Elemental', 'Hands', 'Both', 'Mana-Etched Gloves'), +(7, 0, 10, 0, 120, 29367, 'Pre-Raid', 'Shaman', 'Elemental', 'Finger1', 'Both', 'Ring of Cryptic Dreams'), +(7, 0, 11, 0, 120, 29126, 'Pre-Raid', 'Shaman', 'Elemental', 'Finger2', 'Both', 'Seer''s Signet'), +(7, 0, 12, 0, 120, 29370, 'Pre-Raid', 'Shaman', 'Elemental', 'Trinket1', 'Both', 'Icon of the Silver Crescent'), +(7, 0, 13, 0, 120, 27683, 'Pre-Raid', 'Shaman', 'Elemental', 'Trinket2', 'Both', 'Quagmirran''s Eye'), +(7, 0, 14, 0, 120, 29369, 'Pre-Raid', 'Shaman', 'Elemental', 'Back', 'Both', 'Shawl of Shifting Probabilities'), +(7, 0, 15, 0, 120, 23554, 'Pre-Raid', 'Shaman', 'Elemental', 'MainHand', 'Both', 'Eternium Runed Blade'), +(7, 0, 16, 0, 120, 29268, 'Pre-Raid', 'Shaman', 'Elemental', 'OffHand', 'Both', 'Mazthoril Honor Shield'), +(7, 0, 17, 0, 120, 28248, 'Pre-Raid', 'Shaman', 'Elemental', 'Ranged', 'Both', 'Totem of the Void'); + +-- ilvl 125 (Phase 1) +INSERT INTO `playerbots_bis_gear` VALUES +(7, 0, 0, 0, 125, 29035, 'Phase 1', 'Shaman', 'Elemental', 'Head', 'Both', 'Cyclone Faceguard'), +(7, 0, 1, 0, 125, 28762, 'Phase 1', 'Shaman', 'Elemental', 'Neck', 'Both', 'Adornment of Stolen Souls'), +(7, 0, 2, 0, 125, 29037, 'Phase 1', 'Shaman', 'Elemental', 'Shoulders', 'Both', 'Cyclone Shoulderguards'), +(7, 0, 4, 0, 125, 29519, 'Phase 1', 'Shaman', 'Elemental', 'Chest', 'Both', 'Netherstrike Breastplate'), +(7, 0, 5, 0, 125, 29520, 'Phase 1', 'Shaman', 'Elemental', 'Waist', 'Both', 'Netherstrike Belt'), +(7, 0, 6, 0, 125, 30734, 'Phase 1', 'Shaman', 'Elemental', 'Legs', 'Both', 'Leggings of the Seventh Circle'), +(7, 0, 7, 0, 125, 28517, 'Phase 1', 'Shaman', 'Elemental', 'Feet', 'Both', 'Boots of Foretelling'), +(7, 0, 8, 0, 125, 29521, 'Phase 1', 'Shaman', 'Elemental', 'Wrists', 'Both', 'Netherstrike Bracers'), +(7, 0, 9, 0, 125, 30725, 'Phase 1', 'Shaman', 'Elemental', 'Hands', 'Both', 'Anger-Spark Gloves'), +(7, 0, 10, 0, 125, 28753, 'Phase 1', 'Shaman', 'Elemental', 'Finger1', 'Both', 'Ring of Recurrence'), +(7, 0, 11, 0, 125, 30667, 'Phase 1', 'Shaman', 'Elemental', 'Finger2', 'Both', 'Ring of Unrelenting Storms'), +(7, 0, 12, 0, 125, 29370, 'Phase 1', 'Shaman', 'Elemental', 'Trinket1', 'Both', 'Icon of the Silver Crescent'), +(7, 0, 13, 0, 125, 28785, 'Phase 1', 'Shaman', 'Elemental', 'Trinket2', 'Both', 'The Lightning Capacitor'), +(7, 0, 14, 0, 125, 30735, 'Phase 1', 'Shaman', 'Elemental', 'Back', 'Both', 'Ancient Spellcloak of the Highborne'), +(7, 0, 16, 0, 125, 29268, 'Phase 1', 'Shaman', 'Elemental', 'OffHand', 'Both', 'Mazthoril Honor Shield'), +(7, 0, 17, 0, 125, 28248, 'Phase 1', 'Shaman', 'Elemental', 'Ranged', 'Both', 'Totem of the Void'); + +-- ilvl 141 (Phase 2) +INSERT INTO `playerbots_bis_gear` VALUES +(7, 0, 0, 0, 141, 29035, 'Phase 2', 'Shaman', 'Elemental', 'Head', 'Both', 'Cyclone Faceguard'), +(7, 0, 1, 0, 141, 30015, 'Phase 2', 'Shaman', 'Elemental', 'Neck', 'Both', 'The Sun King''s Talisman'), +(7, 0, 2, 0, 141, 29037, 'Phase 2', 'Shaman', 'Elemental', 'Shoulders', 'Both', 'Cyclone Shoulderguards'), +(7, 0, 4, 0, 141, 30107, 'Phase 2', 'Shaman', 'Elemental', 'Chest', 'Both', 'Vestments of the Sea-Witch'), +(7, 0, 5, 0, 141, 30038, 'Phase 2', 'Shaman', 'Elemental', 'Waist', 'Both', 'Belt of Blasting'), +(7, 0, 6, 0, 141, 30734, 'Phase 2', 'Shaman', 'Elemental', 'Legs', 'Both', 'Leggings of the Seventh Circle'), +(7, 0, 7, 0, 141, 30067, 'Phase 2', 'Shaman', 'Elemental', 'Feet', 'Both', 'Velvet Boots of the Guardian'), +(7, 0, 8, 0, 141, 29918, 'Phase 2', 'Shaman', 'Elemental', 'Wrists', 'Both', 'Mindstorm Wristbands'), +(7, 0, 9, 0, 141, 28780, 'Phase 2', 'Shaman', 'Elemental', 'Hands', 'Both', 'Soul-Eater''s Handwraps'), +(7, 0, 10, 0, 141, 30109, 'Phase 2', 'Shaman', 'Elemental', 'Finger1', 'Both', 'Ring of Endless Coils'), +(7, 0, 11, 0, 141, 30667, 'Phase 2', 'Shaman', 'Elemental', 'Finger2', 'Both', 'Ring of Unrelenting Storms'), +(7, 0, 12, 0, 141, 29370, 'Phase 2', 'Shaman', 'Elemental', 'Trinket1', 'Both', 'Icon of the Silver Crescent'), +(7, 0, 13, 0, 141, 28785, 'Phase 2', 'Shaman', 'Elemental', 'Trinket2', 'Both', 'The Lightning Capacitor'), +(7, 0, 14, 0, 141, 30735, 'Phase 2', 'Shaman', 'Elemental', 'Back', 'Both', 'Ancient Spellcloak of the Highborne'), +(7, 0, 15, 0, 141, 29988, 'Phase 2', 'Shaman', 'Elemental', 'MainHand', 'Both', 'The Nexus Key'), +(7, 0, 17, 0, 141, 28248, 'Phase 2', 'Shaman', 'Elemental', 'Ranged', 'Both', 'Totem of the Void'); + +-- ilvl 156 (Phase 3) +INSERT INTO `playerbots_bis_gear` VALUES +(7, 0, 0, 0, 156, 31014, 'Phase 3', 'Shaman', 'Elemental', 'Head', 'Both', 'Skyshatter Headguard'), +(7, 0, 1, 0, 156, 32349, 'Phase 3', 'Shaman', 'Elemental', 'Neck', 'Both', 'Translucent Spellthread Necklace'), +(7, 0, 2, 0, 156, 31023, 'Phase 3', 'Shaman', 'Elemental', 'Shoulders', 'Both', 'Skyshatter Mantle'), +(7, 0, 4, 0, 156, 31017, 'Phase 3', 'Shaman', 'Elemental', 'Chest', 'Both', 'Skyshatter Breastplate'), +(7, 0, 5, 0, 156, 32276, 'Phase 3', 'Shaman', 'Elemental', 'Waist', 'Both', 'Flashfire Girdle'), +(7, 0, 6, 0, 156, 30916, 'Phase 3', 'Shaman', 'Elemental', 'Legs', 'Both', 'Leggings of Channeled Elements'), +(7, 0, 7, 0, 156, 32239, 'Phase 3', 'Shaman', 'Elemental', 'Feet', 'Both', 'Slippers of the Seacaller'), +(7, 0, 8, 0, 156, 32586, 'Phase 3', 'Shaman', 'Elemental', 'Wrists', 'Both', 'Bracers of Nimble Thought'), +(7, 0, 9, 0, 156, 31008, 'Phase 3', 'Shaman', 'Elemental', 'Hands', 'Both', 'Skyshatter Gauntlets'), +(7, 0, 10, 0, 156, 32527, 'Phase 3', 'Shaman', 'Elemental', 'Finger1', 'Both', 'Ring of Ancient Knowledge'), +(7, 0, 11, 0, 156, 32527, 'Phase 3', 'Shaman', 'Elemental', 'Finger2', 'Both', 'Ring of Ancient Knowledge'), +(7, 0, 12, 0, 156, 28785, 'Phase 3', 'Shaman', 'Elemental', 'Trinket1', 'Both', 'The Lightning Capacitor'), +(7, 0, 13, 0, 156, 32483, 'Phase 3', 'Shaman', 'Elemental', 'Trinket2', 'Both', 'The Skull of Gul''dan'), +(7, 0, 14, 0, 156, 32524, 'Phase 3', 'Shaman', 'Elemental', 'Back', 'Both', 'Shroud of the Highborne'), +(7, 0, 15, 0, 156, 32374, 'Phase 3', 'Shaman', 'Elemental', 'MainHand', 'Both', 'Zhar''doom, Greatstaff of the Devourer'), +(7, 0, 17, 0, 156, 32330, 'Phase 3', 'Shaman', 'Elemental', 'Ranged', 'Both', 'Totem of Ancestral Guidance'); + +-- ilvl 164 (Phase 4) +INSERT INTO `playerbots_bis_gear` VALUES +(7, 0, 0, 0, 164, 31014, 'Phase 4', 'Shaman', 'Elemental', 'Head', 'Both', 'Skyshatter Headguard'), +(7, 0, 1, 0, 164, 33281, 'Phase 4', 'Shaman', 'Elemental', 'Neck', 'Both', 'Brooch of Nature''s Mercy'), +(7, 0, 2, 0, 164, 31023, 'Phase 4', 'Shaman', 'Elemental', 'Shoulders', 'Both', 'Skyshatter Mantle'), +(7, 0, 4, 0, 164, 31017, 'Phase 4', 'Shaman', 'Elemental', 'Chest', 'Both', 'Skyshatter Breastplate'), +(7, 0, 5, 0, 164, 32276, 'Phase 4', 'Shaman', 'Elemental', 'Waist', 'Both', 'Flashfire Girdle'), +(7, 0, 6, 0, 164, 33584, 'Phase 4', 'Shaman', 'Elemental', 'Legs', 'Both', 'Pantaloons of Arcane Annihilation'), +(7, 0, 7, 0, 164, 33357, 'Phase 4', 'Shaman', 'Elemental', 'Feet', 'Both', 'Footpads of Madness'), +(7, 0, 8, 0, 164, 32586, 'Phase 4', 'Shaman', 'Elemental', 'Wrists', 'Both', 'Bracers of Nimble Thought'), +(7, 0, 9, 0, 164, 31008, 'Phase 4', 'Shaman', 'Elemental', 'Hands', 'Both', 'Skyshatter Gauntlets'), +(7, 0, 10, 0, 164, 32527, 'Phase 4', 'Shaman', 'Elemental', 'Finger1', 'Both', 'Ring of Ancient Knowledge'), +(7, 0, 11, 0, 164, 32527, 'Phase 4', 'Shaman', 'Elemental', 'Finger2', 'Both', 'Ring of Ancient Knowledge'), +(7, 0, 12, 0, 164, 33829, 'Phase 4', 'Shaman', 'Elemental', 'Trinket1', 'Both', 'Hex Shrunken Head'), +(7, 0, 13, 0, 164, 32483, 'Phase 4', 'Shaman', 'Elemental', 'Trinket2', 'Both', 'The Skull of Gul''dan'), +(7, 0, 14, 0, 164, 32524, 'Phase 4', 'Shaman', 'Elemental', 'Back', 'Both', 'Shroud of the Highborne'), +(7, 0, 15, 0, 164, 32374, 'Phase 4', 'Shaman', 'Elemental', 'MainHand', 'Both', 'Zhar''doom, Greatstaff of the Devourer'), +(7, 0, 16, 0, 164, 30909, 'Phase 5', 'Shaman', 'Elemental', 'OffHand', 'Both', 'Antonidas''s Aegis of Rapt Concentration'), +(7, 0, 17, 0, 164, 32330, 'Phase 4', 'Shaman', 'Elemental', 'Ranged', 'Both', 'Totem of Ancestral Guidance'); + +-- ilvl 200 (Pre-Raid) +INSERT INTO `playerbots_bis_gear` VALUES +(7, 0, 0, 0, 200, 42555, 'Pre-Raid', 'Shaman', 'Elemental', 'Head', 'Both', 'Electroflux Sight Enhancers'), +(7, 0, 1, 0, 200, 37595, 'Pre-Raid', 'Shaman', 'Elemental', 'Neck', 'Both', 'Necklace of Taldaram'), +(7, 0, 2, 0, 200, 37398, 'Pre-Raid', 'Shaman', 'Elemental', 'Shoulders', 'Both', 'Mantle of Discarded Ways'), +(7, 0, 4, 0, 200, 42101, 'Pre-Raid', 'Shaman', 'Elemental', 'Chest', 'Both', 'Ebonweave Robe'), +(7, 0, 5, 0, 200, 37855, 'Pre-Raid', 'Shaman', 'Elemental', 'Waist', 'Both', 'Mail Girdle of the Audient Earth'), +(7, 0, 6, 0, 200, 37695, 'Pre-Raid', 'Shaman', 'Elemental', 'Legs', 'Both', 'Legguards of Nature''s Power'), +(7, 0, 7, 0, 200, 43469, 'Pre-Raid', 'Shaman', 'Elemental', 'Feet', 'Both', 'Revenant''s Treads'), +(7, 0, 8, 0, 200, 37788, 'Pre-Raid', 'Shaman', 'Elemental', 'Wrists', 'Both', 'Limb Regeneration Bracers'), +(7, 0, 9, 0, 200, 37623, 'Pre-Raid', 'Shaman', 'Elemental', 'Hands', 'Both', 'Fiery Obelisk Handguards'), +(7, 0, 10, 0, 200, 37192, 'Pre-Raid', 'Shaman', 'Elemental', 'Finger1', 'Both', 'Annhylde''s Ring'), +(7, 0, 12, 0, 200, 40682, 'Pre-Raid', 'Shaman', 'Elemental', 'Trinket1', 'Both', 'Sundial of the Exiled'), +(7, 0, 14, 0, 200, 41610, 'Pre-Raid', 'Shaman', 'Elemental', 'Back', 'Both', 'Deathchill Cloak'), +(7, 0, 15, 0, 200, 41384, 'Pre-Raid', 'Shaman', 'Elemental', 'MainHand', 'Both', 'Titansteel Guardian'), +(7, 0, 17, 0, 200, 40708, 'Pre-Raid', 'Shaman', 'Elemental', 'Ranged', 'Both', 'Totem of the Elemental Plane'); + +-- ilvl 224 (Phase 1) +INSERT INTO `playerbots_bis_gear` VALUES +(7, 0, 0, 0, 224, 40516, 'Phase 1', 'Shaman', 'Elemental', 'Head', 'Both', 'Valorous Earthshatter Helm'), +(7, 0, 1, 0, 224, 44661, 'Phase 1', 'Shaman', 'Elemental', 'Neck', 'Both', 'Wyrmrest Necklace of Power'), +(7, 0, 2, 0, 224, 40518, 'Phase 1', 'Shaman', 'Elemental', 'Shoulders', 'Both', 'Valorous Earthshatter Shoulderpads'), +(7, 0, 4, 0, 224, 40283, 'Phase 1', 'Shaman', 'Elemental', 'Chest', 'Both', 'Fallout Impervious Tunic'), +(7, 0, 5, 0, 224, 40327, 'Phase 1', 'Shaman', 'Elemental', 'Waist', 'Both', 'Girdle of Recuperation'), +(7, 0, 6, 0, 224, 40560, 'Phase 1', 'Shaman', 'Elemental', 'Legs', 'Both', 'Leggings of the Wanton Spellcaster'), +(7, 0, 7, 0, 224, 40237, 'Phase 1', 'Shaman', 'Elemental', 'Feet', 'Both', 'Eruption-Scarred Boots'), +(7, 0, 8, 0, 224, 40324, 'Phase 1', 'Shaman', 'Elemental', 'Wrists', 'Both', 'Bands of Mutual Respect'), +(7, 0, 9, 0, 224, 40302, 'Phase 1', 'Shaman', 'Elemental', 'Hands', 'Both', 'Benefactor''s Gauntlets'), +(7, 0, 10, 0, 224, 40399, 'Phase 1', 'Shaman', 'Elemental', 'Finger1', 'Both', 'Signet of Manifested Pain'), +(7, 0, 12, 0, 224, 40255, 'Phase 1', 'Shaman', 'Elemental', 'Trinket1', 'Both', 'Dying Curse'), +(7, 0, 14, 0, 224, 44005, 'Phase 1', 'Shaman', 'Elemental', 'Back', 'Both', 'Pennant Cloak'), +(7, 0, 17, 0, 224, 40267, 'Phase 1', 'Shaman', 'Elemental', 'Ranged', 'Both', 'Totem of Hex'); + +-- ilvl 245 (Phase 2) +INSERT INTO `playerbots_bis_gear` VALUES +(7, 0, 0, 0, 245, 46209, 'Phase 2', 'Shaman', 'Elemental', 'Head', 'Both', 'Conqueror''s Worldbreaker Helm'), +(7, 0, 2, 0, 245, 46211, 'Phase 2', 'Shaman', 'Elemental', 'Shoulders', 'Both', 'Conqueror''s Worldbreaker Shoulderpads'), +(7, 0, 4, 0, 245, 46206, 'Phase 2', 'Shaman', 'Elemental', 'Chest', 'Both', 'Conqueror''s Worldbreaker Hauberk'), +(7, 0, 6, 0, 245, 46210, 'Phase 2', 'Shaman', 'Elemental', 'Legs', 'Both', 'Conqueror''s Worldbreaker Kilt'), +(7, 0, 7, 0, 245, 45537, 'Phase 2', 'Shaman', 'Elemental', 'Feet', 'Both', 'Treads of the False Oracle'), +(7, 0, 8, 1, 245, 45616, 'Phase 2', 'Shaman', 'Elemental', 'Wrists', 'Alliance', 'Star-beaded Clutch'), +(7, 0, 8, 2, 245, 45460, 'Phase 2', 'Shaman', 'Elemental', 'Wrists', 'Horde', 'Bindings of Winter Gale'), +(7, 0, 9, 0, 245, 45665, 'Phase 2', 'Shaman', 'Elemental', 'Hands', 'Both', 'Pharos Gloves'), +(7, 0, 10, 0, 245, 45495, 'Phase 2', 'Shaman', 'Elemental', 'Finger1', 'Both', 'Conductive Seal'), +(7, 0, 12, 0, 245, 45518, 'Phase 2', 'Shaman', 'Elemental', 'Trinket1', 'Both', 'Flare of the Heavens'), +(7, 0, 14, 0, 245, 45242, 'Phase 2', 'Shaman', 'Elemental', 'Back', 'Both', 'Drape of Mortal Downfall'), +(7, 0, 17, 0, 245, 40267, 'Phase 2', 'Shaman', 'Elemental', 'Ranged', 'Both', 'Totem of Hex'); + +-- ilvl 264 (Phase 4) +INSERT INTO `playerbots_bis_gear` VALUES +(7, 0, 0, 0, 264, 51237, 'Phase 4', 'Shaman', 'Elemental', 'Head', 'Both', 'Sanctified Frost Witch''s Helm'), +(7, 0, 2, 0, 264, 50698, 'Phase 4', 'Shaman', 'Elemental', 'Shoulders', 'Both', 'Horrific Flesh Epaulets'), +(7, 0, 4, 0, 264, 51239, 'Phase 4', 'Shaman', 'Elemental', 'Chest', 'Both', 'Sanctified Frost Witch''s Hauberk'), +(7, 0, 5, 0, 264, 50613, 'Phase 4', 'Shaman', 'Elemental', 'Waist', 'Both', 'Crushing Coldwraith Belt'), +(7, 0, 6, 0, 264, 51236, 'Phase 4', 'Shaman', 'Elemental', 'Legs', 'Both', 'Sanctified Frost Witch''s Kilt'), +(7, 0, 7, 0, 264, 50699, 'Phase 4', 'Shaman', 'Elemental', 'Feet', 'Both', 'Plague Scientist''s Boots'), +(7, 0, 8, 0, 264, 50687, 'Phase 4', 'Shaman', 'Elemental', 'Wrists', 'Both', 'Bloodsunder''s Bracers'), +(7, 0, 9, 0, 264, 51238, 'Phase 4', 'Shaman', 'Elemental', 'Hands', 'Both', 'Sanctified Frost Witch''s Gloves'), +(7, 0, 10, 0, 264, 50664, 'Phase 4', 'Shaman', 'Elemental', 'Finger1', 'Both', 'Ring of Rapid Ascent'), +(7, 0, 12, 0, 264, 50348, 'Phase 4', 'Shaman', 'Elemental', 'Trinket1', 'Both', 'Dislodged Foreign Object'), +(7, 0, 14, 0, 264, 50628, 'Phase 4', 'Shaman', 'Elemental', 'Back', 'Both', 'Frostbinder''s Shredded Cape'), +(7, 0, 15, 0, 264, 50734, 'Phase 4', 'Shaman', 'Elemental', 'MainHand', 'Both', 'Royal Scepter of Terenas II'), +(7, 0, 17, 0, 264, 50458, 'Phase 4', 'Shaman', 'Elemental', 'Ranged', 'Both', 'Bizuri''s Totem of Shattered Ice'); + +-- ilvl 290 (Phase 5) +INSERT INTO `playerbots_bis_gear` VALUES +(7, 0, 0, 0, 290, 51237, 'Phase 5', 'Shaman', 'Elemental', 'Head', 'Both', 'Sanctified Frost Witch''s Helm'), +(7, 0, 1, 0, 290, 50182, 'Phase 5', 'Shaman', 'Elemental', 'Neck', 'Both', 'Blood Queen''s Crimson Choker'), +(7, 0, 2, 0, 290, 51235, 'Phase 5', 'Shaman', 'Elemental', 'Shoulders', 'Both', 'Sanctified Frost Witch''s Shoulderpads'), +(7, 0, 4, 0, 290, 51239, 'Phase 5', 'Shaman', 'Elemental', 'Chest', 'Both', 'Sanctified Frost Witch''s Hauberk'), +(7, 0, 5, 0, 290, 54587, 'Phase 5', 'Shaman', 'Elemental', 'Waist', 'Both', 'Split Shape Belt'), +(7, 0, 6, 0, 290, 50694, 'Phase 5', 'Shaman', 'Elemental', 'Legs', 'Both', 'Plaguebringer''s Stained Pants'), +(7, 0, 7, 0, 290, 50699, 'Phase 5', 'Shaman', 'Elemental', 'Feet', 'Both', 'Plague Scientist''s Boots'), +(7, 0, 8, 0, 290, 54582, 'Phase 5', 'Shaman', 'Elemental', 'Wrists', 'Both', 'Bracers of Fiery Night'), +(7, 0, 9, 0, 290, 51238, 'Phase 5', 'Shaman', 'Elemental', 'Hands', 'Both', 'Sanctified Frost Witch''s Gloves'), +(7, 0, 10, 0, 290, 50664, 'Phase 5', 'Shaman', 'Elemental', 'Finger1', 'Both', 'Ring of Rapid Ascent'), +(7, 0, 11, 0, 290, 50398, 'Phase 5', 'Shaman', 'Elemental', 'Finger2', 'Both', 'Ashen Band of Endless Destruction'), +(7, 0, 12, 0, 290, 54588, 'Phase 5', 'Shaman', 'Elemental', 'Trinket1', 'Both', 'Charred Twilight Scale'), +(7, 0, 13, 0, 290, 50365, 'Phase 5', 'Shaman', 'Elemental', 'Trinket2', 'Both', 'Phylactery of the Nameless Lich'), +(7, 0, 14, 0, 290, 54583, 'Phase 5', 'Shaman', 'Elemental', 'Back', 'Both', 'Cloak of Burning Dusk'), +(7, 0, 15, 0, 290, 50734, 'Phase 5', 'Shaman', 'Elemental', 'MainHand', 'Both', 'Royal Scepter of Terenas II'), +(7, 0, 16, 0, 290, 50616, 'Phase 5', 'Shaman', 'Elemental', 'OffHand', 'Both', 'Bulwark of Smouldering Steel'), +(7, 0, 17, 0, 290, 50458, 'Phase 5', 'Shaman', 'Elemental', 'Ranged', 'Both', 'Bizuri''s Totem of Shattered Ice'); + +-- Enhancement (tab 1) +-- ilvl 66 (Phase 1 (Pre-Raid)) +INSERT INTO `playerbots_bis_gear` VALUES +(7, 1, 0, 0, 66, 12587, 'Phase 1 (Pre-Raid)', 'Shaman', 'Enhancement', 'Head', 'Both', 'Eye of Rend'), +(7, 1, 1, 0, 66, 15411, 'Phase 1 (Pre-Raid)', 'Shaman', 'Enhancement', 'Neck', 'Both', 'Mark of Fordring'), +(7, 1, 2, 0, 66, 12927, 'Phase 1 (Pre-Raid)', 'Shaman', 'Enhancement', 'Shoulders', 'Both', 'Truestrike Shoulders'), +(7, 1, 4, 0, 66, 11726, 'Phase 1 (Pre-Raid)', 'Shaman', 'Enhancement', 'Chest', 'Both', 'Savage Gladiator Chain'), +(7, 1, 5, 0, 66, 22232, 'Phase 1 (Pre-Raid)', 'Shaman', 'Enhancement', 'Waist', 'Both', 'Marksman''s Girdle'), +(7, 1, 6, 0, 66, 15062, 'Phase 1 (Pre-Raid)', 'Shaman', 'Enhancement', 'Legs', 'Both', 'Devilsaur Leggings'), +(7, 1, 7, 0, 66, 14616, 'Phase 1 (Pre-Raid)', 'Shaman', 'Enhancement', 'Feet', 'Both', 'Bloodmail Boots'), +(7, 1, 8, 0, 66, 13211, 'Phase 1 (Pre-Raid)', 'Shaman', 'Enhancement', 'Wrists', 'Both', 'Slashclaw Bracers'), +(7, 1, 9, 0, 66, 13957, 'Phase 1 (Pre-Raid)', 'Shaman', 'Enhancement', 'Hands', 'Both', 'Gargoyle Slashers'), +(7, 1, 10, 0, 66, 13098, 'Phase 1 (Pre-Raid)', 'Shaman', 'Enhancement', 'Finger1', 'Both', 'Painweaver Band'), +(7, 1, 11, 0, 66, 17713, 'Phase 1 (Pre-Raid)', 'Shaman', 'Enhancement', 'Finger2', 'Both', 'Blackstone Ring'), +(7, 1, 12, 0, 66, 13965, 'Phase 1 (Pre-Raid)', 'Shaman', 'Enhancement', 'Trinket1', 'Both', 'Blackhand''s Breadth'), +(7, 1, 13, 0, 66, 11815, 'Phase 1 (Pre-Raid)', 'Shaman', 'Enhancement', 'Trinket2', 'Both', 'Hand of Justice'), +(7, 1, 14, 0, 66, 13340, 'Phase 1 (Pre-Raid)', 'Shaman', 'Enhancement', 'Back', 'Both', 'Cape of the Black Baron'), +(7, 1, 15, 0, 66, 12784, 'Phase 1 (Pre-Raid)', 'Shaman', 'Enhancement', 'MainHand', 'Both', 'Arcanite Reaper'); + +-- ilvl 76 (Phase 2 (Pre-Raid)) +INSERT INTO `playerbots_bis_gear` VALUES +(7, 1, 0, 0, 76, 12587, 'Phase 2 (Pre-Raid)', 'Shaman', 'Enhancement', 'Head', 'Both', 'Eye of Rend'), +(7, 1, 1, 0, 76, 15411, 'Phase 2 (Pre-Raid)', 'Shaman', 'Enhancement', 'Neck', 'Both', 'Mark of Fordring'), +(7, 1, 2, 0, 76, 12927, 'Phase 2 (Pre-Raid)', 'Shaman', 'Enhancement', 'Shoulders', 'Both', 'Truestrike Shoulders'), +(7, 1, 4, 0, 76, 11726, 'Phase 2 (Pre-Raid)', 'Shaman', 'Enhancement', 'Chest', 'Both', 'Savage Gladiator Chain'), +(7, 1, 5, 0, 76, 22232, 'Phase 2 (Pre-Raid)', 'Shaman', 'Enhancement', 'Waist', 'Both', 'Marksman''s Girdle'), +(7, 1, 6, 0, 76, 15062, 'Phase 2 (Pre-Raid)', 'Shaman', 'Enhancement', 'Legs', 'Both', 'Devilsaur Leggings'), +(7, 1, 7, 0, 76, 14616, 'Phase 2 (Pre-Raid)', 'Shaman', 'Enhancement', 'Feet', 'Both', 'Bloodmail Boots'), +(7, 1, 8, 0, 76, 13211, 'Phase 2 (Pre-Raid)', 'Shaman', 'Enhancement', 'Wrists', 'Both', 'Slashclaw Bracers'), +(7, 1, 9, 0, 76, 13957, 'Phase 2 (Pre-Raid)', 'Shaman', 'Enhancement', 'Hands', 'Both', 'Gargoyle Slashers'), +(7, 1, 10, 0, 76, 13098, 'Phase 2 (Pre-Raid)', 'Shaman', 'Enhancement', 'Finger1', 'Both', 'Painweaver Band'), +(7, 1, 11, 0, 76, 18500, 'Phase 2 (Pre-Raid)', 'Shaman', 'Enhancement', 'Finger2', 'Both', 'Tarnished Elven Ring'), +(7, 1, 12, 0, 76, 13965, 'Phase 2 (Pre-Raid)', 'Shaman', 'Enhancement', 'Trinket1', 'Both', 'Blackhand''s Breadth'), +(7, 1, 13, 0, 76, 11815, 'Phase 2 (Pre-Raid)', 'Shaman', 'Enhancement', 'Trinket2', 'Both', 'Hand of Justice'), +(7, 1, 14, 0, 76, 13340, 'Phase 2 (Pre-Raid)', 'Shaman', 'Enhancement', 'Back', 'Both', 'Cape of the Black Baron'), +(7, 1, 15, 0, 76, 12784, 'Phase 2 (Pre-Raid)', 'Shaman', 'Enhancement', 'MainHand', 'Both', 'Arcanite Reaper'); + +-- ilvl 78 (Phase 2) +INSERT INTO `playerbots_bis_gear` VALUES +(7, 1, 0, 0, 78, 18817, 'Phase 2', 'Shaman', 'Enhancement', 'Head', 'Both', 'Crown of Destruction'), +(7, 1, 1, 0, 78, 18404, 'Phase 2', 'Shaman', 'Enhancement', 'Neck', 'Both', 'Onyxia Tooth Pendant'), +(7, 1, 2, 0, 78, 12927, 'Phase 2', 'Shaman', 'Enhancement', 'Shoulders', 'Both', 'Truestrike Shoulders'), +(7, 1, 4, 0, 78, 11726, 'Phase 2', 'Shaman', 'Enhancement', 'Chest', 'Both', 'Savage Gladiator Chain'), +(7, 1, 5, 0, 78, 18393, 'Phase 2', 'Shaman', 'Enhancement', 'Waist', 'Both', 'Warpwood Binding'), +(7, 1, 6, 0, 78, 15062, 'Phase 2', 'Shaman', 'Enhancement', 'Legs', 'Both', 'Devilsaur Leggings'), +(7, 1, 7, 0, 78, 14616, 'Phase 2', 'Shaman', 'Enhancement', 'Feet', 'Both', 'Bloodmail Boots'), +(7, 1, 8, 0, 78, 19146, 'Phase 2', 'Shaman', 'Enhancement', 'Wrists', 'Both', 'Wristguards of Stability'), +(7, 1, 9, 0, 78, 13957, 'Phase 2', 'Shaman', 'Enhancement', 'Hands', 'Both', 'Gargoyle Slashers'), +(7, 1, 10, 0, 78, 17063, 'Phase 2', 'Shaman', 'Enhancement', 'Finger1', 'Both', 'Band of Accuria'), +(7, 1, 11, 0, 78, 18821, 'Phase 2', 'Shaman', 'Enhancement', 'Finger2', 'Both', 'Quick Strike Ring'), +(7, 1, 12, 0, 78, 13965, 'Phase 2', 'Shaman', 'Enhancement', 'Trinket1', 'Both', 'Blackhand''s Breadth'), +(7, 1, 13, 0, 78, 11815, 'Phase 2', 'Shaman', 'Enhancement', 'Trinket2', 'Both', 'Hand of Justice'), +(7, 1, 14, 0, 78, 13340, 'Phase 2', 'Shaman', 'Enhancement', 'Back', 'Both', 'Cape of the Black Baron'), +(7, 1, 15, 0, 78, 17104, 'Phase 2', 'Shaman', 'Enhancement', 'MainHand', 'Both', 'Spinal Reaper'); + +-- ilvl 83 (Phase 3) +INSERT INTO `playerbots_bis_gear` VALUES +(7, 1, 0, 0, 83, 18817, 'Phase 3', 'Shaman', 'Enhancement', 'Head', 'Both', 'Crown of Destruction'), +(7, 1, 1, 0, 83, 18404, 'Phase 3', 'Shaman', 'Enhancement', 'Neck', 'Both', 'Onyxia Tooth Pendant'), +(7, 1, 2, 0, 83, 12927, 'Phase 3', 'Shaman', 'Enhancement', 'Shoulders', 'Both', 'Truestrike Shoulders'), +(7, 1, 4, 0, 83, 11726, 'Phase 3', 'Shaman', 'Enhancement', 'Chest', 'Both', 'Savage Gladiator Chain'), +(7, 1, 5, 0, 83, 19380, 'Phase 3', 'Shaman', 'Enhancement', 'Waist', 'Both', 'Therazane''s Link'), +(7, 1, 6, 0, 83, 15062, 'Phase 3', 'Shaman', 'Enhancement', 'Legs', 'Both', 'Devilsaur Leggings'), +(7, 1, 7, 0, 83, 19381, 'Phase 3', 'Shaman', 'Enhancement', 'Feet', 'Both', 'Boots of the Shadow Flame'), +(7, 1, 8, 0, 83, 19146, 'Phase 3', 'Shaman', 'Enhancement', 'Wrists', 'Both', 'Wristguards of Stability'), +(7, 1, 9, 0, 83, 19157, 'Phase 3', 'Shaman', 'Enhancement', 'Hands', 'Both', 'Chromatic Gauntlets'), +(7, 1, 10, 0, 83, 19384, 'Phase 3', 'Shaman', 'Enhancement', 'Finger1', 'Both', 'Master Dragonslayer''s Ring'), +(7, 1, 11, 0, 83, 18821, 'Phase 3', 'Shaman', 'Enhancement', 'Finger2', 'Both', 'Quick Strike Ring'), +(7, 1, 12, 0, 83, 13965, 'Phase 3', 'Shaman', 'Enhancement', 'Trinket1', 'Both', 'Blackhand''s Breadth'), +(7, 1, 13, 0, 83, 11815, 'Phase 3', 'Shaman', 'Enhancement', 'Trinket2', 'Both', 'Hand of Justice'), +(7, 1, 14, 0, 83, 19436, 'Phase 3', 'Shaman', 'Enhancement', 'Back', 'Both', 'Cloak of Draconic Might'), +(7, 1, 15, 0, 83, 17104, 'Phase 3', 'Shaman', 'Enhancement', 'MainHand', 'Both', 'Spinal Reaper'); + +-- ilvl 88 (Phase 5) +INSERT INTO `playerbots_bis_gear` VALUES +(7, 1, 0, 0, 88, 18817, 'Phase 5', 'Shaman', 'Enhancement', 'Head', 'Both', 'Crown of Destruction'), +(7, 1, 1, 0, 88, 18404, 'Phase 5', 'Shaman', 'Enhancement', 'Neck', 'Both', 'Onyxia Tooth Pendant'), +(7, 1, 2, 0, 88, 21684, 'Phase 5', 'Shaman', 'Enhancement', 'Shoulders', 'Both', 'Mantle of the Desert''s Fury'), +(7, 1, 4, 0, 88, 21374, 'Phase 5', 'Shaman', 'Enhancement', 'Chest', 'Both', 'Stormcaller''s Hauberk'), +(7, 1, 5, 0, 88, 21586, 'Phase 5', 'Shaman', 'Enhancement', 'Waist', 'Both', 'Belt of Never-ending Agony'), +(7, 1, 6, 0, 88, 21651, 'Phase 5', 'Shaman', 'Enhancement', 'Legs', 'Both', 'Scaled Sand Reaver Leggings'), +(7, 1, 7, 0, 88, 21705, 'Phase 5', 'Shaman', 'Enhancement', 'Feet', 'Both', 'Boots of the Fallen Prophet'), +(7, 1, 8, 0, 88, 21602, 'Phase 5', 'Shaman', 'Enhancement', 'Wrists', 'Both', 'Qiraji Execution Bracers'), +(7, 1, 9, 0, 88, 21624, 'Phase 5', 'Shaman', 'Enhancement', 'Hands', 'Both', 'Gauntlets of Kalimdor'), +(7, 1, 10, 0, 88, 17063, 'Phase 5', 'Shaman', 'Enhancement', 'Finger1', 'Both', 'Band of Accuria'), +(7, 1, 11, 0, 88, 18821, 'Phase 5', 'Shaman', 'Enhancement', 'Finger2', 'Both', 'Quick Strike Ring'), +(7, 1, 12, 0, 88, 22321, 'Phase 5', 'Shaman', 'Enhancement', 'Trinket1', 'Both', 'Heart of Wyrmthalak'), +(7, 1, 13, 0, 88, 19289, 'Phase 5', 'Shaman', 'Enhancement', 'Trinket2', 'Both', 'Darkmoon Card: Maelstrom'), +(7, 1, 14, 0, 88, 21701, 'Phase 5', 'Shaman', 'Enhancement', 'Back', 'Both', 'Cloak of Concentrated Hatred'), +(7, 1, 15, 0, 88, 21134, 'Phase 5', 'Shaman', 'Enhancement', 'MainHand', 'Both', 'Dark Edge of Insanity'), +(7, 1, 17, 0, 88, 22395, 'Phase 5', 'Shaman', 'Enhancement', 'Ranged', 'Both', 'Totem of Rage'); + +-- ilvl 92 (Phase 6) +INSERT INTO `playerbots_bis_gear` VALUES +(7, 1, 0, 0, 92, 18817, 'Phase 6', 'Shaman', 'Enhancement', 'Head', 'Both', 'Crown of Destruction'), +(7, 1, 1, 0, 92, 18404, 'Phase 6', 'Shaman', 'Enhancement', 'Neck', 'Both', 'Onyxia Tooth Pendant'), +(7, 1, 2, 0, 92, 21684, 'Phase 6', 'Shaman', 'Enhancement', 'Shoulders', 'Both', 'Mantle of the Desert''s Fury'), +(7, 1, 4, 0, 92, 21374, 'Phase 6', 'Shaman', 'Enhancement', 'Chest', 'Both', 'Stormcaller''s Hauberk'), +(7, 1, 5, 0, 92, 21586, 'Phase 6', 'Shaman', 'Enhancement', 'Waist', 'Both', 'Belt of Never-ending Agony'), +(7, 1, 6, 0, 92, 21651, 'Phase 6', 'Shaman', 'Enhancement', 'Legs', 'Both', 'Scaled Sand Reaver Leggings'), +(7, 1, 7, 0, 92, 21705, 'Phase 6', 'Shaman', 'Enhancement', 'Feet', 'Both', 'Boots of the Fallen Prophet'), +(7, 1, 8, 0, 92, 21602, 'Phase 6', 'Shaman', 'Enhancement', 'Wrists', 'Both', 'Qiraji Execution Bracers'), +(7, 1, 9, 0, 92, 21624, 'Phase 6', 'Shaman', 'Enhancement', 'Hands', 'Both', 'Gauntlets of Kalimdor'), +(7, 1, 10, 0, 92, 17063, 'Phase 6', 'Shaman', 'Enhancement', 'Finger1', 'Both', 'Band of Accuria'), +(7, 1, 12, 0, 92, 22321, 'Phase 6', 'Shaman', 'Enhancement', 'Trinket1', 'Both', 'Heart of Wyrmthalak'), +(7, 1, 13, 0, 92, 19289, 'Phase 6', 'Shaman', 'Enhancement', 'Trinket2', 'Both', 'Darkmoon Card: Maelstrom'), +(7, 1, 14, 0, 92, 23045, 'Phase 6', 'Shaman', 'Enhancement', 'Back', 'Both', 'Shroud of Dominion'), +(7, 1, 15, 0, 92, 22798, 'Phase 6', 'Shaman', 'Enhancement', 'MainHand', 'Both', 'Might of Menethil'), +(7, 1, 17, 0, 92, 22395, 'Phase 6', 'Shaman', 'Enhancement', 'Ranged', 'Both', 'Totem of Rage'); + +-- ilvl 120 (Pre-Raid) +INSERT INTO `playerbots_bis_gear` VALUES +(7, 1, 0, 0, 120, 28224, 'Pre-Raid', 'Shaman', 'Enhancement', 'Head', 'Both', 'Wastewalker Helm'), +(7, 1, 1, 0, 120, 29381, 'Pre-Raid', 'Shaman', 'Enhancement', 'Neck', 'Both', 'Choker of Vile Intent'), +(7, 1, 2, 0, 120, 27797, 'Pre-Raid', 'Shaman', 'Enhancement', 'Shoulders', 'Both', 'Wastewalker Shoulderpads'), +(7, 1, 4, 0, 120, 29515, 'Pre-Raid', 'Shaman', 'Enhancement', 'Chest', 'Both', 'Ebon Netherscale Breastplate'), +(7, 1, 5, 0, 120, 29516, 'Pre-Raid', 'Shaman', 'Enhancement', 'Waist', 'Both', 'Ebon Netherscale Belt'), +(7, 1, 6, 0, 120, 31544, 'Pre-Raid', 'Shaman', 'Enhancement', 'Legs', 'Both', 'Clefthoof Hide Leggings'), +(7, 1, 7, 0, 120, 25686, 'Pre-Raid', 'Shaman', 'Enhancement', 'Feet', 'Both', 'Fel Leather Boots'), +(7, 1, 8, 0, 120, 29517, 'Pre-Raid', 'Shaman', 'Enhancement', 'Wrists', 'Both', 'Ebon Netherscale Bracers'), +(7, 1, 9, 0, 120, 25685, 'Pre-Raid', 'Shaman', 'Enhancement', 'Hands', 'Both', 'Fel Leather Gloves'), +(7, 1, 10, 0, 120, 30365, 'Pre-Raid', 'Shaman', 'Enhancement', 'Finger1', 'Both', 'Overseer''s Signet'), +(7, 1, 11, 0, 120, 30834, 'Pre-Raid', 'Shaman', 'Enhancement', 'Finger2', 'Both', 'Shapeshifter''s Signet'), +(7, 1, 12, 0, 120, 28288, 'Pre-Raid', 'Shaman', 'Enhancement', 'Trinket1', 'Both', 'Abacus of Violent Odds'), +(7, 1, 13, 0, 120, 29383, 'Pre-Raid', 'Shaman', 'Enhancement', 'Trinket2', 'Both', 'Bloodlust Brooch'), +(7, 1, 14, 0, 120, 24259, 'Pre-Raid', 'Shaman', 'Enhancement', 'Back', 'Both', 'Vengeance Wrap'), +(7, 1, 15, 0, 120, 27872, 'Pre-Raid', 'Shaman', 'Enhancement', 'MainHand', 'Both', 'The Harvester of Souls'), +(7, 1, 16, 0, 120, 27872, 'Pre-Raid', 'Shaman', 'Enhancement', 'OffHand', 'Both', 'The Harvester of Souls'), +(7, 1, 17, 0, 120, 27815, 'Pre-Raid', 'Shaman', 'Enhancement', 'Ranged', 'Both', 'Totem of the Astral Winds'); + +-- ilvl 125 (Phase 1) +INSERT INTO `playerbots_bis_gear` VALUES +(7, 1, 0, 0, 125, 29040, 'Phase 1', 'Shaman', 'Enhancement', 'Head', 'Both', 'Cyclone Helm'), +(7, 1, 1, 0, 125, 29381, 'Phase 1', 'Shaman', 'Enhancement', 'Neck', 'Both', 'Choker of Vile Intent'), +(7, 1, 2, 0, 125, 29043, 'Phase 1', 'Shaman', 'Enhancement', 'Shoulders', 'Both', 'Cyclone Shoulderplates'), +(7, 1, 4, 0, 125, 29515, 'Phase 1', 'Shaman', 'Enhancement', 'Chest', 'Both', 'Ebon Netherscale Breastplate'), +(7, 1, 5, 0, 125, 29516, 'Phase 1', 'Shaman', 'Enhancement', 'Waist', 'Both', 'Ebon Netherscale Belt'), +(7, 1, 6, 0, 125, 30739, 'Phase 1', 'Shaman', 'Enhancement', 'Legs', 'Both', 'Scaled Greaves of the Marksman'), +(7, 1, 7, 0, 125, 28545, 'Phase 1', 'Shaman', 'Enhancement', 'Feet', 'Both', 'Edgewalker Longboots'), +(7, 1, 8, 0, 125, 29517, 'Phase 1', 'Shaman', 'Enhancement', 'Wrists', 'Both', 'Ebon Netherscale Bracers'), +(7, 1, 9, 0, 125, 28776, 'Phase 1', 'Shaman', 'Enhancement', 'Hands', 'Both', 'Liar''s Tongue Gloves'), +(7, 1, 10, 0, 125, 28757, 'Phase 1', 'Shaman', 'Enhancement', 'Finger1', 'Both', 'Ring of a Thousand Marks'), +(7, 1, 11, 0, 125, 30834, 'Phase 1', 'Shaman', 'Enhancement', 'Finger2', 'Both', 'Shapeshifter''s Signet'), +(7, 1, 12, 0, 125, 29383, 'Phase 1', 'Shaman', 'Enhancement', 'Trinket1', 'Both', 'Bloodlust Brooch'), +(7, 1, 13, 0, 125, 28830, 'Phase 1', 'Shaman', 'Enhancement', 'Trinket2', 'Both', 'Dragonspine Trophy'), +(7, 1, 14, 0, 125, 24259, 'Phase 1', 'Shaman', 'Enhancement', 'Back', 'Both', 'Vengeance Wrap'), +(7, 1, 15, 0, 125, 28308, 'Phase 1', 'Shaman', 'Enhancement', 'MainHand', 'Both', 'Gladiator''s Cleaver'), +(7, 1, 16, 0, 125, 28308, 'Phase 1', 'Shaman', 'Enhancement', 'OffHand', 'Both', 'Gladiator''s Cleaver'), +(7, 1, 17, 0, 125, 27815, 'Phase 1', 'Shaman', 'Enhancement', 'Ranged', 'Both', 'Totem of the Astral Winds'); + +-- ilvl 141 (Phase 2) +INSERT INTO `playerbots_bis_gear` VALUES +(7, 1, 0, 0, 141, 30190, 'Phase 2', 'Shaman', 'Enhancement', 'Head', 'Both', 'Cataclysm Helm'), +(7, 1, 1, 0, 141, 30017, 'Phase 2', 'Shaman', 'Enhancement', 'Neck', 'Both', 'Telonicus''s Pendant of Mayhem'), +(7, 1, 2, 0, 141, 30055, 'Phase 2', 'Shaman', 'Enhancement', 'Shoulders', 'Both', 'Shoulderpads of the Stranger'), +(7, 1, 4, 0, 141, 30185, 'Phase 2', 'Shaman', 'Enhancement', 'Chest', 'Both', 'Cataclysm Chestplate'), +(7, 1, 5, 0, 141, 30106, 'Phase 2', 'Shaman', 'Enhancement', 'Waist', 'Both', 'Belt of One-Hundred Deaths'), +(7, 1, 6, 0, 141, 30192, 'Phase 2', 'Shaman', 'Enhancement', 'Legs', 'Both', 'Cataclysm Legplates'), +(7, 1, 7, 0, 141, 30039, 'Phase 2', 'Shaman', 'Enhancement', 'Feet', 'Both', 'Boots of Utter Darkness'), +(7, 1, 8, 0, 141, 30091, 'Phase 2', 'Shaman', 'Enhancement', 'Wrists', 'Both', 'True-Aim Stalker Bands'), +(7, 1, 9, 0, 141, 30189, 'Phase 2', 'Shaman', 'Enhancement', 'Hands', 'Both', 'Cataclysm Gauntlets'), +(7, 1, 10, 0, 141, 29997, 'Phase 2', 'Shaman', 'Enhancement', 'Finger1', 'Both', 'Band of the Ranger-General'), +(7, 1, 11, 0, 141, 30052, 'Phase 2', 'Shaman', 'Enhancement', 'Finger2', 'Both', 'Ring of Lethality'), +(7, 1, 12, 0, 141, 29383, 'Phase 2', 'Shaman', 'Enhancement', 'Trinket1', 'Both', 'Bloodlust Brooch'), +(7, 1, 13, 0, 141, 28830, 'Phase 2', 'Shaman', 'Enhancement', 'Trinket2', 'Both', 'Dragonspine Trophy'), +(7, 1, 14, 0, 141, 29994, 'Phase 2', 'Shaman', 'Enhancement', 'Back', 'Both', 'Thalassian Wildercloak'), +(7, 1, 16, 0, 141, 28433, 'Phase 2', 'Shaman', 'Enhancement', 'OffHand', 'Both', 'Wicked Edge of the Planes'), +(7, 1, 17, 0, 141, 27815, 'Phase 2', 'Shaman', 'Enhancement', 'Ranged', 'Both', 'Totem of the Astral Winds'); + +-- ilvl 156 (Phase 3) +INSERT INTO `playerbots_bis_gear` VALUES +(7, 1, 0, 0, 156, 32235, 'Phase 3', 'Shaman', 'Enhancement', 'Head', 'Both', 'Cursed Vision of Sargeras'), +(7, 1, 1, 0, 156, 32260, 'Phase 3', 'Shaman', 'Enhancement', 'Neck', 'Both', 'Choker of Endless Nightmares'), +(7, 1, 2, 0, 156, 32581, 'Phase 3', 'Shaman', 'Enhancement', 'Shoulders', 'Both', 'Swiftstrike Shoulders'), +(7, 1, 4, 0, 156, 30905, 'Phase 3', 'Shaman', 'Enhancement', 'Chest', 'Both', 'Midnight Chestguard'), +(7, 1, 5, 0, 156, 30106, 'Phase 3', 'Shaman', 'Enhancement', 'Waist', 'Both', 'Belt of One-Hundred Deaths'), +(7, 1, 6, 0, 156, 30900, 'Phase 3', 'Shaman', 'Enhancement', 'Legs', 'Both', 'Bow-Stitched Leggings'), +(7, 1, 7, 0, 156, 32366, 'Phase 3', 'Shaman', 'Enhancement', 'Feet', 'Both', 'Shadowmaster''s Boots'), +(7, 1, 8, 0, 156, 32574, 'Phase 3', 'Shaman', 'Enhancement', 'Wrists', 'Both', 'Bindings of Lightning Reflexes'), +(7, 1, 9, 0, 156, 32347, 'Phase 3', 'Shaman', 'Enhancement', 'Hands', 'Both', 'Grips of Damnation'), +(7, 1, 10, 0, 156, 29301, 'Phase 3', 'Shaman', 'Enhancement', 'Finger1', 'Both', 'Band of the Eternal Champion'), +(7, 1, 11, 0, 156, 32497, 'Phase 3', 'Shaman', 'Enhancement', 'Finger2', 'Both', 'Stormrage Signet Ring'), +(7, 1, 12, 0, 156, 28830, 'Phase 3', 'Shaman', 'Enhancement', 'Trinket1', 'Both', 'Dragonspine Trophy'), +(7, 1, 13, 0, 156, 32505, 'Phase 3', 'Shaman', 'Enhancement', 'Trinket2', 'Both', 'Madness of the Betrayer'), +(7, 1, 14, 0, 156, 32323, 'Phase 3', 'Shaman', 'Enhancement', 'Back', 'Both', 'Shadowmoon Destroyer''s Drape'), +(7, 1, 17, 0, 156, 27815, 'Phase 3', 'Shaman', 'Enhancement', 'Ranged', 'Both', 'Totem of the Astral Winds'); + +-- ilvl 164 (Phase 4) +INSERT INTO `playerbots_bis_gear` VALUES +(7, 1, 0, 0, 164, 32235, 'Phase 4', 'Shaman', 'Enhancement', 'Head', 'Both', 'Cursed Vision of Sargeras'), +(7, 1, 1, 0, 164, 32260, 'Phase 4', 'Shaman', 'Enhancement', 'Neck', 'Both', 'Choker of Endless Nightmares'), +(7, 1, 2, 0, 164, 32581, 'Phase 4', 'Shaman', 'Enhancement', 'Shoulders', 'Both', 'Swiftstrike Shoulders'), +(7, 1, 4, 0, 164, 30905, 'Phase 4', 'Shaman', 'Enhancement', 'Chest', 'Both', 'Midnight Chestguard'), +(7, 1, 5, 0, 164, 30106, 'Phase 4', 'Shaman', 'Enhancement', 'Waist', 'Both', 'Belt of One-Hundred Deaths'), +(7, 1, 6, 0, 164, 30900, 'Phase 4', 'Shaman', 'Enhancement', 'Legs', 'Both', 'Bow-Stitched Leggings'), +(7, 1, 7, 0, 164, 32366, 'Phase 4', 'Shaman', 'Enhancement', 'Feet', 'Both', 'Shadowmaster''s Boots'), +(7, 1, 8, 0, 164, 32574, 'Phase 4', 'Shaman', 'Enhancement', 'Wrists', 'Both', 'Bindings of Lightning Reflexes'), +(7, 1, 9, 0, 164, 32347, 'Phase 4', 'Shaman', 'Enhancement', 'Hands', 'Both', 'Grips of Damnation'), +(7, 1, 10, 0, 164, 33496, 'Phase 4', 'Shaman', 'Enhancement', 'Finger1', 'Both', 'Signet of Primal Wrath'), +(7, 1, 11, 0, 164, 32497, 'Phase 4', 'Shaman', 'Enhancement', 'Finger2', 'Both', 'Stormrage Signet Ring'), +(7, 1, 12, 0, 164, 28830, 'Phase 4', 'Shaman', 'Enhancement', 'Trinket1', 'Both', 'Dragonspine Trophy'), +(7, 1, 13, 0, 164, 32505, 'Phase 4', 'Shaman', 'Enhancement', 'Trinket2', 'Both', 'Madness of the Betrayer'), +(7, 1, 14, 0, 164, 32323, 'Phase 4', 'Shaman', 'Enhancement', 'Back', 'Both', 'Shadowmoon Destroyer''s Drape'), +(7, 1, 15, 0, 164, 34331, 'Phase 5', 'Shaman', 'Enhancement', 'MainHand', 'Both', 'Hand of the Deceiver'), +(7, 1, 16, 0, 164, 34346, 'Phase 5', 'Shaman', 'Enhancement', 'OffHand', 'Both', 'Mounting Vengeance'), +(7, 1, 17, 0, 164, 33507, 'Phase 4', 'Shaman', 'Enhancement', 'Ranged', 'Both', 'Stonebreaker''s Totem'); + +-- ilvl 224 (Phase 1) +INSERT INTO `playerbots_bis_gear` VALUES +(7, 1, 0, 0, 224, 39602, 'Phase 1', 'Shaman', 'Enhancement', 'Head', 'Both', 'Heroes'' Earthshatter Faceguard'), +(7, 1, 1, 0, 224, 39146, 'Phase 1', 'Shaman', 'Enhancement', 'Neck', 'Both', 'Collar of Dissolution'), +(7, 1, 2, 0, 224, 40524, 'Phase 1', 'Shaman', 'Enhancement', 'Shoulders', 'Both', 'Valorous Earthshatter Shoulderguards'), +(7, 1, 4, 0, 224, 39597, 'Phase 1', 'Shaman', 'Enhancement', 'Chest', 'Both', 'Heroes'' Earthshatter Chestguard'), +(7, 1, 5, 0, 224, 39762, 'Phase 1', 'Shaman', 'Enhancement', 'Waist', 'Both', 'Torn Web Wrapping'), +(7, 1, 6, 0, 224, 40522, 'Phase 1', 'Shaman', 'Enhancement', 'Legs', 'Both', 'Valorous Earthshatter War-Kilt'), +(7, 1, 7, 0, 224, 40746, 'Phase 1', 'Shaman', 'Enhancement', 'Feet', 'Both', 'Pack-Ice Striders'), +(7, 1, 8, 0, 224, 40282, 'Phase 1', 'Shaman', 'Enhancement', 'Wrists', 'Both', 'Slime Stream Bands'), +(7, 1, 9, 0, 224, 39601, 'Phase 1', 'Shaman', 'Enhancement', 'Hands', 'Both', 'Heroes'' Earthshatter Grips'), +(7, 1, 10, 0, 224, 45688, 'Phase 1', 'Shaman', 'Enhancement', 'Finger1', 'Both', 'Inscribed Band of the Kirin Tor'), +(7, 1, 12, 1, 224, 40322, 'Phase 1', 'Shaman', 'Enhancement', 'Trinket1', 'Alliance', 'Totem of Dueling'), +(7, 1, 12, 2, 224, 37390, 'Phase 1', 'Shaman', 'Enhancement', 'Trinket1', 'Horde', 'Meteorite Whetstone'), +(7, 1, 14, 0, 224, 40721, 'Phase 1', 'Shaman', 'Enhancement', 'Back', 'Both', 'Hammerhead Sharkskin Cloak'); + +-- ilvl 245 (Phase 2) +INSERT INTO `playerbots_bis_gear` VALUES +(7, 1, 0, 1, 245, 51242, 'Phase 2', 'Shaman', 'Enhancement', 'Head', 'Alliance', 'Sanctified Frost Witch''s Faceguard'), +(7, 1, 0, 2, 245, 40543, 'Phase 2', 'Shaman', 'Enhancement', 'Head', 'Horde', 'Blue Aspect Helm'), +(7, 1, 1, 1, 245, 50658, 'Phase 2', 'Shaman', 'Enhancement', 'Neck', 'Alliance', 'Amulet of the Silent Eulogy'), +(7, 1, 1, 2, 245, 44661, 'Phase 2', 'Shaman', 'Enhancement', 'Neck', 'Horde', 'Wyrmrest Necklace of Power'), +(7, 1, 2, 1, 245, 51240, 'Phase 2', 'Shaman', 'Enhancement', 'Shoulders', 'Alliance', 'Sanctified Frost Witch''s Shoulderguards'), +(7, 1, 2, 2, 245, 40524, 'Phase 2', 'Shaman', 'Enhancement', 'Shoulders', 'Horde', 'Valorous Earthshatter Shoulderguards'), +(7, 1, 4, 1, 245, 50689, 'Phase 2', 'Shaman', 'Enhancement', 'Chest', 'Alliance', 'Carapace of Forgotten Kings'), +(7, 1, 4, 2, 245, 40523, 'Phase 2', 'Shaman', 'Enhancement', 'Chest', 'Horde', 'Valorous Earthshatter Chestguard'), +(7, 1, 5, 1, 245, 54587, 'Phase 2', 'Shaman', 'Enhancement', 'Waist', 'Alliance', 'Split Shape Belt'), +(7, 1, 5, 2, 245, 39762, 'Phase 2', 'Shaman', 'Enhancement', 'Waist', 'Horde', 'Torn Web Wrapping'), +(7, 1, 6, 1, 245, 51241, 'Phase 2', 'Shaman', 'Enhancement', 'Legs', 'Alliance', 'Sanctified Frost Witch''s War-Kilt'), +(7, 1, 6, 2, 245, 40522, 'Phase 2', 'Shaman', 'Enhancement', 'Legs', 'Horde', 'Valorous Earthshatter War-Kilt'), +(7, 1, 7, 1, 245, 50711, 'Phase 2', 'Shaman', 'Enhancement', 'Feet', 'Alliance', 'Treads of the Wasteland'), +(7, 1, 7, 2, 245, 40367, 'Phase 2', 'Shaman', 'Enhancement', 'Feet', 'Horde', 'Boots of the Great Construct'), +(7, 1, 8, 1, 245, 54582, 'Phase 2', 'Shaman', 'Enhancement', 'Wrists', 'Alliance', 'Bracers of Fiery Night'), +(7, 1, 8, 2, 245, 40282, 'Phase 2', 'Shaman', 'Enhancement', 'Wrists', 'Horde', 'Slime Stream Bands'), +(7, 1, 9, 1, 245, 51243, 'Phase 2', 'Shaman', 'Enhancement', 'Hands', 'Alliance', 'Sanctified Frost Witch''s Grips'), +(7, 1, 9, 2, 245, 40520, 'Phase 2', 'Shaman', 'Enhancement', 'Hands', 'Horde', 'Valorous Earthshatter Grips'), +(7, 1, 10, 1, 245, 54576, 'Phase 2', 'Shaman', 'Enhancement', 'Finger1', 'Alliance', 'Signet of Twilight'), +(7, 1, 10, 2, 245, 40074, 'Phase 2', 'Shaman', 'Enhancement', 'Finger1', 'Horde', 'Strong-Handed Ring'), +(7, 1, 12, 1, 245, 50365, 'Phase 2', 'Shaman', 'Enhancement', 'Trinket1', 'Alliance', 'Phylactery of the Nameless Lich'), +(7, 1, 12, 2, 245, 40684, 'Phase 2', 'Shaman', 'Enhancement', 'Trinket1', 'Horde', 'Mirror of Truth'), +(7, 1, 14, 1, 245, 54583, 'Phase 2', 'Shaman', 'Enhancement', 'Back', 'Alliance', 'Cloak of Burning Dusk'), +(7, 1, 14, 2, 245, 40403, 'Phase 2', 'Shaman', 'Enhancement', 'Back', 'Horde', 'Drape of the Deadly Foe'), +(7, 1, 15, 0, 245, 50734, 'Phase 2', 'Shaman', 'Enhancement', 'MainHand', 'Both', 'Royal Scepter of Terenas II'), +(7, 1, 17, 1, 245, 50458, 'Phase 2', 'Shaman', 'Enhancement', 'Ranged', 'Alliance', 'Bizuri''s Totem of Shattered Ice'), +(7, 1, 17, 2, 245, 40322, 'Phase 2', 'Shaman', 'Enhancement', 'Ranged', 'Horde', 'Totem of Dueling'); + +-- ilvl 258 (Phase 3) +INSERT INTO `playerbots_bis_gear` VALUES +(7, 1, 0, 0, 258, 45610, 'Phase 3', 'Shaman', 'Enhancement', 'Head', 'Both', 'Boundless Gaze'), +(7, 1, 1, 0, 258, 45517, 'Phase 3', 'Shaman', 'Enhancement', 'Neck', 'Both', 'Pendulum of Infinity'), +(7, 1, 2, 0, 258, 46203, 'Phase 3', 'Shaman', 'Enhancement', 'Shoulders', 'Both', 'Conqueror''s Worldbreaker Shoulderguards'), +(7, 1, 4, 0, 258, 46205, 'Phase 3', 'Shaman', 'Enhancement', 'Chest', 'Both', 'Conqueror''s Worldbreaker Chestguard'), +(7, 1, 5, 0, 258, 45553, 'Phase 3', 'Shaman', 'Enhancement', 'Waist', 'Both', 'Belt of Dragons'), +(7, 1, 6, 0, 258, 46208, 'Phase 3', 'Shaman', 'Enhancement', 'Legs', 'Both', 'Conqueror''s Worldbreaker War-Kilt'), +(7, 1, 7, 0, 258, 45244, 'Phase 3', 'Shaman', 'Enhancement', 'Feet', 'Both', 'Greaves of Swift Vengeance'), +(7, 1, 8, 0, 258, 45611, 'Phase 3', 'Shaman', 'Enhancement', 'Wrists', 'Both', 'Solar Bindings'), +(7, 1, 9, 0, 258, 46200, 'Phase 3', 'Shaman', 'Enhancement', 'Hands', 'Both', 'Conqueror''s Worldbreaker Grips'), +(7, 1, 12, 0, 258, 45609, 'Phase 3', 'Shaman', 'Enhancement', 'Trinket1', 'Both', 'Comet''s Trail'), +(7, 1, 14, 0, 258, 45461, 'Phase 3', 'Shaman', 'Enhancement', 'Back', 'Both', 'Drape of Icy Intent'), +(7, 1, 15, 0, 258, 45132, 'Phase 3', 'Shaman', 'Enhancement', 'MainHand', 'Both', 'Golden Saronite Dragon'), +(7, 1, 17, 0, 258, 42608, 'Phase 3', 'Shaman', 'Enhancement', 'Ranged', 'Both', 'Furious Gladiator''s Totem of Indomitability'); + +-- ilvl 264 (Phase 4) +INSERT INTO `playerbots_bis_gear` VALUES +(7, 1, 0, 0, 264, 48353, 'Phase 4', 'Shaman', 'Enhancement', 'Head', 'Both', 'Faceguard of Triumph'), +(7, 1, 2, 0, 264, 48351, 'Phase 4', 'Shaman', 'Enhancement', 'Shoulders', 'Both', 'Shoulderguards of Triumph'), +(7, 1, 4, 1, 264, 46965, 'Phase 4', 'Shaman', 'Enhancement', 'Chest', 'Alliance', 'Breastplate of Cruel Intent'), +(7, 1, 4, 2, 264, 47412, 'Phase 4', 'Shaman', 'Enhancement', 'Chest', 'Horde', 'Cuirass of Cruel Intent'), +(7, 1, 5, 1, 264, 47112, 'Phase 4', 'Shaman', 'Enhancement', 'Waist', 'Alliance', 'Belt of the Merciless Killer'), +(7, 1, 5, 2, 264, 47460, 'Phase 4', 'Shaman', 'Enhancement', 'Waist', 'Horde', 'Belt of the Pitiless Killer'), +(7, 1, 6, 0, 264, 48352, 'Phase 4', 'Shaman', 'Enhancement', 'Legs', 'Both', 'War-Kilt of Triumph'), +(7, 1, 7, 1, 264, 47099, 'Phase 4', 'Shaman', 'Enhancement', 'Feet', 'Alliance', 'Boots of Tremoring Earth'), +(7, 1, 7, 2, 264, 47456, 'Phase 4', 'Shaman', 'Enhancement', 'Feet', 'Horde', 'Sabatons of Tremoring Earth'), +(7, 1, 8, 1, 264, 47143, 'Phase 4', 'Shaman', 'Enhancement', 'Wrists', 'Alliance', 'Bindings of Dark Essence'), +(7, 1, 8, 2, 264, 47467, 'Phase 4', 'Shaman', 'Enhancement', 'Wrists', 'Horde', 'Dark Essence Bindings'), +(7, 1, 9, 0, 264, 48354, 'Phase 4', 'Shaman', 'Enhancement', 'Hands', 'Both', 'Grips of Triumph'), +(7, 1, 10, 1, 264, 47075, 'Phase 4', 'Shaman', 'Enhancement', 'Finger1', 'Alliance', 'Ring of Callous Aggression'), +(7, 1, 10, 2, 264, 47443, 'Phase 4', 'Shaman', 'Enhancement', 'Finger1', 'Horde', 'Band of Callous Aggression'), +(7, 1, 12, 1, 264, 45609, 'Phase 4', 'Shaman', 'Enhancement', 'Trinket1', 'Alliance', 'Comet''s Trail'), +(7, 1, 14, 1, 264, 47552, 'Phase 4', 'Shaman', 'Enhancement', 'Back', 'Alliance', 'Jaina''s Radiance'), +(7, 1, 14, 2, 264, 47551, 'Phase 4', 'Shaman', 'Enhancement', 'Back', 'Horde', 'Aethas'' Intensity'), +(7, 1, 17, 1, 264, 47666, 'Phase 4', 'Shaman', 'Enhancement', 'Ranged', 'Alliance', 'Totem of Electrifying Wind'); + +-- ilvl 290 (Phase 5) +INSERT INTO `playerbots_bis_gear` VALUES +(7, 1, 0, 0, 290, 51242, 'Phase 5', 'Shaman', 'Enhancement', 'Head', 'Both', 'Sanctified Frost Witch''s Faceguard'), +(7, 1, 1, 0, 290, 50633, 'Phase 5', 'Shaman', 'Enhancement', 'Neck', 'Both', 'Sindragosa''s Cruel Claw'), +(7, 1, 2, 0, 290, 51240, 'Phase 5', 'Shaman', 'Enhancement', 'Shoulders', 'Both', 'Sanctified Frost Witch''s Shoulderguards'), +(7, 1, 4, 0, 290, 50689, 'Phase 5', 'Shaman', 'Enhancement', 'Chest', 'Both', 'Carapace of Forgotten Kings'), +(7, 1, 5, 0, 290, 50688, 'Phase 5', 'Shaman', 'Enhancement', 'Waist', 'Both', 'Nerub''ar Stalker''s Cord'), +(7, 1, 6, 0, 290, 51241, 'Phase 5', 'Shaman', 'Enhancement', 'Legs', 'Both', 'Sanctified Frost Witch''s War-Kilt'), +(7, 1, 7, 0, 290, 54577, 'Phase 5', 'Shaman', 'Enhancement', 'Feet', 'Both', 'Returning Footfalls'), +(7, 1, 8, 0, 290, 50655, 'Phase 5', 'Shaman', 'Enhancement', 'Wrists', 'Both', 'Scourge Hunter''s Vambraces'), +(7, 1, 9, 0, 290, 51243, 'Phase 5', 'Shaman', 'Enhancement', 'Hands', 'Both', 'Sanctified Frost Witch''s Grips'), +(7, 1, 10, 0, 290, 50678, 'Phase 5', 'Shaman', 'Enhancement', 'Finger1', 'Both', 'Seal of Many Mouths'), +(7, 1, 11, 0, 290, 50402, 'Phase 5', 'Shaman', 'Enhancement', 'Finger2', 'Both', 'Ashen Band of Endless Vengeance'), +(7, 1, 12, 0, 290, 50706, 'Phase 5', 'Shaman', 'Enhancement', 'Trinket1', 'Both', 'Tiny Abomination in a Jar'), +(7, 1, 13, 0, 290, 54590, 'Phase 5', 'Shaman', 'Enhancement', 'Trinket2', 'Both', 'Sharpened Twilight Scale'), +(7, 1, 14, 0, 290, 50653, 'Phase 5', 'Shaman', 'Enhancement', 'Back', 'Both', 'Shadowvault Slayer''s Cloak'), +(7, 1, 15, 0, 290, 50737, 'Phase 5', 'Shaman', 'Enhancement', 'MainHand', 'Both', 'Havoc''s Call, Blade of Lordaeron Kings'), +(7, 1, 16, 0, 290, 50737, 'Phase 5', 'Shaman', 'Enhancement', 'OffHand', 'Both', 'Havoc''s Call, Blade of Lordaeron Kings'), +(7, 1, 17, 0, 290, 50463, 'Phase 5', 'Shaman', 'Enhancement', 'Ranged', 'Both', 'Totem of the Avalanche'); + +-- Restoration (tab 2) +-- ilvl 66 (Phase 1 (Pre-Raid)) +INSERT INTO `playerbots_bis_gear` VALUES +(7, 2, 0, 0, 66, 13216, 'Phase 1 (Pre-Raid)', 'Shaman', 'Restoration', 'Head', 'Both', 'Crown of the Penitent'), +(7, 2, 1, 0, 66, 18723, 'Phase 1 (Pre-Raid)', 'Shaman', 'Restoration', 'Neck', 'Both', 'Animated Chain Necklace'), +(7, 2, 2, 0, 66, 15061, 'Phase 1 (Pre-Raid)', 'Shaman', 'Restoration', 'Shoulders', 'Both', 'Living Shoulders'), +(7, 2, 4, 0, 66, 13346, 'Phase 1 (Pre-Raid)', 'Shaman', 'Restoration', 'Chest', 'Both', 'Robes of the Exalted'), +(7, 2, 5, 0, 66, 14553, 'Phase 1 (Pre-Raid)', 'Shaman', 'Restoration', 'Waist', 'Both', 'Sash of Mercy'), +(7, 2, 6, 0, 66, 11841, 'Phase 1 (Pre-Raid)', 'Shaman', 'Restoration', 'Legs', 'Both', 'Senior Designer''s Pantaloons'), +(7, 2, 7, 0, 66, 13954, 'Phase 1 (Pre-Raid)', 'Shaman', 'Restoration', 'Feet', 'Both', 'Verdant Footpads'), +(7, 2, 8, 0, 66, 13969, 'Phase 1 (Pre-Raid)', 'Shaman', 'Restoration', 'Wrists', 'Both', 'Loomguard Armbraces'), +(7, 2, 9, 0, 66, 10787, 'Phase 1 (Pre-Raid)', 'Shaman', 'Restoration', 'Hands', 'Both', 'Atal''ai Gloves'), +(7, 2, 10, 0, 66, 16058, 'Phase 1 (Pre-Raid)', 'Shaman', 'Restoration', 'Finger1', 'Both', 'Fordring''s Seal'), +(7, 2, 11, 0, 66, 13178, 'Phase 1 (Pre-Raid)', 'Shaman', 'Restoration', 'Finger2', 'Both', 'Rosewine Circle'), +(7, 2, 12, 0, 66, 12930, 'Phase 1 (Pre-Raid)', 'Shaman', 'Restoration', 'Trinket1', 'Both', 'Briarwood Reed'), +(7, 2, 13, 0, 66, 11819, 'Phase 1 (Pre-Raid)', 'Shaman', 'Restoration', 'Trinket2', 'Both', 'Second Wind'), +(7, 2, 14, 0, 66, 13386, 'Phase 1 (Pre-Raid)', 'Shaman', 'Restoration', 'Back', 'Both', 'Archivist Cape'), +(7, 2, 15, 0, 66, 11923, 'Phase 1 (Pre-Raid)', 'Shaman', 'Restoration', 'MainHand', 'Both', 'The Hammer of Grace'), +(7, 2, 16, 0, 66, 11928, 'Phase 1 (Pre-Raid)', 'Shaman', 'Restoration', 'OffHand', 'Both', 'Thaurissan''s Royal Scepter'); + +-- ilvl 76 (Phase 2 (Pre-Raid)) +INSERT INTO `playerbots_bis_gear` VALUES +(7, 2, 0, 0, 76, 13216, 'Phase 2 (Pre-Raid)', 'Shaman', 'Restoration', 'Head', 'Both', 'Crown of the Penitent'), +(7, 2, 1, 0, 76, 18723, 'Phase 2 (Pre-Raid)', 'Shaman', 'Restoration', 'Neck', 'Both', 'Animated Chain Necklace'), +(7, 2, 2, 0, 76, 18757, 'Phase 2 (Pre-Raid)', 'Shaman', 'Restoration', 'Shoulders', 'Both', 'Diabolic Mantle'), +(7, 2, 4, 0, 76, 13346, 'Phase 2 (Pre-Raid)', 'Shaman', 'Restoration', 'Chest', 'Both', 'Robes of the Exalted'), +(7, 2, 5, 0, 76, 14553, 'Phase 2 (Pre-Raid)', 'Shaman', 'Restoration', 'Waist', 'Both', 'Sash of Mercy'), +(7, 2, 6, 0, 76, 18386, 'Phase 2 (Pre-Raid)', 'Shaman', 'Restoration', 'Legs', 'Both', 'Padre''s Trousers'), +(7, 2, 7, 0, 76, 13954, 'Phase 2 (Pre-Raid)', 'Shaman', 'Restoration', 'Feet', 'Both', 'Verdant Footpads'), +(7, 2, 8, 0, 76, 13969, 'Phase 2 (Pre-Raid)', 'Shaman', 'Restoration', 'Wrists', 'Both', 'Loomguard Armbraces'), +(7, 2, 9, 0, 76, 18527, 'Phase 2 (Pre-Raid)', 'Shaman', 'Restoration', 'Hands', 'Both', 'Harmonious Gauntlets'), +(7, 2, 10, 0, 76, 16058, 'Phase 2 (Pre-Raid)', 'Shaman', 'Restoration', 'Finger1', 'Both', 'Fordring''s Seal'), +(7, 2, 11, 0, 76, 13178, 'Phase 2 (Pre-Raid)', 'Shaman', 'Restoration', 'Finger2', 'Both', 'Rosewine Circle'), +(7, 2, 12, 0, 76, 18371, 'Phase 2 (Pre-Raid)', 'Shaman', 'Restoration', 'Trinket1', 'Both', 'Mindtap Talisman'), +(7, 2, 13, 0, 76, 18371, 'Phase 2 (Pre-Raid)', 'Shaman', 'Restoration', 'Trinket2', 'Both', 'Mindtap Talisman'), +(7, 2, 14, 0, 76, 13386, 'Phase 2 (Pre-Raid)', 'Shaman', 'Restoration', 'Back', 'Both', 'Archivist Cape'), +(7, 2, 15, 0, 76, 11923, 'Phase 2 (Pre-Raid)', 'Shaman', 'Restoration', 'MainHand', 'Both', 'The Hammer of Grace'), +(7, 2, 16, 0, 76, 18523, 'Phase 2 (Pre-Raid)', 'Shaman', 'Restoration', 'OffHand', 'Both', 'Brightly Glowing Stone'); + +-- ilvl 78 (Phase 2) +INSERT INTO `playerbots_bis_gear` VALUES +(7, 2, 0, 0, 78, 16842, 'Phase 2', 'Shaman', 'Restoration', 'Head', 'Both', 'Earthfury Helmet'), +(7, 2, 1, 0, 78, 18723, 'Phase 2', 'Shaman', 'Restoration', 'Neck', 'Both', 'Animated Chain Necklace'), +(7, 2, 2, 0, 78, 18810, 'Phase 2', 'Shaman', 'Restoration', 'Shoulders', 'Both', 'Wild Growth Spaulders'), +(7, 2, 4, 0, 78, 13346, 'Phase 2', 'Shaman', 'Restoration', 'Chest', 'Both', 'Robes of the Exalted'), +(7, 2, 5, 0, 78, 19162, 'Phase 2', 'Shaman', 'Restoration', 'Waist', 'Both', 'Corehound Belt'), +(7, 2, 6, 0, 78, 18875, 'Phase 2', 'Shaman', 'Restoration', 'Legs', 'Both', 'Salamander Scale Pants'), +(7, 2, 7, 0, 78, 13954, 'Phase 2', 'Shaman', 'Restoration', 'Feet', 'Both', 'Verdant Footpads'), +(7, 2, 8, 0, 78, 13969, 'Phase 2', 'Shaman', 'Restoration', 'Wrists', 'Both', 'Loomguard Armbraces'), +(7, 2, 9, 0, 78, 18527, 'Phase 2', 'Shaman', 'Restoration', 'Hands', 'Both', 'Harmonious Gauntlets'), +(7, 2, 10, 0, 78, 19140, 'Phase 2', 'Shaman', 'Restoration', 'Finger1', 'Both', 'Cauterizing Band'), +(7, 2, 11, 0, 78, 13178, 'Phase 2', 'Shaman', 'Restoration', 'Finger2', 'Both', 'Rosewine Circle'), +(7, 2, 12, 0, 78, 17064, 'Phase 2', 'Shaman', 'Restoration', 'Trinket1', 'Both', 'Shard of the Scale'), +(7, 2, 13, 0, 78, 18371, 'Phase 2', 'Shaman', 'Restoration', 'Trinket2', 'Both', 'Mindtap Talisman'), +(7, 2, 14, 0, 78, 13386, 'Phase 2', 'Shaman', 'Restoration', 'Back', 'Both', 'Archivist Cape'), +(7, 2, 15, 0, 78, 17070, 'Phase 2', 'Shaman', 'Restoration', 'MainHand', 'Both', 'Fang of the Mystics'), +(7, 2, 16, 0, 78, 18523, 'Phase 2', 'Shaman', 'Restoration', 'OffHand', 'Both', 'Brightly Glowing Stone'); + +-- ilvl 83 (Phase 3) +INSERT INTO `playerbots_bis_gear` VALUES +(7, 2, 0, 0, 83, 16947, 'Phase 3', 'Shaman', 'Restoration', 'Head', 'Both', 'Helmet of Ten Storms'), +(7, 2, 1, 0, 83, 19371, 'Phase 3', 'Shaman', 'Restoration', 'Neck', 'Both', 'Pendant of the Fallen Dragon'), +(7, 2, 2, 0, 83, 18810, 'Phase 3', 'Shaman', 'Restoration', 'Shoulders', 'Both', 'Wild Growth Spaulders'), +(7, 2, 4, 0, 83, 13346, 'Phase 3', 'Shaman', 'Restoration', 'Chest', 'Both', 'Robes of the Exalted'), +(7, 2, 5, 0, 83, 19162, 'Phase 3', 'Shaman', 'Restoration', 'Waist', 'Both', 'Corehound Belt'), +(7, 2, 6, 0, 83, 18875, 'Phase 3', 'Shaman', 'Restoration', 'Legs', 'Both', 'Salamander Scale Pants'), +(7, 2, 7, 0, 83, 19391, 'Phase 3', 'Shaman', 'Restoration', 'Feet', 'Both', 'Shimmering Geta'), +(7, 2, 8, 0, 83, 16943, 'Phase 3', 'Shaman', 'Restoration', 'Wrists', 'Both', 'Bracers of Ten Storms'), +(7, 2, 9, 0, 83, 16948, 'Phase 3', 'Shaman', 'Restoration', 'Hands', 'Both', 'Gauntlets of Ten Storms'), +(7, 2, 10, 0, 83, 19397, 'Phase 3', 'Shaman', 'Restoration', 'Finger1', 'Both', 'Ring of Blackrock'), +(7, 2, 11, 0, 83, 19382, 'Phase 3', 'Shaman', 'Restoration', 'Finger2', 'Both', 'Pure Elementium Band'), +(7, 2, 12, 0, 83, 17064, 'Phase 3', 'Shaman', 'Restoration', 'Trinket1', 'Both', 'Shard of the Scale'), +(7, 2, 13, 0, 83, 19395, 'Phase 3', 'Shaman', 'Restoration', 'Trinket2', 'Both', 'Rejuvenating Gem'), +(7, 2, 14, 0, 83, 19430, 'Phase 3', 'Shaman', 'Restoration', 'Back', 'Both', 'Shroud of Pure Thought'), +(7, 2, 15, 0, 83, 19347, 'Phase 3', 'Shaman', 'Restoration', 'MainHand', 'Both', 'Claw of Chromaggus'), +(7, 2, 16, 0, 83, 19312, 'Phase 3', 'Shaman', 'Restoration', 'OffHand', 'Both', 'Lei of the Lifegiver'); + +-- ilvl 88 (Phase 5) +INSERT INTO `playerbots_bis_gear` VALUES +(7, 2, 0, 0, 88, 21372, 'Phase 5', 'Shaman', 'Restoration', 'Head', 'Both', 'Stormcaller''s Diadem'), +(7, 2, 1, 0, 88, 21712, 'Phase 5', 'Shaman', 'Restoration', 'Neck', 'Both', 'Amulet of the Fallen God'), +(7, 2, 2, 0, 88, 21376, 'Phase 5', 'Shaman', 'Restoration', 'Shoulders', 'Both', 'Stormcaller''s Pauldrons'), +(7, 2, 4, 0, 88, 21374, 'Phase 5', 'Shaman', 'Restoration', 'Chest', 'Both', 'Stormcaller''s Hauberk'), +(7, 2, 5, 0, 88, 16944, 'Phase 5', 'Shaman', 'Restoration', 'Waist', 'Both', 'Belt of Ten Storms'), +(7, 2, 6, 0, 88, 21375, 'Phase 5', 'Shaman', 'Restoration', 'Legs', 'Both', 'Stormcaller''s Leggings'), +(7, 2, 7, 0, 88, 21373, 'Phase 5', 'Shaman', 'Restoration', 'Feet', 'Both', 'Stormcaller''s Footguards'), +(7, 2, 8, 0, 88, 16943, 'Phase 5', 'Shaman', 'Restoration', 'Wrists', 'Both', 'Bracers of Ten Storms'), +(7, 2, 9, 0, 88, 16948, 'Phase 5', 'Shaman', 'Restoration', 'Hands', 'Both', 'Gauntlets of Ten Storms'), +(7, 2, 10, 0, 88, 21620, 'Phase 5', 'Shaman', 'Restoration', 'Finger1', 'Both', 'Ring of the Martyr'), +(7, 2, 11, 0, 88, 21681, 'Phase 5', 'Shaman', 'Restoration', 'Finger2', 'Both', 'Ring of the Devoured'), +(7, 2, 12, 0, 88, 17064, 'Phase 5', 'Shaman', 'Restoration', 'Trinket1', 'Both', 'Shard of the Scale'), +(7, 2, 13, 0, 88, 19395, 'Phase 5', 'Shaman', 'Restoration', 'Trinket2', 'Both', 'Rejuvenating Gem'), +(7, 2, 14, 0, 88, 21583, 'Phase 5', 'Shaman', 'Restoration', 'Back', 'Both', 'Cloak of Clarity'), +(7, 2, 15, 0, 88, 21839, 'Phase 5', 'Shaman', 'Restoration', 'MainHand', 'Both', 'Scepter of the False Prophet'), +(7, 2, 16, 0, 88, 21610, 'Phase 5', 'Shaman', 'Restoration', 'OffHand', 'Both', 'Wormscale Blocker'), +(7, 2, 17, 0, 88, 22396, 'Phase 5', 'Shaman', 'Restoration', 'Ranged', 'Both', 'Totem of Life'); + +-- ilvl 92 (Phase 6) +INSERT INTO `playerbots_bis_gear` VALUES +(7, 2, 0, 0, 92, 22466, 'Phase 6', 'Shaman', 'Restoration', 'Head', 'Both', 'Earthshatter Headpiece'), +(7, 2, 1, 0, 92, 21712, 'Phase 6', 'Shaman', 'Restoration', 'Neck', 'Both', 'Amulet of the Fallen God'), +(7, 2, 2, 0, 92, 22467, 'Phase 6', 'Shaman', 'Restoration', 'Shoulders', 'Both', 'Earthshatter Spaulders'), +(7, 2, 4, 0, 92, 22464, 'Phase 6', 'Shaman', 'Restoration', 'Chest', 'Both', 'Earthshatter Tunic'), +(7, 2, 5, 0, 92, 22470, 'Phase 6', 'Shaman', 'Restoration', 'Waist', 'Both', 'Earthshatter Girdle'), +(7, 2, 6, 0, 92, 22465, 'Phase 6', 'Shaman', 'Restoration', 'Legs', 'Both', 'Earthshatter Legguards'), +(7, 2, 7, 0, 92, 22468, 'Phase 6', 'Shaman', 'Restoration', 'Feet', 'Both', 'Earthshatter Boots'), +(7, 2, 8, 0, 92, 22471, 'Phase 6', 'Shaman', 'Restoration', 'Wrists', 'Both', 'Earthshatter Wristguards'), +(7, 2, 9, 0, 92, 21619, 'Phase 6', 'Shaman', 'Restoration', 'Hands', 'Both', 'Gloves of the Messiah'), +(7, 2, 10, 0, 92, 21620, 'Phase 6', 'Shaman', 'Restoration', 'Finger1', 'Both', 'Ring of the Martyr'), +(7, 2, 11, 0, 92, 23065, 'Phase 6', 'Shaman', 'Restoration', 'Finger2', 'Both', 'Ring of the Earthshatterer'), +(7, 2, 12, 0, 92, 23027, 'Phase 6', 'Shaman', 'Restoration', 'Trinket1', 'Both', 'Warmth of Forgiveness'), +(7, 2, 13, 0, 92, 19395, 'Phase 6', 'Shaman', 'Restoration', 'Trinket2', 'Both', 'Rejuvenating Gem'), +(7, 2, 14, 0, 92, 21583, 'Phase 6', 'Shaman', 'Restoration', 'Back', 'Both', 'Cloak of Clarity'), +(7, 2, 15, 0, 92, 23056, 'Phase 6', 'Shaman', 'Restoration', 'MainHand', 'Both', 'Hammer of the Twisting Nether'), +(7, 2, 16, 0, 92, 22819, 'Phase 6', 'Shaman', 'Restoration', 'OffHand', 'Both', 'Shield of Condemnation'), +(7, 2, 17, 0, 92, 22396, 'Phase 6', 'Shaman', 'Restoration', 'Ranged', 'Both', 'Totem of Life'); + +-- ilvl 120 (Pre-Raid) +INSERT INTO `playerbots_bis_gear` VALUES +(7, 2, 0, 0, 120, 24264, 'Pre-Raid', 'Shaman', 'Restoration', 'Head', 'Both', 'Whitemend Hood'), +(7, 2, 1, 0, 120, 31691, 'Pre-Raid', 'Shaman', 'Restoration', 'Neck', 'Both', 'Natasha''s Guardian Cord'), +(7, 2, 2, 0, 120, 35395, 'Pre-Raid', 'Shaman', 'Restoration', 'Shoulders', 'Both', 'Seer''s Ringmail Shoulderpads'), +(7, 2, 4, 0, 120, 29522, 'Pre-Raid', 'Shaman', 'Restoration', 'Chest', 'Both', 'Windhawk Hauberk'), +(7, 2, 5, 0, 120, 29524, 'Pre-Raid', 'Shaman', 'Restoration', 'Waist', 'Both', 'Windhawk Belt'), +(7, 2, 6, 0, 120, 24261, 'Pre-Raid', 'Shaman', 'Restoration', 'Legs', 'Both', 'Whitemend Pants'), +(7, 2, 7, 0, 120, 27549, 'Pre-Raid', 'Shaman', 'Restoration', 'Feet', 'Both', 'Wavefury Boots'), +(7, 2, 8, 0, 120, 29523, 'Pre-Raid', 'Shaman', 'Restoration', 'Wrists', 'Both', 'Windhawk Bracers'), +(7, 2, 9, 0, 120, 29506, 'Pre-Raid', 'Shaman', 'Restoration', 'Hands', 'Both', 'Gloves of the Living Touch'), +(7, 2, 10, 0, 120, 28259, 'Pre-Raid', 'Shaman', 'Restoration', 'Finger1', 'Both', 'Cosmic Lifeband'), +(7, 2, 10, 2, 120, 29168, 'Pre-Raid', 'Shaman', 'Restoration', 'Finger1', 'Horde', 'Ancestral Band'), +(7, 2, 12, 0, 120, 29376, 'Pre-Raid', 'Shaman', 'Restoration', 'Trinket1', 'Both', 'Essence of the Martyr'), +(7, 2, 13, 0, 120, 30841, 'Pre-Raid', 'Shaman', 'Restoration', 'Trinket2', 'Both', 'Lower City Prayerbook'), +(7, 2, 14, 0, 120, 24254, 'Pre-Raid', 'Shaman', 'Restoration', 'Back', 'Both', 'White Remedy Cape'), +(7, 2, 15, 0, 120, 23556, 'Pre-Raid', 'Shaman', 'Restoration', 'MainHand', 'Both', 'Hand of Eternity'), +(7, 2, 16, 0, 120, 29267, 'Pre-Raid', 'Shaman', 'Restoration', 'OffHand', 'Both', 'Light-Bearer''s Faith Shield'), +(7, 2, 17, 0, 120, 27544, 'Pre-Raid', 'Shaman', 'Restoration', 'Ranged', 'Both', 'Totem of Spontaneous Regrowth'); + +-- ilvl 125 (Phase 1) +INSERT INTO `playerbots_bis_gear` VALUES +(7, 2, 0, 0, 125, 29035, 'Phase 1', 'Shaman', 'Restoration', 'Head', 'Both', 'Cyclone Faceguard'), +(7, 2, 1, 0, 125, 28609, 'Phase 1', 'Shaman', 'Restoration', 'Neck', 'Both', 'Emberspur Talisman'), +(7, 2, 2, 0, 125, 29037, 'Phase 1', 'Shaman', 'Restoration', 'Shoulders', 'Both', 'Cyclone Shoulderguards'), +(7, 2, 4, 0, 125, 29522, 'Phase 1', 'Shaman', 'Restoration', 'Chest', 'Both', 'Windhawk Hauberk'), +(7, 2, 5, 0, 125, 29524, 'Phase 1', 'Shaman', 'Restoration', 'Waist', 'Both', 'Windhawk Belt'), +(7, 2, 6, 0, 125, 30727, 'Phase 1', 'Shaman', 'Restoration', 'Legs', 'Both', 'Gilded Trousers of Benediction'), +(7, 2, 7, 0, 125, 30737, 'Phase 1', 'Shaman', 'Restoration', 'Feet', 'Both', 'Gold-Leaf Wildboots'), +(7, 2, 8, 0, 125, 29523, 'Phase 1', 'Shaman', 'Restoration', 'Wrists', 'Both', 'Windhawk Bracers'), +(7, 2, 9, 0, 125, 28520, 'Phase 1', 'Shaman', 'Restoration', 'Hands', 'Both', 'Gloves of Centering'), +(7, 2, 10, 0, 125, 28763, 'Phase 1', 'Shaman', 'Restoration', 'Finger1', 'Both', 'Jade Ring of the Everliving'), +(7, 2, 11, 0, 125, 28790, 'Phase 1', 'Shaman', 'Restoration', 'Finger2', 'Both', 'Naaru Lightwarden''s Band'), +(7, 2, 12, 0, 125, 29376, 'Phase 1', 'Shaman', 'Restoration', 'Trinket1', 'Both', 'Essence of the Martyr'), +(7, 2, 13, 0, 125, 28590, 'Phase 1', 'Shaman', 'Restoration', 'Trinket2', 'Both', 'Ribbon of Sacrifice'), +(7, 2, 14, 0, 125, 28765, 'Phase 1', 'Shaman', 'Restoration', 'Back', 'Both', 'Stainless Cloak of the Pure Hearted'), +(7, 2, 15, 0, 125, 28771, 'Phase 1', 'Shaman', 'Restoration', 'MainHand', 'Both', 'Light''s Justice'), +(7, 2, 16, 0, 125, 29458, 'Phase 1', 'Shaman', 'Restoration', 'OffHand', 'Both', 'Aegis of the Vindicator'), +(7, 2, 17, 0, 125, 28523, 'Phase 1', 'Shaman', 'Restoration', 'Ranged', 'Both', 'Totem of Healing Rains'); + +-- ilvl 141 (Phase 2) +INSERT INTO `playerbots_bis_gear` VALUES +(7, 2, 0, 0, 141, 32475, 'Phase 2', 'Shaman', 'Restoration', 'Head', 'Both', 'Living Replicator Specs'), +(7, 2, 1, 0, 141, 30018, 'Phase 2', 'Shaman', 'Restoration', 'Neck', 'Both', 'Lord Sanguinar''s Claim'), +(7, 2, 2, 0, 141, 30168, 'Phase 2', 'Shaman', 'Restoration', 'Shoulders', 'Both', 'Cataclysm Shoulderguards'), +(7, 2, 4, 0, 141, 30164, 'Phase 2', 'Shaman', 'Restoration', 'Chest', 'Both', 'Cataclysm Chestguard'), +(7, 2, 5, 0, 141, 21873, 'Phase 2', 'Shaman', 'Restoration', 'Waist', 'Both', 'Primal Mooncloth Belt'), +(7, 2, 6, 0, 141, 30727, 'Phase 2', 'Shaman', 'Restoration', 'Legs', 'Both', 'Gilded Trousers of Benediction'), +(7, 2, 7, 0, 141, 30737, 'Phase 2', 'Shaman', 'Restoration', 'Feet', 'Both', 'Gold-Leaf Wildboots'), +(7, 2, 8, 0, 141, 30047, 'Phase 2', 'Shaman', 'Restoration', 'Wrists', 'Both', 'Blackfathom Warbands'), +(7, 2, 9, 0, 141, 29976, 'Phase 2', 'Shaman', 'Restoration', 'Hands', 'Both', 'Worldstorm Gauntlets'), +(7, 2, 10, 0, 141, 28763, 'Phase 2', 'Shaman', 'Restoration', 'Finger1', 'Both', 'Jade Ring of the Everliving'), +(7, 2, 11, 0, 141, 30736, 'Phase 2', 'Shaman', 'Restoration', 'Finger2', 'Both', 'Ring of Flowing Light'), +(7, 2, 12, 0, 141, 29376, 'Phase 2', 'Shaman', 'Restoration', 'Trinket1', 'Both', 'Essence of the Martyr'), +(7, 2, 13, 0, 141, 28590, 'Phase 2', 'Shaman', 'Restoration', 'Trinket2', 'Both', 'Ribbon of Sacrifice'), +(7, 2, 14, 0, 141, 29989, 'Phase 2', 'Shaman', 'Restoration', 'Back', 'Both', 'Sunshower Light Cloak'), +(7, 2, 15, 0, 141, 30108, 'Phase 2', 'Shaman', 'Restoration', 'MainHand', 'Both', 'Lightfathom Scepter'), +(7, 2, 16, 0, 141, 29458, 'Phase 2', 'Shaman', 'Restoration', 'OffHand', 'Both', 'Aegis of the Vindicator'), +(7, 2, 17, 0, 141, 28523, 'Phase 2', 'Shaman', 'Restoration', 'Ranged', 'Both', 'Totem of Healing Rains'); + +-- ilvl 156 (Phase 3) +INSERT INTO `playerbots_bis_gear` VALUES +(7, 2, 0, 0, 156, 31012, 'Phase 3', 'Shaman', 'Restoration', 'Head', 'Both', 'Skyshatter Helmet'), +(7, 2, 1, 0, 156, 32370, 'Phase 3', 'Shaman', 'Restoration', 'Neck', 'Both', 'Nadina''s Pendant of Purity'), +(7, 2, 2, 0, 156, 31022, 'Phase 3', 'Shaman', 'Restoration', 'Shoulders', 'Both', 'Skyshatter Shoulderpads'), +(7, 2, 4, 0, 156, 31016, 'Phase 3', 'Shaman', 'Restoration', 'Chest', 'Both', 'Skyshatter Chestguard'), +(7, 2, 5, 0, 156, 32258, 'Phase 3', 'Shaman', 'Restoration', 'Waist', 'Both', 'Naturalist''s Preserving Cinch'), +(7, 2, 6, 0, 156, 31019, 'Phase 3', 'Shaman', 'Restoration', 'Legs', 'Both', 'Skyshatter Leggings'), +(7, 2, 7, 0, 156, 30737, 'Phase 3', 'Shaman', 'Restoration', 'Feet', 'Both', 'Gold-Leaf Wildboots'), +(7, 2, 8, 0, 156, 32584, 'Phase 3', 'Shaman', 'Restoration', 'Wrists', 'Both', 'Swiftheal Wraps'), +(7, 2, 9, 0, 156, 32328, 'Phase 3', 'Shaman', 'Restoration', 'Hands', 'Both', 'Botanist''s Gloves of Growth'), +(7, 2, 10, 0, 156, 32528, 'Phase 3', 'Shaman', 'Restoration', 'Finger1', 'Both', 'Blessed Band of Karabor'), +(7, 2, 11, 0, 156, 32528, 'Phase 3', 'Shaman', 'Restoration', 'Finger2', 'Both', 'Blessed Band of Karabor'), +(7, 2, 12, 0, 156, 29376, 'Phase 3', 'Shaman', 'Restoration', 'Trinket1', 'Both', 'Essence of the Martyr'), +(7, 2, 13, 0, 156, 32496, 'Phase 3', 'Shaman', 'Restoration', 'Trinket2', 'Both', 'Memento of Tyrande'), +(7, 2, 14, 0, 156, 32524, 'Phase 3', 'Shaman', 'Restoration', 'Back', 'Both', 'Shroud of the Highborne'), +(7, 2, 15, 0, 156, 32500, 'Phase 3', 'Shaman', 'Restoration', 'MainHand', 'Both', 'Crystal Spire of Karabor'), +(7, 2, 16, 0, 156, 30882, 'Phase 3', 'Shaman', 'Restoration', 'OffHand', 'Both', 'Bastion of Light'), +(7, 2, 17, 0, 156, 28523, 'Phase 3', 'Shaman', 'Restoration', 'Ranged', 'Both', 'Totem of Healing Rains'); + +-- ilvl 164 (Phase 4) +INSERT INTO `playerbots_bis_gear` VALUES +(7, 2, 0, 0, 164, 31012, 'Phase 4', 'Shaman', 'Restoration', 'Head', 'Both', 'Skyshatter Helmet'), +(7, 2, 1, 0, 164, 33281, 'Phase 4', 'Shaman', 'Restoration', 'Neck', 'Both', 'Brooch of Nature''s Mercy'), +(7, 2, 2, 0, 164, 31022, 'Phase 4', 'Shaman', 'Restoration', 'Shoulders', 'Both', 'Skyshatter Shoulderpads'), +(7, 2, 4, 0, 164, 31016, 'Phase 4', 'Shaman', 'Restoration', 'Chest', 'Both', 'Skyshatter Chestguard'), +(7, 2, 5, 0, 164, 32258, 'Phase 4', 'Shaman', 'Restoration', 'Waist', 'Both', 'Naturalist''s Preserving Cinch'), +(7, 2, 6, 0, 164, 31019, 'Phase 4', 'Shaman', 'Restoration', 'Legs', 'Both', 'Skyshatter Leggings'), +(7, 2, 7, 0, 164, 33471, 'Phase 4', 'Shaman', 'Restoration', 'Feet', 'Both', 'Two-Toed Sandals'), +(7, 2, 8, 0, 164, 32584, 'Phase 4', 'Shaman', 'Restoration', 'Wrists', 'Both', 'Swiftheal Wraps'), +(7, 2, 9, 0, 164, 32328, 'Phase 4', 'Shaman', 'Restoration', 'Hands', 'Both', 'Botanist''s Gloves of Growth'), +(7, 2, 10, 0, 164, 32528, 'Phase 4', 'Shaman', 'Restoration', 'Finger1', 'Both', 'Blessed Band of Karabor'), +(7, 2, 11, 0, 164, 32528, 'Phase 4', 'Shaman', 'Restoration', 'Finger2', 'Both', 'Blessed Band of Karabor'), +(7, 2, 12, 0, 164, 29376, 'Phase 4', 'Shaman', 'Restoration', 'Trinket1', 'Both', 'Essence of the Martyr'), +(7, 2, 13, 0, 164, 32496, 'Phase 4', 'Shaman', 'Restoration', 'Trinket2', 'Both', 'Memento of Tyrande'), +(7, 2, 14, 0, 164, 32524, 'Phase 4', 'Shaman', 'Restoration', 'Back', 'Both', 'Shroud of the Highborne'), +(7, 2, 15, 0, 164, 32500, 'Phase 4', 'Shaman', 'Restoration', 'MainHand', 'Both', 'Crystal Spire of Karabor'), +(7, 2, 16, 0, 164, 30882, 'Phase 4', 'Shaman', 'Restoration', 'OffHand', 'Both', 'Bastion of Light'), +(7, 2, 17, 0, 164, 28523, 'Phase 4', 'Shaman', 'Restoration', 'Ranged', 'Both', 'Totem of Healing Rains'); + +-- ilvl 200 (Pre-Raid) +INSERT INTO `playerbots_bis_gear` VALUES +(7, 2, 0, 0, 200, 42555, 'Pre-Raid', 'Shaman', 'Restoration', 'Head', 'Both', 'Electroflux Sight Enhancers'), +(7, 2, 1, 0, 200, 42647, 'Pre-Raid', 'Shaman', 'Restoration', 'Neck', 'Both', 'Titanium Spellshock Necklace'), +(7, 2, 2, 0, 200, 37875, 'Pre-Raid', 'Shaman', 'Restoration', 'Shoulders', 'Both', 'Spaulders of the Violet Hold'), +(7, 2, 4, 0, 200, 43461, 'Pre-Raid', 'Shaman', 'Restoration', 'Chest', 'Both', 'Revenant''s Breastplate'), +(7, 2, 5, 0, 200, 37855, 'Pre-Raid', 'Shaman', 'Restoration', 'Waist', 'Both', 'Mail Girdle of the Audient Earth'), +(7, 2, 6, 0, 200, 44305, 'Pre-Raid', 'Shaman', 'Restoration', 'Legs', 'Both', 'Kilt of Dark Mercy'), +(7, 2, 7, 0, 200, 43469, 'Pre-Raid', 'Shaman', 'Restoration', 'Feet', 'Both', 'Revenant''s Treads'), +(7, 2, 8, 0, 200, 37788, 'Pre-Raid', 'Shaman', 'Restoration', 'Wrists', 'Both', 'Limb Regeneration Bracers'), +(7, 2, 9, 0, 200, 44204, 'Pre-Raid', 'Shaman', 'Restoration', 'Hands', 'Both', 'Grips of Fierce Pronouncements'), +(7, 2, 10, 0, 200, 37192, 'Pre-Raid', 'Shaman', 'Restoration', 'Finger1', 'Both', 'Annhylde''s Ring'), +(7, 2, 12, 0, 200, 40685, 'Pre-Raid', 'Shaman', 'Restoration', 'Trinket1', 'Both', 'The Egg of Mortal Essence'), +(7, 2, 14, 0, 200, 41609, 'Pre-Raid', 'Shaman', 'Restoration', 'Back', 'Both', 'Wispcloak'), +(7, 2, 15, 0, 200, 37169, 'Pre-Raid', 'Shaman', 'Restoration', 'MainHand', 'Both', 'War Mace of Unrequited Love'), +(7, 2, 17, 0, 200, 40709, 'Pre-Raid', 'Shaman', 'Restoration', 'Ranged', 'Both', 'Totem of Forest Growth'); + +-- ilvl 224 (Phase 1) +INSERT INTO `playerbots_bis_gear` VALUES +(7, 2, 0, 0, 224, 40510, 'Phase 1', 'Shaman', 'Restoration', 'Head', 'Both', 'Valorous Earthshatter Headpiece'), +(7, 2, 1, 0, 224, 44662, 'Phase 1', 'Shaman', 'Restoration', 'Neck', 'Both', 'Life-Binder''s Locket'), +(7, 2, 2, 0, 224, 40513, 'Phase 1', 'Shaman', 'Restoration', 'Shoulders', 'Both', 'Valorous Earthshatter Spaulders'), +(7, 2, 4, 0, 224, 40508, 'Phase 1', 'Shaman', 'Restoration', 'Chest', 'Both', 'Valorous Earthshatter Tunic'), +(7, 2, 5, 0, 224, 40327, 'Phase 1', 'Shaman', 'Restoration', 'Waist', 'Both', 'Girdle of Recuperation'), +(7, 2, 6, 0, 224, 40512, 'Phase 1', 'Shaman', 'Restoration', 'Legs', 'Both', 'Valorous Earthshatter Legguards'), +(7, 2, 7, 0, 224, 40519, 'Phase 1', 'Shaman', 'Restoration', 'Feet', 'Both', 'Footsteps of Malygos'), +(7, 2, 8, 0, 224, 40324, 'Phase 1', 'Shaman', 'Restoration', 'Wrists', 'Both', 'Bands of Mutual Respect'), +(7, 2, 9, 0, 224, 40564, 'Phase 1', 'Shaman', 'Restoration', 'Hands', 'Both', 'Winter Spectacle Gloves'), +(7, 2, 10, 0, 224, 40375, 'Phase 1', 'Shaman', 'Restoration', 'Finger1', 'Both', 'Ring of Decaying Beauty'), +(7, 2, 12, 0, 224, 40258, 'Phase 1', 'Shaman', 'Restoration', 'Trinket1', 'Both', 'Forethought Talisman'), +(7, 2, 14, 0, 224, 44005, 'Phase 1', 'Shaman', 'Restoration', 'Back', 'Both', 'Pennant Cloak'), +(7, 2, 17, 0, 224, 39728, 'Phase 1', 'Shaman', 'Restoration', 'Ranged', 'Both', 'Totem of Misery'); + +-- ilvl 245 (Phase 2) +INSERT INTO `playerbots_bis_gear` VALUES +(7, 2, 0, 0, 245, 46201, 'Phase 2', 'Shaman', 'Restoration', 'Head', 'Both', 'Conqueror''s Worldbreaker Headpiece'), +(7, 2, 1, 0, 245, 45443, 'Phase 2', 'Shaman', 'Restoration', 'Neck', 'Both', 'Charm of Meticulous Timing'), +(7, 2, 2, 0, 245, 46204, 'Phase 2', 'Shaman', 'Restoration', 'Shoulders', 'Both', 'Conqueror''s Worldbreaker Spaulders'), +(7, 2, 4, 0, 245, 45867, 'Phase 2', 'Shaman', 'Restoration', 'Chest', 'Both', 'Breastplate of the Stoneshaper'), +(7, 2, 5, 0, 245, 45151, 'Phase 2', 'Shaman', 'Restoration', 'Waist', 'Both', 'Belt of the Fallen Wyrm'), +(7, 2, 6, 0, 245, 46202, 'Phase 2', 'Shaman', 'Restoration', 'Legs', 'Both', 'Conqueror''s Worldbreaker Legguards'), +(7, 2, 7, 0, 245, 45537, 'Phase 2', 'Shaman', 'Restoration', 'Feet', 'Both', 'Treads of the False Oracle'), +(7, 2, 8, 0, 245, 45460, 'Phase 2', 'Shaman', 'Restoration', 'Wrists', 'Both', 'Bindings of Winter Gale'), +(7, 2, 9, 0, 245, 46199, 'Phase 2', 'Shaman', 'Restoration', 'Hands', 'Both', 'Conqueror''s Worldbreaker Handguards'), +(7, 2, 10, 0, 245, 45614, 'Phase 2', 'Shaman', 'Restoration', 'Finger1', 'Both', 'Starshine Circle'), +(7, 2, 12, 0, 245, 40432, 'Phase 2', 'Shaman', 'Restoration', 'Trinket1', 'Both', 'Illustration of the Dragon Soul'), +(7, 2, 14, 0, 245, 44005, 'Phase 2', 'Shaman', 'Restoration', 'Back', 'Both', 'Pennant Cloak'), +(7, 2, 15, 0, 245, 46017, 'Phase 2', 'Shaman', 'Restoration', 'MainHand', 'Both', 'Val''anyr, Hammer of Ancient Kings'), +(7, 2, 17, 0, 245, 45114, 'Phase 2', 'Shaman', 'Restoration', 'Ranged', 'Both', 'Steamcaller''s Totem'); + +-- ilvl 258 (Phase 3) +INSERT INTO `playerbots_bis_gear` VALUES +(7, 2, 0, 0, 258, 46201, 'Phase 3', 'Shaman', 'Restoration', 'Head', 'Both', 'Conqueror''s Worldbreaker Headpiece'), +(7, 2, 1, 1, 258, 47144, 'Phase 3', 'Shaman', 'Restoration', 'Neck', 'Alliance', 'Wail of the Val''kyr'), +(7, 2, 1, 2, 258, 47468, 'Phase 3', 'Shaman', 'Restoration', 'Neck', 'Horde', 'Cry of the Val''kyr'), +(7, 2, 2, 0, 258, 46204, 'Phase 3', 'Shaman', 'Restoration', 'Shoulders', 'Both', 'Conqueror''s Worldbreaker Spaulders'), +(7, 2, 4, 0, 258, 46198, 'Phase 3', 'Shaman', 'Restoration', 'Chest', 'Both', 'Conqueror''s Worldbreaker Tunic'), +(7, 2, 5, 0, 258, 46991, 'Phase 3', 'Shaman', 'Restoration', 'Waist', 'Both', 'Belt of the Ice Burrower'), +(7, 2, 6, 1, 258, 47190, 'Phase 3', 'Shaman', 'Restoration', 'Legs', 'Alliance', 'Legwraps of the Awakening'), +(7, 2, 6, 2, 258, 47479, 'Phase 3', 'Shaman', 'Restoration', 'Legs', 'Horde', 'Leggings of the Awakening'), +(7, 2, 7, 1, 258, 47099, 'Phase 3', 'Shaman', 'Restoration', 'Feet', 'Alliance', 'Boots of Tremoring Earth'), +(7, 2, 7, 2, 258, 47456, 'Phase 3', 'Shaman', 'Restoration', 'Feet', 'Horde', 'Sabatons of Tremoring Earth'), +(7, 2, 8, 0, 258, 45460, 'Phase 3', 'Shaman', 'Restoration', 'Wrists', 'Both', 'Bindings of Winter Gale'), +(7, 2, 9, 0, 258, 46199, 'Phase 3', 'Shaman', 'Restoration', 'Hands', 'Both', 'Conqueror''s Worldbreaker Handguards'), +(7, 2, 10, 1, 258, 47224, 'Phase 3', 'Shaman', 'Restoration', 'Finger1', 'Alliance', 'Ring of the Darkmender'), +(7, 2, 10, 2, 258, 47439, 'Phase 3', 'Shaman', 'Restoration', 'Finger1', 'Horde', 'Circle of the Darkmender'), +(7, 2, 12, 1, 258, 47059, 'Phase 3', 'Shaman', 'Restoration', 'Trinket1', 'Alliance', 'Solace of the Defeated'), +(7, 2, 12, 2, 258, 47432, 'Phase 3', 'Shaman', 'Restoration', 'Trinket1', 'Horde', 'Solace of the Fallen'), +(7, 2, 14, 1, 258, 47552, 'Phase 3', 'Shaman', 'Restoration', 'Back', 'Alliance', 'Jaina''s Radiance'), +(7, 2, 14, 2, 258, 47551, 'Phase 3', 'Shaman', 'Restoration', 'Back', 'Horde', 'Aethas'' Intensity'), +(7, 2, 15, 0, 258, 46017, 'Phase 3', 'Shaman', 'Restoration', 'MainHand', 'Both', 'Val''anyr, Hammer of Ancient Kings'); + +-- ilvl 264 (Phase 4) +INSERT INTO `playerbots_bis_gear` VALUES +(7, 2, 0, 0, 264, 51247, 'Phase 4', 'Shaman', 'Restoration', 'Head', 'Both', 'Sanctified Frost Witch''s Headpiece'), +(7, 2, 1, 0, 264, 50724, 'Phase 4', 'Shaman', 'Restoration', 'Neck', 'Both', 'Blood Queen''s Crimson Choker'), +(7, 2, 2, 0, 264, 51245, 'Phase 4', 'Shaman', 'Restoration', 'Shoulders', 'Both', 'Sanctified Frost Witch''s Spaulders'), +(7, 2, 4, 0, 264, 51249, 'Phase 4', 'Shaman', 'Restoration', 'Chest', 'Both', 'Sanctified Frost Witch''s Tunic'), +(7, 2, 5, 0, 264, 50613, 'Phase 4', 'Shaman', 'Restoration', 'Waist', 'Both', 'Crushing Coldwraith Belt'), +(7, 2, 6, 0, 264, 51246, 'Phase 4', 'Shaman', 'Restoration', 'Legs', 'Both', 'Sanctified Frost Witch''s Legguards'), +(7, 2, 7, 0, 264, 50699, 'Phase 4', 'Shaman', 'Restoration', 'Feet', 'Both', 'Plague Scientist''s Boots'), +(7, 2, 8, 0, 264, 50687, 'Phase 4', 'Shaman', 'Restoration', 'Wrists', 'Both', 'Bloodsunder''s Bracers'), +(7, 2, 9, 0, 264, 50703, 'Phase 4', 'Shaman', 'Restoration', 'Hands', 'Both', 'Unclean Surgical Gloves'), +(7, 2, 10, 0, 264, 50400, 'Phase 4', 'Shaman', 'Restoration', 'Finger1', 'Both', 'Ashen Band of Endless Wisdom'), +(7, 2, 12, 1, 264, 47059, 'Phase 4', 'Shaman', 'Restoration', 'Trinket1', 'Alliance', 'Solace of the Defeated'), +(7, 2, 12, 2, 264, 47432, 'Phase 4', 'Shaman', 'Restoration', 'Trinket1', 'Horde', 'Solace of the Fallen'), +(7, 2, 14, 0, 264, 50628, 'Phase 4', 'Shaman', 'Restoration', 'Back', 'Both', 'Frostbinder''s Shredded Cape'), +(7, 2, 15, 0, 264, 46017, 'Phase 4', 'Shaman', 'Restoration', 'MainHand', 'Both', 'Val''anyr, Hammer of Ancient Kings'), +(7, 2, 17, 0, 264, 50464, 'Phase 4', 'Shaman', 'Restoration', 'Ranged', 'Both', 'Totem of the Surging Sea'); + +-- ilvl 290 (Phase 5) +INSERT INTO `playerbots_bis_gear` VALUES +(7, 2, 0, 0, 290, 51247, 'Phase 5', 'Shaman', 'Restoration', 'Head', 'Both', 'Sanctified Frost Witch''s Headpiece'), +(7, 2, 1, 0, 290, 50182, 'Phase 5', 'Shaman', 'Restoration', 'Neck', 'Both', 'Blood Queen''s Crimson Choker'), +(7, 2, 2, 0, 290, 51245, 'Phase 5', 'Shaman', 'Restoration', 'Shoulders', 'Both', 'Sanctified Frost Witch''s Spaulders'), +(7, 2, 4, 0, 290, 51249, 'Phase 5', 'Shaman', 'Restoration', 'Chest', 'Both', 'Sanctified Frost Witch''s Tunic'), +(7, 2, 5, 0, 290, 54587, 'Phase 5', 'Shaman', 'Restoration', 'Waist', 'Both', 'Split Shape Belt'), +(7, 2, 6, 0, 290, 51246, 'Phase 5', 'Shaman', 'Restoration', 'Legs', 'Both', 'Sanctified Frost Witch''s Legguards'), +(7, 2, 7, 0, 290, 50699, 'Phase 5', 'Shaman', 'Restoration', 'Feet', 'Both', 'Plague Scientist''s Boots'), +(7, 2, 8, 0, 290, 50687, 'Phase 5', 'Shaman', 'Restoration', 'Wrists', 'Both', 'Bloodsunder''s Bracers'), +(7, 2, 9, 0, 290, 50703, 'Phase 5', 'Shaman', 'Restoration', 'Hands', 'Both', 'Unclean Surgical Gloves'), +(7, 2, 10, 0, 290, 50664, 'Phase 5', 'Shaman', 'Restoration', 'Finger1', 'Both', 'Ring of Rapid Ascent'), +(7, 2, 11, 0, 290, 50400, 'Phase 5', 'Shaman', 'Restoration', 'Finger2', 'Both', 'Ashen Band of Endless Wisdom'), +(7, 2, 12, 0, 290, 50366, 'Phase 5', 'Shaman', 'Restoration', 'Trinket1', 'Both', 'Althor''s Abacus'), +(7, 2, 13, 0, 290, 54589, 'Phase 5', 'Shaman', 'Restoration', 'Trinket2', 'Both', 'Glowing Twilight Scale'), +(7, 2, 14, 0, 290, 54583, 'Phase 5', 'Shaman', 'Restoration', 'Back', 'Both', 'Cloak of Burning Dusk'), +(7, 2, 15, 0, 290, 46017, 'Phase 5', 'Shaman', 'Restoration', 'MainHand', 'Both', 'Val''anyr, Hammer of Ancient Kings'), +(7, 2, 16, 0, 290, 50616, 'Phase 5', 'Shaman', 'Restoration', 'OffHand', 'Both', 'Bulwark of Smouldering Steel'), +(7, 2, 17, 0, 290, 50458, 'Phase 5', 'Shaman', 'Restoration', 'Ranged', 'Both', 'Bizuri''s Totem of Shattered Ice'); + + +-- ============================================================ +-- Mage (8) +-- ============================================================ +-- Arcane (tab 0) +-- ilvl 66 (Phase 1 (Pre-Raid)) +INSERT INTO `playerbots_bis_gear` VALUES +(8, 0, 0, 0, 66, 10504, 'Phase 1 (Pre-Raid)', 'Mage', 'Arcane', 'Head', 'Both', 'Green Lens'), +(8, 0, 1, 0, 66, 12103, 'Phase 1 (Pre-Raid)', 'Mage', 'Arcane', 'Neck', 'Both', 'Star of Mystaria'), +(8, 0, 2, 0, 66, 11782, 'Phase 1 (Pre-Raid)', 'Mage', 'Arcane', 'Shoulders', 'Both', 'Boreal Mantle'), +(8, 0, 4, 0, 66, 14152, 'Phase 1 (Pre-Raid)', 'Mage', 'Arcane', 'Chest', 'Both', 'Robe of the Archmage'), +(8, 0, 5, 0, 66, 11662, 'Phase 1 (Pre-Raid)', 'Mage', 'Arcane', 'Waist', 'Both', 'Ban''thok Sash'), +(8, 0, 6, 0, 66, 13170, 'Phase 1 (Pre-Raid)', 'Mage', 'Arcane', 'Legs', 'Both', 'Skyshroud Leggings'), +(8, 0, 7, 0, 66, 11822, 'Phase 1 (Pre-Raid)', 'Mage', 'Arcane', 'Feet', 'Both', 'Omnicast Boots'), +(8, 0, 8, 0, 66, 11766, 'Phase 1 (Pre-Raid)', 'Mage', 'Arcane', 'Wrists', 'Both', 'Flameweave Cuffs'), +(8, 0, 9, 0, 66, 13253, 'Phase 1 (Pre-Raid)', 'Mage', 'Arcane', 'Hands', 'Both', 'Hands of Power'), +(8, 0, 10, 0, 66, 942, 'Phase 1 (Pre-Raid)', 'Mage', 'Arcane', 'Finger1', 'Both', 'Freezing Band'), +(8, 0, 11, 0, 66, 942, 'Phase 1 (Pre-Raid)', 'Mage', 'Arcane', 'Finger2', 'Both', 'Freezing Band'), +(8, 0, 12, 0, 66, 12930, 'Phase 1 (Pre-Raid)', 'Mage', 'Arcane', 'Trinket1', 'Both', 'Briarwood Reed'), +(8, 0, 13, 0, 66, 13968, 'Phase 1 (Pre-Raid)', 'Mage', 'Arcane', 'Trinket2', 'Both', 'Eye of the Beast'), +(8, 0, 14, 0, 66, 13386, 'Phase 1 (Pre-Raid)', 'Mage', 'Arcane', 'Back', 'Both', 'Archivist Cape'), +(8, 0, 15, 0, 66, 13964, 'Phase 1 (Pre-Raid)', 'Mage', 'Arcane', 'MainHand', 'Both', 'Witchblade'), +(8, 0, 16, 0, 66, 10796, 'Phase 1 (Pre-Raid)', 'Mage', 'Arcane', 'OffHand', 'Both', 'Drakestone'), +(8, 0, 17, 0, 66, 13938, 'Phase 1 (Pre-Raid)', 'Mage', 'Arcane', 'Ranged', 'Both', 'Bonecreeper Stylus'); + +-- ilvl 76 (Phase 2 (Pre-Raid)) +INSERT INTO `playerbots_bis_gear` VALUES +(8, 0, 0, 0, 76, 23318, 'Phase 2 (Pre-Raid)', 'Mage', 'Arcane', 'Head', 'Both', 'Lieutenant Commander''s Silk Cowl'), +(8, 0, 1, 0, 76, 12103, 'Phase 2 (Pre-Raid)', 'Mage', 'Arcane', 'Neck', 'Both', 'Star of Mystaria'), +(8, 0, 2, 0, 76, 23319, 'Phase 2 (Pre-Raid)', 'Mage', 'Arcane', 'Shoulders', 'Both', 'Lieutenant Commander''s Silk Mantle'), +(8, 0, 4, 0, 76, 14152, 'Phase 2 (Pre-Raid)', 'Mage', 'Arcane', 'Chest', 'Both', 'Robe of the Archmage'), +(8, 0, 5, 0, 76, 11662, 'Phase 2 (Pre-Raid)', 'Mage', 'Arcane', 'Waist', 'Both', 'Ban''thok Sash'), +(8, 0, 6, 0, 76, 23304, 'Phase 2 (Pre-Raid)', 'Mage', 'Arcane', 'Legs', 'Both', 'Knight-Captain''s Silk Legguards'), +(8, 0, 7, 0, 76, 23291, 'Phase 2 (Pre-Raid)', 'Mage', 'Arcane', 'Feet', 'Both', 'Knight-Lieutenant''s Silk Walkers'), +(8, 0, 8, 0, 76, 11766, 'Phase 2 (Pre-Raid)', 'Mage', 'Arcane', 'Wrists', 'Both', 'Flameweave Cuffs'), +(8, 0, 9, 0, 76, 13253, 'Phase 2 (Pre-Raid)', 'Mage', 'Arcane', 'Hands', 'Both', 'Hands of Power'), +(8, 0, 10, 0, 76, 942, 'Phase 2 (Pre-Raid)', 'Mage', 'Arcane', 'Finger1', 'Both', 'Freezing Band'), +(8, 0, 11, 0, 76, 942, 'Phase 2 (Pre-Raid)', 'Mage', 'Arcane', 'Finger2', 'Both', 'Freezing Band'), +(8, 0, 12, 0, 76, 12930, 'Phase 2 (Pre-Raid)', 'Mage', 'Arcane', 'Trinket1', 'Both', 'Briarwood Reed'), +(8, 0, 13, 0, 76, 13968, 'Phase 2 (Pre-Raid)', 'Mage', 'Arcane', 'Trinket2', 'Both', 'Eye of the Beast'), +(8, 0, 14, 0, 76, 13386, 'Phase 2 (Pre-Raid)', 'Mage', 'Arcane', 'Back', 'Both', 'Archivist Cape'), +(8, 0, 15, 0, 76, 13964, 'Phase 2 (Pre-Raid)', 'Mage', 'Arcane', 'MainHand', 'Both', 'Witchblade'), +(8, 0, 16, 0, 76, 10796, 'Phase 2 (Pre-Raid)', 'Mage', 'Arcane', 'OffHand', 'Both', 'Drakestone'), +(8, 0, 17, 0, 76, 13938, 'Phase 2 (Pre-Raid)', 'Mage', 'Arcane', 'Ranged', 'Both', 'Bonecreeper Stylus'); + +-- ilvl 78 (Phase 2) +INSERT INTO `playerbots_bis_gear` VALUES +(8, 0, 0, 0, 78, 16914, 'Phase 2', 'Mage', 'Arcane', 'Head', 'Both', 'Netherwind Crown'), +(8, 0, 1, 0, 78, 18814, 'Phase 2', 'Mage', 'Arcane', 'Neck', 'Both', 'Choker of the Fire Lord'), +(8, 0, 2, 0, 78, 23319, 'Phase 2', 'Mage', 'Arcane', 'Shoulders', 'Both', 'Lieutenant Commander''s Silk Mantle'), +(8, 0, 4, 0, 78, 14152, 'Phase 2', 'Mage', 'Arcane', 'Chest', 'Both', 'Robe of the Archmage'), +(8, 0, 5, 0, 78, 19136, 'Phase 2', 'Mage', 'Arcane', 'Waist', 'Both', 'Mana Igniting Cord'), +(8, 0, 6, 0, 78, 16915, 'Phase 2', 'Mage', 'Arcane', 'Legs', 'Both', 'Netherwind Pants'), +(8, 0, 7, 0, 78, 23291, 'Phase 2', 'Mage', 'Arcane', 'Feet', 'Both', 'Knight-Lieutenant''s Silk Walkers'), +(8, 0, 8, 0, 78, 11766, 'Phase 2', 'Mage', 'Arcane', 'Wrists', 'Both', 'Flameweave Cuffs'), +(8, 0, 9, 0, 78, 13253, 'Phase 2', 'Mage', 'Arcane', 'Hands', 'Both', 'Hands of Power'), +(8, 0, 10, 0, 78, 19147, 'Phase 2', 'Mage', 'Arcane', 'Finger1', 'Both', 'Ring of Spell Power'), +(8, 0, 11, 0, 78, 19147, 'Phase 2', 'Mage', 'Arcane', 'Finger2', 'Both', 'Ring of Spell Power'), +(8, 0, 12, 0, 78, 12930, 'Phase 2', 'Mage', 'Arcane', 'Trinket1', 'Both', 'Briarwood Reed'), +(8, 0, 13, 0, 78, 18820, 'Phase 2', 'Mage', 'Arcane', 'Trinket2', 'Both', 'Talisman of Ephemeral Power'), +(8, 0, 14, 0, 78, 13386, 'Phase 2', 'Mage', 'Arcane', 'Back', 'Both', 'Archivist Cape'), +(8, 0, 15, 0, 78, 17103, 'Phase 2', 'Mage', 'Arcane', 'MainHand', 'Both', 'Azuresong Mageblade'), +(8, 0, 16, 0, 78, 10796, 'Phase 2', 'Mage', 'Arcane', 'OffHand', 'Both', 'Drakestone'), +(8, 0, 17, 0, 78, 19130, 'Phase 2', 'Mage', 'Arcane', 'Ranged', 'Both', 'Cold Snap'); + +-- ilvl 83 (Phase 3) +INSERT INTO `playerbots_bis_gear` VALUES +(8, 0, 0, 0, 83, 19375, 'Phase 3', 'Mage', 'Arcane', 'Head', 'Both', 'Mish''undare, Circlet of the Mind Flayer'), +(8, 0, 1, 0, 83, 18814, 'Phase 3', 'Mage', 'Arcane', 'Neck', 'Both', 'Choker of the Fire Lord'), +(8, 0, 2, 0, 83, 19370, 'Phase 3', 'Mage', 'Arcane', 'Shoulders', 'Both', 'Mantle of the Blackwing Cabal'), +(8, 0, 4, 0, 83, 14152, 'Phase 3', 'Mage', 'Arcane', 'Chest', 'Both', 'Robe of the Archmage'), +(8, 0, 5, 0, 83, 19136, 'Phase 3', 'Mage', 'Arcane', 'Waist', 'Both', 'Mana Igniting Cord'), +(8, 0, 6, 0, 83, 16915, 'Phase 3', 'Mage', 'Arcane', 'Legs', 'Both', 'Netherwind Pants'), +(8, 0, 7, 0, 83, 19438, 'Phase 3', 'Mage', 'Arcane', 'Feet', 'Both', 'Ringo''s Blizzard Boots'), +(8, 0, 8, 0, 83, 19374, 'Phase 3', 'Mage', 'Arcane', 'Wrists', 'Both', 'Bracers of Arcane Accuracy'), +(8, 0, 9, 0, 83, 16913, 'Phase 3', 'Mage', 'Arcane', 'Hands', 'Both', 'Netherwind Gloves'), +(8, 0, 11, 0, 83, 19147, 'Phase 3', 'Mage', 'Arcane', 'Finger2', 'Both', 'Ring of Spell Power'), +(8, 0, 12, 0, 83, 19379, 'Phase 3', 'Mage', 'Arcane', 'Trinket1', 'Both', 'Neltharion''s Tear'), +(8, 0, 13, 0, 83, 19339, 'Phase 3', 'Mage', 'Arcane', 'Trinket2', 'Both', 'Mind Quickening Gem'), +(8, 0, 14, 0, 83, 19378, 'Phase 3', 'Mage', 'Arcane', 'Back', 'Both', 'Cloak of the Brood Lord'), +(8, 0, 15, 0, 83, 19356, 'Phase 3', 'Mage', 'Arcane', 'MainHand', 'Both', 'Staff of the Shadow Flame'), +(8, 0, 17, 0, 83, 19130, 'Phase 3', 'Mage', 'Arcane', 'Ranged', 'Both', 'Cold Snap'); + +-- ilvl 88 (Phase 5) +INSERT INTO `playerbots_bis_gear` VALUES +(8, 0, 0, 0, 88, 19375, 'Phase 5', 'Mage', 'Arcane', 'Head', 'Both', 'Mish''undare, Circlet of the Mind Flayer'), +(8, 0, 1, 0, 88, 21608, 'Phase 5', 'Mage', 'Arcane', 'Neck', 'Both', 'Amulet of Vek''nilash'), +(8, 0, 2, 0, 88, 19370, 'Phase 5', 'Mage', 'Arcane', 'Shoulders', 'Both', 'Mantle of the Blackwing Cabal'), +(8, 0, 4, 0, 88, 19145, 'Phase 5', 'Mage', 'Arcane', 'Chest', 'Both', 'Robe of Volatile Power'), +(8, 0, 5, 0, 88, 22730, 'Phase 5', 'Mage', 'Arcane', 'Waist', 'Both', 'Eyestalk Waist Cord'), +(8, 0, 6, 0, 88, 21461, 'Phase 5', 'Mage', 'Arcane', 'Legs', 'Both', 'Leggings of the Black Blizzard'), +(8, 0, 7, 0, 88, 21344, 'Phase 5', 'Mage', 'Arcane', 'Feet', 'Both', 'Enigma Boots'), +(8, 0, 8, 0, 88, 21186, 'Phase 5', 'Mage', 'Arcane', 'Wrists', 'Both', 'Rockfury Bracers'), +(8, 0, 9, 0, 88, 21585, 'Phase 5', 'Mage', 'Arcane', 'Hands', 'Both', 'Dark Storm Gauntlets'), +(8, 0, 10, 0, 88, 21836, 'Phase 5', 'Mage', 'Arcane', 'Finger1', 'Both', 'Ritssyn''s Ring of Chaos'), +(8, 0, 11, 0, 88, 21709, 'Phase 5', 'Mage', 'Arcane', 'Finger2', 'Both', 'Ring of the Fallen God'), +(8, 0, 12, 0, 88, 19379, 'Phase 5', 'Mage', 'Arcane', 'Trinket1', 'Both', 'Neltharion''s Tear'), +(8, 0, 13, 0, 88, 19339, 'Phase 5', 'Mage', 'Arcane', 'Trinket2', 'Both', 'Mind Quickening Gem'), +(8, 0, 14, 0, 88, 22731, 'Phase 5', 'Mage', 'Arcane', 'Back', 'Both', 'Cloak of the Devoured'), +(8, 0, 15, 0, 88, 21622, 'Phase 5', 'Mage', 'Arcane', 'MainHand', 'Both', 'Sharpened Silithid Femur'), +(8, 0, 16, 0, 88, 21597, 'Phase 5', 'Mage', 'Arcane', 'OffHand', 'Both', 'Royal Scepter of Vek''lor'), +(8, 0, 17, 0, 88, 21603, 'Phase 5', 'Mage', 'Arcane', 'Ranged', 'Both', 'Wand of Qiraji Nobility'); + +-- ilvl 92 (Phase 6) +INSERT INTO `playerbots_bis_gear` VALUES +(8, 0, 0, 0, 92, 22498, 'Phase 6', 'Mage', 'Arcane', 'Head', 'Both', 'Frostfire Circlet'), +(8, 0, 1, 0, 92, 23057, 'Phase 6', 'Mage', 'Arcane', 'Neck', 'Both', 'Gem of Trapped Innocents'), +(8, 0, 2, 0, 92, 22983, 'Phase 6', 'Mage', 'Arcane', 'Shoulders', 'Both', 'Rime Covered Mantle'), +(8, 0, 4, 0, 92, 22496, 'Phase 6', 'Mage', 'Arcane', 'Chest', 'Both', 'Frostfire Robe'), +(8, 0, 5, 0, 92, 22730, 'Phase 6', 'Mage', 'Arcane', 'Waist', 'Both', 'Eyestalk Waist Cord'), +(8, 0, 6, 0, 92, 23070, 'Phase 6', 'Mage', 'Arcane', 'Legs', 'Both', 'Leggings of Polarity'), +(8, 0, 7, 0, 92, 22500, 'Phase 6', 'Mage', 'Arcane', 'Feet', 'Both', 'Frostfire Sandals'), +(8, 0, 8, 0, 92, 23021, 'Phase 6', 'Mage', 'Arcane', 'Wrists', 'Both', 'The Soul Harvester''s Bindings'), +(8, 0, 9, 0, 92, 21585, 'Phase 6', 'Mage', 'Arcane', 'Hands', 'Both', 'Dark Storm Gauntlets'), +(8, 0, 10, 0, 92, 23237, 'Phase 6', 'Mage', 'Arcane', 'Finger1', 'Both', 'Ring of the Eternal Flame'), +(8, 0, 11, 0, 92, 23062, 'Phase 6', 'Mage', 'Arcane', 'Finger2', 'Both', 'Frostfire Ring'), +(8, 0, 12, 0, 92, 19379, 'Phase 6', 'Mage', 'Arcane', 'Trinket1', 'Both', 'Neltharion''s Tear'), +(8, 0, 13, 0, 92, 23046, 'Phase 6', 'Mage', 'Arcane', 'Trinket2', 'Both', 'The Restrained Essence of Sapphiron'), +(8, 0, 14, 0, 92, 23050, 'Phase 6', 'Mage', 'Arcane', 'Back', 'Both', 'Cloak of the Necropolis'), +(8, 0, 15, 0, 92, 22807, 'Phase 6', 'Mage', 'Arcane', 'MainHand', 'Both', 'Wraith Blade'), +(8, 0, 16, 0, 92, 23049, 'Phase 6', 'Mage', 'Arcane', 'OffHand', 'Both', 'Sapphiron''s Left Eye'), +(8, 0, 17, 0, 92, 22821, 'Phase 6', 'Mage', 'Arcane', 'Ranged', 'Both', 'Doomfinger'); + +-- ilvl 120 (Pre-Raid) +INSERT INTO `playerbots_bis_gear` VALUES +(8, 0, 0, 0, 120, 28278, 'Pre-Raid', 'Mage', 'Arcane', 'Head', 'Both', 'Incanter''s Cowl'), +(8, 0, 1, 0, 120, 28134, 'Pre-Raid', 'Mage', 'Arcane', 'Neck', 'Both', 'Brooch of Heightened Potential'), +(8, 0, 2, 0, 120, 27796, 'Pre-Raid', 'Mage', 'Arcane', 'Shoulders', 'Both', 'Mana-Etched Spaulders'), +(8, 0, 4, 0, 120, 21848, 'Pre-Raid', 'Mage', 'Arcane', 'Chest', 'Both', 'Spellfire Robe'), +(8, 0, 5, 0, 120, 21846, 'Pre-Raid', 'Mage', 'Arcane', 'Waist', 'Both', 'Spellfire Belt'), +(8, 0, 6, 0, 120, 30532, 'Pre-Raid', 'Mage', 'Arcane', 'Legs', 'Both', 'Kirin Tor Master''s Trousers'), +(8, 0, 7, 0, 120, 28406, 'Pre-Raid', 'Mage', 'Arcane', 'Feet', 'Both', 'Sigil-Laced Boots'), +(8, 0, 8, 0, 120, 29240, 'Pre-Raid', 'Mage', 'Arcane', 'Wrists', 'Both', 'Bands of Negation'), +(8, 0, 9, 0, 120, 21847, 'Pre-Raid', 'Mage', 'Arcane', 'Hands', 'Both', 'Spellfire Gloves'), +(8, 0, 10, 0, 120, 29172, 'Pre-Raid', 'Mage', 'Arcane', 'Finger1', 'Both', 'Ashyen''s Gift'), +(8, 0, 11, 0, 120, 28227, 'Pre-Raid', 'Mage', 'Arcane', 'Finger2', 'Both', 'Sparking Arcanite Ring'), +(8, 0, 12, 0, 120, 29370, 'Pre-Raid', 'Mage', 'Arcane', 'Trinket1', 'Both', 'Icon of the Silver Crescent'), +(8, 0, 13, 0, 120, 27683, 'Pre-Raid', 'Mage', 'Arcane', 'Trinket2', 'Both', 'Quagmirran''s Eye'), +(8, 0, 14, 0, 120, 27981, 'Pre-Raid', 'Mage', 'Arcane', 'Back', 'Both', 'Sethekk Oracle Cloak'), +(8, 0, 15, 0, 120, 23554, 'Pre-Raid', 'Mage', 'Arcane', 'MainHand', 'Both', 'Eternium Runed Blade'), +(8, 0, 16, 0, 120, 29271, 'Pre-Raid', 'Mage', 'Arcane', 'OffHand', 'Both', 'Talisman of Kalecgos'), +(8, 0, 17, 0, 120, 29350, 'Pre-Raid', 'Mage', 'Arcane', 'Ranged', 'Both', 'The Black Stalk'); + +-- ilvl 125 (Phase 1) +INSERT INTO `playerbots_bis_gear` VALUES +(8, 0, 0, 0, 125, 29076, 'Phase 1', 'Mage', 'Arcane', 'Head', 'Both', 'Collar of the Aldor'), +(8, 0, 1, 0, 125, 28762, 'Phase 1', 'Mage', 'Arcane', 'Neck', 'Both', 'Adornment of Stolen Souls'), +(8, 0, 2, 0, 125, 29079, 'Phase 1', 'Mage', 'Arcane', 'Shoulders', 'Both', 'Pauldrons of the Aldor'), +(8, 0, 4, 0, 125, 21848, 'Phase 1', 'Mage', 'Arcane', 'Chest', 'Both', 'Spellfire Robe'), +(8, 0, 5, 0, 125, 21846, 'Phase 1', 'Mage', 'Arcane', 'Waist', 'Both', 'Spellfire Belt'), +(8, 0, 6, 0, 125, 29078, 'Phase 1', 'Mage', 'Arcane', 'Legs', 'Both', 'Legwraps of the Aldor'), +(8, 0, 7, 0, 125, 28517, 'Phase 1', 'Mage', 'Arcane', 'Feet', 'Both', 'Boots of Foretelling'), +(8, 0, 8, 0, 125, 28515, 'Phase 1', 'Mage', 'Arcane', 'Wrists', 'Both', 'Bands of Nefarious Deeds'), +(8, 0, 9, 0, 125, 21847, 'Phase 1', 'Mage', 'Arcane', 'Hands', 'Both', 'Spellfire Gloves'), +(8, 0, 10, 0, 125, 28793, 'Phase 1', 'Mage', 'Arcane', 'Finger1', 'Both', 'Band of Crimson Fury'), +(8, 0, 11, 0, 125, 29287, 'Phase 1', 'Mage', 'Arcane', 'Finger2', 'Both', 'Violet Signet of the Archmage'), +(8, 0, 12, 0, 125, 29370, 'Phase 1', 'Mage', 'Arcane', 'Trinket1', 'Both', 'Icon of the Silver Crescent'), +(8, 0, 13, 0, 125, 28785, 'Phase 1', 'Mage', 'Arcane', 'Trinket2', 'Both', 'The Lightning Capacitor'), +(8, 0, 14, 0, 125, 28766, 'Phase 1', 'Mage', 'Arcane', 'Back', 'Both', 'Ruby Drape of the Mysticant'), +(8, 0, 15, 0, 125, 28770, 'Phase 1', 'Mage', 'Arcane', 'MainHand', 'Both', 'Nathrezim Mindblade'), +(8, 0, 16, 0, 125, 29271, 'Phase 1', 'Mage', 'Arcane', 'OffHand', 'Both', 'Talisman of Kalecgos'), +(8, 0, 17, 0, 125, 28783, 'Phase 1', 'Mage', 'Arcane', 'Ranged', 'Both', 'Eredar Wand of Obliteration'); + +-- ilvl 141 (Phase 2) +INSERT INTO `playerbots_bis_gear` VALUES +(8, 0, 0, 0, 141, 30206, 'Phase 2', 'Mage', 'Arcane', 'Head', 'Both', 'Cowl of Tirisfal'), +(8, 0, 1, 0, 141, 30015, 'Phase 2', 'Mage', 'Arcane', 'Neck', 'Both', 'The Sun King''s Talisman'), +(8, 0, 2, 0, 141, 30210, 'Phase 2', 'Mage', 'Arcane', 'Shoulders', 'Both', 'Mantle of Tirisfal'), +(8, 0, 4, 0, 141, 30196, 'Phase 2', 'Mage', 'Arcane', 'Chest', 'Both', 'Robes of Tirisfal'), +(8, 0, 5, 0, 141, 30038, 'Phase 2', 'Mage', 'Arcane', 'Waist', 'Both', 'Belt of Blasting'), +(8, 0, 6, 0, 141, 30207, 'Phase 2', 'Mage', 'Arcane', 'Legs', 'Both', 'Leggings of Tirisfal'), +(8, 0, 7, 0, 141, 30067, 'Phase 2', 'Mage', 'Arcane', 'Feet', 'Both', 'Velvet Boots of the Guardian'), +(8, 0, 8, 0, 141, 29918, 'Phase 2', 'Mage', 'Arcane', 'Wrists', 'Both', 'Mindstorm Wristbands'), +(8, 0, 9, 0, 141, 29987, 'Phase 2', 'Mage', 'Arcane', 'Hands', 'Both', 'Gauntlets of the Sun King'), +(8, 0, 10, 0, 141, 29302, 'Phase 2', 'Mage', 'Arcane', 'Finger1', 'Both', 'Band of Eternity'), +(8, 0, 11, 0, 141, 29287, 'Phase 2', 'Mage', 'Arcane', 'Finger2', 'Both', 'Violet Signet of the Archmage'), +(8, 0, 12, 0, 141, 29370, 'Phase 2', 'Mage', 'Arcane', 'Trinket1', 'Both', 'Icon of the Silver Crescent'), +(8, 0, 13, 0, 141, 30720, 'Phase 2', 'Mage', 'Arcane', 'Trinket2', 'Both', 'Serpent-Coil Braid'), +(8, 0, 14, 0, 141, 29992, 'Phase 2', 'Mage', 'Arcane', 'Back', 'Both', 'Royal Cloak of the Sunstriders'), +(8, 0, 15, 0, 141, 30095, 'Phase 2', 'Mage', 'Arcane', 'MainHand', 'Both', 'Fang of the Leviathan'), +(8, 0, 16, 0, 141, 30049, 'Phase 2', 'Mage', 'Arcane', 'OffHand', 'Both', 'Fathomstone'), +(8, 0, 17, 0, 141, 28783, 'Phase 2', 'Mage', 'Arcane', 'Ranged', 'Both', 'Eredar Wand of Obliteration'); + +-- ilvl 156 (Phase 3) +INSERT INTO `playerbots_bis_gear` VALUES +(8, 0, 0, 0, 156, 30206, 'Phase 3', 'Mage', 'Arcane', 'Head', 'Both', 'Cowl of Tirisfal'), +(8, 0, 1, 0, 156, 30015, 'Phase 3', 'Mage', 'Arcane', 'Neck', 'Both', 'The Sun King''s Talisman'), +(8, 0, 2, 0, 156, 30210, 'Phase 3', 'Mage', 'Arcane', 'Shoulders', 'Both', 'Mantle of Tirisfal'), +(8, 0, 4, 0, 156, 30196, 'Phase 3', 'Mage', 'Arcane', 'Chest', 'Both', 'Robes of Tirisfal'), +(8, 0, 5, 0, 156, 30888, 'Phase 3', 'Mage', 'Arcane', 'Waist', 'Both', 'Anetheron''s Noose'), +(8, 0, 6, 0, 156, 31058, 'Phase 3', 'Mage', 'Arcane', 'Legs', 'Both', 'Leggings of the Tempest'), +(8, 0, 7, 0, 156, 32239, 'Phase 3', 'Mage', 'Arcane', 'Feet', 'Both', 'Slippers of the Seacaller'), +(8, 0, 8, 0, 156, 30870, 'Phase 3', 'Mage', 'Arcane', 'Wrists', 'Both', 'Cuffs of Devastation'), +(8, 0, 9, 0, 156, 30205, 'Phase 3', 'Mage', 'Arcane', 'Hands', 'Both', 'Gloves of Tirisfal'), +(8, 0, 10, 0, 156, 29305, 'Phase 3', 'Mage', 'Arcane', 'Finger1', 'Both', 'Band of the Eternal Sage'), +(8, 0, 11, 0, 156, 32527, 'Phase 3', 'Mage', 'Arcane', 'Finger2', 'Both', 'Ring of Ancient Knowledge'), +(8, 0, 12, 0, 156, 32488, 'Phase 3', 'Mage', 'Arcane', 'Trinket1', 'Both', 'Ashtongue Talisman of Insight'), +(8, 0, 13, 0, 156, 32483, 'Phase 3', 'Mage', 'Arcane', 'Trinket2', 'Both', 'The Skull of Gul''dan'), +(8, 0, 14, 0, 156, 32331, 'Phase 3', 'Mage', 'Arcane', 'Back', 'Both', 'Cloak of the Illidari Council'), +(8, 0, 15, 0, 156, 30910, 'Phase 3', 'Mage', 'Arcane', 'MainHand', 'Both', 'Tempest of Chaos'), +(8, 0, 16, 0, 156, 30872, 'Phase 3', 'Mage', 'Arcane', 'OffHand', 'Both', 'Chronicle of Dark Secrets'), +(8, 0, 17, 0, 156, 28783, 'Phase 3', 'Mage', 'Arcane', 'Ranged', 'Both', 'Eredar Wand of Obliteration'); + +-- ilvl 164 (Phase 4) +INSERT INTO `playerbots_bis_gear` VALUES +(8, 0, 0, 0, 164, 30206, 'Phase 4', 'Mage', 'Arcane', 'Head', 'Both', 'Cowl of Tirisfal'), +(8, 0, 1, 0, 164, 30015, 'Phase 4', 'Mage', 'Arcane', 'Neck', 'Both', 'The Sun King''s Talisman'), +(8, 0, 2, 0, 164, 30210, 'Phase 4', 'Mage', 'Arcane', 'Shoulders', 'Both', 'Mantle of Tirisfal'), +(8, 0, 4, 0, 164, 30196, 'Phase 4', 'Mage', 'Arcane', 'Chest', 'Both', 'Robes of Tirisfal'), +(8, 0, 5, 0, 164, 30888, 'Phase 4', 'Mage', 'Arcane', 'Waist', 'Both', 'Anetheron''s Noose'), +(8, 0, 6, 0, 164, 31058, 'Phase 4', 'Mage', 'Arcane', 'Legs', 'Both', 'Leggings of the Tempest'), +(8, 0, 7, 0, 164, 32239, 'Phase 4', 'Mage', 'Arcane', 'Feet', 'Both', 'Slippers of the Seacaller'), +(8, 0, 8, 0, 164, 30870, 'Phase 4', 'Mage', 'Arcane', 'Wrists', 'Both', 'Cuffs of Devastation'), +(8, 0, 9, 0, 164, 30205, 'Phase 4', 'Mage', 'Arcane', 'Hands', 'Both', 'Gloves of Tirisfal'), +(8, 0, 10, 0, 164, 29305, 'Phase 4', 'Mage', 'Arcane', 'Finger1', 'Both', 'Band of the Eternal Sage'), +(8, 0, 11, 0, 164, 32527, 'Phase 4', 'Mage', 'Arcane', 'Finger2', 'Both', 'Ring of Ancient Knowledge'), +(8, 0, 12, 0, 164, 32488, 'Phase 4', 'Mage', 'Arcane', 'Trinket1', 'Both', 'Ashtongue Talisman of Insight'), +(8, 0, 13, 0, 164, 32483, 'Phase 4', 'Mage', 'Arcane', 'Trinket2', 'Both', 'The Skull of Gul''dan'), +(8, 0, 14, 0, 164, 32331, 'Phase 4', 'Mage', 'Arcane', 'Back', 'Both', 'Cloak of the Illidari Council'), +(8, 0, 15, 0, 164, 30910, 'Phase 4', 'Mage', 'Arcane', 'MainHand', 'Both', 'Tempest of Chaos'), +(8, 0, 16, 0, 164, 30872, 'Phase 4', 'Mage', 'Arcane', 'OffHand', 'Both', 'Chronicle of Dark Secrets'), +(8, 0, 17, 0, 164, 33192, 'Phase 4', 'Mage', 'Arcane', 'Ranged', 'Both', 'Carved Witch Doctor''s Stick'); + +-- ilvl 200 (Pre-Raid) +INSERT INTO `playerbots_bis_gear` VALUES +(8, 0, 0, 0, 200, 37294, 'Pre-Raid', 'Mage', 'Arcane', 'Head', 'Both', 'Crown of Unbridled Magic'), +(8, 0, 1, 0, 200, 39472, 'Pre-Raid', 'Mage', 'Arcane', 'Neck', 'Both', 'Chain of Latent Energies'), +(8, 0, 2, 0, 200, 37673, 'Pre-Raid', 'Mage', 'Arcane', 'Shoulders', 'Both', 'Dark Runic Mantle'), +(8, 0, 4, 0, 200, 39492, 'Pre-Raid', 'Mage', 'Arcane', 'Chest', 'Both', 'Heroes'' Frostfire Robe'), +(8, 0, 5, 0, 200, 40696, 'Pre-Raid', 'Mage', 'Arcane', 'Waist', 'Both', 'Plush Sash of Guzbah'), +(8, 0, 6, 0, 200, 37854, 'Pre-Raid', 'Mage', 'Arcane', 'Legs', 'Both', 'Woven Bracae Leggings'), +(8, 0, 7, 0, 200, 44202, 'Pre-Raid', 'Mage', 'Arcane', 'Feet', 'Both', 'Sandals of Crimson Fury'), +(8, 0, 8, 0, 200, 37361, 'Pre-Raid', 'Mage', 'Arcane', 'Wrists', 'Both', 'Cuffs of Winged Levitation'), +(8, 0, 9, 0, 200, 39495, 'Pre-Raid', 'Mage', 'Arcane', 'Hands', 'Both', 'Heroes'' Frostfire Gloves'), +(8, 0, 10, 0, 200, 37694, 'Pre-Raid', 'Mage', 'Arcane', 'Finger1', 'Both', 'Band of Guile'), +(8, 0, 12, 0, 200, 37660, 'Pre-Raid', 'Mage', 'Arcane', 'Trinket1', 'Both', 'Forge Ember'), +(8, 0, 14, 0, 200, 41610, 'Pre-Raid', 'Mage', 'Arcane', 'Back', 'Both', 'Deathchill Cloak'), +(8, 0, 15, 0, 200, 37360, 'Pre-Raid', 'Mage', 'Arcane', 'MainHand', 'Both', 'Staff of Draconic Combat'), +(8, 0, 17, 0, 200, 37177, 'Pre-Raid', 'Mage', 'Arcane', 'Ranged', 'Both', 'Wand of the San''layn'); + +-- ilvl 224 (Phase 1) +INSERT INTO `playerbots_bis_gear` VALUES +(8, 0, 1, 0, 224, 44661, 'Phase 1', 'Mage', 'Arcane', 'Neck', 'Both', 'Wyrmrest Necklace of Power'), +(8, 0, 2, 0, 224, 40419, 'Phase 1', 'Mage', 'Arcane', 'Shoulders', 'Both', 'Valorous Frostfire Shoulderpads'), +(8, 0, 4, 0, 224, 44002, 'Phase 1', 'Mage', 'Arcane', 'Chest', 'Both', 'The Sanctum''s Flowing Vestments'), +(8, 0, 5, 0, 224, 40561, 'Phase 1', 'Mage', 'Arcane', 'Waist', 'Both', 'Leash of Heedless Magic'), +(8, 0, 6, 0, 224, 40417, 'Phase 1', 'Mage', 'Arcane', 'Legs', 'Both', 'Valorous Frostfire Leggings'), +(8, 0, 7, 0, 224, 40558, 'Phase 1', 'Mage', 'Arcane', 'Feet', 'Both', 'Arcanic Tramplers'), +(8, 0, 8, 0, 224, 44008, 'Phase 1', 'Mage', 'Arcane', 'Wrists', 'Both', 'Unsullied Cuffs'), +(8, 0, 9, 0, 224, 40415, 'Phase 1', 'Mage', 'Arcane', 'Hands', 'Both', 'Valorous Frostfire Gloves'), +(8, 0, 10, 0, 224, 40719, 'Phase 1', 'Mage', 'Arcane', 'Finger1', 'Both', 'Band of Channeled Magic'), +(8, 0, 12, 0, 224, 40255, 'Phase 1', 'Mage', 'Arcane', 'Trinket1', 'Both', 'Dying Curse'), +(8, 0, 14, 0, 224, 44005, 'Phase 1', 'Mage', 'Arcane', 'Back', 'Both', 'Pennant Cloak'), +(8, 0, 15, 0, 224, 40396, 'Phase 1', 'Mage', 'Arcane', 'MainHand', 'Both', 'The Turning Tide'), +(8, 0, 17, 0, 224, 39426, 'Phase 1', 'Mage', 'Arcane', 'Ranged', 'Both', 'Wand of the Archlich'); + +-- ilvl 245 (Phase 2) +INSERT INTO `playerbots_bis_gear` VALUES +(8, 0, 0, 0, 245, 46129, 'Phase 2', 'Mage', 'Arcane', 'Head', 'Both', 'Conqueror''s Kirin Tor Hood'), +(8, 0, 1, 0, 245, 45243, 'Phase 2', 'Mage', 'Arcane', 'Neck', 'Both', 'Sapphire Amulet of Renewal'), +(8, 0, 2, 0, 245, 46134, 'Phase 2', 'Mage', 'Arcane', 'Shoulders', 'Both', 'Conqueror''s Kirin Tor Shoulderpads'), +(8, 0, 4, 0, 245, 46130, 'Phase 2', 'Mage', 'Arcane', 'Chest', 'Both', 'Conqueror''s Kirin Tor Tunic'), +(8, 0, 5, 0, 245, 45557, 'Phase 2', 'Mage', 'Arcane', 'Waist', 'Both', 'Sash of Ancient Power'), +(8, 0, 6, 0, 245, 46133, 'Phase 2', 'Mage', 'Arcane', 'Legs', 'Both', 'Conqueror''s Kirin Tor Leggings'), +(8, 0, 7, 0, 245, 45135, 'Phase 2', 'Mage', 'Arcane', 'Feet', 'Both', 'Boots of Fiery Resolution'), +(8, 0, 8, 0, 245, 45446, 'Phase 2', 'Mage', 'Arcane', 'Wrists', 'Both', 'Grasps of Reason'), +(8, 0, 9, 0, 245, 45665, 'Phase 2', 'Mage', 'Arcane', 'Hands', 'Both', 'Pharos Gloves'), +(8, 0, 10, 0, 245, 46046, 'Phase 2', 'Mage', 'Arcane', 'Finger1', 'Both', 'Nebula Band'), +(8, 0, 12, 0, 245, 45518, 'Phase 2', 'Mage', 'Arcane', 'Trinket1', 'Both', 'Flare of the Heavens'), +(8, 0, 14, 0, 245, 45618, 'Phase 2', 'Mage', 'Arcane', 'Back', 'Both', 'Sunglimmer Cloak'), +(8, 0, 15, 0, 245, 45457, 'Phase 2', 'Mage', 'Arcane', 'MainHand', 'Both', 'Staff of Endless Winter'), +(8, 0, 17, 0, 245, 45511, 'Phase 2', 'Mage', 'Arcane', 'Ranged', 'Both', 'Scepter of Lost Souls'); + +-- ilvl 258 (Phase 3) +INSERT INTO `playerbots_bis_gear` VALUES +(8, 0, 0, 0, 258, 47761, 'Phase 3', 'Mage', 'Arcane', 'Head', 'Both', 'Hood of Triumph'), +(8, 0, 2, 0, 258, 47758, 'Phase 3', 'Mage', 'Arcane', 'Shoulders', 'Both', 'Shoulderpads of Triumph'), +(8, 0, 4, 1, 258, 46993, 'Phase 3', 'Mage', 'Arcane', 'Chest', 'Alliance', 'Flowing Vestments of Ascent'), +(8, 0, 4, 2, 258, 47425, 'Phase 3', 'Mage', 'Arcane', 'Chest', 'Horde', 'Flowing Robes of Ascent'), +(8, 0, 5, 1, 258, 47419, 'Phase 3', 'Mage', 'Arcane', 'Waist', 'Alliance', 'Belt of the Tenebrous Mist'), +(8, 0, 5, 2, 258, 46973, 'Phase 3', 'Mage', 'Arcane', 'Waist', 'Horde', 'Cord of the Tenebrous Mist'), +(8, 0, 6, 0, 258, 47760, 'Phase 3', 'Mage', 'Arcane', 'Legs', 'Both', 'Leggings of Triumph'), +(8, 0, 7, 1, 258, 47097, 'Phase 3', 'Mage', 'Arcane', 'Feet', 'Alliance', 'Boots of the Mourning Widow'), +(8, 0, 7, 2, 258, 47454, 'Phase 3', 'Mage', 'Arcane', 'Feet', 'Horde', 'Sandals of the Mourning Widow'), +(8, 0, 8, 1, 258, 47208, 'Phase 3', 'Mage', 'Arcane', 'Wrists', 'Alliance', 'Armbands of the Ashen Saint'), +(8, 0, 8, 2, 258, 47485, 'Phase 3', 'Mage', 'Arcane', 'Wrists', 'Horde', 'Bindings of the Ashen Saint'), +(8, 0, 9, 0, 258, 47762, 'Phase 3', 'Mage', 'Arcane', 'Hands', 'Both', 'Gauntlets of Triumph'), +(8, 0, 10, 1, 258, 47489, 'Phase 3', 'Mage', 'Arcane', 'Finger1', 'Alliance', 'Lurid Manifestation'), +(8, 0, 10, 2, 258, 47237, 'Phase 3', 'Mage', 'Arcane', 'Finger1', 'Horde', 'Band of Deplorable Violence'), +(8, 0, 12, 1, 258, 47188, 'Phase 3', 'Mage', 'Arcane', 'Trinket1', 'Alliance', 'Reign of the Unliving'), +(8, 0, 12, 2, 258, 47477, 'Phase 3', 'Mage', 'Arcane', 'Trinket1', 'Horde', 'Reign of the Dead'), +(8, 0, 14, 1, 258, 47552, 'Phase 3', 'Mage', 'Arcane', 'Back', 'Alliance', 'Jaina''s Radiance'), +(8, 0, 14, 2, 258, 47551, 'Phase 3', 'Mage', 'Arcane', 'Back', 'Horde', 'Aethas'' Intensity'), +(8, 0, 17, 0, 258, 45294, 'Phase 3', 'Mage', 'Arcane', 'Ranged', 'Both', 'Petrified Ivy Sprig'); + +-- ilvl 264 (Phase 4) +INSERT INTO `playerbots_bis_gear` VALUES +(8, 0, 0, 0, 264, 51281, 'Phase 4', 'Mage', 'Arcane', 'Head', 'Both', 'Sanctified Bloodmage Hood'), +(8, 0, 1, 0, 264, 50724, 'Phase 4', 'Mage', 'Arcane', 'Neck', 'Both', 'Blood Queen''s Crimson Choker'), +(8, 0, 2, 0, 264, 51284, 'Phase 4', 'Mage', 'Arcane', 'Shoulders', 'Both', 'Sanctified Bloodmage Shoulderpads'), +(8, 0, 4, 0, 264, 51283, 'Phase 4', 'Mage', 'Arcane', 'Chest', 'Both', 'Sanctified Bloodmage Robe'), +(8, 0, 5, 0, 264, 50613, 'Phase 4', 'Mage', 'Arcane', 'Waist', 'Both', 'Crushing Coldwraith Belt'), +(8, 0, 6, 0, 264, 51282, 'Phase 4', 'Mage', 'Arcane', 'Legs', 'Both', 'Sanctified Bloodmage Leggings'), +(8, 0, 7, 0, 264, 50699, 'Phase 4', 'Mage', 'Arcane', 'Feet', 'Both', 'Plague Scientist''s Boots'), +(8, 0, 8, 0, 264, 50651, 'Phase 4', 'Mage', 'Arcane', 'Wrists', 'Both', 'The Lady''s Brittle Bracers'), +(8, 0, 9, 0, 264, 50722, 'Phase 4', 'Mage', 'Arcane', 'Hands', 'Both', 'San''layn Ritualist Gloves'), +(8, 0, 10, 0, 264, 50398, 'Phase 4', 'Mage', 'Arcane', 'Finger1', 'Both', 'Ashen Band of Endless Destruction'), +(8, 0, 12, 1, 264, 47188, 'Phase 4', 'Mage', 'Arcane', 'Trinket1', 'Alliance', 'Reign of the Unliving'), +(8, 0, 12, 2, 264, 47477, 'Phase 4', 'Mage', 'Arcane', 'Trinket1', 'Horde', 'Reign of the Dead'), +(8, 0, 14, 0, 264, 50628, 'Phase 4', 'Mage', 'Arcane', 'Back', 'Both', 'Frostbinder''s Shredded Cape'), +(8, 0, 15, 0, 264, 50732, 'Phase 4', 'Mage', 'Arcane', 'MainHand', 'Both', 'Bloodsurge, Kel''Thuzad''s Blade of Agony'), +(8, 0, 17, 0, 264, 50684, 'Phase 4', 'Mage', 'Arcane', 'Ranged', 'Both', 'Corpse-Impaling Spike'); + +-- ilvl 290 (Phase 5) +INSERT INTO `playerbots_bis_gear` VALUES +(8, 0, 0, 0, 290, 51281, 'Phase 5', 'Mage', 'Arcane', 'Head', 'Both', 'Sanctified Bloodmage Hood'), +(8, 0, 1, 0, 290, 50182, 'Phase 5', 'Mage', 'Arcane', 'Neck', 'Both', 'Blood Queen''s Crimson Choker'), +(8, 0, 2, 0, 290, 51284, 'Phase 5', 'Mage', 'Arcane', 'Shoulders', 'Both', 'Sanctified Bloodmage Shoulderpads'), +(8, 0, 4, 0, 290, 51283, 'Phase 5', 'Mage', 'Arcane', 'Chest', 'Both', 'Sanctified Bloodmage Robe'), +(8, 0, 5, 0, 290, 50613, 'Phase 5', 'Mage', 'Arcane', 'Waist', 'Both', 'Crushing Coldwraith Belt'), +(8, 0, 6, 0, 290, 50694, 'Phase 5', 'Mage', 'Arcane', 'Legs', 'Both', 'Plaguebringer''s Stained Pants'), +(8, 0, 7, 0, 290, 50699, 'Phase 5', 'Mage', 'Arcane', 'Feet', 'Both', 'Plague Scientist''s Boots'), +(8, 0, 8, 0, 290, 54582, 'Phase 5', 'Mage', 'Arcane', 'Wrists', 'Both', 'Bracers of Fiery Night'), +(8, 0, 9, 0, 290, 51280, 'Phase 5', 'Mage', 'Arcane', 'Hands', 'Both', 'Sanctified Bloodmage Gloves'), +(8, 0, 10, 0, 290, 50614, 'Phase 5', 'Mage', 'Arcane', 'Finger1', 'Both', 'Loop of the Endless Labyrinth'), +(8, 0, 11, 0, 290, 50398, 'Phase 5', 'Mage', 'Arcane', 'Finger2', 'Both', 'Ashen Band of Endless Destruction'), +(8, 0, 12, 0, 290, 54588, 'Phase 5', 'Mage', 'Arcane', 'Trinket1', 'Both', 'Charred Twilight Scale'), +(8, 0, 13, 0, 290, 50348, 'Phase 5', 'Mage', 'Arcane', 'Trinket2', 'Both', 'Dislodged Foreign Object'), +(8, 0, 14, 0, 290, 54583, 'Phase 5', 'Mage', 'Arcane', 'Back', 'Both', 'Cloak of Burning Dusk'), +(8, 0, 15, 0, 290, 50732, 'Phase 5', 'Mage', 'Arcane', 'MainHand', 'Both', 'Bloodsurge, Kel''Thuzad''s Blade of Agony'), +(8, 0, 16, 0, 290, 50719, 'Phase 5', 'Mage', 'Arcane', 'OffHand', 'Both', 'Shadow Silk Spindle'), +(8, 0, 17, 0, 290, 50684, 'Phase 5', 'Mage', 'Arcane', 'Ranged', 'Both', 'Corpse-Impaling Spike'); + +-- Fire (tab 1) +-- ilvl 66 (Phase 1 (Pre-Raid)) +INSERT INTO `playerbots_bis_gear` VALUES +(8, 1, 0, 0, 66, 10504, 'Phase 1 (Pre-Raid)', 'Mage', 'Fire', 'Head', 'Both', 'Green Lens'), +(8, 1, 1, 0, 66, 12103, 'Phase 1 (Pre-Raid)', 'Mage', 'Fire', 'Neck', 'Both', 'Star of Mystaria'), +(8, 1, 2, 0, 66, 11782, 'Phase 1 (Pre-Raid)', 'Mage', 'Fire', 'Shoulders', 'Both', 'Boreal Mantle'), +(8, 1, 4, 0, 66, 14152, 'Phase 1 (Pre-Raid)', 'Mage', 'Fire', 'Chest', 'Both', 'Robe of the Archmage'), +(8, 1, 5, 0, 66, 11662, 'Phase 1 (Pre-Raid)', 'Mage', 'Fire', 'Waist', 'Both', 'Ban''thok Sash'), +(8, 1, 6, 0, 66, 13170, 'Phase 1 (Pre-Raid)', 'Mage', 'Fire', 'Legs', 'Both', 'Skyshroud Leggings'), +(8, 1, 7, 0, 66, 11822, 'Phase 1 (Pre-Raid)', 'Mage', 'Fire', 'Feet', 'Both', 'Omnicast Boots'), +(8, 1, 8, 0, 66, 11766, 'Phase 1 (Pre-Raid)', 'Mage', 'Fire', 'Wrists', 'Both', 'Flameweave Cuffs'), +(8, 1, 9, 0, 66, 13253, 'Phase 1 (Pre-Raid)', 'Mage', 'Fire', 'Hands', 'Both', 'Hands of Power'), +(8, 1, 10, 0, 66, 942, 'Phase 1 (Pre-Raid)', 'Mage', 'Fire', 'Finger1', 'Both', 'Freezing Band'), +(8, 1, 11, 0, 66, 942, 'Phase 1 (Pre-Raid)', 'Mage', 'Fire', 'Finger2', 'Both', 'Freezing Band'), +(8, 1, 12, 0, 66, 12930, 'Phase 1 (Pre-Raid)', 'Mage', 'Fire', 'Trinket1', 'Both', 'Briarwood Reed'), +(8, 1, 13, 0, 66, 13968, 'Phase 1 (Pre-Raid)', 'Mage', 'Fire', 'Trinket2', 'Both', 'Eye of the Beast'), +(8, 1, 14, 0, 66, 13386, 'Phase 1 (Pre-Raid)', 'Mage', 'Fire', 'Back', 'Both', 'Archivist Cape'), +(8, 1, 15, 0, 66, 13964, 'Phase 1 (Pre-Raid)', 'Mage', 'Fire', 'MainHand', 'Both', 'Witchblade'), +(8, 1, 16, 0, 66, 10796, 'Phase 1 (Pre-Raid)', 'Mage', 'Fire', 'OffHand', 'Both', 'Drakestone'), +(8, 1, 17, 0, 66, 13938, 'Phase 1 (Pre-Raid)', 'Mage', 'Fire', 'Ranged', 'Both', 'Bonecreeper Stylus'); + +-- ilvl 76 (Phase 2 (Pre-Raid)) +INSERT INTO `playerbots_bis_gear` VALUES +(8, 1, 0, 0, 76, 23318, 'Phase 2 (Pre-Raid)', 'Mage', 'Fire', 'Head', 'Both', 'Lieutenant Commander''s Silk Cowl'), +(8, 1, 1, 0, 76, 12103, 'Phase 2 (Pre-Raid)', 'Mage', 'Fire', 'Neck', 'Both', 'Star of Mystaria'), +(8, 1, 2, 0, 76, 23319, 'Phase 2 (Pre-Raid)', 'Mage', 'Fire', 'Shoulders', 'Both', 'Lieutenant Commander''s Silk Mantle'), +(8, 1, 4, 0, 76, 14152, 'Phase 2 (Pre-Raid)', 'Mage', 'Fire', 'Chest', 'Both', 'Robe of the Archmage'), +(8, 1, 5, 0, 76, 11662, 'Phase 2 (Pre-Raid)', 'Mage', 'Fire', 'Waist', 'Both', 'Ban''thok Sash'), +(8, 1, 6, 0, 76, 23304, 'Phase 2 (Pre-Raid)', 'Mage', 'Fire', 'Legs', 'Both', 'Knight-Captain''s Silk Legguards'), +(8, 1, 7, 0, 76, 23291, 'Phase 2 (Pre-Raid)', 'Mage', 'Fire', 'Feet', 'Both', 'Knight-Lieutenant''s Silk Walkers'), +(8, 1, 8, 0, 76, 11766, 'Phase 2 (Pre-Raid)', 'Mage', 'Fire', 'Wrists', 'Both', 'Flameweave Cuffs'), +(8, 1, 9, 0, 76, 13253, 'Phase 2 (Pre-Raid)', 'Mage', 'Fire', 'Hands', 'Both', 'Hands of Power'), +(8, 1, 10, 0, 76, 942, 'Phase 2 (Pre-Raid)', 'Mage', 'Fire', 'Finger1', 'Both', 'Freezing Band'), +(8, 1, 11, 0, 76, 942, 'Phase 2 (Pre-Raid)', 'Mage', 'Fire', 'Finger2', 'Both', 'Freezing Band'), +(8, 1, 12, 0, 76, 12930, 'Phase 2 (Pre-Raid)', 'Mage', 'Fire', 'Trinket1', 'Both', 'Briarwood Reed'), +(8, 1, 13, 0, 76, 13968, 'Phase 2 (Pre-Raid)', 'Mage', 'Fire', 'Trinket2', 'Both', 'Eye of the Beast'), +(8, 1, 14, 0, 76, 13386, 'Phase 2 (Pre-Raid)', 'Mage', 'Fire', 'Back', 'Both', 'Archivist Cape'), +(8, 1, 15, 0, 76, 13964, 'Phase 2 (Pre-Raid)', 'Mage', 'Fire', 'MainHand', 'Both', 'Witchblade'), +(8, 1, 16, 0, 76, 10796, 'Phase 2 (Pre-Raid)', 'Mage', 'Fire', 'OffHand', 'Both', 'Drakestone'), +(8, 1, 17, 0, 76, 13938, 'Phase 2 (Pre-Raid)', 'Mage', 'Fire', 'Ranged', 'Both', 'Bonecreeper Stylus'); + +-- ilvl 78 (Phase 2) +INSERT INTO `playerbots_bis_gear` VALUES +(8, 1, 0, 0, 78, 16914, 'Phase 2', 'Mage', 'Fire', 'Head', 'Both', 'Netherwind Crown'), +(8, 1, 1, 0, 78, 18814, 'Phase 2', 'Mage', 'Fire', 'Neck', 'Both', 'Choker of the Fire Lord'), +(8, 1, 2, 0, 78, 23319, 'Phase 2', 'Mage', 'Fire', 'Shoulders', 'Both', 'Lieutenant Commander''s Silk Mantle'), +(8, 1, 4, 0, 78, 14152, 'Phase 2', 'Mage', 'Fire', 'Chest', 'Both', 'Robe of the Archmage'), +(8, 1, 5, 0, 78, 19136, 'Phase 2', 'Mage', 'Fire', 'Waist', 'Both', 'Mana Igniting Cord'), +(8, 1, 6, 0, 78, 16915, 'Phase 2', 'Mage', 'Fire', 'Legs', 'Both', 'Netherwind Pants'), +(8, 1, 7, 0, 78, 23291, 'Phase 2', 'Mage', 'Fire', 'Feet', 'Both', 'Knight-Lieutenant''s Silk Walkers'), +(8, 1, 8, 0, 78, 11766, 'Phase 2', 'Mage', 'Fire', 'Wrists', 'Both', 'Flameweave Cuffs'), +(8, 1, 9, 0, 78, 13253, 'Phase 2', 'Mage', 'Fire', 'Hands', 'Both', 'Hands of Power'), +(8, 1, 10, 0, 78, 19147, 'Phase 2', 'Mage', 'Fire', 'Finger1', 'Both', 'Ring of Spell Power'), +(8, 1, 11, 0, 78, 19147, 'Phase 2', 'Mage', 'Fire', 'Finger2', 'Both', 'Ring of Spell Power'), +(8, 1, 12, 0, 78, 12930, 'Phase 2', 'Mage', 'Fire', 'Trinket1', 'Both', 'Briarwood Reed'), +(8, 1, 13, 0, 78, 18820, 'Phase 2', 'Mage', 'Fire', 'Trinket2', 'Both', 'Talisman of Ephemeral Power'), +(8, 1, 14, 0, 78, 13386, 'Phase 2', 'Mage', 'Fire', 'Back', 'Both', 'Archivist Cape'), +(8, 1, 15, 0, 78, 17103, 'Phase 2', 'Mage', 'Fire', 'MainHand', 'Both', 'Azuresong Mageblade'), +(8, 1, 16, 0, 78, 10796, 'Phase 2', 'Mage', 'Fire', 'OffHand', 'Both', 'Drakestone'), +(8, 1, 17, 0, 78, 19130, 'Phase 2', 'Mage', 'Fire', 'Ranged', 'Both', 'Cold Snap'); + +-- ilvl 83 (Phase 3) +INSERT INTO `playerbots_bis_gear` VALUES +(8, 1, 0, 0, 83, 19375, 'Phase 3', 'Mage', 'Fire', 'Head', 'Both', 'Mish''undare, Circlet of the Mind Flayer'), +(8, 1, 1, 0, 83, 18814, 'Phase 3', 'Mage', 'Fire', 'Neck', 'Both', 'Choker of the Fire Lord'), +(8, 1, 2, 0, 83, 19370, 'Phase 3', 'Mage', 'Fire', 'Shoulders', 'Both', 'Mantle of the Blackwing Cabal'), +(8, 1, 4, 0, 83, 14152, 'Phase 3', 'Mage', 'Fire', 'Chest', 'Both', 'Robe of the Archmage'), +(8, 1, 5, 0, 83, 19136, 'Phase 3', 'Mage', 'Fire', 'Waist', 'Both', 'Mana Igniting Cord'), +(8, 1, 6, 0, 83, 16915, 'Phase 3', 'Mage', 'Fire', 'Legs', 'Both', 'Netherwind Pants'), +(8, 1, 7, 0, 83, 19438, 'Phase 3', 'Mage', 'Fire', 'Feet', 'Both', 'Ringo''s Blizzard Boots'), +(8, 1, 8, 0, 83, 19374, 'Phase 3', 'Mage', 'Fire', 'Wrists', 'Both', 'Bracers of Arcane Accuracy'), +(8, 1, 9, 0, 83, 16913, 'Phase 3', 'Mage', 'Fire', 'Hands', 'Both', 'Netherwind Gloves'), +(8, 1, 11, 0, 83, 19147, 'Phase 3', 'Mage', 'Fire', 'Finger2', 'Both', 'Ring of Spell Power'), +(8, 1, 12, 0, 83, 19379, 'Phase 3', 'Mage', 'Fire', 'Trinket1', 'Both', 'Neltharion''s Tear'), +(8, 1, 13, 0, 83, 19339, 'Phase 3', 'Mage', 'Fire', 'Trinket2', 'Both', 'Mind Quickening Gem'), +(8, 1, 14, 0, 83, 19378, 'Phase 3', 'Mage', 'Fire', 'Back', 'Both', 'Cloak of the Brood Lord'), +(8, 1, 15, 0, 83, 19356, 'Phase 3', 'Mage', 'Fire', 'MainHand', 'Both', 'Staff of the Shadow Flame'), +(8, 1, 17, 0, 83, 19130, 'Phase 3', 'Mage', 'Fire', 'Ranged', 'Both', 'Cold Snap'); + +-- ilvl 88 (Phase 5) +INSERT INTO `playerbots_bis_gear` VALUES +(8, 1, 0, 0, 88, 19375, 'Phase 5', 'Mage', 'Fire', 'Head', 'Both', 'Mish''undare, Circlet of the Mind Flayer'), +(8, 1, 1, 0, 88, 21608, 'Phase 5', 'Mage', 'Fire', 'Neck', 'Both', 'Amulet of Vek''nilash'), +(8, 1, 2, 0, 88, 19370, 'Phase 5', 'Mage', 'Fire', 'Shoulders', 'Both', 'Mantle of the Blackwing Cabal'), +(8, 1, 4, 0, 88, 19145, 'Phase 5', 'Mage', 'Fire', 'Chest', 'Both', 'Robe of Volatile Power'), +(8, 1, 5, 0, 88, 22730, 'Phase 5', 'Mage', 'Fire', 'Waist', 'Both', 'Eyestalk Waist Cord'), +(8, 1, 6, 0, 88, 21461, 'Phase 5', 'Mage', 'Fire', 'Legs', 'Both', 'Leggings of the Black Blizzard'), +(8, 1, 7, 0, 88, 21344, 'Phase 5', 'Mage', 'Fire', 'Feet', 'Both', 'Enigma Boots'), +(8, 1, 8, 0, 88, 21186, 'Phase 5', 'Mage', 'Fire', 'Wrists', 'Both', 'Rockfury Bracers'), +(8, 1, 9, 0, 88, 21585, 'Phase 5', 'Mage', 'Fire', 'Hands', 'Both', 'Dark Storm Gauntlets'), +(8, 1, 10, 0, 88, 21836, 'Phase 5', 'Mage', 'Fire', 'Finger1', 'Both', 'Ritssyn''s Ring of Chaos'), +(8, 1, 11, 0, 88, 21709, 'Phase 5', 'Mage', 'Fire', 'Finger2', 'Both', 'Ring of the Fallen God'), +(8, 1, 12, 0, 88, 19379, 'Phase 5', 'Mage', 'Fire', 'Trinket1', 'Both', 'Neltharion''s Tear'), +(8, 1, 13, 0, 88, 19339, 'Phase 5', 'Mage', 'Fire', 'Trinket2', 'Both', 'Mind Quickening Gem'), +(8, 1, 14, 0, 88, 22731, 'Phase 5', 'Mage', 'Fire', 'Back', 'Both', 'Cloak of the Devoured'), +(8, 1, 15, 0, 88, 21622, 'Phase 5', 'Mage', 'Fire', 'MainHand', 'Both', 'Sharpened Silithid Femur'), +(8, 1, 16, 0, 88, 21597, 'Phase 5', 'Mage', 'Fire', 'OffHand', 'Both', 'Royal Scepter of Vek''lor'), +(8, 1, 17, 0, 88, 21603, 'Phase 5', 'Mage', 'Fire', 'Ranged', 'Both', 'Wand of Qiraji Nobility'); + +-- ilvl 92 (Phase 6) +INSERT INTO `playerbots_bis_gear` VALUES +(8, 1, 0, 0, 92, 22498, 'Phase 6', 'Mage', 'Fire', 'Head', 'Both', 'Frostfire Circlet'), +(8, 1, 1, 0, 92, 23057, 'Phase 6', 'Mage', 'Fire', 'Neck', 'Both', 'Gem of Trapped Innocents'), +(8, 1, 2, 0, 92, 22983, 'Phase 6', 'Mage', 'Fire', 'Shoulders', 'Both', 'Rime Covered Mantle'), +(8, 1, 4, 0, 92, 22496, 'Phase 6', 'Mage', 'Fire', 'Chest', 'Both', 'Frostfire Robe'), +(8, 1, 5, 0, 92, 22730, 'Phase 6', 'Mage', 'Fire', 'Waist', 'Both', 'Eyestalk Waist Cord'), +(8, 1, 6, 0, 92, 23070, 'Phase 6', 'Mage', 'Fire', 'Legs', 'Both', 'Leggings of Polarity'), +(8, 1, 7, 0, 92, 22500, 'Phase 6', 'Mage', 'Fire', 'Feet', 'Both', 'Frostfire Sandals'), +(8, 1, 8, 0, 92, 23021, 'Phase 6', 'Mage', 'Fire', 'Wrists', 'Both', 'The Soul Harvester''s Bindings'), +(8, 1, 9, 0, 92, 21585, 'Phase 6', 'Mage', 'Fire', 'Hands', 'Both', 'Dark Storm Gauntlets'), +(8, 1, 10, 0, 92, 23237, 'Phase 6', 'Mage', 'Fire', 'Finger1', 'Both', 'Ring of the Eternal Flame'), +(8, 1, 11, 0, 92, 23062, 'Phase 6', 'Mage', 'Fire', 'Finger2', 'Both', 'Frostfire Ring'), +(8, 1, 12, 0, 92, 19379, 'Phase 6', 'Mage', 'Fire', 'Trinket1', 'Both', 'Neltharion''s Tear'), +(8, 1, 13, 0, 92, 23046, 'Phase 6', 'Mage', 'Fire', 'Trinket2', 'Both', 'The Restrained Essence of Sapphiron'), +(8, 1, 14, 0, 92, 23050, 'Phase 6', 'Mage', 'Fire', 'Back', 'Both', 'Cloak of the Necropolis'), +(8, 1, 15, 0, 92, 22807, 'Phase 6', 'Mage', 'Fire', 'MainHand', 'Both', 'Wraith Blade'), +(8, 1, 16, 0, 92, 23049, 'Phase 6', 'Mage', 'Fire', 'OffHand', 'Both', 'Sapphiron''s Left Eye'), +(8, 1, 17, 0, 92, 22821, 'Phase 6', 'Mage', 'Fire', 'Ranged', 'Both', 'Doomfinger'); + +-- ilvl 120 (Pre-Raid) +INSERT INTO `playerbots_bis_gear` VALUES +(8, 1, 0, 0, 120, 24266, 'Pre-Raid', 'Mage', 'Fire', 'Head', 'Both', 'Spellstrike Hood'), +(8, 1, 1, 0, 120, 28134, 'Pre-Raid', 'Mage', 'Fire', 'Neck', 'Both', 'Brooch of Heightened Potential'), +(8, 1, 2, 0, 120, 27994, 'Pre-Raid', 'Mage', 'Fire', 'Shoulders', 'Both', 'Mantle of Three Terrors'), +(8, 1, 4, 0, 120, 21848, 'Pre-Raid', 'Mage', 'Fire', 'Chest', 'Both', 'Spellfire Robe'), +(8, 1, 5, 0, 120, 21846, 'Pre-Raid', 'Mage', 'Fire', 'Waist', 'Both', 'Spellfire Belt'), +(8, 1, 6, 0, 120, 24262, 'Pre-Raid', 'Mage', 'Fire', 'Legs', 'Both', 'Spellstrike Pants'), +(8, 1, 7, 0, 120, 28406, 'Pre-Raid', 'Mage', 'Fire', 'Feet', 'Both', 'Sigil-Laced Boots'), +(8, 1, 8, 0, 120, 29240, 'Pre-Raid', 'Mage', 'Fire', 'Wrists', 'Both', 'Bands of Negation'), +(8, 1, 9, 0, 120, 21847, 'Pre-Raid', 'Mage', 'Fire', 'Hands', 'Both', 'Spellfire Gloves'), +(8, 1, 10, 0, 120, 29172, 'Pre-Raid', 'Mage', 'Fire', 'Finger1', 'Both', 'Ashyen''s Gift'), +(8, 1, 11, 0, 120, 28227, 'Pre-Raid', 'Mage', 'Fire', 'Finger2', 'Both', 'Sparking Arcanite Ring'), +(8, 1, 12, 0, 120, 29370, 'Pre-Raid', 'Mage', 'Fire', 'Trinket1', 'Both', 'Icon of the Silver Crescent'), +(8, 1, 13, 0, 120, 27683, 'Pre-Raid', 'Mage', 'Fire', 'Trinket2', 'Both', 'Quagmirran''s Eye'), +(8, 1, 14, 0, 120, 27981, 'Pre-Raid', 'Mage', 'Fire', 'Back', 'Both', 'Sethekk Oracle Cloak'), +(8, 1, 15, 0, 120, 23554, 'Pre-Raid', 'Mage', 'Fire', 'MainHand', 'Both', 'Eternium Runed Blade'), +(8, 1, 16, 0, 120, 29270, 'Pre-Raid', 'Mage', 'Fire', 'OffHand', 'Both', 'Flametongue Seal'), +(8, 1, 17, 0, 120, 28386, 'Pre-Raid', 'Mage', 'Fire', 'Ranged', 'Both', 'Nether Core''s Control Rod'); + +-- ilvl 125 (Phase 1) +INSERT INTO `playerbots_bis_gear` VALUES +(8, 1, 0, 0, 125, 29076, 'Phase 1', 'Mage', 'Fire', 'Head', 'Both', 'Collar of the Aldor'), +(8, 1, 1, 0, 125, 28530, 'Phase 1', 'Mage', 'Fire', 'Neck', 'Both', 'Brooch of Unquenchable Fury'), +(8, 1, 2, 0, 125, 29079, 'Phase 1', 'Mage', 'Fire', 'Shoulders', 'Both', 'Pauldrons of the Aldor'), +(8, 1, 4, 0, 125, 21848, 'Phase 1', 'Mage', 'Fire', 'Chest', 'Both', 'Spellfire Robe'), +(8, 1, 5, 0, 125, 21846, 'Phase 1', 'Mage', 'Fire', 'Waist', 'Both', 'Spellfire Belt'), +(8, 1, 6, 0, 125, 29078, 'Phase 1', 'Mage', 'Fire', 'Legs', 'Both', 'Legwraps of the Aldor'), +(8, 1, 7, 0, 125, 28517, 'Phase 1', 'Mage', 'Fire', 'Feet', 'Both', 'Boots of Foretelling'), +(8, 1, 8, 0, 125, 28515, 'Phase 1', 'Mage', 'Fire', 'Wrists', 'Both', 'Bands of Nefarious Deeds'), +(8, 1, 9, 0, 125, 21847, 'Phase 1', 'Mage', 'Fire', 'Hands', 'Both', 'Spellfire Gloves'), +(8, 1, 10, 0, 125, 28793, 'Phase 1', 'Mage', 'Fire', 'Finger1', 'Both', 'Band of Crimson Fury'), +(8, 1, 11, 0, 125, 29287, 'Phase 1', 'Mage', 'Fire', 'Finger2', 'Both', 'Violet Signet of the Archmage'), +(8, 1, 12, 0, 125, 29370, 'Phase 1', 'Mage', 'Fire', 'Trinket1', 'Both', 'Icon of the Silver Crescent'), +(8, 1, 13, 0, 125, 27683, 'Phase 1', 'Mage', 'Fire', 'Trinket2', 'Both', 'Quagmirran''s Eye'), +(8, 1, 14, 0, 125, 28766, 'Phase 1', 'Mage', 'Fire', 'Back', 'Both', 'Ruby Drape of the Mysticant'), +(8, 1, 15, 0, 125, 28770, 'Phase 1', 'Mage', 'Fire', 'MainHand', 'Both', 'Nathrezim Mindblade'), +(8, 1, 16, 0, 125, 28734, 'Phase 1', 'Mage', 'Fire', 'OffHand', 'Both', 'Jewel of Infinite Possibilities'), +(8, 1, 17, 0, 125, 28673, 'Phase 1', 'Mage', 'Fire', 'Ranged', 'Both', 'Tirisfal Wand of Ascendancy'); + +-- ilvl 141 (Phase 2) +INSERT INTO `playerbots_bis_gear` VALUES +(8, 1, 0, 0, 141, 32494, 'Phase 2', 'Mage', 'Fire', 'Head', 'Both', 'Destruction Holo-gogs'), +(8, 1, 1, 0, 141, 30015, 'Phase 2', 'Mage', 'Fire', 'Neck', 'Both', 'The Sun King''s Talisman'), +(8, 1, 2, 0, 141, 30024, 'Phase 2', 'Mage', 'Fire', 'Shoulders', 'Both', 'Mantle of the Elven Kings'), +(8, 1, 4, 0, 141, 30107, 'Phase 2', 'Mage', 'Fire', 'Chest', 'Both', 'Vestments of the Sea-Witch'), +(8, 1, 5, 0, 141, 30038, 'Phase 2', 'Mage', 'Fire', 'Waist', 'Both', 'Belt of Blasting'), +(8, 1, 6, 0, 141, 30207, 'Phase 2', 'Mage', 'Fire', 'Legs', 'Both', 'Leggings of Tirisfal'), +(8, 1, 7, 0, 141, 30067, 'Phase 2', 'Mage', 'Fire', 'Feet', 'Both', 'Velvet Boots of the Guardian'), +(8, 1, 8, 0, 141, 29918, 'Phase 2', 'Mage', 'Fire', 'Wrists', 'Both', 'Mindstorm Wristbands'), +(8, 1, 9, 0, 141, 29987, 'Phase 2', 'Mage', 'Fire', 'Hands', 'Both', 'Gauntlets of the Sun King'), +(8, 1, 10, 0, 141, 29302, 'Phase 2', 'Mage', 'Fire', 'Finger1', 'Both', 'Band of Eternity'), +(8, 1, 11, 0, 141, 29287, 'Phase 2', 'Mage', 'Fire', 'Finger2', 'Both', 'Violet Signet of the Archmage'), +(8, 1, 12, 0, 141, 27683, 'Phase 2', 'Mage', 'Fire', 'Trinket1', 'Both', 'Quagmirran''s Eye'), +(8, 1, 13, 0, 141, 30626, 'Phase 2', 'Mage', 'Fire', 'Trinket2', 'Both', 'Sextant of Unstable Currents'), +(8, 1, 14, 0, 141, 28766, 'Phase 2', 'Mage', 'Fire', 'Back', 'Both', 'Ruby Drape of the Mysticant'), +(8, 1, 15, 0, 141, 30095, 'Phase 2', 'Mage', 'Fire', 'MainHand', 'Both', 'Fang of the Leviathan'), +(8, 1, 16, 0, 141, 29270, 'Phase 2', 'Mage', 'Fire', 'OffHand', 'Both', 'Flametongue Seal'), +(8, 1, 17, 0, 141, 29982, 'Phase 2', 'Mage', 'Fire', 'Ranged', 'Both', 'Wand of the Forgotten Star'); + +-- ilvl 156 (Phase 3) +INSERT INTO `playerbots_bis_gear` VALUES +(8, 1, 0, 0, 156, 32525, 'Phase 3', 'Mage', 'Fire', 'Head', 'Both', 'Cowl of the Illidari High Lord'), +(8, 1, 1, 0, 156, 32589, 'Phase 3', 'Mage', 'Fire', 'Neck', 'Both', 'Hellfire-Encased Pendant'), +(8, 1, 2, 0, 156, 31059, 'Phase 3', 'Mage', 'Fire', 'Shoulders', 'Both', 'Mantle of the Tempest'), +(8, 1, 4, 0, 156, 31057, 'Phase 3', 'Mage', 'Fire', 'Chest', 'Both', 'Robes of the Tempest'), +(8, 1, 5, 0, 156, 30038, 'Phase 3', 'Mage', 'Fire', 'Waist', 'Both', 'Belt of Blasting'), +(8, 1, 6, 0, 156, 31058, 'Phase 3', 'Mage', 'Fire', 'Legs', 'Both', 'Leggings of the Tempest'), +(8, 1, 7, 0, 156, 32239, 'Phase 3', 'Mage', 'Fire', 'Feet', 'Both', 'Slippers of the Seacaller'), +(8, 1, 8, 0, 156, 32586, 'Phase 3', 'Mage', 'Fire', 'Wrists', 'Both', 'Bracers of Nimble Thought'), +(8, 1, 9, 0, 156, 31055, 'Phase 3', 'Mage', 'Fire', 'Hands', 'Both', 'Gloves of the Tempest'), +(8, 1, 10, 0, 156, 32527, 'Phase 3', 'Mage', 'Fire', 'Finger1', 'Both', 'Ring of Ancient Knowledge'), +(8, 1, 11, 0, 156, 32527, 'Phase 3', 'Mage', 'Fire', 'Finger2', 'Both', 'Ring of Ancient Knowledge'), +(8, 1, 12, 0, 156, 32488, 'Phase 3', 'Mage', 'Fire', 'Trinket1', 'Both', 'Ashtongue Talisman of Insight'), +(8, 1, 13, 0, 156, 32483, 'Phase 3', 'Mage', 'Fire', 'Trinket2', 'Both', 'The Skull of Gul''dan'), +(8, 1, 14, 0, 156, 32331, 'Phase 3', 'Mage', 'Fire', 'Back', 'Both', 'Cloak of the Illidari Council'), +(8, 1, 15, 0, 156, 30910, 'Phase 3', 'Mage', 'Fire', 'MainHand', 'Both', 'Tempest of Chaos'), +(8, 1, 16, 0, 156, 30872, 'Phase 3', 'Mage', 'Fire', 'OffHand', 'Both', 'Chronicle of Dark Secrets'), +(8, 1, 17, 0, 156, 29982, 'Phase 3', 'Mage', 'Fire', 'Ranged', 'Both', 'Wand of the Forgotten Star'); + +-- ilvl 164 (Phase 4) +INSERT INTO `playerbots_bis_gear` VALUES +(8, 1, 0, 0, 164, 32525, 'Phase 4', 'Mage', 'Fire', 'Head', 'Both', 'Cowl of the Illidari High Lord'), +(8, 1, 1, 0, 164, 32589, 'Phase 4', 'Mage', 'Fire', 'Neck', 'Both', 'Hellfire-Encased Pendant'), +(8, 1, 2, 0, 164, 31059, 'Phase 4', 'Mage', 'Fire', 'Shoulders', 'Both', 'Mantle of the Tempest'), +(8, 1, 4, 0, 164, 31057, 'Phase 4', 'Mage', 'Fire', 'Chest', 'Both', 'Robes of the Tempest'), +(8, 1, 5, 0, 164, 30038, 'Phase 4', 'Mage', 'Fire', 'Waist', 'Both', 'Belt of Blasting'), +(8, 1, 6, 0, 164, 31058, 'Phase 4', 'Mage', 'Fire', 'Legs', 'Both', 'Leggings of the Tempest'), +(8, 1, 7, 0, 164, 32239, 'Phase 4', 'Mage', 'Fire', 'Feet', 'Both', 'Slippers of the Seacaller'), +(8, 1, 8, 0, 164, 32586, 'Phase 4', 'Mage', 'Fire', 'Wrists', 'Both', 'Bracers of Nimble Thought'), +(8, 1, 9, 0, 164, 31055, 'Phase 4', 'Mage', 'Fire', 'Hands', 'Both', 'Gloves of the Tempest'), +(8, 1, 10, 0, 164, 32527, 'Phase 4', 'Mage', 'Fire', 'Finger1', 'Both', 'Ring of Ancient Knowledge'), +(8, 1, 11, 0, 164, 32527, 'Phase 4', 'Mage', 'Fire', 'Finger2', 'Both', 'Ring of Ancient Knowledge'), +(8, 1, 12, 0, 164, 33829, 'Phase 4', 'Mage', 'Fire', 'Trinket1', 'Both', 'Hex Shrunken Head'), +(8, 1, 13, 0, 164, 32483, 'Phase 4', 'Mage', 'Fire', 'Trinket2', 'Both', 'The Skull of Gul''dan'), +(8, 1, 14, 0, 164, 32331, 'Phase 4', 'Mage', 'Fire', 'Back', 'Both', 'Cloak of the Illidari Council'), +(8, 1, 15, 0, 164, 30910, 'Phase 4', 'Mage', 'Fire', 'MainHand', 'Both', 'Tempest of Chaos'), +(8, 1, 16, 0, 164, 30872, 'Phase 4', 'Mage', 'Fire', 'OffHand', 'Both', 'Chronicle of Dark Secrets'), +(8, 1, 17, 0, 164, 29982, 'Phase 4', 'Mage', 'Fire', 'Ranged', 'Both', 'Wand of the Forgotten Star'); + +-- ilvl 200 (Pre-Raid) +INSERT INTO `playerbots_bis_gear` VALUES +(8, 1, 0, 0, 200, 42553, 'Pre-Raid', 'Mage', 'Fire', 'Head', 'Both', 'Visage Liquification Goggles'), +(8, 1, 1, 0, 200, 39472, 'Pre-Raid', 'Mage', 'Fire', 'Neck', 'Both', 'Chain of Latent Energies'), +(8, 1, 2, 0, 200, 37673, 'Pre-Raid', 'Mage', 'Fire', 'Shoulders', 'Both', 'Dark Runic Mantle'), +(8, 1, 4, 0, 200, 39492, 'Pre-Raid', 'Mage', 'Fire', 'Chest', 'Both', 'Heroes'' Frostfire Robe'), +(8, 1, 5, 0, 200, 40696, 'Pre-Raid', 'Mage', 'Fire', 'Waist', 'Both', 'Plush Sash of Guzbah'), +(8, 1, 6, 0, 200, 37854, 'Pre-Raid', 'Mage', 'Fire', 'Legs', 'Both', 'Woven Bracae Leggings'), +(8, 1, 7, 0, 200, 44202, 'Pre-Raid', 'Mage', 'Fire', 'Feet', 'Both', 'Sandals of Crimson Fury'), +(8, 1, 8, 0, 200, 37884, 'Pre-Raid', 'Mage', 'Fire', 'Wrists', 'Both', 'Azure Cloth Bindings'), +(8, 1, 9, 0, 200, 39495, 'Pre-Raid', 'Mage', 'Fire', 'Hands', 'Both', 'Heroes'' Frostfire Gloves'), +(8, 1, 10, 0, 200, 42644, 'Pre-Raid', 'Mage', 'Fire', 'Finger1', 'Both', 'Titanium Spellshock Ring'), +(8, 1, 12, 0, 200, 37873, 'Pre-Raid', 'Mage', 'Fire', 'Trinket1', 'Both', 'Mark of the War Prisoner'), +(8, 1, 14, 0, 200, 41610, 'Pre-Raid', 'Mage', 'Fire', 'Back', 'Both', 'Deathchill Cloak'), +(8, 1, 15, 0, 200, 45085, 'Pre-Raid', 'Mage', 'Fire', 'MainHand', 'Both', 'Titansteel Spellblade'), +(8, 1, 17, 0, 200, 37238, 'Pre-Raid', 'Mage', 'Fire', 'Ranged', 'Both', 'Rod of the Fallen Monarch'); + +-- ilvl 224 (Phase 1) +INSERT INTO `playerbots_bis_gear` VALUES +(8, 1, 0, 0, 224, 40416, 'Phase 1', 'Mage', 'Fire', 'Head', 'Both', 'Valorous Frostfire Circlet'), +(8, 1, 1, 0, 224, 44661, 'Phase 1', 'Mage', 'Fire', 'Neck', 'Both', 'Wyrmrest Necklace of Power'), +(8, 1, 2, 0, 224, 40419, 'Phase 1', 'Mage', 'Fire', 'Shoulders', 'Both', 'Valorous Frostfire Shoulderpads'), +(8, 1, 4, 0, 224, 40418, 'Phase 1', 'Mage', 'Fire', 'Chest', 'Both', 'Valorous Frostfire Robe'), +(8, 1, 5, 0, 224, 40301, 'Phase 1', 'Mage', 'Fire', 'Waist', 'Both', 'Cincture of Polarity'), +(8, 1, 6, 0, 224, 40560, 'Phase 1', 'Mage', 'Fire', 'Legs', 'Both', 'Leggings of the Wanton Spellcaster'), +(8, 1, 7, 0, 224, 40246, 'Phase 1', 'Mage', 'Fire', 'Feet', 'Both', 'Boots of Impetuous Ideals'), +(8, 1, 8, 0, 224, 44008, 'Phase 1', 'Mage', 'Fire', 'Wrists', 'Both', 'Unsullied Cuffs'), +(8, 1, 9, 0, 224, 40415, 'Phase 1', 'Mage', 'Fire', 'Hands', 'Both', 'Valorous Frostfire Gloves'), +(8, 1, 10, 0, 224, 40399, 'Phase 1', 'Mage', 'Fire', 'Finger1', 'Both', 'Signet of Manifested Pain'), +(8, 1, 12, 0, 224, 40255, 'Phase 1', 'Mage', 'Fire', 'Trinket1', 'Both', 'Dying Curse'), +(8, 1, 14, 0, 224, 44005, 'Phase 1', 'Mage', 'Fire', 'Back', 'Both', 'Pennant Cloak'), +(8, 1, 15, 0, 224, 40396, 'Phase 1', 'Mage', 'Fire', 'MainHand', 'Both', 'The Turning Tide'), +(8, 1, 17, 0, 224, 39712, 'Phase 1', 'Mage', 'Fire', 'Ranged', 'Both', 'Gemmed Wand of the Nerubians'); + +-- ilvl 245 (Phase 2) +INSERT INTO `playerbots_bis_gear` VALUES +(8, 1, 0, 0, 245, 46129, 'Phase 2', 'Mage', 'Fire', 'Head', 'Both', 'Conqueror''s Kirin Tor Hood'), +(8, 1, 1, 0, 245, 45133, 'Phase 2', 'Mage', 'Fire', 'Neck', 'Both', 'Pendant of Fiery Havoc'), +(8, 1, 2, 0, 245, 46134, 'Phase 2', 'Mage', 'Fire', 'Shoulders', 'Both', 'Conqueror''s Kirin Tor Shoulderpads'), +(8, 1, 4, 0, 245, 46130, 'Phase 2', 'Mage', 'Fire', 'Chest', 'Both', 'Conqueror''s Kirin Tor Tunic'), +(8, 1, 5, 0, 245, 45619, 'Phase 2', 'Mage', 'Fire', 'Waist', 'Both', 'Starwatcher''s Binding'), +(8, 1, 6, 0, 245, 46133, 'Phase 2', 'Mage', 'Fire', 'Legs', 'Both', 'Conqueror''s Kirin Tor Leggings'), +(8, 1, 7, 0, 245, 45135, 'Phase 2', 'Mage', 'Fire', 'Feet', 'Both', 'Boots of Fiery Resolution'), +(8, 1, 8, 0, 245, 45446, 'Phase 2', 'Mage', 'Fire', 'Wrists', 'Both', 'Grasps of Reason'), +(8, 1, 9, 0, 245, 45665, 'Phase 2', 'Mage', 'Fire', 'Hands', 'Both', 'Pharos Gloves'), +(8, 1, 10, 0, 245, 46046, 'Phase 2', 'Mage', 'Fire', 'Finger1', 'Both', 'Nebula Band'), +(8, 1, 12, 0, 245, 45518, 'Phase 2', 'Mage', 'Fire', 'Trinket1', 'Both', 'Flare of the Heavens'), +(8, 1, 14, 0, 245, 45242, 'Phase 2', 'Mage', 'Fire', 'Back', 'Both', 'Drape of Mortal Downfall'), +(8, 1, 15, 0, 245, 45620, 'Phase 2', 'Mage', 'Fire', 'MainHand', 'Both', 'Starshard Edge'), +(8, 1, 17, 0, 245, 45511, 'Phase 2', 'Mage', 'Fire', 'Ranged', 'Both', 'Scepter of Lost Souls'); + +-- ilvl 258 (Phase 3) +INSERT INTO `playerbots_bis_gear` VALUES +(8, 1, 0, 0, 258, 47761, 'Phase 3', 'Mage', 'Fire', 'Head', 'Both', 'Hood of Triumph'), +(8, 1, 2, 0, 258, 47758, 'Phase 3', 'Mage', 'Fire', 'Shoulders', 'Both', 'Shoulderpads of Triumph'), +(8, 1, 4, 1, 258, 47129, 'Phase 3', 'Mage', 'Fire', 'Chest', 'Alliance', 'Skyweaver Robes'), +(8, 1, 4, 2, 258, 47462, 'Phase 3', 'Mage', 'Fire', 'Chest', 'Horde', 'Skyweaver Vestments'), +(8, 1, 5, 1, 258, 47084, 'Phase 3', 'Mage', 'Fire', 'Waist', 'Alliance', 'Cord of Biting Cold'), +(8, 1, 5, 2, 258, 47447, 'Phase 3', 'Mage', 'Fire', 'Waist', 'Horde', 'Belt of Biting Cold'), +(8, 1, 6, 0, 258, 47760, 'Phase 3', 'Mage', 'Fire', 'Legs', 'Both', 'Leggings of Triumph'), +(8, 1, 7, 1, 258, 47097, 'Phase 3', 'Mage', 'Fire', 'Feet', 'Alliance', 'Boots of the Mourning Widow'), +(8, 1, 7, 2, 258, 47454, 'Phase 3', 'Mage', 'Fire', 'Feet', 'Horde', 'Sandals of the Mourning Widow'), +(8, 1, 8, 1, 258, 47143, 'Phase 3', 'Mage', 'Fire', 'Wrists', 'Alliance', 'Bindings of Dark Essence'), +(8, 1, 8, 2, 258, 47467, 'Phase 3', 'Mage', 'Fire', 'Wrists', 'Horde', 'Dark Essence Bindings'), +(8, 1, 9, 0, 258, 47762, 'Phase 3', 'Mage', 'Fire', 'Hands', 'Both', 'Gauntlets of Triumph'), +(8, 1, 10, 1, 258, 47489, 'Phase 3', 'Mage', 'Fire', 'Finger1', 'Alliance', 'Lurid Manifestation'), +(8, 1, 10, 2, 258, 47237, 'Phase 3', 'Mage', 'Fire', 'Finger1', 'Horde', 'Band of Deplorable Violence'), +(8, 1, 12, 1, 258, 47188, 'Phase 3', 'Mage', 'Fire', 'Trinket1', 'Alliance', 'Reign of the Unliving'), +(8, 1, 12, 2, 258, 47477, 'Phase 3', 'Mage', 'Fire', 'Trinket1', 'Horde', 'Reign of the Dead'), +(8, 1, 14, 1, 258, 47552, 'Phase 3', 'Mage', 'Fire', 'Back', 'Alliance', 'Jaina''s Radiance'), +(8, 1, 14, 2, 258, 47551, 'Phase 3', 'Mage', 'Fire', 'Back', 'Horde', 'Aethas'' Intensity'), +(8, 1, 17, 0, 258, 45294, 'Phase 3', 'Mage', 'Fire', 'Ranged', 'Both', 'Petrified Ivy Sprig'); + +-- ilvl 264 (Phase 4) +INSERT INTO `playerbots_bis_gear` VALUES +(8, 1, 0, 0, 264, 51281, 'Phase 4', 'Mage', 'Fire', 'Head', 'Both', 'Sanctified Bloodmage Hood'), +(8, 1, 1, 0, 264, 50724, 'Phase 4', 'Mage', 'Fire', 'Neck', 'Both', 'Blood Queen''s Crimson Choker'), +(8, 1, 2, 0, 264, 51284, 'Phase 4', 'Mage', 'Fire', 'Shoulders', 'Both', 'Sanctified Bloodmage Shoulderpads'), +(8, 1, 4, 0, 264, 50629, 'Phase 4', 'Mage', 'Fire', 'Chest', 'Both', 'Robe of the Waking Nightmare'), +(8, 1, 5, 0, 264, 50613, 'Phase 4', 'Mage', 'Fire', 'Waist', 'Both', 'Crushing Coldwraith Belt'), +(8, 1, 6, 0, 264, 51282, 'Phase 4', 'Mage', 'Fire', 'Legs', 'Both', 'Sanctified Bloodmage Leggings'), +(8, 1, 7, 0, 264, 50699, 'Phase 4', 'Mage', 'Fire', 'Feet', 'Both', 'Plague Scientist''s Boots'), +(8, 1, 8, 0, 264, 50651, 'Phase 4', 'Mage', 'Fire', 'Wrists', 'Both', 'The Lady''s Brittle Bracers'), +(8, 1, 9, 0, 264, 51280, 'Phase 4', 'Mage', 'Fire', 'Hands', 'Both', 'Sanctified Bloodmage Gloves'), +(8, 1, 10, 0, 264, 50398, 'Phase 4', 'Mage', 'Fire', 'Finger1', 'Both', 'Ashen Band of Endless Destruction'), +(8, 1, 12, 0, 264, 50365, 'Phase 4', 'Mage', 'Fire', 'Trinket1', 'Both', 'Phylactery of the Nameless Lich'), +(8, 1, 14, 0, 264, 50628, 'Phase 4', 'Mage', 'Fire', 'Back', 'Both', 'Frostbinder''s Shredded Cape'), +(8, 1, 15, 0, 264, 50732, 'Phase 4', 'Mage', 'Fire', 'MainHand', 'Both', 'Bloodsurge, Kel''Thuzad''s Blade of Agony'), +(8, 1, 17, 0, 264, 50684, 'Phase 4', 'Mage', 'Fire', 'Ranged', 'Both', 'Corpse-Impaling Spike'); + +-- ilvl 290 (Phase 5) +INSERT INTO `playerbots_bis_gear` VALUES +(8, 1, 0, 0, 290, 51281, 'Phase 5', 'Mage', 'Fire', 'Head', 'Both', 'Sanctified Bloodmage Hood'), +(8, 1, 1, 0, 290, 50182, 'Phase 5', 'Mage', 'Fire', 'Neck', 'Both', 'Blood Queen''s Crimson Choker'), +(8, 1, 2, 0, 290, 51284, 'Phase 5', 'Mage', 'Fire', 'Shoulders', 'Both', 'Sanctified Bloodmage Shoulderpads'), +(8, 1, 4, 0, 290, 51283, 'Phase 5', 'Mage', 'Fire', 'Chest', 'Both', 'Sanctified Bloodmage Robe'), +(8, 1, 5, 0, 290, 50613, 'Phase 5', 'Mage', 'Fire', 'Waist', 'Both', 'Crushing Coldwraith Belt'), +(8, 1, 6, 0, 290, 50694, 'Phase 5', 'Mage', 'Fire', 'Legs', 'Both', 'Plaguebringer''s Stained Pants'), +(8, 1, 7, 0, 290, 50699, 'Phase 5', 'Mage', 'Fire', 'Feet', 'Both', 'Plague Scientist''s Boots'), +(8, 1, 8, 0, 290, 54582, 'Phase 5', 'Mage', 'Fire', 'Wrists', 'Both', 'Bracers of Fiery Night'), +(8, 1, 9, 0, 290, 51280, 'Phase 5', 'Mage', 'Fire', 'Hands', 'Both', 'Sanctified Bloodmage Gloves'), +(8, 1, 10, 0, 290, 50614, 'Phase 5', 'Mage', 'Fire', 'Finger1', 'Both', 'Loop of the Endless Labyrinth'), +(8, 1, 11, 0, 290, 50398, 'Phase 5', 'Mage', 'Fire', 'Finger2', 'Both', 'Ashen Band of Endless Destruction'), +(8, 1, 12, 0, 290, 54588, 'Phase 5', 'Mage', 'Fire', 'Trinket1', 'Both', 'Charred Twilight Scale'), +(8, 1, 13, 0, 290, 50365, 'Phase 5', 'Mage', 'Fire', 'Trinket2', 'Both', 'Phylactery of the Nameless Lich'), +(8, 1, 14, 0, 290, 54583, 'Phase 5', 'Mage', 'Fire', 'Back', 'Both', 'Cloak of Burning Dusk'), +(8, 1, 15, 0, 290, 50732, 'Phase 5', 'Mage', 'Fire', 'MainHand', 'Both', 'Bloodsurge, Kel''Thuzad''s Blade of Agony'), +(8, 1, 16, 0, 290, 50719, 'Phase 5', 'Mage', 'Fire', 'OffHand', 'Both', 'Shadow Silk Spindle'), +(8, 1, 17, 0, 290, 50684, 'Phase 5', 'Mage', 'Fire', 'Ranged', 'Both', 'Corpse-Impaling Spike'); + +-- Frost (tab 2) +-- ilvl 66 (Phase 1 (Pre-Raid)) +INSERT INTO `playerbots_bis_gear` VALUES +(8, 2, 0, 0, 66, 10504, 'Phase 1 (Pre-Raid)', 'Mage', 'Frost', 'Head', 'Both', 'Green Lens'), +(8, 2, 1, 0, 66, 12103, 'Phase 1 (Pre-Raid)', 'Mage', 'Frost', 'Neck', 'Both', 'Star of Mystaria'), +(8, 2, 2, 0, 66, 11782, 'Phase 1 (Pre-Raid)', 'Mage', 'Frost', 'Shoulders', 'Both', 'Boreal Mantle'), +(8, 2, 4, 0, 66, 14152, 'Phase 1 (Pre-Raid)', 'Mage', 'Frost', 'Chest', 'Both', 'Robe of the Archmage'), +(8, 2, 5, 0, 66, 11662, 'Phase 1 (Pre-Raid)', 'Mage', 'Frost', 'Waist', 'Both', 'Ban''thok Sash'), +(8, 2, 6, 0, 66, 13170, 'Phase 1 (Pre-Raid)', 'Mage', 'Frost', 'Legs', 'Both', 'Skyshroud Leggings'), +(8, 2, 7, 0, 66, 11822, 'Phase 1 (Pre-Raid)', 'Mage', 'Frost', 'Feet', 'Both', 'Omnicast Boots'), +(8, 2, 8, 0, 66, 11766, 'Phase 1 (Pre-Raid)', 'Mage', 'Frost', 'Wrists', 'Both', 'Flameweave Cuffs'), +(8, 2, 9, 0, 66, 13253, 'Phase 1 (Pre-Raid)', 'Mage', 'Frost', 'Hands', 'Both', 'Hands of Power'), +(8, 2, 10, 0, 66, 942, 'Phase 1 (Pre-Raid)', 'Mage', 'Frost', 'Finger1', 'Both', 'Freezing Band'), +(8, 2, 11, 0, 66, 942, 'Phase 1 (Pre-Raid)', 'Mage', 'Frost', 'Finger2', 'Both', 'Freezing Band'), +(8, 2, 12, 0, 66, 12930, 'Phase 1 (Pre-Raid)', 'Mage', 'Frost', 'Trinket1', 'Both', 'Briarwood Reed'), +(8, 2, 13, 0, 66, 13968, 'Phase 1 (Pre-Raid)', 'Mage', 'Frost', 'Trinket2', 'Both', 'Eye of the Beast'), +(8, 2, 14, 0, 66, 13386, 'Phase 1 (Pre-Raid)', 'Mage', 'Frost', 'Back', 'Both', 'Archivist Cape'), +(8, 2, 15, 0, 66, 13964, 'Phase 1 (Pre-Raid)', 'Mage', 'Frost', 'MainHand', 'Both', 'Witchblade'), +(8, 2, 16, 0, 66, 10796, 'Phase 1 (Pre-Raid)', 'Mage', 'Frost', 'OffHand', 'Both', 'Drakestone'), +(8, 2, 17, 0, 66, 13938, 'Phase 1 (Pre-Raid)', 'Mage', 'Frost', 'Ranged', 'Both', 'Bonecreeper Stylus'); + +-- ilvl 76 (Phase 2 (Pre-Raid)) +INSERT INTO `playerbots_bis_gear` VALUES +(8, 2, 0, 0, 76, 23318, 'Phase 2 (Pre-Raid)', 'Mage', 'Frost', 'Head', 'Both', 'Lieutenant Commander''s Silk Cowl'), +(8, 2, 1, 0, 76, 12103, 'Phase 2 (Pre-Raid)', 'Mage', 'Frost', 'Neck', 'Both', 'Star of Mystaria'), +(8, 2, 2, 0, 76, 23319, 'Phase 2 (Pre-Raid)', 'Mage', 'Frost', 'Shoulders', 'Both', 'Lieutenant Commander''s Silk Mantle'), +(8, 2, 4, 0, 76, 14152, 'Phase 2 (Pre-Raid)', 'Mage', 'Frost', 'Chest', 'Both', 'Robe of the Archmage'), +(8, 2, 5, 0, 76, 11662, 'Phase 2 (Pre-Raid)', 'Mage', 'Frost', 'Waist', 'Both', 'Ban''thok Sash'), +(8, 2, 6, 0, 76, 23304, 'Phase 2 (Pre-Raid)', 'Mage', 'Frost', 'Legs', 'Both', 'Knight-Captain''s Silk Legguards'), +(8, 2, 7, 0, 76, 23291, 'Phase 2 (Pre-Raid)', 'Mage', 'Frost', 'Feet', 'Both', 'Knight-Lieutenant''s Silk Walkers'), +(8, 2, 8, 0, 76, 11766, 'Phase 2 (Pre-Raid)', 'Mage', 'Frost', 'Wrists', 'Both', 'Flameweave Cuffs'), +(8, 2, 9, 0, 76, 13253, 'Phase 2 (Pre-Raid)', 'Mage', 'Frost', 'Hands', 'Both', 'Hands of Power'), +(8, 2, 10, 0, 76, 942, 'Phase 2 (Pre-Raid)', 'Mage', 'Frost', 'Finger1', 'Both', 'Freezing Band'), +(8, 2, 11, 0, 76, 942, 'Phase 2 (Pre-Raid)', 'Mage', 'Frost', 'Finger2', 'Both', 'Freezing Band'), +(8, 2, 12, 0, 76, 12930, 'Phase 2 (Pre-Raid)', 'Mage', 'Frost', 'Trinket1', 'Both', 'Briarwood Reed'), +(8, 2, 13, 0, 76, 13968, 'Phase 2 (Pre-Raid)', 'Mage', 'Frost', 'Trinket2', 'Both', 'Eye of the Beast'), +(8, 2, 14, 0, 76, 13386, 'Phase 2 (Pre-Raid)', 'Mage', 'Frost', 'Back', 'Both', 'Archivist Cape'), +(8, 2, 15, 0, 76, 13964, 'Phase 2 (Pre-Raid)', 'Mage', 'Frost', 'MainHand', 'Both', 'Witchblade'), +(8, 2, 16, 0, 76, 10796, 'Phase 2 (Pre-Raid)', 'Mage', 'Frost', 'OffHand', 'Both', 'Drakestone'), +(8, 2, 17, 0, 76, 13938, 'Phase 2 (Pre-Raid)', 'Mage', 'Frost', 'Ranged', 'Both', 'Bonecreeper Stylus'); + +-- ilvl 78 (Phase 2) +INSERT INTO `playerbots_bis_gear` VALUES +(8, 2, 0, 0, 78, 16914, 'Phase 2', 'Mage', 'Frost', 'Head', 'Both', 'Netherwind Crown'), +(8, 2, 1, 0, 78, 18814, 'Phase 2', 'Mage', 'Frost', 'Neck', 'Both', 'Choker of the Fire Lord'), +(8, 2, 2, 0, 78, 23319, 'Phase 2', 'Mage', 'Frost', 'Shoulders', 'Both', 'Lieutenant Commander''s Silk Mantle'), +(8, 2, 4, 0, 78, 14152, 'Phase 2', 'Mage', 'Frost', 'Chest', 'Both', 'Robe of the Archmage'), +(8, 2, 5, 0, 78, 19136, 'Phase 2', 'Mage', 'Frost', 'Waist', 'Both', 'Mana Igniting Cord'), +(8, 2, 6, 0, 78, 16915, 'Phase 2', 'Mage', 'Frost', 'Legs', 'Both', 'Netherwind Pants'), +(8, 2, 7, 0, 78, 23291, 'Phase 2', 'Mage', 'Frost', 'Feet', 'Both', 'Knight-Lieutenant''s Silk Walkers'), +(8, 2, 8, 0, 78, 11766, 'Phase 2', 'Mage', 'Frost', 'Wrists', 'Both', 'Flameweave Cuffs'), +(8, 2, 9, 0, 78, 13253, 'Phase 2', 'Mage', 'Frost', 'Hands', 'Both', 'Hands of Power'), +(8, 2, 10, 0, 78, 19147, 'Phase 2', 'Mage', 'Frost', 'Finger1', 'Both', 'Ring of Spell Power'), +(8, 2, 11, 0, 78, 19147, 'Phase 2', 'Mage', 'Frost', 'Finger2', 'Both', 'Ring of Spell Power'), +(8, 2, 12, 0, 78, 12930, 'Phase 2', 'Mage', 'Frost', 'Trinket1', 'Both', 'Briarwood Reed'), +(8, 2, 13, 0, 78, 18820, 'Phase 2', 'Mage', 'Frost', 'Trinket2', 'Both', 'Talisman of Ephemeral Power'), +(8, 2, 14, 0, 78, 13386, 'Phase 2', 'Mage', 'Frost', 'Back', 'Both', 'Archivist Cape'), +(8, 2, 15, 0, 78, 17103, 'Phase 2', 'Mage', 'Frost', 'MainHand', 'Both', 'Azuresong Mageblade'), +(8, 2, 16, 0, 78, 10796, 'Phase 2', 'Mage', 'Frost', 'OffHand', 'Both', 'Drakestone'), +(8, 2, 17, 0, 78, 19130, 'Phase 2', 'Mage', 'Frost', 'Ranged', 'Both', 'Cold Snap'); + +-- ilvl 83 (Phase 3) +INSERT INTO `playerbots_bis_gear` VALUES +(8, 2, 0, 0, 83, 19375, 'Phase 3', 'Mage', 'Frost', 'Head', 'Both', 'Mish''undare, Circlet of the Mind Flayer'), +(8, 2, 1, 0, 83, 18814, 'Phase 3', 'Mage', 'Frost', 'Neck', 'Both', 'Choker of the Fire Lord'), +(8, 2, 2, 0, 83, 19370, 'Phase 3', 'Mage', 'Frost', 'Shoulders', 'Both', 'Mantle of the Blackwing Cabal'), +(8, 2, 4, 0, 83, 14152, 'Phase 3', 'Mage', 'Frost', 'Chest', 'Both', 'Robe of the Archmage'), +(8, 2, 5, 0, 83, 19136, 'Phase 3', 'Mage', 'Frost', 'Waist', 'Both', 'Mana Igniting Cord'), +(8, 2, 6, 0, 83, 16915, 'Phase 3', 'Mage', 'Frost', 'Legs', 'Both', 'Netherwind Pants'), +(8, 2, 7, 0, 83, 19438, 'Phase 3', 'Mage', 'Frost', 'Feet', 'Both', 'Ringo''s Blizzard Boots'), +(8, 2, 8, 0, 83, 19374, 'Phase 3', 'Mage', 'Frost', 'Wrists', 'Both', 'Bracers of Arcane Accuracy'), +(8, 2, 9, 0, 83, 16913, 'Phase 3', 'Mage', 'Frost', 'Hands', 'Both', 'Netherwind Gloves'), +(8, 2, 11, 0, 83, 19147, 'Phase 3', 'Mage', 'Frost', 'Finger2', 'Both', 'Ring of Spell Power'), +(8, 2, 12, 0, 83, 19379, 'Phase 3', 'Mage', 'Frost', 'Trinket1', 'Both', 'Neltharion''s Tear'), +(8, 2, 13, 0, 83, 19339, 'Phase 3', 'Mage', 'Frost', 'Trinket2', 'Both', 'Mind Quickening Gem'), +(8, 2, 14, 0, 83, 19378, 'Phase 3', 'Mage', 'Frost', 'Back', 'Both', 'Cloak of the Brood Lord'), +(8, 2, 15, 0, 83, 19356, 'Phase 3', 'Mage', 'Frost', 'MainHand', 'Both', 'Staff of the Shadow Flame'), +(8, 2, 17, 0, 83, 19130, 'Phase 3', 'Mage', 'Frost', 'Ranged', 'Both', 'Cold Snap'); + +-- ilvl 88 (Phase 5) +INSERT INTO `playerbots_bis_gear` VALUES +(8, 2, 0, 0, 88, 19375, 'Phase 5', 'Mage', 'Frost', 'Head', 'Both', 'Mish''undare, Circlet of the Mind Flayer'), +(8, 2, 1, 0, 88, 21608, 'Phase 5', 'Mage', 'Frost', 'Neck', 'Both', 'Amulet of Vek''nilash'), +(8, 2, 2, 0, 88, 19370, 'Phase 5', 'Mage', 'Frost', 'Shoulders', 'Both', 'Mantle of the Blackwing Cabal'), +(8, 2, 4, 0, 88, 19145, 'Phase 5', 'Mage', 'Frost', 'Chest', 'Both', 'Robe of Volatile Power'), +(8, 2, 5, 0, 88, 22730, 'Phase 5', 'Mage', 'Frost', 'Waist', 'Both', 'Eyestalk Waist Cord'), +(8, 2, 6, 0, 88, 21461, 'Phase 5', 'Mage', 'Frost', 'Legs', 'Both', 'Leggings of the Black Blizzard'), +(8, 2, 7, 0, 88, 21344, 'Phase 5', 'Mage', 'Frost', 'Feet', 'Both', 'Enigma Boots'), +(8, 2, 8, 0, 88, 21186, 'Phase 5', 'Mage', 'Frost', 'Wrists', 'Both', 'Rockfury Bracers'), +(8, 2, 9, 0, 88, 21585, 'Phase 5', 'Mage', 'Frost', 'Hands', 'Both', 'Dark Storm Gauntlets'), +(8, 2, 10, 0, 88, 21836, 'Phase 5', 'Mage', 'Frost', 'Finger1', 'Both', 'Ritssyn''s Ring of Chaos'), +(8, 2, 11, 0, 88, 21709, 'Phase 5', 'Mage', 'Frost', 'Finger2', 'Both', 'Ring of the Fallen God'), +(8, 2, 12, 0, 88, 19379, 'Phase 5', 'Mage', 'Frost', 'Trinket1', 'Both', 'Neltharion''s Tear'), +(8, 2, 13, 0, 88, 19339, 'Phase 5', 'Mage', 'Frost', 'Trinket2', 'Both', 'Mind Quickening Gem'), +(8, 2, 14, 0, 88, 22731, 'Phase 5', 'Mage', 'Frost', 'Back', 'Both', 'Cloak of the Devoured'), +(8, 2, 15, 0, 88, 21622, 'Phase 5', 'Mage', 'Frost', 'MainHand', 'Both', 'Sharpened Silithid Femur'), +(8, 2, 16, 0, 88, 21597, 'Phase 5', 'Mage', 'Frost', 'OffHand', 'Both', 'Royal Scepter of Vek''lor'), +(8, 2, 17, 0, 88, 21603, 'Phase 5', 'Mage', 'Frost', 'Ranged', 'Both', 'Wand of Qiraji Nobility'); + +-- ilvl 92 (Phase 6) +INSERT INTO `playerbots_bis_gear` VALUES +(8, 2, 0, 0, 92, 22498, 'Phase 6', 'Mage', 'Frost', 'Head', 'Both', 'Frostfire Circlet'), +(8, 2, 1, 0, 92, 23057, 'Phase 6', 'Mage', 'Frost', 'Neck', 'Both', 'Gem of Trapped Innocents'), +(8, 2, 2, 0, 92, 22983, 'Phase 6', 'Mage', 'Frost', 'Shoulders', 'Both', 'Rime Covered Mantle'), +(8, 2, 4, 0, 92, 22496, 'Phase 6', 'Mage', 'Frost', 'Chest', 'Both', 'Frostfire Robe'), +(8, 2, 5, 0, 92, 22730, 'Phase 6', 'Mage', 'Frost', 'Waist', 'Both', 'Eyestalk Waist Cord'), +(8, 2, 6, 0, 92, 23070, 'Phase 6', 'Mage', 'Frost', 'Legs', 'Both', 'Leggings of Polarity'), +(8, 2, 7, 0, 92, 22500, 'Phase 6', 'Mage', 'Frost', 'Feet', 'Both', 'Frostfire Sandals'), +(8, 2, 8, 0, 92, 23021, 'Phase 6', 'Mage', 'Frost', 'Wrists', 'Both', 'The Soul Harvester''s Bindings'), +(8, 2, 9, 0, 92, 21585, 'Phase 6', 'Mage', 'Frost', 'Hands', 'Both', 'Dark Storm Gauntlets'), +(8, 2, 10, 0, 92, 23237, 'Phase 6', 'Mage', 'Frost', 'Finger1', 'Both', 'Ring of the Eternal Flame'), +(8, 2, 11, 0, 92, 23062, 'Phase 6', 'Mage', 'Frost', 'Finger2', 'Both', 'Frostfire Ring'), +(8, 2, 12, 0, 92, 19379, 'Phase 6', 'Mage', 'Frost', 'Trinket1', 'Both', 'Neltharion''s Tear'), +(8, 2, 13, 0, 92, 23046, 'Phase 6', 'Mage', 'Frost', 'Trinket2', 'Both', 'The Restrained Essence of Sapphiron'), +(8, 2, 14, 0, 92, 23050, 'Phase 6', 'Mage', 'Frost', 'Back', 'Both', 'Cloak of the Necropolis'), +(8, 2, 15, 0, 92, 22807, 'Phase 6', 'Mage', 'Frost', 'MainHand', 'Both', 'Wraith Blade'), +(8, 2, 16, 0, 92, 23049, 'Phase 6', 'Mage', 'Frost', 'OffHand', 'Both', 'Sapphiron''s Left Eye'), +(8, 2, 17, 0, 92, 22821, 'Phase 6', 'Mage', 'Frost', 'Ranged', 'Both', 'Doomfinger'); + +-- ilvl 120 (Pre-Raid) +INSERT INTO `playerbots_bis_gear` VALUES +(8, 2, 0, 0, 120, 24266, 'Pre-Raid', 'Mage', 'Frost', 'Head', 'Both', 'Spellstrike Hood'), +(8, 2, 1, 0, 120, 28134, 'Pre-Raid', 'Mage', 'Frost', 'Neck', 'Both', 'Brooch of Heightened Potential'), +(8, 2, 2, 0, 120, 21869, 'Pre-Raid', 'Mage', 'Frost', 'Shoulders', 'Both', 'Frozen Shadoweave Shoulders'), +(8, 2, 4, 0, 120, 21871, 'Pre-Raid', 'Mage', 'Frost', 'Chest', 'Both', 'Frozen Shadoweave Robe'), +(8, 2, 5, 0, 120, 24256, 'Pre-Raid', 'Mage', 'Frost', 'Waist', 'Both', 'Girdle of Ruination'), +(8, 2, 6, 0, 120, 24262, 'Pre-Raid', 'Mage', 'Frost', 'Legs', 'Both', 'Spellstrike Pants'), +(8, 2, 7, 0, 120, 21870, 'Pre-Raid', 'Mage', 'Frost', 'Feet', 'Both', 'Frozen Shadoweave Boots'), +(8, 2, 8, 0, 120, 24250, 'Pre-Raid', 'Mage', 'Frost', 'Wrists', 'Both', 'Bracers of Havok'), +(8, 2, 9, 0, 120, 30725, 'Pre-Raid', 'Mage', 'Frost', 'Hands', 'Both', 'Anger-Spark Gloves'), +(8, 2, 10, 0, 120, 29172, 'Pre-Raid', 'Mage', 'Frost', 'Finger1', 'Both', 'Ashyen''s Gift'), +(8, 2, 11, 0, 120, 28227, 'Pre-Raid', 'Mage', 'Frost', 'Finger2', 'Both', 'Sparking Arcanite Ring'), +(8, 2, 12, 0, 120, 29370, 'Pre-Raid', 'Mage', 'Frost', 'Trinket1', 'Both', 'Icon of the Silver Crescent'), +(8, 2, 13, 0, 120, 27683, 'Pre-Raid', 'Mage', 'Frost', 'Trinket2', 'Both', 'Quagmirran''s Eye'), +(8, 2, 14, 0, 120, 27981, 'Pre-Raid', 'Mage', 'Frost', 'Back', 'Both', 'Sethekk Oracle Cloak'), +(8, 2, 15, 0, 120, 23554, 'Pre-Raid', 'Mage', 'Frost', 'MainHand', 'Both', 'Eternium Runed Blade'), +(8, 2, 16, 0, 120, 29273, 'Pre-Raid', 'Mage', 'Frost', 'OffHand', 'Both', 'Khadgar''s Knapsack'), +(8, 2, 17, 0, 120, 29350, 'Pre-Raid', 'Mage', 'Frost', 'Ranged', 'Both', 'The Black Stalk'); + +-- ilvl 125 (Phase 1) +INSERT INTO `playerbots_bis_gear` VALUES +(8, 2, 0, 0, 125, 29076, 'Phase 1', 'Mage', 'Frost', 'Head', 'Both', 'Collar of the Aldor'), +(8, 2, 1, 0, 125, 28530, 'Phase 1', 'Mage', 'Frost', 'Neck', 'Both', 'Brooch of Unquenchable Fury'), +(8, 2, 2, 0, 125, 21869, 'Phase 1', 'Mage', 'Frost', 'Shoulders', 'Both', 'Frozen Shadoweave Shoulders'), +(8, 2, 4, 0, 125, 21871, 'Phase 1', 'Mage', 'Frost', 'Chest', 'Both', 'Frozen Shadoweave Robe'), +(8, 2, 5, 0, 125, 24256, 'Phase 1', 'Mage', 'Frost', 'Waist', 'Both', 'Girdle of Ruination'), +(8, 2, 6, 0, 125, 29078, 'Phase 1', 'Mage', 'Frost', 'Legs', 'Both', 'Legwraps of the Aldor'), +(8, 2, 7, 0, 125, 21870, 'Phase 1', 'Mage', 'Frost', 'Feet', 'Both', 'Frozen Shadoweave Boots'), +(8, 2, 8, 0, 125, 28515, 'Phase 1', 'Mage', 'Frost', 'Wrists', 'Both', 'Bands of Nefarious Deeds'), +(8, 2, 9, 0, 125, 30725, 'Phase 1', 'Mage', 'Frost', 'Hands', 'Both', 'Anger-Spark Gloves'), +(8, 2, 10, 0, 125, 28793, 'Phase 1', 'Mage', 'Frost', 'Finger1', 'Both', 'Band of Crimson Fury'), +(8, 2, 11, 0, 125, 29287, 'Phase 1', 'Mage', 'Frost', 'Finger2', 'Both', 'Violet Signet of the Archmage'), +(8, 2, 12, 0, 125, 29370, 'Phase 1', 'Mage', 'Frost', 'Trinket1', 'Both', 'Icon of the Silver Crescent'), +(8, 2, 13, 0, 125, 27683, 'Phase 1', 'Mage', 'Frost', 'Trinket2', 'Both', 'Quagmirran''s Eye'), +(8, 2, 14, 0, 125, 28766, 'Phase 1', 'Mage', 'Frost', 'Back', 'Both', 'Ruby Drape of the Mysticant'), +(8, 2, 15, 0, 125, 28770, 'Phase 1', 'Mage', 'Frost', 'MainHand', 'Both', 'Nathrezim Mindblade'), +(8, 2, 16, 0, 125, 29269, 'Phase 1', 'Mage', 'Frost', 'OffHand', 'Both', 'Sapphiron''s Wing Bone'), +(8, 2, 17, 0, 125, 28783, 'Phase 1', 'Mage', 'Frost', 'Ranged', 'Both', 'Eredar Wand of Obliteration'); + +-- ilvl 200 (Pre-Raid) +INSERT INTO `playerbots_bis_gear` VALUES +(8, 2, 0, 0, 200, 37294, 'Pre-Raid', 'Mage', 'Frost', 'Head', 'Both', 'Crown of Unbridled Magic'), +(8, 2, 1, 0, 200, 39472, 'Pre-Raid', 'Mage', 'Frost', 'Neck', 'Both', 'Chain of Latent Energies'), +(8, 2, 2, 0, 200, 37673, 'Pre-Raid', 'Mage', 'Frost', 'Shoulders', 'Both', 'Dark Runic Mantle'), +(8, 2, 4, 0, 200, 39492, 'Pre-Raid', 'Mage', 'Frost', 'Chest', 'Both', 'Heroes'' Frostfire Robe'), +(8, 2, 5, 0, 200, 40696, 'Pre-Raid', 'Mage', 'Frost', 'Waist', 'Both', 'Plush Sash of Guzbah'), +(8, 2, 6, 0, 200, 37854, 'Pre-Raid', 'Mage', 'Frost', 'Legs', 'Both', 'Woven Bracae Leggings'), +(8, 2, 7, 0, 200, 44202, 'Pre-Raid', 'Mage', 'Frost', 'Feet', 'Both', 'Sandals of Crimson Fury'), +(8, 2, 8, 0, 200, 37361, 'Pre-Raid', 'Mage', 'Frost', 'Wrists', 'Both', 'Cuffs of Winged Levitation'), +(8, 2, 9, 0, 200, 39495, 'Pre-Raid', 'Mage', 'Frost', 'Hands', 'Both', 'Heroes'' Frostfire Gloves'), +(8, 2, 10, 0, 200, 40585, 'Pre-Raid', 'Mage', 'Frost', 'Finger1', 'Both', 'Signet of the Kirin Tor'), +(8, 2, 12, 0, 200, 37660, 'Pre-Raid', 'Mage', 'Frost', 'Trinket1', 'Both', 'Forge Ember'), +(8, 2, 14, 0, 200, 41610, 'Pre-Raid', 'Mage', 'Frost', 'Back', 'Both', 'Deathchill Cloak'), +(8, 2, 15, 0, 200, 45085, 'Pre-Raid', 'Mage', 'Frost', 'MainHand', 'Both', 'Titansteel Spellblade'), +(8, 2, 17, 0, 200, 37177, 'Pre-Raid', 'Mage', 'Frost', 'Ranged', 'Both', 'Wand of the San''layn'); + +-- ilvl 224 (Phase 1) +INSERT INTO `playerbots_bis_gear` VALUES +(8, 2, 1, 0, 224, 44661, 'Phase 1', 'Mage', 'Frost', 'Neck', 'Both', 'Wyrmrest Necklace of Power'), +(8, 2, 2, 0, 224, 40419, 'Phase 1', 'Mage', 'Frost', 'Shoulders', 'Both', 'Valorous Frostfire Shoulderpads'), +(8, 2, 4, 0, 224, 40418, 'Phase 1', 'Mage', 'Frost', 'Chest', 'Both', 'Valorous Frostfire Robe'), +(8, 2, 5, 0, 224, 40561, 'Phase 1', 'Mage', 'Frost', 'Waist', 'Both', 'Leash of Heedless Magic'), +(8, 2, 6, 0, 224, 40560, 'Phase 1', 'Mage', 'Frost', 'Legs', 'Both', 'Leggings of the Wanton Spellcaster'), +(8, 2, 7, 0, 224, 40558, 'Phase 1', 'Mage', 'Frost', 'Feet', 'Both', 'Arcanic Tramplers'), +(8, 2, 8, 0, 224, 44008, 'Phase 1', 'Mage', 'Frost', 'Wrists', 'Both', 'Unsullied Cuffs'), +(8, 2, 9, 0, 224, 40415, 'Phase 1', 'Mage', 'Frost', 'Hands', 'Both', 'Valorous Frostfire Gloves'), +(8, 2, 10, 0, 224, 40719, 'Phase 1', 'Mage', 'Frost', 'Finger1', 'Both', 'Band of Channeled Magic'), +(8, 2, 12, 0, 224, 40255, 'Phase 1', 'Mage', 'Frost', 'Trinket1', 'Both', 'Dying Curse'), +(8, 2, 14, 0, 224, 44005, 'Phase 1', 'Mage', 'Frost', 'Back', 'Both', 'Pennant Cloak'), +(8, 2, 15, 0, 224, 40396, 'Phase 1', 'Mage', 'Frost', 'MainHand', 'Both', 'The Turning Tide'), +(8, 2, 17, 0, 224, 39426, 'Phase 1', 'Mage', 'Frost', 'Ranged', 'Both', 'Wand of the Archlich'); + +-- ilvl 245 (Phase 2) +INSERT INTO `playerbots_bis_gear` VALUES +(8, 2, 0, 0, 245, 46129, 'Phase 2', 'Mage', 'Frost', 'Head', 'Both', 'Conqueror''s Kirin Tor Hood'), +(8, 2, 1, 0, 245, 45133, 'Phase 2', 'Mage', 'Frost', 'Neck', 'Both', 'Pendant of Fiery Havoc'), +(8, 2, 2, 0, 245, 46134, 'Phase 2', 'Mage', 'Frost', 'Shoulders', 'Both', 'Conqueror''s Kirin Tor Shoulderpads'), +(8, 2, 4, 0, 245, 46130, 'Phase 2', 'Mage', 'Frost', 'Chest', 'Both', 'Conqueror''s Kirin Tor Tunic'), +(8, 2, 5, 0, 245, 45619, 'Phase 2', 'Mage', 'Frost', 'Waist', 'Both', 'Starwatcher''s Binding'), +(8, 2, 6, 0, 245, 46133, 'Phase 2', 'Mage', 'Frost', 'Legs', 'Both', 'Conqueror''s Kirin Tor Leggings'), +(8, 2, 7, 0, 245, 45135, 'Phase 2', 'Mage', 'Frost', 'Feet', 'Both', 'Boots of Fiery Resolution'), +(8, 2, 8, 0, 245, 45446, 'Phase 2', 'Mage', 'Frost', 'Wrists', 'Both', 'Grasps of Reason'), +(8, 2, 9, 0, 245, 45665, 'Phase 2', 'Mage', 'Frost', 'Hands', 'Both', 'Pharos Gloves'), +(8, 2, 10, 0, 245, 46046, 'Phase 2', 'Mage', 'Frost', 'Finger1', 'Both', 'Nebula Band'), +(8, 2, 12, 0, 245, 45466, 'Phase 2', 'Mage', 'Frost', 'Trinket1', 'Both', 'Scale of Fates'), +(8, 2, 14, 0, 245, 45242, 'Phase 2', 'Mage', 'Frost', 'Back', 'Both', 'Drape of Mortal Downfall'), +(8, 2, 15, 0, 245, 45620, 'Phase 2', 'Mage', 'Frost', 'MainHand', 'Both', 'Starshard Edge'), +(8, 2, 17, 0, 245, 45511, 'Phase 2', 'Mage', 'Frost', 'Ranged', 'Both', 'Scepter of Lost Souls'); + +-- ilvl 258 (Phase 3) +INSERT INTO `playerbots_bis_gear` VALUES +(8, 2, 0, 1, 258, 47761, 'Phase 3', 'Mage', 'Frost', 'Head', 'Alliance', 'Khadgar''s Hood of Triumph'), +(8, 2, 2, 1, 258, 47758, 'Phase 3', 'Mage', 'Frost', 'Shoulders', 'Alliance', 'Khadgar''s Shoulderpads of Triumph'), +(8, 2, 4, 1, 258, 47129, 'Phase 3', 'Mage', 'Frost', 'Chest', 'Alliance', 'Skyweaver Robes'), +(8, 2, 5, 1, 258, 47419, 'Phase 3', 'Mage', 'Frost', 'Waist', 'Alliance', 'Belt of the Tenebrous Mist'), +(8, 2, 6, 1, 258, 47760, 'Phase 3', 'Mage', 'Frost', 'Legs', 'Alliance', 'Khadgar''s Leggings of Triumph'), +(8, 2, 7, 1, 258, 47097, 'Phase 3', 'Mage', 'Frost', 'Feet', 'Alliance', 'Boots of the Mourning Widow'), +(8, 2, 8, 1, 258, 47143, 'Phase 3', 'Mage', 'Frost', 'Wrists', 'Alliance', 'Bindings of Dark Essence'), +(8, 2, 9, 1, 258, 47762, 'Phase 3', 'Mage', 'Frost', 'Hands', 'Alliance', 'Khadgar''s Gauntlets of Triumph'), +(8, 2, 10, 1, 258, 47489, 'Phase 3', 'Mage', 'Frost', 'Finger1', 'Alliance', 'Lurid Manifestation'), +(8, 2, 12, 1, 258, 47188, 'Phase 3', 'Mage', 'Frost', 'Trinket1', 'Alliance', 'Reign of the Unliving'), +(8, 2, 14, 1, 258, 47552, 'Phase 3', 'Mage', 'Frost', 'Back', 'Alliance', 'Jaina''s Radiance'), +(8, 2, 17, 0, 258, 45294, 'Phase 3', 'Mage', 'Frost', 'Ranged', 'Both', 'Petrified Ivy Sprig'); + +-- ilvl 264 (Phase 4) +INSERT INTO `playerbots_bis_gear` VALUES +(8, 2, 0, 0, 264, 51281, 'Phase 4', 'Mage', 'Frost', 'Head', 'Both', 'Sanctified Bloodmage Hood'), +(8, 2, 1, 0, 264, 50724, 'Phase 4', 'Mage', 'Frost', 'Neck', 'Both', 'Blood Queen''s Crimson Choker'), +(8, 2, 2, 0, 264, 51284, 'Phase 4', 'Mage', 'Frost', 'Shoulders', 'Both', 'Sanctified Bloodmage Shoulderpads'), +(8, 2, 4, 0, 264, 50717, 'Phase 4', 'Mage', 'Frost', 'Chest', 'Both', 'Sanguine Silk Robes'), +(8, 2, 5, 0, 264, 50613, 'Phase 4', 'Mage', 'Frost', 'Waist', 'Both', 'Crushing Coldwraith Belt'), +(8, 2, 6, 0, 264, 51282, 'Phase 4', 'Mage', 'Frost', 'Legs', 'Both', 'Sanctified Bloodmage Leggings'), +(8, 2, 7, 0, 264, 50699, 'Phase 4', 'Mage', 'Frost', 'Feet', 'Both', 'Plague Scientist''s Boots'), +(8, 2, 8, 0, 264, 50651, 'Phase 4', 'Mage', 'Frost', 'Wrists', 'Both', 'The Lady''s Brittle Bracers'), +(8, 2, 9, 0, 264, 51280, 'Phase 4', 'Mage', 'Frost', 'Hands', 'Both', 'Sanctified Bloodmage Gloves'), +(8, 2, 10, 0, 264, 50398, 'Phase 4', 'Mage', 'Frost', 'Finger1', 'Both', 'Ashen Band of Endless Destruction'), +(8, 2, 12, 0, 264, 50365, 'Phase 4', 'Mage', 'Frost', 'Trinket1', 'Both', 'Phylactery of the Nameless Lich'), +(8, 2, 14, 0, 264, 50628, 'Phase 4', 'Mage', 'Frost', 'Back', 'Both', 'Frostbinder''s Shredded Cape'), +(8, 2, 15, 0, 264, 50732, 'Phase 4', 'Mage', 'Frost', 'MainHand', 'Both', 'Bloodsurge, Kel''Thuzad''s Blade of Agony'), +(8, 2, 17, 0, 264, 50684, 'Phase 4', 'Mage', 'Frost', 'Ranged', 'Both', 'Corpse-Impaling Spike'); + +-- ilvl 290 (Phase 5) +INSERT INTO `playerbots_bis_gear` VALUES +(8, 2, 0, 0, 290, 51281, 'Phase 5', 'Mage', 'Frost', 'Head', 'Both', 'Sanctified Bloodmage Hood'), +(8, 2, 1, 0, 290, 50182, 'Phase 5', 'Mage', 'Frost', 'Neck', 'Both', 'Blood Queen''s Crimson Choker'), +(8, 2, 2, 0, 290, 51284, 'Phase 5', 'Mage', 'Frost', 'Shoulders', 'Both', 'Sanctified Bloodmage Shoulderpads'), +(8, 2, 4, 0, 290, 51283, 'Phase 5', 'Mage', 'Frost', 'Chest', 'Both', 'Sanctified Bloodmage Robe'), +(8, 2, 5, 0, 290, 50613, 'Phase 5', 'Mage', 'Frost', 'Waist', 'Both', 'Crushing Coldwraith Belt'), +(8, 2, 6, 0, 290, 50694, 'Phase 5', 'Mage', 'Frost', 'Legs', 'Both', 'Plaguebringer''s Stained Pants'), +(8, 2, 7, 0, 290, 50699, 'Phase 5', 'Mage', 'Frost', 'Feet', 'Both', 'Plague Scientist''s Boots'), +(8, 2, 8, 0, 290, 54582, 'Phase 5', 'Mage', 'Frost', 'Wrists', 'Both', 'Bracers of Fiery Night'), +(8, 2, 9, 0, 290, 51280, 'Phase 5', 'Mage', 'Frost', 'Hands', 'Both', 'Sanctified Bloodmage Gloves'), +(8, 2, 10, 0, 290, 50614, 'Phase 5', 'Mage', 'Frost', 'Finger1', 'Both', 'Loop of the Endless Labyrinth'), +(8, 2, 11, 0, 290, 50398, 'Phase 5', 'Mage', 'Frost', 'Finger2', 'Both', 'Ashen Band of Endless Destruction'), +(8, 2, 12, 0, 290, 54588, 'Phase 5', 'Mage', 'Frost', 'Trinket1', 'Both', 'Charred Twilight Scale'), +(8, 2, 13, 0, 290, 50348, 'Phase 5', 'Mage', 'Frost', 'Trinket2', 'Both', 'Dislodged Foreign Object'), +(8, 2, 14, 0, 290, 54583, 'Phase 5', 'Mage', 'Frost', 'Back', 'Both', 'Cloak of Burning Dusk'), +(8, 2, 15, 0, 290, 50732, 'Phase 5', 'Mage', 'Frost', 'MainHand', 'Both', 'Bloodsurge, Kel''Thuzad''s Blade of Agony'), +(8, 2, 16, 0, 290, 50719, 'Phase 5', 'Mage', 'Frost', 'OffHand', 'Both', 'Shadow Silk Spindle'), +(8, 2, 17, 0, 290, 50684, 'Phase 5', 'Mage', 'Frost', 'Ranged', 'Both', 'Corpse-Impaling Spike'); + + +-- ============================================================ +-- Warlock (9) +-- ============================================================ +-- Affliction (tab 0) +-- ilvl 66 (Phase 1 (Pre-Raid)) +INSERT INTO `playerbots_bis_gear` VALUES +(9, 0, 0, 1, 66, 10504, 'Phase 1 (Pre-Raid)', 'Warlock', 'Affliction', 'Head', 'Alliance', 'Green Lens'), +(9, 0, 0, 2, 66, 10504, 'Phase 1 (Pre-Raid)', 'Warlock', 'Affliction', 'Head', 'Horde', 'Green Lens'), +(9, 0, 1, 1, 66, 18691, 'Phase 1 (Pre-Raid)', 'Warlock', 'Affliction', 'Neck', 'Alliance', 'Dark Advisor''s Pendant'), +(9, 0, 1, 2, 66, 18691, 'Phase 1 (Pre-Raid)', 'Warlock', 'Affliction', 'Neck', 'Horde', 'Dark Advisor''s Pendant'), +(9, 0, 2, 1, 66, 14112, 'Phase 1 (Pre-Raid)', 'Warlock', 'Affliction', 'Shoulders', 'Alliance', 'Felcloth Shoulders'), +(9, 0, 2, 2, 66, 14112, 'Phase 1 (Pre-Raid)', 'Warlock', 'Affliction', 'Shoulders', 'Horde', 'Felcloth Shoulders'), +(9, 0, 4, 1, 66, 14153, 'Phase 1 (Pre-Raid)', 'Warlock', 'Affliction', 'Chest', 'Alliance', 'Robe of the Void'), +(9, 0, 4, 2, 66, 14153, 'Phase 1 (Pre-Raid)', 'Warlock', 'Affliction', 'Chest', 'Horde', 'Robe of the Void'), +(9, 0, 5, 1, 66, 11662, 'Phase 1 (Pre-Raid)', 'Warlock', 'Affliction', 'Waist', 'Alliance', 'Ban''thok Sash'), +(9, 0, 5, 2, 66, 11662, 'Phase 1 (Pre-Raid)', 'Warlock', 'Affliction', 'Waist', 'Horde', 'Ban''thok Sash'), +(9, 0, 6, 1, 66, 13170, 'Phase 1 (Pre-Raid)', 'Warlock', 'Affliction', 'Legs', 'Alliance', 'Skyshroud Leggings'), +(9, 0, 6, 2, 66, 13170, 'Phase 1 (Pre-Raid)', 'Warlock', 'Affliction', 'Legs', 'Horde', 'Skyshroud Leggings'), +(9, 0, 7, 1, 66, 18735, 'Phase 1 (Pre-Raid)', 'Warlock', 'Affliction', 'Feet', 'Alliance', 'Maleki''s Footwraps'), +(9, 0, 7, 2, 66, 18735, 'Phase 1 (Pre-Raid)', 'Warlock', 'Affliction', 'Feet', 'Horde', 'Maleki''s Footwraps'), +(9, 0, 8, 1, 66, 11766, 'Phase 1 (Pre-Raid)', 'Warlock', 'Affliction', 'Wrists', 'Alliance', 'Flameweave Cuffs'), +(9, 0, 8, 2, 66, 11766, 'Phase 1 (Pre-Raid)', 'Warlock', 'Affliction', 'Wrists', 'Horde', 'Flameweave Cuffs'), +(9, 0, 9, 1, 66, 13253, 'Phase 1 (Pre-Raid)', 'Warlock', 'Affliction', 'Hands', 'Alliance', 'Hands of Power'), +(9, 0, 9, 2, 66, 13253, 'Phase 1 (Pre-Raid)', 'Warlock', 'Affliction', 'Hands', 'Horde', 'Hands of Power'), +(9, 0, 10, 1, 66, 12543, 'Phase 1 (Pre-Raid)', 'Warlock', 'Affliction', 'Finger1', 'Alliance', 'Songstone of Ironforge'), +(9, 0, 10, 2, 66, 12545, 'Phase 1 (Pre-Raid)', 'Warlock', 'Affliction', 'Finger1', 'Horde', 'Eye of Orgrimmar'), +(9, 0, 11, 1, 66, 13001, 'Phase 1 (Pre-Raid)', 'Warlock', 'Affliction', 'Finger2', 'Alliance', 'Maiden''s Circle'), +(9, 0, 11, 2, 66, 13001, 'Phase 1 (Pre-Raid)', 'Warlock', 'Affliction', 'Finger2', 'Horde', 'Maiden''s Circle'), +(9, 0, 12, 1, 66, 12930, 'Phase 1 (Pre-Raid)', 'Warlock', 'Affliction', 'Trinket1', 'Alliance', 'Briarwood Reed'), +(9, 0, 12, 2, 66, 12930, 'Phase 1 (Pre-Raid)', 'Warlock', 'Affliction', 'Trinket1', 'Horde', 'Briarwood Reed'), +(9, 0, 13, 1, 66, 13968, 'Phase 1 (Pre-Raid)', 'Warlock', 'Affliction', 'Trinket2', 'Alliance', 'Eye of the Beast'), +(9, 0, 13, 2, 66, 13968, 'Phase 1 (Pre-Raid)', 'Warlock', 'Affliction', 'Trinket2', 'Horde', 'Eye of the Beast'), +(9, 0, 14, 1, 66, 13386, 'Phase 1 (Pre-Raid)', 'Warlock', 'Affliction', 'Back', 'Alliance', 'Archivist Cape'), +(9, 0, 14, 2, 66, 13386, 'Phase 1 (Pre-Raid)', 'Warlock', 'Affliction', 'Back', 'Horde', 'Archivist Cape'), +(9, 0, 15, 1, 66, 13964, 'Phase 1 (Pre-Raid)', 'Warlock', 'Affliction', 'MainHand', 'Alliance', 'Witchblade'), +(9, 0, 15, 2, 66, 13964, 'Phase 1 (Pre-Raid)', 'Warlock', 'Affliction', 'MainHand', 'Horde', 'Witchblade'), +(9, 0, 16, 1, 66, 10796, 'Phase 1 (Pre-Raid)', 'Warlock', 'Affliction', 'OffHand', 'Alliance', 'Drakestone'), +(9, 0, 16, 2, 66, 10796, 'Phase 1 (Pre-Raid)', 'Warlock', 'Affliction', 'OffHand', 'Horde', 'Drakestone'), +(9, 0, 17, 1, 66, 13396, 'Phase 1 (Pre-Raid)', 'Warlock', 'Affliction', 'Ranged', 'Alliance', 'Skul''s Ghastly Touch'), +(9, 0, 17, 2, 66, 13396, 'Phase 1 (Pre-Raid)', 'Warlock', 'Affliction', 'Ranged', 'Horde', 'Skul''s Ghastly Touch'); + +-- ilvl 76 (Phase 2 (Pre-Raid)) +INSERT INTO `playerbots_bis_gear` VALUES +(9, 0, 0, 1, 76, 23310, 'Phase 2 (Pre-Raid)', 'Warlock', 'Affliction', 'Head', 'Alliance', 'Lieutenant Commander''s Dreadweave Cowl'), +(9, 0, 0, 2, 76, 23310, 'Phase 2 (Pre-Raid)', 'Warlock', 'Affliction', 'Head', 'Horde', 'Lieutenant Commander''s Dreadweave Cowl'), +(9, 0, 1, 1, 76, 18691, 'Phase 2 (Pre-Raid)', 'Warlock', 'Affliction', 'Neck', 'Alliance', 'Dark Advisor''s Pendant'), +(9, 0, 1, 2, 76, 18691, 'Phase 2 (Pre-Raid)', 'Warlock', 'Affliction', 'Neck', 'Horde', 'Dark Advisor''s Pendant'), +(9, 0, 2, 1, 76, 23311, 'Phase 2 (Pre-Raid)', 'Warlock', 'Affliction', 'Shoulders', 'Alliance', 'Lieutenant Commander''s Dreadweave Spaulders'), +(9, 0, 2, 2, 76, 23311, 'Phase 2 (Pre-Raid)', 'Warlock', 'Affliction', 'Shoulders', 'Horde', 'Lieutenant Commander''s Dreadweave Spaulders'), +(9, 0, 4, 1, 76, 14153, 'Phase 2 (Pre-Raid)', 'Warlock', 'Affliction', 'Chest', 'Alliance', 'Robe of the Void'), +(9, 0, 4, 2, 76, 14153, 'Phase 2 (Pre-Raid)', 'Warlock', 'Affliction', 'Chest', 'Horde', 'Robe of the Void'), +(9, 0, 5, 1, 76, 11662, 'Phase 2 (Pre-Raid)', 'Warlock', 'Affliction', 'Waist', 'Alliance', 'Ban''thok Sash'), +(9, 0, 5, 2, 76, 11662, 'Phase 2 (Pre-Raid)', 'Warlock', 'Affliction', 'Waist', 'Horde', 'Ban''thok Sash'), +(9, 0, 6, 1, 76, 13170, 'Phase 2 (Pre-Raid)', 'Warlock', 'Affliction', 'Legs', 'Alliance', 'Skyshroud Leggings'), +(9, 0, 6, 2, 76, 13170, 'Phase 2 (Pre-Raid)', 'Warlock', 'Affliction', 'Legs', 'Horde', 'Skyshroud Leggings'), +(9, 0, 7, 1, 76, 18735, 'Phase 2 (Pre-Raid)', 'Warlock', 'Affliction', 'Feet', 'Alliance', 'Maleki''s Footwraps'), +(9, 0, 7, 2, 76, 18735, 'Phase 2 (Pre-Raid)', 'Warlock', 'Affliction', 'Feet', 'Horde', 'Maleki''s Footwraps'), +(9, 0, 8, 1, 76, 11766, 'Phase 2 (Pre-Raid)', 'Warlock', 'Affliction', 'Wrists', 'Alliance', 'Flameweave Cuffs'), +(9, 0, 8, 2, 76, 11766, 'Phase 2 (Pre-Raid)', 'Warlock', 'Affliction', 'Wrists', 'Horde', 'Flameweave Cuffs'), +(9, 0, 9, 1, 76, 18407, 'Phase 2 (Pre-Raid)', 'Warlock', 'Affliction', 'Hands', 'Alliance', 'Felcloth Gloves'), +(9, 0, 9, 2, 76, 18407, 'Phase 2 (Pre-Raid)', 'Warlock', 'Affliction', 'Hands', 'Horde', 'Felcloth Gloves'), +(9, 0, 10, 1, 76, 12543, 'Phase 2 (Pre-Raid)', 'Warlock', 'Affliction', 'Finger1', 'Alliance', 'Songstone of Ironforge'), +(9, 0, 10, 2, 76, 12545, 'Phase 2 (Pre-Raid)', 'Warlock', 'Affliction', 'Finger1', 'Horde', 'Eye of Orgrimmar'), +(9, 0, 11, 1, 76, 13001, 'Phase 2 (Pre-Raid)', 'Warlock', 'Affliction', 'Finger2', 'Alliance', 'Maiden''s Circle'), +(9, 0, 11, 2, 76, 13001, 'Phase 2 (Pre-Raid)', 'Warlock', 'Affliction', 'Finger2', 'Horde', 'Maiden''s Circle'), +(9, 0, 12, 1, 76, 12930, 'Phase 2 (Pre-Raid)', 'Warlock', 'Affliction', 'Trinket1', 'Alliance', 'Briarwood Reed'), +(9, 0, 12, 2, 76, 12930, 'Phase 2 (Pre-Raid)', 'Warlock', 'Affliction', 'Trinket1', 'Horde', 'Briarwood Reed'), +(9, 0, 13, 1, 76, 18467, 'Phase 2 (Pre-Raid)', 'Warlock', 'Affliction', 'Trinket2', 'Alliance', 'Royal Seal of Eldre''Thalas'), +(9, 0, 13, 2, 76, 18467, 'Phase 2 (Pre-Raid)', 'Warlock', 'Affliction', 'Trinket2', 'Horde', 'Royal Seal of Eldre''Thalas'), +(9, 0, 14, 1, 76, 13386, 'Phase 2 (Pre-Raid)', 'Warlock', 'Affliction', 'Back', 'Alliance', 'Archivist Cape'), +(9, 0, 14, 2, 76, 13386, 'Phase 2 (Pre-Raid)', 'Warlock', 'Affliction', 'Back', 'Horde', 'Archivist Cape'), +(9, 0, 15, 1, 76, 18372, 'Phase 2 (Pre-Raid)', 'Warlock', 'Affliction', 'MainHand', 'Alliance', 'Blade of the New Moon'), +(9, 0, 15, 2, 76, 18372, 'Phase 2 (Pre-Raid)', 'Warlock', 'Affliction', 'MainHand', 'Horde', 'Blade of the New Moon'), +(9, 0, 16, 1, 76, 10796, 'Phase 2 (Pre-Raid)', 'Warlock', 'Affliction', 'OffHand', 'Alliance', 'Drakestone'), +(9, 0, 16, 2, 76, 10796, 'Phase 2 (Pre-Raid)', 'Warlock', 'Affliction', 'OffHand', 'Horde', 'Drakestone'), +(9, 0, 17, 1, 76, 13396, 'Phase 2 (Pre-Raid)', 'Warlock', 'Affliction', 'Ranged', 'Alliance', 'Skul''s Ghastly Touch'), +(9, 0, 17, 2, 76, 13396, 'Phase 2 (Pre-Raid)', 'Warlock', 'Affliction', 'Ranged', 'Horde', 'Skul''s Ghastly Touch'); + +-- ilvl 78 (Phase 2) +INSERT INTO `playerbots_bis_gear` VALUES +(9, 0, 0, 0, 78, 23310, 'Phase 2', 'Warlock', 'Affliction', 'Head', 'Both', 'Lieutenant Commander''s Dreadweave Cowl'), +(9, 0, 1, 0, 78, 18814, 'Phase 2', 'Warlock', 'Affliction', 'Neck', 'Both', 'Choker of the Fire Lord'), +(9, 0, 2, 0, 78, 23311, 'Phase 2', 'Warlock', 'Affliction', 'Shoulders', 'Both', 'Lieutenant Commander''s Dreadweave Spaulders'), +(9, 0, 4, 0, 78, 19145, 'Phase 2', 'Warlock', 'Affliction', 'Chest', 'Both', 'Robe of Volatile Power'), +(9, 0, 5, 0, 78, 18809, 'Phase 2', 'Warlock', 'Affliction', 'Waist', 'Both', 'Sash of Whispered Secrets'), +(9, 0, 6, 0, 78, 19133, 'Phase 2', 'Warlock', 'Affliction', 'Legs', 'Both', 'Fel Infused Leggings'), +(9, 0, 7, 0, 78, 19131, 'Phase 2', 'Warlock', 'Affliction', 'Feet', 'Both', 'Snowblind Shoes'), +(9, 0, 8, 0, 78, 11766, 'Phase 2', 'Warlock', 'Affliction', 'Wrists', 'Both', 'Flameweave Cuffs'), +(9, 0, 9, 0, 78, 18407, 'Phase 2', 'Warlock', 'Affliction', 'Hands', 'Both', 'Felcloth Gloves'), +(9, 0, 10, 0, 78, 19147, 'Phase 2', 'Warlock', 'Affliction', 'Finger1', 'Both', 'Ring of Spell Power'), +(9, 0, 11, 0, 78, 19147, 'Phase 2', 'Warlock', 'Affliction', 'Finger2', 'Both', 'Ring of Spell Power'), +(9, 0, 12, 0, 78, 12930, 'Phase 2', 'Warlock', 'Affliction', 'Trinket1', 'Both', 'Briarwood Reed'), +(9, 0, 13, 0, 78, 18820, 'Phase 2', 'Warlock', 'Affliction', 'Trinket2', 'Both', 'Talisman of Ephemeral Power'), +(9, 0, 14, 0, 78, 13386, 'Phase 2', 'Warlock', 'Affliction', 'Back', 'Both', 'Archivist Cape'), +(9, 0, 15, 0, 78, 17103, 'Phase 2', 'Warlock', 'Affliction', 'MainHand', 'Both', 'Azuresong Mageblade'), +(9, 0, 16, 0, 78, 10796, 'Phase 2', 'Warlock', 'Affliction', 'OffHand', 'Both', 'Drakestone'), +(9, 0, 17, 0, 78, 13396, 'Phase 2', 'Warlock', 'Affliction', 'Ranged', 'Both', 'Skul''s Ghastly Touch'); + +-- ilvl 83 (Phase 3) +INSERT INTO `playerbots_bis_gear` VALUES +(9, 0, 0, 0, 83, 19375, 'Phase 3', 'Warlock', 'Affliction', 'Head', 'Both', 'Mish''undare, Circlet of the Mind Flayer'), +(9, 0, 1, 0, 83, 18814, 'Phase 3', 'Warlock', 'Affliction', 'Neck', 'Both', 'Choker of the Fire Lord'), +(9, 0, 2, 0, 83, 19370, 'Phase 3', 'Warlock', 'Affliction', 'Shoulders', 'Both', 'Mantle of the Blackwing Cabal'), +(9, 0, 4, 0, 83, 19145, 'Phase 3', 'Warlock', 'Affliction', 'Chest', 'Both', 'Robe of Volatile Power'), +(9, 0, 5, 0, 83, 18809, 'Phase 3', 'Warlock', 'Affliction', 'Waist', 'Both', 'Sash of Whispered Secrets'), +(9, 0, 6, 0, 83, 19133, 'Phase 3', 'Warlock', 'Affliction', 'Legs', 'Both', 'Fel Infused Leggings'), +(9, 0, 7, 0, 83, 19131, 'Phase 3', 'Warlock', 'Affliction', 'Feet', 'Both', 'Snowblind Shoes'), +(9, 0, 8, 0, 83, 19374, 'Phase 3', 'Warlock', 'Affliction', 'Wrists', 'Both', 'Bracers of Arcane Accuracy'), +(9, 0, 9, 0, 83, 19407, 'Phase 3', 'Warlock', 'Affliction', 'Hands', 'Both', 'Ebony Flame Gloves'), +(9, 0, 11, 0, 83, 19434, 'Phase 3', 'Warlock', 'Affliction', 'Finger2', 'Both', 'Band of Dark Dominion'), +(9, 0, 12, 0, 83, 19379, 'Phase 3', 'Warlock', 'Affliction', 'Trinket1', 'Both', 'Neltharion''s Tear'), +(9, 0, 13, 0, 83, 18820, 'Phase 3', 'Warlock', 'Affliction', 'Trinket2', 'Both', 'Talisman of Ephemeral Power'), +(9, 0, 14, 0, 83, 19378, 'Phase 3', 'Warlock', 'Affliction', 'Back', 'Both', 'Cloak of the Brood Lord'), +(9, 0, 15, 0, 83, 19356, 'Phase 3', 'Warlock', 'Affliction', 'MainHand', 'Both', 'Staff of the Shadow Flame'), +(9, 0, 17, 0, 83, 13396, 'Phase 3', 'Warlock', 'Affliction', 'Ranged', 'Both', 'Skul''s Ghastly Touch'); + +-- ilvl 88 (Phase 5) +INSERT INTO `playerbots_bis_gear` VALUES +(9, 0, 0, 0, 88, 21337, 'Phase 5', 'Warlock', 'Affliction', 'Head', 'Both', 'Doomcaller''s Circlet'), +(9, 0, 1, 0, 88, 21608, 'Phase 5', 'Warlock', 'Affliction', 'Neck', 'Both', 'Amulet of Vek''nilash'), +(9, 0, 2, 0, 88, 21335, 'Phase 5', 'Warlock', 'Affliction', 'Shoulders', 'Both', 'Doomcaller''s Mantle'), +(9, 0, 4, 0, 88, 19682, 'Phase 5', 'Warlock', 'Affliction', 'Chest', 'Both', 'Bloodvine Vest'), +(9, 0, 5, 0, 88, 22730, 'Phase 5', 'Warlock', 'Affliction', 'Waist', 'Both', 'Eyestalk Waist Cord'), +(9, 0, 6, 0, 88, 19683, 'Phase 5', 'Warlock', 'Affliction', 'Legs', 'Both', 'Bloodvine Leggings'), +(9, 0, 7, 0, 88, 19684, 'Phase 5', 'Warlock', 'Affliction', 'Feet', 'Both', 'Bloodvine Boots'), +(9, 0, 8, 0, 88, 21186, 'Phase 5', 'Warlock', 'Affliction', 'Wrists', 'Both', 'Rockfury Bracers'), +(9, 0, 9, 0, 88, 21585, 'Phase 5', 'Warlock', 'Affliction', 'Hands', 'Both', 'Dark Storm Gauntlets'), +(9, 0, 10, 0, 88, 21417, 'Phase 5', 'Warlock', 'Affliction', 'Finger1', 'Both', 'Ring of Unspoken Names'), +(9, 0, 11, 0, 88, 21709, 'Phase 5', 'Warlock', 'Affliction', 'Finger2', 'Both', 'Ring of the Fallen God'), +(9, 0, 12, 0, 88, 19379, 'Phase 5', 'Warlock', 'Affliction', 'Trinket1', 'Both', 'Neltharion''s Tear'), +(9, 0, 13, 0, 88, 18820, 'Phase 5', 'Warlock', 'Affliction', 'Trinket2', 'Both', 'Talisman of Ephemeral Power'), +(9, 0, 14, 0, 88, 22731, 'Phase 5', 'Warlock', 'Affliction', 'Back', 'Both', 'Cloak of the Devoured'), +(9, 0, 15, 0, 88, 21622, 'Phase 5', 'Warlock', 'Affliction', 'MainHand', 'Both', 'Sharpened Silithid Femur'), +(9, 0, 16, 0, 88, 21597, 'Phase 5', 'Warlock', 'Affliction', 'OffHand', 'Both', 'Royal Scepter of Vek''lor'), +(9, 0, 17, 0, 88, 21603, 'Phase 5', 'Warlock', 'Affliction', 'Ranged', 'Both', 'Wand of Qiraji Nobility'); + +-- ilvl 92 (Phase 6) +INSERT INTO `playerbots_bis_gear` VALUES +(9, 0, 0, 0, 92, 22506, 'Phase 6', 'Warlock', 'Affliction', 'Head', 'Both', 'Plagueheart Circlet'), +(9, 0, 1, 0, 92, 21608, 'Phase 6', 'Warlock', 'Affliction', 'Neck', 'Both', 'Amulet of Vek''nilash'), +(9, 0, 2, 0, 92, 22507, 'Phase 6', 'Warlock', 'Affliction', 'Shoulders', 'Both', 'Plagueheart Shoulderpads'), +(9, 0, 4, 0, 92, 22504, 'Phase 6', 'Warlock', 'Affliction', 'Chest', 'Both', 'Plagueheart Robe'), +(9, 0, 5, 0, 92, 22730, 'Phase 6', 'Warlock', 'Affliction', 'Waist', 'Both', 'Eyestalk Waist Cord'), +(9, 0, 6, 0, 92, 23070, 'Phase 6', 'Warlock', 'Affliction', 'Legs', 'Both', 'Leggings of Polarity'), +(9, 0, 7, 0, 92, 22508, 'Phase 6', 'Warlock', 'Affliction', 'Feet', 'Both', 'Plagueheart Sandals'), +(9, 0, 8, 0, 92, 21186, 'Phase 6', 'Warlock', 'Affliction', 'Wrists', 'Both', 'Rockfury Bracers'), +(9, 0, 9, 0, 92, 21585, 'Phase 6', 'Warlock', 'Affliction', 'Hands', 'Both', 'Dark Storm Gauntlets'), +(9, 0, 10, 0, 92, 23031, 'Phase 6', 'Warlock', 'Affliction', 'Finger1', 'Both', 'Band of the Inevitable'), +(9, 0, 11, 0, 92, 21709, 'Phase 6', 'Warlock', 'Affliction', 'Finger2', 'Both', 'Ring of the Fallen God'), +(9, 0, 12, 0, 92, 19379, 'Phase 6', 'Warlock', 'Affliction', 'Trinket1', 'Both', 'Neltharion''s Tear'), +(9, 0, 13, 0, 92, 23046, 'Phase 6', 'Warlock', 'Affliction', 'Trinket2', 'Both', 'The Restrained Essence of Sapphiron'), +(9, 0, 14, 0, 92, 23050, 'Phase 6', 'Warlock', 'Affliction', 'Back', 'Both', 'Cloak of the Necropolis'), +(9, 0, 15, 0, 92, 22807, 'Phase 6', 'Warlock', 'Affliction', 'MainHand', 'Both', 'Wraith Blade'), +(9, 0, 16, 0, 92, 23049, 'Phase 6', 'Warlock', 'Affliction', 'OffHand', 'Both', 'Sapphiron''s Left Eye'), +(9, 0, 17, 0, 92, 22820, 'Phase 6', 'Warlock', 'Affliction', 'Ranged', 'Both', 'Wand of Fates'); + +-- ilvl 120 (Pre-Raid) +INSERT INTO `playerbots_bis_gear` VALUES +(9, 0, 0, 0, 120, 24266, 'Pre-Raid', 'Warlock', 'Affliction', 'Head', 'Both', 'Spellstrike Hood'), +(9, 0, 1, 0, 120, 28134, 'Pre-Raid', 'Warlock', 'Affliction', 'Neck', 'Both', 'Brooch of Heightened Potential'), +(9, 0, 2, 0, 120, 21869, 'Pre-Raid', 'Warlock', 'Affliction', 'Shoulders', 'Both', 'Frozen Shadoweave Shoulders'), +(9, 0, 4, 0, 120, 21871, 'Pre-Raid', 'Warlock', 'Affliction', 'Chest', 'Both', 'Frozen Shadoweave Robe'), +(9, 0, 5, 0, 120, 24256, 'Pre-Raid', 'Warlock', 'Affliction', 'Waist', 'Both', 'Girdle of Ruination'), +(9, 0, 6, 0, 120, 24262, 'Pre-Raid', 'Warlock', 'Affliction', 'Legs', 'Both', 'Spellstrike Pants'), +(9, 0, 7, 0, 120, 21870, 'Pre-Raid', 'Warlock', 'Affliction', 'Feet', 'Both', 'Frozen Shadoweave Boots'), +(9, 0, 8, 0, 120, 24250, 'Pre-Raid', 'Warlock', 'Affliction', 'Wrists', 'Both', 'Bracers of Havok'), +(9, 0, 9, 0, 120, 24450, 'Pre-Raid', 'Warlock', 'Affliction', 'Hands', 'Both', 'Manaspark Gloves'), +(9, 0, 10, 0, 120, 29172, 'Pre-Raid', 'Warlock', 'Affliction', 'Finger1', 'Both', 'Ashyen''s Gift'), +(9, 0, 11, 0, 120, 28227, 'Pre-Raid', 'Warlock', 'Affliction', 'Finger2', 'Both', 'Sparking Arcanite Ring'), +(9, 0, 12, 0, 120, 29370, 'Pre-Raid', 'Warlock', 'Affliction', 'Trinket1', 'Both', 'Icon of the Silver Crescent'), +(9, 0, 13, 0, 120, 29132, 'Pre-Raid', 'Warlock', 'Affliction', 'Trinket2', 'Both', 'Scryer''s Bloodgem'), +(9, 0, 14, 0, 120, 27981, 'Pre-Raid', 'Warlock', 'Affliction', 'Back', 'Both', 'Sethekk Oracle Cloak'), +(9, 0, 15, 0, 120, 23554, 'Pre-Raid', 'Warlock', 'Affliction', 'MainHand', 'Both', 'Eternium Runed Blade'), +(9, 0, 16, 0, 120, 29273, 'Pre-Raid', 'Warlock', 'Affliction', 'OffHand', 'Both', 'Khadgar''s Knapsack'), +(9, 0, 17, 0, 120, 29350, 'Pre-Raid', 'Warlock', 'Affliction', 'Ranged', 'Both', 'The Black Stalk'); + +-- ilvl 125 (Phase 1) +INSERT INTO `playerbots_bis_gear` VALUES +(9, 0, 0, 0, 125, 28963, 'Phase 1', 'Warlock', 'Affliction', 'Head', 'Both', 'Voidheart Crown'), +(9, 0, 1, 0, 125, 28530, 'Phase 1', 'Warlock', 'Affliction', 'Neck', 'Both', 'Brooch of Unquenchable Fury'), +(9, 0, 2, 0, 125, 28967, 'Phase 1', 'Warlock', 'Affliction', 'Shoulders', 'Both', 'Voidheart Mantle'), +(9, 0, 4, 0, 125, 28964, 'Phase 1', 'Warlock', 'Affliction', 'Chest', 'Both', 'Voidheart Robe'), +(9, 0, 5, 0, 125, 24256, 'Phase 1', 'Warlock', 'Affliction', 'Waist', 'Both', 'Girdle of Ruination'), +(9, 0, 6, 0, 125, 30734, 'Phase 1', 'Warlock', 'Affliction', 'Legs', 'Both', 'Leggings of the Seventh Circle'), +(9, 0, 7, 0, 125, 21870, 'Phase 1', 'Warlock', 'Affliction', 'Feet', 'Both', 'Frozen Shadoweave Boots'), +(9, 0, 8, 0, 125, 24250, 'Phase 1', 'Warlock', 'Affliction', 'Wrists', 'Both', 'Bracers of Havok'), +(9, 0, 9, 0, 125, 28968, 'Phase 1', 'Warlock', 'Affliction', 'Hands', 'Both', 'Voidheart Gloves'), +(9, 0, 10, 0, 125, 29172, 'Phase 1', 'Warlock', 'Affliction', 'Finger1', 'Both', 'Ashyen''s Gift'), +(9, 0, 11, 0, 125, 28793, 'Phase 1', 'Warlock', 'Affliction', 'Finger2', 'Both', 'Band of Crimson Fury'), +(9, 0, 12, 0, 125, 29370, 'Phase 1', 'Warlock', 'Affliction', 'Trinket1', 'Both', 'Icon of the Silver Crescent'), +(9, 0, 13, 0, 125, 27683, 'Phase 1', 'Warlock', 'Affliction', 'Trinket2', 'Both', 'Quagmirran''s Eye'), +(9, 0, 14, 0, 125, 28766, 'Phase 1', 'Warlock', 'Affliction', 'Back', 'Both', 'Ruby Drape of the Mysticant'), +(9, 0, 15, 0, 125, 30723, 'Phase 1', 'Warlock', 'Affliction', 'MainHand', 'Both', 'Talon of the Tempest'), +(9, 0, 16, 0, 125, 29272, 'Phase 1', 'Warlock', 'Affliction', 'OffHand', 'Both', 'Orb of the Soul-Eater'), +(9, 0, 17, 0, 125, 28783, 'Phase 1', 'Warlock', 'Affliction', 'Ranged', 'Both', 'Eredar Wand of Obliteration'); + +-- ilvl 141 (Phase 2) +INSERT INTO `playerbots_bis_gear` VALUES +(9, 0, 0, 0, 141, 32494, 'Phase 2', 'Warlock', 'Affliction', 'Head', 'Both', 'Destruction Holo-gogs'), +(9, 0, 1, 0, 141, 30015, 'Phase 2', 'Warlock', 'Affliction', 'Neck', 'Both', 'The Sun King''s Talisman'), +(9, 0, 2, 0, 141, 28967, 'Phase 2', 'Warlock', 'Affliction', 'Shoulders', 'Both', 'Voidheart Mantle'), +(9, 0, 4, 0, 141, 30107, 'Phase 2', 'Warlock', 'Affliction', 'Chest', 'Both', 'Vestments of the Sea-Witch'), +(9, 0, 5, 0, 141, 30038, 'Phase 2', 'Warlock', 'Affliction', 'Waist', 'Both', 'Belt of Blasting'), +(9, 0, 6, 0, 141, 30213, 'Phase 2', 'Warlock', 'Affliction', 'Legs', 'Both', 'Leggings of the Corruptor'), +(9, 0, 7, 0, 141, 30037, 'Phase 2', 'Warlock', 'Affliction', 'Feet', 'Both', 'Boots of Blasting'), +(9, 0, 8, 0, 141, 29918, 'Phase 2', 'Warlock', 'Affliction', 'Wrists', 'Both', 'Mindstorm Wristbands'), +(9, 0, 9, 0, 141, 28968, 'Phase 2', 'Warlock', 'Affliction', 'Hands', 'Both', 'Voidheart Gloves'), +(9, 0, 10, 0, 141, 29302, 'Phase 2', 'Warlock', 'Affliction', 'Finger1', 'Both', 'Band of Eternity'), +(9, 0, 11, 0, 141, 30109, 'Phase 2', 'Warlock', 'Affliction', 'Finger2', 'Both', 'Ring of Endless Coils'), +(9, 0, 12, 0, 141, 29370, 'Phase 2', 'Warlock', 'Affliction', 'Trinket1', 'Both', 'Icon of the Silver Crescent'), +(9, 0, 13, 0, 141, 27683, 'Phase 2', 'Warlock', 'Affliction', 'Trinket2', 'Both', 'Quagmirran''s Eye'), +(9, 0, 14, 0, 141, 28766, 'Phase 2', 'Warlock', 'Affliction', 'Back', 'Both', 'Ruby Drape of the Mysticant'), +(9, 0, 15, 0, 141, 30095, 'Phase 2', 'Warlock', 'Affliction', 'MainHand', 'Both', 'Fang of the Leviathan'), +(9, 0, 16, 0, 141, 30049, 'Phase 2', 'Warlock', 'Affliction', 'OffHand', 'Both', 'Fathomstone'), +(9, 0, 17, 0, 141, 29982, 'Phase 2', 'Warlock', 'Affliction', 'Ranged', 'Both', 'Wand of the Forgotten Star'); + +-- ilvl 156 (Phase 3) +INSERT INTO `playerbots_bis_gear` VALUES +(9, 0, 0, 0, 156, 31051, 'Phase 3', 'Warlock', 'Affliction', 'Head', 'Both', 'Hood of the Malefic'), +(9, 0, 1, 0, 156, 30015, 'Phase 3', 'Warlock', 'Affliction', 'Neck', 'Both', 'Sun King''s Talisman'), +(9, 0, 2, 0, 156, 31054, 'Phase 3', 'Warlock', 'Affliction', 'Shoulders', 'Both', 'Mantle of the Malefic'), +(9, 0, 4, 0, 156, 30107, 'Phase 3', 'Warlock', 'Affliction', 'Chest', 'Both', 'Vestments of the Sea-Witch'), +(9, 0, 5, 0, 156, 30888, 'Phase 3', 'Warlock', 'Affliction', 'Waist', 'Both', 'Anetheron''s Noose'), +(9, 0, 6, 0, 156, 31053, 'Phase 3', 'Warlock', 'Affliction', 'Legs', 'Both', 'Leggings of the Malefic'), +(9, 0, 7, 0, 156, 32239, 'Phase 3', 'Warlock', 'Affliction', 'Feet', 'Both', 'Slippers of the Seacaller'), +(9, 0, 8, 0, 156, 32586, 'Phase 3', 'Warlock', 'Affliction', 'Wrists', 'Both', 'Bracers of Nimble Thought'), +(9, 0, 9, 0, 156, 31050, 'Phase 3', 'Warlock', 'Affliction', 'Hands', 'Both', 'Gloves of the Malefic'), +(9, 0, 10, 0, 156, 32527, 'Phase 3', 'Warlock', 'Affliction', 'Finger1', 'Both', 'Ring of Ancient Knowledge'), +(9, 0, 11, 0, 156, 32247, 'Phase 3', 'Warlock', 'Affliction', 'Finger2', 'Both', 'Ring of Captured Storms'), +(9, 0, 12, 0, 156, 29370, 'Phase 3', 'Warlock', 'Affliction', 'Trinket1', 'Both', 'Icon of the Silver Crescent'), +(9, 0, 13, 0, 156, 32483, 'Phase 3', 'Warlock', 'Affliction', 'Trinket2', 'Both', 'The Skull of Gul''dan'), +(9, 0, 14, 0, 156, 32590, 'Phase 3', 'Warlock', 'Affliction', 'Back', 'Both', 'Nethervoid Cloak'), +(9, 0, 15, 0, 156, 32374, 'Phase 3', 'Warlock', 'Affliction', 'MainHand', 'Both', 'Zhar''doom, Greatstaff of the Devourer'), +(9, 0, 17, 0, 156, 29982, 'Phase 3', 'Warlock', 'Affliction', 'Ranged', 'Both', 'Wand of the Forgotten Star'); + +-- ilvl 164 (Phase 4) +INSERT INTO `playerbots_bis_gear` VALUES +(9, 0, 0, 0, 164, 31051, 'Phase 4', 'Warlock', 'Affliction', 'Head', 'Both', 'Hood of the Malefic'), +(9, 0, 1, 0, 164, 30015, 'Phase 4', 'Warlock', 'Affliction', 'Neck', 'Both', 'Sun King''s Talisman'), +(9, 0, 2, 0, 164, 31054, 'Phase 4', 'Warlock', 'Affliction', 'Shoulders', 'Both', 'Mantle of the Malefic'), +(9, 0, 4, 0, 164, 30107, 'Phase 4', 'Warlock', 'Affliction', 'Chest', 'Both', 'Vestments of the Sea-Witch'), +(9, 0, 5, 0, 164, 30888, 'Phase 4', 'Warlock', 'Affliction', 'Waist', 'Both', 'Anetheron''s Noose'), +(9, 0, 6, 0, 164, 31053, 'Phase 4', 'Warlock', 'Affliction', 'Legs', 'Both', 'Leggings of the Malefic'), +(9, 0, 7, 0, 164, 32239, 'Phase 4', 'Warlock', 'Affliction', 'Feet', 'Both', 'Slippers of the Seacaller'), +(9, 0, 8, 0, 164, 32586, 'Phase 4', 'Warlock', 'Affliction', 'Wrists', 'Both', 'Bracers of Nimble Thought'), +(9, 0, 9, 0, 164, 31050, 'Phase 4', 'Warlock', 'Affliction', 'Hands', 'Both', 'Gloves of the Malefic'), +(9, 0, 10, 0, 164, 33497, 'Phase 4', 'Warlock', 'Affliction', 'Finger1', 'Both', 'Mana Attuned Band'), +(9, 0, 11, 0, 164, 32247, 'Phase 4', 'Warlock', 'Affliction', 'Finger2', 'Both', 'Ring of Captured Storms'), +(9, 0, 12, 0, 164, 33829, 'Phase 4', 'Warlock', 'Affliction', 'Trinket1', 'Both', 'Hex Shrunken Head'), +(9, 0, 13, 0, 164, 32483, 'Phase 4', 'Warlock', 'Affliction', 'Trinket2', 'Both', 'The Skull of Gul''dan'), +(9, 0, 14, 0, 164, 32590, 'Phase 4', 'Warlock', 'Affliction', 'Back', 'Both', 'Nethervoid Cloak'), +(9, 0, 15, 0, 164, 32374, 'Phase 4', 'Warlock', 'Affliction', 'MainHand', 'Both', 'Zhar''doom, Greatstaff of the Devourer'), +(9, 0, 16, 0, 164, 34179, 'Phase 5', 'Warlock', 'Affliction', 'OffHand', 'Both', 'Heart of the Pit'), +(9, 0, 17, 0, 164, 33192, 'Phase 4', 'Warlock', 'Affliction', 'Ranged', 'Both', 'Carved Witch Doctor''s Stick'); + +-- ilvl 200 (Pre-Raid) +INSERT INTO `playerbots_bis_gear` VALUES +(9, 0, 0, 0, 200, 44910, 'Pre-Raid', 'Warlock', 'Affliction', 'Head', 'Both', 'Titan-Forged Hood of Dominance'), +(9, 0, 1, 0, 200, 40680, 'Pre-Raid', 'Warlock', 'Affliction', 'Neck', 'Both', 'Encircling Burnished Gold Chains'), +(9, 0, 2, 0, 200, 34210, 'Pre-Raid', 'Warlock', 'Affliction', 'Shoulders', 'Both', 'Amice of the Convoker'), +(9, 0, 4, 0, 200, 39497, 'Pre-Raid', 'Warlock', 'Affliction', 'Chest', 'Both', 'Heroes'' Plagueheart Robe'), +(9, 0, 5, 0, 200, 40696, 'Pre-Raid', 'Warlock', 'Affliction', 'Waist', 'Both', 'Plush Sash of Guzbah'), +(9, 0, 6, 0, 200, 34181, 'Pre-Raid', 'Warlock', 'Affliction', 'Legs', 'Both', 'Leggings of Calamity'), +(9, 0, 7, 0, 200, 44202, 'Pre-Raid', 'Warlock', 'Affliction', 'Feet', 'Both', 'Sandals of Crimson Fury'), +(9, 0, 8, 0, 200, 37361, 'Pre-Raid', 'Warlock', 'Affliction', 'Wrists', 'Both', 'Cuffs of Winged Levitation'), +(9, 0, 9, 0, 200, 42113, 'Pre-Raid', 'Warlock', 'Affliction', 'Hands', 'Both', 'Spellweave Gloves'), +(9, 0, 10, 0, 200, 43253, 'Pre-Raid', 'Warlock', 'Affliction', 'Finger1', 'Both', 'Ring of Northern Tears'), +(9, 0, 12, 0, 200, 40682, 'Pre-Raid', 'Warlock', 'Affliction', 'Trinket1', 'Both', 'Sundial of the Exiled'), +(9, 0, 14, 0, 200, 41610, 'Pre-Raid', 'Warlock', 'Affliction', 'Back', 'Both', 'Deathchill Cloak'), +(9, 0, 17, 0, 200, 37177, 'Pre-Raid', 'Warlock', 'Affliction', 'Ranged', 'Both', 'Wand of the San''layn'); + +-- ilvl 224 (Phase 1) +INSERT INTO `playerbots_bis_gear` VALUES +(9, 0, 0, 0, 224, 40421, 'Phase 1', 'Warlock', 'Affliction', 'Head', 'Both', 'Valorous Plagueheart Circlet'), +(9, 0, 1, 0, 224, 44661, 'Phase 1', 'Warlock', 'Affliction', 'Neck', 'Both', 'Wyrmrest Necklace of Power'), +(9, 0, 2, 0, 224, 40424, 'Phase 1', 'Warlock', 'Affliction', 'Shoulders', 'Both', 'Valorous Plagueheart Shoulderpads'), +(9, 0, 4, 0, 224, 40423, 'Phase 1', 'Warlock', 'Affliction', 'Chest', 'Both', 'Valorous Plagueheart Robe'), +(9, 0, 5, 0, 224, 40561, 'Phase 1', 'Warlock', 'Affliction', 'Waist', 'Both', 'Leash of Heedless Magic'), +(9, 0, 6, 0, 224, 40560, 'Phase 1', 'Warlock', 'Affliction', 'Legs', 'Both', 'Leggings of the Wanton Spellcaster'), +(9, 0, 7, 0, 224, 40558, 'Phase 1', 'Warlock', 'Affliction', 'Feet', 'Both', 'Arcanic Tramplers'), +(9, 0, 8, 0, 224, 44008, 'Phase 1', 'Warlock', 'Affliction', 'Wrists', 'Both', 'Unsullied Cuffs'), +(9, 0, 9, 0, 224, 40420, 'Phase 1', 'Warlock', 'Affliction', 'Hands', 'Both', 'Valorous Plagueheart Gloves'), +(9, 0, 10, 0, 224, 40399, 'Phase 1', 'Warlock', 'Affliction', 'Finger1', 'Both', 'Signet of Manifested Pain'), +(9, 0, 12, 0, 224, 40432, 'Phase 1', 'Warlock', 'Affliction', 'Trinket1', 'Both', 'Illustration of the Dragon Soul'), +(9, 0, 14, 0, 224, 44005, 'Phase 1', 'Warlock', 'Affliction', 'Back', 'Both', 'Pennant Cloak'), +(9, 0, 15, 0, 224, 40396, 'Phase 1', 'Warlock', 'Affliction', 'MainHand', 'Both', 'The Turning Tide'), +(9, 0, 17, 0, 224, 39712, 'Phase 1', 'Warlock', 'Affliction', 'Ranged', 'Both', 'Gemmed Wand of the Nerubians'); + +-- ilvl 245 (Phase 2) +INSERT INTO `playerbots_bis_gear` VALUES +(9, 0, 0, 0, 245, 45497, 'Phase 2', 'Warlock', 'Affliction', 'Head', 'Both', 'Crown of Luminescence'), +(9, 0, 1, 0, 245, 45133, 'Phase 2', 'Warlock', 'Affliction', 'Neck', 'Both', 'Pendant of Fiery Havoc'), +(9, 0, 2, 0, 245, 46068, 'Phase 2', 'Warlock', 'Affliction', 'Shoulders', 'Both', 'Amice of Inconceivable Horror'), +(9, 0, 4, 0, 245, 46137, 'Phase 2', 'Warlock', 'Affliction', 'Chest', 'Both', 'Conqueror''s Deathbringer Robe'), +(9, 0, 9, 0, 245, 45665, 'Phase 2', 'Warlock', 'Affliction', 'Hands', 'Both', 'Pharos Gloves'), +(9, 0, 10, 0, 245, 45495, 'Phase 2', 'Warlock', 'Affliction', 'Finger1', 'Both', 'Conductive Seal'), +(9, 0, 14, 0, 245, 45618, 'Phase 2', 'Warlock', 'Affliction', 'Back', 'Both', 'Sunglimmer Cloak'), +(9, 0, 15, 0, 245, 45620, 'Phase 2', 'Warlock', 'Affliction', 'MainHand', 'Both', 'Starshard Edge'), +(9, 0, 17, 0, 245, 45294, 'Phase 2', 'Warlock', 'Affliction', 'Ranged', 'Both', 'Petrified Ivy Sprig'); + +-- ilvl 258 (Phase 3) +INSERT INTO `playerbots_bis_gear` VALUES +(9, 0, 10, 0, 258, 45495, 'Phase 3', 'Warlock', 'Affliction', 'Finger1', 'Both', 'Conductive Seal'), +(9, 0, 17, 0, 258, 45294, 'Phase 3', 'Warlock', 'Affliction', 'Ranged', 'Both', 'Petrified Ivy Sprig'); + +-- ilvl 290 (Phase 5) +INSERT INTO `playerbots_bis_gear` VALUES +(9, 0, 0, 0, 290, 51231, 'Phase 5', 'Warlock', 'Affliction', 'Head', 'Both', 'Sanctified Dark Coven Hood'), +(9, 0, 1, 0, 290, 50182, 'Phase 5', 'Warlock', 'Affliction', 'Neck', 'Both', 'Blood Queen''s Crimson Choker'), +(9, 0, 2, 0, 290, 51234, 'Phase 5', 'Warlock', 'Affliction', 'Shoulders', 'Both', 'Sanctified Dark Coven Shoulderpads'), +(9, 0, 4, 0, 290, 51233, 'Phase 5', 'Warlock', 'Affliction', 'Chest', 'Both', 'Sanctified Dark Coven Robe'), +(9, 0, 5, 0, 290, 50613, 'Phase 5', 'Warlock', 'Affliction', 'Waist', 'Both', 'Crushing Coldwraith Belt'), +(9, 0, 6, 0, 290, 50694, 'Phase 5', 'Warlock', 'Affliction', 'Legs', 'Both', 'Plaguebringer''s Stained Pants'), +(9, 0, 7, 0, 290, 50699, 'Phase 5', 'Warlock', 'Affliction', 'Feet', 'Both', 'Plague Scientist''s Boots'), +(9, 0, 8, 0, 290, 54582, 'Phase 5', 'Warlock', 'Affliction', 'Wrists', 'Both', 'Bracers of Fiery Night'), +(9, 0, 9, 0, 290, 51230, 'Phase 5', 'Warlock', 'Affliction', 'Hands', 'Both', 'Sanctified Dark Coven Gloves'), +(9, 0, 10, 0, 290, 50614, 'Phase 5', 'Warlock', 'Affliction', 'Finger1', 'Both', 'Loop of the Endless Labyrinth'), +(9, 0, 11, 0, 290, 50664, 'Phase 5', 'Warlock', 'Affliction', 'Finger2', 'Both', 'Ring of Rapid Ascent'), +(9, 0, 12, 0, 290, 54588, 'Phase 5', 'Warlock', 'Affliction', 'Trinket1', 'Both', 'Charred Twilight Scale'), +(9, 0, 13, 0, 290, 50365, 'Phase 5', 'Warlock', 'Affliction', 'Trinket2', 'Both', 'Phylactery of the Nameless Lich'), +(9, 0, 14, 0, 290, 54583, 'Phase 5', 'Warlock', 'Affliction', 'Back', 'Both', 'Cloak of Burning Dusk'), +(9, 0, 15, 0, 290, 50732, 'Phase 5', 'Warlock', 'Affliction', 'MainHand', 'Both', 'Bloodsurge, Kel''Thuzad''s Blade of Agony'), +(9, 0, 16, 0, 290, 50719, 'Phase 5', 'Warlock', 'Affliction', 'OffHand', 'Both', 'Shadow Silk Spindle'), +(9, 0, 17, 0, 290, 50684, 'Phase 5', 'Warlock', 'Affliction', 'Ranged', 'Both', 'Corpse-Impaling Spike'); + +-- Demonology (tab 1) +-- ilvl 66 (Phase 1 (Pre-Raid)) +INSERT INTO `playerbots_bis_gear` VALUES +(9, 1, 0, 1, 66, 10504, 'Phase 1 (Pre-Raid)', 'Warlock', 'Demonology', 'Head', 'Alliance', 'Green Lens'), +(9, 1, 0, 2, 66, 10504, 'Phase 1 (Pre-Raid)', 'Warlock', 'Demonology', 'Head', 'Horde', 'Green Lens'), +(9, 1, 1, 1, 66, 18691, 'Phase 1 (Pre-Raid)', 'Warlock', 'Demonology', 'Neck', 'Alliance', 'Dark Advisor''s Pendant'), +(9, 1, 1, 2, 66, 18691, 'Phase 1 (Pre-Raid)', 'Warlock', 'Demonology', 'Neck', 'Horde', 'Dark Advisor''s Pendant'), +(9, 1, 2, 1, 66, 14112, 'Phase 1 (Pre-Raid)', 'Warlock', 'Demonology', 'Shoulders', 'Alliance', 'Felcloth Shoulders'), +(9, 1, 2, 2, 66, 14112, 'Phase 1 (Pre-Raid)', 'Warlock', 'Demonology', 'Shoulders', 'Horde', 'Felcloth Shoulders'), +(9, 1, 4, 1, 66, 14153, 'Phase 1 (Pre-Raid)', 'Warlock', 'Demonology', 'Chest', 'Alliance', 'Robe of the Void'), +(9, 1, 4, 2, 66, 14153, 'Phase 1 (Pre-Raid)', 'Warlock', 'Demonology', 'Chest', 'Horde', 'Robe of the Void'), +(9, 1, 5, 1, 66, 11662, 'Phase 1 (Pre-Raid)', 'Warlock', 'Demonology', 'Waist', 'Alliance', 'Ban''thok Sash'), +(9, 1, 5, 2, 66, 11662, 'Phase 1 (Pre-Raid)', 'Warlock', 'Demonology', 'Waist', 'Horde', 'Ban''thok Sash'), +(9, 1, 6, 1, 66, 13170, 'Phase 1 (Pre-Raid)', 'Warlock', 'Demonology', 'Legs', 'Alliance', 'Skyshroud Leggings'), +(9, 1, 6, 2, 66, 13170, 'Phase 1 (Pre-Raid)', 'Warlock', 'Demonology', 'Legs', 'Horde', 'Skyshroud Leggings'), +(9, 1, 7, 1, 66, 18735, 'Phase 1 (Pre-Raid)', 'Warlock', 'Demonology', 'Feet', 'Alliance', 'Maleki''s Footwraps'), +(9, 1, 7, 2, 66, 18735, 'Phase 1 (Pre-Raid)', 'Warlock', 'Demonology', 'Feet', 'Horde', 'Maleki''s Footwraps'), +(9, 1, 8, 1, 66, 11766, 'Phase 1 (Pre-Raid)', 'Warlock', 'Demonology', 'Wrists', 'Alliance', 'Flameweave Cuffs'), +(9, 1, 8, 2, 66, 11766, 'Phase 1 (Pre-Raid)', 'Warlock', 'Demonology', 'Wrists', 'Horde', 'Flameweave Cuffs'), +(9, 1, 9, 1, 66, 13253, 'Phase 1 (Pre-Raid)', 'Warlock', 'Demonology', 'Hands', 'Alliance', 'Hands of Power'), +(9, 1, 9, 2, 66, 13253, 'Phase 1 (Pre-Raid)', 'Warlock', 'Demonology', 'Hands', 'Horde', 'Hands of Power'), +(9, 1, 10, 1, 66, 12543, 'Phase 1 (Pre-Raid)', 'Warlock', 'Demonology', 'Finger1', 'Alliance', 'Songstone of Ironforge'), +(9, 1, 10, 2, 66, 12545, 'Phase 1 (Pre-Raid)', 'Warlock', 'Demonology', 'Finger1', 'Horde', 'Eye of Orgrimmar'), +(9, 1, 11, 1, 66, 13001, 'Phase 1 (Pre-Raid)', 'Warlock', 'Demonology', 'Finger2', 'Alliance', 'Maiden''s Circle'), +(9, 1, 11, 2, 66, 13001, 'Phase 1 (Pre-Raid)', 'Warlock', 'Demonology', 'Finger2', 'Horde', 'Maiden''s Circle'), +(9, 1, 12, 1, 66, 12930, 'Phase 1 (Pre-Raid)', 'Warlock', 'Demonology', 'Trinket1', 'Alliance', 'Briarwood Reed'), +(9, 1, 12, 2, 66, 12930, 'Phase 1 (Pre-Raid)', 'Warlock', 'Demonology', 'Trinket1', 'Horde', 'Briarwood Reed'), +(9, 1, 13, 1, 66, 13968, 'Phase 1 (Pre-Raid)', 'Warlock', 'Demonology', 'Trinket2', 'Alliance', 'Eye of the Beast'), +(9, 1, 13, 2, 66, 13968, 'Phase 1 (Pre-Raid)', 'Warlock', 'Demonology', 'Trinket2', 'Horde', 'Eye of the Beast'), +(9, 1, 14, 1, 66, 13386, 'Phase 1 (Pre-Raid)', 'Warlock', 'Demonology', 'Back', 'Alliance', 'Archivist Cape'), +(9, 1, 14, 2, 66, 13386, 'Phase 1 (Pre-Raid)', 'Warlock', 'Demonology', 'Back', 'Horde', 'Archivist Cape'), +(9, 1, 15, 1, 66, 13964, 'Phase 1 (Pre-Raid)', 'Warlock', 'Demonology', 'MainHand', 'Alliance', 'Witchblade'), +(9, 1, 15, 2, 66, 13964, 'Phase 1 (Pre-Raid)', 'Warlock', 'Demonology', 'MainHand', 'Horde', 'Witchblade'), +(9, 1, 16, 1, 66, 10796, 'Phase 1 (Pre-Raid)', 'Warlock', 'Demonology', 'OffHand', 'Alliance', 'Drakestone'), +(9, 1, 16, 2, 66, 10796, 'Phase 1 (Pre-Raid)', 'Warlock', 'Demonology', 'OffHand', 'Horde', 'Drakestone'), +(9, 1, 17, 1, 66, 13396, 'Phase 1 (Pre-Raid)', 'Warlock', 'Demonology', 'Ranged', 'Alliance', 'Skul''s Ghastly Touch'), +(9, 1, 17, 2, 66, 13396, 'Phase 1 (Pre-Raid)', 'Warlock', 'Demonology', 'Ranged', 'Horde', 'Skul''s Ghastly Touch'); + +-- ilvl 76 (Phase 2 (Pre-Raid)) +INSERT INTO `playerbots_bis_gear` VALUES +(9, 1, 0, 1, 76, 23310, 'Phase 2 (Pre-Raid)', 'Warlock', 'Demonology', 'Head', 'Alliance', 'Lieutenant Commander''s Dreadweave Cowl'), +(9, 1, 0, 2, 76, 23310, 'Phase 2 (Pre-Raid)', 'Warlock', 'Demonology', 'Head', 'Horde', 'Lieutenant Commander''s Dreadweave Cowl'), +(9, 1, 1, 1, 76, 18691, 'Phase 2 (Pre-Raid)', 'Warlock', 'Demonology', 'Neck', 'Alliance', 'Dark Advisor''s Pendant'), +(9, 1, 1, 2, 76, 18691, 'Phase 2 (Pre-Raid)', 'Warlock', 'Demonology', 'Neck', 'Horde', 'Dark Advisor''s Pendant'), +(9, 1, 2, 1, 76, 23311, 'Phase 2 (Pre-Raid)', 'Warlock', 'Demonology', 'Shoulders', 'Alliance', 'Lieutenant Commander''s Dreadweave Spaulders'), +(9, 1, 2, 2, 76, 23311, 'Phase 2 (Pre-Raid)', 'Warlock', 'Demonology', 'Shoulders', 'Horde', 'Lieutenant Commander''s Dreadweave Spaulders'), +(9, 1, 4, 1, 76, 14153, 'Phase 2 (Pre-Raid)', 'Warlock', 'Demonology', 'Chest', 'Alliance', 'Robe of the Void'), +(9, 1, 4, 2, 76, 14153, 'Phase 2 (Pre-Raid)', 'Warlock', 'Demonology', 'Chest', 'Horde', 'Robe of the Void'), +(9, 1, 5, 1, 76, 11662, 'Phase 2 (Pre-Raid)', 'Warlock', 'Demonology', 'Waist', 'Alliance', 'Ban''thok Sash'), +(9, 1, 5, 2, 76, 11662, 'Phase 2 (Pre-Raid)', 'Warlock', 'Demonology', 'Waist', 'Horde', 'Ban''thok Sash'), +(9, 1, 6, 1, 76, 13170, 'Phase 2 (Pre-Raid)', 'Warlock', 'Demonology', 'Legs', 'Alliance', 'Skyshroud Leggings'), +(9, 1, 6, 2, 76, 13170, 'Phase 2 (Pre-Raid)', 'Warlock', 'Demonology', 'Legs', 'Horde', 'Skyshroud Leggings'), +(9, 1, 7, 1, 76, 18735, 'Phase 2 (Pre-Raid)', 'Warlock', 'Demonology', 'Feet', 'Alliance', 'Maleki''s Footwraps'), +(9, 1, 7, 2, 76, 18735, 'Phase 2 (Pre-Raid)', 'Warlock', 'Demonology', 'Feet', 'Horde', 'Maleki''s Footwraps'), +(9, 1, 8, 1, 76, 11766, 'Phase 2 (Pre-Raid)', 'Warlock', 'Demonology', 'Wrists', 'Alliance', 'Flameweave Cuffs'), +(9, 1, 8, 2, 76, 11766, 'Phase 2 (Pre-Raid)', 'Warlock', 'Demonology', 'Wrists', 'Horde', 'Flameweave Cuffs'), +(9, 1, 9, 1, 76, 18407, 'Phase 2 (Pre-Raid)', 'Warlock', 'Demonology', 'Hands', 'Alliance', 'Felcloth Gloves'), +(9, 1, 9, 2, 76, 18407, 'Phase 2 (Pre-Raid)', 'Warlock', 'Demonology', 'Hands', 'Horde', 'Felcloth Gloves'), +(9, 1, 10, 1, 76, 12543, 'Phase 2 (Pre-Raid)', 'Warlock', 'Demonology', 'Finger1', 'Alliance', 'Songstone of Ironforge'), +(9, 1, 10, 2, 76, 12545, 'Phase 2 (Pre-Raid)', 'Warlock', 'Demonology', 'Finger1', 'Horde', 'Eye of Orgrimmar'), +(9, 1, 11, 1, 76, 13001, 'Phase 2 (Pre-Raid)', 'Warlock', 'Demonology', 'Finger2', 'Alliance', 'Maiden''s Circle'), +(9, 1, 11, 2, 76, 13001, 'Phase 2 (Pre-Raid)', 'Warlock', 'Demonology', 'Finger2', 'Horde', 'Maiden''s Circle'), +(9, 1, 12, 1, 76, 12930, 'Phase 2 (Pre-Raid)', 'Warlock', 'Demonology', 'Trinket1', 'Alliance', 'Briarwood Reed'), +(9, 1, 12, 2, 76, 12930, 'Phase 2 (Pre-Raid)', 'Warlock', 'Demonology', 'Trinket1', 'Horde', 'Briarwood Reed'), +(9, 1, 13, 1, 76, 18467, 'Phase 2 (Pre-Raid)', 'Warlock', 'Demonology', 'Trinket2', 'Alliance', 'Royal Seal of Eldre''Thalas'), +(9, 1, 13, 2, 76, 18467, 'Phase 2 (Pre-Raid)', 'Warlock', 'Demonology', 'Trinket2', 'Horde', 'Royal Seal of Eldre''Thalas'), +(9, 1, 14, 1, 76, 13386, 'Phase 2 (Pre-Raid)', 'Warlock', 'Demonology', 'Back', 'Alliance', 'Archivist Cape'), +(9, 1, 14, 2, 76, 13386, 'Phase 2 (Pre-Raid)', 'Warlock', 'Demonology', 'Back', 'Horde', 'Archivist Cape'), +(9, 1, 15, 1, 76, 18372, 'Phase 2 (Pre-Raid)', 'Warlock', 'Demonology', 'MainHand', 'Alliance', 'Blade of the New Moon'), +(9, 1, 15, 2, 76, 18372, 'Phase 2 (Pre-Raid)', 'Warlock', 'Demonology', 'MainHand', 'Horde', 'Blade of the New Moon'), +(9, 1, 16, 1, 76, 10796, 'Phase 2 (Pre-Raid)', 'Warlock', 'Demonology', 'OffHand', 'Alliance', 'Drakestone'), +(9, 1, 16, 2, 76, 10796, 'Phase 2 (Pre-Raid)', 'Warlock', 'Demonology', 'OffHand', 'Horde', 'Drakestone'), +(9, 1, 17, 1, 76, 13396, 'Phase 2 (Pre-Raid)', 'Warlock', 'Demonology', 'Ranged', 'Alliance', 'Skul''s Ghastly Touch'), +(9, 1, 17, 2, 76, 13396, 'Phase 2 (Pre-Raid)', 'Warlock', 'Demonology', 'Ranged', 'Horde', 'Skul''s Ghastly Touch'); + +-- ilvl 78 (Phase 2) +INSERT INTO `playerbots_bis_gear` VALUES +(9, 1, 0, 0, 78, 23310, 'Phase 2', 'Warlock', 'Demonology', 'Head', 'Both', 'Lieutenant Commander''s Dreadweave Cowl'), +(9, 1, 1, 0, 78, 18814, 'Phase 2', 'Warlock', 'Demonology', 'Neck', 'Both', 'Choker of the Fire Lord'), +(9, 1, 2, 0, 78, 23311, 'Phase 2', 'Warlock', 'Demonology', 'Shoulders', 'Both', 'Lieutenant Commander''s Dreadweave Spaulders'), +(9, 1, 4, 0, 78, 19145, 'Phase 2', 'Warlock', 'Demonology', 'Chest', 'Both', 'Robe of Volatile Power'), +(9, 1, 5, 0, 78, 18809, 'Phase 2', 'Warlock', 'Demonology', 'Waist', 'Both', 'Sash of Whispered Secrets'), +(9, 1, 6, 0, 78, 19133, 'Phase 2', 'Warlock', 'Demonology', 'Legs', 'Both', 'Fel Infused Leggings'), +(9, 1, 7, 0, 78, 19131, 'Phase 2', 'Warlock', 'Demonology', 'Feet', 'Both', 'Snowblind Shoes'), +(9, 1, 8, 0, 78, 11766, 'Phase 2', 'Warlock', 'Demonology', 'Wrists', 'Both', 'Flameweave Cuffs'), +(9, 1, 9, 0, 78, 18407, 'Phase 2', 'Warlock', 'Demonology', 'Hands', 'Both', 'Felcloth Gloves'), +(9, 1, 10, 0, 78, 19147, 'Phase 2', 'Warlock', 'Demonology', 'Finger1', 'Both', 'Ring of Spell Power'), +(9, 1, 11, 0, 78, 19147, 'Phase 2', 'Warlock', 'Demonology', 'Finger2', 'Both', 'Ring of Spell Power'), +(9, 1, 12, 0, 78, 12930, 'Phase 2', 'Warlock', 'Demonology', 'Trinket1', 'Both', 'Briarwood Reed'), +(9, 1, 13, 0, 78, 18820, 'Phase 2', 'Warlock', 'Demonology', 'Trinket2', 'Both', 'Talisman of Ephemeral Power'), +(9, 1, 14, 0, 78, 13386, 'Phase 2', 'Warlock', 'Demonology', 'Back', 'Both', 'Archivist Cape'), +(9, 1, 15, 0, 78, 17103, 'Phase 2', 'Warlock', 'Demonology', 'MainHand', 'Both', 'Azuresong Mageblade'), +(9, 1, 16, 0, 78, 10796, 'Phase 2', 'Warlock', 'Demonology', 'OffHand', 'Both', 'Drakestone'), +(9, 1, 17, 0, 78, 13396, 'Phase 2', 'Warlock', 'Demonology', 'Ranged', 'Both', 'Skul''s Ghastly Touch'); + +-- ilvl 83 (Phase 3) +INSERT INTO `playerbots_bis_gear` VALUES +(9, 1, 0, 0, 83, 19375, 'Phase 3', 'Warlock', 'Demonology', 'Head', 'Both', 'Mish''undare, Circlet of the Mind Flayer'), +(9, 1, 1, 0, 83, 18814, 'Phase 3', 'Warlock', 'Demonology', 'Neck', 'Both', 'Choker of the Fire Lord'), +(9, 1, 2, 0, 83, 19370, 'Phase 3', 'Warlock', 'Demonology', 'Shoulders', 'Both', 'Mantle of the Blackwing Cabal'), +(9, 1, 4, 0, 83, 19145, 'Phase 3', 'Warlock', 'Demonology', 'Chest', 'Both', 'Robe of Volatile Power'), +(9, 1, 5, 0, 83, 18809, 'Phase 3', 'Warlock', 'Demonology', 'Waist', 'Both', 'Sash of Whispered Secrets'), +(9, 1, 6, 0, 83, 19133, 'Phase 3', 'Warlock', 'Demonology', 'Legs', 'Both', 'Fel Infused Leggings'), +(9, 1, 7, 0, 83, 19131, 'Phase 3', 'Warlock', 'Demonology', 'Feet', 'Both', 'Snowblind Shoes'), +(9, 1, 8, 0, 83, 19374, 'Phase 3', 'Warlock', 'Demonology', 'Wrists', 'Both', 'Bracers of Arcane Accuracy'), +(9, 1, 9, 0, 83, 19407, 'Phase 3', 'Warlock', 'Demonology', 'Hands', 'Both', 'Ebony Flame Gloves'), +(9, 1, 11, 0, 83, 19434, 'Phase 3', 'Warlock', 'Demonology', 'Finger2', 'Both', 'Band of Dark Dominion'), +(9, 1, 12, 0, 83, 19379, 'Phase 3', 'Warlock', 'Demonology', 'Trinket1', 'Both', 'Neltharion''s Tear'), +(9, 1, 13, 0, 83, 18820, 'Phase 3', 'Warlock', 'Demonology', 'Trinket2', 'Both', 'Talisman of Ephemeral Power'), +(9, 1, 14, 0, 83, 19378, 'Phase 3', 'Warlock', 'Demonology', 'Back', 'Both', 'Cloak of the Brood Lord'), +(9, 1, 15, 0, 83, 19356, 'Phase 3', 'Warlock', 'Demonology', 'MainHand', 'Both', 'Staff of the Shadow Flame'), +(9, 1, 17, 0, 83, 13396, 'Phase 3', 'Warlock', 'Demonology', 'Ranged', 'Both', 'Skul''s Ghastly Touch'); + +-- ilvl 88 (Phase 5) +INSERT INTO `playerbots_bis_gear` VALUES +(9, 1, 0, 0, 88, 21337, 'Phase 5', 'Warlock', 'Demonology', 'Head', 'Both', 'Doomcaller''s Circlet'), +(9, 1, 1, 0, 88, 21608, 'Phase 5', 'Warlock', 'Demonology', 'Neck', 'Both', 'Amulet of Vek''nilash'), +(9, 1, 2, 0, 88, 21335, 'Phase 5', 'Warlock', 'Demonology', 'Shoulders', 'Both', 'Doomcaller''s Mantle'), +(9, 1, 4, 0, 88, 19682, 'Phase 5', 'Warlock', 'Demonology', 'Chest', 'Both', 'Bloodvine Vest'), +(9, 1, 5, 0, 88, 22730, 'Phase 5', 'Warlock', 'Demonology', 'Waist', 'Both', 'Eyestalk Waist Cord'), +(9, 1, 6, 0, 88, 19683, 'Phase 5', 'Warlock', 'Demonology', 'Legs', 'Both', 'Bloodvine Leggings'), +(9, 1, 7, 0, 88, 19684, 'Phase 5', 'Warlock', 'Demonology', 'Feet', 'Both', 'Bloodvine Boots'), +(9, 1, 8, 0, 88, 21186, 'Phase 5', 'Warlock', 'Demonology', 'Wrists', 'Both', 'Rockfury Bracers'), +(9, 1, 9, 0, 88, 21585, 'Phase 5', 'Warlock', 'Demonology', 'Hands', 'Both', 'Dark Storm Gauntlets'), +(9, 1, 10, 0, 88, 21417, 'Phase 5', 'Warlock', 'Demonology', 'Finger1', 'Both', 'Ring of Unspoken Names'), +(9, 1, 11, 0, 88, 21709, 'Phase 5', 'Warlock', 'Demonology', 'Finger2', 'Both', 'Ring of the Fallen God'), +(9, 1, 12, 0, 88, 19379, 'Phase 5', 'Warlock', 'Demonology', 'Trinket1', 'Both', 'Neltharion''s Tear'), +(9, 1, 13, 0, 88, 18820, 'Phase 5', 'Warlock', 'Demonology', 'Trinket2', 'Both', 'Talisman of Ephemeral Power'), +(9, 1, 14, 0, 88, 22731, 'Phase 5', 'Warlock', 'Demonology', 'Back', 'Both', 'Cloak of the Devoured'), +(9, 1, 15, 0, 88, 21622, 'Phase 5', 'Warlock', 'Demonology', 'MainHand', 'Both', 'Sharpened Silithid Femur'), +(9, 1, 16, 0, 88, 21597, 'Phase 5', 'Warlock', 'Demonology', 'OffHand', 'Both', 'Royal Scepter of Vek''lor'), +(9, 1, 17, 0, 88, 21603, 'Phase 5', 'Warlock', 'Demonology', 'Ranged', 'Both', 'Wand of Qiraji Nobility'); + +-- ilvl 92 (Phase 6) +INSERT INTO `playerbots_bis_gear` VALUES +(9, 1, 0, 0, 92, 22506, 'Phase 6', 'Warlock', 'Demonology', 'Head', 'Both', 'Plagueheart Circlet'), +(9, 1, 1, 0, 92, 21608, 'Phase 6', 'Warlock', 'Demonology', 'Neck', 'Both', 'Amulet of Vek''nilash'), +(9, 1, 2, 0, 92, 22507, 'Phase 6', 'Warlock', 'Demonology', 'Shoulders', 'Both', 'Plagueheart Shoulderpads'), +(9, 1, 4, 0, 92, 22504, 'Phase 6', 'Warlock', 'Demonology', 'Chest', 'Both', 'Plagueheart Robe'), +(9, 1, 5, 0, 92, 22730, 'Phase 6', 'Warlock', 'Demonology', 'Waist', 'Both', 'Eyestalk Waist Cord'), +(9, 1, 6, 0, 92, 23070, 'Phase 6', 'Warlock', 'Demonology', 'Legs', 'Both', 'Leggings of Polarity'), +(9, 1, 7, 0, 92, 22508, 'Phase 6', 'Warlock', 'Demonology', 'Feet', 'Both', 'Plagueheart Sandals'), +(9, 1, 8, 0, 92, 21186, 'Phase 6', 'Warlock', 'Demonology', 'Wrists', 'Both', 'Rockfury Bracers'), +(9, 1, 9, 0, 92, 21585, 'Phase 6', 'Warlock', 'Demonology', 'Hands', 'Both', 'Dark Storm Gauntlets'), +(9, 1, 10, 0, 92, 23031, 'Phase 6', 'Warlock', 'Demonology', 'Finger1', 'Both', 'Band of the Inevitable'), +(9, 1, 11, 0, 92, 21709, 'Phase 6', 'Warlock', 'Demonology', 'Finger2', 'Both', 'Ring of the Fallen God'), +(9, 1, 12, 0, 92, 19379, 'Phase 6', 'Warlock', 'Demonology', 'Trinket1', 'Both', 'Neltharion''s Tear'), +(9, 1, 13, 0, 92, 23046, 'Phase 6', 'Warlock', 'Demonology', 'Trinket2', 'Both', 'The Restrained Essence of Sapphiron'), +(9, 1, 14, 0, 92, 23050, 'Phase 6', 'Warlock', 'Demonology', 'Back', 'Both', 'Cloak of the Necropolis'), +(9, 1, 15, 0, 92, 22807, 'Phase 6', 'Warlock', 'Demonology', 'MainHand', 'Both', 'Wraith Blade'), +(9, 1, 16, 0, 92, 23049, 'Phase 6', 'Warlock', 'Demonology', 'OffHand', 'Both', 'Sapphiron''s Left Eye'), +(9, 1, 17, 0, 92, 22820, 'Phase 6', 'Warlock', 'Demonology', 'Ranged', 'Both', 'Wand of Fates'); + +-- ilvl 120 (Pre-Raid) +INSERT INTO `playerbots_bis_gear` VALUES +(9, 1, 0, 0, 120, 24266, 'Pre-Raid', 'Warlock', 'Demonology', 'Head', 'Both', 'Spellstrike Hood'), +(9, 1, 1, 0, 120, 28134, 'Pre-Raid', 'Warlock', 'Demonology', 'Neck', 'Both', 'Brooch of Heightened Potential'), +(9, 1, 2, 0, 120, 21869, 'Pre-Raid', 'Warlock', 'Demonology', 'Shoulders', 'Both', 'Frozen Shadoweave Shoulders'), +(9, 1, 4, 0, 120, 21871, 'Pre-Raid', 'Warlock', 'Demonology', 'Chest', 'Both', 'Frozen Shadoweave Robe'), +(9, 1, 5, 0, 120, 24256, 'Pre-Raid', 'Warlock', 'Demonology', 'Waist', 'Both', 'Girdle of Ruination'), +(9, 1, 6, 0, 120, 24262, 'Pre-Raid', 'Warlock', 'Demonology', 'Legs', 'Both', 'Spellstrike Pants'), +(9, 1, 7, 0, 120, 21870, 'Pre-Raid', 'Warlock', 'Demonology', 'Feet', 'Both', 'Frozen Shadoweave Boots'), +(9, 1, 8, 0, 120, 24250, 'Pre-Raid', 'Warlock', 'Demonology', 'Wrists', 'Both', 'Bracers of Havok'), +(9, 1, 9, 0, 120, 24450, 'Pre-Raid', 'Warlock', 'Demonology', 'Hands', 'Both', 'Manaspark Gloves'), +(9, 1, 10, 0, 120, 29172, 'Pre-Raid', 'Warlock', 'Demonology', 'Finger1', 'Both', 'Ashyen''s Gift'), +(9, 1, 11, 0, 120, 28227, 'Pre-Raid', 'Warlock', 'Demonology', 'Finger2', 'Both', 'Sparking Arcanite Ring'), +(9, 1, 12, 0, 120, 29370, 'Pre-Raid', 'Warlock', 'Demonology', 'Trinket1', 'Both', 'Icon of the Silver Crescent'), +(9, 1, 13, 0, 120, 29132, 'Pre-Raid', 'Warlock', 'Demonology', 'Trinket2', 'Both', 'Scryer''s Bloodgem'), +(9, 1, 14, 0, 120, 27981, 'Pre-Raid', 'Warlock', 'Demonology', 'Back', 'Both', 'Sethekk Oracle Cloak'), +(9, 1, 15, 0, 120, 23554, 'Pre-Raid', 'Warlock', 'Demonology', 'MainHand', 'Both', 'Eternium Runed Blade'), +(9, 1, 16, 0, 120, 29273, 'Pre-Raid', 'Warlock', 'Demonology', 'OffHand', 'Both', 'Khadgar''s Knapsack'), +(9, 1, 17, 0, 120, 29350, 'Pre-Raid', 'Warlock', 'Demonology', 'Ranged', 'Both', 'The Black Stalk'); + +-- ilvl 125 (Phase 1) +INSERT INTO `playerbots_bis_gear` VALUES +(9, 1, 0, 0, 125, 28963, 'Phase 1', 'Warlock', 'Demonology', 'Head', 'Both', 'Voidheart Crown'), +(9, 1, 1, 0, 125, 28530, 'Phase 1', 'Warlock', 'Demonology', 'Neck', 'Both', 'Brooch of Unquenchable Fury'), +(9, 1, 2, 0, 125, 28967, 'Phase 1', 'Warlock', 'Demonology', 'Shoulders', 'Both', 'Voidheart Mantle'), +(9, 1, 4, 0, 125, 28964, 'Phase 1', 'Warlock', 'Demonology', 'Chest', 'Both', 'Voidheart Robe'), +(9, 1, 5, 0, 125, 24256, 'Phase 1', 'Warlock', 'Demonology', 'Waist', 'Both', 'Girdle of Ruination'), +(9, 1, 6, 0, 125, 30734, 'Phase 1', 'Warlock', 'Demonology', 'Legs', 'Both', 'Leggings of the Seventh Circle'), +(9, 1, 7, 0, 125, 21870, 'Phase 1', 'Warlock', 'Demonology', 'Feet', 'Both', 'Frozen Shadoweave Boots'), +(9, 1, 8, 0, 125, 24250, 'Phase 1', 'Warlock', 'Demonology', 'Wrists', 'Both', 'Bracers of Havok'), +(9, 1, 9, 0, 125, 28968, 'Phase 1', 'Warlock', 'Demonology', 'Hands', 'Both', 'Voidheart Gloves'), +(9, 1, 10, 0, 125, 29172, 'Phase 1', 'Warlock', 'Demonology', 'Finger1', 'Both', 'Ashyen''s Gift'), +(9, 1, 11, 0, 125, 28793, 'Phase 1', 'Warlock', 'Demonology', 'Finger2', 'Both', 'Band of Crimson Fury'), +(9, 1, 12, 0, 125, 29370, 'Phase 1', 'Warlock', 'Demonology', 'Trinket1', 'Both', 'Icon of the Silver Crescent'), +(9, 1, 13, 0, 125, 27683, 'Phase 1', 'Warlock', 'Demonology', 'Trinket2', 'Both', 'Quagmirran''s Eye'), +(9, 1, 14, 0, 125, 28766, 'Phase 1', 'Warlock', 'Demonology', 'Back', 'Both', 'Ruby Drape of the Mysticant'), +(9, 1, 15, 0, 125, 30723, 'Phase 1', 'Warlock', 'Demonology', 'MainHand', 'Both', 'Talon of the Tempest'), +(9, 1, 16, 0, 125, 29272, 'Phase 1', 'Warlock', 'Demonology', 'OffHand', 'Both', 'Orb of the Soul-Eater'), +(9, 1, 17, 0, 125, 28783, 'Phase 1', 'Warlock', 'Demonology', 'Ranged', 'Both', 'Eredar Wand of Obliteration'); + +-- ilvl 141 (Phase 2) +INSERT INTO `playerbots_bis_gear` VALUES +(9, 1, 0, 0, 141, 32494, 'Phase 2', 'Warlock', 'Demonology', 'Head', 'Both', 'Destruction Holo-gogs'), +(9, 1, 1, 0, 141, 30015, 'Phase 2', 'Warlock', 'Demonology', 'Neck', 'Both', 'The Sun King''s Talisman'), +(9, 1, 2, 0, 141, 28967, 'Phase 2', 'Warlock', 'Demonology', 'Shoulders', 'Both', 'Voidheart Mantle'), +(9, 1, 4, 0, 141, 30107, 'Phase 2', 'Warlock', 'Demonology', 'Chest', 'Both', 'Vestments of the Sea-Witch'), +(9, 1, 5, 0, 141, 30038, 'Phase 2', 'Warlock', 'Demonology', 'Waist', 'Both', 'Belt of Blasting'), +(9, 1, 6, 0, 141, 30213, 'Phase 2', 'Warlock', 'Demonology', 'Legs', 'Both', 'Leggings of the Corruptor'), +(9, 1, 7, 0, 141, 30037, 'Phase 2', 'Warlock', 'Demonology', 'Feet', 'Both', 'Boots of Blasting'), +(9, 1, 8, 0, 141, 29918, 'Phase 2', 'Warlock', 'Demonology', 'Wrists', 'Both', 'Mindstorm Wristbands'), +(9, 1, 9, 0, 141, 28968, 'Phase 2', 'Warlock', 'Demonology', 'Hands', 'Both', 'Voidheart Gloves'), +(9, 1, 10, 0, 141, 29302, 'Phase 2', 'Warlock', 'Demonology', 'Finger1', 'Both', 'Band of Eternity'), +(9, 1, 11, 0, 141, 30109, 'Phase 2', 'Warlock', 'Demonology', 'Finger2', 'Both', 'Ring of Endless Coils'), +(9, 1, 12, 0, 141, 29370, 'Phase 2', 'Warlock', 'Demonology', 'Trinket1', 'Both', 'Icon of the Silver Crescent'), +(9, 1, 13, 0, 141, 27683, 'Phase 2', 'Warlock', 'Demonology', 'Trinket2', 'Both', 'Quagmirran''s Eye'), +(9, 1, 14, 0, 141, 28766, 'Phase 2', 'Warlock', 'Demonology', 'Back', 'Both', 'Ruby Drape of the Mysticant'), +(9, 1, 15, 0, 141, 30095, 'Phase 2', 'Warlock', 'Demonology', 'MainHand', 'Both', 'Fang of the Leviathan'), +(9, 1, 16, 0, 141, 30049, 'Phase 2', 'Warlock', 'Demonology', 'OffHand', 'Both', 'Fathomstone'), +(9, 1, 17, 0, 141, 29982, 'Phase 2', 'Warlock', 'Demonology', 'Ranged', 'Both', 'Wand of the Forgotten Star'); + +-- ilvl 156 (Phase 3) +INSERT INTO `playerbots_bis_gear` VALUES +(9, 1, 0, 0, 156, 31051, 'Phase 3', 'Warlock', 'Demonology', 'Head', 'Both', 'Hood of the Malefic'), +(9, 1, 1, 0, 156, 30015, 'Phase 3', 'Warlock', 'Demonology', 'Neck', 'Both', 'Sun King''s Talisman'), +(9, 1, 2, 0, 156, 31054, 'Phase 3', 'Warlock', 'Demonology', 'Shoulders', 'Both', 'Mantle of the Malefic'), +(9, 1, 4, 0, 156, 30107, 'Phase 3', 'Warlock', 'Demonology', 'Chest', 'Both', 'Vestments of the Sea-Witch'), +(9, 1, 5, 0, 156, 30888, 'Phase 3', 'Warlock', 'Demonology', 'Waist', 'Both', 'Anetheron''s Noose'), +(9, 1, 6, 0, 156, 31053, 'Phase 3', 'Warlock', 'Demonology', 'Legs', 'Both', 'Leggings of the Malefic'), +(9, 1, 7, 0, 156, 32239, 'Phase 3', 'Warlock', 'Demonology', 'Feet', 'Both', 'Slippers of the Seacaller'), +(9, 1, 8, 0, 156, 32586, 'Phase 3', 'Warlock', 'Demonology', 'Wrists', 'Both', 'Bracers of Nimble Thought'), +(9, 1, 9, 0, 156, 31050, 'Phase 3', 'Warlock', 'Demonology', 'Hands', 'Both', 'Gloves of the Malefic'), +(9, 1, 10, 0, 156, 32527, 'Phase 3', 'Warlock', 'Demonology', 'Finger1', 'Both', 'Ring of Ancient Knowledge'), +(9, 1, 11, 0, 156, 32247, 'Phase 3', 'Warlock', 'Demonology', 'Finger2', 'Both', 'Ring of Captured Storms'), +(9, 1, 12, 0, 156, 29370, 'Phase 3', 'Warlock', 'Demonology', 'Trinket1', 'Both', 'Icon of the Silver Crescent'), +(9, 1, 13, 0, 156, 32483, 'Phase 3', 'Warlock', 'Demonology', 'Trinket2', 'Both', 'The Skull of Gul''dan'), +(9, 1, 14, 0, 156, 32590, 'Phase 3', 'Warlock', 'Demonology', 'Back', 'Both', 'Nethervoid Cloak'), +(9, 1, 15, 0, 156, 32374, 'Phase 3', 'Warlock', 'Demonology', 'MainHand', 'Both', 'Zhar''doom, Greatstaff of the Devourer'), +(9, 1, 17, 0, 156, 29982, 'Phase 3', 'Warlock', 'Demonology', 'Ranged', 'Both', 'Wand of the Forgotten Star'); + +-- ilvl 164 (Phase 4) +INSERT INTO `playerbots_bis_gear` VALUES +(9, 1, 0, 0, 164, 31051, 'Phase 4', 'Warlock', 'Demonology', 'Head', 'Both', 'Hood of the Malefic'), +(9, 1, 1, 0, 164, 30015, 'Phase 4', 'Warlock', 'Demonology', 'Neck', 'Both', 'Sun King''s Talisman'), +(9, 1, 2, 0, 164, 31054, 'Phase 4', 'Warlock', 'Demonology', 'Shoulders', 'Both', 'Mantle of the Malefic'), +(9, 1, 4, 0, 164, 30107, 'Phase 4', 'Warlock', 'Demonology', 'Chest', 'Both', 'Vestments of the Sea-Witch'), +(9, 1, 5, 0, 164, 30888, 'Phase 4', 'Warlock', 'Demonology', 'Waist', 'Both', 'Anetheron''s Noose'), +(9, 1, 6, 0, 164, 31053, 'Phase 4', 'Warlock', 'Demonology', 'Legs', 'Both', 'Leggings of the Malefic'), +(9, 1, 7, 0, 164, 32239, 'Phase 4', 'Warlock', 'Demonology', 'Feet', 'Both', 'Slippers of the Seacaller'), +(9, 1, 8, 0, 164, 32586, 'Phase 4', 'Warlock', 'Demonology', 'Wrists', 'Both', 'Bracers of Nimble Thought'), +(9, 1, 9, 0, 164, 31050, 'Phase 4', 'Warlock', 'Demonology', 'Hands', 'Both', 'Gloves of the Malefic'), +(9, 1, 10, 0, 164, 33497, 'Phase 4', 'Warlock', 'Demonology', 'Finger1', 'Both', 'Mana Attuned Band'), +(9, 1, 11, 0, 164, 32247, 'Phase 4', 'Warlock', 'Demonology', 'Finger2', 'Both', 'Ring of Captured Storms'), +(9, 1, 12, 0, 164, 33829, 'Phase 4', 'Warlock', 'Demonology', 'Trinket1', 'Both', 'Hex Shrunken Head'), +(9, 1, 13, 0, 164, 32483, 'Phase 4', 'Warlock', 'Demonology', 'Trinket2', 'Both', 'The Skull of Gul''dan'), +(9, 1, 14, 0, 164, 32590, 'Phase 4', 'Warlock', 'Demonology', 'Back', 'Both', 'Nethervoid Cloak'), +(9, 1, 15, 0, 164, 32374, 'Phase 4', 'Warlock', 'Demonology', 'MainHand', 'Both', 'Zhar''doom, Greatstaff of the Devourer'), +(9, 1, 16, 0, 164, 34179, 'Phase 5', 'Warlock', 'Demonology', 'OffHand', 'Both', 'Heart of the Pit'), +(9, 1, 17, 0, 164, 33192, 'Phase 4', 'Warlock', 'Demonology', 'Ranged', 'Both', 'Carved Witch Doctor''s Stick'); + +-- ilvl 200 (Pre-Raid) +INSERT INTO `playerbots_bis_gear` VALUES +(9, 1, 0, 0, 200, 37684, 'Pre-Raid', 'Warlock', 'Demonology', 'Head', 'Both', 'Forgotten Shadow Hood'), +(9, 1, 1, 0, 200, 40680, 'Pre-Raid', 'Warlock', 'Demonology', 'Neck', 'Both', 'Encircling Burnished Gold Chains'), +(9, 1, 2, 0, 200, 37196, 'Pre-Raid', 'Warlock', 'Demonology', 'Shoulders', 'Both', 'Runecaster''s Mantle'), +(9, 1, 4, 0, 200, 39497, 'Pre-Raid', 'Warlock', 'Demonology', 'Chest', 'Both', 'Heroes'' Plagueheart Robe'), +(9, 1, 5, 0, 200, 37408, 'Pre-Raid', 'Warlock', 'Demonology', 'Waist', 'Both', 'Girdle of Bane'), +(9, 1, 6, 0, 200, 34170, 'Pre-Raid', 'Warlock', 'Demonology', 'Legs', 'Both', 'Pantaloons of Calming Strife'), +(9, 1, 7, 0, 200, 44202, 'Pre-Raid', 'Warlock', 'Demonology', 'Feet', 'Both', 'Sandals of Crimson Fury'), +(9, 1, 8, 0, 200, 37361, 'Pre-Raid', 'Warlock', 'Demonology', 'Wrists', 'Both', 'Cuffs of Winged Levitation'), +(9, 1, 9, 0, 200, 42113, 'Pre-Raid', 'Warlock', 'Demonology', 'Hands', 'Both', 'Spellweave Gloves'), +(9, 1, 10, 0, 200, 40585, 'Pre-Raid', 'Warlock', 'Demonology', 'Finger1', 'Both', 'Signet of the Kirin Tor'), +(9, 1, 12, 0, 200, 40682, 'Pre-Raid', 'Warlock', 'Demonology', 'Trinket1', 'Both', 'Sundial of the Exiled'), +(9, 1, 14, 0, 200, 41610, 'Pre-Raid', 'Warlock', 'Demonology', 'Back', 'Both', 'Deathchill Cloak'), +(9, 1, 17, 0, 200, 34348, 'Pre-Raid', 'Warlock', 'Demonology', 'Ranged', 'Both', 'Wand of Cleansing Light'); + +-- ilvl 224 (Phase 1) +INSERT INTO `playerbots_bis_gear` VALUES +(9, 1, 0, 0, 224, 40421, 'Phase 1', 'Warlock', 'Demonology', 'Head', 'Both', 'Valorous Plagueheart Circlet'), +(9, 1, 1, 0, 224, 44661, 'Phase 1', 'Warlock', 'Demonology', 'Neck', 'Both', 'Wyrmrest Necklace of Power'), +(9, 1, 2, 0, 224, 40424, 'Phase 1', 'Warlock', 'Demonology', 'Shoulders', 'Both', 'Valorous Plagueheart Shoulderpads'), +(9, 1, 4, 0, 224, 40423, 'Phase 1', 'Warlock', 'Demonology', 'Chest', 'Both', 'Valorous Plagueheart Robe'), +(9, 1, 5, 0, 224, 40561, 'Phase 1', 'Warlock', 'Demonology', 'Waist', 'Both', 'Leash of Heedless Magic'), +(9, 1, 6, 0, 224, 40560, 'Phase 1', 'Warlock', 'Demonology', 'Legs', 'Both', 'Leggings of the Wanton Spellcaster'), +(9, 1, 7, 0, 224, 40558, 'Phase 1', 'Warlock', 'Demonology', 'Feet', 'Both', 'Arcanic Tramplers'), +(9, 1, 8, 0, 224, 44008, 'Phase 1', 'Warlock', 'Demonology', 'Wrists', 'Both', 'Unsullied Cuffs'), +(9, 1, 9, 0, 224, 40420, 'Phase 1', 'Warlock', 'Demonology', 'Hands', 'Both', 'Valorous Plagueheart Gloves'), +(9, 1, 10, 0, 224, 40399, 'Phase 1', 'Warlock', 'Demonology', 'Finger1', 'Both', 'Signet of Manifested Pain'), +(9, 1, 12, 0, 224, 40432, 'Phase 1', 'Warlock', 'Demonology', 'Trinket1', 'Both', 'Illustration of the Dragon Soul'), +(9, 1, 14, 0, 224, 44005, 'Phase 1', 'Warlock', 'Demonology', 'Back', 'Both', 'Pennant Cloak'), +(9, 1, 15, 0, 224, 40396, 'Phase 1', 'Warlock', 'Demonology', 'MainHand', 'Both', 'The Turning Tide'), +(9, 1, 17, 0, 224, 39426, 'Phase 1', 'Warlock', 'Demonology', 'Ranged', 'Both', 'Wand of the Archlich'); + +-- ilvl 245 (Phase 2) +INSERT INTO `playerbots_bis_gear` VALUES +(9, 1, 0, 0, 245, 45497, 'Phase 2', 'Warlock', 'Demonology', 'Head', 'Both', 'Crown of Luminescence'), +(9, 1, 1, 0, 245, 45133, 'Phase 2', 'Warlock', 'Demonology', 'Neck', 'Both', 'Pendant of Fiery Havoc'), +(9, 1, 2, 0, 245, 46068, 'Phase 2', 'Warlock', 'Demonology', 'Shoulders', 'Both', 'Amice of Inconceivable Horror'), +(9, 1, 4, 0, 245, 46137, 'Phase 2', 'Warlock', 'Demonology', 'Chest', 'Both', 'Conqueror''s Deathbringer Robe'), +(9, 1, 9, 0, 245, 45665, 'Phase 2', 'Warlock', 'Demonology', 'Hands', 'Both', 'Pharos Gloves'), +(9, 1, 10, 0, 245, 45495, 'Phase 2', 'Warlock', 'Demonology', 'Finger1', 'Both', 'Conductive Seal'), +(9, 1, 14, 0, 245, 45618, 'Phase 2', 'Warlock', 'Demonology', 'Back', 'Both', 'Sunglimmer Cloak'), +(9, 1, 15, 0, 245, 45620, 'Phase 2', 'Warlock', 'Demonology', 'MainHand', 'Both', 'Starshard Edge'), +(9, 1, 17, 0, 245, 45294, 'Phase 2', 'Warlock', 'Demonology', 'Ranged', 'Both', 'Petrified Ivy Sprig'); + +-- ilvl 258 (Phase 3) +INSERT INTO `playerbots_bis_gear` VALUES +(9, 1, 10, 0, 258, 45495, 'Phase 3', 'Warlock', 'Demonology', 'Finger1', 'Both', 'Conductive Seal'), +(9, 1, 17, 0, 258, 45294, 'Phase 3', 'Warlock', 'Demonology', 'Ranged', 'Both', 'Petrified Ivy Sprig'); + +-- ilvl 290 (Phase 5) +INSERT INTO `playerbots_bis_gear` VALUES +(9, 1, 0, 0, 290, 51231, 'Phase 5', 'Warlock', 'Demonology', 'Head', 'Both', 'Sanctified Dark Coven Hood'), +(9, 1, 1, 0, 290, 50182, 'Phase 5', 'Warlock', 'Demonology', 'Neck', 'Both', 'Blood Queen''s Crimson Choker'), +(9, 1, 2, 0, 290, 51234, 'Phase 5', 'Warlock', 'Demonology', 'Shoulders', 'Both', 'Sanctified Dark Coven Shoulderpads'), +(9, 1, 4, 0, 290, 51233, 'Phase 5', 'Warlock', 'Demonology', 'Chest', 'Both', 'Sanctified Dark Coven Robe'), +(9, 1, 5, 0, 290, 50613, 'Phase 5', 'Warlock', 'Demonology', 'Waist', 'Both', 'Crushing Coldwraith Belt'), +(9, 1, 6, 0, 290, 50694, 'Phase 5', 'Warlock', 'Demonology', 'Legs', 'Both', 'Plaguebringer''s Stained Pants'), +(9, 1, 7, 0, 290, 50699, 'Phase 5', 'Warlock', 'Demonology', 'Feet', 'Both', 'Plague Scientist''s Boots'), +(9, 1, 8, 0, 290, 54582, 'Phase 5', 'Warlock', 'Demonology', 'Wrists', 'Both', 'Bracers of Fiery Night'), +(9, 1, 9, 0, 290, 51230, 'Phase 5', 'Warlock', 'Demonology', 'Hands', 'Both', 'Sanctified Dark Coven Gloves'), +(9, 1, 10, 0, 290, 50614, 'Phase 5', 'Warlock', 'Demonology', 'Finger1', 'Both', 'Loop of the Endless Labyrinth'), +(9, 1, 11, 0, 290, 50664, 'Phase 5', 'Warlock', 'Demonology', 'Finger2', 'Both', 'Ring of Rapid Ascent'), +(9, 1, 12, 0, 290, 54588, 'Phase 5', 'Warlock', 'Demonology', 'Trinket1', 'Both', 'Charred Twilight Scale'), +(9, 1, 13, 0, 290, 50365, 'Phase 5', 'Warlock', 'Demonology', 'Trinket2', 'Both', 'Phylactery of the Nameless Lich'), +(9, 1, 14, 0, 290, 54583, 'Phase 5', 'Warlock', 'Demonology', 'Back', 'Both', 'Cloak of Burning Dusk'), +(9, 1, 15, 0, 290, 50732, 'Phase 5', 'Warlock', 'Demonology', 'MainHand', 'Both', 'Bloodsurge, Kel''Thuzad''s Blade of Agony'), +(9, 1, 16, 0, 290, 50719, 'Phase 5', 'Warlock', 'Demonology', 'OffHand', 'Both', 'Shadow Silk Spindle'), +(9, 1, 17, 0, 290, 50684, 'Phase 5', 'Warlock', 'Demonology', 'Ranged', 'Both', 'Corpse-Impaling Spike'); + +-- Destruction (tab 2) +-- ilvl 66 (Phase 1 (Pre-Raid)) +INSERT INTO `playerbots_bis_gear` VALUES +(9, 2, 0, 1, 66, 10504, 'Phase 1 (Pre-Raid)', 'Warlock', 'Destruction', 'Head', 'Alliance', 'Green Lens'), +(9, 2, 0, 2, 66, 10504, 'Phase 1 (Pre-Raid)', 'Warlock', 'Destruction', 'Head', 'Horde', 'Green Lens'), +(9, 2, 1, 1, 66, 18691, 'Phase 1 (Pre-Raid)', 'Warlock', 'Destruction', 'Neck', 'Alliance', 'Dark Advisor''s Pendant'), +(9, 2, 1, 2, 66, 18691, 'Phase 1 (Pre-Raid)', 'Warlock', 'Destruction', 'Neck', 'Horde', 'Dark Advisor''s Pendant'), +(9, 2, 2, 1, 66, 14112, 'Phase 1 (Pre-Raid)', 'Warlock', 'Destruction', 'Shoulders', 'Alliance', 'Felcloth Shoulders'), +(9, 2, 2, 2, 66, 14112, 'Phase 1 (Pre-Raid)', 'Warlock', 'Destruction', 'Shoulders', 'Horde', 'Felcloth Shoulders'), +(9, 2, 4, 1, 66, 14153, 'Phase 1 (Pre-Raid)', 'Warlock', 'Destruction', 'Chest', 'Alliance', 'Robe of the Void'), +(9, 2, 4, 2, 66, 14153, 'Phase 1 (Pre-Raid)', 'Warlock', 'Destruction', 'Chest', 'Horde', 'Robe of the Void'), +(9, 2, 5, 1, 66, 11662, 'Phase 1 (Pre-Raid)', 'Warlock', 'Destruction', 'Waist', 'Alliance', 'Ban''thok Sash'), +(9, 2, 5, 2, 66, 11662, 'Phase 1 (Pre-Raid)', 'Warlock', 'Destruction', 'Waist', 'Horde', 'Ban''thok Sash'), +(9, 2, 6, 1, 66, 13170, 'Phase 1 (Pre-Raid)', 'Warlock', 'Destruction', 'Legs', 'Alliance', 'Skyshroud Leggings'), +(9, 2, 6, 2, 66, 13170, 'Phase 1 (Pre-Raid)', 'Warlock', 'Destruction', 'Legs', 'Horde', 'Skyshroud Leggings'), +(9, 2, 7, 1, 66, 18735, 'Phase 1 (Pre-Raid)', 'Warlock', 'Destruction', 'Feet', 'Alliance', 'Maleki''s Footwraps'), +(9, 2, 7, 2, 66, 18735, 'Phase 1 (Pre-Raid)', 'Warlock', 'Destruction', 'Feet', 'Horde', 'Maleki''s Footwraps'), +(9, 2, 8, 1, 66, 11766, 'Phase 1 (Pre-Raid)', 'Warlock', 'Destruction', 'Wrists', 'Alliance', 'Flameweave Cuffs'), +(9, 2, 8, 2, 66, 11766, 'Phase 1 (Pre-Raid)', 'Warlock', 'Destruction', 'Wrists', 'Horde', 'Flameweave Cuffs'), +(9, 2, 9, 1, 66, 13253, 'Phase 1 (Pre-Raid)', 'Warlock', 'Destruction', 'Hands', 'Alliance', 'Hands of Power'), +(9, 2, 9, 2, 66, 13253, 'Phase 1 (Pre-Raid)', 'Warlock', 'Destruction', 'Hands', 'Horde', 'Hands of Power'), +(9, 2, 10, 1, 66, 12543, 'Phase 1 (Pre-Raid)', 'Warlock', 'Destruction', 'Finger1', 'Alliance', 'Songstone of Ironforge'), +(9, 2, 10, 2, 66, 12545, 'Phase 1 (Pre-Raid)', 'Warlock', 'Destruction', 'Finger1', 'Horde', 'Eye of Orgrimmar'), +(9, 2, 11, 1, 66, 13001, 'Phase 1 (Pre-Raid)', 'Warlock', 'Destruction', 'Finger2', 'Alliance', 'Maiden''s Circle'), +(9, 2, 11, 2, 66, 13001, 'Phase 1 (Pre-Raid)', 'Warlock', 'Destruction', 'Finger2', 'Horde', 'Maiden''s Circle'), +(9, 2, 12, 1, 66, 12930, 'Phase 1 (Pre-Raid)', 'Warlock', 'Destruction', 'Trinket1', 'Alliance', 'Briarwood Reed'), +(9, 2, 12, 2, 66, 12930, 'Phase 1 (Pre-Raid)', 'Warlock', 'Destruction', 'Trinket1', 'Horde', 'Briarwood Reed'), +(9, 2, 13, 1, 66, 13968, 'Phase 1 (Pre-Raid)', 'Warlock', 'Destruction', 'Trinket2', 'Alliance', 'Eye of the Beast'), +(9, 2, 13, 2, 66, 13968, 'Phase 1 (Pre-Raid)', 'Warlock', 'Destruction', 'Trinket2', 'Horde', 'Eye of the Beast'), +(9, 2, 14, 1, 66, 13386, 'Phase 1 (Pre-Raid)', 'Warlock', 'Destruction', 'Back', 'Alliance', 'Archivist Cape'), +(9, 2, 14, 2, 66, 13386, 'Phase 1 (Pre-Raid)', 'Warlock', 'Destruction', 'Back', 'Horde', 'Archivist Cape'), +(9, 2, 15, 1, 66, 13964, 'Phase 1 (Pre-Raid)', 'Warlock', 'Destruction', 'MainHand', 'Alliance', 'Witchblade'), +(9, 2, 15, 2, 66, 13964, 'Phase 1 (Pre-Raid)', 'Warlock', 'Destruction', 'MainHand', 'Horde', 'Witchblade'), +(9, 2, 16, 1, 66, 10796, 'Phase 1 (Pre-Raid)', 'Warlock', 'Destruction', 'OffHand', 'Alliance', 'Drakestone'), +(9, 2, 16, 2, 66, 10796, 'Phase 1 (Pre-Raid)', 'Warlock', 'Destruction', 'OffHand', 'Horde', 'Drakestone'), +(9, 2, 17, 1, 66, 13396, 'Phase 1 (Pre-Raid)', 'Warlock', 'Destruction', 'Ranged', 'Alliance', 'Skul''s Ghastly Touch'), +(9, 2, 17, 2, 66, 13396, 'Phase 1 (Pre-Raid)', 'Warlock', 'Destruction', 'Ranged', 'Horde', 'Skul''s Ghastly Touch'); + +-- ilvl 76 (Phase 2 (Pre-Raid)) +INSERT INTO `playerbots_bis_gear` VALUES +(9, 2, 0, 1, 76, 23310, 'Phase 2 (Pre-Raid)', 'Warlock', 'Destruction', 'Head', 'Alliance', 'Lieutenant Commander''s Dreadweave Cowl'), +(9, 2, 0, 2, 76, 23310, 'Phase 2 (Pre-Raid)', 'Warlock', 'Destruction', 'Head', 'Horde', 'Lieutenant Commander''s Dreadweave Cowl'), +(9, 2, 1, 1, 76, 18691, 'Phase 2 (Pre-Raid)', 'Warlock', 'Destruction', 'Neck', 'Alliance', 'Dark Advisor''s Pendant'), +(9, 2, 1, 2, 76, 18691, 'Phase 2 (Pre-Raid)', 'Warlock', 'Destruction', 'Neck', 'Horde', 'Dark Advisor''s Pendant'), +(9, 2, 2, 1, 76, 23311, 'Phase 2 (Pre-Raid)', 'Warlock', 'Destruction', 'Shoulders', 'Alliance', 'Lieutenant Commander''s Dreadweave Spaulders'), +(9, 2, 2, 2, 76, 23311, 'Phase 2 (Pre-Raid)', 'Warlock', 'Destruction', 'Shoulders', 'Horde', 'Lieutenant Commander''s Dreadweave Spaulders'), +(9, 2, 4, 1, 76, 14153, 'Phase 2 (Pre-Raid)', 'Warlock', 'Destruction', 'Chest', 'Alliance', 'Robe of the Void'), +(9, 2, 4, 2, 76, 14153, 'Phase 2 (Pre-Raid)', 'Warlock', 'Destruction', 'Chest', 'Horde', 'Robe of the Void'), +(9, 2, 5, 1, 76, 11662, 'Phase 2 (Pre-Raid)', 'Warlock', 'Destruction', 'Waist', 'Alliance', 'Ban''thok Sash'), +(9, 2, 5, 2, 76, 11662, 'Phase 2 (Pre-Raid)', 'Warlock', 'Destruction', 'Waist', 'Horde', 'Ban''thok Sash'), +(9, 2, 6, 1, 76, 13170, 'Phase 2 (Pre-Raid)', 'Warlock', 'Destruction', 'Legs', 'Alliance', 'Skyshroud Leggings'), +(9, 2, 6, 2, 76, 13170, 'Phase 2 (Pre-Raid)', 'Warlock', 'Destruction', 'Legs', 'Horde', 'Skyshroud Leggings'), +(9, 2, 7, 1, 76, 18735, 'Phase 2 (Pre-Raid)', 'Warlock', 'Destruction', 'Feet', 'Alliance', 'Maleki''s Footwraps'), +(9, 2, 7, 2, 76, 18735, 'Phase 2 (Pre-Raid)', 'Warlock', 'Destruction', 'Feet', 'Horde', 'Maleki''s Footwraps'), +(9, 2, 8, 1, 76, 11766, 'Phase 2 (Pre-Raid)', 'Warlock', 'Destruction', 'Wrists', 'Alliance', 'Flameweave Cuffs'), +(9, 2, 8, 2, 76, 11766, 'Phase 2 (Pre-Raid)', 'Warlock', 'Destruction', 'Wrists', 'Horde', 'Flameweave Cuffs'), +(9, 2, 9, 1, 76, 18407, 'Phase 2 (Pre-Raid)', 'Warlock', 'Destruction', 'Hands', 'Alliance', 'Felcloth Gloves'), +(9, 2, 9, 2, 76, 18407, 'Phase 2 (Pre-Raid)', 'Warlock', 'Destruction', 'Hands', 'Horde', 'Felcloth Gloves'), +(9, 2, 10, 1, 76, 12543, 'Phase 2 (Pre-Raid)', 'Warlock', 'Destruction', 'Finger1', 'Alliance', 'Songstone of Ironforge'), +(9, 2, 10, 2, 76, 12545, 'Phase 2 (Pre-Raid)', 'Warlock', 'Destruction', 'Finger1', 'Horde', 'Eye of Orgrimmar'), +(9, 2, 11, 1, 76, 13001, 'Phase 2 (Pre-Raid)', 'Warlock', 'Destruction', 'Finger2', 'Alliance', 'Maiden''s Circle'), +(9, 2, 11, 2, 76, 13001, 'Phase 2 (Pre-Raid)', 'Warlock', 'Destruction', 'Finger2', 'Horde', 'Maiden''s Circle'), +(9, 2, 12, 1, 76, 12930, 'Phase 2 (Pre-Raid)', 'Warlock', 'Destruction', 'Trinket1', 'Alliance', 'Briarwood Reed'), +(9, 2, 12, 2, 76, 12930, 'Phase 2 (Pre-Raid)', 'Warlock', 'Destruction', 'Trinket1', 'Horde', 'Briarwood Reed'), +(9, 2, 13, 1, 76, 18467, 'Phase 2 (Pre-Raid)', 'Warlock', 'Destruction', 'Trinket2', 'Alliance', 'Royal Seal of Eldre''Thalas'), +(9, 2, 13, 2, 76, 18467, 'Phase 2 (Pre-Raid)', 'Warlock', 'Destruction', 'Trinket2', 'Horde', 'Royal Seal of Eldre''Thalas'), +(9, 2, 14, 1, 76, 13386, 'Phase 2 (Pre-Raid)', 'Warlock', 'Destruction', 'Back', 'Alliance', 'Archivist Cape'), +(9, 2, 14, 2, 76, 13386, 'Phase 2 (Pre-Raid)', 'Warlock', 'Destruction', 'Back', 'Horde', 'Archivist Cape'), +(9, 2, 15, 1, 76, 18372, 'Phase 2 (Pre-Raid)', 'Warlock', 'Destruction', 'MainHand', 'Alliance', 'Blade of the New Moon'), +(9, 2, 15, 2, 76, 18372, 'Phase 2 (Pre-Raid)', 'Warlock', 'Destruction', 'MainHand', 'Horde', 'Blade of the New Moon'), +(9, 2, 16, 1, 76, 10796, 'Phase 2 (Pre-Raid)', 'Warlock', 'Destruction', 'OffHand', 'Alliance', 'Drakestone'), +(9, 2, 16, 2, 76, 10796, 'Phase 2 (Pre-Raid)', 'Warlock', 'Destruction', 'OffHand', 'Horde', 'Drakestone'), +(9, 2, 17, 1, 76, 13396, 'Phase 2 (Pre-Raid)', 'Warlock', 'Destruction', 'Ranged', 'Alliance', 'Skul''s Ghastly Touch'), +(9, 2, 17, 2, 76, 13396, 'Phase 2 (Pre-Raid)', 'Warlock', 'Destruction', 'Ranged', 'Horde', 'Skul''s Ghastly Touch'); + +-- ilvl 78 (Phase 2) +INSERT INTO `playerbots_bis_gear` VALUES +(9, 2, 0, 0, 78, 23310, 'Phase 2', 'Warlock', 'Destruction', 'Head', 'Both', 'Lieutenant Commander''s Dreadweave Cowl'), +(9, 2, 1, 0, 78, 18814, 'Phase 2', 'Warlock', 'Destruction', 'Neck', 'Both', 'Choker of the Fire Lord'), +(9, 2, 2, 0, 78, 23311, 'Phase 2', 'Warlock', 'Destruction', 'Shoulders', 'Both', 'Lieutenant Commander''s Dreadweave Spaulders'), +(9, 2, 4, 0, 78, 19145, 'Phase 2', 'Warlock', 'Destruction', 'Chest', 'Both', 'Robe of Volatile Power'), +(9, 2, 5, 0, 78, 18809, 'Phase 2', 'Warlock', 'Destruction', 'Waist', 'Both', 'Sash of Whispered Secrets'), +(9, 2, 6, 0, 78, 19133, 'Phase 2', 'Warlock', 'Destruction', 'Legs', 'Both', 'Fel Infused Leggings'), +(9, 2, 7, 0, 78, 19131, 'Phase 2', 'Warlock', 'Destruction', 'Feet', 'Both', 'Snowblind Shoes'), +(9, 2, 8, 0, 78, 11766, 'Phase 2', 'Warlock', 'Destruction', 'Wrists', 'Both', 'Flameweave Cuffs'), +(9, 2, 9, 0, 78, 18407, 'Phase 2', 'Warlock', 'Destruction', 'Hands', 'Both', 'Felcloth Gloves'), +(9, 2, 10, 0, 78, 19147, 'Phase 2', 'Warlock', 'Destruction', 'Finger1', 'Both', 'Ring of Spell Power'), +(9, 2, 11, 0, 78, 19147, 'Phase 2', 'Warlock', 'Destruction', 'Finger2', 'Both', 'Ring of Spell Power'), +(9, 2, 12, 0, 78, 12930, 'Phase 2', 'Warlock', 'Destruction', 'Trinket1', 'Both', 'Briarwood Reed'), +(9, 2, 13, 0, 78, 18820, 'Phase 2', 'Warlock', 'Destruction', 'Trinket2', 'Both', 'Talisman of Ephemeral Power'), +(9, 2, 14, 0, 78, 13386, 'Phase 2', 'Warlock', 'Destruction', 'Back', 'Both', 'Archivist Cape'), +(9, 2, 15, 0, 78, 17103, 'Phase 2', 'Warlock', 'Destruction', 'MainHand', 'Both', 'Azuresong Mageblade'), +(9, 2, 16, 0, 78, 10796, 'Phase 2', 'Warlock', 'Destruction', 'OffHand', 'Both', 'Drakestone'), +(9, 2, 17, 0, 78, 13396, 'Phase 2', 'Warlock', 'Destruction', 'Ranged', 'Both', 'Skul''s Ghastly Touch'); + +-- ilvl 83 (Phase 3) +INSERT INTO `playerbots_bis_gear` VALUES +(9, 2, 0, 0, 83, 19375, 'Phase 3', 'Warlock', 'Destruction', 'Head', 'Both', 'Mish''undare, Circlet of the Mind Flayer'), +(9, 2, 1, 0, 83, 18814, 'Phase 3', 'Warlock', 'Destruction', 'Neck', 'Both', 'Choker of the Fire Lord'), +(9, 2, 2, 0, 83, 19370, 'Phase 3', 'Warlock', 'Destruction', 'Shoulders', 'Both', 'Mantle of the Blackwing Cabal'), +(9, 2, 4, 0, 83, 19145, 'Phase 3', 'Warlock', 'Destruction', 'Chest', 'Both', 'Robe of Volatile Power'), +(9, 2, 5, 0, 83, 18809, 'Phase 3', 'Warlock', 'Destruction', 'Waist', 'Both', 'Sash of Whispered Secrets'), +(9, 2, 6, 0, 83, 19133, 'Phase 3', 'Warlock', 'Destruction', 'Legs', 'Both', 'Fel Infused Leggings'), +(9, 2, 7, 0, 83, 19131, 'Phase 3', 'Warlock', 'Destruction', 'Feet', 'Both', 'Snowblind Shoes'), +(9, 2, 8, 0, 83, 19374, 'Phase 3', 'Warlock', 'Destruction', 'Wrists', 'Both', 'Bracers of Arcane Accuracy'), +(9, 2, 9, 0, 83, 19407, 'Phase 3', 'Warlock', 'Destruction', 'Hands', 'Both', 'Ebony Flame Gloves'), +(9, 2, 11, 0, 83, 19434, 'Phase 3', 'Warlock', 'Destruction', 'Finger2', 'Both', 'Band of Dark Dominion'), +(9, 2, 12, 0, 83, 19379, 'Phase 3', 'Warlock', 'Destruction', 'Trinket1', 'Both', 'Neltharion''s Tear'), +(9, 2, 13, 0, 83, 18820, 'Phase 3', 'Warlock', 'Destruction', 'Trinket2', 'Both', 'Talisman of Ephemeral Power'), +(9, 2, 14, 0, 83, 19378, 'Phase 3', 'Warlock', 'Destruction', 'Back', 'Both', 'Cloak of the Brood Lord'), +(9, 2, 15, 0, 83, 19356, 'Phase 3', 'Warlock', 'Destruction', 'MainHand', 'Both', 'Staff of the Shadow Flame'), +(9, 2, 17, 0, 83, 13396, 'Phase 3', 'Warlock', 'Destruction', 'Ranged', 'Both', 'Skul''s Ghastly Touch'); + +-- ilvl 88 (Phase 5) +INSERT INTO `playerbots_bis_gear` VALUES +(9, 2, 0, 0, 88, 21337, 'Phase 5', 'Warlock', 'Destruction', 'Head', 'Both', 'Doomcaller''s Circlet'), +(9, 2, 1, 0, 88, 21608, 'Phase 5', 'Warlock', 'Destruction', 'Neck', 'Both', 'Amulet of Vek''nilash'), +(9, 2, 2, 0, 88, 21335, 'Phase 5', 'Warlock', 'Destruction', 'Shoulders', 'Both', 'Doomcaller''s Mantle'), +(9, 2, 4, 0, 88, 19682, 'Phase 5', 'Warlock', 'Destruction', 'Chest', 'Both', 'Bloodvine Vest'), +(9, 2, 5, 0, 88, 22730, 'Phase 5', 'Warlock', 'Destruction', 'Waist', 'Both', 'Eyestalk Waist Cord'), +(9, 2, 6, 0, 88, 19683, 'Phase 5', 'Warlock', 'Destruction', 'Legs', 'Both', 'Bloodvine Leggings'), +(9, 2, 7, 0, 88, 19684, 'Phase 5', 'Warlock', 'Destruction', 'Feet', 'Both', 'Bloodvine Boots'), +(9, 2, 8, 0, 88, 21186, 'Phase 5', 'Warlock', 'Destruction', 'Wrists', 'Both', 'Rockfury Bracers'), +(9, 2, 9, 0, 88, 21585, 'Phase 5', 'Warlock', 'Destruction', 'Hands', 'Both', 'Dark Storm Gauntlets'), +(9, 2, 10, 0, 88, 21417, 'Phase 5', 'Warlock', 'Destruction', 'Finger1', 'Both', 'Ring of Unspoken Names'), +(9, 2, 11, 0, 88, 21709, 'Phase 5', 'Warlock', 'Destruction', 'Finger2', 'Both', 'Ring of the Fallen God'), +(9, 2, 12, 0, 88, 19379, 'Phase 5', 'Warlock', 'Destruction', 'Trinket1', 'Both', 'Neltharion''s Tear'), +(9, 2, 13, 0, 88, 18820, 'Phase 5', 'Warlock', 'Destruction', 'Trinket2', 'Both', 'Talisman of Ephemeral Power'), +(9, 2, 14, 0, 88, 22731, 'Phase 5', 'Warlock', 'Destruction', 'Back', 'Both', 'Cloak of the Devoured'), +(9, 2, 15, 0, 88, 21622, 'Phase 5', 'Warlock', 'Destruction', 'MainHand', 'Both', 'Sharpened Silithid Femur'), +(9, 2, 16, 0, 88, 21597, 'Phase 5', 'Warlock', 'Destruction', 'OffHand', 'Both', 'Royal Scepter of Vek''lor'), +(9, 2, 17, 0, 88, 21603, 'Phase 5', 'Warlock', 'Destruction', 'Ranged', 'Both', 'Wand of Qiraji Nobility'); + +-- ilvl 92 (Phase 6) +INSERT INTO `playerbots_bis_gear` VALUES +(9, 2, 0, 0, 92, 22506, 'Phase 6', 'Warlock', 'Destruction', 'Head', 'Both', 'Plagueheart Circlet'), +(9, 2, 1, 0, 92, 21608, 'Phase 6', 'Warlock', 'Destruction', 'Neck', 'Both', 'Amulet of Vek''nilash'), +(9, 2, 2, 0, 92, 22507, 'Phase 6', 'Warlock', 'Destruction', 'Shoulders', 'Both', 'Plagueheart Shoulderpads'), +(9, 2, 4, 0, 92, 22504, 'Phase 6', 'Warlock', 'Destruction', 'Chest', 'Both', 'Plagueheart Robe'), +(9, 2, 5, 0, 92, 22730, 'Phase 6', 'Warlock', 'Destruction', 'Waist', 'Both', 'Eyestalk Waist Cord'), +(9, 2, 6, 0, 92, 23070, 'Phase 6', 'Warlock', 'Destruction', 'Legs', 'Both', 'Leggings of Polarity'), +(9, 2, 7, 0, 92, 22508, 'Phase 6', 'Warlock', 'Destruction', 'Feet', 'Both', 'Plagueheart Sandals'), +(9, 2, 8, 0, 92, 21186, 'Phase 6', 'Warlock', 'Destruction', 'Wrists', 'Both', 'Rockfury Bracers'), +(9, 2, 9, 0, 92, 21585, 'Phase 6', 'Warlock', 'Destruction', 'Hands', 'Both', 'Dark Storm Gauntlets'), +(9, 2, 10, 0, 92, 23031, 'Phase 6', 'Warlock', 'Destruction', 'Finger1', 'Both', 'Band of the Inevitable'), +(9, 2, 11, 0, 92, 21709, 'Phase 6', 'Warlock', 'Destruction', 'Finger2', 'Both', 'Ring of the Fallen God'), +(9, 2, 12, 0, 92, 19379, 'Phase 6', 'Warlock', 'Destruction', 'Trinket1', 'Both', 'Neltharion''s Tear'), +(9, 2, 13, 0, 92, 23046, 'Phase 6', 'Warlock', 'Destruction', 'Trinket2', 'Both', 'The Restrained Essence of Sapphiron'), +(9, 2, 14, 0, 92, 23050, 'Phase 6', 'Warlock', 'Destruction', 'Back', 'Both', 'Cloak of the Necropolis'), +(9, 2, 15, 0, 92, 22807, 'Phase 6', 'Warlock', 'Destruction', 'MainHand', 'Both', 'Wraith Blade'), +(9, 2, 16, 0, 92, 23049, 'Phase 6', 'Warlock', 'Destruction', 'OffHand', 'Both', 'Sapphiron''s Left Eye'), +(9, 2, 17, 0, 92, 22820, 'Phase 6', 'Warlock', 'Destruction', 'Ranged', 'Both', 'Wand of Fates'); + +-- ilvl 120 (Pre-Raid) +INSERT INTO `playerbots_bis_gear` VALUES +(9, 2, 0, 0, 120, 24266, 'Pre-Raid', 'Warlock', 'Destruction', 'Head', 'Both', 'Spellstrike Hood'), +(9, 2, 1, 0, 120, 28134, 'Pre-Raid', 'Warlock', 'Destruction', 'Neck', 'Both', 'Brooch of Heightened Potential'), +(9, 2, 2, 0, 120, 21869, 'Pre-Raid', 'Warlock', 'Destruction', 'Shoulders', 'Both', 'Frozen Shadoweave Shoulders'), +(9, 2, 4, 0, 120, 21871, 'Pre-Raid', 'Warlock', 'Destruction', 'Chest', 'Both', 'Frozen Shadoweave Robe'), +(9, 2, 5, 0, 120, 24256, 'Pre-Raid', 'Warlock', 'Destruction', 'Waist', 'Both', 'Girdle of Ruination'), +(9, 2, 6, 0, 120, 24262, 'Pre-Raid', 'Warlock', 'Destruction', 'Legs', 'Both', 'Spellstrike Pants'), +(9, 2, 7, 0, 120, 21870, 'Pre-Raid', 'Warlock', 'Destruction', 'Feet', 'Both', 'Frozen Shadoweave Boots'), +(9, 2, 8, 0, 120, 24250, 'Pre-Raid', 'Warlock', 'Destruction', 'Wrists', 'Both', 'Bracers of Havok'), +(9, 2, 9, 0, 120, 24450, 'Pre-Raid', 'Warlock', 'Destruction', 'Hands', 'Both', 'Manaspark Gloves'), +(9, 2, 10, 0, 120, 29172, 'Pre-Raid', 'Warlock', 'Destruction', 'Finger1', 'Both', 'Ashyen''s Gift'), +(9, 2, 11, 0, 120, 28227, 'Pre-Raid', 'Warlock', 'Destruction', 'Finger2', 'Both', 'Sparking Arcanite Ring'), +(9, 2, 12, 0, 120, 29370, 'Pre-Raid', 'Warlock', 'Destruction', 'Trinket1', 'Both', 'Icon of the Silver Crescent'), +(9, 2, 13, 0, 120, 29132, 'Pre-Raid', 'Warlock', 'Destruction', 'Trinket2', 'Both', 'Scryer''s Bloodgem'), +(9, 2, 14, 0, 120, 27981, 'Pre-Raid', 'Warlock', 'Destruction', 'Back', 'Both', 'Sethekk Oracle Cloak'), +(9, 2, 15, 0, 120, 23554, 'Pre-Raid', 'Warlock', 'Destruction', 'MainHand', 'Both', 'Eternium Runed Blade'), +(9, 2, 16, 0, 120, 29272, 'Pre-Raid', 'Warlock', 'Destruction', 'OffHand', 'Both', 'Orb of the Soul-Eater'), +(9, 2, 17, 0, 120, 29350, 'Pre-Raid', 'Warlock', 'Destruction', 'Ranged', 'Both', 'The Black Stalk'); + +-- ilvl 125 (Phase 1) +INSERT INTO `playerbots_bis_gear` VALUES +(9, 2, 0, 0, 125, 28963, 'Phase 1', 'Warlock', 'Destruction', 'Head', 'Both', 'Voidheart Crown'), +(9, 2, 1, 0, 125, 28530, 'Phase 1', 'Warlock', 'Destruction', 'Neck', 'Both', 'Brooch of Unquenchable Fury'), +(9, 2, 2, 0, 125, 28967, 'Phase 1', 'Warlock', 'Destruction', 'Shoulders', 'Both', 'Voidheart Mantle'), +(9, 2, 4, 0, 125, 28964, 'Phase 1', 'Warlock', 'Destruction', 'Chest', 'Both', 'Voidheart Robe'), +(9, 2, 5, 0, 125, 24256, 'Phase 1', 'Warlock', 'Destruction', 'Waist', 'Both', 'Girdle of Ruination'), +(9, 2, 6, 0, 125, 30734, 'Phase 1', 'Warlock', 'Destruction', 'Legs', 'Both', 'Leggings of the Seventh Circle'), +(9, 2, 7, 0, 125, 21870, 'Phase 1', 'Warlock', 'Destruction', 'Feet', 'Both', 'Frozen Shadoweave Boots'), +(9, 2, 8, 0, 125, 24250, 'Phase 1', 'Warlock', 'Destruction', 'Wrists', 'Both', 'Bracers of Havok'), +(9, 2, 9, 0, 125, 28968, 'Phase 1', 'Warlock', 'Destruction', 'Hands', 'Both', 'Voidheart Gloves'), +(9, 2, 10, 0, 125, 29172, 'Phase 1', 'Warlock', 'Destruction', 'Finger1', 'Both', 'Ashyen''s Gift'), +(9, 2, 11, 0, 125, 28793, 'Phase 1', 'Warlock', 'Destruction', 'Finger2', 'Both', 'Band of Crimson Fury'), +(9, 2, 12, 0, 125, 29370, 'Phase 1', 'Warlock', 'Destruction', 'Trinket1', 'Both', 'Icon of the Silver Crescent'), +(9, 2, 13, 0, 125, 27683, 'Phase 1', 'Warlock', 'Destruction', 'Trinket2', 'Both', 'Quagmirran''s Eye'), +(9, 2, 14, 0, 125, 28766, 'Phase 1', 'Warlock', 'Destruction', 'Back', 'Both', 'Ruby Drape of the Mysticant'), +(9, 2, 15, 0, 125, 30723, 'Phase 1', 'Warlock', 'Destruction', 'MainHand', 'Both', 'Talon of the Tempest'), +(9, 2, 16, 0, 125, 29272, 'Phase 1', 'Warlock', 'Destruction', 'OffHand', 'Both', 'Orb of the Soul-Eater'), +(9, 2, 17, 0, 125, 28783, 'Phase 1', 'Warlock', 'Destruction', 'Ranged', 'Both', 'Eredar Wand of Obliteration'); + +-- ilvl 141 (Phase 2) +INSERT INTO `playerbots_bis_gear` VALUES +(9, 2, 0, 0, 141, 32494, 'Phase 2', 'Warlock', 'Destruction', 'Head', 'Both', 'Destruction Holo-gogs'), +(9, 2, 1, 0, 141, 30015, 'Phase 2', 'Warlock', 'Destruction', 'Neck', 'Both', 'The Sun King''s Talisman'), +(9, 2, 2, 0, 141, 28967, 'Phase 2', 'Warlock', 'Destruction', 'Shoulders', 'Both', 'Voidheart Mantle'), +(9, 2, 4, 0, 141, 30107, 'Phase 2', 'Warlock', 'Destruction', 'Chest', 'Both', 'Vestments of the Sea-Witch'), +(9, 2, 5, 0, 141, 30038, 'Phase 2', 'Warlock', 'Destruction', 'Waist', 'Both', 'Belt of Blasting'), +(9, 2, 6, 0, 141, 30213, 'Phase 2', 'Warlock', 'Destruction', 'Legs', 'Both', 'Leggings of the Corruptor'), +(9, 2, 7, 0, 141, 30037, 'Phase 2', 'Warlock', 'Destruction', 'Feet', 'Both', 'Boots of Blasting'), +(9, 2, 8, 0, 141, 29918, 'Phase 2', 'Warlock', 'Destruction', 'Wrists', 'Both', 'Mindstorm Wristbands'), +(9, 2, 9, 0, 141, 28968, 'Phase 2', 'Warlock', 'Destruction', 'Hands', 'Both', 'Voidheart Gloves'), +(9, 2, 10, 0, 141, 29302, 'Phase 2', 'Warlock', 'Destruction', 'Finger1', 'Both', 'Band of Eternity'), +(9, 2, 11, 0, 141, 30109, 'Phase 2', 'Warlock', 'Destruction', 'Finger2', 'Both', 'Ring of Endless Coils'), +(9, 2, 12, 0, 141, 29370, 'Phase 2', 'Warlock', 'Destruction', 'Trinket1', 'Both', 'Icon of the Silver Crescent'), +(9, 2, 13, 0, 141, 27683, 'Phase 2', 'Warlock', 'Destruction', 'Trinket2', 'Both', 'Quagmirran''s Eye'), +(9, 2, 14, 0, 141, 28766, 'Phase 2', 'Warlock', 'Destruction', 'Back', 'Both', 'Ruby Drape of the Mysticant'), +(9, 2, 15, 0, 141, 30095, 'Phase 2', 'Warlock', 'Destruction', 'MainHand', 'Both', 'Fang of the Leviathan'), +(9, 2, 16, 0, 141, 30049, 'Phase 2', 'Warlock', 'Destruction', 'OffHand', 'Both', 'Fathomstone'), +(9, 2, 17, 0, 141, 29982, 'Phase 2', 'Warlock', 'Destruction', 'Ranged', 'Both', 'Wand of the Forgotten Star'); + +-- ilvl 156 (Phase 3) +INSERT INTO `playerbots_bis_gear` VALUES +(9, 2, 0, 0, 156, 31051, 'Phase 3', 'Warlock', 'Destruction', 'Head', 'Both', 'Hood of the Malefic'), +(9, 2, 1, 0, 156, 32589, 'Phase 3', 'Warlock', 'Destruction', 'Neck', 'Both', 'Hellfire-Encased Pendant'), +(9, 2, 2, 0, 156, 31054, 'Phase 3', 'Warlock', 'Destruction', 'Shoulders', 'Both', 'Mantle of the Malefic'), +(9, 2, 4, 0, 156, 30107, 'Phase 3', 'Warlock', 'Destruction', 'Chest', 'Both', 'Vestments of the Sea-Witch'), +(9, 2, 5, 0, 156, 30888, 'Phase 3', 'Warlock', 'Destruction', 'Waist', 'Both', 'Anetheron''s Noose'), +(9, 2, 6, 0, 156, 31053, 'Phase 3', 'Warlock', 'Destruction', 'Legs', 'Both', 'Leggings of the Malefic'), +(9, 2, 7, 0, 156, 32239, 'Phase 3', 'Warlock', 'Destruction', 'Feet', 'Both', 'Slippers of the Seacaller'), +(9, 2, 8, 0, 156, 32586, 'Phase 3', 'Warlock', 'Destruction', 'Wrists', 'Both', 'Bracers of Nimble Thought'), +(9, 2, 9, 0, 156, 31050, 'Phase 3', 'Warlock', 'Destruction', 'Hands', 'Both', 'Gloves of the Malefic'), +(9, 2, 10, 0, 156, 32527, 'Phase 3', 'Warlock', 'Destruction', 'Finger1', 'Both', 'Ring of Ancient Knowledge'), +(9, 2, 11, 0, 156, 32247, 'Phase 3', 'Warlock', 'Destruction', 'Finger2', 'Both', 'Ring of Captured Storms'), +(9, 2, 12, 0, 156, 29370, 'Phase 3', 'Warlock', 'Destruction', 'Trinket1', 'Both', 'Icon of the Silver Crescent'), +(9, 2, 13, 0, 156, 32483, 'Phase 3', 'Warlock', 'Destruction', 'Trinket2', 'Both', 'The Skull of Gul''dan'), +(9, 2, 14, 0, 156, 32590, 'Phase 3', 'Warlock', 'Destruction', 'Back', 'Both', 'Nethervoid Cloak'), +(9, 2, 15, 0, 156, 32374, 'Phase 3', 'Warlock', 'Destruction', 'MainHand', 'Both', 'Zhar''doom, Greatstaff of the Devourer'), +(9, 2, 17, 0, 156, 29982, 'Phase 3', 'Warlock', 'Destruction', 'Ranged', 'Both', 'Wand of the Forgotten Star'); + +-- ilvl 164 (Phase 4) +INSERT INTO `playerbots_bis_gear` VALUES +(9, 2, 0, 0, 164, 31051, 'Phase 4', 'Warlock', 'Destruction', 'Head', 'Both', 'Hood of the Malefic'), +(9, 2, 1, 0, 164, 32589, 'Phase 4', 'Warlock', 'Destruction', 'Neck', 'Both', 'Hellfire-Encased Pendant'), +(9, 2, 2, 0, 164, 31054, 'Phase 4', 'Warlock', 'Destruction', 'Shoulders', 'Both', 'Mantle of the Malefic'), +(9, 2, 4, 0, 164, 30107, 'Phase 4', 'Warlock', 'Destruction', 'Chest', 'Both', 'Vestments of the Sea-Witch'), +(9, 2, 5, 0, 164, 30888, 'Phase 4', 'Warlock', 'Destruction', 'Waist', 'Both', 'Anetheron''s Noose'), +(9, 2, 6, 0, 164, 31053, 'Phase 4', 'Warlock', 'Destruction', 'Legs', 'Both', 'Leggings of the Malefic'), +(9, 2, 7, 0, 164, 32239, 'Phase 4', 'Warlock', 'Destruction', 'Feet', 'Both', 'Slippers of the Seacaller'), +(9, 2, 8, 0, 164, 32586, 'Phase 4', 'Warlock', 'Destruction', 'Wrists', 'Both', 'Bracers of Nimble Thought'), +(9, 2, 9, 0, 164, 31050, 'Phase 4', 'Warlock', 'Destruction', 'Hands', 'Both', 'Gloves of the Malefic'), +(9, 2, 10, 0, 164, 33497, 'Phase 4', 'Warlock', 'Destruction', 'Finger1', 'Both', 'Mana Attuned Band'), +(9, 2, 11, 0, 164, 32247, 'Phase 4', 'Warlock', 'Destruction', 'Finger2', 'Both', 'Ring of Captured Storms'), +(9, 2, 12, 0, 164, 33829, 'Phase 4', 'Warlock', 'Destruction', 'Trinket1', 'Both', 'Hex Shrunken Head'), +(9, 2, 13, 0, 164, 32483, 'Phase 4', 'Warlock', 'Destruction', 'Trinket2', 'Both', 'The Skull of Gul''dan'), +(9, 2, 14, 0, 164, 32590, 'Phase 4', 'Warlock', 'Destruction', 'Back', 'Both', 'Nethervoid Cloak'), +(9, 2, 15, 0, 164, 32374, 'Phase 4', 'Warlock', 'Destruction', 'MainHand', 'Both', 'Zhar''doom, Greatstaff of the Devourer'), +(9, 2, 16, 0, 164, 34179, 'Phase 5', 'Warlock', 'Destruction', 'OffHand', 'Both', 'Heart of the Pit'), +(9, 2, 17, 0, 164, 33192, 'Phase 4', 'Warlock', 'Destruction', 'Ranged', 'Both', 'Carved Witch Doctor''s Stick'); + +-- ilvl 200 (Pre-Raid) +INSERT INTO `playerbots_bis_gear` VALUES +(9, 2, 0, 0, 200, 42553, 'Pre-Raid', 'Warlock', 'Destruction', 'Head', 'Both', 'Visage Liquification Goggles'), +(9, 2, 1, 0, 200, 39472, 'Pre-Raid', 'Warlock', 'Destruction', 'Neck', 'Both', 'Chain of Latent Energies'), +(9, 2, 2, 0, 200, 34393, 'Pre-Raid', 'Warlock', 'Destruction', 'Shoulders', 'Both', 'Shoulderpads of Knowledge''s Pursuit'), +(9, 2, 4, 0, 200, 39497, 'Pre-Raid', 'Warlock', 'Destruction', 'Chest', 'Both', 'Heroes'' Plagueheart Robe'), +(9, 2, 5, 0, 200, 40696, 'Pre-Raid', 'Warlock', 'Destruction', 'Waist', 'Both', 'Plush Sash of Guzbah'), +(9, 2, 6, 0, 200, 34170, 'Pre-Raid', 'Warlock', 'Destruction', 'Legs', 'Both', 'Pantaloons of Calming Strife'), +(9, 2, 7, 0, 200, 40558, 'Pre-Raid', 'Warlock', 'Destruction', 'Feet', 'Both', 'Arcanic Tramplers'), +(9, 2, 8, 0, 200, 37361, 'Pre-Raid', 'Warlock', 'Destruction', 'Wrists', 'Both', 'Cuffs of Winged Levitation'), +(9, 2, 9, 0, 200, 39500, 'Pre-Raid', 'Warlock', 'Destruction', 'Hands', 'Both', 'Heroes'' Plagueheart Gloves'), +(9, 2, 10, 0, 200, 43253, 'Pre-Raid', 'Warlock', 'Destruction', 'Finger1', 'Both', 'Ring of Northern Tears'), +(9, 2, 12, 0, 200, 40682, 'Pre-Raid', 'Warlock', 'Destruction', 'Trinket1', 'Both', 'Sundial of the Exiled'), +(9, 2, 14, 0, 200, 41610, 'Pre-Raid', 'Warlock', 'Destruction', 'Back', 'Both', 'Deathchill Cloak'), +(9, 2, 17, 0, 200, 34348, 'Pre-Raid', 'Warlock', 'Destruction', 'Ranged', 'Both', 'Wand of Cleansing Light'); + +-- ilvl 224 (Phase 1) +INSERT INTO `playerbots_bis_gear` VALUES +(9, 2, 0, 0, 224, 40421, 'Phase 1', 'Warlock', 'Destruction', 'Head', 'Both', 'Valorous Plagueheart Circlet'), +(9, 2, 1, 0, 224, 44661, 'Phase 1', 'Warlock', 'Destruction', 'Neck', 'Both', 'Wyrmrest Necklace of Power'), +(9, 2, 2, 0, 224, 40424, 'Phase 1', 'Warlock', 'Destruction', 'Shoulders', 'Both', 'Valorous Plagueheart Shoulderpads'), +(9, 2, 4, 0, 224, 40423, 'Phase 1', 'Warlock', 'Destruction', 'Chest', 'Both', 'Valorous Plagueheart Robe'), +(9, 2, 5, 0, 224, 40561, 'Phase 1', 'Warlock', 'Destruction', 'Waist', 'Both', 'Leash of Heedless Magic'), +(9, 2, 6, 0, 224, 40560, 'Phase 1', 'Warlock', 'Destruction', 'Legs', 'Both', 'Leggings of the Wanton Spellcaster'), +(9, 2, 7, 0, 224, 40558, 'Phase 1', 'Warlock', 'Destruction', 'Feet', 'Both', 'Arcanic Tramplers'), +(9, 2, 8, 0, 224, 44008, 'Phase 1', 'Warlock', 'Destruction', 'Wrists', 'Both', 'Unsullied Cuffs'), +(9, 2, 9, 0, 224, 40420, 'Phase 1', 'Warlock', 'Destruction', 'Hands', 'Both', 'Valorous Plagueheart Gloves'), +(9, 2, 10, 0, 224, 40399, 'Phase 1', 'Warlock', 'Destruction', 'Finger1', 'Both', 'Signet of Manifested Pain'), +(9, 2, 12, 0, 224, 40432, 'Phase 1', 'Warlock', 'Destruction', 'Trinket1', 'Both', 'Illustration of the Dragon Soul'), +(9, 2, 14, 0, 224, 44005, 'Phase 1', 'Warlock', 'Destruction', 'Back', 'Both', 'Pennant Cloak'), +(9, 2, 15, 0, 224, 40396, 'Phase 1', 'Warlock', 'Destruction', 'MainHand', 'Both', 'The Turning Tide'), +(9, 2, 17, 0, 224, 39712, 'Phase 1', 'Warlock', 'Destruction', 'Ranged', 'Both', 'Gemmed Wand of the Nerubians'); + +-- ilvl 245 (Phase 2) +INSERT INTO `playerbots_bis_gear` VALUES +(9, 2, 0, 0, 245, 45497, 'Phase 2', 'Warlock', 'Destruction', 'Head', 'Both', 'Crown of Luminescence'), +(9, 2, 1, 0, 245, 45133, 'Phase 2', 'Warlock', 'Destruction', 'Neck', 'Both', 'Pendant of Fiery Havoc'), +(9, 2, 2, 0, 245, 46068, 'Phase 2', 'Warlock', 'Destruction', 'Shoulders', 'Both', 'Amice of Inconceivable Horror'), +(9, 2, 4, 0, 245, 46137, 'Phase 2', 'Warlock', 'Destruction', 'Chest', 'Both', 'Conqueror''s Deathbringer Robe'), +(9, 2, 9, 0, 245, 45665, 'Phase 2', 'Warlock', 'Destruction', 'Hands', 'Both', 'Pharos Gloves'), +(9, 2, 10, 0, 245, 45495, 'Phase 2', 'Warlock', 'Destruction', 'Finger1', 'Both', 'Conductive Seal'), +(9, 2, 14, 0, 245, 45618, 'Phase 2', 'Warlock', 'Destruction', 'Back', 'Both', 'Sunglimmer Cloak'), +(9, 2, 15, 0, 245, 45620, 'Phase 2', 'Warlock', 'Destruction', 'MainHand', 'Both', 'Starshard Edge'), +(9, 2, 17, 0, 245, 45294, 'Phase 2', 'Warlock', 'Destruction', 'Ranged', 'Both', 'Petrified Ivy Sprig'); + +-- ilvl 258 (Phase 3) +INSERT INTO `playerbots_bis_gear` VALUES +(9, 2, 10, 0, 258, 45495, 'Phase 3', 'Warlock', 'Destruction', 'Finger1', 'Both', 'Conductive Seal'), +(9, 2, 17, 0, 258, 45294, 'Phase 3', 'Warlock', 'Destruction', 'Ranged', 'Both', 'Petrified Ivy Sprig'); + +-- ilvl 290 (Phase 5) +INSERT INTO `playerbots_bis_gear` VALUES +(9, 2, 0, 0, 290, 51231, 'Phase 5', 'Warlock', 'Destruction', 'Head', 'Both', 'Sanctified Dark Coven Hood'), +(9, 2, 1, 0, 290, 50182, 'Phase 5', 'Warlock', 'Destruction', 'Neck', 'Both', 'Blood Queen''s Crimson Choker'), +(9, 2, 2, 0, 290, 51234, 'Phase 5', 'Warlock', 'Destruction', 'Shoulders', 'Both', 'Sanctified Dark Coven Shoulderpads'), +(9, 2, 4, 0, 290, 51233, 'Phase 5', 'Warlock', 'Destruction', 'Chest', 'Both', 'Sanctified Dark Coven Robe'), +(9, 2, 5, 0, 290, 50613, 'Phase 5', 'Warlock', 'Destruction', 'Waist', 'Both', 'Crushing Coldwraith Belt'), +(9, 2, 6, 0, 290, 50694, 'Phase 5', 'Warlock', 'Destruction', 'Legs', 'Both', 'Plaguebringer''s Stained Pants'), +(9, 2, 7, 0, 290, 50699, 'Phase 5', 'Warlock', 'Destruction', 'Feet', 'Both', 'Plague Scientist''s Boots'), +(9, 2, 8, 0, 290, 54582, 'Phase 5', 'Warlock', 'Destruction', 'Wrists', 'Both', 'Bracers of Fiery Night'), +(9, 2, 9, 0, 290, 51230, 'Phase 5', 'Warlock', 'Destruction', 'Hands', 'Both', 'Sanctified Dark Coven Gloves'), +(9, 2, 10, 0, 290, 50614, 'Phase 5', 'Warlock', 'Destruction', 'Finger1', 'Both', 'Loop of the Endless Labyrinth'), +(9, 2, 11, 0, 290, 50664, 'Phase 5', 'Warlock', 'Destruction', 'Finger2', 'Both', 'Ring of Rapid Ascent'), +(9, 2, 12, 0, 290, 54588, 'Phase 5', 'Warlock', 'Destruction', 'Trinket1', 'Both', 'Charred Twilight Scale'), +(9, 2, 13, 0, 290, 50365, 'Phase 5', 'Warlock', 'Destruction', 'Trinket2', 'Both', 'Phylactery of the Nameless Lich'), +(9, 2, 14, 0, 290, 54583, 'Phase 5', 'Warlock', 'Destruction', 'Back', 'Both', 'Cloak of Burning Dusk'), +(9, 2, 15, 0, 290, 50732, 'Phase 5', 'Warlock', 'Destruction', 'MainHand', 'Both', 'Bloodsurge, Kel''Thuzad''s Blade of Agony'), +(9, 2, 16, 0, 290, 50719, 'Phase 5', 'Warlock', 'Destruction', 'OffHand', 'Both', 'Shadow Silk Spindle'), +(9, 2, 17, 0, 290, 50684, 'Phase 5', 'Warlock', 'Destruction', 'Ranged', 'Both', 'Corpse-Impaling Spike'); + + +-- ============================================================ +-- Druid (11) +-- ============================================================ +-- Balance (tab 0) +-- ilvl 66 (Phase 1 (Pre-Raid)) +INSERT INTO `playerbots_bis_gear` VALUES +(11, 0, 0, 1, 66, 10504, 'Phase 1 (Pre-Raid)', 'Druid', 'Balance', 'Head', 'Alliance', 'Green Lens'), +(11, 0, 0, 2, 66, 10504, 'Phase 1 (Pre-Raid)', 'Druid', 'Balance', 'Head', 'Horde', 'Green Lens'), +(11, 0, 1, 1, 66, 12103, 'Phase 1 (Pre-Raid)', 'Druid', 'Balance', 'Neck', 'Alliance', 'Star of Mystaria'), +(11, 0, 1, 2, 66, 12103, 'Phase 1 (Pre-Raid)', 'Druid', 'Balance', 'Neck', 'Horde', 'Star of Mystaria'), +(11, 0, 2, 1, 66, 13013, 'Phase 1 (Pre-Raid)', 'Druid', 'Balance', 'Shoulders', 'Alliance', 'Elder Wizard''s Mantle'), +(11, 0, 2, 2, 66, 13013, 'Phase 1 (Pre-Raid)', 'Druid', 'Balance', 'Shoulders', 'Horde', 'Elder Wizard''s Mantle'), +(11, 0, 4, 1, 66, 11924, 'Phase 1 (Pre-Raid)', 'Druid', 'Balance', 'Chest', 'Alliance', 'Robes of the Royal Crown'), +(11, 0, 4, 2, 66, 11924, 'Phase 1 (Pre-Raid)', 'Druid', 'Balance', 'Chest', 'Horde', 'Robes of the Royal Crown'), +(11, 0, 5, 1, 66, 11662, 'Phase 1 (Pre-Raid)', 'Druid', 'Balance', 'Waist', 'Alliance', 'Ban''thok Sash'), +(11, 0, 5, 2, 66, 11662, 'Phase 1 (Pre-Raid)', 'Druid', 'Balance', 'Waist', 'Horde', 'Ban''thok Sash'), +(11, 0, 6, 1, 66, 13170, 'Phase 1 (Pre-Raid)', 'Druid', 'Balance', 'Legs', 'Alliance', 'Skyshroud Leggings'), +(11, 0, 6, 2, 66, 13170, 'Phase 1 (Pre-Raid)', 'Druid', 'Balance', 'Legs', 'Horde', 'Skyshroud Leggings'), +(11, 0, 7, 1, 66, 11822, 'Phase 1 (Pre-Raid)', 'Druid', 'Balance', 'Feet', 'Alliance', 'Omnicast Boots'), +(11, 0, 7, 2, 66, 11822, 'Phase 1 (Pre-Raid)', 'Druid', 'Balance', 'Feet', 'Horde', 'Omnicast Boots'), +(11, 0, 8, 1, 66, 11766, 'Phase 1 (Pre-Raid)', 'Druid', 'Balance', 'Wrists', 'Alliance', 'Flameweave Cuffs'), +(11, 0, 8, 2, 66, 11766, 'Phase 1 (Pre-Raid)', 'Druid', 'Balance', 'Wrists', 'Horde', 'Flameweave Cuffs'), +(11, 0, 9, 1, 66, 13258, 'Phase 1 (Pre-Raid)', 'Druid', 'Balance', 'Hands', 'Alliance', 'Slaghide Gauntlets'), +(11, 0, 9, 2, 66, 13258, 'Phase 1 (Pre-Raid)', 'Druid', 'Balance', 'Hands', 'Horde', 'Slaghide Gauntlets'), +(11, 0, 10, 1, 66, 12543, 'Phase 1 (Pre-Raid)', 'Druid', 'Balance', 'Finger1', 'Alliance', 'Songstone of Ironforge'), +(11, 0, 10, 2, 66, 12545, 'Phase 1 (Pre-Raid)', 'Druid', 'Balance', 'Finger1', 'Horde', 'Eye of Orgrimmar'), +(11, 0, 11, 1, 66, 13001, 'Phase 1 (Pre-Raid)', 'Druid', 'Balance', 'Finger2', 'Alliance', 'Maiden''s Circle'), +(11, 0, 11, 2, 66, 13001, 'Phase 1 (Pre-Raid)', 'Druid', 'Balance', 'Finger2', 'Horde', 'Maiden''s Circle'), +(11, 0, 12, 1, 66, 12930, 'Phase 1 (Pre-Raid)', 'Druid', 'Balance', 'Trinket1', 'Alliance', 'Briarwood Reed'), +(11, 0, 12, 2, 66, 12930, 'Phase 1 (Pre-Raid)', 'Druid', 'Balance', 'Trinket1', 'Horde', 'Briarwood Reed'), +(11, 0, 13, 1, 66, 13968, 'Phase 1 (Pre-Raid)', 'Druid', 'Balance', 'Trinket2', 'Alliance', 'Eye of the Beast'), +(11, 0, 13, 2, 66, 13968, 'Phase 1 (Pre-Raid)', 'Druid', 'Balance', 'Trinket2', 'Horde', 'Eye of the Beast'), +(11, 0, 14, 1, 66, 13386, 'Phase 1 (Pre-Raid)', 'Druid', 'Balance', 'Back', 'Alliance', 'Archivist Cape'), +(11, 0, 14, 2, 66, 13386, 'Phase 1 (Pre-Raid)', 'Druid', 'Balance', 'Back', 'Horde', 'Archivist Cape'), +(11, 0, 15, 1, 66, 13964, 'Phase 1 (Pre-Raid)', 'Druid', 'Balance', 'MainHand', 'Alliance', 'Witchblade'), +(11, 0, 15, 2, 66, 13964, 'Phase 1 (Pre-Raid)', 'Druid', 'Balance', 'MainHand', 'Horde', 'Witchblade'), +(11, 0, 16, 1, 66, 10796, 'Phase 1 (Pre-Raid)', 'Druid', 'Balance', 'OffHand', 'Alliance', 'Drakestone'), +(11, 0, 16, 2, 66, 10796, 'Phase 1 (Pre-Raid)', 'Druid', 'Balance', 'OffHand', 'Horde', 'Drakestone'); + +-- ilvl 76 (Phase 2 (Pre-Raid)) +INSERT INTO `playerbots_bis_gear` VALUES +(11, 0, 0, 1, 76, 10504, 'Phase 2 (Pre-Raid)', 'Druid', 'Balance', 'Head', 'Alliance', 'Green Lens'), +(11, 0, 0, 2, 76, 10504, 'Phase 2 (Pre-Raid)', 'Druid', 'Balance', 'Head', 'Horde', 'Green Lens'), +(11, 0, 1, 1, 76, 12103, 'Phase 2 (Pre-Raid)', 'Druid', 'Balance', 'Neck', 'Alliance', 'Star of Mystaria'), +(11, 0, 1, 2, 76, 12103, 'Phase 2 (Pre-Raid)', 'Druid', 'Balance', 'Neck', 'Horde', 'Star of Mystaria'), +(11, 0, 2, 1, 76, 13013, 'Phase 2 (Pre-Raid)', 'Druid', 'Balance', 'Shoulders', 'Alliance', 'Elder Wizard''s Mantle'), +(11, 0, 2, 2, 76, 13013, 'Phase 2 (Pre-Raid)', 'Druid', 'Balance', 'Shoulders', 'Horde', 'Elder Wizard''s Mantle'), +(11, 0, 4, 1, 76, 18385, 'Phase 2 (Pre-Raid)', 'Druid', 'Balance', 'Chest', 'Alliance', 'Robe of Everlasting Night'), +(11, 0, 4, 2, 76, 18385, 'Phase 2 (Pre-Raid)', 'Druid', 'Balance', 'Chest', 'Horde', 'Robe of Everlasting Night'), +(11, 0, 5, 1, 76, 11662, 'Phase 2 (Pre-Raid)', 'Druid', 'Balance', 'Waist', 'Alliance', 'Ban''thok Sash'), +(11, 0, 5, 2, 76, 11662, 'Phase 2 (Pre-Raid)', 'Druid', 'Balance', 'Waist', 'Horde', 'Ban''thok Sash'), +(11, 0, 6, 1, 76, 13170, 'Phase 2 (Pre-Raid)', 'Druid', 'Balance', 'Legs', 'Alliance', 'Skyshroud Leggings'), +(11, 0, 6, 2, 76, 13170, 'Phase 2 (Pre-Raid)', 'Druid', 'Balance', 'Legs', 'Horde', 'Skyshroud Leggings'), +(11, 0, 7, 1, 76, 11822, 'Phase 2 (Pre-Raid)', 'Druid', 'Balance', 'Feet', 'Alliance', 'Omnicast Boots'), +(11, 0, 7, 2, 76, 11822, 'Phase 2 (Pre-Raid)', 'Druid', 'Balance', 'Feet', 'Horde', 'Omnicast Boots'), +(11, 0, 8, 1, 76, 11766, 'Phase 2 (Pre-Raid)', 'Druid', 'Balance', 'Wrists', 'Alliance', 'Flameweave Cuffs'), +(11, 0, 8, 2, 76, 11766, 'Phase 2 (Pre-Raid)', 'Druid', 'Balance', 'Wrists', 'Horde', 'Flameweave Cuffs'), +(11, 0, 9, 1, 76, 13258, 'Phase 2 (Pre-Raid)', 'Druid', 'Balance', 'Hands', 'Alliance', 'Slaghide Gauntlets'), +(11, 0, 9, 2, 76, 13258, 'Phase 2 (Pre-Raid)', 'Druid', 'Balance', 'Hands', 'Horde', 'Slaghide Gauntlets'), +(11, 0, 10, 1, 76, 12543, 'Phase 2 (Pre-Raid)', 'Druid', 'Balance', 'Finger1', 'Alliance', 'Songstone of Ironforge'), +(11, 0, 10, 2, 76, 12545, 'Phase 2 (Pre-Raid)', 'Druid', 'Balance', 'Finger1', 'Horde', 'Eye of Orgrimmar'), +(11, 0, 11, 1, 76, 13001, 'Phase 2 (Pre-Raid)', 'Druid', 'Balance', 'Finger2', 'Alliance', 'Maiden''s Circle'), +(11, 0, 11, 2, 76, 13001, 'Phase 2 (Pre-Raid)', 'Druid', 'Balance', 'Finger2', 'Horde', 'Maiden''s Circle'), +(11, 0, 12, 1, 76, 12930, 'Phase 2 (Pre-Raid)', 'Druid', 'Balance', 'Trinket1', 'Alliance', 'Briarwood Reed'), +(11, 0, 12, 2, 76, 12930, 'Phase 2 (Pre-Raid)', 'Druid', 'Balance', 'Trinket1', 'Horde', 'Briarwood Reed'), +(11, 0, 13, 1, 76, 13968, 'Phase 2 (Pre-Raid)', 'Druid', 'Balance', 'Trinket2', 'Alliance', 'Eye of the Beast'), +(11, 0, 13, 2, 76, 13968, 'Phase 2 (Pre-Raid)', 'Druid', 'Balance', 'Trinket2', 'Horde', 'Eye of the Beast'), +(11, 0, 14, 1, 76, 13386, 'Phase 2 (Pre-Raid)', 'Druid', 'Balance', 'Back', 'Alliance', 'Archivist Cape'), +(11, 0, 14, 2, 76, 13386, 'Phase 2 (Pre-Raid)', 'Druid', 'Balance', 'Back', 'Horde', 'Archivist Cape'), +(11, 0, 15, 1, 76, 18534, 'Phase 2 (Pre-Raid)', 'Druid', 'Balance', 'MainHand', 'Alliance', 'Rod of the Ogre Magi'), +(11, 0, 15, 2, 76, 18534, 'Phase 2 (Pre-Raid)', 'Druid', 'Balance', 'MainHand', 'Horde', 'Rod of the Ogre Magi'); + +-- ilvl 78 (Phase 2) +INSERT INTO `playerbots_bis_gear` VALUES +(11, 0, 0, 0, 78, 10504, 'Phase 2', 'Druid', 'Balance', 'Head', 'Both', 'Green Lens'), +(11, 0, 1, 0, 78, 18814, 'Phase 2', 'Druid', 'Balance', 'Neck', 'Both', 'Choker of the Fire Lord'), +(11, 0, 2, 0, 78, 16836, 'Phase 2', 'Druid', 'Balance', 'Shoulders', 'Both', 'Cenarion Spaulders'), +(11, 0, 4, 0, 78, 19145, 'Phase 2', 'Druid', 'Balance', 'Chest', 'Both', 'Robe of Volatile Power'), +(11, 0, 5, 0, 78, 19136, 'Phase 2', 'Druid', 'Balance', 'Waist', 'Both', 'Mana Igniting Cord'), +(11, 0, 6, 0, 78, 13170, 'Phase 2', 'Druid', 'Balance', 'Legs', 'Both', 'Skyshroud Leggings'), +(11, 0, 7, 0, 78, 11822, 'Phase 2', 'Druid', 'Balance', 'Feet', 'Both', 'Omnicast Boots'), +(11, 0, 8, 0, 78, 11766, 'Phase 2', 'Druid', 'Balance', 'Wrists', 'Both', 'Flameweave Cuffs'), +(11, 0, 9, 0, 78, 13258, 'Phase 2', 'Druid', 'Balance', 'Hands', 'Both', 'Slaghide Gauntlets'), +(11, 0, 10, 0, 78, 19147, 'Phase 2', 'Druid', 'Balance', 'Finger1', 'Both', 'Ring of Spell Power'), +(11, 0, 11, 0, 78, 19147, 'Phase 2', 'Druid', 'Balance', 'Finger2', 'Both', 'Ring of Spell Power'), +(11, 0, 12, 0, 78, 12930, 'Phase 2', 'Druid', 'Balance', 'Trinket1', 'Both', 'Briarwood Reed'), +(11, 0, 13, 0, 78, 18820, 'Phase 2', 'Druid', 'Balance', 'Trinket2', 'Both', 'Talisman of Ephemeral Power'), +(11, 0, 14, 0, 78, 13386, 'Phase 2', 'Druid', 'Balance', 'Back', 'Both', 'Archivist Cape'), +(11, 0, 15, 0, 78, 18842, 'Phase 2', 'Druid', 'Balance', 'MainHand', 'Both', 'Staff of Dominance'); + +-- ilvl 83 (Phase 3) +INSERT INTO `playerbots_bis_gear` VALUES +(11, 0, 0, 0, 83, 19375, 'Phase 3', 'Druid', 'Balance', 'Head', 'Both', 'Mish''undare, Circlet of the Mind Flayer'), +(11, 0, 1, 0, 83, 18814, 'Phase 3', 'Druid', 'Balance', 'Neck', 'Both', 'Choker of the Fire Lord'), +(11, 0, 2, 0, 83, 19370, 'Phase 3', 'Druid', 'Balance', 'Shoulders', 'Both', 'Mantle of the Blackwing Cabal'), +(11, 0, 4, 0, 83, 19145, 'Phase 3', 'Druid', 'Balance', 'Chest', 'Both', 'Robe of Volatile Power'), +(11, 0, 5, 0, 83, 19400, 'Phase 3', 'Druid', 'Balance', 'Waist', 'Both', 'Firemaw''s Clutch'), +(11, 0, 6, 0, 83, 13170, 'Phase 3', 'Druid', 'Balance', 'Legs', 'Both', 'Skyshroud Leggings'), +(11, 0, 7, 0, 83, 11822, 'Phase 3', 'Druid', 'Balance', 'Feet', 'Both', 'Omnicast Boots'), +(11, 0, 8, 0, 83, 19374, 'Phase 3', 'Druid', 'Balance', 'Wrists', 'Both', 'Bracers of Arcane Accuracy'), +(11, 0, 9, 0, 83, 13258, 'Phase 3', 'Druid', 'Balance', 'Hands', 'Both', 'Slaghide Gauntlets'), +(11, 0, 11, 0, 83, 19147, 'Phase 3', 'Druid', 'Balance', 'Finger2', 'Both', 'Ring of Spell Power'), +(11, 0, 12, 0, 83, 19379, 'Phase 3', 'Druid', 'Balance', 'Trinket1', 'Both', 'Neltharion''s Tear'), +(11, 0, 13, 0, 83, 18820, 'Phase 3', 'Druid', 'Balance', 'Trinket2', 'Both', 'Talisman of Ephemeral Power'), +(11, 0, 14, 0, 83, 19378, 'Phase 3', 'Druid', 'Balance', 'Back', 'Both', 'Cloak of the Brood Lord'), +(11, 0, 15, 0, 83, 19360, 'Phase 3', 'Druid', 'Balance', 'MainHand', 'Both', 'Lok''amir il Romathis'), +(11, 0, 16, 0, 83, 19308, 'Phase 3', 'Druid', 'Balance', 'OffHand', 'Both', 'Tome of Arcane Domination'); + +-- ilvl 88 (Phase 5) +INSERT INTO `playerbots_bis_gear` VALUES +(11, 0, 0, 0, 88, 19375, 'Phase 5', 'Druid', 'Balance', 'Head', 'Both', 'Mish''undare, Circlet of the Mind Flayer'), +(11, 0, 1, 0, 88, 21608, 'Phase 5', 'Druid', 'Balance', 'Neck', 'Both', 'Amulet of Vek''nilash'), +(11, 0, 2, 0, 88, 19370, 'Phase 5', 'Druid', 'Balance', 'Shoulders', 'Both', 'Mantle of the Blackwing Cabal'), +(11, 0, 4, 0, 88, 19682, 'Phase 5', 'Druid', 'Balance', 'Chest', 'Both', 'Bloodvine Vest'), +(11, 0, 5, 0, 88, 22730, 'Phase 5', 'Druid', 'Balance', 'Waist', 'Both', 'Eyestalk Waist Cord'), +(11, 0, 6, 0, 88, 19683, 'Phase 5', 'Druid', 'Balance', 'Legs', 'Both', 'Bloodvine Leggings'), +(11, 0, 7, 0, 88, 19684, 'Phase 5', 'Druid', 'Balance', 'Feet', 'Both', 'Bloodvine Boots'), +(11, 0, 8, 0, 88, 21186, 'Phase 5', 'Druid', 'Balance', 'Wrists', 'Both', 'Rockfury Bracers'), +(11, 0, 9, 0, 88, 21585, 'Phase 5', 'Druid', 'Balance', 'Hands', 'Both', 'Dark Storm Gauntlets'), +(11, 0, 11, 0, 88, 21709, 'Phase 5', 'Druid', 'Balance', 'Finger2', 'Both', 'Ring of the Fallen God'), +(11, 0, 12, 0, 88, 19379, 'Phase 5', 'Druid', 'Balance', 'Trinket1', 'Both', 'Neltharion''s Tear'), +(11, 0, 13, 0, 88, 18820, 'Phase 5', 'Druid', 'Balance', 'Trinket2', 'Both', 'Talisman of Ephemeral Power'), +(11, 0, 14, 0, 88, 22731, 'Phase 5', 'Druid', 'Balance', 'Back', 'Both', 'Cloak of the Devoured'), +(11, 0, 15, 0, 88, 19360, 'Phase 5', 'Druid', 'Balance', 'MainHand', 'Both', 'Lok''amir il Romathis'), +(11, 0, 16, 0, 88, 21597, 'Phase 5', 'Druid', 'Balance', 'OffHand', 'Both', 'Royal Scepter of Vek''lor'), +(11, 0, 17, 0, 88, 23197, 'Phase 5', 'Druid', 'Balance', 'Ranged', 'Both', 'Idol of the Moon'); + +-- ilvl 92 (Phase 6) +INSERT INTO `playerbots_bis_gear` VALUES +(11, 0, 0, 0, 92, 19375, 'Phase 6', 'Druid', 'Balance', 'Head', 'Both', 'Mish''undare, Circlet of the Mind Flayer'), +(11, 0, 1, 0, 92, 23057, 'Phase 6', 'Druid', 'Balance', 'Neck', 'Both', 'Gem of Trapped Innocents'), +(11, 0, 2, 0, 92, 22983, 'Phase 6', 'Druid', 'Balance', 'Shoulders', 'Both', 'Rime Covered Mantle'), +(11, 0, 4, 0, 92, 19682, 'Phase 6', 'Druid', 'Balance', 'Chest', 'Both', 'Bloodvine Vest'), +(11, 0, 5, 0, 92, 22730, 'Phase 6', 'Druid', 'Balance', 'Waist', 'Both', 'Eyestalk Waist Cord'), +(11, 0, 6, 0, 92, 19683, 'Phase 6', 'Druid', 'Balance', 'Legs', 'Both', 'Bloodvine Leggings'), +(11, 0, 7, 0, 92, 19684, 'Phase 6', 'Druid', 'Balance', 'Feet', 'Both', 'Bloodvine Boots'), +(11, 0, 8, 0, 92, 23021, 'Phase 6', 'Druid', 'Balance', 'Wrists', 'Both', 'The Soul Harvester''s Bindings'), +(11, 0, 9, 0, 92, 21585, 'Phase 6', 'Druid', 'Balance', 'Hands', 'Both', 'Dark Storm Gauntlets'), +(11, 0, 10, 0, 92, 23025, 'Phase 6', 'Druid', 'Balance', 'Finger1', 'Both', 'Seal of the Damned'), +(11, 0, 11, 0, 92, 21709, 'Phase 6', 'Druid', 'Balance', 'Finger2', 'Both', 'Ring of the Fallen God'), +(11, 0, 12, 0, 92, 19379, 'Phase 6', 'Druid', 'Balance', 'Trinket1', 'Both', 'Neltharion''s Tear'), +(11, 0, 13, 0, 92, 23046, 'Phase 6', 'Druid', 'Balance', 'Trinket2', 'Both', 'The Restrained Essence of Sapphiron'), +(11, 0, 14, 0, 92, 23050, 'Phase 6', 'Druid', 'Balance', 'Back', 'Both', 'Cloak of the Necropolis'), +(11, 0, 15, 0, 92, 22988, 'Phase 6', 'Druid', 'Balance', 'MainHand', 'Both', 'The End of Dreams'), +(11, 0, 16, 0, 92, 23049, 'Phase 6', 'Druid', 'Balance', 'OffHand', 'Both', 'Sapphiron''s Left Eye'), +(11, 0, 17, 0, 92, 23197, 'Phase 6', 'Druid', 'Balance', 'Ranged', 'Both', 'Idol of the Moon'); + +-- ilvl 120 (Pre-Raid) +INSERT INTO `playerbots_bis_gear` VALUES +(11, 0, 0, 0, 120, 24266, 'Pre-Raid', 'Druid', 'Balance', 'Head', 'Both', 'Spellstrike Hood'), +(11, 0, 1, 0, 120, 28134, 'Pre-Raid', 'Druid', 'Balance', 'Neck', 'Both', 'Brooch of Heightened Potential'), +(11, 0, 2, 0, 120, 27796, 'Pre-Raid', 'Druid', 'Balance', 'Shoulders', 'Both', 'Mana-Etched Spaulders'), +(11, 0, 4, 0, 120, 21848, 'Pre-Raid', 'Druid', 'Balance', 'Chest', 'Both', 'Spellfire Robe'), +(11, 0, 5, 0, 120, 21846, 'Pre-Raid', 'Druid', 'Balance', 'Waist', 'Both', 'Spellfire Belt'), +(11, 0, 6, 0, 120, 24262, 'Pre-Raid', 'Druid', 'Balance', 'Legs', 'Both', 'Spellstrike Pants'), +(11, 0, 7, 0, 120, 28406, 'Pre-Raid', 'Druid', 'Balance', 'Feet', 'Both', 'Sigil-Laced Boots'), +(11, 0, 8, 0, 120, 24250, 'Pre-Raid', 'Druid', 'Balance', 'Wrists', 'Both', 'Bracers of Havok'), +(11, 0, 9, 0, 120, 21847, 'Pre-Raid', 'Druid', 'Balance', 'Hands', 'Both', 'Spellfire Gloves'), +(11, 0, 10, 0, 120, 29172, 'Pre-Raid', 'Druid', 'Balance', 'Finger1', 'Both', 'Ashyen''s Gift'), +(11, 0, 11, 0, 120, 28227, 'Pre-Raid', 'Druid', 'Balance', 'Finger2', 'Both', 'Sparking Arcanite Ring'), +(11, 0, 12, 0, 120, 29370, 'Pre-Raid', 'Druid', 'Balance', 'Trinket1', 'Both', 'Icon of the Silver Crescent'), +(11, 0, 13, 0, 120, 27683, 'Pre-Raid', 'Druid', 'Balance', 'Trinket2', 'Both', 'Quagmirran''s Eye'), +(11, 0, 14, 0, 120, 27981, 'Pre-Raid', 'Druid', 'Balance', 'Back', 'Both', 'Sethekk Oracle Cloak'), +(11, 0, 15, 0, 120, 30832, 'Pre-Raid', 'Druid', 'Balance', 'MainHand', 'Both', 'Gavel of Unearthed Secrets'), +(11, 0, 16, 0, 120, 29271, 'Pre-Raid', 'Druid', 'Balance', 'OffHand', 'Both', 'Talisman of Kalecgos'), +(11, 0, 17, 0, 120, 27518, 'Pre-Raid', 'Druid', 'Balance', 'Ranged', 'Both', 'Ivory Idol of the Moongoddess'); + +-- ilvl 125 (Phase 1) +INSERT INTO `playerbots_bis_gear` VALUES +(11, 0, 0, 0, 125, 29093, 'Phase 1', 'Druid', 'Balance', 'Head', 'Both', 'Antlers of Malorne'), +(11, 0, 1, 0, 125, 28530, 'Phase 1', 'Druid', 'Balance', 'Neck', 'Both', 'Brooch of Unquenchable Fury'), +(11, 0, 2, 0, 125, 29095, 'Phase 1', 'Druid', 'Balance', 'Shoulders', 'Both', 'Pauldrons of Malorne'), +(11, 0, 4, 0, 125, 21848, 'Phase 1', 'Druid', 'Balance', 'Chest', 'Both', 'Spellfire Robe'), +(11, 0, 5, 0, 125, 21846, 'Phase 1', 'Druid', 'Balance', 'Waist', 'Both', 'Spellfire Belt'), +(11, 0, 6, 0, 125, 24262, 'Phase 1', 'Druid', 'Balance', 'Legs', 'Both', 'Spellstrike Pants'), +(11, 0, 7, 0, 125, 28517, 'Phase 1', 'Druid', 'Balance', 'Feet', 'Both', 'Boots of Foretelling'), +(11, 0, 8, 0, 125, 24250, 'Phase 1', 'Druid', 'Balance', 'Wrists', 'Both', 'Bracers of Havok'), +(11, 0, 9, 0, 125, 21847, 'Phase 1', 'Druid', 'Balance', 'Hands', 'Both', 'Spellfire Gloves'), +(11, 0, 10, 0, 125, 28793, 'Phase 1', 'Druid', 'Balance', 'Finger1', 'Both', 'Band of Crimson Fury'), +(11, 0, 11, 0, 125, 28753, 'Phase 1', 'Druid', 'Balance', 'Finger2', 'Both', 'Ring of Recurrence'), +(11, 0, 12, 0, 125, 29370, 'Phase 1', 'Druid', 'Balance', 'Trinket1', 'Both', 'Icon of the Silver Crescent'), +(11, 0, 13, 0, 125, 27683, 'Phase 1', 'Druid', 'Balance', 'Trinket2', 'Both', 'Quagmirran''s Eye'), +(11, 0, 14, 0, 125, 28766, 'Phase 1', 'Druid', 'Balance', 'Back', 'Both', 'Ruby Drape of the Mysticant'), +(11, 0, 15, 0, 125, 28770, 'Phase 1', 'Druid', 'Balance', 'MainHand', 'Both', 'Nathrezim Mindblade'), +(11, 0, 16, 0, 125, 29271, 'Phase 1', 'Druid', 'Balance', 'OffHand', 'Both', 'Talisman of Kalecgos'), +(11, 0, 17, 0, 125, 27518, 'Phase 1', 'Druid', 'Balance', 'Ranged', 'Both', 'Ivory Idol of the Moongoddess'); + +-- ilvl 200 (Fresh 80) +INSERT INTO `playerbots_bis_gear` VALUES +(11, 0, 0, 0, 200, 39545, 'Fresh 80', 'Druid', 'Balance', 'Head', 'Both', 'Heroes'' Dreamwalker Cover'), +(11, 0, 1, 0, 200, 44658, 'Fresh 80', 'Druid', 'Balance', 'Neck', 'Both', 'Chain of the Ancient Wyrm'), +(11, 0, 2, 0, 200, 39548, 'Fresh 80', 'Druid', 'Balance', 'Shoulders', 'Both', 'Heroes'' Dreamwalker Mantle'), +(11, 0, 4, 0, 200, 39547, 'Fresh 80', 'Druid', 'Balance', 'Chest', 'Both', 'Heroes'' Dreamwalker Vestments'), +(11, 0, 5, 0, 200, 40696, 'Fresh 80', 'Druid', 'Balance', 'Waist', 'Both', 'Plush Sash of Guzbah'), +(11, 0, 6, 0, 200, 39546, 'Fresh 80', 'Druid', 'Balance', 'Legs', 'Both', 'Heroes'' Dreamwalker Trousers'), +(11, 0, 7, 0, 200, 40519, 'Fresh 80', 'Druid', 'Balance', 'Feet', 'Both', 'Footsteps of Malygos'), +(11, 0, 8, 0, 200, 40740, 'Fresh 80', 'Druid', 'Balance', 'Wrists', 'Both', 'Wraps of the Astral Traveler'), +(11, 0, 9, 0, 200, 39544, 'Fresh 80', 'Druid', 'Balance', 'Hands', 'Both', 'Heroes'' Dreamwalker Gloves'), +(11, 0, 10, 0, 200, 40719, 'Fresh 80', 'Druid', 'Balance', 'Finger1', 'Both', 'Band of Channeled Magic'), +(11, 0, 12, 0, 200, 39229, 'Fresh 80', 'Druid', 'Balance', 'Trinket1', 'Both', 'Embrace of the Spider'), +(11, 0, 14, 0, 200, 40723, 'Fresh 80', 'Druid', 'Balance', 'Back', 'Both', 'Disguise of the Kumiho'), +(11, 0, 15, 0, 200, 39423, 'Fresh 80', 'Druid', 'Balance', 'MainHand', 'Both', 'Hammer of the Astral Plane'), +(11, 0, 17, 0, 200, 40321, 'Fresh 80', 'Druid', 'Balance', 'Ranged', 'Both', 'Idol of the Shooting Star'); + +-- ilvl 213 (Pre-Ulduar) +INSERT INTO `playerbots_bis_gear` VALUES +(11, 0, 0, 0, 213, 40467, 'Pre-Ulduar', 'Druid', 'Balance', 'Head', 'Both', 'Valorous Dreamwalker Cover'), +(11, 0, 1, 0, 213, 44661, 'Pre-Ulduar', 'Druid', 'Balance', 'Neck', 'Both', 'Wyrmrest Necklace of Power'), +(11, 0, 2, 0, 213, 40470, 'Pre-Ulduar', 'Druid', 'Balance', 'Shoulders', 'Both', 'Valorous Dreamwalker Mantle'), +(11, 0, 4, 0, 213, 40469, 'Pre-Ulduar', 'Druid', 'Balance', 'Chest', 'Both', 'Valorous Dreamwalker Vestments'), +(11, 0, 5, 0, 213, 40561, 'Pre-Ulduar', 'Druid', 'Balance', 'Waist', 'Both', 'Leash of Heedless Magic'), +(11, 0, 6, 0, 213, 40560, 'Pre-Ulduar', 'Druid', 'Balance', 'Legs', 'Both', 'Leggings of the Wanton Spellcaster'), +(11, 0, 7, 0, 213, 40519, 'Pre-Ulduar', 'Druid', 'Balance', 'Feet', 'Both', 'Footsteps of Malygos'), +(11, 0, 8, 0, 213, 40323, 'Pre-Ulduar', 'Druid', 'Balance', 'Wrists', 'Both', 'Esteemed Bindings'), +(11, 0, 9, 0, 213, 40460, 'Pre-Ulduar', 'Druid', 'Balance', 'Hands', 'Both', 'Valorous Dreamwalker Handguards'), +(11, 0, 10, 0, 213, 40080, 'Pre-Ulduar', 'Druid', 'Balance', 'Finger1', 'Both', 'Lost Jewel'), +(11, 0, 12, 0, 213, 40682, 'Pre-Ulduar', 'Druid', 'Balance', 'Trinket1', 'Both', 'Sundial of the Exiled'), +(11, 0, 14, 0, 213, 40405, 'Pre-Ulduar', 'Druid', 'Balance', 'Back', 'Both', 'Cape of the Unworthy Wizard'), +(11, 0, 17, 0, 213, 40321, 'Pre-Ulduar', 'Druid', 'Balance', 'Ranged', 'Both', 'Idol of the Shooting Star'); + +-- ilvl 245 (Phase 2) +INSERT INTO `playerbots_bis_gear` VALUES +(11, 0, 0, 0, 245, 46191, 'Phase 2', 'Druid', 'Balance', 'Head', 'Both', 'Conqueror''s Nightsong Cover'), +(11, 0, 1, 0, 245, 45933, 'Phase 2', 'Druid', 'Balance', 'Neck', 'Both', 'Pendant of the Shallow Grave'), +(11, 0, 2, 0, 245, 45136, 'Phase 2', 'Druid', 'Balance', 'Shoulders', 'Both', 'Shoulderpads of Dormant Energies'), +(11, 0, 4, 0, 245, 45519, 'Phase 2', 'Druid', 'Balance', 'Chest', 'Both', 'Vestments of the Blind Denizen'), +(11, 0, 5, 0, 245, 45619, 'Phase 2', 'Druid', 'Balance', 'Waist', 'Both', 'Starwatcher''s Binding'), +(11, 0, 6, 0, 245, 46192, 'Phase 2', 'Druid', 'Balance', 'Legs', 'Both', 'Conqueror''s Nightsong Trousers'), +(11, 0, 7, 0, 245, 45537, 'Phase 2', 'Druid', 'Balance', 'Feet', 'Both', 'Treads of the False Oracle'), +(11, 0, 8, 0, 245, 45446, 'Phase 2', 'Druid', 'Balance', 'Wrists', 'Both', 'Grasps of Reason'), +(11, 0, 9, 0, 245, 45665, 'Phase 2', 'Druid', 'Balance', 'Hands', 'Both', 'Pharos Gloves'), +(11, 0, 10, 0, 245, 45495, 'Phase 2', 'Druid', 'Balance', 'Finger1', 'Both', 'Conductive Seal'), +(11, 0, 12, 0, 245, 45466, 'Phase 2', 'Druid', 'Balance', 'Trinket1', 'Both', 'Scale of Fates'), +(11, 0, 14, 0, 245, 45242, 'Phase 2', 'Druid', 'Balance', 'Back', 'Both', 'Drape of Mortal Downfall'), +(11, 0, 17, 0, 245, 40321, 'Phase 2', 'Druid', 'Balance', 'Ranged', 'Both', 'Idol of the Shooting Star'); + +-- ilvl 258 (Phase 3) +INSERT INTO `playerbots_bis_gear` VALUES +(11, 0, 0, 0, 258, 48171, 'Phase 3', 'Druid', 'Balance', 'Head', 'Both', 'Cover of Triumph'), +(11, 0, 1, 1, 258, 47144, 'Phase 3', 'Druid', 'Balance', 'Neck', 'Alliance', 'Wail of the Val''kyr'), +(11, 0, 1, 2, 258, 47468, 'Phase 3', 'Druid', 'Balance', 'Neck', 'Horde', 'Cry of the Val''kyr'), +(11, 0, 2, 0, 258, 48168, 'Phase 3', 'Druid', 'Balance', 'Shoulders', 'Both', 'Mantle of Triumph'), +(11, 0, 4, 0, 258, 48169, 'Phase 3', 'Druid', 'Balance', 'Chest', 'Both', 'Vestments of Triumph'), +(11, 0, 5, 1, 258, 47084, 'Phase 3', 'Druid', 'Balance', 'Waist', 'Alliance', 'Cord of Biting Cold'), +(11, 0, 5, 2, 258, 47447, 'Phase 3', 'Druid', 'Balance', 'Waist', 'Horde', 'Belt of Biting Cold'), +(11, 0, 6, 1, 258, 47190, 'Phase 3', 'Druid', 'Balance', 'Legs', 'Alliance', 'Legwraps of the Awakening'), +(11, 0, 6, 2, 258, 47479, 'Phase 3', 'Druid', 'Balance', 'Legs', 'Horde', 'Leggings of the Awakening'), +(11, 0, 7, 1, 258, 47097, 'Phase 3', 'Druid', 'Balance', 'Feet', 'Alliance', 'Boots of the Mourning Widow'), +(11, 0, 7, 2, 258, 47454, 'Phase 3', 'Druid', 'Balance', 'Feet', 'Horde', 'Sandals of the Mourning Widow'), +(11, 0, 8, 1, 258, 47066, 'Phase 3', 'Druid', 'Balance', 'Wrists', 'Alliance', 'Bracers of the Autumn Willow'), +(11, 0, 8, 2, 258, 47438, 'Phase 3', 'Druid', 'Balance', 'Wrists', 'Horde', 'Bindings of the Autumn Willow'), +(11, 0, 9, 0, 258, 48172, 'Phase 3', 'Druid', 'Balance', 'Hands', 'Both', 'Gloves of Triumph'), +(11, 0, 10, 1, 258, 47237, 'Phase 3', 'Druid', 'Balance', 'Finger1', 'Alliance', 'Band of Deplorable Violence'), +(11, 0, 10, 2, 258, 47489, 'Phase 3', 'Druid', 'Balance', 'Finger1', 'Horde', 'Lurid Manifestation'), +(11, 0, 12, 2, 258, 45518, 'Phase 3', 'Druid', 'Balance', 'Trinket1', 'Horde', 'Flare of the Heavens'), +(11, 0, 14, 1, 258, 47552, 'Phase 3', 'Druid', 'Balance', 'Back', 'Alliance', 'Jaina''s Radiance'), +(11, 0, 14, 2, 258, 47551, 'Phase 3', 'Druid', 'Balance', 'Back', 'Horde', 'Aethas'' Intensity'), +(11, 0, 17, 0, 258, 47670, 'Phase 3', 'Druid', 'Balance', 'Ranged', 'Both', 'Idol of Lunar Fury'); + +-- ilvl 264 (Phase 4) +INSERT INTO `playerbots_bis_gear` VALUES +(11, 0, 0, 0, 264, 51290, 'Phase 4', 'Druid', 'Balance', 'Head', 'Both', 'Sanctified Lasherweave Cover'), +(11, 0, 1, 0, 264, 50724, 'Phase 4', 'Druid', 'Balance', 'Neck', 'Both', 'Blood Queen''s Crimson Choker'), +(11, 0, 2, 0, 264, 51292, 'Phase 4', 'Druid', 'Balance', 'Shoulders', 'Both', 'Sanctified Lasherweave Mantle'), +(11, 0, 4, 0, 264, 51294, 'Phase 4', 'Druid', 'Balance', 'Chest', 'Both', 'Sanctified Lasherweave Vestment'), +(11, 0, 5, 0, 264, 50613, 'Phase 4', 'Druid', 'Balance', 'Waist', 'Both', 'Crushing Coldwraith Belt'), +(11, 0, 6, 0, 264, 50694, 'Phase 4', 'Druid', 'Balance', 'Legs', 'Both', 'Plaguebringer''s Stained Pants'), +(11, 0, 7, 0, 264, 50699, 'Phase 4', 'Druid', 'Balance', 'Feet', 'Both', 'Plague Scientist''s Boots'), +(11, 0, 8, 0, 264, 50630, 'Phase 4', 'Druid', 'Balance', 'Wrists', 'Both', 'Bracers of Eternal Dreaming'), +(11, 0, 9, 0, 264, 51291, 'Phase 4', 'Druid', 'Balance', 'Hands', 'Both', 'Sanctified Lasherweave Gloves'), +(11, 0, 10, 0, 264, 50398, 'Phase 4', 'Druid', 'Balance', 'Finger1', 'Both', 'Ashen Band of Endless Destruction'), +(11, 0, 12, 0, 264, 50348, 'Phase 4', 'Druid', 'Balance', 'Trinket1', 'Both', 'Dislodged Foreign Object'), +(11, 0, 14, 0, 264, 50628, 'Phase 4', 'Druid', 'Balance', 'Back', 'Both', 'Frostbinder''s Shredded Cape'), +(11, 0, 17, 0, 264, 50719, 'Phase 4', 'Druid', 'Balance', 'Ranged', 'Both', 'Shadow Silk Spindle'); + +-- ilvl 290 (Phase 5) +INSERT INTO `playerbots_bis_gear` VALUES +(11, 0, 0, 0, 290, 51290, 'Phase 5', 'Druid', 'Balance', 'Head', 'Both', 'Sanctified Lasherweave Cover'), +(11, 0, 1, 0, 290, 50182, 'Phase 5', 'Druid', 'Balance', 'Neck', 'Both', 'Blood Queen''s Crimson Choker'), +(11, 0, 2, 0, 290, 51292, 'Phase 5', 'Druid', 'Balance', 'Shoulders', 'Both', 'Sanctified Lasherweave Mantle'), +(11, 0, 4, 0, 290, 51294, 'Phase 5', 'Druid', 'Balance', 'Chest', 'Both', 'Sanctified Lasherweave Vestment'), +(11, 0, 5, 0, 290, 50613, 'Phase 5', 'Druid', 'Balance', 'Waist', 'Both', 'Crushing Coldwraith Belt'), +(11, 0, 6, 0, 290, 50694, 'Phase 5', 'Druid', 'Balance', 'Legs', 'Both', 'Plaguebringer''s Stained Pants'), +(11, 0, 7, 0, 290, 50699, 'Phase 5', 'Druid', 'Balance', 'Feet', 'Both', 'Plague Scientist''s Boots'), +(11, 0, 8, 0, 290, 54582, 'Phase 5', 'Druid', 'Balance', 'Wrists', 'Both', 'Bracers of Fiery Night'), +(11, 0, 9, 0, 290, 51291, 'Phase 5', 'Druid', 'Balance', 'Hands', 'Both', 'Sanctified Lasherweave Gloves'), +(11, 0, 10, 0, 290, 50398, 'Phase 5', 'Druid', 'Balance', 'Finger1', 'Both', 'Ashen Band of Endless Destruction'), +(11, 0, 11, 0, 290, 50714, 'Phase 5', 'Druid', 'Balance', 'Finger2', 'Both', 'Valanar''s Other Signet Ring'), +(11, 0, 12, 0, 290, 54588, 'Phase 5', 'Druid', 'Balance', 'Trinket1', 'Both', 'Charred Twilight Scale'), +(11, 0, 13, 0, 290, 50365, 'Phase 5', 'Druid', 'Balance', 'Trinket2', 'Both', 'Phylactery of the Nameless Lich'), +(11, 0, 14, 0, 290, 54583, 'Phase 5', 'Druid', 'Balance', 'Back', 'Both', 'Cloak of Burning Dusk'), +(11, 0, 15, 0, 290, 50734, 'Phase 5', 'Druid', 'Balance', 'MainHand', 'Both', 'Royal Scepter of Terenas II'), +(11, 0, 16, 0, 290, 50719, 'Phase 5', 'Druid', 'Balance', 'OffHand', 'Both', 'Shadow Silk Spindle'); + +-- Feral Cat (tab 1) +-- ilvl 66 (Phase 1 (Pre-Raid)) +INSERT INTO `playerbots_bis_gear` VALUES +(11, 1, 0, 0, 66, 8345, 'Phase 1 (Pre-Raid)', 'Druid', 'Feral Cat', 'Head', 'Both', 'Wolfshead Helm'), +(11, 1, 1, 0, 66, 15411, 'Phase 1 (Pre-Raid)', 'Druid', 'Feral Cat', 'Neck', 'Both', 'Mark of Fordring'), +(11, 1, 2, 0, 66, 12927, 'Phase 1 (Pre-Raid)', 'Druid', 'Feral Cat', 'Shoulders', 'Both', 'Truestrike Shoulders'), +(11, 1, 4, 0, 66, 14637, 'Phase 1 (Pre-Raid)', 'Druid', 'Feral Cat', 'Chest', 'Both', 'Cadaverous Armor'), +(11, 1, 5, 0, 66, 13252, 'Phase 1 (Pre-Raid)', 'Druid', 'Feral Cat', 'Waist', 'Both', 'Cloudrunner Girdle'), +(11, 1, 6, 0, 66, 15062, 'Phase 1 (Pre-Raid)', 'Druid', 'Feral Cat', 'Legs', 'Both', 'Devilsaur Leggings'), +(11, 1, 7, 0, 66, 12553, 'Phase 1 (Pre-Raid)', 'Druid', 'Feral Cat', 'Feet', 'Both', 'Swiftwalker Boots'), +(11, 1, 8, 0, 66, 16710, 'Phase 1 (Pre-Raid)', 'Druid', 'Feral Cat', 'Wrists', 'Both', 'Shadowcraft Bracers'), +(11, 1, 9, 0, 66, 15063, 'Phase 1 (Pre-Raid)', 'Druid', 'Feral Cat', 'Hands', 'Both', 'Devilsaur Gauntlets'), +(11, 1, 10, 0, 66, 17713, 'Phase 1 (Pre-Raid)', 'Druid', 'Feral Cat', 'Finger1', 'Both', 'Blackstone Ring'), +(11, 1, 11, 0, 66, 2246, 'Phase 1 (Pre-Raid)', 'Druid', 'Feral Cat', 'Finger2', 'Both', 'Myrmidon''s Signet'), +(11, 1, 12, 0, 66, 13965, 'Phase 1 (Pre-Raid)', 'Druid', 'Feral Cat', 'Trinket1', 'Both', 'Blackhand''s Breadth'), +(11, 1, 13, 0, 66, 11815, 'Phase 1 (Pre-Raid)', 'Druid', 'Feral Cat', 'Trinket2', 'Both', 'Hand of Justice'), +(11, 1, 14, 0, 66, 13340, 'Phase 1 (Pre-Raid)', 'Druid', 'Feral Cat', 'Back', 'Both', 'Cape of the Black Baron'), +(11, 1, 15, 0, 66, 11921, 'Phase 1 (Pre-Raid)', 'Druid', 'Feral Cat', 'MainHand', 'Both', 'Impervious Giant'); + +-- ilvl 76 (Phase 2 (Pre-Raid)) +INSERT INTO `playerbots_bis_gear` VALUES +(11, 1, 0, 0, 76, 8345, 'Phase 2 (Pre-Raid)', 'Druid', 'Feral Cat', 'Head', 'Both', 'Wolfshead Helm'), +(11, 1, 1, 0, 76, 15411, 'Phase 2 (Pre-Raid)', 'Druid', 'Feral Cat', 'Neck', 'Both', 'Mark of Fordring'), +(11, 1, 2, 0, 76, 12927, 'Phase 2 (Pre-Raid)', 'Druid', 'Feral Cat', 'Shoulders', 'Both', 'Truestrike Shoulders'), +(11, 1, 4, 0, 76, 14637, 'Phase 2 (Pre-Raid)', 'Druid', 'Feral Cat', 'Chest', 'Both', 'Cadaverous Armor'), +(11, 1, 5, 0, 76, 13252, 'Phase 2 (Pre-Raid)', 'Druid', 'Feral Cat', 'Waist', 'Both', 'Cloudrunner Girdle'), +(11, 1, 6, 0, 76, 15062, 'Phase 2 (Pre-Raid)', 'Druid', 'Feral Cat', 'Legs', 'Both', 'Devilsaur Leggings'), +(11, 1, 7, 0, 76, 12553, 'Phase 2 (Pre-Raid)', 'Druid', 'Feral Cat', 'Feet', 'Both', 'Swiftwalker Boots'), +(11, 1, 8, 0, 76, 18375, 'Phase 2 (Pre-Raid)', 'Druid', 'Feral Cat', 'Wrists', 'Both', 'Bracers of the Eclipse'), +(11, 1, 9, 0, 76, 15063, 'Phase 2 (Pre-Raid)', 'Druid', 'Feral Cat', 'Hands', 'Both', 'Devilsaur Gauntlets'), +(11, 1, 10, 0, 76, 17713, 'Phase 2 (Pre-Raid)', 'Druid', 'Feral Cat', 'Finger1', 'Both', 'Blackstone Ring'), +(11, 1, 11, 0, 76, 18500, 'Phase 2 (Pre-Raid)', 'Druid', 'Feral Cat', 'Finger2', 'Both', 'Tarnished Elven Ring'), +(11, 1, 12, 0, 76, 13965, 'Phase 2 (Pre-Raid)', 'Druid', 'Feral Cat', 'Trinket1', 'Both', 'Blackhand''s Breadth'), +(11, 1, 13, 0, 76, 11815, 'Phase 2 (Pre-Raid)', 'Druid', 'Feral Cat', 'Trinket2', 'Both', 'Hand of Justice'), +(11, 1, 14, 0, 76, 13340, 'Phase 2 (Pre-Raid)', 'Druid', 'Feral Cat', 'Back', 'Both', 'Cape of the Black Baron'), +(11, 1, 15, 0, 76, 18420, 'Phase 2 (Pre-Raid)', 'Druid', 'Feral Cat', 'MainHand', 'Both', 'Bonecrusher'); + +-- ilvl 78 (Phase 2) +INSERT INTO `playerbots_bis_gear` VALUES +(11, 1, 0, 0, 78, 8345, 'Phase 2', 'Druid', 'Feral Cat', 'Head', 'Both', 'Wolfshead Helm'), +(11, 1, 1, 0, 78, 18404, 'Phase 2', 'Druid', 'Feral Cat', 'Neck', 'Both', 'Onyxia Tooth Pendant'), +(11, 1, 2, 0, 78, 12927, 'Phase 2', 'Druid', 'Feral Cat', 'Shoulders', 'Both', 'Truestrike Shoulders'), +(11, 1, 4, 0, 78, 14637, 'Phase 2', 'Druid', 'Feral Cat', 'Chest', 'Both', 'Cadaverous Armor'), +(11, 1, 5, 0, 78, 13252, 'Phase 2', 'Druid', 'Feral Cat', 'Waist', 'Both', 'Cloudrunner Girdle'), +(11, 1, 6, 0, 78, 15062, 'Phase 2', 'Druid', 'Feral Cat', 'Legs', 'Both', 'Devilsaur Leggings'), +(11, 1, 7, 0, 78, 12553, 'Phase 2', 'Druid', 'Feral Cat', 'Feet', 'Both', 'Swiftwalker Boots'), +(11, 1, 8, 0, 78, 19146, 'Phase 2', 'Druid', 'Feral Cat', 'Wrists', 'Both', 'Wristguards of Stability'), +(11, 1, 9, 0, 78, 18823, 'Phase 2', 'Druid', 'Feral Cat', 'Hands', 'Both', 'Aged Core Leather Gloves'), +(11, 1, 10, 0, 78, 17063, 'Phase 2', 'Druid', 'Feral Cat', 'Finger1', 'Both', 'Band of Accuria'), +(11, 1, 11, 0, 78, 18500, 'Phase 2', 'Druid', 'Feral Cat', 'Finger2', 'Both', 'Tarnished Elven Ring'), +(11, 1, 12, 0, 78, 13965, 'Phase 2', 'Druid', 'Feral Cat', 'Trinket1', 'Both', 'Blackhand''s Breadth'), +(11, 1, 13, 0, 78, 11815, 'Phase 2', 'Druid', 'Feral Cat', 'Trinket2', 'Both', 'Hand of Justice'), +(11, 1, 14, 0, 78, 13340, 'Phase 2', 'Druid', 'Feral Cat', 'Back', 'Both', 'Cape of the Black Baron'), +(11, 1, 15, 0, 78, 18420, 'Phase 2', 'Druid', 'Feral Cat', 'MainHand', 'Both', 'Bonecrusher'); + +-- ilvl 83 (Phase 3) +INSERT INTO `playerbots_bis_gear` VALUES +(11, 1, 0, 0, 83, 8345, 'Phase 3', 'Druid', 'Feral Cat', 'Head', 'Both', 'Wolfshead Helm'), +(11, 1, 1, 0, 83, 19377, 'Phase 3', 'Druid', 'Feral Cat', 'Neck', 'Both', 'Prestor''s Talisman of Connivery'), +(11, 1, 2, 0, 83, 12927, 'Phase 3', 'Druid', 'Feral Cat', 'Shoulders', 'Both', 'Truestrike Shoulders'), +(11, 1, 4, 0, 83, 19405, 'Phase 3', 'Druid', 'Feral Cat', 'Chest', 'Both', 'Malfurion''s Blessed Bulwark'), +(11, 1, 5, 0, 83, 19396, 'Phase 3', 'Druid', 'Feral Cat', 'Waist', 'Both', 'Taut Dragonhide Belt'), +(11, 1, 6, 0, 83, 15062, 'Phase 3', 'Druid', 'Feral Cat', 'Legs', 'Both', 'Devilsaur Leggings'), +(11, 1, 7, 0, 83, 19381, 'Phase 3', 'Druid', 'Feral Cat', 'Feet', 'Both', 'Boots of the Shadow Flame'), +(11, 1, 8, 0, 83, 19146, 'Phase 3', 'Druid', 'Feral Cat', 'Wrists', 'Both', 'Wristguards of Stability'), +(11, 1, 9, 0, 83, 18823, 'Phase 3', 'Druid', 'Feral Cat', 'Hands', 'Both', 'Aged Core Leather Gloves'), +(11, 1, 10, 0, 83, 19384, 'Phase 3', 'Druid', 'Feral Cat', 'Finger1', 'Both', 'Master Dragonslayer''s Ring'), +(11, 1, 12, 0, 83, 19406, 'Phase 3', 'Druid', 'Feral Cat', 'Trinket1', 'Both', 'Drake Fang Talisman'), +(11, 1, 13, 0, 83, 11815, 'Phase 3', 'Druid', 'Feral Cat', 'Trinket2', 'Both', 'Hand of Justice'), +(11, 1, 14, 0, 83, 19436, 'Phase 3', 'Druid', 'Feral Cat', 'Back', 'Both', 'Cloak of Draconic Might'), +(11, 1, 15, 0, 83, 19358, 'Phase 3', 'Druid', 'Feral Cat', 'MainHand', 'Both', 'Draconic Maul'); + +-- ilvl 88 (Phase 5) +INSERT INTO `playerbots_bis_gear` VALUES +(11, 1, 0, 0, 88, 8345, 'Phase 5', 'Druid', 'Feral Cat', 'Head', 'Both', 'Wolfshead Helm'), +(11, 1, 1, 0, 88, 19377, 'Phase 5', 'Druid', 'Feral Cat', 'Neck', 'Both', 'Prestor''s Talisman of Connivery'), +(11, 1, 2, 0, 88, 21665, 'Phase 5', 'Druid', 'Feral Cat', 'Shoulders', 'Both', 'Mantle of Wicked Revenge'), +(11, 1, 4, 0, 88, 21680, 'Phase 5', 'Druid', 'Feral Cat', 'Chest', 'Both', 'Vest of Swift Execution'), +(11, 1, 5, 0, 88, 21586, 'Phase 5', 'Druid', 'Feral Cat', 'Waist', 'Both', 'Belt of Never-ending Agony'), +(11, 1, 6, 0, 88, 20665, 'Phase 5', 'Druid', 'Feral Cat', 'Legs', 'Both', 'Abyssal Leather Leggings'), +(11, 1, 7, 0, 88, 21493, 'Phase 5', 'Druid', 'Feral Cat', 'Feet', 'Both', 'Boots of the Vanguard'), +(11, 1, 8, 0, 88, 21602, 'Phase 5', 'Druid', 'Feral Cat', 'Wrists', 'Both', 'Qiraji Execution Bracers'), +(11, 1, 10, 0, 88, 21205, 'Phase 5', 'Druid', 'Feral Cat', 'Finger1', 'Both', 'Signet Ring of the Bronze Dragonflight'), +(11, 1, 11, 0, 88, 17063, 'Phase 5', 'Druid', 'Feral Cat', 'Finger2', 'Both', 'Band of Accuria'), +(11, 1, 12, 0, 88, 19406, 'Phase 5', 'Druid', 'Feral Cat', 'Trinket1', 'Both', 'Drake Fang Talisman'), +(11, 1, 13, 0, 88, 13965, 'Phase 5', 'Druid', 'Feral Cat', 'Trinket2', 'Both', 'Blackhand''s Breadth'), +(11, 1, 14, 0, 88, 21701, 'Phase 5', 'Druid', 'Feral Cat', 'Back', 'Both', 'Cloak of Concentrated Hatred'), +(11, 1, 15, 0, 88, 21268, 'Phase 5', 'Druid', 'Feral Cat', 'MainHand', 'Both', 'Blessed Qiraji War Hammer'), +(11, 1, 16, 0, 88, 13385, 'Phase 5', 'Druid', 'Feral Cat', 'OffHand', 'Both', 'Tome of Knowledge'), +(11, 1, 17, 0, 88, 22397, 'Phase 5', 'Druid', 'Feral Cat', 'Ranged', 'Both', 'Idol of Ferocity'); + +-- ilvl 92 (Phase 6) +INSERT INTO `playerbots_bis_gear` VALUES +(11, 1, 0, 0, 92, 8345, 'Phase 6', 'Druid', 'Feral Cat', 'Head', 'Both', 'Wolfshead Helm'), +(11, 1, 1, 0, 92, 19377, 'Phase 6', 'Druid', 'Feral Cat', 'Neck', 'Both', 'Prestor''s Talisman of Connivery'), +(11, 1, 2, 0, 92, 21665, 'Phase 6', 'Druid', 'Feral Cat', 'Shoulders', 'Both', 'Mantle of Wicked Revenge'), +(11, 1, 4, 0, 92, 23226, 'Phase 6', 'Druid', 'Feral Cat', 'Chest', 'Both', 'Ghoul Skin Tunic'), +(11, 1, 5, 0, 92, 21586, 'Phase 6', 'Druid', 'Feral Cat', 'Waist', 'Both', 'Belt of Never-ending Agony'), +(11, 1, 6, 0, 92, 23071, 'Phase 6', 'Druid', 'Feral Cat', 'Legs', 'Both', 'Leggings of Apocalypse'), +(11, 1, 7, 0, 92, 21493, 'Phase 6', 'Druid', 'Feral Cat', 'Feet', 'Both', 'Boots of the Vanguard'), +(11, 1, 8, 0, 92, 21602, 'Phase 6', 'Druid', 'Feral Cat', 'Wrists', 'Both', 'Qiraji Execution Bracers'), +(11, 1, 12, 0, 92, 19406, 'Phase 6', 'Druid', 'Feral Cat', 'Trinket1', 'Both', 'Drake Fang Talisman'), +(11, 1, 13, 0, 92, 23041, 'Phase 6', 'Druid', 'Feral Cat', 'Trinket2', 'Both', 'Slayer''s Crest'), +(11, 1, 14, 0, 92, 21710, 'Phase 6', 'Druid', 'Feral Cat', 'Back', 'Both', 'Cloak of the Fallen God'), +(11, 1, 15, 0, 92, 22988, 'Phase 6', 'Druid', 'Feral Cat', 'MainHand', 'Both', 'The End of Dreams'), +(11, 1, 16, 0, 92, 13385, 'Phase 6', 'Druid', 'Feral Cat', 'OffHand', 'Both', 'Tome of Knowledge'), +(11, 1, 17, 0, 92, 22397, 'Phase 6', 'Druid', 'Feral Cat', 'Ranged', 'Both', 'Idol of Ferocity'); + +-- ilvl 120 (Pre-Raid) +INSERT INTO `playerbots_bis_gear` VALUES +(11, 1, 0, 0, 120, 8345, 'Pre-Raid', 'Druid', 'Feral Cat', 'Head', 'Both', 'Wolfshead Helm'), +(11, 1, 1, 0, 120, 29381, 'Pre-Raid', 'Druid', 'Feral Cat', 'Neck', 'Both', 'Choker of Vile Intent'), +(11, 1, 2, 0, 120, 27797, 'Pre-Raid', 'Druid', 'Feral Cat', 'Shoulders', 'Both', 'Wastewalker Shoulderpads'), +(11, 1, 4, 0, 120, 29525, 'Pre-Raid', 'Druid', 'Feral Cat', 'Chest', 'Both', 'Primalstrike Vest'), +(11, 1, 5, 0, 120, 29247, 'Pre-Raid', 'Druid', 'Feral Cat', 'Waist', 'Both', 'Girdle of the Deathdealer'), +(11, 1, 6, 0, 120, 31544, 'Pre-Raid', 'Druid', 'Feral Cat', 'Legs', 'Both', 'Clefthoof Hide Leggings'), +(11, 1, 7, 0, 120, 25686, 'Pre-Raid', 'Druid', 'Feral Cat', 'Feet', 'Both', 'Fel Leather Boots'), +(11, 1, 8, 0, 120, 29246, 'Pre-Raid', 'Druid', 'Feral Cat', 'Wrists', 'Both', 'Nightfall Wristguards'), +(11, 1, 9, 0, 120, 29507, 'Pre-Raid', 'Druid', 'Feral Cat', 'Hands', 'Both', 'Windslayer Wraps'), +(11, 1, 10, 0, 120, 30365, 'Pre-Raid', 'Druid', 'Feral Cat', 'Finger1', 'Both', 'Overseer''s Signet'), +(11, 1, 11, 0, 120, 31920, 'Pre-Raid', 'Druid', 'Feral Cat', 'Finger2', 'Both', 'Shaffar''s Band of Brutality'), +(11, 1, 12, 0, 120, 29383, 'Pre-Raid', 'Druid', 'Feral Cat', 'Trinket1', 'Both', 'Bloodlust Brooch'), +(11, 1, 13, 0, 120, 28034, 'Pre-Raid', 'Druid', 'Feral Cat', 'Trinket2', 'Both', 'Hourglass of the Unraveller'), +(11, 1, 14, 0, 120, 31255, 'Pre-Raid', 'Druid', 'Feral Cat', 'Back', 'Both', 'Cloak of the Craft'), +(11, 1, 15, 0, 120, 31334, 'Pre-Raid', 'Druid', 'Feral Cat', 'MainHand', 'Both', 'Staff of Natural Fury'), +(11, 1, 17, 0, 120, 28372, 'Pre-Raid', 'Druid', 'Feral Cat', 'Ranged', 'Both', 'Idol of Feral Shadows'); + +-- ilvl 125 (Phase 1) +INSERT INTO `playerbots_bis_gear` VALUES +(11, 1, 0, 0, 125, 8345, 'Phase 1', 'Druid', 'Feral Cat', 'Head', 'Both', 'Wolfshead Helm'), +(11, 1, 1, 0, 125, 29381, 'Phase 1', 'Druid', 'Feral Cat', 'Neck', 'Both', 'Choker of Vile Intent'), +(11, 1, 2, 0, 125, 29100, 'Phase 1', 'Druid', 'Feral Cat', 'Shoulders', 'Both', 'Mantle of Malorne'), +(11, 1, 4, 0, 125, 29096, 'Phase 1', 'Druid', 'Feral Cat', 'Chest', 'Both', 'Breastplate of Malorne'), +(11, 1, 5, 0, 125, 29247, 'Phase 1', 'Druid', 'Feral Cat', 'Waist', 'Both', 'Girdle of the Deathdealer'), +(11, 1, 6, 0, 125, 28741, 'Phase 1', 'Druid', 'Feral Cat', 'Legs', 'Both', 'Skulker''s Greaves'), +(11, 1, 7, 0, 125, 28545, 'Phase 1', 'Druid', 'Feral Cat', 'Feet', 'Both', 'Edgewalker Longboots'), +(11, 1, 8, 0, 125, 29246, 'Phase 1', 'Druid', 'Feral Cat', 'Wrists', 'Both', 'Nightfall Wristguards'), +(11, 1, 9, 0, 125, 28506, 'Phase 1', 'Druid', 'Feral Cat', 'Hands', 'Both', 'Gloves of Dexterous Manipulation'), +(11, 1, 10, 0, 125, 28757, 'Phase 1', 'Druid', 'Feral Cat', 'Finger1', 'Both', 'Ring of a Thousand Marks'), +(11, 1, 11, 0, 125, 30834, 'Phase 1', 'Druid', 'Feral Cat', 'Finger2', 'Both', 'Shapeshifter''s Signet'), +(11, 1, 12, 0, 125, 28830, 'Phase 1', 'Druid', 'Feral Cat', 'Trinket1', 'Both', 'Dragonspine Trophy'), +(11, 1, 13, 0, 125, 29383, 'Phase 1', 'Druid', 'Feral Cat', 'Trinket2', 'Both', 'Bloodlust Brooch'), +(11, 1, 14, 0, 125, 28660, 'Phase 1', 'Druid', 'Feral Cat', 'Back', 'Both', 'Gilded Thorium Cloak'), +(11, 1, 15, 0, 125, 28658, 'Phase 1', 'Druid', 'Feral Cat', 'MainHand', 'Both', 'Terestian''s Stranglestaff'), +(11, 1, 17, 0, 125, 29390, 'Phase 1', 'Druid', 'Feral Cat', 'Ranged', 'Both', 'Everbloom Idol'); + +-- ilvl 200 (Pre-Raid) +INSERT INTO `playerbots_bis_gear` VALUES +(11, 1, 0, 0, 200, 42550, 'Pre-Raid', 'Druid', 'Feral Cat', 'Head', 'Both', 'Weakness Spectralizers'), +(11, 1, 2, 0, 200, 43481, 'Pre-Raid', 'Druid', 'Feral Cat', 'Shoulders', 'Both', 'Trollwoven Spaulders'), +(11, 1, 4, 0, 200, 39554, 'Pre-Raid', 'Druid', 'Feral Cat', 'Chest', 'Both', 'Heroes'' Dreamwalker Raiments'), +(11, 1, 5, 0, 200, 40694, 'Pre-Raid', 'Druid', 'Feral Cat', 'Waist', 'Both', 'Jorach''s Crocolisk Skin Belt'), +(11, 1, 6, 0, 200, 37644, 'Pre-Raid', 'Druid', 'Feral Cat', 'Legs', 'Both', 'Gored Hide Legguards'), +(11, 1, 7, 0, 200, 44297, 'Pre-Raid', 'Druid', 'Feral Cat', 'Feet', 'Both', 'Boots of the Neverending Path'), +(11, 1, 8, 0, 200, 44203, 'Pre-Raid', 'Druid', 'Feral Cat', 'Wrists', 'Both', 'Dragonfriend Bracers'), +(11, 1, 9, 0, 200, 37409, 'Pre-Raid', 'Druid', 'Feral Cat', 'Hands', 'Both', 'Gilt-Edged Leather Gauntlets'), +(11, 1, 10, 0, 200, 40586, 'Pre-Raid', 'Druid', 'Feral Cat', 'Finger1', 'Both', 'Band of the Kirin Tor'), +(11, 1, 12, 0, 200, 44253, 'Pre-Raid', 'Druid', 'Feral Cat', 'Trinket1', 'Both', 'Darkmoon Card: Greatness'), +(11, 1, 14, 0, 200, 43406, 'Pre-Raid', 'Druid', 'Feral Cat', 'Back', 'Both', 'Cloak of the Gushing Wound'), +(11, 1, 15, 0, 200, 41257, 'Pre-Raid', 'Druid', 'Feral Cat', 'MainHand', 'Both', 'Titansteel Destroyer'), +(11, 1, 17, 0, 200, 40713, 'Pre-Raid', 'Druid', 'Feral Cat', 'Ranged', 'Both', 'Idol of the Ravenous Beast'); + +-- ilvl 224 (Phase 1) +INSERT INTO `playerbots_bis_gear` VALUES +(11, 1, 0, 0, 224, 40473, 'Phase 1', 'Druid', 'Feral Cat', 'Head', 'Both', 'Valorous Dreamwalker Headguard'), +(11, 1, 2, 0, 224, 40494, 'Phase 1', 'Druid', 'Feral Cat', 'Shoulders', 'Both', 'Valorous Dreamwalker Shoulderpads'), +(11, 1, 4, 0, 224, 40539, 'Phase 1', 'Druid', 'Feral Cat', 'Chest', 'Both', 'Chestguard of the Recluse'), +(11, 1, 5, 0, 224, 40205, 'Phase 1', 'Druid', 'Feral Cat', 'Waist', 'Both', 'Stalk-Skin Belt'), +(11, 1, 6, 0, 224, 44011, 'Phase 1', 'Druid', 'Feral Cat', 'Legs', 'Both', 'Leggings of the Honored'), +(11, 1, 7, 0, 224, 40243, 'Phase 1', 'Druid', 'Feral Cat', 'Feet', 'Both', 'Footwraps of Vile Deceit'), +(11, 1, 8, 0, 224, 40738, 'Phase 1', 'Druid', 'Feral Cat', 'Wrists', 'Both', 'Wristwraps of the Cutthroat'), +(11, 1, 9, 0, 224, 40541, 'Phase 1', 'Druid', 'Feral Cat', 'Hands', 'Both', 'Frosted Adroit Handguards'), +(11, 1, 10, 0, 224, 40474, 'Phase 1', 'Druid', 'Feral Cat', 'Finger1', 'Both', 'Surge Needle Ring'), +(11, 1, 12, 0, 224, 42987, 'Phase 1', 'Druid', 'Feral Cat', 'Trinket1', 'Both', 'Darkmoon Card: Greatness'), +(11, 1, 14, 0, 224, 40403, 'Phase 1', 'Druid', 'Feral Cat', 'Back', 'Both', 'Drape of the Deadly Foe'), +(11, 1, 17, 0, 224, 40713, 'Phase 1', 'Druid', 'Feral Cat', 'Ranged', 'Both', 'Idol of the Ravenous Beast'); + +-- ilvl 245 (Phase 2) +INSERT INTO `playerbots_bis_gear` VALUES +(11, 1, 0, 0, 245, 46161, 'Phase 2', 'Druid', 'Feral Cat', 'Head', 'Both', 'Conqueror''s Nightsong Headguard'), +(11, 1, 2, 0, 245, 45245, 'Phase 2', 'Druid', 'Feral Cat', 'Shoulders', 'Both', 'Shoulderpads of the Intruder'), +(11, 1, 4, 0, 245, 45473, 'Phase 2', 'Druid', 'Feral Cat', 'Chest', 'Both', 'Embrace of the Gladiator'), +(11, 1, 5, 0, 245, 46095, 'Phase 2', 'Druid', 'Feral Cat', 'Waist', 'Both', 'Soul-Devouring Cinch'), +(11, 1, 6, 0, 245, 45536, 'Phase 2', 'Druid', 'Feral Cat', 'Legs', 'Both', 'Legguards of Cunning Deception'), +(11, 1, 7, 0, 245, 45564, 'Phase 2', 'Druid', 'Feral Cat', 'Feet', 'Both', 'Footpads of Silence'), +(11, 1, 8, 0, 245, 45869, 'Phase 2', 'Druid', 'Feral Cat', 'Wrists', 'Both', 'Fluxing Energy Coils'), +(11, 1, 9, 0, 245, 46158, 'Phase 2', 'Druid', 'Feral Cat', 'Hands', 'Both', 'Conqueror''s Nightsong Handgrips'), +(11, 1, 10, 0, 245, 45608, 'Phase 2', 'Druid', 'Feral Cat', 'Finger1', 'Both', 'Brann''s Signet Ring'), +(11, 1, 12, 0, 245, 45931, 'Phase 2', 'Druid', 'Feral Cat', 'Trinket1', 'Both', 'Mjolnir Runestone'), +(11, 1, 14, 0, 245, 46032, 'Phase 2', 'Druid', 'Feral Cat', 'Back', 'Both', 'Drape of the Faceless General'), +(11, 1, 17, 0, 245, 40713, 'Phase 2', 'Druid', 'Feral Cat', 'Ranged', 'Both', 'Idol of the Ravenous Beast'); + +-- ilvl 258 (Phase 3) +INSERT INTO `playerbots_bis_gear` VALUES +(11, 1, 0, 0, 258, 48201, 'Phase 3', 'Druid', 'Feral Cat', 'Head', 'Both', 'Headguard of Triumph'), +(11, 1, 2, 0, 258, 48198, 'Phase 3', 'Druid', 'Feral Cat', 'Shoulders', 'Both', 'Shoulderpads of Triumph'), +(11, 1, 4, 0, 258, 48139, 'Phase 3', 'Druid', 'Feral Cat', 'Chest', 'Both', 'Robe of Triumph'), +(11, 1, 5, 1, 258, 47112, 'Phase 3', 'Druid', 'Feral Cat', 'Waist', 'Alliance', 'Belt of the Merciless Killer'), +(11, 1, 5, 2, 258, 47460, 'Phase 3', 'Druid', 'Feral Cat', 'Waist', 'Horde', 'Belt of the Pitiless Killer'), +(11, 1, 6, 0, 258, 45536, 'Phase 3', 'Druid', 'Feral Cat', 'Legs', 'Both', 'Legguards of Cunning Deception'), +(11, 1, 7, 1, 258, 47077, 'Phase 3', 'Druid', 'Feral Cat', 'Feet', 'Alliance', 'Treads of the Icewalker'), +(11, 1, 7, 2, 258, 47445, 'Phase 3', 'Druid', 'Feral Cat', 'Feet', 'Horde', 'Icewalker Treads'), +(11, 1, 8, 1, 258, 47155, 'Phase 3', 'Druid', 'Feral Cat', 'Wrists', 'Alliance', 'Bracers of Dark Determination'), +(11, 1, 8, 2, 258, 47474, 'Phase 3', 'Druid', 'Feral Cat', 'Wrists', 'Horde', 'Armbands of Dark Determination'), +(11, 1, 9, 0, 258, 48202, 'Phase 3', 'Druid', 'Feral Cat', 'Hands', 'Both', 'Handgrips of Triumph'), +(11, 1, 10, 1, 258, 47075, 'Phase 3', 'Druid', 'Feral Cat', 'Finger1', 'Alliance', 'Ring of Callous Aggression'), +(11, 1, 10, 2, 258, 47443, 'Phase 3', 'Druid', 'Feral Cat', 'Finger1', 'Horde', 'Band of Callous Aggression'), +(11, 1, 12, 1, 258, 47131, 'Phase 3', 'Druid', 'Feral Cat', 'Trinket1', 'Alliance', 'Death''s Verdict'), +(11, 1, 12, 2, 258, 47464, 'Phase 3', 'Druid', 'Feral Cat', 'Trinket1', 'Horde', 'Death''s Choice'), +(11, 1, 14, 1, 258, 47545, 'Phase 3', 'Druid', 'Feral Cat', 'Back', 'Alliance', 'Vereesa''s Dexterity'), +(11, 1, 14, 2, 258, 47546, 'Phase 3', 'Druid', 'Feral Cat', 'Back', 'Horde', 'Sylvanas'' Cunning'), +(11, 1, 15, 1, 258, 47130, 'Phase 3', 'Druid', 'Feral Cat', 'MainHand', 'Alliance', 'Lupine Longstaff'), +(11, 1, 15, 2, 258, 47463, 'Phase 3', 'Druid', 'Feral Cat', 'MainHand', 'Horde', 'Twin''s Pact'), +(11, 1, 17, 0, 258, 47668, 'Phase 3', 'Druid', 'Feral Cat', 'Ranged', 'Both', 'Idol of Mutilation'); + +-- ilvl 264 (Phase 4) +INSERT INTO `playerbots_bis_gear` VALUES +(11, 1, 0, 0, 264, 51296, 'Phase 4', 'Druid', 'Feral Cat', 'Head', 'Both', 'Sanctified Lasherweave Headguard'), +(11, 1, 2, 0, 264, 51299, 'Phase 4', 'Druid', 'Feral Cat', 'Shoulders', 'Both', 'Sanctified Lasherweave Shoulderpads'), +(11, 1, 4, 0, 264, 51298, 'Phase 4', 'Druid', 'Feral Cat', 'Chest', 'Both', 'Sanctified Lasherweave Raiment'), +(11, 1, 5, 0, 264, 50707, 'Phase 4', 'Druid', 'Feral Cat', 'Waist', 'Both', 'Astrylian''s Sutured Cinch'), +(11, 1, 6, 0, 264, 51297, 'Phase 4', 'Druid', 'Feral Cat', 'Legs', 'Both', 'Sanctified Lasherweave Legguards'), +(11, 1, 7, 0, 264, 50607, 'Phase 4', 'Druid', 'Feral Cat', 'Feet', 'Both', 'Frostbitten Fur Boots'), +(11, 1, 8, 0, 264, 50670, 'Phase 4', 'Druid', 'Feral Cat', 'Wrists', 'Both', 'Toskk''s Maximized Wristguards'), +(11, 1, 9, 0, 264, 50675, 'Phase 4', 'Druid', 'Feral Cat', 'Hands', 'Both', 'Aldriana''s Gloves of Secrecy'), +(11, 1, 10, 0, 264, 50402, 'Phase 4', 'Druid', 'Feral Cat', 'Finger1', 'Both', 'Ashen Band of Endless Vengeance'), +(11, 1, 12, 0, 264, 50363, 'Phase 4', 'Druid', 'Feral Cat', 'Trinket1', 'Both', 'Deathbringer''s Will'), +(11, 1, 14, 1, 264, 47545, 'Phase 4', 'Druid', 'Feral Cat', 'Back', 'Alliance', 'Vereesa''s Dexterity'), +(11, 1, 14, 2, 264, 47546, 'Phase 4', 'Druid', 'Feral Cat', 'Back', 'Horde', 'Sylvanas'' Cunning'), +(11, 1, 15, 0, 264, 50735, 'Phase 4', 'Druid', 'Feral Cat', 'MainHand', 'Both', 'Oathbinder, Charge of the Ranger-General'), +(11, 1, 17, 0, 264, 50456, 'Phase 4', 'Druid', 'Feral Cat', 'Ranged', 'Both', 'Idol of the Crying Moon'); + +-- ilvl 290 (Phase 5) +INSERT INTO `playerbots_bis_gear` VALUES +(11, 1, 0, 0, 290, 51296, 'Phase 5', 'Druid', 'FeralCat', 'Head', 'Both', 'Sanctified Lasherweave Headguard'), +(11, 1, 1, 0, 290, 50633, 'Phase 5', 'Druid', 'FeralCat', 'Neck', 'Both', 'Sindragosa''s Cruel Claw'), +(11, 1, 2, 0, 290, 51299, 'Phase 5', 'Druid', 'FeralCat', 'Shoulders', 'Both', 'Sanctified Lasherweave Shoulderpads'), +(11, 1, 4, 0, 290, 51298, 'Phase 5', 'Druid', 'FeralCat', 'Chest', 'Both', 'Sanctified Lasherweave Raiment'), +(11, 1, 5, 0, 290, 50707, 'Phase 5', 'Druid', 'FeralCat', 'Waist', 'Both', 'Astrylian''s Sutured Cinch'), +(11, 1, 6, 0, 290, 51297, 'Phase 5', 'Druid', 'FeralCat', 'Legs', 'Both', 'Sanctified Lasherweave Legguards'), +(11, 1, 7, 0, 290, 50607, 'Phase 5', 'Druid', 'FeralCat', 'Feet', 'Both', 'Frostbitten Fur Boots'), +(11, 1, 8, 0, 290, 50670, 'Phase 5', 'Druid', 'FeralCat', 'Wrists', 'Both', 'Toskk''s Maximized Wristguards'), +(11, 1, 9, 0, 290, 50675, 'Phase 5', 'Druid', 'FeralCat', 'Hands', 'Both', 'Aldriana''s Gloves of Secrecy'), +(11, 1, 10, 0, 290, 50618, 'Phase 5', 'Druid', 'FeralCat', 'Finger1', 'Both', 'Frostbrood Sapphire Ring'), +(11, 1, 11, 0, 290, 50402, 'Phase 5', 'Druid', 'FeralCat', 'Finger2', 'Both', 'Ashen Band of Endless Vengeance'), +(11, 1, 12, 0, 290, 50363, 'Phase 5', 'Druid', 'FeralCat', 'Trinket1', 'Both', 'Deathbringer''s Will'), +(11, 1, 13, 0, 290, 54590, 'Phase 5', 'Druid', 'FeralCat', 'Trinket2', 'Both', 'Sharpened Twilight Scale'), +(11, 1, 14, 0, 290, 50653, 'Phase 5', 'Druid', 'FeralCat', 'Back', 'Both', 'Shadowvault Slayer''s Cloak'), +(11, 1, 15, 0, 290, 50735, 'Phase 5', 'Druid', 'FeralCat', 'MainHand', 'Both', 'Oathbinder, Charge of the Ranger-General'), +(11, 1, 17, 0, 290, 50456, 'Phase 5', 'Druid', 'FeralCat', 'Ranged', 'Both', 'Idol of the Crying Moon'); + +-- Restoration (tab 2) +-- ilvl 66 (Phase 1 (Pre-Raid)) +INSERT INTO `playerbots_bis_gear` VALUES +(11, 2, 0, 0, 66, 13102, 'Phase 1 (Pre-Raid)', 'Druid', 'Restoration', 'Head', 'Both', 'Cassandra''s Grace'), +(11, 2, 1, 0, 66, 18723, 'Phase 1 (Pre-Raid)', 'Druid', 'Restoration', 'Neck', 'Both', 'Animated Chain Necklace'), +(11, 2, 2, 0, 66, 15061, 'Phase 1 (Pre-Raid)', 'Druid', 'Restoration', 'Shoulders', 'Both', 'Living Shoulders'), +(11, 2, 4, 0, 66, 13346, 'Phase 1 (Pre-Raid)', 'Druid', 'Restoration', 'Chest', 'Both', 'Robes of the Exalted'), +(11, 2, 5, 0, 66, 14553, 'Phase 1 (Pre-Raid)', 'Druid', 'Restoration', 'Waist', 'Both', 'Sash of Mercy'), +(11, 2, 6, 0, 66, 11841, 'Phase 1 (Pre-Raid)', 'Druid', 'Restoration', 'Legs', 'Both', 'Senior Designer''s Pantaloons'), +(11, 2, 7, 0, 66, 13954, 'Phase 1 (Pre-Raid)', 'Druid', 'Restoration', 'Feet', 'Both', 'Verdant Footpads'), +(11, 2, 8, 0, 66, 13208, 'Phase 1 (Pre-Raid)', 'Druid', 'Restoration', 'Wrists', 'Both', 'Bleak Howler Armguards'), +(11, 2, 9, 0, 66, 10787, 'Phase 1 (Pre-Raid)', 'Druid', 'Restoration', 'Hands', 'Both', 'Atal''ai Gloves'), +(11, 2, 10, 0, 66, 13178, 'Phase 1 (Pre-Raid)', 'Druid', 'Restoration', 'Finger1', 'Both', 'Rosewine Circle'), +(11, 2, 11, 0, 66, 16058, 'Phase 1 (Pre-Raid)', 'Druid', 'Restoration', 'Finger2', 'Both', 'Fordring''s Seal'), +(11, 2, 12, 0, 66, 12930, 'Phase 1 (Pre-Raid)', 'Druid', 'Restoration', 'Trinket1', 'Both', 'Briarwood Reed'), +(11, 2, 13, 0, 66, 11819, 'Phase 1 (Pre-Raid)', 'Druid', 'Restoration', 'Trinket2', 'Both', 'Second Wind'), +(11, 2, 14, 0, 66, 13386, 'Phase 1 (Pre-Raid)', 'Druid', 'Restoration', 'Back', 'Both', 'Archivist Cape'), +(11, 2, 15, 0, 66, 11923, 'Phase 1 (Pre-Raid)', 'Druid', 'Restoration', 'MainHand', 'Both', 'The Hammer of Grace'), +(11, 2, 16, 0, 66, 11928, 'Phase 1 (Pre-Raid)', 'Druid', 'Restoration', 'OffHand', 'Both', 'Thaurissan''s Royal Scepter'); + +-- ilvl 76 (Phase 2 (Pre-Raid)) +INSERT INTO `playerbots_bis_gear` VALUES +(11, 2, 0, 0, 76, 13102, 'Phase 2 (Pre-Raid)', 'Druid', 'Restoration', 'Head', 'Both', 'Cassandra''s Grace'), +(11, 2, 1, 0, 76, 18723, 'Phase 2 (Pre-Raid)', 'Druid', 'Restoration', 'Neck', 'Both', 'Animated Chain Necklace'), +(11, 2, 2, 0, 76, 15061, 'Phase 2 (Pre-Raid)', 'Druid', 'Restoration', 'Shoulders', 'Both', 'Living Shoulders'), +(11, 2, 4, 0, 76, 13346, 'Phase 2 (Pre-Raid)', 'Druid', 'Restoration', 'Chest', 'Both', 'Robes of the Exalted'), +(11, 2, 5, 0, 76, 14553, 'Phase 2 (Pre-Raid)', 'Druid', 'Restoration', 'Waist', 'Both', 'Sash of Mercy'), +(11, 2, 6, 0, 76, 18386, 'Phase 2 (Pre-Raid)', 'Druid', 'Restoration', 'Legs', 'Both', 'Padre''s Trousers'), +(11, 2, 7, 0, 76, 13954, 'Phase 2 (Pre-Raid)', 'Druid', 'Restoration', 'Feet', 'Both', 'Verdant Footpads'), +(11, 2, 8, 0, 76, 18525, 'Phase 2 (Pre-Raid)', 'Druid', 'Restoration', 'Wrists', 'Both', 'Bracers of Prosperity'), +(11, 2, 9, 0, 76, 10787, 'Phase 2 (Pre-Raid)', 'Druid', 'Restoration', 'Hands', 'Both', 'Atal''ai Gloves'), +(11, 2, 10, 0, 76, 13178, 'Phase 2 (Pre-Raid)', 'Druid', 'Restoration', 'Finger1', 'Both', 'Rosewine Circle'), +(11, 2, 11, 0, 76, 16058, 'Phase 2 (Pre-Raid)', 'Druid', 'Restoration', 'Finger2', 'Both', 'Fordring''s Seal'), +(11, 2, 12, 0, 76, 18470, 'Phase 2 (Pre-Raid)', 'Druid', 'Restoration', 'Trinket1', 'Both', 'Royal Seal of Eldre''Thalas'), +(11, 2, 13, 0, 76, 18371, 'Phase 2 (Pre-Raid)', 'Druid', 'Restoration', 'Trinket2', 'Both', 'Mindtap Talisman'), +(11, 2, 14, 0, 76, 18510, 'Phase 2 (Pre-Raid)', 'Druid', 'Restoration', 'Back', 'Both', 'Hide of the Wild'), +(11, 2, 15, 0, 76, 11923, 'Phase 2 (Pre-Raid)', 'Druid', 'Restoration', 'MainHand', 'Both', 'The Hammer of Grace'), +(11, 2, 16, 0, 76, 18523, 'Phase 2 (Pre-Raid)', 'Druid', 'Restoration', 'OffHand', 'Both', 'Brightly Glowing Stone'); + +-- ilvl 78 (Phase 2) +INSERT INTO `playerbots_bis_gear` VALUES +(11, 2, 0, 0, 78, 19132, 'Phase 2', 'Druid', 'Restoration', 'Head', 'Both', 'Crystal Adorned Crown'), +(11, 2, 1, 0, 78, 18723, 'Phase 2', 'Druid', 'Restoration', 'Neck', 'Both', 'Animated Chain Necklace'), +(11, 2, 2, 0, 78, 18810, 'Phase 2', 'Druid', 'Restoration', 'Shoulders', 'Both', 'Wild Growth Spaulders'), +(11, 2, 4, 0, 78, 13346, 'Phase 2', 'Druid', 'Restoration', 'Chest', 'Both', 'Robes of the Exalted'), +(11, 2, 5, 0, 78, 19162, 'Phase 2', 'Druid', 'Restoration', 'Waist', 'Both', 'Corehound Belt'), +(11, 2, 6, 0, 78, 18875, 'Phase 2', 'Druid', 'Restoration', 'Legs', 'Both', 'Salamander Scale Pants'), +(11, 2, 7, 0, 78, 16829, 'Phase 2', 'Druid', 'Restoration', 'Feet', 'Both', 'Cenarion Boots'), +(11, 2, 8, 0, 78, 18263, 'Phase 2', 'Druid', 'Restoration', 'Wrists', 'Both', 'Flarecore Wraps'), +(11, 2, 9, 0, 78, 10787, 'Phase 2', 'Druid', 'Restoration', 'Hands', 'Both', 'Atal''ai Gloves'), +(11, 2, 10, 0, 78, 19140, 'Phase 2', 'Druid', 'Restoration', 'Finger1', 'Both', 'Cauterizing Band'), +(11, 2, 11, 0, 78, 19140, 'Phase 2', 'Druid', 'Restoration', 'Finger2', 'Both', 'Cauterizing Band'), +(11, 2, 12, 0, 78, 18470, 'Phase 2', 'Druid', 'Restoration', 'Trinket1', 'Both', 'Royal Seal of Eldre''Thalas'), +(11, 2, 13, 0, 78, 17064, 'Phase 2', 'Druid', 'Restoration', 'Trinket2', 'Both', 'Shard of the Scale'), +(11, 2, 14, 0, 78, 18510, 'Phase 2', 'Druid', 'Restoration', 'Back', 'Both', 'Hide of the Wild'), +(11, 2, 15, 0, 78, 17070, 'Phase 2', 'Druid', 'Restoration', 'MainHand', 'Both', 'Fang of the Mystics'), +(11, 2, 16, 0, 78, 18523, 'Phase 2', 'Druid', 'Restoration', 'OffHand', 'Both', 'Brightly Glowing Stone'); + +-- ilvl 83 (Phase 3) +INSERT INTO `playerbots_bis_gear` VALUES +(11, 2, 0, 0, 83, 19132, 'Phase 3', 'Druid', 'Restoration', 'Head', 'Both', 'Crystal Adorned Crown'), +(11, 2, 1, 0, 83, 18723, 'Phase 3', 'Druid', 'Restoration', 'Neck', 'Both', 'Animated Chain Necklace'), +(11, 2, 2, 0, 83, 18810, 'Phase 3', 'Druid', 'Restoration', 'Shoulders', 'Both', 'Wild Growth Spaulders'), +(11, 2, 4, 0, 83, 13346, 'Phase 3', 'Druid', 'Restoration', 'Chest', 'Both', 'Robes of the Exalted'), +(11, 2, 5, 0, 83, 19162, 'Phase 3', 'Druid', 'Restoration', 'Waist', 'Both', 'Corehound Belt'), +(11, 2, 6, 0, 83, 19385, 'Phase 3', 'Druid', 'Restoration', 'Legs', 'Both', 'Empowered Leggings'), +(11, 2, 7, 0, 83, 16898, 'Phase 3', 'Druid', 'Restoration', 'Feet', 'Both', 'Stormrage Boots'), +(11, 2, 8, 0, 83, 16904, 'Phase 3', 'Druid', 'Restoration', 'Wrists', 'Both', 'Stormrage Bracers'), +(11, 2, 9, 0, 83, 16899, 'Phase 3', 'Druid', 'Restoration', 'Hands', 'Both', 'Stormrage Handguards'), +(11, 2, 10, 0, 83, 19140, 'Phase 3', 'Druid', 'Restoration', 'Finger1', 'Both', 'Cauterizing Band'), +(11, 2, 11, 0, 83, 19382, 'Phase 3', 'Druid', 'Restoration', 'Finger2', 'Both', 'Pure Elementium Band'), +(11, 2, 12, 0, 83, 19395, 'Phase 3', 'Druid', 'Restoration', 'Trinket1', 'Both', 'Rejuvenating Gem'), +(11, 2, 13, 0, 83, 17064, 'Phase 3', 'Druid', 'Restoration', 'Trinket2', 'Both', 'Shard of the Scale'), +(11, 2, 14, 0, 83, 19430, 'Phase 3', 'Druid', 'Restoration', 'Back', 'Both', 'Shroud of Pure Thought'), +(11, 2, 15, 0, 83, 19360, 'Phase 3', 'Druid', 'Restoration', 'MainHand', 'Both', 'Lok''amir il Romathis'), +(11, 2, 16, 0, 83, 19312, 'Phase 3', 'Druid', 'Restoration', 'OffHand', 'Both', 'Lei of the Lifegiver'); + +-- ilvl 88 (Phase 5) +INSERT INTO `playerbots_bis_gear` VALUES +(11, 2, 0, 0, 88, 20628, 'Phase 5', 'Druid', 'Restoration', 'Head', 'Both', 'Deviate Growth Cap'), +(11, 2, 1, 0, 88, 21712, 'Phase 5', 'Druid', 'Restoration', 'Neck', 'Both', 'Amulet of the Fallen God'), +(11, 2, 2, 0, 88, 18810, 'Phase 5', 'Druid', 'Restoration', 'Shoulders', 'Both', 'Wild Growth Spaulders'), +(11, 2, 4, 0, 88, 21663, 'Phase 5', 'Druid', 'Restoration', 'Chest', 'Both', 'Robes of the Guardian Saint'), +(11, 2, 5, 0, 88, 21582, 'Phase 5', 'Druid', 'Restoration', 'Waist', 'Both', 'Grasp of the Old God'), +(11, 2, 6, 0, 88, 19385, 'Phase 5', 'Druid', 'Restoration', 'Legs', 'Both', 'Empowered Leggings'), +(11, 2, 7, 0, 88, 19437, 'Phase 5', 'Druid', 'Restoration', 'Feet', 'Both', 'Boots of Pure Thought'), +(11, 2, 8, 0, 88, 21604, 'Phase 5', 'Druid', 'Restoration', 'Wrists', 'Both', 'Bracelets of Royal Redemption'), +(11, 2, 9, 0, 88, 21617, 'Phase 5', 'Druid', 'Restoration', 'Hands', 'Both', 'Wasphide Gauntlets'), +(11, 2, 10, 0, 88, 21620, 'Phase 5', 'Druid', 'Restoration', 'Finger1', 'Both', 'Ring of the Martyr'), +(11, 2, 11, 0, 88, 19382, 'Phase 5', 'Druid', 'Restoration', 'Finger2', 'Both', 'Pure Elementium Band'), +(11, 2, 12, 0, 88, 19395, 'Phase 5', 'Druid', 'Restoration', 'Trinket1', 'Both', 'Rejuvenating Gem'), +(11, 2, 13, 0, 88, 17064, 'Phase 5', 'Druid', 'Restoration', 'Trinket2', 'Both', 'Shard of the Scale'), +(11, 2, 14, 0, 88, 21583, 'Phase 5', 'Druid', 'Restoration', 'Back', 'Both', 'Cloak of Clarity'), +(11, 2, 15, 0, 88, 21839, 'Phase 5', 'Druid', 'Restoration', 'MainHand', 'Both', 'Scepter of the False Prophet'), +(11, 2, 16, 0, 88, 21666, 'Phase 5', 'Druid', 'Restoration', 'OffHand', 'Both', 'Sartura''s Might'), +(11, 2, 17, 0, 88, 22399, 'Phase 5', 'Druid', 'Restoration', 'Ranged', 'Both', 'Idol of Health'); + +-- ilvl 92 (Phase 6) +INSERT INTO `playerbots_bis_gear` VALUES +(11, 2, 0, 0, 92, 20628, 'Phase 6', 'Druid', 'Restoration', 'Head', 'Both', 'Deviate Growth Cap'), +(11, 2, 1, 0, 92, 21712, 'Phase 6', 'Druid', 'Restoration', 'Neck', 'Both', 'Amulet of the Fallen God'), +(11, 2, 2, 0, 92, 22491, 'Phase 6', 'Druid', 'Restoration', 'Shoulders', 'Both', 'Dreamwalker Spaulders'), +(11, 2, 4, 0, 92, 22488, 'Phase 6', 'Druid', 'Restoration', 'Chest', 'Both', 'Dreamwalker Tunic'), +(11, 2, 5, 0, 92, 21582, 'Phase 6', 'Druid', 'Restoration', 'Waist', 'Both', 'Grasp of the Old God'), +(11, 2, 6, 0, 92, 22489, 'Phase 6', 'Druid', 'Restoration', 'Legs', 'Both', 'Dreamwalker Legguards'), +(11, 2, 7, 0, 92, 22492, 'Phase 6', 'Druid', 'Restoration', 'Feet', 'Both', 'Dreamwalker Boots'), +(11, 2, 8, 0, 92, 21604, 'Phase 6', 'Druid', 'Restoration', 'Wrists', 'Both', 'Bracelets of Royal Redemption'), +(11, 2, 9, 0, 92, 22493, 'Phase 6', 'Druid', 'Restoration', 'Hands', 'Both', 'Dreamwalker Handguards'), +(11, 2, 10, 0, 92, 21620, 'Phase 6', 'Druid', 'Restoration', 'Finger1', 'Both', 'Ring of the Martyr'), +(11, 2, 11, 0, 92, 22939, 'Phase 6', 'Druid', 'Restoration', 'Finger2', 'Both', 'Band of Unanswered Prayers'), +(11, 2, 12, 0, 92, 23047, 'Phase 6', 'Druid', 'Restoration', 'Trinket1', 'Both', 'Eye of the Dead'), +(11, 2, 13, 0, 92, 23027, 'Phase 6', 'Druid', 'Restoration', 'Trinket2', 'Both', 'Warmth of Forgiveness'), +(11, 2, 14, 0, 92, 22960, 'Phase 6', 'Druid', 'Restoration', 'Back', 'Both', 'Cloak of Suturing'), +(11, 2, 15, 0, 92, 23056, 'Phase 6', 'Druid', 'Restoration', 'MainHand', 'Both', 'Hammer of the Twisting Nether'), +(11, 2, 16, 0, 92, 23048, 'Phase 6', 'Druid', 'Restoration', 'OffHand', 'Both', 'Sapphiron''s Right Eye'), +(11, 2, 17, 0, 92, 22399, 'Phase 6', 'Druid', 'Restoration', 'Ranged', 'Both', 'Idol of Health'); + +-- ilvl 120 (Pre-Raid) +INSERT INTO `playerbots_bis_gear` VALUES +(11, 2, 0, 0, 120, 24264, 'Pre-Raid', 'Druid', 'Restoration', 'Head', 'Both', 'Whitemend Hood'), +(11, 2, 1, 0, 120, 30377, 'Pre-Raid', 'Druid', 'Restoration', 'Neck', 'Both', 'Karja''s Medallion'), +(11, 2, 2, 0, 120, 21874, 'Pre-Raid', 'Druid', 'Restoration', 'Shoulders', 'Both', 'Primal Mooncloth Shoulders'), +(11, 2, 4, 0, 120, 21875, 'Pre-Raid', 'Druid', 'Restoration', 'Chest', 'Both', 'Primal Mooncloth Robe'), +(11, 2, 5, 0, 120, 21873, 'Pre-Raid', 'Druid', 'Restoration', 'Waist', 'Both', 'Primal Mooncloth Belt'), +(11, 2, 6, 0, 120, 24261, 'Pre-Raid', 'Druid', 'Restoration', 'Legs', 'Both', 'Whitemend Pants'), +(11, 2, 7, 0, 120, 27411, 'Pre-Raid', 'Druid', 'Restoration', 'Feet', 'Both', 'Slippers of Serenity'), +(11, 2, 8, 0, 120, 29183, 'Pre-Raid', 'Druid', 'Restoration', 'Wrists', 'Both', 'Bindings of the Timewalker'), +(11, 2, 9, 0, 120, 29506, 'Pre-Raid', 'Druid', 'Restoration', 'Hands', 'Both', 'Gloves of the Living Touch'), +(11, 2, 10, 0, 120, 27780, 'Pre-Raid', 'Druid', 'Restoration', 'Finger1', 'Both', 'Ring of Fabled Hope'), +(11, 2, 11, 0, 120, 31383, 'Pre-Raid', 'Druid', 'Restoration', 'Finger2', 'Both', 'Spiritualist''s Mark of the Sha''tar'), +(11, 2, 12, 0, 120, 29376, 'Pre-Raid', 'Druid', 'Restoration', 'Trinket1', 'Both', 'Essence of the Martyr'), +(11, 2, 13, 0, 120, 30841, 'Pre-Raid', 'Druid', 'Restoration', 'Trinket2', 'Both', 'Lower City Prayerbook'), +(11, 2, 14, 0, 120, 24254, 'Pre-Raid', 'Druid', 'Restoration', 'Back', 'Both', 'White Remedy Cape'), +(11, 2, 15, 0, 120, 23556, 'Pre-Raid', 'Druid', 'Restoration', 'MainHand', 'Both', 'Hand of Eternity'), +(11, 2, 16, 0, 120, 29274, 'Pre-Raid', 'Druid', 'Restoration', 'OffHand', 'Both', 'Tears of Heaven'), +(11, 2, 17, 0, 120, 27886, 'Pre-Raid', 'Druid', 'Restoration', 'Ranged', 'Both', 'Idol of the Emerald Queen'); + +-- ilvl 125 (Phase 1) +INSERT INTO `playerbots_bis_gear` VALUES +(11, 2, 0, 0, 125, 24264, 'Phase 1', 'Druid', 'Restoration', 'Head', 'Both', 'Whitemend Hood'), +(11, 2, 1, 0, 125, 30726, 'Phase 1', 'Druid', 'Restoration', 'Neck', 'Both', 'Archaic Charm of Presence'), +(11, 2, 2, 0, 125, 21874, 'Phase 1', 'Druid', 'Restoration', 'Shoulders', 'Both', 'Primal Mooncloth Shoulders'), +(11, 2, 4, 0, 125, 21875, 'Phase 1', 'Druid', 'Restoration', 'Chest', 'Both', 'Primal Mooncloth Robe'), +(11, 2, 5, 0, 125, 21873, 'Phase 1', 'Druid', 'Restoration', 'Waist', 'Both', 'Primal Mooncloth Belt'), +(11, 2, 6, 0, 125, 30727, 'Phase 1', 'Druid', 'Restoration', 'Legs', 'Both', 'Gilded Trousers of Benediction'), +(11, 2, 7, 0, 125, 30737, 'Phase 1', 'Druid', 'Restoration', 'Feet', 'Both', 'Gold-Leaf Wildboots'), +(11, 2, 8, 0, 125, 29183, 'Phase 1', 'Druid', 'Restoration', 'Wrists', 'Both', 'Bindings of the Timewalker'), +(11, 2, 9, 0, 125, 28521, 'Phase 1', 'Druid', 'Restoration', 'Hands', 'Both', 'Mitts of the Treemender'), +(11, 2, 10, 0, 125, 28763, 'Phase 1', 'Druid', 'Restoration', 'Finger1', 'Both', 'Jade Ring of the Everliving'), +(11, 2, 11, 0, 125, 30736, 'Phase 1', 'Druid', 'Restoration', 'Finger2', 'Both', 'Ring of Flowing Light'), +(11, 2, 12, 0, 125, 29376, 'Phase 1', 'Druid', 'Restoration', 'Trinket1', 'Both', 'Essence of the Martyr'), +(11, 2, 13, 0, 125, 30841, 'Phase 1', 'Druid', 'Restoration', 'Trinket2', 'Both', 'Lower City Prayerbook'), +(11, 2, 14, 0, 125, 31329, 'Phase 1', 'Druid', 'Restoration', 'Back', 'Both', 'Lifegiving Cloak'), +(11, 2, 15, 0, 125, 28771, 'Phase 1', 'Druid', 'Restoration', 'MainHand', 'Both', 'Light''s Justice'), +(11, 2, 16, 0, 125, 29274, 'Phase 1', 'Druid', 'Restoration', 'OffHand', 'Both', 'Tears of Heaven'), +(11, 2, 17, 0, 125, 27886, 'Phase 1', 'Druid', 'Restoration', 'Ranged', 'Both', 'Idol of the Emerald Queen'); + +-- ilvl 141 (Phase 2) +INSERT INTO `playerbots_bis_gear` VALUES +(11, 2, 0, 0, 141, 30219, 'Phase 2', 'Druid', 'Restoration', 'Head', 'Both', 'Nordrassil Headguard'), +(11, 2, 1, 0, 141, 30018, 'Phase 2', 'Druid', 'Restoration', 'Neck', 'Both', 'Lord Sanguinar''s Claim'), +(11, 2, 2, 0, 141, 30221, 'Phase 2', 'Druid', 'Restoration', 'Shoulders', 'Both', 'Nordrassil Life-Mantle'), +(11, 2, 4, 0, 141, 30216, 'Phase 2', 'Druid', 'Restoration', 'Chest', 'Both', 'Nordrassil Chestguard'), +(11, 2, 5, 0, 141, 21873, 'Phase 2', 'Druid', 'Restoration', 'Waist', 'Both', 'Primal Mooncloth Belt'), +(11, 2, 6, 0, 141, 30727, 'Phase 2', 'Druid', 'Restoration', 'Legs', 'Both', 'Gilded Trousers of Benediction'), +(11, 2, 7, 0, 141, 30737, 'Phase 2', 'Druid', 'Restoration', 'Feet', 'Both', 'Gold-Leaf Wildboots'), +(11, 2, 8, 0, 141, 30062, 'Phase 2', 'Druid', 'Restoration', 'Wrists', 'Both', 'Grove-Bands of Remulos'), +(11, 2, 9, 0, 141, 28521, 'Phase 2', 'Druid', 'Restoration', 'Hands', 'Both', 'Mitts of the Treemender'), +(11, 2, 10, 0, 141, 30110, 'Phase 2', 'Druid', 'Restoration', 'Finger1', 'Both', 'Coral Band of the Revived'), +(11, 2, 11, 0, 141, 28763, 'Phase 2', 'Druid', 'Restoration', 'Finger2', 'Both', 'Jade Ring of the Everliving'), +(11, 2, 12, 0, 141, 29376, 'Phase 2', 'Druid', 'Restoration', 'Trinket1', 'Both', 'Essence of the Martyr'), +(11, 2, 13, 0, 141, 30841, 'Phase 2', 'Druid', 'Restoration', 'Trinket2', 'Both', 'Lower City Prayerbook'), +(11, 2, 14, 0, 141, 29989, 'Phase 2', 'Druid', 'Restoration', 'Back', 'Both', 'Sunshower Light Cloak'), +(11, 2, 15, 0, 141, 30108, 'Phase 2', 'Druid', 'Restoration', 'MainHand', 'Both', 'Lightfathom Scepter'), +(11, 2, 16, 0, 141, 29274, 'Phase 2', 'Druid', 'Restoration', 'OffHand', 'Both', 'Tears of Heaven'), +(11, 2, 17, 0, 141, 27886, 'Phase 2', 'Druid', 'Restoration', 'Ranged', 'Both', 'Idol of the Emerald Queen'); + +-- ilvl 156 (Phase 3) +INSERT INTO `playerbots_bis_gear` VALUES +(11, 2, 0, 0, 156, 31037, 'Phase 3', 'Druid', 'Restoration', 'Head', 'Both', 'Thunderheart Helmet'), +(11, 2, 1, 0, 156, 30018, 'Phase 3', 'Druid', 'Restoration', 'Neck', 'Both', 'Lord Sanguinar''s Claim'), +(11, 2, 2, 0, 156, 32583, 'Phase 3', 'Druid', 'Restoration', 'Shoulders', 'Both', 'Shoulderpads of Renewed Life'), +(11, 2, 4, 0, 156, 31041, 'Phase 3', 'Druid', 'Restoration', 'Chest', 'Both', 'Thunderheart Tunic'), +(11, 2, 5, 0, 156, 30895, 'Phase 3', 'Druid', 'Restoration', 'Waist', 'Both', 'Angelista''s Sash'), +(11, 2, 6, 0, 156, 30912, 'Phase 3', 'Druid', 'Restoration', 'Legs', 'Both', 'Leggings of Eternity'), +(11, 2, 7, 0, 156, 30737, 'Phase 3', 'Druid', 'Restoration', 'Feet', 'Both', 'Gold-Leaf Wildboots'), +(11, 2, 8, 0, 156, 32584, 'Phase 3', 'Druid', 'Restoration', 'Wrists', 'Both', 'Swiftheal Wraps'), +(11, 2, 9, 0, 156, 32328, 'Phase 3', 'Druid', 'Restoration', 'Hands', 'Both', 'Botanist''s Gloves of Growth'), +(11, 2, 10, 0, 156, 32528, 'Phase 3', 'Druid', 'Restoration', 'Finger1', 'Both', 'Blessed Band of Karabor'), +(11, 2, 11, 0, 156, 32528, 'Phase 3', 'Druid', 'Restoration', 'Finger2', 'Both', 'Blessed Band of Karabor'), +(11, 2, 12, 0, 156, 29376, 'Phase 3', 'Druid', 'Restoration', 'Trinket1', 'Both', 'Essence of the Martyr'), +(11, 2, 13, 0, 156, 32496, 'Phase 3', 'Druid', 'Restoration', 'Trinket2', 'Both', 'Memento of Tyrande'), +(11, 2, 14, 0, 156, 32524, 'Phase 3', 'Druid', 'Restoration', 'Back', 'Both', 'Shroud of the Highborne'), +(11, 2, 15, 0, 156, 32500, 'Phase 3', 'Druid', 'Restoration', 'MainHand', 'Both', 'Crystal Spire of Karabor'), +(11, 2, 16, 0, 156, 30911, 'Phase 3', 'Druid', 'Restoration', 'OffHand', 'Both', 'Scepter of Purification'), +(11, 2, 17, 0, 156, 27886, 'Phase 3', 'Druid', 'Restoration', 'Ranged', 'Both', 'Idol of the Emerald Queen'); + +-- ilvl 164 (Phase 4) +INSERT INTO `playerbots_bis_gear` VALUES +(11, 2, 0, 0, 164, 31037, 'Phase 4', 'Druid', 'Restoration', 'Head', 'Both', 'Thunderheart Helmet'), +(11, 2, 1, 0, 164, 33281, 'Phase 4', 'Druid', 'Restoration', 'Neck', 'Both', 'Brooch of Nature''s Mercy'), +(11, 2, 2, 0, 164, 32583, 'Phase 4', 'Druid', 'Restoration', 'Shoulders', 'Both', 'Shoulderpads of Renewed Life'), +(11, 2, 4, 0, 164, 31041, 'Phase 4', 'Druid', 'Restoration', 'Chest', 'Both', 'Thunderheart Tunic'), +(11, 2, 5, 0, 164, 30895, 'Phase 4', 'Druid', 'Restoration', 'Waist', 'Both', 'Angelista''s Sash'), +(11, 2, 6, 0, 164, 30912, 'Phase 4', 'Druid', 'Restoration', 'Legs', 'Both', 'Leggings of Eternity'), +(11, 2, 7, 0, 164, 30737, 'Phase 4', 'Druid', 'Restoration', 'Feet', 'Both', 'Gold-Leaf Wildboots'), +(11, 2, 8, 0, 164, 32584, 'Phase 4', 'Druid', 'Restoration', 'Wrists', 'Both', 'Swiftheal Wraps'), +(11, 2, 9, 0, 164, 32328, 'Phase 4', 'Druid', 'Restoration', 'Hands', 'Both', 'Botanist''s Gloves of Growth'), +(11, 2, 10, 0, 164, 32528, 'Phase 4', 'Druid', 'Restoration', 'Finger1', 'Both', 'Blessed Band of Karabor'), +(11, 2, 11, 0, 164, 32528, 'Phase 4', 'Druid', 'Restoration', 'Finger2', 'Both', 'Blessed Band of Karabor'), +(11, 2, 12, 0, 164, 29376, 'Phase 4', 'Druid', 'Restoration', 'Trinket1', 'Both', 'Essence of the Martyr'), +(11, 2, 13, 0, 164, 32496, 'Phase 4', 'Druid', 'Restoration', 'Trinket2', 'Both', 'Memento of Tyrande'), +(11, 2, 14, 0, 164, 32524, 'Phase 4', 'Druid', 'Restoration', 'Back', 'Both', 'Shroud of the Highborne'), +(11, 2, 15, 0, 164, 32500, 'Phase 4', 'Druid', 'Restoration', 'MainHand', 'Both', 'Crystal Spire of Karabor'), +(11, 2, 16, 0, 164, 30911, 'Phase 4', 'Druid', 'Restoration', 'OffHand', 'Both', 'Scepter of Purification'), +(11, 2, 17, 0, 164, 27886, 'Phase 4', 'Druid', 'Restoration', 'Ranged', 'Both', 'Idol of the Emerald Queen'); + +-- ilvl 200 (Pre-Raid) +INSERT INTO `playerbots_bis_gear` VALUES +(11, 2, 0, 0, 200, 42554, 'Pre-Raid', 'Druid', 'Restoration', 'Head', 'Both', 'Greensight Gogs'), +(11, 2, 1, 0, 200, 42647, 'Pre-Raid', 'Druid', 'Restoration', 'Neck', 'Both', 'Titanium Spellshock Necklace'), +(11, 2, 2, 0, 200, 37368, 'Pre-Raid', 'Druid', 'Restoration', 'Shoulders', 'Both', 'Silent Spectator Shoulderpads'), +(11, 2, 4, 0, 200, 42102, 'Pre-Raid', 'Druid', 'Restoration', 'Chest', 'Both', 'Spellweave Robe'), +(11, 2, 5, 0, 200, 37643, 'Pre-Raid', 'Druid', 'Restoration', 'Waist', 'Both', 'Sash of Blood Removal'), +(11, 2, 6, 0, 200, 37791, 'Pre-Raid', 'Druid', 'Restoration', 'Legs', 'Both', 'Leggings of the Winged Serpent'), +(11, 2, 7, 0, 200, 43502, 'Pre-Raid', 'Druid', 'Restoration', 'Feet', 'Both', 'Earthgiving Boots'), +(11, 2, 8, 0, 200, 37696, 'Pre-Raid', 'Druid', 'Restoration', 'Wrists', 'Both', 'Plague-Infected Bracers'), +(11, 2, 9, 0, 200, 37230, 'Pre-Raid', 'Druid', 'Restoration', 'Hands', 'Both', 'Grotto Mist Gloves'), +(11, 2, 10, 0, 200, 37694, 'Pre-Raid', 'Druid', 'Restoration', 'Finger1', 'Both', 'Band of Guile'), +(11, 2, 12, 0, 200, 40685, 'Pre-Raid', 'Druid', 'Restoration', 'Trinket1', 'Both', 'The Egg of Mortal Essence'), +(11, 2, 14, 0, 200, 41609, 'Pre-Raid', 'Druid', 'Restoration', 'Back', 'Both', 'Wispcloak'), +(11, 2, 17, 0, 200, 40711, 'Pre-Raid', 'Druid', 'Restoration', 'Ranged', 'Both', 'Idol of Lush Moss'); + +-- ilvl 224 (Phase 1) +INSERT INTO `playerbots_bis_gear` VALUES +(11, 2, 0, 0, 224, 44007, 'Phase 1', 'Druid', 'Restoration', 'Head', 'Both', 'Headpiece of Reconciliation'), +(11, 2, 1, 0, 224, 44661, 'Phase 1', 'Druid', 'Restoration', 'Neck', 'Both', 'Wyrmrest Necklace of Power'), +(11, 2, 2, 0, 224, 39719, 'Phase 1', 'Druid', 'Restoration', 'Shoulders', 'Both', 'Mantle of the Locusts'), +(11, 2, 4, 0, 224, 44002, 'Phase 1', 'Druid', 'Restoration', 'Chest', 'Both', 'The Sanctum''s Flowing Vestments'), +(11, 2, 5, 0, 224, 40561, 'Phase 1', 'Druid', 'Restoration', 'Waist', 'Both', 'Leash of Heedless Magic'), +(11, 2, 6, 0, 224, 40379, 'Phase 1', 'Druid', 'Restoration', 'Legs', 'Both', 'Legguards of the Boneyard'), +(11, 2, 7, 0, 224, 40558, 'Phase 1', 'Druid', 'Restoration', 'Feet', 'Both', 'Arcanic Tramplers'), +(11, 2, 8, 0, 224, 44008, 'Phase 1', 'Druid', 'Restoration', 'Wrists', 'Both', 'Unsullied Cuffs'), +(11, 2, 9, 0, 224, 40460, 'Phase 1', 'Druid', 'Restoration', 'Hands', 'Both', 'Valorous Dreamwalker Handguards'), +(11, 2, 10, 0, 224, 40375, 'Phase 1', 'Druid', 'Restoration', 'Finger1', 'Both', 'Ring of Decaying Beauty'), +(11, 2, 12, 0, 224, 40432, 'Phase 1', 'Druid', 'Restoration', 'Trinket1', 'Both', 'Illustration of the Dragon Soul'), +(11, 2, 14, 0, 224, 44005, 'Phase 1', 'Druid', 'Restoration', 'Back', 'Both', 'Pennant Cloak'), +(11, 2, 17, 0, 224, 40342, 'Phase 1', 'Druid', 'Restoration', 'Ranged', 'Both', 'Idol of Awakening'); + +-- ilvl 245 (Phase 2) +INSERT INTO `playerbots_bis_gear` VALUES +(11, 2, 0, 0, 245, 46184, 'Phase 2', 'Druid', 'Restoration', 'Head', 'Both', 'Conqueror''s Nightsong Headpiece'), +(11, 2, 1, 0, 245, 45243, 'Phase 2', 'Druid', 'Restoration', 'Neck', 'Both', 'Sapphire Amulet of Renewal'), +(11, 2, 2, 0, 245, 46187, 'Phase 2', 'Druid', 'Restoration', 'Shoulders', 'Both', 'Conqueror''s Nightsong Spaulders'), +(11, 2, 4, 0, 245, 45519, 'Phase 2', 'Druid', 'Restoration', 'Chest', 'Both', 'Vestments of the Blind Denizen'), +(11, 2, 5, 0, 245, 45616, 'Phase 2', 'Druid', 'Restoration', 'Waist', 'Both', 'Star-beaded Clutch'), +(11, 2, 6, 0, 245, 46185, 'Phase 2', 'Druid', 'Restoration', 'Legs', 'Both', 'Conqueror''s Nightsong Leggings'), +(11, 2, 7, 0, 245, 45135, 'Phase 2', 'Druid', 'Restoration', 'Feet', 'Both', 'Boots of Fiery Resolution'), +(11, 2, 8, 0, 245, 45446, 'Phase 2', 'Druid', 'Restoration', 'Wrists', 'Both', 'Grasps of Reason'), +(11, 2, 9, 0, 245, 46183, 'Phase 2', 'Druid', 'Restoration', 'Hands', 'Both', 'Conqueror''s Nightsong Handguards'), +(11, 2, 10, 0, 245, 45495, 'Phase 2', 'Druid', 'Restoration', 'Finger1', 'Both', 'Conductive Seal'), +(11, 2, 12, 0, 245, 45535, 'Phase 2', 'Druid', 'Restoration', 'Trinket1', 'Both', 'Show of Faith'), +(11, 2, 14, 0, 245, 45618, 'Phase 2', 'Druid', 'Restoration', 'Back', 'Both', 'Sunglimmer Cloak'), +(11, 2, 17, 0, 245, 40342, 'Phase 2', 'Druid', 'Restoration', 'Ranged', 'Both', 'Idol of Awakening'); + +-- ilvl 258 (Phase 3) +INSERT INTO `playerbots_bis_gear` VALUES +(11, 2, 0, 1, 258, 48141, 'Phase 3', 'Druid', 'Restoration', 'Head', 'Alliance', 'Malfurion''s Headpiece of Triumph'), +(11, 2, 1, 1, 258, 47144, 'Phase 3', 'Druid', 'Restoration', 'Neck', 'Alliance', 'Wail of the Val''kyr'), +(11, 2, 1, 2, 258, 47468, 'Phase 3', 'Druid', 'Restoration', 'Neck', 'Horde', 'Cry of the Val''kyr'), +(11, 2, 2, 1, 258, 48138, 'Phase 3', 'Druid', 'Restoration', 'Shoulders', 'Alliance', 'Malfurion''s Spaulders of Triumph'), +(11, 2, 4, 0, 258, 48169, 'Phase 3', 'Druid', 'Restoration', 'Chest', 'Both', 'Vestments of Triumph'), +(11, 2, 5, 1, 258, 47145, 'Phase 3', 'Druid', 'Restoration', 'Waist', 'Alliance', 'Cord of Pale Thorns'), +(11, 2, 5, 2, 258, 47469, 'Phase 3', 'Druid', 'Restoration', 'Waist', 'Horde', 'Belt of Pale Thorns'), +(11, 2, 6, 0, 258, 48140, 'Phase 3', 'Druid', 'Restoration', 'Legs', 'Both', 'Leggings of Triumph'), +(11, 2, 7, 1, 258, 47097, 'Phase 3', 'Druid', 'Restoration', 'Feet', 'Alliance', 'Boots of the Mourning Widow'), +(11, 2, 7, 2, 258, 47454, 'Phase 3', 'Druid', 'Restoration', 'Feet', 'Horde', 'Sandals of the Mourning Widow'), +(11, 2, 8, 1, 258, 47066, 'Phase 3', 'Druid', 'Restoration', 'Wrists', 'Alliance', 'Bracers of the Autumn Willow'), +(11, 2, 8, 2, 258, 47438, 'Phase 3', 'Druid', 'Restoration', 'Wrists', 'Horde', 'Bindings of the Autumn Willow'), +(11, 2, 9, 0, 258, 48142, 'Phase 3', 'Druid', 'Restoration', 'Hands', 'Both', 'Handguards of Triumph'), +(11, 2, 10, 1, 258, 47224, 'Phase 3', 'Druid', 'Restoration', 'Finger1', 'Alliance', 'Ring of the Darkmender'), +(11, 2, 10, 2, 258, 47439, 'Phase 3', 'Druid', 'Restoration', 'Finger1', 'Horde', 'Circle of the Darkmender'), +(11, 2, 12, 1, 258, 47059, 'Phase 3', 'Druid', 'Restoration', 'Trinket1', 'Alliance', 'Solace of the Defeated'), +(11, 2, 12, 2, 258, 47432, 'Phase 3', 'Druid', 'Restoration', 'Trinket1', 'Horde', 'Solace of the Fallen'), +(11, 2, 14, 1, 258, 47553, 'Phase 3', 'Druid', 'Restoration', 'Back', 'Alliance', 'Bolvar''s Devotion'), +(11, 2, 14, 2, 258, 47554, 'Phase 3', 'Druid', 'Restoration', 'Back', 'Horde', 'Lady Liadrin''s Conviction'), +(11, 2, 17, 0, 258, 47671, 'Phase 3', 'Druid', 'Restoration', 'Ranged', 'Both', 'Idol of Flaring Growth'); + +-- ilvl 264 (Phase 4) +INSERT INTO `playerbots_bis_gear` VALUES +(11, 2, 0, 0, 264, 51302, 'Phase 4', 'Druid', 'Restoration', 'Head', 'Both', 'Sanctified Lasherweave Helmet'), +(11, 2, 1, 0, 264, 50609, 'Phase 4', 'Druid', 'Restoration', 'Neck', 'Both', 'Bone Sentinel''s Amulet'), +(11, 2, 2, 0, 264, 51304, 'Phase 4', 'Druid', 'Restoration', 'Shoulders', 'Both', 'Sanctified Lasherweave Pauldrons'), +(11, 2, 4, 0, 264, 50717, 'Phase 4', 'Druid', 'Restoration', 'Chest', 'Both', 'Sanguine Silk Robes'), +(11, 2, 5, 0, 264, 50705, 'Phase 4', 'Druid', 'Restoration', 'Waist', 'Both', 'Professor''s Bloodied Smock'), +(11, 2, 6, 0, 264, 51303, 'Phase 4', 'Druid', 'Restoration', 'Legs', 'Both', 'Sanctified Lasherweave Legplates'), +(11, 2, 7, 0, 264, 50699, 'Phase 4', 'Druid', 'Restoration', 'Feet', 'Both', 'Plague Scientist''s Boots'), +(11, 2, 8, 0, 264, 50630, 'Phase 4', 'Druid', 'Restoration', 'Wrists', 'Both', 'Bracers of Eternal Dreaming'), +(11, 2, 9, 0, 264, 51301, 'Phase 4', 'Druid', 'Restoration', 'Hands', 'Both', 'Sanctified Lasherweave Gauntlets'), +(11, 2, 10, 0, 264, 50636, 'Phase 4', 'Druid', 'Restoration', 'Finger1', 'Both', 'Memory of Malygos'), +(11, 2, 12, 1, 264, 47059, 'Phase 4', 'Druid', 'Restoration', 'Trinket1', 'Alliance', 'Solace of the Defeated'), +(11, 2, 12, 2, 264, 47432, 'Phase 4', 'Druid', 'Restoration', 'Trinket1', 'Horde', 'Solace of the Fallen'), +(11, 2, 14, 0, 264, 50668, 'Phase 4', 'Druid', 'Restoration', 'Back', 'Both', 'Greatcloak of the Turned Champion'), +(11, 2, 15, 0, 264, 46017, 'Phase 4', 'Druid', 'Restoration', 'MainHand', 'Both', 'Val''anyr, Hammer of Ancient Kings'), +(11, 2, 17, 0, 264, 50454, 'Phase 4', 'Druid', 'Restoration', 'Ranged', 'Both', 'Idol of the Black Willow'); + +-- ilvl 290 (Phase 5) +INSERT INTO `playerbots_bis_gear` VALUES +(11, 2, 0, 0, 290, 51302, 'Phase 5', 'Druid', 'Restoration', 'Head', 'Both', 'Sanctified Lasherweave Helmet'), +(11, 2, 1, 0, 290, 50609, 'Phase 5', 'Druid', 'Restoration', 'Neck', 'Both', 'Bone Sentinel''s Amulet'), +(11, 2, 2, 0, 290, 51304, 'Phase 5', 'Druid', 'Restoration', 'Shoulders', 'Both', 'Sanctified Lasherweave Pauldrons'), +(11, 2, 4, 0, 290, 50717, 'Phase 5', 'Druid', 'Restoration', 'Chest', 'Both', 'Sanguine Silk Robes'), +(11, 2, 5, 0, 290, 50705, 'Phase 5', 'Druid', 'Restoration', 'Waist', 'Both', 'Professor''s Bloodied Smock'), +(11, 2, 6, 0, 290, 51303, 'Phase 5', 'Druid', 'Restoration', 'Legs', 'Both', 'Sanctified Lasherweave Legplates'), +(11, 2, 7, 0, 290, 50665, 'Phase 5', 'Druid', 'Restoration', 'Feet', 'Both', 'Boots Of Unnatural Growth'), +(11, 2, 8, 0, 290, 54584, 'Phase 5', 'Druid', 'Restoration', 'Wrists', 'Both', 'Phaseshifter Bracers'), +(11, 2, 9, 0, 290, 51301, 'Phase 5', 'Druid', 'Restoration', 'Hands', 'Both', 'Sanctified Lasherweave Gauntlets'), +(11, 2, 10, 0, 290, 50400, 'Phase 5', 'Druid', 'Restoration', 'Finger1', 'Both', 'Ashen Band of Endless Wisdom'), +(11, 2, 11, 0, 290, 50636, 'Phase 5', 'Druid', 'Restoration', 'Finger2', 'Both', 'Memory of Malygos'), +(11, 2, 12, 0, 290, 47059, 'Phase 5', 'Druid', 'Restoration', 'Trinket1', 'Both', 'Solace of the Defeated'), +(11, 2, 13, 0, 290, 54589, 'Phase 5', 'Druid', 'Restoration', 'Trinket2', 'Both', 'Glowing Twilight Scale'), +(11, 2, 14, 0, 290, 50668, 'Phase 5', 'Druid', 'Restoration', 'Back', 'Both', 'Greatcloak of the Turned Champion'), +(11, 2, 15, 0, 290, 46017, 'Phase 5', 'Druid', 'Restoration', 'MainHand', 'Both', 'Val''anyr, Hammer of Ancient Kings'), +(11, 2, 16, 0, 290, 50635, 'Phase 5', 'Druid', 'Restoration', 'OffHand', 'Both', 'Sundial of Eternal Dusk'), +(11, 2, 17, 0, 290, 50454, 'Phase 5', 'Druid', 'Restoration', 'Ranged', 'Both', 'Idol of the Black Willow'); + +-- Feral Bear (tab 10) +-- ilvl 66 (Phase 1 (Pre-Raid)) +INSERT INTO `playerbots_bis_gear` VALUES +(11, 10, 0, 0, 66, 14539, 'Phase 1 (Pre-Raid)', 'Druid', 'Feral Bear', 'Head', 'Both', 'Bone Ring Helm'), +(11, 10, 1, 0, 66, 13177, 'Phase 1 (Pre-Raid)', 'Druid', 'Feral Bear', 'Neck', 'Both', 'Talisman of Evasion'), +(11, 10, 2, 0, 66, 10783, 'Phase 1 (Pre-Raid)', 'Druid', 'Feral Bear', 'Shoulders', 'Both', 'Atal''ai Spaulders'), +(11, 10, 4, 0, 66, 15064, 'Phase 1 (Pre-Raid)', 'Druid', 'Feral Bear', 'Chest', 'Both', 'Warbear Harness'), +(11, 10, 5, 0, 66, 13252, 'Phase 1 (Pre-Raid)', 'Druid', 'Feral Bear', 'Waist', 'Both', 'Cloudrunner Girdle'), +(11, 10, 6, 0, 66, 11821, 'Phase 1 (Pre-Raid)', 'Druid', 'Feral Bear', 'Legs', 'Both', 'Warstrife Leggings'), +(11, 10, 7, 0, 66, 16711, 'Phase 1 (Pre-Raid)', 'Druid', 'Feral Bear', 'Feet', 'Both', 'Shadowcraft Boots'), +(11, 10, 8, 0, 66, 12966, 'Phase 1 (Pre-Raid)', 'Druid', 'Feral Bear', 'Wrists', 'Both', 'Blackmist Armguards'), +(11, 10, 9, 0, 66, 13258, 'Phase 1 (Pre-Raid)', 'Druid', 'Feral Bear', 'Hands', 'Both', 'Slaghide Gauntlets'), +(11, 10, 10, 0, 66, 15855, 'Phase 1 (Pre-Raid)', 'Druid', 'Feral Bear', 'Finger1', 'Both', 'Ring of Protection'), +(11, 10, 11, 0, 66, 11669, 'Phase 1 (Pre-Raid)', 'Druid', 'Feral Bear', 'Finger2', 'Both', 'Naglering'), +(11, 10, 12, 0, 66, 13966, 'Phase 1 (Pre-Raid)', 'Druid', 'Feral Bear', 'Trinket1', 'Both', 'Mark of Tyranny'), +(11, 10, 13, 0, 66, 11811, 'Phase 1 (Pre-Raid)', 'Druid', 'Feral Bear', 'Trinket2', 'Both', 'Smoking Heart of the Mountain'), +(11, 10, 14, 0, 66, 12551, 'Phase 1 (Pre-Raid)', 'Druid', 'Feral Bear', 'Back', 'Both', 'Stoneshield Cloak'), +(11, 10, 15, 0, 66, 943, 'Phase 1 (Pre-Raid)', 'Druid', 'Feral Bear', 'MainHand', 'Both', 'Warden Staff'); + +-- ilvl 76 (Phase 2 (Pre-Raid)) +INSERT INTO `playerbots_bis_gear` VALUES +(11, 10, 0, 0, 76, 14539, 'Phase 2 (Pre-Raid)', 'Druid', 'Feral Bear', 'Head', 'Both', 'Bone Ring Helm'), +(11, 10, 1, 0, 76, 13177, 'Phase 2 (Pre-Raid)', 'Druid', 'Feral Bear', 'Neck', 'Both', 'Talisman of Evasion'), +(11, 10, 2, 0, 76, 10783, 'Phase 2 (Pre-Raid)', 'Druid', 'Feral Bear', 'Shoulders', 'Both', 'Atal''ai Spaulders'), +(11, 10, 4, 0, 76, 15064, 'Phase 2 (Pre-Raid)', 'Druid', 'Feral Bear', 'Chest', 'Both', 'Warbear Harness'), +(11, 10, 5, 0, 76, 13252, 'Phase 2 (Pre-Raid)', 'Druid', 'Feral Bear', 'Waist', 'Both', 'Cloudrunner Girdle'), +(11, 10, 6, 0, 76, 11821, 'Phase 2 (Pre-Raid)', 'Druid', 'Feral Bear', 'Legs', 'Both', 'Warstrife Leggings'), +(11, 10, 7, 0, 76, 16711, 'Phase 2 (Pre-Raid)', 'Druid', 'Feral Bear', 'Feet', 'Both', 'Shadowcraft Boots'), +(11, 10, 8, 0, 76, 12966, 'Phase 2 (Pre-Raid)', 'Druid', 'Feral Bear', 'Wrists', 'Both', 'Blackmist Armguards'), +(11, 10, 9, 0, 76, 13258, 'Phase 2 (Pre-Raid)', 'Druid', 'Feral Bear', 'Hands', 'Both', 'Slaghide Gauntlets'), +(11, 10, 10, 0, 76, 15855, 'Phase 2 (Pre-Raid)', 'Druid', 'Feral Bear', 'Finger1', 'Both', 'Ring of Protection'), +(11, 10, 11, 0, 76, 11669, 'Phase 2 (Pre-Raid)', 'Druid', 'Feral Bear', 'Finger2', 'Both', 'Naglering'), +(11, 10, 12, 0, 76, 13966, 'Phase 2 (Pre-Raid)', 'Druid', 'Feral Bear', 'Trinket1', 'Both', 'Mark of Tyranny'), +(11, 10, 13, 0, 76, 11811, 'Phase 2 (Pre-Raid)', 'Druid', 'Feral Bear', 'Trinket2', 'Both', 'Smoking Heart of the Mountain'), +(11, 10, 14, 0, 76, 12551, 'Phase 2 (Pre-Raid)', 'Druid', 'Feral Bear', 'Back', 'Both', 'Stoneshield Cloak'), +(11, 10, 15, 0, 76, 943, 'Phase 2 (Pre-Raid)', 'Druid', 'Feral Bear', 'MainHand', 'Both', 'Warden Staff'); + +-- ilvl 78 (Phase 2) +INSERT INTO `playerbots_bis_gear` VALUES +(11, 10, 0, 0, 78, 14539, 'Phase 2', 'Druid', 'Feral Bear', 'Head', 'Both', 'Bone Ring Helm'), +(11, 10, 1, 0, 78, 17065, 'Phase 2', 'Druid', 'Feral Bear', 'Neck', 'Both', 'Medallion of Steadfast Might'), +(11, 10, 2, 0, 78, 19139, 'Phase 2', 'Druid', 'Feral Bear', 'Shoulders', 'Both', 'Fireguard Shoulders'), +(11, 10, 4, 0, 78, 15064, 'Phase 2', 'Druid', 'Feral Bear', 'Chest', 'Both', 'Warbear Harness'), +(11, 10, 5, 0, 78, 19149, 'Phase 2', 'Druid', 'Feral Bear', 'Waist', 'Both', 'Lava Belt'), +(11, 10, 6, 0, 78, 11821, 'Phase 2', 'Druid', 'Feral Bear', 'Legs', 'Both', 'Warstrife Leggings'), +(11, 10, 7, 0, 78, 16711, 'Phase 2', 'Druid', 'Feral Bear', 'Feet', 'Both', 'Shadowcraft Boots'), +(11, 10, 8, 0, 78, 12966, 'Phase 2', 'Druid', 'Feral Bear', 'Wrists', 'Both', 'Blackmist Armguards'), +(11, 10, 9, 0, 78, 13258, 'Phase 2', 'Druid', 'Feral Bear', 'Hands', 'Both', 'Slaghide Gauntlets'), +(11, 10, 10, 0, 78, 15855, 'Phase 2', 'Druid', 'Feral Bear', 'Finger1', 'Both', 'Ring of Protection'), +(11, 10, 11, 0, 78, 18879, 'Phase 2', 'Druid', 'Feral Bear', 'Finger2', 'Both', 'Heavy Dark Iron Ring'), +(11, 10, 12, 0, 78, 13966, 'Phase 2', 'Druid', 'Feral Bear', 'Trinket1', 'Both', 'Mark of Tyranny'), +(11, 10, 13, 0, 78, 11811, 'Phase 2', 'Druid', 'Feral Bear', 'Trinket2', 'Both', 'Smoking Heart of the Mountain'), +(11, 10, 14, 0, 78, 17107, 'Phase 2', 'Druid', 'Feral Bear', 'Back', 'Both', 'Dragon''s Blood Cape'), +(11, 10, 15, 0, 78, 943, 'Phase 2', 'Druid', 'Feral Bear', 'MainHand', 'Both', 'Warden Staff'); + +-- ilvl 83 (Phase 3) +INSERT INTO `playerbots_bis_gear` VALUES +(11, 10, 0, 0, 83, 14539, 'Phase 3', 'Druid', 'Feral Bear', 'Head', 'Both', 'Bone Ring Helm'), +(11, 10, 1, 0, 83, 17065, 'Phase 3', 'Druid', 'Feral Bear', 'Neck', 'Both', 'Medallion of Steadfast Might'), +(11, 10, 2, 0, 83, 19389, 'Phase 3', 'Druid', 'Feral Bear', 'Shoulders', 'Both', 'Taut Dragonhide Shoulderpads'), +(11, 10, 4, 0, 83, 19405, 'Phase 3', 'Druid', 'Feral Bear', 'Chest', 'Both', 'Malfurion''s Blessed Bulwark'), +(11, 10, 5, 0, 83, 19149, 'Phase 3', 'Druid', 'Feral Bear', 'Waist', 'Both', 'Lava Belt'), +(11, 10, 6, 0, 83, 11821, 'Phase 3', 'Druid', 'Feral Bear', 'Legs', 'Both', 'Warstrife Leggings'), +(11, 10, 7, 0, 83, 19381, 'Phase 3', 'Druid', 'Feral Bear', 'Feet', 'Both', 'Boots of the Shadow Flame'), +(11, 10, 8, 0, 83, 12966, 'Phase 3', 'Druid', 'Feral Bear', 'Wrists', 'Both', 'Blackmist Armguards'), +(11, 10, 9, 0, 83, 13258, 'Phase 3', 'Druid', 'Feral Bear', 'Hands', 'Both', 'Slaghide Gauntlets'), +(11, 10, 10, 0, 83, 19376, 'Phase 3', 'Druid', 'Feral Bear', 'Finger1', 'Both', 'Archimtiros'' Ring of Reckoning'), +(11, 10, 11, 0, 83, 18879, 'Phase 3', 'Druid', 'Feral Bear', 'Finger2', 'Both', 'Heavy Dark Iron Ring'), +(11, 10, 12, 0, 83, 13966, 'Phase 3', 'Druid', 'Feral Bear', 'Trinket1', 'Both', 'Mark of Tyranny'), +(11, 10, 13, 0, 83, 11811, 'Phase 3', 'Druid', 'Feral Bear', 'Trinket2', 'Both', 'Smoking Heart of the Mountain'), +(11, 10, 14, 0, 83, 19386, 'Phase 3', 'Druid', 'Feral Bear', 'Back', 'Both', 'Elementium Threaded Cloak'), +(11, 10, 15, 0, 83, 943, 'Phase 3', 'Druid', 'Feral Bear', 'MainHand', 'Both', 'Warden Staff'); + +-- ilvl 88 (Phase 5) +INSERT INTO `playerbots_bis_gear` VALUES +(11, 10, 0, 1, 88, 21693, 'Phase 5', 'Druid', 'Feral Bear', 'Head', 'Alliance', 'Guise of the Devourer'), +(11, 10, 0, 2, 88, 21693, 'Phase 5', 'Druid', 'Feral Bear', 'Head', 'Horde', 'Guise of the Devourer'), +(11, 10, 1, 1, 88, 22732, 'Phase 5', 'Druid', 'Feral Bear', 'Neck', 'Alliance', 'Mark of C''Thun'), +(11, 10, 1, 2, 88, 22732, 'Phase 5', 'Druid', 'Feral Bear', 'Neck', 'Horde', 'Mark of C''Thun'), +(11, 10, 2, 1, 88, 20059, 'Phase 5', 'Druid', 'Feral Bear', 'Shoulders', 'Alliance', 'Highlander''s Leather Shoulders'), +(11, 10, 2, 2, 88, 20194, 'Phase 5', 'Druid', 'Feral Bear', 'Shoulders', 'Horde', 'Defiler''s Leather Shoulders'), +(11, 10, 4, 1, 88, 19405, 'Phase 5', 'Druid', 'Feral Bear', 'Chest', 'Alliance', 'Malfurion''s Blessed Bulwark'), +(11, 10, 4, 2, 88, 19405, 'Phase 5', 'Druid', 'Feral Bear', 'Chest', 'Horde', 'Malfurion''s Blessed Bulwark'), +(11, 10, 5, 1, 88, 21675, 'Phase 5', 'Druid', 'Feral Bear', 'Waist', 'Alliance', 'Thick Qirajihide Belt'), +(11, 10, 5, 2, 88, 21675, 'Phase 5', 'Druid', 'Feral Bear', 'Waist', 'Horde', 'Thick Qirajihide Belt'), +(11, 10, 6, 1, 88, 22749, 'Phase 5', 'Druid', 'Feral Bear', 'Legs', 'Alliance', 'Sentinel''s Leather Pants'), +(11, 10, 6, 2, 88, 22740, 'Phase 5', 'Druid', 'Feral Bear', 'Legs', 'Horde', 'Outrider''s Leather Pants'), +(11, 10, 7, 1, 88, 19381, 'Phase 5', 'Druid', 'Feral Bear', 'Feet', 'Alliance', 'Boots of the Shadow Flame'), +(11, 10, 7, 2, 88, 19381, 'Phase 5', 'Druid', 'Feral Bear', 'Feet', 'Horde', 'Boots of the Shadow Flame'), +(11, 10, 8, 1, 88, 21602, 'Phase 5', 'Druid', 'Feral Bear', 'Wrists', 'Alliance', 'Qiraji Execution Bracers'), +(11, 10, 8, 2, 88, 21602, 'Phase 5', 'Druid', 'Feral Bear', 'Wrists', 'Horde', 'Qiraji Execution Bracers'), +(11, 10, 9, 1, 88, 21605, 'Phase 5', 'Druid', 'Feral Bear', 'Hands', 'Alliance', 'Gloves of the Hidden Temple'), +(11, 10, 9, 2, 88, 21605, 'Phase 5', 'Druid', 'Feral Bear', 'Hands', 'Horde', 'Gloves of the Hidden Temple'), +(11, 10, 10, 1, 88, 21601, 'Phase 5', 'Druid', 'Feral Bear', 'Finger1', 'Alliance', 'Ring of Emperor Vek''lor'), +(11, 10, 10, 2, 88, 21601, 'Phase 5', 'Druid', 'Feral Bear', 'Finger1', 'Horde', 'Ring of Emperor Vek''lor'), +(11, 10, 11, 1, 88, 18879, 'Phase 5', 'Druid', 'Feral Bear', 'Finger2', 'Alliance', 'Heavy Dark Iron Ring'), +(11, 10, 11, 2, 88, 18879, 'Phase 5', 'Druid', 'Feral Bear', 'Finger2', 'Horde', 'Heavy Dark Iron Ring'), +(11, 10, 12, 1, 88, 13966, 'Phase 5', 'Druid', 'Feral Bear', 'Trinket1', 'Alliance', 'Mark of Tyranny'), +(11, 10, 12, 2, 88, 13966, 'Phase 5', 'Druid', 'Feral Bear', 'Trinket1', 'Horde', 'Mark of Tyranny'), +(11, 10, 13, 1, 88, 11811, 'Phase 5', 'Druid', 'Feral Bear', 'Trinket2', 'Alliance', 'Smoking Heart of the Mountain'), +(11, 10, 13, 2, 88, 11811, 'Phase 5', 'Druid', 'Feral Bear', 'Trinket2', 'Horde', 'Smoking Heart of the Mountain'), +(11, 10, 14, 1, 88, 19386, 'Phase 5', 'Druid', 'Feral Bear', 'Back', 'Alliance', 'Elementium Threaded Cloak'), +(11, 10, 14, 2, 88, 19386, 'Phase 5', 'Druid', 'Feral Bear', 'Back', 'Horde', 'Elementium Threaded Cloak'), +(11, 10, 15, 1, 88, 943, 'Phase 5', 'Druid', 'Feral Bear', 'MainHand', 'Alliance', 'Warden Staff'), +(11, 10, 15, 2, 88, 943, 'Phase 5', 'Druid', 'Feral Bear', 'MainHand', 'Horde', 'Warden Staff'), +(11, 10, 17, 1, 88, 23198, 'Phase 5', 'Druid', 'Feral Bear', 'Ranged', 'Alliance', 'Idol of Brutality'), +(11, 10, 17, 2, 88, 23198, 'Phase 5', 'Druid', 'Feral Bear', 'Ranged', 'Horde', 'Idol of Brutality'); + +-- ilvl 92 (Phase 6) +INSERT INTO `playerbots_bis_gear` VALUES +(11, 10, 0, 1, 92, 21693, 'Phase 6', 'Druid', 'Feral Bear', 'Head', 'Alliance', 'Guise of the Devourer'), +(11, 10, 0, 2, 92, 21693, 'Phase 6', 'Druid', 'Feral Bear', 'Head', 'Horde', 'Guise of the Devourer'), +(11, 10, 1, 1, 92, 22732, 'Phase 6', 'Druid', 'Feral Bear', 'Neck', 'Alliance', 'Mark of C''Thun'), +(11, 10, 1, 2, 92, 22732, 'Phase 6', 'Druid', 'Feral Bear', 'Neck', 'Horde', 'Mark of C''Thun'), +(11, 10, 2, 1, 92, 20059, 'Phase 6', 'Druid', 'Feral Bear', 'Shoulders', 'Alliance', 'Highlander''s Leather Shoulders'), +(11, 10, 2, 2, 92, 20194, 'Phase 6', 'Druid', 'Feral Bear', 'Shoulders', 'Horde', 'Defiler''s Leather Shoulders'), +(11, 10, 4, 1, 92, 23226, 'Phase 6', 'Druid', 'Feral Bear', 'Chest', 'Alliance', 'Ghoul Skin Tunic'), +(11, 10, 4, 2, 92, 23226, 'Phase 6', 'Druid', 'Feral Bear', 'Chest', 'Horde', 'Ghoul Skin Tunic'), +(11, 10, 5, 1, 92, 21675, 'Phase 6', 'Druid', 'Feral Bear', 'Waist', 'Alliance', 'Thick Qirajihide Belt'), +(11, 10, 5, 2, 92, 21675, 'Phase 6', 'Druid', 'Feral Bear', 'Waist', 'Horde', 'Thick Qirajihide Belt'), +(11, 10, 6, 1, 92, 22749, 'Phase 6', 'Druid', 'Feral Bear', 'Legs', 'Alliance', 'Sentinel''s Leather Pants'), +(11, 10, 6, 2, 92, 22740, 'Phase 6', 'Druid', 'Feral Bear', 'Legs', 'Horde', 'Outrider''s Leather Pants'), +(11, 10, 7, 1, 92, 19381, 'Phase 6', 'Druid', 'Feral Bear', 'Feet', 'Alliance', 'Boots of the Shadow Flame'), +(11, 10, 7, 2, 92, 19381, 'Phase 6', 'Druid', 'Feral Bear', 'Feet', 'Horde', 'Boots of the Shadow Flame'), +(11, 10, 8, 1, 92, 22663, 'Phase 6', 'Druid', 'Feral Bear', 'Wrists', 'Alliance', 'Polar Bracers'), +(11, 10, 8, 2, 92, 22663, 'Phase 6', 'Druid', 'Feral Bear', 'Wrists', 'Horde', 'Polar Bracers'), +(11, 10, 9, 1, 92, 21605, 'Phase 6', 'Druid', 'Feral Bear', 'Hands', 'Alliance', 'Gloves of the Hidden Temple'), +(11, 10, 9, 2, 92, 21605, 'Phase 6', 'Druid', 'Feral Bear', 'Hands', 'Horde', 'Gloves of the Hidden Temple'), +(11, 10, 10, 1, 92, 21601, 'Phase 6', 'Druid', 'Feral Bear', 'Finger1', 'Alliance', 'Ring of Emperor Vek''lor'), +(11, 10, 10, 2, 92, 21601, 'Phase 6', 'Druid', 'Feral Bear', 'Finger1', 'Horde', 'Ring of Emperor Vek''lor'), +(11, 10, 11, 1, 92, 23018, 'Phase 6', 'Druid', 'Feral Bear', 'Finger2', 'Alliance', 'Signet of the Fallen Defender'), +(11, 10, 11, 2, 92, 23018, 'Phase 6', 'Druid', 'Feral Bear', 'Finger2', 'Horde', 'Signet of the Fallen Defender'), +(11, 10, 12, 1, 92, 13966, 'Phase 6', 'Druid', 'Feral Bear', 'Trinket1', 'Alliance', 'Mark of Tyranny'), +(11, 10, 12, 2, 92, 13966, 'Phase 6', 'Druid', 'Feral Bear', 'Trinket1', 'Horde', 'Mark of Tyranny'), +(11, 10, 13, 1, 92, 11811, 'Phase 6', 'Druid', 'Feral Bear', 'Trinket2', 'Alliance', 'Smoking Heart of the Mountain'), +(11, 10, 13, 2, 92, 11811, 'Phase 6', 'Druid', 'Feral Bear', 'Trinket2', 'Horde', 'Smoking Heart of the Mountain'), +(11, 10, 14, 1, 92, 22938, 'Phase 6', 'Druid', 'Feral Bear', 'Back', 'Alliance', 'Cryptfiend Silk Cloak'), +(11, 10, 14, 2, 92, 22938, 'Phase 6', 'Druid', 'Feral Bear', 'Back', 'Horde', 'Cryptfiend Silk Cloak'), +(11, 10, 15, 1, 92, 943, 'Phase 6', 'Druid', 'Feral Bear', 'MainHand', 'Alliance', 'Warden Staff'), +(11, 10, 15, 2, 92, 943, 'Phase 6', 'Druid', 'Feral Bear', 'MainHand', 'Horde', 'Warden Staff'), +(11, 10, 17, 1, 92, 23198, 'Phase 6', 'Druid', 'Feral Bear', 'Ranged', 'Alliance', 'Idol of Brutality'), +(11, 10, 17, 2, 92, 23198, 'Phase 6', 'Druid', 'Feral Bear', 'Ranged', 'Horde', 'Idol of Brutality'); + +-- ilvl 120 (Pre-Raid) +INSERT INTO `playerbots_bis_gear` VALUES +(11, 10, 0, 0, 120, 28182, 'Pre-Raid', 'Druid', 'Feral Bear', 'Head', 'Both', 'Helm of the Claw'), +(11, 10, 1, 0, 120, 27779, 'Pre-Raid', 'Druid', 'Feral Bear', 'Neck', 'Both', 'Bone Chain Necklace'), +(11, 10, 2, 0, 120, 27434, 'Pre-Raid', 'Druid', 'Feral Bear', 'Shoulders', 'Both', 'Mantle of Perenolde'), +(11, 10, 4, 0, 120, 25689, 'Pre-Raid', 'Druid', 'Feral Bear', 'Chest', 'Both', 'Heavy Clefthoof Vest'), +(11, 10, 5, 0, 120, 30942, 'Pre-Raid', 'Druid', 'Feral Bear', 'Waist', 'Both', 'Manimal''s Cinch'), +(11, 10, 6, 0, 120, 31544, 'Pre-Raid', 'Druid', 'Feral Bear', 'Legs', 'Both', 'Clefthoof Hide Leggings'), +(11, 10, 7, 0, 120, 25691, 'Pre-Raid', 'Druid', 'Feral Bear', 'Feet', 'Both', 'Heavy Clefthoof Boots'), +(11, 10, 8, 0, 120, 29263, 'Pre-Raid', 'Druid', 'Feral Bear', 'Wrists', 'Both', 'Forestheart Bracers'), +(11, 10, 9, 0, 120, 30341, 'Pre-Raid', 'Druid', 'Feral Bear', 'Hands', 'Both', 'Flesh Handler''s Gauntlets'), +(11, 10, 10, 0, 120, 30834, 'Pre-Raid', 'Druid', 'Feral Bear', 'Finger1', 'Both', 'Shapeshifter''s Signet'), +(11, 10, 12, 0, 120, 19406, 'Pre-Raid', 'Druid', 'Feral Bear', 'Trinket1', 'Both', 'Drake Fang Talisman'), +(11, 10, 13, 0, 120, 29383, 'Pre-Raid', 'Druid', 'Feral Bear', 'Trinket2', 'Both', 'Bloodlust Brooch'), +(11, 10, 14, 0, 120, 24258, 'Pre-Raid', 'Druid', 'Feral Bear', 'Back', 'Both', 'Resolute Cape'), +(11, 10, 15, 0, 120, 29171, 'Pre-Raid', 'Druid', 'Feral Bear', 'MainHand', 'Both', 'Earthwarden'), +(11, 10, 17, 0, 120, 28064, 'Pre-Raid', 'Druid', 'Feral Bear', 'Ranged', 'Both', 'Idol of the Wild'); + +-- ilvl 125 (Phase 1) +INSERT INTO `playerbots_bis_gear` VALUES +(11, 10, 0, 0, 125, 29098, 'Phase 1', 'Druid', 'Feral Bear', 'Head', 'Both', 'Stag-Helm of Malorne'), +(11, 10, 1, 0, 125, 28509, 'Phase 1', 'Druid', 'Feral Bear', 'Neck', 'Both', 'Worgen Claw Necklace'), +(11, 10, 2, 0, 125, 29100, 'Phase 1', 'Druid', 'Feral Bear', 'Shoulders', 'Both', 'Mantle of Malorne'), +(11, 10, 4, 0, 125, 29096, 'Phase 1', 'Druid', 'Feral Bear', 'Chest', 'Both', 'Breastplate of Malorne'), +(11, 10, 5, 0, 125, 29264, 'Phase 1', 'Druid', 'Feral Bear', 'Waist', 'Both', 'Tree-Mender''s Belt'), +(11, 10, 6, 0, 125, 29099, 'Phase 1', 'Druid', 'Feral Bear', 'Legs', 'Both', 'Greaves of Malorne'), +(11, 10, 7, 0, 125, 28545, 'Phase 1', 'Druid', 'Feral Bear', 'Feet', 'Both', 'Edgewalker Longboots'), +(11, 10, 8, 0, 125, 29263, 'Phase 1', 'Druid', 'Feral Bear', 'Wrists', 'Both', 'Forestheart Bracers'), +(11, 10, 9, 0, 125, 29097, 'Phase 1', 'Druid', 'Feral Bear', 'Hands', 'Both', 'Gauntlets of Malorne'), +(11, 10, 10, 0, 125, 30834, 'Phase 1', 'Druid', 'Feral Bear', 'Finger1', 'Both', 'Shapeshifter''s Signet'), +(11, 10, 11, 0, 125, 29279, 'Phase 1', 'Druid', 'Feral Bear', 'Finger2', 'Both', 'Violet Signet of the Great Protector'), +(11, 10, 12, 0, 125, 29383, 'Phase 1', 'Druid', 'Feral Bear', 'Trinket1', 'Both', 'Bloodlust Brooch'), +(11, 10, 13, 0, 125, 28830, 'Phase 1', 'Druid', 'Feral Bear', 'Trinket2', 'Both', 'Dragonspine Trophy'), +(11, 10, 14, 0, 125, 28660, 'Phase 1', 'Druid', 'Feral Bear', 'Back', 'Both', 'Gilded Thorium Cloak'), +(11, 10, 15, 0, 125, 28658, 'Phase 1', 'Druid', 'Feral Bear', 'MainHand', 'Both', 'Terestian''s Stranglestaff'), +(11, 10, 17, 0, 125, 23198, 'Phase 1', 'Druid', 'Feral Bear', 'Ranged', 'Both', 'Idol of Brutality'); + +-- ilvl 200 (Pre-Raid) +INSERT INTO `playerbots_bis_gear` VALUES +(11, 10, 0, 0, 200, 42550, 'Pre-Raid', 'Druid', 'Feral Bear', 'Head', 'Both', 'Weakness Spectralizers'), +(11, 10, 2, 0, 200, 43481, 'Pre-Raid', 'Druid', 'Feral Bear', 'Shoulders', 'Both', 'Trollwoven Spaulders'), +(11, 10, 4, 0, 200, 39554, 'Pre-Raid', 'Druid', 'Feral Bear', 'Chest', 'Both', 'Heroes'' Dreamwalker Raiments'), +(11, 10, 5, 0, 200, 40694, 'Pre-Raid', 'Druid', 'Feral Bear', 'Waist', 'Both', 'Jorach''s Crocolisk Skin Belt'), +(11, 10, 6, 0, 200, 37644, 'Pre-Raid', 'Druid', 'Feral Bear', 'Legs', 'Both', 'Gored Hide Legguards'), +(11, 10, 7, 0, 200, 44297, 'Pre-Raid', 'Druid', 'Feral Bear', 'Feet', 'Both', 'Boots of the Neverending Path'), +(11, 10, 8, 0, 200, 44203, 'Pre-Raid', 'Druid', 'Feral Bear', 'Wrists', 'Both', 'Dragonfriend Bracers'), +(11, 10, 9, 0, 200, 37409, 'Pre-Raid', 'Druid', 'Feral Bear', 'Hands', 'Both', 'Gilt-Edged Leather Gauntlets'), +(11, 10, 10, 0, 200, 40586, 'Pre-Raid', 'Druid', 'Feral Bear', 'Finger1', 'Both', 'Band of the Kirin Tor'), +(11, 10, 12, 0, 200, 42987, 'Pre-Raid', 'Druid', 'Feral Bear', 'Trinket1', 'Both', 'Darkmoon Card: Greatness'), +(11, 10, 14, 0, 200, 43406, 'Pre-Raid', 'Druid', 'Feral Bear', 'Back', 'Both', 'Cloak of the Gushing Wound'), +(11, 10, 17, 0, 200, 40713, 'Pre-Raid', 'Druid', 'Feral Bear', 'Ranged', 'Both', 'Idol of the Ravenous Beast'); + +-- ilvl 224 (Phase 1) +INSERT INTO `playerbots_bis_gear` VALUES +(11, 10, 0, 0, 224, 40329, 'Phase 1', 'Druid', 'Feral Bear', 'Head', 'Both', 'Hood of the Exodus'), +(11, 10, 2, 0, 224, 40494, 'Phase 1', 'Druid', 'Feral Bear', 'Shoulders', 'Both', 'Valorous Dreamwalker Shoulderpads'), +(11, 10, 4, 0, 224, 40471, 'Phase 1', 'Druid', 'Feral Bear', 'Chest', 'Both', 'Valorous Dreamwalker Raiments'), +(11, 10, 5, 0, 224, 43591, 'Phase 1', 'Druid', 'Feral Bear', 'Waist', 'Both', 'Polar Cord'), +(11, 10, 6, 0, 224, 44011, 'Phase 1', 'Druid', 'Feral Bear', 'Legs', 'Both', 'Leggings of the Honored'), +(11, 10, 7, 0, 224, 40243, 'Phase 1', 'Druid', 'Feral Bear', 'Feet', 'Both', 'Footwraps of Vile Deceit'), +(11, 10, 8, 0, 224, 40186, 'Phase 1', 'Druid', 'Feral Bear', 'Wrists', 'Both', 'Thrusting Bands'), +(11, 10, 9, 0, 224, 40472, 'Phase 1', 'Druid', 'Feral Bear', 'Hands', 'Both', 'Valorous Dreamwalker Handgrips'), +(11, 10, 10, 0, 224, 40370, 'Phase 1', 'Druid', 'Feral Bear', 'Finger1', 'Both', 'Gatekeeper'), +(11, 10, 12, 0, 224, 44253, 'Phase 1', 'Druid', 'Feral Bear', 'Trinket1', 'Both', 'Darkmoon Card: Greatness'), +(11, 10, 14, 0, 224, 40252, 'Phase 1', 'Druid', 'Feral Bear', 'Back', 'Both', 'Cloak of the Shadowed Sun'), +(11, 10, 17, 0, 224, 38365, 'Phase 1', 'Druid', 'Feral Bear', 'Ranged', 'Both', 'Idol of Perspicacious Attacks'); + +-- ilvl 245 (Phase 2) +INSERT INTO `playerbots_bis_gear` VALUES +(11, 10, 0, 0, 245, 41678, 'Phase 2', 'Druid', 'Feral Bear', 'Head', 'Both', 'Furious Gladiator''s Dragonhide Helm'), +(11, 10, 2, 0, 245, 45245, 'Phase 2', 'Druid', 'Feral Bear', 'Shoulders', 'Both', 'Shoulderpads of the Intruder'), +(11, 10, 4, 0, 245, 45473, 'Phase 2', 'Druid', 'Feral Bear', 'Chest', 'Both', 'Embrace of the Gladiator'), +(11, 10, 5, 0, 245, 46095, 'Phase 2', 'Druid', 'Feral Bear', 'Waist', 'Both', 'Soul-Devouring Cinch'), +(11, 10, 6, 0, 245, 45536, 'Phase 2', 'Druid', 'Feral Bear', 'Legs', 'Both', 'Legguards of Cunning Deception'), +(11, 10, 7, 0, 245, 45232, 'Phase 2', 'Druid', 'Feral Bear', 'Feet', 'Both', 'Runed Ironhide Boots'), +(11, 10, 8, 0, 245, 45611, 'Phase 2', 'Druid', 'Feral Bear', 'Wrists', 'Both', 'Solar Bindings'), +(11, 10, 9, 0, 245, 46043, 'Phase 2', 'Druid', 'Feral Bear', 'Hands', 'Both', 'Gloves of the Endless Dark'), +(11, 10, 10, 0, 245, 45471, 'Phase 2', 'Druid', 'Feral Bear', 'Finger1', 'Both', 'Fate''s Clutch'), +(11, 10, 12, 0, 245, 45158, 'Phase 2', 'Druid', 'Feral Bear', 'Trinket1', 'Both', 'Heart of Iron'), +(11, 10, 14, 0, 245, 45496, 'Phase 2', 'Druid', 'Feral Bear', 'Back', 'Both', 'Titanskin Cloak'), +(11, 10, 17, 0, 245, 45509, 'Phase 2', 'Druid', 'Feral Bear', 'Ranged', 'Both', 'Idol of the Corruptor'); + +-- ilvl 258 (Phase 3) +INSERT INTO `playerbots_bis_gear` VALUES +(11, 10, 0, 0, 258, 48201, 'Phase 3', 'Druid', 'Feral Bear', 'Head', 'Both', 'Headguard of Triumph'), +(11, 10, 2, 0, 258, 45245, 'Phase 3', 'Druid', 'Feral Bear', 'Shoulders', 'Both', 'Shoulderpads of the Intruder'), +(11, 10, 4, 1, 258, 47004, 'Phase 3', 'Druid', 'Feral Bear', 'Chest', 'Alliance', 'Cuirass of Calamitous Fate'), +(11, 10, 4, 2, 258, 47431, 'Phase 3', 'Druid', 'Feral Bear', 'Chest', 'Horde', 'Vest of Calamitous Fate'), +(11, 10, 5, 1, 258, 47112, 'Phase 3', 'Druid', 'Feral Bear', 'Waist', 'Alliance', 'Belt of the Merciless Killer'), +(11, 10, 5, 2, 258, 47460, 'Phase 3', 'Druid', 'Feral Bear', 'Waist', 'Horde', 'Belt of the Pitiless Killer'), +(11, 10, 6, 1, 258, 46975, 'Phase 3', 'Druid', 'Feral Bear', 'Legs', 'Alliance', 'Leggings of the Broken Beast'), +(11, 10, 6, 2, 258, 47420, 'Phase 3', 'Druid', 'Feral Bear', 'Legs', 'Horde', 'Legwraps of the Broken Beast'), +(11, 10, 7, 1, 258, 47077, 'Phase 3', 'Druid', 'Feral Bear', 'Feet', 'Alliance', 'Treads of the Icewalker'), +(11, 10, 7, 2, 258, 47445, 'Phase 3', 'Druid', 'Feral Bear', 'Feet', 'Horde', 'Icewalker Treads'), +(11, 10, 8, 1, 258, 47155, 'Phase 3', 'Druid', 'Feral Bear', 'Wrists', 'Alliance', 'Bracers of Dark Determination'), +(11, 10, 8, 2, 258, 47474, 'Phase 3', 'Druid', 'Feral Bear', 'Wrists', 'Horde', 'Armbands of Dark Determination'), +(11, 10, 9, 0, 258, 48202, 'Phase 3', 'Druid', 'Feral Bear', 'Hands', 'Both', 'Handgrips of Triumph'), +(11, 10, 10, 1, 258, 47955, 'Phase 3', 'Druid', 'Feral Bear', 'Finger1', 'Alliance', 'Loop of the Twin Val''kyr'), +(11, 10, 10, 2, 258, 48027, 'Phase 3', 'Druid', 'Feral Bear', 'Finger1', 'Horde', 'Band of the Twin Val''kyr'), +(11, 10, 12, 1, 258, 47088, 'Phase 3', 'Druid', 'Feral Bear', 'Trinket1', 'Alliance', 'Satrina''s Impeding Scarab'), +(11, 10, 12, 2, 258, 47451, 'Phase 3', 'Druid', 'Feral Bear', 'Trinket1', 'Horde', 'Juggernaut''s Vitality'), +(11, 10, 14, 1, 258, 47549, 'Phase 3', 'Druid', 'Feral Bear', 'Back', 'Alliance', 'Magni''s Resolution'), +(11, 10, 14, 2, 258, 47550, 'Phase 3', 'Druid', 'Feral Bear', 'Back', 'Horde', 'Cairne''s Endurance'), +(11, 10, 15, 0, 258, 48523, 'Phase 3', 'Druid', 'Feral Bear', 'MainHand', 'Both', 'Relentless Gladiator''s Greatstaff'), +(11, 10, 17, 0, 258, 45509, 'Phase 3', 'Druid', 'Feral Bear', 'Ranged', 'Both', 'Idol of the Corruptor'); + +-- ilvl 264 (Phase 4) +INSERT INTO `playerbots_bis_gear` VALUES +(11, 10, 0, 0, 264, 51296, 'Phase 4', 'Druid', 'Feral Bear', 'Head', 'Both', 'Sanctified Lasherweave Headguard'), +(11, 10, 2, 0, 264, 51299, 'Phase 4', 'Druid', 'Feral Bear', 'Shoulders', 'Both', 'Sanctified Lasherweave Shoulderpads'), +(11, 10, 4, 0, 264, 51298, 'Phase 4', 'Druid', 'Feral Bear', 'Chest', 'Both', 'Sanctified Lasherweave Raiment'), +(11, 10, 5, 0, 264, 50707, 'Phase 4', 'Druid', 'Feral Bear', 'Waist', 'Both', 'Astrylian''s Sutured Cinch'), +(11, 10, 6, 0, 264, 51297, 'Phase 4', 'Druid', 'Feral Bear', 'Legs', 'Both', 'Sanctified Lasherweave Legguards'), +(11, 10, 7, 0, 264, 50607, 'Phase 4', 'Druid', 'Feral Bear', 'Feet', 'Both', 'Frostbitten Fur Boots'), +(11, 10, 8, 0, 264, 50670, 'Phase 4', 'Druid', 'Feral Bear', 'Wrists', 'Both', 'Toskk''s Maximized Wristguards'), +(11, 10, 9, 0, 264, 50675, 'Phase 4', 'Druid', 'Feral Bear', 'Hands', 'Both', 'Aldriana''s Gloves of Secrecy'), +(11, 10, 10, 0, 264, 50402, 'Phase 4', 'Druid', 'Feral Bear', 'Finger1', 'Both', 'Ashen Band of Endless Vengeance'), +(11, 10, 12, 1, 264, 47088, 'Phase 4', 'Druid', 'Feral Bear', 'Trinket1', 'Alliance', 'Satrina''s Impeding Scarab'), +(11, 10, 12, 2, 264, 47451, 'Phase 4', 'Druid', 'Feral Bear', 'Trinket1', 'Horde', 'Juggernaut''s Vitality'), +(11, 10, 14, 0, 264, 50466, 'Phase 4', 'Druid', 'Feral Bear', 'Back', 'Both', 'Sentinel''s Winter Cloak'), +(11, 10, 15, 0, 264, 50735, 'Phase 4', 'Druid', 'Feral Bear', 'MainHand', 'Both', 'Oathbinder, Charge of the Ranger-General'), +(11, 10, 17, 0, 264, 50456, 'Phase 4', 'Druid', 'Feral Bear', 'Ranged', 'Both', 'Idol of the Crying Moon'); + +-- ilvl 290 (Phase 5) +INSERT INTO `playerbots_bis_gear` VALUES +(11, 10, 0, 0, 290, 51296, 'Phase 5', 'Druid', 'FeralBear', 'Head', 'Both', 'Sanctified Lasherweave Headguard'), +(11, 10, 1, 0, 290, 50682, 'Phase 5', 'Druid', 'FeralBear', 'Neck', 'Both', 'Bile-Encrusted Medallion'), +(11, 10, 2, 0, 290, 51299, 'Phase 5', 'Druid', 'FeralBear', 'Shoulders', 'Both', 'Sanctified Lasherweave Shoulderpads'), +(11, 10, 4, 0, 290, 50656, 'Phase 5', 'Druid', 'FeralBear', 'Chest', 'Both', 'Ikfirus''s Sack of Wonder'), +(11, 10, 5, 0, 290, 50707, 'Phase 5', 'Druid', 'FeralBear', 'Waist', 'Both', 'Astrylian''s Sutured Cinch'), +(11, 10, 6, 0, 290, 51297, 'Phase 5', 'Druid', 'FeralBear', 'Legs', 'Both', 'Sanctified Lasherweave Legguards'), +(11, 10, 7, 0, 290, 50607, 'Phase 5', 'Druid', 'FeralBear', 'Feet', 'Both', 'Frostbitten Fur Boots'), +(11, 10, 8, 0, 290, 54580, 'Phase 5', 'Druid', 'FeralBear', 'Wrists', 'Both', 'Umbrage Armbands'), +(11, 10, 9, 0, 290, 51295, 'Phase 5', 'Druid', 'FeralBear', 'Hands', 'Both', 'Sanctified Lasherweave Handgrips'), +(11, 10, 10, 0, 290, 50622, 'Phase 5', 'Druid', 'FeralBear', 'Finger1', 'Both', 'Devium''s Eternally Cold Ring'), +(11, 10, 11, 0, 290, 50404, 'Phase 5', 'Druid', 'FeralBear', 'Finger2', 'Both', 'Ashen Band of Endless Courage'), +(11, 10, 12, 0, 290, 50364, 'Phase 5', 'Druid', 'FeralBear', 'Trinket1', 'Both', 'Sindragosa''s Flawless Fang'), +(11, 10, 13, 0, 290, 50356, 'Phase 5', 'Druid', 'FeralBear', 'Trinket2', 'Both', 'Corroded Skeleton Key'), +(11, 10, 14, 0, 290, 50466, 'Phase 5', 'Druid', 'FeralBear', 'Back', 'Both', 'Sentinel''s Winter Cloak'), +(11, 10, 15, 0, 290, 50735, 'Phase 5', 'Druid', 'FeralBear', 'MainHand', 'Both', 'Oathbinder, Charge of the Ranger-General'), +(11, 10, 17, 0, 290, 50456, 'Phase 5', 'Druid', 'FeralBear', 'Ranged', 'Both', 'Idol of the Crying Moon'); + diff --git a/data/sql/playerbots/updates/2026_04_28_01_ai_playerbot_bis_texts.sql b/data/sql/playerbots/updates/2026_04_28_01_ai_playerbot_bis_texts.sql new file mode 100644 index 00000000000..42fece3d68a --- /dev/null +++ b/data/sql/playerbots/updates/2026_04_28_01_ai_playerbot_bis_texts.sql @@ -0,0 +1,183 @@ +-- ######################################################### +-- Playerbots - Add /p autogear bis command texts +-- Localized for all WotLK locales (koKR, frFR, deDE, zhCN, +-- zhTW, esES, esMX, ruRU) +-- ######################################################### + +DELETE FROM ai_playerbot_texts WHERE name IN ( + 'bis_autogear_unavailable_error', + 'bis_no_rows_fallback', + 'bis_command_unavailable_error', + 'bis_altbot_refused_error', + 'bis_quality_floor_error', + 'bis_pvp_refused_error', + 'bis_invalid_arg_error', + 'bis_arg_above_limit_error', + 'bis_no_rows_autogear_msg', + 'bis_closest_match_msg', + 'bis_applying_msg', + 'bis_applied_msg' +); +DELETE FROM ai_playerbot_texts_chance WHERE name IN ( + 'bis_autogear_unavailable_error', + 'bis_no_rows_fallback', + 'bis_command_unavailable_error', + 'bis_altbot_refused_error', + 'bis_quality_floor_error', + 'bis_pvp_refused_error', + 'bis_invalid_arg_error', + 'bis_arg_above_limit_error', + 'bis_no_rows_autogear_msg', + 'bis_closest_match_msg', + 'bis_applying_msg', + 'bis_applied_msg' +); + +INSERT INTO `ai_playerbot_texts` + (`id`, `name`, `text`, `say_type`, `reply_type`, + `text_loc1`, `text_loc2`, `text_loc3`, `text_loc4`, + `text_loc5`, `text_loc6`, `text_loc7`, `text_loc8`) +VALUES + (1767, 'bis_autogear_unavailable_error', + 'autogear command is not allowed, please check the configuration.', 0, 0, + '자동 장비 명령이 허용되지 않습니다. 설정을 확인하세요.', + 'La commande autogear n''est pas autorisée, veuillez vérifier la configuration.', + 'Der autogear-Befehl ist nicht erlaubt, bitte überprüfe die Konfiguration.', + '自动装备命令未启用,请检查配置。', + '自動裝備指令未啟用,請檢查設定。', + 'El comando autogear no está permitido, por favor revisa la configuración.', + 'El comando autogear no está permitido, por favor revisa la configuración.', + 'Команда autogear не разрешена, проверьте конфигурацию.'), + + (1768, 'bis_no_rows_fallback', + 'No BiS for your tier/spec/level, check cfg, running autogear instead', 0, 0, + '해당 등급/전문화/레벨에 BiS 목록이 없습니다. 설정을 확인하세요. 대신 자동 장비를 실행합니다.', + 'Pas de BiS pour votre tier/spé/niveau, vérifiez la config, exécution d''autogear à la place.', + 'Kein BiS für deinen Tier/Spec/Level, prüfe die Config, führe stattdessen autogear aus.', + '您的等级/天赋/级别没有BiS数据,请检查配置,改为运行autogear。', + '你的等級/天賦/級別沒有BiS資料,請檢查設定,改為執行autogear。', + 'No hay BiS para tu tier/spec/nivel, revisa la configuración, ejecutando autogear en su lugar.', + 'No hay BiS para tu tier/spec/nivel, revisa la configuración, ejecutando autogear en su lugar.', + 'Нет BiS для вашего тира/спека/уровня, проверьте конфиг, запускаю autogear.'), + + (1769, 'bis_command_unavailable_error', + 'bis command is not allowed, please check the configuration.', 0, 0, + 'bis 명령이 허용되지 않습니다. 설정을 확인하세요.', + 'La commande bis n''est pas autorisée, veuillez vérifier la configuration.', + 'Der bis-Befehl ist nicht erlaubt, bitte überprüfe die Konfiguration.', + 'bis命令未启用,请检查配置。', + 'bis指令未啟用,請檢查設定。', + 'El comando bis no está permitido, por favor revisa la configuración.', + 'El comando bis no está permitido, por favor revisa la configuración.', + 'Команда bis не разрешена, проверьте конфигурацию.'), + + (1770, 'bis_altbot_refused_error', + 'You cannot use bis on alt bots.', 0, 0, + '부캐 봇에는 bis를 사용할 수 없습니다.', + 'Vous ne pouvez pas utiliser bis sur des bots alternatifs.', + 'Du kannst bis nicht auf Zweitbots verwenden.', + '你不能在副号机器人上使用bis。', + '你不能在副號機器人上使用bis。', + 'No puedes usar bis en bots alternativos.', + 'No puedes usar bis en bots alternativos.', + 'Вы не можете использовать bis на дополнительных ботах.'), + + (1771, 'bis_quality_floor_error', + 'AutoGearQualityLimit must be 4 for BiS.', 0, 0, + 'BiS를 사용하려면 AutoGearQualityLimit이 4여야 합니다.', + 'AutoGearQualityLimit doit être à 4 pour utiliser BiS.', + 'AutoGearQualityLimit muss für BiS auf 4 stehen.', + 'BiS要求AutoGearQualityLimit设置为4。', + 'BiS要求AutoGearQualityLimit設定為4。', + 'AutoGearQualityLimit debe ser 4 para BiS.', + 'AutoGearQualityLimit debe ser 4 para BiS.', + 'AutoGearQualityLimit должен быть 4 для BiS.'), + + (1772, 'bis_pvp_refused_error', + 'bis is PvE only, bot is configured as PvP.', 0, 0, + 'bis는 PvE 전용이며, 이 봇은 PvP로 설정되어 있습니다.', + 'bis est uniquement pour le JcE, ce bot est configuré en JcJ.', + 'bis ist nur für PvE, dieser Bot ist als PvP konfiguriert.', + 'bis仅适用于PvE,此机器人配置为PvP。', + 'bis僅適用於PvE,此機器人設定為PvP。', + 'bis es solo para PvE, el bot está configurado como PvP.', + 'bis es solo para PvE, el bot está configurado como PvP.', + 'bis только для PvE, бот настроен на PvP.'), + + (1773, 'bis_invalid_arg_error', + 'Invalid BiS ilvl argument ''%param''. Use a positive integer.', 0, 0, + '잘못된 BiS 아이템 레벨 인수 ''%param''. 양의 정수를 사용하세요.', + 'Argument iLvl BiS invalide ''%param''. Utilisez un entier positif.', + 'Ungültiges BiS-iLvl-Argument ''%param''. Verwende eine positive ganze Zahl.', + '无效的BiS物品等级参数“%param”。请使用正整数。', + '無效的BiS物品等級參數「%param」。請使用正整數。', + 'Argumento iLvl BiS inválido ''%param''. Usa un entero positivo.', + 'Argumento iLvl BiS inválido ''%param''. Usa un entero positivo.', + 'Неверный аргумент iLvl BiS ''%param''. Используйте положительное целое число.'), + + (1774, 'bis_arg_above_limit_error', + 'BiS ilvl %requested exceeds AutoGearScoreLimit %limit, refusing', 0, 0, + 'BiS 아이템 레벨 %requested이(가) AutoGearScoreLimit %limit을(를) 초과합니다. 거부합니다.', + 'iLvl BiS %requested dépasse AutoGearScoreLimit %limit, refusé.', + 'BiS-iLvl %requested überschreitet AutoGearScoreLimit %limit, abgelehnt.', + 'BiS物品等级%requested超过AutoGearScoreLimit %limit,已拒绝。', + 'BiS物品等級%requested超過AutoGearScoreLimit %limit,已拒絕。', + 'iLvl BiS %requested supera AutoGearScoreLimit %limit, rechazado.', + 'iLvl BiS %requested supera AutoGearScoreLimit %limit, rechazado.', + 'BiS iLvl %requested превышает AutoGearScoreLimit %limit, отказано.'), + + (1775, 'bis_no_rows_autogear_msg', + 'No BiS at ilvl %ilvl, using Autogear %ilvl instead', 0, 0, + '아이템 레벨 %ilvl의 BiS가 없어 대신 Autogear %ilvl을(를) 사용합니다.', + 'Pas de BiS à l''iLvl %ilvl, utilisation d''Autogear %ilvl à la place.', + 'Kein BiS auf iLvl %ilvl, verwende stattdessen Autogear %ilvl.', + '物品等级%ilvl没有BiS,改用Autogear %ilvl。', + '物品等級%ilvl沒有BiS,改用Autogear %ilvl。', + 'No hay BiS en iLvl %ilvl, usando Autogear %ilvl en su lugar.', + 'No hay BiS en iLvl %ilvl, usando Autogear %ilvl en su lugar.', + 'Нет BiS на iLvl %ilvl, использую Autogear %ilvl.'), + + (1776, 'bis_closest_match_msg', + 'No BiS at ilvl %requested, using closest match at ilvl %resolved', 0, 0, + '아이템 레벨 %requested의 BiS가 없어 가장 가까운 아이템 레벨 %resolved을(를) 사용합니다.', + 'Pas de BiS à l''iLvl %requested, utilisation de la correspondance la plus proche à l''iLvl %resolved.', + 'Kein BiS auf iLvl %requested, verwende nächstliegende Übereinstimmung auf iLvl %resolved.', + '物品等级%requested没有BiS,使用最接近的物品等级%resolved。', + '物品等級%requested沒有BiS,使用最接近的物品等級%resolved。', + 'No hay BiS en iLvl %requested, usando coincidencia más cercana en iLvl %resolved.', + 'No hay BiS en iLvl %requested, usando coincidencia más cercana en iLvl %resolved.', + 'Нет BiS на iLvl %requested, использую ближайшее совпадение на iLvl %resolved.'), + + (1777, 'bis_applying_msg', 'Applying BiS gear', 0, 0, + 'BiS 장비를 적용합니다.', + 'Application de l''équipement BiS.', + 'Wende BiS-Ausrüstung an.', + '正在装备BiS装备。', + '正在裝備BiS裝備。', + 'Aplicando equipo BiS.', + 'Aplicando equipo BiS.', + 'Применяю BiS-снаряжение.'), + + (1778, 'bis_applied_msg', 'BiS applied', 0, 0, + 'BiS 장비가 적용되었습니다.', + 'Équipement BiS appliqué.', + 'BiS-Ausrüstung angewendet.', + 'BiS装备已应用。', + 'BiS裝備已套用。', + 'Equipo BiS aplicado.', + 'Equipo BiS aplicado.', + 'BiS-снаряжение применено.'); + +INSERT INTO ai_playerbot_texts_chance (name, probability) VALUES + ('bis_autogear_unavailable_error', 100), + ('bis_no_rows_fallback', 100), + ('bis_command_unavailable_error', 100), + ('bis_altbot_refused_error', 100), + ('bis_quality_floor_error', 100), + ('bis_pvp_refused_error', 100), + ('bis_invalid_arg_error', 100), + ('bis_arg_above_limit_error', 100), + ('bis_no_rows_autogear_msg', 100), + ('bis_closest_match_msg', 100), + ('bis_applying_msg', 100), + ('bis_applied_msg', 100); diff --git a/data/sql/playerbots/updates/2026_05_19_00_ai_playerbot_audit_texts.sql b/data/sql/playerbots/updates/2026_05_19_00_ai_playerbot_audit_texts.sql new file mode 100644 index 00000000000..39639b968e4 --- /dev/null +++ b/data/sql/playerbots/updates/2026_05_19_00_ai_playerbot_audit_texts.sql @@ -0,0 +1,489 @@ +DELETE FROM ai_playerbot_texts WHERE name IN ( + 'quest_accept_debug', + 'quest_already_have_error', + 'quest_cant_take_error', + 'arena_team_already_in_team', + 'arena_team_thanks_for_invite', + 'area_trigger_follow_too_far_error', + 'area_trigger_wait_for_me', + 'attack_no_target_error', + 'attack_target_not_in_world_error', + 'attack_in_flight_error', + 'attack_pvp_prohibited_error', + 'attack_target_friendly_error', + 'attack_target_dead_error', + 'attack_target_not_in_sight_error', + 'attack_already_attacking_error', + 'attack_invalid_target_error', + 'bank_no_banker_nearby_error', + 'move_from_group', + 'running_away', + 'clean_quest_log_started', + 'quest_trivial_will_remove', + 'quest_has_been_removed', + 'quest_not_trivial_kept', + 'quest_removed_debug', + 'quest_removed_with_name', + 'guild_accept_inviter_not_in_guild', + 'guild_accept_already_in_guild', + 'guild_accept_declined', + 'outfit_usage_add', + 'outfit_usage_remove', + 'outfit_usage_equip', + 'outfit_set_as', + 'outfit_equipping', + 'outfit_replace_current', + 'outfit_resetting', + 'outfit_updating_current', + 'outfit_item_removed_from', + 'outfit_item_added_to', + 'release_spirit_not_dead_wait', + 'release_spirit_already_spirit', + 'release_spirit_releasing', + 'release_spirit_meet_graveyard', + 'send_mail_no_mailbox_nearby', + 'send_mail_one_item_only', + 'send_mail_cannot_send_money', + 'send_mail_not_enough_money', + 'send_mail_sending_to', + 'send_mail_cannot_send_item', + 'send_mail_item_not_for_sale', + 'send_mail_sent_to', + 'craft_reset', + 'craft_usage', + 'craft_cannot_craft', + 'craft_summary', + 'set_home_success', + 'set_home_no_innkeeper_error', + 'quest_shared', + 'tame_invalid_id_error', + 'tame_usage_error', + 'tame_pet_changed', + 'tame_pet_changed_initialized', + 'tame_exotic_requires_beast_mastery', + 'tame_no_pet_by_name', + 'tame_no_pet_by_id', + 'tame_no_pet_by_family', + 'tame_no_pet_to_rename', + 'tame_pet_name_length_error', + 'tame_pet_name_alpha_error', + 'tame_pet_name_forbidden_error', + 'tame_pet_renamed', + 'tame_pet_rename_refresh_hint', + 'tame_only_hunters_level_10', + 'tame_creature_template_not_found', + 'tame_create_pet_failed', + 'tame_pet_abandoned', + 'tame_no_hunter_pet_to_abandon', + 'taxi_ready_next_flight', + 'taxi_cant_fly_with_you', + 'taxi_no_flightmaster_nearby', + 'trade_busy_now', + 'trade_disabled', + 'trade_thank_you_player', + 'trade_selling_disabled', + 'trade_buying_disabled', + 'trade_item_not_for_sale', + 'trade_item_not_needed', + 'trade_no_items_error', + 'trade_discount_buy_only', + 'trade_success_pleasure', + 'trade_success_fair_trade', + 'trade_success_thanks', + 'trade_success_off_with_you', + 'trade_want_money_for_this', + 'use_item_none_available', + 'use_gameobject', + 'socket_does_not_fit', + 'use_item_on_target', + 'use_item', + 'socketing_item_with_gem', + 'meeting_stone_in_combat', + 'meeting_stone_welcome', + 'meeting_stone_none_nearby', + 'meeting_stone_none_near_you', + 'meeting_stone_no_hearthstone_self', + 'meeting_stone_no_hearthstone_you', + 'meeting_stone_hearthstone_not_ready_self', + 'meeting_stone_hearthstone_not_ready_you', + 'meeting_stone_no_innkeepers_nearby', + 'meeting_stone_no_innkeepers_near_you', + 'meeting_stone_cannot_summon_vehicle', + 'meeting_stone_cannot_summon_master_in_combat', + 'meeting_stone_cannot_summon_master_dead', + 'meeting_stone_cannot_summon_bot_dead', + 'meeting_stone_revived', + 'meeting_stone_not_enough_space', + 'new_rpg_quest_accepted', + 'new_rpg_quest_rewarded', + 'new_rpg_quest_dropped', + 'rpg_item_better_for_player', + 'rpg_start_trade_with_player' +); + +DELETE FROM ai_playerbot_texts_chance WHERE name IN ( + 'quest_accept_debug', + 'quest_already_have_error', + 'quest_cant_take_error', + 'arena_team_already_in_team', + 'arena_team_thanks_for_invite', + 'area_trigger_follow_too_far_error', + 'area_trigger_wait_for_me', + 'attack_no_target_error', + 'attack_target_not_in_world_error', + 'attack_in_flight_error', + 'attack_pvp_prohibited_error', + 'attack_target_friendly_error', + 'attack_target_dead_error', + 'attack_target_not_in_sight_error', + 'attack_already_attacking_error', + 'attack_invalid_target_error', + 'bank_no_banker_nearby_error', + 'move_from_group', + 'running_away', + 'clean_quest_log_started', + 'quest_trivial_will_remove', + 'quest_has_been_removed', + 'quest_not_trivial_kept', + 'quest_removed_debug', + 'quest_removed_with_name', + 'guild_accept_inviter_not_in_guild', + 'guild_accept_already_in_guild', + 'guild_accept_declined', + 'outfit_usage_add', + 'outfit_usage_remove', + 'outfit_usage_equip', + 'outfit_set_as', + 'outfit_equipping', + 'outfit_replace_current', + 'outfit_resetting', + 'outfit_updating_current', + 'outfit_item_removed_from', + 'outfit_item_added_to', + 'release_spirit_not_dead_wait', + 'release_spirit_already_spirit', + 'release_spirit_releasing', + 'release_spirit_meet_graveyard', + 'send_mail_no_mailbox_nearby', + 'send_mail_one_item_only', + 'send_mail_cannot_send_money', + 'send_mail_not_enough_money', + 'send_mail_sending_to', + 'send_mail_cannot_send_item', + 'send_mail_item_not_for_sale', + 'send_mail_sent_to', + 'craft_reset', + 'craft_usage', + 'craft_cannot_craft', + 'craft_summary', + 'set_home_success', + 'set_home_no_innkeeper_error', + 'quest_shared', + 'tame_invalid_id_error', + 'tame_usage_error', + 'tame_pet_changed', + 'tame_pet_changed_initialized', + 'tame_exotic_requires_beast_mastery', + 'tame_no_pet_by_name', + 'tame_no_pet_by_id', + 'tame_no_pet_by_family', + 'tame_no_pet_to_rename', + 'tame_pet_name_length_error', + 'tame_pet_name_alpha_error', + 'tame_pet_name_forbidden_error', + 'tame_pet_renamed', + 'tame_pet_rename_refresh_hint', + 'tame_only_hunters_level_10', + 'tame_creature_template_not_found', + 'tame_create_pet_failed', + 'tame_pet_abandoned', + 'tame_no_hunter_pet_to_abandon', + 'taxi_ready_next_flight', + 'taxi_cant_fly_with_you', + 'taxi_no_flightmaster_nearby', + 'trade_busy_now', + 'trade_disabled', + 'trade_thank_you_player', + 'trade_selling_disabled', + 'trade_buying_disabled', + 'trade_item_not_for_sale', + 'trade_item_not_needed', + 'trade_no_items_error', + 'trade_discount_buy_only', + 'trade_success_pleasure', + 'trade_success_fair_trade', + 'trade_success_thanks', + 'trade_success_off_with_you', + 'trade_want_money_for_this', + 'use_item_none_available', + 'use_gameobject', + 'socket_does_not_fit', + 'use_item_on_target', + 'use_item', + 'socketing_item_with_gem', + 'meeting_stone_in_combat', + 'meeting_stone_welcome', + 'meeting_stone_none_nearby', + 'meeting_stone_none_near_you', + 'meeting_stone_no_hearthstone_self', + 'meeting_stone_no_hearthstone_you', + 'meeting_stone_hearthstone_not_ready_self', + 'meeting_stone_hearthstone_not_ready_you', + 'meeting_stone_no_innkeepers_nearby', + 'meeting_stone_no_innkeepers_near_you', + 'meeting_stone_cannot_summon_vehicle', + 'meeting_stone_cannot_summon_master_in_combat', + 'meeting_stone_cannot_summon_master_dead', + 'meeting_stone_cannot_summon_bot_dead', + 'meeting_stone_revived', + 'meeting_stone_not_enough_space', + 'new_rpg_quest_accepted', + 'new_rpg_quest_rewarded', + 'new_rpg_quest_dropped', + 'rpg_item_better_for_player', + 'rpg_start_trade_with_player' +); + +INSERT INTO ai_playerbot_texts (id, name, text, say_type, reply_type, text_loc1, text_loc2, text_loc3, text_loc4, text_loc5, text_loc6, text_loc7, text_loc8) VALUES +(1779, 'quest_accept_debug', 'Quest [%quest] accepted', 0, 0, '퀘스트 [%quest] 수락', 'Quête [%quest] acceptée', 'Quest [%quest] angenommen', '任务 [%quest] 已接受', '任務 [%quest] 已接受', 'Misión [%quest] aceptada', 'Misión [%quest] aceptada', 'Задание [%quest] принято'), +(1780, 'quest_already_have_error', 'I have this quest', 0, 0, '이미 이 퀘스트를 가지고 있습니다', 'J''ai déjà cette quête', 'Ich habe diese Quest bereits', '我已经有这个任务了', '我已經有這個任務了', 'Ya tengo esta misión', 'Ya tengo esta misión', 'У меня уже есть это задание'), +(1781, 'quest_cant_take_error', 'I can''t take this quest', 0, 0, '이 퀘스트를 받을 수 없습니다', 'Je ne peux pas prendre cette quête', 'Ich kann diese Quest nicht annehmen', '我无法接受这个任务', '我無法接受這個任務', 'No puedo aceptar esta misión', 'No puedo aceptar esta misión', 'Я не могу взять это задание'), +(1782, 'arena_team_already_in_team', 'Sorry, I am already in such team', 0, 0, '죄송하지만 이미 그런 팀에 속해 있습니다', 'Désolé, je suis déjà dans une telle équipe', 'Entschuldigung, ich bin bereits in so einem Team', '抱歉,我已经在这样的队伍里了', '抱歉,我已經在這樣的隊伍裡了', 'Lo siento, ya estoy en un equipo así', 'Lo siento, ya estoy en un equipo así', 'Извините, я уже состою в такой команде'), +(1783, 'arena_team_thanks_for_invite', 'Thanks for the invite!', 0, 0, '초대해 주셔서 감사합니다!', 'Merci pour l''invitation !', 'Danke für die Einladung!', '谢谢你的邀请!', '謝謝你的邀請!', 'Gracias por la invitación!', 'Gracias por la invitación!', 'Спасибо за приглашение!'), +(1784, 'area_trigger_follow_too_far_error', 'I won''t follow: too far away', 0, 0, '너무 멀어서 따라가지 않겠습니다', 'Je ne suivrai pas : c''est trop loin', 'Ich werde nicht folgen: zu weit entfernt', '我不会跟随:距离太远了', '我不會跟隨:距離太遠了', 'No te seguiré: estás demasiado lejos', 'No te seguiré: estás demasiado lejos', 'Я не пойду следом: слишком далеко'), +(1785, 'area_trigger_wait_for_me', 'Wait for me', 0, 0, '잠깐만 기다려 주세요', 'Attendez-moi', 'Warte auf mich', '等等我', '等等我', 'Espérame', 'Espérame', 'Подожди меня'), +(1786, 'attack_no_target_error', 'I have no target', 0, 0, '대상이 없습니다', 'Je n''ai pas de cible', 'Ich habe kein Ziel', '我没有目标', '我沒有目標', 'No tengo objetivo', 'No tengo objetivo', 'У меня нет цели'), +(1787, 'attack_target_not_in_world_error', '%target is no longer in the world.', 0, 0, '%target은(는) 더 이상 월드에 없습니다.', '%target n''est plus dans le monde.', '%target ist nicht mehr in der Welt.', '%target 已不在世界中。', '%target 已不在世界中。', '%target ya no está en el mundo.', '%target ya no está en el mundo.', '%target больше не находится в мире.'), +(1788, 'attack_in_flight_error', 'I cannot attack in flight', 0, 0, '비행 중에는 공격할 수 없습니다', 'Je ne peux pas attaquer en vol', 'Ich kann im Flug nicht angreifen', '飞行中无法攻击', '飛行中無法攻擊', 'No puedo atacar en vuelo', 'No puedo atacar en vuelo', 'Я не могу атаковать в полёте'), +(1789, 'attack_pvp_prohibited_error', 'I cannot attack other players in PvP prohibited areas.', 0, 0, 'PvP 금지 지역에서는 다른 플레이어를 공격할 수 없습니다.', 'Je ne peux pas attaquer d''autres joueurs dans les zones où le PvP est interdit.', 'Ich kann andere Spieler in PvP-verbotenen Gebieten nicht angreifen.', '我不能在禁止 PvP 的区域攻击其他玩家。', '我不能在禁止 PvP 的區域攻擊其他玩家。', 'No puedo atacar a otros jugadores en zonas donde el JcJ está prohibido.', 'No puedo atacar a otros jugadores en zonas donde el JcJ está prohibido.', 'Я не могу атаковать других игроков в зонах, где PvP запрещено.'), +(1790, 'attack_target_friendly_error', '%target is friendly to me.', 0, 0, '%target은(는) 우호적인 대상입니다.', '%target m''est amical.', '%target ist mir gegenüber freundlich.', '%target 对我是友方目标。', '%target 對我是友方目標。', '%target es amistoso conmigo.', '%target es amistoso conmigo.', '%target дружелюбен ко мне.'), +(1791, 'attack_target_dead_error', '%target is dead.', 0, 0, '%target은(는) 죽었습니다.', '%target est mort.', '%target ist tot.', '%target 已经死了。', '%target 已經死了。', '%target está muerto.', '%target está muerto.', '%target мёртв.'), +(1792, 'attack_target_not_in_sight_error', '%target is not in my sight.', 0, 0, '%target이(가) 시야에 없습니다.', '%target n''est pas dans mon champ de vision.', '%target ist nicht in Sichtweite.', '%target 不在我的视野中。', '%target 不在我的視野中。', '%target no está a mi vista.', '%target no está a mi vista.', '%target вне поля моего зрения.'), +(1793, 'attack_already_attacking_error', 'I am already attacking %target.', 0, 0, '이미 %target을(를) 공격하고 있습니다.', 'J''attaque déjà %target.', 'Ich greife %target bereits an.', '我已经在攻击 %target。', '我已經在攻擊 %target。', 'Ya estoy atacando a %target.', 'Ya estoy atacando a %target.', 'Я уже атакую %target.'), +(1794, 'attack_invalid_target_error', 'I cannot attack an invalid target.', 0, 0, '유효하지 않은 대상은 공격할 수 없습니다.', 'Je ne peux pas attaquer une cible invalide.', 'Ich kann kein ungültiges Ziel angreifen.', '我不能攻击无效目标。', '我不能攻擊無效目標。', 'No puedo atacar un objetivo no válido.', 'No puedo atacar un objetivo no válido.', 'Я не могу атаковать недопустимую цель.'), +(1795, 'bank_no_banker_nearby_error', 'Cannot find banker nearby', 0, 0, '근처에 은행원이 없습니다', 'Impossible de trouver un banquier à proximité', 'Kein Bankier in der Nähe gefunden', '附近找不到银行职员', '附近找不到銀行職員', 'No encuentro un banquero cerca', 'No encuentro un banquero cerca', 'Поблизости нет банкира'), +(1796, 'move_from_group', 'Moving away from group', 0, 0, '파티에서 멀어지는 중입니다', 'Je m''éloigne du groupe', 'Ich entferne mich von der Gruppe', '正在远离队伍', '正在遠離隊伍', 'Me estoy alejando del grupo', 'Me estoy alejando del grupo', 'Отхожу от группы'), +(1797, 'running_away', 'Running away', 0, 0, '도망치는 중입니다', 'Je m''enfuis', 'Ich laufe weg', '正在逃跑', '正在逃跑', 'Estoy huyendo', 'Estoy huyendo', 'Убегаю'), +(1798, 'clean_quest_log_started', 'Clean Quest Log command received, removing grey/trivial quests...', 0, 0, '퀘스트 로그 정리 명령을 받았습니다. 회색/사소한 퀘스트를 제거하는 중입니다...', 'Commande de nettoyage du journal des quêtes reçue, suppression des quêtes grises/triviales...', 'Befehl zum Bereinigen des Questlogs erhalten, graue/triviale Quests werden entfernt...', '已收到清理任务日志命令,正在移除灰色/琐碎任务...', '已收到清理任務日誌命令,正在移除灰色/瑣碎任務...', 'Comando para limpiar el registro de misiones recibido, eliminando misiones grises/triviales...', 'Comando para limpiar el registro de misiones recibido, eliminando misiones grises/triviales...', 'Получена команда очистки журнала заданий, удаляю серые/простые задания...'), +(1799, 'quest_trivial_will_remove', 'Quest [%title] will be removed because it is trivial (grey).', 0, 0, '퀘스트 [%title]은(는) 사소한(회색) 퀘스트이므로 제거됩니다.', 'La quête [%title] sera supprimée car elle est triviale (grise).', 'Quest [%title] wird entfernt, weil sie trivial (grau) ist.', '任务 [%title] 将被移除,因为它是琐碎的(灰色)任务。', '任務 [%title] 將被移除,因為它是瑣碎的(灰色)任務。', 'La misión [%title] se eliminará porque es trivial (gris).', 'La misión [%title] se eliminará porque es trivial (gris).', 'Задание [%title] будет удалено, так как оно простое (серое).'), +(1800, 'quest_has_been_removed', 'Quest [%title] has been removed.', 0, 0, '퀘스트 [%title]이(가) 제거되었습니다.', 'La quête [%title] a été supprimée.', 'Quest [%title] wurde entfernt.', '任务 [%title] 已被移除。', '任務 [%title] 已被移除。', 'La misión [%title] ha sido eliminada.', 'La misión [%title] ha sido eliminada.', 'Задание [%title] было удалено.'), +(1801, 'quest_not_trivial_kept', 'Quest [%title] is not trivial and will be kept.', 0, 0, '퀘스트 [%title]은(는) 사소하지 않으므로 유지됩니다.', 'La quête [%title] n''est pas triviale et sera conservée.', 'Quest [%title] ist nicht trivial und wird behalten.', '任务 [%title] 并不琐碎,将被保留。', '任務 [%title] 並不瑣碎,將被保留。', 'La misión [%title] no es trivial y se conservará.', 'La misión [%title] no es trivial y se conservará.', 'Задание [%title] не является простым и будет сохранено.'), +(1802, 'quest_removed_debug', 'Quest [%quest] removed', 0, 0, '퀘스트 [%quest] 제거됨', 'Quête [%quest] supprimée', 'Quest [%quest] entfernt', '任务 [%quest] 已移除', '任務 [%quest] 已移除', 'Misión [%quest] eliminada', 'Misión [%quest] eliminada', 'Задание [%quest] удалено'), +(1803, 'quest_removed_with_name', 'Quest removed %quest', 0, 0, '퀘스트 제거됨 %quest', 'Quête supprimée %quest', 'Quest entfernt %quest', '任务已移除 %quest', '任務已移除 %quest', 'Misión eliminada %quest', 'Misión eliminada %quest', 'Задание удалено %quest'), +(1804, 'guild_accept_inviter_not_in_guild', 'You are not in a guild!', 0, 0, '당신은 길드에 속해 있지 않습니다!', 'Vous n''êtes pas dans une guilde !', 'Du bist in keiner Gilde!', '你不在公会中!', '你不在公會中!', 'No estás en un gremio!', 'No estás en un gremio!', 'Вы не состоите в гильдии!'), +(1805, 'guild_accept_already_in_guild', 'Sorry, I am in a guild already', 0, 0, '죄송하지만 이미 길드에 속해 있습니다', 'Désolé, je suis déjà dans une guilde', 'Entschuldigung, ich bin bereits in einer Gilde', '抱歉,我已经在公会里了', '抱歉,我已經在公會裡了', 'Lo siento, ya estoy en un gremio', 'Lo siento, ya estoy en un gremio', 'Извините, я уже в гильдии'), +(1806, 'guild_accept_declined', 'Sorry, I don''t want to join your guild :(', 0, 0, '죄송하지만 당신의 길드에 가입하고 싶지 않습니다 :(', 'Désolé, je ne veux pas rejoindre votre guilde :(', 'Entschuldigung, ich möchte deiner Gilde nicht beitreten :(', '抱歉,我不想加入你的公会 :(', '抱歉,我不想加入你的公會 :(', 'Lo siento, no quiero unirme a tu gremio :(', 'Lo siento, no quiero unirme a tu gremio :(', 'Извините, я не хочу вступать в вашу гильдию :('), +(1807, 'outfit_usage_add', 'outfit +[item] to add items', 0, 0, '아이템을 추가하려면 outfit +[item]', 'outfit +[item] pour ajouter des objets', 'outfit +[item], um Gegenstände hinzuzufügen', '使用 outfit +[item] 添加物品', '使用 outfit +[item] 添加物品', 'outfit +[item] para agregar objetos', 'outfit +[item] para agregar objetos', 'outfit +[item], чтобы добавить предметы'), +(1808, 'outfit_usage_remove', 'outfit -[item] to remove items', 0, 0, '아이템을 제거하려면 outfit -[item]', 'outfit -[item] pour retirer des objets', 'outfit -[item], um Gegenstände zu entfernen', '使用 outfit -[item] 移除物品', '使用 outfit -[item] 移除物品', 'outfit -[item] para eliminar objetos', 'outfit -[item] para eliminar objetos', 'outfit -[item], чтобы убрать предметы'), +(1809, 'outfit_usage_equip', 'outfit equip/replace to equip items', 0, 0, '아이템을 장착하려면 outfit equip/replace', 'outfit equip/replace pour équiper des objets', 'outfit equip/replace, um Gegenstände anzulegen', '使用 outfit equip/replace 装备物品', '使用 outfit equip/replace 裝備物品', 'outfit equip/replace para equipar objetos', 'outfit equip/replace para equipar objetos', 'outfit equip/replace, чтобы экипировать предметы'), +(1810, 'outfit_set_as', 'Setting outfit %name as %param', 0, 0, '복장 %name을(를) %param(으)로 설정하는 중입니다', 'Définition de la tenue %name comme %param', 'Setze Outfit %name als %param', '正在将装备方案 %name 设置为 %param', '正在將裝備方案 %name 設定為 %param', 'Estableciendo el atuendo %name como %param', 'Estableciendo el atuendo %name como %param', 'Устанавливаю комплект %name как %param'), +(1811, 'outfit_equipping', 'Equipping outfit %name', 0, 0, '복장 %name을(를) 장착하는 중입니다', 'Équipement de la tenue %name', 'Outfit %name wird angelegt', '正在装备方案 %name', '正在裝備方案 %name', 'Equipando el atuendo %name', 'Equipando el atuendo %name', 'Экипирую комплект %name'), +(1812, 'outfit_replace_current', 'Replacing current equip with outfit %name', 0, 0, '현재 장비를 복장 %name으로 교체하는 중입니다', 'Remplacement de l''équipement actuel par la tenue %name', 'Aktuelle Ausrüstung wird durch Outfit %name ersetzt', '正在用装备方案 %name 替换当前装备', '正在用裝備方案 %name 替換目前裝備', 'Reemplazando el equipo actual con el atuendo %name', 'Reemplazando el equipo actual con el atuendo %name', 'Заменяю текущую экипировку комплектом %name'), +(1813, 'outfit_resetting', 'Resetting outfit %name', 0, 0, '복장 %name을(를) 초기화하는 중입니다', 'Réinitialisation de la tenue %name', 'Outfit %name wird zurückgesetzt', '正在重置装备方案 %name', '正在重置裝備方案 %name', 'Restableciendo el atuendo %name', 'Restableciendo el atuendo %name', 'Сбрасываю комплект %name'), +(1814, 'outfit_updating_current', 'Updating with current items outfit %name', 0, 0, '현재 아이템으로 복장 %name을(를) 업데이트하는 중입니다', 'Mise à jour de la tenue %name avec les objets actuels', 'Outfit %name wird mit der aktuellen Ausrüstung aktualisiert', '正在用当前物品更新装备方案 %name', '正在用目前物品更新裝備方案 %name', 'Actualizando el atuendo %name con los objetos actuales', 'Actualizando el atuendo %name con los objetos actuales', 'Обновляю комплект %name текущими предметами'), +(1815, 'outfit_item_removed_from', '%item removed from %name', 0, 0, '%name에서 %item이(가) 제거되었습니다', '%item retiré de %name', '%item aus %name entfernt', '已从 %name 中移除 %item', '已從 %name 中移除 %item', '%item eliminado de %name', '%item eliminado de %name', '%item удалён из %name'), +(1816, 'outfit_item_added_to', '%item added to %name', 0, 0, '%item이(가) %name에 추가되었습니다', '%item ajouté à %name', '%item zu %name hinzugefügt', '已将 %item 添加到 %name', '已將 %item 添加到 %name', '%item agregado a %name', '%item agregado a %name', '%item добавлен в %name'), +(1817, 'release_spirit_not_dead_wait', 'I am not dead, will wait here', 0, 0, '저는 죽지 않았습니다. 여기서 기다리겠습니다', 'Je ne suis pas mort, j''attendrai ici', 'Ich bin nicht tot und werde hier warten', '我还没死,会在这里等', '我還沒死,會在這裡等', 'No estoy muerto, esperaré aquí', 'No estoy muerto, esperaré aquí', 'Я не мёртв, буду ждать здесь'), +(1818, 'release_spirit_already_spirit', 'I am already a spirit', 0, 0, '저는 이미 유령입니다', 'Je suis déjà un esprit', 'Ich bin bereits ein Geist', '我已经是灵魂状态了', '我已經是靈魂狀態了', 'Ya soy un espíritu', 'Ya soy un espíritu', 'Я уже дух'), +(1819, 'release_spirit_releasing', 'Releasing...', 0, 0, '해방 중...', 'Je libère mon esprit...', 'Geist wird freigesetzt...', '正在释放灵魂...', '正在釋放靈魂...', 'Liberando espíritu...', 'Liberando espíritu...', 'Освобождаю дух...'), +(1820, 'release_spirit_meet_graveyard', 'Meet me at the graveyard', 0, 0, '묘지에서 만나요', 'Retrouvez-moi au cimetière', 'Triff mich auf dem Friedhof', '墓地见', '墓地見', 'Encuéntrame en el cementerio', 'Encuéntrame en el cementerio', 'Встречаемся на кладбище'), +(1821, 'send_mail_no_mailbox_nearby', 'There is no mailbox nearby', 0, 0, '근처에 우체통이 없습니다', 'Il n''y a pas de boîte aux lettres à proximité', 'Kein Briefkasten in der Nähe', '附近没有邮箱', '附近沒有郵箱', 'No hay un buzón cerca', 'No hay un buzón cerca', 'Поблизости нет почтового ящика'), +(1822, 'send_mail_one_item_only', 'You can not request more than one item', 0, 0, '둘 이상의 아이템은 요청할 수 없습니다', 'Vous ne pouvez pas demander plus d''un objet', 'Du kannst nicht mehr als einen Gegenstand anfordern', '你不能请求多于一件物品', '你不能要求超過一件物品', 'No puedes solicitar más de un objeto', 'No puedes solicitar más de un objeto', 'Нельзя запрашивать больше одного предмета'), +(1823, 'send_mail_cannot_send_money', 'I cannot send money', 0, 0, '돈은 보낼 수 없습니다', 'Je ne peux pas envoyer d''argent', 'Ich kann kein Geld senden', '我不能寄送钱', '我不能寄送金錢', 'No puedo enviar dinero', 'No puedo enviar dinero', 'Я не могу отправить деньги'), +(1824, 'send_mail_not_enough_money', 'I don''t have enough money', 0, 0, '돈이 충분하지 않습니다', 'Je n''ai pas assez d''argent', 'Ich habe nicht genug Geld', '我没有足够的钱', '我沒有足夠的錢', 'No tengo suficiente dinero', 'No tengo suficiente dinero', 'У меня недостаточно денег'), +(1825, 'send_mail_sending_to', 'Sending mail to %receiver', 0, 0, '%receiver에게 우편을 보내는 중입니다', 'Envoi du courrier à %receiver', 'Sende Post an %receiver', '正在向 %receiver 发送邮件', '正在向 %receiver 發送郵件', 'Enviando correo a %receiver', 'Enviando correo a %receiver', 'Отправляю почту %receiver'), +(1826, 'send_mail_cannot_send_item', 'Cannot send %item', 0, 0, '%item을(를) 보낼 수 없습니다', 'Impossible d''envoyer %item', '%item kann nicht gesendet werden', '无法发送 %item', '無法發送 %item', 'No se puede enviar %item', 'No se puede enviar %item', 'Невозможно отправить %item'), +(1827, 'send_mail_item_not_for_sale', '%item: it is not for sale', 0, 0, '%item: 판매 대상이 아닙니다', '%item : ce n''est pas à vendre', '%item: Das steht nicht zum Verkauf', '%item:这不是出售物品', '%item:這不是出售物品', '%item: esto no está en venta', '%item: esto no está en venta', '%item: это не продаётся'), +(1828, 'send_mail_sent_to', 'Sent mail to %receiver', 0, 0, '%receiver에게 우편을 보냈습니다', 'Courrier envoyé à %receiver', 'Post an %receiver gesendet', '已向 %receiver 发送邮件', '已向 %receiver 發送郵件', 'Correo enviado a %receiver', 'Correo enviado a %receiver', 'Почта отправлена %receiver'), +(1829, 'craft_reset', 'I will not craft anything', 0, 0, '아무것도 제작하지 않겠습니다', 'Je ne fabriquerai rien', 'Ich werde nichts herstellen', '我不会制作任何东西', '我不會製作任何東西', 'No fabricaré nada', 'No fabricaré nada', 'Я ничего не буду создавать'), +(1830, 'craft_usage', 'Usage: ''craft [itemId]'' or ''craft reset''', 0, 0, '사용법: ''craft [itemId]'' 또는 ''craft reset''', 'Utilisation : ''craft [itemId]'' ou ''craft reset''', 'Verwendung: ''craft [itemId]'' oder ''craft reset''', '用法:''craft [itemId]'' 或 ''craft reset''', '用法:''craft [itemId]'' 或 ''craft reset''', 'Uso: ''craft [itemId]'' o ''craft reset''', 'Uso: ''craft [itemId]'' o ''craft reset''', 'Использование: ''craft [itemId]'' или ''craft reset'''), +(1831, 'craft_cannot_craft', 'I cannot craft this', 0, 0, '이것은 제작할 수 없습니다', 'Je ne peux pas fabriquer ceci', 'Ich kann das nicht herstellen', '我无法制作这个', '我無法製作這個', 'No puedo fabricar esto', 'No puedo fabricar esto', 'Я не могу это создать'), +(1832, 'craft_summary', 'I will craft %item using reagents: %reagents (craft fee: %money)', 0, 0, '재료 %reagents을(를) 사용해 %item을(를) 제작하겠습니다 (제작 수수료: %money)', 'Je fabriquerai %item en utilisant les composants : %reagents (frais de fabrication : %money)', 'Ich werde %item mit folgenden Reagenzien herstellen: %reagents (Herstellungsgebühr: %money)', '我将使用材料 %reagents 制作 %item(制作费用:%money)', '我將使用材料 %reagents 製作 %item(製作費用:%money)', 'Fabricaré %item usando los materiales: %reagents (tarifa de fabricación: %money)', 'Fabricaré %item usando los materiales: %reagents (tarifa de fabricación: %money)', 'Я изготовлю %item, используя реагенты: %reagents (плата за изготовление: %money)'), +(1833, 'set_home_success', 'This inn is my new home', 0, 0, '이 여관을 새로운 집으로 설정했습니다', 'Cette auberge est ma nouvelle maison', 'Dieses Gasthaus ist mein neues Zuhause', '这家旅店是我的新家', '這間旅店是我的新家', 'Esta posada es mi nuevo hogar', 'Esta posada es mi nuevo hogar', 'Этот трактир теперь мой новый дом'), +(1834, 'set_home_no_innkeeper_error', 'Can''t find any innkeeper around', 0, 0, '주변에서 여관주인을 찾을 수 없습니다', 'Impossible de trouver un aubergiste aux alentours', 'Kein Gastwirt in der Nähe gefunden', '附近找不到旅店老板', '附近找不到旅店老闆', 'No encuentro ningún posadero cerca', 'No encuentro ningún posadero cerca', 'Не удалось найти поблизости трактирщика'), +(1835, 'quest_shared', 'Quest shared', 0, 0, '퀘스트 공유됨', 'Quête partagée', 'Quest geteilt', '任务已共享', '任務已共享', 'Misión compartida', 'Misión compartida', 'Задание поделено'), +(1836, 'tame_invalid_id_error', 'Invalid tame id.', 0, 0, '잘못된 길들이기 ID입니다.', 'Identifiant de dressage invalide.', 'Ungültige Zähm-ID.', '无效的驯服 ID。', '無效的馴服 ID。', 'ID de domesticación no válido.', 'ID de domesticación no válido.', 'Недопустимый ID приручения.'), +(1837, 'tame_usage_error', 'Usage: tame name | tame id | tame family | tame rename | tame abandon', 0, 0, '사용법: tame name | tame id | tame family | tame rename | tame abandon', 'Utilisation : tame name | tame id | tame family | tame rename | tame abandon', 'Verwendung: tame name | tame id | tame family | tame rename | tame abandon', '用法:tame name | tame id | tame family | tame rename | tame abandon', '用法:tame name | tame id | tame family | tame rename | tame abandon', 'Uso: tame name | tame id | tame family | tame rename | tame abandon', 'Uso: tame name | tame id | tame family | tame rename | tame abandon', 'Использование: tame name | tame id | tame family | tame rename | tame abandon'), +(1838, 'tame_pet_changed', 'Pet changed to %name, ID: %id.', 0, 0, '소환수가 %name, ID: %id(으)로 변경되었습니다.', 'Familier changé en %name, ID : %id.', 'Begleiter geändert zu %name, ID: %id.', '宠物已更改为 %name,ID:%id。', '寵物已更改為 %name,ID:%id。', 'Mascota cambiada a %name, ID: %id.', 'Mascota cambiada a %name, ID: %id.', 'Питомец изменён на %name, ID: %id.'), +(1839, 'tame_pet_changed_initialized', 'Pet changed and initialized!', 0, 0, '소환수가 변경되고 초기화되었습니다!', 'Familier changé et initialisé !', 'Begleiter geändert und initialisiert!', '宠物已更改并初始化!', '寵物已更改並初始化!', 'La mascota ha sido cambiada e inicializada!', 'La mascota ha sido cambiada e inicializada!', 'Питомец изменён и инициализирован!'), +(1840, 'tame_exotic_requires_beast_mastery', 'I cannot use exotic pets unless I have the Beast Mastery talent.', 0, 0, '야수 지배 특성이 없으면 특수 야수 소환수를 사용할 수 없습니다.', 'Je ne peux pas utiliser de familiers exotiques sans le talent Maîtrise des bêtes.', 'Ich kann ohne das Talent Tierherrschaft keine exotischen Begleiter benutzen.', '如果没有野兽掌握天赋,我无法使用异种宠物。', '如果沒有野獸控制天賦,我無法使用異種寵物。', 'No puedo usar mascotas exóticas a menos que tenga el talento Dominio de bestias.', 'No puedo usar mascotas exóticas a menos que tenga el talento Dominio de bestias.', 'Я не могу использовать экзотических питомцев без таланта Повелитель зверей.'), +(1841, 'tame_no_pet_by_name', 'No tameable pet found with name: %name', 0, 0, '이름이 %name인 길들일 수 있는 소환수를 찾을 수 없습니다', 'Aucun familier apprivoisable trouvé avec le nom : %name', 'Kein zähmbares Tier mit dem Namen %name gefunden', '未找到名为 %name 的可驯服宠物', '未找到名為 %name 的可馴服寵物', 'No se encontró ninguna mascota domesticable con el nombre: %name', 'No se encontró ninguna mascota domesticable con el nombre: %name', 'Не найден приручаемый питомец с именем: %name'), +(1842, 'tame_no_pet_by_id', 'No tameable pet found with id: %id', 0, 0, 'ID가 %id인 길들일 수 있는 소환수를 찾을 수 없습니다', 'Aucun familier apprivoisable trouvé avec l''id : %id', 'Kein zähmbares Tier mit der ID %id gefunden', '未找到 ID 为 %id 的可驯服宠物', '未找到 ID 為 %id 的可馴服寵物', 'No se encontró ninguna mascota domesticable con el id: %id', 'No se encontró ninguna mascota domesticable con el id: %id', 'Не найден приручаемый питомец с id: %id'), +(1843, 'tame_no_pet_by_family', 'No tameable pet found with family: %family', 0, 0, '계열이 %family인 길들일 수 있는 소환수를 찾을 수 없습니다', 'Aucun familier apprivoisable trouvé avec la famille : %family', 'Kein zähmbares Tier mit der Familie %family gefunden', '未找到家族为 %family 的可驯服宠物', '未找到家族為 %family 的可馴服寵物', 'No se encontró ninguna mascota domesticable con la familia: %family', 'No se encontró ninguna mascota domesticable con la familia: %family', 'Не найден приручаемый питомец семейства: %family'), +(1844, 'tame_no_pet_to_rename', 'You have no pet to rename.', 0, 0, '이름을 바꿀 소환수가 없습니다.', 'Vous n''avez pas de familier à renommer.', 'Du hast kein Tier zum Umbenennen.', '你没有可重命名的宠物。', '你沒有可重新命名的寵物。', 'No tienes ninguna mascota para renombrar.', 'No tienes ninguna mascota para renombrar.', 'У вас нет питомца для переименования.'), +(1845, 'tame_pet_name_length_error', 'Pet name must be between 1 and 12 alphabetic characters.', 0, 0, '소환수 이름은 1자에서 12자의 알파벳 문자여야 합니다.', 'Le nom du familier doit contenir entre 1 et 12 caractères alphabétiques.', 'Der Name des Begleiters muss zwischen 1 und 12 alphabetischen Zeichen lang sein.', '宠物名字必须为 1 到 12 个字母字符。', '寵物名字必須為 1 到 12 個字母字元。', 'El nombre de la mascota debe tener entre 1 y 12 caracteres alfabéticos.', 'El nombre de la mascota debe tener entre 1 y 12 caracteres alfabéticos.', 'Имя питомца должно содержать от 1 до 12 буквенных символов.'), +(1846, 'tame_pet_name_alpha_error', 'Pet name must only contain alphabetic characters (A-Z, a-z).', 0, 0, '소환수 이름에는 알파벳 문자(A-Z, a-z)만 포함될 수 있습니다.', 'Le nom du familier ne doit contenir que des caractères alphabétiques (A-Z, a-z).', 'Der Name des Begleiters darf nur alphabetische Zeichen (A-Z, a-z) enthalten.', '宠物名字只能包含字母字符(A-Z, a-z)。', '寵物名字只能包含字母字元(A-Z, a-z)。', 'El nombre de la mascota solo puede contener caracteres alfabéticos (A-Z, a-z).', 'El nombre de la mascota solo puede contener caracteres alfabéticos (A-Z, a-z).', 'Имя питомца должно содержать только буквенные символы (A-Z, a-z).'), +(1847, 'tame_pet_name_forbidden_error', 'That pet name is forbidden. Please choose another name.', 0, 0, '그 소환수 이름은 사용할 수 없습니다. 다른 이름을 선택해 주세요.', 'Ce nom de familier est interdit. Veuillez en choisir un autre.', 'Dieser Name für den Begleiter ist verboten. Bitte wähle einen anderen Namen.', '该宠物名称被禁止使用。请选择其他名字。', '該寵物名稱被禁止使用。請選擇其他名字。', 'Ese nombre de mascota está prohibido. Por favor, elige otro nombre.', 'Ese nombre de mascota está prohibido. Por favor, elige otro nombre.', 'Это имя для питомца запрещено. Пожалуйста, выберите другое имя.'), +(1848, 'tame_pet_renamed', 'Your pet has been renamed to %name!', 0, 0, '당신의 소환수 이름이 %name(으)로 변경되었습니다!', 'Votre familier a été renommé en %name !', 'Dein Begleiter wurde in %name umbenannt!', '你的宠物已重命名为 %name!', '你的寵物已重新命名為 %name!', 'Tu mascota ha sido renombrada a %name!', 'Tu mascota ha sido renombrada a %name!', 'Ваш питомец был переименован в %name!'), +(1849, 'tame_pet_rename_refresh_hint', 'If you do not see the new name, please dismiss and recall your pet.', 0, 0, '새 이름이 보이지 않으면 소환수를 해제했다가 다시 소환해 주세요.', 'Si vous ne voyez pas le nouveau nom, veuillez renvoyer puis rappeler votre familier.', 'Wenn du den neuen Namen nicht siehst, schicke deinen Begleiter weg und rufe ihn erneut.', '如果你看不到新名字,请先解散再召回你的宠物。', '如果你看不到新名字,請先解散再召回你的寵物。', 'Si no ves el nuevo nombre, por favor retira y vuelve a invocar a tu mascota.', 'Si no ves el nuevo nombre, por favor retira y vuelve a invocar a tu mascota.', 'Если вы не видите новое имя, отпустите и снова призовите питомца.'), +(1850, 'tame_only_hunters_level_10', 'Only level 10+ hunters can have pets.', 0, 0, '10레벨 이상의 사냥꾼만 소환수를 가질 수 있습니다.', 'Seuls les chasseurs de niveau 10+ peuvent avoir des familiers.', 'Nur Jäger ab Stufe 10 können Begleiter haben.', '只有 10 级以上的猎人才能拥有宠物。', '只有 10 級以上的獵人才能擁有寵物。', 'Solo los cazadores de nivel 10 o superior pueden tener mascotas.', 'Solo los cazadores de nivel 10 o superior pueden tener mascotas.', 'Только охотники 10 уровня и выше могут иметь питомцев.'), +(1851, 'tame_creature_template_not_found', 'Creature template not found.', 0, 0, '생물 템플릿을 찾을 수 없습니다.', 'Modèle de créature introuvable.', 'Kreaturvorlage nicht gefunden.', '未找到生物模板。', '未找到生物範本。', 'No se encontró la plantilla de criatura.', 'No se encontró la plantilla de criatura.', 'Шаблон существа не найден.'), +(1852, 'tame_create_pet_failed', 'Failed to create pet.', 0, 0, '소환수 생성에 실패했습니다.', 'Échec de la création du familier.', 'Erstellen des Begleiters fehlgeschlagen.', '创建宠物失败。', '建立寵物失敗。', 'No se pudo crear la mascota.', 'No se pudo crear la mascota.', 'Не удалось создать питомца.'), +(1853, 'tame_pet_abandoned', 'Your pet has been abandoned.', 0, 0, '당신의 소환수를 버렸습니다.', 'Votre familier a été abandonné.', 'Dein Begleiter wurde freigelassen.', '你的宠物已被放弃。', '你的寵物已被放棄。', 'Tu mascota ha sido abandonada.', 'Tu mascota ha sido abandonada.', 'Ваш питомец был брошен.'), +(1854, 'tame_no_hunter_pet_to_abandon', 'You have no hunter pet to abandon.', 0, 0, '버릴 사냥꾼 소환수가 없습니다.', 'Vous n''avez pas de familier de chasseur à abandonner.', 'Du hast kein Jägertier zum Freilassen.', '你没有可放弃的猎人宠物。', '你沒有可放棄的獵人寵物。', 'No tienes ninguna mascota de cazador que abandonar.', 'No tienes ninguna mascota de cazador que abandonar.', 'У вас нет питомца охотника, которого можно бросить.'), +(1855, 'taxi_ready_next_flight', 'I am ready for the next flight', 0, 0, '다음 비행을 탈 준비가 되었습니다', 'Je suis prêt pour le prochain vol', 'Ich bin bereit für den nächsten Flug', '我已准备好进行下一次飞行', '我已準備好進行下一次飛行', 'Estoy listo para el siguiente vuelo', 'Estoy listo para el siguiente vuelo', 'Я готов к следующему перелёту'), +(1856, 'taxi_cant_fly_with_you', 'I can''t fly with you', 0, 0, '당신과 함께 날 수 없습니다', 'Je ne peux pas voler avec vous', 'Ich kann nicht mit dir fliegen', '我不能和你一起飞', '我不能和你一起飛', 'No puedo volar contigo', 'No puedo volar contigo', 'Я не могу лететь с вами'), +(1857, 'taxi_no_flightmaster_nearby', 'Cannot find any flightmaster to talk', 0, 0, '대화할 비행 조련사를 찾을 수 없습니다', 'Impossible de trouver un maître de vol à qui parler', 'Keinen Flugmeister zum Ansprechen gefunden', '找不到可以交谈的飞行管理员', '找不到可以交談的飛行管理員', 'No encuentro ningún maestro de vuelo con quien hablar', 'No encuentro ningún maestro de vuelo con quien hablar', 'Не могу найти распорядителя полётов, с которым можно поговорить'), +(1858, 'trade_busy_now', 'I''m kind of busy now', 0, 0, '지금은 좀 바쁩니다', 'Je suis un peu occupé en ce moment', 'Ich bin gerade etwas beschäftigt', '我现在有点忙', '我現在有點忙', 'Ahora estoy un poco ocupado', 'Ahora estoy un poco ocupado', 'Сейчас я немного занят'), +(1859, 'trade_disabled', 'Trading is disabled', 0, 0, '거래가 비활성화되어 있습니다', 'L''échange est désactivé', 'Handel ist deaktiviert', '交易已禁用', '交易已停用', 'El comercio está deshabilitado', 'El comercio está deshabilitado', 'Торговля отключена'), +(1860, 'trade_thank_you_player', 'Thank you %player', 0, 0, '고마워요 %player', 'Merci %player', 'Danke %player', '谢谢你 %player', '謝謝你 %player', 'Gracias %player', 'Gracias %player', 'Спасибо, %player'), +(1861, 'trade_selling_disabled', 'Selling is disabled.', 0, 0, '판매가 비활성화되어 있습니다.', 'La vente est désactivée.', 'Verkaufen ist deaktiviert.', '出售已禁用。', '出售已停用。', 'La venta está deshabilitada.', 'La venta está deshabilitada.', 'Продажа отключена.'), +(1862, 'trade_buying_disabled', 'Buying is disabled.', 0, 0, '구매가 비활성화되어 있습니다.', 'L''achat est désactivé.', 'Kaufen ist deaktiviert.', '购买已禁用。', '購買已停用。', 'La compra está deshabilitada.', 'La compra está deshabilitada.', 'Покупка отключена.'), +(1863, 'trade_item_not_for_sale', '%item - This is not for sale', 0, 0, '%item - 이것은 판매용이 아닙니다', '%item - Ceci n''est pas à vendre', '%item - Das steht nicht zum Verkauf', '%item - 这不是出售物品', '%item - 這不是出售物品', '%item - Esto no está en venta', '%item - Esto no está en venta', '%item - это не продаётся'), +(1864, 'trade_item_not_needed', '%item - I don''t need this', 0, 0, '%item - 저는 이것이 필요 없습니다', '%item - Je n''en ai pas besoin', '%item - Das brauche ich nicht', '%item - 我不需要这个', '%item - 我不需要這個', '%item - No necesito esto', '%item - No necesito esto', '%item - мне это не нужно'), +(1865, 'trade_no_items_error', 'There are no items to trade', 0, 0, '거래할 아이템이 없습니다', 'Il n''y a aucun objet à échanger', 'Es gibt keine Gegenstände zum Handeln', '没有可交易的物品', '沒有可交易的物品', 'No hay objetos para comerciar', 'No hay objetos para comerciar', 'Нет предметов для обмена'), +(1866, 'trade_discount_buy_only', 'You can use discount to buy items only', 0, 0, '할인은 아이템 구매에만 사용할 수 있습니다', 'Vous ne pouvez utiliser la réduction que pour acheter des objets', 'Rabatte können nur zum Kauf von Gegenständen verwendet werden', '折扣只能用于购买物品', '折扣只能用於購買物品', 'Solo puedes usar el descuento para comprar objetos', 'Solo puedes usar el descuento para comprar objetos', 'Скидку можно использовать только для покупки предметов'), +(1867, 'trade_success_pleasure', 'A pleasure doing business with you', 0, 0, '당신과 거래해서 즐거웠습니다', 'Un plaisir de faire affaire avec vous', 'Es war mir ein Vergnügen, mit dir Geschäfte zu machen', '很高兴和你做生意', '很高興和你做生意', 'Un placer hacer negocios contigo', 'Un placer hacer negocios contigo', 'Приятно было иметь с вами дело'), +(1868, 'trade_success_fair_trade', 'Fair trade', 0, 0, '공정한 거래입니다', 'Échange équitable', 'Fairer Handel', '公平交易', '公平交易', 'Intercambio justo', 'Intercambio justo', 'Честная сделка'), +(1869, 'trade_success_thanks', 'Thanks', 0, 0, '감사합니다', 'Merci', 'Danke', '谢谢', '謝謝', 'Gracias', 'Gracias', 'Спасибо'), +(1870, 'trade_success_off_with_you', 'Off with you', 0, 0, '이제 가보세요', 'Allez-vous-en maintenant', 'Nun geh deiner Wege', '走吧你', '走吧你', 'Ahora vete', 'Ahora vete', 'Иди уже'), +(1871, 'trade_want_money_for_this', 'I want %money for this', 0, 0, '이것에 대해 %money을(를) 원합니다', 'Je veux %money pour ceci', 'Ich möchte %money dafür', '这个我想要 %money', '這個我想要 %money', 'Quiero %money por esto', 'Quiero %money por esto', 'Я хочу %money за это'), +(1872, 'use_item_none_available', 'No items (or game objects) available', 0, 0, '사용할 수 있는 아이템(또는 게임 오브젝트)이 없습니다', 'Aucun objet (ou objet interactif) disponible', 'Keine Gegenstände (oder Spielobjekte) verfügbar', '没有可用的物品(或游戏物体)', '沒有可用的物品(或遊戲物件)', 'No hay objetos (ni objetos del juego) disponibles', 'No hay objetos (ni objetos del juego) disponibles', 'Нет доступных предметов (или игровых объектов)'), +(1873, 'use_gameobject', 'Using %gameobject', 0, 0, '%gameobject 사용 중', 'Utilisation de %gameobject', 'Benutze %gameobject', '正在使用 %gameobject', '正在使用 %gameobject', 'Usando %gameobject', 'Usando %gameobject', 'Использую %gameobject'), +(1874, 'socket_does_not_fit', 'Socket does not fit', 0, 0, '소켓이 맞지 않습니다', 'La châsse ne correspond pas', 'Sockel passt nicht', '插槽不匹配', '插槽不匹配', 'La ranura no encaja', 'La ranura no encaja', 'Гнездо не подходит'), +(1875, 'use_item_on_target', 'Using %item on %target', 0, 0, '%target에게 %item을(를) 사용하는 중', 'Utilisation de %item sur %target', 'Benutze %item auf %target', '正在对 %target 使用 %item', '正在對 %target 使用 %item', 'Usando %item en %target', 'Usando %item en %target', 'Использую %item на %target'), +(1876, 'use_item', 'Using %item', 0, 0, '%item 사용 중', 'Utilisation de %item', 'Benutze %item', '正在使用 %item', '正在使用 %item', 'Usando %item', 'Usando %item', 'Использую %item'), +(1877, 'socketing_item_with_gem', 'Socketing %item with %gem', 0, 0, '%item에 %gem 보석을 장착하는 중', 'Sertissage de %item avec %gem', 'Sockele %item mit %gem', '正在将 %gem 镶嵌到 %item 上', '正在將 %gem 鑲嵌到 %item 上', 'Engarzando %item con %gem', 'Engarzando %item con %gem', 'Вставляю %gem в %item'), +(1878, 'meeting_stone_in_combat', 'I am in combat', 0, 0, '전투 중입니다', 'Je suis en combat', 'Ich bin im Kampf', '我在战斗中', '我在戰鬥中', 'Estoy en combate', 'Estoy en combate', 'Я в бою'), +(1879, 'meeting_stone_welcome', 'Welcome!', 0, 0, '환영합니다!', 'Bienvenue !', 'Willkommen!', '欢迎!', '歡迎!', 'Bienvenido!', 'Bienvenido!', 'Добро пожаловать!'), +(1880, 'meeting_stone_none_nearby', 'There is no meeting stone nearby', 0, 0, '근처에 집결의 돌이 없습니다', 'Il n''y a pas de pierre de rencontre à proximité', 'Es gibt keinen Beschwörungsstein in der Nähe', '附近没有集合石', '附近沒有集合石', 'No hay ninguna piedra de encuentro cerca', 'No hay ninguna piedra de encuentro cerca', 'Поблизости нет камня встреч'), +(1881, 'meeting_stone_none_near_you', 'There is no meeting stone near you', 0, 0, '당신 근처에 집결의 돌이 없습니다', 'Il n''y a pas de pierre de rencontre près de vous', 'Es gibt keinen Beschwörungsstein in deiner Nähe', '你附近没有集合石', '你附近沒有集合石', 'No hay ninguna piedra de encuentro cerca de ti', 'No hay ninguna piedra de encuentro cerca de ti', 'Рядом с вами нет камня встреч'), +(1882, 'meeting_stone_no_hearthstone_self', 'I have no hearthstone', 0, 0, '귀환석이 없습니다', 'Je n''ai pas de pierre de foyer', 'Ich habe keinen Ruhestein', '我没有炉石', '我沒有爐石', 'No tengo piedra de hogar', 'No tengo piedra de hogar', 'У меня нет камня возвращения'), +(1883, 'meeting_stone_no_hearthstone_you', 'You have no hearthstone', 0, 0, '당신에게 귀환석이 없습니다', 'Vous n''avez pas de pierre de foyer', 'Du hast keinen Ruhestein', '你没有炉石', '你沒有爐石', 'No tienes piedra de hogar', 'No tienes piedra de hogar', 'У вас нет камня возвращения'), +(1884, 'meeting_stone_hearthstone_not_ready_self', 'My hearthstone is not ready', 0, 0, '제 귀환석은 아직 준비되지 않았습니다', 'Ma pierre de foyer n''est pas prête', 'Mein Ruhestein ist nicht bereit', '我的炉石还没准备好', '我的爐石還沒準備好', 'Mi piedra de hogar no está lista', 'Mi piedra de hogar no está lista', 'Мой камень возвращения ещё не готов'), +(1885, 'meeting_stone_hearthstone_not_ready_you', 'Your hearthstone is not ready', 0, 0, '당신의 귀환석은 아직 준비되지 않았습니다', 'Votre pierre de foyer n''est pas prête', 'Dein Ruhestein ist nicht bereit', '你的炉石还没准备好', '你的爐石還沒準備好', 'Tu piedra de hogar no está lista', 'Tu piedra de hogar no está lista', 'Ваш камень возвращения ещё не готов'), +(1886, 'meeting_stone_no_innkeepers_nearby', 'There are no innkeepers nearby', 0, 0, '근처에 여관주인이 없습니다', 'Il n''y a pas d''aubergistes à proximité', 'Es gibt keine Gastwirte in der Nähe', '附近没有旅店老板', '附近沒有旅店老闆', 'No hay posaderos cerca', 'No hay posaderos cerca', 'Поблизости нет трактирщиков'), +(1887, 'meeting_stone_no_innkeepers_near_you', 'There are no innkeepers near you', 0, 0, '당신 근처에 여관주인이 없습니다', 'Il n''y a pas d''aubergistes près de vous', 'Es gibt keine Gastwirte in deiner Nähe', '你附近没有旅店老板', '你附近沒有旅店老闆', 'No hay posaderos cerca de ti', 'No hay posaderos cerca de ti', 'Рядом с вами нет трактирщиков'), +(1888, 'meeting_stone_cannot_summon_vehicle', 'You cannot summon me while I''m on a vehicle', 0, 0, '제가 탈것/차량에 타고 있는 동안에는 소환할 수 없습니다', 'Vous ne pouvez pas m''invoquer tant que je suis sur un véhicule', 'Du kannst mich nicht beschwören, solange ich auf einem Fahrzeug bin', '当我在载具上时,你不能召唤我', '當我在載具上時,你不能召喚我', 'No puedes invocarme mientras esté en un vehículo', 'No puedes invocarme mientras esté en un vehículo', 'Вы не можете призвать меня, пока я на транспорте'), +(1889, 'meeting_stone_cannot_summon_master_in_combat', 'You cannot summon me while you''re in combat', 0, 0, '당신이 전투 중일 때는 저를 소환할 수 없습니다', 'Vous ne pouvez pas m''invoquer pendant que vous êtes en combat', 'Du kannst mich nicht beschwören, solange du im Kampf bist', '当你在战斗中时,不能召唤我', '當你在戰鬥中時,不能召喚我', 'No puedes invocarme mientras estés en combate', 'No puedes invocarme mientras estés en combate', 'Вы не можете призвать меня, пока вы в бою'), +(1890, 'meeting_stone_cannot_summon_master_dead', 'You cannot summon me while you''re dead', 0, 0, '당신이 죽어 있는 동안에는 저를 소환할 수 없습니다', 'Vous ne pouvez pas m''invoquer pendant que vous êtes mort', 'Du kannst mich nicht beschwören, solange du tot bist', '当你死亡时,不能召唤我', '當你死亡時,不能召喚我', 'No puedes invocarme mientras estés muerto', 'No puedes invocarme mientras estés muerto', 'Вы не можете призвать меня, пока вы мертвы'), +(1891, 'meeting_stone_cannot_summon_bot_dead', 'You cannot summon me while I''m dead, you need to release my spirit first', 0, 0, '제가 죽어 있는 동안에는 저를 소환할 수 없습니다. 먼저 제 영혼을 해방해야 합니다', 'Vous ne pouvez pas m''invoquer tant que je suis mort, vous devez d''abord libérer mon esprit', 'Du kannst mich nicht beschwören, solange ich tot bin; du musst zuerst meinen Geist freisetzen', '当我死亡时,你不能召唤我,你需要先释放我的灵魂', '當我死亡時,你不能召喚我,你需要先釋放我的靈魂', 'No puedes invocarme mientras esté muerto, primero debes liberar mi espíritu', 'No puedes invocarme mientras esté muerto, primero debes liberar mi espíritu', 'Вы не можете призвать меня, пока я мёртв, сначала нужно освободить мой дух'), +(1892, 'meeting_stone_revived', 'I live, again!', 0, 0, '다시 살아났습니다!', 'Je vis à nouveau !', 'Ich lebe wieder!', '我又活了!', '我又活了!', 'Vivo de nuevo!', 'Vivo de nuevo!', 'Я снова жив!'), +(1893, 'meeting_stone_not_enough_space', 'Not enough place to summon', 0, 0, '소환할 공간이 부족합니다', 'Pas assez de place pour invoquer', 'Nicht genug Platz zum Beschwören', '没有足够的空间进行召唤', '沒有足夠的空間進行召喚', 'No hay suficiente espacio para invocar', 'No hay suficiente espacio para invocar', 'Недостаточно места для призыва'), +(1894, 'new_rpg_quest_accepted', 'Quest accepted %quest', 0, 0, '퀘스트 수락 %quest', 'Quête acceptée %quest', 'Quest angenommen %quest', '任务已接受 %quest', '任務已接受 %quest', 'Misión aceptada %quest', 'Misión aceptada %quest', 'Задание принято %quest'), +(1895, 'new_rpg_quest_rewarded', 'Quest rewarded %quest', 0, 0, '퀘스트 보상 받음 %quest', 'Quête récompensée %quest', 'Quest abgeschlossen %quest', '任务已奖励 %quest', '任務已獎勵 %quest', 'Misión completada %quest', 'Misión completada %quest', 'Награда за задание получена %quest'), +(1896, 'new_rpg_quest_dropped', 'Quest dropped %quest', 0, 0, '퀘스트 포기 %quest', 'Quête abandonnée %quest', 'Quest abgebrochen %quest', '任务已放弃 %quest', '任務已放棄 %quest', 'Misión abandonada %quest', 'Misión abandonada %quest', 'Задание отменено %quest'), +(1897, 'rpg_item_better_for_player', 'You can use this %item better than me, %player.', 0, 0, '%player님, 이 %item은(는) 저보다 당신에게 더 잘 맞습니다.', 'Vous pouvez mieux utiliser cet objet %item que moi, %player.', 'Du kannst diesen %item besser gebrauchen als ich, %player.', '%player,这个 %item 比我更适合你使用。', '%player,這個 %item 比我更適合你使用。', 'Tú puedes usar este %item mejor que yo, %player.', 'Tú puedes usar este %item mejor que yo, %player.', '%player, ты можешь использовать %item лучше, чем я.'), +(1898, 'rpg_start_trade_with_player', 'Start trade with %player', 0, 0, '%player와(과) 거래를 시작합니다', 'Début de l''échange avec %player', 'Beginne Handel mit %player', '开始与 %player 交易', '開始與 %player 交易', 'Iniciando intercambio con %player', 'Iniciando intercambio con %player', 'Начинаю обмен с %player'); + +INSERT INTO ai_playerbot_texts_chance (name, probability) VALUES +('quest_accept_debug', 100), +('quest_already_have_error', 100), +('quest_cant_take_error', 100), +('arena_team_already_in_team', 100), +('arena_team_thanks_for_invite', 100), +('area_trigger_follow_too_far_error', 100), +('area_trigger_wait_for_me', 100), +('attack_no_target_error', 100), +('attack_target_not_in_world_error', 100), +('attack_in_flight_error', 100), +('attack_pvp_prohibited_error', 100), +('attack_target_friendly_error', 100), +('attack_target_dead_error', 100), +('attack_target_not_in_sight_error', 100), +('attack_already_attacking_error', 100), +('attack_invalid_target_error', 100), +('bank_no_banker_nearby_error', 100), +('move_from_group', 100), +('running_away', 100), +('clean_quest_log_started', 100), +('quest_trivial_will_remove', 100), +('quest_has_been_removed', 100), +('quest_not_trivial_kept', 100), +('quest_removed_debug', 100), +('quest_removed_with_name', 100), +('guild_accept_inviter_not_in_guild', 100), +('guild_accept_already_in_guild', 100), +('guild_accept_declined', 100), +('outfit_usage_add', 100), +('outfit_usage_remove', 100), +('outfit_usage_equip', 100), +('outfit_set_as', 100), +('outfit_equipping', 100), +('outfit_replace_current', 100), +('outfit_resetting', 100), +('outfit_updating_current', 100), +('outfit_item_removed_from', 100), +('outfit_item_added_to', 100), +('release_spirit_not_dead_wait', 100), +('release_spirit_already_spirit', 100), +('release_spirit_releasing', 100), +('release_spirit_meet_graveyard', 100), +('send_mail_no_mailbox_nearby', 100), +('send_mail_one_item_only', 100), +('send_mail_cannot_send_money', 100), +('send_mail_not_enough_money', 100), +('send_mail_sending_to', 100), +('send_mail_cannot_send_item', 100), +('send_mail_item_not_for_sale', 100), +('send_mail_sent_to', 100), +('craft_reset', 100), +('craft_usage', 100), +('craft_cannot_craft', 100), +('craft_summary', 100), +('set_home_success', 100), +('set_home_no_innkeeper_error', 100), +('quest_shared', 100), +('tame_invalid_id_error', 100), +('tame_usage_error', 100), +('tame_pet_changed', 100), +('tame_pet_changed_initialized', 100), +('tame_exotic_requires_beast_mastery', 100), +('tame_no_pet_by_name', 100), +('tame_no_pet_by_id', 100), +('tame_no_pet_by_family', 100), +('tame_no_pet_to_rename', 100), +('tame_pet_name_length_error', 100), +('tame_pet_name_alpha_error', 100), +('tame_pet_name_forbidden_error', 100), +('tame_pet_renamed', 100), +('tame_pet_rename_refresh_hint', 100), +('tame_only_hunters_level_10', 100), +('tame_creature_template_not_found', 100), +('tame_create_pet_failed', 100), +('tame_pet_abandoned', 100), +('tame_no_hunter_pet_to_abandon', 100), +('taxi_ready_next_flight', 100), +('taxi_cant_fly_with_you', 100), +('taxi_no_flightmaster_nearby', 100), +('trade_busy_now', 100), +('trade_disabled', 100), +('trade_thank_you_player', 100), +('trade_selling_disabled', 100), +('trade_buying_disabled', 100), +('trade_item_not_for_sale', 100), +('trade_item_not_needed', 100), +('trade_no_items_error', 100), +('trade_discount_buy_only', 100), +('trade_success_pleasure', 100), +('trade_success_fair_trade', 100), +('trade_success_thanks', 100), +('trade_success_off_with_you', 100), +('trade_want_money_for_this', 100), +('use_item_none_available', 100), +('use_gameobject', 100), +('socket_does_not_fit', 100), +('use_item_on_target', 100), +('use_item', 100), +('socketing_item_with_gem', 100), +('meeting_stone_in_combat', 100), +('meeting_stone_welcome', 100), +('meeting_stone_none_nearby', 100), +('meeting_stone_none_near_you', 100), +('meeting_stone_no_hearthstone_self', 100), +('meeting_stone_no_hearthstone_you', 100), +('meeting_stone_hearthstone_not_ready_self', 100), +('meeting_stone_hearthstone_not_ready_you', 100), +('meeting_stone_no_innkeepers_nearby', 100), +('meeting_stone_no_innkeepers_near_you', 100), +('meeting_stone_cannot_summon_vehicle', 100), +('meeting_stone_cannot_summon_master_in_combat', 100), +('meeting_stone_cannot_summon_master_dead', 100), +('meeting_stone_cannot_summon_bot_dead', 100), +('meeting_stone_revived', 100), +('meeting_stone_not_enough_space', 100), +('new_rpg_quest_accepted', 100), +('new_rpg_quest_rewarded', 100), +('new_rpg_quest_dropped', 100), +('rpg_item_better_for_player', 100), +('rpg_start_trade_with_player', 100); diff --git a/src/Ai/Base/Actions/AcceptInvitationAction.cpp b/src/Ai/Base/Actions/AcceptInvitationAction.cpp index 1be3c0921a5..deb1159a124 100644 --- a/src/Ai/Base/Actions/AcceptInvitationAction.cpp +++ b/src/Ai/Base/Actions/AcceptInvitationAction.cpp @@ -9,6 +9,7 @@ #include "ObjectAccessor.h" #include "PlayerbotAIConfig.h" #include "PlayerbotSecurity.h" +#include "PlayerbotTextMgr.h" #include "Playerbots.h" #include "WorldPacket.h" @@ -55,7 +56,7 @@ bool AcceptInvitationAction::Execute(Event event) botAI->ChangeStrategy("+follow,-lfg,-bg", BOT_STATE_NON_COMBAT); botAI->Reset(); - botAI->TellMaster("Hello"); + botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault("hello", "Hello", {})); if (sPlayerbotAIConfig.summonWhenGroup && bot->GetDistance(inviter) > sPlayerbotAIConfig.sightDistance) { diff --git a/src/Ai/Base/Actions/AcceptQuestAction.cpp b/src/Ai/Base/Actions/AcceptQuestAction.cpp index 005cfb08a94..e80bdbe1158 100644 --- a/src/Ai/Base/Actions/AcceptQuestAction.cpp +++ b/src/Ai/Base/Actions/AcceptQuestAction.cpp @@ -6,6 +6,7 @@ #include "AcceptQuestAction.h" #include "Event.h" +#include "PlayerbotTextMgr.h" #include "Playerbots.h" bool AcceptAllQuestsAction::ProcessQuest(Quest const* quest, Object* questGiver) @@ -18,7 +19,11 @@ bool AcceptAllQuestsAction::ProcessQuest(Quest const* quest, Object* questGiver) if (botAI->HasStrategy("debug quest", BotState::BOT_STATE_NON_COMBAT) || botAI->HasStrategy("debug rpg", BotState::BOT_STATE_COMBAT)) { LOG_INFO("playerbots", "{} => Quest [{}] accepted", bot->GetName(), quest->GetTitle()); - bot->Say("Quest [" + text_quest + "] accepted", LANG_UNIVERSAL); + std::string text = PlayerbotTextMgr::instance().GetBotTextOrDefault( + "quest_accept_debug", + "Quest [%quest] accepted", + {{"%quest", text_quest}}); + bot->Say(text, LANG_UNIVERSAL); } return true; @@ -113,7 +118,8 @@ bool AcceptQuestShareAction::Execute(Event event) if (bot->HasQuest(quest)) { bot->SetDivider(ObjectGuid::Empty); - botAI->TellError("I have this quest"); + botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "quest_already_have_error", "I have this quest", {})); return false; } @@ -121,7 +127,8 @@ bool AcceptQuestShareAction::Execute(Event event) { // can't take quest bot->SetDivider(ObjectGuid::Empty); - botAI->TellError("I can't take this quest"); + botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "quest_cant_take_error", "I can't take this quest", {})); return false; } @@ -149,7 +156,8 @@ bool AcceptQuestShareAction::Execute(Event event) bot->CastSpell(bot, qInfo->GetSrcSpell(), true); } - botAI->TellMaster("Quest accepted"); + botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "quest_accept", "Quest accepted", {})); return true; } diff --git a/src/Ai/Base/Actions/AreaTriggerAction.cpp b/src/Ai/Base/Actions/AreaTriggerAction.cpp index a3339ea5c89..deb242bf702 100644 --- a/src/Ai/Base/Actions/AreaTriggerAction.cpp +++ b/src/Ai/Base/Actions/AreaTriggerAction.cpp @@ -7,6 +7,7 @@ #include "Event.h" #include "LastMovementValue.h" +#include "PlayerbotTextMgr.h" #include "Playerbots.h" #include "Transport.h" @@ -36,7 +37,8 @@ bool ReachAreaTriggerAction::Execute(Event event) if (bot->GetMapId() != at->map) { - botAI->TellError("I won't follow: too far away"); + botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "area_trigger_follow_too_far_error", "I won't follow: too far away", {})); return true; } @@ -51,7 +53,8 @@ bool ReachAreaTriggerAction::Execute(Event event) float distance = bot->GetDistance(at->x, at->y, at->z); float delay = 1000.0f * distance / bot->GetSpeed(MOVE_RUN) + sPlayerbotAIConfig.reactDelay; - botAI->TellError("Wait for me"); + botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "area_trigger_wait_for_me", "Wait for me", {})); botAI->SetNextCheckDelay(delay); context->GetValue("last area trigger")->Get().lastAreaTrigger = triggerId; @@ -76,6 +79,6 @@ bool AreaTriggerAction::Execute(Event /*event*/) p.rpos(0); bot->GetSession()->HandleAreaTriggerOpcode(p); - botAI->TellMaster("Hello"); + botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault("hello", "Hello", {})); return true; } diff --git a/src/Ai/Base/Actions/ArenaTeamActions.cpp b/src/Ai/Base/Actions/ArenaTeamActions.cpp index 82672cd6d7c..b40cd687df2 100644 --- a/src/Ai/Base/Actions/ArenaTeamActions.cpp +++ b/src/Ai/Base/Actions/ArenaTeamActions.cpp @@ -6,6 +6,7 @@ #include "ArenaTeamActions.h" #include "ArenaTeamMgr.h" +#include "PlayerbotTextMgr.h" #include "Playerbots.h" bool ArenaTeamAcceptAction::Execute(Event event) @@ -31,7 +32,9 @@ bool ArenaTeamAcceptAction::Execute(Event event) if (bot->GetArenaTeamId(at->GetSlot())) { // bot is already in an arena team - bot->Say("Sorry, I am already in such team", LANG_UNIVERSAL); + std::string text = PlayerbotTextMgr::instance().GetBotTextOrDefault( + "arena_team_already_in_team", "Sorry, I am already in such team", {}); + bot->Say(text, LANG_UNIVERSAL); accept = false; } @@ -39,7 +42,9 @@ bool ArenaTeamAcceptAction::Execute(Event event) { WorldPacket data(CMSG_ARENA_TEAM_ACCEPT); bot->GetSession()->HandleArenaTeamAcceptOpcode(data); - bot->Say("Thanks for the invite!", LANG_UNIVERSAL); + std::string text = PlayerbotTextMgr::instance().GetBotTextOrDefault( + "arena_team_thanks_for_invite", "Thanks for the invite!", {}); + bot->Say(text, LANG_UNIVERSAL); LOG_INFO("playerbots", "Bot {} <{}> accepts Arena Team invite", bot->GetGUID().ToString().c_str(), bot->GetName().c_str()); return true; diff --git a/src/Ai/Base/Actions/AttackAction.cpp b/src/Ai/Base/Actions/AttackAction.cpp index a537dd218a5..0a91eab5aa2 100644 --- a/src/Ai/Base/Actions/AttackAction.cpp +++ b/src/Ai/Base/Actions/AttackAction.cpp @@ -10,6 +10,7 @@ #include "LastMovementValue.h" #include "LootObjectStack.h" #include "PlayerbotAI.h" +#include "PlayerbotTextMgr.h" #include "Playerbots.h" #include "ServerFacade.h" #include "SharedDefines.h" @@ -38,7 +39,8 @@ bool AttackMyTargetAction::Execute(Event /*event*/) if (!guid) { if (verbose) - botAI->TellError("You have no target"); + botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "pull_no_target_error", "You have no target", {})); return false; } @@ -56,7 +58,8 @@ bool AttackAction::Attack(Unit* target, bool /*with_pet*/ /*true*/) if (!target) { if (verbose) - botAI->TellError("I have no target"); + botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "attack_no_target_error", "I have no target", {})); return false; } @@ -64,7 +67,10 @@ bool AttackAction::Attack(Unit* target, bool /*with_pet*/ /*true*/) if (!target->IsInWorld()) { if (verbose) - botAI->TellError(std::string(target->GetName()) + " is no longer in the world."); + botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "attack_target_not_in_world_error", + "%target is no longer in the world.", + {{"%target", target->GetName()}})); return false; } @@ -73,7 +79,8 @@ bool AttackAction::Attack(Unit* target, bool /*with_pet*/ /*true*/) bot->HasUnitState(UNIT_STATE_IN_FLIGHT)) { if (verbose) - botAI->TellError("I cannot attack in flight"); + botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "attack_in_flight_error", "I cannot attack in flight", {})); return false; } @@ -85,7 +92,10 @@ bool AttackAction::Attack(Unit* target, bool /*with_pet*/ /*true*/) sPlayerbotAIConfig.IsPvpProhibited(target->GetZoneId(), target->GetAreaId()))) { if (verbose) - botAI->TellError("I cannot attack other players in PvP prohibited areas."); + botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "attack_pvp_prohibited_error", + "I cannot attack other players in PvP prohibited areas.", + {})); return false; } @@ -93,7 +103,10 @@ bool AttackAction::Attack(Unit* target, bool /*with_pet*/ /*true*/) if (bot->IsFriendlyTo(target)) { if (verbose) - botAI->TellError(std::string(target->GetName()) + " is friendly to me."); + botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "attack_target_friendly_error", + "%target is friendly to me.", + {{"%target", target->GetName()}})); return false; } @@ -101,7 +114,10 @@ bool AttackAction::Attack(Unit* target, bool /*with_pet*/ /*true*/) if (target->isDead()) { if (verbose) - botAI->TellError(std::string(target->GetName()) + " is dead."); + botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "attack_target_dead_error", + "%target is dead.", + {{"%target", target->GetName()}})); return false; } @@ -109,7 +125,10 @@ bool AttackAction::Attack(Unit* target, bool /*with_pet*/ /*true*/) if (!bot->IsWithinLOSInMap(target)) { if (verbose) - botAI->TellError(std::string(target->GetName()) + " is not in my sight."); + botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "attack_target_not_in_sight_error", + "%target is not in my sight.", + {{"%target", target->GetName()}})); return false; } @@ -129,7 +148,10 @@ bool AttackAction::Attack(Unit* target, bool /*with_pet*/ /*true*/) if (sameTarget && inCombat && sameAttackMode) { if (verbose) - botAI->TellError("I am already attacking " + std::string(target->GetName()) + "."); + botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "attack_already_attacking_error", + "I am already attacking %target.", + {{"%target", target->GetName()}})); return false; } @@ -137,7 +159,8 @@ bool AttackAction::Attack(Unit* target, bool /*with_pet*/ /*true*/) if (!bot->IsValidAttackTarget(target)) { if (verbose) - botAI->TellError("I cannot attack an invalid target."); + botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "attack_invalid_target_error", "I cannot attack an invalid target.", {})); return false; } diff --git a/src/Ai/Base/Actions/BankAction.cpp b/src/Ai/Base/Actions/BankAction.cpp index 5a4975f77b2..d5bce50ed7d 100644 --- a/src/Ai/Base/Actions/BankAction.cpp +++ b/src/Ai/Base/Actions/BankAction.cpp @@ -7,6 +7,7 @@ #include "Event.h" #include "ItemCountValue.h" +#include "PlayerbotTextMgr.h" #include "Playerbots.h" bool BankAction::Execute(Event event) @@ -23,7 +24,8 @@ bool BankAction::Execute(Event event) return ExecuteBank(text, npc); } - botAI->TellError("Cannot find banker nearby"); + botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "bank_no_banker_nearby_error", "Cannot find banker nearby", {})); return false; } diff --git a/src/Ai/Base/Actions/BattleGroundTactics.cpp b/src/Ai/Base/Actions/BattleGroundTactics.cpp index efb2fce7fea..238a2291e5d 100644 --- a/src/Ai/Base/Actions/BattleGroundTactics.cpp +++ b/src/Ai/Base/Actions/BattleGroundTactics.cpp @@ -1299,7 +1299,7 @@ std::string const BGTactics::HandleConsoleCommandPrivate(WorldSession* session, Player* player = session->GetPlayer(); if (!player) return "Error - session player not found"; - if (player->GetSession()->GetSecurity() < SEC_GAMEMASTER) + if (!player->CanBeGameMaster()) return "Command can only be used by a GM"; Battleground* bg = player->GetBattleground(); if (!bg) diff --git a/src/Ai/Base/Actions/ChangeStrategyAction.cpp b/src/Ai/Base/Actions/ChangeStrategyAction.cpp index e81096b2a5a..9c8c92552d6 100644 --- a/src/Ai/Base/Actions/ChangeStrategyAction.cpp +++ b/src/Ai/Base/Actions/ChangeStrategyAction.cpp @@ -49,7 +49,7 @@ bool ChangeNonCombatStrategyAction::Execute(Event event) uint32 account = bot->GetSession()->GetAccountId(); if (sPlayerbotAIConfig.IsInRandomAccountList(account) && botAI->GetMaster() && - botAI->GetMaster()->GetSession()->GetSecurity() < SEC_GAMEMASTER) + !botAI->GetMaster()->CanBeGameMaster()) { if (text.find("loot") != std::string::npos || text.find("gather") != std::string::npos) { diff --git a/src/Ai/Base/Actions/ChatShortcutActions.cpp b/src/Ai/Base/Actions/ChatShortcutActions.cpp index b715cb2bb54..a12d842eaaa 100644 --- a/src/Ai/Base/Actions/ChatShortcutActions.cpp +++ b/src/Ai/Base/Actions/ChatShortcutActions.cpp @@ -7,6 +7,7 @@ #include "Event.h" #include "Formations.h" +#include "PlayerbotTextMgr.h" #include "Playerbots.h" #include "PositionValue.h" @@ -85,7 +86,8 @@ bool FollowChatShortcutAction::Execute(Event /*event*/) if (moved) { - botAI->TellMaster("Following"); + botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "following", "Following", {})); return true; } } @@ -108,7 +110,8 @@ bool FollowChatShortcutAction::Execute(Event /*event*/) } */ - botAI->TellMaster("Following"); + botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "following", "Following", {})); return true; } @@ -125,7 +128,8 @@ bool StayChatShortcutAction::Execute(Event /*event*/) SetReturnPosition(bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ()); SetStayPosition(bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ()); - botAI->TellMaster("Staying"); + botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "staying", "Staying", {})); return true; } @@ -140,7 +144,8 @@ bool MoveFromGroupChatShortcutAction::Execute(Event /*event*/) botAI->ChangeStrategy("+move from group", BOT_STATE_NON_COMBAT); botAI->ChangeStrategy("+move from group", BOT_STATE_COMBAT); - botAI->TellMaster("Moving away from group"); + botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "move_from_group", "Moving away from group", {})); return true; } @@ -159,11 +164,13 @@ bool FleeChatShortcutAction::Execute(Event /*event*/) if (bot->GetMapId() != master->GetMapId() || bot->GetDistance(master) > sPlayerbotAIConfig.sightDistance) { - botAI->TellError("I will not flee with you - too far away"); + botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "fleeing_far", "I will not flee with you - too far away", {})); return true; } - botAI->TellMaster("Fleeing"); + botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "fleeing", "Fleeing", {})); return true; } @@ -180,7 +187,8 @@ bool GoawayChatShortcutAction::Execute(Event /*event*/) ResetReturnPosition(); ResetStayPosition(); - botAI->TellMaster("Running away"); + botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "running_away", "Running away", {})); return true; } @@ -196,7 +204,8 @@ bool GrindChatShortcutAction::Execute(Event /*event*/) ResetReturnPosition(); ResetStayPosition(); - botAI->TellMaster("Grinding"); + botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "grinding", "Grinding", {})); return true; } @@ -216,7 +225,8 @@ bool TankAttackChatShortcutAction::Execute(Event /*event*/) ResetReturnPosition(); ResetStayPosition(); - botAI->TellMaster("Attacking"); + botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "attacking", "Attacking", {})); return true; } diff --git a/src/Ai/Base/Actions/DropQuestAction.cpp b/src/Ai/Base/Actions/DropQuestAction.cpp index f6712abf3c7..f47eb41af0f 100644 --- a/src/Ai/Base/Actions/DropQuestAction.cpp +++ b/src/Ai/Base/Actions/DropQuestAction.cpp @@ -7,6 +7,7 @@ #include "ChatHelper.h" #include "Event.h" +#include "PlayerbotTextMgr.h" #include "Playerbots.h" bool DropQuestAction::Execute(Event event) @@ -51,10 +52,15 @@ bool DropQuestAction::Execute(Event event) const Quest* pQuest = sObjectMgr->GetQuestTemplate(entry); const std::string text_quest = ChatHelper::FormatQuest(pQuest); LOG_INFO("playerbots", "{} => Quest [ {} ] removed", bot->GetName(), pQuest->GetTitle()); - bot->Say("Quest [ " + text_quest + " ] removed", LANG_UNIVERSAL); + std::string text = PlayerbotTextMgr::instance().GetBotTextOrDefault( + "quest_removed_debug", + "Quest [%quest] removed", + {{"%quest", text_quest}}); + bot->Say(text, LANG_UNIVERSAL); } - botAI->TellMaster("Quest removed"); + botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "quest_remove", "Quest removed", {})); return true; } @@ -69,7 +75,10 @@ bool CleanQuestLogAction::Execute(Event event) // Only output this message if "debug rpg" strategy is enabled if (botAI->HasStrategy("debug rpg", BotState::BOT_STATE_COMBAT)) - botAI->TellMaster("Clean Quest Log command received, removing grey/trivial quests..."); + botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "clean_quest_log_started", + "Clean Quest Log command received, removing grey/trivial quests...", + {})); uint8 botLevel = bot->GetLevel(); // Get bot's level @@ -103,7 +112,10 @@ bool CleanQuestLogAction::Execute(Event event) { // Output only if "debug rpg" strategy is enabled if (botAI->HasStrategy("debug rpg", BotState::BOT_STATE_COMBAT)) - botAI->TellMaster("Quest [ " + quest->GetTitle() + " ] will be removed because it is trivial (grey)."); + botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "quest_trivial_will_remove", + "Quest [%title] will be removed because it is trivial (grey).", + {{"%title", quest->GetTitle()}})); // Remove quest botAI->rpgStatistic.questDropped++; @@ -116,17 +128,27 @@ bool CleanQuestLogAction::Execute(Event event) { const std::string text_quest = ChatHelper::FormatQuest(quest); LOG_INFO("playerbots", "{} => Quest [ {} ] removed", bot->GetName(), quest->GetTitle()); - bot->Say("Quest [ " + text_quest + " ] removed", LANG_UNIVERSAL); + std::string text = PlayerbotTextMgr::instance().GetBotTextOrDefault( + "quest_removed_debug", + "Quest [%quest] removed", + {{"%quest", text_quest}}); + bot->Say(text, LANG_UNIVERSAL); } if (botAI->HasStrategy("debug rpg", BotState::BOT_STATE_COMBAT)) - botAI->TellMaster("Quest [ " + quest->GetTitle() + " ] has been removed."); + botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "quest_has_been_removed", + "Quest [%title] has been removed.", + {{"%title", quest->GetTitle()}})); } else { // Only output if "debug rpg" strategy is enabled if (botAI->HasStrategy("debug rpg", BotState::BOT_STATE_COMBAT)) - botAI->TellMaster("Quest [ " + quest->GetTitle() + " ] is not trivial and will be kept."); + botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "quest_not_trivial_kept", + "Quest [%title] is not trivial and will be kept.", + {{"%title", quest->GetTitle()}})); } } @@ -204,9 +226,16 @@ void CleanQuestLogAction::DropQuestType(uint8& numQuest, uint8 wantNum, bool isG { const std::string text_quest = ChatHelper::FormatQuest(quest); LOG_INFO("playerbots", "{} => Quest [ {} ] removed", bot->GetName(), quest->GetTitle()); - bot->Say("Quest [ " + text_quest + " ] removed", LANG_UNIVERSAL); + std::string text = PlayerbotTextMgr::instance().GetBotTextOrDefault( + "quest_removed_debug", + "Quest [%quest] removed", + {{"%quest", text_quest}}); + bot->Say(text, LANG_UNIVERSAL); } - botAI->TellMaster("Quest removed" + chat->FormatQuest(quest)); + botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "quest_removed_with_name", + "Quest removed %quest", + {{"%quest", chat->FormatQuest(quest)}})); } } diff --git a/src/Ai/Base/Actions/GenericActions.cpp b/src/Ai/Base/Actions/GenericActions.cpp index 35fa51b316d..354cb456eff 100644 --- a/src/Ai/Base/Actions/GenericActions.cpp +++ b/src/Ai/Base/Actions/GenericActions.cpp @@ -8,6 +8,7 @@ #include "Player.h" #include "Pet.h" #include "PlayerbotAIConfig.h" +#include "PlayerbotTextMgr.h" #include "CreatureAI.h" #include "Playerbots.h" #include "CharmInfo.h" @@ -49,7 +50,10 @@ bool MeleeAction::isUseful() if (botAI->IsInVehicle() && !botAI->IsInVehicle(false, false, true)) return false; - return true; + // Do not start autoattack while prowled — let opener spells break stealth intentionally. + // Future rogue stealth implementation should use this instead: + // return !(botAI->HasAura("stealth", bot) || botAI->HasAura("prowl", bot)); + return !botAI->HasAura("prowl", bot); } bool TogglePetSpellAutoCastAction::Execute(Event /*event*/) @@ -178,7 +182,8 @@ bool SetPetStanceAction::Execute(Event /*event*/) // If there are no controlled pets or guardians, notify the player and exit if (targets.empty()) { - botAI->TellError("You have no pet or guardian pet."); + botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "pet_no_pet_error", "You have no pet or guardian pet.", {})); return false; } diff --git a/src/Ai/Base/Actions/GuildAcceptAction.cpp b/src/Ai/Base/Actions/GuildAcceptAction.cpp index cd7635a97b2..b087472929e 100644 --- a/src/Ai/Base/Actions/GuildAcceptAction.cpp +++ b/src/Ai/Base/Actions/GuildAcceptAction.cpp @@ -8,6 +8,7 @@ #include "Event.h" #include "GuildPackets.h" #include "PlayerbotSecurity.h" +#include "PlayerbotTextMgr.h" #include "Playerbots.h" bool GuildAcceptAction::Execute(Event event) @@ -28,17 +29,20 @@ bool GuildAcceptAction::Execute(Event event) uint32 guildId = inviter->GetGuildId(); if (!guildId) { - botAI->TellError("You are not in a guild!"); + botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "guild_accept_inviter_not_in_guild", "You are not in a guild!", {})); accept = false; } else if (bot->GetGuildId()) { - botAI->TellError("Sorry, I am in a guild already"); + botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "guild_accept_already_in_guild", "Sorry, I am in a guild already", {})); accept = false; } else if (!botAI->GetSecurity()->CheckLevelFor(PLAYERBOT_SECURITY_INVITE, false, inviter, true)) { - botAI->TellError("Sorry, I don't want to join your guild :("); + botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "guild_accept_declined", "Sorry, I don't want to join your guild :(", {})); accept = false; } diff --git a/src/Ai/Base/Actions/LeaveGroupAction.cpp b/src/Ai/Base/Actions/LeaveGroupAction.cpp index 337d115444c..6618cbbe13d 100644 --- a/src/Ai/Base/Actions/LeaveGroupAction.cpp +++ b/src/Ai/Base/Actions/LeaveGroupAction.cpp @@ -7,6 +7,7 @@ #include "Event.h" #include "PlayerbotAIConfig.h" +#include "PlayerbotTextMgr.h" #include "Playerbots.h" bool LeaveGroupAction::Execute(Event event) @@ -86,7 +87,9 @@ bool LeaveGroupAction::Leave() Player* master = botAI -> GetMaster(); if (master) - botAI->TellMaster("Goodbye!", PLAYERBOT_SECURITY_TALK); + botAI->TellMaster( + PlayerbotTextMgr::instance().GetBotTextOrDefault("goodbye", "Goodbye!", {}), + PLAYERBOT_SECURITY_TALK); botAI->LeaveOrDisbandGroup(); return true; diff --git a/src/Ai/Base/Actions/OutfitAction.cpp b/src/Ai/Base/Actions/OutfitAction.cpp index cbb41219466..07abc4a074d 100644 --- a/src/Ai/Base/Actions/OutfitAction.cpp +++ b/src/Ai/Base/Actions/OutfitAction.cpp @@ -7,6 +7,7 @@ #include "Event.h" #include "ItemVisitors.h" +#include "PlayerbotTextMgr.h" #include "PlayerbotRepository.h" #include "Playerbots.h" #include "ItemPackets.h" @@ -18,9 +19,12 @@ bool OutfitAction::Execute(Event event) if (param == "?") { List(); - botAI->TellMaster("outfit +[item] to add items"); - botAI->TellMaster("outfit -[item] to remove items"); - botAI->TellMaster("outfit equip/replace to equip items"); + botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "outfit_usage_add", "outfit +[item] to add items", {})); + botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "outfit_usage_remove", "outfit -[item] to remove items", {})); + botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "outfit_usage_equip", "outfit equip/replace to equip items", {})); } else { @@ -32,8 +36,10 @@ bool OutfitAction::Execute(Event event) PlayerbotRepository::instance().Save(botAI); std::ostringstream out; - out << "Setting outfit " << name << " as " << param; - botAI->TellMaster(out); + botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "outfit_set_as", + "Setting outfit %name as %param", + {{"%name", name}, {"%param", param}})); return true; } @@ -49,18 +55,20 @@ bool OutfitAction::Execute(Event event) std::string const command = param.substr(space + 1); if (command == "equip") { - std::ostringstream out; - out << "Equipping outfit " << name; - botAI->TellMaster(out); + botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "outfit_equipping", + "Equipping outfit %name", + {{"%name", name}})); EquipItems(outfit); return true; } else if (command == "replace") { - std::ostringstream out; - out << "Replacing current equip with outfit " << name; - botAI->TellMaster(out); + botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "outfit_replace_current", + "Replacing current equip with outfit %name", + {{"%name", name}})); for (uint8 slot = EQUIPMENT_SLOT_START; slot < EQUIPMENT_SLOT_END; slot++) { @@ -83,9 +91,10 @@ bool OutfitAction::Execute(Event event) } else if (command == "reset") { - std::ostringstream out; - out << "Resetting outfit " << name; - botAI->TellMaster(out); + botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "outfit_resetting", + "Resetting outfit %name", + {{"%name", name}})); Save(name, ItemIds()); PlayerbotRepository::instance().Save(botAI); @@ -93,9 +102,10 @@ bool OutfitAction::Execute(Event event) } else if (command == "update") { - std::ostringstream out; - out << "Updating with current items outfit " << name; - botAI->TellMaster(out); + botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "outfit_updating_current", + "Updating with current items outfit %name", + {{"%name", name}})); Update(name); PlayerbotRepository::instance().Save(botAI); @@ -107,24 +117,25 @@ bool OutfitAction::Execute(Event event) { ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemid); - std::ostringstream out; - out << chat->FormatItem(proto); if (remove) { std::set::iterator j = outfit.find(itemid); if (j != outfit.end()) outfit.erase(j); - out << " removed from "; + botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "outfit_item_removed_from", + "%item removed from %name", + {{"%item", chat->FormatItem(proto)}, {"%name", name}})); } else { outfit.insert(itemid); - out << " added to "; + botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "outfit_item_added_to", + "%item added to %name", + {{"%item", chat->FormatItem(proto)}, {"%name", name}})); } - - out << name; - botAI->TellMaster(out.str()); } Save(name, outfit); diff --git a/src/Ai/Base/Actions/ReleaseSpiritAction.cpp b/src/Ai/Base/Actions/ReleaseSpiritAction.cpp index 19bb870db20..78897c08201 100644 --- a/src/Ai/Base/Actions/ReleaseSpiritAction.cpp +++ b/src/Ai/Base/Actions/ReleaseSpiritAction.cpp @@ -10,6 +10,7 @@ #include "NearestNpcsValue.h" #include "ObjectDefines.h" #include "ObjectGuid.h" +#include "PlayerbotTextMgr.h" #include "Playerbots.h" #include "ServerFacade.h" #include "Corpse.h" @@ -22,7 +23,8 @@ bool ReleaseSpiritAction::Execute(Event event) { if (!bot->InBattleground()) { - botAI->TellMasterNoFacing("I am not dead, will wait here"); + botAI->TellMasterNoFacing(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "release_spirit_not_dead_wait", "I am not dead, will wait here", {})); // -follow in bg is overwriten each tick with +follow // +stay in bg causes stuttering effect as bot is cycled between +stay and +follow each tick botAI->ChangeStrategy("-follow,+stay", BOT_STATE_NON_COMBAT); @@ -33,14 +35,15 @@ bool ReleaseSpiritAction::Execute(Event event) if (bot->GetCorpse() && bot->HasPlayerFlag(PLAYER_FLAGS_GHOST)) { - botAI->TellMasterNoFacing("I am already a spirit"); + botAI->TellMasterNoFacing(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "release_spirit_already_spirit", "I am already a spirit", {})); return false; } const WorldPacket& packet = event.getPacket(); const std::string message = !packet.empty() && packet.GetOpcode() == CMSG_REPOP_REQUEST - ? "Releasing..." - : "Meet me at the graveyard"; + ? PlayerbotTextMgr::instance().GetBotTextOrDefault("release_spirit_releasing", "Releasing...", {}) + : PlayerbotTextMgr::instance().GetBotTextOrDefault("release_spirit_meet_graveyard", "Meet me at the graveyard", {}); botAI->TellMasterNoFacing(message); IncrementDeathCount(); diff --git a/src/Ai/Base/Actions/ReviveFromCorpseAction.cpp b/src/Ai/Base/Actions/ReviveFromCorpseAction.cpp index 2599fb1cff7..3efca28d1e7 100644 --- a/src/Ai/Base/Actions/ReviveFromCorpseAction.cpp +++ b/src/Ai/Base/Actions/ReviveFromCorpseAction.cpp @@ -9,6 +9,7 @@ #include "FleeManager.h" #include "GameGraveyard.h" #include "MapMgr.h" +#include "PlayerbotTextMgr.h" #include "Playerbots.h" #include "RandomPlayerbotMgr.h" #include "ServerFacade.h" @@ -322,7 +323,7 @@ bool SpiritHealerAction::Execute(Event /*event*/) bot->SpawnCorpseBones(); context->GetValue("current target")->Set(nullptr); bot->SetTarget(); - botAI->TellMaster("Hello"); + botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault("hello", "Hello", {})); if (dCount > 20) context->GetValue("death count")->Set(0); diff --git a/src/Ai/Base/Actions/SecurityCheckAction.cpp b/src/Ai/Base/Actions/SecurityCheckAction.cpp index 3d1b4c6015c..d4daa367acd 100644 --- a/src/Ai/Base/Actions/SecurityCheckAction.cpp +++ b/src/Ai/Base/Actions/SecurityCheckAction.cpp @@ -12,7 +12,7 @@ bool SecurityCheckAction::isUseful() { return RandomPlayerbotMgr::instance().IsRandomBot(bot) && botAI->GetMaster() - && botAI->GetMaster()->GetSession()->GetSecurity() < SEC_GAMEMASTER + && !botAI->GetMaster()->CanBeGameMaster() && !GET_PLAYERBOT_AI(botAI->GetMaster()); } diff --git a/src/Ai/Base/Actions/SendMailAction.cpp b/src/Ai/Base/Actions/SendMailAction.cpp index 9be9d553abf..549e6b71610 100644 --- a/src/Ai/Base/Actions/SendMailAction.cpp +++ b/src/Ai/Base/Actions/SendMailAction.cpp @@ -9,6 +9,7 @@ #include "Event.h" #include "ItemVisitors.h" #include "Mail.h" +#include "PlayerbotTextMgr.h" #include "Playerbots.h" bool SendMailAction::Execute(Event event) @@ -53,14 +54,18 @@ bool SendMailAction::Execute(Event event) if (!mailboxFound && !randomBot) { - bot->Whisper("There is no mailbox nearby", LANG_UNIVERSAL, tellTo); + bot->Whisper(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "send_mail_no_mailbox_nearby", "There is no mailbox nearby", {}), + LANG_UNIVERSAL, tellTo); return false; } ItemIds ids = chat->parseItems(text); if (ids.size() > 1) { - bot->Whisper("You can not request more than one item", LANG_UNIVERSAL, tellTo); + bot->Whisper(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "send_mail_one_item_only", "You can not request more than one item", {}), + LANG_UNIVERSAL, tellTo); return false; } @@ -72,13 +77,16 @@ bool SendMailAction::Execute(Event event) if (randomBot) { - bot->Whisper("I cannot send money", LANG_UNIVERSAL, tellTo); + bot->Whisper(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "send_mail_cannot_send_money", "I cannot send money", {}), + LANG_UNIVERSAL, tellTo); return false; } if (bot->GetMoney() < money) { - botAI->TellError("I don't have enough money"); + botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "send_mail_not_enough_money", "I don't have enough money", {})); return false; } @@ -100,8 +108,10 @@ bool SendMailAction::Execute(Event event) CharacterDatabase.CommitTransaction(trans); std::ostringstream out; - out << "Sending mail to " << receiver->GetName(); - botAI->TellMaster(out.str()); + botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "send_mail_sending_to", + "Sending mail to %receiver", + {{"%receiver", receiver->GetName()}})); return true; } @@ -125,7 +135,10 @@ bool SendMailAction::Execute(Event event) if (item->IsSoulBound() || item->IsConjuredConsumable()) { std::ostringstream out; - out << "Cannot send " << ChatHelper::FormatItem(item->GetTemplate()); + out << PlayerbotTextMgr::instance().GetBotTextOrDefault( + "send_mail_cannot_send_item", + "Cannot send %item", + {{"%item", ChatHelper::FormatItem(item->GetTemplate())}}); bot->Whisper(out.str(), LANG_UNIVERSAL, tellTo); continue; } @@ -140,7 +153,10 @@ bool SendMailAction::Execute(Event event) if (!price) { std::ostringstream out; - out << ChatHelper::FormatItem(item->GetTemplate()) << ": it is not for sale"; + out << PlayerbotTextMgr::instance().GetBotTextOrDefault( + "send_mail_item_not_for_sale", + "%item: it is not for sale", + {{"%item", ChatHelper::FormatItem(item->GetTemplate())}}); bot->Whisper(out.str(), LANG_UNIVERSAL, tellTo); return false; } @@ -160,7 +176,10 @@ bool SendMailAction::Execute(Event event) CharacterDatabase.CommitTransaction(trans); std::ostringstream out; - out << "Sent mail to " << receiver->GetName(); + out << PlayerbotTextMgr::instance().GetBotTextOrDefault( + "send_mail_sent_to", + "Sent mail to %receiver", + {{"%receiver", receiver->GetName()}}); bot->Whisper(out.str(), LANG_UNIVERSAL, tellTo); return true; } diff --git a/src/Ai/Base/Actions/SetCraftAction.cpp b/src/Ai/Base/Actions/SetCraftAction.cpp index 40134821f1b..ca475a52efe 100644 --- a/src/Ai/Base/Actions/SetCraftAction.cpp +++ b/src/Ai/Base/Actions/SetCraftAction.cpp @@ -8,6 +8,7 @@ #include "ChatHelper.h" #include "CraftValue.h" #include "Event.h" +#include "PlayerbotTextMgr.h" #include "Playerbots.h" std::map SetCraftAction::skillSpells; @@ -24,7 +25,8 @@ bool SetCraftAction::Execute(Event event) if (link == "reset") { data.Reset(); - botAI->TellMaster("I will not craft anything"); + botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "craft_reset", "I will not craft anything", {})); return true; } @@ -37,7 +39,8 @@ bool SetCraftAction::Execute(Event event) ItemIds itemIds = chat->parseItems(link); if (itemIds.empty()) { - botAI->TellMaster("Usage: 'craft [itemId]' or 'craft reset'"); + botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "craft_usage", "Usage: 'craft [itemId]' or 'craft reset'", {})); return false; } @@ -94,7 +97,8 @@ bool SetCraftAction::Execute(Event event) if (data.required.empty()) { - botAI->TellMaster("I cannot craft this"); + botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "craft_cannot_craft", "I cannot craft this", {})); return false; } @@ -109,7 +113,8 @@ void SetCraftAction::TellCraft() CraftData& data = AI_VALUE(CraftData&, "craft"); if (data.IsEmpty()) { - botAI->TellMaster("I will not craft anything"); + botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "craft_reset", "I will not craft anything", {})); return; } @@ -117,8 +122,7 @@ void SetCraftAction::TellCraft() if (!proto) return; - std::ostringstream out; - out << "I will craft " << chat->FormatItem(proto) << " using reagents: "; + std::ostringstream reagentsOut; bool first = true; for (std::map::iterator i = data.required.begin(); i != data.required.end(); ++i) @@ -130,20 +134,23 @@ void SetCraftAction::TellCraft() { if (first) first = false; - else - out << ", "; + reagentsOut << ", "; - out << chat->FormatItem(reagent, required); + reagentsOut << chat->FormatItem(reagent, required); uint32 given = data.obtained[item]; if (given) - out << "|cffffff00(x" << given << " given)|r "; + reagentsOut << "|cffffff00(x" << given << " given)|r "; } } - out << " (craft fee: " << chat->formatMoney(GetCraftFee(data)) << ")"; - botAI->TellMaster(out.str()); + botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "craft_summary", + "I will craft %item using reagents: %reagents (craft fee: %money)", + {{"%item", chat->FormatItem(proto)}, + {"%reagents", reagentsOut.str()}, + {"%money", chat->formatMoney(GetCraftFee(data))}})); } uint32 SetCraftAction::GetCraftFee(CraftData& data) diff --git a/src/Ai/Base/Actions/SetHomeAction.cpp b/src/Ai/Base/Actions/SetHomeAction.cpp index ed6bcbfaa9c..85828c26e78 100644 --- a/src/Ai/Base/Actions/SetHomeAction.cpp +++ b/src/Ai/Base/Actions/SetHomeAction.cpp @@ -6,6 +6,7 @@ #include "SetHomeAction.h" #include "Event.h" +#include "PlayerbotTextMgr.h" #include "Playerbots.h" bool SetHomeAction::Execute(Event /*event*/) @@ -28,7 +29,8 @@ bool SetHomeAction::Execute(Event /*event*/) { Creature* creature = botAI->GetCreature(selection); bot->GetSession()->SendBindPoint(creature); - botAI->TellMaster("This inn is my new home"); + botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "set_home_success", "This inn is my new home", {})); return true; } @@ -40,10 +42,12 @@ bool SetHomeAction::Execute(Event /*event*/) continue; bot->GetSession()->SendBindPoint(unit); - botAI->TellMaster("This inn is my new home"); + botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "set_home_success", "This inn is my new home", {})); return true; } - botAI->TellError("Can't find any innkeeper around"); + botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "set_home_no_innkeeper_error", "Can't find any innkeeper around", {})); return false; } diff --git a/src/Ai/Base/Actions/ShareQuestAction.cpp b/src/Ai/Base/Actions/ShareQuestAction.cpp index 50ce2ac3224..492b3ad6e6f 100644 --- a/src/Ai/Base/Actions/ShareQuestAction.cpp +++ b/src/Ai/Base/Actions/ShareQuestAction.cpp @@ -6,6 +6,7 @@ #include "ShareQuestAction.h" #include "Event.h" +#include "PlayerbotTextMgr.h" #include "Playerbots.h" bool ShareQuestAction::Execute(Event event) @@ -32,7 +33,8 @@ bool ShareQuestAction::Execute(Event event) WorldPacket p; p << entry; bot->GetSession()->HandlePushQuestToParty(p); - botAI->TellMaster("Quest shared"); + botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "quest_shared", "Quest shared", {})); return true; } } @@ -98,7 +100,8 @@ bool AutoShareQuestAction::Execute(Event /*event*/) WorldPacket p; p << logQuest; bot->GetSession()->HandlePushQuestToParty(p); - botAI->TellMaster("Quest shared"); + botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "quest_shared", "Quest shared", {})); shared = true; } diff --git a/src/Ai/Base/Actions/TameAction.cpp b/src/Ai/Base/Actions/TameAction.cpp index b19626e5949..dfeb61a47f5 100644 --- a/src/Ai/Base/Actions/TameAction.cpp +++ b/src/Ai/Base/Actions/TameAction.cpp @@ -15,6 +15,7 @@ #include "Player.h" #include "PlayerbotAI.h" #include "PlayerbotFactory.h" +#include "PlayerbotTextMgr.h" #include "SpellMgr.h" #include "WorldSession.h" @@ -123,7 +124,8 @@ bool TameAction::Execute(Event event) } catch (...) { - botAI->TellError("Invalid tame id."); + botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "tame_invalid_id_error", "Invalid tame id.", {})); } } else if (mode == "family" && !value.empty()) @@ -137,8 +139,10 @@ bool TameAction::Execute(Event event) else { // Unrecognized command or missing argument; show usage - botAI->TellError( - "Usage: tame name | tame id | tame family | tame rename | tame abandon"); + botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "tame_usage_error", + "Usage: tame name | tame id | tame family | tame rename | tame abandon", + {})); return false; } @@ -157,12 +161,15 @@ bool TameAction::Execute(Event event) if (!lastPetName.empty() && lastPetId != 0) { std::ostringstream oss; - oss << "Pet changed to " << lastPetName << ", ID: " << lastPetId << "."; - botAI->TellMaster(oss.str()); + botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "tame_pet_changed", + "Pet changed to %name, ID: %id.", + {{"%name", lastPetName}, {"%id", std::to_string(lastPetId)}})); } else { - botAI->TellMaster("Pet changed and initialized!"); + botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "tame_pet_changed_initialized", "Pet changed and initialized!", {})); } } @@ -197,7 +204,10 @@ bool TameAction::SetPetByName(const std::string& name) // If the creature is exotic and the bot doesn't have Beast Mastery, show error and fail if (IsExoticPet(&creature) && !HasBeastMastery(bot)) { - botAI->TellError("I cannot use exotic pets unless I have the Beast Mastery talent."); + botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "tame_exotic_requires_beast_mastery", + "I cannot use exotic pets unless I have the Beast Mastery talent.", + {})); return false; } @@ -214,7 +224,8 @@ bool TameAction::SetPetByName(const std::string& name) } // If no suitable pet found, show an error and return failure - botAI->TellError("No tameable pet found with name: " + name); + botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "tame_no_pet_by_name", "No tameable pet found with name: %name", {{"%name", name}})); return false; } @@ -231,21 +242,26 @@ bool TameAction::SetPetById(uint32 id) if (!creature->IsTameable(true)) { // If not tameable at all, show an error and fail - botAI->TellError("No tameable pet found with id: " + std::to_string(id)); + botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "tame_no_pet_by_id", "No tameable pet found with id: %id", {{"%id", std::to_string(id)}})); return false; } // If it's an exotic pet, make sure the bot has the Beast Mastery talent if (IsExoticPet(creature) && !HasBeastMastery(bot)) { - botAI->TellError("I cannot use exotic pets unless I have the Beast Mastery talent."); + botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "tame_exotic_requires_beast_mastery", + "I cannot use exotic pets unless I have the Beast Mastery talent.", + {})); return false; } // Check if the bot is actually allowed to tame this pet (honoring exotic pet rules) if (!creature->IsTameable(bot->CanTameExoticPets())) { - botAI->TellError("No tameable pet found with id: " + std::to_string(id)); + botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "tame_no_pet_by_id", "No tameable pet found with id: %id", {{"%id", std::to_string(id)}})); return false; } @@ -257,7 +273,8 @@ bool TameAction::SetPetById(uint32 id) } // If no valid creature was found by id, show an error - botAI->TellError("No tameable pet found with id: " + std::to_string(id)); + botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "tame_no_pet_by_id", "No tameable pet found with id: %id", {{"%id", std::to_string(id)}})); return false; } @@ -315,9 +332,13 @@ bool TameAction::SetPetByFamily(const std::string& family) if (candidates.empty()) { if (foundExotic && !HasBeastMastery(bot)) - botAI->TellError("I cannot use exotic pets unless I have the Beast Mastery talent."); + botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "tame_exotic_requires_beast_mastery", + "I cannot use exotic pets unless I have the Beast Mastery talent.", + {})); else - botAI->TellError("No tameable pet found with family: " + family); + botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "tame_no_pet_by_family", "No tameable pet found with family: %family", {{"%family", family}})); return false; } @@ -342,14 +363,18 @@ bool TameAction::RenamePet(const std::string& newName) // Check if the bot currently has a pet if (!pet) { - botAI->TellError("You have no pet to rename."); + botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "tame_no_pet_to_rename", "You have no pet to rename.", {})); return false; } // Validate the new name: must not be empty and max 12 characters if (newName.empty() || newName.length() > 12) { - botAI->TellError("Pet name must be between 1 and 12 alphabetic characters."); + botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "tame_pet_name_length_error", + "Pet name must be between 1 and 12 alphabetic characters.", + {})); return false; } @@ -358,7 +383,10 @@ bool TameAction::RenamePet(const std::string& newName) { if (!std::isalpha(static_cast(c))) { - botAI->TellError("Pet name must only contain alphabetic characters (A-Z, a-z)."); + botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "tame_pet_name_alpha_error", + "Pet name must only contain alphabetic characters (A-Z, a-z).", + {})); return false; } } @@ -372,7 +400,10 @@ bool TameAction::RenamePet(const std::string& newName) // Check if the new name is reserved or forbidden if (sObjectMgr->IsReservedName(normalized)) { - botAI->TellError("That pet name is forbidden. Please choose another name."); + botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "tame_pet_name_forbidden_error", + "That pet name is forbidden. Please choose another name.", + {})); return false; } @@ -382,8 +413,12 @@ bool TameAction::RenamePet(const std::string& newName) bot->GetSession()->SendPetNameQuery(pet->GetGUID(), pet->GetEntry()); // Notify the master about the rename and give a tip to update the client name display - botAI->TellMaster("Your pet has been renamed to " + normalized + "!"); - botAI->TellMaster("If you do not see the new name, please dismiss and recall your pet."); + botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "tame_pet_renamed", "Your pet has been renamed to %name!", {{"%name", normalized}})); + botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "tame_pet_rename_refresh_hint", + "If you do not see the new name, please dismiss and recall your pet.", + {})); // Remove the current pet and (re-)cast Call Pet spell if the bot is a hunter bot->RemovePet(nullptr, PET_SAVE_AS_CURRENT, true); @@ -401,7 +436,8 @@ bool TameAction::CreateAndSetPet(uint32 creatureEntry) // Ensure the player is a hunter and at least level 10 (required for pets) if (bot->getClass() != CLASS_HUNTER || bot->GetLevel() < 10) { - botAI->TellError("Only level 10+ hunters can have pets."); + botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "tame_only_hunters_level_10", "Only level 10+ hunters can have pets.", {})); return false; } @@ -409,7 +445,8 @@ bool TameAction::CreateAndSetPet(uint32 creatureEntry) CreatureTemplate const* creature = sObjectMgr->GetCreatureTemplate(creatureEntry); if (!creature) { - botAI->TellError("Creature template not found."); + botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "tame_creature_template_not_found", "Creature template not found.", {})); return false; } @@ -430,7 +467,8 @@ bool TameAction::CreateAndSetPet(uint32 creatureEntry) Pet* pet = bot->CreateTamedPetFrom(creatureEntry, 0); if (!pet) { - botAI->TellError("Failed to create pet."); + botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "tame_create_pet_failed", "Failed to create pet.", {})); return false; } @@ -485,13 +523,15 @@ bool TameAction::AbandonPet() // Remove the pet from the bot and mark it as deleted in the database bot->RemovePet(pet, PET_SAVE_AS_DELETED); // Inform the bot's master/player that the pet was abandoned - botAI->TellMaster("Your pet has been abandoned."); + botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "tame_pet_abandoned", "Your pet has been abandoned.", {})); return true; } else { // If there is no hunter pet, show an error message - botAI->TellError("You have no hunter pet to abandon."); + botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "tame_no_hunter_pet_to_abandon", "You have no hunter pet to abandon.", {})); return false; } } diff --git a/src/Ai/Base/Actions/TaxiAction.cpp b/src/Ai/Base/Actions/TaxiAction.cpp index 0bbbb8a1090..d79a992e22b 100644 --- a/src/Ai/Base/Actions/TaxiAction.cpp +++ b/src/Ai/Base/Actions/TaxiAction.cpp @@ -7,6 +7,7 @@ #include "Event.h" #include "LastMovementValue.h" +#include "PlayerbotTextMgr.h" #include "Playerbots.h" #include "PlayerbotAIConfig.h" #include "Config.h" @@ -24,7 +25,8 @@ bool TaxiAction::Execute(Event event) { movement.taxiNodes.clear(); movement.Set(nullptr); - botAI->TellMaster("I am ready for the next flight"); + botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "taxi_ready_next_flight", "I am ready for the next flight", {})); return true; } @@ -120,13 +122,15 @@ bool TaxiAction::Execute(Event event) { movement.taxiNodes.clear(); movement.Set(nullptr); - botAI->TellError("I can't fly with you"); + botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "taxi_cant_fly_with_you", "I can't fly with you", {})); return false; } return true; } - botAI->TellError("Cannot find any flightmaster to talk"); + botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "taxi_no_flightmaster_nearby", "Cannot find any flightmaster to talk", {})); return false; } diff --git a/src/Ai/Base/Actions/TradeStatusAction.cpp b/src/Ai/Base/Actions/TradeStatusAction.cpp index 96a7180a85a..c3a63ee7c9d 100644 --- a/src/Ai/Base/Actions/TradeStatusAction.cpp +++ b/src/Ai/Base/Actions/TradeStatusAction.cpp @@ -12,6 +12,7 @@ #include "ItemVisitors.h" #include "PlayerbotMgr.h" #include "PlayerbotSecurity.h" +#include "PlayerbotTextMgr.h" #include "Playerbots.h" #include "RandomPlayerbotMgr.h" #include "SetCraftAction.h" @@ -28,13 +29,17 @@ bool TradeStatusAction::Execute(Event event) // Allow the master and group members to trade if (trader != master && !traderBotAI && (!bot->GetGroup() || !bot->GetGroup()->IsMember(trader->GetGUID()))) { - bot->Whisper("I'm kind of busy now", LANG_UNIVERSAL, trader); + bot->Whisper(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "trade_busy_now", "I'm kind of busy now", {}), + LANG_UNIVERSAL, trader); return false; } if (sPlayerbotAIConfig.enableRandomBotTrading == 0 && (sRandomPlayerbotMgr.IsRandomBot(bot)|| sRandomPlayerbotMgr.IsAddclassBot(bot))) { - bot->Whisper("Trading is disabled", LANG_UNIVERSAL, trader); + bot->Whisper(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "trade_disabled", "Trading is disabled", {}), + LANG_UNIVERSAL, trader); return false; } @@ -180,9 +185,15 @@ bool TradeStatusAction::CheckTrade() { if (bot->GetGroup() && bot->GetGroup()->IsMember(bot->GetTrader()->GetGUID()) && botAI->HasRealPlayerMaster()) - botAI->TellMasterNoFacing("Thank you " + chat->FormatWorldobject(bot->GetTrader())); + botAI->TellMasterNoFacing(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "trade_thank_you_player", + "Thank you %player", + {{"%player", chat->FormatWorldobject(bot->GetTrader())}})); else - bot->Say("Thank you " + chat->FormatWorldobject(bot->GetTrader()), + bot->Say(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "trade_thank_you_player", + "Thank you %player", + {{"%player", chat->FormatWorldobject(bot->GetTrader())}}), (bot->GetTeamId() == TEAM_ALLIANCE ? LANG_COMMON : LANG_ORCISH)); } return isGettingItem; @@ -210,12 +221,16 @@ bool TradeStatusAction::CheckTrade() int32 playerMoney = trader->GetTradeData()->GetMoney() + playerItemsMoney; if (botItemsMoney > 0 && sPlayerbotAIConfig.enableRandomBotTrading == 2 && (sRandomPlayerbotMgr.IsRandomBot(bot)|| sRandomPlayerbotMgr.IsAddclassBot(bot))) { - bot->Whisper("Selling is disabled.", LANG_UNIVERSAL, trader); + bot->Whisper(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "trade_selling_disabled", "Selling is disabled.", {}), + LANG_UNIVERSAL, trader); return false; } if (playerItemsMoney && sPlayerbotAIConfig.enableRandomBotTrading == 3 && (sRandomPlayerbotMgr.IsRandomBot(bot)|| sRandomPlayerbotMgr.IsAddclassBot(bot))) { - bot->Whisper("Buying is disabled.", LANG_UNIVERSAL, trader); + bot->Whisper(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "trade_buying_disabled", "Buying is disabled.", {}), + LANG_UNIVERSAL, trader); return false; } for (uint32 slot = 0; slot < TRADE_SLOT_TRADED_COUNT; ++slot) @@ -224,8 +239,10 @@ bool TradeStatusAction::CheckTrade() if (item && !item->GetTemplate()->SellPrice && !item->GetTemplate()->IsConjuredConsumable()) { std::ostringstream out; - out << chat->FormatItem(item->GetTemplate()) << " - This is not for sale"; - botAI->TellMaster(out); + botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "trade_item_not_for_sale", + "%item - This is not for sale", + {{"%item", chat->FormatItem(item->GetTemplate())}})); botAI->PlaySound(TEXT_EMOTE_NO); return false; } @@ -239,8 +256,10 @@ bool TradeStatusAction::CheckTrade() if ((botMoney && !item->GetTemplate()->BuyPrice) || usage == ITEM_USAGE_NONE) { std::ostringstream out; - out << chat->FormatItem(item->GetTemplate()) << " - I don't need this"; - botAI->TellMaster(out); + botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "trade_item_not_needed", + "%item - I don't need this", + {{"%item", chat->FormatItem(item->GetTemplate())}})); botAI->PlaySound(TEXT_EMOTE_NO); return false; } @@ -252,7 +271,8 @@ bool TradeStatusAction::CheckTrade() if (!botItemsMoney && !playerItemsMoney) { - botAI->TellError("There are no items to trade"); + botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "trade_no_items_error", "There are no items to trade", {})); return false; } @@ -266,7 +286,8 @@ bool TradeStatusAction::CheckTrade() { if (moneyDelta < 0) { - botAI->TellError("You can use discount to buy items only"); + botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "trade_discount_buy_only", "You can use discount to buy items only", {})); botAI->PlaySound(TEXT_EMOTE_NO); return false; } @@ -282,16 +303,20 @@ bool TradeStatusAction::CheckTrade() switch (urand(0, 4)) { case 0: - botAI->TellMaster("A pleasure doing business with you"); + botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "trade_success_pleasure", "A pleasure doing business with you", {})); break; case 1: - botAI->TellMaster("Fair trade"); + botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "trade_success_fair_trade", "Fair trade", {})); break; case 2: - botAI->TellMaster("Thanks"); + botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "trade_success_thanks", "Thanks", {})); break; case 3: - botAI->TellMaster("Off with you"); + botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "trade_success_off_with_you", "Off with you", {})); break; } @@ -300,8 +325,10 @@ bool TradeStatusAction::CheckTrade() } std::ostringstream out; - out << "I want " << chat->formatMoney(-(delta + discount)) << " for this"; - botAI->TellMaster(out); + botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "trade_want_money_for_this", + "I want %money for this", + {{"%money", chat->formatMoney(-(delta + discount))}})); botAI->PlaySound(TEXT_EMOTE_NO); return false; } diff --git a/src/Ai/Base/Actions/TrainerAction.cpp b/src/Ai/Base/Actions/TrainerAction.cpp index 4f46bba186a..b0530cc578a 100644 --- a/src/Ai/Base/Actions/TrainerAction.cpp +++ b/src/Ai/Base/Actions/TrainerAction.cpp @@ -5,10 +5,14 @@ #include "TrainerAction.h" +#include "AiFactory.h" +#include "BisListMgr.h" #include "BudgetValues.h" #include "Event.h" #include "PlayerbotFactory.h" +#include "PlayerbotTextMgr.h" #include "Playerbots.h" +#include "ReputationMgr.h" #include "Trainer.h" bool TrainerAction::Execute(Event event) @@ -269,6 +273,282 @@ bool MaintenanceAction::Execute(Event /*event*/) return true; } +bool BisGearAction::RunAutogearFallback(uint16 effectiveIlvl) +{ + if (!sPlayerbotAIConfig.autoGearCommand) + { + botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "bis_autogear_unavailable_error", + "autogear command is not allowed, please check the configuration.", {})); + return false; + } + + botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "bis_no_rows_fallback", + "No BiS for your tier/spec/level, check cfg, running autogear instead", {})); + + // Wipe all equipped slots so autogear gears from scratch at the requested ilvl + // (avoids old high-tier items surviving the incremental 1.2x threshold). + for (uint8 slot = EQUIPMENT_SLOT_START; slot < EQUIPMENT_SLOT_END; ++slot) + { + if (slot == EQUIPMENT_SLOT_TABARD || slot == EQUIPMENT_SLOT_BODY) + continue; + if (bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot)) + bot->DestroyItem(INVENTORY_SLOT_BAG_0, slot, true); + } + + uint32 gs = effectiveIlvl == 0 + ? 0 + : PlayerbotFactory::CalcMixedGearScore(effectiveIlvl, sPlayerbotAIConfig.autoGearQualityLimit); + PlayerbotFactory factory(bot, bot->GetLevel(), sPlayerbotAIConfig.autoGearQualityLimit, gs); + factory.InitEquipment(false, sPlayerbotAIConfig.twoRoundsGearInit); + factory.InitAmmo(); + if (bot->GetLevel() >= sPlayerbotAIConfig.minEnchantingBotLevel) + factory.ApplyEnchantAndGemsNew(); + bot->DurabilityRepairAll(false, 1.0f, false); + return true; +} + +bool BisGearAction::Execute(Event event) +{ + if (!sPlayerbotAIConfig.autoGearBisCommand) + { + botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "bis_command_unavailable_error", + "bis command is not allowed, please check the configuration.", {})); + return false; + } + + if (!sPlayerbotAIConfig.autoGearCommandAltBots && + !sPlayerbotAIConfig.IsInRandomAccountList(bot->GetSession()->GetAccountId())) + { + botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "bis_altbot_refused_error", "You cannot use bis on alt bots.", {})); + return false; + } + + if (sPlayerbotAIConfig.autoGearQualityLimit < 4) + { + botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "bis_quality_floor_error", "AutoGearQualityLimit must be 4 for BiS.", {})); + return false; + } + + if (sRandomPlayerbotMgr.IsSpecPvp(bot->GetGUID().GetCounter(), bot->getClass())) + { + botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "bis_pvp_refused_error", "bis is PvE only, bot is configured as PvP.", {})); + return false; + } + + uint16 ilvl = static_cast(sPlayerbotAIConfig.autoGearScoreLimit); + + // Optional explicit ilvl override: `/p autogear bis 55`. + // Garbage or out-of-range args are hard-rejected: no autogear fallback, no gear change. + std::string const param = event.getParam(); + if (!param.empty()) + { + unsigned long parsed = 0; + size_t pos = 0; + bool valid = false; + try + { + parsed = std::stoul(param, &pos); + valid = (parsed > 0 && pos == param.size() && parsed <= 0xFFFFu); + } + catch (...) + { + valid = false; + } + + if (!valid) + { + std::map phs; + phs["%param"] = param; + botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "bis_invalid_arg_error", + "Invalid BiS ilvl argument '%param'. Use a positive integer.", phs)); + return false; + } + if (parsed > static_cast(sPlayerbotAIConfig.autoGearScoreLimit)) + { + std::map phs; + phs["%requested"] = std::to_string(parsed); + phs["%limit"] = std::to_string(sPlayerbotAIConfig.autoGearScoreLimit); + botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "bis_arg_above_limit_error", + "BiS ilvl %requested exceeds AutoGearScoreLimit %limit, refusing", phs)); + return false; + } + ilvl = static_cast(parsed); + } + uint8 cls = bot->getClass(); + uint8 tab = AiFactory::GetPlayerSpecTab(bot); + uint8 faction = bot->GetTeamId() == TEAM_ALLIANCE ? 1 : 2; + + // Druid Bear (Feral Tank) shares tab 1 with Cat. Use sentinel tab 10 when tank strategy active. + constexpr uint8 BIS_TAB_DRUID_BEAR = 10; + constexpr uint16 BIS_ILVL_FALLBACK_WINDOW = 20; + uint16 resolvedIlvl = 0; + std::map bisMap; + if (cls == CLASS_DRUID && tab == DRUID_TAB_FERAL && PlayerbotAI::IsTank(bot)) + bisMap = sBisListMgr->GetBisForNearest(ilvl, BIS_ILVL_FALLBACK_WINDOW, cls, BIS_TAB_DRUID_BEAR, faction, + &resolvedIlvl); + if (bisMap.empty()) + bisMap = sBisListMgr->GetBisForNearest(ilvl, BIS_ILVL_FALLBACK_WINDOW, cls, tab, faction, &resolvedIlvl); + + // No rows within fallback window -> full autogear fallback at the effective ilvl. + if (bisMap.empty()) + { + std::map phs; + phs["%ilvl"] = std::to_string(ilvl); + botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "bis_no_rows_autogear_msg", + "No BiS at ilvl %ilvl, using Autogear %ilvl instead", phs)); + return RunAutogearFallback(ilvl); + } + + if (resolvedIlvl != ilvl) + { + std::map phs; + phs["%requested"] = std::to_string(ilvl); + phs["%resolved"] = std::to_string(resolvedIlvl); + botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "bis_closest_match_msg", + "No BiS at ilvl %requested, using closest match at ilvl %resolved", phs)); + } + + botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "bis_applying_msg", "Applying BiS gear", {})); + + // 1. Wipe everything currently equipped so autogear starts from a clean slate. + // Old items linger in inventory otherwise and autogear leaves slots empty on bag conflicts. + for (uint8 slot = EQUIPMENT_SLOT_START; slot < EQUIPMENT_SLOT_END; ++slot) + { + if (slot == EQUIPMENT_SLOT_TABARD || slot == EQUIPMENT_SLOT_BODY) + continue; + if (bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot)) + bot->DestroyItem(INVENTORY_SLOT_BAG_0, slot, true); + } + + // Wipe equippable items from bags too. Autogear can shove old equipped items into bags + // (HandleAutoStoreBagItemOpcode), and a unique-equipped duplicate stuck in a bag blocks + // CanEquipNewItem on subsequent BiS runs. Spare consumables/reagents. + auto destroyIfEquippable = [&](uint8 bag, uint8 slot) + { + Item* item = bot->GetItemByPos(bag, slot); + if (!item) + return; + ItemTemplate const* tmpl = item->GetTemplate(); + if (!tmpl) + return; + if (tmpl->Class == ITEM_CLASS_WEAPON || tmpl->Class == ITEM_CLASS_ARMOR) + bot->DestroyItem(bag, slot, true); + }; + for (uint8 slot = INVENTORY_SLOT_ITEM_START; slot < INVENTORY_SLOT_ITEM_END; ++slot) + destroyIfEquippable(INVENTORY_SLOT_BAG_0, slot); + for (uint8 bag = INVENTORY_SLOT_BAG_START; bag < INVENTORY_SLOT_BAG_END; ++bag) + { + if (Bag* container = bot->GetBagByPos(bag)) + for (uint32 slot = 0; slot < container->GetBagSize(); ++slot) + destroyIfEquippable(bag, slot); + } + + // 2. Run full autogear on the empty bot so every slot gets a best-available pick. + // Uncovered slots will keep the autogear pick; BiS overwrites the rest below. + if (sPlayerbotAIConfig.autoGearCommand) + { + uint32 fillGs = ilvl == 0 + ? 0 + : PlayerbotFactory::CalcMixedGearScore(ilvl, sPlayerbotAIConfig.autoGearQualityLimit); + PlayerbotFactory fillFactory(bot, bot->GetLevel(), sPlayerbotAIConfig.autoGearQualityLimit, fillGs); + fillFactory.InitEquipment(false, sPlayerbotAIConfig.twoRoundsGearInit); + } + + // 2b. Pre-destroy autogear picks that would conflict with any BiS item by entry. + // Autogear may have placed the exact item BiS wants into trinket2/finger2 (or vice versa); + // unique-equipped enforcement would then make BiS's equip silently drop one copy. + std::set bisEntries; + for (auto const& kv : bisMap) + bisEntries.insert(kv.second); + for (uint8 slot = EQUIPMENT_SLOT_START; slot < EQUIPMENT_SLOT_END; ++slot) + { + if (Item* item = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot)) + if (bisEntries.count(item->GetEntry())) + bot->DestroyItem(INVENTORY_SLOT_BAG_0, slot, true); + } + + // 3. Apply BiS: only touch slots where the bot can actually equip the BiS item. + // If item requires reputation, grant the required rank first. If CanUseItem still + // fails (class/race/skill/level), keep autogear's pick for that slot. + for (auto const& kv : bisMap) + { + ItemTemplate const* proto = sObjectMgr->GetItemTemplate(kv.second); + if (!proto) + continue; + + // Grant required reputation rank if the item gates on it. + if (proto->RequiredReputationFaction && proto->RequiredReputationRank > 0) + { + if (FactionEntry const* fac = sFactionStore.LookupEntry(proto->RequiredReputationFaction)) + { + ReputationRank requiredRank = static_cast(proto->RequiredReputationRank); + if (bot->GetReputationRank(proto->RequiredReputationFaction) < requiredRank) + { + int32 standing = ReputationMgr::ReputationRankToStanding( + static_cast(requiredRank - 1)) + 1; + bot->GetReputationMgr().SetReputation(fac, standing); + } + } + } + + if (bot->CanUseItem(proto) != EQUIP_ERR_OK) + continue; + + uint8 slot = kv.first; + if (bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot)) + bot->DestroyItem(INVENTORY_SLOT_BAG_0, slot, true); + + uint16 dest = 0; + InventoryResult eqResult = bot->CanEquipNewItem(slot, dest, kv.second, false); + + // Paired slots (finger 10<->11, trinket 12<->13): destroy paired slot and retry once + // when unique-equipped or autogear residue blocks the first attempt. + if (eqResult != EQUIP_ERR_OK) + { + uint8 pairedSlot = 0xFF; + if (slot == EQUIPMENT_SLOT_FINGER1) pairedSlot = EQUIPMENT_SLOT_FINGER2; + else if (slot == EQUIPMENT_SLOT_FINGER2) pairedSlot = EQUIPMENT_SLOT_FINGER1; + else if (slot == EQUIPMENT_SLOT_TRINKET1) pairedSlot = EQUIPMENT_SLOT_TRINKET2; + else if (slot == EQUIPMENT_SLOT_TRINKET2) pairedSlot = EQUIPMENT_SLOT_TRINKET1; + + if (pairedSlot != 0xFF) + { + if (bot->GetItemByPos(INVENTORY_SLOT_BAG_0, pairedSlot)) + bot->DestroyItem(INVENTORY_SLOT_BAG_0, pairedSlot, true); + eqResult = bot->CanEquipNewItem(slot, dest, kv.second, false); + } + } + + if (eqResult == EQUIP_ERR_OK) + { + bot->EquipNewItem(dest, kv.second, true); + bot->AutoUnequipOffhandIfNeed(); + } + } + + PlayerbotFactory factory(bot, bot->GetLevel(), ITEM_QUALITY_EPIC, 0); + factory.InitAmmo(); + if (bot->GetLevel() >= sPlayerbotAIConfig.minEnchantingBotLevel) + factory.ApplyEnchantAndGemsNew(); + + bot->DurabilityRepairAll(false, 1.0f, false); + + botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "bis_applied_msg", "BiS applied", {})); + return true; +} + bool RemoveGlyphAction::Execute(Event /*event*/) { for (uint32 slotIndex = 0; slotIndex < MAX_GLYPH_SLOT_INDEX; ++slotIndex) diff --git a/src/Ai/Base/Actions/TrainerAction.h b/src/Ai/Base/Actions/TrainerAction.h index bfb6538de1b..733bed529e0 100644 --- a/src/Ai/Base/Actions/TrainerAction.h +++ b/src/Ai/Base/Actions/TrainerAction.h @@ -53,4 +53,14 @@ class AutoGearAction : public Action bool Execute(Event event) override; }; +class BisGearAction : public Action +{ +public: + BisGearAction(PlayerbotAI* botAI) : Action(botAI, "autogear bis") {} + bool Execute(Event event) override; + +private: + bool RunAutogearFallback(uint16 effectiveIlvl); +}; + #endif diff --git a/src/Ai/Base/Actions/UseItemAction.cpp b/src/Ai/Base/Actions/UseItemAction.cpp index dde06906c9d..403dbc0568c 100644 --- a/src/Ai/Base/Actions/UseItemAction.cpp +++ b/src/Ai/Base/Actions/UseItemAction.cpp @@ -9,6 +9,7 @@ #include "Event.h" #include "ItemPackets.h" #include "ItemUsageValue.h" +#include "PlayerbotTextMgr.h" #include "Playerbots.h" bool UseItemAction::Execute(Event event) @@ -35,7 +36,8 @@ bool UseItemAction::Execute(Event event) return UseItemOnGameObject(*items.begin(), *gos.begin()); } - botAI->TellError("No items (or game objects) available"); + botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "use_item_none_available", "No items (or game objects) available", {})); return false; } @@ -48,8 +50,10 @@ bool UseItemAction::UseGameObject(ObjectGuid guid) go->Use(bot); std::ostringstream out; - out << "Using " << chat->FormatGameobject(go); - botAI->TellMasterNoFacing(out.str()); + botAI->TellMasterNoFacing(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "use_gameobject", + "Using %gameobject", + {{"%gameobject", chat->FormatGameobject(go)}})); return true; } @@ -92,16 +96,16 @@ bool UseItemAction::UseItem(Item* item, ObjectGuid goGuid, Item* itemTarget, Uni bool targetSelected = false; - std::ostringstream out; - out << "Using " << chat->FormatItem(item->GetTemplate()); + std::string itemText = chat->FormatItem(item->GetTemplate()); + std::string targetText; if (item->GetTemplate()->Stackable > 1) { uint32 count = item->GetCount(); if (count > 1) - out << " (" << count << " available) "; + itemText += " (" + std::to_string(count) + " available)"; else - out << " (the last one!)"; + itemText += " (the last one!)"; } if (goGuid) @@ -114,7 +118,7 @@ bool UseItemAction::UseItem(Item* item, ObjectGuid goGuid, Item* itemTarget, Uni packet << targetFlag; packet << goGuid.WriteAsPacked(); - out << " on " << chat->FormatGameobject(go); + targetText = chat->FormatGameobject(go); targetSelected = true; } @@ -124,7 +128,8 @@ bool UseItemAction::UseItem(Item* item, ObjectGuid goGuid, Item* itemTarget, Uni { bool fit = SocketItem(itemTarget, item) || SocketItem(itemTarget, item, true); if (!fit) - botAI->TellMaster("Socket does not fit"); + botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "socket_does_not_fit", "Socket does not fit", {})); return fit; } @@ -133,7 +138,7 @@ bool UseItemAction::UseItem(Item* item, ObjectGuid goGuid, Item* itemTarget, Uni targetFlag = TARGET_FLAG_ITEM; packet << targetFlag; packet << itemTarget->GetGUID().WriteAsPacked(); - out << " on " << chat->FormatItem(itemTarget->GetTemplate()); + targetText = chat->FormatItem(itemTarget->GetTemplate()); targetSelected = true; } } @@ -149,7 +154,7 @@ bool UseItemAction::UseItem(Item* item, ObjectGuid goGuid, Item* itemTarget, Uni { targetFlag = TARGET_FLAG_UNIT; packet << targetFlag << masterSelection.WriteAsPacked(); - out << " on " << unit->GetName(); + targetText = unit->GetName(); targetSelected = true; } } @@ -159,7 +164,7 @@ bool UseItemAction::UseItem(Item* item, ObjectGuid goGuid, Item* itemTarget, Uni { targetFlag = TARGET_FLAG_UNIT; packet << targetFlag << unitTarget->GetGUID().WriteAsPacked(); - out << " on " << unitTarget->GetName(); + targetText = unitTarget->GetName(); targetSelected = true; } @@ -173,9 +178,7 @@ bool UseItemAction::UseItem(Item* item, ObjectGuid goGuid, Item* itemTarget, Uni packet << uint32(0); bot->GetSession()->HandleQuestgiverAcceptQuestOpcode(packet); - std::ostringstream out; - out << "Got quest " << chat->FormatQuest(qInfo); - botAI->TellMasterNoFacing(out.str()); + botAI->TellMasterNoFacing("Got quest " + chat->FormatQuest(qInfo)); return true; } } @@ -217,7 +220,7 @@ bool UseItemAction::UseItem(Item* item, ObjectGuid goGuid, Item* itemTarget, Uni targetFlag = TARGET_FLAG_TRADE_ITEM; packet << targetFlag << (uint8)1 << ObjectGuid((uint64)TRADE_SLOT_NONTRADED).WriteAsPacked(); targetSelected = true; - out << " on traded item"; + targetText = "traded item"; } else { @@ -225,7 +228,7 @@ bool UseItemAction::UseItem(Item* item, ObjectGuid goGuid, Item* itemTarget, Uni packet << targetFlag; packet << itemForSpell->GetGUID().WriteAsPacked(); targetSelected = true; - out << " on " << chat->FormatItem(itemForSpell->GetTemplate()); + targetText = chat->FormatItem(itemForSpell->GetTemplate()); } uint32 castTime = spellInfo->CalcCastTime(); botAI->SetNextCheckDelay(castTime + sPlayerbotAIConfig.reactDelay); @@ -246,17 +249,17 @@ bool UseItemAction::UseItem(Item* item, ObjectGuid goGuid, Item* itemTarget, Uni targetSelected = true; if (unitTarget == bot || !unitTarget->IsInWorld() || unitTarget->IsDuringRemoveFromWorld()) - out << " on self"; + targetText = "self"; else if (unitTarget->IsHostileTo(bot)) - out << " on self"; + targetText = "self"; else - out << " on " << unitTarget->GetName(); + targetText = unitTarget->GetName(); } else { packet << bot->GetPackGUID(); targetSelected = true; - out << " on self"; + targetText = "self"; } } @@ -307,7 +310,12 @@ bool UseItemAction::UseItem(Item* item, ObjectGuid goGuid, Item* itemTarget, Uni return false; // botAI->SetNextCheckDelay(sPlayerbotAIConfig.globalCoolDown); - botAI->TellMasterNoFacing(out.str()); + std::string useText = targetSelected + ? PlayerbotTextMgr::instance().GetBotTextOrDefault( + "use_item_on_target", "Using %item on %target", {{"%item", itemText}, {"%target", targetText}}) + : PlayerbotTextMgr::instance().GetBotTextOrDefault( + "use_item", "Using %item", {{"%item", itemText}}); + botAI->TellMasterNoFacing(useText); bot->GetSession()->HandleUseItemOpcode(packet); return true; } @@ -372,10 +380,10 @@ bool UseItemAction::SocketItem(Item* item, Item* gem, bool replace) if (fits) { - std::ostringstream out; - out << "Socketing " << chat->FormatItem(item->GetTemplate()); - out << " with " << chat->FormatItem(gem->GetTemplate()); - botAI->TellMaster(out); + botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "socketing_item_with_gem", + "Socketing %item with %gem", + {{"%item", chat->FormatItem(item->GetTemplate())}, {"%gem", chat->FormatItem(gem->GetTemplate())}})); WorldPackets::Item::SocketGems nicePacket(std::move(packet)); nicePacket.Read(); diff --git a/src/Ai/Base/Actions/UseMeetingStoneAction.cpp b/src/Ai/Base/Actions/UseMeetingStoneAction.cpp index d6032acb12d..382425db3e7 100644 --- a/src/Ai/Base/Actions/UseMeetingStoneAction.cpp +++ b/src/Ai/Base/Actions/UseMeetingStoneAction.cpp @@ -11,6 +11,7 @@ #include "GridNotifiersImpl.h" #include "NearestGameObjects.h" #include "PlayerbotAIConfig.h" +#include "PlayerbotTextMgr.h" #include "Playerbots.h" #include "PositionValue.h" @@ -36,7 +37,8 @@ bool UseMeetingStoneAction::Execute(Event event) if (bot->IsInCombat()) { - botAI->TellError("I am in combat"); + botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "meeting_stone_in_combat", "I am in combat", {})); return false; } @@ -73,13 +75,15 @@ bool SummonAction::Execute(Event /*event*/) if (SummonUsingGos(master, bot, true) || SummonUsingNpcs(master, bot, true)) { - botAI->TellMasterNoFacing("Hello!"); + botAI->TellMasterNoFacing(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "hello", "Hello!", {})); return true; } if (SummonUsingGos(bot, master, true) || SummonUsingNpcs(bot, master, true)) { - botAI->TellMasterNoFacing("Welcome!"); + botAI->TellMasterNoFacing(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "meeting_stone_welcome", "Welcome!", {})); return true; } @@ -99,7 +103,10 @@ bool SummonAction::SummonUsingGos(Player* summoner, Player* player, bool preserv return Teleport(summoner, player, preserveAuras); } - botAI->TellError(summoner == bot ? "There is no meeting stone nearby" : "There is no meeting stone near you"); + botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault( + summoner == bot ? "meeting_stone_none_nearby" : "meeting_stone_none_near_you", + summoner == bot ? "There is no meeting stone nearby" : "There is no meeting stone near you", + {})); return false; } @@ -119,13 +126,19 @@ bool SummonAction::SummonUsingNpcs(Player* summoner, Player* player, bool preser { if (!player->HasItemCount(6948, 1, false)) { - botAI->TellError(player == bot ? "I have no hearthstone" : "You have no hearthstone"); + botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault( + player == bot ? "meeting_stone_no_hearthstone_self" : "meeting_stone_no_hearthstone_you", + player == bot ? "I have no hearthstone" : "You have no hearthstone", + {})); return false; } if (player->HasSpellCooldown(8690)) { - botAI->TellError(player == bot ? "My hearthstone is not ready" : "Your hearthstone is not ready"); + botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault( + player == bot ? "meeting_stone_hearthstone_not_ready_self" : "meeting_stone_hearthstone_not_ready_you", + player == bot ? "My hearthstone is not ready" : "Your hearthstone is not ready", + {})); return false; } @@ -141,7 +154,10 @@ bool SummonAction::SummonUsingNpcs(Player* summoner, Player* player, bool preser } } - botAI->TellError(summoner == bot ? "There are no innkeepers nearby" : "There are no innkeepers near you"); + botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault( + summoner == bot ? "meeting_stone_no_innkeepers_nearby" : "meeting_stone_no_innkeepers_near_you", + summoner == bot ? "There are no innkeepers nearby" : "There are no innkeepers near you", + {})); return false; } @@ -153,7 +169,8 @@ bool SummonAction::Teleport(Player* summoner, Player* player, bool preserveAuras if (player->GetVehicle()) { - botAI->TellError("You cannot summon me while I'm on a vehicle"); + botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "meeting_stone_cannot_summon_vehicle", "You cannot summon me while I'm on a vehicle", {})); return false; } @@ -174,20 +191,29 @@ bool SummonAction::Teleport(Player* summoner, Player* player, bool preserveAuras if (summoner->IsInCombat() && !sPlayerbotAIConfig.allowSummonInCombat) { - botAI->TellError("You cannot summon me while you're in combat"); + botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "meeting_stone_cannot_summon_master_in_combat", + "You cannot summon me while you're in combat", + {})); return false; } if (!summoner->IsAlive() && !sPlayerbotAIConfig.allowSummonWhenMasterIsDead) { - botAI->TellError("You cannot summon me while you're dead"); + botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "meeting_stone_cannot_summon_master_dead", + "You cannot summon me while you're dead", + {})); return false; } if (bot->isDead() && !bot->HasPlayerFlag(PLAYER_FLAGS_GHOST) && !sPlayerbotAIConfig.allowSummonWhenBotIsDead) { - botAI->TellError("You cannot summon me while I'm dead, you need to release my spirit first"); + botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "meeting_stone_cannot_summon_bot_dead", + "You cannot summon me while I'm dead, you need to release my spirit first", + {})); return false; } @@ -199,7 +225,8 @@ bool SummonAction::Teleport(Player* summoner, Player* player, bool preserveAuras { bot->ResurrectPlayer(1.0f, false); bot->SpawnCorpseBones(); - botAI->TellMasterNoFacing("I live, again!"); + botAI->TellMasterNoFacing(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "meeting_stone_revived", "I live, again!", {})); botAI->GetAiObjectContext()->GetValue("prioritized targets")->Reset(); } @@ -229,6 +256,7 @@ bool SummonAction::Teleport(Player* summoner, Player* player, bool preserveAuras } if (summoner != player) - botAI->TellError("Not enough place to summon"); + botAI->TellError(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "meeting_stone_not_enough_space", "Not enough place to summon", {})); return false; } diff --git a/src/Ai/Base/ChatActionContext.h b/src/Ai/Base/ChatActionContext.h index 497ae2e9c13..9bcfda61750 100644 --- a/src/Ai/Base/ChatActionContext.h +++ b/src/Ai/Base/ChatActionContext.h @@ -139,6 +139,7 @@ class ChatActionContext : public NamedObjectContext creators["maintenance"] = &ChatActionContext::maintenance; creators["remove glyph"] = &ChatActionContext::remove_glyph; creators["autogear"] = &ChatActionContext::autogear; + creators["autogear bis"] = &ChatActionContext::autogear_bis; creators["equip upgrade"] = &ChatActionContext::equip_upgrade; creators["attack my target"] = &ChatActionContext::attack_my_target; creators["pull my target"] = &ChatActionContext::pull_my_target; @@ -261,6 +262,7 @@ class ChatActionContext : public NamedObjectContext static Action* maintenance(PlayerbotAI* botAI) { return new MaintenanceAction(botAI); } static Action* remove_glyph(PlayerbotAI* botAI) { return new RemoveGlyphAction(botAI); } static Action* autogear(PlayerbotAI* botAI) { return new AutoGearAction(botAI); } + static Action* autogear_bis(PlayerbotAI* botAI) { return new BisGearAction(botAI); } static Action* equip_upgrade(PlayerbotAI* botAI) { return new EquipUpgradeAction(botAI); } static Action* co(PlayerbotAI* botAI) { return new ChangeCombatStrategyAction(botAI); } static Action* nc(PlayerbotAI* botAI) { return new ChangeNonCombatStrategyAction(botAI); } diff --git a/src/Ai/Base/ChatTriggerContext.h b/src/Ai/Base/ChatTriggerContext.h index ef8827c2927..700fa95b460 100644 --- a/src/Ai/Base/ChatTriggerContext.h +++ b/src/Ai/Base/ChatTriggerContext.h @@ -65,6 +65,7 @@ class ChatTriggerContext : public NamedObjectContext creators["maintenance"] = &ChatTriggerContext::maintenance; creators["remove glyph"] = &ChatTriggerContext::remove_glyph; creators["autogear"] = &ChatTriggerContext::autogear; + creators["autogear bis"] = &ChatTriggerContext::autogear_bis; creators["equip upgrade"] = &ChatTriggerContext::equip_upgrade; creators["attack"] = &ChatTriggerContext::attack; creators["pull"] = &ChatTriggerContext::pull; @@ -220,6 +221,7 @@ class ChatTriggerContext : public NamedObjectContext static Trigger* maintenance(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "maintenance"); } static Trigger* remove_glyph(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "remove glyph"); } static Trigger* autogear(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "autogear"); } + static Trigger* autogear_bis(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "autogear bis"); } static Trigger* equip_upgrade(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "equip upgrade"); } static Trigger* co(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "co"); } static Trigger* nc(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "nc"); } diff --git a/src/Ai/Base/Strategy/ChatCommandHandlerStrategy.cpp b/src/Ai/Base/Strategy/ChatCommandHandlerStrategy.cpp index 9bb30a1ca7f..4afc86dcb3d 100644 --- a/src/Ai/Base/Strategy/ChatCommandHandlerStrategy.cpp +++ b/src/Ai/Base/Strategy/ChatCommandHandlerStrategy.cpp @@ -111,6 +111,7 @@ ChatCommandHandlerStrategy::ChatCommandHandlerStrategy(PlayerbotAI* botAI) : Pas supported.push_back("maintenance"); supported.push_back("remove glyph"); supported.push_back("autogear"); + supported.push_back("autogear bis"); supported.push_back("equip upgrade"); supported.push_back("chat"); supported.push_back("home"); diff --git a/src/Ai/Base/Trigger/GenericTriggers.cpp b/src/Ai/Base/Trigger/GenericTriggers.cpp index 27dd5999cbf..2035e1c29ca 100644 --- a/src/Ai/Base/Trigger/GenericTriggers.cpp +++ b/src/Ai/Base/Trigger/GenericTriggers.cpp @@ -34,6 +34,11 @@ bool MediumManaTrigger::IsActive() AI_VALUE2(uint8, "mana", "self target") < sPlayerbotAIConfig.mediumMana; } +bool LowEnergyTrigger::IsActive() +{ + return AI_VALUE2(uint8, "energy", "self target") < threshold; +} + bool NoPetTrigger::IsActive() { return (bot->GetMinionGUID().IsEmpty()) && (!AI_VALUE(Unit*, "pet target")) && (!bot->GetGuardianPet()) && diff --git a/src/Ai/Base/Trigger/GenericTriggers.h b/src/Ai/Base/Trigger/GenericTriggers.h index 7a44112f36f..a3931c24619 100644 --- a/src/Ai/Base/Trigger/GenericTriggers.h +++ b/src/Ai/Base/Trigger/GenericTriggers.h @@ -550,6 +550,17 @@ class MediumManaTrigger : public Trigger bool IsActive() override; }; +class LowEnergyTrigger : public Trigger +{ +public: + LowEnergyTrigger(PlayerbotAI* botAI, uint8 threshold = 30) : Trigger(botAI, "low energy"), threshold(threshold) {} + + bool IsActive() override; + +private: + uint8 threshold; +}; + BEGIN_TRIGGER(PanicTrigger, Trigger) // cppcheck-suppress unknownMacro std::string const getName() override { return "panic"; } END_TRIGGER() diff --git a/src/Ai/Base/Trigger/HealthTriggers.cpp b/src/Ai/Base/Trigger/HealthTriggers.cpp index a2b36962d53..746198acffa 100644 --- a/src/Ai/Base/Trigger/HealthTriggers.cpp +++ b/src/Ai/Base/Trigger/HealthTriggers.cpp @@ -22,6 +22,15 @@ bool DeadTrigger::IsActive() { return AI_VALUE2(bool, "dead", GetTargetName()); bool AoeHealTrigger::IsActive() { return AI_VALUE2(uint8, "aoe heal", type) >= count; } +bool HealerLowManaTrigger::IsActive() +{ + Unit* target = GetTarget(); + if (!target) + return false; + + return target->GetPowerPct(POWER_MANA) < sPlayerbotAIConfig.lowMana; +} + bool AoeInGroupTrigger::IsActive() { int32 member = botAI->GetNearGroupMemberCount(); diff --git a/src/Ai/Base/Trigger/HealthTriggers.h b/src/Ai/Base/Trigger/HealthTriggers.h index 0fbd403d72c..14e68c7e3d7 100644 --- a/src/Ai/Base/Trigger/HealthTriggers.h +++ b/src/Ai/Base/Trigger/HealthTriggers.h @@ -143,6 +143,15 @@ class TargetCriticalHealthTrigger : public TargetLowHealthTrigger TargetCriticalHealthTrigger(PlayerbotAI* botAI) : TargetLowHealthTrigger(botAI, 20) {} }; +class HealerLowManaTrigger : public Trigger +{ +public: + HealerLowManaTrigger(PlayerbotAI* botAI) : Trigger(botAI, "healer low mana") {} + + std::string const GetTargetName() override { return "healer low mana"; } + bool IsActive() override; +}; + class PartyMemberDeadTrigger : public Trigger { public: diff --git a/src/Ai/Base/Trigger/RtiTriggers.cpp b/src/Ai/Base/Trigger/RtiTriggers.cpp index c7dc737cf33..ba934c5d7a0 100644 --- a/src/Ai/Base/Trigger/RtiTriggers.cpp +++ b/src/Ai/Base/Trigger/RtiTriggers.cpp @@ -18,3 +18,19 @@ bool NoRtiTrigger::IsActive() Unit* target = AI_VALUE(Unit*, "rti target"); return target == nullptr; } + +// Fires when the RTI CC target should be crowd controlled by this spell. +// Standard path: the target is already in the attackers list and "cc target" matches the RTI +// mark — delegates to HasCcTargetTrigger to confirm no one else is already CCing it. +bool RtiCcTrigger::IsActive() +{ + Unit* rtiCcTarget = AI_VALUE(Unit*, "rti cc target"); + if (!rtiCcTarget) + return false; + + Unit* ccTarget = AI_VALUE2(Unit*, "cc target", getName()); + if (ccTarget && ccTarget == rtiCcTarget) + return HasCcTargetTrigger::IsActive(); + + return botAI->CanCastSpell(getName(), rtiCcTarget); +} diff --git a/src/Ai/Base/Trigger/RtiTriggers.h b/src/Ai/Base/Trigger/RtiTriggers.h index a8ecab4d93a..9ff128ec24f 100644 --- a/src/Ai/Base/Trigger/RtiTriggers.h +++ b/src/Ai/Base/Trigger/RtiTriggers.h @@ -6,6 +6,7 @@ #ifndef _PLAYERBOT_RTITRIGGERS_H #define _PLAYERBOT_RTITRIGGERS_H +#include "GenericTriggers.h" #include "Trigger.h" class PlayerbotAI; @@ -18,4 +19,12 @@ class NoRtiTrigger : public Trigger bool IsActive() override; }; +class RtiCcTrigger : public HasCcTargetTrigger +{ +public: + RtiCcTrigger(PlayerbotAI* botAI, std::string const name) : HasCcTargetTrigger(botAI, name) {} + + bool IsActive() override; +}; + #endif diff --git a/src/Ai/Base/TriggerContext.h b/src/Ai/Base/TriggerContext.h index 54edbb01794..d440e5b5ff3 100644 --- a/src/Ai/Base/TriggerContext.h +++ b/src/Ai/Base/TriggerContext.h @@ -51,6 +51,7 @@ class TriggerContext : public NamedObjectContext creators["low mana"] = &TriggerContext::LowMana; creators["medium mana"] = &TriggerContext::MediumMana; + creators["low energy"] = &TriggerContext::LowEnergy; creators["high mana"] = &TriggerContext::HighMana; creators["almost full mana"] = &TriggerContext::AlmostFullMana; creators["enough mana"] = &TriggerContext::EnoughMana; @@ -59,6 +60,7 @@ class TriggerContext : public NamedObjectContext creators["party member low health"] = &TriggerContext::PartyMemberLowHealth; creators["party member medium health"] = &TriggerContext::PartyMemberMediumHealth; creators["party member almost full health"] = &TriggerContext::PartyMemberAlmostFullHealth; + creators["healer low mana"] = &TriggerContext::HealerLowMana; creators["generic boost"] = &TriggerContext::generic_boost; creators["loss of control"] = &TriggerContext::loss_of_control; @@ -312,6 +314,7 @@ class TriggerContext : public NamedObjectContext static Trigger* TargetCriticalHealth(PlayerbotAI* botAI) { return new TargetCriticalHealthTrigger(botAI); } static Trigger* LowMana(PlayerbotAI* botAI) { return new LowManaTrigger(botAI); } static Trigger* MediumMana(PlayerbotAI* botAI) { return new MediumManaTrigger(botAI); } + static Trigger* LowEnergy(PlayerbotAI* botAI) { return new LowEnergyTrigger(botAI); } static Trigger* HighMana(PlayerbotAI* botAI) { return new HighManaTrigger(botAI); } static Trigger* AlmostFullMana(PlayerbotAI* botAI) { return new AlmostFullManaTrigger(botAI); } static Trigger* EnoughMana(PlayerbotAI* botAI) { return new EnoughManaTrigger(botAI); } @@ -383,6 +386,7 @@ class TriggerContext : public NamedObjectContext { return new PartyMemberCriticalHealthTrigger(botAI); } + static Trigger* HealerLowMana(PlayerbotAI* botAI) { return new HealerLowManaTrigger(botAI); } static Trigger* protect_party_member(PlayerbotAI* botAI) { return new ProtectPartyMemberTrigger(botAI); } static Trigger* no_pet(PlayerbotAI* botAI) { return new NoPetTrigger(botAI); } static Trigger* has_pet(PlayerbotAI* botAI) { return new HasPetTrigger(botAI); } diff --git a/src/Ai/Base/Value/PartyMemberToHeal.cpp b/src/Ai/Base/Value/PartyMemberToHeal.cpp index 9845bc11912..80621775a7a 100644 --- a/src/Ai/Base/Value/PartyMemberToHeal.cpp +++ b/src/Ai/Base/Value/PartyMemberToHeal.cpp @@ -135,6 +135,32 @@ bool PartyMemberToHeal::Check(Unit* player) bot->GetDistance2d(player) < sPlayerbotAIConfig.healDistance * 2 && bot->IsWithinLOSInMap(player); } +Unit* HealerLowMana::Calculate() +{ + Group* group = bot->GetGroup(); + if (!group) + return nullptr; + + MinValueCalculator calc(100); + + for (GroupReference* gref = group->GetFirstMember(); gref; gref = gref->next()) + { + Player* player = gref->GetSource(); + if (!player || player == bot) + continue; + if (player->IsGameMaster() || !player->IsAlive()) + continue; + if (!botAI->IsHeal(player)) + continue; + + float mana = player->GetPowerPct(POWER_MANA); + if (mana < calc.minValue) + calc.probe(mana, player); + } + + return (Unit*)calc.param; +} + Unit* PartyMemberToProtect::Calculate() { return nullptr; diff --git a/src/Ai/Base/Value/PartyMemberToHeal.h b/src/Ai/Base/Value/PartyMemberToHeal.h index f86035c09b1..6a255b724e7 100644 --- a/src/Ai/Base/Value/PartyMemberToHeal.h +++ b/src/Ai/Base/Value/PartyMemberToHeal.h @@ -37,4 +37,13 @@ class PartyMemberToProtect : public PartyMemberValue Unit* Calculate() override; }; +class HealerLowMana : public PartyMemberValue +{ +public: + HealerLowMana(PlayerbotAI* botAI) : PartyMemberValue(botAI, "healer low mana") {} + +protected: + Unit* Calculate() override; +}; + #endif diff --git a/src/Ai/Base/ValueContext.h b/src/Ai/Base/ValueContext.h index bac5fd835c4..4070da80b56 100644 --- a/src/Ai/Base/ValueContext.h +++ b/src/Ai/Base/ValueContext.h @@ -132,6 +132,7 @@ class ValueContext : public NamedObjectContext creators["attacker without aura"] = &ValueContext::attacker_without_aura; creators["melee attacker without aura"] = &ValueContext::melee_attacker_without_aura; creators["party member to heal"] = &ValueContext::party_member_to_heal; + creators["healer low mana"] = &ValueContext::healer_low_mana; creators["party member to resurrect"] = &ValueContext::party_member_to_resurrect; creators["current target"] = &ValueContext::current_target; creators["self target"] = &ValueContext::self_target; @@ -451,6 +452,7 @@ class ValueContext : public NamedObjectContext return new MeleeAttackerWithoutAuraTargetValue(botAI); } static UntypedValue* party_member_to_heal(PlayerbotAI* botAI) { return new PartyMemberToHeal(botAI); } + static UntypedValue* healer_low_mana(PlayerbotAI* botAI) { return new HealerLowMana(botAI); } static UntypedValue* party_member_to_resurrect(PlayerbotAI* botAI) { return new PartyMemberToResurrect(botAI); } static UntypedValue* party_member_to_dispel(PlayerbotAI* botAI) { return new PartyMemberToDispel(botAI); } static UntypedValue* party_member_to_protect(PlayerbotAI* botAI) { return new PartyMemberToProtect(botAI); } diff --git a/src/Ai/Class/Dk/Strategy/GenericDKNonCombatStrategy.cpp b/src/Ai/Class/Dk/Strategy/GenericDKNonCombatStrategy.cpp index 28179d74ece..9bf2b54b16e 100644 --- a/src/Ai/Class/Dk/Strategy/GenericDKNonCombatStrategy.cpp +++ b/src/Ai/Class/Dk/Strategy/GenericDKNonCombatStrategy.cpp @@ -41,6 +41,7 @@ void GenericDKNonCombatStrategy::InitTriggers(std::vector& trigger { NonCombatStrategy::InitTriggers(triggers); + triggers.push_back(new TriggerNode("often", { NextAction("apply stone", 1.0f) })); triggers.push_back( new TriggerNode("horn of winter", { NextAction("horn of winter", 21.0f) })); triggers.push_back( diff --git a/src/Ai/Class/Druid/Action/DruidActions.cpp b/src/Ai/Class/Druid/Action/DruidActions.cpp index c01cc4342e3..e32a5ea04f7 100644 --- a/src/Ai/Class/Druid/Action/DruidActions.cpp +++ b/src/Ai/Class/Druid/Action/DruidActions.cpp @@ -11,6 +11,9 @@ #include "AoeValues.h" #include "TargetValue.h" +constexpr uint32 SPELL_ECLIPSE_SOLAR = 48517; +constexpr uint32 SPELL_ECLIPSE_LUNAR = 48518; + namespace { bool PrepareThornsTarget(PlayerbotAI* botAI, Unit* target) @@ -64,16 +67,89 @@ bool CastThornsOnMainTankAction::Execute(Event event) return PrepareThornsTarget(botAI, GetTarget()) && BuffOnMainTankAction::Execute(event); } +bool CastWrathAction::isUseful() +{ + time_t now = time(nullptr); + time_t solarTime = context->GetValue("eclipse solar proc time")->Get(); + time_t lunarTime = context->GetValue("eclipse lunar proc time")->Get(); + + // --- Update Solar Eclipse tracking --- + // Wrath is selected during Solar Eclipse (eclipse trigger at 20.0f), so we reliably see it here. + if (bot->HasAura(SPELL_ECLIPSE_SOLAR) && !solarTime) + context->GetValue("eclipse solar proc time")->Set(now); + // Lunar procced — Solar fishing window is over, new cycle begins + if (bot->HasAura(SPELL_ECLIPSE_LUNAR) && solarTime) + context->GetValue("eclipse solar proc time")->Set(0); + // 30 s cooldown window expired + if (solarTime && (now - solarTime) >= 30) + context->GetValue("eclipse solar proc time")->Set(0); + + // --- Update Lunar Eclipse tracking (belt-and-suspenders in case Starfire isn't evaluated) --- + if (bot->HasAura(SPELL_ECLIPSE_LUNAR) && !lunarTime) + context->GetValue("eclipse lunar proc time")->Set(now); + // Solar procced — Lunar fishing window is over + if (bot->HasAura(SPELL_ECLIPSE_SOLAR) && lunarTime) + context->GetValue("eclipse lunar proc time")->Set(0); + if (lunarTime && (now - lunarTime) >= 30) + context->GetValue("eclipse lunar proc time")->Set(0); + + // Block Wrath while in Lunar Eclipse / post-Lunar fishing window + if (context->GetValue("eclipse lunar proc time")->Get()) + return false; + + return CastSpellAction::isUseful(); +} + +bool CastStarfireAction::isUseful() +{ + time_t now = time(nullptr); + time_t solarTime = context->GetValue("eclipse solar proc time")->Get(); + time_t lunarTime = context->GetValue("eclipse lunar proc time")->Get(); + + // --- Update Lunar Eclipse tracking --- + // Starfire is selected during Lunar Eclipse (eclipse trigger at 20.0f), so we reliably see it here. + if (bot->HasAura(SPELL_ECLIPSE_LUNAR) && !lunarTime) + context->GetValue("eclipse lunar proc time")->Set(now); + // Solar procced — Lunar fishing window is over, new cycle begins + if (bot->HasAura(SPELL_ECLIPSE_SOLAR) && lunarTime) + context->GetValue("eclipse lunar proc time")->Set(0); + // 30 s cooldown window expired + if (lunarTime && (now - lunarTime) >= 30) + context->GetValue("eclipse lunar proc time")->Set(0); + + // --- Update Solar Eclipse tracking (belt-and-suspenders in case Wrath isn't evaluated) --- + if (bot->HasAura(SPELL_ECLIPSE_SOLAR) && !solarTime) + context->GetValue("eclipse solar proc time")->Set(now); + // Lunar procced — Solar fishing window is over + if (bot->HasAura(SPELL_ECLIPSE_LUNAR) && solarTime) + context->GetValue("eclipse solar proc time")->Set(0); + if (solarTime && (now - solarTime) >= 30) + context->GetValue("eclipse solar proc time")->Set(0); + + // Block Starfire while in Solar Eclipse / post-Solar fishing window + if (context->GetValue("eclipse solar proc time")->Get()) + return false; + + return CastSpellAction::isUseful(); +} + Value* CastEntanglingRootsCcAction::GetTargetValue() { - return context->GetValue("cc target", "entangling roots"); + return context->GetValue("rti cc target"); } -bool CastEntanglingRootsCcAction::Execute(Event /*event*/) { return botAI->CastSpell("entangling roots", GetTarget()); } +Value* CastHibernateCcAction::GetTargetValue() { return context->GetValue("rti cc target"); } + +Value* CastCycloneCcAction::GetTargetValue() { return context->GetValue("rti cc target"); } -Value* CastHibernateCcAction::GetTargetValue() { return context->GetValue("cc target", "hibernate"); } +bool CastTyphoonAction::isUseful() +{ + bool facingTarget = AI_VALUE2(bool, "facing", "current target"); + bool targetClose = ServerFacade::instance().IsDistanceLessOrEqualThan( + AI_VALUE2(float, "distance", GetTargetName()), 15.f); + return facingTarget && targetClose; +} -bool CastHibernateCcAction::Execute(Event /*event*/) { return botAI->CastSpell("hibernate", GetTarget()); } bool CastStarfallAction::isUseful() { if (!CastSpellAction::isUseful()) @@ -89,12 +165,26 @@ bool CastStarfallAction::isUseful() return false; } - // Avoid single-target usage on initial pull - uint8 aoeCount = *context->GetValue("aoe count"); - if (aoeCount < 2) + // Suppress if any unengaged hostile unit is within 40 yards — Starfall's 36-yard radius would pull them. + Unit* currentTarget = AI_VALUE(Unit*, "current target"); + GuidVector const& nearbyNpcs = AI_VALUE(GuidVector, "possible targets"); + for (ObjectGuid const& guid : nearbyNpcs) { - Unit* target = context->GetValue("current target")->Get(); - if (!target || (!botAI->HasAura("moonfire", target) && !botAI->HasAura("insect swarm", target))) + Unit* unit = botAI->GetUnit(guid); + // Standard null/world-state guard before touching the unit. + if (!unit || !unit->IsAlive() || !unit->IsInWorld()) + continue; + // Already our target — its in-combat flag covers it. + if (unit == currentTarget) + continue; + // Safety net for any hostile-faction trigger creature that carries NON_ATTACKABLE flags. + if (!bot->IsValidAttackTarget(unit)) + continue; + // Outside Starfall's actual radius; no pull risk. + if (ServerFacade::instance().GetDistance2d(bot, unit) > 40.0f) + continue; + // Unengaged mob within range — casting would pull it. + if (!unit->IsInCombat()) return false; } @@ -119,6 +209,24 @@ bool CastRebirthAction::isUseful() AI_VALUE2(float, "distance", GetTargetName()) <= sPlayerbotAIConfig.spellDistance; } +bool CastInnervateOnHealerAction::isPossible() +{ + Unit* target = GetTarget(); + if (!target || !target->IsInWorld()) + return false; + + if (botAI->HasAura("innervate", target)) + return false; + + uint32 spellId = AI_VALUE2(uint32, "spell id", "innervate"); + return spellId && !bot->HasSpellCooldown(spellId); +} + +std::vector CastInnervateOnHealerAction::getPrerequisites() +{ + return { NextAction("caster form") }; +} + Unit* CastRejuvenationOnNotFullAction::GetTarget() { Group* group = bot->GetGroup(); @@ -149,3 +257,63 @@ bool CastRejuvenationOnNotFullAction::isUseful() { return GetTarget(); } + +// --- Blanket HoT actions --- + +Unit* CastBlanketHotAction::GetBlanketTarget(std::string const& auraName) +{ + Group* group = bot->GetGroup(); + if (!group) + return nullptr; + + auto eligible = [&](Player* member) -> bool + { + return member && member->IsAlive() && + !member->IsGameMaster() && + bot->GetDistance2d(member) <= sPlayerbotAIConfig.spellDistance && + !botAI->HasAura(auraName, member, false, true); + }; + + Player* firstMelee = nullptr; + Player* firstRanged = nullptr; + + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + if (!eligible(member)) + continue; + + if (PlayerbotAI::IsTank(member)) + return member; + else if (!firstMelee && PlayerbotAI::IsMelee(member) && !PlayerbotAI::IsTank(member)) + firstMelee = member; + else if (!firstRanged && PlayerbotAI::IsRanged(member)) + firstRanged = member; + + if (firstMelee && firstRanged) + break; + } + + if (firstMelee) return firstMelee; + return firstRanged; +} + +Unit* CastRejuvenationBlanketAction::GetTarget() +{ + return GetBlanketTarget("rejuvenation"); +} + +bool CastRejuvenationBlanketAction::isUseful() +{ + return GetTarget() != nullptr; +} + +Unit* CastWildGrowthBlanketAction::GetTarget() +{ + return GetBlanketTarget("wild growth"); +} + +bool CastWildGrowthBlanketAction::isUseful() +{ + return GetTarget() != nullptr; +} diff --git a/src/Ai/Class/Druid/Action/DruidActions.h b/src/Ai/Class/Druid/Action/DruidActions.h index 7e02a985fd0..e386ff05d00 100644 --- a/src/Ai/Class/Druid/Action/DruidActions.h +++ b/src/Ai/Class/Druid/Action/DruidActions.h @@ -8,6 +8,7 @@ #include "GenericSpellActions.h" #include "SharedDefines.h" +#include "Value.h" class PlayerbotAI; class Unit; @@ -64,7 +65,7 @@ class CastHealingTouchOnPartyAction : public HealPartyMemberAction { public: CastHealingTouchOnPartyAction(PlayerbotAI* botAI) - : HealPartyMemberAction(botAI, "healing touch", 50.0f, HealingManaEfficiency::LOW) + : HealPartyMemberAction(botAI, "healing touch", 50.0f, HealingManaEfficiency::MEDIUM) { } }; @@ -142,16 +143,11 @@ class CastLifebloomOnMainTankAction : public BuffOnMainTankAction bool isUseful() override; }; -class CastOmenOfClarityAction : public CastBuffSpellAction -{ -public: - CastOmenOfClarityAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "omen of clarity") {} -}; - class CastWrathAction : public CastSpellAction { public: CastWrathAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "wrath") {} + bool isUseful() override; }; class CastStarfallAction : public CastSpellAction @@ -169,6 +165,14 @@ class CastHurricaneAction : public CastSpellAction ActionThreatType getThreatType() override { return ActionThreatType::Aoe; } }; +class CastTyphoonAction : public CastSpellAction +{ +public: + CastTyphoonAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "typhoon") {} + ActionThreatType getThreatType() override { return ActionThreatType::Aoe; } + bool isUseful() override; +}; + class CastMoonfireAction : public CastDebuffSpellAction { public: @@ -185,6 +189,7 @@ class CastStarfireAction : public CastSpellAction { public: CastStarfireAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "starfire") {} + bool isUseful() override; }; class CastEntanglingRootsAction : public CastSpellAction @@ -193,12 +198,11 @@ class CastEntanglingRootsAction : public CastSpellAction CastEntanglingRootsAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "entangling roots") {} }; -class CastEntanglingRootsCcAction : public CastSpellAction +class CastEntanglingRootsCcAction : public CastCrowdControlSpellAction { public: - CastEntanglingRootsCcAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "entangling roots on cc") {} + CastEntanglingRootsCcAction(PlayerbotAI* botAI) : CastCrowdControlSpellAction(botAI, "entangling roots") {} Value* GetTargetValue() override; - bool Execute(Event event) override; }; class CastHibernateAction : public CastSpellAction @@ -207,12 +211,18 @@ class CastHibernateAction : public CastSpellAction CastHibernateAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "hibernate") {} }; -class CastHibernateCcAction : public CastSpellAction +class CastHibernateCcAction : public CastCrowdControlSpellAction { public: - CastHibernateCcAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "hibernate on cc") {} + CastHibernateCcAction(PlayerbotAI* botAI) : CastCrowdControlSpellAction(botAI, "hibernate") {} + Value* GetTargetValue() override; +}; + +class CastCycloneCcAction : public CastCrowdControlSpellAction +{ +public: + CastCycloneCcAction(PlayerbotAI* botAI) : CastCrowdControlSpellAction(botAI, "cyclone") {} Value* GetTargetValue() override; - bool Execute(Event event) override; }; class CastNaturesGraspAction : public CastBuffSpellAction @@ -264,6 +274,16 @@ class CastInnervateAction : public CastSpellAction std::string const GetTargetName() override { return "self target"; } }; +class CastInnervateOnHealerAction : public CastSpellAction +{ +public: + CastInnervateOnHealerAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "innervate") {} + + std::string const GetTargetName() override { return "healer low mana"; } + bool isPossible() override; + std::vector getPrerequisites() override; +}; + class CastTranquilityAction : public CastAoeHealSpellAction { public: @@ -312,13 +332,15 @@ class CastDruidRemoveCurseOnPartyAction : public CurePartyMemberAction class CastInsectSwarmOnAttackerAction : public CastDebuffSpellOnAttackerAction { public: - CastInsectSwarmOnAttackerAction(PlayerbotAI* ai) : CastDebuffSpellOnAttackerAction(ai, "insect swarm") {} + CastInsectSwarmOnAttackerAction(PlayerbotAI* ai) : CastDebuffSpellOnAttackerAction(ai, "insect swarm", true, 0.0f) {} + bool isUseful() override { return CastAuraSpellAction::isUseful(); } }; class CastMoonfireOnAttackerAction : public CastDebuffSpellOnAttackerAction { public: - CastMoonfireOnAttackerAction(PlayerbotAI* ai) : CastDebuffSpellOnAttackerAction(ai, "moonfire") {} + CastMoonfireOnAttackerAction(PlayerbotAI* ai) : CastDebuffSpellOnAttackerAction(ai, "moonfire", true, 0.0f) {} + bool isUseful() override { return CastAuraSpellAction::isUseful(); } }; class CastEnrageAction : public CastBuffSpellAction @@ -344,4 +366,48 @@ class CastForceOfNatureAction : public CastSpellAction CastForceOfNatureAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "force of nature") {} }; +// Base for blanket HoT actions. Provides GetBlanketTarget() as a member so +// subclasses can use AI_VALUE and the standard context machinery. +class CastBlanketHotAction : public CastSpellAction +{ +public: + CastBlanketHotAction(PlayerbotAI* ai, std::string const& spell) : CastSpellAction(ai, spell) + { + range = botAI->GetRange("heal"); + } + +protected: + Unit* GetBlanketTarget(std::string const& auraName); +}; + +class CastRejuvenationBlanketAction : public CastBlanketHotAction +{ +public: + CastRejuvenationBlanketAction(PlayerbotAI* ai) : CastBlanketHotAction(ai, "rejuvenation") {} + bool isUseful() override; + Unit* GetTarget() override; + std::string const getName() override { return "rejuvenation blanket"; } +}; + +class CastWildGrowthBlanketAction : public CastBlanketHotAction +{ +public: + CastWildGrowthBlanketAction(PlayerbotAI* ai) : CastBlanketHotAction(ai, "wild growth") {} + bool isUseful() override; + Unit* GetTarget() override; + std::string const getName() override { return "wild growth blanket"; } +}; + +class EclipseSolarProcTimeValue : public ManualSetValue +{ +public: + EclipseSolarProcTimeValue(PlayerbotAI* botAI) : ManualSetValue(botAI, 0) {} +}; + +class EclipseLunarProcTimeValue : public ManualSetValue +{ +public: + EclipseLunarProcTimeValue(PlayerbotAI* botAI) : ManualSetValue(botAI, 0) {} +}; + #endif diff --git a/src/Ai/Class/Druid/Action/DruidCatActions.h b/src/Ai/Class/Druid/Action/DruidCatActions.h index f6d8f232039..1fdd9aba7ff 100644 --- a/src/Ai/Class/Druid/Action/DruidCatActions.h +++ b/src/Ai/Class/Druid/Action/DruidCatActions.h @@ -9,12 +9,23 @@ #include "GenericSpellActions.h" #include "ReachTargetActions.h" +constexpr uint32 SPELL_POUNCE_RANK_1 = 9005; +constexpr uint32 SPELL_RAVAGE_RANK_1 = 6785; + class PlayerbotAI; class CastFeralChargeCatAction : public CastReachTargetSpellAction { public: CastFeralChargeCatAction(PlayerbotAI* botAI) : CastReachTargetSpellAction(botAI, "feral charge - cat", 1.5f) {} + + bool isUseful() override + { + if (botAI->HasAura("prowl", bot)) + return false; + + return CastReachTargetSpellAction::isUseful(); + } }; class CastCowerAction : public CastBuffSpellAction @@ -48,28 +59,47 @@ class CastRakeAction : public CastDebuffSpellAction CastRakeAction(PlayerbotAI* botAI) : CastDebuffSpellAction(botAI, "rake", true, 6.0f) {} }; -class CastRakeOnMeleeAttackersAction : public CastDebuffSpellOnMeleeAttackerAction -{ -public: - CastRakeOnMeleeAttackersAction(PlayerbotAI* botAI) : CastDebuffSpellOnMeleeAttackerAction(botAI, "rake", true, 6.0f) {} -}; - class CastClawAction : public CastMeleeSpellAction { public: CastClawAction(PlayerbotAI* botAI) : CastMeleeSpellAction(botAI, "claw") {} + + bool isUseful() override + { + // Block Claw once Pounce is learned; Claw remains available as the stealth opener before then. + if (botAI->HasAura("prowl", bot) && bot->HasSpell(SPELL_POUNCE_RANK_1)) + return false; + + return CastMeleeSpellAction::isUseful(); + } }; class CastMangleCatAction : public CastMeleeSpellAction { public: CastMangleCatAction(PlayerbotAI* botAI) : CastMeleeSpellAction(botAI, "mangle (cat)") {} + + bool isUseful() override + { + if (botAI->HasAura("prowl", bot)) + return false; + + return CastMeleeSpellAction::isUseful(); + } }; class CastSwipeCatAction : public CastMeleeSpellAction { public: CastSwipeCatAction(PlayerbotAI* botAI) : CastMeleeSpellAction(botAI, "swipe (cat)") {} + + bool isUseful() override + { + if (botAI->HasAura("prowl", bot)) + return false; + + return CastMeleeSpellAction::isUseful(); + } }; class CastFerociousBiteAction : public CastMeleeSpellAction @@ -78,6 +108,21 @@ class CastFerociousBiteAction : public CastMeleeSpellAction CastFerociousBiteAction(PlayerbotAI* botAI) : CastMeleeSpellAction(botAI, "ferocious bite") {} }; +class CastMaimAction : public CastMeleeSpellAction +{ +public: + CastMaimAction(PlayerbotAI* botAI) : CastMeleeSpellAction(botAI, "maim") {} + + bool isUseful() override + { + Unit* target = GetTarget(); + if (!target || !target->ToPlayer()) + return false; + + return CastMeleeSpellAction::isUseful(); + } +}; + class CastRipAction : public CastMeleeDebuffSpellAction { public: @@ -88,6 +133,14 @@ class CastShredAction : public CastMeleeSpellAction { public: CastShredAction(PlayerbotAI* botAI) : CastMeleeSpellAction(botAI, "shred") {} + + bool isUseful() override + { + if (botAI->HasAura("prowl", bot) && bot->HasSpell(SPELL_RAVAGE_RANK_1)) + return false; + + return CastMeleeSpellAction::isUseful(); + } }; class CastProwlAction : public CastBuffSpellAction @@ -106,12 +159,28 @@ class CastRavageAction : public CastMeleeSpellAction { public: CastRavageAction(PlayerbotAI* botAI) : CastMeleeSpellAction(botAI, "ravage") {} + + bool isUseful() override + { + if (!botAI->HasAura("prowl", bot)) + return false; + + return CastMeleeSpellAction::isUseful(); + } }; class CastPounceAction : public CastMeleeSpellAction { public: CastPounceAction(PlayerbotAI* botAI) : CastMeleeSpellAction(botAI, "pounce") {} + + bool isUseful() override + { + if (!botAI->HasAura("prowl", bot)) + return false; + + return CastMeleeSpellAction::isUseful(); + } }; #endif diff --git a/src/Ai/Class/Druid/DruidAiObjectContext.cpp b/src/Ai/Class/Druid/DruidAiObjectContext.cpp index 4d74d1db357..9a4f243c014 100644 --- a/src/Ai/Class/Druid/DruidAiObjectContext.cpp +++ b/src/Ai/Class/Druid/DruidAiObjectContext.cpp @@ -5,9 +5,9 @@ #include "DruidAiObjectContext.h" -#include "BearTankDruidStrategy.h" -#include "CasterDruidStrategy.h" -#include "CatDpsDruidStrategy.h" +#include "BalanceDruidStrategy.h" +#include "BearDruidStrategy.h" +#include "CatDruidStrategy.h" #include "DruidActions.h" #include "DruidBearActions.h" #include "DruidCatActions.h" @@ -15,9 +15,7 @@ #include "DruidTriggers.h" #include "GenericDruidNonCombatStrategy.h" #include "GenericDruidStrategy.h" -#include "HealDruidStrategy.h" -#include "MeleeDruidStrategy.h" -#include "OffhealDruidCatStrategy.h" +#include "RestoDruidStrategy.h" #include "Playerbots.h" #include "DruidPullStrategy.h" @@ -28,30 +26,31 @@ class DruidStrategyFactoryInternal : public NamedObjectContext { creators["nc"] = &DruidStrategyFactoryInternal::nc; creators["pull"] = &DruidStrategyFactoryInternal::pull; - creators["cat aoe"] = &DruidStrategyFactoryInternal::cat_aoe; - creators["caster aoe"] = &DruidStrategyFactoryInternal::caster_aoe; - creators["caster debuff"] = &DruidStrategyFactoryInternal::caster_debuff; - creators["dps debuff"] = &DruidStrategyFactoryInternal::caster_debuff; + creators["aoe"] = &DruidStrategyFactoryInternal::aoe; creators["cure"] = &DruidStrategyFactoryInternal::cure; - creators["melee"] = &DruidStrategyFactoryInternal::melee; creators["buff"] = &DruidStrategyFactoryInternal::buff; creators["boost"] = &DruidStrategyFactoryInternal::boost; creators["cc"] = &DruidStrategyFactoryInternal::cc; creators["healer dps"] = &DruidStrategyFactoryInternal::healer_dps; + creators["offheal"] = &DruidStrategyFactoryInternal::offheal; + creators["blanketing"] = &DruidStrategyFactoryInternal::blanketing; + creators["tranquility"] = &DruidStrategyFactoryInternal::tranquility; + creators["feral charge"] = &DruidStrategyFactoryInternal::feral_charge; } private: static Strategy* nc(PlayerbotAI* botAI) { return new GenericDruidNonCombatStrategy(botAI); } static Strategy* pull(PlayerbotAI* botAI) { return new DruidPullStrategy(botAI); } - static Strategy* cat_aoe(PlayerbotAI* botAI) { return new CatAoeDruidStrategy(botAI); } - static Strategy* caster_aoe(PlayerbotAI* botAI) { return new CasterDruidAoeStrategy(botAI); } - static Strategy* caster_debuff(PlayerbotAI* botAI) { return new CasterDruidDebuffStrategy(botAI); } + static Strategy* aoe(PlayerbotAI* botAI) { return new DruidAoeStrategy(botAI); } static Strategy* cure(PlayerbotAI* botAI) { return new DruidCureStrategy(botAI); } - static Strategy* melee(PlayerbotAI* botAI) { return new MeleeDruidStrategy(botAI); } static Strategy* buff(PlayerbotAI* botAI) { return new GenericDruidBuffStrategy(botAI); } static Strategy* boost(PlayerbotAI* botAI) { return new DruidBoostStrategy(botAI); } static Strategy* cc(PlayerbotAI* botAI) { return new DruidCcStrategy(botAI); } static Strategy* healer_dps(PlayerbotAI* botAI) { return new DruidHealerDpsStrategy(botAI); } + static Strategy* offheal(PlayerbotAI* botAI) { return new CatOffhealStrategy(botAI); } + static Strategy* blanketing(PlayerbotAI* botAI) { return new DruidBlanketStrategy(botAI); } + static Strategy* tranquility(PlayerbotAI* botAI) { return new DruidTranquilityStrategy(botAI); } + static Strategy* feral_charge(PlayerbotAI* botAI) { return new FeralChargeDruidStrategy(botAI); } }; class DruidDruidStrategyFactoryInternal : public NamedObjectContext @@ -62,18 +61,16 @@ class DruidDruidStrategyFactoryInternal : public NamedObjectContext creators["bear"] = &DruidDruidStrategyFactoryInternal::bear; creators["tank"] = &DruidDruidStrategyFactoryInternal::bear; creators["cat"] = &DruidDruidStrategyFactoryInternal::cat; - creators["caster"] = &DruidDruidStrategyFactoryInternal::caster; + creators["balance"] = &DruidDruidStrategyFactoryInternal::balance; creators["dps"] = &DruidDruidStrategyFactoryInternal::cat; - creators["heal"] = &DruidDruidStrategyFactoryInternal::heal; - creators["offheal"] = &DruidDruidStrategyFactoryInternal::offheal; + creators["resto"] = &DruidDruidStrategyFactoryInternal::heal; } private: - static Strategy* bear(PlayerbotAI* botAI) { return new BearTankDruidStrategy(botAI); } - static Strategy* cat(PlayerbotAI* botAI) { return new CatDpsDruidStrategy(botAI); } - static Strategy* caster(PlayerbotAI* botAI) { return new CasterDruidStrategy(botAI); } - static Strategy* heal(PlayerbotAI* botAI) { return new HealDruidStrategy(botAI); } - static Strategy* offheal(PlayerbotAI* botAI) { return new OffhealDruidCatStrategy(botAI); } + static Strategy* bear(PlayerbotAI* botAI) { return new BearDruidStrategy(botAI); } + static Strategy* cat(PlayerbotAI* botAI) { return new CatDruidStrategy(botAI); } + static Strategy* balance(PlayerbotAI* botAI) { return new BalanceDruidStrategy(botAI); } + static Strategy* heal(PlayerbotAI* botAI) { return new RestoDruidStrategy(botAI); } }; class DruidTriggerFactoryInternal : public NamedObjectContext @@ -81,7 +78,6 @@ class DruidTriggerFactoryInternal : public NamedObjectContext public: DruidTriggerFactoryInternal() { - creators["omen of clarity"] = &DruidTriggerFactoryInternal::omen_of_clarity; creators["clearcasting"] = &DruidTriggerFactoryInternal::clearcasting; creators["thorns"] = &DruidTriggerFactoryInternal::thorns; creators["thorns on party"] = &DruidTriggerFactoryInternal::thorns_on_party; @@ -90,10 +86,12 @@ class DruidTriggerFactoryInternal : public NamedObjectContext creators["faerie fire (feral)"] = &DruidTriggerFactoryInternal::faerie_fire_feral; creators["faerie fire"] = &DruidTriggerFactoryInternal::faerie_fire; creators["insect swarm"] = &DruidTriggerFactoryInternal::insect_swarm; + creators["insect swarm on attacker"] = &DruidTriggerFactoryInternal::insect_swarm_on_attacker; creators["moonfire"] = &DruidTriggerFactoryInternal::moonfire; + creators["moonfire on attacker"] = &DruidTriggerFactoryInternal::moonfire_on_attacker; creators["nature's grasp"] = &DruidTriggerFactoryInternal::natures_grasp; - creators["tiger's fury"] = &DruidTriggerFactoryInternal::tigers_fury; creators["berserk"] = &DruidTriggerFactoryInternal::berserk; + creators["berserk active"] = &DruidTriggerFactoryInternal::berserk_active; creators["savage roar"] = &DruidTriggerFactoryInternal::savage_roar; creators["rake"] = &DruidTriggerFactoryInternal::rake; creators["mark of the wild"] = &DruidTriggerFactoryInternal::mark_of_the_wild; @@ -110,17 +108,34 @@ class DruidTriggerFactoryInternal : public NamedObjectContext creators["eclipse (lunar)"] = &DruidTriggerFactoryInternal::eclipse_lunar; creators["bash on enemy healer"] = &DruidTriggerFactoryInternal::bash_on_enemy_healer; creators["nature's swiftness"] = &DruidTriggerFactoryInternal::natures_swiftness; + creators["nature's swiftness active"] = &DruidTriggerFactoryInternal::natures_swiftness_active; creators["party member remove curse"] = &DruidTriggerFactoryInternal::party_member_remove_curse; - creators["eclipse (solar) cooldown"] = &DruidTriggerFactoryInternal::eclipse_solar_cooldown; - creators["eclipse (lunar) cooldown"] = &DruidTriggerFactoryInternal::eclipse_lunar_cooldown; + creators["mangle (bear)"] = &DruidTriggerFactoryInternal::mangle_bear_trigger; + creators["lacerate"] = &DruidTriggerFactoryInternal::lacerate_trigger; + creators["demoralizing roar"] = &DruidTriggerFactoryInternal::demoralize_roar; creators["mangle (cat)"] = &DruidTriggerFactoryInternal::mangle_cat; creators["ferocious bite time"] = &DruidTriggerFactoryInternal::ferocious_bite_time; + creators["ferocious bite execute"] = &DruidTriggerFactoryInternal::ferocious_bite_execute; creators["hurricane channel check"] = &DruidTriggerFactoryInternal::hurricane_channel_check; creators["no healer dps strategy"] = &DruidTriggerFactoryInternal::no_healer_dps_strategy; + creators["starfall"] = &DruidTriggerFactoryInternal::starfall; + creators["force of nature"] = &DruidTriggerFactoryInternal::force_of_nature; + creators["cyclone"] = &DruidTriggerFactoryInternal::cyclone; + creators["predator's swiftness"] = &DruidTriggerFactoryInternal::predators_swiftness; + creators["predator's swiftness and cyclone"] = &DruidTriggerFactoryInternal::predators_swiftness_and_cyclone; + creators["predator's swiftness and hibernate"] = &DruidTriggerFactoryInternal::predators_swiftness_and_hibernate; + creators["predator's swiftness and entangling roots"] = &DruidTriggerFactoryInternal::predators_swiftness_and_entangling_roots; + creators["predator's swiftness and combat party member dead"] = &DruidTriggerFactoryInternal::predators_swiftness_and_combat_party_member_dead; + creators["clearcasting and medium aoe"] = &DruidTriggerFactoryInternal::clearcasting_and_medium_aoe; + creators["prowl"] = &DruidTriggerFactoryInternal::prowl_trigger; + creators["rejuvenation blanket"] = &DruidTriggerFactoryInternal::rejuvenation_blanket; + creators["wild growth blanket"] = &DruidTriggerFactoryInternal::wild_growth_blanket; + creators["aquatic form"] = &DruidTriggerFactoryInternal::aquatic_form; } private: static Trigger* natures_swiftness(PlayerbotAI* botAI) { return new NaturesSwiftnessTrigger(botAI); } + static Trigger* natures_swiftness_active(PlayerbotAI* botAI) { return new NaturesSwiftnessActiveTrigger(botAI); } static Trigger* clearcasting(PlayerbotAI* botAI) { return new ClearcastingTrigger(botAI); } static Trigger* eclipse_solar(PlayerbotAI* botAI) { return new EclipseSolarTrigger(botAI); } static Trigger* eclipse_lunar(PlayerbotAI* botAI) { return new EclipseLunarTrigger(botAI); } @@ -130,11 +145,13 @@ class DruidTriggerFactoryInternal : public NamedObjectContext static Trigger* bash(PlayerbotAI* botAI) { return new BashInterruptSpellTrigger(botAI); } static Trigger* faerie_fire_feral(PlayerbotAI* botAI) { return new FaerieFireFeralTrigger(botAI); } static Trigger* insect_swarm(PlayerbotAI* botAI) { return new InsectSwarmTrigger(botAI); } + static Trigger* insect_swarm_on_attacker(PlayerbotAI* botAI) { return new InsectSwarmOnAttackerTrigger(botAI); } static Trigger* moonfire(PlayerbotAI* botAI) { return new MoonfireTrigger(botAI); } + static Trigger* moonfire_on_attacker(PlayerbotAI* botAI) { return new MoonfireOnAttackerTrigger(botAI); } static Trigger* faerie_fire(PlayerbotAI* botAI) { return new FaerieFireTrigger(botAI); } static Trigger* natures_grasp(PlayerbotAI* botAI) { return new NaturesGraspTrigger(botAI); } - static Trigger* tigers_fury(PlayerbotAI* botAI) { return new TigersFuryTrigger(botAI); } static Trigger* berserk(PlayerbotAI* botAI) { return new BerserkTrigger(botAI); } + static Trigger* berserk_active(PlayerbotAI* botAI) { return new BerserkActiveTrigger(botAI); } static Trigger* savage_roar(PlayerbotAI* botAI) { return new SavageRoarTrigger(botAI); } static Trigger* rake(PlayerbotAI* botAI) { return new RakeTrigger(botAI); } static Trigger* mark_of_the_wild(PlayerbotAI* botAI) { return new MarkOfTheWildTrigger(botAI); } @@ -148,14 +165,28 @@ class DruidTriggerFactoryInternal : public NamedObjectContext static Trigger* cat_form(PlayerbotAI* botAI) { return new CatFormTrigger(botAI); } static Trigger* tree_form(PlayerbotAI* botAI) { return new TreeFormTrigger(botAI); } static Trigger* bash_on_enemy_healer(PlayerbotAI* botAI) { return new BashInterruptEnemyHealerSpellTrigger(botAI); } - static Trigger* omen_of_clarity(PlayerbotAI* botAI) { return new OmenOfClarityTrigger(botAI); } static Trigger* party_member_remove_curse(PlayerbotAI* ai) { return new DruidPartyMemberRemoveCurseTrigger(ai); } - static Trigger* eclipse_solar_cooldown(PlayerbotAI* ai) { return new EclipseSolarCooldownTrigger(ai); } - static Trigger* eclipse_lunar_cooldown(PlayerbotAI* ai) { return new EclipseLunarCooldownTrigger(ai); } + static Trigger* mangle_bear_trigger(PlayerbotAI* botAI) { return new MangleBearTrigger(botAI); } + static Trigger* lacerate_trigger(PlayerbotAI* botAI) { return new LacerateTrigger(botAI); } + static Trigger* demoralize_roar(PlayerbotAI* botAI) { return new DemoralizeRoarTrigger(botAI); } static Trigger* mangle_cat(PlayerbotAI* ai) { return new MangleCatTrigger(ai); } static Trigger* ferocious_bite_time(PlayerbotAI* ai) { return new FerociousBiteTimeTrigger(ai); } + static Trigger* ferocious_bite_execute(PlayerbotAI* ai) { return new FerociousBiteExecuteTrigger(ai); } static Trigger* hurricane_channel_check(PlayerbotAI* ai) { return new HurricaneChannelCheckTrigger(ai); } static Trigger* no_healer_dps_strategy(PlayerbotAI* ai) { return new NoHealerDpsStrategyTrigger(ai); } + static Trigger* starfall(PlayerbotAI* ai) { return new StarfallTrigger(ai); } + static Trigger* force_of_nature(PlayerbotAI* ai) { return new ForceOfNatureTrigger(ai); } + static Trigger* cyclone(PlayerbotAI* ai) { return new CycloneTrigger(ai); } + static Trigger* predators_swiftness(PlayerbotAI* ai) { return new PredatorsSwiftnessTrigger(ai); } + static Trigger* predators_swiftness_and_cyclone(PlayerbotAI* ai) { return new TwoTriggers(ai, "predator's swiftness", "cyclone"); } + static Trigger* predators_swiftness_and_hibernate(PlayerbotAI* ai) { return new TwoTriggers(ai, "predator's swiftness", "hibernate"); } + static Trigger* predators_swiftness_and_entangling_roots(PlayerbotAI* ai) { return new TwoTriggers(ai, "predator's swiftness", "entangling roots"); } + static Trigger* predators_swiftness_and_combat_party_member_dead(PlayerbotAI* ai) { return new TwoTriggers(ai, "predator's swiftness", "combat party member dead"); } + static Trigger* clearcasting_and_medium_aoe(PlayerbotAI* ai) { return new TwoTriggers(ai, "clearcasting", "medium aoe"); } + static Trigger* prowl_trigger(PlayerbotAI* ai) { return new ProwlTrigger(ai); } + static Trigger* rejuvenation_blanket(PlayerbotAI* ai) { return new BuffOnPartyTrigger(ai, "rejuvenation"); } + static Trigger* wild_growth_blanket(PlayerbotAI* ai) { return new BuffOnPartyTrigger(ai, "wild growth"); } + static Trigger* aquatic_form(PlayerbotAI* ai) { return new AquaticFormTrigger(ai); } }; class DruidAiObjectContextInternal : public NamedObjectContext @@ -193,8 +224,8 @@ class DruidAiObjectContextInternal : public NamedObjectContext creators["hibernate"] = &DruidAiObjectContextInternal::hibernate; creators["entangling roots"] = &DruidAiObjectContextInternal::entangling_roots; creators["entangling roots on cc"] = &DruidAiObjectContextInternal::entangling_roots_on_cc; - creators["hibernate"] = &DruidAiObjectContextInternal::hibernate; creators["hibernate on cc"] = &DruidAiObjectContextInternal::hibernate_on_cc; + creators["cyclone on cc"] = &DruidAiObjectContextInternal::cyclone_on_cc; creators["wrath"] = &DruidAiObjectContextInternal::wrath; creators["starfall"] = &DruidAiObjectContextInternal::starfall; creators["insect swarm"] = &DruidAiObjectContextInternal::insect_swarm; @@ -205,9 +236,9 @@ class DruidAiObjectContextInternal : public NamedObjectContext creators["mangle (cat)"] = &DruidAiObjectContextInternal::mangle_cat; creators["swipe (cat)"] = &DruidAiObjectContextInternal::swipe_cat; creators["rake"] = &DruidAiObjectContextInternal::rake; - creators["rake on attacker"] = &DruidAiObjectContextInternal::rake_on_attacker; creators["ferocious bite"] = &DruidAiObjectContextInternal::ferocious_bite; creators["rip"] = &DruidAiObjectContextInternal::rip; + creators["maim"] = &DruidAiObjectContextInternal::maim; creators["cower"] = &DruidAiObjectContextInternal::cower; creators["survival instincts"] = &DruidAiObjectContextInternal::survival_instincts; creators["frenzied regeneration"] = &DruidAiObjectContextInternal::frenzied_regeneration; @@ -237,9 +268,9 @@ class DruidAiObjectContextInternal : public NamedObjectContext creators["lacerate"] = &DruidAiObjectContextInternal::lacerate; creators["hurricane"] = &DruidAiObjectContextInternal::hurricane; creators["innervate"] = &DruidAiObjectContextInternal::innervate; + creators["innervate on healer"] = &DruidAiObjectContextInternal::innervate_on_healer; creators["tranquility"] = &DruidAiObjectContextInternal::tranquility; creators["bash on enemy healer"] = &DruidAiObjectContextInternal::bash_on_enemy_healer; - creators["omen of clarity"] = &DruidAiObjectContextInternal::omen_of_clarity; creators["nature's swiftness"] = &DruidAiObjectContextInternal::natures_swiftness; creators["prowl"] = &DruidAiObjectContextInternal::prowl; creators["dash"] = &DruidAiObjectContextInternal::dash; @@ -254,11 +285,13 @@ class DruidAiObjectContextInternal : public NamedObjectContext creators["moonfire on attacker"] = &DruidAiObjectContextInternal::moonfire_on_attacker; creators["enrage"] = &DruidAiObjectContextInternal::enrage; creators["force of nature"] = &DruidAiObjectContextInternal::force_of_nature; + creators["typhoon"] = &DruidAiObjectContextInternal::typhoon; + creators["rejuvenation blanket"] = &DruidAiObjectContextInternal::rejuvenation_blanket; + creators["wild growth blanket"] = &DruidAiObjectContextInternal::wild_growth_blanket; } private: static Action* natures_swiftness(PlayerbotAI* botAI) { return new CastNaturesSwiftnessAction(botAI); } - static Action* omen_of_clarity(PlayerbotAI* botAI) { return new CastOmenOfClarityAction(botAI); } static Action* tranquility(PlayerbotAI* botAI) { return new CastTranquilityAction(botAI); } static Action* feral_charge_bear(PlayerbotAI* botAI) { return new CastFeralChargeBearAction(botAI); } static Action* feral_charge_cat(PlayerbotAI* botAI) { return new CastFeralChargeCatAction(botAI); } @@ -291,6 +324,7 @@ class DruidAiObjectContextInternal : public NamedObjectContext static Action* entangling_roots(PlayerbotAI* botAI) { return new CastEntanglingRootsAction(botAI); } static Action* hibernate_on_cc(PlayerbotAI* botAI) { return new CastHibernateCcAction(botAI); } static Action* entangling_roots_on_cc(PlayerbotAI* botAI) { return new CastEntanglingRootsCcAction(botAI); } + static Action* cyclone_on_cc(PlayerbotAI* botAI) { return new CastCycloneCcAction(botAI); } static Action* wrath(PlayerbotAI* botAI) { return new CastWrathAction(botAI); } static Action* starfall(PlayerbotAI* botAI) { return new CastStarfallAction(botAI); } static Action* insect_swarm(PlayerbotAI* botAI) { return new CastInsectSwarmAction(botAI); } @@ -301,9 +335,9 @@ class DruidAiObjectContextInternal : public NamedObjectContext static Action* mangle_cat(PlayerbotAI* botAI) { return new CastMangleCatAction(botAI); } static Action* swipe_cat(PlayerbotAI* botAI) { return new CastSwipeCatAction(botAI); } static Action* rake(PlayerbotAI* botAI) { return new CastRakeAction(botAI); } - static Action* rake_on_attacker(PlayerbotAI* botAI) { return new CastRakeOnMeleeAttackersAction(botAI); } static Action* ferocious_bite(PlayerbotAI* botAI) { return new CastFerociousBiteAction(botAI); } static Action* rip(PlayerbotAI* botAI) { return new CastRipAction(botAI); } + static Action* maim(PlayerbotAI* botAI) { return new CastMaimAction(botAI); } static Action* cower(PlayerbotAI* botAI) { return new CastCowerAction(botAI); } static Action* survival_instincts(PlayerbotAI* botAI) { return new CastSurvivalInstinctsAction(botAI); } static Action* frenzied_regeneration(PlayerbotAI* botAI) { return new CastFrenziedRegenerationAction(botAI); } @@ -333,6 +367,7 @@ class DruidAiObjectContextInternal : public NamedObjectContext static Action* lacerate(PlayerbotAI* botAI) { return new CastLacerateAction(botAI); } static Action* hurricane(PlayerbotAI* botAI) { return new CastHurricaneAction(botAI); } static Action* innervate(PlayerbotAI* botAI) { return new CastInnervateAction(botAI); } + static Action* innervate_on_healer(PlayerbotAI* botAI) { return new CastInnervateOnHealerAction(botAI); } static Action* bash_on_enemy_healer(PlayerbotAI* botAI) { return new CastBashOnEnemyHealerAction(botAI); } static Action* ravage(PlayerbotAI* botAI) { return new CastRavageAction(botAI); } static Action* pounce(PlayerbotAI* botAI) { return new CastPounceAction(botAI); } @@ -347,6 +382,9 @@ class DruidAiObjectContextInternal : public NamedObjectContext static Action* moonfire_on_attacker(PlayerbotAI* ai) { return new CastMoonfireOnAttackerAction(ai); } static Action* enrage(PlayerbotAI* ai) { return new CastEnrageAction(ai); } static Action* force_of_nature(PlayerbotAI* ai) { return new CastForceOfNatureAction(ai); } + static Action* typhoon(PlayerbotAI* ai) { return new CastTyphoonAction(ai); } + static Action* rejuvenation_blanket(PlayerbotAI* ai) { return new CastRejuvenationBlanketAction(ai); } + static Action* wild_growth_blanket(PlayerbotAI* ai) { return new CastWildGrowthBlanketAction(ai); } }; SharedNamedObjectContextList DruidAiObjectContext::sharedStrategyContexts; @@ -386,7 +424,22 @@ void DruidAiObjectContext::BuildSharedTriggerContexts(SharedNamedObjectContextLi triggerContexts.Add(new DruidTriggerFactoryInternal()); } +class DruidValueContextInternal : public NamedObjectContext +{ +public: + DruidValueContextInternal() + { + creators["eclipse solar proc time"] = &DruidValueContextInternal::eclipse_solar_proc_time; + creators["eclipse lunar proc time"] = &DruidValueContextInternal::eclipse_lunar_proc_time; + } + +private: + static UntypedValue* eclipse_solar_proc_time(PlayerbotAI* botAI) { return new EclipseSolarProcTimeValue(botAI); } + static UntypedValue* eclipse_lunar_proc_time(PlayerbotAI* botAI) { return new EclipseLunarProcTimeValue(botAI); } +}; + void DruidAiObjectContext::BuildSharedValueContexts(SharedNamedObjectContextList& valueContexts) { AiObjectContext::BuildSharedValueContexts(valueContexts); + valueContexts.Add(new DruidValueContextInternal()); } diff --git a/src/Ai/Class/Druid/Strategy/CasterDruidStrategy.cpp b/src/Ai/Class/Druid/Strategy/BalanceDruidStrategy.cpp similarity index 52% rename from src/Ai/Class/Druid/Strategy/CasterDruidStrategy.cpp rename to src/Ai/Class/Druid/Strategy/BalanceDruidStrategy.cpp index a91f3b54090..f3fbc2218f7 100644 --- a/src/Ai/Class/Druid/Strategy/CasterDruidStrategy.cpp +++ b/src/Ai/Class/Druid/Strategy/BalanceDruidStrategy.cpp @@ -3,15 +3,15 @@ * and/or modify it under version 3 of the License, or (at your option), any later version. */ -#include "CasterDruidStrategy.h" +#include "BalanceDruidStrategy.h" #include "AiObjectContext.h" #include "FeralDruidStrategy.h" -class CasterDruidStrategyActionNodeFactory : public NamedObjectFactory +class BalanceDruidStrategyActionNodeFactory : public NamedObjectFactory { public: - CasterDruidStrategyActionNodeFactory() + BalanceDruidStrategyActionNodeFactory() { creators["faerie fire"] = &faerie_fire; creators["hibernate"] = &hibernate; @@ -23,6 +23,10 @@ class CasterDruidStrategyActionNodeFactory : public NamedObjectFactory CasterDruidStrategy::getDefaultActions() +std::vector BalanceDruidStrategy::getDefaultActions() { return { - NextAction("starfall", ACTION_HIGH + 1.0f), - NextAction("force of nature", ACTION_DEFAULT + 1.0f), - NextAction("wrath", ACTION_DEFAULT + 0.1f), + NextAction("starfire", 5.4f), + NextAction("wrath", 5.3f), }; } -void CasterDruidStrategy::InitTriggers(std::vector& triggers) +void BalanceDruidStrategy::InitTriggers(std::vector& triggers) { GenericDruidStrategy::InitTriggers(triggers); - triggers.push_back( - new TriggerNode( - "eclipse (lunar) cooldown", - { - NextAction("starfire", ACTION_DEFAULT + 0.2f) - } - ) - ); - triggers.push_back( - new TriggerNode( - "eclipse (solar) cooldown", - { - NextAction("wrath", ACTION_DEFAULT + 0.2f) - } - ) - ); - triggers.push_back( - new TriggerNode( - "insect swarm", - { - NextAction("insect swarm", ACTION_NORMAL + 5) - } - ) - ); - triggers.push_back( - new TriggerNode( - "moonfire", - { - NextAction("moonfire", ACTION_NORMAL + 4) - } - ) - ); - triggers.push_back( - new TriggerNode( - "eclipse (solar)", - { - NextAction("wrath", ACTION_NORMAL + 6) - } - ) - ); - triggers.push_back( - new TriggerNode( - "eclipse (lunar)", - { - NextAction("starfire", ACTION_NORMAL + 6) - } - ) - ); - triggers.push_back( - new TriggerNode( - "medium mana", - { - NextAction("innervate", ACTION_HIGH + 9) - } - ) - ); - triggers.push_back( - new TriggerNode( - "enemy too close for spell", - { - NextAction("flee", ACTION_MOVE + 9) - } - ) - ); -} + // Debuffs and DoTs + triggers.push_back(new TriggerNode("faerie fire", { NextAction("faerie fire", 29.5f) })); + triggers.push_back(new TriggerNode("insect swarm", { NextAction("insect swarm", 18.0f) })); + triggers.push_back(new TriggerNode("moonfire", { NextAction("moonfire", 17.5f) })); -void CasterDruidAoeStrategy::InitTriggers(std::vector& triggers) -{ - triggers.push_back( - new TriggerNode( - "hurricane channel check", - { - NextAction("cancel channel", ACTION_HIGH + 2) - } - ) - ); - triggers.push_back( - new TriggerNode( - "medium aoe", - { - NextAction("hurricane", ACTION_HIGH + 1) - } - ) - ); - triggers.push_back( - new TriggerNode( - "light aoe", - { - NextAction("insect swarm on attacker", ACTION_NORMAL + 3), - NextAction("moonfire on attacker", ACTION_NORMAL + 3) - } - ) - ); -} + // Eclipse procs + triggers.push_back(new TriggerNode("eclipse (solar)", { NextAction("wrath", 20.0f) })); + triggers.push_back(new TriggerNode("eclipse (lunar)", { NextAction("starfire", 20.0f) })); -void CasterDruidDebuffStrategy::InitTriggers(std::vector& triggers) -{ - triggers.push_back( - new TriggerNode( - "faerie fire", - { - NextAction("faerie fire", ACTION_HIGH) - } - ) - ); + // Utility/Defensive + triggers.push_back(new TriggerNode("medium mana", { NextAction("innervate", 29.0f) })); + triggers.push_back(new TriggerNode("enemy too close for spell", { NextAction("flee", 39.0f) })); } diff --git a/src/Ai/Class/Druid/Strategy/BalanceDruidStrategy.h b/src/Ai/Class/Druid/Strategy/BalanceDruidStrategy.h new file mode 100644 index 00000000000..d01db490108 --- /dev/null +++ b/src/Ai/Class/Druid/Strategy/BalanceDruidStrategy.h @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#ifndef _PLAYERBOT_BALANCEDRUIDSTRATEGY_H +#define _PLAYERBOT_BALANCEDRUIDSTRATEGY_H + +#include "GenericDruidStrategy.h" + +class PlayerbotAI; + +class BalanceDruidStrategy : public GenericDruidStrategy +{ +public: + BalanceDruidStrategy(PlayerbotAI* botAI); + +public: + void InitTriggers(std::vector& triggers) override; + std::string const getName() override { return "balance"; } + std::vector getDefaultActions() override; + uint32 GetType() const override { return STRATEGY_TYPE_COMBAT | STRATEGY_TYPE_DPS | STRATEGY_TYPE_RANGED; } +}; + +#endif diff --git a/src/Ai/Class/Druid/Strategy/BearTankDruidStrategy.cpp b/src/Ai/Class/Druid/Strategy/BearDruidStrategy.cpp similarity index 59% rename from src/Ai/Class/Druid/Strategy/BearTankDruidStrategy.cpp rename to src/Ai/Class/Druid/Strategy/BearDruidStrategy.cpp index 13af635c3d3..418f057cd67 100644 --- a/src/Ai/Class/Druid/Strategy/BearTankDruidStrategy.cpp +++ b/src/Ai/Class/Druid/Strategy/BearDruidStrategy.cpp @@ -3,19 +3,17 @@ * and/or modify it under version 3 of the License, or (at your option), any later version. */ -#include "BearTankDruidStrategy.h" +#include "BearDruidStrategy.h" #include "Playerbots.h" -class BearTankDruidStrategyActionNodeFactory : public NamedObjectFactory +class BearDruidStrategyActionNodeFactory : public NamedObjectFactory { public: - BearTankDruidStrategyActionNodeFactory() + BearDruidStrategyActionNodeFactory() { - creators["melee"] = &melee; creators["feral charge - bear"] = &feral_charge_bear; creators["swipe (bear)"] = &swipe_bear; - creators["faerie fire (feral)"] = &faerie_fire_feral; creators["bear form"] = &bear_form; creators["dire bear form"] = &dire_bear_form; creators["mangle (bear)"] = &mangle_bear; @@ -28,16 +26,6 @@ class BearTankDruidStrategyActionNodeFactory : public NamedObjectFactory BearTankDruidStrategy::getDefaultActions() +std::vector BearDruidStrategy::getDefaultActions() { return { - NextAction("mangle (bear)", ACTION_DEFAULT + 0.5f), - NextAction("faerie fire (feral)", ACTION_DEFAULT + 0.4f), - NextAction("lacerate", ACTION_DEFAULT + 0.3f), - NextAction("maul", ACTION_DEFAULT + 0.2f), - NextAction("enrage", ACTION_DEFAULT + 0.1f), - NextAction("melee", ACTION_DEFAULT) + NextAction("maul", 5.2f), + NextAction("enrage", 5.1f), + NextAction("melee", 5.0f) }; } -void BearTankDruidStrategy::InitTriggers(std::vector& triggers) +void BearDruidStrategy::InitTriggers(std::vector& triggers) { FeralDruidStrategy::InitTriggers(triggers); - triggers.push_back( - new TriggerNode( - "enemy out of melee", - { - NextAction("feral charge - bear", ACTION_NORMAL + 8) - } - ) - ); triggers.push_back( new TriggerNode( "bear form", - { - NextAction("dire bear form", ACTION_HIGH + 8) - } + { NextAction("dire bear form", 28.0f) } ) ); triggers.push_back( new TriggerNode( - "low health", - { - NextAction("frenzied regeneration", ACTION_HIGH + 7) - } - ) - ); - triggers.push_back( - new TriggerNode( - "faerie fire (feral)", - { - NextAction("faerie fire (feral)", ACTION_HIGH + 7) - } - ) - ); - triggers.push_back(new TriggerNode("high aoe", {NextAction("challenging roar", ACTION_HIGH + 8)})); - triggers.push_back( - new TriggerNode( - "lose aggro", - { - NextAction("growl", ACTION_HIGH + 8) - } + "medium health", + { NextAction("frenzied regeneration", 27.0f) } ) ); + triggers.push_back(new TriggerNode( + "mangle (bear)", { NextAction("mangle (bear)", 17.5f) } + )); + triggers.push_back(new TriggerNode( + "faerie fire (feral)", { NextAction("faerie fire (feral)", 17.0f) } + )); + triggers.push_back(new TriggerNode( + "lacerate", { NextAction("lacerate", 16.0f) } + )); + triggers.push_back(new TriggerNode( + "demoralizing roar", { NextAction("demoralizing roar", 15.5f) } + )); + triggers.push_back(new TriggerNode("high aoe", { NextAction("challenging roar", 26.5f) })); + triggers.push_back(new TriggerNode("lose aggro", + { + NextAction("growl", 26.0f), + NextAction("faerie fire (feral)", 25.5f) + } + )); + triggers.push_back(new TriggerNode("berserk active", { NextAction("mangle (bear)", 25.0f) })); triggers.push_back( new TriggerNode( "medium aoe", { - NextAction("demoralizing roar", ACTION_HIGH + 6), - NextAction("swipe (bear)", ACTION_HIGH + 6) + NextAction("demoralizing roar", 24.5f), + NextAction("swipe (bear)", 24.0f) } ) ); triggers.push_back( new TriggerNode( "light aoe", - { - NextAction("swipe (bear)", ACTION_HIGH + 5) - } + { NextAction("swipe (bear)", 24.0f) } ) ); triggers.push_back( new TriggerNode( "bash", - { - NextAction("bash", ACTION_INTERRUPT + 2) - } + { NextAction("bash", 42.0f) } ) ); triggers.push_back( new TriggerNode( "bash on enemy healer", - { - NextAction("bash on enemy healer", ACTION_INTERRUPT + 1) - } + { NextAction("bash on enemy healer", 41.0f) } ) ); } diff --git a/src/Ai/Class/Druid/Strategy/BearTankDruidStrategy.h b/src/Ai/Class/Druid/Strategy/BearDruidStrategy.h similarity index 75% rename from src/Ai/Class/Druid/Strategy/BearTankDruidStrategy.h rename to src/Ai/Class/Druid/Strategy/BearDruidStrategy.h index 2019bd0eba8..1e47fd08afb 100644 --- a/src/Ai/Class/Druid/Strategy/BearTankDruidStrategy.h +++ b/src/Ai/Class/Druid/Strategy/BearDruidStrategy.h @@ -3,17 +3,17 @@ * and/or modify it under version 3 of the License, or (at your option), any later version. */ -#ifndef _PLAYERBOT_BEARTANKDRUIDSTRATEGY_H -#define _PLAYERBOT_BEARTANKDRUIDSTRATEGY_H +#ifndef _PLAYERBOT_BEARDRUIDSTRATEGY_H +#define _PLAYERBOT_BEARDRUIDSTRATEGY_H #include "FeralDruidStrategy.h" class PlayerbotAI; -class BearTankDruidStrategy : public FeralDruidStrategy +class BearDruidStrategy : public FeralDruidStrategy { public: - BearTankDruidStrategy(PlayerbotAI* botAI); + BearDruidStrategy(PlayerbotAI* botAI); void InitTriggers(std::vector& triggers) override; std::string const getName() override { return "bear"; } diff --git a/src/Ai/Class/Druid/Strategy/CasterDruidStrategy.h b/src/Ai/Class/Druid/Strategy/CasterDruidStrategy.h deleted file mode 100644 index 45bc78dba80..00000000000 --- a/src/Ai/Class/Druid/Strategy/CasterDruidStrategy.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it - * and/or modify it under version 3 of the License, or (at your option), any later version. - */ - -#ifndef _PLAYERBOT_CASTERDRUIDSTRATEGY_H -#define _PLAYERBOT_CASTERDRUIDSTRATEGY_H - -#include "GenericDruidStrategy.h" - -class PlayerbotAI; - -class CasterDruidStrategy : public GenericDruidStrategy -{ -public: - CasterDruidStrategy(PlayerbotAI* botAI); - -public: - void InitTriggers(std::vector& triggers) override; - std::string const getName() override { return "caster"; } - std::vector getDefaultActions() override; - uint32 GetType() const override { return STRATEGY_TYPE_COMBAT | STRATEGY_TYPE_DPS | STRATEGY_TYPE_RANGED; } -}; - -class CasterDruidAoeStrategy : public CombatStrategy -{ -public: - CasterDruidAoeStrategy(PlayerbotAI* botAI) : CombatStrategy(botAI) {} - -public: - void InitTriggers(std::vector& triggers) override; - std::string const getName() override { return "caster aoe"; } -}; - -class CasterDruidDebuffStrategy : public CombatStrategy -{ -public: - CasterDruidDebuffStrategy(PlayerbotAI* botAI) : CombatStrategy(botAI) {} - -public: - void InitTriggers(std::vector& triggers) override; - std::string const getName() override { return "caster debuff"; } -}; - -#endif diff --git a/src/Ai/Class/Druid/Strategy/CatDpsDruidStrategy.cpp b/src/Ai/Class/Druid/Strategy/CatDpsDruidStrategy.cpp deleted file mode 100644 index f7a76b0fc8c..00000000000 --- a/src/Ai/Class/Druid/Strategy/CatDpsDruidStrategy.cpp +++ /dev/null @@ -1,314 +0,0 @@ -/* - * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it - * and/or modify it under version 3 of the License, or (at your option), any later version. - */ - -#include "CatDpsDruidStrategy.h" - -#include "AiObjectContext.h" - -class CatDpsDruidStrategyActionNodeFactory : public NamedObjectFactory -{ -public: - CatDpsDruidStrategyActionNodeFactory() - { - creators["faerie fire (feral)"] = &faerie_fire_feral; - creators["melee"] = &melee; - creators["feral charge - cat"] = &feral_charge_cat; - creators["cat form"] = &cat_form; - creators["claw"] = &claw; - creators["mangle (cat)"] = &mangle_cat; - creators["rake"] = &rake; - creators["ferocious bite"] = &ferocious_bite; - creators["rip"] = &rip; - creators["pounce"] = &pounce; - creators["ravage"] = &ravage; - } - -private: - static ActionNode* faerie_fire_feral([[maybe_unused]] PlayerbotAI* botAI) - { - return new ActionNode( - "faerie fire (feral)", - /*P*/ {}, - /*A*/ {}, - /*C*/ {} - ); - } - - static ActionNode* melee([[maybe_unused]] PlayerbotAI* botAI) - { - return new ActionNode( - "melee", - /*P*/ { NextAction("feral charge - cat") }, - /*A*/ {}, - /*C*/ {} - ); - } - - static ActionNode* feral_charge_cat([[maybe_unused]] PlayerbotAI* botAI) - { - return new ActionNode( - "feral charge - cat", - /*P*/ {}, - /*A*/ { NextAction("reach melee") }, - /*C*/ {} - ); - } - - static ActionNode* cat_form([[maybe_unused]] PlayerbotAI* botAI) - { - return new ActionNode( - "cat form", - /*P*/ { NextAction("caster form") }, - /*A*/ { NextAction("bear form") }, - /*C*/ {} - ); - } - - static ActionNode* claw([[maybe_unused]] PlayerbotAI* botAI) - { - return new ActionNode( - "claw", - /*P*/ {}, - /*A*/ { NextAction("melee") }, - /*C*/ {} - ); - } - - static ActionNode* mangle_cat([[maybe_unused]] PlayerbotAI* botAI) - { - return new ActionNode( - "mangle (cat)", - /*P*/ {}, - /*A*/ {}, - /*C*/ {} - ); - } - - static ActionNode* rake([[maybe_unused]] PlayerbotAI* botAI) - { - return new ActionNode( - "rake", - /*P*/ {}, - /*A*/ {}, - /*C*/ {} - ); - } - - static ActionNode* ferocious_bite([[maybe_unused]] PlayerbotAI* botAI) - { - return new ActionNode( - "ferocious bite", - /*P*/ {}, - /*A*/ { NextAction("rip") }, - /*C*/ {} - ); - } - - static ActionNode* rip([[maybe_unused]] PlayerbotAI* botAI) - { - return new ActionNode( - "rip", - /*P*/ {}, - /*A*/ {}, - /*C*/ {} - ); - } - - static ActionNode* pounce([[maybe_unused]] PlayerbotAI* botAI) - { - return new ActionNode( - "pounce", - /*P*/ {}, - /*A*/ { NextAction("ravage") }, - /*C*/ {} - ); - } - - static ActionNode* ravage([[maybe_unused]] PlayerbotAI* botAI) - { - return new ActionNode( - "ravage", - /*P*/ {}, - /*A*/ { NextAction("shred") }, - /*C*/ {} - ); - } -}; - -CatDpsDruidStrategy::CatDpsDruidStrategy(PlayerbotAI* botAI) : FeralDruidStrategy(botAI) -{ - actionNodeFactories.Add(new CatDpsDruidStrategyActionNodeFactory()); -} - -std::vector CatDpsDruidStrategy::getDefaultActions() -{ - return { - NextAction("tiger's fury", ACTION_DEFAULT + 0.1f) - }; -} - -void CatDpsDruidStrategy::InitTriggers(std::vector& triggers) -{ - FeralDruidStrategy::InitTriggers(triggers); - - // Default priority - triggers.push_back( - new TriggerNode( - "almost full energy available", - { - NextAction("shred", ACTION_DEFAULT + 0.4f) - } - ) - ); - triggers.push_back( - new TriggerNode( - "combo points not full", - { - NextAction("shred", ACTION_DEFAULT + 0.4f) - } - ) - ); - triggers.push_back( - new TriggerNode( - "almost full energy available", - { - NextAction("mangle (cat)", ACTION_DEFAULT + 0.3f) - } - ) - ); - triggers.push_back( - new TriggerNode( - "combo points not full and high energy", - { - NextAction("mangle (cat)", ACTION_DEFAULT + 0.3f) - } - ) - ); - triggers.push_back( - new TriggerNode( - "almost full energy available", - { - NextAction("claw", ACTION_DEFAULT + 0.2f) - } - ) - ); - triggers.push_back( - new TriggerNode( - "combo points not full and high energy", - { - NextAction("claw", ACTION_DEFAULT + 0.2f) - } - ) - ); - triggers.push_back( - new TriggerNode( - "faerie fire (feral)", - { - NextAction("faerie fire (feral)", ACTION_DEFAULT + 0.0f) - } - ) - ); - - // Main spell - triggers.push_back( - new TriggerNode( - "cat form", { - NextAction("cat form", ACTION_HIGH + 8) - } - ) - ); - triggers.push_back( - new TriggerNode( - "savage roar", { - NextAction("savage roar", ACTION_HIGH + 7) - } - ) - ); - triggers.push_back( - new TriggerNode( - "combo points 5 available", - { - NextAction("rip", ACTION_HIGH + 6) - } - ) - ); - triggers.push_back( - new TriggerNode( - "ferocious bite time", - { - NextAction("ferocious bite", ACTION_HIGH + 5) - } - ) - ); - triggers.push_back( - new TriggerNode( - "target with combo points almost dead", - { - NextAction("ferocious bite", ACTION_HIGH + 4) - } - ) - ); - triggers.push_back( - new TriggerNode( - "mangle (cat)", - { - NextAction("mangle (cat)", ACTION_HIGH + 3) - } - ) - ); - triggers.push_back( - new TriggerNode( - "rake", - { - NextAction("rake", ACTION_HIGH + 2) - } - ) - ); - triggers.push_back( - new TriggerNode( - "medium threat", - { - NextAction("cower", ACTION_HIGH + 1) - } - ) - ); - - // AOE - triggers.push_back( - new TriggerNode( - "medium aoe", - { - NextAction("swipe (cat)", ACTION_HIGH + 3) - } - ) - ); - triggers.push_back( - new TriggerNode( - "light aoe", - { - NextAction("rake on attacker", ACTION_HIGH + 2) - } - ) - ); - // Reach target - triggers.push_back( - new TriggerNode( - "enemy out of melee", - { - NextAction("feral charge - cat", ACTION_HIGH + 9) - } - ) - ); - triggers.push_back( - new TriggerNode( - "enemy out of melee", - { - NextAction("dash", ACTION_HIGH + 8) - } - ) - ); -} - -void CatAoeDruidStrategy::InitTriggers(std::vector& /*triggers*/) {} diff --git a/src/Ai/Class/Druid/Strategy/CatDruidStrategy.cpp b/src/Ai/Class/Druid/Strategy/CatDruidStrategy.cpp new file mode 100644 index 00000000000..df6ba4a927c --- /dev/null +++ b/src/Ai/Class/Druid/Strategy/CatDruidStrategy.cpp @@ -0,0 +1,446 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#include "CatDruidStrategy.h" + +#include "AiObjectContext.h" + +class CatDruidStrategyActionNodeFactory : public NamedObjectFactory +{ +public: + CatDruidStrategyActionNodeFactory() + { + creators["faerie fire (feral)"] = &faerie_fire_feral; + creators["melee"] = &melee; + creators["feral charge - cat"] = &feral_charge_cat; + creators["cat form"] = &cat_form; + creators["claw"] = &claw; + creators["mangle (cat)"] = &mangle_cat; + creators["rake"] = &rake; + creators["ferocious bite"] = &ferocious_bite; + creators["rip"] = &rip; + creators["pounce"] = &pounce; + creators["ravage"] = &ravage; + creators["prowl"] = &prowl; + } + +private: + static ActionNode* faerie_fire_feral([[maybe_unused]] PlayerbotAI* botAI) + { + return new ActionNode( + "faerie fire (feral)", + /*P*/ {}, + /*A*/ {}, + /*C*/ {} + ); + } + + static ActionNode* melee([[maybe_unused]] PlayerbotAI* botAI) + { + return new ActionNode( + "melee", + /*P*/ {}, + /*A*/ {}, + /*C*/ {} + ); + } + + static ActionNode* feral_charge_cat([[maybe_unused]] PlayerbotAI* botAI) + { + return new ActionNode( + "feral charge - cat", + /*P*/ {}, + /*A*/ { NextAction("reach melee") }, + /*C*/ {} + ); + } + + static ActionNode* cat_form([[maybe_unused]] PlayerbotAI* botAI) + { + return new ActionNode( + "cat form", + /*P*/ { NextAction("caster form") }, + /*A*/ { NextAction("bear form") }, + /*C*/ {} + ); + } + + static ActionNode* claw([[maybe_unused]] PlayerbotAI* botAI) + { + return new ActionNode( + "claw", + /*P*/ {}, + /*A*/ { NextAction("melee") }, + /*C*/ {} + ); + } + + static ActionNode* mangle_cat([[maybe_unused]] PlayerbotAI* botAI) + { + return new ActionNode( + "mangle (cat)", + /*P*/ {}, + /*A*/ {}, + /*C*/ {} + ); + } + + static ActionNode* rake([[maybe_unused]] PlayerbotAI* botAI) + { + return new ActionNode( + "rake", + /*P*/ {}, + /*A*/ {}, + /*C*/ {} + ); + } + + static ActionNode* ferocious_bite([[maybe_unused]] PlayerbotAI* botAI) + { + return new ActionNode( + "ferocious bite", + /*P*/ {}, + /*A*/ {}, + /*C*/ {} + ); + } + + static ActionNode* rip([[maybe_unused]] PlayerbotAI* botAI) + { + return new ActionNode( + "rip", + /*P*/ {}, + /*A*/ {}, + /*C*/ {} + ); + } + + static ActionNode* ravage([[maybe_unused]] PlayerbotAI* botAI) + { + return new ActionNode( + "ravage", + /*P*/ {}, + /*A*/ { NextAction("pounce") }, + /*C*/ {} + ); + } + + static ActionNode* pounce([[maybe_unused]] PlayerbotAI* botAI) + { + return new ActionNode( + "pounce", + /*P*/ {}, + /*A*/ { NextAction("shred") }, + /*C*/ {} + ); + } + + static ActionNode* prowl([[maybe_unused]] PlayerbotAI* botAI) + { + return new ActionNode( + "prowl", + /*P*/ { NextAction("cat form") }, + /*A*/ {}, + /*C*/ {} + ); + } + +}; + +CatDruidStrategy::CatDruidStrategy(PlayerbotAI* botAI) : FeralDruidStrategy(botAI) +{ + actionNodeFactories.Add(new CatDruidStrategyActionNodeFactory()); +} + +std::vector CatDruidStrategy::getDefaultActions() +{ + return { + NextAction("melee", ACTION_DEFAULT) + }; +} + +void CatDruidStrategy::InitTriggers(std::vector& triggers) +{ + FeralDruidStrategy::InitTriggers(triggers); + + triggers.push_back( + new TriggerNode( + "healer low mana", { + NextAction("innervate on healer", 35.0f) + } + ) + ); + triggers.push_back( + new TriggerNode( + "prowl", { + NextAction("prowl", 29.5f) + } + ) + ); + triggers.push_back( + new TriggerNode( + "enemy out of melee", { + NextAction("dash", 28.0f) + } + ) + ); + + triggers.push_back( + new TriggerNode( + "cat form", { + NextAction("cat form", 28.0f) + } + ) + ); + triggers.push_back( + new TriggerNode( + "low energy", { + NextAction("tiger's fury", 27.0f) + } + ) + ); + triggers.push_back( + new TriggerNode( + "savage roar", { + NextAction("savage roar", 26.0f) + } + ) + ); + + triggers.push_back( + new TriggerNode( + "combo points 5 available", { + NextAction("rip", 23.5f) + } + ) + ); + triggers.push_back( + new TriggerNode( + "combo points 5 available", { + NextAction("maim", 23.0f) + } + ) + ); + triggers.push_back( + new TriggerNode( + "ferocious bite execute", { + NextAction("ferocious bite", 24.0f) + } + ) + ); + triggers.push_back( + new TriggerNode( + "clearcasting", { + NextAction("shred", 24.5f) + } + ) + ); + triggers.push_back( + new TriggerNode( + "ferocious bite time", { + NextAction("ferocious bite", 22.5f) + } + ) + ); + triggers.push_back( + new TriggerNode( + "mangle (cat)", { + NextAction("mangle (cat)", 22.0f) + } + ) + ); + triggers.push_back( + new TriggerNode( + "rake", { + NextAction("rake", 21.5f) + } + ) + ); + triggers.push_back( + new TriggerNode( + "medium threat", { + NextAction("cower", 21.0f) + } + ) + ); + + triggers.push_back( + new TriggerNode( + "almost full energy available", { + NextAction("ravage", 5.6f) + } + ) + ); + triggers.push_back( + new TriggerNode( + "combo points not full", { + NextAction("ravage", 5.6f) + } + ) + ); + triggers.push_back( + new TriggerNode( + "almost full energy available", { + NextAction("pounce", 5.5f) + } + ) + ); + triggers.push_back( + new TriggerNode( + "combo points not full", { + NextAction("pounce", 5.5f) + } + ) + ); + triggers.push_back( + new TriggerNode( + "almost full energy available", { + NextAction("shred", 5.4f) + } + ) + ); + triggers.push_back( + new TriggerNode( + "combo points not full", { + NextAction("shred", 5.4f) + } + ) + ); + triggers.push_back( + new TriggerNode( + "almost full energy available", { + NextAction("mangle (cat)", 5.3f) + } + ) + ); + triggers.push_back( + new TriggerNode( + "combo points not full and high energy", { + NextAction("mangle (cat)", 5.3f) + } + ) + ); + triggers.push_back( + new TriggerNode( + "almost full energy available", { + NextAction("claw", 5.2f) + } + ) + ); + triggers.push_back( + new TriggerNode( + "combo points not full and high energy", { + NextAction("claw", 5.2f) + } + ) + ); + triggers.push_back( + new TriggerNode( + "faerie fire (feral)", { + NextAction("faerie fire (feral)", 5.0f) + } + ) + ); +} + +// ============================================================ +// CatOffhealStrategy +// Additive overlay — only the healing triggers. Designed to be +// stacked on top of "cat" so the bot stays in cat form for DPS +// but shifts out to heal when the party needs it. +// ============================================================ + +class CatOffhealStrategyActionNodeFactory : public NamedObjectFactory +{ +public: + CatOffhealStrategyActionNodeFactory() + { + creators["healing touch on party"] = &healing_touch_on_party; + creators["regrowth on party"] = ®rowth_on_party; + creators["rejuvenation on party"] = &rejuvenation_on_party; + } + +private: + // P: shift to caster form before casting C: shift back to cat form afterwards + static ActionNode* healing_touch_on_party([[maybe_unused]] PlayerbotAI* botAI) + { + return new ActionNode( + "healing touch on party", + /*P*/ { NextAction("caster form") }, + /*A*/ {}, + /*C*/ { NextAction("cat form") } + ); + } + + static ActionNode* regrowth_on_party([[maybe_unused]] PlayerbotAI* botAI) + { + return new ActionNode( + "regrowth on party", + /*P*/ { NextAction("caster form") }, + /*A*/ {}, + /*C*/ { NextAction("cat form") } + ); + } + + static ActionNode* rejuvenation_on_party([[maybe_unused]] PlayerbotAI* botAI) + { + return new ActionNode( + "rejuvenation on party", + /*P*/ { NextAction("caster form") }, + /*A*/ {}, + /*C*/ { NextAction("cat form") } + ); + } +}; + +CatOffhealStrategy::CatOffhealStrategy(PlayerbotAI* botAI) : CombatStrategy(botAI) +{ + actionNodeFactories.Add(new CatOffhealStrategyActionNodeFactory()); +} + +void CatOffhealStrategy::InitTriggers(std::vector& triggers) +{ + triggers.push_back( + new TriggerNode( + "party member critical health", + { + NextAction("regrowth on party", 36.0f), + NextAction("healing touch on party", 35.0f) + } + ) + ); + triggers.push_back( + new TriggerNode( + "party member low health", + { + NextAction("healing touch on party", 25.0f) + } + ) + ); + triggers.push_back( + new TriggerNode( + "party member medium health", + { + NextAction("rejuvenation on party", 18.0f) + } + ) + ); + triggers.push_back( + new TriggerNode( + "party member to heal out of spell range", + { + NextAction("reach party member to heal", 93.0f) + } + ) + ); + triggers.push_back( + new TriggerNode( + "low mana", + { + NextAction("innervate", 24.0f) + } + ) + ); +} diff --git a/src/Ai/Class/Druid/Strategy/CatDpsDruidStrategy.h b/src/Ai/Class/Druid/Strategy/CatDruidStrategy.h similarity index 59% rename from src/Ai/Class/Druid/Strategy/CatDpsDruidStrategy.h rename to src/Ai/Class/Druid/Strategy/CatDruidStrategy.h index 312e94d0f6b..51d80cd7718 100644 --- a/src/Ai/Class/Druid/Strategy/CatDpsDruidStrategy.h +++ b/src/Ai/Class/Druid/Strategy/CatDruidStrategy.h @@ -3,17 +3,17 @@ * and/or modify it under version 3 of the License, or (at your option), any later version. */ -#ifndef _PLAYERBOT_CATDPSDRUIDSTRATEGY_H -#define _PLAYERBOT_CATDPSDRUIDSTRATEGY_H +#ifndef _PLAYERBOT_CATDRUIDSTRATEGY_H +#define _PLAYERBOT_CATDRUIDSTRATEGY_H #include "FeralDruidStrategy.h" class PlayerbotAI; -class CatDpsDruidStrategy : public FeralDruidStrategy +class CatDruidStrategy : public FeralDruidStrategy { public: - CatDpsDruidStrategy(PlayerbotAI* botAI); + CatDruidStrategy(PlayerbotAI* botAI); public: void InitTriggers(std::vector& triggers) override; @@ -22,14 +22,16 @@ class CatDpsDruidStrategy : public FeralDruidStrategy uint32 GetType() const override { return STRATEGY_TYPE_COMBAT | STRATEGY_TYPE_MELEE; } }; -class CatAoeDruidStrategy : public CombatStrategy +// Optional additive strategy. Layers emergency heals on top of the "cat" strategy. +// Enable : co +offheal +// Disable: co -offheal +class CatOffhealStrategy : public CombatStrategy { public: - CatAoeDruidStrategy(PlayerbotAI* botAI) : CombatStrategy(botAI) {} + CatOffhealStrategy(PlayerbotAI* botAI); -public: void InitTriggers(std::vector& triggers) override; - std::string const getName() override { return "cat aoe"; } + std::string const getName() override { return "offheal"; } }; #endif diff --git a/src/Ai/Class/Druid/Strategy/FeralDruidStrategy.cpp b/src/Ai/Class/Druid/Strategy/FeralDruidStrategy.cpp index 894c05bff68..aec438890ee 100644 --- a/src/Ai/Class/Druid/Strategy/FeralDruidStrategy.cpp +++ b/src/Ai/Class/Druid/Strategy/FeralDruidStrategy.cpp @@ -14,12 +14,10 @@ class FeralDruidStrategyActionNodeFactory : public NamedObjectFactory& triggers) GenericDruidStrategy::InitTriggers(triggers); triggers.push_back(new TriggerNode( - "enemy out of melee", { NextAction("reach melee", ACTION_HIGH + 1) })); + "enemy out of melee", { NextAction("reach melee", 21.0f) })); triggers.push_back(new TriggerNode( - "critical health", { NextAction("survival instincts", ACTION_EMERGENCY + 1) })); - triggers.push_back(new TriggerNode( - "omen of clarity", { NextAction("omen of clarity", ACTION_HIGH + 9) })); + "low health", { NextAction("survival instincts", 91.0f) })); triggers.push_back(new TriggerNode("player has flag", - { NextAction("dash", ACTION_EMERGENCY + 2) })); + { NextAction("dash", 92.0f) })); triggers.push_back(new TriggerNode("enemy flagcarrier near", - { NextAction("dash", ACTION_EMERGENCY + 2) })); - triggers.push_back( - new TriggerNode("berserk", { NextAction("berserk", ACTION_HIGH + 6) })); + { NextAction("dash", 92.0f) })); +} + +void FeralChargeDruidStrategy::InitTriggers(std::vector& triggers) +{ + Player* bot = botAI->GetBot(); + + if (bot->HasSpell(SPELL_CAT_FORM) && !bot->HasAura(AURA_THICK_HIDE)) + triggers.push_back(new TriggerNode( + "enemy out of melee", { NextAction("feral charge - cat", 29.0f) })); + else + triggers.push_back(new TriggerNode( + "enemy out of melee", { NextAction("feral charge - bear", 18.0f) })); } diff --git a/src/Ai/Class/Druid/Strategy/FeralDruidStrategy.h b/src/Ai/Class/Druid/Strategy/FeralDruidStrategy.h index abf01c694ca..ebff73de357 100644 --- a/src/Ai/Class/Druid/Strategy/FeralDruidStrategy.h +++ b/src/Ai/Class/Druid/Strategy/FeralDruidStrategy.h @@ -3,11 +3,14 @@ * and/or modify it under version 3 of the License, or (at your option), any later version. */ -#ifndef _PLAYERBOT_FERALRUIDSTRATEGY_H -#define _PLAYERBOT_FERALRUIDSTRATEGY_H +#ifndef _PLAYERBOT_FERALDRUIDSTRATEGY_H +#define _PLAYERBOT_FERALDRUIDSTRATEGY_H #include "GenericDruidStrategy.h" +constexpr uint32 SPELL_CAT_FORM = 768; +constexpr uint32 AURA_THICK_HIDE = 16931; + class PlayerbotAI; class ShapeshiftDruidStrategyActionNodeFactory : public NamedObjectFactory @@ -83,4 +86,18 @@ class FeralDruidStrategy : public GenericDruidStrategy uint32 GetType() const override { return STRATEGY_TYPE_COMBAT | STRATEGY_TYPE_MELEE; } }; +// Optional strategy — enabled by default for cat and bear. +// Registers the "enemy out of melee" → Feral Charge trigger, spec-gated at +// init time so cats get Feral Charge (Cat) and bears get Feral Charge (Bear). +// Disable with: co -feral charge +// Re-enable with: co +feral charge +class FeralChargeDruidStrategy : public CombatStrategy +{ +public: + FeralChargeDruidStrategy(PlayerbotAI* botAI) : CombatStrategy(botAI) {} + + void InitTriggers(std::vector& triggers) override; + std::string const getName() override { return "feral charge"; } +}; + #endif diff --git a/src/Ai/Class/Druid/Strategy/GenericDruidNonCombatStrategy.cpp b/src/Ai/Class/Druid/Strategy/GenericDruidNonCombatStrategy.cpp index 7d1a5f7ca12..0977540f630 100644 --- a/src/Ai/Class/Druid/Strategy/GenericDruidNonCombatStrategy.cpp +++ b/src/Ai/Class/Druid/Strategy/GenericDruidNonCombatStrategy.cpp @@ -17,12 +17,13 @@ class GenericDruidNonCombatStrategyActionNodeFactory : public NamedObjectFactory creators["thorns on party"] = þs_on_party; creators["mark of the wild"] = &mark_of_the_wild; creators["mark of the wild on party"] = &mark_of_the_wild_on_party; - // creators["innervate"] = &innervate; creators["regrowth_on_party"] = ®rowth_on_party; creators["rejuvenation on party"] = &rejuvenation_on_party; creators["remove curse on party"] = &remove_curse_on_party; creators["abolish poison on party"] = &abolish_poison_on_party; creators["revive"] = &revive; + creators["prowl"] = &prowl; + creators["aquatic form"] = &aquatic_form; } private: @@ -92,6 +93,23 @@ class GenericDruidNonCombatStrategyActionNodeFactory : public NamedObjectFactory /*A*/ {}, /*C*/ {}); } + + static ActionNode* prowl([[maybe_unused]] PlayerbotAI* botAI) + { + return new ActionNode("prowl", + /*P*/ { NextAction("cat form") }, + /*A*/ {}, + /*C*/ {}); + } + + static ActionNode* aquatic_form([[maybe_unused]] PlayerbotAI* botAI) + { + return new ActionNode("aquatic form", + /*P*/ { NextAction("caster form") }, + /*A*/ {}, + /*C*/ {}); + } + }; GenericDruidNonCombatStrategy::GenericDruidNonCombatStrategy(PlayerbotAI* botAI) : NonCombatStrategy(botAI) @@ -165,11 +183,16 @@ void GenericDruidNonCombatStrategy::InitTriggers(std::vector& trig NextAction("remove curse on party", ACTION_DISPEL + 7), })); + triggers.push_back(new TriggerNode("aquatic form", { NextAction("aquatic form", 10.0f) })); + int specTab = AiFactory::GetPlayerSpecTab(botAI->GetBot()); - if (specTab == 0 || specTab == 2) // Balance or Restoration + if (specTab == DRUID_TAB_BALANCE || specTab == DRUID_TAB_RESTORATION) triggers.push_back(new TriggerNode("often", { NextAction("apply oil", 1.0f) })); - if (specTab == 1) // Feral + if (specTab == DRUID_TAB_FERAL) + { triggers.push_back(new TriggerNode("often", { NextAction("apply stone", 1.0f) })); + triggers.push_back(new TriggerNode("prowl", { NextAction("prowl", ACTION_INTERRUPT) })); + } } diff --git a/src/Ai/Class/Druid/Strategy/GenericDruidStrategy.cpp b/src/Ai/Class/Druid/Strategy/GenericDruidStrategy.cpp index 36b90a146a2..d5e7b9f4035 100644 --- a/src/Ai/Class/Druid/Strategy/GenericDruidStrategy.cpp +++ b/src/Ai/Class/Druid/Strategy/GenericDruidStrategy.cpp @@ -5,6 +5,8 @@ #include "GenericDruidStrategy.h" +#include "AiFactory.h" +#include "FeralDruidStrategy.h" #include "Playerbots.h" class GenericDruidStrategyActionNodeFactory : public NamedObjectFactory @@ -20,6 +22,8 @@ class GenericDruidStrategyActionNodeFactory : public NamedObjectFactory& triggers) CombatStrategy::InitTriggers(triggers); triggers.push_back( - new TriggerNode("low health", { NextAction("barkskin", ACTION_HIGH + 7) })); + new TriggerNode("almost full health", { NextAction("barkskin", 40.0f) })); + + Player* bot = botAI->GetBot(); + int tab = AiFactory::GetPlayerSpecTab(bot); + + if (tab == DRUID_TAB_FERAL) + { + if (!bot->HasAura(16931) /*thick hide — bear spec*/) + { + triggers.push_back(new TriggerNode("predator's swiftness and combat party member dead", + { NextAction("rebirth", 29.0f) })); + triggers.push_back(new TriggerNode("combat party member dead", + { NextAction("rebirth", 28.5f) })); + } + } + else + { + triggers.push_back(new TriggerNode("combat party member dead", + { NextAction("rebirth", 29.0f) })); + } - triggers.push_back(new TriggerNode("combat party member dead", - { NextAction("rebirth", ACTION_HIGH + 9) })); triggers.push_back(new TriggerNode("being attacked", - { NextAction("nature's grasp", ACTION_HIGH + 1) })); - triggers.push_back(new TriggerNode("new pet", { NextAction("set pet stance", 60.0f) })); + { NextAction("nature's grasp", 39.0f) })); } void DruidCureStrategy::InitTriggers(std::vector& triggers) { triggers.push_back( new TriggerNode("party member cure poison", - { NextAction("abolish poison on party", ACTION_DISPEL + 1) })); + { NextAction("abolish poison on party", 51.0f) })); triggers.push_back( new TriggerNode("party member remove curse", - { NextAction("remove curse on party", ACTION_DISPEL + 7) })); + { NextAction("remove curse on party", 57.0f) })); } void DruidBoostStrategy::InitTriggers(std::vector& triggers) { - triggers.push_back(new TriggerNode( - "nature's swiftness", { NextAction("nature's swiftness", ACTION_HIGH + 9) })); + Player* bot = botAI->GetBot(); + int tab = AiFactory::GetPlayerSpecTab(bot); + + if (tab == DRUID_TAB_BALANCE) + { + triggers.push_back(new TriggerNode("force of nature", { NextAction("force of nature", 29.0f) })); + triggers.push_back(new TriggerNode("new pet", { NextAction("set pet stance", 60.0f) })); + } + + if (tab == DRUID_TAB_FERAL) + { + triggers.push_back(new TriggerNode("berserk", { NextAction("berserk", 27.5f) })); + } } void DruidCcStrategy::InitTriggers(std::vector& triggers) { - triggers.push_back(new TriggerNode( - "entangling roots", { NextAction("entangling roots on cc", ACTION_HIGH + 2) })); - triggers.push_back(new TriggerNode( - "entangling roots kite", { NextAction("entangling roots", ACTION_HIGH + 2) })); - triggers.push_back(new TriggerNode( - "hibernate", { NextAction("hibernate on cc", ACTION_HIGH + 3) })); + Player* bot = botAI->GetBot(); + int tab = AiFactory::GetPlayerSpecTab(bot); + + if (tab == DRUID_TAB_BALANCE || tab == DRUID_TAB_RESTORATION) + { + triggers.push_back(new TriggerNode( + "cyclone", { NextAction("cyclone on cc", 42.0f) })); + triggers.push_back(new TriggerNode( + "hibernate", { NextAction("hibernate on cc", 41.0f) })); + triggers.push_back(new TriggerNode( + "entangling roots", { NextAction("entangling roots on cc", 40.0f) })); + } + if (tab == DRUID_TAB_FERAL) + { + if (bot->HasSpell(SPELL_CAT_FORM) && !bot->HasAura(AURA_THICK_HIDE)) + { + triggers.push_back(new TriggerNode( + "predator's swiftness and cyclone", { NextAction("cyclone on cc", 42.0f) })); + triggers.push_back(new TriggerNode( + "predator's swiftness and hibernate", { NextAction("hibernate on cc", 41.0f) })); + triggers.push_back(new TriggerNode( + "predator's swiftness and entangling roots", { NextAction("entangling roots on cc", 40.0f) })); + } + else + { + triggers.push_back(new TriggerNode( + "cyclone", { NextAction("cyclone on cc", 42.0f) })); + triggers.push_back(new TriggerNode( + "hibernate", { NextAction("hibernate on cc", 41.0f) })); + triggers.push_back(new TriggerNode( + "entangling roots", { NextAction("entangling roots on cc", 40.0f) })); + } + } } void DruidHealerDpsStrategy::InitTriggers(std::vector& triggers) @@ -149,10 +223,39 @@ void DruidHealerDpsStrategy::InitTriggers(std::vector& triggers) triggers.push_back( new TriggerNode("healer should attack", { - NextAction("cancel tree form", ACTION_DEFAULT + 0.4f), - NextAction("moonfire", ACTION_DEFAULT + 0.3f), - NextAction("wrath", ACTION_DEFAULT + 0.2f), - NextAction("starfire", ACTION_DEFAULT + 0.1f), -})); + NextAction("cancel tree form", 5.4f), + NextAction("moonfire", 5.3f), + NextAction("wrath", 5.2f), + NextAction("starfire", 5.1f), + })); +} +void DruidAoeStrategy::InitTriggers(std::vector& triggers) +{ + Player* bot = botAI->GetBot(); + int tab = AiFactory::GetPlayerSpecTab(bot); + + if (tab == DRUID_TAB_BALANCE) + { + triggers.push_back(new TriggerNode("hurricane channel check", { NextAction("cancel channel", 22.0f) })); + triggers.push_back(new TriggerNode("starfall", { NextAction("starfall", 28.5f) })); + triggers.push_back(new TriggerNode("medium aoe", { NextAction("hurricane", 23.0f) })); + triggers.push_back(new TriggerNode("enemy within melee", { NextAction("typhoon", 40.0f) })); + triggers.push_back(new TriggerNode("insect swarm on attacker", { NextAction("insect swarm on attacker", 5.2f) })); + triggers.push_back(new TriggerNode("moonfire on attacker", { NextAction("moonfire on attacker", 5.1f) })); + } + + if (tab == DRUID_TAB_RESTORATION) + { + triggers.push_back(new TriggerNode("hurricane channel check", { NextAction("cancel channel", 22.0f) })); + triggers.push_back(new TriggerNode("medium aoe", { NextAction("hurricane", 23.0f) })); + triggers.push_back(new TriggerNode("insect swarm on attacker", { NextAction("insect swarm on attacker", 5.2f) })); + triggers.push_back(new TriggerNode("moonfire on attacker", { NextAction("moonfire on attacker", 5.1f) })); + } + + if (tab == DRUID_TAB_FERAL && bot->HasSpell(SPELL_CAT_FORM) && !bot->HasAura(AURA_THICK_HIDE)) + { + triggers.push_back(new TriggerNode("clearcasting and medium aoe", { NextAction("swipe (cat)", 25.5f) })); + triggers.push_back(new TriggerNode("medium aoe", { NextAction("swipe (cat)", 25.0f) })); + } } diff --git a/src/Ai/Class/Druid/Strategy/GenericDruidStrategy.h b/src/Ai/Class/Druid/Strategy/GenericDruidStrategy.h index 2f1c2e78743..1cb88f82ab3 100644 --- a/src/Ai/Class/Druid/Strategy/GenericDruidStrategy.h +++ b/src/Ai/Class/Druid/Strategy/GenericDruidStrategy.h @@ -55,4 +55,13 @@ class DruidHealerDpsStrategy : public Strategy std::string const getName() override { return "healer dps"; } }; +class DruidAoeStrategy : public Strategy +{ +public: + DruidAoeStrategy(PlayerbotAI* botAI) : Strategy(botAI) {} + + void InitTriggers(std::vector& triggers) override; + std::string const getName() override { return "aoe"; } +}; + #endif diff --git a/src/Ai/Class/Druid/Strategy/HealDruidStrategy.cpp b/src/Ai/Class/Druid/Strategy/HealDruidStrategy.cpp deleted file mode 100644 index 0710e0fdc1e..00000000000 --- a/src/Ai/Class/Druid/Strategy/HealDruidStrategy.cpp +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it - * and/or modify it under version 3 of the License, or (at your option), any later version. - */ - -#include "HealDruidStrategy.h" - -#include "Playerbots.h" - -class HealDruidStrategyActionNodeFactory : public NamedObjectFactory -{ -public: - HealDruidStrategyActionNodeFactory() { - creators["nourish on party"] = &nourtish_on_party; - } - -private: - static ActionNode* nourtish_on_party([[maybe_unused]] PlayerbotAI* botAI) - { - return new ActionNode("nourish on party", - /*P*/ {}, - /*A*/ { NextAction("healing touch on party") }, - /*C*/ {}); - } -}; - -HealDruidStrategy::HealDruidStrategy(PlayerbotAI* botAI) : GenericDruidStrategy(botAI) -{ - actionNodeFactories.Add(new HealDruidStrategyActionNodeFactory()); -} - -void HealDruidStrategy::InitTriggers(std::vector& triggers) -{ - GenericDruidStrategy::InitTriggers(triggers); - - // no healer dps strategy - triggers.push_back(new TriggerNode("no healer dps strategy", - { NextAction("tree form", ACTION_DEFAULT) })); - - triggers.push_back(new TriggerNode( - "party member to heal out of spell range", - { NextAction("reach party member to heal", ACTION_CRITICAL_HEAL + 9) })); - - // CRITICAL - triggers.push_back( - new TriggerNode("party member critical health", - { - NextAction("tree form", ACTION_CRITICAL_HEAL + 4.1f), - NextAction("swiftmend on party", ACTION_CRITICAL_HEAL + 4), - NextAction("regrowth on party", ACTION_CRITICAL_HEAL + 3), - NextAction("wild growth on party", ACTION_CRITICAL_HEAL + 2), - NextAction("nourish on party", ACTION_CRITICAL_HEAL + 1), - })); - - triggers.push_back( - new TriggerNode("party member critical health", - { NextAction("nature's swiftness", ACTION_CRITICAL_HEAL + 4) })); - - triggers.push_back(new TriggerNode("clearcasting", - { NextAction("lifebloom on main tank", ACTION_CRITICAL_HEAL - 1) })); - - triggers.push_back(new TriggerNode( - "group heal setting", - { - NextAction("tree form", ACTION_MEDIUM_HEAL + 2.3f), - NextAction("wild growth on party", ACTION_MEDIUM_HEAL + 2.2f), - NextAction("rejuvenation on not full", ACTION_MEDIUM_HEAL + 2.1f), - })); - - triggers.push_back( - new TriggerNode("medium group heal setting", - { - NextAction("tree form", ACTION_CRITICAL_HEAL + 0.6f), - NextAction("tranquility", ACTION_CRITICAL_HEAL + 0.5f) })); - - // LOW - triggers.push_back( - new TriggerNode("party member low health", - { NextAction("tree form", ACTION_MEDIUM_HEAL + 1.5f), - NextAction("wild growth on party", ACTION_MEDIUM_HEAL + 1.4f), - NextAction("regrowth on party", ACTION_MEDIUM_HEAL + 1.3f), - NextAction("swiftmend on party", ACTION_MEDIUM_HEAL + 1.2), - NextAction("nourish on party", ACTION_MEDIUM_HEAL + 1.1f), - })); - - // MEDIUM - triggers.push_back( - new TriggerNode("party member medium health", - { - NextAction("tree form", ACTION_MEDIUM_HEAL + 0.5f), - NextAction("wild growth on party", ACTION_MEDIUM_HEAL + 0.4f), - NextAction("rejuvenation on party", ACTION_MEDIUM_HEAL + 0.3f), - NextAction("regrowth on party", ACTION_MEDIUM_HEAL + 0.2f), - NextAction("nourish on party", ACTION_MEDIUM_HEAL + 0.1f) })); - - // almost full - triggers.push_back( - new TriggerNode("party member almost full health", - { NextAction("wild growth on party", ACTION_LIGHT_HEAL + 0.3f), - NextAction("rejuvenation on party", ACTION_LIGHT_HEAL + 0.2f), - NextAction("regrowth on party", ACTION_LIGHT_HEAL + 0.1f) })); - - triggers.push_back( - new TriggerNode("medium mana", { NextAction("innervate", ACTION_HIGH + 5) })); - - triggers.push_back(new TriggerNode("enemy too close for spell", - { NextAction("flee", ACTION_MOVE + 9) })); -} diff --git a/src/Ai/Class/Druid/Strategy/HealDruidStrategy.h b/src/Ai/Class/Druid/Strategy/HealDruidStrategy.h deleted file mode 100644 index 36ce402715d..00000000000 --- a/src/Ai/Class/Druid/Strategy/HealDruidStrategy.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it - * and/or modify it under version 3 of the License, or (at your option), any later version. - */ - -#ifndef _PLAYERBOT_HEALDRUIDSTRATEGY_H -#define _PLAYERBOT_HEALDRUIDSTRATEGY_H - -#include "GenericDruidStrategy.h" -#include "Strategy.h" - -class PlayerbotAI; - -class HealDruidStrategy : public GenericDruidStrategy -{ -public: - HealDruidStrategy(PlayerbotAI* botAI); - - void InitTriggers(std::vector& triggers) override; - std::string const getName() override { return "heal"; } - uint32 GetType() const override { return STRATEGY_TYPE_RANGED | STRATEGY_TYPE_HEAL; } -}; - -#endif diff --git a/src/Ai/Class/Druid/Strategy/MeleeDruidStrategy.cpp b/src/Ai/Class/Druid/Strategy/MeleeDruidStrategy.cpp deleted file mode 100644 index 5dc0f85d914..00000000000 --- a/src/Ai/Class/Druid/Strategy/MeleeDruidStrategy.cpp +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it - * and/or modify it under version 3 of the License, or (at your option), any later version. - */ - -#include "MeleeDruidStrategy.h" - -#include "Playerbots.h" - -MeleeDruidStrategy::MeleeDruidStrategy(PlayerbotAI* botAI) : CombatStrategy(botAI) {} - -std::vector MeleeDruidStrategy::getDefaultActions() -{ - return { - NextAction("faerie fire", ACTION_DEFAULT + 0.1f), - NextAction("melee", ACTION_DEFAULT) - }; -} - -void MeleeDruidStrategy::InitTriggers(std::vector& triggers) -{ - triggers.push_back( - new TriggerNode( - "omen of clarity", - { - NextAction("omen of clarity", ACTION_HIGH + 9) - } - ) - ); - - CombatStrategy::InitTriggers(triggers); -} diff --git a/src/Ai/Class/Druid/Strategy/MeleeDruidStrategy.h b/src/Ai/Class/Druid/Strategy/MeleeDruidStrategy.h deleted file mode 100644 index 67bbbbfed10..00000000000 --- a/src/Ai/Class/Druid/Strategy/MeleeDruidStrategy.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it - * and/or modify it under version 3 of the License, or (at your option), any later version. - */ - -#ifndef _PLAYERBOT_MELEEDRUIDSTRATEGY_H -#define _PLAYERBOT_MELEEDRUIDSTRATEGY_H - -#include "CombatStrategy.h" - -class MeleeDruidStrategy : public CombatStrategy -{ -public: - MeleeDruidStrategy(PlayerbotAI* botAI); - - void InitTriggers(std::vector& triggers) override; - std::string const getName() override { return "melee"; } - std::vector getDefaultActions() override; -}; - -#endif diff --git a/src/Ai/Class/Druid/Strategy/OffhealDruidCatStrategy.cpp b/src/Ai/Class/Druid/Strategy/OffhealDruidCatStrategy.cpp deleted file mode 100644 index fb7893651c0..00000000000 --- a/src/Ai/Class/Druid/Strategy/OffhealDruidCatStrategy.cpp +++ /dev/null @@ -1,307 +0,0 @@ -/* - * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it - * and/or modify it under version 3 of the License, or (at your option), any later version. - */ - - #include "OffhealDruidCatStrategy.h" - - #include "Playerbots.h" - #include "Strategy.h" - - class OffhealDruidCatStrategyActionNodeFactory : public NamedObjectFactory -{ -public: - OffhealDruidCatStrategyActionNodeFactory() - { - creators["cat form"] = &cat_form; - creators["mangle (cat)"] = &mangle_cat; - creators["shred"] = &shred; - creators["rake"] = &rake; - creators["rip"] = &rip; - creators["ferocious bite"] = &ferocious_bite; - creators["savage roar"] = &savage_roar; - creators["faerie fire (feral)"] = &faerie_fire_feral; - creators["healing touch on party"] = &healing_touch_on_party; - creators["regrowth on party"] = ®rowth_on_party; - creators["rejuvenation on party"] = &rejuvenation_on_party; - } - -private: - static ActionNode* cat_form([[maybe_unused]] PlayerbotAI* botAI) - { - return new ActionNode( - "cat form", - /*P*/ {}, - /*A*/ {}, - /*C*/ {} - ); - } - - static ActionNode* mangle_cat([[maybe_unused]] PlayerbotAI* botAI) - { - return new ActionNode( - "mangle (cat)", - /*P*/ {}, - /*A*/ {}, - /*C*/ {} - ); - } - - static ActionNode* shred([[maybe_unused]] PlayerbotAI* botAI) - { - return new ActionNode( - "shred", - /*P*/ {}, - /*A*/ { NextAction("claw") }, - /*C*/ {} - ); - } - - static ActionNode* rake([[maybe_unused]] PlayerbotAI* botAI) - { - return new ActionNode( - "rake", - /*P*/ {}, - /*A*/ {}, - /*C*/ {} - ); - } - - static ActionNode* rip([[maybe_unused]] PlayerbotAI* botAI) - { - return new ActionNode( - "rip", - /*P*/ {}, - /*A*/ {}, - /*C*/ {} - ); - } - - static ActionNode* ferocious_bite([[maybe_unused]] PlayerbotAI* botAI) - { - return new ActionNode( - "ferocious bite", - /*P*/ {}, - /*A*/ { NextAction("rip") }, - /*C*/ {} - ); - } - - static ActionNode* savage_roar([[maybe_unused]] PlayerbotAI* botAI) - { - return new ActionNode( - "savage roar", - /*P*/ {}, - /*A*/ {}, - /*C*/ {} - ); - } - - static ActionNode* faerie_fire_feral([[maybe_unused]] PlayerbotAI* botAI) - { - return new ActionNode( - "faerie fire (feral)", - /*P*/ {}, - /*A*/ {}, - /*C*/ {} - ); - } - - static ActionNode* healing_touch_on_party([[maybe_unused]] PlayerbotAI* botAI) - { - return new ActionNode( - "healing touch on party", - /*P*/ { NextAction("caster form") }, - /*A*/ {}, - /*C*/ { NextAction("cat form") } - ); - } - - static ActionNode* regrowth_on_party([[maybe_unused]] PlayerbotAI* botAI) - { - return new ActionNode( - "regrowth on party", - /*P*/ { NextAction("caster form") }, - /*A*/ {}, - /*C*/ { NextAction("cat form") } - ); - } - - static ActionNode* rejuvenation_on_party([[maybe_unused]] PlayerbotAI* botAI) - { - return new ActionNode( - "rejuvenation on party", - /*P*/ { NextAction("caster form") }, - /*A*/ {}, - /*C*/ { NextAction("cat form") } - ); - } -}; - -OffhealDruidCatStrategy::OffhealDruidCatStrategy(PlayerbotAI* botAI) : FeralDruidStrategy(botAI) -{ - actionNodeFactories.Add(new OffhealDruidCatStrategyActionNodeFactory()); -} - -std::vector OffhealDruidCatStrategy::getDefaultActions() -{ - return { - NextAction("mangle (cat)", ACTION_DEFAULT + 0.5f), - NextAction("shred", ACTION_DEFAULT + 0.4f), - NextAction("rake", ACTION_DEFAULT + 0.3f), - NextAction("melee", ACTION_DEFAULT), - NextAction("cat form", ACTION_DEFAULT - 0.1f) - }; -} - -void OffhealDruidCatStrategy::InitTriggers(std::vector& triggers) -{ - FeralDruidStrategy::InitTriggers(triggers); - - triggers.push_back( - new TriggerNode( - "cat form", - { - NextAction("cat form", ACTION_HIGH + 8) - } - ) - ); - triggers.push_back( - new TriggerNode( - "savage roar", - { - NextAction("savage roar", ACTION_HIGH + 7) - } - ) - ); - triggers.push_back( - new TriggerNode( - "combo points 5 available", - { - NextAction("rip", ACTION_HIGH + 6) - } - ) - ); - triggers.push_back( - new TriggerNode( - "ferocious bite time", - { - NextAction("ferocious bite", ACTION_HIGH + 5) - } - ) - ); - triggers.push_back( - new TriggerNode( - "target with combo points almost dead", - { - NextAction("ferocious bite", ACTION_HIGH + 4) - } - ) - ); - triggers.push_back( - new TriggerNode( - "mangle (cat)", - { - NextAction("mangle (cat)", ACTION_HIGH + 3) - } - ) - ); - triggers.push_back( - new TriggerNode( - "rake", - { - NextAction("rake", ACTION_HIGH + 2) - } - ) - ); - triggers.push_back( - new TriggerNode( - "almost full energy available", - { - NextAction("shred", ACTION_DEFAULT + 0.4f) - } - ) - ); - triggers.push_back( - new TriggerNode( - "combo points not full", - { - NextAction("shred", ACTION_DEFAULT + 0.4f) - } - ) - ); - triggers.push_back( - new TriggerNode( - "faerie fire (feral)", - { - NextAction("faerie fire (feral)", ACTION_NORMAL) - } - ) - ); - triggers.push_back( - new TriggerNode( - "enemy out of melee", - { - NextAction("feral charge - cat", ACTION_HIGH + 9), - NextAction("dash", ACTION_HIGH + 8) - } - ) - ); - triggers.push_back( - new TriggerNode( - "medium aoe", - { - NextAction("swipe (cat)", ACTION_HIGH + 3) - } - ) - ); - triggers.push_back( - new TriggerNode( - "tiger's fury", - { - NextAction("tiger's fury", ACTION_NORMAL + 1) - } - ) - ); - triggers.push_back( - new TriggerNode( - "party member critical health", - { - NextAction("regrowth on party", ACTION_CRITICAL_HEAL + 6), - NextAction("healing touch on party", ACTION_CRITICAL_HEAL + 5) - } - ) - ); - triggers.push_back( - new TriggerNode( - "party member low health", - { - NextAction("healing touch on party", ACTION_MEDIUM_HEAL + 5) - } - ) - ); - triggers.push_back( - new TriggerNode( - "party member medium health", - { - NextAction("rejuvenation on party", ACTION_LIGHT_HEAL + 8) - } - ) - ); - triggers.push_back( - new TriggerNode( - "party member to heal out of spell range", - { - NextAction("reach party member to heal", ACTION_EMERGENCY + 3) - } - ) - ); - triggers.push_back( - new TriggerNode( - "low mana", - { - NextAction("innervate", ACTION_HIGH + 4) - } - ) - ); -} diff --git a/src/Ai/Class/Druid/Strategy/OffhealDruidCatStrategy.h b/src/Ai/Class/Druid/Strategy/OffhealDruidCatStrategy.h deleted file mode 100644 index 83775ef98c0..00000000000 --- a/src/Ai/Class/Druid/Strategy/OffhealDruidCatStrategy.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it - * and/or modify it under version 3 of the License, or (at your option), any later version. - */ - - #ifndef _PLAYERBOT_OFFHEALDRUIDCATSTRATEGY_H - #define _PLAYERBOT_OFFHEALDRUIDCATSTRATEGY_H - - #include "FeralDruidStrategy.h" - - class PlayerbotAI; - - class OffhealDruidCatStrategy : public FeralDruidStrategy - { - public: - OffhealDruidCatStrategy(PlayerbotAI* botAI); - - void InitTriggers(std::vector& triggers) override; - std::string const getName() override { return "offheal"; } - std::vector getDefaultActions() override; - uint32 GetType() const override - { - return STRATEGY_TYPE_COMBAT | STRATEGY_TYPE_DPS | STRATEGY_TYPE_HEAL | STRATEGY_TYPE_MELEE; - } - }; - - #endif diff --git a/src/Ai/Class/Druid/Strategy/RestoDruidStrategy.cpp b/src/Ai/Class/Druid/Strategy/RestoDruidStrategy.cpp new file mode 100644 index 00000000000..5f086ee507b --- /dev/null +++ b/src/Ai/Class/Druid/Strategy/RestoDruidStrategy.cpp @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#include "RestoDruidStrategy.h" + +#include "Playerbots.h" + +class RestoDruidStrategyActionNodeFactory : public NamedObjectFactory +{ +public: + RestoDruidStrategyActionNodeFactory() { + creators["nourish on party"] = &nourish_on_party; + } + +private: + static ActionNode* nourish_on_party([[maybe_unused]] PlayerbotAI* botAI) + { + return new ActionNode("nourish on party", + /*P*/ {}, + /*A*/ {}, + /*C*/ {}); + } +}; + +RestoDruidStrategy::RestoDruidStrategy(PlayerbotAI* botAI) : GenericDruidStrategy(botAI) +{ + actionNodeFactories.Add(new RestoDruidStrategyActionNodeFactory()); +} + +void RestoDruidStrategy::InitTriggers(std::vector& triggers) +{ + GenericDruidStrategy::InitTriggers(triggers); + + triggers.push_back(new TriggerNode("no healer dps strategy", + { NextAction("tree form", 5.0f) })); + + triggers.push_back(new TriggerNode( + "party member to heal out of spell range", + { NextAction("reach party member to heal", 39.0f) })); + + triggers.push_back( + new TriggerNode("party member critical health", + { + NextAction("tree form", 34.1f), + NextAction("swiftmend on party", 34.0f), + NextAction("wild growth on party", 33.0f), + NextAction("nourish on party", 32.0f), + NextAction("regrowth on party", 31.0f), + NextAction("healing touch on party", 30.0f), + })); + + triggers.push_back( + new TriggerNode("party member critical health", + { NextAction("nature's swiftness", 58.0f) })); + + triggers.push_back(new TriggerNode( + "nature's swiftness active", + { NextAction("healing touch on party", 55.0f) })); + + triggers.push_back(new TriggerNode("clearcasting", + { NextAction("lifebloom on main tank", 13.0f) })); + + // LOW + triggers.push_back( + new TriggerNode("party member low health", + { + NextAction("tree form", 21.5f), + NextAction("swiftmend on party", 21.4f), + NextAction("wild growth on party", 21.3f), + NextAction("nourish on party", 21.2f), + NextAction("regrowth on party", 21.1f), + NextAction("healing touch on party", 21.0f), + })); + + // MEDIUM + triggers.push_back( + new TriggerNode("party member medium health", + { + NextAction("tree form", 20.5f), + NextAction("swiftmend on party", 20.4f), + NextAction("wild growth on party", 20.3f), + NextAction("nourish on party", 20.2f), + NextAction("regrowth on party", 20.1f), + NextAction("healing touch on party", 20.0f), + })); + + // ALMOST FULL + triggers.push_back( + new TriggerNode("party member almost full health", + { + NextAction("wild growth on party", 10.3f), + NextAction("rejuvenation on party", 10.2f), + NextAction("regrowth on party", 10.1f), + })); + + triggers.push_back( + new TriggerNode("medium mana", { NextAction("innervate", 25.0f) })); + + triggers.push_back(new TriggerNode("enemy too close for spell", + { NextAction("flee", 39.0f) })); +} + +void DruidTranquilityStrategy::InitTriggers(std::vector& triggers) +{ + triggers.push_back(new TriggerNode("medium group heal setting", + { NextAction("tree form", 30.6f), NextAction("tranquility", 30.5f) })); +} + +void DruidBlanketStrategy::InitTriggers(std::vector& triggers) +{ + triggers.push_back(new TriggerNode( + "wild growth blanket", + { NextAction("tree form", 8.1f), NextAction("wild growth blanket", 8.0f) })); + + triggers.push_back(new TriggerNode( + "rejuvenation blanket", + { NextAction("tree form", 6.1f), NextAction("rejuvenation blanket", 6.0f) })); +} diff --git a/src/Ai/Class/Druid/Strategy/RestoDruidStrategy.h b/src/Ai/Class/Druid/Strategy/RestoDruidStrategy.h new file mode 100644 index 00000000000..afc3a93bc0b --- /dev/null +++ b/src/Ai/Class/Druid/Strategy/RestoDruidStrategy.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#ifndef _PLAYERBOT_RESTODRUIDSTRATEGY_H +#define _PLAYERBOT_RESTODRUIDSTRATEGY_H + +#include "GenericDruidStrategy.h" +#include "Strategy.h" + +class PlayerbotAI; + +class RestoDruidStrategy : public GenericDruidStrategy +{ +public: + RestoDruidStrategy(PlayerbotAI* botAI); + + void InitTriggers(std::vector& triggers) override; + std::string const getName() override { return "resto"; } + uint32 GetType() const override { return STRATEGY_TYPE_RANGED | STRATEGY_TYPE_HEAL; } +}; + +class DruidBlanketStrategy : public Strategy +{ +public: + DruidBlanketStrategy(PlayerbotAI* botAI) : Strategy(botAI) {} + + void InitTriggers(std::vector& triggers) override; + std::string const getName() override { return "blanketing"; } +}; + +class DruidTranquilityStrategy : public Strategy +{ +public: + DruidTranquilityStrategy(PlayerbotAI* botAI) : Strategy(botAI) {} + + void InitTriggers(std::vector& triggers) override; + std::string const getName() override { return "tranquility"; } +}; + +#endif diff --git a/src/Ai/Class/Druid/Trigger/DruidTriggers.cpp b/src/Ai/Class/Druid/Trigger/DruidTriggers.cpp index 6cf8552a3af..5a8dbdfa4d7 100644 --- a/src/Ai/Class/Druid/Trigger/DruidTriggers.cpp +++ b/src/Ai/Class/Druid/Trigger/DruidTriggers.cpp @@ -4,8 +4,10 @@ */ #include "DruidTriggers.h" +#include "DynamicObject.h" #include "Player.h" #include "Playerbots.h" +#include "ServerFacade.h" bool MarkOfTheWildOnPartyTrigger::IsActive() { @@ -35,6 +37,45 @@ bool TreeFormTrigger::IsActive() { return !botAI->HasAura(33891, bot); } bool CatFormTrigger::IsActive() { return !botAI->HasAura("cat form", bot); } +bool AquaticFormTrigger::IsActive() +{ + return !bot->IsInCombat() && !botAI->HasAura("aquatic form", bot) && + bot->GetLiquidData().Status == LIQUID_MAP_UNDER_WATER; +} + +bool ProwlTrigger::IsActive() +{ + if (botAI->HasAura("prowl", bot) || bot->IsInCombat()) + return false; + + uint32 prowlId = botAI->GetAiObjectContext()->GetValue("spell id", "prowl")->Get(); + if (!prowlId || !bot->HasSpell(prowlId) || bot->HasSpellCooldown(prowlId)) + return false; + + float distance = 30.f; + + Unit* target = AI_VALUE(Unit*, "enemy player target"); + if (target && !target->IsInWorld()) + return false; + if (!target) + target = AI_VALUE(Unit*, "grind target"); + if (!target) + target = AI_VALUE(Unit*, "dps target"); + if (!target) + return false; + + if (target && target->GetVictim()) + distance -= 10; + if (target->isMoving() && target->GetVictim()) + distance -= 10; + if (bot->InBattleground()) + distance += 15; + if (bot->InArena()) + distance += 15; + + return target && ServerFacade::instance().GetDistance2d(bot, target) < distance; +} + const std::set HurricaneChannelCheckTrigger::HURRICANE_SPELL_IDS = { 16914, // Hurricane Rank 1 17401, // Hurricane Rank 2 @@ -45,19 +86,38 @@ const std::set HurricaneChannelCheckTrigger::HURRICANE_SPELL_IDS = { bool HurricaneChannelCheckTrigger::IsActive() { - Player* bot = botAI->GetBot(); - - // Check if the bot is channeling a spell if (Spell* spell = bot->GetCurrentSpell(CURRENT_CHANNELED_SPELL)) { - // Only trigger if the spell being channeled is Hurricane - if (HURRICANE_SPELL_IDS.count(spell->m_spellInfo->Id)) + if (!HURRICANE_SPELL_IDS.count(spell->m_spellInfo->Id)) + return false; + + // Find this bot's own Hurricane DynamicObject + DynamicObject* dynObj = nullptr; + for (uint32 spellId : HURRICANE_SPELL_IDS) { - uint8 attackerCount = AI_VALUE(uint8, "attacker count"); - return attackerCount < minEnemies; + dynObj = bot->GetDynObject(spellId); + if (dynObj) + break; } + + if (!dynObj) + return false; + + // Count attackers actually inside the Hurricane AoE + float radius = dynObj->GetRadius(); + GuidVector attackers = AI_VALUE(GuidVector, "attackers"); + uint32 count = 0; + for (ObjectGuid const& guid : attackers) + { + Unit* unit = botAI->GetUnit(guid); + if (!unit || !unit->IsAlive()) + continue; + if (unit->GetDistance(dynObj->GetPosition()) <= radius) + count++; + } + + return count < minEnemies; } - // Not channeling Hurricane return false; } diff --git a/src/Ai/Class/Druid/Trigger/DruidTriggers.h b/src/Ai/Class/Druid/Trigger/DruidTriggers.h index 01daaeba65a..1f389c947a8 100644 --- a/src/Ai/Class/Druid/Trigger/DruidTriggers.h +++ b/src/Ai/Class/Druid/Trigger/DruidTriggers.h @@ -8,6 +8,7 @@ #include "CureTriggers.h" #include "GenericTriggers.h" +#include "RtiTriggers.h" #include "Player.h" #include "PlayerbotAI.h" #include "Playerbots.h" @@ -15,6 +16,8 @@ #include "Trigger.h" #include +constexpr uint32 AURA_OMEN_OF_CLARITY = 16864; + class PlayerbotAI; class MarkOfTheWildOnPartyTrigger : public BuffOnPartyTrigger @@ -55,22 +58,30 @@ class ThornsTrigger : public BuffTrigger bool IsActive() override; }; -class OmenOfClarityTrigger : public BuffTrigger +class ClearcastingTrigger : public HasAuraTrigger { public: - OmenOfClarityTrigger(PlayerbotAI* botAI) : BuffTrigger(botAI, "omen of clarity") {} + ClearcastingTrigger(PlayerbotAI* botAI) : HasAuraTrigger(botAI, "clearcasting") {} }; -class ClearcastingTrigger : public HasAuraTrigger +class PredatorsSwiftnessTrigger : public HasAuraTrigger { public: - ClearcastingTrigger(PlayerbotAI* botAI) : HasAuraTrigger(botAI, "clearcasting") {} + PredatorsSwiftnessTrigger(PlayerbotAI* botAI) : HasAuraTrigger(botAI, "predator's swiftness") {} +}; + +class NaturesSwiftnessActiveTrigger : public HasAuraTrigger +{ +public: + NaturesSwiftnessActiveTrigger(PlayerbotAI* botAI) : HasAuraTrigger(botAI, "nature's swiftness") {} + bool IsActive() override { return botAI->HasAura("nature's swiftness", bot); } }; class RakeTrigger : public DebuffTrigger { public: RakeTrigger(PlayerbotAI* botAI) : DebuffTrigger(botAI, "rake", 1, true) {} + bool IsActive() override { return !botAI->HasAura("prowl", bot) && DebuffTrigger::IsActive(); } }; class InsectSwarmTrigger : public DebuffTrigger @@ -79,12 +90,26 @@ class InsectSwarmTrigger : public DebuffTrigger InsectSwarmTrigger(PlayerbotAI* botAI) : DebuffTrigger(botAI, "insect swarm", 1, true) {} }; +class InsectSwarmOnAttackerTrigger : public DebuffOnAttackerTrigger +{ +public: + InsectSwarmOnAttackerTrigger(PlayerbotAI* botAI) : DebuffOnAttackerTrigger(botAI, "insect swarm", true) {} + bool IsActive() override { return BuffTrigger::IsActive(); } +}; + class MoonfireTrigger : public DebuffTrigger { public: MoonfireTrigger(PlayerbotAI* botAI) : DebuffTrigger(botAI, "moonfire", 1, true) {} }; +class MoonfireOnAttackerTrigger : public DebuffOnAttackerTrigger +{ +public: + MoonfireOnAttackerTrigger(PlayerbotAI* botAI) : DebuffOnAttackerTrigger(botAI, "moonfire", true) {} + bool IsActive() override { return BuffTrigger::IsActive(); } +}; + class FaerieFireTrigger : public DebuffTrigger { public: @@ -95,6 +120,35 @@ class FaerieFireFeralTrigger : public DebuffTrigger { public: FaerieFireFeralTrigger(PlayerbotAI* botAI) : DebuffTrigger(botAI, "faerie fire (feral)") {} + + bool IsActive() override + { + if (!bot->IsInCombat()) + return false; + + // Bear: every cast generates immediate threat/damage for free — spam it + if (botAI->HasAnyAuraOf(bot, "bear form", "dire bear form", nullptr)) + { + Unit* target = GetTarget(); + return target && target->IsAlive() && target->IsInWorld(); + } + + if (!botAI->HasAura("cat form", bot)) + return false; + + if (botAI->HasAura("prowl", bot)) + return false; + + // Cat with Omen of Clarity: spam to fish for Clearcasting procs + if (bot->HasAura(AURA_OMEN_OF_CLARITY)) + { + Unit* target = GetTarget(); + return target && target->IsAlive() && target->IsInWorld(); + } + + // Cat without Omen of Clarity: apply as a normal debuff, don't reapply + return DebuffTrigger::IsActive(); + } }; class BashInterruptSpellTrigger : public InterruptSpellTrigger @@ -103,16 +157,16 @@ class BashInterruptSpellTrigger : public InterruptSpellTrigger BashInterruptSpellTrigger(PlayerbotAI* botAI) : InterruptSpellTrigger(botAI, "bash") {} }; -class TigersFuryTrigger : public BuffTrigger +class BerserkTrigger : public BoostTrigger { public: - TigersFuryTrigger(PlayerbotAI* botAI) : BuffTrigger(botAI, "tiger's fury") {} + BerserkTrigger(PlayerbotAI* botAI) : BoostTrigger(botAI, "berserk") {} }; -class BerserkTrigger : public BoostTrigger +class BerserkActiveTrigger : public HasAuraTrigger { public: - BerserkTrigger(PlayerbotAI* botAI) : BoostTrigger(botAI, "berserk") {} + BerserkActiveTrigger(PlayerbotAI* botAI) : HasAuraTrigger(botAI, "berserk") {} }; class SavageRoarTrigger : public BuffTrigger @@ -127,10 +181,10 @@ class NaturesGraspTrigger : public BuffTrigger NaturesGraspTrigger(PlayerbotAI* botAI) : BuffTrigger(botAI, "nature's grasp") {} }; -class EntanglingRootsTrigger : public HasCcTargetTrigger +class EntanglingRootsTrigger : public RtiCcTrigger { public: - EntanglingRootsTrigger(PlayerbotAI* botAI) : HasCcTargetTrigger(botAI, "entangling roots") {} + EntanglingRootsTrigger(PlayerbotAI* botAI) : RtiCcTrigger(botAI, "entangling roots") {} }; class EntanglingRootsKiteTrigger : public DebuffTrigger @@ -141,10 +195,16 @@ class EntanglingRootsKiteTrigger : public DebuffTrigger bool IsActive() override; }; -class HibernateTrigger : public HasCcTargetTrigger +class HibernateTrigger : public RtiCcTrigger +{ +public: + HibernateTrigger(PlayerbotAI* botAI) : RtiCcTrigger(botAI, "hibernate") {} +}; + +class CycloneTrigger : public RtiCcTrigger { public: - HibernateTrigger(PlayerbotAI* botAI) : HasCcTargetTrigger(botAI, "hibernate") {} + CycloneTrigger(PlayerbotAI* botAI) : RtiCcTrigger(botAI, "cyclone") {} }; class CurePoisonTrigger : public NeedCureTrigger @@ -185,6 +245,14 @@ class CatFormTrigger : public BuffTrigger bool IsActive() override; }; +class AquaticFormTrigger : public Trigger +{ +public: + AquaticFormTrigger(PlayerbotAI* botAI) : Trigger(botAI, "aquatic form") {} + + bool IsActive() override; +}; + class EclipseSolarTrigger : public HasAuraTrigger { public: @@ -218,18 +286,69 @@ class DruidPartyMemberRemoveCurseTrigger : public PartyMemberNeedCureTrigger } }; -class EclipseSolarCooldownTrigger : public SpellCooldownTrigger +class StarfallTrigger : public SpellNoCooldownTrigger +{ +public: + StarfallTrigger(PlayerbotAI* botAI) : SpellNoCooldownTrigger(botAI, "starfall") {} +}; + +class ForceOfNatureTrigger : public BoostTrigger +{ +public: + ForceOfNatureTrigger(PlayerbotAI* botAI) : BoostTrigger(botAI, "force of nature") {} +}; + +class MangleBearTrigger : public DebuffTrigger +{ +public: + MangleBearTrigger(PlayerbotAI* botAI) : DebuffTrigger(botAI, "mangle (bear)") {} + + bool IsActive() override + { + if (!bot->IsInCombat() || !botAI->HasAnyAuraOf(bot, "bear form", "dire bear form", nullptr)) + return false; + Unit* target = GetTarget(); + return target && target->IsAlive() && target->IsInWorld(); + } +}; + +class LacerateTrigger : public DebuffTrigger { public: - EclipseSolarCooldownTrigger(PlayerbotAI* ai) : SpellCooldownTrigger(ai, "eclipse (solar)") {} - bool IsActive() override { return bot->HasSpellCooldown(48517); } + LacerateTrigger(PlayerbotAI* botAI) : DebuffTrigger(botAI, "lacerate") {} + + bool IsActive() override + { + if (!bot->IsInCombat() || !botAI->HasAnyAuraOf(bot, "bear form", "dire bear form", nullptr)) + return false; + + Unit* target = GetTarget(); + if (!target || !target->IsAlive() || !target->IsInWorld()) + return false; + + Aura* lacerate = botAI->GetAura("lacerate", target, false, false); + if (!lacerate) + return true; + + if (lacerate->GetStackAmount() < 5) + return true; + + return lacerate->GetDuration() <= 6000; + } }; -class EclipseLunarCooldownTrigger : public SpellCooldownTrigger +class DemoralizeRoarTrigger : public DebuffTrigger { public: - EclipseLunarCooldownTrigger(PlayerbotAI* ai) : SpellCooldownTrigger(ai, "eclipse (lunar)") {} - bool IsActive() override { return bot->HasSpellCooldown(48518); } + DemoralizeRoarTrigger(PlayerbotAI* botAI) : DebuffTrigger(botAI, "demoralizing roar") {} + + bool IsActive() override + { + return DebuffTrigger::IsActive() + && !botAI->HasAura("curse of weakness", GetTarget(), false, false) + && !botAI->HasAura("demoralizing shout", GetTarget(), false, false) + && !botAI->HasAura("vindication", GetTarget(), false, false); + } }; class MangleCatTrigger : public DebuffTrigger @@ -238,6 +357,8 @@ class MangleCatTrigger : public DebuffTrigger MangleCatTrigger(PlayerbotAI* ai) : DebuffTrigger(ai, "mangle (cat)", 1, false, 0.0f) {} bool IsActive() override { + if (botAI->HasAura("prowl", bot)) + return false; return DebuffTrigger::IsActive() && !botAI->HasAura("mangle (bear)", GetTarget(), false, false, -1, true) && !botAI->HasAura("trauma", GetTarget(), false, false, -1, true); } @@ -271,10 +392,36 @@ class FerociousBiteTimeTrigger : public Trigger } }; +class FerociousBiteExecuteTrigger : public Trigger +{ +public: + FerociousBiteExecuteTrigger(PlayerbotAI* ai) : Trigger(ai, "ferocious bite execute") {} + bool IsActive() override + { + Unit* target = AI_VALUE(Unit*, "current target"); + if (!target || !target->IsAlive()) + return false; + + if (!AI_VALUE2(uint32, "spell id", "ferocious bite")) + return false; + + if (AI_VALUE2(uint8, "combo", "current target") < 1) + return false; + + if (target->GetHealthPct() >= 25.0f) + return false; + + if (target->GetHealth() >= 20000) + return false; + + return true; + } +}; + class HurricaneChannelCheckTrigger : public Trigger { public: - HurricaneChannelCheckTrigger(PlayerbotAI* botAI, uint32 minEnemies = 2) + HurricaneChannelCheckTrigger(PlayerbotAI* botAI, uint32 minEnemies = 3) : Trigger(botAI, "hurricane channel check"), minEnemies(minEnemies) { } @@ -297,4 +444,12 @@ class NoHealerDpsStrategyTrigger : public Trigger } }; +class ProwlTrigger : public Trigger +{ +public: + ProwlTrigger(PlayerbotAI* botAI) : Trigger(botAI, "prowl") {} + + bool IsActive() override; +}; + #endif diff --git a/src/Ai/Class/Mage/Action/MageActions.cpp b/src/Ai/Class/Mage/Action/MageActions.cpp index 63ba0d9d596..9c190d54c0e 100644 --- a/src/Ai/Class/Mage/Action/MageActions.cpp +++ b/src/Ai/Class/Mage/Action/MageActions.cpp @@ -11,6 +11,22 @@ #include "ServerFacade.h" #include "SharedDefines.h" +std::vector CastMoltenArmorAction::getAlternatives() +{ + if (!AI_VALUE2(uint32, "spell id", "molten armor")) + return NextAction::merge({ NextAction("mage armor") }, CastBuffSpellAction::getAlternatives()); + + return CastBuffSpellAction::getAlternatives(); +} + +std::vector CastMageArmorAction::getAlternatives() +{ + if (!AI_VALUE2(uint32, "spell id", "mage armor")) + return NextAction::merge({ NextAction("ice armor") }, CastBuffSpellAction::getAlternatives()); + + return CastBuffSpellAction::getAlternatives(); +} + bool UseManaSapphireAction::isUseful() { Player* bot = botAI->GetBot(); @@ -50,22 +66,23 @@ bool UseManaAgateAction::isUseful() bool CastFrostNovaAction::isUseful() { Unit* target = AI_VALUE(Unit*, "current target"); - if (!target || !target->IsInWorld()) - return false; - - if (target->ToCreature() && target->ToCreature()->HasMechanicTemplateImmunity(1 << (MECHANIC_FREEZE - 1))) - return false; - - if (target->isFrozen()) + if (!target || !target->IsInWorld() || target->isFrozen() || + (target->ToCreature() && + target->ToCreature()->HasMechanicTemplateImmunity(1 << (MECHANIC_FREEZE - 1)))) + { return false; + } - return ServerFacade::instance().IsDistanceLessOrEqualThan(AI_VALUE2(float, "distance", GetTargetName()), 10.f); + return ServerFacade::instance().IsDistanceLessOrEqualThan( + AI_VALUE2(float, "distance", GetTargetName()), 10.f); } bool CastConeOfColdAction::isUseful() { bool facingTarget = AI_VALUE2(bool, "facing", "current target"); - bool targetClose = ServerFacade::instance().IsDistanceLessOrEqualThan(AI_VALUE2(float, "distance", GetTargetName()), 10.f); + bool targetClose = ServerFacade::instance().IsDistanceLessOrEqualThan( + AI_VALUE2(float, "distance", GetTargetName()), 10.f); + return facingTarget && targetClose; } @@ -74,6 +91,7 @@ bool CastDragonsBreathAction::isUseful() Unit* target = AI_VALUE(Unit*, "current target"); if (!target) return false; + bool facingTarget = AI_VALUE2(bool, "facing", "current target"); bool targetClose = bot->IsWithinCombatRange(target, 10.0f); return facingTarget && targetClose; @@ -84,6 +102,7 @@ bool CastBlastWaveAction::isUseful() Unit* target = AI_VALUE(Unit*, "current target"); if (!target) return false; + bool targetClose = bot->IsWithinCombatRange(target, 10.0f); return targetClose; } @@ -100,14 +119,11 @@ Unit* CastFocusMagicOnPartyAction::GetTarget() for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) { Player* member = ref->GetSource(); - if (!member || member == bot || !member->IsAlive()) - continue; - - if (member->GetMap() != bot->GetMap() || bot->GetDistance(member) > sPlayerbotAIConfig.spellDistance) - continue; - - if (member->HasAura(54646)) + if (!member || member == bot || !member->IsAlive() || member->GetMap() != bot->GetMap() || + bot->GetDistance(member) > sPlayerbotAIConfig.spellDistance || member->HasAura(54646)) // Focus Magic + { continue; + } if (member->getClass() == CLASS_MAGE) return member; @@ -136,7 +152,7 @@ bool CastBlinkBackAction::Execute(Event event) Unit* target = AI_VALUE(Unit*, "current target"); if (!target) return false; - // can cast spell check passed in isUseful() + bot->SetOrientation(bot->GetAngle(target) + M_PI); return CastSpellAction::Execute(event); } diff --git a/src/Ai/Class/Mage/Action/MageActions.h b/src/Ai/Class/Mage/Action/MageActions.h index abd3020526a..4c7f76a68fa 100644 --- a/src/Ai/Class/Mage/Action/MageActions.h +++ b/src/Ai/Class/Mage/Action/MageActions.h @@ -18,12 +18,14 @@ class CastMoltenArmorAction : public CastBuffSpellAction { public: CastMoltenArmorAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "molten armor") {} + std::vector getAlternatives() override; }; class CastMageArmorAction : public CastBuffSpellAction { public: CastMageArmorAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "mage armor") {} + std::vector getAlternatives() override; }; class CastIceArmorAction : public CastBuffSpellAction @@ -60,7 +62,8 @@ class CastFocusMagicOnPartyAction : public CastSpellAction class CastSummonWaterElementalAction : public CastBuffSpellAction { public: - CastSummonWaterElementalAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "summon water elemental") {} + CastSummonWaterElementalAction(PlayerbotAI* botAI) + : CastBuffSpellAction(botAI, "summon water elemental") {} }; // Boost Actions @@ -236,7 +239,8 @@ class CastCounterspellAction : public CastSpellAction class CastCounterspellOnEnemyHealerAction : public CastSpellOnEnemyHealerAction { public: - CastCounterspellOnEnemyHealerAction(PlayerbotAI* botAI) : CastSpellOnEnemyHealerAction(botAI, "counterspell") {} + CastCounterspellOnEnemyHealerAction(PlayerbotAI* botAI) + : CastSpellOnEnemyHealerAction(botAI, "counterspell") {} }; class CastFrostNovaAction : public CastSpellAction @@ -275,9 +279,7 @@ class CastRemoveLesserCurseOnPartyAction : public CurePartyMemberAction { public: CastRemoveLesserCurseOnPartyAction(PlayerbotAI* botAI) - : CurePartyMemberAction(botAI, "remove lesser curse", DISPEL_CURSE) - { - } + : CurePartyMemberAction(botAI, "remove lesser curse", DISPEL_CURSE) {} }; // Damage and Debuff Actions @@ -331,7 +333,6 @@ class CastLivingBombAction : public CastDebuffSpellAction CastLivingBombAction(PlayerbotAI* botAI) : CastDebuffSpellAction(botAI, "living bomb", true) {} bool isUseful() override { - // Bypass TTL check return CastAuraSpellAction::isUseful(); } }; @@ -342,7 +343,6 @@ class CastLivingBombOnAttackersAction : public CastDebuffSpellOnAttackerAction CastLivingBombOnAttackersAction(PlayerbotAI* botAI) : CastDebuffSpellOnAttackerAction(botAI, "living bomb", true) {} bool isUseful() override { - // Bypass TTL check return CastAuraSpellAction::isUseful(); } }; diff --git a/src/Ai/Class/Mage/MageAiObjectContext.cpp b/src/Ai/Class/Mage/MageAiObjectContext.cpp index 477c0e075d2..b2b03166008 100644 --- a/src/Ai/Class/Mage/MageAiObjectContext.cpp +++ b/src/Ai/Class/Mage/MageAiObjectContext.cpp @@ -89,6 +89,7 @@ class MageTriggerFactoryInternal : public NamedObjectContext creators["arcane intellect"] = &MageTriggerFactoryInternal::arcane_intellect; creators["arcane intellect on party"] = &MageTriggerFactoryInternal::arcane_intellect_on_party; creators["mage armor"] = &MageTriggerFactoryInternal::mage_armor; + creators["molten armor"] = &MageTriggerFactoryInternal::molten_armor; creators["remove curse"] = &MageTriggerFactoryInternal::remove_curse; creators["remove curse on party"] = &MageTriggerFactoryInternal::remove_curse_on_party; creators["counterspell"] = &MageTriggerFactoryInternal::counterspell; @@ -143,6 +144,7 @@ class MageTriggerFactoryInternal : public NamedObjectContext static Trigger* arcane_intellect(PlayerbotAI* botAI) { return new ArcaneIntellectTrigger(botAI); } static Trigger* arcane_intellect_on_party(PlayerbotAI* botAI) { return new ArcaneIntellectOnPartyTrigger(botAI); } static Trigger* mage_armor(PlayerbotAI* botAI) { return new MageArmorTrigger(botAI); } + static Trigger* molten_armor(PlayerbotAI* botAI) { return new MoltenArmorTrigger(botAI); } static Trigger* remove_curse(PlayerbotAI* botAI) { return new RemoveCurseTrigger(botAI); } static Trigger* remove_curse_on_party(PlayerbotAI* botAI) { return new PartyMemberRemoveCurseTrigger(botAI); } static Trigger* counterspell(PlayerbotAI* botAI) { return new CounterspellInterruptSpellTrigger(botAI); } diff --git a/src/Ai/Class/Mage/Strategy/ArcaneMageStrategy.cpp b/src/Ai/Class/Mage/Strategy/ArcaneMageStrategy.cpp index 4707231db13..b9ed1e3dd6c 100644 --- a/src/Ai/Class/Mage/Strategy/ArcaneMageStrategy.cpp +++ b/src/Ai/Class/Mage/Strategy/ArcaneMageStrategy.cpp @@ -13,22 +13,18 @@ class ArcaneMageStrategyActionNodeFactory : public NamedObjectFactory -{ -public: - FireMageStrategyActionNodeFactory() - { - creators["fireball"] = &fireball; - creators["frostbolt"] = &frostbolt; - creators["fire blast"] = &fire_blast; - creators["pyroblast"] = &pyroblast; - creators["scorch"] = &scorch; - creators["living bomb"] = &living_bomb; - creators["combustion"] = &combustion; - } - -private: - static ActionNode* fireball(PlayerbotAI*) { return new ActionNode("fireball", {}, {}, {}); } - static ActionNode* frostbolt(PlayerbotAI*) { return new ActionNode("frostbolt", {}, {}, {}); } - static ActionNode* fire_blast(PlayerbotAI*) { return new ActionNode("fire blast", {}, {}, {}); } - static ActionNode* pyroblast(PlayerbotAI*) { return new ActionNode("pyroblast", {}, {}, {}); } - static ActionNode* scorch(PlayerbotAI*) { return new ActionNode("scorch", {}, {}, {}); } - static ActionNode* living_bomb(PlayerbotAI*) { return new ActionNode("living bomb", {}, {}, {}); } - static ActionNode* combustion(PlayerbotAI*) { return new ActionNode("combustion", {}, {}, {}); } -}; - -// ===== Single Target Strategy ===== FireMageStrategy::FireMageStrategy(PlayerbotAI* botAI) : GenericMageStrategy(botAI) { - actionNodeFactories.Add(new FireMageStrategyActionNodeFactory()); + // No custom ActionNodeFactory needed } // ===== Default Actions ===== diff --git a/src/Ai/Class/Mage/Strategy/FireMageStrategy.h b/src/Ai/Class/Mage/Strategy/FireMageStrategy.h index 03d50641b6b..14655de3a4c 100644 --- a/src/Ai/Class/Mage/Strategy/FireMageStrategy.h +++ b/src/Ai/Class/Mage/Strategy/FireMageStrategy.h @@ -28,4 +28,5 @@ class FirestarterStrategy : public CombatStrategy void InitTriggers(std::vector& triggers) override; std::string const getName() override { return "firestarter"; } }; + #endif diff --git a/src/Ai/Class/Mage/Strategy/FrostFireMageStrategy.cpp b/src/Ai/Class/Mage/Strategy/FrostFireMageStrategy.cpp index 4448a434937..44baf4afcc0 100644 --- a/src/Ai/Class/Mage/Strategy/FrostFireMageStrategy.cpp +++ b/src/Ai/Class/Mage/Strategy/FrostFireMageStrategy.cpp @@ -6,35 +6,9 @@ #include "FrostFireMageStrategy.h" #include "Playerbots.h" -// ===== Action Node Factory ===== -class FrostFireMageStrategyActionNodeFactory : public NamedObjectFactory -{ -public: - FrostFireMageStrategyActionNodeFactory() - { - creators["frostfire bolt"] = &frostfire_bolt; - creators["fire blast"] = &fire_blast; - creators["pyroblast"] = &pyroblast; - creators["combustion"] = &combustion; - creators["icy veins"] = &icy_veins; - creators["scorch"] = &scorch; - creators["living bomb"] = &living_bomb; - } - -private: - static ActionNode* frostfire_bolt(PlayerbotAI*) { return new ActionNode("frostfire bolt", {}, {}, {}); } - static ActionNode* fire_blast(PlayerbotAI*) { return new ActionNode("fire blast", {}, {}, {}); } - static ActionNode* pyroblast(PlayerbotAI*) { return new ActionNode("pyroblast", {}, {}, {}); } - static ActionNode* combustion(PlayerbotAI*) { return new ActionNode("combustion", {}, {}, {}); } - static ActionNode* icy_veins(PlayerbotAI*) { return new ActionNode("icy veins", {}, {}, {}); } - static ActionNode* scorch(PlayerbotAI*) { return new ActionNode("scorch", {}, {}, {}); } - static ActionNode* living_bomb(PlayerbotAI*) { return new ActionNode("living bomb", {}, {}, {}); } -}; - -// ===== Single Target Strategy ===== FrostFireMageStrategy::FrostFireMageStrategy(PlayerbotAI* botAI) : GenericMageStrategy(botAI) { - actionNodeFactories.Add(new FrostFireMageStrategyActionNodeFactory()); + // No custom ActionNodeFactory needed } // ===== Default Actions ===== diff --git a/src/Ai/Class/Mage/Strategy/FrostMageStrategy.cpp b/src/Ai/Class/Mage/Strategy/FrostMageStrategy.cpp index fe703f354d2..4f50cee72aa 100644 --- a/src/Ai/Class/Mage/Strategy/FrostMageStrategy.cpp +++ b/src/Ai/Class/Mage/Strategy/FrostMageStrategy.cpp @@ -4,44 +4,11 @@ */ #include "FrostMageStrategy.h" - #include "Playerbots.h" -// ===== Action Node Factory ===== -class FrostMageStrategyActionNodeFactory : public NamedObjectFactory -{ -public: - FrostMageStrategyActionNodeFactory() - { - creators["cold snap"] = &cold_snap; - creators["ice barrier"] = &ice_barrier; - creators["summon water elemental"] = &summon_water_elemental; - creators["deep freeze"] = &deep_freeze; - creators["icy veins"] = &icy_veins; - creators["frostbolt"] = &frostbolt; - creators["ice lance"] = &ice_lance; - creators["fire blast"] = &fire_blast; - creators["fireball"] = &fireball; - creators["frostfire bolt"] = &frostfire_bolt; - } - -private: - static ActionNode* cold_snap(PlayerbotAI*) { return new ActionNode("cold snap", {}, {}, {}); } - static ActionNode* ice_barrier(PlayerbotAI*) { return new ActionNode("ice barrier", {}, {}, {}); } - static ActionNode* summon_water_elemental(PlayerbotAI*) { return new ActionNode("summon water elemental", {}, {}, {}); } - static ActionNode* deep_freeze(PlayerbotAI*) { return new ActionNode("deep freeze", {}, {}, {}); } - static ActionNode* icy_veins(PlayerbotAI*) { return new ActionNode("icy veins", {}, {}, {}); } - static ActionNode* frostbolt(PlayerbotAI*) { return new ActionNode("frostbolt", {}, {}, {}); } - static ActionNode* ice_lance(PlayerbotAI*) { return new ActionNode("ice lance", {}, {}, {}); } - static ActionNode* fire_blast(PlayerbotAI*) { return new ActionNode("fire blast", {}, {}, {}); } - static ActionNode* fireball(PlayerbotAI*) { return new ActionNode("fireball", {}, {}, {}); } - static ActionNode* frostfire_bolt(PlayerbotAI*) { return new ActionNode("frostfire bolt", {}, { NextAction("fireball") }, {}); } -}; - -// ===== Single Target Strategy ===== FrostMageStrategy::FrostMageStrategy(PlayerbotAI* botAI) : GenericMageStrategy(botAI) { - actionNodeFactories.Add(new FrostMageStrategyActionNodeFactory()); + // No custom ActionNodeFactory needed } // ===== Default Actions ===== diff --git a/src/Ai/Class/Mage/Strategy/GenericMageNonCombatStrategy.cpp b/src/Ai/Class/Mage/Strategy/GenericMageNonCombatStrategy.cpp index eab98ea5a22..e93f7589edb 100644 --- a/src/Ai/Class/Mage/Strategy/GenericMageNonCombatStrategy.cpp +++ b/src/Ai/Class/Mage/Strategy/GenericMageNonCombatStrategy.cpp @@ -12,28 +12,10 @@ class GenericMageNonCombatStrategyActionNodeFactory : public NamedObjectFactory< public: GenericMageNonCombatStrategyActionNodeFactory() { - creators["molten armor"] = &molten_armor; - creators["mage armor"] = &mage_armor; creators["ice armor"] = &ice_armor; } private: - static ActionNode* molten_armor([[maybe_unused]] PlayerbotAI* botAI) - { - return new ActionNode("molten armor", - /*P*/ {}, - /*A*/ { NextAction("mage armor") }, - /*C*/ {}); - } - - static ActionNode* mage_armor([[maybe_unused]] PlayerbotAI* botAI) - { - return new ActionNode("mage armor", - /*P*/ {}, - /*A*/ { NextAction("ice armor") }, - /*C*/ {}); - } - static ActionNode* ice_armor([[maybe_unused]] PlayerbotAI* botAI) { return new ActionNode("ice armor", @@ -65,7 +47,7 @@ void MageBuffManaStrategy::InitTriggers(std::vector& triggers) void MageBuffDpsStrategy::InitTriggers(std::vector& triggers) { - triggers.push_back(new TriggerNode("mage armor", { NextAction("molten armor", 19.0f) })); + triggers.push_back(new TriggerNode("molten armor", { NextAction("molten armor", 19.0f) })); } void MageBuffStrategy::InitTriggers(std::vector& triggers) diff --git a/src/Ai/Class/Mage/Strategy/GenericMageStrategy.cpp b/src/Ai/Class/Mage/Strategy/GenericMageStrategy.cpp index 0e26c692d95..69389a8c443 100644 --- a/src/Ai/Class/Mage/Strategy/GenericMageStrategy.cpp +++ b/src/Ai/Class/Mage/Strategy/GenericMageStrategy.cpp @@ -15,16 +15,8 @@ class GenericMageStrategyActionNodeFactory : public NamedObjectFactory& triggers) Player* bot = botAI->GetBot(); int tab = AiFactory::GetPlayerSpecTab(bot); - if (tab == 0) // Arcane + if (tab == MAGE_TAB_ARCANE) { triggers.push_back(new TriggerNode("arcane power", { NextAction("arcane power", 29.0f) })); triggers.push_back(new TriggerNode("icy veins", { NextAction("icy veins", 28.5f) })); triggers.push_back(new TriggerNode("mirror image", { NextAction("mirror image", 28.0f) })); } - else if (tab == 1) + else if (tab == MAGE_TAB_FIRE) { if (bot->HasSpell(44614) /*Frostfire Bolt*/ && bot->HasAura(15047) /*Ice Shards*/) { // Frostfire @@ -226,7 +154,7 @@ void MageBoostStrategy::InitTriggers(std::vector& triggers) triggers.push_back(new TriggerNode("mirror image", { NextAction("mirror image", 17.5f) })); } } - else if (tab == 2) // Frost + else if (tab == MAGE_TAB_FROST) // Frost { triggers.push_back(new TriggerNode("cold snap", { NextAction("cold snap", 28.0f) })); triggers.push_back(new TriggerNode("icy veins", { NextAction("icy veins", 27.5f) })); @@ -254,15 +182,14 @@ void MageAoeStrategy::InitTriggers(std::vector& triggers) Player* bot = botAI->GetBot(); int tab = AiFactory::GetPlayerSpecTab(bot); - if (tab == 0) // Arcane + if (tab == MAGE_TAB_ARCANE) { triggers.push_back(new TriggerNode("flamestrike active and medium aoe", { NextAction("blizzard", 24.0f) })); triggers.push_back(new TriggerNode("medium aoe", { NextAction("flamestrike", 23.0f), NextAction("blizzard", 22.0f) })); - triggers.push_back(new TriggerNode("light aoe", { NextAction("arcane explosion", 21.0f) })); } - else if (tab == 1) // Fire and Frostfire + else if (tab == MAGE_TAB_FIRE) { triggers.push_back( new TriggerNode("medium aoe", { @@ -275,7 +202,7 @@ void MageAoeStrategy::InitTriggers(std::vector& triggers) triggers.push_back(new TriggerNode("firestarter", { NextAction("flamestrike", 40.0f) })); triggers.push_back(new TriggerNode("living bomb on attackers", { NextAction("living bomb on attackers", 21.0f) })); } - else if (tab == 2) // Frost + else if (tab == MAGE_TAB_FROST) { triggers.push_back(new TriggerNode("flamestrike active and medium aoe", { NextAction("blizzard", 24.0f) })); triggers.push_back(new TriggerNode("medium aoe", { diff --git a/src/Ai/Class/Mage/Trigger/MageTriggers.cpp b/src/Ai/Class/Mage/Trigger/MageTriggers.cpp index e222107988b..7e7ba9ab732 100644 --- a/src/Ai/Class/Mage/Trigger/MageTriggers.cpp +++ b/src/Ai/Class/Mage/Trigger/MageTriggers.cpp @@ -4,7 +4,6 @@ */ #include "MageTriggers.h" -#include "MageActions.h" #include "Playerbots.h" #include "Player.h" #include "Spell.h" @@ -23,7 +22,7 @@ bool NoManaGemTrigger::IsActive() 5513, // Mana Jade 5514 // Mana Agate }; - Player* bot = botAI->GetBot(); + for (uint32 gemId : gemIds) { if (bot->GetItemCount(gemId, false) > 0) // false = only in bags @@ -45,17 +44,35 @@ bool ArcaneIntellectTrigger::IsActive() bool MageArmorTrigger::IsActive() { Unit* target = GetTarget(); + if (botAI->HasAura("mage armor", target)) + return false; + + if (AI_VALUE2(uint32, "spell id", "mage armor")) + return true; + + return !botAI->HasAura("ice armor", target) && !botAI->HasAura("frost armor", target) && + !botAI->HasAura("molten armor", target); +} + +bool MoltenArmorTrigger::IsActive() +{ + Unit* target = GetTarget(); + if (botAI->HasAura("molten armor", target)) + return false; + + if (AI_VALUE2(uint32, "spell id", "molten armor")) + return true; + return !botAI->HasAura("ice armor", target) && !botAI->HasAura("frost armor", target) && - !botAI->HasAura("molten armor", target) && !botAI->HasAura("mage armor", target); + !botAI->HasAura("mage armor", target); } bool FrostNovaOnTargetTrigger::IsActive() { Unit* target = GetTarget(); if (!target || !target->IsAlive() || !target->IsInWorld()) - { return false; - } + return botAI->HasAura(spell, target); } @@ -63,15 +80,14 @@ bool FrostbiteOnTargetTrigger::IsActive() { Unit* target = GetTarget(); if (!target || !target->IsAlive() || !target->IsInWorld()) - { return false; - } + return botAI->HasAura(spell, target); } bool NoFocusMagicTrigger::IsActive() { - if (!bot->HasSpell(54646)) + if (!bot->HasSpell(54646)) // Focus Magic return false; Group* group = bot->GetGroup(); @@ -92,24 +108,19 @@ bool NoFocusMagicTrigger::IsActive() bool DeepFreezeCooldownTrigger::IsActive() { - Player* bot = botAI->GetBot(); - static const uint32 DEEP_FREEZE_SPELL_ID = 44572; - // If the bot does NOT have Deep Freeze, treat as "on cooldown" - if (!bot->HasSpell(DEEP_FREEZE_SPELL_ID)) + if (!bot->HasSpell(44572)) // Deep Freeze return true; - // Otherwise, use the default cooldown logic return SpellCooldownTrigger::IsActive(); } -const std::set FlamestrikeNearbyTrigger::FLAMESTRIKE_SPELL_IDS = {2120, 2121, 8422, 8423, 10215, - 10216, 27086, 42925, 42926}; +const std::unordered_set FlamestrikeNearbyTrigger::FLAMESTRIKE_SPELL_IDS = { + 2120, 2121, 8422, 8423, 10215, 10216, 27086, 42925, 42926 +}; bool FlamestrikeNearbyTrigger::IsActive() { - Player* bot = botAI->GetBot(); - for (uint32 spellId : FLAMESTRIKE_SPELL_IDS) { Aura* aura = bot->GetAura(spellId, bot->GetGUID()); @@ -133,7 +144,6 @@ bool ImprovedScorchTrigger::IsActive() if (!target || !target->IsAlive() || !target->IsInWorld()) return false; - // List of all spell IDs for Improved Scorch, Winter's Chill, and Shadow Mastery static const uint32 ImprovedScorchExclusiveDebuffs[] = {// Shadow Mastery 17794, 17797, 17798, 17799, 17800, // Winter's Chill @@ -147,11 +157,10 @@ bool ImprovedScorchTrigger::IsActive() return false; } - // Use default DebuffTrigger logic for the rest (only trigger if debuff is missing or expiring) return DebuffTrigger::IsActive(); } -const std::set BlizzardChannelCheckTrigger::BLIZZARD_SPELL_IDS = { +const std::unordered_set BlizzardChannelCheckTrigger::BLIZZARD_SPELL_IDS = { 10, // Blizzard Rank 1 6141, // Blizzard Rank 2 8427, // Blizzard Rank 3 @@ -165,19 +174,12 @@ const std::set BlizzardChannelCheckTrigger::BLIZZARD_SPELL_IDS = { bool BlizzardChannelCheckTrigger::IsActive() { - Player* bot = botAI->GetBot(); - - // Check if the bot is channeling a spell - if (Spell* spell = bot->GetCurrentSpell(CURRENT_CHANNELED_SPELL)) + if (Spell* spell = bot->GetCurrentSpell(CURRENT_CHANNELED_SPELL); + spell && BLIZZARD_SPELL_IDS.count(spell->m_spellInfo->Id)) { - // Only trigger if the spell being channeled is Blizzard - if (BLIZZARD_SPELL_IDS.count(spell->m_spellInfo->Id)) - { - uint8 attackerCount = AI_VALUE(uint8, "attacker count"); - return attackerCount < minEnemies; - } + uint8 attackerCount = AI_VALUE(uint8, "attacker count"); + return attackerCount < minEnemies; } - // Not channeling Blizzard return false; } diff --git a/src/Ai/Class/Mage/Trigger/MageTriggers.h b/src/Ai/Class/Mage/Trigger/MageTriggers.h index e769916f47e..566b6b61eed 100644 --- a/src/Ai/Class/Mage/Trigger/MageTriggers.h +++ b/src/Ai/Class/Mage/Trigger/MageTriggers.h @@ -11,19 +11,15 @@ #include "SharedDefines.h" #include "Trigger.h" #include "Playerbots.h" -#include "PlayerbotAI.h" -#include #include -class PlayerbotAI; - // Buff and Out of Combat Triggers class ArcaneIntellectOnPartyTrigger : public BuffOnPartyTrigger { public: - ArcaneIntellectOnPartyTrigger(PlayerbotAI* botAI) : BuffOnPartyTrigger(botAI, "arcane intellect", 2 * 2000) {} - + ArcaneIntellectOnPartyTrigger(PlayerbotAI* botAI) + : BuffOnPartyTrigger(botAI, "arcane intellect", 2 * 2000) {} bool IsActive() override; }; @@ -41,6 +37,13 @@ class MageArmorTrigger : public BuffTrigger bool IsActive() override; }; +class MoltenArmorTrigger : public BuffTrigger +{ +public: + MoltenArmorTrigger(PlayerbotAI* botAI) : BuffTrigger(botAI, "molten armor", 5 * 2000) {} + bool IsActive() override; +}; + class NoFocusMagicTrigger : public Trigger { public: @@ -58,7 +61,6 @@ class NoManaGemTrigger : public Trigger { public: NoManaGemTrigger(PlayerbotAI* botAI) : Trigger(botAI, "no mana gem") {} - bool IsActive() override; }; @@ -109,10 +111,8 @@ class ArcaneBlastStackTrigger : public HasAuraStackTrigger class ArcaneBlast4StacksAndMissileBarrageTrigger : public TwoTriggers { public: - ArcaneBlast4StacksAndMissileBarrageTrigger(PlayerbotAI* ai) - : TwoTriggers(ai, "arcane blast stack", "missile barrage") - { - } + ArcaneBlast4StacksAndMissileBarrageTrigger(PlayerbotAI* botAI) + : TwoTriggers(botAI, "arcane blast stack", "missile barrage") {} }; class CombustionTrigger : public BoostTrigger @@ -138,7 +138,7 @@ class DeepFreezeCooldownTrigger : public SpellCooldownTrigger class ColdSnapTrigger : public TwoTriggers { public: - ColdSnapTrigger(PlayerbotAI* ai) : TwoTriggers(ai, "icy veins on cd", "deep freeze on cd") {} + ColdSnapTrigger(PlayerbotAI* botAI) : TwoTriggers(botAI, "icy veins on cd", "deep freeze on cd") {} }; class MirrorImageTrigger : public BoostTrigger @@ -181,9 +181,8 @@ class RemoveCurseTrigger : public NeedCureTrigger class PartyMemberRemoveCurseTrigger : public PartyMemberNeedCureTrigger { public: - PartyMemberRemoveCurseTrigger(PlayerbotAI* botAI) : PartyMemberNeedCureTrigger(botAI, "remove curse", DISPEL_CURSE) - { - } + PartyMemberRemoveCurseTrigger(PlayerbotAI* botAI) + : PartyMemberNeedCureTrigger(botAI, "remove curse", DISPEL_CURSE) {} }; class SpellstealTrigger : public TargetAuraDispelTrigger @@ -216,7 +215,7 @@ class LivingBombTrigger : public DebuffTrigger class LivingBombOnAttackersTrigger : public DebuffOnAttackerTrigger { public: - LivingBombOnAttackersTrigger(PlayerbotAI* ai) : DebuffOnAttackerTrigger(ai, "living bomb", true) {} + LivingBombOnAttackersTrigger(PlayerbotAI* botAI) : DebuffOnAttackerTrigger(botAI, "living bomb", true) {} bool IsActive() override { return BuffTrigger::IsActive(); } }; @@ -282,13 +281,13 @@ class FlamestrikeNearbyTrigger : public Trigger protected: float radius; - static const std::set FLAMESTRIKE_SPELL_IDS; + static const std::unordered_set FLAMESTRIKE_SPELL_IDS; }; class FlamestrikeBlizzardTrigger : public TwoTriggers { public: - FlamestrikeBlizzardTrigger(PlayerbotAI* ai) : TwoTriggers(ai, "flamestrike nearby", "medium aoe") {} + FlamestrikeBlizzardTrigger(PlayerbotAI* botAI) : TwoTriggers(botAI, "flamestrike nearby", "medium aoe") {} }; class BlizzardChannelCheckTrigger : public Trigger @@ -301,7 +300,7 @@ class BlizzardChannelCheckTrigger : public Trigger protected: uint32 minEnemies; - static const std::set BLIZZARD_SPELL_IDS; + static const std::unordered_set BLIZZARD_SPELL_IDS; }; class BlastWaveOffCdTrigger : public SpellNoCooldownTrigger @@ -313,7 +312,8 @@ class BlastWaveOffCdTrigger : public SpellNoCooldownTrigger class BlastWaveOffCdTriggerAndMediumAoeTrigger : public TwoTriggers { public: - BlastWaveOffCdTriggerAndMediumAoeTrigger(PlayerbotAI* ai) : TwoTriggers(ai, "blast wave off cd", "medium aoe") {} + BlastWaveOffCdTriggerAndMediumAoeTrigger(PlayerbotAI* botAI) + : TwoTriggers(botAI, "blast wave off cd", "medium aoe") {} }; class NoFirestarterStrategyTrigger : public Trigger diff --git a/src/Ai/Class/Paladin/Strategy/GenericPaladinNonCombatStrategy.cpp b/src/Ai/Class/Paladin/Strategy/GenericPaladinNonCombatStrategy.cpp index 670d7e629a3..84f449813e4 100644 --- a/src/Ai/Class/Paladin/Strategy/GenericPaladinNonCombatStrategy.cpp +++ b/src/Ai/Class/Paladin/Strategy/GenericPaladinNonCombatStrategy.cpp @@ -26,8 +26,8 @@ void GenericPaladinNonCombatStrategy::InitTriggers(std::vector& tr triggers.push_back(new TriggerNode("not sensing undead", { NextAction("sense undead", ACTION_IDLE + 1.0f) })); int specTab = AiFactory::GetPlayerSpecTab(botAI->GetBot()); - if (specTab == PALADIN_TAB_HOLY || specTab == PALADIN_TAB_PROTECTION) + if (specTab == PALADIN_TAB_HOLY) triggers.push_back(new TriggerNode("often", { NextAction("apply oil", ACTION_IDLE + 1.0f) })); - if (specTab == PALADIN_TAB_RETRIBUTION) + if (specTab == PALADIN_TAB_PROTECTION || specTab == PALADIN_TAB_RETRIBUTION) triggers.push_back(new TriggerNode("often", { NextAction("apply stone", ACTION_IDLE + 1.0f) })); } diff --git a/src/Ai/Class/Priest/Strategy/HealPriestStrategy.cpp b/src/Ai/Class/Priest/Strategy/HealPriestStrategy.cpp index 35f764e4df2..89e9895abce 100644 --- a/src/Ai/Class/Priest/Strategy/HealPriestStrategy.cpp +++ b/src/Ai/Class/Priest/Strategy/HealPriestStrategy.cpp @@ -86,6 +86,7 @@ void HealPriestStrategy::InitTriggers(std::vector& triggers) new TriggerNode( "party member almost full health", { + NextAction("power word: shield on party", ACTION_LIGHT_HEAL + 3), NextAction("prayer of mending on party", ACTION_LIGHT_HEAL + 2), NextAction("renew on party", ACTION_LIGHT_HEAL + 1) } diff --git a/src/Ai/Class/Warlock/Action/WarlockActions.cpp b/src/Ai/Class/Warlock/Action/WarlockActions.cpp index 93798b702f6..5e23f1af5ac 100644 --- a/src/Ai/Class/Warlock/Action/WarlockActions.cpp +++ b/src/Ai/Class/Warlock/Action/WarlockActions.cpp @@ -27,6 +27,9 @@ bool CastDrainSoulAction::isUseful() { return AI_VALUE2(uint32, "item count", "s // Checks if the bot's health is above a certain threshold, and if so, allows casting Life Tap bool CastLifeTapAction::isUseful() { return AI_VALUE2(uint8, "health", "self target") > sPlayerbotAIConfig.lowHealth; } +Value* CastBanishOnCcAction::GetTargetValue() { return context->GetValue("rti cc target"); } +Value* CastFearOnCcAction::GetTargetValue() { return context->GetValue("rti cc target"); } + // Checks if the target marked with the moon icon can be banished bool CastBanishOnCcAction::isPossible() { diff --git a/src/Ai/Class/Warlock/Action/WarlockActions.h b/src/Ai/Class/Warlock/Action/WarlockActions.h index 09f34637031..8fca0fdc402 100644 --- a/src/Ai/Class/Warlock/Action/WarlockActions.h +++ b/src/Ai/Class/Warlock/Action/WarlockActions.h @@ -170,6 +170,7 @@ class CastBanishOnCcAction : public CastCrowdControlSpellAction { public: CastBanishOnCcAction(PlayerbotAI* botAI) : CastCrowdControlSpellAction(botAI, "banish") {} + Value* GetTargetValue() override; bool isPossible() override; }; @@ -177,6 +178,7 @@ class CastFearOnCcAction : public CastCrowdControlSpellAction { public: CastFearOnCcAction(PlayerbotAI* botAI) : CastCrowdControlSpellAction(botAI, "fear") {} + Value* GetTargetValue() override; bool isPossible() override; }; diff --git a/src/Ai/Class/Warlock/Trigger/WarlockTriggers.cpp b/src/Ai/Class/Warlock/Trigger/WarlockTriggers.cpp index 1df531e7fd7..14e7a52e585 100644 --- a/src/Ai/Class/Warlock/Trigger/WarlockTriggers.cpp +++ b/src/Ai/Class/Warlock/Trigger/WarlockTriggers.cpp @@ -50,22 +50,6 @@ bool TooManySoulShardsTrigger::IsActive() { return GetSoulShardCount(botAI->GetB bool OutOfSoulstoneTrigger::IsActive() { return GetSoulstoneCount(botAI->GetBot()) == 0; } -// Checks if the target marked with the moon icon can be banished -bool BanishTrigger::IsActive() -{ - Unit* ccTarget = context->GetValue("cc target", "banish")->Get(); - Unit* moonTarget = context->GetValue("rti cc target")->Get(); - return ccTarget && moonTarget && ccTarget == moonTarget && HasCcTargetTrigger::IsActive(); -} - -// Checks if the target marked with the moon icon can be feared -bool FearTrigger::IsActive() -{ - Unit* ccTarget = context->GetValue("cc target", "fear")->Get(); - Unit* moonTarget = context->GetValue("rti cc target")->Get(); - return ccTarget && moonTarget && ccTarget == moonTarget && HasCcTargetTrigger::IsActive(); -} - bool DemonArmorTrigger::IsActive() { Unit* target = GetTarget(); diff --git a/src/Ai/Class/Warlock/Trigger/WarlockTriggers.h b/src/Ai/Class/Warlock/Trigger/WarlockTriggers.h index d66fe537e1f..15991e270b3 100644 --- a/src/Ai/Class/Warlock/Trigger/WarlockTriggers.h +++ b/src/Ai/Class/Warlock/Trigger/WarlockTriggers.h @@ -8,6 +8,7 @@ #include "GenericTriggers.h" #include "PlayerbotAI.h" +#include "RtiTriggers.h" #include "Playerbots.h" #include "CureTriggers.h" #include "Trigger.h" @@ -137,18 +138,16 @@ class WrongPetTrigger : public Trigger // CC and Pet Triggers -class BanishTrigger : public HasCcTargetTrigger +class BanishTrigger : public RtiCcTrigger { public: - BanishTrigger(PlayerbotAI* botAI) : HasCcTargetTrigger(botAI, "banish") {} - bool IsActive() override; + BanishTrigger(PlayerbotAI* botAI) : RtiCcTrigger(botAI, "banish") {} }; -class FearTrigger : public HasCcTargetTrigger +class FearTrigger : public RtiCcTrigger { public: - FearTrigger(PlayerbotAI* botAI) : HasCcTargetTrigger(botAI, "fear") {} - bool IsActive() override; + FearTrigger(PlayerbotAI* botAI) : RtiCcTrigger(botAI, "fear") {} }; class SpellLockInterruptSpellTrigger : public InterruptSpellTrigger diff --git a/src/Ai/Raid/BlackTemple/Action/RaidBlackTempleActions.cpp b/src/Ai/Raid/BlackTemple/Action/RaidBlackTempleActions.cpp index 0d47dbf0720..cd6c06892f0 100644 --- a/src/Ai/Raid/BlackTemple/Action/RaidBlackTempleActions.cpp +++ b/src/Ai/Raid/BlackTemple/Action/RaidBlackTempleActions.cpp @@ -1941,7 +1941,7 @@ bool IllidanStormrageMainTankRepositionBossAction::MoveToShadowTrap(GameObject* if (distToTrap <= 0.0f) return false; - constexpr float distBeyondTrap = 4.0f; + constexpr float distBeyondTrap = 6.0f; const float dx = trapX - bot->GetPositionX(); const float dy = trapY - bot->GetPositionY(); diff --git a/src/Ai/Raid/BlackwingLair/Action/RaidBwlActions.cpp b/src/Ai/Raid/BlackwingLair/Action/RaidBwlActions.cpp index 9014345cc02..2d018fc91e6 100644 --- a/src/Ai/Raid/BlackwingLair/Action/RaidBwlActions.cpp +++ b/src/Ai/Raid/BlackwingLair/Action/RaidBwlActions.cpp @@ -1,35 +1,40 @@ #include "RaidBwlActions.h" #include "Playerbots.h" +#include "RaidBwlHelpers.h" + +using namespace BlackwingLairHelpers; + +// General bool BwlOnyxiaScaleCloakAuraCheckAction::Execute(Event /*event*/) { - bot->AddAura(22683, bot); + bot->AddAura(SPELL_ONYXIA_SCALE_CLOAK, bot); return true; } -bool BwlOnyxiaScaleCloakAuraCheckAction::isUseful() { return !bot->HasAura(22683); } +bool BwlOnyxiaScaleCloakAuraCheckAction::isUseful() +{ + return !bot->HasAura(SPELL_ONYXIA_SCALE_CLOAK); +} bool BwlTurnOffSuppressionDeviceAction::Execute(Event /*event*/) { GuidVector gos = AI_VALUE(GuidVector, "nearest game objects"); - for (GuidVector::iterator i = gos.begin(); i != gos.end(); i++) + for (auto i = gos.begin(); i != gos.end(); ++i) { GameObject* go = botAI->GetGameObject(*i); - if (!go) + if (IsActiveSuppressionDeviceInRange(go, bot)) { - continue; + go->SetGoState(GO_STATE_ACTIVE); } - if (go->GetEntry() != 179784 || go->GetDistance(bot) >= 15.0f || go->GetGoState() != GO_STATE_READY) - { - continue; - } - go->SetGoState(GO_STATE_ACTIVE); } return true; } +// Chromaggus + bool BwlUseHourglassSandAction::Execute(Event /*event*/) { - return botAI->CastSpell(23645, bot); + return botAI->CastSpell(SPELL_HOURGLASS_SAND, bot); } diff --git a/src/Ai/Raid/BlackwingLair/Action/RaidBwlActions.h b/src/Ai/Raid/BlackwingLair/Action/RaidBwlActions.h index 24dcf3996e0..27037414aa1 100644 --- a/src/Ai/Raid/BlackwingLair/Action/RaidBwlActions.h +++ b/src/Ai/Raid/BlackwingLair/Action/RaidBwlActions.h @@ -2,11 +2,8 @@ #define _PLAYERBOT_RAIDBWLACTIONS_H #include "Action.h" -#include "AttackAction.h" -#include "GenericActions.h" -#include "MovementActions.h" -#include "PlayerbotAI.h" -#include "Playerbots.h" + +// General class BwlOnyxiaScaleCloakAuraCheckAction : public Action { @@ -23,6 +20,8 @@ class BwlTurnOffSuppressionDeviceAction : public Action bool Execute(Event event) override; }; +// Chromaggus + class BwlUseHourglassSandAction : public Action { public: diff --git a/src/Ai/Raid/BlackwingLair/Strategy/RaidBwlStrategy.cpp b/src/Ai/Raid/BlackwingLair/Strategy/RaidBwlStrategy.cpp index c65a80ecbaf..ec36b2cde64 100644 --- a/src/Ai/Raid/BlackwingLair/Strategy/RaidBwlStrategy.cpp +++ b/src/Ai/Raid/BlackwingLair/Strategy/RaidBwlStrategy.cpp @@ -1,15 +1,13 @@ #include "RaidBwlStrategy.h" -#include "Strategy.h" - void RaidBwlStrategy::InitTriggers(std::vector& triggers) { - triggers.push_back(new TriggerNode("often", - { NextAction("bwl check onyxia scale cloak", ACTION_RAID) })); + triggers.push_back(new TriggerNode("often", { + NextAction("bwl check onyxia scale cloak", ACTION_RAID) })); - triggers.push_back(new TriggerNode("bwl suppression device", - { NextAction("bwl turn off suppression device", ACTION_RAID) })); + triggers.push_back(new TriggerNode("bwl suppression device", { + NextAction("bwl turn off suppression device", ACTION_RAID) })); - triggers.push_back(new TriggerNode("bwl affliction bronze", - { NextAction("bwl use hourglass sand", ACTION_RAID) })); + triggers.push_back(new TriggerNode("bwl affliction bronze", { + NextAction("bwl use hourglass sand", ACTION_RAID) })); } diff --git a/src/Ai/Raid/BlackwingLair/Strategy/RaidBwlStrategy.h b/src/Ai/Raid/BlackwingLair/Strategy/RaidBwlStrategy.h index e09ea2f3ee5..375ddad7198 100644 --- a/src/Ai/Raid/BlackwingLair/Strategy/RaidBwlStrategy.h +++ b/src/Ai/Raid/BlackwingLair/Strategy/RaidBwlStrategy.h @@ -8,9 +8,9 @@ class RaidBwlStrategy : public Strategy { public: RaidBwlStrategy(PlayerbotAI* ai) : Strategy(ai) {} - virtual std::string const getName() override { return "bwl"; } - virtual void InitTriggers(std::vector& triggers) override; - // virtual void InitMultipliers(std::vector &multipliers) override; + std::string const getName() override { return "bwl"; } + void InitTriggers(std::vector& triggers) override; + // void InitMultipliers(std::vector &multipliers) override; }; #endif diff --git a/src/Ai/Raid/BlackwingLair/Trigger/RaidBwlTriggers.cpp b/src/Ai/Raid/BlackwingLair/Trigger/RaidBwlTriggers.cpp index e34e7a9a6af..4b4bee1adf2 100644 --- a/src/Ai/Raid/BlackwingLair/Trigger/RaidBwlTriggers.cpp +++ b/src/Ai/Raid/BlackwingLair/Trigger/RaidBwlTriggers.cpp @@ -1,24 +1,29 @@ #include "RaidBwlTriggers.h" -#include "SharedDefines.h" +#include "Playerbots.h" +#include "RaidBwlHelpers.h" + +using namespace BlackwingLairHelpers; + +// General bool BwlSuppressionDeviceTrigger::IsActive() { GuidVector gos = AI_VALUE(GuidVector, "nearest game objects"); - for (GuidVector::iterator i = gos.begin(); i != gos.end(); i++) + for (auto i = gos.begin(); i != gos.end(); ++i) { - GameObject* go = botAI->GetGameObject(*i); - if (!go) - { - continue; - } - if (go->GetEntry() != 179784 || go->GetDistance(bot) >= 15.0f || go->GetGoState() != GO_STATE_READY) + const GameObject* go = botAI->GetGameObject(*i); + if (IsActiveSuppressionDeviceInRange(go, bot)) { - continue; + return true; } - return true; } return false; } -bool BwlAfflictionBronzeTrigger::IsActive() { return bot->HasAura(23170); } +// Chromaggus + +bool BwlAfflictionBronzeTrigger::IsActive() +{ + return bot->HasAura(SPELL_BROOD_AFFLICTION_BRONZE); +} diff --git a/src/Ai/Raid/BlackwingLair/Trigger/RaidBwlTriggers.h b/src/Ai/Raid/BlackwingLair/Trigger/RaidBwlTriggers.h index fc19acd5d35..0aa29000720 100644 --- a/src/Ai/Raid/BlackwingLair/Trigger/RaidBwlTriggers.h +++ b/src/Ai/Raid/BlackwingLair/Trigger/RaidBwlTriggers.h @@ -1,10 +1,10 @@ #ifndef _PLAYERBOT_RAIDBWLTRIGGERS_H #define _PLAYERBOT_RAIDBWLTRIGGERS_H -#include "PlayerbotAI.h" -#include "Playerbots.h" #include "Trigger.h" +// General + class BwlSuppressionDeviceTrigger : public Trigger { public: @@ -12,6 +12,8 @@ class BwlSuppressionDeviceTrigger : public Trigger bool IsActive() override; }; +// Chromaggus + class BwlAfflictionBronzeTrigger : public Trigger { public: diff --git a/src/Ai/Raid/BlackwingLair/Util/RaidBwlHelpers.cpp b/src/Ai/Raid/BlackwingLair/Util/RaidBwlHelpers.cpp new file mode 100644 index 00000000000..cc0714bd134 --- /dev/null +++ b/src/Ai/Raid/BlackwingLair/Util/RaidBwlHelpers.cpp @@ -0,0 +1,12 @@ +#include "RaidBwlHelpers.h" + +namespace BlackwingLairHelpers +{ + bool IsActiveSuppressionDeviceInRange(const GameObject* go, const Player* bot) + { + return go && + go->GetEntry() == GO_SUPPRESSION_DEVICE && + go->GetDistance(bot) < 15.0f && + go->GetGoState() == GO_STATE_READY; + } +} diff --git a/src/Ai/Raid/BlackwingLair/Util/RaidBwlHelpers.h b/src/Ai/Raid/BlackwingLair/Util/RaidBwlHelpers.h new file mode 100644 index 00000000000..3333d826d9d --- /dev/null +++ b/src/Ai/Raid/BlackwingLair/Util/RaidBwlHelpers.h @@ -0,0 +1,27 @@ +#ifndef _PLAYERBOT_RAIDBWLHELPERS_H +#define _PLAYERBOT_RAIDBWLHELPERS_H + +#include "Player.h" + +namespace BlackwingLairHelpers +{ + enum BlackwingLairSpells + { + // General + SPELL_ONYXIA_SCALE_CLOAK = 22683, + + // Chromaggus + SPELL_BROOD_AFFLICTION_BRONZE = 23170, + SPELL_HOURGLASS_SAND = 23645 + }; + + enum BlackwingLairGameObjects + { + // General + GO_SUPPRESSION_DEVICE = 179784 + }; + + bool IsActiveSuppressionDeviceInRange(const GameObject* go, const Player* bot); +} + +#endif //_PLAYERBOT_RAIDBWLHELPERS_H diff --git a/src/Ai/Raid/ICC/Action/ICCActions.h b/src/Ai/Raid/ICC/Action/ICCActions.h new file mode 100644 index 00000000000..23a8d2c6933 --- /dev/null +++ b/src/Ai/Raid/ICC/Action/ICCActions.h @@ -0,0 +1,878 @@ +#ifndef _PLAYERBOT_ICCA_H +#define _PLAYERBOT_ICCA_H + +#include + +#include "Action.h" +#include "MovementActions.h" +#include "PlayerbotAI.h" +#include "Playerbots.h" +#include "AttackAction.h" +#include "LastMovementValue.h" +#include "ObjectGuid.h" +#include "PlayerbotAIConfig.h" +#include "ICCStrategy.h" +#include "ScriptedCreature.h" +#include "SharedDefines.h" +#include "Trigger.h" +#include "CellImpl.h" +#include "GridNotifiers.h" +#include "GridNotifiersImpl.h" +#include "Vehicle.h" +#include "ICCTriggers.h" + +inline const Position ICC_LM_TANK_POSITION = Position(-391.0f, 2259.0f, 42.0f); +inline const Position ICC_LM_BONE_STORM_AT_POSITION = Position(-390.02332f, 2179.3481f, 41.96729f); +inline const Position ICC_LM_MID_POSITION = Position(-393.61722f, 2216.335f, 41.99396f); +inline const Position ICC_DARK_RECKONING_SAFE_POSITION = Position(-523.33386f, 2211.2031f, 62.823116f); +inline const Position ICC_LDW_TANK_POSTION = Position(-593.7436f, 2211.298f, 49.476673f); //old closer to stairs -570.1f, 2211.2456f, 49.476616f +inline const Position ICC_LDW_RANGED_POSITION = Position(-653.11096f, 2211.9568f,51.551437f); //old in frint of the boss (-607.0631f, 2212.2488f, 49.47009f) +inline const Position ICC_ROTTING_FROST_GIANT_TANK_POSITION = Position(-328.5085f, 2225.5142f, 199.97298f); +inline const Position ICC_GUNSHIP_ROCKET_JUMP_ALLY = Position (-370.04645f, 1993.3536f, 466.65656f); +inline const Position ICC_GUNSHIP_ROCKET_JUMP_ALLY2 = Position (-392.66208f, 2064.893f, 466.5672f); +inline const Position ICC_GUNSHIP_ROCKET_JUMP_ALLY_MIDDLE_POINT = Position(-383.01166f, 2022.8046f, 467.38193f); +inline const Position ICC_GUNSHIP_ROCKET_JUMP_ALLY_FRIENDLY_POINT = Position(-389.26788f, 2045.9136f, 467.56168f); +inline const Position ICC_GUNSHIP_ROCKET_JUMP_HORDE = Position (-449.5343f, 2477.2024f, 471.31906f); +inline const Position ICC_GUNSHIP_ROCKET_JUMP_HORDE2 = Position (-429.81586f, 2400.6804f, 471.56537f); +inline const Position ICC_GUNSHIP_ROCKET_JUMP_HORDE_MIDDLE_POINT = Position(-449.17645f, 2449.2705f, 470.9257f); +inline const Position ICC_GUNSHIP_ROCKET_JUMP_HORDE_FRIENDLY_POINT = Position(-444.74533f, 2420.1208f, 470.78748f); +inline const Position ICC_DBS_TANK_POSITION = Position(-494.26517f, 2211.549f, 541.11414f); +inline const Position ICC_FESTERGUT_TANK_POSITION = Position(4269.1772f, 3144.7673f, 360.38577f); +inline const Position ICC_FESTERGUT_RANGED_SPORE = Position(4264.3623f, 3120.889f, 360.38565f); //old closer to gates 4261.143f, 3109.4146f, 360.38605f +inline const Position ICC_FESTERGUT_RANGED_SPORE_2 = Position(4288.7974f, 3115.6274f, 360.38577f); //new position +inline const Position ICC_FESTERGUT_MELEE_SPORE = Position(4269.1772f, 3144.7673f, 360.38577f); +inline const Position ICC_ROTFACE_RANGED_POSITION_HC_1 = Position(4453.2085f, 3108.7488f, 360.38626f); +inline const Position ICC_ROTFACE_RANGED_POSITION_HC_2 = Position(4459.4390f, 3118.0210f, 360.38626f); +inline const Position ICC_ROTFACE_RANGED_POSITION_HC_3 = Position(4473.4062f, 3126.2861f, 360.38626f); +inline const Position ICC_ROTFACE_RANGED_POSITION_HC_4 = Position(4469.9370f, 3137.0173f, 360.38626f); +inline const Position ICC_ROTFACE_RANGED_POSITION_HC_5 = Position(4476.7583f, 3145.7160f, 360.38626f); +inline const Position ICC_ROTFACE_RANGED_POSITION_HC_6 = Position(4466.2100f, 3150.0630f, 360.38626f); +inline const Position ICC_ROTFACE_RANGED_POSITION_HC_7 = Position(4464.4297f, 3161.2317f, 360.38626f); +inline const Position ICC_ROTFACE_RANGED_POSITION_HC_8 = Position(4447.2900f, 3163.0322f, 360.38626f); +inline const Position ICC_ROTFACE_RANGED_POSITION_HC_9 = Position(4437.1250f, 3168.4062f, 360.38626f); +inline const Position ICC_ROTFACE_RANGED_POSITION_HC_10 = Position(4417.2217f, 3148.8909f, 360.38626f); +inline const Position ICC_ROTFACE_RANGED_POSITION_HC_11 = Position(4420.9727f, 3137.7700f, 360.38626f); +inline const Position ICC_ROTFACE_RANGED_POSITION_HC_12 = Position(4414.8716f, 3127.9534f, 360.38626f); +inline const Position ICC_ROTFACE_RANGED_POSITION_HC_13 = Position(4425.1553f, 3123.4717f, 360.38626f); +inline const Position ICC_ROTFACE_RANGED_POSITION_HC_14 = Position(4426.3760f, 3113.4153f, 360.38626f); +inline const Position ICC_ROTFACE_RANGED_POSITION_HC_15 = Position(4443.8027f, 3113.9207f, 360.38626f); +inline const Position ICC_ROTFACE_RANGED_POSITION_HC_16 = Position(4432.477f, 3156.7651f, 360.38568f); +inline const Position ICC_ROTFACE_RANGED_POSITION_HC_17 = Position(4458.083f, 3132.5842f, 360.38565f); +inline const Position ICC_ROTFACE_RANGED_POSITION_HC_18 = Position(4457.4565f, 3144.8442f, 360.38565f); +inline const Position ICC_ROTFACE_RANGED_POSITION_HC_19 = Position(4422.5460f, 3158.0435f, 360.38565f); +inline const Position ICC_ROTFACE_RANGED_POSITION_HC_20 = Position(4432.1646f, 3142.3418f, 360.38565f); +inline const Position ICC_ROTFACE_RANGED_POSITION_HC_21 = Position(4436.649f, 3126.1245f, 360.38565f); +inline const Position ICC_ROTFACE_RANGED_POSITION_HC_22 = Position(4450.2363f, 3122.4033f, 360.38565f); +inline const Position ICC_ROTFACE_EXPLOSION_POSITION_1 = Position(4481.433f, 3137.0117f, 360.38522f); +inline const Position ICC_ROTFACE_EXPLOSION_POSITION_2 = Position(4474.4565f, 3156.9617f, 360.38522f); +inline const Position ICC_ROTFACE_EXPLOSION_POSITION_3 = Position(4452.0586f, 3170.2231f, 360.38522f); +inline const Position ICC_ROTFACE_EXPLOSION_POSITION_4 = Position(4421.2437f, 3161.2275f, 360.38522f); +inline const Position ICC_ROTFACE_EXPLOSION_POSITION_5 = Position(4410.904f, 3136.9976f, 360.38522f); +inline const Position ICC_ROTFACE_EXPLOSION_POSITION_6 = Position(4417.603f, 3119.4712f, 360.38522f); +inline const Position ICC_ROTFACE_EXPLOSION_POSITION_7 = Position(4442.514f, 3105.2234f, 360.38522f); +inline const Position ICC_ROTFACE_EXPLOSION_POSITION_8 = Position(4465.7173f, 3113.6284f, 360.38522f); +inline const Position ICC_ROTFACE_TANK_POSITION = Position(4447.061f, 3150.9758f, 360.38568f); +inline const Position ICC_ROTFACE_BIG_OOZE_POSITION = Position(4432.687f, 3142.3035f, 360.38568f); +inline const Position ICC_ROTFACE_SAFE_POSITION = Position(4446.557f, 3065.6594f, 360.38568f); +inline const Position ICC_ROTFACE_CENTER_POSITION = Position(4446.0547f, 3144.8677f, 360.38568f); //actual center 4.74089 4445.6616f, 3137.1526f, 360.38608 +inline const Position ICC_ROTFACE_CENTER_POSITION_BOSS = Position(4445.656f, 3137.1663f, 360.38565f); +inline const Position ICC_PUTRICIDE_TANK_POSITION = Position(4373.227f, 3222.058f, 389.4029f); +inline const Position ICC_PUTRICIDE_GREEN_POSITION = Position(4423.4126f, 3194.2715f, 389.37683f); +inline const Position ICC_PUTRICIDE_GATE_POSITION = Position(4356.3345f, 3167.9407f, 389.39825f); +inline const Position ICC_BPC_OT_POSITION = Position(4649.2236f, 2796.0972f, 361.1815f); +inline const Position ICC_BPC_MT_POSITION = Position(4648.5674f, 2744.847f, 361.18222f); +inline const Position ICC_BPC_CENTER_POSITION = Position(4638.7056f, 2769.3713f, 361.17108f); +inline const Position ICC_BQL_CENTER_POSITION = Position(4595.0f, 2769.0f, 400.0f); +inline const Position ICC_BQL_LWALL1_POSITION = Position(4624.685f, 2789.4895f, 400.13834f); +inline const Position ICC_BQL_LWALL2_POSITION = Position(4600.749f, 2805.7568f, 400.1374f); +inline const Position ICC_BQL_LWALL3_POSITION = Position(4572.857f, 2797.3872f, 400.1374f); +inline const Position ICC_BQL_RWALL1_POSITION = Position(4625.724f, 2748.9917f, 400.13693f); +inline const Position ICC_BQL_RWALL2_POSITION = Position(4608.3774f, 2735.7466f, 400.13693f); +inline const Position ICC_BQL_RWALL3_POSITION = Position(4576.813f, 2739.6067f, 400.13693f); +inline const Position ICC_BQL_LRWALL4_POSITION = Position(4539.345f, 2769.3853f, 403.7267f); +inline const Position ICC_BQL_TANK_POSITION = Position(4633.7964f, 2769.2515f, 401.74777f); //old 4629.746f, 2769.6396f, 401.7479f +inline const Position ICC_VDW_HEAL_POSITION = Position(4203.752f, 2483.4343f, 364.87274f); +inline const Position ICC_VDW_GROUP1_POSITION = Position(4203.585f, 2464.422f, 364.87323f); +inline const Position ICC_VDW_GROUP2_POSITION = Position(4203.5806f, 2505.2383f, 364.87323f); +inline const Position ICC_VDW_PORTALSTART_POSITION = Position(4202.637f, 2488.171f, 375.00256f); +inline const Position ICC_SINDRAGOSA_TANK_POSITION = Position(4408.016f, 2508.0647f, 203.37955f); //X: 4183.136 Y: 2492.9358 Z: 364.87595 Orientation: 5.3975997 +inline const Position ICC_SINDRAGOSA_FLYING_POSITION = Position(4525.6f, 2485.15f, 245.082f); +inline const Position ICC_SINDRAGOSA_RANGED_POSITION = Position(4441.572f, 2484.482f, 203.37836f); +inline const Position ICC_SINDRAGOSA_MELEE_POSITION = Position(4423.1646f, 2486.6792f, 203.3749f); //old 4423.4546f, 2491.7175f, 203.37686f +inline const Position ICC_SINDRAGOSA_BLISTERING_COLD_POSITION = Position(4473.6616f, 2484.8489f, 203.38258f); +inline const Position ICC_SINDRAGOSA_THOMB1_POSITION = Position(4433.6484f, 2469.4133f, 203.3806f); +inline const Position ICC_SINDRAGOSA_THOMB2_POSITION = Position(4434.143f, 2486.201f, 203.37473f); +inline const Position ICC_SINDRAGOSA_THOMB3_POSITION = Position(4436.1147f, 2501.464f, 203.38266f); +inline const Position ICC_SINDRAGOSA_UNCHAINEDMAGIC1_POSITION = Position(4444.9707f, 2455.7322f, 203.38701f); +inline const Position ICC_SINDRAGOSA_UNCHAINEDMAGIC2_POSITION = Position(4461.3945f, 2463.5513f, 203.38727f); +inline const Position ICC_SINDRAGOSA_UNCHAINEDMAGIC3_POSITION = Position(4473.6616f, 2484.8489f, 203.38258f); +inline const Position ICC_SINDRAGOSA_UNCHAINEDMAGIC4_POSITION = Position(4459.9336f, 2507.409f, 203.38606f); +inline const Position ICC_SINDRAGOSA_UNCHAINEDMAGIC5_POSITION = Position(4442.3096f, 2512.4688f, 203.38647f); +inline const Position ICC_SINDRAGOSA_CENTER_POSITION = Position(4408.0464f, 2484.478f, 203.37529f); +inline const Position ICC_SINDRAGOSA_THOMBMB2_POSITION = Position(4436.895f, 2498.1401f, 203.38133f); +inline const Position ICC_SINDRAGOSA_FBOMB_POSITION = Position(4449.3647f, 2486.4524f, 203.379f); +inline const Position ICC_SINDRAGOSA_FBOMB10_POSITION = Position(4449.3647f, 2486.4524f, 203.379f); +inline const Position ICC_SINDRAGOSA_LOS2_POSITION = Position(4441.8286f, 2501.946f, 203.38435f); +inline const Position ICC_LICH_KING_ADDS_POSITION = Position(476.7332f, -2095.3894f, 840.857f); // old 486.63647f, -2095.7915f, 840.857f +inline const Position ICC_LICH_KING_MELEE_POSITION = Position(503.5546f, -2106.8213f, 840.857f); +inline const Position ICC_LICH_KING_RANGED_POSITION = Position(501.3563f, -2085.1816f, 840.857f); +inline const Position ICC_LICH_KING_ASSISTHC_POSITION = Position(517.2145f, -2125.0674f, 840.857f); +inline const Position ICC_LICH_KING_CENTER_POSITION = Position(503.62036f, -2124.7336f, 840.857f); +inline const Position ICC_LK_FROST1_POSITION = Position(503.96548f, -2183.216f, 840.857f); +inline const Position ICC_LK_FROST2_POSITION = Position(563.07166f, -2125.7578f, 840.857f); +inline const Position ICC_LK_FROST3_POSITION = Position(503.40182f, -2067.3435f, 840.857f); +inline const Position ICC_LK_FROSTR1_POSITION = Position(481.168f, -2177.8723f, 840.857f); +inline const Position ICC_LK_FROSTR2_POSITION = Position(562.20807f, -2100.2393f, 840.857f); +inline const Position ICC_LK_FROSTR3_POSITION = Position(526.35297f, -2071.0317f, 840.857f); +inline const Position ICC_LK_VILE_SPIRIT1_POSITION = Position(505.24002f, -2086.7778f, 840.857f); +inline const Position ICC_LK_VILE_SPIRIT2_POSITION = Position(532.668f, -2122.603f, 840.857f); +inline const Position ICC_LK_VILE_SPIRIT3_POSITION = Position(502.8796f, -2159.7466f, 840.857f); + +//Lord Marrogwar +class IccLmTankPositionAction : public AttackAction +{ +public: + IccLmTankPositionAction(PlayerbotAI* botAI, std::string const name = "icc lm tank position") + : AttackAction(botAI, name) {} + bool Execute(Event event) override; + + bool MoveTowardPosition(Position const& position, float incrementSize); +}; + +class IccSpikeAction : public AttackAction +{ +public: + IccSpikeAction(PlayerbotAI* botAI) : AttackAction(botAI, "icc spike") {} + bool Execute(Event event) override; + + std::vector FindAliveSpikes(); + bool HandleSpikeMarking(std::vector const& spikes, Unit* boss); + bool HandleNoSpikesMarking(Unit* boss); + bool HandleSpikeAssignment(std::vector const& spikes, Unit* boss); + bool MoveTowardPosition(Position const& position, float incrementSize); + static std::vector CalculateBalancedGroupSizes(size_t totalMembers, size_t numSpikes); + static size_t GetAssignedSpikeIndex(size_t memberIndex, std::vector const& groupSizes); + static std::string GetRTIValueForSpike(size_t spikeIndex); + bool IsSpikeInColdFlame(Unit* spike); + static Player* GetSpikeVictim(Unit* spike); +}; + +//Lady Deathwhisper +class IccDarkReckoningAction : public MovementAction +{ +public: + IccDarkReckoningAction(PlayerbotAI* botAI, std::string const name = "icc dark reckoning") + : MovementAction(botAI, name) {} + bool Execute(Event event) override; +}; + +class IccRangedPositionLadyDeathwhisperAction : public AttackAction +{ +public: + IccRangedPositionLadyDeathwhisperAction(PlayerbotAI* botAI, std::string const name = "icc ranged position lady deathwhisper") + : AttackAction(botAI, name) {} + bool Execute(Event event) override; + + bool MaintainRangedSpacing(); +}; + +class IccAddsLadyDeathwhisperAction : public AttackAction +{ +public: + IccAddsLadyDeathwhisperAction(PlayerbotAI* botAI, std::string const name = "icc adds lady deathwhisper") + : AttackAction(botAI, name) {} + bool Execute(Event event) override; + + bool IsTargetedByShade(uint32 shadeEntry); + bool MoveTowardPosition(Position const& position, float incrementSize); + bool HandleAddTargeting(Unit* boss); + bool UpdateRaidTargetIcon(Unit* target); + bool HandleNonTankAddEvasion(); + bool IsAdd(Unit* unit); + bool IsAssistTankAlive(); + bool ApplyNearbyAddCC(); + bool ApplyCCToAdd(Unit* add); + bool IsAddsAlive(); + bool EngageBoss(); + Unit* FindAndCollectAdd(Unit* boss); + Unit* FindAddNearBoss(Unit* boss, float maxDist); + +}; + +class IccShadeLadyDeathwhisperAction : public MovementAction +{ +public: + IccShadeLadyDeathwhisperAction(PlayerbotAI* botAI, std::string const name = "icc shade lady deathwhisper") + : MovementAction(botAI, name) {} + bool Execute(Event event) override; +}; + +//Gunship Battle +class IccRottingFrostGiantTankPositionAction : public AttackAction +{ +public: + IccRottingFrostGiantTankPositionAction(PlayerbotAI* botAI, std::string const name = "icc rotting frost giant tank position") + : AttackAction(botAI, name) {} + bool Execute(Event event) override; +}; + +// Gunship Battle +class IccCannonFireAction : public Action +{ +public: + IccCannonFireAction(PlayerbotAI* botAI, std::string const name = "icc cannon fire") : Action(botAI, name) {} + bool Execute(Event event) override; + +private: + Unit* FindValidCannonTarget(); + bool TryCastCannonSpell(uint32 spellId, Unit* target, Unit* vehicleBase); +}; + +class IccGunshipEnterCannonAction : public MovementAction +{ +public: + IccGunshipEnterCannonAction(PlayerbotAI* botAI, std::string const name = "icc gunship enter cannon") + : MovementAction(botAI, name) {} + bool Execute(Event event) override; + +private: + Unit* FindBestAvailableCannon(); + bool IsValidCannon(Unit* vehicle); + bool EnterVehicle(Unit* vehicleBase, bool moveIfFar); +}; + +class IccGunshipRocketJumpAction : public AttackAction +{ +public: + IccGunshipRocketJumpAction(PlayerbotAI* botAI, std::string const name = "icc gunship rocket jump") + : AttackAction(botAI, name) {} + bool Execute(Event event) override; + +private: + enum class GunshipSide + { + NONE, + ALLY, + HORDE + }; + + GunshipSide DetectShip() const; + Item* FindRocketPack() const; + bool UseRocketPack(Position const& destination, bool walkIfOutOfRange); + bool RocketPackJumpToward(Position const& target); + bool ExitCannonIfSeated(); + bool CleanupSkullIcon(uint8 skullIconIndex); + bool UpdateBossSkullIcon(Unit* boss, uint8 skullIconIndex); + bool IsMainTankOnEnemyShip(GunshipSide side) const; + bool AnyNonTankAwayFromFriendly(GunshipSide side) const; + Unit* FindNearestFriendlyCannon(GunshipSide side) const; +}; + +class IccGunshipRocketPackSetupAction : public MovementAction +{ +public: + IccGunshipRocketPackSetupAction(PlayerbotAI* botAI, std::string const name = "icc gunship rocket pack setup") + : MovementAction(botAI, name) {} + bool Execute(Event event) override; + +private: + Item* FindRocketPack() const; + bool AcquireRocketPack(); + bool EquipRocketPack(); +}; + +//DBS +class IccDbsTankPositionAction : public AttackAction +{ +public: + IccDbsTankPositionAction(PlayerbotAI* botAI, std::string const name = "icc dbs tank position") + : AttackAction(botAI, name) {} + bool Execute(Event event) override; + + bool CrowdControlBloodBeasts(); + bool EvadeBloodBeasts(); + bool PositionInRangedFormation(); +}; + +class IccAddsDbsAction : public AttackAction +{ +public: + IccAddsDbsAction(PlayerbotAI* botAI, std::string const name = "icc adds dbs") + : AttackAction(botAI, name) {} + bool Execute(Event event) override; + + Unit* FindPriorityTarget(Unit* boss); + bool UpdateSkullMarker(Unit* priorityTarget); +}; + +class IccDogsTankPositionAction : public AttackAction +{ +public: + IccDogsTankPositionAction(PlayerbotAI* botAI, std::string const name = "icc dogs tank position") + : AttackAction(botAI, name) {} + bool Execute(Event event) override; +}; + +//FESTERGUT +class IccFestergutGroupPositionAction : public AttackAction +{ +public: + IccFestergutGroupPositionAction(PlayerbotAI* botAI, std::string const name = "icc festergut group position") + : AttackAction(botAI, name) {} + bool Execute(Event event) override; + + bool HasSporesInGroup(); + bool PositionNonTankMembers(); + int CalculatePositionIndex(Group* group); +}; + +class IccFestergutSporeAction : public AttackAction +{ +public: + IccFestergutSporeAction(PlayerbotAI* botAI, std::string const name = "icc festergut spore") + : AttackAction(botAI, name) {} + bool Execute(Event event) override; + + Position CalculateSpreadPosition(); + struct SporeInfo + { + std::vector sporedPlayers; + ObjectGuid lowestGuid; + bool hasLowestGuid = false; + }; + SporeInfo FindSporedPlayers(); + Position DetermineTargetPosition(bool hasSpore, const SporeInfo& sporeInfo, const Position& spreadRangedPos); + bool CheckMainTankSpore(); + bool GooNear(Position const& pos); +}; + +class IccFestergutAvoidMalleableGooAction : public MovementAction +{ +public: + IccFestergutAvoidMalleableGooAction(PlayerbotAI* botAI, + std::string const name = "icc festergut avoid malleable goo") + : MovementAction(botAI, name) {} + bool Execute(Event event) override; +}; + +//Rotface +class IccRotfaceTankPositionAction : public AttackAction +{ +public: + IccRotfaceTankPositionAction(PlayerbotAI* botAI, std::string const name = "icc rotface tank position") + : AttackAction(botAI, name) {} + bool Execute(Event event) override; + + bool MarkBossWithSkull(Unit* boss); + bool PositionMainTankAndMelee(Unit* boss, Unit* smallOoze = nullptr); + bool HandleAssistTankPositioning(Unit* boss); + Unit* FindAssignedBigOoze(Unit* boss, std::vector& bigOozes); + bool HandleBigOozeKiting(Unit* bigOoze); +}; + +class IccRotfaceGroupPositionAction : public AttackAction +{ +public: + IccRotfaceGroupPositionAction(PlayerbotAI* botAI, std::string const name = "icc rotface group position") + : AttackAction(botAI, name) {} + bool Execute(Event event) override; + + //bool MoveAwayFromBigOoze(Unit* bigOoze); + bool HandlePuddleAvoidance(Unit* boss); + bool MoveAwayFromPuddle(Unit* boss, Unit* puddle, float puddleDistance); + bool HandleOozeTargeting(); + bool HandleOozeMemberPositioning(Unit* mySmallOoze); + bool PositionRangedAndHealers(Unit* boss,Unit* smallOoze); + bool PositionHeroicGrid(Unit* boss); +}; + +class IccRotfaceMoveAwayFromExplosionAction : public MovementAction +{ +public: + IccRotfaceMoveAwayFromExplosionAction(PlayerbotAI* botAI, std::string const name = "icc rotface move away from explosion") + : MovementAction(botAI, name), + _escapePosition(0.0f, 0.0f, 0.0f), + _hasEscape(false), + _holdUntil(0) {} + bool Execute(Event event) override; + +private: + Position _escapePosition; + bool _hasEscape; + uint32 _holdUntil; +}; + +class IccRotfaceAvoidVileGasAction : public MovementAction +{ +public: + IccRotfaceAvoidVileGasAction(PlayerbotAI* botAI, std::string const name = "icc rotface avoid vile gas") + : MovementAction(botAI, name), + _safeSpot(0.0f, 0.0f, 0.0f), + _hasSafeSpot(false) {} + bool Execute(Event event) override; + +private: + Position _safeSpot; + bool _hasSafeSpot; +}; + +//PP +class IccPutricideMutatedPlagueAction : public AttackAction +{ +public: + IccPutricideMutatedPlagueAction(PlayerbotAI* botAI, std::string const name = "icc putricide mutated plague") + : AttackAction(botAI, name) {} + bool Execute(Event event) override; +}; + +class IccPutricideGrowingOozePuddleAction : public AttackAction +{ +public: + IccPutricideGrowingOozePuddleAction(PlayerbotAI* botAI, std::string const name = "icc putricide growing ooze puddle") + : AttackAction(botAI, name) {} + bool Execute(Event event) override; + + Unit* FindClosestThreateningPuddle(); + Position CalculateSafeMovePosition(Unit* closestPuddle); + bool IsPositionTooCloseToOtherPuddles(float x, float y, Unit* ignorePuddle); + bool PathCrossesAnyPuddle(float fromX, float fromY, float toX, float toY, Unit* ignorePuddle); +}; + +class IccPutricideVolatileOozeAction : public AttackAction +{ +public: + IccPutricideVolatileOozeAction(PlayerbotAI* botAI, std::string const name = "icc putricide volatile ooze") + : AttackAction(botAI, name) {} + bool Execute(Event event) override; + + bool MarkOozeWithSkull(Unit* ooze); + Unit* FindAuraTarget(); +}; + +class IccPutricideGasCloudAction : public AttackAction +{ +public: + IccPutricideGasCloudAction(PlayerbotAI* botAI, std::string const name = "icc putricide gas cloud") + : AttackAction(botAI, name) {} + bool Execute(Event event) override; + + bool HandleGaseousBloatMovement(Unit* gasCloud); + bool HandleGroupAuraSituation(Unit* gasCloud); + bool GroupHasGaseousBloat(Group* group); +}; + +class IccPutricideAvoidMalleableGooAction : public MovementAction +{ +public: + IccPutricideAvoidMalleableGooAction(PlayerbotAI* botAI, std::string const name = "icc putricide avoid malleable goo") + : MovementAction(botAI, name) {} + bool Execute(Event event) override; + + bool HandleTankPositioning(Unit* boss); + bool HandleUnboundPlague(Unit* boss); + bool HandleBossPositioning(Unit* boss); + bool HasObstacleBetween(Position const& from, Position const& to); + bool IsOnPath(Position const& from, Position const& to, Position const& point, float threshold); +}; + +class IccPutricideAbominationAction : public AttackAction +{ +public: + IccPutricideAbominationAction(PlayerbotAI* botAI, std::string const name = "icc putricide abomination") + : AttackAction(botAI, name) {} + bool Execute(Event event) override; + +private: + bool BecomeAbomination(); + bool IsSomeoneAlreadyPiloting(); + Unit* FindClosestPuddle(float maxRange); + Unit* PickSlashTarget(Unit* boss); + bool TryRegurgitate(Unit* abo, Unit* target); + bool TryEatOoze(Unit* abo, Unit* puddle); +}; + +//BPC +class IccBpcKelesethTankAction : public AttackAction +{ +public: + IccBpcKelesethTankAction(PlayerbotAI* botAI) + : AttackAction(botAI, "icc bpc keleseth tank") {} + bool Execute(Event event) override; +}; + +class IccBpcMainTankAction : public AttackAction +{ +public: + IccBpcMainTankAction(PlayerbotAI* botAI) + : AttackAction(botAI, "icc bpc main tank") {} + bool Execute(Event event) override; + + bool MarkEmpoweredPrince(); +}; + +class IccBpcEmpoweredVortexAction : public MovementAction +{ +public: + IccBpcEmpoweredVortexAction(PlayerbotAI* botAI) + : MovementAction(botAI, "icc bpc empowered vortex") {} + bool Execute(Event event) override; + + bool MaintainRangedSpacing(); + bool HandleEmpoweredVortexSpread(); +}; + +class IccBpcKineticBombAction : public AttackAction +{ +public: + IccBpcKineticBombAction(PlayerbotAI* botAI) + : AttackAction(botAI, "icc bpc kinetic bomb") {} + bool Execute(Event event) override; + + Unit* FindNearestBomb(); +}; + +class IccBpcBallOfFlameAction : public MovementAction +{ +public: + IccBpcBallOfFlameAction(PlayerbotAI* botAI) + : MovementAction(botAI, "icc bpc ball of flame") {} + bool Execute(Event event) override; +}; + +//Blood Queen Lana'thel +class IccBqlGroupPositionAction : public AttackAction +{ +public: + IccBqlGroupPositionAction(PlayerbotAI* botAI) + : AttackAction(botAI, "icc bql group position") {} + bool Execute(Event event) override; + + bool HandleTankPosition(Unit* boss, Aura* frenzyAura, Aura* shadowAura); + bool HandleShadowsMovement(); + Position AdjustControlPoint(const Position& wall, const Position& center, float factor); + Position CalculateBezierPoint(float t, const Position path[4]); + bool HandleGroupPosition(Unit* boss, Aura* frenzyAura, Aura* shadowAura); + +private: + // Evaluate curves + struct CurveInfo + { + Position moveTarget; + int curveIdx = 0; + bool foundSafe = false; + float minDist = 0.0f; + float score = 0.0f; + Position closestPoint; + float t_closest = 0.0f; + }; +}; + +class IccBqlPactOfDarkfallenAction : public MovementAction +{ +public: + IccBqlPactOfDarkfallenAction(PlayerbotAI* botAI) + : MovementAction(botAI, "icc bql pact of darkfallen") {} + bool Execute(Event event) override; + + bool CalculateCenterPosition(Position& targetPos, const std::vector& playersWithAura); + bool MoveToTargetPosition(const Position& targetPos, int auraCount); +}; + +class IccBqlVampiricBiteAction : public AttackAction +{ +public: + IccBqlVampiricBiteAction(PlayerbotAI* botAI) + : AttackAction(botAI, "icc bql vampiric bite") {} + bool Execute(Event event) override; + + Player* FindBestBiteTarget(Group* group); + bool IsInvalidTarget(Player* player); + bool MoveTowardsTarget(Player* target); + bool CastVampiricBite(Player* target); +}; + +// Sister Svalna +class IccValkyreSpearAction : public AttackAction +{ +public: + IccValkyreSpearAction(PlayerbotAI* botAI) + : AttackAction(botAI, "icc valkyre spear") {} + bool Execute(Event event) override; +}; + +class IccSisterSvalnaAction : public AttackAction +{ +public: + IccSisterSvalnaAction(PlayerbotAI* botAI) + : AttackAction(botAI, "icc sister svalna") {} + bool Execute(Event event) override; +}; + +// Valithria Dreamwalker + +class IccValithriaGroupAction : public AttackAction +{ +public: + IccValithriaGroupAction(PlayerbotAI* botAI) + : AttackAction(botAI, "icc valithria group") {} + bool Execute(Event event) override; + + bool Handle25ManGroupLogic(); + bool HandleMarkingLogic(bool inGroup1, bool inGroup2, bool singleMarkMode); + bool Handle10ManGroupLogic(); + +private: + bool ApplyCrowdControl(Unit* zombie); +}; + +class IccValithriaZombieKiteAction : public MovementAction +{ +public: + IccValithriaZombieKiteAction(PlayerbotAI* botAI) + : MovementAction(botAI, "icc valithria zombie kite") {} + bool Execute(Event event) override; +}; + +class IccValithriaPortalAction : public MovementAction +{ +public: + IccValithriaPortalAction(PlayerbotAI* botAI) + : MovementAction(botAI, "icc valithria portal") {} + bool Execute(Event event) override; +}; + +class IccValithriaHealAction : public AttackAction +{ +public: + IccValithriaHealAction(PlayerbotAI* botAI) + : AttackAction(botAI, "icc valithria heal") {} + bool Execute(Event event) override; +}; + +class IccValithriaDreamCloudAction : public MovementAction +{ +public: + IccValithriaDreamCloudAction(PlayerbotAI* botAI) + : MovementAction(botAI, "icc valithria dream cloud") {} + bool Execute(Event event) override; + +private: + std::vector CollectClouds(uint32 entry, Unit* reference); +}; + +//Sindragosa +class IccSindragosaGroupPositionAction : public AttackAction +{ +public: + IccSindragosaGroupPositionAction(PlayerbotAI* botAI) + : AttackAction(botAI, "icc sindragosa group position") {} + bool Execute(Event event) override; + + bool HandleTankPositioning(Unit* boss); + bool HandleNonTankPositioning(); + bool MoveIncrementallyToPosition(const Position& targetPos, float maxStep); +}; + +class IccSindragosaFrostBeaconAction : public MovementAction +{ +public: + IccSindragosaFrostBeaconAction(PlayerbotAI* botAI) + : MovementAction(botAI, "icc sindragosa frost beacon") {} + bool Execute(Event event) override; + + bool HandleSupportActions(); + bool HandleBeaconedPlayer(const Unit* boss); + bool HandleNonBeaconedPlayer(const Unit* boss); + bool MoveToPositionIfNeeded(const Position& position, float tolerance); + bool MoveToPosition(const Position& position); + bool IsBossFlying(const Unit* boss); + bool TryDropTombFlares(Unit const* boss); + + private: + static constexpr float POSITION_TOLERANCE = 1.0f; + static constexpr float TOMB_POSITION_TOLERANCE = 0.5f; + static constexpr float MIN_SAFE_DISTANCE = 13.0f; + static constexpr float MOVE_TOLERANCE = 2.0f; + // Keyed per-instance to avoid cross-instance pollution when multiple ICCs run simultaneously + static std::map> s_flaredRedThisPhase; + static std::map s_flaredBluePhase3; + static std::map s_lastPhase3; + static uint32 s_nextFlareMs; + static constexpr uint32 FLARE_ITEM_COOLDOWN_MS = 1000; +}; + +class IccSindragosaHotAction : public Action +{ +public: + IccSindragosaHotAction(PlayerbotAI* botAI) : Action(botAI, "icc sindragosa hot") {} + bool Execute(Event event) override; +}; + +class IccSindragosaBlisteringColdAction : public MovementAction +{ +public: + IccSindragosaBlisteringColdAction(PlayerbotAI* botAI) + : MovementAction(botAI, "icc sindragosa blistering cold") {} + bool Execute(Event event) override; +}; + +class IccSindragosaUnchainedMagicAction : public AttackAction +{ +public: + IccSindragosaUnchainedMagicAction(PlayerbotAI* botAI) + : AttackAction(botAI, "icc sindragosa unchained magic") {} + bool Execute(Event event) override; +}; + +class IccSindragosaChilledToTheBoneAction : public AttackAction +{ +public: + IccSindragosaChilledToTheBoneAction(PlayerbotAI* botAI) + : AttackAction(botAI, "icc sindragosa chilled to the bone") {} + bool Execute(Event event) override; +}; + +class IccSindragosaMysticBuffetAction : public MovementAction +{ +public: + IccSindragosaMysticBuffetAction(PlayerbotAI* botAI) + : MovementAction(botAI, "icc sindragosa mystic buffet") {} + bool Execute(Event event) override; +}; + +class IccSindragosaFrostBombAction : public MovementAction +{ +public: + IccSindragosaFrostBombAction(PlayerbotAI* botAI) + : MovementAction(botAI, "icc sindragosa frost bomb") {} + bool Execute(Event event) override; + +private: + struct FrostBombContext + { + Unit* marker = nullptr; + std::vector tombs; + }; + bool CollectContext(FrostBombContext& ctx) const; + int ResolveGroupIndex(Group* group) const; + void PinGroupToCurrentZone(); + std::vector SelectTombs(std::vector const& tombs, int groupIndex, int groupCount) const; + Unit* ResolveStickyTomb(std::vector const& myTombs); + bool HandleRtiMarking(Group* group, int groupIndex, std::vector const& myTombs, Unit* losTomb); + // Keyed per-instance to avoid cross-instance pollution when multiple ICCs run simultaneously + static std::map, int> s_groupAssignments; + static std::map, ObjectGuid> s_tombAssignments; + static std::set> s_freedFallback; + + // Per-bot last LOS move stamp. When the LOS tomb dies/loses mark mid-walk + // the bot would otherwise freeze in the open. Replaying the last move for + // up to 2 seconds keeps it on its path until a new LOS target is chosen. + struct LastLosMove + { + uint32 timestampMs = 0; + float x = 0.0f; + float y = 0.0f; + float z = 0.0f; + }; + // Keyed per-instance to avoid cross-instance pollution + static std::map, LastLosMove> s_lastLosMove; +}; + +class IccSindragosaTankSwapPositionAction : public AttackAction +{ + public: + IccSindragosaTankSwapPositionAction(PlayerbotAI* botAI) + : AttackAction(botAI, "icc sindragosa tank swap position") {} + bool Execute(Event event) override; +}; + +//LK +class IccLichKingShadowTrapAction : public MovementAction +{ +public: + IccLichKingShadowTrapAction(PlayerbotAI* botAI) + : MovementAction(botAI, "icc lich king shadow trap") {} + bool Execute(Event event) override; +}; + +class IccLichKingNecroticPlagueAction : public MovementAction +{ +public: + IccLichKingNecroticPlagueAction(PlayerbotAI* botAI) + : MovementAction(botAI, "icc lich king necrotic plague") {} + bool Execute(Event event) override; +}; + +class IccLichKingWinterAction : public AttackAction +{ +public: + IccLichKingWinterAction(PlayerbotAI* botAI) + : AttackAction(botAI, "icc lich king winter") {} + bool Execute(Event event) override; + + bool IsPositionSafeFromDefile(float x, float y, float z, float minSafeDistance) const; + bool IsPositionSafeFromShadowTraps(float x, float y) const; + bool IsValidCollectibleAdd(Unit* unit) const; + bool HandleTankPositioning(); + bool HandleMeleePositioning(); + bool HandleRangedPositioning(); + bool HandleMainTankAddManagement(Unit* boss, Position const* tankPos); + bool HandleAssistTankAddManagement(Unit* boss, Position const* tankPos); + bool HandlePetManagement(); + +private: + static constexpr float PLATFORM_Z = 840.857f; + static constexpr float BEHIND_DISTANCE = 4.0f; + bool FixPlatformPosition(); + bool ClearInvalidTarget(); + Position const* GetMainTankPosition(); + Position const* GetMainTankRangedPosition(); + bool TryMoveToPosition(float x, float y, float z, bool forced = true); +}; + +class IccLichKingAddsAction : public AttackAction +{ +public: + IccLichKingAddsAction(PlayerbotAI* botAI) + : AttackAction(botAI, "icc lich king adds") {} + bool Execute(Event event) override; + + bool HandleTeleportationFixes(Difficulty diff, Unit* terenas); + bool HandleHeroicNonTankPositioning(Difficulty diff, Unit* terenas); + bool HandleSpiritMarkingAndTargeting(Difficulty diff, Unit* terenas); + bool HandleQuakeMechanics(Unit* boss); + bool HandleShamblingHorrors(Unit* boss, bool hasPlague); + bool HandleRagingSpiritFlanking(Unit* boss, bool hasPlague, Difficulty diff); + bool HandleAssistTankAddManagement(Unit* boss, Difficulty diff); + bool HandleMeleePositioning(Unit* boss, bool hasPlague, Difficulty diff); + bool HandleMainTankTargeting(Unit* boss, Difficulty diff); + bool HandleNonTankHeroicPositioning(Unit* boss, Difficulty diff, bool hasPlague); + bool HandleRangedPositioning(Unit* boss, bool hasPlague, Difficulty diff); + bool HandleDefileMechanics(Unit* boss, Difficulty diff); + bool HandleCenterStacking(Unit* boss, Difficulty diff); + bool HandleValkyrMechanics(Difficulty diff); + bool HandleValkyrMarking(std::vector const& valkyrs, Difficulty diff); + bool HandleValkyrAssignment(std::vector const& valkyrs); + bool HandleVileSpiritMechanics(); + bool HandleIceSphereMechanics(); + bool ApplyCCToValkyr(Unit* valkyr); + bool IsValkyr(Unit* unit); + std::vector CalculateBalancedGroupSizes(size_t totalAssist, size_t numValkyrs); + size_t GetAssignedValkyrIndex(size_t assistIndex, std::vector const& groupSizes); + std::string GetRTIValueForValkyr(size_t valkyrIndex); + std::pair DefileAwareStep(float tx, float ty, + std::vector const& defiles, + Difficulty diff); +}; + +class IccLichKingSpiritBombAction : public MovementAction +{ +public: + IccLichKingSpiritBombAction(PlayerbotAI* botAI) + : MovementAction(botAI, "icc lich king spirit bomb") {} + bool Execute(Event event) override; + + static bool IsBombThreatActive(PlayerbotAI* botAI, Player* bot); +}; + +#endif diff --git a/src/Ai/Raid/ICC/Action/ICCActions_BPC.cpp b/src/Ai/Raid/ICC/Action/ICCActions_BPC.cpp new file mode 100644 index 00000000000..2b4405b0080 --- /dev/null +++ b/src/Ai/Raid/ICC/Action/ICCActions_BPC.cpp @@ -0,0 +1,821 @@ +#include "ICCActions.h" +#include "NearestNpcsValue.h" +#include "ObjectAccessor.h" +#include "Playerbots.h" +#include "Vehicle.h" +#include "RtiValue.h" +#include "GenericSpellActions.h" +#include "GenericActions.h" +#include "ICCTriggers.h" +#include "Multiplier.h" + +static float const BPC_FLOOR_Z = 361.18222f; + +static bool CastClassTaunt(Player* bot, PlayerbotAI* botAI, Unit* target) +{ + if (!target || !target->IsAlive()) + return false; + + switch (bot->getClass()) + { + case CLASS_PALADIN: + { + bot->RemoveSpellCooldown(SPELL_TAUNT_PALADIN, true); + if (botAI->CastSpell("hand of reckoning", target)) + return true; + break; + } + case CLASS_DEATH_KNIGHT: + { + bot->RemoveSpellCooldown(SPELL_TAUNT_DK, true); + if (botAI->CastSpell("dark command", target)) + return true; + break; + } + case CLASS_DRUID: + { + bot->RemoveSpellCooldown(SPELL_TAUNT_DRUID, true); + if (botAI->CastSpell("growl", target)) + return true; + break; + } + case CLASS_WARRIOR: + { + bot->RemoveSpellCooldown(SPELL_TAUNT_WARRIOR, true); + if (botAI->CastSpell("taunt", target)) + return true; + break; + } + default: + break; + } + + if (botAI->CastSpell("shoot", target) || botAI->CastSpell("throw", target)) + return true; + + return false; +} + +bool IccBpcKelesethTankAction::Execute(Event /*event*/) +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "prince keleseth"); + if (!boss) + return false; + + static float const Z_TELEPORT_THRESHOLD = 6.0f; + + // Teleport Z-axis back to center-level if the bot is significantly off in Z + float centerZ = ICC_BPC_CENTER_POSITION.GetPositionZ(); + if (std::abs(bot->GetPositionZ() - centerZ) > Z_TELEPORT_THRESHOLD) + { + bot->TeleportTo(bot->GetMapId(), bot->GetPositionX(), bot->GetPositionY(), centerZ, bot->GetOrientation()); + return false; + } + + if (!botAI->IsAssistTank(bot)) + { + // Non-assist-tank: pull toward center if too far + static float const CENTER_MOVE_THRESHOLD = 30.0f; + static float const MOVE_INCREMENT = 15.0f; + float distToCenter = bot->GetExactDist2d(ICC_BPC_CENTER_POSITION); + if (distToCenter > CENTER_MOVE_THRESHOLD) + { + float dirX = ICC_BPC_CENTER_POSITION.GetPositionX() - bot->GetPositionX(); + float dirY = ICC_BPC_CENTER_POSITION.GetPositionY() - bot->GetPositionY(); + float length = std::sqrt(dirX * dirX + dirY * dirY); + if (length > 0.001f) + { + dirX /= length; + dirY /= length; + float needed = distToCenter - CENTER_MOVE_THRESHOLD; + float step = std::min(MOVE_INCREMENT, needed); + float moveX = bot->GetPositionX() + dirX * step; + float moveY = bot->GetPositionY() + dirY * step; + MoveTo(bot->GetMapId(), moveX, moveY, bot->GetPositionZ(), false, false, false, false, MovementPriority::MOVEMENT_COMBAT); + return false; + } + } + } + + if (!botAI->IsAssistTank(bot)) + return false; + + bool const isBossVictim = boss->GetVictim() == bot; + + if (!isBossVictim) + { + CastClassTaunt(bot, botAI, boss); + bot->SetTarget(boss->GetGUID()); + bot->SetFacingToObject(boss); + } + else + { + // Collect stray Dark Nuclei via taunt, move toward if out of range + static float const TAUNT_RANGE = 30.0f; + std::list nuclei; + bot->GetCreatureListWithEntryInGrid(nuclei, NPC_DARK_NUCLEUS, 100.0f); + + Unit* strayNucleus = nullptr; + for (Creature* nucleus : nuclei) + { + if (nucleus && nucleus->IsAlive() && nucleus->GetVictim() != bot) + { + strayNucleus = nucleus; + break; + } + } + + if (strayNucleus) + { + float dist = bot->GetExactDist2d(strayNucleus); + if (dist <= TAUNT_RANGE) + CastClassTaunt(bot, botAI, strayNucleus); + else + { + float dirX = strayNucleus->GetPositionX() - bot->GetPositionX(); + float dirY = strayNucleus->GetPositionY() - bot->GetPositionY(); + float length = std::sqrt(dirX * dirX + dirY * dirY); + if (length > 0.001f) + { + dirX /= length; + dirY /= length; + static float const NUCLEUS_MOVE_STEP = 10.0f; + static float const NUCLEUS_APPROACH_BUFFER = 5.0f; + float step = std::min(NUCLEUS_MOVE_STEP, dist - TAUNT_RANGE + NUCLEUS_APPROACH_BUFFER); + float moveX = bot->GetPositionX() + dirX * step; + float moveY = bot->GetPositionY() + dirY * step; + MoveTo(bot->GetMapId(), moveX, moveY, bot->GetPositionZ(), + false, false, false, false, MovementPriority::MOVEMENT_COMBAT); + } + return false; + } + } + } + + bot->SetTarget(boss->GetGUID()); + bot->SetFacingToObject(boss); + Attack(boss); + + return false; +} + +bool IccBpcMainTankAction::Execute(Event /*event*/) +{ + if (!botAI->IsMainTank(bot)) + { + if (botAI->IsTank(bot)) + MarkEmpoweredPrince(); + return false; + } + + Unit* valanar = AI_VALUE2(Unit*, "find target", "prince valanar"); + Unit* taldaram = AI_VALUE2(Unit*, "find target", "prince taldaram"); + + bool const isVictimOfValanar = valanar && valanar->GetVictim() == bot; + bool const isVictimOfTaldaram = taldaram && taldaram->GetVictim() == bot; + + // Move to MT position if targeted by both princes and not already close + static float const MT_POSITION_THRESHOLD = 15.0f; + if (isVictimOfValanar && isVictimOfTaldaram && bot->GetExactDist2d(ICC_BPC_MT_POSITION) > MT_POSITION_THRESHOLD) + { + float dirX = ICC_BPC_MT_POSITION.GetPositionX() - bot->GetPositionX(); + float dirY = ICC_BPC_MT_POSITION.GetPositionY() - bot->GetPositionY(); + float length = std::sqrt(dirX * dirX + dirY * dirY); + if (length > 0.001f) + { + dirX /= length; + dirY /= length; + float moveDist = std::min(3.0f, length); + float moveX = bot->GetPositionX() + dirX * moveDist; + float moveY = bot->GetPositionY() + dirY * moveDist; + MoveTo(bot->GetMapId(), moveX, moveY, bot->GetPositionZ(), false, false, false, false, + MovementPriority::MOVEMENT_COMBAT); + } + } + + // Taunt princes not targeting us + if (valanar && !isVictimOfValanar) + { + CastClassTaunt(bot, botAI, valanar); + bot->SetTarget(valanar->GetGUID()); + bot->SetFacingToObject(valanar); + Attack(valanar); + } + + if (taldaram && !isVictimOfTaldaram) + { + CastClassTaunt(bot, botAI, taldaram); + bot->SetTarget(taldaram->GetGUID()); + bot->SetFacingToObject(taldaram); + Attack(taldaram); + } + + // Taunt nearby hostile adds not targeting a tank + GuidVector const npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); + for (auto const& npc : npcs) + { + Unit* unit = botAI->GetUnit(npc); + if (!unit || !unit->IsAlive()) + continue; + + if (unit->GetEntry() == NPC_PRINCE_KELESETH || unit->GetEntry() == NPC_PRINCE_VALANAR || + unit->GetEntry() == NPC_PRINCE_TALDARAM || unit->GetEntry() == NPC_DARK_NUCLEUS) + continue; + + static float const ADD_TAUNT_RANGE = 20.0f; + if (bot->GetDistance2d(unit) > ADD_TAUNT_RANGE) + continue; + + Unit* victim = unit->GetVictim(); + Player* victimPlayer = victim ? victim->ToPlayer() : nullptr; + if (!victimPlayer || !botAI->IsTank(victimPlayer)) + { + CastClassTaunt(bot, botAI, unit); + break; + } + } + + // Target marking for all tanks, called after main tank priority actions + if (botAI->IsTank(bot)) + MarkEmpoweredPrince(); + + return false; +} + +bool IccBpcMainTankAction::MarkEmpoweredPrince() +{ + static constexpr uint8 SKULL_RAID_ICON = 7; + + // Find empowered prince (Invocation of Blood) + Unit* empoweredPrince = nullptr; + GuidVector const& targets = AI_VALUE(GuidVector, "possible targets"); + + for (auto const& targetGuid : targets) + { + Unit* unit = botAI->GetUnit(targetGuid); + if (!unit || !unit->IsAlive()) + continue; + + if (botAI->HasAura("Invocation of Blood", unit)) + { + uint32 const entry = unit->GetEntry(); + if (entry == NPC_PRINCE_KELESETH || entry == NPC_PRINCE_VALANAR || entry == NPC_PRINCE_TALDARAM) + { + empoweredPrince = unit; + break; + } + } + } + + // Handle marking if we found an empowered prince + if (empoweredPrince && empoweredPrince->IsAlive()) + { + Group* group = bot->GetGroup(); + if (group) + { + ObjectGuid const currentSkullGuid = group->GetTargetIcon(SKULL_RAID_ICON); + Unit* markedUnit = botAI->GetUnit(currentSkullGuid); + + // Clear dead marks or marks that are not on empowered prince + if (markedUnit && (!markedUnit->IsAlive() || markedUnit != empoweredPrince)) + group->SetTargetIcon(SKULL_RAID_ICON, bot->GetGUID(), ObjectGuid::Empty); + + // Mark alive empowered prince if needed + if (!currentSkullGuid || !markedUnit) + group->SetTargetIcon(SKULL_RAID_ICON, bot->GetGUID(), empoweredPrince->GetGUID()); + } + } + + return false; +} + +bool IccBpcEmpoweredVortexAction::Execute(Event /*event*/) +{ + Unit* valanar = AI_VALUE2(Unit*, "find target", "prince valanar"); + if (!valanar) + return false; + + // Check if boss is casting empowered vortex + bool const isCastingVortex = valanar->HasUnitState(UNIT_STATE_CASTING) && + (valanar->FindCurrentSpellBySpellId(SPELL_EMPOWERED_SHOCK_VORTEX1) || + valanar->FindCurrentSpellBySpellId(SPELL_EMPOWERED_SHOCK_VORTEX2) || + valanar->FindCurrentSpellBySpellId(SPELL_EMPOWERED_SHOCK_VORTEX3) || + valanar->FindCurrentSpellBySpellId(SPELL_EMPOWERED_SHOCK_VORTEX4)); + + // Use complex positioning system for empowered vortex + if (isCastingVortex) + return HandleEmpoweredVortexSpread(); + + // Use simple ranged spreading for non-vortex situations + return MaintainRangedSpacing(); +} + +bool IccBpcEmpoweredVortexAction::MaintainRangedSpacing() +{ + static float const IDEAL_RADIUS = 25.0f; + static float const RADIUS_TOLERANCE = 3.0f; + static float const MOVE_INCREMENT = 3.0f; + static float const MIN_SPACING = 13.0f; + + bool const isRanged = botAI->IsRanged(bot) || botAI->IsHeal(bot); + if (!isRanged) + return false; + + Unit* valanar = AI_VALUE2(Unit*, "find target", "prince valanar"); + if (!valanar) + return false; + + // Collect ranged/healer bots sorted by GUID for consistent slot assignment + Group* group = bot->GetGroup(); + if (!group) + return false; + + std::vector rangedBots; + for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next()) + { + Player* member = itr->GetSource(); + if (!member || !member->IsAlive()) + continue; + if (botAI->IsTank(member)) + continue; + PlayerbotAI* memberAI = GET_PLAYERBOT_AI(member); + if (!memberAI) + continue; + if (memberAI->IsRanged(member) || memberAI->IsHeal(member)) + rangedBots.push_back(member); + } + + if (rangedBots.empty()) + return false; + + std::sort(rangedBots.begin(), rangedBots.end(), + [](Player* a, Player* b) { return a->GetGUID() < b->GetGUID(); }); + + // Find this bot's slot index + int slotIndex = -1; + for (size_t i = 0; i < rangedBots.size(); ++i) + { + if (rangedBots[i] == bot) + { + slotIndex = static_cast(i); + break; + } + } + + if (slotIndex < 0) + return false; + + // Calculate angular position around Valanar + float angleStep = 2.0f * static_cast(M_PI) / static_cast(rangedBots.size()); + float targetAngle = angleStep * static_cast(slotIndex); + + // Collect shock vortex positions to avoid + static float const VORTEX_AVOID_DIST = 13.0f; + std::list vortexList; + bot->GetCreatureListWithEntryInGrid(vortexList, NPC_SHOCK_VORTEX, 100.0f); + + // Find safe angle — try assigned slot first, then nudge away from vortices + static float const ANGLE_NUDGE = static_cast(M_PI) / 18.0f; + static int const MAX_NUDGE_STEPS = 18; + float bestAngle = targetAngle; + + auto isAngleSafe = [&](float angle) -> bool + { + float testX = valanar->GetPositionX() + IDEAL_RADIUS * std::cos(angle); + float testY = valanar->GetPositionY() + IDEAL_RADIUS * std::sin(angle); + for (Creature* vortex : vortexList) + { + if (!vortex || !vortex->IsAlive()) + continue; + float vdx = testX - vortex->GetPositionX(); + float vdy = testY - vortex->GetPositionY(); + if (std::sqrt(vdx * vdx + vdy * vdy) < VORTEX_AVOID_DIST) + return false; + } + return true; + }; + + if (!isAngleSafe(bestAngle)) + { + for (int i = 1; i <= MAX_NUDGE_STEPS; ++i) + { + float plusAngle = targetAngle + ANGLE_NUDGE * static_cast(i); + if (isAngleSafe(plusAngle)) + { + bestAngle = plusAngle; + break; + } + float minusAngle = targetAngle - ANGLE_NUDGE * static_cast(i); + if (isAngleSafe(minusAngle)) + { + bestAngle = minusAngle; + break; + } + } + } + + float targetX = valanar->GetPositionX() + IDEAL_RADIUS * std::cos(bestAngle); + float targetY = valanar->GetPositionY() + IDEAL_RADIUS * std::sin(bestAngle); + float targetZ = bot->GetPositionZ(); + + float distToSlot = bot->GetExactDist2d(targetX, targetY); + if (distToSlot <= RADIUS_TOLERANCE) + return false; + + // Move toward assigned slot + float dx = targetX - bot->GetPositionX(); + float dy = targetY - bot->GetPositionY(); + float len = std::sqrt(dx * dx + dy * dy); + if (len < 0.001f) + return false; + + dx /= len; + dy /= len; + float step = std::min(MOVE_INCREMENT, distToSlot); + float moveX = bot->GetPositionX() + dx * step; + float moveY = bot->GetPositionY() + dy * step; + + if (bot->IsWithinLOS(moveX, moveY, targetZ)) + { + MoveTo(bot->GetMapId(), moveX, moveY, targetZ, false, false, false, true, + MovementPriority::MOVEMENT_NORMAL); + } + + return false; +} + +bool IccBpcEmpoweredVortexAction::HandleEmpoweredVortexSpread() +{ + static std::map, uint32> spreadLockTimers; + static uint32 const SPREAD_LOCK_DURATION_MS = 250; + static float const MOVE_INCREMENT = 4.0f; + static float const SLOT_TOLERANCE = 2.0f; + static float const MIN_SPACING = 13.0f; + + if (botAI->IsTank(bot)) + return false; + + Unit* valanar = AI_VALUE2(Unit*, "find target", "prince valanar"); + if (!valanar) + return false; + + // If locked in position, don't move — allow combat/healing + uint32 now = getMSTime(); + + // Prune expired entries to prevent unbounded growth + for (auto it = spreadLockTimers.begin(); it != spreadLockTimers.end(); ) + { + if (getMSTimeDiff(it->second, now) >= SPREAD_LOCK_DURATION_MS) + it = spreadLockTimers.erase(it); + else + ++it; + } + + uint32 const instanceId = bot->GetInstanceId(); + auto it = spreadLockTimers.find({instanceId, bot->GetGUID()}); + if (it != spreadLockTimers.end()) + return false; + + // Collect ALL non-tank bots for spreading (melee must scatter too) + Group* group = bot->GetGroup(); + if (!group) + return false; + + std::vector spreadBots; + for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next()) + { + Player* member = itr->GetSource(); + if (!member || !member->IsAlive()) + continue; + if (botAI->IsTank(member)) + continue; + spreadBots.push_back(member); + } + + if (spreadBots.empty()) + return false; + + std::sort(spreadBots.begin(), spreadBots.end(), + [](Player* a, Player* b) { return a->GetGUID() < b->GetGUID(); }); + + int slotIndex = -1; + for (size_t i = 0; i < spreadBots.size(); ++i) + { + if (spreadBots[i] == bot) + { + slotIndex = static_cast(i); + break; + } + } + + if (slotIndex < 0) + return false; + + // Calculate radius so angular spacing >= MIN_SPACING + // Arc length between slots = 2*pi*R / N, need >= MIN_SPACING + float const botCount = static_cast(spreadBots.size()); + float idealRadius = (botCount * MIN_SPACING) / (2.0f * static_cast(M_PI)); + // Clamp radius to room bounds (room ~40y radius from center) + if (idealRadius < 15.0f) + idealRadius = 15.0f; + if (idealRadius > 38.0f) + idealRadius = 38.0f; + + float angleStep = 2.0f * static_cast(M_PI) / botCount; + float targetAngle = angleStep * static_cast(slotIndex); + + // Collect shock vortex positions to avoid + static float const VORTEX_AVOID_DIST = 13.0f; + std::list vortexList; + bot->GetCreatureListWithEntryInGrid(vortexList, NPC_SHOCK_VORTEX, 100.0f); + + // Find safe angle — try assigned slot first, then nudge away from vortices + static float const ANGLE_NUDGE = static_cast(M_PI) / 18.0f; + static int const MAX_NUDGE_STEPS = 18; + float bestAngle = targetAngle; + + auto isAngleSafe = [&](float angle) -> bool + { + float testX = valanar->GetPositionX() + idealRadius * std::cos(angle); + float testY = valanar->GetPositionY() + idealRadius * std::sin(angle); + for (Creature* vortex : vortexList) + { + if (!vortex || !vortex->IsAlive()) + continue; + float vdx = testX - vortex->GetPositionX(); + float vdy = testY - vortex->GetPositionY(); + if (std::sqrt(vdx * vdx + vdy * vdy) < VORTEX_AVOID_DIST) + return false; + } + return true; + }; + + if (!isAngleSafe(bestAngle)) + { + for (int i = 1; i <= MAX_NUDGE_STEPS; ++i) + { + float plusAngle = targetAngle + ANGLE_NUDGE * static_cast(i); + if (isAngleSafe(plusAngle)) + { + bestAngle = plusAngle; + break; + } + float minusAngle = targetAngle - ANGLE_NUDGE * static_cast(i); + if (isAngleSafe(minusAngle)) + { + bestAngle = minusAngle; + break; + } + } + } + + float targetX = valanar->GetPositionX() + idealRadius * std::cos(bestAngle); + float targetY = valanar->GetPositionY() + idealRadius * std::sin(bestAngle); + float targetZ = bot->GetPositionZ(); + + float distToSlot = bot->GetExactDist2d(targetX, targetY); + + // Close enough to slot — lock position + if (distToSlot <= SLOT_TOLERANCE) + { + spreadLockTimers[{instanceId, bot->GetGUID()}] = now; + return false; + } + + // Move toward assigned slot + float dx = targetX - bot->GetPositionX(); + float dy = targetY - bot->GetPositionY(); + float len = std::sqrt(dx * dx + dy * dy); + if (len < 0.001f) + return false; + + dx /= len; + dy /= len; + float step = std::min(MOVE_INCREMENT, distToSlot); + float moveX = bot->GetPositionX() + dx * step; + float moveY = bot->GetPositionY() + dy * step; + + if (bot->IsWithinLOS(moveX, moveY, targetZ)) + { + MoveTo(bot->GetMapId(), moveX, moveY, targetZ, false, false, false, true, + MovementPriority::MOVEMENT_COMBAT); + } + + return false; +} + +bool IccBpcKineticBombAction::Execute(Event /*event*/) +{ + if (!botAI->IsRangedDps(bot)) + return false; + + static float const MAX_HEIGHT_DIFF = 30.0f; + static float const SAFE_HEIGHT_Z = 371.16473f; + static float const TELEPORT_DOWN_Z = 367.16473f; + + // Fix bot stuck above arena + if (bot->GetPositionZ() > SAFE_HEIGHT_Z) + { + bot->TeleportTo(bot->GetMapId(), bot->GetPositionX(), bot->GetPositionY(), TELEPORT_DOWN_Z, + bot->GetOrientation()); + } + + // Assign bombs 1-to-1 to nearest available ranged DPS + Unit* assignedBomb = FindNearestBomb(); + if (!assignedBomb) + return false; + + // Already attacking a valid bomb that's still low enough, keep going + if (Unit* currentTarget = AI_VALUE(Unit*, "current target")) + { + if (currentTarget == assignedBomb && currentTarget->IsAlive()) + { + float const heightDiff = currentTarget->GetPositionZ() - BPC_FLOOR_Z; + if (heightDiff < MAX_HEIGHT_DIFF) + return false; + } + } + + bot->SetTarget(assignedBomb->GetGUID()); + bot->SetFacingToObject(assignedBomb); + Attack(assignedBomb); + return false; +} + +Unit* IccBpcKineticBombAction::FindNearestBomb() +{ + static constexpr std::array BOMB_ENTRIES = {NPC_KINETIC_BOMB1, NPC_KINETIC_BOMB2, NPC_KINETIC_BOMB3, + NPC_KINETIC_BOMB4}; + static float const MAX_HEIGHT_DIFF = 35.0f; + + GuidVector const targets = AI_VALUE(GuidVector, "possible targets no los"); + if (targets.empty()) + return nullptr; + + // Gather all valid bombs, sorted by Z ascending (lowest = most urgent) + std::vector kineticBombs; + for (auto const& guid : targets) + { + Unit* unit = botAI->GetUnit(guid); + if (!unit || !unit->IsAlive()) + continue; + + if (std::find(BOMB_ENTRIES.begin(), BOMB_ENTRIES.end(), unit->GetEntry()) == BOMB_ENTRIES.end()) + continue; + + if (unit->GetPositionZ() - BPC_FLOOR_Z > MAX_HEIGHT_DIFF) + continue; + + kineticBombs.push_back(unit); + } + + if (kineticBombs.empty()) + return nullptr; + + std::sort(kineticBombs.begin(), kineticBombs.end(), + [](Unit* unitA, Unit* unitB) { return unitA->GetPositionZ() < unitB->GetPositionZ(); }); + + // Gather alive ranged DPS bots only (no real players) + std::vector rangedDps; + Group* group = bot->GetGroup(); + if (group) + { + for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next()) + { + Player* member = itr->GetSource(); + if (member && member->IsAlive() && GET_PLAYERBOT_AI(member) && botAI->IsRangedDps(member)) + rangedDps.push_back(member); + } + } + else + { + rangedDps.push_back(bot); + } + + // Greedy 1-to-1 assignment: hunters first, druids second, then any ranged DPS + static float const MAX_ASSIGN_RANGE = 80.0f; + std::set assigned; + for (Unit* bomb : kineticBombs) + { + Player* nearest = nullptr; + float nearestDist = std::numeric_limits::max(); + + // Priority classes: hunter > druid > any + static constexpr std::array classPriority = {CLASS_HUNTER, CLASS_DRUID, 0}; + for (uint8 priorityClass : classPriority) + { + nearest = nullptr; + nearestDist = std::numeric_limits::max(); + + for (Player* dps : rangedDps) + { + if (assigned.count(dps)) + continue; + + if (priorityClass != 0 && dps->getClass() != priorityClass) + continue; + + float dist = dps->GetDistance(bomb); + if (dist < nearestDist && dist < MAX_ASSIGN_RANGE) + { + nearestDist = dist; + nearest = dps; + } + } + + if (nearest) + break; + } + + if (nearest) + { + assigned.insert(nearest); + if (nearest == bot) + return bomb; + } + } + + return nullptr; +} + +bool IccBpcBallOfFlameAction::Execute(Event /*event*/) +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "prince taldaram"); + if (!boss) + return false; + + Unit* ballOfFlameUnit = bot->FindNearestCreature(NPC_BALL_OF_FLAME, 100.0f); + Unit* infernoFlameUnit = bot->FindNearestCreature(NPC_BALL_OF_INFERNO_FLAME, 100.0f); + + bool const hasBallOfFlame = ballOfFlameUnit && (ballOfFlameUnit->GetVictim() == bot); + bool const hasInfernoFlame = infernoFlameUnit && (infernoFlameUnit->GetVictim() == bot); + + float infernoDist = infernoFlameUnit ? infernoFlameUnit->GetDistance2d(boss) : 0.0f; + // Hunters excluded — they can DPS from range without needing to soak + if (infernoFlameUnit && infernoDist > 2.0f && infernoDist <= 10.0f && !hasInfernoFlame && + bot->getClass() != CLASS_HUNTER) + { + if (!botAI->IsTank(bot) && infernoFlameUnit->GetVictim() != bot) + { + float flameX = infernoFlameUnit->GetPositionX(); + float flameY = infernoFlameUnit->GetPositionY(); + float botX = bot->GetPositionX(); + float botY = bot->GetPositionY(); + + // Calculate direction vector + float dx = flameX - botX; + float dy = flameY - botY; + float distance = std::sqrt(dx * dx + dy * dy); + + // Normalize and scale to 5 units (or remaining distance if less than 5) + float step = std::min(5.0f, distance); + if (distance > 0.001f) + { + dx = dx / distance * step; + dy = dy / distance * step; + } + + // Calculate intermediate position + float newX = botX + dx; + float newY = botY + dy; + + MoveTo(infernoFlameUnit->GetMapId(), newX, newY, bot->GetPositionZ(), false, false, false, true, + MovementPriority::MOVEMENT_COMBAT); + } + } + + // If victim of ball of flame, keep at least 15f from other party members + if (hasBallOfFlame || hasInfernoFlame) + { + static float const SAFE_DIST = 15.0f; + GuidVector members = AI_VALUE(GuidVector, "group members"); + for (auto const& memberGuid : members) + { + Unit* member = botAI->GetUnit(memberGuid); + if (!member || !member->IsAlive() || member == bot) + continue; + float dist = bot->GetExactDist2d(member); + if (dist < SAFE_DIST) + { + // Move away from this member + float dx = bot->GetPositionX() - member->GetPositionX(); + float dy = bot->GetPositionY() - member->GetPositionY(); + float len = std::sqrt(dx * dx + dy * dy); + if (len < 0.001f) + continue; + dx /= len; + dy /= len; + float moveX = bot->GetPositionX() + dx * (SAFE_DIST - dist + 1.0f); + float moveY = bot->GetPositionY() + dy * (SAFE_DIST - dist + 1.0f); + float moveZ = bot->GetPositionZ(); + if (bot->IsWithinLOS(moveX, moveY, moveZ)) + { + MoveTo(bot->GetMapId(), moveX, moveY, moveZ, false, false, false, true, + MovementPriority::MOVEMENT_FORCED); + } + } + } + } + + return false; +} diff --git a/src/Ai/Raid/ICC/Action/ICCActions_BQL.cpp b/src/Ai/Raid/ICC/Action/ICCActions_BQL.cpp new file mode 100644 index 00000000000..4e3602e714e --- /dev/null +++ b/src/Ai/Raid/ICC/Action/ICCActions_BQL.cpp @@ -0,0 +1,1318 @@ +#include "ICCActions.h" +#include "NearestNpcsValue.h" +#include "ObjectAccessor.h" +#include "Playerbots.h" +#include "Vehicle.h" +#include "RtiValue.h" +#include "GenericSpellActions.h" +#include "GenericActions.h" +#include "ICCTriggers.h" +#include "Multiplier.h" + +bool IccBqlGroupPositionAction::Execute(Event /*event*/) +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "blood-queen lana'thel"); + if (!boss) + return false; + + Aura* frenzyAura = botAI->GetAura("Frenzied Bloodthirst", bot); + Aura* shadowAura = botAI->GetAura("Swarming Shadows", bot); + bool isTank = botAI->IsTank(bot); + // Handle tank positioning + if (isTank && HandleTankPosition(boss, frenzyAura, shadowAura)) + return true; + + // Handle swarming shadows movement + if (shadowAura && HandleShadowsMovement()) + return true; + + // Handle group positioning + if (!frenzyAura && !shadowAura && HandleGroupPosition(boss, frenzyAura, shadowAura)) + return true; + + return false; +} + +bool IccBqlGroupPositionAction::HandleTankPosition(Unit* boss, Aura* frenzyAura, Aura* shadowAura) +{ + if (frenzyAura || shadowAura) + return false; + + // Main tank positioning + if (botAI->IsMainTank(bot) && botAI->HasAggro(boss)) + { + if (bot->GetExactDist2d(ICC_BQL_TANK_POSITION) > 3.0f) + { + MoveTo(bot->GetMapId(), ICC_BQL_TANK_POSITION.GetPositionX(), ICC_BQL_TANK_POSITION.GetPositionY(), + ICC_BQL_TANK_POSITION.GetPositionZ(), false, false, false, true, + MovementPriority::MOVEMENT_COMBAT); + } + } + + // Assist tank positioning + if (botAI->IsAssistTank(bot) && !botAI->GetAura("Blood Mirror", bot)) + { + if (Unit* mainTank = AI_VALUE(Unit*, "main tank")) + { + MoveTo(bot->GetMapId(), mainTank->GetPositionX(), mainTank->GetPositionY(), mainTank->GetPositionZ(), + false, false, false, true, MovementPriority::MOVEMENT_COMBAT); + } + } + + if (botAI->IsAssistTank(bot) && botAI->GetAura("Blood Mirror", bot) && boss && boss->HealthAbovePct(90)) + return true; // don't do anything to avoid taking bite + + return false; +} + +bool IccBqlGroupPositionAction::HandleShadowsMovement() +{ + float const SAFE_SHADOW_DIST = 4.0f; + float const ARC_STEP = 0.05f; + float const CURVE_SPACING = 15.0f; + int const MAX_CURVES = 3; + float const maxClosestDist = botAI->IsMelee(bot) ? 25.0f : 20.0f; + Position const& center = ICC_BQL_CENTER_POSITION; + float const OUTER_CURVE_PREFERENCE = 200.0f; // Strong preference for outer curves + float const CURVE_SWITCH_PENALTY = 50.0f; // Penalty for switching curves + float const DISTANCE_PENALTY_FACTOR = 100.0f; // Penalty per yard moved from current position + float const MAX_CURVE_JUMP_DIST = 5.0f; // Maximum distance for jumping between curves + + // Track current curve to avoid unnecessary switching (keyed per-instance to avoid + // cross-instance pollution when multiple ICCs run simultaneously) + static std::map, int> botCurrentCurve; + auto curveKey = std::make_pair(bot->GetInstanceId(), bot->GetGUID()); + int currentCurve = botCurrentCurve.count(curveKey) ? botCurrentCurve[curveKey] : 0; + + // Find closest wall path + Position lwall[4] = {ICC_BQL_LWALL1_POSITION, AdjustControlPoint(ICC_BQL_LWALL2_POSITION, center, 1.30f), + AdjustControlPoint(ICC_BQL_LWALL3_POSITION, center, 1.30f), ICC_BQL_LRWALL4_POSITION}; + Position rwall[4] = {ICC_BQL_RWALL1_POSITION, AdjustControlPoint(ICC_BQL_RWALL2_POSITION, center, 1.30f), + AdjustControlPoint(ICC_BQL_RWALL3_POSITION, center, 1.30f), ICC_BQL_LRWALL4_POSITION}; + Position* basePath = (bot->GetExactDist2d(lwall[0]) < bot->GetExactDist2d(rwall[0])) ? lwall : rwall; + + // Find all swarming shadows + GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); + constexpr int MAX_SHADOW_NPCS = 100; + Unit* shadows[MAX_SHADOW_NPCS]{}; // Reasonable max estimate + int shadowCount = 0; + for (int i = 0; i < npcs.size() && shadowCount < MAX_SHADOW_NPCS; i++) + { + Unit* unit = botAI->GetUnit(npcs[i]); + if (unit && unit->IsAlive() && unit->GetEntry() == NPC_SWARMING_SHADOWS) + shadows[shadowCount++] = unit; + } + + // Helper lambda to check if a position is inside a shadow + auto IsPositionInShadow = [&](Position const& pos) -> bool + { + for (int i = 0; i < shadowCount; ++i) + { + if (pos.GetExactDist2d(shadows[i]) < SAFE_SHADOW_DIST) + return true; + } + return false; + }; + + // If bot is at the 4th position (end of the wall), move towards 3rd position or center to avoid getting stuck + float distToL4 = bot->GetExactDist2d(lwall[3]); + float distToR4 = bot->GetExactDist2d(rwall[3]); + float const STUCK_DIST = 2.0f; // within 2 yards is considered stuck at the end + + if (distToL4 < STUCK_DIST || distToR4 < STUCK_DIST) + { + // Move towards 3rd position of the same wall, or towards center if blocked + Position target; + if (distToL4 < distToR4) + { + target = lwall[2]; + } + else + { + target = rwall[2]; + } + + float tx = target.GetPositionX(); + float ty = target.GetPositionY(); + float tz = target.GetPositionZ(); + bot->UpdateAllowedPositionZ(tx, ty, tz); + if (!bot->IsWithinLOS(tx, ty, tz) || IsPositionInShadow(Position(tx, ty, tz))) + { + tx = center.GetPositionX(); + ty = center.GetPositionY(); + tz = center.GetPositionZ(); + } + + if (bot->GetExactDist2d(tx, ty) > 1.0f) + { + MoveTo(bot->GetMapId(), tx, ty, tz, false, false, false, true, MovementPriority::MOVEMENT_FORCED, + true, false); + } + return false; + } + + CurveInfo bestCurve; + bestCurve.foundSafe = false; + bestCurve.score = FLT_MAX; + bool foundCurve = false; + + // Keep track of information about all curves for possible fallback + CurveInfo curveInfos[MAX_CURVES]; + for (int i = 0; i < MAX_CURVES; i++) + { + curveInfos[i].foundSafe = false; + curveInfos[i].score = FLT_MAX; + } + + // Evaluate all curves starting from outermost (lowest index) + for (int curveIdx = 0; curveIdx < MAX_CURVES; curveIdx++) + { + float curveShrink = float(curveIdx) * CURVE_SPACING; + float shrinkFactor = 1.30f - (curveShrink / 30.0f); + if (shrinkFactor < 1.0f) + shrinkFactor = 1.0f; + + Position path[4] = {basePath[0], AdjustControlPoint(basePath[1], center, shrinkFactor / 1.30f), + AdjustControlPoint(basePath[2], center, shrinkFactor / 1.30f), basePath[3]}; + + // Find closest point on curve + float minDist = 9999.0f; + float t_closest = 0.0f; + Position closestPoint = path[0]; + + for (float t = 0.0f; t <= 1.0f; t += ARC_STEP) + { + Position pt = CalculateBezierPoint(t, path); + float dist = bot->GetExactDist2d(pt); + if (dist < minDist) + { + minDist = dist; + t_closest = t; + closestPoint = pt; + } + } + + // Check if the closest point is safe + bool closestIsSafe = !IsPositionInShadow(closestPoint); + + // Find closest safe point by searching in both directions from closest point + Position safeMoveTarget = closestPoint; + bool foundSafe = closestIsSafe; + + // Only search for safe spots if the closest point isn't already safe + if (!closestIsSafe) + { + // Find the nearest safe point along the curve, not by direct distance + // but by distance along the curve from the closest point + + // Search forward on curve from closest point + float forwardT = -1.0f; + Position forwardPt; + for (float t = t_closest + ARC_STEP; t <= 1.0f; t += ARC_STEP) + { + Position pt = CalculateBezierPoint(t, path); + if (!IsPositionInShadow(pt)) + { + forwardT = t; + forwardPt = pt; + break; + } + } + + // Search backward on curve from closest point + float backwardT = -1.0f; + Position backwardPt; + for (float t = t_closest - ARC_STEP; t >= 0.0f; t -= ARC_STEP) + { + Position pt = CalculateBezierPoint(t, path); + if (!IsPositionInShadow(pt)) + { + backwardT = t; + backwardPt = pt; + break; + } + } + + // Choose the closest safe point based on curve distance, not direct distance + if (forwardT >= 0 && backwardT >= 0) + { + // Both directions have safe points, choose the closer one by curve distance + if (std::abs(forwardT - t_closest) < std::abs(backwardT - t_closest)) + { + safeMoveTarget = forwardPt; + foundSafe = true; + } + else + { + safeMoveTarget = backwardPt; + foundSafe = true; + } + } + else if (forwardT >= 0) + { + safeMoveTarget = forwardPt; + foundSafe = true; + } + else if (backwardT >= 0) + { + safeMoveTarget = backwardPt; + foundSafe = true; + } + } + + // Score this curve + float distancePenalty = 0.0f; + float score = 0.0f; + + if (foundSafe) + { + // If we found a safe point, penalize based on travel distance along the curve to reach it + float safeDist = bot->GetExactDist2d(safeMoveTarget); + + // Add distance penalty based on how far we need to move along the curve + distancePenalty = safeDist * (1.0f / DISTANCE_PENALTY_FACTOR); + score = safeDist + distancePenalty; + } + else + { + // No safe point found, assign a high score + distancePenalty = minDist * (1.0f / DISTANCE_PENALTY_FACTOR); + score = minDist + distancePenalty + 1000.0f; // Penalty for unsafe position + } + + // Apply strong penalty for curves that are too far + if (minDist > maxClosestDist) + score += 500.0f; + + // Apply penalty for unsafe curves + if (!foundSafe) + score += 1000.0f; + + // Apply curve index preference (strongly prefer outer curves) + score += curveIdx * OUTER_CURVE_PREFERENCE; + + // Apply curve switching penalty + if (curveIdx != currentCurve && currentCurve != 0) + score += CURVE_SWITCH_PENALTY; + + // MORE IMPORTANT: Apply additional curve switching penalty if the bot is far away + // from the target curve (prevent jumping between curves when far away) + if (curveIdx != currentCurve && minDist > MAX_CURVE_JUMP_DIST) + score += 2000.0f; // Strong penalty to prevent jumping between curves + + // Store this curve's info + curveInfos[curveIdx].moveTarget = foundSafe ? safeMoveTarget : closestPoint; + curveInfos[curveIdx].foundSafe = foundSafe; + curveInfos[curveIdx].minDist = minDist; + curveInfos[curveIdx].curveIdx = curveIdx; + curveInfos[curveIdx].score = score; + curveInfos[curveIdx].closestPoint = closestPoint; + curveInfos[curveIdx].t_closest = t_closest; + + // Only update if this curve is better than our current best + if (!foundCurve || score < bestCurve.score) + { + bestCurve = curveInfos[curveIdx]; + foundCurve = true; + } + } + + // Fallback: If we're trying to switch to a far curve and we're not near any curve, + // find and use the closest curve instead of making a direct beeline + if (foundCurve && bestCurve.minDist > MAX_CURVE_JUMP_DIST && bestCurve.curveIdx != currentCurve) + { + // Look for the closest curve first + float closestDist = FLT_MAX; + int closestCurveIdx = -1; + + for (int i = 0; i < MAX_CURVES; i++) + { + if (curveInfos[i].minDist < closestDist) + { + closestDist = curveInfos[i].minDist; + closestCurveIdx = i; + } + } + + // If we found a closer curve, use that instead + if (closestCurveIdx >= 0 && closestCurveIdx != bestCurve.curveIdx) + { + bestCurve = curveInfos[closestCurveIdx]; + } + } + + // Remember the selected curve for next time + if (foundCurve) + { + botCurrentCurve[curveKey] = bestCurve.curveIdx; + } + + // Create a move plan to guide the bot along the curve if necessary + if (foundCurve && bot->GetExactDist2d(bestCurve.moveTarget) > 1.0f) + { + // Final check: ensure we're not moving into a shadow + if (!IsPositionInShadow(bestCurve.moveTarget)) + { + // Get the curve + float curveShrink = float(bestCurve.curveIdx) * CURVE_SPACING; + float shrinkFactor = 1.30f - (curveShrink / 30.0f); + if (shrinkFactor < 1.0f) + shrinkFactor = 1.0f; + + Position path[4] = {basePath[0], AdjustControlPoint(basePath[1], center, shrinkFactor / 1.30f), + AdjustControlPoint(basePath[2], center, shrinkFactor / 1.30f), basePath[3]}; + + // CRITICAL CHANGE: First check if we need to move to the curve + float distToClosestPoint = bot->GetExactDist2d(bestCurve.closestPoint); + + // If we're not on the curve yet, first move to the closest point on the curve + if (distToClosestPoint > 2.0f) + { + botAI->Reset(); + return MoveTo(bot->GetMapId(), bestCurve.closestPoint.GetPositionX(), + bestCurve.closestPoint.GetPositionY(), bestCurve.closestPoint.GetPositionZ(), false, + false, false, true, MovementPriority::MOVEMENT_FORCED, true, false); + } + + // Now we know we're on or very close to the curve, so we'll follow it properly + + // Find target point on curve (t_target parameter) + float t_target = 0.0f; + float targetMinDist = 9999.0f; + + for (float t = 0.0f; t <= 1.0f; t += ARC_STEP) + { + Position pt = CalculateBezierPoint(t, path); + float dist = bestCurve.moveTarget.GetExactDist2d(pt); + if (dist < targetMinDist) + { + targetMinDist = dist; + t_target = t; + } + } + + // Find an intermediate point along the curve between closest and target + float t_step = (t_target > bestCurve.t_closest) ? ARC_STEP : -ARC_STEP; + float t_intermediate = bestCurve.t_closest + t_step; + Position intermediateTarget; + bool foundValidIntermediate = false; + + // Limit the distance we move along the curve in one step + float const MAX_CURVE_MOVEMENT = 7.0f; // Max yards to move along curve + float curveDistanceMoved = 0.0f; + Position lastPos = bestCurve.closestPoint; + + while ((t_step > 0 && t_intermediate <= t_target) || (t_step < 0 && t_intermediate >= t_target)) + { + Position pt = CalculateBezierPoint(t_intermediate, path); + + // Check if this point is safe + if (!IsPositionInShadow(pt)) + { + // Calculate distance moved along curve so far + curveDistanceMoved += lastPos.GetExactDist2d(pt); + lastPos = pt; + + // If we've moved the maximum allowed distance, use this position + if (curveDistanceMoved >= MAX_CURVE_MOVEMENT) + { + intermediateTarget = pt; + foundValidIntermediate = true; + break; + } + + // Otherwise, continue moving along the curve + intermediateTarget = pt; + foundValidIntermediate = true; + } + else + { + // We've hit a shadow, stop here + break; + } + + t_intermediate += t_step; + } + + // If we found a valid intermediate point, use it + if (foundValidIntermediate) + { + botAI->Reset(); + MoveTo(bot->GetMapId(), intermediateTarget.GetPositionX(), intermediateTarget.GetPositionY(), + intermediateTarget.GetPositionZ(), false, false, false, true, + MovementPriority::MOVEMENT_FORCED, true, false); + } + + botAI->Reset(); + // Fallback to direct movement to the target point on the curve + MoveTo(bot->GetMapId(), bestCurve.moveTarget.GetPositionX(), bestCurve.moveTarget.GetPositionY(), + bestCurve.moveTarget.GetPositionZ(), false, false, false, true, + MovementPriority::MOVEMENT_FORCED, true, false); + } + } + + return false; +} + +Position IccBqlGroupPositionAction::AdjustControlPoint(Position const& wall, Position const& center, float factor) +{ + float dx = wall.GetPositionX() - center.GetPositionX(); + float dy = wall.GetPositionY() - center.GetPositionY(); + float dz = wall.GetPositionZ() - center.GetPositionZ(); + return Position(center.GetPositionX() + dx * factor, center.GetPositionY() + dy * factor, + center.GetPositionZ() + dz * factor); +} + +Position IccBqlGroupPositionAction::CalculateBezierPoint(float t, Position const path[4]) +{ + float omt = 1 - t; + float omt2 = omt * omt; + float omt3 = omt2 * omt; + float t2 = t * t; + float t3 = t2 * t; + + float x = omt3 * path[0].GetPositionX() + 3 * omt2 * t * path[1].GetPositionX() + + 3 * omt * t2 * path[2].GetPositionX() + t3 * path[3].GetPositionX(); + + float y = omt3 * path[0].GetPositionY() + 3 * omt2 * t * path[1].GetPositionY() + + 3 * omt * t2 * path[2].GetPositionY() + t3 * path[3].GetPositionY(); + + float z = omt3 * path[0].GetPositionZ() + 3 * omt2 * t * path[1].GetPositionZ() + + 3 * omt * t2 * path[2].GetPositionZ() + t3 * path[3].GetPositionZ(); + + return Position(x, y, z); +} + +bool IccBqlGroupPositionAction::HandleGroupPosition(Unit* boss, Aura* frenzyAura, Aura* shadowAura) +{ + if (frenzyAura || shadowAura) + return false; + + GuidVector members = AI_VALUE(GuidVector, "group members"); + bool isRanged = botAI->IsRanged(bot); + bool isTank = botAI->IsTank(bot); + bool isMeleeDps = botAI->IsMelee(bot) && !isTank; + + // Air-phase latch: only arm once boss has been anchored at tank pos (ground phase + // established). Prevents false-trigger at pull when boss comes near center. + // Disarm when boss returns to tank pos (ground phase resumed after landing). + // Keyed per-instance so concurrent ICC raids don't share the latch. + static std::map groundPhaseEstablishedByInstance; + // Tracks airborne state on the previous tick, so we can detect the air->ground edge. + static std::map bossWasAirborneByInstance; + // Armed when boss lands from air, disarmed when boss returns to tank pos. + // While armed, bots skip the pre-air center stack so they don't bunch up at center + // during the post-air walk-back and die to lingering AoE. + static std::map postAirLandingByInstance; + uint32 instanceId = bot->GetInstanceId(); + bool& groundPhaseEstablished = groundPhaseEstablishedByInstance[instanceId]; + bool& wasAirborne = bossWasAirborneByInstance[instanceId]; + bool& postAirLanding = postAirLandingByInstance[instanceId]; + float bossFromTank = boss->GetExactDist2d(ICC_BQL_TANK_POSITION); + float bossFromCenter = boss->GetExactDist2d(ICC_BQL_CENTER_POSITION); + bool bossAirborne = (boss->GetPositionZ() - ICC_BQL_CENTER_POSITION.GetPositionZ()) > 5.0f; + + // Landing edge: arm post-air latch + if (wasAirborne && !bossAirborne) + postAirLanding = true; + wasAirborne = bossAirborne; + + if (!bossAirborne && bossFromTank < 10.0f) + { + groundPhaseEstablished = true; + postAirLanding = false; + } + + bool bossMovingToCenter = groundPhaseEstablished && !bossAirborne && !postAirLanding && + bossFromCenter < 20.0f && bossFromTank > 10.0f; + bool isAirPhase = bossAirborne || bossMovingToCenter; + + // Pre-airborne: nitro boost + move all bots to center (not ring slots yet) + if (bossMovingToCenter) + { + float cx = ICC_BQL_CENTER_POSITION.GetPositionX(); + float cy = ICC_BQL_CENTER_POSITION.GetPositionY(); + float cz = ICC_BQL_CENTER_POSITION.GetPositionZ(); + if (bot->GetExactDist2d(cx, cy) > 2.0f && bot->IsWithinLOS(cx, cy, cz)) + { + MoveTo(bot->GetMapId(), cx, cy, cz, false, false, false, true, + MovementPriority::MOVEMENT_COMBAT, true, false); + return true; // block until at center + } + return false; // at center, let combat rotation run + } + + // Air phase: every bot spreads around room center on concentric rings, hunters outer + if (bossAirborne) + { + // Bloodbolt Whirl is heavy raid-wide damage — pop personal defensive CD to survive + if (boss->FindCurrentSpellBySpellId(SPELL_BLOODBOLT_WHIRL)) + { + static char const* defensives[] = { + "shield wall", "last stand", "icebound fortitude", "survival instincts", + "barkskin", "dispersion", "ice block", "divine shield", "divine protection", + "evasion", "cloak of shadows", "deterrence", "shamanistic rage" + }; + for (char const* spell : defensives) + { + if (botAI->CanCastSpell(spell, bot)) + { + botAI->CastSpell(spell, bot); + break; + } + } + } + + float const SHADOW_AVOID_DIST = 7.0f; + + std::vector hunters; + std::vector nonHunters; + std::vector tanks; + for (auto const& guid : members) + { + Unit* member = botAI->GetUnit(guid); + if (!member || !member->IsAlive()) + continue; + Player* player = member->ToPlayer(); + if (!player) + continue; + if (!sPlayerbotsMgr.GetPlayerbotAI(player)) + continue; + // Frenzied biters roam freely to reach their bite target — exclude from slot pool + // (and tank stack) so we don't reserve a slot for them and so other bots can use + // any slot they left. + if (botAI->GetAura("Frenzied Bloodthirst", player)) + continue; + if (botAI->IsTank(player)) + { + tanks.push_back(player); + continue; + } + if (player->getClass() == CLASS_HUNTER) + hunters.push_back(player); + else + nonHunters.push_back(player); + } + + auto guidSort = [](Player* a, Player* b) { return a->GetGUID() < b->GetGUID(); }; + std::sort(hunters.begin(), hunters.end(), guidSort); + std::sort(nonHunters.begin(), nonHunters.end(), guidSort); + std::sort(tanks.begin(), tanks.end(), guidSort); + + // Tanks stack together at lowest-GUID tank. Anchor tank sits at fixed center position. + if (isTank) + { + Player* anchorTank = tanks.empty() ? nullptr : tanks.front(); + float tx, ty, tz; + if (anchorTank == bot || !anchorTank) + { + tx = ICC_BQL_CENTER_POSITION.GetPositionX(); + ty = ICC_BQL_CENTER_POSITION.GetPositionY(); + tz = ICC_BQL_CENTER_POSITION.GetPositionZ(); + } + else + { + tx = anchorTank->GetPositionX(); + ty = anchorTank->GetPositionY(); + tz = anchorTank->GetPositionZ(); + } + if (bot->GetExactDist2d(tx, ty) > 2.0f && bot->IsWithinLOS(tx, ty, tz)) + { + MoveTo(bot->GetMapId(), tx, ty, tz, false, false, false, true, + MovementPriority::MOVEMENT_COMBAT, true, false); + return true; + } + return false; + } + + // Roster order: hunters first (they get outer slots), then others (inner slots) + std::vector roster; + roster.insert(roster.end(), hunters.begin(), hunters.end()); + roster.insert(roster.end(), nonHunters.begin(), nonHunters.end()); + + int myIndex = -1; + for (int i = 0; i < (int)roster.size(); i++) + { + if (roster[i] == bot) + { + myIndex = i; + break; + } + } + if (myIndex < 0) + return false; + + // 4 rings around boss center, 73 slots total, all pairs >=7f apart, ring gap 8f + struct AirSlot { float radius; float angle; }; + float const D2R = float(M_PI) / 180.0f; + static AirSlot const allSlots[] = { + // Inner ring r=10f, 8 slots at 45° — chord 7.65f + {10.0f, 0.0f * D2R}, {10.0f, 45.0f * D2R}, {10.0f, 90.0f * D2R}, {10.0f, 135.0f * D2R}, + {10.0f, 180.0f * D2R}, {10.0f, 225.0f * D2R}, {10.0f, 270.0f * D2R}, {10.0f, 315.0f * D2R}, + // Ring 2 r=18f, 15 slots at 24° — chord 7.5f + {18.0f, 0.0f * D2R}, {18.0f, 24.0f * D2R}, {18.0f, 48.0f * D2R}, {18.0f, 72.0f * D2R}, + {18.0f, 96.0f * D2R}, {18.0f, 120.0f * D2R}, {18.0f, 144.0f * D2R}, {18.0f, 168.0f * D2R}, + {18.0f, 192.0f * D2R}, {18.0f, 216.0f * D2R}, {18.0f, 240.0f * D2R}, {18.0f, 264.0f * D2R}, + {18.0f, 288.0f * D2R}, {18.0f, 312.0f * D2R}, {18.0f, 336.0f * D2R}, + // Ring 3 r=26f, 20 slots at 18° — chord 8.1f + {26.0f, 0.0f * D2R}, {26.0f, 18.0f * D2R}, {26.0f, 36.0f * D2R}, {26.0f, 54.0f * D2R}, + {26.0f, 72.0f * D2R}, {26.0f, 90.0f * D2R}, {26.0f, 108.0f * D2R}, {26.0f, 126.0f * D2R}, + {26.0f, 144.0f * D2R}, {26.0f, 162.0f * D2R}, {26.0f, 180.0f * D2R}, {26.0f, 198.0f * D2R}, + {26.0f, 216.0f * D2R}, {26.0f, 234.0f * D2R}, {26.0f, 252.0f * D2R}, {26.0f, 270.0f * D2R}, + {26.0f, 288.0f * D2R}, {26.0f, 306.0f * D2R}, {26.0f, 324.0f * D2R}, {26.0f, 342.0f * D2R}, + // Outer ring r=34f, 30 slots at 12° — chord 7.1f + {34.0f, 0.0f * D2R}, {34.0f, 12.0f * D2R}, {34.0f, 24.0f * D2R}, {34.0f, 36.0f * D2R}, + {34.0f, 48.0f * D2R}, {34.0f, 60.0f * D2R}, {34.0f, 72.0f * D2R}, {34.0f, 84.0f * D2R}, + {34.0f, 96.0f * D2R}, {34.0f, 108.0f * D2R}, {34.0f, 120.0f * D2R}, {34.0f, 132.0f * D2R}, + {34.0f, 144.0f * D2R}, {34.0f, 156.0f * D2R}, {34.0f, 168.0f * D2R}, {34.0f, 180.0f * D2R}, + {34.0f, 192.0f * D2R}, {34.0f, 204.0f * D2R}, {34.0f, 216.0f * D2R}, {34.0f, 228.0f * D2R}, + {34.0f, 240.0f * D2R}, {34.0f, 252.0f * D2R}, {34.0f, 264.0f * D2R}, {34.0f, 276.0f * D2R}, + {34.0f, 288.0f * D2R}, {34.0f, 300.0f * D2R}, {34.0f, 312.0f * D2R}, {34.0f, 324.0f * D2R}, + {34.0f, 336.0f * D2R}, {34.0f, 348.0f * D2R}, + }; + int const totalSlots = sizeof(allSlots) / sizeof(allSlots[0]); + int const OUTER_RING_START = 8 + 15 + 20; // first index of outer ring + int const MID_RING_START = 8 + 15; + + float const cx = ICC_BQL_CENTER_POSITION.GetPositionX(); + float const cy = ICC_BQL_CENTER_POSITION.GetPositionY(); + float const cz = ICC_BQL_CENTER_POSITION.GetPositionZ(); + + // Shadow safety (rare during air phase but possible at transition) + std::list shadowList; + bot->GetCreatureListWithEntryInGrid(shadowList, NPC_SWARMING_SHADOWS, 100.0f); + auto IsInShadow = [&](float x, float y) -> bool + { + for (Creature* shadow : shadowList) + { + if (!shadow->IsAlive()) + continue; + float sdx = x - shadow->GetPositionX(); + float sdy = y - shadow->GetPositionY(); + if ((sdx * sdx + sdy * sdy) < SHADOW_AVOID_DIST * SHADOW_AVOID_DIST) + return true; + } + return false; + }; + auto AirSlotPos = [&](int idx, float& x, float& y) + { + float r = allSlots[idx].radius; + float a = allSlots[idx].angle; + x = cx + r * std::cos(a); + y = cy + r * std::sin(a); + }; + auto IsAirSlotSafe = [&](int idx) -> bool + { + float sx, sy; + AirSlotPos(idx, sx, sy); + return !IsInShadow(sx, sy); + }; + + // Persistent memory separate from ground phase (different slot sets). + // Keyed per-instance to avoid cross-instance pollution. + static std::map, int> airSlotMemory; + uint32 const airInstanceId = bot->GetInstanceId(); + + std::vector reservedSlots; + for (Player* rp : roster) + { + if (rp == bot) + continue; + auto it = airSlotMemory.find(std::make_pair(airInstanceId, rp->GetGUID())); + if (it != airSlotMemory.end() && it->second >= 0 && it->second < totalSlots) + reservedSlots.push_back(it->second); + } + auto IsReserved = [&](int s) -> bool + { + return std::find(reservedSlots.begin(), reservedSlots.end(), s) != reservedSlots.end(); + }; + + bool botInShadow = IsInShadow(bot->GetPositionX(), bot->GetPositionY()); + + int myAssignedSlot = -1; + auto myAirKey = std::make_pair(airInstanceId, bot->GetGUID()); + + auto myMemIt = airSlotMemory.find(myAirKey); + if (myMemIt != airSlotMemory.end()) + { + int prev = myMemIt->second; + if (prev >= 0 && prev < totalSlots && !IsReserved(prev) && IsAirSlotSafe(prev)) + myAssignedSlot = prev; + } + + // Pick a new slot — hunters start from outer ring going in, others from inner going out + if (myAssignedSlot < 0) + { + bool isHunter = (bot->getClass() == CLASS_HUNTER); + for (int attempt = 0; attempt < totalSlots; attempt++) + { + int s; + if (isHunter) + s = (totalSlots - 1) - ((myIndex + attempt) % totalSlots); + else + s = (myIndex + attempt) % totalSlots; + if (IsReserved(s)) + continue; + if (IsAirSlotSafe(s)) + { + myAssignedSlot = s; + break; + } + } + } + + if (myAssignedSlot < 0) + { + airSlotMemory.erase(myAirKey); + + // No safe slot available — if standing in shadow, flee away from nearest shadow + if (botInShadow) + { + Creature* nearest = nullptr; + float bestDist = 1e9f; + for (Creature* shadow : shadowList) + { + if (!shadow->IsAlive()) + continue; + float d = bot->GetExactDist2d(shadow); + if (d < bestDist) + { + bestDist = d; + nearest = shadow; + } + } + if (nearest) + { + float dx = bot->GetPositionX() - nearest->GetPositionX(); + float dy = bot->GetPositionY() - nearest->GetPositionY(); + float mag = std::sqrt(dx * dx + dy * dy); + if (mag > 0.001f) + { + float fleeX = bot->GetPositionX() + (dx / mag) * 10.0f; + float fleeY = bot->GetPositionY() + (dy / mag) * 10.0f; + float fleeZ = bot->GetPositionZ(); + bot->UpdateAllowedPositionZ(fleeX, fleeY, fleeZ); + MoveTo(bot->GetMapId(), fleeX, fleeY, fleeZ, false, false, false, true, + MovementPriority::MOVEMENT_FORCED, true, false); + } + } + } + } + else + { + airSlotMemory[myAirKey] = myAssignedSlot; + + float candidateX, candidateY; + AirSlotPos(myAssignedSlot, candidateX, candidateY); + float candidateZ = cz; + bot->UpdateAllowedPositionZ(candidateX, candidateY, candidateZ); + + MovementPriority prio = botInShadow ? MovementPriority::MOVEMENT_FORCED : MovementPriority::MOVEMENT_COMBAT; + float moveGate = botInShadow ? 0.0f : 1.0f; + + if (bot->IsWithinLOS(candidateX, candidateY, candidateZ) && + bot->GetExactDist2d(candidateX, candidateY) > moveGate) + { + MoveTo(bot->GetMapId(), candidateX, candidateY, candidateZ, false, false, false, true, + prio, true, false); + // Still moving to slot — block combat so we actually get there + return true; + } + } + // At slot (or fleeing shadow) — let combat rotation attack/heal + return false; + } + + if (isMeleeDps && !isAirPhase) + { + if (bot->GetDistance2d(boss) > 2.0f) + { + MoveTo(bot->GetMapId(), boss->GetPositionX(), boss->GetPositionY(), boss->GetPositionZ(), false, false, + false, true, MovementPriority::MOVEMENT_COMBAT, true, false); + } + return false; + } + + // Ground phase ranged positioning — persistent slot assignment, only affected bots reassign + if (isRanged && !isAirPhase) + { + float const SHADOW_AVOID_DIST = 7.0f; + + // Gather ranged bots + melee bots (skip real players) + std::vector hunters; + std::vector otherRanged; + std::vector meleeBots; + for (auto const& guid : members) + { + Unit* member = botAI->GetUnit(guid); + if (!member || !member->IsAlive()) + continue; + Player* player = member->ToPlayer(); + if (!player) + continue; + if (!sPlayerbotsMgr.GetPlayerbotAI(player)) + continue; + if (botAI->IsRanged(player)) + { + if (player->getClass() == CLASS_HUNTER) + hunters.push_back(player); + else + otherRanged.push_back(player); + } + else if (botAI->IsMelee(player)) + { + meleeBots.push_back(player); + } + } + + auto guidSort = [](Player* a, Player* b) { return a->GetGUID() < b->GetGUID(); }; + std::sort(hunters.begin(), hunters.end(), guidSort); + std::sort(otherRanged.begin(), otherRanged.end(), guidSort); + std::sort(meleeBots.begin(), meleeBots.end(), guidSort); + + std::vector roster; + roster.insert(roster.end(), hunters.begin(), hunters.end()); + roster.insert(roster.end(), otherRanged.begin(), otherRanged.end()); + + int myIndex = -1; + for (int i = 0; i < (int)roster.size(); i++) + { + if (roster[i] == bot) + { + myIndex = i; + break; + } + } + if (myIndex < 0) + return false; + + // Fixed world-space anchor and direction + float const anchorX = ICC_BQL_TANK_POSITION.GetPositionX(); + float const anchorY = ICC_BQL_TANK_POSITION.GetPositionY(); + float const anchorZ = ICC_BQL_TANK_POSITION.GetPositionZ(); + float baseDx = ICC_BQL_CENTER_POSITION.GetPositionX() - anchorX; + float baseDy = ICC_BQL_CENTER_POSITION.GetPositionY() - anchorY; + float const anchorToCenter = std::atan2(baseDy, baseDx); + + // 19 fixed slots: 6 inner (20f) + 7 middle (27f) + 6 outer (35f) + struct Slot { float radius; float angleOffset; }; + float const D2R = float(M_PI) / 180.0f; + static Slot const allSlots[] = { + {20.0f, -60.0f * D2R}, {20.0f, -36.0f * D2R}, {20.0f, -12.0f * D2R}, + {20.0f, 12.0f * D2R}, {20.0f, 36.0f * D2R}, {20.0f, 60.0f * D2R}, + {27.0f, -45.0f * D2R}, {27.0f, -30.0f * D2R}, {27.0f, -15.0f * D2R}, + {27.0f, 0.0f}, {27.0f, 15.0f * D2R}, {27.0f, 30.0f * D2R}, + {27.0f, 45.0f * D2R}, + {35.0f, -60.0f * D2R}, {35.0f, -36.0f * D2R}, {35.0f, -12.0f * D2R}, + {35.0f, 12.0f * D2R}, {35.0f, 36.0f * D2R}, {35.0f, 60.0f * D2R}, + }; + int const totalSlots = sizeof(allSlots) / sizeof(allSlots[0]); + + // Shadow list + safety check + std::list shadowList; + bot->GetCreatureListWithEntryInGrid(shadowList, NPC_SWARMING_SHADOWS, 100.0f); + auto IsInShadow = [&](float x, float y) -> bool + { + for (Creature* shadow : shadowList) + { + if (!shadow->IsAlive()) + continue; + float sdx = x - shadow->GetPositionX(); + float sdy = y - shadow->GetPositionY(); + if ((sdx * sdx + sdy * sdy) < SHADOW_AVOID_DIST * SHADOW_AVOID_DIST) + return true; + } + return false; + }; + auto SlotPos = [&](int idx, float& x, float& y) + { + float angle = anchorToCenter + allSlots[idx].angleOffset; + float r = allSlots[idx].radius; + x = anchorX + r * std::cos(angle); + y = anchorY + r * std::sin(angle); + }; + auto IsSlotSafe = [&](int idx) -> bool + { + float sx, sy; + SlotPos(idx, sx, sy); + return !IsInShadow(sx, sy); + }; + + // Persistent per-bot slot memory shared across all bots. + // Keyed per-instance to avoid cross-instance pollution. + static std::map, int> botSlotMemory; + uint32 const groundInstanceId = bot->GetInstanceId(); + + // Collect every OTHER bot's remembered slot as "reserved" — each bot owns its own + // memory and we must respect their claim, even if we can't see the same shadows. + // This prevents cascading reassignments when one bot moves. + std::vector reservedSlots; + for (Player* rp : roster) + { + if (rp == bot) + continue; + auto it = botSlotMemory.find(std::make_pair(groundInstanceId, rp->GetGUID())); + if (it != botSlotMemory.end() && it->second >= 0 && it->second < totalSlots) + reservedSlots.push_back(it->second); + } + + auto IsReserved = [&](int s) -> bool + { + return std::find(reservedSlots.begin(), reservedSlots.end(), s) != reservedSlots.end(); + }; + + int myAssignedSlot = -1; + bool myFellBack = false; + auto myGroundKey = std::make_pair(groundInstanceId, bot->GetGUID()); + + // Step 1: keep my remembered slot if still safe and not reserved by someone else + auto myMemIt = botSlotMemory.find(myGroundKey); + if (myMemIt != botSlotMemory.end()) + { + int prev = myMemIt->second; + if (prev >= 0 && prev < totalSlots && !IsReserved(prev) && IsSlotSafe(prev)) + myAssignedSlot = prev; + } + + // Step 2: need a new slot — pick first safe slot not reserved by others + if (myAssignedSlot < 0) + { + // Prefer slots by roster seniority (hunters/lower-GUID first get inner slots) + // Start from index myIndex to fall into "my natural zone", wrap around + for (int attempt = 0; attempt < totalSlots; attempt++) + { + int s = (myIndex + attempt) % totalSlots; + if (IsReserved(s)) + continue; + if (IsSlotSafe(s)) + { + myAssignedSlot = s; + break; + } + } + } + + if (myAssignedSlot < 0) + { + // No safe unreserved slot — fall back to melee. Forget my slot so others can use it. + botSlotMemory.erase(myGroundKey); + myFellBack = true; + } + else + { + botSlotMemory[myGroundKey] = myAssignedSlot; + } + + if (myAssignedSlot >= 0) + { + float candidateX, candidateY; + SlotPos(myAssignedSlot, candidateX, candidateY); + float candidateZ = anchorZ; + bot->UpdateAllowedPositionZ(candidateX, candidateY, candidateZ); + + if (bot->IsWithinLOS(candidateX, candidateY, candidateZ) && + bot->GetExactDist2d(candidateX, candidateY) > 1.0f) + { + MoveTo(bot->GetMapId(), candidateX, candidateY, candidateZ, false, false, false, true, + MovementPriority::MOVEMENT_COMBAT, true, false); + } + } + else if (myFellBack && !meleeBots.empty()) + { + // No safe slot — flee to lowest-GUID melee bot until a safe slot frees up + Player* anchor = meleeBots.front(); + if (anchor != bot) + { + float ax = anchor->GetPositionX(); + float ay = anchor->GetPositionY(); + float az = anchor->GetPositionZ(); + if (bot->GetExactDist2d(ax, ay) > 3.0f && bot->IsWithinLOS(ax, ay, az)) + { + MoveTo(bot->GetMapId(), ax, ay, az, false, false, false, true, + MovementPriority::MOVEMENT_FORCED, true, false); + } + } + } + } + + return false; +} + +bool IccBqlPactOfDarkfallenAction::Execute(Event /*event*/) +{ + // Check if bot has Pact of the Darkfallen + if (!botAI->GetAura("Pact of the Darkfallen", bot)) + return false; + Group* group = bot->GetGroup(); + if (!group) + return false; + + // Find other players with Pact of the Darkfallen + Player* tankWithAura = nullptr; + std::vector playersWithAura; + + for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next()) + { + Player* member = itr->GetSource(); + if (!member || member == bot) + continue; + if (botAI->GetAura("Pact of the Darkfallen", member)) + { + playersWithAura.push_back(member); + if (botAI->IsTank(member)) + tankWithAura = member; + } + } + + if (playersWithAura.empty()) + return false; + + // Determine target position + Position targetPos; + if (tankWithAura) + { + // If there's a tank with aura, everyone moves to the tank (including the tank itself for center positioning) + if (botAI->IsTank(bot)) + { + // If current bot is the tank, stay put or move slightly for better positioning + targetPos.Relocate(bot); + } + else + { + // Non-tank bots move to the tank + targetPos.Relocate(tankWithAura); + } + } + else if (playersWithAura.size() >= 2) + { + // Calculate center position of all players with aura (including bot) + CalculateCenterPosition(targetPos, playersWithAura); + } + else if (playersWithAura.size() == 1) + { + // Move to the single other player with aura + targetPos.Relocate(playersWithAura[0]); + } + else + { + // No valid movement case found + return true; + } + + // Move to target position if needed + return MoveToTargetPosition(targetPos, playersWithAura.size() + 1); // +1 to include the bot itself +} + +bool IccBqlPactOfDarkfallenAction::CalculateCenterPosition(Position& targetPos, const std::vector& playersWithAura) +{ + float sumX = bot->GetPositionX(); + float sumY = bot->GetPositionY(); + float sumZ = bot->GetPositionZ(); + + // Add positions of all other players with aura + for (Player* player : playersWithAura) + { + sumX += player->GetPositionX(); + sumY += player->GetPositionY(); + sumZ += player->GetPositionZ(); + } + + // Calculate average position (center) + int totalPlayers = playersWithAura.size() + 1; // +1 for the bot itself + targetPos.Relocate(sumX / totalPlayers, sumY / totalPlayers, sumZ / totalPlayers); + + return false; +} + +bool IccBqlPactOfDarkfallenAction::MoveToTargetPosition(Position const& targetPos, int auraCount) +{ + float const POSITION_TOLERANCE = 0.1f; + float distance = bot->GetDistance(targetPos); + if (distance <= POSITION_TOLERANCE) + return true; + + // Calculate movement increment + float dx = targetPos.GetPositionX() - bot->GetPositionX(); + float dy = targetPos.GetPositionY() - bot->GetPositionY(); + float dz = targetPos.GetPositionZ() - bot->GetPositionZ(); + float len = sqrt(dx * dx + dy * dy); + + float moveX, moveY, moveZ; + if (len > 5.0f && auraCount <= 2) + { + dx /= len; + dy /= len; + moveX = bot->GetPositionX() + dx * 5.0f; + moveY = bot->GetPositionY() + dy * 5.0f; + moveZ = bot->GetPositionZ() + (dz / distance) * 5.0f; + } + else + { + moveX = targetPos.GetPositionX(); + moveY = targetPos.GetPositionY(); + moveZ = targetPos.GetPositionZ(); + } + + botAI->Reset(); + MoveTo(bot->GetMapId(), moveX, moveY, moveZ, false, false, false, true, MovementPriority::MOVEMENT_FORCED); + return false; +} + +bool IccBqlVampiricBiteAction::Execute(Event /*event*/) +{ + // Only act when bot has Frenzied Bloodthirst + if (!botAI->GetAura("Frenzied Bloodthirst", bot)) + return false; + + // Bloodbolt Whirl defensive CDs (divine shield, ice block, cloak) make bot untargetable + // and block the bite cast. Strip them now so the bite can land. + botAI->RemoveAura("divine shield"); + botAI->RemoveAura("ice block"); + botAI->RemoveAura("cloak of shadows"); + botAI->RemoveAura("deterrence"); + + float const BITE_RANGE = 4.0f; + Group* group = bot->GetGroup(); + if (!group) + return false; + + // Find best target + Player* target = FindBestBiteTarget(group); + if (!target) + return false; + + // Handle movement or casting + if (bot->GetExactDist2d(target) > BITE_RANGE) + return MoveTowardsTarget(target); + + return CastVampiricBite(target); +} + +Player* IccBqlVampiricBiteAction::FindBestBiteTarget(Group* group) +{ + // Collect frenzied biters (sorted by GUID) and all candidate targets. + // Lower-GUID biters claim their nearest valid target first; higher-GUID + // biters skip claimed targets. Deterministic across all bots so two biters + // never converge on the same victim. + std::vector biters; + std::vector dpsHealCandidates; + std::vector tankCandidates; + + for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next()) + { + Player* member = itr->GetSource(); + if (!member || !member->IsAlive()) + continue; + + if (botAI->GetAura("Frenzied Bloodthirst", member)) + { + biters.push_back(member); + continue; + } + + // Skip already-bitten / shadowed / mind-controlled + if (botAI->GetAura("Essence of the Blood Queen", member) || + botAI->GetAura("Uncontrollable Frenzy", member) || + botAI->GetAura("Swarming Shadows", member)) + continue; + + if (botAI->IsTank(member)) + tankCandidates.push_back(member); + else if (botAI->IsDps(member) || botAI->IsHeal(member)) + dpsHealCandidates.push_back(member); + } + + std::sort(biters.begin(), biters.end(), + [](Player* a, Player* b) { return a->GetGUID() < b->GetGUID(); }); + + auto pickFromPool = [&](std::vector& pool) -> Player* + { + std::set claimed; + Player* myPick = nullptr; + for (Player* biter : biters) + { + float bestDist = FLT_MAX; + Player* bestTarget = nullptr; + for (Player* cand : pool) + { + if (claimed.count(cand->GetGUID())) + continue; + float d = biter->GetDistance(cand); + if (d < bestDist) + { + bestDist = d; + bestTarget = cand; + } + } + if (!bestTarget) + continue; + claimed.insert(bestTarget->GetGUID()); + if (biter == bot) + { + myPick = bestTarget; + break; + } + } + return myPick; + }; + + if (Player* pick = pickFromPool(dpsHealCandidates)) + return pick; + + // Fallback: tanks only when no DPS/heal target available + return pickFromPool(tankCandidates); +} + +bool IccBqlVampiricBiteAction::IsInvalidTarget(Player* player) +{ + return botAI->GetAura("Frenzied Bloodthirst", player) || botAI->GetAura("Essence of the Blood Queen", player) || + botAI->GetAura("Uncontrollable Frenzy", player) || botAI->GetAura("Swarming Shadows", player); +} + +bool IccBqlVampiricBiteAction::MoveTowardsTarget(Player* target) +{ + if (IsInvalidTarget(target) || !target->IsAlive()) + return false; + + float x = target->GetPositionX(); + float y = target->GetPositionY(); + float z = target->GetPositionZ(); + + // Don't gate movement on LOS — if blocked, take a step toward target so LOS + // can clear next tick. Multiplier zeroes all non-bite actions for biters, + // so silently aborting here would idle the bot until the aura expires. + + float dx = x - bot->GetPositionX(); + float dy = y - bot->GetPositionY(); + float dz = z - bot->GetPositionZ(); + float len = sqrt(dx * dx + dy * dy); + + float moveX, moveY, moveZ; + if (len > 5.0f) + { + dx /= len; + dy /= len; + moveX = bot->GetPositionX() + dx * 5.0f; + moveY = bot->GetPositionY() + dy * 5.0f; + moveZ = bot->GetPositionZ() + (dz / len) * 5.0f; + } + else + { + moveX = x; + moveY = y; + moveZ = z; + } + + MoveTo(target->GetMapId(), moveX, moveY, moveZ, false, false, false, true, + MovementPriority::MOVEMENT_FORCED); + + return false; +} + +bool IccBqlVampiricBiteAction::CastVampiricBite(Player* target) +{ + if (IsInvalidTarget(target) || !target->IsAlive()) + return false; + + return botAI->CanCastSpell("Vampiric Bite", target) && botAI->CastSpell("Vampiric Bite", target); +} \ No newline at end of file diff --git a/src/Ai/Raid/ICC/Action/ICCActions_DBS.cpp b/src/Ai/Raid/ICC/Action/ICCActions_DBS.cpp new file mode 100644 index 00000000000..648c7ff8a4a --- /dev/null +++ b/src/Ai/Raid/ICC/Action/ICCActions_DBS.cpp @@ -0,0 +1,504 @@ + +#include "GenericActions.h" +#include "GenericSpellActions.h" +#include "Multiplier.h" +#include "NearestNpcsValue.h" +#include "ObjectAccessor.h" +#include "Playerbots.h" +#include "ICCActions.h" +#include "ICCTriggers.h" +#include "RtiValue.h" +#include "Vehicle.h" + +bool IccDbsTankPositionAction::Execute(Event /*event*/) +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "deathbringer saurfang"); + if (!boss) + return false; + + // Class-specific taunt with forced cooldown reset + auto CastClassTaunt = [&](Unit* target) -> bool + { + if (!target || !target->IsAlive()) + return false; + + switch (bot->getClass()) + { + case CLASS_PALADIN: + { + bot->RemoveSpellCooldown(SPELL_TAUNT_PALADIN, true); + if (botAI->CastSpell("hand of reckoning", target)) + return true; + break; + } + case CLASS_DEATH_KNIGHT: + { + bot->RemoveSpellCooldown(SPELL_TAUNT_DK, true); + if (botAI->CastSpell("dark command", target)) + return true; + break; + } + case CLASS_DRUID: + { + bot->RemoveSpellCooldown(SPELL_TAUNT_DRUID, true); + if (botAI->CastSpell("growl", target)) + return true; + break; + } + case CLASS_WARRIOR: + { + bot->RemoveSpellCooldown(SPELL_TAUNT_WARRIOR, true); + if (botAI->CastSpell("taunt", target)) + return true; + break; + } + default: + break; + } + + if (botAI->CastSpell("shoot", target) || botAI->CastSpell("throw", target)) + return true; + + return false; + }; + + if (botAI->IsTank(bot)) + { + bool const hasRuneOfBlood = botAI->GetAura("Rune of Blood", bot) != nullptr; + + if (hasRuneOfBlood) + { + // Stop attacking boss but still taunt loose blood beasts + if (bot->GetVictim() == boss) + bot->AttackStop(); + + std::array const beastEntries = {NPC_BLOOD_BEAST1, NPC_BLOOD_BEAST2, NPC_BLOOD_BEAST3, + NPC_BLOOD_BEAST4}; + GuidVector const npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); + for (auto const& npc : npcs) + { + Unit* unit = botAI->GetUnit(npc); + if (!unit || !unit->IsAlive()) + continue; + + bool const isBloodBeast = + std::find(beastEntries.begin(), beastEntries.end(), unit->GetEntry()) != beastEntries.end(); + if (!isBloodBeast) + continue; + + Unit* victim = unit->GetVictim(); + Player* victimPlayer = victim ? victim->ToPlayer() : nullptr; + if (!victimPlayer || !botAI->IsTank(victimPlayer)) + { + CastClassTaunt(unit); + break; + } + } + + if (bot->GetExactDist2d(ICC_DBS_TANK_POSITION) > 5.0f) + return MoveTo(bot->GetMapId(), ICC_DBS_TANK_POSITION.GetPositionX(), + ICC_DBS_TANK_POSITION.GetPositionY(), ICC_DBS_TANK_POSITION.GetPositionZ(), false, false, + false, true, MovementPriority::MOVEMENT_NORMAL); + return true; + } + + // Tank without Rune of Blood: taunt boss if current tank has the debuff + Unit* currentTarget = boss->GetVictim(); + if (currentTarget && currentTarget != bot && botAI->GetAura("Rune of Blood", currentTarget)) + CastClassTaunt(boss); + + // Taunt any blood beasts not targeting a tank + std::array const bloodBeastEntries = {NPC_BLOOD_BEAST1, NPC_BLOOD_BEAST2, NPC_BLOOD_BEAST3, + NPC_BLOOD_BEAST4}; + GuidVector const npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); + for (auto const& npc : npcs) + { + Unit* unit = botAI->GetUnit(npc); + if (!unit || !unit->IsAlive()) + continue; + + bool const isBloodBeast = std::find(bloodBeastEntries.begin(), bloodBeastEntries.end(), unit->GetEntry()) != + bloodBeastEntries.end(); + if (!isBloodBeast) + continue; + + Unit* victim = unit->GetVictim(); + Player* victimPlayer = victim ? victim->ToPlayer() : nullptr; + if (!victimPlayer || !botAI->IsTank(victimPlayer)) + { + CastClassTaunt(unit); + break; + } + } + + if (bot->GetExactDist2d(ICC_DBS_TANK_POSITION) > 5.0f) + return MoveTo(bot->GetMapId(), ICC_DBS_TANK_POSITION.GetPositionX(), ICC_DBS_TANK_POSITION.GetPositionY(), + ICC_DBS_TANK_POSITION.GetPositionZ(), false, false, false, true, + MovementPriority::MOVEMENT_NORMAL); + + return false; + } + + if (!botAI->IsTank(bot)) + { + if (CrowdControlBloodBeasts()) + return true; + } + + // Handle ranged and healer positioning + if (botAI->IsRanged(bot) || botAI->IsHeal(bot)) + { + // Handle evasion from blood beasts + if (EvadeBloodBeasts()) + return true; + + // Position in formation + return PositionInRangedFormation(); + } + + return false; +} + +bool IccDbsTankPositionAction::CrowdControlBloodBeasts() +{ + std::array const bloodBeastEntries = {NPC_BLOOD_BEAST1, NPC_BLOOD_BEAST2, NPC_BLOOD_BEAST3, + NPC_BLOOD_BEAST4}; + GuidVector const npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); + + for (auto const& npc : npcs) + { + Unit* unit = botAI->GetUnit(npc); + if (!unit || !unit->IsAlive()) + continue; + + // Check if this is a blood beast + bool const isBloodBeast = + std::find(bloodBeastEntries.begin(), bloodBeastEntries.end(), unit->GetEntry()) != bloodBeastEntries.end(); + + if (!isBloodBeast) + continue; + + switch (bot->getClass()) + { + case CLASS_MAGE: + { + if (!botAI->HasAura("Frost Nova", unit)) + return botAI->CastSpell("Frost Nova", unit); + break; + } + case CLASS_DRUID: + { + if (!botAI->HasAura("Entangling Roots", unit)) + return botAI->CastSpell("Entangling Roots", unit); + break; + } + case CLASS_PALADIN: + { + if (!botAI->HasAura("Hammer of Justice", unit)) + return botAI->CastSpell("Hammer of Justice", unit); + break; + } + case CLASS_WARRIOR: + { + if (!botAI->HasAura("Hamstring", unit)) + return botAI->CastSpell("Hamstring", unit); + break; + } + case CLASS_HUNTER: + { + if (!botAI->HasAura("Concussive Shot", unit)) + return botAI->CastSpell("Concussive Shot", unit); + break; + } + case CLASS_ROGUE: + { + if (!botAI->HasAura("Kidney Shot", unit)) + return botAI->CastSpell("Kidney Shot", unit); + break; + } + case CLASS_SHAMAN: + { + if (!botAI->HasAura("Frost Shock", unit)) + return botAI->CastSpell("Frost Shock", unit); + break; + } + case CLASS_DEATH_KNIGHT: + { + if (!botAI->HasAura("Chains of Ice", unit)) + return botAI->CastSpell("Chains of Ice", unit); + break; + } + case CLASS_PRIEST: + { + if (!botAI->HasAura("Psychic Scream", unit)) + return botAI->CastSpell("Psychic Scream", unit); + break; + } + case CLASS_WARLOCK: + { + if (!botAI->HasAura("Fear", unit)) + return botAI->CastSpell("Fear", unit); + break; + } + default: + break; + } + } + + return false; +} + +bool IccDbsTankPositionAction::EvadeBloodBeasts() +{ + float const evasionDistance = 12.0f; + std::array const bloodBeastEntries = {NPC_BLOOD_BEAST1, NPC_BLOOD_BEAST2, NPC_BLOOD_BEAST3, + NPC_BLOOD_BEAST4}; + + // Get the nearest hostile NPCs + GuidVector const npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); + + for (auto const& npc : npcs) + { + Unit* unit = botAI->GetUnit(npc); + if (!unit) + continue; + + // Check if this is a blood beast + bool const isBloodBeast = + std::find(bloodBeastEntries.begin(), bloodBeastEntries.end(), unit->GetEntry()) != bloodBeastEntries.end(); + + // Only evade if it's a blood beast targeting us + if (isBloodBeast && unit->GetVictim() == bot) + { + float const currentDistance = bot->GetDistance2d(unit); + + // Move away if too close + if (currentDistance < evasionDistance) + return MoveAway(unit, evasionDistance - currentDistance); + } + } + + return false; +} + +bool IccDbsTankPositionAction::PositionInRangedFormation() +{ + // Get group + Group* group = bot->GetGroup(); + if (!group) + return false; + + int32 const totalSlots = 15; // 3 rows x 5 cols + uint32 const dbsInstanceId = bot->GetInstanceId(); + + // Persistent per-bot slot memory shared across all bots. + // Keyed per-instance to avoid cross-instance pollution. + static std::map, int> botSlotMemory; + auto myKey = std::make_pair(dbsInstanceId, bot->GetGUID()); + + // Single pass: collect natural index (alive ranged/healer non-tank order) + // and other bots' reserved slots. + int32 myIndex = -1; + int32 currentIndex = 0; + std::vector reservedSlots; + + for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next()) + { + Player* member = itr->GetSource(); + if (!member) + continue; + + if (member != bot) + { + auto it = botSlotMemory.find(std::make_pair(dbsInstanceId, member->GetGUID())); + if (it != botSlotMemory.end() && it->second >= 0 && it->second < totalSlots) + reservedSlots.push_back(it->second); + } + + if (!member->IsAlive()) + continue; + + if ((botAI->IsRanged(member) || botAI->IsHeal(member)) && !botAI->IsTank(member)) + { + if (member == bot) + { + myIndex = currentIndex; + } + currentIndex++; + } + } + + if (myIndex == -1) + return false; + + auto IsReserved = [&](int s) -> bool + { + return std::find(reservedSlots.begin(), reservedSlots.end(), s) != reservedSlots.end(); + }; + + int myAssignedSlot = -1; + + // Step 1: keep my remembered slot if still in range and not reserved by someone else. + auto myMemIt = botSlotMemory.find(myKey); + if (myMemIt != botSlotMemory.end()) + { + int prev = myMemIt->second; + if (prev >= 0 && prev < totalSlots && !IsReserved(prev)) + myAssignedSlot = prev; + } + + // Step 2: pick first unreserved slot, seeded from my natural index. + if (myAssignedSlot < 0) + { + for (int attempt = 0; attempt < totalSlots; attempt++) + { + int s = (myIndex + attempt) % totalSlots; + if (!IsReserved(s)) + { + myAssignedSlot = s; + break; + } + } + } + + // Step 3: overflow (16th+ ranged) - stack with closest non-tank melee bot, + // lowest GUID on tie. Forget my slot so others can take it. + if (myAssignedSlot < 0) + { + botSlotMemory.erase(myKey); + + Player* anchor = nullptr; + float anchorDist = 0.0f; + uint64 anchorGuidRaw = 0; + for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next()) + { + Player* member = itr->GetSource(); + if (!member || member == bot || !member->IsAlive()) + continue; + if (!botAI->IsMelee(member) || botAI->IsTank(member)) + continue; + + float d = bot->GetExactDist2d(member); + uint64 g = member->GetGUID().GetRawValue(); + if (!anchor || d < anchorDist || (d == anchorDist && g < anchorGuidRaw)) + { + anchor = member; + anchorDist = d; + anchorGuidRaw = g; + } + } + + if (!anchor) + return false; + + float ax = anchor->GetPositionX(); + float ay = anchor->GetPositionY(); + float az = anchor->GetPositionZ(); + if (bot->GetExactDist2d(ax, ay) > 3.0f) + return MoveTo(bot->GetMapId(), ax, ay, az, false, false, false, true, + MovementPriority::MOVEMENT_COMBAT); + return false; + } + + botSlotMemory[myKey] = myAssignedSlot; + + // Fixed positions calculation + float const tankToBossAngle = 3.14f; + float const minBossDistance = 11.0f; + float const spreadDistance = 10.0f; + int32 const columnsPerRow = 5; + + // Calculate position in a fixed grid (3 rows x 5 columns) + int32 const row = myAssignedSlot / columnsPerRow; + int32 const col = myAssignedSlot % columnsPerRow; + + // Calculate base position + float xOffset = (col - 2) * spreadDistance; // Center around tank position + float yOffset = minBossDistance + (row * spreadDistance); // Each row further back + + // Add zigzag offset for odd rows + if (row % 2 == 1) + xOffset += spreadDistance / 2.0f; + + // Rotate position based on tank-to-boss angle + float finalX = + ICC_DBS_TANK_POSITION.GetPositionX() + (cos(tankToBossAngle) * yOffset - sin(tankToBossAngle) * xOffset); + float finalY = + ICC_DBS_TANK_POSITION.GetPositionY() + (sin(tankToBossAngle) * yOffset + cos(tankToBossAngle) * xOffset); + float finalZ = ICC_DBS_TANK_POSITION.GetPositionZ(); + + // Update Z coordinate + bot->UpdateAllowedPositionZ(finalX, finalY, finalZ); + + // Move if not in position + if (bot->GetExactDist2d(finalX, finalY) > 3.0f) + return MoveTo(bot->GetMapId(), finalX, finalY, finalZ, false, false, false, true, + MovementPriority::MOVEMENT_COMBAT); + + return false; +} + +bool IccAddsDbsAction::Execute(Event /*event*/) +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "deathbringer saurfang"); + if (!boss) + return false; + + // This action is only for melee + if (!botAI->IsMelee(bot)) + return false; + + Unit* priorityTarget = FindPriorityTarget(boss); + + // Update raid target icons if needed + UpdateSkullMarker(priorityTarget); + + return false; +} + +Unit* IccAddsDbsAction::FindPriorityTarget(Unit* boss) +{ + GuidVector const targets = AI_VALUE(GuidVector, "possible targets no los"); + + // Blood beast entry IDs + std::array const addEntries = {NPC_BLOOD_BEAST1, NPC_BLOOD_BEAST2, NPC_BLOOD_BEAST3, NPC_BLOOD_BEAST4}; + + // First check for alive adds + for (uint32 const entry : addEntries) + { + for (ObjectGuid const& guid : targets) + { + Unit* unit = botAI->GetUnit(guid); + if (unit && unit->IsAlive() && unit->GetEntry() == entry) + return unit; + } + } + + // Only fallback to boss if it's alive + return boss->IsAlive() ? const_cast(boss) : nullptr; +} + +bool IccAddsDbsAction::UpdateSkullMarker(Unit* priorityTarget) +{ + if (!priorityTarget) + return false; + + Group* group = bot->GetGroup(); + if (!group) + return false; + + uint8 const skullIconId = 7; + + // Get current skull target + ObjectGuid const currentSkull = group->GetTargetIcon(skullIconId); + Unit* currentSkullUnit = botAI->GetUnit(currentSkull); + + // Determine if skull marker needs updating + bool const needsUpdate = !currentSkullUnit || !currentSkullUnit->IsAlive() || currentSkullUnit != priorityTarget; + + // Update if needed + if (needsUpdate) + group->SetTargetIcon(skullIconId, bot->GetGUID(), priorityTarget->GetGUID()); + + return false; +} diff --git a/src/Ai/Raid/ICC/Action/ICCActions_Dogs.cpp b/src/Ai/Raid/ICC/Action/ICCActions_Dogs.cpp new file mode 100644 index 00000000000..d7b9f5dc669 --- /dev/null +++ b/src/Ai/Raid/ICC/Action/ICCActions_Dogs.cpp @@ -0,0 +1,110 @@ +#include "GenericActions.h" +#include "GenericSpellActions.h" +#include "Multiplier.h" +#include "Playerbots.h" +#include "ICCActions.h" +#include "ICCTriggers.h" + +bool IccDogsTankPositionAction::Execute(Event /*event*/) +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "stinky"); + if (!boss) + boss = AI_VALUE2(Unit*, "find target", "precious"); + if (!boss) + return false; + + auto CastClassTaunt = [&](Unit* target) -> bool + { + if (!target || !target->IsAlive()) + return false; + + switch (bot->getClass()) + { + case CLASS_PALADIN: + { + bot->RemoveSpellCooldown(SPELL_TAUNT_PALADIN, true); + if (botAI->CastSpell("hand of reckoning", target)) + return true; + break; + } + case CLASS_DEATH_KNIGHT: + { + bot->RemoveSpellCooldown(SPELL_TAUNT_DK, true); + if (botAI->CastSpell("dark command", target)) + return true; + break; + } + case CLASS_DRUID: + { + bot->RemoveSpellCooldown(SPELL_TAUNT_DRUID, true); + if (botAI->CastSpell("growl", target)) + return true; + break; + } + case CLASS_WARRIOR: + { + bot->RemoveSpellCooldown(SPELL_TAUNT_WARRIOR, true); + if (botAI->CastSpell("taunt", target)) + return true; + break; + } + default: + break; + } + + if (botAI->CastSpell("shoot", target) || botAI->CastSpell("throw", target)) + return true; + + return false; + }; + + if (botAI->IsTank(bot)) + { + Aura* aura = botAI->GetAura("mortal wound", bot, false, true); + bool const hasMortalWound = aura && aura->GetStackAmount() >= 8; + + if (hasMortalWound) + { + if (bot->GetVictim() == boss) + bot->AttackStop(); + + return true; + } + + // Tank without high mortal wound stacks: taunt boss if current tank has the debuff + Unit* currentTarget = boss->GetVictim(); + if (currentTarget && currentTarget != bot) + { + Aura* victimAura = botAI->GetAura("mortal wound", currentTarget, false, true); + if (victimAura && victimAura->GetStackAmount() >= 8) + CastClassTaunt(boss); + } + + // Taunt nearby hostile adds not targeting a tank + GuidVector const npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); + for (auto const& npc : npcs) + { + Unit* unit = botAI->GetUnit(npc); + if (!unit || !unit->IsAlive()) + continue; + + if (unit == boss) + continue; + + if (bot->GetDistance2d(unit) > 20.0f) + continue; + + Unit* victim = unit->GetVictim(); + Player* victimPlayer = victim ? victim->ToPlayer() : nullptr; + if (!victimPlayer || !botAI->IsTank(victimPlayer)) + { + CastClassTaunt(unit); + break; + } + } + + return false; + } + + return false; +} diff --git a/src/Ai/Raid/ICC/Action/ICCActions_FG.cpp b/src/Ai/Raid/ICC/Action/ICCActions_FG.cpp new file mode 100644 index 00000000000..f0994e6f5bd --- /dev/null +++ b/src/Ai/Raid/ICC/Action/ICCActions_FG.cpp @@ -0,0 +1,614 @@ +#include "GenericActions.h" +#include "GenericSpellActions.h" +#include "Multiplier.h" +#include "NearestNpcsValue.h" +#include "ObjectAccessor.h" +#include "Playerbots.h" +#include "ICCActions.h" +#include "ICCTriggers.h" +#include "ICCScripts.h" +#include "RtiValue.h" +#include "Vehicle.h" +#include +#include + +// Festergut +bool IccFestergutGroupPositionAction::Execute(Event /*event*/) +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "festergut"); + if (!boss) + return false; + + bot->SetTarget(boss->GetGUID()); + + auto CastClassTaunt = [&](Unit* target) -> bool + { + if (!target || !target->IsAlive()) + return false; + + switch (bot->getClass()) + { + case CLASS_PALADIN: + { + bot->RemoveSpellCooldown(SPELL_TAUNT_PALADIN, true); + if (botAI->CastSpell("hand of reckoning", target)) + return true; + break; + } + case CLASS_DEATH_KNIGHT: + { + bot->RemoveSpellCooldown(SPELL_TAUNT_DK, true); + if (botAI->CastSpell("dark command", target)) + return true; + break; + } + case CLASS_DRUID: + { + bot->RemoveSpellCooldown(SPELL_TAUNT_DRUID, true); + if (botAI->CastSpell("growl", target)) + return true; + break; + } + case CLASS_WARRIOR: + { + bot->RemoveSpellCooldown(SPELL_TAUNT_WARRIOR, true); + if (botAI->CastSpell("taunt", target)) + return true; + break; + } + default: + break; + } + + if (botAI->CastSpell("shoot", target) || botAI->CastSpell("throw", target)) + return true; + + return false; + }; + + if (botAI->IsTank(bot)) + { + Aura* aura = botAI->GetAura("gastric bloat", bot, false, true); + bool const hasGastricBloat = aura && aura->GetStackAmount() >= 6; + + if (hasGastricBloat) + { + if (bot->GetVictim() == boss) + bot->AttackStop(); + + if (bot->GetExactDist2d(ICC_FESTERGUT_TANK_POSITION) > 5.0f) + return MoveTo(bot->GetMapId(), ICC_FESTERGUT_TANK_POSITION.GetPositionX(), + ICC_FESTERGUT_TANK_POSITION.GetPositionY(), ICC_FESTERGUT_TANK_POSITION.GetPositionZ(), + false, false, false, true, MovementPriority::MOVEMENT_NORMAL); + return true; + } + + Unit* currentTarget = boss->GetVictim(); + if (currentTarget && currentTarget != bot) + { + Aura* victimAura = botAI->GetAura("gastric bloat", currentTarget, false, true); + if (victimAura && victimAura->GetStackAmount() >= 6) + CastClassTaunt(boss); + } + + if (bot->GetExactDist2d(ICC_FESTERGUT_TANK_POSITION) > 5.0f) + return MoveTo(bot->GetMapId(), ICC_FESTERGUT_TANK_POSITION.GetPositionX(), + ICC_FESTERGUT_TANK_POSITION.GetPositionY(), ICC_FESTERGUT_TANK_POSITION.GetPositionZ(), false, + false, false, true, MovementPriority::MOVEMENT_NORMAL); + + return false; + } + + // Check for spores in the group + if (HasSporesInGroup()) + return false; + + // No spore, no goo dodge - melee stack on main tank. + if (botAI->IsMelee(bot)) + { + Unit* mainTank = AI_VALUE(Unit*, "main tank"); + if (mainTank && bot->GetExactDist2d(mainTank) > 3.0f) + return MoveTo(bot->GetMapId(), mainTank->GetPositionX(), mainTank->GetPositionY(), + mainTank->GetPositionZ(), false, false, false, true, + MovementPriority::MOVEMENT_NORMAL); + return false; + } + + // Position non-tank ranged and healers + return PositionNonTankMembers(); +} + +bool IccFestergutGroupPositionAction::HasSporesInGroup() +{ + GuidVector const members = AI_VALUE(GuidVector, "group members"); + + for (auto const& memberGuid : members) + { + Unit* unit = botAI->GetUnit(memberGuid); + if (unit && unit->HasAura(SPELL_GAS_SPORE)) + return true; + } + + return false; +} + +bool IccFestergutGroupPositionAction::PositionNonTankMembers() +{ + // Only position ranged and healers without spores + if (!(botAI->IsRanged(bot) || botAI->IsHeal(bot))) + return false; + + Group* group = bot->GetGroup(); + if (!group) + return false; + + int32 positionIndex = CalculatePositionIndex(group); + if (positionIndex == -1) + return false; + + // Position calculation parameters + constexpr float tankToBossAngle = 4.58f; + constexpr float minBossDistance = 15.0f; + constexpr float spreadDistance = 10.0f; + constexpr int32 columnsPerRow = 6; + + // Calculate grid position + int32 row = positionIndex / columnsPerRow; + int32 col = positionIndex % columnsPerRow; + + // Calculate base position + float xOffset = (col - 2) * spreadDistance; // Center around tank position + float yOffset = minBossDistance + (row * spreadDistance); // Each row further back + + // Add zigzag offset for odd rows + if (row % 2 == 1) + xOffset += spreadDistance / 2.0f; + + // Rotate position based on tank-to-boss angle + float finalX = + ICC_FESTERGUT_TANK_POSITION.GetPositionX() + (cos(tankToBossAngle) * yOffset - sin(tankToBossAngle) * xOffset); + float finalY = + ICC_FESTERGUT_TANK_POSITION.GetPositionY() + (sin(tankToBossAngle) * yOffset + cos(tankToBossAngle) * xOffset); + float finalZ = ICC_FESTERGUT_TANK_POSITION.GetPositionZ(); + + // Update Z coordinate + bot->UpdateAllowedPositionZ(finalX, finalY, finalZ); + + // Move if not in position + if (bot->GetExactDist2d(finalX, finalY) > 3.0f) + return MoveTo(bot->GetMapId(), finalX, finalY, finalZ, false, false, false, true, + MovementPriority::MOVEMENT_COMBAT); + + return false; +} + +int32 IccFestergutGroupPositionAction::CalculatePositionIndex(Group* group) +{ + std::vector healerGuids; + std::vector rangedDpsGuids; + std::vector hunterGuids; + + // Collect all eligible members with their GUIDs + for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next()) + { + Player* member = itr->GetSource(); + if (!member || !member->IsAlive() || botAI->IsTank(member)) + continue; + + ObjectGuid memberGuid = member->GetGUID(); + + if (botAI->IsHeal(member)) + healerGuids.push_back(memberGuid); + else if (botAI->IsRanged(member)) + { + if (member->getClass() == CLASS_HUNTER) + hunterGuids.push_back(memberGuid); + else + rangedDpsGuids.push_back(memberGuid); + } + } + + // Sort GUIDs for consistent ordering across all bots + std::sort(healerGuids.begin(), healerGuids.end()); + std::sort(rangedDpsGuids.begin(), rangedDpsGuids.end()); + std::sort(hunterGuids.begin(), hunterGuids.end()); + + ObjectGuid botGuid = bot->GetGUID(); + + // Find which group this bot belongs to + auto healerIt = std::find(healerGuids.begin(), healerGuids.end(), botGuid); + auto rangedIt = std::find(rangedDpsGuids.begin(), rangedDpsGuids.end(), botGuid); + auto hunterIt = std::find(hunterGuids.begin(), hunterGuids.end(), botGuid); + + // Calculate global position index considering group constraints + int32 const healerRows = 2; + int32 const columnsPerRow = 6; + + // Healers: rows 0-1 (first two rows) + if (healerIt != healerGuids.end()) + { + int32 healerIndex = static_cast(std::distance(healerGuids.begin(), healerIt)); + + // Ensure healers only occupy first two rows + if (healerIndex < healerRows * columnsPerRow) + return healerIndex; + + // If too many healers, overflow to later rows but keep them early + return healerIndex; // Will be in row = index / 6, col = index % 6 + } + + // Non-hunter ranged DPS: can be any row (no strict restriction) + if (rangedIt != rangedDpsGuids.end()) + { + int32 rangedIndex = static_cast(std::distance(rangedDpsGuids.begin(), rangedIt)); + + // Start after all healers, then fill remaining spots + return static_cast(healerGuids.size()) + rangedIndex; + } + + // Hunters: never in 1st row (row 0) + if (hunterIt != hunterGuids.end()) + { + int32 hunterIndex = static_cast(std::distance(hunterGuids.begin(), hunterIt)); + + // Calculate how many non-healer positions are before this hunter position + int32 baseOffset = static_cast(healerGuids.size()) + static_cast(rangedDpsGuids.size()); + + // Each row of hunters starts at positions that are multiples of columnsPerRow + // To avoid row 0, skip first column slots reserved for healers/non-hunters + return baseOffset + hunterIndex; + } + + return -1; +} + +bool IccFestergutSporeAction::Execute(Event /*event*/) +{ + constexpr float positionTolerance = 4.0f; + + bool hasSpore = bot->HasAura(SPELL_GAS_SPORE); + + Position spreadRangedPos = CalculateSpreadPosition(); + SporeInfo sporeInfo = FindSporedPlayers(); + Position targetPos = DetermineTargetPosition(hasSpore, sporeInfo, spreadRangedPos); + + if (bot->GetExactDist2d(targetPos) > positionTolerance) + { + botAI->Reset(); + return MoveTo(bot->GetMapId(), targetPos.GetPositionX(), targetPos.GetPositionY(), targetPos.GetPositionZ(), + true, false, false, true, MovementPriority::MOVEMENT_FORCED); + } + + // In position — let combat rotation run (multiplier blocks movement so bot stays put) + return false; +} + +Position IccFestergutSporeAction::CalculateSpreadPosition() +{ + constexpr float spreadRadius = 2.0f; + constexpr float gooNearSporeRadius = 12.0f; + constexpr uint32 impactLifetimeMs = 8000; + constexpr uint32 cycleIdleResetMs = 2000; + + // Group-wide sticky slot decision. Spore + malleable goo can overlap: if + // an active goo lands near the current spread spot we flip to the other + // spot, and stay there for the rest of this spore cycle so bots aren't + // pulled back into the danger zone when the goo expires. The action only + // runs while the spore trigger is active, so a gap >2s between calls + // means the cycle ended - reset to the primary slot for the next cycle. + // State is keyed by instance ID so concurrent ICC raids don't share slots. + struct SpreadSlotState { uint32 lastCallMs = 0; int currentSlot = 1; }; + static std::unordered_map s_slotState; + SpreadSlotState& state = s_slotState[bot->GetMap()->GetInstanceId()]; + + uint32 now = getMSTime(); + if (state.lastCallMs == 0 || now - state.lastCallMs > cycleIdleResetMs) + state.currentSlot = 1; + state.lastCallMs = now; + + Position currentSpot = (state.currentSlot == 2) ? ICC_FESTERGUT_RANGED_SPORE_2 + : ICC_FESTERGUT_RANGED_SPORE; + + auto it = IcecrownHelpers::malleableGooImpacts.find(bot->GetMap()->GetInstanceId()); + if (it != IcecrownHelpers::malleableGooImpacts.end()) + { + for (auto const& impact : it->second) + { + if (getMSTimeDiff(impact.castTime, now) > impactLifetimeMs) + continue; + float dx = impact.position.GetPositionX() - currentSpot.GetPositionX(); + float dy = impact.position.GetPositionY() - currentSpot.GetPositionY(); + if (dx * dx + dy * dy < gooNearSporeRadius * gooNearSporeRadius) + { + state.currentSlot = (state.currentSlot == 1) ? 2 : 1; + currentSpot = (state.currentSlot == 2) ? ICC_FESTERGUT_RANGED_SPORE_2 + : ICC_FESTERGUT_RANGED_SPORE; + break; + } + } + } + + // Unique angle based on bot's GUID + float angle = (bot->GetGUID().GetCounter() % 16) * (M_PI / 8); + + Position spreadRangedPos = currentSpot; + spreadRangedPos.Relocate(spreadRangedPos.GetPositionX() + cos(angle) * spreadRadius, + spreadRangedPos.GetPositionY() + sin(angle) * spreadRadius, + spreadRangedPos.GetPositionZ(), spreadRangedPos.GetOrientation()); + + return spreadRangedPos; +} + +IccFestergutSporeAction::SporeInfo IccFestergutSporeAction::FindSporedPlayers() +{ + SporeInfo info; + GuidVector const members = AI_VALUE(GuidVector, "group members"); + + for (auto const& memberGuid : members) + { + Unit* unit = botAI->GetUnit(memberGuid); + if (!unit) + continue; + + if (unit->HasAura(SPELL_GAS_SPORE)) + { + info.sporedPlayers.push_back(unit); + + if (!info.hasLowestGuid || unit->GetGUID() < info.lowestGuid) + { + info.lowestGuid = unit->GetGUID(); + info.hasLowestGuid = true; + } + } + } + + return info; +} + +bool IccFestergutSporeAction::GooNear(Position const& pos) +{ + constexpr uint32 impactLifetimeMs = 8000; + constexpr float gooDangerRadius = 12.0f; + + uint32 now = getMSTime(); + auto it = IcecrownHelpers::malleableGooImpacts.find(bot->GetMap()->GetInstanceId()); + if (it != IcecrownHelpers::malleableGooImpacts.end()) + { + for (auto const& impact : it->second) + { + if (getMSTimeDiff(impact.castTime, now) > impactLifetimeMs) + continue; + float dx = pos.GetPositionX() - impact.position.GetPositionX(); + float dy = pos.GetPositionY() - impact.position.GetPositionY(); + if (dx * dx + dy * dy < gooDangerRadius * gooDangerRadius) + return true; + } + } + return false; +} + +Position IccFestergutSporeAction::DetermineTargetPosition(bool hasSpore, SporeInfo const& sporeInfo, + Position const& spreadRangedPos) +{ + // No spores at all + if (sporeInfo.sporedPlayers.empty()) + return botAI->IsMelee(bot) ? ICC_FESTERGUT_MELEE_SPORE : spreadRangedPos; + + bool mainTankHasSpore = CheckMainTankSpore(); + + // Goo overlap override — checked before hasSpore so non-spored bots also redirect. + bool gooAtMelee = GooNear(ICC_FESTERGUT_MELEE_SPORE); + bool gooAtRanged = GooNear(ICC_FESTERGUT_RANGED_SPORE) || GooNear(ICC_FESTERGUT_RANGED_SPORE_2); + + if (gooAtMelee && !gooAtRanged) + { + // Goo at melee: tank + melee-spore bot hold, other melee flee to ranged slot 1. + bool isMeleeSporeBot = (hasSpore && bot->GetGUID() == sporeInfo.lowestGuid && !botAI->IsTank(bot) && !mainTankHasSpore); + if (botAI->IsMainTank(bot) || isMeleeSporeBot) + return ICC_FESTERGUT_MELEE_SPORE; + if (botAI->IsMelee(bot)) + return ICC_FESTERGUT_RANGED_SPORE; + return spreadRangedPos; + } + + if (gooAtRanged && !gooAtMelee) + { + // Goo at ranged: all ranged collapse to melee spot. + if (!botAI->IsMelee(bot)) + return ICC_FESTERGUT_MELEE_SPORE; + return ICC_FESTERGUT_MELEE_SPORE; + } + + // Normal spore logic (no overlap or both spots hit). + if (!hasSpore) + return botAI->IsMelee(bot) ? ICC_FESTERGUT_MELEE_SPORE : spreadRangedPos; + + if (botAI->IsMainTank(bot)) + return ICC_FESTERGUT_MELEE_SPORE; + + if (bot->GetGUID() == sporeInfo.lowestGuid && !botAI->IsTank(bot) && !mainTankHasSpore) + return ICC_FESTERGUT_MELEE_SPORE; + + return spreadRangedPos; +} + +bool IccFestergutSporeAction::CheckMainTankSpore() +{ + GuidVector const members = AI_VALUE(GuidVector, "group members"); + + for (auto const& memberGuid : members) + { + Unit* unit = botAI->GetUnit(memberGuid); + if (!unit) + continue; + + if (botAI->IsMainTank(unit->ToPlayer()) && unit->HasAura(SPELL_GAS_SPORE)) + return true; + } + + return false; +} + +bool IccFestergutAvoidMalleableGooAction::Execute(Event /*event*/) +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "festergut"); + if (!boss) + return false; + + // Tanks hold aggro at the fixed tank spot - never dodge. + if (botAI->IsTank(bot)) + return false; + + // Festergut heroic - Putricide throws Malleable Goo from the balcony at + // random non-tank players. The impact is a triggered cast with no cast bar + // and no DynamicObject, so the IccPutricideListenerScript stamps the target's + // position into IcecrownHelpers::malleableGooImpacts at OnSpellCast time. + // Any bot within 12yd of an active impact must flee; danger persists 8s. + // Once a bot dodges, we return true (blocking group-position) until the + // impact expires so the bot doesn't immediately re-enter the danger zone. + constexpr uint32 impactLifetimeMs = 8000; + constexpr float gooDangerRadius = 12.0f; + + uint32 now = getMSTime(); + float botX = bot->GetPositionX(); + float botY = bot->GetPositionY(); + float botZ = bot->GetPositionZ(); + ObjectGuid botGuid = bot->GetGUID(); + + std::vector goos; + goos.reserve(4); + bool botInDanger = false; + auto impactIt = IcecrownHelpers::malleableGooImpacts.find(bot->GetMap()->GetInstanceId()); + if (impactIt != IcecrownHelpers::malleableGooImpacts.end()) + { + for (auto const& impact : impactIt->second) + { + if (getMSTimeDiff(impact.castTime, now) > impactLifetimeMs) + continue; + goos.push_back(impact.position); + + float dx = botX - impact.position.GetPositionX(); + float dy = botY - impact.position.GetPositionY(); + if (dx * dx + dy * dy < gooDangerRadius * gooDangerRadius) + botInDanger = true; + } + } + + if (!botInDanger) + { + // Already safe. Return false so DPS/heal rotations can still fire - + // the multiplier blocks repositioning actions during the wait window + // via festergutGooWaitUntil so the bot stays put without idling. + return false; + } + + // Keep 10yd between fleeing bots. During spore phase melee stack at the + // tank spot, so we ignore melee allies (the tank/melee pile would reject + // every nearby candidate). When no spore is active, melee also spread. + constexpr float botSpacing = 10.0f; + bool sporeActive = false; + if (Group* group = bot->GetGroup()) + { + for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next()) + { + Player* member = itr->GetSource(); + if (member && member->HasAura(SPELL_GAS_SPORE)) + { + sporeActive = true; + break; + } + } + } + + std::vector alliesToSpace; + if (Group* group = bot->GetGroup()) + { + for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next()) + { + Player* member = itr->GetSource(); + if (!member || !member->IsAlive() || member->GetGUID() == botGuid) + continue; + if (sporeActive && botAI->IsMelee(member)) + continue; + alliesToSpace.push_back(member->GetPosition()); + } + } + + constexpr int angleSteps = 24; + float const radii[] = {13.0f, 16.0f, 20.0f}; + float bestScore = -1.0f; + float bestX = botX; + float bestY = botY; + bool found = false; + + // Per-bot preferred flee angle - stable across ticks, distinct per GUID - + // so stacked bots fan out into different sectors instead of converging. + float preferredAngle = (botGuid.GetCounter() % angleSteps) * (2.0f * float(M_PI) / angleSteps); + constexpr float angleBias = 3.0f; + + for (float r : radii) + { + for (int i = 0; i < angleSteps; ++i) + { + float a = (2.0f * float(M_PI) * i) / angleSteps; + float cx = botX + std::cos(a) * r; + float cy = botY + std::sin(a) * r; + + float minGooDistSq = std::numeric_limits::max(); + bool safe = true; + for (Position const& g : goos) + { + float gdx = cx - g.GetPositionX(); + float gdy = cy - g.GetPositionY(); + float d2 = gdx * gdx + gdy * gdy; + if (d2 < gooDangerRadius * gooDangerRadius) + { + safe = false; + break; + } + if (d2 < minGooDistSq) + minGooDistSq = d2; + } + if (!safe) + continue; + + bool tooCloseToAlly = false; + for (Position const& a2 : alliesToSpace) + { + float adx = cx - a2.GetPositionX(); + float ady = cy - a2.GetPositionY(); + if (adx * adx + ady * ady < botSpacing * botSpacing) + { + tooCloseToAlly = true; + break; + } + } + if (tooCloseToAlly) + continue; + + if (!bot->IsWithinLOS(cx, cy, botZ)) + continue; + + float travel = std::sqrt((cx - botX) * (cx - botX) + (cy - botY) * (cy - botY)); + float score = std::sqrt(minGooDistSq) - travel * 0.1f + std::cos(a - preferredAngle) * angleBias; + + if (score > bestScore) + { + bestScore = score; + bestX = cx; + bestY = cy; + found = true; + } + } + if (found) + break; + } + + if (found) + { + return MoveTo(bot->GetMapId(), bestX, bestY, botZ, false, false, false, false, + MovementPriority::MOVEMENT_COMBAT); + } + + return false; +} diff --git a/src/Ai/Raid/ICC/Action/ICCActions_GSB.cpp b/src/Ai/Raid/ICC/Action/ICCActions_GSB.cpp new file mode 100644 index 00000000000..7d181dc7c42 --- /dev/null +++ b/src/Ai/Raid/ICC/Action/ICCActions_GSB.cpp @@ -0,0 +1,1074 @@ +#include "EquipAction.h" +#include "GenericActions.h" +#include "GenericSpellActions.h" +#include "Multiplier.h" +#include "NearestNpcsValue.h" +#include "ObjectAccessor.h" +#include "Playerbots.h" +#include "ICCActions.h" +#include "ICCTriggers.h" +#include "RtiValue.h" +#include "Vehicle.h" + +static bool CastClassTaunt(Player* bot, PlayerbotAI* botAI, Unit* target) +{ + if (!target || !target->IsAlive()) + return false; + + switch (bot->getClass()) + { + case CLASS_PALADIN: + { + bot->RemoveSpellCooldown(SPELL_TAUNT_PALADIN, true); + if (botAI->CastSpell("hand of reckoning", target)) + return true; + break; + } + case CLASS_DEATH_KNIGHT: + { + bot->RemoveSpellCooldown(SPELL_TAUNT_DK, true); + if (botAI->CastSpell("dark command", target)) + return true; + break; + } + case CLASS_DRUID: + { + bot->RemoveSpellCooldown(SPELL_TAUNT_DRUID, true); + if (botAI->CastSpell("growl", target)) + return true; + break; + } + case CLASS_WARRIOR: + { + bot->RemoveSpellCooldown(SPELL_TAUNT_WARRIOR, true); + if (botAI->CastSpell("taunt", target)) + return true; + break; + } + default: + break; + } + + if (botAI->CastSpell("shoot", target) || botAI->CastSpell("throw", target)) + return true; + + return false; +} + +bool IccCannonFireAction::Execute(Event /*event*/) +{ + Unit* vehicleBase = bot->GetVehicleBase(); + Vehicle* vehicle = bot->GetVehicle(); + + if (!vehicleBase || !vehicle) + return false; + + Unit* target = FindValidCannonTarget(); + if (!target) + return false; + + // Use Incinerating Blast when we have enough energy, otherwise Cannon Blast + static constexpr float ENERGY_THRESHOLD = 90.0f; + if (vehicleBase->GetPower(POWER_ENERGY) >= ENERGY_THRESHOLD) + { + uint32 const blastSpellId = AI_VALUE2(uint32, "vehicle spell id", "incinerating blast"); + if (TryCastCannonSpell(blastSpellId, target, vehicleBase)) + return true; + } + + uint32 const cannonSpellId = AI_VALUE2(uint32, "vehicle spell id", "cannon blast"); + return TryCastCannonSpell(cannonSpellId, target, vehicleBase); +} + +Unit* IccCannonFireAction::FindValidCannonTarget() +{ + // availableTargetsGS is ordered per-faction (earliest = highest priority). + // For each entry, enumerate all creatures and pick the nearest hostile + // alive one. FindNearestCreature returns the single closest match which + // may be a friendly of the same entry on our own ship, causing the entry + // to be skipped even when a hostile of that entry exists on the enemy + // ship. + static constexpr float CANNON_TARGET_RANGE = 150.0f; + + for (size_t i = 0; i < availableTargetsGS.size(); ++i) + { + std::list candidates; + bot->GetCreatureListWithEntryInGrid(candidates, availableTargetsGS[i], CANNON_TARGET_RANGE); + + Unit* best = nullptr; + float bestDist = FLT_MAX; + for (Creature* c : candidates) + { + if (!c || !c->IsAlive() || !c->IsHostileTo(bot)) + continue; + float const d = bot->GetExactDist(c); + if (d < bestDist) + { + bestDist = d; + best = c; + } + } + + if (best) + return best; + } + + return nullptr; +} + +bool IccCannonFireAction::TryCastCannonSpell(uint32 spellId, Unit* target, Unit* vehicleBase) +{ + static constexpr uint32 COOLDOWN_MS = 1000; + + if (botAI->CanCastVehicleSpell(spellId, target) && botAI->CastVehicleSpell(spellId, target)) + { + vehicleBase->AddSpellCooldown(spellId, 0, COOLDOWN_MS); + return true; + } + + return false; +} + +bool IccGunshipEnterCannonAction::Execute(Event /*event*/) +{ + // Never switch vehicles while already in one + if (bot->GetVehicle()) + return false; + + // Require rocket pack acquired and equipped before boarding a cannon + Item* rocketPack = bot->GetItemByEntry(ITEM_GOBLIN_ROCKET_PACK); + if (!rocketPack || !rocketPack->IsEquipped()) + return false; + + Unit* bestVehicle = FindBestAvailableCannon(); + if (!bestVehicle) + return false; + + return EnterVehicle(bestVehicle, true); +} + +Unit* IccGunshipEnterCannonAction::FindBestAvailableCannon() +{ + Unit* bestVehicle = nullptr; + + GuidVector const npcs = AI_VALUE(GuidVector, "nearest vehicles"); + for (ObjectGuid const& npcGuid : npcs) + { + Unit* vehicleBase = botAI->GetUnit(npcGuid); + if (!IsValidCannon(vehicleBase)) + continue; + + // Prefer the closest valid cannon + if (!bestVehicle || bot->GetExactDist(vehicleBase) < bot->GetExactDist(bestVehicle)) + bestVehicle = vehicleBase; + } + + return bestVehicle; +} + +bool IccGunshipEnterCannonAction::IsValidCannon(Unit* vehicle) +{ + if (!vehicle) + return false; + + if (vehicle->HasUnitFlag(UNIT_FLAG_NOT_SELECTABLE)) + return false; + + if (!vehicle->IsFriendlyTo(bot)) + return false; + + if (!vehicle->GetVehicleKit() || !vehicle->GetVehicleKit()->GetAvailableSeatCount()) + return false; + + uint32 const entry = vehicle->GetEntry(); + if (entry != NPC_CANNONA && entry != NPC_CANNONH) + return false; + + // Frozen or disabled cannon - skip + if (vehicle->HasAura(SPELL_FROZEN_CANNON) || vehicle->HasAura(SPELL_BELOW_ZERO)) + return false; + + return true; +} + +bool IccGunshipEnterCannonAction::EnterVehicle(Unit* vehicleBase, bool moveIfFar) +{ + float const dist = bot->GetDistance(vehicleBase); + + if (dist > INTERACTION_DISTANCE && !moveIfFar) + return false; + + if (dist > INTERACTION_DISTANCE) + return MoveTo(vehicleBase); + + botAI->RemoveShapeshift(); + bot->GetMotionMaster()->Clear(); + bot->StopMoving(); + + vehicleBase->HandleSpellClick(bot); + + if (!bot->IsOnVehicle(vehicleBase)) + return false; + + // Dismount - bots can enter a vehicle while still mounted + WorldPacket emptyPacket; + bot->GetSession()->HandleCancelMountAuraOpcode(emptyPacket); + + return true; +} + +IccGunshipRocketJumpAction::GunshipSide IccGunshipRocketJumpAction::DetectShip() const +{ + Unit* cannonA = bot->FindNearestCreature(NPC_CANNONA, 100.0f); + if (cannonA && cannonA->IsFriendlyTo(bot)) + return GunshipSide::ALLY; + + Unit* cannonH = bot->FindNearestCreature(NPC_CANNONH, 100.0f); + if (cannonH && cannonH->IsFriendlyTo(bot)) + return GunshipSide::HORDE; + + return GunshipSide::NONE; +} + +bool IccGunshipRocketJumpAction::Execute(Event /*event*/) +{ + GunshipSide const side = DetectShip(); + if (side == GunshipSide::NONE) + return false; + + // Z-axis correction: teleport bot back if fallen below deck + if (!bot->GetVehicle()) + { + Position const& zRef = (side == GunshipSide::ALLY) ? ICC_GUNSHIP_ROCKET_JUMP_ALLY2 : ICC_GUNSHIP_ROCKET_JUMP_HORDE2; + Position const& zDest = (side == GunshipSide::ALLY) ? ICC_GUNSHIP_ROCKET_JUMP_ALLY2 : ICC_GUNSHIP_ROCKET_JUMP_HORDE2; + float const Z_THRESHOLD = zRef.GetPositionZ() - 5.0f; + if (bot->GetPositionZ() < Z_THRESHOLD) + { + bot->TeleportTo(bot->GetMapId(), zDest.GetPositionX(), zDest.GetPositionY(), + zDest.GetPositionZ(), bot->GetOrientation()); + botAI->Reset(); + return false; + } + } + + float const maxWaitingDistance = (side == GunshipSide::ALLY) ? 30.0f : 25.0f; + static constexpr float MAX_ATTACK_DISTANCE = 20.0f; + static constexpr float HOLD_RADIUS = 20.0f; + static constexpr uint8 SKULL_ICON_INDEX = 7; + + uint32 const mageEntry = (side == GunshipSide::ALLY) ? NPC_KOR_KRON_BATTLE_MAGE : NPC_SKYBREAKER_SORCERER; + Position const& waitPos = (side == GunshipSide::ALLY) ? ICC_GUNSHIP_ROCKET_JUMP_ALLY2 : ICC_GUNSHIP_ROCKET_JUMP_HORDE_FRIENDLY_POINT; + Position const& attackPos = (side == GunshipSide::ALLY) ? ICC_GUNSHIP_ROCKET_JUMP_ALLY : ICC_GUNSHIP_ROCKET_JUMP_HORDE; + + Unit* boss = nullptr; + { + std::list mages; + bot->GetCreatureListWithEntryInGrid(mages, mageEntry, 200.0f); + for (Creature* m : mages) + { + if (m && m->IsAlive() && m->HasUnitState(UNIT_STATE_CASTING)) + { + boss = m; + break; + } + } + if (!boss) + boss = bot->FindNearestCreature(mageEntry, 200.0f); + } + + CleanupSkullIcon(SKULL_ICON_INDEX); + + uint32 const cannonEntry = (side == GunshipSide::ALLY) ? NPC_CANNONA : NPC_CANNONH; + bool cannonsHaveBelowZero = false; + { + std::list cannons; + bot->GetCreatureListWithEntryInGrid(cannons, cannonEntry, 100.0f); + for (Creature* c : cannons) + { + if (c && c->IsFriendlyTo(bot) && c->HasAura(SPELL_BELOW_ZERO)) + { + cannonsHaveBelowZero = true; + break; + } + } + } + + bool const botOnEnemyShip = bot->GetExactDist2d(attackPos) <= MAX_ATTACK_DISTANCE; + bool const mageAlive = boss && boss->IsAlive() && boss->HasUnitState(UNIT_STATE_CASTING); + + // Assist tank stays on friendly ship to collect and tank adds + if (botAI->IsAssistTank(bot)) + { + GuidVector const npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); + + // Find nearest uncollected add near hold position + Unit* targetAdd = nullptr; + float closestDist = FLT_MAX; + + for (ObjectGuid const& guid : npcs) + { + Unit* unit = botAI->GetUnit(guid); + if (!unit || !unit->IsAlive()) + continue; + + if (unit->GetExactDist2d(waitPos) > HOLD_RADIUS) + continue; + + Unit* victim = unit->GetVictim(); + bool alreadyTanked = victim && victim->IsPlayer() && botAI->IsTank(victim->ToPlayer()); + if (alreadyTanked) + continue; + + float dist = bot->GetExactDist2d(unit); + if (dist < closestDist) + { + closestDist = dist; + targetAdd = unit; + } + } + + if (targetAdd) + { + CastClassTaunt(bot, botAI, targetAdd); + bot->SetTarget(targetAdd->GetGUID()); + bot->SetFacingToObject(targetAdd); + Attack(targetAdd); + return false; + } + + // No uncollected adds - keep attacking adds targeting us + for (ObjectGuid const& guid : npcs) + { + Unit* unit = botAI->GetUnit(guid); + if (!unit || !unit->IsAlive()) + continue; + + if (unit->GetExactDist2d(waitPos) > HOLD_RADIUS) + continue; + + if (unit->GetVictim() == bot) + { + bot->SetTarget(unit->GetGUID()); + bot->SetFacingToObject(unit); + Attack(unit); + return false; + } + } + + // No adds — return to hold position if drifted + if (bot->GetExactDist2d(waitPos) > HOLD_RADIUS) + { + return MoveTo(bot->GetMapId(), waitPos.GetPositionX(), waitPos.GetPositionY(), waitPos.GetPositionZ(), + false, false, false, false, MovementPriority::MOVEMENT_NORMAL); + } + + return false; + } + + bool const isMainTank = botAI->IsMainTank(bot); + bool const isHorde = (side == GunshipSide::HORDE); + Position const& middlePoint = ICC_GUNSHIP_ROCKET_JUMP_HORDE_MIDDLE_POINT; + static constexpr float JUMP_GATE = 30.0f; + + // Main tank: tank captain during below-zero OR while an ally is on enemy ship. + // Otherwise return to friendly ship — captain one-shots if tanked too long. + if (isMainTank) + { + uint32 const captainEntry = (side == GunshipSide::ALLY) ? NPC_HIGH_OVERLORD_SAURFANG : NPC_MURADIN_BRONZEBEARD; + Unit* captain = bot->FindNearestCreature(captainEntry, 1000.0f); + + // Battle Fury buff bugged on captain - strip all difficulty variants every tick. todo: remove when boss agro fixed + if (captain) + { + static constexpr uint32 BATTLE_FURY_IDS[] = {SPELL_BATTLE_FURY1, SPELL_BATTLE_FURY2, + SPELL_BATTLE_FURY3, SPELL_BATTLE_FURY4, + SPELL_BATTLE_FURY5}; + for (uint32 const id : BATTLE_FURY_IDS) + if (captain->HasAura(id)) + captain->RemoveAurasDueToSpell(id); + } + + bool allyAtMage = false; + if (Group* group = bot->GetGroup()) + { + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + if (!member || !member->IsAlive() || member == bot) + continue; + if (botAI->IsMainTank(member) || botAI->IsAssistTank(member)) + continue; + if (member->GetExactDist2d(attackPos) <= 20.0f) + { + allyAtMage = true; + break; + } + } + } + bool const shouldTankCaptain = cannonsHaveBelowZero || allyAtMage; + + // Captain gone or outside tanking window: return to friendly ship if stranded, else hold. + // Drop target/threat so combat rotation doesn't chase adds on friendly ship. + if (!captain || !captain->IsAlive() || !shouldTankCaptain) + { + if (bot->GetVictim()) + { + bot->AttackStop(); + bot->InterruptNonMeleeSpells(true); + bot->SetTarget(ObjectGuid::Empty); + } + if (botOnEnemyShip) + { + // Main tank jumps back last - wait for all non-tanks to leave enemy ship + if (AnyNonTankAwayFromFriendly(side)) + return false; + + if (ExitCannonIfSeated()) + return true; + + if (isHorde) + { + float const distFriendly = bot->GetExactDist2d(waitPos); + float const distMiddle = bot->GetExactDist2d(middlePoint); + if (distMiddle <= 10.0f && distFriendly > 5.0f) + { + if (UseRocketPack(waitPos, /*walkIfOutOfRange=*/true)) + return true; + return MoveTo(bot->GetMapId(), waitPos.GetPositionX(), waitPos.GetPositionY(), + waitPos.GetPositionZ(), false, false, false, false, + MovementPriority::MOVEMENT_NORMAL); + } + if (distMiddle <= JUMP_GATE) + { + if (UseRocketPack(middlePoint, /*walkIfOutOfRange=*/false)) + return true; + return MoveTo(bot->GetMapId(), middlePoint.GetPositionX(), middlePoint.GetPositionY(), + middlePoint.GetPositionZ(), false, false, false, false, + MovementPriority::MOVEMENT_NORMAL); + } + if (UseRocketPack(middlePoint, /*walkIfOutOfRange=*/true)) + return true; + return MoveTo(bot->GetMapId(), middlePoint.GetPositionX(), middlePoint.GetPositionY(), + middlePoint.GetPositionZ(), false, false, false, false, + MovementPriority::MOVEMENT_NORMAL); + } + + // Alliance MT return: ALLY_MIDDLE_POINT -> ALLY_FRIENDLY_POINT + Position const& allyFriendly = ICC_GUNSHIP_ROCKET_JUMP_ALLY_FRIENDLY_POINT; + Position const& allyMiddle = ICC_GUNSHIP_ROCKET_JUMP_ALLY_MIDDLE_POINT; + float const distAllyMid = bot->GetExactDist2d(allyMiddle); + float const distAllyFriendly = bot->GetExactDist2d(allyFriendly); + + if (distAllyMid <= 10.0f && distAllyFriendly > 5.0f) + { + if (UseRocketPack(allyFriendly, /*walkIfOutOfRange=*/true)) + return true; + return MoveTo(bot->GetMapId(), allyFriendly.GetPositionX(), allyFriendly.GetPositionY(), + allyFriendly.GetPositionZ(), false, false, false, false, + MovementPriority::MOVEMENT_NORMAL); + } + if (distAllyMid <= JUMP_GATE) + { + if (UseRocketPack(allyMiddle, /*walkIfOutOfRange=*/false)) + return true; + return MoveTo(bot->GetMapId(), allyMiddle.GetPositionX(), allyMiddle.GetPositionY(), + allyMiddle.GetPositionZ(), false, false, false, false, + MovementPriority::MOVEMENT_NORMAL); + } + if (UseRocketPack(allyMiddle, /*walkIfOutOfRange=*/true)) + return true; + return MoveTo(bot->GetMapId(), allyMiddle.GetPositionX(), allyMiddle.GetPositionY(), + allyMiddle.GetPositionZ(), false, false, false, false, + MovementPriority::MOVEMENT_NORMAL); + } + if (bot->GetExactDist2d(waitPos) > maxWaitingDistance) + { + if (ExitCannonIfSeated()) + return true; + return UseRocketPack(waitPos, /*walkIfOutOfRange=*/true); + } + return false; + } + + // Not on enemy ship: rocket directly to captain + if (!botOnEnemyShip) + { + if (ExitCannonIfSeated()) + return true; + + Position const captainPos(captain->GetPositionX(), captain->GetPositionY(), captain->GetPositionZ()); + + if (isHorde) + { + Position const& friendlyPoint = ICC_GUNSHIP_ROCKET_JUMP_HORDE_FRIENDLY_POINT; + float const distFriendly = bot->GetExactDist2d(friendlyPoint); + float const distMiddle = bot->GetExactDist2d(middlePoint); + float const distCaptain = bot->GetExactDist2d(&captainPos); + + if (distCaptain <= JUMP_GATE || distMiddle <= 5.0f) + return UseRocketPack(captainPos, /*walkIfOutOfRange=*/false); + if (distFriendly <= 5.0f) + return RocketPackJumpToward(middlePoint); + if (distMiddle > JUMP_GATE && distCaptain > JUMP_GATE) + return UseRocketPack(friendlyPoint, /*walkIfOutOfRange=*/true); + + Position const* nearest = &friendlyPoint; + float nearestDist = distFriendly; + if (distMiddle < nearestDist) { nearest = &middlePoint; nearestDist = distMiddle; } + if (distCaptain < nearestDist) { nearest = &captainPos; nearestDist = distCaptain; } + return UseRocketPack(*nearest, /*walkIfOutOfRange=*/true); + } + + // Alliance: ALLY_FRIENDLY_POINT -> ALLY_MIDDLE_POINT -> captain + Position const& allyFriendly = ICC_GUNSHIP_ROCKET_JUMP_ALLY_FRIENDLY_POINT; + Position const& allyMiddle = ICC_GUNSHIP_ROCKET_JUMP_ALLY_MIDDLE_POINT; + float const distAllyFriendly = bot->GetExactDist2d(allyFriendly); + float const distAllyMid = bot->GetExactDist2d(allyMiddle); + float const distCaptainA = bot->GetExactDist2d(&captainPos); + + if (distCaptainA <= JUMP_GATE || distAllyMid <= 5.0f) + return UseRocketPack(captainPos, /*walkIfOutOfRange=*/false); + if (distAllyFriendly <= 5.0f) + return RocketPackJumpToward(allyMiddle); + if (distAllyMid > JUMP_GATE && distCaptainA > JUMP_GATE) + return UseRocketPack(allyFriendly, /*walkIfOutOfRange=*/true); + + Position const* nearestA = &allyFriendly; + float nearestDistA = distAllyFriendly; + if (distAllyMid < nearestDistA) { nearestA = &allyMiddle; nearestDistA = distAllyMid; } + if (distCaptainA < nearestDistA) { nearestA = &captainPos; nearestDistA = distCaptainA; } + return UseRocketPack(*nearestA, /*walkIfOutOfRange=*/true); + } + + // On enemy ship. If not captain's victim, force aggro. + if (captain->GetVictim() != bot) + { + CastClassTaunt(bot, botAI, captain); + bot->SetTarget(captain->GetGUID()); + bot->SetFacingToObject(captain); + Attack(captain); + return false; + } + + // Captain's victim: drag boss to 40f-from-friendly-cannon spot on enemy ship + static constexpr float TANK_OFFSET_FROM_CANNON = 40.0f; + static constexpr float TANK_POS_TOLERANCE = 3.0f; + Unit* friendlyCannon = FindNearestFriendlyCannon(side); + if (friendlyCannon) + { + float const cx = friendlyCannon->GetPositionX(); + float const cy = friendlyCannon->GetPositionY(); + float const dirX = attackPos.GetPositionX() - cx; + float const dirY = attackPos.GetPositionY() - cy; + float const dirLen = std::sqrt(dirX * dirX + dirY * dirY); + if (dirLen > 0.1f) + { + float const tx = cx + (dirX / dirLen) * TANK_OFFSET_FROM_CANNON; + float const ty = cy + (dirY / dirLen) * TANK_OFFSET_FROM_CANNON; + float const tz = bot->GetPositionZ(); + float const dx = bot->GetPositionX() - tx; + float const dy = bot->GetPositionY() - ty; + if (std::sqrt(dx * dx + dy * dy) > TANK_POS_TOLERANCE) + { + bot->SetTarget(captain->GetGUID()); + return MoveTo(bot->GetMapId(), tx, ty, tz, false, false, false, false, + MovementPriority::MOVEMENT_COMBAT); + } + } + } + + bot->SetTarget(captain->GetGUID()); + bot->SetFacingToObject(captain); + Attack(captain); + return false; + } + + // Non-tank rocket jump logic + + if (cannonsHaveBelowZero && mageAlive) + { + UpdateBossSkullIcon(boss, SKULL_ICON_INDEX); + if (!botOnEnemyShip) + { + if (ExitCannonIfSeated()) + return true; + + if (isHorde) + { + Position const& friendlyPoint = ICC_GUNSHIP_ROCKET_JUMP_HORDE_FRIENDLY_POINT; + float const distFriendly = bot->GetExactDist2d(friendlyPoint); + float const distMiddle = bot->GetExactDist2d(middlePoint); + float const distMage = bot->GetExactDist2d(attackPos); + + // Mage in range or already at middle: jump mage + if (distMage <= JUMP_GATE || distMiddle <= 5.0f) + return UseRocketPack(attackPos, /*walkIfOutOfRange=*/false); + // At friendly: jump middle + if (distFriendly <= 5.0f) + return RocketPackJumpToward(middlePoint); + // Both middle and mage out of range: detour through friendly + if (distMiddle > JUMP_GATE && distMage > JUMP_GATE) + return UseRocketPack(friendlyPoint, /*walkIfOutOfRange=*/true); + + // Fallback: jump nearest of the three + Position const* nearest = &friendlyPoint; + float nearestDist = distFriendly; + if (distMiddle < nearestDist) { nearest = &middlePoint; nearestDist = distMiddle; } + if (distMage < nearestDist) { nearest = &attackPos; nearestDist = distMage; } + return UseRocketPack(*nearest, /*walkIfOutOfRange=*/true); + } + + // Alliance non-tank: ALLY_FRIENDLY_POINT -> ALLY_MIDDLE_POINT -> mage (attackPos) + Position const& allyFriendly = ICC_GUNSHIP_ROCKET_JUMP_ALLY_FRIENDLY_POINT; + Position const& allyMiddle = ICC_GUNSHIP_ROCKET_JUMP_ALLY_MIDDLE_POINT; + float const distAllyFriendly = bot->GetExactDist2d(allyFriendly); + float const distAllyMid = bot->GetExactDist2d(allyMiddle); + float const distMageA = bot->GetExactDist2d(attackPos); + + if (distMageA <= JUMP_GATE || distAllyMid <= 5.0f) + return UseRocketPack(attackPos, /*walkIfOutOfRange=*/false); + if (distAllyFriendly <= 5.0f) + return RocketPackJumpToward(allyMiddle); + if (distAllyMid > JUMP_GATE && distMageA > JUMP_GATE) + return UseRocketPack(allyFriendly, /*walkIfOutOfRange=*/true); + + Position const* nearestA = &allyFriendly; + float nearestDistA = distAllyFriendly; + if (distAllyMid < nearestDistA) { nearestA = &allyMiddle; nearestDistA = distAllyMid; } + if (distMageA < nearestDistA) { nearestA = &attackPos; nearestDistA = distMageA; } + return UseRocketPack(*nearestA, /*walkIfOutOfRange=*/true); + } + + // On enemy ship: attack mage + context->GetValue("rti")->Set("skull"); + if (!bot->GetVehicle()) + Attack(boss); + return false; + } + else + { + // Stranded at middle point: force jump back to friendly point + if (isHorde) + { + float const distMiddle = bot->GetExactDist2d(middlePoint); + float const distFriendly = bot->GetExactDist2d(waitPos); + if (distMiddle <= 10.0f && distFriendly > 5.0f) + { + if (ExitCannonIfSeated()) + return true; + if (UseRocketPack(waitPos, /*walkIfOutOfRange=*/true)) + return true; + return MoveTo(bot->GetMapId(), waitPos.GetPositionX(), waitPos.GetPositionY(), + waitPos.GetPositionZ(), false, false, false, false, + MovementPriority::MOVEMENT_NORMAL); + } + } + + // Return to friendly point if on enemy ship or drifted too far + if (botOnEnemyShip || bot->GetExactDist2d(waitPos) > maxWaitingDistance) + { + if (ExitCannonIfSeated()) + return true; + + if (isHorde) + { + float const distMiddle = bot->GetExactDist2d(middlePoint); + // Middle in range: jump middle (next tick stranded override jumps friendly) + if (distMiddle <= JUMP_GATE) + { + if (UseRocketPack(middlePoint, /*walkIfOutOfRange=*/false)) + return true; + return MoveTo(bot->GetMapId(), middlePoint.GetPositionX(), middlePoint.GetPositionY(), + middlePoint.GetPositionZ(), false, false, false, false, + MovementPriority::MOVEMENT_NORMAL); + } + // Middle out of range: walk 10f toward middle + if (UseRocketPack(middlePoint, /*walkIfOutOfRange=*/true)) + return true; + return MoveTo(bot->GetMapId(), middlePoint.GetPositionX(), middlePoint.GetPositionY(), + middlePoint.GetPositionZ(), false, false, false, false, + MovementPriority::MOVEMENT_NORMAL); + } + + // Alliance return: ALLY_MIDDLE_POINT -> ALLY_FRIENDLY_POINT + Position const& allyFriendly = ICC_GUNSHIP_ROCKET_JUMP_ALLY_FRIENDLY_POINT; + Position const& allyMiddle = ICC_GUNSHIP_ROCKET_JUMP_ALLY_MIDDLE_POINT; + float const distAllyMid = bot->GetExactDist2d(allyMiddle); + float const distAllyFriendly = bot->GetExactDist2d(allyFriendly); + + if (distAllyMid <= 10.0f && distAllyFriendly > 5.0f) + { + if (UseRocketPack(allyFriendly, /*walkIfOutOfRange=*/true)) + return true; + return MoveTo(bot->GetMapId(), allyFriendly.GetPositionX(), allyFriendly.GetPositionY(), + allyFriendly.GetPositionZ(), false, false, false, false, + MovementPriority::MOVEMENT_NORMAL); + } + if (distAllyMid <= JUMP_GATE) + { + if (UseRocketPack(allyMiddle, /*walkIfOutOfRange=*/false)) + return true; + return MoveTo(bot->GetMapId(), allyMiddle.GetPositionX(), allyMiddle.GetPositionY(), + allyMiddle.GetPositionZ(), false, false, false, false, + MovementPriority::MOVEMENT_NORMAL); + } + if (UseRocketPack(allyMiddle, /*walkIfOutOfRange=*/true)) + return true; + return MoveTo(bot->GetMapId(), allyMiddle.GetPositionX(), allyMiddle.GetPositionY(), + allyMiddle.GetPositionZ(), false, false, false, false, + MovementPriority::MOVEMENT_NORMAL); + } + + // Melee DPS on friendly ship: assist the assist tank on adds it's tanking + if (botAI->IsMelee(bot) && !botAI->IsTank(bot)) + { + if (Group* group = bot->GetGroup()) + { + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + if (!member || !member->IsAlive()) + continue; + if (!botAI->IsAssistTank(member)) + continue; + Unit* assistTarget = member->GetVictim(); + if (!assistTarget || !assistTarget->IsAlive()) + break; + if (assistTarget->GetExactDist2d(waitPos) > HOLD_RADIUS) + break; + bot->SetTarget(assistTarget->GetGUID()); + bot->SetFacingToObject(assistTarget); + Attack(assistTarget); + return false; + } + } + } + + // Ranged DPS star-mark axethrower/rifleman for weapon focus fire. + // Handoff uses the previous star's spot as anchor so the marker + // doesn't jump to whichever add is closest to the picking bot. + if (botAI->IsRangedDps(bot)) + { + static constexpr uint8 STAR_ICON_INDEX = 0; + static constexpr float ADD_SEARCH_RANGE = 200.0f; + uint32 const addEntry = (side == GunshipSide::ALLY) + ? NPC_KOR_KRON_AXETHROWER : NPC_SKYBREAKER_RIFLEMAN; + + static std::map s_lastStarPos; + uint32 const instId = bot->GetInstanceId(); + + if (Group* group = bot->GetGroup()) + { + Unit* starTarget = botAI->GetUnit(group->GetTargetIcon(STAR_ICON_INDEX)); + bool const validStar = starTarget && starTarget->IsAlive() + && starTarget->GetEntry() == addEntry; + + if (validStar) + { + s_lastStarPos[instId] = Position(starTarget->GetPositionX(), + starTarget->GetPositionY(), + starTarget->GetPositionZ()); + } + else + { + Position refPos; + auto const it = s_lastStarPos.find(instId); + if (it != s_lastStarPos.end()) + refPos = it->second; + else + refPos = Position(bot->GetPositionX(), bot->GetPositionY(), + bot->GetPositionZ()); + + std::list adds; + bot->GetCreatureListWithEntryInGrid(adds, addEntry, ADD_SEARCH_RANGE); + + Unit* nextAdd = nullptr; + float bestDist = FLT_MAX; + for (Creature* c : adds) + { + if (!c || !c->IsAlive() || !c->IsHostileTo(bot)) + continue; + float const d = c->GetExactDist(&refPos); + if (d < bestDist) + { + bestDist = d; + nextAdd = c; + } + } + + if (nextAdd) + { + group->SetTargetIcon(STAR_ICON_INDEX, bot->GetGUID(), nextAdd->GetGUID()); + s_lastStarPos[instId] = Position(nextAdd->GetPositionX(), + nextAdd->GetPositionY(), + nextAdd->GetPositionZ()); + starTarget = nextAdd; + } + else + { + starTarget = nullptr; + } + } + + if (starTarget) + { + context->GetValue("rti")->Set("star"); + if (!bot->GetVehicle()) + Attack(starTarget); + } + } + } + } + + return false; +} + +bool IccGunshipRocketJumpAction::IsMainTankOnEnemyShip(GunshipSide side) const +{ + Group* group = bot->GetGroup(); + if (!group) + return false; + + uint32 const captainEntry = (side == GunshipSide::ALLY) ? NPC_HIGH_OVERLORD_SAURFANG : NPC_MURADIN_BRONZEBEARD; + + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + if (!member || !member->IsAlive()) + continue; + if (!botAI->IsMainTank(member)) + continue; + Unit* captain = member->FindNearestCreature(captainEntry, 15.0f); + return captain != nullptr; + } + return false; +} + +bool IccGunshipRocketJumpAction::AnyNonTankAwayFromFriendly(GunshipSide side) const +{ + Group* group = bot->GetGroup(); + if (!group) + return false; + + Position const& waitPos = (side == GunshipSide::ALLY) ? ICC_GUNSHIP_ROCKET_JUMP_ALLY2 : ICC_GUNSHIP_ROCKET_JUMP_HORDE2; + float const threshold = (side == GunshipSide::ALLY) ? 40.0f : 45.0f; + + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + if (!member || !member->IsAlive()) + continue; + if (botAI->IsMainTank(member) || botAI->IsAssistTank(member)) + continue; + if (member->GetExactDist2d(waitPos) > threshold) + return true; + } + return false; +} + +bool IccGunshipRocketJumpAction::ExitCannonIfSeated() +{ + Vehicle* vehicle = bot->GetVehicle(); + if (!vehicle) + return false; + + VehicleSeatEntry const* seat = vehicle->GetSeatForPassenger(bot); + if (!seat || !seat->CanEnterOrExit()) + return false; + + WorldPacket packet; + bot->GetSession()->HandleRequestVehicleExit(packet); + return true; +} + +Item* IccGunshipRocketJumpAction::FindRocketPack() const +{ + return bot->GetItemByEntry(ITEM_GOBLIN_ROCKET_PACK); +} + +bool IccGunshipRocketJumpAction::UseRocketPack(Position const& destination, bool walkIfOutOfRange) +{ + Item* rocketPack = FindRocketPack(); + if (!rocketPack) + return false; + + if (!bot->HasAura(SPELL_ROCKET_PACK_USEABLE)) + return false; + + if (bot->IsNonMeleeSpellCast(true)) + return false; + + if (bot->CanUseItem(rocketPack) != EQUIP_ERR_OK) + return false; + + // Throttle rocket pack use to prevent mid-air re-jump spam + static constexpr uint32 ROCKET_PACK_COOLDOWN_MS = 2500; + static std::map lastRocketPackUse; + uint32 const now = getMSTime(); + auto const it = lastRocketPackUse.find(bot->GetGUID()); + bool const onCooldown = it != lastRocketPackUse.end() && + getMSTimeDiff(it->second, now) < ROCKET_PACK_COOLDOWN_MS; + + static constexpr float MAX_JUMP_RANGE = 30.0f; + float const dist = bot->GetExactDist(&destination); + + // On cooldown: consume tick to block MoveTo fallback when target is reachable + if (onCooldown) + { + if (dist <= MAX_JUMP_RANGE) + return true; + return false; + } + if (dist > MAX_JUMP_RANGE) + { + if (!walkIfOutOfRange) + return false; + + // Walk 10f toward destination, retry jump next tick + static constexpr float STEP_DISTANCE = 10.0f; + float const ratio = STEP_DISTANCE / dist; + float const stepX = bot->GetPositionX() + (destination.GetPositionX() - bot->GetPositionX()) * ratio; + float const stepY = bot->GetPositionY() + (destination.GetPositionY() - bot->GetPositionY()) * ratio; + float const stepZ = bot->GetPositionZ() + (destination.GetPositionZ() - bot->GetPositionZ()) * ratio; + return MoveTo(bot->GetMapId(), stepX, stepY, stepZ, false, false, false, false, + MovementPriority::MOVEMENT_NORMAL); + } + + // Settle bot before firing rocket pack - moving bots tend to misfire mid-stride + if (bot->isMoving()) + { + bot->GetMotionMaster()->Clear(); + bot->StopMoving(); + return true; + } + + uint8 bagIndex = rocketPack->GetBagSlot(); + uint8 slot = rocketPack->GetSlot(); + uint8 castCount = 1; + uint32 spellId = SPELL_ROCKET_PACK_USE; + ObjectGuid itemGuid = rocketPack->GetGUID(); + uint32 glyphIndex = 0; + uint8 castFlags = 0; + + WorldPacket packet(CMSG_USE_ITEM); + packet << bagIndex << slot << castCount << spellId << itemGuid << glyphIndex << castFlags; + packet << uint32(TARGET_FLAG_DEST_LOCATION); + packet.appendPackGUID(0); + packet << destination.GetPositionX() << destination.GetPositionY() << destination.GetPositionZ(); + + bot->GetSession()->HandleUseItemOpcode(packet); + lastRocketPackUse[bot->GetGUID()] = now; + return true; +} + +bool IccGunshipRocketJumpAction::RocketPackJumpToward(Position const& target) +{ + static constexpr float JUMP_DISTANCE = 30.0f; + float const dist = bot->GetExactDist(&target); + if (dist <= JUMP_DISTANCE) + return UseRocketPack(target, /*walkIfOutOfRange=*/false); + + float const ratio = JUMP_DISTANCE / dist; + Position jumpTo( + bot->GetPositionX() + (target.GetPositionX() - bot->GetPositionX()) * ratio, + bot->GetPositionY() + (target.GetPositionY() - bot->GetPositionY()) * ratio, + bot->GetPositionZ() + (target.GetPositionZ() - bot->GetPositionZ()) * ratio); + return UseRocketPack(jumpTo, /*walkIfOutOfRange=*/false); +} + +Unit* IccGunshipRocketJumpAction::FindNearestFriendlyCannon(GunshipSide side) const +{ + if (side == GunshipSide::NONE) + return nullptr; + + uint32 const cannonEntry = (side == GunshipSide::ALLY) ? NPC_CANNONA : NPC_CANNONH; + Unit* cannon = bot->FindNearestCreature(cannonEntry, 300.0f); + if (cannon && cannon->IsFriendlyTo(bot)) + return cannon; + return nullptr; +} + +bool IccGunshipRocketJumpAction::CleanupSkullIcon(uint8 skullIconIndex) +{ + Group* group = bot->GetGroup(); + if (!group) + return false; + + ObjectGuid const currentSkullTarget = group->GetTargetIcon(skullIconIndex); + if (currentSkullTarget.IsEmpty()) + return false; + + Unit* skullTarget = ObjectAccessor::GetUnit(*bot, currentSkullTarget); + if (!skullTarget || !skullTarget->IsAlive()) + group->SetTargetIcon(skullIconIndex, bot->GetGUID(), ObjectGuid::Empty); + + return false; +} + +bool IccGunshipRocketJumpAction::UpdateBossSkullIcon(Unit* boss, uint8 skullIconIndex) +{ + Group* group = bot->GetGroup(); + if (!group) + return false; + + if (group->GetTargetIcon(skullIconIndex) != boss->GetGUID()) + group->SetTargetIcon(skullIconIndex, bot->GetGUID(), boss->GetGUID()); + + return false; +} + +bool IccGunshipRocketPackSetupAction::Execute(Event /*event*/) +{ + Item* rocketPack = FindRocketPack(); + if (!rocketPack) + return AcquireRocketPack(); + if (!rocketPack->IsEquipped()) + return EquipRocketPack(); + return false; +} + +Item* IccGunshipRocketPackSetupAction::FindRocketPack() const +{ + return bot->GetItemByEntry(ITEM_GOBLIN_ROCKET_PACK); +} + +bool IccGunshipRocketPackSetupAction::AcquireRocketPack() +{ + static constexpr float ZAFOD_SEARCH_RADIUS = 150.0f; + static constexpr uint32 ZAFOD_GOSSIP_MENU_ID = 10885; + static constexpr uint32 ZAFOD_GOSSIP_OPTION = 0; + + Creature* zafod = bot->FindNearestCreature(NPC_ZAFOD_BOOMBOX, ZAFOD_SEARCH_RADIUS); + if (!zafod) + return false; + + if (bot->GetDistance(zafod) > INTERACTION_DISTANCE) + return MoveTo(bot->GetMapId(), zafod->GetPositionX(), zafod->GetPositionY(), zafod->GetPositionZ(), + false, false, false, false, MovementPriority::MOVEMENT_NORMAL); + + bot->GetMotionMaster()->Clear(); + bot->StopMoving(); + bot->SetFacingToObject(zafod); + + WorldPacket gossipHello(CMSG_GOSSIP_HELLO); + gossipHello << zafod->GetGUID(); + bot->GetSession()->HandleGossipHelloOpcode(gossipHello); + + WorldPacket gossipSelect(CMSG_GOSSIP_SELECT_OPTION); + gossipSelect << zafod->GetGUID(); + gossipSelect << uint32(ZAFOD_GOSSIP_MENU_ID) << uint32(ZAFOD_GOSSIP_OPTION); + bot->GetSession()->HandleGossipSelectOptionOpcode(gossipSelect); + return true; +} + +bool IccGunshipRocketPackSetupAction::EquipRocketPack() +{ + EquipAction* equipAction = dynamic_cast(botAI->GetAiObjectContext()->GetAction("equip")); + if (!equipAction) + return false; + + ItemIds ids; + ids.insert(ITEM_GOBLIN_ROCKET_PACK); + equipAction->EquipItems(ids); + return true; +} diff --git a/src/Ai/Raid/ICC/Action/ICCActions_LDW.cpp b/src/Ai/Raid/ICC/Action/ICCActions_LDW.cpp new file mode 100644 index 00000000000..c6573abc331 --- /dev/null +++ b/src/Ai/Raid/ICC/Action/ICCActions_LDW.cpp @@ -0,0 +1,1044 @@ +#include "GenericActions.h" +#include "GenericSpellActions.h" +#include "Multiplier.h" +#include "NearestNpcsValue.h" +#include "ObjectAccessor.h" +#include "Playerbots.h" +#include "ICCActions.h" +#include "ICCTriggers.h" +#include "RtiValue.h" +#include "Vehicle.h" + +// Lady Deathwhisper +bool IccDarkReckoningAction::Execute(Event /*event*/) +{ + constexpr float SAFE_DISTANCE_THRESHOLD = 2.0f; + + if (bot->HasAura(SPELL_DARK_RECKONING) && + bot->GetExactDist2d(ICC_DARK_RECKONING_SAFE_POSITION) > SAFE_DISTANCE_THRESHOLD) + { + return MoveTo(bot->GetMapId(), ICC_DARK_RECKONING_SAFE_POSITION.GetPositionX(), + ICC_DARK_RECKONING_SAFE_POSITION.GetPositionY(), ICC_DARK_RECKONING_SAFE_POSITION.GetPositionZ(), + false, false, false, true, MovementPriority::MOVEMENT_NORMAL); + } + + return false; +} + +bool IccRangedPositionLadyDeathwhisperAction::Execute(Event /*event*/) +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "lady deathwhisper"); + if (!boss) + return false; + + float const currentDistance = bot->GetDistance2d(boss); + float const minDistance = 7.0f; + float const maxDistance = 30.0f; + + if (currentDistance < minDistance || currentDistance > maxDistance) + return false; + + if (!botAI->IsRanged(bot) && !botAI->IsHeal(bot)) + return false; + + return MaintainRangedSpacing(); +} + +bool IccRangedPositionLadyDeathwhisperAction::MaintainRangedSpacing() +{ + float const safeSpacingRadius = 3.0f; + float const moveIncrement = 2.0f; + float const maxMoveDistance = 5.0f; + bool const isRanged = botAI->IsRanged(bot) || botAI->IsHeal(bot); + + if (!isRanged) + return false; + + GuidVector const members = AI_VALUE(GuidVector, "group members"); + + float totalX = 0.0f; + float totalY = 0.0f; + int nearbyCount = 0; + + for (auto const& memberGuid : members) + { + Unit* member = botAI->GetUnit(memberGuid); + if (!member || !member->IsAlive() || member == bot) + continue; + + float const distance = bot->GetExactDist2d(member); + if (distance < safeSpacingRadius) + { + float dx = bot->GetPositionX() - member->GetPositionX(); + float dy = bot->GetPositionY() - member->GetPositionY(); + + float weight = (safeSpacingRadius - distance) / safeSpacingRadius; + totalX += dx * weight; + totalY += dy * weight; + ++nearbyCount; + } + } + + if (nearbyCount > 0) + { + float magnitude = std::sqrt(totalX * totalX + totalY * totalY); + if (magnitude > 0.001f) + { + totalX /= magnitude; + totalY /= magnitude; + + float const moveDistance = std::min(moveIncrement, maxMoveDistance); + + float targetX = bot->GetPositionX() + totalX * moveDistance; + float targetY = bot->GetPositionY() + totalY * moveDistance; + float targetZ = bot->GetPositionZ(); + + if (bot->IsWithinLOS(targetX, targetY, targetZ)) + { + Position targetPos(targetX, targetY, targetZ); + MoveTo(bot->GetMapId(), targetPos.GetPositionX(), targetPos.GetPositionY(), targetPos.GetPositionZ(), + false, false, false, true, MovementPriority::MOVEMENT_NORMAL); + } + else + { + targetX = bot->GetPositionX() + totalX * (moveDistance * 0.5f); + targetY = bot->GetPositionY() + totalY * (moveDistance * 0.5f); + Position targetPos(targetX, targetY, targetZ); + MoveTo(bot->GetMapId(), targetPos.GetPositionX(), targetPos.GetPositionY(), targetPos.GetPositionZ(), + false, false, false, true, MovementPriority::MOVEMENT_NORMAL); + } + } + } + + return false; +} + +bool IccAddsLadyDeathwhisperAction::Execute(Event /*event*/) +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "lady deathwhisper"); + if (!boss) + return false; + + Difficulty diff = bot->GetRaidDifficulty(); + + if (sPlayerbotAIConfig.EnableICCBuffs && diff && + (diff == RAID_DIFFICULTY_10MAN_HEROIC || diff == RAID_DIFFICULTY_25MAN_HEROIC)) + { + if (!bot->HasAura(SPELL_NO_THREAT) && botAI->HasAggro(boss) && !botAI->IsTank(bot)) + bot->AddAura(SPELL_NO_THREAT, bot); + + if (botAI->IsMainTank(bot) && !bot->HasAura(SPELL_SPITEFULL_FURY) && boss->GetVictim() != bot) + bot->AddAura(SPELL_SPITEFULL_FURY, bot); + } + + if (botAI->HasAura("Dominate Mind", bot, false, false) && !bot->HasAura(SPELL_CYCLONE)) + bot->AddAura(SPELL_CYCLONE, bot); + else if (bot->HasAura(SPELL_CYCLONE) && !botAI->HasAura("Dominate Mind", bot, false, false)) + bot->RemoveAura(SPELL_CYCLONE); + + if (Group* group = bot->GetGroup()) + { + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + if (!member || !member->IsAlive() || member == bot) + continue; + + PlayerbotAI* memberBotAI = GET_PLAYERBOT_AI(member); + if (memberBotAI && !memberBotAI->IsRealPlayer()) + continue; + + if (botAI->HasAura("Dominate Mind", member, false, false) && !member->HasAura(SPELL_CYCLONE)) + member->AddAura(SPELL_CYCLONE, member); + else if (member->HasAura(SPELL_CYCLONE) && !botAI->HasAura("Dominate Mind", member, false, false)) + member->RemoveAura(SPELL_CYCLONE); + } + } + + constexpr uint32 shadeEntryId = NPC_SHADE; + + if (botAI->IsTank(bot)) + { + if (bot->HasAura(SPELL_TOUCH_OF_INSIGNIFICANCE)) + bot->RemoveAura(SPELL_TOUCH_OF_INSIGNIFICANCE); + + if (IsTargetedByShade(shadeEntryId)) + return false; + + Unit* currentTarget = bot->GetVictim(); + bool const isAttackingAdd = currentTarget && IsAdd(currentTarget); + if (isAttackingAdd && currentTarget->IsAlive()) + { + if (botAI->IsAssistTank(bot) || !IsAssistTankAlive()) + { + constexpr float MARK_RANGE = 25.0f; + Unit* nearAdd = FindAddNearBoss(boss, MARK_RANGE); + UpdateRaidTargetIcon(nearAdd ? nearAdd : boss); + } + + if (currentTarget->GetVictim() == bot) + { + constexpr float KITE_THRESHOLD = 25.0f; + if (!boss->HealthAbovePct(95)) + { + Position const& tankPos = ICC_LDW_TANK_POSTION; + if (bot->GetExactDist2d(tankPos.GetPositionX(), tankPos.GetPositionY()) > KITE_THRESHOLD) + MoveTo(bot->GetMapId(), tankPos.GetPositionX(), tankPos.GetPositionY(), tankPos.GetPositionZ(), + false, false, false, true, MovementPriority::MOVEMENT_COMBAT); + } + else + { + constexpr float CONSOLIDATE_THRESHOLD = 5.0f; + if (currentTarget->GetExactDist2d(boss) > CONSOLIDATE_THRESHOLD) + MoveTo(bot->GetMapId(), boss->GetPositionX(), boss->GetPositionY(), boss->GetPositionZ(), false, + false, false, true, MovementPriority::MOVEMENT_COMBAT); + } + } + + return false; + } + + Unit* targetAdd = FindAndCollectAdd(boss); + if (targetAdd) + { + if (botAI->IsAssistTank(bot) || !IsAssistTankAlive()) + { + constexpr float MARK_RANGE = 25.0f; + Unit* nearAdd = FindAddNearBoss(boss, MARK_RANGE); + UpdateRaidTargetIcon(nearAdd ? nearAdd : boss); + } + } + + if (boss->IsAlive()) + { + { + constexpr float MARK_RANGE = 25.0f; + Unit* nearAdd = FindAddNearBoss(boss, MARK_RANGE); + UpdateRaidTargetIcon(nearAdd ? nearAdd : boss); + } + + if (!boss->HealthAbovePct(95)) + { + bool anyTankHasAggro = false; + if (Group* group = bot->GetGroup()) + { + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + if (member && member->IsAlive() && botAI->IsTank(member) && boss->GetVictim() == member) + { + anyTankHasAggro = true; + break; + } + } + } + + if (anyTankHasAggro) + { + Position const& tankPos = ICC_LDW_TANK_POSTION; + if (bot->GetExactDist2d(tankPos.GetPositionX(), tankPos.GetPositionY()) > 5.0f) + MoveTo(bot->GetMapId(), tankPos.GetPositionX(), tankPos.GetPositionY(), tankPos.GetPositionZ(), + false, false, false, true, MovementPriority::MOVEMENT_COMBAT); + } + } + } + + return false; + } + + // Phase 1 mana drain: every hunter keeps its own Viper Sting up on boss. + // Per-hunter aura tracking via caster-GUID so multiple hunters stack cleanly. + if (bot->getClass() == CLASS_HUNTER && boss && boss->HealthAbovePct(95) && boss->IsAlive() && !boss->HasAura(SPELL_VIPER_STING, bot->GetGUID()) && botAI->CanCastSpell(SPELL_VIPER_STING, boss)) + botAI->CastSpell("viper sting", boss); + + if (HandleNonTankAddEvasion()) + return false; + + ApplyNearbyAddCC(); + + if (!boss->HealthAbovePct(95) && (botAI->IsRanged(bot) || botAI->IsHeal(bot))) + { + bool threatened = false; + GuidVector const npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); + for (auto const& npcGuid : npcs) + { + Unit* unit = botAI->GetUnit(npcGuid); + if (unit && unit->IsAlive() && unit->GetVictim() == bot && (IsAdd(unit) || unit->GetEntry() == NPC_SHADE)) + { + threatened = true; + break; + } + } + + if (!threatened) + { + constexpr float REGROUP_DISTANCE = 18.0f; + Position const& tankPos = ICC_LDW_TANK_POSTION; + if (bot->GetExactDist2d(tankPos.GetPositionX(), tankPos.GetPositionY()) > REGROUP_DISTANCE) + MoveTo(bot->GetMapId(), tankPos.GetPositionX(), tankPos.GetPositionY(), tankPos.GetPositionZ(), + false, false, false, true, MovementPriority::MOVEMENT_NORMAL); + } + } + + if (IsAddsAlive()) + return false; + + if (boss->HealthAbovePct(95)) + return EngageBoss(); + + return false; +} + +Unit* IccAddsLadyDeathwhisperAction::FindAndCollectAdd(Unit* boss) +{ + GuidVector const npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); + + Unit* bestAdd = nullptr; + float bestScore = FLT_MAX; + + for (auto const& npcGuid : npcs) + { + Unit* unit = botAI->GetUnit(npcGuid); + if (!unit || !unit->IsAlive()) + continue; + + if (!IsAdd(unit)) + continue; + + Unit* victim = unit->GetVictim(); + if (victim && victim->IsPlayer() && botAI->IsTank(victim->ToPlayer())) + continue; + + float distance = bot->GetExactDist(unit); + float score = distance; + if (victim && victim != bot) + score += 5.0f; + + if (score < bestScore) + { + bestScore = score; + bestAdd = unit; + } + } + + if (!bestAdd) + return nullptr; + + float const tauntRange = 30.0f; + float const currentDist = bot->GetExactDist(bestAdd); + + if (currentDist > tauntRange) + { + float moveX = bot->GetPositionX(); + float moveY = bot->GetPositionY(); + float moveZ = bot->GetPositionZ(); + float step = 5.0f; + float dx = bestAdd->GetPositionX() - bot->GetPositionX(); + float dy = bestAdd->GetPositionY() - bot->GetPositionY(); + float dist = std::sqrt(dx * dx + dy * dy); + if (dist > 0.1f) + { + dx /= dist; + dy /= dist; + float moveDist = std::min(step, dist - (tauntRange - 5.0f)); + moveX = bot->GetPositionX() + dx * moveDist; + moveY = bot->GetPositionY() + dy * moveDist; + } + MoveTo(bot->GetMapId(), moveX, moveY, moveZ, false, false, false, true, MovementPriority::MOVEMENT_COMBAT); + return nullptr; + } + + bool taunted = false; + if (botAI->CastSpell("taunt", bestAdd)) + taunted = true; + else + { + switch (bot->getClass()) + { + case CLASS_PALADIN: + taunted = botAI->CastSpell("hand of reckoning", bestAdd); + break; + case CLASS_DEATH_KNIGHT: + taunted = botAI->CastSpell("dark command", bestAdd); + break; + case CLASS_DRUID: + taunted = botAI->CastSpell("growl", bestAdd); + break; + default: + break; + } + } + + if (!taunted) + { + if (currentDist > 5.0f) + { + float dx = bestAdd->GetPositionX() - bot->GetPositionX(); + float dy = bestAdd->GetPositionY() - bot->GetPositionY(); + float dist = std::sqrt(dx * dx + dy * dy); + dx /= dist; + dy /= dist; + float step = std::max(0.5f, std::min(3.0f, dist - 3.0f)); + float moveX = bot->GetPositionX() + dx * step; + float moveY = bot->GetPositionY() + dy * step; + MoveTo(bot->GetMapId(), moveX, moveY, bot->GetPositionZ(), false, false, false, true, + MovementPriority::MOVEMENT_COMBAT); + return nullptr; + } + } + + if (boss && boss->HealthAbovePct(95)) + { + constexpr float CONSOLIDATE_THRESHOLD = 5.0f; + if (bestAdd->GetExactDist2d(boss) > CONSOLIDATE_THRESHOLD) + MoveTo(bot->GetMapId(), boss->GetPositionX(), boss->GetPositionY(), boss->GetPositionZ(), + false, false, false, true, MovementPriority::MOVEMENT_COMBAT); + } + + return bestAdd; +} + +bool IccAddsLadyDeathwhisperAction::HandleNonTankAddEvasion() +{ + GuidVector const npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); + + bool targetedByAdd = false; + for (auto const& npcGuid : npcs) + { + Unit* unit = botAI->GetUnit(npcGuid); + if (!unit || !unit->IsAlive() || unit->GetVictim() != bot) + continue; + + if (IsAdd(unit)) + { + targetedByAdd = true; + ApplyCCToAdd(unit); + } + } + + if (!targetedByAdd) + return false; + + Unit* boss = AI_VALUE2(Unit*, "find target", "lady deathwhisper"); + if (!boss) + return false; + + if (boss->HealthAbovePct(95)) + { + Unit* closestAdd = nullptr; + float closestDist = FLT_MAX; + GuidVector const npcs2 = AI_VALUE(GuidVector, "nearest hostile npcs"); + for (auto const& npcGuid : npcs2) + { + Unit* unit = botAI->GetUnit(npcGuid); + if (!unit || !unit->IsAlive() || unit->GetVictim() != bot || !IsAdd(unit)) + continue; + + float d = bot->GetExactDist2d(unit); + if (d < closestDist) + { + closestDist = d; + closestAdd = unit; + } + } + + if (!closestAdd) + return false; + + constexpr float KEEP_DISTANCE = 10.0f; + if (closestDist >= KEEP_DISTANCE) + return false; + + float dx = bot->GetPositionX() - closestAdd->GetPositionX(); + float dy = bot->GetPositionY() - closestAdd->GetPositionY(); + float len = std::sqrt(dx * dx + dy * dy); + if (len < 0.001f) + return false; + + dx /= len; + dy /= len; + float moveX = bot->GetPositionX() + dx * (KEEP_DISTANCE - closestDist + 2.0f); + float moveY = bot->GetPositionY() + dy * (KEEP_DISTANCE - closestDist + 2.0f); + return MoveTo(bot->GetMapId(), moveX, moveY, bot->GetPositionZ(), false, false, false, false, + MovementPriority::MOVEMENT_COMBAT); + } + else + { + constexpr float ARRIVAL_THRESHOLD = 5.0f; + if (bot->GetExactDist2d(boss) <= ARRIVAL_THRESHOLD) + return false; + + return MoveTo(bot->GetMapId(), boss->GetPositionX(), boss->GetPositionY(), boss->GetPositionZ(), false, false, + false, false, MovementPriority::MOVEMENT_COMBAT); + } +} + +bool IccAddsLadyDeathwhisperAction::ApplyNearbyAddCC() +{ + GuidVector const npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); + constexpr float CC_RANGE = 10.0f; + + for (auto const& npcGuid : npcs) + { + Unit* unit = botAI->GetUnit(npcGuid); + if (!unit || !unit->IsAlive()) + continue; + + if (!IsAdd(unit)) + continue; + + if (bot->GetDistance(unit) <= CC_RANGE) + ApplyCCToAdd(unit); + } + + return false; +} + +bool IccAddsLadyDeathwhisperAction::ApplyCCToAdd(Unit* add) +{ + if (!add || !add->IsAlive()) + return false; + + switch (bot->getClass()) + { + case CLASS_MAGE: + if (!botAI->HasAura("Frost Nova", add)) + botAI->CastSpell("Frost Nova", add); + break; + case CLASS_DRUID: + if (!botAI->HasAura("Entangling Roots", add)) + botAI->CastSpell("Entangling Roots", add); + break; + case CLASS_PALADIN: + if (!botAI->HasAura("Hammer of Justice", add)) + botAI->CastSpell("Hammer of Justice", add); + break; + case CLASS_WARRIOR: + if (!botAI->HasAura("Hamstring", add)) + botAI->CastSpell("Hamstring", add); + break; + case CLASS_HUNTER: + if (!botAI->HasAura("Concussive Shot", add)) + botAI->CastSpell("Concussive Shot", add); + break; + case CLASS_ROGUE: + if (!botAI->HasAura("Kidney Shot", add)) + botAI->CastSpell("Kidney Shot", add); + break; + case CLASS_SHAMAN: + if (!botAI->HasAura("Frost Shock", add)) + botAI->CastSpell("Frost Shock", add); + break; + case CLASS_DEATH_KNIGHT: + if (!botAI->HasAura("Chains of Ice", add)) + botAI->CastSpell("Chains of Ice", add); + break; + case CLASS_PRIEST: + if (!botAI->HasAura("Psychic Scream", add)) + botAI->CastSpell("Psychic Scream", add); + break; + case CLASS_WARLOCK: + if (!botAI->HasAura("Fear", add)) + botAI->CastSpell("Fear", add); + break; + default: + break; + } + + return false; +} + +bool IccAddsLadyDeathwhisperAction::IsAddsAlive() +{ + GuidVector const npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); + for (auto const& npcGuid : npcs) + { + Unit* unit = botAI->GetUnit(npcGuid); + if (unit && unit->IsAlive() && IsAdd(unit)) + return true; + } + + return false; +} + +bool IccAddsLadyDeathwhisperAction::EngageBoss() +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "lady deathwhisper"); + if (!boss || !boss->IsAlive()) + return false; + + bool const isMelee = botAI->IsMelee(bot); + bool const isRanged = botAI->IsRanged(bot) || botAI->IsHeal(bot); + + if (isMelee) + { + float dist = bot->GetDistance(boss); + if (dist > 25.0f) + { + float dx = boss->GetPositionX() - bot->GetPositionX(); + float dy = boss->GetPositionY() - bot->GetPositionY(); + float len = std::sqrt(dx * dx + dy * dy); + if (len > 0.1f) + { + dx /= len; + dy /= len; + float step = std::min(3.0f, len); + float moveX = bot->GetPositionX() + dx * step; + float moveY = bot->GetPositionY() + dy * step; + MoveTo(bot->GetMapId(), moveX, moveY, bot->GetPositionZ(), false, false, false, true, + MovementPriority::MOVEMENT_COMBAT); + return false; + } + } + } + + if (isRanged) + { + Position const rangedPos = ICC_LDW_RANGED_POSITION; + float const distToPos = bot->GetDistance2d(rangedPos.GetPositionX(), rangedPos.GetPositionY()); + if (distToPos > 30.0f) + { + MoveTo(bot->GetMapId(), rangedPos.GetPositionX(), rangedPos.GetPositionY(), rangedPos.GetPositionZ(), false, + false, false, true, MovementPriority::MOVEMENT_COMBAT); + return false; + } + } + + return false; +} + +Unit* IccAddsLadyDeathwhisperAction::FindAddNearBoss(Unit* boss, float maxDist) +{ + GuidVector const npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); + Unit* best = nullptr; + float bestDist = maxDist; + + for (auto const& npcGuid : npcs) + { + Unit* unit = botAI->GetUnit(npcGuid); + if (!unit || !unit->IsAlive() || !IsAdd(unit)) + continue; + + float dist = unit->GetExactDist2d(boss); + if (dist < bestDist) + { + bestDist = dist; + best = unit; + } + } + + return best; +} + +bool IccAddsLadyDeathwhisperAction::IsAdd(Unit* unit) +{ + if (!unit) + return false; + + uint32 entry = unit->GetEntry(); + return std::find(addEntriesLady.begin(), addEntriesLady.end(), entry) != addEntriesLady.end(); +} + +bool IccAddsLadyDeathwhisperAction::IsAssistTankAlive() +{ + Group* group = bot->GetGroup(); + if (!group) + return false; + + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + if (!member || !member->IsAlive() || member == bot) + continue; + + if (botAI->IsTank(member) && !botAI->IsMainTank(member)) + return true; + } + + return false; +} + +bool IccAddsLadyDeathwhisperAction::IsTargetedByShade(uint32 shadeEntry) +{ + GuidVector const npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); + for (auto const& npcGuid : npcs) + { + Unit* unit = botAI->GetUnit(npcGuid); + if (unit && unit->GetEntry() == shadeEntry && unit->GetVictim() == bot) + return true; + } + + return false; +} + +bool IccAddsLadyDeathwhisperAction::MoveTowardPosition(Position const& position, float incrementSize) +{ + float const dirX = position.GetPositionX() - bot->GetPositionX(); + float const dirY = position.GetPositionY() - bot->GetPositionY(); + float const length = std::sqrt(dirX * dirX + dirY * dirY); + + if (length < 0.001f) + return false; + + float const normalizedDirX = dirX / length; + float const normalizedDirY = dirY / length; + + float const moveX = bot->GetPositionX() + normalizedDirX * incrementSize; + float const moveY = bot->GetPositionY() + normalizedDirY * incrementSize; + + return MoveTo(bot->GetMapId(), moveX, moveY, bot->GetPositionZ(), false, false, false, false, + MovementPriority::MOVEMENT_COMBAT); +} + +bool IccAddsLadyDeathwhisperAction::HandleAddTargeting(Unit* boss) +{ + GuidVector const targets = AI_VALUE(GuidVector, "possible targets no los"); + + Unit* priorityTarget = nullptr; + bool hasValidAdds = false; + + for (auto const& entry : addEntriesLady) + { + for (auto const& guid : targets) + { + Unit* unit = botAI->GetUnit(guid); + if (unit && unit->IsAlive() && unit->GetEntry() == entry) + { + priorityTarget = unit; + hasValidAdds = true; + break; + } + } + + if (priorityTarget) + break; + } + + if (!hasValidAdds && boss->IsAlive()) + priorityTarget = boss; + + if (priorityTarget) + UpdateRaidTargetIcon(priorityTarget); + + return false; +} + +bool IccAddsLadyDeathwhisperAction::UpdateRaidTargetIcon(Unit* target) +{ + static constexpr uint8 SKULL_ICON_INDEX = 7; + + if (!target || !target->IsAlive()) + return false; + + Group* group = bot->GetGroup(); + if (!group) + return false; + + ObjectGuid currentSkull = group->GetTargetIcon(SKULL_ICON_INDEX); + Unit* currentSkullUnit = botAI->GetUnit(currentSkull); + + bool const currentIsAdd = currentSkullUnit && currentSkullUnit->IsAlive() && IsAdd(currentSkullUnit); + bool const targetIsAdd = IsAdd(target); + + bool const shouldUpdate = !currentSkullUnit || !currentSkullUnit->IsAlive() || (targetIsAdd && !currentIsAdd); + + if (shouldUpdate) + group->SetTargetIcon(SKULL_ICON_INDEX, bot->GetGUID(), target->GetGUID()); + + return false; +} + +bool IccShadeLadyDeathwhisperAction::Execute(Event /*event*/) +{ + static constexpr uint32 VENGEFUL_SHADE_ID = NPC_SHADE; + static constexpr float SAFE_DISTANCE = 12.0f; + + GuidVector const npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); + + float fleeX = 0.0f; + float fleeY = 0.0f; + float closestDist = SAFE_DISTANCE; + int shadeCount = 0; + + for (auto const& npcGuid : npcs) + { + Unit* shade = botAI->GetUnit(npcGuid); + + if (!shade || shade->GetEntry() != VENGEFUL_SHADE_ID) + continue; + + if (!shade->GetVictim() || shade->GetVictim()->GetGUID() != bot->GetGUID()) + continue; + + float const currentDistance = bot->GetDistance2d(shade); + + if (currentDistance >= SAFE_DISTANCE) + continue; + + float dx = bot->GetPositionX() - shade->GetPositionX(); + float dy = bot->GetPositionY() - shade->GetPositionY(); + float dist = std::sqrt(dx * dx + dy * dy); + + if (dist < 0.001f) + continue; + + float weight = (SAFE_DISTANCE - currentDistance) / dist; + fleeX += dx * weight; + fleeY += dy * weight; + + if (currentDistance < closestDist) + closestDist = currentDistance; + + shadeCount++; + } + + if (shadeCount == 0) + return false; + + float fleeDist = std::sqrt(fleeX * fleeX + fleeY * fleeY); + if (fleeDist < 0.001f) + return false; + + fleeX /= fleeDist; + fleeY /= fleeDist; + + float moveDistance = SAFE_DISTANCE - closestDist + 2.0f; + float targetX = bot->GetPositionX() + fleeX * moveDistance; + float targetY = bot->GetPositionY() + fleeY * moveDistance; + float targetZ = bot->GetPositionZ(); + + if (!bot->IsWithinLOS(targetX, targetY, targetZ)) + { + moveDistance *= 0.5f; + targetX = bot->GetPositionX() + fleeX * moveDistance; + targetY = bot->GetPositionY() + fleeY * moveDistance; + } + + botAI->Reset(); + return MoveTo(bot->GetMapId(), targetX, targetY, targetZ, false, false, false, false, + MovementPriority::MOVEMENT_COMBAT); +} + +// Rotting Giant WiP (waiting for core fix) +bool IccRottingFrostGiantTankPositionAction::Execute(Event /*event*/) +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "rotting frost giant"); + if (!boss) + return false; + + Aura* aura = botAI->GetAura("death plague", bot, false, false); + if (aura) + bot->RemoveAura(aura->GetId()); + + /* TODO: code works for handling plague, but atm script is bugged and one bot can have 2 plagues at the same time or + when cured, which should not happen, and it is immpossible to handle plague atm the legit way. const bool hasCure + = botAI->GetAura("recently infected", bot) != nullptr; + + // Tank behavior - unchanged + if (botAI->IsTank(bot) && botAI->HasAggro(boss) && !isInfected) + if (bot->GetExactDist2d(ICC_ROTTING_FROST_GIANT_TANK_POSITION) > 5.0f) + return MoveTo(bot->GetMapId(), ICC_ROTTING_FROST_GIANT_TANK_POSITION.GetPositionX(), + ICC_ROTTING_FROST_GIANT_TANK_POSITION.GetPositionY(), + ICC_ROTTING_FROST_GIANT_TANK_POSITION.GetPositionZ(), false, false, false, true, + MovementPriority::MOVEMENT_NORMAL); + + if (botAI->IsTank(bot)) + return false; + + // Handle infected bot behavior - move near a non-infected, non-cured bot + if (isInfected) + { + const GuidVector members = AI_VALUE(GuidVector, "group members"); + + // Count how many bots are targeting each potential target + std::map targetCounts; + + // First, identify all infected bots and their current targets (approximate) + for (auto const& memberGuid : members) + { + Unit* member = botAI->GetUnit(memberGuid); + if (!member || !member->IsAlive() || member == bot) + continue; + + const bool memberIsInfected = botAI->GetAura("death plague", member) != nullptr; + + if (memberIsInfected) + { + // Find the nearest non-infected bot to this infected bot (as a guess of its target) + float minDist = 5.0f; // Only count if they're close enough to likely be targeting + Unit* likelyTarget = nullptr; + + for (auto const& targetGuid : members) + { + Unit* potentialTarget = botAI->GetUnit(targetGuid); + if (!potentialTarget || !potentialTarget->IsAlive() || potentialTarget == member) + continue; + + const bool targetIsInfected = botAI->GetAura("death plague", potentialTarget) != nullptr; + const bool targetHasCure = botAI->GetAura("recently infected", potentialTarget) != nullptr; + + if (!targetIsInfected && !targetHasCure) + { + float dist = member->GetExactDist2d(potentialTarget); + if (dist < minDist) + { + minDist = dist; + likelyTarget = potentialTarget; + } + } + } + + if (likelyTarget) + { + targetCounts[likelyTarget->GetGUID()]++; + } + } + } + + // Find viable targets and score them based on various factors + std::vector> viableTargets; + + // First try to find ranged, non-infected, non-cured bots + for (auto const& memberGuid : members) + { + Unit* member = botAI->GetUnit(memberGuid); + if (!member || !member->IsAlive() || member == bot) + continue; + + const bool memberHasCure = botAI->GetAura("recently infected", member) != nullptr; + const bool memberIsInfected = botAI->GetAura("death plague", member) != nullptr; + + if (!memberIsInfected && !memberHasCure) + { + // Base score is distance (lower is better) + float score = bot->GetExactDist2d(member); + + // Prefer ranged targets + if (botAI->IsRanged(bot)) + { + score *= 0.7f; // Bonus for ranged targets + } + + // Apply penalty based on how many other infected bots are targeting this one + int targetingCount = targetCounts[member->GetGUID()]; + score *= (1.0f + targetingCount * 0.5f); // Increase score (worse) for heavily targeted bots + + viableTargets.push_back(std::make_pair(member, score)); + } + } + + // Sort targets by score (lowest/best first) + std::sort(viableTargets.begin(), viableTargets.end(), + [](const std::pair& a, const std::pair& b) + { return a.second < b.second; }); + + // Choose the best target + Unit* targetBot = nullptr; + if (!viableTargets.empty()) + { + targetBot = viableTargets[0].first; + } + + // Move to target bot if found + if (targetBot) + { + // If we're already close enough (1 yard), no need to move + if (bot->GetExactDist2d(targetBot) > 1.0f) + { + // Calculate a unique angle based on bot's GUID to ensure different approach angles + // This helps spread infected bots around the target + uint32 guidLow = bot->GetGUID().GetCounter(); + float angleOffset = float(guidLow % 628) / 100.0f; // Random angle between 0 and 2π + + // Calculate position 1 yard away from target at our unique angle + float angle = targetBot->GetOrientation() + angleOffset; + float targetX = targetBot->GetPositionX() + cos(angle) * 1.0f; + float targetY = targetBot->GetPositionY() + sin(angle) * 1.0f; + float targetZ = targetBot->GetPositionZ(); + + return MoveTo(bot->GetMapId(), targetX, targetY, targetZ, false, false, false, true, + MovementPriority::MOVEMENT_COMBAT); + } + return true; // Already in position + } + // No suitable target found, continue with normal behavior + } + + // For ranged bots, only spread from non-infected bots + if (botAI->IsRanged(bot)) + { + const float safeSpacingRadius = 11.0f; + const float moveIncrement = 2.0f; + const float maxMoveDistance = 15.0f; + + const GuidVector members = AI_VALUE(GuidVector, "group members"); + + // Calculate a combined vector representing all nearby NON-INFECTED members' positions + float totalX = 0.0f; + float totalY = 0.0f; + int nearbyCount = 0; + + for (auto const& memberGuid : members) + { + Unit* member = botAI->GetUnit(memberGuid); + if (!member || !member->IsAlive() || member == bot) + continue; + + // Only spread from non-infected bots (can stay near infected or cured bots) + const bool memberIsInfected = botAI->GetAura("death plague", member) != nullptr; + if (memberIsInfected) + continue; + + const float distance = bot->GetExactDist2d(member); + if (distance < safeSpacingRadius) + { + // Calculate vector from member to bot + float dx = bot->GetPositionX() - member->GetPositionX(); + float dy = bot->GetPositionY() - member->GetPositionY(); + + // Weight by inverse distance (closer members have more influence) + float weight = (safeSpacingRadius - distance) / safeSpacingRadius; + totalX += dx * weight; + totalY += dy * weight; + nearbyCount++; + } + } + + // If we have nearby non-infected members, move away in the combined direction + if (nearbyCount > 0) + { + // Normalize the combined vector + float magnitude = std::sqrt(totalX * totalX + totalY * totalY); + if (magnitude > 0.001f) // Avoid division by zero + { + totalX /= magnitude; + totalY /= magnitude; + + // Calculate move distance based on nearest member + float moveDistance = std::min(moveIncrement, maxMoveDistance); + + // Create target position in the combined direction + float targetX = bot->GetPositionX() + totalX * moveDistance; + float targetY = bot->GetPositionY() + totalY * moveDistance; + float targetZ = bot->GetPositionZ(); // Maintain current Z + + // Check if the target position is valid and move there + if (bot->IsWithinLOS(targetX, targetY, targetZ)) + { + return MoveTo(bot->GetMapId(), targetX, targetY, targetZ, false, false, false, true, + MovementPriority::MOVEMENT_NORMAL); + } + else + { + // If los check fails, try shorter distance + targetX = bot->GetPositionX() + totalX * (moveDistance * 0.5f); + targetY = bot->GetPositionY() + totalY * (moveDistance * 0.5f); + return MoveTo(bot->GetMapId(), targetX, targetY, targetZ, false, false, false, true, + MovementPriority::MOVEMENT_NORMAL); + } + } + } + } + */ + return false; +} diff --git a/src/Ai/Raid/ICC/Action/ICCActions_LK.cpp b/src/Ai/Raid/ICC/Action/ICCActions_LK.cpp new file mode 100644 index 00000000000..1230e9d6ca5 --- /dev/null +++ b/src/Ai/Raid/ICC/Action/ICCActions_LK.cpp @@ -0,0 +1,4568 @@ +#include "ICCActions.h" +#include "NearestNpcsValue.h" +#include "ObjectAccessor.h" +#include "Playerbots.h" +#include "Vehicle.h" +#include "RtiValue.h" +#include "GenericSpellActions.h" +#include "GenericActions.h" +#include "ICCTriggers.h" +#include "ICCScripts.h" +#include "Multiplier.h" + +static bool IsLkShambling(uint32 entry) +{ + return entry == NPC_SHAMBLING_HORROR1 || entry == NPC_SHAMBLING_HORROR2 || + entry == NPC_SHAMBLING_HORROR3 || entry == NPC_SHAMBLING_HORROR4; +} + +static bool IsLkRagingSpirit(uint32 entry) +{ + return entry == NPC_RAGING_SPIRIT1 || entry == NPC_RAGING_SPIRIT2 || + entry == NPC_RAGING_SPIRIT3 || entry == NPC_RAGING_SPIRIT4; +} + +static bool IsLkValkyr(Unit* unit) +{ + if (!unit) + return false; + + uint32 const entry = unit->GetEntry(); + return entry == NPC_VALKYR_SHADOWGUARD1 || entry == NPC_VALKYR_SHADOWGUARD2 || + entry == NPC_VALKYR_SHADOWGUARD3 || entry == NPC_VALKYR_SHADOWGUARD4; +} + +static bool IsLkVileSpirit(Unit* unit) +{ + if (!unit) + return false; + + uint32 const entry = unit->GetEntry(); + return entry == NPC_VILE_SPIRIT1 || entry == NPC_VILE_SPIRIT2 || + entry == NPC_VILE_SPIRIT3 || entry == NPC_VILE_SPIRIT4; +} + +static bool IsLkWickedSpirit(uint32 entry) +{ + return entry == NPC_WICKED_SPIRIT1 || entry == NPC_WICKED_SPIRIT2 || + entry == NPC_WICKED_SPIRIT3 || entry == NPC_WICKED_SPIRIT4; +} + +static bool IsIceSphere(uint32 entry) +{ + return entry == NPC_ICE_SPHERE1 || entry == NPC_ICE_SPHERE2 || + entry == NPC_ICE_SPHERE3 || entry == NPC_ICE_SPHERE4; +} + +static bool IsLkCollectibleAdd(Unit* unit) +{ + if (!unit || !unit->IsAlive()) + return false; + + uint32 const entry = unit->GetEntry(); + return IsLkShambling(entry) || IsLkRagingSpirit(entry) || + entry == NPC_DRUDGE_GHOUL1 || entry == NPC_DRUDGE_GHOUL2 || + entry == NPC_DRUDGE_GHOUL3 || entry == NPC_DRUDGE_GHOUL4; +} + +static bool HasFrontalAbility(uint32 entry) +{ + return IsLkShambling(entry) || IsLkRagingSpirit(entry); +} + +static bool IsHeroicLk(Difficulty diff) +{ + return diff == RAID_DIFFICULTY_10MAN_HEROIC || diff == RAID_DIFFICULTY_25MAN_HEROIC; +} + +static bool HasAnyRemorselessWinter(Unit* boss) +{ + if (!boss) + return false; + + if (boss->HasAura(SPELL_REMORSELESS_WINTER1) || boss->HasAura(SPELL_REMORSELESS_WINTER2) || + boss->HasAura(SPELL_REMORSELESS_WINTER3) || boss->HasAura(SPELL_REMORSELESS_WINTER4) || + boss->HasAura(SPELL_REMORSELESS_WINTER5) || boss->HasAura(SPELL_REMORSELESS_WINTER6) || + boss->HasAura(SPELL_REMORSELESS_WINTER7) || boss->HasAura(SPELL_REMORSELESS_WINTER8)) + return true; + + if (!boss->HasUnitState(UNIT_STATE_CASTING)) + return false; + + return boss->FindCurrentSpellBySpellId(SPELL_REMORSELESS_WINTER1) || + boss->FindCurrentSpellBySpellId(SPELL_REMORSELESS_WINTER2) || + boss->FindCurrentSpellBySpellId(SPELL_REMORSELESS_WINTER3) || + boss->FindCurrentSpellBySpellId(SPELL_REMORSELESS_WINTER4) || + boss->FindCurrentSpellBySpellId(SPELL_REMORSELESS_WINTER5) || + boss->FindCurrentSpellBySpellId(SPELL_REMORSELESS_WINTER6) || + boss->FindCurrentSpellBySpellId(SPELL_REMORSELESS_WINTER7) || + boss->FindCurrentSpellBySpellId(SPELL_REMORSELESS_WINTER8); +} + +// Apply heroic cheat buffs to bots & players +static void ApplyHeroicBuffToMember(PlayerbotAI* botAI, Player* member, bool applyNoThreat) +{ + if (!member->HasAura(SPELL_EXPERIENCED)) + member->AddAura(SPELL_EXPERIENCED, member); + + if (!member->HasAura(SPELL_AGEIS_OF_DALARAN)) + member->AddAura(SPELL_AGEIS_OF_DALARAN, member); + + if (!member->HasAura(SPELL_PAIN_SUPPRESION)) + member->AddAura(SPELL_PAIN_SUPPRESION, member); + + if (applyNoThreat && !botAI->IsTank(member) && !member->HasAura(SPELL_NO_THREAT)) + member->AddAura(SPELL_NO_THREAT, member); +} + +static float GetDefileEffectiveRadius(Unit const* defile, Difficulty diff) +{ + static constexpr float BASE_RADIUS = 6.0f; + + for (uint32 const auraId : DEFILE_AURAS) + { + Aura const* grow = defile->GetAura(auraId); + if (!grow) + continue; + + float const multiplier = + (diff == RAID_DIFFICULTY_10MAN_HEROIC || diff == RAID_DIFFICULTY_10MAN_NORMAL) ? 1.4f : 0.95f; + + return BASE_RADIUS + grow->GetStackAmount() * multiplier; + } + + return BASE_RADIUS; +} + +// Compute the centroid of alive group members for a stable reference point. +// Unlike a single reference player, the centroid barely shifts when one +// player dies, preventing the raid from oscillating between positions. +static Position ComputeGroupCentroid(Player* bot) +{ + Group* group = bot->GetGroup(); + if (!group) + return Position(bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ()); + + float sumX = 0.0f; + float sumY = 0.0f; + int count = 0; + + for (GroupReference* itr = group->GetFirstMember(); itr; itr = itr->next()) + { + Player* member = itr->GetSource(); + if (!member || !member->IsAlive() || !member->IsInWorld()) + continue; + + sumX += member->GetPositionX(); + sumY += member->GetPositionY(); + ++count; + } + + if (count == 0) + return Position(bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ()); + + return Position(sumX / count, sumY / count, bot->GetPositionZ()); +} + +static Position const& SelectClosestOf3(Position const& ref, Position const& p1, Position const& p2, Position const& p3) +{ + float const dx1 = ref.GetPositionX() - p1.GetPositionX(); + float const dy1 = ref.GetPositionY() - p1.GetPositionY(); + float const d1 = dx1 * dx1 + dy1 * dy1; + + float const dx2 = ref.GetPositionX() - p2.GetPositionX(); + float const dy2 = ref.GetPositionY() - p2.GetPositionY(); + float const d2 = dx2 * dx2 + dy2 * dy2; + + float const dx3 = ref.GetPositionX() - p3.GetPositionX(); + float const dy3 = ref.GetPositionY() - p3.GetPositionY(); + float const d3 = dx3 * dx3 + dy3 * dy3; + + if (d2 < d1 && d2 < d3) + return p2; + if (d3 < d1 && d3 < d2) + return p3; + return p1; +} + +// Single-target taunt with class-specific fallbacks +static bool CastSingleTargetTaunt(PlayerbotAI* botAI, Player* bot, Unit* target) +{ + if (!target || !target->IsAlive()) + return false; + + if (botAI->CastSpell("taunt", target)) + return true; + + switch (bot->getClass()) + { + case CLASS_PALADIN: + if (botAI->CastSpell("hand of reckoning", target)) + return true; + break; + case CLASS_DEATH_KNIGHT: + if (botAI->CastSpell("dark command", target)) + return true; + break; + case CLASS_DRUID: + if (botAI->CastSpell("growl", target)) + return true; + break; + default: + break; + } + + // Ranged poke generates threat without moving + if (botAI->CastSpell("shoot", target) || botAI->CastSpell("throw", target)) + return true; + + return false; +} + +// AoE taunt — returns true if a spell was cast +static bool CastAoeTaunt(PlayerbotAI* botAI, Player* bot) +{ + switch (bot->getClass()) + { + case CLASS_WARRIOR: + if (botAI->CastSpell("challenging shout", bot)) + return true; + break; + case CLASS_DRUID: + if (botAI->CastSpell("challenging roar", bot)) + return true; + break; + default: + break; + } + return false; +} + +bool IccLichKingShadowTrapAction::Execute(Event /*event*/) +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "the lich king"); + if (!boss || !botAI->IsTank(bot)) + return false; + + Difficulty const diff = bot->GetRaidDifficulty(); + + if (sPlayerbotAIConfig.EnableICCBuffs && IsHeroicLk(diff)) + ApplyHeroicBuffToMember(botAI, bot, false); + + static constexpr float CIRCLE_RADIUS = 20.0f; + static constexpr float SAFE_DISTANCE = 12.0f; + static constexpr float SEARCH_DISTANCE = SAFE_DISTANCE + 5.0f; + static constexpr int TEST_POSITIONS = 16; + static constexpr float ANGLE_STEP = 2.0f * float(M_PI) / TEST_POSITIONS; + + float const centerX = ICC_LICH_KING_ASSISTHC_POSITION.GetPositionX(); + float const centerY = ICC_LICH_KING_ASSISTHC_POSITION.GetPositionY(); + float const centerZ = ICC_LICH_KING_ASSISTHC_POSITION.GetPositionZ(); + + // Collect shadow traps within search range + GuidVector const npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); + std::vector nearbyTraps; + + for (ObjectGuid const& guid : npcs) + { + Unit* unit = botAI->GetUnit(guid); + if (!unit || !unit->IsAlive() || unit->GetEntry() != NPC_SHADOW_TRAP) + continue; + + if (bot->GetDistance(unit) < SEARCH_DISTANCE) + nearbyTraps.push_back(unit); + } + + if (nearbyTraps.empty()) + return false; + + // Check whether the current position is already safe + auto isSafePosition = [&](float x, float y) -> bool + { + for (Unit const* trap : nearbyTraps) + { + if (trap->GetDistance2d(x, y) < SAFE_DISTANCE) + return false; + } + return true; + }; + + if (isSafePosition(bot->GetPositionX(), bot->GetPositionY())) + return false; + + // Walk clockwise around the centre circle to find the nearest safe spot + float const currentAngle = std::atan2(bot->GetPositionY() - centerY, bot->GetPositionX() - centerX); + + for (int i = 1; i <= TEST_POSITIONS + 1; ++i) + { + // Last iteration tests the diametrically opposite point as a fallback + float const testAngle = (i <= TEST_POSITIONS) + ? currentAngle - ANGLE_STEP * i + : currentAngle + float(M_PI); + + float testX = centerX + std::cos(testAngle) * CIRCLE_RADIUS; + float testY = centerY + std::sin(testAngle) * CIRCLE_RADIUS; + float testZ = centerZ; + + bot->UpdateAllowedPositionZ(testX, testY, testZ); + + if (!bot->IsWithinLOS(testX, testY, testZ)) + continue; + + if (!isSafePosition(testX, testY)) + continue; + + return MoveTo(bot->GetMapId(), testX, testY, testZ, false, false, false, true, MovementPriority::MOVEMENT_FORCED, true, false); + } + + return false; +} + +bool IccLichKingNecroticPlagueAction::Execute(Event /*event*/) +{ + if (!botAI->HasAura("Necrotic Plague", bot)) + return false; + + static constexpr float DELIVER_RANGE = 3.0f; + + GuidVector const& npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); + + // Prefer shamblings tanked by the assist tank so plague is delivered + // to the correct add pile. Fall back to nearest if none qualify. + Unit* closestHorror = nullptr; + float minDist = 150.0f; + Unit* fallbackHorror = nullptr; + float fallbackDist = 150.0f; + + for (ObjectGuid const& guid : npcs) + { + Unit* unit = botAI->GetUnit(guid); + if (!unit || !unit->IsAlive()) + continue; + + if (!IsLkShambling(unit->GetEntry())) + continue; + + float const dist = bot->GetDistance(unit); + + Unit* victim = unit->GetVictim(); + if (victim && botAI->IsAssistTank(victim->ToPlayer())) + { + if (dist < minDist) + { + minDist = dist; + closestHorror = unit; + } + } + else if (dist < fallbackDist) + { + fallbackDist = dist; + fallbackHorror = unit; + } + } + + if (!closestHorror) + { + closestHorror = fallbackHorror; + minDist = fallbackDist; + } + + // No alive Horror — stop moving so healers can dispel immediately. + // Keeping the plague active with no valid target risks a raid wipe. + if (!closestHorror) + { + bot->StopMoving(); + bot->AttackStop(); + bot->SetTarget(ObjectGuid::Empty); + return true; + } + + // Already in delivery range — hold position for dispel + if (minDist <= DELIVER_RANGE) + { + bot->StopMoving(); + return true; + } + + // Move toward the Horror, suppressing combat so we don't get sidetracked + bot->AttackStop(); + bot->SetTarget(ObjectGuid::Empty); + + // During winter (boss <= 71% HP) approach from behind the shambling to + // avoid its frontal Shockwave. + Unit* boss = AI_VALUE2(Unit*, "find target", "the lich king"); + bool const duringWinter = boss && !boss->HealthAbovePct(71); + + if (duringWinter) + { + Unit* victim = closestHorror->GetVictim(); + if (victim) + { + static constexpr float BEHIND_OFFSET = 2.0f; + float const vecX = closestHorror->GetPositionX() - victim->GetPositionX(); + float const vecY = closestHorror->GetPositionY() - victim->GetPositionY(); + float const vecLen = std::hypot(vecX, vecY); + + if (vecLen > 0.1f) + { + float const dirX = vecX / vecLen; + float const dirY = vecY / vecLen; + + // Try directly behind, then slight left/right arcs + static constexpr std::array Arcs = {0.0f, 0.35f, -0.35f, 0.70f, -0.70f}; + + for (float const arc : Arcs) + { + float const cosA = std::cos(arc); + float const sinA = std::sin(arc); + float const rotX = dirX * cosA - dirY * sinA; + float const rotY = dirX * sinA + dirY * cosA; + + float const destX = closestHorror->GetPositionX() + rotX * BEHIND_OFFSET; + float const destY = closestHorror->GetPositionY() + rotY * BEHIND_OFFSET; + + if (bot->IsWithinLOS(destX, destY, closestHorror->GetPositionZ())) + { + return MoveTo(bot->GetMapId(), destX, destY, closestHorror->GetPositionZ(), + false, false, false, false, MovementPriority::MOVEMENT_FORCED, true, false); + } + } + } + } + } + + return MoveTo(bot->GetMapId(), closestHorror->GetPositionX(), closestHorror->GetPositionY(), + closestHorror->GetPositionZ(), false, false, false, false, MovementPriority::MOVEMENT_FORCED, true, false); +} + +bool IccLichKingWinterAction::Execute(Event /*event*/) +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "the lich king"); + if (!boss) + return false; + + Difficulty const diff = bot->GetRaidDifficulty(); + + if (sPlayerbotAIConfig.EnableICCBuffs && IsHeroicLk(diff)) + ApplyHeroicBuffToMember(botAI, bot, true); + + // Speed boost to help escape the inward push + if (bot->GetDistance2d(boss) < 35.0f && !bot->HasAura(SPELL_NITRO_BOOSTS)) + bot->AddAura(SPELL_NITRO_BOOSTS, bot); + + // Heroic: if more than 2 Raging Spirits are alive, kill the one with the + // highest HP. Only the main tank does this to avoid simultaneous actions. + if (botAI->IsMainTank(bot) && IsHeroicLk(diff)) + { + GuidVector const& heroicNpcs = AI_VALUE(GuidVector, "nearest hostile npcs"); + std::vector spirits; + + for (ObjectGuid const& guid : heroicNpcs) + { + Unit* unit = botAI->GetUnit(guid); + if (!unit || !unit->IsAlive()) + continue; + + if (IsLkRagingSpirit(unit->GetEntry())) + spirits.push_back(unit); + } + + if (spirits.size() > 2) + { + Unit* killTarget = *std::max_element(spirits.begin(), spirits.end(), + [](Unit const* a, Unit const* b) { return a->GetHealth() < b->GetHealth(); }); + bot->Kill(bot, killTarget); + } + } + + // Skull marking during Remorseless Winter. + // Priority: Shambling Horror > Raging Spirit (both on MT within 10 yd). + // Only re-mark when the current skull target is dead/gone. + if (Group* group = bot->GetGroup()) + { + static constexpr float MARK_RANGE = 10.0f; + + Unit* currentSkull = botAI->GetUnit(group->GetTargetIcon(7)); + bool const currentValid = currentSkull && currentSkull->IsAlive() && (IsLkShambling(currentSkull->GetEntry()) || IsLkRagingSpirit(currentSkull->GetEntry())); + + if (!currentValid) + { + Unit* mainTank = AI_VALUE(Unit*, "main tank"); + GuidVector const& winterNpcs = AI_VALUE(GuidVector, "nearest hostile npcs"); + + Unit* bestShambling = nullptr; + Unit* bestSpirit = nullptr; + + for (ObjectGuid const& guid : winterNpcs) + { + Unit* unit = botAI->GetUnit(guid); + if (!unit || !unit->IsAlive()) + continue; + + if (!mainTank || unit->GetVictim() != mainTank) + continue; + + if (mainTank->GetDistance(unit) > MARK_RANGE) + continue; + + uint32 const entry = unit->GetEntry(); + if (!bestShambling && IsLkShambling(entry)) + bestShambling = unit; + else if (!bestSpirit && IsLkRagingSpirit(entry)) + bestSpirit = unit; + + if (bestShambling) + break; + } + + Unit* markTarget = bestShambling ? bestShambling : bestSpirit; + + if (markTarget) + group->SetTargetIcon(7, bot->GetGUID(), markTarget->GetGUID()); + else if (!group->GetTargetIcon(7).IsEmpty()) + group->SetTargetIcon(7, bot->GetGUID(), ObjectGuid::Empty); + } + } + + // Teleport back onto the platform if the engine pushed us through + FixPlatformPosition(); + + // Staging during Remorseless Winter cast: non-tanks converge on ONE shared + // safe vile spirit anchor and hold for 4s before falling through to the + // final-spot logic. The chosen anchor is locked-in on first detection of + // the cast so all bots agree even if defile state shifts mid-cast. + // Among safe slots VILE_SPIRIT1 and VILE_SPIRIT3, picks + // the one closest to the group centroid; VILE_SPIRIT2 is fallback only. + struct WinterStageState + { + uint32 startMs; + Position const* pos; + }; + static std::map, WinterStageState> s_winterStage; + auto const winterKey = std::make_pair(boss->GetInstanceId(), boss->GetGUID()); + + bool const bossCastingWinter = boss->HasUnitState(UNIT_STATE_CASTING) && + (boss->FindCurrentSpellBySpellId(SPELL_REMORSELESS_WINTER1) || + boss->FindCurrentSpellBySpellId(SPELL_REMORSELESS_WINTER2) || + boss->FindCurrentSpellBySpellId(SPELL_REMORSELESS_WINTER3) || + boss->FindCurrentSpellBySpellId(SPELL_REMORSELESS_WINTER4) || + boss->FindCurrentSpellBySpellId(SPELL_REMORSELESS_WINTER5) || + boss->FindCurrentSpellBySpellId(SPELL_REMORSELESS_WINTER6) || + boss->FindCurrentSpellBySpellId(SPELL_REMORSELESS_WINTER7) || + boss->FindCurrentSpellBySpellId(SPELL_REMORSELESS_WINTER8)); + + static constexpr uint32 STAGE_DURATION_MS = 4000; + + // Enter staging block if boss is casting Winter OR a staging entry exists + // that hasn't yet expired. The cast ends before the aura applies, so we + // can't rely on bossCastingWinter staying true for the full window. + uint32 const now = getMSTime(); + auto winterIt = s_winterStage.find(winterKey); + bool const stageActive = winterIt != s_winterStage.end() && + getMSTimeDiff(winterIt->second.startMs, now) < STAGE_DURATION_MS; + + if (!botAI->IsTank(bot) && (bossCastingWinter || stageActive)) + { + if (winterIt == s_winterStage.end()) + { + Position const* candidates[2] = {&ICC_LK_VILE_SPIRIT1_POSITION, + &ICC_LK_VILE_SPIRIT3_POSITION}; + Position const centroid = ComputeGroupCentroid(bot); + + Position const* chosen = nullptr; + float bestDist = std::numeric_limits::max(); + for (Position const* slot : candidates) + { + if (!IsPositionSafeFromDefile(slot->GetPositionX(), slot->GetPositionY(), + PLATFORM_Z, 3.0f)) + continue; + + if (!IsPositionSafeFromShadowTraps(slot->GetPositionX(), slot->GetPositionY())) + continue; + + float const d = std::hypot(centroid.GetPositionX() - slot->GetPositionX(), + centroid.GetPositionY() - slot->GetPositionY()); + if (d < bestDist) + { + bestDist = d; + chosen = slot; + } + } + + if (!chosen && + IsPositionSafeFromDefile(ICC_LK_VILE_SPIRIT2_POSITION.GetPositionX(), + ICC_LK_VILE_SPIRIT2_POSITION.GetPositionY(), + PLATFORM_Z, 3.0f) && + IsPositionSafeFromShadowTraps(ICC_LK_VILE_SPIRIT2_POSITION.GetPositionX(), + ICC_LK_VILE_SPIRIT2_POSITION.GetPositionY())) + chosen = &ICC_LK_VILE_SPIRIT2_POSITION; + + winterIt = s_winterStage.emplace(winterKey, WinterStageState{now, chosen}).first; + } + + uint32 const elapsed = getMSTimeDiff(winterIt->second.startMs, now); + Position const* stagePos = winterIt->second.pos; + + if (stagePos && elapsed < STAGE_DURATION_MS) + { + static constexpr float STAGE_TOLERANCE = 3.0f; + static std::map, bool> s_stageInbound; + auto const stageKey = std::make_pair(bot->GetInstanceId(), bot->GetGUID()); + float const dist = bot->GetDistance2d(stagePos->GetPositionX(), stagePos->GetPositionY()); + if (dist > STAGE_TOLERANCE) + { + if (!s_stageInbound[stageKey]) + { + bot->AttackStop(); + bot->InterruptNonMeleeSpells(true); + bot->SetTarget(ObjectGuid::Empty); + context->GetValue("current target")->Set(nullptr); + botAI->Reset(); + s_stageInbound[stageKey] = true; + } + TryMoveToPosition(stagePos->GetPositionX(), stagePos->GetPositionY(), PLATFORM_Z, true); + } + else + { + s_stageInbound[stageKey] = false; + bot->StopMoving(); + } + return true; + } + } + else if (!bossCastingWinter && !stageActive) + { + // Only clear when no staging window is active (timer expired AND not + // re-casting). This keeps the staging entry alive across the brief + // gap between cast end and aura application, and prevents tanks from + // wiping the entry mid-staging (tanks always hit this branch). + s_winterStage.erase(winterKey); + } + + // Defile evacuation has absolute priority over everything else + if (!IsPositionSafeFromDefile(bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ(), 3.0f)) + { + Position const& safePos = botAI->IsTank(bot) + ? *GetMainTankPosition() + : *GetMainTankRangedPosition(); + + TryMoveToPosition(safePos.GetPositionX(), safePos.GetPositionY(), PLATFORM_Z, true); + return true; + } + + // If any ice sphere is targeting this bot, move to the midpoint between the + // melee and ranged frost positions + if (!botAI->IsTank(bot)) + { + GuidVector const& sphereNpcs = AI_VALUE(GuidVector, "nearest hostile npcs"); + for (ObjectGuid const& guid : sphereNpcs) + { + Unit* sphere = botAI->GetUnit(guid); + if (!sphere || !sphere->IsAlive() || !IsIceSphere(sphere->GetEntry())) + continue; + + if (sphere->GetVictim() != bot) + continue; + + Position const& meleePos = *GetMainTankPosition(); + Position const& rangedPos = *GetMainTankRangedPosition(); + float const midX = (meleePos.GetPositionX() + rangedPos.GetPositionX()) * 0.5f; + float const midY = (meleePos.GetPositionY() + rangedPos.GetPositionY()) * 0.5f; + TryMoveToPosition(midX, midY, PLATFORM_Z, true); + return false; + } + } + + ClearInvalidTarget(); + HandlePetManagement(); + + if (botAI->IsTank(bot)) + HandleTankPositioning(); + else if (!botAI->IsRanged(bot)) + { + // Non-tank melee: + // - Add (shambling/spirit) within 5y of MT: flank it. + // - No add near MT: wait at midpoint between melee and ranged + // anchors — staying close enough to engage when an add lands on + // MT, far enough not to eat shambling frontals on stragglers. + // Never pulled to MT/melee anchor. + Unit* mainTank = AI_VALUE(Unit*, "main tank"); + bool addNearTank = false; + + if (mainTank && mainTank->IsAlive()) + { + GuidVector const& npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); + for (ObjectGuid const& guid : npcs) + { + Unit* add = botAI->GetUnit(guid); + if (!add || !add->IsAlive()) + continue; + if (!IsLkShambling(add->GetEntry()) && !IsLkRagingSpirit(add->GetEntry())) + continue; + if (add->GetDistance2d(mainTank) <= 5.0f) + { + addNearTank = true; + break; + } + } + } + + if (addNearTank) + HandleMeleePositioning(); + else + { + Position const& meleePos = *GetMainTankPosition(); + Position const& rangedPos = *GetMainTankRangedPosition(); + float const midX = (meleePos.GetPositionX() + rangedPos.GetPositionX()) * 0.5f; + float const midY = (meleePos.GetPositionY() + rangedPos.GetPositionY()) * 0.5f; + + static constexpr float MID_TOLERANCE = 2.0f; + if (bot->GetDistance2d(midX, midY) > MID_TOLERANCE) + TryMoveToPosition(midX, midY, PLATFORM_Z, true); + else + bot->StopMoving(); + return false; + } + } + else + HandleRangedPositioning(); + + return false; +} + +bool IccLichKingWinterAction::FixPlatformPosition() +{ + if (std::abs(bot->GetPositionZ() - PLATFORM_Z) > 1.0f) + { + bot->TeleportTo(bot->GetMapId(), bot->GetPositionX(), bot->GetPositionY(), + PLATFORM_Z, bot->GetOrientation()); + return true; + } + + return false; +} + +bool IccLichKingWinterAction::ClearInvalidTarget() +{ + Unit* currentTarget = AI_VALUE(Unit*, "current target"); + if (!currentTarget) + return false; + + Unit* boss = AI_VALUE2(Unit*, "find target", "the lich king"); + bool doClear = false; + + // Bots must not fight the boss during Remorseless Winter + if (boss && currentTarget == boss) + doClear = true; + + // Tanks must not chase ice spheres + if (botAI->IsTank(bot) && IsIceSphere(currentTarget->GetEntry())) + doClear = true; + + if (doClear) + { + context->GetValue("current target")->Set(nullptr); + bot->AttackStop(); + bot->SetTarget(ObjectGuid::Empty); + return true; + } + + return false; +} + +Position const* IccLichKingWinterAction::GetMainTankPosition() +{ + // Use group centroid so the chosen frost slot follows the majority of the + // raid, not the tanks. Non-tanks can't survive Remorseless Winter ticks + // while traversing the platform, so dragging them to a far slot just + // because tanks parked there gets them killed. Tanks tolerate a few + // ticks and will reroute to the majority slot too. + Position ref = ComputeGroupCentroid(bot); + + return &SelectClosestOf3(ref, ICC_LK_FROST1_POSITION, ICC_LK_FROST2_POSITION, ICC_LK_FROST3_POSITION); +} + +Position const* IccLichKingWinterAction::GetMainTankRangedPosition() +{ + Position const* melee = GetMainTankPosition(); + + if (melee == &ICC_LK_FROST1_POSITION) + return &ICC_LK_FROSTR1_POSITION; + if (melee == &ICC_LK_FROST2_POSITION) + return &ICC_LK_FROSTR2_POSITION; + + return &ICC_LK_FROSTR3_POSITION; +} + +bool IccLichKingWinterAction::IsPositionSafeFromDefile(float x, float y, float /*z*/, float minSafeDistance) const +{ + static constexpr float BASE_RADIUS = 6.0f; + static constexpr float SAFETY_MARGIN = 3.0f; + + GuidVector const& npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); + + for (ObjectGuid const& guid : npcs) + { + Unit* unit = botAI->GetUnit(guid); + if (!unit || !unit->IsAlive() || unit->GetEntry() != DEFILE_NPC_ID) + continue; + + float currentRadius = BASE_RADIUS; + for (uint32 const auraId : DEFILE_AURAS) + { + Aura* grow = unit->GetAura(auraId); + if (!grow) + continue; + + float const growthMultiplier = + (bot->GetRaidDifficulty() == RAID_DIFFICULTY_10MAN_HEROIC || + bot->GetRaidDifficulty() == RAID_DIFFICULTY_10MAN_NORMAL) + ? 1.4f + : 0.95f; + + currentRadius = BASE_RADIUS + grow->GetStackAmount() * growthMultiplier; + break; + } + + float const dx = x - unit->GetPositionX(); + float const dy = y - unit->GetPositionY(); + + if (std::sqrt(dx * dx + dy * dy) < currentRadius + SAFETY_MARGIN + minSafeDistance) + return false; + } + + return true; +} + +bool IccLichKingWinterAction::IsPositionSafeFromShadowTraps(float x, float y) const +{ + // Shadow trap has a fixed 12 yd lethal radius (matches the SAFE_DISTANCE + // used by IccLichKingShadowTrapAction). + static constexpr float TRAP_SAFE_RADIUS = 12.0f; + + GuidVector const& npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); + + for (ObjectGuid const& guid : npcs) + { + Unit* unit = botAI->GetUnit(guid); + if (!unit || !unit->IsAlive() || unit->GetEntry() != NPC_SHADOW_TRAP) + continue; + + float const dx = x - unit->GetPositionX(); + float const dy = y - unit->GetPositionY(); + + if (std::sqrt(dx * dx + dy * dy) < TRAP_SAFE_RADIUS) + return false; + } + + return true; +} + +bool IccLichKingWinterAction::TryMoveToPosition(float targetX, float targetY, float targetZ, bool forced) +{ + float const dx = targetX - bot->GetPositionX(); + float const dy = targetY - bot->GetPositionY(); + float const dist = std::hypot(dx, dy); + + if (dist < 0.1f) + return true; + + MovementPriority const priority = forced + ? MovementPriority::MOVEMENT_FORCED + : MovementPriority::MOVEMENT_COMBAT; + + if (IsPositionSafeFromDefile(targetX, targetY, targetZ, 3.0f) && + IsPositionSafeFromShadowTraps(targetX, targetY) && + bot->IsWithinLOS(targetX, targetY, targetZ)) + { + // Sample the straight-line path for defile and shadow trap crossings + bool pathSafe = true; + static constexpr float PATH_CHECK_INTERVAL = 3.0f; + int const numChecks = std::max(1, static_cast(dist / PATH_CHECK_INTERVAL)); + + for (int i = 1; i < numChecks; ++i) + { + float const t = static_cast(i) / static_cast(numChecks); + float const checkX = bot->GetPositionX() + dx * t; + float const checkY = bot->GetPositionY() + dy * t; + + if (!IsPositionSafeFromDefile(checkX, checkY, targetZ, 2.0f) || + !IsPositionSafeFromShadowTraps(checkX, checkY)) + { + pathSafe = false; + break; + } + } + + if (pathSafe) + { + MoveTo(bot->GetMapId(), targetX, targetY, targetZ, false, false, false, true, priority, true, false); + return true; + } + // Path crosses defile or shadow trap — fall through to arc-stepping logic + } + + // Arc-stepping: rotate around the movement direction to find a way around defile/traps + static constexpr int STEPS = 8; + static constexpr float STEP_DIST = 8.0f; + + float const dirX = dx / dist; + float const dirY = dy / dist; + + for (int i = 1; i <= STEPS; ++i) + { + for (int sign : {1, -1}) + { + float const arc = float(M_PI) / STEPS * i * sign; + float const cosA = std::cos(arc); + float const sinA = std::sin(arc); + + float const testX = bot->GetPositionX() + (dirX * cosA - dirY * sinA) * STEP_DIST; + float const testY = bot->GetPositionY() + (dirX * sinA + dirY * cosA) * STEP_DIST; + + if (!IsPositionSafeFromDefile(testX, testY, targetZ, 3.0f)) + continue; + if (!IsPositionSafeFromShadowTraps(testX, testY)) + continue; + if (!bot->IsWithinLOS(testX, testY, targetZ)) + continue; + + MoveTo(bot->GetMapId(), testX, testY, targetZ, false, false, false, true, priority, true, false); + return false; + } + } + + // Probe along the direct path, shortening the distance until safe + static constexpr float PROBE_STEP = 5.0f; + + for (float probeLen = dist - PROBE_STEP; probeLen >= PROBE_STEP; probeLen -= PROBE_STEP) + { + float probeX = bot->GetPositionX() + dirX * probeLen; + float probeY = bot->GetPositionY() + dirY * probeLen; + float probeZ = targetZ; + bot->UpdateAllowedPositionZ(probeX, probeY, probeZ); + + if (!IsPositionSafeFromDefile(probeX, probeY, probeZ, 3.0f)) + continue; + if (!IsPositionSafeFromShadowTraps(probeX, probeY)) + continue; + if (!bot->IsWithinLOS(probeX, probeY, probeZ)) + continue; + + MoveTo(bot->GetMapId(), probeX, probeY, probeZ, + false, false, false, true, priority, true, false); + return false; + } + + // Small nudge forward + { + float nudgeX = bot->GetPositionX() + dirX * 2.0f; + float nudgeY = bot->GetPositionY() + dirY * 2.0f; + float nudgeZ = targetZ; + bot->UpdateAllowedPositionZ(nudgeX, nudgeY, nudgeZ); + + if (bot->IsWithinLOS(nudgeX, nudgeY, nudgeZ)) + { + MoveTo(bot->GetMapId(), nudgeX, nudgeY, nudgeZ, false, false, false, true, MovementPriority::MOVEMENT_FORCED, true, false); + return false; + } + } + + // Last resort: move away from the nearest defile to get to safety first + { + GuidVector const& npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); + Unit* nearestDefile = nullptr; + float nearestDefileDist = FLT_MAX; + + for (ObjectGuid const& guid : npcs) + { + Unit* unit = botAI->GetUnit(guid); + if (!unit || !unit->IsAlive() || unit->GetEntry() != DEFILE_NPC_ID) + continue; + + float const d = bot->GetDistance2d(unit); + if (d < nearestDefileDist) + { + nearestDefileDist = d; + nearestDefile = unit; + } + } + + if (nearestDefile) + { + float const awayX = bot->GetPositionX() - nearestDefile->GetPositionX(); + float const awayY = bot->GetPositionY() - nearestDefile->GetPositionY(); + float const awayLen = std::hypot(awayX, awayY); + + if (awayLen > 0.1f) + { + float const escapeX = bot->GetPositionX() + (awayX / awayLen) * 5.0f; + float const escapeY = bot->GetPositionY() + (awayY / awayLen) * 5.0f; + + MoveTo(bot->GetMapId(), escapeX, escapeY, targetZ, + false, false, false, true, + MovementPriority::MOVEMENT_FORCED, true, false); + return false; + } + } + } + + // No defile nearby — safe to move directly to the target. + + if (bot->IsWithinLOS(targetX, targetY, targetZ)) + { + botAI->Reset(); + MoveTo(bot->GetMapId(), targetX, targetY, targetZ, false, false, false, true, MovementPriority::MOVEMENT_FORCED, true, false); + } + + return false; +} + +bool IccLichKingWinterAction::IsValidCollectibleAdd(Unit* unit) const +{ + return IsLkCollectibleAdd(unit); +} + +bool IccLichKingWinterAction::HandleTankPositioning() +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "the lich king"); + if (!boss) + return false; + + // Both tanks converge on the same frost position + Position const& frostPos = *GetMainTankPosition(); + static constexpr float FROST_AT_POS_TOLERANCE = 3.0f; + + if (botAI->IsMainTank(bot)) + { + float const dist = bot->GetDistance2d(frostPos.GetPositionX(), frostPos.GetPositionY()); + static std::map, bool> s_mtInbound; + auto const mtKey = std::make_pair(bot->GetInstanceId(), bot->GetGUID()); + + if (dist > FROST_AT_POS_TOLERANCE) + { + if (!s_mtInbound[mtKey]) + { + bot->AttackStop(); + bot->InterruptNonMeleeSpells(true); + bot->SetTarget(ObjectGuid::Empty); + context->GetValue("current target")->Set(nullptr); + botAI->Reset(); + s_mtInbound[mtKey] = true; + } + TryMoveToPosition(frostPos.GetPositionX(), frostPos.GetPositionY(), PLATFORM_Z, true); + return false; + } + + s_mtInbound[mtKey] = false; + return HandleMainTankAddManagement(boss, &frostPos); + } + + if (botAI->IsAssistTank(bot)) + return HandleAssistTankAddManagement(boss, &frostPos); + + return false; +} + +bool IccLichKingWinterAction::HandleMeleePositioning() +{ + // Frost engagement zone: bots within MELEE_FLANK_RADIUS of the frost anchor + // are considered "engaged" and trusted to flank. Outside, they're inbound + // and may need to wait at the ranged anchor for MT to gain control. + static constexpr float MELEE_FLANK_RADIUS = 10.0f; + Position const& tankPos = *GetMainTankPosition(); + float const distToPos = bot->GetDistance2d(tankPos.GetPositionX(), tankPos.GetPositionY()); + + // Transition guard: only retreat to ranged frost position if the bot is + // still inbound (outside the engagement zone) AND a nearby Shambling is + // not yet under MT control. Once engaged, trust flank logic — no retreats. + if (distToPos > MELEE_FLANK_RADIUS) + { + Unit* mainTank = AI_VALUE(Unit*, "main tank"); + if (mainTank && mainTank->IsAlive()) + { + static constexpr float NEARBY_SHAMBLING_RANGE = 10.0f; + GuidVector const& transNpcs = AI_VALUE(GuidVector, "nearest hostile npcs"); + bool nearbyShamblingLoose = false; + + for (ObjectGuid const& guid : transNpcs) + { + Unit* add = botAI->GetUnit(guid); + if (!add || !add->IsAlive()) + continue; + + if (!IsLkShambling(add->GetEntry())) + continue; + + if (bot->GetExactDist2d(add) > NEARBY_SHAMBLING_RANGE) + continue; + + Unit* victim = add->GetVictim(); + if (!victim || victim->GetGUID() != mainTank->GetGUID()) + { + nearbyShamblingLoose = true; + break; + } + } + + if (nearbyShamblingLoose) + { + Position const& rangedPos = *GetMainTankRangedPosition(); + TryMoveToPosition(rangedPos.GetPositionX(), rangedPos.GetPositionY(), PLATFORM_Z, true); + return false; + } + } + } + + // No re-anchor to tank position: flanking owns positioning. Pulling melee + // toward MT during winter puts bots in front of shamblings (frontal cone). + + // Only adds within MT_ADD_RANGE of the main tank are valid melee targets. + // Adds further out are not under tank control — engaging them puts bots + // in front of unrooted shamblings. + static constexpr float MT_ADD_RANGE = 5.0f; + Unit* mainTank = AI_VALUE(Unit*, "main tank"); + + auto addNearMT = [&](Unit* add) -> bool + { + return mainTank && mainTank->IsAlive() && add && + add->GetDistance2d(mainTank) <= MT_ADD_RANGE; + }; + + // Acquire a valid add target + Unit* currentTarget = AI_VALUE(Unit*, "current target"); + if (!currentTarget || !currentTarget->IsAlive() || + !IsLkCollectibleAdd(currentTarget) || IsIceSphere(currentTarget->GetEntry()) || + !addNearMT(currentTarget)) + { + Unit* newTarget = nullptr; + + // Priority: skull-marked target (only if near MT) + if (Group* group = bot->GetGroup()) + { + Unit* skull = botAI->GetUnit(group->GetTargetIcon(7)); + if (skull && skull->IsAlive() && IsLkCollectibleAdd(skull) && addNearMT(skull)) + newTarget = skull; + } + + // Fallback: nearest valid add within MT range + if (!newTarget) + { + GuidVector const& npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); + float closestDist = FLT_MAX; + + for (ObjectGuid const& guid : npcs) + { + Unit* add = botAI->GetUnit(guid); + if (!IsLkCollectibleAdd(add)) + continue; + if (!addNearMT(add)) + continue; + + float const d = bot->GetDistance(add); + if (d < closestDist) + { + closestDist = d; + newTarget = add; + } + } + } + + if (!newTarget) + return false; + + context->GetValue("current target")->Set(newTarget); + bot->SetTarget(newTarget->GetGUID()); + currentTarget = newTarget; + } + + // Settle band: if a candidate flank slot is safe AND the bot is already + // within FLANK_SETTLE_DIST of it, stay put and attack — no per-tick step + // (prevents back-and-forth jitter near the slot). + static constexpr float FLANK_SETTLE_DIST = 2.0f; + + // Position behind the current melee target using mainTank→add vector + if (mainTank && mainTank->IsAlive()) + { + float const vecX = currentTarget->GetPositionX() - mainTank->GetPositionX(); + float const vecY = currentTarget->GetPositionY() - mainTank->GetPositionY(); + float const vecLen = std::hypot(vecX, vecY); + + if (vecLen > 0.1f) + { + float const dirX = vecX / vecLen; + float const dirY = vecY / vecLen; + + static constexpr std::array Arcs = {0.0f, 0.35f, -0.35f, 0.70f, -0.70f}; + + for (float const arc : Arcs) + { + float const cosA = std::cos(arc); + float const sinA = std::sin(arc); + float const rotX = dirX * cosA - dirY * sinA; + float const rotY = dirX * sinA + dirY * cosA; + + float const destX = currentTarget->GetPositionX() + rotX * BEHIND_DISTANCE; + float const destY = currentTarget->GetPositionY() + rotY * BEHIND_DISTANCE; + + float const bDx = destX - bot->GetPositionX(); + float const bDy = destY - bot->GetPositionY(); + float const bDist = std::hypot(bDx, bDy); + + if (!IsPositionSafeFromDefile(destX, destY, bot->GetPositionZ(), 2.0f)) + continue; + if (!bot->IsWithinLOS(destX, destY, bot->GetPositionZ())) + continue; + + if (bDist < FLANK_SETTLE_DIST) + { + bot->SetFacingToObject(currentTarget); + Attack(currentTarget); + return false; + } + + float const step = std::min(1.25f, bDist); + TryMoveToPosition(bot->GetPositionX() + (bDx / bDist) * step, bot->GetPositionY() + (bDy / bDist) * step, bot->GetPositionZ(), true); + return false; + } + } + } + + // Fallback: stored orientation-based angles + static constexpr std::array BehindAngles = + { + float(M_PI), // 180° directly behind + float(M_PI) * 0.75f, // 135° left-rear flank + float(M_PI) * 1.25f, // 225° right-rear flank + float(M_PI) * 0.5f, // 90° left flank + float(M_PI) * 1.5f, // 270° right flank + float(M_PI) * 0.875f, // 157° closer left-rear + }; + + for (float const angleOffset : BehindAngles) + { + float const angle = currentTarget->GetOrientation() + angleOffset; + float const destX = currentTarget->GetPositionX() + std::cos(angle) * BEHIND_DISTANCE; + float const destY = currentTarget->GetPositionY() + std::sin(angle) * BEHIND_DISTANCE; + + float const bDx = destX - bot->GetPositionX(); + float const bDy = destY - bot->GetPositionY(); + float const bDist = std::hypot(bDx, bDy); + + if (!IsPositionSafeFromDefile(destX, destY, bot->GetPositionZ(), 2.0f)) + continue; + if (!bot->IsWithinLOS(destX, destY, bot->GetPositionZ())) + continue; + + if (bDist < FLANK_SETTLE_DIST) + { + bot->SetFacingToObject(currentTarget); + Attack(currentTarget); + return false; + } + + float const step = std::min(1.25f, bDist); + TryMoveToPosition(bot->GetPositionX() + (bDx / bDist) * step, + bot->GetPositionY() + (bDy / bDist) * step, + bot->GetPositionZ(), true); + return false; + } + + bot->SetFacingToObject(currentTarget); + Attack(currentTarget); + return false; +} + +bool IccLichKingWinterAction::HandleRangedPositioning() +{ + Position const& targetPos = *GetMainTankRangedPosition(); + + // Evacuate defile first + if (!IsPositionSafeFromDefile(bot->GetPositionX(), bot->GetPositionY(), + bot->GetPositionZ(), 3.0f)) + { + TryMoveToPosition(targetPos.GetPositionX(), targetPos.GetPositionY(), PLATFORM_Z, true); + return false; + } + + // Move to ranged frost position. Clear target + reset only on the FIRST + // tick of the inbound phase — otherwise we cancel the bot's own movement + // every tick and it ends up walking 1y per cycle. + static std::map, bool> s_rangedInbound; + auto const rangedKey = std::make_pair(bot->GetInstanceId(), bot->GetGUID()); + bool const farFromAnchor = + bot->GetDistance2d(targetPos.GetPositionX(), targetPos.GetPositionY()) > 2.0f; + + if (farFromAnchor) + { + bool const wasInbound = s_rangedInbound[rangedKey]; + if (!wasInbound) + { + bot->AttackStop(); + bot->InterruptNonMeleeSpells(true); + bot->SetTarget(ObjectGuid::Empty); + context->GetValue("current target")->Set(nullptr); + botAI->Reset(); + s_rangedInbound[rangedKey] = true; + } + TryMoveToPosition(targetPos.GetPositionX(), targetPos.GetPositionY(), PLATFORM_Z, true); + // Skip target acquisition while inbound — Attack() would cancel movement. + return false; + } + + s_rangedInbound[rangedKey] = false; + + if (!botAI->IsRangedDps(bot)) + return false; + + static constexpr float SPHERE_RANGE = 30.0f; + + // Keep attacking current sphere if still alive and in range + Unit* cur = bot->GetVictim(); + if (cur && cur->IsAlive() && IsIceSphere(cur->GetEntry()) && + bot->GetDistance(cur) <= SPHERE_RANGE) + { + bot->SetFacingToObject(cur); + Attack(cur); + return false; + } + + // Drop stale sphere target + if (cur && IsIceSphere(cur->GetEntry())) + { + bot->AttackStop(); + bot->SetTarget(ObjectGuid::Empty); + } + + // Find the nearest sphere within range + GuidVector const& npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); + Unit* nearestSphere = nullptr; + float nearestDist = SPHERE_RANGE; + + for (ObjectGuid const& guid : npcs) + { + Unit* unit = botAI->GetUnit(guid); + if (!unit || !unit->IsAlive() || !IsIceSphere(unit->GetEntry())) + continue; + + float const d = bot->GetDistance(unit); + if (d < nearestDist) + { + nearestDist = d; + nearestSphere = unit; + } + } + + if (nearestSphere) + { + bot->SetTarget(nearestSphere->GetGUID()); + bot->SetFacingToObject(nearestSphere); + Attack(nearestSphere); + return true; + } + + // No spheres — engage skull-marked add or nearest valid add + Unit* addTarget = nullptr; + if (Group* group = bot->GetGroup()) + { + Unit* skull = botAI->GetUnit(group->GetTargetIcon(7)); + if (skull && skull->IsAlive() && IsLkCollectibleAdd(skull)) + addTarget = skull; + } + + if (!addTarget) + { + float closestDist = SPHERE_RANGE; + for (ObjectGuid const& guid : npcs) + { + Unit* add = botAI->GetUnit(guid); + if (!IsLkCollectibleAdd(add)) + continue; + + float const d = bot->GetDistance(add); + if (d < closestDist) + { + closestDist = d; + addTarget = add; + } + } + } + + if (addTarget) + { + context->GetValue("current target")->Set(addTarget); + bot->SetTarget(addTarget->GetGUID()); + bot->SetFacingToObject(addTarget); + Attack(addTarget); + return true; + } + + return false; +} + +bool IccLichKingWinterAction::HandleMainTankAddManagement(Unit* boss, Position const* frostPos) +{ + static constexpr float ENGAGE_RADIUS = 12.0f; + static constexpr float TAUNT_RADIUS = 30.0f; + static constexpr int AOE_TAUNT_MIN = 2; + + bool hasAliveAssistTank = false; + if (Group* group = bot->GetGroup()) + { + for (GroupReference* itr = group->GetFirstMember(); itr; itr = itr->next()) + { + Player* member = itr->GetSource(); + if (member && member->IsAlive() && member != bot && botAI->IsAssistTank(member)) + { + hasAliveAssistTank = true; + break; + } + } + } + + GuidVector const& targets = AI_VALUE(GuidVector, "possible targets"); + + Unit* priorityAdd = nullptr; // attacking non-tank + Unit* secondaryAdd = nullptr; // not yet on MT + Unit* fallbackAdd = nullptr; // already on MT + int nearbyCount = 0; + + for (ObjectGuid const& guid : targets) + { + Unit* add = botAI->GetUnit(guid); + if (!IsLkCollectibleAdd(add)) + continue; + + float const addDist = bot->GetDistance(add); + float const maxEngage = hasAliveAssistTank ? ENGAGE_RADIUS : 25.0f; + + if (addDist <= TAUNT_RADIUS) + ++nearbyCount; + + Unit* victim = add->GetVictim(); + + // Taunt pass: all adds in range that are NOT already on MT + bool const onMT = victim && victim->IsPlayer() && botAI->IsMainTank(victim->ToPlayer()); + if (!onMT && addDist <= TAUNT_RADIUS) + CastSingleTargetTaunt(botAI, bot, add); + + // Priority taunt: adds on the assist tank within 10 yd + bool const onAT = victim && victim->IsPlayer() && botAI->IsAssistTank(victim->ToPlayer()); + if (onAT && addDist <= 10.0f) + CastSingleTargetTaunt(botAI, bot, add); + + if (nearbyCount >= AOE_TAUNT_MIN) + CastAoeTaunt(botAI, bot); + + if (addDist > maxEngage) + continue; + + if (victim && victim->IsPlayer() && !botAI->IsTank(victim->ToPlayer())) + { + if (!priorityAdd || addDist < bot->GetDistance(priorityAdd)) + priorityAdd = add; + } + else if (victim != bot) + { + if (!secondaryAdd || addDist < bot->GetDistance(secondaryAdd)) + secondaryAdd = add; + } + else + { + if (!fallbackAdd || addDist < bot->GetDistance(fallbackAdd)) + fallbackAdd = add; + } + } + + Unit* targetAdd = priorityAdd ? priorityAdd : secondaryAdd ? secondaryAdd : fallbackAdd; + + if (!targetAdd) + { + Unit* cur = bot->GetVictim(); + if (cur && !IsLkCollectibleAdd(cur)) + bot->SetTarget(ObjectGuid::Empty); + return false; + } + + // Stack-consolidation and orientation nudge. + // Move away from the ranged position when either: + // (a) any two Shamblings/Spirits on the MT are more than 1 yd apart, or + // (b) any add is facing toward the ranged position. + { + static constexpr float STACK_THRESHOLD = 1.0f; + static constexpr float NUDGE_DIST = 2.0f; + + Position const& rangedPos = *GetMainTankRangedPosition(); + float const awayDx = frostPos->GetPositionX() - rangedPos.GetPositionX(); + float const awayDy = frostPos->GetPositionY() - rangedPos.GetPositionY(); + float const awayLen = std::hypot(awayDx, awayDy); + + if (awayLen > 0.01f) + { + float const awayNx = awayDx / awayLen; + float const awayNy = awayDy / awayLen; + + std::vector stackAdds; + for (ObjectGuid const& guid : targets) + { + Unit* add = botAI->GetUnit(guid); + if (!add || !add->IsAlive()) + continue; + + uint32 const entry = add->GetEntry(); + if (!IsLkShambling(entry) && !IsLkRagingSpirit(entry)) + continue; + + Unit* v = add->GetVictim(); + if (!v || !v->IsPlayer() || !botAI->IsMainTank(v->ToPlayer())) + continue; + + stackAdds.push_back(add); + } + + bool needNudge = false; + + // (a) spread check + for (size_t i = 0; !needNudge && i < stackAdds.size(); ++i) + { + for (size_t j = i + 1; !needNudge && j < stackAdds.size(); ++j) + { + if (stackAdds[i]->GetDistance2d(stackAdds[j]) > STACK_THRESHOLD) + needNudge = true; + } + } + + // (b) orientation check — add facing toward rangedPos + for (size_t i = 0; !needNudge && i < stackAdds.size(); ++i) + { + Unit* add = stackAdds[i]; + float const toRangedX = rangedPos.GetPositionX() - add->GetPositionX(); + float const toRangedY = rangedPos.GetPositionY() - add->GetPositionY(); + float const dot = std::cos(add->GetOrientation()) * toRangedX + + std::sin(add->GetOrientation()) * toRangedY; + if (dot > 0.0f) + needNudge = true; + } + + if (needNudge) + { + float const nudgeX = bot->GetPositionX() + awayNx * NUDGE_DIST; + float const nudgeY = bot->GetPositionY() + awayNy * NUDGE_DIST; + TryMoveToPosition(nudgeX, nudgeY, PLATFORM_Z, false); + } + } + } + + float const addDist = bot->GetDistance(targetAdd); + + if (addDist <= ENGAGE_RADIUS || !hasAliveAssistTank) + { + // Pull toward frostPos if solo-tanking and add is far + if (addDist > ENGAGE_RADIUS && !hasAliveAssistTank) + { + float const pullDx = targetAdd->GetPositionX() - frostPos->GetPositionX(); + float const pullDy = targetAdd->GetPositionY() - frostPos->GetPositionY(); + float const pullLen = std::hypot(pullDx, pullDy); + float const pullRatio = std::min(1.0f, 15.0f / (pullLen > 0.1f ? pullLen : 0.1f)); + TryMoveToPosition(frostPos->GetPositionX() + pullDx * pullRatio, + frostPos->GetPositionY() + pullDy * pullRatio, + PLATFORM_Z, false); + } + + bot->SetTarget(targetAdd->GetGUID()); + bot->SetFacingToObject(targetAdd); + Attack(targetAdd); + } + else + { + // Add is still being herded by assist tank + bot->SetTarget(targetAdd->GetGUID()); + bot->SetFacingToObject(targetAdd); + Attack(targetAdd); + } + + return false; +} + +bool IccLichKingWinterAction::HandleAssistTankAddManagement(Unit* boss, Position const* frostPos) +{ + static constexpr float FROST_TOL = 3.0f; + static constexpr float MELE_RANGE = 5.0f; + static constexpr float TAUNT_RADIUS = 30.0f; + + GuidVector const& targets = AI_VALUE(GuidVector, "possible targets"); + + Unit* mainTank = AI_VALUE(Unit*, "main tank"); + + std::vector addsOnUs; + std::vector addsLoose; + + for (ObjectGuid const& guid : targets) + { + Unit* add = botAI->GetUnit(guid); + if (!IsLkCollectibleAdd(add)) + continue; + + Unit* victim = add->GetVictim(); + + // Skip adds already securely held by the main tank + if (mainTank && victim && victim->GetGUID() == mainTank->GetGUID()) + continue; + + if (victim == bot) + addsOnUs.push_back(add); + else + addsLoose.push_back(add); + } + + // adds are on us — walk back to frost position + if (!addsOnUs.empty()) + { + for (Unit* add : addsOnUs) + CastSingleTargetTaunt(botAI, bot, add); + + if (addsOnUs.size() >= 2) + CastAoeTaunt(botAI, bot); + + float const distToFrost = bot->GetExactDist2d(frostPos->GetPositionX(), + frostPos->GetPositionY()); + if (distToFrost > FROST_TOL) + TryMoveToPosition(frostPos->GetPositionX(), frostPos->GetPositionY(), PLATFORM_Z, true); + + // Maintain combat target on the way back + Unit* currentTarget = bot->GetVictim(); + bool keepCurrent = false; + + if (currentTarget && currentTarget->IsAlive()) + { + for (Unit* add : addsOnUs) + { + if (add->GetGUID() == currentTarget->GetGUID()) + { + keepCurrent = true; + break; + } + } + } + + if (!keepCurrent) + { + currentTarget = nullptr; + + // Prefer shambling as combat target + for (Unit* add : addsOnUs) + { + if (IsLkShambling(add->GetEntry())) + { + currentTarget = add; + break; + } + } + + if (!currentTarget) + currentTarget = addsOnUs.front(); + } + + if (currentTarget) + { + bot->SetTarget(currentTarget->GetGUID()); + bot->SetFacingToObject(currentTarget); + Attack(currentTarget); + } + + return false; + } + + // return to frost position if we drifted + float const distToFrost = bot->GetExactDist2d(frostPos->GetPositionX(), + frostPos->GetPositionY()); + if (distToFrost > FROST_TOL) + { + botAI->Reset(); + TryMoveToPosition(frostPos->GetPositionX(), frostPos->GetPositionY(), PLATFORM_Z, true); + + Unit* cur = bot->GetVictim(); + if (cur && !IsLkCollectibleAdd(cur)) + bot->SetTarget(ObjectGuid::Empty); + return false; + } + + // at frost position, collect the nearest loose add + Unit* targetAdd = nullptr; + float closestDist = FLT_MAX; + + // Priority 1: rescue add attacking a non-tank + for (Unit* add : addsLoose) + { + Unit* victim = add->GetVictim(); + if (victim && victim->IsPlayer() && !botAI->IsTank(victim->ToPlayer())) + { + float const dist = bot->GetDistance(add); + if (dist < closestDist) + { + closestDist = dist; + targetAdd = add; + } + } + } + + // Priority 2: any loose add + if (!targetAdd) + { + for (Unit* add : addsLoose) + { + float const dist = bot->GetDistance(add); + if (dist < closestDist) + { + closestDist = dist; + targetAdd = add; + } + } + } + + if (!targetAdd) + { + Unit* cur = bot->GetVictim(); + if (cur && !IsLkCollectibleAdd(cur)) + bot->SetTarget(ObjectGuid::Empty); + + // No loose adds — idle at frost pos but allow lower-priority actions to run + return false; + } + + CastSingleTargetTaunt(botAI, bot, targetAdd); + + // Also taunt other loose adds in range + for (Unit* add : addsLoose) + { + if (add == targetAdd) + continue; + if (bot->GetExactDist2d(add) <= TAUNT_RADIUS) + CastSingleTargetTaunt(botAI, bot, add); + } + + // AoE taunt if 2+ loose adds are close enough + { + int nearbyLoose = 0; + for (Unit* add : addsLoose) + { + if (bot->GetExactDist2d(add) <= TAUNT_RADIUS) + ++nearbyLoose; + } + if (nearbyLoose >= 2) + CastAoeTaunt(botAI, bot); + } + + bot->SetTarget(targetAdd->GetGUID()); + + if (closestDist > MELE_RANGE) + { + botAI->Reset(); + TryMoveToPosition(targetAdd->GetPositionX(), targetAdd->GetPositionY(), PLATFORM_Z, false); + } + else + { + bot->SetFacingToObject(targetAdd); + Attack(targetAdd); + } + + return false; +} + +bool IccLichKingWinterAction::HandlePetManagement() +{ + Pet* pet = bot->GetPet(); + if (!pet || !pet->IsAlive()) + return false; + + CharmInfo* ci = pet->GetCharmInfo(); + if (!ci) + return false; + + if (botAI->IsHeal(bot)) + { + if (ci->GetCommandState() != COMMAND_FOLLOW) + { + pet->AttackStop(); + pet->InterruptNonMeleeSpells(false); + pet->GetMotionMaster()->MoveFollow(bot, PET_FOLLOW_DIST, pet->GetFollowAngle()); + ci->SetCommandState(COMMAND_FOLLOW); + ci->SetIsCommandAttack(false); + ci->SetIsAtStay(false); + ci->SetIsReturning(true); + ci->SetIsCommandFollow(true); + ci->SetIsFollowing(false); + ci->RemoveStayPosition(); + } + return false; + } + + GuidVector const& npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); + + Unit* bestTarget = nullptr; + int bestPriority = -1; + float bestHpPct = 101.0f; + + for (ObjectGuid const& guid : npcs) + { + Unit* unit = botAI->GetUnit(guid); + if (!unit || !unit->IsAlive()) + continue; + + uint32 const entry = unit->GetEntry(); + int priority = -1; + + if (IsLkShambling(entry)) + priority = 3; + else if (IsLkRagingSpirit(entry)) + priority = 2; + else if (IsIceSphere(entry)) + priority = 1; + + if (priority < 0) + continue; + + float const hp = unit->GetHealthPct(); + if (priority > bestPriority || (priority == bestPriority && hp < bestHpPct)) + { + bestPriority = priority; + bestHpPct = hp; + bestTarget = unit; + } + } + + if (!bestTarget) + return false; + + // Skip if the pet is already attacking the right unit + if (pet->GetVictim() == bestTarget) + return false; + + ci->SetIsCommandAttack(true); + ci->SetIsAtStay(false); + ci->SetIsReturning(false); + ci->SetIsCommandFollow(false); + ci->SetIsFollowing(false); + ci->SetCommandState(COMMAND_ATTACK); + + if (pet->IsAIEnabled) + { + pet->AI()->AttackStart(bestTarget); + pet->GetMotionMaster()->MoveChase(bestTarget); + } + + return true; +} + +bool IccLichKingAddsAction::Execute(Event /*event*/) +{ + // Being carried by a Val'kyr — no actions possible + if (bot->HasAura(SPELL_HARVEST_SOUL_VALKYR)) + return false; + + Unit* boss = AI_VALUE2(Unit*, "find target", "the lich king"); + Difficulty const diff = bot->GetRaidDifficulty(); + bool const hasPlague = botAI->HasAura("Necrotic Plague", bot); + Unit* const terenas = bot->FindNearestCreature(NPC_TERENAS_MENETHIL_HC, 55.0f); + + // Heroic cheat buffs — apply to all group members + if (sPlayerbotAIConfig.EnableICCBuffs && IsHeroicLk(diff)) + { + Group* buffGroup = bot->GetGroup(); + if (buffGroup) + { + for (GroupReference* itr = buffGroup->GetFirstMember(); itr; itr = itr->next()) + { + Player* member = itr->GetSource(); + if (!member || !member->IsAlive() || !member->IsInWorld()) + continue; + + ApplyHeroicBuffToMember(botAI, member, true); + + if (boss && boss->HealthBelowPct(60) && boss->HealthAbovePct(40) && + !member->HasAura(SPELL_EMPOWERED_BLOOD)) + member->AddAura(SPELL_EMPOWERED_BLOOD, member); + } + } + } + + // Skull marking: phase 1 marks the boss; phase 2+ prioritises Raging Spirits. + // Val'kyr marking owns skull while any Val'kyr is actively grabbing — skip here. + if (Group* group = bot->GetGroup()) + { + bool anyValkyrGrabbing = false; + { + GuidVector const& nearbyNpcs = AI_VALUE(GuidVector, "nearest hostile npcs"); + for (ObjectGuid const& guid : nearbyNpcs) + { + Unit* unit = botAI->GetUnit(guid); + if (unit && unit->IsAlive() && IsLkValkyr(unit) && + unit->HasAura(SPELL_HARVEST_SOUL_VALKYR) && + (!IsHeroicLk(diff) || unit->HealthAbovePct(49))) + { + anyValkyrGrabbing = true; + break; + } + } + } + + if (!anyValkyrGrabbing) + { + // Phase 3 (boss < 40%, non-winter): skull priority is Raging Spirit > Vile Spirit > boss. + // During Vile Spirit windows, melee can't reach the spirits — mark boss on cross + // and route melee DPS there. + bool phase3 = false; + if (boss && boss->HealthBelowPct(40) && !HasAnyRemorselessWinter(boss)) + { + phase3 = true; + + Unit* currentSkull = botAI->GetUnit(group->GetTargetIcon(7)); + bool const ragingMarked = currentSkull && currentSkull->IsAlive() && + IsLkRagingSpirit(currentSkull->GetEntry()); + + GuidVector const& p3Npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); + + Unit* nearestRaging = nullptr; + float nearestRagingDist = std::numeric_limits::max(); + + for (ObjectGuid const& guid : p3Npcs) + { + Unit* unit = botAI->GetUnit(guid); + if (!unit || !unit->IsAlive()) + continue; + + if (!IsLkRagingSpirit(unit->GetEntry())) + continue; + + float const dist = bot->GetDistance(unit); + if (dist < nearestRagingDist) + { + nearestRagingDist = dist; + nearestRaging = unit; + } + } + + static constexpr uint8 CROSS_ICON = 6; + + // Priority: Raging Spirit > boss. Vile Spirits are not marked + // (handled positionally by HandleVileSpiritMechanics + AT chase + // + hunter frost trap). + if (nearestRaging) + { + if (!ragingMarked && group->GetTargetIcon(7) != nearestRaging->GetGUID()) + group->SetTargetIcon(7, bot->GetGUID(), nearestRaging->GetGUID()); + } + else if (group->GetTargetIcon(7) != boss->GetGUID()) + { + group->SetTargetIcon(7, bot->GetGUID(), boss->GetGUID()); + } + + // Cross marker is no longer used for vile windows — clear if set + if (!group->GetTargetIcon(CROSS_ICON).IsEmpty()) + group->SetTargetIcon(CROSS_ICON, bot->GetGUID(), ObjectGuid::Empty); + + // Per-bot RTI: melee non-tanks always target skull (boss or raging) + if (botAI->IsMelee(bot) && !botAI->IsTank(bot)) + context->GetValue("rti")->Set("skull"); + } + + if (phase3) + { + // skip default skull logic + } + else if (boss && boss->HealthAbovePct(71)) + { + if (group->GetTargetIcon(7) != boss->GetGUID()) + group->SetTargetIcon(7, bot->GetGUID(), boss->GetGUID()); + } + else if (boss) + { + Unit* currentSkull = botAI->GetUnit(group->GetTargetIcon(7)); + bool const spiritMarked = currentSkull && currentSkull->IsAlive() && + IsLkRagingSpirit(currentSkull->GetEntry()); + + if (!spiritMarked) + { + GuidVector const& npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); + Unit* nearestSpirit = nullptr; + float nearestDist = std::numeric_limits::max(); + + for (ObjectGuid const& guid : npcs) + { + Unit* unit = botAI->GetUnit(guid); + if (!unit || !unit->IsAlive()) + continue; + + if (!IsLkRagingSpirit(unit->GetEntry())) + continue; + + float const dist = bot->GetDistance(unit); + if (dist < nearestDist) + { + nearestDist = dist; + nearestSpirit = unit; + } + } + + if (nearestSpirit) + { + if (group->GetTargetIcon(7) != nearestSpirit->GetGUID()) + group->SetTargetIcon(7, bot->GetGUID(), nearestSpirit->GetGUID()); + } + else if (group->GetTargetIcon(7) != boss->GetGUID()) + { + group->SetTargetIcon(7, bot->GetGUID(), boss->GetGUID()); + } + } + } + } + } + + // Val'kyr edge-dive (ongoing) + if (bot->HasAura(SPELL_VALKYR_CARRY)) + { + if (bot->GetPositionZ() > 779.0f) + return JumpTo(bot->GetMapId(), bot->GetPositionX(), bot->GetPositionY(), 740.01f); + + bot->Kill(bot, bot); + return true; + } + + // Detect bots fallen off edge and initiate dive + if (boss && boss->GetHealthPct() < 70.0f && boss->GetHealthPct() > 40.0f && + !HasAnyRemorselessWinter(boss)) + { + static constexpr float PLATFORM_CENTER_X = 503.0f; + static constexpr float PLATFORM_CENTER_Y = -2124.0f; + static constexpr float PLATFORM_EDGE_MIN = 52.0f; + static constexpr float PLATFORM_EDGE_MAX = 70.0f; + static constexpr float PLATFORM_MIN_Z = 844.0f; + + float const dx = bot->GetPositionX() - PLATFORM_CENTER_X; + float const dy = bot->GetPositionY() - PLATFORM_CENTER_Y; + float const platDist = std::hypot(dx, dy); + + if (platDist > PLATFORM_EDGE_MIN && platDist < PLATFORM_EDGE_MAX && + bot->GetPositionZ() > PLATFORM_MIN_Z) + { + bot->AddAura(SPELL_VALKYR_CARRY, bot); + return JumpTo(bot->GetMapId(), bot->GetPositionX(), bot->GetPositionY(), 740.01f); + } + } + + if (HandleTeleportationFixes(diff, terenas)) + return true; + + HandleHeroicNonTankPositioning(diff, terenas); + HandleSpiritMarkingAndTargeting(diff, terenas); + + if (terenas) + return false; + + // Normal encounter flow + if (HandleQuakeMechanics(boss)) + return true; + + HandleShamblingHorrors(boss, hasPlague); + + if (HandleAssistTankAddManagement(boss, diff)) + return true; + + if (HandleRagingSpiritFlanking(boss, hasPlague, diff)) + return true; + + HandleMeleePositioning(boss, hasPlague, diff); + HandleMainTankTargeting(boss, diff); + HandleNonTankHeroicPositioning(boss, diff, hasPlague); + HandleRangedPositioning(boss, hasPlague, diff); + if (HandleDefileMechanics(boss, diff)) + return true; + if (HandleCenterStacking(boss, diff)) + return true; + HandleValkyrMechanics(diff); + HandleVileSpiritMechanics(); + HandleIceSphereMechanics(); + + return false; +} + +bool IccLichKingAddsAction::HandleTeleportationFixes(Difficulty diff, Unit* terenas) +{ + static constexpr float PLATFORM_Z = 840.857f; + static constexpr float SPIRIT_Z = 1049.865f; + static constexpr float MAX_Y_DRIFT = 200.0f; + static constexpr float MAX_Z_DIFF = 1.0f; + static constexpr float SPIRIT_Z_TOLERANCE = 5.0f; + + // Normal mode: snap back if teleported far outside the encounter area + // (Harvest Soul victim exits Frostmourne room). Land on the main tank, + // falling back to assist tank, then the fixed adds anchor. Iterate group + // directly so distance/LOS gating in PartyMemberValue::Check doesn't hide + // a far-away main tank. + if (!IsHeroicLk(diff) && std::abs(bot->GetPositionY() - (-2095.7915f)) > MAX_Y_DRIFT) + { + Player* mainTank = nullptr; + Player* assistTank = nullptr; + if (Group* group = bot->GetGroup()) + { + for (GroupReference* itr = group->GetFirstMember(); itr; itr = itr->next()) + { + Player* member = itr->GetSource(); + if (!member || !member->IsAlive()) + continue; + if (!mainTank && botAI->IsMainTank(member)) + mainTank = member; + else if (!assistTank && botAI->IsAssistTank(member)) + assistTank = member; + } + } + Unit* anchor = mainTank ? static_cast(mainTank) : static_cast(assistTank); + + if (anchor) + bot->TeleportTo(bot->GetMapId(), anchor->GetPositionX(), anchor->GetPositionY(), + anchor->GetPositionZ(), bot->GetOrientation()); + else + bot->TeleportTo(bot->GetMapId(), + ICC_LICH_KING_ADDS_POSITION.GetPositionX(), + ICC_LICH_KING_ADDS_POSITION.GetPositionY(), + ICC_LICH_KING_ADDS_POSITION.GetPositionZ(), + bot->GetOrientation()); + return true; + } + + // Fix bots going underground (buggy ice-platform collisions) + if (!botAI->GetAura("Harvest Soul", bot, false, false) && + !botAI->GetAura("Harvest Souls", bot, false, false) && + std::abs(bot->GetPositionZ() - PLATFORM_Z) > MAX_Z_DIFF) + { + bot->TeleportTo(bot->GetMapId(), bot->GetPositionX(), bot->GetPositionY(), + PLATFORM_Z, bot->GetOrientation()); + return true; + } + + // Heroic: keep bot near main tank during Harvest Soul (fall back to Terenas). + // Resolve main tank via group iteration so distance/LOS gating in + // PartyMemberValue::Check doesn't hide a far-away main tank. + if (terenas && botAI->GetAura("Harvest Soul", bot, false, false) && + std::abs(bot->GetPositionZ() - SPIRIT_Z) > SPIRIT_Z_TOLERANCE) + { + Player* mainTank = nullptr; + if (Group* group = bot->GetGroup()) + { + for (GroupReference* itr = group->GetFirstMember(); itr; itr = itr->next()) + { + Player* member = itr->GetSource(); + if (member && member->IsAlive() && botAI->IsMainTank(member)) + { + mainTank = member; + break; + } + } + } + Unit* anchor = mainTank ? static_cast(mainTank) : terenas; + bot->TeleportTo(bot->GetMapId(), anchor->GetPositionX(), anchor->GetPositionY(), + SPIRIT_Z, bot->GetOrientation()); + return true; + } + + return false; +} + +bool IccLichKingSpiritBombAction::IsBombThreatActive(PlayerbotAI* botAI, Player* bot) +{ + if (!botAI || !bot || !botAI->IsMainTank(bot)) + return false; + + Difficulty const diff = bot->GetMap() ? bot->GetMap()->GetDifficulty() : RAID_DIFFICULTY_10MAN_NORMAL; + if (!IsHeroicLk(diff)) + return false; + + if (!bot->FindNearestCreature(NPC_TERENAS_MENETHIL_HC, 55.0f)) + return false; + + GuidVector const& npcs = botAI->GetAiObjectContext()->GetValue("nearest hostile npcs")->Get(); + for (ObjectGuid const& guid : npcs) + { + Unit* unit = botAI->GetUnit(guid); + if (unit && unit->IsAlive() && unit->GetEntry() == NPC_SPIRIT_BOMB) + return true; + } + + return false; +} + +bool IccLichKingSpiritBombAction::Execute(Event event) +{ + Difficulty const diff = bot->GetMap() ? bot->GetMap()->GetDifficulty() : RAID_DIFFICULTY_10MAN_NORMAL; + Unit* terenas = bot->FindNearestCreature(NPC_TERENAS_MENETHIL_HC, 55.0f); + + if (!botAI->IsMainTank(bot) || !terenas || !IsHeroicLk(diff)) + return false; + + // Snap back to spirit-room Z if glitched through the floor + static constexpr float SPIRIT_Z = 1049.865f; + static constexpr float SPIRIT_Z_TOLERANCE = 5.0f; + if (std::abs(bot->GetPositionZ() - SPIRIT_Z) > SPIRIT_Z_TOLERANCE) + { + bot->TeleportTo(bot->GetMapId(), bot->GetPositionX(), bot->GetPositionY(), + SPIRIT_Z, bot->GetOrientation()); + return true; + } + + static constexpr float SAFE_DIST = 14.0f; + static constexpr float SAFE_HEIGHT = 12.0f; + static constexpr float DENSITY_RADIUS = 10.0f; + static constexpr float DENSITY_PENALTY = 10.0f; + static constexpr float MAX_HEIGHT_DIFF = 8.0f; + static constexpr uint32 UNSAFE_MEM_MS = 15000; + + static std::map s_lastUnsafeX; + static std::map s_lastUnsafeY; + static std::map s_lastUnsafeTime; + uint32 const instId = bot->GetInstanceId(); + float& lastUnsafeX = s_lastUnsafeX[instId]; + float& lastUnsafeY = s_lastUnsafeY[instId]; + uint32& lastUnsafeTime = s_lastUnsafeTime[instId]; + + GuidVector const& npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); + + std::vector bombs; + for (ObjectGuid const& guid : npcs) + { + Unit* unit = botAI->GetUnit(guid); + if (unit && unit->IsAlive() && unit->GetEntry() == NPC_SPIRIT_BOMB) + bombs.push_back(unit); + } + + if (bombs.empty()) + return false; + + // Check current position safety + bool currentlySafe = true; + for (Unit const* bomb : bombs) + { + float const hDist = std::hypot(bot->GetPositionX() - bomb->GetPositionX(), + bot->GetPositionY() - bomb->GetPositionY()); + float const vDist = std::abs(bot->GetPositionZ() - bomb->GetPositionZ()); + + if (hDist < SAFE_DIST && vDist <= SAFE_HEIGHT) + { + currentlySafe = false; + break; + } + } + + if (currentlySafe) + return true; + + lastUnsafeX = bot->GetPositionX(); + lastUnsafeY = bot->GetPositionY(); + lastUnsafeTime = getMSTime(); + + // Search for the best nearby safe position + static constexpr std::array SearchDistances = {6.0f, 10.0f, 15.0f, 20.0f, 25.0f}; + static constexpr int ANGLE_COUNT = 36; + + float bestScore = -std::numeric_limits::max(); + float bestX = 0.0f; + float bestY = 0.0f; + float bestZ = 0.0f; + bool found = false; + + for (float const radius : SearchDistances) + { + for (int i = 0; i < ANGLE_COUNT; ++i) + { + float const angle = i * 2.0f * float(M_PI) / ANGLE_COUNT; + float const testX = bot->GetPositionX() + radius * std::cos(angle); + float const testY = bot->GetPositionY() + radius * std::sin(angle); + float testZ = bot->GetPositionZ(); + + bot->UpdateAllowedPositionZ(testX, testY, testZ); + + if (std::abs(testZ - bot->GetPositionZ()) >= MAX_HEIGHT_DIFF) + continue; + if (!bot->IsWithinLOS(testX, testY, testZ)) + continue; + + bool posSafe = true; + float minHDist = std::numeric_limits::max(); + int nearbyCount = 0; + + for (Unit const* bomb : bombs) + { + float const hDist = std::hypot(testX - bomb->GetPositionX(), + testY - bomb->GetPositionY()); + float const vDist = std::abs(testZ - bomb->GetPositionZ()); + + minHDist = std::min(minHDist, hDist); + + if (hDist < DENSITY_RADIUS) + ++nearbyCount; + + if (hDist < SAFE_DIST && vDist <= SAFE_HEIGHT) + { + posSafe = false; + break; + } + } + + if (!posSafe) + continue; + + if (lastUnsafeTime != 0 && (getMSTime() - lastUnsafeTime) < UNSAFE_MEM_MS && + std::hypot(testX - lastUnsafeX, testY - lastUnsafeY) < SAFE_DIST) + continue; + + float const distBonus = std::max(0.0f, 30.0f - radius); + float const score = minHDist - nearbyCount * DENSITY_PENALTY + distBonus; + + if (score > bestScore) + { + bestScore = score; + bestX = testX; + bestY = testY; + bestZ = testZ; + found = true; + } + } + + if (found && radius <= 15.0f) + break; + } + + // Fallback: surrounded with no clean path. Pick direction where the + // blocking bomb is highest on Z axis and move thru there + if (!found) + { + float bestEscapeZ = std::numeric_limits::max(); + for (float const radius : SearchDistances) + { + for (int i = 0; i < ANGLE_COUNT; ++i) + { + float const angle = i * 2.0f * float(M_PI) / ANGLE_COUNT; + float const testX = bot->GetPositionX() + radius * std::cos(angle); + float const testY = bot->GetPositionY() + radius * std::sin(angle); + float testZ = bot->GetPositionZ(); + + bot->UpdateAllowedPositionZ(testX, testY, testZ); + + if (std::abs(testZ - bot->GetPositionZ()) >= MAX_HEIGHT_DIFF) + continue; + if (!bot->IsWithinLOS(testX, testY, testZ)) + continue; + + // Highest blocking bomb on the line from bot to test pos + float worstBombZ = -std::numeric_limits::max(); + bool blocked = false; + for (Unit const* bomb : bombs) + { + float const hDist = std::hypot(testX - bomb->GetPositionX(), + testY - bomb->GetPositionY()); + if (hDist < SAFE_DIST) + { + blocked = true; + worstBombZ = std::max(worstBombZ, bomb->GetPositionZ()); + } + } + + if (!blocked) + continue; + + if (worstBombZ < bestEscapeZ) + { + bestEscapeZ = worstBombZ; + bestX = testX; + bestY = testY; + bestZ = testZ; + found = true; + } + } + + if (found) + break; + } + } + + if (found && bot->IsWithinLOS(bestX, bestY, bestZ) && + std::abs(bestZ - bot->GetPositionZ()) <= MAX_HEIGHT_DIFF) + { + MoveTo(bot->GetMapId(), bestX, bestY, bestZ, + false, false, false, true, MovementPriority::MOVEMENT_FORCED); + } + + return true; +} + +bool IccLichKingAddsAction::HandleHeroicNonTankPositioning(Difficulty diff, Unit* terenas) +{ + if (!terenas || botAI->IsMainTank(bot) || !IsHeroicLk(diff)) + return false; + + Unit* mainTank = AI_VALUE(Unit*, "main tank"); + if (!mainTank) + return false; + + // Stack on main tank — 3-yard threshold avoids micro-jitter + if (bot->GetExactDist2d(mainTank) > 3.0f) + { + MoveTo(bot->GetMapId(), mainTank->GetPositionX(), mainTank->GetPositionY(), + mainTank->GetPositionZ(), false, false, false, true, + MovementPriority::MOVEMENT_FORCED); + } + + return false; +} + +bool IccLichKingAddsAction::HandleSpiritMarkingAndTargeting(Difficulty diff, Unit* terenas) +{ + if (!terenas || botAI->IsMainTank(bot) || !IsHeroicLk(diff)) + return false; + + Group* group = bot->GetGroup(); + if (!group) + return false; + + static constexpr uint8 STAR_ICON = 0; + static constexpr float MAX_Z_DIFF = 20.0f; + + auto const spiritTargetsGroup = [&](Unit* spirit) -> bool + { + Unit* victim = spirit->GetVictim(); + if (!victim || !victim->IsPlayer()) + return false; + + Group* g = victim->ToPlayer()->GetGroup(); + return g && g->GetGUID() == group->GetGUID(); + }; + + Unit* currentMark = botAI->GetUnit(group->GetTargetIcon(STAR_ICON)); + bool const needNewMark = !currentMark || !currentMark->IsAlive(); + bool const markOnTarget = currentMark && currentMark->IsAlive() && spiritTargetsGroup(currentMark); + + if (needNewMark || !markOnTarget) + { + GuidVector const& npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); + + Unit* prioritySpirit = nullptr; + Unit* nearestSpirit = nullptr; + float priorityDist = 100.0f; + float nearestDist = 100.0f; + + for (ObjectGuid const& guid : npcs) + { + Unit* unit = botAI->GetUnit(guid); + if (!unit || !unit->IsAlive() || !unit->isTargetableForAttack()) + continue; + + if (!IsLkWickedSpirit(unit->GetEntry())) + continue; + + if (std::abs(unit->GetPositionZ() - bot->GetPositionZ()) > MAX_Z_DIFF) + continue; + + float const dist = bot->GetDistance(unit); + if (spiritTargetsGroup(unit) && dist < priorityDist) + { + prioritySpirit = unit; + priorityDist = dist; + } + if (dist < nearestDist) + { + nearestSpirit = unit; + nearestDist = dist; + } + } + + Unit* toMark = prioritySpirit ? prioritySpirit : nearestSpirit; + if (toMark && (needNewMark || (prioritySpirit && !markOnTarget))) + group->SetTargetIcon(STAR_ICON, bot->GetGUID(), toMark->GetGUID()); + } + + // Ranged DPS focus the star target + if (botAI->IsRangedDps(bot)) + { + context->GetValue("rti")->Set("star"); + + Unit* starTarget = botAI->GetUnit(group->GetTargetIcon(STAR_ICON)); + if (starTarget && starTarget->IsAlive()) + { + bot->SetTarget(starTarget->GetGUID()); + bot->SetFacingToObject(starTarget); + Attack(starTarget); + bot->Kill(bot, starTarget); + return true; + } + } + + return false; +} + +bool IccLichKingAddsAction::HandleQuakeMechanics(Unit* boss) +{ + if (!boss || !boss->HasUnitState(UNIT_STATE_CASTING) || !boss->FindCurrentSpellBySpellId(SPELL_QUAKE)) + return false; + + static constexpr float QUAKE_MIN = 35.0f; + static constexpr float QUAKE_MAX = 45.0f; + static constexpr float QUAKE_TARGET = 40.0f; + + float const dist = bot->GetExactDist2d(boss); + if (dist >= QUAKE_MIN && dist <= QUAKE_MAX) + return false; + + // Interrupt active spell cast before repositioning + if (bot->HasUnitState(UNIT_STATE_CASTING)) + bot->InterruptNonMeleeSpells(false); + + float const dx = bot->GetPositionX() - boss->GetPositionX(); + float const dy = bot->GetPositionY() - boss->GetPositionY(); + float const len = std::hypot(dx, dy); + if (len < 0.01f) + return false; + + float const targetX = boss->GetPositionX() + (dx / len) * QUAKE_TARGET; + float const targetY = boss->GetPositionY() + (dy / len) * QUAKE_TARGET; + + return MoveTo(bot->GetMapId(), targetX, targetY, bot->GetPositionZ(), + false, false, false, false, MovementPriority::MOVEMENT_COMBAT); +} + +bool IccLichKingAddsAction::HandleRagingSpiritFlanking(Unit* boss, bool hasPlague, Difficulty diff) +{ + if (!boss || botAI->IsTank(bot) || hasPlague) + return false; + if (HasAnyRemorselessWinter(boss)) + return false; + if (bot->GetVehicle()) + return false; + if (bot->HasAura(SPELL_HARVEST_SOUL_VALKYR) || bot->HasAura(SPELL_VALKYR_CARRY)) + return false; + + GuidVector const& npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); + + // Designated trap hunter (lowest-GUID alive bot hunter) skips flanking when + // vile spirits are active so HandleVileSpiritMechanics can place the hunter + // at center for Frost Trap duty. On heroic, raging + vile coexist during P3 + // and would otherwise keep the hunter stuck in flanking. + if (bot->getClass() == CLASS_HUNTER) + { + bool vileAlive = false; + for (ObjectGuid const& guid : npcs) + { + Unit* unit = botAI->GetUnit(guid); + if (unit && unit->IsAlive() && IsLkVileSpirit(unit)) + { + vileAlive = true; + break; + } + } + + if (vileAlive) + { + if (Group* hunterGroup = bot->GetGroup()) + { + ObjectGuid bestGuid; + for (GroupReference* ref = hunterGroup->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + if (!member || !member->IsAlive()) + continue; + if (member->getClass() != CLASS_HUNTER) + continue; + if (!GET_PLAYERBOT_AI(member)) + continue; + + if (bestGuid.IsEmpty() || member->GetGUID() < bestGuid) + bestGuid = member->GetGUID(); + } + if (!bestGuid.IsEmpty() && bot->GetGUID() == bestGuid) + return false; + } + } + } + + std::vector spirits; + Unit* nearestSpirit = nullptr; + float nearestDist = std::numeric_limits::max(); + + for (ObjectGuid const& guid : npcs) + { + Unit* unit = botAI->GetUnit(guid); + if (!unit || !unit->IsAlive()) + continue; + if (!IsLkRagingSpirit(unit->GetEntry())) + continue; + + float const dist = bot->GetExactDist2d(unit); + if (dist > 40.0f) + continue; + + spirits.push_back(unit); + if (dist < nearestDist) + { + nearestDist = dist; + nearestSpirit = unit; + } + } + + if (spirits.empty()) + return false; + + bool const isRanged = botAI->IsRanged(bot); + static constexpr float RANGED_SAFE_DIST = 15.0f; + + auto IsSafeFromAllSpirits = [&](float x, float y) -> bool + { + for (Unit* spirit : spirits) + { + float const dx = x - spirit->GetPositionX(); + float const dy = y - spirit->GetPositionY(); + float const d = std::hypot(dx, dy); + + if (isRanged && d < RANGED_SAFE_DIST) + return false; + + if (d < 0.1f) + return false; + + // Cone test: dot of spirit's facing vs vector spirit->point. + // dot > 0 means point is in front half of spirit. + float const dot = (std::cos(spirit->GetOrientation()) * dx + + std::sin(spirit->GetOrientation()) * dy) / d; + if (dot > 0.0f) + return false; + } + return true; + }; + + // Already safe from every spirit -- nothing to do + if (IsSafeFromAllSpirits(bot->GetPositionX(), bot->GetPositionY())) + return false; + + std::vector defiles; + for (ObjectGuid const& guid : npcs) + { + Unit* unit = botAI->GetUnit(guid); + if (unit && unit->IsAlive() && unit->GetEntry() == DEFILE_NPC_ID) + defiles.push_back(unit); + } + + auto IsDefileSafe = [&](float x, float y) -> bool + { + static constexpr float SAFETY_MARGIN = 2.0f; + for (Unit const* defile : defiles) + { + float const radius = GetDefileEffectiveRadius(defile, diff); + float const d = std::hypot(x - defile->GetPositionX(), y - defile->GetPositionY()); + if (d < radius + SAFETY_MARGIN) + return false; + } + return true; + }; + + static constexpr float MELEE_STEP = 7.0f; + static constexpr float RANGED_STEP = 5.0f; + float const stepSize = isRanged ? RANGED_STEP : MELEE_STEP; + + auto TryDest = [&](float destX, float destY) -> bool + { + float destZ = nearestSpirit->GetPositionZ(); + bot->UpdateAllowedPositionZ(destX, destY, destZ); + + if (!IsSafeFromAllSpirits(destX, destY)) + return false; + if (!IsDefileSafe(destX, destY)) + return false; + if (!bot->IsWithinLOS(destX, destY, destZ)) + return false; + + float const bDx = destX - bot->GetPositionX(); + float const bDy = destY - bot->GetPositionY(); + float const bDist = std::hypot(bDx, bDy); + if (bDist < 0.5f) + return false; + + // Ranged: don't chase spirits across the room. Reject destinations that + // require more than one short hop. + if (isRanged && bDist > RANGED_STEP * 1.5f) + return false; + + float const step = std::min(stepSize, bDist); + float const stepX = bot->GetPositionX() + (bDx / bDist) * step; + float const stepY = bot->GetPositionY() + (bDy / bDist) * step; + float stepZ = bot->GetPositionZ(); + bot->UpdateAllowedPositionZ(stepX, stepY, stepZ); + + MoveTo(bot->GetMapId(), stepX, stepY, stepZ, + false, false, false, true, + MovementPriority::MOVEMENT_FORCED, true, false); + return true; + }; + + static constexpr int ANGLE_STEPS = 24; + + if (isRanged) + { + // Step away from current bot position. Prefer the direction directly away + // from the nearest spirit, then sweep outward in alternating angles. + float const awayX = bot->GetPositionX() - nearestSpirit->GetPositionX(); + float const awayY = bot->GetPositionY() - nearestSpirit->GetPositionY(); + float const awayLen = std::hypot(awayX, awayY); + float const baseAngle = (awayLen > 0.01f) ? std::atan2(awayY, awayX) : 0.0f; + + for (int i = 0; i < ANGLE_STEPS; ++i) + { + int const sign = (i % 2 == 0) ? 1 : -1; + int const offset = (i + 1) / 2; + float const angle = baseAngle + sign * offset * (2.0f * float(M_PI) / ANGLE_STEPS); + + float const destX = bot->GetPositionX() + std::cos(angle) * RANGED_STEP; + float const destY = bot->GetPositionY() + std::sin(angle) * RANGED_STEP; + + if (TryDest(destX, destY)) + return true; + } + + return false; + } + + // Melee: search rings around the nearest spirit + static constexpr std::array MeleeRings = {4.0f, 6.0f, 9.0f, 13.0f}; + + for (float const radius : MeleeRings) + { + for (int i = 0; i < ANGLE_STEPS; ++i) + { + float const angle = (i * 2.0f * float(M_PI)) / ANGLE_STEPS; + float const destX = nearestSpirit->GetPositionX() + std::cos(angle) * radius; + float const destY = nearestSpirit->GetPositionY() + std::sin(angle) * radius; + + if (TryDest(destX, destY)) + return true; + } + } + + return false; +} + +bool IccLichKingAddsAction::HandleShamblingHorrors(Unit* /*boss*/, bool /*hasPlague*/) +{ + if (bot->getClass() != CLASS_HUNTER) + return false; + + GuidVector const& npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); + for (ObjectGuid const& guid : npcs) + { + Unit* unit = botAI->GetUnit(guid); + if (!unit || !unit->IsAlive() || !IsLkShambling(unit->GetEntry())) + continue; + + if (botAI->HasAura("Enrage", unit)) + { + botAI->CastSpell("Tranquilizing Shot", unit); + return true; + } + } + + return false; +} + +bool IccLichKingAddsAction::HandleAssistTankAddManagement(Unit* boss, Difficulty diff) +{ + if (!botAI->IsAssistTank(bot) || !boss) + return false; + + // Below 71%: stun all shamblings until winter starts so they don't + // shockwave the raid during the transition gap. + if (boss->HealthBelowPct(72) && boss->HealthAbovePct(70)) + { + if (!HasAnyRemorselessWinter(boss)) + { + GuidVector const& stunTargets = AI_VALUE(GuidVector, "possible targets"); + for (ObjectGuid const& guid : stunTargets) + { + Unit* unit = botAI->GetUnit(guid); + if (!unit || !unit->IsAlive()) + continue; + if (!IsLkShambling(unit->GetEntry())) + continue; + if (!unit->HasAura(SPELL_HAMMER_OF_JUSTICE)) + bot->AddAura(SPELL_HAMMER_OF_JUSTICE, unit); + } + } + + return false; + } + + Position const& holdPos = IsHeroicLk(diff) + ? ICC_LICH_KING_ASSISTHC_POSITION + : ICC_LICH_KING_ADDS_POSITION; + + // Class-specific taunt with forced cooldown reset + auto CastClassTaunt = [&](Unit* target) -> bool + { + if (!target || !target->IsAlive()) + return false; + + switch (bot->getClass()) + { + case CLASS_PALADIN: + bot->RemoveSpellCooldown(SPELL_TAUNT_PALADIN, true); + if (botAI->CastSpell("hand of reckoning", target)) + return true; + break; + case CLASS_DEATH_KNIGHT: + bot->RemoveSpellCooldown(SPELL_TAUNT_DK, true); + if (botAI->CastSpell("dark command", target)) + return true; + break; + case CLASS_DRUID: + bot->RemoveSpellCooldown(SPELL_TAUNT_DRUID, true); + if (botAI->CastSpell("growl", target)) + return true; + break; + case CLASS_WARRIOR: + bot->RemoveSpellCooldown(SPELL_TAUNT_WARRIOR, true); + if (botAI->CastSpell("taunt", target)) + return true; + break; + default: + break; + } + + if (botAI->CastSpell("shoot", target) || botAI->CastSpell("throw", target)) + return true; + + return false; + }; + + // Categorise visible adds + GuidVector const& targets = AI_VALUE(GuidVector, "possible targets"); + + std::vector addsOnUs; + std::vector addsElsewhere; + + for (ObjectGuid const& guid : targets) + { + Unit* unit = botAI->GetUnit(guid); + if (!IsLkCollectibleAdd(unit)) + continue; + + if (unit->GetVictim() == bot) + addsOnUs.push_back(unit); + else + addsElsewhere.push_back(unit); + } + + bool const isHeroic = IsHeroicLk(diff); + + // Non-winter Raging Spirit pickup: AT grabs ALL spirits before joining MT. + if (!HasAnyRemorselessWinter(boss)) + { + std::vector looseSpirits; + std::vector spiritsOnUs; + + for (Unit* add : addsOnUs) + { + if (IsLkRagingSpirit(add->GetEntry())) + spiritsOnUs.push_back(add); + } + + for (Unit* add : addsElsewhere) + { + if (!IsLkRagingSpirit(add->GetEntry())) + continue; + if (bot->GetExactDist2d(add) > 60.0f) + continue; + looseSpirits.push_back(add); + } + + if (!looseSpirits.empty() || !spiritsOnUs.empty()) + { + // Taunt every loose spirit that we can reach + Unit* nearestLoose = nullptr; + float nearestLooseDist = FLT_MAX; + for (Unit* spirit : looseSpirits) + { + CastClassTaunt(spirit); + float const d = bot->GetExactDist2d(spirit); + if (d < nearestLooseDist) + { + nearestLooseDist = d; + nearestLoose = spirit; + } + } + + // If any spirit is loose, go fetch the nearest loose one (do not drag yet) + if (nearestLoose) + { + bot->SetTarget(nearestLoose->GetGUID()); + bot->SetFacingToObject(nearestLoose); + Attack(nearestLoose); + + if (nearestLooseDist > 4.0f) + { + float const dx = nearestLoose->GetPositionX() - bot->GetPositionX(); + float const dy = nearestLoose->GetPositionY() - bot->GetPositionY(); + float const len = std::hypot(dx, dy); + if (len > 0.1f) + { + float const step = std::min(7.0f, len - 3.0f); + if (step > 0.0f) + { + float const goalX = bot->GetPositionX() + (dx / len) * step; + float const goalY = bot->GetPositionY() + (dy / len) * step; + float goalZ = bot->GetPositionZ(); + bot->UpdateAllowedPositionZ(goalX, goalY, goalZ); + MoveTo(bot->GetMapId(), goalX, goalY, goalZ, + false, false, false, true, + MovementPriority::MOVEMENT_FORCED, true, false); + } + } + } + return false; + } + + // All spirits are on us: drag the pack toward boss + Unit* nearestOnUs = nullptr; + float nearestOnUsDist = FLT_MAX; + for (Unit* spirit : spiritsOnUs) + { + float const d = bot->GetExactDist2d(spirit); + if (d < nearestOnUsDist) + { + nearestOnUsDist = d; + nearestOnUs = spirit; + } + } + + if (nearestOnUs) + { + bot->SetTarget(nearestOnUs->GetGUID()); + bot->SetFacingToObject(nearestOnUs); + Attack(nearestOnUs); + + if (bot->GetExactDist2d(boss) > 5.0f) + { + float const dx = boss->GetPositionX() - bot->GetPositionX(); + float const dy = boss->GetPositionY() - bot->GetPositionY(); + float const len = std::hypot(dx, dy); + if (len > 0.1f) + { + float const step = std::min(5.0f, len - 4.0f); + if (step > 0.0f) + { + float const goalX = bot->GetPositionX() + (dx / len) * step; + float const goalY = bot->GetPositionY() + (dy / len) * step; + float goalZ = bot->GetPositionZ(); + bot->UpdateAllowedPositionZ(goalX, goalY, goalZ); + MoveTo(bot->GetMapId(), goalX, goalY, goalZ, + false, false, false, true, + MovementPriority::MOVEMENT_FORCED, true, false); + } + } + } + return false; + } + } + } + + // Hold position during phase 1 (boss above 70%) + if (boss->HealthAbovePct(70)) + { + if (!isHeroic) + { + if (bot->GetExactDist2d(holdPos) > 5.0f) + { + MoveTo(bot->GetMapId(), holdPos.GetPositionX(), holdPos.GetPositionY(), + holdPos.GetPositionZ(), false, false, false, true, + MovementPriority::MOVEMENT_FORCED, true, false); + } + } + else + { + Unit* mainTankStep1 = AI_VALUE(Unit*, "main tank"); + if (mainTankStep1 && mainTankStep1->IsAlive()) + { + float const distToMt = bot->GetExactDist2d(mainTankStep1); + + if (distToMt < 20.0f || distToMt > 30.0f) + { + float const axX = holdPos.GetPositionX() - mainTankStep1->GetPositionX(); + float const axY = holdPos.GetPositionY() - mainTankStep1->GetPositionY(); + float const axLen = std::hypot(axX, axY); + + if (axLen > 0.01f) + { + float const targetDist = std::max(20.0f, std::min(30.0f, distToMt)); + float const goalX = mainTankStep1->GetPositionX() + (axX / axLen) * targetDist; + float const goalY = mainTankStep1->GetPositionY() + (axY / axLen) * targetDist; + MoveTo(bot->GetMapId(), goalX, goalY, holdPos.GetPositionZ(), + false, false, false, true, + MovementPriority::MOVEMENT_FORCED, true, false); + } + } + } + } + } + + // Taunt loose adds: shamblings first, then others; nearest wins within tier. + Unit* tauntTargetShambling = nullptr; + float tauntDistShambling = FLT_MAX; + Unit* tauntTargetOther = nullptr; + float tauntDistOther = FLT_MAX; + + for (Unit* add : addsElsewhere) + { + float const dist = bot->GetExactDist2d(add); + if (dist > 30.0f) + continue; + + if (IsLkShambling(add->GetEntry())) + { + if (dist < tauntDistShambling) + { + tauntDistShambling = dist; + tauntTargetShambling = add; + } + } + else if (dist < tauntDistOther) + { + tauntDistOther = dist; + tauntTargetOther = add; + } + } + + Unit* tauntTarget = tauntTargetShambling ? tauntTargetShambling : tauntTargetOther; + if (tauntTarget) + CastClassTaunt(tauntTarget); + + // No adds at all — stay at hold position + if (addsOnUs.empty() && addsElsewhere.empty()) + return boss->HealthAbovePct(70); + + // Target priority by mob type (stable, not victim-dependent): + // Tier 1 — Shambling Horror + // Tier 0 — Ghoul / Raging Spirit / other collectible add + // Sticky: never switch away from a same-or-higher tier target. + auto TargetTier = [](Unit* t) -> int + { + if (!t || !t->IsAlive()) + return -1; + if (!IsLkCollectibleAdd(t)) + return -1; + return IsLkShambling(t->GetEntry()) ? 1 : 0; + }; + + Unit* currentTarget = bot->GetVictim(); + int const currentTier = TargetTier(currentTarget); + + // Current target is a valid add — keep it unless a higher-tier add exists + if (currentTier >= 0) + { + if (currentTier < 1) + { + for (Unit* add : addsOnUs) + { + if (IsLkShambling(add->GetEntry())) + { + bot->SetTarget(add->GetGUID()); + bot->SetFacingToObject(add); + Attack(add); + currentTarget = add; + break; + } + } + } + + if (currentTarget) + { + bot->SetFacingToObject(currentTarget); + Attack(currentTarget); + } + } + else + { + // Current target is dead/invalid — pick a new one. + // Prefer adds already on us, then nearby adds within melee reach. + Unit* bestTarget = nullptr; + int bestTier = -1; + float bestDist = FLT_MAX; + + for (Unit* add : addsOnUs) + { + int const tier = TargetTier(add); + if (tier < 0) + continue; + float const dist = bot->GetExactDist2d(add); + if (tier > bestTier || (tier == bestTier && dist < bestDist)) + { + bestTier = tier; + bestDist = dist; + bestTarget = add; + } + } + + if (!bestTarget) + { + for (Unit* add : addsElsewhere) + { + int const tier = TargetTier(add); + if (tier < 0) + continue; + float const dist = bot->GetExactDist2d(add); + if (dist > 8.0f) + continue; + if (tier > bestTier || (tier == bestTier && dist < bestDist)) + { + bestTier = tier; + bestDist = dist; + bestTarget = add; + } + } + } + + if (bestTarget) + { + bot->SetTarget(bestTarget->GetGUID()); + bot->SetFacingToObject(bestTarget); + Attack(bestTarget); + } + } + + // Face the nearest high-priority add (even when not actively attacking) + { + Unit* faceTarget = nullptr; + int faceTier = -1; + float faceDist = FLT_MAX; + + auto const& faceList = addsOnUs.empty() ? addsElsewhere : addsOnUs; + for (Unit* add : faceList) + { + int const tier = TargetTier(add); + if (tier < 0) + continue; + float const dist = bot->GetExactDist2d(add); + if (tier > faceTier || (tier == faceTier && dist < faceDist)) + { + faceTier = tier; + faceDist = dist; + faceTarget = add; + } + } + + if (faceTarget) + bot->SetFacingToObject(faceTarget); + } + + // Heroic: rotate any Shambling that is facing the MT + if (isHeroic && !addsOnUs.empty()) + { + Unit* mainTank = AI_VALUE(Unit*, "main tank"); + if (mainTank && mainTank->IsAlive()) + { + Unit* worstShambling = nullptr; + float worstDot = -2.0f; + + for (Unit* add : addsOnUs) + { + if (!IsLkShambling(add->GetEntry())) + continue; + + float const toMtX = mainTank->GetPositionX() - add->GetPositionX(); + float const toMtY = mainTank->GetPositionY() - add->GetPositionY(); + float const toMtLen = std::hypot(toMtX, toMtY); + if (toMtLen < 0.01f) + continue; + + float const dot = (std::cos(add->GetOrientation()) * toMtX + + std::sin(add->GetOrientation()) * toMtY) / toMtLen; + if (dot > worstDot) + { + worstDot = dot; + worstShambling = add; + } + } + + // dot > 0.3: Shambling is facing toward MT — reposition to turn it away + if (worstShambling && worstDot > 0.3f) + { + float const axisX = worstShambling->GetPositionX() - mainTank->GetPositionX(); + float const axisY = worstShambling->GetPositionY() - mainTank->GetPositionY(); + float const axisLen = std::hypot(axisX, axisY); + + if (axisLen > 0.01f) + { + float const goalX = worstShambling->GetPositionX() + (axisX / axisLen) * 5.0f; + float const goalY = worstShambling->GetPositionY() + (axisY / axisLen) * 5.0f; + + if (std::hypot(goalX - bot->GetPositionX(), goalY - bot->GetPositionY()) > 1.0f) + { + MoveTo(bot->GetMapId(), goalX, goalY, bot->GetPositionZ(), + false, false, false, true, + MovementPriority::MOVEMENT_COMBAT, true, false); + } + } + } + } + } + + return false; +} + +bool IccLichKingAddsAction::HandleMeleePositioning(Unit* boss, bool hasPlague, Difficulty diff) +{ + if (!boss || !botAI->IsMelee(bot) || botAI->IsAssistTank(bot) || + boss->HealthBelowPct(71) || hasPlague || IsHeroicLk(diff)) + return false; + + float const distToPos = bot->GetDistance(ICC_LICH_KING_MELEE_POSITION); + if (distToPos <= 6.0f) + return false; + + if (!botAI->IsMainTank(bot)) + { + MoveTo(bot->GetMapId(), + ICC_LICH_KING_MELEE_POSITION.GetPositionX(), + ICC_LICH_KING_MELEE_POSITION.GetPositionY(), + ICC_LICH_KING_MELEE_POSITION.GetPositionZ(), + false, false, false, true, + MovementPriority::MOVEMENT_FORCED, true, false); + return true; + } + + // Main tank: step toward position in 3-yard increments to avoid overshooting + if (boss->GetVictim() != bot) + return false; + + float const dx = ICC_LICH_KING_MELEE_POSITION.GetPositionX() - bot->GetPositionX(); + float const dy = ICC_LICH_KING_MELEE_POSITION.GetPositionY() - bot->GetPositionY(); + float const len = std::hypot(dx, dy); + if (len < 0.1f) + return false; + + float const step = std::min(3.0f, len - 1.0f); + if (step <= 0.0f) + { + MoveTo(bot->GetMapId(), + ICC_LICH_KING_MELEE_POSITION.GetPositionX(), + ICC_LICH_KING_MELEE_POSITION.GetPositionY(), + bot->GetPositionZ(), + false, false, false, true, + MovementPriority::MOVEMENT_FORCED, true, false); + return true; + } + + MoveTo(bot->GetMapId(), + bot->GetPositionX() + (dx / len) * step, + bot->GetPositionY() + (dy / len) * step, + bot->GetPositionZ(), + false, false, false, true, + MovementPriority::MOVEMENT_FORCED, true, false); + + return true; +} + +bool IccLichKingAddsAction::HandleMainTankTargeting(Unit* boss, Difficulty diff) +{ + if (!botAI->IsMainTank(bot) || !boss) + return false; + + if (boss->GetVictim() == bot) + return false; + + // Non-winter Raging Spirit case: stay on boss only when an assist tank is alive + // to take the spirit. If AT is dead, fall through so MT can swap. + if (!HasAnyRemorselessWinter(boss)) + { + GuidVector const& npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); + bool spiritAlive = false; + for (ObjectGuid const& guid : npcs) + { + Unit* unit = botAI->GetUnit(guid); + if (unit && unit->IsAlive() && IsLkRagingSpirit(unit->GetEntry())) + { + spiritAlive = true; + break; + } + } + + if (spiritAlive) + { + bool assistTankAlive = false; + if (Group* group = bot->GetGroup()) + { + for (GroupReference* itr = group->GetFirstMember(); itr; itr = itr->next()) + { + Player* member = itr->GetSource(); + if (member && member->IsAlive() && member != bot && + botAI->IsAssistTank(member)) + { + assistTankAlive = true; + break; + } + } + } + + if (assistTankAlive) + { + bot->SetTarget(boss->GetGUID()); + bot->SetFacingToObject(boss); + Attack(boss); + return true; + } + } + } + + if (!IsHeroicLk(diff) || boss->HealthBelowPct(71)) + return false; + + bot->SetTarget(boss->GetGUID()); + bot->SetFacingToObject(boss); + Attack(boss); + + return true; +} + +bool IccLichKingAddsAction::HandleNonTankHeroicPositioning(Unit* boss, Difficulty diff, bool hasPlague) +{ + if (botAI->IsTank(bot) || !boss || !IsHeroicLk(diff)) + return false; + + if (boss->HealthBelowPct(71) || hasPlague) + return false; + + Unit* mainTank = AI_VALUE(Unit*, "main tank"); + if (!mainTank) + return false; + + float const distToTank = bot->GetDistance2d(mainTank); + + if (bot->getClass() == CLASS_HUNTER) + { + // Hunters stay within 10 yd of MT but away from the assist tank position + if (distToTank > 10.0f) + { + float goalX = mainTank->GetPositionX(); + float goalY = mainTank->GetPositionY(); + + float const atDx = ICC_LICH_KING_ASSISTHC_POSITION.GetPositionX() - mainTank->GetPositionX(); + float const atDy = ICC_LICH_KING_ASSISTHC_POSITION.GetPositionY() - mainTank->GetPositionY(); + float const atLen = std::hypot(atDx, atDy); + if (atLen > 0.01f) + { + goalX -= (atDx / atLen) * 8.0f; + goalY -= (atDy / atLen) * 8.0f; + } + + MoveTo(bot->GetMapId(), goalX, goalY, bot->GetPositionZ(), + false, false, false, true, + MovementPriority::MOVEMENT_FORCED, true, false); + return true; + } + } + else + { + // Everyone else stacks on main tank + if (distToTank > 3.0f) + { + MoveTo(bot->GetMapId(), mainTank->GetPositionX(), mainTank->GetPositionY(), + bot->GetPositionZ(), false, false, false, true, + MovementPriority::MOVEMENT_FORCED, true, false); + return true; + } + } + + return false; +} + +bool IccLichKingAddsAction::HandleRangedPositioning(Unit* boss, bool hasPlague, Difficulty diff) +{ + if (!boss || !botAI->IsRanged(bot) || boss->HealthBelowPct(71) || + hasPlague || IsHeroicLk(diff)) + return false; + + if (bot->GetDistance(ICC_LICH_KING_RANGED_POSITION) > 2.0f) + { + MoveTo(bot->GetMapId(), + ICC_LICH_KING_RANGED_POSITION.GetPositionX(), + ICC_LICH_KING_RANGED_POSITION.GetPositionY(), + ICC_LICH_KING_RANGED_POSITION.GetPositionZ(), + false, false, false, true, + MovementPriority::MOVEMENT_FORCED, true, false); + return true; + } + + return false; +} + +bool IccLichKingAddsAction::HandleCenterStacking(Unit* boss, Difficulty diff) +{ + if (!boss || !boss->HealthBelowPct(67) || HasAnyRemorselessWinter(boss)) + return false; + + // Defile target: let HandleDefileMechanics() handle movement + // (perpendicular run). Don't override with slot/center movement. + auto const defileIt = IcecrownHelpers::defileCast.find(bot->GetInstanceId()); + if (defileIt != IcecrownHelpers::defileCast.end()) + { + auto const& defileInfo = defileIt->second; + if (!defileInfo.targetGuid.IsEmpty() && + getMSTimeDiff(defileInfo.castTime, getMSTime()) <= 3000 && + defileInfo.targetGuid == bot->GetGUID()) + { + return false; + } + } + + // Marked Val'kyrs (Skull/Cross/Star) are being kited by assist - bots + // assigned to them must not be locked to the center stack. + if (Group* group = bot->GetGroup()) + { + static constexpr std::array ValkyrIcons = {7, 6, 0}; + for (uint8 const iconIdx : ValkyrIcons) + { + Unit* marked = botAI->GetUnit(group->GetTargetIcon(iconIdx)); + if (marked && marked->IsAlive() && IsLkValkyr(marked)) + return false; + } + } + + static constexpr float HUNTER_DISTANCE = 8.0f; + static constexpr float OTHER_DISTANCE = 5.0f; + // Worst-case stack radius: hunters sit furthest out, plus safety margin. + // Defile reach = dist - radius; unsafe if reach < stack + safety. + static constexpr float STACK_SAFETY = 3.0f; + static constexpr float STACK_DEFILE_BUFFER = HUNTER_DISTANCE + STACK_SAFETY; + + bool const lastPhase = boss->HealthBelowPct(37); + bool const spiritPhase = boss->HealthBelowPct(43) && boss->HealthAbovePct(37); + + GuidVector const& npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); + std::vector defiles; + for (ObjectGuid const& guid : npcs) + { + Unit* unit = botAI->GetUnit(guid); + if (!unit || !unit->IsAlive()) + continue; + + if (IsLkRagingSpirit(unit->GetEntry()) || IsLkVileSpirit(unit) || + IsIceSphere(unit->GetEntry())) + return false; + + if (unit->GetEntry() == DEFILE_NPC_ID) + defiles.push_back(unit); + } + + Position dest; + if (spiritPhase || lastPhase) + { + // Phase 3: pick the safe vile-spirit slot closest to the bot. + Position const slots[3] = { + ICC_LK_VILE_SPIRIT1_POSITION, + ICC_LK_VILE_SPIRIT2_POSITION, + ICC_LK_VILE_SPIRIT3_POSITION, + }; + + auto IsSlotSafe = [&](Position const& slot) -> bool + { + for (Unit const* defile : defiles) + { + float const radius = GetDefileEffectiveRadius(defile, diff); + float const d = std::hypot(slot.GetPositionX() - defile->GetPositionX(), + slot.GetPositionY() - defile->GetPositionY()); + if (d < radius + STACK_DEFILE_BUFFER) + return false; + } + return true; + }; + + // Among safe slots (0 and 2), pick the one closest to the group + // centroid. Centroid is identical for every bot in the raid so all + // bots converge on the same anchor. Cached per boss GUID for 2s to + // dampen flicker if defile state shifts between ticks. + struct StackChoice { uint32 evaluatedMs; int slotIdx; }; + static std::map, StackChoice> s_stackChoice; + static constexpr uint32 STACK_CHOICE_TTL_MS = 2000; + auto const stackKey = std::make_pair(boss->GetInstanceId(), boss->GetGUID()); + + uint32 const now = getMSTime(); + int chosen = -1; + auto cacheIt = s_stackChoice.find(stackKey); + if (cacheIt != s_stackChoice.end() && + getMSTimeDiff(cacheIt->second.evaluatedMs, now) < STACK_CHOICE_TTL_MS) + { + chosen = cacheIt->second.slotIdx; + } + else + { + Position const centroid = ComputeGroupCentroid(bot); + float bestDist = std::numeric_limits::max(); + for (int const i : {0, 2}) + { + if (!IsSlotSafe(slots[i])) + continue; + + float const d = std::hypot(centroid.GetPositionX() - slots[i].GetPositionX(), + centroid.GetPositionY() - slots[i].GetPositionY()); + if (d < bestDist) + { + bestDist = d; + chosen = i; + } + } + s_stackChoice[stackKey] = {now, chosen}; + } + + if (chosen < 0) + return false; + + dest = slots[chosen]; + } + else + { + // Phase 2: stack on platform center if it's safe from defiles. + for (Unit const* defile : defiles) + { + float const radius = GetDefileEffectiveRadius(defile, diff); + float const d = std::hypot( + defile->GetPositionX() - ICC_LICH_KING_CENTER_POSITION.GetPositionX(), + defile->GetPositionY() - ICC_LICH_KING_CENTER_POSITION.GetPositionY()); + if (d < radius + STACK_DEFILE_BUFFER) + return false; + } + dest = ICC_LICH_KING_CENTER_POSITION; + } + + // Main tank only moves to the stack point if he's still the boss's victim. + if (botAI->IsMainTank(bot) && boss->GetVictim() != bot) + return false; + + float const distToDest = bot->GetDistance2d(dest.GetPositionX(), dest.GetPositionY()); + float const threshold = (bot->getClass() == CLASS_HUNTER) ? HUNTER_DISTANCE : OTHER_DISTANCE; + if (distToDest <= threshold) + return false; + + auto const [stepX, stepY] = DefileAwareStep(dest.GetPositionX(), dest.GetPositionY(), defiles, diff); + float stepZ = dest.GetPositionZ(); + bot->UpdateAllowedPositionZ(stepX, stepY, stepZ); + + MoveTo(bot->GetMapId(), stepX, stepY, stepZ, + false, false, false, true, + MovementPriority::MOVEMENT_FORCED, true, false); + return true; +} + +bool IccLichKingAddsAction::HandleDefileMechanics(Unit* boss, Difficulty diff) +{ + if (!boss) + return false; + + static constexpr float SAFETY_MARGIN = 3.0f; + static constexpr float MOVE_DISTANCE = 5.0f; + static constexpr float FIXED_Z = 840.857f; + static constexpr float MAX_HEIGHT_DIFF = 5.0f; + static constexpr int ANGLE_TESTS = 16; + + GuidVector const& npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); + + std::vector defiles; + for (ObjectGuid const& guid : npcs) + { + Unit* unit = botAI->GetUnit(guid); + if (unit && unit->IsAlive() && unit->GetEntry() == DEFILE_NPC_ID) + defiles.push_back(unit); + } + + if (!defiles.empty()) + { + float const botX = bot->GetPositionX(); + float const botY = bot->GetPositionY(); + + bool needToMove = false; + for (Unit const* defile : defiles) + { + float const radius = GetDefileEffectiveRadius(defile, diff); + float const dist = std::hypot(botX - defile->GetPositionX(), + botY - defile->GetPositionY()); + if (dist < radius + SAFETY_MARGIN) + { + needToMove = true; + break; + } + } + + if (needToMove) + { + float bestScore = 0.0f; + float bestAngle = 0.0f; + bool found = false; + + for (int i = 0; i < ANGLE_TESTS; ++i) + { + float const angle = i * float(M_PI) / 8.0f; + float const testX = botX + MOVE_DISTANCE * std::cos(angle); + float const testY = botY + MOVE_DISTANCE * std::sin(angle); + float testZ = FIXED_Z; + + bot->UpdateAllowedPositionZ(testX, testY, testZ); + + if (!bot->IsWithinLOS(testX, testY, testZ) || + std::abs(testZ - bot->GetPositionZ()) >= MAX_HEIGHT_DIFF) + continue; + + float minDefileDist = std::numeric_limits::max(); + for (Unit const* defile : defiles) + { + float const d = std::hypot(testX - defile->GetPositionX(), + testY - defile->GetPositionY()); + minDefileDist = std::min(minDefileDist, d); + } + + float const bossProximity = 100.0f - std::min(100.0f, boss->GetDistance2d(testX, testY)); + float const score = minDefileDist + bossProximity * 0.5f; + + if (score > bestScore) + { + bestScore = score; + bestAngle = angle; + found = true; + } + } + + if (found) + { + float moveX = botX + MOVE_DISTANCE * std::cos(bestAngle); + float moveY = botY + MOVE_DISTANCE * std::sin(bestAngle); + float moveZ = FIXED_Z; + + if (bot->HasUnitState(UNIT_STATE_CASTING)) + bot->InterruptNonMeleeSpells(false); + + bot->UpdateAllowedPositionZ(moveX, moveY, moveZ); + MoveTo(bot->GetMapId(), moveX, moveY, moveZ, + false, false, false, true, MovementPriority::MOVEMENT_FORCED); + return true; + } + } + } + + if (!boss->HasUnitState(UNIT_STATE_CASTING) || !boss->FindCurrentSpellBySpellId(DEFILE_CAST_ID)) + return false; + + // Boss casting Defile — only the targeted player runs out. Target is + // stamped by IccLichKingListenerScript at OnSpellPrepare time (cast start). + auto const defileIt = IcecrownHelpers::defileCast.find(bot->GetInstanceId()); + if (defileIt == IcecrownHelpers::defileCast.end()) + return false; + auto const& info = defileIt->second; + if (info.targetGuid.IsEmpty() || getMSTimeDiff(info.castTime, getMSTime()) > 3000) + return false; + + Player* target = ObjectAccessor::FindPlayer(info.targetGuid); + if (!target || !target->IsAlive()) + return false; + + // Main tank yells once per cast. + static std::map s_lastYellMs; + uint32& lastYellMs = s_lastYellMs[bot->GetInstanceId()]; + if (botAI->IsMainTank(bot) && info.castTime != lastYellMs) + { + botAI->Yell("Defile on " + target->GetName() + " - move to the edge!"); + lastYellMs = info.castTime; + } + + if (!target->HasAura(SPELL_NITRO_BOOSTS)) + target->AddAura(SPELL_NITRO_BOOSTS, target); + + // Real players run themselves; only the targeted bot moves. + if (target != bot) + return false; + + // During Vile Spirit phase, run perpendicular (left/right) to the line from + // spirit centroid -> this bot, so defile drops sideways instead of into the + // raid stack or into the spirit cluster. + float spiritSumX = 0.0f; + float spiritSumY = 0.0f; + uint32 spiritCount = 0; + for (ObjectGuid const& guid : npcs) + { + Unit* unit = botAI->GetUnit(guid); + if (!unit || !unit->IsAlive() || !IsLkVileSpirit(unit)) + continue; + spiritSumX += unit->GetPositionX(); + spiritSumY += unit->GetPositionY(); + ++spiritCount; + } + + float baseAngle; + if (spiritCount > 0) + { + float const cx = spiritSumX / spiritCount; + float const cy = spiritSumY / spiritCount; + float const sx = bot->GetPositionX() - cx; + float const sy = bot->GetPositionY() - cy; + float const sLen = std::hypot(sx, sy); + if (sLen < 0.01f) + baseAngle = (bot->GetGUID().GetCounter() % 16) * (float(M_PI) / 8.0f); + else + { + float const radial = std::atan2(sy, sx); + // Pick left or right perpendicular based on bot GUID for stable choice + float const sign = (bot->GetGUID().GetCounter() & 1) ? 1.0f : -1.0f; + baseAngle = radial + sign * (float(M_PI) / 2.0f); + } + } + else + { + Position centroid = ComputeGroupCentroid(bot); + float const dx = bot->GetPositionX() - centroid.GetPositionX(); + float const dy = bot->GetPositionY() - centroid.GetPositionY(); + float const len = std::hypot(dx, dy); + if (len < 0.01f) + baseAngle = (bot->GetGUID().GetCounter() % 16) * (float(M_PI) / 8.0f); + else + baseAngle = std::atan2(dy, dx); + } + + // Reject candidates landing inside any defile (radius from grow stacks). + auto IsSafeFromDefiles = [&](float x, float y) -> bool + { + for (Unit const* defile : defiles) + { + float const radius = GetDefileEffectiveRadius(defile, diff); + float const d = std::hypot(x - defile->GetPositionX(), + y - defile->GetPositionY()); + if (d < radius + SAFETY_MARGIN) + return false; + } + return true; + }; + + // Try base direction first, then fan out in +/- 22.5 deg increments. + float destX = 0.0f; + float destY = 0.0f; + bool found = false; + for (int offset = 0; offset <= 8 && !found; ++offset) + { + for (int dir : {-1, 1}) + { + if (offset == 0 && dir > 0) + continue; + + float const angle = baseAngle + dir * offset * float(M_PI) / 8.0f; + float const tx = bot->GetPositionX() + 10.0f * std::cos(angle); + float const ty = bot->GetPositionY() + 10.0f * std::sin(angle); + if (!IsSafeFromDefiles(tx, ty)) + continue; + + destX = tx; + destY = ty; + found = true; + break; + } + } + + if (!found) + return false; + + float destZ = bot->GetPositionZ(); + bot->UpdateAllowedPositionZ(destX, destY, destZ); + + if (bot->HasUnitState(UNIT_STATE_CASTING)) + bot->InterruptNonMeleeSpells(false); + + MoveTo(bot->GetMapId(), destX, destY, destZ, + false, false, false, true, MovementPriority::MOVEMENT_FORCED); + return true; +} + +bool IccLichKingAddsAction::HandleValkyrMechanics(Difficulty diff) +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "the lich king"); + Group* group = bot->GetGroup(); + if (!group) + return false; + + GuidVector const& npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); + + std::vector grabbingValkyrs; + for (ObjectGuid const& guid : npcs) + { + Unit* unit = botAI->GetUnit(guid); + if (!unit || !unit->IsAlive() || !IsLkValkyr(unit)) + continue; + + // Heroic: only target Val'kyrs above 49% health + bool const isGrabbing = unit->HasAura(SPELL_HARVEST_SOUL_VALKYR) && + (!IsHeroicLk(diff) || unit->HealthAbovePct(49)); + + if (isGrabbing) + grabbingValkyrs.push_back(unit); + } + + // No active valkyrs — reset bots still targeting an excluded valkyr + if (grabbingValkyrs.empty() || (boss && boss->HealthBelowPct(40))) + { + Unit* currentTarget = bot->GetVictim(); + if (currentTarget && IsLkValkyr(currentTarget) && boss) + { + bot->SetTarget(boss->GetGUID()); + context->GetValue("rti")->Set("skull"); + } + return false; + } + + if (botAI->IsMainTank(bot)) + return false; + + // Defile target: let HandleDefileMechanics() handle movement + // (perpendicular run). Don't override with Val'kyr chase. + auto const defileIt = IcecrownHelpers::defileCast.find(bot->GetInstanceId()); + if (defileIt != IcecrownHelpers::defileCast.end()) + { + auto const& defileInfo = defileIt->second; + if (!defileInfo.targetGuid.IsEmpty() && getMSTimeDiff(defileInfo.castTime, getMSTime()) <= 3000 && defileInfo.targetGuid == bot->GetGUID()) + return false; + } + + HandleValkyrMarking(grabbingValkyrs, diff); + HandleValkyrAssignment(grabbingValkyrs); + + return true; +} + +bool IccLichKingAddsAction::HandleValkyrMarking(std::vector const& grabbingValkyrs, + Difficulty diff) +{ + Group* group = bot->GetGroup(); + if (!group) + return false; + + std::vector sorted = grabbingValkyrs; + std::sort(sorted.begin(), sorted.end(), + [](Unit* a, Unit* b) { return a->GetGUID() < b->GetGUID(); }); + + static constexpr std::array Icons = {7, 6, 0}; // Skull, Cross, Star + + // Heroic: clear stale markers for Val'kyrs no longer grabbing or at wrong Z + if (IsHeroicLk(diff)) + { + for (uint8 const iconIdx : Icons) + { + Unit* marked = botAI->GetUnit(group->GetTargetIcon(iconIdx)); + if (!marked || !IsLkValkyr(marked)) + continue; + + bool const stale = !marked->HasAura(SPELL_HARVEST_SOUL_VALKYR) || + std::abs(marked->GetPositionZ() - bot->GetPositionZ()) > 5.0f; + if (stale) + group->SetTargetIcon(iconIdx, bot->GetGUID(), ObjectGuid::Empty); + } + } + + // Clear icon slots beyond the current Val'kyr count + for (size_t i = sorted.size(); i < Icons.size(); ++i) + { + if (!group->GetTargetIcon(Icons[i]).IsEmpty()) + group->SetTargetIcon(Icons[i], bot->GetGUID(), ObjectGuid::Empty); + } + + // Assign an icon to each active Val'kyr. + // Skip skull if a Raging Spirit currently owns it. + for (size_t i = 0; i < sorted.size() && i < Icons.size(); ++i) + { + uint8 const iconIdx = Icons[i]; + + if (iconIdx == 7) + { + Unit* currentSkull = botAI->GetUnit(group->GetTargetIcon(7)); + if (currentSkull && currentSkull->IsAlive() && IsLkRagingSpirit(currentSkull->GetEntry())) + continue; + } + + Unit* marked = botAI->GetUnit(group->GetTargetIcon(iconIdx)); + if (!marked || marked != sorted[i]) + group->SetTargetIcon(iconIdx, bot->GetGUID(), sorted[i]->GetGUID()); + } + + return true; +} + +bool IccLichKingAddsAction::HandleValkyrAssignment(std::vector const& grabbingValkyrs) +{ + Group* group = bot->GetGroup(); + if (!group) + return false; + + Unit* boss = AI_VALUE2(Unit*, "find target", "the lich king"); + if (boss && boss->HealthBelowPct(40)) + return false; + + std::vector valid; + for (Unit* valkyr : grabbingValkyrs) + { + if (valkyr && valkyr->IsAlive() && valkyr->HasAura(SPELL_HARVEST_SOUL_VALKYR)) + valid.push_back(valkyr); + } + + if (valid.empty()) + return false; + + std::sort(valid.begin(), valid.end(), + [](Unit* a, Unit* b) { return a->GetGUID() < b->GetGUID(); }); + + // Build sorted list of non-main-tank members for deterministic assignment + std::vector assistMembers; + for (GroupReference* itr = group->GetFirstMember(); itr; itr = itr->next()) + { + Player* member = itr->GetSource(); + if (member && !botAI->IsMainTank(member)) + assistMembers.push_back(member); + } + + if (assistMembers.empty()) + return false; + + std::sort(assistMembers.begin(), assistMembers.end(), + [](Player* a, Player* b) { return a->GetGUID() < b->GetGUID(); }); + + auto const it = std::find(assistMembers.begin(), assistMembers.end(), bot); + if (it == assistMembers.end()) + return false; + + size_t const myIndex = std::distance(assistMembers.begin(), it); + auto const groupSizes = CalculateBalancedGroupSizes(assistMembers.size(), valid.size()); + size_t const valkyrIndex = GetAssignedValkyrIndex(myIndex, groupSizes); + + if (valkyrIndex >= valid.size()) + return false; + + Unit* myValkyr = valid[valkyrIndex]; + context->GetValue("rti")->Set(GetRTIValueForValkyr(valkyrIndex)); + + Attack(myValkyr); + + Difficulty const diff = bot->GetRaidDifficulty(); + if (sPlayerbotAIConfig.EnableICCBuffs && IsHeroicLk(diff) && + !myValkyr->HasAura(SPELL_HAMMER_OF_JUSTICE)) + bot->AddAura(SPELL_HAMMER_OF_JUSTICE, myValkyr); + + ApplyCCToValkyr(myValkyr); + + return true; +} + +std::pair IccLichKingAddsAction::DefileAwareStep(float tx, float ty, + std::vector const& defiles, + Difficulty diff) +{ + float const px = bot->GetPositionX(); + float const py = bot->GetPositionY(); + float const ddx = tx - px; + float const ddy = ty - py; + float const fullLen = std::hypot(ddx, ddy); + if (fullLen < 0.01f) + return {tx, ty}; + + static constexpr float STEP_DISTANCE = 10.0f; + static constexpr float MARGIN = 2.0f; + static constexpr int SAMPLES = 5; + + auto PathSafe = [&](float ex, float ey) -> bool + { + for (int s = 1; s <= SAMPLES; ++s) + { + float const t = float(s) / float(SAMPLES); + float const sx = px + (ex - px) * t; + float const sy = py + (ey - py) * t; + for (Unit const* defile : defiles) + { + float const radius = GetDefileEffectiveRadius(defile, diff); + float const d = std::hypot(sx - defile->GetPositionX(), sy - defile->GetPositionY()); + if (d < radius + MARGIN) + return false; + } + } + return true; + }; + + float const stepLen = std::min(STEP_DISTANCE, fullLen); + float const baseAngle = std::atan2(ddy, ddx); + + for (int offset = 0; offset <= 8; ++offset) + { + for (int dir : {-1, 1}) + { + if (offset == 0 && dir > 0) + continue; + float const angle = baseAngle + dir * offset * float(M_PI) / 8.0f; + float const ex = px + stepLen * std::cos(angle); + float const ey = py + stepLen * std::sin(angle); + if (PathSafe(ex, ey)) + return {ex, ey}; + } + } + + return {tx, ty}; +} + +bool IccLichKingAddsAction::HandleVileSpiritMechanics() +{ + static constexpr float ARRIVE_TOLERANCE = 4.0f; + + // Defile target: let HandleDefileMechanics() handle movement + // (perpendicular run). Don't override with spirit chase or slot + // movement. + auto const defileIt = IcecrownHelpers::defileCast.find(bot->GetInstanceId()); + if (defileIt != IcecrownHelpers::defileCast.end()) + { + auto const& defileInfo = defileIt->second; + if (!defileInfo.targetGuid.IsEmpty() && getMSTimeDiff(defileInfo.castTime, getMSTime()) <= 3000 && defileInfo.targetGuid == bot->GetGUID()) + return false; + } + + GuidVector const& npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); + + uint32 spiritCount = 0; + std::vector spirits; + std::vector defiles; + + for (ObjectGuid const& guid : npcs) + { + Unit* unit = botAI->GetUnit(guid); + if (!unit || !unit->IsAlive()) + continue; + + if (IsLkVileSpirit(unit)) + { + ++spiritCount; + spirits.push_back(unit); + } + else if (unit->GetEntry() == DEFILE_NPC_ID) + { + defiles.push_back(unit); + } + } + + // Shared raid-wide slot choice. All bots converge on the same position so + // they stay stacked. Reset when no spirits are alive. Keyed per-instance to + // avoid cross-instance pollution when multiple ICCs run simultaneously. + static std::map s_sharedSlotByInstance; + auto sharedSlotIt = s_sharedSlotByInstance.find(bot->GetInstanceId()); + if (sharedSlotIt == s_sharedSlotByInstance.end()) + sharedSlotIt = s_sharedSlotByInstance.emplace(bot->GetInstanceId(), -1).first; + int& sharedSlot = sharedSlotIt->second; + + if (spiritCount == 0) + { + sharedSlot = -1; + return false; + } + + Difficulty const diff = bot->GetRaidDifficulty(); + auto IsSlotSafeFromDefile = [&](Position const& slot) -> bool + { + static constexpr float SAFETY_MARGIN = 2.0f; + for (Unit const* defile : defiles) + { + float const radius = GetDefileEffectiveRadius(defile, diff); + float const d = + std::hypot(slot.GetPositionX() - defile->GetPositionX(), slot.GetPositionY() - defile->GetPositionY()); + if (d < radius + SAFETY_MARGIN) + return false; + } + return true; + }; + + static constexpr float SPIRIT_NEAR_SLOT = 15.0f; + auto IsSlotSafeFromSpirits = [&](Position const& slot) -> bool + { + for (Unit const* spirit : spirits) + { + float const d = + std::hypot(slot.GetPositionX() - spirit->GetPositionX(), slot.GetPositionY() - spirit->GetPositionY()); + if (d < SPIRIT_NEAR_SLOT) + return false; + } + return true; + }; + + Position const slots[3] = { + ICC_LK_VILE_SPIRIT1_POSITION, + ICC_LK_VILE_SPIRIT2_POSITION, + ICC_LK_VILE_SPIRIT3_POSITION, + }; + + // Spirits beyond this radius from bot = previous wave still alive, block move + static constexpr float OLD_SPIRIT_RADIUS = 20.0f; + // Priority order: pos1 (0), pos3 (2), pos2 (1) + static constexpr int SLOT_PRIORITY[3] = {0, 2, 1}; + + auto HasOldSpirits = [&]() -> bool + { + for (Unit const* spirit : spirits) + { + float const d = std::hypot(bot->GetPositionX() - spirit->GetPositionX(), + bot->GetPositionY() - spirit->GetPositionY()); + if (d > OLD_SPIRIT_RADIUS) + return true; + } + return false; + }; + + if (sharedSlot >= 0 && sharedSlot < 3) + { + bool const defileHit = !IsSlotSafeFromDefile(slots[sharedSlot]); + bool const spiritHit = !IsSlotSafeFromSpirits(slots[sharedSlot]); + + if (defileHit) + { + // Defile is fatal — move regardless of old spirits + sharedSlot = -1; + } + else if (spiritHit) + { + // New spirits at current slot; wait if previous-wave spirits still alive + if (HasOldSpirits()) + return false; + + sharedSlot = -1; + } + else if (sharedSlot == 1) + { + // On fallback slot (pos2); upgrade to pos1 or pos3 if now safe + for (int pri : SLOT_PRIORITY) + { + if (pri == 1) + break; + if (IsSlotSafeFromDefile(slots[pri]) && IsSlotSafeFromSpirits(slots[pri])) + { + sharedSlot = pri; + break; + } + } + } + } + + if (sharedSlot < 0) + { + for (int pri : SLOT_PRIORITY) + { + if (!IsSlotSafeFromDefile(slots[pri])) + continue; + if (!IsSlotSafeFromSpirits(slots[pri])) + continue; + sharedSlot = pri; + break; + } + + if (sharedSlot < 0) + return false; + } + + if (sharedSlot < 0 || sharedSlot >= 3) + return false; + + int const chosen = sharedSlot; + Position const& slotPos = slots[chosen]; + + // Assist tank: stacks with the raid at the chosen vile slot, but allowed + // a 40y leash to intercept the spirit nearest the slot. Keeps nitro boost + // for chase speed. + if (botAI->IsAssistTank(bot)) + { + if (!bot->HasAura(SPELL_NITRO_BOOSTS)) + bot->AddAura(SPELL_NITRO_BOOSTS, bot); + if (!bot->HasAura(SPELL_PAIN_SUPPRESION)) + bot->AddAura(SPELL_PAIN_SUPPRESION, bot); + if (!bot->HasAura(SPELL_AGEIS_OF_DALARAN)) + bot->AddAura(SPELL_AGEIS_OF_DALARAN, bot); + + static constexpr float LEASH_RADIUS = 40.0f; + + float const anchorX = slotPos.GetPositionX(); + float const anchorY = slotPos.GetPositionY(); + + // Find spirit closest to the slot (not to bot) — that's the spirit + // most likely to reach the raid stack first. + Unit* chaseTarget = nullptr; + float chaseTargetDist = std::numeric_limits::max(); + for (Unit* spirit : spirits) + { + float const d = std::hypot(spirit->GetPositionX() - anchorX, spirit->GetPositionY() - anchorY); + if (d < chaseTargetDist) + { + chaseTargetDist = d; + chaseTarget = spirit; + } + } + + float ax = anchorX; + float ay = anchorY; + if (chaseTarget) + { + float tx = chaseTarget->GetPositionX(); + float ty = chaseTarget->GetPositionY(); + float const dxc = tx - anchorX; + float const dyc = ty - anchorY; + float const lenc = std::hypot(dxc, dyc); + if (lenc > LEASH_RADIUS) + { + // Spirit too far from slot — clamp chase point to leash radius + tx = anchorX + dxc * LEASH_RADIUS / lenc; + ty = anchorY + dyc * LEASH_RADIUS / lenc; + } + ax = tx; + ay = ty; + } + + float az = slotPos.GetPositionZ(); + + float const distToAnchor = std::hypot(bot->GetPositionX() - ax, bot->GetPositionY() - ay); + if (distToAnchor <= ARRIVE_TOLERANCE) + return false; + + auto const [sx, sy] = DefileAwareStep(ax, ay, defiles, diff); + float sz = az; + bot->UpdateAllowedPositionZ(sx, sy, sz); + + return MoveTo(bot->GetMapId(), sx, sy, sz, false, false, false, true, MovementPriority::MOVEMENT_FORCED, true, + false); + } + + // Hunter trap duty: only the lowest-GUID alive bot hunter handles it so + // multiple hunters don't pile up at center. Real-player hunters are skipped + // — they may not know the strategy, so a bot owns the role. + bool isDesignatedHunter = false; + if (bot->getClass() == CLASS_HUNTER) + { + Group* hunterGroup = bot->GetGroup(); + if (hunterGroup) + { + ObjectGuid bestGuid; + for (GroupReference* ref = hunterGroup->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + if (!member || !member->IsAlive()) + continue; + if (member->getClass() != CLASS_HUNTER) + continue; + if (!GET_PLAYERBOT_AI(member)) + continue; + + if (bestGuid.IsEmpty() || member->GetGUID() < bestGuid) + bestGuid = member->GetGUID(); + } + isDesignatedHunter = (!bestGuid.IsEmpty() && bot->GetGUID() == bestGuid); + } + } + + // Skip the spirit-flee logic entirely if boss is casting Harvest Soul(s). + // The harvested player must stay alive — flee movement breaks range for + // healers and gets the soul victim killed, buffing LK and wiping raid. + Unit* Boss = AI_VALUE2(Unit*, "find target", "the lich king"); + bool const bossCastingHarvest = Boss && Boss->HasUnitState(UNIT_STATE_CASTING) && + (Boss->FindCurrentSpellBySpellId(SPELL_HARVEST_SOUL_LK) || + Boss->FindCurrentSpellBySpellId(SPELL_HARVEST_SOULS_LK_25) || + Boss->FindCurrentSpellBySpellId(SPELL_HARVEST_SOULS_LK_H1) || + Boss->FindCurrentSpellBySpellId(SPELL_HARVEST_SOULS_LK_H2) || + Boss->FindCurrentSpellBySpellId(SPELL_HARVEST_SOULS_LK_H3)); + + if (!botAI->IsTank(bot) && !bossCastingHarvest) + { + // Flee to MT if a spirit is targeting this bot OR is within FLEE_RANGE. + // Either condition is enough — proximity catches spirits that haven't + // committed a target yet, targeting catches faraway chasers. + static constexpr float FLEE_RANGE = 15.0f; + + Unit* chaser = nullptr; + for (Unit* spirit : spirits) + { + bool const isTargetingBot = spirit->GetVictim() && + spirit->GetVictim()->GetGUID() == bot->GetGUID(); + bool const isClose = bot->GetDistance2d(spirit) < FLEE_RANGE; + if (isTargetingBot || isClose) + { + chaser = spirit; + break; + } + } + + if (chaser) + { + // Flee toward main tank in 10y increments, leash ignored + Unit* mainTank = AI_VALUE(Unit*, "main tank"); + if (mainTank && mainTank->IsAlive()) + { + auto const [fx, fy] = DefileAwareStep(mainTank->GetPositionX(), mainTank->GetPositionY(), defiles, diff); + float fz = slotPos.GetPositionZ(); + bot->UpdateAllowedPositionZ(fx, fy, fz); + return MoveTo(bot->GetMapId(), fx, fy, fz, false, false, false, true, MovementPriority::MOVEMENT_FORCED, + true, false); + } + // No MT — fall through to leash logic + } + } + + // Designated hunter: anchored at platform center with 30y leash. Drops + // Frost Trap to slow spirits. If a spirit is targeting the hunter, flee + // toward main tank in 10y steps (leash ignored, trap not dropped). + if (isDesignatedHunter) + { + static constexpr float HUNTER_CENTER_X = 503.62f; + static constexpr float HUNTER_CENTER_Y = -2124.73f; + static constexpr float HUNTER_LEASH = 4.0f; + static constexpr float HUNTER_DROP_TOLERANCE = 5.0f; + + float const dxh = bot->GetPositionX() - HUNTER_CENTER_X; + float const dyh = bot->GetPositionY() - HUNTER_CENTER_Y; + float const distToCenter = std::hypot(dxh, dyh); + + if (distToCenter > HUNTER_LEASH) + { + auto const [hx, hy] = DefileAwareStep(HUNTER_CENTER_X, HUNTER_CENTER_Y, defiles, diff); + float hz = slotPos.GetPositionZ(); + bot->UpdateAllowedPositionZ(hx, hy, hz); + return MoveTo(bot->GetMapId(), hx, hy, hz, false, false, false, true, + MovementPriority::MOVEMENT_FORCED, true, false); + } + + if (distToCenter <= HUNTER_DROP_TOLERANCE && !bot->HasSpellCooldown(SPELL_FROST_TRAP1)) + { + if (botAI->CastSpell("frost trap", bot)) + return true; + } + + return false; + } + + // Healers may stay up to 20y from the slot so they can heal the assist tank + float const arriveTol = botAI->IsHeal(bot) ? 20.0f : ARRIVE_TOLERANCE; + + float const tx = slotPos.GetPositionX(); + float const ty = slotPos.GetPositionY(); + float const distToSlot = std::hypot(bot->GetPositionX() - tx, bot->GetPositionY() - ty); + if (distToSlot <= arriveTol) + return false; + + auto const [sx, sy] = DefileAwareStep(tx, ty, defiles, diff); + float sz = slotPos.GetPositionZ(); + bot->UpdateAllowedPositionZ(sx, sy, sz); + + return MoveTo(bot->GetMapId(), sx, sy, sz, false, false, false, true, + MovementPriority::MOVEMENT_FORCED, true, false); +} + +bool IccLichKingAddsAction::HandleIceSphereMechanics() +{ + if (!botAI->IsRangedDps(bot)) + return false; + + Group* group = bot->GetGroup(); + if (!group) + return false; + + static constexpr uint8 SPHERE_ICON = 1; // Diamond + + GuidVector const& npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); + + Unit* currentMark = botAI->GetUnit(group->GetTargetIcon(SPHERE_ICON)); + bool const sphereMarked = currentMark && currentMark->IsAlive() && + IsIceSphere(currentMark->GetEntry()); + + if (!sphereMarked) + { + Unit* nearestSphere = nullptr; + float nearestDist = std::numeric_limits::max(); + + for (ObjectGuid const& guid : npcs) + { + Unit* unit = botAI->GetUnit(guid); + if (!unit || !unit->IsAlive() || !IsIceSphere(unit->GetEntry())) + continue; + + float const dist = bot->GetDistance(unit); + if (dist < nearestDist) + { + nearestDist = dist; + nearestSphere = unit; + } + } + + if (nearestSphere) + group->SetTargetIcon(SPHERE_ICON, bot->GetGUID(), nearestSphere->GetGUID()); + else + { + if (!group->GetTargetIcon(SPHERE_ICON).IsEmpty()) + group->SetTargetIcon(SPHERE_ICON, bot->GetGUID(), ObjectGuid::Empty); + return false; + } + + currentMark = botAI->GetUnit(group->GetTargetIcon(SPHERE_ICON)); + } + + if (currentMark && currentMark->IsAlive()) + { + bot->SetTarget(currentMark->GetGUID()); + bot->SetFacingToObject(currentMark); + Attack(currentMark); + return true; + } + + return false; +} + +bool IccLichKingAddsAction::IsValkyr(Unit* unit) +{ + return IsLkValkyr(unit); +} + +std::vector IccLichKingAddsAction::CalculateBalancedGroupSizes(size_t totalAssist, + size_t numValkyrs) +{ + std::vector groupSizes(numValkyrs, 0); + if (numValkyrs == 0) + return groupSizes; + + size_t const baseSize = totalAssist / numValkyrs; + size_t const remainder = totalAssist % numValkyrs; + + for (size_t i = 0; i < numValkyrs; ++i) + { + groupSizes[i] = baseSize; + if (i < remainder) + ++groupSizes[i]; + } + + return groupSizes; +} + +size_t IccLichKingAddsAction::GetAssignedValkyrIndex(size_t assistIndex, + std::vector const& groupSizes) +{ + size_t cursor = 0; + for (size_t valkyrIndex = 0; valkyrIndex < groupSizes.size(); ++valkyrIndex) + { + if (assistIndex < cursor + groupSizes[valkyrIndex]) + return valkyrIndex; + cursor += groupSizes[valkyrIndex]; + } + + return 0; // fallback +} + +std::string IccLichKingAddsAction::GetRTIValueForValkyr(size_t valkyrIndex) +{ + switch (valkyrIndex) + { + case 0: + return "skull"; + case 1: + return "cross"; + case 2: + return "star"; + default: + return "skull"; + } +} + +bool IccLichKingAddsAction::ApplyCCToValkyr(Unit* valkyr) +{ + switch (bot->getClass()) + { + case CLASS_MAGE: + if (!botAI->HasAura("Deep Freeze", valkyr) && botAI->CanCastSpell("Deep Freeze", valkyr)) + return botAI->CastSpell("Deep Freeze", valkyr); + if (!botAI->HasAura("Frost Nova", valkyr) && botAI->CanCastSpell("Frost Nova", valkyr)) + return botAI->CastSpell("Frost Nova", valkyr); + if (!botAI->HasAura("Cone of Cold", valkyr) && botAI->CanCastSpell("Cone of Cold", valkyr)) + return botAI->CastSpell("Cone of Cold", valkyr); + if (!botAI->HasAura("Frostbolt", valkyr) && botAI->CanCastSpell("Frostbolt", valkyr)) + return botAI->CastSpell("Frostbolt", valkyr); + if (!botAI->HasAura("Slow", valkyr) && botAI->CanCastSpell("Slow", valkyr)) + return botAI->CastSpell("Slow", valkyr); + break; + case CLASS_DRUID: + if (!botAI->HasAura("Bash", valkyr) && botAI->CanCastSpell("Bash", valkyr)) + return botAI->CastSpell("Bash", valkyr); + if (!botAI->HasAura("Maim", valkyr) && botAI->CanCastSpell("Maim", valkyr)) + return botAI->CastSpell("Maim", valkyr); + break; + case CLASS_PALADIN: + if (!botAI->HasAura("Hammer of Justice", valkyr) && botAI->CanCastSpell("Hammer of Justice", valkyr)) + return botAI->CastSpell("Hammer of Justice", valkyr); + break; + case CLASS_WARRIOR: + if (!botAI->HasAura("Concussion Blow", valkyr) && botAI->CanCastSpell("Concussion Blow", valkyr)) + return botAI->CastSpell("Concussion Blow", valkyr); + if (!botAI->HasAura("Shockwave", valkyr) && botAI->CanCastSpell("Shockwave", valkyr)) + return botAI->CastSpell("Shockwave", valkyr); + if (!botAI->HasAura("Intercept", valkyr) && botAI->CanCastSpell("Intercept", valkyr)) + return botAI->CastSpell("Intercept", valkyr); + if (!botAI->HasAura("Charge", valkyr) && botAI->CanCastSpell("Charge", valkyr)) + return botAI->CastSpell("Charge", valkyr); + if (!botAI->HasAura("Hamstring", valkyr) && botAI->CanCastSpell("Hamstring", valkyr)) + return botAI->CastSpell("Hamstring", valkyr); + if (!botAI->HasAura("Piercing Howl", valkyr) && botAI->CanCastSpell("Piercing Howl", valkyr)) + return botAI->CastSpell("Piercing Howl", valkyr); + break; + case CLASS_HUNTER: + if (!botAI->HasAura("Intimidation", valkyr) && botAI->CanCastSpell("Intimidation", valkyr)) + return botAI->CastSpell("Intimidation", valkyr); + if (!botAI->HasAura("Concussive Shot", valkyr) && botAI->CanCastSpell("Concussive Shot", valkyr)) + return botAI->CastSpell("Concussive Shot", valkyr); + if (!botAI->HasAura("Wing Clip", valkyr) && botAI->CanCastSpell("Wing Clip", valkyr)) + return botAI->CastSpell("Wing Clip", valkyr); + if (!botAI->HasAura("Freezing Trap", valkyr) && botAI->CanCastSpell("Freezing Trap", valkyr)) + return botAI->CastSpell("Freezing Trap", valkyr); + break; + case CLASS_ROGUE: + if (!botAI->HasAura("Kidney Shot", valkyr) && botAI->CanCastSpell("Kidney Shot", valkyr)) + return botAI->CastSpell("Kidney Shot", valkyr); + if (!botAI->HasAura("Gouge", valkyr) && botAI->CanCastSpell("Gouge", valkyr)) + return botAI->CastSpell("Gouge", valkyr); + if (!botAI->HasAura("Blind", valkyr) && botAI->CanCastSpell("Blind", valkyr)) + return botAI->CastSpell("Blind", valkyr); + if (!botAI->HasAura("Deadly Throw", valkyr) && botAI->CanCastSpell("Deadly Throw", valkyr)) + return botAI->CastSpell("Deadly Throw", valkyr); + break; + case CLASS_SHAMAN: + if (!botAI->HasAura("Thunderstorm", valkyr) && botAI->CanCastSpell("Thunderstorm", valkyr)) + return botAI->CastSpell("Thunderstorm", valkyr); + if (!botAI->HasAura("Frost Shock", valkyr) && botAI->CanCastSpell("Frost Shock", valkyr)) + return botAI->CastSpell("Frost Shock", valkyr); + if (!botAI->HasAura("Earthbind Totem", valkyr) && botAI->CanCastSpell("Earthbind Totem", valkyr)) + return botAI->CastSpell("Earthbind Totem", valkyr); + break; + case CLASS_DEATH_KNIGHT: + if (!botAI->HasAura("Hungering Cold", valkyr) && botAI->CanCastSpell("Hungering Cold", valkyr)) + return botAI->CastSpell("Hungering Cold", valkyr); + if (!botAI->HasAura("Gnaw", valkyr) && botAI->CanCastSpell("Gnaw", valkyr)) + return botAI->CastSpell("Gnaw", valkyr); + if (!botAI->HasAura("Chains of Ice", valkyr) && botAI->CanCastSpell("Chains of Ice", valkyr)) + return botAI->CastSpell("Chains of Ice", valkyr); + if (!botAI->HasAura("Desecration", valkyr) && botAI->CanCastSpell("Desecration", valkyr)) + return botAI->CastSpell("Desecration", valkyr); + break; + case CLASS_PRIEST: + if (!botAI->HasAura("Psychic Horror", valkyr) && botAI->CanCastSpell("Psychic Horror", valkyr)) + return botAI->CastSpell("Psychic Horror", valkyr); + if (!botAI->HasAura("Mind Flay", valkyr) && botAI->CanCastSpell("Mind Flay", valkyr)) + return botAI->CastSpell("Mind Flay", valkyr); + break; + case CLASS_WARLOCK: + if (!botAI->HasAura("Shadowfury", valkyr) && botAI->CanCastSpell("Shadowfury", valkyr)) + return botAI->CastSpell("Shadowfury", valkyr); + if (!botAI->HasAura("Death Coil", valkyr) && botAI->CanCastSpell("Death Coil", valkyr)) + return botAI->CastSpell("Death Coil", valkyr); + if (!botAI->HasAura("Curse of Exhaustion", valkyr) && botAI->CanCastSpell("Curse of Exhaustion", valkyr)) + return botAI->CastSpell("Curse of Exhaustion", valkyr); + break; + default: + break; + } + + return false; +} diff --git a/src/Ai/Raid/ICC/Action/ICCActions_LM.cpp b/src/Ai/Raid/ICC/Action/ICCActions_LM.cpp new file mode 100644 index 00000000000..af1dff47d81 --- /dev/null +++ b/src/Ai/Raid/ICC/Action/ICCActions_LM.cpp @@ -0,0 +1,601 @@ +#include "GenericActions.h" +#include "GenericSpellActions.h" +#include "Multiplier.h" +#include "NearestNpcsValue.h" +#include "ObjectAccessor.h" +#include "Playerbots.h" +#include "ICCActions.h" +#include "ICCTriggers.h" +#include "RtiValue.h" +#include "Vehicle.h" + +// Lord Marrowgar + +// Group iteration filter for same-instance, alive, in-world members. +static bool IsValidLmMember(Player* member, Player* bot) +{ + if (!member || !member->IsInWorld() || !member->IsAlive()) + return false; + if (member->GetMapId() != bot->GetMapId()) + return false; + if (member->GetInstanceId() != bot->GetInstanceId()) + return false; + if (member->HasAura(SPELL_LM_IMPALED)) + return false; + return true; +} + +// Up to two lowest-GUID ranged bots in same instance, hunter-priority. +static std::vector PickBoneStormRangedTargets(Player* bot, PlayerbotAI* botAI) +{ + std::vector result; + + Group* group = bot->GetGroup(); + if (!group) + return result; + + std::vector ranged; + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + if (!IsValidLmMember(member, bot)) + continue; + if (botAI->IsTank(member)) + continue; + if (!botAI->IsRanged(member)) + continue; + + ranged.push_back(member); + } + + if (ranged.empty()) + return result; + + std::sort(ranged.begin(), ranged.end(), + [](Player const* a, Player const* b) { return a->GetGUID() < b->GetGUID(); }); + + for (Player* p : ranged) + { + if (p->getClass() != CLASS_HUNTER) + continue; + result.push_back(p); + if (result.size() == 2) + return result; + } + + for (Player* p : ranged) + { + if (p->getClass() == CLASS_HUNTER) + continue; + result.push_back(p); + if (result.size() == 2) + return result; + } + + return result; +} + +// True if any coldflame line sits within 10f of the anchor position. +// Used to widen the tank's "stay-put" tolerance so AvoidAoe can move them +// off the line without IccLmTankPositionAction dragging them back. +static bool ColdflameNearAnchor(Player* bot, Position const& anchor, float leash) +{ + std::list coldflames; + bot->GetCreatureListWithEntryInGrid(coldflames, NPC_COLDFLAME, 200.0f); + for (Creature* c : coldflames) + if (c->GetExactDist2d(anchor.GetPositionX(), anchor.GetPositionY()) < leash) + return true; + return false; +} + +bool IccLmTankPositionAction::Execute(Event /*event*/) +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "lord marrowgar"); + if (!boss) + return false; + + bool const isBossInBoneStorm = botAI->GetAura("Bone Storm", boss) != nullptr; + float const maxDistanceThreshold = 3.0f; + + if (isBossInBoneStorm) + { + std::vector const rangedTargets = PickBoneStormRangedTargets(bot, botAI); + if (std::find(rangedTargets.begin(), rangedTargets.end(), bot) != rangedTargets.end()) + { + float const anchorDist = bot->GetExactDist2d(ICC_LM_BONE_STORM_AT_POSITION.GetPositionX(), + ICC_LM_BONE_STORM_AT_POSITION.GetPositionY()); + + float const bossDist = bot->GetExactDist2d(boss); + float const proximityTrigger = 20.0f; + float const leash = 10.0f; + + // Boss too close or standing in coldflame: reposition within leash from anchor + bool const bossNear = bossDist < proximityTrigger; + bool const inColdflame = [&]() + { + std::list coldflames; + bot->GetCreatureListWithEntryInGrid(coldflames, NPC_COLDFLAME, 4.0f); + return !coldflames.empty(); + }(); + + if (bossNear || inColdflame) + { + // Try eight candidate offsets from the anchor at the leash radius; + // pick first one that is far from boss and clear of coldflames. + float bestX = bot->GetPositionX(); + float bestY = bot->GetPositionY(); + float bestScore = -1.0f; + bool found = false; + + for (int i = 0; i < 8; ++i) + { + float const angle = (float)i * (float)M_PI / 4.0f; + float const cx = ICC_LM_BONE_STORM_AT_POSITION.GetPositionX() + std::cos(angle) * leash; + float const cy = ICC_LM_BONE_STORM_AT_POSITION.GetPositionY() + std::sin(angle) * leash; + + float const dx = cx - boss->GetPositionX(); + float const dy = cy - boss->GetPositionY(); + float const distToBoss = std::sqrt(dx * dx + dy * dy); + + std::list coldflames; + bot->GetCreatureListWithEntryInGrid(coldflames, NPC_COLDFLAME, 200.0f); + bool hitColdflame = false; + for (Creature* c : coldflames) + { + if (c->GetExactDist2d(cx, cy) < 4.0f) + { + hitColdflame = true; + break; + } + } + if (hitColdflame) + continue; + + if (distToBoss > bestScore) + { + bestScore = distToBoss; + bestX = cx; + bestY = cy; + found = true; + } + } + + if (found) + return MoveTo(bot->GetMapId(), bestX, bestY, ICC_LM_BONE_STORM_AT_POSITION.GetPositionZ(), false, + false, false, false, MovementPriority::MOVEMENT_COMBAT); + } + + if (anchorDist > maxDistanceThreshold) + return MoveTo(bot->GetMapId(), ICC_LM_BONE_STORM_AT_POSITION.GetPositionX(), + ICC_LM_BONE_STORM_AT_POSITION.GetPositionY(), + ICC_LM_BONE_STORM_AT_POSITION.GetPositionZ(), false, false, false, false, + MovementPriority::MOVEMENT_COMBAT); + return true; + } + + float const tankLeash = + ColdflameNearAnchor(bot, ICC_LM_TANK_POSITION, 10.0f) ? 10.0f : maxDistanceThreshold; + + if (botAI->IsMainTank(bot)) + { + float const distance = + bot->GetExactDist2d(ICC_LM_TANK_POSITION.GetPositionX(), ICC_LM_TANK_POSITION.GetPositionY()); + if (distance > tankLeash) + return MoveTowardPosition(ICC_LM_TANK_POSITION, maxDistanceThreshold); + return false; + } + + if (botAI->IsAssistTank(bot)) + { + float const distance = + bot->GetExactDist2d(ICC_LM_TANK_POSITION.GetPositionX(), ICC_LM_TANK_POSITION.GetPositionY()); + if (distance > tankLeash) + return MoveTo(bot->GetMapId(), ICC_LM_TANK_POSITION.GetPositionX(), + ICC_LM_TANK_POSITION.GetPositionY(), ICC_LM_TANK_POSITION.GetPositionZ(), false, false, + false, false, MovementPriority::MOVEMENT_COMBAT); + return false; + } + + // Non-tanks: if too far from mid position, move toward it + float const distance = + bot->GetExactDist2d(ICC_LM_MID_POSITION.GetPositionX(), ICC_LM_MID_POSITION.GetPositionY()); + if (distance > 35.0f) + return MoveTowardPosition(ICC_LM_MID_POSITION, 15.0f); + + return false; + } + + float const tankLeash = + ColdflameNearAnchor(bot, ICC_LM_TANK_POSITION, 10.0f) ? 10.0f : maxDistanceThreshold; + + if (botAI->HasAggro(boss) && botAI->IsMainTank(bot) && boss->GetVictim() == bot) + { + float const distance = + bot->GetExactDist2d(ICC_LM_TANK_POSITION.GetPositionX(), ICC_LM_TANK_POSITION.GetPositionY()); + + if (distance > tankLeash) + return MoveTowardPosition(ICC_LM_TANK_POSITION, maxDistanceThreshold); + } + + if (botAI->IsAssistTank(bot)) + { + float const distance = + bot->GetExactDist2d(ICC_LM_TANK_POSITION.GetPositionX(), ICC_LM_TANK_POSITION.GetPositionY()); + + if (distance > tankLeash) + return MoveTo(bot->GetMapId(), ICC_LM_TANK_POSITION.GetPositionX(), ICC_LM_TANK_POSITION.GetPositionY(), + ICC_LM_TANK_POSITION.GetPositionZ(), false, false, false, false, + MovementPriority::MOVEMENT_COMBAT); + + if (distance < maxDistanceThreshold) + { + bot->SetFacingToObject(boss); + return true; + } + } + + return false; +} + +bool IccLmTankPositionAction::MoveTowardPosition(Position const& position, float incrementSize) +{ + float const dirX = position.GetPositionX() - bot->GetPositionX(); + float const dirY = position.GetPositionY() - bot->GetPositionY(); + float const length = std::sqrt(dirX * dirX + dirY * dirY); + + float const normalizedDirX = dirX / length; + float const normalizedDirY = dirY / length; + + float const moveX = bot->GetPositionX() + normalizedDirX * incrementSize; + float const moveY = bot->GetPositionY() + normalizedDirY * incrementSize; + + return MoveTo(bot->GetMapId(), moveX, moveY, bot->GetPositionZ(), false, false, false, false, + MovementPriority::MOVEMENT_COMBAT); +} + +bool IccSpikeAction::Execute(Event /*event*/) +{ + if (bot->HasAura(SPELL_LM_IMPALED)) + return false; + + Unit* boss = AI_VALUE2(Unit*, "find target", "lord marrowgar"); + if (!boss) + return false; + + bool const isBossInBoneStorm = botAI->GetAura("Bone Storm", boss) != nullptr; + std::vector const spikes = FindAliveSpikes(); + + if (!spikes.empty()) + { + HandleSpikeMarking(spikes, boss); + return HandleSpikeAssignment(spikes, boss); + } + + // No spikes alive -- skull on boss, clear cross, all bots on skull + HandleNoSpikesMarking(boss); + + // Melee non-tanks in front of boss should reposition + if (boss->isInFront(bot) && !botAI->IsTank(bot) && !isBossInBoneStorm) + { + Position const safePosition = {-390.6757f, 2230.5283f, 0.0f}; + float const distance = bot->GetExactDist2d(safePosition.GetPositionX(), safePosition.GetPositionY()); + if (distance > 3.0f) + return MoveTowardPosition(safePosition, 3.0f); + } + + return false; +} + +std::vector IccSpikeAction::FindAliveSpikes() +{ + // All difficulty variants — AzerothCore spawns a different entry per difficulty mode. + // Bonespike NPCs have UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_IMMUNE_TO_PC so they + // never appear in "possible targets no los". Use a direct grid search instead. + static uint32 const spikeEntries[] = { + NPC_SPIKE1, NPC_SPIKE1_10H, NPC_SPIKE1_25N, NPC_SPIKE1_25H, + NPC_SPIKE2, NPC_SPIKE2_10H, NPC_SPIKE2_25N, NPC_SPIKE2_25H, + NPC_SPIKE3, NPC_SPIKE3_10H, NPC_SPIKE3_25N, NPC_SPIKE3_25H + }; + + std::vector spikes; + for (uint32 const entry : spikeEntries) + { + std::list found; + bot->GetCreatureListWithEntryInGrid(found, entry, 200.0f); + for (Creature* c : found) + { + if (c && c->IsAlive()) + spikes.push_back(c); + } + } + + std::sort(spikes.begin(), spikes.end(), [](Unit const* a, Unit const* b) { return a->GetGUID() < b->GetGUID(); }); + return spikes; +} + +bool IccSpikeAction::HandleSpikeMarking(std::vector const& spikes, Unit* boss) +{ + Group* group = bot->GetGroup(); + if (!group) + return false; + + static uint8 const Icons[] = {7, 6, 0}; // Skull, Cross, Star + + std::vector aliveSpikeGuids; + aliveSpikeGuids.reserve(spikes.size()); + for (Unit* spike : spikes) + aliveSpikeGuids.push_back(spike->GetGUID()); + + for (uint8 const iconIdx : Icons) + { + ObjectGuid const iconGuid = group->GetTargetIcon(iconIdx); + if (iconGuid.IsEmpty()) + continue; + if (std::find(aliveSpikeGuids.begin(), aliveSpikeGuids.end(), iconGuid) != aliveSpikeGuids.end()) + continue; + + Unit* marked = botAI->GetUnit(iconGuid); + if (marked && !marked->IsAlive()) + group->SetTargetIcon(iconIdx, bot->GetGUID(), ObjectGuid::Empty); + } + + // Check if the only spike left is a tank spike + Player* firstSpikeVictim = spikes.size() == 1 ? GetSpikeVictim(spikes[0]) : nullptr; + bool const onlyTankSpike = firstSpikeVictim && botAI->IsTank(firstSpikeVictim); + + if (onlyTankSpike) + { + // Skull on spike, Cross on boss + if (group->GetTargetIcon(7) != spikes[0]->GetGUID()) + group->SetTargetIcon(7, bot->GetGUID(), spikes[0]->GetGUID()); + + if (group->GetTargetIcon(6) != boss->GetGUID()) + group->SetTargetIcon(6, bot->GetGUID(), boss->GetGUID()); + + if (!group->GetTargetIcon(0).IsEmpty()) + group->SetTargetIcon(0, bot->GetGUID(), ObjectGuid::Empty); + + return true; + } + + // Clear icon slots beyond the current spike count + for (size_t i = spikes.size(); i < sizeof(Icons); ++i) + { + if (!group->GetTargetIcon(Icons[i]).IsEmpty()) + group->SetTargetIcon(Icons[i], bot->GetGUID(), ObjectGuid::Empty); + } + + // Assign Skull/Cross/Star to each alive spike + for (size_t i = 0; i < spikes.size() && i < sizeof(Icons); ++i) + { + uint8 const iconIdx = Icons[i]; + if (group->GetTargetIcon(iconIdx) != spikes[i]->GetGUID()) + group->SetTargetIcon(iconIdx, bot->GetGUID(), spikes[i]->GetGUID()); + } + + return true; +} + +bool IccSpikeAction::HandleNoSpikesMarking(Unit* boss) +{ + Group* group = bot->GetGroup(); + if (!group) + return false; + + // Clear cross and star + for (uint8 const iconIdx : {uint8(6), uint8(0)}) + { + if (!group->GetTargetIcon(iconIdx).IsEmpty()) + group->SetTargetIcon(iconIdx, bot->GetGUID(), ObjectGuid::Empty); + } + + // Skull on boss + if (group->GetTargetIcon(7) != boss->GetGUID()) + group->SetTargetIcon(7, bot->GetGUID(), boss->GetGUID()); + + // Per-bot context value -- every bot needs this for its own ChooseTarget. + context->GetValue("rti")->Set("skull"); + return true; +} + +bool IccSpikeAction::HandleSpikeAssignment(std::vector const& spikes, Unit* boss) +{ + Group* group = bot->GetGroup(); + if (!group) + return false; + + bool const isMelee = botAI->IsMelee(bot) && !botAI->IsTank(bot); + bool const isAssistTank = botAI->IsAssistTank(bot); + + auto isTankSpike = [&](Unit* spike) -> bool + { + Player* victim = GetSpikeVictim(spike); + return victim && botAI->IsTank(victim); + }; + + // Assist tank: only attack tank spike, ignore all others + if (isAssistTank) + { + for (Unit* spike : spikes) + { + if (isTankSpike(spike)) + { + Attack(spike); + return false; + } + } + return false; + } + + // Only tank spike left -- ranged go skull (spike), melee go cross (boss) + bool const onlyTankSpike = spikes.size() == 1 && isTankSpike(spikes[0]); + if (onlyTankSpike) + { + if (isMelee) + { + context->GetValue("rti")->Set("cross"); + Attack(boss); + } + else + { + context->GetValue("rti")->Set("skull"); + Attack(spikes[0]); + } + return false; + } + + // Melee DPS: pick closest safe spike within 20y, never tank spikes + if (isMelee) + { + Unit* bestSpike = nullptr; + float bestDist = 20.0f; + for (Unit* spike : spikes) + { + if (isTankSpike(spike)) + continue; + + if (boss->isInFront(spike, 7.0f)) + continue; + + if (IsSpikeInColdFlame(spike)) + continue; + + float const dist = bot->GetExactDist2d(spike); + if (dist < bestDist) + { + bestDist = dist; + bestSpike = spike; + } + } + + if (bestSpike) + Attack(bestSpike); + + return false; + } + + // Ranged / healers: balanced assignment across all spikes + std::vector rangedMembers; + for (GroupReference* itr = group->GetFirstMember(); itr; itr = itr->next()) + { + Player* member = itr->GetSource(); + if (!member || !member->IsAlive() || member->HasAura(SPELL_LM_IMPALED)) + continue; + + if (botAI->IsMainTank(member) || botAI->IsAssistTank(member)) + continue; + + if (botAI->IsMelee(member) && !botAI->IsTank(member)) + continue; + + rangedMembers.push_back(member); + } + + if (rangedMembers.empty()) + return false; + + std::sort(rangedMembers.begin(), rangedMembers.end(), + [](Player const* a, Player const* b) { return a->GetGUID() < b->GetGUID(); }); + + auto const it = std::find(rangedMembers.begin(), rangedMembers.end(), bot); + if (it == rangedMembers.end()) + return false; + + size_t const myIndex = std::distance(rangedMembers.begin(), it); + std::vector const groupSizes = CalculateBalancedGroupSizes(rangedMembers.size(), spikes.size()); + size_t const spikeIndex = GetAssignedSpikeIndex(myIndex, groupSizes); + + if (spikeIndex >= spikes.size()) + return false; + + Unit* mySpike = spikes[spikeIndex]; + context->GetValue("rti")->Set(GetRTIValueForSpike(spikeIndex)); + + Attack(mySpike); + return false; +} + +bool IccSpikeAction::MoveTowardPosition(Position const& position, float incrementSize) +{ + float const dirX = position.GetPositionX() - bot->GetPositionX(); + float const dirY = position.GetPositionY() - bot->GetPositionY(); + float const length = std::sqrt(dirX * dirX + dirY * dirY); + + float const normalizedDirX = dirX / length; + float const normalizedDirY = dirY / length; + + float const moveX = bot->GetPositionX() + normalizedDirX * incrementSize; + float const moveY = bot->GetPositionY() + normalizedDirY * incrementSize; + + return MoveTo(bot->GetMapId(), moveX, moveY, bot->GetPositionZ(), false, false, false, false, + MovementPriority::MOVEMENT_COMBAT); +} + +std::vector IccSpikeAction::CalculateBalancedGroupSizes(size_t totalMembers, size_t numSpikes) +{ + std::vector groupSizes(numSpikes, 0); + if (numSpikes == 0) + return groupSizes; + + size_t const baseSize = totalMembers / numSpikes; + size_t const remainder = totalMembers % numSpikes; + + for (size_t i = 0; i < numSpikes; ++i) + { + groupSizes[i] = baseSize; + if (i < remainder) + ++groupSizes[i]; + } + + return groupSizes; +} + +size_t IccSpikeAction::GetAssignedSpikeIndex(size_t memberIndex, std::vector const& groupSizes) +{ + size_t cursor = 0; + for (size_t spikeIndex = 0; spikeIndex < groupSizes.size(); ++spikeIndex) + { + if (memberIndex < cursor + groupSizes[spikeIndex]) + return spikeIndex; + cursor += groupSizes[spikeIndex]; + } + + return 0; +} + +std::string IccSpikeAction::GetRTIValueForSpike(size_t spikeIndex) +{ + switch (spikeIndex) + { + case 0: + return "skull"; + case 1: + return "cross"; + case 2: + return "star"; + default: + return "skull"; + } +} + +Player* IccSpikeAction::GetSpikeVictim(Unit* spike) +{ + // Spike holds player via vehicle; GetVictim() is unreliable (NPC not in combat) + if (Vehicle* veh = spike->GetVehicleKit()) + { + for (auto const& [seatId, seatInfo] : veh->Seats) + { + if (Unit* passenger = ObjectAccessor::GetUnit(*spike, seatInfo.Passenger.Guid)) + return passenger->ToPlayer(); + } + } + return nullptr; +} + +bool IccSpikeAction::IsSpikeInColdFlame(Unit* spike) +{ + float const checkRadius = 6.0f; + std::list coldflames; + spike->GetCreatureListWithEntryInGrid(coldflames, NPC_COLDFLAME, checkRadius); + return !coldflames.empty(); +} diff --git a/src/Ai/Raid/ICC/Action/ICCActions_PP.cpp b/src/Ai/Raid/ICC/Action/ICCActions_PP.cpp new file mode 100644 index 00000000000..f344248474a --- /dev/null +++ b/src/Ai/Raid/ICC/Action/ICCActions_PP.cpp @@ -0,0 +1,1733 @@ +#include "GenericActions.h" +#include "GenericSpellActions.h" +#include "Multiplier.h" +#include "NearestNpcsValue.h" +#include "ObjectAccessor.h" +#include "Playerbots.h" +#include "ICCActions.h" +#include "ICCTriggers.h" +#include "ICCScripts.h" +#include "RtiValue.h" +#include "Vehicle.h" +#include +#include +#include +#include + +namespace +{ + // Per-bot last flee direction during Gaseous Bloat. Used to prevent + // backtracking — once a bot commits to a direction, candidate angles + // pointing backward (negative dot product) are rejected so the cloud + // can't trap it in a back-and-forth loop. Cleared when the aura drops. + struct BloatDir { float x; float y; }; + std::unordered_map g_bloatLastDir; +} + +// Professor Putricide +bool IccPutricideMutatedPlagueAction::Execute(Event /*event*/) +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "professor putricide"); + if (!boss) + return false; + + if (!botAI->IsTank(bot)) + return false; + + if (bot->GetMotionMaster()->GetCurrentMovementGeneratorType() == FOLLOW_MOTION_TYPE) + { + bot->AttackStop(); + bot->InterruptNonMeleeSpells(true); + if (bot->GetTarget()) + bot->SetTarget(ObjectGuid::Empty); + return false; + } + + auto CastClassTaunt = [&](Unit* target) -> bool + { + if (!target || !target->IsAlive()) + return false; + + switch (bot->getClass()) + { + case CLASS_PALADIN: + { + bot->RemoveSpellCooldown(SPELL_TAUNT_PALADIN, true); + if (botAI->CastSpell("hand of reckoning", target)) + return true; + break; + } + case CLASS_DEATH_KNIGHT: + { + bot->RemoveSpellCooldown(SPELL_TAUNT_DK, true); + if (botAI->CastSpell("dark command", target)) + return true; + break; + } + case CLASS_DRUID: + { + bot->RemoveSpellCooldown(SPELL_TAUNT_DRUID, true); + if (botAI->CastSpell("growl", target)) + return true; + break; + } + case CLASS_WARRIOR: + { + bot->RemoveSpellCooldown(SPELL_TAUNT_WARRIOR, true); + if (botAI->CastSpell("taunt", target)) + return true; + break; + } + default: + break; + } + + if (botAI->CastSpell("shoot", target) || botAI->CastSpell("throw", target)) + return true; + + return false; + }; + + auto GetPlagueStacks = [&](Unit* unit) -> uint32 + { + if (!unit) + return 0; + + Aura* a = botAI->GetAura("mutated plague", unit, false, true); + return a ? a->GetStackAmount() : 0; + }; + + uint32 const myStacks = GetPlagueStacks(bot); + + bool shouldTaunt = false; + if (Group* group = bot->GetGroup()) + { + for (GroupReference* itr = group->GetFirstMember(); itr; itr = itr->next()) + { + Player* member = itr->GetSource(); + if (!member || member == bot || !member->IsAlive() || !member->IsInWorld()) + continue; + + if (!PlayerbotAI::IsTank(member)) + continue; + + if (myStacks < GetPlagueStacks(member)) + { + shouldTaunt = true; + break; + } + } + } + + if (shouldTaunt && boss->GetVictim() != bot) + CastClassTaunt(boss); + + return false; +} + +bool IccPutricideGrowingOozePuddleAction::Execute(Event /*event*/) +{ + if (botAI->IsMainTank(bot) && + bot->GetMotionMaster()->GetCurrentMovementGeneratorType() == FOLLOW_MOTION_TYPE) + { + bot->AttackStop(); + bot->InterruptNonMeleeSpells(true); + if (bot->GetTarget()) + bot->SetTarget(ObjectGuid::Empty); + if (Unit* master = botAI->GetMaster()) + Follow(master); + return true; + } + + // Phase 3: only MT avoids hazards. Non-MT bots stack on MT blindly so + // they don't scatter when a puddle drops on the stack. + if (!botAI->IsMainTank(bot)) + { + Unit* bossP3 = AI_VALUE2(Unit*, "find target", "professor putricide"); + if (bossP3 && bossP3->HealthBelowPct(35)) + return false; + } + + // Main tank rotation kite: when one or more active Growing Ooze Puddles + // sit close to the boss, walk the boss to a position safe from ALL of + // them. Tank picks an angle around the boss whose forward arc clears + // every nearby puddle. Boss turns to face the tank, rotating its frontal + // cone away from the puddle field. Stack bots line up behind the boss. + // Phase 3: skip kite (MT idles near boss). Flee logic below still runs so + // MT steps out of puddles. + if (botAI->IsMainTank(bot)) + { + Unit* boss = AI_VALUE2(Unit*, "find target", "professor putricide"); + if (boss && boss->IsAlive() && boss->GetVictim() == bot && !boss->HealthBelowPct(35)) + { + constexpr float puddleNearBossRange = 8.0f; + constexpr float puddleSafeRadius = 10.0f; + constexpr float bossReach = 4.0f; + constexpr int kiteAngleSteps = 24; + + std::vector nearPuddles; + GuidVector const npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); + for (auto const& g : npcs) + { + Unit* u = botAI->GetUnit(g); + if (!u || !u->IsAlive() || u->GetEntry() != NPC_GROWING_OOZE_PUDDLE) + continue; + if (u->GetExactDist2d(boss) <= puddleNearBossRange) + nearPuddles.push_back(u); + } + + if (!nearPuddles.empty()) + { + // Centroid of near puddles → seed direction is boss-from-centroid. + float cx = 0.0f, cy = 0.0f; + for (Unit* p : nearPuddles) + { + cx += p->GetPositionX(); + cy += p->GetPositionY(); + } + cx /= static_cast(nearPuddles.size()); + cy /= static_cast(nearPuddles.size()); + + float seedX = boss->GetPositionX() - cx; + float seedY = boss->GetPositionY() - cy; + float seedLen = std::sqrt(seedX * seedX + seedY * seedY); + float seedAngle = (seedLen > 0.01f) ? std::atan2(seedY, seedX) : 0.0f; + + float bossX = boss->GetPositionX(); + float bossY = boss->GetPositionY(); + float botZ = bot->GetPositionZ(); + + // Scan angles around the boss starting from the seed (away + // from centroid) and spiraling outward. Pick the first angle + // whose tank-stand point clears every near puddle. + float bestGoalX = 0.0f, bestGoalY = 0.0f; + float bestMinDist = -FLT_MAX; + bool found = false; + bool foundClean = false; + + for (int i = 0; i < kiteAngleSteps; ++i) + { + // Alternate +/- around seedAngle to prefer minimal rotation. + int sign = (i % 2 == 0) ? 1 : -1; + int step = (i + 1) / 2; + float angle = seedAngle + sign * step * + (2.0f * static_cast(M_PI) / kiteAngleSteps); + + float gx = bossX + std::cos(angle) * bossReach; + float gy = bossY + std::sin(angle) * bossReach; + + float minDist = FLT_MAX; + for (Unit* p : nearPuddles) + { + float dd = p->GetDistance2d(gx, gy); + if (dd < minDist) + minDist = dd; + } + + if (!bot->IsWithinLOS(gx, gy, botZ)) + continue; + + if (minDist >= puddleSafeRadius) + { + bestGoalX = gx; + bestGoalY = gy; + foundClean = true; + found = true; + break; + } + + if (minDist > bestMinDist) + { + bestMinDist = minDist; + bestGoalX = gx; + bestGoalY = gy; + found = true; + } + } + + if (found && bot->GetExactDist2d(bestGoalX, bestGoalY) > 1.0f) + { + return MoveTo(bot->GetMapId(), bestGoalX, bestGoalY, botZ, false, false, false, true, + MovementPriority::MOVEMENT_COMBAT); + } + + // No clean angle and already at best position: fall through + // to puddle-flee logic below so tank still avoids damage. + (void)foundClean; + } + } + } + + Unit* closestPuddle = FindClosestThreateningPuddle(); + if (!closestPuddle) + return false; + + Position movePosition = CalculateSafeMovePosition(closestPuddle); + return MoveTo(bot->GetMapId(), movePosition.GetPositionX(), movePosition.GetPositionY(), + movePosition.GetPositionZ(), false, false, false, false, MovementPriority::MOVEMENT_COMBAT); +} + +Unit* IccPutricideGrowingOozePuddleAction::FindClosestThreateningPuddle() +{ + constexpr float baseRadius = 2.0f; + constexpr float stackMultiplier = 0.8f; + constexpr float mainTankSafeDistance = 10.0f; + constexpr float minDistance = 0.1f; + + GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); + if (npcs.empty()) + return nullptr; + + // Phase 3: MT no longer kites, treat as regular bot for puddle avoidance. + Unit* boss = AI_VALUE2(Unit*, "find target", "professor putricide"); + bool const isMainTank = botAI->IsMainTank(bot) && !(boss && boss->HealthBelowPct(35)); + + Unit* closestPuddle = nullptr; + float closestDistance = FLT_MAX; + + for (auto const& npc : npcs) + { + Unit* unit = botAI->GetUnit(npc); + if (!unit || unit->GetEntry() != NPC_GROWING_OOZE_PUDDLE) + continue; + + float currentDistance = std::max(minDistance, bot->GetExactDist2d(unit)); + float safeDistance = isMainTank ? mainTankSafeDistance : baseRadius; + + if (!isMainTank) + { + if (Aura* grow = unit->GetAura(SPELL_GROW_AURA)) + safeDistance += (grow->GetStackAmount() * stackMultiplier); + } + + if (currentDistance < safeDistance && currentDistance < closestDistance) + { + closestDistance = currentDistance; + closestPuddle = unit; + } + } + + return closestPuddle; +} + +Position IccPutricideGrowingOozePuddleAction::CalculateSafeMovePosition(Unit* closestPuddle) +{ + constexpr float baseRadius = 2.0f; + constexpr float stackMultiplier = 0.8f; + constexpr float mainTankSafeDistance = 10.0f; + constexpr float bufferDistance = 2.0f; + constexpr float minDistance = 0.1f; + constexpr int numAnglesToTest = 8; + constexpr float tankShoveDistance = 6.0f; + + Unit* boss = AI_VALUE2(Unit*, "find target", "professor putricide"); + bool const isMainTank = botAI->IsMainTank(bot) && !(boss && boss->HealthBelowPct(35)); + bool const isP3Tank = botAI->IsMainTank(bot) && boss && boss->HealthBelowPct(35); + + float botX = bot->GetPositionX(); + float botY = bot->GetPositionY(); + float botZ = bot->GetPositionZ(); + + float gateDx = 0.0f, gateDy = 0.0f, gateLen = 0.0f; + if (isP3Tank) + { + gateDx = ICC_PUTRICIDE_GATE_POSITION.GetPositionX() - botX; + gateDy = ICC_PUTRICIDE_GATE_POSITION.GetPositionY() - botY; + gateLen = std::sqrt(gateDx * gateDx + gateDy * gateDy); + if (gateLen > 0.01f) + { + gateDx /= gateLen; + gateDy /= gateLen; + } + } + + float currentDistance = std::max(minDistance, bot->GetExactDist2d(closestPuddle)); + bool const useTankSafeDistance = isMainTank || isP3Tank; + float safeDistance = useTankSafeDistance ? mainTankSafeDistance : baseRadius; + if (!useTankSafeDistance) + { + if (Aura* grow = closestPuddle->GetAura(SPELL_GROW_AURA)) + safeDistance += (grow->GetStackAmount() * stackMultiplier); + } + + float dx = botX - closestPuddle->GetPositionX(); + float dy = botY - closestPuddle->GetPositionY(); + float dist = std::max(minDistance, std::sqrt(dx * dx + dy * dy)); + + if (dist < minDistance * 2.0f) + { + float randomAngle = static_cast(rand()) / static_cast(RAND_MAX) * 2.0f * static_cast(M_PI); + dx = std::cos(randomAngle); + dy = std::sin(randomAngle); + } + else + { + dx /= dist; + dy /= dist; + } + + float moveDistance = safeDistance - currentDistance + bufferDistance; + + // If the bot is already inside the puddle, anchor candidates on the + // puddle's safe-radius circle instead of rotating around the bot's + // current (unsafe) position. Otherwise a rotated move can land deeper + // into the puddle. Outward radial from puddle center is always safe. + bool const insidePuddle = currentDistance < safeDistance; + + for (int i = 0; i < numAnglesToTest; ++i) + { + float angle = (2.0f * static_cast(M_PI) * i) / numAnglesToTest; + float rotatedDx = dx * std::cos(angle) - dy * std::sin(angle); + float rotatedDy = dx * std::sin(angle) + dy * std::cos(angle); + + float testX, testY; + if (insidePuddle) + { + // Stand on safe ring around puddle, rotated by `angle` from the + // bot-relative radial. i=0 → straight outward from puddle center. + float radius = safeDistance + bufferDistance; + testX = closestPuddle->GetPositionX() + rotatedDx * radius; + testY = closestPuddle->GetPositionY() + rotatedDy * radius; + } + else + { + testX = botX + rotatedDx * moveDistance; + testY = botY + rotatedDy * moveDistance; + } + + // Reject any candidate that still sits inside the closest puddle. + float candDist = closestPuddle->GetDistance2d(testX, testY); + if (candDist < safeDistance) + continue; + + if (!IsPositionTooCloseToOtherPuddles(testX, testY, closestPuddle) && bot->IsWithinLOS(testX, testY, botZ)) + { + if (isP3Tank && gateLen > 0.01f) + { + float moveDx = testX - botX; + float moveDy = testY - botY; + if (moveDx * gateDx + moveDy * gateDy <= 0.0f) + continue; + } + + if (PathCrossesAnyPuddle(botX, botY, testX, testY, nullptr)) + continue; + + if (botAI->IsTank(bot)) + { + float awayDx = testX - closestPuddle->GetPositionX(); + float awayDy = testY - closestPuddle->GetPositionY(); + float awayDist = std::sqrt(awayDx * awayDx + awayDy * awayDy); + if (awayDist > 0.001f) + { + awayDx /= awayDist; + awayDy /= awayDist; + testX += awayDx * tankShoveDistance; + testY += awayDy * tankShoveDistance; + } + } + return Position(testX, testY, botZ); + } + } + + // Fallback: straight outward from puddle center on its safe ring. + float fallbackRadius = safeDistance + bufferDistance; + float fallbackX = insidePuddle ? (closestPuddle->GetPositionX() + dx * fallbackRadius) + : (botX + dx * moveDistance); + float fallbackY = insidePuddle ? (closestPuddle->GetPositionY() + dy * fallbackRadius) + : (botY + dy * moveDistance); + if (isP3Tank && gateLen > 0.01f) + { + float fbDx = fallbackX - botX; + float fbDy = fallbackY - botY; + if (fbDx * gateDx + fbDy * gateDy <= 0.0f) + { + fallbackX = closestPuddle->GetPositionX() + gateDx * fallbackRadius; + fallbackY = closestPuddle->GetPositionY() + gateDy * fallbackRadius; + } + } + if (botAI->IsTank(bot)) + { + float awayDx = fallbackX - closestPuddle->GetPositionX(); + float awayDy = fallbackY - closestPuddle->GetPositionY(); + float awayDist = std::sqrt(awayDx * awayDx + awayDy * awayDy); + if (awayDist > 0.001f) + { + awayDx /= awayDist; + awayDy /= awayDist; + fallbackX += awayDx * tankShoveDistance; + fallbackY += awayDy * tankShoveDistance; + } + } + return Position(fallbackX, fallbackY, botZ); +} + +bool IccPutricideGrowingOozePuddleAction::PathCrossesAnyPuddle(float fromX, float fromY, float toX, float toY, Unit* ignorePuddle) +{ + constexpr float baseRadius = 2.0f; + constexpr float stackMultiplier = 0.8f; + constexpr float mainTankSafeDistance = 10.0f; + + bool const useTankSafeDistance = botAI->IsMainTank(bot); + + float segDx = toX - fromX; + float segDy = toY - fromY; + float segLenSq = segDx * segDx + segDy * segDy; + if (segLenSq < 0.01f) + return false; + + GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); + for (auto const& npc : npcs) + { + Unit* unit = botAI->GetUnit(npc); + if (!unit || unit == ignorePuddle || unit->GetEntry() != NPC_GROWING_OOZE_PUDDLE) + continue; + + float radius = useTankSafeDistance ? mainTankSafeDistance : baseRadius; + if (!useTankSafeDistance) + { + if (Aura* grow = unit->GetAura(SPELL_GROW_AURA)) + radius += (grow->GetStackAmount() * stackMultiplier); + } + + float px = unit->GetPositionX(); + float py = unit->GetPositionY(); + float t = ((px - fromX) * segDx + (py - fromY) * segDy) / segLenSq; + if (t < 0.0f) t = 0.0f; + else if (t > 1.0f) t = 1.0f; + + float closestX = fromX + segDx * t; + float closestY = fromY + segDy * t; + float ddx = closestX - px; + float ddy = closestY - py; + if (ddx * ddx + ddy * ddy < radius * radius) + return true; + } + + return false; +} + +bool IccPutricideGrowingOozePuddleAction::IsPositionTooCloseToOtherPuddles(float x, float y, Unit* ignorePuddle) +{ + constexpr float baseRadius = 2.0f; + constexpr float stackMultiplier = 0.8f; + constexpr float mainTankSafeDistance = 10.0f; + + bool const isMainTank = botAI->IsMainTank(bot); + + GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); + for (auto const& npc : npcs) + { + Unit* unit = botAI->GetUnit(npc); + if (!unit || unit == ignorePuddle || unit->GetEntry() != NPC_GROWING_OOZE_PUDDLE) + continue; + + float safeDistance = isMainTank ? mainTankSafeDistance : baseRadius; + if (!isMainTank) + { + if (Aura* grow = unit->GetAura(SPELL_GROW_AURA)) + safeDistance += (grow->GetStackAmount() * stackMultiplier); + } + + float dist = unit->GetDistance2d(x, y); + if (dist < safeDistance) + return true; + } + + return false; +} + +bool IccPutricideVolatileOozeAction::Execute(Event /*event*/) +{ + constexpr float stackDistance = 7.0f; + + Unit* ooze = AI_VALUE2(Unit*, "find target", "volatile ooze"); + if (!ooze) + return false; + + Unit* boss = AI_VALUE2(Unit*, "find target", "professor putricide"); + if (!boss) + return false; + + if (botAI->IsMainTank(bot) && + bot->GetMotionMaster()->GetCurrentMovementGeneratorType() != FOLLOW_MOTION_TYPE && + bot->GetExactDist2d(ICC_PUTRICIDE_TANK_POSITION) > 20.0f && + !boss->HealthBelowPct(36) && boss->GetVictim() == bot) + return MoveTo(bot->GetMapId(), ICC_PUTRICIDE_TANK_POSITION.GetPositionX(), + ICC_PUTRICIDE_TANK_POSITION.GetPositionY(), ICC_PUTRICIDE_TANK_POSITION.GetPositionZ(), false, + false, false, true, MovementPriority::MOVEMENT_COMBAT, true, false); + + if (botAI->HasAura("Gaseous Bloat", bot) || botAI->HasAura("Unbound Plague", bot)) + return false; + + std::vector aliveOozes; + GuidVector const npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); + for (auto const& guid : npcs) + { + Unit* unit = botAI->GetUnit(guid); + if (unit && unit->IsAlive() && unit->GetEntry() == ooze->GetEntry()) + aliveOozes.push_back(unit); + } + + if (aliveOozes.size() > 1) + { + for (size_t i = 0; i < aliveOozes.size() - 1; ++i) + bot->Kill(bot, aliveOozes[i]); + } + + MarkOozeWithSkull(ooze); + + if (botAI->IsMelee(bot) && !botAI->IsMainTank(bot)) + { + if (bot->IsWithinMeleeRange(ooze)) + { + Attack(ooze); + return false; + } + + // If we're closer to the ooze than the targeted stack player is, + // running to the stack point just drags the ooze further. Go attack + // the ooze directly — the stack target will end up here anyway. + Unit* stackTarget = FindAuraTarget(); + if (stackTarget) + { + float botToOoze = bot->GetDistance2d(ooze); + float stackToOoze = stackTarget->GetDistance2d(ooze); + if (botToOoze < stackToOoze) + { + Attack(ooze); + return false; + } + } + } + + if (botAI->IsRanged(bot) || botAI->IsHeal(bot)) + { + constexpr float nearbyStackRange = 20.0f; + + Unit* stackTarget = FindAuraTarget(); + float distToStack = stackTarget ? bot->GetDistance2d(stackTarget) : 0.0f; + + // Only run to the stack target if it is reasonably close. Otherwise + // we'd chase across the room and barely attack. If it is far, check + // whether any other group member is already stacking near it — if + // yes, join them; if no, just stay put and attack from range. + bool shouldMoveToStack = false; + if (stackTarget && distToStack > stackDistance) + { + if (distToStack <= nearbyStackRange) + { + shouldMoveToStack = true; + } + else if (Group* group = bot->GetGroup()) + { + for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next()) + { + Player* member = itr->GetSource(); + if (!member || !member->IsAlive() || member == bot || member == stackTarget) + continue; + if (member->GetDistance2d(stackTarget) <= stackDistance && + bot->GetDistance2d(member) <= nearbyStackRange) + { + shouldMoveToStack = true; + break; + } + } + } + } + + if (shouldMoveToStack) + { + return MoveTo(bot->GetMapId(), stackTarget->GetPositionX(), stackTarget->GetPositionY(), + stackTarget->GetPositionZ(), false, false, false, false, MovementPriority::MOVEMENT_COMBAT); + } + + if (ooze && !botAI->IsHeal(bot)) + { + bot->SetTarget(ooze->GetGUID()); + bot->SetFacingToObject(ooze); + if (bot->IsWithinRange(ooze, 25.0f)) + { + Attack(ooze); + return false; + } + } + } + + return false; +} + +bool IccPutricideVolatileOozeAction::MarkOozeWithSkull(Unit* ooze) +{ + Group* group = bot->GetGroup(); + if (!group) + return false; + + constexpr uint8 skullIconId = 7; + ObjectGuid skullGuid = group->GetTargetIcon(skullIconId); + Unit* markedUnit = botAI->GetUnit(skullGuid); + + if (markedUnit && (!markedUnit->IsAlive() || (ooze && markedUnit != ooze))) + group->SetTargetIcon(skullIconId, bot->GetGUID(), ObjectGuid::Empty); + + if (ooze && ooze->IsAlive() && (!skullGuid || !markedUnit)) + group->SetTargetIcon(skullIconId, bot->GetGUID(), ooze->GetGUID()); + + return false; +} + +Unit* IccPutricideVolatileOozeAction::FindAuraTarget() +{ + Group* group = bot->GetGroup(); + if (!group) + return nullptr; + + for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next()) + { + Player* member = itr->GetSource(); + if (!member || !member->IsAlive() || member == bot) + continue; + + if (botAI->HasAura("Volatile Ooze Adhesive", member)) + return member; + } + + return nullptr; +} + +bool IccPutricideGasCloudAction::Execute(Event /*event*/) +{ + Unit* gasCloud = AI_VALUE2(Unit*, "find target", "gas cloud"); + if (!gasCloud) + return false; + + Unit* boss = AI_VALUE2(Unit*, "find target", "professor putricide"); + if (!boss) + return false; + + if (botAI->IsTank(bot) && + bot->GetMotionMaster()->GetCurrentMovementGeneratorType() != FOLLOW_MOTION_TYPE && + bot->GetExactDist2d(ICC_PUTRICIDE_TANK_POSITION) > 20.0f && !boss->HealthBelowPct(36) && + boss->GetVictim() == bot) + return MoveTo(bot->GetMapId(), ICC_PUTRICIDE_TANK_POSITION.GetPositionX(), + ICC_PUTRICIDE_TANK_POSITION.GetPositionY(), ICC_PUTRICIDE_TANK_POSITION.GetPositionZ(), false, + false, false, true, MovementPriority::MOVEMENT_COMBAT, true, false); + + if (botAI->IsMainTank(bot)) + return false; + + bool hasGaseousBloat = botAI->HasAura("Gaseous Bloat", bot); + Unit* volatileOoze = AI_VALUE2(Unit*, "find target", "volatile ooze"); + + std::vector aliveGasCloud; + GuidVector const npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); + for (auto const& guid : npcs) + { + Unit* unit = botAI->GetUnit(guid); + if (unit && unit->IsAlive() && unit->GetEntry() == gasCloud->GetEntry()) + aliveGasCloud.push_back(unit); + } + + if (aliveGasCloud.size() > 1) + { + for (size_t i = 0; i < aliveGasCloud.size() - 1; ++i) + bot->Kill(bot, aliveGasCloud[i]); + } + + if (!hasGaseousBloat && volatileOoze) + return false; + + if (hasGaseousBloat) + return HandleGaseousBloatMovement(gasCloud); + + return HandleGroupAuraSituation(gasCloud); +} + +bool IccPutricideGasCloudAction::HandleGaseousBloatMovement(Unit* gasCloud) +{ + if (!botAI->HasAura("Gaseous Bloat", bot)) + { + g_bloatLastDir.erase(bot->GetGUID().GetRawValue()); + return false; + } + + // Lookup prior committed flee direction (if any) so we can reject + // backtracking candidates this tick. + uint64 botKey = bot->GetGUID().GetRawValue(); + auto lastDirIt = g_bloatLastDir.find(botKey); + bool hasLastDir = lastDirIt != g_bloatLastDir.end(); + float lastDirX = hasLastDir ? lastDirIt->second.x : 0.0f; + float lastDirY = hasLastDir ? lastDirIt->second.y : 0.0f; + + auto isBacktrack = [&](float candDx, float candDy) -> bool + { + if (!hasLastDir) + return false; + // Require forward progress: dot must be positive (>0). This still + // allows up to ~89deg turns but blocks any move with a backward + // component. + return (candDx * lastDirX + candDy * lastDirY) <= 0.0f; + }; + + auto commitDir = [&](float fromX, float fromY, float toX, float toY) + { + float ddx = toX - fromX; + float ddy = toY - fromY; + float l = std::sqrt(ddx * ddx + ddy * ddy); + if (l < 0.01f) + return; + g_bloatLastDir[botKey] = { ddx / l, ddy / l }; + }; + + if (!bot->HasAura(SPELL_NITRO_BOOSTS)) + bot->AddAura(SPELL_NITRO_BOOSTS, bot); + + constexpr int numAngles = 32; + constexpr float gasBombSafeDist = 6.0f; + constexpr float movementIncrement = 5.0f; + constexpr float maxTestDist = 30.0f; + constexpr int checkDirs = 16; + constexpr float checkDist = 8.0f; + constexpr float minFreedomScore = 0.75f; + + Position botPos = bot->GetPosition(); + Position cloudPos = gasCloud->GetPosition(); + float cloudDist = gasCloud->GetExactDist2d(botPos); + + // Detect if bot is trapped near walls by testing 8 directions at 40yd. + // Threshold for "corner-ish" is intentionally low so we trigger the + // corner-escape branch before the bot fully parks against a wall. + constexpr int cornerCheckDirs = 8; + constexpr float cornerCheckDist = 40.0f; + int blockedAtStart = 0; + for (int i = 0; i < cornerCheckDirs; ++i) + { + float cA = (2.0f * static_cast(M_PI) * i) / cornerCheckDirs; + if (!bot->IsWithinLOS(botPos.GetPositionX() + std::cos(cA) * cornerCheckDist, + botPos.GetPositionY() + std::sin(cA) * cornerCheckDist, botPos.GetPositionZ())) + blockedAtStart++; + } + + // No distance early-exit: bloated bots must kite continuously because + // the gas cloud actively chases them. Stopping even briefly lets it catch up. + + GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); + std::vector gasBombs; + for (auto const& guid : npcs) + { + Unit* unit = botAI->GetUnit(guid); + if (unit && unit->IsAlive() && unit->GetEntry() == NPC_CHOKING_GAS_BOMB) + gasBombs.push_back(unit); + } + + float dx = botPos.GetPositionX() - cloudPos.GetPositionX(); + float dy = botPos.GetPositionY() - cloudPos.GetPositionY(); + float dist = std::max(0.1f, std::sqrt(dx * dx + dy * dy)); + dx /= dist; + dy /= dist; + + Position bestPos; + bool foundPath = false; + float bestScore = 0.0f; + + // Corner escape: test all 32 angles at a fixed medium distance with strict requirements. + // Trigger at >=3 blocked to catch edge-of-wall situations before parking. + if (blockedAtStart >= 3) + { + constexpr float escRadius = 10.0f; + for (int i = 0; i < 32; ++i) + { + float escAngle = (2.0f * static_cast(M_PI) * i) / 32; + float escX = botPos.GetPositionX() + std::cos(escAngle) * escRadius; + float escY = botPos.GetPositionY() + std::sin(escAngle) * escRadius; + float escZ = botPos.GetPositionZ(); + + if (!bot->IsWithinLOS(escX, escY, escZ)) + continue; + + if (isBacktrack(escX - botPos.GetPositionX(), escY - botPos.GetPositionY())) + continue; + + int freeDirs = 0; + for (int j = 0; j < checkDirs; ++j) + { + float cA = (2.0f * static_cast(M_PI) * j) / checkDirs; + if (bot->IsWithinLOS(escX + std::cos(cA) * checkDist, escY + std::sin(cA) * checkDist, escZ)) + freeDirs++; + } + + // Count long-range openness (25f rays) — reject positions near walls. + int farFreeDirs = 0; + for (int k = 0; k < 8; ++k) + { + float cA = (2.0f * static_cast(M_PI) * k) / 8; + if (bot->IsWithinLOS(escX + std::cos(cA) * 25.0f, escY + std::sin(cA) * 25.0f, escZ)) + farFreeDirs++; + } + + float escCloudDist = cloudPos.GetExactDist2d(escX, escY); + + // Must be moving away, open locally, and open at range. + if (escCloudDist > cloudDist && freeDirs >= 12 && farFreeDirs >= 6) + { + commitDir(botPos.GetPositionX(), botPos.GetPositionY(), escX, escY); + botAI->Reset(); + return MoveTo(bot->GetMapId(), escX, escY, escZ, false, false, false, false, + MovementPriority::MOVEMENT_COMBAT); + } + } + + // Hard-corner relief: no strict candidate found. Re-scan and pick the + // angle with the most local openness regardless of cloud direction — + // even briefly stepping toward the cloud is preferable to standing + // still in a wedge while it walks into us. + int bestRelOpen = -1; + float bestRelX = 0.0f, bestRelY = 0.0f, bestRelZ = botPos.GetPositionZ(); + for (int i = 0; i < 32; ++i) + { + float escAngle = (2.0f * static_cast(M_PI) * i) / 32; + float escX = botPos.GetPositionX() + std::cos(escAngle) * escRadius; + float escY = botPos.GetPositionY() + std::sin(escAngle) * escRadius; + float escZ = botPos.GetPositionZ(); + + if (!bot->IsWithinLOS(escX, escY, escZ)) + continue; + + if (isBacktrack(escX - botPos.GetPositionX(), escY - botPos.GetPositionY())) + continue; + + int openCount = 0; + for (int j = 0; j < checkDirs; ++j) + { + float cA = (2.0f * static_cast(M_PI) * j) / checkDirs; + if (bot->IsWithinLOS(escX + std::cos(cA) * checkDist, + escY + std::sin(cA) * checkDist, escZ)) + openCount++; + } + if (openCount > bestRelOpen) + { + bestRelOpen = openCount; + bestRelX = escX; + bestRelY = escY; + bestRelZ = escZ; + } + } + if (bestRelOpen >= 0) + { + commitDir(botPos.GetPositionX(), botPos.GetPositionY(), bestRelX, bestRelY); + botAI->Reset(); + return MoveTo(bot->GetMapId(), bestRelX, bestRelY, bestRelZ, false, false, false, false, + MovementPriority::MOVEMENT_COMBAT); + } + } + + // Normal search: scan 32 angles × distance increments, score each valid position. + for (int i = 0; i < numAngles; ++i) + { + float angle = (2.0f * static_cast(M_PI) * i) / numAngles; + float rotatedDx = dx * std::cos(angle) - dy * std::sin(angle); + float rotatedDy = dx * std::sin(angle) + dy * std::cos(angle); + + for (float testDist = movementIncrement; testDist <= maxTestDist; testDist += movementIncrement) + { + float testX = botPos.GetPositionX() + rotatedDx * testDist; + float testY = botPos.GetPositionY() + rotatedDy * testDist; + float testZ = botPos.GetPositionZ(); + + float newCloudDist = cloudPos.GetExactDist2d(testX, testY); + + // Reject positions on the far side of the cloud (through it). + // The move direction must point generally away from the cloud. + float toTestX = testX - botPos.GetPositionX(); + float toTestY = testY - botPos.GetPositionY(); + if (toTestX * dx + toTestY * dy <= 0.0f) + continue; + + // Anti-backtrack: don't pick a candidate that walks against the + // direction we already committed to this Bloat session. + if (isBacktrack(toTestX, toTestY)) + continue; + + float minGasBombDist = FLT_MAX; + for (Unit* bomb : gasBombs) + { + float bombDist = bomb->GetDistance2d(testX, testY); + if (bombDist < minGasBombDist) + minGasBombDist = bombDist; + } + + if (newCloudDist > cloudDist && minGasBombDist >= gasBombSafeDist && + bot->IsWithinLOS(testX, testY, testZ)) + { + int freeDirections = 0; + for (int j = 0; j < checkDirs; ++j) + { + float checkAngle = (2.0f * static_cast(M_PI) * j) / checkDirs; + float checkX = testX + std::cos(checkAngle) * checkDist; + float checkY = testY + std::sin(checkAngle) * checkDist; + if (bot->IsWithinLOS(checkX, checkY, testZ)) + freeDirections++; + } + + float freedomScore = static_cast(freeDirections) / static_cast(checkDirs); + if (freedomScore < minFreedomScore) + continue; + + // Long-range corner check: 8 rays at 25f. If too many blocked, skip. + int farFreeDirs = 0; + for (int k = 0; k < 8; ++k) + { + float cA = (2.0f * static_cast(M_PI) * k) / 8; + if (bot->IsWithinLOS(testX + std::cos(cA) * 25.0f, testY + std::sin(cA) * 25.0f, testZ)) + farFreeDirs++; + } + if (farFreeDirs < 5) + continue; + + // Score: cloud distance, freedom, gas bomb distance, continuity. + bool canContinueMoving = false; + { + float continueX = testX + rotatedDx * movementIncrement; + float continueY = testY + rotatedDy * movementIncrement; + if (bot->IsWithinLOS(continueX, continueY, testZ) && + farFreeDirs >= 6) + canContinueMoving = true; + } + + float continuity = canContinueMoving ? 5.0f : 0.0f; + float combinedScore = newCloudDist + (freedomScore * 15.0f) + minGasBombDist + + static_cast(farFreeDirs) * 2.0f + continuity; + + if (!foundPath || combinedScore > bestScore) + { + bestPos = Position(testX, testY, testZ); + bestScore = combinedScore; + foundPath = true; + } + } + } + } + + if (foundPath) + { + commitDir(botPos.GetPositionX(), botPos.GetPositionY(), + bestPos.GetPositionX(), bestPos.GetPositionY()); + botAI->Reset(); + return MoveTo(bot->GetMapId(), bestPos.GetPositionX(), bestPos.GetPositionY(), bestPos.GetPositionZ(), false, + false, false, false, MovementPriority::MOVEMENT_COMBAT); + } + + // Fallback pass: no candidate met the strict freedom/openness thresholds. + // Relax requirements and pick the most-open position that still moves us + // away from the cloud. Better to make imperfect progress than to stand + // still while the cloud walks into us. + float bestFallbackScore = -FLT_MAX; + Position bestFallbackPos; + bool foundFallback = false; + for (int i = 0; i < numAngles; ++i) + { + float angle = (2.0f * static_cast(M_PI) * i) / numAngles; + float rotatedDx = dx * std::cos(angle) - dy * std::sin(angle); + float rotatedDy = dx * std::sin(angle) + dy * std::cos(angle); + + for (float testDist = movementIncrement; testDist <= maxTestDist; testDist += movementIncrement) + { + float testX = botPos.GetPositionX() + rotatedDx * testDist; + float testY = botPos.GetPositionY() + rotatedDy * testDist; + float testZ = botPos.GetPositionZ(); + + // Must be moving away from the cloud. + float toTestX = testX - botPos.GetPositionX(); + float toTestY = testY - botPos.GetPositionY(); + if (toTestX * dx + toTestY * dy <= 0.0f) + continue; + + if (isBacktrack(toTestX, toTestY)) + continue; + + float newCloudDist = cloudPos.GetExactDist2d(testX, testY); + if (newCloudDist <= cloudDist) + continue; + + if (!bot->IsWithinLOS(testX, testY, testZ)) + continue; + + // Gas bomb hard-reject only. + float minGasBombDist = FLT_MAX; + for (Unit* bomb : gasBombs) + { + float bombDist = bomb->GetDistance2d(testX, testY); + if (bombDist < minGasBombDist) + minGasBombDist = bombDist; + } + if (minGasBombDist < gasBombSafeDist) + continue; + + // Score by cloud distance + local openness, no hard threshold. + int freeDirections = 0; + for (int j = 0; j < checkDirs; ++j) + { + float checkAngle = (2.0f * static_cast(M_PI) * j) / checkDirs; + if (bot->IsWithinLOS(testX + std::cos(checkAngle) * checkDist, + testY + std::sin(checkAngle) * checkDist, testZ)) + freeDirections++; + } + + float score = newCloudDist + static_cast(freeDirections) * 2.0f; + if (score > bestFallbackScore) + { + bestFallbackScore = score; + bestFallbackPos = Position(testX, testY, testZ); + foundFallback = true; + } + } + } + + if (foundFallback) + { + commitDir(botPos.GetPositionX(), botPos.GetPositionY(), + bestFallbackPos.GetPositionX(), bestFallbackPos.GetPositionY()); + botAI->Reset(); + return MoveTo(bot->GetMapId(), bestFallbackPos.GetPositionX(), bestFallbackPos.GetPositionY(), + bestFallbackPos.GetPositionZ(), false, false, false, false, + MovementPriority::MOVEMENT_COMBAT); + } + + // Last resort: move directly away from the cloud ignoring everything. + // Better to slide along a wall than to stand and eat it. + float lastX = botPos.GetPositionX() + dx * movementIncrement; + float lastY = botPos.GetPositionY() + dy * movementIncrement; + commitDir(botPos.GetPositionX(), botPos.GetPositionY(), lastX, lastY); + botAI->Reset(); + return MoveTo(bot->GetMapId(), lastX, lastY, botPos.GetPositionZ(), false, false, false, false, + MovementPriority::MOVEMENT_COMBAT); +} + +bool IccPutricideGasCloudAction::HandleGroupAuraSituation(Unit* gasCloud) +{ + Group* group = bot->GetGroup(); + if (!group || botAI->IsHeal(bot)) + return false; + + constexpr float rangeMinSafeDistance = 15.0f; + constexpr float rangedMaxDistance = 25.0f; + constexpr float meleeRange = 5.0f; + constexpr uint8 skullIconId = 7; + + Unit* volatileOoze = AI_VALUE2(Unit*, "find target", "volatile ooze"); + if ((!volatileOoze || !volatileOoze->IsAlive()) && gasCloud && gasCloud->IsAlive()) + { + ObjectGuid currentSkull = group->GetTargetIcon(skullIconId); + Unit* markedUnit = botAI->GetUnit(currentSkull); + if (!markedUnit || !markedUnit->IsAlive() || markedUnit != gasCloud) + group->SetTargetIcon(skullIconId, bot->GetGUID(), gasCloud->GetGUID()); + } + + float currentDist = gasCloud ? bot->GetDistance(gasCloud) : 0.0f; + + if (!GroupHasGaseousBloat(group) && gasCloud && currentDist < rangeMinSafeDistance) + { + float dx = bot->GetPositionX() - gasCloud->GetPositionX(); + float dy = bot->GetPositionY() - gasCloud->GetPositionY(); + float dist = std::max(0.1f, std::sqrt(dx * dx + dy * dy)); + dx /= dist; + dy /= dist; + + float step = std::min(5.0f, rangeMinSafeDistance - currentDist); + return MoveTo(bot->GetMapId(), bot->GetPositionX() + dx * step, bot->GetPositionY() + dy * step, + bot->GetPositionZ(), false, false, false, false, MovementPriority::MOVEMENT_COMBAT); + } + + return false; +} + +bool IccPutricideGasCloudAction::GroupHasGaseousBloat(Group* group) +{ + for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next()) + { + Player* member = itr->GetSource(); + if (member && botAI->HasAura("Gaseous Bloat", member)) + return true; + } + return false; +} + +bool IccPutricideAvoidMalleableGooAction::Execute(Event /*event*/) +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "professor putricide"); + if (!boss) + return false; + + if (botAI->IsTank(bot) && + bot->GetMotionMaster()->GetCurrentMovementGeneratorType() == FOLLOW_MOTION_TYPE) + { + bot->AttackStop(); + bot->InterruptNonMeleeSpells(true); + if (bot->GetTarget()) + bot->SetTarget(ObjectGuid::Empty); + if (Unit* master = botAI->GetMaster()) + Follow(master); + return true; + } + + // Reactive avoidance: find a position safe from ALL active Malleable Goo + // impact points (boss casts up to 3 simultaneously and they may be near + // each other - fleeing one can land in another). We sample a ring of + // candidates around the bot and score by minimum distance to every active + // hazard (goo impacts, ooze puddles, choking gas bombs). The best-scoring + // candidate that clears the danger radius for all goos is chosen. + // Only active in phase 1/2 (>35% HP) - in phase 3 bots must stack on the + // boss for Mutated Plague healing. + if (!boss->HealthBelowPct(35)) + { + constexpr uint32 impactLifetimeMs = 6000; + constexpr float gooDangerRadius = 8.0f; // 5yd AoE + 3yd safety + constexpr float puddleAvoidRadius = 6.0f; + constexpr float bombAvoidRadius = 6.0f; + + uint32 now = getMSTime(); + float botX = bot->GetPositionX(); + float botY = bot->GetPositionY(); + float botZ = bot->GetPositionZ(); + + // Collect active goo impacts + std::vector goos; + goos.reserve(4); + bool botInDanger = false; + auto impactIt = IcecrownHelpers::malleableGooImpacts.find(bot->GetMap()->GetInstanceId()); + if (impactIt != IcecrownHelpers::malleableGooImpacts.end()) + { + for (auto const& impact : impactIt->second) + { + if (getMSTimeDiff(impact.castTime, now) > impactLifetimeMs) + continue; + goos.push_back(impact.position); + + float dx = botX - impact.position.GetPositionX(); + float dy = botY - impact.position.GetPositionY(); + if (dx * dx + dy * dy < gooDangerRadius * gooDangerRadius) + botInDanger = true; + } + } + + if (botInDanger) + { + // Gather puddle / bomb obstacle positions to avoid in scoring + std::vector obstacles; + std::list nearbyUnits; + bot->GetCreatureListWithEntryInGrid(nearbyUnits, NPC_GROWING_OOZE_PUDDLE, 40.0f); + for (Creature* c : nearbyUnits) + if (c && c->IsAlive()) + obstacles.push_back(c->GetPosition()); + nearbyUnits.clear(); + bot->GetCreatureListWithEntryInGrid(nearbyUnits, NPC_CHOKING_GAS_BOMB, 40.0f); + for (Creature* c : nearbyUnits) + if (c && c->IsAlive()) + obstacles.push_back(c->GetPosition()); + + Position fromPos = bot->GetPosition(); + float bestScore = -1.0f; + float bestX = botX, bestY = botY; + bool found = false; + + // Sample ring: multiple radii × multiple angles. Prefer minimum + // displacement (closer rings tried first via scoring bias). + constexpr int angleSteps = 24; + float const radii[] = {8.0f, 11.0f, 14.0f, 17.0f}; + for (float r : radii) + { + for (int i = 0; i < angleSteps; ++i) + { + float a = (2.0f * float(M_PI) * i) / angleSteps; + float cx = botX + std::cos(a) * r; + float cy = botY + std::sin(a) * r; + Position toPos(cx, cy, botZ); + + // Must clear every goo's danger radius + float minGooDistSq = std::numeric_limits::max(); + bool safe = true; + for (Position const& g : goos) + { + float gdx = cx - g.GetPositionX(); + float gdy = cy - g.GetPositionY(); + float d2 = gdx * gdx + gdy * gdy; + if (d2 < gooDangerRadius * gooDangerRadius) + { + safe = false; + break; + } + if (d2 < minGooDistSq) + minGooDistSq = d2; + } + if (!safe) + continue; + + // Reachability checks + if (!bot->IsWithinLOS(cx, cy, botZ)) + continue; + if (HasObstacleBetween(fromPos, toPos)) + continue; + + // Score = min distance to nearest hazard at the candidate + // (goo, puddle, bomb), penalize travel distance lightly. + float minHazard = std::sqrt(minGooDistSq); + for (Position const& o : obstacles) + { + float odx = cx - o.GetPositionX(); + float ody = cy - o.GetPositionY(); + float od = std::sqrt(odx * odx + ody * ody); + // Treat obstacles as having a minimum stand-off + float effective = od - (puddleAvoidRadius - 4.0f); + if (effective < minHazard) + minHazard = effective; + } + + float travel = std::sqrt((cx - botX) * (cx - botX) + + (cy - botY) * (cy - botY)); + float score = minHazard - travel * 0.1f; + + if (score > bestScore) + { + bestScore = score; + bestX = cx; + bestY = cy; + found = true; + } + } + // If we found a clean candidate at this radius, stop expanding + if (found) + break; + } + + if (found) + { + return MoveTo(bot->GetMapId(), bestX, bestY, botZ, false, false, false, false, + MovementPriority::MOVEMENT_COMBAT); + } + } + } + + if (HandleTankPositioning(boss)) + return false; + + if (AI_VALUE2(Unit*, "find target", "volatile ooze") || AI_VALUE2(Unit*, "find target", "gas cloud")) + return false; + + if (HandleUnboundPlague(boss)) + return false; + + // Only stack at boss in phase 3 (<=35% HP). Above 35% bots may stand + // anywhere; the goo flee block above handles emergencies. + if (!boss->HealthBelowPct(35)) + return false; + + return HandleBossPositioning(boss); +} + +bool IccPutricideAvoidMalleableGooAction::HandleTankPositioning(Unit* boss) +{ + if (!botAI->IsTank(bot)) + return false; + + if (boss && boss->IsAlive() && + !AI_VALUE2(Unit*, "find target", "volatile ooze") && !AI_VALUE2(Unit*, "find target", "gas cloud")) + { + if (Group* group = bot->GetGroup()) + { + constexpr uint8 skullIconId = 7; + ObjectGuid skullGuid = group->GetTargetIcon(skullIconId); + Unit* markedUnit = botAI->GetUnit(skullGuid); + if (!skullGuid || !markedUnit || !markedUnit->IsAlive()) + group->SetTargetIcon(skullIconId, bot->GetGUID(), boss->GetGUID()); + } + } + + constexpr float bombSearchRange = 100.0f; + constexpr float safeDistance = 15.0f; + + Unit* bomb = bot->FindNearestCreature(NPC_CHOKING_GAS_BOMB, bombSearchRange); + if (!bomb) + return false; + + float currentDistance = bot->GetDistance2d(bomb); + + if (currentDistance < safeDistance) + return MoveAway(bomb, safeDistance - currentDistance); + + return false; +} + +bool IccPutricideAvoidMalleableGooAction::HandleUnboundPlague(Unit* boss) +{ + if (boss && boss->HealthBelowPct(35)) + return false; + + if (!botAI->HasAura("Unbound Plague", bot)) + return false; + + Group* group = bot->GetGroup(); + if (!group) + return false; + + constexpr float unboundPlagueDistance = 20.0f; + constexpr float unboundPlagueBuffer = 2.0f; + float closestDistance = unboundPlagueDistance; + Unit* closestPlayer = nullptr; + + for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next()) + { + Player* member = itr->GetSource(); + if (!member || !member->IsAlive() || member == bot) + continue; + + float dist = bot->GetDistance2d(member); + if (dist < closestDistance) + { + closestDistance = dist; + closestPlayer = member; + } + } + + if (!closestPlayer || closestDistance >= unboundPlagueDistance) + { + bot->Kill(bot, bot); + return true; + } + + float dx = bot->GetPositionX() - closestPlayer->GetPositionX(); + float dy = bot->GetPositionY() - closestPlayer->GetPositionY(); + float dist = std::sqrt(dx * dx + dy * dy); + + if (dist <= 0.0f) + return false; + + dx /= dist; + dy /= dist; + float moveDistance = unboundPlagueDistance - closestDistance + unboundPlagueBuffer; + + float moveX = bot->GetPositionX() + dx * moveDistance; + float moveY = bot->GetPositionY() + dy * moveDistance; + + if (bot->IsWithinLOS(moveX, moveY, bot->GetPositionZ())) + { + return MoveTo(bot->GetMapId(), moveX, moveY, bot->GetPositionZ(), false, false, false, false, + MovementPriority::MOVEMENT_COMBAT); + } + + return false; +} + +bool IccPutricideAvoidMalleableGooAction::HandleBossPositioning(Unit* boss) +{ + if (botAI->IsMainTank(bot)) + return false; + + Unit* mainTank = AI_VALUE(Unit*, "main tank"); + if (!mainTank || !mainTank->IsAlive()) + return false; + + constexpr float stackTolerance = 2.0f; + constexpr float hunterMaxDistance = 12.0f; + constexpr float frontalConeHalfAngle = float(M_PI) / 3.0f; // 60 degrees -> 120 deg cone + + float distToTank = bot->GetExactDist2d(mainTank); + + if (bot->getClass() == CLASS_HUNTER) + { + // Hunter slack: stay up to 12f from main tank, but only if standing in + // the boss's frontal cone. Outside the cone or beyond 12f -> move to MT. + float bossOrient = boss->GetOrientation(); + float toBotX = bot->GetPositionX() - boss->GetPositionX(); + float toBotY = bot->GetPositionY() - boss->GetPositionY(); + float toBotLen = std::sqrt(toBotX * toBotX + toBotY * toBotY); + bool inFrontCone = false; + if (toBotLen > 0.01f) + { + float bearing = std::atan2(toBotY, toBotX); + float delta = bearing - bossOrient; + while (delta > float(M_PI)) delta -= 2.0f * float(M_PI); + while (delta < -float(M_PI)) delta += 2.0f * float(M_PI); + inFrontCone = std::fabs(delta) <= frontalConeHalfAngle; + } + + if (distToTank <= hunterMaxDistance && inFrontCone) + return false; + } + else if (distToTank <= stackTolerance) + { + return false; + } + + bot->SetFacingToObject(boss); + return MoveTo(bot->GetMapId(), mainTank->GetPositionX(), mainTank->GetPositionY(), mainTank->GetPositionZ(), + false, false, false, botAI->IsRanged(bot), MovementPriority::MOVEMENT_COMBAT); +} + +bool IccPutricideAvoidMalleableGooAction::HasObstacleBetween(Position const& from, Position const& to) +{ + GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); + for (auto const& npc : npcs) + { + Unit* unit = botAI->GetUnit(npc); + if (!unit || !unit->IsAlive()) + continue; + + if (unit->GetEntry() == NPC_GROWING_OOZE_PUDDLE || unit->GetEntry() == NPC_CHOKING_GAS_BOMB) + { + if (IsOnPath(from, to, unit->GetPosition(), 3.0f)) + return true; + } + } + return false; +} + +bool IccPutricideAvoidMalleableGooAction::IsOnPath(Position const& from, Position const& to, Position const& point, + float threshold) +{ + float pathX = to.GetPositionX() - from.GetPositionX(); + float pathY = to.GetPositionY() - from.GetPositionY(); + float pathLen = std::sqrt(pathX * pathX + pathY * pathY); + + if (pathLen < 0.1f) + return false; + + float normX = pathX / pathLen; + float normY = pathY / pathLen; + + float toPointX = point.GetPositionX() - from.GetPositionX(); + float toPointY = point.GetPositionY() - from.GetPositionY(); + float proj = toPointX * normX + toPointY * normY; + + if (proj < 0.0f || proj > pathLen) + return false; + + float closestX = from.GetPositionX() + normX * proj; + float closestY = from.GetPositionY() + normY * proj; + float distToPath = std::sqrt((point.GetPositionX() - closestX) * (point.GetPositionX() - closestX) + + (point.GetPositionY() - closestY) * (point.GetPositionY() - closestY)); + + return distToPath < threshold; +} + +bool IccPutricideAbominationAction::IsSomeoneAlreadyPiloting() +{ + Group* group = bot->GetGroup(); + if (!group) + return false; + + for (GroupReference* itr = group->GetFirstMember(); itr; itr = itr->next()) + { + Player* m = itr->GetSource(); + if (!m || m == bot || !m->IsAlive()) + continue; + if (Unit* vb = m->GetVehicleBase()) + { + uint32 e = vb->GetEntry(); + if (e == NPC_MUTATED_ABOMINATION_10 || e == NPC_MUTATED_ABOMINATION_25) + return true; + } + } + return false; +} + +Unit* IccPutricideAbominationAction::FindClosestPuddle(float maxRange) +{ + GuidVector const npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); + Unit* best = nullptr; + float bestDist = maxRange; + for (auto const& g : npcs) + { + Unit* u = botAI->GetUnit(g); + if (!u || !u->IsAlive() || u->GetEntry() != NPC_GROWING_OOZE_PUDDLE) + continue; + float d = bot->GetExactDist2d(u); + if (d < bestDist) + { + bestDist = d; + best = u; + } + } + return best; +} + +bool IccPutricideAbominationAction::BecomeAbomination() +{ + GameObject* go = bot->FindNearestGameObject(GO_PUTRICIDE_DRINK_ME, 100.0f); + if (!go || !go->isSpawned()) + return false; + + float dist = bot->GetDistance(go); + if (dist > INTERACTION_DISTANCE) + { + return MoveTo(bot->GetMapId(), go->GetPositionX(), go->GetPositionY(), go->GetPositionZ(), + false, false, false, false, MovementPriority::MOVEMENT_COMBAT); + } + + bot->GetMotionMaster()->Clear(); + bot->StopMoving(); + bot->SetFacingToObject(go); + bool hasAura = botAI->HasAura("Mutated Transformation", bot); + if (!hasAura) + { + go->Use(bot); + return true; + } + return false; +} + +Unit* IccPutricideAbominationAction::PickSlashTarget(Unit* boss) +{ + Unit* volatileOoze = AI_VALUE2(Unit*, "find target", "volatile ooze"); + if (volatileOoze && volatileOoze->IsAlive()) + return volatileOoze; + + Unit* gasCloud = AI_VALUE2(Unit*, "find target", "gas cloud"); + if (gasCloud && gasCloud->IsAlive()) + return gasCloud; + + return boss; +} + +bool IccPutricideAbominationAction::TryRegurgitate(Unit* abo, Unit* target) +{ + if (!abo || !target || !target->IsAlive()) + return false; + + if (abo->GetExactDist2d(target) > 50.0f) + return false; + + if (botAI->HasAura("Regurgitated Ooze", target)) + return false; + + uint32 spellId = AI_VALUE2(uint32, "vehicle spell id", "Regurgitated Ooze"); + if (!spellId) + return false; + + if (abo->HasSpellCooldown(spellId)) + return false; + + SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId); + if (!spellInfo) + return false; + + if (!abo->HasInArc(CAST_ANGLE_IN_FRONT, target, 100.0f)) + { + abo->SetFacingToObject(target); + return false; + } + + Spell* spell = new Spell(abo, spellInfo, TRIGGERED_IGNORE_POWER_AND_REAGENT_COST); + SpellCastTargets targets; + targets.SetUnitTarget(target); + spell->prepare(&targets); + + abo->AddSpellCooldown(spellId, 0, 1000); + return true; +} + +bool IccPutricideAbominationAction::TryEatOoze(Unit* abo, Unit* puddle) +{ + if (!abo || !puddle || !puddle->IsAlive()) + return false; + + constexpr float eatRange = 4.0f; + if (abo->GetExactDist2d(puddle) > eatRange) + { + return MoveTo(bot->GetMapId(), puddle->GetPositionX(), puddle->GetPositionY(), puddle->GetPositionZ(), false, + false, false, false, MovementPriority::MOVEMENT_COMBAT); + } + + // In range of puddle — hold position until it dies, even on cooldown + uint32 spellId = AI_VALUE2(uint32, "vehicle spell id", "Eat Ooze"); + if (!spellId) + return true; + + if (abo->HasSpellCooldown(spellId)) + return true; + + if (botAI->CanCastVehicleSpell(spellId, puddle) && botAI->CastVehicleSpell(spellId, puddle)) + abo->AddSpellCooldown(spellId, 0, 1000); + + return true; +} + +bool IccPutricideAbominationAction::Execute(Event /*event*/) +{ + if (!botAI->IsAssistTank(bot)) + return false; + + Unit* boss = AI_VALUE2(Unit*, "find target", "professor putricide"); + if (!boss) + return false; + + Unit* abo = bot->GetVehicleBase(); + bool piloting = + abo && (abo->GetEntry() == NPC_MUTATED_ABOMINATION_10 || abo->GetEntry() == NPC_MUTATED_ABOMINATION_25); + + if (!piloting) + { + if (boss->HealthBelowPct(35)) + return false; + if (IsSomeoneAlreadyPiloting()) + return false; + Unit* nearestPuddle = FindClosestPuddle(50.0f); + if (!nearestPuddle) + return false; + return BecomeAbomination(); + } + + Unit* volatileOoze = AI_VALUE2(Unit*, "find target", "volatile ooze"); + Unit* gasCloud = AI_VALUE2(Unit*, "find target", "gas cloud"); + Unit* puddle = FindClosestPuddle(100.0f); + + // Priority 1: Regurgitated Ooze on volatile ooze (aura-gated, energy bypassed) + if (volatileOoze && volatileOoze->IsAlive() && + !botAI->HasAura("Regurgitated Ooze", volatileOoze)) + { + if (TryRegurgitate(abo, volatileOoze)) + return true; + } + + // Priority 2: Regurgitated Ooze on gas cloud (aura-gated, energy bypassed) + if (gasCloud && gasCloud->IsAlive() && + !botAI->HasAura("Regurgitated Ooze", gasCloud)) + { + if (TryRegurgitate(abo, gasCloud)) + return true; + } + + // Priority 3: actively seek and eat puddle + if (puddle) + { + if (TryEatOoze(abo, puddle)) + return true; + } + + // Priority 4: slash target volatile > gas > boss + Unit* slashTarget = PickSlashTarget(boss); + if (slashTarget) + { + if (abo->GetExactDist2d(slashTarget) > 5.0f) + { + return MoveTo(bot->GetMapId(), slashTarget->GetPositionX(), slashTarget->GetPositionY(), + slashTarget->GetPositionZ(), false, false, false, false, MovementPriority::MOVEMENT_COMBAT); + } + + if (abo) + { + uint32 msSpellId = AI_VALUE2(uint32, "vehicle spell id", "Mutated Slash"); + if (msSpellId && !abo->HasSpellCooldown(msSpellId) && botAI->CanCastVehicleSpell(msSpellId, slashTarget) && + botAI->CastVehicleSpell(msSpellId, slashTarget)) + { + abo->AddSpellCooldown(msSpellId, 0, 1000); + return true; + } + } + + if (bot->GetTarget() != slashTarget->GetGUID()) + bot->SetTarget(slashTarget->GetGUID()); + Attack(slashTarget); + } + + return true; +} diff --git a/src/Ai/Raid/ICC/Action/ICCActions_RF.cpp b/src/Ai/Raid/ICC/Action/ICCActions_RF.cpp new file mode 100644 index 00000000000..30cab598ee3 --- /dev/null +++ b/src/Ai/Raid/ICC/Action/ICCActions_RF.cpp @@ -0,0 +1,1091 @@ +#include +#include +#include +#include "GenericActions.h" +#include "GenericSpellActions.h" +#include "Multiplier.h" +#include "NearestNpcsValue.h" +#include "ObjectAccessor.h" +#include "Playerbots.h" +#include "ICCActions.h" +#include "ICCScripts.h" +#include "ICCTriggers.h" +#include "RtiValue.h" +#include "Timer.h" +#include "Vehicle.h" + +// Rotface +bool IccRotfaceTankPositionAction::Execute(Event /*event*/) +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "rotface"); + if (!boss) + return false; + + Unit* smallOoze = AI_VALUE2(Unit*, "find target", "little ooze"); + + MarkBossWithSkull(boss); + + if (botAI->IsMainTank(bot)) + { + bool assistTankAlive = false; + if (Group* group = bot->GetGroup()) + { + for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next()) + { + Player* member = itr->GetSource(); + if (member && member->IsAlive() && member != bot && botAI->IsAssistTank(member)) + { + assistTankAlive = true; + break; + } + } + } + + Unit* bigOoze = bot->FindNearestCreature(NPC_BIG_OOZE, 100.0f); + bool const bigOozeHandled = bigOoze && bigOoze->IsAlive() && bigOoze->GetVictim() && + bigOoze->GetVictim()->IsPlayer() && + botAI->IsAssistTank(bigOoze->GetVictim()->ToPlayer()); + + if (bigOoze && bigOoze->IsAlive() && !assistTankAlive && !bigOozeHandled) + return HandleAssistTankPositioning(boss); + + return PositionMainTankAndMelee(boss, smallOoze); + } + + if (botAI->IsAssistTank(bot)) + return HandleAssistTankPositioning(boss); + + return false; +} + +bool IccRotfaceTankPositionAction::MarkBossWithSkull(Unit* boss) +{ + Group* group = bot->GetGroup(); + if (!group) + return false; + + constexpr uint8 skullIconId = 7; + ObjectGuid skullGuid = group->GetTargetIcon(skullIconId); + if (skullGuid != boss->GetGUID()) + group->SetTargetIcon(skullIconId, bot->GetGUID(), boss->GetGUID()); + + return false; +} + +bool IccRotfaceTankPositionAction::PositionMainTankAndMelee(Unit* boss, Unit* smallOoze) +{ + bool isBossCasting = false; + if (boss && boss->HasUnitState(UNIT_STATE_CASTING)) + isBossCasting = true; + + if (botAI->IsMainTank(bot) && boss && boss->GetVictim() == bot) + { + bool const bossInPosition = boss->GetExactDist2d(ICC_ROTFACE_CENTER_POSITION_BOSS) <= 4.0f; + + if (!bossInPosition) + { + // Step 2y in the direction from boss toward center — tank leads, boss follows + float dirX = ICC_ROTFACE_CENTER_POSITION_BOSS.GetPositionX() - boss->GetPositionX(); + float dirY = ICC_ROTFACE_CENTER_POSITION_BOSS.GetPositionY() - boss->GetPositionY(); + float const len = std::sqrt(dirX * dirX + dirY * dirY); + if (len > 0.5f) + { + dirX /= len; + dirY /= len; + } + float const destX = bot->GetPositionX() + dirX * 2.0f; + float const destY = bot->GetPositionY() + dirY * 2.0f; + MoveTo(bot->GetMapId(), destX, destY, bot->GetPositionZ(), false, false, false, true, + MovementPriority::MOVEMENT_COMBAT); + } + else if (bot->GetExactDist2d(ICC_ROTFACE_CENTER_POSITION) > 7.0f) + { + MoveTo(bot->GetMapId(), ICC_ROTFACE_CENTER_POSITION.GetPositionX(), + ICC_ROTFACE_CENTER_POSITION.GetPositionY(), ICC_ROTFACE_CENTER_POSITION.GetPositionZ(), false, false, + false, true, MovementPriority::MOVEMENT_COMBAT); + } + else + { + if (bot->GetVictim() != boss) + bot->Attack(boss, true); + } + } + + if (boss && isBossCasting && !botAI->IsTank(bot)) + { + float const x = boss->GetPositionX(); + float const y = boss->GetPositionY(); + float const z = boss->GetPositionZ(); + + if (bot->GetExactDist2d(x, y) > 0.5f) + MoveTo(bot->GetMapId(), x, y, z, false, false, false, false, MovementPriority::MOVEMENT_FORCED, true, + false); + } + + return false; +} + +bool IccRotfaceTankPositionAction::HandleAssistTankPositioning(Unit* boss) +{ + GuidVector bigOozes = AI_VALUE(GuidVector, "nearest hostile npcs"); + std::vector activeBigOozes; + + for (auto const& guid : bigOozes) + { + Unit* unit = botAI->GetUnit(guid); + if (unit && unit->IsAlive() && unit->GetEntry() == NPC_BIG_OOZE && unit->IsVisible()) + activeBigOozes.push_back(unit); + } + + if (activeBigOozes.empty()) + return false; + + auto CastClassTaunt = [&](Unit* target) -> bool + { + if (!target || !target->IsAlive()) + return false; + + if (!bot->HasAura(SPELL_SPITEFULL_FURY)) + bot->AddAura(SPELL_SPITEFULL_FURY, bot); + + switch (bot->getClass()) + { + case CLASS_PALADIN: + { + bot->RemoveSpellCooldown(SPELL_TAUNT_PALADIN, true); + if (botAI->CastSpell("hand of reckoning", target)) + return true; + break; + } + case CLASS_DEATH_KNIGHT: + { + bot->RemoveSpellCooldown(SPELL_TAUNT_DK, true); + if (botAI->CastSpell("dark command", target)) + return true; + break; + } + case CLASS_DRUID: + { + bot->RemoveSpellCooldown(SPELL_TAUNT_DRUID, true); + if (botAI->CastSpell("growl", target)) + return true; + break; + } + case CLASS_WARRIOR: + { + bot->RemoveSpellCooldown(SPELL_TAUNT_WARRIOR, true); + if (botAI->CastSpell("taunt", target)) + return true; + break; + } + default: + break; + } + + if (botAI->CastSpell("shoot", target) || botAI->CastSpell("throw", target)) + return true; + + return false; + }; + + Unit* uncollectedOoze = nullptr; + float minUncollectedDist = FLT_MAX; + for (Unit* ooze : activeBigOozes) + { + if (ooze->GetVictim() == bot) + continue; + + CastClassTaunt(ooze); + + float const dist = bot->GetExactDist2d(ooze); + if (dist < minUncollectedDist) + { + minUncollectedDist = dist; + uncollectedOoze = ooze; + } + } + + if (uncollectedOoze) + { + if (!bot->HasAura(SPELL_NITRO_BOOSTS)) + bot->AddAura(SPELL_NITRO_BOOSTS, bot); + return MoveTo(bot->GetMapId(), uncollectedOoze->GetPositionX(), uncollectedOoze->GetPositionY(), + uncollectedOoze->GetPositionZ(), false, false, false, false, MovementPriority::MOVEMENT_COMBAT); + } + + Unit* targetOoze = nullptr; + float minDist = FLT_MAX; + for (Unit* ooze : activeBigOozes) + { + float const dist = bot->GetExactDist2d(ooze); + if (dist < minDist) + { + minDist = dist; + targetOoze = ooze; + } + } + + if (!targetOoze) + return false; + + return HandleBigOozeKiting(targetOoze); +} + +Unit* IccRotfaceTankPositionAction::FindAssignedBigOoze(Unit* /*boss*/, std::vector& bigOozes) +{ + Group* group = bot->GetGroup(); + if (!group) + return nullptr; + + Unit* bestOoze = nullptr; + float minDistance = FLT_MAX; + + for (Unit* ooze : bigOozes) + { + if (!ooze || !ooze->IsAlive() || !ooze->IsVisible()) + continue; + + ObjectGuid oozeGuid = ooze->GetGUID(); + + bool isAssignedToOther = false; + for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next()) + { + Player* member = itr->GetSource(); + if (!member || !member->IsInWorld() || member == bot) + continue; + + Unit* memberTarget = botAI->GetUnit(member->GetTarget()); + if (memberTarget && memberTarget->GetGUID() == oozeGuid) + { + isAssignedToOther = true; + break; + } + } + + if (!isAssignedToOther) + { + float const dist = bot->GetExactDist2d(ooze); + if (dist < minDistance) + { + minDistance = dist; + bestOoze = ooze; + } + } + } + + return bestOoze; +} + +bool IccRotfaceTankPositionAction::HandleBigOozeKiting(Unit* bigOoze) +{ + auto CastClassTaunt = [&](Unit* target) -> bool + { + if (!target || !target->IsAlive()) + return false; + + if (!bot->HasAura(SPELL_SPITEFULL_FURY)) + bot->AddAura(SPELL_SPITEFULL_FURY, bot); + + switch (bot->getClass()) + { + case CLASS_PALADIN: + { + bot->RemoveSpellCooldown(SPELL_TAUNT_PALADIN, true); + if (botAI->CastSpell("hand of reckoning", target)) + return true; + break; + } + case CLASS_DEATH_KNIGHT: + { + bot->RemoveSpellCooldown(SPELL_TAUNT_DK, true); + if (botAI->CastSpell("dark command", target)) + return true; + break; + } + case CLASS_DRUID: + { + bot->RemoveSpellCooldown(SPELL_TAUNT_DRUID, true); + if (botAI->CastSpell("growl", target)) + return true; + break; + } + case CLASS_WARRIOR: + { + bot->RemoveSpellCooldown(SPELL_TAUNT_WARRIOR, true); + if (botAI->CastSpell("taunt", target)) + return true; + break; + } + default: + break; + } + + if (botAI->CastSpell("shoot", target) || botAI->CastSpell("throw", target)) + return true; + + return false; + }; + + if (bigOoze->GetVictim() != bot && bigOoze->IsAlive()) + CastClassTaunt(bigOoze); + + float const oozeDistance = bot->GetExactDist2d(bigOoze); + + if (oozeDistance > 12.0f) + { + bot->SetTarget(bigOoze->GetGUID()); + bot->SetFacingToObject(bigOoze); + if (bigOoze->GetVictim() != bot) + CastClassTaunt(bigOoze); + return false; + } + + float const minRadius = 24.0f; + float const maxRadius = 34.0f; + float const safeDistanceFromOoze = 13.0f; + + float const currentDistance = bot->GetExactDist2d(ICC_ROTFACE_CENTER_POSITION); + + if (currentDistance < minRadius || currentDistance > maxRadius) + { + float dirX = bot->GetPositionX() - ICC_ROTFACE_CENTER_POSITION.GetPositionX(); + float dirY = bot->GetPositionY() - ICC_ROTFACE_CENTER_POSITION.GetPositionY(); + float length = std::sqrt(dirX * dirX + dirY * dirY); + dirX /= length; + dirY /= length; + + float const targetX = ICC_ROTFACE_CENTER_POSITION.GetPositionX() + dirX * maxRadius; + float const targetY = ICC_ROTFACE_CENTER_POSITION.GetPositionY() + dirY * maxRadius; + + if (bigOoze->GetExactDist2d(targetX, targetY) >= safeDistanceFromOoze) + return MoveTo(bot->GetMapId(), targetX, targetY, bot->GetPositionZ(), false, false, false, true, + MovementPriority::MOVEMENT_COMBAT); + } + + float currentAngle = std::atan2(bot->GetPositionY() - ICC_ROTFACE_CENTER_POSITION.GetPositionY(), + bot->GetPositionX() - ICC_ROTFACE_CENTER_POSITION.GetPositionX()); + + for (int32 i = 0; i < 16; ++i) + { + float const angleOffset = (i % 2 == 0 ? 1 : -1) * (M_PI / 16.0f) * (i / 2.0f); + float const newAngle = currentAngle + angleOffset; + + float const newX = ICC_ROTFACE_CENTER_POSITION.GetPositionX() + maxRadius * std::cos(newAngle); + float const newY = ICC_ROTFACE_CENTER_POSITION.GetPositionY() + maxRadius * std::sin(newAngle); + + if (bigOoze->GetExactDist2d(newX, newY) >= safeDistanceFromOoze) + { + GuidVector puddles = AI_VALUE(GuidVector, "nearest hostile npcs"); + bool isSafeFromPuddles = true; + + for (auto const& puddleGuid : puddles) + { + Unit* puddle = botAI->GetUnit(puddleGuid); + if (puddle && botAI->GetAura("Ooze Flood", puddle)) + { + float const puddleDistance = puddle->GetDistance2d(newX, newY); + if (puddleDistance < 30.0f) + { + isSafeFromPuddles = false; + break; + } + } + } + + if (isSafeFromPuddles) + { + MoveTo(bot->GetMapId(), newX, newY, bot->GetPositionZ(), false, false, false, true, + MovementPriority::MOVEMENT_COMBAT); + return true; + } + } + } + + return false; +} + +bool IccRotfaceGroupPositionAction::Execute(Event /*event*/) +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "rotface"); + if (!boss) + return false; + + GuidVector const npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); + + bool const hasOozeFlood = botAI->HasAura("Ooze Flood", bot); + Unit* smallOoze = AI_VALUE2(Unit*, "find target", "little ooze"); + + if (!botAI->IsTank(bot) && HandlePuddleAvoidance(boss)) + return true; + + if (HandleOozeTargeting()) + return true; + + if (!(smallOoze && smallOoze->GetVictim() == bot) && !hasOozeFlood && PositionRangedAndHealers(boss, smallOoze)) + return true; + + return false; +} + +bool IccRotfaceGroupPositionAction::HandlePuddleAvoidance(Unit* boss) +{ + GuidVector const npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); + + for (auto const& npc : npcs) + { + Unit* unit = botAI->GetUnit(npc); + if (!unit || !botAI->HasAura("Ooze Flood", unit)) + continue; + + float const puddleDistance = bot->GetExactDist2d(unit); + float const bossDistance = bot->GetExactDist2d(ICC_ROTFACE_CENTER_POSITION); + + if (bossDistance < 15.0f) + return false; + + if (puddleDistance < 30.0f) + return MoveAwayFromPuddle(boss, unit, puddleDistance); + } + + return false; +} + +bool IccRotfaceGroupPositionAction::MoveAwayFromPuddle(Unit* boss, Unit* puddle, float) +{ + if (!boss || !puddle) + return false; + + float const dx = puddle->GetPositionX() - bot->GetPositionX(); + float const dy = puddle->GetPositionY() - bot->GetPositionY(); + float const angle = std::atan2(dy, dx); + + float const stepSize = 7.0f; + float const minPuddleDistance = 30.0f; + float const minCenterDistance = 15.0f; + float const maxCenterDistance = 25.0f; + int32 const directions = 8; + + for (int32 i = 0; i < directions; ++i) + { + float const testAngle = angle + M_PI + (i * M_PI / 4); + float const moveX = bot->GetPositionX() + stepSize * std::cos(testAngle); + float const moveY = bot->GetPositionY() + stepSize * std::sin(testAngle); + float const moveZ = bot->GetPositionZ(); + + float const newPuddleDistance = puddle->GetDistance2d(moveX, moveY); + float const newCenterDistance = std::sqrt(std::pow(moveX - ICC_ROTFACE_CENTER_POSITION.GetPositionX(), 2) + + std::pow(moveY - ICC_ROTFACE_CENTER_POSITION.GetPositionY(), 2)); + + if (newPuddleDistance >= minPuddleDistance && newCenterDistance >= minCenterDistance && + newCenterDistance <= maxCenterDistance && bot->IsWithinLOS(moveX, moveY, moveZ)) + { + return MoveTo(bot->GetMapId(), moveX, moveY, moveZ, false, false, false, false, + MovementPriority::MOVEMENT_COMBAT); + } + } + + return false; +} + +bool IccRotfaceGroupPositionAction::HandleOozeTargeting() +{ + if (botAI->IsMainTank(bot)) + return false; + + GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); + + Player* mainTankWithOoze = nullptr; + if (Group* group = bot->GetGroup()) + { + for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next()) + { + Player* member = itr->GetSource(); + if (!member || !member->IsAlive() || !botAI->IsMainTank(member)) + continue; + + for (auto const& guid : npcs) + { + Unit* unit = botAI->GetUnit(guid); + if (unit && unit->IsAlive() && unit->GetEntry() == NPC_SMALL_OOZE && unit->GetVictim() == member) + { + mainTankWithOoze = member; + break; + } + } + break; + } + } + + Aura* infectionAura = botAI->GetAura("Mutated Infection", bot, false, false); + + // Find assist tank currently kiting a Big Ooze — preferred merge point + Player* assistTankKiting = nullptr; + if (Group* group = bot->GetGroup()) + { + for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next()) + { + Player* member = itr->GetSource(); + if (!member || !member->IsAlive() || !botAI->IsAssistTank(member)) + continue; + + for (auto const& guid : npcs) + { + Unit* unit = botAI->GetUnit(guid); + if (unit && unit->IsAlive() && unit->GetEntry() == NPC_BIG_OOZE && unit->GetVictim() == member) + { + assistTankKiting = member; + break; + } + } + if (assistTankKiting) + break; + } + } + + bool const hasSmallOoze = [&]() -> bool { + for (auto const& guid : npcs) + { + Unit* unit = botAI->GetUnit(guid); + if (unit && unit->IsAlive() && unit->GetEntry() == NPC_SMALL_OOZE && unit->GetVictim() == bot) + return true; + } + return false; + }(); + + bool const needsMerge = hasSmallOoze || (infectionAura && infectionAura->GetDuration() <= 4000); + + if (needsMerge) + { + if (assistTankKiting && bot->GetExactDist2d(assistTankKiting) > 2.0f) + return MoveTo(bot->GetMapId(), assistTankKiting->GetPositionX(), assistTankKiting->GetPositionY(), + assistTankKiting->GetPositionZ(), false, false, false, true, + MovementPriority::MOVEMENT_FORCED); + + if (!assistTankKiting) + { + if (mainTankWithOoze && bot->GetExactDist2d(mainTankWithOoze) > 2.0f) + return MoveTo(bot->GetMapId(), mainTankWithOoze->GetPositionX(), mainTankWithOoze->GetPositionY(), + mainTankWithOoze->GetPositionZ(), false, false, false, true, + MovementPriority::MOVEMENT_FORCED); + + return HandleOozeMemberPositioning(nullptr); + } + } + + return false; +} + +bool IccRotfaceGroupPositionAction::HandleOozeMemberPositioning(Unit* /*mySmallOoze*/) +{ + GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); + std::vector bigOozes; + for (auto const& guid : npcs) + { + Unit* unit = botAI->GetUnit(guid); + if (unit && unit->IsAlive() && unit->IsVisible() && unit->GetEntry() == NPC_BIG_OOZE) + bigOozes.push_back(unit); + } + + if (!bigOozes.empty()) + { + Unit* target = nullptr; + float minDist = FLT_MAX; + + for (Unit* ooze : bigOozes) + { + Unit* victim = ooze->GetVictim(); + if (victim && victim->IsPlayer() && botAI->IsAssistTank(victim->ToPlayer())) + { + float const dist = bot->GetExactDist2d(ooze); + if (dist < minDist) + { + minDist = dist; + target = ooze; + } + } + } + + if (!target) + { + for (Unit* ooze : bigOozes) + { + float const dist = bot->GetExactDist2d(ooze); + if (dist < minDist) + { + minDist = dist; + target = ooze; + } + } + } + + if (target && bot->GetExactDist2d(target) > 2.0f && !botAI->IsAssistTank(bot)) + return MoveTo(bot->GetMapId(), target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), + false, false, false, true, MovementPriority::MOVEMENT_FORCED); + + return false; + } + + if (bot->GetExactDist2d(ICC_ROTFACE_BIG_OOZE_POSITION) > 5.0f) + return MoveTo(bot->GetMapId(), ICC_ROTFACE_BIG_OOZE_POSITION.GetPositionX(), + ICC_ROTFACE_BIG_OOZE_POSITION.GetPositionY(), ICC_ROTFACE_BIG_OOZE_POSITION.GetPositionZ(), false, + false, false, true, MovementPriority::MOVEMENT_FORCED); + + return false; +} + +bool IccRotfaceGroupPositionAction::PositionRangedAndHealers(Unit* boss, Unit* smallOoze) +{ + if (!(botAI->IsRanged(bot) || botAI->IsHeal(bot))) + return false; + + if (smallOoze && smallOoze->GetVictim() == bot) + return false; + + Difficulty const diff = bot->GetRaidDifficulty(); + bool isBossCasting = false; + if (boss && boss->HasUnitState(UNIT_STATE_CASTING) && boss->FindCurrentSpellBySpellId(SPELL_SLIME_SPRAY)) + isBossCasting = true; + + bool const isHeroic = (diff == RAID_DIFFICULTY_10MAN_HEROIC || diff == RAID_DIFFICULTY_25MAN_HEROIC); + + if (boss && isBossCasting && !isHeroic) + { + float const x = boss->GetPositionX(); + float const y = boss->GetPositionY(); + float const z = boss->GetPositionZ(); + + if (bot->GetExactDist2d(x, y) > 0.5f) + { + MoveTo(bot->GetMapId(), x, y, z, false, false, false, false, MovementPriority::MOVEMENT_FORCED, true, + false); + } + return false; + } + + if (!isHeroic && !isBossCasting && boss && bot->getClass() != CLASS_HUNTER && + (bot->GetExactDist2d(boss->GetPositionX(), boss->GetPositionY()) < 2.0f || + bot->GetExactDist2d(boss->GetPositionX(), boss->GetPositionY()) > 5.0f)) + { + float const angle = + std::atan2(bot->GetPositionY() - boss->GetPositionY(), bot->GetPositionX() - boss->GetPositionX()); + float const destX = boss->GetPositionX() + 3.5f * std::cos(angle); + float const destY = boss->GetPositionY() + 3.5f * std::sin(angle); + return MoveTo(bot->GetMapId(), destX, destY, bot->GetPositionZ(), false, false, false, true, + MovementPriority::MOVEMENT_COMBAT); + } + + if (!isHeroic) + return false; + + return PositionHeroicGrid(boss); +} + +bool IccRotfaceGroupPositionAction::PositionHeroicGrid(Unit* boss) +{ + Group* group = bot->GetGroup(); + if (!group) + return false; + + static Position const rangedSpots[] = { + ICC_ROTFACE_RANGED_POSITION_HC_1, ICC_ROTFACE_RANGED_POSITION_HC_2, ICC_ROTFACE_RANGED_POSITION_HC_3, + ICC_ROTFACE_RANGED_POSITION_HC_4, ICC_ROTFACE_RANGED_POSITION_HC_5, ICC_ROTFACE_RANGED_POSITION_HC_6, + ICC_ROTFACE_RANGED_POSITION_HC_7, ICC_ROTFACE_RANGED_POSITION_HC_8, ICC_ROTFACE_RANGED_POSITION_HC_9, + ICC_ROTFACE_RANGED_POSITION_HC_10, ICC_ROTFACE_RANGED_POSITION_HC_11, ICC_ROTFACE_RANGED_POSITION_HC_12, + ICC_ROTFACE_RANGED_POSITION_HC_13, ICC_ROTFACE_RANGED_POSITION_HC_14, ICC_ROTFACE_RANGED_POSITION_HC_15, + ICC_ROTFACE_RANGED_POSITION_HC_16, ICC_ROTFACE_RANGED_POSITION_HC_17, ICC_ROTFACE_RANGED_POSITION_HC_18, + ICC_ROTFACE_RANGED_POSITION_HC_19, ICC_ROTFACE_RANGED_POSITION_HC_20, ICC_ROTFACE_RANGED_POSITION_HC_21, + ICC_ROTFACE_RANGED_POSITION_HC_22, + }; + constexpr int32 totalSpots = 22; + + GuidVector const npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); + std::vector floodPuddles; + for (auto const& npc : npcs) + { + Unit* unit = botAI->GetUnit(npc); + if (unit && botAI->HasAura("Ooze Flood", unit)) + floodPuddles.push_back(unit); + } + + bool spotFlooded[totalSpots] = {}; + for (int32 i = 0; i < totalSpots; ++i) + { + for (Unit* puddle : floodPuddles) + { + if (puddle->GetExactDist2d(rangedSpots[i].GetPositionX(), rangedSpots[i].GetPositionY()) < 30.0f) + { + spotFlooded[i] = true; + break; + } + } + } + + std::vector hunterGuids; + std::vector rangedGuids; + std::vector healerGuids; + for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next()) + { + Player* member = itr->GetSource(); + if (!member || !member->IsAlive() || botAI->IsTank(member)) + continue; + + if (!GET_PLAYERBOT_AI(member)) + continue; + + ObjectGuid const guid = member->GetGUID(); + if (botAI->IsHeal(member)) + healerGuids.push_back(guid); + else if (botAI->IsRanged(member)) + { + if (member->getClass() == CLASS_HUNTER) + hunterGuids.push_back(guid); + else + rangedGuids.push_back(guid); + } + } + std::sort(hunterGuids.begin(), hunterGuids.end()); + std::sort(rangedGuids.begin(), rangedGuids.end()); + std::sort(healerGuids.begin(), healerGuids.end()); + + std::vector ordered; + for (auto const& g : hunterGuids) + ordered.push_back(g); + for (auto const& g : rangedGuids) + ordered.push_back(g); + for (auto const& g : healerGuids) + ordered.push_back(g); + + auto it = std::find(ordered.begin(), ordered.end(), bot->GetGUID()); + if (it == ordered.end()) + return false; + + int32 const botRank = static_cast(std::distance(ordered.begin(), it)); + int32 const totalBots = static_cast(ordered.size()); + + bool const hasHomeSpot = (botRank < totalSpots); + bool const homeSpotSafe = hasHomeSpot && !spotFlooded[botRank]; + + auto moveTowardSpot = [&](Position const& spot) -> bool + { + float const dx = spot.GetPositionX() - bot->GetPositionX(); + float const dy = spot.GetPositionY() - bot->GetPositionY(); + float const dist = std::sqrt(dx * dx + dy * dy); + if (dist <= 1.0f) + return false; + constexpr float moveStep = 7.0f; + float const step = std::min(moveStep, dist); + float const targetX = bot->GetPositionX() + (dx / dist) * step; + float const targetY = bot->GetPositionY() + (dy / dist) * step; + + if (!bot->HasAura(SPELL_NITRO_BOOSTS)) + bot->AddAura(SPELL_NITRO_BOOSTS, bot); + + return MoveTo(bot->GetMapId(), targetX, targetY, spot.GetPositionZ(), false, false, false, true, + MovementPriority::MOVEMENT_COMBAT); + }; + + if (homeSpotSafe) + return moveTowardSpot(rangedSpots[botRank]); + + std::vector tempSpots; + for (int32 i = 0; i < totalSpots; ++i) + { + if (i >= totalBots && !spotFlooded[i]) + tempSpots.push_back(i); + } + + if (hasHomeSpot) + { + int32 displacedRank = 0; + for (int32 rank = 0; rank < totalSpots && rank < totalBots; ++rank) + { + if (rank == botRank) + break; + if (spotFlooded[rank]) + displacedRank++; + } + + if (displacedRank < static_cast(tempSpots.size())) + return moveTowardSpot(rangedSpots[tempSpots[displacedRank]]); + } + + if (boss && boss->HasUnitState(UNIT_STATE_CASTING) && !botAI->IsTank(bot)) + { + float const bx = boss->GetPositionX(); + float const by = boss->GetPositionY(); + float const bz = boss->GetPositionZ(); + if (bot->GetExactDist2d(bx, by) > 0.5f) + return MoveTo(bot->GetMapId(), bx, by, bz, false, false, false, false, MovementPriority::MOVEMENT_FORCED, + true, false); + return false; + } + + if (Group* grp = bot->GetGroup()) + { + for (GroupReference* itr = grp->GetFirstMember(); itr != nullptr; itr = itr->next()) + { + Player* member = itr->GetSource(); + if (!member || !member->IsAlive() || member == bot) + continue; + if (botAI->IsMainTank(member)) + { + if (bot->getClass() == CLASS_HUNTER) + { + float const distToTank = bot->GetExactDist2d(member->GetPositionX(), member->GetPositionY()); + if (distToTank <= 12.0f) + return false; + + if (!bot->HasAura(SPELL_NITRO_BOOSTS)) + bot->AddAura(SPELL_NITRO_BOOSTS, bot); + + return MoveTo(bot->GetMapId(), member->GetPositionX(), member->GetPositionY(), + member->GetPositionZ(), false, false, false, true, MovementPriority::MOVEMENT_COMBAT); + } + + Position const mainTankPos(member->GetPositionX(), member->GetPositionY(), member->GetPositionZ()); + return moveTowardSpot(mainTankPos); + } + } + } + return false; +} + +bool IccRotfaceMoveAwayFromExplosionAction::Execute(Event /*event*/) +{ + if (botAI->IsTank(bot)) + return false; + + Creature* bigOoze = bot->FindNearestCreature(NPC_BIG_OOZE, 100.0f); + bool const castingNow = bigOoze && bigOoze->IsAlive() && bigOoze->HasUnitState(UNIT_STATE_CASTING) && + bigOoze->FindCurrentSpellBySpellId(SPELL_UNSTABLE_OOZE_EXPLOSION); + + uint32 const now = getMSTime(); + bool const stillHolding = _hasEscape && _holdUntil != 0 && now < _holdUntil; + + if (!castingNow && !stillHolding) + { + _hasEscape = false; + _holdUntil = 0; + return false; + } + + if (!bot->HasAura(SPELL_NITRO_BOOSTS)) + bot->AddAura(SPELL_NITRO_BOOSTS, bot); + + Position const anchor = ICC_ROTFACE_CENTER_POSITION_BOSS; + + GuidVector const npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); + std::vector puddles; + for (auto const& guid : npcs) + { + Unit* unit = botAI->GetUnit(guid); + if (unit && botAI->HasAura("Ooze Flood", unit)) + puddles.push_back(unit); + } + + auto isSlotSafe = [&](float x, float y) -> bool + { + for (Unit* puddle : puddles) + { + if (puddle->GetDistance2d(x, y) < 30.0f) + return false; + } + return true; + }; + + constexpr uint32 TOTAL_SLOTS = 25; + constexpr float ESCAPE_RADIUS = 40.0f; + + auto slotPos = [&](uint32 i) -> std::pair + { + float const angle = static_cast(i) * (2.0f * static_cast(M_PI) / static_cast(TOTAL_SLOTS)); + return {anchor.GetPositionX() + ESCAPE_RADIUS * std::cos(angle), + anchor.GetPositionY() + ESCAPE_RADIUS * std::sin(angle)}; + }; + + static std::map, int32> sExplosionSlotMemory; + + uint32 const instanceId = bot->GetMap()->GetInstanceId(); + auto const myKey = std::make_pair(instanceId, bot->GetGUID()); + + // count how many OTHER bots in this instance occupy each slot + std::array otherCount{}; + for (auto const& [key, slot] : sExplosionSlotMemory) + { + if (key.first == instanceId && key.second != bot->GetGUID()) + ++otherCount[slot]; + } + + auto assignSlot = [&]() -> int32 + { + int32 bestSlot = -1; + float bestDist = FLT_MAX; + for (uint32 i = 0; i < TOTAL_SLOTS; ++i) + { + if (otherCount[i] >= 2) + continue; + auto [x, y] = slotPos(i); + if (!isSlotSafe(x, y)) + continue; + float const d = bot->GetExactDist2d(x, y); + if (d < bestDist) + { + bestDist = d; + bestSlot = static_cast(i); + } + } + return bestSlot; + }; + + auto currentSlotIt = sExplosionSlotMemory.find(myKey); + bool needsAssignment = true; + + if (currentSlotIt != sExplosionSlotMemory.end()) + { + int32 const mySlot = currentSlotIt->second; + auto [x, y] = slotPos(mySlot); + if (otherCount[mySlot] < 2 && isSlotSafe(x, y)) + needsAssignment = false; + } + + if (needsAssignment) + { + int32 const slot = assignSlot(); + if (slot >= 0) + sExplosionSlotMemory[myKey] = slot; + else + sExplosionSlotMemory.erase(myKey); + } + + auto finalSlotIt = sExplosionSlotMemory.find(myKey); + + Position escapePos; + if (finalSlotIt != sExplosionSlotMemory.end()) + { + auto [x, y] = slotPos(finalSlotIt->second); + escapePos = Position(x, y, anchor.GetPositionZ()); + } + else + { + escapePos = anchor; + } + + _escapePosition = escapePos; + _hasEscape = true; + + if (castingNow) + { + _holdUntil = now + 2000; + if (bot->GetExactDist2d(_escapePosition) > 2.0f) + return MoveTo(bot->GetMapId(), _escapePosition.GetPositionX(), _escapePosition.GetPositionY(), + _escapePosition.GetPositionZ(), false, false, false, false, + MovementPriority::MOVEMENT_FORCED); + return false; + } + + // Cast finished but still inside 2s hold window: stay at escape spot. + if (bot->GetExactDist2d(_escapePosition) <= 2.0f) + return true; + + return false; +} + +bool IccRotfaceAvoidVileGasAction::Execute(Event /*event*/) +{ + uint32 const now = getMSTime(); + + auto vgIt = IcecrownHelpers::rotfaceVileGas.find(bot->GetMap()->GetInstanceId()); + bool const isVictim = + vgIt != IcecrownHelpers::rotfaceVileGas.end() && + vgIt->second.victimGuid == bot->GetGUID() && + getMSTimeDiff(vgIt->second.castTime, now) < 8000; + bool const hasAura = botAI->HasAura("Vile Gas", bot); + + auto& waitMap = IcecrownHelpers::rotfaceVileGasWaitUntil; + auto waitIt = waitMap.find(bot->GetGUID()); + bool const inWait = waitIt != waitMap.end() && now < waitIt->second; + + if (!isVictim && !hasAura && !inWait) + { + if (waitIt != waitMap.end()) + waitMap.erase(waitIt); + _hasSafeSpot = false; + return false; + } + + Position const anchor = ICC_ROTFACE_CENTER_POSITION_BOSS; + + Creature* bigOoze = bot->FindNearestCreature(NPC_BIG_OOZE, 100.0f); + + GuidVector const npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); + std::vector puddles; + for (auto const& guid : npcs) + { + Unit* unit = botAI->GetUnit(guid); + if (unit && botAI->HasAura("Ooze Flood", unit)) + puddles.push_back(unit); + } + + auto isSafe = [&](float x, float y, float z) -> bool + { + if (bigOoze && bigOoze->IsAlive() && bigOoze->GetDistance2d(x, y) < 20.0f) + return false; + + for (Unit* puddle : puddles) + { + if (puddle->GetDistance2d(x, y) < 30.0f) + return false; + } + + if (!bot->IsWithinLOS(x, y, z)) + return false; + + return true; + }; + + if (!_hasSafeSpot) + { + float const baseAngle = std::atan2(bot->GetPositionY() - anchor.GetPositionY(), + bot->GetPositionX() - anchor.GetPositionX()); + + constexpr float escapeDistance = 40.0f; + constexpr int32 sweepSteps = 24; + constexpr float stepAngle = 2.0f * static_cast(M_PI) / sweepSteps; + + float chosenAngle = baseAngle; + for (int32 i = 0; i < sweepSteps; ++i) + { + int32 const sign = (i % 2 == 0) ? 1 : -1; + int32 const magnitude = (i + 1) / 2; + float const angle = baseAngle + sign * magnitude * stepAngle; + float const x = anchor.GetPositionX() + escapeDistance * std::cos(angle); + float const y = anchor.GetPositionY() + escapeDistance * std::sin(angle); + float const z = anchor.GetPositionZ(); + if (isSafe(x, y, z)) + { + chosenAngle = angle; + break; + } + } + + _safeSpot = Position(anchor.GetPositionX() + escapeDistance * std::cos(chosenAngle), + anchor.GetPositionY() + escapeDistance * std::sin(chosenAngle), + anchor.GetPositionZ()); + _hasSafeSpot = true; + } + + if (bot->GetExactDist2d(_safeSpot) > 2.0f) + { + if (!bot->HasAura(SPELL_NITRO_BOOSTS)) + bot->AddAura(SPELL_NITRO_BOOSTS, bot); + + return MoveTo(bot->GetMapId(), _safeSpot.GetPositionX(), _safeSpot.GetPositionY(), + _safeSpot.GetPositionZ(), false, false, false, false, + MovementPriority::MOVEMENT_FORCED); + } + + if (!inWait) + waitMap[bot->GetGUID()] = now + 4000; + + return true; +} diff --git a/src/Ai/Raid/ICC/Action/ICCActions_SG.cpp b/src/Ai/Raid/ICC/Action/ICCActions_SG.cpp new file mode 100644 index 00000000000..53096aafbf1 --- /dev/null +++ b/src/Ai/Raid/ICC/Action/ICCActions_SG.cpp @@ -0,0 +1,1530 @@ +#include "ICCActions.h" +#include +#include "NearestNpcsValue.h" +#include "ObjectAccessor.h" +#include "Playerbots.h" +#include "Vehicle.h" +#include "RtiValue.h" +#include "GenericSpellActions.h" +#include "GenericActions.h" +#include "ICCTriggers.h" +#include "Multiplier.h" + +bool IccSindragosaGroupPositionAction::Execute(Event /*event*/) +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "sindragosa"); + if (!boss) + return false; + + // Block positioning only during the pre-combat intro fly-in. + // Once Sindragosa is in combat the air phase is handled separately, + // so ground bots must still be allowed to pre-position. + if (boss->HasUnitMovementFlag(MOVEMENTFLAG_DISABLE_GRAVITY) && !boss->IsInCombat()) + return false; + + Aura* aura = botAI->GetAura("mystic buffet", bot, false, true); + if (aura && aura->GetStackAmount() >= 6 && botAI->IsMainTank(bot)) + return false; + + // Route tanks to tank positioning. + // At pull GetVictim() may still be nullptr, so also check IsMainTank so the + // tank is not incorrectly sent to the melee stack before aggro is established. + bool const isTankingBoss = botAI->IsTank(bot) && (boss->GetVictim() == bot || botAI->IsMainTank(bot)); + if (isTankingBoss) + return HandleTankPositioning(boss); + + // Everyone else: boss is not targeting this bot + if (boss->GetVictim() != bot) + return HandleNonTankPositioning(); + + return false; +} + +bool IccSindragosaGroupPositionAction::HandleTankPositioning(Unit* boss) +{ + float const distBossToCenter = boss->GetExactDist2d(ICC_SINDRAGOSA_CENTER_POSITION); + float const distToTankPos = bot->GetExactDist2d(ICC_SINDRAGOSA_TANK_POSITION); + + // Compute how far the boss orientation deviates from east (PI/2) + float const targetOrientation = fmod(float(M_PI) / 2.0f + 2.0f * float(M_PI), 2.0f * float(M_PI)); + float const currentOrientation = fmod(boss->GetOrientation() + 2.0f * float(M_PI), 2.0f * float(M_PI)); + float orientationDiff = currentOrientation - targetOrientation; + + // Clamp difference to [-PI, PI] + if (orientationDiff > float(M_PI)) + orientationDiff -= 2.0f * float(M_PI); + else if (orientationDiff < -float(M_PI)) + orientationDiff += 2.0f * float(M_PI); + + // Stage 1: Drag boss toward the arena centre when it has drifted too far + if (distBossToCenter > 16.0f && distToTankPos <= 20.0f) + { + float const dirX = ICC_SINDRAGOSA_CENTER_POSITION.GetPositionX() - boss->GetPositionX(); + float const dirY = ICC_SINDRAGOSA_CENTER_POSITION.GetPositionY() - boss->GetPositionY(); + + // Step 4 yards past centre to keep the boss moving through it + float const moveX = ICC_SINDRAGOSA_CENTER_POSITION.GetPositionX() + (dirX / distBossToCenter) * 8.0f; + float const moveY = ICC_SINDRAGOSA_CENTER_POSITION.GetPositionY() + (dirY / distBossToCenter) * 8.0f; + + return MoveTo(bot->GetMapId(), moveX, moveY, boss->GetPositionZ(), false, false, false, false, + MovementPriority::MOVEMENT_FORCED, true, false); + } + + // Stage 2: Walk toward the designated tank position + if (distToTankPos > 10.0f) + { + Position const& botPos = bot->GetPosition(); + Position const& tankPos = ICC_SINDRAGOSA_TANK_POSITION; + + float const dx = tankPos.GetPositionX() - botPos.GetPositionX(); + float const dy = tankPos.GetPositionY() - botPos.GetPositionY(); + float const distance = std::hypot(dx, dy); + + // Advance one yard at a time for smooth pathing + float const scale = 1.0f / distance; + float const targetX = botPos.GetPositionX() + dx * scale; + float const targetY = botPos.GetPositionY() + dy * scale; + + return MoveTo(bot->GetMapId(), targetX, targetY, bot->GetPositionZ(), false, false, false, true, + MovementPriority::MOVEMENT_COMBAT, true, false); + } + + // Stage 3: Arc around the boss to correct its facing toward east + if (std::abs(orientationDiff) > 0.15f) + { + float const centerX = boss->GetPositionX(); + float const centerY = boss->GetPositionY(); + float const radius = std::max(2.0f, bot->GetExactDist2d(centerX, centerY)); + + float angle = atan2(bot->GetPositionY() - centerY, bot->GetPositionX() - centerX); + + // Negative diff → step counterclockwise (north); positive → clockwise (south) + static constexpr float ARC_STEP = 0.125f; + angle += (orientationDiff < 0) ? ARC_STEP : -ARC_STEP; + + float const moveX = centerX + radius * cos(angle); + float const moveY = centerY + radius * sin(angle); + + return MoveTo(bot->GetMapId(), moveX, moveY, bot->GetPositionZ(), false, false, false, false, + MovementPriority::MOVEMENT_FORCED, true, false); + } + + // Stage 4: Fine-tune Y-axis alignment with the tank position + float const yDiff = std::abs(bot->GetPositionY() - ICC_SINDRAGOSA_TANK_POSITION.GetPositionY()); + if (yDiff > 2.0f) + { + Position const& botPos = bot->GetPosition(); + Position const& tankPos = ICC_SINDRAGOSA_TANK_POSITION; + + float const newY = botPos.GetPositionY() + (tankPos.GetPositionY() > botPos.GetPositionY() ? 1.0f : -1.0f); + + return MoveTo(bot->GetMapId(), botPos.GetPositionX(), newY, botPos.GetPositionZ(), false, false, false, false, + MovementPriority::MOVEMENT_FORCED, true, false); + } + + return false; +} + +bool IccSindragosaGroupPositionAction::HandleNonTankPositioning() +{ + Group* group = bot->GetGroup(); + if (!group) + return false; + + // Collect all alive raid members + std::vector raidMembers; + for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next()) + { + Player* member = itr->GetSource(); + if (!member || !member->IsAlive()) + continue; + + raidMembers.push_back(member); + } + + uint32 const totalMembers = static_cast(raidMembers.size()); + if (totalMembers == 0) + return false; + + // Count members currently free of Mystic Buffet + uint32 membersWithoutAura = 0; + for (Player* member : raidMembers) + { + if (!botAI->GetAura("mystic buffet", member)) + ++membersWithoutAura; + } + + // Raid is considered "clear" when 60 % or more lack the debuff stack + float const percentageWithoutAura = static_cast(membersWithoutAura) / static_cast(totalMembers); + bool const raidClear = (percentageWithoutAura >= 0.6f); + + static constexpr std::array TombEntries = {NPC_TOMB1, NPC_TOMB2, NPC_TOMB3, NPC_TOMB4}; + static constexpr uint8 SKULL_ICON_INDEX = 7; + + // Priority: if a tank is ice-tombed (ground phase), mark that tomb skull + // immediately so the raid DPSes it and frees the tank. Any bot can issue + // the mark — redundant SetTargetIcon calls are idempotent. + Unit* const bossForFlyCheck = AI_VALUE2(Unit*, "find target", "sindragosa"); + bool const bossGrounded = bossForFlyCheck && + bossForFlyCheck->GetExactDist2d(ICC_SINDRAGOSA_FLYING_POSITION.GetPositionX(), + ICC_SINDRAGOSA_FLYING_POSITION.GetPositionY()) >= 30.0f; + + Player* entombedTank = nullptr; + if (bossGrounded) + { + for (Player* member : raidMembers) + { + if (botAI->IsTank(member) && member->HasAura(SPELL_ICE_TOMB)) + { + entombedTank = member; + break; + } + } + } + + Unit* tankTomb = nullptr; + if (entombedTank) + { + GuidVector const tombGuids = AI_VALUE(GuidVector, "possible targets no los"); + float minDist = 4.0f; + for (uint32 const entry : TombEntries) + { + for (auto const& guid : tombGuids) + { + Unit* unit = botAI->GetUnit(guid); + if (!unit || unit->GetEntry() != entry || !unit->IsAlive()) + continue; + float const d = unit->GetDistance(entombedTank); + if (d < minDist) + { + minDist = d; + tankTomb = unit; + } + } + } + } + + if (tankTomb) + { + ObjectGuid const currentSkull = group->GetTargetIcon(SKULL_ICON_INDEX); + if (currentSkull != tankTomb->GetGUID()) + group->SetTargetIcon(SKULL_ICON_INDEX, bot->GetGUID(), tankTomb->GetGUID()); + } + else if (raidClear && botAI->IsTank(bot)) + { + GuidVector const tombGuids = AI_VALUE(GuidVector, "possible targets no los"); + + Unit* nearestTomb = nullptr; + float minDist = 150.0f; + + for (uint32 const entry : TombEntries) + { + for (auto const& guid : tombGuids) + { + Unit* unit = botAI->GetUnit(guid); + if (!unit || unit->GetEntry() != entry || !unit->IsAlive()) + continue; + + float const dist = bot->GetDistance(unit); + if (dist < minDist) + { + minDist = dist; + nearestTomb = unit; + } + } + } + + // Prefer the nearest tomb; fall back to marking the boss itself + Unit* targetToMark = nearestTomb; + if (!targetToMark) + { + Unit* boss = AI_VALUE2(Unit*, "find target", "sindragosa"); + if (boss && boss->IsAlive()) + targetToMark = boss; + } + + if (targetToMark) + { + ObjectGuid const currentSkull = group->GetTargetIcon(SKULL_ICON_INDEX); + Unit* const currentSkullUnit = botAI->GetUnit(currentSkull); + bool const needsUpdate = + !currentSkullUnit || !currentSkullUnit->IsAlive() || currentSkullUnit != targetToMark; + + if (needsUpdate) + group->SetTargetIcon(SKULL_ICON_INDEX, bot->GetGUID(), targetToMark->GetGUID()); + } + } + + context->GetValue("rti")->Set("skull"); + + if (botAI->IsRanged(bot)) + { + static constexpr float RANGED_TOLERANCE = 9.0f; + static constexpr float RANGED_MAX_STEP = 5.0f; + + if (bot->GetExactDist2d(ICC_SINDRAGOSA_RANGED_POSITION) > RANGED_TOLERANCE) + return MoveIncrementallyToPosition(ICC_SINDRAGOSA_RANGED_POSITION, RANGED_MAX_STEP); + + return false; + } + + static constexpr float MELEE_TOLERANCE = 10.0f; + static constexpr float MELEE_MAX_STEP = 5.0f; + + if (bot->GetExactDist2d(ICC_SINDRAGOSA_MELEE_POSITION) > MELEE_TOLERANCE) + return MoveIncrementallyToPosition(ICC_SINDRAGOSA_MELEE_POSITION, MELEE_MAX_STEP); + + return false; +} + +bool IccSindragosaGroupPositionAction::MoveIncrementallyToPosition(Position const& targetPos, float maxStep) +{ + float const dirX = targetPos.GetPositionX() - bot->GetPositionX(); + float const dirY = targetPos.GetPositionY() - bot->GetPositionY(); + float const length = std::hypot(dirX, dirY); + + float const stepSize = std::min(maxStep, bot->GetExactDist2d(targetPos)); + float const moveX = bot->GetPositionX() + (dirX / length) * stepSize; + float const moveY = bot->GetPositionY() + (dirY / length) * stepSize; + + return MoveTo(bot->GetMapId(), moveX, moveY, targetPos.GetPositionZ(), false, false, false, false, + MovementPriority::MOVEMENT_COMBAT); +} + +bool IccSindragosaFrostBeaconAction::TryDropTombFlares(Unit const* boss) +{ + Group* group = bot->GetGroup(); + if (!group) + return false; + + bool anyBeacon = false; + for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next()) + { + Player* member = itr->GetSource(); + if (member && member->IsAlive() && member->HasAura(SPELL_FROST_BEACON)) + { + anyBeacon = true; + break; + } + } + if (!anyBeacon) + return false; + + // Phase tracking: clear flared sets on phase boundary so each phase gets fresh markers. + // Keyed per-instance so concurrent ICC raids don't share phase state. + uint32 const instanceId = bot->GetInstanceId(); + bool const phase3 = boss->HealthBelowPct(35); + bool& lastPhase3 = s_lastPhase3[instanceId]; + if (phase3 != lastPhase3) + { + s_flaredRedThisPhase[instanceId].clear(); + s_flaredBluePhase3[instanceId] = false; + lastPhase3 = phase3; + } + + // Build position list for current phase. + std::vector> targets; + if (phase3) + { + targets.emplace_back(3, &ICC_SINDRAGOSA_THOMBMB2_POSITION); + } + else + { + Difficulty const diff = bot->GetRaidDifficulty(); + bool const is25Man = (diff == RAID_DIFFICULTY_25MAN_NORMAL || diff == RAID_DIFFICULTY_25MAN_HEROIC); + targets.emplace_back(0, &ICC_SINDRAGOSA_THOMB1_POSITION); + if (is25Man) + targets.emplace_back(1, &ICC_SINDRAGOSA_THOMB2_POSITION); + targets.emplace_back(2, &ICC_SINDRAGOSA_THOMB3_POSITION); + } + + // Build marker pool: alive non-tank bots (skip real players). One bot per position + // sidesteps the item cooldown — each marker only casts once per phase. + std::vector preferred; // non-beaconed + std::vector fallback; // beaconed allowed + for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next()) + { + Player* member = itr->GetSource(); + if (!member || !member->IsAlive()) + continue; + PlayerbotAI* memberAI = sPlayerbotsMgr.GetPlayerbotAI(member); + if (!memberAI) + continue; // real player + if (memberAI->IsTank(member)) + continue; + fallback.push_back(member); + if (!member->HasAura(SPELL_FROST_BEACON)) + preferred.push_back(member); + } + + auto byGuid = [](Player* a, Player* b) { return a->GetGUID() < b->GetGUID(); }; + std::sort(preferred.begin(), preferred.end(), byGuid); + std::sort(fallback.begin(), fallback.end(), byGuid); + + // Assign N markers (one per position) from preferred first, then top up from fallback. + size_t const needed = targets.size(); + std::vector markers; + markers.reserve(needed); + for (Player* p : preferred) + { + if (markers.size() >= needed) + break; + markers.push_back(p); + } + if (markers.size() < needed) + { + for (Player* p : fallback) + { + if (markers.size() >= needed) + break; + if (std::find(markers.begin(), markers.end(), p) != markers.end()) + continue; + markers.push_back(p); + } + } + + // Find this bot's slot in the marker list. + auto myIt = std::find(markers.begin(), markers.end(), bot); + if (myIt == markers.end()) + return false; + size_t const mySlot = std::distance(markers.begin(), myIt); + if (mySlot >= targets.size()) + return false; + + auto const& [chosenIdx, chosenPos] = targets[mySlot]; + + // Already flared by some bot — don't duplicate. + if (phase3 && s_flaredBluePhase3[instanceId]) + return false; + if (!phase3 && s_flaredRedThisPhase[instanceId].count(chosenIdx)) + return false; + + // Skip if a tomb is already standing on this position. + static constexpr std::array tombEntries = {NPC_TOMB1, NPC_TOMB2, NPC_TOMB3, NPC_TOMB4}; + GuidVector const tombGuids = AI_VALUE(GuidVector, "possible targets no los"); + for (uint32 const entry : tombEntries) + { + for (auto const& guid : tombGuids) + { + Unit* unit = botAI->GetUnit(guid); + if (!unit || !unit->IsAlive() || unit->GetEntry() != entry) + continue; + if (unit->GetExactDist2d(chosenPos->GetPositionX(), chosenPos->GetPositionY()) <= 5.0f) + return false; + } + } + + uint32 const itemId = phase3 ? ITEM_BLUE_SMOKE_FLARE : ITEM_RED_SMOKE_FLARE; + if (bot->GetItemCount(itemId, false) == 0) + bot->StoreNewItemInBestSlots(itemId, 10); + + Item* flare = bot->GetItemByEntry(itemId); + if (!flare) + return false; + + if (bot->IsNonMeleeSpellCast(false)) + return false; + if (bot->CanUseItem(flare) != EQUIP_ERR_OK) + return false; + + uint8 const bagIndex = flare->GetBagSlot(); + uint8 const slot = flare->GetSlot(); + uint8 const castCount = 1; + uint32 const spellId = flare->GetTemplate()->Spells[0].SpellId; + ObjectGuid const itemGuid = flare->GetGUID(); + uint32 const glyphIndex = 0; + uint8 const castFlags = 0; + + WorldPacket packet(CMSG_USE_ITEM); + packet << bagIndex << slot << castCount << spellId << itemGuid << glyphIndex << castFlags; + packet << uint32(TARGET_FLAG_DEST_LOCATION); + packet.appendPackGUID(0); + packet << chosenPos->GetPositionX() << chosenPos->GetPositionY() << chosenPos->GetPositionZ(); + + bot->GetSession()->HandleUseItemOpcode(packet); + if (phase3) + s_flaredBluePhase3[instanceId] = true; + else + s_flaredRedThisPhase[instanceId].insert(chosenIdx); + return false; +} + +// Todo not really used since tigger is bypassed +bool IccSindragosaTankSwapPositionAction::Execute(Event /*event*/) +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "sindragosa"); + if (!boss) + return false; + + if (!botAI->IsAssistTank(bot)) + return false; + + // Keep the assist tank on the swap position with a tight tolerance + if (bot->GetExactDist2d(ICC_SINDRAGOSA_TANK_POSITION) > 3.0f) + { + return MoveTo(bot->GetMapId(), ICC_SINDRAGOSA_TANK_POSITION.GetPositionX(), + ICC_SINDRAGOSA_TANK_POSITION.GetPositionY(), ICC_SINDRAGOSA_TANK_POSITION.GetPositionZ(), false, + false, false, false, MovementPriority::MOVEMENT_FORCED, true, false); + } + + return false; +} + +std::map> IccSindragosaFrostBeaconAction::s_flaredRedThisPhase; +std::map IccSindragosaFrostBeaconAction::s_flaredBluePhase3; +std::map IccSindragosaFrostBeaconAction::s_lastPhase3; +uint32 IccSindragosaFrostBeaconAction::s_nextFlareMs = 0; // deprecated, kept for ABI of header + +bool IccSindragosaFrostBeaconAction::Execute(Event /*event*/) +{ + Unit* boss = bot->FindNearestCreature(NPC_SINDRAGOSA, 200.0f); + if (!boss) + return false; + + TryDropTombFlares(boss); + + HandleSupportActions(); + + if (bot->HasAura(SPELL_FROST_BEACON)) + { + return HandleBeaconedPlayer(boss); + } + + return HandleNonBeaconedPlayer(boss); +} + +bool IccSindragosaFrostBeaconAction::HandleSupportActions() +{ + Group* group = bot->GetGroup(); + + // Tank support - Paladin Hand of Freedom + if (group && bot->getClass() == CLASS_PALADIN) + { + for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next()) + { + Player* member = itr->GetSource(); + if (!member || !member->IsAlive() || !botAI->IsTank(member)) + { + continue; + } + + if (botAI->GetAura("Frost Breath", member) && !member->HasAura(SPELL_HAND_OF_FREEDOM)) + { + botAI->CastSpell(SPELL_HAND_OF_FREEDOM, member); + break; + } + } + } + + return false; +} + +bool IccSindragosaHotAction::Execute(Event /*event*/) +{ + if (!botAI->IsHeal(bot) || bot->HasAura(SPELL_FROST_BEACON)) + return false; + + auto const members = AI_VALUE(GuidVector, "group members"); + for (auto const& memberGuid : members) + { + Unit* member = botAI->GetUnit(memberGuid); + if (!member || !member->IsAlive() || !member->HasAura(SPELL_FROST_BEACON)) + continue; + + if (member->HasAura(SPELL_ICE_TOMB)) + continue; + + uint32 spellId = 0; + switch (bot->getClass()) + { + case CLASS_PRIEST: + spellId = 48068; // Renew + break; + case CLASS_SHAMAN: + spellId = 61301; // Riptide + break; + case CLASS_DRUID: + spellId = 48441; // Rejuvenation + break; + default: + return false; + } + + if (!member->HasAura(spellId) && botAI->CanCastSpell(spellId, member)) + botAI->CastSpell(spellId, member); + } + + return false; +} + +bool IccSindragosaFrostBeaconAction::HandleBeaconedPlayer(const Unit* boss) +{ + // Phase 3 positioning (below 35% health, not flying) + if (boss->HealthBelowPct(35) && !IsBossFlying(boss)) + { + if (!bot->HasAura(SPELL_NITRO_BOOSTS)) + bot->AddAura(SPELL_NITRO_BOOSTS, bot); + botAI->Reset(); + return MoveToPositionIfNeeded(ICC_SINDRAGOSA_THOMBMB2_POSITION, POSITION_TOLERANCE); + } + + // Regular beacon positioning using tomb spots + Group* group = bot->GetGroup(); + if (!group) + { + return false; + } + + // Collect and sort beaconed players by GUID for deterministic assignment + std::vector beaconedPlayers; + for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next()) + { + Player* member = itr->GetSource(); + if (member && member->IsAlive() && member->HasAura(SPELL_FROST_BEACON)) + beaconedPlayers.push_back(member); + } + + std::sort(beaconedPlayers.begin(), beaconedPlayers.end(), + [](const Player* a, const Player* b) { return a->GetGUID() < b->GetGUID(); }); + + // Find this bot's index + auto const it = std::find(beaconedPlayers.begin(), beaconedPlayers.end(), bot); + if (it == beaconedPlayers.end()) + return false; + + size_t const myIndex = std::distance(beaconedPlayers.begin(), it); + size_t const beaconCount = beaconedPlayers.size(); + + // Calculate tomb spot based on beacon count + size_t spot = 0; + switch (beaconCount) + { + case 2: + spot = (myIndex == 0) ? 0 : 2; + break; + case 5: + spot = (myIndex < 2) ? 0 : ((myIndex == 2) ? 1 : 2); + break; + case 6: + spot = myIndex / 2; + break; + default: + spot = myIndex % 3; + break; + } + + // Get tomb position and move if needed + static constexpr std::array tombPositions = { + &ICC_SINDRAGOSA_THOMB1_POSITION, &ICC_SINDRAGOSA_THOMB2_POSITION, &ICC_SINDRAGOSA_THOMB3_POSITION}; + + const Position& tombPosition = *tombPositions[std::min(spot, tombPositions.size() - 1)]; + return MoveToPositionIfNeeded(tombPosition, TOMB_POSITION_TOLERANCE); +} + +bool IccSindragosaFrostBeaconAction::HandleNonBeaconedPlayer(const Unit* boss) +{ + // Collect beaconed players + std::vector beaconedPlayers; + auto const members = AI_VALUE(GuidVector, "group members"); + for (auto const& memberGuid : members) + { + Unit* player = botAI->GetUnit(memberGuid); + if (player && player->GetGUID() != bot->GetGUID() && player->HasAura(SPELL_FROST_BEACON)) + { + beaconedPlayers.push_back(player); + } + } + + if (beaconedPlayers.empty()) + { + return false; + } + + // Air phase positioning + if (IsBossFlying(boss)) + { + if (!bot->HasAura(SPELL_FROST_BEACON)) + { + const Difficulty diff = bot->GetRaidDifficulty(); + bool is25Man = false; + if (diff && (diff == RAID_DIFFICULTY_25MAN_NORMAL || diff == RAID_DIFFICULTY_25MAN_HEROIC)) + is25Man = true; + + const Position& safePosition = is25Man ? ICC_SINDRAGOSA_FBOMB_POSITION : ICC_SINDRAGOSA_FBOMB10_POSITION; + + float const dist = bot->GetExactDist2d(safePosition.GetPositionX(), safePosition.GetPositionY()); + if (dist > MOVE_TOLERANCE) + { + return MoveToPosition(safePosition); + } + + } + return botAI->IsHeal(bot); // Continue for healers, wait for others + } + + // Ground phase - position based on role and avoid beaconed players + bool const isRanged = botAI->IsRanged(bot) && !botAI->IsHeal(bot) /*(bot->GetExactDist2d(ICC_SINDRAGOSA_RANGED_POSITION.GetPositionX(),ICC_SINDRAGOSA_RANGED_POSITION.GetPositionY()) < + bot->GetExactDist2d(ICC_SINDRAGOSA_MELEE_POSITION.GetPositionX(),ICC_SINDRAGOSA_MELEE_POSITION.GetPositionY()))*/; + + const Position& targetPosition = isRanged ? ICC_SINDRAGOSA_RANGED_POSITION : ICC_SINDRAGOSA_MELEE_POSITION; + + float const deltaX = std::abs(targetPosition.GetPositionX() - bot->GetPositionX()); + float const deltaY = std::abs(targetPosition.GetPositionY() - bot->GetPositionY()); + if (boss && boss->GetVictim() != bot) + { + if ((deltaX > MOVE_TOLERANCE) || (deltaY > MOVE_TOLERANCE)) + { + if (bot->HasUnitState(UNIT_STATE_CASTING)) + { + botAI->Reset(); + } + return MoveToPosition(targetPosition); + } + } + return false; +} + +bool IccSindragosaFrostBeaconAction::MoveToPositionIfNeeded(const Position& position, float tolerance) +{ + float const distance = bot->GetExactDist2d(position.GetPositionX(), position.GetPositionY()); + if (distance > tolerance) + { + return MoveToPosition(position); + } + return distance <= tolerance; +} + +bool IccSindragosaFrostBeaconAction::MoveToPosition(const Position& position) +{ + float posX = position.GetPositionX(); + float posY = position.GetPositionY(); + float posZ = position.GetPositionZ(); + + bot->UpdateAllowedPositionZ(posX, posY, posZ); + + return MoveTo(bot->GetMapId(), posX, posY, posZ, false, false, false, false, MovementPriority::MOVEMENT_FORCED, + true, false); +} + +bool IccSindragosaFrostBeaconAction::IsBossFlying(const Unit* boss) +{ + return boss->GetExactDist2d(ICC_SINDRAGOSA_FLYING_POSITION.GetPositionX(), + ICC_SINDRAGOSA_FLYING_POSITION.GetPositionY()) < 30.0f; +} + +bool IccSindragosaBlisteringColdAction::Execute(Event /*event*/) +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "sindragosa"); + if (!boss) + return false; + + // Only non-tanks should move out + if (botAI->IsMainTank(bot)) + return false; + + float dist = bot->GetExactDist2d(boss->GetPositionX(), boss->GetPositionY()); + + if (dist >= 33.0f) + return false; + + Position const& targetPos = ICC_SINDRAGOSA_BLISTERING_COLD_POSITION; + + // Only move if we're too close to the boss (< 30 yards) + if (dist < 33.0f) + { + + float const STEP_SIZE = 15.0f; + float distToTarget = bot->GetDistance2d(targetPos.GetPositionX(), targetPos.GetPositionY()); + + if (distToTarget > 0.1f) // Avoid division by zero + { + if (!bot->HasAura(SPELL_NITRO_BOOSTS)) + bot->AddAura(SPELL_NITRO_BOOSTS, bot); + // Calculate direction vector + float dirX = targetPos.GetPositionX() - bot->GetPositionX(); + float dirY = targetPos.GetPositionY() - bot->GetPositionY(); + + // Normalize direction vector + float length = sqrt(dirX * dirX + dirY * dirY); + dirX /= length; + dirY /= length; + + // Move STEP_SIZE yards in that direction + float moveX = bot->GetPositionX() + dirX * STEP_SIZE; + float moveY = bot->GetPositionY() + dirY * STEP_SIZE; + + return MoveTo(bot->GetMapId(), moveX, moveY, bot->GetPositionZ(), + false, false, false, true, MovementPriority::MOVEMENT_FORCED, true, false); + } + } + return false; +} + +bool IccSindragosaUnchainedMagicAction::Execute(Event /*event*/) +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "sindragosa"); + if (!boss) + return false; + + Aura* aura = botAI->GetAura("Unchained Magic", bot, false, true); + if (!aura) + return false; + + Aura* aura1 = botAI->GetAura("Instability", bot, false, true); + + Difficulty diff = bot->GetRaidDifficulty(); + if (aura && (diff == RAID_DIFFICULTY_10MAN_NORMAL || diff == RAID_DIFFICULTY_25MAN_NORMAL)) + { + if (aura1 && aura1->GetStackAmount() >= 6) + return true; // Stop casting spells + } + + return false; +} + +bool IccSindragosaChilledToTheBoneAction::Execute(Event /*event*/) +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "sindragosa"); + if (!boss) + return false; + + Aura* aura = botAI->GetAura("Chilled to the Bone", bot, false, true); + if (!aura) + return false; + + if (aura) // Chilled to the Bone + { + if (aura->GetStackAmount() >= 6) + { + botAI->Reset(); + bot->AttackStop(); + return true; + } + } + + return false; +} + +bool IccSindragosaMysticBuffetAction::Execute(Event /*event*/) +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "sindragosa"); + if (!boss || !bot || !bot->IsAlive()) + return false; + + // Check if we have Mystic Buffet + Aura* aura = botAI->GetAura("mystic buffet", bot, false, true); + if (!aura) + return false; + + if (boss->GetVictim() == bot) + return false; + + // Skip if we have Frost Beacon + if (bot->HasAura(SPELL_FROST_BEACON)) + return false; + + Group* group = bot->GetGroup(); + if (!group) + return false; + + static const std::array tombEntries = {NPC_TOMB1, NPC_TOMB2, NPC_TOMB3, NPC_TOMB4}; + const GuidVector tombGuids = AI_VALUE(GuidVector, "possible targets no los"); + + Unit* nearestTomb = nullptr; + float minDist = 150.0f; + + for (auto const entry : tombEntries) + { + for (auto const& guid : tombGuids) + { + if (Unit* unit = botAI->GetUnit(guid)) + { + if (unit->GetEntry() == entry && unit->IsAlive()) + { + float dist = bot->GetDistance(unit); + if (dist < minDist) + { + minDist = dist; + nearestTomb = unit; + } + } + } + } + } + + // Check if anyone in group has Frost Beacon (SPELL_FROST_BEACON) + bool anyoneHasFrostBeacon = false; + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + if (member && member->IsAlive() && member->HasAura(SPELL_FROST_BEACON)) + { + anyoneHasFrostBeacon = true; + break; + } + } + + bool tombPresent = nearestTomb != nullptr; + bool atLOS2 = bot->GetExactDist2d(ICC_SINDRAGOSA_LOS2_POSITION.GetPositionX(), + ICC_SINDRAGOSA_LOS2_POSITION.GetPositionY()) <= 2.0f; + + // Move to LOS2 position if: tomb is present and no one has Frost Beacon + bool shouldMoveLOS2 = tombPresent && !anyoneHasFrostBeacon; + + if (shouldMoveLOS2) + { + // If already at LOS2: instead of idling while stacks drop, DPS the LOS + // tomb down to MYSTIC_BUFFET_TOMB_STOP_HP_PCT so the wait isn't wasted. + // Single skull icon for the whole raid — pick the tomb with lowest GUID + // so every bot converges deterministically on the same target. + if (atLOS2 && aura && !botAI->IsHeal(bot)) + { + constexpr uint8 SKULL_ICON = 7; + float MYSTIC_BUFFET_TOMB_STOP_HP_PCT = 50.0f; + Difficulty const diff = bot->GetRaidDifficulty(); + + if (diff && (diff == RAID_DIFFICULTY_10MAN_HEROIC)) + MYSTIC_BUFFET_TOMB_STOP_HP_PCT = 90.0f; + + Unit* tombToMark = nullptr; + ObjectGuid bestGuid; + for (auto const entry : tombEntries) + { + for (auto const& guid : tombGuids) + { + Unit* unit = botAI->GetUnit(guid); + if (!unit || !unit->IsAlive() || unit->GetEntry() != entry) + continue; + if (unit->GetHealthPct() <= MYSTIC_BUFFET_TOMB_STOP_HP_PCT) + continue; + if (!tombToMark || unit->GetGUID() < bestGuid) + { + tombToMark = unit; + bestGuid = unit->GetGUID(); + } + } + } + + if (!tombToMark) + { + ObjectGuid const currentIcon = group->GetTargetIcon(SKULL_ICON); + if (!currentIcon.IsEmpty()) + group->SetTargetIcon(SKULL_ICON, bot->GetGUID(), ObjectGuid::Empty); + return true; + } + + context->GetValue("rti")->Set("skull"); + + Unit* currentIconUnit = botAI->GetUnit(group->GetTargetIcon(SKULL_ICON)); + if (!currentIconUnit || !currentIconUnit->IsAlive() || currentIconUnit != tombToMark) + group->SetTargetIcon(SKULL_ICON, bot->GetGUID(), tombToMark->GetGUID()); + + return false; + } + + botAI->Reset(); + // Move to LOS2 position + return MoveTo(bot->GetMapId(), ICC_SINDRAGOSA_LOS2_POSITION.GetPositionX(), + ICC_SINDRAGOSA_LOS2_POSITION.GetPositionY(), ICC_SINDRAGOSA_LOS2_POSITION.GetPositionZ(), false, + false, false, true, MovementPriority::MOVEMENT_FORCED); + } + return false; +} + +std::map, int> IccSindragosaFrostBombAction::s_groupAssignments; +std::map, ObjectGuid> IccSindragosaFrostBombAction::s_tombAssignments; +std::set> IccSindragosaFrostBombAction::s_freedFallback; +std::map, IccSindragosaFrostBombAction::LastLosMove> + IccSindragosaFrostBombAction::s_lastLosMove; + +bool IccSindragosaFrostBombAction::Execute(Event /*event*/) +{ + if (!bot || !bot->IsAlive()) + return false; + + Group* group = bot->GetGroup(); + if (!group) + return false; + + if (bot->HasAura(SPELL_ICE_TOMB)) + { + PinGroupToCurrentZone(); + s_freedFallback.insert(std::make_pair(bot->GetInstanceId(), bot->GetGUID())); + return false; + } + + FrostBombContext ctx; + if (!CollectContext(ctx)) + { + bot->AttackStop(); + return true; + } + + int const groupIndex = ResolveGroupIndex(group); + if (groupIndex < 0) + return false; + + Difficulty const diff = bot->GetRaidDifficulty(); + int const groupCount = (diff == RAID_DIFFICULTY_25MAN_NORMAL || diff == RAID_DIFFICULTY_25MAN_HEROIC) ? 3 : 2; + + // Fixed tomb zone positions per group: + // 25-man (3 groups): group 0→THOMB1, group 1→THOMB2, group 2→THOMB3 + // 10-man (2 groups): group 0→THOMB1, group 1→THOMB3 (beacons skip THOMB2 in 2-beacon case) + static constexpr std::array tombZones = { + &ICC_SINDRAGOSA_THOMB1_POSITION, + &ICC_SINDRAGOSA_THOMB2_POSITION, + &ICC_SINDRAGOSA_THOMB3_POSITION + }; + int const zoneIdx = (groupCount == 2) ? (groupIndex == 0 ? 0 : 2) : groupIndex; + Position const& myZone = *tombZones[zoneIdx]; + + std::vector myTombs = SelectTombs(ctx.tombs, groupIndex, groupCount); + + bool myZoneAllProtected = false; + { + static constexpr std::array raidIcons = {7, 6, 0}; + static constexpr float STRIP_HP_PCT = 30.0f; + bool const is10Man = + (diff == RAID_DIFFICULTY_10MAN_NORMAL || diff == RAID_DIFFICULTY_10MAN_HEROIC); + float const tombStopHpPct = is10Man ? 60.0f : 40.0f; + + auto isMarked = [&](Unit* tomb) -> bool + { + for (uint8 const icon : raidIcons) + if (group->GetTargetIcon(icon) == tomb->GetGUID()) + return true; + return false; + }; + + bool anyAlive = false; + bool anyKillable = false; + for (Unit* tomb : myTombs) + { + if (!tomb || !tomb->IsAlive()) + continue; + anyAlive = true; + bool const marked = isMarked(tomb); + + if (!marked && tomb->GetHealthPct() < STRIP_HP_PCT) + { + Unit::AuraMap& auras = tomb->GetOwnedAuras(); + for (Unit::AuraMap::iterator it = auras.begin(); it != auras.end();) + { + Aura* aura = it->second; + if (aura && aura->GetDuration() != -1) + { + tomb->RemoveOwnedAura(it); + continue; + } + ++it; + } + } + (void)marked; + if (tomb->GetHealthPct() > tombStopHpPct) + anyKillable = true; + } + + myZoneAllProtected = anyAlive && !anyKillable; + + std::vector pets; + if (Pet* mainPet = bot->GetPet()) + pets.push_back(mainPet); + for (Unit* controlled : bot->m_Controlled) + { + if (Creature* c = dynamic_cast(controlled)) + { + if (std::find(pets.begin(), pets.end(), c) == pets.end()) + pets.push_back(c); + } + } + + if (myZoneAllProtected) + { + for (Creature* pet : pets) + { + if (!pet || !pet->IsAlive()) + continue; + pet->SetReactState(REACT_PASSIVE); + pet->AttackStop(); + pet->InterruptNonMeleeSpells(true); + pet->CombatStop(); + pet->SetTarget(ObjectGuid::Empty); + if (CharmInfo* ci = pet->GetCharmInfo()) + { + ci->SetPlayerReactState(REACT_PASSIVE); + pet->GetMotionMaster()->MoveFollow(bot, PET_FOLLOW_DIST, + pet->GetFollowAngle()); + ci->SetCommandState(COMMAND_FOLLOW); + ci->SetIsCommandAttack(false); + ci->SetIsAtStay(false); + ci->SetIsReturning(true); + ci->SetIsFollowing(true); + } + } + } + else + { + for (Creature* pet : pets) + { + if (pet && pet->IsAlive() && pet->GetReactState() == REACT_PASSIVE) + { + pet->SetReactState(REACT_DEFENSIVE); + if (CharmInfo* ci = pet->GetCharmInfo()) + ci->SetPlayerReactState(REACT_DEFENSIVE); + } + } + } + } + + // No tomb in zone + if (myTombs.empty()) + { + // Freed-from-tomb fallback: hide behind the nearest alive tomb anywhere + // in the arena rather than running across to our pinned zone anchor. + if (s_freedFallback.count(std::make_pair(bot->GetInstanceId(), bot->GetGUID()))) + { + Unit* nearest = nullptr; + float minDist = std::numeric_limits::max(); + for (Unit* tomb : ctx.tombs) + { + if (!tomb || !tomb->IsAlive()) + continue; + float const d = bot->GetExactDist2d(tomb); + if (d < minDist) + { + minDist = d; + nearest = tomb; + } + } + + if (nearest) + { + float const fbAngle = ctx.marker->GetAngle(nearest); + float const fbX = nearest->GetPositionX() + std::cos(fbAngle) * 6.5f; + float const fbY = nearest->GetPositionY() + std::sin(fbAngle) * 6.5f; + float const fbZ = nearest->GetPositionZ(); + + if (bot->GetDistance2d(fbX, fbY) > 0.1f) + { + botAI->Reset(); + bot->AttackStop(); + return MoveTo(bot->GetMapId(), fbX, fbY, fbZ, false, false, false, true, + MovementPriority::MOVEMENT_FORCED); + } + return false; + } + } + + // Default: pre-position behind the zone's anchor point + float const preAngle = ctx.marker->GetAngle(myZone.GetPositionX(), myZone.GetPositionY()); + float const preX = myZone.GetPositionX() + std::cos(preAngle) * 6.5f; + float const preY = myZone.GetPositionY() + std::sin(preAngle) * 6.5f; + + if (bot->GetDistance2d(preX, preY) > 3.0f) + { + botAI->Reset(); + bot->AttackStop(); + return MoveTo(bot->GetMapId(), preX, preY, myZone.GetPositionZ(), false, false, false, true, + MovementPriority::MOVEMENT_FORCED); + } + return false; + } + + // Pinned zone has tombs again — exit fallback mode + auto const losKey = std::make_pair(bot->GetInstanceId(), bot->GetGUID()); + s_freedFallback.erase(losKey); + + Unit* losTomb = ResolveStickyTomb(myTombs); + if (!losTomb) + { + // LOS tomb died / lost mark mid-walk. If we recently issued an LOS + // move, replay it for up to 2 seconds so the bot finishes its path + // instead of freezing in the open until the next valid sticky tomb. + auto it = s_lastLosMove.find(losKey); + if (it != s_lastLosMove.end()) + { + uint32 const now = getMSTime(); + if (getMSTimeDiff(it->second.timestampMs, now) <= 2000 && + bot->GetDistance2d(it->second.x, it->second.y) > 0.1f) + { + botAI->Reset(); + bot->AttackStop(); + return MoveTo(bot->GetMapId(), it->second.x, it->second.y, it->second.z, + false, false, false, true, MovementPriority::MOVEMENT_FORCED); + } + s_lastLosMove.erase(it); + } + return false; + } + + float const angle = ctx.marker->GetAngle(losTomb); + float const posX = losTomb->GetPositionX() + std::cos(angle) * 6.5f; + float const posY = losTomb->GetPositionY() + std::sin(angle) * 6.5f; + float const posZ = losTomb->GetPositionZ(); + + float const losDist = bot->GetDistance2d(posX, posY); + if (losDist > 0.1f) + { + botAI->Reset(); + bot->AttackStop(); + // Mark the tomb early (within 5yd) so the raid converges on the kill + // target while the bot is still walking the last few yards into LOS. + if (losDist <= 10.0f) + HandleRtiMarking(group, groupIndex, myTombs, losTomb); + + // Stamp this LOS move so we can replay it for up to 2 seconds if the + // tomb dies/loses mark before we arrive. + LastLosMove& stamp = s_lastLosMove[losKey]; + stamp.timestampMs = getMSTime(); + stamp.x = posX; + stamp.y = posY; + stamp.z = posZ; + + return MoveTo(bot->GetMapId(), posX, posY, posZ, false, false, false, true, MovementPriority::MOVEMENT_FORCED); + } + + // Reached LOS spot — clear the replay stamp. + s_lastLosMove.erase(losKey); + + // Bot is parked at LOS spot. Face away from the LOS tomb only when our + // zone has no kill-candidates left (every remaining tomb is protected). + // While extras are still up, the bot must keep facing them so it can DPS. + if (myZoneAllProtected) + { + bot->AttackStop(); + bot->InterruptNonMeleeSpells(true); + bot->SetTarget(ObjectGuid::Empty); + bot->SetFacingTo(losTomb->GetAngle(bot)); + } + + return HandleRtiMarking(group, groupIndex, myTombs, losTomb); +} + +bool IccSindragosaFrostBombAction::CollectContext(FrostBombContext& ctx) const +{ + constexpr uint32 tombEntries[] = {NPC_TOMB1, NPC_TOMB2, NPC_TOMB3, NPC_TOMB4}; + + std::list units; + float const range = 200.0f; + Acore::AnyUnitInObjectRangeCheck check(bot, range); + Acore::UnitListSearcher searcher(bot, units, check); + Cell::VisitObjects(bot, searcher, range); + + for (Unit* unit : units) + { + if (!unit || !unit->IsAlive()) + continue; + + if (unit->HasAura(SPELL_FROST_BOMB_VISUAL)) + ctx.marker = unit; + + for (uint32 entry : tombEntries) + { + if (unit->GetEntry() == entry) + { + ctx.tombs.push_back(unit); + break; + } + } + } + + return ctx.marker && !ctx.tombs.empty(); +} + +int IccSindragosaFrostBombAction::ResolveGroupIndex(Group* group) const +{ + // Collect bot-only GUIDs from the current group snapshot. + // Real players are excluded so they are never assigned a group index and + // never become the designated marker; only bots manage icons. + uint32 const instanceId = bot->GetInstanceId(); + std::vector currentGuids; + for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next()) + { + Player* member = itr->GetSource(); + if (!member || !sPlayerbotsMgr.GetPlayerbotAI(member)) + continue; + currentGuids.push_back(member->GetGUID()); + } + std::sort(currentGuids.begin(), currentGuids.end()); + + Difficulty const diff = bot->GetRaidDifficulty(); + int const groupCount = (diff == RAID_DIFFICULTY_25MAN_NORMAL || diff == RAID_DIFFICULTY_25MAN_HEROIC) ? 3 : 2; + + // Assign any GUIDs not yet seen, preserving all existing assignments. + // Never clear s_groupAssignments — this ensures bots that temporarily + // drop from the group (die, get entombed) keep their original group index + // and don't trigger a full reshuffle that migrates other bots. + for (ObjectGuid const& guid : currentGuids) + { + auto const key = std::make_pair(instanceId, guid); + if (s_groupAssignments.find(key) == s_groupAssignments.end()) + { + // Assign to the group with the fewest members so far (this instance only) + std::array counts = {}; + for (auto const& [k, idx] : s_groupAssignments) + if (k.first == instanceId && idx < groupCount) + ++counts[idx]; + + int minGroup = 0; + for (int g = 1; g < groupCount; ++g) + if (counts[g] < counts[minGroup]) + minGroup = g; + + s_groupAssignments[key] = minGroup; + } + } + + auto it = s_groupAssignments.find(std::make_pair(instanceId, bot->GetGUID())); + return it != s_groupAssignments.end() ? it->second : -1; +} + +void IccSindragosaFrostBombAction::PinGroupToCurrentZone() +{ + static constexpr std::array tombZones = { + &ICC_SINDRAGOSA_THOMB1_POSITION, + &ICC_SINDRAGOSA_THOMB2_POSITION, + &ICC_SINDRAGOSA_THOMB3_POSITION + }; + + Difficulty const diff = bot->GetRaidDifficulty(); + int const groupCount = + (diff == RAID_DIFFICULTY_25MAN_NORMAL || diff == RAID_DIFFICULTY_25MAN_HEROIC) ? 3 : 2; + + // Find the tomb the bot is inside (ice tomb spawns on top of the frozen bot). + // Mapping the bot's group via the actual tomb position guarantees consistency + // with SelectTombs, which classifies tombs by nearest anchor. Falling back to + // raw bot position vs anchor would mis-pin when the boss/beacon dragged the + // bot into a different zone than where the tomb was assigned. + constexpr uint32 tombEntries[] = {NPC_TOMB1, NPC_TOMB2, NPC_TOMB3, NPC_TOMB4}; + + std::list units; + float const range = 15.0f; + Acore::AnyUnitInObjectRangeCheck check(bot, range); + Acore::UnitListSearcher searcher(bot, units, check); + Cell::VisitObjects(bot, searcher, range); + + Unit* myTomb = nullptr; + float minDist = std::numeric_limits::max(); + for (Unit* unit : units) + { + if (!unit || !unit->IsAlive()) + continue; + bool isTomb = false; + for (uint32 entry : tombEntries) + { + if (unit->GetEntry() == entry) + { + isTomb = true; + break; + } + } + if (!isTomb) + continue; + float const d = bot->GetExactDist2d(unit); + if (d < minDist) + { + minDist = d; + myTomb = unit; + } + } + + int bestGroup = 0; + float bestDist = std::numeric_limits::max(); + + // Anchor-from-tomb path (preferred): map nearest tomb -> nearest anchor -> group. + if (myTomb) + { + for (int g = 0; g < groupCount; ++g) + { + int const zoneIdx = (groupCount == 2) ? (g == 0 ? 0 : 2) : g; + float const d = myTomb->GetExactDist2d(*tombZones[zoneIdx]); + if (d < bestDist) + { + bestDist = d; + bestGroup = g; + } + } + } + else + { + // Fallback: use bot position if no tomb visible (shouldn't happen while tombed, + // but guard against odd states). + for (int g = 0; g < groupCount; ++g) + { + int const zoneIdx = (groupCount == 2) ? (g == 0 ? 0 : 2) : g; + float const d = bot->GetExactDist2d(*tombZones[zoneIdx]); + if (d < bestDist) + { + bestDist = d; + bestGroup = g; + } + } + } + + s_groupAssignments[std::make_pair(bot->GetInstanceId(), bot->GetGUID())] = bestGroup; +} + +std::vector IccSindragosaFrostBombAction::SelectTombs(std::vector const& tombs, int groupIndex, int groupCount) const +{ + if (tombs.empty()) + return {}; + + // Map group index to its intended zone anchor + static constexpr std::array tombZones = { + &ICC_SINDRAGOSA_THOMB1_POSITION, + &ICC_SINDRAGOSA_THOMB2_POSITION, + &ICC_SINDRAGOSA_THOMB3_POSITION + }; + int const zoneIdx = (groupCount == 2) ? (groupIndex == 0 ? 0 : 2) : groupIndex; + Position const& zone = *tombZones[zoneIdx]; + + static constexpr float MAX_ZONE_RADIUS = 3.0f; + std::vector zoneTombs; + for (Unit* tomb : tombs) + { + int closestZone = 0; + float closestDist = tomb->GetExactDist2d(*tombZones[0]); + for (int z = 1; z < 3; ++z) + { + // Skip zone 1 (THOMB2) in 2-group mode — it is never assigned + if (groupCount == 2 && z == 1) + continue; + float d = tomb->GetExactDist2d(*tombZones[z]); + if (d < closestDist) + { + closestDist = d; + closestZone = z; + } + } + if (closestZone != zoneIdx) + continue; + if (closestDist > MAX_ZONE_RADIUS) + continue; + zoneTombs.push_back(tomb); + } + return zoneTombs; +} + +Unit* IccSindragosaFrostBombAction::ResolveStickyTomb(std::vector const& myTombs) +{ + // Keep the previously assigned tomb while it is still a valid member of + // this group's zone. This avoids the cascading reassignment where a tomb's + // HP fluctuation causes every bot to flip to a different LOS spot each tick. + auto const key = std::make_pair(bot->GetInstanceId(), bot->GetGUID()); + auto it = s_tombAssignments.find(key); + if (it != s_tombAssignments.end()) + { + for (Unit* tomb : myTombs) + { + if (tomb->GetGUID() == it->second && tomb->IsAlive()) + return tomb; + } + } + + // No valid sticky — pick the tomb nearest to the bot so the first + // assignment snaps to where the bot already stands. + Unit* nearest = nullptr; + float minDist = std::numeric_limits::max(); + for (Unit* tomb : myTombs) + { + if (!tomb->IsAlive()) + continue; + float const d = bot->GetExactDist2d(tomb); + if (d < minDist) + { + minDist = d; + nearest = tomb; + } + } + + if (nearest) + s_tombAssignments[key] = nearest->GetGUID(); + else + s_tombAssignments.erase(key); + + return nearest; +} + +bool IccSindragosaFrostBombAction::HandleRtiMarking(Group* group, int groupIndex, std::vector const& myTombs, Unit* losTomb) +{ + constexpr uint8 SKULL_ICON = 7; + constexpr uint8 CROSS_ICON = 6; + constexpr uint8 STAR_ICON = 0; + constexpr float TOMB_STOP_HP_PCT = 40.0f; + constexpr float TOMB_STOP_HP_PCT_10_MAN = 60.0f; + + Difficulty const diff = bot->GetRaidDifficulty(); + bool const is10Man = (diff == RAID_DIFFICULTY_10MAN_NORMAL || diff == RAID_DIFFICULTY_10MAN_HEROIC); + + uint8 iconIndex = 0; + std::string rtiValue; + + switch (groupIndex) + { + case 0: iconIndex = SKULL_ICON; rtiValue = "skull"; break; + case 1: iconIndex = CROSS_ICON; rtiValue = "cross"; break; + case 2: iconIndex = STAR_ICON; rtiValue = "star"; break; + default: return false; + } + + context->GetValue("rti")->Set(rtiValue); + + Unit* currentIconUnit = botAI->GetUnit(group->GetTargetIcon(iconIndex)); + + Unit* tombToMark = nullptr; + + // Prefer to keep the current icon if it is still a valid extra in our zone. + if (currentIconUnit && currentIconUnit->IsAlive() && currentIconUnit != losTomb && + !(is10Man && currentIconUnit->GetHealthPct() <= TOMB_STOP_HP_PCT_10_MAN)) + { + for (Unit* tomb : myTombs) + { + if (tomb == currentIconUnit) + { + tombToMark = currentIconUnit; + break; + } + } + } + + // No valid current icon — pick a new extra deterministically (lowest GUID) + // so every bot in the group agrees on the same choice. + if (!tombToMark) + { + ObjectGuid bestGuid; + for (Unit* tomb : myTombs) + { + if (!tomb || !tomb->IsAlive() || tomb == losTomb) + continue; + if (is10Man && tomb->GetHealthPct() <= TOMB_STOP_HP_PCT_10_MAN) + continue; + if (!tombToMark || tomb->GetGUID() < bestGuid) + { + tombToMark = tomb; + bestGuid = tomb->GetGUID(); + } + } + } + + // No extras left — DPS the sticky down to the stop threshold + float const stickyStopPct = is10Man ? TOMB_STOP_HP_PCT_10_MAN : TOMB_STOP_HP_PCT; + if (!tombToMark && losTomb && losTomb->IsAlive() && losTomb->GetHealthPct() > stickyStopPct) + tombToMark = losTomb; + + if (!tombToMark) + { + // All tombs at/below threshold — clear icon and stand idle + ObjectGuid const currentIcon = group->GetTargetIcon(iconIndex); + if (!currentIcon.IsEmpty()) + group->SetTargetIcon(iconIndex, bot->GetGUID(), ObjectGuid::Empty); + + bot->AttackStop(); + return true; + } + + if (!currentIconUnit || !currentIconUnit->IsAlive() || currentIconUnit != tombToMark) + group->SetTargetIcon(iconIndex, bot->GetGUID(), tombToMark->GetGUID()); + + // Let combat actions fire so this bot DPSes the marked tomb + return false; +} \ No newline at end of file diff --git a/src/Ai/Raid/ICC/Action/ICCActions_SS.cpp b/src/Ai/Raid/ICC/Action/ICCActions_SS.cpp new file mode 100644 index 00000000000..1259650c444 --- /dev/null +++ b/src/Ai/Raid/ICC/Action/ICCActions_SS.cpp @@ -0,0 +1,61 @@ +#include "ICCActions.h" +#include "NearestNpcsValue.h" +#include "ObjectAccessor.h" +#include "Playerbots.h" +#include "Vehicle.h" +#include "RtiValue.h" +#include "GenericSpellActions.h" +#include "GenericActions.h" +#include "ICCTriggers.h" +#include "Multiplier.h" + +bool IccValkyreSpearAction::Execute(Event /*event*/) +{ + // Find the nearest spear + Creature* spear = bot->FindNearestCreature(NPC_SPEAR, 100.0f); + if (!spear) + return false; + + // Move to the spear if not in range + if (!spear->IsWithinDistInMap(bot, INTERACTION_DISTANCE)) + return MoveTo(spear, INTERACTION_DISTANCE); + + // Remove shapeshift forms + botAI->RemoveShapeshift(); + + // Stop movement and click the spear + bot->GetMotionMaster()->Clear(); + bot->StopMoving(); + spear->HandleSpellClick(bot); + + // Dismount if mounted + WorldPacket emptyPacket; + bot->GetSession()->HandleCancelMountAuraOpcode(emptyPacket); + + return false; +} + +bool IccSisterSvalnaAction::Execute(Event /*event*/) +{ + Unit* svalna = AI_VALUE2(Unit*, "find target", "sister svalna"); + if (!svalna || !svalna->HasAura(SPELL_AETHER_SHIELD)) + return false; + + // Check if bot has the spear item + if (!botAI->HasItemInInventory(ITEM_SPEAR)) + return false; + + // Get all items from inventory + std::vector items = botAI->GetInventoryItems(); + for (Item* item : items) + { + if (item->GetEntry() == ITEM_SPEAR) + { + // Use spear on Svalna + botAI->ImbueItem(item, svalna); + return false; + } + } + + return false; +} \ No newline at end of file diff --git a/src/Ai/Raid/ICC/Action/ICCActions_VT.cpp b/src/Ai/Raid/ICC/Action/ICCActions_VT.cpp new file mode 100644 index 00000000000..9c800d9a184 --- /dev/null +++ b/src/Ai/Raid/ICC/Action/ICCActions_VT.cpp @@ -0,0 +1,1357 @@ +#include +#include + +#include "GenericActions.h" +#include "GenericSpellActions.h" +#include "Multiplier.h" +#include "NearestNpcsValue.h" +#include "ObjectAccessor.h" +#include "Playerbots.h" +#include "ICCActions.h" +#include "ICCTriggers.h" +#include "RtiValue.h" +#include "Vehicle.h" + +namespace +{ +// How long (ms) all bots stay at a cloud before advancing to the next one. +// This window lets every portal bot teleport in and collect the cloud together. +constexpr uint32 CLOUD_SYNC_WAIT_MS = 1000; + +struct ValithriaCloudSync +{ + ObjectGuid targetCloudGuid; + uint32 moveOnAfterMs = 0; +}; + +std::unordered_map VdwCloudSync; // key: map instance ID + +// Per-instance per-healer remembered portal claim. Other healers in the same +// instance see these claims and avoid taking the same portal. Outer key is the +// map instance ID so concurrent ICC raids don't share or evict each other's claims. +std::unordered_map> VdwPortalClaim; +} + +static bool CastClassTaunt(Player* bot, PlayerbotAI* botAI, Unit* target) +{ + if (!target || !target->IsAlive()) + return false; + + switch (bot->getClass()) + { + case CLASS_PALADIN: + { + bot->RemoveSpellCooldown(SPELL_TAUNT_PALADIN, true); + if (botAI->CastSpell("hand of reckoning", target)) + return true; + break; + } + case CLASS_DEATH_KNIGHT: + { + bot->RemoveSpellCooldown(SPELL_TAUNT_DK, true); + if (botAI->CastSpell("dark command", target)) + return true; + break; + } + case CLASS_DRUID: + { + bot->RemoveSpellCooldown(SPELL_TAUNT_DRUID, true); + if (botAI->CastSpell("growl", target)) + return true; + break; + } + case CLASS_WARRIOR: + { + bot->RemoveSpellCooldown(SPELL_TAUNT_WARRIOR, true); + if (botAI->CastSpell("taunt", target)) + return true; + break; + } + default: + break; + } + + if (botAI->CastSpell("shoot", target) || botAI->CastSpell("throw", target)) + return true; + + return false; +} + +static std::vector GetCreaturesByEntry(WorldObject* searcher, uint32 entry, float range) +{ + std::list raw; + searcher->GetCreatureListWithEntryInGrid(raw, entry, range); + + std::vector out; + out.reserve(raw.size()); + for (Creature* c : raw) + if (c && c->IsAlive()) + out.push_back(c); + + return out; +} + +static std::vector GetCreaturesByEntries(WorldObject* searcher, std::initializer_list entries, + float range) +{ + std::vector out; + for (uint32 entry : entries) + { + auto part = GetCreaturesByEntry(searcher, entry, range); + out.insert(out.end(), part.begin(), part.end()); + } + std::sort(out.begin(), out.end(), [](Creature const* a, Creature const* b) { return a->GetGUID() < b->GetGUID(); }); + out.erase(std::unique(out.begin(), out.end()), out.end()); + + return out; +} + +bool IccValithriaGroupAction::Execute(Event /*event*/) +{ + std::vector const portalList = GetCreaturesByEntries( + bot, {NPC_DREAM_PORTAL, NPC_DREAM_PORTAL_PRE_EFFECT, NPC_NIGHTMARE_PORTAL, NPC_NIGHTMARE_PORTAL_PRE_EFFECT}, + 100.0f); + Creature* portal = portalList.empty() ? nullptr : portalList.front(); + + Creature* worm = bot->FindNearestCreature(NPC_ROT_WORM, 100.0f); + Creature* zombie = bot->FindNearestCreature(NPC_BLISTERING_ZOMBIE, 100.0f); + Creature* manaVoid = bot->FindNearestCreature(NPC_MANA_VOID, 100.0f); + + // Column of Frost units - still hostile so the hostile list is fine here + GuidVector const npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); + std::vector frostColumns; + for (ObjectGuid const& guid : npcs) + { + Unit* unit = botAI->GetUnit(guid); + if (unit && unit->IsAlive() && unit->GetEntry() == NPC_COLUMN_OF_FROST) + frostColumns.push_back(unit); + } + + // Tanks collect stray Gluttonous Abominations / Rot Worms anywhere. + // Each tank picks NEAREST stray so two tanks naturally split work. + // In taunt range -> taunt. Out of range -> step toward add to close gap. + if (botAI->IsTank(bot)) + { + constexpr float ADD_TAUNT_RANGE = 30.0f; + constexpr float CHASE_STEP = 10.0f; + + Unit* strayAdd = nullptr; + float bestDist = FLT_MAX; + for (ObjectGuid const& guid : AI_VALUE(GuidVector, "possible targets")) + { + Unit* unit = botAI->GetUnit(guid); + if (!unit || !unit->IsAlive()) + continue; + if (unit->GetEntry() != NPC_GLUTTONOUS_ABOMINATION && unit->GetEntry() != NPC_ROT_WORM) + continue; + + Unit* victim = unit->GetVictim(); + Player* victimPlayer = victim ? victim->ToPlayer() : nullptr; + if (victimPlayer && botAI->IsTank(victimPlayer)) + continue; + + float const d = bot->GetExactDist2d(unit); + if (d < bestDist) + { + bestDist = d; + strayAdd = unit; + } + } + + if (strayAdd) + { + if (bestDist <= ADD_TAUNT_RANGE) + { + CastClassTaunt(bot, botAI, strayAdd); + } + else + { + float dx = strayAdd->GetPositionX() - bot->GetPositionX(); + float dy = strayAdd->GetPositionY() - bot->GetPositionY(); + float const len = std::sqrt(dx * dx + dy * dy); + if (len > 0.001f) + { + dx /= len; + dy /= len; + float const step = std::min(CHASE_STEP, bestDist - ADD_TAUNT_RANGE + 5.0f); + float const moveX = bot->GetPositionX() + dx * step; + float const moveY = bot->GetPositionY() + dy * step; + MoveTo(bot->GetMapId(), moveX, moveY, bot->GetPositionZ(), false, false, false, false, + MovementPriority::MOVEMENT_COMBAT); + return true; + } + } + } + } + + // Healers move toward the heal position when no portal is active + if (botAI->IsHeal(bot) && !portal && bot->GetExactDist2d(ICC_VDW_HEAL_POSITION) > 30.0f) + { + return MoveTo(bot->GetMapId(), ICC_VDW_HEAL_POSITION.GetPositionX(), ICC_VDW_HEAL_POSITION.GetPositionY(), + ICC_VDW_HEAL_POSITION.GetPositionZ(), false, false, false, false, + MovementPriority::MOVEMENT_NORMAL); + } + + // Avoidance + if (manaVoid && bot->GetExactDist2d(manaVoid) < 10.0f && !botAI->GetAura("Twisted Nightmares", bot) && + !botAI->GetAura("Emerald Vigor", bot)) + { + botAI->Reset(); + FleePosition(manaVoid->GetPosition(), 11.0f, 250U); + } + + for (Unit* column : frostColumns) + { + if (column && bot->GetExactDist2d(column) < 7.0f) + { + botAI->Reset(); + FleePosition(column->GetPosition(), 8.0f, 250U); + } + } + + if (worm && worm->IsAlive() && worm->GetVictim() == bot && !botAI->IsTank(bot)) + { + botAI->Reset(); + FleePosition(worm->GetPosition(), 10.0f, 250U); + } + + // Zombie handling: only exploding-zombie flee here (any non-tank within 20f). + // Per-bot kiting when zombie victim handled by IccValithriaZombieKiteAction + // (higher priority trigger ACTION_EMERGENCY+9, blocks lower actions). + std::list allZombies; + bot->GetCreatureListWithEntryInGrid(allZombies, NPC_BLISTERING_ZOMBIE, 100.0f); + + Creature* explodingZombie = nullptr; + Creature* nearbyZombie = nullptr; + float nearbyDist = FLT_MAX; + + for (Creature* z : allZombies) + { + if (!z || !z->IsAlive()) + continue; + + bool const exploding = + z->HealthBelowPct(5) || z->FindCurrentSpellBySpellId(SPELL_ACID_BURST) || z->HasAura(SPELL_ACID_BURST); + float const d = bot->GetExactDist2d(z); + + if (exploding && d < 20.0f && (!explodingZombie || d < bot->GetExactDist2d(explodingZombie))) + explodingZombie = z; + + if (d < nearbyDist) + { + nearbyDist = d; + nearbyZombie = z; + } + } + + if (explodingZombie) + { + botAI->Reset(); + return FleePosition(explodingZombie->GetPosition(), 20.0f, 250U); + } + + // Non-tank melee must never sit inside 15f of any zombie: the zombie can + // flip victim at any time and at melee range the bot has no time to kite. + if (nearbyZombie && botAI->IsMelee(bot) && !botAI->IsTank(bot) && nearbyDist < 15.0f) + { + botAI->Reset(); + return FleePosition(nearbyZombie->GetPosition(), 15.0f, 250U); + } + + if (nearbyZombie && !botAI->IsMainTank(bot) && !botAI->IsHeal(bot) && nearbyZombie->GetVictim() != bot) + ApplyCrowdControl(nearbyZombie); + + // Leash: every role stays within 35f of the boss anchor unless doing an + // exclusive task (zombie kite, portal work, dream-state cloud collection). + // Zombie kite action runs at ACTION_EMERGENCY+9 so it preempts this action; + // extra skip-conditions below guard the remaining exclusive tasks. + constexpr float LEASH_RADIUS = 35.0f; + bool const inDreamState = bot->HasAura(SPELL_DREAM_STATE); + bool hasPortalClaim = false; + if (botAI->IsHeal(bot)) + { + auto instanceIt = VdwPortalClaim.find(bot->GetMap()->GetInstanceId()); + if (instanceIt != VdwPortalClaim.end() && + instanceIt->second.find(bot->GetGUID()) != instanceIt->second.end()) + hasPortalClaim = true; + } + bool const hasZombieThreat = nearbyZombie && nearbyZombie->GetVictim() == bot; + if (!inDreamState && !hasPortalClaim && !hasZombieThreat) + { + float const distToAnchor = bot->GetExactDist2d(ICC_VDW_HEAL_POSITION); + if (distToAnchor > LEASH_RADIUS) + { + constexpr float STEP = 8.0f; + float const dx = ICC_VDW_HEAL_POSITION.GetPositionX() - bot->GetPositionX(); + float const dy = ICC_VDW_HEAL_POSITION.GetPositionY() - bot->GetPositionY(); + float const dz = ICC_VDW_HEAL_POSITION.GetPositionZ() - bot->GetPositionZ(); + float const dist = std::hypot(dx, dy); + float moveX; + float moveY; + float moveZ; + if (dist > STEP) + { + moveX = bot->GetPositionX() + (dx / dist) * STEP; + moveY = bot->GetPositionY() + (dy / dist) * STEP; + moveZ = bot->GetPositionZ() + (dz / dist) * STEP; + } + else + { + moveX = ICC_VDW_HEAL_POSITION.GetPositionX(); + moveY = ICC_VDW_HEAL_POSITION.GetPositionY(); + moveZ = ICC_VDW_HEAL_POSITION.GetPositionZ(); + } + MoveTo(bot->GetMapId(), moveX, moveY, moveZ, false, false, false, true, + MovementPriority::MOVEMENT_COMBAT, true, false); + return true; + } + } + + Difficulty const diff = bot->GetRaidDifficulty(); + Group* group = bot->GetGroup(); + + if (group && (diff == RAID_DIFFICULTY_25MAN_NORMAL || diff == RAID_DIFFICULTY_25MAN_HEROIC)) + return Handle25ManGroupLogic(); + + return Handle10ManGroupLogic(); +} + +bool IccValithriaGroupAction::ApplyCrowdControl(Unit* zombie) +{ + switch (bot->getClass()) + { + case CLASS_MAGE: + if (!botAI->HasAura("Frost Nova", zombie)) + return botAI->CastSpell("Frost Nova", zombie); + break; + case CLASS_DRUID: + if (!botAI->HasAura("Entangling Roots", zombie)) + return botAI->CastSpell("Entangling Roots", zombie); + break; + case CLASS_PALADIN: + if (!botAI->HasAura("Hammer of Justice", zombie)) + return botAI->CastSpell("Hammer of Justice", zombie); + break; + case CLASS_WARRIOR: + if (!botAI->HasAura("Hamstring", zombie)) + return botAI->CastSpell("Hamstring", zombie); + break; + case CLASS_HUNTER: + if (!botAI->HasAura("Concussive Shot", zombie)) + return botAI->CastSpell("Concussive Shot", zombie); + break; + case CLASS_ROGUE: + if (!botAI->HasAura("Kidney Shot", zombie)) + return botAI->CastSpell("Kidney Shot", zombie); + break; + case CLASS_SHAMAN: + if (!botAI->HasAura("Frost Shock", zombie)) + return botAI->CastSpell("Frost Shock", zombie); + break; + case CLASS_DEATH_KNIGHT: + if (!botAI->HasAura("Chains of Ice", zombie)) + return botAI->CastSpell("Chains of Ice", zombie); + break; + case CLASS_PRIEST: + if (!botAI->HasAura("Psychic Scream", zombie)) + return botAI->CastSpell("Psychic Scream", zombie); + break; + case CLASS_WARLOCK: + if (!botAI->HasAura("Fear", zombie)) + return botAI->CastSpell("Fear", zombie); + break; + default: + break; + } + + return false; +} + +bool IccValithriaGroupAction::Handle25ManGroupLogic() +{ + Group* group = bot->GetGroup(); + if (!group) + return false; + + // Priority order follows Wowhead/Icy-Veins strategy: + // Blazing Skeleton (AoE Lay Waste) -> Suppresser (stacks -10% heal) -> + // Risen Archmage (Mana Void/Column of Frost) -> Blistering Zombie (ranged + // kites it, melee flees at 15f) -> Gluttonous Abomination (tank keeps it + // faced away) -> Rot Worm (cleanup from dead Abominations). + static constexpr std::array ADD_PRIORITY_CHECK = {NPC_BLAZING_SKELETON, NPC_SUPPRESSER, + NPC_RISEN_ARCHMAGE, NPC_BLISTERING_ZOMBIE, + NPC_GLUTTONOUS_ABOMINATION, NPC_ROT_WORM}; + constexpr float MARK_RADIUS = 45.0f; + + int priorityAddsAlive = 0; + GuidVector const targets = AI_VALUE(GuidVector, "possible targets"); + for (ObjectGuid const& guid : targets) + { + Unit* unit = botAI->GetUnit(guid); + if (!unit || !unit->IsAlive()) + continue; + uint32 const e = unit->GetEntry(); + bool isPriority = false; + for (uint32 pe : ADD_PRIORITY_CHECK) + { + if (pe == e) + { + isPriority = true; + break; + } + } + if (!isPriority) + continue; + float const d = + unit->GetExactDist2d(ICC_VDW_HEAL_POSITION.GetPositionX(), ICC_VDW_HEAL_POSITION.GetPositionY()); + if (d > MARK_RADIUS) + continue; + ++priorityAddsAlive; + if (priorityAddsAlive >= 2) + break; + } + + bool const singleMarkMode = priorityAddsAlive <= 1; + + std::vector eligible; + for (GroupReference* itr = group->GetFirstMember(); itr; itr = itr->next()) + { + Player* member = itr->GetSource(); + if (!member || !member->IsAlive()) + continue; + + PlayerbotAI* memberAI = GET_PLAYERBOT_AI(member); + if (!memberAI || memberAI->IsRealPlayer()) + continue; + + if (memberAI->IsHeal(member) && member->HasAura(SPELL_DREAM_STATE)) + continue; + + if (!memberAI->IsTank(member) && !memberAI->IsDps(member) && !memberAI->IsHeal(member)) + continue; + + eligible.push_back(member); + } + + std::sort(eligible.begin(), eligible.end(), + [](Player const* a, Player const* b) { return a->GetGUID() < b->GetGUID(); }); + + bool inGroup1 = false; + bool inGroup2 = false; + + if (singleMarkMode) + { + inGroup1 = std::any_of(eligible.begin(), eligible.end(), [this](Player* p) { return p == bot; }); + } + else + { + // Bucket by role (tank / melee dps / ranged dps / healer) so each group + // gets roughly the same number of each role. Within a bucket alternate + // assignment; flip the starting side on odd-sized buckets so leftover + // slots don't all pile onto Group1. + std::vector tanks; + std::vector meleeDps; + std::vector rangedDps; + std::vector healers; + for (Player* p : eligible) + { + PlayerbotAI* pai = GET_PLAYERBOT_AI(p); + if (!pai) + continue; + if (pai->IsTank(p)) + tanks.push_back(p); + else if (pai->IsHeal(p)) + healers.push_back(p); + else if (pai->IsMelee(p)) + meleeDps.push_back(p); + else + rangedDps.push_back(p); + } + + std::vector group1; + std::vector group2; + int parity = 0; + auto splitBucket = [&](std::vector const& bucket) + { + for (size_t i = 0; i < bucket.size(); ++i) + { + if (((i + parity) & 1) == 0) + group1.push_back(bucket[i]); + else + group2.push_back(bucket[i]); + } + if (bucket.size() & 1) + parity ^= 1; + }; + splitBucket(tanks); + splitBucket(meleeDps); + splitBucket(rangedDps); + splitBucket(healers); + + inGroup1 = std::any_of(group1.begin(), group1.end(), [this](Player* p) { return p == bot; }); + inGroup2 = std::any_of(group2.begin(), group2.end(), [this](Player* p) { return p == bot; }); + } + + if (botAI->IsTank(bot) || botAI->IsDps(bot) || botAI->IsHeal(bot)) + HandleMarkingLogic(inGroup1, inGroup2, singleMarkMode); + + return false; +} + +bool IccValithriaGroupAction::HandleMarkingLogic(bool inGroup1, bool inGroup2, bool singleMarkMode) +{ + static constexpr uint8 SKULL_ICON = 7; + static constexpr uint8 CROSS_ICON = 6; + // Kill priority per Wowhead/Icy-Veins: Lay Waste skeletons first, then + // heal-debuff suppressers, then archmages, then zombies (ranged only - + // melee flee at 15f), then abominations (tank-faced), then rot worm cleanup. + static constexpr std::array ADD_PRIORITY = {NPC_BLAZING_SKELETON, NPC_SUPPRESSER, + NPC_RISEN_ARCHMAGE, NPC_BLISTERING_ZOMBIE, + NPC_GLUTTONOUS_ABOMINATION, NPC_ROT_WORM}; + + uint8 iconIndex; + std::string rtiValue; + + if (singleMarkMode) + { + if (!inGroup1) + return false; + iconIndex = SKULL_ICON; + rtiValue = "skull"; + } + else if (inGroup1) + { + iconIndex = SKULL_ICON; + rtiValue = "skull"; + } + else if (inGroup2) + { + iconIndex = CROSS_ICON; + rtiValue = "cross"; + } + else + return false; + + Group* group = bot->GetGroup(); + if (!group) + return false; + + constexpr float MARK_RADIUS = 45.0f; + + auto inMarkRange = [&](Unit* unit) -> bool + { + return unit->GetExactDist2d(ICC_VDW_HEAL_POSITION.GetPositionX(), ICC_VDW_HEAL_POSITION.GetPositionY()) <= + MARK_RADIUS; + }; + + context->GetValue("rti")->Set(rtiValue); + + bool const dispatchRti = !botAI->IsTank(bot) && !botAI->IsHeal(bot); + bool const isMeleeDps = botAI->IsMelee(bot) && !botAI->IsTank(bot); + + auto tryDispatchRti = [&](ObjectGuid rtiGuid) + { + if (!dispatchRti || rtiGuid.IsEmpty()) + return; + // Melee DPS must not attack zombies - they explode at melee range. + // Ranged DPS still handle the marked zombie via RTI. + Unit* rtiUnit = botAI->GetUnit(rtiGuid); + if (rtiUnit && rtiUnit->GetEntry() == NPC_BLISTERING_ZOMBIE && isMeleeDps) + return; + Unit* const victim = bot->GetVictim(); + if (victim && victim->GetGUID() == rtiGuid) + return; + botAI->DoSpecificAction("attack rti target"); + }; + + GuidVector const adds = AI_VALUE(GuidVector, "possible targets"); + + auto getPriorityRank = [&](uint32 entry) -> int + { + for (size_t i = 0; i < ADD_PRIORITY.size(); ++i) + if (ADD_PRIORITY[i] == entry) + return static_cast(i); + return -1; + }; + + auto isValidAdd = [&](Unit* unit) -> bool + { + if (!unit || !unit->IsAlive()) + return false; + if (getPriorityRank(unit->GetEntry()) < 0) + return false; + return inMarkRange(unit); + }; + + ObjectGuid otherIconGuid; + if (!singleMarkMode) + { + uint8 const otherIcon = (iconIndex == SKULL_ICON) ? CROSS_ICON : SKULL_ICON; + otherIconGuid = group->GetTargetIcon(otherIcon); + } + + Unit* currentIconUnit = botAI->GetUnit(group->GetTargetIcon(iconIndex)); + + // Split the room by a world-Y line through the heal anchor. Group1 (skull) + // owns one half, Group2 (cross) owns the other. A group only falls back to + // the other half when its own side has no marking candidate. + auto isOwnSide = [&](Unit* unit) -> bool + { + if (singleMarkMode) + return true; + bool const sideA = unit->GetPositionY() < ICC_VDW_HEAL_POSITION.GetPositionY(); + return inGroup1 ? sideA : !sideA; + }; + + // Pick the reference point for "closest add" selection so DPS travels the + // shortest distance when the mark flips: prefer the current icon's + // position (alive or corpse), then the other group's still-live mark, + // then the heal anchor. + Position refPos = ICC_VDW_HEAL_POSITION; + if (currentIconUnit) + refPos = currentIconUnit->GetPosition(); + else if (!singleMarkMode) + { + if (Unit* otherUnit = botAI->GetUnit(otherIconGuid)) + if (otherUnit->IsAlive()) + refPos = otherUnit->GetPosition(); + } + + Unit* bestTarget = nullptr; + int bestRank = static_cast(ADD_PRIORITY.size()); + bool bestTargetOwnSide = false; + + // Two-pass scan: own side first, then any side. Within each pass walk + // priorities top-down and within the first tier that has any candidate + // pick the one closest to refPos. + for (int pass = 0; pass < 2 && !bestTarget; ++pass) + { + bool const ownSideOnly = (pass == 0); + for (size_t rank = 0; rank < ADD_PRIORITY.size(); ++rank) + { + uint32 const entry = ADD_PRIORITY[rank]; + float bestDist = FLT_MAX; + for (ObjectGuid const& guid : adds) + { + if (!singleMarkMode && guid == otherIconGuid) + continue; + Unit* unit = botAI->GetUnit(guid); + if (!unit || !unit->IsAlive() || unit->GetEntry() != entry) + continue; + if (!inMarkRange(unit)) + continue; + if (ownSideOnly && !isOwnSide(unit)) + continue; + float const d = unit->GetExactDist2d(refPos.GetPositionX(), refPos.GetPositionY()); + if (d < bestDist) + { + bestDist = d; + bestTarget = unit; + bestRank = static_cast(rank); + bestTargetOwnSide = ownSideOnly; + } + } + if (bestTarget) + break; + } + } + + if (isValidAdd(currentIconUnit)) + { + int currentRank = -1; + if (currentIconUnit) + currentRank = getPriorityRank(currentIconUnit->GetEntry()); + + bool const currentOnOwnSide = isOwnSide(currentIconUnit); + + // Side takes precedence: if an own-side candidate exists and the current + // icon is on the other side, flip regardless of priority rank. + bool const sideFlip = bestTarget && bestTargetOwnSide && !currentOnOwnSide; + + if (!sideFlip && (!bestTarget || bestRank >= currentRank)) + { + if (currentIconUnit) + tryDispatchRti(currentIconUnit->GetGUID()); + return false; + } + } + + if (!bestTarget) + { + if (currentIconUnit && !group->GetTargetIcon(iconIndex).IsEmpty()) + group->SetTargetIcon(iconIndex, bot->GetGUID(), ObjectGuid::Empty); + return false; + } + + group->SetTargetIcon(iconIndex, bot->GetGUID(), bestTarget->GetGUID()); + + if (singleMarkMode) + { + uint8 const crossIcon = CROSS_ICON; + Unit* currentCrossUnit = botAI->GetUnit(group->GetTargetIcon(crossIcon)); + if (currentCrossUnit && !group->GetTargetIcon(crossIcon).IsEmpty()) + group->SetTargetIcon(crossIcon, bot->GetGUID(), ObjectGuid::Empty); + } + + tryDispatchRti(bestTarget->GetGUID()); + + return false; +} + +bool IccValithriaGroupAction::Handle10ManGroupLogic() +{ + static constexpr uint8 DEFAULT_ICON = 7; + // Kill priority per Wowhead/Icy-Veins: skeleton > suppresser > archmage > + // zombie (ranged) > abomination (tank) > rot worm (cleanup). + static constexpr std::array ADD_PRIORITY = {NPC_BLAZING_SKELETON, NPC_SUPPRESSER, + NPC_RISEN_ARCHMAGE, NPC_BLISTERING_ZOMBIE, + NPC_GLUTTONOUS_ABOMINATION, NPC_ROT_WORM}; + + Group* group = bot->GetGroup(); + if (!group) + return false; + + PlayerbotAI* selfAI = GET_PLAYERBOT_AI(bot); + if (!selfAI || selfAI->IsRealPlayer()) + return false; + if (botAI->IsHeal(bot) && bot->HasAura(SPELL_DREAM_STATE)) + return false; + + constexpr float MARK_RADIUS = 45.0f; + + auto inMarkRange = [&](Unit* unit) -> bool + { + return unit->GetExactDist2d(ICC_VDW_HEAL_POSITION.GetPositionX(), ICC_VDW_HEAL_POSITION.GetPositionY()) <= + MARK_RADIUS; + }; + + context->GetValue("rti")->Set("skull"); + + bool const dispatchRti = !botAI->IsTank(bot) && !botAI->IsHeal(bot); + bool const isMeleeDps = botAI->IsMelee(bot) && !botAI->IsTank(bot); + + auto tryDispatchRti = [&](ObjectGuid rtiGuid) + { + if (!dispatchRti || rtiGuid.IsEmpty()) + return; + // Melee DPS must not attack zombies - ranged only. + Unit* rtiUnit = botAI->GetUnit(rtiGuid); + if (rtiUnit && rtiUnit->GetEntry() == NPC_BLISTERING_ZOMBIE && isMeleeDps) + return; + Unit* const victim = bot->GetVictim(); + if (victim && victim->GetGUID() == rtiGuid) + return; + botAI->DoSpecificAction("attack rti target"); + }; + + GuidVector const adds = AI_VALUE(GuidVector, "possible targets"); + + auto getPriorityRank = [&](uint32 entry) -> int + { + for (size_t i = 0; i < ADD_PRIORITY.size(); ++i) + if (ADD_PRIORITY[i] == entry) + return static_cast(i); + return -1; + }; + + auto isValidAdd = [&](Unit* unit) -> bool + { + if (!unit || !unit->IsAlive()) + return false; + if (getPriorityRank(unit->GetEntry()) < 0) + return false; + return inMarkRange(unit); + }; + + Unit* currentIconUnit = botAI->GetUnit(group->GetTargetIcon(DEFAULT_ICON)); + + // Use the current mark's position (alive or corpse) as the reference so + // DPS moves the least when the mark flips; fall back to the heal anchor. + Position refPos = ICC_VDW_HEAL_POSITION; + if (currentIconUnit) + refPos = currentIconUnit->GetPosition(); + + Unit* bestTarget = nullptr; + int bestRank = static_cast(ADD_PRIORITY.size()); + + for (size_t rank = 0; rank < ADD_PRIORITY.size(); ++rank) + { + uint32 const entry = ADD_PRIORITY[rank]; + float bestDist = FLT_MAX; + for (ObjectGuid const& guid : adds) + { + Unit* unit = botAI->GetUnit(guid); + if (!unit || !unit->IsAlive() || unit->GetEntry() != entry) + continue; + if (!inMarkRange(unit)) + continue; + float const d = unit->GetExactDist2d(refPos.GetPositionX(), refPos.GetPositionY()); + if (d < bestDist) + { + bestDist = d; + bestTarget = unit; + bestRank = static_cast(rank); + } + } + if (bestTarget) + break; + } + if (isValidAdd(currentIconUnit)) + { + int currentRank; + if (currentIconUnit) + currentRank = getPriorityRank(currentIconUnit->GetEntry()); + else + { + // Handle the null case appropriately, e.g.: + currentRank = -1; // or another default/error value + } + + if (!bestTarget || bestRank >= currentRank) + { + if (currentIconUnit) + tryDispatchRti(currentIconUnit->GetGUID()); + return false; + } + } + + if (!bestTarget) + { + if (currentIconUnit && !group->GetTargetIcon(DEFAULT_ICON).IsEmpty()) + group->SetTargetIcon(DEFAULT_ICON, bot->GetGUID(), ObjectGuid::Empty); + return false; + } + + group->SetTargetIcon(DEFAULT_ICON, bot->GetGUID(), bestTarget->GetGUID()); + + tryDispatchRti(bestTarget->GetGUID()); + + return false; +} + +bool IccValithriaPortalAction::Execute(Event /*event*/) +{ + if (!botAI->IsHeal(bot) || bot->HasAura(SPELL_DREAM_STATE)) + return false; + + // Healer just dropped Dream State - if still airborne (portal exit leaves + // bots above the floor) snap to floor Z immediately so they don't fall and die. + constexpr float MAX_Z = 367.961f; + constexpr float TARGET_Z = 365.0f; + if (bot->GetPositionZ() > MAX_Z) + bot->TeleportTo(bot->GetMapId(), bot->GetPositionX(), bot->GetPositionY(), TARGET_Z, bot->GetOrientation()); + + constexpr float SEARCH_RANGE = 200.0f; + + std::vector preEffectPortals = + GetCreaturesByEntries(bot, {NPC_DREAM_PORTAL_PRE_EFFECT, NPC_NIGHTMARE_PORTAL_PRE_EFFECT}, SEARCH_RANGE); + + std::vector realPortals = + GetCreaturesByEntries(bot, {NPC_DREAM_PORTAL, NPC_NIGHTMARE_PORTAL}, SEARCH_RANGE); + + // Evict stale claims BEFORE early-return so claims don't leak after fight. + // Scope eviction to this instance only - other ICC instances have their + // own portal GUID universes and we must not erase their claims. + uint32 const instanceId = bot->GetMap()->GetInstanceId(); + auto& claims = VdwPortalClaim[instanceId]; + + std::unordered_set livePortalGuids; + for (Creature* p : preEffectPortals) + if (p) + livePortalGuids.insert(p->GetGUID()); + for (Creature* p : realPortals) + if (p) + livePortalGuids.insert(p->GetGUID()); + + for (auto it = claims.begin(); it != claims.end();) + { + if (!livePortalGuids.count(it->second)) + it = claims.erase(it); + else + ++it; + } + + if (preEffectPortals.empty() && realPortals.empty()) + { + if (claims.empty()) + VdwPortalClaim.erase(instanceId); + return false; + } + + // Collect OTHER healers' claims so we never pick a portal already taken. + std::unordered_set reservedPortals; + for (auto const& kv : claims) + if (kv.first != bot->GetGUID()) + reservedPortals.insert(kv.second); + + auto pickClosestUnreserved = [&](std::vector const& portals) -> Creature* + { + Creature* best = nullptr; + float bestDist = FLT_MAX; + for (Creature* p : portals) + { + if (!p) + continue; + if (reservedPortals.count(p->GetGUID())) + continue; + float const d = bot->GetExactDist2d(p); + if (d < bestDist) + { + bestDist = d; + best = p; + } + } + return best; + }; + + // Prefer pre-effect (claim early so others see it). Fall through to real + // portal if no pre-effect available. + Creature* assigned = nullptr; + if (!preEffectPortals.empty()) + assigned = pickClosestUnreserved(preEffectPortals); + if (!assigned && !realPortals.empty()) + assigned = pickClosestUnreserved(realPortals); + + // If everything reserved (more healers than portals), fall back to nearest + // overall so this healer still does something useful. + if (!assigned) + { + std::vector const& pool = !preEffectPortals.empty() ? preEffectPortals : realPortals; + float bestDist = FLT_MAX; + for (Creature* p : pool) + { + if (!p) + continue; + float const d = bot->GetExactDist2d(p); + if (d < bestDist) + { + bestDist = d; + assigned = p; + } + } + } + + if (!assigned) + return false; + + claims[bot->GetGUID()] = assigned->GetGUID(); + + if (bot->GetDistance2d(assigned->GetPositionX(), assigned->GetPositionY()) > 0.5f) + { + MoveTo(assigned->GetMapId(), assigned->GetPositionX(), assigned->GetPositionY(), assigned->GetPositionZ(), + false, false, false, true, MovementPriority::MOVEMENT_NORMAL); + } + + botAI->RemoveShapeshift(); + + // Click the real portal once it spawns within reach. Only click our claimed + // portal (if it became real) or any real portal at our exact location. + Creature* clickTarget = nullptr; + float minDist = FLT_MAX; + for (Creature* portal : realPortals) + { + if (!portal) + continue; + float const d = bot->GetDistance2d(portal); + if (d < 3.0f && d < minDist) + { + clickTarget = portal; + minDist = d; + } + } + + if (clickTarget) + { + botAI->RemoveShapeshift(); + bot->GetMotionMaster()->Clear(); + bot->StopMoving(); + bot->SetFacingToObject(clickTarget); + clickTarget->HandleSpellClick(bot); + return true; + } + + return false; +} + +bool IccValithriaHealAction::Execute(Event /*event*/) +{ + if (!botAI->IsHeal(bot)) + return false; + + // Snap to floor Z first - healer may have just dropped Dream State and be + // airborne above MAX_Z. Run BEFORE the HP gate so it fires every tick. + constexpr float MAX_Z = 367.961f; + constexpr float TARGET_Z = 365.0f; + if (bot->GetPositionZ() > MAX_Z) + bot->TeleportTo(bot->GetMapId(), bot->GetPositionX(), bot->GetPositionY(), TARGET_Z, bot->GetOrientation()); + + if (bot->GetHealthPct() < 50.0f) + return false; + + if (!bot->HasAura(SPELL_DREAM_STATE)) + { + constexpr float NORMAL_SPEED = 1.0f; + bot->SetSpeed(MOVE_RUN, NORMAL_SPEED, true); + bot->SetSpeed(MOVE_WALK, NORMAL_SPEED, true); + bot->SetSpeed(MOVE_FLIGHT, NORMAL_SPEED, true); + } + + Creature* valithria = bot->FindNearestCreature(NPC_VALITHRIA_DREAMWALKER, 100.0f); + if (!valithria) + return false; + + switch (bot->getClass()) + { + case CLASS_DRUID: + { + constexpr uint32 SPELL_REJUVENATION = 48441; + constexpr uint32 SPELL_REGROWTH = 48443; + constexpr uint32 SPELL_LIFEBLOOM = 48451; + constexpr uint32 SPELL_WILD_GROWTH = 53251; + constexpr uint8 LIFEBLOOM_MAX = 3; + + if (!valithria->HasAura(SPELL_REJUVENATION, bot->GetGUID())) + return botAI->CastSpell(SPELL_REJUVENATION, valithria); + + if (!valithria->HasAura(SPELL_REGROWTH, bot->GetGUID())) + return botAI->CastSpell(SPELL_REGROWTH, valithria); + + Aura* lb = valithria->GetAura(SPELL_LIFEBLOOM, bot->GetGUID()); + if (!lb || lb->GetStackAmount() < LIFEBLOOM_MAX) + return botAI->CastSpell(SPELL_LIFEBLOOM, valithria); + + return botAI->CastSpell(SPELL_WILD_GROWTH, valithria); + } + case CLASS_SHAMAN: + { + constexpr uint32 SPELL_RIPTIDE = 61301; + constexpr uint32 SPELL_HEALING_WAVE = 49273; + return valithria->HasAura(SPELL_RIPTIDE, bot->GetGUID()) ? botAI->CastSpell(SPELL_HEALING_WAVE, valithria) + : botAI->CastSpell(SPELL_RIPTIDE, valithria); + } + case CLASS_PRIEST: + { + constexpr uint32 SPELL_RENEW = 48068; + constexpr uint32 SPELL_GREATER_HEAL = 48063; + return valithria->HasAura(SPELL_RENEW, bot->GetGUID()) ? botAI->CastSpell(SPELL_GREATER_HEAL, valithria) + : botAI->CastSpell(SPELL_RENEW, valithria); + } + case CLASS_PALADIN: + { + constexpr uint32 SPELL_BEACON = 53563; + constexpr uint32 SPELL_HOLY_LIGHT = 48782; + return valithria->HasAura(SPELL_BEACON, bot->GetGUID()) ? botAI->CastSpell(SPELL_HOLY_LIGHT, valithria) + : botAI->CastSpell(SPELL_BEACON, valithria); + } + default: + break; + } + return false; +} + +bool IccValithriaDreamCloudAction::Execute(Event /*event*/) +{ + if (!bot->HasAura(SPELL_DREAM_STATE)) + return false; + + bot->SetSpeed(MOVE_RUN, 2.0f, true); + bot->SetSpeed(MOVE_WALK, 2.0f, true); + bot->SetSpeed(MOVE_FLIGHT, 2.0f, true); + + std::vector allDream; + std::vector realDream; + + Map::PlayerList const& playerList = bot->GetMap()->GetPlayers(); + for (Map::PlayerList::const_iterator itr = playerList.begin(); itr != playerList.end(); ++itr) + { + Player* player = itr->GetSource(); + if (!player || !player->IsAlive() || !player->HasAura(SPELL_DREAM_STATE)) + continue; + + allDream.push_back(player); + + PlayerbotAI* playerBotAI = GET_PLAYERBOT_AI(player); + if (!playerBotAI || playerBotAI->IsRealPlayer()) + realDream.push_back(player); + } + + if (allDream.empty()) + return false; + + std::vector& leaderPool = realDream.empty() ? allDream : realDream; + + std::sort(leaderPool.begin(), leaderPool.end(), + [](Player const* a, Player const* b) { return a->GetGUID() < b->GetGUID(); }); + + Player* leader = leaderPool.front(); + + if (!realDream.empty()) + { + constexpr float STACK_DIST = 1.0f; + if (bot->GetDistance(leader) > STACK_DIST) + { + bot->TeleportTo(bot->GetMapId(), leader->GetPositionX(), leader->GetPositionY(), leader->GetPositionZ(), + bot->GetOrientation()); + } + return false; + } + + // No real players - synchronized bot cloud collection + uint32 const instanceId = bot->GetInstanceId(); + uint32 const nowMs = getMSTime(); + + ValithriaCloudSync& sync = VdwCloudSync[instanceId]; + + std::vector dreamClouds = CollectClouds(NPC_DREAM_CLOUD, leader); + std::vector nightmareClouds = CollectClouds(NPC_NIGHTMARE_CLOUD, leader); + + std::vector allClouds; + allClouds.insert(allClouds.end(), dreamClouds.begin(), dreamClouds.end()); + allClouds.insert(allClouds.end(), nightmareClouds.begin(), nightmareClouds.end()); + + if (allClouds.empty()) + { + VdwCloudSync.erase(instanceId); + return false; + } + + // Find whether the shared target cloud is still alive + Creature* target = nullptr; + if (!sync.targetCloudGuid.IsEmpty()) + { + for (Creature* c : allClouds) + { + if (c->GetGUID() == sync.targetCloudGuid) + { + target = c; + break; + } + } + } + + // Advance to a new cloud only when the current one is gone or the wait window has passed. + // The wait window gives every portal bot time to teleport in and collect the same cloud. + if (!target || nowMs >= sync.moveOnAfterMs) + { + Creature* closest = nullptr; + float minDist = FLT_MAX; + for (Creature* c : allClouds) + { + float const d = leader->GetExactDist(c); + if (d < minDist) + { + minDist = d; + closest = c; + } + } + + target = closest; + if (target) + sync.targetCloudGuid = target->GetGUID(); + else + sync.targetCloudGuid = ObjectGuid::Empty; + sync.moveOnAfterMs = nowMs + CLOUD_SYNC_WAIT_MS; + } + + // All bots teleport to the shared target so none miss the cloud + if (target) + bot->TeleportTo(target->GetMapId(), target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), + bot->GetOrientation()); + + return false; +} + +// Collect all live clouds sorted by distance from the given reference unit +std::vector IccValithriaDreamCloudAction::CollectClouds(uint32 entry, Unit* reference) +{ + constexpr float SEARCH_RANGE = 200.0f; + + std::list raw; + bot->GetCreatureListWithEntryInGrid(raw, entry, SEARCH_RANGE); + + std::vector result; + result.reserve(raw.size()); + for (Creature* c : raw) + if (c && c->IsAlive()) + result.push_back(c); + + // Sort by reference unit's distance so all bots get the same ordering + std::sort(result.begin(), result.end(), [reference](Creature const* a, Creature const* b) + { return reference->GetExactDist(a) < reference->GetExactDist(b); }); + + return result; +} + +bool IccValithriaZombieKiteAction::Execute(Event /*event*/) +{ + if (botAI->IsTank(bot)) + return false; + + Unit* boss = bot->FindNearestCreature(NPC_VALITHRIA_DREAMWALKER, 100.0f); + if (!boss) + return false; + + std::list allZombies; + bot->GetCreatureListWithEntryInGrid(allZombies, NPC_BLISTERING_ZOMBIE, 100.0f); + + std::vector threatZombies; + Creature* nearestThreat = nullptr; + float nearestDist = FLT_MAX; + for (Creature* z : allZombies) + { + if (!z || !z->IsAlive()) + continue; + if (z->GetVictim() != bot) + continue; + threatZombies.push_back(z); + float const d = bot->GetExactDist2d(z); + if (d < nearestDist) + { + nearestDist = d; + nearestThreat = z; + } + } + + if (threatZombies.empty()) + return false; + + if (!bot->HasAura(SPELL_NITRO_BOOSTS)) + bot->AddAura(SPELL_NITRO_BOOSTS, bot); + + // Stop spell-casting that would root the bot. DON'T call botAI->Reset() - + // it nukes the motion master mid-tick which restarts pathing every tick + // and lets zombies catch up. + if (bot->IsNonMeleeSpellCast(true)) + bot->InterruptNonMeleeSpells(true); + + constexpr float ANCHOR_RADIUS_LIMIT = 25.0f; // kite stays within this radius of heal anchor + constexpr float STEP = 18.0f; // how far we move per tick + constexpr float SAFE_FROM_ZOMBIE = 14.0f; // candidate must be this far from every zombie + constexpr float PATH_CLEARANCE = 8.0f; // path segment must miss every zombie by this much + constexpr int NUM_CANDIDATES = 36; + + float const anchorX = ICC_VDW_HEAL_POSITION.GetPositionX(); + float const anchorY = ICC_VDW_HEAL_POSITION.GetPositionY(); + + auto segmentDistToPoint = [](float x1, float y1, float x2, float y2, float px, float py) -> float + { + float const vx = x2 - x1; + float const vy = y2 - y1; + float const wx = px - x1; + float const wy = py - y1; + float const len2 = vx * vx + vy * vy; + float t = len2 > 0.0001f ? (vx * wx + vy * wy) / len2 : 0.0f; + if (t < 0.0f) + t = 0.0f; + else if (t > 1.0f) + t = 1.0f; + float const cx = x1 + t * vx; + float const cy = y1 + t * vy; + float const dx = px - cx; + float const dy = py - cy; + return std::sqrt(dx * dx + dy * dy); + }; + + // Compute weighted-away-from-all-zombies vector. Closer zombies pull harder. + float awayX = 0.0f; + float awayY = 0.0f; + for (Creature* z : threatZombies) + { + float const dx = bot->GetPositionX() - z->GetPositionX(); + float const dy = bot->GetPositionY() - z->GetPositionY(); + float const d = std::sqrt(dx * dx + dy * dy); + if (d < 0.1f) + continue; + float const w = 1.0f / std::max(d, 1.0f); + awayX += (dx / d) * w; + awayY += (dy / d) * w; + } + float const awayLen = std::sqrt(awayX * awayX + awayY * awayY); + float baseAngle = 0.0f; + if (awayLen > 0.001f) + baseAngle = std::atan2(awayY / awayLen, awayX / awayLen); + else if (nearestThreat) + baseAngle = std::atan2(bot->GetPositionY() - nearestThreat->GetPositionY(), + bot->GetPositionX() - nearestThreat->GetPositionX()); + + float const bx = bot->GetPositionX(); + float const by = bot->GetPositionY(); + + float bestX = bx; + float bestY = by; + float bestScore = -FLT_MAX; + + // Sample arc centered on away-direction, +/-150 degrees + for (int i = 0; i < NUM_CANDIDATES; ++i) + { + float const offset = (-150.0f + (300.0f * i) / (NUM_CANDIDATES - 1)) * static_cast(M_PI) / 180.0f; + float const angle = baseAngle + offset; + float const cx = bx + STEP * std::cos(angle); + float const cy = by + STEP * std::sin(angle); + + // Stay within the anchor radius so kite doesn't drag bot out of raid + float const dxAnchor = cx - anchorX; + float const dyAnchor = cy - anchorY; + if (std::sqrt(dxAnchor * dxAnchor + dyAnchor * dyAnchor) > ANCHOR_RADIUS_LIMIT) + continue; + + // Reject if endpoint too close to any zombie OR straight path passes near one + float minEndDist = FLT_MAX; + bool unsafe = false; + for (Creature* z : threatZombies) + { + float const zx = z->GetPositionX(); + float const zy = z->GetPositionY(); + float const dxe = cx - zx; + float const dye = cy - zy; + float const endDist = std::sqrt(dxe * dxe + dye * dye); + if (endDist < SAFE_FROM_ZOMBIE) + { + unsafe = true; + break; + } + float const pathDist = segmentDistToPoint(bx, by, cx, cy, zx, zy); + if (pathDist < PATH_CLEARANCE) + { + unsafe = true; + break; + } + if (endDist < minEndDist) + minEndDist = endDist; + } + if (unsafe) + continue; + + // Prefer points that maximize distance from the closest zombie. + // Mild bonus for being aligned with away-direction (smaller offset). + float const alignBonus = 1.5f * (1.0f - std::fabs(offset) / static_cast(M_PI)); + float const score = minEndDist + alignBonus; + if (score > bestScore) + { + bestScore = score; + bestX = cx; + bestY = cy; + } + } + + if (bestScore == -FLT_MAX) + { + // No safe arc point - sprint straight away from the weighted center, + // but clamp endpoint to the anchor radius so we don't flee out of raid. + float fx = bx + STEP * std::cos(baseAngle); + float fy = by + STEP * std::sin(baseAngle); + float const dxA = fx - anchorX; + float const dyA = fy - anchorY; + float const distA = std::sqrt(dxA * dxA + dyA * dyA); + if (distA > ANCHOR_RADIUS_LIMIT) + { + fx = anchorX + (dxA / distA) * ANCHOR_RADIUS_LIMIT; + fy = anchorY + (dyA / distA) * ANCHOR_RADIUS_LIMIT; + } + return MoveTo(bot->GetMapId(), fx, fy, bot->GetPositionZ(), false, false, false, true, + MovementPriority::MOVEMENT_FORCED); + } + + return MoveTo(bot->GetMapId(), bestX, bestY, bot->GetPositionZ(), false, false, false, true, + MovementPriority::MOVEMENT_FORCED); +} diff --git a/src/Ai/Raid/Icecrown/RaidIccActionContext.h b/src/Ai/Raid/ICC/ICCActionContext.h similarity index 81% rename from src/Ai/Raid/Icecrown/RaidIccActionContext.h rename to src/Ai/Raid/ICC/ICCActionContext.h index e648b09b73a..2c1c4c9611f 100644 --- a/src/Ai/Raid/Icecrown/RaidIccActionContext.h +++ b/src/Ai/Raid/ICC/ICCActionContext.h @@ -1,9 +1,9 @@ -#ifndef _PLAYERBOT_RAIDICCACTIONCONTEXT_H -#define _PLAYERBOT_RAIDICCACTIONCONTEXT_H +#ifndef _PLAYERBOT_ICCACTIONCONTEXT_H +#define _PLAYERBOT_ICCACTIONCONTEXT_H #include "Action.h" #include "NamedObjectContext.h" -#include "RaidIccActions.h" +#include "ICCActions.h" class RaidIccActionContext : public NamedObjectContext { @@ -21,23 +21,29 @@ class RaidIccActionContext : public NamedObjectContext creators["icc rotting frost giant tank position"] = &RaidIccActionContext::icc_rotting_frost_giant_tank_position; creators["icc cannon fire"] = &RaidIccActionContext::icc_cannon_fire; creators["icc gunship enter cannon"] = &RaidIccActionContext::icc_gunship_enter_cannon; - creators["icc gunship teleport ally"] = &RaidIccActionContext::icc_gunship_teleport_ally; - creators["icc gunship teleport horde"] = &RaidIccActionContext::icc_gunship_teleport_horde; + creators["icc gunship rocket jump"] = &RaidIccActionContext::icc_gunship_rocket_jump; + creators["icc gunship rocket pack setup"] = &RaidIccActionContext::icc_gunship_rocket_pack_setup; creators["icc dbs tank position"] = &RaidIccActionContext::icc_dbs_tank_position; creators["icc adds dbs"] = &RaidIccActionContext::icc_adds_dbs; + creators["icc dogs tank position"] = &RaidIccActionContext::icc_dogs_tank_position; + creators["icc festergut group position"] = &RaidIccActionContext::icc_festergut_group_position; creators["icc festergut spore"] = &RaidIccActionContext::icc_festergut_spore; + creators["icc festergut avoid malleable goo"] = &RaidIccActionContext::icc_festergut_avoid_malleable_goo; creators["icc rotface tank position"] = &RaidIccActionContext::icc_rotface_tank_position; creators["icc rotface group position"] = &RaidIccActionContext::icc_rotface_group_position; creators["icc rotface move away from explosion"] = &RaidIccActionContext::icc_rotface_move_away_from_explosion; + creators["icc rotface avoid vile gas"] = &RaidIccActionContext::icc_rotface_avoid_vile_gas; + creators["icc putricide mutated plague"] = &RaidIccActionContext::icc_putricide_mutated_plague; creators["icc putricide volatile ooze"] = &RaidIccActionContext::icc_putricide_volatile_ooze; creators["icc putricide gas cloud"] = &RaidIccActionContext::icc_putricide_gas_cloud; creators["icc putricide growing ooze puddle"] = &RaidIccActionContext::icc_putricide_growing_ooze_puddle; creators["icc putricide avoid malleable goo"] = &RaidIccActionContext::icc_putricide_avoid_malleable_goo; + creators["icc putricide abomination"] = &RaidIccActionContext::icc_putricide_abomination; creators["icc bpc keleseth tank"] = &RaidIccActionContext::icc_bpc_keleseth_tank; creators["icc bpc main tank"] = &RaidIccActionContext::icc_bpc_main_tank; @@ -56,9 +62,11 @@ class RaidIccActionContext : public NamedObjectContext creators["icc valithria portal"] = &RaidIccActionContext::icc_valithria_portal; creators["icc valithria heal"] = &RaidIccActionContext::icc_valithria_heal; creators["icc valithria dream cloud"] = &RaidIccActionContext::icc_valithria_dream_cloud; + creators["icc valithria zombie kite"] = &RaidIccActionContext::icc_valithria_zombie_kite; creators["icc sindragosa group position"] = &RaidIccActionContext::icc_sindragosa_group_position; creators["icc sindragosa frost beacon"] = &RaidIccActionContext::icc_sindragosa_frost_beacon; + creators["icc sindragosa hot"] = &RaidIccActionContext::icc_sindragosa_hot; creators["icc sindragosa blistering cold"] = &RaidIccActionContext::icc_sindragosa_blistering_cold; creators["icc sindragosa unchained magic"] = &RaidIccActionContext::icc_sindragosa_unchained_magic; creators["icc sindragosa chilled to the bone"] = &RaidIccActionContext::icc_sindragosa_chilled_to_the_bone; @@ -70,6 +78,7 @@ class RaidIccActionContext : public NamedObjectContext creators["icc lich king necrotic plague"] = &RaidIccActionContext::icc_lich_king_necrotic_plague; creators["icc lich king winter"] = &RaidIccActionContext::icc_lich_king_winter; creators["icc lich king adds"] = &RaidIccActionContext::icc_lich_king_adds; + creators["icc lich king spirit bomb"] = &RaidIccActionContext::icc_lich_king_spirit_bomb; } private: @@ -84,23 +93,29 @@ class RaidIccActionContext : public NamedObjectContext static Action* icc_rotting_frost_giant_tank_position(PlayerbotAI* ai) { return new IccRottingFrostGiantTankPositionAction(ai); } static Action* icc_cannon_fire(PlayerbotAI* ai) { return new IccCannonFireAction(ai); } static Action* icc_gunship_enter_cannon(PlayerbotAI* ai) { return new IccGunshipEnterCannonAction(ai); } - static Action* icc_gunship_teleport_ally(PlayerbotAI* ai) { return new IccGunshipTeleportAllyAction(ai); } - static Action* icc_gunship_teleport_horde(PlayerbotAI* ai) { return new IccGunshipTeleportHordeAction(ai); } + static Action* icc_gunship_rocket_jump(PlayerbotAI* ai) { return new IccGunshipRocketJumpAction(ai); } + static Action* icc_gunship_rocket_pack_setup(PlayerbotAI* ai) { return new IccGunshipRocketPackSetupAction(ai); } static Action* icc_dbs_tank_position(PlayerbotAI* ai) { return new IccDbsTankPositionAction(ai); } static Action* icc_adds_dbs(PlayerbotAI* ai) { return new IccAddsDbsAction(ai); } + static Action* icc_dogs_tank_position(PlayerbotAI* ai) { return new IccDogsTankPositionAction(ai); } + static Action* icc_festergut_group_position(PlayerbotAI* ai) { return new IccFestergutGroupPositionAction(ai); } static Action* icc_festergut_spore(PlayerbotAI* ai) { return new IccFestergutSporeAction(ai); } + static Action* icc_festergut_avoid_malleable_goo(PlayerbotAI* ai) { return new IccFestergutAvoidMalleableGooAction(ai); } static Action* icc_rotface_tank_position(PlayerbotAI* ai) { return new IccRotfaceTankPositionAction(ai); } static Action* icc_rotface_group_position(PlayerbotAI* ai) { return new IccRotfaceGroupPositionAction(ai); } static Action* icc_rotface_move_away_from_explosion(PlayerbotAI* ai) { return new IccRotfaceMoveAwayFromExplosionAction(ai); } + static Action* icc_rotface_avoid_vile_gas(PlayerbotAI* ai) { return new IccRotfaceAvoidVileGasAction(ai); } + static Action* icc_putricide_mutated_plague(PlayerbotAI* ai) { return new IccPutricideMutatedPlagueAction(ai); } static Action* icc_putricide_volatile_ooze(PlayerbotAI* ai) { return new IccPutricideVolatileOozeAction(ai); } static Action* icc_putricide_gas_cloud(PlayerbotAI* ai) { return new IccPutricideGasCloudAction(ai); } static Action* icc_putricide_growing_ooze_puddle(PlayerbotAI* ai) { return new IccPutricideGrowingOozePuddleAction(ai); } static Action* icc_putricide_avoid_malleable_goo(PlayerbotAI* ai) { return new IccPutricideAvoidMalleableGooAction(ai); } + static Action* icc_putricide_abomination(PlayerbotAI* ai) { return new IccPutricideAbominationAction(ai); } static Action* icc_bpc_keleseth_tank(PlayerbotAI* ai) { return new IccBpcKelesethTankAction(ai); } static Action* icc_bpc_main_tank(PlayerbotAI* ai) { return new IccBpcMainTankAction(ai); } @@ -119,9 +134,11 @@ class RaidIccActionContext : public NamedObjectContext static Action* icc_valithria_portal(PlayerbotAI* ai) { return new IccValithriaPortalAction(ai); } static Action* icc_valithria_heal(PlayerbotAI* ai) { return new IccValithriaHealAction(ai); } static Action* icc_valithria_dream_cloud(PlayerbotAI* ai) { return new IccValithriaDreamCloudAction(ai); } + static Action* icc_valithria_zombie_kite(PlayerbotAI* ai) { return new IccValithriaZombieKiteAction(ai); } static Action* icc_sindragosa_group_position(PlayerbotAI* ai) { return new IccSindragosaGroupPositionAction(ai); } static Action* icc_sindragosa_frost_beacon(PlayerbotAI* ai) { return new IccSindragosaFrostBeaconAction(ai); } + static Action* icc_sindragosa_hot(PlayerbotAI* ai) { return new IccSindragosaHotAction(ai); } static Action* icc_sindragosa_blistering_cold(PlayerbotAI* ai) { return new IccSindragosaBlisteringColdAction(ai); } static Action* icc_sindragosa_unchained_magic(PlayerbotAI* ai) { return new IccSindragosaUnchainedMagicAction(ai); } static Action* icc_sindragosa_chilled_to_the_bone(PlayerbotAI* ai) { return new IccSindragosaChilledToTheBoneAction(ai); } @@ -133,6 +150,7 @@ class RaidIccActionContext : public NamedObjectContext static Action* icc_lich_king_necrotic_plague(PlayerbotAI* ai) { return new IccLichKingNecroticPlagueAction(ai); } static Action* icc_lich_king_winter(PlayerbotAI* ai) { return new IccLichKingWinterAction(ai); } static Action* icc_lich_king_adds(PlayerbotAI* ai) { return new IccLichKingAddsAction(ai); } + static Action* icc_lich_king_spirit_bomb(PlayerbotAI* ai) { return new IccLichKingSpiritBombAction(ai); } }; diff --git a/src/Ai/Raid/ICC/ICCMultipliers.cpp b/src/Ai/Raid/ICC/ICCMultipliers.cpp new file mode 100644 index 00000000000..f7e7679921e --- /dev/null +++ b/src/Ai/Raid/ICC/ICCMultipliers.cpp @@ -0,0 +1,1208 @@ +#include "ICCMultipliers.h" + +#include "ChooseTargetActions.h" +#include "DKActions.h" +#include "DruidActions.h" +#include "DruidBearActions.h" +#include "FollowActions.h" +#include "GenericActions.h" +#include "GenericSpellActions.h" +#include "HunterActions.h" +#include "MageActions.h" +#include "MovementActions.h" +#include "PaladinActions.h" +#include "PriestActions.h" +#include "ICCActions.h" +#include "ReachTargetActions.h" +#include "RogueActions.h" +#include "ShamanActions.h" +#include "UseMeetingStoneAction.h" +#include "WarriorActions.h" +#include "PlayerbotAI.h" +#include "ICCTriggers.h" +#include "ICCScripts.h" + +// LK global variables +namespace +{ +std::map g_plagueTimes; +std::map g_allowCure; +std::mutex g_plagueMutex; // Lock before accessing shared variables +} + +// Lady Deathwhisper +float IccLadyDeathwhisperMultiplier::GetValue(Action* action) +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "lady deathwhisper"); + if (!boss) + return 1.0f; + + if (dynamic_cast(action) || dynamic_cast(action) || dynamic_cast(action) || dynamic_cast(action)) + return 0.0f; + + static constexpr uint32 VENGEFUL_SHADE_ID = NPC_SHADE; + + // Get the nearest hostile NPCs + const GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); + + // Allow the IccShadeLadyDeathwhisperAction to run + if (dynamic_cast(action)) + return 1.0f; + + for (auto const& npcGuid : npcs) + { + Unit* shade = botAI->GetUnit(npcGuid); + + if (!shade || shade->GetEntry() != VENGEFUL_SHADE_ID) + continue; + + if (!shade->GetVictim() || shade->GetVictim()->GetGUID() != bot->GetGUID()) + continue; + + return 0.0f; // Cancel all other actions when we need to handle Vengeful Shade + } + + return 1.0f; +} + +// dbs +float IccAddsDbsMultiplier::GetValue(Action* action) +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "deathbringer saurfang"); + if (!boss) + return 1.0f; + + if (dynamic_cast(action) || dynamic_cast(action) || + dynamic_cast(action) || dynamic_cast(action) || + dynamic_cast(action) || dynamic_cast(action) || + dynamic_cast(action) || dynamic_cast(action) || + dynamic_cast(action) || dynamic_cast(action) || + dynamic_cast(action) || dynamic_cast(action)) + return 0.0f; + + if (botAI->IsRanged(bot)) + if (dynamic_cast(action)) + return 0.0f; + + if (botAI->IsTank(bot)) + { + Aura* aura = botAI->GetAura("rune of blood", bot); + if (aura) + { + if (dynamic_cast(action) || + dynamic_cast(action) || + dynamic_cast(action) || + dynamic_cast(action)) + return 0.0f; + + if (dynamic_cast(action)) + return 1.0f; + + return 0.0f; + } + } + + return 1.0f; +} + +// Gunship +float IccGunshipMultiplier::GetValue(Action* action) +{ + // Detect gunship encounter via hostile enemy captain nearby + Unit* saurfang = bot->FindNearestCreature(NPC_HIGH_OVERLORD_SAURFANG, 200.0f); + Unit* muradin = bot->FindNearestCreature(NPC_MURADIN_BRONZEBEARD, 200.0f); + bool const inGunship = (saurfang && saurfang->IsAlive() && saurfang->IsHostileTo(bot)) || + (muradin && muradin->IsAlive() && muradin->IsHostileTo(bot)); + if (!inGunship) + return 1.0f; + + if (dynamic_cast(action) || dynamic_cast(action)) + return 0.0f; + + // Main tank is locked to captain via IccGunshipRocketJumpAction — block RTI targeting + if (botAI->IsMainTank(bot) && dynamic_cast(action)) + return 0.0f; + + // Bot in transit between ships: lock to rocket-jump action only so combat/movement + // actions don't interfere with jump packet timing. + bool const isHordeSide = muradin && muradin->IsAlive() && muradin->IsHostileTo(bot); + bool const isAllySide = saurfang && saurfang->IsAlive() && saurfang->IsHostileTo(bot); + Position const friendlyPoint = isHordeSide ? ICC_GUNSHIP_ROCKET_JUMP_HORDE_FRIENDLY_POINT + : ICC_GUNSHIP_ROCKET_JUMP_ALLY_FRIENDLY_POINT; + Position const middlePoint = isHordeSide ? ICC_GUNSHIP_ROCKET_JUMP_HORDE_MIDDLE_POINT + : ICC_GUNSHIP_ROCKET_JUMP_ALLY_MIDDLE_POINT; + Position const attackPos = isHordeSide ? ICC_GUNSHIP_ROCKET_JUMP_HORDE + : ICC_GUNSHIP_ROCKET_JUMP_ALLY; + static constexpr float JUMP_GATE = 30.0f; + bool const nearFriendly = bot->GetExactDist2d(friendlyPoint) <= JUMP_GATE; + bool const nearMiddle = bot->GetExactDist2d(middlePoint) <= JUMP_GATE; + bool const nearAttack = bot->GetExactDist2d(attackPos) <= JUMP_GATE; + if (!nearFriendly && !nearMiddle && !nearAttack) + { + if (!dynamic_cast(action)) + return 0.0f; + } + + return 1.0f; +} + +// Dogs +float IccDogsMultiplier::GetValue(Action* action) +{ + if (!AI_VALUE2(Unit*, "find target", "stinky") && !AI_VALUE2(Unit*, "find target", "precious")) + return 1.0f; + + if (botAI->IsTank(bot)) + { + Aura* aura = botAI->GetAura("mortal wound", bot, false, true); + if (aura && aura->GetStackAmount() >= 8) + { + if (dynamic_cast(action)) + return 1.0f; + + if (dynamic_cast(action) || + dynamic_cast(action) || + dynamic_cast(action) || + dynamic_cast(action)) + return 0.0f; + + return 0.0f; + } + } + + if (dynamic_cast(action) || dynamic_cast(action)) + return 0.0f; + + return 1.0f; +} + +// Festergut +float IccFestergutMultiplier::GetValue(Action* action) +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "festergut"); + if (!boss) + return 1.0f; + + if (dynamic_cast(action) || dynamic_cast(action)) + return 0.0f; + + if (dynamic_cast(action)) + return 0.0f; + + if (dynamic_cast(action) || dynamic_cast(action)) + return 0.0f; + + if (botAI->IsTank(bot)) + { + Aura* aura = botAI->GetAura("gastric bloat", bot, false, true); + if (aura && aura->GetStackAmount() >= 6) + { + if (dynamic_cast(action) || + dynamic_cast(action) || + dynamic_cast(action) || + dynamic_cast(action)) + return 0.0f; + + if (dynamic_cast(action)) + return 1.0f; + + return 0.0f; + } + } + + if (dynamic_cast(action)) + return 1.0f; + + if (bot->HasAura(SPELL_GAS_SPORE)) + { + if (dynamic_cast(action) || dynamic_cast(action) || + dynamic_cast(action)) + return 0.0f; + } + + // Hold position during the 8s malleable goo wait window so the bot can + // keep DPS/heal rotations running without drifting back into the impact. + // Avoid action itself is whitelisted (may still need to dodge new goos). + { + auto const& waitMap = IcecrownHelpers::festergutGooWaitUntil; + auto it = waitMap.find(bot->GetGUID()); + if (it != waitMap.end() && getMSTime() < it->second) + { + if (dynamic_cast(action)) + return 1.0f; + + if (dynamic_cast(action)) + return 0.0f; + } + } + + return 1.0f; +} + +// Rotface +float IccRotfaceMultiplier::GetValue(Action* action) +{ + Unit* boss1 = AI_VALUE2(Unit*, "find target", "rotface"); + if (!boss1) + return 1.0f; + + { + uint32 const now = getMSTime(); + auto const& waitMap = IcecrownHelpers::rotfaceVileGasWaitUntil; + auto it = waitMap.find(bot->GetGUID()); + bool const inWait = it != waitMap.end() && now < it->second; + auto vgIt = IcecrownHelpers::rotfaceVileGas.find(bot->GetMap()->GetInstanceId()); + bool const isVictim = + vgIt != IcecrownHelpers::rotfaceVileGas.end() && + vgIt->second.victimGuid == bot->GetGUID() && + getMSTimeDiff(vgIt->second.castTime, now) < 8000; + + if (isVictim || inWait || botAI->HasAura("Vile Gas", bot)) + { + if (dynamic_cast(action)) + return 1.0f; + if (dynamic_cast(action)) + return 0.0f; + } + } + + if (botAI->HasAura("Vile Gas", bot)) + return 0.0f; + + if (botAI->IsTank(bot) && dynamic_cast(action)) + return 0.0f; + + if (dynamic_cast(action) || dynamic_cast(action)) + return 0.0f; + + if (dynamic_cast(action) && !(bot->getClass() == CLASS_HUNTER)) + return 0.0f; + + if (dynamic_cast(action) || dynamic_cast(action)) + return 0.0f; + + if (botAI->IsAssistTank(bot) && + (dynamic_cast(action) || dynamic_cast(action) || + dynamic_cast(action) || dynamic_cast(action) || + dynamic_cast(action) || dynamic_cast(action))) + return 0.0f; + + if (botAI->IsAssistTank(bot) && boss1 && bot->GetVictim() == boss1) + { + bot->AttackStop(); + bot->SetTarget(ObjectGuid::Empty); + return 0.0f; + } + + // Never cure/dispel Mutated Infection — it must expire naturally to spawn a small ooze + if (dynamic_cast(action) || dynamic_cast(action) || + dynamic_cast(action) || + dynamic_cast(action) || dynamic_cast(action)) + return 0.0f; + + { + Creature* bigOoze = bot->FindNearestCreature(NPC_BIG_OOZE, 100.0f); + bool castingNow = bigOoze && bigOoze->IsAlive() && + bigOoze->HasUnitState(UNIT_STATE_CASTING) && bigOoze->FindCurrentSpellBySpellId(SPELL_UNSTABLE_OOZE_EXPLOSION); + + if (castingNow && (dynamic_cast(action) || dynamic_cast(action)) && + !dynamic_cast(action)) + return 0.0f; + } + + return 1.0f; +} + +// pp +float IccAddsPutricideMultiplier::GetValue(Action* action) +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "professor putricide"); + if (!boss) + return 1.0f; + Unit* boss1 = AI_VALUE2(Unit*, "find target", "rotface"); + if (boss1) + return 1.0f; + Unit* boss2 = AI_VALUE2(Unit*, "find target", "festergut"); + if (boss2) + return 1.0f; + + if (Unit* vehBase = bot->GetVehicleBase()) + { + uint32 e = vehBase->GetEntry(); + if (e == NPC_MUTATED_ABOMINATION_10 || e == NPC_MUTATED_ABOMINATION_25) + { + if (dynamic_cast(action)) + return 1.0f; + return 0.0f; + } + } + + bool hasGaseousBloat = botAI->HasAura("Gaseous Bloat", bot); + bool hasUnboundPlague = botAI->HasAura("Unbound Plague", bot); + + if (botAI->IsTank(bot) && + bot->GetMotionMaster()->GetCurrentMovementGeneratorType() == FOLLOW_MOTION_TYPE) + { + if (dynamic_cast(action) || + dynamic_cast(action)) + return 1.0f; + return 0.0f; + } + + if (!(bot->getClass() == CLASS_HUNTER) && dynamic_cast(action)) + return 0.0f; + + if (dynamic_cast(action)) + return 0.0f; + + if (dynamic_cast(action)) + return 0.0f; + + if (dynamic_cast(action)) + return 0.0f; + + if (botAI->IsTank(bot)) + { + auto GetPlagueStacks = [&](Unit* unit) -> uint32 + { + if (!unit) + return 0; + Aura* a = botAI->GetAura("mutated plague", unit, false, true); + return a ? a->GetStackAmount() : 0; + }; + + uint32 const myStacks = GetPlagueStacks(bot); + + // Another tank has fewer stacks — they should be tanking instead of us. + // Block generic taunts (so we don't fight for aggro), keep movement, and + // cancel the rest of the rotation (IccPutricideMutatedPlagueAction owns + // the AttackStop/taunt handoff). + bool anotherTankHasFewer = false; + if (Group* group = bot->GetGroup()) + { + for (GroupReference* itr = group->GetFirstMember(); itr; itr = itr->next()) + { + Player* member = itr->GetSource(); + if (!member || member == bot || !member->IsAlive() || !member->IsInWorld()) + continue; + if (!PlayerbotAI::IsTank(member)) + continue; + + if (GetPlagueStacks(member) < myStacks) + { + anotherTankHasFewer = true; + break; + } + } + } + + if (anotherTankHasFewer) + { + if (dynamic_cast(action) || + dynamic_cast(action) || + dynamic_cast(action) || + dynamic_cast(action)) + return 0.0f; + + if (dynamic_cast(action)) + return 1.0f; + + if (dynamic_cast(action)) + return 1.0f; + + return 0.0f; + } + } + + if (hasGaseousBloat) + { + if (dynamic_cast(action)) + return 1.0f; + + if (dynamic_cast(action)) + return 1.0f; + + if (botAI->IsHeal(bot)) + return 1.0f; + else + return 0.0f; // Cancel all other actions when we need to handle Gaseous Bloat + } + + if (hasUnboundPlague && boss && !boss->HealthBelowPct(35)) + { + if (dynamic_cast(action)) + return 1.0f; + else + return 0.0f; // Cancel all other actions when we need to handle Unbound Plague + } + + if (dynamic_cast(action)) + { + if (dynamic_cast(action)) + return 1.0f; + if (dynamic_cast(action) && !botAI->IsMainTank(bot)) + return 0.0f; + } + + return 1.0f; +} + +// bpc +float IccBpcAssistMultiplier::GetValue(Action* action) +{ + Unit* keleseth = AI_VALUE2(Unit*, "find target", "prince keleseth"); + if (!keleseth) + return 1.0f; + + if (keleseth && (dynamic_cast(action) || dynamic_cast(action) || + dynamic_cast(action) || dynamic_cast(action) || + dynamic_cast(action) || dynamic_cast(action) || + dynamic_cast(action) || dynamic_cast(action) || + dynamic_cast(action) || dynamic_cast(action) || + dynamic_cast(action) || dynamic_cast(action) || + dynamic_cast(action))) + return 0.0f; + + Aura* aura = botAI->GetAura("Shadow Prison", bot, false, true); + + // Bomb assignment check (done early so it can override shadow prison stack limits) + static const std::array bombEntries = {NPC_KINETIC_BOMB1, NPC_KINETIC_BOMB2, NPC_KINETIC_BOMB3, + NPC_KINETIC_BOMB4}; + GuidVector const bombs = AI_VALUE(GuidVector, "possible targets no los"); + + std::vector kineticBombs; + for (auto const& guid : bombs) + { + Unit* unit = botAI->GetUnit(guid); + if (!unit || !unit->IsAlive()) + continue; + + if (std::find(bombEntries.begin(), bombEntries.end(), unit->GetEntry()) == bombEntries.end()) + continue; + + if (unit->GetPositionZ() - bot->GetPositionZ() > 35.0f) + continue; + + kineticBombs.push_back(unit); + } + + bool botAssignedToBomb = false; + if (!kineticBombs.empty() && botAI->IsRangedDps(bot) && + !(aura && aura->GetStackAmount() > 18)) + { + std::sort(kineticBombs.begin(), kineticBombs.end(), + [](Unit* a, Unit* b) { return a->GetPositionZ() < b->GetPositionZ(); }); + + std::vector rangedDps; + Group* group = bot->GetGroup(); + if (group) + { + for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next()) + { + Player* member = itr->GetSource(); + if (member && member->IsAlive() && GET_PLAYERBOT_AI(member) && botAI->IsRangedDps(member)) + rangedDps.push_back(member); + } + } + + static float const MAX_ASSIGN_RANGE = 80.0f; + std::set assigned; + for (Unit* bomb : kineticBombs) + { + Player* nearest = nullptr; + float nearestDist = std::numeric_limits::max(); + + // Priority classes: hunter > druid > any + static constexpr std::array classPriority = {CLASS_HUNTER, CLASS_DRUID, 0}; + for (uint8 priorityClass : classPriority) + { + nearest = nullptr; + nearestDist = std::numeric_limits::max(); + + for (Player* dps : rangedDps) + { + if (assigned.count(dps)) + continue; + + if (priorityClass != 0 && dps->getClass() != priorityClass) + continue; + + float dist = dps->GetDistance(bomb); + if (dist < nearestDist && dist < MAX_ASSIGN_RANGE) + { + nearestDist = dist; + nearest = dps; + } + } + + if (nearest) + break; + } + + if (nearest) + { + assigned.insert(nearest); + if (nearest == bot) + botAssignedToBomb = true; + } + } + } + + // Bomb-assigned bot: block target switching and non-bomb BPC actions, allow combat rotation + if (botAssignedToBomb) + { + if (dynamic_cast(action) || dynamic_cast(action)) + return 1.0f; + + if (dynamic_cast(action) || dynamic_cast(action) || + dynamic_cast(action) || dynamic_cast(action) || + dynamic_cast(action) || dynamic_cast(action) || + dynamic_cast(action)) + return 0.0f; + } + + // Shadow prison movement block (non-bomb bots use normal 12 stack limit) + if (aura) + { + if (aura->GetStackAmount() > 18 && botAI->IsTank(bot)) + { + if (dynamic_cast(action)) + return 0.0f; + } + + if (aura->GetStackAmount() > 12 && !botAI->IsTank(bot)) + { + if (dynamic_cast(action)) + return 0.0f; + } + } + + Unit* valanar = AI_VALUE2(Unit*, "find target", "prince valanar"); + if (!valanar) + return 1.0f; + + if (valanar && valanar->HasUnitState(UNIT_STATE_CASTING) && + (valanar->FindCurrentSpellBySpellId(SPELL_EMPOWERED_SHOCK_VORTEX1) || + valanar->FindCurrentSpellBySpellId(SPELL_EMPOWERED_SHOCK_VORTEX2) || + valanar->FindCurrentSpellBySpellId(SPELL_EMPOWERED_SHOCK_VORTEX3) || + valanar->FindCurrentSpellBySpellId(SPELL_EMPOWERED_SHOCK_VORTEX4))) + { + if (dynamic_cast(action) || dynamic_cast(action)) + return 1.0f; + else + return 0.0f; + } + + Unit* flame1 = bot->FindNearestCreature(NPC_BALL_OF_FLAME, 100.0f); + Unit* flame2 = bot->FindNearestCreature(NPC_BALL_OF_INFERNO_FLAME, 100.0f); + bool ballOfFlame = flame1 && flame1->GetVictim() == bot; + bool infernoFlame = flame2 && flame2->GetVictim() == bot; + + if (flame2) + { + if (dynamic_cast(action) || dynamic_cast(action)) + return 0.0f; + + if (dynamic_cast(action)) + return 1.0f; + } + + if (ballOfFlame || infernoFlame) + { + if (dynamic_cast(action)) + return 1.0f; + else + return 0.0f; + } + + // For assist tank during BPC fight + if (botAI->IsAssistTank(bot) && !(aura && aura->GetStackAmount() > 18)) + { + // Allow BPC-specific actions + if (dynamic_cast(action)) + return 1.0f; + + // Disable normal assist behavior (allow RTI targeting) + if (dynamic_cast(action) || + dynamic_cast(action) || + dynamic_cast(action)) + return 0.0f; + } + + return 1.0f; +} + +//BQL +float IccBqlMultiplier::GetValue(Action* action) +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "blood-queen lana'thel"); + if (!boss) + return 1.0f; + + Aura* aura2 = botAI->GetAura("Swarming Shadows", bot); + Aura* aura = botAI->GetAura("Frenzied Bloodthirst", bot); + + if (botAI->IsRanged(bot)) + if (dynamic_cast(action) || dynamic_cast(action) || + dynamic_cast(action) || dynamic_cast(action)) + return 0.0f; + + // If bot has Pact of Darkfallen aura, return 0 for all other actions + if (bot->HasAura(SPELL_PACT_OF_THE_DARKFALLEN)) + { + if (dynamic_cast(action)) + return 1.0f; // Allow Pact of Darkfallen action + else + return 0.0f; // Cancel all other actions when we need to handle Pact of Darkfallen + } + + // Air phase: block movement/chase actions, allow combat rotation (attacks/heals) + if (((boss->GetPositionZ() - ICC_BQL_CENTER_POSITION.GetPositionZ()) > 5.0f) && !aura) + { + if (dynamic_cast(action) || dynamic_cast(action) || + dynamic_cast(action) || dynamic_cast(action) || + dynamic_cast(action) || dynamic_cast(action)) + return 0.0f; + } + + // If bot has frenzied bloodthirst, allow highest priority for bite action + if (aura) // If bot has frenzied bloodthirst + { + if (dynamic_cast(action)) + return 1.0f; + else + return 0.0f; + } + + if (aura2 && !aura) + { + if (dynamic_cast(action)) + return 1.0f; + else + return 0.0f; // Cancel all other actions when we need to handle Swarming Shadows + } + + if ((boss->GetExactDist2d(ICC_BQL_TANK_POSITION.GetPositionX(), ICC_BQL_TANK_POSITION.GetPositionY()) > 10.0f) && + botAI->IsRanged(bot) && !((boss->GetPositionZ() - bot->GetPositionZ()) > 5.0f)) + { + if (dynamic_cast(action) || dynamic_cast(action)) + return 0.0f; + } + + return 1.0f; +} + +//VDW +float IccValithriaDreamCloudMultiplier::GetValue(Action* action) +{ + Unit* boss = bot->FindNearestCreature(NPC_VALITHRIA_DREAMWALKER, 100.0f); + + Aura* twistedNightmares = botAI->GetAura("Twisted Nightmares", bot); + Aura* emeraldVigor = botAI->GetAura("Emerald Vigor", bot); + + if (!boss && !bot->HasAura(SPELL_DREAM_STATE)) + return 1.0f; + + if (dynamic_cast(action) || dynamic_cast(action)) + return 0.0f; + + // Zombie victim: only the kite action runs. Blocks combat/movement so bot + // doesn't try to attack/cast/move toward marks while being chased. + if (boss && !botAI->IsTank(bot)) + { + Creature* attackingZombie = nullptr; + std::list zombies; + bot->GetCreatureListWithEntryInGrid(zombies, NPC_BLISTERING_ZOMBIE, 100.0f); + for (Creature* z : zombies) + { + if (z && z->IsAlive() && z->GetVictim() == bot) + { + attackingZombie = z; + break; + } + } + if (attackingZombie) + { + if (dynamic_cast(action)) + return 1.0f; + return 0.0f; + } + } + + if (botAI->IsTank(bot)) + { + if (dynamic_cast(action)) + return 0.0f; + } + else + { + // Non-tanks must strictly follow RTI marks. Block generic assist actions + // so bots never attack unmarked adds; AttackRtiTargetAction drives them to + // skull/cross targets set by HandleMarkingLogic. + if (dynamic_cast(action)) + return 0.0f; + + // Melee bots must not engage Blistering Zombies (one-shot melee swing). + // Only ranged DPS handle them. If RTI/current target is a zombie, block + // attack actions so melee falls through to other priorities. + if (!PlayerbotAI::IsRangedDps(bot) && !botAI->IsHeal(bot)) + { + Unit* victim = bot->GetVictim(); + bool victimIsZombie = victim && victim->GetEntry() == NPC_BLISTERING_ZOMBIE; + + bool rtiIsZombie = false; + if (Group* group = bot->GetGroup()) + { + ObjectGuid rtiGuid = group->GetTargetIcon(7); + if (!rtiGuid.IsEmpty()) + { + Unit* rtiUnit = ObjectAccessor::GetUnit(*bot, rtiGuid); + if (rtiUnit && rtiUnit->GetEntry() == NPC_BLISTERING_ZOMBIE) + rtiIsZombie = true; + } + } + + if (victimIsZombie || rtiIsZombie) + { + if (dynamic_cast(action) || + dynamic_cast(action)) + return 0.0f; + } + } + } + + if (botAI->IsHeal(bot) && (twistedNightmares || emeraldVigor)) + if (dynamic_cast(action) || dynamic_cast(action)) + return 0.0f; + + if (bot->HasAura(SPELL_DREAM_STATE) && !bot->HealthBelowPct(50)) + { + if (dynamic_cast(action)) + return 1.0f; // Allow Dream Cloud action + else + return 0.0f; // Cancel all other actions when we need to handle Dream Cloud + } + + return 1.0f; + +} + +//SINDRAGOSA + +float IccSindragosaMultiplier::GetValue(Action* action) +{ + Unit* boss = bot->FindNearestCreature(NPC_SINDRAGOSA, 200.0f); + if (!boss) + return 1.0f; + + // HoT support is an instant cast that never moves the bot. Always allow so + // beaconed targets stay topped up across air phase, blistering cold cast, + // phase 3 tank lockdown, and other "everything else 0.0f" branches below. + if (dynamic_cast(action)) + return 1.0f; + + Aura* aura = botAI->GetAura("Unchained Magic", bot, false, true); + + Difficulty diff = bot->GetRaidDifficulty(); + + if (boss->HealthBelowPct(95)) + { + if (dynamic_cast(action) || dynamic_cast(action) || + dynamic_cast(action) || dynamic_cast(action)) + return 0.0f; + } + + if (aura && (diff == RAID_DIFFICULTY_10MAN_HEROIC || diff == RAID_DIFFICULTY_25MAN_HEROIC) && + !dynamic_cast(action)) + { + if (dynamic_cast(action) || dynamic_cast(action)) + return 1.0f; + else + return 0.0f; + } + + // Check if boss is casting blistering cold (using both normal and heroic spell IDs) + if (boss->HasUnitState(UNIT_STATE_CASTING) && + (boss->FindCurrentSpellBySpellId(SPELL_BLISTERING_COLD1) || boss->FindCurrentSpellBySpellId(SPELL_BLISTERING_COLD2) || + boss->FindCurrentSpellBySpellId(SPELL_BLISTERING_COLD3) || boss->FindCurrentSpellBySpellId(SPELL_BLISTERING_COLD4))) + { + // If this is the blistering cold action, give it highest priority + if (dynamic_cast(action) || + dynamic_cast(action) || + dynamic_cast(action) || + dynamic_cast(action)) + return 1.0f; + + // Ranged / healer already beyond the blast radius: keep DPSing or + // healing, just block any movement so they don't wander back in. + bool const safe = bot->GetExactDist2d(boss) >= 33.0f; + if (safe && (botAI->IsRanged(bot) || botAI->IsHeal(bot))) + { + if (dynamic_cast(action)) + return 0.0f; + return 1.0f; + } + + // Disable all other actions while blistering cold is casting + return 0.0f; + } + + // Highest priority if we have beacon + if (bot->HasAura(SPELL_FROST_BEACON)) + { + if (dynamic_cast(action)) + return 1.0f; + else + return 0.0f; + } + + Group* group = bot->GetGroup(); + // Check if anyone in group has Frost Beacon (SPELL_FROST_BEACON) + bool anyoneHasFrostBeacon = false; + + if (group) + { + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + if (member && member->IsAlive() && member->HasAura(SPELL_FROST_BEACON)) + { + anyoneHasFrostBeacon = true; + break; + } + } + } + + if (anyoneHasFrostBeacon && boss && + boss->GetExactDist2d(ICC_SINDRAGOSA_FLYING_POSITION.GetPositionX(), + ICC_SINDRAGOSA_FLYING_POSITION.GetPositionY()) < 30.0f && + !boss->HealthBelowPct(25) && !boss->HealthAbovePct(99)) + { + if (dynamic_cast(action)) + return 1.0f; + else + return 0.0f; + } + + if (anyoneHasFrostBeacon && !botAI->IsMainTank(bot)) + { + if (dynamic_cast(action)) + return 0.0f; + } + + if (botAI->IsMainTank(bot)) + { + Aura* aura = botAI->GetAura("mystic buffet", bot, false, true); + if (aura && aura->GetStackAmount() >= 6) + { + if (dynamic_cast(action)) + return 1.0f; + else + return 0.0f; + } + } + + if (!botAI->IsTank(bot) && boss && boss->HealthBelowPct(35)) + { + if (dynamic_cast(action)) + return 0.0f; + } + + if (boss && botAI->IsTank(bot)) + { + if (boss->HealthBelowPct(35)) + { + if (dynamic_cast(action) || dynamic_cast(action) || + dynamic_cast(action) || dynamic_cast(action)) + return 1.0f; + else + return 0.0f; + } + } + + if (boss && boss->GetExactDist2d(ICC_SINDRAGOSA_FLYING_POSITION.GetPositionX(), ICC_SINDRAGOSA_FLYING_POSITION.GetPositionY()) < 30.0f && !boss->HealthBelowPct(25) && !boss->HealthAbovePct(99)) + { + if (dynamic_cast(action)) + return 1.0f; + + if (dynamic_cast(action) || dynamic_cast(action) || + dynamic_cast(action) || dynamic_cast(action) || + dynamic_cast(action) || dynamic_cast(action) || + dynamic_cast(action) || dynamic_cast(action) || dynamic_cast(action) || + dynamic_cast(action) || dynamic_cast(action) || + dynamic_cast(action) || dynamic_cast(action) || + dynamic_cast(action) || dynamic_cast(action) || + dynamic_cast(action) || dynamic_cast(action) || + dynamic_cast(action) || dynamic_cast(action) || + dynamic_cast(action) || dynamic_cast(action) || + dynamic_cast(action) || dynamic_cast(action) || + dynamic_cast(action) || dynamic_cast(action)) + return 0.0f; + } + + return 1.0f; +} + +float IccLichKingAddsMultiplier::GetValue(Action* action) +{ + if (bot->FindNearestCreature(NPC_VALITHRIA_DREAMWALKER, 100.0f)) + return 1.0f; + + Unit* terenas = bot->FindNearestCreature(NPC_TERENAS_MENETHIL_HC, 55.0f); + if (terenas) + { + // Warlocks and melee stay functional (movement + adds action only) + if (botAI->IsMelee(bot) || bot->getClass() == CLASS_WARLOCK) + { + if (dynamic_cast(action) || dynamic_cast(action)) + return 1.0f; + return 0.0f; + } + + // Main tank near another tank: suppress movement jitter + Unit* mainTank = AI_VALUE(Unit*, "main tank"); + if (!botAI->IsMainTank(bot) && mainTank && bot->GetExactDist2d(mainTank) < 2.0f && + dynamic_cast(action)) + return 0.0f; + + // Suppress all these regardless of role + if (dynamic_cast(action) || dynamic_cast(action) || + dynamic_cast(action) || dynamic_cast(action) || + dynamic_cast(action) || dynamic_cast(action) || + dynamic_cast(action) || dynamic_cast(action) || + dynamic_cast(action) || dynamic_cast(action) || + dynamic_cast(action)) + return 0.0f; + + return 1.0f; + } + + Unit* boss = AI_VALUE2(Unit*, "find target", "the lich king"); + if (!boss) + return 1.0f; + + // Allow cure actions only after a brief delay so the plague can spread once + if (dynamic_cast(action) || dynamic_cast(action) || + dynamic_cast(action) || + dynamic_cast(action) || dynamic_cast(action)) + { + Unit* boss = AI_VALUE2(Unit*, "find target", "the lich king"); + if (!boss) + return 1.0f; + + Group* group = bot->GetGroup(); + if (!group) + return 1.0f; + + static constexpr float DELIVER_RANGE = 3.0f; + static constexpr std::array HorrorEntries = {NPC_SHAMBLING_HORROR1, NPC_SHAMBLING_HORROR2, + NPC_SHAMBLING_HORROR3, NPC_SHAMBLING_HORROR4}; + + // Check whether any Shambling Horror is alive anywhere in the encounter + auto const anyHorrorAlive = [&]() -> bool + { + GuidVector const& npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); + for (ObjectGuid const& guid : npcs) + { + Unit* unit = botAI->GetUnit(guid); + if (!unit || !unit->IsAlive()) + continue; + + uint32 const entry = unit->GetEntry(); + if (entry == NPC_SHAMBLING_HORROR1 || entry == NPC_SHAMBLING_HORROR2 || + entry == NPC_SHAMBLING_HORROR3 || entry == NPC_SHAMBLING_HORROR4) + return true; + } + return false; + }; + + bool anyPlagued = false; + bool allDelivered = true; + + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + if (!member || !member->IsAlive()) + continue; + + if (!botAI->HasAura("Necrotic Plague", member)) + continue; + + anyPlagued = true; + + bool nearHorror = false; + for (uint32 const entry : HorrorEntries) + { + Creature* horror = member->FindNearestCreature(entry, DELIVER_RANGE); + if (horror && horror->IsAlive()) + { + nearHorror = true; + break; + } + } + + if (!nearHorror) + { + allDelivered = false; + break; + } + } + + if (!anyPlagued) + return 1.0f; + + // No Horror alive at all — allow immediate dispel to prevent + // uncontrolled spread wiping the raid + if (!anyHorrorAlive()) + return 1.0f; + + // Horrors exist but not everyone has delivered yet — suppress cures + return allDelivered ? 1.0f : 0.0f; + } + + if (dynamic_cast(action) || dynamic_cast(action) || + dynamic_cast(action) || dynamic_cast(action)) + return 0.0f; + + // Hunters may flee (kite mechanics); everyone else stays put + if (dynamic_cast(action) && bot->getClass() != CLASS_HUNTER) + return 0.0f; + + if (boss->HealthAbovePct(71)) + { + // Assist tank targeting is fully managed by HandleAssistTankAddManagement — + // suppress generic target-switching actions so they don't override it. + if (botAI->IsAssistTank(bot) && + (dynamic_cast(action) || dynamic_cast(action) || + dynamic_cast(action))) + return 0.0f; + + if (!botAI->IsTank(bot) && dynamic_cast(action)) + return 0.0f; + + if (dynamic_cast(action) || dynamic_cast(action) || + dynamic_cast(action) || dynamic_cast(action) || + dynamic_cast(action) || dynamic_cast(action) || + dynamic_cast(action) || dynamic_cast(action) || + dynamic_cast(action) || dynamic_cast(action) || + dynamic_cast(action) || dynamic_cast(action) || + dynamic_cast(action)) + return 0.0f; + } + + auto const hasWinterAura = [&]() -> bool + { + return boss->HasAura(SPELL_REMORSELESS_WINTER1) || boss->HasAura(SPELL_REMORSELESS_WINTER2) || + boss->HasAura(SPELL_REMORSELESS_WINTER3) || boss->HasAura(SPELL_REMORSELESS_WINTER4) || + boss->HasAura(SPELL_REMORSELESS_WINTER5) || boss->HasAura(SPELL_REMORSELESS_WINTER6) || + boss->HasAura(SPELL_REMORSELESS_WINTER7) || boss->HasAura(SPELL_REMORSELESS_WINTER8); + }; + + auto const isCastingWinter = [&]() -> bool + { + if (!boss->HasUnitState(UNIT_STATE_CASTING)) + return false; + + return boss->FindCurrentSpellBySpellId(SPELL_REMORSELESS_WINTER1) || + boss->FindCurrentSpellBySpellId(SPELL_REMORSELESS_WINTER2) || + boss->FindCurrentSpellBySpellId(SPELL_REMORSELESS_WINTER3) || + boss->FindCurrentSpellBySpellId(SPELL_REMORSELESS_WINTER4) || + boss->FindCurrentSpellBySpellId(SPELL_REMORSELESS_WINTER5) || + boss->FindCurrentSpellBySpellId(SPELL_REMORSELESS_WINTER6) || + boss->FindCurrentSpellBySpellId(SPELL_REMORSELESS_WINTER7) || + boss->FindCurrentSpellBySpellId(SPELL_REMORSELESS_WINTER8); + }; + + if (hasWinterAura() || isCastingWinter()) + { + // Winter action and facing take priority + if (dynamic_cast(action) || dynamic_cast(action)) + return 1.0f; + + // Staging window: while boss is casting Winter, non-tanks must commit + // to the staging move. Only heals are allowed; everything else blocked. + if (isCastingWinter() && !botAI->IsTank(bot)) + { + if (dynamic_cast(action) || + dynamic_cast(action)) + return 1.0f; + return 0.0f; + } + + // Adds action is suppressed during winter + if (dynamic_cast(action)) + return 0.0f; + + if (dynamic_cast(action)) + return 0.0f; + + // Assist tank should not pick up adds independently during winter + if (botAI->IsAssistTank(bot) && dynamic_cast(action)) + return 0.0f; + + // Suppress movement/attack toward the boss if we are far away + Unit* currentTarget = AI_VALUE(Unit*, "current target"); + if (currentTarget && currentTarget == boss && bot->GetDistance2d(boss) > 50.0f) + { + if (dynamic_cast(action) || + dynamic_cast(action) || dynamic_cast(action) || + dynamic_cast(action) || dynamic_cast(action)) + return 0.0f; + } + + // Suppress movement toward boss/sphere — but allow target-switching actions + // (DpsAssistAction, AttackRtiTargetAction) so bots can pick up skull-marked adds. + if (currentTarget && + (currentTarget == boss || currentTarget->GetEntry() == NPC_ICE_SPHERE1 || + currentTarget->GetEntry() == NPC_ICE_SPHERE2 || + currentTarget->GetEntry() == NPC_ICE_SPHERE3 || currentTarget->GetEntry() == NPC_ICE_SPHERE4)) + { + if (dynamic_cast(action) || dynamic_cast(action) || + dynamic_cast(action) || dynamic_cast(action)) + return 0.0f; + } + } + + if (botAI->IsRanged(bot) && !botAI->GetAura("Harvest Soul", bot, false, false)) + { + GuidVector const& npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); + bool defilePresent = false; + for (ObjectGuid const& guid : npcs) + { + Unit* unit = botAI->GetUnit(guid); + if (unit && unit->IsAlive() && unit->GetEntry() == DEFILE_NPC_ID) + { + defilePresent = true; + break; + } + } + + if (defilePresent && (dynamic_cast(action) || dynamic_cast(action) || + dynamic_cast(action) || dynamic_cast(action) || + dynamic_cast(action))) + return 0.0f; + } + + if (botAI->IsAssistTank(bot) && boss->HealthAbovePct(71)) + { + Unit* currentTarget = AI_VALUE(Unit*, "current target"); + if (currentTarget && currentTarget == boss && dynamic_cast(action)) + return 0.0f; + } + + return 1.0f; +} + +float IccLichKingSpiritBombMultiplier::GetValue(Action* action) +{ + if (!IccLichKingSpiritBombAction::IsBombThreatActive(botAI, bot)) + return 1.0f; + + // Allowlist: only the avoidance move and facing run during a bomb threat. + // Everything else is suppressed so the avoidance move sticks. + if (dynamic_cast(action) || + dynamic_cast(action)) + return 1.0f; + + return 0.0f; +} diff --git a/src/Ai/Raid/Icecrown/Multiplier/RaidIccMultipliers.h b/src/Ai/Raid/ICC/ICCMultipliers.h similarity index 84% rename from src/Ai/Raid/Icecrown/Multiplier/RaidIccMultipliers.h rename to src/Ai/Raid/ICC/ICCMultipliers.h index 69d1ac51bac..245b4d1321b 100644 --- a/src/Ai/Raid/Icecrown/Multiplier/RaidIccMultipliers.h +++ b/src/Ai/Raid/ICC/ICCMultipliers.h @@ -1,5 +1,5 @@ -#ifndef _PLAYERBOT_RAIDICCMULTIPLIERS_H -#define _PLAYERBOT_RAIDICCMULTIPLIERS_H +#ifndef _PLAYERBOT_ICCM_H +#define _PLAYERBOT_ICCM_H #include "Multiplier.h" @@ -99,4 +99,19 @@ class IccLichKingAddsMultiplier : public Multiplier virtual float GetValue(Action* action); }; +class IccLichKingSpiritBombMultiplier : public Multiplier +{ +public: + IccLichKingSpiritBombMultiplier(PlayerbotAI* ai) : Multiplier(ai, "icc lich king spirit bomb") {} + virtual float GetValue(Action* action); +}; + +//GUNSHIP +class IccGunshipMultiplier : public Multiplier +{ +public: + IccGunshipMultiplier(PlayerbotAI* ai) : Multiplier(ai, "icc gunship") {} + virtual float GetValue(Action* action); +}; + #endif diff --git a/src/Ai/Raid/ICC/ICCScripts.cpp b/src/Ai/Raid/ICC/ICCScripts.cpp new file mode 100644 index 00000000000..81c42c00590 --- /dev/null +++ b/src/Ai/Raid/ICC/ICCScripts.cpp @@ -0,0 +1,123 @@ +#include "ICCScripts.h" +#include "Player.h" +#include "ICCTriggers.h" +#include "ScriptMgr.h" +#include "Spell.h" +#include "SpellInfo.h" +#include "Timer.h" +#include + +namespace IcecrownHelpers +{ + std::unordered_map> malleableGooImpacts; + std::map festergutGooWaitUntil; + std::unordered_map defileCast; + std::unordered_map rotfaceVileGas; + std::map rotfaceVileGasWaitUntil; +} + +class IccPutricideListenerScript : public AllSpellScript +{ +public: + IccPutricideListenerScript() : AllSpellScript("IccPutricideListenerScript") { } + + void OnSpellCast(Spell* spell, Unit* caster, SpellInfo const* spellInfo, bool /*skipCheck*/) override + { + if (!caster || !spellInfo) + return; + + if (spellInfo->Id != SPELL_MALLEABLE_GOO_10N && + spellInfo->Id != SPELL_MALLEABLE_GOO_25N && + spellInfo->Id != SPELL_MALLEABLE_GOO_10H && + spellInfo->Id != SPELL_MALLEABLE_GOO_25H && + spellInfo->Id != SPELL_MALLEABLE_GOO_BALCONY) + return; + + // Malleable Goo is cast triggered, so m_UniqueTargetInfo is not yet + // populated at this point; read the explicit unit target directly. + Unit* target = spell->m_targets.GetUnitTarget(); + if (!target || !target->IsPlayer()) + return; + + uint32 now = getMSTime(); + + IcecrownHelpers::MalleableGooImpact impact; + impact.position = target->GetPosition(); + impact.castTime = now; + + auto& impacts = IcecrownHelpers::malleableGooImpacts[caster->GetMap()->GetInstanceId()]; + impacts.push_back(impact); + + // Evict stale entries to keep the list bounded. Retention covers the + // longest consumer window (Festergut avoid: 8s) + slack. + impacts.erase( + std::remove_if(impacts.begin(), impacts.end(), + [now](IcecrownHelpers::MalleableGooImpact const& i) + { return getMSTimeDiff(i.castTime, now) > 9000; }), + impacts.end()); + } +}; + +class IccRotfaceListenerScript : public AllSpellScript +{ +public: + IccRotfaceListenerScript() : AllSpellScript("IccRotfaceListenerScript") { } + + void OnSpellCast(Spell* spell, Unit* caster, SpellInfo const* spellInfo, bool /*skipCheck*/) override + { + if (!caster || !spellInfo) + return; + + if (spellInfo->Id != SPELL_VILE_GAS_H) + return; + + // Professor Putricide casts vile gas from the balcony during Rotface + // heroic. Filtering on caster entry keeps this hook scoped to the + // Rotface encounter only (Festergut also uses 'vile gas' as the gas + // spore aura name but a different spell ID). + if (caster->GetEntry() != NPC_PROFESSOR_PUTRICIDE) + return; + + Unit* target = spell->m_targets.GetUnitTarget(); + if (!target || !target->IsPlayer()) + return; + + IcecrownHelpers::VileGasVictim& entry = IcecrownHelpers::rotfaceVileGas[caster->GetMap()->GetInstanceId()]; + entry.victimGuid = target->GetGUID(); + entry.castTime = getMSTime(); + } +}; + +class IccLichKingListenerScript : public AllSpellScript +{ +public: + IccLichKingListenerScript() : AllSpellScript("IccLichKingListenerScript") { } + + // OnSpellPrepare fires at cast START (Spell::prepare). OnSpellCast fires + // at cast END, which for Defile (2s cast time) is too late - the puddle + // is already spawning and bots have no time to move out. + void OnSpellPrepare(Spell* spell, Unit* caster, SpellInfo const* spellInfo) override + { + if (!caster || !spellInfo) + return; + + if (spellInfo->Id != DEFILE_CAST_ID) + return; + + Unit* target = spell->m_targets.GetUnitTarget(); + if (!target || !target->IsPlayer()) + return; + + IcecrownHelpers::DefileCastInfo& entry = + IcecrownHelpers::defileCast[caster->GetMap()->GetInstanceId()]; + entry.targetGuid = target->GetGUID(); + entry.castTime = getMSTime(); + } +}; + +void AddSC_IcecrownBotScripts() +{ + new IccPutricideListenerScript(); + new IccRotfaceListenerScript(); + new IccLichKingListenerScript(); +} diff --git a/src/Ai/Raid/ICC/ICCScripts.h b/src/Ai/Raid/ICC/ICCScripts.h new file mode 100644 index 00000000000..28e02b00209 --- /dev/null +++ b/src/Ai/Raid/ICC/ICCScripts.h @@ -0,0 +1,68 @@ +#ifndef _PLAYERBOT_ICCSCRIPTS_H +#define _PLAYERBOT_ICCSCRIPTS_H + +#include +#include +#include +#include "ObjectGuid.h" +#include "Position.h" + +namespace IcecrownHelpers +{ + // Putricide - Malleable Goo + // Each entry records the impact position (target's location at cast time) + // and the ms timestamp of the cast. IccPutricideAvoidMalleableGooAction + // reads this list on every tick and makes every bot flee any active + // impact points, since the core casts the spell triggered (no cast bar) + // and it is neither a DynamicObject, trap, nor trigger NPC. + struct MalleableGooImpact + { + Position position; + uint32 castTime; + }; + extern std::unordered_map> malleableGooImpacts; + + // Festergut avoid-malleable-goo wait state. When a bot dodges goo we stamp + // a wait-until timestamp here so the trigger stays active and movement is + // held for the full 8s impact window - otherwise the group-position action + // pulls the bot back the very next tick, producing jitter. + extern std::map festergutGooWaitUntil; + + // Lich King - Defile (SPELL_DEFILE = 72762). Stamped at OnSpellCast time + // because the boss script casts via CastSpell(target, ...) and reading the + // target later via current-spell APIs is unreliable. Readers treat entries + // older than ~3s as expired (cast time is 2s). + struct DefileCastInfo + { + ObjectGuid targetGuid; + uint32 castTime; + }; + extern std::unordered_map defileCast; + + // Rotface - Vile Gas. Stamped at OnSpellCast time so the targeted bot can + // react before the aura applies. Readers treat entries older than ~5s as + // expired (covers the dodge window plus the 3s post-arrival hold). + struct VileGasVictim + { + ObjectGuid victimGuid; + uint32 castTime; + }; + extern std::unordered_map rotfaceVileGas; + + // Rotface vile gas hold-at-safe-spot state. When the victim bot reaches + // its safe spot we stamp now+3000ms so the multiplier blocks any other + // movement action that would yank the bot back into the raid stack. + extern std::map rotfaceVileGasWaitUntil; +} + +// Putricide - Mutated Abomination vehicle +constexpr uint32 GO_PUTRICIDE_DRINK_ME = 201584; +constexpr uint32 NPC_MUTATED_ABOMINATION_10 = 37672; +constexpr uint32 NPC_MUTATED_ABOMINATION_25 = 38285; +constexpr uint32 SPELL_MUTATED_TRANSFORMATION = 70311; +constexpr uint32 SPELL_ABO_EAT_OOZE = 70346; +constexpr uint32 SPELL_ABO_REGURGITATED_OOZE = 70539; + +void AddSC_IcecrownBotScripts(); + +#endif diff --git a/src/Ai/Raid/Icecrown/Strategy/RaidIccStrategy.cpp b/src/Ai/Raid/ICC/ICCStrategy.cpp similarity index 81% rename from src/Ai/Raid/Icecrown/Strategy/RaidIccStrategy.cpp rename to src/Ai/Raid/ICC/ICCStrategy.cpp index e0dbe82306e..8e651354036 100644 --- a/src/Ai/Raid/Icecrown/Strategy/RaidIccStrategy.cpp +++ b/src/Ai/Raid/ICC/ICCStrategy.cpp @@ -1,6 +1,6 @@ -#include "RaidIccStrategy.h" +#include "ICCStrategy.h" -#include "RaidIccMultipliers.h" +#include "ICCMultipliers.h" void RaidIccStrategy::InitTriggers(std::vector& triggers) { @@ -28,34 +28,32 @@ void RaidIccStrategy::InitTriggers(std::vector& triggers) triggers.push_back( new TriggerNode("icc in cannon", { NextAction("icc cannon fire", ACTION_RAID+5) })); - triggers.push_back(new TriggerNode("icc gunship teleport ally", - { NextAction("icc gunship teleport ally", ACTION_RAID + 4) })); + triggers.push_back(new TriggerNode("icc gunship rocket jump", + { NextAction("icc gunship rocket jump", ACTION_RAID + 4)})); - triggers.push_back(new TriggerNode("icc gunship teleport horde", - { NextAction("icc gunship teleport horde", ACTION_RAID + 4) })); + triggers.push_back(new TriggerNode("icc gunship rocket pack setup", + { NextAction("icc gunship rocket pack setup", ACTION_RAID + 2)})); //DBS triggers.push_back(new TriggerNode("icc dbs", { NextAction("icc dbs tank position", ACTION_RAID + 3), NextAction("icc adds dbs", ACTION_RAID + 5) })); - triggers.push_back(new TriggerNode("icc dbs main tank rune of blood", - { NextAction("taunt spell", ACTION_EMERGENCY + 4) })); + // Boss taunt on Rune of Blood is handled inside icc dbs tank position action - //DOGS - triggers.push_back(new TriggerNode("icc stinky precious main tank mortal wound", - { NextAction("taunt spell", ACTION_EMERGENCY + 4) })); + triggers.push_back(new TriggerNode("icc dogs", + { NextAction("icc dogs tank position", ACTION_RAID + 3) })); //FESTERGUT triggers.push_back(new TriggerNode("icc festergut group position", { NextAction("icc festergut group position", ACTION_MOVE + 4) })); - triggers.push_back(new TriggerNode("icc festergut main tank gastric bloat", - { NextAction("taunt spell", ACTION_EMERGENCY + 6) })); - triggers.push_back(new TriggerNode("icc festergut spore", { NextAction("icc festergut spore", ACTION_MOVE + 5) })); + triggers.push_back(new TriggerNode("icc festergut avoid malleable goo", + { NextAction("icc festergut avoid malleable goo", ACTION_RAID + 7) })); + //ROTFACE triggers.push_back(new TriggerNode("icc rotface tank position", { NextAction("icc rotface tank position", ACTION_RAID + 5) })); @@ -66,6 +64,9 @@ void RaidIccStrategy::InitTriggers(std::vector& triggers) triggers.push_back(new TriggerNode("icc rotface move away from explosion", { NextAction("icc rotface move away from explosion", ACTION_RAID +7) })); + triggers.push_back(new TriggerNode("icc rotface avoid vile gas", + { NextAction("icc rotface avoid vile gas", ACTION_RAID + 8) })); + //PP triggers.push_back(new TriggerNode("icc putricide volatile ooze", { NextAction("icc putricide volatile ooze", ACTION_RAID + 4) })); @@ -76,11 +77,14 @@ void RaidIccStrategy::InitTriggers(std::vector& triggers) triggers.push_back(new TriggerNode("icc putricide growing ooze puddle", { NextAction("icc putricide growing ooze puddle", ACTION_RAID + 3) })); - triggers.push_back(new TriggerNode("icc putricide main tank mutated plague", - { NextAction("taunt spell", ACTION_RAID + 10) })); + triggers.push_back(new TriggerNode("icc putricide mutated plague", + { NextAction("icc putricide mutated plague", ACTION_RAID + 3) })); triggers.push_back(new TriggerNode("icc putricide malleable goo", - { NextAction("icc putricide avoid malleable goo", ACTION_RAID + 2) })); + { NextAction("icc putricide avoid malleable goo", ACTION_RAID + 6) })); + + triggers.push_back(new TriggerNode("icc putricide abomination", + { NextAction("icc putricide abomination", ACTION_RAID + 7) })); //BPC triggers.push_back(new TriggerNode("icc bpc keleseth tank", @@ -119,6 +123,9 @@ void RaidIccStrategy::InitTriggers(std::vector& triggers) triggers.push_back(new TriggerNode("icc valithria group", { NextAction("icc valithria group", ACTION_RAID + 1) })); + triggers.push_back(new TriggerNode("icc valithria zombie kite", + { NextAction("icc valithria zombie kite", ACTION_EMERGENCY + 9) })); + triggers.push_back(new TriggerNode("icc valithria portal", { NextAction("icc valithria portal", ACTION_RAID + 5) })); @@ -135,6 +142,9 @@ void RaidIccStrategy::InitTriggers(std::vector& triggers) triggers.push_back(new TriggerNode("icc sindragosa frost beacon", { NextAction("icc sindragosa frost beacon", ACTION_RAID + 5) })); + triggers.push_back(new TriggerNode("icc sindragosa hot", + { NextAction("icc sindragosa hot", ACTION_RAID + 6) })); + triggers.push_back(new TriggerNode("icc sindragosa blistering cold", { NextAction("icc sindragosa blistering cold", ACTION_EMERGENCY + 4) })); @@ -168,6 +178,9 @@ void RaidIccStrategy::InitTriggers(std::vector& triggers) triggers.push_back(new TriggerNode("icc lich king adds", { NextAction("icc lich king adds", ACTION_RAID +2) })); + + triggers.push_back(new TriggerNode("icc lich king spirit bomb", + { NextAction("icc lich king spirit bomb", ACTION_RAID +7) })); } void RaidIccStrategy::InitMultipliers(std::vector& multipliers) @@ -183,4 +196,6 @@ void RaidIccStrategy::InitMultipliers(std::vector& multipliers) multipliers.push_back(new IccValithriaDreamCloudMultiplier(botAI)); multipliers.push_back(new IccSindragosaMultiplier(botAI)); multipliers.push_back(new IccLichKingAddsMultiplier(botAI)); + multipliers.push_back(new IccLichKingSpiritBombMultiplier(botAI)); + multipliers.push_back(new IccGunshipMultiplier(botAI)); } diff --git a/src/Ai/Raid/Icecrown/Strategy/RaidIccStrategy.h b/src/Ai/Raid/ICC/ICCStrategy.h similarity index 83% rename from src/Ai/Raid/Icecrown/Strategy/RaidIccStrategy.h rename to src/Ai/Raid/ICC/ICCStrategy.h index fbd54cc6482..8f331eba383 100644 --- a/src/Ai/Raid/Icecrown/Strategy/RaidIccStrategy.h +++ b/src/Ai/Raid/ICC/ICCStrategy.h @@ -1,5 +1,5 @@ -#ifndef _PLAYERBOT_RAIDICCSTRATEGY_H -#define _PLAYERBOT_RAIDICCSTRATEGY_H +#ifndef _PLAYERBOT_ICCS_H +#define _PLAYERBOT_ICCS_H #include "Strategy.h" diff --git a/src/Ai/Raid/Icecrown/RaidIccTriggerContext.h b/src/Ai/Raid/ICC/ICCTriggerContext.h similarity index 81% rename from src/Ai/Raid/Icecrown/RaidIccTriggerContext.h rename to src/Ai/Raid/ICC/ICCTriggerContext.h index 83f3004668e..ca6e8f7da01 100644 --- a/src/Ai/Raid/Icecrown/RaidIccTriggerContext.h +++ b/src/Ai/Raid/ICC/ICCTriggerContext.h @@ -1,8 +1,8 @@ -#ifndef _PLAYERBOT_RAIDICCTRIGGERCONTEXT_H -#define _PLAYERBOT_RAIDICCTRIGGERCONTEXT_H +#ifndef _PLAYERBOT_ICCTRIGGERCONTEXT_H +#define _PLAYERBOT_ICCTRIGGERCONTEXT_H #include "NamedObjectContext.h" -#include "RaidIccTriggers.h" +#include "ICCTriggers.h" class RaidIccTriggerContext : public NamedObjectContext { @@ -17,27 +17,29 @@ class RaidIccTriggerContext : public NamedObjectContext creators["icc rotting frost giant tank position"] = &RaidIccTriggerContext::icc_rotting_frost_giant_tank_position; creators["icc in cannon"] = &RaidIccTriggerContext::icc_in_cannon; creators["icc gunship cannon near"] = &RaidIccTriggerContext::icc_gunship_cannon_near; - creators["icc gunship teleport ally"] = &RaidIccTriggerContext::icc_gunship_teleport_ally; - creators["icc gunship teleport horde"] = &RaidIccTriggerContext::icc_gunship_teleport_horde; + creators["icc gunship rocket jump"] = &RaidIccTriggerContext::icc_gunship_rocket_jump; + creators["icc gunship rocket pack setup"] = &RaidIccTriggerContext::icc_gunship_rocket_pack_setup; creators["icc dbs"] = &RaidIccTriggerContext::icc_dbs; creators["icc dbs main tank rune of blood"] = &RaidIccTriggerContext::icc_dbs_main_tank_rune_of_blood; - creators["icc stinky precious main tank mortal wound"] = &RaidIccTriggerContext::icc_stinky_precious_main_tank_mortal_wound; + creators["icc dogs"] = &RaidIccTriggerContext::icc_dogs; creators["icc festergut group position"] = &RaidIccTriggerContext::icc_festergut_group_position; - creators["icc festergut main tank gastric bloat"] = &RaidIccTriggerContext::icc_festergut_main_tank_gastric_bloat; creators["icc festergut spore"] = &RaidIccTriggerContext::icc_festergut_spore; + creators["icc festergut avoid malleable goo"] = &RaidIccTriggerContext::icc_festergut_avoid_malleable_goo; creators["icc rotface tank position"] = &RaidIccTriggerContext::icc_rotface_tank_position; creators["icc rotface group position"] = &RaidIccTriggerContext::icc_rotface_group_position; creators["icc rotface move away from explosion"] = &RaidIccTriggerContext::icc_rotface_move_away_from_explosion; + creators["icc rotface avoid vile gas"] = &RaidIccTriggerContext::icc_rotface_avoid_vile_gas; creators["icc putricide volatile ooze"] = &RaidIccTriggerContext::icc_putricide_volatile_ooze; creators["icc putricide gas cloud"] = &RaidIccTriggerContext::icc_putricide_gas_cloud; creators["icc putricide growing ooze puddle"] = &RaidIccTriggerContext::icc_putricide_growing_ooze_puddle; - creators["icc putricide main tank mutated plague"] = &RaidIccTriggerContext::icc_putricide_main_tank_mutated_plague; + creators["icc putricide mutated plague"] = &RaidIccTriggerContext::icc_putricide_mutated_plague; creators["icc putricide malleable goo"] = &RaidIccTriggerContext::icc_putricide_malleable_goo; + creators["icc putricide abomination"] = &RaidIccTriggerContext::icc_putricide_abomination; creators["icc bpc keleseth tank"] = &RaidIccTriggerContext::icc_bpc_keleseth_tank; creators["icc bpc main tank"] = &RaidIccTriggerContext::icc_bpc_main_tank; @@ -56,9 +58,11 @@ class RaidIccTriggerContext : public NamedObjectContext creators["icc valithria portal"] = &RaidIccTriggerContext::icc_valithria_portal; creators["icc valithria heal"] = &RaidIccTriggerContext::icc_valithria_heal; creators["icc valithria dream cloud"] = &RaidIccTriggerContext::icc_valithria_dream_cloud; + creators["icc valithria zombie kite"] = &RaidIccTriggerContext::icc_valithria_zombie_kite; creators["icc sindragosa group position"] = &RaidIccTriggerContext::icc_sindragosa_group_position; creators["icc sindragosa frost beacon"] = &RaidIccTriggerContext::icc_sindragosa_frost_beacon; + creators["icc sindragosa hot"] = &RaidIccTriggerContext::icc_sindragosa_hot; creators["icc sindragosa blistering cold"] = &RaidIccTriggerContext::icc_sindragosa_blistering_cold; creators["icc sindragosa unchained magic"] = &RaidIccTriggerContext::icc_sindragosa_unchained_magic; creators["icc sindragosa chilled to the bone"] = &RaidIccTriggerContext::icc_sindragosa_chilled_to_the_bone; @@ -71,6 +75,7 @@ class RaidIccTriggerContext : public NamedObjectContext creators["icc lich king necrotic plague"] = &RaidIccTriggerContext::icc_lich_king_necrotic_plague; creators["icc lich king winter"] = &RaidIccTriggerContext::icc_lich_king_winter; creators["icc lich king adds"] = &RaidIccTriggerContext::icc_lich_king_adds; + creators["icc lich king spirit bomb"] = &RaidIccTriggerContext::icc_lich_king_spirit_bomb; } private: @@ -82,27 +87,29 @@ class RaidIccTriggerContext : public NamedObjectContext static Trigger* icc_rotting_frost_giant_tank_position(PlayerbotAI* ai) { return new IccRottingFrostGiantTankPositionTrigger(ai); } static Trigger* icc_in_cannon(PlayerbotAI* ai) { return new IccInCannonTrigger(ai); } static Trigger* icc_gunship_cannon_near(PlayerbotAI* ai) { return new IccGunshipCannonNearTrigger(ai); } - static Trigger* icc_gunship_teleport_ally(PlayerbotAI* ai) { return new IccGunshipTeleportAllyTrigger(ai); } - static Trigger* icc_gunship_teleport_horde(PlayerbotAI* ai) { return new IccGunshipTeleportHordeTrigger(ai); } + static Trigger* icc_gunship_rocket_jump(PlayerbotAI* ai) { return new IccGunshipRocketJumpTrigger(ai); } + static Trigger* icc_gunship_rocket_pack_setup(PlayerbotAI* ai) { return new IccGunshipRocketPackSetupTrigger(ai); } static Trigger* icc_dbs(PlayerbotAI* ai) { return new IccDbsTrigger(ai); } static Trigger* icc_dbs_main_tank_rune_of_blood(PlayerbotAI* ai) { return new IccDbsMainTankRuneOfBloodTrigger(ai); } - static Trigger* icc_stinky_precious_main_tank_mortal_wound(PlayerbotAI* ai) { return new IccStinkyPreciousMainTankMortalWoundTrigger(ai); } + static Trigger* icc_dogs(PlayerbotAI* ai) { return new IccDogsTrigger(ai); } static Trigger* icc_festergut_group_position(PlayerbotAI* ai) { return new IccFestergutGroupPositionTrigger(ai); } - static Trigger* icc_festergut_main_tank_gastric_bloat(PlayerbotAI* ai) { return new IccFestergutMainTankGastricBloatTrigger(ai); } static Trigger* icc_festergut_spore(PlayerbotAI* ai) { return new IccFestergutSporeTrigger(ai); } + static Trigger* icc_festergut_avoid_malleable_goo(PlayerbotAI* ai) { return new IccFestergutAvoidMalleableGooTrigger(ai); } static Trigger* icc_rotface_tank_position(PlayerbotAI* ai) { return new IccRotfaceTankPositionTrigger(ai); } static Trigger* icc_rotface_group_position(PlayerbotAI* ai) { return new IccRotfaceGroupPositionTrigger(ai); } static Trigger* icc_rotface_move_away_from_explosion(PlayerbotAI* ai) { return new IccRotfaceMoveAwayFromExplosionTrigger(ai); } + static Trigger* icc_rotface_avoid_vile_gas(PlayerbotAI* ai) { return new IccRotfaceAvoidVileGasTrigger(ai); } static Trigger* icc_putricide_volatile_ooze(PlayerbotAI* ai) { return new IccPutricideVolatileOozeTrigger(ai); } static Trigger* icc_putricide_gas_cloud(PlayerbotAI* ai) { return new IccPutricideGasCloudTrigger(ai); } static Trigger* icc_putricide_growing_ooze_puddle(PlayerbotAI* ai) { return new IccPutricideGrowingOozePuddleTrigger(ai); } - static Trigger* icc_putricide_main_tank_mutated_plague(PlayerbotAI* ai) { return new IccPutricideMainTankMutatedPlagueTrigger(ai); } + static Trigger* icc_putricide_mutated_plague(PlayerbotAI* ai) { return new IccPutricideMutatedPlagueTrigger(ai); } static Trigger* icc_putricide_malleable_goo(PlayerbotAI* ai) { return new IccPutricideMalleableGooTrigger(ai); } + static Trigger* icc_putricide_abomination(PlayerbotAI* ai) { return new IccPutricideAbominationTrigger(ai); } static Trigger* icc_bpc_keleseth_tank(PlayerbotAI* ai) { return new IccBpcKelesethTankTrigger(ai); } static Trigger* icc_bpc_main_tank(PlayerbotAI* ai) { return new IccBpcMainTankTrigger(ai); } @@ -120,10 +127,12 @@ class RaidIccTriggerContext : public NamedObjectContext static Trigger* icc_valithria_group(PlayerbotAI* ai) { return new IccValithriaGroupTrigger(ai); } static Trigger* icc_valithria_portal(PlayerbotAI* ai) { return new IccValithriaPortalTrigger(ai); } static Trigger* icc_valithria_heal(PlayerbotAI* ai) { return new IccValithriaHealTrigger(ai); } + static Trigger* icc_valithria_zombie_kite(PlayerbotAI* ai) { return new IccValithriaZombieKiteTrigger(ai); } static Trigger* icc_valithria_dream_cloud(PlayerbotAI* ai) { return new IccValithriaDreamCloudTrigger(ai); } static Trigger* icc_sindragosa_group_position(PlayerbotAI* ai) { return new IccSindragosaGroupPositionTrigger(ai); } static Trigger* icc_sindragosa_frost_beacon(PlayerbotAI* ai) { return new IccSindragosaFrostBeaconTrigger(ai); } + static Trigger* icc_sindragosa_hot(PlayerbotAI* ai) { return new IccSindragosaHotTrigger(ai); } static Trigger* icc_sindragosa_blistering_cold(PlayerbotAI* ai) { return new IccSindragosaBlisteringColdTrigger(ai); } static Trigger* icc_sindragosa_unchained_magic(PlayerbotAI* ai) { return new IccSindragosaUnchainedMagicTrigger(ai); } static Trigger* icc_sindragosa_chilled_to_the_bone(PlayerbotAI* ai) { return new IccSindragosaChilledToTheBoneTrigger(ai); } @@ -136,6 +145,7 @@ class RaidIccTriggerContext : public NamedObjectContext static Trigger* icc_lich_king_necrotic_plague(PlayerbotAI* ai) { return new IccLichKingNecroticPlagueTrigger(ai); } static Trigger* icc_lich_king_winter(PlayerbotAI* ai) { return new IccLichKingWinterTrigger(ai); } static Trigger* icc_lich_king_adds(PlayerbotAI* ai) { return new IccLichKingAddsTrigger(ai); } + static Trigger* icc_lich_king_spirit_bomb(PlayerbotAI* ai) { return new IccLichKingSpiritBombTrigger(ai); } }; diff --git a/src/Ai/Raid/Icecrown/Trigger/RaidIccTriggers.cpp b/src/Ai/Raid/ICC/ICCTriggers.cpp similarity index 64% rename from src/Ai/Raid/Icecrown/Trigger/RaidIccTriggers.cpp rename to src/Ai/Raid/ICC/ICCTriggers.cpp index bc304ec0c61..c3a28a2a576 100644 --- a/src/Ai/Raid/Icecrown/Trigger/RaidIccTriggers.cpp +++ b/src/Ai/Raid/ICC/ICCTriggers.cpp @@ -1,5 +1,5 @@ -#include "RaidIccTriggers.h" -#include "RaidIccActions.h" +#include "ICCTriggers.h" +#include "ICCActions.h" #include "NearestNpcsValue.h" #include "PlayerbotAIConfig.h" #include "ObjectAccessor.h" @@ -8,6 +8,7 @@ #include "Trigger.h" #include "GridNotifiers.h" #include "Vehicle.h" +#include "ICCScripts.h" //Lord Marrogwar bool IccLmTrigger::IsActive() @@ -37,9 +38,6 @@ bool IccLadyDeathwhisperTrigger::IsActive() if (!boss) return false; - if (bot->HasAura(SPELL_EXPERIENCED)) - bot->RemoveAura(SPELL_EXPERIENCED); - return true; } @@ -73,52 +71,60 @@ bool IccGunshipCannonNearTrigger::IsActive() return false; Unit* mount1 = bot->FindNearestCreature(NPC_CANNONA, 100.0f); - Unit* mount2 = bot->FindNearestCreature(NPC_CANNONH, 100.0f); if (!mount1 && !mount2) return false; - if (!botAI->IsDps(bot)) + // If cannons have Below Zero aura, don't try to enter them + Unit* friendlyCannon = nullptr; + if (mount1 && mount1->IsFriendlyTo(bot)) + friendlyCannon = mount1; + else if (mount2 && mount2->IsFriendlyTo(bot)) + friendlyCannon = mount2; + + if (friendlyCannon && friendlyCannon->HasAura(SPELL_BELOW_ZERO)) return false; - // Player* master = botAI->GetMaster(); - // if (!master) - // return false; - // if (!master->GetVehicle()) - // return false; + if (!botAI->IsDps(bot)) + return false; return true; } -bool IccGunshipTeleportAllyTrigger::IsActive() +bool IccGunshipRocketJumpTrigger::IsActive() { - Unit* boss = bot->FindNearestCreature(NPC_HIGH_OVERLORD_SAURFANG, 100.0f); - if (!boss || !boss->IsInWorld() || boss->IsDuringRemoveFromWorld()) - return false; - - if (!boss->IsAlive()) - return false; + // The rocket jump mechanic is only needed when the gunship battle is active. + // We detect which ship we are on by checking which enemy boss is present: + // - Saurfang hostile => we are on the Alliance ship + // - Muradin hostile => we are on the Horde ship + // Using the hostile boss (not cannon friendliness) avoids conflicting with + // the cannon-near trigger that fires on the same condition. + Unit* saurfang = bot->FindNearestCreature(NPC_HIGH_OVERLORD_SAURFANG, 100.0f); + if (saurfang && saurfang->IsAlive() && saurfang->IsHostileTo(bot)) + return true; - if (!boss->IsHostileTo(bot)) - return false; + Unit* muradin = bot->FindNearestCreature(NPC_MURADIN_BRONZEBEARD, 100.0f); + if (muradin && muradin->IsAlive() && muradin->IsHostileTo(bot)) + return true; - return true; + return false; } -bool IccGunshipTeleportHordeTrigger::IsActive() +bool IccGunshipRocketPackSetupTrigger::IsActive() { - Unit* boss = bot->FindNearestCreature(NPC_MURADIN_BRONZEBEARD, 100.0f); - if (!boss || !boss->IsInWorld() || boss->IsDuringRemoveFromWorld()) - return false; - - if (!boss->IsAlive()) - return false; + // Fires any time a bot is standing on a friendly gunship deck, regardless of + // combat state. Lets bots walk to Zafod and equip the rocket pack before the + // encounter starts (and keep it ready if they acquire it mid-fight). + Unit* cannonA = bot->FindNearestCreature(NPC_CANNONA, 100.0f); + if (cannonA && cannonA->IsFriendlyTo(bot)) + return true; - if (!boss->IsHostileTo(bot)) - return false; + Unit* cannonH = bot->FindNearestCreature(NPC_CANNONH, 100.0f); + if (cannonH && cannonH->IsFriendlyTo(bot)) + return true; - return true; + return false; } //DBS @@ -155,28 +161,12 @@ bool IccDbsMainTankRuneOfBloodTrigger::IsActive() return true; } -//DOGS -bool IccStinkyPreciousMainTankMortalWoundTrigger::IsActive() +bool IccDogsTrigger::IsActive() { - bool bossPresent = false; if (AI_VALUE2(Unit*, "find target", "stinky") || AI_VALUE2(Unit*, "find target", "precious")) - bossPresent = true; - - if (!bossPresent) - return false; - - if (!botAI->IsAssistTankOfIndex(bot, 0)) - return false; - - Unit* mt = AI_VALUE(Unit*, "main tank"); - if (!mt) - return false; - - Aura* aura = botAI->GetAura("mortal wound", mt, false, true); - if (!aura || aura->GetStackAmount() < 8) - return false; + return true; - return true; + return false; } //FESTERGUT @@ -192,30 +182,6 @@ bool IccFestergutGroupPositionTrigger::IsActive() return true; } -bool IccFestergutMainTankGastricBloatTrigger::IsActive() -{ - Unit* boss = AI_VALUE2(Unit*, "find target", "festergut"); - if (!boss) - { - return false; - } - if (!botAI->IsAssistTankOfIndex(bot, 0)) - { - return false; - } - Unit* mt = AI_VALUE(Unit*, "main tank"); - if (!mt) - { - return false; - } - Aura* aura = botAI->GetAura("Gastric Bloat", mt, false, true); - if (!aura || aura->GetStackAmount() < 6) - { - return false; - } - return true; -} - bool IccFestergutSporeTrigger::IsActive() { Unit* boss = AI_VALUE2(Unit*, "find target", "festergut"); @@ -240,6 +206,71 @@ bool IccFestergutSporeTrigger::IsActive() return false; } +bool IccFestergutAvoidMalleableGooTrigger::IsActive() +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "festergut"); + if (!boss) + return false; + + // Tanks hold the boss at the fixed tank spot; goo can land on tanks but + // moving would lose threat and let goo land on melee stack anyway. + if (botAI->IsTank(bot)) + return false; + + // During spore phase, position switching handles goo avoidance — free-dodge + // would pull bots out of their assigned spore spots. + Group* group = bot->GetGroup(); + if (group) + { + for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next()) + { + Player* member = itr->GetSource(); + if (member && member->HasAura(SPELL_GAS_SPORE)) + return false; + } + } + + constexpr uint32 impactLifetimeMs = 8000; + constexpr float gooDangerRadius = 12.0f; + + uint32 now = getMSTime(); + float botX = bot->GetPositionX(); + float botY = bot->GetPositionY(); + ObjectGuid botGuid = bot->GetGUID(); + + auto impactIt = IcecrownHelpers::malleableGooImpacts.find(bot->GetMap()->GetInstanceId()); + if (impactIt != IcecrownHelpers::malleableGooImpacts.end()) + { + for (auto const& impact : impactIt->second) + { + if (getMSTimeDiff(impact.castTime, now) > impactLifetimeMs) + continue; + float dx = botX - impact.position.GetPositionX(); + float dy = botY - impact.position.GetPositionY(); + if (dx * dx + dy * dy < gooDangerRadius * gooDangerRadius) + { + // Lock bot into wait mode until this impact expires - prevents + // group-position from yanking it back into the danger zone. + uint32 waitUntil = impact.castTime + impactLifetimeMs; + auto& slot = IcecrownHelpers::festergutGooWaitUntil[botGuid]; + if (waitUntil > slot) + slot = waitUntil; + return true; + } + } + } + + auto it = IcecrownHelpers::festergutGooWaitUntil.find(botGuid); + if (it != IcecrownHelpers::festergutGooWaitUntil.end()) + { + if (now < it->second) + return true; + IcecrownHelpers::festergutGooWaitUntil.erase(it); + } + + return false; +} + //ROTFACE bool IccRotfaceTankPositionTrigger::IsActive() { @@ -264,11 +295,58 @@ bool IccRotfaceGroupPositionTrigger::IsActive() bool IccRotfaceMoveAwayFromExplosionTrigger::IsActive() { - Unit* boss = AI_VALUE2(Unit*, "find target", "big ooze"); + Creature* boss = bot->FindNearestCreature(NPC_BIG_OOZE, 100.0f); + bool castingNow = boss && boss->IsAlive() && + boss->HasUnitState(UNIT_STATE_CASTING) && boss->FindCurrentSpellBySpellId(SPELL_UNSTABLE_OOZE_EXPLOSION); + + if (castingNow) + { + _wasCasting = true; + _castEndTime = 0; + return true; + } + + // Cast just ended — record the time + if (_wasCasting) + { + _wasCasting = false; + if (_castEndTime == 0) + _castEndTime = time(nullptr); + } + + // Stay active for 6 seconds after cast ended (2s wait + return movement) + if (_castEndTime > 0 && time(nullptr) - _castEndTime < 6) + return true; + + _castEndTime = 0; + return false; +} + +bool IccRotfaceAvoidVileGasTrigger::IsActive() +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "rotface"); if (!boss) return false; - return boss->HasUnitState(UNIT_STATE_CASTING) && boss->FindCurrentSpellBySpellId(SPELL_UNSTABLE_OOZE_EXPLOSION); + uint32 const now = getMSTime(); + + auto vgIt = IcecrownHelpers::rotfaceVileGas.find(bot->GetMap()->GetInstanceId()); + bool const isVictim = + vgIt != IcecrownHelpers::rotfaceVileGas.end() && + vgIt->second.victimGuid == bot->GetGUID() && + getMSTimeDiff(vgIt->second.castTime, now) < 8000; + if (isVictim) + return true; + + if (botAI->HasAura("Vile Gas", bot)) + return true; + + auto const& waitMap = IcecrownHelpers::rotfaceVileGasWaitUntil; + auto it = waitMap.find(bot->GetGUID()); + if (it != waitMap.end() && now < it->second) + return true; + + return false; } //PP @@ -280,25 +358,6 @@ bool IccPutricideGrowingOozePuddleTrigger::IsActive() if (!boss) return false; - Difficulty diff = bot->GetRaidDifficulty(); - - if (sPlayerbotAIConfig.EnableICCBuffs && diff && (diff == RAID_DIFFICULTY_10MAN_HEROIC || diff == RAID_DIFFICULTY_25MAN_HEROIC)) - { - //-------CHEAT------- - if (!bot->HasAura(SPELL_EXPERIENCED)) - bot->AddAura(SPELL_EXPERIENCED, bot); - - if (!bot->HasAura(SPELL_AGEIS_OF_DALARAN)) - bot->AddAura(SPELL_AGEIS_OF_DALARAN, bot); - - if (!bot->HasAura(SPELL_NO_THREAT) && botAI->HasAggro(boss) && !botAI->IsTank(bot)) - bot->AddAura(SPELL_NO_THREAT, bot); - - if (botAI->IsMainTank(bot) && !bot->HasAura(SPELL_SPITEFULL_FURY) && boss->GetVictim() != bot) - bot->AddAura(SPELL_SPITEFULL_FURY, bot); - //-------CHEAT------- - } - const GuidVector& npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); for (auto const& npc : npcs) { @@ -344,50 +403,98 @@ bool IccPutricideGasCloudTrigger::IsActive() return true; } -bool IccPutricideMainTankMutatedPlagueTrigger::IsActive() +bool IccPutricideMutatedPlagueTrigger::IsActive() { - bool bossPresent = false; - if (AI_VALUE2(Unit*, "find target", "professor putricide")) - bossPresent = true; + return AI_VALUE2(Unit*, "find target", "professor putricide") != nullptr; +} - if (!bossPresent) +bool IccPutricideMalleableGooTrigger::IsActive() +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "professor putricide"); + if (!boss) return false; - if (!botAI->IsAssistTankOfIndex(bot, 0)) - { - return false; - } - Unit* mt = AI_VALUE(Unit*, "main tank"); - if (!mt) - { - return false; - } - Aura* aura = botAI->GetAura("Mutated Plague", mt, false, true); - if (!aura || aura->GetStackAmount() < 4) + Difficulty const diff = bot->GetRaidDifficulty(); + + // Heroic cheat buffs — apply to all group members (bots + real players) + if (boss && sPlayerbotAIConfig.EnableICCBuffs && + (diff == RAID_DIFFICULTY_10MAN_HEROIC || diff == RAID_DIFFICULTY_25MAN_HEROIC)) { - return false; + if (Group* buffGroup = bot->GetGroup()) + { + for (GroupReference* itr = buffGroup->GetFirstMember(); itr; itr = itr->next()) + { + Player* member = itr->GetSource(); + if (!member || !member->IsAlive() || !member->IsInWorld()) + continue; + + if (!member->HasAura(SPELL_EXPERIENCED)) + member->AddAura(SPELL_EXPERIENCED, member); + + if (!member->HasAura(SPELL_AGEIS_OF_DALARAN)) + member->AddAura(SPELL_AGEIS_OF_DALARAN, member); + + if (!PlayerbotAI::IsTank(member) && !member->HasAura(SPELL_NO_THREAT)) + member->AddAura(SPELL_NO_THREAT, member); + + if (PlayerbotAI::IsTank(member) && !member->HasAura(SPELL_SPITEFULL_FURY) && + boss->GetVictim() != member) + member->AddAura(SPELL_SPITEFULL_FURY, member); + } + } } + return true; } -bool IccPutricideMalleableGooTrigger::IsActive() +bool IccPutricideAbominationTrigger::IsActive() { Unit* boss = AI_VALUE2(Unit*, "find target", "professor putricide"); if (!boss) return false; - if (botAI->IsTank(bot)) - return true; - - Unit* boss1 = AI_VALUE2(Unit*, "find target", "volatile ooze"); - if (boss1) + if (!botAI->IsAssistTank(bot)) return false; - Unit* boss2 = AI_VALUE2(Unit*, "find target", "gas cloud"); - if (boss2) + // Already piloting - keep action firing until vehicle drops. + if (Unit* veh = bot->GetVehicleBase()) + { + uint32 e = veh->GetEntry(); + if (e == NPC_MUTATED_ABOMINATION_10 || e == NPC_MUTATED_ABOMINATION_25) + return true; + } + + // Phase 3: boss takes toy back. No transformation. + if (boss->HealthBelowPct(35)) return false; - return true; + // Someone else already piloting - do not drink. + if (Group* group = bot->GetGroup()) + { + for (GroupReference* itr = group->GetFirstMember(); itr; itr = itr->next()) + { + Player* m = itr->GetSource(); + if (!m || m == bot || !m->IsAlive()) + continue; + if (Unit* vb = m->GetVehicleBase()) + { + uint32 e = vb->GetEntry(); + if (e == NPC_MUTATED_ABOMINATION_10 || e == NPC_MUTATED_ABOMINATION_25) + return false; + } + } + } + + // Require at least one Growing Ooze Puddle nearby. + GuidVector const npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); + for (auto const& g : npcs) + { + if (Unit* u = botAI->GetUnit(g)) + if (u->GetEntry() == NPC_GROWING_OOZE_PUDDLE) + return true; + } + + return false; } //BPC @@ -463,10 +570,10 @@ bool IccBpcKineticBombTrigger::IsActive() if (!botAI->IsRanged(bot) || botAI->IsHeal(bot)) return false; - // Early exit condition - if Shadow Prison has too many stacks + // Allow up to 18 stacks for bomb-assigned bots (multiplier handles assignment) if (Aura* aura = botAI->GetAura("Shadow Prison", bot, false, true)) { - if (aura->GetStackAmount() > 12) + if (aura->GetStackAmount() > 18) return false; } @@ -485,7 +592,7 @@ bool IccBpcKineticBombTrigger::IsActive() if (unit->GetEntry() == entry) { // Check if bomb is within valid Z-axis range - if (unit->GetPositionZ() - bot->GetPositionZ() < 25.0f) + if (unit->GetPositionZ() - bot->GetPositionZ() < 35.0f) { bombFound = true; break; @@ -513,16 +620,23 @@ bool IccBpcBallOfFlameTrigger::IsActive() if (!auraTaldaram) return false; - return true; + return true; } -//BQL +// BQL bool IccBqlGroupPositionTrigger::IsActive() { Unit* boss = AI_VALUE2(Unit*, "find target", "blood-queen lana'thel"); if (!boss) return false; + Unit* valanar = AI_VALUE2(Unit*, "find target", "prince valanar"); + Unit* taldaram = AI_VALUE2(Unit*, "find target", "prince taldaram"); + Unit* keleseth = AI_VALUE2(Unit*, "find target", "prince keleseth"); + + if (valanar || taldaram || keleseth) + return false; + if (bot->HasAura(SPELL_EXPERIENCED)) bot->RemoveAura(SPELL_EXPERIENCED); @@ -535,6 +649,13 @@ bool IccBqlPactOfDarkfallenTrigger::IsActive() if (!boss) return false; + Unit* valanar = AI_VALUE2(Unit*, "find target", "prince valanar"); + Unit* taldaram = AI_VALUE2(Unit*, "find target", "prince taldaram"); + Unit* keleseth = AI_VALUE2(Unit*, "find target", "prince keleseth"); + + if (valanar || taldaram || keleseth) + return false; + Aura* aura = botAI->GetAura("Pact of the Darkfallen", bot); if (!aura) return false; @@ -548,6 +669,13 @@ bool IccBqlVampiricBiteTrigger::IsActive() if (!boss) return false; + Unit* valanar = AI_VALUE2(Unit*, "find target", "prince valanar"); + Unit* taldaram = AI_VALUE2(Unit*, "find target", "prince taldaram"); + Unit* keleseth = AI_VALUE2(Unit*, "find target", "prince keleseth"); + + if (valanar || taldaram || keleseth) + return false; + Aura* aura = botAI->GetAura("Frenzied Bloodthirst", bot); if (!aura) return false; @@ -590,6 +718,26 @@ bool IccValithriaGroupTrigger::IsActive() return true; } +bool IccValithriaZombieKiteTrigger::IsActive() +{ + Unit* boss = bot->FindNearestCreature(NPC_VALITHRIA_DREAMWALKER, 100.0f); + if (!boss) + return false; + + if (botAI->IsTank(bot)) + return false; + + std::list zombies; + bot->GetCreatureListWithEntryInGrid(zombies, NPC_BLISTERING_ZOMBIE, 100.0f); + for (Creature* z : zombies) + { + if (z && z->IsAlive() && z->GetVictim() == bot) + return true; + } + + return false; +} + bool IccValithriaPortalTrigger::IsActive() { Unit* boss = bot->FindNearestCreature(NPC_VALITHRIA_DREAMWALKER, 100.0f); @@ -809,15 +957,10 @@ bool IccValithriaHealTrigger::IsActive() bool IccValithriaDreamCloudTrigger::IsActive() { - // Only active if we're in dream state if (!bot->HasAura(SPELL_DREAM_STATE) || bot->HealthBelowPct(50)) return false; - // Find nearest cloud of either type - Creature* dreamCloud = bot->FindNearestCreature(NPC_DREAM_CLOUD, 100.0f); - Creature* nightmareCloud = bot->FindNearestCreature(NPC_NIGHTMARE_CLOUD, 100.0f); - - return (dreamCloud || nightmareCloud); + return true; } //SINDRAGOSA @@ -832,21 +975,47 @@ bool IccSindragosaGroupPositionTrigger::IsActive() if (sPlayerbotAIConfig.EnableICCBuffs && diff && (diff == RAID_DIFFICULTY_10MAN_HEROIC || diff == RAID_DIFFICULTY_25MAN_HEROIC)) { //-------CHEAT------- - if (!bot->HasAura(SPELL_EXPERIENCED)) - bot->AddAura(SPELL_EXPERIENCED, bot); + // Apply to every alive group member so real players benefit too, + if (Group* group = bot->GetGroup()) + { + for (GroupReference* itr = group->GetFirstMember(); itr; itr = itr->next()) + { + Player* member = itr->GetSource(); + if (!member || !member->IsAlive() || !member->IsInWorld()) + continue; + + if (!member->HasAura(SPELL_EXPERIENCED)) + member->AddAura(SPELL_EXPERIENCED, member); - if (!bot->HasAura(SPELL_AGEIS_OF_DALARAN)) - bot->AddAura(SPELL_AGEIS_OF_DALARAN, bot); + if (!member->HasAura(SPELL_AGEIS_OF_DALARAN)) + member->AddAura(SPELL_AGEIS_OF_DALARAN, member); - if (!bot->HasAura(SPELL_NO_THREAT) && botAI->HasAggro(boss) && !botAI->IsTank(bot)) - bot->AddAura(SPELL_NO_THREAT, bot); + if (!botAI->IsTank(member) && !member->HasAura(SPELL_NO_THREAT)) + member->AddAura(SPELL_NO_THREAT, member); - if (botAI->IsMainTank(bot) && !bot->HasAura(SPELL_SPITEFULL_FURY) && boss->GetVictim() != bot) - bot->AddAura(SPELL_SPITEFULL_FURY, bot); + if (botAI->IsMainTank(member) && boss->GetVictim() != member && + !member->HasAura(SPELL_SPITEFULL_FURY)) + member->AddAura(SPELL_SPITEFULL_FURY, member); + } + } //-------CHEAT------- } - if (!boss || bot->HasAura(SPELL_FROST_BEACON) /*|| bot->HasAura(69762)*/ || boss->GetExactDist2d(ICC_SINDRAGOSA_FLYING_POSITION.GetPositionX(), ICC_SINDRAGOSA_FLYING_POSITION.GetPositionY()) < 50.0f) + // Air phase: give all tanks nitro boosts so they can quickly reposition to tombs + if (boss->IsInCombat() && botAI->IsTank(bot) && + boss->GetExactDist2d(ICC_SINDRAGOSA_FLYING_POSITION.GetPositionX(), ICC_SINDRAGOSA_FLYING_POSITION.GetPositionY()) < 50.0f) + { + if (!bot->HasAura(SPELL_NITRO_BOOSTS)) + bot->AddAura(SPELL_NITRO_BOOSTS, bot); + } + + // Last phase: tanks must keep tanking, never run to a tomb spot. Strip + // Frost Beacon so the tomb-positioning logic doesn't apply to them. + if (botAI->IsTank(bot) && bot->HasAura(SPELL_FROST_BEACON) && boss->HealthBelowPct(35) && + boss->GetExactDist2d(ICC_SINDRAGOSA_FLYING_POSITION.GetPositionX(), ICC_SINDRAGOSA_FLYING_POSITION.GetPositionY()) >= 30.0f) + bot->RemoveAura(SPELL_FROST_BEACON); + + if (!boss || bot->HasAura(SPELL_FROST_BEACON) || boss->GetExactDist2d(ICC_SINDRAGOSA_FLYING_POSITION.GetPositionX(), ICC_SINDRAGOSA_FLYING_POSITION.GetPositionY()) < 50.0f) return false; return true; @@ -854,7 +1023,7 @@ bool IccSindragosaGroupPositionTrigger::IsActive() bool IccSindragosaFrostBeaconTrigger::IsActive() { - Unit* boss = AI_VALUE2(Unit*, "find target", "sindragosa"); + Unit* boss = bot->FindNearestCreature(NPC_SINDRAGOSA, 200.0f); if (!boss) return false; @@ -879,6 +1048,32 @@ bool IccSindragosaFrostBeaconTrigger::IsActive() return false; } +bool IccSindragosaHotTrigger::IsActive() +{ + if (!botAI->IsHeal(bot)) + return false; + + if (bot->HasAura(SPELL_FROST_BEACON)) + return false; + + Unit* boss = bot->FindNearestCreature(NPC_SINDRAGOSA, 200.0f); + if (!boss) + return false; + + Group* group = bot->GetGroup(); + if (!group) + return false; + + for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next()) + { + Player* member = itr->GetSource(); + if (member && member->IsAlive() && member->HasAura(SPELL_FROST_BEACON)) + return true; + } + + return false; +} + bool IccSindragosaBlisteringColdTrigger::IsActive() { Unit* boss = AI_VALUE2(Unit*, "find target", "sindragosa"); @@ -974,6 +1169,15 @@ bool IccSindragosaMysticBuffetTrigger::IsActive() if (bot->HasAura(SPELL_FROST_BEACON)) return false; + // Blistering Cold takes priority over tomb-hiding in the last phase: + // skip hiding so the bot can run to the safe spot instead. + if (boss->HasUnitState(UNIT_STATE_CASTING) && + (boss->FindCurrentSpellBySpellId(SPELL_BLISTERING_COLD1) || + boss->FindCurrentSpellBySpellId(SPELL_BLISTERING_COLD2) || + boss->FindCurrentSpellBySpellId(SPELL_BLISTERING_COLD3) || + boss->FindCurrentSpellBySpellId(SPELL_BLISTERING_COLD4))) + return false; + if (aura->GetStackAmount() >= 1) return true; @@ -1023,6 +1227,7 @@ bool IccSindragosaMainTankMysticBuffetTrigger::IsActive() return true; } +// TODO never triggers since mystic buffet is bypassed in action bool IccSindragosaTankSwapPositionTrigger::IsActive() { Unit* boss = AI_VALUE2(Unit*, "find target", "sindragosa"); @@ -1072,9 +1277,12 @@ bool IccSindragosaFrostBombTrigger::IsActive() if (!boss) return false; - if (!bot->IsAlive() || bot->HasAura(SPELL_ICE_TOMB)) // Skip if dead or in Ice Tomb + if (!bot->IsAlive()) // Skip if dead return false; + // Tombed bots intentionally pass through: the action pins their group to + // the current tomb's zone so when freed they don't migrate to the wrong + // zone. The action returns false for tombed bots without moving them. if (boss->GetExactDist2d(ICC_SINDRAGOSA_FLYING_POSITION.GetPositionX(), ICC_SINDRAGOSA_FLYING_POSITION.GetPositionY()) < 50.0f && !boss->HealthBelowPct(25) && !boss->HealthAbovePct(99)) return true; @@ -1085,6 +1293,10 @@ bool IccSindragosaFrostBombTrigger::IsActive() bool IccLichKingShadowTrapTrigger::IsActive() { + Unit* vdw = bot->FindNearestCreature(NPC_VALITHRIA_DREAMWALKER, 100.0f); + if (vdw) + return false; + Unit* boss = AI_VALUE2(Unit*, "find target", "the lich king"); if (!boss) return false; @@ -1118,71 +1330,76 @@ bool IccLichKingShadowTrapTrigger::IsActive() bool IccLichKingNecroticPlagueTrigger::IsActive() { - bool hasPlague = botAI->HasAura("Necrotic Plague", bot); + Unit* vdw = bot->FindNearestCreature(NPC_VALITHRIA_DREAMWALKER, 100.0f); + if (vdw) + return false; + + if (!AI_VALUE2(Unit*, "find target", "the lich king")) + return false; - return hasPlague; + return botAI->HasAura("Necrotic Plague", bot); } bool IccLichKingWinterTrigger::IsActive() { + Unit* vdw = bot->FindNearestCreature(NPC_VALITHRIA_DREAMWALKER, 100.0f); + if (vdw) + return false; + Unit* boss = AI_VALUE2(Unit*, "find target", "the lich king"); if (!boss) return false; - // Check for either Remorseless Winter - bool hasWinterAura = false; - if (boss && (boss->HasAura(SPELL_REMORSELESS_WINTER1) || boss->HasAura(SPELL_REMORSELESS_WINTER2) || - boss->HasAura(SPELL_REMORSELESS_WINTER3) || boss->HasAura(SPELL_REMORSELESS_WINTER4))) - hasWinterAura = true; - - bool hasWinter2Aura = false; - if (boss && (boss->HasAura(SPELL_REMORSELESS_WINTER5) || boss->HasAura(SPELL_REMORSELESS_WINTER6) || - boss->HasAura(SPELL_REMORSELESS_WINTER7) || boss->HasAura(SPELL_REMORSELESS_WINTER8))) - hasWinter2Aura = true; - - bool isCasting = false; - if (boss && boss->HasUnitState(UNIT_STATE_CASTING)) - isCasting = true; - - bool isWinter = false; - if (boss && boss->FindCurrentSpellBySpellId(SPELL_REMORSELESS_WINTER1) || - boss->FindCurrentSpellBySpellId(SPELL_REMORSELESS_WINTER2) || - boss->FindCurrentSpellBySpellId(SPELL_REMORSELESS_WINTER5) || - boss->FindCurrentSpellBySpellId(SPELL_REMORSELESS_WINTER6) || - boss->FindCurrentSpellBySpellId(SPELL_REMORSELESS_WINTER3) || - boss->FindCurrentSpellBySpellId(SPELL_REMORSELESS_WINTER4) || - boss->FindCurrentSpellBySpellId(SPELL_REMORSELESS_WINTER7) || - boss->FindCurrentSpellBySpellId(SPELL_REMORSELESS_WINTER8)) - isWinter = true; - - if (hasWinterAura || hasWinter2Aura) - return true; + auto const hasWinterAura = [&]() -> bool + { + return boss->HasAura(SPELL_REMORSELESS_WINTER1) || boss->HasAura(SPELL_REMORSELESS_WINTER2) || + boss->HasAura(SPELL_REMORSELESS_WINTER3) || boss->HasAura(SPELL_REMORSELESS_WINTER4) || + boss->HasAura(SPELL_REMORSELESS_WINTER5) || boss->HasAura(SPELL_REMORSELESS_WINTER6) || + boss->HasAura(SPELL_REMORSELESS_WINTER7) || boss->HasAura(SPELL_REMORSELESS_WINTER8); + }; - if (isCasting && isWinter) - return true; + auto const isCastingWinter = [&]() -> bool + { + if (!boss->HasUnitState(UNIT_STATE_CASTING)) + return false; - return false; + return boss->FindCurrentSpellBySpellId(SPELL_REMORSELESS_WINTER1) || + boss->FindCurrentSpellBySpellId(SPELL_REMORSELESS_WINTER2) || + boss->FindCurrentSpellBySpellId(SPELL_REMORSELESS_WINTER3) || + boss->FindCurrentSpellBySpellId(SPELL_REMORSELESS_WINTER4) || + boss->FindCurrentSpellBySpellId(SPELL_REMORSELESS_WINTER5) || + boss->FindCurrentSpellBySpellId(SPELL_REMORSELESS_WINTER6) || + boss->FindCurrentSpellBySpellId(SPELL_REMORSELESS_WINTER7) || + boss->FindCurrentSpellBySpellId(SPELL_REMORSELESS_WINTER8); + }; + + return hasWinterAura() || isCastingWinter(); } bool IccLichKingAddsTrigger::IsActive() { - Unit* boss = AI_VALUE2(Unit*, "find target", "the lich king"); - - bool hasPlague = botAI->HasAura("Necrotic Plague", bot); - if (hasPlague) + Unit* vdw = bot->FindNearestCreature(NPC_VALITHRIA_DREAMWALKER, 100.0f); + if (vdw) return false; - Unit* terenasMenethilHC = bot->FindNearestCreature(NPC_TERENAS_MENETHIL_HC, 55.0f); - Unit* terenasMenethil = bot->FindNearestCreature(NPC_TERENAS_MENETHIL, 55.0f); + if (bot->HasAura(SPELL_HARVEST_SOUL_VALKYR)) + return false; - if (terenasMenethilHC) - return true; + if (botAI->HasAura("Necrotic Plague", bot)) + return false; - if (terenasMenethil) + if (bot->FindNearestCreature(NPC_TERENAS_MENETHIL_HC, 55.0f) || + bot->FindNearestCreature(NPC_TERENAS_MENETHIL, 55.0f)) return true; - if (!boss) + Unit* lk = AI_VALUE2(Unit*, "find target", "the lich king"); + if (!lk) return false; return true; } + +bool IccLichKingSpiritBombTrigger::IsActive() +{ + return IccLichKingSpiritBombAction::IsBombThreatActive(botAI, bot); +} diff --git a/src/Ai/Raid/Icecrown/Trigger/RaidIccTriggers.h b/src/Ai/Raid/ICC/ICCTriggers.h similarity index 74% rename from src/Ai/Raid/Icecrown/Trigger/RaidIccTriggers.h rename to src/Ai/Raid/ICC/ICCTriggers.h index cd332e00467..96b8af5a487 100644 --- a/src/Ai/Raid/Icecrown/Trigger/RaidIccTriggers.h +++ b/src/Ai/Raid/ICC/ICCTriggers.h @@ -1,5 +1,5 @@ -#ifndef _PLAYERBOT_RAIDICCTRIGGERS_H -#define _PLAYERBOT_RAIDICCTRIGGERS_H +#ifndef _PLAYERBOT_ICCT_H +#define _PLAYERBOT_ICCT_H #include "PlayerbotAI.h" #include "Playerbots.h" @@ -9,11 +9,23 @@ enum CreatureIdsICC { // Lord Marrowgar - NPC_SPIKE1 = 36619, - NPC_SPIKE2 = 38711, - NPC_SPIKE3 = 38712, + NPC_SPIKE1 = 36619, // 10N base + NPC_SPIKE1_10H = 38233, // 10H + NPC_SPIKE1_25N = 38459, // 25N + NPC_SPIKE1_25H = 38460, // 25H + NPC_SPIKE2 = 38711, // 25N base + NPC_SPIKE2_10H = 38970, + NPC_SPIKE2_25N = 38971, + NPC_SPIKE2_25H = 38972, + NPC_SPIKE3 = 38712, // 25N base + NPC_SPIKE3_10H = 38973, + NPC_SPIKE3_25N = 38974, + NPC_SPIKE3_25H = 38975, + NPC_COLDFLAME = 36672, // Lady Deathwhisper + NPC_DARNAVAN_10 = 38472, + NPC_DARNAVAN_25 = 38485, NPC_SHADE = 38222, // Gunship Battle @@ -29,6 +41,8 @@ enum CreatureIdsICC NPC_CANNONH = 36839, NPC_MURADIN_BRONZEBEARD = 36948, NPC_HIGH_OVERLORD_SAURFANG = 36939, + NPC_ZAFOD_BOOMBOX = 37184, + ITEM_GOBLIN_ROCKET_PACK = 49278, // Deathbringer Saurfang NPC_BLOOD_BEAST1 = 38508, @@ -38,9 +52,11 @@ enum CreatureIdsICC // Rotface NPC_PUDDLE = 37013, + NPC_SMALL_OOZE = 36897, NPC_BIG_OOZE = 36899, // Putricide + NPC_PROFESSOR_PUTRICIDE = 36678, NPC_MALLEABLE_OOZE_STALKER = 38556, NPC_GROWING_OOZE_PUDDLE = 37690, NPC_CHOKING_GAS_BOMB = 38159, @@ -56,6 +72,7 @@ enum CreatureIdsICC NPC_KINETIC_BOMB4 = 38777, NPC_BALL_OF_FLAME = 38332, NPC_BALL_OF_INFERNO_FLAME = 38451, + NPC_SHOCK_VORTEX = 38422, // Blood Queen Lana'thel NPC_SWARMING_SHADOWS = 38163, @@ -133,26 +150,47 @@ enum SpellIdsICC SPELL_NO_THREAT = 70115, //reduce threat SPELL_SPITEFULL_FURY = 36886, //500% more threat SPELL_NITRO_BOOSTS = 54861, //Speed + SPELL_FROST_TRAP1 = 13809, //Hunter slow trap SPELL_PAIN_SUPPRESION = 69910, //40% dmg reduction SPELL_AGEIS_OF_DALARAN = 71638, //268 all ress SPELL_CYCLONE = 33786, SPELL_HAMMER_OF_JUSTICE = 10308, //stun + // Taunt spells (used to reset cooldowns for assist tank) + SPELL_TAUNT_WARRIOR = 355, + SPELL_TAUNT_PALADIN = 62124, // Hand of Reckoning + SPELL_TAUNT_DK = 56222, // Dark Command + SPELL_TAUNT_DRUID = 6795, // Growl + SPELL_VIPER_STING = 3034, + + // Lord Marrowgar + SPELL_LM_IMPALED = 69065, // Lady Deathwhisper SPELL_DARK_RECKONING = 69483, + SPELL_TOUCH_OF_INSIGNIFICANCE = 71204, // Gunship Battle SPELL_DEATH_PLAGUE = 72865, + SPELL_FROZEN_CANNON = 69704, SPELL_BELOW_ZERO = 69705, + SPELL_ROCKET_PACK_USE = 68645, + SPELL_ROCKET_PACK_USEABLE = 70348, + SPELL_BATTLE_FURY1 = 69637, + SPELL_BATTLE_FURY2 = 69638, + SPELL_BATTLE_FURY3 = 72306, + SPELL_BATTLE_FURY4 = 72307, + SPELL_BATTLE_FURY5 = 72308, // Festergut SPELL_GAS_SPORE = 69279, - // Rotface SPELL_SLIME_SPRAY = 69508, SPELL_OOZE_FLOOD = 71215, SPELL_UNSTABLE_OOZE_EXPLOSION = 69839, SPELL_OOZE_FLOOD_VISUAL = 69785, + // Cast by Professor Putricide from balcony during Rotface heroic. + // Single ID (no difficulty variants in spelldifficulty_dbc). + SPELL_VILE_GAS_H = 69240, // Putricide SPELL_MALLEABLE_GOO = 70852, @@ -166,6 +204,7 @@ enum SpellIdsICC // Blood Queen Lana'thel SPELL_PACT_OF_THE_DARKFALLEN = 71340, + SPELL_BLOODBOLT_WHIRL = 71772, // Sister Svalna SPELL_AETHER_SHIELD = 71463, @@ -173,6 +212,7 @@ enum SpellIdsICC // Valithria Dreamwalker SPELL_DREAM_STATE = 70766, SPELL_EMERALD_VIGOR = 70873, + SPELL_ACID_BURST = 70744, // Sindragosa SPELL_FROST_BEACON = 70126, @@ -182,6 +222,9 @@ enum SpellIdsICC SPELL_BLISTERING_COLD2 = 71047, SPELL_BLISTERING_COLD3 = 71048, SPELL_BLISTERING_COLD4 = 71049, + SPELL_HAND_OF_FREEDOM = 1044, + ITEM_RED_SMOKE_FLARE = 23769, + ITEM_BLUE_SMOKE_FLARE = 23770, // The Lich King SPELL_HARVEST_SOUL_VALKYR = 68985, @@ -194,12 +237,26 @@ enum SpellIdsICC SPELL_REMORSELESS_WINTER6 = 74270, SPELL_REMORSELESS_WINTER7 = 74271, SPELL_REMORSELESS_WINTER8 = 74272, -}; - -const uint32 DEFILE_AURAS[] = {72756, 74162, 74163, 74164}; -const uint32 DEFILE_CAST_ID = 72762; -const uint32 DEFILE_NPC_ID = 38757; -const size_t DEFILE_AURA_COUNT = 4; + SPELL_VALKYR_CARRY = 30440, + SPELL_HARVEST_SOUL_LK = 68980, + SPELL_HARVEST_SOULS_LK_25 = 73654, + SPELL_HARVEST_SOULS_LK_H1 = 74295, + SPELL_HARVEST_SOULS_LK_H2 = 74296, + SPELL_HARVEST_SOULS_LK_H3 = 74297, +}; + +inline constexpr uint32 DEFILE_AURAS[] = {72756, 74162, 74163, 74164}; +inline constexpr uint32 DEFILE_CAST_ID = 72762; +inline constexpr uint32 DEFILE_NPC_ID = 38757; +inline constexpr size_t DEFILE_AURA_COUNT = 4; + +// Malleable Goo (Putricide / Festergut-heroic). Multiple variants because of +// SpellDifficulty remapping and the balcony stalker variant. +inline constexpr uint32 SPELL_MALLEABLE_GOO_10N = 70852; +inline constexpr uint32 SPELL_MALLEABLE_GOO_25N = 72297; +inline constexpr uint32 SPELL_MALLEABLE_GOO_10H = 74280; +inline constexpr uint32 SPELL_MALLEABLE_GOO_25H = 74281; +inline constexpr uint32 SPELL_MALLEABLE_GOO_BALCONY = 72296; // All fanatics and adherents entry ids Lady Deathwhisper static const std::array addEntriesLady = { @@ -212,8 +269,8 @@ const std::vector spellEntriesFlood = { 69799, 69801, 69802, 69795}; const std::vector availableTargetsGS = { - NPC_KOR_KRON_AXETHROWER, NPC_KOR_KRON_ROCKETEER, NPC_KOR_KRON_BATTLE_MAGE, NPC_IGB_HIGH_OVERLORD_SAURFANG, - NPC_SKYBREAKER_RIFLEMAN, NPC_SKYBREAKER_MORTAR_SOLDIER, NPC_SKYBREAKER_SORCERER, NPC_IGB_MURADIN_BRONZEBEARD}; + NPC_KOR_KRON_ROCKETEER, NPC_KOR_KRON_AXETHROWER, NPC_KOR_KRON_BATTLE_MAGE, NPC_IGB_HIGH_OVERLORD_SAURFANG, + NPC_SKYBREAKER_MORTAR_SOLDIER, NPC_SKYBREAKER_RIFLEMAN, NPC_SKYBREAKER_SORCERER, NPC_IGB_MURADIN_BRONZEBEARD}; static std::vector sporeOrder; @@ -262,17 +319,17 @@ class IccGunshipCannonNearTrigger : public Trigger bool IsActive() override; }; -class IccGunshipTeleportAllyTrigger : public Trigger +class IccGunshipRocketJumpTrigger : public Trigger { public: - IccGunshipTeleportAllyTrigger(PlayerbotAI* botAI) : Trigger(botAI, "icc gunship teleport ally") {} + IccGunshipRocketJumpTrigger(PlayerbotAI* botAI) : Trigger(botAI, "icc gunship rocket jump") {} bool IsActive() override; }; -class IccGunshipTeleportHordeTrigger : public Trigger +class IccGunshipRocketPackSetupTrigger : public Trigger { public: - IccGunshipTeleportHordeTrigger(PlayerbotAI* botAI) : Trigger(botAI, "icc gunship teleport horde") {} + IccGunshipRocketPackSetupTrigger(PlayerbotAI* botAI) : Trigger(botAI, "icc gunship rocket pack setup") {} bool IsActive() override; }; @@ -291,11 +348,10 @@ class IccDbsMainTankRuneOfBloodTrigger : public Trigger bool IsActive() override; }; -//DOGS -class IccStinkyPreciousMainTankMortalWoundTrigger : public Trigger +class IccDogsTrigger : public Trigger { public: - IccStinkyPreciousMainTankMortalWoundTrigger(PlayerbotAI* botAI) : Trigger(botAI, "icc stinky precious main tank mortal wound") {} + IccDogsTrigger(PlayerbotAI* botAI) : Trigger(botAI, "icc dogs") {} bool IsActive() override; }; @@ -321,6 +377,14 @@ class IccFestergutSporeTrigger : public Trigger bool IsActive() override; }; +class IccFestergutAvoidMalleableGooTrigger : public Trigger +{ +public: + IccFestergutAvoidMalleableGooTrigger(PlayerbotAI* botAI) + : Trigger(botAI, "icc festergut avoid malleable goo") {} + bool IsActive() override; +}; + //ROTFACE class IccRotfaceTankPositionTrigger : public Trigger { @@ -339,7 +403,20 @@ class IccRotfaceGroupPositionTrigger : public Trigger class IccRotfaceMoveAwayFromExplosionTrigger : public Trigger { public: - IccRotfaceMoveAwayFromExplosionTrigger(PlayerbotAI* botAI) : Trigger(botAI, "icc rotface move away from explosion") {} + IccRotfaceMoveAwayFromExplosionTrigger(PlayerbotAI* botAI) + : Trigger(botAI, "icc rotface move away from explosion"), _castEndTime(0), _wasCasting(false) {} + bool IsActive() override; + +private: + time_t _castEndTime; + bool _wasCasting; +}; + +class IccRotfaceAvoidVileGasTrigger : public Trigger +{ +public: + IccRotfaceAvoidVileGasTrigger(PlayerbotAI* botAI) + : Trigger(botAI, "icc rotface avoid vile gas") {} bool IsActive() override; }; @@ -365,10 +442,10 @@ class IccPutricideGrowingOozePuddleTrigger : public Trigger bool IsActive() override; }; -class IccPutricideMainTankMutatedPlagueTrigger : public Trigger +class IccPutricideMutatedPlagueTrigger : public Trigger { public: - IccPutricideMainTankMutatedPlagueTrigger(PlayerbotAI* botAI) : Trigger(botAI, "icc putricide main tank mutated plague") {} + IccPutricideMutatedPlagueTrigger(PlayerbotAI* botAI) : Trigger(botAI, "icc putricide mutated plague") {} bool IsActive() override; }; @@ -379,6 +456,13 @@ class IccPutricideMalleableGooTrigger : public Trigger bool IsActive() override; }; +class IccPutricideAbominationTrigger : public Trigger +{ +public: + IccPutricideAbominationTrigger(PlayerbotAI* ai) : Trigger(ai, "icc putricide abomination") {} + bool IsActive() override; +}; + //BPC class IccBpcKelesethTankTrigger : public Trigger { @@ -461,6 +545,13 @@ class IccValithriaGroupTrigger : public Trigger bool IsActive() override; }; +class IccValithriaZombieKiteTrigger : public Trigger +{ +public: + IccValithriaZombieKiteTrigger(PlayerbotAI* botAI) : Trigger(botAI, "icc valithria zombie kite") {} + bool IsActive() override; +}; + class IccValithriaPortalTrigger : public Trigger { public: @@ -497,6 +588,13 @@ class IccSindragosaFrostBeaconTrigger : public Trigger bool IsActive() override; }; +class IccSindragosaHotTrigger : public Trigger +{ +public: + IccSindragosaHotTrigger(PlayerbotAI* botAI) : Trigger(botAI, "icc sindragosa hot") {} + bool IsActive() override; +}; + class IccSindragosaBlisteringColdTrigger : public Trigger { public: @@ -575,4 +673,11 @@ class IccLichKingAddsTrigger : public Trigger bool IsActive() override; }; +class IccLichKingSpiritBombTrigger : public Trigger +{ +public: + IccLichKingSpiritBombTrigger(PlayerbotAI* botAI) : Trigger(botAI, "icc lich king spirit bomb") {} + bool IsActive() override; +}; + #endif diff --git a/src/Ai/Raid/Icecrown/Action/RaidIccActions.cpp b/src/Ai/Raid/Icecrown/Action/RaidIccActions.cpp deleted file mode 100644 index 0ae27ffabbb..00000000000 --- a/src/Ai/Raid/Icecrown/Action/RaidIccActions.cpp +++ /dev/null @@ -1,9258 +0,0 @@ -#include "RaidIccActions.h" -#include "NearestNpcsValue.h" -#include "ObjectAccessor.h" -#include "Playerbots.h" -#include "Vehicle.h" -#include "RtiValue.h" -#include "GenericSpellActions.h" -#include "GenericActions.h" -#include "RaidIccTriggers.h" -#include "Multiplier.h" - -// Lord Marrowgwar -bool IccLmTankPositionAction::Execute(Event /*event*/) -{ - Unit* boss = AI_VALUE2(Unit*, "find target", "lord marrowgar"); - if (!boss) - return false; - - if (!botAI->IsTank(bot)) - return false; - - const bool isBossInBoneStorm = botAI->GetAura("Bone Storm", boss) != nullptr; - - if (isBossInBoneStorm) - return false; - - if (botAI->HasAggro(boss) && botAI->IsMainTank(bot) && boss->GetVictim() == bot) - { - const float maxDistanceThreshold = 3.0f; - const float distance = bot->GetExactDist2d(ICC_LM_TANK_POSITION.GetPositionX(), ICC_LM_TANK_POSITION.GetPositionY()); - - if (distance > maxDistanceThreshold) - return MoveTowardPosition(ICC_LM_TANK_POSITION, maxDistanceThreshold); - } - - if (botAI->IsAssistTank(bot)) - { - const float maxDistanceThreshold = 3.0f; - const float distance = bot->GetExactDist2d(ICC_LM_TANK_POSITION.GetPositionX(), ICC_LM_TANK_POSITION.GetPositionY()); - - if (distance > maxDistanceThreshold) - return MoveTowardPosition(ICC_LM_TANK_POSITION, maxDistanceThreshold); - - if (distance < maxDistanceThreshold) - { - bot->SetFacingToObject(boss); - return true; - } - } - - return false; -} - -bool IccLmTankPositionAction::MoveTowardPosition(const Position& position, float incrementSize) -{ - // Calculate direction vector - const float dirX = position.GetPositionX() - bot->GetPositionX(); - const float dirY = position.GetPositionY() - bot->GetPositionY(); - const float length = std::sqrt(dirX * dirX + dirY * dirY); - - // Normalize direction vector - const float normalizedDirX = dirX / length; - const float normalizedDirY = dirY / length; - - // Calculate new position with increment - const float moveX = bot->GetPositionX() + normalizedDirX * incrementSize; - const float moveY = bot->GetPositionY() + normalizedDirY * incrementSize; - - return MoveTo(bot->GetMapId(), moveX, moveY, bot->GetPositionZ(), false, false, false, false, - MovementPriority::MOVEMENT_COMBAT); -} - -bool IccSpikeAction::Execute(Event /*event*/) -{ - // If we're impaled, we can't do anything - if (botAI->GetAura("Impaled", bot)) - return false; - - // Find the boss - Unit* boss = AI_VALUE2(Unit*, "find target", "lord marrowgar"); - if (!boss) - return false; - - const bool isBossInBoneStorm = botAI->GetAura("Bone Storm", boss) != nullptr; - const bool shouldMoveToSafePosition = boss->isInFront(bot) && !botAI->IsTank(bot) && !isBossInBoneStorm; - - if (shouldMoveToSafePosition) - { - const Position safePosition{-390.6757f, 2230.5283f, 0.0f}; // Z value to be overridden by actual bot Z - const float distance = bot->GetExactDist2d(safePosition.GetPositionX(), safePosition.GetPositionY()); - const float maxDistanceThreshold = 3.0f; - - if (distance > maxDistanceThreshold) - return MoveTowardPosition(safePosition, maxDistanceThreshold); - - return false; - } - - if (!botAI->IsTank(bot)) - return false; - - return HandleSpikeTargeting(boss); -} - -bool IccSpikeAction::HandleSpikeTargeting(Unit* boss) -{ - static const std::array spikeEntries = {NPC_SPIKE1, NPC_SPIKE2, NPC_SPIKE3}; - const GuidVector spikes = AI_VALUE(GuidVector, "possible targets no los"); - - Unit* priorityTarget = nullptr; - bool anySpikesExist = false; - - // First check for alive spikes - for (const auto entry : spikeEntries) - { - for (auto const& guid : spikes) - { - if (Unit* unit = botAI->GetUnit(guid)) - { - if (unit->GetEntry() == entry) - { - anySpikesExist = true; // At least one spike exists - - if (unit->IsAlive()) - { // Only consider alive ones for targeting - priorityTarget = unit; - break; - } - } - } - } - if (priorityTarget) - break; - } - - // Only fallback to boss if NO spikes exist at all (alive or dead) - if (!anySpikesExist && boss->IsAlive()) - priorityTarget = boss; - - // Update raid target icon if needed - if (priorityTarget) - UpdateRaidTargetIcon(priorityTarget); - - return false; -} - -bool IccSpikeAction::MoveTowardPosition(const Position& position, float incrementSize) -{ - // Calculate direction vector - const float dirX = position.GetPositionX() - bot->GetPositionX(); - const float dirY = position.GetPositionY() - bot->GetPositionY(); - const float length = std::sqrt(dirX * dirX + dirY * dirY); - - // Normalize direction vector - const float normalizedDirX = dirX / length; - const float normalizedDirY = dirY / length; - - // Calculate new position with increment - const float moveX = bot->GetPositionX() + normalizedDirX * incrementSize; - const float moveY = bot->GetPositionY() + normalizedDirY * incrementSize; - - return MoveTo(bot->GetMapId(), moveX, moveY, bot->GetPositionZ(), false, false, false, false, - MovementPriority::MOVEMENT_COMBAT); -} - -void IccSpikeAction::UpdateRaidTargetIcon(Unit* target) -{ - static constexpr uint8_t SKULL_ICON_INDEX = 7; - - if (Group* group = bot->GetGroup()) - { - const ObjectGuid currentSkull = group->GetTargetIcon(SKULL_ICON_INDEX); - Unit* currentSkullUnit = botAI->GetUnit(currentSkull); - - const bool needsUpdate = !currentSkullUnit || !currentSkullUnit->IsAlive() || currentSkullUnit != target; - - if (needsUpdate) - group->SetTargetIcon(SKULL_ICON_INDEX, bot->GetGUID(), target->GetGUID()); - } -} - -// Lady Deathwhisper -bool IccDarkReckoningAction::Execute(Event /*event*/) -{ - constexpr float SAFE_DISTANCE_THRESHOLD = 2.0f; - - // Check if the bot needs to move to the safe position - if (bot->HasAura(SPELL_DARK_RECKONING) && - bot->GetExactDist2d(ICC_DARK_RECKONING_SAFE_POSITION) > SAFE_DISTANCE_THRESHOLD) - { - // Move to the safe position with the same parameters as before - return MoveTo(bot->GetMapId(), ICC_DARK_RECKONING_SAFE_POSITION.GetPositionX(), - ICC_DARK_RECKONING_SAFE_POSITION.GetPositionY(), - ICC_DARK_RECKONING_SAFE_POSITION.GetPositionZ(), - false, false, false, true, MovementPriority::MOVEMENT_NORMAL); - } - - return false; -} - -bool IccRangedPositionLadyDeathwhisperAction::Execute(Event /*event*/) -{ - Unit* boss = AI_VALUE2(Unit*, "find target", "lady deathwhisper"); - if (!boss) - return false; - - const float currentDistance = bot->GetDistance2d(boss); - const float minDistance = 7.0f; - const float maxDistance = 30.0f; - - if (currentDistance < minDistance || currentDistance > maxDistance) - return false; - - if (!botAI->IsRanged(bot) && !botAI->IsHeal(bot)) - return false; - - return MaintainRangedSpacing(); -} - -bool IccRangedPositionLadyDeathwhisperAction::MaintainRangedSpacing() -{ - const float safeSpacingRadius = 3.0f; - const float moveIncrement = 2.0f; - const float maxMoveDistance = 5.0f; // Limit maximum movement distance - const bool isRanged = botAI->IsRanged(bot) || botAI->IsHeal(bot); - - if (!isRanged) - return false; - - // Ranged: spread from other members - const GuidVector members = AI_VALUE(GuidVector, "group members"); - - // Calculate a combined vector representing all nearby members' positions - float totalX = 0.0f; - float totalY = 0.0f; - int nearbyCount = 0; - - for (auto const& memberGuid : members) - { - Unit* member = botAI->GetUnit(memberGuid); - if (!member || !member->IsAlive() || member == bot) - { - continue; - } - - const float distance = bot->GetExactDist2d(member); - if (distance < safeSpacingRadius) - { - // Calculate vector from member to bot - float dx = bot->GetPositionX() - member->GetPositionX(); - float dy = bot->GetPositionY() - member->GetPositionY(); - - // Weight by inverse distance (closer members have more influence) - float weight = (safeSpacingRadius - distance) / safeSpacingRadius; - totalX += dx * weight; - totalY += dy * weight; - nearbyCount++; - } - } - - // If we have nearby members, move away in the combined direction - if (nearbyCount > 0) - { - // Normalize the combined vector - float magnitude = std::sqrt(totalX * totalX + totalY * totalY); - if (magnitude > 0.001f) // Avoid division by zero - { - totalX /= magnitude; - totalY /= magnitude; - - // Calculate move distance based on nearest member - float moveDistance = std::min(moveIncrement, maxMoveDistance); - - // Create target position in the combined direction - float targetX = bot->GetPositionX() + totalX * moveDistance; - float targetY = bot->GetPositionY() + totalY * moveDistance; - float targetZ = bot->GetPositionZ(); // Maintain current Z - - // Check if the target position is valid and move there - if (bot->IsWithinLOS(targetX, targetY, targetZ)) - { - Position targetPos(targetX, targetY, targetZ); - MoveTo(bot->GetMapId(), targetPos.GetPositionX(), targetPos.GetPositionY(), targetPos.GetPositionZ(), - false, false, false, true, MovementPriority::MOVEMENT_NORMAL); - } - else - { - // If los check fails, try shorter distance - targetX = bot->GetPositionX() + totalX * (moveDistance * 0.5f); - targetY = bot->GetPositionY() + totalY * (moveDistance * 0.5f); - Position targetPos(targetX, targetY, targetZ); - MoveTo(bot->GetMapId(), targetPos.GetPositionX(), targetPos.GetPositionY(), targetPos.GetPositionZ(), - false, false, false, true, MovementPriority::MOVEMENT_NORMAL); - } - } - } - - return false; // Everyone is properly spaced -} - -bool IccAddsLadyDeathwhisperAction::Execute(Event /*event*/) -{ - Unit* boss = AI_VALUE2(Unit*, "find target", "lady deathwhisper"); - if (!boss) - return false; - - if (botAI->HasAura("Dominate Mind", bot, false, false) && !bot->HasAura(SPELL_CYCLONE)) - bot->AddAura(SPELL_CYCLONE, bot); - else if (bot->HasAura(SPELL_CYCLONE) && !botAI->HasAura("Dominate Mind", bot, false, false)) - bot->RemoveAura(SPELL_CYCLONE); - - const uint32 shadeEntryId = NPC_SHADE; - - if (botAI->IsTank(bot) && boss && boss->HealthBelowPct(95) && boss->GetVictim() == bot) - { - // Check if the bot is not the victim of a shade - if (IsTargetedByShade(shadeEntryId)) - return false; - - const float maxDistanceToTankPosition = 20.0f; - const float moveIncrement = 3.0f; - - const float distance = bot->GetExactDist2d(ICC_LDW_TANK_POSTION.GetPositionX(), ICC_LDW_TANK_POSTION.GetPositionY()); - - if (distance > maxDistanceToTankPosition) - { - return MoveTowardPosition(ICC_LDW_TANK_POSTION, moveIncrement); - } - } - - if (!botAI->IsTank(bot)) - return false; - - return HandleAddTargeting(boss); -} - -bool IccAddsLadyDeathwhisperAction::IsTargetedByShade(uint32 shadeEntry) -{ - const GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); - for (auto const& npcGuid : npcs) - { - Unit* unit = botAI->GetUnit(npcGuid); - if (unit && unit->GetEntry() == shadeEntry && unit->GetVictim() == bot) - return true; - } - return false; -} - -bool IccAddsLadyDeathwhisperAction::MoveTowardPosition(const Position& position, float incrementSize) -{ - // Calculate direction vector - const float dirX = position.GetPositionX() - bot->GetPositionX(); - const float dirY = position.GetPositionY() - bot->GetPositionY(); - const float length = std::sqrt(dirX * dirX + dirY * dirY); - - // Normalize direction vector - const float normalizedDirX = dirX / length; - const float normalizedDirY = dirY / length; - - // Calculate new position with increment - const float moveX = bot->GetPositionX() + normalizedDirX * incrementSize; - const float moveY = bot->GetPositionY() + normalizedDirY * incrementSize; - - return MoveTo(bot->GetMapId(), moveX, moveY, bot->GetPositionZ(), false, false, false, false, - MovementPriority::MOVEMENT_COMBAT); -} - -bool IccAddsLadyDeathwhisperAction::HandleAddTargeting(Unit* boss) -{ - const GuidVector targets = AI_VALUE(GuidVector, "possible targets no los"); - - Unit* priorityTarget = nullptr; - bool hasValidAdds = false; - - // First check for alive adds - for (auto const& entry : addEntriesLady) - { - for (auto const& guid : targets) - { - Unit* unit = botAI->GetUnit(guid); - if (unit && unit->IsAlive() && unit->GetEntry() == entry) - { - priorityTarget = unit; - hasValidAdds = true; - break; - } - } - if (priorityTarget) - break; - } - - // Only fallback to boss if NO adds exist - if (!hasValidAdds && boss->IsAlive()) - priorityTarget = boss; - - // Update skull icon if needed - if (priorityTarget) - UpdateRaidTargetIcon(priorityTarget); - - return false; -} - -void IccAddsLadyDeathwhisperAction::UpdateRaidTargetIcon(Unit* target) -{ - static constexpr uint8_t SKULL_ICON_INDEX = 7; - - if (Group* group = bot->GetGroup()) - { - const ObjectGuid currentSkull = group->GetTargetIcon(SKULL_ICON_INDEX); - Unit* currentSkullUnit = botAI->GetUnit(currentSkull); - - const bool needsUpdate = !currentSkullUnit || !currentSkullUnit->IsAlive() || currentSkullUnit != target; - - if (needsUpdate) - group->SetTargetIcon(SKULL_ICON_INDEX, bot->GetGUID(), target->GetGUID()); - } -} - -bool IccShadeLadyDeathwhisperAction::Execute(Event /*event*/) -{ - static constexpr uint32 VENGEFUL_SHADE_ID = NPC_SHADE; - static constexpr float SAFE_DISTANCE = 12.0f; - - // Get the nearest hostile NPCs - const GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); - - for (auto const& npcGuid : npcs) - { - Unit* shade = botAI->GetUnit(npcGuid); - - // Skip if not a vengeful shade - if (!shade || shade->GetEntry() != VENGEFUL_SHADE_ID) - continue; - - // Only run away if the shade is targeting us - // Check by GUID comparison to ensure we're accurately identifying the specific shade - // This is especially important in 25HC where multiple shades can spawn - if (!shade->GetVictim() || shade->GetVictim()->GetGUID() != bot->GetGUID()) - continue; - - const float currentDistance = bot->GetDistance2d(shade); - - // Move away from the Vengeful Shade if the bot is too close - if (currentDistance < SAFE_DISTANCE) - { - // Calculate direction away from shade - float dx = bot->GetPositionX() - shade->GetPositionX(); - float dy = bot->GetPositionY() - shade->GetPositionY(); - float dist = std::sqrt(dx * dx + dy * dy); - - if (dist < 0.001f) - continue; - - dx /= dist; - dy /= dist; - - float moveDistance = SAFE_DISTANCE - currentDistance; - float targetX = bot->GetPositionX() + dx * moveDistance; - float targetY = bot->GetPositionY() + dy * moveDistance; - float targetZ = bot->GetPositionZ(); - - if (bot->IsWithinLOS(targetX, targetY, targetZ)) - { - botAI->Reset(); - MoveTo(bot->GetMapId(), targetX, targetY, targetZ, false, false, false, false, MovementPriority::MOVEMENT_COMBAT); - } - } - } - - return false; -} - -bool IccRottingFrostGiantTankPositionAction::Execute(Event /*event*/) -{ - Unit* boss = AI_VALUE2(Unit*, "find target", "rotting frost giant"); - if (!boss) - return false; - - Aura* aura = botAI->GetAura("death plague", bot, false, false); - if (aura) - bot->RemoveAura(aura->GetId()); - -/* TODO: code works for handling plague, but atm script is bugged and one bot can have 2 plagues at the same time or when cured, which should not happen, and it is immpossible to handle plague atm the legit way. - const bool hasCure = botAI->GetAura("recently infected", bot) != nullptr; - - // Tank behavior - unchanged - if (botAI->IsTank(bot) && botAI->HasAggro(boss) && !isInfected) - if (bot->GetExactDist2d(ICC_ROTTING_FROST_GIANT_TANK_POSITION) > 5.0f) - return MoveTo(bot->GetMapId(), ICC_ROTTING_FROST_GIANT_TANK_POSITION.GetPositionX(), - ICC_ROTTING_FROST_GIANT_TANK_POSITION.GetPositionY(), - ICC_ROTTING_FROST_GIANT_TANK_POSITION.GetPositionZ(), false, false, false, true, - MovementPriority::MOVEMENT_NORMAL); - - if (botAI->IsTank(bot)) - return false; - - // Handle infected bot behavior - move near a non-infected, non-cured bot - if (isInfected) - { - const GuidVector members = AI_VALUE(GuidVector, "group members"); - - // Count how many bots are targeting each potential target - std::map targetCounts; - - // First, identify all infected bots and their current targets (approximate) - for (auto const& memberGuid : members) - { - Unit* member = botAI->GetUnit(memberGuid); - if (!member || !member->IsAlive() || member == bot) - continue; - - const bool memberIsInfected = botAI->GetAura("death plague", member) != nullptr; - - if (memberIsInfected) - { - // Find the nearest non-infected bot to this infected bot (as a guess of its target) - float minDist = 5.0f; // Only count if they're close enough to likely be targeting - Unit* likelyTarget = nullptr; - - for (auto const& targetGuid : members) - { - Unit* potentialTarget = botAI->GetUnit(targetGuid); - if (!potentialTarget || !potentialTarget->IsAlive() || potentialTarget == member) - continue; - - const bool targetIsInfected = botAI->GetAura("death plague", potentialTarget) != nullptr; - const bool targetHasCure = botAI->GetAura("recently infected", potentialTarget) != nullptr; - - if (!targetIsInfected && !targetHasCure) - { - float dist = member->GetExactDist2d(potentialTarget); - if (dist < minDist) - { - minDist = dist; - likelyTarget = potentialTarget; - } - } - } - - if (likelyTarget) - { - targetCounts[likelyTarget->GetGUID()]++; - } - } - } - - // Find viable targets and score them based on various factors - std::vector> viableTargets; - - // First try to find ranged, non-infected, non-cured bots - for (auto const& memberGuid : members) - { - Unit* member = botAI->GetUnit(memberGuid); - if (!member || !member->IsAlive() || member == bot) - continue; - - const bool memberHasCure = botAI->GetAura("recently infected", member) != nullptr; - const bool memberIsInfected = botAI->GetAura("death plague", member) != nullptr; - - if (!memberIsInfected && !memberHasCure) - { - // Base score is distance (lower is better) - float score = bot->GetExactDist2d(member); - - // Prefer ranged targets - if (botAI->IsRanged(bot)) - { - score *= 0.7f; // Bonus for ranged targets - } - - // Apply penalty based on how many other infected bots are targeting this one - int targetingCount = targetCounts[member->GetGUID()]; - score *= (1.0f + targetingCount * 0.5f); // Increase score (worse) for heavily targeted bots - - viableTargets.push_back(std::make_pair(member, score)); - } - } - - // Sort targets by score (lowest/best first) - std::sort(viableTargets.begin(), viableTargets.end(), - [](const std::pair& a, const std::pair& b) - { return a.second < b.second; }); - - // Choose the best target - Unit* targetBot = nullptr; - if (!viableTargets.empty()) - { - targetBot = viableTargets[0].first; - } - - // Move to target bot if found - if (targetBot) - { - // If we're already close enough (1 yard), no need to move - if (bot->GetExactDist2d(targetBot) > 1.0f) - { - // Calculate a unique angle based on bot's GUID to ensure different approach angles - // This helps spread infected bots around the target - uint32 guidLow = bot->GetGUID().GetCounter(); - float angleOffset = float(guidLow % 628) / 100.0f; // Random angle between 0 and 2π - - // Calculate position 1 yard away from target at our unique angle - float angle = targetBot->GetOrientation() + angleOffset; - float targetX = targetBot->GetPositionX() + cos(angle) * 1.0f; - float targetY = targetBot->GetPositionY() + sin(angle) * 1.0f; - float targetZ = targetBot->GetPositionZ(); - - return MoveTo(bot->GetMapId(), targetX, targetY, targetZ, false, false, false, true, - MovementPriority::MOVEMENT_COMBAT); - } - return true; // Already in position - } - // No suitable target found, continue with normal behavior - } - - // For ranged bots, only spread from non-infected bots - if (botAI->IsRanged(bot)) - { - const float safeSpacingRadius = 11.0f; - const float moveIncrement = 2.0f; - const float maxMoveDistance = 15.0f; - - const GuidVector members = AI_VALUE(GuidVector, "group members"); - - // Calculate a combined vector representing all nearby NON-INFECTED members' positions - float totalX = 0.0f; - float totalY = 0.0f; - int nearbyCount = 0; - - for (auto const& memberGuid : members) - { - Unit* member = botAI->GetUnit(memberGuid); - if (!member || !member->IsAlive() || member == bot) - continue; - - // Only spread from non-infected bots (can stay near infected or cured bots) - const bool memberIsInfected = botAI->GetAura("death plague", member) != nullptr; - if (memberIsInfected) - continue; - - const float distance = bot->GetExactDist2d(member); - if (distance < safeSpacingRadius) - { - // Calculate vector from member to bot - float dx = bot->GetPositionX() - member->GetPositionX(); - float dy = bot->GetPositionY() - member->GetPositionY(); - - // Weight by inverse distance (closer members have more influence) - float weight = (safeSpacingRadius - distance) / safeSpacingRadius; - totalX += dx * weight; - totalY += dy * weight; - nearbyCount++; - } - } - - // If we have nearby non-infected members, move away in the combined direction - if (nearbyCount > 0) - { - // Normalize the combined vector - float magnitude = std::sqrt(totalX * totalX + totalY * totalY); - if (magnitude > 0.001f) // Avoid division by zero - { - totalX /= magnitude; - totalY /= magnitude; - - // Calculate move distance based on nearest member - float moveDistance = std::min(moveIncrement, maxMoveDistance); - - // Create target position in the combined direction - float targetX = bot->GetPositionX() + totalX * moveDistance; - float targetY = bot->GetPositionY() + totalY * moveDistance; - float targetZ = bot->GetPositionZ(); // Maintain current Z - - // Check if the target position is valid and move there - if (bot->IsWithinLOS(targetX, targetY, targetZ)) - { - return MoveTo(bot->GetMapId(), targetX, targetY, targetZ, false, false, false, true, - MovementPriority::MOVEMENT_NORMAL); - } - else - { - // If los check fails, try shorter distance - targetX = bot->GetPositionX() + totalX * (moveDistance * 0.5f); - targetY = bot->GetPositionY() + totalY * (moveDistance * 0.5f); - return MoveTo(bot->GetMapId(), targetX, targetY, targetZ, false, false, false, true, - MovementPriority::MOVEMENT_NORMAL); - } - } - } - } -*/ - return false; // No movement needed -} - -//Gunship -bool IccCannonFireAction::Execute(Event /*event*/) -{ - Unit* vehicleBase = bot->GetVehicleBase(); - Vehicle* vehicle = bot->GetVehicle(); - - if (!vehicleBase || !vehicle) - return false; - - Unit* target = FindValidCannonTarget(); - if (!target) - return false; - - // Try to cast Incinerating Blast if we have enough energy - const float energyThreshold = 90.0f; - if (vehicleBase->GetPower(POWER_ENERGY) >= energyThreshold) - { - const uint32 blastSpellId = AI_VALUE2(uint32, "vehicle spell id", "incinerating blast"); - if (TryCastCannonSpell(blastSpellId, target, vehicleBase)) - return true; - } - - // Otherwise just use regular Cannon Blast - const uint32 cannonSpellId = AI_VALUE2(uint32, "vehicle spell id", "cannon blast"); - return TryCastCannonSpell(cannonSpellId, target, vehicleBase); -} - -Unit* IccCannonFireAction::FindValidCannonTarget() -{ - const GuidVector attackers = AI_VALUE(GuidVector, "possible targets no los"); - - for (auto const& attackerGuid : attackers) - { - Unit* unit = botAI->GetUnit(attackerGuid); - if (!unit) - continue; - - for (const uint32 entry : availableTargetsGS) - { - if (unit->GetEntry() == entry) - return unit; - } - } - - return nullptr; -} - -bool IccCannonFireAction::TryCastCannonSpell(uint32 spellId, Unit* target, Unit* vehicleBase) -{ - static constexpr uint32 cooldownMs = 1000; - - if (botAI->CanCastVehicleSpell(spellId, target) && botAI->CastVehicleSpell(spellId, target)) - { - vehicleBase->AddSpellCooldown(spellId, 0, cooldownMs); - return true; - } - - return false; -} - -bool IccGunshipEnterCannonAction::Execute(Event /*event*/) -{ - // Do not switch vehicles if already in one - if (bot->GetVehicle()) - return false; - - Unit* bestVehicle = FindBestAvailableCannon(); - if (!bestVehicle) - return false; - - return EnterVehicle(bestVehicle, true); -} - -Unit* IccGunshipEnterCannonAction::FindBestAvailableCannon() -{ - const uint32 validCannonEntries[] = {NPC_CANNONA, NPC_CANNONH}; - Unit* bestVehicle = nullptr; - - const GuidVector npcs = AI_VALUE(GuidVector, "nearest vehicles"); - for (auto const& npcGuid : npcs) - { - Unit* vehicleBase = botAI->GetUnit(npcGuid); - if (!IsValidCannon(vehicleBase, validCannonEntries)) - continue; - - // Choose the closest valid cannon - if (!bestVehicle || bot->GetExactDist(vehicleBase) < bot->GetExactDist(bestVehicle)) - bestVehicle = vehicleBase; - } - - return bestVehicle; -} - -bool IccGunshipEnterCannonAction::IsValidCannon(Unit* vehicle, const uint32 validEntries[]) -{ - if (!vehicle) - return false; - - // Must be selectable - if (vehicle->HasUnitFlag(UNIT_FLAG_NOT_SELECTABLE)) - return false; - - // Must be friendly - if (!vehicle->IsFriendlyTo(bot)) - return false; - - // Must have available seats - if (!vehicle->GetVehicleKit() || !vehicle->GetVehicleKit()->GetAvailableSeatCount()) - return false; - - // Must be one of the cannon entries - const uint32 entry = vehicle->GetEntry(); - bool isValidEntry = false; - for (size_t i = 0; i < 2; ++i) - { // 2 is the size of validEntries - if (entry == validEntries[i]) - { - isValidEntry = true; - break; - } - } - - if (!isValidEntry) - return false; - - // Must not have these auras (frozen or disabled) - if (vehicle->HasAura(69704) || vehicle->HasAura(69705)) - return false; - - return true; -} - -bool IccGunshipEnterCannonAction::EnterVehicle(Unit* vehicleBase, bool moveIfFar) -{ - const float dist = bot->GetDistance(vehicleBase); - - if (dist > INTERACTION_DISTANCE && !moveIfFar) - return false; - - if (dist > INTERACTION_DISTANCE) - return MoveTo(vehicleBase); - - // Prepare for entering vehicle - botAI->RemoveShapeshift(); - bot->GetMotionMaster()->Clear(); - bot->StopMoving(); - - // Enter the vehicle - vehicleBase->HandleSpellClick(bot); - - if (!bot->IsOnVehicle(vehicleBase)) - return false; - - // Dismount because bots can enter vehicle while mounted - WorldPacket emptyPacket; - bot->GetSession()->HandleCancelMountAuraOpcode(emptyPacket); - - return true; -} - -bool IccGunshipTeleportAllyAction::Execute(Event /*event*/) -{ - static constexpr float MAX_WAITING_DISTANCE = 45.0f; - static constexpr float MAX_ATTACK_DISTANCE = 15.0f; - static constexpr uint8_t SKULL_ICON_INDEX = 7; - - // Find the Battle-Mage boss - Unit* boss = AI_VALUE2(Unit*, "find target", "kor'kron battle-mage"); - - // Check if we need to remove skull icon when boss is dead - CleanupSkullIcon(SKULL_ICON_INDEX); - - // If no boss found or boss is dead or not casting, check waiting position - if (!boss || !boss->IsAlive() || !boss->HasUnitState(UNIT_STATE_CASTING)) - { - // If we're too far from waiting position, go there - if (bot->GetExactDist2d(ICC_GUNSHIP_TELEPORT_ALLY2) > MAX_WAITING_DISTANCE) - return TeleportTo(ICC_GUNSHIP_TELEPORT_ALLY2); - } - else if (boss->HasUnitState(UNIT_STATE_CASTING) && boss->FindCurrentSpellBySpellId(SPELL_BELOW_ZERO) && - boss->IsAlive()) - { - // Mark the boss with skull icon - UpdateBossSkullIcon(boss, SKULL_ICON_INDEX); - - // Teleport non-tank bots to attack position if not already there - if (!botAI->IsAssistTank(bot) && bot->GetExactDist2d(ICC_GUNSHIP_TELEPORT_ALLY) > MAX_ATTACK_DISTANCE) - return TeleportTo(ICC_GUNSHIP_TELEPORT_ALLY); - } - - return false; -} - -bool IccGunshipTeleportAllyAction::TeleportTo(const Position& position) -{ - return bot->TeleportTo(bot->GetMapId(), position.GetPositionX(), position.GetPositionY(), position.GetPositionZ(), - bot->GetOrientation()); -} - -void IccGunshipTeleportAllyAction::CleanupSkullIcon(uint8_t SKULL_ICON_INDEX) -{ - if (Group* group = bot->GetGroup()) - { - const ObjectGuid currentSkullTarget = group->GetTargetIcon(SKULL_ICON_INDEX); - - if (!currentSkullTarget.IsEmpty()) - { - Unit* skullTarget = ObjectAccessor::GetUnit(*bot, currentSkullTarget); - - if (!skullTarget || !skullTarget->IsAlive()) - { - // Target is dead or doesn't exist, remove icon - group->SetTargetIcon(SKULL_ICON_INDEX, bot->GetGUID(), ObjectGuid::Empty); - } - } - } -} - -void IccGunshipTeleportAllyAction::UpdateBossSkullIcon(Unit* boss, uint8_t SKULL_ICON_INDEX) -{ - if (Group* group = bot->GetGroup()) - { - if (group->GetTargetIcon(SKULL_ICON_INDEX) != boss->GetGUID()) - group->SetTargetIcon(SKULL_ICON_INDEX, bot->GetGUID(), boss->GetGUID()); - } -} - -bool IccGunshipTeleportHordeAction::Execute(Event /*event*/) -{ - static constexpr float MAX_WAITING_DISTANCE = 45.0f; - static constexpr float MAX_ATTACK_DISTANCE = 15.0f; - static constexpr uint8_t SKULL_ICON_INDEX = 7; - - // Find the Sorcerer boss - Unit* boss = AI_VALUE2(Unit*, "find target", "skybreaker sorcerer"); - - // Check if we need to remove skull icon when boss is dead - CleanupSkullIcon(SKULL_ICON_INDEX); - - // If no boss found or boss is dead or not casting, check waiting position - if (!boss || !boss->IsAlive() || !boss->HasUnitState(UNIT_STATE_CASTING)) - { - // If we're too far from waiting position, go there - if (bot->GetExactDist2d(ICC_GUNSHIP_TELEPORT_HORDE2) > MAX_WAITING_DISTANCE) - return TeleportTo(ICC_GUNSHIP_TELEPORT_HORDE2); - } - else if (boss->HasUnitState(UNIT_STATE_CASTING) && boss->FindCurrentSpellBySpellId(SPELL_BELOW_ZERO) && - boss->IsAlive()) - { - // Mark the boss with skull icon - UpdateBossSkullIcon(boss, SKULL_ICON_INDEX); - - // Teleport non-tank bots to attack position if not already there - if (!botAI->IsAssistTank(bot) && bot->GetExactDist2d(ICC_GUNSHIP_TELEPORT_HORDE) > MAX_ATTACK_DISTANCE) - return TeleportTo(ICC_GUNSHIP_TELEPORT_HORDE); - } - - return false; -} - -bool IccGunshipTeleportHordeAction::TeleportTo(const Position& position) -{ - return bot->TeleportTo(bot->GetMapId(), position.GetPositionX(), position.GetPositionY(), position.GetPositionZ(), - bot->GetOrientation()); -} - -void IccGunshipTeleportHordeAction::CleanupSkullIcon(uint8_t SKULL_ICON_INDEX) -{ - if (Group* group = bot->GetGroup()) - { - const ObjectGuid currentSkullTarget = group->GetTargetIcon(SKULL_ICON_INDEX); - - if (!currentSkullTarget.IsEmpty()) - { - Unit* skullTarget = ObjectAccessor::GetUnit(*bot, currentSkullTarget); - - if (!skullTarget || !skullTarget->IsAlive()) - { - // Target is dead or doesn't exist, remove icon - group->SetTargetIcon(SKULL_ICON_INDEX, bot->GetGUID(), ObjectGuid::Empty); - } - } - } -} - -void IccGunshipTeleportHordeAction::UpdateBossSkullIcon(Unit* boss, uint8_t SKULL_ICON_INDEX) -{ - if (Group* group = bot->GetGroup()) - { - if (group->GetTargetIcon(SKULL_ICON_INDEX) != boss->GetGUID()) - group->SetTargetIcon(SKULL_ICON_INDEX, bot->GetGUID(), boss->GetGUID()); - } -} - -//DBS -bool IccDbsTankPositionAction::Execute(Event /*event*/) -{ - Unit* boss = AI_VALUE2(Unit*, "find target", "deathbringer saurfang"); - if (!boss) - return false; - - Unit* beast = AI_VALUE2(Unit*, "find target", "blood beast"); - - // Handle tank positioning - if (botAI->IsTank(bot) && !beast) - { - if (bot->GetExactDist2d(ICC_DBS_TANK_POSITION) > 5.0f) - return MoveTo(bot->GetMapId(), ICC_DBS_TANK_POSITION.GetPositionX(), ICC_DBS_TANK_POSITION.GetPositionY(), - ICC_DBS_TANK_POSITION.GetPositionZ(), false, false, false, true, - MovementPriority::MOVEMENT_NORMAL); - - // Early return if this tank has Rune of Blood - if (botAI->GetAura("Rune of Blood", bot)) - return true; - } - - if (!botAI->IsTank(bot)) - { - if (CrowdControlBloodBeasts()) - return true; - } - - // Handle ranged and healer positioning - if (botAI->IsRanged(bot) || botAI->IsHeal(bot)) - { - // Handle evasion from blood beasts - if (EvadeBloodBeasts()) - return true; - - // Position in formation - return PositionInRangedFormation(); - } - - return false; -} - -bool IccDbsTankPositionAction::CrowdControlBloodBeasts() -{ - const std::array bloodBeastEntries = {NPC_BLOOD_BEAST1, NPC_BLOOD_BEAST2, NPC_BLOOD_BEAST3, - NPC_BLOOD_BEAST4}; - const GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); - - for (auto const& npc : npcs) - { - Unit* unit = botAI->GetUnit(npc); - if (!unit || !unit->IsAlive()) - continue; - - // Check if this is a blood beast - const bool isBloodBeast = - std::find(bloodBeastEntries.begin(), bloodBeastEntries.end(), unit->GetEntry()) != bloodBeastEntries.end(); - - if (!isBloodBeast) - continue; - - // Apply class-specific CC - switch (bot->getClass()) - { - case CLASS_MAGE: - if (!botAI->HasAura("Frost Nova", unit)) - botAI->CastSpell("Frost Nova", unit); - break; - case CLASS_DRUID: - if (!botAI->HasAura("Entangling Roots", unit)) - botAI->CastSpell("Entangling Roots", unit); - break; - case CLASS_PALADIN: - if (!botAI->HasAura("Hammer of Justice", unit)) - botAI->CastSpell("Hammer of Justice", unit); - break; - case CLASS_WARRIOR: - if (!botAI->HasAura("Hamstring", unit)) - botAI->CastSpell("Hamstring", unit); - break; - case CLASS_HUNTER: - if (!botAI->HasAura("Concussive Shot", unit)) - botAI->CastSpell("Concussive Shot", unit); - break; - case CLASS_ROGUE: - if (!botAI->HasAura("Kidney Shot", unit)) - botAI->CastSpell("Kidney Shot", unit); - break; - case CLASS_SHAMAN: - if (!botAI->HasAura("Frost Shock", unit)) - botAI->CastSpell("Frost Shock", unit); - break; - case CLASS_DEATH_KNIGHT: - if (!botAI->HasAura("Chains of Ice", unit)) - botAI->CastSpell("Chains of Ice", unit); - break; - case CLASS_PRIEST: - if (!botAI->HasAura("Psychic Scream", unit)) - botAI->CastSpell("Psychic Scream", unit); - break; - case CLASS_WARLOCK: - if (!botAI->HasAura("Fear", unit)) - botAI->CastSpell("Fear", unit); - break; - default: - break; - } - } - - return false; -} - -bool IccDbsTankPositionAction::EvadeBloodBeasts() -{ - const float evasionDistance = 12.0f; - const std::array bloodBeastEntries = {NPC_BLOOD_BEAST1, NPC_BLOOD_BEAST2, NPC_BLOOD_BEAST3, NPC_BLOOD_BEAST4}; - - // Get the nearest hostile NPCs - const GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); - - for (auto const& npc : npcs) - { - Unit* unit = botAI->GetUnit(npc); - if (!unit) - continue; - - // Check if this is a blood beast - const bool isBloodBeast = std::find(bloodBeastEntries.begin(), bloodBeastEntries.end(), unit->GetEntry()) != bloodBeastEntries.end(); - - // Only evade if it's a blood beast targeting us - if (isBloodBeast && unit->GetVictim() == bot) - { - float currentDistance = bot->GetDistance2d(unit); - - // Move away if too close - if (currentDistance < evasionDistance) - return MoveAway(unit, evasionDistance - currentDistance); - } - } - - return false; -} - -bool IccDbsTankPositionAction::PositionInRangedFormation() -{ - // Get group - Group* group = bot->GetGroup(); - if (!group) - return false; - - // Find this bot's position among ranged/healers in the group - int rangedIndex = -1; - int currentIndex = 0; - - for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next()) - { - Player* member = itr->GetSource(); - if (!member || !member->IsAlive()) - continue; - - if ((botAI->IsRanged(member) || botAI->IsHeal(member)) && !botAI->IsTank(member)) - { - if (member == bot) - { - rangedIndex = currentIndex; - break; - } - currentIndex++; - } - } - - if (rangedIndex == -1) - return false; - - // Fixed positions calculation - constexpr float tankToBossAngle = 3.14f; - constexpr float minBossDistance = 11.0f; - constexpr float spreadDistance = 10.0f; - constexpr int columnsPerRow = 5; - - // Calculate position in a fixed grid (3 rows x 5 columns) - int row = rangedIndex / columnsPerRow; - int col = rangedIndex % columnsPerRow; - - // Calculate base position - float xOffset = (col - 2) * spreadDistance; // Center around tank position - float yOffset = minBossDistance + (row * spreadDistance); // Each row further back - - // Add zigzag offset for odd rows - if (row % 2 == 1) - xOffset += spreadDistance / 2; - - // Rotate position based on tank-to-boss angle - float finalX = - ICC_DBS_TANK_POSITION.GetPositionX() + (cos(tankToBossAngle) * yOffset - sin(tankToBossAngle) * xOffset); - float finalY = - ICC_DBS_TANK_POSITION.GetPositionY() + (sin(tankToBossAngle) * yOffset + cos(tankToBossAngle) * xOffset); - float finalZ = ICC_DBS_TANK_POSITION.GetPositionZ(); - - // Update Z coordinate - bot->UpdateAllowedPositionZ(finalX, finalY, finalZ); - - // Move if not in position - if (bot->GetExactDist2d(finalX, finalY) > 3.0f) - return MoveTo(bot->GetMapId(), finalX, finalY, finalZ, false, false, false, true, - MovementPriority::MOVEMENT_COMBAT); - - return false; -} - -bool IccAddsDbsAction::Execute(Event /*event*/) -{ - Unit* boss = AI_VALUE2(Unit*, "find target", "deathbringer saurfang"); - if (!boss) - return false; - - // This action is only for melee - if (!botAI->IsMelee(bot)) - return false; - - Unit* priorityTarget = FindPriorityTarget(boss); - - // Update raid target icons if needed - UpdateSkullMarker(priorityTarget); - - return false; -} - -Unit* IccAddsDbsAction::FindPriorityTarget(Unit* boss) -{ - const GuidVector targets = AI_VALUE(GuidVector, "possible targets no los"); - - // Blood beast entry IDs - constexpr std::array addEntries = {NPC_BLOOD_BEAST1, NPC_BLOOD_BEAST2, NPC_BLOOD_BEAST3, NPC_BLOOD_BEAST4}; - - // First check for alive adds - for (uint32_t entry : addEntries) - { - for (const ObjectGuid& guid : targets) - { - Unit* unit = botAI->GetUnit(guid); - if (unit && unit->IsAlive() && unit->GetEntry() == entry) - return unit; - } - } - - // Only fallback to boss if it's alive - return boss->IsAlive() ? const_cast(boss) : nullptr; -} - -void IccAddsDbsAction::UpdateSkullMarker(Unit* priorityTarget) -{ - if (!priorityTarget) - return; - - Group* group = bot->GetGroup(); - if (!group) - return; - - constexpr uint8_t skullIconId = 7; - - // Get current skull target - ObjectGuid currentSkull = group->GetTargetIcon(skullIconId); - Unit* currentSkullUnit = botAI->GetUnit(currentSkull); - - // Determine if skull marker needs updating - bool needsUpdate = !currentSkullUnit || !currentSkullUnit->IsAlive() || currentSkullUnit != priorityTarget; - - // Update if needed - if (needsUpdate) - group->SetTargetIcon(skullIconId, bot->GetGUID(), priorityTarget->GetGUID()); -} - -// Festergut -bool IccFestergutGroupPositionAction::Execute(Event /*event*/) -{ - Unit* boss = AI_VALUE2(Unit*, "find target", "festergut"); - if (!boss) - return false; - - bot->SetTarget(boss->GetGUID()); - - // Handle tank positioning - if ((botAI->HasAggro(boss) && botAI->IsMainTank(bot)) || botAI->IsAssistTank(bot)) - { - if (bot->GetExactDist2d(ICC_FESTERGUT_TANK_POSITION) > 5.0f) - return MoveTo(bot->GetMapId(), ICC_FESTERGUT_TANK_POSITION.GetPositionX(), - ICC_FESTERGUT_TANK_POSITION.GetPositionY(), ICC_FESTERGUT_TANK_POSITION.GetPositionZ(), false, - false, false, true, MovementPriority::MOVEMENT_NORMAL); - } - - // Check for spores in the group - if (HasSporesInGroup()) - return false; - - // Position non-tank ranged and healers - return PositionNonTankMembers(); -} - -bool IccFestergutGroupPositionAction::HasSporesInGroup() -{ - const GuidVector members = AI_VALUE(GuidVector, "group members"); - - for (auto const& memberGuid : members) - { - Unit* unit = botAI->GetUnit(memberGuid); - if (unit && unit->HasAura(SPELL_GAS_SPORE)) - return true; - } - - return false; -} - -bool IccFestergutGroupPositionAction::PositionNonTankMembers() -{ - // Only position ranged and healers without spores - if (!(botAI->IsRanged(bot) || botAI->IsHeal(bot))) - return false; - - Group* group = bot->GetGroup(); - if (!group) - return false; - - int positionIndex = CalculatePositionIndex(group); - if (positionIndex == -1) - return false; - - // Position calculation parameters - constexpr float tankToBossAngle = 4.58f; - constexpr float minBossDistance = 15.0f; - constexpr float spreadDistance = 10.0f; - constexpr int columnsPerRow = 6; - - // Calculate grid position - int row = positionIndex / columnsPerRow; - int col = positionIndex % columnsPerRow; - - // Calculate base position - float xOffset = (col - 2) * spreadDistance; // Center around tank position - float yOffset = minBossDistance + (row * spreadDistance); // Each row further back - - // Add zigzag offset for odd rows - if (row % 2 == 1) - xOffset += spreadDistance / 2; - - // Rotate position based on tank-to-boss angle - float finalX = - ICC_FESTERGUT_TANK_POSITION.GetPositionX() + (cos(tankToBossAngle) * yOffset - sin(tankToBossAngle) * xOffset); - float finalY = - ICC_FESTERGUT_TANK_POSITION.GetPositionY() + (sin(tankToBossAngle) * yOffset + cos(tankToBossAngle) * xOffset); - float finalZ = ICC_FESTERGUT_TANK_POSITION.GetPositionZ(); - - // Update Z coordinate - bot->UpdateAllowedPositionZ(finalX, finalY, finalZ); - - // Move if not in position - if (bot->GetExactDist2d(finalX, finalY) > 3.0f) - return MoveTo(bot->GetMapId(), finalX, finalY, finalZ, false, false, false, true, - MovementPriority::MOVEMENT_COMBAT); - - return false; -} - -int IccFestergutGroupPositionAction::CalculatePositionIndex(Group* group) -{ - std::vector healerGuids; - std::vector rangedDpsGuids; - std::vector hunterGuids; - - // Collect all eligible members with their GUIDs - for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next()) - { - Player* member = itr->GetSource(); - if (!member || !member->IsAlive() || botAI->IsTank(member)) - continue; - - ObjectGuid memberGuid = member->GetGUID(); - - if (botAI->IsHeal(member)) - { - healerGuids.push_back(memberGuid); - } - else if (botAI->IsRanged(member)) - { - if (member->getClass() == CLASS_HUNTER) - { - hunterGuids.push_back(memberGuid); - } - else - { - rangedDpsGuids.push_back(memberGuid); - } - } - } - - // Sort GUIDs for consistent ordering - std::sort(healerGuids.begin(), healerGuids.end()); - std::sort(rangedDpsGuids.begin(), rangedDpsGuids.end()); - std::sort(hunterGuids.begin(), hunterGuids.end()); - - ObjectGuid botGuid = bot->GetGUID(); - - // Find bot's position among healers - auto healerIt = std::find(healerGuids.begin(), healerGuids.end(), botGuid); - if (healerIt != healerGuids.end()) - { - int healerIndex = std::distance(healerGuids.begin(), healerIt); - int totalHealers = healerGuids.size(); - - // Healers in first two rows, distributed evenly - int healersPerRow = (totalHealers + 1) / 2; // Round up for first row - - if (healerIndex < healersPerRow) - { - // First row of healers (positions 0-5) - return healerIndex; - } - else - { - // Second row of healers (positions 6-11) - return healerIndex - healersPerRow + 6; - } - } - - // Find bot's position among non-hunter ranged DPS - auto rangedIt = std::find(rangedDpsGuids.begin(), rangedDpsGuids.end(), botGuid); - if (rangedIt != rangedDpsGuids.end()) - { - int rangedIndex = std::distance(rangedDpsGuids.begin(), rangedIt); - int totalHealers = healerGuids.size(); - - // Non-hunter ranged DPS fill remaining spots in healer rows first - int healerSpotsUsed = totalHealers; - int remainingSpotsInHealerRows = 12 - healerSpotsUsed; // 2 rows of 6 spots each - - if (rangedIndex < remainingSpotsInHealerRows) - { - // Fill remaining spots in healer rows - if (healerSpotsUsed < 6) - { - // Fill remaining spots in first row - return healerSpotsUsed + rangedIndex; - } - else - { - // Fill remaining spots in second row - int spotsInSecondRow = healerSpotsUsed - 6; - int remainingInSecondRow = 6 - spotsInSecondRow; - - if (rangedIndex < remainingInSecondRow) - { - return 6 + spotsInSecondRow + rangedIndex; - } - else - { - // Move to third row - return 12 + (rangedIndex - remainingInSecondRow); - } - } - } - else - { - // Start new rows for remaining ranged DPS - return 12 + (rangedIndex - remainingSpotsInHealerRows); - } - } - - // Find bot's position among hunters - auto hunterIt = std::find(hunterGuids.begin(), hunterGuids.end(), botGuid); - if (hunterIt != hunterGuids.end()) - { - int hunterIndex = std::distance(hunterGuids.begin(), hunterIt); - int totalHealers = healerGuids.size(); - int totalRangedDps = rangedDpsGuids.size(); - - // Hunters start after healers and non-hunter ranged DPS - // But ensure they're at least in the second row (position 6+) - int basePosition = totalHealers + totalRangedDps + hunterIndex; - - // If position would be in first row (0-5), move to second row minimum - if (basePosition < 6) - basePosition = 6 + hunterIndex; - - return basePosition; - } - - return -1; -} - -bool IccFestergutSporeAction::Execute(Event /*event*/) -{ - constexpr float POSITION_TOLERANCE = 4.0f; - - // Check if bot has spore - bool hasSpore = bot->HasAura(SPELL_GAS_SPORE); // gas spore - - // Stop attacking if spored - if (hasSpore) - botAI->Reset(); - - // Calculate unique spread position for ranged - Position spreadRangedPos = CalculateSpreadPosition(); - - // Find spored players - SporeInfo sporeInfo = FindSporedPlayers(); - - // Determine target position - Position targetPos = DetermineTargetPosition(hasSpore, sporeInfo, spreadRangedPos); - - // Move to position if not already there - if (bot->GetExactDist2d(targetPos) > POSITION_TOLERANCE) - { - botAI->Reset(); - return MoveTo(bot->GetMapId(), targetPos.GetPositionX(), targetPos.GetPositionY(), targetPos.GetPositionZ(), - true, false, false, true, MovementPriority::MOVEMENT_FORCED); - } - - return hasSpore; -} - -Position IccFestergutSporeAction::CalculateSpreadPosition() -{ - constexpr float SPREAD_RADIUS = 2.0f; - - // Unique angle based on bot's GUID - float angle = (bot->GetGUID().GetCounter() % 16) * (M_PI / 8); - - Position spreadRangedPos = ICC_FESTERGUT_RANGED_SPORE; - spreadRangedPos.Relocate(spreadRangedPos.GetPositionX() + cos(angle) * SPREAD_RADIUS, - spreadRangedPos.GetPositionY() + sin(angle) * SPREAD_RADIUS, - spreadRangedPos.GetPositionZ(), - spreadRangedPos.GetOrientation()); - - return spreadRangedPos; -} - -IccFestergutSporeAction::SporeInfo IccFestergutSporeAction::FindSporedPlayers() -{ - SporeInfo info; - const GuidVector members = AI_VALUE(GuidVector, "group members"); - - for (auto const& memberGuid : members) - { - Unit* unit = botAI->GetUnit(memberGuid); - if (!unit) - continue; - - if (unit->HasAura(SPELL_GAS_SPORE)) - { - info.sporedPlayers.push_back(unit); - - if (!info.hasLowestGuid || unit->GetGUID() < info.lowestGuid) - { - info.lowestGuid = unit->GetGUID(); - info.hasLowestGuid = true; - } - } - } - - return info; -} - -Position IccFestergutSporeAction::DetermineTargetPosition(bool hasSpore, const SporeInfo& sporeInfo, const Position& spreadRangedPos) -{ - // No spores at all - if (sporeInfo.sporedPlayers.empty()) - return botAI->IsMelee(bot) ? ICC_FESTERGUT_MELEE_SPORE : spreadRangedPos; - - // Bot has no spore, go to standard position - if (!hasSpore) - return botAI->IsMelee(bot) ? ICC_FESTERGUT_MELEE_SPORE : spreadRangedPos; - - // Check if main tank has spore - bool mainTankHasSpore = CheckMainTankSpore(); - - // Determine position based on spore logic - if (botAI->IsMainTank(bot)) - return ICC_FESTERGUT_MELEE_SPORE; - - if (bot->GetGUID() == sporeInfo.lowestGuid && !botAI->IsTank(bot) && !mainTankHasSpore) - return ICC_FESTERGUT_MELEE_SPORE; - - return spreadRangedPos; -} - -bool IccFestergutSporeAction::CheckMainTankSpore() -{ - const GuidVector members = AI_VALUE(GuidVector, "group members"); - - for (auto const& memberGuid : members) - { - Unit* unit = botAI->GetUnit(memberGuid); - if (!unit) - continue; - - if (botAI->IsMainTank(unit->ToPlayer()) && unit->HasAura(SPELL_GAS_SPORE)) - return true; - } - - return false; -} - -// Rotface -bool IccRotfaceTankPositionAction::Execute(Event /*event*/) -{ - Unit* boss = AI_VALUE2(Unit*, "find target", "rotface"); - if (!boss) - return false; - - Unit* smallOoze = AI_VALUE2(Unit*, "find target", "little ooze"); - bool victimOfSmallOoze = smallOoze && smallOoze->GetVictim() == bot; - // Mark Rotface with skull - MarkBossWithSkull(boss); - - // Main tank positioning and melee positioning - if ((botAI->IsMainTank(bot) || botAI->IsMelee(bot)) && !botAI->IsAssistTank(bot) && !victimOfSmallOoze) - return PositionMainTankAndMelee(boss); - - // Assist tank positioning for big ooze - if (botAI->IsAssistTank(bot)) - return HandleAssistTankPositioning(boss); - - return false; -} - -void IccRotfaceTankPositionAction::MarkBossWithSkull(Unit* boss) -{ - Group* group = bot->GetGroup(); - if (!group) - return; - - constexpr uint8_t skullIconId = 7; - ObjectGuid skullGuid = group->GetTargetIcon(skullIconId); - if (skullGuid != boss->GetGUID()) - group->SetTargetIcon(skullIconId, bot->GetGUID(), boss->GetGUID()); -} - -bool IccRotfaceTankPositionAction::PositionMainTankAndMelee(Unit* boss) -{ - bool isBossCasting = false; - if (boss && boss->HasUnitState(UNIT_STATE_CASTING) && boss->GetCurrentSpell(SPELL_SLIME_SPRAY)) - isBossCasting = true; - - if (bot->GetExactDist2d(ICC_ROTFACE_CENTER_POSITION) > 7.0f && botAI->HasAggro(boss) && botAI->IsMainTank(bot)) - MoveTo(bot->GetMapId(), ICC_ROTFACE_CENTER_POSITION.GetPositionX(), - ICC_ROTFACE_CENTER_POSITION.GetPositionY(), ICC_ROTFACE_CENTER_POSITION.GetPositionZ(), - false, false, false, true, MovementPriority::MOVEMENT_COMBAT); - - if (boss && isBossCasting && !botAI->IsTank(bot)) - { - float x = boss->GetPositionX(); - float y = boss->GetPositionY(); - float z = boss->GetPositionZ(); - - // If not already close to the boss's position, move there - if (bot->GetExactDist2d(x, y) > 0.5f) - { - MoveTo(bot->GetMapId(), x, y, z, false, false, false, false, MovementPriority::MOVEMENT_FORCED, true, - false); - } - // Otherwise, already at the correct position - return false; - } - - if (!isBossCasting && (bot->GetExactDist2d(ICC_ROTFACE_CENTER_POSITION) < 2.0f || bot->GetExactDist2d(ICC_ROTFACE_CENTER_POSITION) > 7.0f) && !botAI->IsTank(bot)) - { - MoveTo(bot->GetMapId(), ICC_ROTFACE_CENTER_POSITION.GetPositionX(), ICC_ROTFACE_CENTER_POSITION.GetPositionY(), - bot->GetPositionZ(), false, false, false, true, MovementPriority::MOVEMENT_COMBAT); - } - - return false; -} - -bool IccRotfaceTankPositionAction::HandleAssistTankPositioning(Unit* boss) -{ - // Handle big ooze positioning - return HandleBigOozePositioning(boss); -} - -bool IccRotfaceTankPositionAction::HandleBigOozePositioning(Unit*) -{ - // Find all big oozes - GuidVector bigOozes = AI_VALUE(GuidVector, "nearest hostile npcs"); - std::vector activeBigOozes; - - for (auto const& guid : bigOozes) - { - Unit* unit = botAI->GetUnit(guid); - if (unit && unit->IsAlive() && unit->GetEntry() == NPC_BIG_OOZE && unit->IsVisible()) - activeBigOozes.push_back(unit); - } - - if (activeBigOozes.empty()) - return false; - - // Iterate through all big oozes and handle them - for (Unit* bigOoze : activeBigOozes) - { - // Taunt if not targeting us - if (bigOoze->GetVictim() != bot && bigOoze->IsAlive() && bigOoze->IsVisible()) - { - if (botAI->CastSpell("taunt", bigOoze)) - return true; - bot->SetTarget(bigOoze->GetGUID()); - bot->SetFacingToObject(bigOoze); - return Attack(bigOoze); - } - - // Calculate distances - float oozeDistance = bot->GetExactDist2d(bigOoze); - - // Stop moving if ooze is far enough - if (oozeDistance > 12.0f) - { - bot->SetTarget(bigOoze->GetGUID()); - bot->SetFacingToObject(bigOoze); - return true; - } - - // If we have the ooze's aggro, kite it in a larger circular pattern between 20f and 30f from the center - if (bigOoze->GetVictim() == bot) - { - const float minRadius = 24.0f; - const float maxRadius = 34.0f; - const float safeDistanceFromOoze = 13.0f; - const float puddleSafeDistance = 30.0f; - const Position centerPosition = ICC_ROTFACE_CENTER_POSITION; - - float currentDistance = bot->GetExactDist2d(centerPosition); - - // If too close or too far, adjust position - if (currentDistance < minRadius || currentDistance > maxRadius) - { - // Calculate direction vector from bot to center - float dirX = bot->GetPositionX() - centerPosition.GetPositionX(); - float dirY = bot->GetPositionY() - centerPosition.GetPositionY(); - float length = std::sqrt(dirX * dirX + dirY * dirY); - - // Normalize direction vector - dirX /= length; - dirY /= length; - - // Adjust position to stay within the desired radius - float targetX = centerPosition.GetPositionX() + dirX * maxRadius; - float targetY = centerPosition.GetPositionY() + dirY * maxRadius; - - // Ensure the position is at least 10f away from the ooze - if (bigOoze->GetExactDist2d(targetX, targetY) >= safeDistanceFromOoze) - { - return MoveTo(bot->GetMapId(), targetX, targetY, bot->GetPositionZ(), false, false, false, true, - MovementPriority::MOVEMENT_COMBAT); - } - } - - // If within the desired radius, continue kiting in a circular pattern - float currentAngle = atan2(bot->GetPositionY() - centerPosition.GetPositionY(), - bot->GetPositionX() - centerPosition.GetPositionX()); - - // Adjust rotation direction to find a safe position - for (int i = 0; i < 16; ++i) // Try 16 directions around the circle - { - float angleOffset = (i % 2 == 0 ? 1 : -1) * (M_PI / 16.0f) * (i / static_cast(2)); - float newAngle = currentAngle + angleOffset; - - // Calculate new position along the circle - float newX = centerPosition.GetPositionX() + maxRadius * cos(newAngle); - float newY = centerPosition.GetPositionY() + maxRadius * sin(newAngle); - - // Ensure the position is at least 10f away from the ooze - if (bigOoze->GetExactDist2d(newX, newY) >= safeDistanceFromOoze) - { - // Check if the position is at least 30f away from any puddle - GuidVector puddles = AI_VALUE(GuidVector, "nearest hostile npcs"); - bool isSafeFromPuddles = true; - - for (auto const& puddleGuid : puddles) - { - Unit* puddle = botAI->GetUnit(puddleGuid); - if (puddle && botAI->GetAura("Ooze Flood", puddle)) - { - float puddleDistance = puddle->GetDistance2d(newX, newY); - if (puddleDistance < puddleSafeDistance) - { - isSafeFromPuddles = false; - break; - } - } - } - - if (isSafeFromPuddles) - { - return MoveTo(bot->GetMapId(), newX, newY, bot->GetPositionZ(), false, false, false, true, - MovementPriority::MOVEMENT_COMBAT); - } - } - } - } - } - - return false; -} - -bool IccRotfaceGroupPositionAction::Execute(Event /*event*/) -{ - Unit* boss = AI_VALUE2(Unit*, "find target", "rotface"); - if (!boss) - return false; - - const GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); - - bool hasOozeFlood = botAI->HasAura("Ooze Flood", bot); - Unit* smallOoze = AI_VALUE2(Unit*, "find target", "little ooze"); - bool hasMutatedInfection = botAI->HasAura("Mutated Infection", bot); - - // Handle puddle avoidance - if (!botAI->IsTank(bot) && HandlePuddleAvoidance(boss)) - return true; - - // Handle little ooze or mutated infection - if (HandleOozeTargeting()) - return true; - - // Position ranged and healers - if (/*!floodPresent && */ !((smallOoze && smallOoze->GetVictim() == bot) || hasMutatedInfection) && !hasOozeFlood && PositionRangedAndHealers(boss, smallOoze)) - return true; - - //if (!hasOozeFlood && bigOoze && bigOoze->IsAlive() && MoveAwayFromBigOoze(bigOoze)) - //return true; - - return false; -} - -bool IccRotfaceGroupPositionAction::HandlePuddleAvoidance(Unit* boss) -{ - const GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); - - for (auto const& npc : npcs) - { - Unit* unit = botAI->GetUnit(npc); - if (!unit || !botAI->HasAura("Ooze Flood", unit)) - continue; - - float puddleDistance = bot->GetExactDist2d(unit); - float bossDistance = bot->GetExactDist2d(ICC_ROTFACE_CENTER_POSITION); - - if (bossDistance < 15.0f) // Reduced center distance threshold - return false; - - if (puddleDistance < 30.0f) - return MoveAwayFromPuddle(boss, unit, puddleDistance); - } - - return false; -} - -bool IccRotfaceGroupPositionAction::MoveAwayFromPuddle(Unit* boss, Unit* puddle, float) -{ - if (!boss || !puddle) - return false; - - // Calculate angle and move direction - float dx = puddle->GetPositionX() - bot->GetPositionX(); - float dy = puddle->GetPositionY() - bot->GetPositionY(); - float angle = atan2(dy, dx); - - // Try to find a valid position in 8 directions - const float increment = 5.0f; - const float minPuddleDistance = 30.0f; - const float minCenterDistance = 15.0f; // Reduced center distance threshold - const float maxCenterDistance = 25.0f; // New maximum center distance threshold - const int directions = 8; // Number of directions to check - float bestX = bot->GetPositionX(); - float bestY = bot->GetPositionY(); - float bestZ = bot->GetPositionZ(); - float maxSafetyScore = 0.0f; - - for (int i = 0; i < directions; ++i) - { - float testAngle = angle + (i * M_PI / 4); // 8 directions (45-degree increments) - for (float distance = increment; distance <= 35.0f; distance += increment) - { - float moveX = bot->GetPositionX() - distance * cos(testAngle); - float moveY = bot->GetPositionY() - distance * sin(testAngle); - float moveZ = bot->GetPositionZ(); - - // Check distances and line of sight - float newPuddleDistance = puddle->GetDistance2d(moveX, moveY); - float newCenterDistance = sqrt(pow(moveX - ICC_ROTFACE_CENTER_POSITION.GetPositionX(), 2) + - pow(moveY - ICC_ROTFACE_CENTER_POSITION.GetPositionY(), 2)); - - if (newPuddleDistance >= minPuddleDistance && newCenterDistance >= minCenterDistance && - newCenterDistance <= maxCenterDistance && bot->IsWithinLOS(moveX, moveY, moveZ)) - { - // Calculate safety score (favor positions farther from puddle and center) - float safetyScore = newPuddleDistance + newCenterDistance; - if (safetyScore > maxSafetyScore) - { - maxSafetyScore = safetyScore; - bestX = moveX; - bestY = moveY; - bestZ = moveZ; - } - } - } - - // If we are already in a valid position, stop moving - if (maxSafetyScore > 0.0f && bot->GetExactDist2d(bestX, bestY) <= increment) - return false; - } - - // Move to the best position found - if (maxSafetyScore > 0.0f) - { - return MoveTo(bot->GetMapId(), bestX, bestY, bestZ, false, false, false, false, - MovementPriority::MOVEMENT_COMBAT); - } - - return false; -} - -bool IccRotfaceGroupPositionAction::HandleOozeTargeting() -{ - Unit* smallOoze = AI_VALUE2(Unit*, "find target", "little ooze"); - bool hasMutatedInfection = botAI->HasAura("Mutated Infection", bot); - - if ((smallOoze && smallOoze->GetVictim() == bot) || hasMutatedInfection) - return HandleOozeMemberPositioning(); - - return false; -} - -bool IccRotfaceGroupPositionAction::HandleOozeMemberPositioning() -{ - Unit* bigOoze = AI_VALUE2(Unit*, "find target", "big ooze"); - - // First case: No big ooze exists or is not alive, move to designated position - if (!bigOoze || !bigOoze->IsAlive() || !bigOoze->IsVisible()) - { - if (bot->GetExactDist2d(ICC_ROTFACE_BIG_OOZE_POSITION) > 3.0f) - { - return MoveTo(bot->GetMapId(), ICC_ROTFACE_BIG_OOZE_POSITION.GetPositionX(), - ICC_ROTFACE_BIG_OOZE_POSITION.GetPositionY(), ICC_ROTFACE_BIG_OOZE_POSITION.GetPositionZ(), - false, false, false, true, MovementPriority::MOVEMENT_COMBAT); - } - } - // Second case: Big ooze exists and is alive, move to it for merging - else if (bot->GetExactDist2d(bigOoze) > 2.0f && bigOoze->IsAlive() && bigOoze->IsVisible()) - { - // Move to big ooze for merge in increments of 5 - float dx = bigOoze->GetPositionX() - bot->GetPositionX(); - float dy = bigOoze->GetPositionY() - bot->GetPositionY(); - float dz = bigOoze->GetPositionZ() - bot->GetPositionZ(); - float dist = std::sqrt(dx * dx + dy * dy); - if (dist > 5.0f) - { - dx /= dist; - dy /= dist; - float moveX = bot->GetPositionX() + dx * 5.0f; - float moveY = bot->GetPositionY() + dy * 5.0f; - float moveZ = bot->GetPositionZ() + (dz / dist) * 5.0f; - return MoveTo(bot->GetMapId(), moveX, moveY, moveZ, false, false, false, true, - MovementPriority::MOVEMENT_COMBAT); - } - return MoveTo(bot->GetMapId(), bigOoze->GetPositionX(), bigOoze->GetPositionY(), bigOoze->GetPositionZ(), false, - false, false, true, MovementPriority::MOVEMENT_COMBAT); - - } - - return false; // Stay at position -} - -bool IccRotfaceGroupPositionAction::PositionRangedAndHealers(Unit* boss,Unit *smallOoze) -{ - // Only for ranged and healers - if (!(botAI->IsRanged(bot) || botAI->IsHeal(bot))) - return false; - - Difficulty diff = bot->GetRaidDifficulty(); - bool isBossCasting = false; - if (boss && boss->HasUnitState(UNIT_STATE_CASTING) && boss->GetCurrentSpell(SPELL_SLIME_SPRAY)) - isBossCasting = true; - - bool isHeroic = (diff == RAID_DIFFICULTY_10MAN_HEROIC || diff == RAID_DIFFICULTY_25MAN_HEROIC); - - // Move to the exact same position as the boss during slime spray - if (boss && isBossCasting && !isHeroic) - { - float x = boss->GetPositionX(); - float y = boss->GetPositionY(); - float z = boss->GetPositionZ(); - - // If not already close to the boss's position, move there - if (bot->GetExactDist2d(x, y) > 0.5f) - { - MoveTo(bot->GetMapId(), x, y, z, false, false, false, false, MovementPriority::MOVEMENT_FORCED, true, - false); - } - // Otherwise, already at the correct position - return false; - } - - if (!isHeroic && !isBossCasting && boss && !(bot->getClass() == CLASS_HUNTER) && - (bot->GetExactDist2d(boss->GetPositionX(), boss->GetPositionY()) < 2.0f || - bot->GetExactDist2d(boss->GetPositionX(), boss->GetPositionY()) > 5.0f)) - { - MoveTo(bot->GetMapId(), boss->GetPositionX(), boss->GetPositionY(), - bot->GetPositionZ(), false, false, false, true, MovementPriority::MOVEMENT_COMBAT); - } - - if (!isHeroic) - return false; - - return FindAndMoveFromClosestMember(smallOoze); -} - -bool IccRotfaceGroupPositionAction::FindAndMoveFromClosestMember(Unit* smallOoze) -{ - - const GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); - Unit* puddle = nullptr; - - for (auto const& npc : npcs) - { - Unit* unit = botAI->GetUnit(npc); - if (!unit || !botAI->HasAura("Ooze Flood", unit)) - continue; - - puddle = unit; - break; - } - - const float safeSpacingRadius = 10.0f; - const float moveIncrement = 2.0f; - const float maxMoveDistance = 12.0f; // Limit maximum movement distance - const float puddleSafeDistance = 30.0f; // Minimum distance to stay away from puddle - const float minCenterDistance = 20.0f; // Minimum distance from center position - - // Ranged: spread from other members - const GuidVector members = AI_VALUE(GuidVector, "group members"); - - // Calculate a combined vector representing all nearby members' positions - float totalX = 0.0f; - float totalY = 0.0f; - int nearbyCount = 0; - - for (auto const& memberGuid : members) - { - Unit* member = botAI->GetUnit(memberGuid); - if (!member || !member->IsAlive() || member == bot || (smallOoze && smallOoze->GetVictim() == member) || - (member->IsPlayer() && botAI->IsAssistTank(static_cast(member)))) - continue; - - const float distance = bot->GetExactDist2d(member); - if (distance < safeSpacingRadius) - { - // Calculate vector from member to bot - float dx = bot->GetPositionX() - member->GetPositionX(); - float dy = bot->GetPositionY() - member->GetPositionY(); - - // Weight by inverse distance (closer members have more influence) - float weight = (safeSpacingRadius - distance) / safeSpacingRadius; - totalX += dx * weight; - totalY += dy * weight; - nearbyCount++; - } - } - - // If we have nearby members, move away in the combined direction - if (nearbyCount > 0) - { - // Normalize the combined vector - float magnitude = std::sqrt(totalX * totalX + totalY * totalY); - if (magnitude > 0.001f) // Avoid division by zero - { - totalX /= magnitude; - totalY /= magnitude; - - // Calculate move distance based on nearest member - float moveDistance = std::min(moveIncrement, maxMoveDistance); - - // Create target position in the combined direction - float targetX = bot->GetPositionX() + totalX * moveDistance; - float targetY = bot->GetPositionY() + totalY * moveDistance; - float targetZ = bot->GetPositionZ(); // Maintain current Z - - // Ensure the target position is at least 30 yards away from the puddle - if (puddle) - { - float puddleDistance = puddle->GetDistance2d(targetX, targetY); - if (puddleDistance < puddleSafeDistance) - { - // Adjust the target position to move further away from the puddle - float dx = targetX - puddle->GetPositionX(); - float dy = targetY - puddle->GetPositionY(); - float adjustmentFactor = (puddleSafeDistance - puddleDistance) / puddleDistance; - targetX += dx * adjustmentFactor; - targetY += dy * adjustmentFactor; - } - } - - // Ensure the target position is at least 20 yards away from the center position - const float centerX = ICC_ROTFACE_CENTER_POSITION.GetPositionX(); - const float centerY = ICC_ROTFACE_CENTER_POSITION.GetPositionY(); - float centerDistance = std::sqrt(std::pow(targetX - centerX, 2) + std::pow(targetY - centerY, 2)); - if (centerDistance < minCenterDistance) - { - // Adjust the target position to move further away from the center - float dx = targetX - centerX; - float dy = targetY - centerY; - float adjustmentFactor = (minCenterDistance - centerDistance) / centerDistance; - targetX += dx * adjustmentFactor; - targetY += dy * adjustmentFactor; - } - - // Check if the target position is valid and move there - if (bot->IsWithinLOS(targetX, targetY, targetZ)) - { - Position targetPos(targetX, targetY, targetZ); - MoveTo(bot->GetMapId(), targetPos.GetPositionX(), targetPos.GetPositionY(), targetPos.GetPositionZ(), - false, false, false, true, MovementPriority::MOVEMENT_NORMAL); - } - else - { - // If los check fails, try shorter distance - targetX = bot->GetPositionX() + totalX * (moveDistance * 0.5f); - targetY = bot->GetPositionY() + totalY * (moveDistance * 0.5f); - Position targetPos(targetX, targetY, targetZ); - MoveTo(bot->GetMapId(), targetPos.GetPositionX(), targetPos.GetPositionY(), targetPos.GetPositionZ(), - false, false, false, true, MovementPriority::MOVEMENT_NORMAL); - } - } - } - - return false; // Everyone is properly spaced -} - -bool IccRotfaceMoveAwayFromExplosionAction::Execute(Event /*event*/) -{ - // Skip if main tank or ooze flood - if (botAI->IsMainTank(bot)) - return false; - - botAI->Reset(); - - return MoveToRandomSafeLocation(); -} - -bool IccRotfaceMoveAwayFromExplosionAction::MoveToRandomSafeLocation() -{ - // Generate random angle - float angle = frand(0, 2 * M_PI); - - // Calculate initial move position - float moveX = bot->GetPositionX() + 5.0f * cos(angle); - float moveY = bot->GetPositionY() + 5.0f * sin(angle); - float moveZ = bot->GetPositionZ(); - - // Ensure the position is at least 30 yards away from any puddle - const GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); - for (auto const& npc : npcs) - { - Unit* puddle = botAI->GetUnit(npc); - if (!puddle || !botAI->HasAura("Ooze Flood", puddle)) - continue; - - float puddleDistance = puddle->GetDistance2d(moveX, moveY); - if (puddleDistance < 30.0f) - { - // Adjust the position to move further away from the puddle - float dx = moveX - puddle->GetPositionX(); - float dy = moveY - puddle->GetPositionY(); - float adjustmentFactor = (30.0f - puddleDistance) / puddleDistance; - moveX += dx * adjustmentFactor; - moveY += dy * adjustmentFactor; - } - } - - // Ensure the position is at least 30 yards away from the center position - const Position centerPosition = ICC_ROTFACE_CENTER_POSITION; - float centerDistance = std::sqrt(std::pow(moveX - centerPosition.GetPositionX(), 2) + - std::pow(moveY - centerPosition.GetPositionY(), 2)); - if (centerDistance < 40.0f) - { - // Adjust the position to move further away from the center - float dx = moveX - centerPosition.GetPositionX(); - float dy = moveY - centerPosition.GetPositionY(); - float adjustmentFactor = (40.0f - centerDistance) / centerDistance; - moveX += dx * adjustmentFactor; - moveY += dy * adjustmentFactor; - } - - // Check line of sight - if (!bot->IsWithinLOS(moveX, moveY, moveZ)) - return false; - - // Move in increments of 5.0f towards the calculated position - float currentX = bot->GetPositionX(); - float currentY = bot->GetPositionY(); - - float directionX = moveX - currentX; - float directionY = moveY - currentY; - float distance = std::sqrt(directionX * directionX + directionY * directionY); - - if (distance > 5.0f) - { - directionX /= distance; - directionY /= distance; - - moveX = currentX + directionX * 5.0f; - moveY = currentY + directionY * 5.0f; - } - - // Move to the position - return MoveTo(bot->GetMapId(), moveX, moveY, moveZ, false, false, false, false, MovementPriority::MOVEMENT_FORCED); -} - -// Proffesor Putricide -bool IccPutricideGrowingOozePuddleAction::Execute(Event /*event*/) -{ - Unit* closestPuddle = FindClosestThreateningPuddle(); - if (!closestPuddle) - return false; - - Position movePosition = CalculateSafeMovePosition(closestPuddle); - return MoveTo(bot->GetMapId(), movePosition.GetPositionX(), movePosition.GetPositionY(), - movePosition.GetPositionZ(), false, false, false, false, MovementPriority::MOVEMENT_COMBAT); -} - -Unit* IccPutricideGrowingOozePuddleAction::FindClosestThreateningPuddle() -{ - static const float BASE_RADIUS = 2.0f; - static const float STACK_MULTIPLIER = 0.6f; - static const float MIN_DISTANCE = 0.1f; - - GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); - if (npcs.empty()) - return nullptr; - - Unit* closestPuddle = nullptr; - float closestDistance = FLT_MAX; - - for (auto const& npc : npcs) - { - Unit* unit = botAI->GetUnit(npc); - if (!unit || unit->GetEntry() != NPC_GROWING_OOZE_PUDDLE) - continue; - - float currentDistance = std::max(MIN_DISTANCE, bot->GetExactDist(unit)); - float safeDistance = BASE_RADIUS; - - if (Aura* grow = unit->GetAura(SPELL_GROW_AURA)) - safeDistance += (grow->GetStackAmount() * STACK_MULTIPLIER); - - if (currentDistance < safeDistance && currentDistance < closestDistance) - { - closestDistance = currentDistance; - closestPuddle = unit; - } - } - - return closestPuddle; -} - -Position IccPutricideGrowingOozePuddleAction::CalculateSafeMovePosition(Unit* closestPuddle) -{ - static const float BASE_RADIUS = 2.0f; - static const float STACK_MULTIPLIER = 0.6f; - static const float BUFFER_DISTANCE = 2.0f; - static const float MIN_DISTANCE = 0.1f; - static const int NUM_ANGLES_TO_TEST = 8; - - float botX = bot->GetPositionX(); - float botY = bot->GetPositionY(); - float botZ = bot->GetPositionZ(); - - // Calculate current distance and safe distance - float currentDistance = std::max(MIN_DISTANCE, bot->GetExactDist(closestPuddle)); - float safeDistance = BASE_RADIUS; - if (Aura* grow = closestPuddle->GetAura(SPELL_GROW_AURA)) - safeDistance += (grow->GetStackAmount() * STACK_MULTIPLIER); - - // Calculate direction vector - float dx = botX - closestPuddle->GetPositionX(); - float dy = botY - closestPuddle->GetPositionY(); - float dist = std::max(MIN_DISTANCE, sqrt(dx * dx + dy * dy)); - - if (dist < MIN_DISTANCE * 2) - { - float randomAngle = float(rand()) / float(RAND_MAX) * 2 * M_PI; - dx = cos(randomAngle); - dy = sin(randomAngle); - } - else - { - dx /= dist; - dy /= dist; - } - - float moveDistance = safeDistance - currentDistance + BUFFER_DISTANCE; - - // Try different angles to find safe position - for (int i = 0; i < NUM_ANGLES_TO_TEST; i++) - { - float angle = (2 * M_PI * i) / NUM_ANGLES_TO_TEST; - float rotatedDx = dx * cos(angle) - dy * sin(angle); - float rotatedDy = dx * sin(angle) + dy * cos(angle); - - float testX = botX + rotatedDx * moveDistance; - float testY = botY + rotatedDy * moveDistance; - - if (!IsPositionTooCloseToOtherPuddles(testX, testY, closestPuddle) && bot->IsWithinLOS(testX, testY, botZ)) - { - // If main tank, add 6f to calculated position in the direction away from the puddle - if (botAI->IsTank(bot)) - { - float awayDx = testX - closestPuddle->GetPositionX(); - float awayDy = testY - closestPuddle->GetPositionY(); - float awayDist = std::sqrt(awayDx * awayDx + awayDy * awayDy); - if (awayDist > 0.001f) - { - awayDx /= awayDist; - awayDy /= awayDist; - testX += awayDx * 6.0f; - testY += awayDy * 6.0f; - } - } - return Position(testX, testY, botZ); - } - } - - // Fallback position if no safe position found - float fallbackX = botX + dx * moveDistance; - float fallbackY = botY + dy * moveDistance; - if (botAI->IsTank(bot)) - { - float awayDx = fallbackX - closestPuddle->GetPositionX(); - float awayDy = fallbackY - closestPuddle->GetPositionY(); - float awayDist = std::sqrt(awayDx * awayDx + awayDy * awayDy); - if (awayDist > 0.001f) - { - awayDx /= awayDist; - awayDy /= awayDist; - fallbackX += awayDx * 6.0f; - fallbackY += awayDy * 6.0f; - } - } - return Position(fallbackX, fallbackY, botZ); -} - -bool IccPutricideGrowingOozePuddleAction::IsPositionTooCloseToOtherPuddles(float x, float y, Unit* ignorePuddle) -{ - static const float BASE_RADIUS = 2.0f; - static const float STACK_MULTIPLIER = 0.6f; - - GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); - for (auto const& npc : npcs) - { - Unit* unit = botAI->GetUnit(npc); - if (!unit || unit == ignorePuddle || unit->GetEntry() != NPC_GROWING_OOZE_PUDDLE) - continue; - - float safeDistance = BASE_RADIUS; - if (Aura* grow = unit->GetAura(SPELL_GROW_AURA)) - safeDistance += (grow->GetStackAmount() * STACK_MULTIPLIER); - - float dist = unit->GetDistance2d(x, y); - if (dist < safeDistance) - return true; - } - - return false; -} - -bool IccPutricideVolatileOozeAction::Execute(Event /*event*/) -{ - static const float STACK_DISTANCE = 7.0f; - - Unit* ooze = AI_VALUE2(Unit*, "find target", "volatile ooze"); - if (!ooze) - return false; - - Unit* boss = AI_VALUE2(Unit*, "find target", "professor putricide"); - if (!boss) - return false; - - // Main tank handling - if (botAI->IsMainTank(bot) && bot->GetExactDist2d(ICC_PUTRICIDE_TANK_POSITION) > 20.0f && - !boss->HealthBelowPct(36) && boss->GetVictim() == bot) - return MoveTo(bot->GetMapId(), ICC_PUTRICIDE_TANK_POSITION.GetPositionX(), ICC_PUTRICIDE_TANK_POSITION.GetPositionY(), - ICC_PUTRICIDE_TANK_POSITION.GetPositionZ(), false, false, false, true, MovementPriority::MOVEMENT_COMBAT, true, false); - - // Skip if we have forbidden auras - if (botAI->HasAura("Gaseous Bloat", bot) || botAI->HasAura("Unbound Plague", bot)) - return false; - - // Find all alive oozes - std::vector aliveOozes; - const GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); - for (auto const& guid : npcs) - { - Unit* unit = botAI->GetUnit(guid); - if (unit && unit->IsAlive() && unit->GetEntry() == ooze->GetEntry()) - aliveOozes.push_back(unit); - } - - // If more than one ooze is alive, kill all but one - if (aliveOozes.size() > 1) - { - for (size_t i = 0; i < aliveOozes.size() - 1; ++i) - { - bot->Kill(bot, aliveOozes[i]); - } - } - - // Mark ooze with skull - MarkOozeWithSkull(ooze); - - // Melee handling (non-tanks) - if (botAI->IsMelee(bot) && !botAI->IsMainTank(bot)) - { - bot->SetTarget(ooze->GetGUID()); - bot->SetFacingToObject(ooze); - if (bot->IsWithinMeleeRange(ooze)) - return Attack(ooze); - } - - // Ranged/healer handling - if (botAI->IsRanged(bot) || botAI->IsHeal(bot)) - { - Unit* stackTarget = FindAuraTarget(); - if (stackTarget && bot->GetDistance2d(stackTarget) > STACK_DISTANCE) - { - return MoveTo(bot->GetMapId(), stackTarget->GetPositionX(), stackTarget->GetPositionY(), - stackTarget->GetPositionZ(), false, false, false, false, MovementPriority::MOVEMENT_COMBAT); - } - - if (ooze && !botAI->IsHeal(bot) && stackTarget && bot->GetDistance2d(stackTarget) <= STACK_DISTANCE) - { - bot->SetTarget(ooze->GetGUID()); - bot->SetFacingToObject(ooze); - if (bot->IsWithinRange(ooze, 25.0f)) - return Attack(ooze); - } - } - - return false; -} - -void IccPutricideVolatileOozeAction::MarkOozeWithSkull(Unit* ooze) -{ - Group* group = bot->GetGroup(); - if (!group) - return; - - constexpr uint8_t skullIconId = 7; - ObjectGuid skullGuid = group->GetTargetIcon(skullIconId); - Unit* markedUnit = botAI->GetUnit(skullGuid); - - // Clear dead marks or marks that are not on ooze - if (markedUnit && (!markedUnit->IsAlive() || (ooze && markedUnit != ooze))) - group->SetTargetIcon(skullIconId, bot->GetGUID(), ObjectGuid::Empty); - - // Mark alive ooze if needed - if (ooze && ooze->IsAlive() && (!skullGuid || !markedUnit)) - group->SetTargetIcon(skullIconId, bot->GetGUID(), ooze->GetGUID()); -} - -Unit* IccPutricideVolatileOozeAction::FindAuraTarget() -{ - Group* group = bot->GetGroup(); - if (!group) - return nullptr; - - for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next()) - { - Player* member = itr->GetSource(); - if (!member || !member->IsAlive() || member == bot) - continue; - - if (botAI->HasAura("Volatile Ooze Adhesive", member)) - return member; - } - - return nullptr; -} - -bool IccPutricideGasCloudAction::Execute(Event /*event*/) -{ - Unit* gasCloud = AI_VALUE2(Unit*, "find target", "gas cloud"); - if (!gasCloud) - return false; - - Unit* boss = AI_VALUE2(Unit*, "find target", "professor putricide"); - if (!boss) - return false; - - // Tank positioning logic - if (botAI->IsTank(bot) && bot->GetExactDist2d(ICC_PUTRICIDE_TANK_POSITION) > 20.0f && !boss->HealthBelowPct(36) && - boss->GetVictim() == bot) - return MoveTo(bot->GetMapId(), ICC_PUTRICIDE_TANK_POSITION.GetPositionX(), - ICC_PUTRICIDE_TANK_POSITION.GetPositionY(), ICC_PUTRICIDE_TANK_POSITION.GetPositionZ(), false, - false, false, true, MovementPriority::MOVEMENT_COMBAT, true, false); - - if (botAI->IsMainTank(bot)) - return false; - - bool hasGaseousBloat = botAI->HasAura("Gaseous Bloat", bot); - Unit* volatileOoze = AI_VALUE2(Unit*, "find target", "volatile ooze"); - - // Find all alive gasCloud - std::vector aliveGasCloud; - const GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); - for (auto const& guid : npcs) - { - Unit* unit = botAI->GetUnit(guid); - if (unit && unit->IsAlive() && unit->GetEntry() == gasCloud->GetEntry()) - aliveGasCloud.push_back(unit); - } - - // If more than one GasCloud is alive, kill all but one - if (aliveGasCloud.size() > 1) - { - for (size_t i = 0; i < aliveGasCloud.size() - 1; ++i) - { - bot->Kill(bot, aliveGasCloud[i]); - } - } - - // Skip if we have no aura but ooze exists - if (!hasGaseousBloat && volatileOoze) - return false; - - if (hasGaseousBloat) - return HandleGaseousBloatMovement(gasCloud); - - return HandleGroupAuraSituation(gasCloud); -} - -bool IccPutricideGasCloudAction::HandleGaseousBloatMovement(Unit* gasCloud) -{ - bool hasGaseousBloat = botAI->HasAura("Gaseous Bloat", bot); - - if (!hasGaseousBloat) - return false; - - if (hasGaseousBloat && !bot->HasAura(SPELL_NITRO_BOOSTS)) - bot->AddAura(SPELL_NITRO_BOOSTS, - bot); // to make it a bit easier when abo fails to slow or bots take forever to kill oozes - - static const int NUM_ANGLES = 32; // Increased from 16 for better corner escape - static const float MIN_SAFE_DISTANCE = 35.0f; - static const float EMERGENCY_DISTANCE = 15.0f; - static const float GAS_BOMB_SAFE_DIST = 6.0f; - static const float MOVEMENT_INCREMENT = 5.0f; // Fixed movement increment - - Position botPos = bot->GetPosition(); - Position cloudPos = gasCloud->GetPosition(); - float cloudDist = gasCloud->GetExactDist2d(botPos); - - if (cloudDist >= MIN_SAFE_DISTANCE) - return false; - - // Gather all choking gas bombs - GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); - std::vector gasBombs; - for (auto const& guid : npcs) - { - Unit* unit = botAI->GetUnit(guid); - if (unit && unit->IsAlive() && unit->GetEntry() == NPC_CHOKING_GAS_BOMB) - gasBombs.push_back(unit); - } - - // Calculate direction away from cloud - float dx = botPos.GetPositionX() - cloudPos.GetPositionX(); - float dy = botPos.GetPositionY() - cloudPos.GetPositionY(); - float dist = std::max(0.1f, sqrt(dx * dx + dy * dy)); - - if (dist <= 0) - return false; - - dx /= dist; - dy /= dist; - - // Try to find safe movement position with strict corner avoidance - Position bestPos; - bool foundPath = false; - float bestScore = 0.0f; - - for (int i = 0; i < NUM_ANGLES; i++) - { - float angle = (2 * M_PI * i) / NUM_ANGLES; - float rotatedDx = dx * cos(angle) - dy * sin(angle); - float rotatedDy = dx * sin(angle) + dy * cos(angle); - - // Only test positions at fixed increments of 5.0f - for (float testDist = MOVEMENT_INCREMENT; testDist <= 20.0f; testDist += MOVEMENT_INCREMENT) - { - float testX = botPos.GetPositionX() + rotatedDx * testDist; - float testY = botPos.GetPositionY() + rotatedDy * testDist; - float testZ = botPos.GetPositionZ(); - Position testPos(testX, testY, testZ); - - float newCloudDist = cloudPos.GetExactDist2d(testPos); - - // Check gas bomb distance - float minGasBombDist = FLT_MAX; - for (Unit* bomb : gasBombs) - { - float bombDist = bomb->GetDistance2d(testX, testY); - if (bombDist < minGasBombDist) - minGasBombDist = bombDist; - } - - if (newCloudDist > cloudDist && minGasBombDist >= GAS_BOMB_SAFE_DIST && - bot->IsWithinLOS(testX, testY, testZ)) - { - // Strict corner avoidance - test movement freedom thoroughly - int freeDirections = 0; - static const int CHECK_DIRS = 16; // More directions for better detection - static const float CHECK_DIST = 8.0f; // Longer distance to detect walls/corners early - - for (int j = 0; j < CHECK_DIRS; j++) - { - float checkAngle = (2 * M_PI * j) / CHECK_DIRS; - float checkX = testX + cos(checkAngle) * CHECK_DIST; - float checkY = testY + sin(checkAngle) * CHECK_DIST; - if (bot->IsWithinLOS(checkX, checkY, testZ)) - freeDirections++; - } - - float freedomScore = (float)freeDirections / CHECK_DIRS; - - // STRICT: Reject any position that looks like a corner or restricted area - // Require at least 80% of directions to be free (13 out of 16) - if (freedomScore < 0.8f) - continue; // Skip this position entirely - - // Also check for "dead ends" - positions that might lead to corners - // Test if we can move further in the same direction - bool canContinueMoving = true; - float continueX = testX + rotatedDx * MOVEMENT_INCREMENT; - float continueY = testY + rotatedDy * MOVEMENT_INCREMENT; - if (!bot->IsWithinLOS(continueX, continueY, testZ)) - canContinueMoving = false; - - // Bonus for positions that allow continued movement in the same direction - float continuityBonus = canContinueMoving ? 5.0f : 0.0f; - - float combinedScore = newCloudDist + (freedomScore * 15.0f) + minGasBombDist + continuityBonus; - - if (!foundPath || combinedScore > bestScore) - { - bestPos = testPos; - bestScore = combinedScore; - foundPath = true; - } - } - } - } - - if (foundPath) - { - botAI->Reset(); - return MoveTo(bot->GetMapId(), bestPos.GetPositionX(), bestPos.GetPositionY(), bestPos.GetPositionZ(), false, - false, false, false, MovementPriority::MOVEMENT_COMBAT); - } - // Emergency movement - only when very close to cloud - if (cloudDist < EMERGENCY_DISTANCE) - { - Position emergencyPos = CalculateEmergencyPosition(botPos, dx, dy); - - // Even for emergency, avoid corners - float minEmergencyGasBombDist = FLT_MAX; - for (Unit* bomb : gasBombs) - { - float bombDist = bomb->GetDistance2d(emergencyPos.GetPositionX(), emergencyPos.GetPositionY()); - if (bombDist < minEmergencyGasBombDist) - minEmergencyGasBombDist = bombDist; - } - - if (minEmergencyGasBombDist >= GAS_BOMB_SAFE_DIST && - bot->IsWithinLOS(emergencyPos.GetPositionX(), emergencyPos.GetPositionY(), emergencyPos.GetPositionZ())) - { - botAI->Reset(); - return MoveTo(bot->GetMapId(), emergencyPos.GetPositionX(), emergencyPos.GetPositionY(), - emergencyPos.GetPositionZ(), false, false, false, false, MovementPriority::MOVEMENT_COMBAT); - } - } - - return false; -} - -bool IccPutricideGasCloudAction::FindSafeMovementPosition(const Position& botPos, const Position& cloudPos, float dx, - float dy, int numAngles, Position& resultPos) -{ - float bestScore = 0.0f; - bool foundPath = false; - resultPos = botPos; - static const float MOVEMENT_INCREMENT = 5.0f; // Fixed movement increment - - for (int i = 0; i < numAngles; i++) - { - float angle = (2 * M_PI * i) / numAngles; - float rotatedDx = dx * cos(angle) - dy * sin(angle); - float rotatedDy = dx * sin(angle) + dy * cos(angle); - - for (float testDist = MOVEMENT_INCREMENT; testDist <= 20.0f; testDist += MOVEMENT_INCREMENT) - { - Position testPos(botPos.GetPositionX() + rotatedDx * testDist, botPos.GetPositionY() + rotatedDy * testDist, - botPos.GetPositionZ()); - - float newCloudDist = cloudPos.GetExactDist2d(testPos); - if (newCloudDist > cloudPos.GetExactDist2d(botPos) && - bot->IsWithinLOS(testPos.GetPositionX(), testPos.GetPositionY(), testPos.GetPositionZ())) - { - // Strict corner prevention - test movement freedom thoroughly - int freeDirections = 0; - static const int CHECK_DIRECTIONS = 16; - static const float CHECK_DISTANCE = 8.0f; - - for (int j = 0; j < CHECK_DIRECTIONS; j++) - { - float checkAngle = (2 * M_PI * j) / CHECK_DIRECTIONS; - float checkX = testPos.GetPositionX() + cos(checkAngle) * CHECK_DISTANCE; - float checkY = testPos.GetPositionY() + sin(checkAngle) * CHECK_DISTANCE; - if (bot->IsWithinLOS(checkX, checkY, testPos.GetPositionZ())) - freeDirections++; - } - - float freedomScore = (float)freeDirections / CHECK_DIRECTIONS; - - // REJECT positions that could lead to corners - require high freedom - if (freedomScore < 0.8f) // 80% of directions must be free - continue; - - // Check if we can continue moving in the same direction (avoid dead ends) - float continueX = testPos.GetPositionX() + rotatedDx * MOVEMENT_INCREMENT; - float continueY = testPos.GetPositionY() + rotatedDy * MOVEMENT_INCREMENT; - bool canContinue = bot->IsWithinLOS(continueX, continueY, testPos.GetPositionZ()); - - float continuityBonus = canContinue ? 3.0f : 0.0f; - float combinedScore = newCloudDist + (freedomScore * 12.0f) + continuityBonus; - - if (!foundPath || combinedScore > bestScore) - { - resultPos = testPos; - bestScore = combinedScore; - foundPath = true; - } - } - } - } - - return foundPath; -} - -Position IccPutricideGasCloudAction::CalculateEmergencyPosition(const Position& botPos, float dx, float dy) -{ - // For emergency, still try to avoid corners but prioritize getting away from immediate danger - Position bestPos = - Position(botPos.GetPositionX() + dx * 15.0f, botPos.GetPositionY() + dy * 15.0f, botPos.GetPositionZ()); - float bestFreedom = 0.0f; - - // Try fewer directions for emergency but still avoid corners - for (int i = 0; i < 8; i++) - { - float angle = (2 * M_PI * i) / 8; - float rotatedDx = dx * cos(angle) - dy * sin(angle); - float rotatedDy = dx * sin(angle) + dy * cos(angle); - - Position testPos(botPos.GetPositionX() + rotatedDx * 15.0f, botPos.GetPositionY() + rotatedDy * 15.0f, - botPos.GetPositionZ()); - - if (bot->IsWithinLOS(testPos.GetPositionX(), testPos.GetPositionY(), testPos.GetPositionZ())) - { - // Quick freedom check for emergency - int freeDirections = 0; - static const int EMERGENCY_CHECK_DIRS = 8; - static const float EMERGENCY_CHECK_DIST = 6.0f; - - for (int j = 0; j < EMERGENCY_CHECK_DIRS; j++) - { - float checkAngle = (2 * M_PI * j) / EMERGENCY_CHECK_DIRS; - float checkX = testPos.GetPositionX() + cos(checkAngle) * EMERGENCY_CHECK_DIST; - float checkY = testPos.GetPositionY() + sin(checkAngle) * EMERGENCY_CHECK_DIST; - if (bot->IsWithinLOS(checkX, checkY, testPos.GetPositionZ())) - freeDirections++; - } - - float freedom = (float)freeDirections / EMERGENCY_CHECK_DIRS; - - // For emergency, accept positions with at least 60% freedom - if (freedom >= 0.6f && freedom > bestFreedom) - { - bestPos = testPos; - bestFreedom = freedom; - } - } - } - - return bestPos; -} - -bool IccPutricideGasCloudAction::HandleGroupAuraSituation(Unit* gasCloud) -{ - Group* group = bot->GetGroup(); - if (!group || botAI->IsHeal(bot)) - return false; - - // Mark gas cloud with skull if no volatile ooze is present or alive - Unit* volatileOoze = AI_VALUE2(Unit*, "find target", "volatile ooze"); - if ((!volatileOoze || !volatileOoze->IsAlive()) && gasCloud && gasCloud->IsAlive()) - { - Group* group = bot->GetGroup(); - if (group) - { - constexpr uint8_t skullIconId = 7; - ObjectGuid currentSkull = group->GetTargetIcon(skullIconId); - Unit* markedUnit = botAI->GetUnit(currentSkull); - if (!markedUnit || !markedUnit->IsAlive() || markedUnit != gasCloud) - group->SetTargetIcon(skullIconId, bot->GetGUID(), gasCloud->GetGUID()); - } - } - - float currentDist = gasCloud ? bot->GetDistance(gasCloud) : 0.0f; - const float MIN_SAFE_DISTANCE = 15.0f; - - // Always maintain minimum distance when group doesn't have bloat - if (!GroupHasGaseousBloat(group) && gasCloud && currentDist < MIN_SAFE_DISTANCE) - { - // Move away from gas cloud - float angle = bot->GetAngle(gasCloud); - float x = bot->GetPositionX() + cos(angle) * -MIN_SAFE_DISTANCE; - float y = bot->GetPositionY() + sin(angle) * -MIN_SAFE_DISTANCE; - return MoveTo(bot->GetMapId(), x, y, bot->GetPositionZ(), false, false, false, false, - MovementPriority::MOVEMENT_COMBAT); - } - - if (GroupHasGaseousBloat(group) && gasCloud) - { - bot->SetTarget(gasCloud->GetGUID()); - bot->SetFacingToObject(gasCloud); - - // Attack logic for group with Gaseous Bloat - if (botAI->IsRanged(bot)) - { - // For ranged attackers, maintain optimal distance (15-25 yards) - if (currentDist > 25.0f) - { - // Move closer if too far - return MoveTo(bot->GetMapId(), gasCloud->GetPositionX(), gasCloud->GetPositionY(), - gasCloud->GetPositionZ(), false, false, false, false, MovementPriority::MOVEMENT_COMBAT); - } - else if (currentDist < MIN_SAFE_DISTANCE) - { - // Move away if too close (but stay closer than normal safe distance since we need to attack) - float angle = bot->GetAngle(gasCloud); - float x = bot->GetPositionX() + cos(angle) * -12.0f; - float y = bot->GetPositionY() + sin(angle) * -12.0f; - return MoveTo(bot->GetMapId(), x, y, bot->GetPositionZ(), false, false, false, false, - MovementPriority::MOVEMENT_COMBAT); - } - else - { - // Attack if at good range - return Attack(gasCloud); - } - } - else if (botAI->IsMelee(bot) && !botAI->IsTank(bot)) - { - // For melee attackers, move to attack range (0-5 yards) - if (currentDist > 5.0f) - { - return MoveTo(bot->GetMapId(), gasCloud->GetPositionX(), gasCloud->GetPositionY(), - gasCloud->GetPositionZ(), false, false, false, false, MovementPriority::MOVEMENT_COMBAT); - } - else - return Attack(gasCloud); - } - } - - return false; -} - -bool IccPutricideGasCloudAction::GroupHasGaseousBloat(Group* group) -{ - for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next()) - { - Player* member = itr->GetSource(); - if (member && botAI->HasAura("Gaseous Bloat", member)) - return true; - } - return false; -} - -bool IccPutricideAvoidMalleableGooAction::Execute(Event /*event*/) -{ - Unit* boss = AI_VALUE2(Unit*, "find target", "professor putricide"); - if (!boss) - return false; - - // Tank handling for choking gas bomb - if (HandleTankPositioning(boss)) - return true; - - // Skip if volatile ooze or gas cloud exists - if (AI_VALUE2(Unit*, "find target", "volatile ooze") || AI_VALUE2(Unit*, "find target", "gas cloud")) - return false; - - // Handle unbound plague movement - if (HandleUnboundPlague(boss)) - return true; - - // Handle ranged/melee positioning - return HandleBossPositioning(boss); -} - -bool IccPutricideAvoidMalleableGooAction::HandleTankPositioning(Unit*) -{ - if (!botAI->IsTank(bot)) - return false; - - Unit* bomb = bot->FindNearestCreature(NPC_CHOKING_GAS_BOMB, 100.0f); - if (!bomb) - return false; - - const float safeDistance = 15.0f; - float currentDistance = bot->GetDistance2d(bomb); - - if (currentDistance < safeDistance) - return MoveAway(bomb, safeDistance - currentDistance); - - return false; -} - -bool IccPutricideAvoidMalleableGooAction::HandleUnboundPlague(Unit* boss) -{ - if (boss && boss->HealthBelowPct(35)) - return false; - - if (!botAI->HasAura("Unbound Plague", bot)) - return false; - - Group* group = bot->GetGroup(); - if (!group) - return false; - - const float UNBOUND_PLAGUE_DISTANCE = 20.0f; - float closestDistance = UNBOUND_PLAGUE_DISTANCE; - Unit* closestPlayer = nullptr; - - // Find closest player - for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next()) - { - Player* member = itr->GetSource(); - if (!member || !member->IsAlive() || member == bot) - continue; - - float dist = bot->GetDistance2d(member); - if (dist < closestDistance) - { - closestDistance = dist; - closestPlayer = member; - } - } - - if (!closestPlayer || closestDistance >= UNBOUND_PLAGUE_DISTANCE) - { - bot->Kill(bot, bot); - return true; - } - - // Calculate move away position - float dx = bot->GetPositionX() - closestPlayer->GetPositionX(); - float dy = bot->GetPositionY() - closestPlayer->GetPositionY(); - float dist = sqrt(dx * dx + dy * dy); - - if (dist <= 0) - return false; - - dx /= dist; - dy /= dist; - float moveDistance = UNBOUND_PLAGUE_DISTANCE - closestDistance + 2.0f; - - float moveX = bot->GetPositionX() + dx * moveDistance; - float moveY = bot->GetPositionY() + dy * moveDistance; - - if (bot->IsWithinLOS(moveX, moveY, bot->GetPositionZ())) - { - return MoveTo(bot->GetMapId(), moveX, moveY, bot->GetPositionZ(), false, false, false, false, - MovementPriority::MOVEMENT_COMBAT); - } - - return false; -} - -bool IccPutricideAvoidMalleableGooAction::HandleBossPositioning(Unit* boss) -{ - if (botAI->IsTank(bot)) - return false; - - // If boss is close to putricide_bad_position, all non-tank bots should be 1f in front of boss - const float BAD_POS_THRESHOLD = 10.0f; - const float IN_FRONT_DISTANCE = 1.0f; - float bossToBadPos = boss->GetExactDist2d(ICC_PUTRICIDE_BAD_POSITION.GetPositionX(), ICC_PUTRICIDE_BAD_POSITION.GetPositionY()); - - if (bossToBadPos <= BAD_POS_THRESHOLD) - { - // Move to 1f in front of boss - float bossOrientation = boss->GetOrientation(); - float targetX = boss->GetPositionX() + cos(bossOrientation) * IN_FRONT_DISTANCE; - float targetY = boss->GetPositionY() + sin(bossOrientation) * IN_FRONT_DISTANCE; - float targetZ = boss->GetPositionZ(); - - // Only move if not already close enough - if (bot->GetExactDist2d(targetX, targetY) > 0.5f) - { - bot->SetFacingToObject(boss); - return MoveTo(bot->GetMapId(), targetX, targetY, targetZ, false, false, false, botAI->IsRanged(bot), - MovementPriority::MOVEMENT_COMBAT); - } - return false; - } - - float distToBoss = bot->GetExactDist2d(boss); - bool isRanged = botAI->IsRanged(bot); - - // Calculate desired position in front of boss - float desiredDistance = - isRanged ? ((bot->getClass() == CLASS_HUNTER) ? 14.0f : 6.0f) : (distToBoss < 2.0f ? 3.0f : 5.0f); - - // Check if we need to move - if ((std::abs(distToBoss - desiredDistance) > 0.5f || !boss->isInFront(bot)) && - (!isRanged || (isRanged && !botAI->IsTank(bot)))) - { - Position targetPos = CalculateBossPosition(boss, desiredDistance); - - // Check for obstacles - if (HasObstacleBetween(bot->GetPosition(), targetPos)) - { - // Use arc movement to navigate around obstacles - Position arcPoint = CalculateArcPoint(bot->GetPosition(), targetPos, boss->GetPosition()); - - if (bot->GetExactDist2d(arcPoint) > 1.0f) - { - bot->SetFacingToObject(boss); - return MoveTo(bot->GetMapId(), arcPoint.GetPositionX(), arcPoint.GetPositionY(), - arcPoint.GetPositionZ(), false, false, false, isRanged, - MovementPriority::MOVEMENT_COMBAT); - } - } - else - { - // No obstacles, move in increments directly toward target - Position adjustedTarget = CalculateIncrementalMove(bot->GetPosition(), targetPos, 2.0f); - bot->SetFacingToObject(boss); - return MoveTo(bot->GetMapId(), adjustedTarget.GetPositionX(), adjustedTarget.GetPositionY(), - adjustedTarget.GetPositionZ(), false, false, false, isRanged, - MovementPriority::MOVEMENT_COMBAT); - } - } - - return false; -} - -Position IccPutricideAvoidMalleableGooAction::CalculateBossPosition(Unit* boss, float distance) -{ - float bossOrientation = boss->GetOrientation(); - return Position(boss->GetPositionX() + cos(bossOrientation) * distance, - boss->GetPositionY() + sin(bossOrientation) * distance, boss->GetPositionZ()); -} - -bool IccPutricideAvoidMalleableGooAction::HasObstacleBetween(const Position& from, const Position& to) -{ - GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); - for (auto const& npc : npcs) - { - Unit* unit = botAI->GetUnit(npc); - if (!unit || !unit->IsAlive()) - continue; - - if (unit->GetEntry() == NPC_GROWING_OOZE_PUDDLE || unit->GetEntry() == NPC_CHOKING_GAS_BOMB) - { - if (IsOnPath(from, to, unit->GetPosition(), 3.0f)) - return true; - } - } - return false; -} - -bool IccPutricideAvoidMalleableGooAction::IsOnPath(const Position& from, const Position& to, const Position& point, - float threshold) -{ - float pathX = to.GetPositionX() - from.GetPositionX(); - float pathY = to.GetPositionY() - from.GetPositionY(); - float pathLen = std::sqrt(pathX * pathX + pathY * pathY); - - if (pathLen < 0.1f) - return false; - - float normX = pathX / pathLen; - float normY = pathY / pathLen; - - float toPointX = point.GetPositionX() - from.GetPositionX(); - float toPointY = point.GetPositionY() - from.GetPositionY(); - float proj = toPointX * normX + toPointY * normY; - - if (proj < 0 || proj > pathLen) - return false; - - float closestX = from.GetPositionX() + normX * proj; - float closestY = from.GetPositionY() + normY * proj; - float distToPath = std::sqrt((point.GetPositionX() - closestX) * (point.GetPositionX() - closestX) + - (point.GetPositionY() - closestY) * (point.GetPositionY() - closestY)); - - return distToPath < threshold; -} - -Position IccPutricideAvoidMalleableGooAction::CalculateArcPoint(const Position& current, const Position& target, const Position& center) -{ - // Calculate vectors from center to current position and target - float currentX = current.GetPositionX() - center.GetPositionX(); - float currentY = current.GetPositionY() - center.GetPositionY(); - float targetX = target.GetPositionX() - center.GetPositionX(); - float targetY = target.GetPositionY() - center.GetPositionY(); - - // Calculate distances - float currentDist = std::sqrt(currentX * currentX + currentY * currentY); - float targetDist = std::sqrt(targetX * targetX + targetY * targetY); - - // Normalize vectors - currentX /= currentDist; - currentY /= currentDist; - targetX /= targetDist; - targetY /= targetDist; - - // Calculate dot product to find the angle between vectors - float dotProduct = currentX * targetX + currentY * targetY; - dotProduct = std::max(-1.0f, std::min(1.0f, dotProduct)); // Clamp to [-1, 1] - float angle = std::acos(dotProduct); - - // Determine rotation direction (clockwise or counterclockwise) - float crossProduct = currentX * targetY - currentY * targetX; - float stepAngle = angle * 0.25f; // Move 25% along the arc - - if (crossProduct < 0) - stepAngle = -stepAngle; // Clockwise - - // Calculate rotation matrix components - float cos_a = std::cos(stepAngle); - float sin_a = std::sin(stepAngle); - - // Rotate current vector - float rotatedX = currentX * cos_a - currentY * sin_a; - float rotatedY = currentX * sin_a + currentY * cos_a; - - // Scale to match the target distance for smoother approach - float desiredDist = currentDist * 0.9f + targetDist * 0.1f; - - // Calculate the new position - return Position(center.GetPositionX() + rotatedX * desiredDist, center.GetPositionY() + rotatedY * desiredDist, - current.GetPositionZ()); -} - -Position IccPutricideAvoidMalleableGooAction::CalculateIncrementalMove(const Position& current, const Position& target, - float maxDistance) -{ - float dx = target.GetPositionX() - current.GetPositionX(); - float dy = target.GetPositionY() - current.GetPositionY(); - float distance = std::sqrt(dx * dx + dy * dy); - - if (distance <= maxDistance) - return target; - - dx /= distance; - dy /= distance; - - return Position(current.GetPositionX() + dx * maxDistance, current.GetPositionY() + dy * maxDistance, - target.GetPositionZ()); -} - -// BPC -bool IccBpcKelesethTankAction::Execute(Event /*event*/) -{ - if (!botAI->IsAssistTank(bot)) - return false; - - // Handle boss positioning - Unit* boss = AI_VALUE2(Unit*, "find target", "prince keleseth"); - if (!boss) - return false; - - bool isBossVictim = false; - if (boss && boss->GetVictim() == bot) - isBossVictim = true; - - // If not actively tanking, attack the boss - if (boss->GetVictim() != bot) - { - bot->SetTarget(boss->GetGUID()); - bot->SetFacingToObject(boss); - Attack(boss); - } - - // If tanking boss, check for Dark Nucleus logic - collect any nucleus not targeting us - if (boss->GetVictim() == bot) - { - GuidVector targets = AI_VALUE(GuidVector, "possible targets"); - for (auto const& targetGuid : targets) - { - Unit* nucleus = botAI->GetUnit(targetGuid); - if (nucleus && nucleus->IsAlive() && nucleus->GetEntry() == NPC_DARK_NUCLEUS) - { - // Attack nucleus that are NOT targeting us (to collect them) - if (nucleus->GetVictim() != bot) - { - bot->SetTarget(nucleus->GetGUID()); - bot->SetFacingToObject(nucleus); - Attack(nucleus); - // Return early to focus on nucleus collection first - return false; - } - } - } - } - - // Positioning logic - only execute if no nucleus needs collecting - if (botAI->HasAura("Invocation of Blood", boss) && bot->GetExactDist2d(ICC_BPC_MT_POSITION) > 15.0f && isBossVictim) - { - // Calculate direction vector - float dirX = ICC_BPC_MT_POSITION.GetPositionX() - bot->GetPositionX(); - float dirY = ICC_BPC_MT_POSITION.GetPositionY() - bot->GetPositionY(); - - // Calculate distance and normalize - float length = std::sqrt(dirX * dirX + dirY * dirY); - if (length > 0.001f) - { - dirX /= length; - dirY /= length; - - // Calculate movement distance (max 3 yards at a time) - float moveDist = std::min(3.0f, length); - - // Calculate target position - float moveX = bot->GetPositionX() + dirX * moveDist; - float moveY = bot->GetPositionY() + dirY * moveDist; - float moveZ = bot->GetPositionZ(); - - MoveTo(bot->GetMapId(), moveX, moveY, moveZ, false, false, false, false, MovementPriority::MOVEMENT_COMBAT); - } - } - - // Always attack boss when tanking (if no nucleus was prioritized) - if (boss->GetVictim() == bot) - { - bot->SetTarget(boss->GetGUID()); - bot->SetFacingToObject(boss); - Attack(boss); - } - - return false; -} - -bool IccBpcMainTankAction::Execute(Event /*event*/) -{ - // Main tank specific behavior (higher priority) - if (botAI->IsMainTank(bot)) - { - // Get target princes - auto* valanar = AI_VALUE2(Unit*, "find target", "prince valanar"); - auto* taldaram = AI_VALUE2(Unit*, "find target", "prince taldaram"); - - // Check if we're the target of both princes - bool isVictimOfValanar = false; - if (valanar && valanar->GetVictim() == bot) - isVictimOfValanar = true; - - bool isVictimOfTaldaram = false; - if (taldaram && taldaram->GetVictim() == bot) - isVictimOfTaldaram = true; - - // Move to MT position if targeted by both princes and not already close - if (isVictimOfValanar && isVictimOfTaldaram && bot->GetExactDist2d(ICC_BPC_MT_POSITION) > 15.0f) - { - // Calculate direction vector - float dirX = ICC_BPC_MT_POSITION.GetPositionX() - bot->GetPositionX(); - float dirY = ICC_BPC_MT_POSITION.GetPositionY() - bot->GetPositionY(); - - // Calculate distance and normalize - float length = std::sqrt(dirX * dirX + dirY * dirY); - if (length > 0.001f) - { - dirX /= length; - dirY /= length; - - // Calculate movement distance (max 3 yards at a time) - float moveDist = std::min(3.0f, length); - - // Calculate target position - float moveX = bot->GetPositionX() + dirX * moveDist; - float moveY = bot->GetPositionY() + dirY * moveDist; - float moveZ = bot->GetPositionZ(); - - MoveTo(bot->GetMapId(), moveX, moveY, moveZ, false, false, false, false, - MovementPriority::MOVEMENT_COMBAT); - } - } - - // Attack prince that's not targeting us - if (valanar && !isVictimOfValanar) - { - bot->SetTarget(valanar->GetGUID()); - bot->SetFacingToObject(valanar); - Attack(valanar); - } - - if (taldaram && !isVictimOfTaldaram) - { - bot->SetTarget(taldaram->GetGUID()); - bot->SetFacingToObject(taldaram); - Attack(taldaram); - } - } - - // Target marking for all tanks, called after main tank priority actions - if (botAI->IsTank(bot)) - MarkEmpoweredPrince(); - - return false; -} - -void IccBpcMainTankAction::MarkEmpoweredPrince() -{ - static constexpr uint8_t SKULL_RAID_ICON = 7; - - // Find empowered prince (Invocation of Blood) - Unit* empoweredPrince = nullptr; - const GuidVector& targets = AI_VALUE(GuidVector, "possible targets"); - - for (auto const& targetGuid : targets) - { - Unit* unit = botAI->GetUnit(targetGuid); - if (!unit || !unit->IsAlive()) - continue; - - if (botAI->HasAura("Invocation of Blood", unit)) - { - const uint32 entry = unit->GetEntry(); - if (entry == NPC_PRINCE_KELESETH || entry == NPC_PRINCE_VALANAR || entry == NPC_PRINCE_TALDARAM) - { - empoweredPrince = unit; - break; - } - } - } - - // Handle marking if we found an empowered prince - if (empoweredPrince && empoweredPrince->IsAlive()) - { - Group* group = bot->GetGroup(); - if (group) - { - const ObjectGuid currentSkullGuid = group->GetTargetIcon(SKULL_RAID_ICON); - Unit* markedUnit = botAI->GetUnit(currentSkullGuid); - - // Clear dead marks or marks that are not on empowered prince - if (markedUnit && (!markedUnit->IsAlive() || markedUnit != empoweredPrince)) - { - group->SetTargetIcon(SKULL_RAID_ICON, bot->GetGUID(), ObjectGuid::Empty); - } - - // Mark alive empowered prince if needed - if (!currentSkullGuid || !markedUnit) - { - group->SetTargetIcon(SKULL_RAID_ICON, bot->GetGUID(), empoweredPrince->GetGUID()); - } - } - } -} - -bool IccBpcEmpoweredVortexAction::Execute(Event /*event*/) -{ - Unit* valanar = AI_VALUE2(Unit*, "find target", "prince valanar"); - if (!valanar) - return false; - - // Check if boss is casting empowered vortex - bool isCastingVortex = false; - if (valanar && valanar->HasUnitState(UNIT_STATE_CASTING) && - (valanar->FindCurrentSpellBySpellId(SPELL_EMPOWERED_SHOCK_VORTEX1) || - valanar->FindCurrentSpellBySpellId(SPELL_EMPOWERED_SHOCK_VORTEX2) || - valanar->FindCurrentSpellBySpellId(SPELL_EMPOWERED_SHOCK_VORTEX3) || - valanar->FindCurrentSpellBySpellId(SPELL_EMPOWERED_SHOCK_VORTEX4))) - isCastingVortex = true; - - if (isCastingVortex) - { - // Use complex positioning system for empowered vortex - return HandleEmpoweredVortexSpread(); - } - else - { - // Use simple ranged spreading for non-vortex situations - return MaintainRangedSpacing(); - } -} - -bool IccBpcEmpoweredVortexAction::MaintainRangedSpacing() -{ - const float safeSpacingRadius = 7.0f; - const float moveIncrement = 2.0f; - const float maxMoveDistance = 5.0f; - const bool isRanged = botAI->IsRanged(bot) || botAI->IsHeal(bot); - - if (!isRanged) - return false; - - // Get group members - const GuidVector members = AI_VALUE(GuidVector, "group members"); - - // Calculate a combined vector representing all nearby members' positions - float totalX = 0.0f; - float totalY = 0.0f; - int nearbyCount = 0; - - for (auto const& memberGuid : members) - { - Unit* member = botAI->GetUnit(memberGuid); - if (!member || !member->IsAlive() || member == bot) - { - continue; - } - - const float distance = bot->GetExactDist2d(member); - if (distance < safeSpacingRadius) - { - // Calculate vector from member to bot - float dx = bot->GetPositionX() - member->GetPositionX(); - float dy = bot->GetPositionY() - member->GetPositionY(); - - // Weight by inverse distance (closer members have more influence) - float weight = (safeSpacingRadius - distance) / safeSpacingRadius; - totalX += dx * weight; - totalY += dy * weight; - nearbyCount++; - } - } - - // If we have nearby members, move away in the combined direction - if (nearbyCount > 0) - { - // Normalize the combined vector - float magnitude = std::sqrt(totalX * totalX + totalY * totalY); - if (magnitude > 0.001f) // Avoid division by zero - { - totalX /= magnitude; - totalY /= magnitude; - - // Calculate move distance - float moveDistance = std::min(moveIncrement, maxMoveDistance); - - // Create target position in the combined direction - float targetX = bot->GetPositionX() + totalX * moveDistance; - float targetY = bot->GetPositionY() + totalY * moveDistance; - float targetZ = bot->GetPositionZ(); - - // Check if the target position is valid and move there - if (bot->IsWithinLOS(targetX, targetY, targetZ)) - { - MoveTo(bot->GetMapId(), targetX, targetY, targetZ, false, false, false, true, - MovementPriority::MOVEMENT_NORMAL); - } - else - { - // If LOS check fails, try shorter distance - targetX = bot->GetPositionX() + totalX * (moveDistance * 0.5f); - targetY = bot->GetPositionY() + totalY * (moveDistance * 0.5f); - MoveTo(bot->GetMapId(), targetX, targetY, targetZ, false, false, false, true, - MovementPriority::MOVEMENT_NORMAL); - } - } - } - - return false; // Everyone is properly spaced -} - -bool IccBpcEmpoweredVortexAction::HandleEmpoweredVortexSpread() -{ - const float safeSpacingRadius = 13.0f; - const float moveIncrement = 2.0f; - const float maxMoveDistance = 5.0f; - const bool isTank = botAI->IsTank(bot); - - if (isTank) - return false; - - // Get group members - const GuidVector members = AI_VALUE(GuidVector, "group members"); - - // Calculate a combined vector representing all nearby members' positions - float totalX = 0.0f; - float totalY = 0.0f; - int nearbyCount = 0; - - for (auto const& memberGuid : members) - { - Unit* member = botAI->GetUnit(memberGuid); - if (!member || !member->IsAlive() || member == bot) - { - continue; - } - - const float distance = bot->GetExactDist2d(member); - if (distance < safeSpacingRadius) - { - // Calculate vector from member to bot - float dx = bot->GetPositionX() - member->GetPositionX(); - float dy = bot->GetPositionY() - member->GetPositionY(); - - // Weight by inverse distance (closer members have more influence) - float weight = (safeSpacingRadius - distance) / safeSpacingRadius; - totalX += dx * weight; - totalY += dy * weight; - nearbyCount++; - } - } - - // If we have nearby members, move away in the combined direction - if (nearbyCount > 0) - { - // Normalize the combined vector - float magnitude = std::sqrt(totalX * totalX + totalY * totalY); - if (magnitude > 0.001f) // Avoid division by zero - { - totalX /= magnitude; - totalY /= magnitude; - - // Calculate move distance - float moveDistance = std::min(moveIncrement, maxMoveDistance); - - // Create target position in the combined direction - float targetX = bot->GetPositionX() + totalX * moveDistance; - float targetY = bot->GetPositionY() + totalY * moveDistance; - float targetZ = bot->GetPositionZ(); - - // Check if the target position is valid and move there - if (bot->IsWithinLOS(targetX, targetY, targetZ)) - { - MoveTo(bot->GetMapId(), targetX, targetY, targetZ, false, false, false, true, - MovementPriority::MOVEMENT_NORMAL); - } - else - { - // If LOS check fails, try shorter distance - targetX = bot->GetPositionX() + totalX * (moveDistance * 0.5f); - targetY = bot->GetPositionY() + totalY * (moveDistance * 0.5f); - MoveTo(bot->GetMapId(), targetX, targetY, targetZ, false, false, false, true, - MovementPriority::MOVEMENT_NORMAL); - } - } - } - - return false; // Everyone is properly spaced -} - -bool IccBpcKineticBombAction::Execute(Event /*event*/) -{ - // Early exit if not ranged DPS - if (!botAI->IsRangedDps(bot)) - return false; - - // Static constants - static constexpr float MAX_HEIGHT_DIFF = 20.0f; - static constexpr float SAFE_HEIGHT = 371.16473f; - static constexpr float TELEPORT_HEIGHT = 366.16473f; - static constexpr std::array KINETIC_BOMB_ENTRIES = {NPC_KINETIC_BOMB1, NPC_KINETIC_BOMB2, - NPC_KINETIC_BOMB3, NPC_KINETIC_BOMB4}; - - // Handle edge case where bot is too high - if (bot->GetPositionZ() > SAFE_HEIGHT) - { - bot->TeleportTo(bot->GetMapId(), bot->GetPositionX(), bot->GetPositionY(), TELEPORT_HEIGHT, - bot->GetOrientation()); - } - - // Check current target if valid - if (Unit* currentTarget = AI_VALUE(Unit*, "current target")) - { - if (currentTarget->IsAlive() && std::find(KINETIC_BOMB_ENTRIES.begin(), KINETIC_BOMB_ENTRIES.end(), - currentTarget->GetEntry()) != KINETIC_BOMB_ENTRIES.end()) - { - const float heightDiff = currentTarget->GetPositionZ() - 361.18222f; - if (heightDiff < MAX_HEIGHT_DIFF) - return false; // Continue current attack - } - } - - // Find the best kinetic bomb to attack - if (Unit* bestBomb = FindOptimalKineticBomb()) - { - bot->SetTarget(bestBomb->GetGUID()); - bot->SetFacingToObject(bestBomb); - Attack(bestBomb); - } - - return false; -} - -Unit* IccBpcKineticBombAction::FindOptimalKineticBomb() -{ - static constexpr std::array KINETIC_BOMB_ENTRIES = {NPC_KINETIC_BOMB1, NPC_KINETIC_BOMB2, - NPC_KINETIC_BOMB3, NPC_KINETIC_BOMB4}; - - const GuidVector targets = AI_VALUE(GuidVector, "possible targets"); - if (targets.empty()) - return nullptr; - - const float botZ = 361.18222f; - Group* group = bot->GetGroup(); - - // Gather all valid kinetic bombs - std::vector kineticBombs; - for (auto const& guid : targets) - { - Unit* unit = botAI->GetUnit(guid); - if (!unit || !unit->IsAlive()) - continue; - if (std::find(KINETIC_BOMB_ENTRIES.begin(), KINETIC_BOMB_ENTRIES.end(), unit->GetEntry()) == - KINETIC_BOMB_ENTRIES.end()) - continue; - kineticBombs.push_back(unit); - } - - if (kineticBombs.empty()) - return nullptr; - - // Sort bombs by Z ascending (lowest first), then by heightDiff ascending (closest to ground) - std::sort(kineticBombs.begin(), kineticBombs.end(), - [botZ](Unit* a, Unit* b) - { - if (a->GetPositionZ() != b->GetPositionZ()) - return a->GetPositionZ() < b->GetPositionZ(); - return std::abs(a->GetPositionZ() - botZ) < std::abs(b->GetPositionZ() - botZ); - }); - - // Assign each ranged DPS to a unique bomb (lowest Z first) - std::vector rangedDps; - if (group) - { - for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next()) - { - Player* member = itr->GetSource(); - if (member && member->IsAlive() && botAI->IsRangedDps(member)) - rangedDps.push_back(member); - } - // Sort by GUID for deterministic assignment - std::sort(rangedDps.begin(), rangedDps.end(), [](Player* a, Player* b) { return a->GetGUID() < b->GetGUID(); }); - } - else - { - rangedDps.push_back(bot); - } - - // Find this bot's index among ranged DPS - auto it = std::find(rangedDps.begin(), rangedDps.end(), bot); - if (it == rangedDps.end()) - return nullptr; - size_t botIndex = std::distance(rangedDps.begin(), it); - - // Assign bombs in order, skip bombs already handled by other ranged DPS - size_t bombCount = kineticBombs.size(); - for (size_t i = 0, assigned = 0; i < bombCount; ++i) - { - Unit* bomb = kineticBombs[i]; - // Check if bomb is already handled by another ranged DPS closer than this bot - if (IsBombAlreadyHandled(bomb, group)) - continue; - if (assigned == botIndex) - return bomb; - ++assigned; - } - - // Fallback: pick the lowest bomb not already handled - for (Unit* bomb : kineticBombs) - { - if (!IsBombAlreadyHandled(bomb, group)) - return bomb; - } - - return nullptr; -} - -bool IccBpcKineticBombAction::IsBombAlreadyHandled(Unit* bomb, Group* group) -{ - if (!group) - return false; - - for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next()) - { - Player* member = itr->GetSource(); - if (!member || member == bot || !member->IsAlive() || !botAI->IsRangedDps(member)) - continue; - - if (member->GetTarget() == bomb->GetGUID() && member->GetDistance(bomb) < bot->GetDistance(bomb)) - return true; - } - - return false; -} - -bool IccBpcBallOfFlameAction::Execute(Event /*event*/) -{ - Unit* boss = AI_VALUE2(Unit*, "find target", "prince taldaram"); - if (!boss) - return false; - - Unit* flame1 = bot->FindNearestCreature(NPC_BALL_OF_FLAME, 100.0f); - Unit* flame2 = bot->FindNearestCreature(NPC_BALL_OF_INFERNO_FLAME, 100.0f); - - bool ballOfFlame = flame1 && (flame1->GetVictim() == bot); - bool infernoFlame = flame2 && (flame2->GetVictim() == bot); - - if (flame2 && (flame2->GetDistance2d(boss) > 2.0f) && !(flame2->GetDistance2d(boss) > 10.0f) && !infernoFlame && - bot->getClass() != CLASS_HUNTER) - { - if (!botAI->IsTank(bot) && !(flame2->GetVictim() == bot)) - { - float targetX = flame2->GetPositionX(); - float targetY = flame2->GetPositionY(); - float currentX = bot->GetPositionX(); - float currentY = bot->GetPositionY(); - - // Calculate direction vector - float dx = targetX - currentX; - float dy = targetY - currentY; - float distance = sqrt(dx * dx + dy * dy); - - // Normalize and scale to 5 units (or remaining distance if less than 5) - float step = std::min(5.0f, distance); - if (distance > 0.1) - { - dx = dx / distance * step; - dy = dy / distance * step; - } - - // Calculate intermediate position - float newX = currentX + dx; - float newY = currentY + dy; - - MoveTo(flame2->GetMapId(), newX, newY, bot->GetPositionZ(), false, false, false, true, - MovementPriority::MOVEMENT_COMBAT); - } - } - - // If victim of ball of flame, keep at least 15f from other party members - if (ballOfFlame || infernoFlame) - { - const float SAFE_DIST = 15.0f; - GuidVector members = AI_VALUE(GuidVector, "group members"); - for (auto const& memberGuid : members) - { - Unit* member = botAI->GetUnit(memberGuid); - if (!member || !member->IsAlive() || member == bot) - continue; - float dist = bot->GetExactDist2d(member); - if (dist < SAFE_DIST) - { - // Move away from this member - float dx = bot->GetPositionX() - member->GetPositionX(); - float dy = bot->GetPositionY() - member->GetPositionY(); - float len = std::sqrt(dx * dx + dy * dy); - if (len < 0.01f) - continue; - dx /= len; - dy /= len; - float moveX = bot->GetPositionX() + dx * (SAFE_DIST - dist + 1.0f); - float moveY = bot->GetPositionY() + dy * (SAFE_DIST - dist + 1.0f); - float moveZ = bot->GetPositionZ(); - if (bot->IsWithinLOS(moveX, moveY, moveZ)) - { - MoveTo(bot->GetMapId(), moveX, moveY, moveZ, false, false, false, true, - MovementPriority::MOVEMENT_FORCED); - } - } - } - } - - return false; -} - -// Blood Queen Lana'thel -bool IccBqlGroupPositionAction::Execute(Event /*event*/) -{ - Unit* boss = AI_VALUE2(Unit*, "find target", "blood-queen lana'thel"); - if (!boss) - return false; - - Aura* frenzyAura = botAI->GetAura("Frenzied Bloodthirst", bot); - Aura* shadowAura = botAI->GetAura("Swarming Shadows", bot); - bool isTank = botAI->IsTank(bot); - // Handle tank positioning - if (isTank && HandleTankPosition(boss, frenzyAura, shadowAura)) - return true; - - // Handle swarming shadows movement - if (shadowAura && HandleShadowsMovement()) - return true; - - // Handle group positioning - if (!frenzyAura && !shadowAura && HandleGroupPosition(boss, frenzyAura, shadowAura)) - return true; - - return false; -} - -bool IccBqlGroupPositionAction::HandleTankPosition(Unit* boss, Aura* frenzyAura, Aura* shadowAura) -{ - if (frenzyAura || shadowAura) - return false; - - // Main tank positioning - if (botAI->IsMainTank(bot) && botAI->HasAggro(boss)) - { - if (bot->GetExactDist2d(ICC_BQL_TANK_POSITION) > 3.0f) - { - MoveTo(bot->GetMapId(), ICC_BQL_TANK_POSITION.GetPositionX(), ICC_BQL_TANK_POSITION.GetPositionY(), - ICC_BQL_TANK_POSITION.GetPositionZ(), false, false, false, true, - MovementPriority::MOVEMENT_COMBAT); - } - } - - // Assist tank positioning - if (botAI->IsAssistTank(bot) && !botAI->GetAura("Blood Mirror", bot)) - { - if (Unit* mainTank = AI_VALUE(Unit*, "main tank")) - { - MoveTo(bot->GetMapId(), mainTank->GetPositionX(), mainTank->GetPositionY(), mainTank->GetPositionZ(), - false, false, false, true, MovementPriority::MOVEMENT_COMBAT); - } - } - - return false; -} - -bool IccBqlGroupPositionAction::HandleShadowsMovement() -{ - const float SAFE_SHADOW_DIST = 4.0f; - const float ARC_STEP = 0.05f; - const float CURVE_SPACING = 15.0f; - const int MAX_CURVES = 3; - const float maxClosestDist = botAI->IsMelee(bot) ? 25.0f : 20.0f; - const Position& center = ICC_BQL_CENTER_POSITION; - const float OUTER_CURVE_PREFERENCE = 200.0f; // Strong preference for outer curves - const float CURVE_SWITCH_PENALTY = 50.0f; // Penalty for switching curves - const float DISTANCE_PENALTY_FACTOR = 100.0f; // Penalty per yard moved from current position - const float MAX_CURVE_JUMP_DIST = 5.0f; // Maximum distance for jumping between curves - - // Track current curve to avoid unnecessary switching - static std::map botCurrentCurve; - int currentCurve = botCurrentCurve.count(bot->GetGUID()) ? botCurrentCurve[bot->GetGUID()] : 0; - - // Find closest wall path - Position lwall[4] = {ICC_BQL_LWALL1_POSITION, AdjustControlPoint(ICC_BQL_LWALL2_POSITION, center, 1.30f), - AdjustControlPoint(ICC_BQL_LWALL3_POSITION, center, 1.30f), ICC_BQL_LRWALL4_POSITION}; - Position rwall[4] = {ICC_BQL_RWALL1_POSITION, AdjustControlPoint(ICC_BQL_RWALL2_POSITION, center, 1.30f), - AdjustControlPoint(ICC_BQL_RWALL3_POSITION, center, 1.30f), ICC_BQL_LRWALL4_POSITION}; - Position* basePath = (bot->GetExactDist2d(lwall[0]) < bot->GetExactDist2d(rwall[0])) ? lwall : rwall; - - // Find all swarming shadows - GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); - Unit* shadows[100]{}; // Reasonable max estimate - int shadowCount = 0; - for (int i = 0; i < npcs.size() && shadowCount < 100; i++) - { - Unit* unit = botAI->GetUnit(npcs[i]); - if (unit && unit->IsAlive() && unit->GetEntry() == NPC_SWARMING_SHADOWS) - shadows[shadowCount++] = unit; - } - - // Helper lambda to check if a position is inside a shadow - auto IsPositionInShadow = [&](const Position& pos) -> bool - { - for (int i = 0; i < shadowCount; ++i) - { - if (pos.GetExactDist2d(shadows[i]) < SAFE_SHADOW_DIST) - return true; - } - return false; - }; - - // If bot is at the 4th position (end of the wall), move towards 3rd position or center to avoid getting stuck - float distToL4 = bot->GetExactDist2d(lwall[3]); - float distToR4 = bot->GetExactDist2d(rwall[3]); - const float STUCK_DIST = 2.0f; // within 2 yards is considered stuck at the end - - if (distToL4 < STUCK_DIST || distToR4 < STUCK_DIST) - { - // Move towards 3rd position of the same wall, or towards center if blocked - Position target; - if (distToL4 < distToR4) - { - target = lwall[2]; - } - else - { - target = rwall[2]; - } - - float tx = target.GetPositionX(); - float ty = target.GetPositionY(); - float tz = target.GetPositionZ(); - bot->UpdateAllowedPositionZ(tx, ty, tz); - if (!bot->IsWithinLOS(tx, ty, tz) || IsPositionInShadow(Position(tx, ty, tz))) - { - tx = center.GetPositionX(); - ty = center.GetPositionY(); - tz = center.GetPositionZ(); - } - - if (bot->GetExactDist2d(tx, ty) > 1.0f) - { - MoveTo(bot->GetMapId(), tx, ty, tz, false, false, false, true, MovementPriority::MOVEMENT_FORCED, - true, false); - } - return false; - } - - CurveInfo bestCurve; - bestCurve.foundSafe = false; - bestCurve.score = FLT_MAX; - bool foundCurve = false; - - // Keep track of information about all curves for possible fallback - CurveInfo curveInfos[MAX_CURVES]; - for (int i = 0; i < MAX_CURVES; i++) - { - curveInfos[i].foundSafe = false; - curveInfos[i].score = FLT_MAX; - } - - // Evaluate all curves starting from outermost (lowest index) - for (int curveIdx = 0; curveIdx < MAX_CURVES; curveIdx++) - { - float curveShrink = float(curveIdx) * CURVE_SPACING; - float shrinkFactor = 1.30f - (curveShrink / 30.0f); - if (shrinkFactor < 1.0f) - shrinkFactor = 1.0f; - - Position path[4] = {basePath[0], AdjustControlPoint(basePath[1], center, shrinkFactor / 1.30f), - AdjustControlPoint(basePath[2], center, shrinkFactor / 1.30f), basePath[3]}; - - // Find closest point on curve - float minDist = 9999.0f; - float t_closest = 0.0f; - Position closestPoint = path[0]; - - for (float t = 0.0f; t <= 1.0f; t += ARC_STEP) - { - Position pt = CalculateBezierPoint(t, path); - float dist = bot->GetExactDist2d(pt); - if (dist < minDist) - { - minDist = dist; - t_closest = t; - closestPoint = pt; - } - } - - // Check if the closest point is safe - bool closestIsSafe = !IsPositionInShadow(closestPoint); - - // Find closest safe point by searching in both directions from closest point - Position safeMoveTarget = closestPoint; - bool foundSafe = closestIsSafe; - - // Only search for safe spots if the closest point isn't already safe - if (!closestIsSafe) - { - // Find the nearest safe point along the curve, not by direct distance - // but by distance along the curve from the closest point - - // Search forward on curve from closest point - float forwardT = -1.0f; - Position forwardPt; - for (float t = t_closest + ARC_STEP; t <= 1.0f; t += ARC_STEP) - { - Position pt = CalculateBezierPoint(t, path); - if (!IsPositionInShadow(pt)) - { - forwardT = t; - forwardPt = pt; - break; - } - } - - // Search backward on curve from closest point - float backwardT = -1.0f; - Position backwardPt; - for (float t = t_closest - ARC_STEP; t >= 0.0f; t -= ARC_STEP) - { - Position pt = CalculateBezierPoint(t, path); - if (!IsPositionInShadow(pt)) - { - backwardT = t; - backwardPt = pt; - break; - } - } - - // Choose the closest safe point based on curve distance, not direct distance - if (forwardT >= 0 && backwardT >= 0) - { - // Both directions have safe points, choose the closer one by curve distance - if (std::abs(forwardT - t_closest) < std::abs(backwardT - t_closest)) - { - safeMoveTarget = forwardPt; - foundSafe = true; - } - else - { - safeMoveTarget = backwardPt; - foundSafe = true; - } - } - else if (forwardT >= 0) - { - safeMoveTarget = forwardPt; - foundSafe = true; - } - else if (backwardT >= 0) - { - safeMoveTarget = backwardPt; - foundSafe = true; - } - } - - // Score this curve - float distancePenalty = 0.0f; - float score = 0.0f; - - if (foundSafe) - { - // If we found a safe point, penalize based on travel distance along the curve to reach it - float safeDist = bot->GetExactDist2d(safeMoveTarget); - - // Add distance penalty based on how far we need to move along the curve - distancePenalty = safeDist * (1.0f / DISTANCE_PENALTY_FACTOR); - score = safeDist + distancePenalty; - } - else - { - // No safe point found, assign a high score - distancePenalty = minDist * (1.0f / DISTANCE_PENALTY_FACTOR); - score = minDist + distancePenalty + 1000.0f; // Penalty for unsafe position - } - - // Apply strong penalty for curves that are too far - if (minDist > maxClosestDist) - score += 500.0f; - - // Apply penalty for unsafe curves - if (!foundSafe) - score += 1000.0f; - - // Apply curve index preference (strongly prefer outer curves) - score += curveIdx * OUTER_CURVE_PREFERENCE; - - // Apply curve switching penalty - if (curveIdx != currentCurve && currentCurve != 0) - score += CURVE_SWITCH_PENALTY; - - // MORE IMPORTANT: Apply additional curve switching penalty if the bot is far away - // from the target curve (prevent jumping between curves when far away) - if (curveIdx != currentCurve && minDist > MAX_CURVE_JUMP_DIST) - score += 2000.0f; // Strong penalty to prevent jumping between curves - - // Store this curve's info - curveInfos[curveIdx].moveTarget = foundSafe ? safeMoveTarget : closestPoint; - curveInfos[curveIdx].foundSafe = foundSafe; - curveInfos[curveIdx].minDist = minDist; - curveInfos[curveIdx].curveIdx = curveIdx; - curveInfos[curveIdx].score = score; - curveInfos[curveIdx].closestPoint = closestPoint; - curveInfos[curveIdx].t_closest = t_closest; - - // Only update if this curve is better than our current best - if (!foundCurve || score < bestCurve.score) - { - bestCurve = curveInfos[curveIdx]; - foundCurve = true; - } - } - - // Fallback: If we're trying to switch to a far curve and we're not near any curve, - // find and use the closest curve instead of making a direct beeline - if (foundCurve && bestCurve.minDist > MAX_CURVE_JUMP_DIST && bestCurve.curveIdx != currentCurve) - { - // Look for the closest curve first - float closestDist = FLT_MAX; - int closestCurveIdx = -1; - - for (int i = 0; i < MAX_CURVES; i++) - { - if (curveInfos[i].minDist < closestDist) - { - closestDist = curveInfos[i].minDist; - closestCurveIdx = i; - } - } - - // If we found a closer curve, use that instead - if (closestCurveIdx >= 0 && closestCurveIdx != bestCurve.curveIdx) - { - bestCurve = curveInfos[closestCurveIdx]; - } - } - - // Remember the selected curve for next time - if (foundCurve) - { - botCurrentCurve[bot->GetGUID()] = bestCurve.curveIdx; - } - - // Create a move plan to guide the bot along the curve if necessary - if (foundCurve && bot->GetExactDist2d(bestCurve.moveTarget) > 1.0f) - { - // Final check: ensure we're not moving into a shadow - if (!IsPositionInShadow(bestCurve.moveTarget)) - { - // Get the curve - float curveShrink = float(bestCurve.curveIdx) * CURVE_SPACING; - float shrinkFactor = 1.30f - (curveShrink / 30.0f); - if (shrinkFactor < 1.0f) - shrinkFactor = 1.0f; - - Position path[4] = {basePath[0], AdjustControlPoint(basePath[1], center, shrinkFactor / 1.30f), - AdjustControlPoint(basePath[2], center, shrinkFactor / 1.30f), basePath[3]}; - - // CRITICAL CHANGE: First check if we need to move to the curve - float distToClosestPoint = bot->GetExactDist2d(bestCurve.closestPoint); - - // If we're not on the curve yet, first move to the closest point on the curve - if (distToClosestPoint > 2.0f) - { - botAI->Reset(); - return MoveTo(bot->GetMapId(), bestCurve.closestPoint.GetPositionX(), - bestCurve.closestPoint.GetPositionY(), bestCurve.closestPoint.GetPositionZ(), false, - false, false, true, MovementPriority::MOVEMENT_FORCED, true, false); - } - - // Now we know we're on or very close to the curve, so we'll follow it properly - - // Find target point on curve (t_target parameter) - float t_target = 0.0f; - float targetMinDist = 9999.0f; - - for (float t = 0.0f; t <= 1.0f; t += ARC_STEP) - { - Position pt = CalculateBezierPoint(t, path); - float dist = bestCurve.moveTarget.GetExactDist2d(pt); - if (dist < targetMinDist) - { - targetMinDist = dist; - t_target = t; - } - } - - // Find an intermediate point along the curve between closest and target - float t_step = (t_target > bestCurve.t_closest) ? ARC_STEP : -ARC_STEP; - float t_intermediate = bestCurve.t_closest + t_step; - Position intermediateTarget; - bool foundValidIntermediate = false; - - // Limit the distance we move along the curve in one step - const float MAX_CURVE_MOVEMENT = 7.0f; // Max yards to move along curve - float curveDistanceMoved = 0.0f; - Position lastPos = bestCurve.closestPoint; - - while ((t_step > 0 && t_intermediate <= t_target) || (t_step < 0 && t_intermediate >= t_target)) - { - Position pt = CalculateBezierPoint(t_intermediate, path); - - // Check if this point is safe - if (!IsPositionInShadow(pt)) - { - // Calculate distance moved along curve so far - curveDistanceMoved += lastPos.GetExactDist2d(pt); - lastPos = pt; - - // If we've moved the maximum allowed distance, use this position - if (curveDistanceMoved >= MAX_CURVE_MOVEMENT) - { - intermediateTarget = pt; - foundValidIntermediate = true; - break; - } - - // Otherwise, continue moving along the curve - intermediateTarget = pt; - foundValidIntermediate = true; - } - else - { - // We've hit a shadow, stop here - break; - } - - t_intermediate += t_step; - } - - // If we found a valid intermediate point, use it - if (foundValidIntermediate) - { - botAI->Reset(); - MoveTo(bot->GetMapId(), intermediateTarget.GetPositionX(), intermediateTarget.GetPositionY(), - intermediateTarget.GetPositionZ(), false, false, false, true, - MovementPriority::MOVEMENT_FORCED, true, false); - } - - botAI->Reset(); - // Fallback to direct movement to the target point on the curve - MoveTo(bot->GetMapId(), bestCurve.moveTarget.GetPositionX(), bestCurve.moveTarget.GetPositionY(), - bestCurve.moveTarget.GetPositionZ(), false, false, false, true, - MovementPriority::MOVEMENT_FORCED, true, false); - } - } - - return false; -} - -Position IccBqlGroupPositionAction::AdjustControlPoint(const Position& wall, const Position& center, float factor) -{ - float dx = wall.GetPositionX() - center.GetPositionX(); - float dy = wall.GetPositionY() - center.GetPositionY(); - float dz = wall.GetPositionZ() - center.GetPositionZ(); - return Position(center.GetPositionX() + dx * factor, center.GetPositionY() + dy * factor, - center.GetPositionZ() + dz * factor); -} - -Position IccBqlGroupPositionAction::CalculateBezierPoint(float t, const Position path[4]) -{ - float omt = 1 - t; - float omt2 = omt * omt; - float omt3 = omt2 * omt; - float t2 = t * t; - float t3 = t2 * t; - - float x = omt3 * path[0].GetPositionX() + 3 * omt2 * t * path[1].GetPositionX() + - 3 * omt * t2 * path[2].GetPositionX() + t3 * path[3].GetPositionX(); - - float y = omt3 * path[0].GetPositionY() + 3 * omt2 * t * path[1].GetPositionY() + - 3 * omt * t2 * path[2].GetPositionY() + t3 * path[3].GetPositionY(); - - float z = omt3 * path[0].GetPositionZ() + 3 * omt2 * t * path[1].GetPositionZ() + - 3 * omt * t2 * path[2].GetPositionZ() + t3 * path[3].GetPositionZ(); - - return Position(x, y, z); -} - -bool IccBqlGroupPositionAction::HandleGroupPosition(Unit* boss, Aura* frenzyAura, Aura* shadowAura) -{ - if (frenzyAura || shadowAura) - return false; - - GuidVector members = AI_VALUE(GuidVector, "group members"); - bool isRanged = botAI->IsRanged(bot); - bool isMelee = botAI->IsMelee(bot); - - if (isRanged && bot->GetExactDist2d(boss->GetPositionX(), boss->GetPositionY()) > 35.0f) - MoveTo(boss, 5.0f, MovementPriority::MOVEMENT_FORCED); - - if ((boss->GetExactDist2d(ICC_BQL_TANK_POSITION.GetPositionX(), ICC_BQL_TANK_POSITION.GetPositionY()) > 10.0f) && - isRanged && !((boss->GetPositionZ() - ICC_BQL_CENTER_POSITION.GetPositionZ()) > 5.0f) && - (bot->GetExactDist2d(ICC_BQL_CENTER_POSITION.GetPositionX(), ICC_BQL_CENTER_POSITION.GetPositionY()) > 10.0f)) - MoveTo(bot->GetMapId(), ICC_BQL_CENTER_POSITION.GetPositionX(), ICC_BQL_CENTER_POSITION.GetPositionY(), - ICC_BQL_CENTER_POSITION.GetPositionZ(), false, false, false, true, - MovementPriority::MOVEMENT_COMBAT, true, false); - - // --- Ranged bots wall assignment logic --- - if (isRanged) - { - // Gather all ranged and healers, sort by GUID for deterministic assignment - std::vector rangedBots; - std::vector healers; - for (auto const& guid : members) - { - Unit* member = botAI->GetUnit(guid); - if (!member || !member->IsAlive()) - continue; - Player* player = member->ToPlayer(); - if (!player) - continue; - if (botAI->IsRanged(player)) - rangedBots.push_back(player); - if (botAI->IsHeal(player)) - healers.push_back(player); - } - // Remove duplicates (healer can be ranged) - std::sort(rangedBots.begin(), rangedBots.end(), - [](Player* a, Player* b) { return a->GetGUID() < b->GetGUID(); }); - std::sort(healers.begin(), healers.end(), [](Player* a, Player* b) { return a->GetGUID() < b->GetGUID(); }); - - // Assign at least one healer to each side, then balance the rest - std::vector leftSide, rightSide; - Position leftPos = ICC_BQL_LWALL2_POSITION; - Position rightPos = ICC_BQL_RWALL2_POSITION; - - // Assign healers first - if (!healers.empty()) - { - leftSide.push_back(healers[0]); - if (healers.size() > 1) - rightSide.push_back(healers[1]); - } - // If only one healer, assign to left, right will be filled by ranged DPS - - // Remove assigned healers from rangedBots - for (Player* h : leftSide) - rangedBots.erase(std::remove(rangedBots.begin(), rangedBots.end(), h), rangedBots.end()); - for (Player* h : rightSide) - rangedBots.erase(std::remove(rangedBots.begin(), rangedBots.end(), h), rangedBots.end()); - - // Distribute remaining ranged evenly - size_t leftCount = leftSide.size(); - size_t rightCount = rightSide.size(); - for (Player* p : rangedBots) - { - if (leftCount <= rightCount) - { - leftSide.push_back(p); - leftCount++; - } - else - { - rightSide.push_back(p); - rightCount++; - } - } - - // Determine which side this bot is assigned to - bool isLeft = std::find(leftSide.begin(), leftSide.end(), bot) != leftSide.end(); - bool isRight = std::find(rightSide.begin(), rightSide.end(), bot) != rightSide.end(); - - // Move to assigned wall position if not already close - const float MAX_WALL_DIST = 30.0f; - const float MOVE_INCREMENT = 2.0f; - const float MAX_MOVE_DISTANCE = 7.0f; - const float SAFE_SPACING_RADIUS = 7.0f; - const float MIN_CENTER_DISTANCE = 10.0f; - - Position targetWall = isLeft ? leftPos : (isRight ? rightPos : Position()); - if (isLeft || isRight) - { - float distToWall = bot->GetExactDist2d(targetWall.GetPositionX(), targetWall.GetPositionY()); - if (distToWall > MAX_WALL_DIST) - { - // Move in increments toward wall - float dx = targetWall.GetPositionX() - bot->GetPositionX(); - float dy = targetWall.GetPositionY() - bot->GetPositionY(); - float len = std::sqrt(dx * dx + dy * dy); - if (len > 0.001f) - { - dx /= len; - dy /= len; - float moveDist = std::min(MOVE_INCREMENT, distToWall); - float targetX = bot->GetPositionX() + dx * moveDist; - float targetY = bot->GetPositionY() + dy * moveDist; - float targetZ = bot->GetPositionZ(); - if (!bot->IsWithinLOS(targetX, targetY, targetZ)) - { - targetX = bot->GetPositionX() + dx * (moveDist * 0.5f); - targetY = bot->GetPositionY() + dy * (moveDist * 0.5f); - } - botAI->Reset(); - MoveTo(bot->GetMapId(), targetX, targetY, targetZ, false, false, false, true, - MovementPriority::MOVEMENT_COMBAT, true, false); - } - } - // Spread from other assigned members on the same side and from swarming shadows - float totalX = 0.0f, totalY = 0.0f; - int nearbyCount = 0; - const std::vector& mySide = isLeft ? leftSide : rightSide; - for (Player* member : mySide) - { - if (!member || !member->IsAlive() || member == bot) - continue; - float distance = bot->GetExactDist2d(member); - if (distance < SAFE_SPACING_RADIUS) - { - float dx = bot->GetPositionX() - member->GetPositionX(); - float dy = bot->GetPositionY() - member->GetPositionY(); - float weight = (SAFE_SPACING_RADIUS - distance) / SAFE_SPACING_RADIUS; - totalX += dx * weight; - totalY += dy * weight; - nearbyCount++; - } - } - // Also spread from swarming shadows - GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); - for (auto const& npcGuid : npcs) - { - Unit* unit = botAI->GetUnit(npcGuid); - if (unit && unit->IsAlive() && unit->GetEntry() == NPC_SWARMING_SHADOWS) - { - float distance = bot->GetExactDist2d(unit); - if (distance < SAFE_SPACING_RADIUS) - { - float dx = bot->GetPositionX() - unit->GetPositionX(); - float dy = bot->GetPositionY() - unit->GetPositionY(); - float weight = (SAFE_SPACING_RADIUS - distance) / SAFE_SPACING_RADIUS; - totalX += dx * weight; - totalY += dy * weight; - nearbyCount++; - } - } - } - if (nearbyCount > 0) - { - float magnitude = sqrt(totalX * totalX + totalY * totalY); - if (magnitude > 0.001f) - { - totalX /= magnitude; - totalY /= magnitude; - float moveDistance = std::min(MOVE_INCREMENT, MAX_MOVE_DISTANCE); - float targetX = bot->GetPositionX() + totalX * moveDistance; - float targetY = bot->GetPositionY() + totalY * moveDistance; - float targetZ = bot->GetPositionZ(); - if (!bot->IsWithinLOS(targetX, targetY, targetZ)) - { - targetX = bot->GetPositionX() + totalX * (moveDistance * 0.5f); - targetY = bot->GetPositionY() + totalY * (moveDistance * 0.5f); - } - botAI->Reset(); - MoveTo(bot->GetMapId(), targetX, targetY, targetZ, false, false, false, true, - MovementPriority::MOVEMENT_COMBAT, true, false); - } - } - // Maintain minimum distance from center position (if too close to center, move out) - float centerX = ICC_BQL_CENTER_POSITION.GetPositionX(); - float centerY = ICC_BQL_CENTER_POSITION.GetPositionY(); - float centerDist = bot->GetDistance2d(centerX, centerY); - if (centerDist < MIN_CENTER_DISTANCE && !((boss->GetPositionZ() - bot->GetPositionZ()) > 5.0f)) - { - float dx = bot->GetPositionX() - centerX; - float dy = bot->GetPositionY() - centerY; - float dist = std::sqrt(dx * dx + dy * dy); - if (dist > 0.001f) - { - dx /= dist; - dy /= dist; - float moveDistance = std::min(MIN_CENTER_DISTANCE - centerDist + 1.0f, MAX_MOVE_DISTANCE); - float targetX = bot->GetPositionX() + dx * moveDistance; - float targetY = bot->GetPositionY() + dy * moveDistance; - float targetZ = bot->GetPositionZ(); - if (!bot->IsWithinLOS(targetX, targetY, targetZ)) - { - targetX = bot->GetPositionX() + dx * (moveDistance * 0.5f); - targetY = bot->GetPositionY() + dy * (moveDistance * 0.5f); - } - botAI->Reset(); - MoveTo(bot->GetMapId(), targetX, targetY, targetZ, false, false, false, true, - MovementPriority::MOVEMENT_COMBAT, true, false); - } - } - } - } - - if (isMelee && ((boss->GetPositionZ() - ICC_BQL_CENTER_POSITION.GetPositionZ()) > 5.0f)) - { - const float SAFE_SPACING_RADIUS = 7.0f; - const float MOVE_INCREMENT = 2.0f; - const float MAX_MOVE_DISTANCE = 7.0f; - - float totalX = 0.0f; - float totalY = 0.0f; - int nearbyCount = 0; - - // Find all swarming shadows - GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); - std::vector swarmingShadows; - for (int i = 0; i < npcs.size(); ++i) - { - Unit* unit = botAI->GetUnit(npcs[i]); - if (unit && unit->IsAlive() && unit->GetEntry() == NPC_SWARMING_SHADOWS) - swarmingShadows.push_back(unit); - } - - for (int i = 0; i < members.size(); i++) - { - Unit* member = botAI->GetUnit(members[i]); - if (!member || !member->IsAlive() || member == bot || botAI->GetAura("Frenzied Bloodthirst", member) || - botAI->GetAura("Uncontrollable Frenzy", member)) - continue; - - float distance = bot->GetExactDist2d(member); - if (distance < SAFE_SPACING_RADIUS) - { - float dx = bot->GetPositionX() - member->GetPositionX(); - float dy = bot->GetPositionY() - member->GetPositionY(); - float weight = (SAFE_SPACING_RADIUS - distance) / SAFE_SPACING_RADIUS; - totalX += dx * weight; - totalY += dy * weight; - nearbyCount++; - } - } - - // Also spread from swarming shadows - for (Unit* shadow : swarmingShadows) - { - float distance = bot->GetExactDist2d(shadow); - if (distance < SAFE_SPACING_RADIUS) - { - float dx = bot->GetPositionX() - shadow->GetPositionX(); - float dy = bot->GetPositionY() - shadow->GetPositionY(); - float weight = (SAFE_SPACING_RADIUS - distance) / SAFE_SPACING_RADIUS; - totalX += dx * weight; - totalY += dy * weight; - nearbyCount++; - } - } - - if (nearbyCount > 0) - { - float magnitude = sqrt(totalX * totalX + totalY * totalY); - if (magnitude > 0.001f) - { - totalX /= magnitude; - totalY /= magnitude; - float moveDistance = MOVE_INCREMENT < MAX_MOVE_DISTANCE ? MOVE_INCREMENT : MAX_MOVE_DISTANCE; - float targetX = bot->GetPositionX() + totalX * moveDistance; - float targetY = bot->GetPositionY() + totalY * moveDistance; - float targetZ = bot->GetPositionZ(); - - if (!bot->IsWithinLOS(targetX, targetY, targetZ)) - { - targetX = bot->GetPositionX() + totalX * (moveDistance * 0.5f); - targetY = bot->GetPositionY() + totalY * (moveDistance * 0.5f); - } - - MoveTo(bot->GetMapId(), targetX, targetY, targetZ, false, false, false, true, - MovementPriority::MOVEMENT_COMBAT, true, false); - } - } - return true; - } - - return false; -} - -bool IccBqlPactOfDarkfallenAction::Execute(Event /*event*/) -{ - // Check if bot has Pact of the Darkfallen - if (!botAI->GetAura("Pact of the Darkfallen", bot)) - return false; - Group* group = bot->GetGroup(); - if (!group) - return false; - - // Find other players with Pact of the Darkfallen - Player* tankWithAura = nullptr; - std::vector playersWithAura; - - for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next()) - { - Player* member = itr->GetSource(); - if (!member || member == bot) - continue; - if (botAI->GetAura("Pact of the Darkfallen", member)) - { - playersWithAura.push_back(member); - if (botAI->IsTank(member)) - tankWithAura = member; - } - } - - if (playersWithAura.empty()) - return false; - - // Determine target position - Position targetPos; - if (tankWithAura) - { - // If there's a tank with aura, everyone moves to the tank (including the tank itself for center positioning) - if (botAI->IsTank(bot)) - { - // If current bot is the tank, stay put or move slightly for better positioning - targetPos.Relocate(bot); - } - else - { - // Non-tank bots move to the tank - targetPos.Relocate(tankWithAura); - } - } - else if (playersWithAura.size() >= 2) - { - // Calculate center position of all players with aura (including bot) - CalculateCenterPosition(targetPos, playersWithAura); - } - else if (playersWithAura.size() == 1) - { - // Move to the single other player with aura - targetPos.Relocate(playersWithAura[0]); - } - else - { - // No valid movement case found - return true; - } - - // Move to target position if needed - return MoveToTargetPosition(targetPos, playersWithAura.size() + 1); // +1 to include the bot itself -} - -void IccBqlPactOfDarkfallenAction::CalculateCenterPosition(Position& targetPos, - const std::vector& playersWithAura) -{ - float sumX = bot->GetPositionX(); - float sumY = bot->GetPositionY(); - float sumZ = bot->GetPositionZ(); - - // Add positions of all other players with aura - for (Player* player : playersWithAura) - { - sumX += player->GetPositionX(); - sumY += player->GetPositionY(); - sumZ += player->GetPositionZ(); - } - - // Calculate average position (center) - int totalPlayers = playersWithAura.size() + 1; // +1 for the bot itself - targetPos.Relocate(sumX / totalPlayers, sumY / totalPlayers, sumZ / totalPlayers); -} - -bool IccBqlPactOfDarkfallenAction::MoveToTargetPosition(const Position& targetPos, int auraCount) -{ - const float POSITION_TOLERANCE = 0.1f; - float distance = bot->GetDistance(targetPos); - if (distance <= POSITION_TOLERANCE) - return true; - - // Calculate movement increment - float dx = targetPos.GetPositionX() - bot->GetPositionX(); - float dy = targetPos.GetPositionY() - bot->GetPositionY(); - float dz = targetPos.GetPositionZ() - bot->GetPositionZ(); - float len = sqrt(dx * dx + dy * dy); - - float moveX, moveY, moveZ; - if (len > 5.0f && auraCount <= 2) - { - dx /= len; - dy /= len; - moveX = bot->GetPositionX() + dx * 5.0f; - moveY = bot->GetPositionY() + dy * 5.0f; - moveZ = bot->GetPositionZ() + (dz / distance) * 5.0f; - } - else - { - moveX = targetPos.GetPositionX(); - moveY = targetPos.GetPositionY(); - moveZ = targetPos.GetPositionZ(); - } - - botAI->Reset(); - MoveTo(bot->GetMapId(), moveX, moveY, moveZ, false, false, false, true, MovementPriority::MOVEMENT_FORCED); - return false; -} - -bool IccBqlVampiricBiteAction::Execute(Event /*event*/) -{ - // Only act when bot has Frenzied Bloodthirst - if (!botAI->GetAura("Frenzied Bloodthirst", bot)) - return false; - - const float BITE_RANGE = 2.0f; - Group* group = bot->GetGroup(); - if (!group) - return false; - - // Find best target - Player* target = FindBestBiteTarget(group); - if (!target) - return false; - - // Handle movement or casting - float x = target->GetPositionX(); - float y = target->GetPositionY(); - float z = target->GetPositionZ(); - - if (bot->GetExactDist2d(target) > BITE_RANGE) - { - return MoveTowardsTarget(target); - } - else if (bot->IsWithinLOS(x, y, z)) - { - return CastVampiricBite(target); - } - - return false; -} - -Player* IccBqlVampiricBiteAction::FindBestBiteTarget(Group* group) -{ - std::set currentlyTargetedPlayers; - std::vector> dpsTargets; - std::vector> healTargets; - - // Get currently targeted players - for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next()) - { - Player* member = itr->GetSource(); - if (!member || !member->IsAlive() || member == bot) - continue; - - if (botAI->GetAura("Frenzied Bloodthirst", member) && member->GetTarget()) - { - currentlyTargetedPlayers.insert(member->GetTarget()); - } - } - - // Evaluate potential targets - for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next()) - { - Player* member = itr->GetSource(); - if (!member || member == bot || !member->IsAlive()) - continue; - - if (IsInvalidTarget(member) || currentlyTargetedPlayers.count(member->GetGUID())) - continue; - - float distance = bot->GetDistance(member); - if (botAI->IsDps(member)) - dpsTargets.push_back({member, distance}); - else if (botAI->IsHeal(member)) - healTargets.push_back({member, distance}); - } - - // Sort by distance - auto sortByDistance = [](auto const& a, auto const& b) { return a.second < b.second; }; - std::sort(dpsTargets.begin(), dpsTargets.end(), sortByDistance); - std::sort(healTargets.begin(), healTargets.end(), sortByDistance); - - // Return closest valid target - if (!dpsTargets.empty()) - return dpsTargets[0].first; - if (!healTargets.empty()) - return healTargets[0].first; - return nullptr; -} - -bool IccBqlVampiricBiteAction::IsInvalidTarget(Player* player) -{ - return botAI->GetAura("Frenzied Bloodthirst", player) || botAI->GetAura("Essence of the Blood Queen", player) || - botAI->GetAura("Uncontrollable Frenzy", player) || botAI->GetAura("Swarming Shadows", player) || - botAI->IsTank(player); -} - -bool IccBqlVampiricBiteAction::MoveTowardsTarget(Player* target) -{ - if (IsInvalidTarget(target) || !target->IsAlive()) - return false; - - float x = target->GetPositionX(); - float y = target->GetPositionY(); - float z = target->GetPositionZ(); - - if (!bot->IsWithinLOS(x, y, z)) - return false; - - float dx = x - bot->GetPositionX(); - float dy = y - bot->GetPositionY(); - float dz = z - bot->GetPositionZ(); - float len = sqrt(dx * dx + dy * dy); - - float moveX, moveY, moveZ; - if (len > 5.0f) - { - dx /= len; - dy /= len; - moveX = bot->GetPositionX() + dx * 5.0f; - moveY = bot->GetPositionY() + dy * 5.0f; - moveZ = bot->GetPositionZ() + (dz / len) * 5.0f; - } - else - { - moveX = x; - moveY = y; - moveZ = z; - } - - MoveTo(target->GetMapId(), moveX, moveY, moveZ, false, false, false, true, - MovementPriority::MOVEMENT_FORCED); - - return false; -} - -bool IccBqlVampiricBiteAction::CastVampiricBite(Player* target) -{ - if (IsInvalidTarget(target) || !target->IsAlive()) - return false; - - return botAI->CanCastSpell("Vampiric Bite", target) && botAI->CastSpell("Vampiric Bite", target); -} - -// Sister Svalna -bool IccValkyreSpearAction::Execute(Event /*event*/) -{ - // Find the nearest spear - Creature* spear = bot->FindNearestCreature(NPC_SPEAR, 100.0f); - if (!spear) - return false; - - // Move to the spear if not in range - if (!spear->IsWithinDistInMap(bot, INTERACTION_DISTANCE)) - return MoveTo(spear, INTERACTION_DISTANCE); - - // Remove shapeshift forms - botAI->RemoveShapeshift(); - - // Stop movement and click the spear - bot->GetMotionMaster()->Clear(); - bot->StopMoving(); - spear->HandleSpellClick(bot); - - // Dismount if mounted - WorldPacket emptyPacket; - bot->GetSession()->HandleCancelMountAuraOpcode(emptyPacket); - - return false; -} - -bool IccSisterSvalnaAction::Execute(Event /*event*/) -{ - Unit* svalna = AI_VALUE2(Unit*, "find target", "sister svalna"); - if (!svalna || !svalna->HasAura(SPELL_AETHER_SHIELD)) // Check for Aether Shield aura - return false; - - // Check if bot has the spear item - if (!botAI->HasItemInInventory(ITEM_SPEAR)) - return false; - - // Get all items from inventory - std::vector items = botAI->GetInventoryItems(); - for (Item* item : items) - { - if (item->GetEntry() == ITEM_SPEAR) // Spear ID - { - botAI->ImbueItem(item, svalna); // Use spear on Svalna - return false; - } - } - - return false; -} - -// VDW -bool IccValithriaGroupAction::Execute(Event /*event*/) -{ - // Helper lambda to find nearest creature of given entries - auto findNearestCreature = [this](std::initializer_list entries, float range) -> Creature* - { - for (uint32 entry : entries) - { - if (Creature* creature = bot->FindNearestCreature(entry, range)) - { - return creature; - } - } - return nullptr; - }; - - // Find portals and enemies - Creature* portal = findNearestCreature({NPC_DREAM_PORTAL, NPC_DREAM_PORTAL_PRE_EFFECT, NPC_NIGHTMARE_PORTAL, NPC_NIGHTMARE_PORTAL_PRE_EFFECT}, 100.0f); - - Creature* worm = bot->FindNearestCreature(NPC_ROT_WORM, 100.0f); - Creature* zombie = bot->FindNearestCreature(NPC_BLISTERING_ZOMBIE, 100.0f); - Creature* manaVoid = bot->FindNearestCreature(NPC_MANA_VOID, 100.0f); - - // Find column of frost units - GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); - std::vector columnOfFrost; - for (ObjectGuid guid : npcs) - { - if (Unit* unit = botAI->GetUnit(guid)) - { - if (unit->IsAlive() && unit->GetEntry() == NPC_COLUMN_OF_FROST) - { - columnOfFrost.push_back(unit); - } - } - } - - // Tank behavior - if (botAI->IsTank(bot)) - { - for (auto const& targetGuid : AI_VALUE(GuidVector, "possible targets")) - { - if (Unit* unit = botAI->GetUnit(targetGuid)) - { - if (unit->IsAlive() && - (unit->GetEntry() == NPC_GLUTTONOUS_ABOMINATION || unit->GetEntry() == NPC_ROT_WORM)) - { - // Skip if unit is already attacking any tank - if (Unit* victim = unit->GetVictim()) - { - if (victim->IsPlayer() && botAI->IsTank(static_cast(victim))) - { - continue; - } - } - - // Only attack if not already targeting us - if (unit->GetVictim() != bot) - { - bot->SetTarget(unit->GetGUID()); - bot->SetFacingToObject(unit); - Attack(unit); - } - } - } - } - } - - // Healer movement logic - if (botAI->IsHeal(bot) && bot->GetExactDist2d(ICC_VDW_HEAL_POSITION) > 30.0f && !portal) - return MoveTo(bot->GetMapId(), ICC_VDW_HEAL_POSITION.GetPositionX(), ICC_VDW_HEAL_POSITION.GetPositionY(), - ICC_VDW_HEAL_POSITION.GetPositionZ(), - false, false, false, false, MovementPriority::MOVEMENT_NORMAL); - - // Avoidance behaviors - if (manaVoid && bot->GetExactDist2d(manaVoid) < 10.0f && - !(botAI->GetAura("Twisted Nightmares", bot) || botAI->GetAura("Emerald Vigor", bot))) - { - botAI->Reset(); - FleePosition(manaVoid->GetPosition(), 11.0f, 250U); - } - - for (Unit* column : columnOfFrost) - { - if (column && bot->GetExactDist2d(column) < 7.0f) - { - botAI->Reset(); - FleePosition(column->GetPosition(), 8.0f, 250U); - } - } - - if (worm && worm->IsAlive() && worm->GetVictim() == bot && !botAI->IsTank(bot)) - { - botAI->Reset(); - FleePosition(worm->GetPosition(), 10.0f, 250U); - } - - if (zombie && zombie->IsAlive() && zombie->GetVictim() == bot && !botAI->IsTank(bot) && - bot->GetExactDist2d(zombie) < 20.0f) - { - botAI->Reset(); - FleePosition(zombie->GetPosition(), 21.0f, 250U); - } - - // Crowd control logic - if (zombie && !botAI->IsMainTank(bot) && !botAI->IsHeal(bot) && zombie->GetVictim() != bot) - { - switch (bot->getClass()) - { - case CLASS_MAGE: - if (!botAI->HasAura("Frost Nova", zombie)) - botAI->CastSpell("Frost Nova", zombie); - break; - case CLASS_DRUID: - if (!botAI->HasAura("Entangling Roots", zombie)) - botAI->CastSpell("Entangling Roots", zombie); - break; - case CLASS_PALADIN: - if (!botAI->HasAura("Hammer of Justice", zombie)) - botAI->CastSpell("Hammer of Justice", zombie); - break; - case CLASS_WARRIOR: - if (!botAI->HasAura("Hamstring", zombie)) - botAI->CastSpell("Hamstring", zombie); - break; - case CLASS_HUNTER: - if (!botAI->HasAura("Concussive Shot", zombie)) - botAI->CastSpell("Concussive Shot", zombie); - break; - case CLASS_ROGUE: - if (!botAI->HasAura("Kidney Shot", zombie)) - botAI->CastSpell("Kidney Shot", zombie); - break; - case CLASS_SHAMAN: - if (!botAI->HasAura("Frost Shock", zombie)) - botAI->CastSpell("Frost Shock", zombie); - break; - case CLASS_DEATH_KNIGHT: - if (!botAI->HasAura("Chains of Ice", zombie)) - botAI->CastSpell("Chains of Ice", zombie); - break; - default: - break; - } - } - - // Group assignment and movement logic - Difficulty diff = bot->GetRaidDifficulty(); - Group* group = bot->GetGroup(); - - if (group && (diff == RAID_DIFFICULTY_25MAN_NORMAL || diff == RAID_DIFFICULTY_25MAN_HEROIC)) - return Handle25ManGroupLogic(); - else - return Handle10ManGroupLogic(); -} - -bool IccValithriaGroupAction::MoveTowardsPosition(const Position& pos, float increment) -{ - float dx = pos.GetPositionX() - bot->GetPositionX(); - float dy = pos.GetPositionY() - bot->GetPositionY(); - float dz = pos.GetPositionZ() - bot->GetPositionZ(); - float dist = std::hypot(dx, dy); - - float moveX, moveY, moveZ; - if (dist > increment) - { - dx /= dist; - dy /= dist; - moveX = bot->GetPositionX() + dx * increment; - moveY = bot->GetPositionY() + dy * increment; - moveZ = bot->GetPositionZ() + (dz / dist) * increment; - } - else - { - moveX = pos.GetPositionX(); - moveY = pos.GetPositionY(); - moveZ = pos.GetPositionZ(); - } - - MoveTo(bot->GetMapId(), moveX, moveY, moveZ, false, false, false, true, MovementPriority::MOVEMENT_COMBAT, - true, false); - - return false; -} - -bool IccValithriaGroupAction::Handle25ManGroupLogic() -{ - const Position group1Pos = ICC_VDW_GROUP1_POSITION; - const Position group2Pos = ICC_VDW_GROUP2_POSITION; - - Group* group = bot->GetGroup(); - if (!group) - return false; - - // Collect group members - std::vector tanks, dps; - std::vector> nonHeals; - - for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next()) - { - if (Player* member = itr->GetSource()) - { - if (member->IsAlive() && !botAI->IsHeal(member)) - { - if (botAI->IsTank(member)) - { - tanks.push_back(member); - } - else - { - dps.push_back(member); - } - nonHeals.emplace_back(member->GetGUID(), member); - } - } - } - - // Sort by GUID for consistent ordering - std::sort(nonHeals.begin(), nonHeals.end(), [](auto const& a, auto const& b) { return a.first < b.first; }); - - // Assign to groups - std::vector group1, group2; - if (!tanks.empty()) - group1.push_back(tanks[0]); - - if (tanks.size() > 1) - group2.push_back(tanks[1]); - else if (tanks.size() == 1 && !dps.empty()) - group2.push_back(dps[0]); - - // Assign remaining DPS - std::set assigned; - for (Player* p : group1) - assigned.insert(p->GetGUID()); - for (Player* p : group2) - assigned.insert(p->GetGUID()); - - for (Player* p : dps) - { - if (assigned.find(p->GetGUID()) == assigned.end()) - { - (group1.size() <= group2.size() ? group1 : group2).push_back(p); - } - } - - // Check which group the bot is in - bool inGroup1 = std::any_of(group1.begin(), group1.end(), [this](Player* p) { return p == bot; }); - bool inGroup2 = std::any_of(group2.begin(), group2.end(), [this](Player* p) { return p == bot; }); - - // Marking logic for tanks and DPS - if (botAI->IsTank(bot) || botAI->IsDps(bot)) - HandleMarkingLogic(inGroup1, inGroup2, group1Pos, group2Pos); - - // Movement logic for non-healers - if (!botAI->IsHeal(bot)) - { - if (inGroup1) - { - float distance = bot->GetDistance(group1Pos); - if (distance > 25.0f) - { - // If far away, move directly to position - MoveTowardsPosition(group1Pos, 5.0f); - } - } - else if (inGroup2) - { - float distance = bot->GetDistance(group2Pos); - if (distance > 25.0f) - { - MoveTowardsPosition(group2Pos, 5.0f); - } - } - } - - return false; -} - -bool IccValithriaGroupAction::HandleMarkingLogic(bool inGroup1, bool inGroup2, const Position& group1Pos, - const Position& group2Pos) -{ - static constexpr uint8_t SKULL_ICON_INDEX = 7; - static constexpr uint8_t CROSS_ICON_INDEX = 6; - static const std::array addPriority = {NPC_BLAZING_SKELETON, NPC_SUPPRESSER, - NPC_RISEN_ARCHMAGE, NPC_BLISTERING_ZOMBIE, - NPC_GLUTTONOUS_ABOMINATION, NPC_ROT_WORM}; - - const Position* groupPos = nullptr; - uint8_t iconIndex = 0; - std::string rtiValue; - - if (inGroup1) - { - iconIndex = SKULL_ICON_INDEX; - groupPos = &group1Pos; - rtiValue = "skull"; - } - else if (inGroup2) - { - iconIndex = CROSS_ICON_INDEX; - groupPos = &group2Pos; - rtiValue = "cross"; - } - else - return false; - - context->GetValue("rti")->Set(rtiValue); - - // Find priority target - const GuidVector adds = AI_VALUE(GuidVector, "possible targets"); - Unit* priorityTarget = nullptr; - - for (uint32 entry : addPriority) - { - for (auto const& guid : adds) - { - if (Unit* unit = botAI->GetUnit(guid)) - { - if (unit->IsAlive() && unit->GetEntry() == entry && - unit->GetExactDist2d(groupPos->GetPositionX(), groupPos->GetPositionY()) <= 40.0f) - { - priorityTarget = unit; - break; - } - } - } - if (priorityTarget) - break; - } - - // Update target icon if needed - if (priorityTarget && bot->GetGroup()) - { - Group* group = bot->GetGroup(); - ObjectGuid currentIcon = group->GetTargetIcon(iconIndex); - Unit* currentIconUnit = botAI->GetUnit(currentIcon); - - // Check if the target already has any raid icon - bool hasOtherIcon = false; - for (uint8 i = 0; i < 8; ++i) - { - if (i == iconIndex) - continue; // Skip our own icon index - if (group->GetTargetIcon(i) == priorityTarget->GetGUID()) - { - hasOtherIcon = true; - break; - } - } - - if (!hasOtherIcon && (!currentIconUnit || !currentIconUnit->IsAlive() || currentIconUnit != priorityTarget)) - { - group->SetTargetIcon(iconIndex, bot->GetGUID(), priorityTarget->GetGUID()); - } - } - - return false; -} - -bool IccValithriaGroupAction::Handle10ManGroupLogic() -{ - static constexpr uint8_t DEFAULT_ICON_INDEX = 7; - static const std::array addPriority = {NPC_BLAZING_SKELETON, NPC_SUPPRESSER, - NPC_RISEN_ARCHMAGE, NPC_BLISTERING_ZOMBIE, - NPC_GLUTTONOUS_ABOMINATION, NPC_ROT_WORM}; - - // Marking logic - Group* group = bot->GetGroup(); - if (group) - { - const GuidVector adds = AI_VALUE(GuidVector, "possible targets"); - Unit* priorityTarget = nullptr; - - for (uint32 entry : addPriority) - { - for (auto const& guid : adds) - { - if (Unit* unit = botAI->GetUnit(guid)) - { - if (unit->IsAlive() && unit->GetEntry() == entry && - unit->GetExactDist2d(ICC_VDW_HEAL_POSITION.GetPositionX(), - ICC_VDW_HEAL_POSITION.GetPositionY()) <= 50.0f) - { - priorityTarget = unit; - break; - } - } - } - if (priorityTarget) - break; - } - - if (priorityTarget) - { - ObjectGuid currentIcon = group->GetTargetIcon(DEFAULT_ICON_INDEX); - Unit* currentIconUnit = botAI->GetUnit(currentIcon); - - if (!currentIconUnit || !currentIconUnit->IsAlive() || currentIconUnit != priorityTarget) - { - group->SetTargetIcon(DEFAULT_ICON_INDEX, bot->GetGUID(), priorityTarget->GetGUID()); - } - } - } - - // Movement logic - if (bot->GetExactDist2d(ICC_VDW_HEAL_POSITION.GetPositionX(), ICC_VDW_HEAL_POSITION.GetPositionY()) > 25.0f) - MoveTowardsPosition(ICC_VDW_HEAL_POSITION, 5.0f); - - return false; -} - -bool IccValithriaPortalAction::Execute(Event /*event*/) -{ - // Only healers should take portals, and not if already inside - if (!botAI->IsHeal(bot) || bot->HasAura(SPELL_DREAM_STATE)) - return false; - - // Gather all portals (pre-effect and real) using nearest npcs - GuidVector npcs = AI_VALUE(GuidVector, "nearest npcs"); - std::vector preEffectPortals; - std::vector realPortals; - for (auto const& guid : npcs) - { - Creature* c = dynamic_cast(botAI->GetUnit(guid)); - if (!c) - continue; - uint32 entry = c->GetEntry(); - if (entry == NPC_DREAM_PORTAL_PRE_EFFECT || entry == NPC_NIGHTMARE_PORTAL_PRE_EFFECT) - preEffectPortals.push_back(c); - else if (entry == NPC_DREAM_PORTAL || entry == NPC_NIGHTMARE_PORTAL) - realPortals.push_back(c); - } - - if (preEffectPortals.empty() && realPortals.empty()) - return false; - - // Remove duplicates (in case of overlap) - auto sortByGuid = [](Creature* a, Creature* b) { return a->GetGUID() < b->GetGUID(); }; - std::sort(preEffectPortals.begin(), preEffectPortals.end(), sortByGuid); - preEffectPortals.erase(std::unique(preEffectPortals.begin(), preEffectPortals.end()), preEffectPortals.end()); - std::sort(realPortals.begin(), realPortals.end(), sortByGuid); - realPortals.erase(std::unique(realPortals.begin(), realPortals.end()), realPortals.end()); - - // Gather all healers in group, sort by GUID for deterministic assignment - Group* group = bot->GetGroup(); - std::vector healers; - if (group) - { - for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next()) - { - Player* member = itr->GetSource(); - if (member && member->IsAlive() && botAI->IsHeal(member)) - healers.push_back(member); - } - std::sort(healers.begin(), healers.end(), [](Player* a, Player* b) { return a->GetGUID() < b->GetGUID(); }); - } - else - healers.push_back(bot); - - // Find this bot's index among healers - auto it = std::find(healers.begin(), healers.end(), bot); - if (it == healers.end()) - return false; - size_t healerIndex = std::distance(healers.begin(), it); - - // Assign each healer to a pre-effect portal by index (wrap if more healers than portals) - Creature* assignedPreEffect = nullptr; - if (!preEffectPortals.empty()) - assignedPreEffect = preEffectPortals[healerIndex % preEffectPortals.size()]; - - // Move to assigned pre-effect portal, stand at portal - if (assignedPreEffect) - { - float portalX = assignedPreEffect->GetPositionX(); - float portalY = assignedPreEffect->GetPositionY(); - float portalZ = assignedPreEffect->GetPositionZ(); - float dist = bot->GetDistance2d(portalX, portalY); - - if (dist > 0.5f) - { - // Move directly to the pre-effect portal position - MoveTo(assignedPreEffect->GetMapId(), portalX, portalY, portalZ, false, false, false, true, - MovementPriority::MOVEMENT_NORMAL); - } - // Remove shapeshift forms - botAI->RemoveShapeshift(); - - // Try to click the real portal if it is close enough - Creature* nearestRealPortal = nullptr; - float minDist = 9999.0f; - for (Creature* portal : realPortals) - { - float d = bot->GetDistance2d(portal); - if (d < 3.0f && d < minDist) - { - nearestRealPortal = portal; - minDist = d; - } - } - - if (nearestRealPortal) - { - botAI->RemoveShapeshift(); - bot->GetMotionMaster()->Clear(); - bot->StopMoving(); - bot->SetFacingToObject(nearestRealPortal); - nearestRealPortal->HandleSpellClick(bot); - return true; - } - - // If no real portal is close, wait at the position - return false; - } - - // If no pre-effect portals, try to find a real portal within 3f - Creature* nearestRealPortal = nullptr; - float minDist = 9999.0f; - for (Creature* portal : realPortals) - { - float d = bot->GetDistance2d(portal); - if (d < 3.0f && d < minDist) - { - nearestRealPortal = portal; - minDist = d; - } - } - - if (nearestRealPortal && minDist > 2.0f) - MoveTo(bot->GetMapId(), nearestRealPortal->GetPositionX(), nearestRealPortal->GetPositionY(), - nearestRealPortal->GetPositionZ(), false, false, false, true, MovementPriority::MOVEMENT_NORMAL); - - if (nearestRealPortal) - { - botAI->RemoveShapeshift(); - bot->GetMotionMaster()->Clear(); - bot->StopMoving(); - bot->SetFacingToObject(nearestRealPortal); - nearestRealPortal->HandleSpellClick(bot); - return true; - } - - return false; -} - -bool IccValithriaHealAction::Execute(Event /*event*/) -{ - // Early validation checks - if (!botAI->IsHeal(bot) || bot->GetHealthPct() < 50.0f) - return false; - - // Handle movement speed when not in dream state - if (!bot->HasAura(SPELL_DREAM_STATE)) - { - constexpr float NORMAL_SPEED = 1.0f; - bot->SetSpeed(MOVE_RUN, NORMAL_SPEED, true); - bot->SetSpeed(MOVE_WALK, NORMAL_SPEED, true); - bot->SetSpeed(MOVE_FLIGHT, NORMAL_SPEED, true); - } - - // Enforce Z-position limit - constexpr float MAX_Z_POSITION = 367.961f; - constexpr float TARGET_Z_POSITION = 365.0f; - if (bot->GetPositionZ() > MAX_Z_POSITION) - bot->TeleportTo(bot->GetMapId(), bot->GetPositionX(), bot->GetPositionY(), TARGET_Z_POSITION, - bot->GetOrientation()); - - // Find Valithria within range - Creature* valithria = bot->FindNearestCreature(NPC_VALITHRIA_DREAMWALKER, 100.0f); - if (!valithria) - return false; - - // Execute class-specific healing logic - switch (bot->getClass()) - { - case CLASS_DRUID: - { - // Druid healing spell constants - constexpr uint32 SPELL_REJUVENATION = 48441; - constexpr uint32 SPELL_REGROWTH = 48443; - constexpr uint32 SPELL_LIFEBLOOM = 48451; - constexpr uint32 SPELL_WILD_GROWTH = 53251; - constexpr uint8 LIFEBLOOM_MAX_STACKS = 3; - - // Apply Rejuvenation if missing - if (!valithria->HasAura(SPELL_REJUVENATION, bot->GetGUID())) - return botAI->CastSpell(SPELL_REJUVENATION, valithria); - - // Apply Regrowth if missing - if (!valithria->HasAura(SPELL_REGROWTH, bot->GetGUID())) - return botAI->CastSpell(SPELL_REGROWTH, valithria); - - // Stack Lifebloom to maximum stacks - Aura* lifebloom = valithria->GetAura(SPELL_LIFEBLOOM, bot->GetGUID()); - if (!lifebloom || lifebloom->GetStackAmount() < LIFEBLOOM_MAX_STACKS) - return botAI->CastSpell(SPELL_LIFEBLOOM, valithria); - - // All HoTs active with full stacks - cast Wild Growth - return botAI->CastSpell(SPELL_WILD_GROWTH, valithria); - } - case CLASS_SHAMAN: - { - constexpr uint32 SPELL_RIPTIDE = 61301; - constexpr uint32 SPELL_HEALING_WAVE = 49273; - - // Cast Healing Wave if Riptide is active, otherwise apply Riptide - return valithria->HasAura(SPELL_RIPTIDE, bot->GetGUID()) ? botAI->CastSpell(SPELL_HEALING_WAVE, valithria) - : botAI->CastSpell(SPELL_RIPTIDE, valithria); - } - case CLASS_PRIEST: - { - constexpr uint32 SPELL_RENEW = 48068; - constexpr uint32 SPELL_GREATER_HEAL = 48063; - - // Cast Greater Heal if Renew is active, otherwise apply Renew - return valithria->HasAura(SPELL_RENEW, bot->GetGUID()) ? botAI->CastSpell(SPELL_GREATER_HEAL, valithria) - : botAI->CastSpell(SPELL_RENEW, valithria); - } - case CLASS_PALADIN: - { - constexpr uint32 SPELL_BEACON_OF_LIGHT = 53563; - constexpr uint32 SPELL_HOLY_LIGHT = 48782; - - // Cast Holy Light if Beacon is active, otherwise apply Beacon of Light - return valithria->HasAura(SPELL_BEACON_OF_LIGHT, bot->GetGUID()) - ? botAI->CastSpell(SPELL_HOLY_LIGHT, valithria) - : botAI->CastSpell(SPELL_BEACON_OF_LIGHT, valithria); - } - default: - return false; - } - - return false; -} - -bool IccValithriaDreamCloudAction::Execute(Event /*event*/) -{ - // Only execute if we're in dream state - if (!bot->HasAura(SPELL_DREAM_STATE)) - return false; - - // Set speed to match players in dream state - bot->SetSpeed(MOVE_RUN, 2.0f, true); - bot->SetSpeed(MOVE_WALK, 2.0f, true); - bot->SetSpeed(MOVE_FLIGHT, 2.0f, true); - - // Gather all group members with dream state - const GuidVector members = AI_VALUE(GuidVector, "group members"); - std::vector dreamBots; - for (auto const& guid : members) - { - Unit* member = botAI->GetUnit(guid); - if (member && member->IsAlive() && member->HasAura(SPELL_DREAM_STATE)) - dreamBots.push_back(member); - } - - if (dreamBots.empty()) - return false; - - // Sort dreamBots by GUID (lowest first) - std::sort(dreamBots.begin(), dreamBots.end(), [](Unit* a, Unit* b) { return a->GetGUID() < b->GetGUID(); }); - - // Find this bot's index in the sorted list - auto it = std::find(dreamBots.begin(), dreamBots.end(), bot); - if (it == dreamBots.end()) - return false; - - // Check if all dream bots are stacked within 3f of the current leader (lowest guid) - constexpr float STACK_RADIUS = 2.0f; - Unit* leader = dreamBots.front(); - bool allStacked = true; - for (Unit* member : dreamBots) - { - // Only require stacking for bots, not real players - Player* player = member->ToPlayer(); - if (player && !player->GetSession()) // is a bot - { - if (member->GetDistance(leader) > STACK_RADIUS) - { - allStacked = false; - break; - } - } - } - - // If not all stacked, everyone moves to the leader's position (clouds' position) - constexpr float PORTALSTART_TOLERANCE = 1.0f; - if (!allStacked) - { - if (bot != leader) - { - if (bot->GetDistance(leader) > PORTALSTART_TOLERANCE) - { - bot->TeleportTo(bot->GetMapId(), leader->GetPositionX(), leader->GetPositionY(), leader->GetPositionZ(), - bot->GetOrientation()); - } - } - } - - // All stacked: leader (lowest guid) moves to next cloud, others follow and stack at leader's new position - // Find all dream and nightmare clouds - GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); - std::vector dreamClouds; - std::vector nightmareClouds; - - for (int i = 0; i < npcs.size(); ++i) - { - Unit* unit = botAI->GetUnit(npcs[i]); - if (unit && unit->IsAlive()) - { - if (Creature* creature = unit->ToCreature()) - { - if (creature->GetEntry() == NPC_DREAM_CLOUD) - dreamClouds.push_back(creature); - else if (creature->GetEntry() == NPC_NIGHTMARE_CLOUD) - nightmareClouds.push_back(creature); - } - } - } - - // Sort clouds by distance - std::sort(dreamClouds.begin(), dreamClouds.end(), - [this](Creature* a, Creature* b) { return bot->GetExactDist2d(a) < bot->GetExactDist2d(b); }); - - std::sort(nightmareClouds.begin(), nightmareClouds.end(), - [this](Creature* a, Creature* b) { return bot->GetExactDist2d(a) < bot->GetExactDist2d(b); }); - - // Only the leader moves to the next cloud - if (bot == leader) - { - // Use GUID to determine which cloud type to prefer - bool preferDream = (bot->GetGUID().GetCounter() % 2 == 0); - - // Check if we're close to any cloud - bool atDreamCloud = false; - bool atNightmareCloud = false; - - for (Creature* cloud : dreamClouds) - { - if (bot->GetExactDist2d(cloud) <= 2.0f) - { - atDreamCloud = true; - break; - } - } - - for (Creature* cloud : nightmareClouds) - { - if (bot->GetExactDist2d(cloud) <= 2.0f) - { - atNightmareCloud = true; - break; - } - } - - // If we have emerald vigor, prioritize dream clouds - if (bot->HasAura(SPELL_EMERALD_VIGOR)) - { - // If at dream cloud, move to 2nd closest dream cloud or closest nightmare cloud - if (atDreamCloud) - { - Creature* targetCloud = nullptr; - // Try 2nd closest dream cloud first - if (dreamClouds.size() >= 2 && bot->GetExactDist2d(dreamClouds[1]) > 2.0f) - targetCloud = dreamClouds[1]; - // Otherwise move to closest nightmare cloud - else if (!nightmareClouds.empty() && bot->GetExactDist2d(nightmareClouds[0]) > 2.0f) - targetCloud = nightmareClouds[0]; - - if (targetCloud) - MoveTo(targetCloud->GetMapId(), targetCloud->GetPositionX(), targetCloud->GetPositionY(), - targetCloud->GetPositionZ(), false, false, false, true, MovementPriority::MOVEMENT_NORMAL); - } - // If at nightmare cloud, move to closest dream cloud or 2nd closest nightmare cloud - else if (atNightmareCloud) - { - Creature* targetCloud = nullptr; - // Try closest dream cloud first - if (!dreamClouds.empty() && bot->GetExactDist2d(dreamClouds[0]) > 2.0f) - targetCloud = dreamClouds[0]; - // Otherwise move to 2nd closest nightmare cloud - else if (nightmareClouds.size() >= 2 && bot->GetExactDist2d(nightmareClouds[1]) > 2.0f) - targetCloud = nightmareClouds[1]; - - if (targetCloud) - MoveTo(targetCloud->GetMapId(), targetCloud->GetPositionX(), targetCloud->GetPositionY(), - targetCloud->GetPositionZ(), false, false, false, true, MovementPriority::MOVEMENT_NORMAL); - } - // If not at any cloud, move to closest dream cloud or nightmare cloud - else - { - if (!dreamClouds.empty() && bot->GetExactDist2d(dreamClouds[0]) > 2.0f) - MoveTo(dreamClouds[0]->GetMapId(), dreamClouds[0]->GetPositionX(), dreamClouds[0]->GetPositionY(), - dreamClouds[0]->GetPositionZ(), false, false, false, true, - MovementPriority::MOVEMENT_NORMAL); - else if (!nightmareClouds.empty() && bot->GetExactDist2d(nightmareClouds[0]) > 2.0f) - MoveTo(nightmareClouds[0]->GetMapId(), nightmareClouds[0]->GetPositionX(), - nightmareClouds[0]->GetPositionY(), nightmareClouds[0]->GetPositionZ(), false, false, false, - true, MovementPriority::MOVEMENT_NORMAL); - } - } - // Otherwise use GUID-based preference - else - { - // If prefer dream clouds based on GUID - if (preferDream) - { - // If at dream cloud, move to 2nd closest dream cloud or closest nightmare cloud - if (atDreamCloud) - { - Creature* targetCloud = nullptr; - // Try 2nd closest dream cloud first - if (dreamClouds.size() >= 2 && bot->GetExactDist2d(dreamClouds[1]) > 2.0f) - targetCloud = dreamClouds[1]; - // Otherwise move to closest nightmare cloud - else if (!nightmareClouds.empty() && bot->GetExactDist2d(nightmareClouds[0]) > 2.0f) - targetCloud = nightmareClouds[0]; - - if (targetCloud) - MoveTo(targetCloud->GetMapId(), targetCloud->GetPositionX(), targetCloud->GetPositionY(), - targetCloud->GetPositionZ(), false, false, false, true, - MovementPriority::MOVEMENT_NORMAL); - } - // If at nightmare cloud, move to closest dream cloud or 2nd closest nightmare cloud - else if (atNightmareCloud) - { - Creature* targetCloud = nullptr; - // Try closest dream cloud first - if (!dreamClouds.empty() && bot->GetExactDist2d(dreamClouds[0]) > 2.0f) - targetCloud = dreamClouds[0]; - // Otherwise move to 2nd closest nightmare cloud - else if (nightmareClouds.size() >= 2 && bot->GetExactDist2d(nightmareClouds[1]) > 2.0f) - targetCloud = nightmareClouds[1]; - - if (targetCloud) - MoveTo(targetCloud->GetMapId(), targetCloud->GetPositionX(), targetCloud->GetPositionY(), - targetCloud->GetPositionZ(), false, false, false, true, - MovementPriority::MOVEMENT_NORMAL); - } - // If not at any cloud, move to closest dream cloud or nightmare cloud based on preference - else - { - if (!dreamClouds.empty() && bot->GetExactDist2d(dreamClouds[0]) > 2.0f) - MoveTo(dreamClouds[0]->GetMapId(), dreamClouds[0]->GetPositionX(), - dreamClouds[0]->GetPositionY(), dreamClouds[0]->GetPositionZ(), false, false, false, - true, MovementPriority::MOVEMENT_NORMAL); - else if (!nightmareClouds.empty() && bot->GetExactDist2d(nightmareClouds[0]) > 2.0f) - MoveTo(nightmareClouds[0]->GetMapId(), nightmareClouds[0]->GetPositionX(), - nightmareClouds[0]->GetPositionY(), nightmareClouds[0]->GetPositionZ(), false, false, - false, true, MovementPriority::MOVEMENT_NORMAL); - } - } - // If prefer nightmare clouds based on GUID - else - { - // If at nightmare cloud, move to 2nd closest nightmare cloud or closest dream cloud - if (atNightmareCloud) - { - Creature* targetCloud = nullptr; - // Try 2nd closest nightmare cloud first - if (nightmareClouds.size() >= 2 && bot->GetExactDist2d(nightmareClouds[1]) > 2.0f) - targetCloud = nightmareClouds[1]; - // Otherwise move to closest dream cloud - else if (!dreamClouds.empty() && bot->GetExactDist2d(dreamClouds[0]) > 2.0f) - targetCloud = dreamClouds[0]; - - if (targetCloud) - MoveTo(targetCloud->GetMapId(), targetCloud->GetPositionX(), targetCloud->GetPositionY(), - targetCloud->GetPositionZ(), false, false, false, true, - MovementPriority::MOVEMENT_NORMAL); - } - // If at dream cloud, move to closest nightmare cloud or 2nd closest dream cloud - else if (atDreamCloud) - { - Creature* targetCloud = nullptr; - // Try closest nightmare cloud first - if (!nightmareClouds.empty() && bot->GetExactDist2d(nightmareClouds[0]) > 2.0f) - targetCloud = nightmareClouds[0]; - // Otherwise move to 2nd closest dream cloud - else if (dreamClouds.size() >= 2 && bot->GetExactDist2d(dreamClouds[1]) > 2.0f) - targetCloud = dreamClouds[1]; - - if (targetCloud) - MoveTo(targetCloud->GetMapId(), targetCloud->GetPositionX(), targetCloud->GetPositionY(), - targetCloud->GetPositionZ(), false, false, false, true, - MovementPriority::MOVEMENT_NORMAL); - } - // If not at any cloud, move to closest nightmare cloud or dream cloud based on preference - else - { - if (!nightmareClouds.empty() && bot->GetExactDist2d(nightmareClouds[0]) > 2.0f) - MoveTo(nightmareClouds[0]->GetMapId(), nightmareClouds[0]->GetPositionX(), - nightmareClouds[0]->GetPositionY(), nightmareClouds[0]->GetPositionZ(), false, false, - false, true, MovementPriority::MOVEMENT_NORMAL); - else if (!dreamClouds.empty() && bot->GetExactDist2d(dreamClouds[0]) > 2.0f) - MoveTo(dreamClouds[0]->GetMapId(), dreamClouds[0]->GetPositionX(), - dreamClouds[0]->GetPositionY(), dreamClouds[0]->GetPositionZ(), false, false, false, - true, MovementPriority::MOVEMENT_NORMAL); - } - } - } - } - else - { - // Non-leader bots follow and stack at leader's position - if (bot->GetDistance(leader) > PORTALSTART_TOLERANCE) - { - botAI->Reset(); - bot->TeleportTo(bot->GetMapId(), leader->GetPositionX(), leader->GetPositionY(), leader->GetPositionZ(), - bot->GetOrientation()); - } - } - - return false; -} - -// Sindragosa -bool IccSindragosaGroupPositionAction::Execute(Event /*event*/) -{ - Unit* boss = AI_VALUE2(Unit*, "find target", "sindragosa"); - if (!boss || boss->HasUnitMovementFlag(MOVEMENTFLAG_DISABLE_GRAVITY)) - return false; - - Aura* aura = botAI->GetAura("mystic buffet", bot, false, true); - - if (aura && aura->GetStackAmount() >= 6 && botAI->IsMainTank(bot)) - return false; - - if (botAI->IsTank(bot) && boss->GetVictim() == bot) - return HandleTankPositioning(boss); - - if (boss && boss->GetVictim() != bot) - return HandleNonTankPositioning(); - - return false; -} - -bool IccSindragosaGroupPositionAction::HandleTankPositioning(Unit* boss) -{ - float distBossToCenter = boss->GetExactDist2d(ICC_SINDRAGOSA_CENTER_POSITION); - float distToTankPos = bot->GetExactDist2d(ICC_SINDRAGOSA_TANK_POSITION); - float targetOrientation = M_PI / 2; // We want boss to face east - float currentOrientation = boss->GetOrientation(); - - // Normalize both orientations to 0-2π range - currentOrientation = fmod(currentOrientation + 2 * M_PI, 2 * M_PI); - targetOrientation = fmod(targetOrientation + 2 * M_PI, 2 * M_PI); - - float orientationDiff = currentOrientation - targetOrientation; - - // Normalize the difference to be between -PI and PI - while (orientationDiff > M_PI) - orientationDiff -= 2 * M_PI; - while (orientationDiff < -M_PI) - orientationDiff += 2 * M_PI; - - // Stage 1: Move boss to center if too far - if (boss && boss->GetVictim() == bot && distBossToCenter > 16.0f && distToTankPos <= 20.0f) - { - // Calculate direction vector from boss to center - float dirX = ICC_SINDRAGOSA_CENTER_POSITION.GetPositionX() - boss->GetPositionX(); - float dirY = ICC_SINDRAGOSA_CENTER_POSITION.GetPositionY() - boss->GetPositionY(); - - // Move 10 yards beyond center in the same direction - float moveX = ICC_SINDRAGOSA_CENTER_POSITION.GetPositionX() + (dirX / distBossToCenter) * 4.0f; - float moveY = ICC_SINDRAGOSA_CENTER_POSITION.GetPositionY() + (dirY / distBossToCenter) * 4.0f; - - return MoveTo(bot->GetMapId(), moveX, moveY, boss->GetPositionZ(), false, false, false, false, - MovementPriority::MOVEMENT_FORCED, true, false); - } - - // Stage 2: Move to tank position if too far - if (boss && boss->GetVictim() == bot && distToTankPos > 10.0f) - { - Position botPos = bot->GetPosition(); - Position tankPos = ICC_SINDRAGOSA_TANK_POSITION; - - float dx = tankPos.GetPositionX() - botPos.GetPositionX(); - float dy = tankPos.GetPositionY() - botPos.GetPositionY(); - - float distance = std::sqrt(dx * dx + dy * dy); - float step = 1.0f; - - // Normalize and scale direction vector - float scale = step / distance; - - float targetX = botPos.GetPositionX() + dx * scale; - float targetY = botPos.GetPositionY() + dy * scale; - - return MoveTo(bot->GetMapId(), targetX, targetY, bot->GetPositionZ(), false, false, false, true, - MovementPriority::MOVEMENT_COMBAT, true, false); - } - - // Stage 3: Adjust orientation when in position - if (boss && boss->GetVictim() == bot && std::abs(orientationDiff) > 0.15f) - { - // Move in an arc (circle) north or south around the boss until the orientation matches - float currentX = bot->GetPositionX(); - float currentY = bot->GetPositionY(); - float centerX = boss->GetPositionX(); - float centerY = boss->GetPositionY(); - float radius = std::max(2.0f, bot->GetExactDist2d(centerX, centerY)); // keep at least 2 yards from boss - - // Calculate current angle from boss to bot - float angle = atan2(currentY - centerY, currentX - centerX); - - // Determine direction: negative diff = move counterclockwise (north), positive = clockwise (south) - float arcStep = 0.125f; // radians per move, adjust for smoothness - if (orientationDiff < 0) - angle += arcStep; // move north (counterclockwise) - else - angle -= arcStep; // move south (clockwise) - - // Calculate new position on the arc - float moveX = centerX + radius * cos(angle); - float moveY = centerY + radius * sin(angle); - - return MoveTo(bot->GetMapId(), moveX, moveY, bot->GetPositionZ(), false, false, false, false, - MovementPriority::MOVEMENT_FORCED, true, false); - } - - // Stage 4: Adjust Y-axis position if too far from tank position - float yDiff = std::abs(bot->GetPositionY() - ICC_SINDRAGOSA_TANK_POSITION.GetPositionY()); - if (boss && boss->GetVictim() == bot && yDiff > 2.0f) - { - Position botPos = bot->GetPosition(); - Position tankPos = ICC_SINDRAGOSA_TANK_POSITION; - - // Only adjust Y position, keep X and Z the same - float newY = botPos.GetPositionY() + (tankPos.GetPositionY() > botPos.GetPositionY() ? 1.0f : -1.0f); - - return MoveTo(bot->GetMapId(), botPos.GetPositionX(), newY, botPos.GetPositionZ(), false, false, false, false, - MovementPriority::MOVEMENT_FORCED, true, false); - } - - return false; -} - -bool IccSindragosaGroupPositionAction::HandleNonTankPositioning() -{ - Group* group = bot->GetGroup(); - if (!group) - return false; - - // Collect all alive raid members - std::vector raidMembers; - for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next()) - { - Player* member = itr->GetSource(); - if (!member || !member->IsAlive()) - continue; - raidMembers.push_back(member); - } - - // Count members without aura 1111 - size_t membersWithoutAura = 0; - for (Player* member : raidMembers) - { - if (!botAI->GetAura("mystic buffet", member)) - membersWithoutAura++; - } - - // Calculate percentage without aura - size_t totalMembers = raidMembers.size(); - if (totalMembers == 0) - return false; - - double percentageWithoutAura = static_cast(membersWithoutAura) / totalMembers; - bool raidClear = (percentageWithoutAura >= 0.6); // 60% or more don't have aura 1111 - - if (raidClear && botAI->IsTank(bot)) - { - static const std::array tombEntries = {NPC_TOMB1, NPC_TOMB2, NPC_TOMB3, NPC_TOMB4}; - const GuidVector tombGuids = AI_VALUE(GuidVector, "possible targets no los"); - - Unit* nearestTomb = nullptr; - float minDist = 150.0f; - - for (const auto entry : tombEntries) - { - for (auto const& guid : tombGuids) - { - if (Unit* unit = botAI->GetUnit(guid)) - { - if (unit->GetEntry() == entry && unit->IsAlive()) - { - float dist = bot->GetDistance(unit); - if (dist < minDist) - { - minDist = dist; - nearestTomb = unit; - } - } - } - } - } - - static constexpr uint8_t SKULL_ICON_INDEX = 7; - - Group* group = bot->GetGroup(); - if (!group) - return false; // Cannot assign icon without group - - Unit* targetToMark = nearestTomb; - - // Fallback: mark boss if no tomb is found - if (!targetToMark) - { - Unit* boss = AI_VALUE2(Unit*, "find target", "sindragosa"); - if (boss && boss->IsAlive()) - targetToMark = boss; - } - - if (targetToMark) - { - const ObjectGuid currentSkull = group->GetTargetIcon(SKULL_ICON_INDEX); - Unit* currentSkullUnit = botAI->GetUnit(currentSkull); - - const bool needsUpdate = - !currentSkullUnit || !currentSkullUnit->IsAlive() || currentSkullUnit != targetToMark; - - if (needsUpdate) - group->SetTargetIcon(SKULL_ICON_INDEX, bot->GetGUID(), targetToMark->GetGUID()); - } - } - - context->GetValue("rti")->Set("skull"); - if (botAI->IsRanged(bot)) - { - const float TOLERANCE = 9.0f; - const float MAX_STEP = 5.0f; - - float distToTarget = bot->GetExactDist2d(ICC_SINDRAGOSA_RANGED_POSITION); - - // Only move if outside tolerance - if (distToTarget > TOLERANCE) - return MoveIncrementallyToPosition(ICC_SINDRAGOSA_RANGED_POSITION, MAX_STEP); - - return false; - } - else - { - const float TOLERANCE = 10.0f; - const float MAX_STEP = 5.0f; - - float distToTarget = bot->GetExactDist2d(ICC_SINDRAGOSA_MELEE_POSITION); - - // Only move if outside tolerance - if (distToTarget > TOLERANCE) - return MoveIncrementallyToPosition(ICC_SINDRAGOSA_MELEE_POSITION, MAX_STEP); - - return false; - } -} - -bool IccSindragosaGroupPositionAction::MoveIncrementallyToPosition(const Position& targetPos, float maxStep) -{ - // Calculate direction vector to target - float dirX = targetPos.GetPositionX() - bot->GetPositionX(); - float dirY = targetPos.GetPositionY() - bot->GetPositionY(); - - // Normalize direction vector - float length = sqrt(dirX * dirX + dirY * dirY); - dirX /= length; - dirY /= length; - - // Calculate intermediate point - float distToTarget = bot->GetExactDist2d(targetPos); - float stepSize = std::min(maxStep, distToTarget); - float moveX = bot->GetPositionX() + dirX * stepSize; - float moveY = bot->GetPositionY() + dirY * stepSize; - - return MoveTo(bot->GetMapId(), moveX, moveY, targetPos.GetPositionZ(), false, false, false, false, - MovementPriority::MOVEMENT_COMBAT); -} - -bool IccSindragosaTankSwapPositionAction::Execute(Event /*event*/) -{ - Unit* boss = AI_VALUE2(Unit*, "find target", "sindragosa"); - if (!boss) - return false; - - // Only for assist tank - if (!botAI->IsAssistTank(bot)) - return false; - - float distToTankPos = bot->GetExactDist2d(ICC_SINDRAGOSA_TANK_POSITION); - - // Move to tank position - if (distToTankPos > 3.0f) // Tighter tolerance for tank swap - { - return MoveTo(bot->GetMapId(), ICC_SINDRAGOSA_TANK_POSITION.GetPositionX(), - ICC_SINDRAGOSA_TANK_POSITION.GetPositionY(), - ICC_SINDRAGOSA_TANK_POSITION.GetPositionZ(), - false, false, false, false, MovementPriority::MOVEMENT_FORCED, true, false); - } - - return false; -} - -bool IccSindragosaFrostBeaconAction::Execute(Event /*event*/) -{ - const Unit* boss = AI_VALUE2(Unit*, "find target", "sindragosa"); - if (!boss) - return false; - - HandleSupportActions(); - - if (bot->HasAura(FROST_BEACON_AURA_ID)) - { - return HandleBeaconedPlayer(boss); - } - - return HandleNonBeaconedPlayer(boss); -} - -void IccSindragosaFrostBeaconAction::HandleSupportActions() -{ - Group* group = bot->GetGroup(); - - // Tank support - Paladin Hand of Freedom - if (group && bot->getClass() == CLASS_PALADIN) - { - for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next()) - { - Player* member = itr->GetSource(); - if (!member || !member->IsAlive() || !botAI->IsTank(member)) - { - continue; - } - - if (botAI->GetAura("Frost Breath", member) && !member->HasAura(HAND_OF_FREEDOM_SPELL_ID)) - { - botAI->CastSpell(HAND_OF_FREEDOM_SPELL_ID, member); - break; - } - } - } - - // Healer support - Apply HoTs to beaconed players - if (botAI->IsHeal(bot) && !bot->HasAura(FROST_BEACON_AURA_ID)) - { - const auto members = AI_VALUE(GuidVector, "group members"); - for (auto const& memberGuid : members) - { - Unit* member = botAI->GetUnit(memberGuid); - if (!member || !member->IsAlive() || !member->HasAura(FROST_BEACON_AURA_ID)) - { - continue; - } - - // Apply class-specific HoT spells - uint32 spellId = 0; - switch (bot->getClass()) - { - case CLASS_PRIEST: - spellId = 48068; - break; // Renew - case CLASS_SHAMAN: - spellId = 61301; - break; // Riptide - case CLASS_DRUID: - spellId = 48441; - break; // Rejuvenation - default: - continue; - } - - if (!member->HasAura(spellId)) - { - botAI->CastSpell(spellId, member); - } - } - } -} - -bool IccSindragosaFrostBeaconAction::HandleBeaconedPlayer(const Unit* boss) -{ - // Phase 3 positioning (below 35% health, not flying) - if (boss->HealthBelowPct(35) && !IsBossFlying(boss)) - { - if (!bot->HasAura(SPELL_NITRO_BOOSTS)) - bot->AddAura(SPELL_NITRO_BOOSTS, bot); - botAI->Reset(); - return MoveToPositionIfNeeded(ICC_SINDRAGOSA_THOMBMB2_POSITION, POSITION_TOLERANCE); - } - - // Regular beacon positioning using tomb spots - Group* group = bot->GetGroup(); - if (!group) - { - return false; - } - - // Collect and sort beaconed players by GUID for deterministic assignment - std::vector beaconedPlayers; - for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next()) - { - Player* member = itr->GetSource(); - if (member && member->IsAlive() && member->HasAura(FROST_BEACON_AURA_ID)) - { - beaconedPlayers.push_back(member); - } - } - - std::sort(beaconedPlayers.begin(), beaconedPlayers.end(), - [](const Player* a, const Player* b) { return a->GetGUID() < b->GetGUID(); }); - - // Find this bot's index - const auto it = std::find(beaconedPlayers.begin(), beaconedPlayers.end(), bot); - if (it == beaconedPlayers.end()) - { - return false; - } - - const size_t myIndex = std::distance(beaconedPlayers.begin(), it); - const size_t beaconCount = beaconedPlayers.size(); - - // Calculate tomb spot based on beacon count - size_t spot = 0; - switch (beaconCount) - { - case 2: - spot = (myIndex == 0) ? 0 : 2; - break; - case 5: - spot = (myIndex < 2) ? 0 : ((myIndex == 2) ? 1 : 2); - break; - case 6: - spot = myIndex / 2; - break; - default: - spot = myIndex % 3; - break; - } - - // Get tomb position and move if needed - static constexpr std::array tombPositions = { - &ICC_SINDRAGOSA_THOMB1_POSITION, &ICC_SINDRAGOSA_THOMB2_POSITION, &ICC_SINDRAGOSA_THOMB3_POSITION}; - - const Position& tombPosition = *tombPositions[std::min(spot, tombPositions.size() - 1)]; - return MoveToPositionIfNeeded(tombPosition, TOMB_POSITION_TOLERANCE); -} - -bool IccSindragosaFrostBeaconAction::HandleNonBeaconedPlayer(const Unit* boss) -{ - // Collect beaconed players - std::vector beaconedPlayers; - const auto members = AI_VALUE(GuidVector, "group members"); - for (auto const& memberGuid : members) - { - Unit* player = botAI->GetUnit(memberGuid); - if (player && player->GetGUID() != bot->GetGUID() && player->HasAura(FROST_BEACON_AURA_ID)) - { - beaconedPlayers.push_back(player); - } - } - - if (beaconedPlayers.empty()) - { - return false; - } - - // Air phase positioning - if (IsBossFlying(boss)) - { - if (!bot->HasAura(FROST_BEACON_AURA_ID)) - { - const Difficulty diff = bot->GetRaidDifficulty(); - bool is25Man = false; - if (diff && (diff == RAID_DIFFICULTY_25MAN_NORMAL || diff == RAID_DIFFICULTY_25MAN_HEROIC)) - is25Man = true; - - const Position& safePosition = is25Man ? ICC_SINDRAGOSA_FBOMB_POSITION : ICC_SINDRAGOSA_FBOMB10_POSITION; - - const float dist = bot->GetExactDist2d(safePosition.GetPositionX(), safePosition.GetPositionY()); - if (dist > MOVE_TOLERANCE) - { - return MoveToPosition(safePosition); - } - } - return botAI->IsHeal(bot); // Continue for healers, wait for others - } - - // Ground phase - position based on role and avoid beaconed players - const bool isRanged = botAI->IsRanged(bot) || (bot->GetExactDist2d(ICC_SINDRAGOSA_RANGED_POSITION.GetPositionX(),ICC_SINDRAGOSA_RANGED_POSITION.GetPositionY()) < - bot->GetExactDist2d(ICC_SINDRAGOSA_MELEE_POSITION.GetPositionX(),ICC_SINDRAGOSA_MELEE_POSITION.GetPositionY())); - - const Position& targetPosition = isRanged ? ICC_SINDRAGOSA_RANGED_POSITION : ICC_SINDRAGOSA_MELEE_POSITION; - - const float deltaX = std::abs(targetPosition.GetPositionX() - bot->GetPositionX()); - const float deltaY = std::abs(targetPosition.GetPositionY() - bot->GetPositionY()); - if (boss && boss->GetVictim() != bot) - { - if ((deltaX > MOVE_TOLERANCE) || (deltaY > MOVE_TOLERANCE)) - { - if (bot->HasUnitState(UNIT_STATE_CASTING)) - { - botAI->Reset(); - } - return MoveToPosition(targetPosition); - } - } - return false; -} - -bool IccSindragosaFrostBeaconAction::MoveToPositionIfNeeded(const Position& position, float tolerance) -{ - const float distance = bot->GetExactDist2d(position.GetPositionX(), position.GetPositionY()); - if (distance > tolerance) - { - return MoveToPosition(position); - } - return distance <= tolerance; -} - -bool IccSindragosaFrostBeaconAction::MoveToPosition(const Position& position) -{ - float posX = position.GetPositionX(); - float posY = position.GetPositionY(); - float posZ = position.GetPositionZ(); - - bot->UpdateAllowedPositionZ(posX, posY, posZ); - - return MoveTo(bot->GetMapId(), posX, posY, posZ, false, false, false, false, MovementPriority::MOVEMENT_FORCED, - true, false); -} - -bool IccSindragosaFrostBeaconAction::IsBossFlying(const Unit* boss) -{ - return boss->GetExactDist2d(ICC_SINDRAGOSA_FLYING_POSITION.GetPositionX(), - ICC_SINDRAGOSA_FLYING_POSITION.GetPositionY()) < 30.0f; -} - -bool IccSindragosaBlisteringColdAction::Execute(Event /*event*/) -{ - Unit* boss = AI_VALUE2(Unit*, "find target", "sindragosa"); - if (!boss) - return false; - - // Only non-tanks should move out - if (botAI->IsMainTank(bot)) - return false; - - float dist = bot->GetExactDist2d(boss->GetPositionX(), boss->GetPositionY()); - - if (dist >= 33.0f) - return false; - - Position const& targetPos = ICC_SINDRAGOSA_BLISTERING_COLD_POSITION; - - // Only move if we're too close to the boss (< 30 yards) - if (dist < 33.0f) - { - - float const STEP_SIZE = 15.0f; - float distToTarget = bot->GetDistance2d(targetPos.GetPositionX(), targetPos.GetPositionY()); - - if (distToTarget > 0.1f) // Avoid division by zero - { - if (!bot->HasAura(SPELL_NITRO_BOOSTS)) - bot->AddAura(SPELL_NITRO_BOOSTS, bot); - // Calculate direction vector - float dirX = targetPos.GetPositionX() - bot->GetPositionX(); - float dirY = targetPos.GetPositionY() - bot->GetPositionY(); - - // Normalize direction vector - float length = sqrt(dirX * dirX + dirY * dirY); - dirX /= length; - dirY /= length; - - // Move STEP_SIZE yards in that direction - float moveX = bot->GetPositionX() + dirX * STEP_SIZE; - float moveY = bot->GetPositionY() + dirY * STEP_SIZE; - - return MoveTo(bot->GetMapId(), moveX, moveY, bot->GetPositionZ(), - false, false, false, true, MovementPriority::MOVEMENT_FORCED, true, false); - } - } - return false; -} - -bool IccSindragosaUnchainedMagicAction::Execute(Event /*event*/) -{ - Unit* boss = AI_VALUE2(Unit*, "find target", "sindragosa"); - if (!boss) - return false; - - Aura* aura = botAI->GetAura("Unchained Magic", bot, false, true); - if (!aura) - return false; - - Aura* aura1 = botAI->GetAura("Instability", bot, false, true); - - Difficulty diff = bot->GetRaidDifficulty(); - if (aura && (diff == RAID_DIFFICULTY_10MAN_NORMAL || diff == RAID_DIFFICULTY_25MAN_NORMAL)) - { - if (aura1 && aura1->GetStackAmount() >= 6) - return true; // Stop casting spells - } - - return false; -} - -bool IccSindragosaChilledToTheBoneAction::Execute(Event /*event*/) -{ - Unit* boss = AI_VALUE2(Unit*, "find target", "sindragosa"); - if (!boss) - return false; - - Aura* aura = botAI->GetAura("Chilled to the Bone", bot, false, true); - if (!aura) - return false; - - if (aura) // Chilled to the Bone - { - if (aura->GetStackAmount() >= 6) - { - botAI->Reset(); - bot->AttackStop(); - return true; - } - } - - return false; -} - -bool IccSindragosaMysticBuffetAction::Execute(Event /*event*/) -{ - Unit* boss = AI_VALUE2(Unit*, "find target", "sindragosa"); - if (!boss || !bot || !bot->IsAlive()) - return false; - - // Check if we have Mystic Buffet - Aura* aura = botAI->GetAura("mystic buffet", bot, false, true); - if (!aura) - return false; - - if (boss->GetVictim() == bot) - return false; - - // Skip if we have Frost Beacon - if (bot->HasAura(SPELL_FROST_BEACON)) - return false; - - Group* group = bot->GetGroup(); - if (!group) - return false; - - static const std::array tombEntries = {NPC_TOMB1, NPC_TOMB2, NPC_TOMB3, NPC_TOMB4}; - const GuidVector tombGuids = AI_VALUE(GuidVector, "possible targets no los"); - - Unit* nearestTomb = nullptr; - float minDist = 150.0f; - - for (const auto entry : tombEntries) - { - for (auto const& guid : tombGuids) - { - if (Unit* unit = botAI->GetUnit(guid)) - { - if (unit->GetEntry() == entry && unit->IsAlive()) - { - float dist = bot->GetDistance(unit); - if (dist < minDist) - { - minDist = dist; - nearestTomb = unit; - } - } - } - } - } - - // Check if anyone in group has Frost Beacon (SPELL_FROST_BEACON) - bool anyoneHasFrostBeacon = false; - for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) - { - Player* member = ref->GetSource(); - if (member && member->IsAlive() && member->HasAura(SPELL_FROST_BEACON)) - { - anyoneHasFrostBeacon = true; - break; - } - } - - bool tombPresent = nearestTomb != nullptr; - bool atLOS2 = bot->GetExactDist2d(ICC_SINDRAGOSA_LOS2_POSITION.GetPositionX(), - ICC_SINDRAGOSA_LOS2_POSITION.GetPositionY()) <= 2.0f; - - // Move to LOS2 position if: tomb is present and no one has Frost Beacon - bool shouldMoveLOS2 = tombPresent && !anyoneHasFrostBeacon; - - if (shouldMoveLOS2) - { - // If already at LOS2 and have 3+ stacks, stay still - if (atLOS2 && aura && !botAI->IsHeal(bot)) - { - return true; - } - - botAI->Reset(); - // Move to LOS2 position - return MoveTo(bot->GetMapId(), ICC_SINDRAGOSA_LOS2_POSITION.GetPositionX(), - ICC_SINDRAGOSA_LOS2_POSITION.GetPositionY(), ICC_SINDRAGOSA_LOS2_POSITION.GetPositionZ(), false, - false, false, true, MovementPriority::MOVEMENT_FORCED); - } - return false; -} - -bool IccSindragosaFrostBombAction::Execute(Event /*event*/) -{ - if (!bot || !bot->IsAlive() || bot->HasAura(SPELL_ICE_TOMB)) // Skip if dead or in Ice Tomb - return false; - - Group* group = bot->GetGroup(); - if (!group) - return false; - - // Find frost bomb marker and tombs - GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); - const uint32 tombEntries[] = {NPC_TOMB1, NPC_TOMB2, NPC_TOMB3, NPC_TOMB4}; // tomb id's - Unit* marker = nullptr; - std::vector tombs; - std::vector tombGuids; - - // Manually search for units with frost bomb aura (SPELL_FROST_BOMB_VISUAL) using NearestHostileNpcsValue logic - std::list units; - float range = 200.0f; - Acore::AnyUnitInObjectRangeCheck u_check(bot, range); - Acore::UnitListSearcher searcher(bot, units, u_check); - Cell::VisitObjects(bot, searcher, range); - - for (Unit* unit : units) - { - if (!unit || !unit->IsAlive()) - continue; - - if (unit->HasAura(SPELL_FROST_BOMB_VISUAL)) // Frost bomb visual - marker = unit; - - // Check if unit is a tomb - for (uint32 entry : tombEntries) - { - if (unit->GetEntry() == entry) - { - tombs.push_back(unit); - tombGuids.push_back(unit->GetGUID()); - break; - } - } - } - - if (!marker || tombs.empty()) - { - bot->AttackStop(); - return true; - } - - // Get persistent group assignment - use a static map to store assignments - static std::map persistentGroupAssignments; - static std::vector allGroupGuids; // All guids that have ever been in the raid - - // Gather all group members (alive and dead, including those with ice tomb) - std::vector currentGuids; - for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next()) - { - Player* member = itr->GetSource(); - if (member) - currentGuids.push_back(member->GetGUID()); - } - - // Add any new GUIDs to our persistent list - for (const ObjectGuid& guid : currentGuids) - { - if (std::find(allGroupGuids.begin(), allGroupGuids.end(), guid) == allGroupGuids.end()) - { - allGroupGuids.push_back(guid); - } - } - - // Sort the complete guid list for consistency - std::sort(allGroupGuids.begin(), allGroupGuids.end()); - - Difficulty diff = bot->GetRaidDifficulty(); - // Determine group count (2 for 10m, 3 for 25m) - int groupCount = (diff == RAID_DIFFICULTY_25MAN_NORMAL || diff == RAID_DIFFICULTY_25MAN_HEROIC) ? 3 : 2; - - // Assign group indices to GUIDs that don't have assignments yet - for (size_t i = 0; i < allGroupGuids.size(); ++i) - { - const ObjectGuid& guid = allGroupGuids[i]; - if (persistentGroupAssignments.find(guid) == persistentGroupAssignments.end()) - { - // Assign to group based on their position in the sorted list - persistentGroupAssignments[guid] = int(i) % groupCount; - } - } - - // Get this bot's group assignment - auto it = persistentGroupAssignments.find(bot->GetGUID()); - if (it == persistentGroupAssignments.end()) - return false; - - int myGroupIndex = it->second; - - // Build group positions based on available tombs - std::vector groupPositions; - for (int i = 0; i < groupCount; ++i) - { - if (i < int(tombs.size())) - { - groupPositions.push_back(tombs[i]->GetPosition()); - } - else - { - groupPositions.push_back(marker->GetPosition()); - } - } - - // PRIORITY 1: Check if there are any tombs near our current position (within 8 yards) - std::vector nearbyTombs; - for (Unit* tomb : tombs) - { - if (tomb->GetExactDist2d(bot) <= 8.0f) - { - nearbyTombs.push_back(tomb); - } - } - - // PRIORITY 2: If no tombs nearby, find tombs near our assigned group position - std::vector groupPositionTombs; - if (nearbyTombs.empty()) - { - for (Unit* tomb : tombs) - { - if (tomb->GetExactDist2d(groupPositions[myGroupIndex]) <= 8.0f) - { - groupPositionTombs.push_back(tomb); - } - } - } - - // Select which tombs to use based on priority - std::vector myTombs; - std::vector myTombGuids; - - if (!nearbyTombs.empty()) - { - // Use tombs near current position (highest priority) - myTombs = nearbyTombs; - for (Unit* tomb : nearbyTombs) - { - myTombGuids.push_back(tomb->GetGUID()); - } - } - else if (!groupPositionTombs.empty()) - { - // Use tombs near group position (medium priority) - myTombs = groupPositionTombs; - for (Unit* tomb : groupPositionTombs) - { - myTombGuids.push_back(tomb->GetGUID()); - } - } - else - { - // Fallback: use closest available tomb (lowest priority) - Unit* closestTomb = nullptr; - float closestDist = 999.0f; - for (Unit* tomb : tombs) - { - float dist = tomb->GetExactDist2d(bot); - if (dist < closestDist) - { - closestDist = dist; - closestTomb = tomb; - } - } - if (closestTomb) - { - myTombs.push_back(closestTomb); - myTombGuids.push_back(closestTomb->GetGUID()); - } - } - - if (myTombs.empty()) - return false; - - // Pick the tomb with highest HP in our selection - size_t bestIdx = 0; - float bestHp = 0.0f; - for (size_t i = 0; i < myTombs.size(); ++i) - { - float hp = myTombs[i]->GetHealthPct(); - if (i == 0 || hp > bestHp) - { - bestHp = hp; - bestIdx = i; - } - } - Unit* losTomb = myTombs[bestIdx]; - - // Calculate position for LOS (stand at least 6.5f behind the tomb from the bomb) - float angle = marker->GetAngle(losTomb); - float posX = losTomb->GetPositionX() + cos(angle) * 6.5f; - float posY = losTomb->GetPositionY() + sin(angle) * 6.5f; - float posZ = losTomb->GetPositionZ(); - - // Always move to exact LOS position for safety - float distToLosPos = bot->GetDistance2d(posX, posY); - if (distToLosPos > 0.01f) - { - botAI->Reset(); - bot->AttackStop(); - return MoveTo(bot->GetMapId(), posX, posY, posZ, false, false, false, true, MovementPriority::MOVEMENT_FORCED); - } - - // Check if we are in LOS of the bomb (must be very close to calculated position) - bool inLOS = (distToLosPos <= 0.01f); - - // RTI marker constants - static constexpr uint8_t SKULL_ICON_INDEX = 7; - static constexpr uint8_t CROSS_ICON_INDEX = 6; - static constexpr uint8_t STAR_ICON_INDEX = 0; - - // If in LOS, handle RTI marking for group's tombs - if (inLOS) - { - // Determine RTI marker for this group - uint8_t iconIndex = 0; - std::string rtiValue; - if (myGroupIndex == 0) - { - iconIndex = SKULL_ICON_INDEX; - rtiValue = "skull"; - } - else if (myGroupIndex == 1) - { - iconIndex = CROSS_ICON_INDEX; - rtiValue = "cross"; - } - else if (myGroupIndex == 2) - { - iconIndex = STAR_ICON_INDEX; - rtiValue = "star"; - } - else - return false; - - context->GetValue("rti")->Set(rtiValue); - - // Find a tomb in our group with 45% or more HP to mark - Unit* tombToMark = nullptr; - for (size_t i = 0; i < myTombs.size(); ++i) - { - Unit* tomb = myTombs[i]; - if (tomb->IsAlive() && tomb->HealthAbovePct(45)) - { - tombToMark = tomb; - break; - } - } - - if (tombToMark) - { - // Check if this tomb is already marked with our group's icon - ObjectGuid currentIcon = group->GetTargetIcon(iconIndex); - Unit* currentIconUnit = botAI->GetUnit(currentIcon); - if (!currentIconUnit || !currentIconUnit->IsAlive() || currentIconUnit != tombToMark) - { - // Mark the tomb with our group's target icon - group->SetTargetIcon(iconIndex, bot->GetGUID(), tombToMark->GetGUID()); - } - } - else - { - // No tombs above 45% HP, remove marker if one exists - ObjectGuid currentIcon = group->GetTargetIcon(iconIndex); - if (!currentIcon.IsEmpty()) - { - // Clear the marker for our group's icon - group->SetTargetIcon(iconIndex, bot->GetGUID(), ObjectGuid::Empty); - } - bot->AttackStop(); - return true; - } - } - - return false; -} - -// The Lich King -bool IccLichKingShadowTrapAction::Execute(Event /*event*/) -{ - Unit* boss = AI_VALUE2(Unit*, "find target", "the lich king"); - if (!boss || !botAI->IsTank(bot)) - return false; - - Difficulty diff = bot->GetRaidDifficulty(); - - if (sPlayerbotAIConfig.EnableICCBuffs && diff && (diff == RAID_DIFFICULTY_10MAN_HEROIC || diff == RAID_DIFFICULTY_25MAN_HEROIC)) - { - //-------CHEAT------- - if (!bot->HasAura(SPELL_EXPERIENCED)) - bot->AddAura(SPELL_EXPERIENCED, bot); - - if (!bot->HasAura(SPELL_AGEIS_OF_DALARAN)) - bot->AddAura(SPELL_AGEIS_OF_DALARAN, bot); - - if (!bot->HasAura(SPELL_NO_THREAT) && !botAI->IsTank(bot)) - bot->AddAura(SPELL_NO_THREAT, bot); - - if (!bot->HasAura(SPELL_PAIN_SUPPRESION)) - bot->AddAura(SPELL_PAIN_SUPPRESION, bot); - //-------CHEAT------- - } - - // Define ICC_LICH_POSITION and circle parameters - const float X = ICC_LICH_KING_ASSISTHC_POSITION.GetPositionX(); - const float Y = ICC_LICH_KING_ASSISTHC_POSITION.GetPositionY(); - const float Z = ICC_LICH_KING_ASSISTHC_POSITION.GetPositionZ(); - const float CIRCLE_RADIUS = 20.0f; - const float SAFE_DISTANCE = 12.0f; - const int TEST_POSITIONS = 16; - const float ANGLE_STEP = 2 * M_PI / TEST_POSITIONS; - - // Find all nearby shadow traps - GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); - std::vector trapGuids; - for (auto& npc : npcs) - { - Unit* unit = botAI->GetUnit(npc); - if (!unit || !unit->IsAlive() || unit->GetEntry() != NPC_SHADOW_TRAP) - continue; - if (bot->GetDistance(unit) < SAFE_DISTANCE + 5.0f) - { - trapGuids.push_back(npc); - } - } - - if (trapGuids.empty()) - return false; - - // Check if current position is already safe - bool currentPositionSafe = true; - for (auto& trapGuid : trapGuids) - { - Unit* trap = botAI->GetUnit(trapGuid); - if (!trap) - continue; - if (bot->GetDistance(trap) < SAFE_DISTANCE) - { - currentPositionSafe = false; - break; - } - } - - // If current position is safe, no need to move - if (currentPositionSafe) - return false; - - // Calculate current angle relative to ICC_LICH_POSITION - float currentX = bot->GetPositionX() - X; - float currentY = bot->GetPositionY() - Y; - float currentAngle = atan2(currentY, currentX); - - // Test clockwise positions first, then opposite position - std::vector testAngles; - // Add clockwise positions - for (int i = 1; i <= TEST_POSITIONS; ++i) - { - testAngles.push_back(currentAngle - (ANGLE_STEP * i)); - } - // Add opposite position as fallback - testAngles.push_back(currentAngle + M_PI); - - // Test all positions - for (float testAngle : testAngles) - { - // Calculate position on circle - float testX = X + cos(testAngle) * CIRCLE_RADIUS; - float testY = Y + sin(testAngle) * CIRCLE_RADIUS; - float testZ = Z; - - // Update Z coordinate for terrain - bot->UpdateAllowedPositionZ(testX, testY, testZ); - - // Check line of sight - if (!bot->IsWithinLOS(testX, testY, testZ)) - continue; - - // Check if this position is safe from all traps - bool isSafe = true; - for (auto& trapGuid : trapGuids) - { - Unit* trap = botAI->GetUnit(trapGuid); - if (!trap) - continue; - float distToTrap = trap->GetDistance2d(testX, testY); - if (distToTrap < SAFE_DISTANCE) - { - isSafe = false; - break; - } - } - - // Found a safe spot - move there - if (isSafe) - { - // Remove botAI->Reset() as it might interfere with movement - MoveTo(bot->GetMapId(), testX, testY, testZ, false, false, false, true, MovementPriority::MOVEMENT_FORCED, - true, false); - } - } - - // No safe position found - return false; -} - -bool IccLichKingNecroticPlagueAction::Execute(Event /*event*/) -{ - bool hasPlague = botAI->HasAura("Necrotic Plague", bot); - // Only execute if we have the plague - if (!hasPlague) - return false; - - // Find closest shambling horror - GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); - Unit* closestHorror = nullptr; - float minHorrorDist = 100.0f; - - for (auto& npc : npcs) - { - Unit* unit = botAI->GetUnit(npc); - if (!unit || !unit->IsAlive()) - continue; - - uint32 entry = unit->GetEntry(); - if (entry == NPC_SHAMBLING_HORROR1 || entry == NPC_SHAMBLING_HORROR2 || - entry == NPC_SHAMBLING_HORROR3 || entry == NPC_SHAMBLING_HORROR4) - { - float distance = bot->GetDistance(unit); - if (distance < minHorrorDist) - { - minHorrorDist = distance; - closestHorror = unit; - } - } - } - - // If we found a shambling horror, handle movement - if (closestHorror) - { - // If we're close enough, stop and return success - if (minHorrorDist <= 2.0f) - { - bot->StopMoving(); - return true; - } - - // We need to move to the horror - botAI->Reset(); - MoveTo(closestHorror, 2.0f, MovementPriority::MOVEMENT_FORCED); - - return false; // Still moving, not finished yet - } - - // No shambling horror found, but we have plague - this shouldn't happen normally - return false; -} - -bool IccLichKingWinterAction::Execute(Event /*event*/) -{ - Unit* boss = AI_VALUE2(Unit*, "find target", "the lich king"); - if (!boss) - return false; - - Unit* iceSphere = AI_VALUE2(Unit*, "find target", "ice sphere"); - - bool isVictim = false; - if (iceSphere && iceSphere->GetVictim() == bot && !botAI->IsTank(bot)) - isVictim = true; - - // First priority: Get out of Defile if we're in one - if (!IsPositionSafeFromDefile(bot->GetPositionX(), bot->GetPositionY(), 3.0f)) - { - // Find nearest safe position (use tank position as fallback) - const Position* safePos = botAI->IsTank(bot) ? GetMainTankPosition() : GetMainTankRangedPosition(); - TryMoveToPosition(safePos->GetPositionX(), safePos->GetPositionY(), 840.857f, true); - return true; - } - - float currentDistance = bot->GetDistance2d(boss); - - Difficulty diff = bot->GetRaidDifficulty(); - - if (sPlayerbotAIConfig.EnableICCBuffs && diff && (diff == RAID_DIFFICULTY_10MAN_HEROIC || diff == RAID_DIFFICULTY_25MAN_HEROIC)) - { - //------CHEAT------- - if (!bot->HasAura(SPELL_EXPERIENCED)) - bot->AddAura(SPELL_EXPERIENCED, bot); - - if (!bot->HasAura(SPELL_AGEIS_OF_DALARAN)) - bot->AddAura(SPELL_AGEIS_OF_DALARAN, bot); - - if (!bot->HasAura(SPELL_NO_THREAT) && !botAI->IsTank(bot)) - bot->AddAura(SPELL_NO_THREAT, bot); - - if (!bot->HasAura(SPELL_PAIN_SUPPRESION)) - bot->AddAura(SPELL_PAIN_SUPPRESION, bot); - //------CHEAT------- - } - - if (currentDistance < 35.0f && !bot->HasAura(SPELL_NITRO_BOOSTS)) - bot->AddAura(SPELL_NITRO_BOOSTS, bot); - - // Handle group target management - if (Group* group = bot->GetGroup()) - { - const ObjectGuid currentSkullTarget = group->GetTargetIcon(7); - if (!currentSkullTarget.IsEmpty()) - group->SetTargetIcon(7, bot->GetGUID(), ObjectGuid::Empty); - } - - if (isVictim) - MoveFromGroup(6.0f); - - if (!isVictim) - { - HandlePositionCorrection(); - - // Handle tank positioning and add management FIRST - HandleTankPositioning(); // New method that handles both main and assist tanks - - // Then handle other roles - HandleMeleePositioning(); - HandleRangedPositioning(); - } - - return false; -} - -void IccLichKingWinterAction::HandlePositionCorrection() -{ - Unit* boss = AI_VALUE2(Unit*, "find target", "the lich king"); - Unit* currentTarget = AI_VALUE(Unit*, "current target"); - - // Fix underground bug - if (abs(bot->GetPositionZ() - 840.857f) > 1.0f) - bot->TeleportTo(bot->GetMapId(), bot->GetPositionX(), bot->GetPositionY(), 840.857f, bot->GetOrientation()); - - // Reset targeting for specific conditions - if (currentTarget && boss && currentTarget == boss) - botAI->Reset(); - - if (botAI->IsTank(bot) && currentTarget && - ((currentTarget->GetEntry() == NPC_ICE_SPHERE1 || currentTarget->GetEntry() == NPC_ICE_SPHERE2 || - currentTarget->GetEntry() == NPC_ICE_SPHERE3 || currentTarget->GetEntry() == NPC_ICE_SPHERE4))) - botAI->Reset(); -} - -const Position* IccLichKingWinterAction::GetMainTankPosition() -{ - Unit* mainTank = AI_VALUE(Unit*, "main tank"); - if (!mainTank) - { - // FIXED: When no main tank, use the bot with lowest GUID to determine position - // This ensures ALL bots make the same decision collectively - - Unit* referenceBot = nullptr; - ObjectGuid lowestGuid; - - // Find the bot with lowest GUID in the group - Group* group = bot->GetGroup(); - if (group) - { - for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next()) - { - Player* member = itr->GetSource(); - if (member && member->IsAlive() && member->IsInWorld()) - { - if (lowestGuid.IsEmpty() || member->GetGUID() < lowestGuid) - { - lowestGuid = member->GetGUID(); - referenceBot = member; - } - } - } - } - - // If no group or reference bot found, fall back to current bot - if (!referenceBot) - referenceBot = bot; - - // Use the reference bot's position to determine closest tank position - float dist1 = - referenceBot->GetDistance2d(ICC_LK_FROST1_POSITION.GetPositionX(), ICC_LK_FROST1_POSITION.GetPositionY()); - float dist2 = - referenceBot->GetDistance2d(ICC_LK_FROST2_POSITION.GetPositionX(), ICC_LK_FROST2_POSITION.GetPositionY()); - float dist3 = - referenceBot->GetDistance2d(ICC_LK_FROST3_POSITION.GetPositionX(), ICC_LK_FROST3_POSITION.GetPositionY()); - - if (dist2 < dist1 && dist2 < dist3) - return &ICC_LK_FROST2_POSITION; - else if (dist3 < dist1 && dist3 < dist2) - return &ICC_LK_FROST3_POSITION; - else - return &ICC_LK_FROST1_POSITION; - } - - // Calculate which position the main tank is closest to - float dist1 = mainTank->GetDistance2d(ICC_LK_FROST1_POSITION.GetPositionX(), ICC_LK_FROST1_POSITION.GetPositionY()); - float dist2 = mainTank->GetDistance2d(ICC_LK_FROST2_POSITION.GetPositionX(), ICC_LK_FROST2_POSITION.GetPositionY()); - float dist3 = mainTank->GetDistance2d(ICC_LK_FROST3_POSITION.GetPositionX(), ICC_LK_FROST3_POSITION.GetPositionY()); - - if (dist2 < dist1 && dist2 < dist3) - return &ICC_LK_FROST2_POSITION; - else if (dist3 < dist1 && dist3 < dist2) - return &ICC_LK_FROST3_POSITION; - else - return &ICC_LK_FROST1_POSITION; -} - -const Position* IccLichKingWinterAction::GetMainTankRangedPosition() -{ - Unit* mainTank = AI_VALUE(Unit*, "main tank"); - if (!mainTank) - { - // FIXED: When no main tank, use the bot with lowest GUID to determine position - // This ensures ALL bots make the same decision collectively - - Unit* referenceBot = nullptr; - ObjectGuid lowestGuid; - - // Find the bot with lowest GUID in the group - Group* group = bot->GetGroup(); - if (group) - { - for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next()) - { - Player* member = itr->GetSource(); - if (member && member->IsAlive() && member->IsInWorld()) - { - if (lowestGuid.IsEmpty() || member->GetGUID() < lowestGuid) - { - lowestGuid = member->GetGUID(); - referenceBot = member; - } - } - } - } - - // If no group or reference bot found, fall back to current bot - if (!referenceBot) - referenceBot = bot; - - // Use the reference bot's position to determine closest ranged position - float dist1 = - referenceBot->GetDistance2d(ICC_LK_FROSTR1_POSITION.GetPositionX(), ICC_LK_FROSTR1_POSITION.GetPositionY()); - float dist2 = - referenceBot->GetDistance2d(ICC_LK_FROSTR2_POSITION.GetPositionX(), ICC_LK_FROSTR2_POSITION.GetPositionY()); - float dist3 = - referenceBot->GetDistance2d(ICC_LK_FROSTR3_POSITION.GetPositionX(), ICC_LK_FROSTR3_POSITION.GetPositionY()); - - if (dist2 < dist1 && dist2 < dist3) - return &ICC_LK_FROSTR2_POSITION; - else if (dist3 < dist1 && dist3 < dist2) - return &ICC_LK_FROSTR3_POSITION; - else - return &ICC_LK_FROSTR1_POSITION; - } - - // Map main tank's melee position to corresponding ranged position - const Position* tankMeleePos = GetMainTankPosition(); - - if (tankMeleePos == &ICC_LK_FROST1_POSITION) - return &ICC_LK_FROSTR1_POSITION; - else if (tankMeleePos == &ICC_LK_FROST2_POSITION) - return &ICC_LK_FROSTR2_POSITION; - else - return &ICC_LK_FROSTR3_POSITION; -} - -bool IccLichKingWinterAction::IsPositionSafeFromDefile(float x, float y, float minSafeDistance) -{ - Unit* boss = AI_VALUE2(Unit*, "find target", "the lich king"); - if (!boss) - return true; // No boss, assume safe - - GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); - const float BASE_RADIUS = 6.0f; - const float SAFETY_MARGIN = 3.0f; - - for (auto& npc : npcs) - { - Unit* unit = botAI->GetUnit(npc); - if (unit && unit->IsAlive() && unit->GetEntry() == DEFILE_NPC_ID) - { - // Calculate current defile radius including growth - float currentRadius = BASE_RADIUS; - Aura* growAura = nullptr; - - // Find growth aura (you'll need to define DEFILE_AURAS array) - for (size_t i = 0; i < DEFILE_AURA_COUNT; i++) - { - growAura = unit->GetAura(DEFILE_AURAS[i]); - if (growAura) - break; - } - - if (growAura) - { - uint8 stacks = growAura->GetStackAmount(); - float growthMultiplier = (bot->GetRaidDifficulty() == RAID_DIFFICULTY_10MAN_HEROIC || - bot->GetRaidDifficulty() == RAID_DIFFICULTY_10MAN_NORMAL) - ? 1.4f - : 0.95f; - currentRadius = BASE_RADIUS + (stacks * growthMultiplier); - } - - float dx = x - unit->GetPositionX(); - float dy = y - unit->GetPositionY(); - float distance = sqrt(dx * dx + dy * dy); - - if (distance < (currentRadius + SAFETY_MARGIN + minSafeDistance)) - return false; - } - } - return true; -} - -bool IccLichKingWinterAction::TryMoveToPosition(float targetX, float targetY, float targetZ, bool isForced) -{ - float currentX = bot->GetPositionX(); - float currentY = bot->GetPositionY(); - float currentZ = bot->GetPositionZ(); - - float dx = targetX - currentX; - float dy = targetY - currentY; - float dz = targetZ - currentZ; - float distance = sqrtf(dx * dx + dy * dy + dz * dz); - - if (distance < 0.1f) - return true; // Already at the position - - dx /= distance; - dy /= distance; - - // First check if direct path is safe - if (bot->IsWithinLOS(targetX, targetY, targetZ) && IsPositionSafeFromDefile(targetX, targetY, 3.0f)) - { - if (isForced) - botAI->Reset(); - - MoveTo(bot->GetMapId(), targetX, targetY, targetZ, false, false, false, true, MovementPriority::MOVEMENT_FORCED, - true, false); - return true; - } - - // If direct path isn't safe, try to find a safe path around defiles - const int MAX_ATTEMPTS = 8; - const float ANGLE_STEP = M_PI / 4.0f; - float attemptDistance = std::min(10.0f, distance); - - for (int i = 0; i < MAX_ATTEMPTS; i++) - { - float angle = i * ANGLE_STEP; - float offsetX = attemptDistance * cos(angle); - float offsetY = attemptDistance * sin(angle); - - // Try positions clockwise and counter-clockwise - for (int direction = -1; direction <= 1; direction += 2) - { - if (i == 0 && direction == 1) - continue; // Skip duplicate first attempt - - float testX = currentX + dx * attemptDistance + offsetX * direction; - float testY = currentY + dy * attemptDistance + offsetY * direction; - float testZ = targetZ; - - if (bot->IsWithinLOS(testX, testY, testZ) && IsPositionSafeFromDefile(testX, testY, 3.0f)) - { - if (isForced) - botAI->Reset(); - - MoveTo(bot->GetMapId(), testX, testY, testZ, false, false, false, true, - MovementPriority::MOVEMENT_FORCED, true, false); - return false; // Not at final position yet - } - } - } - - // If no safe path found, just move directly (better than standing in defile) - if (isForced) - botAI->Reset(); - - MoveTo(bot->GetMapId(), targetX, targetY, targetZ, false, false, false, true, MovementPriority::MOVEMENT_FORCED, - true, false); - return false; -} - -// Helper function to check if a unit is a valid collectible add -bool IccLichKingWinterAction::IsValidCollectibleAdd(Unit* unit) -{ - if (!unit || !unit->IsAlive()) - return false; - - uint32 entry = unit->GetEntry(); - - // Only spirits, shambling horrors, and ghouls are valid collectible adds - return (entry == NPC_SHAMBLING_HORROR1 || entry == NPC_SHAMBLING_HORROR2 || entry == NPC_SHAMBLING_HORROR3 || - entry == NPC_SHAMBLING_HORROR4 || entry == NPC_RAGING_SPIRIT1 || entry == NPC_RAGING_SPIRIT2 || - entry == NPC_RAGING_SPIRIT3 || entry == NPC_RAGING_SPIRIT4 || entry == NPC_DRUDGE_GHOUL1 || - entry == NPC_DRUDGE_GHOUL2 || entry == NPC_DRUDGE_GHOUL3 || entry == NPC_DRUDGE_GHOUL4); -} - -// FIXED HandleTankPositioning method -void IccLichKingWinterAction::HandleTankPositioning() -{ - if (!botAI->IsTank(bot)) - return; - - Unit* boss = AI_VALUE2(Unit*, "find target", "the lich king"); - if (!boss) - return; - - // Get the target position based on main tank's choice - const Position* targetPos = GetMainTankPosition(); - - // First check if current position is safe - if (!IsPositionSafeFromDefile(bot->GetPositionX(), bot->GetPositionY(), 3.0f)) - { - // If in defile, prioritize getting out - TryMoveToPosition(targetPos->GetPositionX(), targetPos->GetPositionY(), 840.857f, true); - return; - } - - float distToTarget = bot->GetDistance2d(targetPos->GetPositionX(), targetPos->GetPositionY()); - - // MAIN TANK: Always stay at tank position - if (botAI->IsMainTank(bot)) - { - // Main tank should always maintain position at tank spot - if (distToTarget > 2.0f) - { - float targetX = targetPos->GetPositionX(); - float targetY = targetPos->GetPositionY(); - float targetZ = 840.857f; - - TryMoveToPosition(targetX, targetY, targetZ, true); - return; // Don't do add management until in position - } - - // Once in position, handle add management from tank position - HandleMainTankAddManagement(targetPos); - } - // ASSIST TANK: More flexible positioning based on add collection - else if (botAI->IsAssistTank(bot)) - { - // First ensure we're reasonably close to tank area - if (distToTarget > 15.0f) - { - float targetX = targetPos->GetPositionX() + 3.0f; // Slight offset from main tank - float targetY = targetPos->GetPositionY() + 2.0f; - float targetZ = 840.857f; - - TryMoveToPosition(targetX, targetY, targetZ, true); - return; - } - - // Handle assist tank add collection and positioning - HandleAssistTankAddManagement(targetPos); - } -} - -// Updated HandleMeleePositioning method - only for non-tanks -void IccLichKingWinterAction::HandleMeleePositioning() -{ - // Skip if this is a tank - they have their own positioning logic - if (botAI->IsTank(bot)) - return; - Unit* currentTarget = AI_VALUE(Unit*, "current target"); - // Handle melee positioning behind target (for DPS only) - if (currentTarget && !botAI->IsRanged(bot) && currentTarget->isInFront(bot) && currentTarget->IsAlive() && - currentTarget->GetEntry() != NPC_THE_LICH_KING && currentTarget->GetEntry() != NPC_ICE_SPHERE1 && - currentTarget->GetEntry() != NPC_ICE_SPHERE2 && currentTarget->GetEntry() != NPC_ICE_SPHERE3 && currentTarget->GetEntry() != NPC_ICE_SPHERE4) - { - // Calculate desired position (4.0f behind the target) - float orientation = currentTarget->GetOrientation() + M_PI + M_PI / 8; - float x = currentTarget->GetPositionX(); - float y = currentTarget->GetPositionY(); - float z = bot->GetPositionZ(); - float targetX = x + cos(orientation) * 4.0f; - float targetY = y + sin(orientation) * 4.0f; - Position botPos = bot->GetPosition(); - float dx = targetX - botPos.GetPositionX(); - float dy = targetY - botPos.GetPositionY(); - float distance = sqrt(dx * dx + dy * dy); - - if (distance <= 1.0f) - return; - - // Move in increments of 2 yards toward the target position - float moveDistance = std::min(2.0f, distance); - float normalizedDx = dx / distance; - float normalizedDy = dy / distance; - - float newX = botPos.GetPositionX() + normalizedDx * moveDistance; - float newY = botPos.GetPositionY() + normalizedDy * moveDistance; - - TryMoveToPosition(newX, newY, z, false); - } - // Handle non-ranged DPS positioning - USE MAIN TANK'S POSITION - if (!botAI->IsRanged(bot)) - { - Unit* boss = AI_VALUE2(Unit*, "find target", "the lich king"); - const Position* targetPos = GetMainTankPosition(); - float distToTarget = bot->GetDistance2d(targetPos->GetPositionX(), targetPos->GetPositionY()); - if (distToTarget > 8.0f) - { - float targetX = targetPos->GetPositionX() - 5.0f; - float targetY = targetPos->GetPositionY() + 5.0f; - float targetZ = 840.857f; - if (boss && !boss->HealthAbovePct(50)) - { - targetX = targetPos->GetPositionX(); - targetY = targetPos->GetPositionY(); - targetZ = 840.857f; - } - - // Move in increments of 2 yards toward the tank position - Position botPos = bot->GetPosition(); - float dx = targetX - botPos.GetPositionX(); - float dy = targetY - botPos.GetPositionY(); - float distance = sqrt(dx * dx + dy * dy); - - if (distance > 2.0f) - { - float normalizedDx = dx / distance; - float normalizedDy = dy / distance; - - float newX = botPos.GetPositionX() + normalizedDx * 2.0f; - float newY = botPos.GetPositionY() + normalizedDy * 2.0f; - - TryMoveToPosition(newX, newY, targetZ); - } - else - { - TryMoveToPosition(targetX, targetY, targetZ); - } - } - } -} - -// Updated HandleRangedPositioning method -void IccLichKingWinterAction::HandleRangedPositioning() -{ - if (!botAI->IsRanged(bot)) - return; - - // Get the ranged position based on main tank's choice - const Position* targetPos = GetMainTankRangedPosition(); - - // First check if current position is safe - if (!IsPositionSafeFromDefile(bot->GetPositionX(), bot->GetPositionY(), 3.0f)) - { - // If in defile, prioritize getting out - TryMoveToPosition(targetPos->GetPositionX(), targetPos->GetPositionY(), 840.857f, true); - return; - } - - float distToTarget = bot->GetDistance2d(targetPos->GetPositionX(), targetPos->GetPositionY()); - - if (distToTarget > 2.0f) - { - float targetX = targetPos->GetPositionX(); - float targetY = targetPos->GetPositionY(); - float targetZ = 840.857f; - - TryMoveToPosition(targetX, targetY, targetZ); - } - - // Handle sphere targeting for ranged DPS - if (botAI->IsRangedDps(bot)) - { - bool hasHunter = false; - Group* group = bot->GetGroup(); - if (group) - { - for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next()) - { - Player* member = itr->GetSource(); - if (member && member->IsAlive() && member->IsInWorld() && member->getClass() == CLASS_HUNTER) - { - hasHunter = true; - break; - } - } - } - - if (bot->getClass() == CLASS_HUNTER || !hasHunter) - { - Unit* currentTarget = bot->GetVictim(); - if (currentTarget && currentTarget->IsAlive()) - { - uint32 entry = currentTarget->GetEntry(); - if (entry == NPC_ICE_SPHERE1 || entry == NPC_ICE_SPHERE2 || entry == NPC_ICE_SPHERE3 || entry == NPC_ICE_SPHERE4) - { - bot->SetFacingToObject(currentTarget); - Attack(currentTarget); - return; - } - } - - Unit* closestSphere = nullptr; - float closestDist = 100.0f; - GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); - for (auto& npc : npcs) - { - Unit* unit = botAI->GetUnit(npc); - if (!unit || !unit->IsAlive()) - continue; - uint32 entry = unit->GetEntry(); - if (entry == NPC_ICE_SPHERE1 || entry == NPC_ICE_SPHERE2 || entry == NPC_ICE_SPHERE3 || entry == NPC_ICE_SPHERE4) - { - float dist = bot->GetDistance(unit); - if (!closestSphere || dist < closestDist) - { - closestSphere = unit; - closestDist = dist; - } - } - } - if (closestSphere) - { - bot->SetTarget(closestSphere->GetGUID()); - bot->SetFacingToObject(closestSphere); - Attack(closestSphere); - } - } - } -} - -void IccLichKingWinterAction::HandleMainTankAddManagement(const Position* tankPos) -{ - if (!botAI->IsMainTank(bot)) - return; - - // First, ensure we're at the correct tank position - float distToTankPos = bot->GetDistance2d(tankPos->GetPositionX(), tankPos->GetPositionY()); - if (distToTankPos > 3.0f) - { - TryMoveToPosition(tankPos->GetPositionX(), tankPos->GetPositionY(), 840.857f, true); - return; // Wait until we're in position - } - - // Get all valid adds in the encounter area - GuidVector targets = AI_VALUE(GuidVector, "possible targets"); - std::vector validAdds; - Unit* currentTarget = bot->GetVictim(); - - // Collect all valid adds - for (auto i = targets.begin(); i != targets.end(); ++i) - { - Unit* unit = botAI->GetUnit(*i); - if (!IsValidCollectibleAdd(unit)) - continue; - - validAdds.push_back(unit); - } - - // If we have no adds, clear target if needed - if (validAdds.empty()) - { - if (currentTarget && !IsValidCollectibleAdd(currentTarget)) - { - bot->SetTarget(ObjectGuid::Empty); - } - return; - } - - // Strategy for add management: - // 1. First priority: Adds attacking non-tanks - // 2. Second priority: Adds not attacking us - // 3. Third priority: All other valid adds - Unit* priorityAdd = nullptr; - Unit* secondaryAdd = nullptr; - Unit* otherAdd = nullptr; - - for (Unit* add : validAdds) - { - Unit* addVictim = add->GetVictim(); - - // Highest priority: Adds attacking non-tanks - if (addVictim && addVictim->IsPlayer() && !botAI->IsTank(addVictim->ToPlayer())) - { - if (!priorityAdd || bot->GetDistance(add) < bot->GetDistance(priorityAdd)) - { - priorityAdd = add; - } - continue; - } - - // Medium priority: Adds not attacking us - if (addVictim != bot) - { - if (!secondaryAdd || bot->GetDistance(add) < bot->GetDistance(secondaryAdd)) - { - secondaryAdd = add; - } - continue; - } - - // Lowest priority: All other valid adds - if (!otherAdd || bot->GetDistance(add) < bot->GetDistance(otherAdd)) - { - otherAdd = add; - } - } - - // Select the highest priority add available - Unit* targetAdd = priorityAdd ? priorityAdd : (secondaryAdd ? secondaryAdd : otherAdd); - - if (targetAdd) - { - float addDist = bot->GetDistance(targetAdd); - - // If add is close enough (within melee range), attack it - if (addDist < 10.0f) - { - // If we're not already attacking this add, switch to it - if (currentTarget != targetAdd) - { - bot->SetTarget(targetAdd->GetGUID()); - bot->SetFacingToObject(targetAdd); - Attack(targetAdd); - } - } - else - { - // Add is too far - move toward it while staying near tank position - float moveX = targetAdd->GetPositionX(); - float moveY = targetAdd->GetPositionY(); - - // Don't move too far from tank position (max 15 yards) - float maxPullDistance = 15.0f; - float pullRatio = std::min(1.0f, maxPullDistance / addDist); - - float adjustedX = tankPos->GetPositionX() + (moveX - tankPos->GetPositionX()) * pullRatio; - float adjustedY = tankPos->GetPositionY() + (moveY - tankPos->GetPositionY()) * pullRatio; - - TryMoveToPosition(adjustedX, adjustedY, 840.857f, false); - - // Still try to attack while moving - bot->SetTarget(targetAdd->GetGUID()); - bot->SetFacingToObject(targetAdd); - Attack(targetAdd); - } - } - else if (currentTarget && !IsValidCollectibleAdd(currentTarget)) - { - // Clear invalid target - bot->SetTarget(ObjectGuid::Empty); - } -} - -void IccLichKingWinterAction::HandleAssistTankAddManagement(const Position* tankPos) -{ - if (!botAI->IsAssistTank(bot)) - return; - - Unit* mainTank = AI_VALUE(Unit*, "main tank"); - if (!mainTank) - return; - - // Look for priority adds that need to be collected - Unit* targetAdd = nullptr; - float closestDist = FLT_MAX; - bool foundPriorityAdd = false; - - GuidVector targets = AI_VALUE(GuidVector, "possible targets"); - - // Priority 1: Adds attacking non-tanks (players/healers) - for (auto i = targets.begin(); i != targets.end(); ++i) - { - Unit* unit = botAI->GetUnit(*i); - if (!IsValidCollectibleAdd(unit)) - continue; - - Unit* addVictim = unit->GetVictim(); - if (addVictim && addVictim->IsPlayer() && !botAI->IsTank(addVictim->ToPlayer())) - { - float addDist = bot->GetDistance(unit); - if (addDist < closestDist) - { - targetAdd = unit; - closestDist = addDist; - foundPriorityAdd = true; - } - } - } - - // Priority 2: Adds not attacking main tank (if no priority adds found) - if (!foundPriorityAdd) - { - closestDist = FLT_MAX; - for (auto i = targets.begin(); i != targets.end(); ++i) - { - Unit* unit = botAI->GetUnit(*i); - if (!IsValidCollectibleAdd(unit)) - continue; - - // Only target adds that are NOT attacking the main tank - if (unit->GetVictim() != mainTank) - { - float addDist = bot->GetDistance(unit); - if (addDist < closestDist) - { - targetAdd = unit; - closestDist = addDist; - } - } - } - } - - if (targetAdd) - { - // Calculate position between add and main tank - float pullX = (targetAdd->GetPositionX() + tankPos->GetPositionX()) / 2; - float pullY = (targetAdd->GetPositionY() + tankPos->GetPositionY()) / 2; - - // Move to intercept position if add is far from main tank - if (targetAdd->GetDistance2d(tankPos->GetPositionX(), tankPos->GetPositionY()) > 10.0f) - { - if (bot->GetDistance2d(pullX, pullY) > 3.0f) - { - TryMoveToPosition(pullX, pullY, 840.857f, false); - } - } - // Otherwise move toward the add - else if (closestDist > 5.0f) - { - TryMoveToPosition(targetAdd->GetPositionX(), targetAdd->GetPositionY(), 840.857f, false); - } - - // Attack the add - bot->SetTarget(targetAdd->GetGUID()); - bot->SetFacingToObject(targetAdd); - Attack(targetAdd); - } - else - { - // No adds to collect, position near main tank - float distToTankPos = bot->GetDistance2d(tankPos->GetPositionX(), tankPos->GetPositionY()); - if (distToTankPos > 2.0f) - { - float X = mainTank->GetPositionX(); - float Y = mainTank->GetPositionY(); - TryMoveToPosition(X, Y, 840.857f, false); - } - - // Check current target validity - Unit* currentTarget = bot->GetVictim(); - if (currentTarget && !IsValidCollectibleAdd(currentTarget)) - { - bot->SetTarget(ObjectGuid::Empty); - } - } -} - -bool IccLichKingAddsAction::Execute(Event /*event*/) -{ - if (bot->HasAura(SPELL_HARVEST_SOUL_VALKYR)) // Don't process actions if bot is picked up by Val'kyr - return false; - - Unit* boss = AI_VALUE2(Unit*, "find target", "the lich king"); - - Difficulty diff = bot->GetRaidDifficulty(); - - if (sPlayerbotAIConfig.EnableICCBuffs && diff && (diff == RAID_DIFFICULTY_10MAN_HEROIC || diff == RAID_DIFFICULTY_25MAN_HEROIC)) - { - //------CHEAT------- - if (!bot->HasAura(SPELL_EXPERIENCED)) - bot->AddAura(SPELL_EXPERIENCED, bot); - - if (!bot->HasAura(SPELL_AGEIS_OF_DALARAN)) - bot->AddAura(SPELL_AGEIS_OF_DALARAN, bot); - - if (boss && boss->HealthBelowPct(60) && boss->HealthAbovePct(40) && !bot->HasAura(SPELL_EMPOWERED_BLOOD)) - bot->AddAura(SPELL_EMPOWERED_BLOOD, bot); - - if (!bot->HasAura(SPELL_NO_THREAT) && !botAI->IsTank(bot)) - bot->AddAura(SPELL_NO_THREAT, bot); - - if (!bot->HasAura(SPELL_PAIN_SUPPRESION)) - bot->AddAura(SPELL_PAIN_SUPPRESION, bot); - //------CHEAT------- - } - - bool hasPlague = botAI->HasAura("Necrotic Plague", bot); - Unit* terenasMenethilHC = bot->FindNearestCreature(NPC_TERENAS_MENETHIL_HC, 55.0f); - - Group* group = bot->GetGroup(); - if (group && boss && boss->HealthAbovePct(71)) - { - constexpr uint8_t skullIconId = 7; - ObjectGuid skullGuid = group->GetTargetIcon(skullIconId); - if (skullGuid != boss->GetGUID()) - group->SetTargetIcon(skullIconId, bot->GetGUID(), boss->GetGUID()); - } - - //-----------Valkyr bot suicide if group fails to kill Valkyr in time------------- comment out if you dont want it - if (bot->HasAura(30440)) // Random aura tracking whether bot has fallen off edge / been thrown by Val'kyr - { - if (bot->GetPositionZ() > 779.0f) - return JumpTo(bot->GetMapId(), bot->GetPositionX(), bot->GetPositionY(), 740.01f); - else - { - bot->Kill(bot, bot); // If bot has jumped past the kill Z (780), **Now it is fixed and bots will actually die instead of beeing frozen** - return true; - } - } - - bool hasWinterAura = false; - if (boss && (boss->HasAura(SPELL_REMORSELESS_WINTER1) || boss->HasAura(SPELL_REMORSELESS_WINTER2) || - boss->HasAura(SPELL_REMORSELESS_WINTER3) || boss->HasAura(SPELL_REMORSELESS_WINTER4))) - hasWinterAura = true; - - bool hasWinter2Aura = false; - if (boss && (boss->HasAura(SPELL_REMORSELESS_WINTER5) || boss->HasAura(SPELL_REMORSELESS_WINTER6) || - boss->HasAura(SPELL_REMORSELESS_WINTER7) || boss->HasAura(SPELL_REMORSELESS_WINTER8))) - hasWinter2Aura = true; - - if (boss && boss->GetHealthPct() < 70 && boss->GetHealthPct() > 40 && !hasWinterAura && - !hasWinter2Aura) // If boss is in p2, check if bot has been thrown off platform - { - float dx = bot->GetPositionX() - 503.0f; - float dy = bot->GetPositionY() - (-2124.0f); - float distance = sqrt(dx * dx + dy * dy); // Calculate distance from the center of the platform - - if (distance > 52.0f && distance < 70.0f && - bot->GetPositionZ() > 844) // If bot has fallen off edge, distance is over 52 - { - bot->AddAura(30440, bot); // Apply random 30 sec aura to track that we've initiated a jump - return JumpTo(bot->GetMapId(), bot->GetPositionX(), bot->GetPositionY(), - 740.01f); // Start jumping to the abyss - } - } - //-----------Valkyr bot suicide if group fails to kill Valkyr in time------------- comment out if you dont want it - - // Handle teleportation fixes - HandleTeleportationFixes(diff, terenasMenethilHC); - - // Handle heroic mode spirit bomb avoidance for main tank - if (HandleSpiritBombAvoidance(diff, terenasMenethilHC)) - return true; - - // Handle non-main tank positioning in heroic mode - HandleHeroicNonTankPositioning(diff, terenasMenethilHC); - - // Handle spirit marking and targeting in heroic mode - HandleSpiritMarkingAndTargeting(diff, terenasMenethilHC); - - if (terenasMenethilHC) - return false; - - // Handle quake mechanics - if (HandleQuakeMechanics(boss)) - return true; - - // Handle shambling horror interactions - HandleShamblingHorrors(); - - // Handle assist tank add management - if (HandleAssistTankAddManagement(boss, diff)) - return true; - - // Handle melee positioning - HandleMeleePositioning(boss, hasPlague, diff); - - // Handle main tank targeting in heroic - HandleMainTankTargeting(boss, diff); - - // Handle non-tank positioning in heroic - HandleNonTankHeroicPositioning(boss, diff, hasPlague); - - // Handle ranged positioning - HandleRangedPositioning(boss, hasPlague, diff); - - // Handle defile mechanics - HandleDefileMechanics(boss, diff); - - // Handle Val'kyr mechanics - HandleValkyrMechanics(diff); - - // Handle vile spirit mechanics - HandleVileSpiritMechanics(); - - return false; -} - -void IccLichKingAddsAction::HandleTeleportationFixes(Difficulty diff, Unit* terenasMenethilHC) -{ - // temp soultion for bots when they get teleport far away into another dimension (they are unable to attack spirit - // warden) in heroic they will be in safe spot while player is surviving vile spirits - if (!(diff == RAID_DIFFICULTY_10MAN_HEROIC || diff == RAID_DIFFICULTY_25MAN_HEROIC) && - abs(bot->GetPositionY() - -2095.7915f) > 200.0f) - { - bot->TeleportTo(bot->GetMapId(), ICC_LICH_KING_ADDS_POSITION.GetPositionX(), - ICC_LICH_KING_ADDS_POSITION.GetPositionY(), ICC_LICH_KING_ADDS_POSITION.GetPositionZ(), - bot->GetOrientation()); - } - - // temp solution for bots going underground due to buggy ice platfroms and adds that go underground - if (abs(bot->GetPositionZ() - 840.857f) > 1.0f && !botAI->GetAura("Harvest Soul", bot, false, false) && - !botAI->GetAura("Harvest Souls", bot, false, false)) - bot->TeleportTo(bot->GetMapId(), bot->GetPositionX(), bot->GetPositionY(), 840.857f, bot->GetOrientation()); - - if (abs(bot->GetPositionZ() - 1049.865f) > 5.0f && botAI->GetAura("Harvest Soul", bot, false, false) && - terenasMenethilHC) - bot->TeleportTo(bot->GetMapId(), terenasMenethilHC->GetPositionX(), terenasMenethilHC->GetPositionY(), 1049.865f, - bot->GetOrientation()); -} - -bool IccLichKingAddsAction::HandleSpiritBombAvoidance(Difficulty diff, Unit* terenasMenethilHC) -{ - if (!botAI->IsMainTank(bot) || !terenasMenethilHC || !diff || - !(diff == RAID_DIFFICULTY_10MAN_HEROIC || diff == RAID_DIFFICULTY_25MAN_HEROIC)) - return false; - - std::map spiritBombs; - - // Gather all spirit bombs using their GUIDs for reliable tracking - GuidVector npcs1 = AI_VALUE(GuidVector, "nearest hostile npcs"); - for (auto& npcGuid : npcs1) - { - Unit* unit = botAI->GetUnit(npcGuid); - if (unit && unit->IsAlive() && unit->GetEntry() == NPC_SPIRIT_BOMB) - { - spiritBombs[npcGuid] = unit; - } - } - - // Only proceed if there are actually spirit bombs present - if (spiritBombs.empty()) - return false; - - const float SAFE_DISTANCE = 14.0f; // Minimum safe horizontal distance - const float SAFE_HEIGHT_DIFF = 12.0f; // Safe if Z difference is greater than this - const float BOMB_DENSITY_RADIUS = 10.0f; // Radius for counting nearby bombs - const float BOMB_COUNT_PENALTY = 10.0f; // Score penalty per bomb in vicinity - const float MAX_HEIGHT_DIFF = 8.0f; // Increased from 5.0f for more flexibility - - // First check if current position is already safe - bool currentPositionSafe = true; - float minDistanceToAnyBomb = std::numeric_limits::max(); - - // Clean up invalid bombs and check current position safety - auto it = spiritBombs.begin(); - while (it != spiritBombs.end()) - { - Unit* verifiedBomb = botAI->GetUnit(it->first); - if (!verifiedBomb || !verifiedBomb->IsAlive() || verifiedBomb->GetEntry() != NPC_SPIRIT_BOMB) - { - it = spiritBombs.erase(it); - continue; - } - - float dx = bot->GetPositionX() - verifiedBomb->GetPositionX(); - float dy = bot->GetPositionY() - verifiedBomb->GetPositionY(); - float dz = bot->GetPositionZ() - verifiedBomb->GetPositionZ(); - float horizontalDistance = sqrt(dx * dx + dy * dy); - float verticalDistance = fabs(dz); - - minDistanceToAnyBomb = std::min(minDistanceToAnyBomb, horizontalDistance); - - // Position is dangerous if horizontally close AND not high enough above/below - if (horizontalDistance < SAFE_DISTANCE && verticalDistance <= SAFE_HEIGHT_DIFF) - { - currentPositionSafe = false; - } - ++it; - } - - // If no valid bombs remain after cleanup, exit early - if (spiritBombs.empty()) - { - return false; - } - - // Only move if current position is unsafe - if (!currentPositionSafe) - { - float bestScore = -std::numeric_limits::max(); - float bestX = 0, bestY = 0, bestZ = 0; - bool foundSafePosition = false; - - // Multi-distance search to avoid getting stuck - std::vector searchDistances = {6.0f, 10.0f, 15.0f, 20.0f, 25.0f}; - - for (float searchDistance : searchDistances) - { - // Try 36 different angles for thorough coverage - for (int i = 0; i < 36; i++) - { - float testAngle = i * 2 * M_PI / 36; - - float testX = bot->GetPositionX() + searchDistance * cos(testAngle); - float testY = bot->GetPositionY() + searchDistance * sin(testAngle); - float testZ = bot->GetPositionZ(); - - bot->UpdateAllowedPositionZ(testX, testY, testZ); - - // More lenient LOS and height checks - bool validPosition = true; - float heightDiff = fabs(testZ - bot->GetPositionZ()); - - // Skip positions that are too high/low or not in LOS - if (heightDiff >= MAX_HEIGHT_DIFF) - { - validPosition = false; - } - - // Only check LOS if height difference is reasonable - if (validPosition && !bot->IsWithinLOS(testX, testY, testZ)) - { - validPosition = false; - } - - if (!validPosition) - continue; - - // Check position safety and bomb density - bool positionSafe = true; - float minDistAtPos = std::numeric_limits::max(); - int bombCountInVicinity = 0; - - for (auto const& bombPair : spiritBombs) - { - Unit* bomb = bombPair.second; - if (!bomb || !bomb->IsAlive()) - continue; - - float dx = testX - bomb->GetPositionX(); - float dy = testY - bomb->GetPositionY(); - float dz = testZ - bomb->GetPositionZ(); - float horizontalDist = sqrt(dx * dx + dy * dy); - float verticalDist = fabs(dz); - - // Track minimum distance to any bomb - minDistAtPos = std::min(minDistAtPos, horizontalDist); - - // Count bombs within density radius - if (horizontalDist < BOMB_DENSITY_RADIUS) - { - bombCountInVicinity++; - } - - // Check safety condition - if (horizontalDist < SAFE_DISTANCE && verticalDist <= SAFE_HEIGHT_DIFF) - { - positionSafe = false; - break; - } - } - - // Skip unsafe positions - if (!positionSafe) - continue; - - // Calculate composite score with distance bonus to prefer closer safe positions - float distanceBonus = std::max(0.0f, 30.0f - searchDistance); // Prefer closer positions - float score = minDistAtPos - (bombCountInVicinity * BOMB_COUNT_PENALTY) + distanceBonus; - - // Update best position if this one is better - if (score > bestScore) - { - bestScore = score; - bestX = testX; - bestY = testY; - bestZ = testZ; - foundSafePosition = true; - } - } - - // If we found a safe position at this distance, use it (prefer closer positions) - if (foundSafePosition && searchDistance <= 15.0f) - break; - } - - // Move to the best position found - if (foundSafePosition) - { - // Final validation before moving - if (bot->IsWithinLOS(bestX, bestY, bestZ) && fabs(bestZ - bot->GetPositionZ()) <= MAX_HEIGHT_DIFF) - { - MoveTo(bot->GetMapId(), bestX, bestY, bestZ, false, false, false, true, - MovementPriority::MOVEMENT_FORCED); - } - } - } - return true; -} - -void IccLichKingAddsAction::HandleHeroicNonTankPositioning(Difficulty diff, Unit* terenasMenethilHC) -{ - if (!terenasMenethilHC || botAI->IsMainTank(bot) || !diff || - !(diff == RAID_DIFFICULTY_10MAN_HEROIC || diff == RAID_DIFFICULTY_25MAN_HEROIC)) - return; - - Unit* mainTank = AI_VALUE(Unit*, "main tank"); - // Only move if significantly far from main tank (increased threshold to reduce jittery movement) - if (mainTank && bot->GetExactDist2d(mainTank->GetPositionX(), mainTank->GetPositionY()) > 2.0f) - { - MoveTo(bot->GetMapId(), mainTank->GetPositionX(), mainTank->GetPositionY(), mainTank->GetPositionZ(), false, - false, false, true, MovementPriority::MOVEMENT_FORCED); - } -} - -void IccLichKingAddsAction::HandleSpiritMarkingAndTargeting(Difficulty diff, Unit* terenasMenethilHC) -{ - if (!terenasMenethilHC || botAI->IsMainTank(bot) || !diff || - !(diff == RAID_DIFFICULTY_10MAN_HEROIC || diff == RAID_DIFFICULTY_25MAN_HEROIC)) - return; - - Group* group = bot->GetGroup(); - if (!group) - return; - - static constexpr uint8_t STAR_ICON_INDEX = 0; - static constexpr float MAX_Z_DIFF = 20.0f; - - // Check if current marked target is still valid and threatening - Unit* currentMarkedTarget = botAI->GetUnit(group->GetTargetIcon(STAR_ICON_INDEX)); - bool needNewMark = !currentMarkedTarget || !currentMarkedTarget->IsAlive(); - - // Check if current marked spirit is targeting a group member - bool currentTargetingGroupMember = false; - if (currentMarkedTarget && currentMarkedTarget->IsAlive()) - { - Unit* spiritTarget = currentMarkedTarget->GetVictim(); - if (spiritTarget && spiritTarget->IsPlayer()) - { - if (Group* spiritTargetGroup = spiritTarget->ToPlayer()->GetGroup()) - { - if (spiritTargetGroup->GetGUID() == group->GetGUID()) - { - currentTargetingGroupMember = true; - } - } - } - } - - // Only search for new target if we need to mark OR if we can find a higher priority target - if (needNewMark || !currentTargetingGroupMember) - { - Unit* prioritySpirit = nullptr; // Spirit targeting group member - Unit* nearestSpirit = nullptr; // Fallback: nearest spirit - float priorityDist = 100.0f; - float nearestDist = 100.0f; - - GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); - for (auto& npc : npcs) - { - Unit* unit = botAI->GetUnit(npc); - if (!unit || !unit->IsAlive() || !unit->isTargetableForAttack()) - continue; - - uint32 entry = unit->GetEntry(); - if (entry == NPC_WICKED_SPIRIT1 || entry == NPC_WICKED_SPIRIT2 || entry == NPC_WICKED_SPIRIT3 || - entry == NPC_WICKED_SPIRIT4) - { - // Check Z-axis difference first - float zDiff = std::abs(unit->GetPositionZ() - bot->GetPositionZ()); - if (zDiff <= MAX_Z_DIFF) - { - float dist = bot->GetDistance(unit->GetPositionX(), unit->GetPositionY(), unit->GetPositionZ()); - - // Check if this spirit is targeting a group member - bool targetingGroupMember = false; - Unit* spiritTarget = unit->GetVictim(); - if (spiritTarget && spiritTarget->IsPlayer()) - { - if (Group* spiritTargetGroup = spiritTarget->ToPlayer()->GetGroup()) - { - if (spiritTargetGroup->GetGUID() == group->GetGUID()) - { - targetingGroupMember = true; - } - } - } - - // Priority: spirits targeting group members - if (targetingGroupMember) - { - if (!prioritySpirit || dist < priorityDist) - { - prioritySpirit = unit; - priorityDist = dist; - } - } - - // Fallback: track nearest spirit regardless of target - if (!nearestSpirit || dist < nearestDist) - { - nearestSpirit = unit; - nearestDist = dist; - } - } - } - } - - // Mark priority spirit if found, otherwise fall back to nearest - Unit* spiritToMark = prioritySpirit ? prioritySpirit : nearestSpirit; - - // Only mark if we found a better target or need a new mark - if (spiritToMark && (needNewMark || (prioritySpirit && !currentTargetingGroupMember))) - { - group->SetTargetIcon(STAR_ICON_INDEX, bot->GetGUID(), spiritToMark->GetGUID()); - } - } - - // Only ranged DPS use star for RTI - if (botAI->IsRangedDps(bot)) - { - context->GetValue("rti")->Set("star"); - Unit* starTarget = botAI->GetUnit(group->GetTargetIcon(STAR_ICON_INDEX)); - if (starTarget && starTarget->IsAlive()) - { - bot->SetTarget(starTarget->GetGUID()); - bot->SetFacingToObject(starTarget); - Attack(starTarget); - bot->Kill(bot, starTarget); //temp solution since bots struggle to kill spirits in time, they have to follow main tank closely so that they do not get hit by bomb, thus making them have very limited time to react - } - } -} - -bool IccLichKingAddsAction::HandleQuakeMechanics(Unit* boss) -{ - if (!boss || !boss->HasUnitState(UNIT_STATE_CASTING) || !boss->FindCurrentSpellBySpellId(SPELL_QUAKE)) - return false; - - float currentDistance = bot->GetExactDist2d(boss); - - // If already at ideal distance (40f), no need to move - if (currentDistance >= 35.0f && currentDistance <= 45.0f) - return false; - - if (bot->HasUnitState(UNIT_STATE_CASTING)) - botAI->Reset(); - - float botX = bot->GetPositionX(); - float botY = bot->GetPositionY(); - float targetX, targetY; - - if (!botAI->IsTank(bot)) - { - // Non-tanks: use offset position as direction guide - float offsetX = boss->GetPositionX() + 15.0f; - float offsetY = boss->GetPositionY() + 15.0f; - - // Calculate direction towards offset position - float dx = offsetX - botX; - float dy = offsetY - botY; - float distance = sqrt(dx * dx + dy * dy); - - if (distance > 0.0f) - { - // Move 10f towards offset position - float ratio = 10.0f / distance; - targetX = botX + dx * ratio; - targetY = botY + dy * ratio; - } - else - { - targetX = botX; - targetY = botY; - } - } - else - { - // Tanks: use offset position as direction guide - float offsetX = boss->GetPositionX() - 15.0f; - float offsetY = boss->GetPositionY() - 15.0f; - - // Calculate direction towards offset position - float dx = offsetX - botX; - float dy = offsetY - botY; - float distance = sqrt(dx * dx + dy * dy); - - if (distance > 0.0f) - { - // Move 10f towards offset position - float ratio = 10.0f / distance; - targetX = botX + dx * ratio; - targetY = botY + dy * ratio; - } - else - { - targetX = botX; - targetY = botY; - } - } - - MoveTo(bot->GetMapId(), targetX, targetY, boss->GetPositionZ(), false, false, false, false, - MovementPriority::MOVEMENT_COMBAT); - - return false; -} - -void IccLichKingAddsAction::HandleShamblingHorrors() -{ - // Find closest shambling horror - GuidVector npcs2 = AI_VALUE(GuidVector, "nearest hostile npcs"); - Unit* closestHorror = nullptr; - float minHorrorDistance = std::numeric_limits::max(); - - for (auto& npc : npcs2) - { - Unit* unit = botAI->GetUnit(npc); - if (unit && unit->IsAlive() && - (unit->GetEntry() == NPC_SHAMBLING_HORROR1 || unit->GetEntry() == NPC_SHAMBLING_HORROR2 || unit->GetEntry() == NPC_SHAMBLING_HORROR3 || - unit->GetEntry() == NPC_SHAMBLING_HORROR4)) // Shambling horror entries - { - float distance = bot->GetDistance(unit); - if (distance < minHorrorDistance) - { - minHorrorDistance = distance; - closestHorror = unit; - } - } - } - - /* - if (!closestHorror || hasPlague) - { - } - else if (!hasPlague && closestHorror->isInFront(bot) && closestHorror->IsAlive() && !botAI->IsTank(bot) && - bot->GetDistance2d(closestHorror) < 3.0f) - return FleePosition(closestHorror->GetPosition(), 2.0f, 250U); - */ - - // If bot is hunter and shambling is enraged, use Tranquilizing Shot - if (bot->getClass() == CLASS_HUNTER && closestHorror && botAI->HasAura("Enrage", closestHorror)) - botAI->CastSpell("Tranquilizing Shot", closestHorror); -} - -bool IccLichKingAddsAction::HandleAssistTankAddManagement(Unit* boss, Difficulty diff) -{ - if (!botAI->IsAssistTank(bot) || !boss || boss->HealthBelowPct(71)) - return false; - - // Find all adds and categorize them by targeting status - GuidVector targets = AI_VALUE(GuidVector, "possible targets"); - std::vector addsNotTargetingUs; - std::vector addsTargetingUs; - - for (auto i = targets.begin(); i != targets.end(); ++i) - { - Unit* unit = botAI->GetUnit(*i); - if (unit && unit->IsAlive() && - (unit->GetEntry() == NPC_SHAMBLING_HORROR1 || unit->GetEntry() == NPC_SHAMBLING_HORROR2 || - unit->GetEntry() == NPC_SHAMBLING_HORROR3 || - unit->GetEntry() == NPC_SHAMBLING_HORROR4 || // Shambling entry - unit->GetEntry() == NPC_RAGING_SPIRIT1 || unit->GetEntry() == NPC_RAGING_SPIRIT2 || - unit->GetEntry() == NPC_RAGING_SPIRIT3 || unit->GetEntry() == NPC_RAGING_SPIRIT4 || // Spirits entry - unit->GetEntry() == NPC_DRUDGE_GHOUL1 || unit->GetEntry() == NPC_DRUDGE_GHOUL2 || - unit->GetEntry() == NPC_DRUDGE_GHOUL3 || unit->GetEntry() == NPC_DRUDGE_GHOUL4)) // Drudge Ghouls entry - { - if (unit->GetVictim() == bot) - { - addsTargetingUs.push_back(unit->GetGUID()); - } - else - { - addsNotTargetingUs.push_back(unit->GetGUID()); - } - } - } - - // If there are adds not targeting us, we need to collect them all - if (!addsNotTargetingUs.empty()) - { - // Find the highest priority target (Shamblings first, then closest) - Unit* priorityTarget = nullptr; - Unit* closestAdd = nullptr; - float closestDist = 999.0f; - - for (const ObjectGuid& addGuid : addsNotTargetingUs) - { - Unit* add = botAI->GetUnit(addGuid); - if (add && add->IsAlive()) - { - // Shambling takes absolute priority regardless of distance - if (add->GetEntry() == NPC_SHAMBLING_HORROR1 || add->GetEntry() == NPC_SHAMBLING_HORROR2 || - add->GetEntry() == NPC_SHAMBLING_HORROR3 || add->GetEntry() == NPC_SHAMBLING_HORROR4) - { - priorityTarget = add; - break; // Found shambling, stop looking - } - - // Track closest add as backup - float dist = bot->GetExactDist2d(add); - if (dist < closestDist) - { - closestDist = dist; - closestAdd = add; - } - } - } - - // Choose target: Shambling first, then closest - Unit* targetToAttack = priorityTarget ? priorityTarget : closestAdd; - - if (targetToAttack) - { - // Generate threat on ALL adds not targeting us using ranged abilities - for (const ObjectGuid& addGuid : addsNotTargetingUs) - { - Unit* add = botAI->GetUnit(addGuid); - if (add && add->IsAlive()) - { - float dist = bot->GetExactDist2d(add); - - // Use ranged threat generation if within range - if (dist <= 30.0f) - { - // Try taunt first if available - if (botAI->CastSpell("taunt", add)) - { - continue; - } - // Fall back to ranged attack - else if (botAI->CastSpell("shoot", add) || botAI->CastSpell("throw", add)) - { - continue; - } - // Last resort - basic attack state update - else - { - bot->AttackerStateUpdate(add); - } - } - } - } - - // Move towards and attack the priority target - float distToTarget = bot->GetExactDist2d(targetToAttack); - - // If we're too far from our priority target, move closer - if (distToTarget > 5.0f) - { - MoveTo(bot->GetMapId(), targetToAttack->GetPositionX(), targetToAttack->GetPositionY(), - targetToAttack->GetPositionZ(), false, false, false, true, MovementPriority::MOVEMENT_FORCED, - true, false); - } - else - { - // We're close enough, set target and attack - bot->SetTarget(targetToAttack->GetGUID()); - bot->SetFacingToObject(targetToAttack); - Attack(targetToAttack); - } - } - } - // If all adds are targeting us or there are no adds, maintain position based on difficulty - else - { - // In heroic mode, stay at melee position - if (diff && (diff == RAID_DIFFICULTY_10MAN_HEROIC || diff == RAID_DIFFICULTY_25MAN_HEROIC)) - { - if (bot->GetExactDist2d(ICC_LICH_KING_ASSISTHC_POSITION.GetPositionX(), - ICC_LICH_KING_ASSISTHC_POSITION.GetPositionY()) > 2.0f) - { - MoveTo(bot->GetMapId(), ICC_LICH_KING_ASSISTHC_POSITION.GetPositionX(), - ICC_LICH_KING_ASSISTHC_POSITION.GetPositionY(), ICC_LICH_KING_ASSISTHC_POSITION.GetPositionZ(), - false, false, false, true, MovementPriority::MOVEMENT_FORCED, true, false); - } - } - // In normal mode, stay at adds position - else - { - if (bot->GetExactDist2d(ICC_LICH_KING_ADDS_POSITION) > 2.0f) - { - MoveTo(bot->GetMapId(), ICC_LICH_KING_ADDS_POSITION.GetPositionX(), - ICC_LICH_KING_ADDS_POSITION.GetPositionY(), ICC_LICH_KING_ADDS_POSITION.GetPositionZ(), false, - false, false, true, MovementPriority::MOVEMENT_FORCED, true, false); - } - } - - // If we have adds targeting us, attack them with stable target selection - if (!addsTargetingUs.empty()) - { - Unit* currentTarget = bot->GetVictim(); - bool needNewTarget = true; - - // Check if current target is still valid (alive and attacking us) - if (currentTarget && currentTarget->IsAlive()) - { - for (const ObjectGuid& addGuid : addsTargetingUs) - { - if (addGuid == currentTarget->GetGUID()) - { - needNewTarget = false; - break; - } - } - } - - // Only pick new target if current one is invalid - if (needNewTarget) - { - currentTarget = nullptr; - - // Priority 1: Shambling Horror - for (const ObjectGuid& addGuid : addsTargetingUs) - { - Unit* add = botAI->GetUnit(addGuid); - if (add && add->IsAlive()) - { - if (add->GetEntry() == NPC_SHAMBLING_HORROR1 || add->GetEntry() == NPC_SHAMBLING_HORROR2 || - add->GetEntry() == NPC_SHAMBLING_HORROR3 || add->GetEntry() == NPC_SHAMBLING_HORROR4) - { - currentTarget = add; - break; - } - } - } - - // Priority 2: Any other add if no Shambling Horror - if (!currentTarget) - { - for (const ObjectGuid& addGuid : addsTargetingUs) - { - Unit* add = botAI->GetUnit(addGuid); - if (add && add->IsAlive()) - { - currentTarget = add; - break; - } - } - } - } - - if (currentTarget) - { - bot->SetTarget(currentTarget->GetGUID()); - bot->SetFacingToObject(currentTarget); - Attack(currentTarget); - } - } - } - return false; -} - -void IccLichKingAddsAction::HandleMeleePositioning(Unit* boss, bool hasPlague, Difficulty diff) -{ - if (!boss || !botAI->IsMelee(bot) || botAI->IsAssistTank(bot) || boss->HealthBelowPct(71) || hasPlague) - return; - - if (diff == RAID_DIFFICULTY_10MAN_HEROIC || diff == RAID_DIFFICULTY_25MAN_HEROIC) - return; - - float currentDist = bot->GetDistance(ICC_LICH_KING_MELEE_POSITION); - - if (currentDist > 6.0f && !botAI->IsMainTank(bot)) - { - MoveTo(bot->GetMapId(), ICC_LICH_KING_MELEE_POSITION.GetPositionX(), - ICC_LICH_KING_MELEE_POSITION.GetPositionY(), ICC_LICH_KING_MELEE_POSITION.GetPositionZ(), false, false, - false, true, MovementPriority::MOVEMENT_FORCED, true, false); - } - - if (currentDist > 6.0f && botAI->IsMainTank(bot) && boss && boss->GetVictim() == bot) - { - Position currentPos = bot->GetPosition(); - Position targetPos = ICC_LICH_KING_MELEE_POSITION; - - // Calculate direction vector - float dx = targetPos.GetPositionX() - currentPos.GetPositionX(); - float dy = targetPos.GetPositionY() - currentPos.GetPositionY(); - - // Calculate distance and normalize direction - float distance = sqrt(dx * dx + dy * dy); - if (distance > 0.1) - { - dx /= distance; - dy /= distance; - } - - // Calculate intermediate position (3f towards target) - float step = std::min(3.0f, distance - 1.0f); // Don't overshoot the target - if (step > 0) - { - float intermediateX = currentPos.GetPositionX() + dx * step; - float intermediateY = currentPos.GetPositionY() + dy * step; - - MoveTo(bot->GetMapId(), intermediateX, intermediateY, bot->GetPositionZ(), false, false, false, true, - MovementPriority::MOVEMENT_FORCED, true, false); - } - else - { - // If we're within 1.0f + 3.0f of the target, move directly to it - MoveTo(bot->GetMapId(), targetPos.GetPositionX(), targetPos.GetPositionY(), bot->GetPositionZ(), false, - false, false, true, MovementPriority::MOVEMENT_FORCED, true, false); - } - } -} - -void IccLichKingAddsAction::HandleMainTankTargeting(Unit* boss, Difficulty diff) -{ - if (!botAI->IsMainTank(bot) || !boss) - return; - - if (!(diff == RAID_DIFFICULTY_10MAN_HEROIC || diff == RAID_DIFFICULTY_25MAN_HEROIC)) - return; - - if (boss->HealthBelowPct(71) || boss->GetVictim() == bot) - return; - - bot->SetTarget(boss->GetGUID()); - bot->SetFacingToObject(boss); - Attack(boss); -} - -void IccLichKingAddsAction::HandleNonTankHeroicPositioning(Unit* boss, Difficulty diff, bool hasPlague) -{ - if (botAI->IsTank(bot) || !boss) - return; - - if (!(diff == RAID_DIFFICULTY_10MAN_HEROIC || diff == RAID_DIFFICULTY_25MAN_HEROIC)) - return; - - if (boss->HealthBelowPct(71) || hasPlague) - return; - - Unit* mainTank = AI_VALUE(Unit*, "main tank"); - if (!mainTank) - return; - - if (bot->GetDistance2d(mainTank->GetPositionX(), mainTank->GetPositionY()) > 20.0f && (bot->getClass() == CLASS_HUNTER)) - { - botAI->Reset(); - - // Calculate direction vector to main tank - float dx = mainTank->GetPositionX() - bot->GetPositionX(); - float dy = mainTank->GetPositionY() - bot->GetPositionY(); - - // Normalize and scale to 2f increments - float distance = sqrt(dx * dx + dy * dy); - if (distance > 0) - { - dx = dx / distance * 2.0f; - dy = dy / distance * 2.0f; - - // Calculate new position (2f closer to main tank) - float newX = bot->GetPositionX() + dx; - float newY = bot->GetPositionY() + dy; - - MoveTo(bot->GetMapId(), newX, newY, bot->GetPositionZ(), false, false, false, true, - MovementPriority::MOVEMENT_FORCED, true, false); - } - } - - if (bot->GetDistance2d(mainTank->GetPositionX(), mainTank->GetPositionY()) > 1.0f && (bot->getClass() != CLASS_HUNTER)) - { - botAI->Reset(); - MoveTo(bot->GetMapId(), mainTank->GetPositionX(), mainTank->GetPositionY(), bot->GetPositionZ(), false, false, - false, true, MovementPriority::MOVEMENT_FORCED, true, false); - } -} - -void IccLichKingAddsAction::HandleRangedPositioning(Unit* boss, bool hasPlague, Difficulty diff) -{ - if (!boss || !botAI->IsRanged(bot) || boss->HealthBelowPct(71) || hasPlague) - return; - - if (diff == RAID_DIFFICULTY_10MAN_HEROIC || diff == RAID_DIFFICULTY_25MAN_HEROIC) - return; - - float currentDist = bot->GetDistance(ICC_LICH_KING_RANGED_POSITION); - if (currentDist > 2.0f) - { - MoveTo(bot->GetMapId(), ICC_LICH_KING_RANGED_POSITION.GetPositionX(), - ICC_LICH_KING_RANGED_POSITION.GetPositionY(), ICC_LICH_KING_RANGED_POSITION.GetPositionZ(), false, false, - false, true, MovementPriority::MOVEMENT_FORCED, true, false); - } -} - -void IccLichKingAddsAction::HandleDefileMechanics(Unit* boss, Difficulty diff) -{ - if (!boss) - return; - - // Constants - const float BASE_RADIUS = 6.0f; - const float SAFETY_MARGIN = 3.0f; - const float MOVE_DISTANCE = 5.0f; - const float SPREAD_DISTANCE = 12.0f; - const float FIXED_Z = 840.857f; - const float MAX_HEIGHT_DIFF = 5.0f; - const float MIN_PLAYER_SPACING = 5.0f; - const float MAX_BOSS_DISTANCE = 40.0f; - const int ANGLE_TESTS = 16; - const int MAX_ANGLE_OFFSETS = 8; - - // Gather all defile units - std::vector defiles; - float closestDistance = std::numeric_limits::max(); - - GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); - for (auto& npc : npcs) - { - Unit* unit = botAI->GetUnit(npc); - if (unit && unit->IsAlive() && unit->GetEntry() == DEFILE_NPC_ID) - { - defiles.push_back(unit); - float dist = bot->GetDistance(unit); - if (dist < closestDistance) - closestDistance = dist; - } - } - - // Only process defile avoidance if defiles exist - if (!defiles.empty()) - { - // Check if we need to move away from defiles - bool needToMove = false; - float botX = bot->GetPositionX(); - float botY = bot->GetPositionY(); - - for (Unit* defile : defiles) - { - if (!defile || !defile->IsAlive()) - continue; - - // Calculate current defile radius including growth - float currentRadius = BASE_RADIUS; - Aura* growAura = nullptr; - - // Find growth aura - for (size_t i = 0; i < DEFILE_AURA_COUNT; i++) - { - growAura = defile->GetAura(DEFILE_AURAS[i]); - if (growAura) - break; - } - - if (growAura) - { - uint8 stacks = growAura->GetStackAmount(); - float growthMultiplier = - (diff == RAID_DIFFICULTY_10MAN_HEROIC || diff == RAID_DIFFICULTY_10MAN_NORMAL) ? 1.4f : 0.95f; - currentRadius = BASE_RADIUS + (stacks * growthMultiplier); - } - - // Check if bot is too close to this defile - float dx = botX - defile->GetPositionX(); - float dy = botY - defile->GetPositionY(); - float distanceToCenter = sqrt(dx * dx + dy * dy); - - if (distanceToCenter < (currentRadius + SAFETY_MARGIN)) - { - needToMove = true; - break; - } - } - - // Move away from defiles if needed - if (needToMove) - { - float bestAngle = 0.0f; - float maxSafetyScore = 0.0f; - bool foundSafePosition = false; - - // Test multiple angles to find safest escape route - for (int i = 0; i < ANGLE_TESTS; i++) - { - float testAngle = i * M_PI / 8; - float testX = botX + MOVE_DISTANCE * cos(testAngle); - float testY = botY + MOVE_DISTANCE * sin(testAngle); - float testZ = FIXED_Z; - - bot->UpdateAllowedPositionZ(testX, testY, testZ); - - // Skip invalid positions (LOS and height check) - if (!bot->IsWithinLOS(testX, testY, testZ) || fabs(testZ - bot->GetPositionZ()) >= MAX_HEIGHT_DIFF) - continue; - - // Calculate minimum distance to any defile from this position - float minDefileDistance = std::numeric_limits::max(); - for (Unit* defile : defiles) - { - if (!defile || !defile->IsAlive()) - continue; - - float dx = testX - defile->GetPositionX(); - float dy = testY - defile->GetPositionY(); - float dist = sqrt(dx * dx + dy * dy); - minDefileDistance = std::min(minDefileDistance, dist); - } - - // Calculate scoring (safety + boss proximity) - float distanceToBoss = boss->GetDistance2d(testX, testY); - float safetyScore = minDefileDistance; - float bossScore = 100.0f - std::min(100.0f, distanceToBoss); - float totalScore = safetyScore + (bossScore * 0.5f); - - if (totalScore > maxSafetyScore) - { - maxSafetyScore = totalScore; - bestAngle = testAngle; - foundSafePosition = true; - } - } - - // Execute movement if safe position found - if (foundSafePosition && maxSafetyScore > 0) - { - float moveX = botX + MOVE_DISTANCE * cos(bestAngle); - float moveY = botY + MOVE_DISTANCE * sin(bestAngle); - float moveZ = FIXED_Z; - - if (bot->HasUnitState(UNIT_STATE_CASTING)) - botAI->Reset(); - - bot->UpdateAllowedPositionZ(moveX, moveY, moveZ); - MoveTo(bot->GetMapId(), moveX, moveY, moveZ, false, false, false, true, - MovementPriority::MOVEMENT_FORCED); - } - } - } - - // Handle Defile cast - spread positioning - if (boss->HasUnitState(UNIT_STATE_CASTING) && boss->FindCurrentSpellBySpellId(DEFILE_CAST_ID)) - { - // Count players and determine bot's index - uint32 playerCount = 0; - uint32 botIndex = 0; - uint32 currentIndex = 0; - - float botX = bot->GetPositionX(); - float botY = bot->GetPositionY(); - - Map::PlayerList const& players = bot->GetMap()->GetPlayers(); - for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr) - { - Player* player = itr->GetSource(); - if (!player || !player->IsAlive()) - continue; - - if (player == bot) - botIndex = currentIndex; - - currentIndex++; - playerCount++; - } - - // Calculate preferred spread angle based on bot index - float preferredAngle = (float(botIndex) / float(playerCount)) * 2 * M_PI; - bool foundSafeSpot = false; - float bestSpreadAngle = preferredAngle; - - // Try positions starting from preferred angle, expanding outward - for (int offset = 0; offset <= MAX_ANGLE_OFFSETS && !foundSafeSpot; offset++) - { - for (int direction = -1; direction <= 1; direction += 2) - { - if (offset == 0 && direction > 0) // Skip duplicate check of preferred angle - continue; - - float testAngle = preferredAngle + (direction * offset * M_PI / 16); - float testX = botX + SPREAD_DISTANCE * cos(testAngle); - float testY = botY + SPREAD_DISTANCE * sin(testAngle); - float testZ = FIXED_Z; - - bot->UpdateAllowedPositionZ(testX, testY, testZ); - - // Validate position basics (LOS and height) - if (!bot->IsWithinLOS(testX, testY, testZ) || fabs(testZ - bot->GetPositionZ()) >= MAX_HEIGHT_DIFF) - continue; - - // Check boss distance - if (boss->GetDistance2d(testX, testY) > MAX_BOSS_DISTANCE) - continue; - - // Check safety from all defiles (only if defiles exist) - bool safeFromDefiles = true; - for (Unit* defile : defiles) - { - if (!defile || !defile->IsAlive()) - continue; - - // Calculate current defile radius including growth - float currentRadius = BASE_RADIUS; - Aura* growAura = nullptr; - - // Find growth aura - for (size_t i = 0; i < DEFILE_AURA_COUNT; i++) - { - growAura = defile->GetAura(DEFILE_AURAS[i]); - if (growAura) - break; - } - - if (growAura) - { - uint8 stacks = growAura->GetStackAmount(); - float growthMultiplier = - (diff == RAID_DIFFICULTY_10MAN_HEROIC || diff == RAID_DIFFICULTY_10MAN_NORMAL) ? 1.4f - : 0.95f; - currentRadius = BASE_RADIUS + (stacks * growthMultiplier); - } - - float dx = testX - defile->GetPositionX(); - float dy = testY - defile->GetPositionY(); - float distToDefile = sqrt(dx * dx + dy * dy); - - if (distToDefile < (currentRadius + SAFETY_MARGIN)) - { - safeFromDefiles = false; - break; - } - } - - if (!safeFromDefiles) - continue; - - // Check spacing from other players - bool tooCloseToPlayers = false; - for (Map::PlayerList::const_iterator playerItr = players.begin(); playerItr != players.end(); - ++playerItr) - { - Player* player = playerItr->GetSource(); - if (!player || !player->IsAlive() || player == bot) - continue; - - float dx = testX - player->GetPositionX(); - float dy = testY - player->GetPositionY(); - float dist = sqrt(dx * dx + dy * dy); - - if (dist < MIN_PLAYER_SPACING) - { - tooCloseToPlayers = true; - break; - } - } - - if (tooCloseToPlayers) - continue; - - // Found valid position - bestSpreadAngle = testAngle; - foundSafeSpot = true; - break; - } - } - - // Execute spread movement if safe spot found - if (foundSafeSpot) - { - float spreadX = botX + SPREAD_DISTANCE * cos(bestSpreadAngle); - float spreadY = botY + SPREAD_DISTANCE * sin(bestSpreadAngle); - float spreadZ = FIXED_Z; - - bot->UpdateAllowedPositionZ(spreadX, spreadY, spreadZ); - MoveTo(bot->GetMapId(), spreadX, spreadY, spreadZ, false, false, false, false, - MovementPriority::MOVEMENT_COMBAT); - } - } -} - -void IccLichKingAddsAction::HandleValkyrMechanics(Difficulty diff) -{ - GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); - std::vector grabbingValkyrs; - Unit* boss = AI_VALUE2(Unit*, "find target", "the lich king"); - - // Find grabbing Val'kyrs - for (auto& npc : npcs) - { - Unit* unit = botAI->GetUnit(npc); - if (!unit || !unit->IsAlive()) - continue; - - if (unit->GetEntry() == NPC_VALKYR_SHADOWGUARD1 || unit->GetEntry() == NPC_VALKYR_SHADOWGUARD2 || - unit->GetEntry() == NPC_VALKYR_SHADOWGUARD3 || unit->GetEntry() == NPC_VALKYR_SHADOWGUARD4) - { - bool isGrabbing = false; - - if (diff && !(diff == RAID_DIFFICULTY_10MAN_HEROIC || diff == RAID_DIFFICULTY_25MAN_HEROIC)) - { - if (unit->HasAura(SPELL_HARVEST_SOUL_VALKYR)) - isGrabbing = true; - } - else - { - if (unit->HasAura(SPELL_HARVEST_SOUL_VALKYR) && unit->HealthAbovePct(49)) - isGrabbing = true; - } - - if (isGrabbing) - grabbingValkyrs.push_back(unit); - } - } - - Group* group = bot->GetGroup(); - if (!group) - return; - - // If no grabbing Val'kyrs, mark the Lich King with skull - if (grabbingValkyrs.empty() || (boss && boss->HealthBelowPct(43))) - { - // Find the Lich King - for (auto& npc : npcs) - { - Unit* unit = botAI->GetUnit(npc); - if (unit && unit->IsAlive() && unit->GetEntry() == NPC_THE_LICH_KING && unit->HealthBelowPct(68) && unit->HealthAbovePct(40)) - { - ObjectGuid currentSkull = group->GetTargetIcon(7); // Skull icon - if (currentSkull != unit->GetGUID()) - { - group->SetTargetIcon(7, bot->GetGUID(), unit->GetGUID()); - } - break; - } - } - return; - } - - if (botAI->IsMainTank(bot)) - return; - - // Filter out dead Val'kyrs to ensure accurate group calculation - std::vector aliveGrabbingValkyrs; - for (Unit* valkyr : grabbingValkyrs) - { - if (valkyr && valkyr->IsAlive()) - aliveGrabbingValkyrs.push_back(valkyr); - } - - if (aliveGrabbingValkyrs.empty()) - return; - - HandleValkyrMarking(aliveGrabbingValkyrs, diff); - HandleValkyrAssignment(aliveGrabbingValkyrs); -} - -void IccLichKingAddsAction::HandleValkyrMarking(const std::vector& grabbingValkyrs, Difficulty diff) -{ - Group* group = bot->GetGroup(); - if (!group) - return; - - // Sort Val'kyrs by their GUID to ensure consistent ordering - std::vector sortedValkyrs = grabbingValkyrs; - std::sort(sortedValkyrs.begin(), sortedValkyrs.end(), [](Unit* a, Unit* b) { return a->GetGUID() < b->GetGUID(); }); - - static constexpr uint8_t ICON_INDICES[] = {7, 6, 0}; // Skull, Cross, Star - - // In heroic mode, clean up invalid markers for all possible icons - if (diff && (diff == RAID_DIFFICULTY_10MAN_HEROIC || diff == RAID_DIFFICULTY_25MAN_HEROIC)) - { - for (size_t i = 0; i < 3; ++i) - { - ObjectGuid currentIcon = group->GetTargetIcon(ICON_INDICES[i]); - Unit* currentIconUnit = botAI->GetUnit(currentIcon); - - if (currentIconUnit && IsValkyr(currentIconUnit)) - { - bool shouldRemoveMarker = !currentIconUnit->HasAura(SPELL_HARVEST_SOUL_VALKYR) || - std::abs(currentIconUnit->GetPositionZ() - bot->GetPositionZ()) > 5.0f; - - if (shouldRemoveMarker) - group->SetTargetIcon(ICON_INDICES[i], bot->GetGUID(), ObjectGuid::Empty); - } - } - } - - // Clear unused markers if we have fewer Val'kyrs than icons - for (size_t i = sortedValkyrs.size(); i < 3; ++i) - { - ObjectGuid currentIcon = group->GetTargetIcon(ICON_INDICES[i]); - if (!currentIcon.IsEmpty()) - { - group->SetTargetIcon(ICON_INDICES[i], bot->GetGUID(), ObjectGuid::Empty); - } - } - - // Mark each alive Val'kyr with appropriate icon - for (size_t i = 0; i < sortedValkyrs.size() && i < 3; ++i) - { - ObjectGuid currentIcon = group->GetTargetIcon(ICON_INDICES[i]); - Unit* currentIconUnit = botAI->GetUnit(currentIcon); - - if (!currentIconUnit || currentIconUnit != sortedValkyrs[i]) - { - group->SetTargetIcon(ICON_INDICES[i], bot->GetGUID(), sortedValkyrs[i]->GetGUID()); - } - } -} - -void IccLichKingAddsAction::HandleValkyrAssignment(const std::vector& grabbingValkyrs) -{ - Group* group = bot->GetGroup(); - if (!group) - return; - - Unit* boss = AI_VALUE2(Unit*, "find target", "the lich king"); - if (boss && boss->HealthBelowPct(40)) - return; - - // Double-check that all Val'kyrs in the list are actually alive and valid targets - std::vector validValkyrs; - for (Unit* valkyr : grabbingValkyrs) - { - if (valkyr && valkyr->IsAlive() && valkyr->HasAura(SPELL_HARVEST_SOUL_VALKYR)) - { - validValkyrs.push_back(valkyr); - } - } - - if (validValkyrs.empty()) - return; - - // Sort valid Val'kyrs for consistent assignment - std::sort(validValkyrs.begin(), validValkyrs.end(), [](Unit* a, Unit* b) { return a->GetGUID() < b->GetGUID(); }); - - // Get all non-main-tank members (DPS, healers, and off-tanks) - std::vector assistMembers; - for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next()) - { - Player* member = itr->GetSource(); - if (member && !botAI->IsMainTank(member)) - assistMembers.push_back(member); - } - - if (assistMembers.empty()) - return; - - // Sort assist members by GUID for consistent assignment - std::sort(assistMembers.begin(), assistMembers.end(), - [](Player* a, Player* b) { return a->GetGUID() < b->GetGUID(); }); - - // Find our position among assist members - auto it = std::find(assistMembers.begin(), assistMembers.end(), bot); - if (it == assistMembers.end()) - return; // We're main tank, shouldn't handle Val'kyrs - - size_t myAssistIndex = std::distance(assistMembers.begin(), it); - size_t totalAssist = assistMembers.size(); - size_t aliveValkyrs = validValkyrs.size(); - - // Calculate balanced group sizes - std::vector groupSizes = CalculateBalancedGroupSizes(totalAssist, aliveValkyrs); - - // Determine which Val'kyr this bot should target - size_t assignedValkyrIndex = GetAssignedValkyrIndex(myAssistIndex, groupSizes); - - if (assignedValkyrIndex < validValkyrs.size()) - { - Unit* myValkyr = validValkyrs[assignedValkyrIndex]; - - // Set RTI context based on assignment - std::string rtiValue = GetRTIValueForValkyr(assignedValkyrIndex); - context->GetValue("rti")->Set(rtiValue); - - // Attack and apply CC - bot->SetTarget(myValkyr->GetGUID()); - bot->SetFacingToObject(myValkyr); - Difficulty diff = bot->GetRaidDifficulty(); - - if (sPlayerbotAIConfig.EnableICCBuffs && diff && (diff == RAID_DIFFICULTY_10MAN_HEROIC || diff == RAID_DIFFICULTY_25MAN_HEROIC)) - { - //---------CHEAT--------- - if (!myValkyr->HasAura(SPELL_HAMMER_OF_JUSTICE)) - bot->AddAura(SPELL_HAMMER_OF_JUSTICE, myValkyr); - //---------CHEAT--------- - } - ApplyCCToValkyr(myValkyr); - } -} - -std::vector IccLichKingAddsAction::CalculateBalancedGroupSizes(size_t totalAssist, size_t numValkyrs) -{ - std::vector groupSizes(numValkyrs, 0); - - if (numValkyrs == 0) - return groupSizes; - - // Base size for each group - size_t baseSize = totalAssist / numValkyrs; - size_t remainder = totalAssist % numValkyrs; - - // Distribute assist members as evenly as possible - for (size_t i = 0; i < numValkyrs; ++i) - { - groupSizes[i] = baseSize; - if (i < remainder) - groupSizes[i]++; // Add extra member to first 'remainder' groups - } - - return groupSizes; -} - -size_t IccLichKingAddsAction::GetAssignedValkyrIndex(size_t assistIndex, const std::vector& groupSizes) -{ - size_t currentIndex = 0; - - for (size_t valkyrIndex = 0; valkyrIndex < groupSizes.size(); ++valkyrIndex) - { - if (assistIndex < currentIndex + groupSizes[valkyrIndex]) - return valkyrIndex; - - currentIndex += groupSizes[valkyrIndex]; - } - - // Fallback - should not happen with correct logic - return 0; -} - -std::string IccLichKingAddsAction::GetRTIValueForValkyr(size_t valkyrIndex) -{ - switch (valkyrIndex) - { - case 0: - return "skull"; - case 1: - return "cross"; - case 2: - return "star"; - default: - return "skull"; // Fallback - } -} - -void IccLichKingAddsAction::ApplyCCToValkyr(Unit* valkyr) -{ - switch (bot->getClass()) - { - case CLASS_MAGE: - if (!botAI->HasAura("Frost Nova", valkyr)) - botAI->CastSpell("Frost Nova", valkyr); - break; - case CLASS_DRUID: - if (!botAI->HasAura("Entangling Roots", valkyr)) - botAI->CastSpell("Entangling Roots", valkyr); - break; - case CLASS_PALADIN: - if (!botAI->HasAura("Hammer of Justice", valkyr)) - botAI->CastSpell("Hammer of Justice", valkyr); - break; - case CLASS_WARRIOR: - if (!botAI->HasAura("Hamstring", valkyr)) - botAI->CastSpell("Hamstring", valkyr); - break; - case CLASS_HUNTER: - if (!botAI->HasAura("Concussive Shot", valkyr)) - botAI->CastSpell("Concussive Shot", valkyr); - break; - case CLASS_ROGUE: - if (!botAI->HasAura("Kidney Shot", valkyr)) - botAI->CastSpell("Kidney Shot", valkyr); - break; - case CLASS_SHAMAN: - if (!botAI->HasAura("Frost Shock", valkyr)) - botAI->CastSpell("Frost Shock", valkyr); - break; - case CLASS_DEATH_KNIGHT: - if (!botAI->HasAura("Chains of Ice", valkyr)) - botAI->CastSpell("Chains of Ice", valkyr); - break; - case CLASS_PRIEST: - if (!botAI->HasAura("Psychic Scream", valkyr)) - botAI->CastSpell("Psychic Scream", valkyr); - break; - case CLASS_WARLOCK: - if (!botAI->HasAura("Fear", valkyr)) - botAI->CastSpell("Fear", valkyr); - break; - default: - break; - } -} - -bool IccLichKingAddsAction::IsValkyr(Unit* unit) -{ - return unit->GetEntry() == NPC_VALKYR_SHADOWGUARD1 || unit->GetEntry() == NPC_VALKYR_SHADOWGUARD2 || - unit->GetEntry() == NPC_VALKYR_SHADOWGUARD3 || unit->GetEntry() == NPC_VALKYR_SHADOWGUARD4; -} - -void IccLichKingAddsAction::HandleVileSpiritMechanics() -{ - const float radiusVile = 12.0f; - - GuidVector npcs3 = AI_VALUE(GuidVector, "nearest hostile npcs"); - for (auto& npc : npcs3) - { - Unit* unit = botAI->GetUnit(npc); - if (!unit || (unit->GetEntry() != NPC_VILE_SPIRIT1 && unit->GetEntry() != NPC_VILE_SPIRIT2 && unit->GetEntry() != NPC_VILE_SPIRIT3 && - unit->GetEntry() != NPC_VILE_SPIRIT4)) - continue; - - // Only run away if the spirit is targeting us - if (unit->GetVictim() && unit->GetVictim()->GetGUID() == bot->GetGUID()) - { - float currentDistance = bot->GetDistance2d(unit); - - if (currentDistance < radiusVile) - { - botAI->Reset(); - MoveAway(unit, radiusVile - currentDistance); - } - } - } -} diff --git a/src/Ai/Raid/Icecrown/Action/RaidIccActions.h b/src/Ai/Raid/Icecrown/Action/RaidIccActions.h deleted file mode 100644 index 1d63a4e3696..00000000000 --- a/src/Ai/Raid/Icecrown/Action/RaidIccActions.h +++ /dev/null @@ -1,669 +0,0 @@ -#ifndef _PLAYERBOT_RAIDICCACTIONS_H -#define _PLAYERBOT_RAIDICCACTIONS_H - -#include "Action.h" -#include "MovementActions.h" -#include "PlayerbotAI.h" -#include "Playerbots.h" -#include "AttackAction.h" -#include "LastMovementValue.h" -#include "ObjectGuid.h" -#include "PlayerbotAIConfig.h" -#include "RaidIccStrategy.h" -#include "ScriptedCreature.h" -#include "SharedDefines.h" -#include "Trigger.h" -#include "CellImpl.h" -#include "GridNotifiers.h" -#include "GridNotifiersImpl.h" -#include "Vehicle.h" -#include "RaidIccTriggers.h" - -const Position ICC_LM_TANK_POSITION = Position(-391.0f, 2259.0f, 42.0f); -const Position ICC_DARK_RECKONING_SAFE_POSITION = Position(-523.33386f, 2211.2031f, 62.823116f); -const Position ICC_LDW_TANK_POSTION = Position(-570.1f, 2211.2456f, 49.476616f); //-590.0f -const Position ICC_ROTTING_FROST_GIANT_TANK_POSITION = Position(-328.5085f, 2225.5142f, 199.97298f); -const Position ICC_GUNSHIP_TELEPORT_ALLY = Position (-370.04645f, 1993.3536f, 466.65656f); -const Position ICC_GUNSHIP_TELEPORT_ALLY2 = Position (-392.66208f, 2064.893f, 466.5672f, 5.058196f); -const Position ICC_GUNSHIP_TELEPORT_HORDE = Position (-449.5343f, 2477.2024f, 470.17648f); -const Position ICC_GUNSHIP_TELEPORT_HORDE2 = Position (-429.81586f, 2400.6804f, 471.56537f); -const Position ICC_DBS_TANK_POSITION = Position(-494.26517f, 2211.549f, 541.11414f); -const Position ICC_FESTERGUT_TANK_POSITION = Position(4269.1772f, 3144.7673f, 360.38577f); -const Position ICC_FESTERGUT_RANGED_SPORE = Position(4261.143f, 3109.4146f, 360.38605f); -const Position ICC_FESTERGUT_MELEE_SPORE = Position(4269.1772f, 3144.7673f, 360.38577f); -const Position ICC_ROTFACE_TANK_POSITION = Position(4447.061f, 3150.9758f, 360.38568f); -const Position ICC_ROTFACE_BIG_OOZE_POSITION = Position(4432.687f, 3142.3035f, 360.38623f); -const Position ICC_ROTFACE_SAFE_POSITION = Position(4446.557f, 3065.6594f, 360.51843f); -const Position ICC_ROTFACE_CENTER_POSITION = Position(4446.0547f, 3144.8677f, 360.38593f); //actual center 4.74089 4445.6616f, 3137.1526f, 360.38608 -const Position ICC_PUTRICIDE_TANK_POSITION = Position(4373.227f, 3222.058f, 389.4029f); -const Position ICC_PUTRICIDE_GREEN_POSITION = Position(4423.4126f, 3194.2715f, 389.37683f); -const Position ICC_PUTRICIDE_BAD_POSITION = Position(4356.1724f, 3261.5232f, 389.3985f); -//const Position ICC_PUTRICIDE_GAS3_POSITION = Position(4367.753f, 3177.5894f, 389.39575f); -//const Position ICC_PUTRICIDE_GAS4_POSITION = Position(4321.8486f, 3206.464f, 389.3982f); -const Position ICC_BPC_OT_POSITION = Position(4649.2236f, 2796.0972f, 361.1815f); -const Position ICC_BPC_MT_POSITION = Position(4648.5674f, 2744.847f, 361.18222f); -const Position ICC_BQL_CENTER_POSITION = Position(4595.0f, 2769.0f, 400.0f); -const Position ICC_BQL_LWALL1_POSITION = Position(4624.685f, 2789.4895f, 400.13834f); -const Position ICC_BQL_LWALL2_POSITION = Position(4600.749f, 2805.7568f, 400.1374f); -const Position ICC_BQL_LWALL3_POSITION = Position(4572.857f, 2797.3872f, 400.1374f); -const Position ICC_BQL_RWALL1_POSITION = Position(4625.724f, 2748.9917f, 400.13693f); -const Position ICC_BQL_RWALL2_POSITION = Position(4608.3774f, 2735.7466f, 400.13693f); -const Position ICC_BQL_RWALL3_POSITION = Position(4576.813f, 2739.6067f, 400.13693f); -const Position ICC_BQL_LRWALL4_POSITION = Position(4539.345f, 2769.3853f, 403.7267f); -const Position ICC_BQL_TANK_POSITION = Position(4629.746f, 2769.6396f, 401.7479f); //old just in front of stairs 4616.102f, 2768.9167f, 400.13797f -const Position ICC_VDW_HEAL_POSITION = Position(4203.752f, 2483.4343f, 364.87274f); -const Position ICC_VDW_GROUP1_POSITION = Position(4203.585f, 2464.422f, 364.86887f); -const Position ICC_VDW_GROUP2_POSITION = Position(4203.5806f, 2505.2383f, 364.87677f); -const Position ICC_VDW_PORTALSTART_POSITION = Position(4202.637f, 2488.171f, 375.00256f); -const Position ICC_SINDRAGOSA_TANK_POSITION = Position(4408.016f, 2508.0647f, 203.37955f); -const Position ICC_SINDRAGOSA_FLYING_POSITION = Position(4525.6f, 2485.15f, 245.082f); -const Position ICC_SINDRAGOSA_RANGED_POSITION = Position(4441.572f, 2484.482f, 203.37836f); -const Position ICC_SINDRAGOSA_MELEE_POSITION = Position(4423.4546f, 2491.7175f, 203.37686f); -const Position ICC_SINDRAGOSA_BLISTERING_COLD_POSITION = Position(4473.6616f, 2484.8489f, 203.38258f); -const Position ICC_SINDRAGOSA_THOMB1_POSITION = Position(4433.6484f, 2469.4133f, 203.3806f); -const Position ICC_SINDRAGOSA_THOMB2_POSITION = Position(4434.143f, 2486.201f, 203.37473f); -const Position ICC_SINDRAGOSA_THOMB3_POSITION = Position(4436.1147f, 2501.464f, 203.38266f); -const Position ICC_SINDRAGOSA_UNCHAINEDMAGIC1_POSITION = Position(4444.9707f, 2455.7322f, 203.38701f); -const Position ICC_SINDRAGOSA_UNCHAINEDMAGIC2_POSITION = Position(4461.3945f, 2463.5513f, 203.38727f); -const Position ICC_SINDRAGOSA_UNCHAINEDMAGIC3_POSITION = Position(4473.6616f, 2484.8489f, 203.38258f); -const Position ICC_SINDRAGOSA_UNCHAINEDMAGIC4_POSITION = Position(4459.9336f, 2507.409f, 203.38606f); -const Position ICC_SINDRAGOSA_UNCHAINEDMAGIC5_POSITION = Position(4442.3096f, 2512.4688f, 203.38647f); -const Position ICC_SINDRAGOSA_CENTER_POSITION = Position(4408.0464f, 2484.478f, 203.37529f); -const Position ICC_SINDRAGOSA_THOMBMB2_POSITION = Position(4436.895f, 2498.1401f, 203.38133f); -const Position ICC_SINDRAGOSA_FBOMB_POSITION = Position(4449.3647f, 2486.4524f, 203.379f); -const Position ICC_SINDRAGOSA_FBOMB10_POSITION = Position(4449.3647f, 2486.4524f, 203.379f); -const Position ICC_SINDRAGOSA_LOS2_POSITION = Position(4441.8286f, 2501.946f, 203.38435f); -const Position ICC_LICH_KING_ADDS_POSITION = Position(476.7332f, -2095.3894f, 840.857f); // old 486.63647f, -2095.7915f, 840.857f -const Position ICC_LICH_KING_MELEE_POSITION = Position(503.5546f, -2106.8213f, 840.857f); -const Position ICC_LICH_KING_RANGED_POSITION = Position(501.3563f, -2085.1816f, 840.857f); -const Position ICC_LICH_KING_ASSISTHC_POSITION = Position(517.2145f, -2125.0674f, 840.857f); -const Position ICC_LK_FROST1_POSITION = Position(503.96548f, -2183.216f, 840.857f); -const Position ICC_LK_FROST2_POSITION = Position(563.07166f, -2125.7578f, 840.857f); -const Position ICC_LK_FROST3_POSITION = Position(503.40182f, -2067.3435f, 840.857f); -const Position ICC_LK_FROSTR1_POSITION = Position(481.168f, -2177.8723f, 840.857f); -const Position ICC_LK_FROSTR2_POSITION = Position(562.20807f, -2100.2393f, 840.857f); -const Position ICC_LK_FROSTR3_POSITION = Position(526.35297f, -2071.0317f, 840.857f); - -//Lord Marrogwar -class IccLmTankPositionAction : public AttackAction -{ -public: - IccLmTankPositionAction(PlayerbotAI* botAI, std::string const name = "icc lm tank position") - : AttackAction(botAI, name) {} - bool Execute(Event event) override; - - bool MoveTowardPosition(const Position& position, float incrementSize); -}; - -class IccSpikeAction : public AttackAction -{ -public: - IccSpikeAction(PlayerbotAI* botAI) : AttackAction(botAI, "icc spike") {} - bool Execute(Event event) override; - - bool HandleSpikeTargeting(Unit* boss); - bool MoveTowardPosition(const Position& position, float incrementSize); - void UpdateRaidTargetIcon(Unit* target); -}; - -//Lady Deathwhisper -class IccDarkReckoningAction : public MovementAction -{ -public: - IccDarkReckoningAction(PlayerbotAI* botAI, std::string const name = "icc dark reckoning") - : MovementAction(botAI, name) {} - bool Execute(Event event) override; -}; - -class IccRangedPositionLadyDeathwhisperAction : public AttackAction -{ -public: - IccRangedPositionLadyDeathwhisperAction(PlayerbotAI* botAI, std::string const name = "icc ranged position lady deathwhisper") - : AttackAction(botAI, name) {} - bool Execute(Event event) override; - - bool MaintainRangedSpacing(); -}; - -class IccAddsLadyDeathwhisperAction : public AttackAction -{ -public: - IccAddsLadyDeathwhisperAction(PlayerbotAI* botAI, std::string const name = "icc adds lady deathwhisper") - : AttackAction(botAI, name) {} - bool Execute(Event event) override; - - bool IsTargetedByShade(uint32 shadeEntry); - bool MoveTowardPosition(const Position& position, float incrementSize); - bool HandleAddTargeting(Unit* boss); - void UpdateRaidTargetIcon(Unit* target); - -}; - -class IccShadeLadyDeathwhisperAction : public MovementAction -{ -public: - IccShadeLadyDeathwhisperAction(PlayerbotAI* botAI, std::string const name = "icc shade lady deathwhisper") - : MovementAction(botAI, name) {} - bool Execute(Event event) override; -}; - -//Gunship Battle -class IccRottingFrostGiantTankPositionAction : public AttackAction -{ -public: - IccRottingFrostGiantTankPositionAction(PlayerbotAI* botAI, std::string const name = "icc rotting frost giant tank position") - : AttackAction(botAI, name) {} - bool Execute(Event event) override; -}; - -class IccCannonFireAction : public Action -{ -public: - IccCannonFireAction(PlayerbotAI* botAI, std::string const name = "icc cannon fire") - : Action(botAI, name) {} - bool Execute(Event event) override; - - Unit* FindValidCannonTarget(); - bool TryCastCannonSpell(uint32 spellId, Unit* target, Unit* vehicleBase); -}; - -class IccGunshipEnterCannonAction : public MovementAction -{ -public: - IccGunshipEnterCannonAction(PlayerbotAI* botAI, std::string const name = "icc gunship enter cannon") - : MovementAction(botAI, name) {} - bool Execute(Event event) override; - - bool EnterVehicle(Unit* vehicleBase, bool moveIfFar); - Unit* FindBestAvailableCannon(); - bool IsValidCannon(Unit* vehicle, const uint32 validEntries[]); -}; - -class IccGunshipTeleportAllyAction : public AttackAction -{ -public: - IccGunshipTeleportAllyAction(PlayerbotAI* botAI, std::string const name = "icc gunship teleport ally") - : AttackAction(botAI, name) {} - bool Execute(Event event) override; - - bool TeleportTo(const Position& position); - void CleanupSkullIcon(uint8_t SKULL_ICON_INDEX); - void UpdateBossSkullIcon(Unit* boss, uint8_t SKULL_ICON_INDEX); -}; - -class IccGunshipTeleportHordeAction : public AttackAction -{ -public: - IccGunshipTeleportHordeAction(PlayerbotAI* botAI, std::string const name = "icc gunship teleport horde") - : AttackAction(botAI, name) {} - bool Execute(Event event) override; - - bool TeleportTo(const Position& position); - void CleanupSkullIcon(uint8_t SKULL_ICON_INDEX); - void UpdateBossSkullIcon(Unit* boss, uint8_t SKULL_ICON_INDEX); -}; - -//DBS -class IccDbsTankPositionAction : public AttackAction -{ -public: - IccDbsTankPositionAction(PlayerbotAI* botAI, std::string const name = "icc dbs tank position") - : AttackAction(botAI, name) {} - bool Execute(Event event) override; - - bool CrowdControlBloodBeasts(); - bool EvadeBloodBeasts(); - bool PositionInRangedFormation(); -}; - -class IccAddsDbsAction : public AttackAction -{ -public: - IccAddsDbsAction(PlayerbotAI* botAI, std::string const name = "icc adds dbs") - : AttackAction(botAI, name) {} - bool Execute(Event event) override; - - Unit* FindPriorityTarget(Unit* boss); - void UpdateSkullMarker(Unit* priorityTarget); -}; - -//FESTERGUT -class IccFestergutGroupPositionAction : public AttackAction -{ -public: - IccFestergutGroupPositionAction(PlayerbotAI* botAI, std::string const name = "icc festergut group position") - : AttackAction(botAI, name) {} - bool Execute(Event event) override; - - bool HasSporesInGroup(); - bool PositionNonTankMembers(); - int CalculatePositionIndex(Group* group); -}; - -class IccFestergutSporeAction : public AttackAction -{ -public: - IccFestergutSporeAction(PlayerbotAI* botAI, std::string const name = "icc festergut spore") - : AttackAction(botAI, name) {} - bool Execute(Event event) override; - - Position CalculateSpreadPosition(); - struct SporeInfo - { - std::vector sporedPlayers; - ObjectGuid lowestGuid; - bool hasLowestGuid = false; - }; - SporeInfo FindSporedPlayers(); - Position DetermineTargetPosition(bool hasSpore, const SporeInfo& sporeInfo, const Position& spreadRangedPos); - bool CheckMainTankSpore(); -}; - -//Rotface -class IccRotfaceTankPositionAction : public AttackAction -{ -public: - IccRotfaceTankPositionAction(PlayerbotAI* botAI, std::string const name = "icc rotface tank position") - : AttackAction(botAI, name) {} - bool Execute(Event event) override; - - void MarkBossWithSkull(Unit* boss); - bool PositionMainTankAndMelee(Unit *boss); - bool HandleAssistTankPositioning(Unit* boss); - bool HandleBigOozePositioning(Unit* boss); -}; - -class IccRotfaceGroupPositionAction : public AttackAction -{ -public: - IccRotfaceGroupPositionAction(PlayerbotAI* botAI, std::string const name = "icc rotface group position") - : AttackAction(botAI, name) {} - bool Execute(Event event) override; - - //bool MoveAwayFromBigOoze(Unit* bigOoze); - bool HandlePuddleAvoidance(Unit* boss); - bool MoveAwayFromPuddle(Unit* boss, Unit* puddle, float puddleDistance); - bool HandleOozeTargeting(); - bool HandleOozeMemberPositioning(); - bool PositionRangedAndHealers(Unit* boss,Unit* smallOoze); - bool FindAndMoveFromClosestMember(Unit* smallOoze); -}; - -class IccRotfaceMoveAwayFromExplosionAction : public MovementAction -{ -public: - IccRotfaceMoveAwayFromExplosionAction(PlayerbotAI* botAI, std::string const name = "icc rotface move away from explosion") - : MovementAction(botAI, name) {} - bool Execute(Event event) override; - - bool MoveToRandomSafeLocation(); - -}; - -//PP -class IccPutricideGrowingOozePuddleAction : public AttackAction -{ -public: - IccPutricideGrowingOozePuddleAction(PlayerbotAI* botAI, std::string const name = "icc putricide growing ooze puddle") - : AttackAction(botAI, name) {} - bool Execute(Event event) override; - - Unit* FindClosestThreateningPuddle(); - Position CalculateSafeMovePosition(Unit* closestPuddle); - bool IsPositionTooCloseToOtherPuddles(float x, float y, Unit* ignorePuddle); -}; - -class IccPutricideVolatileOozeAction : public AttackAction -{ -public: - IccPutricideVolatileOozeAction(PlayerbotAI* botAI, std::string const name = "icc putricide volatile ooze") - : AttackAction(botAI, name) {} - bool Execute(Event event) override; - - void MarkOozeWithSkull(Unit* ooze); - Unit* FindAuraTarget(); -}; - -class IccPutricideGasCloudAction : public AttackAction -{ -public: - IccPutricideGasCloudAction(PlayerbotAI* botAI, std::string const name = "icc putricide gas cloud") - : AttackAction(botAI, name) {} - bool Execute(Event event) override; - - bool HandleGaseousBloatMovement(Unit* gasCloud); - Position CalculateEmergencyPosition(const Position& botPos, float dx, float dy); - bool FindSafeMovementPosition(const Position& botPos, const Position& cloudPos, float dx, float dy, int numAngles, - Position& resultPos); - bool HandleGroupAuraSituation(Unit* gasCloud); - bool GroupHasGaseousBloat(Group* group); -}; - -class IccPutricideAvoidMalleableGooAction : public MovementAction -{ -public: - IccPutricideAvoidMalleableGooAction(PlayerbotAI* botAI, std::string const name = "icc putricide avoid malleable goo") - : MovementAction(botAI, name) {} - bool Execute(Event event) override; - - bool HandleTankPositioning(Unit* boss); - bool HandleUnboundPlague(Unit* boss); - bool HandleBossPositioning(Unit* boss); - Position CalculateBossPosition(Unit* boss, float distance); - bool HasObstacleBetween(const Position& from, const Position& to); - bool IsOnPath(const Position& from, const Position& to, const Position& point, float threshold); - Position CalculateArcPoint(const Position& current, const Position& target, const Position& center); - Position CalculateIncrementalMove(const Position& current, const Position& target, float maxDistance); -}; - -//BPC -class IccBpcKelesethTankAction : public AttackAction -{ -public: - IccBpcKelesethTankAction(PlayerbotAI* botAI) - : AttackAction(botAI, "icc bpc keleseth tank") {} - bool Execute(Event event) override; -}; - -class IccBpcMainTankAction : public AttackAction -{ -public: - IccBpcMainTankAction(PlayerbotAI* botAI) - : AttackAction(botAI, "icc bpc main tank") {} - bool Execute(Event event) override; - - void MarkEmpoweredPrince(); -}; - -class IccBpcEmpoweredVortexAction : public MovementAction -{ -public: - IccBpcEmpoweredVortexAction(PlayerbotAI* botAI) - : MovementAction(botAI, "icc bpc empowered vortex") {} - bool Execute(Event event) override; - - bool MaintainRangedSpacing(); - bool HandleEmpoweredVortexSpread(); -}; - -class IccBpcKineticBombAction : public AttackAction -{ -public: - IccBpcKineticBombAction(PlayerbotAI* botAI) - : AttackAction(botAI, "icc bpc kinetic bomb") {} - bool Execute(Event event) override; - - Unit* FindOptimalKineticBomb(); - bool IsBombAlreadyHandled(Unit* bomb, Group* group); -}; - -class IccBpcBallOfFlameAction : public MovementAction -{ -public: - IccBpcBallOfFlameAction(PlayerbotAI* botAI) - : MovementAction(botAI, "icc bpc ball of flame") {} - bool Execute(Event event) override; -}; - -//Blood Queen Lana'thel -class IccBqlGroupPositionAction : public AttackAction -{ -public: - IccBqlGroupPositionAction(PlayerbotAI* botAI) - : AttackAction(botAI, "icc group tank position") {} - bool Execute(Event event) override; - - bool HandleTankPosition(Unit* boss, Aura* frenzyAura, Aura* shadowAura); - bool HandleShadowsMovement(); - Position AdjustControlPoint(const Position& wall, const Position& center, float factor); - Position CalculateBezierPoint(float t, const Position path[4]); - bool HandleGroupPosition(Unit* boss, Aura* frenzyAura, Aura* shadowAura); - -private: - // Evaluate curves - struct CurveInfo - { - Position moveTarget; - int curveIdx = 0; - bool foundSafe = false; - float minDist = 0.0f; - float score = 0.0f; - Position closestPoint; - float t_closest = 0.0f; - }; -}; - -class IccBqlPactOfDarkfallenAction : public MovementAction -{ -public: - IccBqlPactOfDarkfallenAction(PlayerbotAI* botAI) - : MovementAction(botAI, "icc bql pact of darkfallen") {} - bool Execute(Event event) override; - - void CalculateCenterPosition(Position& targetPos, const std::vector& playersWithAura); - bool MoveToTargetPosition(const Position& targetPos, int auraCount); -}; - -class IccBqlVampiricBiteAction : public AttackAction -{ -public: - IccBqlVampiricBiteAction(PlayerbotAI* botAI) - : AttackAction(botAI, "icc bql vampiric bite") {} - bool Execute(Event event) override; - - Player* FindBestBiteTarget(Group* group); - bool IsInvalidTarget(Player* player); - bool MoveTowardsTarget(Player* target); - bool CastVampiricBite(Player* target); -}; - -// Sister Svalna -class IccValkyreSpearAction : public AttackAction -{ -public: - IccValkyreSpearAction(PlayerbotAI* botAI) - : AttackAction(botAI, "icc valkyre spear") {} - bool Execute(Event event) override; -}; - -class IccSisterSvalnaAction : public AttackAction -{ -public: - IccSisterSvalnaAction(PlayerbotAI* botAI) - : AttackAction(botAI, "icc sister svalna") {} - bool Execute(Event event) override; -}; - -// Valithria Dreamwalker - -class IccValithriaGroupAction : public AttackAction -{ -public: - IccValithriaGroupAction(PlayerbotAI* botAI) - : AttackAction(botAI, "icc valithria group") {} - bool Execute(Event event) override; - - bool MoveTowardsPosition(const Position& pos, float increment); - bool Handle25ManGroupLogic(); - bool HandleMarkingLogic(bool inGroup1, bool inGroup2, const Position& group1Pos, const Position& group2Pos); - bool Handle10ManGroupLogic(); -}; - -class IccValithriaPortalAction : public MovementAction -{ -public: - IccValithriaPortalAction(PlayerbotAI* botAI) - : MovementAction(botAI, "icc valithria portal") {} - bool Execute(Event event) override; -}; - -class IccValithriaHealAction : public AttackAction -{ -public: - IccValithriaHealAction(PlayerbotAI* botAI) - : AttackAction(botAI, "icc valithria heal") {} - bool Execute(Event event) override; -}; - -class IccValithriaDreamCloudAction : public MovementAction -{ -public: - IccValithriaDreamCloudAction(PlayerbotAI* botAI) - : MovementAction(botAI, "icc valithria dream cloud") {} - bool Execute(Event event) override; -}; - -//Sindragosa -class IccSindragosaGroupPositionAction : public AttackAction -{ -public: - IccSindragosaGroupPositionAction(PlayerbotAI* botAI) - : AttackAction(botAI, "icc sindragosa group position") {} - bool Execute(Event event) override; - - bool HandleTankPositioning(Unit* boss); - bool HandleNonTankPositioning(); - bool MoveIncrementallyToPosition(const Position& targetPos, float maxStep); -}; - -class IccSindragosaFrostBeaconAction : public MovementAction -{ -public: - IccSindragosaFrostBeaconAction(PlayerbotAI* botAI) - : MovementAction(botAI, "icc sindragosa frost beacon") {} - bool Execute(Event event) override; - - void HandleSupportActions(); - bool HandleBeaconedPlayer(const Unit* boss); - bool HandleNonBeaconedPlayer(const Unit* boss); - bool MoveToPositionIfNeeded(const Position& position, float tolerance); - bool MoveToPosition(const Position& position); - bool IsBossFlying(const Unit* boss); - - private: - static constexpr uint32 FROST_BEACON_AURA_ID = SPELL_FROST_BEACON; - static constexpr uint32 HAND_OF_FREEDOM_SPELL_ID = 1044; - static constexpr float POSITION_TOLERANCE = 1.0f; - static constexpr float TOMB_POSITION_TOLERANCE = 0.5f; - static constexpr float MIN_SAFE_DISTANCE = 13.0f; - static constexpr float MOVE_TOLERANCE = 2.0f; -}; - -class IccSindragosaBlisteringColdAction : public MovementAction -{ -public: - IccSindragosaBlisteringColdAction(PlayerbotAI* botAI) - : MovementAction(botAI, "icc sindragosa blistering cold") {} - bool Execute(Event event) override; -}; - -class IccSindragosaUnchainedMagicAction : public AttackAction -{ -public: - IccSindragosaUnchainedMagicAction(PlayerbotAI* botAI) - : AttackAction(botAI, "icc sindragosa unchained magic") {} - bool Execute(Event event) override; -}; - -class IccSindragosaChilledToTheBoneAction : public AttackAction -{ -public: - IccSindragosaChilledToTheBoneAction(PlayerbotAI* botAI) - : AttackAction(botAI, "icc sindragosa chilled to the bone") {} - bool Execute(Event event) override; -}; - -class IccSindragosaMysticBuffetAction : public MovementAction -{ -public: - IccSindragosaMysticBuffetAction(PlayerbotAI* botAI) - : MovementAction(botAI, "icc sindragosa mystic buffet") {} - bool Execute(Event event) override; -}; - -class IccSindragosaFrostBombAction : public MovementAction -{ -public: - IccSindragosaFrostBombAction(PlayerbotAI* botAI) - : MovementAction(botAI, "icc sindragosa frost bomb") {} - bool Execute(Event event) override; -}; - -class IccSindragosaTankSwapPositionAction : public AttackAction -{ - public: - IccSindragosaTankSwapPositionAction(PlayerbotAI* botAI) - : AttackAction(botAI, "sindragosa tank swap position") {} - bool Execute(Event event) override; -}; - -//LK -class IccLichKingShadowTrapAction : public MovementAction -{ - public: - IccLichKingShadowTrapAction(PlayerbotAI* botAI) - : MovementAction(botAI, "icc lich king shadow trap") {} - bool Execute(Event event) override; -}; - -class IccLichKingNecroticPlagueAction : public MovementAction -{ - public: - IccLichKingNecroticPlagueAction(PlayerbotAI* botAI) - : MovementAction(botAI, "icc lich king necrotic plague") {} - bool Execute(Event event) override; -}; - -class IccLichKingWinterAction : public AttackAction -{ - public: - IccLichKingWinterAction(PlayerbotAI* botAI) - : AttackAction(botAI, "icc lich king winter") {} - bool Execute(Event event) override; - - void HandlePositionCorrection(); - bool IsValidCollectibleAdd(Unit* unit); - bool IsPositionSafeFromDefile(float x, float y, float minSafeDistance); - void HandleTankPositioning(); - void HandleMeleePositioning(); - void HandleRangedPositioning(); - void HandleMainTankAddManagement(const Position* tankPos); - void HandleAssistTankAddManagement(const Position* tankPos); - - private: - const Position* GetMainTankPosition(); - const Position* GetMainTankRangedPosition(); - bool TryMoveToPosition(float targetX, float targetY, float targetZ, bool isForced = true); -}; - -class IccLichKingAddsAction : public AttackAction -{ - public: - IccLichKingAddsAction(PlayerbotAI* botAI) - : AttackAction(botAI, "icc lich king adds") {} - bool Execute(Event event) override; - - void HandleTeleportationFixes(Difficulty diff, Unit* terenasMenethilHC); - bool HandleSpiritBombAvoidance(Difficulty diff, Unit* terenasMenethilHC); - void HandleHeroicNonTankPositioning(Difficulty diff, Unit* terenasMenethilHC); - void HandleSpiritMarkingAndTargeting(Difficulty diff, Unit* terenasMenethilHC); - bool HandleQuakeMechanics(Unit* boss); - void HandleShamblingHorrors(); - bool HandleAssistTankAddManagement(Unit* boss, Difficulty diff); - void HandleMeleePositioning(Unit* boss, bool hasPlague, Difficulty diff); - void HandleMainTankTargeting(Unit* boss, Difficulty diff); - void HandleNonTankHeroicPositioning(Unit* boss, Difficulty diff, bool hasPlague); - void HandleRangedPositioning(Unit* boss, bool hasPlague, Difficulty diff); - void HandleDefileMechanics(Unit* boss, Difficulty diff); - void HandleValkyrMechanics(Difficulty diff); - std::vector CalculateBalancedGroupSizes(size_t totalAssist, size_t numValkyrs); - size_t GetAssignedValkyrIndex(size_t assistIndex, const std::vector& groupSizes); - std::string GetRTIValueForValkyr(size_t valkyrIndex); - void HandleValkyrMarking(const std::vector& grabbingValkyrs, Difficulty diff); - void HandleValkyrAssignment(const std::vector& grabbingValkyrs); - void ApplyCCToValkyr(Unit* valkyr); - bool IsValkyr(Unit* unit); - void HandleVileSpiritMechanics(); -}; - -#endif diff --git a/src/Ai/Raid/Icecrown/Multiplier/RaidIccMultipliers.cpp b/src/Ai/Raid/Icecrown/Multiplier/RaidIccMultipliers.cpp deleted file mode 100644 index 710c15e0a03..00000000000 --- a/src/Ai/Raid/Icecrown/Multiplier/RaidIccMultipliers.cpp +++ /dev/null @@ -1,872 +0,0 @@ -#include "RaidIccMultipliers.h" - -#include "ChooseTargetActions.h" -#include "DKActions.h" -#include "DruidActions.h" -#include "DruidBearActions.h" -#include "FollowActions.h" -#include "GenericActions.h" -#include "GenericSpellActions.h" -#include "HunterActions.h" -#include "MageActions.h" -#include "MovementActions.h" -#include "PaladinActions.h" -#include "PriestActions.h" -#include "RaidIccActions.h" -#include "ReachTargetActions.h" -#include "RogueActions.h" -#include "ShamanActions.h" -#include "UseMeetingStoneAction.h" -#include "WarriorActions.h" -#include "PlayerbotAI.h" -#include "RaidIccTriggers.h" - -// LK global variables -namespace -{ -std::map g_plagueTimes; -std::map g_allowCure; -std::mutex g_plagueMutex; // Lock before accessing shared variables -} - -// Lady Deathwhisper -float IccLadyDeathwhisperMultiplier::GetValue(Action* action) -{ - Unit* boss = AI_VALUE2(Unit*, "find target", "lady deathwhisper"); - if (!boss) - return 1.0f; - - if (dynamic_cast(action) || dynamic_cast(action) || dynamic_cast(action)) - return 0.0f; - - static constexpr uint32 VENGEFUL_SHADE_ID = NPC_SHADE; - - // Get the nearest hostile NPCs - const GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); - - // Allow the IccShadeLadyDeathwhisperAction to run - if (dynamic_cast(action)) - return 1.0f; - - for (auto const& npcGuid : npcs) - { - Unit* shade = botAI->GetUnit(npcGuid); - - if (!shade || shade->GetEntry() != VENGEFUL_SHADE_ID) - continue; - - if (!shade->GetVictim() || shade->GetVictim()->GetGUID() != bot->GetGUID()) - continue; - - return 0.0f; // Cancel all other actions when we need to handle Vengeful Shade - } - - return 1.0f; -} - -// dbs -float IccAddsDbsMultiplier::GetValue(Action* action) -{ - Unit* boss = AI_VALUE2(Unit*, "find target", "deathbringer saurfang"); - if (!boss) - return 1.0f; - - if (dynamic_cast(action) || dynamic_cast(action) || - dynamic_cast(action) || dynamic_cast(action) || - dynamic_cast(action) || dynamic_cast(action) || - dynamic_cast(action) || dynamic_cast(action) || - dynamic_cast(action) || dynamic_cast(action) || - dynamic_cast(action)) - return 0.0f; - - if (botAI->IsRanged(bot)) - if (dynamic_cast(action)) - return 0.0f; - - if (botAI->IsMainTank(bot)) - { - Aura* aura = botAI->GetAura("rune of blood", bot); - if (aura) - { - if (dynamic_cast(action)) - return 1.0f; - else - return 0.0f; - } - } - - return 1.0f; -} - -// dogs -float IccDogsMultiplier::GetValue(Action* action) -{ - bool bossPresent = false; - if (AI_VALUE2(Unit*, "find target", "stinky") || AI_VALUE2(Unit*, "find target", "precious")) - bossPresent = true; - - if (!bossPresent) - return 1.0f; - - if (botAI->IsMainTank(bot)) - { - Aura* aura = botAI->GetAura("mortal wound", bot, false, true); - if (aura && aura->GetStackAmount() >= 8) - { - if (dynamic_cast(action)) - return 1.0f; - else - return 0.0f; - } - } - return 1.0f; -} - -// Festergut -float IccFestergutMultiplier::GetValue(Action* action) -{ - Unit* boss = AI_VALUE2(Unit*, "find target", "festergut"); - if (!boss) - return 1.0f; - - if (dynamic_cast(action) || dynamic_cast(action)) - return 0.0f; - - if (dynamic_cast(action)) - return 0.0f; - - if (botAI->IsMainTank(bot)) - { - Aura* aura = botAI->GetAura("gastric bloat", bot, false, true); - if (aura && aura->GetStackAmount() >= 6) - { - if (dynamic_cast(action)) - return 1.0f; - else - return 0.0f; - } - } - - if (dynamic_cast(action)) - return 1.0f; - - if (bot->HasAura(SPELL_GAS_SPORE)) - return 0.0f; - - return 1.0f; -} - -// Rotface -float IccRotfaceMultiplier::GetValue(Action* action) -{ - Unit* boss1 = AI_VALUE2(Unit*, "find target", "rotface"); - if (!boss1) - return 1.0f; - - if (dynamic_cast(action)) - return 0.0f; - - if (dynamic_cast(action) && !(bot->getClass() == CLASS_HUNTER)) - return 0.0f; - - if (dynamic_cast(action)) - return 0.0f; - - if (botAI->IsAssistTank(bot) && (dynamic_cast(action) || dynamic_cast(action))) - return 0.0f; - - Unit* boss = AI_VALUE2(Unit*, "find target", "big ooze"); - if (!boss) - return 1.0f; - - static std::map lastExplosionTimes; - static std::map hasMoved; - - ObjectGuid botGuid = bot->GetGUID(); - - // When cast starts, record the time - if (boss->HasUnitState(UNIT_STATE_CASTING) && boss->FindCurrentSpellBySpellId(SPELL_UNSTABLE_OOZE_EXPLOSION)) - { - if (lastExplosionTimes[botGuid] == 0) // Only set if not already set - { - lastExplosionTimes[botGuid] = time(nullptr); - hasMoved[botGuid] = false; - } - } - - // If explosion cast is no longer active, reset the timers - if (!boss->HasUnitState(UNIT_STATE_CASTING) || !boss->FindCurrentSpellBySpellId(SPELL_UNSTABLE_OOZE_EXPLOSION)) - { - if (lastExplosionTimes[botGuid] > 0 && time(nullptr) - lastExplosionTimes[botGuid] >= 16) - { - lastExplosionTimes[botGuid] = 0; - hasMoved[botGuid] = false; - return 1.0f; // Allow normal actions to resume - } - } - - // If 9 seconds have passed since cast start and we haven't moved yet - if (lastExplosionTimes[botGuid] > 0 && !hasMoved[botGuid] && time(nullptr) - lastExplosionTimes[botGuid] >= 9) - { - if (dynamic_cast(action) - && !dynamic_cast(action)) - { - return 0.0f; // Block other movement actions - } - hasMoved[botGuid] = true; // Mark that we've initiated movement - } - - // Continue blocking other movements for 7 seconds after moving - if (hasMoved[botGuid] && time(nullptr) - lastExplosionTimes[botGuid] < 16 // 9 seconds wait + 7 seconds stay - && dynamic_cast(action) - && !dynamic_cast(action)) - return 0.0f; - - return 1.0f; -} - -// pp -float IccAddsPutricideMultiplier::GetValue(Action* action) -{ - Unit* boss = AI_VALUE2(Unit*, "find target", "professor putricide"); - if (!boss) - return 1.0f; - - bool hasGaseousBloat = botAI->HasAura("Gaseous Bloat", bot); - bool hasUnboundPlague = botAI->HasAura("Unbound Plague", bot); - - if (!(bot->getClass() == CLASS_HUNTER) && dynamic_cast(action)) - return 0.0f; - - if (dynamic_cast(action)) - return 0.0f; - - if (dynamic_cast(action)) - return 0.0f; - - if (dynamic_cast(action)) - return 0.0f; - - if (botAI->IsMainTank(bot)) - { - Aura* aura = botAI->GetAura("mutated plague", bot, false, true); - if (aura && aura->GetStackAmount() >= 4) - { - if (dynamic_cast(action)) - return 1.0f; - else - return 0.0f; - } - } - - if (hasGaseousBloat) - { - if (dynamic_cast(action)) - return 1.0f; - - if (dynamic_cast(action)) - return 1.0f; - - if (botAI->IsHeal(bot)) - return 1.0f; - else - return 0.0f; // Cancel all other actions when we need to handle Gaseous Bloat - } - - if (hasUnboundPlague && boss && !boss->HealthBelowPct(35)) - { - if (dynamic_cast(action)) - return 1.0f; - else - return 0.0f; // Cancel all other actions when we need to handle Unbound Plague - } - - if (dynamic_cast(action)) - { - if (dynamic_cast(action)) - return 0.0f; - if (dynamic_cast(action) && !botAI->IsMainTank(bot)) - return 0.0f; - //if (dynamic_cast(action) && !hasGaseousBloat) - //return 0.0f; - } - - return 1.0f; -} - -// bpc -float IccBpcAssistMultiplier::GetValue(Action* action) -{ - Unit* keleseth = AI_VALUE2(Unit*, "find target", "prince keleseth"); - if (!keleseth) - return 1.0f; - - if (dynamic_cast(action) || dynamic_cast(action) || - dynamic_cast(action) || dynamic_cast(action) || - dynamic_cast(action) || dynamic_cast(action) || - dynamic_cast(action) || dynamic_cast(action) || - dynamic_cast(action) || dynamic_cast(action)) - return 0.0f; - - Aura* aura = botAI->GetAura("Shadow Prison", bot, false, true); - if (aura) - { - if (aura->GetStackAmount() > 18 && botAI->IsTank(bot)) - { - if (dynamic_cast(action)) - return 0.0f; - } - - if (aura->GetStackAmount() > 12 && !botAI->IsTank(bot)) - { - if (dynamic_cast(action)) - return 0.0f; - } - } - - Unit* valanar = AI_VALUE2(Unit*, "find target", "prince valanar"); - if (!valanar) - return 1.0f; - - if (valanar && valanar->HasUnitState(UNIT_STATE_CASTING) && - (valanar->FindCurrentSpellBySpellId(SPELL_EMPOWERED_SHOCK_VORTEX1) || - valanar->FindCurrentSpellBySpellId(SPELL_EMPOWERED_SHOCK_VORTEX2) || - valanar->FindCurrentSpellBySpellId(SPELL_EMPOWERED_SHOCK_VORTEX3) || - valanar->FindCurrentSpellBySpellId(SPELL_EMPOWERED_SHOCK_VORTEX4))) - { - if (dynamic_cast(action) || dynamic_cast(action)) - return 1.0f; - else - return 0.0f; // Cancel all other actions when we need to handle Empowered Vortex - } - - Unit* flame1 = bot->FindNearestCreature(NPC_BALL_OF_FLAME, 100.0f); - Unit* flame2 = bot->FindNearestCreature(NPC_BALL_OF_INFERNO_FLAME, 100.0f); - bool ballOfFlame = flame1 && flame1->GetVictim() == bot; - bool infernoFlame = flame2 && flame2->GetVictim() == bot; - - if (flame2) - { - if (dynamic_cast(action) || dynamic_cast(action)) - return 0.0f; - - if (dynamic_cast(action)) - return 1.0f; - } - - if (ballOfFlame || infernoFlame) - { - // If bot is tank, do nothing special - if (dynamic_cast(action)) - return 1.0f; - else - return 0.0f; // Cancel all other actions when we need to handle Ball of Flame - } - - static const std::array bombEntries = {NPC_KINETIC_BOMB1, NPC_KINETIC_BOMB2, NPC_KINETIC_BOMB3, - NPC_KINETIC_BOMB4}; - const GuidVector bombs = AI_VALUE(GuidVector, "possible targets no los"); - - bool bombFound = false; - - for (const auto entry : bombEntries) - { - for (auto const& guid : bombs) - { - if (Unit* unit = botAI->GetUnit(guid)) - { - if (unit->GetEntry() == entry) - { - // Check if bomb is within valid Z-axis range - if (unit->GetPositionZ() - bot->GetPositionZ() < 25.0f) - { - bombFound = true; - break; - } - } - } - } - if (bombFound) - break; - } - - if (bombFound && !(aura && aura->GetStackAmount() > 12) && !botAI->IsTank(bot)) - { - // If kinetic bomb action is active, disable these actions - if (dynamic_cast(action)) - return 1.0f; - - if (dynamic_cast(action) || dynamic_cast(action) || - dynamic_cast(action)) - return 0.0f; - } - - // For assist tank during BPC fight - if (botAI->IsAssistTank(bot) && !(aura && aura->GetStackAmount() > 18)) - { - // Allow BPC-specific actions - if (dynamic_cast(action)) - return 1.0f; - - // Disable normal assist behavior - if (dynamic_cast(action) || - dynamic_cast(action) || - dynamic_cast(action) || - dynamic_cast(action)) - return 0.0f; - - } - - return 1.0f; -} - -//BQL -float IccBqlMultiplier::GetValue(Action* action) -{ - Unit* boss = AI_VALUE2(Unit*, "find target", "blood-queen lana'thel"); - if (!boss) - return 1.0f; - - Aura* aura2 = botAI->GetAura("Swarming Shadows", bot); - Aura* aura = botAI->GetAura("Frenzied Bloodthirst", bot); - - if (botAI->IsRanged(bot)) - if (dynamic_cast(action) || dynamic_cast(action) || - dynamic_cast(action) || dynamic_cast(action)) - return 0.0f; - - // If bot has Pact of Darkfallen aura, return 0 for all other actions - if (bot->HasAura(SPELL_PACT_OF_THE_DARKFALLEN)) - { - if (dynamic_cast(action)) - return 1.0f; // Allow Pact of Darkfallen action - else - return 0.0f; // Cancel all other actions when we need to handle Pact of Darkfallen - } - - if (botAI->IsMelee(bot) && ((boss->GetPositionZ() - ICC_BQL_CENTER_POSITION.GetPositionZ()) > 5.0f) && !aura) - { - if (dynamic_cast(action)) - return 1.0f; - else - return 0.0f; - } - - // If bot has frenzied bloodthirst, allow highest priority for bite action - if (aura) // If bot has frenzied bloodthirst - { - if (dynamic_cast(action)) - return 1.0f; - else - return 0.0f; - } - - if (aura2 && !aura) - { - if (dynamic_cast(action)) - return 1.0f; - else - return 0.0f; // Cancel all other actions when we need to handle Swarming Shadows - } - - if ((boss->GetExactDist2d(ICC_BQL_TANK_POSITION.GetPositionX(), ICC_BQL_TANK_POSITION.GetPositionY()) > 10.0f) && - botAI->IsRanged(bot) && !((boss->GetPositionZ() - bot->GetPositionZ()) > 5.0f)) - { - if (dynamic_cast(action) || dynamic_cast(action)) - return 0.0f; - } - - return 1.0f; -} - -//VDW -float IccValithriaDreamCloudMultiplier::GetValue(Action* action) -{ - Unit* boss = bot->FindNearestCreature(NPC_VALITHRIA_DREAMWALKER, 100.0f); - - Aura* twistedNightmares = botAI->GetAura("Twisted Nightmares", bot); - Aura* emeraldVigor = botAI->GetAura("Emerald Vigor", bot); - - if (!boss && !bot->HasAura(SPELL_DREAM_STATE)) - return 1.0f; - - if (dynamic_cast(action) || dynamic_cast(action)) - return 0.0f; - - if (botAI->IsTank(bot)) - { - if (dynamic_cast(action)) - return 0.0f; - } - - if (botAI->IsHeal(bot) && (twistedNightmares || emeraldVigor)) - if (dynamic_cast(action) || dynamic_cast(action)) - return 0.0f; - - if (bot->HasAura(SPELL_DREAM_STATE) && !bot->HealthBelowPct(50)) - { - if (dynamic_cast(action)) - return 1.0f; // Allow Dream Cloud action - else - return 0.0f; // Cancel all other actions when we need to handle Dream Cloud - } - - return 1.0f; - -} - -//SINDRAGOSA - -float IccSindragosaMultiplier::GetValue(Action* action) -{ - Unit* boss = bot->FindNearestCreature(NPC_SINDRAGOSA, 200.0f); - if (!boss) - return 1.0f; - Aura* aura = botAI->GetAura("Unchained Magic", bot, false, true); - - Difficulty diff = bot->GetRaidDifficulty(); - - if (boss->HealthBelowPct(95)) - { - if (dynamic_cast(action) || dynamic_cast(action) || - dynamic_cast(action) || dynamic_cast(action)) - return 0.0f; - } - - if (aura && (diff == RAID_DIFFICULTY_10MAN_HEROIC || diff == RAID_DIFFICULTY_25MAN_HEROIC) && - !dynamic_cast(action)) - { - if (dynamic_cast(action) || dynamic_cast(action)) - return 1.0f; - else - return 0.0f; - } - - // Check if boss is casting blistering cold (using both normal and heroic spell IDs) - if (boss->HasUnitState(UNIT_STATE_CASTING) && - (boss->FindCurrentSpellBySpellId(70123) || boss->FindCurrentSpellBySpellId(71047) || - boss->FindCurrentSpellBySpellId(71048) || boss->FindCurrentSpellBySpellId(71049))) - { - // If this is the blistering cold action, give it highest priority - if (dynamic_cast(action) || - dynamic_cast(action) || - dynamic_cast(action) || - dynamic_cast(action)) - return 1.0f; - - // Disable all other actions while blistering cold is casting - return 0.0f; - } - - // Highest priority if we have beacon - if (bot->HasAura(SPELL_FROST_BEACON)) - { - if (dynamic_cast(action)) - return 1.0f; - else - return 0.0f; - } - - Group* group = bot->GetGroup(); - // Check if anyone in group has Frost Beacon (SPELL_FROST_BEACON) - bool anyoneHasFrostBeacon = false; - - if (group) - { - for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) - { - Player* member = ref->GetSource(); - if (member && member->IsAlive() && member->HasAura(SPELL_FROST_BEACON)) - { - anyoneHasFrostBeacon = true; - break; - } - } - } - - if (anyoneHasFrostBeacon && boss && - boss->GetExactDist2d(ICC_SINDRAGOSA_FLYING_POSITION.GetPositionX(), - ICC_SINDRAGOSA_FLYING_POSITION.GetPositionY()) < 30.0f && - !boss->HealthBelowPct(25) && !boss->HealthAbovePct(99)) - { - if (dynamic_cast(action)) - return 1.0f; - else - return 0.0f; - } - - if (anyoneHasFrostBeacon && !botAI->IsMainTank(bot)) - { - if (dynamic_cast(action)) - return 0.0f; - } - - if (botAI->IsMainTank(bot)) - { - Aura* aura = botAI->GetAura("mystic buffet", bot, false, true); - if (aura && aura->GetStackAmount() >= 6) - { - if (dynamic_cast(action)) - return 1.0f; - else - return 0.0f; - } - } - - if (!botAI->IsTank(bot) && boss && boss->HealthBelowPct(35)) - { - if (dynamic_cast(action)) - return 0.0f; - } - - if (boss && botAI->IsTank(bot)) - { - if (boss->HealthBelowPct(35)) - { - if (dynamic_cast(action) || dynamic_cast(action) || - dynamic_cast(action) || dynamic_cast(action)) - return 1.0f; - else - return 0.0f; - } - } - - if (boss && boss->GetExactDist2d(ICC_SINDRAGOSA_FLYING_POSITION.GetPositionX(), ICC_SINDRAGOSA_FLYING_POSITION.GetPositionY()) < 30.0f && !boss->HealthBelowPct(25) && !boss->HealthAbovePct(99)) - { - if (dynamic_cast(action)) - return 1.0f; - - if (dynamic_cast(action) || dynamic_cast(action) || - dynamic_cast(action) || dynamic_cast(action) || - dynamic_cast(action) || dynamic_cast(action) || - dynamic_cast(action) || dynamic_cast(action) || dynamic_cast(action) || - dynamic_cast(action) || dynamic_cast(action) || - dynamic_cast(action) || dynamic_cast(action) || - dynamic_cast(action) || dynamic_cast(action) || - dynamic_cast(action) || dynamic_cast(action) || - dynamic_cast(action) || dynamic_cast(action) || - dynamic_cast(action) || dynamic_cast(action) || - dynamic_cast(action) || dynamic_cast(action) || - dynamic_cast(action)) - return 0.0f; - } - - return 1.0f; -} - -float IccLichKingAddsMultiplier::GetValue(Action* action) -{ - Unit* terenasMenethilHC = bot->FindNearestCreature(NPC_TERENAS_MENETHIL_HC, 55.0f); - - if (!terenasMenethilHC) - if (dynamic_cast(action)) - return 0.0f; - - if (terenasMenethilHC) - { - Unit* mainTank = AI_VALUE(Unit*, "main tank"); - - if (!botAI->IsMainTank(bot) && mainTank && bot->GetExactDist2d(mainTank->GetPositionX(), mainTank->GetPositionY()) < 2.0f) - { - if (dynamic_cast(action)) - return 0.0f; - } - - if (botAI->IsMelee(bot) || (bot->getClass() == CLASS_WARLOCK)) - { - if (dynamic_cast(action) || dynamic_cast(action)) - return 1.0f; - else - return 0.0f; - } - - if (dynamic_cast(action) || dynamic_cast(action) || - dynamic_cast(action) || dynamic_cast(action) || - dynamic_cast(action) || dynamic_cast(action) || - dynamic_cast(action) || dynamic_cast(action) || - dynamic_cast(action) || dynamic_cast(action)) - return 0.0f; - } - - Unit* boss = AI_VALUE2(Unit*, "find target", "the lich king"); - if (!boss) - return 1.0f; - - // Handle cure actions - if (dynamic_cast(action) || dynamic_cast(action) || - dynamic_cast(action) || - dynamic_cast(action) || dynamic_cast(action)) - { - Group* group = bot->GetGroup(); - if (!group) - return 1.0f; - - // Check if any bot in the group has plague - bool anyBotHasPlague = false; - ObjectGuid plaguedPlayerGuid; // Track who has plague - for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) - { - if (Player* member = ref->GetSource()) - { - if (botAI->HasAura("Necrotic Plague", member)) - { - anyBotHasPlague = true; - plaguedPlayerGuid = member->GetGUID(); // Changed from GetObjectGuid() - break; - } - } - } - - uint32 currentTime = getMSTime(); - - // Reset state if no one has plague - if (!anyBotHasPlague) - { - std::lock_guard lock(g_plagueMutex); // Properly initialized - g_plagueTimes.clear(); - g_allowCure.clear(); - return 1.0f; - } - - { // New scope for lock_guard - std::lock_guard lock(g_plagueMutex); // Properly initialized - - // Start timer if this is a new plague - if (g_plagueTimes.find(plaguedPlayerGuid) == g_plagueTimes.end()) - { - g_plagueTimes[plaguedPlayerGuid] = currentTime; - g_allowCure[plaguedPlayerGuid] = false; - return 0.0f; - } - - // Once we allow cure, keep allowing it until plague is gone - if (g_allowCure[plaguedPlayerGuid]) - { - return 1.0f; - } - - // Check if enough time has passed (2,5 seconds) - if (currentTime - g_plagueTimes[plaguedPlayerGuid] >= 2500) - { - g_allowCure[plaguedPlayerGuid] = true; - return 1.0f; - } - } // lock_guard is automatically released here - - return 0.0f; - } - - if (dynamic_cast(action) && (bot->getClass() != CLASS_HUNTER)) - return 0.0f; - - if (dynamic_cast(action) || dynamic_cast(action) || - dynamic_cast(action) || dynamic_cast(action)) - return 0.0f; - - if (boss && !boss->HealthBelowPct(71)) - { - if (!botAI->IsTank(bot)) - if (dynamic_cast(action)) - return 0.0f; - - if (dynamic_cast(action) || dynamic_cast(action) || - dynamic_cast(action) || dynamic_cast(action) || - dynamic_cast(action) || dynamic_cast(action) || - dynamic_cast(action) || dynamic_cast(action) || - dynamic_cast(action) || dynamic_cast(action) || - dynamic_cast(action) || dynamic_cast(action)) - return 0.0f; - } - - Unit* currentTarget = AI_VALUE(Unit*, "current target"); - - bool hasWinterAura = false; - if (boss && (boss->HasAura(SPELL_REMORSELESS_WINTER1) || boss->HasAura(SPELL_REMORSELESS_WINTER2) || - boss->HasAura(SPELL_REMORSELESS_WINTER3) || boss->HasAura(SPELL_REMORSELESS_WINTER4))) - hasWinterAura = true; - - bool hasWinter2Aura = false; - if (boss && (boss->HasAura(SPELL_REMORSELESS_WINTER5) || boss->HasAura(SPELL_REMORSELESS_WINTER6) || - boss->HasAura(SPELL_REMORSELESS_WINTER7) || boss->HasAura(SPELL_REMORSELESS_WINTER8))) - hasWinter2Aura = true; - - bool isCasting = false; - if (boss && boss->HasUnitState(UNIT_STATE_CASTING)) - isCasting = true; - - bool isWinter = false; - if (boss && (boss->FindCurrentSpellBySpellId(SPELL_REMORSELESS_WINTER1) || - boss->FindCurrentSpellBySpellId(SPELL_REMORSELESS_WINTER2) || - boss->FindCurrentSpellBySpellId(SPELL_REMORSELESS_WINTER5) || - boss->FindCurrentSpellBySpellId(SPELL_REMORSELESS_WINTER6) || - boss->FindCurrentSpellBySpellId(SPELL_REMORSELESS_WINTER3) || - boss->FindCurrentSpellBySpellId(SPELL_REMORSELESS_WINTER4) || - boss->FindCurrentSpellBySpellId(SPELL_REMORSELESS_WINTER7) || - boss->FindCurrentSpellBySpellId(SPELL_REMORSELESS_WINTER8))) - isWinter = true; - - if (hasWinterAura || hasWinter2Aura || (isCasting && isWinter)) - { - if (dynamic_cast(action) || dynamic_cast(action)) - return 1.0f; - - if (botAI->IsAssistTank(bot) && dynamic_cast(action)) - return 0.0f; - - if (dynamic_cast(action)) - return 0.0f; - - if (currentTarget && boss && bot->GetDistance2d(boss->GetPositionX(), boss->GetPositionY()) > 50.0f && currentTarget == boss) - { - if (dynamic_cast(action) || dynamic_cast(action) || - dynamic_cast(action) || dynamic_cast(action) || - dynamic_cast(action) || dynamic_cast(action) || - dynamic_cast(action)) - return 0.0f; - } - - if (currentTarget && (currentTarget->GetEntry() == NPC_ICE_SPHERE1 || currentTarget->GetEntry() == NPC_ICE_SPHERE2 || - currentTarget->GetEntry() == NPC_ICE_SPHERE3 || currentTarget->GetEntry() == NPC_ICE_SPHERE4)) - { - if (dynamic_cast(action) || dynamic_cast(action) || - dynamic_cast(action)) - return 0.0f; - } - - } - - if (botAI->IsRanged(bot) && !botAI->GetAura("Harvest Soul", bot, false, false)) - { - // Check for defile presence - GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); - bool defilePresent = false; - for (auto& npc : npcs) - { - Unit* unit = botAI->GetUnit(npc); - if (unit && unit->IsAlive() && unit->GetEntry() == DEFILE_NPC_ID) // Defile entry - { - defilePresent = true; - break; - } - } - - // Only disable movement if defile is present - if (defilePresent && ( - dynamic_cast(action) || - dynamic_cast(action) || - dynamic_cast(action) || - dynamic_cast(action) || - dynamic_cast(action))) - { - return 0.0f; - } - } - - if (botAI->IsAssistTank(bot) && boss && !boss->HealthBelowPct(71) && currentTarget == boss) - { - if (dynamic_cast(action)) - return 0.0f; - } - - return 1.0f; -} diff --git a/src/Ai/Raid/Icecrown/Util/RaidIccScripts.h b/src/Ai/Raid/Icecrown/Util/RaidIccScripts.h deleted file mode 100644 index e2fc782cad6..00000000000 --- a/src/Ai/Raid/Icecrown/Util/RaidIccScripts.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef _PLAYERBOT_RAIDICCSCRIPTS_H -#define _PLAYERBOT_RAIDICCSCRIPTS_H - -#include "../../../../src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.h" - -#endif diff --git a/src/Ai/Raid/RaidStrategyContext.h b/src/Ai/Raid/RaidStrategyContext.h index f307c994fde..732310b28b5 100644 --- a/src/Ai/Raid/RaidStrategyContext.h +++ b/src/Ai/Raid/RaidStrategyContext.h @@ -19,7 +19,7 @@ #include "RaidVoAStrategy.h" #include "RaidUlduarStrategy.h" #include "RaidOnyxiaStrategy.h" -#include "RaidIccStrategy.h" +#include "ICCStrategy.h" class RaidStrategyContext : public NamedObjectContext { diff --git a/src/Ai/World/Rpg/Action/NewRpgBaseAction.cpp b/src/Ai/World/Rpg/Action/NewRpgBaseAction.cpp index 336c7599dea..5ae65e14e01 100644 --- a/src/Ai/World/Rpg/Action/NewRpgBaseAction.cpp +++ b/src/Ai/World/Rpg/Action/NewRpgBaseAction.cpp @@ -20,6 +20,7 @@ #include "Player.h" #include "PlayerbotAI.h" #include "PlayerbotAIConfig.h" +#include "PlayerbotTextMgr.h" #include "Playerbots.h" #include "Position.h" #include "QuestDef.h" @@ -310,7 +311,10 @@ bool NewRpgBaseAction::InteractWithNpcOrGameObjectForQuest(ObjectGuid guid) { AcceptQuest(quest, guid); if (botAI->GetMaster()) - botAI->TellMasterNoFacing("Quest accepted " + ChatHelper::FormatQuest(quest)); + botAI->TellMasterNoFacing(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "new_rpg_quest_accepted", + "Quest accepted %quest", + {{"%quest", ChatHelper::FormatQuest(quest)}})); BroadcastHelper::BroadcastQuestAccepted(botAI, bot, quest); botAI->rpgStatistic.questAccepted++; LOG_DEBUG("playerbots", "[New RPG] {} accept quest {}", bot->GetName(), quest->GetQuestId()); @@ -319,7 +323,10 @@ bool NewRpgBaseAction::InteractWithNpcOrGameObjectForQuest(ObjectGuid guid) { TurnInQuest(quest, guid); if (botAI->GetMaster()) - botAI->TellMasterNoFacing("Quest rewarded " + ChatHelper::FormatQuest(quest)); + botAI->TellMasterNoFacing(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "new_rpg_quest_rewarded", + "Quest rewarded %quest", + {{"%quest", ChatHelper::FormatQuest(quest)}})); BroadcastHelper::BroadcastQuestTurnedIn(botAI, bot, quest); botAI->rpgStatistic.questRewarded++; LOG_DEBUG("playerbots", "[New RPG] {} turned in quest {}", bot->GetName(), quest->GetQuestId()); @@ -599,7 +606,10 @@ bool NewRpgBaseAction::OrganizeQuestLog() packet << (uint8)i; bot->GetSession()->HandleQuestLogRemoveQuest(packet); if (botAI->GetMaster()) - botAI->TellMasterNoFacing("Quest dropped " + ChatHelper::FormatQuest(quest)); + botAI->TellMasterNoFacing(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "new_rpg_quest_dropped", + "Quest dropped %quest", + {{"%quest", ChatHelper::FormatQuest(quest)}})); botAI->rpgStatistic.questDropped++; dropped++; } @@ -626,7 +636,10 @@ bool NewRpgBaseAction::OrganizeQuestLog() packet << (uint8)i; bot->GetSession()->HandleQuestLogRemoveQuest(packet); if (botAI->GetMaster()) - botAI->TellMasterNoFacing("Quest dropped " + ChatHelper::FormatQuest(quest)); + botAI->TellMasterNoFacing(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "new_rpg_quest_dropped", + "Quest dropped %quest", + {{"%quest", ChatHelper::FormatQuest(quest)}})); botAI->rpgStatistic.questDropped++; dropped++; } @@ -648,7 +661,10 @@ bool NewRpgBaseAction::OrganizeQuestLog() packet << (uint8)i; bot->GetSession()->HandleQuestLogRemoveQuest(packet); if (botAI->GetMaster()) - botAI->TellMasterNoFacing("Quest dropped " + ChatHelper::FormatQuest(quest)); + botAI->TellMasterNoFacing(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "new_rpg_quest_dropped", + "Quest dropped %quest", + {{"%quest", ChatHelper::FormatQuest(quest)}})); botAI->rpgStatistic.questDropped++; } diff --git a/src/Ai/World/Rpg/Action/RpgSubActions.cpp b/src/Ai/World/Rpg/Action/RpgSubActions.cpp index 727f67a8fb0..24d03b7f358 100644 --- a/src/Ai/World/Rpg/Action/RpgSubActions.cpp +++ b/src/Ai/World/Rpg/Action/RpgSubActions.cpp @@ -13,6 +13,7 @@ #include "GuildCreateActions.h" #include "LastMovementValue.h" #include "MovementActions.h" +#include "PlayerbotTextMgr.h" #include "Playerbots.h" #include "PossibleRpgTargetsValue.h" #include "SocialMgr.h" @@ -430,12 +431,15 @@ bool RpgTradeUsefulAction::Execute(Event /*event*/) if (bot->GetTradeData() && bot->GetTradeData()->HasItem(item->GetGUID())) { if (bot->GetGroup() && bot->GetGroup()->IsMember(guidP) && botAI->HasRealPlayerMaster()) - botAI->TellMasterNoFacing( - "You can use this " + chat->FormatItem(item->GetTemplate()) + " better than me, " + - guidP.GetPlayer()->GetName() /*chat->FormatWorldobject(guidP.GetPlayer())*/ + "."); + botAI->TellMasterNoFacing(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "rpg_item_better_for_player", + "You can use this %item better than me, %player.", + {{"%item", chat->FormatItem(item->GetTemplate())}, {"%player", guidP.GetPlayer()->GetName()}})); else - bot->Say("You can use this " + chat->FormatItem(item->GetTemplate()) + " better than me, " + - player->GetName() /*chat->FormatWorldobject(player)*/ + ".", + bot->Say(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "rpg_item_better_for_player", + "You can use this %item better than me, %player.", + {{"%item", chat->FormatItem(item->GetTemplate())}, {"%player", player->GetName()}}), (bot->GetTeamId() == TEAM_ALLIANCE ? LANG_COMMON : LANG_ORCISH)); if (!urand(0, 4) || items.size() < 2) @@ -449,7 +453,10 @@ bool RpgTradeUsefulAction::Execute(Event /*event*/) } } else - bot->Say("Start trade with" + chat->FormatWorldobject(player), + bot->Say(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "rpg_start_trade_with_player", + "Start trade with %player", + {{"%player", chat->FormatWorldobject(player)}}), (bot->GetTeamId() == TEAM_ALLIANCE ? LANG_COMMON : LANG_ORCISH)); botAI->SetNextCheckDelay(sPlayerbotAIConfig.rpgDelay); diff --git a/src/Bot/Engine/BuildSharedActionContexts.cpp b/src/Bot/Engine/BuildSharedActionContexts.cpp index 3cd68a20a50..c2972a68f29 100644 --- a/src/Bot/Engine/BuildSharedActionContexts.cpp +++ b/src/Bot/Engine/BuildSharedActionContexts.cpp @@ -19,7 +19,7 @@ #include "Ai/Raid/VaultOfArchavon/RaidVoAActionContext.h" #include "Ai/Raid/Ulduar/RaidUlduarActionContext.h" #include "Ai/Raid/Onyxia/RaidOnyxiaActionContext.h" -#include "Ai/Raid/Icecrown/RaidIccActionContext.h" +#include "Ai/Raid/ICC/ICCActionContext.h" #include "Ai/Dungeon/TbcDungeonActionContext.h" #include "Ai/Dungeon/WotlkDungeonActionContext.h" diff --git a/src/Bot/Engine/BuildSharedTriggerContexts.cpp b/src/Bot/Engine/BuildSharedTriggerContexts.cpp index 403e162699f..43b59542c87 100644 --- a/src/Bot/Engine/BuildSharedTriggerContexts.cpp +++ b/src/Bot/Engine/BuildSharedTriggerContexts.cpp @@ -19,7 +19,7 @@ #include "Ai/Raid/VaultOfArchavon/RaidVoATriggerContext.h" #include "Ai/Raid/Ulduar/RaidUlduarTriggerContext.h" #include "Ai/Raid/Onyxia/RaidOnyxiaTriggerContext.h" -#include "Ai/Raid/Icecrown/RaidIccTriggerContext.h" +#include "Ai/Raid/ICC/ICCTriggerContext.h" #include "Ai/Dungeon/TbcDungeonTriggerContext.h" #include "Ai/Dungeon/WotlkDungeonTriggerContext.h" diff --git a/src/Bot/Factory/AiFactory.cpp b/src/Bot/Factory/AiFactory.cpp index 6c638e8071c..f1fd2491f47 100644 --- a/src/Bot/Factory/AiFactory.cpp +++ b/src/Bot/Factory/AiFactory.cpp @@ -91,9 +91,6 @@ uint8 AiFactory::GetPlayerSpecTab(Player* bot) case CLASS_WARLOCK: tab = WARLOCK_TAB_DEMONOLOGY; break; - case CLASS_SHAMAN: - tab = SHAMAN_TAB_ELEMENTAL; - break; } return tab; @@ -292,24 +289,24 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa if (tab == PRIEST_TAB_SHADOW) engine->addStrategiesNoInit("dps", "shadow debuff", "shadow aoe", nullptr); else if (tab == PRIEST_TAB_DISCIPLINE) - engine->addStrategiesNoInit("heal", nullptr); - else - engine->addStrategiesNoInit("holy heal", nullptr); + engine->addStrategy("heal", false); + else // if (tab == PRIEST_TAB_HOLY) + engine->addStrategy("holy heal", false); engine->addStrategiesNoInit("dps assist", "cure", nullptr); break; case CLASS_MAGE: if (tab == MAGE_TAB_ARCANE) - engine->addStrategiesNoInit("arcane", nullptr); + engine->addStrategiesNoInit("arcane", "bdps", nullptr); else if (tab == MAGE_TAB_FIRE) { if (player->HasSpell(44614) /*Frostfire Bolt*/ && player->HasAura(15047) /*Ice Shards*/) - engine->addStrategiesNoInit("frostfire", nullptr); + engine->addStrategiesNoInit("frostfire", "bdps", nullptr); else - engine->addStrategiesNoInit("fire", nullptr); + engine->addStrategiesNoInit("fire", "bdps", nullptr); } - else - engine->addStrategiesNoInit("frost", nullptr); + else // if (tab == MAGE_TAB_FROST) + engine->addStrategiesNoInit("frost", "bmana", nullptr); engine->addStrategiesNoInit("dps", "dps assist", "cure", "cc", "aoe", nullptr); break; @@ -318,7 +315,7 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa engine->addStrategiesNoInit("tank", "tank assist", "pull", "pull back", "aoe", nullptr); else if (tab == WARRIOR_TAB_ARMS || !player->HasSpell(1680)) // Whirlwind engine->addStrategiesNoInit("arms", "aoe", "dps assist", nullptr); - else + else // if (tab == WARRIOR_TAB_FURY) engine->addStrategiesNoInit("fury", "aoe", "dps assist", nullptr); break; case CLASS_SHAMAN: @@ -326,7 +323,7 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa engine->addStrategiesNoInit("ele", "stoneskin", "wrath", "mana spring", "wrath of air", nullptr); else if (tab == SHAMAN_TAB_RESTORATION) engine->addStrategiesNoInit("resto", "stoneskin", "flametongue", "mana spring", "wrath of air", nullptr); - else + else // if (tab == SHAMAN_TAB_ENHANCEMENT) engine->addStrategiesNoInit("enh", "strength of earth", "magma", "healing stream", "windfury", nullptr); engine->addStrategiesNoInit("dps assist", "cure", "aoe", nullptr); @@ -336,39 +333,38 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa engine->addStrategiesNoInit("tank", "tank assist", "pull", "pull back", "bthreat", "barmor", "cure", nullptr); else if (tab == PALADIN_TAB_HOLY) engine->addStrategiesNoInit("heal", "dps assist", "cure", "bcast", nullptr); - else + else // if (tab == PALADIN_TAB_RETRIBUTION) engine->addStrategiesNoInit("dps", "dps assist", "cure", "baoe", nullptr); break; case CLASS_DRUID: if (tab == DRUID_TAB_BALANCE) { - engine->addStrategiesNoInit("caster", "cure", "caster aoe", "dps assist", nullptr); - engine->addStrategy("caster debuff", false); + engine->addStrategiesNoInit("balance", "cure", "aoe", "cc", "dps assist", nullptr); } else if (tab == DRUID_TAB_RESTORATION) - engine->addStrategiesNoInit("heal", "cure", "dps assist", nullptr); + engine->addStrategiesNoInit("resto", "cure", "dps assist", "blanketing", "tranquility", nullptr); else { if (player->HasSpell(768) /*cat form*/ && !player->HasAura(16931) /*thick hide*/) - engine->addStrategiesNoInit("cat", "dps assist", nullptr); + engine->addStrategiesNoInit("cat", "aoe", "cc", "dps assist", "feral charge", nullptr); else - engine->addStrategiesNoInit("bear", "tank assist", "pull", "pull back", nullptr); + engine->addStrategiesNoInit("bear", "tank assist", "pull", "pull back", "feral charge", nullptr); } break; case CLASS_HUNTER: if (tab == HUNTER_TAB_BEAST_MASTERY) - engine->addStrategiesNoInit("bm", nullptr); + engine->addStrategy("bm", false); else if (tab == HUNTER_TAB_MARKSMANSHIP) - engine->addStrategiesNoInit("mm", nullptr); - else - engine->addStrategiesNoInit("surv", nullptr); + engine->addStrategy("mm", false); + else // if (tab == HUNTER_TAB_SURVIVAL) + engine->addStrategy("surv", false); engine->addStrategiesNoInit("cc", "dps assist", "aoe", "bdps", nullptr); break; case CLASS_ROGUE: if (tab == ROGUE_TAB_ASSASSINATION || tab == ROGUE_TAB_SUBTLETY) engine->addStrategiesNoInit("melee", "dps assist", "aoe", nullptr); - else + else // if (tab == ROGUE_TAB_COMBAT) engine->addStrategiesNoInit("dps", "dps assist", "aoe", nullptr); break; case CLASS_WARLOCK: @@ -376,7 +372,7 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa engine->addStrategiesNoInit("affli", "curse of agony", nullptr); else if (tab == WARLOCK_TAB_DEMONOLOGY) engine->addStrategiesNoInit("demo", "curse of agony", "meta melee", nullptr); - else + else // if (tab == WARLOCK_TAB_DESTRUCTION) engine->addStrategiesNoInit("destro", "curse of elements", nullptr); engine->addStrategiesNoInit("cc", "dps assist", "aoe", nullptr); @@ -386,7 +382,7 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa engine->addStrategiesNoInit("blood", "tank assist", "pull", "pull back", nullptr); else if (tab == DEATH_KNIGHT_TAB_FROST) engine->addStrategiesNoInit("frost", "frost aoe", "dps assist", nullptr); - else + else // if (tab == DEATH_KNIGHT_TAB_UNHOLY) engine->addStrategiesNoInit("unholy", "unholy aoe", "dps assist", nullptr); break; } @@ -426,15 +422,14 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa { if (tab == DRUID_TAB_RESTORATION) { - engine->addStrategiesNoInit("caster", "caster aoe", nullptr); - engine->addStrategy("caster debuff", false); + engine->addStrategiesNoInit("aoe", nullptr); } break; } case CLASS_SHAMAN: { if (tab == SHAMAN_TAB_RESTORATION) - engine->addStrategiesNoInit("caster", "caster aoe", "bmana", nullptr); + engine->addStrategiesNoInit("caster", "caster aoe", nullptr); break; } case CLASS_PALADIN: @@ -527,11 +522,6 @@ void AiFactory::AddDefaultNonCombatStrategies(Player* player, PlayerbotAI* const nonCombatEngine->addStrategiesNoInit("bdps", "dps assist", "pet", nullptr); break; case CLASS_SHAMAN: - if (tab == SHAMAN_TAB_ELEMENTAL || tab == SHAMAN_TAB_RESTORATION) - nonCombatEngine->addStrategy("bmana", false); - else - nonCombatEngine->addStrategy("bdps", false); - nonCombatEngine->addStrategiesNoInit("dps assist", "cure", nullptr); break; case CLASS_MAGE: diff --git a/src/Bot/Factory/PlayerbotFactory.cpp b/src/Bot/Factory/PlayerbotFactory.cpp index f8e0e5dfe27..161410c3f61 100644 --- a/src/Bot/Factory/PlayerbotFactory.cpp +++ b/src/Bot/Factory/PlayerbotFactory.cpp @@ -850,36 +850,36 @@ void PlayerbotFactory::Refresh() void PlayerbotFactory::InitConsumables() { - int specTab = AiFactory::GetPlayerSpecTab(bot); + uint8 specTab = AiFactory::GetPlayerSpecTab(bot); std::vector> items; switch (bot->getClass()) { case CLASS_PRIEST: { - // Discipline or Holy: Mana Oil - if (specTab == 0 || specTab == 1) + if (specTab == PRIEST_TAB_SHADOW) { - std::vector mana_oils = {BRILLIANT_MANA_OIL, SUPERIOR_MANA_OIL, LESSER_MANA_OIL, MINOR_MANA_OIL}; - for (uint32 itemId : mana_oils) + std::vector wizard_oils = { + BRILLIANT_WIZARD_OIL, SUPERIOR_WIZARD_OIL, WIZARD_OIL, LESSER_WIZARD_OIL, MINOR_WIZARD_OIL }; + for (uint32 itemId : wizard_oils) { ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId); if (proto->RequiredLevel > level || level > 75) continue; - items.push_back({itemId, 4}); + items.push_back({itemId, 2}); break; } } - // Shadow: Wizard Oil - if (specTab == 2) + else { - std::vector wizard_oils = {BRILLIANT_WIZARD_OIL, SUPERIOR_WIZARD_OIL, WIZARD_OIL, LESSER_WIZARD_OIL, MINOR_WIZARD_OIL}; - for (uint32 itemId : wizard_oils) + std::vector mana_oils = { + BRILLIANT_MANA_OIL, SUPERIOR_MANA_OIL, LESSER_MANA_OIL, MINOR_MANA_OIL }; + for (uint32 itemId : mana_oils) { ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId); if (proto->RequiredLevel > level || level > 75) continue; - items.push_back({itemId, 4}); + items.push_back({itemId, 2}); break; } } @@ -887,38 +887,41 @@ void PlayerbotFactory::InitConsumables() } case CLASS_MAGE: { - // Always Wizard Oil - std::vector wizard_oils = {BRILLIANT_WIZARD_OIL, SUPERIOR_WIZARD_OIL, WIZARD_OIL, LESSER_WIZARD_OIL, MINOR_WIZARD_OIL}; + std::vector wizard_oils = { + BRILLIANT_WIZARD_OIL, SUPERIOR_WIZARD_OIL, WIZARD_OIL, LESSER_WIZARD_OIL, MINOR_WIZARD_OIL }; for (uint32 itemId : wizard_oils) { ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId); if (proto->RequiredLevel > level || level > 75) continue; - items.push_back({itemId, 4}); + items.push_back({itemId, 2}); break; } break; } case CLASS_DRUID: { - // Balance: Wizard Oil - if (specTab == 0) + if (specTab == DRUID_TAB_BALANCE) { - std::vector wizard_oils = {BRILLIANT_WIZARD_OIL, SUPERIOR_WIZARD_OIL, WIZARD_OIL, LESSER_WIZARD_OIL, MINOR_WIZARD_OIL}; + std::vector wizard_oils = { + BRILLIANT_WIZARD_OIL, SUPERIOR_WIZARD_OIL, WIZARD_OIL, LESSER_WIZARD_OIL, MINOR_WIZARD_OIL }; for (uint32 itemId : wizard_oils) { ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId); if (proto->RequiredLevel > level || level > 75) continue; - items.push_back({itemId, 4}); + items.push_back({itemId, 2}); break; } } - // Feral: Sharpening Stones & Weightstones - else if (specTab == 1) + else if (specTab == DRUID_TAB_FERAL) { - std::vector sharpening_stones = {ADAMANTITE_SHARPENING_STONE, FEL_SHARPENING_STONE, DENSE_SHARPENING_STONE, SOLID_SHARPENING_STONE, HEAVY_SHARPENING_STONE, COARSE_SHARPENING_STONE, ROUGH_SHARPENING_STONE}; - std::vector weightstones = {ADAMANTITE_WEIGHTSTONE, FEL_WEIGHTSTONE, DENSE_WEIGHTSTONE, SOLID_WEIGHTSTONE, HEAVY_WEIGHTSTONE, COARSE_WEIGHTSTONE, ROUGH_WEIGHTSTONE}; + std::vector sharpening_stones = { + ADAMANTITE_SHARPENING_STONE, FEL_SHARPENING_STONE, DENSE_SHARPENING_STONE, SOLID_SHARPENING_STONE, + HEAVY_SHARPENING_STONE, COARSE_SHARPENING_STONE, ROUGH_SHARPENING_STONE }; + std::vector weightstones = { + ADAMANTITE_WEIGHTSTONE, FEL_WEIGHTSTONE, DENSE_WEIGHTSTONE, SOLID_WEIGHTSTONE, + HEAVY_WEIGHTSTONE, COARSE_WEIGHTSTONE, ROUGH_WEIGHTSTONE }; for (uint32 itemId : sharpening_stones) { ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId); @@ -936,16 +939,16 @@ void PlayerbotFactory::InitConsumables() break; } } - // Restoration: Mana Oil - else if (specTab == 2) + else { - std::vector mana_oils = {BRILLIANT_MANA_OIL, SUPERIOR_MANA_OIL, LESSER_MANA_OIL, MINOR_MANA_OIL}; + std::vector mana_oils = { + BRILLIANT_MANA_OIL, SUPERIOR_MANA_OIL, LESSER_MANA_OIL, MINOR_MANA_OIL }; for (uint32 itemId : mana_oils) { ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId); if (proto->RequiredLevel > level || level > 75) continue; - items.push_back({itemId, 4}); + items.push_back({itemId, 2}); break; } } @@ -953,37 +956,27 @@ void PlayerbotFactory::InitConsumables() } case CLASS_PALADIN: { - // Holy: Mana Oil - if (specTab == 0) + if (specTab == PALADIN_TAB_HOLY) { - std::vector mana_oils = {BRILLIANT_MANA_OIL, SUPERIOR_MANA_OIL, LESSER_MANA_OIL, MINOR_MANA_OIL}; + std::vector mana_oils = { + BRILLIANT_MANA_OIL, SUPERIOR_MANA_OIL, LESSER_MANA_OIL, MINOR_MANA_OIL }; for (uint32 itemId : mana_oils) { ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId); if (proto->RequiredLevel > level || level > 75) continue; - items.push_back({itemId, 4}); + items.push_back({itemId, 2}); break; } } - // Protection: Wizard Oil (Protection prioritizes Superior over Brilliant) - else if (specTab == 1) - { - std::vector wizard_oils = {BRILLIANT_WIZARD_OIL, SUPERIOR_WIZARD_OIL, WIZARD_OIL, LESSER_WIZARD_OIL, MINOR_WIZARD_OIL}; - for (uint32 itemId : wizard_oils) - { - ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId); - if (proto->RequiredLevel > level || level > 75) - continue; - items.push_back({itemId, 4}); - break; - } - } - // Retribution: Sharpening Stones & Weightstones - else if (specTab == 2) + else { - std::vector sharpening_stones = {ADAMANTITE_SHARPENING_STONE, FEL_SHARPENING_STONE, DENSE_SHARPENING_STONE, SOLID_SHARPENING_STONE, HEAVY_SHARPENING_STONE, COARSE_SHARPENING_STONE, ROUGH_SHARPENING_STONE}; - std::vector weightstones = {ADAMANTITE_WEIGHTSTONE, FEL_WEIGHTSTONE, DENSE_WEIGHTSTONE, SOLID_WEIGHTSTONE, HEAVY_WEIGHTSTONE, COARSE_WEIGHTSTONE, ROUGH_WEIGHTSTONE}; + std::vector sharpening_stones = { + ADAMANTITE_SHARPENING_STONE, FEL_SHARPENING_STONE, DENSE_SHARPENING_STONE, SOLID_SHARPENING_STONE, + HEAVY_SHARPENING_STONE, COARSE_SHARPENING_STONE, ROUGH_SHARPENING_STONE }; + std::vector weightstones = { + ADAMANTITE_WEIGHTSTONE, FEL_WEIGHTSTONE, DENSE_WEIGHTSTONE, SOLID_WEIGHTSTONE, + HEAVY_WEIGHTSTONE, COARSE_WEIGHTSTONE, ROUGH_WEIGHTSTONE }; for (uint32 itemId : sharpening_stones) { ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId); @@ -1005,10 +998,14 @@ void PlayerbotFactory::InitConsumables() } case CLASS_WARRIOR: case CLASS_HUNTER: + case CLASS_DEATH_KNIGHT: { - // Sharpening Stones & Weightstones - std::vector sharpening_stones = {ADAMANTITE_SHARPENING_STONE, FEL_SHARPENING_STONE, DENSE_SHARPENING_STONE, SOLID_SHARPENING_STONE, HEAVY_SHARPENING_STONE, COARSE_SHARPENING_STONE, ROUGH_SHARPENING_STONE}; - std::vector weightstones = {ADAMANTITE_WEIGHTSTONE, FEL_WEIGHTSTONE, DENSE_WEIGHTSTONE, SOLID_WEIGHTSTONE, HEAVY_WEIGHTSTONE, COARSE_WEIGHTSTONE, ROUGH_WEIGHTSTONE}; + std::vector sharpening_stones = { + ADAMANTITE_SHARPENING_STONE, FEL_SHARPENING_STONE, DENSE_SHARPENING_STONE, SOLID_SHARPENING_STONE, + HEAVY_SHARPENING_STONE, COARSE_SHARPENING_STONE, ROUGH_SHARPENING_STONE }; + std::vector weightstones = { + ADAMANTITE_WEIGHTSTONE, FEL_WEIGHTSTONE, DENSE_WEIGHTSTONE, SOLID_WEIGHTSTONE, + HEAVY_WEIGHTSTONE, COARSE_WEIGHTSTONE, ROUGH_WEIGHTSTONE }; for (uint32 itemId : sharpening_stones) { ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId); @@ -1029,9 +1026,12 @@ void PlayerbotFactory::InitConsumables() } case CLASS_ROGUE: { - // Poisons - std::vector instant_poisons = {INSTANT_POISON_IX, INSTANT_POISON_VIII, INSTANT_POISON_VII, INSTANT_POISON_VI, INSTANT_POISON_V, INSTANT_POISON_IV, INSTANT_POISON_III, INSTANT_POISON_II, INSTANT_POISON}; - std::vector deadly_poisons = {DEADLY_POISON_IX, DEADLY_POISON_VIII, DEADLY_POISON_VII, DEADLY_POISON_VI, DEADLY_POISON_V, DEADLY_POISON_IV, DEADLY_POISON_III, DEADLY_POISON_II, DEADLY_POISON}; + std::vector instant_poisons = { + INSTANT_POISON_IX, INSTANT_POISON_VIII, INSTANT_POISON_VII, INSTANT_POISON_VI, INSTANT_POISON_V, + INSTANT_POISON_IV, INSTANT_POISON_III, INSTANT_POISON_II, INSTANT_POISON }; + std::vector deadly_poisons = { + DEADLY_POISON_IX, DEADLY_POISON_VIII, DEADLY_POISON_VII, DEADLY_POISON_VI, DEADLY_POISON_V, + DEADLY_POISON_IV, DEADLY_POISON_III, DEADLY_POISON_II, DEADLY_POISON }; for (uint32 itemId : deadly_poisons) { ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId); diff --git a/src/Bot/PlayerbotAI.cpp b/src/Bot/PlayerbotAI.cpp index 958a5799ab1..e1096b0cce2 100644 --- a/src/Bot/PlayerbotAI.cpp +++ b/src/Bot/PlayerbotAI.cpp @@ -38,11 +38,13 @@ #include "ObjectMgr.h" #include "PerfMonitor.h" #include "Player.h" +#include "PlayerbotTextMgr.h" #include "PlayerbotAIConfig.h" #include "PlayerbotMgr.h" #include "PlayerbotGuildMgr.h" #include "Playerbots.h" #include "PositionValue.h" +#include "RBAC.h" #include "RandomPlayerbotMgr.h" #include "SayAction.h" #include "ScriptMgr.h" @@ -450,9 +452,11 @@ void PlayerbotAI::UpdateAIGroupMaster() botAI->ChangeStrategy("+follow", BOT_STATE_NON_COMBAT); if (botAI->GetMaster() == botAI->GetGroupLeader()) - botAI->TellMaster("Hello, I follow you!"); + botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "hello_follow", "Hello, I follow you!", {})); else - botAI->TellMaster(!urand(0, 2) ? "Hello!" : "Hi!"); + botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "hello", "Hello!", {})); } else { @@ -507,7 +511,7 @@ void PlayerbotAI::UpdateAIInternal([[maybe_unused]] uint32 elapsed, bool minimal logout = true; if (bot->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING) || bot->HasUnitState(UNIT_STATE_IN_FLIGHT) || - botWorldSessionPtr->GetSecurity() >= (AccountTypes)sWorld->getIntConfig(CONFIG_INSTANT_LOGOUT)) + botWorldSessionPtr->HasPermission(rbac::RBAC_PERM_INSTANT_LOGOUT)) { logout = true; } @@ -515,7 +519,7 @@ void PlayerbotAI::UpdateAIInternal([[maybe_unused]] uint32 elapsed, bool minimal if (master && (master->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING) || master->HasUnitState(UNIT_STATE_IN_FLIGHT) || (master->GetSession() && - master->GetSession()->GetSecurity() >= (AccountTypes)sWorld->getIntConfig(CONFIG_INSTANT_LOGOUT)))) + master->GetSession()->HasPermission(rbac::RBAC_PERM_INSTANT_LOGOUT)))) { logout = true; } @@ -856,7 +860,8 @@ void PlayerbotAI::Reset(bool full) { WorldPackets::Character::LogoutCancel data = WorldPacket(CMSG_LOGOUT_CANCEL); bot->GetSession()->HandleLogoutCancelOpcode(data); - TellMaster("Logout cancelled!"); + TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "logout_cancel", "Logout cancelled!", {})); } currentEngine = engines[BOT_STATE_NON_COMBAT]; @@ -3003,7 +3008,7 @@ bool PlayerbotAI::IsTellAllowed(PlayerbotSecurityLevel securityLevel) return false; if (sPlayerbotAIConfig.whisperDistance && !bot->GetGroup() && sRandomPlayerbotMgr.IsRandomBot(bot) && - master->GetSession()->GetSecurity() < SEC_GAMEMASTER && + !master->CanBeGameMaster() && (bot->GetMapId() != master->GetMapId() || ServerFacade::instance().GetDistance2d(bot, master) > sPlayerbotAIConfig.whisperDistance)) return false; diff --git a/src/Bot/PlayerbotMgr.cpp b/src/Bot/PlayerbotMgr.cpp index 9d3d61ca335..0634bbf781f 100644 --- a/src/Bot/PlayerbotMgr.cpp +++ b/src/Bot/PlayerbotMgr.cpp @@ -27,6 +27,7 @@ #include "PlayerbotFactory.h" #include "PlayerbotOperations.h" #include "PlayerbotSecurity.h" +#include "PlayerbotTextMgr.h" #include "PlayerbotWorldThreadProcessor.h" #include "Playerbots.h" #include "PlayerbotGuildMgr.h" @@ -320,7 +321,8 @@ void PlayerbotMgr::CancelLogout() { WorldPackets::Character::LogoutCancel data = WorldPacket(CMSG_LOGOUT_CANCEL); bot->GetSession()->HandleLogoutCancelOpcode(data); - botAI->TellMaster("Logout cancelled!"); + botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "logout_cancel", "Logout cancelled!", {})); } } @@ -411,7 +413,8 @@ void PlayerbotHolder::DisablePlayerBot(ObjectGuid guid) { return; } - botAI->TellMaster("Goodbye!"); + botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "goodbye", "Goodbye!", {})); bot->StopMoving(); bot->GetMotionMaster()->Clear(); @@ -544,7 +547,8 @@ void PlayerbotHolder::OnBotLogin(Player* const bot) // set delay on login botAI->SetNextCheckDelay(urand(2000, 4000)); - botAI->TellMaster("Hello!", PLAYERBOT_SECURITY_TALK); + botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "hello", "Hello!", {}), PLAYERBOT_SECURITY_TALK); // Queue group operations for world thread if (master && master->GetGroup() && !group) @@ -919,7 +923,7 @@ std::vector PlayerbotHolder::HandlePlayerbotCommand(char const* arg if (!strcmp(cmd, "initself")) { - if (master->GetSession()->GetSecurity() >= SEC_GAMEMASTER) + if (master->CanBeGameMaster()) { // OnBotLogin(master); PlayerbotFactory factory(master, master->GetLevel(), ITEM_QUALITY_EPIC); @@ -938,7 +942,7 @@ std::vector PlayerbotHolder::HandlePlayerbotCommand(char const* arg { if (!strcmp(cmd, "initself=uncommon")) { - if (master->GetSession()->GetSecurity() >= SEC_GAMEMASTER) + if (master->CanBeGameMaster()) { // OnBotLogin(master); PlayerbotFactory factory(master, master->GetLevel(), ITEM_QUALITY_UNCOMMON); @@ -954,7 +958,7 @@ std::vector PlayerbotHolder::HandlePlayerbotCommand(char const* arg } if (!strcmp(cmd, "initself=rare")) { - if (master->GetSession()->GetSecurity() >= SEC_GAMEMASTER) + if (master->CanBeGameMaster()) { // OnBotLogin(master); PlayerbotFactory factory(master, master->GetLevel(), ITEM_QUALITY_RARE); @@ -970,7 +974,7 @@ std::vector PlayerbotHolder::HandlePlayerbotCommand(char const* arg } if (!strcmp(cmd, "initself=epic")) { - if (master->GetSession()->GetSecurity() >= SEC_GAMEMASTER) + if (master->CanBeGameMaster()) { // OnBotLogin(master); PlayerbotFactory factory(master, master->GetLevel(), ITEM_QUALITY_EPIC); @@ -986,7 +990,7 @@ std::vector PlayerbotHolder::HandlePlayerbotCommand(char const* arg } if (!strcmp(cmd, "initself=legendary")) { - if (master->GetSession()->GetSecurity() >= SEC_GAMEMASTER) + if (master->CanBeGameMaster()) { // OnBotLogin(master); PlayerbotFactory factory(master, master->GetLevel(), ITEM_QUALITY_LEGENDARY); @@ -1003,7 +1007,7 @@ std::vector PlayerbotHolder::HandlePlayerbotCommand(char const* arg int32 gs; if (sscanf(cmd, "initself=%d", &gs) != -1) { - if (master->GetSession()->GetSecurity() >= SEC_GAMEMASTER) + if (master->CanBeGameMaster()) { // OnBotLogin(master); PlayerbotFactory factory(master, master->GetLevel(), ITEM_QUALITY_LEGENDARY, gs); @@ -1027,7 +1031,7 @@ std::vector PlayerbotHolder::HandlePlayerbotCommand(char const* arg if (!strcmp(cmd, "reload")) { - if (master->GetSession()->GetSecurity() >= SEC_GAMEMASTER) + if (master->CanBeGameMaster()) { sPlayerbotAIConfig.Initialize(); messages.push_back("Config reloaded."); @@ -1059,7 +1063,7 @@ std::vector PlayerbotHolder::HandlePlayerbotCommand(char const* arg } else if (sPlayerbotAIConfig.selfBotLevel == 0) messages.push_back("Self-bot is disabled"); - else if (sPlayerbotAIConfig.selfBotLevel == 1 && master->GetSession()->GetSecurity() < SEC_GAMEMASTER) + else if (sPlayerbotAIConfig.selfBotLevel == 1 && !master->CanBeGameMaster()) messages.push_back("You do not have permission to enable player botAI"); else { @@ -1079,7 +1083,7 @@ std::vector PlayerbotHolder::HandlePlayerbotCommand(char const* arg if (!strcmp(cmd, "addclass")) { - if (sPlayerbotAIConfig.addClassCommand == 0 && master->GetSession()->GetSecurity() < SEC_GAMEMASTER) + if (sPlayerbotAIConfig.addClassCommand == 0 && !master->CanBeGameMaster()) { messages.push_back("You do not have permission to create bot by addclass command"); return messages; @@ -1304,7 +1308,7 @@ std::vector PlayerbotHolder::HandlePlayerbotCommand(char const* arg else if (master && member != master->GetGUID()) { out << ProcessBotCommand(cmdStr, member, master->GetGUID(), - master->GetSession()->GetSecurity() >= SEC_GAMEMASTER, + master->CanBeGameMaster(), master->GetSession()->GetAccountId(), master->GetGuildId()); } else if (!master) @@ -1639,7 +1643,7 @@ void PlayerbotMgr::OnPlayerLogin(Player* player) // For bot texts (DB-driven), prefer the database locale with a safe fallback. LocaleConstant usedLocale = databaseLocale; - if (usedLocale >= MAX_LOCALES) + if (usedLocale >= TOTAL_LOCALES) usedLocale = LOCALE_enUS; // fallback // set locale priority for bot texts diff --git a/src/Bot/RandomPlayerbotMgr.cpp b/src/Bot/RandomPlayerbotMgr.cpp index 3c0a9054c09..bca11966446 100644 --- a/src/Bot/RandomPlayerbotMgr.cpp +++ b/src/Bot/RandomPlayerbotMgr.cpp @@ -34,6 +34,7 @@ #include "PlayerbotAI.h" #include "PlayerbotAIConfig.h" #include "PlayerbotFactory.h" +#include "PlayerbotTextMgr.h" #include "Playerbots.h" #include "Position.h" #include "RaceMgr.h" @@ -2589,7 +2590,8 @@ void RandomPlayerbotMgr::OnPlayerLogin(Player* player) { botAI->SetMaster(player); botAI->ResetStrategies(); - botAI->TellMaster("Hello"); + botAI->TellMaster(PlayerbotTextMgr::instance().GetBotTextOrDefault( + "hello", "Hello", {})); } break; diff --git a/src/Mgr/Item/BisListMgr.cpp b/src/Mgr/Item/BisListMgr.cpp new file mode 100644 index 00000000000..324b32a93a6 --- /dev/null +++ b/src/Mgr/Item/BisListMgr.cpp @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#include "BisListMgr.h" + +#include "DatabaseEnv.h" +#include "Field.h" +#include "Log.h" +#include "QueryResult.h" + +void BisListMgr::LoadAll() +{ + _bis.clear(); + + QueryResult result = PlayerbotsDatabase.Query( + "SELECT class, tab, slot, faction, auto_gear_score_limit, item_id FROM playerbots_bis_gear"); + if (!result) + { + LOG_INFO("server.loading", "playerbots_bis_gear table missing or empty"); + return; + } + + uint32 count = 0; + do + { + Field* fields = result->Fetch(); + uint8 cls = fields[0].Get(); + uint8 tab = fields[1].Get(); + uint8 slot = fields[2].Get(); + uint8 faction = fields[3].Get(); + uint16 autoGearScoreLimit = fields[4].Get(); + uint32 item = fields[5].Get(); + + _bis[autoGearScoreLimit][MakeKey(cls, tab)][faction][slot] = item; + ++count; + } while (result->NextRow()); + + LOG_INFO("server.loading", "Loaded {} BiS entries across {} item levels", + count, static_cast(_bis.size())); +} + +std::map BisListMgr::GetBisFor(uint16 autoGearScoreLimit, uint8 cls, uint8 tab, uint8 faction) const +{ + auto ilvlIt = _bis.find(autoGearScoreLimit); + if (ilvlIt == _bis.end()) + return {}; + + auto comboIt = ilvlIt->second.find(MakeKey(cls, tab)); + if (comboIt == ilvlIt->second.end()) + return {}; + + std::map result; + + // Base: faction=0 (Both). + auto bothIt = comboIt->second.find(0); + if (bothIt != comboIt->second.end()) + result = bothIt->second; + + // Faction-specific overrides Both. + if (faction == 1 || faction == 2) + { + auto facIt = comboIt->second.find(faction); + if (facIt != comboIt->second.end()) + for (auto const& kv : facIt->second) + result[kv.first] = kv.second; + } + + return result; +} + +std::map BisListMgr::GetBisForNearest(uint16 requestedIlvl, uint16 maxDrop, uint8 cls, uint8 tab, + uint8 faction, uint16* outResolved) const +{ + uint16 floor = requestedIlvl > maxDrop ? requestedIlvl - maxDrop : 1; + for (uint16 try_ilvl = requestedIlvl; try_ilvl >= floor; --try_ilvl) + { + auto result = GetBisFor(try_ilvl, cls, tab, faction); + if (!result.empty()) + { + if (outResolved) + *outResolved = try_ilvl; + return result; + } + if (try_ilvl == 0) + break; + } + if (outResolved) + *outResolved = 0; + return {}; +} diff --git a/src/Mgr/Item/BisListMgr.h b/src/Mgr/Item/BisListMgr.h new file mode 100644 index 00000000000..04401ec34b4 --- /dev/null +++ b/src/Mgr/Item/BisListMgr.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license, you may redistribute it + * and/or modify it under version 3 of the License, or (at your option), any later version. + */ + +#ifndef _PLAYERBOT_BISLISTMGR_H +#define _PLAYERBOT_BISLISTMGR_H + +#include "Define.h" + +#include + +class BisListMgr +{ +public: + static BisListMgr* instance() + { + static BisListMgr inst; + return &inst; + } + + void LoadAll(); + + // faction: 1=Alliance, 2=Horde. Faction-specific rows override faction=0 (Both). + // Returns slot -> itemId for the matching auto_gear_score_limit tier. Empty map = no data. + std::map GetBisFor(uint16 autoGearScoreLimit, uint8 cls, uint8 tab, uint8 faction) const; + + // Closest-lower fallback: scan ilvls down from requested to (requested - maxDrop), return first non-empty set. + // outResolved receives the matched ilvl (0 if nothing matched within the window). + std::map GetBisForNearest(uint16 requestedIlvl, uint16 maxDrop, uint8 cls, uint8 tab, + uint8 faction, uint16* outResolved = nullptr) const; + +private: + BisListMgr() = default; + + static uint16 MakeKey(uint8 cls, uint8 tab) { return (uint16(cls) << 8) | tab; } + + // autoGearScoreLimit -> (cls<<8|tab) -> faction (0/1/2) -> slot -> itemId + std::map>>> _bis; +}; + +#define sBisListMgr BisListMgr::instance() + +#endif diff --git a/src/Mgr/Security/PlayerbotSecurity.cpp b/src/Mgr/Security/PlayerbotSecurity.cpp index 4a3bd365535..f2a2863ac6d 100644 --- a/src/Mgr/Security/PlayerbotSecurity.cpp +++ b/src/Mgr/Security/PlayerbotSecurity.cpp @@ -27,7 +27,7 @@ PlayerbotSecurityLevel PlayerbotSecurity::LevelFor(Player* from, DenyReason* rea } // GMs always have full access - if (from->GetSession()->GetSecurity() >= SEC_GAMEMASTER) + if (from->CanBeGameMaster()) return PLAYERBOT_SECURITY_ALLOW_ALL; PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot); @@ -188,9 +188,8 @@ bool PlayerbotSecurity::CheckLevelFor(PlayerbotSecurityLevel level, bool silent, Player* master = botAI->GetMaster(); if (master && botAI->IsOpposing(master)) - if (WorldSession* session = master->GetSession()) - if (session->GetSecurity() < SEC_GAMEMASTER) - return false; + if (master->GetSession() && !master->CanBeGameMaster()) + return false; std::ostringstream out; diff --git a/src/Mgr/Text/PlayerbotTextMgr.cpp b/src/Mgr/Text/PlayerbotTextMgr.cpp index 0998a5392f9..e8be8f53302 100644 --- a/src/Mgr/Text/PlayerbotTextMgr.cpp +++ b/src/Mgr/Text/PlayerbotTextMgr.cpp @@ -38,7 +38,7 @@ void PlayerbotTextMgr::LoadBotTexts() text[0] = fields[1].Get(); uint8 sayType = fields[2].Get(); uint8 replyType = fields[3].Get(); - for (uint8 i = 1; i < MAX_LOCALES; ++i) + for (uint8 i = 1; i < TOTAL_LOCALES; ++i) { text[i] = fields[i + 3].Get(); } @@ -192,9 +192,9 @@ bool PlayerbotTextMgr::GetBotText(std::string name, std::string& text, std::map< void PlayerbotTextMgr::AddLocalePriority(uint32 locale) { - if (locale >= MAX_LOCALES) + if (locale >= TOTAL_LOCALES) { - LOG_WARN("playerbots", "Ignoring locale {} for bot texts because it exceeds MAX_LOCALES ({})", locale, MAX_LOCALES - 1); + LOG_WARN("playerbots", "Ignoring locale {} for bot texts because it exceeds TOTAL_LOCALES ({})", locale, TOTAL_LOCALES - 1); return; } @@ -212,7 +212,7 @@ uint32 PlayerbotTextMgr::GetLocalePriority() } uint32 topLocale = 0; - for (uint8 i = 0; i < MAX_LOCALES; ++i) + for (uint8 i = 0; i < TOTAL_LOCALES; ++i) { if (botTextLocalePriority[i] > botTextLocalePriority[topLocale]) topLocale = i; @@ -223,7 +223,7 @@ uint32 PlayerbotTextMgr::GetLocalePriority() void PlayerbotTextMgr::ResetLocalePriority() { - for (uint8 i = 0; i < MAX_LOCALES; ++i) + for (uint8 i = 0; i < TOTAL_LOCALES; ++i) { botTextLocalePriority[i] = 0; } diff --git a/src/Mgr/Text/PlayerbotTextMgr.h b/src/Mgr/Text/PlayerbotTextMgr.h index 398d7640abc..1613630f947 100644 --- a/src/Mgr/Text/PlayerbotTextMgr.h +++ b/src/Mgr/Text/PlayerbotTextMgr.h @@ -87,7 +87,7 @@ class PlayerbotTextMgr private: PlayerbotTextMgr() { - for (uint8 i = 0; i < MAX_LOCALES; ++i) + for (uint8 i = 0; i < TOTAL_LOCALES; ++i) { botTextLocalePriority[i] = 0; } @@ -102,7 +102,7 @@ class PlayerbotTextMgr std::map> botTexts; std::map botTextChance; - uint32 botTextLocalePriority[MAX_LOCALES]; + uint32 botTextLocalePriority[TOTAL_LOCALES]; }; #endif diff --git a/src/PlayerbotAIConfig.cpp b/src/PlayerbotAIConfig.cpp index 8a0c6b0a0fb..af08edf64d3 100644 --- a/src/PlayerbotAIConfig.cpp +++ b/src/PlayerbotAIConfig.cpp @@ -5,6 +5,7 @@ #include "PlayerbotAIConfig.h" #include +#include "BisListMgr.h" #include "Config.h" #include "NewRpgInfo.h" #include "PlayerbotDungeonRepository.h" @@ -595,6 +596,7 @@ bool PlayerbotAIConfig::Initialize() autoGearCommand = sConfigMgr->GetOption("AiPlayerbot.AutoGearCommand", 1); autoGearCommandAltBots = sConfigMgr->GetOption("AiPlayerbot.AutoGearCommandAltBots", 1); + autoGearBisCommand = sConfigMgr->GetOption("AiPlayerbot.AutoGearBisCommand", 0); autoGearQualityLimit = sConfigMgr->GetOption("AiPlayerbot.AutoGearQualityLimit", 3); autoGearScoreLimit = sConfigMgr->GetOption("AiPlayerbot.AutoGearScoreLimit", 0); @@ -692,6 +694,7 @@ bool PlayerbotAIConfig::Initialize() PlayerbotGuildMgr::instance().Init(); sRandomItemMgr.Init(); sRandomItemMgr.InitAfterAhBot(); + sBisListMgr->LoadAll(); PlayerbotTextMgr::instance().LoadBotTexts(); PlayerbotTextMgr::instance().LoadBotTextChance(); PlayerbotFactory::Init(); diff --git a/src/PlayerbotAIConfig.h b/src/PlayerbotAIConfig.h index 1a343db4dac..715701231da 100644 --- a/src/PlayerbotAIConfig.h +++ b/src/PlayerbotAIConfig.h @@ -427,6 +427,7 @@ class PlayerbotAIConfig altMaintenanceKeyring, altMaintenanceGemsEnchants; int32 autoGearCommand, autoGearCommandAltBots, autoGearQualityLimit, autoGearScoreLimit; + int32 autoGearBisCommand; uint32 useGroundMountAtMinLevel; uint32 useFastGroundMountAtMinLevel; diff --git a/src/Script/Playerbots.cpp b/src/Script/Playerbots.cpp index ab92729a760..e9abd5b74f6 100644 --- a/src/Script/Playerbots.cpp +++ b/src/Script/Playerbots.cpp @@ -526,6 +526,7 @@ class PlayerbotsBattlefieldScript : public BattlefieldScript void AddPlayerbotsSecureLoginScripts(); void AddSC_TempestKeepBotScripts(); +void AddSC_IcecrownBotScripts(); void AddSC_HyjalSummitBotScripts(); void AddPlayerbotsScripts() @@ -542,5 +543,6 @@ void AddPlayerbotsScripts() AddPlayerbotsCommandscripts(); PlayerBotsGuildValidationScript(); AddSC_TempestKeepBotScripts(); + AddSC_IcecrownBotScripts(); AddSC_HyjalSummitBotScripts(); } diff --git a/src/Util/BroadcastHelper.cpp b/src/Util/BroadcastHelper.cpp index 246344bdafa..0804ab9fa94 100644 --- a/src/Util/BroadcastHelper.cpp +++ b/src/Util/BroadcastHelper.cpp @@ -11,7 +11,7 @@ uint8 BroadcastHelper::GetLocale() { uint8 locale = sWorld->GetDefaultDbcLocale(); // -- In case we're using auto detect on config file^M - if (locale >= MAX_LOCALES) + if (locale >= TOTAL_LOCALES) locale = LocaleConstant::LOCALE_enUS; return locale; }