diff --git a/conf/playerbots.conf.dist b/conf/playerbots.conf.dist index 6c4b7411..42d762cc 100644 --- a/conf/playerbots.conf.dist +++ b/conf/playerbots.conf.dist @@ -1341,8 +1341,8 @@ AiPlayerbot.PremadeSpecLink.9.1.70 = -003203301135112530135201051-55 AiPlayerbot.PremadeSpecLink.9.1.80 = -003203301135112530135221351-55000005 AiPlayerbot.PremadeSpecName.9.2 = destro pve AiPlayerbot.PremadeSpecGlyph.9.2 = 45785,43390,50077,43394,43393,42454 -AiPlayerbot.PremadeSpecLink.9.2.60 = --05203205210131051313230341 -AiPlayerbot.PremadeSpecLink.9.2.80 = -03310030003-05203205210331051335230351 +AiPlayerbot.PremadeSpecLink.9.2.60 = --05203215200231051305031151 +AiPlayerbot.PremadeSpecLink.9.2.80 = 23-0302-05203215220331051335231351 # # @@ -1590,11 +1590,11 @@ AiPlayerbot.RandomClassSpecIndex.8.2 = 2 # # -AiPlayerbot.RandomClassSpecProb.9.0 = 45 +AiPlayerbot.RandomClassSpecProb.9.0 = 33 AiPlayerbot.RandomClassSpecIndex.9.0 = 0 -AiPlayerbot.RandomClassSpecProb.9.1 = 45 +AiPlayerbot.RandomClassSpecProb.9.1 = 34 AiPlayerbot.RandomClassSpecIndex.9.1 = 1 -AiPlayerbot.RandomClassSpecProb.9.2 = 10 +AiPlayerbot.RandomClassSpecProb.9.2 = 33 AiPlayerbot.RandomClassSpecIndex.9.2 = 2 # diff --git a/src/AiFactory.cpp b/src/AiFactory.cpp index 71ef3c1c..adf53668 100644 --- a/src/AiFactory.cpp +++ b/src/AiFactory.cpp @@ -385,8 +385,16 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa } break; case CLASS_WARLOCK: - engine->addStrategiesNoInit("dps assist", "dps", "dps debuff", "aoe", nullptr); + if (tab == 0) // Affliction + engine->addStrategiesNoInit("affli", "affli aoe", nullptr); + else if (tab == 1) // Demonology + engine->addStrategiesNoInit("demo", "demo aoe", "meta melee", nullptr); + else if (tab == 2) // Destruction + engine->addStrategiesNoInit("destro", "destro aoe", "curse of elements", nullptr); + + engine->addStrategiesNoInit("cc", "dps assist", nullptr); break; + case CLASS_DEATH_KNIGHT: if (tab == 0) engine->addStrategiesNoInit("blood", "tank assist", nullptr); @@ -588,15 +596,15 @@ void AiFactory::AddDefaultNonCombatStrategies(Player* player, PlayerbotAI* const case CLASS_WARLOCK: if (tab == WARLOCK_TAB_AFFLICATION) { - nonCombatEngine->addStrategiesNoInit("bmana", nullptr); + nonCombatEngine->addStrategiesNoInit("felhunter", nullptr); } else if (tab == WARLOCK_TAB_DEMONOLOGY) { - nonCombatEngine->addStrategiesNoInit("bdps", nullptr); + nonCombatEngine->addStrategiesNoInit("felguard", nullptr); } else if (tab == WARLOCK_TAB_DESTRUCTION) { - nonCombatEngine->addStrategiesNoInit("bhealth", nullptr); + nonCombatEngine->addStrategiesNoInit("imp", nullptr); } nonCombatEngine->addStrategiesNoInit("dps assist", nullptr); break; diff --git a/src/PlayerbotAI.cpp b/src/PlayerbotAI.cpp index fd7872ee..8c22533c 100644 --- a/src/PlayerbotAI.cpp +++ b/src/PlayerbotAI.cpp @@ -3154,22 +3154,41 @@ bool PlayerbotAI::CastSpell(uint32 spellId, Unit* target, Item* itemTarget) SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId); if (pet && pet->HasSpell(spellId)) { - bool autocast = false; - for (unsigned int& m_autospell : pet->m_autospells) + // List of spell IDs for which we do NOT want to toggle auto-cast or send message + // We are excluding Spell Lock and Devour Magic because they are casted in the GenericWarlockStrategy + // Without this exclusion, the skill would be togged for auto-cast and the player would + // be spammed with messages about enabling/disabling auto-cast + switch (spellId) { - if (m_autospell == spellId) - { - autocast = true; + case 19244: // Spell Lock rank 1 + case 19647: // Spell Lock rank 2 + case 19505: // Devour Magic rank 1 + case 19731: // Devour Magic rank 2 + case 19734: // Devour Magic rank 3 + case 19736: // Devour Magic rank 4 + case 27276: // Devour Magic rank 5 + case 27277: // Devour Magic rank 6 + case 48011: // Devour Magic rank 7 + // No message - just break out of the switch and let normal cast logic continue break; - } - } + default: + bool autocast = false; + for (unsigned int& m_autospell : pet->m_autospells) + { + if (m_autospell == spellId) + { + autocast = true; + break; + } + } - pet->ToggleAutocast(spellInfo, !autocast); - std::ostringstream out; - out << (autocast ? "|cffff0000|Disabling" : "|cFF00ff00|Enabling") << " pet auto-cast for "; - out << chatHelper.FormatSpell(spellInfo); - TellMaster(out); - return true; + pet->ToggleAutocast(spellInfo, !autocast); + std::ostringstream out; + out << (autocast ? "|cffff0000|Disabling" : "|cFF00ff00|Enabling") << " pet auto-cast for "; + out << chatHelper.FormatSpell(spellInfo); + TellMaster(out); + return true; + } } // aiObjectContext->GetValue("last movement")->Get().Set(nullptr); @@ -6302,4 +6321,4 @@ void PlayerbotAI::AddTimedEvent(std::function callback, uint32 delayMs) bot->m_Events.AddEvent( new LambdaEvent(std::move(callback)), bot->m_Events.CalculateTime(delayMs)); -} \ No newline at end of file +} diff --git a/src/strategy/actions/GenericActions.cpp b/src/strategy/actions/GenericActions.cpp index f0ce6fb0..a7c2a9cb 100644 --- a/src/strategy/actions/GenericActions.cpp +++ b/src/strategy/actions/GenericActions.cpp @@ -14,10 +14,25 @@ enum PetSpells PET_PROWL_2 = 24452, PET_PROWL_3 = 24453, PET_COWER = 1742, - PET_LEAP = 47482 + PET_LEAP = 47482, + PET_SPELL_LOCK_1 = 19244, + PET_SPELL_LOCK_2 = 19647, + PET_DEVOUR_MAGIC_1 = 19505, + PET_DEVOUR_MAGIC_2 = 19731, + PET_DEVOUR_MAGIC_3 = 19734, + PET_DEVOUR_MAGIC_4 = 19736, + PET_DEVOUR_MAGIC_5 = 27276, + PET_DEVOUR_MAGIC_6 = 27277, + PET_DEVOUR_MAGIC_7 = 48011 }; -static std::vector disabledPetSpells = {PET_PROWL_1, PET_PROWL_2, PET_PROWL_3, PET_COWER, PET_LEAP}; +static std::vector disabledPetSpells = { + PET_PROWL_1, PET_PROWL_2, PET_PROWL_3, + PET_COWER, PET_LEAP, + PET_SPELL_LOCK_1, PET_SPELL_LOCK_2, + PET_DEVOUR_MAGIC_1, PET_DEVOUR_MAGIC_2, PET_DEVOUR_MAGIC_3, + PET_DEVOUR_MAGIC_4, PET_DEVOUR_MAGIC_5, PET_DEVOUR_MAGIC_6, PET_DEVOUR_MAGIC_7 +}; bool MeleeAction::isUseful() { diff --git a/src/strategy/actions/ReleaseSpiritAction.cpp b/src/strategy/actions/ReleaseSpiritAction.cpp index bc44e16f..cff34f8d 100644 --- a/src/strategy/actions/ReleaseSpiritAction.cpp +++ b/src/strategy/actions/ReleaseSpiritAction.cpp @@ -4,7 +4,7 @@ */ #include "ReleaseSpiritAction.h" - +#include "ServerFacade.h" #include "Event.h" #include "GameGraveyard.h" #include "NearestNpcsValue.h" @@ -13,6 +13,7 @@ #include "Playerbots.h" #include "ServerFacade.h" #include "Corpse.h" +#include "Log.h" // ReleaseSpiritAction implementation bool ReleaseSpiritAction::Execute(Event event) @@ -247,3 +248,19 @@ void RepopAction::PerformGraveyardTeleport(const GraveyardStruct* graveyard) con RESET_AI_VALUE(bool, "combat::self target"); RESET_AI_VALUE(WorldPosition, "current position"); } + +// SelfResurrectAction implementation for Warlock's Soulstone Resurrection/Shaman's Reincarnation +bool SelfResurrectAction::Execute(Event event) +{ + if (!bot->IsAlive() && bot->GetUInt32Value(PLAYER_SELF_RES_SPELL)) + { + WorldPacket packet(CMSG_SELF_RES); + bot->GetSession()->HandleSelfResOpcode(packet); + return true; + } + return false; +} +bool SelfResurrectAction::isUseful() +{ + return !bot->IsAlive() && bot->GetUInt32Value(PLAYER_SELF_RES_SPELL); +} diff --git a/src/strategy/actions/ReleaseSpiritAction.h b/src/strategy/actions/ReleaseSpiritAction.h index ccb14b39..7771f9ff 100644 --- a/src/strategy/actions/ReleaseSpiritAction.h +++ b/src/strategy/actions/ReleaseSpiritAction.h @@ -56,4 +56,13 @@ private: void PerformGraveyardTeleport(const GraveyardStruct* graveyard) const; }; +// SelfResurrectAction action registration +class SelfResurrectAction : public Action +{ +public: + SelfResurrectAction(PlayerbotAI* ai) : Action(ai, "self resurrect") {} + virtual bool Execute(Event event) override; + bool isUseful() override; +}; + #endif diff --git a/src/strategy/actions/UseItemAction.cpp b/src/strategy/actions/UseItemAction.cpp index 3747ef67..2654c1fa 100644 --- a/src/strategy/actions/UseItemAction.cpp +++ b/src/strategy/actions/UseItemAction.cpp @@ -238,9 +238,24 @@ bool UseItemAction::UseItem(Item* item, ObjectGuid goGuid, Item* itemTarget, Uni { targetFlag = TARGET_FLAG_NONE; packet << targetFlag; - packet << bot->GetPackGUID(); - targetSelected = true; - out << " on self"; + + // Use the actual target if provided + if (unitTarget) + { + packet << unitTarget->GetGUID(); + targetSelected = true; + // If the target is bot or is an enemy, say "on self" + if (unitTarget == bot || (unitTarget->IsHostileTo(bot))) + out << " on self"; + else + out << " on " << unitTarget->GetName(); + } + else + { + packet << bot->GetPackGUID(); + targetSelected = true; + out << " on self"; + } } ItemTemplate const* proto = item->GetTemplate(); diff --git a/src/strategy/actions/WorldPacketActionContext.h b/src/strategy/actions/WorldPacketActionContext.h index 2c32d118..88540cfd 100644 --- a/src/strategy/actions/WorldPacketActionContext.h +++ b/src/strategy/actions/WorldPacketActionContext.h @@ -40,6 +40,7 @@ #include "TradeStatusExtendedAction.h" #include "UseMeetingStoneAction.h" #include "NamedObjectContext.h" +#include "ReleaseSpiritAction.h" class PlayerbotAI; @@ -68,6 +69,7 @@ public: creators["accept trade"] = &WorldPacketActionContext::accept_trade; creators["trade status extended"] = &WorldPacketActionContext::trade_status_extended; creators["store loot"] = &WorldPacketActionContext::store_loot; + creators["self resurrect"] = &WorldPacketActionContext::self_resurrect; // quest creators["talk to quest giver"] = &WorldPacketActionContext::turn_in_quest; @@ -136,6 +138,7 @@ private: static Action* tell_not_enough_money(PlayerbotAI* botAI) { return new TellMasterAction(botAI, "Not enough money"); } static Action* tell_not_enough_reputation(PlayerbotAI* botAI) { return new TellMasterAction(botAI, "Not enough reputation"); } static Action* tell_cannot_equip(PlayerbotAI* botAI) { return new InventoryChangeFailureAction(botAI); } + static Action* self_resurrect(PlayerbotAI* botAI) { return new SelfResurrectAction(botAI); } // quest static Action* quest_update_add_kill(PlayerbotAI* ai) { return new QuestUpdateAddKillAction(ai); } diff --git a/src/strategy/generic/DeadStrategy.cpp b/src/strategy/generic/DeadStrategy.cpp index c3bc59c7..31de5afd 100644 --- a/src/strategy/generic/DeadStrategy.cpp +++ b/src/strategy/generic/DeadStrategy.cpp @@ -25,6 +25,8 @@ void DeadStrategy::InitTriggers(std::vector& triggers) new TriggerNode("falling far", NextAction::array(0, new NextAction("repop", relevance + 1.f), nullptr))); triggers.push_back( new TriggerNode("location stuck", NextAction::array(0, new NextAction("repop", relevance + 1), nullptr))); + triggers.push_back(new TriggerNode( + "can self resurrect", NextAction::array(0, new NextAction("self resurrect", relevance + 2.0f), nullptr))); } DeadStrategy::DeadStrategy(PlayerbotAI* botAI) : PassTroughStrategy(botAI) {} diff --git a/src/strategy/triggers/GenericTriggers.h b/src/strategy/triggers/GenericTriggers.h index cee6b18a..cabbbfd2 100644 --- a/src/strategy/triggers/GenericTriggers.h +++ b/src/strategy/triggers/GenericTriggers.h @@ -11,6 +11,7 @@ #include "HealthTriggers.h" #include "RangeTriggers.h" #include "Trigger.h" +#include "Player.h" class PlayerbotAI; class Unit; @@ -922,4 +923,13 @@ public: public: virtual Value* GetTargetValue(); }; + +class SelfResurrectTrigger : public Trigger +{ +public: + SelfResurrectTrigger(PlayerbotAI* ai) : Trigger(ai, "can self resurrect") {} + + bool IsActive() override { return !bot->IsAlive() && bot->GetUInt32Value(PLAYER_SELF_RES_SPELL); } +}; + #endif diff --git a/src/strategy/triggers/TriggerContext.h b/src/strategy/triggers/TriggerContext.h index 7fe85291..39951285 100644 --- a/src/strategy/triggers/TriggerContext.h +++ b/src/strategy/triggers/TriggerContext.h @@ -223,6 +223,7 @@ public: creators["near random status"] = &TriggerContext::near_random_status; creators["near npc status"] = &TriggerContext::near_npc_status; creators["do quest status"] = &TriggerContext::do_quest_status; + creators["can self resurrect"] = &TriggerContext::can_self_resurrect; } private: @@ -420,6 +421,7 @@ private: static Trigger* near_random_status(PlayerbotAI* botAI) { return new NewRpgStatusTrigger(botAI, RPG_NEAR_RANDOM); } static Trigger* near_npc_status(PlayerbotAI* botAI) { return new NewRpgStatusTrigger(botAI, RPG_NEAR_NPC); } static Trigger* do_quest_status(PlayerbotAI* botAI) { return new NewRpgStatusTrigger(botAI, RPG_DO_QUEST); } + static Trigger* can_self_resurrect(PlayerbotAI* ai) { return new SelfResurrectTrigger(ai); } }; #endif diff --git a/src/strategy/warlock/AfflictionWarlockStrategy.cpp b/src/strategy/warlock/AfflictionWarlockStrategy.cpp new file mode 100644 index 00000000..eaeef3be --- /dev/null +++ b/src/strategy/warlock/AfflictionWarlockStrategy.cpp @@ -0,0 +1,99 @@ +/* + * 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 "AfflictionWarlockStrategy.h" +#include "Playerbots.h" + +// ===== Action Node Factory ===== +class AfflictionWarlockStrategyActionNodeFactory : public NamedObjectFactory +{ +public: + AfflictionWarlockStrategyActionNodeFactory() + { + creators["corruption"] = &corruption; + creators["corruption on attacker"] = &corruption; + creators["unstable affliction"] = &unstable_affliction; + creators["unstable affliction on attacker"] = &unstable_affliction; + creators["curse of agony"] = &curse_of_agony; + creators["curse of agony on attacker"] = &curse_of_agony; + creators["haunt"] = &haunt; + creators["shadow bolt"] = &shadow_bolt; + creators["drain soul"] = &drain_soul; + creators["life tap"] = &life_tap; + creators["shadowflame"] = &shadowflame; + creators["seed of corruption on attacker"] = &seed_of_corruption; + creators["seed of corruption"] = &seed_of_corruption; + creators["rain of fire"] = &rain_of_fire; + } + +private: + static ActionNode* corruption(PlayerbotAI*) { return new ActionNode("corruption", nullptr, nullptr, nullptr); } + static ActionNode* corruption_on_attacker(PlayerbotAI*) { return new ActionNode("corruption on attacker", nullptr, nullptr, nullptr); } + static ActionNode* unstable_affliction(PlayerbotAI*) { return new ActionNode("unstable affliction", nullptr, nullptr, nullptr); } + static ActionNode* unstable_affliction_on_attacker(PlayerbotAI*) { return new ActionNode("unstable affliction on attacker", nullptr, nullptr, nullptr); } + static ActionNode* curse_of_agony(PlayerbotAI*) { return new ActionNode("curse of agony", nullptr, nullptr, nullptr); } + static ActionNode* curse_of_agony_on_attacker(PlayerbotAI*) { return new ActionNode("curse of agony on attacker", nullptr, nullptr, nullptr); } + static ActionNode* haunt(PlayerbotAI*) { return new ActionNode("haunt", nullptr, nullptr, nullptr); } + static ActionNode* shadow_bolt(PlayerbotAI*) { return new ActionNode("shadow bolt", nullptr, nullptr, nullptr); } + static ActionNode* drain_soul(PlayerbotAI*) { return new ActionNode("drain soul", nullptr, nullptr, nullptr); } + static ActionNode* life_tap(PlayerbotAI*) { return new ActionNode("life tap", nullptr, nullptr, nullptr); } + static ActionNode* shadowflame(PlayerbotAI*) { return new ActionNode("shadowflame", nullptr, nullptr, nullptr); } + static ActionNode* seed_of_corruption_on_attacker(PlayerbotAI*) { return new ActionNode("seed of corruption on attacker", nullptr, nullptr, nullptr); } + static ActionNode* seed_of_corruption(PlayerbotAI*) { return new ActionNode("seed of corruption", nullptr, nullptr, nullptr); } + static ActionNode* rain_of_fire(PlayerbotAI*) { return new ActionNode("rain of fire", nullptr, nullptr, nullptr); } +}; + +// ===== Single Target Strategy ===== +AfflictionWarlockStrategy::AfflictionWarlockStrategy(PlayerbotAI* botAI) : GenericWarlockStrategy(botAI) +{ + actionNodeFactories.Add(new AfflictionWarlockStrategyActionNodeFactory()); +} + +// ===== Default Actions ===== +NextAction** AfflictionWarlockStrategy::getDefaultActions() +{ + return NextAction::array( 0, + new NextAction("corruption", 5.6f), + new NextAction("unstable affliction", 5.5f), + new NextAction("curse of agony", 5.4f), + new NextAction("haunt", 5.3f), + new NextAction("shadow bolt", 5.2f), + new NextAction("shoot", 5.0f), nullptr); +} + +// ===== Trigger Initialization === +void AfflictionWarlockStrategy::InitTriggers(std::vector& triggers) +{ + GenericWarlockStrategy::InitTriggers(triggers); + + // Main DoT triggers for high uptime + triggers.push_back(new TriggerNode("corruption on attacker", NextAction::array(0, new NextAction("corruption on attacker", 19.5f), nullptr))); + triggers.push_back(new TriggerNode("unstable affliction on attacker", NextAction::array(0, new NextAction("unstable affliction on attacker", 19.0f), nullptr))); + triggers.push_back(new TriggerNode("curse of agony on attacker", NextAction::array(0, new NextAction("curse of agony on attacker", 18.5f), nullptr))); + triggers.push_back(new TriggerNode("corruption", NextAction::array(0, new NextAction("corruption", 18.0f), nullptr))); + triggers.push_back(new TriggerNode("unstable affliction", NextAction::array(0, new NextAction("unstable affliction", 17.5f), nullptr))); + triggers.push_back(new TriggerNode("curse of agony", NextAction::array(0, new NextAction("curse of agony", 17.0f), nullptr))); + triggers.push_back(new TriggerNode("haunt", NextAction::array(0, new NextAction("haunt", 16.5f), nullptr))); + + // Drain Soul as execute if target is low HP // Shadow Trance for free casts + triggers.push_back(new TriggerNode("shadow trance", NextAction::array(0, new NextAction("shadow bolt", 16.0f), nullptr))); + triggers.push_back(new TriggerNode("target critical health", NextAction::array(0, new NextAction("drain soul", 15.5f), nullptr))); + + // Life Tap glyph buff, and Life Tap as filler + triggers.push_back(new TriggerNode("life tap glyph buff", NextAction::array(0, new NextAction("life tap", 29.0f), nullptr))); + triggers.push_back(new TriggerNode("life tap", NextAction::array(0, new NextAction("life tap", 5.1f), nullptr))); +} + +// ===== AoE Strategy, 3+ enemies ===== +AfflictionWarlockAoeStrategy::AfflictionWarlockAoeStrategy(PlayerbotAI* botAI) : CombatStrategy(botAI) {} + +void AfflictionWarlockAoeStrategy::InitTriggers(std::vector& triggers) +{ + triggers.push_back(new TriggerNode("medium aoe", NextAction::array(0, + new NextAction("shadowflame", 22.5f), + new NextAction("seed of corruption on attacker", 22.0f), + new NextAction("seed of corruption", 21.5f), + new NextAction("rain of fire", 21.0f), nullptr))); +} diff --git a/src/strategy/warlock/AfflictionWarlockStrategy.h b/src/strategy/warlock/AfflictionWarlockStrategy.h new file mode 100644 index 00000000..64910596 --- /dev/null +++ b/src/strategy/warlock/AfflictionWarlockStrategy.h @@ -0,0 +1,32 @@ +/* + * 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_AFFLICTIONWARLOCKSTRATEGY_H +#define _PLAYERBOT_AFFLICTIONWARLOCKSTRATEGY_H + +#include "GenericWarlockStrategy.h" +#include "CombatStrategy.h" + +class PlayerbotAI; + +class AfflictionWarlockStrategy : public GenericWarlockStrategy +{ +public: + AfflictionWarlockStrategy(PlayerbotAI* botAI); + + void InitTriggers(std::vector& triggers) override; + std::string const getName() override { return "affli"; } + NextAction** getDefaultActions() override; +}; + +class AfflictionWarlockAoeStrategy : public CombatStrategy +{ +public: + AfflictionWarlockAoeStrategy(PlayerbotAI* botAI); + + void InitTriggers(std::vector& triggers) override; + std::string const getName() override { return "affli aoe"; } +}; +#endif diff --git a/src/strategy/warlock/DemonologyWarlockStrategy.cpp b/src/strategy/warlock/DemonologyWarlockStrategy.cpp new file mode 100644 index 00000000..2cfdfac7 --- /dev/null +++ b/src/strategy/warlock/DemonologyWarlockStrategy.cpp @@ -0,0 +1,127 @@ +/* + * 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 "DemonologyWarlockStrategy.h" +#include "Playerbots.h" + +// ===== Action Node Factory ===== +class DemonologyWarlockStrategyActionNodeFactory : public NamedObjectFactory +{ +public: + DemonologyWarlockStrategyActionNodeFactory() + { + creators["metamorphosis"] = &metamorphosis; + creators["demonic empowerment"] = &demonic_empowerment; + creators["curse of agony"] = &curse_of_agony; + creators["curse of agony on attacker"] = &curse_of_agony_on_attacker; + creators["corruption"] = &corruption; + creators["corruption on attacker"] = &corruption_on_attacker; + creators["immolate"] = &immolate; + creators["immolate on attacker"] = &immolate_on_attacker; + creators["incinerate"] = &incinerate; + creators["soul fire"] = &soul_fire; + creators["shadow bolt"] = &shadow_bolt; + creators["life tap"] = &life_tap; + creators["immolation aura"] = &immolation_aura; + creators["shadowflame"] = &shadowflame; + creators["seed of corruption on attacker"] = &seed_of_corruption_on_attacker; + creators["seed of corruption"] = &seed_of_corruption; + creators["rain of fire"] = &rain_of_fire; + creators["demon charge"] = &demon_charge; + creators["shadow cleave"] = &shadow_cleave; + } + +private: + static ActionNode* metamorphosis(PlayerbotAI*) { return new ActionNode("metamorphosis", nullptr, nullptr, nullptr); } + static ActionNode* demonic_empowerment(PlayerbotAI*) { return new ActionNode("demonic empowerment", nullptr, nullptr, nullptr); } + static ActionNode* curse_of_agony(PlayerbotAI*) { return new ActionNode("curse of agony", nullptr, nullptr, nullptr); } + static ActionNode* curse_of_agony_on_attacker(PlayerbotAI*) { return new ActionNode("curse of agony on attacker", nullptr, nullptr, nullptr); } + static ActionNode* corruption(PlayerbotAI*) { return new ActionNode("corruption", nullptr, nullptr, nullptr); } + static ActionNode* corruption_on_attacker(PlayerbotAI*) { return new ActionNode("corruption on attacker", nullptr, nullptr, nullptr); } + static ActionNode* immolate(PlayerbotAI*) { return new ActionNode("immolate", nullptr, nullptr, nullptr); } + static ActionNode* immolate_on_attacker(PlayerbotAI*) { return new ActionNode("immolate on attacker", nullptr, nullptr, nullptr); } + static ActionNode* incinerate(PlayerbotAI*) { return new ActionNode("incinerate", nullptr, nullptr, nullptr); } + static ActionNode* soul_fire(PlayerbotAI*) { return new ActionNode("soul fire", nullptr, nullptr, nullptr); } + static ActionNode* shadow_bolt(PlayerbotAI*) { return new ActionNode("shadow bolt", nullptr, nullptr, nullptr); } + static ActionNode* life_tap(PlayerbotAI*) { return new ActionNode("life tap", nullptr, nullptr, nullptr); } + static ActionNode* immolation_aura(PlayerbotAI*) { return new ActionNode("immolation aura", nullptr, nullptr, nullptr); } + static ActionNode* shadowflame(PlayerbotAI*) { return new ActionNode("shadowflame", nullptr, nullptr, nullptr); } + static ActionNode* seed_of_corruption_on_attacker(PlayerbotAI*) { return new ActionNode("seed of corruption on attacker", nullptr, nullptr, nullptr); } + static ActionNode* seed_of_corruption(PlayerbotAI*) { return new ActionNode("seed of corruption", nullptr, nullptr, nullptr); } + static ActionNode* rain_of_fire(PlayerbotAI*) { return new ActionNode("rain of fire", nullptr, nullptr, nullptr); } + static ActionNode* demon_charge(PlayerbotAI*) { return new ActionNode("demon charge", nullptr, nullptr, nullptr); } + static ActionNode* shadow_cleave(PlayerbotAI*) { return new ActionNode("shadow cleave", nullptr, nullptr, nullptr); } +}; + +// ===== Single Target Strategy ===== +DemonologyWarlockStrategy::DemonologyWarlockStrategy(PlayerbotAI* botAI) : GenericWarlockStrategy(botAI) +{ + actionNodeFactories.Add(new DemonologyWarlockStrategyActionNodeFactory()); +} + +// ===== Default Actions ===== +NextAction** DemonologyWarlockStrategy::getDefaultActions() +{ + return NextAction::array(0, + new NextAction("corruption", 5.6f), + new NextAction("curse of agony", 5.5f), + new NextAction("immolate", 5.4f), + new NextAction("shadow bolt", 5.3f), + new NextAction("incinerate", 5.2f), + new NextAction("shoot", 5.0f), nullptr); +} + +// ===== Trigger Initialization === +void DemonologyWarlockStrategy::InitTriggers(std::vector& triggers) +{ + GenericWarlockStrategy::InitTriggers(triggers); + + // High priority cooldowns + triggers.push_back(new TriggerNode("metamorphosis", NextAction::array(0, new NextAction("metamorphosis", 28.5f), nullptr))); + triggers.push_back(new TriggerNode("demonic empowerment", NextAction::array(0, new NextAction("demonic empowerment", 28.0f), nullptr))); + + // Main DoT triggers for high uptime + triggers.push_back(new TriggerNode("corruption on attacker", NextAction::array(0, new NextAction("corruption on attacker", 20.0f), nullptr))); + triggers.push_back(new TriggerNode("curse of agony on attacker", NextAction::array(0, new NextAction("curse of agony on attacker", 19.5f), nullptr))); + triggers.push_back(new TriggerNode("immolate on attacker", NextAction::array(0, new NextAction("immolate on attacker", 19.0f), nullptr))); + triggers.push_back(new TriggerNode("corruption", NextAction::array(0, new NextAction("corruption", 18.5f), nullptr))); + triggers.push_back(new TriggerNode("curse of agony", NextAction::array(0, new NextAction("curse of agony", 18.0f), nullptr))); + triggers.push_back(new TriggerNode("immolate", NextAction::array(0, new NextAction("immolate", 17.5f), nullptr))); + + // Procs + triggers.push_back(new TriggerNode("decimation", NextAction::array(0, new NextAction("soul fire", 17.0f), nullptr))); + triggers.push_back(new TriggerNode("molten core", NextAction::array(0, new NextAction("incinerate", 16.5f), nullptr))); + + // Life Tap glyph buff, and Life Tap as filler + triggers.push_back(new TriggerNode("life tap glyph buff", NextAction::array(0, new NextAction("life tap", 29.0f), nullptr))); + triggers.push_back(new TriggerNode("life tap", NextAction::array(0, new NextAction("life tap", 5.1f), nullptr))); +} + +// ===== AoE Strategy, 3+ enemies ===== +DemonologyWarlockAoeStrategy::DemonologyWarlockAoeStrategy(PlayerbotAI* botAI) : CombatStrategy(botAI) {} + +void DemonologyWarlockAoeStrategy::InitTriggers(std::vector& triggers) +{ + triggers.push_back(new TriggerNode("medium aoe", NextAction::array(0, + new NextAction("immolation aura", 26.0f), + new NextAction("shadowflame", 22.5f), + new NextAction("seed of corruption on attacker", 22.0f), + new NextAction("seed of corruption", 21.5f), + new NextAction("rain of fire", 21.0f), nullptr))); +} + +// Combat strategy to run to melee for Immolation Aura +// Enabled by default for the Demonology spec +// To enable, type "co +meta melee" +// To disable, type "co -meta melee" +MetaMeleeAoeStrategy::MetaMeleeAoeStrategy(PlayerbotAI* botAI) : CombatStrategy(botAI) {} + +void MetaMeleeAoeStrategy::InitTriggers(std::vector& triggers) +{ + triggers.push_back(new TriggerNode("immolation aura active", NextAction::array(0, + new NextAction("reach melee", 25.5f), + new NextAction("demon charge", 25.0f), + new NextAction("shadow cleave", 24.5f), nullptr))); +} diff --git a/src/strategy/warlock/DemonologyWarlockStrategy.h b/src/strategy/warlock/DemonologyWarlockStrategy.h new file mode 100644 index 00000000..59519271 --- /dev/null +++ b/src/strategy/warlock/DemonologyWarlockStrategy.h @@ -0,0 +1,41 @@ +/* + * 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_DEMONOLOGYWARLOCKSTRATEGY_H +#define _PLAYERBOT_DEMONOLOGYWARLOCKSTRATEGY_H + +#include "GenericWarlockStrategy.h" +#include "CombatStrategy.h" + +class PlayerbotAI; + +class DemonologyWarlockStrategy : public GenericWarlockStrategy +{ +public: + DemonologyWarlockStrategy(PlayerbotAI* botAI); + + void InitTriggers(std::vector& triggers) override; + std::string const getName() override { return "demo"; } + NextAction** getDefaultActions() override; +}; + +class DemonologyWarlockAoeStrategy : public CombatStrategy +{ +public: + DemonologyWarlockAoeStrategy(PlayerbotAI* botAI); + + void InitTriggers(std::vector& triggers) override; + std::string const getName() override { return "demo aoe"; } +}; + +class MetaMeleeAoeStrategy : public CombatStrategy +{ +public: + MetaMeleeAoeStrategy(PlayerbotAI* botAI); + + void InitTriggers(std::vector& triggers) override; + std::string const getName() override { return "meta melee"; } +}; +#endif diff --git a/src/strategy/warlock/DestructionWarlockStrategy.cpp b/src/strategy/warlock/DestructionWarlockStrategy.cpp new file mode 100644 index 00000000..42498bde --- /dev/null +++ b/src/strategy/warlock/DestructionWarlockStrategy.cpp @@ -0,0 +1,107 @@ +/* + * 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 "DestructionWarlockStrategy.h" +#include "Playerbots.h" + +// ===== Action Node Factory ===== +class DestructionWarlockStrategyActionNodeFactory : public NamedObjectFactory +{ +public: + DestructionWarlockStrategyActionNodeFactory() + { + creators["immolate"] = &immolate; + creators["conflagrate"] = &conflagrate; + creators["chaos bolt"] = &chaos_bolt; + creators["incinerate"] = &incinerate; + creators["corruption"] = &corruption; + creators["corruption on attacker"] = &corruption_on_attacker; + creators["curse of agony"] = &curse_of_agony; + creators["curse of agony on attacker"] = &curse_of_agony_on_attacker; + creators["shadow bolt"] = &shadow_bolt; + creators["shadowburn"] = &shadowburn; + creators["life tap"] = &life_tap; + creators["shadowfury"] = &shadowfury; + creators["shadowflame"] = &shadowflame; + creators["seed of corruption"] = &seed_of_corruption; + creators["seed of corruption on attacker"] = &seed_of_corruption; + creators["rain of fire"] = &rain_of_fire; + } + +private: + static ActionNode* immolate(PlayerbotAI*) { return new ActionNode("immolate", nullptr, nullptr, nullptr); } + static ActionNode* conflagrate(PlayerbotAI*) { return new ActionNode("conflagrate", nullptr, nullptr, nullptr); } + static ActionNode* chaos_bolt(PlayerbotAI*) { return new ActionNode("chaos bolt", nullptr, nullptr, nullptr); } + static ActionNode* incinerate(PlayerbotAI*) { return new ActionNode("incinerate", nullptr, nullptr, nullptr); } + static ActionNode* corruption(PlayerbotAI*) { return new ActionNode("corruption", nullptr, nullptr, nullptr); } + static ActionNode* corruption_on_attacker(PlayerbotAI*) { return new ActionNode("corruption on attacker", nullptr, nullptr, nullptr); } + static ActionNode* curse_of_agony(PlayerbotAI*) { return new ActionNode("curse of agony", nullptr, nullptr, nullptr); } + static ActionNode* curse_of_agony_on_attacker(PlayerbotAI*) { return new ActionNode("curse of agony on attacker", nullptr, nullptr, nullptr); } + static ActionNode* shadow_bolt(PlayerbotAI*) { return new ActionNode("shadow bolt", nullptr, nullptr, nullptr); } + static ActionNode* shadowburn(PlayerbotAI*) { return new ActionNode("shadowburn", nullptr, nullptr, nullptr); } + static ActionNode* life_tap(PlayerbotAI*) { return new ActionNode("life tap", nullptr, nullptr, nullptr); } + static ActionNode* shadowfury(PlayerbotAI*) { return new ActionNode("shadowfury", nullptr, nullptr, nullptr); } + static ActionNode* shadowflame(PlayerbotAI*) { return new ActionNode("shadowflame", nullptr, nullptr, nullptr); } + static ActionNode* seed_of_corruption(PlayerbotAI*) { return new ActionNode("seed of corruption", nullptr, nullptr, nullptr); } + static ActionNode* seed_of_corruption_on_attacker(PlayerbotAI*) { return new ActionNode("seed of corruption on attacker", nullptr, nullptr, nullptr); } + static ActionNode* rain_of_fire(PlayerbotAI*) { return new ActionNode("rain of fire", nullptr, nullptr, nullptr); } +}; + +// ===== Single Target Strategy ===== +DestructionWarlockStrategy::DestructionWarlockStrategy(PlayerbotAI* botAI) : GenericWarlockStrategy(botAI) +{ + actionNodeFactories.Add(new DestructionWarlockStrategyActionNodeFactory()); +} + +// ===== Default Actions ===== +NextAction** DestructionWarlockStrategy::getDefaultActions() +{ + return NextAction::array( 0, + new NextAction("immolate", 6.1f), + new NextAction("conflagrate", 6.0f), + new NextAction("chaos bolt", 5.9f), + new NextAction("incinerate", 5.8f), + new NextAction("corruption", 5.4f), // Note: Corruption, Curse of Agony, and Shadow Bolt won't be used after + new NextAction("curse of agony", 5.3f), // the character learns Incinerate at level 64 + new NextAction("shadow bolt", 5.2f), + new NextAction("shoot", 5.0f), nullptr); +} + +// ===== Trigger Initialization === +void DestructionWarlockStrategy::InitTriggers(std::vector& triggers) +{ + GenericWarlockStrategy::InitTriggers(triggers); + + // Main DoT triggers for high uptime + high priority cooldowns + triggers.push_back(new TriggerNode("immolate", NextAction::array(0, new NextAction("immolate", 20.0f), nullptr))); + triggers.push_back(new TriggerNode("conflagrate", NextAction::array(0, new NextAction("conflagrate", 19.5f), nullptr))); + triggers.push_back(new TriggerNode("chaos bolt", NextAction::array(0, new NextAction("chaos bolt", 19.0f), nullptr))); + + // Note: These DoTs won't be used after the character learns Incinerate at level 64 + triggers.push_back(new TriggerNode("corruption on attacker", NextAction::array(0, new NextAction("corruption on attacker", 5.6f), nullptr))); + triggers.push_back(new TriggerNode("curse of agony on attacker", NextAction::array(0, new NextAction("curse of agony on attacker", 5.5f), nullptr))); + triggers.push_back(new TriggerNode("corruption", NextAction::array(0, new NextAction("corruption", 5.4f), nullptr))); + triggers.push_back(new TriggerNode("curse of agony", NextAction::array(0, new NextAction("curse of agony", 5.3f), nullptr))); + + // Shadowburn as execute if target is low HP + triggers.push_back(new TriggerNode("target critical health", NextAction::array(0, new NextAction("shadowburn", 18.0f), nullptr))); + + // Life Tap glyph buff, and Life Tap as filler + triggers.push_back(new TriggerNode("life tap glyph buff", NextAction::array(0, new NextAction("life tap", 29.0f), nullptr))); + triggers.push_back(new TriggerNode("life tap", NextAction::array(0, new NextAction("life tap", 5.1f), nullptr))); +} + +// ===== AoE Strategy, 3+ enemies ===== +DestructionWarlockAoeStrategy::DestructionWarlockAoeStrategy(PlayerbotAI* botAI) : CombatStrategy(botAI) {} + +void DestructionWarlockAoeStrategy::InitTriggers(std::vector& triggers) +{ + triggers.push_back(new TriggerNode("medium aoe", NextAction::array(0, + new NextAction("shadowfury", 23.0f), + new NextAction("shadowflame", 22.5f), + new NextAction("seed of corruption on attacker", 22.0f), + new NextAction("seed of corruption", 21.5f), + new NextAction("rain of fire", 21.0f), nullptr))); +} diff --git a/src/strategy/warlock/DestructionWarlockStrategy.h b/src/strategy/warlock/DestructionWarlockStrategy.h new file mode 100644 index 00000000..3df8c01c --- /dev/null +++ b/src/strategy/warlock/DestructionWarlockStrategy.h @@ -0,0 +1,33 @@ +/* + * 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_DESTRUCTIONWARLOCKSTRATEGY_H +#define _PLAYERBOT_DESTRUCTIONWARLOCKSTRATEGY_H + +#include "GenericWarlockStrategy.h" +#include "CombatStrategy.h" + +class PlayerbotAI; + +class DestructionWarlockStrategy : public GenericWarlockStrategy +{ +public: + DestructionWarlockStrategy(PlayerbotAI* botAI); + + void InitTriggers(std::vector& triggers) override; + std::string const getName() override { return "destro"; } + NextAction** getDefaultActions() override; +}; + +class DestructionWarlockAoeStrategy : public CombatStrategy +{ +public: + DestructionWarlockAoeStrategy(PlayerbotAI* botAI); + + void InitTriggers(std::vector& triggers) override; + std::string const getName() override { return "destro aoe"; } +}; + +#endif diff --git a/src/strategy/warlock/DpsWarlockStrategy.cpp b/src/strategy/warlock/DpsWarlockStrategy.cpp index 0085bfc6..49b6a2be 100644 --- a/src/strategy/warlock/DpsWarlockStrategy.cpp +++ b/src/strategy/warlock/DpsWarlockStrategy.cpp @@ -4,105 +4,85 @@ */ #include "DpsWarlockStrategy.h" - #include "Playerbots.h" +// This strategy is designed for low-level Warlocks without talents. +// All of the important spells/cooldowns have been migrated to +// their respective specs. + +// ===== Action Node Factory ===== class DpsWarlockStrategyActionNodeFactory : public NamedObjectFactory { public: DpsWarlockStrategyActionNodeFactory() { + creators["corruption"] = &corruption; + creators["curse of agony"] = &curse_of_agony; + creators["immolate"] = &immolate; creators["shadow bolt"] = &shadow_bolt; - creators["unstable affliction"] = &unstable_affliction; - creators["unstable affliction on attacker"] = &unstable_affliction_on_attacker; + creators["life tap"] = &life_tap; + creators["shadowflame"] = &shadowflame; + creators["seed of corruption"] = &seed_of_corruption; + creators["rain of fire"] = &rain_of_fire; + creators["drain soul"] = &drain_soul; } private: - static ActionNode* shadow_bolt([[maybe_unused]] PlayerbotAI* botAI) - { - return new ActionNode("shadow bolt", - /*P*/ nullptr, - /*A*/ NextAction::array(0, new NextAction("shoot"), nullptr), - /*C*/ nullptr); - } - static ActionNode* unstable_affliction([[maybe_unused]] PlayerbotAI* botAI) - { - return new ActionNode("unstable affliction", - /*P*/ NULL, - /*A*/ NextAction::array(0, new NextAction("immolate"), NULL), - /*C*/ NULL); - } - static ActionNode* unstable_affliction_on_attacker([[maybe_unused]] PlayerbotAI* botAI) - { - return new ActionNode("unstable affliction on attacker", - /*P*/ NULL, - /*A*/ NextAction::array(0, new NextAction("immolate on attacker"), NULL), - /*C*/ NULL); - } + static ActionNode* corruption(PlayerbotAI*) { return new ActionNode("corruption", nullptr, nullptr, nullptr); } + static ActionNode* curse_of_agony(PlayerbotAI*){return new ActionNode("curse of agony", nullptr, nullptr, nullptr);} + static ActionNode* immolate(PlayerbotAI*) { return new ActionNode("immolate", nullptr, nullptr, nullptr); } + static ActionNode* shadow_bolt(PlayerbotAI*) { return new ActionNode("shadow bolt", nullptr, nullptr, nullptr); } + static ActionNode* life_tap(PlayerbotAI*) { return new ActionNode("life tap", nullptr, nullptr, nullptr); } + static ActionNode* shadowflame(PlayerbotAI*) { return new ActionNode("shadowflame", nullptr, nullptr, nullptr); } + static ActionNode* seed_of_corruption(PlayerbotAI*){return new ActionNode("seed of corruption", nullptr, nullptr, nullptr);} + static ActionNode* rain_of_fire(PlayerbotAI*) { return new ActionNode("rain of fire", nullptr, nullptr, nullptr); } + static ActionNode* drain_soul(PlayerbotAI*) { return new ActionNode("drain soul", nullptr, nullptr, nullptr); } }; +// ===== Single Target Strategy ===== DpsWarlockStrategy::DpsWarlockStrategy(PlayerbotAI* botAI) : GenericWarlockStrategy(botAI) { actionNodeFactories.Add(new DpsWarlockStrategyActionNodeFactory()); } +// ===== Default Actions ===== NextAction** DpsWarlockStrategy::getDefaultActions() { - return NextAction::array( - 0, new NextAction("haunt", ACTION_DEFAULT + 0.4f), new NextAction("demonic empowerment", ACTION_DEFAULT + 0.3f), - new NextAction("shadow bolt", ACTION_DEFAULT + 0.2f), new NextAction("shoot", ACTION_DEFAULT), nullptr); + return NextAction::array(0, + new NextAction("immolate", 5.5f), + new NextAction("corruption", 5.4f), + new NextAction("curse of agony", 5.3f), + new NextAction("shadow bolt", 5.2f), + new NextAction("shoot", 5.0f), nullptr); } +// ===== Trigger Initialization === void DpsWarlockStrategy::InitTriggers(std::vector& triggers) { GenericWarlockStrategy::InitTriggers(triggers); - triggers.push_back( - new TriggerNode("backlash", NextAction::array(0, new NextAction("shadow bolt", 20.0f), nullptr))); + // Main DoT triggers for high uptime + triggers.push_back(new TriggerNode("corruption on attacker", NextAction::array(0, new NextAction("corruption on attacker", 20.0f), nullptr))); + triggers.push_back(new TriggerNode("curse of agony on attacker", NextAction::array(0, new NextAction("curse of agony on attacker", 19.5f), nullptr))); + triggers.push_back(new TriggerNode("immolate on attacker", NextAction::array(0, new NextAction("immolate on attacker", 19.0f), nullptr))); + triggers.push_back(new TriggerNode("corruption", NextAction::array(0, new NextAction("corruption", 18.5f), nullptr))); + triggers.push_back(new TriggerNode("curse of agony", NextAction::array(0, new NextAction("curse of agony", 18.0f), nullptr))); + triggers.push_back(new TriggerNode("immolate", NextAction::array(0, new NextAction("immolate", 17.5f), nullptr))); - triggers.push_back(new TriggerNode("haunt", NextAction::array(0, new NextAction("haunt", 26.0f), NULL))); + // Drain Soul as execute if target is low HP + triggers.push_back(new TriggerNode("target critical health", NextAction::array(0, new NextAction("drain soul", 17.0f), nullptr))); - triggers.push_back( - new TriggerNode("shadow trance", NextAction::array(0, new NextAction("shadow bolt", 15.0f), NULL))); - - triggers.push_back(new TriggerNode("backlash", NextAction::array(0, new NextAction("shadow bolt", 15.0f), NULL))); - - triggers.push_back(new TriggerNode("molten core", NextAction::array(0, new NextAction("incinerate", 15.0f), NULL))); - - triggers.push_back(new TriggerNode("decimation", NextAction::array(0, new NextAction("soul fire", 16.0f), NULL))); - - // cast during movement - triggers.push_back( - new TriggerNode("high mana", NextAction::array(0, new NextAction("life tap", ACTION_DEFAULT + 0.1f), nullptr))); - + // Cast during movement or to activate glyph buff + triggers.push_back(new TriggerNode("life tap", NextAction::array(0, new NextAction("life tap", ACTION_DEFAULT + 0.1f), nullptr))); triggers.push_back(new TriggerNode("life tap glyph buff", NextAction::array(0, new NextAction("life tap", 28.0f), NULL))); - - triggers.push_back( - new TriggerNode("metamorphosis", NextAction::array(0, new NextAction("metamorphosis", 20.0f), NULL))); } +// ===== AoE Strategy, 3+ enemies ===== void DpsAoeWarlockStrategy::InitTriggers(std::vector& triggers) { - triggers.push_back( - new TriggerNode("medium aoe", NextAction::array(0, new NextAction("seed of corruption", 33.0f), - new NextAction("seed of corruption on attacker", 32.0f), - new NextAction("rain of fire", 31.0f), nullptr))); - triggers.push_back(new TriggerNode("corruption on attacker", - NextAction::array(0, new NextAction("corruption on attacker", 27.0f), nullptr))); - triggers.push_back( - new TriggerNode("unstable affliction on attacker", - NextAction::array(0, new NextAction("unstable affliction on attacker", 26.0f), NULL))); - triggers.push_back( - new TriggerNode("curse of agony on attacker", - NextAction::array(0, new NextAction("curse of agony on attacker", 25.0f), nullptr))); -} - -void DpsWarlockDebuffStrategy::InitTriggers(std::vector& triggers) -{ - triggers.push_back( - new TriggerNode("corruption", NextAction::array(0, new NextAction("corruption", 22.0f), nullptr))); - triggers.push_back(new TriggerNode("unstable affliction", - NextAction::array(0, new NextAction("unstable affliction", 21.0f), NULL))); - triggers.push_back( - new TriggerNode("curse of agony", NextAction::array(0, new NextAction("curse of agony", 20.0f), nullptr))); + triggers.push_back(new TriggerNode("medium aoe", NextAction::array(0, + new NextAction("shadowflame", 22.5f), + new NextAction("seed of corruption on attacker", 22.0f), + new NextAction("seed of corruption", 21.5f), + new NextAction("rain of fire", 21.0f), nullptr))); } diff --git a/src/strategy/warlock/DpsWarlockStrategy.h b/src/strategy/warlock/DpsWarlockStrategy.h index b6be2eee..4ae63930 100644 --- a/src/strategy/warlock/DpsWarlockStrategy.h +++ b/src/strategy/warlock/DpsWarlockStrategy.h @@ -30,14 +30,4 @@ public: void InitTriggers(std::vector& triggers) override; std::string const getName() override { return "aoe"; } }; - -class DpsWarlockDebuffStrategy : public CombatStrategy -{ -public: - DpsWarlockDebuffStrategy(PlayerbotAI* botAI) : CombatStrategy(botAI) {} - - void InitTriggers(std::vector& triggers) override; - std::string const getName() override { return "dps debuff"; } -}; - #endif diff --git a/src/strategy/warlock/GenericWarlockNonCombatStrategy.cpp b/src/strategy/warlock/GenericWarlockNonCombatStrategy.cpp index ccdd2c67..f575750b 100644 --- a/src/strategy/warlock/GenericWarlockNonCombatStrategy.cpp +++ b/src/strategy/warlock/GenericWarlockNonCombatStrategy.cpp @@ -4,7 +4,7 @@ */ #include "GenericWarlockNonCombatStrategy.h" - +#include "AiFactory.h" #include "Playerbots.h" class GenericWarlockNonCombatStrategyActionNodeFactory : public NamedObjectFactory @@ -20,6 +20,10 @@ public: creators["summon felhunter"] = &summon_felhunter; } + // Pet skills are setup in pass-through fashion, so if one fails, it attempts to cast the next one + // The order goes Felguard -> Felhunter -> Succubus -> Voidwalker -> Imp + // Pets are summoned based on the non-combat strategy you have active, the warlock's level, and if they have a soulstone available + private: static ActionNode* fel_armor([[maybe_unused]] PlayerbotAI* botAI) { @@ -36,7 +40,6 @@ private: /*A*/ NextAction::array(0, new NextAction("demon skin"), nullptr), /*C*/ nullptr); } - static ActionNode* summon_voidwalker([[maybe_unused]] PlayerbotAI* botAI) { return new ActionNode("summon voidwalker", @@ -62,7 +65,7 @@ private: { return new ActionNode("summon felguard", /*P*/ nullptr, - /*A*/ NextAction::array(0, new NextAction("summon succubus"), nullptr), + /*A*/ NextAction::array(0, new NextAction("summon felhunter"), nullptr), /*C*/ nullptr); } }; @@ -75,45 +78,133 @@ GenericWarlockNonCombatStrategy::GenericWarlockNonCombatStrategy(PlayerbotAI* bo void GenericWarlockNonCombatStrategy::InitTriggers(std::vector& triggers) { NonCombatStrategy::InitTriggers(triggers); + triggers.push_back(new TriggerNode("has pet", NextAction::array(0, new NextAction("toggle pet spell", 60.0f), nullptr))); + triggers.push_back(new TriggerNode("no pet", NextAction::array(0, new NextAction("fel domination", 30.0f), nullptr))); + triggers.push_back(new TriggerNode("soul link", NextAction::array(0, new NextAction("soul link", 28.0f), nullptr))); + triggers.push_back(new TriggerNode("demon armor", NextAction::array(0, new NextAction("fel armor", 27.0f), nullptr))); + triggers.push_back(new TriggerNode("no healthstone", NextAction::array(0, new NextAction("create healthstone", 26.0f), nullptr))); + triggers.push_back(new TriggerNode("no soulstone", NextAction::array(0, new NextAction("create soulstone", 25.0f), nullptr))); + triggers.push_back(new TriggerNode("life tap", NextAction::array(0, new NextAction("life tap", 23.0f), nullptr))); - triggers.push_back( - new TriggerNode("demon armor", NextAction::array(0, new NextAction("fel armor", 21.0f), nullptr))); - // triggers.push_back(new TriggerNode("often", NextAction::array(0, new NextAction("apply oil", 1.0f), nullptr))); - triggers.push_back( - new TriggerNode("has pet", NextAction::array(0, new NextAction("toggle pet spell", 60.0f), nullptr))); - triggers.push_back( - new TriggerNode("no healthstone", NextAction::array(0, new NextAction("create healthstone", 15.0f), nullptr))); - triggers.push_back( - new TriggerNode("no spellstone", NextAction::array(0, new NextAction("create spellstone", 13.0f), nullptr))); - triggers.push_back( - new TriggerNode("spellstone", NextAction::array(0, new NextAction("spellstone", 13.0f), nullptr))); -} - -void WarlockPetStrategy::InitTriggers(std::vector& triggers) -{ - // triggers.push_back(new TriggerNode("no pet", NextAction::array(0, new NextAction("summon felguard", 60.0f), - // nullptr))); - // TODO Warlock pets + Player* bot = botAI->GetBot(); + int tab = AiFactory::GetPlayerSpecTab(bot); + + if (tab == 2) // Destruction uses Firestone + { + triggers.push_back( + new TriggerNode("no firestone", NextAction::array(0, new NextAction("create firestone", 24.0f), nullptr))); + triggers.push_back( + new TriggerNode("firestone", NextAction::array(0, new NextAction("firestone", 24.0f), nullptr))); + } + else // Affliction and Demonology use Spellstone + { + triggers.push_back(new TriggerNode("no spellstone", + NextAction::array(0, new NextAction("create spellstone", 24.0f), nullptr))); + triggers.push_back( + new TriggerNode("spellstone", NextAction::array(0, new NextAction("spellstone", 24.0f), nullptr))); + } } +// Non-combat strategy for summoning a Imp +// Enabled by default for the Destruction spec +// To enable, type "nc +imp" +// To disable, type "nc -imp" SummonImpStrategy::SummonImpStrategy(PlayerbotAI* ai) : NonCombatStrategy(ai) {} void SummonImpStrategy::InitTriggers(std::vector& triggers) { - triggers.push_back(new TriggerNode("no pet", NextAction::array(0, new NextAction("summon imp", 11.0f), NULL))); + triggers.push_back(new TriggerNode("no pet", NextAction::array(0, new NextAction("summon imp", 29.0f), NULL))); } -SummonFelguardStrategy::SummonFelguardStrategy(PlayerbotAI* ai) : NonCombatStrategy(ai) {} +// Non-combat strategy for summoning a Voidwalker +// Disabled by default +// To enable, type "nc +voidwalker" +// To disable, type "nc -voidwalker" +SummonVoidwalkerStrategy::SummonVoidwalkerStrategy(PlayerbotAI* ai) : NonCombatStrategy(ai) {} -void SummonFelguardStrategy::InitTriggers(std::vector& triggers) +void SummonVoidwalkerStrategy::InitTriggers(std::vector& triggers) { - triggers.push_back(new TriggerNode("no pet", NextAction::array(0, new NextAction("summon felguard", 11.0f), NULL))); + triggers.push_back( + new TriggerNode("no pet", NextAction::array(0, new NextAction("summon voidwalker", 29.0f), NULL))); } +// Non-combat strategy for summoning a Succubus +// Disabled by default +// To enable, type "nc +succubus" +// To disable, type "nc -succubus" +SummonSuccubusStrategy::SummonSuccubusStrategy(PlayerbotAI* ai) : NonCombatStrategy(ai) {} + +void SummonSuccubusStrategy::InitTriggers(std::vector& triggers) +{ + triggers.push_back(new TriggerNode("no pet", NextAction::array(0, new NextAction("summon succubus", 29.0f), NULL))); +} + +// Non-combat strategy for summoning a Felhunter +// Enabled by default for the Affliction spec +// To enable, type "nc +felhunter" +// To disable, type "nc -felhunter" SummonFelhunterStrategy::SummonFelhunterStrategy(PlayerbotAI* ai) : NonCombatStrategy(ai) {} void SummonFelhunterStrategy::InitTriggers(std::vector& triggers) { triggers.push_back( - new TriggerNode("no pet", NextAction::array(0, new NextAction("summon felhunter", 11.0f), NULL))); -} \ No newline at end of file + new TriggerNode("no pet", NextAction::array(0, new NextAction("summon felhunter", 29.0f), NULL))); +} + +// Non-combat strategy for summoning a Felguard +// Enabled by default for the Demonology spec +// To enable, type "nc +felguard" +// To disable, type "nc -felguard" +SummonFelguardStrategy::SummonFelguardStrategy(PlayerbotAI* ai) : NonCombatStrategy(ai) {} + +void SummonFelguardStrategy::InitTriggers(std::vector& triggers) +{ + triggers.push_back(new TriggerNode("no pet", NextAction::array(0, new NextAction("summon felguard", 29.0f), NULL))); +} + +// Non-combat strategy for selecting themselves to receive soulstone +// Disabled by default +// To enable, type "nc +ss self" +// To disable, type "nc -ss self" +SoulstoneSelfStrategy::SoulstoneSelfStrategy(PlayerbotAI* ai) : NonCombatStrategy(ai) {} + +void SoulstoneSelfStrategy::InitTriggers(std::vector& triggers) +{ + triggers.push_back(new TriggerNode("soulstone", NextAction::array(0, new NextAction("soulstone self", 24.0f), NULL))); +} + +// Non-combat strategy for selecting the master to receive soulstone +// Disabled by default +// To enable, type "nc +ss master" +// To disable, type "nc -ss master" +SoulstoneMasterStrategy::SoulstoneMasterStrategy(PlayerbotAI* ai) : NonCombatStrategy(ai) {} + +void SoulstoneMasterStrategy::InitTriggers(std::vector& triggers) +{ + triggers.push_back( + new TriggerNode("soulstone", NextAction::array(0, new NextAction("soulstone master", 24.0f), NULL))); +} + +// Non-combat strategy for selecting tanks to receive soulstone +// Disabled by default +// To enable, type "nc +ss tank" +// To disable, type "nc -ss tank" +SoulstoneTankStrategy::SoulstoneTankStrategy(PlayerbotAI* ai) : NonCombatStrategy(ai) {} + +void SoulstoneTankStrategy::InitTriggers(std::vector& triggers) +{ + triggers.push_back( + new TriggerNode("soulstone", NextAction::array(0, new NextAction("soulstone tank", 24.0f), NULL))); +} + +// Non-combat strategy for selecting healers to receive soulstone +// Disabled by default +// To enable, type "nc +ss healer" +// To disable, type "nc -ss healer" +SoulstoneHealerStrategy::SoulstoneHealerStrategy(PlayerbotAI* ai) : NonCombatStrategy(ai) {} + +void SoulstoneHealerStrategy::InitTriggers(std::vector& triggers) +{ + triggers.push_back( + new TriggerNode("soulstone", NextAction::array(0, new NextAction("soulstone healer", 24.0f), NULL))); +} diff --git a/src/strategy/warlock/GenericWarlockNonCombatStrategy.h b/src/strategy/warlock/GenericWarlockNonCombatStrategy.h index 18fdcc3e..ef226c50 100644 --- a/src/strategy/warlock/GenericWarlockNonCombatStrategy.h +++ b/src/strategy/warlock/GenericWarlockNonCombatStrategy.h @@ -19,30 +19,31 @@ public: void InitTriggers(std::vector& triggers) override; }; -class WarlockPetStrategy : public Strategy -{ -public: - WarlockPetStrategy(PlayerbotAI* botAI) : Strategy(botAI) {} - - std::string const getName() override { return "pet"; } - void InitTriggers(std::vector& triggers) override; -}; - class SummonImpStrategy : public NonCombatStrategy { public: SummonImpStrategy(PlayerbotAI* ai); - virtual std::string const getName() override { return "bhealth"; } + virtual std::string const getName() override { return "imp"; } public: void InitTriggers(std::vector& triggers) override; }; -class SummonFelguardStrategy : public NonCombatStrategy +class SummonVoidwalkerStrategy : public NonCombatStrategy { public: - SummonFelguardStrategy(PlayerbotAI* ai); - virtual std::string const getName() override { return "bdps"; } + SummonVoidwalkerStrategy(PlayerbotAI* ai); + virtual std::string const getName() override { return "voidwalker"; } + +public: + void InitTriggers(std::vector& triggers) override; +}; + +class SummonSuccubusStrategy : public NonCombatStrategy +{ +public: + SummonSuccubusStrategy(PlayerbotAI* ai); + virtual std::string const getName() override { return "succubus"; } public: void InitTriggers(std::vector& triggers) override; @@ -52,7 +53,57 @@ class SummonFelhunterStrategy : public NonCombatStrategy { public: SummonFelhunterStrategy(PlayerbotAI* ai); - virtual std::string const getName() override { return "bmana"; } + virtual std::string const getName() override { return "felhunter"; } + +public: + void InitTriggers(std::vector& triggers) override; +}; + +class SummonFelguardStrategy : public NonCombatStrategy +{ +public: + SummonFelguardStrategy(PlayerbotAI* ai); + virtual std::string const getName() override { return "felguard"; } + +public: + void InitTriggers(std::vector& triggers) override; +}; + +class SoulstoneSelfStrategy : public NonCombatStrategy +{ +public: + SoulstoneSelfStrategy(PlayerbotAI* ai); + virtual std::string const getName() override { return "ss self"; } + +public: + void InitTriggers(std::vector& triggers) override; +}; + +class SoulstoneMasterStrategy : public NonCombatStrategy +{ +public: + SoulstoneMasterStrategy(PlayerbotAI* ai); + virtual std::string const getName() override { return "ss master"; } + +public: + void InitTriggers(std::vector& triggers) override; +}; + +class SoulstoneTankStrategy : public NonCombatStrategy +{ +public: + SoulstoneTankStrategy(PlayerbotAI* ai); + virtual std::string const getName() override { return "ss tank"; } + +public: + void InitTriggers(std::vector& triggers) override; +}; + +class SoulstoneHealerStrategy : public NonCombatStrategy +{ +public: + SoulstoneHealerStrategy(PlayerbotAI* ai); + virtual std::string const getName() override { return "ss healer"; } public: void InitTriggers(std::vector& triggers) override; diff --git a/src/strategy/warlock/GenericWarlockStrategy.cpp b/src/strategy/warlock/GenericWarlockStrategy.cpp index 8ed081b7..7f413685 100644 --- a/src/strategy/warlock/GenericWarlockStrategy.cpp +++ b/src/strategy/warlock/GenericWarlockStrategy.cpp @@ -4,7 +4,7 @@ */ #include "GenericWarlockStrategy.h" - +#include "Strategy.h" #include "Playerbots.h" class GenericWarlockStrategyActionNodeFactory : public NamedObjectFactory @@ -12,28 +12,22 @@ class GenericWarlockStrategyActionNodeFactory : public NamedObjectFactory& triggers) { - RangedCombatStrategy::InitTriggers(triggers); + CombatStrategy::InitTriggers(triggers); - // triggers.push_back(new TriggerNode("shadow trance", NextAction::array(0, new NextAction("shadow bolt", 20.0f), - // nullptr))); triggers.push_back(new TriggerNode("low health", NextAction::array(0, new NextAction("drain - // life", 40.0f), nullptr))); - triggers.push_back( - new TriggerNode("low mana", NextAction::array(0, new NextAction("life tap", ACTION_EMERGENCY + 5), nullptr))); - triggers.push_back( - new TriggerNode("target critical health", NextAction::array(0, new NextAction("drain soul", 30.0f), nullptr))); - // triggers.push_back(new TriggerNode("immolate", NextAction::array(0, new NextAction("immolate", 13.0f), new - // NextAction("conflagrate", 13.0f), nullptr))); triggers.push_back(new TriggerNode("enemy too close for spell", - // NextAction::array(0, new NextAction("flee", 49.0f), NULL))); + triggers.push_back(new TriggerNode("low mana", NextAction::array(0, new NextAction("life tap", ACTION_EMERGENCY + 5), nullptr))); + triggers.push_back(new TriggerNode("medium threat", NextAction::array(0, new NextAction("soulshatter", 55.0f), nullptr))); + triggers.push_back(new TriggerNode("spell lock", NextAction::array(0, new NextAction("spell lock", ACTION_INTERRUPT), nullptr))); + triggers.push_back(new TriggerNode("devour magic purge", NextAction::array(0, new NextAction("devour magic purge", ACTION_DISPEL), nullptr))); + triggers.push_back(new TriggerNode("devour magic cleanse", NextAction::array(0, new NextAction("devour magic cleanse", ACTION_DISPEL), nullptr))); } void WarlockBoostStrategy::InitTriggers(std::vector& triggers) { - triggers.push_back( - new TriggerNode("amplify curse", NextAction::array(0, new NextAction("amplify curse", 41.0f), nullptr))); + // Placeholder for future boost triggers +} + +void WarlockPetStrategy::InitTriggers(std::vector& triggers) +{ + // Placeholder for future pet triggers } void WarlockCcStrategy::InitTriggers(std::vector& triggers) { - triggers.push_back(new TriggerNode("banish", NextAction::array(0, new NextAction("banish on cc", 32.0f), nullptr))); - triggers.push_back(new TriggerNode("fear", NextAction::array(0, new NextAction("fear on cc", 33.0f), nullptr))); + triggers.push_back(new TriggerNode("banish", NextAction::array(0, new NextAction("banish on cc", 33.0f), nullptr))); + triggers.push_back(new TriggerNode("fear", NextAction::array(0, new NextAction("fear on cc", 32.0f), nullptr))); +} + +// Combat strategy for using Curse of the Elements +// Enabling this will turn off their use of Curse of Agony +// Enabled by default for the Destruction spec +// To enable, type "co +curse of elements" +// To disable, type "co -curse of elements" + +void WarlockCurseOfTheElementsStrategy::InitTriggers(std::vector& triggers) +{ + triggers.push_back(new TriggerNode("curse of the elements", NextAction::array(0, new NextAction("curse of the elements", 30.0f), nullptr))); } diff --git a/src/strategy/warlock/GenericWarlockStrategy.h b/src/strategy/warlock/GenericWarlockStrategy.h index 5cf98510..23fa739b 100644 --- a/src/strategy/warlock/GenericWarlockStrategy.h +++ b/src/strategy/warlock/GenericWarlockStrategy.h @@ -7,11 +7,10 @@ #define _PLAYERBOT_GENERICWARLOCKSTRATEGY_H #include "CombatStrategy.h" -#include "RangedCombatStrategy.h" class PlayerbotAI; -class GenericWarlockStrategy : public RangedCombatStrategy +class GenericWarlockStrategy : public CombatStrategy { public: GenericWarlockStrategy(PlayerbotAI* botAI); @@ -30,6 +29,15 @@ public: void InitTriggers(std::vector& triggers) override; }; +class WarlockPetStrategy : public Strategy +{ +public: + WarlockPetStrategy(PlayerbotAI* botAI) : Strategy(botAI) {} + + std::string const getName() override { return "pet"; } + void InitTriggers(std::vector& triggers) override; +}; + class WarlockCcStrategy : public Strategy { public: @@ -39,4 +47,13 @@ public: void InitTriggers(std::vector& triggers) override; }; +class WarlockCurseOfTheElementsStrategy : public Strategy +{ +public: + WarlockCurseOfTheElementsStrategy(PlayerbotAI* botAI) : Strategy(botAI) {} + + std::string const getName() override { return "curse of elements"; } + void InitTriggers(std::vector& triggers) override; +}; + #endif diff --git a/src/strategy/warlock/TankWarlockStrategy.cpp b/src/strategy/warlock/TankWarlockStrategy.cpp index 551a6e0e..dfa36b50 100644 --- a/src/strategy/warlock/TankWarlockStrategy.cpp +++ b/src/strategy/warlock/TankWarlockStrategy.cpp @@ -4,65 +4,41 @@ */ #include "TankWarlockStrategy.h" - #include "Playerbots.h" -class GenericWarlockStrategyActionNodeFactory : public NamedObjectFactory +// Combat strategy for a Warlock Tank, for certain bosses like Twin Emperors +// Priority is set to spam Searing Pain and use Shadow Ward on CD +// Disabled by default +// To enable, type "co +tank" +// To disable, type "co -tank" + +// ===== Action Node Factory ===== +class TankWarlockStrategyActionNodeFactory : public NamedObjectFactory { public: - GenericWarlockStrategyActionNodeFactory() + TankWarlockStrategyActionNodeFactory() { - creators["summon voidwalker"] = &summon_voidwalker; - creators["summon felguard"] = &summon_felguard; - creators["summon succubus"] = &summon_succubus; - creators["summon felhunter"] = &summon_felhunter; + creators["shadow ward"] = &shadow_ward; + creators["searing pain"] = &searing_pain; } private: - static ActionNode* summon_voidwalker([[maybe_unused]] PlayerbotAI* botAI) - { - return new ActionNode("summon voidwalker", - /*P*/ nullptr, - /*A*/ NextAction::array(0, new NextAction("summon imp"), nullptr), - /*C*/ nullptr); - } - - static ActionNode* summon_felguard([[maybe_unused]] PlayerbotAI* botAI) - { - return new ActionNode("summon felguard", - /*P*/ nullptr, - /*A*/ NextAction::array(0, new NextAction("summon succubus"), nullptr), - /*C*/ nullptr); - } - - static ActionNode* summon_succubus([[maybe_unused]] PlayerbotAI* botAI) - { - return new ActionNode("summon succubus", - /*P*/ nullptr, - /*A*/ NextAction::array(0, new NextAction("summon voidwalker"), nullptr), - /*C*/ nullptr); - } - - static ActionNode* summon_felhunter([[maybe_unused]] PlayerbotAI* botAI) - { - return new ActionNode("summon felhunter", - /*P*/ nullptr, - /*A*/ NextAction::array(0, new NextAction("summon imp"), nullptr), - /*C*/ nullptr); - } + static ActionNode* shadow_ward(PlayerbotAI*) { return new ActionNode("shadow ward", nullptr, nullptr, nullptr); } + static ActionNode* searing_pain(PlayerbotAI*) { return new ActionNode("searing pain", nullptr, nullptr, nullptr); } }; +// ===== Warlock Tank Combat Strategy ===== TankWarlockStrategy::TankWarlockStrategy(PlayerbotAI* botAI) : GenericWarlockStrategy(botAI) { - actionNodeFactories.Add(new GenericWarlockStrategyActionNodeFactory()); + actionNodeFactories.Add(new TankWarlockStrategyActionNodeFactory()); } NextAction** TankWarlockStrategy::getDefaultActions() { - return NextAction::array(0, new NextAction("shoot", ACTION_DEFAULT), nullptr); + // Shadow Ward is the highest priority, Searing Pain next. + return NextAction::array(0, new NextAction("shadow ward", 27.5f), new NextAction("searing pain", 27.0f), nullptr); } void TankWarlockStrategy::InitTriggers(std::vector& triggers) { - GenericWarlockStrategy::InitTriggers(triggers); } diff --git a/src/strategy/warlock/WarlockActions.cpp b/src/strategy/warlock/WarlockActions.cpp index 6be8826b..8b362cd6 100644 --- a/src/strategy/warlock/WarlockActions.cpp +++ b/src/strategy/warlock/WarlockActions.cpp @@ -5,23 +5,204 @@ #include "WarlockActions.h" +#include +#include #include "Event.h" +#include "Item.h" +#include "ObjectGuid.h" +#include "Player.h" +#include "PlayerbotAI.h" #include "Playerbots.h" +#include "ServerFacade.h" +#include "Unit.h" -bool CastDrainSoulAction::isUseful() { return AI_VALUE2(uint32, "item count", "soul shard") < 10; } - -Value* CastBanishAction::GetTargetValue() { return context->GetValue("cc target", "banish"); } - -bool CastBanishAction::Execute(Event event) { return botAI->CastSpell("banish", GetTarget()); } - -Value* CastFearOnCcAction::GetTargetValue() { return context->GetValue("cc target", "fear"); } - -bool CastFearOnCcAction::Execute(Event event) { return botAI->CastSpell("fear", GetTarget()); } - -bool CastFearOnCcAction::isPossible() { return true; } - -bool CastFearOnCcAction::isUseful() { return true; } +// Checks if the bot has less than 32 soul shards, and if so, allows casting Drain Soul +bool CastDrainSoulAction::isUseful() { return AI_VALUE2(uint32, "item count", "soul shard") < 32; } +// 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; } -Unit* UseSoulstoneAction::GetTarget() { return botAI->GetMaster(); } +// Checks if the target marked with the moon icon can be banished +bool CastBanishOnCcAction::isPossible() +{ + Unit* target = GetTarget(); + if (!target) + return false; + + // Only possible on elementals or demons + uint32 creatureType = target->GetCreatureType(); + if (creatureType != CREATURE_TYPE_DEMON && creatureType != CREATURE_TYPE_ELEMENTAL) + return false; + + // Use base class to check spell available, range, etc + return CastCrowdControlSpellAction::isPossible(); +} + +// Checks if the target marked with the moon icon can be feared +bool CastFearOnCcAction::isPossible() +{ + Unit* target = GetTarget(); + if (!target) + return false; + + // Fear cannot be cast on mechanical or undead creatures + uint32 creatureType = target->GetCreatureType(); + if (creatureType == CREATURE_TYPE_MECHANICAL || creatureType == CREATURE_TYPE_UNDEAD) + return false; + + // Use base class to check spell available, range, etc + return CastCrowdControlSpellAction::isPossible(); +} + +// Checks if the enemies are close enough to use Shadowflame +bool CastShadowflameAction::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, 7.0f); // 7 yard cone + return facingTarget && targetClose; +} + +// Checks if the bot knows Seed of Corruption, and prevents the use of Rain of Fire if it does +bool CastRainOfFireAction::isUseful() +{ + Unit* target = GetTarget(); + if (!target) + return false; + if (bot->HasSpell(27243) || bot->HasSpell(47835) || bot->HasSpell(47836)) // Seed of Corruption spell IDs + return false; + return true; +} + +// Checks if the enemies are close enough to use Hellfire +bool CastHellfireAction::isUseful() +{ + Unit* target = AI_VALUE(Unit*, "current target"); + if (!target) + return false; + + return bot->IsWithinCombatRange(target, 5.0f); // 5 yard AoE radius +} + +// Checks if the "meta melee aoe" strategy is active, OR if the bot is in melee range of the target +bool CastImmolationAuraAction::isUseful() +{ + if (botAI->HasStrategy("meta melee", BOT_STATE_COMBAT)) + return true; + + Unit* target = AI_VALUE(Unit*, "current target"); + if (!target) + return false; + + if (!bot->HasAura(47241)) // 47241 is Metamorphosis spell ID (WotLK) + return false; + + return bot->IsWithinCombatRange(target, 5.0f); // 5 yard AoE radius +} + +// Checks if the "warlock tank" strategy is active, and if so, prevents the use of Soulshatter +bool CastSoulshatterAction::isUseful() +{ + if (botAI->HasStrategy("tank", BOT_STATE_COMBAT)) + return false; + return true; +} + +// Checks if the target has a soulstone aura +static bool HasSoulstoneAura(Unit* unit) +{ + static const std::vector soulstoneAuraIds = {20707, 20762, 20763, 20764, 20765, 27239, 47883}; + for (uint32 spellId : soulstoneAuraIds) + if (unit->HasAura(spellId)) + return true; + return false; +} + +// Use the soulstone item on the bot itself with nc strategy "ss self" +bool UseSoulstoneSelfAction::Execute(Event event) +{ + std::vector items = AI_VALUE2(std::vector, "inventory items", "soulstone"); + if (items.empty()) + return false; + + if (HasSoulstoneAura(bot)) + return false; + + bot->SetSelection(bot->GetGUID()); + return UseItem(items[0], ObjectGuid::Empty, nullptr, bot); +} + +// Use the soulstone item on the bot's master with nc strategy "ss master" +bool UseSoulstoneMasterAction::Execute(Event event) +{ + std::vector items = AI_VALUE2(std::vector, "inventory items", "soulstone"); + if (items.empty()) + return false; + + Player* master = botAI->GetMaster(); + if (!master || HasSoulstoneAura(master)) + return false; + + bot->SetSelection(master->GetGUID()); + return UseItem(items[0], ObjectGuid::Empty, nullptr, master); +} + +// Use the soulstone item on a tank in the group with nc strategy "ss tank" +bool UseSoulstoneTankAction::Execute(Event event) +{ + std::vector items = AI_VALUE2(std::vector, "inventory items", "soulstone"); + if (items.empty()) + return false; + + Player* tank = nullptr; + Group* group = bot->GetGroup(); + if (group) + { + for (GroupReference* gref = group->GetFirstMember(); gref; gref = gref->next()) + { + Player* member = gref->GetSource(); + if (member && member->IsAlive() && botAI->IsTank(member) && !HasSoulstoneAura(member)) + { + tank = member; + break; + } + } + } + + if (!tank) + return false; + + bot->SetSelection(tank->GetGUID()); + return UseItem(items[0], ObjectGuid::Empty, nullptr, tank); +} + +// Use the soulstone item on a healer in the group with nc strategy "ss healer" +bool UseSoulstoneHealerAction::Execute(Event event) +{ + std::vector items = AI_VALUE2(std::vector, "inventory items", "soulstone"); + if (items.empty()) + return false; + + Player* healer = nullptr; + Group* group = bot->GetGroup(); + if (group) + { + for (GroupReference* gref = group->GetFirstMember(); gref; gref = gref->next()) + { + Player* member = gref->GetSource(); + if (member && member->IsAlive() && botAI->IsHeal(member) && !HasSoulstoneAura(member)) + { + healer = member; + break; + } + } + } + + if (!healer) + return false; + + bot->SetSelection(healer->GetGUID()); + return UseItem(items[0], ObjectGuid::Empty, nullptr, healer); +} diff --git a/src/strategy/warlock/WarlockActions.h b/src/strategy/warlock/WarlockActions.h index 4b29c59b..ad037d4d 100644 --- a/src/strategy/warlock/WarlockActions.h +++ b/src/strategy/warlock/WarlockActions.h @@ -8,10 +8,13 @@ #include "GenericSpellActions.h" #include "UseItemAction.h" +#include "Action.h" class PlayerbotAI; class Unit; +// Buff and Out of Combat Spells + class CastDemonSkinAction : public CastBuffSpellAction { public: @@ -30,71 +33,67 @@ public: CastFelArmorAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "fel armor") {} }; -BEGIN_RANGED_SPELL_ACTION(CastShadowBoltAction, "shadow bolt") -END_SPELL_ACTION() - -class CastDrainSoulAction : public CastSpellAction +class CastSoulLinkAction : public CastBuffSpellAction { public: - CastDrainSoulAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "drain soul") {} - - bool isUseful() override; + CastSoulLinkAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "soul link", false, 5000) {} + std::string const GetTargetName() override { return "pet target"; } }; -class CastDrainManaAction : public CastSpellAction +class CastCreateHealthstoneAction : public CastBuffSpellAction { public: - CastDrainManaAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "drain mana") {} + CastCreateHealthstoneAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "create healthstone") {} }; -class CastDrainLifeAction : public CastSpellAction +class CastCreateFirestoneAction : public CastBuffSpellAction { public: - CastDrainLifeAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "drain life") {} + CastCreateFirestoneAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "create firestone") {} }; -class CastCurseOfAgonyAction : public CastDebuffSpellAction +class CastCreateSpellstoneAction : public CastBuffSpellAction { public: - CastCurseOfAgonyAction(PlayerbotAI* botAI) : CastDebuffSpellAction(botAI, "curse of agony", true) {} + CastCreateSpellstoneAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "create spellstone") {} }; -class CastCurseOfWeaknessAction : public CastDebuffSpellAction +class CastCreateSoulstoneAction : public CastBuffSpellAction { public: - CastCurseOfWeaknessAction(PlayerbotAI* botAI) : CastDebuffSpellAction(botAI, "curse of weakness") {} + CastCreateSoulstoneAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "create soulstone") {} }; -class CastCorruptionAction : public CastDebuffSpellAction +class UseSoulstoneSelfAction : public UseSpellItemAction { public: - CastCorruptionAction(PlayerbotAI* botAI) : CastDebuffSpellAction(botAI, "corruption", true) {} - bool isUseful() override - { - return CastDebuffSpellAction::isUseful() && !botAI->HasAura("seed of corruption", GetTarget(), false, true); - } + UseSoulstoneSelfAction(PlayerbotAI* botAI) : UseSpellItemAction(botAI, "soulstone") {} + bool Execute(Event event) override; }; -class CastCorruptionOnAttackerAction : public CastDebuffSpellOnAttackerAction +class UseSoulstoneMasterAction : public UseSpellItemAction { public: - CastCorruptionOnAttackerAction(PlayerbotAI* botAI) : CastDebuffSpellOnAttackerAction(botAI, "corruption", true) {} - bool isUseful() override - { - return CastDebuffSpellOnAttackerAction::isUseful() && - !botAI->HasAura("seed of corruption", GetTarget(), false, true); - } + UseSoulstoneMasterAction(PlayerbotAI* botAI) : UseSpellItemAction(botAI, "soulstone") {} + bool Execute(Event event) override; }; -class CastCurseOfAgonyOnAttackerAction : public CastDebuffSpellOnAttackerAction +class UseSoulstoneTankAction : public UseSpellItemAction { public: - CastCurseOfAgonyOnAttackerAction(PlayerbotAI* botAI) - : CastDebuffSpellOnAttackerAction(botAI, "curse of agony", true) - { - } + UseSoulstoneTankAction(PlayerbotAI* botAI) : UseSpellItemAction(botAI, "soulstone") {} + bool Execute(Event event) override; }; +class UseSoulstoneHealerAction : public UseSpellItemAction +{ +public: + UseSoulstoneHealerAction(PlayerbotAI* botAI) : UseSpellItemAction(botAI, "soulstone") {} + bool Execute(Event event) override; +}; + +// Summoning Spells + class CastSummonVoidwalkerAction : public CastBuffSpellAction { public: @@ -124,41 +123,293 @@ class CastSummonSuccubusAction : public CastBuffSpellAction public: CastSummonSuccubusAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "summon succubus") {} }; - -class CastCreateHealthstoneAction : public CastBuffSpellAction +class CastFelDominationAction : public CastBuffSpellAction { public: - CastCreateHealthstoneAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "create healthstone") {} + CastFelDominationAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "fel domination") {} }; -class CastCreateFirestoneAction : public CastBuffSpellAction +// CC and Pet Spells + +class CastBanishOnCcAction : public CastCrowdControlSpellAction { public: - CastCreateFirestoneAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "create firestone") {} + CastBanishOnCcAction(PlayerbotAI* botAI) : CastCrowdControlSpellAction(botAI, "banish") {} + bool isPossible() override; }; -class CastCreateSpellstoneAction : public CastBuffSpellAction +class CastFearOnCcAction : public CastCrowdControlSpellAction { public: - CastCreateSpellstoneAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "create spellstone") {} + CastFearOnCcAction(PlayerbotAI* botAI) : CastCrowdControlSpellAction(botAI, "fear") {} + bool isPossible() override; }; -class CastBanishAction : public CastBuffSpellAction +class CastSpellLockAction : public CastSpellAction { public: - CastBanishAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "banish on cc") {} - - Value* GetTargetValue() override; - bool Execute(Event event) override; + CastSpellLockAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "spell lock") {} }; +class CastDevourMagicPurgeAction : public CastSpellAction +{ +public: + CastDevourMagicPurgeAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "devour magic") {} + + std::string const GetTargetName() override { return "current target"; } +}; + +class CastDevourMagicCleanseAction : public CastSpellAction +{ +public: + CastDevourMagicCleanseAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "devour magic cleanse") {} + std::string const getName() override { return "cleanse magic on party"; } +}; + +// Utility Spells + +class CastShadowWardAction : public CastBuffSpellAction +{ +public: + CastShadowWardAction(PlayerbotAI* ai) : CastBuffSpellAction(ai, "shadow ward") {} +}; + +class CastSoulshatterAction : public CastSpellAction +{ +public: + CastSoulshatterAction(PlayerbotAI* ai) : CastSpellAction(ai, "soulshatter") {} + bool isUseful() override; +}; + +class CastLifeTapAction : public CastSpellAction +{ +public: + CastLifeTapAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "life tap") {} + + std::string const GetTargetName() override { return "self target"; } + bool isUseful() override; +}; + +class CastCurseOfWeaknessAction : public CastDebuffSpellAction +{ +public: + CastCurseOfWeaknessAction(PlayerbotAI* botAI) : CastDebuffSpellAction(botAI, "curse of weakness") {} +}; + +class CastCurseOfTheElementsAction : public CastDebuffSpellAction +{ +public: + CastCurseOfTheElementsAction(PlayerbotAI* ai) : CastDebuffSpellAction(ai, "curse of the elements", true) {} + bool isUseful() override + { + // Bypass TTL check + return CastAuraSpellAction::isUseful(); + } +}; + +class DemonChargeAction : public CastSpellAction +{ +public: + DemonChargeAction(PlayerbotAI* ai) : CastSpellAction(ai, "demon charge") {} +}; + +// Cooldown Spells + +class CastMetamorphosisAction : public CastBuffSpellAction +{ +public: + CastMetamorphosisAction(PlayerbotAI* ai) : CastBuffSpellAction(ai, "metamorphosis") {} +}; + +class CastDemonicEmpowermentAction : public CastBuffSpellAction +{ +public: + CastDemonicEmpowermentAction(PlayerbotAI* ai) : CastBuffSpellAction(ai, "demonic empowerment") {} + std::string const GetTargetName() override { return "pet target"; } +}; + +// DoT Spells + +class CastCurseOfAgonyAction : public CastDebuffSpellAction +{ +public: + CastCurseOfAgonyAction(PlayerbotAI* botAI) : CastDebuffSpellAction(botAI, "curse of agony", true) {} + bool isUseful() override + { + if (botAI->HasStrategy( + "curse of elements", BOT_STATE_COMBAT)) // If Curse of the Elements strategy is active, do not cast Curse of Agony + return false; + // Bypass TTL check + return CastAuraSpellAction::isUseful(); + } +}; + +class CastCurseOfAgonyOnAttackerAction : public CastDebuffSpellOnAttackerAction +{ +public: + CastCurseOfAgonyOnAttackerAction(PlayerbotAI* botAI) + : CastDebuffSpellOnAttackerAction(botAI, "curse of agony", true) + { + } + bool isUseful() override + { + if (botAI->HasStrategy( + "curse of elements", BOT_STATE_COMBAT)) // If Curse of the Elements strategy is active, do not cast Curse of Agony + return false; + // Bypass TTL check + return CastAuraSpellAction::isUseful(); + } +}; + +class CastCorruptionAction : public CastDebuffSpellAction +{ +public: + CastCorruptionAction(PlayerbotAI* botAI) : CastDebuffSpellAction(botAI, "corruption", true) {} + bool isUseful() override + { + // Bypass TTL check and prevent casting if Seed of Corruption is present + return CastAuraSpellAction::isUseful() && !botAI->HasAura("seed of corruption", GetTarget(), false, true); + } +}; + +class CastCorruptionOnAttackerAction : public CastDebuffSpellOnAttackerAction +{ +public: + CastCorruptionOnAttackerAction(PlayerbotAI* botAI) : CastDebuffSpellOnAttackerAction(botAI, "corruption", true) {} + bool isUseful() override + { + // Bypass TTL check and prevent casting if Seed of Corruption is present + return CastAuraSpellAction::isUseful() && !botAI->HasAura("seed of corruption", GetTarget(), false, true); + } +}; + +class CastImmolateAction : public CastDebuffSpellAction +{ +public: + CastImmolateAction(PlayerbotAI* botAI) : CastDebuffSpellAction(botAI, "immolate", true) {} + bool isUseful() override + { + // Bypass TTL check + return CastAuraSpellAction::isUseful(); + } +}; + +class CastImmolateOnAttackerAction : public CastDebuffSpellOnAttackerAction +{ +public: + CastImmolateOnAttackerAction(PlayerbotAI* botAI) : CastDebuffSpellOnAttackerAction(botAI, "immolate", true) {} + bool isUseful() override + { + // Bypass TTL check + return CastAuraSpellAction::isUseful(); + } +}; + +class CastUnstableAfflictionAction : public CastDebuffSpellAction +{ +public: + CastUnstableAfflictionAction(PlayerbotAI* ai) : CastDebuffSpellAction(ai, "unstable affliction", true) {} + bool isUseful() override + { + // Bypass TTL check + return CastAuraSpellAction::isUseful(); + } +}; + +class CastUnstableAfflictionOnAttackerAction : public CastDebuffSpellOnAttackerAction +{ +public: + CastUnstableAfflictionOnAttackerAction(PlayerbotAI* ai) + : CastDebuffSpellOnAttackerAction(ai, "unstable affliction", true) + { + } + bool isUseful() override + { + // Bypass TTL check + return CastAuraSpellAction::isUseful(); + } +}; + +// Damage Spells + +class CastShadowBoltAction : public CastSpellAction +{ +public: + CastShadowBoltAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "shadow bolt") {} +}; + +class CastDrainSoulAction : public CastSpellAction +{ +public: + CastDrainSoulAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "drain soul") {} + + bool isUseful() override; +}; + +class CastDrainManaAction : public CastSpellAction +{ +public: + CastDrainManaAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "drain mana") {} +}; + +class CastDrainLifeAction : public CastSpellAction +{ +public: + CastDrainLifeAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "drain life") {} +}; + +class CastConflagrateAction : public CastSpellAction +{ +public: + CastConflagrateAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "conflagrate") {} +}; + +class CastIncinerateAction : public CastSpellAction +{ +public: + CastIncinerateAction(PlayerbotAI* ai) : CastSpellAction(ai, "incinerate") {} +}; + +class CastHauntAction : public CastSpellAction +{ +public: + CastHauntAction(PlayerbotAI* ai) : CastSpellAction(ai, "haunt") {} +}; + +class CastSoulFireAction : public CastSpellAction +{ +public: + CastSoulFireAction(PlayerbotAI* ai) : CastSpellAction(ai, "soul fire") {} +}; + +class CastShadowburnAction : public CastSpellAction +{ +public: + CastShadowburnAction(PlayerbotAI* ai) : CastSpellAction(ai, "shadowburn") {} +}; + +class CastChaosBoltAction : public CastSpellAction +{ +public: + CastChaosBoltAction(PlayerbotAI* ai) : CastSpellAction(ai, "chaos bolt") {} +}; + +class CastSearingPainAction : public CastSpellAction +{ +public: + CastSearingPainAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "searing pain") {} +}; + +// AoE Spells + class CastSeedOfCorruptionAction : public CastDebuffSpellAction { public: CastSeedOfCorruptionAction(PlayerbotAI* botAI) : CastDebuffSpellAction(botAI, "seed of corruption", true, 0) {} bool isUseful() override { - return CastDebuffSpellAction::isUseful() && !botAI->HasAura("corruption", GetTarget(), false, true); + // Bypass TTL check + return CastAuraSpellAction::isUseful(); } ActionThreatType getThreatType() override { return ActionThreatType::Aoe; } }; @@ -172,7 +423,8 @@ public: } bool isUseful() override { - return CastDebuffSpellOnAttackerAction::isUseful() && !botAI->HasAura("corruption", GetTarget(), false, true); + // Bypass TTL check + return CastAuraSpellAction::isUseful(); } ActionThreatType getThreatType() override { return ActionThreatType::Aoe; } }; @@ -182,6 +434,22 @@ class CastRainOfFireAction : public CastSpellAction public: CastRainOfFireAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "rain of fire") {} ActionThreatType getThreatType() override { return ActionThreatType::Aoe; } + bool isUseful() override; +}; + +class CastHellfireAction : public CastSpellAction +{ +public: + CastHellfireAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "hellfire") {} + ActionThreatType getThreatType() override { return ActionThreatType::Aoe; } + bool isUseful() override; +}; + +class CastShadowflameAction : public CastSpellAction +{ +public: + CastShadowflameAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "shadowflame") {} + bool isUseful() override; }; class CastShadowfuryAction : public CastSpellAction @@ -190,125 +458,17 @@ public: CastShadowfuryAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "shadowfury") {} }; -class CastImmolateAction : public CastDebuffSpellAction +class CastImmolationAuraAction : public CastSpellAction { public: - CastImmolateAction(PlayerbotAI* botAI) : CastDebuffSpellAction(botAI, "immolate", true) {} -}; - -class CastImmolateOnAttackerAction : public CastDebuffSpellOnAttackerAction -{ -public: - CastImmolateOnAttackerAction(PlayerbotAI* botAI) : CastDebuffSpellOnAttackerAction(botAI, "immolate", true) {} -}; - -class CastConflagrateAction : public CastSpellAction -{ -public: - CastConflagrateAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "conflagrate") {} -}; - -class CastIncinirateAction : public CastSpellAction -{ -public: - CastIncinirateAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "incinirate") {} -}; - -class CastFearAction : public CastDebuffSpellAction -{ -public: - CastFearAction(PlayerbotAI* botAI) : CastDebuffSpellAction(botAI, "fear") {} -}; - -class CastFearOnCcAction : public CastBuffSpellAction -{ -public: - CastFearOnCcAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "fear on cc") {} - - Value* GetTargetValue() override; - bool Execute(Event event) override; - bool isPossible() override; + CastImmolationAuraAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "immolation aura") {} + ActionThreatType getThreatType() override { return ActionThreatType::Aoe; } bool isUseful() override; }; -class CastLifeTapAction : public CastSpellAction +class ShadowCleaveAction : public CastMeleeSpellAction { public: - CastLifeTapAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "life tap") {} - - std::string const GetTargetName() override { return "self target"; } - bool isUseful() override; -}; - -class CastAmplifyCurseAction : public CastBuffSpellAction -{ -public: - CastAmplifyCurseAction(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, "amplify curse") {} -}; - -class CastSiphonLifeAction : public CastDebuffSpellAction -{ -public: - CastSiphonLifeAction(PlayerbotAI* botAI) : CastDebuffSpellAction(botAI, "siphon life", true) {} -}; - -class CastSiphonLifeOnAttackerAction : public CastDebuffSpellOnAttackerAction -{ -public: - CastSiphonLifeOnAttackerAction(PlayerbotAI* botAI) : CastDebuffSpellOnAttackerAction(botAI, "siphon life") {} -}; - -class CastUnstableAfflictionAction : public CastDebuffSpellAction -{ -public: - CastUnstableAfflictionAction(PlayerbotAI* ai) : CastDebuffSpellAction(ai, "unstable affliction", true) {} -}; - -class CastHauntAction : public CastSpellAction -{ -public: - CastHauntAction(PlayerbotAI* ai) : CastSpellAction(ai, "haunt") {} -}; - -class CastDemonicEmpowermentAction : public CastBuffSpellAction -{ -public: - CastDemonicEmpowermentAction(PlayerbotAI* ai) : CastBuffSpellAction(ai, "demonic empowerment") {} - std::string const GetTargetName() override { return "pet target"; } -}; - -class CastMetamorphosisAction : public CastBuffSpellAction -{ -public: - CastMetamorphosisAction(PlayerbotAI* ai) : CastBuffSpellAction(ai, "metamorphosis") {} -}; - -class CastUnstableAfflictionOnAttackerAction : public CastDebuffSpellOnAttackerAction -{ -public: - CastUnstableAfflictionOnAttackerAction(PlayerbotAI* ai) - : CastDebuffSpellOnAttackerAction(ai, "unstable affliction", true) - { - } -}; - -class CastSoulFireAction : public CastSpellAction -{ -public: - CastSoulFireAction(PlayerbotAI* ai) : CastSpellAction(ai, "soul fire") {} -}; - -class CastIncinerateAction : public CastSpellAction -{ -public: - CastIncinerateAction(PlayerbotAI* ai) : CastSpellAction(ai, "incinerate") {} -}; - -class UseSoulstoneAction : public UseSpellItemAction -{ -public: - UseSoulstoneAction(PlayerbotAI* ai) : UseSpellItemAction(ai, "soulstone") {} - - Unit* GetTarget() override; + ShadowCleaveAction(PlayerbotAI* ai) : CastMeleeSpellAction(ai, "shadow cleave") {} }; #endif diff --git a/src/strategy/warlock/WarlockAiObjectContext.cpp b/src/strategy/warlock/WarlockAiObjectContext.cpp index 74dde632..e3dc6ca9 100644 --- a/src/strategy/warlock/WarlockAiObjectContext.cpp +++ b/src/strategy/warlock/WarlockAiObjectContext.cpp @@ -4,7 +4,10 @@ */ #include "WarlockAiObjectContext.h" - +#include "AfflictionWarlockStrategy.h" +#include "DemonologyWarlockStrategy.h" +#include "DestructionWarlockStrategy.h" +#include "TankWarlockStrategy.h" #include "DpsWarlockStrategy.h" #include "GenericTriggers.h" #include "GenericWarlockNonCombatStrategy.h" @@ -12,7 +15,6 @@ #include "Playerbots.h" #include "PullStrategy.h" #include "Strategy.h" -#include "TankWarlockStrategy.h" #include "UseItemAction.h" #include "WarlockActions.h" #include "WarlockTriggers.h" @@ -21,24 +23,40 @@ class WarlockStrategyFactoryInternal : public NamedObjectContext { public: WarlockStrategyFactoryInternal() - { + { creators["nc"] = &WarlockStrategyFactoryInternal::nc; creators["pull"] = &WarlockStrategyFactoryInternal::pull; - creators["aoe"] = &WarlockStrategyFactoryInternal::aoe; - creators["dps debuff"] = &WarlockStrategyFactoryInternal::dps_debuff; creators["boost"] = &WarlockStrategyFactoryInternal::boost; creators["cc"] = &WarlockStrategyFactoryInternal::cc; creators["pet"] = &WarlockStrategyFactoryInternal::pet; + creators["affli"] = &WarlockStrategyFactoryInternal::affliction; + creators["affli aoe"] = &WarlockStrategyFactoryInternal::affliction_aoe; + creators["demo"] = &WarlockStrategyFactoryInternal::demonology; + creators["demo aoe"] = &WarlockStrategyFactoryInternal::demonology_aoe; + creators["destro"] = &WarlockStrategyFactoryInternal::destruction; + creators["destro aoe"] = &WarlockStrategyFactoryInternal::destruction_aoe; + creators["meta melee"] = &WarlockStrategyFactoryInternal::meta_melee_aoe; + creators["dps"] = &WarlockStrategyFactoryInternal::dps; + creators["aoe"] = &WarlockStrategyFactoryInternal::aoe; + creators["curse of elements"] = &WarlockStrategyFactoryInternal::curse_of_elements; } private: static Strategy* pet(PlayerbotAI* botAI) { return new WarlockPetStrategy(botAI); } static Strategy* nc(PlayerbotAI* botAI) { return new GenericWarlockNonCombatStrategy(botAI); } - static Strategy* aoe(PlayerbotAI* botAI) { return new DpsAoeWarlockStrategy(botAI); } - static Strategy* dps_debuff(PlayerbotAI* botAI) { return new DpsWarlockDebuffStrategy(botAI); } static Strategy* pull(PlayerbotAI* botAI) { return new PullStrategy(botAI, "shoot"); } static Strategy* boost(PlayerbotAI* botAI) { return new WarlockBoostStrategy(botAI); } static Strategy* cc(PlayerbotAI* botAI) { return new WarlockCcStrategy(botAI); } + static Strategy* affliction(PlayerbotAI* botAI) { return new AfflictionWarlockStrategy(botAI); } + static Strategy* affliction_aoe(PlayerbotAI* botAI) { return new AfflictionWarlockAoeStrategy(botAI); } + static Strategy* demonology(PlayerbotAI* botAI) { return new DemonologyWarlockStrategy(botAI); } + static Strategy* demonology_aoe(PlayerbotAI* botAI) { return new DemonologyWarlockAoeStrategy(botAI); } + static Strategy* destruction(PlayerbotAI* botAI) { return new DestructionWarlockStrategy(botAI); } + static Strategy* destruction_aoe(PlayerbotAI* botAI) { return new DestructionWarlockAoeStrategy(botAI); } + static Strategy* meta_melee_aoe(PlayerbotAI* botAI) { return new MetaMeleeAoeStrategy(botAI); } + static Strategy* dps(PlayerbotAI* botAI) { return new DpsWarlockStrategy(botAI); } + static Strategy* aoe(PlayerbotAI* botAI) { return new DpsAoeWarlockStrategy(botAI); } + static Strategy* curse_of_elements(PlayerbotAI* botAI) { return new WarlockCurseOfTheElementsStrategy(botAI); } }; class WarlockCombatStrategyFactoryInternal : public NamedObjectContext @@ -46,13 +64,11 @@ class WarlockCombatStrategyFactoryInternal : public NamedObjectContext public: WarlockCombatStrategyFactoryInternal() : NamedObjectContext(false, true) { - creators["dps"] = &WarlockCombatStrategyFactoryInternal::dps; creators["tank"] = &WarlockCombatStrategyFactoryInternal::tank; } private: static Strategy* tank(PlayerbotAI* botAI) { return new TankWarlockStrategy(botAI); } - static Strategy* dps(PlayerbotAI* botAI) { return new DpsWarlockStrategy(botAI); } }; class NonCombatBuffStrategyFactoryInternal : public NamedObjectContext @@ -60,15 +76,27 @@ class NonCombatBuffStrategyFactoryInternal : public NamedObjectContext public: NonCombatBuffStrategyFactoryInternal() : NamedObjectContext(false, true) { - creators["bdps"] = &NonCombatBuffStrategyFactoryInternal::felguard; - creators["bmana"] = &NonCombatBuffStrategyFactoryInternal::felhunter; - creators["bhealth"] = &NonCombatBuffStrategyFactoryInternal::imp; + creators["imp"] = &NonCombatBuffStrategyFactoryInternal::imp; + creators["voidwalker"] = &NonCombatBuffStrategyFactoryInternal::voidwalker; + creators["succubus"] = &NonCombatBuffStrategyFactoryInternal::succubus; + creators["felhunter"] = &NonCombatBuffStrategyFactoryInternal::felhunter; + creators["felguard"] = &NonCombatBuffStrategyFactoryInternal::felguard; + creators["ss self"] = &NonCombatBuffStrategyFactoryInternal::soulstone_self; + creators["ss master"] = &NonCombatBuffStrategyFactoryInternal::soulstone_master; + creators["ss tank"] = &NonCombatBuffStrategyFactoryInternal::soulstone_tank; + creators["ss healer"] = &NonCombatBuffStrategyFactoryInternal::soulstone_healer; } private: static Strategy* imp(PlayerbotAI* ai) { return new SummonImpStrategy(ai); } + static Strategy* voidwalker(PlayerbotAI* ai) { return new SummonVoidwalkerStrategy(ai); } + static Strategy* succubus(PlayerbotAI* ai) { return new SummonSuccubusStrategy(ai); } static Strategy* felhunter(PlayerbotAI* ai) { return new SummonFelhunterStrategy(ai); } static Strategy* felguard(PlayerbotAI* ai) { return new SummonFelguardStrategy(ai); } + static Strategy* soulstone_self(PlayerbotAI* ai) { return new SoulstoneSelfStrategy(ai); } + static Strategy* soulstone_master(PlayerbotAI* ai) { return new SoulstoneMasterStrategy(ai); } + static Strategy* soulstone_tank(PlayerbotAI* ai) { return new SoulstoneTankStrategy(ai); } + static Strategy* soulstone_healer(PlayerbotAI* ai) { return new SoulstoneHealerStrategy(ai); } }; class WarlockTriggerFactoryInternal : public NamedObjectContext @@ -78,64 +106,73 @@ public: { creators["shadow trance"] = &WarlockTriggerFactoryInternal::shadow_trance; creators["demon armor"] = &WarlockTriggerFactoryInternal::demon_armor; + creators["soul link"] = &WarlockTriggerFactoryInternal::soul_link; creators["no healthstone"] = &WarlockTriggerFactoryInternal::HasHealthstone; creators["no firestone"] = &WarlockTriggerFactoryInternal::HasFirestone; creators["no spellstone"] = &WarlockTriggerFactoryInternal::HasSpellstone; + creators["no soulstone"] = &WarlockTriggerFactoryInternal::HasSoulstone; + creators["firestone"] = &WarlockTriggerFactoryInternal::firestone; + creators["spellstone"] = &WarlockTriggerFactoryInternal::spellstone; + creators["soulstone"] = &WarlockTriggerFactoryInternal::soulstone; + creators["banish"] = &WarlockTriggerFactoryInternal::banish; + creators["fear"] = &WarlockTriggerFactoryInternal::fear; + creators["spell lock"] = &WarlockTriggerFactoryInternal::spell_lock; + creators["devour magic purge"] = &WarlockTriggerFactoryInternal::devour_magic_purge; + creators["devour magic cleanse"] = &WarlockTriggerFactoryInternal::devour_magic_cleanse; + creators["backlash"] = &WarlockTriggerFactoryInternal::backlash; creators["corruption"] = &WarlockTriggerFactoryInternal::corruption; creators["corruption on attacker"] = &WarlockTriggerFactoryInternal::corruption_on_attacker; creators["curse of agony"] = &WarlockTriggerFactoryInternal::curse_of_agony; creators["curse of agony on attacker"] = &WarlockTriggerFactoryInternal::curse_of_agony_on_attacker; - creators["banish"] = &WarlockTriggerFactoryInternal::banish; - creators["spellstone"] = &WarlockTriggerFactoryInternal::spellstone; - creators["backlash"] = &WarlockTriggerFactoryInternal::backlash; - creators["fear"] = &WarlockTriggerFactoryInternal::fear; creators["immolate"] = &WarlockTriggerFactoryInternal::immolate; - creators["amplify curse"] = &WarlockTriggerFactoryInternal::amplify_curse; - creators["siphon life"] = &WarlockTriggerFactoryInternal::siphon_life; - creators["siphon life on attacker"] = &WarlockTriggerFactoryInternal::siphon_life_on_attacker; - creators["immolate on attacker"] = &WarlockTriggerFactoryInternal::immolate_on_attacker; creators["unstable affliction"] = &WarlockTriggerFactoryInternal::unstable_affliction; creators["unstable affliction on attacker"] = &WarlockTriggerFactoryInternal::unstable_affliction_on_attacker; creators["haunt"] = &WarlockTriggerFactoryInternal::haunt; + creators["curse of the elements"] = &WarlockTriggerFactoryInternal::curse_of_the_elements; creators["decimation"] = &WarlockTriggerFactoryInternal::decimation; + creators["life tap"] = &WarlockTriggerFactoryInternal::life_tap; creators["life tap glyph buff"] = &WarlockTriggerFactoryInternal::life_tap_glyph_buff; creators["molten core"] = &WarlockTriggerFactoryInternal::molten_core; creators["metamorphosis"] = &WarlockTriggerFactoryInternal::metamorphosis; + creators["demonic empowerment"] = &WarlockTriggerFactoryInternal::demonic_empowerment; + creators["immolation aura active"] = &WarlockTriggerFactoryInternal::immolation_aura_active; } private: - static Trigger* amplify_curse(PlayerbotAI* botAI) { return new AmplifyCurseTrigger(botAI); } static Trigger* shadow_trance(PlayerbotAI* botAI) { return new ShadowTranceTrigger(botAI); } static Trigger* demon_armor(PlayerbotAI* botAI) { return new DemonArmorTrigger(botAI); } + static Trigger* soul_link(PlayerbotAI* botAI) { return new SoulLinkTrigger(botAI); } static Trigger* HasHealthstone(PlayerbotAI* botAI) { return new HasHealthstoneTrigger(botAI); } static Trigger* HasFirestone(PlayerbotAI* botAI) { return new HasFirestoneTrigger(botAI); } static Trigger* HasSpellstone(PlayerbotAI* botAI) { return new HasSpellstoneTrigger(botAI); } + static Trigger* HasSoulstone(PlayerbotAI* botAI) { return new HasSoulstoneTrigger(botAI); } + static Trigger* firestone(PlayerbotAI* botAI) { return new FirestoneTrigger(botAI); } + static Trigger* spellstone(PlayerbotAI* botAI) { return new SpellstoneTrigger(botAI); } + static Trigger* soulstone(PlayerbotAI* botAI) { return new SoulstoneTrigger(botAI); } static Trigger* corruption(PlayerbotAI* botAI) { return new CorruptionTrigger(botAI); } static Trigger* corruption_on_attacker(PlayerbotAI* botAI) { return new CorruptionOnAttackerTrigger(botAI); } - static Trigger* siphon_life(PlayerbotAI* botAI) { return new SiphonLifeTrigger(botAI); } - static Trigger* siphon_life_on_attacker(PlayerbotAI* botAI) { return new SiphonLifeOnAttackerTrigger(botAI); } static Trigger* curse_of_agony(PlayerbotAI* botAI) { return new CurseOfAgonyTrigger(botAI); } - static Trigger* curse_of_agony_on_attacker(PlayerbotAI* botAI) - { - return new CastCurseOfAgonyOnAttackerTrigger(botAI); - } + static Trigger* curse_of_agony_on_attacker(PlayerbotAI* botAI) { return new CurseOfAgonyOnAttackerTrigger(botAI); } static Trigger* banish(PlayerbotAI* botAI) { return new BanishTrigger(botAI); } - static Trigger* spellstone(PlayerbotAI* botAI) { return new SpellstoneTrigger(botAI); } - static Trigger* backlash(PlayerbotAI* botAI) { return new BacklashTrigger(botAI); } static Trigger* fear(PlayerbotAI* botAI) { return new FearTrigger(botAI); } + static Trigger* spell_lock(PlayerbotAI* botAI) { return new SpellLockInterruptSpellTrigger(botAI); } + static Trigger* devour_magic_purge(PlayerbotAI* botAI) { return new DevourMagicPurgeTrigger(botAI); } + static Trigger* devour_magic_cleanse(PlayerbotAI* botAI) { return new DevourMagicCleanseTrigger(botAI); } + static Trigger* backlash(PlayerbotAI* botAI) { return new BacklashTrigger(botAI); } static Trigger* immolate(PlayerbotAI* botAI) { return new ImmolateTrigger(botAI); } static Trigger* immolate_on_attacker(PlayerbotAI* ai) { return new ImmolateOnAttackerTrigger(ai); } static Trigger* unstable_affliction(PlayerbotAI* ai) { return new UnstableAfflictionTrigger(ai); } - static Trigger* unstable_affliction_on_attacker(PlayerbotAI* ai) - { - return new UnstableAfflictionOnAttackerTrigger(ai); - } + static Trigger* unstable_affliction_on_attacker(PlayerbotAI* ai) { return new UnstableAfflictionOnAttackerTrigger(ai); } static Trigger* haunt(PlayerbotAI* ai) { return new HauntTrigger(ai); } + static Trigger* curse_of_the_elements(PlayerbotAI* ai) { return new CurseOfTheElementsTrigger(ai); } static Trigger* decimation(PlayerbotAI* ai) { return new DecimationTrigger(ai); } + static Trigger* life_tap(PlayerbotAI* ai) { return new LifeTapTrigger(ai); } static Trigger* life_tap_glyph_buff(PlayerbotAI* ai) { return new LifeTapGlyphBuffTrigger(ai); } static Trigger* molten_core(PlayerbotAI* ai) { return new MoltenCoreTrigger(ai); } static Trigger* metamorphosis(PlayerbotAI* ai) { return new MetamorphosisTrigger(ai); } + static Trigger* demonic_empowerment(PlayerbotAI* ai) { return new DemonicEmpowermentTrigger(ai); } + static Trigger* immolation_aura_active(PlayerbotAI* ai) { return new ImmolationAuraActiveTrigger(ai); } }; class WarlockAiObjectContextInternal : public NamedObjectContext @@ -146,104 +183,125 @@ public: creators["fel armor"] = &WarlockAiObjectContextInternal::fel_armor; creators["demon armor"] = &WarlockAiObjectContextInternal::demon_armor; creators["demon skin"] = &WarlockAiObjectContextInternal::demon_skin; + creators["soul link"] = &WarlockAiObjectContextInternal::soul_link; creators["create healthstone"] = &WarlockAiObjectContextInternal::create_healthstone; creators["create firestone"] = &WarlockAiObjectContextInternal::create_firestone; creators["create spellstone"] = &WarlockAiObjectContextInternal::create_spellstone; + creators["create soulstone"] = &WarlockAiObjectContextInternal::create_soulstone; + creators["firestone"] = &WarlockAiObjectContextInternal::firestone; creators["spellstone"] = &WarlockAiObjectContextInternal::spellstone; + creators["soulstone self"] = &WarlockAiObjectContextInternal::soulstone_self; + creators["soulstone master"] = &WarlockAiObjectContextInternal::soulstone_master; + creators["soulstone tank"] = &WarlockAiObjectContextInternal::soulstone_tank; + creators["soulstone healer"] = &WarlockAiObjectContextInternal::soulstone_healer; creators["summon voidwalker"] = &WarlockAiObjectContextInternal::summon_voidwalker; creators["summon felguard"] = &WarlockAiObjectContextInternal::summon_felguard; creators["summon felhunter"] = &WarlockAiObjectContextInternal::summon_felhunter; creators["summon succubus"] = &WarlockAiObjectContextInternal::summon_succubus; creators["summon imp"] = &WarlockAiObjectContextInternal::summon_imp; + creators["fel domination"] = &WarlockAiObjectContextInternal::fel_domination; creators["immolate"] = &WarlockAiObjectContextInternal::immolate; + creators["immolate on attacker"] = &WarlockAiObjectContextInternal::immolate_on_attacker; creators["corruption"] = &WarlockAiObjectContextInternal::corruption; creators["corruption on attacker"] = &WarlockAiObjectContextInternal::corruption_on_attacker; - creators["siphon life"] = &WarlockAiObjectContextInternal::siphon_life; - creators["siphon life on attacker"] = &WarlockAiObjectContextInternal::siphon_life_on_attacker; creators["curse of agony"] = &WarlockAiObjectContextInternal::curse_of_agony; creators["curse of agony on attacker"] = &WarlockAiObjectContextInternal::curse_of_agony_on_attacker; creators["shadow bolt"] = &WarlockAiObjectContextInternal::shadow_bolt; creators["drain soul"] = &WarlockAiObjectContextInternal::drain_soul; creators["drain mana"] = &WarlockAiObjectContextInternal::drain_mana; creators["drain life"] = &WarlockAiObjectContextInternal::drain_life; - creators["banish"] = &WarlockAiObjectContextInternal::banish; creators["banish on cc"] = &WarlockAiObjectContextInternal::banish_on_cc; + creators["fear on cc"] = &WarlockAiObjectContextInternal::fear_on_cc; + creators["spell lock"] = &WarlockAiObjectContextInternal::spell_lock; + creators["devour magic purge"] = &WarlockAiObjectContextInternal::devour_magic_purge; + creators["devour magic cleanse"] = &WarlockAiObjectContextInternal::devour_magic_cleanse; creators["seed of corruption"] = &WarlockAiObjectContextInternal::seed_of_corruption; creators["seed of corruption on attacker"] = &WarlockAiObjectContextInternal::seed_of_corruption_on_attacker; creators["rain of fire"] = &WarlockAiObjectContextInternal::rain_of_fire; + creators["hellfire"] = &WarlockAiObjectContextInternal::hellfire; creators["shadowfury"] = &WarlockAiObjectContextInternal::shadowfury; creators["life tap"] = &WarlockAiObjectContextInternal::life_tap; - creators["fear"] = &WarlockAiObjectContextInternal::fear; - creators["fear on cc"] = &WarlockAiObjectContextInternal::fear_on_cc; - creators["incinirate"] = &WarlockAiObjectContextInternal::incinirate; + creators["incinerate"] = &WarlockAiObjectContextInternal::incinerate; creators["conflagrate"] = &WarlockAiObjectContextInternal::conflagrate; - creators["amplify curse"] = &WarlockAiObjectContextInternal::amplify_curse; - - creators["immolate on attacker"] = &WarlockAiObjectContextInternal::immolate_on_attacker; creators["unstable affliction"] = &WarlockAiObjectContextInternal::unstable_affliction; creators["unstable affliction on attacker"] = &WarlockAiObjectContextInternal::unstable_affliction_on_attacker; creators["haunt"] = &WarlockAiObjectContextInternal::haunt; + creators["curse of the elements"] = &WarlockAiObjectContextInternal::curse_of_the_elements; creators["demonic empowerment"] = &WarlockAiObjectContextInternal::demonic_empowerment; creators["metamorphosis"] = &WarlockAiObjectContextInternal::metamorphosis; creators["soul fire"] = &WarlockAiObjectContextInternal::soul_fire; creators["incinerate"] = &WarlockAiObjectContextInternal::incinerate; - creators["soulstone"] = &WarlockAiObjectContextInternal::soulstone; - } + creators["demon charge"] = &WarlockAiObjectContextInternal::demon_charge; + creators["shadow cleave"] = &WarlockAiObjectContextInternal::shadow_cleave; + creators["shadowburn"] = &WarlockAiObjectContextInternal::shadowburn; + creators["shadowflame"] = &WarlockAiObjectContextInternal::shadowflame; + creators["immolation aura"] = &WarlockAiObjectContextInternal::immolation_aura; + creators["chaos bolt"] = &WarlockAiObjectContextInternal::chaos_bolt; + creators["soulshatter"] = &WarlockAiObjectContextInternal::soulshatter; + creators["searing pain"] = WarlockAiObjectContextInternal::searing_pain; + creators["shadow ward"] = &WarlockAiObjectContextInternal::shadow_ward; + } private: - static Action* amplify_curse(PlayerbotAI* botAI) { return new CastAmplifyCurseAction(botAI); } static Action* conflagrate(PlayerbotAI* botAI) { return new CastConflagrateAction(botAI); } - static Action* incinirate(PlayerbotAI* botAI) { return new CastIncinirateAction(botAI); } - static Action* fear_on_cc(PlayerbotAI* botAI) { return new CastFearOnCcAction(botAI); } - static Action* fear(PlayerbotAI* botAI) { return new CastFearAction(botAI); } + static Action* incinerate(PlayerbotAI* botAI) { return new CastIncinerateAction(botAI); } static Action* immolate(PlayerbotAI* botAI) { return new CastImmolateAction(botAI); } - static Action* summon_imp(PlayerbotAI* botAI) { return new CastSummonImpAction(botAI); } - static Action* summon_succubus(PlayerbotAI* botAI) { return new CastSummonSuccubusAction(botAI); } + static Action* immolate_on_attacker(PlayerbotAI* botAI) { return new CastImmolateOnAttackerAction(botAI); } static Action* fel_armor(PlayerbotAI* botAI) { return new CastFelArmorAction(botAI); } static Action* demon_armor(PlayerbotAI* botAI) { return new CastDemonArmorAction(botAI); } static Action* demon_skin(PlayerbotAI* botAI) { return new CastDemonSkinAction(botAI); } + static Action* soul_link(PlayerbotAI* botAI) { return new CastSoulLinkAction(botAI); } static Action* create_healthstone(PlayerbotAI* botAI) { return new CastCreateHealthstoneAction(botAI); } static Action* create_firestone(PlayerbotAI* botAI) { return new CastCreateFirestoneAction(botAI); } static Action* create_spellstone(PlayerbotAI* botAI) { return new CastCreateSpellstoneAction(botAI); } + static Action* create_soulstone(PlayerbotAI* botAI) { return new CastCreateSoulstoneAction(botAI); } + static Action* firestone(PlayerbotAI* botAI) { return new UseSpellItemAction(botAI, "firestone", true); } static Action* spellstone(PlayerbotAI* botAI) { return new UseSpellItemAction(botAI, "spellstone", true); } + static Action* soulstone_self(PlayerbotAI* botAI) { return new UseSoulstoneSelfAction(botAI); } + static Action* soulstone_master(PlayerbotAI* botAI) { return new UseSoulstoneMasterAction(botAI); } + static Action* soulstone_tank(PlayerbotAI* botAI) { return new UseSoulstoneTankAction(botAI); } + static Action* soulstone_healer(PlayerbotAI* botAI) { return new UseSoulstoneHealerAction(botAI); } static Action* summon_voidwalker(PlayerbotAI* botAI) { return new CastSummonVoidwalkerAction(botAI); } static Action* summon_felguard(PlayerbotAI* botAI) { return new CastSummonFelguardAction(botAI); } static Action* summon_felhunter(PlayerbotAI* botAI) { return new CastSummonFelhunterAction(botAI); } + static Action* summon_imp(PlayerbotAI* botAI) { return new CastSummonImpAction(botAI); } + static Action* summon_succubus(PlayerbotAI* botAI) { return new CastSummonSuccubusAction(botAI); } + static Action* fel_domination(PlayerbotAI* botAI) { return new CastFelDominationAction(botAI); } static Action* corruption(PlayerbotAI* botAI) { return new CastCorruptionAction(botAI); } static Action* corruption_on_attacker(PlayerbotAI* botAI) { return new CastCorruptionOnAttackerAction(botAI); } - static Action* siphon_life(PlayerbotAI* botAI) { return new CastSiphonLifeAction(botAI); } - static Action* siphon_life_on_attacker(PlayerbotAI* botAI) { return new CastSiphonLifeOnAttackerAction(botAI); } static Action* curse_of_agony(PlayerbotAI* botAI) { return new CastCurseOfAgonyAction(botAI); } - static Action* curse_of_agony_on_attacker(PlayerbotAI* botAI) - { - return new CastCurseOfAgonyOnAttackerAction(botAI); - } + static Action* curse_of_agony_on_attacker(PlayerbotAI* botAI) { return new CastCurseOfAgonyOnAttackerAction(botAI); } static Action* shadow_bolt(PlayerbotAI* botAI) { return new CastShadowBoltAction(botAI); } static Action* drain_soul(PlayerbotAI* botAI) { return new CastDrainSoulAction(botAI); } static Action* drain_mana(PlayerbotAI* botAI) { return new CastDrainManaAction(botAI); } static Action* drain_life(PlayerbotAI* botAI) { return new CastDrainLifeAction(botAI); } - static Action* banish(PlayerbotAI* botAI) { return new CastBanishAction(botAI); } - static Action* banish_on_cc(PlayerbotAI* botAI) { return new CastBanishAction(botAI); } + static Action* banish_on_cc(PlayerbotAI* botAI) { return new CastBanishOnCcAction(botAI); } + static Action* fear_on_cc(PlayerbotAI* botAI) { return new CastFearOnCcAction(botAI); } + static Action* spell_lock(PlayerbotAI* botAI) { return new CastSpellLockAction(botAI); } + static Action* devour_magic_purge(PlayerbotAI* botAI) { return new CastDevourMagicPurgeAction(botAI); } + static Action* devour_magic_cleanse(PlayerbotAI* botAI) { return new CastDevourMagicCleanseAction(botAI); } static Action* seed_of_corruption(PlayerbotAI* botAI) { return new CastSeedOfCorruptionAction(botAI); } - static Action* seed_of_corruption_on_attacker(PlayerbotAI* botAI) - { - return new CastSeedOfCorruptionOnAttackerAction(botAI); - } + static Action* seed_of_corruption_on_attacker(PlayerbotAI* botAI) { return new CastSeedOfCorruptionOnAttackerAction(botAI); } static Action* rain_of_fire(PlayerbotAI* botAI) { return new CastRainOfFireAction(botAI); } + static Action* hellfire(PlayerbotAI* botAI) { return new CastHellfireAction(botAI); } static Action* shadowfury(PlayerbotAI* botAI) { return new CastShadowfuryAction(botAI); } static Action* life_tap(PlayerbotAI* botAI) { return new CastLifeTapAction(botAI); } - static Action* immolate_on_attacker(PlayerbotAI* ai) { return new CastImmolateOnAttackerAction(ai); } static Action* unstable_affliction(PlayerbotAI* ai) { return new CastUnstableAfflictionAction(ai); } - static Action* unstable_affliction_on_attacker(PlayerbotAI* ai) - { - return new CastUnstableAfflictionOnAttackerAction(ai); - } + static Action* unstable_affliction_on_attacker(PlayerbotAI* ai) { return new CastUnstableAfflictionOnAttackerAction(ai); } static Action* haunt(PlayerbotAI* ai) { return new CastHauntAction(ai); } + static Action* curse_of_the_elements(PlayerbotAI* ai) { return new CastCurseOfTheElementsAction(ai); } static Action* demonic_empowerment(PlayerbotAI* ai) { return new CastDemonicEmpowermentAction(ai); } static Action* metamorphosis(PlayerbotAI* ai) { return new CastMetamorphosisAction(ai); } static Action* soul_fire(PlayerbotAI* ai) { return new CastSoulFireAction(ai); } - static Action* incinerate(PlayerbotAI* ai) { return new CastIncinerateAction(ai); } - static Action* soulstone(PlayerbotAI* ai) { return new UseSoulstoneAction(ai); } + static Action* demon_charge(PlayerbotAI* ai) { return new DemonChargeAction(ai); } + static Action* shadow_cleave(PlayerbotAI* ai) { return new ShadowCleaveAction(ai); } + static Action* shadowburn(PlayerbotAI* ai) { return new CastShadowburnAction(ai); } + static Action* shadowflame(PlayerbotAI* botAI) { return new CastShadowflameAction(botAI); } + static Action* immolation_aura(PlayerbotAI* botAI) { return new CastImmolationAuraAction(botAI); } + static Action* chaos_bolt(PlayerbotAI* botAI) { return new CastChaosBoltAction(botAI); } + static Action* soulshatter(PlayerbotAI* botAI) { return new CastSoulshatterAction(botAI);} + static Action* searing_pain(PlayerbotAI* botAI) { return new CastSearingPainAction(botAI); } + static Action* shadow_ward(PlayerbotAI* botAI) { return new CastShadowWardAction(botAI); } }; WarlockAiObjectContext::WarlockAiObjectContext(PlayerbotAI* botAI) : AiObjectContext(botAI) diff --git a/src/strategy/warlock/WarlockTriggers.cpp b/src/strategy/warlock/WarlockTriggers.cpp index 1ba37ca2..1a0d0e77 100644 --- a/src/strategy/warlock/WarlockTriggers.cpp +++ b/src/strategy/warlock/WarlockTriggers.cpp @@ -4,10 +4,34 @@ */ #include "WarlockTriggers.h" - #include "GenericTriggers.h" #include "Playerbots.h" +bool SpellstoneTrigger::IsActive() { return BuffTrigger::IsActive() && AI_VALUE2(uint32, "item count", getName()) > 0; } + +bool FirestoneTrigger::IsActive() { return BuffTrigger::IsActive() && AI_VALUE2(uint32, "item count", getName()) > 0; } + +bool WarlockConjuredItemTrigger::IsActive() +{ + return ItemCountTrigger::IsActive() && AI_VALUE2(uint32, "item count", "soul shard") > 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(); @@ -15,30 +39,18 @@ bool DemonArmorTrigger::IsActive() !botAI->HasAura("fel armor", target); } -bool SpellstoneTrigger::IsActive() { return BuffTrigger::IsActive() && AI_VALUE2(uint32, "item count", getName()) > 0; } - -bool WarlockConjuredItemTrigger::IsActive() +bool SoulLinkTrigger::IsActive() { - return ItemCountTrigger::IsActive() && AI_VALUE2(uint32, "item count", "soul shard") > 0; + Unit* target = GetTarget(); + return !botAI->HasAura("soul link", target); } -bool ImmolateOnAttackerTrigger::IsActive() +bool DemonicEmpowermentTrigger::IsActive() { - return DebuffOnAttackerTrigger::IsActive() && - // !botAI->HasAura("immolate", GetTarget(), false, true) && - !botAI->HasAura("unstable affliction", GetTarget(), false, true); -} - -bool UnstableAfflictionTrigger::IsActive() -{ - return DebuffTrigger::IsActive() && !botAI->HasAura("immolate", GetTarget(), false, true); - // !botAI->HasAura("unstable affliction", GetTarget(), false, true); -} - -bool UnstableAfflictionOnAttackerTrigger::IsActive() -{ - return DebuffOnAttackerTrigger::IsActive() && !botAI->HasAura("immolate", GetTarget(), false, true); - // !botAI->HasAura("unstable affliction", GetTarget(), false, true); + Pet* pet = bot->GetPet(); + if (!pet) + return false; + return !botAI->HasAura("demonic empowerment", pet); } bool DecimationTrigger::IsActive() @@ -47,11 +59,51 @@ bool DecimationTrigger::IsActive() return aura && aura->GetDuration() > 3000; } +// Checks if the bot's mana is below 85% and health is above a low health threshold +bool LifeTapTrigger::IsActive() +{ + if (AI_VALUE2(uint8, "health", "self target") <= sPlayerbotAIConfig->lowHealth) + return false; + + if (!AI_VALUE2(bool, "has mana", "self target")) + return false; + if (AI_VALUE2(uint8, "mana", "self target") >= 85) + return false; + + return true; +} + +// Checks if the Life Tap Glyph buff is active bool LifeTapGlyphBuffTrigger::IsActive() { - // Check life tap glyph first if (!botAI->HasAura(63320, bot)) return false; return BuffTrigger::IsActive(); -} \ No newline at end of file +} + +// Checks if the target has a conflicting debuff that is equal to Curse of the Elements +bool CurseOfTheElementsTrigger::IsActive() +{ + Unit* target = GetTarget(); + if (!target || !target->IsAlive() || !target->IsInWorld()) + return false; + + // List of all spell IDs for Ebon Plague, Earth and Moon, and Curse of the Elements + static const uint32 CurseOfTheElementsExclusiveDebuffs[] = {// Ebon Plague + 51735, 51734, 51726, + // Earth and Moon + 48511, 48513, 48514, + // Curse of the Elements + 1490, 11721, 11722, 27228, 47865}; + + // Check if target has any of the exclusive debuffs + for (uint32 spellId : CurseOfTheElementsExclusiveDebuffs) + { + if (target->HasAura(spellId)) + return false; + } + + // Use default BuffTrigger logic for the rest (only trigger if debuff is missing or expiring) + return BuffTrigger::IsActive(); +} diff --git a/src/strategy/warlock/WarlockTriggers.h b/src/strategy/warlock/WarlockTriggers.h index 6c908ed7..da12c697 100644 --- a/src/strategy/warlock/WarlockTriggers.h +++ b/src/strategy/warlock/WarlockTriggers.h @@ -8,9 +8,13 @@ #include "GenericTriggers.h" #include "PlayerbotAI.h" +#include "Playerbots.h" +#include "CureTriggers.h" class PlayerbotAI; +// Buff and Out of Combat Triggers + class DemonArmorTrigger : public BuffTrigger { public: @@ -19,90 +23,50 @@ public: bool IsActive() override; }; +class SoulLinkTrigger : public BuffTrigger +{ +public: + SoulLinkTrigger(PlayerbotAI* botAI) : BuffTrigger(botAI, "soul link") {} + bool IsActive() override; +}; + +class FirestoneTrigger : public BuffTrigger +{ +public: + FirestoneTrigger(PlayerbotAI* botAI) : BuffTrigger(botAI, "firestone") {} + bool IsActive() override; +}; + class SpellstoneTrigger : public BuffTrigger { public: SpellstoneTrigger(PlayerbotAI* botAI) : BuffTrigger(botAI, "spellstone") {} - bool IsActive() override; }; -// DEBUFF_CHECKISOWNER_TRIGGER(CurseOfAgonyTrigger, "curse of agony"); -class CurseOfAgonyTrigger : public DebuffTrigger +class HasSoulstoneTrigger : public Trigger { public: - CurseOfAgonyTrigger(PlayerbotAI* botAI) : DebuffTrigger(botAI, "curse of agony", 1, true, 20.0f) {} + HasSoulstoneTrigger(PlayerbotAI* botAI) : Trigger(botAI, "no soulstone") {} + bool IsActive() override { return AI_VALUE2(uint32, "item count", "soulstone") == 0; } }; -class CorruptionTrigger : public DebuffTrigger +class SoulstoneTrigger : public Trigger { public: - CorruptionTrigger(PlayerbotAI* botAI) : DebuffTrigger(botAI, "corruption", 1, true) {} + SoulstoneTrigger(PlayerbotAI* botAI) : Trigger(botAI, "soulstone") {} + bool IsActive() override { - return DebuffTrigger::IsActive() && !botAI->HasAura("seed of corruption", GetTarget(), false, true); + // Just check if we have a soulstone item available + return AI_VALUE2(uint32, "item count", "soulstone") > 0; } }; -DEBUFF_CHECKISOWNER_TRIGGER(SiphonLifeTrigger, "siphon life"); - -class CorruptionOnAttackerTrigger : public DebuffOnAttackerTrigger -{ -public: - CorruptionOnAttackerTrigger(PlayerbotAI* botAI) : DebuffOnAttackerTrigger(botAI, "corruption", true) {} - bool IsActive() override - { - return DebuffOnAttackerTrigger::IsActive() && !botAI->HasAura("seed of corruption", GetTarget(), false, true); - } -}; - -class CastCurseOfAgonyOnAttackerTrigger : public DebuffOnAttackerTrigger -{ -public: - CastCurseOfAgonyOnAttackerTrigger(PlayerbotAI* botAI) - : DebuffOnAttackerTrigger(botAI, "curse of agony", true, 20.0f) - { - } -}; - -class SiphonLifeOnAttackerTrigger : public DebuffOnAttackerTrigger -{ -public: - SiphonLifeOnAttackerTrigger(PlayerbotAI* botAI) : DebuffOnAttackerTrigger(botAI, "siphon life") {} -}; - -DEBUFF_CHECKISOWNER_TRIGGER(ImmolateTrigger, "immolate"); - -class ImmolateOnAttackerTrigger : public DebuffOnAttackerTrigger -{ -public: - ImmolateOnAttackerTrigger(PlayerbotAI* ai) : DebuffOnAttackerTrigger(ai, "immolate") {} - virtual bool IsActive(); -}; - -class ShadowTranceTrigger : public HasAuraTrigger -{ -public: - ShadowTranceTrigger(PlayerbotAI* botAI) : HasAuraTrigger(botAI, "shadow trance") {} -}; - -class BacklashTrigger : public HasAuraTrigger -{ -public: - BacklashTrigger(PlayerbotAI* botAI) : HasAuraTrigger(botAI, "backlash") {} -}; - -class BanishTrigger : public HasCcTargetTrigger -{ -public: - BanishTrigger(PlayerbotAI* botAI) : HasCcTargetTrigger(botAI, "banish") {} -}; - class WarlockConjuredItemTrigger : public ItemCountTrigger { public: WarlockConjuredItemTrigger(PlayerbotAI* botAI, std::string const item) : ItemCountTrigger(botAI, item, 1) {} - bool IsActive() override; }; @@ -124,30 +88,114 @@ public: HasHealthstoneTrigger(PlayerbotAI* botAI) : WarlockConjuredItemTrigger(botAI, "healthstone") {} }; +// CC and Pet Triggers + +class BanishTrigger : public HasCcTargetTrigger +{ +public: + BanishTrigger(PlayerbotAI* botAI) : HasCcTargetTrigger(botAI, "banish") {} + bool IsActive() override; +}; + class FearTrigger : public HasCcTargetTrigger { public: FearTrigger(PlayerbotAI* botAI) : HasCcTargetTrigger(botAI, "fear") {} -}; - -class AmplifyCurseTrigger : public BuffTrigger -{ -public: - AmplifyCurseTrigger(PlayerbotAI* botAI) : BuffTrigger(botAI, "amplify curse") {} -}; - -class UnstableAfflictionTrigger : public DebuffTrigger // SpellTrigger -{ -public: - UnstableAfflictionTrigger(PlayerbotAI* ai) : DebuffTrigger(ai, "unstable affliction", 1, true) {} bool IsActive() override; }; +class SpellLockInterruptSpellTrigger : public InterruptSpellTrigger +{ +public: + SpellLockInterruptSpellTrigger(PlayerbotAI* botAI) : InterruptSpellTrigger(botAI, "spell lock") {} +}; + +class DevourMagicPurgeTrigger : public TargetAuraDispelTrigger +{ +public: + DevourMagicPurgeTrigger(PlayerbotAI* botAI) : TargetAuraDispelTrigger(botAI, "devour magic", DISPEL_MAGIC) {} +}; + +class DevourMagicCleanseTrigger : public PartyMemberNeedCureTrigger +{ +public: + DevourMagicCleanseTrigger(PlayerbotAI* botAI) : PartyMemberNeedCureTrigger(botAI, "devour magic", DISPEL_MAGIC) {} +}; + +// DoT/Debuff Triggers + +class CurseOfAgonyTrigger : public DebuffTrigger +{ +public: + CurseOfAgonyTrigger(PlayerbotAI* botAI) : DebuffTrigger(botAI, "curse of agony", 1, true, 0.5f) {} + bool IsActive() override + { + if (botAI->HasStrategy( + "curse of elements", BOT_STATE_COMBAT)) // If Curse of the Elements strategy is active, do not cast Curse of Agony + return false; + return BuffTrigger::IsActive(); + } +}; + +class CurseOfAgonyOnAttackerTrigger : public DebuffOnAttackerTrigger +{ +public: + CurseOfAgonyOnAttackerTrigger(PlayerbotAI* botAI) : DebuffOnAttackerTrigger(botAI, "curse of agony", true) {} + bool IsActive() override + { + if (botAI->HasStrategy( + "curse of elements", BOT_STATE_COMBAT)) // If Curse of the Elements strategy is active, do not cast Curse of Agony + return false; + return BuffTrigger::IsActive(); + } +}; + +class CorruptionTrigger : public DebuffTrigger +{ +public: + CorruptionTrigger(PlayerbotAI* botAI) : DebuffTrigger(botAI, "corruption", 1, true, 0.5f) {} + bool IsActive() override + { + return BuffTrigger::IsActive() && !botAI->HasAura("seed of corruption", GetTarget(), false, true); + } +}; + +class CorruptionOnAttackerTrigger : public DebuffOnAttackerTrigger +{ +public: + CorruptionOnAttackerTrigger(PlayerbotAI* botAI) : DebuffOnAttackerTrigger(botAI, "corruption", true) {} + bool IsActive() override + { + return BuffTrigger::IsActive() && !botAI->HasAura("seed of corruption", GetTarget(), false, true); + } +}; + +class ImmolateTrigger : public DebuffTrigger +{ +public: + ImmolateTrigger(PlayerbotAI* botAI) : DebuffTrigger(botAI, "immolate", 1, true, 0.5f) {} + bool IsActive() override { return BuffTrigger::IsActive(); } +}; + +class ImmolateOnAttackerTrigger : public DebuffOnAttackerTrigger +{ +public: + ImmolateOnAttackerTrigger(PlayerbotAI* ai) : DebuffOnAttackerTrigger(ai, "immolate", true) {} + bool IsActive() override { return BuffTrigger::IsActive(); } +}; + +class UnstableAfflictionTrigger : public DebuffTrigger +{ +public: + UnstableAfflictionTrigger(PlayerbotAI* ai) : DebuffTrigger(ai, "unstable affliction", 1, true, 0.5f) {} + bool IsActive() override { return BuffTrigger::IsActive(); } +}; + class UnstableAfflictionOnAttackerTrigger : public DebuffOnAttackerTrigger { public: UnstableAfflictionOnAttackerTrigger(PlayerbotAI* ai) : DebuffOnAttackerTrigger(ai, "unstable affliction", true) {} - bool IsActive() override; + bool IsActive() override { return BuffTrigger::IsActive(); } }; class HauntTrigger : public DebuffTrigger @@ -156,10 +204,20 @@ public: HauntTrigger(PlayerbotAI* ai) : DebuffTrigger(ai, "haunt", 1, true, 0) {} }; -class DecimationTrigger : public HasAuraTrigger +class CurseOfTheElementsTrigger : public DebuffTrigger { public: - DecimationTrigger(PlayerbotAI* ai) : HasAuraTrigger(ai, "decimation") {} + CurseOfTheElementsTrigger(PlayerbotAI* botAI) + : DebuffTrigger(botAI, "curse of the elements", 1, true, 0.5f) {} + bool IsActive() override; +}; + +// Proc/Cooldown Triggers + +class LifeTapTrigger : public Trigger +{ +public: + LifeTapTrigger(PlayerbotAI* ai) : Trigger(ai, "life tap") {} bool IsActive() override; }; @@ -170,15 +228,47 @@ public: bool IsActive() override; }; -class MoltenCoreTrigger : public HasAuraTrigger -{ -public: - MoltenCoreTrigger(PlayerbotAI* ai) : HasAuraTrigger(ai, "molten core") {} -}; - class MetamorphosisTrigger : public BoostTrigger { public: MetamorphosisTrigger(PlayerbotAI* ai) : BoostTrigger(ai, "metamorphosis") {} }; + +class DemonicEmpowermentTrigger : public BuffTrigger +{ +public: + DemonicEmpowermentTrigger(PlayerbotAI* ai) : BuffTrigger(ai, "demonic empowerment") {} + bool IsActive() override; +}; + +class ImmolationAuraActiveTrigger : public HasAuraTrigger +{ +public: + ImmolationAuraActiveTrigger(PlayerbotAI* ai) : HasAuraTrigger(ai, "immolation aura") {} +}; + +class ShadowTranceTrigger : public HasAuraTrigger +{ +public: + ShadowTranceTrigger(PlayerbotAI* botAI) : HasAuraTrigger(botAI, "shadow trance") {} +}; + +class BacklashTrigger : public HasAuraTrigger +{ +public: + BacklashTrigger(PlayerbotAI* botAI) : HasAuraTrigger(botAI, "backlash") {} +}; + +class DecimationTrigger : public HasAuraTrigger +{ +public: + DecimationTrigger(PlayerbotAI* ai) : HasAuraTrigger(ai, "decimation") {} + bool IsActive() override; +}; + +class MoltenCoreTrigger : public HasAuraTrigger +{ +public: + MoltenCoreTrigger(PlayerbotAI* ai) : HasAuraTrigger(ai, "molten core") {} +}; #endif