From e3cb8e9377507c1756c8555f17fe51c9c9df17d9 Mon Sep 17 00:00:00 2001 From: Bobblybook Date: Sun, 10 Nov 2024 01:54:42 +1100 Subject: [PATCH] Dungeon strat improvements (GD & UP) - Slad'ran (Gun'Drak): DPS will now kill snake wraps - King Ymiron (Utgarde Pinnacle): Bots will stop attack during Bane. This still needs work, sometimes if they are mid-cast they will still let it finish and blow everyone up --- .../wotlk/gundrak/GundrakActionContext.h | 2 ++ .../dungeons/wotlk/gundrak/GundrakActions.cpp | 26 ++++++++++++++ .../dungeons/wotlk/gundrak/GundrakActions.h | 7 ++++ .../wotlk/gundrak/GundrakMultipliers.cpp | 35 ++++++++++++++++--- .../wotlk/gundrak/GundrakStrategy.cpp | 3 +- .../wotlk/gundrak/GundrakTriggerContext.h | 2 ++ .../wotlk/gundrak/GundrakTriggers.cpp | 21 ++++++++++- .../dungeons/wotlk/gundrak/GundrakTriggers.h | 8 +++++ .../UtgardePinnacleActionContext.h | 2 ++ .../UtgardePinnacleMultipliers.cpp | 15 ++++++++ .../UtgardePinnacleMultipliers.h | 9 +++++ .../UtgardePinnacleStrategy.cpp | 3 ++ .../UtgardePinnacleTriggerContext.h | 2 ++ .../UtgardePinnacleTriggers.cpp | 8 +++++ .../utgardepinnacle/UtgardePinnacleTriggers.h | 12 +++++++ 15 files changed, 148 insertions(+), 7 deletions(-) diff --git a/src/strategy/dungeons/wotlk/gundrak/GundrakActionContext.h b/src/strategy/dungeons/wotlk/gundrak/GundrakActionContext.h index bbece7ef..746fed39 100644 --- a/src/strategy/dungeons/wotlk/gundrak/GundrakActionContext.h +++ b/src/strategy/dungeons/wotlk/gundrak/GundrakActionContext.h @@ -10,10 +10,12 @@ class WotlkDungeonGDActionContext : public NamedObjectContext public: WotlkDungeonGDActionContext() { creators["avoid poison nova"] = &WotlkDungeonGDActionContext::avoid_poison_nova; + creators["attack snake wrap"] = &WotlkDungeonGDActionContext::attack_snake_wrap; creators["avoid whirling slash"] = &WotlkDungeonGDActionContext::avoid_whirling_slash; } private: static Action* avoid_poison_nova(PlayerbotAI* ai) { return new AvoidPoisonNovaAction(ai); } + static Action* attack_snake_wrap(PlayerbotAI* ai) { return new AttackSnakeWrapAction(ai); } static Action* avoid_whirling_slash(PlayerbotAI* ai) { return new AvoidWhirlingSlashAction(ai); } }; diff --git a/src/strategy/dungeons/wotlk/gundrak/GundrakActions.cpp b/src/strategy/dungeons/wotlk/gundrak/GundrakActions.cpp index 122c47a1..4e991099 100644 --- a/src/strategy/dungeons/wotlk/gundrak/GundrakActions.cpp +++ b/src/strategy/dungeons/wotlk/gundrak/GundrakActions.cpp @@ -19,6 +19,32 @@ bool AvoidPoisonNovaAction::Execute(Event event) return false; } +bool AttackSnakeWrapAction::Execute(Event event) +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "slad'ran"); + if (!boss) { return false; } + + Unit* snakeWrap = nullptr; + // Target is not findable from threat table using AI_VALUE2(), + // therefore need to search manually for the unit name + GuidVector targets = AI_VALUE(GuidVector, "possible targets no los"); + + for (auto& target : targets) + { + Unit* unit = botAI->GetUnit(target); + if (unit && unit->GetEntry() == NPC_SNAKE_WRAP) + { + Unit* currentTarget = AI_VALUE(Unit*, "current target"); + if (!currentTarget || currentTarget->GetEntry() != NPC_SNAKE_WRAP) + { + return Attack(unit); + } + } + } + + return false; +} + bool AvoidWhirlingSlashAction::Execute(Event event) { Unit* boss = AI_VALUE2(Unit*, "find target", "gal'darah"); diff --git a/src/strategy/dungeons/wotlk/gundrak/GundrakActions.h b/src/strategy/dungeons/wotlk/gundrak/GundrakActions.h index e163248b..095cc5cf 100644 --- a/src/strategy/dungeons/wotlk/gundrak/GundrakActions.h +++ b/src/strategy/dungeons/wotlk/gundrak/GundrakActions.h @@ -14,6 +14,13 @@ public: bool Execute(Event event) override; }; +class AttackSnakeWrapAction : public AttackAction +{ +public: + AttackSnakeWrapAction(PlayerbotAI* ai) : AttackAction(ai, "attack snake wrap") {} + bool Execute(Event event) override; +}; + class AvoidWhirlingSlashAction : public MovementAction { public: diff --git a/src/strategy/dungeons/wotlk/gundrak/GundrakMultipliers.cpp b/src/strategy/dungeons/wotlk/gundrak/GundrakMultipliers.cpp index a484a593..2d440425 100644 --- a/src/strategy/dungeons/wotlk/gundrak/GundrakMultipliers.cpp +++ b/src/strategy/dungeons/wotlk/gundrak/GundrakMultipliers.cpp @@ -11,13 +11,38 @@ float SladranMultiplier::GetValue(Action* action) Unit* boss = AI_VALUE2(Unit*, "find target", "slad'ran"); if (!boss) { return 1.0f; } - if (boss->HasUnitState(UNIT_STATE_CASTING) && boss->FindCurrentSpellBySpellId(SPELL_POISON_NOVA)) + if (boss->FindCurrentSpellBySpellId(SPELL_POISON_NOVA)) + { + if (dynamic_cast(action) && !dynamic_cast(action)) { - if (dynamic_cast(action) && !dynamic_cast(action)) - { - return 0.0f; - } + return 0.0f; } + } + + if (!botAI->IsDps(bot)) { return 1.0f; } + + if (action->getThreatType() == Action::ActionThreatType::Aoe) + { + return 0.0f; + } + + Unit* snakeWrap = nullptr; + GuidVector targets = AI_VALUE(GuidVector, "possible targets no los"); + for (auto& target : targets) + { + Unit* unit = botAI->GetUnit(target); + if (unit && unit->GetEntry() == NPC_SNAKE_WRAP) + { + snakeWrap = unit; + break; + } + } + // Prevent auto-target acquisition during snake wraps + if (snakeWrap && dynamic_cast(action)) + { + return 0.0f; + } + return 1.0f; } diff --git a/src/strategy/dungeons/wotlk/gundrak/GundrakStrategy.cpp b/src/strategy/dungeons/wotlk/gundrak/GundrakStrategy.cpp index 5c29bd21..c77c3d15 100644 --- a/src/strategy/dungeons/wotlk/gundrak/GundrakStrategy.cpp +++ b/src/strategy/dungeons/wotlk/gundrak/GundrakStrategy.cpp @@ -13,13 +13,14 @@ void WotlkDungeonGDStrategy::InitTriggers(std::vector &triggers) // Will re-test in heroic, decent dps groups should be able to blast him down with no funky strats. triggers.push_back(new TriggerNode("poison nova", NextAction::array(0, new NextAction("avoid poison nova", ACTION_RAID + 5), nullptr))); + triggers.push_back(new TriggerNode("snake wrap", + NextAction::array(0, new NextAction("attack snake wrap", ACTION_RAID + 4), nullptr))); // Gal'darah triggers.push_back(new TriggerNode("whirling slash", NextAction::array(0, new NextAction("avoid whirling slash", ACTION_RAID + 5), nullptr))); // Eck the Ferocious (Heroic only) - // TODO } void WotlkDungeonGDStrategy::InitMultipliers(std::vector &multipliers) diff --git a/src/strategy/dungeons/wotlk/gundrak/GundrakTriggerContext.h b/src/strategy/dungeons/wotlk/gundrak/GundrakTriggerContext.h index fc744d1d..4160639b 100644 --- a/src/strategy/dungeons/wotlk/gundrak/GundrakTriggerContext.h +++ b/src/strategy/dungeons/wotlk/gundrak/GundrakTriggerContext.h @@ -11,10 +11,12 @@ class WotlkDungeonGDTriggerContext : public NamedObjectContext WotlkDungeonGDTriggerContext() { creators["poison nova"] = &WotlkDungeonGDTriggerContext::poison_nova; + creators["snake wrap"] = &WotlkDungeonGDTriggerContext::snake_wrap; creators["whirling slash"] = &WotlkDungeonGDTriggerContext::whirling_slash; } private: static Trigger* poison_nova(PlayerbotAI* ai) { return new SladranPoisonNovaTrigger(ai); } + static Trigger* snake_wrap(PlayerbotAI* ai) { return new SladranSnakeWrapTrigger(ai); } static Trigger* whirling_slash(PlayerbotAI* ai) { return new GaldarahWhirlingSlashTrigger(ai); } }; diff --git a/src/strategy/dungeons/wotlk/gundrak/GundrakTriggers.cpp b/src/strategy/dungeons/wotlk/gundrak/GundrakTriggers.cpp index 875b41dd..6229f0c8 100644 --- a/src/strategy/dungeons/wotlk/gundrak/GundrakTriggers.cpp +++ b/src/strategy/dungeons/wotlk/gundrak/GundrakTriggers.cpp @@ -8,7 +8,26 @@ bool SladranPoisonNovaTrigger::IsActive() Unit* boss = AI_VALUE2(Unit*, "find target", "slad'ran"); if (!boss) { return false; } - return boss->HasUnitState(UNIT_STATE_CASTING) && boss->FindCurrentSpellBySpellId(SPELL_POISON_NOVA); + return bool(boss->FindCurrentSpellBySpellId(SPELL_POISON_NOVA)); +} + +bool SladranSnakeWrapTrigger::IsActive() +{ + if (!botAI->IsDps(bot)) { return false; } + + // Target is not findable from threat table using AI_VALUE2(), + // therefore need to search manually for the unit name + GuidVector targets = AI_VALUE(GuidVector, "possible targets"); + + for (auto& target : targets) + { + Unit* unit = botAI->GetUnit(target); + if (unit && unit->GetEntry() == NPC_SNAKE_WRAP) + { + return true; + } + } + return false; } bool GaldarahWhirlingSlashTrigger::IsActive() diff --git a/src/strategy/dungeons/wotlk/gundrak/GundrakTriggers.h b/src/strategy/dungeons/wotlk/gundrak/GundrakTriggers.h index 4b2245bc..73f8c234 100644 --- a/src/strategy/dungeons/wotlk/gundrak/GundrakTriggers.h +++ b/src/strategy/dungeons/wotlk/gundrak/GundrakTriggers.h @@ -11,6 +11,7 @@ enum GundrakIDs // Slad'ran SPELL_POISON_NOVA_N = 55081, SPELL_POISON_NOVA_H = 59842, + NPC_SNAKE_WRAP = 29742, // Gal'darah SPELL_WHIRLING_SLASH_N = 55250, @@ -27,6 +28,13 @@ public: bool IsActive() override; }; +class SladranSnakeWrapTrigger : public Trigger +{ +public: + SladranSnakeWrapTrigger(PlayerbotAI* ai) : Trigger(ai, "slad'ran snake wrap") {} + bool IsActive() override; +}; + class GaldarahWhirlingSlashTrigger : public Trigger { public: diff --git a/src/strategy/dungeons/wotlk/utgardepinnacle/UtgardePinnacleActionContext.h b/src/strategy/dungeons/wotlk/utgardepinnacle/UtgardePinnacleActionContext.h index 5a9dff5b..9e7532b9 100644 --- a/src/strategy/dungeons/wotlk/utgardepinnacle/UtgardePinnacleActionContext.h +++ b/src/strategy/dungeons/wotlk/utgardepinnacle/UtgardePinnacleActionContext.h @@ -11,10 +11,12 @@ class WotlkDungeonUPActionContext : public NamedObjectContext WotlkDungeonUPActionContext() { creators["avoid freezing cloud"] = &WotlkDungeonUPActionContext::avoid_freezing_cloud; creators["avoid skadi whirlwind"] = &WotlkDungeonUPActionContext::avoid_whirlwind; + creators["stop attack"] = &WotlkDungeonUPActionContext::stop_attack; } private: static Action* avoid_freezing_cloud(PlayerbotAI* ai) { return new AvoidFreezingCloudAction(ai); } static Action* avoid_whirlwind(PlayerbotAI* ai) { return new AvoidSkadiWhirlwindAction(ai); } + static Action* stop_attack(PlayerbotAI* ai) { return new DropTargetAction(ai); } }; #endif diff --git a/src/strategy/dungeons/wotlk/utgardepinnacle/UtgardePinnacleMultipliers.cpp b/src/strategy/dungeons/wotlk/utgardepinnacle/UtgardePinnacleMultipliers.cpp index 82e5fd53..9a950933 100644 --- a/src/strategy/dungeons/wotlk/utgardepinnacle/UtgardePinnacleMultipliers.cpp +++ b/src/strategy/dungeons/wotlk/utgardepinnacle/UtgardePinnacleMultipliers.cpp @@ -82,3 +82,18 @@ float SkadiMultiplier::GetValue(Action* action) return 1.0f; } + +float YmironMultiplier::GetValue(Action* action) +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "king ymiron"); + if (!boss) { return 1.0f; } + + if (boss->FindCurrentSpellBySpellId(SPELL_BANE) || boss->HasAura(SPELL_BANE)) + { + if (dynamic_cast(action)) + { + return 0.0f; + } + } + return 1.0f; +} \ No newline at end of file diff --git a/src/strategy/dungeons/wotlk/utgardepinnacle/UtgardePinnacleMultipliers.h b/src/strategy/dungeons/wotlk/utgardepinnacle/UtgardePinnacleMultipliers.h index 36576678..06cb8578 100644 --- a/src/strategy/dungeons/wotlk/utgardepinnacle/UtgardePinnacleMultipliers.h +++ b/src/strategy/dungeons/wotlk/utgardepinnacle/UtgardePinnacleMultipliers.h @@ -12,4 +12,13 @@ class SkadiMultiplier : public Multiplier virtual float GetValue(Action* action); }; +class YmironMultiplier : public Multiplier +{ + public: + YmironMultiplier(PlayerbotAI* ai) : Multiplier(ai, "king ymiron") {} + + public: + virtual float GetValue(Action* action); +}; + #endif diff --git a/src/strategy/dungeons/wotlk/utgardepinnacle/UtgardePinnacleStrategy.cpp b/src/strategy/dungeons/wotlk/utgardepinnacle/UtgardePinnacleStrategy.cpp index 6877ba2c..00ff192e 100644 --- a/src/strategy/dungeons/wotlk/utgardepinnacle/UtgardePinnacleStrategy.cpp +++ b/src/strategy/dungeons/wotlk/utgardepinnacle/UtgardePinnacleStrategy.cpp @@ -17,9 +17,12 @@ void WotlkDungeonUPStrategy::InitTriggers(std::vector &triggers) // King Ymiron // May need to avoid orb.. unclear if the generic avoid AoE does this well + triggers.push_back(new TriggerNode("ymiron bane", + NextAction::array(0, new NextAction("stop attack", ACTION_RAID + 5), nullptr))); } void WotlkDungeonUPStrategy::InitMultipliers(std::vector &multipliers) { multipliers.push_back(new SkadiMultiplier(botAI)); + multipliers.push_back(new YmironMultiplier(botAI)); } diff --git a/src/strategy/dungeons/wotlk/utgardepinnacle/UtgardePinnacleTriggerContext.h b/src/strategy/dungeons/wotlk/utgardepinnacle/UtgardePinnacleTriggerContext.h index 143f3df5..69d51c0e 100644 --- a/src/strategy/dungeons/wotlk/utgardepinnacle/UtgardePinnacleTriggerContext.h +++ b/src/strategy/dungeons/wotlk/utgardepinnacle/UtgardePinnacleTriggerContext.h @@ -12,10 +12,12 @@ class WotlkDungeonUPTriggerContext : public NamedObjectContext { creators["freezing cloud"] = &WotlkDungeonUPTriggerContext::freezing_cloud; creators["skadi whirlwind"] = &WotlkDungeonUPTriggerContext::whirlwind; + creators["ymiron bane"] = &WotlkDungeonUPTriggerContext::bane; } private: static Trigger* freezing_cloud(PlayerbotAI* ai) { return new SkadiFreezingCloudTrigger(ai); } static Trigger* whirlwind(PlayerbotAI* ai) { return new SkadiWhirlwindTrigger(ai); } + static Trigger* bane(PlayerbotAI* ai) { return new YmironBaneTrigger(ai); } }; #endif diff --git a/src/strategy/dungeons/wotlk/utgardepinnacle/UtgardePinnacleTriggers.cpp b/src/strategy/dungeons/wotlk/utgardepinnacle/UtgardePinnacleTriggers.cpp index 168d16e6..b9b21dc8 100644 --- a/src/strategy/dungeons/wotlk/utgardepinnacle/UtgardePinnacleTriggers.cpp +++ b/src/strategy/dungeons/wotlk/utgardepinnacle/UtgardePinnacleTriggers.cpp @@ -46,3 +46,11 @@ bool SkadiWhirlwindTrigger::IsActive() Unit* boss = AI_VALUE2(Unit*, "find target", "skadi the ruthless"); return boss && boss->HasAura(SPELL_SKADI_WHIRLWIND); } + +bool YmironBaneTrigger::IsActive() +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "king ymiron"); + if (!boss) { return false; } + + return boss->FindCurrentSpellBySpellId(SPELL_BANE) || boss->HasAura(SPELL_BANE); +} \ No newline at end of file diff --git a/src/strategy/dungeons/wotlk/utgardepinnacle/UtgardePinnacleTriggers.h b/src/strategy/dungeons/wotlk/utgardepinnacle/UtgardePinnacleTriggers.h index bd42c3d6..72bf59f8 100644 --- a/src/strategy/dungeons/wotlk/utgardepinnacle/UtgardePinnacleTriggers.h +++ b/src/strategy/dungeons/wotlk/utgardepinnacle/UtgardePinnacleTriggers.h @@ -15,10 +15,15 @@ enum UtgardePinnacleIDs NPC_BREATH_TRIGGER = 28351, SPELL_SKADI_WHIRLWIND_N = 50228, SPELL_SKADI_WHIRLWIND_H = 59322, + + // King Ymiron + SPELL_BANE_N = 48294, + SPELL_BANE_H = 59301, }; #define SPELL_FREEZING_CLOUD DUNGEON_MODE(bot, SPELL_FREEZING_CLOUD_N, SPELL_FREEZING_CLOUD_H) #define SPELL_SKADI_WHIRLWIND DUNGEON_MODE(bot, SPELL_SKADI_WHIRLWIND_N, SPELL_SKADI_WHIRLWIND_H) +#define SPELL_BANE DUNGEON_MODE(bot, SPELL_BANE_N, SPELL_BANE_H) // const float SKADI_BREATH_CENTRELINE = -512.46875f; @@ -36,4 +41,11 @@ public: bool IsActive() override; }; +class YmironBaneTrigger : public Trigger +{ +public: + YmironBaneTrigger(PlayerbotAI* ai) : Trigger(ai, "ymiron bane") {} + bool IsActive() override; +}; + #endif