diff --git a/src/strategy/actions/ActionContext.h b/src/strategy/actions/ActionContext.h index 250b9367..06ee6461 100644 --- a/src/strategy/actions/ActionContext.h +++ b/src/strategy/actions/ActionContext.h @@ -63,6 +63,7 @@ #include "WorldBuffAction.h" #include "XpGainAction.h" #include "NewRpgAction.h" +#include "CancelChannelAction.h" class PlayerbotAI; @@ -189,6 +190,7 @@ public: creators["buy tabard"] = &ActionContext::buy_tabard; creators["guild manage nearby"] = &ActionContext::guild_manage_nearby; creators["clean quest log"] = &ActionContext::clean_quest_log; + creators["cancel channel"] = &ActionContext::cancel_channel; // BG Tactics creators["bg tactics"] = &ActionContext::bg_tactics; @@ -298,6 +300,7 @@ private: static Action* arcane_torrent(PlayerbotAI* botAI) { return new CastArcaneTorrentAction(botAI); } static Action* mana_tap(PlayerbotAI* botAI) { return new CastManaTapAction(botAI); } static Action* end_pull(PlayerbotAI* botAI) { return new ChangeCombatStrategyAction(botAI, "-pull"); } + static Action* cancel_channel(PlayerbotAI* botAI) { return new CancelChannelAction(botAI); } static Action* emote(PlayerbotAI* botAI) { return new EmoteAction(botAI); } static Action* talk(PlayerbotAI* botAI) { return new TalkAction(botAI); } diff --git a/src/strategy/actions/CancelChannelAction.cpp b/src/strategy/actions/CancelChannelAction.cpp new file mode 100644 index 00000000..89837475 --- /dev/null +++ b/src/strategy/actions/CancelChannelAction.cpp @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU GPL v2 license, you may redistribute it + * and/or modify it under version 2 of the License, or (at your option), any later version. + */ + +#include "CancelChannelAction.h" +#include "Player.h" +#include "PlayerbotAI.h" + +bool CancelChannelAction::Execute(Event event) +{ + if (bot->GetCurrentSpell(CURRENT_CHANNELED_SPELL)) + { + bot->InterruptSpell(CURRENT_CHANNELED_SPELL); + return true; + } + return false; +} diff --git a/src/strategy/actions/CancelChannelAction.h b/src/strategy/actions/CancelChannelAction.h new file mode 100644 index 00000000..4f352570 --- /dev/null +++ b/src/strategy/actions/CancelChannelAction.h @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU GPL v2 license, you may redistribute it + * and/or modify it under version 2 of the License, or (at your option), any later version. + */ + +#ifndef _PLAYERBOT_CANCELCHANNELACTION_H +#define _PLAYERBOT_CANCELCHANNELACTION_H + +#include "Action.h" + +class PlayerbotAI; + +class CancelChannelAction : public Action +{ +public: + CancelChannelAction(PlayerbotAI* botAI) : Action(botAI, "cancel channel") {} + + bool Execute(Event event) override; +}; + +#endif diff --git a/src/strategy/druid/CasterDruidStrategy.cpp b/src/strategy/druid/CasterDruidStrategy.cpp index ff134960..7feca9d8 100644 --- a/src/strategy/druid/CasterDruidStrategy.cpp +++ b/src/strategy/druid/CasterDruidStrategy.cpp @@ -151,6 +151,8 @@ void CasterDruidStrategy::InitTriggers(std::vector& triggers) void CasterDruidAoeStrategy::InitTriggers(std::vector& triggers) { + triggers.push_back( + new TriggerNode("hurricane channel check", NextAction::array(0, new NextAction("cancel channel", ACTION_HIGH + 2), nullptr))); triggers.push_back( new TriggerNode("medium aoe", NextAction::array(0, new NextAction("hurricane", ACTION_HIGH + 1), nullptr))); triggers.push_back(new TriggerNode( diff --git a/src/strategy/druid/DruidAiObjectContext.cpp b/src/strategy/druid/DruidAiObjectContext.cpp index 3866e1cd..db2d9a70 100644 --- a/src/strategy/druid/DruidAiObjectContext.cpp +++ b/src/strategy/druid/DruidAiObjectContext.cpp @@ -111,6 +111,7 @@ public: creators["eclipse (lunar) cooldown"] = &DruidTriggerFactoryInternal::eclipse_lunar_cooldown; creators["mangle (cat)"] = &DruidTriggerFactoryInternal::mangle_cat; creators["ferocious bite time"] = &DruidTriggerFactoryInternal::ferocious_bite_time; + creators["hurricane channel check"] = &DruidTriggerFactoryInternal::hurricane_channel_check; } private: @@ -147,6 +148,7 @@ private: static Trigger* eclipse_lunar_cooldown(PlayerbotAI* ai) { return new EclipseLunarCooldownTrigger(ai); } static Trigger* mangle_cat(PlayerbotAI* ai) { return new MangleCatTrigger(ai); } static Trigger* ferocious_bite_time(PlayerbotAI* ai) { return new FerociousBiteTimeTrigger(ai); } + static Trigger* hurricane_channel_check(PlayerbotAI* ai) { return new HurricaneChannelCheckTrigger(ai); } }; class DruidAiObjectContextInternal : public NamedObjectContext diff --git a/src/strategy/druid/DruidTriggers.cpp b/src/strategy/druid/DruidTriggers.cpp index a083ade1..d38a29c4 100644 --- a/src/strategy/druid/DruidTriggers.cpp +++ b/src/strategy/druid/DruidTriggers.cpp @@ -4,7 +4,7 @@ */ #include "DruidTriggers.h" - +#include "Player.h" #include "Playerbots.h" bool MarkOfTheWildOnPartyTrigger::IsActive() @@ -34,3 +34,30 @@ bool BearFormTrigger::IsActive() { return !botAI->HasAnyAuraOf(bot, "bear form", bool TreeFormTrigger::IsActive() { return !botAI->HasAura(33891, bot); } bool CatFormTrigger::IsActive() { return !botAI->HasAura("cat form", bot); } + +const std::set HurricaneChannelCheckTrigger::HURRICANE_SPELL_IDS = { + 16914, // Hurricane Rank 1 + 17401, // Hurricane Rank 2 + 17402, // Hurricane Rank 3 + 27012, // Hurricane Rank 4 + 48467 // Hurricane Rank 5 +}; + +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)) + { + uint8 attackerCount = AI_VALUE(uint8, "attacker count"); + return attackerCount < minEnemies; + } + } + + // Not channeling Hurricane + return false; +} diff --git a/src/strategy/druid/DruidTriggers.h b/src/strategy/druid/DruidTriggers.h index 1ccb7236..1b9c7a25 100644 --- a/src/strategy/druid/DruidTriggers.h +++ b/src/strategy/druid/DruidTriggers.h @@ -12,6 +12,8 @@ #include "PlayerbotAI.h" #include "Playerbots.h" #include "SharedDefines.h" +#include "Trigger.h" +#include class PlayerbotAI; @@ -263,4 +265,19 @@ public: } }; +class HurricaneChannelCheckTrigger : public Trigger +{ +public: + HurricaneChannelCheckTrigger(PlayerbotAI* botAI, uint32 minEnemies = 2) + : Trigger(botAI, "hurricane channel check"), minEnemies(minEnemies) + { + } + + bool IsActive() override; + +protected: + uint32 minEnemies; + static const std::set HURRICANE_SPELL_IDS; +}; + #endif diff --git a/src/strategy/hunter/GenericHunterStrategy.cpp b/src/strategy/hunter/GenericHunterStrategy.cpp index 7b7f59a3..de2fbf64 100644 --- a/src/strategy/hunter/GenericHunterStrategy.cpp +++ b/src/strategy/hunter/GenericHunterStrategy.cpp @@ -136,9 +136,9 @@ AoEHunterStrategy::AoEHunterStrategy(PlayerbotAI* botAI) : CombatStrategy(botAI) void AoEHunterStrategy::InitTriggers(std::vector& triggers) { + triggers.push_back(new TriggerNode("volley channel check", NextAction::array(0, new NextAction("cancel channel", 23.0f), nullptr))); triggers.push_back(new TriggerNode("medium aoe", NextAction::array(0, new NextAction("volley", 22.0f), nullptr))); - triggers.push_back( - new TriggerNode("light aoe", NextAction::array(0, new NextAction("multi-shot", 21.0f), nullptr))); + triggers.push_back(new TriggerNode("light aoe", NextAction::array(0, new NextAction("multi-shot", 21.0f), nullptr))); } void HunterBoostStrategy::InitTriggers(std::vector& triggers) diff --git a/src/strategy/hunter/HunterAiObjectContext.cpp b/src/strategy/hunter/HunterAiObjectContext.cpp index 904e7a9f..42c5de3e 100644 --- a/src/strategy/hunter/HunterAiObjectContext.cpp +++ b/src/strategy/hunter/HunterAiObjectContext.cpp @@ -101,6 +101,7 @@ public: creators["lock and load"] = &HunterTriggerFactoryInternal::lock_and_load; creators["silencing shot"] = &HunterTriggerFactoryInternal::silencing_shot; creators["intimidation"] = &HunterTriggerFactoryInternal::intimidation; + creators["volley channel check"] = &HunterTriggerFactoryInternal::volley_channel_check; } private: @@ -141,6 +142,7 @@ private: static Trigger* lock_and_load(PlayerbotAI* botAI) { return new LockAndLoadTrigger(botAI); } static Trigger* silencing_shot(PlayerbotAI* botAI) { return new SilencingShotTrigger(botAI); } static Trigger* intimidation(PlayerbotAI* botAI) { return new IntimidationTrigger(botAI); } + static Trigger* volley_channel_check(PlayerbotAI* botAI) { return new VolleyChannelCheckTrigger(botAI); } }; class HunterAiObjectContextInternal : public NamedObjectContext diff --git a/src/strategy/hunter/HunterTriggers.cpp b/src/strategy/hunter/HunterTriggers.cpp index 6a79b7eb..eece2ad1 100644 --- a/src/strategy/hunter/HunterTriggers.cpp +++ b/src/strategy/hunter/HunterTriggers.cpp @@ -12,6 +12,7 @@ #include "Playerbots.h" #include "ServerFacade.h" #include "SharedDefines.h" +#include "Player.h" bool KillCommandTrigger::IsActive() { @@ -139,3 +140,31 @@ bool SerpentStingOnAttackerTrigger::IsActive() !botAI->HasAura("viper sting", target, false, true); return BuffTrigger::IsActive(); } + +const std::set VolleyChannelCheckTrigger::VOLLEY_SPELL_IDS = { + 1510, // Volley Rank 1 + 14294, // Volley Rank 2 + 14295, // Volley Rank 3 + 27022, // Volley Rank 4 + 58431, // Volley Rank 5 + 58434 // Volley Rank 6 +}; + +bool VolleyChannelCheckTrigger::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 Volley + if (VOLLEY_SPELL_IDS.count(spell->m_spellInfo->Id)) + { + uint8 attackerCount = AI_VALUE(uint8, "attacker count"); + return attackerCount < minEnemies; + } + } + + // Not channeling Volley + return false; +} diff --git a/src/strategy/hunter/HunterTriggers.h b/src/strategy/hunter/HunterTriggers.h index c0d18ddc..50a4b776 100644 --- a/src/strategy/hunter/HunterTriggers.h +++ b/src/strategy/hunter/HunterTriggers.h @@ -10,6 +10,7 @@ #include "GenericTriggers.h" #include "Trigger.h" #include "PlayerbotAI.h" +#include class PlayerbotAI; @@ -244,4 +245,19 @@ END_TRIGGER() BEGIN_TRIGGER(HunterPetNotHappy, Trigger) END_TRIGGER() +class VolleyChannelCheckTrigger : public Trigger +{ +public: + VolleyChannelCheckTrigger(PlayerbotAI* botAI, uint32 minEnemies = 2) + : Trigger(botAI, "volley channel check"), minEnemies(minEnemies) + { + } + + bool IsActive() override; + +protected: + uint32 minEnemies; + static const std::set VOLLEY_SPELL_IDS; +}; + #endif diff --git a/src/strategy/mage/MageActions.cpp b/src/strategy/mage/MageActions.cpp index a2fa4045..03e97402 100644 --- a/src/strategy/mage/MageActions.cpp +++ b/src/strategy/mage/MageActions.cpp @@ -142,13 +142,3 @@ bool CastBlinkBackAction::Execute(Event event) bot->SetOrientation(bot->GetAngle(target) + M_PI); return CastSpellAction::Execute(event); } - -bool CancelChannelAction::Execute(Event event) -{ - if (bot->GetCurrentSpell(CURRENT_CHANNELED_SPELL)) - { - bot->InterruptSpell(CURRENT_CHANNELED_SPELL); - return true; - } - return false; -} diff --git a/src/strategy/mage/MageActions.h b/src/strategy/mage/MageActions.h index cf52a9a8..dd22bd77 100644 --- a/src/strategy/mage/MageActions.h +++ b/src/strategy/mage/MageActions.h @@ -404,12 +404,4 @@ public: bool isUseful() override; }; -class CancelChannelAction : public Action -{ -public: - CancelChannelAction(PlayerbotAI* botAI) : Action(botAI, "cancel channel") {} - - bool Execute(Event event) override; -}; - #endif diff --git a/src/strategy/mage/MageAiObjectContext.cpp b/src/strategy/mage/MageAiObjectContext.cpp index 83de0705..c28a8315 100644 --- a/src/strategy/mage/MageAiObjectContext.cpp +++ b/src/strategy/mage/MageAiObjectContext.cpp @@ -237,7 +237,6 @@ public: creators["use mana citrine"] = &MageAiObjectContextInternal::use_mana_citrine; creators["use mana jade"] = &MageAiObjectContextInternal::use_mana_jade; creators["use mana agate"] = &MageAiObjectContextInternal::use_mana_agate; - creators["cancel channel"] = &MageAiObjectContextInternal::cancel_channel; creators["mana shield"] = &MageAiObjectContextInternal::mana_shield; } @@ -299,7 +298,6 @@ private: static Action* use_mana_citrine(PlayerbotAI* botAI) { return new UseManaCitrineAction(botAI); } static Action* use_mana_jade(PlayerbotAI* botAI) { return new UseManaJadeAction(botAI); } static Action* use_mana_agate(PlayerbotAI* botAI) { return new UseManaAgateAction(botAI); } - static Action* cancel_channel(PlayerbotAI* botAI) { return new CancelChannelAction(botAI); } static Action* mana_shield(PlayerbotAI* botAI) { return new CastManaShieldAction(botAI); } }; diff --git a/src/strategy/priest/PriestAiObjectContext.cpp b/src/strategy/priest/PriestAiObjectContext.cpp index 444ed3a1..b433fbf7 100644 --- a/src/strategy/priest/PriestAiObjectContext.cpp +++ b/src/strategy/priest/PriestAiObjectContext.cpp @@ -105,6 +105,7 @@ public: creators["silence"] = &PriestTriggerFactoryInternal::silence; creators["silence on enemy healer"] = &PriestTriggerFactoryInternal::silence_on_enemy_healer; creators["shadowfiend"] = &PriestTriggerFactoryInternal::shadowfiend; + creators["mind sear channel check"] = &PriestTriggerFactoryInternal::mind_sear_channel_check; } private: @@ -148,6 +149,7 @@ private: static Trigger* silence(PlayerbotAI* botAI) { return new SilenceTrigger(botAI); } static Trigger* chastise(PlayerbotAI* botAI) { return new ChastiseTrigger(botAI); } static Trigger* binding_heal(PlayerbotAI* botAI) { return new BindingHealTrigger(botAI); } + static Trigger* mind_sear_channel_check(PlayerbotAI* botAI) { return new MindSearChannelCheckTrigger(botAI); } }; class PriestAiObjectContextInternal : public NamedObjectContext @@ -388,4 +390,4 @@ void PriestAiObjectContext::BuildSharedTriggerContexts(SharedNamedObjectContextL void PriestAiObjectContext::BuildSharedValueContexts(SharedNamedObjectContextList& valueContexts) { AiObjectContext::BuildSharedValueContexts(valueContexts); -} \ No newline at end of file +} diff --git a/src/strategy/priest/PriestTriggers.cpp b/src/strategy/priest/PriestTriggers.cpp index 391ac94e..8a924e9c 100644 --- a/src/strategy/priest/PriestTriggers.cpp +++ b/src/strategy/priest/PriestTriggers.cpp @@ -4,7 +4,8 @@ */ #include "PriestTriggers.h" - +#include "PlayerbotAI.h" +#include "Player.h" #include "Playerbots.h" bool PowerWordFortitudeOnPartyTrigger::IsActive() @@ -76,3 +77,27 @@ bool BindingHealTrigger::IsActive() return PartyMemberLowHealthTrigger::IsActive() && AI_VALUE2(uint8, "health", "self target") < sPlayerbotAIConfig->mediumHealth; } + +const std::set MindSearChannelCheckTrigger::MIND_SEAR_SPELL_IDS = { + 48045, // Mind Sear Rank 1 + 53023 // Mind Sear Rank 2 +}; + +bool MindSearChannelCheckTrigger::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 Mind Sear + if (MIND_SEAR_SPELL_IDS.count(spell->m_spellInfo->Id)) + { + uint8 attackerCount = AI_VALUE(uint8, "attacker count"); + return attackerCount < minEnemies; + } + } + + // Not channeling Mind Sear + return false; +} diff --git a/src/strategy/priest/PriestTriggers.h b/src/strategy/priest/PriestTriggers.h index f4eab6da..747d2c66 100644 --- a/src/strategy/priest/PriestTriggers.h +++ b/src/strategy/priest/PriestTriggers.h @@ -8,6 +8,8 @@ #include "CureTriggers.h" #include "SharedDefines.h" +#include "Trigger.h" +#include class PlayerbotAI; @@ -100,4 +102,19 @@ public: bool IsActive() override; }; +class MindSearChannelCheckTrigger : public Trigger +{ +public: + MindSearChannelCheckTrigger(PlayerbotAI* botAI, uint32 minEnemies = 2) + : Trigger(botAI, "mind sear channel check"), minEnemies(minEnemies) + { + } + + bool IsActive() override; + +protected: + uint32 minEnemies; + static const std::set MIND_SEAR_SPELL_IDS; +}; + #endif diff --git a/src/strategy/priest/ShadowPriestStrategy.cpp b/src/strategy/priest/ShadowPriestStrategy.cpp index 30fd5975..5532dd27 100644 --- a/src/strategy/priest/ShadowPriestStrategy.cpp +++ b/src/strategy/priest/ShadowPriestStrategy.cpp @@ -54,6 +54,8 @@ void ShadowPriestAoeStrategy::InitTriggers(std::vector& triggers) triggers.push_back(new TriggerNode( "vampiric touch on attacker", NextAction::array(0, new NextAction("vampiric touch on attacker", ACTION_NORMAL + 4), nullptr))); + triggers.push_back( + new TriggerNode("mind sear channel check", NextAction::array(0, new NextAction("cancel channel", ACTION_HIGH + 5), nullptr))); triggers.push_back( new TriggerNode("medium aoe", NextAction::array(0, new NextAction("mind sear", ACTION_HIGH + 4), nullptr))); } diff --git a/src/strategy/warlock/GenericWarlockStrategy.cpp b/src/strategy/warlock/GenericWarlockStrategy.cpp index df8265c7..9ffc7a1b 100644 --- a/src/strategy/warlock/GenericWarlockStrategy.cpp +++ b/src/strategy/warlock/GenericWarlockStrategy.cpp @@ -58,6 +58,9 @@ void AoEWarlockStrategy::InitTriggers(std::vector& triggers) new NextAction("seed of corruption on attacker", 22.0f), new NextAction("seed of corruption", 21.5f), new NextAction("rain of fire", 21.0f), nullptr))); + + triggers.push_back( + new TriggerNode("rain of fire channel check", NextAction::array(0, new NextAction("cancel channel", 21.5f), nullptr))); } void WarlockBoostStrategy::InitTriggers(std::vector& triggers) diff --git a/src/strategy/warlock/WarlockAiObjectContext.cpp b/src/strategy/warlock/WarlockAiObjectContext.cpp index 8a87a823..f4bc5ae0 100644 --- a/src/strategy/warlock/WarlockAiObjectContext.cpp +++ b/src/strategy/warlock/WarlockAiObjectContext.cpp @@ -172,6 +172,7 @@ public: creators["curse of tongues"] = &WarlockTriggerFactoryInternal::curse_of_tongues; creators["curse of weakness"] = &WarlockTriggerFactoryInternal::curse_of_weakness; creators["wrong pet"] = &WarlockTriggerFactoryInternal::wrong_pet; + creators["rain of fire channel check"] = &WarlockTriggerFactoryInternal::rain_of_fire_channel_check; } private: @@ -217,6 +218,7 @@ private: static Trigger* curse_of_tongues(PlayerbotAI* ai) { return new CurseOfTonguesTrigger(ai); } static Trigger* curse_of_weakness(PlayerbotAI* ai) { return new CurseOfWeaknessTrigger(ai); } static Trigger* wrong_pet(PlayerbotAI* ai) { return new WrongPetTrigger(ai); } + static Trigger* rain_of_fire_channel_check(PlayerbotAI* ai) { return new RainOfFireChannelCheckTrigger(ai); } }; class WarlockAiObjectContextInternal : public NamedObjectContext diff --git a/src/strategy/warlock/WarlockTriggers.cpp b/src/strategy/warlock/WarlockTriggers.cpp index e8198c1a..62d9c311 100644 --- a/src/strategy/warlock/WarlockTriggers.cpp +++ b/src/strategy/warlock/WarlockTriggers.cpp @@ -6,6 +6,8 @@ #include "WarlockTriggers.h" #include "GenericTriggers.h" #include "Playerbots.h" +#include "PlayerbotAI.h" +#include "Player.h" static const uint32 SOUL_SHARD_ITEM_ID = 6265; @@ -228,3 +230,32 @@ bool WrongPetTrigger::IsActive() // Step 6: If we get here, the bot doesn't know the spell required to support the active pet strategy return false; } + +const std::set RainOfFireChannelCheckTrigger::RAIN_OF_FIRE_SPELL_IDS = { + 5740, // Rain of Fire Rank 1 + 6219, // Rain of Fire Rank 2 + 11677, // Rain of Fire Rank 3 + 11678, // Rain of Fire Rank 4 + 27212, // Rain of Fire Rank 5 + 47819, // Rain of Fire Rank 6 + 47820 // Rain of Fire Rank 7 +}; + +bool RainOfFireChannelCheckTrigger::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 Rain of Fire + if (RAIN_OF_FIRE_SPELL_IDS.count(spell->m_spellInfo->Id)) + { + uint8 attackerCount = AI_VALUE(uint8, "attacker count"); + return attackerCount < minEnemies; + } + } + + // Not channeling Rain of Fire + return false; +} diff --git a/src/strategy/warlock/WarlockTriggers.h b/src/strategy/warlock/WarlockTriggers.h index e886635a..e8ef51ed 100644 --- a/src/strategy/warlock/WarlockTriggers.h +++ b/src/strategy/warlock/WarlockTriggers.h @@ -10,6 +10,8 @@ #include "PlayerbotAI.h" #include "Playerbots.h" #include "CureTriggers.h" +#include "Trigger.h" +#include class PlayerbotAI; @@ -332,4 +334,19 @@ public: : TwoTriggers(ai, "enemy too close for spell", "metamorphosis not active") {} }; +class RainOfFireChannelCheckTrigger : public Trigger +{ +public: + RainOfFireChannelCheckTrigger(PlayerbotAI* botAI, uint32 minEnemies = 2) + : Trigger(botAI, "rain of fire channel check"), minEnemies(minEnemies) + { + } + + bool IsActive() override; + +protected: + uint32 minEnemies; + static const std::set RAIN_OF_FIRE_SPELL_IDS; +}; + #endif