From 657bb9a6daf7b30b223c9a3b43c017db8bc95ef6 Mon Sep 17 00:00:00 2001 From: Bobblybook Date: Mon, 2 Dec 2024 00:25:36 +1100 Subject: [PATCH 1/3] Obsidian Sanctum implementation OS+2 implemented. - Kill Vesperon before fight - Mark Main Tank in raid interface Offtank still needs a bit of work, and dps needs to stop running around once they're safe. But it's usable currently. I think this should probably work for OS+1 and OS+0 with no changes but I was more concerned about implementing +2. --- src/PlayerbotAI.cpp | 3 + src/strategy/AiObjectContext.cpp | 4 + src/strategy/raids/RaidStrategyContext.h | 3 + .../obsidiansanctum/RaidOsActionContext.h | 30 +++ .../raids/obsidiansanctum/RaidOsActions.cpp | 246 ++++++++++++++++++ .../raids/obsidiansanctum/RaidOsActions.h | 64 +++++ .../obsidiansanctum/RaidOsMultipliers.cpp | 49 ++++ .../raids/obsidiansanctum/RaidOsMultipliers.h | 16 ++ .../raids/obsidiansanctum/RaidOsStrategy.cpp | 32 +++ .../raids/obsidiansanctum/RaidOsStrategy.h | 17 ++ .../obsidiansanctum/RaidOsTriggerContext.h | 32 +++ .../raids/obsidiansanctum/RaidOsTriggers.cpp | 128 +++++++++ .../raids/obsidiansanctum/RaidOsTriggers.h | 120 +++++++++ 13 files changed, 744 insertions(+) create mode 100644 src/strategy/raids/obsidiansanctum/RaidOsActionContext.h create mode 100644 src/strategy/raids/obsidiansanctum/RaidOsActions.cpp create mode 100644 src/strategy/raids/obsidiansanctum/RaidOsActions.h create mode 100644 src/strategy/raids/obsidiansanctum/RaidOsMultipliers.cpp create mode 100644 src/strategy/raids/obsidiansanctum/RaidOsMultipliers.h create mode 100644 src/strategy/raids/obsidiansanctum/RaidOsStrategy.cpp create mode 100644 src/strategy/raids/obsidiansanctum/RaidOsStrategy.h create mode 100644 src/strategy/raids/obsidiansanctum/RaidOsTriggerContext.h create mode 100644 src/strategy/raids/obsidiansanctum/RaidOsTriggers.cpp create mode 100644 src/strategy/raids/obsidiansanctum/RaidOsTriggers.h diff --git a/src/PlayerbotAI.cpp b/src/PlayerbotAI.cpp index 44e234d5..3b153a07 100644 --- a/src/PlayerbotAI.cpp +++ b/src/PlayerbotAI.cpp @@ -1603,6 +1603,9 @@ void PlayerbotAI::ApplyInstanceStrategies(uint32 mapId, bool tellMaster) case 608: strategyName = "wotlk-vh"; // Violet Hold break; + case 615: + strategyName = "wotlk-os"; // Obsidian Sanctum + break; case 619: strategyName = "wotlk-ok"; // Ahn'kahet: The Old Kingdom break; diff --git a/src/strategy/AiObjectContext.cpp b/src/strategy/AiObjectContext.cpp index fc791244..10e9e187 100644 --- a/src/strategy/AiObjectContext.cpp +++ b/src/strategy/AiObjectContext.cpp @@ -24,6 +24,8 @@ #include "raids/blackwinglair/RaidBwlTriggerContext.h" #include "raids/naxxramas/RaidNaxxActionContext.h" #include "raids/naxxramas/RaidNaxxTriggerContext.h" +#include "raids/obsidiansanctum/RaidOsActionContext.h" +#include "raids/obsidiansanctum/RaidOsTriggerContext.h" #include "raids/moltencore/RaidMcActionContext.h" #include "raids/moltencore/RaidMcTriggerContext.h" #include "raids/aq20/RaidAq20ActionContext.h" @@ -48,6 +50,7 @@ AiObjectContext::AiObjectContext(PlayerbotAI* botAI) : PlayerbotAIAware(botAI) actionContexts.Add(new RaidBwlActionContext()); actionContexts.Add(new RaidAq20ActionContext()); actionContexts.Add(new RaidNaxxActionContext()); + actionContexts.Add(new RaidOsActionContext()); actionContexts.Add(new RaidUlduarActionContext()); actionContexts.Add(new RaidIccActionContext()); actionContexts.Add(new WotlkDungeonUKActionContext()); @@ -71,6 +74,7 @@ AiObjectContext::AiObjectContext(PlayerbotAI* botAI) : PlayerbotAIAware(botAI) triggerContexts.Add(new RaidBwlTriggerContext()); triggerContexts.Add(new RaidAq20TriggerContext()); triggerContexts.Add(new RaidNaxxTriggerContext()); + triggerContexts.Add(new RaidOsTriggerContext()); triggerContexts.Add(new RaidUlduarTriggerContext()); triggerContexts.Add(new RaidIccTriggerContext()); triggerContexts.Add(new WotlkDungeonUKTriggerContext()); diff --git a/src/strategy/raids/RaidStrategyContext.h b/src/strategy/raids/RaidStrategyContext.h index af30ff14..31675f7f 100644 --- a/src/strategy/raids/RaidStrategyContext.h +++ b/src/strategy/raids/RaidStrategyContext.h @@ -5,6 +5,7 @@ #include "Strategy.h" #include "RaidBwlStrategy.h" #include "RaidNaxxStrategy.h" +#include "RaidOsStrategy.h" #include "RaidMcStrategy.h" #include "RaidAq20Strategy.h" #include "RaidIccStrategy.h" @@ -21,6 +22,7 @@ public: creators["bwl"] = &RaidStrategyContext::bwl; creators["aq20"] = &RaidStrategyContext::aq20; creators["naxx"] = &RaidStrategyContext::naxx; + creators["wotlk-os"] = &RaidStrategyContext::wotlk_os; creators["uld"] = &RaidStrategyContext::uld; creators["icc"] = &RaidStrategyContext::icc; } @@ -30,6 +32,7 @@ private: static Strategy* bwl(PlayerbotAI* botAI) { return new RaidBwlStrategy(botAI); } static Strategy* aq20(PlayerbotAI* botAI) { return new RaidAq20Strategy(botAI); } static Strategy* naxx(PlayerbotAI* botAI) { return new RaidNaxxStrategy(botAI); } + static Strategy* wotlk_os(PlayerbotAI* botAI) { return new RaidOsStrategy(botAI); } static Strategy* uld(PlayerbotAI* botAI) { return new RaidUlduarStrategy(botAI); } static Strategy* icc(PlayerbotAI* botAI) { return new RaidIccStrategy(botAI); } }; diff --git a/src/strategy/raids/obsidiansanctum/RaidOsActionContext.h b/src/strategy/raids/obsidiansanctum/RaidOsActionContext.h new file mode 100644 index 00000000..55afe526 --- /dev/null +++ b/src/strategy/raids/obsidiansanctum/RaidOsActionContext.h @@ -0,0 +1,30 @@ +#ifndef _PLAYERBOT_RAIDOSACTIONCONTEXT_H +#define _PLAYERBOT_RAIDOSACTIONCONTEXT_H + +#include "Action.h" +#include "NamedObjectContext.h" +#include "RaidOsActions.h" + +class RaidOsActionContext : public NamedObjectContext +{ +public: + RaidOsActionContext() + { + creators["sartharion tank position"] = &RaidOsActionContext::tank_position; + creators["avoid twilight fissure"] = &RaidOsActionContext::avoid_twilight_fissure; + creators["avoid flame tsunami"] = &RaidOsActionContext::avoid_flame_tsunami; + creators["sartharion attack priority"] = &RaidOsActionContext::attack_priority; + creators["enter twilight portal"] = &RaidOsActionContext::enter_twilight_portal; + creators["exit twilight portal"] = &RaidOsActionContext::exit_twilight_portal; + } + +private: + static Action* tank_position(PlayerbotAI* ai) { return new SartharionTankPositionAction(ai); } + static Action* avoid_twilight_fissure(PlayerbotAI* ai) { return new AvoidTwilightFissureAction(ai); } + static Action* avoid_flame_tsunami(PlayerbotAI* ai) { return new AvoidFlameTsunamiAction(ai); } + static Action* attack_priority(PlayerbotAI* ai) { return new SartharionAttackPriorityAction(ai); } + static Action* enter_twilight_portal(PlayerbotAI* ai) { return new EnterTwilightPortalAction(ai); } + static Action* exit_twilight_portal(PlayerbotAI* ai) { return new ExitTwilightPortalAction(ai); } +}; + +#endif diff --git a/src/strategy/raids/obsidiansanctum/RaidOsActions.cpp b/src/strategy/raids/obsidiansanctum/RaidOsActions.cpp new file mode 100644 index 00000000..2ee68bca --- /dev/null +++ b/src/strategy/raids/obsidiansanctum/RaidOsActions.cpp @@ -0,0 +1,246 @@ +#include "RaidOsActions.h" +#include "RaidOsTriggers.h" + +#include "Playerbots.h" + +bool SartharionTankPositionAction::Execute(Event event) +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "sartharion"); + if (!boss) { return false; } + + // Unit* shadron = AI_VALUE2(Unit*, "find target", "shadron"); + // Unit* tenebron = AI_VALUE2(Unit*, "find target", "tenebron"); + // Unit* vesperon = AI_VALUE2(Unit*, "find target", "vesperon"); + Unit* shadron = nullptr; + Unit* tenebron = nullptr; + Unit* vesperon = nullptr; + + // Detect incoming drakes before they are on aggro table + GuidVector targets = AI_VALUE(GuidVector, "possible targets no los"); + for (auto& target : targets) + { + Unit* unit = botAI->GetUnit(target); + if (!unit) { continue; } + + switch (unit->GetEntry()) + { + case NPC_SHADRON: + shadron = unit; + continue; + case NPC_TENEBRON: + tenebron = unit; + continue; + case NPC_VESPERON: + vesperon = unit; + continue; + default: + continue; + } + } + + Position currentPos = bot->GetPosition(); + // Adjustable, this is the acceptable distance to stack point that will be accepted as "safe" + float looseDistance = 12.0f; + + if (botAI->IsMainTank(bot)) + { + if (bot->GetExactDist2d(SARTHARION_MAINTANK_POSITION.first, SARTHARION_MAINTANK_POSITION.second) > looseDistance) + { + return MoveTo(OS_MAP_ID, SARTHARION_MAINTANK_POSITION.first, SARTHARION_MAINTANK_POSITION.second, currentPos.GetPositionZ(), + false, false, false, false, MovementPriority::MOVEMENT_COMBAT); + } + } + // Offtank grab drakes + else if (shadron || tenebron || vesperon) + { + float triggerDistance = 100.0f; + // Prioritise threat before positioning + if (tenebron && bot->GetExactDist2d(tenebron) < triggerDistance && + tenebron->GetTarget() != bot->GetGUID() && AI_VALUE(Unit*, "current target") != tenebron) + { + return Attack(tenebron); + } + if (shadron && bot->GetExactDist2d(shadron) < triggerDistance && + shadron->GetTarget() != bot->GetGUID() && AI_VALUE(Unit*, "current target") != shadron) + { + return Attack(shadron); + } + if (vesperon && bot->GetExactDist2d(vesperon) < triggerDistance && + vesperon->GetTarget() != bot->GetGUID() && AI_VALUE(Unit*, "current target") != vesperon) + { + return Attack(vesperon); + } + + bool drakeInCombat = (tenebron && bot->GetExactDist2d(tenebron) < triggerDistance) || + (shadron && bot->GetExactDist2d(shadron) < triggerDistance) || + (vesperon && bot->GetExactDist2d(vesperon) < triggerDistance); + // Offtank has threat on drakes, check positioning + if (drakeInCombat && bot->GetExactDist2d(SARTHARION_OFFTANK_POSITION.first, SARTHARION_OFFTANK_POSITION.second) > looseDistance) + { + return MoveTo(OS_MAP_ID, SARTHARION_OFFTANK_POSITION.first, SARTHARION_OFFTANK_POSITION.second, currentPos.GetPositionZ(), + false, false, false, false, MovementPriority::MOVEMENT_COMBAT); + } + } + return false; +} + +bool AvoidTwilightFissureAction::Execute(Event event) +{ + const float radius = 5.0f; + + GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); + for (auto& npc : npcs) + { + Unit* unit = botAI->GetUnit(npc); + if (unit && unit->GetEntry() == NPC_TWILIGHT_FISSURE) + { + float currentDistance = bot->GetDistance2d(unit); + if (currentDistance < radius) + { + return MoveAway(unit, radius - currentDistance); + } + } + } + return false; +} + +bool AvoidFlameTsunamiAction::Execute(Event event) +{ + // Adjustable, this is the acceptable distance to stack point that will be accepted as "safe" + float looseDistance = 4.0f; + + GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); + for (auto& npc : npcs) + { + Unit* unit = botAI->GetUnit(npc); + if (unit && unit->GetEntry() == NPC_FLAME_TSUNAMI) + { + Position currentPos = bot->GetPosition(); + + // I think these are centrepoints for the wave segments. Either way they uniquely identify the wave + // direction as they have different coords for the left and right waves + // int casting is not a mistake, need to avoid FP errors somehow. + // I always saw these accurate to around 6 decimal places, but if there are issues, + // can switch this to abs comparison of floats which would technically be more robust. + int posY = (int) unit->GetPositionY(); + if (posY == 505 || posY == 555) // RIGHT WAVE + { + bool wavePassed = currentPos.GetPositionX() > unit->GetPositionX(); + if (wavePassed) + { + return false; + } + + if (bot->GetExactDist2d(currentPos.GetPositionX(), TSUNAMI_RIGHT_SAFE_ALL) > looseDistance) + { + return MoveTo(OS_MAP_ID, currentPos.GetPositionX(), TSUNAMI_RIGHT_SAFE_ALL, currentPos.GetPositionZ(), + false, false, false, false, MovementPriority::MOVEMENT_COMBAT); + } + } + else // LEFT WAVE + { + bool wavePassed = currentPos.GetPositionX() < unit->GetPositionX(); + if (wavePassed) + { + return false; + } + + if (botAI->IsMelee(bot)) + { + if (bot->GetExactDist2d(currentPos.GetPositionX(), TSUNAMI_LEFT_SAFE_MELEE) > looseDistance) + { + return MoveTo(OS_MAP_ID, currentPos.GetPositionX(), TSUNAMI_LEFT_SAFE_MELEE, currentPos.GetPositionZ(), + false, false, false, false, MovementPriority::MOVEMENT_COMBAT); + } + } + else // Ranged/healers + { + if (bot->GetExactDist2d(currentPos.GetPositionX(), TSUNAMI_LEFT_SAFE_RANGED) > looseDistance) + { + return MoveTo(OS_MAP_ID, currentPos.GetPositionX(), TSUNAMI_LEFT_SAFE_RANGED, currentPos.GetPositionZ(), + false, false, false, false, MovementPriority::MOVEMENT_COMBAT); + } + } + } + } + } + return false; +} + +bool SartharionAttackPriorityAction::Execute(Event event) +{ + Unit* sartharion = AI_VALUE2(Unit*, "find target", "sartharion"); + Unit* shadron = AI_VALUE2(Unit*, "find target", "shadron"); + Unit* tenebron = AI_VALUE2(Unit*, "find target", "tenebron"); + Unit* vesperon = AI_VALUE2(Unit*, "find target", "vesperon"); + Unit* acolyte = AI_VALUE2(Unit*, "find target", "acolyte of shadron"); + + Unit* target = nullptr; + + if (acolyte) + { + target = acolyte; + } + else if (vesperon) + { + target = vesperon; + } + else if (tenebron) + { + target = tenebron; + } + else if (shadron) + { + target = shadron; + } + else if (sartharion) + { + target = sartharion; + } + + if (target && AI_VALUE(Unit*, "current target") != target) + { + return Attack(target); + } + + return false; +} + +bool EnterTwilightPortalAction::Execute(Event event) +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "sartharion"); + if (!boss || !boss->HasAura(SPELL_GIFT_OF_TWILIGHT_FIRE)) { return false; } + + GameObject* portal = bot->FindNearestGameObject(GO_TWILIGHT_PORTAL, 100.0f); + if (!portal) { return false; } + + if (!portal->IsAtInteractDistance(bot)) + { + return MoveTo(portal, fmaxf(portal->GetInteractionDistance() - 1.0f, 0.0f)); + } + + // Go through portal + WorldPacket data1(CMSG_GAMEOBJ_USE); + data1 << portal->GetGUID(); + bot->GetSession()->HandleGameObjectUseOpcode(data1); + + return true; +} + +bool ExitTwilightPortalAction::Execute(Event event) +{ + GameObject* portal = bot->FindNearestGameObject(GO_NORMAL_PORTAL, 100.0f); + if (!portal) { return false; } + + if (!portal->IsAtInteractDistance(bot)) + { + return MoveTo(portal, fmaxf(portal->GetInteractionDistance() - 1.0f, 0.0f)); + } + + // Go through portal + WorldPacket data1(CMSG_GAMEOBJ_USE); + data1 << portal->GetGUID(); + bot->GetSession()->HandleGameObjectUseOpcode(data1); + + return true; +} diff --git a/src/strategy/raids/obsidiansanctum/RaidOsActions.h b/src/strategy/raids/obsidiansanctum/RaidOsActions.h new file mode 100644 index 00000000..eba7c11d --- /dev/null +++ b/src/strategy/raids/obsidiansanctum/RaidOsActions.h @@ -0,0 +1,64 @@ +#ifndef _PLAYERBOT_RAIDOSACTIONS_H +#define _PLAYERBOT_RAIDOSACTIONS_H + +#include "MovementActions.h" +#include "AttackAction.h" +#include "PlayerbotAI.h" +#include "Playerbots.h" + +const float TSUNAMI_LEFT_SAFE_MELEE = 552.0f; +const float TSUNAMI_LEFT_SAFE_RANGED = 504.0f; +const float TSUNAMI_RIGHT_SAFE_ALL = 529.0f; +const std::pair SARTHARION_MAINTANK_POSITION = {3258.5f, 532.5f}; +const std::pair SARTHARION_OFFTANK_POSITION = {3230.0f, 526.0f}; +const std::pair SARTHARION_RANGED_POSITION = {3248.0f, 507.0f}; + +class SartharionTankPositionAction : public AttackAction +{ +public: + SartharionTankPositionAction(PlayerbotAI* botAI, std::string const name = "sartharion tank position") + : AttackAction(botAI, name) {} + bool Execute(Event event) override; +}; + +class AvoidTwilightFissureAction : public MovementAction +{ +public: + AvoidTwilightFissureAction(PlayerbotAI* botAI, std::string const name = "avoid twilight fissure") + : MovementAction(botAI, name) {} + bool Execute(Event event) override; +}; + +class AvoidFlameTsunamiAction : public MovementAction +{ +public: + AvoidFlameTsunamiAction(PlayerbotAI* botAI, std::string const name = "avoid flame tsunami") + : MovementAction(botAI, name) {} + bool Execute(Event event) override; +}; + +class SartharionAttackPriorityAction : public AttackAction +{ +public: + SartharionAttackPriorityAction(PlayerbotAI* botAI, std::string const name = "sartharion attack priority") + : AttackAction(botAI, name) {} + bool Execute(Event event) override; +}; + +class EnterTwilightPortalAction : public MovementAction +{ +public: + EnterTwilightPortalAction(PlayerbotAI* botAI, std::string const name = "enter twilight portal") + : MovementAction(botAI, name) {} + bool Execute(Event event) override; +}; + +class ExitTwilightPortalAction : public MovementAction +{ +public: + ExitTwilightPortalAction(PlayerbotAI* botAI, std::string const name = "exit twilight portal") + : MovementAction(botAI, name) {} + bool Execute(Event event) override; +}; + +#endif diff --git a/src/strategy/raids/obsidiansanctum/RaidOsMultipliers.cpp b/src/strategy/raids/obsidiansanctum/RaidOsMultipliers.cpp new file mode 100644 index 00000000..bc63f967 --- /dev/null +++ b/src/strategy/raids/obsidiansanctum/RaidOsMultipliers.cpp @@ -0,0 +1,49 @@ +#include "RaidOsMultipliers.h" + +#include "ChooseTargetActions.h" +#include "DKActions.h" +#include "DruidActions.h" +#include "DruidBearActions.h" +#include "FollowActions.h" +#include "GenericActions.h" +#include "GenericSpellActions.h" +#include "MovementActions.h" +#include "PaladinActions.h" +#include "RaidOsActions.h" +#include "RaidOsTriggers.h" +#include "ReachTargetActions.h" +#include "ScriptedCreature.h" +#include "WarriorActions.h" + +float SartharionMultiplier::GetValue(Action* action) +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "sartharion"); + if (!boss) { return 1.0f; } + + Unit* target = action->GetTarget(); + + if (botAI->IsMainTank(bot) && dynamic_cast(action)) + { + // return 0.0f; + } + + if (botAI->IsDps(bot) && dynamic_cast(action)) + { + return 0.0f; + } + + if (botAI->IsMainTank(bot) && target && target != boss && + (dynamic_cast(action) || dynamic_cast(action) || dynamic_cast(action) || + dynamic_cast(action) || dynamic_cast(action))) + { + return 0.0f; + } + + if (botAI->IsAssistTank(bot) && target && target == boss && + (dynamic_cast(action) || dynamic_cast(action) || + dynamic_cast(action) || dynamic_cast(action))) + { + return 0.0f; + } + return 1.0f; +} diff --git a/src/strategy/raids/obsidiansanctum/RaidOsMultipliers.h b/src/strategy/raids/obsidiansanctum/RaidOsMultipliers.h new file mode 100644 index 00000000..802335e1 --- /dev/null +++ b/src/strategy/raids/obsidiansanctum/RaidOsMultipliers.h @@ -0,0 +1,16 @@ + +#ifndef _PLAYERRBOT_RAIDOSMULTIPLIERS_H +#define _PLAYERRBOT_RAIDOSMULTIPLIERS_H + +#include "Multiplier.h" + +class SartharionMultiplier : public Multiplier +{ +public: + SartharionMultiplier(PlayerbotAI* ai) : Multiplier(ai, "sartharion") {} + +public: + virtual float GetValue(Action* action); +}; + +#endif diff --git a/src/strategy/raids/obsidiansanctum/RaidOsStrategy.cpp b/src/strategy/raids/obsidiansanctum/RaidOsStrategy.cpp new file mode 100644 index 00000000..508f7609 --- /dev/null +++ b/src/strategy/raids/obsidiansanctum/RaidOsStrategy.cpp @@ -0,0 +1,32 @@ +#include "RaidOsStrategy.h" +#include "RaidOsMultipliers.h" +#include "Strategy.h" + +void RaidOsStrategy::InitTriggers(std::vector& triggers) +{ + triggers.push_back( + new TriggerNode("sartharion tank", + NextAction::array(0, new NextAction("sartharion tank position", ACTION_MOVE), nullptr))); + triggers.push_back( + new TriggerNode("twilight fissure", + NextAction::array(0, new NextAction("avoid twilight fissure", ACTION_RAID + 2), nullptr))); + triggers.push_back( + new TriggerNode("flame tsunami", + NextAction::array(0, new NextAction("avoid flame tsunami", ACTION_RAID + 1), nullptr))); + triggers.push_back( + new TriggerNode("sartharion dps", + NextAction::array(0, new NextAction("sartharion attack priority", ACTION_RAID), nullptr))); + // Flank dragon positioning + triggers.push_back(new TriggerNode("sartharion melee positioning", + NextAction::array(0, new NextAction("rear flank", ACTION_MOVE + 4), nullptr))); + + triggers.push_back(new TriggerNode("twilight portal enter", + NextAction::array(0, new NextAction("enter twilight portal", ACTION_RAID + 1), nullptr))); + triggers.push_back(new TriggerNode("twilight portal exit", + NextAction::array(0, new NextAction("exit twilight portal", ACTION_RAID + 1), nullptr))); +} + +void RaidOsStrategy::InitMultipliers(std::vector &multipliers) +{ + multipliers.push_back(new SartharionMultiplier(botAI)); +} diff --git a/src/strategy/raids/obsidiansanctum/RaidOsStrategy.h b/src/strategy/raids/obsidiansanctum/RaidOsStrategy.h new file mode 100644 index 00000000..44983f1f --- /dev/null +++ b/src/strategy/raids/obsidiansanctum/RaidOsStrategy.h @@ -0,0 +1,17 @@ +#ifndef _PLAYERBOT_RAIDOSSTRATEGY_H +#define _PLAYERBOT_RAIDOSSTRATEGY_H + +#include "AiObjectContext.h" +#include "Multiplier.h" +#include "Strategy.h" + +class RaidOsStrategy : public Strategy +{ +public: + RaidOsStrategy(PlayerbotAI* ai) : Strategy(ai) {} + virtual std::string const getName() override { return "wotlk-os"; } + virtual void InitTriggers(std::vector &triggers) override; + virtual void InitMultipliers(std::vector &multipliers) override; +}; + +#endif diff --git a/src/strategy/raids/obsidiansanctum/RaidOsTriggerContext.h b/src/strategy/raids/obsidiansanctum/RaidOsTriggerContext.h new file mode 100644 index 00000000..b8a1f4b3 --- /dev/null +++ b/src/strategy/raids/obsidiansanctum/RaidOsTriggerContext.h @@ -0,0 +1,32 @@ +#ifndef _PLAYERBOT_RAIDOSTRIGGERCONTEXT_H +#define _PLAYERBOT_RAIDOSTRIGGERCONTEXT_H + +#include "AiObjectContext.h" +#include "NamedObjectContext.h" +#include "RaidOsTriggers.h" + +class RaidOsTriggerContext : public NamedObjectContext +{ +public: + RaidOsTriggerContext() + { + creators["sartharion tank"] = &RaidOsTriggerContext::sartharion_tank; + creators["flame tsunami"] = &RaidOsTriggerContext::flame_tsunami; + creators["twilight fissure"] = &RaidOsTriggerContext::twilight_fissure; + creators["sartharion dps"] = &RaidOsTriggerContext::sartharion_dps; + creators["sartharion melee positioning"] = &RaidOsTriggerContext::sartharion_melee; + creators["twilight portal enter"] = &RaidOsTriggerContext::twilight_portal_enter; + creators["twilight portal exit"] = &RaidOsTriggerContext::twilight_portal_exit; + } + +private: + static Trigger* sartharion_tank(PlayerbotAI* ai) { return new SartharionTankTrigger(ai); } + static Trigger* flame_tsunami(PlayerbotAI* ai) { return new FlameTsunamiTrigger(ai); } + static Trigger* twilight_fissure(PlayerbotAI* ai) { return new TwilightFissureTrigger(ai); } + static Trigger* sartharion_dps(PlayerbotAI* ai) { return new SartharionDpsTrigger(ai); } + static Trigger* sartharion_melee(PlayerbotAI* ai) { return new SartharionMeleePositioningTrigger(ai); } + static Trigger* twilight_portal_enter(PlayerbotAI* ai) { return new TwilightPortalEnterTrigger(ai); } + static Trigger* twilight_portal_exit(PlayerbotAI* ai) { return new TwilightPortalExitTrigger(ai); } +}; + +#endif diff --git a/src/strategy/raids/obsidiansanctum/RaidOsTriggers.cpp b/src/strategy/raids/obsidiansanctum/RaidOsTriggers.cpp new file mode 100644 index 00000000..45b3b12f --- /dev/null +++ b/src/strategy/raids/obsidiansanctum/RaidOsTriggers.cpp @@ -0,0 +1,128 @@ +#include "RaidOsTriggers.h" + +#include "SharedDefines.h" + +bool SartharionTankTrigger::IsActive() +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "sartharion"); + if (!boss) { return false; } + + return botAI->IsTank(bot); +} + +bool FlameTsunamiTrigger::IsActive() +{ + if (botAI->IsTank(bot)) { return false; } + + Unit* boss = AI_VALUE2(Unit*, "find target", "sartharion"); + if (!boss) { return false; } + + + GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); + for (auto& npc : npcs) + { + Unit* unit = botAI->GetUnit(npc); + if (unit) + { + if (unit->GetEntry() == NPC_FLAME_TSUNAMI) + { + return true; + } + } + } + + return false; +} + +bool TwilightFissureTrigger::IsActive() +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "sartharion"); + if (!boss) { return false; } + + + GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); + for (auto& npc : npcs) + { + Unit* unit = botAI->GetUnit(npc); + if (unit) + { + if (unit->GetEntry() == NPC_TWILIGHT_FISSURE) + { + return true; + } + } + } + + return false; +} + +bool SartharionDpsTrigger::IsActive() +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "sartharion"); + if (!boss) { return false; } + + return botAI->IsDps(bot); +} + +bool SartharionMeleePositioningTrigger::IsActive() +{ + if (!botAI->IsMelee(bot) || !botAI->IsDps(bot)) { return false; } + + Unit* boss = AI_VALUE2(Unit*, "find target", "sartharion"); + if (!boss) { return false; } + + Unit* shadron = AI_VALUE2(Unit*, "find target", "shadron"); + Unit* tenebron = AI_VALUE2(Unit*, "find target", "tenebron"); + Unit* vesperon = AI_VALUE2(Unit*, "find target", "vesperon"); + + return !(shadron || tenebron || vesperon); +} + +bool TwilightPortalEnterTrigger::IsActive() +{ + if (botAI->IsMainTank(bot) || botAI->IsHealAssistantOfIndex(bot, 0)) { return false; } + + // In 25-man, take two healers in. Otherwise just take one + // if (bot->GetRaidDifficulty() == RAID_DIFFICULTY_25MAN_NORMAL) + // { + // if (botAI->IsHealAssistantOfIndex(bot, 0) || botAI->IsHealAssistantOfIndex(bot, 1)) + // { + // return false; + // } + // } + // else + // { + // if (botAI->IsHealAssistantOfIndex(bot, 0)) + // { + // return false; + // } + // } + + + // Don't enter portal until drakes are dead + if (bot->HasAura(SPELL_POWER_OF_SHADRON) || + bot->HasAura(SPELL_POWER_OF_TENEBRON) || + bot->HasAura(SPELL_POWER_OF_VESPERON)) + { + return false; + } + + Unit* boss = AI_VALUE2(Unit*, "find target", "sartharion"); + if (!boss) { return false; } + + // GuidVector objects = AI_VALUE(GuidVector, "nearest game objects no los"); + // for (auto& object : objects) + // { + // GameObject* go = botAI->GetGameObject(object); + // if (go && go->GetEntry() == GO_TWILIGHT_PORTAL) + // { + // return true; + // } + // } + return bool(bot->FindNearestGameObject(GO_TWILIGHT_PORTAL, 100.0f)); +} + +bool TwilightPortalExitTrigger::IsActive() +{ + return bot->HasAura(SPELL_TWILIGHT_SHIFT) && !AI_VALUE2(Unit*, "find target", "acolyte of shadron"); +} \ No newline at end of file diff --git a/src/strategy/raids/obsidiansanctum/RaidOsTriggers.h b/src/strategy/raids/obsidiansanctum/RaidOsTriggers.h new file mode 100644 index 00000000..b7a45703 --- /dev/null +++ b/src/strategy/raids/obsidiansanctum/RaidOsTriggers.h @@ -0,0 +1,120 @@ +#ifndef _PLAYERBOT_RAIDOSTRIGGERS_H +#define _PLAYERBOT_RAIDOSTRIGGERS_H + +#include "PlayerbotAI.h" +#include "Playerbots.h" +#include "Trigger.h" + +enum ObsidianSanctumIDs +{ + // Bosses + NPC_SARTHARION = 28860, + NPC_SHADRON = 30451, + NPC_TENEBRON = 30452, + NPC_VESPERON = 30449, + + // Mini-boss shared + SPELL_SHADOW_BREATH = 57570, + SPELL_SHADOW_FISSURE = 57579, + SPELL_SUMMON_TWILIGHT_WHELP = 58035, + SPELL_GIFT_OF_TWILIGHT_SHADOW = 57835, + SPELL_TWILIGHT_TORMENT_VESPERON = 57935, + + // Sartharion + SPELL_SARTHARION_CLEAVE = 56909, + SPELL_SARTHARION_FLAME_BREATH = 56908, + SPELL_SARTHARION_TAIL_LASH = 56910, + SPELL_CYCLONE_AURA_PERIODIC = 57598, + SPELL_LAVA_STRIKE_DUMMY = 57578, + SPELL_LAVA_STRIKE_DUMMY_TRIGGER = 57697, + SPELL_LAVA_STRIKE_SUMMON = 57572, + SPELL_SARTHARION_PYROBUFFET = 56916, + SPELL_SARTHARION_BERSERK = 61632, + SPELL_SARTHARION_TWILIGHT_REVENGE = 60639, + + // Sartharion with drakes + SPELL_WILL_OF_SARTHARION = 61254, + SPELL_POWER_OF_TENEBRON = 61248, + SPELL_POWER_OF_VESPERON = 61251, + SPELL_POWER_OF_SHADRON = 58105, + SPELL_GIFT_OF_TWILIGHT_FIRE = 58766, + + // Visuals + SPELL_EGG_MARKER_VISUAL = 58547, + SPELL_FLAME_TSUNAMI_VISUAL = 57494, + + // Misc + SPELL_FADE_ARMOR = 60708, + SPELL_FLAME_TSUNAMI_DAMAGE_AURA = 57492, + SPELL_FLAME_TSUNAMI_LEAP = 60241, + SPELL_SARTHARION_PYROBUFFET_TRIGGER = 57557, + + NPC_TWILIGHT_EGG = 30882, + NPC_TWILIGHT_WHELP = 30890, + NPC_DISCIPLE_OF_SHADRON = 30688, + NPC_DISCIPLE_OF_VESPERON = 30858, + NPC_ACOLYTE_OF_SHADRON = 31218, + NPC_ACOLYTE_OF_VESPERON = 31219, + + // Sartharion fight + NPC_LAVA_BLAZE = 30643, + NPC_FLAME_TSUNAMI = 30616, + NPC_SAFE_AREA_TRIGGER = 30494, + NPC_TWILIGHT_FISSURE = 30641, + GO_TWILIGHT_PORTAL = 193988, + GO_NORMAL_PORTAL = 193989, + SPELL_TWILIGHT_SHIFT = 57874, +}; + +const uint32 OS_MAP_ID = 615; + +class SartharionTankTrigger : public Trigger +{ +public: + SartharionTankTrigger(PlayerbotAI* botAI) : Trigger(botAI, "sartharion tank") {} + bool IsActive() override; +}; + +class FlameTsunamiTrigger : public Trigger +{ +public: + FlameTsunamiTrigger(PlayerbotAI* botAI) : Trigger(botAI, "flame tsunami") {} + bool IsActive() override; +}; + +class TwilightFissureTrigger : public Trigger +{ +public: + TwilightFissureTrigger(PlayerbotAI* botAI) : Trigger(botAI, "twilight fissure") {} + bool IsActive() override; +}; + +class SartharionDpsTrigger : public Trigger +{ +public: + SartharionDpsTrigger(PlayerbotAI* botAI) : Trigger(botAI, "sartharion dps") {} + bool IsActive() override; +}; + +class SartharionMeleePositioningTrigger : public Trigger +{ +public: + SartharionMeleePositioningTrigger(PlayerbotAI* botAI) : Trigger(botAI, "sartharion melee positioning") {} + bool IsActive() override; +}; + +class TwilightPortalEnterTrigger : public Trigger +{ +public: + TwilightPortalEnterTrigger(PlayerbotAI* botAI) : Trigger(botAI, "twilight portal enter") {} + bool IsActive() override; +}; + +class TwilightPortalExitTrigger : public Trigger +{ +public: + TwilightPortalExitTrigger(PlayerbotAI* botAI) : Trigger(botAI, "twilight portal exit") {} + bool IsActive() override; +}; + +#endif From f20e5d76d1290cbf6ee2d169140b89b964725ae8 Mon Sep 17 00:00:00 2001 From: Bobblybook Date: Tue, 3 Dec 2024 21:13:28 +1100 Subject: [PATCH 2/3] Eye of Eternity skeleton --- src/PlayerbotAI.cpp | 3 + src/strategy/AiObjectContext.cpp | 4 + .../eyeofeternity/RaidEoEActionContext.h | 30 +++ .../raids/eyeofeternity/RaidEoEActions.cpp | 246 ++++++++++++++++++ .../raids/eyeofeternity/RaidEoEActions.h | 64 +++++ .../eyeofeternity/RaidEoEMultipliers.cpp | 49 ++++ .../raids/eyeofeternity/RaidEoEMultipliers.h | 16 ++ .../raids/eyeofeternity/RaidEoEStrategy.cpp | 32 +++ .../raids/eyeofeternity/RaidEoEStrategy.h | 17 ++ .../eyeofeternity/RaidEoETriggerContext.h | 32 +++ .../raids/eyeofeternity/RaidEoETriggers.cpp | 128 +++++++++ .../raids/eyeofeternity/RaidEoETriggers.h | 120 +++++++++ 12 files changed, 741 insertions(+) create mode 100644 src/strategy/raids/eyeofeternity/RaidEoEActionContext.h create mode 100644 src/strategy/raids/eyeofeternity/RaidEoEActions.cpp create mode 100644 src/strategy/raids/eyeofeternity/RaidEoEActions.h create mode 100644 src/strategy/raids/eyeofeternity/RaidEoEMultipliers.cpp create mode 100644 src/strategy/raids/eyeofeternity/RaidEoEMultipliers.h create mode 100644 src/strategy/raids/eyeofeternity/RaidEoEStrategy.cpp create mode 100644 src/strategy/raids/eyeofeternity/RaidEoEStrategy.h create mode 100644 src/strategy/raids/eyeofeternity/RaidEoETriggerContext.h create mode 100644 src/strategy/raids/eyeofeternity/RaidEoETriggers.cpp create mode 100644 src/strategy/raids/eyeofeternity/RaidEoETriggers.h diff --git a/src/PlayerbotAI.cpp b/src/PlayerbotAI.cpp index 3b153a07..c2a5895f 100644 --- a/src/PlayerbotAI.cpp +++ b/src/PlayerbotAI.cpp @@ -1606,6 +1606,9 @@ void PlayerbotAI::ApplyInstanceStrategies(uint32 mapId, bool tellMaster) case 615: strategyName = "wotlk-os"; // Obsidian Sanctum break; + case 616: + strategyName = "wotlk-eoe"; // Eye Of Eternity + break; case 619: strategyName = "wotlk-ok"; // Ahn'kahet: The Old Kingdom break; diff --git a/src/strategy/AiObjectContext.cpp b/src/strategy/AiObjectContext.cpp index 10e9e187..12e741b6 100644 --- a/src/strategy/AiObjectContext.cpp +++ b/src/strategy/AiObjectContext.cpp @@ -26,6 +26,8 @@ #include "raids/naxxramas/RaidNaxxTriggerContext.h" #include "raids/obsidiansanctum/RaidOsActionContext.h" #include "raids/obsidiansanctum/RaidOsTriggerContext.h" +#include "raids/eyeofeternity/RaidEoEActionContext.h" +#include "raids/eyeofeternity/RaidEoETriggerContext.h" #include "raids/moltencore/RaidMcActionContext.h" #include "raids/moltencore/RaidMcTriggerContext.h" #include "raids/aq20/RaidAq20ActionContext.h" @@ -51,6 +53,7 @@ AiObjectContext::AiObjectContext(PlayerbotAI* botAI) : PlayerbotAIAware(botAI) actionContexts.Add(new RaidAq20ActionContext()); actionContexts.Add(new RaidNaxxActionContext()); actionContexts.Add(new RaidOsActionContext()); + actionContexts.Add(new RaidEoEActionContext()); actionContexts.Add(new RaidUlduarActionContext()); actionContexts.Add(new RaidIccActionContext()); actionContexts.Add(new WotlkDungeonUKActionContext()); @@ -75,6 +78,7 @@ AiObjectContext::AiObjectContext(PlayerbotAI* botAI) : PlayerbotAIAware(botAI) triggerContexts.Add(new RaidAq20TriggerContext()); triggerContexts.Add(new RaidNaxxTriggerContext()); triggerContexts.Add(new RaidOsTriggerContext()); + triggerContexts.Add(new RaidEoETriggerContext()); triggerContexts.Add(new RaidUlduarTriggerContext()); triggerContexts.Add(new RaidIccTriggerContext()); triggerContexts.Add(new WotlkDungeonUKTriggerContext()); diff --git a/src/strategy/raids/eyeofeternity/RaidEoEActionContext.h b/src/strategy/raids/eyeofeternity/RaidEoEActionContext.h new file mode 100644 index 00000000..1250b26b --- /dev/null +++ b/src/strategy/raids/eyeofeternity/RaidEoEActionContext.h @@ -0,0 +1,30 @@ +#ifndef _PLAYERBOT_RAIDEOEACTIONCONTEXT_H +#define _PLAYERBOT_RAIDEOEACTIONCONTEXT_H + +#include "Action.h" +#include "NamedObjectContext.h" +#include "RaidEoEActions.h" + +class RaidEoEActionContext : public NamedObjectContext +{ +public: + RaidEoEActionContext() + { + // creators["sartharion tank position"] = &RaidOsActionContext::tank_position; + // creators["avoid twilight fissure"] = &RaidOsActionContext::avoid_twilight_fissure; + // creators["avoid flame tsunami"] = &RaidOsActionContext::avoid_flame_tsunami; + // creators["sartharion attack priority"] = &RaidOsActionContext::attack_priority; + // creators["enter twilight portal"] = &RaidOsActionContext::enter_twilight_portal; + // creators["exit twilight portal"] = &RaidOsActionContext::exit_twilight_portal; + } + +private: + // static Action* tank_position(PlayerbotAI* ai) { return new SartharionTankPositionAction(ai); } + // static Action* avoid_twilight_fissure(PlayerbotAI* ai) { return new AvoidTwilightFissureAction(ai); } + // static Action* avoid_flame_tsunami(PlayerbotAI* ai) { return new AvoidFlameTsunamiAction(ai); } + // static Action* attack_priority(PlayerbotAI* ai) { return new SartharionAttackPriorityAction(ai); } + // static Action* enter_twilight_portal(PlayerbotAI* ai) { return new EnterTwilightPortalAction(ai); } + // static Action* exit_twilight_portal(PlayerbotAI* ai) { return new ExitTwilightPortalAction(ai); } +}; + +#endif diff --git a/src/strategy/raids/eyeofeternity/RaidEoEActions.cpp b/src/strategy/raids/eyeofeternity/RaidEoEActions.cpp new file mode 100644 index 00000000..0166d92e --- /dev/null +++ b/src/strategy/raids/eyeofeternity/RaidEoEActions.cpp @@ -0,0 +1,246 @@ +#include "RaidEoEActions.h" +#include "RaidEoETriggers.h" + +#include "Playerbots.h" + +// bool SartharionTankPositionAction::Execute(Event event) +// { +// Unit* boss = AI_VALUE2(Unit*, "find target", "sartharion"); +// if (!boss) { return false; } + +// // Unit* shadron = AI_VALUE2(Unit*, "find target", "shadron"); +// // Unit* tenebron = AI_VALUE2(Unit*, "find target", "tenebron"); +// // Unit* vesperon = AI_VALUE2(Unit*, "find target", "vesperon"); +// Unit* shadron = nullptr; +// Unit* tenebron = nullptr; +// Unit* vesperon = nullptr; + +// // Detect incoming drakes before they are on aggro table +// GuidVector targets = AI_VALUE(GuidVector, "possible targets no los"); +// for (auto& target : targets) +// { +// Unit* unit = botAI->GetUnit(target); +// if (!unit) { continue; } + +// switch (unit->GetEntry()) +// { +// case NPC_SHADRON: +// shadron = unit; +// continue; +// case NPC_TENEBRON: +// tenebron = unit; +// continue; +// case NPC_VESPERON: +// vesperon = unit; +// continue; +// default: +// continue; +// } +// } + +// Position currentPos = bot->GetPosition(); +// // Adjustable, this is the acceptable distance to stack point that will be accepted as "safe" +// float looseDistance = 12.0f; + +// if (botAI->IsMainTank(bot)) +// { +// if (bot->GetExactDist2d(SARTHARION_MAINTANK_POSITION.first, SARTHARION_MAINTANK_POSITION.second) > looseDistance) +// { +// return MoveTo(OS_MAP_ID, SARTHARION_MAINTANK_POSITION.first, SARTHARION_MAINTANK_POSITION.second, currentPos.GetPositionZ(), +// false, false, false, false, MovementPriority::MOVEMENT_COMBAT); +// } +// } +// // Offtank grab drakes +// else if (shadron || tenebron || vesperon) +// { +// float triggerDistance = 100.0f; +// // Prioritise threat before positioning +// if (tenebron && bot->GetExactDist2d(tenebron) < triggerDistance && +// tenebron->GetTarget() != bot->GetGUID() && AI_VALUE(Unit*, "current target") != tenebron) +// { +// return Attack(tenebron); +// } +// if (shadron && bot->GetExactDist2d(shadron) < triggerDistance && +// shadron->GetTarget() != bot->GetGUID() && AI_VALUE(Unit*, "current target") != shadron) +// { +// return Attack(shadron); +// } +// if (vesperon && bot->GetExactDist2d(vesperon) < triggerDistance && +// vesperon->GetTarget() != bot->GetGUID() && AI_VALUE(Unit*, "current target") != vesperon) +// { +// return Attack(vesperon); +// } + +// bool drakeInCombat = (tenebron && bot->GetExactDist2d(tenebron) < triggerDistance) || +// (shadron && bot->GetExactDist2d(shadron) < triggerDistance) || +// (vesperon && bot->GetExactDist2d(vesperon) < triggerDistance); +// // Offtank has threat on drakes, check positioning +// if (drakeInCombat && bot->GetExactDist2d(SARTHARION_OFFTANK_POSITION.first, SARTHARION_OFFTANK_POSITION.second) > looseDistance) +// { +// return MoveTo(OS_MAP_ID, SARTHARION_OFFTANK_POSITION.first, SARTHARION_OFFTANK_POSITION.second, currentPos.GetPositionZ(), +// false, false, false, false, MovementPriority::MOVEMENT_COMBAT); +// } +// } +// return false; +// } + +// bool AvoidTwilightFissureAction::Execute(Event event) +// { +// const float radius = 5.0f; + +// GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); +// for (auto& npc : npcs) +// { +// Unit* unit = botAI->GetUnit(npc); +// if (unit && unit->GetEntry() == NPC_TWILIGHT_FISSURE) +// { +// float currentDistance = bot->GetDistance2d(unit); +// if (currentDistance < radius) +// { +// return MoveAway(unit, radius - currentDistance); +// } +// } +// } +// return false; +// } + +// bool AvoidFlameTsunamiAction::Execute(Event event) +// { +// // Adjustable, this is the acceptable distance to stack point that will be accepted as "safe" +// float looseDistance = 4.0f; + +// GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); +// for (auto& npc : npcs) +// { +// Unit* unit = botAI->GetUnit(npc); +// if (unit && unit->GetEntry() == NPC_FLAME_TSUNAMI) +// { +// Position currentPos = bot->GetPosition(); + +// // I think these are centrepoints for the wave segments. Either way they uniquely identify the wave +// // direction as they have different coords for the left and right waves +// // int casting is not a mistake, need to avoid FP errors somehow. +// // I always saw these accurate to around 6 decimal places, but if there are issues, +// // can switch this to abs comparison of floats which would technically be more robust. +// int posY = (int) unit->GetPositionY(); +// if (posY == 505 || posY == 555) // RIGHT WAVE +// { +// bool wavePassed = currentPos.GetPositionX() > unit->GetPositionX(); +// if (wavePassed) +// { +// return false; +// } + +// if (bot->GetExactDist2d(currentPos.GetPositionX(), TSUNAMI_RIGHT_SAFE_ALL) > looseDistance) +// { +// return MoveTo(OS_MAP_ID, currentPos.GetPositionX(), TSUNAMI_RIGHT_SAFE_ALL, currentPos.GetPositionZ(), +// false, false, false, false, MovementPriority::MOVEMENT_COMBAT); +// } +// } +// else // LEFT WAVE +// { +// bool wavePassed = currentPos.GetPositionX() < unit->GetPositionX(); +// if (wavePassed) +// { +// return false; +// } + +// if (botAI->IsMelee(bot)) +// { +// if (bot->GetExactDist2d(currentPos.GetPositionX(), TSUNAMI_LEFT_SAFE_MELEE) > looseDistance) +// { +// return MoveTo(OS_MAP_ID, currentPos.GetPositionX(), TSUNAMI_LEFT_SAFE_MELEE, currentPos.GetPositionZ(), +// false, false, false, false, MovementPriority::MOVEMENT_COMBAT); +// } +// } +// else // Ranged/healers +// { +// if (bot->GetExactDist2d(currentPos.GetPositionX(), TSUNAMI_LEFT_SAFE_RANGED) > looseDistance) +// { +// return MoveTo(OS_MAP_ID, currentPos.GetPositionX(), TSUNAMI_LEFT_SAFE_RANGED, currentPos.GetPositionZ(), +// false, false, false, false, MovementPriority::MOVEMENT_COMBAT); +// } +// } +// } +// } +// } +// return false; +// } + +// bool SartharionAttackPriorityAction::Execute(Event event) +// { +// Unit* sartharion = AI_VALUE2(Unit*, "find target", "sartharion"); +// Unit* shadron = AI_VALUE2(Unit*, "find target", "shadron"); +// Unit* tenebron = AI_VALUE2(Unit*, "find target", "tenebron"); +// Unit* vesperon = AI_VALUE2(Unit*, "find target", "vesperon"); +// Unit* acolyte = AI_VALUE2(Unit*, "find target", "acolyte of shadron"); + +// Unit* target = nullptr; + +// if (acolyte) +// { +// target = acolyte; +// } +// else if (vesperon) +// { +// target = vesperon; +// } +// else if (tenebron) +// { +// target = tenebron; +// } +// else if (shadron) +// { +// target = shadron; +// } +// else if (sartharion) +// { +// target = sartharion; +// } + +// if (target && AI_VALUE(Unit*, "current target") != target) +// { +// return Attack(target); +// } + +// return false; +// } + +// bool EnterTwilightPortalAction::Execute(Event event) +// { +// Unit* boss = AI_VALUE2(Unit*, "find target", "sartharion"); +// if (!boss || !boss->HasAura(SPELL_GIFT_OF_TWILIGHT_FIRE)) { return false; } + +// GameObject* portal = bot->FindNearestGameObject(GO_TWILIGHT_PORTAL, 100.0f); +// if (!portal) { return false; } + +// if (!portal->IsAtInteractDistance(bot)) +// { +// return MoveTo(portal, fmaxf(portal->GetInteractionDistance() - 1.0f, 0.0f)); +// } + +// // Go through portal +// WorldPacket data1(CMSG_GAMEOBJ_USE); +// data1 << portal->GetGUID(); +// bot->GetSession()->HandleGameObjectUseOpcode(data1); + +// return true; +// } + +// bool ExitTwilightPortalAction::Execute(Event event) +// { +// GameObject* portal = bot->FindNearestGameObject(GO_NORMAL_PORTAL, 100.0f); +// if (!portal) { return false; } + +// if (!portal->IsAtInteractDistance(bot)) +// { +// return MoveTo(portal, fmaxf(portal->GetInteractionDistance() - 1.0f, 0.0f)); +// } + +// // Go through portal +// WorldPacket data1(CMSG_GAMEOBJ_USE); +// data1 << portal->GetGUID(); +// bot->GetSession()->HandleGameObjectUseOpcode(data1); + +// return true; +// } diff --git a/src/strategy/raids/eyeofeternity/RaidEoEActions.h b/src/strategy/raids/eyeofeternity/RaidEoEActions.h new file mode 100644 index 00000000..b5173f71 --- /dev/null +++ b/src/strategy/raids/eyeofeternity/RaidEoEActions.h @@ -0,0 +1,64 @@ +#ifndef _PLAYERBOT_RAIDEOEACTIONS_H +#define _PLAYERBOT_RAIDEOEACTIONS_H + +#include "MovementActions.h" +#include "AttackAction.h" +#include "PlayerbotAI.h" +#include "Playerbots.h" + +// const float TSUNAMI_LEFT_SAFE_MELEE = 552.0f; +// const float TSUNAMI_LEFT_SAFE_RANGED = 504.0f; +// const float TSUNAMI_RIGHT_SAFE_ALL = 529.0f; +// const std::pair SARTHARION_MAINTANK_POSITION = {3258.5f, 532.5f}; +// const std::pair SARTHARION_OFFTANK_POSITION = {3230.0f, 526.0f}; +// const std::pair SARTHARION_RANGED_POSITION = {3248.0f, 507.0f}; + +// class SartharionTankPositionAction : public AttackAction +// { +// public: +// SartharionTankPositionAction(PlayerbotAI* botAI, std::string const name = "sartharion tank position") +// : AttackAction(botAI, name) {} +// bool Execute(Event event) override; +// }; + +// class AvoidTwilightFissureAction : public MovementAction +// { +// public: +// AvoidTwilightFissureAction(PlayerbotAI* botAI, std::string const name = "avoid twilight fissure") +// : MovementAction(botAI, name) {} +// bool Execute(Event event) override; +// }; + +// class AvoidFlameTsunamiAction : public MovementAction +// { +// public: +// AvoidFlameTsunamiAction(PlayerbotAI* botAI, std::string const name = "avoid flame tsunami") +// : MovementAction(botAI, name) {} +// bool Execute(Event event) override; +// }; + +// class SartharionAttackPriorityAction : public AttackAction +// { +// public: +// SartharionAttackPriorityAction(PlayerbotAI* botAI, std::string const name = "sartharion attack priority") +// : AttackAction(botAI, name) {} +// bool Execute(Event event) override; +// }; + +// class EnterTwilightPortalAction : public MovementAction +// { +// public: +// EnterTwilightPortalAction(PlayerbotAI* botAI, std::string const name = "enter twilight portal") +// : MovementAction(botAI, name) {} +// bool Execute(Event event) override; +// }; + +// class ExitTwilightPortalAction : public MovementAction +// { +// public: +// ExitTwilightPortalAction(PlayerbotAI* botAI, std::string const name = "exit twilight portal") +// : MovementAction(botAI, name) {} +// bool Execute(Event event) override; +// }; + +#endif diff --git a/src/strategy/raids/eyeofeternity/RaidEoEMultipliers.cpp b/src/strategy/raids/eyeofeternity/RaidEoEMultipliers.cpp new file mode 100644 index 00000000..9d077802 --- /dev/null +++ b/src/strategy/raids/eyeofeternity/RaidEoEMultipliers.cpp @@ -0,0 +1,49 @@ +#include "RaidEoEMultipliers.h" + +#include "ChooseTargetActions.h" +#include "DKActions.h" +#include "DruidActions.h" +#include "DruidBearActions.h" +#include "FollowActions.h" +#include "GenericActions.h" +#include "GenericSpellActions.h" +#include "MovementActions.h" +#include "PaladinActions.h" +#include "RaidEoEActions.h" +#include "RaidEoETriggers.h" +#include "ReachTargetActions.h" +#include "ScriptedCreature.h" +#include "WarriorActions.h" + +// float SartharionMultiplier::GetValue(Action* action) +// { +// Unit* boss = AI_VALUE2(Unit*, "find target", "sartharion"); +// if (!boss) { return 1.0f; } + +// Unit* target = action->GetTarget(); + +// if (botAI->IsMainTank(bot) && dynamic_cast(action)) +// { +// // return 0.0f; +// } + +// if (botAI->IsDps(bot) && dynamic_cast(action)) +// { +// return 0.0f; +// } + +// if (botAI->IsMainTank(bot) && target && target != boss && +// (dynamic_cast(action) || dynamic_cast(action) || dynamic_cast(action) || +// dynamic_cast(action) || dynamic_cast(action))) +// { +// return 0.0f; +// } + +// if (botAI->IsAssistTank(bot) && target && target == boss && +// (dynamic_cast(action) || dynamic_cast(action) || +// dynamic_cast(action) || dynamic_cast(action))) +// { +// return 0.0f; +// } +// return 1.0f; +// } diff --git a/src/strategy/raids/eyeofeternity/RaidEoEMultipliers.h b/src/strategy/raids/eyeofeternity/RaidEoEMultipliers.h new file mode 100644 index 00000000..0a710601 --- /dev/null +++ b/src/strategy/raids/eyeofeternity/RaidEoEMultipliers.h @@ -0,0 +1,16 @@ + +#ifndef _PLAYERRBOT_RAIDEOEMULTIPLIERS_H +#define _PLAYERRBOT_RAIDEOEMULTIPLIERS_H + +#include "Multiplier.h" + +// class SartharionMultiplier : public Multiplier +// { +// public: +// SartharionMultiplier(PlayerbotAI* ai) : Multiplier(ai, "sartharion") {} + +// public: +// virtual float GetValue(Action* action); +// }; + +#endif diff --git a/src/strategy/raids/eyeofeternity/RaidEoEStrategy.cpp b/src/strategy/raids/eyeofeternity/RaidEoEStrategy.cpp new file mode 100644 index 00000000..630ca7f3 --- /dev/null +++ b/src/strategy/raids/eyeofeternity/RaidEoEStrategy.cpp @@ -0,0 +1,32 @@ +#include "RaidEoEStrategy.h" +#include "RaidEoEMultipliers.h" +#include "Strategy.h" + +void RaidEoEStrategy::InitTriggers(std::vector& triggers) +{ + // triggers.push_back( + // new TriggerNode("sartharion tank", + // NextAction::array(0, new NextAction("sartharion tank position", ACTION_MOVE), nullptr))); + // triggers.push_back( + // new TriggerNode("twilight fissure", + // NextAction::array(0, new NextAction("avoid twilight fissure", ACTION_RAID + 2), nullptr))); + // triggers.push_back( + // new TriggerNode("flame tsunami", + // NextAction::array(0, new NextAction("avoid flame tsunami", ACTION_RAID + 1), nullptr))); + // triggers.push_back( + // new TriggerNode("sartharion dps", + // NextAction::array(0, new NextAction("sartharion attack priority", ACTION_RAID), nullptr))); + // // Flank dragon positioning + // triggers.push_back(new TriggerNode("sartharion melee positioning", + // NextAction::array(0, new NextAction("rear flank", ACTION_MOVE + 4), nullptr))); + + // triggers.push_back(new TriggerNode("twilight portal enter", + // NextAction::array(0, new NextAction("enter twilight portal", ACTION_RAID + 1), nullptr))); + // triggers.push_back(new TriggerNode("twilight portal exit", + // NextAction::array(0, new NextAction("exit twilight portal", ACTION_RAID + 1), nullptr))); +} + +void RaidEoEStrategy::InitMultipliers(std::vector &multipliers) +{ + // multipliers.push_back(new SartharionMultiplier(botAI)); +} diff --git a/src/strategy/raids/eyeofeternity/RaidEoEStrategy.h b/src/strategy/raids/eyeofeternity/RaidEoEStrategy.h new file mode 100644 index 00000000..eb7a147b --- /dev/null +++ b/src/strategy/raids/eyeofeternity/RaidEoEStrategy.h @@ -0,0 +1,17 @@ +#ifndef _PLAYERBOT_RAIDEOESTRATEGY_H +#define _PLAYERBOT_RAIDEOESTRATEGY_H + +#include "AiObjectContext.h" +#include "Multiplier.h" +#include "Strategy.h" + +class RaidEoEStrategy : public Strategy +{ +public: + RaidEoEStrategy(PlayerbotAI* ai) : Strategy(ai) {} + virtual std::string const getName() override { return "wotlk-eoe"; } + virtual void InitTriggers(std::vector &triggers) override; + virtual void InitMultipliers(std::vector &multipliers) override; +}; + +#endif diff --git a/src/strategy/raids/eyeofeternity/RaidEoETriggerContext.h b/src/strategy/raids/eyeofeternity/RaidEoETriggerContext.h new file mode 100644 index 00000000..438d0ef7 --- /dev/null +++ b/src/strategy/raids/eyeofeternity/RaidEoETriggerContext.h @@ -0,0 +1,32 @@ +#ifndef _PLAYERBOT_RAIDEOETRIGGERCONTEXT_H +#define _PLAYERBOT_RAIDEOETRIGGERCONTEXT_H + +#include "AiObjectContext.h" +#include "NamedObjectContext.h" +#include "RaidEoETriggers.h" + +class RaidEoETriggerContext : public NamedObjectContext +{ +public: + RaidEoETriggerContext() + { + // creators["sartharion tank"] = &RaidOsTriggerContext::sartharion_tank; + // creators["flame tsunami"] = &RaidOsTriggerContext::flame_tsunami; + // creators["twilight fissure"] = &RaidOsTriggerContext::twilight_fissure; + // creators["sartharion dps"] = &RaidOsTriggerContext::sartharion_dps; + // creators["sartharion melee positioning"] = &RaidOsTriggerContext::sartharion_melee; + // creators["twilight portal enter"] = &RaidOsTriggerContext::twilight_portal_enter; + // creators["twilight portal exit"] = &RaidOsTriggerContext::twilight_portal_exit; + } + +private: + // static Trigger* sartharion_tank(PlayerbotAI* ai) { return new SartharionTankTrigger(ai); } + // static Trigger* flame_tsunami(PlayerbotAI* ai) { return new FlameTsunamiTrigger(ai); } + // static Trigger* twilight_fissure(PlayerbotAI* ai) { return new TwilightFissureTrigger(ai); } + // static Trigger* sartharion_dps(PlayerbotAI* ai) { return new SartharionDpsTrigger(ai); } + // static Trigger* sartharion_melee(PlayerbotAI* ai) { return new SartharionMeleePositioningTrigger(ai); } + // static Trigger* twilight_portal_enter(PlayerbotAI* ai) { return new TwilightPortalEnterTrigger(ai); } + // static Trigger* twilight_portal_exit(PlayerbotAI* ai) { return new TwilightPortalExitTrigger(ai); } +}; + +#endif diff --git a/src/strategy/raids/eyeofeternity/RaidEoETriggers.cpp b/src/strategy/raids/eyeofeternity/RaidEoETriggers.cpp new file mode 100644 index 00000000..8427ab8f --- /dev/null +++ b/src/strategy/raids/eyeofeternity/RaidEoETriggers.cpp @@ -0,0 +1,128 @@ +#include "RaidEoETriggers.h" + +#include "SharedDefines.h" + +// bool SartharionTankTrigger::IsActive() +// { +// Unit* boss = AI_VALUE2(Unit*, "find target", "sartharion"); +// if (!boss) { return false; } + +// return botAI->IsTank(bot); +// } + +// bool FlameTsunamiTrigger::IsActive() +// { +// if (botAI->IsTank(bot)) { return false; } + +// Unit* boss = AI_VALUE2(Unit*, "find target", "sartharion"); +// if (!boss) { return false; } + + +// GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); +// for (auto& npc : npcs) +// { +// Unit* unit = botAI->GetUnit(npc); +// if (unit) +// { +// if (unit->GetEntry() == NPC_FLAME_TSUNAMI) +// { +// return true; +// } +// } +// } + +// return false; +// } + +// bool TwilightFissureTrigger::IsActive() +// { +// Unit* boss = AI_VALUE2(Unit*, "find target", "sartharion"); +// if (!boss) { return false; } + + +// GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); +// for (auto& npc : npcs) +// { +// Unit* unit = botAI->GetUnit(npc); +// if (unit) +// { +// if (unit->GetEntry() == NPC_TWILIGHT_FISSURE) +// { +// return true; +// } +// } +// } + +// return false; +// } + +// bool SartharionDpsTrigger::IsActive() +// { +// Unit* boss = AI_VALUE2(Unit*, "find target", "sartharion"); +// if (!boss) { return false; } + +// return botAI->IsDps(bot); +// } + +// bool SartharionMeleePositioningTrigger::IsActive() +// { +// if (!botAI->IsMelee(bot) || !botAI->IsDps(bot)) { return false; } + +// Unit* boss = AI_VALUE2(Unit*, "find target", "sartharion"); +// if (!boss) { return false; } + +// Unit* shadron = AI_VALUE2(Unit*, "find target", "shadron"); +// Unit* tenebron = AI_VALUE2(Unit*, "find target", "tenebron"); +// Unit* vesperon = AI_VALUE2(Unit*, "find target", "vesperon"); + +// return !(shadron || tenebron || vesperon); +// } + +// bool TwilightPortalEnterTrigger::IsActive() +// { +// if (botAI->IsMainTank(bot) || botAI->IsHealAssistantOfIndex(bot, 0)) { return false; } + +// // In 25-man, take two healers in. Otherwise just take one +// // if (bot->GetRaidDifficulty() == RAID_DIFFICULTY_25MAN_NORMAL) +// // { +// // if (botAI->IsHealAssistantOfIndex(bot, 0) || botAI->IsHealAssistantOfIndex(bot, 1)) +// // { +// // return false; +// // } +// // } +// // else +// // { +// // if (botAI->IsHealAssistantOfIndex(bot, 0)) +// // { +// // return false; +// // } +// // } + + +// // Don't enter portal until drakes are dead +// if (bot->HasAura(SPELL_POWER_OF_SHADRON) || +// bot->HasAura(SPELL_POWER_OF_TENEBRON) || +// bot->HasAura(SPELL_POWER_OF_VESPERON)) +// { +// return false; +// } + +// Unit* boss = AI_VALUE2(Unit*, "find target", "sartharion"); +// if (!boss) { return false; } + +// // GuidVector objects = AI_VALUE(GuidVector, "nearest game objects no los"); +// // for (auto& object : objects) +// // { +// // GameObject* go = botAI->GetGameObject(object); +// // if (go && go->GetEntry() == GO_TWILIGHT_PORTAL) +// // { +// // return true; +// // } +// // } +// return bool(bot->FindNearestGameObject(GO_TWILIGHT_PORTAL, 100.0f)); +// } + +// bool TwilightPortalExitTrigger::IsActive() +// { +// return bot->HasAura(SPELL_TWILIGHT_SHIFT) && !AI_VALUE2(Unit*, "find target", "acolyte of shadron"); +// } \ No newline at end of file diff --git a/src/strategy/raids/eyeofeternity/RaidEoETriggers.h b/src/strategy/raids/eyeofeternity/RaidEoETriggers.h new file mode 100644 index 00000000..798e5a49 --- /dev/null +++ b/src/strategy/raids/eyeofeternity/RaidEoETriggers.h @@ -0,0 +1,120 @@ +#ifndef _PLAYERBOT_RAIDEOETRIGGERS_H +#define _PLAYERBOT_RAIDEOETRIGGERS_H + +#include "PlayerbotAI.h" +#include "Playerbots.h" +#include "Trigger.h" + +enum EyeOfEternityIDs +{ + // // Bosses + // NPC_SARTHARION = 28860, + // NPC_SHADRON = 30451, + // NPC_TENEBRON = 30452, + // NPC_VESPERON = 30449, + + // // Mini-boss shared + // SPELL_SHADOW_BREATH = 57570, + // SPELL_SHADOW_FISSURE = 57579, + // SPELL_SUMMON_TWILIGHT_WHELP = 58035, + // SPELL_GIFT_OF_TWILIGHT_SHADOW = 57835, + // SPELL_TWILIGHT_TORMENT_VESPERON = 57935, + + // // Sartharion + // SPELL_SARTHARION_CLEAVE = 56909, + // SPELL_SARTHARION_FLAME_BREATH = 56908, + // SPELL_SARTHARION_TAIL_LASH = 56910, + // SPELL_CYCLONE_AURA_PERIODIC = 57598, + // SPELL_LAVA_STRIKE_DUMMY = 57578, + // SPELL_LAVA_STRIKE_DUMMY_TRIGGER = 57697, + // SPELL_LAVA_STRIKE_SUMMON = 57572, + // SPELL_SARTHARION_PYROBUFFET = 56916, + // SPELL_SARTHARION_BERSERK = 61632, + // SPELL_SARTHARION_TWILIGHT_REVENGE = 60639, + + // // Sartharion with drakes + // SPELL_WILL_OF_SARTHARION = 61254, + // SPELL_POWER_OF_TENEBRON = 61248, + // SPELL_POWER_OF_VESPERON = 61251, + // SPELL_POWER_OF_SHADRON = 58105, + // SPELL_GIFT_OF_TWILIGHT_FIRE = 58766, + + // // Visuals + // SPELL_EGG_MARKER_VISUAL = 58547, + // SPELL_FLAME_TSUNAMI_VISUAL = 57494, + + // // Misc + // SPELL_FADE_ARMOR = 60708, + // SPELL_FLAME_TSUNAMI_DAMAGE_AURA = 57492, + // SPELL_FLAME_TSUNAMI_LEAP = 60241, + // SPELL_SARTHARION_PYROBUFFET_TRIGGER = 57557, + + // NPC_TWILIGHT_EGG = 30882, + // NPC_TWILIGHT_WHELP = 30890, + // NPC_DISCIPLE_OF_SHADRON = 30688, + // NPC_DISCIPLE_OF_VESPERON = 30858, + // NPC_ACOLYTE_OF_SHADRON = 31218, + // NPC_ACOLYTE_OF_VESPERON = 31219, + + // // Sartharion fight + // NPC_LAVA_BLAZE = 30643, + // NPC_FLAME_TSUNAMI = 30616, + // NPC_SAFE_AREA_TRIGGER = 30494, + // NPC_TWILIGHT_FISSURE = 30641, + // GO_TWILIGHT_PORTAL = 193988, + // GO_NORMAL_PORTAL = 193989, + // SPELL_TWILIGHT_SHIFT = 57874, +}; + +const uint32 EOE_MAP_ID = 615; + +// class SartharionTankTrigger : public Trigger +// { +// public: +// SartharionTankTrigger(PlayerbotAI* botAI) : Trigger(botAI, "sartharion tank") {} +// bool IsActive() override; +// }; + +// class FlameTsunamiTrigger : public Trigger +// { +// public: +// FlameTsunamiTrigger(PlayerbotAI* botAI) : Trigger(botAI, "flame tsunami") {} +// bool IsActive() override; +// }; + +// class TwilightFissureTrigger : public Trigger +// { +// public: +// TwilightFissureTrigger(PlayerbotAI* botAI) : Trigger(botAI, "twilight fissure") {} +// bool IsActive() override; +// }; + +// class SartharionDpsTrigger : public Trigger +// { +// public: +// SartharionDpsTrigger(PlayerbotAI* botAI) : Trigger(botAI, "sartharion dps") {} +// bool IsActive() override; +// }; + +// class SartharionMeleePositioningTrigger : public Trigger +// { +// public: +// SartharionMeleePositioningTrigger(PlayerbotAI* botAI) : Trigger(botAI, "sartharion melee positioning") {} +// bool IsActive() override; +// }; + +// class TwilightPortalEnterTrigger : public Trigger +// { +// public: +// TwilightPortalEnterTrigger(PlayerbotAI* botAI) : Trigger(botAI, "twilight portal enter") {} +// bool IsActive() override; +// }; + +// class TwilightPortalExitTrigger : public Trigger +// { +// public: +// TwilightPortalExitTrigger(PlayerbotAI* botAI) : Trigger(botAI, "twilight portal exit") {} +// bool IsActive() override; +// }; + +#endif From af6eb61d336f6ab72a2837baf55f6be3b97f6ad1 Mon Sep 17 00:00:00 2001 From: Bobblybook Date: Sun, 15 Dec 2024 23:37:56 +1100 Subject: [PATCH 3/3] Prelim EoE completion --- .../wotlk/oculus/OculusActionContext.h | 4 +- .../dungeons/wotlk/oculus/OculusActions.cpp | 12 +- .../dungeons/wotlk/oculus/OculusActions.h | 9 +- .../wotlk/oculus/OculusMultipliers.cpp | 6 +- .../dungeons/wotlk/oculus/OculusMultipliers.h | 4 +- .../dungeons/wotlk/oculus/OculusStrategy.cpp | 2 +- src/strategy/raids/RaidStrategyContext.h | 3 + .../eyeofeternity/RaidEoEActionContext.h | 24 +- .../raids/eyeofeternity/RaidEoEActions.cpp | 564 +++++++++++------- .../raids/eyeofeternity/RaidEoEActions.h | 101 ++-- .../eyeofeternity/RaidEoEMultipliers.cpp | 65 ++ .../raids/eyeofeternity/RaidEoEMultipliers.h | 14 +- .../raids/eyeofeternity/RaidEoEStrategy.cpp | 35 +- .../eyeofeternity/RaidEoETriggerContext.h | 18 +- .../raids/eyeofeternity/RaidEoETriggers.cpp | 171 ++---- .../raids/eyeofeternity/RaidEoETriggers.h | 140 ++--- 16 files changed, 617 insertions(+), 555 deletions(-) diff --git a/src/strategy/dungeons/wotlk/oculus/OculusActionContext.h b/src/strategy/dungeons/wotlk/oculus/OculusActionContext.h index 38cd640b..fc22fdbb 100644 --- a/src/strategy/dungeons/wotlk/oculus/OculusActionContext.h +++ b/src/strategy/dungeons/wotlk/oculus/OculusActionContext.h @@ -21,8 +21,8 @@ class WotlkDungeonOccActionContext : public NamedObjectContext static Action* avoid_unstable_sphere(PlayerbotAI* ai) { return new AvoidUnstableSphereAction(ai); } static Action* mount_drake(PlayerbotAI* ai) { return new MountDrakeAction(ai); } static Action* dismount_drake(PlayerbotAI* ai) { return new DismountDrakeAction(ai); } - static Action* fly_drake(PlayerbotAI* ai) { return new FlyDrakeAction(ai); } - static Action* drake_attack(PlayerbotAI* ai) { return new DrakeAttackAction(ai); } + static Action* fly_drake(PlayerbotAI* ai) { return new OccFlyDrakeAction(ai); } + static Action* drake_attack(PlayerbotAI* ai) { return new OccDrakeAttackAction(ai); } static Action* avoid_arcane_explosion(PlayerbotAI* ai) { return new AvoidArcaneExplosionAction(ai); } static Action* time_bomb_spread(PlayerbotAI* ai) { return new TimeBombSpreadAction(ai); } }; diff --git a/src/strategy/dungeons/wotlk/oculus/OculusActions.cpp b/src/strategy/dungeons/wotlk/oculus/OculusActions.cpp index 115181e2..608ff618 100644 --- a/src/strategy/dungeons/wotlk/oculus/OculusActions.cpp +++ b/src/strategy/dungeons/wotlk/oculus/OculusActions.cpp @@ -111,7 +111,7 @@ bool DismountDrakeAction::Execute(Event event) return false; } -bool FlyDrakeAction::Execute(Event event) +bool OccFlyDrakeAction::Execute(Event event) { Player* master = botAI->GetMaster(); if (!master) { return false; } @@ -152,7 +152,7 @@ bool FlyDrakeAction::Execute(Event event) return false; } -bool DrakeAttackAction::Execute(Event event) +bool OccDrakeAttackAction::Execute(Event event) { vehicleBase = bot->GetVehicleBase(); if (!vehicleBase) { return false; } @@ -188,7 +188,7 @@ bool DrakeAttackAction::Execute(Event event) return false; } -bool DrakeAttackAction::CastDrakeSpellAction(Unit* target, uint32 spellId, uint32 cooldown) +bool OccDrakeAttackAction::CastDrakeSpellAction(Unit* target, uint32 spellId, uint32 cooldown) { if (botAI->CanCastVehicleSpell(spellId, target)) if (botAI->CastVehicleSpell(spellId, target)) @@ -199,7 +199,7 @@ bool DrakeAttackAction::CastDrakeSpellAction(Unit* target, uint32 spellId, uint3 return false; } -bool DrakeAttackAction::AmberDrakeAction(Unit* target) +bool OccDrakeAttackAction::AmberDrakeAction(Unit* target) { Aura* shockCharges = target->GetAura(SPELL_SHOCK_CHARGE, vehicleBase->GetGUID()); if (shockCharges && shockCharges->GetStackAmount() > 8) @@ -225,7 +225,7 @@ bool DrakeAttackAction::AmberDrakeAction(Unit* target) return false; } -bool DrakeAttackAction::EmeraldDrakeAction(Unit* target) +bool OccDrakeAttackAction::EmeraldDrakeAction(Unit* target) { Aura* poisonStacks = target->GetAura(SPELL_LEECHING_POISON, vehicleBase->GetGUID()); if (!poisonStacks || (poisonStacks->GetStackAmount() < 3 || @@ -286,7 +286,7 @@ bool DrakeAttackAction::EmeraldDrakeAction(Unit* target) return false; } -bool DrakeAttackAction::RubyDrakeAction(Unit* target) +bool OccDrakeAttackAction::RubyDrakeAction(Unit* target) { Aura* evasiveCharges = vehicleBase->GetAura(SPELL_EVASIVE_CHARGES); Aura* evasiveManeuvers = vehicleBase->GetAura(SPELL_EVASIVE_MANEUVERS); diff --git a/src/strategy/dungeons/wotlk/oculus/OculusActions.h b/src/strategy/dungeons/wotlk/oculus/OculusActions.h index 2111461a..6b26b9dd 100644 --- a/src/strategy/dungeons/wotlk/oculus/OculusActions.h +++ b/src/strategy/dungeons/wotlk/oculus/OculusActions.h @@ -38,17 +38,17 @@ public: bool Execute(Event event) override; }; -class FlyDrakeAction : public MovementAction +class OccFlyDrakeAction : public MovementAction { public: - FlyDrakeAction(PlayerbotAI* ai) : MovementAction(ai, "fly drake") {} + OccFlyDrakeAction(PlayerbotAI* ai) : MovementAction(ai, "occ fly drake") {} bool Execute(Event event) override; }; -class DrakeAttackAction : public Action +class OccDrakeAttackAction : public Action { public: - DrakeAttackAction(PlayerbotAI* botAI) : Action(botAI, "drake attack") {} + OccDrakeAttackAction(PlayerbotAI* botAI) : Action(botAI, "occ drake attack") {} bool Execute(Event event) override; protected: @@ -57,7 +57,6 @@ protected: bool AmberDrakeAction(Unit* target); bool EmeraldDrakeAction(Unit* target); bool RubyDrakeAction(Unit* target); - }; class AvoidArcaneExplosionAction : public MovementAction diff --git a/src/strategy/dungeons/wotlk/oculus/OculusMultipliers.cpp b/src/strategy/dungeons/wotlk/oculus/OculusMultipliers.cpp index 023d5256..b30c4db1 100644 --- a/src/strategy/dungeons/wotlk/oculus/OculusMultipliers.cpp +++ b/src/strategy/dungeons/wotlk/oculus/OculusMultipliers.cpp @@ -26,12 +26,12 @@ float MountingDrakeMultiplier::GetValue(Action* action) return 1.0f; } -float FlyingMultiplier::GetValue(Action* action) +float OccFlyingMultiplier::GetValue(Action* action) { if (bot->GetMapId() != OCULUS_MAP_ID || !bot->GetVehicleBase()) { return 1.0f; } // Suppresses FollowAction as well as some attack-based movements - if (dynamic_cast(action) && !dynamic_cast(action)) + if (dynamic_cast(action) && !dynamic_cast(action)) { return 0.0f; } @@ -103,7 +103,7 @@ float EregosMultiplier::GetValue(Action* action) Unit* boss = AI_VALUE2(Unit*, "find target", "ley-guardian eregos"); if (!boss) { return 1.0f; } - if (boss->HasAura(SPELL_PLANAR_SHIFT && dynamic_cast(action))) + if (boss->HasAura(SPELL_PLANAR_SHIFT && dynamic_cast(action))) { return 0.0f; } diff --git a/src/strategy/dungeons/wotlk/oculus/OculusMultipliers.h b/src/strategy/dungeons/wotlk/oculus/OculusMultipliers.h index f2f86f79..423364b0 100644 --- a/src/strategy/dungeons/wotlk/oculus/OculusMultipliers.h +++ b/src/strategy/dungeons/wotlk/oculus/OculusMultipliers.h @@ -21,10 +21,10 @@ class MountingDrakeMultiplier : public Multiplier virtual float GetValue(Action* action); }; -class FlyingMultiplier : public Multiplier +class OccFlyingMultiplier : public Multiplier { public: - FlyingMultiplier(PlayerbotAI* ai) : Multiplier(ai, "flying drake") {} + OccFlyingMultiplier(PlayerbotAI* ai) : Multiplier(ai, "occ flying drake") {} public: virtual float GetValue(Action* action); diff --git a/src/strategy/dungeons/wotlk/oculus/OculusStrategy.cpp b/src/strategy/dungeons/wotlk/oculus/OculusStrategy.cpp index 2b98e7da..9c1786b5 100644 --- a/src/strategy/dungeons/wotlk/oculus/OculusStrategy.cpp +++ b/src/strategy/dungeons/wotlk/oculus/OculusStrategy.cpp @@ -36,7 +36,7 @@ void WotlkDungeonOccStrategy::InitTriggers(std::vector &triggers) void WotlkDungeonOccStrategy::InitMultipliers(std::vector &multipliers) { multipliers.push_back(new MountingDrakeMultiplier(botAI)); - multipliers.push_back(new FlyingMultiplier(botAI)); + multipliers.push_back(new OccFlyingMultiplier(botAI)); multipliers.push_back(new UromMultiplier(botAI)); multipliers.push_back(new EregosMultiplier(botAI)); } diff --git a/src/strategy/raids/RaidStrategyContext.h b/src/strategy/raids/RaidStrategyContext.h index 31675f7f..d863aa0a 100644 --- a/src/strategy/raids/RaidStrategyContext.h +++ b/src/strategy/raids/RaidStrategyContext.h @@ -6,6 +6,7 @@ #include "RaidBwlStrategy.h" #include "RaidNaxxStrategy.h" #include "RaidOsStrategy.h" +#include "RaidEoEStrategy.h" #include "RaidMcStrategy.h" #include "RaidAq20Strategy.h" #include "RaidIccStrategy.h" @@ -23,6 +24,7 @@ public: creators["aq20"] = &RaidStrategyContext::aq20; creators["naxx"] = &RaidStrategyContext::naxx; creators["wotlk-os"] = &RaidStrategyContext::wotlk_os; + creators["wotlk-eoe"] = &RaidStrategyContext::wotlk_eoe; creators["uld"] = &RaidStrategyContext::uld; creators["icc"] = &RaidStrategyContext::icc; } @@ -33,6 +35,7 @@ private: static Strategy* aq20(PlayerbotAI* botAI) { return new RaidAq20Strategy(botAI); } static Strategy* naxx(PlayerbotAI* botAI) { return new RaidNaxxStrategy(botAI); } static Strategy* wotlk_os(PlayerbotAI* botAI) { return new RaidOsStrategy(botAI); } + static Strategy* wotlk_eoe(PlayerbotAI* botAI) { return new RaidEoEStrategy(botAI); } static Strategy* uld(PlayerbotAI* botAI) { return new RaidUlduarStrategy(botAI); } static Strategy* icc(PlayerbotAI* botAI) { return new RaidIccStrategy(botAI); } }; diff --git a/src/strategy/raids/eyeofeternity/RaidEoEActionContext.h b/src/strategy/raids/eyeofeternity/RaidEoEActionContext.h index 1250b26b..ba4d841d 100644 --- a/src/strategy/raids/eyeofeternity/RaidEoEActionContext.h +++ b/src/strategy/raids/eyeofeternity/RaidEoEActionContext.h @@ -10,21 +10,21 @@ class RaidEoEActionContext : public NamedObjectContext public: RaidEoEActionContext() { - // creators["sartharion tank position"] = &RaidOsActionContext::tank_position; - // creators["avoid twilight fissure"] = &RaidOsActionContext::avoid_twilight_fissure; - // creators["avoid flame tsunami"] = &RaidOsActionContext::avoid_flame_tsunami; - // creators["sartharion attack priority"] = &RaidOsActionContext::attack_priority; - // creators["enter twilight portal"] = &RaidOsActionContext::enter_twilight_portal; - // creators["exit twilight portal"] = &RaidOsActionContext::exit_twilight_portal; + creators["malygos position"] = &RaidEoEActionContext::position; + creators["malygos target"] = &RaidEoEActionContext::target; + // creators["pull power spark"] = &RaidEoEActionContext::pull_power_spark; + // creators["kill power spark"] = &RaidEoEActionContext::kill_power_spark; + creators["fly drake"] = &RaidEoEActionContext::fly_drake; + creators["drake attack"] = &RaidEoEActionContext::drake_attack; } private: - // static Action* tank_position(PlayerbotAI* ai) { return new SartharionTankPositionAction(ai); } - // static Action* avoid_twilight_fissure(PlayerbotAI* ai) { return new AvoidTwilightFissureAction(ai); } - // static Action* avoid_flame_tsunami(PlayerbotAI* ai) { return new AvoidFlameTsunamiAction(ai); } - // static Action* attack_priority(PlayerbotAI* ai) { return new SartharionAttackPriorityAction(ai); } - // static Action* enter_twilight_portal(PlayerbotAI* ai) { return new EnterTwilightPortalAction(ai); } - // static Action* exit_twilight_portal(PlayerbotAI* ai) { return new ExitTwilightPortalAction(ai); } + static Action* position(PlayerbotAI* ai) { return new MalygosPositionAction(ai); } + static Action* target(PlayerbotAI* ai) { return new MalygosTargetAction(ai); } + // static Action* pull_power_spark(PlayerbotAI* ai) { return new PullPowerSparkAction(ai); } + // static Action* kill_power_spark(PlayerbotAI* ai) { return new KillPowerSparkAction(ai); } + static Action* fly_drake(PlayerbotAI* ai) { return new EoEFlyDrakeAction(ai); } + static Action* drake_attack(PlayerbotAI* ai) { return new EoEDrakeAttackAction(ai); } }; #endif diff --git a/src/strategy/raids/eyeofeternity/RaidEoEActions.cpp b/src/strategy/raids/eyeofeternity/RaidEoEActions.cpp index 0166d92e..4bacf89e 100644 --- a/src/strategy/raids/eyeofeternity/RaidEoEActions.cpp +++ b/src/strategy/raids/eyeofeternity/RaidEoEActions.cpp @@ -3,244 +3,388 @@ #include "Playerbots.h" -// bool SartharionTankPositionAction::Execute(Event event) + +bool MalygosPositionAction::Execute(Event event) +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "malygos"); + if (!boss) { return false; } + + uint8 phase = MalygosTrigger::getPhase(bot, boss); + + float distance = 5.0f; + + if (phase == 1) + { + Unit* spark = nullptr; + + GuidVector targets = AI_VALUE(GuidVector, "possible targets no los"); + for (auto& target : targets) + { + Unit* unit = botAI->GetUnit(target); + if (unit && unit->GetEntry() == NPC_POWER_SPARK) + { + spark = unit; + break; + } + } + + // Position tank + if (botAI->IsMainTank(bot)) + { + if (bot->GetDistance2d(MALYGOS_MAINTANK_POSITION.first, MALYGOS_MAINTANK_POSITION.second) > distance) + { + return MoveTo(EOE_MAP_ID, MALYGOS_MAINTANK_POSITION.first, MALYGOS_MAINTANK_POSITION.second, bot->GetPositionZ(), + false, false, false, false, MovementPriority::MOVEMENT_COMBAT); + } + return false; + } + // Position DK for spark pull + // else if (spark && bot->IsClass(CLASS_DEATH_KNIGHT)) + // { + // if (bot->GetDistance2d(MALYGOS_STACK_POSITION.first, MALYGOS_STACK_POSITION.second) > distance) + // { + // bot->Yell("SPARK SPAWNED, MOVING TO STACK", LANG_UNIVERSAL); + // return MoveTo(EOE_MAP_ID, MALYGOS_STACK_POSITION.first, MALYGOS_STACK_POSITION.second, bot->GetPositionZ(), + // false, false, false, false, MovementPriority::MOVEMENT_COMBAT); + // } + // return false; + // } + else if (spark) + { + return false; + } + else if (!bot->IsClass(CLASS_HUNTER)) + { + if (bot->GetDistance2d(MALYGOS_STACK_POSITION.first, MALYGOS_STACK_POSITION.second) > (distance * 3.0f)) + { + return MoveTo(EOE_MAP_ID, MALYGOS_STACK_POSITION.first, MALYGOS_STACK_POSITION.second, bot->GetPositionZ(), + false, false, false, false, MovementPriority::MOVEMENT_COMBAT); + } + return false; + } + } + + return false; +} + +bool MalygosTargetAction::Execute(Event event) +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "malygos"); + if (!boss) { return false; } + + uint8 phase = MalygosTrigger::getPhase(bot, boss); + + if (phase == 1) + { + if (botAI->IsHeal(bot)) { return false; } + + Unit* newTarget = boss; + Unit* spark = nullptr; + + // GuidVector targets = AI_VALUE(GuidVector, "possible targets no los"); + // for (auto& target : targets) + // { + // Unit* unit = botAI->GetUnit(target); + // if (unit && unit->GetEntry() == NPC_POWER_SPARK) + // { + // spark = unit; + // break; + // } + // } + + // if (spark && botAI->IsRangedDps(bot)) + // { + // newTarget = spark; + // } + + Unit* currentTarget = AI_VALUE(Unit*, "current target"); + + if (!currentTarget || currentTarget->GetEntry() != newTarget->GetEntry()) + { + return Attack(newTarget); + } + } + else if (phase == 2) + { + if (botAI->IsHeal(bot)) { return false; } + + Unit* newTarget = nullptr; + Unit* nexusLord = nullptr; + Unit* scionOfEternity = nullptr; + + GuidVector targets = AI_VALUE(GuidVector, "possible targets no los"); + for (auto& target : targets) + { + Unit* unit = botAI->GetUnit(target); + if (!unit) { continue; } + + if (unit->GetEntry() == NPC_NEXUS_LORD) + { + nexusLord = unit; + } + else if (unit->GetEntry() == NPC_SCION_OF_ETERNITY) + { + scionOfEternity = unit; + } + } + + if (botAI->IsRangedDps(bot) && scionOfEternity) + { + newTarget = scionOfEternity; + } + else + { + newTarget = nexusLord; + } + + if (!newTarget) { return false; } + + Unit* currentTarget = AI_VALUE(Unit*, "current target"); + if (!currentTarget || currentTarget->GetEntry() != newTarget->GetEntry()) + { + return Attack(newTarget); + } + } + + // else if (phase == 3) + // {} + + return false; +} + +// bool PullPowerSparkAction::Execute(Event event) // { -// Unit* boss = AI_VALUE2(Unit*, "find target", "sartharion"); -// if (!boss) { return false; } +// Unit* spark = nullptr; -// // Unit* shadron = AI_VALUE2(Unit*, "find target", "shadron"); -// // Unit* tenebron = AI_VALUE2(Unit*, "find target", "tenebron"); -// // Unit* vesperon = AI_VALUE2(Unit*, "find target", "vesperon"); -// Unit* shadron = nullptr; -// Unit* tenebron = nullptr; -// Unit* vesperon = nullptr; - -// // Detect incoming drakes before they are on aggro table // GuidVector targets = AI_VALUE(GuidVector, "possible targets no los"); // for (auto& target : targets) // { // Unit* unit = botAI->GetUnit(target); -// if (!unit) { continue; } - -// switch (unit->GetEntry()) +// if (unit && unit->GetEntry() == NPC_POWER_SPARK) // { -// case NPC_SHADRON: -// shadron = unit; -// continue; -// case NPC_TENEBRON: -// tenebron = unit; -// continue; -// case NPC_VESPERON: -// vesperon = unit; -// continue; -// default: -// continue; +// spark = unit; +// break; // } // } -// Position currentPos = bot->GetPosition(); -// // Adjustable, this is the acceptable distance to stack point that will be accepted as "safe" -// float looseDistance = 12.0f; +// if (!spark) { return false; } -// if (botAI->IsMainTank(bot)) +// if (spark->GetDistance2d(MALYGOS_STACK_POSITION.first, MALYGOS_STACK_POSITION.second) > 3.0f) // { -// if (bot->GetExactDist2d(SARTHARION_MAINTANK_POSITION.first, SARTHARION_MAINTANK_POSITION.second) > looseDistance) -// { -// return MoveTo(OS_MAP_ID, SARTHARION_MAINTANK_POSITION.first, SARTHARION_MAINTANK_POSITION.second, currentPos.GetPositionZ(), -// false, false, false, false, MovementPriority::MOVEMENT_COMBAT); -// } -// } -// // Offtank grab drakes -// else if (shadron || tenebron || vesperon) -// { -// float triggerDistance = 100.0f; -// // Prioritise threat before positioning -// if (tenebron && bot->GetExactDist2d(tenebron) < triggerDistance && -// tenebron->GetTarget() != bot->GetGUID() && AI_VALUE(Unit*, "current target") != tenebron) -// { -// return Attack(tenebron); -// } -// if (shadron && bot->GetExactDist2d(shadron) < triggerDistance && -// shadron->GetTarget() != bot->GetGUID() && AI_VALUE(Unit*, "current target") != shadron) -// { -// return Attack(shadron); -// } -// if (vesperon && bot->GetExactDist2d(vesperon) < triggerDistance && -// vesperon->GetTarget() != bot->GetGUID() && AI_VALUE(Unit*, "current target") != vesperon) -// { -// return Attack(vesperon); -// } - -// bool drakeInCombat = (tenebron && bot->GetExactDist2d(tenebron) < triggerDistance) || -// (shadron && bot->GetExactDist2d(shadron) < triggerDistance) || -// (vesperon && bot->GetExactDist2d(vesperon) < triggerDistance); -// // Offtank has threat on drakes, check positioning -// if (drakeInCombat && bot->GetExactDist2d(SARTHARION_OFFTANK_POSITION.first, SARTHARION_OFFTANK_POSITION.second) > looseDistance) -// { -// return MoveTo(OS_MAP_ID, SARTHARION_OFFTANK_POSITION.first, SARTHARION_OFFTANK_POSITION.second, currentPos.GetPositionZ(), -// false, false, false, false, MovementPriority::MOVEMENT_COMBAT); -// } -// } -// return false; -// } - -// bool AvoidTwilightFissureAction::Execute(Event event) -// { -// const float radius = 5.0f; - -// GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); -// for (auto& npc : npcs) -// { -// Unit* unit = botAI->GetUnit(npc); -// if (unit && unit->GetEntry() == NPC_TWILIGHT_FISSURE) -// { -// float currentDistance = bot->GetDistance2d(unit); -// if (currentDistance < radius) -// { -// return MoveAway(unit, radius - currentDistance); -// } -// } -// } -// return false; -// } - -// bool AvoidFlameTsunamiAction::Execute(Event event) -// { -// // Adjustable, this is the acceptable distance to stack point that will be accepted as "safe" -// float looseDistance = 4.0f; - -// GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); -// for (auto& npc : npcs) -// { -// Unit* unit = botAI->GetUnit(npc); -// if (unit && unit->GetEntry() == NPC_FLAME_TSUNAMI) -// { -// Position currentPos = bot->GetPosition(); - -// // I think these are centrepoints for the wave segments. Either way they uniquely identify the wave -// // direction as they have different coords for the left and right waves -// // int casting is not a mistake, need to avoid FP errors somehow. -// // I always saw these accurate to around 6 decimal places, but if there are issues, -// // can switch this to abs comparison of floats which would technically be more robust. -// int posY = (int) unit->GetPositionY(); -// if (posY == 505 || posY == 555) // RIGHT WAVE -// { -// bool wavePassed = currentPos.GetPositionX() > unit->GetPositionX(); -// if (wavePassed) -// { -// return false; -// } - -// if (bot->GetExactDist2d(currentPos.GetPositionX(), TSUNAMI_RIGHT_SAFE_ALL) > looseDistance) -// { -// return MoveTo(OS_MAP_ID, currentPos.GetPositionX(), TSUNAMI_RIGHT_SAFE_ALL, currentPos.GetPositionZ(), -// false, false, false, false, MovementPriority::MOVEMENT_COMBAT); -// } -// } -// else // LEFT WAVE -// { -// bool wavePassed = currentPos.GetPositionX() < unit->GetPositionX(); -// if (wavePassed) -// { -// return false; -// } - -// if (botAI->IsMelee(bot)) -// { -// if (bot->GetExactDist2d(currentPos.GetPositionX(), TSUNAMI_LEFT_SAFE_MELEE) > looseDistance) -// { -// return MoveTo(OS_MAP_ID, currentPos.GetPositionX(), TSUNAMI_LEFT_SAFE_MELEE, currentPos.GetPositionZ(), -// false, false, false, false, MovementPriority::MOVEMENT_COMBAT); -// } -// } -// else // Ranged/healers -// { -// if (bot->GetExactDist2d(currentPos.GetPositionX(), TSUNAMI_LEFT_SAFE_RANGED) > looseDistance) -// { -// return MoveTo(OS_MAP_ID, currentPos.GetPositionX(), TSUNAMI_LEFT_SAFE_RANGED, currentPos.GetPositionZ(), -// false, false, false, false, MovementPriority::MOVEMENT_COMBAT); -// } -// } -// } -// } -// } -// return false; -// } - -// bool SartharionAttackPriorityAction::Execute(Event event) -// { -// Unit* sartharion = AI_VALUE2(Unit*, "find target", "sartharion"); -// Unit* shadron = AI_VALUE2(Unit*, "find target", "shadron"); -// Unit* tenebron = AI_VALUE2(Unit*, "find target", "tenebron"); -// Unit* vesperon = AI_VALUE2(Unit*, "find target", "vesperon"); -// Unit* acolyte = AI_VALUE2(Unit*, "find target", "acolyte of shadron"); - -// Unit* target = nullptr; - -// if (acolyte) -// { -// target = acolyte; -// } -// else if (vesperon) -// { -// target = vesperon; -// } -// else if (tenebron) -// { -// target = tenebron; -// } -// else if (shadron) -// { -// target = shadron; -// } -// else if (sartharion) -// { -// target = sartharion; -// } - -// if (target && AI_VALUE(Unit*, "current target") != target) -// { -// return Attack(target); +// bot->Yell("GRIPPING SPARK", LANG_UNIVERSAL); +// return botAI->CastSpell("death grip", spark); // } // return false; // } -// bool EnterTwilightPortalAction::Execute(Event event) +// bool PullPowerSparkAction::isPossible() // { -// Unit* boss = AI_VALUE2(Unit*, "find target", "sartharion"); -// if (!boss || !boss->HasAura(SPELL_GIFT_OF_TWILIGHT_FIRE)) { return false; } +// Unit* spark = nullptr; -// GameObject* portal = bot->FindNearestGameObject(GO_TWILIGHT_PORTAL, 100.0f); -// if (!portal) { return false; } - -// if (!portal->IsAtInteractDistance(bot)) +// GuidVector targets = AI_VALUE(GuidVector, "possible targets no los"); +// for (auto& target : targets) // { -// return MoveTo(portal, fmaxf(portal->GetInteractionDistance() - 1.0f, 0.0f)); +// Unit* unit = botAI->GetUnit(target); +// if (unit && unit->GetEntry() == NPC_POWER_SPARK) +// { +// spark = unit; +// break; +// } // } -// // Go through portal -// WorldPacket data1(CMSG_GAMEOBJ_USE); -// data1 << portal->GetGUID(); -// bot->GetSession()->HandleGameObjectUseOpcode(data1); - -// return true; +// return botAI->CanCastSpell(spell, spark); // } -// bool ExitTwilightPortalAction::Execute(Event event) +// bool PullPowerSparkAction::isUseful() // { -// GameObject* portal = bot->FindNearestGameObject(GO_NORMAL_PORTAL, 100.0f); -// if (!portal) { return false; } +// Unit* spark = nullptr; -// if (!portal->IsAtInteractDistance(bot)) +// GuidVector targets = AI_VALUE(GuidVector, "possible targets no los"); +// for (auto& target : targets) // { -// return MoveTo(portal, fmaxf(portal->GetInteractionDistance() - 1.0f, 0.0f)); +// Unit* unit = botAI->GetUnit(target); +// if (unit && unit->GetEntry() == NPC_POWER_SPARK) +// { +// spark = unit; +// break; +// } // } -// // Go through portal -// WorldPacket data1(CMSG_GAMEOBJ_USE); -// data1 << portal->GetGUID(); -// bot->GetSession()->HandleGameObjectUseOpcode(data1); +// if (!spark) +// return false; -// return true; +// if (!spark->IsInWorld() || spark->GetMapId() != bot->GetMapId()) +// return false; + +// return bot->GetDistance2d(MALYGOS_STACK_POSITION.first, MALYGOS_STACK_POSITION.second) < 3.0f; // } + +// bool KillPowerSparkAction::Execute(Event event) +// { +// return false; +// } + +bool EoEFlyDrakeAction::isPossible() +{ + Unit* vehicleBase = bot->GetVehicleBase(); + return (vehicleBase && vehicleBase->GetEntry() == NPC_WYRMREST_SKYTALON); +} +bool EoEFlyDrakeAction::Execute(Event event) +{ + Player* master = botAI->GetMaster(); + if (!master) { return false; } + Unit* masterVehicle = master->GetVehicleBase(); + Unit* vehicleBase = bot->GetVehicleBase(); + if (!vehicleBase || !masterVehicle) { return false; } + + MotionMaster* mm = vehicleBase->GetMotionMaster(); + Unit* boss = AI_VALUE2(Unit*, "find target", "malygos"); + if (boss && false) + { + // Handle as boss encounter instead of formation flight + mm->Clear(false); + float distance = vehicleBase->GetExactDist(boss); + float range = 55.0f; // Drake range is 60yd + if (distance > range) + { + mm->MoveForwards(boss, range - distance); + vehicleBase->SendMovementFlagUpdate(); + return true; + } + + vehicleBase->SetFacingToObject(boss); + mm->MoveIdle(); + vehicleBase->SendMovementFlagUpdate(); + return false; + } + + if (vehicleBase->GetExactDist(masterVehicle) > 5.0f) + { + uint8 numPlayers; + bot->GetRaidDifficulty() == RAID_DIFFICULTY_25MAN_NORMAL ? numPlayers = 25 : numPlayers = 10; + // 3/4 of a circle, with frontal cone 90 deg unobstructed + float angle = botAI->GetGroupSlotIndex(bot) * (2*M_PI - M_PI_2)/numPlayers + M_PI_2; + // float angle = M_PI; + vehicleBase->SetCanFly(true); + mm->MoveFollow(masterVehicle, 3.0f, angle); + vehicleBase->SendMovementFlagUpdate(); + return true; + } + return false; +} + +bool EoEDrakeAttackAction::isPossible() +{ + Unit* vehicleBase = bot->GetVehicleBase(); + return (vehicleBase && vehicleBase->GetEntry() == NPC_WYRMREST_SKYTALON); +} +bool EoEDrakeAttackAction::Execute(Event event) +{ + vehicleBase = bot->GetVehicleBase(); + if (!vehicleBase) { return false; } + + // Unit* target = AI_VALUE(Unit*, "current target"); + Unit* boss = AI_VALUE2(Unit*, "find target", "malygos"); + // if (!boss) { return false; } + + if (!boss) + { + GuidVector npcs = AI_VALUE(GuidVector, "possible targets"); + for (auto& npc : npcs) + { + Unit* unit = botAI->GetUnit(npc); + if (!unit || unit->GetEntry() != NPC_MALYGOS) { continue; } + + boss = unit; + break; + } + } + // Check this again to see if a target was assigned + if (!boss) { return false; } + + if (botAI->IsHeal(bot)) + { + return DrakeHealAction(); + } + else + { + return DrakeDpsAction(boss); + } + + return false; +} + +bool EoEDrakeAttackAction::CastDrakeSpellAction(Unit* target, uint32 spellId, uint32 cooldown) +{ + if (botAI->CanCastVehicleSpell(spellId, target)) + if (botAI->CastVehicleSpell(spellId, target)) + { + vehicleBase->AddSpellCooldown(spellId, 0, cooldown); + return true; + } + return false; +} + +bool EoEDrakeAttackAction::DrakeDpsAction(Unit* target) +{ + Unit* vehicleBase = bot->GetVehicleBase(); + if (!vehicleBase) { return false; } + + Vehicle* veh = bot->GetVehicle(); + + uint8 comboPoints = vehicleBase->GetComboPoints(target); + if (comboPoints >= 2) + { + return CastDrakeSpellAction(target, SPELL_ENGULF_IN_FLAMES, 0); + } + else + { + return CastDrakeSpellAction(target, SPELL_FLAME_SPIKE, 0); + } +} + +bool EoEDrakeAttackAction::DrakeHealAction() +{ + Unit* vehicleBase = bot->GetVehicleBase(); + if (!vehicleBase) { return false; } + + Unit* target = vehicleBase->GetComboTarget(); + if (!target) + { + // Unit* newTarget = nullptr; + Unit* newTarget = vehicleBase; + GuidVector members = AI_VALUE(GuidVector, "group members"); + for (auto& member : members) + { + Unit* unit = botAI->GetUnit(member); + if (!unit) + { + continue; + } + + Unit* drake = unit->GetVehicleBase(); + if (!drake || drake->IsFullHealth()) { continue; } + + if (!newTarget || drake->GetHealthPct() < newTarget->GetHealthPct() - 5.0f) + { + newTarget = drake; + } + } + target = newTarget; + } + + uint8 comboPoints = vehicleBase->GetComboPoints(target); + if (comboPoints >= 5) + { + return CastDrakeSpellAction(target, SPELL_LIFE_BURST, 0); + } + else + { + // "Revivify" may be bugged server-side: + // "botAI->CanCastVehicleSpell()" returns SPELL_FAILED_BAD_TARGETS when targeting drakes. + // Forcing the cast attempt seems to succeed, not sure what's going on here. + // return CastDrakeSpellAction(target, SPELL_REVIVIFY, 0); + return botAI->CastVehicleSpell(SPELL_REVIVIFY, target); + } +} diff --git a/src/strategy/raids/eyeofeternity/RaidEoEActions.h b/src/strategy/raids/eyeofeternity/RaidEoEActions.h index b5173f71..d2d158c7 100644 --- a/src/strategy/raids/eyeofeternity/RaidEoEActions.h +++ b/src/strategy/raids/eyeofeternity/RaidEoEActions.h @@ -3,62 +3,67 @@ #include "MovementActions.h" #include "AttackAction.h" +#include "GenericSpellActions.h" #include "PlayerbotAI.h" #include "Playerbots.h" -// const float TSUNAMI_LEFT_SAFE_MELEE = 552.0f; -// const float TSUNAMI_LEFT_SAFE_RANGED = 504.0f; -// const float TSUNAMI_RIGHT_SAFE_ALL = 529.0f; -// const std::pair SARTHARION_MAINTANK_POSITION = {3258.5f, 532.5f}; -// const std::pair SARTHARION_OFFTANK_POSITION = {3230.0f, 526.0f}; -// const std::pair SARTHARION_RANGED_POSITION = {3248.0f, 507.0f}; +const std::pair MALYGOS_MAINTANK_POSITION = {757.0f, 1337.0f}; +const std::pair MALYGOS_STACK_POSITION = {755.0f, 1301.0f}; -// class SartharionTankPositionAction : public AttackAction -// { -// public: -// SartharionTankPositionAction(PlayerbotAI* botAI, std::string const name = "sartharion tank position") -// : AttackAction(botAI, name) {} -// bool Execute(Event event) override; -// }; +class MalygosPositionAction : public MovementAction +{ +public: + MalygosPositionAction(PlayerbotAI* botAI, std::string const name = "malygos position") + : MovementAction(botAI, name) {} + bool Execute(Event event) override; +}; -// class AvoidTwilightFissureAction : public MovementAction -// { -// public: -// AvoidTwilightFissureAction(PlayerbotAI* botAI, std::string const name = "avoid twilight fissure") -// : MovementAction(botAI, name) {} -// bool Execute(Event event) override; -// }; +class MalygosTargetAction : public AttackAction +{ +public: + MalygosTargetAction(PlayerbotAI* botAI, std::string const name = "malygos target") + : AttackAction(botAI, name) {} + bool Execute(Event event) override; +}; -// class AvoidFlameTsunamiAction : public MovementAction -// { -// public: -// AvoidFlameTsunamiAction(PlayerbotAI* botAI, std::string const name = "avoid flame tsunami") -// : MovementAction(botAI, name) {} -// bool Execute(Event event) override; -// }; +class PullPowerSparkAction : public CastSpellAction +{ +public: + PullPowerSparkAction(PlayerbotAI* botAI, std::string const name = "pull power spark") + : CastSpellAction(botAI, "death grip") {} + bool Execute(Event event) override; + bool isPossible() override; + bool isUseful() override; +}; -// class SartharionAttackPriorityAction : public AttackAction -// { -// public: -// SartharionAttackPriorityAction(PlayerbotAI* botAI, std::string const name = "sartharion attack priority") -// : AttackAction(botAI, name) {} -// bool Execute(Event event) override; -// }; +class KillPowerSparkAction : public AttackAction +{ +public: + KillPowerSparkAction(PlayerbotAI* botAI, std::string const name = "kill power spark") + : AttackAction(botAI, name) {} + bool Execute(Event event) override; +}; -// class EnterTwilightPortalAction : public MovementAction -// { -// public: -// EnterTwilightPortalAction(PlayerbotAI* botAI, std::string const name = "enter twilight portal") -// : MovementAction(botAI, name) {} -// bool Execute(Event event) override; -// }; +class EoEFlyDrakeAction : public MovementAction +{ +public: + EoEFlyDrakeAction(PlayerbotAI* ai) : MovementAction(ai, "eoe fly drake") {} + bool Execute(Event event) override; + bool isPossible() override; +}; -// class ExitTwilightPortalAction : public MovementAction -// { -// public: -// ExitTwilightPortalAction(PlayerbotAI* botAI, std::string const name = "exit twilight portal") -// : MovementAction(botAI, name) {} -// bool Execute(Event event) override; -// }; +class EoEDrakeAttackAction : public Action +{ +public: + EoEDrakeAttackAction(PlayerbotAI* botAI) : Action(botAI, "eoe drake attack") {} + bool Execute(Event event) override; + bool isPossible() override; + +protected: + Unit* vehicleBase; + bool CastDrakeSpellAction(Unit* target, uint32 spellId, uint32 cooldown); + bool DrakeDpsAction(Unit* target); + bool DrakeHealAction(); +}; #endif diff --git a/src/strategy/raids/eyeofeternity/RaidEoEMultipliers.cpp b/src/strategy/raids/eyeofeternity/RaidEoEMultipliers.cpp index 9d077802..0866bfb1 100644 --- a/src/strategy/raids/eyeofeternity/RaidEoEMultipliers.cpp +++ b/src/strategy/raids/eyeofeternity/RaidEoEMultipliers.cpp @@ -47,3 +47,68 @@ // } // return 1.0f; // } + +float MalygosMultiplier::GetValue(Action* action) +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "malygos"); + + uint8 phase = MalygosTrigger::getPhase(bot, boss); + if (phase == 0) { return 1.0f; } + + if (phase == 1) + { + if (dynamic_cast(action)) + { + return 0.0f; + } + + if (botAI->IsDps(bot) && dynamic_cast(action)) + { + return 0.0f; + } + + if (botAI->IsRangedDps(bot) && dynamic_cast(action)) + { + return 0.0f; + } + + if (!botAI->IsMainTank(bot) && dynamic_cast(action)) + { + return 0.0f; + } + + // if (dynamic_cast(action) && !dynamic_cast(action)) + // { + // return 0.0f; + // } + } + else if (phase == 2) + { + if (botAI->IsDps(bot) && dynamic_cast(action)) + { + return 0.0f; + } + + if (dynamic_cast(action)) + { + return 0.0f; + } + + if (dynamic_cast(action)) + { + Unit* target = action->GetTarget(); + if (target && target->GetEntry() == NPC_SCION_OF_ETERNITY) + return 0.0f; + } + } + else if (phase == 3) + { + // Suppresses FollowAction as well as some attack-based movements + if (dynamic_cast(action) && !dynamic_cast(action)) + { + return 0.0f; + } + } + + return 1.0f; +} diff --git a/src/strategy/raids/eyeofeternity/RaidEoEMultipliers.h b/src/strategy/raids/eyeofeternity/RaidEoEMultipliers.h index 0a710601..05db5ee9 100644 --- a/src/strategy/raids/eyeofeternity/RaidEoEMultipliers.h +++ b/src/strategy/raids/eyeofeternity/RaidEoEMultipliers.h @@ -4,13 +4,13 @@ #include "Multiplier.h" -// class SartharionMultiplier : public Multiplier -// { -// public: -// SartharionMultiplier(PlayerbotAI* ai) : Multiplier(ai, "sartharion") {} +class MalygosMultiplier : public Multiplier +{ +public: + MalygosMultiplier(PlayerbotAI* ai) : Multiplier(ai, "malygos") {} -// public: -// virtual float GetValue(Action* action); -// }; +public: + virtual float GetValue(Action* action); +}; #endif diff --git a/src/strategy/raids/eyeofeternity/RaidEoEStrategy.cpp b/src/strategy/raids/eyeofeternity/RaidEoEStrategy.cpp index 630ca7f3..f30cf30a 100644 --- a/src/strategy/raids/eyeofeternity/RaidEoEStrategy.cpp +++ b/src/strategy/raids/eyeofeternity/RaidEoEStrategy.cpp @@ -4,29 +4,22 @@ void RaidEoEStrategy::InitTriggers(std::vector& triggers) { - // triggers.push_back( - // new TriggerNode("sartharion tank", - // NextAction::array(0, new NextAction("sartharion tank position", ACTION_MOVE), nullptr))); - // triggers.push_back( - // new TriggerNode("twilight fissure", - // NextAction::array(0, new NextAction("avoid twilight fissure", ACTION_RAID + 2), nullptr))); - // triggers.push_back( - // new TriggerNode("flame tsunami", - // NextAction::array(0, new NextAction("avoid flame tsunami", ACTION_RAID + 1), nullptr))); - // triggers.push_back( - // new TriggerNode("sartharion dps", - // NextAction::array(0, new NextAction("sartharion attack priority", ACTION_RAID), nullptr))); - // // Flank dragon positioning - // triggers.push_back(new TriggerNode("sartharion melee positioning", - // NextAction::array(0, new NextAction("rear flank", ACTION_MOVE + 4), nullptr))); - - // triggers.push_back(new TriggerNode("twilight portal enter", - // NextAction::array(0, new NextAction("enter twilight portal", ACTION_RAID + 1), nullptr))); - // triggers.push_back(new TriggerNode("twilight portal exit", - // NextAction::array(0, new NextAction("exit twilight portal", ACTION_RAID + 1), nullptr))); + triggers.push_back(new TriggerNode("malygos", + NextAction::array(0, new NextAction("malygos position", ACTION_MOVE), nullptr))); + triggers.push_back(new TriggerNode("malygos", + NextAction::array(0, new NextAction("malygos target", ACTION_RAID + 1), nullptr))); + // triggers.push_back(new TriggerNode("power spark", + // NextAction::array(0, new NextAction("pull power spark", ACTION_RAID + 2), nullptr))); + // triggers.push_back(new TriggerNode("power spark", + // NextAction::array(0, new NextAction("kill power spark", ACTION_RAID + 3), nullptr))); + + triggers.push_back(new TriggerNode("group flying", + NextAction::array(0, new NextAction("fly drake", ACTION_NORMAL + 1), nullptr))); + triggers.push_back(new TriggerNode("drake combat", + NextAction::array(0, new NextAction("drake attack", ACTION_NORMAL + 5), nullptr))); } void RaidEoEStrategy::InitMultipliers(std::vector &multipliers) { - // multipliers.push_back(new SartharionMultiplier(botAI)); + multipliers.push_back(new MalygosMultiplier(botAI)); } diff --git a/src/strategy/raids/eyeofeternity/RaidEoETriggerContext.h b/src/strategy/raids/eyeofeternity/RaidEoETriggerContext.h index 438d0ef7..0c58f6cb 100644 --- a/src/strategy/raids/eyeofeternity/RaidEoETriggerContext.h +++ b/src/strategy/raids/eyeofeternity/RaidEoETriggerContext.h @@ -10,23 +10,13 @@ class RaidEoETriggerContext : public NamedObjectContext public: RaidEoETriggerContext() { - // creators["sartharion tank"] = &RaidOsTriggerContext::sartharion_tank; - // creators["flame tsunami"] = &RaidOsTriggerContext::flame_tsunami; - // creators["twilight fissure"] = &RaidOsTriggerContext::twilight_fissure; - // creators["sartharion dps"] = &RaidOsTriggerContext::sartharion_dps; - // creators["sartharion melee positioning"] = &RaidOsTriggerContext::sartharion_melee; - // creators["twilight portal enter"] = &RaidOsTriggerContext::twilight_portal_enter; - // creators["twilight portal exit"] = &RaidOsTriggerContext::twilight_portal_exit; + creators["malygos"] = &RaidEoETriggerContext::malygos; + creators["power spark"] = &RaidEoETriggerContext::power_spark; } private: - // static Trigger* sartharion_tank(PlayerbotAI* ai) { return new SartharionTankTrigger(ai); } - // static Trigger* flame_tsunami(PlayerbotAI* ai) { return new FlameTsunamiTrigger(ai); } - // static Trigger* twilight_fissure(PlayerbotAI* ai) { return new TwilightFissureTrigger(ai); } - // static Trigger* sartharion_dps(PlayerbotAI* ai) { return new SartharionDpsTrigger(ai); } - // static Trigger* sartharion_melee(PlayerbotAI* ai) { return new SartharionMeleePositioningTrigger(ai); } - // static Trigger* twilight_portal_enter(PlayerbotAI* ai) { return new TwilightPortalEnterTrigger(ai); } - // static Trigger* twilight_portal_exit(PlayerbotAI* ai) { return new TwilightPortalExitTrigger(ai); } + static Trigger* power_spark(PlayerbotAI* ai) { return new PowerSparkTrigger(ai); } + static Trigger* malygos(PlayerbotAI* ai) { return new MalygosTrigger(ai); } }; #endif diff --git a/src/strategy/raids/eyeofeternity/RaidEoETriggers.cpp b/src/strategy/raids/eyeofeternity/RaidEoETriggers.cpp index 8427ab8f..2506ea1f 100644 --- a/src/strategy/raids/eyeofeternity/RaidEoETriggers.cpp +++ b/src/strategy/raids/eyeofeternity/RaidEoETriggers.cpp @@ -2,127 +2,52 @@ #include "SharedDefines.h" -// bool SartharionTankTrigger::IsActive() -// { -// Unit* boss = AI_VALUE2(Unit*, "find target", "sartharion"); -// if (!boss) { return false; } +uint8 MalygosTrigger::getPhase(Player* bot, Unit* boss) +{ + uint8 phase = 0; + Unit* vehicle = bot->GetVehicleBase(); + if (bot->GetMapId() != EOE_MAP_ID) { return phase; } + + if (vehicle && vehicle->GetEntry() == NPC_WYRMREST_SKYTALON) + { + phase = 3; + } + else if (boss && boss->HealthAbovePct(50)) + { + phase = 1; + } + else if (boss) + { + phase = 2; + } + + return phase; +} + +bool MalygosTrigger::IsActive() +{ + return bool(AI_VALUE2(Unit*, "find target", "malygos")); +} + +bool PowerSparkTrigger::IsActive() +{ + Unit* boss = AI_VALUE2(Unit*, "find target", "malygos"); + if (!boss) { return false; } + + if (bot->getClass() != CLASS_DEATH_KNIGHT) + { + return false; + } + + GuidVector targets = AI_VALUE(GuidVector, "possible targets no los"); + for (auto& target : targets) + { + Unit* unit = botAI->GetUnit(target); + if (unit && unit->GetEntry() == NPC_POWER_SPARK) + { + return true; + } + } -// return botAI->IsTank(bot); -// } - -// bool FlameTsunamiTrigger::IsActive() -// { -// if (botAI->IsTank(bot)) { return false; } - -// Unit* boss = AI_VALUE2(Unit*, "find target", "sartharion"); -// if (!boss) { return false; } - - -// GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); -// for (auto& npc : npcs) -// { -// Unit* unit = botAI->GetUnit(npc); -// if (unit) -// { -// if (unit->GetEntry() == NPC_FLAME_TSUNAMI) -// { -// return true; -// } -// } -// } - -// return false; -// } - -// bool TwilightFissureTrigger::IsActive() -// { -// Unit* boss = AI_VALUE2(Unit*, "find target", "sartharion"); -// if (!boss) { return false; } - - -// GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); -// for (auto& npc : npcs) -// { -// Unit* unit = botAI->GetUnit(npc); -// if (unit) -// { -// if (unit->GetEntry() == NPC_TWILIGHT_FISSURE) -// { -// return true; -// } -// } -// } - -// return false; -// } - -// bool SartharionDpsTrigger::IsActive() -// { -// Unit* boss = AI_VALUE2(Unit*, "find target", "sartharion"); -// if (!boss) { return false; } - -// return botAI->IsDps(bot); -// } - -// bool SartharionMeleePositioningTrigger::IsActive() -// { -// if (!botAI->IsMelee(bot) || !botAI->IsDps(bot)) { return false; } - -// Unit* boss = AI_VALUE2(Unit*, "find target", "sartharion"); -// if (!boss) { return false; } - -// Unit* shadron = AI_VALUE2(Unit*, "find target", "shadron"); -// Unit* tenebron = AI_VALUE2(Unit*, "find target", "tenebron"); -// Unit* vesperon = AI_VALUE2(Unit*, "find target", "vesperon"); - -// return !(shadron || tenebron || vesperon); -// } - -// bool TwilightPortalEnterTrigger::IsActive() -// { -// if (botAI->IsMainTank(bot) || botAI->IsHealAssistantOfIndex(bot, 0)) { return false; } - -// // In 25-man, take two healers in. Otherwise just take one -// // if (bot->GetRaidDifficulty() == RAID_DIFFICULTY_25MAN_NORMAL) -// // { -// // if (botAI->IsHealAssistantOfIndex(bot, 0) || botAI->IsHealAssistantOfIndex(bot, 1)) -// // { -// // return false; -// // } -// // } -// // else -// // { -// // if (botAI->IsHealAssistantOfIndex(bot, 0)) -// // { -// // return false; -// // } -// // } - - -// // Don't enter portal until drakes are dead -// if (bot->HasAura(SPELL_POWER_OF_SHADRON) || -// bot->HasAura(SPELL_POWER_OF_TENEBRON) || -// bot->HasAura(SPELL_POWER_OF_VESPERON)) -// { -// return false; -// } - -// Unit* boss = AI_VALUE2(Unit*, "find target", "sartharion"); -// if (!boss) { return false; } - -// // GuidVector objects = AI_VALUE(GuidVector, "nearest game objects no los"); -// // for (auto& object : objects) -// // { -// // GameObject* go = botAI->GetGameObject(object); -// // if (go && go->GetEntry() == GO_TWILIGHT_PORTAL) -// // { -// // return true; -// // } -// // } -// return bool(bot->FindNearestGameObject(GO_TWILIGHT_PORTAL, 100.0f)); -// } - -// bool TwilightPortalExitTrigger::IsActive() -// { -// return bot->HasAura(SPELL_TWILIGHT_SHIFT) && !AI_VALUE2(Unit*, "find target", "acolyte of shadron"); -// } \ No newline at end of file + return false; +} diff --git a/src/strategy/raids/eyeofeternity/RaidEoETriggers.h b/src/strategy/raids/eyeofeternity/RaidEoETriggers.h index 798e5a49..9262c114 100644 --- a/src/strategy/raids/eyeofeternity/RaidEoETriggers.h +++ b/src/strategy/raids/eyeofeternity/RaidEoETriggers.h @@ -7,114 +7,52 @@ enum EyeOfEternityIDs { - // // Bosses - // NPC_SARTHARION = 28860, - // NPC_SHADRON = 30451, - // NPC_TENEBRON = 30452, - // NPC_VESPERON = 30449, + NPC_MALYGOS = 28859, + NPC_POWER_SPARK = 30084, + NPC_NEXUS_LORD = 30245, + NPC_SCION_OF_ETERNITY = 30249, + NPC_WYRMREST_SKYTALON = 30161, + + SPELL_POWER_SPARK_VISUAL = 55845, + SPELL_POWER_SPARK_GROUND_BUFF = 55852, + SPELL_POWER_SPARK_MALYGOS_BUFF = 56152, - // // Mini-boss shared - // SPELL_SHADOW_BREATH = 57570, - // SPELL_SHADOW_FISSURE = 57579, - // SPELL_SUMMON_TWILIGHT_WHELP = 58035, - // SPELL_GIFT_OF_TWILIGHT_SHADOW = 57835, - // SPELL_TWILIGHT_TORMENT_VESPERON = 57935, + SPELL_TELEPORT_VISUAL = 52096, - // // Sartharion - // SPELL_SARTHARION_CLEAVE = 56909, - // SPELL_SARTHARION_FLAME_BREATH = 56908, - // SPELL_SARTHARION_TAIL_LASH = 56910, - // SPELL_CYCLONE_AURA_PERIODIC = 57598, - // SPELL_LAVA_STRIKE_DUMMY = 57578, - // SPELL_LAVA_STRIKE_DUMMY_TRIGGER = 57697, - // SPELL_LAVA_STRIKE_SUMMON = 57572, - // SPELL_SARTHARION_PYROBUFFET = 56916, - // SPELL_SARTHARION_BERSERK = 61632, - // SPELL_SARTHARION_TWILIGHT_REVENGE = 60639, + SPELL_SCION_ARCANE_BARRAGE = 56397, + SPELL_ARCANE_SHOCK_N = 57058, + SPELL_ARCANE_SHOCK_H = 60073, + SPELL_HASTE = 57060, - // // Sartharion with drakes - // SPELL_WILL_OF_SARTHARION = 61254, - // SPELL_POWER_OF_TENEBRON = 61248, - // SPELL_POWER_OF_VESPERON = 61251, - // SPELL_POWER_OF_SHADRON = 58105, - // SPELL_GIFT_OF_TWILIGHT_FIRE = 58766, + SPELL_ALEXSTRASZA_GIFT = 61028, - // // Visuals - // SPELL_EGG_MARKER_VISUAL = 58547, - // SPELL_FLAME_TSUNAMI_VISUAL = 57494, - - // // Misc - // SPELL_FADE_ARMOR = 60708, - // SPELL_FLAME_TSUNAMI_DAMAGE_AURA = 57492, - // SPELL_FLAME_TSUNAMI_LEAP = 60241, - // SPELL_SARTHARION_PYROBUFFET_TRIGGER = 57557, - - // NPC_TWILIGHT_EGG = 30882, - // NPC_TWILIGHT_WHELP = 30890, - // NPC_DISCIPLE_OF_SHADRON = 30688, - // NPC_DISCIPLE_OF_VESPERON = 30858, - // NPC_ACOLYTE_OF_SHADRON = 31218, - // NPC_ACOLYTE_OF_VESPERON = 31219, - - // // Sartharion fight - // NPC_LAVA_BLAZE = 30643, - // NPC_FLAME_TSUNAMI = 30616, - // NPC_SAFE_AREA_TRIGGER = 30494, - // NPC_TWILIGHT_FISSURE = 30641, - // GO_TWILIGHT_PORTAL = 193988, - // GO_NORMAL_PORTAL = 193989, - // SPELL_TWILIGHT_SHIFT = 57874, + // Drake Abilities: + // DPS + SPELL_FLAME_SPIKE = 56091, + SPELL_ENGULF_IN_FLAMES = 56092, + // Healing + SPELL_REVIVIFY = 57090, + SPELL_LIFE_BURST = 57143, + // Utility + SPELL_FLAME_SHIELD = 57108, + SPELL_BLAZING_SPEED = 57092, }; -const uint32 EOE_MAP_ID = 615; +const uint32 EOE_MAP_ID = 616; -// class SartharionTankTrigger : public Trigger -// { -// public: -// SartharionTankTrigger(PlayerbotAI* botAI) : Trigger(botAI, "sartharion tank") {} -// bool IsActive() override; -// }; +class MalygosTrigger : public Trigger +{ +public: + MalygosTrigger(PlayerbotAI* botAI) : Trigger(botAI, "malygos") {} + bool IsActive() override; + uint8 static getPhase(Player* bot, Unit* boss); +}; -// class FlameTsunamiTrigger : public Trigger -// { -// public: -// FlameTsunamiTrigger(PlayerbotAI* botAI) : Trigger(botAI, "flame tsunami") {} -// bool IsActive() override; -// }; - -// class TwilightFissureTrigger : public Trigger -// { -// public: -// TwilightFissureTrigger(PlayerbotAI* botAI) : Trigger(botAI, "twilight fissure") {} -// bool IsActive() override; -// }; - -// class SartharionDpsTrigger : public Trigger -// { -// public: -// SartharionDpsTrigger(PlayerbotAI* botAI) : Trigger(botAI, "sartharion dps") {} -// bool IsActive() override; -// }; - -// class SartharionMeleePositioningTrigger : public Trigger -// { -// public: -// SartharionMeleePositioningTrigger(PlayerbotAI* botAI) : Trigger(botAI, "sartharion melee positioning") {} -// bool IsActive() override; -// }; - -// class TwilightPortalEnterTrigger : public Trigger -// { -// public: -// TwilightPortalEnterTrigger(PlayerbotAI* botAI) : Trigger(botAI, "twilight portal enter") {} -// bool IsActive() override; -// }; - -// class TwilightPortalExitTrigger : public Trigger -// { -// public: -// TwilightPortalExitTrigger(PlayerbotAI* botAI) : Trigger(botAI, "twilight portal exit") {} -// bool IsActive() override; -// }; +class PowerSparkTrigger : public Trigger +{ +public: + PowerSparkTrigger(PlayerbotAI* botAI) : Trigger(botAI, "power spark") {} + bool IsActive() override; +}; #endif