From 82b5c7538ee8284fae527c7d33b00e0ccbf0c4eb Mon Sep 17 00:00:00 2001 From: Crow Date: Fri, 19 Jun 2026 18:02:43 -0500 Subject: [PATCH] Rewrite Gruul's Lair Strategies (#2473) ## Pull Request Description Well, this is what I was doing while I let AI do the buff announcements PR... GL was the first strategy I wrote alone (Revision started Karazhan), and it sucks. Since AC has now fixed Olm to actually chase and Kiggler to no longer move to within his Arcane Explosion distance, it was a good time to redo things. Overall, the code should be much simpler and better for performance. I also tweaked several strategies. My raid is close to expansion BiS now so I tested at 20/20 damage/healing IP nerfs. One shot both, and overall the bot coordination is a lot smoother than I remember when I was actually running these raids. Some stuff is still not great, like GruulTheDragonkillerSpreadRangedAction() is overdone for sure, but I've spent all the time I'm willing to on this one. I don't want this to become a longer project. ## Feature Evaluation - Describe the **minimum logic** required to achieve the intended behavior. - Describe the **processing cost** when this logic executes across many bots. Same structure as before. Improved commonly used methods based on experience since I did the original strategy (e.g., how to do tank movement or spread bots), threw out some super complicated, bulky methods in favor of much simpler ones that work better anyway, yadda yadda. ## How to Test the Changes Go fight HKM and Gruul. For HKM, bring three tanks, a mage, and a boomin. For Gruul, bring an alarm clock. ## Impact Assessment - Does this change increase per-bot/per-tick processing or risk scaling poorly with thousands of bots? - - [x] No, not at all - - [ ] Minimal impact (**explain below**) - - [ ] Moderate impact (**explain below**) - Does this change modify default bot behavior? - - [x] No - - [ ] Yes (**explain why**) - Does this change add new decision branches or increase maintenance complexity? - - [x] No - - [ ] Yes (**explain below**) ## AI Assistance Was AI assistance used while working on this change? - - [x] No - - [ ] Yes (**explain below**) ## Final Checklist - - [x] Stability is not compromised. - - [x] Performance impact is understood, tested, and acceptable. - - [x] Added logic complexity is justified and explained. - - [x] Any new bot dialogue lines are translated. - - [x] Documentation updated if needed (Conf comments, WiKi commands). ## Notes for Reviewers --- src/Ai/Raid/Gruul/GruulActionContext.h | 117 ++++-- src/Ai/Raid/Gruul/GruulActions.cpp | 518 +++++++++++------------- src/Ai/Raid/Gruul/GruulActions.h | 74 ++-- src/Ai/Raid/Gruul/GruulHelpers.cpp | 259 ++++-------- src/Ai/Raid/Gruul/GruulHelpers.h | 59 ++- src/Ai/Raid/Gruul/GruulMultipliers.cpp | 155 ++++--- src/Ai/Raid/Gruul/GruulMultipliers.h | 46 ++- src/Ai/Raid/Gruul/GruulStrategy.cpp | 39 +- src/Ai/Raid/Gruul/GruulStrategy.h | 4 +- src/Ai/Raid/Gruul/GruulTriggerContext.h | 117 ++++-- src/Ai/Raid/Gruul/GruulTriggers.cpp | 169 +++----- src/Ai/Raid/Gruul/GruulTriggers.h | 69 ++-- 12 files changed, 816 insertions(+), 810 deletions(-) diff --git a/src/Ai/Raid/Gruul/GruulActionContext.h b/src/Ai/Raid/Gruul/GruulActionContext.h index 1e88b8f79..b64eaf948 100644 --- a/src/Ai/Raid/Gruul/GruulActionContext.h +++ b/src/Ai/Raid/Gruul/GruulActionContext.h @@ -1,5 +1,5 @@ -#ifndef PLAYERBOTS_RAIDGRUULSLAIRACTIONCONTEXT_H -#define PLAYERBOTS_RAIDGRUULSLAIRACTIONCONTEXT_H +#ifndef PLAYERBOTS_GRUULACTIONCONTEXT_H +#define PLAYERBOTS_GRUULACTIONCONTEXT_H #include "GruulActions.h" #include "NamedObjectContext.h" @@ -10,40 +10,101 @@ public: RaidGruulsLairActionContext() { // High King Maulgar - creators["high king maulgar main tank attack maulgar"] = &RaidGruulsLairActionContext::high_king_maulgar_main_tank_attack_maulgar; - creators["high king maulgar first assist tank attack olm"] = &RaidGruulsLairActionContext::high_king_maulgar_first_assist_tank_attack_olm; - creators["high king maulgar second assist tank attack blindeye"] = &RaidGruulsLairActionContext::high_king_maulgar_second_assist_tank_attack_blindeye; - creators["high king maulgar mage tank attack krosh"] = &RaidGruulsLairActionContext::high_king_maulgar_mage_tank_attack_krosh; - creators["high king maulgar moonkin tank attack kiggler"] = &RaidGruulsLairActionContext::high_king_maulgar_moonkin_tank_attack_kiggler; - creators["high king maulgar assign dps priority"] = &RaidGruulsLairActionContext::high_king_maulgar_assign_dps_priority; - creators["high king maulgar healer find safe position"] = &RaidGruulsLairActionContext::high_king_maulgar_healer_find_safe_position; - creators["high king maulgar run away from whirlwind"] = &RaidGruulsLairActionContext::high_king_maulgar_run_away_from_whirlwind; - creators["high king maulgar banish felstalker"] = &RaidGruulsLairActionContext::high_king_maulgar_banish_felstalker; - creators["high king maulgar misdirect olm and blindeye"] = &RaidGruulsLairActionContext::high_king_maulgar_misdirect_olm_and_blindeye; + creators["high king maulgar main tank attack maulgar"] = + &RaidGruulsLairActionContext::high_king_maulgar_main_tank_attack_maulgar; + + creators["high king maulgar first assist tank attack olm"] = + &RaidGruulsLairActionContext::high_king_maulgar_first_assist_tank_attack_olm; + + creators["high king maulgar second assist tank attack blindeye"] = + &RaidGruulsLairActionContext::high_king_maulgar_second_assist_tank_attack_blindeye; + + creators["high king maulgar mage tank attack krosh"] = + &RaidGruulsLairActionContext::high_king_maulgar_mage_tank_attack_krosh; + + creators["high king maulgar moonkin tank attack kiggler"] = + &RaidGruulsLairActionContext::high_king_maulgar_moonkin_tank_attack_kiggler; + + creators["high king maulgar assign dps priority"] = + &RaidGruulsLairActionContext::high_king_maulgar_assign_dps_priority; + + creators["high king maulgar run away from whirlwind"] = + &RaidGruulsLairActionContext::high_king_maulgar_run_away_from_whirlwind; + + creators["high king maulgar move away from blast nova danger"] = + &RaidGruulsLairActionContext::high_king_maulgar_move_away_from_blast_nova_danger; + + creators["high king maulgar banish fel stalker"] = + &RaidGruulsLairActionContext::high_king_maulgar_banish_fel_stalker; + + creators["high king maulgar misdirect ogres to tanks"] = + &RaidGruulsLairActionContext::high_king_maulgar_misdirect_ogres_to_tanks; // Gruul the Dragonkiller - creators["gruul the dragonkiller tanks position boss"] = &RaidGruulsLairActionContext::gruul_the_dragonkiller_tanks_position_boss; - creators["gruul the dragonkiller spread ranged"] = &RaidGruulsLairActionContext::gruul_the_dragonkiller_spread_ranged; - creators["gruul the dragonkiller shatter spread"] = &RaidGruulsLairActionContext::gruul_the_dragonkiller_shatter_spread; + creators["gruul the dragonkiller tanks position boss"] = + &RaidGruulsLairActionContext::gruul_the_dragonkiller_tanks_position_boss; + + creators["gruul the dragonkiller spread ranged"] = + &RaidGruulsLairActionContext::gruul_the_dragonkiller_spread_ranged; + + creators["gruul the dragonkiller shatter spread"] = + &RaidGruulsLairActionContext::gruul_the_dragonkiller_shatter_spread; } private: // High King Maulgar - static Action* high_king_maulgar_main_tank_attack_maulgar(PlayerbotAI* botAI) { return new HighKingMaulgarMainTankAttackMaulgarAction(botAI); } - static Action* high_king_maulgar_first_assist_tank_attack_olm(PlayerbotAI* botAI) { return new HighKingMaulgarFirstAssistTankAttackOlmAction(botAI); } - static Action* high_king_maulgar_second_assist_tank_attack_blindeye(PlayerbotAI* botAI) { return new HighKingMaulgarSecondAssistTankAttackBlindeyeAction(botAI); } - static Action* high_king_maulgar_mage_tank_attack_krosh(PlayerbotAI* botAI) { return new HighKingMaulgarMageTankAttackKroshAction(botAI); } - static Action* high_king_maulgar_moonkin_tank_attack_kiggler(PlayerbotAI* botAI) { return new HighKingMaulgarMoonkinTankAttackKigglerAction(botAI); } - static Action* high_king_maulgar_assign_dps_priority(PlayerbotAI* botAI) { return new HighKingMaulgarAssignDPSPriorityAction(botAI); } - static Action* high_king_maulgar_healer_find_safe_position(PlayerbotAI* botAI) { return new HighKingMaulgarHealerFindSafePositionAction(botAI); } - static Action* high_king_maulgar_run_away_from_whirlwind(PlayerbotAI* botAI) { return new HighKingMaulgarRunAwayFromWhirlwindAction(botAI); } - static Action* high_king_maulgar_banish_felstalker(PlayerbotAI* botAI) { return new HighKingMaulgarBanishFelstalkerAction(botAI); } - static Action* high_king_maulgar_misdirect_olm_and_blindeye(PlayerbotAI* botAI) { return new HighKingMaulgarMisdirectOlmAndBlindeyeAction(botAI); } + static Action* high_king_maulgar_main_tank_attack_maulgar(PlayerbotAI* botAI) { + return new HighKingMaulgarMainTankAttackMaulgarAction(botAI); + } + + static Action* high_king_maulgar_first_assist_tank_attack_olm(PlayerbotAI* botAI) { + return new HighKingMaulgarFirstAssistTankAttackOlmAction(botAI); + } + + static Action* high_king_maulgar_second_assist_tank_attack_blindeye(PlayerbotAI* botAI) { + return new HighKingMaulgarSecondAssistTankAttackBlindeyeAction(botAI); + } + + static Action* high_king_maulgar_mage_tank_attack_krosh(PlayerbotAI* botAI) { + return new HighKingMaulgarMageTankAttackKroshAction(botAI); + } + + static Action* high_king_maulgar_moonkin_tank_attack_kiggler(PlayerbotAI* botAI) { + return new HighKingMaulgarMoonkinTankAttackKigglerAction(botAI); + } + + static Action* high_king_maulgar_assign_dps_priority(PlayerbotAI* botAI) { + return new HighKingMaulgarAssignDPSPriorityAction(botAI); + } + + static Action* high_king_maulgar_run_away_from_whirlwind(PlayerbotAI* botAI) { + return new HighKingMaulgarRunAwayFromWhirlwindAction(botAI); + } + + static Action* high_king_maulgar_move_away_from_blast_nova_danger(PlayerbotAI* botAI) { + return new HighKingMaulgarMoveAwayFromBlastNovaDangerAction(botAI); + } + + static Action* high_king_maulgar_banish_fel_stalker(PlayerbotAI* botAI) { + return new HighKingMaulgarBanishFelStalkerAction(botAI); + } + + static Action* high_king_maulgar_misdirect_ogres_to_tanks(PlayerbotAI* botAI) { + return new HighKingMaulgarMisdirectOgresToTanksAction(botAI); + } // Gruul the Dragonkiller - static Action* gruul_the_dragonkiller_tanks_position_boss(PlayerbotAI* botAI) { return new GruulTheDragonkillerTanksPositionBossAction(botAI); } - static Action* gruul_the_dragonkiller_spread_ranged(PlayerbotAI* botAI) { return new GruulTheDragonkillerSpreadRangedAction(botAI); } - static Action* gruul_the_dragonkiller_shatter_spread(PlayerbotAI* botAI) { return new GruulTheDragonkillerShatterSpreadAction(botAI); } + static Action* gruul_the_dragonkiller_tanks_position_boss(PlayerbotAI* botAI) { + return new GruulTheDragonkillerTanksPositionBossAction(botAI); + } + + static Action* gruul_the_dragonkiller_spread_ranged(PlayerbotAI* botAI) { + return new GruulTheDragonkillerSpreadRangedAction(botAI); + } + + static Action* gruul_the_dragonkiller_shatter_spread(PlayerbotAI* botAI) { + return new GruulTheDragonkillerShatterSpreadAction(botAI); + } }; #endif diff --git a/src/Ai/Raid/Gruul/GruulActions.cpp b/src/Ai/Raid/Gruul/GruulActions.cpp index f4b8b5d30..539a4e32a 100644 --- a/src/Ai/Raid/Gruul/GruulActions.cpp +++ b/src/Ai/Raid/Gruul/GruulActions.cpp @@ -25,18 +25,19 @@ bool HighKingMaulgarMainTankAttackMaulgarAction::Execute(Event /*event*/) if (maulgar->GetVictim() == bot) { const Position& position = MAULGAR_TANK_POSITION; - const float maxDistance = 3.0f; + const float distToPosition = + bot->GetExactDist2d(position.GetPositionX(), position.GetPositionY()); - float distanceToPosition = bot->GetExactDist2d(position.GetPositionX(), position.GetPositionY()); - - if (distanceToPosition > maxDistance) + if (distToPosition > 3.0f) { - float dX = position.GetPositionX() - bot->GetPositionX(); - float dY = position.GetPositionY() - bot->GetPositionY(); - float moveX = bot->GetPositionX() + (dX / distanceToPosition) * maxDistance; - float moveY = bot->GetPositionY() + (dY / distanceToPosition) * maxDistance; - return MoveTo(GRUULS_LAIR_MAP_ID, moveX, moveY, position.GetPositionZ(), false, false, false, false, - MovementPriority::MOVEMENT_COMBAT, true, true); + const float dX = position.GetPositionX() - bot->GetPositionX(); + const float dY = position.GetPositionY() - bot->GetPositionY(); + const float moveDist = std::min(5.0f, distToPosition); + const float moveX = bot->GetPositionX() + (dX / distToPosition) * moveDist; + const float moveY = bot->GetPositionY() + (dY / distToPosition) * moveDist; + + return MoveTo(GRUULS_LAIR_MAP_ID, moveX, moveY, position.GetPositionZ(), false, false, + false, false, MovementPriority::MOVEMENT_COMBAT, true, true); } } @@ -59,19 +60,19 @@ bool HighKingMaulgarFirstAssistTankAttackOlmAction::Execute(Event /*event*/) if (olm->GetVictim() == bot) { const Position& position = OLM_TANK_POSITION; - const float maxDistance = 3.0f; - const float olmTankLeeway = 30.0f; + const float distToPosition = + bot->GetExactDist2d(position.GetPositionX(), position.GetPositionY()); - float distanceOlmToPosition = olm->GetExactDist2d(position.GetPositionX(), position.GetPositionY()); - - if (distanceOlmToPosition > olmTankLeeway) + if (distToPosition > 3.0f) { - float dX = position.GetPositionX() - bot->GetPositionX(); - float dY = position.GetPositionY() - bot->GetPositionY(); - float moveX = bot->GetPositionX() + (dX / distanceOlmToPosition) * maxDistance; - float moveY = bot->GetPositionY() + (dY / distanceOlmToPosition) * maxDistance; - return MoveTo(GRUULS_LAIR_MAP_ID, moveX, moveY, position.GetPositionZ(), false, false, false, false, - MovementPriority::MOVEMENT_COMBAT, true, false); + const float dX = position.GetPositionX() - bot->GetPositionX(); + const float dY = position.GetPositionY() - bot->GetPositionY(); + const float moveDist = std::min(5.0f, distToPosition); + const float moveX = bot->GetPositionX() + (dX / distToPosition) * moveDist; + const float moveY = bot->GetPositionY() + (dY / distToPosition) * moveDist; + + return MoveTo(GRUULS_LAIR_MAP_ID, moveX, moveY, position.GetPositionZ(), false, false, + false, false, MovementPriority::MOVEMENT_COMBAT, true, true); } } @@ -94,18 +95,19 @@ bool HighKingMaulgarSecondAssistTankAttackBlindeyeAction::Execute(Event /*event* if (blindeye->GetVictim() == bot) { const Position& position = BLINDEYE_TANK_POSITION; - const float maxDistance = 3.0f; + const float distToPosition = + bot->GetExactDist2d(position.GetPositionX(), position.GetPositionY()); - float distanceToPosition = bot->GetExactDist2d(position.GetPositionX(), position.GetPositionY()); - - if (distanceToPosition > maxDistance) + if (distToPosition > 3.0f) { - float dX = position.GetPositionX() - bot->GetPositionX(); - float dY = position.GetPositionY() - bot->GetPositionY(); - float moveX = bot->GetPositionX() + (dX / distanceToPosition) * maxDistance; - float moveY = bot->GetPositionY() + (dY / distanceToPosition) * maxDistance; - return MoveTo(GRUULS_LAIR_MAP_ID, moveX, moveY, position.GetPositionZ(), false, false, false, false, - MovementPriority::MOVEMENT_COMBAT, true, false); + const float dX = position.GetPositionX() - bot->GetPositionX(); + const float dY = position.GetPositionY() - bot->GetPositionY(); + const float moveDist = std::min(5.0f, distToPosition); + const float moveX = bot->GetPositionX() + (dX / distToPosition) * moveDist; + const float moveY = bot->GetPositionY() + (dY / distToPosition) * moveDist; + + return MoveTo(GRUULS_LAIR_MAP_ID, moveX, moveY, position.GetPositionZ(), false, false, + false, false, MovementPriority::MOVEMENT_COMBAT, true, true); } } @@ -122,11 +124,17 @@ bool HighKingMaulgarMageTankAttackKroshAction::Execute(Event /*event*/) MarkTargetWithTriangle(bot, krosh); SetRtiTarget(botAI, "triangle", krosh); - if (krosh->HasAura(SPELL_SPELL_SHIELD) && botAI->CanCastSpell("spellsteal", krosh)) + if (krosh->HasAura(static_cast(GruulsLairSpells::SPELL_SPELL_SHIELD)) && + botAI->CanCastSpell("spellsteal", krosh)) + { return botAI->CastSpell("spellsteal", krosh); + } - if (!bot->HasAura(SPELL_SPELL_SHIELD) && botAI->CanCastSpell("fire ward", bot)) + if (!bot->HasAura(static_cast(GruulsLairSpells::SPELL_SPELL_SHIELD)) && + botAI->CanCastSpell("fire ward", bot)) + { return botAI->CastSpell("fire ward", bot); + } if (AI_VALUE(Unit*, "current target") != krosh) return Attack(krosh); @@ -134,30 +142,27 @@ bool HighKingMaulgarMageTankAttackKroshAction::Execute(Event /*event*/) if (krosh->GetVictim() == bot) { const Position& position = KROSH_TANK_POSITION; - float distanceToKrosh = krosh->GetExactDist2d(position.GetPositionX(), position.GetPositionY()); - const float minDistance = 16.0f; - const float maxDistance = 29.0f; - const float tankPositionLeeway = 1.0f; + const float distanceKroshToPosition = + krosh->GetExactDist2d(position.GetPositionX(), position.GetPositionY()); + constexpr float minDistance = 17.0f; + constexpr float maxDistance = 30.0f; - if (distanceToKrosh > minDistance && distanceToKrosh < maxDistance) + if (distanceKroshToPosition > minDistance && distanceKroshToPosition < maxDistance && + bot->GetExactDist2d(position.GetPositionX(), position.GetPositionY()) > 1.0f) { - if (!bot->IsWithinDist2d(position.GetPositionX(), position.GetPositionY(), tankPositionLeeway)) - { - return MoveTo(GRUULS_LAIR_MAP_ID, position.GetPositionX(), position.GetPositionY(), position.GetPositionZ(), - false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false); - } - - float orientation = atan2(krosh->GetPositionY() - bot->GetPositionY(), - krosh->GetPositionX() - bot->GetPositionX()); - bot->SetFacingTo(orientation); + return MoveTo(GRUULS_LAIR_MAP_ID, position.GetPositionX(), position.GetPositionY(), + position.GetPositionZ(), false, false, false, false, + MovementPriority::MOVEMENT_COMBAT, true, false); } else { - Position safePos; - if (TryGetNewSafePosition(botAI, bot, safePos)) + constexpr float safeDistance = 15.0f; + const float currentDistance = bot->GetDistance2d(krosh); + + if (currentDistance < safeDistance) { - return MoveTo(GRUULS_LAIR_MAP_ID, safePos.GetPositionX(), safePos.GetPositionY(), safePos.GetPositionZ(), - false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false); + botAI->InterruptSpell(); + return MoveAway(krosh, safeDistance - currentDistance); } } } @@ -178,11 +183,16 @@ bool HighKingMaulgarMoonkinTankAttackKigglerAction::Execute(Event /*event*/) if (AI_VALUE(Unit*, "current target") != kiggler) return Attack(kiggler); - Position safePos; - if (TryGetNewSafePosition(botAI, bot, safePos)) + if (kiggler->GetVictim() == bot) { - return MoveTo(GRUULS_LAIR_MAP_ID, safePos.GetPositionX(), safePos.GetPositionY(), safePos.GetPositionZ(), - false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false); + constexpr float safeDistance = 28.5f; + const float currentDistance = bot->GetDistance2d(kiggler); + + if (currentDistance < safeDistance) + { + botAI->InterruptSpell(); + return MoveAway(kiggler, safeDistance - currentDistance); + } } return false; @@ -194,15 +204,7 @@ bool HighKingMaulgarAssignDPSPriorityAction::Execute(Event /*event*/) Unit* blindeye = AI_VALUE2(Unit*, "find target", "blindeye the seer"); if (blindeye) { - Position safePos; - if (TryGetNewSafePosition(botAI, bot, safePos)) - { - bot->AttackStop(); - bot->InterruptNonMeleeSpells(false); - return MoveTo(GRUULS_LAIR_MAP_ID, safePos.GetPositionX(), safePos.GetPositionY(), safePos.GetPositionZ(), - false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false); - } - + MarkTargetWithStar(bot, blindeye); SetRtiTarget(botAI, "star", blindeye); if (AI_VALUE(Unit*, "current target") != blindeye) @@ -215,15 +217,7 @@ bool HighKingMaulgarAssignDPSPriorityAction::Execute(Event /*event*/) Unit* olm = AI_VALUE2(Unit*, "find target", "olm the summoner"); if (olm) { - Position safePos; - if (TryGetNewSafePosition(botAI, bot, safePos)) - { - bot->AttackStop(); - bot->InterruptNonMeleeSpells(false); - return MoveTo(GRUULS_LAIR_MAP_ID, safePos.GetPositionX(), safePos.GetPositionY(), safePos.GetPositionZ(), - false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false); - } - + MarkTargetWithCircle(bot, olm); SetRtiTarget(botAI, "circle", olm); if (AI_VALUE(Unit*, "current target") != olm) @@ -236,15 +230,7 @@ bool HighKingMaulgarAssignDPSPriorityAction::Execute(Event /*event*/) Unit* krosh = AI_VALUE2(Unit*, "find target", "krosh firehand"); if (krosh && botAI->IsRanged(bot)) { - Position safePos; - if (TryGetNewSafePosition(botAI, bot, safePos)) - { - bot->AttackStop(); - bot->InterruptNonMeleeSpells(false); - return MoveTo(GRUULS_LAIR_MAP_ID, safePos.GetPositionX(), safePos.GetPositionY(), safePos.GetPositionZ(), - false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false); - } - + MarkTargetWithTriangle(bot, krosh); SetRtiTarget(botAI, "triangle", krosh); if (AI_VALUE(Unit*, "current target") != krosh) @@ -257,15 +243,7 @@ bool HighKingMaulgarAssignDPSPriorityAction::Execute(Event /*event*/) Unit* kiggler = AI_VALUE2(Unit*, "find target", "kiggler the crazed"); if (kiggler) { - Position safePos; - if (TryGetNewSafePosition(botAI, bot, safePos)) - { - bot->AttackStop(); - bot->InterruptNonMeleeSpells(false); - return MoveTo(GRUULS_LAIR_MAP_ID, safePos.GetPositionX(), safePos.GetPositionY(), safePos.GetPositionZ(), - false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false); - } - + MarkTargetWithDiamond(bot, kiggler); SetRtiTarget(botAI, "diamond", kiggler); if (AI_VALUE(Unit*, "current target") != kiggler) @@ -278,15 +256,7 @@ bool HighKingMaulgarAssignDPSPriorityAction::Execute(Event /*event*/) Unit* maulgar = AI_VALUE2(Unit*, "find target", "high king maulgar"); if (maulgar) { - Position safePos; - if (TryGetNewSafePosition(botAI, bot, safePos)) - { - bot->AttackStop(); - bot->InterruptNonMeleeSpells(false); - return MoveTo(GRUULS_LAIR_MAP_ID, safePos.GetPositionX(), safePos.GetPositionY(), safePos.GetPositionZ(), - false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false); - } - + MarkTargetWithSquare(bot, maulgar); SetRtiTarget(botAI, "square", maulgar); if (AI_VALUE(Unit*, "current target") != maulgar) @@ -296,97 +266,70 @@ bool HighKingMaulgarAssignDPSPriorityAction::Execute(Event /*event*/) return false; } -// Avoid Whirlwind and Blast Wave and generally try to stay near the center of the room -bool HighKingMaulgarHealerFindSafePositionAction::Execute(Event /*event*/) -{ - const Position& center = MAULGAR_ROOM_CENTER; - const float maxDistanceFromCenter = 50.0f; - float distToCenter = bot->GetExactDist2d(center.GetPositionX(), center.GetPositionY()); - - if (distToCenter > maxDistanceFromCenter) - { - float angle = atan2(bot->GetPositionY() - center.GetPositionY(), bot->GetPositionX() - center.GetPositionX()); - float destX = center.GetPositionX() + 40.0f * cos(angle); - float destY = center.GetPositionY() + 40.0f * sin(angle); - float destZ = center.GetPositionZ(); - - if (!bot->GetMap()->CheckCollisionAndGetValidCoords(bot, bot->GetPositionX(), bot->GetPositionY(), - bot->GetPositionZ(), destX, destY, destZ)) - return false; - - return MoveTo(GRUULS_LAIR_MAP_ID, destX, destY, destZ, false, false, false, false, - MovementPriority::MOVEMENT_COMBAT, true, false); - } - - Position safePos; - if (TryGetNewSafePosition(botAI, bot, safePos)) - { - bot->AttackStop(); - bot->InterruptNonMeleeSpells(false); - return MoveTo(GRUULS_LAIR_MAP_ID, safePos.GetPositionX(), safePos.GetPositionY(), safePos.GetPositionZ(), - false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false); - } - - return false; -} - -// Run away from Maulgar during Whirlwind (logic for after all other ogres are dead) bool HighKingMaulgarRunAwayFromWhirlwindAction::Execute(Event /*event*/) { Unit* maulgar = AI_VALUE2(Unit*, "find target", "high king maulgar"); if (!maulgar) return false; - const float safeDistance = 10.0f; - float distance = bot->GetExactDist2d(maulgar); + constexpr float safeDistance = 8.0f; + const float currentDistance = bot->GetDistance2d(maulgar); - if (distance < safeDistance) + if (currentDistance < safeDistance) { - float angle = atan2(bot->GetPositionY() - maulgar->GetPositionY(), - bot->GetPositionX() - maulgar->GetPositionX()); - float destX = maulgar->GetPositionX() + safeDistance * cos(angle); - float destY = maulgar->GetPositionY() + safeDistance * sin(angle); - float destZ = bot->GetPositionZ(); - - if (!bot->GetMap()->CheckCollisionAndGetValidCoords(bot, bot->GetPositionX(), bot->GetPositionY(), - bot->GetPositionZ(), destX, destY, destZ)) - return false; - - float destDist = maulgar->GetExactDist2d(destX, destY); - - if (destDist >= safeDistance - 0.1f) - { - bot->AttackStop(); - bot->InterruptNonMeleeSpells(true); - return MoveTo(GRUULS_LAIR_MAP_ID, destX, destY, destZ, false, false, false, false, - MovementPriority::MOVEMENT_COMBAT, true, false); - } + botAI->InterruptSpell(); + return MoveAway(maulgar, safeDistance - currentDistance); } return false; } -bool HighKingMaulgarBanishFelstalkerAction::Execute(Event /*event*/) +bool HighKingMaulgarMoveAwayFromBlastNovaDangerAction::Execute(Event /*event*/) +{ + Unit* krosh = AI_VALUE2(Unit*, "find target", "krosh firehand"); + if (!krosh) + return false; + + constexpr float safeDistance = 20.0f; + constexpr uint32 minInterval = 1000; + const float currentDistance = bot->GetDistance2d(krosh); + + if (currentDistance < safeDistance) + { + botAI->InterruptSpell(); + return FleePosition(krosh->GetPosition(), safeDistance, minInterval); + } + + return false; +} + +bool HighKingMaulgarBanishFelStalkerAction::Execute(Event /*event*/) { Group* group = bot->GetGroup(); if (!group) return false; - const GuidVector& npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); std::vector felStalkers; - for (auto const& npc : npcs) + std::list creatureList; + constexpr float searchRadius = 50.0f; + bot->GetCreatureListWithEntryInGrid( + creatureList, static_cast(GruulsLairNpcs::NPC_WILD_FEL_STALKER), searchRadius); + + for (Creature* creature : creatureList) { - Unit* unit = botAI->GetUnit(npc); - if (unit && unit->GetEntry() == NPC_WILD_FEL_STALKER && unit->IsAlive()) - felStalkers.push_back(unit); + if (creature && creature->IsAlive()) + felStalkers.push_back(creature); } std::vector warlocks; for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) { Player* member = ref->GetSource(); - if (member && member->IsAlive() && member->getClass() == CLASS_WARLOCK && GET_PLAYERBOT_AI(member)) + if (member && member->IsAlive() && member->getClass() == CLASS_WARLOCK && + GET_PLAYERBOT_AI(member)) + { warlocks.push_back(member); + } } int warlockIndex = -1; @@ -399,21 +342,26 @@ bool HighKingMaulgarBanishFelstalkerAction::Execute(Event /*event*/) } } - const int64_t felStalkersSize = felStalkers.size(); + const uint8 felStalkersSize = felStalkers.size(); if (warlockIndex >= 0 && warlockIndex < felStalkersSize) { Unit* assignedFelStalker = felStalkers[warlockIndex]; - if (!botAI->HasAura("banish", assignedFelStalker) && botAI->CanCastSpell("banish", assignedFelStalker)) + if (!botAI->HasAura("banish", assignedFelStalker) && + botAI->CanCastSpell("banish", assignedFelStalker)) + { return botAI->CastSpell("banish", assignedFelStalker); + } } return false; } -// Hunter 1: Misdirect Olm to first offtank and have pet attack Blindeye -// Hunter 2: Misdirect Blindeye to second offtank -bool HighKingMaulgarMisdirectOlmAndBlindeyeAction::Execute(Event /*event*/) +// Hunter 1: Misdirect Blindeye to second offtank +// Hunter 2: Misdirect Olm to first offtank +// Hunter 3: Misdirect Kiggler to Moonkin tank +// Hunter 4: Misdirect Krosh to Mage tank +bool HighKingMaulgarMisdirectOgresToTanksAction::Execute(Event /*event*/) { Group* group = bot->GetGroup(); if (!group) @@ -423,68 +371,82 @@ bool HighKingMaulgarMisdirectOlmAndBlindeyeAction::Execute(Event /*event*/) for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) { Player* member = ref->GetSource(); - if (member && member->IsAlive() && member->getClass() == CLASS_HUNTER && GET_PLAYERBOT_AI(member)) + if (member && member->IsAlive() && member->getClass() == CLASS_HUNTER && + GET_PLAYERBOT_AI(member)) + { hunters.push_back(member); + } + + if (hunters.size() >= 4) + break; } - int hunterIndex = -1; + int8 hunterIndex = -1; for (size_t i = 0; i < hunters.size(); ++i) { if (hunters[i] == bot) { - hunterIndex = static_cast(i); + hunterIndex = static_cast(i); break; } } + if (hunterIndex == -1) + return false; - Unit* olm = AI_VALUE2(Unit*, "find target", "olm the summoner"); - Unit* blindeye = AI_VALUE2(Unit*, "find target", "blindeye the seer"); - Player* olmTank = nullptr; - Player* blindeyeTank = nullptr; - - for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + Unit* ogreTarget = nullptr; + Player* tankTarget = nullptr; + if (hunterIndex == 0) { - Player* member = ref->GetSource(); - if (!member || !member->IsAlive()) - continue; - else if (botAI->IsAssistTankOfIndex(member, 0)) olmTank = member; - else if (botAI->IsAssistTankOfIndex(member, 1)) blindeyeTank = member; + ogreTarget = AI_VALUE2(Unit*, "find target", "blindeye the seer"); + tankTarget = GetGroupAssistTank(botAI, bot, 1); + } + else if (hunterIndex == 1) + { + ogreTarget = AI_VALUE2(Unit*, "find target", "olm the summoner"); + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + if (Player* member = GetGroupAssistTank(botAI, bot, 0)) + { + tankTarget = member; + break; + } + } + } + else if (hunterIndex == 2) + { + ogreTarget = AI_VALUE2(Unit*, "find target", "kiggler the crazed"); + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + if (Player* member = GetKigglerMoonkinTank(bot)) + { + tankTarget = member; + break; + } + } + } + else if (hunterIndex == 3) + { + ogreTarget = AI_VALUE2(Unit*, "find target", "krosh firehand"); + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + if (Player* member = GetKroshMageTank(bot)) + { + tankTarget = member; + break; + } + } } - switch (hunterIndex) + if (!ogreTarget || !tankTarget || !tankTarget->IsAlive()) + return false; + + if (botAI->CanCastSpell("misdirection", tankTarget)) + return botAI->CastSpell("misdirection", tankTarget); + + if (bot->HasAura(static_cast(GruulsLairSpells::SPELL_MISDIRECTION)) && + botAI->CanCastSpell("steady shot", ogreTarget)) { - case 0: - botAI->CastSpell("misdirection", olmTank); - if (bot->HasAura(SPELL_MISDIRECTION)) - { - Pet* pet = bot->GetPet(); - if (pet && pet->IsAlive() && pet->GetVictim() != blindeye) - { - pet->ClearUnitState(UNIT_STATE_FOLLOW); - pet->AttackStop(); - pet->SetTarget(blindeye->GetGUID()); - if (pet->GetCharmInfo()) - { - pet->GetCharmInfo()->SetIsCommandAttack(true); - pet->GetCharmInfo()->SetIsAtStay(false); - pet->GetCharmInfo()->SetIsFollowing(false); - pet->GetCharmInfo()->SetIsCommandFollow(false); - pet->GetCharmInfo()->SetIsReturning(false); - } - pet->ToCreature()->AI()->AttackStart(blindeye); - } - return botAI->CastSpell("steady shot", olm); - } - break; - - case 1: - botAI->CastSpell("misdirection", blindeyeTank); - if (bot->HasAura(SPELL_MISDIRECTION)) - return botAI->CastSpell("steady shot", blindeye); - break; - - default: - break; + return botAI->CastSpell("steady shot", ogreTarget); } return false; @@ -505,19 +467,19 @@ bool GruulTheDragonkillerTanksPositionBossAction::Execute(Event /*event*/) if (gruul->GetVictim() == bot) { const Position& position = GRUUL_TANK_POSITION; - const float maxDistance = 5.0f; + const float distToPosition = + bot->GetExactDist2d(position.GetPositionX(), position.GetPositionY()); - float dX = position.GetPositionX() - bot->GetPositionX(); - float dY = position.GetPositionY() - bot->GetPositionY(); - float distanceToTankPosition = bot->GetExactDist2d(position.GetPositionX(), position.GetPositionY()); - - if (distanceToTankPosition > maxDistance) + if (distToPosition > 3.0f) { - float moveX = bot->GetPositionX() + (dX / distanceToTankPosition) * maxDistance; - float moveY = bot->GetPositionY() + (dY / distanceToTankPosition) * maxDistance; - const float moveZ = position.GetPositionZ(); - return MoveTo(GRUULS_LAIR_MAP_ID, moveX, moveY, moveZ, false, false, false, false, - MovementPriority::MOVEMENT_COMBAT, true, false); + const float dX = position.GetPositionX() - bot->GetPositionX(); + const float dY = position.GetPositionY() - bot->GetPositionY(); + const float moveDist = std::min(5.0f, distToPosition); + const float moveX = bot->GetPositionX() + (dX / distToPosition) * moveDist; + const float moveY = bot->GetPositionY() + (dY / distToPosition) * moveDist; + + return MoveTo(GRUULS_LAIR_MAP_ID, moveX, moveY, position.GetPositionZ(), false, false, + false, false, MovementPriority::MOVEMENT_COMBAT, true, true); } } @@ -528,27 +490,17 @@ bool GruulTheDragonkillerTanksPositionBossAction::Execute(Event /*event*/) // Ranged should spread out 10 yards from each other bool GruulTheDragonkillerSpreadRangedAction::Execute(Event /*event*/) { + Unit* gruul = AI_VALUE2(Unit*, "find target", "gruul the dragonkiller"); + if (!gruul) + return false; + + if (gruul->GetHealth() == gruul->GetMaxHealth()) + _hasReachedInitialPosition = false; + Group* group = bot->GetGroup(); if (!group) return false; - static std::unordered_map initialPositions; - static std::unordered_map hasReachedInitialPosition; - - Unit* gruul = AI_VALUE2(Unit*, "find target", "gruul the dragonkiller"); - if (gruul && gruul->GetHealth() == gruul->GetMaxHealth()) - { - initialPositions.erase(bot->GetGUID()); - hasReachedInitialPosition.erase(bot->GetGUID()); - } - - const Position& position = GRUUL_TANK_POSITION; - const float centerX = position.GetPositionX(); - const float centerY = position.GetPositionY(); - const float centerZ = position.GetPositionZ(); - const float minRadius = 25.0f; - const float maxRadius = 40.0f; - std::vector members; Player* closestMember = nullptr; float closestDist = std::numeric_limits::max(); @@ -559,6 +511,7 @@ bool GruulTheDragonkillerSpreadRangedAction::Execute(Event /*event*/) continue; members.push_back(member); + if (member != bot) { float dist = bot->GetExactDist2d(member); @@ -570,49 +523,53 @@ bool GruulTheDragonkillerSpreadRangedAction::Execute(Event /*event*/) } } - if (!initialPositions.count(bot->GetGUID())) + const Position& position = GRUUL_TANK_POSITION; + + if (_initialPosition.GetPositionX() == 0.0f && _initialPosition.GetPositionY() == 0.0f) { auto it = std::find(members.begin(), members.end(), bot); uint8 botIndex = (it != members.end()) ? std::distance(members.begin(), it) : 0; uint8 count = members.size(); + constexpr float minRadius = 25.0f; + constexpr float maxRadius = 40.0f; float angle = 2 * M_PI * botIndex / count; float radius = minRadius + static_cast(rand()) / static_cast(RAND_MAX) * (maxRadius - minRadius); - float targetX = centerX + radius * cos(angle); - float targetY = centerY + radius * sin(angle); + float targetX = position.GetPositionX() + radius * cos(angle); + float targetY = position.GetPositionY() + radius * sin(angle); - initialPositions[bot->GetGUID()] = Position(targetX, targetY, centerZ); - hasReachedInitialPosition[bot->GetGUID()] = false; + _initialPosition = Position(targetX, targetY, position.GetPositionZ()); } - Position targetPosition = initialPositions[bot->GetGUID()]; - if (!hasReachedInitialPosition[bot->GetGUID()]) + if (!_hasReachedInitialPosition) { - if (!bot->IsWithinDist2d(targetPosition.GetPositionX(), targetPosition.GetPositionY(), 2.0f)) + const float distanceToTarget = + bot->GetExactDist2d(_initialPosition.GetPositionX(), _initialPosition.GetPositionY()); + if (distanceToTarget > 2.0f) { - float destX = targetPosition.GetPositionX(); - float destY = targetPosition.GetPositionY(); - float destZ = targetPosition.GetPositionZ(); + float destX = _initialPosition.GetPositionX(); + float destY = _initialPosition.GetPositionY(); + float destZ = _initialPosition.GetPositionZ(); if (!bot->GetMap()->CheckCollisionAndGetValidCoords(bot, bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ(), destX, destY, destZ)) + { return false; + } - return MoveTo(GRUULS_LAIR_MAP_ID, destX, destY, destZ, false, false, false, false, - MovementPriority::MOVEMENT_COMBAT, true, false); + return MoveTo(GRUULS_LAIR_MAP_ID, destX, destY, destZ, false, false, false, + false, MovementPriority::MOVEMENT_COMBAT, true, false); } - hasReachedInitialPosition[bot->GetGUID()] = true; + _hasReachedInitialPosition = true; } - - const float minSpreadDistance = 10.0f; - const float movementThreshold = 2.0f; - - if (closestMember && closestDist < minSpreadDistance - movementThreshold) + else { - return FleePosition(Position(closestMember->GetPositionX(), closestMember->GetPositionY(), - closestMember->GetPositionZ()), minSpreadDistance, 0); + constexpr float minSpreadDistance = 10.0f; + constexpr uint32 minInterval = 1000; + if (closestMember && closestDist < minSpreadDistance) + return FleePosition(closestMember->GetPosition(), minSpreadDistance, minInterval); } return false; @@ -621,33 +578,10 @@ bool GruulTheDragonkillerSpreadRangedAction::Execute(Event /*event*/) // Try to get away from other group members when Ground Slam is cast bool GruulTheDragonkillerShatterSpreadAction::Execute(Event /*event*/) { - Group* group = bot->GetGroup(); - if (!group) - return false; - - GuidVector members = AI_VALUE(GuidVector, "group members"); - Unit* closestMember = nullptr; - float closestDist = std::numeric_limits::max(); - - for (auto& member : members) - { - Unit* unit = botAI->GetUnit(member); - if (!unit || bot->GetGUID() == member) - continue; - - const float dist = bot->GetExactDist2d(unit); - if (dist < closestDist) - { - closestDist = dist; - closestMember = unit; - } - } - - if (closestMember) - { - return FleePosition(Position(closestMember->GetPositionX(), closestMember->GetPositionY(), - closestMember->GetPositionZ()), 6.0f, 0); - } + constexpr float safeDistance = 10.0f; + constexpr uint32 minInterval = 0; + if (Unit* nearestPlayer = GetNearestPlayerInRadius(bot, safeDistance)) + return FleePosition(nearestPlayer->GetPosition(), safeDistance, minInterval); return false; } diff --git a/src/Ai/Raid/Gruul/GruulActions.h b/src/Ai/Raid/Gruul/GruulActions.h index 1ac4e4d1d..4cf858f8c 100644 --- a/src/Ai/Raid/Gruul/GruulActions.h +++ b/src/Ai/Raid/Gruul/GruulActions.h @@ -1,5 +1,5 @@ -#ifndef PLAYERBOTS_RAIDGRUULSLAIRACTIONS_H -#define PLAYERBOTS_RAIDGRUULSLAIRACTIONS_H +#ifndef PLAYERBOTS_GRUULACTIONS_H +#define PLAYERBOTS_GRUULACTIONS_H #include "Action.h" #include "AttackAction.h" @@ -8,104 +8,108 @@ class HighKingMaulgarMainTankAttackMaulgarAction : public AttackAction { public: - HighKingMaulgarMainTankAttackMaulgarAction(PlayerbotAI* botAI, std::string const name = "high king maulgar main tank attack maulgar") : AttackAction(botAI, name) {}; - + HighKingMaulgarMainTankAttackMaulgarAction( + PlayerbotAI* botAI, std::string const name = "high king maulgar main tank attack maulgar") : AttackAction(botAI, name) {}; bool Execute(Event event) override; }; class HighKingMaulgarFirstAssistTankAttackOlmAction : public AttackAction { public: - HighKingMaulgarFirstAssistTankAttackOlmAction(PlayerbotAI* botAI, std::string const name = "high king maulgar first assist tank attack olm") : AttackAction(botAI, name) {}; - + HighKingMaulgarFirstAssistTankAttackOlmAction( + PlayerbotAI* botAI, std::string const name = "high king maulgar first assist tank attack olm") : AttackAction(botAI, name) {}; bool Execute(Event event) override; }; class HighKingMaulgarSecondAssistTankAttackBlindeyeAction : public AttackAction { public: - HighKingMaulgarSecondAssistTankAttackBlindeyeAction(PlayerbotAI* botAI, std::string const name = "high king maulgar second assist tank attack blindeye") : AttackAction(botAI, name) {}; - + HighKingMaulgarSecondAssistTankAttackBlindeyeAction( + PlayerbotAI* botAI, std::string const name = "high king maulgar second assist tank attack blindeye") : AttackAction(botAI, name) {}; bool Execute(Event event) override; }; class HighKingMaulgarMageTankAttackKroshAction : public AttackAction { public: - HighKingMaulgarMageTankAttackKroshAction(PlayerbotAI* botAI, std::string const name = "high king maulgar mage tank attack krosh") : AttackAction(botAI, name) {}; - + HighKingMaulgarMageTankAttackKroshAction( + PlayerbotAI* botAI, std::string const name = "high king maulgar mage tank attack krosh") : AttackAction(botAI, name) {}; bool Execute(Event event) override; }; class HighKingMaulgarMoonkinTankAttackKigglerAction : public AttackAction { public: - HighKingMaulgarMoonkinTankAttackKigglerAction(PlayerbotAI* botAI, std::string const name = "high king maulgar moonkin tank attack kiggler") : AttackAction(botAI, name) {}; - + HighKingMaulgarMoonkinTankAttackKigglerAction( + PlayerbotAI* botAI, std::string const name = "high king maulgar moonkin tank attack kiggler") : AttackAction(botAI, name) {}; bool Execute(Event event) override; }; class HighKingMaulgarAssignDPSPriorityAction : public AttackAction { public: - HighKingMaulgarAssignDPSPriorityAction(PlayerbotAI* botAI, std::string const name = "high king maulgar assign dps priority") : AttackAction(botAI, name) {}; - - bool Execute(Event event) override; -}; - -class HighKingMaulgarHealerFindSafePositionAction : public MovementAction -{ -public: - HighKingMaulgarHealerFindSafePositionAction(PlayerbotAI* botAI, std::string const name = "high king maulgar healer find safe position") : MovementAction(botAI, name) {}; - + HighKingMaulgarAssignDPSPriorityAction( + PlayerbotAI* botAI, std::string const name = "high king maulgar assign dps priority") : AttackAction(botAI, name) {}; bool Execute(Event event) override; }; class HighKingMaulgarRunAwayFromWhirlwindAction : public MovementAction { public: - HighKingMaulgarRunAwayFromWhirlwindAction(PlayerbotAI* botAI, std::string const name = "high king maulgar run away from whirlwind") : MovementAction(botAI, name) {}; - + HighKingMaulgarRunAwayFromWhirlwindAction( + PlayerbotAI* botAI, std::string const name = "high king maulgar run away from whirlwind") : MovementAction(botAI, name) {}; bool Execute(Event event) override; }; -class HighKingMaulgarBanishFelstalkerAction : public AttackAction +class HighKingMaulgarMoveAwayFromBlastNovaDangerAction : public MovementAction { public: - HighKingMaulgarBanishFelstalkerAction(PlayerbotAI* botAI, std::string const name = "high king maulgar banish felstalker") : AttackAction(botAI, name) {}; - + HighKingMaulgarMoveAwayFromBlastNovaDangerAction( + PlayerbotAI* botAI, std::string const name = "high king maulgar move away from blast nova danger") : MovementAction(botAI, name) {}; bool Execute(Event event) override; }; -class HighKingMaulgarMisdirectOlmAndBlindeyeAction : public AttackAction +class HighKingMaulgarBanishFelStalkerAction : public AttackAction { public: - HighKingMaulgarMisdirectOlmAndBlindeyeAction(PlayerbotAI* botAI, std::string const name = "high king maulgar misdirect olm and blindeye") : AttackAction(botAI, name) {}; + HighKingMaulgarBanishFelStalkerAction( + PlayerbotAI* botAI, std::string const name = "high king maulgar banish fel stalker") : AttackAction(botAI, name) {}; + bool Execute(Event event) override; +}; +class HighKingMaulgarMisdirectOgresToTanksAction : public AttackAction +{ +public: + HighKingMaulgarMisdirectOgresToTanksAction( + PlayerbotAI* botAI, std::string const name = "high king maulgar misdirect ogres to tanks") : AttackAction(botAI, name) {}; bool Execute(Event event) override; }; class GruulTheDragonkillerTanksPositionBossAction : public AttackAction { public: - GruulTheDragonkillerTanksPositionBossAction(PlayerbotAI* botAI, std::string const name = "gruul the dragonkiller tanks position boss") : AttackAction(botAI, name) {}; - + GruulTheDragonkillerTanksPositionBossAction( + PlayerbotAI* botAI, std::string const name = "gruul the dragonkiller tanks position boss") : AttackAction(botAI, name) {}; bool Execute(Event event) override; }; class GruulTheDragonkillerSpreadRangedAction : public MovementAction { public: - GruulTheDragonkillerSpreadRangedAction(PlayerbotAI* botAI, std::string const name = "gruul the dragonkiller spread ranged") : MovementAction(botAI, name) {}; - + GruulTheDragonkillerSpreadRangedAction( + PlayerbotAI* botAI, std::string const name = "gruul the dragonkiller spread ranged") : MovementAction(botAI, name) {}; bool Execute(Event event) override; + +private: + Position _initialPosition; + bool _hasReachedInitialPosition = false; }; class GruulTheDragonkillerShatterSpreadAction : public MovementAction { public: - GruulTheDragonkillerShatterSpreadAction(PlayerbotAI* botAI, std::string const name = "gruul the dragonkiller shatter spread") : MovementAction(botAI, name) {}; - + GruulTheDragonkillerShatterSpreadAction( + PlayerbotAI* botAI, std::string const name = "gruul the dragonkiller shatter spread") : MovementAction(botAI, name) {}; bool Execute(Event event) override; }; diff --git a/src/Ai/Raid/Gruul/GruulHelpers.cpp b/src/Ai/Raid/Gruul/GruulHelpers.cpp index 4c90f6e41..50bcb3f2a 100644 --- a/src/Ai/Raid/Gruul/GruulHelpers.cpp +++ b/src/Ai/Raid/Gruul/GruulHelpers.cpp @@ -6,199 +6,96 @@ namespace GruulsLairHelpers { - // Olm does not chase properly due to the Core's caster movement issues - // Thus, the below "OlmTankPosition" is beyond the actual desired tanking location - // It is the spot to which the OlmTank runs to to pull Olm to a decent tanking location - // "MaulgarRoomCenter" is to keep healers in a centralized location - const Position MAULGAR_TANK_POSITION = { 90.686f, 167.047f, -13.234f }; - const Position OLM_TANK_POSITION = { 87.485f, 234.942f, -3.635f }; - const Position BLINDEYE_TANK_POSITION = { 99.681f, 213.989f, -10.345f }; - const Position KROSH_TANK_POSITION = { 116.880f, 166.208f, -14.231f }; - const Position MAULGAR_ROOM_CENTER = { 88.754f, 150.759f, -11.569f }; - const Position GRUUL_TANK_POSITION = { 241.238f, 365.025f, -4.220f }; - bool IsAnyOgreBossAlive(PlayerbotAI* botAI) +const Position MAULGAR_TANK_POSITION = { 90.686f, 167.047f, -13.234f }; +const Position OLM_TANK_POSITION = { 101.050f, 219.359f, -9.503f }; +const Position BLINDEYE_TANK_POSITION = { 99.681f, 213.989f, -10.345f }; +const Position KROSH_TANK_POSITION = { 116.880f, 166.208f, -14.231f }; +const Position MAULGAR_ROOM_CENTER = { 88.754f, 150.759f, -11.569f }; +const Position GRUUL_TANK_POSITION = { 241.238f, 365.025f, -4.220f }; + +Player* GetKroshMageTank(Player* bot) +{ + Group* group = bot->GetGroup(); + if (!group) + return nullptr; + + // (1) First loop: Return the first assistant Mage (real player or bot) + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) { - const char* ogreBossNames[] = - { - "high king maulgar", - "kiggler the crazed", - "krosh firehand", - "olm the summoner", - "blindeye the seer" - }; + Player* member = ref->GetSource(); + if (!member || !member->IsAlive() || member->getClass() != CLASS_MAGE) + continue; - for (const char* name : ogreBossNames) - { - Unit* boss = botAI->GetAiObjectContext()->GetValue("find target", name)->Get(); - if (!boss || !boss->IsAlive()) - continue; - return true; - } + if (group->IsAssistant(member->GetGUID())) + return member; - return false; } - bool IsKroshMageTank(Player* bot) + // (2) Fall back to bot Mage with highest HP + Player* highestHpMage = nullptr; + uint32 highestHp = 0; + + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) { - Group* group = bot->GetGroup(); - if (!group) - return false; - - // (1) First loop: Return the first assistant Mage (real player or bot) - for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + Player* member = ref->GetSource(); + if (!member || !member->IsAlive() || !GET_PLAYERBOT_AI(member) || + member->getClass() != CLASS_MAGE) { - Player* member = ref->GetSource(); - if (!member || !member->IsAlive() || member->getClass() != CLASS_MAGE) - continue; - - if (group->IsAssistant(member->GetGUID())) - return member == bot; + continue; } - // (2) Fall back to bot Mage with highest HP - Player* highestHpMage = nullptr; - uint32 highestHp = 0; - - for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + uint32 hp = member->GetMaxHealth(); + if (!highestHpMage || hp > highestHp) { - Player* member = ref->GetSource(); - if (!member || !member->IsAlive() || !GET_PLAYERBOT_AI(member) || - member->getClass() != CLASS_MAGE) - continue; - - uint32 hp = member->GetMaxHealth(); - if (!highestHpMage || hp > highestHp) - { - highestHpMage = member; - highestHp = hp; - } + highestHpMage = member; + highestHp = hp; } - - // (3) Return the found Mage tank, or nullptr if none found - return highestHpMage == bot; } - bool IsKigglerMoonkinTank(Player* bot) - { - Group* group = bot->GetGroup(); - if (!group) - return false; - - // (1) First loop: Return the first assistant Moonkin (real player or bot) - for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) - { - Player* member = ref->GetSource(); - if (!member || !member->IsAlive() || member->getClass() != CLASS_DRUID) - continue; - - if (group->IsAssistant(member->GetGUID()) && - AiFactory::GetPlayerSpecTab(member) == DRUID_TAB_BALANCE) - return member == bot; - } - - // (2) Fall back to bot Moonkin with highest HP - Player* highestHpMoonkin = nullptr; - uint32 highestHp = 0; - for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) - { - Player* member = ref->GetSource(); - if (!member || !member->IsAlive() || member->getClass() != CLASS_DRUID || - !GET_PLAYERBOT_AI(member) || AiFactory::GetPlayerSpecTab(member) != DRUID_TAB_BALANCE) - continue; - - uint32 hp = member->GetMaxHealth(); - if (!highestHpMoonkin || hp > highestHp) - { - highestHpMoonkin = member; - highestHp = hp; - } - } - - // (3) Return the found Moonkin tank, or nullptr if none found - return highestHpMoonkin == bot; - } - - bool IsPositionSafe(PlayerbotAI* botAI, Player* bot, Position pos) - { - const float KROSH_SAFE_DISTANCE = 20.0f; - const float MAULGAR_SAFE_DISTANCE = 10.0f; - bool isSafe = true; - - Unit* krosh = botAI->GetAiObjectContext()->GetValue("find target", "krosh firehand")->Get(); - if (krosh && krosh->IsAlive()) - { - float dist = krosh->GetDistance2d(pos.GetPositionX(), pos.GetPositionY()); - if (dist < KROSH_SAFE_DISTANCE) - isSafe = false; - } - - Unit* maulgar = botAI->GetAiObjectContext()->GetValue("find target", "high king maulgar")->Get(); - if (botAI->IsRanged(bot) && maulgar && maulgar->IsAlive()) - { - float dist = maulgar->GetDistance2d(pos.GetPositionX(), pos.GetPositionY()); - if (dist < MAULGAR_SAFE_DISTANCE) - isSafe = false; - } - - return isSafe; - } - - bool TryGetNewSafePosition(PlayerbotAI* botAI, Player* bot, Position& outPos) - { - const float SEARCH_RADIUS = 30.0f; - const uint8 NUM_POSITIONS = 32; - - outPos = { bot->GetPositionX(), bot->GetPositionY(), bot->GetPositionZ() }; - if (IsPositionSafe(botAI, bot, outPos)) - { - outPos = Position(); - return false; - } - - float bestScore = std::numeric_limits::max(); - bool foundSafeSpot = false; - Position bestPos; - - for (int i = 0; i < NUM_POSITIONS; ++i) - { - float angle = 2 * M_PI * i / NUM_POSITIONS; - Position candidatePos; - candidatePos.Relocate(bot->GetPositionX() + SEARCH_RADIUS * cos(angle), - bot->GetPositionY() + SEARCH_RADIUS * sin(angle), - bot->GetPositionZ()); - - float destX = candidatePos.GetPositionX(); - float destY = candidatePos.GetPositionY(); - float destZ = candidatePos.GetPositionZ(); - if (!bot->GetMap()->CheckCollisionAndGetValidCoords(bot, bot->GetPositionX(), bot->GetPositionY(), - bot->GetPositionZ(), destX, destY, destZ, true)) - continue; - - if (destX != candidatePos.GetPositionX() || destY != candidatePos.GetPositionY()) - continue; - - candidatePos.Relocate(destX, destY, destZ); - - if (IsPositionSafe(botAI, bot, candidatePos)) - { - float movementDistance = bot->GetDistance2d(destX, destY); - if (movementDistance < bestScore) - { - bestScore = movementDistance; - bestPos = candidatePos; - foundSafeSpot = true; - } - } - } - - if (foundSafeSpot) - { - outPos = bestPos; - return true; - } - - outPos = Position(); - return false; - } + return highestHpMage; +} + +Player* GetKigglerMoonkinTank(Player* bot) +{ + Group* group = bot->GetGroup(); + if (!group) + return nullptr; + + uint8 tab = AiFactory::GetPlayerSpecTab(bot); + + // (1) First loop: Return the first assistant Moonkin (real player or bot) + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + if (!member || !member->IsAlive() || member->getClass() != CLASS_DRUID) + continue; + + if (group->IsAssistant(member->GetGUID()) && tab == DRUID_TAB_BALANCE) + return member; + } + + // (2) Fall back to bot Moonkin with highest HP + Player* highestHpMoonkin = nullptr; + uint32 highestHp = 0; + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + if (!member || !member->IsAlive() || member->getClass() != CLASS_DRUID || + !GET_PLAYERBOT_AI(member) || tab != DRUID_TAB_BALANCE) + { + continue; + } + + uint32 hp = member->GetMaxHealth(); + if (!highestHpMoonkin || hp > highestHp) + { + highestHpMoonkin = member; + highestHp = hp; + } + } + + return highestHpMoonkin; +} + } diff --git a/src/Ai/Raid/Gruul/GruulHelpers.h b/src/Ai/Raid/Gruul/GruulHelpers.h index f9315565b..9ef913b85 100644 --- a/src/Ai/Raid/Gruul/GruulHelpers.h +++ b/src/Ai/Raid/Gruul/GruulHelpers.h @@ -1,45 +1,44 @@ -#ifndef RAID_GRUULSLAIRHELPERS_H -#define RAID_GRUULSLAIRHELPERS_H +#ifndef PLAYERBOTS_GRUULHELPERS_H +#define PLAYERBOTS_GRUULHELPERS_H #include "PlayerbotAI.h" namespace GruulsLairHelpers { - enum GruulsLairSpells - { - // High King Maulgar - SPELL_WHIRLWIND = 33238, - // Krosh Firehand - SPELL_SPELL_SHIELD = 33054, +enum class GruulsLairSpells : uint32 +{ + // High King Maulgar + SPELL_WHIRLWIND = 33238, - // Hunter - SPELL_MISDIRECTION = 35079, + // Krosh Firehand + SPELL_SPELL_SHIELD = 33054, - // Gruul the Dragonkiller - SPELL_GROUND_SLAM_1 = 33525, - SPELL_GROUND_SLAM_2 = 39187, - }; + // Hunter + SPELL_MISDIRECTION = 35079, - enum GruulsLairNPCs - { - NPC_WILD_FEL_STALKER = 18847, - }; + // Gruul the Dragonkiller + SPELL_GROUND_SLAM_1 = 33525, + SPELL_GROUND_SLAM_2 = 39187, +}; - constexpr uint32 GRUULS_LAIR_MAP_ID = 565; +enum class GruulsLairNpcs : uint32 +{ + NPC_WILD_FEL_STALKER = 18847, +}; - bool IsAnyOgreBossAlive(PlayerbotAI* botAI); - bool IsKroshMageTank(Player* bot); - bool IsKigglerMoonkinTank(Player* bot); - bool IsPositionSafe(PlayerbotAI* botAI, Player* bot, Position pos); - bool TryGetNewSafePosition(PlayerbotAI* botAI, Player* bot, Position& outPos); +constexpr uint32 GRUULS_LAIR_MAP_ID = 565; + +Player* GetKroshMageTank(Player* bot); +Player* GetKigglerMoonkinTank(Player* bot); + +extern const Position MAULGAR_TANK_POSITION; +extern const Position OLM_TANK_POSITION; +extern const Position BLINDEYE_TANK_POSITION; +extern const Position KROSH_TANK_POSITION; +extern const Position MAULGAR_ROOM_CENTER; +extern const Position GRUUL_TANK_POSITION; - extern const Position MAULGAR_TANK_POSITION; - extern const Position OLM_TANK_POSITION; - extern const Position BLINDEYE_TANK_POSITION; - extern const Position KROSH_TANK_POSITION; - extern const Position MAULGAR_ROOM_CENTER; - extern const Position GRUUL_TANK_POSITION; } #endif diff --git a/src/Ai/Raid/Gruul/GruulMultipliers.cpp b/src/Ai/Raid/Gruul/GruulMultipliers.cpp index e5a02698f..f518f869a 100644 --- a/src/Ai/Raid/Gruul/GruulMultipliers.cpp +++ b/src/Ai/Raid/Gruul/GruulMultipliers.cpp @@ -2,44 +2,69 @@ #include "GruulActions.h" #include "GruulHelpers.h" #include "ChooseTargetActions.h" -#include "DruidBearActions.h" -#include "DruidCatActions.h" #include "GenericSpellActions.h" #include "HunterActions.h" -#include "MageActions.h" #include "Playerbots.h" #include "ReachTargetActions.h" -#include "WarriorActions.h" +#include "ShamanActions.h" using namespace GruulsLairHelpers; -float HighKingMaulgarDisableTankAssistMultiplier::GetValue(Action* action) +float HighKingMaulgarDelayBloodlustAndHeroismMultiplier::GetValue(Action* action) { - if (bot->GetVictim() == nullptr) + if (bot->getClass() != CLASS_SHAMAN) return 1.0f; - if (IsAnyOgreBossAlive(botAI) && dynamic_cast(action)) + Unit* blindeye = AI_VALUE2(Unit*, "find target", "blindeye the seer"); + if (!blindeye || blindeye->GetHealthPct() < 75.0f) + return 1.0f; + + if (dynamic_cast(action) || + dynamic_cast(action)) + { return 0.0f; + } + + return 1.0f; +} + +float HighKingMaulgarControlTankActionsMultiplier::GetValue(Action* action) +{ + if (!botAI->IsTank(bot)) + return 1.0f; + + if (!AI_VALUE2(Unit*, "find target", "high king maulgar")) + return 1.0f; + + if (dynamic_cast(action) || + (bot->GetVictim() != nullptr && dynamic_cast(action))) + { + return 0.0f; + } return 1.0f; } -// Don't run back in during Whirlwind float HighKingMaulgarAvoidWhirlwindMultiplier::GetValue(Action* action) { Unit* maulgar = AI_VALUE2(Unit*, "find target", "high king maulgar"); - Unit* kiggler = AI_VALUE2(Unit*, "find target", "kiggler the crazed"); - Unit* krosh = AI_VALUE2(Unit*, "find target", "krosh firehand"); - Unit* olm = AI_VALUE2(Unit*, "find target", "olm the summoner"); - Unit* blindeye = AI_VALUE2(Unit*, "find target", "blindeye the seer"); - - if (maulgar && maulgar->HasAura(SPELL_WHIRLWIND) && - !kiggler && !krosh && !olm && !blindeye) + if (!maulgar || + !maulgar->HasAura(static_cast(GruulsLairSpells::SPELL_WHIRLWIND))) { - if (dynamic_cast(action) || - (dynamic_cast(action) && - !dynamic_cast(action))) - return 0.0f; + return 1.0f; + } + + if (AI_VALUE(Unit*, "current target") != maulgar) + return 1.0f; + + if (botAI->IsMainTank(bot)) + return 1.0f; + + if (dynamic_cast(action) || + (dynamic_cast(action) && + !dynamic_cast(action))) + { + return 0.0f; } return 1.0f; @@ -48,57 +73,83 @@ float HighKingMaulgarAvoidWhirlwindMultiplier::GetValue(Action* action) // Arcane Shot will remove Spell Shield, which the mage tank needs to survive float HighKingMaulgarDisableArcaneShotOnKroshMultiplier::GetValue(Action* action) { + if (bot->getClass() != CLASS_HUNTER) + return 1.0f; + Unit* krosh = AI_VALUE2(Unit*, "find target", "krosh firehand"); + if (!krosh || AI_VALUE(Unit*, "current target") != krosh) + return 1.0f; - if (krosh && AI_VALUE(Unit*, "current target") == krosh && - dynamic_cast(action)) + if (dynamic_cast(action)) return 0.0f; return 1.0f; } -float HighKingMaulgarDisableMageTankAOEMultiplier::GetValue(Action* action) +float HighKingMaulgarDisableMageTankAoeMultiplier::GetValue(Action* action) { - if (IsKroshMageTank(bot) && - (dynamic_cast(action) || dynamic_cast(action) || - dynamic_cast(action) || dynamic_cast(action) || - dynamic_cast(action) || dynamic_cast(action))) + if (bot->getClass() != CLASS_MAGE) + return 1.0f; + + if (!AI_VALUE2(Unit*, "find target", "krosh firehand")) + return 1.0f; + + auto castSpellAction = dynamic_cast(action); + if (castSpellAction && + castSpellAction->getThreatType() == Action::ActionThreatType::Aoe) + { return 0.0f; - - return 1.0f; -} - -float GruulTheDragonkillerMainTankMovementMultiplier::GetValue(Action* action) -{ - Unit* gruul = AI_VALUE2(Unit*, "find target", "gruul the dragonkiller"); - if (!gruul) - return 1.0f; - - if (botAI->IsMainTank(bot)) - { - if (gruul->GetVictim() == bot && dynamic_cast(action)) - return 0.0f; - - if (dynamic_cast(action)) - return 0.0f; } return 1.0f; } -float GruulTheDragonkillerGroundSlamMultiplier::GetValue(Action* action) +float GruulTheDragonkillerDelayBloodlustAndHeroismMultiplier::GetValue(Action* action) { - Unit* gruul = AI_VALUE2(Unit*, "find target", "gruul the dragonkiller"); - if (!gruul) + if (bot->getClass() != CLASS_SHAMAN) return 1.0f; - if (bot->HasAura(SPELL_GROUND_SLAM_1) || - bot->HasAura(SPELL_GROUND_SLAM_2)) + Unit* gruul = AI_VALUE2(Unit*, "find target", "gruul the dragonkiller"); + if (!gruul || gruul->GetHealthPct() < 95.0f) + return 1.0f; + + if (dynamic_cast(action) || + dynamic_cast(action)) { - if ((dynamic_cast(action) && - !dynamic_cast(action)) || - dynamic_cast(action)) - return 0.0f; + return 0.0f; + } + + return 1.0f; +} + +float GruulTheDragonkillerControlTankMovementMultiplier::GetValue(Action* action) +{ + Unit* gruul = AI_VALUE2(Unit*, "find target", "gruul the dragonkiller"); + if (!gruul || gruul->GetVictim() != bot) + return 1.0f; + + if (dynamic_cast(action) || + dynamic_cast(action)) + { + return 0.0f; + } + + return 1.0f; +} + +float GruulTheDragonkillerStaySpreadForShatterMultiplier::GetValue(Action* action) +{ + if (!bot->HasAura(static_cast(GruulsLairSpells::SPELL_GROUND_SLAM_1)) && + !bot->HasAura(static_cast(GruulsLairSpells::SPELL_GROUND_SLAM_2))) + { + return 1.0f; + } + + if (dynamic_cast(action) || + (dynamic_cast(action) && + !dynamic_cast(action))) + { + return 0.0f; } return 1.0f; diff --git a/src/Ai/Raid/Gruul/GruulMultipliers.h b/src/Ai/Raid/Gruul/GruulMultipliers.h index f64294968..b15f382ad 100644 --- a/src/Ai/Raid/Gruul/GruulMultipliers.h +++ b/src/Ai/Raid/Gruul/GruulMultipliers.h @@ -1,47 +1,69 @@ -#ifndef PLAYERBOTS_RAIDGRUULSLAIRMULTIPLIERS_H -#define PLAYERBOTS_RAIDGRUULSLAIRMULTIPLIERS_H +#ifndef PLAYERBOTS_GRUULMULTIPLIERS_H +#define PLAYERBOTS_GRUULMULTIPLIERS_H #include "Multiplier.h" -class HighKingMaulgarDisableTankAssistMultiplier : public Multiplier +class HighKingMaulgarDelayBloodlustAndHeroismMultiplier : public Multiplier { public: - HighKingMaulgarDisableTankAssistMultiplier(PlayerbotAI* botAI) : Multiplier(botAI, "high king maulgar disable tank assist multiplier") {} + HighKingMaulgarDelayBloodlustAndHeroismMultiplier( + PlayerbotAI* botAI) : Multiplier(botAI, "high king maulgar delay bloodlust and heroism multiplier") {} + float GetValue(Action* action) override; +}; + +class HighKingMaulgarControlTankActionsMultiplier : public Multiplier +{ +public: + HighKingMaulgarControlTankActionsMultiplier( + PlayerbotAI* botAI) : Multiplier(botAI, "high king maulgar control tank actions multiplier") {} float GetValue(Action* action) override; }; class HighKingMaulgarAvoidWhirlwindMultiplier : public Multiplier { public: - HighKingMaulgarAvoidWhirlwindMultiplier(PlayerbotAI* botAI) : Multiplier(botAI, "high king maulgar avoid whirlwind multiplier") {} + HighKingMaulgarAvoidWhirlwindMultiplier( + PlayerbotAI* botAI) : Multiplier(botAI, "high king maulgar avoid whirlwind multiplier") {} float GetValue(Action* action) override; }; class HighKingMaulgarDisableArcaneShotOnKroshMultiplier : public Multiplier { public: - HighKingMaulgarDisableArcaneShotOnKroshMultiplier(PlayerbotAI* botAI) : Multiplier(botAI, "high king maulgar disable arcane shot on krosh multiplier") {} + HighKingMaulgarDisableArcaneShotOnKroshMultiplier( + PlayerbotAI* botAI) : Multiplier(botAI, "high king maulgar disable arcane shot on krosh multiplier") {} float GetValue(Action* action) override; }; -class HighKingMaulgarDisableMageTankAOEMultiplier : public Multiplier +class HighKingMaulgarDisableMageTankAoeMultiplier : public Multiplier { public: - HighKingMaulgarDisableMageTankAOEMultiplier(PlayerbotAI* botAI) : Multiplier(botAI, "high king maulgar disable mage tank aoe multiplier") {} + HighKingMaulgarDisableMageTankAoeMultiplier( + PlayerbotAI* botAI) : Multiplier(botAI, "high king maulgar disable mage tank aoe multiplier") {} float GetValue(Action* action) override; }; -class GruulTheDragonkillerMainTankMovementMultiplier : public Multiplier +class GruulTheDragonkillerDelayBloodlustAndHeroismMultiplier : public Multiplier { public: - GruulTheDragonkillerMainTankMovementMultiplier(PlayerbotAI* botAI) : Multiplier(botAI, "gruul the dragonkiller main tank movement multiplier") {} + GruulTheDragonkillerDelayBloodlustAndHeroismMultiplier( + PlayerbotAI* botAI) : Multiplier(botAI, "gruul the dragonkiller delay bloodlust and heroism multiplier") {} float GetValue(Action* action) override; }; -class GruulTheDragonkillerGroundSlamMultiplier : public Multiplier +class GruulTheDragonkillerControlTankMovementMultiplier : public Multiplier { public: - GruulTheDragonkillerGroundSlamMultiplier(PlayerbotAI* botAI) : Multiplier(botAI, "gruul the dragonkiller ground slam multiplier") {} + GruulTheDragonkillerControlTankMovementMultiplier( + PlayerbotAI* botAI) : Multiplier(botAI, "gruul the dragonkiller control tank movement multiplier") {} + float GetValue(Action* action) override; +}; + +class GruulTheDragonkillerStaySpreadForShatterMultiplier : public Multiplier +{ +public: + GruulTheDragonkillerStaySpreadForShatterMultiplier( + PlayerbotAI* botAI) : Multiplier(botAI, "gruul the dragonkiller stay spread for shatter multiplier") {} float GetValue(Action* action) override; }; diff --git a/src/Ai/Raid/Gruul/GruulStrategy.cpp b/src/Ai/Raid/Gruul/GruulStrategy.cpp index 4f4297650..8cac48f32 100644 --- a/src/Ai/Raid/Gruul/GruulStrategy.cpp +++ b/src/Ai/Raid/Gruul/GruulStrategy.cpp @@ -4,35 +4,35 @@ void RaidGruulsLairStrategy::InitTriggers(std::vector& triggers) { // High King Maulgar - triggers.push_back(new TriggerNode("high king maulgar is main tank", { + triggers.push_back(new TriggerNode("high king maulgar boss engaged by main tank", { NextAction("high king maulgar main tank attack maulgar", ACTION_RAID + 1) })); - triggers.push_back(new TriggerNode("high king maulgar is first assist tank", { + triggers.push_back(new TriggerNode("high king maulgar olm engaged by first assist tank", { NextAction("high king maulgar first assist tank attack olm", ACTION_RAID + 1) })); - triggers.push_back(new TriggerNode("high king maulgar is second assist tank", { + triggers.push_back(new TriggerNode("high king maulgar blindeye engaged by second assist tank", { NextAction("high king maulgar second assist tank attack blindeye", ACTION_RAID + 1) })); - triggers.push_back(new TriggerNode("high king maulgar is mage tank", { + triggers.push_back(new TriggerNode("high king maulgar krosh engaged by mage tank", { NextAction("high king maulgar mage tank attack krosh", ACTION_RAID + 1) })); - triggers.push_back(new TriggerNode("high king maulgar is moonkin tank", { + triggers.push_back(new TriggerNode("high king maulgar kiggler engaged by moonkin tank", { NextAction("high king maulgar moonkin tank attack kiggler", ACTION_RAID + 1) })); triggers.push_back(new TriggerNode("high king maulgar determining kill order", { NextAction("high king maulgar assign dps priority", ACTION_RAID + 1) })); - triggers.push_back(new TriggerNode("high king maulgar healer in danger", { - NextAction("high king maulgar healer find safe position", ACTION_RAID + 1) })); - triggers.push_back(new TriggerNode("high king maulgar boss channeling whirlwind", { - NextAction("high king maulgar run away from whirlwind", ACTION_EMERGENCY + 6) })); + NextAction("high king maulgar run away from whirlwind", ACTION_EMERGENCY + 7) })); - triggers.push_back(new TriggerNode("high king maulgar wild felstalker spawned", { - NextAction("high king maulgar banish felstalker", ACTION_RAID + 2) })); + triggers.push_back(new TriggerNode("high king maulgar krosh casts blast wave", { + NextAction("high king maulgar move away from blast nova danger", ACTION_EMERGENCY + 6) })); - triggers.push_back(new TriggerNode("high king maulgar pulling olm and blindeye", { - NextAction("high king maulgar misdirect olm and blindeye", ACTION_RAID + 2) })); + triggers.push_back(new TriggerNode("high king maulgar wild fel stalker spawned", { + NextAction("high king maulgar banish fel stalker", ACTION_RAID + 2) })); + + triggers.push_back(new TriggerNode("high king maulgar pulling ogre council", { + NextAction("high king maulgar misdirect ogres to tanks", ACTION_RAID + 2) })); // Gruul the Dragonkiller triggers.push_back(new TriggerNode("gruul the dragonkiller boss engaged by tanks", { @@ -47,10 +47,15 @@ void RaidGruulsLairStrategy::InitTriggers(std::vector& triggers) void RaidGruulsLairStrategy::InitMultipliers(std::vector& multipliers) { - multipliers.push_back(new HighKingMaulgarDisableTankAssistMultiplier(botAI)); + // High King Maulgar + multipliers.push_back(new HighKingMaulgarDelayBloodlustAndHeroismMultiplier(botAI)); + multipliers.push_back(new HighKingMaulgarControlTankActionsMultiplier(botAI)); multipliers.push_back(new HighKingMaulgarAvoidWhirlwindMultiplier(botAI)); multipliers.push_back(new HighKingMaulgarDisableArcaneShotOnKroshMultiplier(botAI)); - multipliers.push_back(new HighKingMaulgarDisableMageTankAOEMultiplier(botAI)); - multipliers.push_back(new GruulTheDragonkillerMainTankMovementMultiplier(botAI)); - multipliers.push_back(new GruulTheDragonkillerGroundSlamMultiplier(botAI)); + multipliers.push_back(new HighKingMaulgarDisableMageTankAoeMultiplier(botAI)); + + // Gruul the Dragonkiller + multipliers.push_back(new GruulTheDragonkillerDelayBloodlustAndHeroismMultiplier(botAI)); + multipliers.push_back(new GruulTheDragonkillerControlTankMovementMultiplier(botAI)); + multipliers.push_back(new GruulTheDragonkillerStaySpreadForShatterMultiplier(botAI)); } diff --git a/src/Ai/Raid/Gruul/GruulStrategy.h b/src/Ai/Raid/Gruul/GruulStrategy.h index a494d5c20..2c233b910 100644 --- a/src/Ai/Raid/Gruul/GruulStrategy.h +++ b/src/Ai/Raid/Gruul/GruulStrategy.h @@ -1,5 +1,5 @@ -#ifndef PLAYERBOTS_RAIDGRUULSLAIRSTRATEGY_H -#define PLAYERBOTS_RAIDGRUULSLAIRSTRATEGY_H +#ifndef PLAYERBOTS_GRUULSTRATEGY_H +#define PLAYERBOTS_GRUULSTRATEGY_H #include "Strategy.h" diff --git a/src/Ai/Raid/Gruul/GruulTriggerContext.h b/src/Ai/Raid/Gruul/GruulTriggerContext.h index b1adf8dec..0989ee5e9 100644 --- a/src/Ai/Raid/Gruul/GruulTriggerContext.h +++ b/src/Ai/Raid/Gruul/GruulTriggerContext.h @@ -1,5 +1,5 @@ -#ifndef PLAYERBOTS_RAIDGRUULSLAIRTRIGGERCONTEXT_H -#define PLAYERBOTS_RAIDGRUULSLAIRTRIGGERCONTEXT_H +#ifndef PLAYERBOTS_GRUULTRIGGERCONTEXT_H +#define PLAYERBOTS_GRUULTRIGGERCONTEXT_H #include "GruulTriggers.h" #include "NamedObjectContext.h" @@ -10,40 +10,101 @@ public: RaidGruulsLairTriggerContext() : NamedObjectContext() { // High King Maulgar - creators["high king maulgar is main tank"] = &RaidGruulsLairTriggerContext::high_king_maulgar_is_main_tank; - creators["high king maulgar is first assist tank"] = &RaidGruulsLairTriggerContext::high_king_maulgar_is_first_assist_tank; - creators["high king maulgar is second assist tank"] = &RaidGruulsLairTriggerContext::high_king_maulgar_is_second_assist_tank; - creators["high king maulgar is mage tank"] = &RaidGruulsLairTriggerContext::high_king_maulgar_is_mage_tank; - creators["high king maulgar is moonkin tank"] = &RaidGruulsLairTriggerContext::high_king_maulgar_is_moonkin_tank; - creators["high king maulgar determining kill order"] = &RaidGruulsLairTriggerContext::high_king_maulgar_determining_kill_order; - creators["high king maulgar healer in danger"] = &RaidGruulsLairTriggerContext::high_king_maulgar_healer_in_danger; - creators["high king maulgar boss channeling whirlwind"] = &RaidGruulsLairTriggerContext::high_king_maulgar_boss_channeling_whirlwind; - creators["high king maulgar wild felstalker spawned"] = &RaidGruulsLairTriggerContext::high_king_maulgar_wild_felstalker_spawned; - creators["high king maulgar pulling olm and blindeye"] = &RaidGruulsLairTriggerContext::high_king_maulgar_pulling_olm_and_blindeye; + creators["high king maulgar boss engaged by main tank"] = + &RaidGruulsLairTriggerContext::high_king_maulgar_boss_engaged_by_main_tank; + + creators["high king maulgar olm engaged by first assist tank"] = + &RaidGruulsLairTriggerContext::high_king_maulgar_olm_engaged_by_first_assist_tank; + + creators["high king maulgar blindeye engaged by second assist tank"] = + &RaidGruulsLairTriggerContext::high_king_maulgar_blindeye_engaged_by_second_assist_tank; + + creators["high king maulgar krosh engaged by mage tank"] = + &RaidGruulsLairTriggerContext::high_king_maulgar_krosh_engaged_by_mage_tank; + + creators["high king maulgar kiggler engaged by moonkin tank"] = + &RaidGruulsLairTriggerContext::high_king_maulgar_kiggler_engaged_by_moonkin_tank; + + creators["high king maulgar determining kill order"] = + &RaidGruulsLairTriggerContext::high_king_maulgar_determining_kill_order; + + creators["high king maulgar boss channeling whirlwind"] = + &RaidGruulsLairTriggerContext::high_king_maulgar_boss_channeling_whirlwind; + + creators["high king maulgar krosh casts blast wave"] = + &RaidGruulsLairTriggerContext::high_king_maulgar_krosh_casts_blast_wave; + + creators["high king maulgar wild fel stalker spawned"] = + &RaidGruulsLairTriggerContext::high_king_maulgar_wild_fel_stalker_spawned; + + creators["high king maulgar pulling ogre council"] = + &RaidGruulsLairTriggerContext::high_king_maulgar_pulling_ogre_council; // Gruul the Dragonkiller - creators["gruul the dragonkiller boss engaged by tanks"] = &RaidGruulsLairTriggerContext::gruul_the_dragonkiller_boss_engaged_by_tanks; - creators["gruul the dragonkiller boss engaged by ranged"] = &RaidGruulsLairTriggerContext::gruul_the_dragonkiller_boss_engaged_by_ranged; - creators["gruul the dragonkiller incoming shatter"] = &RaidGruulsLairTriggerContext::gruul_the_dragonkiller_incoming_shatter; + creators["gruul the dragonkiller boss engaged by tanks"] = + &RaidGruulsLairTriggerContext::gruul_the_dragonkiller_boss_engaged_by_tanks; + + creators["gruul the dragonkiller boss engaged by ranged"] = + &RaidGruulsLairTriggerContext::gruul_the_dragonkiller_boss_engaged_by_ranged; + + creators["gruul the dragonkiller incoming shatter"] = + &RaidGruulsLairTriggerContext::gruul_the_dragonkiller_incoming_shatter; } private: // High King Maulgar - static Trigger* high_king_maulgar_is_main_tank(PlayerbotAI* botAI) { return new HighKingMaulgarIsMainTankTrigger(botAI); } - static Trigger* high_king_maulgar_is_first_assist_tank(PlayerbotAI* botAI) { return new HighKingMaulgarIsFirstAssistTankTrigger(botAI); } - static Trigger* high_king_maulgar_is_second_assist_tank(PlayerbotAI* botAI) { return new HighKingMaulgarIsSecondAssistTankTrigger(botAI); } - static Trigger* high_king_maulgar_is_mage_tank(PlayerbotAI* botAI) { return new HighKingMaulgarIsMageTankTrigger(botAI); } - static Trigger* high_king_maulgar_is_moonkin_tank(PlayerbotAI* botAI) { return new HighKingMaulgarIsMoonkinTankTrigger(botAI); } - static Trigger* high_king_maulgar_determining_kill_order(PlayerbotAI* botAI) { return new HighKingMaulgarDeterminingKillOrderTrigger(botAI); } - static Trigger* high_king_maulgar_healer_in_danger(PlayerbotAI* botAI) { return new HighKingMaulgarHealerInDangerTrigger(botAI); } - static Trigger* high_king_maulgar_boss_channeling_whirlwind(PlayerbotAI* botAI) { return new HighKingMaulgarBossChannelingWhirlwindTrigger(botAI); } - static Trigger* high_king_maulgar_wild_felstalker_spawned(PlayerbotAI* botAI) { return new HighKingMaulgarWildFelstalkerSpawnedTrigger(botAI); } - static Trigger* high_king_maulgar_pulling_olm_and_blindeye(PlayerbotAI* botAI) { return new HighKingMaulgarPullingOlmAndBlindeyeTrigger(botAI); } + static Trigger* high_king_maulgar_boss_engaged_by_main_tank(PlayerbotAI* botAI) { + return new HighKingMaulgarBossEngagedByMainTankTrigger(botAI); + } + + static Trigger* high_king_maulgar_olm_engaged_by_first_assist_tank(PlayerbotAI* botAI) { + return new HighKingMaulgarOlmEngagedByFirstAssistTankTrigger(botAI); + } + + static Trigger* high_king_maulgar_blindeye_engaged_by_second_assist_tank(PlayerbotAI* botAI) { + return new HighKingMaulgarBlindeyeEngagedBySecondAssistTankTrigger(botAI); + } + + static Trigger* high_king_maulgar_krosh_engaged_by_mage_tank(PlayerbotAI* botAI) { + return new HighKingMaulgarKroshEngagedByMageTankTrigger(botAI); + } + + static Trigger* high_king_maulgar_kiggler_engaged_by_moonkin_tank(PlayerbotAI* botAI) { + return new HighKingMaulgarKigglerEngagedByMoonkinTankTrigger(botAI); + } + + static Trigger* high_king_maulgar_determining_kill_order(PlayerbotAI* botAI) { + return new HighKingMaulgarDeterminingKillOrderTrigger(botAI); + } + + static Trigger* high_king_maulgar_boss_channeling_whirlwind(PlayerbotAI* botAI) { + return new HighKingMaulgarBossChannelingWhirlwindTrigger(botAI); + } + + static Trigger* high_king_maulgar_krosh_casts_blast_wave(PlayerbotAI* botAI) { + return new HighKingMaulgarKroshCastsBlastWaveTrigger(botAI); + } + + static Trigger* high_king_maulgar_wild_fel_stalker_spawned(PlayerbotAI* botAI) { + return new HighKingMaulgarWildFelStalkerSpawnedTrigger(botAI); + } + + static Trigger* high_king_maulgar_pulling_ogre_council(PlayerbotAI* botAI) { + return new HighKingMaulgarPullingOgreCouncilTrigger(botAI); + } // Gruul the Dragonkiller - static Trigger* gruul_the_dragonkiller_boss_engaged_by_tanks(PlayerbotAI* botAI) { return new GruulTheDragonkillerBossEngagedByTanksTrigger(botAI); } - static Trigger* gruul_the_dragonkiller_boss_engaged_by_ranged(PlayerbotAI* botAI) { return new GruulTheDragonkillerBossEngagedByRangedTrigger(botAI); } - static Trigger* gruul_the_dragonkiller_incoming_shatter(PlayerbotAI* botAI) { return new GruulTheDragonkillerIncomingShatterTrigger(botAI); } + static Trigger* gruul_the_dragonkiller_boss_engaged_by_tanks(PlayerbotAI* botAI) { + return new GruulTheDragonkillerBossEngagedByTanksTrigger(botAI); + } + + static Trigger* gruul_the_dragonkiller_boss_engaged_by_ranged(PlayerbotAI* botAI) { + return new GruulTheDragonkillerBossEngagedByRangedTrigger(botAI); + } + + static Trigger* gruul_the_dragonkiller_incoming_shatter(PlayerbotAI* botAI) { + return new GruulTheDragonkillerIncomingShatterTrigger(botAI); + } }; #endif diff --git a/src/Ai/Raid/Gruul/GruulTriggers.cpp b/src/Ai/Raid/Gruul/GruulTriggers.cpp index 82c3e18ac..419f486c8 100644 --- a/src/Ai/Raid/Gruul/GruulTriggers.cpp +++ b/src/Ai/Raid/Gruul/GruulTriggers.cpp @@ -6,154 +6,113 @@ using namespace GruulsLairHelpers; // High King Maulgar Triggers -bool HighKingMaulgarIsMainTankTrigger::IsActive() +bool HighKingMaulgarBossEngagedByMainTankTrigger::IsActive() { - Unit* maulgar = AI_VALUE2(Unit*, "find target", "high king maulgar"); - - return botAI->IsMainTank(bot) && maulgar; + return botAI->IsMainTank(bot) && + AI_VALUE2(Unit*, "find target", "high king maulgar"); } -bool HighKingMaulgarIsFirstAssistTankTrigger::IsActive() +bool HighKingMaulgarOlmEngagedByFirstAssistTankTrigger::IsActive() { - Unit* olm = AI_VALUE2(Unit*, "find target", "olm the summoner"); - - return botAI->IsAssistTankOfIndex(bot, 0, false) && olm; + return botAI->IsAssistTankOfIndex(bot, 0, false) && + AI_VALUE2(Unit*, "find target", "olm the summoner"); } -bool HighKingMaulgarIsSecondAssistTankTrigger::IsActive() +bool HighKingMaulgarBlindeyeEngagedBySecondAssistTankTrigger::IsActive() { - Unit* blindeye = AI_VALUE2(Unit*, "find target", "blindeye the seer"); - - return botAI->IsAssistTankOfIndex(bot, 1, false) && blindeye; + return botAI->IsAssistTankOfIndex(bot, 1, false) && + AI_VALUE2(Unit*, "find target", "blindeye the seer"); } -bool HighKingMaulgarIsMageTankTrigger::IsActive() +bool HighKingMaulgarKroshEngagedByMageTankTrigger::IsActive() { - Unit* krosh = AI_VALUE2(Unit*, "find target", "krosh firehand"); - - return IsKroshMageTank(bot) && krosh; + return bot->getClass() == CLASS_MAGE && + AI_VALUE2(Unit*, "find target", "krosh firehand") && + GetKroshMageTank(bot) == bot; } -bool HighKingMaulgarIsMoonkinTankTrigger::IsActive() +bool HighKingMaulgarKigglerEngagedByMoonkinTankTrigger::IsActive() { - Unit* kiggler = AI_VALUE2(Unit*, "find target", "kiggler the crazed"); - - return IsKigglerMoonkinTank(bot) && kiggler; + return bot->getClass() == CLASS_DRUID && + AI_VALUE2(Unit*, "find target", "kiggler the crazed") && + GetKigglerMoonkinTank(bot) == bot; } bool HighKingMaulgarDeterminingKillOrderTrigger::IsActive() { + if (botAI->IsHeal(bot) || botAI->IsMainTank(bot)) + return false; + Unit* maulgar = AI_VALUE2(Unit*, "find target", "high king maulgar"); - Unit* kiggler = AI_VALUE2(Unit*, "find target", "kiggler the crazed"); - Unit* olm = AI_VALUE2(Unit*, "find target", "olm the summoner"); - Unit* blindeye = AI_VALUE2(Unit*, "find target", "blindeye the seer"); - Unit* krosh = AI_VALUE2(Unit*, "find target", "krosh firehand"); + if (!maulgar) + return false; - return (botAI->IsDps(bot) || botAI->IsTank(bot)) && - !(botAI->IsMainTank(bot) && maulgar) && - !(botAI->IsAssistTankOfIndex(bot, 0, false) && olm) && - !(botAI->IsAssistTankOfIndex(bot, 1, false) && blindeye) && - !(IsKroshMageTank(bot) && krosh) && - !(IsKigglerMoonkinTank(bot) && kiggler); -} + if (botAI->IsAssistTankOfIndex(bot, 0, false)) + return !AI_VALUE2(Unit*, "find target", "olm the summoner"); -bool HighKingMaulgarHealerInDangerTrigger::IsActive() -{ - return botAI->IsHeal(bot) && IsAnyOgreBossAlive(botAI); + if (botAI->IsAssistTankOfIndex(bot, 1, false)) + return !AI_VALUE2(Unit*, "find target", "blindeye the seer"); + + if (bot->getClass() == CLASS_MAGE && GetKroshMageTank(bot) == bot) + return !AI_VALUE2(Unit*, "find target", "krosh firehand"); + + if (bot->getClass() == CLASS_DRUID && GetKigglerMoonkinTank(bot) == bot) + return !AI_VALUE2(Unit*, "find target", "kiggler the crazed"); + + return true; } bool HighKingMaulgarBossChannelingWhirlwindTrigger::IsActive() { Unit* maulgar = AI_VALUE2(Unit*, "find target", "high king maulgar"); - - return maulgar && maulgar->HasAura(SPELL_WHIRLWIND) && - !botAI->IsMainTank(bot); -} - -bool HighKingMaulgarWildFelstalkerSpawnedTrigger::IsActive() -{ - Unit* felStalker = AI_VALUE2(Unit*, "find target", "wild fel stalker"); - - return felStalker && bot->getClass() == CLASS_WARLOCK; -} - -bool HighKingMaulgarPullingOlmAndBlindeyeTrigger::IsActive() -{ - Group* group = bot->GetGroup(); - if (!group || bot->getClass() != CLASS_HUNTER) - return false; - - std::vector hunters; - for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + if (!maulgar || + !maulgar->HasAura(static_cast(GruulsLairSpells::SPELL_WHIRLWIND))) { - Player* member = ref->GetSource(); - if (member && member->IsAlive() && member->getClass() == CLASS_HUNTER && GET_PLAYERBOT_AI(member)) - hunters.push_back(member); + return false; } - int hunterIndex = -1; - for (size_t i = 0; i < hunters.size(); ++i) - { - if (hunters[i] == bot) - { - hunterIndex = static_cast(i); - break; - } - } - if (hunterIndex == -1 || hunterIndex > 1) + return !botAI->IsMainTank(bot); +} + +bool HighKingMaulgarKroshCastsBlastWaveTrigger::IsActive() +{ + if (!AI_VALUE2(Unit*, "find target", "krosh firehand")) + return false; + + return !botAI->IsTank(bot) && GetKroshMageTank(bot) != bot; +} + +bool HighKingMaulgarWildFelStalkerSpawnedTrigger::IsActive() +{ + return bot->getClass() == CLASS_WARLOCK && + AI_VALUE2(Unit*, "find target", "wild fel stalker"); +} + +bool HighKingMaulgarPullingOgreCouncilTrigger::IsActive() +{ + if (bot->getClass() != CLASS_HUNTER) return false; - Unit* olm = AI_VALUE2(Unit*, "find target", "olm the summoner"); Unit* blindeye = AI_VALUE2(Unit*, "find target", "blindeye the seer"); - Player* olmTank = nullptr; - Player* blindeyeTank = nullptr; - - for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) - { - Player* member = ref->GetSource(); - if (!member || !member->IsAlive()) - continue; - else if (botAI->IsAssistTankOfIndex(member, 0)) olmTank = member; - else if (botAI->IsAssistTankOfIndex(member, 1)) blindeyeTank = member; - } - - switch (hunterIndex) - { - case 0: - return olm && olm->GetHealthPct() > 98.0f && - olmTank && botAI->CanCastSpell("misdirection", olmTank); - - case 1: - return blindeye && blindeye->GetHealthPct() > 90.0f && - blindeyeTank && botAI->CanCastSpell("misdirection", blindeyeTank); - - default: - break; - } - - return false; + return blindeye && blindeye->GetHealthPct() > 80.0f; } // Gruul the Dragonkiller Triggers bool GruulTheDragonkillerBossEngagedByTanksTrigger::IsActive() { - Unit* gruul = AI_VALUE2(Unit*, "find target", "gruul the dragonkiller"); - - return gruul && botAI->IsTank(bot); + return botAI->IsTank(bot) && + AI_VALUE2(Unit*, "find target", "gruul the dragonkiller"); } bool GruulTheDragonkillerBossEngagedByRangedTrigger::IsActive() { - Unit* gruul = AI_VALUE2(Unit*, "find target", "gruul the dragonkiller"); - - return gruul && botAI->IsRanged(bot); + return botAI->IsRanged(bot) && + AI_VALUE2(Unit*, "find target", "gruul the dragonkiller"); } bool GruulTheDragonkillerIncomingShatterTrigger::IsActive() { - Unit* gruul = AI_VALUE2(Unit*, "find target", "gruul the dragonkiller"); - - return gruul && (bot->HasAura(SPELL_GROUND_SLAM_1) || - bot->HasAura(SPELL_GROUND_SLAM_2)); + return bot->HasAura(static_cast(GruulsLairSpells::SPELL_GROUND_SLAM_1)) || + bot->HasAura(static_cast(GruulsLairSpells::SPELL_GROUND_SLAM_2)); } diff --git a/src/Ai/Raid/Gruul/GruulTriggers.h b/src/Ai/Raid/Gruul/GruulTriggers.h index 6a30fd857..2a86c9328 100644 --- a/src/Ai/Raid/Gruul/GruulTriggers.h +++ b/src/Ai/Raid/Gruul/GruulTriggers.h @@ -1,96 +1,109 @@ -#ifndef PLAYERBOTS_RAIDGRUULSLAIRTRIGGERS_H -#define PLAYERBOTS_RAIDGRUULSLAIRTRIGGERS_H +#ifndef PLAYERBOTS_GRUULTRIGGERS_H +#define PLAYERBOTS_GRUULTRIGGERS_H #include "Trigger.h" -class HighKingMaulgarIsMainTankTrigger : public Trigger +class HighKingMaulgarBossEngagedByMainTankTrigger : public Trigger { public: - HighKingMaulgarIsMainTankTrigger(PlayerbotAI* botAI) : Trigger(botAI, "high king maulgar is main tank") {} + HighKingMaulgarBossEngagedByMainTankTrigger( + PlayerbotAI* botAI) : Trigger(botAI, "high king maulgar engaged by main tank") {} bool IsActive() override; }; -class HighKingMaulgarIsFirstAssistTankTrigger : public Trigger +class HighKingMaulgarOlmEngagedByFirstAssistTankTrigger : public Trigger { public: - HighKingMaulgarIsFirstAssistTankTrigger(PlayerbotAI* botAI) : Trigger(botAI, "high king maulgar is first assist tank") {} + HighKingMaulgarOlmEngagedByFirstAssistTankTrigger( + PlayerbotAI* botAI) : Trigger(botAI, "high king maulgar olm engaged by first assist tank") {} bool IsActive() override; }; -class HighKingMaulgarIsSecondAssistTankTrigger : public Trigger +class HighKingMaulgarBlindeyeEngagedBySecondAssistTankTrigger : public Trigger { public: - HighKingMaulgarIsSecondAssistTankTrigger(PlayerbotAI* botAI) : Trigger(botAI, "high king maulgar is second assist tank") {} + HighKingMaulgarBlindeyeEngagedBySecondAssistTankTrigger( + PlayerbotAI* botAI) : Trigger(botAI, "high king maulgar blindeye engaged by second assist tank") {} bool IsActive() override; }; -class HighKingMaulgarIsMageTankTrigger : public Trigger +class HighKingMaulgarKroshEngagedByMageTankTrigger : public Trigger { public: - HighKingMaulgarIsMageTankTrigger(PlayerbotAI* botAI) : Trigger(botAI, "high king maulgar is mage tank") {} + HighKingMaulgarKroshEngagedByMageTankTrigger( + PlayerbotAI* botAI) : Trigger(botAI, "high king maulgar krosh engaged by mage tank") {} bool IsActive() override; }; -class HighKingMaulgarIsMoonkinTankTrigger : public Trigger +class HighKingMaulgarKigglerEngagedByMoonkinTankTrigger : public Trigger { public: - HighKingMaulgarIsMoonkinTankTrigger(PlayerbotAI* botAI) : Trigger(botAI, "high king maulgar is moonkin tank") {} + HighKingMaulgarKigglerEngagedByMoonkinTankTrigger( + PlayerbotAI* botAI) : Trigger(botAI, "high king maulgar kiggler engaged by moonkin tank") {} bool IsActive() override; }; class HighKingMaulgarDeterminingKillOrderTrigger : public Trigger { public: - HighKingMaulgarDeterminingKillOrderTrigger(PlayerbotAI* botAI) : Trigger(botAI, "high king maulgar determining kill order") {} - bool IsActive() override; -}; - -class HighKingMaulgarHealerInDangerTrigger : public Trigger -{ -public: - HighKingMaulgarHealerInDangerTrigger(PlayerbotAI* botAI) : Trigger(botAI, "high king maulgar healers in danger") {} + HighKingMaulgarDeterminingKillOrderTrigger( + PlayerbotAI* botAI) : Trigger(botAI, "high king maulgar determining kill order") {} bool IsActive() override; }; class HighKingMaulgarBossChannelingWhirlwindTrigger : public Trigger { public: - HighKingMaulgarBossChannelingWhirlwindTrigger(PlayerbotAI* botAI) : Trigger(botAI, "high king maulgar boss channeling whirlwind") {} + HighKingMaulgarBossChannelingWhirlwindTrigger( + PlayerbotAI* botAI) : Trigger(botAI, "high king maulgar boss channeling whirlwind") {} bool IsActive() override; }; -class HighKingMaulgarWildFelstalkerSpawnedTrigger : public Trigger +class HighKingMaulgarKroshCastsBlastWaveTrigger : public Trigger { public: - HighKingMaulgarWildFelstalkerSpawnedTrigger(PlayerbotAI* botAI) : Trigger(botAI, "high king maulgar wild felstalker spawned") {} + HighKingMaulgarKroshCastsBlastWaveTrigger( + PlayerbotAI* botAI) : Trigger(botAI, "high king maulgar krosh casts blast wave") {} bool IsActive() override; }; -class HighKingMaulgarPullingOlmAndBlindeyeTrigger : public Trigger +class HighKingMaulgarWildFelStalkerSpawnedTrigger : public Trigger { public: - HighKingMaulgarPullingOlmAndBlindeyeTrigger(PlayerbotAI* botAI) : Trigger(botAI, "high king maulgar pulling olm and blindeye") {} + HighKingMaulgarWildFelStalkerSpawnedTrigger( + PlayerbotAI* botAI) : Trigger(botAI, "high king maulgar wild fel stalker spawned") {} + bool IsActive() override; +}; + +class HighKingMaulgarPullingOgreCouncilTrigger : public Trigger +{ +public: + HighKingMaulgarPullingOgreCouncilTrigger( + PlayerbotAI* botAI) : Trigger(botAI, "high king maulgar pulling ogre council") {} bool IsActive() override; }; class GruulTheDragonkillerBossEngagedByTanksTrigger : public Trigger { public: - GruulTheDragonkillerBossEngagedByTanksTrigger(PlayerbotAI* botAI) : Trigger(botAI, "gruul the dragonkiller boss engaged by tanks") {} + GruulTheDragonkillerBossEngagedByTanksTrigger( + PlayerbotAI* botAI) : Trigger(botAI, "gruul the dragonkiller boss engaged by tanks") {} bool IsActive() override; }; class GruulTheDragonkillerBossEngagedByRangedTrigger : public Trigger { public: - GruulTheDragonkillerBossEngagedByRangedTrigger(PlayerbotAI* botAI) : Trigger(botAI, "gruul the dragonkiller boss engaged by ranged") {} + GruulTheDragonkillerBossEngagedByRangedTrigger( + PlayerbotAI* botAI) : Trigger(botAI, "gruul the dragonkiller boss engaged by ranged") {} bool IsActive() override; }; class GruulTheDragonkillerIncomingShatterTrigger : public Trigger { public: - GruulTheDragonkillerIncomingShatterTrigger(PlayerbotAI* botAI) : Trigger(botAI, "gruul the dragonkiller incoming shatter") {} + GruulTheDragonkillerIncomingShatterTrigger( + PlayerbotAI* botAI) : Trigger(botAI, "gruul the dragonkiller incoming shatter") {} bool IsActive() override; };