diff --git a/src/strategy/AiObjectContext.cpp b/src/strategy/AiObjectContext.cpp index c874f577..b8121320 100644 --- a/src/strategy/AiObjectContext.cpp +++ b/src/strategy/AiObjectContext.cpp @@ -58,6 +58,7 @@ AiObjectContext::AiObjectContext(PlayerbotAI* botAI) : PlayerbotAIAware(botAI) actionContexts.Add(new WotlkDungeonVHActionContext()); actionContexts.Add(new WotlkDungeonGDActionContext()); actionContexts.Add(new WotlkDungeonHoSActionContext()); + actionContexts.Add(new WotlkDungeonHoLActionContext()); triggerContexts.Add(new TriggerContext()); triggerContexts.Add(new ChatTriggerContext()); @@ -76,6 +77,7 @@ AiObjectContext::AiObjectContext(PlayerbotAI* botAI) : PlayerbotAIAware(botAI) triggerContexts.Add(new WotlkDungeonVHTriggerContext()); triggerContexts.Add(new WotlkDungeonGDTriggerContext()); triggerContexts.Add(new WotlkDungeonHoSTriggerContext()); + triggerContexts.Add(new WotlkDungeonHoLTriggerContext()); valueContexts.Add(new ValueContext()); diff --git a/src/strategy/dungeons/DungeonStrategyContext.h b/src/strategy/dungeons/DungeonStrategyContext.h index fa0d31c2..5ed70f6c 100644 --- a/src/strategy/dungeons/DungeonStrategyContext.h +++ b/src/strategy/dungeons/DungeonStrategyContext.h @@ -10,12 +10,11 @@ #include "wotlk/violethold/VioletHoldStrategy.h" #include "wotlk/gundrak/GundrakStrategy.h" #include "wotlk/hallsofstone/HallsOfStoneStrategy.h" +#include "wotlk/hallsoflightning/HallsOfLightningStrategy.h" /* Full list/TODO: -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 @@ -76,8 +75,8 @@ class DungeonStrategyContext : public NamedObjectContext static Strategy* wotlk_vh(PlayerbotAI* botAI) { return new WotlkDungeonVHStrategy(botAI); } static Strategy* wotlk_gd(PlayerbotAI* botAI) { return new WotlkDungeonGDStrategy(botAI); } static Strategy* wotlk_hos(PlayerbotAI* botAI) { return new WotlkDungeonHoSStrategy(botAI); } + static Strategy* wotlk_hol(PlayerbotAI* botAI) { return new WotlkDungeonHoLStrategy(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); } diff --git a/src/strategy/dungeons/wotlk/WotlkDungeonActionContext.h b/src/strategy/dungeons/wotlk/WotlkDungeonActionContext.h index 73fbecb4..b1301868 100644 --- a/src/strategy/dungeons/wotlk/WotlkDungeonActionContext.h +++ b/src/strategy/dungeons/wotlk/WotlkDungeonActionContext.h @@ -9,7 +9,7 @@ #include "violethold/VioletHoldActionContext.h" #include "gundrak/GundrakActionContext.h" #include "hallsofstone/HallsOfStoneActionContext.h" -// #include "hallsoflightning/HallsOfLightningActionContext.h" +#include "hallsoflightning/HallsOfLightningActionContext.h" // #include "oculus/OculusActionContext.h" // #include "utgardepinnacle/UtgardePinnacleActionContext.h" // #include "cullingofstratholme/CullingOfStratholmeActionContext.h" diff --git a/src/strategy/dungeons/wotlk/WotlkDungeonTriggerContext.h b/src/strategy/dungeons/wotlk/WotlkDungeonTriggerContext.h index 7e966e2f..cb819430 100644 --- a/src/strategy/dungeons/wotlk/WotlkDungeonTriggerContext.h +++ b/src/strategy/dungeons/wotlk/WotlkDungeonTriggerContext.h @@ -9,7 +9,7 @@ #include "violethold/VioletHoldTriggerContext.h" #include "gundrak/GundrakTriggerContext.h" #include "hallsofstone/HallsOfStoneTriggerContext.h" -// #include "hallsoflightning/HallsOfLightningTriggerContext.h" +#include "hallsoflightning/HallsOfLightningTriggerContext.h" // #include "oculus/OculusTriggerContext.h" // #include "utgardepinnacle/UtgardePinnacleTriggerContext.h" // #include "cullingofstratholme/CullingOfStratholmeTriggerContext.h" diff --git a/src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubMultipliers.cpp b/src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubMultipliers.cpp index 6c287591..38cb9e9a 100644 --- a/src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubMultipliers.cpp +++ b/src/strategy/dungeons/wotlk/azjolnerub/AzjolNerubMultipliers.cpp @@ -38,19 +38,15 @@ float KrikthirMultiplier::GetValue(Action* action) if (boss && watcher) { // Do not target swap - // TODO: Need to suppress AoE actions but unsure how to identify them - // TODO: TEST AOE Avoid - if (dynamic_cast(action) - || dynamic_cast(action)) + if (dynamic_cast(action)) + { + return 0.0f; + } + + if (action->getThreatType() == Action::ActionThreatType::Aoe) { 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/hallsoflightning/HallsOfLightningActionContext.h b/src/strategy/dungeons/wotlk/hallsoflightning/HallsOfLightningActionContext.h new file mode 100644 index 00000000..c3073d49 --- /dev/null +++ b/src/strategy/dungeons/wotlk/hallsoflightning/HallsOfLightningActionContext.h @@ -0,0 +1,34 @@ +#ifndef _PLAYERBOT_WOTLKDUNGEONHOLACTIONCONTEXT_H +#define _PLAYERBOT_WOTLKDUNGEONHOLACTIONCONTEXT_H + +#include "Action.h" +#include "NamedObjectContext.h" +#include "HallsOfLightningActions.h" + +class WotlkDungeonHoLActionContext : public NamedObjectContext +{ + public: + WotlkDungeonHoLActionContext() { + creators["bjarngrim target"] = &WotlkDungeonHoLActionContext::bjarngrim_target; + creators["avoid whirlwind"] = &WotlkDungeonHoLActionContext::avoid_whirlwind; + creators["volkhan target"] = &WotlkDungeonHoLActionContext::volkhan_target; + creators["static overload spread"] = &WotlkDungeonHoLActionContext::static_overload_spread; + creators["ball lightning spread"] = &WotlkDungeonHoLActionContext::ball_lightning_spread; + creators["ionar tank position"] = &WotlkDungeonHoLActionContext::ionar_tank_position; + creators["disperse position"] = &WotlkDungeonHoLActionContext::disperse_position; + creators["loken stack"] = &WotlkDungeonHoLActionContext::loken_stack; + creators["avoid lightning nova"] = &WotlkDungeonHoLActionContext::avoid_lightning_nova; + } + private: + static Action* bjarngrim_target(PlayerbotAI* ai) { return new BjarngrimTargetAction(ai); } + static Action* avoid_whirlwind(PlayerbotAI* ai) { return new AvoidWhirlwindAction(ai); } + static Action* volkhan_target(PlayerbotAI* ai) { return new VolkhanTargetAction(ai); } + static Action* static_overload_spread(PlayerbotAI* ai) { return new StaticOverloadSpreadAction(ai); } + static Action* ball_lightning_spread(PlayerbotAI* ai) { return new BallLightningSpreadAction(ai); } + static Action* ionar_tank_position(PlayerbotAI* ai) { return new IonarTankPositionAction(ai); } + static Action* disperse_position(PlayerbotAI* ai) { return new DispersePositionAction(ai); } + static Action* loken_stack(PlayerbotAI* ai) { return new LokenStackAction(ai); } + static Action* avoid_lightning_nova(PlayerbotAI* ai) { return new AvoidLightningNovaAction(ai); } +}; + +#endif diff --git a/src/strategy/dungeons/wotlk/hallsoflightning/HallsOfLightningActions.cpp b/src/strategy/dungeons/wotlk/hallsoflightning/HallsOfLightningActions.cpp new file mode 100644 index 00000000..6ca64dd4 --- /dev/null +++ b/src/strategy/dungeons/wotlk/hallsoflightning/HallsOfLightningActions.cpp @@ -0,0 +1,163 @@ +#include "Playerbots.h" +#include "HallsOfLightningActions.h" +#include "HallsOfLightningStrategy.h" + +bool BjarngrimTargetAction::Execute(Event event) +{ + Unit* target = 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_STORMFORGED_LIEUTENANT) + { + target = unit; + break; + } + } + // There are two, we don't want to ping-pong between them if we're attacking one already + if (!target || AI_VALUE(Unit*, "current target") == target) + { + return false; + } + + return Attack(target); +} + +bool AvoidWhirlwindAction::Execute(Event event) +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "general bjarngrim"); + if (!boss) { return false; } + + float distance = bot->GetExactDist2d(boss->GetPosition()); + float radius = 8.0f; + float distanceExtra = 2.0f; + + if (distance < radius + distanceExtra) + { + return MoveAway(boss, radius + distanceExtra - distance); + } + + return false; +} + +bool VolkhanTargetAction::Execute(Event event) +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "volkhan"); + if (!boss || AI_VALUE(Unit*, "current target") == boss) + { + return false; + } + + return Attack(boss); +} + +bool StaticOverloadSpreadAction::Execute(Event event) +{ + float radius = 8.0f; + float distanceExtra = 2.0f; + + GuidVector members = AI_VALUE(GuidVector, "group members"); + for (auto& member : members) + { + if (bot->GetGUID() == member) + { + continue; + } + + Unit* unit = botAI->GetUnit(member); + if (unit && unit->HasAura(SPELL_STATIC_OVERLOAD) + && bot->GetExactDist2d(unit) < radius) + { + return MoveAway(unit, radius + distanceExtra - bot->GetExactDist2d(unit)); + } + } + return false; +} + +bool BallLightningSpreadAction::Execute(Event event) +{ + float radius = 6.0f; + float distanceExtra = 1.0f; + + GuidVector members = AI_VALUE(GuidVector, "group members"); + for (auto& member : members) + { + if (bot->GetGUID() == member) + { + continue; + } + Unit* unit = botAI->GetUnit(member); + if (unit && bot->GetExactDist2d(unit) < radius + distanceExtra) + { + return MoveAway(unit, radius + distanceExtra - bot->GetExactDist2d(unit)); + } + } + return false; +} + +bool IonarTankPositionAction::isUseful() { return bot->GetExactDist2d(IONAR_TANK_POSITION) > 10.0f; } +bool IonarTankPositionAction::Execute(Event event) +{ + return MoveTo(bot->GetMapId(), IONAR_TANK_POSITION.GetPositionX(), IONAR_TANK_POSITION.GetPositionY(), IONAR_TANK_POSITION.GetPositionZ(), + false, false, false, true, MovementPriority::MOVEMENT_COMBAT); +} + +bool DispersePositionAction::isUseful() { return bot->GetExactDist2d(DISPERSE_POSITION) > 8.0f; } +bool DispersePositionAction::Execute(Event event) +{ + return MoveTo(bot->GetMapId(), DISPERSE_POSITION.GetPositionX(), DISPERSE_POSITION.GetPositionY(), DISPERSE_POSITION.GetPositionZ(), + false, false, false, true, MovementPriority::MOVEMENT_COMBAT); +} + +bool LokenStackAction::isUseful() +{ + // Minimum hunter range is 5, but values too close to this seem to cause issues.. + // Hunter bots will try and melee in between ranged attacks, or just melee entirely at 5 as they are in range. + // 6.5 or 7.0 solves this. + if(bot->getClass() == CLASS_HUNTER) + { + return AI_VALUE2(float, "distance", "current target") > 6.5f; + } + // else + return AI_VALUE2(float, "distance", "current target") > 2.0f; +} +bool LokenStackAction::Execute(Event event) +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "loken"); + if (!boss) { return false; } + + if (!boss->HasUnitState(UNIT_STATE_CASTING)) + { + if(bot->getClass() == CLASS_HUNTER) + { + return Move(bot->GetAngle(boss), fmin(bot->GetExactDist2d(boss) - 6.5f, 10.0f)); + } + // else + return Move(bot->GetAngle(boss), fmin(bot->GetExactDist2d(boss), 10.0f)); + } + + return false; +} + + +bool AvoidLightningNovaAction::Execute(Event event) +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "loken"); + if (!boss) { return false; } + + float distance = bot->GetExactDist2d(boss->GetPosition()); + float radius = 20.0f; + float distanceExtra = 2.0f; + + if (distance < radius + distanceExtra) + { + return MoveAway(boss, radius + distanceExtra - distance); + } + + return false; +} diff --git a/src/strategy/dungeons/wotlk/hallsoflightning/HallsOfLightningActions.h b/src/strategy/dungeons/wotlk/hallsoflightning/HallsOfLightningActions.h new file mode 100644 index 00000000..fe81aa44 --- /dev/null +++ b/src/strategy/dungeons/wotlk/hallsoflightning/HallsOfLightningActions.h @@ -0,0 +1,79 @@ +#ifndef _PLAYERBOT_WOTLKDUNGEONHOLACTIONS_H +#define _PLAYERBOT_WOTLKDUNGEONHOLACTIONS_H + +#include "Action.h" +#include "AttackAction.h" +#include "PlayerbotAI.h" +#include "Playerbots.h" +#include "HallsOfLightningTriggers.h" + +const Position IONAR_TANK_POSITION = Position(1078.860f, -261.928f, 61.226f); +const Position DISPERSE_POSITION = Position(1161.152f, -261.584f, 53.223f); + +class BjarngrimTargetAction : public AttackAction +{ +public: + BjarngrimTargetAction(PlayerbotAI* ai) : AttackAction(ai, "bjarngrim target") {} + bool Execute(Event event) override; +}; + +class AvoidWhirlwindAction : public MovementAction +{ +public: + AvoidWhirlwindAction(PlayerbotAI* ai) : MovementAction(ai, "avoid whirlwind") {} + bool Execute(Event event) override; +}; + +class VolkhanTargetAction : public AttackAction +{ +public: + VolkhanTargetAction(PlayerbotAI* ai) : AttackAction(ai, "volkhan target") {} + bool Execute(Event event) override; +}; + +class StaticOverloadSpreadAction : public MovementAction +{ +public: + StaticOverloadSpreadAction(PlayerbotAI* ai) : MovementAction(ai, "static overload spread") {} + bool Execute(Event event) override; +}; + +class BallLightningSpreadAction : public MovementAction +{ +public: + BallLightningSpreadAction(PlayerbotAI* ai) : MovementAction(ai, "ball lightning spread") {} + bool Execute(Event event) override; +}; + +class IonarTankPositionAction : public MovementAction +{ +public: + IonarTankPositionAction(PlayerbotAI* ai) : MovementAction(ai, "ionar tank position") {} + bool Execute(Event event) override; + bool isUseful() override; +}; + +class DispersePositionAction : public MovementAction +{ +public: + DispersePositionAction(PlayerbotAI* ai) : MovementAction(ai, "disperse position") {} + bool Execute(Event event) override; + bool isUseful() override; +}; + +class LokenStackAction : public MovementAction +{ +public: + LokenStackAction(PlayerbotAI* ai) : MovementAction(ai, "loken stack") {} + bool Execute(Event event) override; + bool isUseful() override; +}; + +class AvoidLightningNovaAction : public MovementAction +{ +public: + AvoidLightningNovaAction(PlayerbotAI* ai) : MovementAction(ai, "avoid lightning nova") {} + bool Execute(Event event) override; +}; + +#endif diff --git a/src/strategy/dungeons/wotlk/hallsoflightning/HallsOfLightningMultipliers.cpp b/src/strategy/dungeons/wotlk/hallsoflightning/HallsOfLightningMultipliers.cpp new file mode 100644 index 00000000..0250868d --- /dev/null +++ b/src/strategy/dungeons/wotlk/hallsoflightning/HallsOfLightningMultipliers.cpp @@ -0,0 +1,88 @@ +#include "HallsOfLightningMultipliers.h" +#include "HallsOfLightningActions.h" +#include "GenericSpellActions.h" +#include "ChooseTargetActions.h" +#include "MovementActions.h" +#include "HallsOfLightningTriggers.h" +#include "Action.h" + +float BjarngrimMultiplier::GetValue(Action* action) +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "general bjarngrim"); + if (!boss || botAI->IsHeal(bot)) { return 1.0f; } + + if (boss->HasUnitState(UNIT_STATE_CASTING) && boss->FindCurrentSpellBySpellId(SPELL_WHIRLWIND_BJARNGRIM)) + { + if (dynamic_cast(action) && !dynamic_cast(action)) + { + return 0.0f; + } + } + + Unit* boss_add = AI_VALUE2(Unit*, "find target", "stormforged lieutenant"); + if (!boss_add || botAI->IsTank(bot)) { return 1.0f; } + + if (dynamic_cast(action)) + { + return 0.0f; + } + + if (action->getThreatType() == Action::ActionThreatType::Aoe) + { + return 0.0f; + } + + return 1.0f; +} + +float VolkhanMultiplier::GetValue(Action* action) +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "volkhan"); + if (!boss || botAI->IsTank(bot) || botAI->IsHeal(bot)) { return 1.0f; } + + if (dynamic_cast(action)) + { + return 0.0f; + } + + if (action->getThreatType() == Action::ActionThreatType::Aoe) + { + return 0.0f; + } + + return 1.0f; +} + +float IonarMultiplier::GetValue(Action* action) +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "ionar"); + if (!boss) { return 1.0f; } + + if(!bot->CanSeeOrDetect(boss)) + { + if (dynamic_cast(action) + && !dynamic_cast(action) + && !dynamic_cast(action)) + { + return 0.0f; + } + } + return 1.0f; +} + +float LokenMultiplier::GetValue(Action* action) +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "loken"); + if (!boss) { return 1.0f; } + + if (dynamic_cast(action)) { return 0.0f; } + + if (boss->FindCurrentSpellBySpellId(SPELL_LIGHTNING_NOVA) + && dynamic_cast(action) + && !dynamic_cast(action)) + { + return 0.0f; + } + + return 1.0f; +} diff --git a/src/strategy/dungeons/wotlk/hallsoflightning/HallsOfLightningMultipliers.h b/src/strategy/dungeons/wotlk/hallsoflightning/HallsOfLightningMultipliers.h new file mode 100644 index 00000000..8cc43967 --- /dev/null +++ b/src/strategy/dungeons/wotlk/hallsoflightning/HallsOfLightningMultipliers.h @@ -0,0 +1,42 @@ +#ifndef _PLAYERBOT_WOTLKDUNGEONHOLMULTIPLIERS_H +#define _PLAYERBOT_WOTLKDUNGEONHOLMULTIPLIERS_H + +#include "Multiplier.h" + +class BjarngrimMultiplier : public Multiplier +{ + public: + BjarngrimMultiplier(PlayerbotAI* ai) : Multiplier(ai, "general bjarngrim") {} + + public: + virtual float GetValue(Action* action); +}; + +class VolkhanMultiplier : public Multiplier +{ + public: + VolkhanMultiplier(PlayerbotAI* ai) : Multiplier(ai, "volkhan") {} + + public: + virtual float GetValue(Action* action); +}; + +class IonarMultiplier : public Multiplier +{ + public: + IonarMultiplier(PlayerbotAI* ai) : Multiplier(ai, "ionar") {} + + public: + virtual float GetValue(Action* action); +}; + +class LokenMultiplier : public Multiplier +{ + public: + LokenMultiplier(PlayerbotAI* ai) : Multiplier(ai, "loken") {} + + public: + virtual float GetValue(Action* action); +}; + +#endif diff --git a/src/strategy/dungeons/wotlk/hallsoflightning/HallsOfLightningStrategy.cpp b/src/strategy/dungeons/wotlk/hallsoflightning/HallsOfLightningStrategy.cpp new file mode 100644 index 00000000..f438f17b --- /dev/null +++ b/src/strategy/dungeons/wotlk/hallsoflightning/HallsOfLightningStrategy.cpp @@ -0,0 +1,41 @@ +#include "HallsOfLightningStrategy.h" +#include "HallsOfLightningMultipliers.h" + + +void WotlkDungeonHoLStrategy::InitTriggers(std::vector &triggers) +{ + // General Bjarngrim + triggers.push_back(new TriggerNode("stormforged lieutenant", + NextAction::array(0, new NextAction("bjarngrim target", ACTION_RAID + 5), nullptr))); + triggers.push_back(new TriggerNode("whirlwind", + NextAction::array(0, new NextAction("avoid whirlwind", ACTION_RAID + 4), nullptr))); + + // Volkhan + triggers.push_back(new TriggerNode("volkhan", + NextAction::array(0, new NextAction("volkhan target", ACTION_RAID + 5), nullptr))); + + // Ionar + triggers.push_back(new TriggerNode("ionar disperse", + NextAction::array(0, new NextAction("disperse position", ACTION_MOVE + 5), nullptr))); + triggers.push_back(new TriggerNode("ionar tank aggro", + NextAction::array(0, new NextAction("ionar tank position", ACTION_MOVE + 4), nullptr))); + triggers.push_back(new TriggerNode("static overload", + NextAction::array(0, new NextAction("static overload spread", ACTION_MOVE + 3), nullptr))); + // TODO: Targeted player can dodge the ball, but a single player soaking it isn't too bad to heal + triggers.push_back(new TriggerNode("ball lightning", + NextAction::array(0, new NextAction("ball lightning spread", ACTION_MOVE + 2), nullptr))); + + // Loken + triggers.push_back(new TriggerNode("lightning nova", + NextAction::array(0, new NextAction("avoid lightning nova", ACTION_MOVE + 5), nullptr))); + triggers.push_back(new TriggerNode("loken ranged", + NextAction::array(0, new NextAction("loken stack", ACTION_MOVE + 4), nullptr))); +} + +void WotlkDungeonHoLStrategy::InitMultipliers(std::vector &multipliers) +{ + multipliers.push_back(new BjarngrimMultiplier(botAI)); + multipliers.push_back(new VolkhanMultiplier(botAI)); + multipliers.push_back(new IonarMultiplier(botAI)); + multipliers.push_back(new LokenMultiplier(botAI)); +} diff --git a/src/strategy/dungeons/wotlk/hallsoflightning/HallsOfLightningStrategy.h b/src/strategy/dungeons/wotlk/hallsoflightning/HallsOfLightningStrategy.h new file mode 100644 index 00000000..8ee265dd --- /dev/null +++ b/src/strategy/dungeons/wotlk/hallsoflightning/HallsOfLightningStrategy.h @@ -0,0 +1,18 @@ +#ifndef _PLAYERBOT_WOTLKDUNGEONHOLSTRATEGY_H +#define _PLAYERBOT_WOTLKDUNGEONHOLSTRATEGY_H + +#include "Multiplier.h" +#include "AiObjectContext.h" +#include "Strategy.h" + + +class WotlkDungeonHoLStrategy : public Strategy +{ +public: + WotlkDungeonHoLStrategy(PlayerbotAI* ai) : Strategy(ai) {} + virtual std::string const getName() override { return "halls of lightning"; } + virtual void InitTriggers(std::vector &triggers) override; + virtual void InitMultipliers(std::vector &multipliers) override; +}; + +#endif diff --git a/src/strategy/dungeons/wotlk/hallsoflightning/HallsOfLightningTriggerContext.h b/src/strategy/dungeons/wotlk/hallsoflightning/HallsOfLightningTriggerContext.h new file mode 100644 index 00000000..5c8abc35 --- /dev/null +++ b/src/strategy/dungeons/wotlk/hallsoflightning/HallsOfLightningTriggerContext.h @@ -0,0 +1,35 @@ +#ifndef _PLAYERBOT_WOTLKDUNGEONHOLTRIGGERCONTEXT_H +#define _PLAYERBOT_WOTLKDUNGEONHOLTRIGGERCONTEXT_H + +#include "NamedObjectContext.h" +#include "AiObjectContext.h" +#include "HallsOfLightningTriggers.h" + +class WotlkDungeonHoLTriggerContext : public NamedObjectContext +{ + public: + WotlkDungeonHoLTriggerContext() + { + creators["stormforged lieutenant"] = &WotlkDungeonHoLTriggerContext::stormforged_lieutenant; + creators["whirlwind"] = &WotlkDungeonHoLTriggerContext::bjarngrim_whirlwind; + creators["volkhan"] = &WotlkDungeonHoLTriggerContext::volkhan; + creators["static overload"] = &WotlkDungeonHoLTriggerContext::static_overload; + creators["ball lightning"] = &WotlkDungeonHoLTriggerContext::ball_lightning; + creators["ionar tank aggro"] = &WotlkDungeonHoLTriggerContext::ionar_tank_aggro; + creators["ionar disperse"] = &WotlkDungeonHoLTriggerContext::ionar_disperse; + creators["loken ranged"] = &WotlkDungeonHoLTriggerContext::loken_ranged; + creators["lightning nova"] = &WotlkDungeonHoLTriggerContext::lightning_nova; + } + private: + static Trigger* stormforged_lieutenant(PlayerbotAI* ai) { return new StormforgedLieutenantTrigger(ai); } + static Trigger* bjarngrim_whirlwind(PlayerbotAI* ai) { return new BjarngrimWhirlwindTrigger(ai); } + static Trigger* volkhan(PlayerbotAI* ai) { return new VolkhanTrigger(ai); } + static Trigger* static_overload(PlayerbotAI* ai) { return new IonarStaticOverloadTrigger(ai); } + static Trigger* ball_lightning(PlayerbotAI* ai) { return new IonarBallLightningTrigger(ai); } + static Trigger* ionar_tank_aggro(PlayerbotAI* ai) { return new IonarTankAggroTrigger(ai); } + static Trigger* ionar_disperse(PlayerbotAI* ai) { return new IonarDisperseTrigger(ai); } + static Trigger* loken_ranged(PlayerbotAI* ai) { return new LokenRangedTrigger(ai); } + static Trigger* lightning_nova(PlayerbotAI* ai) { return new LokenLightningNovaTrigger(ai); } +}; + +#endif diff --git a/src/strategy/dungeons/wotlk/hallsoflightning/HallsOfLightningTriggers.cpp b/src/strategy/dungeons/wotlk/hallsoflightning/HallsOfLightningTriggers.cpp new file mode 100644 index 00000000..6350acc5 --- /dev/null +++ b/src/strategy/dungeons/wotlk/hallsoflightning/HallsOfLightningTriggers.cpp @@ -0,0 +1,92 @@ +#include "Playerbots.h" +#include "HallsOfLightningTriggers.h" +#include "AiObject.h" +#include "AiObjectContext.h" + +bool StormforgedLieutenantTrigger::IsActive() +{ + if (botAI->IsTank(bot) || botAI->IsHeal(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_STORMFORGED_LIEUTENANT) + { + return true; + } + } + return false; +} + +bool BjarngrimWhirlwindTrigger::IsActive() +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "general bjarngrim"); + if (!boss) { return false; } + + return boss->HasUnitState(UNIT_STATE_CASTING) && boss->FindCurrentSpellBySpellId(SPELL_WHIRLWIND_BJARNGRIM); +} + +bool VolkhanTrigger::IsActive() +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "volkhan"); + return boss && !botAI->IsTank(bot) && !botAI->IsHeal(bot); +} + +bool IonarStaticOverloadTrigger::IsActive() +{ + GuidVector members = AI_VALUE(GuidVector, "group members"); + for (auto& member : members) + { + Unit* unit = botAI->GetUnit(member); + if (unit && unit->HasAura(SPELL_STATIC_OVERLOAD)) + { + return true; + } + } + return false; +} + +bool IonarBallLightningTrigger::IsActive() +{ + if (botAI->IsMelee(bot)) { return false; } + + Unit* boss = AI_VALUE2(Unit*, "find target", "ionar"); + if (!boss) { return false; } + + return boss->HasUnitState(UNIT_STATE_CASTING) && boss->FindCurrentSpellBySpellId(SPELL_BALL_LIGHTNING); +} + +bool IonarTankAggroTrigger::IsActive() +{ + if (!botAI->IsTank(bot)) { return false; } + + Unit* boss = AI_VALUE2(Unit*, "find target", "ionar"); + if (!boss) { return false; } + + return AI_VALUE2(bool, "has aggro", "current target"); +} + +bool IonarDisperseTrigger::IsActive() +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "ionar"); + if (!boss) { return false; } + + return !bot->CanSeeOrDetect(boss) || boss->FindCurrentSpellBySpellId(SPELL_DISPERSE); +} + +bool LokenRangedTrigger::IsActive() +{ + return !botAI->IsMelee(bot) && AI_VALUE2(Unit*, "find target", "loken"); +} + +bool LokenLightningNovaTrigger::IsActive() +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "loken"); + if (!boss) { return false; } + + return boss->HasUnitState(UNIT_STATE_CASTING) && boss->FindCurrentSpellBySpellId(SPELL_LIGHTNING_NOVA); +} diff --git a/src/strategy/dungeons/wotlk/hallsoflightning/HallsOfLightningTriggers.h b/src/strategy/dungeons/wotlk/hallsoflightning/HallsOfLightningTriggers.h new file mode 100644 index 00000000..7dbbc461 --- /dev/null +++ b/src/strategy/dungeons/wotlk/hallsoflightning/HallsOfLightningTriggers.h @@ -0,0 +1,95 @@ +#ifndef _PLAYERBOT_WOTLKDUNGEONHOLTRIGGERS_H +#define _PLAYERBOT_WOTLKDUNGEONHOLTRIGGERS_H + +#include "Trigger.h" +#include "PlayerbotAIConfig.h" +#include "GenericTriggers.h" +#include "DungeonStrategyUtils.h" + +enum HallsOfLightningIDs +{ + // General Bjarngrim + NPC_STORMFORGED_LIEUTENANT = 29240, + SPELL_WHIRLWIND_BJARNGRIM = 52027, + + // Ionar + SPELL_STATIC_OVERLOAD_N = 52658, + SPELL_STATIC_OVERLOAD_H = 59795, + SPELL_BALL_LIGHTNING_N = 52780, + SPELL_BALL_LIGHTNING_H = 59800, + SPELL_DISPERSE = 52770, + NPC_SPARK_OF_IONAR = 28926, + + // Loken + SPELL_LIGHTNING_NOVA_N = 52960, + SPELL_LIGHTNING_NOVA_H = 59835, +}; + +#define SPELL_STATIC_OVERLOAD DUNGEON_MODE(bot, SPELL_STATIC_OVERLOAD_N, SPELL_STATIC_OVERLOAD_H) +#define SPELL_BALL_LIGHTNING DUNGEON_MODE(bot, SPELL_BALL_LIGHTNING_N, SPELL_BALL_LIGHTNING_H) +#define SPELL_LIGHTNING_NOVA DUNGEON_MODE(bot, SPELL_LIGHTNING_NOVA_N, SPELL_LIGHTNING_NOVA_H) + +class StormforgedLieutenantTrigger : public Trigger +{ +public: + StormforgedLieutenantTrigger(PlayerbotAI* ai) : Trigger(ai, "stormforged lieutenant") {} + bool IsActive() override; +}; + +class BjarngrimWhirlwindTrigger : public Trigger +{ +public: + BjarngrimWhirlwindTrigger(PlayerbotAI* ai) : Trigger(ai, "bjarngrim whirlwind") {} + bool IsActive() override; +}; + +class VolkhanTrigger : public Trigger +{ +public: + VolkhanTrigger(PlayerbotAI* ai) : Trigger(ai, "volkhan") {} + bool IsActive() override; +}; + +class IonarStaticOverloadTrigger : public Trigger +{ +public: + IonarStaticOverloadTrigger(PlayerbotAI* ai) : Trigger(ai, "ionar static overload") {} + bool IsActive() override; +}; + +class IonarBallLightningTrigger : public Trigger +{ +public: + IonarBallLightningTrigger(PlayerbotAI* ai) : Trigger(ai, "ionar ball lightning spread") {} + bool IsActive() override; +}; + +class IonarTankAggroTrigger : public Trigger +{ +public: + IonarTankAggroTrigger(PlayerbotAI* ai) : Trigger(ai, "ionar tank aggro") {} + bool IsActive() override; +}; + +class IonarDisperseTrigger : public Trigger +{ +public: + IonarDisperseTrigger(PlayerbotAI* ai) : Trigger(ai, "ionar disperse") {} + bool IsActive() override; +}; + +class LokenRangedTrigger : public Trigger +{ +public: + LokenRangedTrigger(PlayerbotAI* ai) : Trigger(ai, "loken ranged") {} + bool IsActive() override; +}; + +class LokenLightningNovaTrigger : public Trigger +{ +public: + LokenLightningNovaTrigger(PlayerbotAI* ai) : Trigger(ai, "lightning nova") {} + bool IsActive() override; +}; + +#endif diff --git a/src/strategy/dungeons/wotlk/hallsoflightning/TODO b/src/strategy/dungeons/wotlk/hallsoflightning/TODO deleted file mode 100644 index e69de29b..00000000