From 67b160934ed18a4f8029cc5bf6a5e2b7dedfadef Mon Sep 17 00:00:00 2001 From: SmashingQuasar Date: Wed, 18 Feb 2026 12:49:34 +0100 Subject: [PATCH 1/8] feat: Added Gurubashi Bat Rider strategy. --- src/Ai/Raid/RaidStrategyContext.h | 3 + src/Ai/Raid/ZulGurub/RaidZGStrategy.h | 35 +++++++ src/Ai/Raid/ZulGurub/RaidZGTriggerContext.h | 21 +++++ ...urubashiBatRiderUnstableConcoctionAction.h | 86 +++++++++++++++++ ...ashiBatRiderUnstableConcoctionMultiplier.h | 94 +++++++++++++++++++ ...rubashiBatRiderUnstableConcoctionTrigger.h | 75 +++++++++++++++ .../definition/enum/GurubashiBatRiderEnum.h | 8 ++ src/Bot/Engine/AiObjectContext.cpp | 1 + src/Bot/PlayerbotAI.cpp | 3 + 9 files changed, 326 insertions(+) create mode 100644 src/Ai/Raid/ZulGurub/RaidZGStrategy.h create mode 100644 src/Ai/Raid/ZulGurub/RaidZGTriggerContext.h create mode 100644 src/Ai/Raid/ZulGurub/Trash/GurubashiBatRider/GurubashiBatRiderUnstableConcoctionAction.h create mode 100644 src/Ai/Raid/ZulGurub/Trash/GurubashiBatRider/GurubashiBatRiderUnstableConcoctionMultiplier.h create mode 100644 src/Ai/Raid/ZulGurub/Trash/GurubashiBatRider/GurubashiBatRiderUnstableConcoctionTrigger.h create mode 100644 src/Ai/Raid/ZulGurub/Trash/GurubashiBatRider/definition/enum/GurubashiBatRiderEnum.h diff --git a/src/Ai/Raid/RaidStrategyContext.h b/src/Ai/Raid/RaidStrategyContext.h index 970ecf4aa59..f678ebfe775 100644 --- a/src/Ai/Raid/RaidStrategyContext.h +++ b/src/Ai/Raid/RaidStrategyContext.h @@ -18,6 +18,7 @@ #include "RaidUlduarStrategy.h" #include "RaidOnyxiaStrategy.h" #include "RaidIccStrategy.h" +#include "RaidZGStrategy.h" class RaidStrategyContext : public NamedObjectContext { @@ -34,6 +35,7 @@ class RaidStrategyContext : public NamedObjectContext creators["ssc"] = &RaidStrategyContext::ssc; creators["tempestkeep"] = &RaidStrategyContext::tempestkeep; creators["zulaman"] = &RaidStrategyContext::zulaman; + creators["zg"] = &RaidStrategyContext::zg; creators["wotlk-os"] = &RaidStrategyContext::wotlk_os; creators["wotlk-eoe"] = &RaidStrategyContext::wotlk_eoe; creators["voa"] = &RaidStrategyContext::voa; @@ -53,6 +55,7 @@ class RaidStrategyContext : public NamedObjectContext static Strategy* ssc(PlayerbotAI* botAI) { return new RaidSSCStrategy(botAI); } static Strategy* tempestkeep(PlayerbotAI* botAI) { return new RaidTempestKeepStrategy(botAI); } static Strategy* zulaman(PlayerbotAI* botAI) { return new RaidZulAmanStrategy(botAI); } + static Strategy* zg(PlayerbotAI* botAI) { return new RaidZGStrategy(botAI); } static Strategy* wotlk_os(PlayerbotAI* botAI) { return new RaidOsStrategy(botAI); } static Strategy* wotlk_eoe(PlayerbotAI* botAI) { return new RaidEoEStrategy(botAI); } static Strategy* voa(PlayerbotAI* botAI) { return new RaidVoAStrategy(botAI); } diff --git a/src/Ai/Raid/ZulGurub/RaidZGStrategy.h b/src/Ai/Raid/ZulGurub/RaidZGStrategy.h new file mode 100644 index 00000000000..61d05166f17 --- /dev/null +++ b/src/Ai/Raid/ZulGurub/RaidZGStrategy.h @@ -0,0 +1,35 @@ +#pragma once + +#include "CreateNextAction.h" +#include "Strategy.h" +#include "Multiplier.h" +#include "Trash/GurubashiBatRider/GurubashiBatRiderUnstableConcoctionAction.h" +#include "ZulGurub/Trash/GurubashiBatRider/GurubashiBatRiderUnstableConcoctionMultiplier.h" + +class RaidZGStrategy : public Strategy +{ +public: + RaidZGStrategy(PlayerbotAI* botAI) : Strategy(botAI) {} + + const std::string getName() override + { + return "zg"; + } + + void InitTriggers(std::vector& triggers) override + { + triggers.push_back( + new TriggerNode( + "gurubashi bat rider unstable concoction", + { + CreateNextAction(ACTION_EMERGENCY + 10.0f) + } + ) + ); + } + + void InitMultipliers(std::vector& multipliers) override + { + multipliers.push_back(new GurubashiBatRiderUnstableConcoctionMultiplier(botAI)); + } +}; diff --git a/src/Ai/Raid/ZulGurub/RaidZGTriggerContext.h b/src/Ai/Raid/ZulGurub/RaidZGTriggerContext.h new file mode 100644 index 00000000000..77067267c50 --- /dev/null +++ b/src/Ai/Raid/ZulGurub/RaidZGTriggerContext.h @@ -0,0 +1,21 @@ +#pragma once + +#include "AiObjectContext.h" +#include "Trash/GurubashiBatRider/GurubashiBatRiderUnstableConcoctionTrigger.h" + +class RaidZGTriggerContext : public NamedObjectContext +{ +public: + RaidZGTriggerContext() + { + // Trash + creators["gurubashi bat rider unstable concoction"] = &RaidZGTriggerContext::gurubashiBatRiderUnstableConcoction; + } + +private: + // Trash + static Trigger* gurubashiBatRiderUnstableConcoction(PlayerbotAI* botAI) + { + return new GurubashiBatRiderUnstableConcoctionTrigger(botAI); + } +}; diff --git a/src/Ai/Raid/ZulGurub/Trash/GurubashiBatRider/GurubashiBatRiderUnstableConcoctionAction.h b/src/Ai/Raid/ZulGurub/Trash/GurubashiBatRider/GurubashiBatRiderUnstableConcoctionAction.h new file mode 100644 index 00000000000..aeefffddf39 --- /dev/null +++ b/src/Ai/Raid/ZulGurub/Trash/GurubashiBatRider/GurubashiBatRiderUnstableConcoctionAction.h @@ -0,0 +1,86 @@ +#pragma once + +#include "Spell.h" +#include "AiObjectContext.h" +#include "MovementActions.h" +#include "Player.h" +#include "PlayerbotAI.h" +#include "definition/enum/GurubashiBatRiderEnum.h" + +class GurubashiBatRiderUnstableConcoctionAction : public MovementAction +{ +public: + GurubashiBatRiderUnstableConcoctionAction( + PlayerbotAI* botAI, + const std::string name = "gurubashi bat rider unstable concoction" + ) : MovementAction(botAI, name) {} + + bool Execute(Event) override + { + if (this->bot->GetMapId() != MAP_ZUL_GURUB) + { + return false; + } + + if (this->bot->IsInCombat() == false) + { + return false; + } + + Value* const nearestUnitsValue = this->context->GetValue("nearest hostile npcs"); + + if (nearestUnitsValue == nullptr) + { + return false; + } + + const GuidVector nearestUnitsGuids = nearestUnitsValue->Get(); + + for (const ObjectGuid& guid : nearestUnitsGuids) + { + Unit* const unit = this->botAI->GetUnit(guid); + + if (unit == nullptr) + { + continue; + } + + if (unit->GetEntry() != uint32_t(GurubashiBatRiderEnum::ENTRY)) + { + continue; + } + + const Spell* const castedSpell = unit->GetFirstCurrentCastingSpell(); + + if (castedSpell == nullptr) + { + continue; + } + + const SpellInfo* const castedSpellInfo = castedSpell->GetSpellInfo(); + + if (castedSpellInfo == nullptr) + { + continue; + } + + if (castedSpellInfo->Id != uint32_t(GurubashiBatRiderEnum::SPELL_UNSTABLE_CONCOCTION)) + { + continue; + } + + const float safeDistance = float(GurubashiBatRiderEnum::SPELL_UNSTABLE_CONCOCTION_RADIUS) - this->bot->GetDistance2d(unit); + + if (safeDistance <= 0.0f) + { + continue; + } + + this->MoveAway(unit, safeDistance); + + return true; + } + + return false; + } +}; diff --git a/src/Ai/Raid/ZulGurub/Trash/GurubashiBatRider/GurubashiBatRiderUnstableConcoctionMultiplier.h b/src/Ai/Raid/ZulGurub/Trash/GurubashiBatRider/GurubashiBatRiderUnstableConcoctionMultiplier.h new file mode 100644 index 00000000000..c2b39452cf0 --- /dev/null +++ b/src/Ai/Raid/ZulGurub/Trash/GurubashiBatRider/GurubashiBatRiderUnstableConcoctionMultiplier.h @@ -0,0 +1,94 @@ +#pragma once + +#include "Spell.h" +#include "AiObjectContext.h" +#include "MovementActions.h" +#include "Multiplier.h" +#include "PlayerbotAI.h" +#include "Unit.h" +#include "Value.h" + +#include "GurubashiBatRiderUnstableConcoctionAction.h" +#include "definition/enum/GurubashiBatRiderEnum.h" + +class GurubashiBatRiderUnstableConcoctionMultiplier : public Multiplier +{ +public: + GurubashiBatRiderUnstableConcoctionMultiplier(PlayerbotAI* botAI) : Multiplier(botAI, "gurubashi bat rider unstable concoction") {} + + float GetValue(Action& action) override + { + if (this->bot->GetMapId() != MAP_ZUL_GURUB) + { + return 1.0f; + } + + if (this->bot->IsInCombat() == false) + { + return 1.0f; + } + + Value* const nearestUnitsValue = this->context->GetValue("nearest hostile npcs"); + + if (nearestUnitsValue == nullptr) + { + return 1.0f; + } + + const GuidVector nearestUnitsGuids = nearestUnitsValue->Get(); + + bool unstableConcoctionActive = false; + + for (const ObjectGuid& guid : nearestUnitsGuids) + { + Unit* const unit = this->botAI->GetUnit(guid); + + if (unit == nullptr) + { + continue; + } + + if (unit->GetEntry() != uint32_t(GurubashiBatRiderEnum::ENTRY)) + { + continue; + } + + const Spell* const castedSpell = unit->GetFirstCurrentCastingSpell(); + + if (castedSpell == nullptr) + { + continue; + } + + const SpellInfo* const castedSpellInfo = castedSpell->GetSpellInfo(); + + if (castedSpellInfo == nullptr) + { + continue; + } + + if (castedSpellInfo->Id != uint32_t(GurubashiBatRiderEnum::SPELL_UNSTABLE_CONCOCTION)) + { + continue; + } + + unstableConcoctionActive = true; + + break; + } + + if (!unstableConcoctionActive) + { + return 1.0f; + } + + const GurubashiBatRiderUnstableConcoctionAction* const movementAction = dynamic_cast(&action); + + if (movementAction == nullptr) + { + return 0.0f; + } + + return 1.0f; + } +}; diff --git a/src/Ai/Raid/ZulGurub/Trash/GurubashiBatRider/GurubashiBatRiderUnstableConcoctionTrigger.h b/src/Ai/Raid/ZulGurub/Trash/GurubashiBatRider/GurubashiBatRiderUnstableConcoctionTrigger.h new file mode 100644 index 00000000000..d3253e18389 --- /dev/null +++ b/src/Ai/Raid/ZulGurub/Trash/GurubashiBatRider/GurubashiBatRiderUnstableConcoctionTrigger.h @@ -0,0 +1,75 @@ +#pragma once + +#include "Spell.h" + +#include "AiObjectContext.h" +#include "Player.h" +#include "PlayerbotAI.h" +#include "Trigger.h" +#include "definition/enum/GurubashiBatRiderEnum.h" + +class GurubashiBatRiderUnstableConcoctionTrigger : public Trigger +{ +public: + GurubashiBatRiderUnstableConcoctionTrigger(PlayerbotAI* botAI) : Trigger(botAI, "gurubashi bat rider unstable concoction") {} + + bool IsActive() override + { + if (this->bot->GetMapId() != MAP_ZUL_GURUB) + { + return false; + } + + if (this->bot->IsInCombat() == false) + { + return false; + } + + Value* const nearestUnitsValue = this->context->GetValue("nearest hostile npcs"); + + if (nearestUnitsValue == nullptr) + { + return false; + } + + const GuidVector nearestUnitsGuids = nearestUnitsValue->Get(); + + for (const ObjectGuid& guid : nearestUnitsGuids) + { + Unit* const unit = this->botAI->GetUnit(guid); + + if (unit == nullptr) + { + continue; + } + + if (unit->GetEntry() != uint32_t(GurubashiBatRiderEnum::ENTRY)) + { + continue; + } + + const Spell* const castedSpell = unit->GetFirstCurrentCastingSpell(); + + if (castedSpell == nullptr) + { + continue; + } + + const SpellInfo* const castedSpellInfo = castedSpell->GetSpellInfo(); + + if (castedSpellInfo == nullptr) + { + continue; + } + + if (castedSpellInfo->Id != uint32_t(GurubashiBatRiderEnum::SPELL_UNSTABLE_CONCOCTION)) + { + continue; + } + + return true; + } + + return false; + } +}; diff --git a/src/Ai/Raid/ZulGurub/Trash/GurubashiBatRider/definition/enum/GurubashiBatRiderEnum.h b/src/Ai/Raid/ZulGurub/Trash/GurubashiBatRider/definition/enum/GurubashiBatRiderEnum.h new file mode 100644 index 00000000000..970c624c541 --- /dev/null +++ b/src/Ai/Raid/ZulGurub/Trash/GurubashiBatRider/definition/enum/GurubashiBatRiderEnum.h @@ -0,0 +1,8 @@ +#pragma once + +enum class GurubashiBatRiderEnum +{ + ENTRY = 14750u, + SPELL_UNSTABLE_CONCOCTION = 24024u, + SPELL_UNSTABLE_CONCOCTION_RADIUS = 10u +}; diff --git a/src/Bot/Engine/AiObjectContext.cpp b/src/Bot/Engine/AiObjectContext.cpp index 3b947e2f6ce..9ac969e5946 100644 --- a/src/Bot/Engine/AiObjectContext.cpp +++ b/src/Bot/Engine/AiObjectContext.cpp @@ -11,6 +11,7 @@ #include "MageAiObjectContext.h" #include "PaladinAiObjectContext.h" #include "PriestAiObjectContext.h" +#include "RaidZGTriggerContext.h" #include "RogueAiObjectContext.h" #include "ShamanAiObjectContext.h" #include "WarlockAiObjectContext.h" diff --git a/src/Bot/PlayerbotAI.cpp b/src/Bot/PlayerbotAI.cpp index 2acdd90d948..17285ee858d 100644 --- a/src/Bot/PlayerbotAI.cpp +++ b/src/Bot/PlayerbotAI.cpp @@ -1608,6 +1608,9 @@ void PlayerbotAI::ApplyInstanceStrategies(uint32 mapId, bool tellMaster) std::string strategyName; switch (mapId) { + case MAP_ZUL_GURUB: + strategyName = "zg"; // Zul'Gurub + break; case 249: strategyName = "onyxia"; // Onyxia's Lair break; From b3644c78fad4af18bb19d34d0bc348e8876a80ed Mon Sep 17 00:00:00 2001 From: SmashingQuasar Date: Thu, 19 Feb 2026 16:03:05 +0100 Subject: [PATCH 2/8] feat: Added a functional Venoxis strategy. --- .../HighPriestVenoxisPhase1HolyWrathAction.h | 43 ++++ ...ghPriestVenoxisPhase1HolyWrathMultiplier.h | 88 ++++++++ .../HighPriestVenoxisPhase1HolyWrathTrigger.h | 48 +++++ ...xisPhase1RazzashiCobrasDPSPriorityAction.h | 38 ++++ ...isPhase1RazzashiCobrasDPSPriorityTrigger.h | 52 +++++ ...xisPhase1RazzashiCobrasPositioningAction.h | 103 ++++++++++ ...isPhase1RazzashiCobrasPositioningTrigger.h | 71 +++++++ ...estVenoxisPhase1VenoxisPositioningAction.h | 88 ++++++++ ...stVenoxisPhase1VenoxisPositioningTrigger.h | 59 ++++++ .../definition/enum/HighPriestVenoxisEnum.h | 10 + .../definition/enum/RazzashiCobraEnum.h | 6 + .../facade/HighPriestVenoxisFacade.h | 189 ++++++++++++++++++ src/Ai/Raid/ZulGurub/RaidZGStrategy.h | 45 ++++- src/Ai/Raid/ZulGurub/RaidZGTriggerContext.h | 29 +++ src/Ai/Raid/ZulGurub/facade/ZulGurubFacade.h | 23 +++ 15 files changed, 891 insertions(+), 1 deletion(-) create mode 100644 src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/HolyWrath/HighPriestVenoxisPhase1HolyWrathAction.h create mode 100644 src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/HolyWrath/HighPriestVenoxisPhase1HolyWrathMultiplier.h create mode 100644 src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/HolyWrath/HighPriestVenoxisPhase1HolyWrathTrigger.h create mode 100644 src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/RazzashiCobrasDPSPriority/HighPriestVenoxisPhase1RazzashiCobrasDPSPriorityAction.h create mode 100644 src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/RazzashiCobrasDPSPriority/HighPriestVenoxisPhase1RazzashiCobrasDPSPriorityTrigger.h create mode 100644 src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/RazzashiCobrasPositioning/HighPriestVenoxisPhase1RazzashiCobrasPositioningAction.h create mode 100644 src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/RazzashiCobrasPositioning/HighPriestVenoxisPhase1RazzashiCobrasPositioningTrigger.h create mode 100644 src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/VenoxisPositioning/HighPriestVenoxisPhase1VenoxisPositioningAction.h create mode 100644 src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/VenoxisPositioning/HighPriestVenoxisPhase1VenoxisPositioningTrigger.h create mode 100644 src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/definition/enum/HighPriestVenoxisEnum.h create mode 100644 src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/definition/enum/RazzashiCobraEnum.h create mode 100644 src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/facade/HighPriestVenoxisFacade.h create mode 100644 src/Ai/Raid/ZulGurub/facade/ZulGurubFacade.h diff --git a/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/HolyWrath/HighPriestVenoxisPhase1HolyWrathAction.h b/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/HolyWrath/HighPriestVenoxisPhase1HolyWrathAction.h new file mode 100644 index 00000000000..59bdc7af988 --- /dev/null +++ b/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/HolyWrath/HighPriestVenoxisPhase1HolyWrathAction.h @@ -0,0 +1,43 @@ +#pragma once + +#include "Spell.h" +#include "AiObjectContext.h" +#include "MovementActions.h" +#include "PlayerbotAI.h" + +#include "../../facade/HighPriestVenoxisFacade.h" + +class HighPriestVenoxisPhase1HolyWrathAction : public MovementAction +{ +public: + HighPriestVenoxisPhase1HolyWrathAction( + PlayerbotAI* botAI, + const std::string name = "high priest venoxis phase 1 holy wrath" + ) : MovementAction(botAI, name) {} + + bool Execute(Event) override + { + if (this->bot == nullptr) + { + return false; + } + + Unit* const venoxis = HighPriestVenoxisFacade::FindActiveBoss(*this->bot); + + if (venoxis == nullptr) + { + return false; + } + + const float safeDistance = float(HighPriestVenoxisEnum::PHASE_1_SAFE_DISTANCE) - this->bot->GetDistance2d(venoxis); + + if (safeDistance <= 0.0f) + { + return false; + } + + this->MoveAway(venoxis, safeDistance); + + return true; + } +}; diff --git a/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/HolyWrath/HighPriestVenoxisPhase1HolyWrathMultiplier.h b/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/HolyWrath/HighPriestVenoxisPhase1HolyWrathMultiplier.h new file mode 100644 index 00000000000..c6d100d7944 --- /dev/null +++ b/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/HolyWrath/HighPriestVenoxisPhase1HolyWrathMultiplier.h @@ -0,0 +1,88 @@ +#pragma once + +#include "AiObjectContext.h" +#include "MovementActions.h" +#include "Multiplier.h" +#include "PlayerbotAI.h" +#include "Value.h" + +#include "../../facade/HighPriestVenoxisFacade.h" +// #include "HighPriestVenoxisPhase1HolyWrathAction.h" +// #include "../VenoxisPositioning/HighPriestVenoxisPhase1VenoxisPositioningAction.h" +// #include "../RazzashiCobrasPositioning/HighPriestVenoxisPhase1RazzashiCobrasPositioningAction.h" +// #include "../RangedDPSPositioning/HighPriestVenoxisPhase1RangedPositioningAction.h" +#include "GenericSpellActions.h" + + +class HighPriestVenoxisPhase1HolyWrathMultiplier : public Multiplier +{ +public: + HighPriestVenoxisPhase1HolyWrathMultiplier(PlayerbotAI* botAI) : Multiplier(botAI, "high priest venoxis phase 1 holy wrath") {} + + float GetValue(Action& action) override + { + if (this->bot == nullptr) + { + return 0.0f; + } + + if (this->botAI->IsTank(this->bot) == true) + { + return 1.0f; + } + + if (HighPriestVenoxisFacade::IsInPhase1(*this->bot) == false) + { + return 1.0f; + } + + if (HighPriestVenoxisFacade::IsAtSafeDistanceFromVenoxis(*this->bot) == true) + { + return 1.0f; + } + + const std::vector cobras = HighPriestVenoxisFacade::FindRazzashiCobras(*this->bot); + + if (!cobras.empty()) + { + return 1.0f; + } + + // const HighPriestVenoxisPhase1HolyWrathAction* const holyWrathAction = dynamic_cast(&action); + + // if (holyWrathAction != nullptr) + // { + // return 1.0f; + // } + + // const HighPriestVenoxisPhase1VenoxisPositioningAction* const venoxisPositioningAction = dynamic_cast(&action); + + // if (venoxisPositioningAction != nullptr) + // { + // return 1.0f; + // } + + // const HighPriestVenoxisPhase1RangedPositioningAction* const rangedPositioningAction = dynamic_cast(&action); + + // if (rangedPositioningAction != nullptr) + // { + // return 1.0f; + // } + + // const HighPriestVenoxisPhase1RazzashiCobrasPositioningAction* const razzashiCobrasPositioningAction = dynamic_cast(&action); + + // if (razzashiCobrasPositioningAction != nullptr) + // { + // return 1.0f; + // } + + // const CastSpellAction* const castSpellAction = dynamic_cast(&action); + + // if (castSpellAction != nullptr) + // { + // return 1.0f; + // } + + return 0.0f; + } +}; diff --git a/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/HolyWrath/HighPriestVenoxisPhase1HolyWrathTrigger.h b/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/HolyWrath/HighPriestVenoxisPhase1HolyWrathTrigger.h new file mode 100644 index 00000000000..cac3451739e --- /dev/null +++ b/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/HolyWrath/HighPriestVenoxisPhase1HolyWrathTrigger.h @@ -0,0 +1,48 @@ +#pragma once + +#include "AiObjectContext.h" +#include "PlayerbotAI.h" +#include "Trigger.h" + +#include "../../facade/HighPriestVenoxisFacade.h" + +class HighPriestVenoxisPhase1HolyWrathTrigger : public Trigger +{ +public: + HighPriestVenoxisPhase1HolyWrathTrigger(PlayerbotAI* botAI) : Trigger(botAI, "high priest venoxis phase 1 holy wrath") {} + + bool IsActive() override + { + if (this->bot == nullptr) + { + return false; + } + + if (this->botAI == nullptr) + { + return false; + } + + if (HighPriestVenoxisFacade::IsInCombatWithVenoxis(*this->bot) == false) + { + return false; + } + + if (this->botAI->IsMainTank(this->bot) == true) + { + return false; + } + + if (HighPriestVenoxisFacade::IsInPhase1(*this->bot) == false) + { + return false; + } + + if (HighPriestVenoxisFacade::IsAtSafeDistanceFromVenoxis(*this->bot) == true) + { + return false; + } + + return true; + } +}; diff --git a/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/RazzashiCobrasDPSPriority/HighPriestVenoxisPhase1RazzashiCobrasDPSPriorityAction.h b/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/RazzashiCobrasDPSPriority/HighPriestVenoxisPhase1RazzashiCobrasDPSPriorityAction.h new file mode 100644 index 00000000000..04377e09c32 --- /dev/null +++ b/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/RazzashiCobrasDPSPriority/HighPriestVenoxisPhase1RazzashiCobrasDPSPriorityAction.h @@ -0,0 +1,38 @@ +#pragma once + +#include "Spell.h" +#include "AiObjectContext.h" +#include "AttackAction.h" +#include "PlayerbotAI.h" + +#include "../../facade/HighPriestVenoxisFacade.h" + +class HighPriestVenoxisPhase1RazzashiCobrasDPSPriorityAction : public AttackAction +{ +public: + HighPriestVenoxisPhase1RazzashiCobrasDPSPriorityAction( + PlayerbotAI* botAI, + const std::string name = "high priest venoxis phase 1 razzashi cobras dps priority" + ) : AttackAction(botAI, name) {} + + bool Execute(Event) override + { + if (this->bot == nullptr) + { + return false; + } + + const std::vector razzashiCobras = HighPriestVenoxisFacade::FindRazzashiCobras(*this->bot); + + if (razzashiCobras.empty()) + { + return false; + } + + Unit* const firstCobra = razzashiCobras.front(); + + this->Attack(firstCobra); + + return true; + } +}; diff --git a/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/RazzashiCobrasDPSPriority/HighPriestVenoxisPhase1RazzashiCobrasDPSPriorityTrigger.h b/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/RazzashiCobrasDPSPriority/HighPriestVenoxisPhase1RazzashiCobrasDPSPriorityTrigger.h new file mode 100644 index 00000000000..840c5b98edc --- /dev/null +++ b/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/RazzashiCobrasDPSPriority/HighPriestVenoxisPhase1RazzashiCobrasDPSPriorityTrigger.h @@ -0,0 +1,52 @@ +#pragma once + +#include "AiObjectContext.h" +#include "PlayerbotAI.h" +#include "Trigger.h" + +#include "../../facade/HighPriestVenoxisFacade.h" + +class HighPriestVenoxisPhase1RazzashiCobrasDPSPriorityTrigger : public Trigger +{ +public: + HighPriestVenoxisPhase1RazzashiCobrasDPSPriorityTrigger(PlayerbotAI* botAI) : Trigger(botAI, "high priest venoxis phase 1 razzashi cobras dps priority") {} + + bool IsActive() override + { + if (this->bot == nullptr) + { + return false; + } + + if (this->botAI == nullptr) + { + return false; + } + + if (HighPriestVenoxisFacade::IsInCombatWithVenoxis(*this->bot) == false) + { + return false; + } + + if (this->botAI->IsMainTank(this->bot) == true || this->botAI->IsHeal(this->bot) == true) + { + return false; + } + + const std::vector razzashiCobras = HighPriestVenoxisFacade::FindRazzashiCobras(*this->bot); + + if (razzashiCobras.empty()) + { + return false; + } + + const Unit* const target = this->bot->GetVictim(); + + if (target == razzashiCobras.front()) + { + return false; + } + + return true; + } +}; diff --git a/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/RazzashiCobrasPositioning/HighPriestVenoxisPhase1RazzashiCobrasPositioningAction.h b/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/RazzashiCobrasPositioning/HighPriestVenoxisPhase1RazzashiCobrasPositioningAction.h new file mode 100644 index 00000000000..7a9925fa30f --- /dev/null +++ b/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/RazzashiCobrasPositioning/HighPriestVenoxisPhase1RazzashiCobrasPositioningAction.h @@ -0,0 +1,103 @@ +#pragma once + +#include "Spell.h" +#include "AiObjectContext.h" +#include "AttackAction.h" +#include "PlayerbotAI.h" + +#include "../../facade/HighPriestVenoxisFacade.h" + +class HighPriestVenoxisPhase1RazzashiCobrasPositioningAction : public AttackAction +{ +public: + HighPriestVenoxisPhase1RazzashiCobrasPositioningAction( + PlayerbotAI* botAI, + const std::string name = "high priest venoxis phase 1 razzashi cobras positioning" + ) : AttackAction(botAI, name) {} + + bool Execute(Event) override + { + if (this->bot == nullptr) + { + return false; + } + + if (this->botAI == nullptr) + { + return false; + } + + const std::vector razzashiCobras = HighPriestVenoxisFacade::FindRazzashiCobras(*this->bot); + + for (Unit* const cobra : razzashiCobras) + { + if (cobra == nullptr) + { + return false; + } + + Unit* const target = cobra->GetVictim(); + + if (target == nullptr) + { + this->Attack(cobra); + + return false; + } + + Player* const playerTarget = dynamic_cast(target); + + if (playerTarget == nullptr) + { + this->Attack(cobra); + + LOG_ERROR("playerbots", "cobra target is not a player"); + + return false; + } + + const bool isProperlyTanked = this->botAI->IsTank(playerTarget) == true && this->botAI->IsMainTank(playerTarget) == false; + + if (isProperlyTanked == false) + { + this->Attack(cobra); + + LOG_ERROR("playerbots", "attacking cobra that is not tanked"); + + return false; + } + } + + const Position cobrasIdealPosition = HighPriestVenoxisFacade::GetRazzashiCobrasPosition(); + const float distanceToIdealPosition = this->bot->GetExactDist2d(cobrasIdealPosition.GetPositionX(), cobrasIdealPosition.GetPositionY()); + static constexpr float maxDistance = HighPriestVenoxisFacade::GetRazzashiCobrasMaxPositionDistance(); + + if (distanceToIdealPosition < maxDistance) + { + return false; + } + + const float botPositionX = this->bot->GetPositionX(); + const float botPositionY = this->bot->GetPositionY(); + const float dX = cobrasIdealPosition.GetPositionX() - botPositionX; + const float dY = cobrasIdealPosition.GetPositionY() - botPositionY; + const float moveX = botPositionX + (dX / distanceToIdealPosition) * maxDistance; + const float moveY = botPositionY + (dY / distanceToIdealPosition) * maxDistance; + + return MoveTo( + MAP_ZUL_GURUB, + moveX, + moveY, + cobrasIdealPosition.GetPositionZ(), + false, + false, + false, + false, + MovementPriority::MOVEMENT_COMBAT, + true, + true + ); + + return true; + } +}; diff --git a/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/RazzashiCobrasPositioning/HighPriestVenoxisPhase1RazzashiCobrasPositioningTrigger.h b/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/RazzashiCobrasPositioning/HighPriestVenoxisPhase1RazzashiCobrasPositioningTrigger.h new file mode 100644 index 00000000000..ee6269cc97d --- /dev/null +++ b/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/RazzashiCobrasPositioning/HighPriestVenoxisPhase1RazzashiCobrasPositioningTrigger.h @@ -0,0 +1,71 @@ +#pragma once + +#include "AiObjectContext.h" +#include "PlayerbotAI.h" +#include "Trigger.h" + +#include "../../facade/HighPriestVenoxisFacade.h" + +class HighPriestVenoxisPhase1RazzashiCobrasPositioningTrigger : public Trigger +{ +public: + HighPriestVenoxisPhase1RazzashiCobrasPositioningTrigger(PlayerbotAI* botAI) : Trigger(botAI, "high priest venoxis phase 1 razzashi cobras positioning") {} + + bool IsActive() override + { + if (this->bot == nullptr) + { + return false; + } + + if (this->botAI == nullptr) + { + return false; + } + + if (this->botAI->IsTank(this->bot) == false) + { + return false; + } + + if (this->botAI->IsMainTank(this->bot) == true) + { + return false; + } + + if (HighPriestVenoxisFacade::IsInCombatWithVenoxis(*this->bot) == false) + { + return false; + } + + const std::vector razzashiCobras = HighPriestVenoxisFacade::FindRazzashiCobras(*this->bot); + + if (razzashiCobras.empty()) + { + return false; + } + + Unit* const firstCobra = razzashiCobras.front(); + + if (firstCobra == nullptr) + { + return false; + } + + if (firstCobra->IsAlive() == false) + { + return false; + } + + const Position cobrasIdealPosition = HighPriestVenoxisFacade::GetRazzashiCobrasPosition(); + const float distanceToIdealPosition = this->bot->GetExactDist2d(cobrasIdealPosition.GetPositionX(), cobrasIdealPosition.GetPositionY()); + static constexpr float maxDistance = HighPriestVenoxisFacade::GetRazzashiCobrasMaxPositionDistance(); + + if (distanceToIdealPosition < maxDistance) + { + return false; + } + + return true; + } +}; diff --git a/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/VenoxisPositioning/HighPriestVenoxisPhase1VenoxisPositioningAction.h b/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/VenoxisPositioning/HighPriestVenoxisPhase1VenoxisPositioningAction.h new file mode 100644 index 00000000000..8d6f91c94f5 --- /dev/null +++ b/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/VenoxisPositioning/HighPriestVenoxisPhase1VenoxisPositioningAction.h @@ -0,0 +1,88 @@ +#pragma once + +#include "Spell.h" +#include "AiObjectContext.h" +#include "AttackAction.h" +#include "PlayerbotAI.h" + +#include "../../facade/HighPriestVenoxisFacade.h" + +class HighPriestVenoxisPhase1VenoxisPositioningAction : public AttackAction +{ +public: + HighPriestVenoxisPhase1VenoxisPositioningAction( + PlayerbotAI* botAI, + const std::string name = "high priest venoxis phase 1 venoxis positioning" + ) : AttackAction(botAI, name) {} + + bool Execute(Event) override + { + if (this->bot == nullptr) + { + return false; + } + + if (this->botAI == nullptr) + { + return false; + } + + if (this->botAI->IsMainTank(this->bot) == false) + { + return false; + } + + Unit* const boss = HighPriestVenoxisFacade::FindActiveBoss(*this->bot); + + if (boss == nullptr) + { + return false; + } + + if (this->bot->GetVictim() != boss) + { + this->Attack(boss); + + return false; + } + + if (boss->GetTarget() != this->bot->GetGUID()) + { + this->Attack(boss); + + return false; + } + + const Position bossIdealPosition = HighPriestVenoxisFacade::GetVenoxisPosition(); + const float distanceToIdealPosition = this->bot->GetExactDist2d(bossIdealPosition.GetPositionX(), bossIdealPosition.GetPositionY()); + static constexpr float maxDistance = HighPriestVenoxisFacade::GetVenoxisMaxPositionDistance(); + + if (distanceToIdealPosition < maxDistance) + { + return false; + } + + const float botPositionX = this->bot->GetPositionX(); + const float botPositionY = this->bot->GetPositionY(); + const float dX = bossIdealPosition.GetPositionX() - botPositionX; + const float dY = bossIdealPosition.GetPositionY() - botPositionY; + const float moveX = botPositionX + (dX / distanceToIdealPosition) * maxDistance; + const float moveY = botPositionY + (dY / distanceToIdealPosition) * maxDistance; + + return MoveTo( + MAP_ZUL_GURUB, + moveX, + moveY, + bossIdealPosition.GetPositionZ(), + false, + false, + false, + false, + MovementPriority::MOVEMENT_COMBAT, + true, + true + ); + + return true; + } +}; diff --git a/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/VenoxisPositioning/HighPriestVenoxisPhase1VenoxisPositioningTrigger.h b/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/VenoxisPositioning/HighPriestVenoxisPhase1VenoxisPositioningTrigger.h new file mode 100644 index 00000000000..975cc3a5ce7 --- /dev/null +++ b/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/VenoxisPositioning/HighPriestVenoxisPhase1VenoxisPositioningTrigger.h @@ -0,0 +1,59 @@ +#pragma once + +#include "AiObjectContext.h" +#include "PlayerbotAI.h" +#include "Trigger.h" + +#include "../../facade/HighPriestVenoxisFacade.h" + +class HighPriestVenoxisPhase1VenoxisPositioningTrigger : public Trigger +{ +public: + HighPriestVenoxisPhase1VenoxisPositioningTrigger(PlayerbotAI* botAI) : Trigger(botAI, "high priest venoxis phase 1 venoxis positioning") {} + + bool IsActive() override + { + if (this->bot == nullptr) + { + return false; + } + + if (this->botAI == nullptr) + { + return false; + } + + if (this->botAI->IsMainTank(this->bot) == false) + { + return false; + } + + if (HighPriestVenoxisFacade::IsInCombatWithVenoxis(*this->bot) == false) + { + return false; + } + + if (HighPriestVenoxisFacade::IsInPhase1(*this->bot) == false) + { + return false; + } + + const Unit* const boss = HighPriestVenoxisFacade::FindActiveBoss(*this->bot); + + if (boss == nullptr) + { + return false; + } + + const Position bossIdealPosition = HighPriestVenoxisFacade::GetVenoxisPosition(); + const float distanceToIdealPosition = this->bot->GetExactDist2d(bossIdealPosition.GetPositionX(), bossIdealPosition.GetPositionY()); + static constexpr float maxDistance = HighPriestVenoxisFacade::GetVenoxisMaxPositionDistance(); + + if (distanceToIdealPosition < maxDistance) + { + return false; + } + + return true; + } +}; diff --git a/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/definition/enum/HighPriestVenoxisEnum.h b/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/definition/enum/HighPriestVenoxisEnum.h new file mode 100644 index 00000000000..e8ec245129a --- /dev/null +++ b/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/definition/enum/HighPriestVenoxisEnum.h @@ -0,0 +1,10 @@ +#pragma once + +enum class HighPriestVenoxisEnum +{ + ENTRY = 14507u, + SPELL_HOLY_WRATH = 23979u, + SPELL_RENEW = 23895u, + PHASE_1_SAFE_DISTANCE = 35u, + PHASE_2_THRESHOLD = 50u +}; diff --git a/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/definition/enum/RazzashiCobraEnum.h b/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/definition/enum/RazzashiCobraEnum.h new file mode 100644 index 00000000000..11899bababa --- /dev/null +++ b/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/definition/enum/RazzashiCobraEnum.h @@ -0,0 +1,6 @@ +#pragma once + +enum class RazzashiCobraEnum +{ + ENTRY = 11373u, +}; diff --git a/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/facade/HighPriestVenoxisFacade.h b/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/facade/HighPriestVenoxisFacade.h new file mode 100644 index 00000000000..617f693668c --- /dev/null +++ b/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/facade/HighPriestVenoxisFacade.h @@ -0,0 +1,189 @@ +#pragma once + +#include "Unit.h" +#include "Player.h" +#include "Spell.h" +#include "Position.h" + +#include "GridNotifiers.h" +// Necessary due to a poor implementation by AC. +#include "GridNotifiersImpl.h" + +#include "Cell.h" +#include "CellImpl.h" + + +#include "PlayerbotAIConfig.h" + +#include "../definition/enum/HighPriestVenoxisEnum.h" +#include "../definition/enum/RazzashiCobraEnum.h" +#include "../../../facade/ZulGurubFacade.h" + + +class HighPriestVenoxisFacade +{ +public: + [[nodiscard]] static Position GetVenoxisPosition() noexcept + { + return Position{ + -12026.866f, + -1665.5562f, + 33.59f, + }; + } + + [[nodiscard]] static constexpr float GetVenoxisMaxPositionDistance() noexcept + { + return 4.0f; + } + + [[nodiscard]] static bool IsInCombatWithVenoxis(Player& bot) noexcept + { + const Unit* const venoxis = HighPriestVenoxisFacade::FindActiveBoss(bot); + + if (venoxis == nullptr) + { + return false; + } + + // This method seems to not be working properly. + // return bot.IsInCombatWith(venoxis); + return bot.IsInCombat() && venoxis->IsInCombat(); + } + + [[nodiscard]] static Position GetRangedPosition() noexcept + { + return Position{ + -11985.107f, + -1672.6897f, + 32.31f, + }; + } + + [[nodiscard]] static constexpr float GetRangedMaxPositionDistance() noexcept + { + return 4.0f; + } + + [[nodiscard]] static Position GetRazzashiCobrasPosition() noexcept + { + return Position{ + -11991.636f, + -1698.4347f, + 32.284f, + }; + } + + [[nodiscard]] static constexpr float GetRazzashiCobrasMaxPositionDistance() noexcept + { + return 4.0f; + } + + [[nodiscard]] static Unit* FindActiveBoss(Player& bot) noexcept + { + if (ZulGurubFacade::IsInInstance(bot) == false) + { + return nullptr; + } + + if (!bot.IsInCombat()) + { + return nullptr; + } + + return bot.FindNearestCreature(uint32_t(HighPriestVenoxisEnum::ENTRY), PlayerbotAIConfig::instance().sightDistance, true); + }; + + [[nodiscard]] static std::vector FindRazzashiCobras(Player& bot) noexcept + { + if (ZulGurubFacade::IsInInstance(bot) == false) + { + return {}; + } + + if (!bot.IsInCombat()) + { + return {}; + } + + std::list nearbyUnits{}; + + Acore::AnyUnitInObjectRangeCheck unitChecker{&bot, PlayerbotAIConfig::instance().sightDistance}; + Acore::UnitListSearcher searcher{&bot, nearbyUnits, unitChecker}; + Cell::VisitObjects(&bot, searcher, PlayerbotAIConfig::instance().sightDistance); + + std::vector results{}; + + for (Unit* const unit : nearbyUnits) + { + if (unit->GetEntry() != uint32_t(RazzashiCobraEnum::ENTRY)) + { + continue; + } + + if (unit->IsAlive() == false) + { + continue; + } + + results.emplace_back(unit); + } + + std::sort( + results.begin(), + results.end(), + [](Unit* cobraA, Unit* cobraB) + { + return cobraA->GetGUID().GetHigh() < cobraB->GetGUID().GetHigh(); + } + ); + + return results; + }; + + [[nodiscard]] static bool IsAtSafeDistanceFromVenoxis(Player& bot) noexcept + { + const Unit* const venoxis = FindActiveBoss(bot); + + if (venoxis == nullptr) + { + return true; + } + + const float distanceToVenoxis = bot.GetExactDist2d(venoxis->GetPositionX(), venoxis->GetPositionY()); + + return distanceToVenoxis > float(HighPriestVenoxisEnum::PHASE_1_SAFE_DISTANCE); + }; + + [[nodiscard]] static bool IsInPhase1(Player& bot) noexcept + { + const Unit* const unit = HighPriestVenoxisFacade::FindActiveBoss(bot); + + if (unit == nullptr) + { + return false; + } + + if (unit->GetMapId() != MAP_ZUL_GURUB) + { + return false; + } + + if (!unit->IsInCombat()) + { + return false; + } + + return unit->GetHealthPct() > float(HighPriestVenoxisEnum::PHASE_2_THRESHOLD); + }; + +private: + HighPriestVenoxisFacade() = delete; + ~HighPriestVenoxisFacade() = delete; + + HighPriestVenoxisFacade(const HighPriestVenoxisFacade&) = delete; + HighPriestVenoxisFacade& operator=(const HighPriestVenoxisFacade&) = delete; + + HighPriestVenoxisFacade(HighPriestVenoxisFacade&&) = delete; + HighPriestVenoxisFacade& operator=(HighPriestVenoxisFacade&&) = delete; +}; diff --git a/src/Ai/Raid/ZulGurub/RaidZGStrategy.h b/src/Ai/Raid/ZulGurub/RaidZGStrategy.h index 61d05166f17..0c359c86ed1 100644 --- a/src/Ai/Raid/ZulGurub/RaidZGStrategy.h +++ b/src/Ai/Raid/ZulGurub/RaidZGStrategy.h @@ -1,5 +1,10 @@ #pragma once +#include "Boss/HighPriestVenoxis/Phase1/HolyWrath/HighPriestVenoxisPhase1HolyWrathAction.h" +#include "Boss/HighPriestVenoxis/Phase1/HolyWrath/HighPriestVenoxisPhase1HolyWrathMultiplier.h" +#include "Boss/HighPriestVenoxis/Phase1/RazzashiCobrasDPSPriority/HighPriestVenoxisPhase1RazzashiCobrasDPSPriorityAction.h" +#include "Boss/HighPriestVenoxis/Phase1/RazzashiCobrasPositioning/HighPriestVenoxisPhase1RazzashiCobrasPositioningAction.h" +#include "Boss/HighPriestVenoxis/Phase1/VenoxisPositioning/HighPriestVenoxisPhase1VenoxisPositioningAction.h" #include "CreateNextAction.h" #include "Strategy.h" #include "Multiplier.h" @@ -18,6 +23,8 @@ class RaidZGStrategy : public Strategy void InitTriggers(std::vector& triggers) override { + // Trash mobs + triggers.push_back( new TriggerNode( "gurubashi bat rider unstable concoction", @@ -26,10 +33,46 @@ class RaidZGStrategy : public Strategy } ) ); + + // High Priest Venoxis + + triggers.push_back( + new TriggerNode( + "high priest venoxis phase 1 venoxis positioning", + { + CreateNextAction(ACTION_EMERGENCY + 9.0f) + } + ) + ); + triggers.push_back( + new TriggerNode( + "high priest venoxis phase 1 razzashi cobras positioning", + { + CreateNextAction(ACTION_EMERGENCY + 9.0f) + } + ) + ); + triggers.push_back( + new TriggerNode( + "high priest venoxis phase 1 razzashi cobras dps priority", + { + CreateNextAction(ACTION_HIGH + 1.0f) + } + ) + ); + triggers.push_back( + new TriggerNode( + "high priest venoxis phase 1 holy wrath", + { + CreateNextAction(ACTION_EMERGENCY + 10.0f) + } + ) + ); } void InitMultipliers(std::vector& multipliers) override { - multipliers.push_back(new GurubashiBatRiderUnstableConcoctionMultiplier(botAI)); + multipliers.push_back(new GurubashiBatRiderUnstableConcoctionMultiplier(this->botAI)); + multipliers.push_back(new HighPriestVenoxisPhase1HolyWrathMultiplier(this->botAI)); } }; diff --git a/src/Ai/Raid/ZulGurub/RaidZGTriggerContext.h b/src/Ai/Raid/ZulGurub/RaidZGTriggerContext.h index 77067267c50..91062faf4ec 100644 --- a/src/Ai/Raid/ZulGurub/RaidZGTriggerContext.h +++ b/src/Ai/Raid/ZulGurub/RaidZGTriggerContext.h @@ -1,7 +1,11 @@ #pragma once #include "AiObjectContext.h" +#include "HighPriestVenoxisPhase1HolyWrathTrigger.h" #include "Trash/GurubashiBatRider/GurubashiBatRiderUnstableConcoctionTrigger.h" +#include "Boss/HighPriestVenoxis/Phase1/VenoxisPositioning/HighPriestVenoxisPhase1VenoxisPositioningTrigger.h" +#include "Boss/HighPriestVenoxis/Phase1/RazzashiCobrasPositioning/HighPriestVenoxisPhase1RazzashiCobrasPositioningTrigger.h" +#include "Boss/HighPriestVenoxis/Phase1/RazzashiCobrasDPSPriority/HighPriestVenoxisPhase1RazzashiCobrasDPSPriorityTrigger.h" class RaidZGTriggerContext : public NamedObjectContext { @@ -10,6 +14,10 @@ class RaidZGTriggerContext : public NamedObjectContext { // Trash creators["gurubashi bat rider unstable concoction"] = &RaidZGTriggerContext::gurubashiBatRiderUnstableConcoction; + creators["high priest venoxis phase 1 holy wrath"] = &RaidZGTriggerContext::highPriestVenoxisPhase1HolyWrath; + creators["high priest venoxis phase 1 venoxis positioning"] = &RaidZGTriggerContext::highPriestVenoxisPhase1VenoxisPositioning; + creators["high priest venoxis phase 1 razzashi cobras positioning"] = &RaidZGTriggerContext::highPriestVenoxisPhase1RazzashiCobrasPositioning; + creators["high priest venoxis phase 1 razzashi cobras dps priority"] = &RaidZGTriggerContext::highPriestVenoxisPhase1RazzashiCobrasDPSPriority; } private: @@ -18,4 +26,25 @@ class RaidZGTriggerContext : public NamedObjectContext { return new GurubashiBatRiderUnstableConcoctionTrigger(botAI); } + + // High Priest Venoxis + static Trigger* highPriestVenoxisPhase1HolyWrath(PlayerbotAI* botAI) + { + return new HighPriestVenoxisPhase1HolyWrathTrigger(botAI); + } + + static Trigger* highPriestVenoxisPhase1VenoxisPositioning(PlayerbotAI* botAI) + { + return new HighPriestVenoxisPhase1VenoxisPositioningTrigger(botAI); + } + + static Trigger* highPriestVenoxisPhase1RazzashiCobrasPositioning(PlayerbotAI* botAI) + { + return new HighPriestVenoxisPhase1RazzashiCobrasPositioningTrigger(botAI); + } + + static Trigger* highPriestVenoxisPhase1RazzashiCobrasDPSPriority(PlayerbotAI* botAI) + { + return new HighPriestVenoxisPhase1RazzashiCobrasDPSPriorityTrigger(botAI); + } }; diff --git a/src/Ai/Raid/ZulGurub/facade/ZulGurubFacade.h b/src/Ai/Raid/ZulGurub/facade/ZulGurubFacade.h new file mode 100644 index 00000000000..d5858656703 --- /dev/null +++ b/src/Ai/Raid/ZulGurub/facade/ZulGurubFacade.h @@ -0,0 +1,23 @@ +#pragma once + +#include "Player.h" + +class ZulGurubFacade +{ +public: + + [[nodiscard]] static bool IsInInstance(Player& player) noexcept + { + return player.GetMapId() == MAP_ZUL_GURUB; + } + +private: + ZulGurubFacade() = delete; + ~ZulGurubFacade() = delete; + + ZulGurubFacade(ZulGurubFacade const&) = delete; + ZulGurubFacade& operator=(ZulGurubFacade const&) = delete; + + ZulGurubFacade(ZulGurubFacade&&) = delete; + ZulGurubFacade& operator=(ZulGurubFacade&&) = delete; +}; From 861c4e78841e35f184894d6770f4d050bee98f74 Mon Sep 17 00:00:00 2001 From: SmashingQuasar Date: Tue, 24 Feb 2026 16:18:05 +0100 Subject: [PATCH 3/8] feat: Added HakkarTheSoulflayer strategy and reworked the raid system. --- src/Ai/Class/Paladin/Action/PaladinActions.h | 6 + .../HakkarTheSoulflayerAssistant.h | 231 +++++++++++++++++ .../memory/HakkarTheSoulflayerMemory.h | 24 ++ .../son-of-hakkar/SonOfHakkarAssistant.h | 192 ++++++++++++++ .../son-of-hakkar/memory/SonOfHakkarMemory.h | 68 +++++ .../definition/enum/HakkarTheSoulflayerEnum.h | 11 + .../definition/enum/PoisonousCloudEnum.h | 8 + .../definition/enum/SonOfHakkarEnum.h | 9 + ...karTheSoulflayerGoToPoisonousCloudAction.h | 59 +++++ ...heSoulflayerGoToPoisonousCloudMultiplier.h | 63 +++++ ...arTheSoulflayerGoToPoisonousCloudTrigger.h | 60 +++++ ...karTheSoulflayerPoisonousBloodMultiplier.h | 242 ++++++++++++++++++ .../HakkarTheSoulflayerCauseInsanityAction.h | 75 ++++++ .../HakkarTheSoulflayerCauseInsanityTrigger.h | 59 +++++ ...kkarTheSoulflayerHakkarPositioningAction.h | 92 +++++++ ...karTheSoulflayerHakkarPositioningTrigger.h | 59 +++++ ...rTheSoulflayerBringBackSonOfHakkarAction.h | 83 ++++++ ...TheSoulflayerBringBackSonOfHakkarTrigger.h | 59 +++++ ...kkarTheSoulflayerMoveToSonOfHakkarAction.h | 56 ++++ ...karTheSoulflayerMoveToSonOfHakkarTrigger.h | 86 +++++++ ...HakkarTheSoulflayerPullSonOfHakkarAction.h | 101 ++++++++ ...akkarTheSoulflayerPullSonOfHakkarTrigger.h | 74 ++++++ ...HakkarTheSoulflayerExcessiveThreatAction.h | 68 +++++ ...akkarTheSoulflayerExcessiveThreatTrigger.h | 59 +++++ .../script/HakkarTheSoulflayerPlayerScript.h | 41 +++ .../script/HakkarTheSoulflayerScript.h | 119 +++++++++ .../HakkarTheSoulflayerUnitScriptPlayer.h | 41 +++ .../HighPriestVenoxisPhase1HolyWrathAction.h | 8 +- ...ghPriestVenoxisPhase1HolyWrathMultiplier.h | 31 +-- .../HighPriestVenoxisPhase1HolyWrathTrigger.h | 18 +- ...xisPhase1RazzashiCobrasDPSPriorityAction.h | 9 +- ...isPhase1RazzashiCobrasDPSPriorityTrigger.h | 11 +- ...xisPhase1RazzashiCobrasPositioningAction.h | 18 +- ...isPhase1RazzashiCobrasPositioningTrigger.h | 15 +- ...estVenoxisPhase1VenoxisPositioningAction.h | 16 +- ...stVenoxisPhase1VenoxisPositioningTrigger.h | 18 +- .../HighPriestVenoxisAssistant.h} | 74 ++---- src/Ai/Raid/ZulGurub/RaidZGStrategy.h | 75 ++++++ src/Ai/Raid/ZulGurub/RaidZGTriggerContext.h | 56 +++- src/Ai/Raid/ZulGurub/facade/ZulGurubFacade.h | 23 -- .../Raid/ZulGurub/leader/ZulGurubRaidLeader.h | 49 ++++ .../ZulGurub/script/ZulGurubInstanceScript.h | 36 +++ src/Script/Playerbots.cpp | 8 + .../tank-assistant/RaidTankAssistant.h | 105 ++++++++ .../memory/RaidTankAssistantMemory.h | 69 +++++ src/domain/core/raid/leader/BaseRaidLeader.h | 45 ++++ .../core/raid/leader/RaidLeaderRegistry.h | 147 +++++++++++ .../struct/RaidLeaderContextStruct.h | 8 + .../core/raid/leader/memory/RaidMemory.h | 6 + src/domain/core/utility/StopWatch.h | 37 +++ 50 files changed, 2797 insertions(+), 130 deletions(-) create mode 100644 src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/assistant/hakkar-the-soulflayer/HakkarTheSoulflayerAssistant.h create mode 100644 src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/assistant/hakkar-the-soulflayer/memory/HakkarTheSoulflayerMemory.h create mode 100644 src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/assistant/son-of-hakkar/SonOfHakkarAssistant.h create mode 100644 src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/assistant/son-of-hakkar/memory/SonOfHakkarMemory.h create mode 100644 src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/definition/enum/HakkarTheSoulflayerEnum.h create mode 100644 src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/definition/enum/PoisonousCloudEnum.h create mode 100644 src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/definition/enum/SonOfHakkarEnum.h create mode 100644 src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/blood-siphon/GoToPoisonousCloud/HakkarTheSoulflayerGoToPoisonousCloudAction.h create mode 100644 src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/blood-siphon/GoToPoisonousCloud/HakkarTheSoulflayerGoToPoisonousCloudMultiplier.h create mode 100644 src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/blood-siphon/GoToPoisonousCloud/HakkarTheSoulflayerGoToPoisonousCloudTrigger.h create mode 100644 src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/blood-siphon/PoisonousBlood/HakkarTheSoulflayerPoisonousBloodMultiplier.h create mode 100644 src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/cause-insanity/CauseInsanity/HakkarTheSoulflayerCauseInsanityAction.h create mode 100644 src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/cause-insanity/CauseInsanity/HakkarTheSoulflayerCauseInsanityTrigger.h create mode 100644 src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/positioning/HakkarPositioning/HakkarTheSoulflayerHakkarPositioningAction.h create mode 100644 src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/positioning/HakkarPositioning/HakkarTheSoulflayerHakkarPositioningTrigger.h create mode 100644 src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/son-of-hakkar/BringBackSonOfHakkar/HakkarTheSoulflayerBringBackSonOfHakkarAction.h create mode 100644 src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/son-of-hakkar/BringBackSonOfHakkar/HakkarTheSoulflayerBringBackSonOfHakkarTrigger.h create mode 100644 src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/son-of-hakkar/MoveToSonOfHakkar/HakkarTheSoulflayerMoveToSonOfHakkarAction.h create mode 100644 src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/son-of-hakkar/MoveToSonOfHakkar/HakkarTheSoulflayerMoveToSonOfHakkarTrigger.h create mode 100644 src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/son-of-hakkar/PullSonOfHakkar/HakkarTheSoulflayerPullSonOfHakkarAction.h create mode 100644 src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/son-of-hakkar/PullSonOfHakkar/HakkarTheSoulflayerPullSonOfHakkarTrigger.h create mode 100644 src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/threat/ExcessiveThreat/HakkarTheSoulflayerExcessiveThreatAction.h create mode 100644 src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/threat/ExcessiveThreat/HakkarTheSoulflayerExcessiveThreatTrigger.h create mode 100644 src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/script/HakkarTheSoulflayerPlayerScript.h create mode 100644 src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/script/HakkarTheSoulflayerScript.h create mode 100644 src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/script/HakkarTheSoulflayerUnitScriptPlayer.h rename src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/{facade/HighPriestVenoxisFacade.h => assistant/HighPriestVenoxisAssistant.h} (57%) delete mode 100644 src/Ai/Raid/ZulGurub/facade/ZulGurubFacade.h create mode 100644 src/Ai/Raid/ZulGurub/leader/ZulGurubRaidLeader.h create mode 100644 src/Ai/Raid/ZulGurub/script/ZulGurubInstanceScript.h create mode 100644 src/domain/core/raid/assistant/tank-assistant/RaidTankAssistant.h create mode 100644 src/domain/core/raid/assistant/tank-assistant/memory/RaidTankAssistantMemory.h create mode 100644 src/domain/core/raid/leader/BaseRaidLeader.h create mode 100644 src/domain/core/raid/leader/RaidLeaderRegistry.h create mode 100644 src/domain/core/raid/leader/definition/struct/RaidLeaderContextStruct.h create mode 100644 src/domain/core/raid/leader/memory/RaidMemory.h create mode 100644 src/domain/core/utility/StopWatch.h diff --git a/src/Ai/Class/Paladin/Action/PaladinActions.h b/src/Ai/Class/Paladin/Action/PaladinActions.h index c58c3209d65..5d3f5954562 100644 --- a/src/Ai/Class/Paladin/Action/PaladinActions.h +++ b/src/Ai/Class/Paladin/Action/PaladinActions.h @@ -277,6 +277,12 @@ class CastHandOfReckoningAction : public CastSpellAction CastHandOfReckoningAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "hand of reckoning") {} }; +class CastHandOfSalvationAction : public CastSpellAction +{ +public: + CastHandOfSalvationAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "hand of salvation") {} +}; + class CastRighteousDefenseAction : public CastSpellAction { public: diff --git a/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/assistant/hakkar-the-soulflayer/HakkarTheSoulflayerAssistant.h b/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/assistant/hakkar-the-soulflayer/HakkarTheSoulflayerAssistant.h new file mode 100644 index 00000000000..55cd1156c20 --- /dev/null +++ b/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/assistant/hakkar-the-soulflayer/HakkarTheSoulflayerAssistant.h @@ -0,0 +1,231 @@ +#pragma once + +#include "PlayerbotAI.h" +#include "Unit.h" +#include "Player.h" +#include "Spell.h" +#include "Position.h" + +#include "GridNotifiers.h" +// Necessary due to a poor implementation by AC. +#include "GridNotifiersImpl.h" + +#include "PlayerbotMgr.h" +#include "PlayerbotAIConfig.h" + +#include "../definition/enum/HakkarTheSoulflayerEnum.h" +#include "../../../../../../domain/core/raid/assistant/tank-assistant/memory/RaidTankAssistantMemory.h" +#include "raid/assistant/tank-assistant/RaidTankAssistant.h" +#include "Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/assistant/hakkar-the-soulflayer/memory/HakkarTheSoulflayerMemory.h" +#include "domain/core/raid/leader/definition/struct/RaidLeaderContextStruct.h" + +class HakkarTheSoulflayerAssistant +{ +private: + HakkarTheSoulflayerMemory memory{}; + +public: + HakkarTheSoulflayerAssistant() = default; + ~HakkarTheSoulflayerAssistant() = default; + + void restartBloodSiphonWatch() noexcept + { + this->memory.getBloodSiphonWatch().reset(); + this->memory.getBloodSiphonWatch().start(); + } + + [[nodiscard]] constexpr float getHakkarMaxPositionDistance() const noexcept + { + return 4.0f; + } + + [[nodiscard]] Position getHakkarPosition() const noexcept + { + return Position{ + -11787.951f, + -1655.4058f, + 53.128f, + }; + } + + [[nodiscard]] bool hasExcessiveThreat(Player& bot, RaidLeaderContextStruct context) const noexcept + { + if (this->isInCombatWithHakkar(bot) == false) + { + return false; + } + + if (PlayerbotAI::IsTank(&bot) == true) + { + return false; + } + + Unit* const hakkar = this->findActiveBoss(bot); + + if (hakkar == nullptr) + { + return false; + } + + const RaidTankAssistantMemory& raidTankMemory = context.raidTankAssistant.getMemory(); + + Player* const mainTank = raidTankMemory.getMainTank(); + + if (mainTank == nullptr) + { + return false; + } + + ThreatMgr& threatMgr = hakkar->GetThreatMgr(); + + const float botThreat = threatMgr.GetThreat(&bot); + const float mainTankThreat = threatMgr.GetThreat(mainTank); + + if (mainTankThreat <= 0.0f) + { + return false; + } + + Player* const secondTank = raidTankMemory.getSecondTank(); + + if (secondTank == nullptr) + { + return false; + } + + const float secondTankThreat = threatMgr.GetThreat(secondTank); + + if (secondTankThreat <= 0.0f) + { + return false; + } + + const float secondThreatPct = (botThreat / secondTankThreat) * 100.0f; + + return secondThreatPct >= float(HakkarTheSoulflayerEnum::NON_TANK_THREAT_THRESHOLD); + } + + [[nodiscard]] Player* findSecondInThreat(Player& bot) const noexcept + { + const Unit* const hakkar = this->findActiveBoss(bot); + + if (hakkar == nullptr) + { + return nullptr; + } + + if (hakkar->IsAlive() == false) + { + return nullptr; + } + + if (hakkar->IsInCombat() == false) + { + return nullptr; + } + + const ThreatMgr& threatMgr = hakkar->GetThreatMgr(); + + if (threatMgr.GetThreatListSize() < 2) + { + return nullptr; + } + + const Acore::IteratorPair::const_iterator> threatList = hakkar->GetThreatMgr().GetSortedThreatList(); + + uint8_t index = 0; + + for (const ThreatReference* const threatRef : threatList) + { + if (threatRef == nullptr) + { + continue; + } + + if (index == 2) + { + Unit* const threatUnit = threatRef->GetVictim(); + + if (threatUnit == nullptr) + { + return nullptr; + } + + Player* const playerThreatUnit = dynamic_cast(threatUnit); + + return playerThreatUnit; + } + + ++index; + } + + return nullptr; + } + + [[nodiscard]] Player* findInsaneAlly(Player& bot) const noexcept + { + if (this->isInCombatWithHakkar(bot) == false) + { + return nullptr; + } + + const Group* const group = bot.GetGroup(); + + if (group == nullptr) + { + return nullptr; + } + + const GroupReference* member = group->GetFirstMember(); + + while (member != nullptr) + { + Player* const player = member->GetSource(); + + if (player == nullptr) + { + member = member->next(); + + continue; + } + + const bool affectedByInsanity = player->HasAura(uint32_t(HakkarTheSoulflayerEnum::SPELL_CAUSE_INSANITY)); + + if (affectedByInsanity) + { + return player; + } + + member = member->next(); + } + + return nullptr; + } + + [[nodiscard]] bool isInCombatWithHakkar(Player& bot) const noexcept + { + const Unit* const hakkar = this->findActiveBoss(bot); + + if (hakkar == nullptr) + { + return false; + } + + return bot.IsInCombat() && hakkar->IsInCombat(); + } + + [[nodiscard]] Unit* findActiveBoss(Player& bot) const noexcept + { + if (!bot.IsInCombat()) + { + return nullptr; + } + + return bot.FindNearestCreature(uint32_t(HakkarTheSoulflayerEnum::ENTRY), PlayerbotAIConfig::instance().sightDistance, true); + }; + + [[nodiscard]] const HakkarTheSoulflayerMemory& getMemory() const noexcept + { + return this->memory; + } +}; diff --git a/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/assistant/hakkar-the-soulflayer/memory/HakkarTheSoulflayerMemory.h b/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/assistant/hakkar-the-soulflayer/memory/HakkarTheSoulflayerMemory.h new file mode 100644 index 00000000000..57b51608fc0 --- /dev/null +++ b/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/assistant/hakkar-the-soulflayer/memory/HakkarTheSoulflayerMemory.h @@ -0,0 +1,24 @@ +#pragma once + +#include +#include "utility/StopWatch.h" + +class HakkarTheSoulflayerMemory final +{ +private: + Stopwatch bloodSiphonWatch{}; + +public: + HakkarTheSoulflayerMemory() = default; + ~HakkarTheSoulflayerMemory() = default; + + [[nodiscard]] Stopwatch& getBloodSiphonWatch() noexcept + { + return this->bloodSiphonWatch; + } + + [[nodiscard]] const Stopwatch& getBloodSiphonWatch() const noexcept + { + return this->bloodSiphonWatch; + } +}; \ No newline at end of file diff --git a/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/assistant/son-of-hakkar/SonOfHakkarAssistant.h b/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/assistant/son-of-hakkar/SonOfHakkarAssistant.h new file mode 100644 index 00000000000..58c10d42d0d --- /dev/null +++ b/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/assistant/son-of-hakkar/SonOfHakkarAssistant.h @@ -0,0 +1,192 @@ +#pragma once + +#include "HakkarTheSoulflayer/definition/enum/PoisonousCloudEnum.h" +#include "HakkarTheSoulflayer/definition/enum/SonOfHakkarEnum.h" +#include "PlayerbotAI.h" +#include "RaidBossHelpers.h" +#include "Unit.h" +#include "Player.h" +#include "Spell.h" + +#include "GridNotifiers.h" +// Necessary due to a poor implementation by AC. +#include "GridNotifiersImpl.h" + +#include "Cell.h" +#include "CellImpl.h" + +#include "PlayerbotMgr.h" +#include "PlayerbotAIConfig.h" +#include "assistant/son-of-hakkar/memory/SonOfHakkarMemory.h" + +class SonOfHakkarAssistant +{ +private: + SonOfHakkarMemory memory{}; + +public: + SonOfHakkarAssistant() = default; + ~SonOfHakkarAssistant() = default; + + void setSonOfHakkarKilledForCurrentBloodSiphon(bool value) noexcept + { + this->memory.setSonOfHakkarKilledForCurrentBloodSiphon(value); + } + + [[nodiscard]] Unit* findPoisonousCloud(Player& bot) const noexcept + { + return bot.FindNearestCreature(uint32_t(PoisonousCloudEnum::ENTRY), PlayerbotAIConfig::instance().sightDistance, true); + } + + void setActiveSonOfHakkar(Creature* sonOfHakkar) noexcept + { + this->memory.setActiveSonOfHakkar(sonOfHakkar); + } + + void initialiseDesignatedPuller(Player& player) noexcept + { + const Group* const group = player.GetGroup(); + + if (group == nullptr) + { + return; + } + + const GroupReference* member = group->GetFirstMember(); + + while (member != nullptr) + { + Player* const player = member->GetSource(); + + if (player == nullptr) + { + member = member->next(); + + continue; + } + + if (player->IsAlive() == false) + { + member = member->next(); + + continue; + } + + if (PlayerbotAI::IsRanged(player) == false || PlayerbotAI::IsDps(player) == false) + { + member = member->next(); + + continue; + } + + MarkTargetWithTriangle(player, player); + this->memory.setSonOfHakkarDesignatedPuller(player); + + PlayerbotAI* const botAI = PlayerbotsMgr::instance().GetPlayerbotAI(player); + + botAI->SayToRaid("I will attract the Son of Hakkar when the time comes!"); + + break; + } + } + + void initialiseSonsOfHakkar(Player& player) noexcept + { + this->memory.setSonOfHakkarKilledForCurrentBloodSiphon(false); + + Creature* const leftSonOfHakkar = this->findSonOfHakkar(player, uint32_t(SonOfHakkarEnum::LEFT_SON_OF_HAKKAR_GUID)); + + this->memory.setLeftSonOfHakkar(leftSonOfHakkar); + + MarkTargetWithCross(&player, leftSonOfHakkar); + + Creature* const rightSonOfHakkar = this->findSonOfHakkar(player, uint32_t(SonOfHakkarEnum::RIGHT_SON_OF_HAKKAR_GUID)); + + this->memory.setRightSonOfHakkar(rightSonOfHakkar); + + MarkTargetWithStar(&player, rightSonOfHakkar); + } + + [[nodiscard]] Creature* findSonOfHakkar(Player& player, uint32_t dbGuid) const + { + Map* map = player.GetMap(); + + if (map == nullptr) + { + return nullptr; + } + + const std::unordered_multimap store = map->GetCreatureBySpawnIdStore(); + const std::unordered_multimap::const_iterator it = store.find(dbGuid); + const std::unordered_multimap::const_iterator end = store.end(); + + if (it == end) + { + return nullptr; + } + + Creature* sonOfHakkar = it->second; + + if (sonOfHakkar == nullptr) + { + return nullptr; + } + + if (!sonOfHakkar->IsAlive()) + { + return nullptr; + } + + if (sonOfHakkar->GetEntry() != uint32_t(SonOfHakkarEnum::ENTRY)) + { + return nullptr; + } + + return sonOfHakkar; + } + + [[nodiscard]] Creature* findAnyAliveSonOfHakkar() const noexcept + { + Creature* const leftSonOfHakkar = this->memory.getLeftSonOfHakkar(); + + if (leftSonOfHakkar != nullptr && leftSonOfHakkar->IsAlive()) + { + return leftSonOfHakkar; + } + + Creature* const rightSonOfHakkar = this->memory.getRightSonOfHakkar(); + + if (rightSonOfHakkar != nullptr && rightSonOfHakkar->IsAlive()) + { + return rightSonOfHakkar; + } + + return nullptr; + } + + [[nodiscard]] float getDistanceToActiveSonOfHakkar(Player& bot) const noexcept + { + const Unit* const sonOfHakkar = this->memory.getActiveSonOfHakkar(); + + if (sonOfHakkar == nullptr) + { + return 0.0f; + } + + return bot.GetExactDist( + sonOfHakkar->GetPositionX(), + sonOfHakkar->GetPositionY(), + sonOfHakkar->GetPositionZ() + ); + } + + [[nodiscard]] const SonOfHakkarMemory& getMemory() const noexcept + { + return this->memory; + } + + [[nodiscard]] SonOfHakkarMemory& getMemory() noexcept + { + return this->memory; + } +}; diff --git a/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/assistant/son-of-hakkar/memory/SonOfHakkarMemory.h b/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/assistant/son-of-hakkar/memory/SonOfHakkarMemory.h new file mode 100644 index 00000000000..f124c19accd --- /dev/null +++ b/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/assistant/son-of-hakkar/memory/SonOfHakkarMemory.h @@ -0,0 +1,68 @@ +#pragma once + +#include +#include "Unit.h" + +class SonOfHakkarMemory final +{ +private: + Unit* sonOfHakkarDesignatedPuller = nullptr; + bool sonOfHakkarKilledForCurrentBloodSiphon = false; + Creature* leftSonOfHakkar = nullptr; + Creature* rightSonOfHakkar = nullptr; + Creature* activeSonOfHakkar = nullptr; + +public: + SonOfHakkarMemory() = default; + ~SonOfHakkarMemory() = default; + + [[nodiscard]] Unit* getSonOfHakkarDesignatedPuller() const noexcept + { + return this->sonOfHakkarDesignatedPuller; + } + + void setSonOfHakkarDesignatedPuller(Unit* unit) noexcept + { + this->sonOfHakkarDesignatedPuller = unit; + } + + [[nodiscard]] Creature* getActiveSonOfHakkar() const noexcept + { + return this->activeSonOfHakkar; + } + + void setActiveSonOfHakkar(Creature* unit) noexcept + { + this->activeSonOfHakkar = unit; + } + + [[nodiscard]] Creature* getLeftSonOfHakkar() const noexcept + { + return this->leftSonOfHakkar; + } + + void setLeftSonOfHakkar(Creature* unit) noexcept + { + this->leftSonOfHakkar = unit; + } + + [[nodiscard]] Creature* getRightSonOfHakkar() const noexcept + { + return this->rightSonOfHakkar; + } + + void setRightSonOfHakkar(Creature* unit) noexcept + { + this->rightSonOfHakkar = unit; + } + + [[nodiscard]] bool getSonOfHakkarKilledForCurrentBloodSiphon() const noexcept + { + return this->sonOfHakkarKilledForCurrentBloodSiphon; + } + + void setSonOfHakkarKilledForCurrentBloodSiphon(bool value) noexcept + { + this->sonOfHakkarKilledForCurrentBloodSiphon = value; + } +}; \ No newline at end of file diff --git a/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/definition/enum/HakkarTheSoulflayerEnum.h b/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/definition/enum/HakkarTheSoulflayerEnum.h new file mode 100644 index 00000000000..aaf1487815c --- /dev/null +++ b/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/definition/enum/HakkarTheSoulflayerEnum.h @@ -0,0 +1,11 @@ +#pragma once + +enum class HakkarTheSoulflayerEnum +{ + ENTRY = 14834u, + SPELL_CAUSE_INSANITY = 24327u, + SPELL_BLOOD_SIPHON = 24324u, + SPELL_CORRUPTED_BLOOD = 24328u, + NON_TANK_THREAT_THRESHOLD = 95u, + BLOOD_SIPHON_COOL_DOWN = 90u, +}; diff --git a/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/definition/enum/PoisonousCloudEnum.h b/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/definition/enum/PoisonousCloudEnum.h new file mode 100644 index 00000000000..9310c914d20 --- /dev/null +++ b/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/definition/enum/PoisonousCloudEnum.h @@ -0,0 +1,8 @@ +#pragma once + +enum class PoisonousCloudEnum +{ + ENTRY = 14989u, + AURA_POISONOUS_BLOOD = 24321u, + AURA_POISONOUS_BLOOD_DURATION = 60u, +}; diff --git a/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/definition/enum/SonOfHakkarEnum.h b/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/definition/enum/SonOfHakkarEnum.h new file mode 100644 index 00000000000..0301f1e0e5a --- /dev/null +++ b/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/definition/enum/SonOfHakkarEnum.h @@ -0,0 +1,9 @@ +#pragma once + +enum class SonOfHakkarEnum +{ + ENTRY = 11357u, + LEFT_SON_OF_HAKKAR_GUID = 49034u, + RIGHT_SON_OF_HAKKAR_GUID = 49033u, + PULL_TIME_BEFORE_BLOOD_SIPHON = 65u, +}; diff --git a/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/blood-siphon/GoToPoisonousCloud/HakkarTheSoulflayerGoToPoisonousCloudAction.h b/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/blood-siphon/GoToPoisonousCloud/HakkarTheSoulflayerGoToPoisonousCloudAction.h new file mode 100644 index 00000000000..01f17be6c74 --- /dev/null +++ b/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/blood-siphon/GoToPoisonousCloud/HakkarTheSoulflayerGoToPoisonousCloudAction.h @@ -0,0 +1,59 @@ +#pragma once + +#include "Spell.h" + +#include "AiObjectContext.h" +#include "HunterActions.h" +#include "MageActions.h" +#include "PaladinActions.h" +#include "PriestActions.h" +#include "RogueActions.h" +#include "MovementActions.h" +#include "PlayerbotAI.h" + +#include "WarlockActions.h" +#include "domain/core/raid/leader/RaidLeaderRegistry.h" + +class HakkarTheSoulflayerGoToPoisonousCloudAction : public MovementAction +{ +public: + HakkarTheSoulflayerGoToPoisonousCloudAction( + PlayerbotAI* botAI, + const std::string name = "hakkar the soulflayer go to poisonous cloud" + ) : MovementAction(botAI, name) {} + + bool Execute(Event) override + { + if (this->bot == nullptr) + { + return false; + } + + if (this->botAI == nullptr) + { + return false; + } + + const uint32_t instanceId = this->bot->GetInstanceId(); + RaidLeaderRegistry& raidRegistry = RaidLeaderRegistry::GetInstance(); + const ZulGurubRaidLeader& raidLeader = raidRegistry.getOrBind(instanceId, this->bot->GetMapId()); + const SonOfHakkarAssistant& sonOfHakkarAssistant = raidLeader.getSonOfHakkarAssistant(); + const Unit* const poisonousCloud = sonOfHakkarAssistant.findPoisonousCloud(*this->bot); + + if (poisonousCloud == nullptr) + { + return false; + } + + const Position poisonousCloudPosition = poisonousCloud->GetPosition(); + const float distanceToPoisonousCloud = this->bot->GetExactDist2d(poisonousCloudPosition.GetPositionX(), poisonousCloudPosition.GetPositionY()); + static constexpr float maxDistance = 5.0f; + + if (distanceToPoisonousCloud < maxDistance) + { + return false; + } + + return this->MoveTo(MAP_ZUL_GURUB, poisonousCloudPosition.GetPositionX(), poisonousCloudPosition.GetPositionY(), poisonousCloudPosition.GetPositionZ()); + } +}; diff --git a/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/blood-siphon/GoToPoisonousCloud/HakkarTheSoulflayerGoToPoisonousCloudMultiplier.h b/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/blood-siphon/GoToPoisonousCloud/HakkarTheSoulflayerGoToPoisonousCloudMultiplier.h new file mode 100644 index 00000000000..7fc9a43b746 --- /dev/null +++ b/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/blood-siphon/GoToPoisonousCloud/HakkarTheSoulflayerGoToPoisonousCloudMultiplier.h @@ -0,0 +1,63 @@ +#pragma once + +#include "AiObjectContext.h" +#include "MovementActions.h" +#include "PlayerbotAI.h" +#include "Multiplier.h" + +#include "domain/core/raid/leader/RaidLeaderRegistry.h" + +class HakkarTheSoulflayerGoToPoisonousCloudMultiplier : public Multiplier +{ +public: + HakkarTheSoulflayerGoToPoisonousCloudMultiplier(PlayerbotAI* botAI) : Multiplier(botAI, "hakkar the soulflayer go to poisonous cloud") {} + + float GetValue(Action& action) override + { + if (this->bot == nullptr) + { + return 0.0f; + } + + const uint32_t instanceId = this->bot->GetInstanceId(); + RaidLeaderRegistry& raidRegistry = RaidLeaderRegistry::GetInstance(); + const ZulGurubRaidLeader& raidLeader = raidRegistry.getOrBind(instanceId, this->bot->GetMapId()); + const HakkarTheSoulflayerAssistant& hakkarTheSoulflayerAssistant = raidLeader.getHakkarTheSoulflayerAssistant(); + + if (hakkarTheSoulflayerAssistant.isInCombatWithHakkar(*this->bot) == false) + { + return 1.0f; + } + + if (this->bot->HasAura(uint32_t(PoisonousCloudEnum::AURA_POISONOUS_BLOOD)) == true) + { + return 1.0f; + } + + const SonOfHakkarAssistant& sonOfHakkarAssistant = raidLeader.getSonOfHakkarAssistant(); + const Unit* const poisonousCloud = sonOfHakkarAssistant.findPoisonousCloud(*this->bot); + + if (poisonousCloud == nullptr) + { + return 1.0f; + } + + const Position poisonousCloudPosition = poisonousCloud->GetPosition(); + const float distanceToPoisonousCloud = this->bot->GetExactDist2d(poisonousCloudPosition.GetPositionX(), poisonousCloudPosition.GetPositionY()); + static constexpr float maxDistance = 5.0f; + + if (distanceToPoisonousCloud < maxDistance) + { + return 1.0f; + } + + const MovementAction* const movementAction = dynamic_cast(&action); + + if (movementAction != nullptr) + { + return 1.0f; + } + + return 0.0f; + } +}; diff --git a/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/blood-siphon/GoToPoisonousCloud/HakkarTheSoulflayerGoToPoisonousCloudTrigger.h b/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/blood-siphon/GoToPoisonousCloud/HakkarTheSoulflayerGoToPoisonousCloudTrigger.h new file mode 100644 index 00000000000..74f0af4b2ac --- /dev/null +++ b/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/blood-siphon/GoToPoisonousCloud/HakkarTheSoulflayerGoToPoisonousCloudTrigger.h @@ -0,0 +1,60 @@ +#pragma once + +#include "AiObjectContext.h" +#include "PlayerbotAI.h" +#include "Trigger.h" +#include "domain/core/raid/leader/RaidLeaderRegistry.h" + + +class HakkarTheSoulflayerGoToPoisonousCloudTrigger : public Trigger +{ +public: + HakkarTheSoulflayerGoToPoisonousCloudTrigger(PlayerbotAI* botAI) : Trigger(botAI, "hakkar the soulflayer go to poisonous cloud") {} + + bool IsActive() override + { + if (this->bot == nullptr) + { + return false; + } + + if (this->botAI == nullptr) + { + return false; + } + + const uint32_t instanceId = this->bot->GetInstanceId(); + RaidLeaderRegistry& raidRegistry = RaidLeaderRegistry::GetInstance(); + const ZulGurubRaidLeader& raidLeader = raidRegistry.getOrBind(instanceId, this->bot->GetMapId()); + const HakkarTheSoulflayerAssistant& hakkarTheSoulflayerAssistant = raidLeader.getHakkarTheSoulflayerAssistant(); + const SonOfHakkarAssistant& sonOfHakkarAssistant = raidLeader.getSonOfHakkarAssistant(); + + if (hakkarTheSoulflayerAssistant.isInCombatWithHakkar(*this->bot) == false) + { + return false; + } + + if (this->bot->HasAura(uint32_t(PoisonousCloudEnum::AURA_POISONOUS_BLOOD)) == true) + { + return false; + } + + const Unit* const poisonousCloud = sonOfHakkarAssistant.findPoisonousCloud(*this->bot); + + if (poisonousCloud == nullptr) + { + return false; + } + + const Position poisonousCloudPosition = poisonousCloud->GetPosition(); + const float distanceToPoisonousCloud = this->bot->GetExactDist2d(poisonousCloudPosition.GetPositionX(), poisonousCloudPosition.GetPositionY()); + static constexpr float maxDistance = 5.0f; + + if (distanceToPoisonousCloud < maxDistance) + { + return false; + } + + return true; + } +}; diff --git a/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/blood-siphon/PoisonousBlood/HakkarTheSoulflayerPoisonousBloodMultiplier.h b/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/blood-siphon/PoisonousBlood/HakkarTheSoulflayerPoisonousBloodMultiplier.h new file mode 100644 index 00000000000..28062618ede --- /dev/null +++ b/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/blood-siphon/PoisonousBlood/HakkarTheSoulflayerPoisonousBloodMultiplier.h @@ -0,0 +1,242 @@ +#pragma once + +#include "AiObjectContext.h" +#include "HakkarTheSoulflayer/definition/enum/PoisonousCloudEnum.h" +#include "MovementActions.h" +#include "Multiplier.h" +#include "PlayerbotAI.h" +#include "Value.h" + +#include "DruidActions.h" +#include "PaladinActions.h" +#include "ShamanActions.h" +#include "GenericSpellActions.h" +#include "domain/core/raid/leader/RaidLeaderRegistry.h" + +class HakkarTheSoulflayerPoisonousBloodMultiplier : public Multiplier +{ +public: + HakkarTheSoulflayerPoisonousBloodMultiplier(PlayerbotAI* botAI) : Multiplier(botAI, "hakkar the soulflayer poisonous blood") {} + + float GetValue(Action& action) override + { + if (this->bot == nullptr) + { + return 0.0f; + } + + const uint8_t botClassInfo = this->bot->getClass(); + + static constexpr std::array dispellingClasses = { CLASS_PALADIN, CLASS_SHAMAN, CLASS_DRUID }; + + if (std::find(dispellingClasses.begin(), dispellingClasses.end(), botClassInfo) == dispellingClasses.end()) + { + return 1.0f; + } + + const Unit* const actionTarget = action.GetTarget(); + + if (actionTarget == nullptr) + { + return 1.0f; + } + + const uint32_t instanceId = this->bot->GetInstanceId(); + RaidLeaderRegistry& raidRegistry = RaidLeaderRegistry::GetInstance(); + ZulGurubRaidLeader& raidLeader = raidRegistry.getOrBind(instanceId, this->bot->GetMapId()); + const HakkarTheSoulflayerAssistant& hakkarTheSoulflayerAssistant = raidLeader.getHakkarTheSoulflayerAssistant(); + + if (hakkarTheSoulflayerAssistant.isInCombatWithHakkar(*this->bot) == false) + { + return 1.0f; + } + + const bool hasPoisonousBlood = actionTarget->HasAura(uint32_t(PoisonousCloudEnum::AURA_POISONOUS_BLOOD)); + + if (hasPoisonousBlood == false) + { + return 1.0f; + } + + if (HakkarTheSoulflayerPoisonousBloodMultiplier::IsDruidDispellingAction(action)) + { + return 0.0f; + } + + if (HakkarTheSoulflayerPoisonousBloodMultiplier::IsPaladinDispellingAction(action)) + { + return 0.0f; + } + + if (HakkarTheSoulflayerPoisonousBloodMultiplier::IsShamanDispellingAction(action)) + { + return 0.0f; + } + + return 1.0f; + } + +private: + + [[nodiscard]] static bool IsDruidDispellingAction(const Action& action) noexcept + { + const CastCurePoisonAction* const castCurePoisonAction = dynamic_cast(&action); + + if (castCurePoisonAction != nullptr) + { + return true; + } + + const CastCurePoisonOnPartyAction* const castCurePoisonOnPartyAction = dynamic_cast(&action); + + if (castCurePoisonOnPartyAction != nullptr) + { + return true; + } + + const CastAbolishPoisonAction* const castAbolishPoisonAction = dynamic_cast(&action); + + if (castAbolishPoisonAction != nullptr) + { + return true; + } + + const CastAbolishPoisonOnPartyAction* const castAbolishPoisonOnPartyAction = dynamic_cast(&action); + + if (castAbolishPoisonOnPartyAction != nullptr) + { + return true; + } + + return false; + } + + [[nodiscard]] static bool IsPaladinDispellingAction(const Action& action) noexcept + { + const CastCleansePoisonAction* const castCleansePoisonAction = dynamic_cast(&action); + + if (castCleansePoisonAction != nullptr) + { + return true; + } + + const CastCleansePoisonOnPartyAction* const castCleansePoisonOnPartyAction = dynamic_cast(&action); + + if (castCleansePoisonOnPartyAction != nullptr) + { + return true; + } + + const CastCleanseMagicAction* const castCleanseMagicAction = dynamic_cast(&action); + + if (castCleanseMagicAction != nullptr) + { + return true; + } + + const CastCleanseMagicOnPartyAction* const castCleanseMagicOnPartyAction = dynamic_cast(&action); + + if (castCleanseMagicOnPartyAction != nullptr) + { + return true; + } + + const CastCleanseDiseaseAction* const castCleanseDiseaseAction = dynamic_cast(&action); + + if (castCleanseDiseaseAction != nullptr) + { + return true; + } + + const CastCleanseDiseaseOnPartyAction* const castCleanseDiseaseOnPartyAction = dynamic_cast(&action); + + if (castCleanseDiseaseOnPartyAction != nullptr) + { + return true; + } + + const CastPurifyPoisonAction* const castPurifyPoisonAction = dynamic_cast(&action); + + if (castPurifyPoisonAction != nullptr) + { + return true; + } + + const CastPurifyPoisonOnPartyAction* const castPurifyPoisonOnPartyAction = dynamic_cast(&action); + + if (castPurifyPoisonOnPartyAction != nullptr) + { + return true; + } + + const CastPurifyDiseaseAction* const castPurifyDiseaseAction = dynamic_cast(&action); + + if (castPurifyDiseaseAction != nullptr) + { + return true; + } + + const CastPurifyDiseaseOnPartyAction* const castPurifyDiseaseOnPartyAction = dynamic_cast(&action); + + if (castPurifyDiseaseOnPartyAction != nullptr) + { + return true; + } + + return false; + } + + [[nodiscard]] static bool IsShamanDispellingAction(const Action& action) noexcept + { + const CastCleanseSpiritAction* const castCleanseSpiritAction = dynamic_cast(&action); + + if (castCleanseSpiritAction != nullptr) + { + return true; + } + + const CastCleanseSpiritPoisonOnPartyAction* const castCleanseSpiritPoisonOnPartyAction = dynamic_cast(&action); + + if (castCleanseSpiritPoisonOnPartyAction != nullptr) + { + return true; + } + + const CastCurePoisonActionSham* const castCurePoisonActionSham = dynamic_cast(&action); + + if (castCurePoisonActionSham != nullptr) + { + return true; + } + + const CastCurePoisonOnPartyActionSham* const castCurePoisonOnPartyActionSham = dynamic_cast(&action); + + if (castCurePoisonOnPartyActionSham != nullptr) + { + return true; + } + + const CastCleanseSpiritCurseOnPartyAction* const castCleanseSpiritCurseOnPartyAction = dynamic_cast(&action); + + if (castCleanseSpiritCurseOnPartyAction != nullptr) + { + return true; + } + + const CastCleanseSpiritDiseaseOnPartyAction* const castCleanseSpiritDiseaseOnPartyAction = dynamic_cast(&action); + + if (castCleanseSpiritDiseaseOnPartyAction != nullptr) + { + return true; + } + + const CastCleansingTotemAction* const castCleansingTotemAction = dynamic_cast(&action); + + if (castCleansingTotemAction != nullptr) + { + return true; + } + + return false; + } +}; diff --git a/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/cause-insanity/CauseInsanity/HakkarTheSoulflayerCauseInsanityAction.h b/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/cause-insanity/CauseInsanity/HakkarTheSoulflayerCauseInsanityAction.h new file mode 100644 index 00000000000..43478dc4fba --- /dev/null +++ b/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/cause-insanity/CauseInsanity/HakkarTheSoulflayerCauseInsanityAction.h @@ -0,0 +1,75 @@ +#pragma once + +#include "MageActions.h" +#include "Spell.h" +#include "AiObjectContext.h" +#include "AttackAction.h" +#include "PlayerbotAI.h" + +#include "WarlockActions.h" + +#include "domain/core/raid/leader/RaidLeaderRegistry.h" + +class HakkarTheSoulflayerCauseInsanityAction : public AttackAction +{ +public: + HakkarTheSoulflayerCauseInsanityAction( + PlayerbotAI* botAI, + const std::string name = "hakkar the soulflayer cause insanity" + ) : AttackAction(botAI, name) {} + + bool Execute(Event) override + { + if (this->bot == nullptr) + { + return false; + } + + if (this->botAI == nullptr) + { + return false; + } + + const uint32_t instanceId = this->bot->GetInstanceId(); + RaidLeaderRegistry& raidRegistry = RaidLeaderRegistry::GetInstance(); + ZulGurubRaidLeader& raidLeader = raidRegistry.getOrBind(instanceId, this->bot->GetMapId()); + const HakkarTheSoulflayerAssistant& hakkarTheSoulflayerAssistant = raidLeader.getHakkarTheSoulflayerAssistant(); + + Player* const insaneAlly = hakkarTheSoulflayerAssistant.findInsaneAlly(*this->bot); + + if (insaneAlly == nullptr) + { + return false; + } + + // Value* const ccTargetValue = this->context->GetValue("cc target"); + + // if (ccTargetValue == nullptr) + // { + // return false; + // } + + // ccTargetValue->Set(insaneAlly); + + + const uint8_t botClassInfo = this->bot->getClass(); + + // if (botClassInfo == CLASS_MAGE) + // { + // this->bot->SetTarget(insaneAlly->GetGUID()); + + // this->bot->CastSpell(insaneAlly, ) + + // return this->botAI->DoSpecificAction(CreateNextAction(0.0f).factory); + // } + + if (botClassInfo == CLASS_WARLOCK) + { + const SpellInfo* const fearSpellInfo = SpellMgr::instance()->GetSpellInfo(5782); + + this->bot->CastSpell(insaneAlly, fearSpellInfo); + } + + return true; + } +}; diff --git a/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/cause-insanity/CauseInsanity/HakkarTheSoulflayerCauseInsanityTrigger.h b/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/cause-insanity/CauseInsanity/HakkarTheSoulflayerCauseInsanityTrigger.h new file mode 100644 index 00000000000..6dd61b99b8b --- /dev/null +++ b/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/cause-insanity/CauseInsanity/HakkarTheSoulflayerCauseInsanityTrigger.h @@ -0,0 +1,59 @@ +#pragma once + +#include "AiObjectContext.h" +#include "PlayerbotAI.h" +#include "Trigger.h" + +#include "domain/core/raid/leader/RaidLeaderRegistry.h" + +class HakkarTheSoulflayerCauseInsanityTrigger : public Trigger +{ +public: + HakkarTheSoulflayerCauseInsanityTrigger(PlayerbotAI* botAI) : Trigger(botAI, "hakkar the soulflayer cause insanity") {} + + bool IsActive() override + { + if (this->bot == nullptr) + { + return false; + } + + if (this->botAI == nullptr) + { + return false; + } + + const uint8_t botClassInfo = this->bot->getClass(); + + static constexpr std::array controllingClasses = { CLASS_MAGE, CLASS_WARLOCK }; + + if (std::find(controllingClasses.begin(), controllingClasses.end(), botClassInfo) == controllingClasses.end()) + { + return false; + } + + const uint32_t instanceId = this->bot->GetInstanceId(); + RaidLeaderRegistry& raidRegistry = RaidLeaderRegistry::GetInstance(); + ZulGurubRaidLeader& raidLeader = raidRegistry.getOrBind(instanceId, this->bot->GetMapId()); + const HakkarTheSoulflayerAssistant& hakkarTheSoulflayerAssistant = raidLeader.getHakkarTheSoulflayerAssistant(); + + if (hakkarTheSoulflayerAssistant.isInCombatWithHakkar(*this->bot) == false) + { + return false; + } + + const Player* const insaneAlly = hakkarTheSoulflayerAssistant.findInsaneAlly(*this->bot); + + if (insaneAlly == nullptr) + { + return false; + } + + if (insaneAlly->IsCrowdControlled()) + { + return false; + } + + return true; + } +}; diff --git a/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/positioning/HakkarPositioning/HakkarTheSoulflayerHakkarPositioningAction.h b/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/positioning/HakkarPositioning/HakkarTheSoulflayerHakkarPositioningAction.h new file mode 100644 index 00000000000..27a4e8e2fd9 --- /dev/null +++ b/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/positioning/HakkarPositioning/HakkarTheSoulflayerHakkarPositioningAction.h @@ -0,0 +1,92 @@ +#pragma once + +#include "Spell.h" +#include "AiObjectContext.h" +#include "AttackAction.h" +#include "PlayerbotAI.h" +#include "domain/core/raid/leader/RaidLeaderRegistry.h" + +class HakkarTheSoulflayerHakkarPositioningAction : public AttackAction +{ +public: + HakkarTheSoulflayerHakkarPositioningAction( + PlayerbotAI* botAI, + const std::string name = "hakkar the soulflayer hakkar positioning" + ) : AttackAction(botAI, name) {} + + bool Execute(Event) override + { + if (this->bot == nullptr) + { + return false; + } + + if (this->botAI == nullptr) + { + return false; + } + + if (this->botAI->IsMainTank(this->bot) == false) + { + return false; + } + + const uint32_t instanceId = this->bot->GetInstanceId(); + RaidLeaderRegistry& raidRegistry = RaidLeaderRegistry::GetInstance(); + ZulGurubRaidLeader& raidLeader = raidRegistry.getOrBind(instanceId, this->bot->GetMapId()); + const HakkarTheSoulflayerAssistant& hakkarTheSoulflayerAssistant = raidLeader.getHakkarTheSoulflayerAssistant(); + + Unit* const boss = hakkarTheSoulflayerAssistant.findActiveBoss(*this->bot); + + if (boss == nullptr) + { + return false; + } + + if (this->bot->GetVictim() != boss) + { + this->Attack(boss); + + return false; + } + + if (boss->GetTarget() != this->bot->GetGUID()) + { + this->Attack(boss); + + return false; + } + + const Position bossIdealPosition = hakkarTheSoulflayerAssistant.getHakkarPosition(); + const float distanceToIdealPosition = this->bot->GetExactDist2d(bossIdealPosition.GetPositionX(), bossIdealPosition.GetPositionY()); + const float maxDistance = hakkarTheSoulflayerAssistant.getHakkarMaxPositionDistance(); + + if (distanceToIdealPosition < maxDistance) + { + return false; + } + + const float botPositionX = this->bot->GetPositionX(); + const float botPositionY = this->bot->GetPositionY(); + const float dX = bossIdealPosition.GetPositionX() - botPositionX; + const float dY = bossIdealPosition.GetPositionY() - botPositionY; + const float moveX = botPositionX + (dX / distanceToIdealPosition) * maxDistance; + const float moveY = botPositionY + (dY / distanceToIdealPosition) * maxDistance; + + return this->MoveTo( + MAP_ZUL_GURUB, + moveX, + moveY, + bossIdealPosition.GetPositionZ(), + false, + false, + false, + false, + MovementPriority::MOVEMENT_COMBAT, + true, + true + ); + + return true; + } +}; diff --git a/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/positioning/HakkarPositioning/HakkarTheSoulflayerHakkarPositioningTrigger.h b/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/positioning/HakkarPositioning/HakkarTheSoulflayerHakkarPositioningTrigger.h new file mode 100644 index 00000000000..7c1bf9e9366 --- /dev/null +++ b/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/positioning/HakkarPositioning/HakkarTheSoulflayerHakkarPositioningTrigger.h @@ -0,0 +1,59 @@ +#pragma once + +#include "AiObjectContext.h" +#include "PlayerbotAI.h" +#include "Trigger.h" + +#include "domain/core/raid/leader/RaidLeaderRegistry.h" + +class HakkarTheSoulflayerHakkarPositioningTrigger : public Trigger +{ +public: + HakkarTheSoulflayerHakkarPositioningTrigger(PlayerbotAI* botAI) : Trigger(botAI, "hakkar the soulflayer hakkar positioning") {} + + bool IsActive() override + { + if (this->bot == nullptr) + { + return false; + } + + if (this->botAI == nullptr) + { + return false; + } + + const uint32_t instanceId = this->bot->GetInstanceId(); + RaidLeaderRegistry& raidRegistry = RaidLeaderRegistry::GetInstance(); + ZulGurubRaidLeader& raidLeader = raidRegistry.getOrBind(instanceId, this->bot->GetMapId()); + const HakkarTheSoulflayerAssistant& hakkarTheSoulflayerAssistant = raidLeader.getHakkarTheSoulflayerAssistant(); + + if (hakkarTheSoulflayerAssistant.isInCombatWithHakkar(*this->bot) == false) + { + return false; + } + + const Unit* const boss = hakkarTheSoulflayerAssistant.findActiveBoss(*this->bot); + + if (boss == nullptr) + { + return false; + } + + if (boss->GetVictim() != this->bot) + { + return false; + } + + const Position bossIdealPosition = hakkarTheSoulflayerAssistant.getHakkarPosition(); + const float distanceToIdealPosition = this->bot->GetExactDist2d(bossIdealPosition.GetPositionX(), bossIdealPosition.GetPositionY()); + const float maxDistance = hakkarTheSoulflayerAssistant.getHakkarMaxPositionDistance(); + + if (distanceToIdealPosition < maxDistance) + { + return false; + } + + return true; + } +}; diff --git a/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/son-of-hakkar/BringBackSonOfHakkar/HakkarTheSoulflayerBringBackSonOfHakkarAction.h b/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/son-of-hakkar/BringBackSonOfHakkar/HakkarTheSoulflayerBringBackSonOfHakkarAction.h new file mode 100644 index 00000000000..1c901a09b68 --- /dev/null +++ b/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/son-of-hakkar/BringBackSonOfHakkar/HakkarTheSoulflayerBringBackSonOfHakkarAction.h @@ -0,0 +1,83 @@ +#pragma once + +#include "HunterActions.h" +#include "MageActions.h" +#include "PaladinActions.h" +#include "PriestActions.h" +#include "RogueActions.h" +#include "Spell.h" +#include "AiObjectContext.h" +#include "MovementActions.h" +#include "PlayerbotAI.h" + +#include "WarlockActions.h" +#include "domain/core/raid/leader/RaidLeaderRegistry.h" + + +class HakkarTheSoulflayerBringBackSonOfHakkarAction : public MovementAction +{ +public: + HakkarTheSoulflayerBringBackSonOfHakkarAction( + PlayerbotAI* botAI, + const std::string name = "hakkar the soulflayer bring back son of hakkar" + ) : MovementAction(botAI, name) {} + + bool Execute(Event) override + { + if (this->bot == nullptr) + { + return false; + } + + if (this->botAI == nullptr) + { + return false; + } + + const uint32_t instanceId = this->bot->GetInstanceId(); + RaidLeaderRegistry& raidRegistry = RaidLeaderRegistry::GetInstance(); + ZulGurubRaidLeader& raidLeader = raidRegistry.getOrBind(instanceId, this->bot->GetMapId()); + const HakkarTheSoulflayerAssistant& hakkarTheSoulflayerAssistant = raidLeader.getHakkarTheSoulflayerAssistant(); + SonOfHakkarAssistant& sonOfHakkarAssistant = raidLeader.getSonOfHakkarAssistant(); + const SonOfHakkarMemory& memory = sonOfHakkarAssistant.getMemory(); + + Creature* const sonOfHakkar = memory.getActiveSonOfHakkar(); + + if (sonOfHakkar == nullptr) + { + return false; + } + + const Position hakkarPosition = hakkarTheSoulflayerAssistant.getHakkarPosition(); + + const float distanceToIdealPosition = this->bot->GetExactDist(hakkarPosition.GetPositionX(), hakkarPosition.GetPositionY(), hakkarPosition.GetPositionZ()); + const float maxDistance = hakkarTheSoulflayerAssistant.getHakkarMaxPositionDistance(); + + if (distanceToIdealPosition < maxDistance) + { + return false; + } + + const float sonOfHakkarPositionX = sonOfHakkar->GetPositionX(); + const float sonOfHakkarPositionY = sonOfHakkar->GetPositionY(); + const float dX = hakkarPosition.GetPositionX() - sonOfHakkarPositionX; + const float dY = hakkarPosition.GetPositionY() - sonOfHakkarPositionY; + const float moveX = sonOfHakkarPositionX + (dX / distanceToIdealPosition) * maxDistance; + const float moveY = sonOfHakkarPositionY + (dY / distanceToIdealPosition) * maxDistance; + + return this->MoveTo( + MAP_ZUL_GURUB, + moveX, + moveY, + hakkarPosition.GetPositionZ(), + false, + false, + false, + false, + MovementPriority::MOVEMENT_FORCED, + true, + false + ); + + } +}; diff --git a/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/son-of-hakkar/BringBackSonOfHakkar/HakkarTheSoulflayerBringBackSonOfHakkarTrigger.h b/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/son-of-hakkar/BringBackSonOfHakkar/HakkarTheSoulflayerBringBackSonOfHakkarTrigger.h new file mode 100644 index 00000000000..cd280a9bead --- /dev/null +++ b/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/son-of-hakkar/BringBackSonOfHakkar/HakkarTheSoulflayerBringBackSonOfHakkarTrigger.h @@ -0,0 +1,59 @@ +#pragma once + +#include + +#include "AiObjectContext.h" +#include "PlayerbotAI.h" +#include "Trigger.h" +#include "domain/core/raid/leader/RaidLeaderRegistry.h" + +class HakkarTheSoulflayerBringBackSonOfHakkarTrigger : public Trigger +{ +public: + HakkarTheSoulflayerBringBackSonOfHakkarTrigger(PlayerbotAI* botAI) : Trigger(botAI, "hakkar the soulflayer bring back son of hakkar") {} + + bool IsActive() override + { + if (this->bot == nullptr) + { + return false; + } + + if (this->botAI == nullptr) + { + return false; + } + + const uint32_t instanceId = this->bot->GetInstanceId(); + RaidLeaderRegistry& raidRegistry = RaidLeaderRegistry::GetInstance(); + ZulGurubRaidLeader& raidLeader = raidRegistry.getOrBind(instanceId, this->bot->GetMapId()); + const HakkarTheSoulflayerAssistant& hakkarTheSoulflayerAssistant = raidLeader.getHakkarTheSoulflayerAssistant(); + const SonOfHakkarAssistant& sonOfHakkarAssistant = raidLeader.getSonOfHakkarAssistant(); + + if (hakkarTheSoulflayerAssistant.isInCombatWithHakkar(*this->bot) == false) + { + return false; + } + + const SonOfHakkarMemory& memory = sonOfHakkarAssistant.getMemory(); + + if (memory.getSonOfHakkarKilledForCurrentBloodSiphon() == true) + { + return false; + } + + const Creature* const sonOfHakkar = memory.getActiveSonOfHakkar(); + + if (sonOfHakkar == nullptr) + { + return false; + } + + if (sonOfHakkar->GetVictim() != this->bot) + { + return false; + } + + return true; + } +}; diff --git a/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/son-of-hakkar/MoveToSonOfHakkar/HakkarTheSoulflayerMoveToSonOfHakkarAction.h b/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/son-of-hakkar/MoveToSonOfHakkar/HakkarTheSoulflayerMoveToSonOfHakkarAction.h new file mode 100644 index 00000000000..4bd3d0bb0f3 --- /dev/null +++ b/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/son-of-hakkar/MoveToSonOfHakkar/HakkarTheSoulflayerMoveToSonOfHakkarAction.h @@ -0,0 +1,56 @@ +#pragma once + +#include "HunterActions.h" +#include "MageActions.h" +#include "PaladinActions.h" +#include "PriestActions.h" +#include "RogueActions.h" +#include "Spell.h" +#include "AiObjectContext.h" +#include "MovementActions.h" +#include "PlayerbotAI.h" + +#include "WarlockActions.h" +#include "domain/core/raid/leader/RaidLeaderRegistry.h" + + +class HakkarTheSoulflayerMoveToSonOfHakkarAction : public MovementAction +{ +public: + HakkarTheSoulflayerMoveToSonOfHakkarAction( + PlayerbotAI* botAI, + const std::string name = "hakkar the soulflayer move to son of hakkar" + ) : MovementAction(botAI, name) {} + + bool Execute(Event) override + { + if (this->bot == nullptr) + { + return false; + } + + if (this->botAI == nullptr) + { + return false; + } + + const uint32_t instanceId = this->bot->GetInstanceId(); + RaidLeaderRegistry& raidRegistry = RaidLeaderRegistry::GetInstance(); + ZulGurubRaidLeader& raidLeader = raidRegistry.getOrBind(instanceId, this->bot->GetMapId()); + SonOfHakkarAssistant& sonOfHakkarAssistant = raidLeader.getSonOfHakkarAssistant(); + + Creature* const sonOfHakkar = sonOfHakkarAssistant.findAnyAliveSonOfHakkar(); + + if (sonOfHakkar == nullptr) + { + return false; + } + + sonOfHakkarAssistant.setActiveSonOfHakkar(sonOfHakkar); + + const bool meleeBot = PlayerbotAI::IsMelee(this->bot); + const float maxDistance = meleeBot ? 5.0f : 20.0f; + + return this->ReachCombatTo(sonOfHakkar, maxDistance); + } +}; diff --git a/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/son-of-hakkar/MoveToSonOfHakkar/HakkarTheSoulflayerMoveToSonOfHakkarTrigger.h b/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/son-of-hakkar/MoveToSonOfHakkar/HakkarTheSoulflayerMoveToSonOfHakkarTrigger.h new file mode 100644 index 00000000000..71ec5cb3702 --- /dev/null +++ b/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/son-of-hakkar/MoveToSonOfHakkar/HakkarTheSoulflayerMoveToSonOfHakkarTrigger.h @@ -0,0 +1,86 @@ +#pragma once + +#include + +#include "AiObjectContext.h" +#include "PlayerbotAI.h" +#include "Trigger.h" + +#include "domain/core/raid/leader/RaidLeaderRegistry.h" + +class HakkarTheSoulflayerMoveToSonOfHakkarTrigger : public Trigger +{ +public: + HakkarTheSoulflayerMoveToSonOfHakkarTrigger(PlayerbotAI* botAI) : Trigger(botAI, "hakkar the soulflayer move to son of hakkar") {} + + bool IsActive() override + { + if (this->bot == nullptr) + { + return false; + } + + const uint32_t instanceId = this->bot->GetInstanceId(); + RaidLeaderRegistry& raidRegistry = RaidLeaderRegistry::GetInstance(); + ZulGurubRaidLeader& raidLeader = raidRegistry.getOrBind(instanceId, this->bot->GetMapId()); + HakkarTheSoulflayerAssistant& hakkarTheSoulflayerAssistant = raidLeader.getHakkarTheSoulflayerAssistant(); + const SonOfHakkarAssistant& sonOfHakkarAssistant = raidLeader.getSonOfHakkarAssistant(); + + if (hakkarTheSoulflayerAssistant.isInCombatWithHakkar(*this->bot) == false) + { + return false; + } + + const SonOfHakkarMemory& sonOfHakkarMemory = sonOfHakkarAssistant.getMemory(); + + if (sonOfHakkarMemory.getSonOfHakkarKilledForCurrentBloodSiphon() == true) + { + return false; + } + + const Unit* const sonOfHakkarDesignatedPuller = sonOfHakkarMemory.getSonOfHakkarDesignatedPuller(); + + if (sonOfHakkarDesignatedPuller == nullptr) + { + return false; + } + + if (sonOfHakkarDesignatedPuller->GetGUID() != this->bot->GetGUID()) + { + return false; + } + + const Creature* const sonOfHakkar = sonOfHakkarAssistant.findAnyAliveSonOfHakkar(); + + if (sonOfHakkar == nullptr) + { + return false; + } + + if (sonOfHakkar->IsInCombatWith(this->bot) == true || this->bot->IsInCombatWith(sonOfHakkar) == true) + { + return false; + } + + const HakkarTheSoulflayerMemory& hakkarTheSoulflayerMemory = hakkarTheSoulflayerAssistant.getMemory(); + + const double bloodSiphonCooldown = double(HakkarTheSoulflayerEnum::BLOOD_SIPHON_COOL_DOWN); + const double pullTimeAfterBloodSiphon = bloodSiphonCooldown - double(SonOfHakkarEnum::PULL_TIME_BEFORE_BLOOD_SIPHON); + const double timeSinceLastBloodSiphon = hakkarTheSoulflayerMemory.getBloodSiphonWatch().getElapsedSeconds(); + + if (timeSinceLastBloodSiphon <= pullTimeAfterBloodSiphon) + { + return false; + } + + const float distanceToSonOfHakkar = sonOfHakkarAssistant.getDistanceToActiveSonOfHakkar(*this->bot); + const Creature* const activeSonOfHakkar = sonOfHakkarMemory.getActiveSonOfHakkar(); + + if (activeSonOfHakkar != nullptr && distanceToSonOfHakkar < 25.0f) + { + return false; + } + + return true; + } +}; diff --git a/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/son-of-hakkar/PullSonOfHakkar/HakkarTheSoulflayerPullSonOfHakkarAction.h b/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/son-of-hakkar/PullSonOfHakkar/HakkarTheSoulflayerPullSonOfHakkarAction.h new file mode 100644 index 00000000000..f917b597ae2 --- /dev/null +++ b/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/son-of-hakkar/PullSonOfHakkar/HakkarTheSoulflayerPullSonOfHakkarAction.h @@ -0,0 +1,101 @@ +#pragma once + +#include "DruidActions.h" +#include "HunterActions.h" +#include "MageActions.h" +#include "PaladinActions.h" +#include "PriestActions.h" +#include "RogueActions.h" +#include "Spell.h" +#include "AiObjectContext.h" +#include "AttackAction.h" +#include "PlayerbotAI.h" + +#include "WarlockActions.h" + +#include "domain/core/raid/leader/RaidLeaderRegistry.h" + +class HakkarTheSoulflayerPullSonOfHakkarAction : public AttackAction +{ +public: + HakkarTheSoulflayerPullSonOfHakkarAction( + PlayerbotAI* botAI, + const std::string name = "hakkar the soulflayer pull son of hakkar" + ) : AttackAction(botAI, name) {} + + bool Execute(Event) override + { + if (this->bot == nullptr) + { + return false; + } + + const uint32_t instanceId = this->bot->GetInstanceId(); + RaidLeaderRegistry& raidRegistry = RaidLeaderRegistry::GetInstance(); + const ZulGurubRaidLeader& raidLeader = raidRegistry.getOrBind(instanceId, this->bot->GetMapId()); + const SonOfHakkarAssistant& sonOfHakkarAssistant = raidLeader.getSonOfHakkarAssistant(); + const SonOfHakkarMemory& sonOfHakkarMemory = sonOfHakkarAssistant.getMemory(); + + Creature* const sonOfHakkar = sonOfHakkarMemory.getActiveSonOfHakkar(); + + if (sonOfHakkar == nullptr) + { + return false; + } + + const bool meleeBot = PlayerbotAI::IsMelee(this->bot); + + this->bot->SetTarget(sonOfHakkar->GetGUID()); + + if (meleeBot) + { + return this->Attack(sonOfHakkar); + } + + const uint8_t botClassInfo = this->bot->getClass(); + + if (botClassInfo == CLASS_PRIEST) + { + const SpellInfo* const shadowWordPainSpellInfo = SpellMgr::instance()->GetSpellInfo(589); + + this->bot->CastSpell(sonOfHakkar, shadowWordPainSpellInfo); + } + + if (botClassInfo == CLASS_WARLOCK) + { + const SpellInfo* const corruptionSpellInfo = SpellMgr::instance()->GetSpellInfo(172); + + this->bot->CastSpell(sonOfHakkar, corruptionSpellInfo); + } + + if (botClassInfo == CLASS_MAGE) + { + const SpellInfo* const fireBlastRank1SpellInfo = SpellMgr::instance()->GetSpellInfo(2136); + + this->bot->CastSpell(sonOfHakkar, fireBlastRank1SpellInfo); + } + + if (botClassInfo == CLASS_HUNTER) + { + const SpellInfo* const serpentStingRank1SpellInfo = SpellMgr::instance()->GetSpellInfo(1978); + + this->bot->CastSpell(sonOfHakkar, serpentStingRank1SpellInfo); + } + + if (botClassInfo == CLASS_DRUID) + { + const SpellInfo* const moonfireRank1SpellInfo = SpellMgr::instance()->GetSpellInfo(8921); + + this->bot->CastSpell(sonOfHakkar, moonfireRank1SpellInfo); + } + + if (botClassInfo == CLASS_PALADIN) + { + const SpellInfo* const handOfReckoningSpellInfo = SpellMgr::instance()->GetSpellInfo(62124); + + this->bot->CastSpell(sonOfHakkar, handOfReckoningSpellInfo); + } + + return true; + } +}; diff --git a/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/son-of-hakkar/PullSonOfHakkar/HakkarTheSoulflayerPullSonOfHakkarTrigger.h b/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/son-of-hakkar/PullSonOfHakkar/HakkarTheSoulflayerPullSonOfHakkarTrigger.h new file mode 100644 index 00000000000..b06fcf455e2 --- /dev/null +++ b/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/son-of-hakkar/PullSonOfHakkar/HakkarTheSoulflayerPullSonOfHakkarTrigger.h @@ -0,0 +1,74 @@ +#pragma once + +#include + +#include "AiObjectContext.h" +#include "PlayerbotAI.h" +#include "Trigger.h" + +#include "domain/core/raid/leader/RaidLeaderRegistry.h" + +class HakkarTheSoulflayerPullSonOfHakkarTrigger : public Trigger +{ +public: + HakkarTheSoulflayerPullSonOfHakkarTrigger(PlayerbotAI* botAI) : Trigger(botAI, "hakkar the soulflayer pull son of hakkar") {} + + bool IsActive() override + { + if (this->bot == nullptr) + { + return false; + } + + const uint32_t instanceId = this->bot->GetInstanceId(); + RaidLeaderRegistry& raidRegistry = RaidLeaderRegistry::GetInstance(); + const ZulGurubRaidLeader& raidLeader = raidRegistry.getOrBind(instanceId, this->bot->GetMapId()); + const HakkarTheSoulflayerAssistant& hakkarTheSoulflayerAssistant = raidLeader.getHakkarTheSoulflayerAssistant(); + const SonOfHakkarAssistant& sonOfHakkarAssistant = raidLeader.getSonOfHakkarAssistant(); + + if (hakkarTheSoulflayerAssistant.isInCombatWithHakkar(*this->bot) == false) + { + return false; + } + + const SonOfHakkarMemory& sonOfHakkarMemory = sonOfHakkarAssistant.getMemory(); + + if (sonOfHakkarMemory.getSonOfHakkarKilledForCurrentBloodSiphon() == true) + { + return false; + } + + const Unit* const sonOfHakkarDesignatedPuller = sonOfHakkarMemory.getSonOfHakkarDesignatedPuller(); + + if (sonOfHakkarDesignatedPuller == nullptr) + { + return false; + } + + if (sonOfHakkarDesignatedPuller->GetGUID() != this->bot->GetGUID()) + { + return false; + } + + const Creature* const sonOfHakkar = sonOfHakkarMemory.getActiveSonOfHakkar(); + + if (sonOfHakkar == nullptr) + { + return false; + } + + if (sonOfHakkar->IsInCombatWith(this->bot) == true || this->bot->IsInCombatWith(sonOfHakkar) == true) + { + return false; + } + + const Position sonOfHakkarPosition = sonOfHakkar->GetPosition(); + const float distanceToSonOfHakkar = this->bot->GetExactDist2d(sonOfHakkarPosition.GetPositionX(), sonOfHakkarPosition.GetPositionY()); + + const bool meleeBot = PlayerbotAI::IsMelee(this->bot); + + const float minimumDistance = meleeBot ? 5.0f : 25.0f; + + return distanceToSonOfHakkar < minimumDistance; + } +}; diff --git a/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/threat/ExcessiveThreat/HakkarTheSoulflayerExcessiveThreatAction.h b/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/threat/ExcessiveThreat/HakkarTheSoulflayerExcessiveThreatAction.h new file mode 100644 index 00000000000..9863164bd89 --- /dev/null +++ b/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/threat/ExcessiveThreat/HakkarTheSoulflayerExcessiveThreatAction.h @@ -0,0 +1,68 @@ +#pragma once + +#include "HunterActions.h" +#include "MageActions.h" +#include "PaladinActions.h" +#include "PriestActions.h" +#include "RogueActions.h" +#include "Spell.h" +#include "AiObjectContext.h" +#include "AttackAction.h" +#include "PlayerbotAI.h" + +#include "WarlockActions.h" + +class HakkarTheSoulflayerExcessiveThreatAction : public AttackAction +{ +public: + HakkarTheSoulflayerExcessiveThreatAction( + PlayerbotAI* botAI, + const std::string name = "hakkar the soulflayer excessive threat" + ) : AttackAction(botAI, name) {} + + bool Execute(Event) override + { + if (this->bot == nullptr) + { + return false; + } + + if (this->botAI == nullptr) + { + return false; + } + + // const uint8_t botClassInfo = this->bot->getClass(); + + // if (botClassInfo == CLASS_PALADIN) + // { + // const SpellInfo* const handOfSalvationSpellInfo = SpellMgr::instance()->GetSpellInfo(1038); + + // this->bot->CastSpell(this->bot, handOfSalvationSpellInfo); + // } + + // if (botClassInfo == CLASS_HUNTER) + // { + // const SpellInfo* const feignDeathSpellInfo = SpellMgr::instance()->GetSpellInfo(5384); + + // this->bot->CastSpell(this->bot, feignDeathSpellInfo); + // } + + // if (botClassInfo == CLASS_ROGUE) + // { + + + // return this->botAI->DoSpecificAction(CreateNextAction(0.0f).factory) + // || this->botAI->DoSpecificAction(CreateNextAction(0.0f).factory); + // } + + // if (botClassInfo == CLASS_PRIEST) + // { + // return this->botAI->DoSpecificAction(CreateNextAction(0.0f).factory); + // } + + this->botAI->SetNextCheckDelay(500); + + return true; + } +}; diff --git a/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/threat/ExcessiveThreat/HakkarTheSoulflayerExcessiveThreatTrigger.h b/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/threat/ExcessiveThreat/HakkarTheSoulflayerExcessiveThreatTrigger.h new file mode 100644 index 00000000000..8011ba84785 --- /dev/null +++ b/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/threat/ExcessiveThreat/HakkarTheSoulflayerExcessiveThreatTrigger.h @@ -0,0 +1,59 @@ +#pragma once + +#include "AiObjectContext.h" +#include "PlayerbotAI.h" +#include "Trigger.h" + +#include "domain/core/raid/leader/RaidLeaderRegistry.h" + +class HakkarTheSoulflayerExcessiveThreatTrigger : public Trigger +{ +public: + HakkarTheSoulflayerExcessiveThreatTrigger(PlayerbotAI* botAI) : Trigger(botAI, "hakkar the soulflayer excessive threat") {} + + bool IsActive() override + { + if (this->bot == nullptr) + { + return false; + } + + if (this->botAI == nullptr) + { + return false; + } + + const uint32_t instanceId = this->bot->GetInstanceId(); + RaidLeaderRegistry& raidRegistry = RaidLeaderRegistry::GetInstance(); + ZulGurubRaidLeader& raidLeader = raidRegistry.getOrBind(instanceId, this->bot->GetMapId()); + const HakkarTheSoulflayerAssistant& hakkarTheSoulflayerAssistant = raidLeader.getHakkarTheSoulflayerAssistant(); + + if (hakkarTheSoulflayerAssistant.isInCombatWithHakkar(*this->bot) == false) + { + return false; + } + + const uint8_t botClassInfo = this->bot->getClass(); + + static constexpr std::array classesWithSpecialActions = { + CLASS_PALADIN, + CLASS_HUNTER, + CLASS_ROGUE, + CLASS_PRIEST, + }; + + if (std::find(classesWithSpecialActions.begin(), classesWithSpecialActions.end(), botClassInfo) != classesWithSpecialActions.end()) + { + return false; + } + + RaidTankAssistant& raidTankAssistant = raidLeader.getRaidTankAssistant(); + + return hakkarTheSoulflayerAssistant.hasExcessiveThreat( + *this->bot, + { + .raidTankAssistant = raidTankAssistant + } + ); + } +}; diff --git a/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/script/HakkarTheSoulflayerPlayerScript.h b/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/script/HakkarTheSoulflayerPlayerScript.h new file mode 100644 index 00000000000..68512bb99ab --- /dev/null +++ b/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/script/HakkarTheSoulflayerPlayerScript.h @@ -0,0 +1,41 @@ +#pragma once + +#include "Creature.h" +#include "Player.h" +#include "PlayerScript.h" + +#include "domain/core/raid/leader/RaidLeaderRegistry.h" + +class HakkarTheSoulflayerPlayerScript : public PlayerScript +{ +public: + HakkarTheSoulflayerPlayerScript() : PlayerScript("HakkarTheSoulflayerPlayerScript") {} + + void OnPlayerKilledByCreature(Creature*, Player* player) override + { + if (player == nullptr) + { + return; + } + + const uint32_t mapId = player->GetMapId(); + + if (mapId != MAP_ZUL_GURUB) + { + return; + } + + const uint32_t instanceId = player->GetInstanceId(); + RaidLeaderRegistry& raidRegistry = RaidLeaderRegistry::GetInstance(); + ZulGurubRaidLeader& raidLeader = raidRegistry.getOrBind(instanceId, player->GetMapId()); + SonOfHakkarAssistant& sonOfHakkarAssistant = raidLeader.getSonOfHakkarAssistant(); + + const SonOfHakkarMemory& memory = sonOfHakkarAssistant.getMemory(); + + if (player == memory.getSonOfHakkarDesignatedPuller()) + { + sonOfHakkarAssistant.initialiseDesignatedPuller(*player); + } + } + +}; \ No newline at end of file diff --git a/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/script/HakkarTheSoulflayerScript.h b/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/script/HakkarTheSoulflayerScript.h new file mode 100644 index 00000000000..fbafc00c700 --- /dev/null +++ b/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/script/HakkarTheSoulflayerScript.h @@ -0,0 +1,119 @@ +#pragma once + +#include "Group.h" +#include "Map.h" +#include "HakkarTheSoulflayer/definition/enum/HakkarTheSoulflayerEnum.h" +#include "SonOfHakkarEnum.h" +#include "Unit.h" +#include "Player.h" +#include "UnitScript.h" +#include "PlayerbotAI.h" +#include "utility/StopWatch.h" + +#include "domain/core/raid/leader/RaidLeaderRegistry.h" + +class HakkarTheSoulflayerScript : public UnitScript +{ +public: + HakkarTheSoulflayerScript() : UnitScript("HakkarTheSoulflayerScript") {} + + void OnUnitEnterCombat(Unit* hakkar, Unit* unit) override + { + if (hakkar == nullptr) + { + return; + } + + if (hakkar->GetEntry() != uint32_t(HakkarTheSoulflayerEnum::ENTRY)) + { + return; + } + + const uint32_t instanceId = unit->GetInstanceId(); + RaidLeaderRegistry& raidRegistry = RaidLeaderRegistry::GetInstance(); + ZulGurubRaidLeader& raidLeader = raidRegistry.getOrBind(instanceId, unit->GetMapId()); + RaidTankAssistant& raidTankAssistant = raidLeader.getRaidTankAssistant(); + HakkarTheSoulflayerAssistant& hakkarTheSoulflayerAssistant = raidLeader.getHakkarTheSoulflayerAssistant(); + SonOfHakkarAssistant& sonOfHakkarAssistant = raidLeader.getSonOfHakkarAssistant(); + + Player* const player = dynamic_cast(unit); + + if (player == nullptr) + { + return; + } + + raidTankAssistant.initialiseTanks(*player); + hakkarTheSoulflayerAssistant.restartBloodSiphonWatch(); + sonOfHakkarAssistant.initialiseDesignatedPuller(*player); + sonOfHakkarAssistant.initialiseSonsOfHakkar(*player); + } + + void OnUnitDeath(Unit* unit, Unit*) override + { + if (unit == nullptr) + { + return; + } + + if (unit->GetEntry() != uint32_t(SonOfHakkarEnum::ENTRY)) + { + return; + } + + const uint32_t instanceId = unit->GetInstanceId(); + RaidLeaderRegistry& raidRegistry = RaidLeaderRegistry::GetInstance(); + ZulGurubRaidLeader& raidLeader = raidRegistry.getOrBind(instanceId, unit->GetMapId()); + const HakkarTheSoulflayerAssistant& hakkarTheSoulflayerAssistant = raidLeader.getHakkarTheSoulflayerAssistant(); + SonOfHakkarAssistant& sonOfHakkarAssistant = raidLeader.getSonOfHakkarAssistant(); + + const SonOfHakkarMemory& memory = sonOfHakkarAssistant.getMemory(); + + const Creature* const leftSonOfHakkar = memory.getLeftSonOfHakkar(); + const Creature* const rightSonOfHakkar = memory.getRightSonOfHakkar(); + + if (leftSonOfHakkar == nullptr || rightSonOfHakkar == nullptr) + { + return; + } + + const HakkarTheSoulflayerMemory& hakkarTheSoulflayerMemory = hakkarTheSoulflayerAssistant.getMemory(); + const bool isLeftSonOfHakkarKilled = unit->GetGUID() == leftSonOfHakkar->GetGUID(); + const bool isRightSonOfHakkarKilled = unit->GetGUID() == rightSonOfHakkar->GetGUID(); + const bool isKilledOnTimeForBloodSiphon = hakkarTheSoulflayerMemory.getBloodSiphonWatch().getElapsedSeconds() <= double(PoisonousCloudEnum::AURA_POISONOUS_BLOOD_DURATION); + + if ((isLeftSonOfHakkarKilled || isRightSonOfHakkarKilled) && isKilledOnTimeForBloodSiphon) + { + sonOfHakkarAssistant.setSonOfHakkarKilledForCurrentBloodSiphon(true); + } + } + + void OnAuraApply(Unit* unit, Aura* aura) noexcept override + { + if (unit == nullptr || aura == nullptr) + { + return; + } + + if (aura->GetSpellInfo()->Id != uint32_t(HakkarTheSoulflayerEnum::SPELL_BLOOD_SIPHON)) + { + return; + } + + if (unit->GetEntry() != uint32_t(HakkarTheSoulflayerEnum::ENTRY)) + { + return; + } + + const uint32_t instanceId = unit->GetInstanceId(); + RaidLeaderRegistry& raidRegistry = RaidLeaderRegistry::GetInstance(); + ZulGurubRaidLeader& raidLeader = raidRegistry.getOrBind(instanceId, unit->GetMapId()); + HakkarTheSoulflayerAssistant& hakkarTheSoulflayerAssistant = raidLeader.getHakkarTheSoulflayerAssistant(); + SonOfHakkarAssistant& sonOfHakkarAssistant = raidLeader.getSonOfHakkarAssistant(); + + sonOfHakkarAssistant.setSonOfHakkarKilledForCurrentBloodSiphon(false); + sonOfHakkarAssistant.setActiveSonOfHakkar(nullptr); + hakkarTheSoulflayerAssistant.restartBloodSiphonWatch(); + } + +}; diff --git a/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/script/HakkarTheSoulflayerUnitScriptPlayer.h b/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/script/HakkarTheSoulflayerUnitScriptPlayer.h new file mode 100644 index 00000000000..a4f757b9951 --- /dev/null +++ b/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/script/HakkarTheSoulflayerUnitScriptPlayer.h @@ -0,0 +1,41 @@ +#pragma once + +#include "Group.h" +#include "Map.h" +#include "HakkarTheSoulflayer/definition/enum/HakkarTheSoulflayerEnum.h" +#include "PlayerbotMgr.h" +#include "Unit.h" +#include "Player.h" +#include "UnitScript.h" +#include "PlayerbotAI.h" + + +class HakkarTheSoulflayerUnitScriptPlayer : public UnitScript +{ +public: + HakkarTheSoulflayerUnitScriptPlayer() : UnitScript("HakkarTheSoulflayerUnitScriptPlayer") {} + + void OnAuraApply(Unit* unit, Aura* aura) noexcept override + { + if (unit == nullptr || aura == nullptr) + { + return; + } + + if (aura->GetSpellInfo()->Id != uint32_t(HakkarTheSoulflayerEnum::SPELL_CORRUPTED_BLOOD)) + { + return; + } + + Player* const player = dynamic_cast(unit); + + if (player == nullptr) + { + return; + } + + PlayerbotAI* const botAI = PlayerbotsMgr::instance().GetPlayerbotAI(player); + + botAI->SayToRaid("I have been infected with the soulflayer's corrupted blood!"); + } +}; diff --git a/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/HolyWrath/HighPriestVenoxisPhase1HolyWrathAction.h b/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/HolyWrath/HighPriestVenoxisPhase1HolyWrathAction.h index 59bdc7af988..46ae72490d6 100644 --- a/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/HolyWrath/HighPriestVenoxisPhase1HolyWrathAction.h +++ b/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/HolyWrath/HighPriestVenoxisPhase1HolyWrathAction.h @@ -5,7 +5,7 @@ #include "MovementActions.h" #include "PlayerbotAI.h" -#include "../../facade/HighPriestVenoxisFacade.h" +#include "raid/leader/RaidLeaderRegistry.h" class HighPriestVenoxisPhase1HolyWrathAction : public MovementAction { @@ -22,7 +22,11 @@ class HighPriestVenoxisPhase1HolyWrathAction : public MovementAction return false; } - Unit* const venoxis = HighPriestVenoxisFacade::FindActiveBoss(*this->bot); + const uint32_t instanceId = this->bot->GetInstanceId(); + RaidLeaderRegistry& raidRegistry = RaidLeaderRegistry::GetInstance(); + const ZulGurubRaidLeader& raidLeader = raidRegistry.getOrBind(instanceId, this->bot->GetMapId()); + const HighPriestVenoxisAssistant& highPriestVenoxisAssistant = raidLeader.getHighPriestVenoxisAssistant(); + Unit* const venoxis = highPriestVenoxisAssistant.findActiveBoss(*this->bot); if (venoxis == nullptr) { diff --git a/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/HolyWrath/HighPriestVenoxisPhase1HolyWrathMultiplier.h b/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/HolyWrath/HighPriestVenoxisPhase1HolyWrathMultiplier.h index c6d100d7944..a7f564b9619 100644 --- a/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/HolyWrath/HighPriestVenoxisPhase1HolyWrathMultiplier.h +++ b/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/HolyWrath/HighPriestVenoxisPhase1HolyWrathMultiplier.h @@ -6,12 +6,11 @@ #include "PlayerbotAI.h" #include "Value.h" -#include "../../facade/HighPriestVenoxisFacade.h" // #include "HighPriestVenoxisPhase1HolyWrathAction.h" // #include "../VenoxisPositioning/HighPriestVenoxisPhase1VenoxisPositioningAction.h" // #include "../RazzashiCobrasPositioning/HighPriestVenoxisPhase1RazzashiCobrasPositioningAction.h" -// #include "../RangedDPSPositioning/HighPriestVenoxisPhase1RangedPositioningAction.h" #include "GenericSpellActions.h" +#include "raid/leader/RaidLeaderRegistry.h" class HighPriestVenoxisPhase1HolyWrathMultiplier : public Multiplier @@ -31,17 +30,22 @@ class HighPriestVenoxisPhase1HolyWrathMultiplier : public Multiplier return 1.0f; } - if (HighPriestVenoxisFacade::IsInPhase1(*this->bot) == false) + const uint32_t instanceId = this->bot->GetInstanceId(); + RaidLeaderRegistry& raidRegistry = RaidLeaderRegistry::GetInstance(); + const ZulGurubRaidLeader& raidLeader = raidRegistry.getOrBind(instanceId, this->bot->GetMapId()); + const HighPriestVenoxisAssistant& highPriestVenoxisAssistant = raidLeader.getHighPriestVenoxisAssistant(); + + if (highPriestVenoxisAssistant.isInPhase1(*this->bot) == false) { return 1.0f; } - if (HighPriestVenoxisFacade::IsAtSafeDistanceFromVenoxis(*this->bot) == true) + if (highPriestVenoxisAssistant.isAtSafeDistanceFromVenoxis(*this->bot) == true) { return 1.0f; } - const std::vector cobras = HighPriestVenoxisFacade::FindRazzashiCobras(*this->bot); + const std::vector cobras = highPriestVenoxisAssistant.findRazzashiCobras(*this->bot); if (!cobras.empty()) { @@ -62,13 +66,6 @@ class HighPriestVenoxisPhase1HolyWrathMultiplier : public Multiplier // return 1.0f; // } - // const HighPriestVenoxisPhase1RangedPositioningAction* const rangedPositioningAction = dynamic_cast(&action); - - // if (rangedPositioningAction != nullptr) - // { - // return 1.0f; - // } - // const HighPriestVenoxisPhase1RazzashiCobrasPositioningAction* const razzashiCobrasPositioningAction = dynamic_cast(&action); // if (razzashiCobrasPositioningAction != nullptr) @@ -76,12 +73,12 @@ class HighPriestVenoxisPhase1HolyWrathMultiplier : public Multiplier // return 1.0f; // } - // const CastSpellAction* const castSpellAction = dynamic_cast(&action); + const CastSpellAction* const castSpellAction = dynamic_cast(&action); - // if (castSpellAction != nullptr) - // { - // return 1.0f; - // } + if (castSpellAction != nullptr) + { + return 1.0f; + } return 0.0f; } diff --git a/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/HolyWrath/HighPriestVenoxisPhase1HolyWrathTrigger.h b/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/HolyWrath/HighPriestVenoxisPhase1HolyWrathTrigger.h index cac3451739e..1be719cd42c 100644 --- a/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/HolyWrath/HighPriestVenoxisPhase1HolyWrathTrigger.h +++ b/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/HolyWrath/HighPriestVenoxisPhase1HolyWrathTrigger.h @@ -4,7 +4,7 @@ #include "PlayerbotAI.h" #include "Trigger.h" -#include "../../facade/HighPriestVenoxisFacade.h" +#include "raid/leader/RaidLeaderRegistry.h" class HighPriestVenoxisPhase1HolyWrathTrigger : public Trigger { @@ -23,7 +23,17 @@ class HighPriestVenoxisPhase1HolyWrathTrigger : public Trigger return false; } - if (HighPriestVenoxisFacade::IsInCombatWithVenoxis(*this->bot) == false) + if (PlayerbotAI::IsTank(this->bot) == true) + { + return false; + } + + const uint32_t instanceId = this->bot->GetInstanceId(); + RaidLeaderRegistry& raidRegistry = RaidLeaderRegistry::GetInstance(); + const ZulGurubRaidLeader& raidLeader = raidRegistry.getOrBind(instanceId, this->bot->GetMapId()); + const HighPriestVenoxisAssistant& highPriestVenoxisAssistant = raidLeader.getHighPriestVenoxisAssistant(); + + if (highPriestVenoxisAssistant.isInCombatWithVenoxis(*this->bot) == false) { return false; } @@ -33,12 +43,12 @@ class HighPriestVenoxisPhase1HolyWrathTrigger : public Trigger return false; } - if (HighPriestVenoxisFacade::IsInPhase1(*this->bot) == false) + if (highPriestVenoxisAssistant.isInPhase1(*this->bot) == false) { return false; } - if (HighPriestVenoxisFacade::IsAtSafeDistanceFromVenoxis(*this->bot) == true) + if (highPriestVenoxisAssistant.isAtSafeDistanceFromVenoxis(*this->bot) == true) { return false; } diff --git a/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/RazzashiCobrasDPSPriority/HighPriestVenoxisPhase1RazzashiCobrasDPSPriorityAction.h b/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/RazzashiCobrasDPSPriority/HighPriestVenoxisPhase1RazzashiCobrasDPSPriorityAction.h index 04377e09c32..7bf1751f567 100644 --- a/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/RazzashiCobrasDPSPriority/HighPriestVenoxisPhase1RazzashiCobrasDPSPriorityAction.h +++ b/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/RazzashiCobrasDPSPriority/HighPriestVenoxisPhase1RazzashiCobrasDPSPriorityAction.h @@ -5,7 +5,7 @@ #include "AttackAction.h" #include "PlayerbotAI.h" -#include "../../facade/HighPriestVenoxisFacade.h" +#include "raid/leader/RaidLeaderRegistry.h" class HighPriestVenoxisPhase1RazzashiCobrasDPSPriorityAction : public AttackAction { @@ -22,7 +22,12 @@ class HighPriestVenoxisPhase1RazzashiCobrasDPSPriorityAction : public AttackActi return false; } - const std::vector razzashiCobras = HighPriestVenoxisFacade::FindRazzashiCobras(*this->bot); + const uint32_t instanceId = this->bot->GetInstanceId(); + RaidLeaderRegistry& raidRegistry = RaidLeaderRegistry::GetInstance(); + const ZulGurubRaidLeader& raidLeader = raidRegistry.getOrBind(instanceId, this->bot->GetMapId()); + const HighPriestVenoxisAssistant& highPriestVenoxisAssistant = raidLeader.getHighPriestVenoxisAssistant(); + + const std::vector razzashiCobras = highPriestVenoxisAssistant.findRazzashiCobras(*this->bot); if (razzashiCobras.empty()) { diff --git a/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/RazzashiCobrasDPSPriority/HighPriestVenoxisPhase1RazzashiCobrasDPSPriorityTrigger.h b/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/RazzashiCobrasDPSPriority/HighPriestVenoxisPhase1RazzashiCobrasDPSPriorityTrigger.h index 840c5b98edc..0eeacf334c3 100644 --- a/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/RazzashiCobrasDPSPriority/HighPriestVenoxisPhase1RazzashiCobrasDPSPriorityTrigger.h +++ b/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/RazzashiCobrasDPSPriority/HighPriestVenoxisPhase1RazzashiCobrasDPSPriorityTrigger.h @@ -4,7 +4,7 @@ #include "PlayerbotAI.h" #include "Trigger.h" -#include "../../facade/HighPriestVenoxisFacade.h" +#include "raid/leader/RaidLeaderRegistry.h" class HighPriestVenoxisPhase1RazzashiCobrasDPSPriorityTrigger : public Trigger { @@ -23,7 +23,12 @@ class HighPriestVenoxisPhase1RazzashiCobrasDPSPriorityTrigger : public Trigger return false; } - if (HighPriestVenoxisFacade::IsInCombatWithVenoxis(*this->bot) == false) + const uint32_t instanceId = this->bot->GetInstanceId(); + RaidLeaderRegistry& raidRegistry = RaidLeaderRegistry::GetInstance(); + const ZulGurubRaidLeader& raidLeader = raidRegistry.getOrBind(instanceId, this->bot->GetMapId()); + const HighPriestVenoxisAssistant& highPriestVenoxisAssistant = raidLeader.getHighPriestVenoxisAssistant(); + + if (highPriestVenoxisAssistant.isInCombatWithVenoxis(*this->bot) == false) { return false; } @@ -33,7 +38,7 @@ class HighPriestVenoxisPhase1RazzashiCobrasDPSPriorityTrigger : public Trigger return false; } - const std::vector razzashiCobras = HighPriestVenoxisFacade::FindRazzashiCobras(*this->bot); + const std::vector razzashiCobras = highPriestVenoxisAssistant.findRazzashiCobras(*this->bot); if (razzashiCobras.empty()) { diff --git a/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/RazzashiCobrasPositioning/HighPriestVenoxisPhase1RazzashiCobrasPositioningAction.h b/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/RazzashiCobrasPositioning/HighPriestVenoxisPhase1RazzashiCobrasPositioningAction.h index 7a9925fa30f..349722711d5 100644 --- a/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/RazzashiCobrasPositioning/HighPriestVenoxisPhase1RazzashiCobrasPositioningAction.h +++ b/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/RazzashiCobrasPositioning/HighPriestVenoxisPhase1RazzashiCobrasPositioningAction.h @@ -5,7 +5,7 @@ #include "AttackAction.h" #include "PlayerbotAI.h" -#include "../../facade/HighPriestVenoxisFacade.h" +#include "raid/leader/RaidLeaderRegistry.h" class HighPriestVenoxisPhase1RazzashiCobrasPositioningAction : public AttackAction { @@ -27,8 +27,12 @@ class HighPriestVenoxisPhase1RazzashiCobrasPositioningAction : public AttackActi return false; } - const std::vector razzashiCobras = HighPriestVenoxisFacade::FindRazzashiCobras(*this->bot); + const uint32_t instanceId = this->bot->GetInstanceId(); + RaidLeaderRegistry& raidRegistry = RaidLeaderRegistry::GetInstance(); + const ZulGurubRaidLeader& raidLeader = raidRegistry.getOrBind(instanceId, this->bot->GetMapId()); + const HighPriestVenoxisAssistant& highPriestVenoxisAssistant = raidLeader.getHighPriestVenoxisAssistant(); + const std::vector razzashiCobras = highPriestVenoxisAssistant.findRazzashiCobras(*this->bot); for (Unit* const cobra : razzashiCobras) { if (cobra == nullptr) @@ -51,8 +55,6 @@ class HighPriestVenoxisPhase1RazzashiCobrasPositioningAction : public AttackActi { this->Attack(cobra); - LOG_ERROR("playerbots", "cobra target is not a player"); - return false; } @@ -62,15 +64,13 @@ class HighPriestVenoxisPhase1RazzashiCobrasPositioningAction : public AttackActi { this->Attack(cobra); - LOG_ERROR("playerbots", "attacking cobra that is not tanked"); - return false; } } - const Position cobrasIdealPosition = HighPriestVenoxisFacade::GetRazzashiCobrasPosition(); + const Position cobrasIdealPosition = highPriestVenoxisAssistant.getRazzashiCobrasPosition(); const float distanceToIdealPosition = this->bot->GetExactDist2d(cobrasIdealPosition.GetPositionX(), cobrasIdealPosition.GetPositionY()); - static constexpr float maxDistance = HighPriestVenoxisFacade::GetRazzashiCobrasMaxPositionDistance(); + const float maxDistance = highPriestVenoxisAssistant.getRazzashiCobrasMaxPositionDistance(); if (distanceToIdealPosition < maxDistance) { @@ -84,7 +84,7 @@ class HighPriestVenoxisPhase1RazzashiCobrasPositioningAction : public AttackActi const float moveX = botPositionX + (dX / distanceToIdealPosition) * maxDistance; const float moveY = botPositionY + (dY / distanceToIdealPosition) * maxDistance; - return MoveTo( + return this->MoveTo( MAP_ZUL_GURUB, moveX, moveY, diff --git a/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/RazzashiCobrasPositioning/HighPriestVenoxisPhase1RazzashiCobrasPositioningTrigger.h b/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/RazzashiCobrasPositioning/HighPriestVenoxisPhase1RazzashiCobrasPositioningTrigger.h index ee6269cc97d..3a98ab6647a 100644 --- a/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/RazzashiCobrasPositioning/HighPriestVenoxisPhase1RazzashiCobrasPositioningTrigger.h +++ b/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/RazzashiCobrasPositioning/HighPriestVenoxisPhase1RazzashiCobrasPositioningTrigger.h @@ -4,7 +4,7 @@ #include "PlayerbotAI.h" #include "Trigger.h" -#include "../../facade/HighPriestVenoxisFacade.h" +#include "raid/leader/RaidLeaderRegistry.h" class HighPriestVenoxisPhase1RazzashiCobrasPositioningTrigger : public Trigger { @@ -33,12 +33,17 @@ class HighPriestVenoxisPhase1RazzashiCobrasPositioningTrigger : public Trigger return false; } - if (HighPriestVenoxisFacade::IsInCombatWithVenoxis(*this->bot) == false) + const uint32_t instanceId = this->bot->GetInstanceId(); + RaidLeaderRegistry& raidRegistry = RaidLeaderRegistry::GetInstance(); + const ZulGurubRaidLeader& raidLeader = raidRegistry.getOrBind(instanceId, this->bot->GetMapId()); + const HighPriestVenoxisAssistant& highPriestVenoxisAssistant = raidLeader.getHighPriestVenoxisAssistant(); + + if (highPriestVenoxisAssistant.isInCombatWithVenoxis(*this->bot) == false) { return false; } - const std::vector razzashiCobras = HighPriestVenoxisFacade::FindRazzashiCobras(*this->bot); + const std::vector razzashiCobras = highPriestVenoxisAssistant.findRazzashiCobras(*this->bot); if (razzashiCobras.empty()) { @@ -57,9 +62,9 @@ class HighPriestVenoxisPhase1RazzashiCobrasPositioningTrigger : public Trigger return false; } - const Position cobrasIdealPosition = HighPriestVenoxisFacade::GetRazzashiCobrasPosition(); + const Position cobrasIdealPosition = highPriestVenoxisAssistant.getRazzashiCobrasPosition(); const float distanceToIdealPosition = this->bot->GetExactDist2d(cobrasIdealPosition.GetPositionX(), cobrasIdealPosition.GetPositionY()); - static constexpr float maxDistance = HighPriestVenoxisFacade::GetRazzashiCobrasMaxPositionDistance(); + const float maxDistance = highPriestVenoxisAssistant.getRazzashiCobrasMaxPositionDistance(); if (distanceToIdealPosition < maxDistance) { diff --git a/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/VenoxisPositioning/HighPriestVenoxisPhase1VenoxisPositioningAction.h b/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/VenoxisPositioning/HighPriestVenoxisPhase1VenoxisPositioningAction.h index 8d6f91c94f5..dfc30027f49 100644 --- a/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/VenoxisPositioning/HighPriestVenoxisPhase1VenoxisPositioningAction.h +++ b/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/VenoxisPositioning/HighPriestVenoxisPhase1VenoxisPositioningAction.h @@ -4,8 +4,7 @@ #include "AiObjectContext.h" #include "AttackAction.h" #include "PlayerbotAI.h" - -#include "../../facade/HighPriestVenoxisFacade.h" +#include "raid/leader/RaidLeaderRegistry.h" class HighPriestVenoxisPhase1VenoxisPositioningAction : public AttackAction { @@ -32,7 +31,12 @@ class HighPriestVenoxisPhase1VenoxisPositioningAction : public AttackAction return false; } - Unit* const boss = HighPriestVenoxisFacade::FindActiveBoss(*this->bot); + const uint32_t instanceId = this->bot->GetInstanceId(); + RaidLeaderRegistry& raidRegistry = RaidLeaderRegistry::GetInstance(); + const ZulGurubRaidLeader& raidLeader = raidRegistry.getOrBind(instanceId, this->bot->GetMapId()); + const HighPriestVenoxisAssistant& highPriestVenoxisAssistant = raidLeader.getHighPriestVenoxisAssistant(); + + Unit* const boss = highPriestVenoxisAssistant.findActiveBoss(*this->bot); if (boss == nullptr) { @@ -53,9 +57,9 @@ class HighPriestVenoxisPhase1VenoxisPositioningAction : public AttackAction return false; } - const Position bossIdealPosition = HighPriestVenoxisFacade::GetVenoxisPosition(); + const Position bossIdealPosition = highPriestVenoxisAssistant.getVenoxisPosition(); const float distanceToIdealPosition = this->bot->GetExactDist2d(bossIdealPosition.GetPositionX(), bossIdealPosition.GetPositionY()); - static constexpr float maxDistance = HighPriestVenoxisFacade::GetVenoxisMaxPositionDistance(); + const float maxDistance = highPriestVenoxisAssistant.getVenoxisMaxPositionDistance(); if (distanceToIdealPosition < maxDistance) { @@ -69,7 +73,7 @@ class HighPriestVenoxisPhase1VenoxisPositioningAction : public AttackAction const float moveX = botPositionX + (dX / distanceToIdealPosition) * maxDistance; const float moveY = botPositionY + (dY / distanceToIdealPosition) * maxDistance; - return MoveTo( + return this->MoveTo( MAP_ZUL_GURUB, moveX, moveY, diff --git a/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/VenoxisPositioning/HighPriestVenoxisPhase1VenoxisPositioningTrigger.h b/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/VenoxisPositioning/HighPriestVenoxisPhase1VenoxisPositioningTrigger.h index 975cc3a5ce7..8141479f845 100644 --- a/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/VenoxisPositioning/HighPriestVenoxisPhase1VenoxisPositioningTrigger.h +++ b/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/VenoxisPositioning/HighPriestVenoxisPhase1VenoxisPositioningTrigger.h @@ -3,8 +3,7 @@ #include "AiObjectContext.h" #include "PlayerbotAI.h" #include "Trigger.h" - -#include "../../facade/HighPriestVenoxisFacade.h" +#include "raid/leader/RaidLeaderRegistry.h" class HighPriestVenoxisPhase1VenoxisPositioningTrigger : public Trigger { @@ -28,26 +27,31 @@ class HighPriestVenoxisPhase1VenoxisPositioningTrigger : public Trigger return false; } - if (HighPriestVenoxisFacade::IsInCombatWithVenoxis(*this->bot) == false) + const uint32_t instanceId = this->bot->GetInstanceId(); + RaidLeaderRegistry& raidRegistry = RaidLeaderRegistry::GetInstance(); + const ZulGurubRaidLeader& raidLeader = raidRegistry.getOrBind(instanceId, this->bot->GetMapId()); + const HighPriestVenoxisAssistant& highPriestVenoxisAssistant = raidLeader.getHighPriestVenoxisAssistant(); + + if (highPriestVenoxisAssistant.isInCombatWithVenoxis(*this->bot) == false) { return false; } - if (HighPriestVenoxisFacade::IsInPhase1(*this->bot) == false) + if (highPriestVenoxisAssistant.isInPhase1(*this->bot) == false) { return false; } - const Unit* const boss = HighPriestVenoxisFacade::FindActiveBoss(*this->bot); + const Unit* const boss = highPriestVenoxisAssistant.findActiveBoss(*this->bot); if (boss == nullptr) { return false; } - const Position bossIdealPosition = HighPriestVenoxisFacade::GetVenoxisPosition(); + const Position bossIdealPosition = highPriestVenoxisAssistant.getVenoxisPosition(); const float distanceToIdealPosition = this->bot->GetExactDist2d(bossIdealPosition.GetPositionX(), bossIdealPosition.GetPositionY()); - static constexpr float maxDistance = HighPriestVenoxisFacade::GetVenoxisMaxPositionDistance(); + const float maxDistance = highPriestVenoxisAssistant.getVenoxisMaxPositionDistance(); if (distanceToIdealPosition < maxDistance) { diff --git a/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/facade/HighPriestVenoxisFacade.h b/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/assistant/HighPriestVenoxisAssistant.h similarity index 57% rename from src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/facade/HighPriestVenoxisFacade.h rename to src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/assistant/HighPriestVenoxisAssistant.h index 617f693668c..4ed1ca9bcaa 100644 --- a/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/facade/HighPriestVenoxisFacade.h +++ b/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/assistant/HighPriestVenoxisAssistant.h @@ -17,13 +17,21 @@ #include "../definition/enum/HighPriestVenoxisEnum.h" #include "../definition/enum/RazzashiCobraEnum.h" -#include "../../../facade/ZulGurubFacade.h" -class HighPriestVenoxisFacade +class HighPriestVenoxisAssistant { public: - [[nodiscard]] static Position GetVenoxisPosition() noexcept + HighPriestVenoxisAssistant() = default; + ~HighPriestVenoxisAssistant() = default; + + HighPriestVenoxisAssistant(const HighPriestVenoxisAssistant&) = default; + HighPriestVenoxisAssistant& operator=(const HighPriestVenoxisAssistant&) = default; + + HighPriestVenoxisAssistant(HighPriestVenoxisAssistant&&) = default; + HighPriestVenoxisAssistant& operator=(HighPriestVenoxisAssistant&&) = default; + + [[nodiscard]] const Position getVenoxisPosition() const noexcept { return Position{ -12026.866f, @@ -32,40 +40,24 @@ class HighPriestVenoxisFacade }; } - [[nodiscard]] static constexpr float GetVenoxisMaxPositionDistance() noexcept + [[nodiscard]] constexpr float getVenoxisMaxPositionDistance() const noexcept { return 4.0f; } - [[nodiscard]] static bool IsInCombatWithVenoxis(Player& bot) noexcept + [[nodiscard]] bool isInCombatWithVenoxis(Player& bot) const noexcept { - const Unit* const venoxis = HighPriestVenoxisFacade::FindActiveBoss(bot); + const Unit* const venoxis = this->findActiveBoss(bot); if (venoxis == nullptr) { return false; } - // This method seems to not be working properly. - // return bot.IsInCombatWith(venoxis); - return bot.IsInCombat() && venoxis->IsInCombat(); - } - - [[nodiscard]] static Position GetRangedPosition() noexcept - { - return Position{ - -11985.107f, - -1672.6897f, - 32.31f, - }; - } - - [[nodiscard]] static constexpr float GetRangedMaxPositionDistance() noexcept - { - return 4.0f; + return bot.IsInCombatWith(venoxis) || venoxis->IsInCombatWith(&bot); } - [[nodiscard]] static Position GetRazzashiCobrasPosition() noexcept + [[nodiscard]] const Position getRazzashiCobrasPosition() const noexcept { return Position{ -11991.636f, @@ -74,18 +66,13 @@ class HighPriestVenoxisFacade }; } - [[nodiscard]] static constexpr float GetRazzashiCobrasMaxPositionDistance() noexcept + [[nodiscard]] constexpr float getRazzashiCobrasMaxPositionDistance() const noexcept { return 4.0f; } - [[nodiscard]] static Unit* FindActiveBoss(Player& bot) noexcept + [[nodiscard]] Unit* findActiveBoss(Player& bot) const noexcept { - if (ZulGurubFacade::IsInInstance(bot) == false) - { - return nullptr; - } - if (!bot.IsInCombat()) { return nullptr; @@ -94,13 +81,8 @@ class HighPriestVenoxisFacade return bot.FindNearestCreature(uint32_t(HighPriestVenoxisEnum::ENTRY), PlayerbotAIConfig::instance().sightDistance, true); }; - [[nodiscard]] static std::vector FindRazzashiCobras(Player& bot) noexcept + [[nodiscard]] std::vector findRazzashiCobras(Player& bot) const noexcept { - if (ZulGurubFacade::IsInInstance(bot) == false) - { - return {}; - } - if (!bot.IsInCombat()) { return {}; @@ -141,9 +123,9 @@ class HighPriestVenoxisFacade return results; }; - [[nodiscard]] static bool IsAtSafeDistanceFromVenoxis(Player& bot) noexcept + [[nodiscard]] bool isAtSafeDistanceFromVenoxis(Player& bot) const noexcept { - const Unit* const venoxis = FindActiveBoss(bot); + const Unit* const venoxis = this->findActiveBoss(bot); if (venoxis == nullptr) { @@ -155,9 +137,9 @@ class HighPriestVenoxisFacade return distanceToVenoxis > float(HighPriestVenoxisEnum::PHASE_1_SAFE_DISTANCE); }; - [[nodiscard]] static bool IsInPhase1(Player& bot) noexcept + [[nodiscard]] bool isInPhase1(Player& bot) const noexcept { - const Unit* const unit = HighPriestVenoxisFacade::FindActiveBoss(bot); + const Unit* const unit = this->findActiveBoss(bot); if (unit == nullptr) { @@ -176,14 +158,4 @@ class HighPriestVenoxisFacade return unit->GetHealthPct() > float(HighPriestVenoxisEnum::PHASE_2_THRESHOLD); }; - -private: - HighPriestVenoxisFacade() = delete; - ~HighPriestVenoxisFacade() = delete; - - HighPriestVenoxisFacade(const HighPriestVenoxisFacade&) = delete; - HighPriestVenoxisFacade& operator=(const HighPriestVenoxisFacade&) = delete; - - HighPriestVenoxisFacade(HighPriestVenoxisFacade&&) = delete; - HighPriestVenoxisFacade& operator=(HighPriestVenoxisFacade&&) = delete; }; diff --git a/src/Ai/Raid/ZulGurub/RaidZGStrategy.h b/src/Ai/Raid/ZulGurub/RaidZGStrategy.h index 0c359c86ed1..9d9b7219bc2 100644 --- a/src/Ai/Raid/ZulGurub/RaidZGStrategy.h +++ b/src/Ai/Raid/ZulGurub/RaidZGStrategy.h @@ -6,6 +6,15 @@ #include "Boss/HighPriestVenoxis/Phase1/RazzashiCobrasPositioning/HighPriestVenoxisPhase1RazzashiCobrasPositioningAction.h" #include "Boss/HighPriestVenoxis/Phase1/VenoxisPositioning/HighPriestVenoxisPhase1VenoxisPositioningAction.h" #include "CreateNextAction.h" +#include "HakkarTheSoulflayer/mechanics/son-of-hakkar/BringBackSonOfHakkar/HakkarTheSoulflayerBringBackSonOfHakkarAction.h" +#include "HakkarTheSoulflayer/mechanics/cause-insanity/CauseInsanity/HakkarTheSoulflayerCauseInsanityAction.h" +#include "HakkarTheSoulflayer/mechanics/threat/ExcessiveThreat/HakkarTheSoulflayerExcessiveThreatAction.h" +#include "HakkarTheSoulflayer/mechanics/blood-siphon/GoToPoisonousCloud/HakkarTheSoulflayerGoToPoisonousCloudAction.h" +#include "HakkarTheSoulflayer/mechanics/blood-siphon/GoToPoisonousCloud/HakkarTheSoulflayerGoToPoisonousCloudMultiplier.h" +#include "HakkarTheSoulflayer/mechanics/positioning/HakkarPositioning/HakkarTheSoulflayerHakkarPositioningAction.h" +#include "HakkarTheSoulflayer/mechanics/son-of-hakkar/MoveToSonOfHakkar/HakkarTheSoulflayerMoveToSonOfHakkarAction.h" +#include "HakkarTheSoulflayer/mechanics/blood-siphon/PoisonousBlood/HakkarTheSoulflayerPoisonousBloodMultiplier.h" +#include "HakkarTheSoulflayer/mechanics/son-of-hakkar/PullSonOfHakkar/HakkarTheSoulflayerPullSonOfHakkarAction.h" #include "Strategy.h" #include "Multiplier.h" #include "Trash/GurubashiBatRider/GurubashiBatRiderUnstableConcoctionAction.h" @@ -68,11 +77,77 @@ class RaidZGStrategy : public Strategy } ) ); + + // Hakkar the Soulflayer + triggers.push_back( + new TriggerNode( + "hakkar the soulflayer hakkar positioning", + { + CreateNextAction(ACTION_EMERGENCY + 10.0f) + } + ) + ); + triggers.push_back( + new TriggerNode( + "hakkar the soulflayer cause insanity", + { + CreateNextAction(ACTION_EMERGENCY + 9.0f) + } + ) + ); + triggers.push_back( + new TriggerNode( + "hakkar the soulflayer excessive threat", + { + CreateNextAction(ACTION_EMERGENCY + 1.0f) + } + ) + ); + triggers.push_back( + new TriggerNode( + "hakkar the soulflayer go to poisonous cloud", + { + CreateNextAction(ACTION_EMERGENCY + 10.0f) + } + ) + ); + triggers.push_back( + new TriggerNode( + "hakkar the soulflayer move to son of hakkar", + { + CreateNextAction(ACTION_EMERGENCY + 9.0f) + } + ) + ); + triggers.push_back( + new TriggerNode( + "hakkar the soulflayer pull son of hakkar", + { + CreateNextAction(ACTION_EMERGENCY + 10.0f) + } + ) + ); + triggers.push_back( + new TriggerNode( + "hakkar the soulflayer bring back son of hakkar", + { + CreateNextAction(ACTION_EMERGENCY + 11.0f) + } + ) + ); } void InitMultipliers(std::vector& multipliers) override { + // Trash mobs multipliers.push_back(new GurubashiBatRiderUnstableConcoctionMultiplier(this->botAI)); + + // High Priest Venoxis multipliers.push_back(new HighPriestVenoxisPhase1HolyWrathMultiplier(this->botAI)); + + // Hakkar the Soulflayer + multipliers.push_back(new HakkarTheSoulflayerPoisonousBloodMultiplier(this->botAI)); + multipliers.push_back(new HakkarTheSoulflayerGoToPoisonousCloudMultiplier(this->botAI)); + // multipliers.push_back(new HakkarTheSoulflayerThreatManagementMultiplier(this->botAI)); } }; diff --git a/src/Ai/Raid/ZulGurub/RaidZGTriggerContext.h b/src/Ai/Raid/ZulGurub/RaidZGTriggerContext.h index 91062faf4ec..f0bfe342e81 100644 --- a/src/Ai/Raid/ZulGurub/RaidZGTriggerContext.h +++ b/src/Ai/Raid/ZulGurub/RaidZGTriggerContext.h @@ -1,6 +1,13 @@ #pragma once #include "AiObjectContext.h" +#include "HakkarTheSoulflayer/mechanics/son-of-hakkar/BringBackSonOfHakkar/HakkarTheSoulflayerBringBackSonOfHakkarTrigger.h" +#include "HakkarTheSoulflayer/mechanics/blood-siphon/GoToPoisonousCloud/HakkarTheSoulflayerGoToPoisonousCloudTrigger.h" +#include "HakkarTheSoulflayer/mechanics/cause-insanity/CauseInsanity/HakkarTheSoulflayerCauseInsanityTrigger.h" +#include "HakkarTheSoulflayer/mechanics/threat/ExcessiveThreat/HakkarTheSoulflayerExcessiveThreatTrigger.h" +#include "HakkarTheSoulflayer/mechanics/positioning/HakkarPositioning/HakkarTheSoulflayerHakkarPositioningTrigger.h" +#include "HakkarTheSoulflayer/mechanics/son-of-hakkar/MoveToSonOfHakkar/HakkarTheSoulflayerMoveToSonOfHakkarTrigger.h" +#include "HakkarTheSoulflayer/mechanics/son-of-hakkar/PullSonOfHakkar/HakkarTheSoulflayerPullSonOfHakkarTrigger.h" #include "HighPriestVenoxisPhase1HolyWrathTrigger.h" #include "Trash/GurubashiBatRider/GurubashiBatRiderUnstableConcoctionTrigger.h" #include "Boss/HighPriestVenoxis/Phase1/VenoxisPositioning/HighPriestVenoxisPhase1VenoxisPositioningTrigger.h" @@ -14,10 +21,21 @@ class RaidZGTriggerContext : public NamedObjectContext { // Trash creators["gurubashi bat rider unstable concoction"] = &RaidZGTriggerContext::gurubashiBatRiderUnstableConcoction; + + // High Priest Venoxis creators["high priest venoxis phase 1 holy wrath"] = &RaidZGTriggerContext::highPriestVenoxisPhase1HolyWrath; creators["high priest venoxis phase 1 venoxis positioning"] = &RaidZGTriggerContext::highPriestVenoxisPhase1VenoxisPositioning; creators["high priest venoxis phase 1 razzashi cobras positioning"] = &RaidZGTriggerContext::highPriestVenoxisPhase1RazzashiCobrasPositioning; creators["high priest venoxis phase 1 razzashi cobras dps priority"] = &RaidZGTriggerContext::highPriestVenoxisPhase1RazzashiCobrasDPSPriority; + + // Hakkar the Soulflayer + creators["hakkar the soulflayer go to poisonous cloud"] = &RaidZGTriggerContext::hakkarTheSoulflayerGoToPoisonousCloud; + creators["hakkar the soulflayer cause insanity"] = &RaidZGTriggerContext::hakkarTheSoulflayerCauseInsanity; + creators["hakkar the soulflayer excessive threat"] = &RaidZGTriggerContext::hakkarTheSoulflayerExcessiveThreat; + creators["hakkar the soulflayer pull son of hakkar"] = &RaidZGTriggerContext::hakkarTheSoulflayerPullSonOfHakkar; + creators["hakkar the soulflayer bring back son of hakkar"] = &RaidZGTriggerContext::hakkarTheSoulflayerBringBackSonOfHakkar; + creators["hakkar the soulflayer hakkar positioning"] = &RaidZGTriggerContext::hakkarTheSoulflayerHakkarPositioning; + creators["hakkar the soulflayer move to son of hakkar"] = &RaidZGTriggerContext::hakkarTheSoulflayerMoveToSonOfHakkar; } private: @@ -47,4 +65,40 @@ class RaidZGTriggerContext : public NamedObjectContext { return new HighPriestVenoxisPhase1RazzashiCobrasDPSPriorityTrigger(botAI); } -}; + + // Hakkar the Soulflayer + static Trigger* hakkarTheSoulflayerGoToPoisonousCloud(PlayerbotAI* botAI) + { + return new HakkarTheSoulflayerGoToPoisonousCloudTrigger(botAI); + } + + static Trigger* hakkarTheSoulflayerCauseInsanity(PlayerbotAI* botAI) + { + return new HakkarTheSoulflayerCauseInsanityTrigger(botAI); + } + + static Trigger* hakkarTheSoulflayerExcessiveThreat(PlayerbotAI* botAI) + { + return new HakkarTheSoulflayerExcessiveThreatTrigger(botAI); + } + + static Trigger* hakkarTheSoulflayerPullSonOfHakkar(PlayerbotAI* botAI) + { + return new HakkarTheSoulflayerPullSonOfHakkarTrigger(botAI); + } + + static Trigger* hakkarTheSoulflayerBringBackSonOfHakkar(PlayerbotAI* botAI) + { + return new HakkarTheSoulflayerBringBackSonOfHakkarTrigger(botAI); + } + + static Trigger* hakkarTheSoulflayerHakkarPositioning(PlayerbotAI* botAI) + { + return new HakkarTheSoulflayerHakkarPositioningTrigger(botAI); + } + + static Trigger* hakkarTheSoulflayerMoveToSonOfHakkar(PlayerbotAI* botAI) + { + return new HakkarTheSoulflayerMoveToSonOfHakkarTrigger(botAI); + } +}; \ No newline at end of file diff --git a/src/Ai/Raid/ZulGurub/facade/ZulGurubFacade.h b/src/Ai/Raid/ZulGurub/facade/ZulGurubFacade.h deleted file mode 100644 index d5858656703..00000000000 --- a/src/Ai/Raid/ZulGurub/facade/ZulGurubFacade.h +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -#include "Player.h" - -class ZulGurubFacade -{ -public: - - [[nodiscard]] static bool IsInInstance(Player& player) noexcept - { - return player.GetMapId() == MAP_ZUL_GURUB; - } - -private: - ZulGurubFacade() = delete; - ~ZulGurubFacade() = delete; - - ZulGurubFacade(ZulGurubFacade const&) = delete; - ZulGurubFacade& operator=(ZulGurubFacade const&) = delete; - - ZulGurubFacade(ZulGurubFacade&&) = delete; - ZulGurubFacade& operator=(ZulGurubFacade&&) = delete; -}; diff --git a/src/Ai/Raid/ZulGurub/leader/ZulGurubRaidLeader.h b/src/Ai/Raid/ZulGurub/leader/ZulGurubRaidLeader.h new file mode 100644 index 00000000000..1e0e5c44796 --- /dev/null +++ b/src/Ai/Raid/ZulGurub/leader/ZulGurubRaidLeader.h @@ -0,0 +1,49 @@ +#pragma once + +#include "../../../../domain/core/raid/leader/BaseRaidLeader.h" + +#include "Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/assistant/HighPriestVenoxisAssistant.h" +#include "Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/assistant/hakkar-the-soulflayer/HakkarTheSoulflayerAssistant.h" +#include "Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/assistant/son-of-hakkar/SonOfHakkarAssistant.h" + +class ZulGurubRaidLeader : public BaseRaidLeader +{ +private: + HighPriestVenoxisAssistant highPriestVenoxisAssistant{}; + HakkarTheSoulflayerAssistant hakkarTheSoulflayerAssistant{}; + SonOfHakkarAssistant sonOfHakkarAssistant{}; + +public: + ZulGurubRaidLeader() = default; + ~ZulGurubRaidLeader() = default; + + [[nodiscard]] const HighPriestVenoxisAssistant& getHighPriestVenoxisAssistant() const noexcept + { + return this->highPriestVenoxisAssistant; + } + + [[nodiscard]] HakkarTheSoulflayerAssistant& getHakkarTheSoulflayerAssistant() noexcept + { + return this->hakkarTheSoulflayerAssistant; + } + + [[nodiscard]] const HakkarTheSoulflayerAssistant& getHakkarTheSoulflayerAssistant() const noexcept + { + return this->hakkarTheSoulflayerAssistant; + } + + [[nodiscard]] SonOfHakkarAssistant& getSonOfHakkarAssistant() noexcept + { + return this->sonOfHakkarAssistant; + } + + [[nodiscard]] const SonOfHakkarAssistant& getSonOfHakkarAssistant() const noexcept + { + return this->sonOfHakkarAssistant; + } + + [[nodiscard]] bool isInInstance(Player& player) const noexcept + { + return player.GetMapId() == MAP_ZUL_GURUB; + } +}; diff --git a/src/Ai/Raid/ZulGurub/script/ZulGurubInstanceScript.h b/src/Ai/Raid/ZulGurub/script/ZulGurubInstanceScript.h new file mode 100644 index 00000000000..92f790ac9be --- /dev/null +++ b/src/Ai/Raid/ZulGurub/script/ZulGurubInstanceScript.h @@ -0,0 +1,36 @@ +#pragma once + +#include "AreaDefines.h" +#include "InstanceMapScript.h" + +#include "domain/core/raid/leader/RaidLeaderRegistry.h" + +class ZulGurubInstanceScript : public InstanceMapScript +{ +public: + ZulGurubInstanceScript() : InstanceMapScript("ZulGurubInstanceScript", MAP_ZUL_GURUB) {} + + void OnCreate(InstanceMap* map) override + { + if (map == nullptr) + { + return; + } + + RaidLeaderRegistry& raidLeaderRegistry = RaidLeaderRegistry::GetInstance(); + + raidLeaderRegistry.bind(map->GetInstanceId(), map->GetId()); + } + + void OnDestroy(InstanceMap* map) override + { + if (map == nullptr) + { + return; + } + + RaidLeaderRegistry& raidLeaderRegistry = RaidLeaderRegistry::GetInstance(); + + raidLeaderRegistry.erase(map->GetInstanceId()); + } +}; diff --git a/src/Script/Playerbots.cpp b/src/Script/Playerbots.cpp index 6326eabade5..d7fc9259d6f 100644 --- a/src/Script/Playerbots.cpp +++ b/src/Script/Playerbots.cpp @@ -23,6 +23,9 @@ #include "DatabaseEnv.h" #include "DatabaseLoader.h" #include "GuildTaskMgr.h" +#include "HakkarTheSoulflayer/script/HakkarTheSoulflayerScript.h" +#include "HakkarTheSoulflayer/script/HakkarTheSoulflayerPlayerScript.h" +#include "HakkarTheSoulflayerUnitScriptPlayer.h" #include "PlayerScript.h" #include "PlayerbotAIConfig.h" #include "PlayerbotGuildMgr.h" @@ -31,6 +34,7 @@ #include "RandomPlayerbotMgr.h" #include "ScriptMgr.h" #include "PlayerbotCommandScript.h" +#include "ZulGurubInstanceScript.h" #include "cmath" #include "BattleGroundTactics.h" @@ -531,4 +535,8 @@ void AddPlayerbotsScripts() AddPlayerbotsCommandscripts(); PlayerBotsGuildValidationScript(); AddSC_TempestKeepBotScripts(); + new ZulGurubInstanceScript(); + new HakkarTheSoulflayerScript(); + new HakkarTheSoulflayerPlayerScript(); + new HakkarTheSoulflayerUnitScriptPlayer(); } diff --git a/src/domain/core/raid/assistant/tank-assistant/RaidTankAssistant.h b/src/domain/core/raid/assistant/tank-assistant/RaidTankAssistant.h new file mode 100644 index 00000000000..209786fa911 --- /dev/null +++ b/src/domain/core/raid/assistant/tank-assistant/RaidTankAssistant.h @@ -0,0 +1,105 @@ +#pragma once + +#include "Group.h" +#include "PlayerbotAI.h" +#include "Player.h" +#include "Spell.h" + +#include "PlayerbotMgr.h" +#include "./memory/RaidTankAssistantMemory.h" + +class RaidTankAssistant +{ +private: + RaidTankAssistantMemory memory{}; + +public: + RaidTankAssistant() = default; + ~RaidTankAssistant() = default; + + RaidTankAssistant(const RaidTankAssistant&) = default; + RaidTankAssistant& operator=(const RaidTankAssistant&) = default; + + RaidTankAssistant(RaidTankAssistant&&) = default; + RaidTankAssistant& operator=(RaidTankAssistant&&) = default; + + void initialiseTanks(Player& player) noexcept + { + this->memory.clear(); + + const std::vector tanks = this->getTanksByItemLevel(player); + + for (Player* tank : tanks) + { + this->memory.add(tank); + } + } + + [[nodiscard]] const RaidTankAssistantMemory& getMemory() const noexcept + { + return this->memory; + } + +protected: + [[nodiscard]] const std::vector getTanksByItemLevel(Player& player) const noexcept + { + const Group* const group = player.GetGroup(); + + if (group == nullptr) + { + return { &player }; + } + + const GroupReference* member = group->GetFirstMember(); + + if (member == nullptr) + { + return { &player }; + } + + std::vector tanks{}; + + while (member != nullptr) + { + Player* const player = member->GetSource(); + + if (player == nullptr) + { + member = member->next(); + + continue; + } + + if (PlayerbotAI::IsTank(player) == true) + { + tanks.emplace_back(player); + + member = member->next(); + + continue; + } + + member = member->next(); + } + + std::sort(tanks.begin(), tanks.end(), [](Player* a, Player* b) { + if (a == nullptr) + { + return false; + } + + if (b == nullptr) + { + return true; + } + + const uint32_t aItemLevel = a->GetAverageItemLevel(); + const uint32_t bItemLevel = b->GetAverageItemLevel(); + + return aItemLevel > bItemLevel; + }); + + return tanks; + } + +}; diff --git a/src/domain/core/raid/assistant/tank-assistant/memory/RaidTankAssistantMemory.h b/src/domain/core/raid/assistant/tank-assistant/memory/RaidTankAssistantMemory.h new file mode 100644 index 00000000000..38beaa597f5 --- /dev/null +++ b/src/domain/core/raid/assistant/tank-assistant/memory/RaidTankAssistantMemory.h @@ -0,0 +1,69 @@ +#pragma once + +#include + +#include "Player.h" + +class RaidTankAssistantMemory +{ +private: + std::set tanks{}; + +public: + RaidTankAssistantMemory() = default; + ~RaidTankAssistantMemory() = default; + + void clear() noexcept + { + this->tanks.clear(); + } + + void add(Player* player) noexcept + { + if (player == nullptr) + { + return; + } + + this->tanks.insert(player); + } + + void remove(Player* player) noexcept + { + if (player == nullptr) + { + return; + } + + this->tanks.erase(player); + } + + [[nodiscard]] const std::set& getTanks() const noexcept + { + return this->tanks; + } + + [[nodiscard]] Player* getMainTank() const noexcept + { + if (this->tanks.empty()) + { + return nullptr; + } + + return *this->tanks.begin(); + } + + [[nodiscard]] Player* getSecondTank() const noexcept + { + if (this->tanks.size() < 2) + { + return nullptr; + } + + std::set::iterator it = this->tanks.begin(); + + ++it; + + return *it; + } +}; diff --git a/src/domain/core/raid/leader/BaseRaidLeader.h b/src/domain/core/raid/leader/BaseRaidLeader.h new file mode 100644 index 00000000000..d7fe04f6c19 --- /dev/null +++ b/src/domain/core/raid/leader/BaseRaidLeader.h @@ -0,0 +1,45 @@ +#pragma once + +#include "./memory/RaidMemory.h" +#include "Group.h" +#include "domain/core/raid/assistant/tank-assistant/RaidTankAssistant.h" + +class BaseRaidLeader +{ +protected: + Group* group{nullptr}; + + RaidMemory memory{}; + + RaidTankAssistant raidTankAssistant{}; + +public: + BaseRaidLeader() = default; + virtual ~BaseRaidLeader() = default; + + BaseRaidLeader(const BaseRaidLeader& other) = default; + BaseRaidLeader(BaseRaidLeader&& other) noexcept = default; + + BaseRaidLeader& operator=(const BaseRaidLeader& other) = default; + BaseRaidLeader& operator=(BaseRaidLeader&& other) noexcept = default; + + void setGroup(Group* group) noexcept + { + this->group = group; + } + + [[nodiscard]] Group* getGroup() const noexcept + { + return this->group; + } + + [[nodiscard]] const RaidMemory& getMemory() const noexcept + { + return this->memory; + } + + [[nodiscard]] RaidTankAssistant& getRaidTankAssistant() noexcept + { + return this->raidTankAssistant; + } +}; diff --git a/src/domain/core/raid/leader/RaidLeaderRegistry.h b/src/domain/core/raid/leader/RaidLeaderRegistry.h new file mode 100644 index 00000000000..dc5abbdfe38 --- /dev/null +++ b/src/domain/core/raid/leader/RaidLeaderRegistry.h @@ -0,0 +1,147 @@ +#pragma once + +#include + +#include "AreaDefines.h" + +#include "../leader/BaseRaidLeader.h" +#include "Ai/Raid/ZulGurub/leader/ZulGurubRaidLeader.h" + +class RaidLeaderRegistry +{ +private: + using RaidLeaderVariant = std::variant< + BaseRaidLeader, + ZulGurubRaidLeader + >; + + using RaidLeaderRecord = std::pair; + + std::unordered_map entries{}; + mutable std::mutex entriesLock{}; + +public: + [[nodiscard]] static RaidLeaderRegistry& GetInstance() noexcept + { + static RaidLeaderRegistry instance{}; + + return instance; + } + + void bind(const uint32_t instanceId, const uint32_t mapId) + { + const std::scoped_lock lock{this->entriesLock}; + + const std::pair< + typename std::unordered_map::iterator, + bool + > insertedEntry = + this->entries.try_emplace( + instanceId, + RaidLeaderRecord{mapId, RaidLeaderRegistry::CreateLeaderForMapId(mapId)} + ); + + if (insertedEntry.second == true) + { + return; + } + + const uint32_t existingMapId = insertedEntry.first->second.first; + + if (existingMapId != mapId) + { + throw std::logic_error("RaidRegistry: instanceId/mapId mismatch"); + } + } + + void erase(const uint32_t instanceId) noexcept + { + const std::scoped_lock lock{this->entriesLock}; + + (void) this->entries.erase(instanceId); + } + + template + [[nodiscard]] TRaidLeader& get(const uint32_t instanceId) + { + const std::scoped_lock lock{this->entriesLock}; + + typename std::unordered_map::iterator it = this->entries.find(instanceId); + + if (it == this->entries.end()) + { + throw std::out_of_range("RaidRegistry: instanceId not bound"); + } + + RaidLeaderVariant& v = it->second.second; + + TRaidLeader* const Leader = std::get_if(&v); + + if (Leader == nullptr) + { + throw std::bad_cast{}; + } + + return *Leader; + } + + template + [[nodiscard]] TRaidLeader& getOrBind(const uint32_t instanceId, const uint32_t mapId) + { + const std::scoped_lock lock{this->entriesLock}; + + typename std::unordered_map::iterator it = + this->entries.find(instanceId); + + if (it == this->entries.end()) + { + const std::pair< + typename std::unordered_map::iterator, + bool + > inserted = + this->entries.try_emplace( + instanceId, + RaidLeaderRecord{mapId, RaidLeaderRegistry::CreateLeaderForMapId(mapId)} + ); + + it = inserted.first; + } + + const uint32_t existingMapId = it->second.first; + + if (existingMapId != mapId) + { + throw std::logic_error("RaidLeaderRegistry: instanceId/mapId mismatch"); + } + + RaidLeaderVariant& v = it->second.second; + + TRaidLeader* const leader = std::get_if(&v); + + if (leader == nullptr) + { + throw std::bad_cast{}; + } + + return *leader; + } +private: + [[nodiscard]] static RaidLeaderVariant CreateLeaderForMapId(const uint32_t mapId) + { + if (mapId == MAP_ZUL_GURUB) + { + return RaidLeaderVariant{std::in_place_type}; + } + + return RaidLeaderVariant{std::in_place_type}; + } + + RaidLeaderRegistry() = default; + ~RaidLeaderRegistry() = default; + + RaidLeaderRegistry(const RaidLeaderRegistry&) = delete; + RaidLeaderRegistry& operator=(const RaidLeaderRegistry&) = delete; + + RaidLeaderRegistry(RaidLeaderRegistry&&) = delete; + RaidLeaderRegistry& operator=(RaidLeaderRegistry&&) = delete; +}; diff --git a/src/domain/core/raid/leader/definition/struct/RaidLeaderContextStruct.h b/src/domain/core/raid/leader/definition/struct/RaidLeaderContextStruct.h new file mode 100644 index 00000000000..05ca478daa7 --- /dev/null +++ b/src/domain/core/raid/leader/definition/struct/RaidLeaderContextStruct.h @@ -0,0 +1,8 @@ +#pragma once + +#include "domain/core/raid/assistant/tank-assistant/RaidTankAssistant.h" + +struct RaidLeaderContextStruct +{ + RaidTankAssistant& raidTankAssistant; +}; diff --git a/src/domain/core/raid/leader/memory/RaidMemory.h b/src/domain/core/raid/leader/memory/RaidMemory.h new file mode 100644 index 00000000000..ef610eb1e08 --- /dev/null +++ b/src/domain/core/raid/leader/memory/RaidMemory.h @@ -0,0 +1,6 @@ +#pragma once + +class RaidMemory +{ + +}; diff --git a/src/domain/core/utility/StopWatch.h b/src/domain/core/utility/StopWatch.h new file mode 100644 index 00000000000..5ba37040471 --- /dev/null +++ b/src/domain/core/utility/StopWatch.h @@ -0,0 +1,37 @@ +#pragma once + +#include + +class Stopwatch final +{ +private: + bool hasStarted; + std::chrono::steady_clock::time_point startPoint; +public: + Stopwatch() noexcept : hasStarted(false), startPoint() {} + ~Stopwatch() = default; + + void start() noexcept + { + this->startPoint = std::chrono::steady_clock::now(); + this->hasStarted = true; + } + + void reset() noexcept + { + this->startPoint = std::chrono::steady_clock::now(); + this->hasStarted = false; + } + + [[nodiscard]] double getElapsedSeconds() const noexcept + { + if (this->hasStarted == false) + { + return 0.0f; + } + + const std::chrono::duration elapsed = std::chrono::steady_clock::now() - this->startPoint; + + return elapsed.count(); + } +}; From bc298643310b459c6a17f202a8ed5db99858e9cc Mon Sep 17 00:00:00 2001 From: SmashingQuasar Date: Thu, 26 Feb 2026 15:28:44 +0100 Subject: [PATCH 4/8] feat: Added Gri'lek The Wanderer strategy. --- .../assistant/GrilekTheWandererAssistant.h | 98 +++++++++++++++++++ .../definition/enum/GrilekTheWandererEnum.h | 10 ++ .../Avatar/GrilekTheWandererAvatarAction.h | 47 +++++++++ .../GrilekTheWandererAvatarMultiplier.h | 61 ++++++++++++ .../Avatar/GrilekTheWandererAvatarTrigger.h | 56 +++++++++++ ...karTheSoulflayerGoToPoisonousCloudAction.h | 2 +- ...heSoulflayerGoToPoisonousCloudMultiplier.h | 2 +- ...arTheSoulflayerGoToPoisonousCloudTrigger.h | 2 +- ...karTheSoulflayerPoisonousBloodMultiplier.h | 2 +- .../HakkarTheSoulflayerCauseInsanityAction.h | 2 +- .../HakkarTheSoulflayerCauseInsanityTrigger.h | 2 +- ...kkarTheSoulflayerHakkarPositioningAction.h | 2 +- ...karTheSoulflayerHakkarPositioningTrigger.h | 2 +- ...rTheSoulflayerBringBackSonOfHakkarAction.h | 2 +- ...TheSoulflayerBringBackSonOfHakkarTrigger.h | 2 +- ...kkarTheSoulflayerMoveToSonOfHakkarAction.h | 2 +- ...karTheSoulflayerMoveToSonOfHakkarTrigger.h | 2 +- ...HakkarTheSoulflayerPullSonOfHakkarAction.h | 2 +- ...akkarTheSoulflayerPullSonOfHakkarTrigger.h | 2 +- ...akkarTheSoulflayerExcessiveThreatTrigger.h | 2 +- .../script/HakkarTheSoulflayerPlayerScript.h | 2 +- .../script/HakkarTheSoulflayerScript.h | 6 +- .../HakkarTheSoulflayerUnitScriptPlayer.h | 5 + .../HighPriestVenoxisPhase1HolyWrathAction.h | 2 +- ...ghPriestVenoxisPhase1HolyWrathMultiplier.h | 2 +- .../HighPriestVenoxisPhase1HolyWrathTrigger.h | 2 +- ...xisPhase1RazzashiCobrasDPSPriorityAction.h | 2 +- ...isPhase1RazzashiCobrasDPSPriorityTrigger.h | 2 +- ...xisPhase1RazzashiCobrasPositioningAction.h | 2 +- ...isPhase1RazzashiCobrasPositioningTrigger.h | 2 +- ...estVenoxisPhase1VenoxisPositioningAction.h | 2 +- ...stVenoxisPhase1VenoxisPositioningTrigger.h | 2 +- src/Ai/Raid/ZulGurub/RaidZGStrategy.h | 16 +++ .../Raid/ZulGurub/leader/ZulGurubRaidLeader.h | 7 ++ 34 files changed, 328 insertions(+), 28 deletions(-) create mode 100644 src/Ai/Raid/ZulGurub/Boss/GrilekTheWanderer/assistant/GrilekTheWandererAssistant.h create mode 100644 src/Ai/Raid/ZulGurub/Boss/GrilekTheWanderer/definition/enum/GrilekTheWandererEnum.h create mode 100644 src/Ai/Raid/ZulGurub/Boss/GrilekTheWanderer/mechanics/Avatar/GrilekTheWandererAvatarAction.h create mode 100644 src/Ai/Raid/ZulGurub/Boss/GrilekTheWanderer/mechanics/Avatar/GrilekTheWandererAvatarMultiplier.h create mode 100644 src/Ai/Raid/ZulGurub/Boss/GrilekTheWanderer/mechanics/Avatar/GrilekTheWandererAvatarTrigger.h diff --git a/src/Ai/Raid/ZulGurub/Boss/GrilekTheWanderer/assistant/GrilekTheWandererAssistant.h b/src/Ai/Raid/ZulGurub/Boss/GrilekTheWanderer/assistant/GrilekTheWandererAssistant.h new file mode 100644 index 00000000000..081982775fa --- /dev/null +++ b/src/Ai/Raid/ZulGurub/Boss/GrilekTheWanderer/assistant/GrilekTheWandererAssistant.h @@ -0,0 +1,98 @@ +#pragma once + +#include "Unit.h" +#include "Player.h" +#include "Spell.h" + +#include "GridNotifiers.h" +// Necessary due to a poor implementation by AC. +#include "GridNotifiersImpl.h" + +#include "Cell.h" +#include "CellImpl.h" + +#include "../definition/enum/GrilekTheWandererEnum.h" +#include + + +class GrilekTheWandererAssistant +{ +public: + GrilekTheWandererAssistant() = default; + ~GrilekTheWandererAssistant() = default; + + GrilekTheWandererAssistant(const GrilekTheWandererAssistant&) = default; + GrilekTheWandererAssistant& operator=(const GrilekTheWandererAssistant&) = default; + + GrilekTheWandererAssistant(GrilekTheWandererAssistant&&) = default; + GrilekTheWandererAssistant& operator=(GrilekTheWandererAssistant&&) = default; + + [[nodiscard]] bool isInCombatWithGrilekTheWanderer(Player& bot) const noexcept + { + const Unit* const grilekTheWanderer = this->findActiveBoss(bot); + + if (grilekTheWanderer == nullptr) + { + return false; + } + + return bot.IsInCombatWith(grilekTheWanderer) || grilekTheWanderer->IsInCombatWith(&bot); + } + + [[nodiscard]] Creature* findActiveBoss(Player& bot) const noexcept + { + if (!bot.IsInCombat()) + { + return nullptr; + } + + Map* map = bot.GetMap(); + + if (map == nullptr) + { + return nullptr; + } + + const std::unordered_multimap store = map->GetCreatureBySpawnIdStore(); + const std::unordered_multimap::const_iterator it = store.find(uint32_t(GrilekTheWandererEnum::ENTRY)); + const std::unordered_multimap::const_iterator end = store.end(); + + if (it == end) + { + return nullptr; + } + + Creature* grilekTheWanderer = it->second; + + if (grilekTheWanderer == nullptr) + { + return nullptr; + } + + if (!grilekTheWanderer->IsAlive()) + { + return nullptr; + } + + if (grilekTheWanderer->GetEntry() != uint32_t(GrilekTheWandererEnum::ENTRY)) + { + return nullptr; + } + + return grilekTheWanderer; + }; + + [[nodiscard]] bool isAtSafeDistanceFromGrilekTheWanderer(Player& bot) const noexcept + { + const Unit* const grilekTheWanderer = this->findActiveBoss(bot); + + if (grilekTheWanderer == nullptr) + { + return true; + } + + const float distanceToGrilekTheWanderer = bot.GetExactDist2d(grilekTheWanderer->GetPositionX(), grilekTheWanderer->GetPositionY()); + + return distanceToGrilekTheWanderer > float(GrilekTheWandererEnum::AVATAR_SAFE_DISTANCE); + }; +}; diff --git a/src/Ai/Raid/ZulGurub/Boss/GrilekTheWanderer/definition/enum/GrilekTheWandererEnum.h b/src/Ai/Raid/ZulGurub/Boss/GrilekTheWanderer/definition/enum/GrilekTheWandererEnum.h new file mode 100644 index 00000000000..7bfa969c664 --- /dev/null +++ b/src/Ai/Raid/ZulGurub/Boss/GrilekTheWanderer/definition/enum/GrilekTheWandererEnum.h @@ -0,0 +1,10 @@ +#pragma once + +#include + +enum class GrilekTheWandererEnum : uint32_t +{ + ENTRY = 15082u, + SPELL_AVATAR = 24646u, + AVATAR_SAFE_DISTANCE = 15u, +}; diff --git a/src/Ai/Raid/ZulGurub/Boss/GrilekTheWanderer/mechanics/Avatar/GrilekTheWandererAvatarAction.h b/src/Ai/Raid/ZulGurub/Boss/GrilekTheWanderer/mechanics/Avatar/GrilekTheWandererAvatarAction.h new file mode 100644 index 00000000000..4418629b2f4 --- /dev/null +++ b/src/Ai/Raid/ZulGurub/Boss/GrilekTheWanderer/mechanics/Avatar/GrilekTheWandererAvatarAction.h @@ -0,0 +1,47 @@ +#pragma once + +#include "Spell.h" +#include "AiObjectContext.h" +#include "MovementActions.h" +#include "PlayerbotAI.h" + +#include "raid/leader/RaidLeaderRegistry.h" + +class GrilekTheWandererAvatarAction : public MovementAction +{ +public: + GrilekTheWandererAvatarAction( + PlayerbotAI* botAI, + const std::string name = "grilek the wanderer avatar" + ) : MovementAction(botAI, name) {} + + bool Execute(Event) override + { + if (this->bot == nullptr) + { + return false; + } + + const uint32_t instanceId = this->bot->GetInstanceId(); + RaidLeaderRegistry& raidRegistry = RaidLeaderRegistry::GetInstance(); + const ZulGurubRaidLeader& raidLeader = raidRegistry.getOrBind(instanceId, MAP_ZUL_GURUB); + const GrilekTheWandererAssistant& grilekTheWandererAssistant = raidLeader.getGrilekTheWandererAssistant(); + Unit* const grilekTheWanderer = grilekTheWandererAssistant.findActiveBoss(*this->bot); + + if (grilekTheWanderer == nullptr) + { + return false; + } + + const float safeDistance = float(GrilekTheWandererEnum::AVATAR_SAFE_DISTANCE) - this->bot->GetDistance2d(grilekTheWanderer); + + if (safeDistance <= 0.0f) + { + return false; + } + + this->MoveAway(grilekTheWanderer, safeDistance); + + return true; + } +}; diff --git a/src/Ai/Raid/ZulGurub/Boss/GrilekTheWanderer/mechanics/Avatar/GrilekTheWandererAvatarMultiplier.h b/src/Ai/Raid/ZulGurub/Boss/GrilekTheWanderer/mechanics/Avatar/GrilekTheWandererAvatarMultiplier.h new file mode 100644 index 00000000000..ab112b30e22 --- /dev/null +++ b/src/Ai/Raid/ZulGurub/Boss/GrilekTheWanderer/mechanics/Avatar/GrilekTheWandererAvatarMultiplier.h @@ -0,0 +1,61 @@ +#pragma once + +#include "AiObjectContext.h" +#include "MovementActions.h" +#include "Multiplier.h" +#include "PlayerbotAI.h" +#include "Value.h" + +#include "GenericSpellActions.h" +#include "raid/leader/RaidLeaderRegistry.h" + + +class GrilekTheWandererAvatarMultiplier : public Multiplier +{ +public: + GrilekTheWandererAvatarMultiplier(PlayerbotAI* botAI) : Multiplier(botAI, "grilek the wanderer avatar") {} + + float GetValue(Action& action) override + { + if (this->bot == nullptr) + { + return 0.0f; + } + + if (this->botAI->IsTank(this->bot) == true) + { + return 1.0f; + } + + const uint32_t instanceId = this->bot->GetInstanceId(); + RaidLeaderRegistry& raidRegistry = RaidLeaderRegistry::GetInstance(); + const ZulGurubRaidLeader& raidLeader = raidRegistry.getOrBind(instanceId, MAP_ZUL_GURUB); + const GrilekTheWandererAssistant& grilekTheWandererAssistant = raidLeader.getGrilekTheWandererAssistant(); + + const Creature* const grilekTheWanderer = grilekTheWandererAssistant.findActiveBoss(*this->bot); + + if (grilekTheWanderer == nullptr) + { + return 1.0f; + } + + if (grilekTheWanderer->HasAura(uint32_t(GrilekTheWandererEnum::SPELL_AVATAR)) == false) + { + return 1.0f; + } + + if (grilekTheWandererAssistant.isAtSafeDistanceFromGrilekTheWanderer(*this->bot) == true) + { + return 1.0f; + } + + const MovementAction* const movementAction = dynamic_cast(&action); + + if (movementAction != nullptr) + { + return 1.0f; + } + + return 0.0f; + } +}; diff --git a/src/Ai/Raid/ZulGurub/Boss/GrilekTheWanderer/mechanics/Avatar/GrilekTheWandererAvatarTrigger.h b/src/Ai/Raid/ZulGurub/Boss/GrilekTheWanderer/mechanics/Avatar/GrilekTheWandererAvatarTrigger.h new file mode 100644 index 00000000000..66a1489ff12 --- /dev/null +++ b/src/Ai/Raid/ZulGurub/Boss/GrilekTheWanderer/mechanics/Avatar/GrilekTheWandererAvatarTrigger.h @@ -0,0 +1,56 @@ +#pragma once + +#include "AiObjectContext.h" +#include "GrilekTheWanderer/assistant/GrilekTheWandererAssistant.h" +#include "PlayerbotAI.h" +#include "Trigger.h" + +#include "raid/leader/RaidLeaderRegistry.h" + +class GrilekTheWandererAvatarTrigger : public Trigger +{ +public: + GrilekTheWandererAvatarTrigger(PlayerbotAI* botAI) : Trigger(botAI, "grilek the wanderer avatar") {} + + bool IsActive() override + { + if (this->bot == nullptr) + { + return false; + } + + if (this->botAI == nullptr) + { + return false; + } + + const uint32_t instanceId = this->bot->GetInstanceId(); + RaidLeaderRegistry& raidRegistry = RaidLeaderRegistry::GetInstance(); + const ZulGurubRaidLeader& raidLeader = raidRegistry.getOrBind(instanceId, MAP_ZUL_GURUB); + const GrilekTheWandererAssistant& grilekTheWandererAssistant = raidLeader.getGrilekTheWandererAssistant(); + + if (grilekTheWandererAssistant.isInCombatWithGrilekTheWanderer(*this->bot) == false) + { + return false; + } + + const Creature* const grilekTheWanderer = grilekTheWandererAssistant.findActiveBoss(*this->bot); + + if (grilekTheWanderer == nullptr) + { + return false; + } + + if (grilekTheWanderer->HasAura(uint32_t(GrilekTheWandererEnum::SPELL_AVATAR)) == false) + { + return false; + } + + if (grilekTheWandererAssistant.isAtSafeDistanceFromGrilekTheWanderer(*this->bot) == true) + { + return false; + } + + return true; + } +}; diff --git a/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/blood-siphon/GoToPoisonousCloud/HakkarTheSoulflayerGoToPoisonousCloudAction.h b/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/blood-siphon/GoToPoisonousCloud/HakkarTheSoulflayerGoToPoisonousCloudAction.h index 01f17be6c74..069917a199f 100644 --- a/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/blood-siphon/GoToPoisonousCloud/HakkarTheSoulflayerGoToPoisonousCloudAction.h +++ b/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/blood-siphon/GoToPoisonousCloud/HakkarTheSoulflayerGoToPoisonousCloudAction.h @@ -36,7 +36,7 @@ class HakkarTheSoulflayerGoToPoisonousCloudAction : public MovementAction const uint32_t instanceId = this->bot->GetInstanceId(); RaidLeaderRegistry& raidRegistry = RaidLeaderRegistry::GetInstance(); - const ZulGurubRaidLeader& raidLeader = raidRegistry.getOrBind(instanceId, this->bot->GetMapId()); + const ZulGurubRaidLeader& raidLeader = raidRegistry.getOrBind(instanceId, MAP_ZUL_GURUB); const SonOfHakkarAssistant& sonOfHakkarAssistant = raidLeader.getSonOfHakkarAssistant(); const Unit* const poisonousCloud = sonOfHakkarAssistant.findPoisonousCloud(*this->bot); diff --git a/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/blood-siphon/GoToPoisonousCloud/HakkarTheSoulflayerGoToPoisonousCloudMultiplier.h b/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/blood-siphon/GoToPoisonousCloud/HakkarTheSoulflayerGoToPoisonousCloudMultiplier.h index 7fc9a43b746..ef10d8f77de 100644 --- a/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/blood-siphon/GoToPoisonousCloud/HakkarTheSoulflayerGoToPoisonousCloudMultiplier.h +++ b/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/blood-siphon/GoToPoisonousCloud/HakkarTheSoulflayerGoToPoisonousCloudMultiplier.h @@ -21,7 +21,7 @@ class HakkarTheSoulflayerGoToPoisonousCloudMultiplier : public Multiplier const uint32_t instanceId = this->bot->GetInstanceId(); RaidLeaderRegistry& raidRegistry = RaidLeaderRegistry::GetInstance(); - const ZulGurubRaidLeader& raidLeader = raidRegistry.getOrBind(instanceId, this->bot->GetMapId()); + const ZulGurubRaidLeader& raidLeader = raidRegistry.getOrBind(instanceId, MAP_ZUL_GURUB); const HakkarTheSoulflayerAssistant& hakkarTheSoulflayerAssistant = raidLeader.getHakkarTheSoulflayerAssistant(); if (hakkarTheSoulflayerAssistant.isInCombatWithHakkar(*this->bot) == false) diff --git a/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/blood-siphon/GoToPoisonousCloud/HakkarTheSoulflayerGoToPoisonousCloudTrigger.h b/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/blood-siphon/GoToPoisonousCloud/HakkarTheSoulflayerGoToPoisonousCloudTrigger.h index 74f0af4b2ac..9ac2016dd0d 100644 --- a/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/blood-siphon/GoToPoisonousCloud/HakkarTheSoulflayerGoToPoisonousCloudTrigger.h +++ b/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/blood-siphon/GoToPoisonousCloud/HakkarTheSoulflayerGoToPoisonousCloudTrigger.h @@ -25,7 +25,7 @@ class HakkarTheSoulflayerGoToPoisonousCloudTrigger : public Trigger const uint32_t instanceId = this->bot->GetInstanceId(); RaidLeaderRegistry& raidRegistry = RaidLeaderRegistry::GetInstance(); - const ZulGurubRaidLeader& raidLeader = raidRegistry.getOrBind(instanceId, this->bot->GetMapId()); + const ZulGurubRaidLeader& raidLeader = raidRegistry.getOrBind(instanceId, MAP_ZUL_GURUB); const HakkarTheSoulflayerAssistant& hakkarTheSoulflayerAssistant = raidLeader.getHakkarTheSoulflayerAssistant(); const SonOfHakkarAssistant& sonOfHakkarAssistant = raidLeader.getSonOfHakkarAssistant(); diff --git a/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/blood-siphon/PoisonousBlood/HakkarTheSoulflayerPoisonousBloodMultiplier.h b/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/blood-siphon/PoisonousBlood/HakkarTheSoulflayerPoisonousBloodMultiplier.h index 28062618ede..462ccd0bc2f 100644 --- a/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/blood-siphon/PoisonousBlood/HakkarTheSoulflayerPoisonousBloodMultiplier.h +++ b/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/blood-siphon/PoisonousBlood/HakkarTheSoulflayerPoisonousBloodMultiplier.h @@ -43,7 +43,7 @@ class HakkarTheSoulflayerPoisonousBloodMultiplier : public Multiplier const uint32_t instanceId = this->bot->GetInstanceId(); RaidLeaderRegistry& raidRegistry = RaidLeaderRegistry::GetInstance(); - ZulGurubRaidLeader& raidLeader = raidRegistry.getOrBind(instanceId, this->bot->GetMapId()); + ZulGurubRaidLeader& raidLeader = raidRegistry.getOrBind(instanceId, MAP_ZUL_GURUB); const HakkarTheSoulflayerAssistant& hakkarTheSoulflayerAssistant = raidLeader.getHakkarTheSoulflayerAssistant(); if (hakkarTheSoulflayerAssistant.isInCombatWithHakkar(*this->bot) == false) diff --git a/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/cause-insanity/CauseInsanity/HakkarTheSoulflayerCauseInsanityAction.h b/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/cause-insanity/CauseInsanity/HakkarTheSoulflayerCauseInsanityAction.h index 43478dc4fba..c2da05ecd32 100644 --- a/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/cause-insanity/CauseInsanity/HakkarTheSoulflayerCauseInsanityAction.h +++ b/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/cause-insanity/CauseInsanity/HakkarTheSoulflayerCauseInsanityAction.h @@ -32,7 +32,7 @@ class HakkarTheSoulflayerCauseInsanityAction : public AttackAction const uint32_t instanceId = this->bot->GetInstanceId(); RaidLeaderRegistry& raidRegistry = RaidLeaderRegistry::GetInstance(); - ZulGurubRaidLeader& raidLeader = raidRegistry.getOrBind(instanceId, this->bot->GetMapId()); + ZulGurubRaidLeader& raidLeader = raidRegistry.getOrBind(instanceId, MAP_ZUL_GURUB); const HakkarTheSoulflayerAssistant& hakkarTheSoulflayerAssistant = raidLeader.getHakkarTheSoulflayerAssistant(); Player* const insaneAlly = hakkarTheSoulflayerAssistant.findInsaneAlly(*this->bot); diff --git a/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/cause-insanity/CauseInsanity/HakkarTheSoulflayerCauseInsanityTrigger.h b/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/cause-insanity/CauseInsanity/HakkarTheSoulflayerCauseInsanityTrigger.h index 6dd61b99b8b..6bb818e6643 100644 --- a/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/cause-insanity/CauseInsanity/HakkarTheSoulflayerCauseInsanityTrigger.h +++ b/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/cause-insanity/CauseInsanity/HakkarTheSoulflayerCauseInsanityTrigger.h @@ -34,7 +34,7 @@ class HakkarTheSoulflayerCauseInsanityTrigger : public Trigger const uint32_t instanceId = this->bot->GetInstanceId(); RaidLeaderRegistry& raidRegistry = RaidLeaderRegistry::GetInstance(); - ZulGurubRaidLeader& raidLeader = raidRegistry.getOrBind(instanceId, this->bot->GetMapId()); + ZulGurubRaidLeader& raidLeader = raidRegistry.getOrBind(instanceId, MAP_ZUL_GURUB); const HakkarTheSoulflayerAssistant& hakkarTheSoulflayerAssistant = raidLeader.getHakkarTheSoulflayerAssistant(); if (hakkarTheSoulflayerAssistant.isInCombatWithHakkar(*this->bot) == false) diff --git a/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/positioning/HakkarPositioning/HakkarTheSoulflayerHakkarPositioningAction.h b/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/positioning/HakkarPositioning/HakkarTheSoulflayerHakkarPositioningAction.h index 27a4e8e2fd9..24da4dff350 100644 --- a/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/positioning/HakkarPositioning/HakkarTheSoulflayerHakkarPositioningAction.h +++ b/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/positioning/HakkarPositioning/HakkarTheSoulflayerHakkarPositioningAction.h @@ -33,7 +33,7 @@ class HakkarTheSoulflayerHakkarPositioningAction : public AttackAction const uint32_t instanceId = this->bot->GetInstanceId(); RaidLeaderRegistry& raidRegistry = RaidLeaderRegistry::GetInstance(); - ZulGurubRaidLeader& raidLeader = raidRegistry.getOrBind(instanceId, this->bot->GetMapId()); + ZulGurubRaidLeader& raidLeader = raidRegistry.getOrBind(instanceId, MAP_ZUL_GURUB); const HakkarTheSoulflayerAssistant& hakkarTheSoulflayerAssistant = raidLeader.getHakkarTheSoulflayerAssistant(); Unit* const boss = hakkarTheSoulflayerAssistant.findActiveBoss(*this->bot); diff --git a/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/positioning/HakkarPositioning/HakkarTheSoulflayerHakkarPositioningTrigger.h b/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/positioning/HakkarPositioning/HakkarTheSoulflayerHakkarPositioningTrigger.h index 7c1bf9e9366..86a12bef768 100644 --- a/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/positioning/HakkarPositioning/HakkarTheSoulflayerHakkarPositioningTrigger.h +++ b/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/positioning/HakkarPositioning/HakkarTheSoulflayerHakkarPositioningTrigger.h @@ -25,7 +25,7 @@ class HakkarTheSoulflayerHakkarPositioningTrigger : public Trigger const uint32_t instanceId = this->bot->GetInstanceId(); RaidLeaderRegistry& raidRegistry = RaidLeaderRegistry::GetInstance(); - ZulGurubRaidLeader& raidLeader = raidRegistry.getOrBind(instanceId, this->bot->GetMapId()); + ZulGurubRaidLeader& raidLeader = raidRegistry.getOrBind(instanceId, MAP_ZUL_GURUB); const HakkarTheSoulflayerAssistant& hakkarTheSoulflayerAssistant = raidLeader.getHakkarTheSoulflayerAssistant(); if (hakkarTheSoulflayerAssistant.isInCombatWithHakkar(*this->bot) == false) diff --git a/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/son-of-hakkar/BringBackSonOfHakkar/HakkarTheSoulflayerBringBackSonOfHakkarAction.h b/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/son-of-hakkar/BringBackSonOfHakkar/HakkarTheSoulflayerBringBackSonOfHakkarAction.h index 1c901a09b68..a0b91873058 100644 --- a/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/son-of-hakkar/BringBackSonOfHakkar/HakkarTheSoulflayerBringBackSonOfHakkarAction.h +++ b/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/son-of-hakkar/BringBackSonOfHakkar/HakkarTheSoulflayerBringBackSonOfHakkarAction.h @@ -36,7 +36,7 @@ class HakkarTheSoulflayerBringBackSonOfHakkarAction : public MovementAction const uint32_t instanceId = this->bot->GetInstanceId(); RaidLeaderRegistry& raidRegistry = RaidLeaderRegistry::GetInstance(); - ZulGurubRaidLeader& raidLeader = raidRegistry.getOrBind(instanceId, this->bot->GetMapId()); + ZulGurubRaidLeader& raidLeader = raidRegistry.getOrBind(instanceId, MAP_ZUL_GURUB); const HakkarTheSoulflayerAssistant& hakkarTheSoulflayerAssistant = raidLeader.getHakkarTheSoulflayerAssistant(); SonOfHakkarAssistant& sonOfHakkarAssistant = raidLeader.getSonOfHakkarAssistant(); const SonOfHakkarMemory& memory = sonOfHakkarAssistant.getMemory(); diff --git a/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/son-of-hakkar/BringBackSonOfHakkar/HakkarTheSoulflayerBringBackSonOfHakkarTrigger.h b/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/son-of-hakkar/BringBackSonOfHakkar/HakkarTheSoulflayerBringBackSonOfHakkarTrigger.h index cd280a9bead..3b1f8880118 100644 --- a/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/son-of-hakkar/BringBackSonOfHakkar/HakkarTheSoulflayerBringBackSonOfHakkarTrigger.h +++ b/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/son-of-hakkar/BringBackSonOfHakkar/HakkarTheSoulflayerBringBackSonOfHakkarTrigger.h @@ -26,7 +26,7 @@ class HakkarTheSoulflayerBringBackSonOfHakkarTrigger : public Trigger const uint32_t instanceId = this->bot->GetInstanceId(); RaidLeaderRegistry& raidRegistry = RaidLeaderRegistry::GetInstance(); - ZulGurubRaidLeader& raidLeader = raidRegistry.getOrBind(instanceId, this->bot->GetMapId()); + ZulGurubRaidLeader& raidLeader = raidRegistry.getOrBind(instanceId, MAP_ZUL_GURUB); const HakkarTheSoulflayerAssistant& hakkarTheSoulflayerAssistant = raidLeader.getHakkarTheSoulflayerAssistant(); const SonOfHakkarAssistant& sonOfHakkarAssistant = raidLeader.getSonOfHakkarAssistant(); diff --git a/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/son-of-hakkar/MoveToSonOfHakkar/HakkarTheSoulflayerMoveToSonOfHakkarAction.h b/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/son-of-hakkar/MoveToSonOfHakkar/HakkarTheSoulflayerMoveToSonOfHakkarAction.h index 4bd3d0bb0f3..19a2b03b516 100644 --- a/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/son-of-hakkar/MoveToSonOfHakkar/HakkarTheSoulflayerMoveToSonOfHakkarAction.h +++ b/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/son-of-hakkar/MoveToSonOfHakkar/HakkarTheSoulflayerMoveToSonOfHakkarAction.h @@ -36,7 +36,7 @@ class HakkarTheSoulflayerMoveToSonOfHakkarAction : public MovementAction const uint32_t instanceId = this->bot->GetInstanceId(); RaidLeaderRegistry& raidRegistry = RaidLeaderRegistry::GetInstance(); - ZulGurubRaidLeader& raidLeader = raidRegistry.getOrBind(instanceId, this->bot->GetMapId()); + ZulGurubRaidLeader& raidLeader = raidRegistry.getOrBind(instanceId, MAP_ZUL_GURUB); SonOfHakkarAssistant& sonOfHakkarAssistant = raidLeader.getSonOfHakkarAssistant(); Creature* const sonOfHakkar = sonOfHakkarAssistant.findAnyAliveSonOfHakkar(); diff --git a/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/son-of-hakkar/MoveToSonOfHakkar/HakkarTheSoulflayerMoveToSonOfHakkarTrigger.h b/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/son-of-hakkar/MoveToSonOfHakkar/HakkarTheSoulflayerMoveToSonOfHakkarTrigger.h index 71ec5cb3702..02f648df967 100644 --- a/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/son-of-hakkar/MoveToSonOfHakkar/HakkarTheSoulflayerMoveToSonOfHakkarTrigger.h +++ b/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/son-of-hakkar/MoveToSonOfHakkar/HakkarTheSoulflayerMoveToSonOfHakkarTrigger.h @@ -22,7 +22,7 @@ class HakkarTheSoulflayerMoveToSonOfHakkarTrigger : public Trigger const uint32_t instanceId = this->bot->GetInstanceId(); RaidLeaderRegistry& raidRegistry = RaidLeaderRegistry::GetInstance(); - ZulGurubRaidLeader& raidLeader = raidRegistry.getOrBind(instanceId, this->bot->GetMapId()); + ZulGurubRaidLeader& raidLeader = raidRegistry.getOrBind(instanceId, MAP_ZUL_GURUB); HakkarTheSoulflayerAssistant& hakkarTheSoulflayerAssistant = raidLeader.getHakkarTheSoulflayerAssistant(); const SonOfHakkarAssistant& sonOfHakkarAssistant = raidLeader.getSonOfHakkarAssistant(); diff --git a/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/son-of-hakkar/PullSonOfHakkar/HakkarTheSoulflayerPullSonOfHakkarAction.h b/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/son-of-hakkar/PullSonOfHakkar/HakkarTheSoulflayerPullSonOfHakkarAction.h index f917b597ae2..b5067062c60 100644 --- a/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/son-of-hakkar/PullSonOfHakkar/HakkarTheSoulflayerPullSonOfHakkarAction.h +++ b/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/son-of-hakkar/PullSonOfHakkar/HakkarTheSoulflayerPullSonOfHakkarAction.h @@ -32,7 +32,7 @@ class HakkarTheSoulflayerPullSonOfHakkarAction : public AttackAction const uint32_t instanceId = this->bot->GetInstanceId(); RaidLeaderRegistry& raidRegistry = RaidLeaderRegistry::GetInstance(); - const ZulGurubRaidLeader& raidLeader = raidRegistry.getOrBind(instanceId, this->bot->GetMapId()); + const ZulGurubRaidLeader& raidLeader = raidRegistry.getOrBind(instanceId, MAP_ZUL_GURUB); const SonOfHakkarAssistant& sonOfHakkarAssistant = raidLeader.getSonOfHakkarAssistant(); const SonOfHakkarMemory& sonOfHakkarMemory = sonOfHakkarAssistant.getMemory(); diff --git a/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/son-of-hakkar/PullSonOfHakkar/HakkarTheSoulflayerPullSonOfHakkarTrigger.h b/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/son-of-hakkar/PullSonOfHakkar/HakkarTheSoulflayerPullSonOfHakkarTrigger.h index b06fcf455e2..aa9416b298e 100644 --- a/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/son-of-hakkar/PullSonOfHakkar/HakkarTheSoulflayerPullSonOfHakkarTrigger.h +++ b/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/son-of-hakkar/PullSonOfHakkar/HakkarTheSoulflayerPullSonOfHakkarTrigger.h @@ -22,7 +22,7 @@ class HakkarTheSoulflayerPullSonOfHakkarTrigger : public Trigger const uint32_t instanceId = this->bot->GetInstanceId(); RaidLeaderRegistry& raidRegistry = RaidLeaderRegistry::GetInstance(); - const ZulGurubRaidLeader& raidLeader = raidRegistry.getOrBind(instanceId, this->bot->GetMapId()); + const ZulGurubRaidLeader& raidLeader = raidRegistry.getOrBind(instanceId, MAP_ZUL_GURUB); const HakkarTheSoulflayerAssistant& hakkarTheSoulflayerAssistant = raidLeader.getHakkarTheSoulflayerAssistant(); const SonOfHakkarAssistant& sonOfHakkarAssistant = raidLeader.getSonOfHakkarAssistant(); diff --git a/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/threat/ExcessiveThreat/HakkarTheSoulflayerExcessiveThreatTrigger.h b/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/threat/ExcessiveThreat/HakkarTheSoulflayerExcessiveThreatTrigger.h index 8011ba84785..dd752492dd0 100644 --- a/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/threat/ExcessiveThreat/HakkarTheSoulflayerExcessiveThreatTrigger.h +++ b/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/mechanics/threat/ExcessiveThreat/HakkarTheSoulflayerExcessiveThreatTrigger.h @@ -25,7 +25,7 @@ class HakkarTheSoulflayerExcessiveThreatTrigger : public Trigger const uint32_t instanceId = this->bot->GetInstanceId(); RaidLeaderRegistry& raidRegistry = RaidLeaderRegistry::GetInstance(); - ZulGurubRaidLeader& raidLeader = raidRegistry.getOrBind(instanceId, this->bot->GetMapId()); + ZulGurubRaidLeader& raidLeader = raidRegistry.getOrBind(instanceId, MAP_ZUL_GURUB); const HakkarTheSoulflayerAssistant& hakkarTheSoulflayerAssistant = raidLeader.getHakkarTheSoulflayerAssistant(); if (hakkarTheSoulflayerAssistant.isInCombatWithHakkar(*this->bot) == false) diff --git a/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/script/HakkarTheSoulflayerPlayerScript.h b/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/script/HakkarTheSoulflayerPlayerScript.h index 68512bb99ab..20641d57844 100644 --- a/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/script/HakkarTheSoulflayerPlayerScript.h +++ b/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/script/HakkarTheSoulflayerPlayerScript.h @@ -27,7 +27,7 @@ class HakkarTheSoulflayerPlayerScript : public PlayerScript const uint32_t instanceId = player->GetInstanceId(); RaidLeaderRegistry& raidRegistry = RaidLeaderRegistry::GetInstance(); - ZulGurubRaidLeader& raidLeader = raidRegistry.getOrBind(instanceId, player->GetMapId()); + ZulGurubRaidLeader& raidLeader = raidRegistry.getOrBind(instanceId, MAP_ZUL_GURUB); SonOfHakkarAssistant& sonOfHakkarAssistant = raidLeader.getSonOfHakkarAssistant(); const SonOfHakkarMemory& memory = sonOfHakkarAssistant.getMemory(); diff --git a/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/script/HakkarTheSoulflayerScript.h b/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/script/HakkarTheSoulflayerScript.h index fbafc00c700..99b3cd96315 100644 --- a/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/script/HakkarTheSoulflayerScript.h +++ b/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/script/HakkarTheSoulflayerScript.h @@ -31,7 +31,7 @@ class HakkarTheSoulflayerScript : public UnitScript const uint32_t instanceId = unit->GetInstanceId(); RaidLeaderRegistry& raidRegistry = RaidLeaderRegistry::GetInstance(); - ZulGurubRaidLeader& raidLeader = raidRegistry.getOrBind(instanceId, unit->GetMapId()); + ZulGurubRaidLeader& raidLeader = raidRegistry.getOrBind(instanceId, MAP_ZUL_GURUB); RaidTankAssistant& raidTankAssistant = raidLeader.getRaidTankAssistant(); HakkarTheSoulflayerAssistant& hakkarTheSoulflayerAssistant = raidLeader.getHakkarTheSoulflayerAssistant(); SonOfHakkarAssistant& sonOfHakkarAssistant = raidLeader.getSonOfHakkarAssistant(); @@ -63,7 +63,7 @@ class HakkarTheSoulflayerScript : public UnitScript const uint32_t instanceId = unit->GetInstanceId(); RaidLeaderRegistry& raidRegistry = RaidLeaderRegistry::GetInstance(); - ZulGurubRaidLeader& raidLeader = raidRegistry.getOrBind(instanceId, unit->GetMapId()); + ZulGurubRaidLeader& raidLeader = raidRegistry.getOrBind(instanceId, MAP_ZUL_GURUB); const HakkarTheSoulflayerAssistant& hakkarTheSoulflayerAssistant = raidLeader.getHakkarTheSoulflayerAssistant(); SonOfHakkarAssistant& sonOfHakkarAssistant = raidLeader.getSonOfHakkarAssistant(); @@ -107,7 +107,7 @@ class HakkarTheSoulflayerScript : public UnitScript const uint32_t instanceId = unit->GetInstanceId(); RaidLeaderRegistry& raidRegistry = RaidLeaderRegistry::GetInstance(); - ZulGurubRaidLeader& raidLeader = raidRegistry.getOrBind(instanceId, unit->GetMapId()); + ZulGurubRaidLeader& raidLeader = raidRegistry.getOrBind(instanceId, MAP_ZUL_GURUB); HakkarTheSoulflayerAssistant& hakkarTheSoulflayerAssistant = raidLeader.getHakkarTheSoulflayerAssistant(); SonOfHakkarAssistant& sonOfHakkarAssistant = raidLeader.getSonOfHakkarAssistant(); diff --git a/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/script/HakkarTheSoulflayerUnitScriptPlayer.h b/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/script/HakkarTheSoulflayerUnitScriptPlayer.h index a4f757b9951..48e0dd458e6 100644 --- a/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/script/HakkarTheSoulflayerUnitScriptPlayer.h +++ b/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/script/HakkarTheSoulflayerUnitScriptPlayer.h @@ -36,6 +36,11 @@ class HakkarTheSoulflayerUnitScriptPlayer : public UnitScript PlayerbotAI* const botAI = PlayerbotsMgr::instance().GetPlayerbotAI(player); + if (botAI == nullptr) + { + return; + } + botAI->SayToRaid("I have been infected with the soulflayer's corrupted blood!"); } }; diff --git a/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/HolyWrath/HighPriestVenoxisPhase1HolyWrathAction.h b/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/HolyWrath/HighPriestVenoxisPhase1HolyWrathAction.h index 46ae72490d6..589343b82da 100644 --- a/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/HolyWrath/HighPriestVenoxisPhase1HolyWrathAction.h +++ b/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/HolyWrath/HighPriestVenoxisPhase1HolyWrathAction.h @@ -24,7 +24,7 @@ class HighPriestVenoxisPhase1HolyWrathAction : public MovementAction const uint32_t instanceId = this->bot->GetInstanceId(); RaidLeaderRegistry& raidRegistry = RaidLeaderRegistry::GetInstance(); - const ZulGurubRaidLeader& raidLeader = raidRegistry.getOrBind(instanceId, this->bot->GetMapId()); + const ZulGurubRaidLeader& raidLeader = raidRegistry.getOrBind(instanceId, MAP_ZUL_GURUB); const HighPriestVenoxisAssistant& highPriestVenoxisAssistant = raidLeader.getHighPriestVenoxisAssistant(); Unit* const venoxis = highPriestVenoxisAssistant.findActiveBoss(*this->bot); diff --git a/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/HolyWrath/HighPriestVenoxisPhase1HolyWrathMultiplier.h b/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/HolyWrath/HighPriestVenoxisPhase1HolyWrathMultiplier.h index a7f564b9619..b486af6eb24 100644 --- a/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/HolyWrath/HighPriestVenoxisPhase1HolyWrathMultiplier.h +++ b/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/HolyWrath/HighPriestVenoxisPhase1HolyWrathMultiplier.h @@ -32,7 +32,7 @@ class HighPriestVenoxisPhase1HolyWrathMultiplier : public Multiplier const uint32_t instanceId = this->bot->GetInstanceId(); RaidLeaderRegistry& raidRegistry = RaidLeaderRegistry::GetInstance(); - const ZulGurubRaidLeader& raidLeader = raidRegistry.getOrBind(instanceId, this->bot->GetMapId()); + const ZulGurubRaidLeader& raidLeader = raidRegistry.getOrBind(instanceId, MAP_ZUL_GURUB); const HighPriestVenoxisAssistant& highPriestVenoxisAssistant = raidLeader.getHighPriestVenoxisAssistant(); if (highPriestVenoxisAssistant.isInPhase1(*this->bot) == false) diff --git a/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/HolyWrath/HighPriestVenoxisPhase1HolyWrathTrigger.h b/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/HolyWrath/HighPriestVenoxisPhase1HolyWrathTrigger.h index 1be719cd42c..2f5ffc6214e 100644 --- a/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/HolyWrath/HighPriestVenoxisPhase1HolyWrathTrigger.h +++ b/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/HolyWrath/HighPriestVenoxisPhase1HolyWrathTrigger.h @@ -30,7 +30,7 @@ class HighPriestVenoxisPhase1HolyWrathTrigger : public Trigger const uint32_t instanceId = this->bot->GetInstanceId(); RaidLeaderRegistry& raidRegistry = RaidLeaderRegistry::GetInstance(); - const ZulGurubRaidLeader& raidLeader = raidRegistry.getOrBind(instanceId, this->bot->GetMapId()); + const ZulGurubRaidLeader& raidLeader = raidRegistry.getOrBind(instanceId, MAP_ZUL_GURUB); const HighPriestVenoxisAssistant& highPriestVenoxisAssistant = raidLeader.getHighPriestVenoxisAssistant(); if (highPriestVenoxisAssistant.isInCombatWithVenoxis(*this->bot) == false) diff --git a/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/RazzashiCobrasDPSPriority/HighPriestVenoxisPhase1RazzashiCobrasDPSPriorityAction.h b/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/RazzashiCobrasDPSPriority/HighPriestVenoxisPhase1RazzashiCobrasDPSPriorityAction.h index 7bf1751f567..b730a4cc144 100644 --- a/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/RazzashiCobrasDPSPriority/HighPriestVenoxisPhase1RazzashiCobrasDPSPriorityAction.h +++ b/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/RazzashiCobrasDPSPriority/HighPriestVenoxisPhase1RazzashiCobrasDPSPriorityAction.h @@ -24,7 +24,7 @@ class HighPriestVenoxisPhase1RazzashiCobrasDPSPriorityAction : public AttackActi const uint32_t instanceId = this->bot->GetInstanceId(); RaidLeaderRegistry& raidRegistry = RaidLeaderRegistry::GetInstance(); - const ZulGurubRaidLeader& raidLeader = raidRegistry.getOrBind(instanceId, this->bot->GetMapId()); + const ZulGurubRaidLeader& raidLeader = raidRegistry.getOrBind(instanceId, MAP_ZUL_GURUB); const HighPriestVenoxisAssistant& highPriestVenoxisAssistant = raidLeader.getHighPriestVenoxisAssistant(); const std::vector razzashiCobras = highPriestVenoxisAssistant.findRazzashiCobras(*this->bot); diff --git a/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/RazzashiCobrasDPSPriority/HighPriestVenoxisPhase1RazzashiCobrasDPSPriorityTrigger.h b/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/RazzashiCobrasDPSPriority/HighPriestVenoxisPhase1RazzashiCobrasDPSPriorityTrigger.h index 0eeacf334c3..02be86835ba 100644 --- a/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/RazzashiCobrasDPSPriority/HighPriestVenoxisPhase1RazzashiCobrasDPSPriorityTrigger.h +++ b/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/RazzashiCobrasDPSPriority/HighPriestVenoxisPhase1RazzashiCobrasDPSPriorityTrigger.h @@ -25,7 +25,7 @@ class HighPriestVenoxisPhase1RazzashiCobrasDPSPriorityTrigger : public Trigger const uint32_t instanceId = this->bot->GetInstanceId(); RaidLeaderRegistry& raidRegistry = RaidLeaderRegistry::GetInstance(); - const ZulGurubRaidLeader& raidLeader = raidRegistry.getOrBind(instanceId, this->bot->GetMapId()); + const ZulGurubRaidLeader& raidLeader = raidRegistry.getOrBind(instanceId, MAP_ZUL_GURUB); const HighPriestVenoxisAssistant& highPriestVenoxisAssistant = raidLeader.getHighPriestVenoxisAssistant(); if (highPriestVenoxisAssistant.isInCombatWithVenoxis(*this->bot) == false) diff --git a/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/RazzashiCobrasPositioning/HighPriestVenoxisPhase1RazzashiCobrasPositioningAction.h b/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/RazzashiCobrasPositioning/HighPriestVenoxisPhase1RazzashiCobrasPositioningAction.h index 349722711d5..165b9a6303c 100644 --- a/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/RazzashiCobrasPositioning/HighPriestVenoxisPhase1RazzashiCobrasPositioningAction.h +++ b/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/RazzashiCobrasPositioning/HighPriestVenoxisPhase1RazzashiCobrasPositioningAction.h @@ -29,7 +29,7 @@ class HighPriestVenoxisPhase1RazzashiCobrasPositioningAction : public AttackActi const uint32_t instanceId = this->bot->GetInstanceId(); RaidLeaderRegistry& raidRegistry = RaidLeaderRegistry::GetInstance(); - const ZulGurubRaidLeader& raidLeader = raidRegistry.getOrBind(instanceId, this->bot->GetMapId()); + const ZulGurubRaidLeader& raidLeader = raidRegistry.getOrBind(instanceId, MAP_ZUL_GURUB); const HighPriestVenoxisAssistant& highPriestVenoxisAssistant = raidLeader.getHighPriestVenoxisAssistant(); const std::vector razzashiCobras = highPriestVenoxisAssistant.findRazzashiCobras(*this->bot); diff --git a/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/RazzashiCobrasPositioning/HighPriestVenoxisPhase1RazzashiCobrasPositioningTrigger.h b/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/RazzashiCobrasPositioning/HighPriestVenoxisPhase1RazzashiCobrasPositioningTrigger.h index 3a98ab6647a..2a1dafb5ca9 100644 --- a/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/RazzashiCobrasPositioning/HighPriestVenoxisPhase1RazzashiCobrasPositioningTrigger.h +++ b/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/RazzashiCobrasPositioning/HighPriestVenoxisPhase1RazzashiCobrasPositioningTrigger.h @@ -35,7 +35,7 @@ class HighPriestVenoxisPhase1RazzashiCobrasPositioningTrigger : public Trigger const uint32_t instanceId = this->bot->GetInstanceId(); RaidLeaderRegistry& raidRegistry = RaidLeaderRegistry::GetInstance(); - const ZulGurubRaidLeader& raidLeader = raidRegistry.getOrBind(instanceId, this->bot->GetMapId()); + const ZulGurubRaidLeader& raidLeader = raidRegistry.getOrBind(instanceId, MAP_ZUL_GURUB); const HighPriestVenoxisAssistant& highPriestVenoxisAssistant = raidLeader.getHighPriestVenoxisAssistant(); if (highPriestVenoxisAssistant.isInCombatWithVenoxis(*this->bot) == false) diff --git a/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/VenoxisPositioning/HighPriestVenoxisPhase1VenoxisPositioningAction.h b/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/VenoxisPositioning/HighPriestVenoxisPhase1VenoxisPositioningAction.h index dfc30027f49..074000a37ab 100644 --- a/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/VenoxisPositioning/HighPriestVenoxisPhase1VenoxisPositioningAction.h +++ b/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/VenoxisPositioning/HighPriestVenoxisPhase1VenoxisPositioningAction.h @@ -33,7 +33,7 @@ class HighPriestVenoxisPhase1VenoxisPositioningAction : public AttackAction const uint32_t instanceId = this->bot->GetInstanceId(); RaidLeaderRegistry& raidRegistry = RaidLeaderRegistry::GetInstance(); - const ZulGurubRaidLeader& raidLeader = raidRegistry.getOrBind(instanceId, this->bot->GetMapId()); + const ZulGurubRaidLeader& raidLeader = raidRegistry.getOrBind(instanceId, MAP_ZUL_GURUB); const HighPriestVenoxisAssistant& highPriestVenoxisAssistant = raidLeader.getHighPriestVenoxisAssistant(); Unit* const boss = highPriestVenoxisAssistant.findActiveBoss(*this->bot); diff --git a/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/VenoxisPositioning/HighPriestVenoxisPhase1VenoxisPositioningTrigger.h b/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/VenoxisPositioning/HighPriestVenoxisPhase1VenoxisPositioningTrigger.h index 8141479f845..2b12b3f7355 100644 --- a/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/VenoxisPositioning/HighPriestVenoxisPhase1VenoxisPositioningTrigger.h +++ b/src/Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/Phase1/VenoxisPositioning/HighPriestVenoxisPhase1VenoxisPositioningTrigger.h @@ -29,7 +29,7 @@ class HighPriestVenoxisPhase1VenoxisPositioningTrigger : public Trigger const uint32_t instanceId = this->bot->GetInstanceId(); RaidLeaderRegistry& raidRegistry = RaidLeaderRegistry::GetInstance(); - const ZulGurubRaidLeader& raidLeader = raidRegistry.getOrBind(instanceId, this->bot->GetMapId()); + const ZulGurubRaidLeader& raidLeader = raidRegistry.getOrBind(instanceId, MAP_ZUL_GURUB); const HighPriestVenoxisAssistant& highPriestVenoxisAssistant = raidLeader.getHighPriestVenoxisAssistant(); if (highPriestVenoxisAssistant.isInCombatWithVenoxis(*this->bot) == false) diff --git a/src/Ai/Raid/ZulGurub/RaidZGStrategy.h b/src/Ai/Raid/ZulGurub/RaidZGStrategy.h index 9d9b7219bc2..586d928e42e 100644 --- a/src/Ai/Raid/ZulGurub/RaidZGStrategy.h +++ b/src/Ai/Raid/ZulGurub/RaidZGStrategy.h @@ -2,6 +2,8 @@ #include "Boss/HighPriestVenoxis/Phase1/HolyWrath/HighPriestVenoxisPhase1HolyWrathAction.h" #include "Boss/HighPriestVenoxis/Phase1/HolyWrath/HighPriestVenoxisPhase1HolyWrathMultiplier.h" +#include "Boss/GrilekTheWanderer/mechanics/Avatar/GrilekTheWandererAvatarAction.h" +#include "Boss/GrilekTheWanderer/mechanics/Avatar/GrilekTheWandererAvatarMultiplier.h" #include "Boss/HighPriestVenoxis/Phase1/RazzashiCobrasDPSPriority/HighPriestVenoxisPhase1RazzashiCobrasDPSPriorityAction.h" #include "Boss/HighPriestVenoxis/Phase1/RazzashiCobrasPositioning/HighPriestVenoxisPhase1RazzashiCobrasPositioningAction.h" #include "Boss/HighPriestVenoxis/Phase1/VenoxisPositioning/HighPriestVenoxisPhase1VenoxisPositioningAction.h" @@ -78,6 +80,17 @@ class RaidZGStrategy : public Strategy ) ); + // Gri'lek The Wanderer + + triggers.push_back( + new TriggerNode( + "grilek the wanderer avatar", + { + CreateNextAction(ACTION_EMERGENCY + 10.0f) + } + ) + ); + // Hakkar the Soulflayer triggers.push_back( new TriggerNode( @@ -145,6 +158,9 @@ class RaidZGStrategy : public Strategy // High Priest Venoxis multipliers.push_back(new HighPriestVenoxisPhase1HolyWrathMultiplier(this->botAI)); + // Gri'lek The Wanderer + multipliers.push_back(new GrilekTheWandererAvatarMultiplier(this->botAI)); + // Hakkar the Soulflayer multipliers.push_back(new HakkarTheSoulflayerPoisonousBloodMultiplier(this->botAI)); multipliers.push_back(new HakkarTheSoulflayerGoToPoisonousCloudMultiplier(this->botAI)); diff --git a/src/Ai/Raid/ZulGurub/leader/ZulGurubRaidLeader.h b/src/Ai/Raid/ZulGurub/leader/ZulGurubRaidLeader.h index 1e0e5c44796..9f4261729be 100644 --- a/src/Ai/Raid/ZulGurub/leader/ZulGurubRaidLeader.h +++ b/src/Ai/Raid/ZulGurub/leader/ZulGurubRaidLeader.h @@ -5,11 +5,13 @@ #include "Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/assistant/HighPriestVenoxisAssistant.h" #include "Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/assistant/hakkar-the-soulflayer/HakkarTheSoulflayerAssistant.h" #include "Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/assistant/son-of-hakkar/SonOfHakkarAssistant.h" +#include "GrilekTheWanderer/assistant/GrilekTheWandererAssistant.h" class ZulGurubRaidLeader : public BaseRaidLeader { private: HighPriestVenoxisAssistant highPriestVenoxisAssistant{}; + GrilekTheWandererAssistant grilekTheWandererAssistant{}; HakkarTheSoulflayerAssistant hakkarTheSoulflayerAssistant{}; SonOfHakkarAssistant sonOfHakkarAssistant{}; @@ -22,6 +24,11 @@ class ZulGurubRaidLeader : public BaseRaidLeader return this->highPriestVenoxisAssistant; } + [[nodiscard]] const GrilekTheWandererAssistant& getGrilekTheWandererAssistant() const noexcept + { + return this->grilekTheWandererAssistant; + } + [[nodiscard]] HakkarTheSoulflayerAssistant& getHakkarTheSoulflayerAssistant() noexcept { return this->hakkarTheSoulflayerAssistant; From bdfb86d5f5164d220a4165c8cf571b261fb1744d Mon Sep 17 00:00:00 2001 From: SmashingQuasar Date: Thu, 26 Feb 2026 16:27:55 +0100 Subject: [PATCH 5/8] fix: Corrected Gri'lek strategy. --- .../assistant/GrilekTheWandererAssistant.h | 76 +++++++++++-------- .../GrilekTheWandererAvatarMultiplier.h | 5 -- src/Ai/Raid/ZulGurub/RaidZGTriggerContext.h | 10 +++ 3 files changed, 55 insertions(+), 36 deletions(-) diff --git a/src/Ai/Raid/ZulGurub/Boss/GrilekTheWanderer/assistant/GrilekTheWandererAssistant.h b/src/Ai/Raid/ZulGurub/Boss/GrilekTheWanderer/assistant/GrilekTheWandererAssistant.h index 081982775fa..9f67ec7edc6 100644 --- a/src/Ai/Raid/ZulGurub/Boss/GrilekTheWanderer/assistant/GrilekTheWandererAssistant.h +++ b/src/Ai/Raid/ZulGurub/Boss/GrilekTheWanderer/assistant/GrilekTheWandererAssistant.h @@ -15,6 +15,8 @@ #include +#include "PlayerbotAIConfig.h" + class GrilekTheWandererAssistant { public: @@ -41,46 +43,58 @@ class GrilekTheWandererAssistant [[nodiscard]] Creature* findActiveBoss(Player& bot) const noexcept { - if (!bot.IsInCombat()) - { - return nullptr; - } + return bot.FindNearestCreature(uint32_t(GrilekTheWandererEnum::ENTRY), PlayerbotAIConfig::instance().sightDistance, true); + } - Map* map = bot.GetMap(); + // [[nodiscard]] Creature* findActiveBoss(Player& bot) const noexcept + // { + // if (!bot.IsInCombat()) + // { + // return nullptr; + // } - if (map == nullptr) - { - return nullptr; - } + // Map* map = bot.GetMap(); - const std::unordered_multimap store = map->GetCreatureBySpawnIdStore(); - const std::unordered_multimap::const_iterator it = store.find(uint32_t(GrilekTheWandererEnum::ENTRY)); - const std::unordered_multimap::const_iterator end = store.end(); + // if (map == nullptr) + // { + // return nullptr; + // } - if (it == end) - { - return nullptr; - } + // const std::unordered_multimap store = map->GetCreatureBySpawnIdStore(); + // const std::unordered_multimap::const_iterator it = store.find(uint32_t(GrilekTheWandererEnum::ENTRY)); + // const std::unordered_multimap::const_iterator end = store.end(); - Creature* grilekTheWanderer = it->second; + // if (it == end) + // { + // LOG_ERROR("playerbots") - if (grilekTheWanderer == nullptr) - { - return nullptr; - } + // return nullptr; + // } - if (!grilekTheWanderer->IsAlive()) - { - return nullptr; - } + // Creature* grilekTheWanderer = it->second; - if (grilekTheWanderer->GetEntry() != uint32_t(GrilekTheWandererEnum::ENTRY)) - { - return nullptr; - } + // if (grilekTheWanderer == nullptr) + // { + // LOG_ERROR("playerbots", "Grilek not found in map."); - return grilekTheWanderer; - }; + // return nullptr; + // } + + // if (!grilekTheWanderer->IsAlive()) + // { + // LOG_ERROR("playerbots", "Grilek dead"); + // return nullptr; + // } + + // if (grilekTheWanderer->GetEntry() != uint32_t(GrilekTheWandererEnum::ENTRY)) + // { + // LOG_ERROR("playerbots", "Grilek entry does not match."); + + // return nullptr; + // } + + // return grilekTheWanderer; + // }; [[nodiscard]] bool isAtSafeDistanceFromGrilekTheWanderer(Player& bot) const noexcept { diff --git a/src/Ai/Raid/ZulGurub/Boss/GrilekTheWanderer/mechanics/Avatar/GrilekTheWandererAvatarMultiplier.h b/src/Ai/Raid/ZulGurub/Boss/GrilekTheWanderer/mechanics/Avatar/GrilekTheWandererAvatarMultiplier.h index ab112b30e22..7dff87c3f5e 100644 --- a/src/Ai/Raid/ZulGurub/Boss/GrilekTheWanderer/mechanics/Avatar/GrilekTheWandererAvatarMultiplier.h +++ b/src/Ai/Raid/ZulGurub/Boss/GrilekTheWanderer/mechanics/Avatar/GrilekTheWandererAvatarMultiplier.h @@ -22,11 +22,6 @@ class GrilekTheWandererAvatarMultiplier : public Multiplier return 0.0f; } - if (this->botAI->IsTank(this->bot) == true) - { - return 1.0f; - } - const uint32_t instanceId = this->bot->GetInstanceId(); RaidLeaderRegistry& raidRegistry = RaidLeaderRegistry::GetInstance(); const ZulGurubRaidLeader& raidLeader = raidRegistry.getOrBind(instanceId, MAP_ZUL_GURUB); diff --git a/src/Ai/Raid/ZulGurub/RaidZGTriggerContext.h b/src/Ai/Raid/ZulGurub/RaidZGTriggerContext.h index f0bfe342e81..ae8d35b0350 100644 --- a/src/Ai/Raid/ZulGurub/RaidZGTriggerContext.h +++ b/src/Ai/Raid/ZulGurub/RaidZGTriggerContext.h @@ -1,6 +1,7 @@ #pragma once #include "AiObjectContext.h" +#include "GrilekTheWandererAvatarTrigger.h" #include "HakkarTheSoulflayer/mechanics/son-of-hakkar/BringBackSonOfHakkar/HakkarTheSoulflayerBringBackSonOfHakkarTrigger.h" #include "HakkarTheSoulflayer/mechanics/blood-siphon/GoToPoisonousCloud/HakkarTheSoulflayerGoToPoisonousCloudTrigger.h" #include "HakkarTheSoulflayer/mechanics/cause-insanity/CauseInsanity/HakkarTheSoulflayerCauseInsanityTrigger.h" @@ -28,6 +29,9 @@ class RaidZGTriggerContext : public NamedObjectContext creators["high priest venoxis phase 1 razzashi cobras positioning"] = &RaidZGTriggerContext::highPriestVenoxisPhase1RazzashiCobrasPositioning; creators["high priest venoxis phase 1 razzashi cobras dps priority"] = &RaidZGTriggerContext::highPriestVenoxisPhase1RazzashiCobrasDPSPriority; + // Gri'lek the Wanderer + creators["grilek the wanderer avatar"] = &RaidZGTriggerContext::grilekTheWandererAvatarTrigger; + // Hakkar the Soulflayer creators["hakkar the soulflayer go to poisonous cloud"] = &RaidZGTriggerContext::hakkarTheSoulflayerGoToPoisonousCloud; creators["hakkar the soulflayer cause insanity"] = &RaidZGTriggerContext::hakkarTheSoulflayerCauseInsanity; @@ -66,6 +70,12 @@ class RaidZGTriggerContext : public NamedObjectContext return new HighPriestVenoxisPhase1RazzashiCobrasDPSPriorityTrigger(botAI); } + // Gri'lek the Wanderer + static Trigger* grilekTheWandererAvatarTrigger(PlayerbotAI* botAI) + { + return new GrilekTheWandererAvatarTrigger(botAI); + } + // Hakkar the Soulflayer static Trigger* hakkarTheSoulflayerGoToPoisonousCloud(PlayerbotAI* botAI) { From 393652bdd852b43e37d590dc01fd583c29487cef Mon Sep 17 00:00:00 2001 From: SmashingQuasar Date: Fri, 27 Feb 2026 21:04:03 +0100 Subject: [PATCH 6/8] feat: Added Bloodlord Mandokir & High Priest Thekal strategies. --- .../assistant/BloodlordMandokirAssistant.h | 61 +++++++++++ .../definition/enum/BloodlordMandokirEnum.h | 11 ++ .../BloodlordMandokirWhirlwindAction.h | 47 ++++++++ .../BloodlordMandokirWhirlwindMultiplier.h | 57 ++++++++++ .../BloodlordMandokirWhirlwindTrigger.h | 56 ++++++++++ .../HighPriestThekalHealthBalanceAction.h | 45 ++++++++ .../HighPriestThekalHealthBalanceTrigger.h | 90 ++++++++++++++++ .../HighPriestThekalAssistant.h | 101 ++++++++++++++++++ .../definition/enum/HighPriestThekalEnum.h | 11 ++ .../definition/enum/ZealotLorkhanEnum.h | 9 ++ .../definition/enum/ZealotZathEnum.h | 9 ++ src/Ai/Raid/ZulGurub/RaidZGStrategy.h | 23 ++++ src/Ai/Raid/ZulGurub/RaidZGTriggerContext.h | 20 ++++ .../Raid/ZulGurub/leader/ZulGurubRaidLeader.h | 14 +++ .../assistant/base-assistant/BaseAssistant.h | 52 +++++++++ 15 files changed, 606 insertions(+) create mode 100644 src/Ai/Raid/ZulGurub/Boss/BloodlordMandokir/assistant/BloodlordMandokirAssistant.h create mode 100644 src/Ai/Raid/ZulGurub/Boss/BloodlordMandokir/definition/enum/BloodlordMandokirEnum.h create mode 100644 src/Ai/Raid/ZulGurub/Boss/BloodlordMandokir/mechanics/Whirlwind/BloodlordMandokirWhirlwindAction.h create mode 100644 src/Ai/Raid/ZulGurub/Boss/BloodlordMandokir/mechanics/Whirlwind/BloodlordMandokirWhirlwindMultiplier.h create mode 100644 src/Ai/Raid/ZulGurub/Boss/BloodlordMandokir/mechanics/Whirlwind/BloodlordMandokirWhirlwindTrigger.h create mode 100644 src/Ai/Raid/ZulGurub/Boss/HighPriestThekal/Phase1/HealthBalance/HighPriestThekalHealthBalanceAction.h create mode 100644 src/Ai/Raid/ZulGurub/Boss/HighPriestThekal/Phase1/HealthBalance/HighPriestThekalHealthBalanceTrigger.h create mode 100644 src/Ai/Raid/ZulGurub/Boss/HighPriestThekal/assistant/high-priest-thekal/HighPriestThekalAssistant.h create mode 100644 src/Ai/Raid/ZulGurub/Boss/HighPriestThekal/definition/enum/HighPriestThekalEnum.h create mode 100644 src/Ai/Raid/ZulGurub/Boss/HighPriestThekal/definition/enum/ZealotLorkhanEnum.h create mode 100644 src/Ai/Raid/ZulGurub/Boss/HighPriestThekal/definition/enum/ZealotZathEnum.h create mode 100644 src/domain/core/raid/assistant/base-assistant/BaseAssistant.h diff --git a/src/Ai/Raid/ZulGurub/Boss/BloodlordMandokir/assistant/BloodlordMandokirAssistant.h b/src/Ai/Raid/ZulGurub/Boss/BloodlordMandokir/assistant/BloodlordMandokirAssistant.h new file mode 100644 index 00000000000..7d31437de3b --- /dev/null +++ b/src/Ai/Raid/ZulGurub/Boss/BloodlordMandokir/assistant/BloodlordMandokirAssistant.h @@ -0,0 +1,61 @@ +#pragma once + +#include "Unit.h" +#include "Player.h" +#include "Spell.h" + +#include "GridNotifiers.h" +// Necessary due to a poor implementation by AC. +#include "GridNotifiersImpl.h" + +#include "Cell.h" +#include "CellImpl.h" + +#include "../definition/enum/BloodlordMandokirEnum.h" + + +#include "PlayerbotAIConfig.h" + +class BloodlordMandokirAssistant +{ +public: + BloodlordMandokirAssistant() = default; + ~BloodlordMandokirAssistant() = default; + + BloodlordMandokirAssistant(const BloodlordMandokirAssistant&) = default; + BloodlordMandokirAssistant& operator=(const BloodlordMandokirAssistant&) = default; + + BloodlordMandokirAssistant(BloodlordMandokirAssistant&&) = default; + BloodlordMandokirAssistant& operator=(BloodlordMandokirAssistant&&) = default; + + [[nodiscard]] bool isInCombatWithBloodlordMandokir(Player& bot) const noexcept + { + const Unit* const BloodlordMandokir = this->findActiveBoss(bot); + + if (BloodlordMandokir == nullptr) + { + return false; + } + + return bot.IsInCombatWith(BloodlordMandokir) || BloodlordMandokir->IsInCombatWith(&bot); + } + + [[nodiscard]] Creature* findActiveBoss(Player& bot) const noexcept + { + return bot.FindNearestCreature(uint32_t(BloodlordMandokirEnum::ENTRY), PlayerbotAIConfig::instance().sightDistance, true); + } + + [[nodiscard]] bool isAtSafeDistanceFromBloodlordMandokir(Player& bot) const noexcept + { + const Unit* const BloodlordMandokir = this->findActiveBoss(bot); + + if (BloodlordMandokir == nullptr) + { + return true; + } + + const float distanceToBloodlordMandokir = bot.GetExactDist2d(BloodlordMandokir->GetPositionX(), BloodlordMandokir->GetPositionY()); + + return distanceToBloodlordMandokir > float(BloodlordMandokirEnum::SPELL_WHIRLWIND_SAFE_DISTANCE); + }; +}; diff --git a/src/Ai/Raid/ZulGurub/Boss/BloodlordMandokir/definition/enum/BloodlordMandokirEnum.h b/src/Ai/Raid/ZulGurub/Boss/BloodlordMandokir/definition/enum/BloodlordMandokirEnum.h new file mode 100644 index 00000000000..fb4951864b8 --- /dev/null +++ b/src/Ai/Raid/ZulGurub/Boss/BloodlordMandokir/definition/enum/BloodlordMandokirEnum.h @@ -0,0 +1,11 @@ +#pragma once + +#include + +enum class BloodlordMandokirEnum : uint32_t +{ + ENTRY = 11382u, + SPELL_THREATENING_GAZE = 24314u, + SPELL_WHIRLWIND = 13736u, + SPELL_WHIRLWIND_SAFE_DISTANCE = 15u +}; diff --git a/src/Ai/Raid/ZulGurub/Boss/BloodlordMandokir/mechanics/Whirlwind/BloodlordMandokirWhirlwindAction.h b/src/Ai/Raid/ZulGurub/Boss/BloodlordMandokir/mechanics/Whirlwind/BloodlordMandokirWhirlwindAction.h new file mode 100644 index 00000000000..ffe0d4a3c8d --- /dev/null +++ b/src/Ai/Raid/ZulGurub/Boss/BloodlordMandokir/mechanics/Whirlwind/BloodlordMandokirWhirlwindAction.h @@ -0,0 +1,47 @@ +#pragma once + +#include "Spell.h" +#include "AiObjectContext.h" +#include "MovementActions.h" +#include "PlayerbotAI.h" + +#include "raid/leader/RaidLeaderRegistry.h" + +class BloodlordMandokirWhirlwindAction : public MovementAction +{ +public: + BloodlordMandokirWhirlwindAction( + PlayerbotAI* botAI, + const std::string name = "bloodlord mandokir whirlwind" + ) : MovementAction(botAI, name) {} + + bool Execute(Event) override + { + if (this->bot == nullptr) + { + return false; + } + + const uint32_t instanceId = this->bot->GetInstanceId(); + RaidLeaderRegistry& raidRegistry = RaidLeaderRegistry::GetInstance(); + const ZulGurubRaidLeader& raidLeader = raidRegistry.getOrBind(instanceId, MAP_ZUL_GURUB); + const BloodlordMandokirAssistant& bloodlordMandokirAssistant = raidLeader.getBloodlordMandokirAssistant(); + Unit* const bloodlordMandokir = bloodlordMandokirAssistant.findActiveBoss(*this->bot); + + if (bloodlordMandokir == nullptr) + { + return false; + } + + const float safeDistance = float(BloodlordMandokirEnum::SPELL_WHIRLWIND_SAFE_DISTANCE) - this->bot->GetDistance2d(bloodlordMandokir); + + if (safeDistance <= 0.0f) + { + return false; + } + + this->MoveAway(bloodlordMandokir, safeDistance); + + return true; + } +}; diff --git a/src/Ai/Raid/ZulGurub/Boss/BloodlordMandokir/mechanics/Whirlwind/BloodlordMandokirWhirlwindMultiplier.h b/src/Ai/Raid/ZulGurub/Boss/BloodlordMandokir/mechanics/Whirlwind/BloodlordMandokirWhirlwindMultiplier.h new file mode 100644 index 00000000000..fb45e03f02c --- /dev/null +++ b/src/Ai/Raid/ZulGurub/Boss/BloodlordMandokir/mechanics/Whirlwind/BloodlordMandokirWhirlwindMultiplier.h @@ -0,0 +1,57 @@ +#pragma once + +#include "AiObjectContext.h" +#include "BloodlordMandokirWhirlwindAction.h" +#include "MovementActions.h" +#include "Multiplier.h" +#include "PlayerbotAI.h" +#include "Value.h" + +#include "GenericSpellActions.h" +#include "raid/leader/RaidLeaderRegistry.h" + + +class BloodlordMandokirWhirlwindMultiplier : public Multiplier +{ +public: + BloodlordMandokirWhirlwindMultiplier(PlayerbotAI* botAI) : Multiplier(botAI, "bloodlord mandokir whirlwind") {} + + float GetValue(Action& action) override + { + if (this->bot == nullptr) + { + return 0.0f; + } + + const uint32_t instanceId = this->bot->GetInstanceId(); + RaidLeaderRegistry& raidRegistry = RaidLeaderRegistry::GetInstance(); + const ZulGurubRaidLeader& raidLeader = raidRegistry.getOrBind(instanceId, MAP_ZUL_GURUB); + const BloodlordMandokirAssistant& bloodlordMandokirAssistant = raidLeader.getBloodlordMandokirAssistant(); + + const Creature* const bloodlordMandokir = bloodlordMandokirAssistant.findActiveBoss(*this->bot); + + if (bloodlordMandokir == nullptr) + { + return 1.0f; + } + + if (bloodlordMandokir->HasAura(uint32_t(BloodlordMandokirEnum::SPELL_WHIRLWIND)) == false) + { + return 1.0f; + } + + if (bloodlordMandokirAssistant.isAtSafeDistanceFromBloodlordMandokir(*this->bot) == true) + { + return 1.0f; + } + + const BloodlordMandokirWhirlwindAction* const movementAction = dynamic_cast(&action); + + if (movementAction != nullptr) + { + return 1.0f; + } + + return 0.0f; + } +}; diff --git a/src/Ai/Raid/ZulGurub/Boss/BloodlordMandokir/mechanics/Whirlwind/BloodlordMandokirWhirlwindTrigger.h b/src/Ai/Raid/ZulGurub/Boss/BloodlordMandokir/mechanics/Whirlwind/BloodlordMandokirWhirlwindTrigger.h new file mode 100644 index 00000000000..9fc15a94253 --- /dev/null +++ b/src/Ai/Raid/ZulGurub/Boss/BloodlordMandokir/mechanics/Whirlwind/BloodlordMandokirWhirlwindTrigger.h @@ -0,0 +1,56 @@ +#pragma once + +#include "AiObjectContext.h" +#include "BloodlordMandokir/assistant/BloodlordMandokirAssistant.h" +#include "PlayerbotAI.h" +#include "Trigger.h" + +#include "raid/leader/RaidLeaderRegistry.h" + +class BloodlordMandokirWhirlwindTrigger : public Trigger +{ +public: + BloodlordMandokirWhirlwindTrigger(PlayerbotAI* botAI) : Trigger(botAI, "bloodlord mandokir whirlwind") {} + + bool IsActive() override + { + if (this->bot == nullptr) + { + return false; + } + + if (this->botAI == nullptr) + { + return false; + } + + const uint32_t instanceId = this->bot->GetInstanceId(); + RaidLeaderRegistry& raidRegistry = RaidLeaderRegistry::GetInstance(); + const ZulGurubRaidLeader& raidLeader = raidRegistry.getOrBind(instanceId, MAP_ZUL_GURUB); + const BloodlordMandokirAssistant& bloodlordMandokirAssistant = raidLeader.getBloodlordMandokirAssistant(); + + if (bloodlordMandokirAssistant.isInCombatWithBloodlordMandokir(*this->bot) == false) + { + return false; + } + + const Creature* const bloodlordMandokir = bloodlordMandokirAssistant.findActiveBoss(*this->bot); + + if (bloodlordMandokir == nullptr) + { + return false; + } + + if (bloodlordMandokir->HasAura(uint32_t(BloodlordMandokirEnum::SPELL_WHIRLWIND)) == false) + { + return false; + } + + if (bloodlordMandokirAssistant.isAtSafeDistanceFromBloodlordMandokir(*this->bot) == true) + { + return false; + } + + return true; + } +}; diff --git a/src/Ai/Raid/ZulGurub/Boss/HighPriestThekal/Phase1/HealthBalance/HighPriestThekalHealthBalanceAction.h b/src/Ai/Raid/ZulGurub/Boss/HighPriestThekal/Phase1/HealthBalance/HighPriestThekalHealthBalanceAction.h new file mode 100644 index 00000000000..3005ec5f25d --- /dev/null +++ b/src/Ai/Raid/ZulGurub/Boss/HighPriestThekal/Phase1/HealthBalance/HighPriestThekalHealthBalanceAction.h @@ -0,0 +1,45 @@ +#pragma once + +#include "Pet.h" +#include "Spell.h" +#include "AiObjectContext.h" +#include "AttackAction.h" +#include "PlayerbotAI.h" + +#include "raid/leader/RaidLeaderRegistry.h" + +class HighPriestThekalHealthBalanceAction : public AttackAction +{ +public: + HighPriestThekalHealthBalanceAction( + PlayerbotAI* botAI, + const std::string name = "high priest thekal phase 1 health balance" + ) : AttackAction(botAI, name) {} + + bool Execute(Event) override + { + if (this->bot == nullptr) + { + return false; + } + + const uint32_t instanceId = this->bot->GetInstanceId(); + RaidLeaderRegistry& raidRegistry = RaidLeaderRegistry::GetInstance(); + ZulGurubRaidLeader& raidLeader = raidRegistry.getOrBind(instanceId, MAP_ZUL_GURUB); + HighPriestThekalAssistant& highPriestThekalAssistant = raidLeader.getHighPriestThekalAssistant(); + std::array bosses = highPriestThekalAssistant.getActiveBossesSortedByHealth(*this->bot); + + Value* const rtiTargetValue = this->context->GetValue("rti target"); + + if (rtiTargetValue == nullptr) + { + return false; + } + + MarkTargetWithSkull(this->bot, bosses[0]); + + rtiTargetValue->Set(bosses[0]); + + return true; + } +}; diff --git a/src/Ai/Raid/ZulGurub/Boss/HighPriestThekal/Phase1/HealthBalance/HighPriestThekalHealthBalanceTrigger.h b/src/Ai/Raid/ZulGurub/Boss/HighPriestThekal/Phase1/HealthBalance/HighPriestThekalHealthBalanceTrigger.h new file mode 100644 index 00000000000..60c2c909317 --- /dev/null +++ b/src/Ai/Raid/ZulGurub/Boss/HighPriestThekal/Phase1/HealthBalance/HighPriestThekalHealthBalanceTrigger.h @@ -0,0 +1,90 @@ +#pragma once + +#include "AiObjectContext.h" +#include "PlayerbotAI.h" +#include "Trigger.h" + +#include "raid/leader/RaidLeaderRegistry.h" + +class HighPriestThekalHealthBalanceTrigger : public Trigger +{ +public: + HighPriestThekalHealthBalanceTrigger(PlayerbotAI* botAI) : Trigger(botAI, "high priest thekal phase 1 health balance") {} + + bool IsActive() override + { + if (this->bot == nullptr) + { + return false; + } + + if (this->botAI == nullptr) + { + return false; + } + + if (PlayerbotAI::IsDps(this->bot) == false) + { + return false; + } + + const uint32_t instanceId = this->bot->GetInstanceId(); + RaidLeaderRegistry& raidRegistry = RaidLeaderRegistry::GetInstance(); + ZulGurubRaidLeader& raidLeader = raidRegistry.getOrBind(instanceId, MAP_ZUL_GURUB); + HighPriestThekalAssistant& highPriestThekalAssistant = raidLeader.getHighPriestThekalAssistant(); + + if (highPriestThekalAssistant.isInCombatWithHighPriestThekal(*this->bot) == false) + { + return false; + } + + if (highPriestThekalAssistant.isInPhase1(*this->bot) == false) + { + return false; + } + + const std::array bosses = highPriestThekalAssistant.getActiveBossesSortedByHealth(*this->bot); + + Creature* const highestHealthBoss = bosses[0]; + + if (highestHealthBoss == nullptr) + { + return false; + } + + const Creature* const secondHighestHealthBoss = bosses[1]; + + if (secondHighestHealthBoss == nullptr) + { + return false; + } + + const uint32_t healthDifference = highestHealthBoss->GetHealth() - secondHighestHealthBoss->GetHealth(); + + if (healthDifference < uint32_t(HighPriestThekalEnum::MAXIMUM_HEALTH_DIFFERENCE)) + { + return false; + } + + if (this->bot->GetTarget() == highestHealthBoss->GetGUID()) + { + return false; + } + + Value* const rtiTargetValue = this->context->GetValue("rti target"); + + if (rtiTargetValue == nullptr) + { + return false; + } + + const Unit* const rtiTarget = rtiTargetValue->Get(); + + if (rtiTarget != nullptr && rtiTarget->GetGUID() == highestHealthBoss->GetGUID()) + { + return false; + } + + return true; + } +}; diff --git a/src/Ai/Raid/ZulGurub/Boss/HighPriestThekal/assistant/high-priest-thekal/HighPriestThekalAssistant.h b/src/Ai/Raid/ZulGurub/Boss/HighPriestThekal/assistant/high-priest-thekal/HighPriestThekalAssistant.h new file mode 100644 index 00000000000..bc2a8f388e3 --- /dev/null +++ b/src/Ai/Raid/ZulGurub/Boss/HighPriestThekal/assistant/high-priest-thekal/HighPriestThekalAssistant.h @@ -0,0 +1,101 @@ +#pragma once + +#include +#include + +#include "HighPriestThekal/definition/enum/ZealotLorkhanEnum.h" +#include "HighPriestThekal/definition/enum/ZealotZathEnum.h" +#include "Unit.h" +#include "Player.h" +#include "Spell.h" + +#include "GridNotifiers.h" +// Necessary due to a poor implementation by AC. +#include "GridNotifiersImpl.h" + +#include "../../definition/enum/HighPriestThekalEnum.h" +#include "domain/core/raid/assistant/base-assistant/BaseAssistant.h" + + +class HighPriestThekalAssistant : public BaseAssistant +{ +public: + HighPriestThekalAssistant() = default; + ~HighPriestThekalAssistant() = default; + + HighPriestThekalAssistant(const HighPriestThekalAssistant&) = default; + HighPriestThekalAssistant& operator=(const HighPriestThekalAssistant&) = default; + + HighPriestThekalAssistant(HighPriestThekalAssistant&&) = default; + HighPriestThekalAssistant& operator=(HighPriestThekalAssistant&&) = default; + + [[nodiscard]] bool isInCombatWithHighPriestThekal(Player& bot) const noexcept + { + const Creature* const highPriestThekal = this->findActiveEnemyByDBGUID(bot, uint32_t(HighPriestThekalEnum::GUID), uint32_t(HighPriestThekalEnum::ENTRY)); + + if (highPriestThekal == nullptr) + { + return false; + } + + return bot.IsInCombatWith(highPriestThekal) || highPriestThekal->IsInCombatWith(&bot); + } + + [[nodiscard]] std::array findActiveBosses(Player& bot) const noexcept + { + Creature* const highPriestThekal = this->findActiveEnemyByDBGUID(bot, uint32_t(HighPriestThekalEnum::GUID), uint32_t(HighPriestThekalEnum::ENTRY)); + Creature* const zealotLorkhan = this->findActiveEnemyByDBGUID(bot, uint32_t(ZealotLorkhanEnum::GUID), uint32_t(ZealotLorkhanEnum::ENTRY)); + Creature* const zealotZath = this->findActiveEnemyByDBGUID(bot, uint32_t(ZealotZathEnum::GUID), uint32_t(ZealotZathEnum::ENTRY)); + + return { highPriestThekal, zealotLorkhan, zealotZath }; + } + + [[nodiscard]] std::array getActiveBossesSortedByHealth(Player& bot) const noexcept + { + std::array bosses = this->findActiveBosses(bot); + + if (bosses[0] == nullptr || bosses[1] == nullptr || bosses[2] == nullptr) + { + return bosses; + } + + if (bosses[0]->GetHealth() < bosses[1]->GetHealth()) + { + std::swap(bosses[0], bosses[1]); + } + + if (bosses[1]->GetHealth() < bosses[2]->GetHealth()) + { + std::swap(bosses[1], bosses[2]); + } + + if (bosses[0]->GetHealth() < bosses[1]->GetHealth()) + { + std::swap(bosses[0], bosses[1]); + } + + return bosses; + } + + [[nodiscard]] bool isInPhase1(Player& bot) const noexcept + { + const Creature* const unit = this->findActiveEnemyByDBGUID(bot, uint32_t(HighPriestThekalEnum::GUID), uint32_t(HighPriestThekalEnum::ENTRY)); + + if (unit == nullptr) + { + return false; + } + + if (unit->GetMapId() != MAP_ZUL_GURUB) + { + return false; + } + + if (!unit->IsInCombat()) + { + return false; + } + + return unit->HasAura(uint32_t(HighPriestThekalEnum::SPELL_TIGER_FORM)) == false; + }; +}; diff --git a/src/Ai/Raid/ZulGurub/Boss/HighPriestThekal/definition/enum/HighPriestThekalEnum.h b/src/Ai/Raid/ZulGurub/Boss/HighPriestThekal/definition/enum/HighPriestThekalEnum.h new file mode 100644 index 00000000000..af8e666f993 --- /dev/null +++ b/src/Ai/Raid/ZulGurub/Boss/HighPriestThekal/definition/enum/HighPriestThekalEnum.h @@ -0,0 +1,11 @@ +#pragma once + +#include + +enum class HighPriestThekalEnum : uint32_t +{ + ENTRY = 14509u, + GUID = 49310u, + SPELL_TIGER_FORM = 24169u, + MAXIMUM_HEALTH_DIFFERENCE = 5000u, +}; diff --git a/src/Ai/Raid/ZulGurub/Boss/HighPriestThekal/definition/enum/ZealotLorkhanEnum.h b/src/Ai/Raid/ZulGurub/Boss/HighPriestThekal/definition/enum/ZealotLorkhanEnum.h new file mode 100644 index 00000000000..ec557521168 --- /dev/null +++ b/src/Ai/Raid/ZulGurub/Boss/HighPriestThekal/definition/enum/ZealotLorkhanEnum.h @@ -0,0 +1,9 @@ +#pragma once + +#include + +enum class ZealotLorkhanEnum : uint32_t +{ + ENTRY = 11347u, + GUID = 49314u, +}; diff --git a/src/Ai/Raid/ZulGurub/Boss/HighPriestThekal/definition/enum/ZealotZathEnum.h b/src/Ai/Raid/ZulGurub/Boss/HighPriestThekal/definition/enum/ZealotZathEnum.h new file mode 100644 index 00000000000..b08fdb5dee9 --- /dev/null +++ b/src/Ai/Raid/ZulGurub/Boss/HighPriestThekal/definition/enum/ZealotZathEnum.h @@ -0,0 +1,9 @@ +#pragma once + +#include + +enum class ZealotZathEnum : uint32_t +{ + ENTRY = 11348u, + GUID = 49313u, +}; diff --git a/src/Ai/Raid/ZulGurub/RaidZGStrategy.h b/src/Ai/Raid/ZulGurub/RaidZGStrategy.h index 586d928e42e..60d7cf9194f 100644 --- a/src/Ai/Raid/ZulGurub/RaidZGStrategy.h +++ b/src/Ai/Raid/ZulGurub/RaidZGStrategy.h @@ -1,5 +1,6 @@ #pragma once +#include "BloodlordMandokirWhirlwindAction.h" #include "Boss/HighPriestVenoxis/Phase1/HolyWrath/HighPriestVenoxisPhase1HolyWrathAction.h" #include "Boss/HighPriestVenoxis/Phase1/HolyWrath/HighPriestVenoxisPhase1HolyWrathMultiplier.h" #include "Boss/GrilekTheWanderer/mechanics/Avatar/GrilekTheWandererAvatarAction.h" @@ -17,6 +18,7 @@ #include "HakkarTheSoulflayer/mechanics/son-of-hakkar/MoveToSonOfHakkar/HakkarTheSoulflayerMoveToSonOfHakkarAction.h" #include "HakkarTheSoulflayer/mechanics/blood-siphon/PoisonousBlood/HakkarTheSoulflayerPoisonousBloodMultiplier.h" #include "HakkarTheSoulflayer/mechanics/son-of-hakkar/PullSonOfHakkar/HakkarTheSoulflayerPullSonOfHakkarAction.h" +#include "HighPriestThekal/Phase1/HealthBalance/HighPriestThekalHealthBalanceAction.h" #include "Strategy.h" #include "Multiplier.h" #include "Trash/GurubashiBatRider/GurubashiBatRiderUnstableConcoctionAction.h" @@ -80,6 +82,17 @@ class RaidZGStrategy : public Strategy ) ); + // Bloodlord Mandokir + + triggers.push_back( + new TriggerNode( + "bloodlord mandokir whirlwind", + { + CreateNextAction(ACTION_EMERGENCY + 10.0f) + } + ) + ); + // Gri'lek The Wanderer triggers.push_back( @@ -91,6 +104,16 @@ class RaidZGStrategy : public Strategy ) ); + // High Priest Thekal + triggers.push_back( + new TriggerNode( + "high priest thekal phase 1 health balance", + { + CreateNextAction(ACTION_EMERGENCY + 10.0f) + } + ) + ); + // Hakkar the Soulflayer triggers.push_back( new TriggerNode( diff --git a/src/Ai/Raid/ZulGurub/RaidZGTriggerContext.h b/src/Ai/Raid/ZulGurub/RaidZGTriggerContext.h index ae8d35b0350..2011ab862d9 100644 --- a/src/Ai/Raid/ZulGurub/RaidZGTriggerContext.h +++ b/src/Ai/Raid/ZulGurub/RaidZGTriggerContext.h @@ -1,6 +1,7 @@ #pragma once #include "AiObjectContext.h" +#include "BloodlordMandokirWhirlwindTrigger.h" #include "GrilekTheWandererAvatarTrigger.h" #include "HakkarTheSoulflayer/mechanics/son-of-hakkar/BringBackSonOfHakkar/HakkarTheSoulflayerBringBackSonOfHakkarTrigger.h" #include "HakkarTheSoulflayer/mechanics/blood-siphon/GoToPoisonousCloud/HakkarTheSoulflayerGoToPoisonousCloudTrigger.h" @@ -9,6 +10,7 @@ #include "HakkarTheSoulflayer/mechanics/positioning/HakkarPositioning/HakkarTheSoulflayerHakkarPositioningTrigger.h" #include "HakkarTheSoulflayer/mechanics/son-of-hakkar/MoveToSonOfHakkar/HakkarTheSoulflayerMoveToSonOfHakkarTrigger.h" #include "HakkarTheSoulflayer/mechanics/son-of-hakkar/PullSonOfHakkar/HakkarTheSoulflayerPullSonOfHakkarTrigger.h" +#include "HighPriestThekal/Phase1/HealthBalance/HighPriestThekalHealthBalanceTrigger.h" #include "HighPriestVenoxisPhase1HolyWrathTrigger.h" #include "Trash/GurubashiBatRider/GurubashiBatRiderUnstableConcoctionTrigger.h" #include "Boss/HighPriestVenoxis/Phase1/VenoxisPositioning/HighPriestVenoxisPhase1VenoxisPositioningTrigger.h" @@ -29,9 +31,15 @@ class RaidZGTriggerContext : public NamedObjectContext creators["high priest venoxis phase 1 razzashi cobras positioning"] = &RaidZGTriggerContext::highPriestVenoxisPhase1RazzashiCobrasPositioning; creators["high priest venoxis phase 1 razzashi cobras dps priority"] = &RaidZGTriggerContext::highPriestVenoxisPhase1RazzashiCobrasDPSPriority; + // Bloodlord Mandokir + creators["bloodlord mandokir whirlwind"] = &RaidZGTriggerContext::bloodlordMandokirWhirlwind; + // Gri'lek the Wanderer creators["grilek the wanderer avatar"] = &RaidZGTriggerContext::grilekTheWandererAvatarTrigger; + // High Priest Thekal + creators["high priest thekal phase 1 health balance"] = &RaidZGTriggerContext::highPriestThekalHealthBalance; + // Hakkar the Soulflayer creators["hakkar the soulflayer go to poisonous cloud"] = &RaidZGTriggerContext::hakkarTheSoulflayerGoToPoisonousCloud; creators["hakkar the soulflayer cause insanity"] = &RaidZGTriggerContext::hakkarTheSoulflayerCauseInsanity; @@ -70,12 +78,24 @@ class RaidZGTriggerContext : public NamedObjectContext return new HighPriestVenoxisPhase1RazzashiCobrasDPSPriorityTrigger(botAI); } + // Bloodlord Mandokir + static Trigger* bloodlordMandokirWhirlwind(PlayerbotAI* botAI) + { + return new BloodlordMandokirWhirlwindTrigger(botAI); + } + // Gri'lek the Wanderer static Trigger* grilekTheWandererAvatarTrigger(PlayerbotAI* botAI) { return new GrilekTheWandererAvatarTrigger(botAI); } + // High Priest Thekal + static Trigger* highPriestThekalHealthBalance(PlayerbotAI* botAI) + { + return new HighPriestThekalHealthBalanceTrigger(botAI); + } + // Hakkar the Soulflayer static Trigger* hakkarTheSoulflayerGoToPoisonousCloud(PlayerbotAI* botAI) { diff --git a/src/Ai/Raid/ZulGurub/leader/ZulGurubRaidLeader.h b/src/Ai/Raid/ZulGurub/leader/ZulGurubRaidLeader.h index 9f4261729be..0b4b766ce7a 100644 --- a/src/Ai/Raid/ZulGurub/leader/ZulGurubRaidLeader.h +++ b/src/Ai/Raid/ZulGurub/leader/ZulGurubRaidLeader.h @@ -5,13 +5,17 @@ #include "Ai/Raid/ZulGurub/Boss/HighPriestVenoxis/assistant/HighPriestVenoxisAssistant.h" #include "Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/assistant/hakkar-the-soulflayer/HakkarTheSoulflayerAssistant.h" #include "Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/assistant/son-of-hakkar/SonOfHakkarAssistant.h" +#include "BloodlordMandokir/assistant/BloodlordMandokirAssistant.h" #include "GrilekTheWanderer/assistant/GrilekTheWandererAssistant.h" +#include "HighPriestThekal/assistant/high-priest-thekal/HighPriestThekalAssistant.h" class ZulGurubRaidLeader : public BaseRaidLeader { private: HighPriestVenoxisAssistant highPriestVenoxisAssistant{}; + BloodlordMandokirAssistant bloodlordMandokirAssistant{}; GrilekTheWandererAssistant grilekTheWandererAssistant{}; + HighPriestThekalAssistant highPriestThekalAssistant{}; HakkarTheSoulflayerAssistant hakkarTheSoulflayerAssistant{}; SonOfHakkarAssistant sonOfHakkarAssistant{}; @@ -24,11 +28,21 @@ class ZulGurubRaidLeader : public BaseRaidLeader return this->highPriestVenoxisAssistant; } + [[nodiscard]] const BloodlordMandokirAssistant& getBloodlordMandokirAssistant() const noexcept + { + return this->bloodlordMandokirAssistant; + } + [[nodiscard]] const GrilekTheWandererAssistant& getGrilekTheWandererAssistant() const noexcept { return this->grilekTheWandererAssistant; } + [[nodiscard]] HighPriestThekalAssistant& getHighPriestThekalAssistant() noexcept + { + return this->highPriestThekalAssistant; + } + [[nodiscard]] HakkarTheSoulflayerAssistant& getHakkarTheSoulflayerAssistant() noexcept { return this->hakkarTheSoulflayerAssistant; diff --git a/src/domain/core/raid/assistant/base-assistant/BaseAssistant.h b/src/domain/core/raid/assistant/base-assistant/BaseAssistant.h new file mode 100644 index 00000000000..128a9794840 --- /dev/null +++ b/src/domain/core/raid/assistant/base-assistant/BaseAssistant.h @@ -0,0 +1,52 @@ +#pragma once + +#include "Creature.h" +#include "Player.h" + +class BaseAssistant +{ +public: + BaseAssistant() = default; + virtual ~BaseAssistant() = default; + + +protected: + + [[nodiscard]] Creature* findActiveEnemyByDBGUID(Player& player, uint32_t dbGuid, uint32_t entry) const + { + Map* const map = player.GetMap(); + + if (map == nullptr) + { + return nullptr; + } + + const std::unordered_multimap store = map->GetCreatureBySpawnIdStore(); + const std::unordered_multimap::const_iterator it = store.find(dbGuid); + const std::unordered_multimap::const_iterator end = store.end(); + + if (it == end) + { + return nullptr; + } + + Creature* enemy = it->second; + + if (enemy == nullptr) + { + return nullptr; + } + + if (!enemy->IsAlive()) + { + return nullptr; + } + + if (enemy->GetEntry() != entry) + { + return nullptr; + } + + return enemy; + } +}; From 10608fd011a5257eeca2a334b1f70a94119b8684 Mon Sep 17 00:00:00 2001 From: Nicolas Lebacq Date: Sat, 4 Apr 2026 21:00:34 +0200 Subject: [PATCH 7/8] fix: Lowered Thekal and adds health difference from 5000 to 2500 for a smoother experience. --- .../HighPriestThekal/definition/enum/HighPriestThekalEnum.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ai/Raid/ZulGurub/Boss/HighPriestThekal/definition/enum/HighPriestThekalEnum.h b/src/Ai/Raid/ZulGurub/Boss/HighPriestThekal/definition/enum/HighPriestThekalEnum.h index af8e666f993..4162599d6fd 100644 --- a/src/Ai/Raid/ZulGurub/Boss/HighPriestThekal/definition/enum/HighPriestThekalEnum.h +++ b/src/Ai/Raid/ZulGurub/Boss/HighPriestThekal/definition/enum/HighPriestThekalEnum.h @@ -7,5 +7,5 @@ enum class HighPriestThekalEnum : uint32_t ENTRY = 14509u, GUID = 49310u, SPELL_TIGER_FORM = 24169u, - MAXIMUM_HEALTH_DIFFERENCE = 5000u, + MAXIMUM_HEALTH_DIFFERENCE = 2500u, }; From bd51d4fcd27e8ec21f0b52c292d5343896ea7fe5 Mon Sep 17 00:00:00 2001 From: Nicolas Lebacq Date: Sat, 4 Apr 2026 22:14:45 +0200 Subject: [PATCH 8/8] fix: Migrated to latest core version. --- .../hakkar-the-soulflayer/HakkarTheSoulflayerAssistant.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/assistant/hakkar-the-soulflayer/HakkarTheSoulflayerAssistant.h b/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/assistant/hakkar-the-soulflayer/HakkarTheSoulflayerAssistant.h index 55cd1156c20..fa288df5aa3 100644 --- a/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/assistant/hakkar-the-soulflayer/HakkarTheSoulflayerAssistant.h +++ b/src/Ai/Raid/ZulGurub/Boss/HakkarTheSoulflayer/assistant/hakkar-the-soulflayer/HakkarTheSoulflayerAssistant.h @@ -1,6 +1,7 @@ #pragma once #include "PlayerbotAI.h" +#include "ThreatManager.h" #include "Unit.h" #include "Player.h" #include "Spell.h" @@ -76,7 +77,7 @@ class HakkarTheSoulflayerAssistant return false; } - ThreatMgr& threatMgr = hakkar->GetThreatMgr(); + ThreatManager& threatMgr = hakkar->GetThreatMgr(); const float botThreat = threatMgr.GetThreat(&bot); const float mainTankThreat = threatMgr.GetThreat(mainTank); @@ -124,14 +125,14 @@ class HakkarTheSoulflayerAssistant return nullptr; } - const ThreatMgr& threatMgr = hakkar->GetThreatMgr(); + const ThreatManager& threatMgr = hakkar->GetThreatMgr(); if (threatMgr.GetThreatListSize() < 2) { return nullptr; } - const Acore::IteratorPair::const_iterator> threatList = hakkar->GetThreatMgr().GetSortedThreatList(); + const Acore::IteratorPair threatList = hakkar->GetThreatMgr().GetSortedThreatList(); uint8_t index = 0;