diff --git a/src/strategy/AiObjectContext.cpp b/src/strategy/AiObjectContext.cpp index 0742d363..819ea98a 100644 --- a/src/strategy/AiObjectContext.cpp +++ b/src/strategy/AiObjectContext.cpp @@ -52,6 +52,7 @@ AiObjectContext::AiObjectContext(PlayerbotAI* botAI) : PlayerbotAIAware(botAI) actionContexts.Add(new RaidIccActionContext()); actionContexts.Add(new WotlkDungeonUKActionContext()); actionContexts.Add(new WotlkDungeonNexActionContext()); + actionContexts.Add(new WotlkDungeonANActionContext()); triggerContexts.Add(new TriggerContext()); triggerContexts.Add(new ChatTriggerContext()); @@ -64,6 +65,7 @@ AiObjectContext::AiObjectContext(PlayerbotAI* botAI) : PlayerbotAIAware(botAI) triggerContexts.Add(new RaidIccTriggerContext()); triggerContexts.Add(new WotlkDungeonUKTriggerContext()); triggerContexts.Add(new WotlkDungeonNexTriggerContext()); + triggerContexts.Add(new WotlkDungeonANTriggerContext()); valueContexts.Add(new ValueContext()); diff --git a/src/strategy/dungeons/DungeonStrategyContext.h b/src/strategy/dungeons/DungeonStrategyContext.h index afa2616f..8764d351 100644 --- a/src/strategy/dungeons/DungeonStrategyContext.h +++ b/src/strategy/dungeons/DungeonStrategyContext.h @@ -4,12 +4,12 @@ #include "Strategy.h" #include "wotlk/utgardekeep/UtgardeKeepStrategy.h" #include "wotlk/nexus/NexusStrategy.h" +#include "wotlk/azjolnerub/AzjolNerubStrategy.h" /* Full list/TODO: -Azjol-Nerub: Azjol-Nerub - AN -Krik'thir the Gatewatcher, Hadronox, Anub'arak + Ahn'kahet: The Old Kingdom - OK Elder Nadox, Prince Taldaram, Jedoga Shadowseeker, Herald Volazj, Amanitar (Heroic Only) Drak'Tharon Keep - DTK @@ -76,7 +76,8 @@ class DungeonStrategyContext : public NamedObjectContext private: static Strategy* wotlk_uk(PlayerbotAI* botAI) { return new WotlkDungeonUKStrategy(botAI); } static Strategy* wotlk_nex(PlayerbotAI* botAI) { return new WotlkDungeonNexStrategy(botAI); } - static Strategy* wotlk_an(PlayerbotAI* botAI) { return new WotlkDungeonUKStrategy(botAI); } + static Strategy* wotlk_an(PlayerbotAI* botAI) { return new WotlkDungeonANStrategy(botAI); } + static Strategy* wotlk_ok(PlayerbotAI* botAI) { return new WotlkDungeonUKStrategy(botAI); } static Strategy* wotlk_dtk(PlayerbotAI* botAI) { return new WotlkDungeonUKStrategy(botAI); } static Strategy* wotlk_vh(PlayerbotAI* botAI) { return new WotlkDungeonUKStrategy(botAI); } diff --git a/src/strategy/dungeons/wotlk/WotlkDungeonActionContext.h b/src/strategy/dungeons/wotlk/WotlkDungeonActionContext.h index d9a18ae8..40529513 100644 --- a/src/strategy/dungeons/wotlk/WotlkDungeonActionContext.h +++ b/src/strategy/dungeons/wotlk/WotlkDungeonActionContext.h @@ -3,7 +3,7 @@ #include "utgardekeep/UtgardeKeepActionContext.h" #include "nexus/NexusActionContext.h" -// #include "azjolnerub/AzjolNerubActionContext.h" +#include "azjolnerub/AzjolNerubActionContext.h" // #include "oldkingdom/OldKingdomActionContext.h" // #include "draktharonkeep/DraktharonKeepActionContext.h" // #include "violethold/VioletHoldActionContext.h" diff --git a/src/strategy/dungeons/wotlk/WotlkDungeonTriggerContext.h b/src/strategy/dungeons/wotlk/WotlkDungeonTriggerContext.h index 4cbb41e2..b53d0fa5 100644 --- a/src/strategy/dungeons/wotlk/WotlkDungeonTriggerContext.h +++ b/src/strategy/dungeons/wotlk/WotlkDungeonTriggerContext.h @@ -3,7 +3,7 @@ #include "utgardekeep/UtgardeKeepTriggerContext.h" #include "nexus/NexusTriggerContext.h" -// #include "azjolnerub/AzjolNerubTriggerContext.h" +#include "azjolnerub/AzjolNerubTriggerContext.h" // #include "oldkingdom/OldKingdomTriggerContext.h" // #include "draktharonkeep/DraktharonKeepTriggerContext.h" // #include "violethold/VioletHoldTriggerContext.h" diff --git a/src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubActionContext.h b/src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubActionContext.h new file mode 100644 index 00000000..6306643b --- /dev/null +++ b/src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubActionContext.h @@ -0,0 +1,22 @@ +#ifndef _PLAYERBOT_WOTLKDUNGEONANACTIONCONTEXT_H +#define _PLAYERBOT_WOTLKDUNGEONANACTIONCONTEXT_H + +#include "Action.h" +#include "NamedObjectContext.h" +#include "AzjolNerubActions.h" + +class WotlkDungeonANActionContext : public NamedObjectContext +{ + public: + WotlkDungeonANActionContext() { + creators["attack web wrap"] = &WotlkDungeonANActionContext::attack_web_wrap; + creators["krik'thir priority"] = &WotlkDungeonANActionContext::krikthir_priority; + creators["dodge pound"] = &WotlkDungeonANActionContext::dodge_pound; + } + private: + static Action* attack_web_wrap(PlayerbotAI* ai) { return new AttackWebWrapAction(ai); } + static Action* krikthir_priority(PlayerbotAI* ai) { return new WatchersTargetAction(ai); } + static Action* dodge_pound(PlayerbotAI* ai) { return new AnubarakDodgePoundAction(ai); } +}; + +#endif diff --git a/src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubActions.cpp b/src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubActions.cpp new file mode 100644 index 00000000..161dd0d5 --- /dev/null +++ b/src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubActions.cpp @@ -0,0 +1,111 @@ +#include "Playerbots.h" +#include "AzjolNerubActions.h" +#include "AzjolNerubStrategy.h" + + +bool AttackWebWrapAction::isUseful() { return !botAI->IsHeal(bot); } +bool AttackWebWrapAction::Execute(Event event) +{ + Unit* webWrap = 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 i = targets.begin(); i != targets.end(); ++i) + { + Unit* unit = botAI->GetUnit(*i); + if (unit && unit->GetEntry() == NPC_WEB_WRAP) + { + webWrap = unit; + break; + } + } + if (!webWrap || AI_VALUE(Unit*, "current target") == webWrap) + { + return false; + } + bot->Yell("ATTACKING WRAP", LANG_UNIVERSAL); + return Attack(webWrap); +} + +bool WatchersTargetAction::isUseful() { return !botAI->IsHeal(bot); } +bool WatchersTargetAction::Execute(Event event) +{ + // Always prioritise web wraps + Unit* currTarget = AI_VALUE(Unit*, "current target"); + if (currTarget && currTarget->GetEntry() == NPC_WEB_WRAP) { return false; } + + // Do not search all units in range! + // There are many adds we don't want to aggro in close proximity, + // only check in-combat adds now. + GuidVector attackers = AI_VALUE(GuidVector, "attackers"); + Unit* priorityTargets[4] = {nullptr, nullptr, nullptr, nullptr}; + + for (auto& attacker : attackers) + { + Unit* npc = botAI->GetUnit(attacker); + if (!npc) + { + continue; + } + switch (npc->GetEntry()) + { + // Focus skirmishers first + case NPC_WATCHER_SKIRMISHER: + priorityTargets[0] = npc; + break; + // Then shadowcaster. This doesn't work so well for the shadowcaster + // + skirmisher pack - ideally we would kill the watcher second. + // But don't want to make this unnecessarily complex and rigid... + // Will revisit if this causes problems in heroic. + case NPC_WATCHER_SHADOWCASTER: + priorityTargets[1] = npc; + break; + // Named watcher next + case NPC_WATCHER_SILTHIK: + case NPC_WATCHER_GASHRA: + case NPC_WATCHER_NARJIL: + priorityTargets[2] = npc; + break; + // Warrior last + case NPC_WATCHER_WARRIOR: + priorityTargets[3] = npc; + break; + } + } + + for (Unit* target : priorityTargets) + { + // Attack the first valid split target in the priority list + if (target) + { + if (currTarget != target) + { + // bot->Yell("ATTACKING "+target->GetName(), LANG_UNIVERSAL); + return Attack(target); + } + // Don't continue loop here, the target exists so we don't + // want to move down the prio list. We just don't need to send attack + // command again, just return false and exit the loop that way + return false; + } + } + + return false; +} + +bool AnubarakDodgePoundAction::isUseful() { return !AI_VALUE2(bool, "behind", "current target"); } +bool AnubarakDodgePoundAction::Execute(Event event) +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "anub'arak"); + if (!boss) { return false; } + + float distance = bot->GetExactDist2d(boss->GetPosition()); + // Extra units to move into the boss, instead of being just 1 pixel past his midpoint. + // Can be adjusted - this value tends to mirror how a human would play, + // and visibly ensures you won't get hit while not creating excessive movements. + float distanceExtra = 2.0f; + bot->Yell("MOVING", LANG_UNIVERSAL); + return Move(bot->GetAngle(boss), distance + distanceExtra); +} diff --git a/src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubActions.h b/src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubActions.h new file mode 100644 index 00000000..ef388cbd --- /dev/null +++ b/src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubActions.h @@ -0,0 +1,34 @@ +#ifndef _PLAYERBOT_WOTLKDUNGEONANACTIONS_H +#define _PLAYERBOT_WOTLKDUNGEONANACTIONS_H + +#include "Action.h" +#include "AttackAction.h" +#include "PlayerbotAI.h" +#include "Playerbots.h" +#include "AzjolNerubTriggers.h" + +class AttackWebWrapAction : public AttackAction +{ +public: + AttackWebWrapAction(PlayerbotAI* ai) : AttackAction(ai, "attack web wrap") {} + bool Execute(Event event) override; + bool isUseful() override; +}; + +class WatchersTargetAction : public AttackAction +{ +public: + WatchersTargetAction(PlayerbotAI* ai) : AttackAction(ai, "krik'thir priority") {} + bool Execute(Event event) override; + bool isUseful() override; +}; + +class AnubarakDodgePoundAction : public AttackAction +{ +public: + AnubarakDodgePoundAction(PlayerbotAI* ai) : AttackAction(ai, "anub'arak dodge pound") {} + bool Execute(Event event) override; + bool isUseful() override; +}; + +#endif diff --git a/src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubMultipliers.cpp b/src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubMultipliers.cpp new file mode 100644 index 00000000..e8cece04 --- /dev/null +++ b/src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubMultipliers.cpp @@ -0,0 +1,54 @@ +#include "AzjolNerubMultipliers.h" +#include "AzjolNerubActions.h" +#include "GenericSpellActions.h" +#include "ChooseTargetActions.h" +#include "MovementActions.h" +#include "AzjolNerubTriggers.h" +#include "Action.h" + +float KrikthirMultiplier::GetValue(Action* action) +{ + // Target is not findable from threat table using AI_VALUE2(), + // therefore need to search manually for the unit name + Unit* boss = nullptr; + Unit* watcher = nullptr; + GuidVector targets = AI_VALUE(GuidVector, "possible targets no los"); + + for (auto i = targets.begin(); i != targets.end(); ++i) + { + Unit* unit = botAI->GetUnit(*i); + if (!unit) { continue; } + + switch (unit->GetEntry()) + { + case NPC_KRIKTHIR: + boss = unit; + continue; + case NPC_WATCHER_SILTHIK: + case NPC_WATCHER_GASHRA: + case NPC_WATCHER_NARJIL: + case NPC_WATCHER_SKIRMISHER: + case NPC_WATCHER_SHADOWCASTER: + case NPC_WATCHER_WARRIOR: + watcher = unit; + continue; + } + } + + if (boss && watcher) + { + // Do not target swap + // TODO: Need to suppress AoE actions but unsure how to identify them + if (dynamic_cast(action)) + { + return 0.0f; + } + // Doesn't seem to work + // if (action->getThreatType() == Action::ActionThreatType::Aoe) + // { + // bot->Yell("Suppressed AoE", LANG_UNIVERSAL); + // return 0.0f; + // } + } + return 1.0f; +} diff --git a/src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubMultipliers.h b/src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubMultipliers.h new file mode 100644 index 00000000..bb01115e --- /dev/null +++ b/src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubMultipliers.h @@ -0,0 +1,15 @@ +#ifndef _PLAYERBOT_WOTLKDUNGEONANMULTIPLIERS_H +#define _PLAYERBOT_WOTLKDUNGEONANMULTIPLIERS_H + +#include "Multiplier.h" + +class KrikthirMultiplier : public Multiplier +{ + public: + KrikthirMultiplier(PlayerbotAI* ai) : Multiplier(ai, "krik'thir the gatewatcher") {} + + public: + virtual float GetValue(Action* action); +}; + +#endif diff --git a/src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubStrategy.cpp b/src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubStrategy.cpp new file mode 100644 index 00000000..1f0dd038 --- /dev/null +++ b/src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubStrategy.cpp @@ -0,0 +1,31 @@ +#include "AzjolNerubStrategy.h" +#include "AzjolNerubMultipliers.h" + + +void WotlkDungeonANStrategy::InitTriggers(std::vector &triggers) +{ + // Krik'thir the Gatewatcher + // TODO: Add CC trigger while web wraps are casting? + // TODO: Bring healer closer than ranged dps to avoid fixates? + triggers.push_back(new TriggerNode("krik'thir web wrap", + NextAction::array(0, new NextAction("attack web wrap", ACTION_RAID + 5), nullptr))); + triggers.push_back(new TriggerNode("krik'thir watchers", + NextAction::array(0, new NextAction("krik'thir priority", ACTION_RAID + 4), nullptr))); + + // Hadronox + // The core AC triggers are very buggy with this boss, but default strat seems to play correctly + + //Anub'arak + // TODO: No clear way to track these spikes. They don't seem to appear as gameobjects or triggers, + // and cast time is instant so no way to check currently casting location. + // May need to hook boss AI.. might be able to just heal through it for now. + // triggers.push_back(new TriggerNode("anub'arak impale", + // NextAction::array(0, new NextAction("TODO", ACTION_MOVE + 5), nullptr))); + triggers.push_back(new TriggerNode("anub'arak pound", + NextAction::array(0, new NextAction("dodge pound", ACTION_MOVE + 5), nullptr))); +} + +void WotlkDungeonANStrategy::InitMultipliers(std::vector &multipliers) +{ + multipliers.push_back(new KrikthirMultiplier(botAI)); +} diff --git a/src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubStrategy.h b/src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubStrategy.h new file mode 100644 index 00000000..47d5d635 --- /dev/null +++ b/src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubStrategy.h @@ -0,0 +1,18 @@ +#ifndef _PLAYERBOT_WOTLKDUNGEONANSTRATEGY_H +#define _PLAYERBOT_WOTLKDUNGEONANSTRATEGY_H + +#include "Multiplier.h" +#include "AiObjectContext.h" +#include "Strategy.h" + + +class WotlkDungeonANStrategy : public Strategy +{ +public: + WotlkDungeonANStrategy(PlayerbotAI* ai) : Strategy(ai) {} + virtual std::string const getName() override { return "azjol-nerub"; } + virtual void InitTriggers(std::vector &triggers) override; + virtual void InitMultipliers(std::vector &multipliers) override; +}; + +#endif diff --git a/src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubTriggerContext.h b/src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubTriggerContext.h new file mode 100644 index 00000000..d0e988c0 --- /dev/null +++ b/src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubTriggerContext.h @@ -0,0 +1,25 @@ +#ifndef _PLAYERBOT_WOTLKDUNGEONANTRIGGERCONTEXT_H +#define _PLAYERBOT_WOTLKDUNGEONANTRIGGERCONTEXT_H + +#include "NamedObjectContext.h" +#include "AiObjectContext.h" +#include "AzjolNerubTriggers.h" + +class WotlkDungeonANTriggerContext : public NamedObjectContext +{ + public: + WotlkDungeonANTriggerContext() + { + creators["krik'thir web wrap"] = &WotlkDungeonANTriggerContext::krikthir_web_wrap; + creators["krik'thir watchers"] = &WotlkDungeonANTriggerContext::krikthir_watchers; + // creators["anub'arak impale"] = &WotlkDungeonANTriggerContext::anubarak_impale; + creators["anub'arak pound"] = &WotlkDungeonANTriggerContext::anubarak_pound; + } + private: + static Trigger* krikthir_web_wrap(PlayerbotAI* ai) { return new KrikthirWebWrapTrigger(ai); } + static Trigger* krikthir_watchers(PlayerbotAI* ai) { return new KrikthirWatchersTrigger(ai); } + // static Trigger* anubarak_impale(PlayerbotAI* ai) { return new AnubarakImpaleTrigger(ai); } + static Trigger* anubarak_pound(PlayerbotAI* ai) { return new AnubarakPoundTrigger(ai); } +}; + +#endif diff --git a/src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubTriggers.cpp b/src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubTriggers.cpp new file mode 100644 index 00000000..584269da --- /dev/null +++ b/src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubTriggers.cpp @@ -0,0 +1,69 @@ +#include "Playerbots.h" +#include "AzjolNerubTriggers.h" +#include "AiObject.h" +#include "AiObjectContext.h" + + +bool KrikthirWebWrapTrigger::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 no los"); + + for (auto i = targets.begin(); i != targets.end(); ++i) + { + Unit* unit = botAI->GetUnit(*i); + if (unit && unit->GetEntry() == NPC_WEB_WRAP) + { + return true; + } + } + + return false; +} + +bool KrikthirWatchersTrigger::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 no los"); + + for (auto i = targets.begin(); i != targets.end(); ++i) + { + Unit* unit = botAI->GetUnit(*i); + if (unit && unit->GetEntry() == NPC_KRIKTHIR) + { + return true; + } + } + return false; +} + +// bool AnubarakImpaleTrigger::IsActive() +// { +// Unit* boss = AI_VALUE2(Unit*, "find target", "anub'arak"); +// if (!boss) { return false; } +// GuidVector triggers = AI_VALUE(GuidVector, "possible triggers"); +// for (auto i = triggers.begin(); i != triggers.end(); i++) +// { +// Unit* unit = botAI->GetUnit(*i); + +// if (unit) +// { +// bot->Yell("TRIGGER="+unit->GetName(), LANG_UNIVERSAL); +// } +// } +// return false; +// } + +bool AnubarakPoundTrigger::IsActive() +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "anub'arak"); + if (!boss) { return false; } + + return boss->HasUnitState(UNIT_STATE_CASTING) && boss->FindCurrentSpellBySpellId(SPELL_POUND); +} diff --git a/src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubTriggers.h b/src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubTriggers.h new file mode 100644 index 00000000..5d12b596 --- /dev/null +++ b/src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubTriggers.h @@ -0,0 +1,61 @@ +#ifndef _PLAYERBOT_WOTLKDUNGEONANTRIGGERS_H +#define _PLAYERBOT_WOTLKDUNGEONANTRIGGERS_H + +#include "Trigger.h" +#include "PlayerbotAIConfig.h" +#include "GenericTriggers.h" +#include "DungeonStrategyUtils.h" + +enum AzjolNerubIDs +{ + // Krik'thir the Gatewatcher + NPC_KRIKTHIR = 28684, + NPC_WATCHER_SILTHIK = 28731, + NPC_WATCHER_GASHRA = 28730, + NPC_WATCHER_NARJIL = 28729, + NPC_WATCHER_SKIRMISHER = 28734, + NPC_WATCHER_SHADOWCASTER = 28733, + NPC_WATCHER_WARRIOR = 28732, + DEBUFF_WEB_WRAP = 52086, + NPC_WEB_WRAP = 28619, + + // Anub'arak + // Not sure how to track this - first one is cast as a buff on himself, + // which triggers periodic casts of the spikes spell. + SPELL_IMPALE_PERIODIC = 53456, + SPELL_IMPALE_SPIKES = 53457, + SPELL_POUND_N = 53472, + SPELL_POUND_H = 59433, +}; + +#define SPELL_POUND DUNGEON_MODE(bot, SPELL_POUND_N, SPELL_POUND_H) + +class KrikthirWebWrapTrigger : public Trigger +{ +public: + KrikthirWebWrapTrigger(PlayerbotAI* ai) : Trigger(ai, "krik'thir web wrap") {} + bool IsActive() override; +}; + +class KrikthirWatchersTrigger : public Trigger +{ +public: + KrikthirWatchersTrigger(PlayerbotAI* ai) : Trigger(ai, "krik'thir watchers") {} + bool IsActive() override; +}; + +// class AnubarakImpaleTrigger : public Trigger +// { +// public: +// AnubarakImpaleTrigger(PlayerbotAI* ai) : Trigger(ai, "anub'arak impale") {} +// bool IsActive() override; +// }; + +class AnubarakPoundTrigger : public Trigger +{ +public: + AnubarakPoundTrigger(PlayerbotAI* ai) : Trigger(ai, "anub'arak pound") {} + bool IsActive() override; +}; + +#endif diff --git a/src/strategy/dungeons/wotlk/azjolnerub/TODO b/src/strategy/dungeons/wotlk/azjolnerub/TODO deleted file mode 100644 index e69de29b..00000000