diff --git a/src/PlayerbotAI.cpp b/src/PlayerbotAI.cpp index b2c35166f..816dd2665 100644 --- a/src/PlayerbotAI.cpp +++ b/src/PlayerbotAI.cpp @@ -1529,12 +1529,60 @@ void PlayerbotAI::ApplyInstanceStrategies(uint32 mapId, bool tellMaster) case 533: strategyName = "naxx"; break; + case 574: + strategyName = "wotlk-uk"; // Utgarde Keep + break; + case 575: + strategyName = "wotlk-up"; // Utgarde Pinnacle + break; + case 576: + strategyName = "wotlk-nex"; // The Nexus + break; + case 578: + strategyName = "wotlk-occ"; // The Oculus + break; + case 595: + strategyName = "wotlk-cos"; // The Culling of Stratholme + break; + case 599: + strategyName = "wotlk-hos"; // Halls of Stone + break; + case 600: + strategyName = "wotlk-dtk"; // Drak'Tharon Keep + break; + case 601: + strategyName = "wotlk-an"; // Azjol-Nerub + break; + case 602: + strategyName = "wotlk-hol"; // Halls of Lightning + break; case 603: strategyName = "uld"; break; + case 604: + strategyName = "wotlk-gd"; // Gundrak + break; + case 608: + strategyName = "wotlk-vh"; // Violet Hold + break; + case 619: + strategyName = "wotlk-ok"; // Ahn'kahet: The Old Kingdom + break; case 631: strategyName = "icc"; break; + case 632: + strategyName = "wotlk-fos"; // The Forge of Souls + break; + case 650: + strategyName = "wotlk-toc"; // Trial of the Champion + break; + case 658: + strategyName = "wotlk-pos"; // Pit of Saron + break; + case 668: + strategyName = "wotlk-hor"; // Halls of Reflection + break; default: break; } diff --git a/src/strategy/AiObjectContext.cpp b/src/strategy/AiObjectContext.cpp index 16fec944e..c6fcbc470 100644 --- a/src/strategy/AiObjectContext.cpp +++ b/src/strategy/AiObjectContext.cpp @@ -28,6 +28,9 @@ #include "raids/moltencore/RaidMcTriggerContext.h" #include "raids/aq20/RaidAq20ActionContext.h" #include "raids/aq20/RaidAq20TriggerContext.h" +#include "dungeons/DungeonStrategyContext.h" +#include "dungeons/wotlk/WotlkDungeonActionContext.h" +#include "dungeons/wotlk/WotlkDungeonTriggerContext.h" AiObjectContext::AiObjectContext(PlayerbotAI* botAI) : PlayerbotAIAware(botAI) { @@ -36,6 +39,7 @@ AiObjectContext::AiObjectContext(PlayerbotAI* botAI) : PlayerbotAIAware(botAI) strategyContexts.Add(new AssistStrategyContext()); strategyContexts.Add(new QuestStrategyContext()); strategyContexts.Add(new RaidStrategyContext()); + strategyContexts.Add(new DungeonStrategyContext()); actionContexts.Add(new ActionContext()); actionContexts.Add(new ChatActionContext()); @@ -46,6 +50,7 @@ AiObjectContext::AiObjectContext(PlayerbotAI* botAI) : PlayerbotAIAware(botAI) actionContexts.Add(new RaidNaxxActionContext()); actionContexts.Add(new RaidUlduarActionContext()); actionContexts.Add(new RaidIccActionContext()); + actionContexts.Add(new WotlkDungeonUKActionContext()); triggerContexts.Add(new TriggerContext()); triggerContexts.Add(new ChatTriggerContext()); @@ -56,6 +61,7 @@ AiObjectContext::AiObjectContext(PlayerbotAI* botAI) : PlayerbotAIAware(botAI) triggerContexts.Add(new RaidNaxxTriggerContext()); triggerContexts.Add(new RaidUlduarTriggerContext()); triggerContexts.Add(new RaidIccTriggerContext()); + triggerContexts.Add(new WotlkDungeonUKTriggerContext()); valueContexts.Add(new ValueContext()); diff --git a/src/strategy/dungeons/DungeonStrategyContext.h b/src/strategy/dungeons/DungeonStrategyContext.h new file mode 100644 index 000000000..f1d8e18fa --- /dev/null +++ b/src/strategy/dungeons/DungeonStrategyContext.h @@ -0,0 +1,96 @@ +#ifndef _PLAYERBOT_DUNGEONSTRATEGYCONTEXT_H_ +#define _PLAYERBOT_DUNGEONSTRATEGYCONTEXT_H_ + +#include "Strategy.h" +#include "wotlk/utgardekeep/UtgardeKeepStrategy.h" + +/* +Full list/TODO: + +The Nexus - Nex +Grand Magus Telestra, Anomalus, Ormorok the Tree-Shaper, Keristrasza, Commander Stoutbeard (Horde Heroic Only)/Commander Kolurg (Alliance Heroic Only) +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 +Trollgore, Novos the Summoner, King Dred, The Prophet Tharon'ja +The Violet Hold - VH +Erekem, Moragg, Ichoron, Xevozz, Lavanthor, Zuramat the Obliterator, Cyanigosa +Gundrak - GD +Slad'ran, Drakkari Colossus, Moorabi, Gal'darah, Eck the Ferocious (Heroic only) +Halls of Stone - HoS +Maiden of Grief, Krystallus, Tribunal of Ages, Sjonnir The Ironshaper +Halls of Lightning - HoL +General Bjarngrim, Volkhan, Ionar, Loken +The Oculus - Occ +Drakos the Interrogator, Varos Cloudstrider, Mage-Lord Urom, Ley-Guardian Eregos +Utgarde Pinnacle - UP +Svala Sorrowgrave, Gortok Palehoof, Skadi the Ruthless, King Ymiron +The Culling of Stratholme - CoS +Meathook, Salramm the Fleshcrafter, Chrono-Lord Epoch, Mal'Ganis, Infinite Corruptor (Heroic only) +Trial of the Champion - ToC +Alliance Champions: Deathstalker Visceri, Eressea Dawnsinger, Mokra the Skullcrusher, Runok Wildmane, Zul'tore +Horde Champions: Ambrose Boltspark, Colosos, Jacob Alerius, Jaelyne Evensong, Lana Stouthammer +Argent Champion: Argent Confessor Paletress/Eadric the Pure +The Black Knight +Halls of Reflection - HoR +Falric, Marwyn, The Lich King +Pit of Saron - PoS +Forgemaster Garfrost, Krick & Ick, Scourgelord Tyrannus +The Forge of Souls - FoS +Bronjahm, Devourer of Souls + +*/ + + + +class DungeonStrategyContext : public NamedObjectContext +{ + public: + DungeonStrategyContext() : NamedObjectContext(false, true) + { + // Vanilla + // ... + + // Burning Crusade + // ... + + // Wrath of the Lich King + creators["wotlk-uk"] = &DungeonStrategyContext::wotlk_uk; // Utgarde Keep + creators["wotlk-nex"] = &DungeonStrategyContext::wotlk_nex; // The Nexus + creators["wotlk-an"] = &DungeonStrategyContext::wotlk_an; // Azjol-Nerub + creators["wotlk-ok"] = &DungeonStrategyContext::wotlk_ok; // Ahn'kahet: The Old Kingdom + creators["wotlk-dtk"] = &DungeonStrategyContext::wotlk_dtk; // Drak'Tharon Keep + creators["wotlk-vh"] = &DungeonStrategyContext::wotlk_vh; // The Violet Hold + creators["wotlk-gd"] = &DungeonStrategyContext::wotlk_gd; // Gundrak + creators["wotlk-hos"] = &DungeonStrategyContext::wotlk_hos; // Halls of Stone + creators["wotlk-hol"] = &DungeonStrategyContext::wotlk_hol; // Halls of Lightning + creators["wotlk-occ"] = &DungeonStrategyContext::wotlk_occ; // The Oculus + creators["wotlk-up"] = &DungeonStrategyContext::wotlk_up; // Utgarde Pinnacle + creators["wotlk-cos"] = &DungeonStrategyContext::wotlk_cos; // The Culling of Stratholme + creators["wotlk-toc"] = &DungeonStrategyContext::wotlk_toc; // Trial of the Champion + creators["wotlk-hor"] = &DungeonStrategyContext::wotlk_hor; // Halls of Reflection + creators["wotlk-pos"] = &DungeonStrategyContext::wotlk_pos; // Pit of Saron + creators["wotlk-fos"] = &DungeonStrategyContext::wotlk_fos; // The Forge of Souls + } + private: + static Strategy* wotlk_uk(PlayerbotAI* botAI) { return new WotlkDungeonUKStrategy(botAI); } + static Strategy* wotlk_nex(PlayerbotAI* botAI) { return new WotlkDungeonUKStrategy(botAI); } + static Strategy* wotlk_an(PlayerbotAI* botAI) { return new WotlkDungeonUKStrategy(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); } + static Strategy* wotlk_gd(PlayerbotAI* botAI) { return new WotlkDungeonUKStrategy(botAI); } + static Strategy* wotlk_hos(PlayerbotAI* botAI) { return new WotlkDungeonUKStrategy(botAI); } + static Strategy* wotlk_hol(PlayerbotAI* botAI) { return new WotlkDungeonUKStrategy(botAI); } + static Strategy* wotlk_occ(PlayerbotAI* botAI) { return new WotlkDungeonUKStrategy(botAI); } + static Strategy* wotlk_up(PlayerbotAI* botAI) { return new WotlkDungeonUKStrategy(botAI); } + static Strategy* wotlk_cos(PlayerbotAI* botAI) { return new WotlkDungeonUKStrategy(botAI); } + static Strategy* wotlk_toc(PlayerbotAI* botAI) { return new WotlkDungeonUKStrategy(botAI); } + static Strategy* wotlk_hor(PlayerbotAI* botAI) { return new WotlkDungeonUKStrategy(botAI); } + static Strategy* wotlk_pos(PlayerbotAI* botAI) { return new WotlkDungeonUKStrategy(botAI); } + static Strategy* wotlk_fos(PlayerbotAI* botAI) { return new WotlkDungeonUKStrategy(botAI); } +}; + +#endif diff --git a/src/strategy/dungeons/DungeonStrategyUtils.h b/src/strategy/dungeons/DungeonStrategyUtils.h new file mode 100644 index 000000000..6c014abaa --- /dev/null +++ b/src/strategy/dungeons/DungeonStrategyUtils.h @@ -0,0 +1,21 @@ +#ifndef _PLAYERBOT_DUNGEONUTILS_H_ +#define _PLAYERBOT_DUNGEONUTILS_H_ + + +template inline +const T& DUNGEON_MODE(Player* bot, const T& normal5, const T& heroic10) +{ + switch (bot->GetMap()->GetDifficulty()) + { + case DUNGEON_DIFFICULTY_NORMAL: + return normal5; + case DUNGEON_DIFFICULTY_HEROIC: + return heroic10; + default: + break; + } + + return heroic10; +} + +#endif diff --git a/src/strategy/dungeons/wotlk/WotlkDungeonActionContext.h b/src/strategy/dungeons/wotlk/WotlkDungeonActionContext.h new file mode 100644 index 000000000..ea13c9b0f --- /dev/null +++ b/src/strategy/dungeons/wotlk/WotlkDungeonActionContext.h @@ -0,0 +1,21 @@ +#ifndef _PLAYERBOT_WOTLKDUNGEONACTIONCONTEXT_H_ +#define _PLAYERBOT_WOTLKDUNGEONACTIONCONTEXT_H_ + +#include "utgardekeep/UtgardeKeepActionContext.h" +// #include "nexus/NexusActionContext.h" +// #include "azjolnerub/AzjolNerubActionContext.h" +// #include "oldkingdom/OldKingdomActionContext.h" +// #include "draktharonkeep/DraktharonKeepActionContext.h" +// #include "violethold/VioletHoldActionContext.h" +// #include "gundrak/GundrakActionContext.h" +// #include "hallsofstone/HallsOfStoneActionContext.h" +// #include "hallsoflightning/HallsOfLightningActionContext.h" +// #include "oculus/OculusActionContext.h" +// #include "utgardepinnacle/UtgardePinnacleActionContext.h" +// #include "cullingofstratholme/CullingOfStratholmeActionContext.h" +// #include "trialofthechampion/TrialOfTheChampionActionContext.h" +// #include "hallsofreflection/HallsOfReflectionActionContext.h" +// #include "pitofsaron/PitOfSaronActionContext.h" +// #include "forgeofsouls/ForgeOfSoulsActionContext.h" + +#endif diff --git a/src/strategy/dungeons/wotlk/WotlkDungeonTriggerContext.h b/src/strategy/dungeons/wotlk/WotlkDungeonTriggerContext.h new file mode 100644 index 000000000..126547000 --- /dev/null +++ b/src/strategy/dungeons/wotlk/WotlkDungeonTriggerContext.h @@ -0,0 +1,21 @@ +#ifndef _PLAYERBOT_WOTLKDUNGEONTRIGGERCONTEXT_H_ +#define _PLAYERBOT_WOTLKDUNGEONTRIGGERCONTEXT_H_ + +#include "utgardekeep/UtgardeKeepTriggerContext.h" +// #include "nexus/NexusTriggerContext.h" +// #include "azjolnerub/AzjolNerubTriggerContext.h" +// #include "oldkingdom/OldKingdomTriggerContext.h" +// #include "draktharonkeep/DraktharonKeepTriggerContext.h" +// #include "violethold/VioletHoldTriggerContext.h" +// #include "gundrak/GundrakTriggerContext.h" +// #include "hallsofstone/HallsOfStoneTriggerContext.h" +// #include "hallsoflightning/HallsOfLightningTriggerContext.h" +// #include "oculus/OculusTriggerContext.h" +// #include "utgardepinnacle/UtgardePinnacleTriggerContext.h" +// #include "cullingofstratholme/CullingOfStratholmeTriggerContext.h" +// #include "trialofthechampion/TrialOfTheChampionTriggerContext.h" +// #include "hallsofreflection/HallsOfReflectionTriggerContext.h" +// #include "pitofsaron/PitOfSaronTriggerContext.h" +// #include "forgeofsouls/ForgeOfSoulsTriggerContext.h" + +#endif diff --git a/src/strategy/dungeons/wotlk/azjolnerub/TODO b/src/strategy/dungeons/wotlk/azjolnerub/TODO new file mode 100644 index 000000000..e69de29bb diff --git a/src/strategy/dungeons/wotlk/cullingofstratholme/TODO b/src/strategy/dungeons/wotlk/cullingofstratholme/TODO new file mode 100644 index 000000000..e69de29bb diff --git a/src/strategy/dungeons/wotlk/draktharonkeep/TODO b/src/strategy/dungeons/wotlk/draktharonkeep/TODO new file mode 100644 index 000000000..e69de29bb diff --git a/src/strategy/dungeons/wotlk/forgeofsouls/TODO b/src/strategy/dungeons/wotlk/forgeofsouls/TODO new file mode 100644 index 000000000..e69de29bb diff --git a/src/strategy/dungeons/wotlk/gundrak/TODO b/src/strategy/dungeons/wotlk/gundrak/TODO new file mode 100644 index 000000000..e69de29bb diff --git a/src/strategy/dungeons/wotlk/hallsoflightning/TODO b/src/strategy/dungeons/wotlk/hallsoflightning/TODO new file mode 100644 index 000000000..e69de29bb diff --git a/src/strategy/dungeons/wotlk/hallsofreflection/TODO b/src/strategy/dungeons/wotlk/hallsofreflection/TODO new file mode 100644 index 000000000..e69de29bb diff --git a/src/strategy/dungeons/wotlk/hallsofstone/TODO b/src/strategy/dungeons/wotlk/hallsofstone/TODO new file mode 100644 index 000000000..e69de29bb diff --git a/src/strategy/dungeons/wotlk/nexus/TODO b/src/strategy/dungeons/wotlk/nexus/TODO new file mode 100644 index 000000000..e69de29bb diff --git a/src/strategy/dungeons/wotlk/oculus/TODO b/src/strategy/dungeons/wotlk/oculus/TODO new file mode 100644 index 000000000..e69de29bb diff --git a/src/strategy/dungeons/wotlk/oldkingdom/TODO b/src/strategy/dungeons/wotlk/oldkingdom/TODO new file mode 100644 index 000000000..e69de29bb diff --git a/src/strategy/dungeons/wotlk/pitofsaron/TODO b/src/strategy/dungeons/wotlk/pitofsaron/TODO new file mode 100644 index 000000000..e69de29bb diff --git a/src/strategy/dungeons/wotlk/trialofthechampion/TODO b/src/strategy/dungeons/wotlk/trialofthechampion/TODO new file mode 100644 index 000000000..e69de29bb diff --git a/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepActionContext.h b/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepActionContext.h new file mode 100644 index 000000000..0e51e56b4 --- /dev/null +++ b/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepActionContext.h @@ -0,0 +1,30 @@ +#ifndef _PLAYERBOT_WOTLKDUNGEONUKACTIONCONTEXT_H +#define _PLAYERBOT_WOTLKDUNGEONUKACTIONCONTEXT_H + +#include "Action.h" +#include "NamedObjectContext.h" +#include "UtgardeKeepActions.h" + +class WotlkDungeonUKActionContext : public NamedObjectContext +{ + public: + WotlkDungeonUKActionContext() { + creators["attack frost tomb"] = &WotlkDungeonUKActionContext::attack_frost_tomb; + creators["attack dalronn"] = &WotlkDungeonUKActionContext::attack_dalronn; + creators["ingvar stop casting"] = &WotlkDungeonUKActionContext::ingvar_stop_casting; + creators["ingvar get behind"] = &WotlkDungeonUKActionContext::ingvar_get_behind; + // creators["ingvar hide los"] = &WotlkDungeonUKActionContext::ingvar_hide_los; + creators["ingvar dodge smash"] = &WotlkDungeonUKActionContext::ingvar_dodge_smash; + creators["ingvar smash return"] = &WotlkDungeonUKActionContext::ingvar_smash_return; + } + private: + static Action* attack_frost_tomb(PlayerbotAI* ai) { return new AttackFrostTombAction(ai); } + static Action* attack_dalronn(PlayerbotAI* ai) { return new AttackDalronnAction(ai); } + static Action* ingvar_stop_casting(PlayerbotAI* ai) { return new IngvarStopCastingAction(ai); } + static Action* ingvar_get_behind(PlayerbotAI* ai) { return new SetBehindTargetAction(ai); } + // static Action* ingvar_hide_los(PlayerbotAI* ai) { return new TellLosAction(ai); } + static Action* ingvar_dodge_smash(PlayerbotAI* ai) { return new IngvarDodgeSmashAction(ai); } + static Action* ingvar_smash_return(PlayerbotAI* ai) { return new IngvarSmashReturnAction(ai); } +}; + +#endif diff --git a/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepActions.cpp b/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepActions.cpp new file mode 100644 index 000000000..2c17366d5 --- /dev/null +++ b/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepActions.cpp @@ -0,0 +1,97 @@ +#include "Playerbots.h" +#include "UtgardeKeepActions.h" +#include "UtgardeKeepStrategy.h" + +bool AttackFrostTombAction::Execute(Event event) +{ + Unit* frostTomb = 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->GetName() == "Frost Tomb") + { + frostTomb = unit; + break; + } + } + if (!frostTomb || AI_VALUE(Unit*, "current target") == frostTomb) + { + return false; + } + return Attack(frostTomb); +} + +// TODO: Possibly add player stacking behaviour close to tank, to prevent Skarvald charging ranged +bool AttackDalronnAction::Execute(Event event) +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "dalronn the controller"); + if (!boss || AI_VALUE(Unit*, "current target") == boss) + { + return false; + } + return Attack(boss); +} + +bool IngvarStopCastingAction::Execute(Event event) +{ + // Doesn't work, this action gets queued behind the current spell instead of interrupting it + Unit* boss = AI_VALUE2(Unit*, "find target", "ingvar the plunderer"); + if (!boss) + { + return false; + } + + int32 my_spell_id = AI_VALUE(uint32, "active spell"); + if (!my_spell_id || my_spell_id == 0) + { + return false; + } + + Spell* spell = bot->FindCurrentSpellBySpellId(my_spell_id); + if (!spell) + { + return false; + } + + // bot->Yell("cancelling spell="+std::to_string(my_spell_id), LANG_UNIVERSAL); + bot->InterruptSpell(spell->GetCurrentContainer(), false, true, true); + + // Can slightly optimise by allowing bot to keep casting if they will finish the cast + // before boss spell goes off, however need to hook boss AI for cast remaining. + return true; +} + +bool IngvarDodgeSmashAction::isUseful() { return !AI_VALUE2(bool, "behind", "current target"); } +bool IngvarDodgeSmashAction::Execute(Event event) +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "ingvar the plunderer"); + 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; + return Move(bot->GetAngle(boss), distance + distanceExtra); +} + +bool IngvarSmashReturnAction::isUseful() { return AI_VALUE2(bool, "behind", "current target"); } +bool IngvarSmashReturnAction::Execute(Event event) +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "ingvar the plunderer"); + if (!boss) + { + return false; + } + + float distance = bot->GetExactDist2d(boss->GetPosition()); + return Move(bot->GetAngle(boss), distance + bot->GetMeleeReach()); +} diff --git a/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepActions.h b/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepActions.h new file mode 100644 index 000000000..3774f3744 --- /dev/null +++ b/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepActions.h @@ -0,0 +1,47 @@ +#ifndef _PLAYERBOT_WOTLKDUNGEONUKACTIONS_H +#define _PLAYERBOT_WOTLKDUNGEONUKACTIONS_H + +#include "Action.h" +#include "AttackAction.h" +#include "PlayerbotAI.h" +#include "Playerbots.h" +#include "UtgardeKeepTriggers.h" + +class AttackFrostTombAction : public AttackAction +{ +public: + AttackFrostTombAction(PlayerbotAI* ai) : AttackAction(ai, "attack frost tomb") {} + bool Execute(Event event) override; +}; + +class AttackDalronnAction : public AttackAction +{ +public: + AttackDalronnAction(PlayerbotAI* ai) : AttackAction(ai, "attack dalronn") {} + bool Execute(Event event) override; +}; + +class IngvarStopCastingAction : public Action +{ +public: + IngvarStopCastingAction(PlayerbotAI* ai) : Action(ai, "ingvar stop casting") {} + bool Execute(Event event) override; +}; + +class IngvarDodgeSmashAction : public MovementAction +{ +public: + IngvarDodgeSmashAction(PlayerbotAI* ai) : MovementAction(ai, "ingvar dodge smash") {} + bool isUseful() override; + bool Execute(Event event) override; +}; + +class IngvarSmashReturnAction : public MovementAction +{ +public: + IngvarSmashReturnAction(PlayerbotAI* ai) : MovementAction(ai, "ingvar smash return") {} + bool isUseful() override; + bool Execute(Event event) override; +}; + +#endif diff --git a/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepMultipliers.cpp b/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepMultipliers.cpp new file mode 100644 index 000000000..7d785e4c5 --- /dev/null +++ b/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepMultipliers.cpp @@ -0,0 +1,92 @@ +#include "UtgardeKeepMultipliers.h" +#include "UtgardeKeepActions.h" +#include "GenericSpellActions.h" +#include "ChooseTargetActions.h" +#include "UtgardeKeepTriggers.h" + +float PrinceKelesethMultiplier::GetValue(Action* action) +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "prince keleseth"); + if (!boss) + { + return 1.0f; + } + if (dynamic_cast(action)) + { + return 0.0f; + } + return 1.0f; +} +float SkarvaldAndDalronnMultiplier::GetValue(Action* action) +{ + // Unit* skarvald = AI_VALUE2(Unit*, "find target", "skarvald the constructor"); + Unit* dalronn = AI_VALUE2(Unit*, "find target", "dalronn the controller"); + + if (!dalronn) + { + return 1.0f; + } + + if (dynamic_cast(action)) + { + return 0.0f; + } + return 1.0f; +} + +float IngvarThePlundererMultiplier::GetValue(Action* action) +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "ingvar the plunderer"); + bool isTank = botAI->IsTank(bot); + if (!boss) + { + return 1.0f; + } + + // Prevent movement actions overriding current movement, we're probably dodging a slam + if (isTank && bot->isMoving() && dynamic_cast(action)) + { + return 0.0f; + } + + // If boss is casting a roar, do not allow beginning a spell cast that is non-instant + if (boss->HasUnitState(UNIT_STATE_CASTING)) + { + if (boss->FindCurrentSpellBySpellId(SPELL_STAGGERING_ROAR) || + boss->FindCurrentSpellBySpellId(SPELL_DREADFUL_ROAR)) + { + if (dynamic_cast(action)) + { + uint32 spellId = AI_VALUE2(uint32, "spell id", action->getName()); + SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId); + if (!spellInfo) + { + return 1.0f; + } + + uint32 castTime = spellInfo->CalcCastTime(bot); + if (castTime != 0) + { + return 0.0f; + } + } + } + // Done with non-tank logic + if (!isTank) + { + return 1.0f; + } + // TANK ONLY + if (boss->FindCurrentSpellBySpellId(SPELL_SMASH) || + boss->FindCurrentSpellBySpellId(SPELL_DARK_SMASH)) + { + // Prevent movement actions during smash which can mess up boss position. + // Allow through IngvarDodgeSmashAction only, as well as any non-movement actions. + if (dynamic_cast(action) && !dynamic_cast(action)) + { + return 0.0f; + } + } + } + return 1.0f; +} diff --git a/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepMultipliers.h b/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepMultipliers.h new file mode 100644 index 000000000..e9e265be0 --- /dev/null +++ b/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepMultipliers.h @@ -0,0 +1,33 @@ +#ifndef _PLAYERRBOT_WOTLKDUNGEONUKMULTIPLIERS_H_ +#define _PLAYERRBOT_WOTLKDUNGEONUKMULTIPLIERS_H_ + +#include "Multiplier.h" + +class PrinceKelesethMultiplier : public Multiplier +{ + public: + PrinceKelesethMultiplier(PlayerbotAI* ai) : Multiplier(ai, "prince keleseth") {} + + public: + virtual float GetValue(Action* action); +}; + +class SkarvaldAndDalronnMultiplier : public Multiplier +{ + public: + SkarvaldAndDalronnMultiplier(PlayerbotAI* ai) : Multiplier(ai, "skarvald and dalronn") {} + + public: + virtual float GetValue(Action* action); +}; + +class IngvarThePlundererMultiplier : public Multiplier +{ + public: + IngvarThePlundererMultiplier(PlayerbotAI* ai) : Multiplier(ai, "ingvar the plunderer") {} + + public: + virtual float GetValue(Action* action); +}; + +#endif diff --git a/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepStrategy.cpp b/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepStrategy.cpp new file mode 100644 index 000000000..c79b3dd39 --- /dev/null +++ b/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepStrategy.cpp @@ -0,0 +1,43 @@ +#include "UtgardeKeepStrategy.h" +#include "UtgardeKeepMultipliers.h" + + +void WotlkDungeonUKStrategy::InitTriggers(std::vector &triggers) +{ + // Prince Keleseth + triggers.push_back(new TriggerNode("keleseth frost tomb", + NextAction::array(0, new NextAction("attack frost tomb", ACTION_RAID + 1), nullptr))); + + // Skarvald the Constructor & Dalronn the Controller + triggers.push_back(new TriggerNode("dalronn priority", + NextAction::array(0, new NextAction("attack dalronn", ACTION_RAID + 1), nullptr))); + + // Ingvar the Plunderer + + // Doesn't work yet, this action doesn't get processed until the existing cast finishes + // triggers.push_back(new TriggerNode("ingvar staggering roar", + // NextAction::array(0, new NextAction("ingvar stop casting", ACTION_RAID + 1), nullptr))); + + // No easy way to check LoS here, the pillars do not seem to count as gameobjects. + // Not implemented for now, unsure if this is needed as a good group can probably burst through the boss + // and just eat the debuff. + // triggers.push_back(new TriggerNode("ingvar dreadful roar", + // NextAction::array(0, new NextAction("ingvar hide los", ACTION_RAID + 1), nullptr))); + triggers.push_back(new TriggerNode("ingvar smash tank", + NextAction::array(0, new NextAction("ingvar dodge smash", ACTION_MOVE + 5), nullptr))); + triggers.push_back(new TriggerNode("ingvar smash tank return", + NextAction::array(0, new NextAction("ingvar smash return", ACTION_MOVE + 5), nullptr))); + // Buggy... if not behind target, ai can get stuck running towards and away from target. + // I think for ranged chars, a custom action should be added that doesn't attempt to run into melee. + // This is a bandaid for now, needs to be improved. + triggers.push_back(new TriggerNode("not behind ingvar", + NextAction::array(0, new NextAction("set behind", ACTION_MOVE + 1), nullptr))); + +} + +void WotlkDungeonUKStrategy::InitMultipliers(std::vector &multipliers) +{ + multipliers.push_back(new PrinceKelesethMultiplier(botAI)); + multipliers.push_back(new SkarvaldAndDalronnMultiplier(botAI)); + multipliers.push_back(new IngvarThePlundererMultiplier(botAI)); +} diff --git a/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepStrategy.h b/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepStrategy.h new file mode 100644 index 000000000..90873feb0 --- /dev/null +++ b/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepStrategy.h @@ -0,0 +1,18 @@ +#ifndef _PLAYERBOT_WOTLKDUNGEONUKSTRATEGY_H +#define _PLAYERBOT_WOTLKDUNGEONUKSTRATEGY_H + +#include "Multiplier.h" +#include "AiObjectContext.h" +#include "Strategy.h" + + +class WotlkDungeonUKStrategy : public Strategy +{ +public: + WotlkDungeonUKStrategy(PlayerbotAI* ai) : Strategy(ai) {} + virtual std::string const getName() override { return "utgarde keep"; } + virtual void InitTriggers(std::vector &triggers) override; + virtual void InitMultipliers(std::vector &multipliers) override; +}; + +#endif diff --git a/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepTriggerContext.h b/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepTriggerContext.h new file mode 100644 index 000000000..29cb6dd96 --- /dev/null +++ b/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepTriggerContext.h @@ -0,0 +1,31 @@ +#ifndef _PLAYERBOT_WOTLKDUNGEONUKTRIGGERCONTEXT_H +#define _PLAYERBOT_WOTLKDUNGEONUKTRIGGERCONTEXT_H + +#include "NamedObjectContext.h" +#include "AiObjectContext.h" +#include "UtgardeKeepTriggers.h" + +class WotlkDungeonUKTriggerContext : public NamedObjectContext +{ + public: + WotlkDungeonUKTriggerContext() + { + creators["keleseth frost tomb"] = &WotlkDungeonUKTriggerContext::keleseth_frost_tomb; + creators["dalronn priority"] = &WotlkDungeonUKTriggerContext::dalronn_priority_target; + creators["ingvar staggering roar"] = &WotlkDungeonUKTriggerContext::ingvar_staggering_roar; + creators["ingvar dreadful roar"] = &WotlkDungeonUKTriggerContext::ingvar_dreadful_roar; + creators["ingvar smash tank"] = &WotlkDungeonUKTriggerContext::ingvar_smash_tank; + creators["ingvar smash tank return"] = &WotlkDungeonUKTriggerContext::ingvar_smash_tank_return; + creators["not behind ingvar"] = &WotlkDungeonUKTriggerContext::not_behind_ingvar; + } + private: + static Trigger* keleseth_frost_tomb(PlayerbotAI* ai) { return new KelesethFrostTombTrigger(ai); } + static Trigger* dalronn_priority_target(PlayerbotAI* ai) { return new DalronnNontankTrigger(ai); } + static Trigger* ingvar_staggering_roar(PlayerbotAI* ai) { return new IngvarStaggeringRoarTrigger(ai); } + static Trigger* ingvar_dreadful_roar(PlayerbotAI* ai) { return new IngvarDreadfulRoarTrigger(ai); } + static Trigger* ingvar_smash_tank(PlayerbotAI* ai) { return new IngvarSmashTankTrigger(ai); } + static Trigger* ingvar_smash_tank_return(PlayerbotAI* ai) { return new IngvarSmashTankReturnTrigger(ai); } + static Trigger* not_behind_ingvar(PlayerbotAI* ai) { return new NotBehindIngvarTrigger(ai); } +}; + +#endif diff --git a/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepTriggers.cpp b/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepTriggers.cpp new file mode 100644 index 000000000..9e839ae7f --- /dev/null +++ b/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepTriggers.cpp @@ -0,0 +1,104 @@ +#include "Playerbots.h" +#include "UtgardeKeepTriggers.h" +#include "AiObject.h" +#include "AiObjectContext.h" + +bool KelesethFrostTombTrigger::IsActive() +{ + GuidVector members = AI_VALUE(GuidVector, "group members"); + for (auto& member : members) + { + if (botAI->GetUnit(member)->HasAura(DEBUFF_FROST_TOMB)) + { + return true; + } + } + return false; +} + +bool DalronnNontankTrigger::IsActive() +{ + Unit* dalronn = AI_VALUE2(Unit*, "find target", "dalronn the controller"); + if (!dalronn) + { + return false; + } + return !botAI->IsTank(bot); +} + +bool IngvarStaggeringRoarTrigger::IsActive() +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "ingvar the plunderer"); + if (!boss) + { + return false; + } + if (boss->HasUnitState(UNIT_STATE_CASTING)) + { + if (boss->FindCurrentSpellBySpellId(SPELL_STAGGERING_ROAR)) + { + return true; + } + } + return false; +} + +bool IngvarDreadfulRoarTrigger::IsActive() +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "ingvar the plunderer"); + if (!boss) + { + return false; + } + if (boss->HasUnitState(UNIT_STATE_CASTING)) + { + if (boss->FindCurrentSpellBySpellId(SPELL_DREADFUL_ROAR)) + { + return true; + } + } + return false; +} + +bool IngvarSmashTankTrigger::IsActive() +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "ingvar the plunderer"); + if (!boss || !botAI->IsTank(bot)) + { + return false; + } + + if (boss->HasUnitState(UNIT_STATE_CASTING)) + { + if (boss->FindCurrentSpellBySpellId(SPELL_SMASH) || + boss->FindCurrentSpellBySpellId(SPELL_DARK_SMASH)) + { + return true; + } + } + return false; +} + +bool IngvarSmashTankReturnTrigger::IsActive() +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "ingvar the plunderer"); + // if (!boss || !botAI->IsTank(bot) || boss->HasUnitState(UNIT_STATE_CASTING)) + // Ignore casting state as Ingvar will sometimes chain-cast a roar after a smash.. + // We don't want this to prevent our tank from repositioning properly. + if (!boss || !botAI->IsTank(bot)) + { + return false; + } + return true; +} + +bool NotBehindIngvarTrigger::IsActive() +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "ingvar the plunderer"); + if (!boss || botAI->IsTank(bot)) + { + return false; + } + + return AI_VALUE2(bool, "behind", "current target"); +} diff --git a/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepTriggers.h b/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepTriggers.h new file mode 100644 index 000000000..e59ad8ca5 --- /dev/null +++ b/src/strategy/dungeons/wotlk/utgardekeep/UtgardeKeepTriggers.h @@ -0,0 +1,94 @@ +#ifndef _PLAYERBOT_WOTLKDUNGEONUKTRIGGERS_H +#define _PLAYERBOT_WOTLKDUNGEONUKTRIGGERS_H + +#include "EventMap.h" +#include "Trigger.h" +#include "PlayerbotAIConfig.h" +#include "GenericTriggers.h" +#include "DungeonStrategyUtils.h" + +// Taken from: +// src/server/scripts/Northrend/UtgardeKeep/UtgardeKeep/boss_ingvar_the_plunderer.cpp +enum eSpells +{ + SPELL_SUMMON_VALKYR = 42912, + SPELL_RESURRECTION_BEAM = 42857, + SPELL_RESURRECTION_BALL = 42862, + SPELL_RESURRECTION_HEAL = 42704, + SPELL_INGVAR_TRANSFORM = 42796, + + SPELL_STAGGERING_ROAR_N = 42708, + SPELL_STAGGERING_ROAR_H = 59708, + SPELL_CLEAVE = 42724, + SPELL_SMASH_N = 42669, + SPELL_SMASH_H = 59706, + SPELL_ENRAGE_N = 42705, + SPELL_ENRAGE_H = 59707, + + SPELL_DREADFUL_ROAR_N = 42729, + SPELL_DREADFUL_ROAR_H = 59734, + SPELL_WOE_STRIKE_N = 42730, + SPELL_WOE_STRIKE_H = 59735, + SPELL_DARK_SMASH = 42723, + SPELL_SHADOW_AXE = 42749, + + // Added + DEBUFF_FROST_TOMB = 48400, +}; + +#define SPELL_STAGGERING_ROAR DUNGEON_MODE(bot, SPELL_STAGGERING_ROAR_N, SPELL_STAGGERING_ROAR_H) +#define SPELL_DREADFUL_ROAR DUNGEON_MODE(bot, SPELL_DREADFUL_ROAR_N, SPELL_DREADFUL_ROAR_H) +#define SPELL_WOE_STRIKE DUNGEON_MODE(bot, SPELL_WOE_STRIKE_N, SPELL_WOE_STRIKE_H) +#define SPELL_SMASH DUNGEON_MODE(bot, SPELL_SMASH_N, SPELL_SMASH_H) +#define SPELL_ENRAGE DUNGEON_MODE(bot, SPELL_ENRAGE_N, SPELL_ENRAGE_H) + +class KelesethFrostTombTrigger : public Trigger +{ +public: + KelesethFrostTombTrigger(PlayerbotAI* ai) : Trigger(ai, "keleseth frost tomb") {} + bool IsActive() override; +}; + +class DalronnNontankTrigger : public Trigger +{ +public: + DalronnNontankTrigger(PlayerbotAI* ai) : Trigger(ai, "dalronn non-tank") {} + bool IsActive() override; +}; + +class IngvarStaggeringRoarTrigger : public Trigger +{ +public: + IngvarStaggeringRoarTrigger(PlayerbotAI* ai) : Trigger(ai, "ingvar staggering roar") {} + bool IsActive() override; +}; + +class IngvarDreadfulRoarTrigger : public Trigger +{ +public: + IngvarDreadfulRoarTrigger(PlayerbotAI* ai) : Trigger(ai, "ingvar dreadful roar") {} + bool IsActive() override; +}; + +class IngvarSmashTankTrigger : public Trigger +{ +public: + IngvarSmashTankTrigger(PlayerbotAI* ai) : Trigger(ai, "ingvar smash tank") {} + bool IsActive() override; +}; + +class IngvarSmashTankReturnTrigger : public Trigger +{ +public: + IngvarSmashTankReturnTrigger(PlayerbotAI* ai) : Trigger(ai, "ingvar smash tank return") {} + bool IsActive() override; +}; + +class NotBehindIngvarTrigger : public Trigger +{ +public: + NotBehindIngvarTrigger(PlayerbotAI* ai) : Trigger(ai, "not behind ingvar") {} + bool IsActive() override; +}; + +#endif diff --git a/src/strategy/dungeons/wotlk/utgardepinnacle/TODO b/src/strategy/dungeons/wotlk/utgardepinnacle/TODO new file mode 100644 index 000000000..e69de29bb diff --git a/src/strategy/dungeons/wotlk/violethold/TODO b/src/strategy/dungeons/wotlk/violethold/TODO new file mode 100644 index 000000000..e69de29bb