diff --git a/conf/playerbots.conf.dist b/conf/playerbots.conf.dist index 162c3deb..e7233c2a 100644 --- a/conf/playerbots.conf.dist +++ b/conf/playerbots.conf.dist @@ -1440,7 +1440,7 @@ AiPlayerbot.PremadeSpecName.9.0 = affli pve AiPlayerbot.PremadeSpecGlyph.9.0 = 45785,43390,50077,43394,43393,45779 AiPlayerbot.PremadeSpecLink.9.0.60 = 2350022001113510053500131151 AiPlayerbot.PremadeSpecLink.9.0.70 = 2350022001113510053500131151--55 -AiPlayerbot.PremadeSpecLink.9.0.80 = 2350022001113510253500331151--5500000501 +AiPlayerbot.PremadeSpecLink.9.0.80 = 2350022001123510253500331151--55000005 AiPlayerbot.PremadeSpecName.9.1 = demo pve AiPlayerbot.PremadeSpecGlyph.9.1 = 45785,43390,50077,43394,43393,42459 AiPlayerbot.PremadeSpecLink.9.1.60 = -003203301135112530135201051 diff --git a/src/AiFactory.cpp b/src/AiFactory.cpp index a40c7765..b6693095 100644 --- a/src/AiFactory.cpp +++ b/src/AiFactory.cpp @@ -386,9 +386,9 @@ void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const fa break; case CLASS_WARLOCK: if (tab == 0) // Affliction - engine->addStrategiesNoInit("affli", "affli aoe", nullptr); + engine->addStrategiesNoInit("affli", "affli aoe", "curse of agony", nullptr); else if (tab == 1) // Demonology - engine->addStrategiesNoInit("demo", "demo aoe", "meta melee", nullptr); + engine->addStrategiesNoInit("demo", "demo aoe", "curse of agony", "meta melee", nullptr); else if (tab == 2) // Destruction engine->addStrategiesNoInit("destro", "destro aoe", "curse of elements", nullptr); @@ -596,17 +596,17 @@ void AiFactory::AddDefaultNonCombatStrategies(Player* player, PlayerbotAI* const case CLASS_WARLOCK: if (tab == WARLOCK_TAB_AFFLICATION) { - nonCombatEngine->addStrategiesNoInit("felhunter", nullptr); + nonCombatEngine->addStrategiesNoInit("felhunter", "spellstone", nullptr); } else if (tab == WARLOCK_TAB_DEMONOLOGY) { - nonCombatEngine->addStrategiesNoInit("felguard", nullptr); + nonCombatEngine->addStrategiesNoInit("felguard", "spellstone", nullptr); } else if (tab == WARLOCK_TAB_DESTRUCTION) { - nonCombatEngine->addStrategiesNoInit("imp", nullptr); + nonCombatEngine->addStrategiesNoInit("imp", "firestone", nullptr); } - nonCombatEngine->addStrategiesNoInit("dps assist", nullptr); + nonCombatEngine->addStrategiesNoInit("dps assist", "ss self", nullptr); break; case CLASS_DEATH_KNIGHT: if (tab == 0) diff --git a/src/strategy/warlock/AfflictionWarlockStrategy.cpp b/src/strategy/warlock/AfflictionWarlockStrategy.cpp index 968bb445..0561febc 100644 --- a/src/strategy/warlock/AfflictionWarlockStrategy.cpp +++ b/src/strategy/warlock/AfflictionWarlockStrategy.cpp @@ -16,8 +16,6 @@ public: 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; @@ -33,8 +31,6 @@ private: 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); } @@ -55,9 +51,8 @@ AfflictionWarlockStrategy::AfflictionWarlockStrategy(PlayerbotAI* botAI) : Gener 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("corruption", 5.5f), + new NextAction("unstable affliction", 5.4f), new NextAction("haunt", 5.3f), new NextAction("shadow bolt", 5.2f), new NextAction("shoot", 5.0f), nullptr); @@ -71,10 +66,8 @@ void AfflictionWarlockStrategy::InitTriggers(std::vector& 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 @@ -82,7 +75,7 @@ void AfflictionWarlockStrategy::InitTriggers(std::vector& triggers 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 glyph buff", NextAction::array(0, new NextAction("life tap", 29.5f), nullptr))); triggers.push_back(new TriggerNode("life tap", NextAction::array(0, new NextAction("life tap", 5.1f), nullptr))); triggers.push_back(new TriggerNode("enemy too close for spell", NextAction::array(0, new NextAction("flee", 39.0f), nullptr))); diff --git a/src/strategy/warlock/DemonologyWarlockStrategy.cpp b/src/strategy/warlock/DemonologyWarlockStrategy.cpp index 1062d015..25a80f67 100644 --- a/src/strategy/warlock/DemonologyWarlockStrategy.cpp +++ b/src/strategy/warlock/DemonologyWarlockStrategy.cpp @@ -14,8 +14,6 @@ public: { 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; @@ -35,8 +33,6 @@ public: 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); } @@ -63,8 +59,7 @@ DemonologyWarlockStrategy::DemonologyWarlockStrategy(PlayerbotAI* botAI) : Gener NextAction** DemonologyWarlockStrategy::getDefaultActions() { return NextAction::array(0, - new NextAction("corruption", 5.6f), - new NextAction("curse of agony", 5.5f), + new NextAction("corruption", 5.5f), new NextAction("immolate", 5.4f), new NextAction("shadow bolt", 5.3f), new NextAction("incinerate", 5.2f), @@ -81,11 +76,9 @@ void DemonologyWarlockStrategy::InitTriggers(std::vector& triggers 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("corruption on attacker", NextAction::array(0, new NextAction("corruption 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("corruption", NextAction::array(0, new NextAction("corruption", 18.0f), nullptr))); triggers.push_back(new TriggerNode("immolate", NextAction::array(0, new NextAction("immolate", 17.5f), nullptr))); // Procs @@ -93,7 +86,7 @@ void DemonologyWarlockStrategy::InitTriggers(std::vector& triggers 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 glyph buff", NextAction::array(0, new NextAction("life tap", 29.5f), nullptr))); triggers.push_back(new TriggerNode("life tap", NextAction::array(0, new NextAction("life tap", 5.1f), nullptr))); triggers.push_back(new TriggerNode("meta melee flee check", NextAction::array(0, new NextAction("flee", 39.0f), nullptr))); diff --git a/src/strategy/warlock/DestructionWarlockStrategy.cpp b/src/strategy/warlock/DestructionWarlockStrategy.cpp index 2ecc6467..de9efeb8 100644 --- a/src/strategy/warlock/DestructionWarlockStrategy.cpp +++ b/src/strategy/warlock/DestructionWarlockStrategy.cpp @@ -18,8 +18,6 @@ public: 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; @@ -37,8 +35,6 @@ private: 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); } @@ -59,12 +55,11 @@ DestructionWarlockStrategy::DestructionWarlockStrategy(PlayerbotAI* botAI) : Gen 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("immolate", 5.9f), + new NextAction("conflagrate", 5.8f), + new NextAction("chaos bolt", 5.7f), + new NextAction("incinerate", 5.6f), + new NextAction("corruption", 5.3f), // Note: Corruption and Shadow Bolt won't be used after the character learns Incinerate at level 64 new NextAction("shadow bolt", 5.2f), new NextAction("shoot", 5.0f), nullptr); } @@ -79,17 +74,15 @@ void DestructionWarlockStrategy::InitTriggers(std::vector& trigger 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))); + // Note: Corruption 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.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 glyph buff", NextAction::array(0, new NextAction("life tap", 29.5f), nullptr))); triggers.push_back(new TriggerNode("life tap", NextAction::array(0, new NextAction("life tap", 5.1f), nullptr))); triggers.push_back(new TriggerNode("enemy too close for spell", NextAction::array(0, new NextAction("flee", 39.0f), nullptr))); diff --git a/src/strategy/warlock/GenericWarlockNonCombatStrategy.cpp b/src/strategy/warlock/GenericWarlockNonCombatStrategy.cpp index f99c06c5..88423d90 100644 --- a/src/strategy/warlock/GenericWarlockNonCombatStrategy.cpp +++ b/src/strategy/warlock/GenericWarlockNonCombatStrategy.cpp @@ -22,7 +22,7 @@ public: // 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 + // Pets are summoned based on the non-combat strategy you have active, the warlock's level, and if they have a soul shard available private: static ActionNode* fel_armor([[maybe_unused]] PlayerbotAI* botAI) @@ -80,27 +80,12 @@ void GenericWarlockNonCombatStrategy::InitTriggers(std::vector& tr 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("no soul shard", NextAction::array(0, new NextAction("create soul shard", 29.5f), 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))); - - Player* bot = botAI->GetBot(); - int tab = AiFactory::GetPlayerSpecTab(bot); - - // Firestone/Spellstone triggers - 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 @@ -177,8 +162,7 @@ SoulstoneMasterStrategy::SoulstoneMasterStrategy(PlayerbotAI* ai) : NonCombatStr void SoulstoneMasterStrategy::InitTriggers(std::vector& triggers) { - triggers.push_back( - new TriggerNode("soulstone", NextAction::array(0, new NextAction("soulstone master", 24.0f), NULL))); + 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 @@ -189,8 +173,7 @@ SoulstoneTankStrategy::SoulstoneTankStrategy(PlayerbotAI* ai) : NonCombatStrateg void SoulstoneTankStrategy::InitTriggers(std::vector& triggers) { - triggers.push_back( - new TriggerNode("soulstone", NextAction::array(0, new NextAction("soulstone tank", 24.0f), NULL))); + 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 @@ -201,6 +184,29 @@ SoulstoneHealerStrategy::SoulstoneHealerStrategy(PlayerbotAI* ai) : NonCombatStr void SoulstoneHealerStrategy::InitTriggers(std::vector& triggers) { - triggers.push_back( - new TriggerNode("soulstone", NextAction::array(0, new NextAction("soulstone healer", 24.0f), NULL))); + triggers.push_back(new TriggerNode("soulstone", NextAction::array(0, new NextAction("soulstone healer", 24.0f), NULL))); +} + +// Non-combat strategy for using Spellstone +// Enabled by default for Affliction and Demonology specs +// To enable, type "nc +spellstone" +// To disable, type "nc -spellstone" +UseSpellstoneStrategy::UseSpellstoneStrategy(PlayerbotAI* ai) : NonCombatStrategy(ai) {} + +void UseSpellstoneStrategy::InitTriggers(std::vector& triggers) +{ + 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 using Firestone +// Enabled by default for the Destruction spec +// To enable, type "nc +firestone" +// To disable, type "nc -firestone" +UseFirestoneStrategy::UseFirestoneStrategy(PlayerbotAI* ai) : NonCombatStrategy(ai) {} + +void UseFirestoneStrategy::InitTriggers(std::vector& triggers) +{ + 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))); } diff --git a/src/strategy/warlock/GenericWarlockNonCombatStrategy.h b/src/strategy/warlock/GenericWarlockNonCombatStrategy.h index ef226c50..48f69f14 100644 --- a/src/strategy/warlock/GenericWarlockNonCombatStrategy.h +++ b/src/strategy/warlock/GenericWarlockNonCombatStrategy.h @@ -109,4 +109,24 @@ public: void InitTriggers(std::vector& triggers) override; }; +class UseSpellstoneStrategy : public NonCombatStrategy +{ +public: + UseSpellstoneStrategy(PlayerbotAI* ai); + virtual std::string const getName() override { return "spellstone"; } + +public: + void InitTriggers(std::vector& triggers) override; +}; + +class UseFirestoneStrategy : public NonCombatStrategy +{ +public: + UseFirestoneStrategy(PlayerbotAI* ai); + virtual std::string const getName() override { return "firestone"; } + +public: + void InitTriggers(std::vector& triggers) override; +}; + #endif diff --git a/src/strategy/warlock/GenericWarlockStrategy.cpp b/src/strategy/warlock/GenericWarlockStrategy.cpp index 7f413685..1eb5bdbb 100644 --- a/src/strategy/warlock/GenericWarlockStrategy.cpp +++ b/src/strategy/warlock/GenericWarlockStrategy.cpp @@ -38,11 +38,12 @@ void GenericWarlockStrategy::InitTriggers(std::vector& triggers) { CombatStrategy::InitTriggers(triggers); - triggers.push_back(new TriggerNode("low mana", NextAction::array(0, new NextAction("life tap", ACTION_EMERGENCY + 5), nullptr))); + triggers.push_back(new TriggerNode("low mana", NextAction::array(0, new NextAction("life tap", 95.0f), 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))); + triggers.push_back(new TriggerNode("spell lock", NextAction::array(0, new NextAction("spell lock", 40.0f), nullptr))); + triggers.push_back(new TriggerNode("no soul shard", NextAction::array(0, new NextAction("create soul shard", 60.0f), nullptr))); + triggers.push_back(new TriggerNode("devour magic purge", NextAction::array(0, new NextAction("devour magic purge", 50.0f), nullptr))); + triggers.push_back(new TriggerNode("devour magic cleanse", NextAction::array(0, new NextAction("devour magic cleanse", 50.0f), nullptr))); } void WarlockBoostStrategy::InitTriggers(std::vector& triggers) @@ -61,13 +62,57 @@ void WarlockCcStrategy::InitTriggers(std::vector& triggers) triggers.push_back(new TriggerNode("fear", NextAction::array(0, new NextAction("fear on cc", 32.0f), nullptr))); } +// Combat strategy for using Curse of Agony +// Enabled by default for the Affliction spec +// To enable, type "co +curse of agony" +// To disable, type "co -curse of agony" +void WarlockCurseOfAgonyStrategy::InitTriggers(std::vector& triggers) +{ + 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("curse of agony", NextAction::array(0, new NextAction("curse of agony", 17.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))); + triggers.push_back(new TriggerNode("curse of the elements", NextAction::array(0, new NextAction("curse of the elements", 29.0f), nullptr))); +} + +// Combat strategy for using Curse of Doom +// Disabled by default +// To enable, type "co +curse of doom" +// To disable, type "co -curse of doom" +void WarlockCurseOfDoomStrategy::InitTriggers(std::vector& triggers) +{ + triggers.push_back(new TriggerNode("curse of doom", NextAction::array(0, new NextAction("curse of doom", 29.0f), nullptr))); +} + +// Combat strategy for using Curse of Exhaustion +// Disabled by default +// To enable, type "co +curse of exhaustion" +// To disable, type "co -curse of exhaustion" +void WarlockCurseOfExhaustionStrategy::InitTriggers(std::vector& triggers) +{ + triggers.push_back(new TriggerNode("curse of exhaustion", NextAction::array(0, new NextAction("curse of exhaustion", 29.0f), nullptr))); +} + +// Combat strategy for using Curse of Tongues +// Disabled by default +// To enable, type "co +curse of tongues" +// To disable, type "co -curse of tongues" +void WarlockCurseOfTonguesStrategy::InitTriggers(std::vector& triggers) +{ + triggers.push_back(new TriggerNode("curse of tongues", NextAction::array(0, new NextAction("curse of tongues", 29.0f), nullptr))); +} + +// Combat strategy for using Curse of Weakness +// Disabled by default +// To enable, type "co +curse of weakness" +// To disable, type "co -curse of weakness" +void WarlockCurseOfWeaknessStrategy::InitTriggers(std::vector& triggers) +{ + triggers.push_back(new TriggerNode("curse of weakness", NextAction::array(0, new NextAction("curse of weakness", 29.0f), nullptr))); } diff --git a/src/strategy/warlock/GenericWarlockStrategy.h b/src/strategy/warlock/GenericWarlockStrategy.h index 6b134b60..a1ab80ca 100644 --- a/src/strategy/warlock/GenericWarlockStrategy.h +++ b/src/strategy/warlock/GenericWarlockStrategy.h @@ -48,6 +48,15 @@ public: void InitTriggers(std::vector& triggers) override; }; +class WarlockCurseOfAgonyStrategy : public Strategy +{ +public: + WarlockCurseOfAgonyStrategy(PlayerbotAI* botAI) : Strategy(botAI) {} + + std::string const getName() override { return "curse of agony"; } + void InitTriggers(std::vector& triggers) override; +}; + class WarlockCurseOfTheElementsStrategy : public Strategy { public: @@ -57,4 +66,40 @@ public: void InitTriggers(std::vector& triggers) override; }; +class WarlockCurseOfDoomStrategy : public Strategy +{ +public: + WarlockCurseOfDoomStrategy(PlayerbotAI* botAI) : Strategy(botAI) {} + + std::string const getName() override { return "curse of doom"; } + void InitTriggers(std::vector& triggers) override; +}; + +class WarlockCurseOfExhaustionStrategy : public Strategy +{ +public: + WarlockCurseOfExhaustionStrategy(PlayerbotAI* botAI) : Strategy(botAI) {} + + std::string const getName() override { return "curse of exhaustion"; } + void InitTriggers(std::vector& triggers) override; +}; + +class WarlockCurseOfTonguesStrategy : public Strategy +{ +public: + WarlockCurseOfTonguesStrategy(PlayerbotAI* botAI) : Strategy(botAI) {} + + std::string const getName() override { return "curse of tongues"; } + void InitTriggers(std::vector& triggers) override; +}; + +class WarlockCurseOfWeaknessStrategy : public Strategy +{ +public: + WarlockCurseOfWeaknessStrategy(PlayerbotAI* botAI) : Strategy(botAI) {} + + std::string const getName() override { return "curse of weakness"; } + void InitTriggers(std::vector& triggers) override; +}; + #endif diff --git a/src/strategy/warlock/WarlockActions.cpp b/src/strategy/warlock/WarlockActions.cpp index 3bb1724b..50cb9e83 100644 --- a/src/strategy/warlock/WarlockActions.cpp +++ b/src/strategy/warlock/WarlockActions.cpp @@ -113,6 +113,28 @@ bool CastSoulshatterAction::isUseful() return true; } +// Checks if the bot has enough bag space to create a soul shard, then does so +bool CreateSoulShardAction::Execute(Event event) +{ + Player* bot = botAI->GetBot(); + if (!bot) + return false; + + // Soul Shard item ID is 6265 + uint32 soulShardId = 6265; + ItemPosCountVec dest; + uint32 count = 1; + if (bot->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, soulShardId, count) == EQUIP_ERR_OK) + { + bot->StoreNewItem(dest, soulShardId, true, Item::GenerateItemRandomPropertyId(soulShardId)); + SQLTransaction trans = CharacterDatabase.BeginTransaction(); + bot->SaveInventoryAndGoldToDB(trans); + CharacterDatabase.CommitTransaction(trans); + return true; + } + return false; +} + // Checks if the target has a soulstone aura static bool HasSoulstoneAura(Unit* unit) { diff --git a/src/strategy/warlock/WarlockActions.h b/src/strategy/warlock/WarlockActions.h index ad037d4d..d87358f7 100644 --- a/src/strategy/warlock/WarlockActions.h +++ b/src/strategy/warlock/WarlockActions.h @@ -40,6 +40,13 @@ public: std::string const GetTargetName() override { return "pet target"; } }; +class CreateSoulShardAction : public Action +{ +public: + CreateSoulShardAction(PlayerbotAI* botAI) : Action(botAI, "create soul shard") {} + bool Execute(Event event) override; +}; + class CastCreateHealthstoneAction : public CastBuffSpellAction { public: @@ -190,23 +197,6 @@ public: 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: @@ -228,38 +218,7 @@ public: 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(); - } -}; +// DoT/Curse Spells class CastCorruptionAction : public CastDebuffSpellAction { @@ -330,6 +289,86 @@ public: } }; +class CastCurseOfAgonyAction : public CastDebuffSpellAction +{ +public: + CastCurseOfAgonyAction(PlayerbotAI* botAI) : CastDebuffSpellAction(botAI, "curse of agony", true) {} + bool isUseful() override + { + // Bypass TTL check + return CastAuraSpellAction::isUseful(); + } +}; + +class CastCurseOfAgonyOnAttackerAction : public CastDebuffSpellOnAttackerAction +{ +public: + CastCurseOfAgonyOnAttackerAction(PlayerbotAI* botAI) + : CastDebuffSpellOnAttackerAction(botAI, "curse of agony", true) + { + } + bool isUseful() override + { + // Bypass TTL check + return CastAuraSpellAction::isUseful(); + } +}; + +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 CastCurseOfDoomAction : public CastDebuffSpellAction +{ +public: + CastCurseOfDoomAction(PlayerbotAI* botAI) : CastDebuffSpellAction(botAI, "curse of doom", true, 0) {} + bool isUseful() override + { + // Bypass TTL check + return CastAuraSpellAction::isUseful(); + } +}; + +class CastCurseOfExhaustionAction : public CastDebuffSpellAction +{ +public: + CastCurseOfExhaustionAction(PlayerbotAI* botAI) : CastDebuffSpellAction(botAI, "curse of exhaustion") {} + bool isUseful() override + { + // Bypass TTL check + return CastAuraSpellAction::isUseful(); + } +}; + +class CastCurseOfTonguesAction : public CastDebuffSpellAction +{ +public: + CastCurseOfTonguesAction(PlayerbotAI* botAI) : CastDebuffSpellAction(botAI, "curse of tongues") {} + bool isUseful() override + { + // Bypass TTL check + return CastAuraSpellAction::isUseful(); + } +}; + +class CastCurseOfWeaknessAction : public CastDebuffSpellAction +{ +public: + CastCurseOfWeaknessAction(PlayerbotAI* botAI) : CastDebuffSpellAction(botAI, "curse of weakness") {} + bool isUseful() override + { + // Bypass TTL check + return CastAuraSpellAction::isUseful(); + } +}; + // Damage Spells class CastShadowBoltAction : public CastSpellAction diff --git a/src/strategy/warlock/WarlockAiObjectContext.cpp b/src/strategy/warlock/WarlockAiObjectContext.cpp index cd56398a..1fe4bf1e 100644 --- a/src/strategy/warlock/WarlockAiObjectContext.cpp +++ b/src/strategy/warlock/WarlockAiObjectContext.cpp @@ -28,34 +28,29 @@ public: creators["boost"] = &WarlockStrategyFactoryInternal::boost; creators["cc"] = &WarlockStrategyFactoryInternal::cc; creators["pet"] = &WarlockStrategyFactoryInternal::pet; + creators["spellstone"] = &WarlockStrategyFactoryInternal::spellstone; + creators["firestone"] = &WarlockStrategyFactoryInternal::firestone; creators["affli aoe"] = &WarlockStrategyFactoryInternal::affliction_aoe; creators["demo aoe"] = &WarlockStrategyFactoryInternal::demonology_aoe; creators["destro aoe"] = &WarlockStrategyFactoryInternal::destruction_aoe; creators["meta melee"] = &WarlockStrategyFactoryInternal::meta_melee_aoe; - creators["curse of elements"] = &WarlockStrategyFactoryInternal::curse_of_elements; - creators["imp"] = &WarlockStrategyFactoryInternal::imp; - creators["voidwalker"] = &WarlockStrategyFactoryInternal::voidwalker; - creators["succubus"] = &WarlockStrategyFactoryInternal::succubus; - creators["felhunter"] = &WarlockStrategyFactoryInternal::felhunter; - creators["felguard"] = &WarlockStrategyFactoryInternal::felguard; + creators["tank"] = &WarlockStrategyFactoryInternal::tank; + } private: - static Strategy* pet(PlayerbotAI* botAI) { return new WarlockPetStrategy(botAI); } static Strategy* nc(PlayerbotAI* botAI) { return new GenericWarlockNonCombatStrategy(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* pet(PlayerbotAI* botAI) { return new WarlockPetStrategy(botAI); } + static Strategy* spellstone(PlayerbotAI* botAI) { return new UseSpellstoneStrategy(botAI); } + static Strategy* firestone(PlayerbotAI* botAI) { return new UseFirestoneStrategy(botAI); } static Strategy* affliction_aoe(PlayerbotAI* botAI) { return new AfflictionWarlockAoeStrategy(botAI); } static Strategy* demonology_aoe(PlayerbotAI* botAI) { return new DemonologyWarlockAoeStrategy(botAI); } static Strategy* destruction_aoe(PlayerbotAI* botAI) { return new DestructionWarlockAoeStrategy(botAI); } static Strategy* meta_melee_aoe(PlayerbotAI* botAI) { return new MetaMeleeAoeStrategy(botAI); } - static Strategy* curse_of_elements(PlayerbotAI* botAI) { return new WarlockCurseOfTheElementsStrategy(botAI); } - 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* tank(PlayerbotAI* botAI) { return new TankWarlockStrategy(botAI); } }; class WarlockCombatStrategyFactoryInternal : public NamedObjectContext @@ -63,28 +58,46 @@ class WarlockCombatStrategyFactoryInternal : public NamedObjectContext public: WarlockCombatStrategyFactoryInternal() : NamedObjectContext(false, true) { - creators["tank"] = &WarlockCombatStrategyFactoryInternal::tank; creators["affli"] = &WarlockCombatStrategyFactoryInternal::affliction; creators["demo"] = &WarlockCombatStrategyFactoryInternal::demonology; creators["destro"] = &WarlockCombatStrategyFactoryInternal::destruction; } private: - static Strategy* tank(PlayerbotAI* botAI) { return new TankWarlockStrategy(botAI); } static Strategy* affliction(PlayerbotAI* botAI) { return new AfflictionWarlockStrategy(botAI); } static Strategy* demonology(PlayerbotAI* botAI) { return new DemonologyWarlockStrategy(botAI); } static Strategy* destruction(PlayerbotAI* botAI) { return new DestructionWarlockStrategy(botAI); } }; -class NonCombatBuffStrategyFactoryInternal : public NamedObjectContext +class WarlockPetStrategyFactoryInternal : public NamedObjectContext { public: - NonCombatBuffStrategyFactoryInternal() : NamedObjectContext(false, true) + WarlockPetStrategyFactoryInternal() : NamedObjectContext(false, true) { - creators["ss self"] = &NonCombatBuffStrategyFactoryInternal::soulstone_self; - creators["ss master"] = &NonCombatBuffStrategyFactoryInternal::soulstone_master; - creators["ss tank"] = &NonCombatBuffStrategyFactoryInternal::soulstone_tank; - creators["ss healer"] = &NonCombatBuffStrategyFactoryInternal::soulstone_healer; + creators["imp"] = &WarlockPetStrategyFactoryInternal::imp; + creators["voidwalker"] = &WarlockPetStrategyFactoryInternal::voidwalker; + creators["succubus"] = &WarlockPetStrategyFactoryInternal::succubus; + creators["felhunter"] = &WarlockPetStrategyFactoryInternal::felhunter; + creators["felguard"] = &WarlockPetStrategyFactoryInternal::felguard; + } + +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); } +}; + +class WarlockSoulstoneStrategyFactoryInternal : public NamedObjectContext +{ +public: + WarlockSoulstoneStrategyFactoryInternal() : NamedObjectContext(false, true) + { + creators["ss self"] = &WarlockSoulstoneStrategyFactoryInternal::soulstone_self; + creators["ss master"] = &WarlockSoulstoneStrategyFactoryInternal::soulstone_master; + creators["ss tank"] = &WarlockSoulstoneStrategyFactoryInternal::soulstone_tank; + creators["ss healer"] = &WarlockSoulstoneStrategyFactoryInternal::soulstone_healer; } private: @@ -94,6 +107,28 @@ private: static Strategy* soulstone_healer(PlayerbotAI* ai) { return new SoulstoneHealerStrategy(ai); } }; +class WarlockCurseStrategyFactoryInternal : public NamedObjectContext +{ +public: + WarlockCurseStrategyFactoryInternal() : NamedObjectContext(false, true) + { + creators["curse of agony"] = &WarlockCurseStrategyFactoryInternal::curse_of_agony; + creators["curse of elements"] = &WarlockCurseStrategyFactoryInternal::curse_of_elements; + creators["curse of doom"] = &WarlockCurseStrategyFactoryInternal::curse_of_doom; + creators["curse of exhaustion"] = &WarlockCurseStrategyFactoryInternal::curse_of_exhaustion; + creators["curse of tongues"] = &WarlockCurseStrategyFactoryInternal::curse_of_tongues; + creators["curse of weakness"] = &WarlockCurseStrategyFactoryInternal::curse_of_weakness; + } + +private: + static Strategy* curse_of_agony(PlayerbotAI* botAI) { return new WarlockCurseOfAgonyStrategy(botAI); } + static Strategy* curse_of_elements(PlayerbotAI* botAI) { return new WarlockCurseOfTheElementsStrategy(botAI); } + static Strategy* curse_of_doom(PlayerbotAI* botAI) { return new WarlockCurseOfDoomStrategy(botAI); } + static Strategy* curse_of_exhaustion(PlayerbotAI* botAI) { return new WarlockCurseOfExhaustionStrategy(botAI); } + static Strategy* curse_of_tongues(PlayerbotAI* botAI) { return new WarlockCurseOfTonguesStrategy(botAI); } + static Strategy* curse_of_weakness(PlayerbotAI* botAI) { return new WarlockCurseOfWeaknessStrategy(botAI); } +}; + class WarlockTriggerFactoryInternal : public NamedObjectContext { public: @@ -102,6 +137,7 @@ public: creators["shadow trance"] = &WarlockTriggerFactoryInternal::shadow_trance; creators["demon armor"] = &WarlockTriggerFactoryInternal::demon_armor; creators["soul link"] = &WarlockTriggerFactoryInternal::soul_link; + creators["no soul shard"] = &WarlockTriggerFactoryInternal::no_soul_shard; creators["no healthstone"] = &WarlockTriggerFactoryInternal::HasHealthstone; creators["no firestone"] = &WarlockTriggerFactoryInternal::HasFirestone; creators["no spellstone"] = &WarlockTriggerFactoryInternal::HasSpellstone; @@ -117,14 +153,11 @@ public: 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["immolate"] = &WarlockTriggerFactoryInternal::immolate; 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; @@ -134,12 +167,20 @@ public: creators["immolation aura active"] = &WarlockTriggerFactoryInternal::immolation_aura_active; creators["metamorphosis not active"] = &WarlockTriggerFactoryInternal::metamorphosis_not_active; creators["meta melee flee check"] = &WarlockTriggerFactoryInternal::meta_melee_flee_check; + creators["curse of agony"] = &WarlockTriggerFactoryInternal::curse_of_agony; + creators["curse of agony on attacker"] = &WarlockTriggerFactoryInternal::curse_of_agony_on_attacker; + creators["curse of the elements"] = &WarlockTriggerFactoryInternal::curse_of_the_elements; + creators["curse of doom"] = &WarlockTriggerFactoryInternal::curse_of_doom; + creators["curse of exhaustion"] = &WarlockTriggerFactoryInternal::curse_of_exhaustion; + creators["curse of tongues"] = &WarlockTriggerFactoryInternal::curse_of_tongues; + creators["curse of weakness"] = &WarlockTriggerFactoryInternal::curse_of_weakness; } private: 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* no_soul_shard(PlayerbotAI* botAI) { return new OutOfSoulShardsTrigger(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); } @@ -149,8 +190,6 @@ private: 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* curse_of_agony(PlayerbotAI* botAI) { return new CurseOfAgonyTrigger(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* fear(PlayerbotAI* botAI) { return new FearTrigger(botAI); } static Trigger* spell_lock(PlayerbotAI* botAI) { return new SpellLockInterruptSpellTrigger(botAI); } @@ -162,7 +201,6 @@ private: static Trigger* unstable_affliction(PlayerbotAI* ai) { return new UnstableAfflictionTrigger(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); } @@ -172,6 +210,13 @@ private: static Trigger* immolation_aura_active(PlayerbotAI* ai) { return new ImmolationAuraActiveTrigger(ai); } static Trigger* metamorphosis_not_active(PlayerbotAI* ai) { return new MetamorphosisNotActiveTrigger(ai); } static Trigger* meta_melee_flee_check(PlayerbotAI* ai) { return new MetaMeleeEnemyTooCloseForSpellTrigger(ai); } + static Trigger* curse_of_agony(PlayerbotAI* botAI) { return new CurseOfAgonyTrigger(botAI); } + static Trigger* curse_of_agony_on_attacker(PlayerbotAI* botAI) { return new CurseOfAgonyOnAttackerTrigger(botAI); } + static Trigger* curse_of_the_elements(PlayerbotAI* ai) { return new CurseOfTheElementsTrigger(ai); } + static Trigger* curse_of_doom(PlayerbotAI* ai) { return new CurseOfDoomTrigger(ai); } + static Trigger* curse_of_exhaustion(PlayerbotAI* ai) { return new CurseOfExhaustionTrigger(ai); } + static Trigger* curse_of_tongues(PlayerbotAI* ai) { return new CurseOfTonguesTrigger(ai); } + static Trigger* curse_of_weakness(PlayerbotAI* ai) { return new CurseOfWeaknessTrigger(ai); } }; class WarlockAiObjectContextInternal : public NamedObjectContext @@ -183,6 +228,7 @@ public: creators["demon armor"] = &WarlockAiObjectContextInternal::demon_armor; creators["demon skin"] = &WarlockAiObjectContextInternal::demon_skin; creators["soul link"] = &WarlockAiObjectContextInternal::soul_link; + creators["create soul shard"] = &WarlockAiObjectContextInternal::create_soul_shard; creators["create healthstone"] = &WarlockAiObjectContextInternal::create_healthstone; creators["create firestone"] = &WarlockAiObjectContextInternal::create_firestone; creators["create spellstone"] = &WarlockAiObjectContextInternal::create_spellstone; @@ -203,8 +249,6 @@ public: creators["immolate on attacker"] = &WarlockAiObjectContextInternal::immolate_on_attacker; creators["corruption"] = &WarlockAiObjectContextInternal::corruption; creators["corruption on attacker"] = &WarlockAiObjectContextInternal::corruption_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; @@ -225,7 +269,6 @@ public: 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; @@ -239,6 +282,13 @@ public: creators["soulshatter"] = &WarlockAiObjectContextInternal::soulshatter; creators["searing pain"] = WarlockAiObjectContextInternal::searing_pain; creators["shadow ward"] = &WarlockAiObjectContextInternal::shadow_ward; + creators["curse of agony"] = &WarlockAiObjectContextInternal::curse_of_agony; + creators["curse of agony on attacker"] = &WarlockAiObjectContextInternal::curse_of_agony_on_attacker; + creators["curse of the elements"] = &WarlockAiObjectContextInternal::curse_of_the_elements; + creators["curse of doom"] = &WarlockAiObjectContextInternal::curse_of_doom; + creators["curse of exhaustion"] = &WarlockAiObjectContextInternal::curse_of_exhaustion; + creators["curse of tongues"] = &WarlockAiObjectContextInternal::curse_of_tongues; + creators["curse of weakness"] = &WarlockAiObjectContextInternal::curse_of_weakness; } private: @@ -250,6 +300,7 @@ private: 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_soul_shard(PlayerbotAI* botAI) { return new CreateSoulShardAction(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); } @@ -268,8 +319,6 @@ private: 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* curse_of_agony(PlayerbotAI* botAI) { return new CastCurseOfAgonyAction(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); } @@ -288,7 +337,6 @@ private: static Action* unstable_affliction(PlayerbotAI* ai) { return new CastUnstableAfflictionAction(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); } @@ -301,13 +349,22 @@ private: 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); } + 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_the_elements(PlayerbotAI* ai) { return new CastCurseOfTheElementsAction(ai); } + static Action* curse_of_doom(PlayerbotAI* ai) { return new CastCurseOfDoomAction(ai); } + static Action* curse_of_exhaustion(PlayerbotAI* ai) { return new CastCurseOfExhaustionAction(ai); } + static Action* curse_of_tongues(PlayerbotAI* ai) { return new CastCurseOfTonguesAction(ai); } + static Action* curse_of_weakness(PlayerbotAI* ai) { return new CastCurseOfWeaknessAction(ai); } }; WarlockAiObjectContext::WarlockAiObjectContext(PlayerbotAI* botAI) : AiObjectContext(botAI) { strategyContexts.Add(new WarlockStrategyFactoryInternal()); strategyContexts.Add(new WarlockCombatStrategyFactoryInternal()); - strategyContexts.Add(new NonCombatBuffStrategyFactoryInternal()); + strategyContexts.Add(new WarlockPetStrategyFactoryInternal()); + strategyContexts.Add(new WarlockSoulstoneStrategyFactoryInternal()); + strategyContexts.Add(new WarlockCurseStrategyFactoryInternal()); actionContexts.Add(new WarlockAiObjectContextInternal()); triggerContexts.Add(new WarlockTriggerFactoryInternal()); } diff --git a/src/strategy/warlock/WarlockTriggers.cpp b/src/strategy/warlock/WarlockTriggers.cpp index 1a0d0e77..7f414f2e 100644 --- a/src/strategy/warlock/WarlockTriggers.cpp +++ b/src/strategy/warlock/WarlockTriggers.cpp @@ -107,3 +107,29 @@ bool CurseOfTheElementsTrigger::IsActive() // Use default BuffTrigger logic for the rest (only trigger if debuff is missing or expiring) return BuffTrigger::IsActive(); } + +// Checks if the target has a conflicting debuff that is equal to Curse of Weakness +bool CurseOfWeaknessTrigger::IsActive() +{ + Unit* target = GetTarget(); + if (!target || !target->IsAlive() || !target->IsInWorld()) + return false; + // List of all spell IDs for Curse of Weakness, Demoralizing Roar, Demoralizing Shout, and Vindication + static const uint32 CurseOfWeaknessExclusiveDebuffs[] = {// Curse of Weakness + 702, 1108, 6205, 7646, 11707, 11708, 27224, 30909, 50511, + // Demoralizing Roar + 99, 1735, 9490, 9747, 9898, 26998, 48559, 48560, + // Demoralizing Shout + 1160, 6190, 11554, 11555, 11556, 25202, 25203, 47437, + // Vindication + 67, 26017}; + + // Check if target has any of the exclusive debuffs + for (uint32 spellId : CurseOfWeaknessExclusiveDebuffs) + { + 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 40f72e6a..04de574a 100644 --- a/src/strategy/warlock/WarlockTriggers.h +++ b/src/strategy/warlock/WarlockTriggers.h @@ -30,6 +30,13 @@ public: bool IsActive() override; }; +class OutOfSoulShardsTrigger : public Trigger +{ +public: + OutOfSoulShardsTrigger(PlayerbotAI* botAI) : Trigger(botAI, "no soul shard") {} + bool IsActive() override { return AI_VALUE2(uint32, "item count", "soul shard") == 0; } +}; + class FirestoneTrigger : public BuffTrigger { public: @@ -122,33 +129,7 @@ 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(); - } -}; +// DoT/Curse Triggers class CorruptionTrigger : public DebuffTrigger { @@ -204,11 +185,52 @@ public: HauntTrigger(PlayerbotAI* ai) : DebuffTrigger(ai, "haunt", 1, true, 0) {} }; +class CurseOfAgonyTrigger : public DebuffTrigger +{ +public: + CurseOfAgonyTrigger(PlayerbotAI* botAI) : DebuffTrigger(botAI, "curse of agony", 1, true, 0.5f) {} + bool IsActive() override { return BuffTrigger::IsActive(); } +}; + +class CurseOfAgonyOnAttackerTrigger : public DebuffOnAttackerTrigger +{ +public: + CurseOfAgonyOnAttackerTrigger(PlayerbotAI* botAI) : DebuffOnAttackerTrigger(botAI, "curse of agony", true) {} + bool IsActive() override { return BuffTrigger::IsActive(); } +}; + class CurseOfTheElementsTrigger : public DebuffTrigger { public: - CurseOfTheElementsTrigger(PlayerbotAI* botAI) - : DebuffTrigger(botAI, "curse of the elements", 1, true, 0.5f) {} + CurseOfTheElementsTrigger(PlayerbotAI* botAI) : DebuffTrigger(botAI, "curse of the elements", 1, true, 0.5f) {} + bool IsActive() override; +}; + +class CurseOfDoomTrigger : public DebuffTrigger +{ +public: + CurseOfDoomTrigger(PlayerbotAI* botAI) : DebuffTrigger(botAI, "curse of doom", 1, true, 0.5f) {} + bool IsActive() override { return BuffTrigger::IsActive(); } +}; + +class CurseOfExhaustionTrigger : public DebuffTrigger +{ +public: + CurseOfExhaustionTrigger(PlayerbotAI* botAI) : DebuffTrigger(botAI, "curse of exhaustion", 1, true, 0.5f) {} + bool IsActive() override { return BuffTrigger::IsActive(); } +}; + +class CurseOfTonguesTrigger : public DebuffTrigger +{ +public: + CurseOfTonguesTrigger(PlayerbotAI* botAI) : DebuffTrigger(botAI, "curse of tongues", 1, true, 0.5f) {} + bool IsActive() override { return BuffTrigger::IsActive(); } +}; + +class CurseOfWeaknessTrigger : public DebuffTrigger +{ +public: + CurseOfWeaknessTrigger(PlayerbotAI* botAI) : DebuffTrigger(botAI, "curse of weakness", 1, true, 0.5f) {} bool IsActive() override; };