mirror of
https://github.com/mod-playerbots/mod-playerbots.git
synced 2026-02-07 12:41:09 +00:00
Refactor raid strategy framework (#2069)
# Pull Request The purposes of this PR are to (1) establish a general raid helper framework for the benefit of future raid strategies and (2) make some improvements to problematic areas of the raid strategy code. List of changes: 1. Added new RaidBossHelpers.cpp and RaidBossHelpers.h files in the Raid folder. 3. Moved reused helpers from Karazhan, Gruul, and Magtheridon strategies to the new helper files. 4. Modified the prior function that assigned a DPS bot to store and erase timers and trackers in associative containers--the function now includes parameters for mapId (so a bot that is not in the instance will not be assigned) and for the ability to exclude a bot (useful for excluding particular important roles, such as a Warlock tank, so they are not bogged down by these extra tasks at critical moments). I also renamed it from IsInstanceTimerManager to IsMechanicTrackerBot. 5. Moved all helper files in raid strategies to Util folders (was needed for ICC, MC, and Ulduar). 6. Renamed and reordered includes of Ulduar files in AiObjectContext.cpp to match other raid strategies. a. This initially caused compile errors which made me realize that the existing code had several problems with missing includes and was compiling only due to the prior ordering in AiObjectContext.cpp. Therefore, I added the missing includes to Molten Core, Ulduar, and Vault of Archavon strategies. b. Ulduar and Old Kingdom were also using the same constant name for a spell--the reordering caused a compile error here as well, which just highlighted an existing problem that was being hidden. I renamed the constant for Ulduar to fix this, but I think the better approach going forward would be to use a namespace or enum class. But that is for another time and probably another person. 7. Several changes with respect to Ulduar files: a. The position constants and enums for spells and NPCs and such were in the trigger header file. I did not think that made sense so moved them to existing helper files. b. Since the strategy does not use multipliers, I removed all files and references to multipliers in it. c. I removed some unneeded includes. I did not do a detailed review to determine what else could be removed--I just took some out that I could tell right away were not needed. d. I renamed the ingame strategy name from "uld" to "ulduar," which I think is clearer and is still plenty short. 8. Partial refactor of Gruul and Magtheridon strategies: a. I did not due a full refactoring but made some quick changes to things I did previously that were rather stupid like repeating calculations, having useless logic like pointless IsAlive() checks for creatures already on the hostile references list, and not using the existing Position class for coordinates. b. There were a few substantive changes, such as allowing players to pick Maulgar mage and moonkin tanks with the assistant flag, but a greater refactoring of the strategies themselves is beyond this PR. c. I was clearing some containers used for Gruul and Magtheridon strategies; the methods are now fixed to erase only the applicable keys so that in the unlikely event that one server has multiple groups running Gruul or Magtheridon at the same time, there won't be timer or position tracker conflicts. ## How to Test the Changes 1. Enter any raid instance that has any code impacted by this PR 2. Engage bosses and observe if any strategies are now broken I personally tested Maulgar, Gruul, and Magtheridon and confirmed that they still work as intended. ## Complexity & Impact I do not expect this PR to have any relevant changes to in-game performance, but I will defer to those more knowledgeable than I if there are concerns in this area. As I've mentioned before, you can consider me to be like a person who has taken half an intro C++ course at best. ## AI Assistance None beyond autocomplete of repetitive changes. --------- Co-authored-by: bashermens <31279994+hermensbas@users.noreply.github.com>
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
#include "RaidGruulsLairHelpers.h"
|
||||
#include "CreatureAI.h"
|
||||
#include "Playerbots.h"
|
||||
#include "RaidBossHelpers.h"
|
||||
#include "Unit.h"
|
||||
|
||||
using namespace GruulsLairHelpers;
|
||||
@@ -12,6 +13,8 @@ using namespace GruulsLairHelpers;
|
||||
bool HighKingMaulgarMainTankAttackMaulgarAction::Execute(Event event)
|
||||
{
|
||||
Unit* maulgar = AI_VALUE2(Unit*, "find target", "high king maulgar");
|
||||
if (!maulgar)
|
||||
return false;
|
||||
|
||||
MarkTargetWithSquare(bot, maulgar);
|
||||
SetRtiTarget(botAI, "square", maulgar);
|
||||
@@ -21,31 +24,20 @@ bool HighKingMaulgarMainTankAttackMaulgarAction::Execute(Event event)
|
||||
|
||||
if (maulgar->GetVictim() == bot)
|
||||
{
|
||||
const Location& tankPosition = GruulsLairLocations::MaulgarTankPosition;
|
||||
const Position& position = MAULGAR_TANK_POSITION;
|
||||
const float maxDistance = 3.0f;
|
||||
|
||||
float distanceToTankPosition = bot->GetExactDist2d(tankPosition.x, tankPosition.y);
|
||||
float distanceToPosition = bot->GetExactDist2d(position.GetPositionX(), position.GetPositionY());
|
||||
|
||||
if (distanceToTankPosition > maxDistance)
|
||||
if (distanceToPosition > maxDistance)
|
||||
{
|
||||
float dX = tankPosition.x - bot->GetPositionX();
|
||||
float dY = tankPosition.y - bot->GetPositionY();
|
||||
float dist = sqrt(dX * dX + dY * dY);
|
||||
float moveX = bot->GetPositionX() + (dX / dist) * maxDistance;
|
||||
float moveY = bot->GetPositionY() + (dY / dist) * maxDistance;
|
||||
return MoveTo(bot->GetMapId(), moveX, moveY, tankPosition.z, false, false, false, false,
|
||||
MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||
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);
|
||||
}
|
||||
|
||||
float orientation = atan2(maulgar->GetPositionY() - bot->GetPositionY(),
|
||||
maulgar->GetPositionX() - bot->GetPositionX());
|
||||
bot->SetFacingTo(orientation);
|
||||
}
|
||||
else if (!bot->IsWithinMeleeRange(maulgar))
|
||||
{
|
||||
return MoveTo(maulgar->GetMapId(), maulgar->GetPositionX(), maulgar->GetPositionY(),
|
||||
maulgar->GetPositionZ(), false, false, false, false,
|
||||
MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -55,6 +47,8 @@ bool HighKingMaulgarMainTankAttackMaulgarAction::Execute(Event event)
|
||||
bool HighKingMaulgarFirstAssistTankAttackOlmAction::Execute(Event event)
|
||||
{
|
||||
Unit* olm = AI_VALUE2(Unit*, "find target", "olm the summoner");
|
||||
if (!olm)
|
||||
return false;
|
||||
|
||||
MarkTargetWithCircle(bot, olm);
|
||||
SetRtiTarget(botAI, "circle", olm);
|
||||
@@ -64,29 +58,22 @@ bool HighKingMaulgarFirstAssistTankAttackOlmAction::Execute(Event event)
|
||||
|
||||
if (olm->GetVictim() == bot)
|
||||
{
|
||||
const Location& tankPosition = GruulsLairLocations::OlmTankPosition;
|
||||
const Position& position = OLM_TANK_POSITION;
|
||||
const float maxDistance = 3.0f;
|
||||
const float olmTankLeeway = 30.0f;
|
||||
|
||||
float distanceOlmToTankPosition = olm->GetExactDist2d(tankPosition.x, tankPosition.y);
|
||||
float distanceOlmToPosition = olm->GetExactDist2d(position.GetPositionX(), position.GetPositionY());
|
||||
|
||||
if (distanceOlmToTankPosition > olmTankLeeway)
|
||||
if (distanceOlmToPosition > olmTankLeeway)
|
||||
{
|
||||
float dX = tankPosition.x - bot->GetPositionX();
|
||||
float dY = tankPosition.y - bot->GetPositionY();
|
||||
float dist = sqrt(dX * dX + dY * dY);
|
||||
float moveX = bot->GetPositionX() + (dX / dist) * maxDistance;
|
||||
float moveY = bot->GetPositionY() + (dY / dist) * maxDistance;
|
||||
return MoveTo(bot->GetMapId(), moveX, moveY, tankPosition.z, false, false, false, false,
|
||||
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);
|
||||
}
|
||||
}
|
||||
else if (!bot->IsWithinMeleeRange(olm))
|
||||
{
|
||||
return MoveTo(olm->GetMapId(), olm->GetPositionX(), olm->GetPositionY(),
|
||||
olm->GetPositionZ(), false, false, false, false,
|
||||
MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -95,6 +82,8 @@ bool HighKingMaulgarFirstAssistTankAttackOlmAction::Execute(Event event)
|
||||
bool HighKingMaulgarSecondAssistTankAttackBlindeyeAction::Execute(Event event)
|
||||
{
|
||||
Unit* blindeye = AI_VALUE2(Unit*, "find target", "blindeye the seer");
|
||||
if (!blindeye)
|
||||
return false;
|
||||
|
||||
MarkTargetWithStar(bot, blindeye);
|
||||
SetRtiTarget(botAI, "star", blindeye);
|
||||
@@ -104,31 +93,20 @@ bool HighKingMaulgarSecondAssistTankAttackBlindeyeAction::Execute(Event event)
|
||||
|
||||
if (blindeye->GetVictim() == bot)
|
||||
{
|
||||
const Location& tankPosition = GruulsLairLocations::BlindeyeTankPosition;
|
||||
const Position& position = BLINDEYE_TANK_POSITION;
|
||||
const float maxDistance = 3.0f;
|
||||
|
||||
float distanceToTankPosition = bot->GetExactDist2d(tankPosition.x, tankPosition.y);
|
||||
float distanceToPosition = bot->GetExactDist2d(position.GetPositionX(), position.GetPositionY());
|
||||
|
||||
if (distanceToTankPosition > maxDistance)
|
||||
if (distanceToPosition > maxDistance)
|
||||
{
|
||||
float dX = tankPosition.x - bot->GetPositionX();
|
||||
float dY = tankPosition.y - bot->GetPositionY();
|
||||
float dist = sqrt(dX * dX + dY * dY);
|
||||
float moveX = bot->GetPositionX() + (dX / dist) * maxDistance;
|
||||
float moveY = bot->GetPositionY() + (dY / dist) * maxDistance;
|
||||
return MoveTo(bot->GetMapId(), moveX, moveY, tankPosition.z, false, false, false, false,
|
||||
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);
|
||||
}
|
||||
|
||||
float orientation = atan2(blindeye->GetPositionY() - bot->GetPositionY(),
|
||||
blindeye->GetPositionX() - bot->GetPositionX());
|
||||
bot->SetFacingTo(orientation);
|
||||
}
|
||||
else if (!bot->IsWithinMeleeRange(blindeye))
|
||||
{
|
||||
return MoveTo(blindeye->GetMapId(), blindeye->GetPositionX(), blindeye->GetPositionY(),
|
||||
blindeye->GetPositionZ(), false, false, false, false,
|
||||
MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -138,6 +116,8 @@ bool HighKingMaulgarSecondAssistTankAttackBlindeyeAction::Execute(Event event)
|
||||
bool HighKingMaulgarMageTankAttackKroshAction::Execute(Event event)
|
||||
{
|
||||
Unit* krosh = AI_VALUE2(Unit*, "find target", "krosh firehand");
|
||||
if (!krosh)
|
||||
return false;
|
||||
|
||||
MarkTargetWithTriangle(bot, krosh);
|
||||
SetRtiTarget(botAI, "triangle", krosh);
|
||||
@@ -149,25 +129,22 @@ bool HighKingMaulgarMageTankAttackKroshAction::Execute(Event event)
|
||||
return botAI->CastSpell("fire ward", bot);
|
||||
|
||||
if (bot->GetTarget() != krosh->GetGUID())
|
||||
{
|
||||
bot->SetSelection(krosh->GetGUID());
|
||||
return true;
|
||||
}
|
||||
return Attack(krosh);
|
||||
|
||||
if (krosh->GetVictim() == bot)
|
||||
{
|
||||
const Location& tankPosition = GruulsLairLocations::KroshTankPosition;
|
||||
float distanceToKrosh = krosh->GetExactDist2d(tankPosition.x, tankPosition.y);
|
||||
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;
|
||||
|
||||
if (distanceToKrosh > minDistance && distanceToKrosh < maxDistance)
|
||||
{
|
||||
if (!bot->IsWithinDist2d(tankPosition.x, tankPosition.y, tankPositionLeeway))
|
||||
if (!bot->IsWithinDist2d(position.GetPositionX(), position.GetPositionY(), tankPositionLeeway))
|
||||
{
|
||||
return MoveTo(bot->GetMapId(), tankPosition.x, tankPosition.y, tankPosition.z, false,
|
||||
false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||
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(),
|
||||
@@ -179,7 +156,7 @@ bool HighKingMaulgarMageTankAttackKroshAction::Execute(Event event)
|
||||
Position safePos;
|
||||
if (TryGetNewSafePosition(botAI, bot, safePos))
|
||||
{
|
||||
return MoveTo(krosh->GetMapId(), safePos.m_positionX, safePos.m_positionY, safePos.m_positionZ,
|
||||
return MoveTo(GRUULS_LAIR_MAP_ID, safePos.GetPositionX(), safePos.GetPositionY(), safePos.GetPositionZ(),
|
||||
false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||
}
|
||||
}
|
||||
@@ -192,20 +169,19 @@ bool HighKingMaulgarMageTankAttackKroshAction::Execute(Event event)
|
||||
bool HighKingMaulgarMoonkinTankAttackKigglerAction::Execute(Event event)
|
||||
{
|
||||
Unit* kiggler = AI_VALUE2(Unit*, "find target", "kiggler the crazed");
|
||||
if (!kiggler)
|
||||
return false;
|
||||
|
||||
MarkTargetWithDiamond(bot, kiggler);
|
||||
SetRtiTarget(botAI, "diamond", kiggler);
|
||||
|
||||
if (bot->GetTarget() != kiggler->GetGUID())
|
||||
{
|
||||
bot->SetSelection(kiggler->GetGUID());
|
||||
return true;
|
||||
}
|
||||
return Attack(kiggler);
|
||||
|
||||
Position safePos;
|
||||
if (TryGetNewSafePosition(botAI, bot, safePos))
|
||||
{
|
||||
return MoveTo(kiggler->GetMapId(), safePos.m_positionX, safePos.m_positionY, safePos.m_positionZ,
|
||||
return MoveTo(GRUULS_LAIR_MAP_ID, safePos.GetPositionX(), safePos.GetPositionY(), safePos.GetPositionZ(),
|
||||
false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||
}
|
||||
|
||||
@@ -216,120 +192,105 @@ bool HighKingMaulgarAssignDPSPriorityAction::Execute(Event event)
|
||||
{
|
||||
// Target priority 1: Blindeye
|
||||
Unit* blindeye = AI_VALUE2(Unit*, "find target", "blindeye the seer");
|
||||
if (blindeye && blindeye->IsAlive())
|
||||
if (blindeye)
|
||||
{
|
||||
Position safePos;
|
||||
if (TryGetNewSafePosition(botAI, bot, safePos))
|
||||
{
|
||||
bot->AttackStop();
|
||||
bot->InterruptNonMeleeSpells(false);
|
||||
return MoveTo(blindeye->GetMapId(), safePos.m_positionX, safePos.m_positionY, safePos.m_positionZ,
|
||||
return MoveTo(GRUULS_LAIR_MAP_ID, safePos.GetPositionX(), safePos.GetPositionY(), safePos.GetPositionZ(),
|
||||
false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||
}
|
||||
|
||||
SetRtiTarget(botAI, "star", blindeye);
|
||||
|
||||
if (bot->GetTarget() != blindeye->GetGUID())
|
||||
{
|
||||
bot->SetSelection(blindeye->GetGUID());
|
||||
return Attack(blindeye);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Target priority 2: Olm
|
||||
Unit* olm = AI_VALUE2(Unit*, "find target", "olm the summoner");
|
||||
if (olm && olm->IsAlive())
|
||||
if (olm)
|
||||
{
|
||||
Position safePos;
|
||||
if (TryGetNewSafePosition(botAI, bot, safePos))
|
||||
{
|
||||
bot->AttackStop();
|
||||
bot->InterruptNonMeleeSpells(false);
|
||||
return MoveTo(olm->GetMapId(), safePos.m_positionX, safePos.m_positionY, safePos.m_positionZ,
|
||||
return MoveTo(GRUULS_LAIR_MAP_ID, safePos.GetPositionX(), safePos.GetPositionY(), safePos.GetPositionZ(),
|
||||
false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||
}
|
||||
|
||||
SetRtiTarget(botAI, "circle", olm);
|
||||
|
||||
if (bot->GetTarget() != olm->GetGUID())
|
||||
{
|
||||
bot->SetSelection(olm->GetGUID());
|
||||
return Attack(olm);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Target priority 3a: Krosh (ranged only)
|
||||
Unit* krosh = AI_VALUE2(Unit*, "find target", "krosh firehand");
|
||||
if (krosh && krosh->IsAlive() && botAI->IsRanged(bot))
|
||||
if (krosh && botAI->IsRanged(bot))
|
||||
{
|
||||
Position safePos;
|
||||
if (TryGetNewSafePosition(botAI, bot, safePos))
|
||||
{
|
||||
bot->AttackStop();
|
||||
bot->InterruptNonMeleeSpells(false);
|
||||
return MoveTo(krosh->GetMapId(), safePos.m_positionX, safePos.m_positionY, safePos.m_positionZ,
|
||||
return MoveTo(GRUULS_LAIR_MAP_ID, safePos.GetPositionX(), safePos.GetPositionY(), safePos.GetPositionZ(),
|
||||
false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||
}
|
||||
|
||||
SetRtiTarget(botAI, "triangle", krosh);
|
||||
|
||||
if (bot->GetTarget() != krosh->GetGUID())
|
||||
{
|
||||
bot->SetSelection(krosh->GetGUID());
|
||||
return Attack(krosh);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Target priority 3b: Kiggler
|
||||
Unit* kiggler = AI_VALUE2(Unit*, "find target", "kiggler the crazed");
|
||||
if (kiggler && kiggler->IsAlive())
|
||||
if (kiggler)
|
||||
{
|
||||
Position safePos;
|
||||
if (TryGetNewSafePosition(botAI, bot, safePos))
|
||||
{
|
||||
bot->AttackStop();
|
||||
bot->InterruptNonMeleeSpells(false);
|
||||
return MoveTo(kiggler->GetMapId(), safePos.m_positionX, safePos.m_positionY, safePos.m_positionZ,
|
||||
return MoveTo(GRUULS_LAIR_MAP_ID, safePos.GetPositionX(), safePos.GetPositionY(), safePos.GetPositionZ(),
|
||||
false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||
}
|
||||
|
||||
SetRtiTarget(botAI, "diamond", kiggler);
|
||||
|
||||
if (bot->GetTarget() != kiggler->GetGUID())
|
||||
{
|
||||
bot->SetSelection(kiggler->GetGUID());
|
||||
return Attack(kiggler);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Target priority 4: Maulgar
|
||||
Unit* maulgar = AI_VALUE2(Unit*, "find target", "high king maulgar");
|
||||
if (maulgar && maulgar->IsAlive())
|
||||
if (maulgar)
|
||||
{
|
||||
Position safePos;
|
||||
if (TryGetNewSafePosition(botAI, bot, safePos))
|
||||
{
|
||||
bot->AttackStop();
|
||||
bot->InterruptNonMeleeSpells(false);
|
||||
return MoveTo(maulgar->GetMapId(), safePos.m_positionX, safePos.m_positionY, safePos.m_positionZ,
|
||||
return MoveTo(GRUULS_LAIR_MAP_ID, safePos.GetPositionX(), safePos.GetPositionY(), safePos.GetPositionZ(),
|
||||
false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||
}
|
||||
|
||||
SetRtiTarget(botAI, "square", maulgar);
|
||||
|
||||
if (bot->GetTarget() != maulgar->GetGUID())
|
||||
{
|
||||
bot->SetSelection(maulgar->GetGUID());
|
||||
return Attack(maulgar);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -338,22 +299,22 @@ bool HighKingMaulgarAssignDPSPriorityAction::Execute(Event event)
|
||||
// Avoid Whirlwind and Blast Wave and generally try to stay near the center of the room
|
||||
bool HighKingMaulgarHealerFindSafePositionAction::Execute(Event event)
|
||||
{
|
||||
const Location& fightCenter = GruulsLairLocations::MaulgarRoomCenter;
|
||||
const float maxDistanceFromFight = 50.0f;
|
||||
float distToFight = bot->GetExactDist2d(fightCenter.x, fightCenter.y);
|
||||
const Position& center = MAULGAR_ROOM_CENTER;
|
||||
const float maxDistanceFromCenter = 50.0f;
|
||||
float distToCenter = bot->GetExactDist2d(center.GetPositionX(), center.GetPositionY());
|
||||
|
||||
if (distToFight > maxDistanceFromFight)
|
||||
if (distToCenter > maxDistanceFromCenter)
|
||||
{
|
||||
float angle = atan2(bot->GetPositionY() - fightCenter.y, bot->GetPositionX() - fightCenter.x);
|
||||
float destX = fightCenter.x + 40.0f * cos(angle);
|
||||
float destY = fightCenter.y + 40.0f * sin(angle);
|
||||
float destZ = fightCenter.z;
|
||||
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(bot->GetMapId(), destX, destY, destZ, false, false, false, false,
|
||||
return MoveTo(GRUULS_LAIR_MAP_ID, destX, destY, destZ, false, false, false, false,
|
||||
MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||
}
|
||||
|
||||
@@ -362,7 +323,7 @@ bool HighKingMaulgarHealerFindSafePositionAction::Execute(Event event)
|
||||
{
|
||||
bot->AttackStop();
|
||||
bot->InterruptNonMeleeSpells(false);
|
||||
return MoveTo(bot->GetMapId(), safePos.m_positionX, safePos.m_positionY, safePos.m_positionZ,
|
||||
return MoveTo(GRUULS_LAIR_MAP_ID, safePos.GetPositionX(), safePos.GetPositionY(), safePos.GetPositionZ(),
|
||||
false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||
}
|
||||
|
||||
@@ -373,6 +334,8 @@ bool HighKingMaulgarHealerFindSafePositionAction::Execute(Event event)
|
||||
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);
|
||||
@@ -395,7 +358,7 @@ bool HighKingMaulgarRunAwayFromWhirlwindAction::Execute(Event event)
|
||||
{
|
||||
bot->AttackStop();
|
||||
bot->InterruptNonMeleeSpells(true);
|
||||
return MoveTo(maulgar->GetMapId(), destX, destY, destZ, false, false, false, false,
|
||||
return MoveTo(GRUULS_LAIR_MAP_ID, destX, destY, destZ, false, false, false, false,
|
||||
MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||
}
|
||||
}
|
||||
@@ -439,7 +402,7 @@ bool HighKingMaulgarBanishFelstalkerAction::Execute(Event event)
|
||||
if (warlockIndex >= 0 && warlockIndex < felStalkers.size())
|
||||
{
|
||||
Unit* assignedFelStalker = felStalkers[warlockIndex];
|
||||
if (!assignedFelStalker->HasAura(SPELL_BANISH) && botAI->CanCastSpell(SPELL_BANISH, assignedFelStalker, true))
|
||||
if (!botAI->HasAura("banish", assignedFelStalker) && botAI->CanCastSpell("banish", assignedFelStalker))
|
||||
return botAI->CastSpell("banish", assignedFelStalker);
|
||||
}
|
||||
|
||||
@@ -528,40 +491,33 @@ bool HighKingMaulgarMisdirectOlmAndBlindeyeAction::Execute(Event event)
|
||||
// Gruul the Dragonkiller Actions
|
||||
|
||||
// Position in center of the room
|
||||
bool GruulTheDragonkillerMainTankPositionBossAction::Execute(Event event)
|
||||
bool GruulTheDragonkillerTanksPositionBossAction::Execute(Event event)
|
||||
{
|
||||
Unit* gruul = AI_VALUE2(Unit*, "find target", "gruul the dragonkiller");
|
||||
if (!gruul)
|
||||
return false;
|
||||
|
||||
if (bot->GetVictim() != gruul)
|
||||
return Attack(gruul);
|
||||
|
||||
if (gruul->GetVictim() == bot)
|
||||
{
|
||||
const Location& tankPosition = GruulsLairLocations::GruulTankPosition;
|
||||
const float maxDistance = 3.0f;
|
||||
const Position& position = GRUUL_TANK_POSITION;
|
||||
const float maxDistance = 5.0f;
|
||||
|
||||
float dX = tankPosition.x - bot->GetPositionX();
|
||||
float dY = tankPosition.y - bot->GetPositionY();
|
||||
float distanceToTankPosition = bot->GetExactDist2d(tankPosition.x, tankPosition.y);
|
||||
float dX = position.GetPositionX() - bot->GetPositionX();
|
||||
float dY = position.GetPositionY() - bot->GetPositionY();
|
||||
float distanceToTankPosition = bot->GetExactDist2d(position.GetPositionX(), position.GetPositionY());
|
||||
|
||||
if (distanceToTankPosition > maxDistance)
|
||||
{
|
||||
float step = std::min(maxDistance, distanceToTankPosition);
|
||||
float moveX = bot->GetPositionX() + (dX / distanceToTankPosition) * maxDistance;
|
||||
float moveY = bot->GetPositionY() + (dY / distanceToTankPosition) * maxDistance;
|
||||
const float moveZ = tankPosition.z;
|
||||
return MoveTo(bot->GetMapId(), moveX, moveY, moveZ, false, false, false, false,
|
||||
const float moveZ = position.GetPositionZ();
|
||||
return MoveTo(GRUULS_LAIR_MAP_ID, moveX, moveY, moveZ, false, false, false, false,
|
||||
MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||
}
|
||||
|
||||
float orientation = atan2(gruul->GetPositionY() - bot->GetPositionY(),
|
||||
gruul->GetPositionX() - bot->GetPositionX());
|
||||
bot->SetFacingTo(orientation);
|
||||
}
|
||||
else if (!bot->IsWithinMeleeRange(gruul))
|
||||
{
|
||||
return MoveTo(gruul->GetMapId(), gruul->GetPositionX(), gruul->GetPositionY(), gruul->GetPositionZ(),
|
||||
false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -579,16 +535,16 @@ bool GruulTheDragonkillerSpreadRangedAction::Execute(Event event)
|
||||
static std::unordered_map<ObjectGuid, bool> hasReachedInitialPosition;
|
||||
|
||||
Unit* gruul = AI_VALUE2(Unit*, "find target", "gruul the dragonkiller");
|
||||
if (gruul && gruul->IsAlive() && gruul->GetHealth() == gruul->GetMaxHealth())
|
||||
if (gruul && gruul->GetHealth() == gruul->GetMaxHealth())
|
||||
{
|
||||
initialPositions.clear();
|
||||
hasReachedInitialPosition.clear();
|
||||
initialPositions.erase(bot->GetGUID());
|
||||
hasReachedInitialPosition.erase(bot->GetGUID());
|
||||
}
|
||||
|
||||
const Location& tankPosition = GruulsLairLocations::GruulTankPosition;
|
||||
const float centerX = tankPosition.x;
|
||||
const float centerY = tankPosition.y;
|
||||
float centerZ = bot->GetPositionZ();
|
||||
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;
|
||||
|
||||
@@ -642,7 +598,7 @@ bool GruulTheDragonkillerSpreadRangedAction::Execute(Event event)
|
||||
bot->GetPositionY(), bot->GetPositionZ(), destX, destY, destZ))
|
||||
return false;
|
||||
|
||||
return MoveTo(bot->GetMapId(), destX, destY, destZ, false, false, false, false,
|
||||
return MoveTo(GRUULS_LAIR_MAP_ID, destX, destY, destZ, false, false, false, false,
|
||||
MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||
}
|
||||
|
||||
|
||||
@@ -85,10 +85,10 @@ public:
|
||||
bool Execute(Event event) override;
|
||||
};
|
||||
|
||||
class GruulTheDragonkillerMainTankPositionBossAction : public AttackAction
|
||||
class GruulTheDragonkillerTanksPositionBossAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
GruulTheDragonkillerMainTankPositionBossAction(PlayerbotAI* botAI, std::string const name = "gruul the dragonkiller main tank 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;
|
||||
};
|
||||
|
||||
@@ -8,18 +8,11 @@
|
||||
#include "HunterActions.h"
|
||||
#include "MageActions.h"
|
||||
#include "Playerbots.h"
|
||||
#include "ReachTargetActions.h"
|
||||
#include "WarriorActions.h"
|
||||
|
||||
using namespace GruulsLairHelpers;
|
||||
|
||||
static bool IsChargeAction(Action* action)
|
||||
{
|
||||
return dynamic_cast<CastChargeAction*>(action) ||
|
||||
dynamic_cast<CastInterceptAction*>(action) ||
|
||||
dynamic_cast<CastFeralChargeBearAction*>(action) ||
|
||||
dynamic_cast<CastFeralChargeCatAction*>(action);
|
||||
}
|
||||
|
||||
float HighKingMaulgarDisableTankAssistMultiplier::GetValue(Action* action)
|
||||
{
|
||||
if (IsAnyOgreBossAlive(botAI) && dynamic_cast<TankAssistAction*>(action))
|
||||
@@ -38,12 +31,10 @@ float HighKingMaulgarAvoidWhirlwindMultiplier::GetValue(Action* action)
|
||||
Unit* blindeye = AI_VALUE2(Unit*, "find target", "blindeye the seer");
|
||||
|
||||
if (maulgar && maulgar->HasAura(SPELL_WHIRLWIND) &&
|
||||
(!kiggler || !kiggler->IsAlive()) &&
|
||||
(!krosh || !krosh->IsAlive()) &&
|
||||
(!olm || !olm->IsAlive()) &&
|
||||
(!blindeye || !blindeye->IsAlive()))
|
||||
!kiggler && !krosh && !olm && !blindeye)
|
||||
{
|
||||
if (IsChargeAction(action) || (dynamic_cast<MovementAction*>(action) &&
|
||||
if (dynamic_cast<CastReachTargetSpellAction*>(action) ||
|
||||
(dynamic_cast<MovementAction*>(action) &&
|
||||
!dynamic_cast<HighKingMaulgarRunAwayFromWhirlwindAction*>(action)))
|
||||
return 0.0f;
|
||||
}
|
||||
@@ -57,7 +48,8 @@ float HighKingMaulgarDisableArcaneShotOnKroshMultiplier::GetValue(Action* action
|
||||
Unit* krosh = AI_VALUE2(Unit*, "find target", "krosh firehand");
|
||||
Unit* target = AI_VALUE(Unit*, "current target");
|
||||
|
||||
if (krosh && target && target->GetGUID() == krosh->GetGUID() && dynamic_cast<CastArcaneShotAction*>(action))
|
||||
if (krosh && target && target->GetGUID() == krosh->GetGUID() &&
|
||||
dynamic_cast<CastArcaneShotAction*>(action))
|
||||
return 0.0f;
|
||||
|
||||
return 1.0f;
|
||||
@@ -101,8 +93,9 @@ float GruulTheDragonkillerGroundSlamMultiplier::GetValue(Action* action)
|
||||
if (bot->HasAura(SPELL_GROUND_SLAM_1) ||
|
||||
bot->HasAura(SPELL_GROUND_SLAM_2))
|
||||
{
|
||||
if ((dynamic_cast<MovementAction*>(action) && !dynamic_cast<GruulTheDragonkillerShatterSpreadAction*>(action)) ||
|
||||
IsChargeAction(action))
|
||||
if ((dynamic_cast<MovementAction*>(action) &&
|
||||
!dynamic_cast<GruulTheDragonkillerShatterSpreadAction*>(action)) ||
|
||||
dynamic_cast<CastReachTargetSpellAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ public:
|
||||
creators["high king maulgar misdirect olm and blindeye"] = &RaidGruulsLairActionContext::high_king_maulgar_misdirect_olm_and_blindeye;
|
||||
|
||||
// Gruul the Dragonkiller
|
||||
creators["gruul the dragonkiller main tank position boss"] = &RaidGruulsLairActionContext::gruul_the_dragonkiller_main_tank_position_boss;
|
||||
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;
|
||||
}
|
||||
@@ -41,7 +41,7 @@ private:
|
||||
static Action* high_king_maulgar_misdirect_olm_and_blindeye(PlayerbotAI* botAI) { return new HighKingMaulgarMisdirectOlmAndBlindeyeAction(botAI); }
|
||||
|
||||
// Gruul the Dragonkiller
|
||||
static Action* gruul_the_dragonkiller_main_tank_position_boss(PlayerbotAI* botAI) { return new GruulTheDragonkillerMainTankPositionBossAction(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); }
|
||||
};
|
||||
|
||||
@@ -22,8 +22,8 @@ public:
|
||||
creators["high king maulgar pulling olm and blindeye"] = &RaidGruulsLairTriggerContext::high_king_maulgar_pulling_olm_and_blindeye;
|
||||
|
||||
// Gruul the Dragonkiller
|
||||
creators["gruul the dragonkiller boss engaged by main tank"] = &RaidGruulsLairTriggerContext::gruul_the_dragonkiller_boss_engaged_by_main_tank;
|
||||
creators["gruul the dragonkiller boss engaged by range"] = &RaidGruulsLairTriggerContext::gruul_the_dragonkiller_boss_engaged_by_range;
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -41,8 +41,8 @@ private:
|
||||
static Trigger* high_king_maulgar_pulling_olm_and_blindeye(PlayerbotAI* botAI) { return new HighKingMaulgarPullingOlmAndBlindeyeTrigger(botAI); }
|
||||
|
||||
// Gruul the Dragonkiller
|
||||
static Trigger* gruul_the_dragonkiller_boss_engaged_by_main_tank(PlayerbotAI* botAI) { return new GruulTheDragonkillerBossEngagedByMainTankTrigger(botAI); }
|
||||
static Trigger* gruul_the_dragonkiller_boss_engaged_by_range(PlayerbotAI* botAI) { return new GruulTheDragonkillerBossEngagedByRangeTrigger(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); }
|
||||
};
|
||||
|
||||
|
||||
@@ -35,10 +35,10 @@ void RaidGruulsLairStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
NextAction("high king maulgar misdirect olm and blindeye", ACTION_RAID + 2) }));
|
||||
|
||||
// Gruul the Dragonkiller
|
||||
triggers.push_back(new TriggerNode("gruul the dragonkiller boss engaged by main tank", {
|
||||
NextAction("gruul the dragonkiller main tank position boss", ACTION_RAID + 1) }));
|
||||
triggers.push_back(new TriggerNode("gruul the dragonkiller boss engaged by tanks", {
|
||||
NextAction("gruul the dragonkiller tanks position boss", ACTION_RAID + 1) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("gruul the dragonkiller boss engaged by range", {
|
||||
triggers.push_back(new TriggerNode("gruul the dragonkiller boss engaged by ranged", {
|
||||
NextAction("gruul the dragonkiller spread ranged", ACTION_RAID + 1) }));
|
||||
|
||||
triggers.push_back(new TriggerNode("gruul the dragonkiller incoming shatter", {
|
||||
|
||||
@@ -10,35 +10,35 @@ bool HighKingMaulgarIsMainTankTrigger::IsActive()
|
||||
{
|
||||
Unit* maulgar = AI_VALUE2(Unit*, "find target", "high king maulgar");
|
||||
|
||||
return botAI->IsMainTank(bot) && maulgar && maulgar->IsAlive();
|
||||
return botAI->IsMainTank(bot) && maulgar;
|
||||
}
|
||||
|
||||
bool HighKingMaulgarIsFirstAssistTankTrigger::IsActive()
|
||||
{
|
||||
Unit* olm = AI_VALUE2(Unit*, "find target", "olm the summoner");
|
||||
|
||||
return botAI->IsAssistTankOfIndex(bot, 0) && olm && olm->IsAlive();
|
||||
return botAI->IsAssistTankOfIndex(bot, 0, false) && olm;
|
||||
}
|
||||
|
||||
bool HighKingMaulgarIsSecondAssistTankTrigger::IsActive()
|
||||
{
|
||||
Unit* blindeye = AI_VALUE2(Unit*, "find target", "blindeye the seer");
|
||||
|
||||
return botAI->IsAssistTankOfIndex(bot, 1) && blindeye && blindeye->IsAlive();
|
||||
return botAI->IsAssistTankOfIndex(bot, 1, false) && blindeye;
|
||||
}
|
||||
|
||||
bool HighKingMaulgarIsMageTankTrigger::IsActive()
|
||||
{
|
||||
Unit* krosh = AI_VALUE2(Unit*, "find target", "krosh firehand");
|
||||
|
||||
return IsKroshMageTank(botAI, bot) && krosh && krosh->IsAlive();
|
||||
return IsKroshMageTank(botAI, bot) && krosh;
|
||||
}
|
||||
|
||||
bool HighKingMaulgarIsMoonkinTankTrigger::IsActive()
|
||||
{
|
||||
Unit* kiggler = AI_VALUE2(Unit*, "find target", "kiggler the crazed");
|
||||
|
||||
return IsKigglerMoonkinTank(botAI, bot) && kiggler && kiggler->IsAlive();
|
||||
return IsKigglerMoonkinTank(botAI, bot) && kiggler;
|
||||
}
|
||||
|
||||
bool HighKingMaulgarDeterminingKillOrderTrigger::IsActive()
|
||||
@@ -50,11 +50,11 @@ bool HighKingMaulgarDeterminingKillOrderTrigger::IsActive()
|
||||
Unit* krosh = AI_VALUE2(Unit*, "find target", "krosh firehand");
|
||||
|
||||
return (botAI->IsDps(bot) || botAI->IsTank(bot)) &&
|
||||
!(botAI->IsMainTank(bot) && maulgar && maulgar->IsAlive()) &&
|
||||
!(botAI->IsAssistTankOfIndex(bot, 0) && olm && olm->IsAlive()) &&
|
||||
!(botAI->IsAssistTankOfIndex(bot, 1) && blindeye && blindeye->IsAlive()) &&
|
||||
!(IsKroshMageTank(botAI, bot) && krosh && krosh->IsAlive()) &&
|
||||
!(IsKigglerMoonkinTank(botAI, bot) && kiggler && kiggler->IsAlive());
|
||||
!(botAI->IsMainTank(bot) && maulgar) &&
|
||||
!(botAI->IsAssistTankOfIndex(bot, 0, false) && olm) &&
|
||||
!(botAI->IsAssistTankOfIndex(bot, 1, false) && blindeye) &&
|
||||
!(IsKroshMageTank(botAI, bot) && krosh) &&
|
||||
!(IsKigglerMoonkinTank(botAI, bot) && kiggler);
|
||||
}
|
||||
|
||||
bool HighKingMaulgarHealerInDangerTrigger::IsActive()
|
||||
@@ -66,7 +66,7 @@ bool HighKingMaulgarBossChannelingWhirlwindTrigger::IsActive()
|
||||
{
|
||||
Unit* maulgar = AI_VALUE2(Unit*, "find target", "high king maulgar");
|
||||
|
||||
return maulgar && maulgar->IsAlive() && maulgar->HasAura(SPELL_WHIRLWIND) &&
|
||||
return maulgar && maulgar->HasAura(SPELL_WHIRLWIND) &&
|
||||
!botAI->IsMainTank(bot);
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ bool HighKingMaulgarWildFelstalkerSpawnedTrigger::IsActive()
|
||||
{
|
||||
Unit* felStalker = AI_VALUE2(Unit*, "find target", "wild fel stalker");
|
||||
|
||||
return felStalker && felStalker->IsAlive() && bot->getClass() == CLASS_WARLOCK;
|
||||
return felStalker && bot->getClass() == CLASS_WARLOCK;
|
||||
}
|
||||
|
||||
bool HighKingMaulgarPullingOlmAndBlindeyeTrigger::IsActive()
|
||||
@@ -120,12 +120,12 @@ bool HighKingMaulgarPullingOlmAndBlindeyeTrigger::IsActive()
|
||||
switch (hunterIndex)
|
||||
{
|
||||
case 0:
|
||||
return olm && olm->IsAlive() && olm->GetHealthPct() > 98.0f &&
|
||||
olmTank && olmTank->IsAlive() && botAI->CanCastSpell("misdirection", olmTank);
|
||||
return olm && olm->GetHealthPct() > 98.0f &&
|
||||
olmTank && botAI->CanCastSpell("misdirection", olmTank);
|
||||
|
||||
case 1:
|
||||
return blindeye && blindeye->IsAlive() && blindeye->GetHealthPct() > 90.0f &&
|
||||
blindeyeTank && blindeyeTank->IsAlive() && botAI->CanCastSpell("misdirection", blindeyeTank);
|
||||
return blindeye && blindeye->GetHealthPct() > 90.0f &&
|
||||
blindeyeTank && botAI->CanCastSpell("misdirection", blindeyeTank);
|
||||
|
||||
default:
|
||||
break;
|
||||
@@ -136,25 +136,24 @@ bool HighKingMaulgarPullingOlmAndBlindeyeTrigger::IsActive()
|
||||
|
||||
// Gruul the Dragonkiller Triggers
|
||||
|
||||
bool GruulTheDragonkillerBossEngagedByMainTankTrigger::IsActive()
|
||||
bool GruulTheDragonkillerBossEngagedByTanksTrigger::IsActive()
|
||||
{
|
||||
Unit* gruul = AI_VALUE2(Unit*, "find target", "gruul the dragonkiller");
|
||||
|
||||
return gruul && gruul->IsAlive() && botAI->IsMainTank(bot);
|
||||
return gruul && botAI->IsTank(bot);
|
||||
}
|
||||
|
||||
bool GruulTheDragonkillerBossEngagedByRangeTrigger::IsActive()
|
||||
bool GruulTheDragonkillerBossEngagedByRangedTrigger::IsActive()
|
||||
{
|
||||
Unit* gruul = AI_VALUE2(Unit*, "find target", "gruul the dragonkiller");
|
||||
|
||||
return gruul && gruul->IsAlive() && botAI->IsRanged(bot);
|
||||
return gruul && botAI->IsRanged(bot);
|
||||
}
|
||||
|
||||
bool GruulTheDragonkillerIncomingShatterTrigger::IsActive()
|
||||
{
|
||||
Unit* gruul = AI_VALUE2(Unit*, "find target", "gruul the dragonkiller");
|
||||
|
||||
return gruul && gruul->IsAlive() &&
|
||||
(bot->HasAura(SPELL_GROUND_SLAM_1) ||
|
||||
bot->HasAura(SPELL_GROUND_SLAM_2));
|
||||
return gruul && (bot->HasAura(SPELL_GROUND_SLAM_1) ||
|
||||
bot->HasAura(SPELL_GROUND_SLAM_2));
|
||||
}
|
||||
|
||||
@@ -73,17 +73,17 @@ public:
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class GruulTheDragonkillerBossEngagedByMainTankTrigger : public Trigger
|
||||
class GruulTheDragonkillerBossEngagedByTanksTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
GruulTheDragonkillerBossEngagedByMainTankTrigger(PlayerbotAI* botAI) : Trigger(botAI, "gruul the dragonkiller boss engaged by main tank") {}
|
||||
GruulTheDragonkillerBossEngagedByTanksTrigger(PlayerbotAI* botAI) : Trigger(botAI, "gruul the dragonkiller boss engaged by tanks") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class GruulTheDragonkillerBossEngagedByRangeTrigger : public Trigger
|
||||
class GruulTheDragonkillerBossEngagedByRangedTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
GruulTheDragonkillerBossEngagedByRangeTrigger(PlayerbotAI* botAI) : Trigger(botAI, "gruul the dragonkiller boss engaged by range") {}
|
||||
GruulTheDragonkillerBossEngagedByRangedTrigger(PlayerbotAI* botAI) : Trigger(botAI, "gruul the dragonkiller boss engaged by ranged") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
|
||||
@@ -6,19 +6,16 @@
|
||||
|
||||
namespace GruulsLairHelpers
|
||||
{
|
||||
namespace GruulsLairLocations
|
||||
{
|
||||
// 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 Location MaulgarTankPosition = { 90.686f, 167.047f, -13.234f };
|
||||
const Location OlmTankPosition = { 87.485f, 234.942f, -3.635f };
|
||||
const Location BlindeyeTankPosition = { 99.681f, 213.989f, -10.345f };
|
||||
const Location KroshTankPosition = { 116.880f, 166.208f, -14.231f };
|
||||
const Location MaulgarRoomCenter = { 88.754f, 150.759f, -11.569f };
|
||||
const Location GruulTankPosition = { 241.238f, 365.025f, -4.220f };
|
||||
}
|
||||
// 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)
|
||||
{
|
||||
@@ -42,84 +39,43 @@ namespace GruulsLairHelpers
|
||||
return false;
|
||||
}
|
||||
|
||||
void MarkTargetWithIcon(Player* bot, Unit* target, uint8 iconId)
|
||||
{
|
||||
Group* group = bot->GetGroup();
|
||||
if (!target || !group)
|
||||
return;
|
||||
|
||||
ObjectGuid currentGuid = group->GetTargetIcon(iconId);
|
||||
if (currentGuid != target->GetGUID())
|
||||
{
|
||||
group->SetTargetIcon(iconId, bot->GetGUID(), target->GetGUID());
|
||||
}
|
||||
}
|
||||
|
||||
void MarkTargetWithSquare(Player* bot, Unit* target)
|
||||
{
|
||||
MarkTargetWithIcon(bot, target, RtiTargetValue::squareIndex);
|
||||
}
|
||||
|
||||
void MarkTargetWithStar(Player* bot, Unit* target)
|
||||
{
|
||||
MarkTargetWithIcon(bot, target, RtiTargetValue::starIndex);
|
||||
}
|
||||
|
||||
void MarkTargetWithCircle(Player* bot, Unit* target)
|
||||
{
|
||||
MarkTargetWithIcon(bot, target, RtiTargetValue::circleIndex);
|
||||
}
|
||||
|
||||
void MarkTargetWithDiamond(Player* bot, Unit* target)
|
||||
{
|
||||
MarkTargetWithIcon(bot, target, RtiTargetValue::diamondIndex);
|
||||
}
|
||||
|
||||
void MarkTargetWithTriangle(Player* bot, Unit* target)
|
||||
{
|
||||
MarkTargetWithIcon(bot, target, RtiTargetValue::triangleIndex);
|
||||
}
|
||||
|
||||
void SetRtiTarget(PlayerbotAI* botAI, const std::string& rtiName, Unit* target)
|
||||
{
|
||||
if (!target)
|
||||
return;
|
||||
|
||||
std::string currentRti = botAI->GetAiObjectContext()->GetValue<std::string>("rti")->Get();
|
||||
Unit* currentTarget = botAI->GetAiObjectContext()->GetValue<Unit*>("rti target")->Get();
|
||||
|
||||
if (currentRti != rtiName || currentTarget != target)
|
||||
{
|
||||
botAI->GetAiObjectContext()->GetValue<std::string>("rti")->Set(rtiName);
|
||||
botAI->GetAiObjectContext()->GetValue<Unit*>("rti target")->Set(target);
|
||||
}
|
||||
}
|
||||
|
||||
bool IsKroshMageTank(PlayerbotAI* botAI, Player* bot)
|
||||
{
|
||||
Group* group = bot->GetGroup();
|
||||
if (!group)
|
||||
return false;
|
||||
|
||||
Player* highestHpMage = nullptr;
|
||||
uint32 highestHp = 0;
|
||||
// (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))
|
||||
if (!member || !member->IsAlive() || member->getClass() != CLASS_MAGE)
|
||||
continue;
|
||||
|
||||
if (member->getClass() == CLASS_MAGE)
|
||||
if (group->IsAssistant(member->GetGUID()))
|
||||
return member == 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())
|
||||
{
|
||||
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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -129,30 +85,37 @@ namespace GruulsLairHelpers
|
||||
if (!group)
|
||||
return false;
|
||||
|
||||
Player* highestHpMoonkin = nullptr;
|
||||
uint32 highestHp = 0;
|
||||
|
||||
// (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() || !GET_PLAYERBOT_AI(member))
|
||||
if (!member || !member->IsAlive() || member->getClass() != CLASS_DRUID)
|
||||
continue;
|
||||
|
||||
if (member->getClass() == CLASS_DRUID)
|
||||
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)
|
||||
{
|
||||
int tab = AiFactory::GetPlayerSpecTab(member);
|
||||
if (tab == DRUID_TAB_BALANCE)
|
||||
{
|
||||
uint32 hp = member->GetMaxHealth();
|
||||
if (!highestHpMoonkin || hp > highestHp)
|
||||
{
|
||||
highestHpMoonkin = member;
|
||||
highestHp = hp;
|
||||
}
|
||||
}
|
||||
highestHpMoonkin = member;
|
||||
highestHp = hp;
|
||||
}
|
||||
}
|
||||
|
||||
// (3) Return the found Moonkin tank, or nullptr if none found
|
||||
return highestHpMoonkin == bot;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,23 +2,19 @@
|
||||
#define RAID_GRUULSLAIRHELPERS_H
|
||||
|
||||
#include "PlayerbotAI.h"
|
||||
#include "RtiTargetValue.h"
|
||||
|
||||
namespace GruulsLairHelpers
|
||||
{
|
||||
enum GruulsLairSpells
|
||||
{
|
||||
// High King Maulgar
|
||||
SPELL_WHIRLWIND = 33238,
|
||||
SPELL_WHIRLWIND = 33238,
|
||||
|
||||
// Krosh Firehand
|
||||
SPELL_SPELL_SHIELD = 33054,
|
||||
SPELL_SPELL_SHIELD = 33054,
|
||||
|
||||
// Hunter
|
||||
SPELL_MISDIRECTION = 35079,
|
||||
|
||||
// Warlock
|
||||
SPELL_BANISH = 18647, // Rank 2
|
||||
SPELL_MISDIRECTION = 35079,
|
||||
|
||||
// Gruul the Dragonkiller
|
||||
SPELL_GROUND_SLAM_1 = 33525,
|
||||
@@ -30,33 +26,20 @@ namespace GruulsLairHelpers
|
||||
NPC_WILD_FEL_STALKER = 18847,
|
||||
};
|
||||
|
||||
constexpr uint32 GRUULS_LAIR_MAP_ID = 565;
|
||||
|
||||
bool IsAnyOgreBossAlive(PlayerbotAI* botAI);
|
||||
void MarkTargetWithIcon(Player* bot, Unit* target, uint8 iconId);
|
||||
void MarkTargetWithSquare(Player* bot, Unit* target);
|
||||
void MarkTargetWithStar(Player* bot, Unit* target);
|
||||
void MarkTargetWithCircle(Player* bot, Unit* target);
|
||||
void MarkTargetWithDiamond(Player* bot, Unit* target);
|
||||
void MarkTargetWithTriangle(Player* bot, Unit* target);
|
||||
void SetRtiTarget(PlayerbotAI* botAI, const std::string& rtiName, Unit* target);
|
||||
bool IsKroshMageTank(PlayerbotAI* botAI, Player* bot);
|
||||
bool IsKigglerMoonkinTank(PlayerbotAI* botAI, Player* bot);
|
||||
bool IsPositionSafe(PlayerbotAI* botAI, Player* bot, Position pos);
|
||||
bool TryGetNewSafePosition(PlayerbotAI* botAI, Player* bot, Position& outPos);
|
||||
|
||||
struct Location
|
||||
{
|
||||
float x, y, z;
|
||||
};
|
||||
|
||||
namespace GruulsLairLocations
|
||||
{
|
||||
extern const Location MaulgarTankPosition;
|
||||
extern const Location OlmTankPosition;
|
||||
extern const Location BlindeyeTankPosition;
|
||||
extern const Location KroshTankPosition;
|
||||
extern const Location MaulgarRoomCenter;
|
||||
extern const Location GruulTankPosition;
|
||||
}
|
||||
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
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include "RaidKarazhanHelpers.h"
|
||||
#include "Playerbots.h"
|
||||
#include "PlayerbotTextMgr.h"
|
||||
#include "RaidBossHelpers.h"
|
||||
|
||||
using namespace KarazhanHelpers;
|
||||
|
||||
@@ -44,7 +45,7 @@ bool AttumenTheHuntsmanMarkTargetAction::Execute(Event event)
|
||||
Unit* attumenMounted = GetFirstAliveUnitByEntry(botAI, NPC_ATTUMEN_THE_HUNTSMAN_MOUNTED);
|
||||
if (attumenMounted)
|
||||
{
|
||||
if (IsInstanceTimerManager(botAI, bot))
|
||||
if (IsMechanicTrackerBot(botAI, bot, KARAZHAN_MAP_ID, nullptr))
|
||||
MarkTargetWithStar(bot, attumenMounted);
|
||||
|
||||
SetRtiTarget(botAI, "star", attumenMounted);
|
||||
@@ -57,7 +58,7 @@ bool AttumenTheHuntsmanMarkTargetAction::Execute(Event event)
|
||||
}
|
||||
else if (Unit* midnight = AI_VALUE2(Unit*, "find target", "midnight"))
|
||||
{
|
||||
if (IsInstanceTimerManager(botAI, bot))
|
||||
if (IsMechanicTrackerBot(botAI, bot, KARAZHAN_MAP_ID, nullptr))
|
||||
MarkTargetWithStar(bot, midnight);
|
||||
|
||||
if (!botAI->IsAssistTankOfIndex(bot, 0))
|
||||
@@ -180,7 +181,7 @@ bool MoroesMarkTargetAction::Execute(Event event)
|
||||
|
||||
if (target)
|
||||
{
|
||||
if (IsInstanceTimerManager(botAI, bot))
|
||||
if (IsMechanicTrackerBot(botAI, bot, KARAZHAN_MAP_ID, nullptr))
|
||||
MarkTargetWithSkull(bot, target);
|
||||
|
||||
SetRtiTarget(botAI, "skull", target);
|
||||
@@ -405,7 +406,7 @@ bool TheCuratorMarkAstralFlareAction::Execute(Event event)
|
||||
if (!flare)
|
||||
return false;
|
||||
|
||||
if (IsInstanceTimerManager(botAI, bot))
|
||||
if (IsMechanicTrackerBot(botAI, bot, KARAZHAN_MAP_ID, nullptr))
|
||||
MarkTargetWithSkull(bot, flare);
|
||||
|
||||
SetRtiTarget(botAI, "skull", flare);
|
||||
@@ -469,11 +470,11 @@ bool TheCuratorSpreadRangedAction::Execute(Event event)
|
||||
// Prioritize (1) Demon Chains, (2) Kil'rek, (3) Illhoof
|
||||
bool TerestianIllhoofMarkTargetAction::Execute(Event event)
|
||||
{
|
||||
Unit* demonChains = AI_VALUE2(Unit*, "find target", "demon chains");
|
||||
Unit* kilrek = AI_VALUE2(Unit*, "find target", "kil'rek");
|
||||
Unit* demonChains = GetFirstAliveUnitByEntry(botAI, NPC_DEMON_CHAINS);
|
||||
Unit* kilrek = GetFirstAliveUnitByEntry(botAI, NPC_KILREK);
|
||||
Unit* illhoof = AI_VALUE2(Unit*, "find target", "terestian illhoof");
|
||||
Unit* target = GetFirstAliveUnit({demonChains, kilrek, illhoof});
|
||||
|
||||
Unit* target = GetFirstAliveUnit({demonChains, kilrek, illhoof});
|
||||
if (target)
|
||||
MarkTargetWithSkull(bot, target);
|
||||
|
||||
@@ -1007,7 +1008,7 @@ bool NetherspiteManageTimersAndTrackersAction::Execute(Event event)
|
||||
if (netherspite->GetHealth() == netherspite->GetMaxHealth() &&
|
||||
!netherspite->HasAura(SPELL_GREEN_BEAM_HEAL))
|
||||
{
|
||||
if (IsInstanceTimerManager(botAI, bot))
|
||||
if (IsMechanicTrackerBot(botAI, bot, KARAZHAN_MAP_ID, nullptr))
|
||||
netherspiteDpsWaitTimer.insert_or_assign(instanceId, now);
|
||||
|
||||
if (botAI->IsTank(bot) && !bot->HasAura(SPELL_RED_BEAM_DEBUFF))
|
||||
@@ -1018,7 +1019,7 @@ bool NetherspiteManageTimersAndTrackersAction::Execute(Event event)
|
||||
}
|
||||
else if (netherspite->HasAura(SPELL_NETHERSPITE_BANISHED))
|
||||
{
|
||||
if (IsInstanceTimerManager(botAI, bot))
|
||||
if (IsMechanicTrackerBot(botAI, bot, KARAZHAN_MAP_ID, nullptr))
|
||||
netherspiteDpsWaitTimer.erase(instanceId);
|
||||
|
||||
if (botAI->IsTank(bot))
|
||||
@@ -1029,7 +1030,7 @@ bool NetherspiteManageTimersAndTrackersAction::Execute(Event event)
|
||||
}
|
||||
else if (!netherspite->HasAura(SPELL_NETHERSPITE_BANISHED))
|
||||
{
|
||||
if (IsInstanceTimerManager(botAI, bot))
|
||||
if (IsMechanicTrackerBot(botAI, bot, KARAZHAN_MAP_ID, nullptr))
|
||||
netherspiteDpsWaitTimer.try_emplace(instanceId, now);
|
||||
|
||||
if (botAI->IsTank(bot) && bot->HasAura(SPELL_RED_BEAM_DEBUFF))
|
||||
@@ -1458,7 +1459,7 @@ bool NightbaneManageTimersAndTrackersAction::Execute(Event event)
|
||||
if (botAI->IsRanged(bot))
|
||||
nightbaneRangedStep.erase(botGuid);
|
||||
|
||||
if (IsInstanceTimerManager(botAI, bot))
|
||||
if (IsMechanicTrackerBot(botAI, bot, KARAZHAN_MAP_ID, nullptr))
|
||||
nightbaneDpsWaitTimer.erase(instanceId);
|
||||
}
|
||||
// Erase flight phase timer and Rain of Bones tracker on ground phase and start DPS wait timer
|
||||
@@ -1466,7 +1467,7 @@ bool NightbaneManageTimersAndTrackersAction::Execute(Event event)
|
||||
{
|
||||
nightbaneRainOfBonesHit.erase(botGuid);
|
||||
|
||||
if (IsInstanceTimerManager(botAI, bot))
|
||||
if (IsMechanicTrackerBot(botAI, bot, KARAZHAN_MAP_ID, nullptr))
|
||||
{
|
||||
nightbaneFlightPhaseStartTimer.erase(instanceId);
|
||||
nightbaneDpsWaitTimer.try_emplace(instanceId, now);
|
||||
@@ -1482,7 +1483,7 @@ bool NightbaneManageTimersAndTrackersAction::Execute(Event event)
|
||||
if (botAI->IsRanged(bot))
|
||||
nightbaneRangedStep.erase(botGuid);
|
||||
|
||||
if (IsInstanceTimerManager(botAI, bot))
|
||||
if (IsMechanicTrackerBot(botAI, bot, KARAZHAN_MAP_ID, nullptr))
|
||||
{
|
||||
nightbaneDpsWaitTimer.erase(instanceId);
|
||||
nightbaneFlightPhaseStartTimer.try_emplace(instanceId, now);
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "MageActions.h"
|
||||
#include "Playerbots.h"
|
||||
#include "PriestActions.h"
|
||||
#include "RaidBossHelpers.h"
|
||||
#include "ReachTargetActions.h"
|
||||
#include "RogueActions.h"
|
||||
#include "ShamanActions.h"
|
||||
@@ -242,6 +243,9 @@ float PrinceMalchezaarEnfeebleKeepDistanceMultiplier::GetValue(Action* action)
|
||||
|
||||
if (bot->HasAura(SPELL_ENFEEBLE))
|
||||
{
|
||||
if (dynamic_cast<CastReachTargetSpellAction*>(action))
|
||||
return 0.0f;
|
||||
|
||||
if (dynamic_cast<MovementAction*>(action) &&
|
||||
!dynamic_cast<PrinceMalchezaarEnfeebledAvoidHazardAction*>(action))
|
||||
return 0.0f;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include "RaidKarazhanHelpers.h"
|
||||
#include "RaidKarazhanActions.h"
|
||||
#include "Playerbots.h"
|
||||
#include "RaidBossHelpers.h"
|
||||
|
||||
using namespace KarazhanHelpers;
|
||||
|
||||
@@ -40,7 +41,7 @@ bool AttumenTheHuntsmanAttumenIsMountedTrigger::IsActive()
|
||||
|
||||
bool AttumenTheHuntsmanBossWipesAggroWhenMountingTrigger::IsActive()
|
||||
{
|
||||
if (!IsInstanceTimerManager(botAI, bot))
|
||||
if (!IsMechanicTrackerBot(botAI, bot, KARAZHAN_MAP_ID, nullptr))
|
||||
return false;
|
||||
|
||||
Unit* midnight = AI_VALUE2(Unit*, "find target", "midnight");
|
||||
@@ -110,7 +111,7 @@ bool BigBadWolfBossIsChasingLittleRedRidingHoodTrigger::IsActive()
|
||||
|
||||
bool RomuloAndJulianneBothBossesRevivedTrigger::IsActive()
|
||||
{
|
||||
if (!IsInstanceTimerManager(botAI, bot))
|
||||
if (!IsMechanicTrackerBot(botAI, bot, KARAZHAN_MAP_ID, nullptr))
|
||||
return false;
|
||||
|
||||
Unit* romulo = AI_VALUE2(Unit*, "find target", "romulo");
|
||||
@@ -126,7 +127,7 @@ bool RomuloAndJulianneBothBossesRevivedTrigger::IsActive()
|
||||
|
||||
bool WizardOfOzNeedTargetPriorityTrigger::IsActive()
|
||||
{
|
||||
if (!IsInstanceTimerManager(botAI, bot))
|
||||
if (!IsMechanicTrackerBot(botAI, bot, KARAZHAN_MAP_ID, nullptr))
|
||||
return false;
|
||||
|
||||
Unit* dorothee = AI_VALUE2(Unit*, "find target", "dorothee");
|
||||
@@ -178,7 +179,7 @@ bool TheCuratorBossAstralFlaresCastArcingSearTrigger::IsActive()
|
||||
|
||||
bool TerestianIllhoofNeedTargetPriorityTrigger::IsActive()
|
||||
{
|
||||
if (!IsInstanceTimerManager(botAI, bot))
|
||||
if (!IsMechanicTrackerBot(botAI, bot, KARAZHAN_MAP_ID, nullptr))
|
||||
return false;
|
||||
|
||||
Unit* illhoof = AI_VALUE2(Unit*, "find target", "terestian illhoof");
|
||||
@@ -202,7 +203,7 @@ bool ShadeOfAranFlameWreathIsActiveTrigger::IsActive()
|
||||
// Exclusion of Banish is so the player may Banish elementals if they wish
|
||||
bool ShadeOfAranConjuredElementalsSummonedTrigger::IsActive()
|
||||
{
|
||||
if (!IsInstanceTimerManager(botAI, bot))
|
||||
if (!IsMechanicTrackerBot(botAI, bot, KARAZHAN_MAP_ID, nullptr))
|
||||
return false;
|
||||
|
||||
Unit* elemental = AI_VALUE2(Unit*, "find target", "conjured elemental");
|
||||
@@ -279,7 +280,7 @@ bool NetherspiteBossIsBanishedTrigger::IsActive()
|
||||
|
||||
bool NetherspiteNeedToManageTimersAndTrackersTrigger::IsActive()
|
||||
{
|
||||
if (!botAI->IsTank(bot) && !IsInstanceTimerManager(botAI, bot))
|
||||
if (!botAI->IsTank(bot) && !IsMechanicTrackerBot(botAI, bot, KARAZHAN_MAP_ID, nullptr))
|
||||
return false;
|
||||
|
||||
Unit* netherspite = AI_VALUE2(Unit*, "find target", "netherspite");
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#include "RaidKarazhanHelpers.h"
|
||||
#include "RaidKarazhanActions.h"
|
||||
#include "Playerbots.h"
|
||||
#include "RtiTargetValue.h"
|
||||
|
||||
namespace KarazhanHelpers
|
||||
{
|
||||
@@ -52,75 +51,6 @@ namespace KarazhanHelpers
|
||||
const Position NIGHTBANE_FLIGHT_STACK_POSITION = { -11159.555f, -1893.526f, 91.473f }; // Broken Barrel
|
||||
const Position NIGHTBANE_RAIN_OF_BONES_POSITION = { -11165.233f, -1911.123f, 91.473f };
|
||||
|
||||
void MarkTargetWithIcon(Player* bot, Unit* target, uint8 iconId)
|
||||
{
|
||||
if (!target)
|
||||
return;
|
||||
|
||||
if (Group* group = bot->GetGroup())
|
||||
{
|
||||
ObjectGuid currentGuid = group->GetTargetIcon(iconId);
|
||||
if (currentGuid != target->GetGUID())
|
||||
group->SetTargetIcon(iconId, bot->GetGUID(), target->GetGUID());
|
||||
}
|
||||
}
|
||||
|
||||
void MarkTargetWithSkull(Player* bot, Unit* target)
|
||||
{
|
||||
MarkTargetWithIcon(bot, target, RtiTargetValue::skullIndex);
|
||||
}
|
||||
|
||||
void MarkTargetWithSquare(Player* bot, Unit* target)
|
||||
{
|
||||
MarkTargetWithIcon(bot, target, RtiTargetValue::squareIndex);
|
||||
}
|
||||
|
||||
void MarkTargetWithStar(Player* bot, Unit* target)
|
||||
{
|
||||
MarkTargetWithIcon(bot, target, RtiTargetValue::starIndex);
|
||||
}
|
||||
|
||||
void MarkTargetWithCircle(Player* bot, Unit* target)
|
||||
{
|
||||
MarkTargetWithIcon(bot, target, RtiTargetValue::circleIndex);
|
||||
}
|
||||
|
||||
void MarkTargetWithMoon(Player* bot, Unit* target)
|
||||
{
|
||||
MarkTargetWithIcon(bot, target, RtiTargetValue::moonIndex);
|
||||
}
|
||||
|
||||
void SetRtiTarget(PlayerbotAI* botAI, const std::string& rtiName, Unit* target)
|
||||
{
|
||||
if (!target)
|
||||
return;
|
||||
|
||||
std::string currentRti = botAI->GetAiObjectContext()->GetValue<std::string>("rti")->Get();
|
||||
Unit* currentTarget = botAI->GetAiObjectContext()->GetValue<Unit*>("rti target")->Get();
|
||||
|
||||
if (currentRti != rtiName || currentTarget != target)
|
||||
{
|
||||
botAI->GetAiObjectContext()->GetValue<std::string>("rti")->Set(rtiName);
|
||||
botAI->GetAiObjectContext()->GetValue<Unit*>("rti target")->Set(target);
|
||||
}
|
||||
}
|
||||
|
||||
// Only one bot is needed to set/reset instance-wide timers
|
||||
bool IsInstanceTimerManager(PlayerbotAI* botAI, Player* bot)
|
||||
{
|
||||
if (Group* group = bot->GetGroup())
|
||||
{
|
||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||
{
|
||||
Player* member = ref->GetSource();
|
||||
if (member && member->IsAlive() && botAI->IsDps(member) && GET_PLAYERBOT_AI(member))
|
||||
return member == bot;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Unit* GetFirstAliveUnit(const std::vector<Unit*>& units)
|
||||
{
|
||||
for (Unit* unit : units)
|
||||
@@ -132,44 +62,6 @@ namespace KarazhanHelpers
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Unit* GetFirstAliveUnitByEntry(PlayerbotAI* botAI, uint32 entry)
|
||||
{
|
||||
const GuidVector npcs = botAI->GetAiObjectContext()->GetValue<GuidVector>("nearest hostile npcs")->Get();
|
||||
for (auto const& npcGuid : npcs)
|
||||
{
|
||||
Unit* unit = botAI->GetUnit(npcGuid);
|
||||
if (unit && unit->IsAlive() && unit->GetEntry() == entry)
|
||||
return unit;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Unit* GetNearestPlayerInRadius(Player* bot, float radius)
|
||||
{
|
||||
Unit* nearestPlayer = nullptr;
|
||||
float nearestDistance = radius;
|
||||
|
||||
if (Group* group = bot->GetGroup())
|
||||
{
|
||||
for (GroupReference* ref = group->GetFirstMember(); ref != nullptr; ref = ref->next())
|
||||
{
|
||||
Player* member = ref->GetSource();
|
||||
if (!member || !member->IsAlive() || member == bot)
|
||||
continue;
|
||||
|
||||
float distance = bot->GetExactDist2d(member);
|
||||
if (distance < nearestDistance)
|
||||
{
|
||||
nearestDistance = distance;
|
||||
nearestPlayer = member;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nearestPlayer;
|
||||
}
|
||||
|
||||
bool IsFlameWreathActive(PlayerbotAI* botAI, Player* bot)
|
||||
{
|
||||
Unit* aran = botAI->GetAiObjectContext()->GetValue<Unit*>("find target", "shade of aran")->Get();
|
||||
|
||||
@@ -61,6 +61,11 @@ namespace KarazhanHelpers
|
||||
NPC_ATTUMEN_THE_HUNTSMAN = 15550,
|
||||
NPC_ATTUMEN_THE_HUNTSMAN_MOUNTED = 16152,
|
||||
|
||||
// Terestian Illhoof
|
||||
NPC_TERESTIAN_ILLHOOF = 15688,
|
||||
NPC_DEMON_CHAINS = 17248,
|
||||
NPC_KILREK = 17229,
|
||||
|
||||
// Shade of Aran
|
||||
NPC_CONJURED_ELEMENTAL = 17167,
|
||||
|
||||
@@ -74,8 +79,8 @@ namespace KarazhanHelpers
|
||||
NPC_NETHERSPITE_INFERNAL = 17646,
|
||||
};
|
||||
|
||||
const uint32 KARAZHAN_MAP_ID = 532;
|
||||
const float NIGHTBANE_FLIGHT_Z = 95.0f;
|
||||
constexpr uint32 KARAZHAN_MAP_ID = 532;
|
||||
constexpr float NIGHTBANE_FLIGHT_Z = 95.0f;
|
||||
|
||||
// Attumen the Huntsman
|
||||
extern std::unordered_map<uint32, time_t> attumenDpsWaitTimer;
|
||||
@@ -105,17 +110,7 @@ namespace KarazhanHelpers
|
||||
extern const Position NIGHTBANE_FLIGHT_STACK_POSITION;
|
||||
extern const Position NIGHTBANE_RAIN_OF_BONES_POSITION;
|
||||
|
||||
void MarkTargetWithIcon(Player* bot, Unit* target, uint8 iconId);
|
||||
void MarkTargetWithSkull(Player* bot, Unit* target);
|
||||
void MarkTargetWithSquare(Player* bot, Unit* target);
|
||||
void MarkTargetWithStar(Player* bot, Unit* target);
|
||||
void MarkTargetWithCircle(Player* bot, Unit* target);
|
||||
void MarkTargetWithMoon(Player* bot, Unit* target);
|
||||
void SetRtiTarget(PlayerbotAI* botAI, const std::string& rtiName, Unit* target);
|
||||
bool IsInstanceTimerManager(PlayerbotAI* botAI, Player* bot);
|
||||
Unit* GetFirstAliveUnit(const std::vector<Unit*>& units);
|
||||
Unit* GetFirstAliveUnitByEntry(PlayerbotAI* botAI, uint32 entry);
|
||||
Unit* GetNearestPlayerInRadius(Player* bot, float radius);
|
||||
bool IsFlameWreathActive(PlayerbotAI* botAI, Player* bot);
|
||||
std::vector<Player*> GetRedBlockers(PlayerbotAI* botAI, Player* bot);
|
||||
std::vector<Player*> GetBlueBlockers(PlayerbotAI* botAI, Player* bot);
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "ObjectAccessor.h"
|
||||
#include "ObjectGuid.h"
|
||||
#include "Playerbots.h"
|
||||
#include "RaidBossHelpers.h"
|
||||
|
||||
using namespace MagtheridonHelpers;
|
||||
|
||||
@@ -14,46 +15,45 @@ bool MagtheridonMainTankAttackFirstThreeChannelersAction::Execute(Event event)
|
||||
return false;
|
||||
|
||||
Creature* channelerSquare = GetChanneler(bot, SOUTH_CHANNELER);
|
||||
if (channelerSquare && channelerSquare->IsAlive())
|
||||
if (channelerSquare)
|
||||
MarkTargetWithSquare(bot, channelerSquare);
|
||||
|
||||
Creature* channelerStar = GetChanneler(bot, WEST_CHANNELER);
|
||||
if (channelerStar && channelerStar->IsAlive())
|
||||
if (channelerStar)
|
||||
MarkTargetWithStar(bot, channelerStar);
|
||||
|
||||
Creature* channelerCircle = GetChanneler(bot, EAST_CHANNELER);
|
||||
if (channelerCircle && channelerCircle->IsAlive())
|
||||
if (channelerCircle)
|
||||
MarkTargetWithCircle(bot, channelerCircle);
|
||||
|
||||
// After first three channelers are dead, wait for Magtheridon to activate
|
||||
if ((!channelerSquare || !channelerSquare->IsAlive()) &&
|
||||
(!channelerStar || !channelerStar->IsAlive()) &&
|
||||
(!channelerCircle || !channelerCircle->IsAlive()))
|
||||
if (!channelerSquare && !channelerStar && !channelerCircle)
|
||||
{
|
||||
const Location& position = MagtheridonsLairLocations::WaitingForMagtheridonPosition;
|
||||
if (!bot->IsWithinDist2d(position.x, position.y, 2.0f))
|
||||
const Position& position = WAITING_FOR_MAGTHERIDON_POSITION;
|
||||
if (!bot->IsWithinDist2d(position.GetPositionX(), position.GetPositionY(), 2.0f))
|
||||
{
|
||||
return MoveTo(bot->GetMapId(), position.x, position.y, position.z, false, false, false, false,
|
||||
return MoveTo(MAGTHERIDON_MAP_ID, position.GetPositionX(), position.GetPositionY(),
|
||||
position.GetPositionZ(), false, false, false, false,
|
||||
MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||
}
|
||||
|
||||
bot->SetFacingTo(position.orientation);
|
||||
bot->SetFacingTo(position.GetOrientation());
|
||||
return true;
|
||||
}
|
||||
|
||||
Creature* currentTarget = nullptr;
|
||||
std::string rtiName;
|
||||
if (channelerSquare && channelerSquare->IsAlive())
|
||||
if (channelerSquare)
|
||||
{
|
||||
currentTarget = channelerSquare;
|
||||
rtiName = "square";
|
||||
}
|
||||
else if (channelerStar && channelerStar->IsAlive())
|
||||
else if (channelerStar)
|
||||
{
|
||||
currentTarget = channelerStar;
|
||||
rtiName = "star";
|
||||
}
|
||||
else if (channelerCircle && channelerCircle->IsAlive())
|
||||
else if (channelerCircle)
|
||||
{
|
||||
currentTarget = channelerCircle;
|
||||
rtiName = "circle";
|
||||
@@ -70,7 +70,7 @@ bool MagtheridonMainTankAttackFirstThreeChannelersAction::Execute(Event event)
|
||||
bool MagtheridonFirstAssistTankAttackNWChannelerAction::Execute(Event event)
|
||||
{
|
||||
Creature* channelerDiamond = GetChanneler(bot, NORTHWEST_CHANNELER);
|
||||
if (!channelerDiamond || !channelerDiamond->IsAlive())
|
||||
if (!channelerDiamond)
|
||||
return false;
|
||||
|
||||
MarkTargetWithDiamond(bot, channelerDiamond);
|
||||
@@ -81,18 +81,18 @@ bool MagtheridonFirstAssistTankAttackNWChannelerAction::Execute(Event event)
|
||||
|
||||
if (channelerDiamond->GetVictim() == bot)
|
||||
{
|
||||
const Location& position = MagtheridonsLairLocations::NWChannelerTankPosition;
|
||||
const Position& position = NW_CHANNELER_TANK_POSITION;
|
||||
const float maxDistance = 3.0f;
|
||||
float distanceToPosition = bot->GetExactDist2d(position.GetPositionX(), position.GetPositionY());
|
||||
|
||||
if (bot->GetExactDist2d(position.x, position.y) > maxDistance)
|
||||
if (distanceToPosition > maxDistance)
|
||||
{
|
||||
float dX = position.x - bot->GetPositionX();
|
||||
float dY = position.y - bot->GetPositionY();
|
||||
float dist = sqrt(dX * dX + dY * dY);
|
||||
float moveX = bot->GetPositionX() + (dX / dist) * maxDistance;
|
||||
float moveY = bot->GetPositionY() + (dY / dist) * maxDistance;
|
||||
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(bot->GetMapId(), moveX, moveY, position.z, false, false, false, false,
|
||||
return MoveTo(MAGTHERIDON_MAP_ID, moveX, moveY, position.GetPositionZ(), false, false, false, false,
|
||||
MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||
}
|
||||
}
|
||||
@@ -103,7 +103,7 @@ bool MagtheridonFirstAssistTankAttackNWChannelerAction::Execute(Event event)
|
||||
bool MagtheridonSecondAssistTankAttackNEChannelerAction::Execute(Event event)
|
||||
{
|
||||
Creature* channelerTriangle = GetChanneler(bot, NORTHEAST_CHANNELER);
|
||||
if (!channelerTriangle || !channelerTriangle->IsAlive())
|
||||
if (!channelerTriangle)
|
||||
return false;
|
||||
|
||||
MarkTargetWithTriangle(bot, channelerTriangle);
|
||||
@@ -114,18 +114,18 @@ bool MagtheridonSecondAssistTankAttackNEChannelerAction::Execute(Event event)
|
||||
|
||||
if (channelerTriangle->GetVictim() == bot)
|
||||
{
|
||||
const Location& position = MagtheridonsLairLocations::NEChannelerTankPosition;
|
||||
const Position& position = NE_CHANNELER_TANK_POSITION;
|
||||
const float maxDistance = 3.0f;
|
||||
float distanceToPosition = bot->GetExactDist2d(position.GetPositionX(), position.GetPositionY());
|
||||
|
||||
if (bot->GetExactDist2d(position.x, position.y) > maxDistance)
|
||||
if (distanceToPosition > maxDistance)
|
||||
{
|
||||
float dX = position.x - bot->GetPositionX();
|
||||
float dY = position.y - bot->GetPositionY();
|
||||
float dist = sqrt(dX * dX + dY * dY);
|
||||
float moveX = bot->GetPositionX() + (dX / dist) * maxDistance;
|
||||
float moveY = bot->GetPositionY() + (dY / dist) * maxDistance;
|
||||
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(bot->GetMapId(), moveX, moveY, position.z, false, false, false, false,
|
||||
return MoveTo(MAGTHERIDON_MAP_ID, moveX, moveY, position.GetPositionZ(), false, false, false, false,
|
||||
MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||
}
|
||||
}
|
||||
@@ -175,7 +175,7 @@ bool MagtheridonMisdirectHellfireChannelers::Execute(Event event)
|
||||
switch (hunterIndex)
|
||||
{
|
||||
case 0:
|
||||
if (mainTank && channelerStar && channelerStar->IsAlive() &&
|
||||
if (mainTank && channelerStar &&
|
||||
channelerStar->GetVictim() != mainTank)
|
||||
{
|
||||
if (botAI->CanCastSpell("misdirection", mainTank))
|
||||
@@ -190,7 +190,7 @@ bool MagtheridonMisdirectHellfireChannelers::Execute(Event event)
|
||||
break;
|
||||
|
||||
case 1:
|
||||
if (mainTank && channelerCircle && channelerCircle->IsAlive() &&
|
||||
if (mainTank && channelerCircle &&
|
||||
channelerCircle->GetVictim() != mainTank)
|
||||
{
|
||||
if (botAI->CanCastSpell("misdirection", mainTank))
|
||||
@@ -215,90 +215,69 @@ bool MagtheridonAssignDPSPriorityAction::Execute(Event event)
|
||||
{
|
||||
// Listed in order of priority
|
||||
Creature* channelerSquare = GetChanneler(bot, SOUTH_CHANNELER);
|
||||
if (channelerSquare && channelerSquare->IsAlive())
|
||||
if (channelerSquare)
|
||||
{
|
||||
SetRtiTarget(botAI, "square", channelerSquare);
|
||||
|
||||
if (bot->GetTarget() != channelerSquare->GetGUID())
|
||||
{
|
||||
bot->SetSelection(channelerSquare->GetGUID());
|
||||
return Attack(channelerSquare);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Creature* channelerStar = GetChanneler(bot, WEST_CHANNELER);
|
||||
if (channelerStar && channelerStar->IsAlive())
|
||||
if (channelerStar)
|
||||
{
|
||||
SetRtiTarget(botAI, "star", channelerStar);
|
||||
|
||||
if (bot->GetTarget() != channelerStar->GetGUID())
|
||||
{
|
||||
bot->SetSelection(channelerStar->GetGUID());
|
||||
return Attack(channelerStar);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Creature* channelerCircle = GetChanneler(bot, EAST_CHANNELER);
|
||||
if (channelerCircle && channelerCircle->IsAlive())
|
||||
if (channelerCircle)
|
||||
{
|
||||
SetRtiTarget(botAI, "circle", channelerCircle);
|
||||
|
||||
if (bot->GetTarget() != channelerCircle->GetGUID())
|
||||
{
|
||||
bot->SetSelection(channelerCircle->GetGUID());
|
||||
return Attack(channelerCircle);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Creature* channelerDiamond = GetChanneler(bot, NORTHWEST_CHANNELER);
|
||||
if (channelerDiamond && channelerDiamond->IsAlive())
|
||||
if (channelerDiamond)
|
||||
{
|
||||
SetRtiTarget(botAI, "diamond", channelerDiamond);
|
||||
|
||||
if (bot->GetTarget() != channelerDiamond->GetGUID())
|
||||
{
|
||||
bot->SetSelection(channelerDiamond->GetGUID());
|
||||
return Attack(channelerDiamond);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Creature* channelerTriangle = GetChanneler(bot, NORTHEAST_CHANNELER);
|
||||
if (channelerTriangle && channelerTriangle->IsAlive())
|
||||
if (channelerTriangle)
|
||||
{
|
||||
SetRtiTarget(botAI, "triangle", channelerTriangle);
|
||||
|
||||
if (bot->GetTarget() != channelerTriangle->GetGUID())
|
||||
{
|
||||
bot->SetSelection(channelerTriangle->GetGUID());
|
||||
return Attack(channelerTriangle);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Unit* magtheridon = AI_VALUE2(Unit*, "find target", "magtheridon");
|
||||
if (magtheridon && !magtheridon->HasAura(SPELL_SHADOW_CAGE) &&
|
||||
(!channelerSquare || !channelerSquare->IsAlive()) &&
|
||||
(!channelerStar || !channelerStar->IsAlive()) &&
|
||||
(!channelerCircle || !channelerCircle->IsAlive()) &&
|
||||
(!channelerDiamond || !channelerDiamond->IsAlive()) &&
|
||||
(!channelerTriangle || !channelerTriangle->IsAlive()))
|
||||
!channelerSquare && !channelerStar && !channelerCircle &&
|
||||
!channelerDiamond && !channelerTriangle)
|
||||
{
|
||||
SetRtiTarget(botAI, "cross", magtheridon);
|
||||
|
||||
if (bot->GetTarget() != magtheridon->GetGUID())
|
||||
{
|
||||
bot->SetSelection(magtheridon->GetGUID());
|
||||
return Attack(magtheridon);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -343,15 +322,15 @@ bool MagtheridonWarlockCCBurningAbyssalAction::Execute(Event event)
|
||||
if (warlockIndex >= 0 && warlockIndex < abyssals.size())
|
||||
{
|
||||
Unit* assignedAbyssal = abyssals[warlockIndex];
|
||||
if (!assignedAbyssal->HasAura(SPELL_BANISH) && botAI->CanCastSpell(SPELL_BANISH, assignedAbyssal, true))
|
||||
if (!botAI->HasAura("banish", assignedAbyssal) && botAI->CanCastSpell("banish", assignedAbyssal))
|
||||
return botAI->CastSpell("banish", assignedAbyssal);
|
||||
}
|
||||
|
||||
for (size_t i = warlocks.size(); i < abyssals.size(); ++i)
|
||||
{
|
||||
Unit* excessAbyssal = abyssals[i];
|
||||
if (!excessAbyssal->HasAura(SPELL_BANISH) && !excessAbyssal->HasAura(SPELL_FEAR) &&
|
||||
botAI->CanCastSpell(SPELL_FEAR, excessAbyssal, true))
|
||||
if (!botAI->HasAura("banish", excessAbyssal) && !botAI->HasAura("fear", excessAbyssal) &&
|
||||
botAI->CanCastSpell("fear", excessAbyssal))
|
||||
return botAI->CastSpell("fear", excessAbyssal);
|
||||
}
|
||||
|
||||
@@ -373,22 +352,20 @@ bool MagtheridonMainTankPositionBossAction::Execute(Event event)
|
||||
|
||||
if (magtheridon->GetVictim() == bot)
|
||||
{
|
||||
const Location& position = MagtheridonsLairLocations::MagtheridonTankPosition;
|
||||
const Position& position = MAGTHERIDON_TANK_POSITION;
|
||||
const float maxDistance = 2.0f;
|
||||
float distanceToPosition = bot->GetExactDist2d(position.GetPositionX(), position.GetPositionY());
|
||||
|
||||
if (bot->GetExactDist2d(position.x, position.y) > maxDistance)
|
||||
if (distanceToPosition > maxDistance)
|
||||
{
|
||||
float dX = position.x - bot->GetPositionX();
|
||||
float dY = position.y - bot->GetPositionY();
|
||||
float dist = sqrt(dX * dX + dY * dY);
|
||||
float moveX = bot->GetPositionX() + (dX / dist) * maxDistance;
|
||||
float moveY = bot->GetPositionY() + (dY / dist) * maxDistance;
|
||||
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(bot->GetMapId(), moveX, moveY, position.z, false, false, false, false,
|
||||
return MoveTo(MAGTHERIDON_MAP_ID, moveX, moveY, position.GetPositionZ(), false, false, false, false,
|
||||
MovementPriority::MOVEMENT_COMBAT, true, true);
|
||||
}
|
||||
|
||||
bot->SetFacingTo(position.orientation);
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -440,13 +417,13 @@ bool MagtheridonSpreadRangedAction::Execute(Event event)
|
||||
}
|
||||
|
||||
bool isHealer = botAI->IsHeal(bot);
|
||||
const Location& center = isHealer
|
||||
? MagtheridonsLairLocations::HealerSpreadPosition
|
||||
: MagtheridonsLairLocations::RangedSpreadPosition;
|
||||
const Position& center = isHealer
|
||||
? HEALER_SPREAD_POSITION
|
||||
: RANGED_SPREAD_POSITION;
|
||||
float maxSpreadRadius = isHealer ? 15.0f : 20.0f;
|
||||
float centerX = center.x;
|
||||
float centerY = center.y;
|
||||
float centerZ = bot->GetPositionZ();
|
||||
float centerX = center.GetPositionX();
|
||||
float centerY = center.GetPositionY();
|
||||
float centerZ = center.GetPositionZ();
|
||||
const float radiusBuffer = 3.0f;
|
||||
|
||||
if (!initialPositions.count(bot->GetGUID()))
|
||||
@@ -479,7 +456,7 @@ bool MagtheridonSpreadRangedAction::Execute(Event event)
|
||||
|
||||
bot->AttackStop();
|
||||
bot->InterruptNonMeleeSpells(false);
|
||||
return MoveTo(bot->GetMapId(), destX, destY, destZ, false, false, false, false,
|
||||
return MoveTo(MAGTHERIDON_MAP_ID, destX, destY, destZ, false, false, false, false,
|
||||
MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||
}
|
||||
hasReachedInitialPosition[bot->GetGUID()] = true;
|
||||
@@ -499,7 +476,7 @@ bool MagtheridonSpreadRangedAction::Execute(Event event)
|
||||
{
|
||||
bot->AttackStop();
|
||||
bot->InterruptNonMeleeSpells(false);
|
||||
return MoveTo(bot->GetMapId(), targetX, targetY, centerZ, false, false, false, false,
|
||||
return MoveTo(MAGTHERIDON_MAP_ID, targetX, targetY, centerZ, false, false, false, false,
|
||||
MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||
}
|
||||
}
|
||||
@@ -593,7 +570,7 @@ bool MagtheridonUseManticronCubeAction::HandleWaitingPhase(const CubeInfo& cubeI
|
||||
{
|
||||
bot->AttackStop();
|
||||
bot->InterruptNonMeleeSpells(true);
|
||||
return MoveTo(bot->GetMapId(), targetX, targetY, targetZ, false, false, false, false,
|
||||
return MoveTo(MAGTHERIDON_MAP_ID, targetX, targetY, targetZ, false, false, false, false,
|
||||
MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||
}
|
||||
}
|
||||
@@ -603,7 +580,7 @@ bool MagtheridonUseManticronCubeAction::HandleWaitingPhase(const CubeInfo& cubeI
|
||||
float fallbackY = cubeInfo.y + sin(angle) * safeWaitDistance;
|
||||
float fallbackZ = bot->GetPositionZ();
|
||||
|
||||
return MoveTo(bot->GetMapId(), fallbackX, fallbackY, fallbackZ, false, false, false, false,
|
||||
return MoveTo(MAGTHERIDON_MAP_ID, fallbackX, fallbackY, fallbackZ, false, false, false, false,
|
||||
MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||
}
|
||||
|
||||
@@ -638,7 +615,7 @@ bool MagtheridonUseManticronCubeAction::HandleCubeInteraction(const CubeInfo& cu
|
||||
|
||||
bot->AttackStop();
|
||||
bot->InterruptNonMeleeSpells(true);
|
||||
return MoveTo(bot->GetMapId(), targetX, targetY, targetZ, false, false, false, false,
|
||||
return MoveTo(MAGTHERIDON_MAP_ID, targetX, targetY, targetZ, false, false, false, false,
|
||||
MovementPriority::MOVEMENT_FORCED, true, false);
|
||||
}
|
||||
|
||||
@@ -663,14 +640,14 @@ bool MagtheridonManageTimersAndAssignmentsAction::Execute(Event event)
|
||||
magtheridon->FindCurrentSpellBySpellId(SPELL_BLAST_NOVA);
|
||||
bool lastBlastNova = lastBlastNovaState[instanceId];
|
||||
|
||||
if (lastBlastNova && !blastNovaActive && IsInstanceTimerManager(botAI, bot))
|
||||
if (lastBlastNova && !blastNovaActive && IsMechanicTrackerBot(botAI, bot, MAGTHERIDON_MAP_ID, nullptr))
|
||||
blastNovaTimer[instanceId] = now;
|
||||
|
||||
lastBlastNovaState[instanceId] = blastNovaActive;
|
||||
|
||||
if (!magtheridon->HasAura(SPELL_SHADOW_CAGE))
|
||||
{
|
||||
if (IsInstanceTimerManager(botAI, bot))
|
||||
if (IsMechanicTrackerBot(botAI, bot, MAGTHERIDON_MAP_ID, nullptr))
|
||||
{
|
||||
spreadWaitTimer.try_emplace(instanceId, now);
|
||||
blastNovaTimer.try_emplace(instanceId, now);
|
||||
@@ -679,11 +656,12 @@ bool MagtheridonManageTimersAndAssignmentsAction::Execute(Event event)
|
||||
}
|
||||
else
|
||||
{
|
||||
MagtheridonSpreadRangedAction::initialPositions.clear();
|
||||
MagtheridonSpreadRangedAction::hasReachedInitialPosition.clear();
|
||||
botToCubeAssignment.clear();
|
||||
ObjectGuid guid = bot->GetGUID();
|
||||
MagtheridonSpreadRangedAction::initialPositions.erase(guid);
|
||||
MagtheridonSpreadRangedAction::hasReachedInitialPosition.erase(guid);
|
||||
botToCubeAssignment.erase(guid);
|
||||
|
||||
if (IsInstanceTimerManager(botAI, bot))
|
||||
if (IsMechanicTrackerBot(botAI, bot, MAGTHERIDON_MAP_ID, nullptr))
|
||||
{
|
||||
spreadWaitTimer.erase(instanceId);
|
||||
blastNovaTimer.erase(instanceId);
|
||||
|
||||
@@ -6,8 +6,6 @@
|
||||
#include "AttackAction.h"
|
||||
#include "MovementActions.h"
|
||||
|
||||
using namespace MagtheridonHelpers;
|
||||
|
||||
class MagtheridonMainTankAttackFirstThreeChannelersAction : public AttackAction
|
||||
{
|
||||
public:
|
||||
@@ -85,8 +83,8 @@ public:
|
||||
private:
|
||||
bool HandleCubeRelease(Unit* magtheridon, GameObject* cube);
|
||||
bool ShouldActivateCubeLogic(Unit* magtheridon);
|
||||
bool HandleWaitingPhase(const CubeInfo& cubeInfo);
|
||||
bool HandleCubeInteraction(const CubeInfo& cubeInfo, GameObject* cube);
|
||||
bool HandleWaitingPhase(const MagtheridonHelpers::CubeInfo& cubeInfo);
|
||||
bool HandleCubeInteraction(const MagtheridonHelpers::CubeInfo& cubeInfo, GameObject* cube);
|
||||
};
|
||||
|
||||
class MagtheridonManageTimersAndAssignmentsAction : public Action
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "GenericSpellActions.h"
|
||||
#include "Playerbots.h"
|
||||
#include "WarlockActions.h"
|
||||
#include "WipeAction.h"
|
||||
|
||||
using namespace MagtheridonHelpers;
|
||||
|
||||
@@ -24,10 +25,10 @@ float MagtheridonUseManticronCubeMultiplier::GetValue(Action* action)
|
||||
auto it = botToCubeAssignment.find(bot->GetGUID());
|
||||
if (it != botToCubeAssignment.end())
|
||||
{
|
||||
if (dynamic_cast<MagtheridonUseManticronCubeAction*>(action))
|
||||
if (dynamic_cast<WipeAction*>(action))
|
||||
return 1.0f;
|
||||
|
||||
return 0.0f;
|
||||
else if (!dynamic_cast<MagtheridonUseManticronCubeAction*>(action))
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,28 +42,31 @@ float MagtheridonWaitToAttackMultiplier::GetValue(Action* action)
|
||||
if (!magtheridon || magtheridon->HasAura(SPELL_SHADOW_CAGE))
|
||||
return 1.0f;
|
||||
|
||||
if (botAI->IsMainTank(bot))
|
||||
return 1.0f;
|
||||
|
||||
const uint8 dpsWaitSeconds = 6;
|
||||
auto it = dpsWaitTimer.find(magtheridon->GetMap()->GetInstanceId());
|
||||
if (it == dpsWaitTimer.end() ||
|
||||
(time(nullptr) - it->second) < dpsWaitSeconds)
|
||||
{
|
||||
if (!botAI->IsMainTank(bot) && (dynamic_cast<AttackAction*>(action) ||
|
||||
(!botAI->IsHeal(bot) && dynamic_cast<CastSpellAction*>(action))))
|
||||
if (dynamic_cast<AttackAction*>(action) ||
|
||||
(!botAI->IsHeal(bot) && dynamic_cast<CastSpellAction*>(action)))
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
// No tank assist for offtanks during the channeler phase
|
||||
// So they don't try to pull channelers from each other or the main tank
|
||||
float MagtheridonDisableOffTankAssistMultiplier::GetValue(Action* action)
|
||||
{
|
||||
Unit* magtheridon = AI_VALUE2(Unit*, "find target", "magtheridon");
|
||||
Unit* channeler = AI_VALUE2(Unit*, "find target", "hellfire channeler");
|
||||
if (!magtheridon)
|
||||
return 1.0f;
|
||||
|
||||
if (bot->GetVictim() == nullptr)
|
||||
return 1.0f;
|
||||
|
||||
if ((botAI->IsAssistTankOfIndex(bot, 0) || botAI->IsAssistTankOfIndex(bot, 1)) &&
|
||||
dynamic_cast<TankAssistAction*>(action))
|
||||
return 0.0f;
|
||||
|
||||
@@ -18,7 +18,7 @@ bool MagtheridonNWChannelerEngagedByFirstAssistTankTrigger::IsActive()
|
||||
Creature* channelerDiamond = GetChanneler(bot, NORTHWEST_CHANNELER);
|
||||
|
||||
return magtheridon && botAI->IsAssistTankOfIndex(bot, 0) &&
|
||||
channelerDiamond && channelerDiamond->IsAlive();
|
||||
channelerDiamond;
|
||||
}
|
||||
|
||||
bool MagtheridonNEChannelerEngagedBySecondAssistTankTrigger::IsActive()
|
||||
@@ -27,7 +27,7 @@ bool MagtheridonNEChannelerEngagedBySecondAssistTankTrigger::IsActive()
|
||||
Creature* channelerTriangle = GetChanneler(bot, NORTHEAST_CHANNELER);
|
||||
|
||||
return magtheridon && botAI->IsAssistTankOfIndex(bot, 1) &&
|
||||
channelerTriangle && channelerTriangle->IsAlive();
|
||||
channelerTriangle;
|
||||
}
|
||||
|
||||
bool MagtheridonPullingWestAndEastChannelersTrigger::IsActive()
|
||||
@@ -38,8 +38,7 @@ bool MagtheridonPullingWestAndEastChannelersTrigger::IsActive()
|
||||
Creature* channelerCircle = GetChanneler(bot, EAST_CHANNELER);
|
||||
|
||||
return magtheridon && bot->getClass() == CLASS_HUNTER &&
|
||||
((channelerStar && channelerStar->IsAlive()) ||
|
||||
(channelerCircle && channelerCircle->IsAlive()));
|
||||
(channelerStar || channelerCircle);
|
||||
}
|
||||
|
||||
bool MagtheridonDeterminingKillOrderTrigger::IsActive()
|
||||
@@ -51,12 +50,11 @@ bool MagtheridonDeterminingKillOrderTrigger::IsActive()
|
||||
Creature* channelerTriangle = GetChanneler(bot, NORTHEAST_CHANNELER);
|
||||
|
||||
if (!magtheridon || botAI->IsHeal(bot) || botAI->IsMainTank(bot) ||
|
||||
(botAI->IsAssistTankOfIndex(bot, 0) && channelerDiamond && channelerDiamond->IsAlive()) ||
|
||||
(botAI->IsAssistTankOfIndex(bot, 1) && channelerTriangle && channelerTriangle->IsAlive()))
|
||||
(botAI->IsAssistTankOfIndex(bot, 0) && channelerDiamond) ||
|
||||
(botAI->IsAssistTankOfIndex(bot, 1) && channelerTriangle))
|
||||
return false;
|
||||
|
||||
return (channeler && channeler->IsAlive()) || (magtheridon &&
|
||||
!magtheridon->HasAura(SPELL_SHADOW_CAGE));
|
||||
return channeler || (magtheridon && !magtheridon->HasAura(SPELL_SHADOW_CAGE));
|
||||
}
|
||||
|
||||
bool MagtheridonBurningAbyssalSpawnedTrigger::IsActive()
|
||||
@@ -84,10 +82,8 @@ bool MagtheridonBossEngagedByMainTankTrigger::IsActive()
|
||||
bool MagtheridonBossEngagedByRangedTrigger::IsActive()
|
||||
{
|
||||
Unit* magtheridon = AI_VALUE2(Unit*, "find target", "magtheridon");
|
||||
Unit* channeler = AI_VALUE2(Unit*, "find target", "hellfire channeler");
|
||||
|
||||
return magtheridon && botAI->IsRanged(bot) &&
|
||||
!(channeler && channeler->IsAlive());
|
||||
return magtheridon && !magtheridon->HasAura(SPELL_SHADOW_CAGE) && botAI->IsRanged(bot);
|
||||
}
|
||||
|
||||
bool MagtheridonIncomingBlastNovaTrigger::IsActive()
|
||||
@@ -122,7 +118,5 @@ bool MagtheridonIncomingBlastNovaTrigger::IsActive()
|
||||
|
||||
bool MagtheridonNeedToManageTimersAndAssignmentsTrigger::IsActive()
|
||||
{
|
||||
Unit* magtheridon = AI_VALUE2(Unit*, "find target", "magtheridon");
|
||||
|
||||
return magtheridon;
|
||||
return AI_VALUE2(Unit*, "find target", "magtheridon");
|
||||
}
|
||||
|
||||
@@ -1,22 +1,18 @@
|
||||
#include "RaidMagtheridonHelpers.h"
|
||||
#include "Creature.h"
|
||||
#include "GameObject.h"
|
||||
#include "GroupReference.h"
|
||||
#include "Map.h"
|
||||
#include "ObjectGuid.h"
|
||||
#include "Playerbots.h"
|
||||
|
||||
namespace MagtheridonHelpers
|
||||
{
|
||||
namespace MagtheridonsLairLocations
|
||||
{
|
||||
const Location WaitingForMagtheridonPosition = { 1.359f, 2.048f, -0.406f, 3.135f };
|
||||
const Location MagtheridonTankPosition = { 22.827f, 2.105f, -0.406f, 3.135f };
|
||||
const Location NWChannelerTankPosition = { -11.764f, 30.818f, -0.411f, 0.0f };
|
||||
const Location NEChannelerTankPosition = { -12.490f, -26.211f, -0.411f, 0.0f };
|
||||
const Location RangedSpreadPosition = { -14.890f, 1.995f, -0.406f, 0.0f };
|
||||
const Location HealerSpreadPosition = { -2.265f, 1.874f, -0.404f, 0.0f };
|
||||
}
|
||||
const Position WAITING_FOR_MAGTHERIDON_POSITION = { 1.359f, 2.048f, -0.406f, 3.135f };
|
||||
const Position MAGTHERIDON_TANK_POSITION = { 22.827f, 2.105f, -0.406f, 3.135f };
|
||||
const Position NW_CHANNELER_TANK_POSITION = { -11.764f, 30.818f, -0.411f, 0.0f };
|
||||
const Position NE_CHANNELER_TANK_POSITION = { -12.490f, -26.211f, -0.411f, 0.0f };
|
||||
const Position RANGED_SPREAD_POSITION = { -14.890f, 1.995f, -0.406f, 0.0f };
|
||||
const Position HEALER_SPREAD_POSITION = { -2.265f, 1.874f, -0.404f, 0.0f };
|
||||
|
||||
// Identify channelers by their database GUIDs
|
||||
Creature* GetChanneler(Player* bot, uint32 dbGuid)
|
||||
@@ -29,63 +25,11 @@ namespace MagtheridonHelpers
|
||||
if (it == map->GetCreatureBySpawnIdStore().end())
|
||||
return nullptr;
|
||||
|
||||
return it->second;
|
||||
}
|
||||
Creature* channeler = it->second;
|
||||
if (!channeler->IsAlive())
|
||||
return nullptr;
|
||||
|
||||
void MarkTargetWithIcon(Player* bot, Unit* target, uint8 iconId)
|
||||
{
|
||||
Group* group = bot->GetGroup();
|
||||
if (!target || !group)
|
||||
return;
|
||||
|
||||
ObjectGuid currentGuid = group->GetTargetIcon(iconId);
|
||||
if (currentGuid != target->GetGUID())
|
||||
group->SetTargetIcon(iconId, bot->GetGUID(), target->GetGUID());
|
||||
}
|
||||
|
||||
void SetRtiTarget(PlayerbotAI* botAI, const std::string& rtiName, Unit* target)
|
||||
{
|
||||
if (!target)
|
||||
return;
|
||||
|
||||
std::string currentRti = botAI->GetAiObjectContext()->GetValue<std::string>("rti")->Get();
|
||||
Unit* currentTarget = botAI->GetAiObjectContext()->GetValue<Unit*>("rti target")->Get();
|
||||
|
||||
if (currentRti != rtiName || currentTarget != target)
|
||||
{
|
||||
botAI->GetAiObjectContext()->GetValue<std::string>("rti")->Set(rtiName);
|
||||
botAI->GetAiObjectContext()->GetValue<Unit*>("rti target")->Set(target);
|
||||
}
|
||||
}
|
||||
|
||||
void MarkTargetWithSquare(Player* bot, Unit* target)
|
||||
{
|
||||
MarkTargetWithIcon(bot, target, RtiTargetValue::squareIndex);
|
||||
}
|
||||
|
||||
void MarkTargetWithStar(Player* bot, Unit* target)
|
||||
{
|
||||
MarkTargetWithIcon(bot, target, RtiTargetValue::starIndex);
|
||||
}
|
||||
|
||||
void MarkTargetWithCircle(Player* bot, Unit* target)
|
||||
{
|
||||
MarkTargetWithIcon(bot, target, RtiTargetValue::circleIndex);
|
||||
}
|
||||
|
||||
void MarkTargetWithDiamond(Player* bot, Unit* target)
|
||||
{
|
||||
MarkTargetWithIcon(bot, target, RtiTargetValue::diamondIndex);
|
||||
}
|
||||
|
||||
void MarkTargetWithTriangle(Player* bot, Unit* target)
|
||||
{
|
||||
MarkTargetWithIcon(bot, target, RtiTargetValue::triangleIndex);
|
||||
}
|
||||
|
||||
void MarkTargetWithCross(Player* bot, Unit* target)
|
||||
{
|
||||
MarkTargetWithIcon(bot, target, RtiTargetValue::crossIndex);
|
||||
return channeler;
|
||||
}
|
||||
|
||||
const std::vector<uint32> MANTICRON_CUBE_DB_GUIDS = { 43157, 43158, 43159, 43160, 43161 };
|
||||
@@ -208,19 +152,4 @@ namespace MagtheridonHelpers
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IsInstanceTimerManager(PlayerbotAI* botAI, Player* bot)
|
||||
{
|
||||
if (Group* group = bot->GetGroup())
|
||||
{
|
||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||
{
|
||||
Player* member = ref->GetSource();
|
||||
if (member && member->IsAlive() && botAI->IsDps(member) && GET_PLAYERBOT_AI(member))
|
||||
return member == bot;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
#include "Group.h"
|
||||
#include "ObjectGuid.h"
|
||||
#include "PlayerbotAI.h"
|
||||
#include "RtiTargetValue.h"
|
||||
|
||||
namespace MagtheridonHelpers
|
||||
{
|
||||
@@ -19,10 +18,6 @@ namespace MagtheridonHelpers
|
||||
SPELL_BLAST_NOVA = 30616,
|
||||
SPELL_SHADOW_GRASP = 30410,
|
||||
|
||||
// Warlock
|
||||
SPELL_BANISH = 18647,
|
||||
SPELL_FEAR = 6215,
|
||||
|
||||
// Hunter
|
||||
SPELL_MISDIRECTION = 35079,
|
||||
};
|
||||
@@ -38,6 +33,7 @@ namespace MagtheridonHelpers
|
||||
GO_BLAZE = 181832,
|
||||
};
|
||||
|
||||
constexpr uint32 MAGTHERIDON_MAP_ID = 544;
|
||||
constexpr uint32 SOUTH_CHANNELER = 90978;
|
||||
constexpr uint32 WEST_CHANNELER = 90979;
|
||||
constexpr uint32 NORTHWEST_CHANNELER = 90980;
|
||||
@@ -45,31 +41,14 @@ namespace MagtheridonHelpers
|
||||
constexpr uint32 NORTHEAST_CHANNELER = 90981;
|
||||
|
||||
Creature* GetChanneler(Player* bot, uint32 dbGuid);
|
||||
void MarkTargetWithIcon(Player* bot, Unit* target, uint8 iconId);
|
||||
void MarkTargetWithSquare(Player* bot, Unit* target);
|
||||
void MarkTargetWithStar(Player* bot, Unit* target);
|
||||
void MarkTargetWithCircle(Player* bot, Unit* target);
|
||||
void MarkTargetWithDiamond(Player* bot, Unit* target);
|
||||
void MarkTargetWithTriangle(Player* bot, Unit* target);
|
||||
void MarkTargetWithCross(Player* bot, Unit* target);
|
||||
void SetRtiTarget(PlayerbotAI* botAI, const std::string& rtiName, Unit* target);
|
||||
bool IsSafeFromMagtheridonHazards(PlayerbotAI* botAI, Player* bot, float x, float y, float z);
|
||||
bool IsInstanceTimerManager(PlayerbotAI* botAI, Player* bot);
|
||||
|
||||
struct Location
|
||||
{
|
||||
float x, y, z, orientation;
|
||||
};
|
||||
|
||||
namespace MagtheridonsLairLocations
|
||||
{
|
||||
extern const Location WaitingForMagtheridonPosition;
|
||||
extern const Location MagtheridonTankPosition;
|
||||
extern const Location NWChannelerTankPosition;
|
||||
extern const Location NEChannelerTankPosition;
|
||||
extern const Location RangedSpreadPosition;
|
||||
extern const Location HealerSpreadPosition;
|
||||
}
|
||||
extern const Position WAITING_FOR_MAGTHERIDON_POSITION;
|
||||
extern const Position MAGTHERIDON_TANK_POSITION;
|
||||
extern const Position NW_CHANNELER_TANK_POSITION;
|
||||
extern const Position NE_CHANNELER_TANK_POSITION;
|
||||
extern const Position RANGED_SPREAD_POSITION;
|
||||
extern const Position HEALER_SPREAD_POSITION;
|
||||
|
||||
struct CubeInfo
|
||||
{
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#define _PLAYERBOT_RAIDMCACTIONCONTEXT_H
|
||||
|
||||
#include "Action.h"
|
||||
#include "BossAuraActions.h"
|
||||
#include "NamedObjectContext.h"
|
||||
#include "RaidMcActions.h"
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#define _PLAYERBOT_RAIDMCTRIGGERCONTEXT_H
|
||||
|
||||
#include "AiObjectContext.h"
|
||||
#include "BossAuraTriggers.h"
|
||||
#include "NamedObjectContext.h"
|
||||
#include "RaidMcTriggers.h"
|
||||
|
||||
|
||||
142
src/Ai/Raid/RaidBossHelpers.cpp
Normal file
142
src/Ai/Raid/RaidBossHelpers.cpp
Normal file
@@ -0,0 +1,142 @@
|
||||
#include "RaidBossHelpers.h"
|
||||
#include "Playerbots.h"
|
||||
#include "RtiTargetValue.h"
|
||||
|
||||
// Functions to mark targets with raid target icons
|
||||
// Note that these functions do not allow the player to change the icon during the encounter
|
||||
void MarkTargetWithIcon(Player* bot, Unit* target, uint8 iconId)
|
||||
{
|
||||
if (!target)
|
||||
return;
|
||||
|
||||
if (Group* group = bot->GetGroup())
|
||||
{
|
||||
ObjectGuid currentGuid = group->GetTargetIcon(iconId);
|
||||
if (currentGuid != target->GetGUID())
|
||||
group->SetTargetIcon(iconId, bot->GetGUID(), target->GetGUID());
|
||||
}
|
||||
}
|
||||
|
||||
void MarkTargetWithSkull(Player* bot, Unit* target)
|
||||
{
|
||||
MarkTargetWithIcon(bot, target, RtiTargetValue::skullIndex);
|
||||
}
|
||||
|
||||
void MarkTargetWithSquare(Player* bot, Unit* target)
|
||||
{
|
||||
MarkTargetWithIcon(bot, target, RtiTargetValue::squareIndex);
|
||||
}
|
||||
|
||||
void MarkTargetWithStar(Player* bot, Unit* target)
|
||||
{
|
||||
MarkTargetWithIcon(bot, target, RtiTargetValue::starIndex);
|
||||
}
|
||||
|
||||
void MarkTargetWithCircle(Player* bot, Unit* target)
|
||||
{
|
||||
MarkTargetWithIcon(bot, target, RtiTargetValue::circleIndex);
|
||||
}
|
||||
|
||||
void MarkTargetWithDiamond(Player* bot, Unit* target)
|
||||
{
|
||||
MarkTargetWithIcon(bot, target, RtiTargetValue::diamondIndex);
|
||||
}
|
||||
|
||||
void MarkTargetWithTriangle(Player* bot, Unit* target)
|
||||
{
|
||||
MarkTargetWithIcon(bot, target, RtiTargetValue::triangleIndex);
|
||||
}
|
||||
|
||||
void MarkTargetWithCross(Player* bot, Unit* target)
|
||||
{
|
||||
MarkTargetWithIcon(bot, target, RtiTargetValue::crossIndex);
|
||||
}
|
||||
|
||||
void MarkTargetWithMoon(Player* bot, Unit* target)
|
||||
{
|
||||
MarkTargetWithIcon(bot, target, RtiTargetValue::moonIndex);
|
||||
}
|
||||
|
||||
// For bots to set their raid target icon to the specified icon on the specified target
|
||||
void SetRtiTarget(PlayerbotAI* botAI, const std::string& rtiName, Unit* target)
|
||||
{
|
||||
if (!target)
|
||||
return;
|
||||
|
||||
std::string currentRti = botAI->GetAiObjectContext()->GetValue<std::string>("rti")->Get();
|
||||
Unit* currentTarget = botAI->GetAiObjectContext()->GetValue<Unit*>("rti target")->Get();
|
||||
|
||||
if (currentRti != rtiName || currentTarget != target)
|
||||
{
|
||||
botAI->GetAiObjectContext()->GetValue<std::string>("rti")->Set(rtiName);
|
||||
botAI->GetAiObjectContext()->GetValue<Unit*>("rti target")->Set(target);
|
||||
}
|
||||
}
|
||||
|
||||
// Return the first alive DPS bot in the specified instance map, excluding any specified bot
|
||||
// Intended for purposes of storing and erasing timers and trackers in associative containers
|
||||
bool IsMechanicTrackerBot(PlayerbotAI* botAI, Player* bot, uint32 mapId, Player* exclude)
|
||||
{
|
||||
if (Group* group = bot->GetGroup())
|
||||
{
|
||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||
{
|
||||
Player* member = ref->GetSource();
|
||||
if (!member || !member->IsAlive() || member->GetMapId() != mapId ||
|
||||
!GET_PLAYERBOT_AI(member) || !botAI->IsDps(member))
|
||||
continue;
|
||||
|
||||
if (member != exclude)
|
||||
return member == bot;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Return the first matching alive unit from a cell search of nearby npcs
|
||||
// More responsive than "find target," but performance cost is much higher
|
||||
// Re: using the third parameter (false by default), some units are never considered
|
||||
// to be in combat (e.g., totems)
|
||||
Unit* GetFirstAliveUnitByEntry(PlayerbotAI* botAI, uint32 entry, bool requireInCombat)
|
||||
{
|
||||
auto const& npcs =
|
||||
botAI->GetAiObjectContext()->GetValue<GuidVector>("nearest npcs")->Get();
|
||||
for (auto const& npcGuid : npcs)
|
||||
{
|
||||
Unit* unit = botAI->GetUnit(npcGuid);
|
||||
if (unit && unit->IsAlive() && unit->GetEntry() == entry)
|
||||
{
|
||||
if (!requireInCombat || unit->IsInCombat())
|
||||
return unit;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Return the nearest alive player (human or bot) within the specified radius
|
||||
Unit* GetNearestPlayerInRadius(Player* bot, float radius)
|
||||
{
|
||||
Unit* nearestPlayer = nullptr;
|
||||
float nearestDistance = radius;
|
||||
|
||||
if (Group* group = bot->GetGroup())
|
||||
{
|
||||
for (GroupReference* ref = group->GetFirstMember(); ref != nullptr; ref = ref->next())
|
||||
{
|
||||
Player* member = ref->GetSource();
|
||||
if (!member || !member->IsAlive() || member == bot)
|
||||
continue;
|
||||
|
||||
float distance = bot->GetExactDist2d(member);
|
||||
if (distance < nearestDistance)
|
||||
{
|
||||
nearestDistance = distance;
|
||||
nearestPlayer = member;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nearestPlayer;
|
||||
}
|
||||
21
src/Ai/Raid/RaidBossHelpers.h
Normal file
21
src/Ai/Raid/RaidBossHelpers.h
Normal file
@@ -0,0 +1,21 @@
|
||||
#ifndef _PLAYERBOT_RAIDBOSSHELPERS_H_
|
||||
#define _PLAYERBOT_RAIDBOSSHELPERS_H_
|
||||
|
||||
#include "AiObject.h"
|
||||
#include "Unit.h"
|
||||
|
||||
void MarkTargetWithIcon(Player* bot, Unit* target, uint8 iconId);
|
||||
void MarkTargetWithSkull(Player* bot, Unit* target);
|
||||
void MarkTargetWithSquare(Player* bot, Unit* target);
|
||||
void MarkTargetWithStar(Player* bot, Unit* target);
|
||||
void MarkTargetWithCircle(Player* bot, Unit* target);
|
||||
void MarkTargetWithDiamond(Player* bot, Unit* target);
|
||||
void MarkTargetWithTriangle(Player* bot, Unit* target);
|
||||
void MarkTargetWithCross(Player* bot, Unit* target);
|
||||
void MarkTargetWithMoon(Player* bot, Unit* target);
|
||||
void SetRtiTarget(PlayerbotAI* botAI, const std::string& rtiName, Unit* target);
|
||||
bool IsMechanicTrackerBot(PlayerbotAI* botAI, Player* bot, uint32 mapId, Player* exclude = nullptr);
|
||||
Unit* GetFirstAliveUnitByEntry(PlayerbotAI* botAI, uint32 entry, bool requireInCombat = false);
|
||||
Unit* GetNearestPlayerInRadius(Player* bot, float radius);
|
||||
|
||||
#endif
|
||||
@@ -29,7 +29,7 @@ public:
|
||||
creators["wotlk-os"] = &RaidStrategyContext::wotlk_os;
|
||||
creators["wotlk-eoe"] = &RaidStrategyContext::wotlk_eoe;
|
||||
creators["voa"] = &RaidStrategyContext::voa;
|
||||
creators["uld"] = &RaidStrategyContext::uld;
|
||||
creators["ulduar"] = &RaidStrategyContext::ulduar;
|
||||
creators["onyxia"] = &RaidStrategyContext::onyxia;
|
||||
creators["icc"] = &RaidStrategyContext::icc;
|
||||
}
|
||||
@@ -45,7 +45,7 @@ private:
|
||||
static Strategy* wotlk_eoe(PlayerbotAI* botAI) { return new RaidEoEStrategy(botAI); }
|
||||
static Strategy* voa(PlayerbotAI* botAI) { return new RaidVoAStrategy(botAI); }
|
||||
static Strategy* onyxia(PlayerbotAI* botAI) { return new RaidOnyxiaStrategy(botAI); }
|
||||
static Strategy* uld(PlayerbotAI* botAI) { return new RaidUlduarStrategy(botAI); }
|
||||
static Strategy* ulduar(PlayerbotAI* botAI) { return new RaidUlduarStrategy(botAI); }
|
||||
static Strategy* icc(PlayerbotAI* botAI) { return new RaidIccStrategy(botAI); }
|
||||
};
|
||||
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
#include "GameObject.h"
|
||||
#include "Group.h"
|
||||
#include "LastMovementValue.h"
|
||||
#include "ObjectDefines.h"
|
||||
#include "ObjectGuid.h"
|
||||
#include "PlayerbotAI.h"
|
||||
#include "PlayerbotAIConfig.h"
|
||||
@@ -19,11 +18,9 @@
|
||||
#include "Position.h"
|
||||
#include "RaidUlduarBossHelper.h"
|
||||
#include "RaidUlduarScripts.h"
|
||||
#include "RaidUlduarStrategy.h"
|
||||
#include "RtiValue.h"
|
||||
#include "ScriptedCreature.h"
|
||||
#include "ServerFacade.h"
|
||||
#include "SharedDefines.h"
|
||||
#include "Unit.h"
|
||||
#include "Vehicle.h"
|
||||
#include <RtiTargetValue.h>
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
#include "RaidUlduarMultipliers.h"
|
||||
|
||||
#include "ChooseTargetActions.h"
|
||||
#include "DKActions.h"
|
||||
#include "DruidActions.h"
|
||||
#include "DruidBearActions.h"
|
||||
#include "FollowActions.h"
|
||||
#include "GenericActions.h"
|
||||
#include "GenericSpellActions.h"
|
||||
#include "HunterActions.h"
|
||||
#include "MageActions.h"
|
||||
#include "MovementActions.h"
|
||||
#include "PaladinActions.h"
|
||||
#include "PriestActions.h"
|
||||
#include "RaidUlduarActions.h"
|
||||
#include "ReachTargetActions.h"
|
||||
#include "RogueActions.h"
|
||||
#include "ScriptedCreature.h"
|
||||
#include "ShamanActions.h"
|
||||
#include "UseMeetingStoneAction.h"
|
||||
#include "WarriorActions.h"
|
||||
|
||||
float FlameLeviathanMultiplier::GetValue(Action* action)
|
||||
{
|
||||
// if (dynamic_cast<FleeAction*>(action))
|
||||
// return 0.0f;
|
||||
return 1.0f;
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
|
||||
#ifndef _PLAYERRBOT_RAIDULDUARMULTIPLIERS_H_
|
||||
#define _PLAYERRBOT_RAIDULDUARMULTIPLIERS_H_
|
||||
|
||||
#include "Multiplier.h"
|
||||
#include "Ai/Raid/Ulduar/RaidUlduarBossHelper.h"
|
||||
|
||||
class FlameLeviathanMultiplier : public Multiplier
|
||||
{
|
||||
public:
|
||||
FlameLeviathanMultiplier(PlayerbotAI* ai) : Multiplier(ai, "flame leviathan") {}
|
||||
|
||||
public:
|
||||
virtual float GetValue(Action* action);
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,169 +0,0 @@
|
||||
#ifndef _PLAYERBOT_RAIDULDUARBOSSHELPER_H
|
||||
#define _PLAYERBOT_RAIDULDUARBOSSHELPER_H
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <cmath>
|
||||
#include <ctime>
|
||||
#include <limits>
|
||||
|
||||
#include "AiObject.h"
|
||||
#include "AiObjectContext.h"
|
||||
#include "EventMap.h"
|
||||
#include "Log.h"
|
||||
#include "ObjectGuid.h"
|
||||
#include "Player.h"
|
||||
#include "PlayerbotAI.h"
|
||||
#include "Playerbots.h"
|
||||
#include "ScriptedCreature.h"
|
||||
#include "SharedDefines.h"
|
||||
|
||||
const uint32 ULDUAR_MAP_ID = 603;
|
||||
|
||||
class RazorscaleBossHelper : public AiObject
|
||||
{
|
||||
public:
|
||||
// Enums and constants specific to Razorscale
|
||||
enum RazorscaleUnits : uint32
|
||||
{
|
||||
UNIT_RAZORSCALE = 33186,
|
||||
UNIT_DARK_RUNE_SENTINEL = 33846,
|
||||
UNIT_DARK_RUNE_WATCHER = 33453,
|
||||
UNIT_DARK_RUNE_GUARDIAN = 33388,
|
||||
UNIT_DEVOURING_FLAME = 34188,
|
||||
};
|
||||
|
||||
enum RazorscaleGameObjects : uint32
|
||||
{
|
||||
GO_RAZORSCALE_HARPOON_1 = 194519,
|
||||
GO_RAZORSCALE_HARPOON_2 = 194541,
|
||||
GO_RAZORSCALE_HARPOON_3 = 194542,
|
||||
GO_RAZORSCALE_HARPOON_4 = 194543,
|
||||
};
|
||||
|
||||
enum RazorscaleSpells : uint32
|
||||
{
|
||||
SPELL_CHAIN_1 = 49679,
|
||||
SPELL_CHAIN_2 = 49682,
|
||||
SPELL_CHAIN_3 = 49683,
|
||||
SPELL_CHAIN_4 = 49684,
|
||||
SPELL_SENTINEL_WHIRLWIND = 63806,
|
||||
SPELL_STUN_AURA = 62794,
|
||||
SPELL_FUSEARMOR = 64771
|
||||
};
|
||||
|
||||
static constexpr uint32 FUSEARMOR_THRESHOLD = 2;
|
||||
|
||||
// Constants for arena parameters
|
||||
static constexpr float RAZORSCALE_FLYING_Z_THRESHOLD = 440.0f;
|
||||
static constexpr float RAZORSCALE_ARENA_CENTER_X = 587.54f;
|
||||
static constexpr float RAZORSCALE_ARENA_CENTER_Y = -175.04f;
|
||||
static constexpr float RAZORSCALE_ARENA_RADIUS = 30.0f;
|
||||
|
||||
// Harpoon cooldown (seconds)
|
||||
static constexpr time_t HARPOON_COOLDOWN_DURATION = 5;
|
||||
|
||||
// Structure for harpoon data
|
||||
struct HarpoonData
|
||||
{
|
||||
uint32 gameObjectEntry;
|
||||
uint32 chainSpellId;
|
||||
};
|
||||
|
||||
explicit RazorscaleBossHelper(PlayerbotAI* botAI)
|
||||
: AiObject(botAI), _boss(nullptr) {}
|
||||
|
||||
bool UpdateBossAI();
|
||||
Unit* GetBoss() const;
|
||||
|
||||
bool IsGroundPhase() const;
|
||||
bool IsFlyingPhase() const;
|
||||
|
||||
bool IsHarpoonFired(uint32 chainSpellId) const;
|
||||
static bool IsHarpoonReady(GameObject* harpoonGO);
|
||||
static void SetHarpoonOnCooldown(GameObject* harpoonGO);
|
||||
GameObject* FindNearestHarpoon(float x, float y, float z) const;
|
||||
|
||||
static const std::vector<HarpoonData>& GetHarpoonData();
|
||||
|
||||
void AssignRolesBasedOnHealth();
|
||||
bool AreRolesAssigned() const;
|
||||
bool CanSwapRoles() const;
|
||||
|
||||
private:
|
||||
Unit* _boss;
|
||||
|
||||
// A map to track the last role swap *per bot* by their GUID
|
||||
static std::unordered_map<ObjectGuid, std::time_t> _lastRoleSwapTime;
|
||||
|
||||
// The cooldown that applies to every bot
|
||||
static const std::time_t _roleSwapCooldown = 10;
|
||||
|
||||
static std::unordered_map<ObjectGuid, time_t> _harpoonCooldowns;
|
||||
};
|
||||
|
||||
// template <class BossAiType>
|
||||
// class GenericBossHelper : public AiObject
|
||||
// {
|
||||
// public:
|
||||
// GenericBossHelper(PlayerbotAI* botAI, std::string name) : AiObject(botAI), _name(name) {}
|
||||
// virtual bool UpdateBossAI()
|
||||
// {
|
||||
// if (!bot->IsInCombat())
|
||||
// {
|
||||
// _unit = nullptr;
|
||||
// }
|
||||
// if (_unit && (!_unit->IsInWorld() || !_unit->IsAlive()))
|
||||
// {
|
||||
// _unit = nullptr;
|
||||
// }
|
||||
// if (!_unit)
|
||||
// {
|
||||
// _unit = AI_VALUE2(Unit*, "find target", _name);
|
||||
// if (!_unit)
|
||||
// {
|
||||
// return false;
|
||||
// }
|
||||
// _target = _unit->ToCreature();
|
||||
// if (!_target)
|
||||
// {
|
||||
// return false;
|
||||
// }
|
||||
// _ai = dynamic_cast<BossAiType*>(_target->GetAI());
|
||||
// if (!_ai)
|
||||
// {
|
||||
// return false;
|
||||
// }
|
||||
// _event_map = &_ai->events;
|
||||
// if (!_event_map)
|
||||
// {
|
||||
// return false;
|
||||
// }
|
||||
// }
|
||||
// if (!_event_map)
|
||||
// {
|
||||
// return false;
|
||||
// }
|
||||
// _timer = _event_map->GetTimer();
|
||||
// return true;
|
||||
// }
|
||||
// virtual void Reset()
|
||||
// {
|
||||
// _unit = nullptr;
|
||||
// _target = nullptr;
|
||||
// _ai = nullptr;
|
||||
// _event_map = nullptr;
|
||||
// _timer = 0;
|
||||
// }
|
||||
|
||||
// protected:
|
||||
// std::string _name;
|
||||
// Unit* _unit = nullptr;
|
||||
// Creature* _target = nullptr;
|
||||
// BossAiType* _ai = nullptr;
|
||||
// EventMap* _event_map = nullptr;
|
||||
// uint32 _timer = 0;
|
||||
// };
|
||||
|
||||
#endif
|
||||
@@ -1,7 +1,5 @@
|
||||
#include "RaidUlduarStrategy.h"
|
||||
|
||||
#include "RaidUlduarMultipliers.h"
|
||||
|
||||
void RaidUlduarStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
//
|
||||
@@ -316,8 +314,3 @@ void RaidUlduarStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
"yogg-saron phase 3 positioning trigger",
|
||||
{ NextAction("yogg-saron phase 3 positioning action", ACTION_RAID) }));
|
||||
}
|
||||
|
||||
void RaidUlduarStrategy::InitMultipliers(std::vector<Multiplier*>& multipliers)
|
||||
{
|
||||
multipliers.push_back(new FlameLeviathanMultiplier(botAI));
|
||||
}
|
||||
|
||||
@@ -3,16 +3,14 @@
|
||||
#define _PLAYERBOT_RAIDULDUARSTRATEGY_H
|
||||
|
||||
#include "AiObjectContext.h"
|
||||
#include "Multiplier.h"
|
||||
#include "Strategy.h"
|
||||
|
||||
class RaidUlduarStrategy : public Strategy
|
||||
{
|
||||
public:
|
||||
RaidUlduarStrategy(PlayerbotAI* ai) : Strategy(ai) {}
|
||||
virtual std::string const getName() override { return "uld"; }
|
||||
virtual std::string const getName() override { return "ulduar"; }
|
||||
virtual void InitTriggers(std::vector<TriggerNode*>& triggers) override;
|
||||
virtual void InitMultipliers(std::vector<Multiplier*>& multipliers) override;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1634,7 +1634,7 @@ bool VezaxShadowCrashTrigger::IsActive()
|
||||
return false;
|
||||
}
|
||||
|
||||
return botAI->HasAura(SPELL_SHADOW_CRASH, bot);
|
||||
return botAI->HasAura(SPELL_VEZAX_SHADOW_CRASH, bot);
|
||||
}
|
||||
|
||||
bool VezaxMarkOfTheFacelessTrigger::IsActive()
|
||||
|
||||
@@ -3,187 +3,9 @@
|
||||
|
||||
#include "EventMap.h"
|
||||
#include "GenericTriggers.h"
|
||||
#include "PlayerbotAIConfig.h"
|
||||
#include "RaidUlduarBossHelper.h"
|
||||
#include "Trigger.h"
|
||||
|
||||
enum UlduarIDs
|
||||
{
|
||||
// Iron Assembly
|
||||
SPELL_LIGHTNING_TENDRILS_10_MAN = 61887,
|
||||
SPELL_LIGHTNING_TENDRILS_25_MAN = 63486,
|
||||
SPELL_OVERLOAD_10_MAN = 61869,
|
||||
SPELL_OVERLOAD_25_MAN = 63481,
|
||||
SPELL_OVERLOAD_10_MAN_2 = 63485,
|
||||
SPELL_OVERLOAD_25_MAN_2 = 61886,
|
||||
SPELL_RUNE_OF_POWER = 64320,
|
||||
|
||||
// Kologarn
|
||||
NPC_RIGHT_ARM = 32934,
|
||||
NPC_RUBBLE = 33768,
|
||||
SPELL_CRUNCH_ARMOR = 64002,
|
||||
|
||||
SPELL_FOCUSED_EYEBEAM_10_2 = 63346,
|
||||
SPELL_FOCUSED_EYEBEAM_10 = 63347,
|
||||
SPELL_FOCUSED_EYEBEAM_25_2 = 63976,
|
||||
SPELL_FOCUSED_EYEBEAM_25 = 63977,
|
||||
|
||||
// Hodir
|
||||
NPC_SNOWPACKED_ICICLE = 33174,
|
||||
NPC_TOASTY_FIRE = 33342,
|
||||
SPELL_FLASH_FREEZE = 61968,
|
||||
SPELL_BITING_COLD_PLAYER_AURA = 62039,
|
||||
|
||||
// Freya
|
||||
NPC_SNAPLASHER = 32916,
|
||||
NPC_STORM_LASHER = 32919,
|
||||
NPC_DETONATING_LASHER = 32918,
|
||||
NPC_ANCIENT_WATER_SPIRIT = 33202,
|
||||
NPC_ANCIENT_CONSERVATOR = 33203,
|
||||
NPC_HEALTHY_SPORE = 33215,
|
||||
NPC_EONARS_GIFT = 33228,
|
||||
GOBJECT_NATURE_BOMB = 194902,
|
||||
|
||||
// Thorim
|
||||
NPC_DARK_RUNE_ACOLYTE_I = 32886,
|
||||
NPC_CAPTURED_MERCENARY_SOLDIER_ALLY = 32885,
|
||||
NPC_CAPTURED_MERCENARY_SOLDIER_HORDE = 32883,
|
||||
NPC_CAPTURED_MERCENARY_CAPTAIN_ALLY = 32908,
|
||||
NPC_CAPTURED_MERCENARY_CAPTAIN_HORDE = 32907,
|
||||
NPC_JORMUNGAR_BEHEMOT = 32882,
|
||||
NPC_DARK_RUNE_WARBRINGER = 32877,
|
||||
NPC_DARK_RUNE_EVOKER = 32878,
|
||||
NPC_DARK_RUNE_CHAMPION = 32876,
|
||||
NPC_DARK_RUNE_COMMONER = 32904,
|
||||
NPC_IRON_RING_GUARD = 32874,
|
||||
NPC_RUNIC_COLOSSUS = 32872,
|
||||
NPC_ANCIENT_RUNE_GIANT = 32873,
|
||||
NPC_DARK_RUNE_ACOLYTE_G = 33110,
|
||||
NPC_IRON_HONOR_GUARD = 32875,
|
||||
SPELL_UNBALANCING_STRIKE = 62130,
|
||||
|
||||
// Mimiron
|
||||
NPC_LEVIATHAN_MKII = 33432,
|
||||
NPC_VX001 = 33651,
|
||||
NPC_AERIAL_COMMAND_UNIT = 33670,
|
||||
NPC_BOMB_BOT = 33836,
|
||||
NPC_ROCKET_STRIKE_N = 34047,
|
||||
NPC_ASSAULT_BOT = 34057,
|
||||
NPC_PROXIMITY_MINE = 34362,
|
||||
SPELL_P3WX2_LASER_BARRAGE_1 = 63293,
|
||||
SPELL_P3WX2_LASER_BARRAGE_2 = 63297,
|
||||
SPELL_SPINNING_UP = 63414,
|
||||
SPELL_SHOCK_BLAST = 63631,
|
||||
SPELL_P3WX2_LASER_BARRAGE_3 = 64042,
|
||||
SPELL_P3WX2_LASER_BARRAGE_AURA_1 = 63274,
|
||||
SPELL_P3WX2_LASER_BARRAGE_AURA_2 = 63300,
|
||||
|
||||
// General Vezax
|
||||
SPELL_MARK_OF_THE_FACELESS = 63276,
|
||||
SPELL_SHADOW_CRASH = 63277,
|
||||
|
||||
// Yogg-Saron
|
||||
ACTION_ILLUSION_DRAGONS = 1,
|
||||
ACTION_ILLUSION_ICECROWN = 2,
|
||||
ACTION_ILLUSION_STORMWIND = 3,
|
||||
NPC_GUARDIAN_OF_YS = 33136,
|
||||
NPC_YOGG_SARON = 33288,
|
||||
NPC_OMINOUS_CLOUD = 33292,
|
||||
NPC_RUBY_CONSORT = 33716,
|
||||
NPC_AZURE_CONSORT = 33717,
|
||||
NPC_BRONZE_CONSORT = 33718,
|
||||
NPC_EMERALD_CONSORT = 33719,
|
||||
NPC_OBSIDIAN_CONSORT = 33720,
|
||||
NPC_ALEXTRASZA = 33536,
|
||||
NPC_MALYGOS_ILLUSION = 33535,
|
||||
NPC_NELTHARION = 33523,
|
||||
NPC_YSERA = 33495,
|
||||
GO_DRAGON_SOUL = 194462,
|
||||
NPC_SARA_PHASE_1 = 33134,
|
||||
NPC_LICH_KING_ILLUSION = 33441,
|
||||
NPC_IMMOLATED_CHAMPION = 33442,
|
||||
NPC_SUIT_OF_ARMOR = 33433,
|
||||
NPC_GARONA = 33436,
|
||||
NPC_KING_LLANE = 33437,
|
||||
NPC_DEATHSWORN_ZEALOT = 33567,
|
||||
NPC_INFLUENCE_TENTACLE = 33943,
|
||||
NPC_DEATH_ORB = 33882,
|
||||
NPC_BRAIN = 33890,
|
||||
NPC_CRUSHER_TENTACLE = 33966,
|
||||
NPC_CONSTRICTOR_TENTACLE = 33983,
|
||||
NPC_CORRUPTOR_TENTACLE = 33985,
|
||||
NPC_IMMORTAL_GUARDIAN = 33988,
|
||||
NPC_LAUGHING_SKULL = 33990,
|
||||
NPC_SANITY_WELL = 33991,
|
||||
NPC_DESCEND_INTO_MADNESS = 34072,
|
||||
NPC_MARKED_IMMORTAL_GUARDIAN = 36064,
|
||||
SPELL_SANITY = 63050,
|
||||
SPELL_BRAIN_LINK = 63802,
|
||||
SPELL_MALADY_OF_THE_MIND = 63830,
|
||||
SPELL_SHADOW_BARRIER = 63894,
|
||||
SPELL_TELEPORT_TO_CHAMBER = 63997,
|
||||
SPELL_TELEPORT_TO_ICECROWN = 63998,
|
||||
SPELL_TELEPORT_TO_STORMWIND = 63989,
|
||||
SPELL_TELEPORT_BACK = 63992,
|
||||
SPELL_CANCEL_ILLUSION_AURA = 63993,
|
||||
SPELL_INDUCE_MADNESS = 64059,
|
||||
SPELL_LUNATIC_GAZE_YS = 64163,
|
||||
GO_FLEE_TO_THE_SURFACE_PORTAL = 194625,
|
||||
|
||||
// Buffs
|
||||
SPELL_FROST_TRAP = 13809
|
||||
};
|
||||
|
||||
const float ULDUAR_KOLOGARN_AXIS_Z_PATHING_ISSUE_DETECT = 420.0f;
|
||||
const float ULDUAR_KOLOGARN_EYEBEAM_RADIUS = 3.0f;
|
||||
const float ULDUAR_THORIM_AXIS_Z_FLOOR_THRESHOLD = 429.6094f;
|
||||
const float ULDUAR_THORIM_AXIS_Z_PATHING_ISSUE_DETECT = 410.0f;
|
||||
const float ULDUAR_AURIAYA_AXIS_Z_PATHING_ISSUE_DETECT = 410.0f;
|
||||
const float ULDUAR_YOGG_SARON_BOSS_ROOM_AXIS_Z_PATHING_ISSUE_DETECT = 300.0f;
|
||||
const float ULDUAR_YOGG_SARON_BRAIN_ROOM_AXIS_Z_PATHING_ISSUE_DETECT = 200.0f;
|
||||
const float ULDUAR_YOGG_SARON_STORMWIND_KEEPER_RADIUS = 150.0f;
|
||||
const float ULDUAR_YOGG_SARON_ICECROWN_CITADEL_RADIUS = 150.0f;
|
||||
const float ULDUAR_YOGG_SARON_CHAMBER_OF_ASPECTS_RADIUS = 150.0f;
|
||||
const float ULDUAR_YOGG_SARON_BRAIN_ROOM_RADIUS = 50.0f;
|
||||
|
||||
const Position ULDUAR_THORIM_NEAR_ARENA_CENTER = Position(2134.9854f, -263.11853f, 419.8465f);
|
||||
const Position ULDUAR_THORIM_NEAR_ENTRANCE_POSITION = Position(2172.4355f, -258.27957f, 418.47162f);
|
||||
const Position ULDUAR_THORIM_GAUNTLET_LEFT_SIDE_6_YARDS_1 = Position(2237.6187f, -265.08844f, 412.17548f);
|
||||
const Position ULDUAR_THORIM_GAUNTLET_LEFT_SIDE_6_YARDS_2 = Position(2237.2498f, -275.81122f, 412.17548f);
|
||||
const Position ULDUAR_THORIM_GAUNTLET_LEFT_SIDE_5_YARDS_1 = Position(2236.895f, -294.62448f, 412.1348f);
|
||||
const Position ULDUAR_THORIM_GAUNTLET_LEFT_SIDE_10_YARDS_1 = Position(2242.1162f, -310.15308f, 412.1348f);
|
||||
const Position ULDUAR_THORIM_GAUNTLET_LEFT_SIDE_10_YARDS_2 = Position(2242.018f, -318.66003f, 412.1348f);
|
||||
const Position ULDUAR_THORIM_GAUNTLET_LEFT_SIDE_10_YARDS_3 = Position(2242.1904f, -329.0533f, 412.1348f);
|
||||
const Position ULDUAR_THORIM_GAUNTLET_RIGHT_SIDE_6_YARDS_1 = Position(2219.5417f, -264.77167f, 412.17548f);
|
||||
const Position ULDUAR_THORIM_GAUNTLET_RIGHT_SIDE_6_YARDS_2 = Position(2217.446f, -275.85248f, 412.17548f);
|
||||
const Position ULDUAR_THORIM_GAUNTLET_RIGHT_SIDE_5_YARDS_1 = Position(2217.8877f, -295.01193f, 412.13434f);
|
||||
const Position ULDUAR_THORIM_GAUNTLET_RIGHT_SIDE_10_YARDS_1 = Position(2212.193f, -307.44992f, 412.1348f);
|
||||
const Position ULDUAR_THORIM_GAUNTLET_RIGHT_SIDE_10_YARDS_2 = Position(2212.1353f, -318.20795f, 412.1348f);
|
||||
const Position ULDUAR_THORIM_GAUNTLET_RIGHT_SIDE_10_YARDS_3 = Position(2212.1956f, -328.0144f, 412.1348f);
|
||||
const Position ULDUAR_THORIM_JUMP_END_POINT = Position(2137.8818f, -278.18942f, 419.66653f);
|
||||
const Position ULDUAR_THORIM_PHASE2_TANK_SPOT = Position(2134.8572f, -287.0291f, 419.4935f);
|
||||
const Position ULDUAR_THORIM_PHASE2_RANGE1_SPOT = Position(2112.8752f, -267.69305f, 419.52814f);
|
||||
const Position ULDUAR_THORIM_PHASE2_RANGE2_SPOT = Position(2134.1296f, -257.3316f, 419.8462f);
|
||||
const Position ULDUAR_THORIM_PHASE2_RANGE3_SPOT = Position(2156.798f, -267.57434f, 419.52722f);
|
||||
const Position ULDUAR_MIMIRON_PHASE2_SIDE1RANGE_SPOT = Position(2753.708f, 2583.9617f, 364.31357f);
|
||||
const Position ULDUAR_MIMIRON_PHASE2_SIDE1MELEE_SPOT = Position(2746.9792f, 2573.6716f, 364.31357f);
|
||||
const Position ULDUAR_MIMIRON_PHASE2_SIDE2RANGE_SPOT = Position(2727.7224f, 2569.527f, 364.31357f);
|
||||
const Position ULDUAR_MIMIRON_PHASE2_SIDE2MELEE_SPOT = Position(2739.4746f, 2569.4106f, 364.31357f);
|
||||
const Position ULDUAR_MIMIRON_PHASE2_SIDE3RANGE_SPOT = Position(2754.1294f, 2553.9954f, 364.31357f);
|
||||
const Position ULDUAR_MIMIRON_PHASE2_SIDE3MELEE_SPOT = Position(2746.8513f, 2565.4263f, 364.31357f);
|
||||
const Position ULDUAR_MIMIRON_PHASE4_TANK_SPOT = Position(2744.5754f, 2570.8657f, 364.3138f);
|
||||
const Position ULDUAR_VEZAX_MARK_OF_THE_FACELESS_SPOT = Position(1913.6501f, 122.93989f, 342.38083f);
|
||||
const Position ULDUAR_YOGG_SARON_MIDDLE = Position(1980.28f, -25.5868f, 329.397f);
|
||||
const Position ULDUAR_YOGG_SARON_STORMWIND_KEEPER_MIDDLE = Position(1927.1511f, 68.507256f, 242.37657f);
|
||||
const Position ULDUAR_YOGG_SARON_ICECROWN_CITADEL_MIDDLE = Position(1925.6553f, -121.59296f, 239.98965f);
|
||||
const Position ULDUAR_YOGG_SARON_CHAMBER_OF_ASPECTS_MIDDLE = Position(2104.5667f, -25.509348f, 242.64679f);
|
||||
const Position ULDUAR_YOGG_SARON_BRAIN_ROOM_MIDDLE = Position(1980.1971f, -27.854689f, 236.06789f);
|
||||
const Position ULDUAR_YOGG_SARON_STORMWIND_KEEPER_ENTRANCE = Position(1954.06f, 21.66f, 239.71f);
|
||||
const Position ULDUAR_YOGG_SARON_ICECROWN_CITADEL_ENTRANCE = Position(1950.11f, -79.284f, 239.98982f);
|
||||
const Position ULDUAR_YOGG_SARON_CHAMBER_OF_ASPECTS_ENTRANCE = Position(2048.63f, -25.5f, 239.72f);
|
||||
const Position ULDUAR_YOGG_SARON_PHASE_3_MELEE_SPOT = Position(1998.5377f, -22.90317f, 324.8895f);
|
||||
const Position ULDUAR_YOGG_SARON_PHASE_3_RANGED_SPOT = Position(2018.7628f, -18.896868f, 327.07245f);
|
||||
|
||||
//
|
||||
// Flame Levi
|
||||
//
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#include "ChatHelper.h"
|
||||
#include "RaidUlduarBossHelper.h"
|
||||
#include "ObjectAccessor.h"
|
||||
#include "GameObject.h"
|
||||
@@ -9,6 +8,44 @@
|
||||
#include "Playerbots.h"
|
||||
#include "World.h"
|
||||
|
||||
const Position ULDUAR_THORIM_NEAR_ARENA_CENTER = Position(2134.9854f, -263.11853f, 419.8465f);
|
||||
const Position ULDUAR_THORIM_NEAR_ENTRANCE_POSITION = Position(2172.4355f, -258.27957f, 418.47162f);
|
||||
const Position ULDUAR_THORIM_GAUNTLET_LEFT_SIDE_6_YARDS_1 = Position(2237.6187f, -265.08844f, 412.17548f);
|
||||
const Position ULDUAR_THORIM_GAUNTLET_LEFT_SIDE_6_YARDS_2 = Position(2237.2498f, -275.81122f, 412.17548f);
|
||||
const Position ULDUAR_THORIM_GAUNTLET_LEFT_SIDE_5_YARDS_1 = Position(2236.895f, -294.62448f, 412.1348f);
|
||||
const Position ULDUAR_THORIM_GAUNTLET_LEFT_SIDE_10_YARDS_1 = Position(2242.1162f, -310.15308f, 412.1348f);
|
||||
const Position ULDUAR_THORIM_GAUNTLET_LEFT_SIDE_10_YARDS_2 = Position(2242.018f, -318.66003f, 412.1348f);
|
||||
const Position ULDUAR_THORIM_GAUNTLET_LEFT_SIDE_10_YARDS_3 = Position(2242.1904f, -329.0533f, 412.1348f);
|
||||
const Position ULDUAR_THORIM_GAUNTLET_RIGHT_SIDE_6_YARDS_1 = Position(2219.5417f, -264.77167f, 412.17548f);
|
||||
const Position ULDUAR_THORIM_GAUNTLET_RIGHT_SIDE_6_YARDS_2 = Position(2217.446f, -275.85248f, 412.17548f);
|
||||
const Position ULDUAR_THORIM_GAUNTLET_RIGHT_SIDE_5_YARDS_1 = Position(2217.8877f, -295.01193f, 412.13434f);
|
||||
const Position ULDUAR_THORIM_GAUNTLET_RIGHT_SIDE_10_YARDS_1 = Position(2212.193f, -307.44992f, 412.1348f);
|
||||
const Position ULDUAR_THORIM_GAUNTLET_RIGHT_SIDE_10_YARDS_2 = Position(2212.1353f, -318.20795f, 412.1348f);
|
||||
const Position ULDUAR_THORIM_GAUNTLET_RIGHT_SIDE_10_YARDS_3 = Position(2212.1956f, -328.0144f, 412.1348f);
|
||||
const Position ULDUAR_THORIM_JUMP_END_POINT = Position(2137.8818f, -278.18942f, 419.66653f);
|
||||
const Position ULDUAR_THORIM_PHASE2_TANK_SPOT = Position(2134.8572f, -287.0291f, 419.4935f);
|
||||
const Position ULDUAR_THORIM_PHASE2_RANGE1_SPOT = Position(2112.8752f, -267.69305f, 419.52814f);
|
||||
const Position ULDUAR_THORIM_PHASE2_RANGE2_SPOT = Position(2134.1296f, -257.3316f, 419.8462f);
|
||||
const Position ULDUAR_THORIM_PHASE2_RANGE3_SPOT = Position(2156.798f, -267.57434f, 419.52722f);
|
||||
const Position ULDUAR_MIMIRON_PHASE2_SIDE1RANGE_SPOT = Position(2753.708f, 2583.9617f, 364.31357f);
|
||||
const Position ULDUAR_MIMIRON_PHASE2_SIDE1MELEE_SPOT = Position(2746.9792f, 2573.6716f, 364.31357f);
|
||||
const Position ULDUAR_MIMIRON_PHASE2_SIDE2RANGE_SPOT = Position(2727.7224f, 2569.527f, 364.31357f);
|
||||
const Position ULDUAR_MIMIRON_PHASE2_SIDE2MELEE_SPOT = Position(2739.4746f, 2569.4106f, 364.31357f);
|
||||
const Position ULDUAR_MIMIRON_PHASE2_SIDE3RANGE_SPOT = Position(2754.1294f, 2553.9954f, 364.31357f);
|
||||
const Position ULDUAR_MIMIRON_PHASE2_SIDE3MELEE_SPOT = Position(2746.8513f, 2565.4263f, 364.31357f);
|
||||
const Position ULDUAR_MIMIRON_PHASE4_TANK_SPOT = Position(2744.5754f, 2570.8657f, 364.3138f);
|
||||
const Position ULDUAR_VEZAX_MARK_OF_THE_FACELESS_SPOT = Position(1913.6501f, 122.93989f, 342.38083f);
|
||||
const Position ULDUAR_YOGG_SARON_MIDDLE = Position(1980.28f, -25.5868f, 329.397f);
|
||||
const Position ULDUAR_YOGG_SARON_STORMWIND_KEEPER_MIDDLE = Position(1927.1511f, 68.507256f, 242.37657f);
|
||||
const Position ULDUAR_YOGG_SARON_ICECROWN_CITADEL_MIDDLE = Position(1925.6553f, -121.59296f, 239.98965f);
|
||||
const Position ULDUAR_YOGG_SARON_CHAMBER_OF_ASPECTS_MIDDLE = Position(2104.5667f, -25.509348f, 242.64679f);
|
||||
const Position ULDUAR_YOGG_SARON_BRAIN_ROOM_MIDDLE = Position(1980.1971f, -27.854689f, 236.06789f);
|
||||
const Position ULDUAR_YOGG_SARON_STORMWIND_KEEPER_ENTRANCE = Position(1954.06f, 21.66f, 239.71f);
|
||||
const Position ULDUAR_YOGG_SARON_ICECROWN_CITADEL_ENTRANCE = Position(1950.11f, -79.284f, 239.98982f);
|
||||
const Position ULDUAR_YOGG_SARON_CHAMBER_OF_ASPECTS_ENTRANCE = Position(2048.63f, -25.5f, 239.72f);
|
||||
const Position ULDUAR_YOGG_SARON_PHASE_3_MELEE_SPOT = Position(1998.5377f, -22.90317f, 324.8895f);
|
||||
const Position ULDUAR_YOGG_SARON_PHASE_3_RANGED_SPOT = Position(2018.7628f, -18.896868f, 327.07245f);
|
||||
|
||||
// Prevent harpoon spam
|
||||
std::unordered_map<ObjectGuid, time_t> RazorscaleBossHelper::_harpoonCooldowns;
|
||||
// Prevent role assignment spam
|
||||
341
src/Ai/Raid/Ulduar/Util/RaidUlduarBossHelper.h
Normal file
341
src/Ai/Raid/Ulduar/Util/RaidUlduarBossHelper.h
Normal file
@@ -0,0 +1,341 @@
|
||||
#ifndef _PLAYERBOT_RAIDULDUARBOSSHELPER_H
|
||||
#define _PLAYERBOT_RAIDULDUARBOSSHELPER_H
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "AiObject.h"
|
||||
#include "AiObjectContext.h"
|
||||
#include "EventMap.h"
|
||||
#include "ObjectGuid.h"
|
||||
#include "Player.h"
|
||||
#include "PlayerbotAI.h"
|
||||
#include "Playerbots.h"
|
||||
#include "ScriptedCreature.h"
|
||||
|
||||
constexpr uint32 ULDUAR_MAP_ID = 603;
|
||||
|
||||
enum UlduarIDs
|
||||
{
|
||||
// Iron Assembly
|
||||
SPELL_LIGHTNING_TENDRILS_10_MAN = 61887,
|
||||
SPELL_LIGHTNING_TENDRILS_25_MAN = 63486,
|
||||
SPELL_OVERLOAD_10_MAN = 61869,
|
||||
SPELL_OVERLOAD_25_MAN = 63481,
|
||||
SPELL_OVERLOAD_10_MAN_2 = 63485,
|
||||
SPELL_OVERLOAD_25_MAN_2 = 61886,
|
||||
SPELL_RUNE_OF_POWER = 64320,
|
||||
|
||||
// Kologarn
|
||||
NPC_RIGHT_ARM = 32934,
|
||||
NPC_RUBBLE = 33768,
|
||||
SPELL_CRUNCH_ARMOR = 64002,
|
||||
|
||||
SPELL_FOCUSED_EYEBEAM_10_2 = 63346,
|
||||
SPELL_FOCUSED_EYEBEAM_10 = 63347,
|
||||
SPELL_FOCUSED_EYEBEAM_25_2 = 63976,
|
||||
SPELL_FOCUSED_EYEBEAM_25 = 63977,
|
||||
|
||||
// Hodir
|
||||
NPC_SNOWPACKED_ICICLE = 33174,
|
||||
NPC_TOASTY_FIRE = 33342,
|
||||
SPELL_FLASH_FREEZE = 61968,
|
||||
SPELL_BITING_COLD_PLAYER_AURA = 62039,
|
||||
|
||||
// Freya
|
||||
NPC_SNAPLASHER = 32916,
|
||||
NPC_STORM_LASHER = 32919,
|
||||
NPC_DETONATING_LASHER = 32918,
|
||||
NPC_ANCIENT_WATER_SPIRIT = 33202,
|
||||
NPC_ANCIENT_CONSERVATOR = 33203,
|
||||
NPC_HEALTHY_SPORE = 33215,
|
||||
NPC_EONARS_GIFT = 33228,
|
||||
GOBJECT_NATURE_BOMB = 194902,
|
||||
|
||||
// Thorim
|
||||
NPC_DARK_RUNE_ACOLYTE_I = 32886,
|
||||
NPC_CAPTURED_MERCENARY_SOLDIER_ALLY = 32885,
|
||||
NPC_CAPTURED_MERCENARY_SOLDIER_HORDE = 32883,
|
||||
NPC_CAPTURED_MERCENARY_CAPTAIN_ALLY = 32908,
|
||||
NPC_CAPTURED_MERCENARY_CAPTAIN_HORDE = 32907,
|
||||
NPC_JORMUNGAR_BEHEMOT = 32882,
|
||||
NPC_DARK_RUNE_WARBRINGER = 32877,
|
||||
NPC_DARK_RUNE_EVOKER = 32878,
|
||||
NPC_DARK_RUNE_CHAMPION = 32876,
|
||||
NPC_DARK_RUNE_COMMONER = 32904,
|
||||
NPC_IRON_RING_GUARD = 32874,
|
||||
NPC_RUNIC_COLOSSUS = 32872,
|
||||
NPC_ANCIENT_RUNE_GIANT = 32873,
|
||||
NPC_DARK_RUNE_ACOLYTE_G = 33110,
|
||||
NPC_IRON_HONOR_GUARD = 32875,
|
||||
SPELL_UNBALANCING_STRIKE = 62130,
|
||||
|
||||
// Mimiron
|
||||
NPC_LEVIATHAN_MKII = 33432,
|
||||
NPC_VX001 = 33651,
|
||||
NPC_AERIAL_COMMAND_UNIT = 33670,
|
||||
NPC_BOMB_BOT = 33836,
|
||||
NPC_ROCKET_STRIKE_N = 34047,
|
||||
NPC_ASSAULT_BOT = 34057,
|
||||
NPC_PROXIMITY_MINE = 34362,
|
||||
SPELL_P3WX2_LASER_BARRAGE_1 = 63293,
|
||||
SPELL_P3WX2_LASER_BARRAGE_2 = 63297,
|
||||
SPELL_SPINNING_UP = 63414,
|
||||
SPELL_SHOCK_BLAST = 63631,
|
||||
SPELL_P3WX2_LASER_BARRAGE_3 = 64042,
|
||||
SPELL_P3WX2_LASER_BARRAGE_AURA_1 = 63274,
|
||||
SPELL_P3WX2_LASER_BARRAGE_AURA_2 = 63300,
|
||||
|
||||
// General Vezax
|
||||
SPELL_MARK_OF_THE_FACELESS = 63276,
|
||||
SPELL_VEZAX_SHADOW_CRASH = 63277,
|
||||
|
||||
// Yogg-Saron
|
||||
ACTION_ILLUSION_DRAGONS = 1,
|
||||
ACTION_ILLUSION_ICECROWN = 2,
|
||||
ACTION_ILLUSION_STORMWIND = 3,
|
||||
NPC_GUARDIAN_OF_YS = 33136,
|
||||
NPC_YOGG_SARON = 33288,
|
||||
NPC_OMINOUS_CLOUD = 33292,
|
||||
NPC_RUBY_CONSORT = 33716,
|
||||
NPC_AZURE_CONSORT = 33717,
|
||||
NPC_BRONZE_CONSORT = 33718,
|
||||
NPC_EMERALD_CONSORT = 33719,
|
||||
NPC_OBSIDIAN_CONSORT = 33720,
|
||||
NPC_ALEXTRASZA = 33536,
|
||||
NPC_MALYGOS_ILLUSION = 33535,
|
||||
NPC_NELTHARION = 33523,
|
||||
NPC_YSERA = 33495,
|
||||
GO_DRAGON_SOUL = 194462,
|
||||
NPC_SARA_PHASE_1 = 33134,
|
||||
NPC_LICH_KING_ILLUSION = 33441,
|
||||
NPC_IMMOLATED_CHAMPION = 33442,
|
||||
NPC_SUIT_OF_ARMOR = 33433,
|
||||
NPC_GARONA = 33436,
|
||||
NPC_KING_LLANE = 33437,
|
||||
NPC_DEATHSWORN_ZEALOT = 33567,
|
||||
NPC_INFLUENCE_TENTACLE = 33943,
|
||||
NPC_DEATH_ORB = 33882,
|
||||
NPC_BRAIN = 33890,
|
||||
NPC_CRUSHER_TENTACLE = 33966,
|
||||
NPC_CONSTRICTOR_TENTACLE = 33983,
|
||||
NPC_CORRUPTOR_TENTACLE = 33985,
|
||||
NPC_IMMORTAL_GUARDIAN = 33988,
|
||||
NPC_LAUGHING_SKULL = 33990,
|
||||
NPC_SANITY_WELL = 33991,
|
||||
NPC_DESCEND_INTO_MADNESS = 34072,
|
||||
NPC_MARKED_IMMORTAL_GUARDIAN = 36064,
|
||||
SPELL_SANITY = 63050,
|
||||
SPELL_BRAIN_LINK = 63802,
|
||||
SPELL_MALADY_OF_THE_MIND = 63830,
|
||||
SPELL_SHADOW_BARRIER = 63894,
|
||||
SPELL_TELEPORT_TO_CHAMBER = 63997,
|
||||
SPELL_TELEPORT_TO_ICECROWN = 63998,
|
||||
SPELL_TELEPORT_TO_STORMWIND = 63989,
|
||||
SPELL_TELEPORT_BACK = 63992,
|
||||
SPELL_CANCEL_ILLUSION_AURA = 63993,
|
||||
SPELL_INDUCE_MADNESS = 64059,
|
||||
SPELL_LUNATIC_GAZE_YS = 64163,
|
||||
GO_FLEE_TO_THE_SURFACE_PORTAL = 194625,
|
||||
|
||||
// Buffs
|
||||
SPELL_FROST_TRAP = 13809
|
||||
};
|
||||
|
||||
constexpr float ULDUAR_KOLOGARN_AXIS_Z_PATHING_ISSUE_DETECT = 420.0f;
|
||||
constexpr float ULDUAR_KOLOGARN_EYEBEAM_RADIUS = 3.0f;
|
||||
constexpr float ULDUAR_THORIM_AXIS_Z_FLOOR_THRESHOLD = 429.6094f;
|
||||
constexpr float ULDUAR_THORIM_AXIS_Z_PATHING_ISSUE_DETECT = 410.0f;
|
||||
constexpr float ULDUAR_AURIAYA_AXIS_Z_PATHING_ISSUE_DETECT = 410.0f;
|
||||
constexpr float ULDUAR_YOGG_SARON_BOSS_ROOM_AXIS_Z_PATHING_ISSUE_DETECT = 300.0f;
|
||||
constexpr float ULDUAR_YOGG_SARON_BRAIN_ROOM_AXIS_Z_PATHING_ISSUE_DETECT = 200.0f;
|
||||
constexpr float ULDUAR_YOGG_SARON_STORMWIND_KEEPER_RADIUS = 150.0f;
|
||||
constexpr float ULDUAR_YOGG_SARON_ICECROWN_CITADEL_RADIUS = 150.0f;
|
||||
constexpr float ULDUAR_YOGG_SARON_CHAMBER_OF_ASPECTS_RADIUS = 150.0f;
|
||||
constexpr float ULDUAR_YOGG_SARON_BRAIN_ROOM_RADIUS = 50.0f;
|
||||
|
||||
extern const Position ULDUAR_THORIM_NEAR_ARENA_CENTER;
|
||||
extern const Position ULDUAR_THORIM_NEAR_ENTRANCE_POSITION;
|
||||
extern const Position ULDUAR_THORIM_GAUNTLET_LEFT_SIDE_6_YARDS_1;
|
||||
extern const Position ULDUAR_THORIM_GAUNTLET_LEFT_SIDE_6_YARDS_2;
|
||||
extern const Position ULDUAR_THORIM_GAUNTLET_LEFT_SIDE_5_YARDS_1;
|
||||
extern const Position ULDUAR_THORIM_GAUNTLET_LEFT_SIDE_10_YARDS_1;
|
||||
extern const Position ULDUAR_THORIM_GAUNTLET_LEFT_SIDE_10_YARDS_2;
|
||||
extern const Position ULDUAR_THORIM_GAUNTLET_LEFT_SIDE_10_YARDS_3;
|
||||
extern const Position ULDUAR_THORIM_GAUNTLET_RIGHT_SIDE_6_YARDS_1;
|
||||
extern const Position ULDUAR_THORIM_GAUNTLET_RIGHT_SIDE_6_YARDS_2;
|
||||
extern const Position ULDUAR_THORIM_GAUNTLET_RIGHT_SIDE_5_YARDS_1;
|
||||
extern const Position ULDUAR_THORIM_GAUNTLET_RIGHT_SIDE_10_YARDS_1;
|
||||
extern const Position ULDUAR_THORIM_GAUNTLET_RIGHT_SIDE_10_YARDS_2;
|
||||
extern const Position ULDUAR_THORIM_GAUNTLET_RIGHT_SIDE_10_YARDS_3;
|
||||
extern const Position ULDUAR_THORIM_JUMP_END_POINT;
|
||||
extern const Position ULDUAR_THORIM_PHASE2_TANK_SPOT;
|
||||
extern const Position ULDUAR_THORIM_PHASE2_RANGE1_SPOT;
|
||||
extern const Position ULDUAR_THORIM_PHASE2_RANGE2_SPOT;
|
||||
extern const Position ULDUAR_THORIM_PHASE2_RANGE3_SPOT;
|
||||
extern const Position ULDUAR_MIMIRON_PHASE2_SIDE1RANGE_SPOT;
|
||||
extern const Position ULDUAR_MIMIRON_PHASE2_SIDE1MELEE_SPOT;
|
||||
extern const Position ULDUAR_MIMIRON_PHASE2_SIDE2RANGE_SPOT;
|
||||
extern const Position ULDUAR_MIMIRON_PHASE2_SIDE2MELEE_SPOT;
|
||||
extern const Position ULDUAR_MIMIRON_PHASE2_SIDE3RANGE_SPOT;
|
||||
extern const Position ULDUAR_MIMIRON_PHASE2_SIDE3MELEE_SPOT;
|
||||
extern const Position ULDUAR_MIMIRON_PHASE4_TANK_SPOT;
|
||||
extern const Position ULDUAR_VEZAX_MARK_OF_THE_FACELESS_SPOT;
|
||||
extern const Position ULDUAR_YOGG_SARON_MIDDLE;
|
||||
extern const Position ULDUAR_YOGG_SARON_STORMWIND_KEEPER_MIDDLE;
|
||||
extern const Position ULDUAR_YOGG_SARON_ICECROWN_CITADEL_MIDDLE;
|
||||
extern const Position ULDUAR_YOGG_SARON_CHAMBER_OF_ASPECTS_MIDDLE;
|
||||
extern const Position ULDUAR_YOGG_SARON_BRAIN_ROOM_MIDDLE;
|
||||
extern const Position ULDUAR_YOGG_SARON_STORMWIND_KEEPER_ENTRANCE;
|
||||
extern const Position ULDUAR_YOGG_SARON_ICECROWN_CITADEL_ENTRANCE;
|
||||
extern const Position ULDUAR_YOGG_SARON_CHAMBER_OF_ASPECTS_ENTRANCE;
|
||||
extern const Position ULDUAR_YOGG_SARON_PHASE_3_MELEE_SPOT;
|
||||
extern const Position ULDUAR_YOGG_SARON_PHASE_3_RANGED_SPOT;
|
||||
|
||||
class RazorscaleBossHelper : public AiObject
|
||||
{
|
||||
public:
|
||||
// Enums and constants specific to Razorscale
|
||||
enum RazorscaleUnits : uint32
|
||||
{
|
||||
UNIT_RAZORSCALE = 33186,
|
||||
UNIT_DARK_RUNE_SENTINEL = 33846,
|
||||
UNIT_DARK_RUNE_WATCHER = 33453,
|
||||
UNIT_DARK_RUNE_GUARDIAN = 33388,
|
||||
UNIT_DEVOURING_FLAME = 34188,
|
||||
};
|
||||
|
||||
enum RazorscaleGameObjects : uint32
|
||||
{
|
||||
GO_RAZORSCALE_HARPOON_1 = 194519,
|
||||
GO_RAZORSCALE_HARPOON_2 = 194541,
|
||||
GO_RAZORSCALE_HARPOON_3 = 194542,
|
||||
GO_RAZORSCALE_HARPOON_4 = 194543,
|
||||
};
|
||||
|
||||
enum RazorscaleSpells : uint32
|
||||
{
|
||||
SPELL_CHAIN_1 = 49679,
|
||||
SPELL_CHAIN_2 = 49682,
|
||||
SPELL_CHAIN_3 = 49683,
|
||||
SPELL_CHAIN_4 = 49684,
|
||||
SPELL_SENTINEL_WHIRLWIND = 63806,
|
||||
SPELL_STUN_AURA = 62794,
|
||||
SPELL_FUSEARMOR = 64771
|
||||
};
|
||||
|
||||
static constexpr uint32 FUSEARMOR_THRESHOLD = 2;
|
||||
|
||||
// Constants for arena parameters
|
||||
static constexpr float RAZORSCALE_FLYING_Z_THRESHOLD = 440.0f;
|
||||
static constexpr float RAZORSCALE_ARENA_CENTER_X = 587.54f;
|
||||
static constexpr float RAZORSCALE_ARENA_CENTER_Y = -175.04f;
|
||||
static constexpr float RAZORSCALE_ARENA_RADIUS = 30.0f;
|
||||
|
||||
// Harpoon cooldown (seconds)
|
||||
static constexpr time_t HARPOON_COOLDOWN_DURATION = 5;
|
||||
|
||||
// Structure for harpoon data
|
||||
struct HarpoonData
|
||||
{
|
||||
uint32 gameObjectEntry;
|
||||
uint32 chainSpellId;
|
||||
};
|
||||
|
||||
explicit RazorscaleBossHelper(PlayerbotAI* botAI)
|
||||
: AiObject(botAI), _boss(nullptr) {}
|
||||
|
||||
bool UpdateBossAI();
|
||||
Unit* GetBoss() const;
|
||||
|
||||
bool IsGroundPhase() const;
|
||||
bool IsFlyingPhase() const;
|
||||
|
||||
bool IsHarpoonFired(uint32 chainSpellId) const;
|
||||
static bool IsHarpoonReady(GameObject* harpoonGO);
|
||||
static void SetHarpoonOnCooldown(GameObject* harpoonGO);
|
||||
GameObject* FindNearestHarpoon(float x, float y, float z) const;
|
||||
|
||||
static const std::vector<HarpoonData>& GetHarpoonData();
|
||||
|
||||
void AssignRolesBasedOnHealth();
|
||||
bool AreRolesAssigned() const;
|
||||
bool CanSwapRoles() const;
|
||||
|
||||
private:
|
||||
Unit* _boss;
|
||||
|
||||
// A map to track the last role swap *per bot* by their GUID
|
||||
static std::unordered_map<ObjectGuid, std::time_t> _lastRoleSwapTime;
|
||||
|
||||
// The cooldown that applies to every bot
|
||||
static const std::time_t _roleSwapCooldown = 10;
|
||||
|
||||
static std::unordered_map<ObjectGuid, time_t> _harpoonCooldowns;
|
||||
};
|
||||
|
||||
// template <class BossAiType>
|
||||
// class GenericBossHelper : public AiObject
|
||||
// {
|
||||
// public:
|
||||
// GenericBossHelper(PlayerbotAI* botAI, std::string name) : AiObject(botAI), _name(name) {}
|
||||
// virtual bool UpdateBossAI()
|
||||
// {
|
||||
// if (!bot->IsInCombat())
|
||||
// {
|
||||
// _unit = nullptr;
|
||||
// }
|
||||
// if (_unit && (!_unit->IsInWorld() || !_unit->IsAlive()))
|
||||
// {
|
||||
// _unit = nullptr;
|
||||
// }
|
||||
// if (!_unit)
|
||||
// {
|
||||
// _unit = AI_VALUE2(Unit*, "find target", _name);
|
||||
// if (!_unit)
|
||||
// {
|
||||
// return false;
|
||||
// }
|
||||
// _target = _unit->ToCreature();
|
||||
// if (!_target)
|
||||
// {
|
||||
// return false;
|
||||
// }
|
||||
// _ai = dynamic_cast<BossAiType*>(_target->GetAI());
|
||||
// if (!_ai)
|
||||
// {
|
||||
// return false;
|
||||
// }
|
||||
// _event_map = &_ai->events;
|
||||
// if (!_event_map)
|
||||
// {
|
||||
// return false;
|
||||
// }
|
||||
// }
|
||||
// if (!_event_map)
|
||||
// {
|
||||
// return false;
|
||||
// }
|
||||
// _timer = _event_map->GetTimer();
|
||||
// return true;
|
||||
// }
|
||||
// virtual void Reset()
|
||||
// {
|
||||
// _unit = nullptr;
|
||||
// _target = nullptr;
|
||||
// _ai = nullptr;
|
||||
// _event_map = nullptr;
|
||||
// _timer = 0;
|
||||
// }
|
||||
|
||||
// protected:
|
||||
// std::string _name;
|
||||
// Unit* _unit = nullptr;
|
||||
// Creature* _target = nullptr;
|
||||
// BossAiType* _ai = nullptr;
|
||||
// EventMap* _event_map = nullptr;
|
||||
// uint32 _timer = 0;
|
||||
// };
|
||||
|
||||
#endif
|
||||
@@ -7,6 +7,7 @@
|
||||
#define _PLAYERBOT_RAIDVOAACTIONCONTEXT_H
|
||||
|
||||
#include "Action.h"
|
||||
#include "BossAuraActions.h"
|
||||
#include "NamedObjectContext.h"
|
||||
#include "RaidVoAActions.h"
|
||||
#include "PlayerbotAI.h"
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#define _PLAYERBOT_RAIDVOATRIGGERCONTEXT_H
|
||||
|
||||
#include "AiObjectContext.h"
|
||||
#include "BossAuraTriggers.h"
|
||||
#include "NamedObjectContext.h"
|
||||
#include "RaidVoATriggers.h"
|
||||
|
||||
|
||||
@@ -15,8 +15,6 @@
|
||||
#include "PaladinAiObjectContext.h"
|
||||
#include "Playerbots.h"
|
||||
#include "PriestAiObjectContext.h"
|
||||
#include "RaidUlduarActionContext.h"
|
||||
#include "RaidUlduarTriggerContext.h"
|
||||
#include "RogueAiObjectContext.h"
|
||||
#include "ShamanAiObjectContext.h"
|
||||
#include "SharedValueContext.h"
|
||||
@@ -49,6 +47,8 @@
|
||||
#include "Ai/Raid/VaultOfArchavon/RaidVoATriggerContext.h"
|
||||
#include "Ai/Raid/ObsidianSanctum/RaidOsActionContext.h"
|
||||
#include "Ai/Raid/ObsidianSanctum/RaidOsTriggerContext.h"
|
||||
#include "Ai/Raid/Ulduar/RaidUlduarActionContext.h"
|
||||
#include "Ai/Raid/Ulduar/RaidUlduarTriggerContext.h"
|
||||
#include "Ai/Raid/Onyxia/RaidOnyxiaActionContext.h"
|
||||
#include "Ai/Raid/Onyxia/RaidOnyxiaTriggerContext.h"
|
||||
#include "Ai/Raid/Icecrown/RaidIccActionContext.h"
|
||||
|
||||
@@ -1585,7 +1585,7 @@ void PlayerbotAI::ApplyInstanceStrategies(uint32 mapId, bool tellMaster)
|
||||
strategyName = "wotlk-hol"; // Halls of Lightning
|
||||
break;
|
||||
case 603:
|
||||
strategyName = "uld"; // Ulduar
|
||||
strategyName = "ulduar"; // Ulduar
|
||||
break;
|
||||
case 604:
|
||||
strategyName = "wotlk-gd"; // Gundrak
|
||||
|
||||
Reference in New Issue
Block a user