mirror of
https://github.com/mod-playerbots/mod-playerbots.git
synced 2026-02-07 20:51: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 "RaidGruulsLairHelpers.h"
|
||||||
#include "CreatureAI.h"
|
#include "CreatureAI.h"
|
||||||
#include "Playerbots.h"
|
#include "Playerbots.h"
|
||||||
|
#include "RaidBossHelpers.h"
|
||||||
#include "Unit.h"
|
#include "Unit.h"
|
||||||
|
|
||||||
using namespace GruulsLairHelpers;
|
using namespace GruulsLairHelpers;
|
||||||
@@ -12,6 +13,8 @@ using namespace GruulsLairHelpers;
|
|||||||
bool HighKingMaulgarMainTankAttackMaulgarAction::Execute(Event event)
|
bool HighKingMaulgarMainTankAttackMaulgarAction::Execute(Event event)
|
||||||
{
|
{
|
||||||
Unit* maulgar = AI_VALUE2(Unit*, "find target", "high king maulgar");
|
Unit* maulgar = AI_VALUE2(Unit*, "find target", "high king maulgar");
|
||||||
|
if (!maulgar)
|
||||||
|
return false;
|
||||||
|
|
||||||
MarkTargetWithSquare(bot, maulgar);
|
MarkTargetWithSquare(bot, maulgar);
|
||||||
SetRtiTarget(botAI, "square", maulgar);
|
SetRtiTarget(botAI, "square", maulgar);
|
||||||
@@ -21,31 +24,20 @@ bool HighKingMaulgarMainTankAttackMaulgarAction::Execute(Event event)
|
|||||||
|
|
||||||
if (maulgar->GetVictim() == bot)
|
if (maulgar->GetVictim() == bot)
|
||||||
{
|
{
|
||||||
const Location& tankPosition = GruulsLairLocations::MaulgarTankPosition;
|
const Position& position = MAULGAR_TANK_POSITION;
|
||||||
const float maxDistance = 3.0f;
|
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 dX = position.GetPositionX() - bot->GetPositionX();
|
||||||
float dY = tankPosition.y - bot->GetPositionY();
|
float dY = position.GetPositionY() - bot->GetPositionY();
|
||||||
float dist = sqrt(dX * dX + dY * dY);
|
float moveX = bot->GetPositionX() + (dX / distanceToPosition) * maxDistance;
|
||||||
float moveX = bot->GetPositionX() + (dX / dist) * maxDistance;
|
float moveY = bot->GetPositionY() + (dY / distanceToPosition) * maxDistance;
|
||||||
float moveY = bot->GetPositionY() + (dY / dist) * maxDistance;
|
return MoveTo(GRUULS_LAIR_MAP_ID, moveX, moveY, position.GetPositionZ(), false, false, false, false,
|
||||||
return MoveTo(bot->GetMapId(), moveX, moveY, tankPosition.z, false, false, false, false,
|
MovementPriority::MOVEMENT_COMBAT, true, true);
|
||||||
MovementPriority::MOVEMENT_COMBAT, true, false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
return false;
|
||||||
@@ -55,6 +47,8 @@ bool HighKingMaulgarMainTankAttackMaulgarAction::Execute(Event event)
|
|||||||
bool HighKingMaulgarFirstAssistTankAttackOlmAction::Execute(Event event)
|
bool HighKingMaulgarFirstAssistTankAttackOlmAction::Execute(Event event)
|
||||||
{
|
{
|
||||||
Unit* olm = AI_VALUE2(Unit*, "find target", "olm the summoner");
|
Unit* olm = AI_VALUE2(Unit*, "find target", "olm the summoner");
|
||||||
|
if (!olm)
|
||||||
|
return false;
|
||||||
|
|
||||||
MarkTargetWithCircle(bot, olm);
|
MarkTargetWithCircle(bot, olm);
|
||||||
SetRtiTarget(botAI, "circle", olm);
|
SetRtiTarget(botAI, "circle", olm);
|
||||||
@@ -64,29 +58,22 @@ bool HighKingMaulgarFirstAssistTankAttackOlmAction::Execute(Event event)
|
|||||||
|
|
||||||
if (olm->GetVictim() == bot)
|
if (olm->GetVictim() == bot)
|
||||||
{
|
{
|
||||||
const Location& tankPosition = GruulsLairLocations::OlmTankPosition;
|
const Position& position = OLM_TANK_POSITION;
|
||||||
const float maxDistance = 3.0f;
|
const float maxDistance = 3.0f;
|
||||||
const float olmTankLeeway = 30.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 dX = position.GetPositionX() - bot->GetPositionX();
|
||||||
float dY = tankPosition.y - bot->GetPositionY();
|
float dY = position.GetPositionY() - bot->GetPositionY();
|
||||||
float dist = sqrt(dX * dX + dY * dY);
|
float moveX = bot->GetPositionX() + (dX / distanceOlmToPosition) * maxDistance;
|
||||||
float moveX = bot->GetPositionX() + (dX / dist) * maxDistance;
|
float moveY = bot->GetPositionY() + (dY / distanceOlmToPosition) * maxDistance;
|
||||||
float moveY = bot->GetPositionY() + (dY / dist) * maxDistance;
|
return MoveTo(GRUULS_LAIR_MAP_ID, moveX, moveY, position.GetPositionZ(), false, false, false, false,
|
||||||
return MoveTo(bot->GetMapId(), moveX, moveY, tankPosition.z, false, false, false, false,
|
|
||||||
MovementPriority::MOVEMENT_COMBAT, true, 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;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -95,6 +82,8 @@ bool HighKingMaulgarFirstAssistTankAttackOlmAction::Execute(Event event)
|
|||||||
bool HighKingMaulgarSecondAssistTankAttackBlindeyeAction::Execute(Event event)
|
bool HighKingMaulgarSecondAssistTankAttackBlindeyeAction::Execute(Event event)
|
||||||
{
|
{
|
||||||
Unit* blindeye = AI_VALUE2(Unit*, "find target", "blindeye the seer");
|
Unit* blindeye = AI_VALUE2(Unit*, "find target", "blindeye the seer");
|
||||||
|
if (!blindeye)
|
||||||
|
return false;
|
||||||
|
|
||||||
MarkTargetWithStar(bot, blindeye);
|
MarkTargetWithStar(bot, blindeye);
|
||||||
SetRtiTarget(botAI, "star", blindeye);
|
SetRtiTarget(botAI, "star", blindeye);
|
||||||
@@ -104,31 +93,20 @@ bool HighKingMaulgarSecondAssistTankAttackBlindeyeAction::Execute(Event event)
|
|||||||
|
|
||||||
if (blindeye->GetVictim() == bot)
|
if (blindeye->GetVictim() == bot)
|
||||||
{
|
{
|
||||||
const Location& tankPosition = GruulsLairLocations::BlindeyeTankPosition;
|
const Position& position = BLINDEYE_TANK_POSITION;
|
||||||
const float maxDistance = 3.0f;
|
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 dX = position.GetPositionX() - bot->GetPositionX();
|
||||||
float dY = tankPosition.y - bot->GetPositionY();
|
float dY = position.GetPositionY() - bot->GetPositionY();
|
||||||
float dist = sqrt(dX * dX + dY * dY);
|
float moveX = bot->GetPositionX() + (dX / distanceToPosition) * maxDistance;
|
||||||
float moveX = bot->GetPositionX() + (dX / dist) * maxDistance;
|
float moveY = bot->GetPositionY() + (dY / distanceToPosition) * maxDistance;
|
||||||
float moveY = bot->GetPositionY() + (dY / dist) * maxDistance;
|
return MoveTo(GRUULS_LAIR_MAP_ID, moveX, moveY, position.GetPositionZ(), false, false, false, false,
|
||||||
return MoveTo(bot->GetMapId(), moveX, moveY, tankPosition.z, false, false, false, false,
|
|
||||||
MovementPriority::MOVEMENT_COMBAT, true, 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;
|
return false;
|
||||||
@@ -138,6 +116,8 @@ bool HighKingMaulgarSecondAssistTankAttackBlindeyeAction::Execute(Event event)
|
|||||||
bool HighKingMaulgarMageTankAttackKroshAction::Execute(Event event)
|
bool HighKingMaulgarMageTankAttackKroshAction::Execute(Event event)
|
||||||
{
|
{
|
||||||
Unit* krosh = AI_VALUE2(Unit*, "find target", "krosh firehand");
|
Unit* krosh = AI_VALUE2(Unit*, "find target", "krosh firehand");
|
||||||
|
if (!krosh)
|
||||||
|
return false;
|
||||||
|
|
||||||
MarkTargetWithTriangle(bot, krosh);
|
MarkTargetWithTriangle(bot, krosh);
|
||||||
SetRtiTarget(botAI, "triangle", krosh);
|
SetRtiTarget(botAI, "triangle", krosh);
|
||||||
@@ -149,25 +129,22 @@ bool HighKingMaulgarMageTankAttackKroshAction::Execute(Event event)
|
|||||||
return botAI->CastSpell("fire ward", bot);
|
return botAI->CastSpell("fire ward", bot);
|
||||||
|
|
||||||
if (bot->GetTarget() != krosh->GetGUID())
|
if (bot->GetTarget() != krosh->GetGUID())
|
||||||
{
|
return Attack(krosh);
|
||||||
bot->SetSelection(krosh->GetGUID());
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (krosh->GetVictim() == bot)
|
if (krosh->GetVictim() == bot)
|
||||||
{
|
{
|
||||||
const Location& tankPosition = GruulsLairLocations::KroshTankPosition;
|
const Position& position = KROSH_TANK_POSITION;
|
||||||
float distanceToKrosh = krosh->GetExactDist2d(tankPosition.x, tankPosition.y);
|
float distanceToKrosh = krosh->GetExactDist2d(position.GetPositionX(), position.GetPositionY());
|
||||||
const float minDistance = 16.0f;
|
const float minDistance = 16.0f;
|
||||||
const float maxDistance = 29.0f;
|
const float maxDistance = 29.0f;
|
||||||
const float tankPositionLeeway = 1.0f;
|
const float tankPositionLeeway = 1.0f;
|
||||||
|
|
||||||
if (distanceToKrosh > minDistance && distanceToKrosh < maxDistance)
|
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,
|
return MoveTo(GRUULS_LAIR_MAP_ID, position.GetPositionX(), position.GetPositionY(), position.GetPositionZ(),
|
||||||
false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false);
|
false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
float orientation = atan2(krosh->GetPositionY() - bot->GetPositionY(),
|
float orientation = atan2(krosh->GetPositionY() - bot->GetPositionY(),
|
||||||
@@ -179,7 +156,7 @@ bool HighKingMaulgarMageTankAttackKroshAction::Execute(Event event)
|
|||||||
Position safePos;
|
Position safePos;
|
||||||
if (TryGetNewSafePosition(botAI, bot, 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);
|
false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -192,20 +169,19 @@ bool HighKingMaulgarMageTankAttackKroshAction::Execute(Event event)
|
|||||||
bool HighKingMaulgarMoonkinTankAttackKigglerAction::Execute(Event event)
|
bool HighKingMaulgarMoonkinTankAttackKigglerAction::Execute(Event event)
|
||||||
{
|
{
|
||||||
Unit* kiggler = AI_VALUE2(Unit*, "find target", "kiggler the crazed");
|
Unit* kiggler = AI_VALUE2(Unit*, "find target", "kiggler the crazed");
|
||||||
|
if (!kiggler)
|
||||||
|
return false;
|
||||||
|
|
||||||
MarkTargetWithDiamond(bot, kiggler);
|
MarkTargetWithDiamond(bot, kiggler);
|
||||||
SetRtiTarget(botAI, "diamond", kiggler);
|
SetRtiTarget(botAI, "diamond", kiggler);
|
||||||
|
|
||||||
if (bot->GetTarget() != kiggler->GetGUID())
|
if (bot->GetTarget() != kiggler->GetGUID())
|
||||||
{
|
return Attack(kiggler);
|
||||||
bot->SetSelection(kiggler->GetGUID());
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
Position safePos;
|
Position safePos;
|
||||||
if (TryGetNewSafePosition(botAI, bot, 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);
|
false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -216,121 +192,106 @@ bool HighKingMaulgarAssignDPSPriorityAction::Execute(Event event)
|
|||||||
{
|
{
|
||||||
// Target priority 1: Blindeye
|
// Target priority 1: Blindeye
|
||||||
Unit* blindeye = AI_VALUE2(Unit*, "find target", "blindeye the seer");
|
Unit* blindeye = AI_VALUE2(Unit*, "find target", "blindeye the seer");
|
||||||
if (blindeye && blindeye->IsAlive())
|
if (blindeye)
|
||||||
{
|
{
|
||||||
Position safePos;
|
Position safePos;
|
||||||
if (TryGetNewSafePosition(botAI, bot, safePos))
|
if (TryGetNewSafePosition(botAI, bot, safePos))
|
||||||
{
|
{
|
||||||
bot->AttackStop();
|
bot->AttackStop();
|
||||||
bot->InterruptNonMeleeSpells(false);
|
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);
|
false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
SetRtiTarget(botAI, "star", blindeye);
|
SetRtiTarget(botAI, "star", blindeye);
|
||||||
|
|
||||||
if (bot->GetTarget() != blindeye->GetGUID())
|
if (bot->GetTarget() != blindeye->GetGUID())
|
||||||
{
|
|
||||||
bot->SetSelection(blindeye->GetGUID());
|
|
||||||
return Attack(blindeye);
|
return Attack(blindeye);
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Target priority 2: Olm
|
// Target priority 2: Olm
|
||||||
Unit* olm = AI_VALUE2(Unit*, "find target", "olm the summoner");
|
Unit* olm = AI_VALUE2(Unit*, "find target", "olm the summoner");
|
||||||
if (olm && olm->IsAlive())
|
if (olm)
|
||||||
{
|
{
|
||||||
Position safePos;
|
Position safePos;
|
||||||
if (TryGetNewSafePosition(botAI, bot, safePos))
|
if (TryGetNewSafePosition(botAI, bot, safePos))
|
||||||
{
|
{
|
||||||
bot->AttackStop();
|
bot->AttackStop();
|
||||||
bot->InterruptNonMeleeSpells(false);
|
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);
|
false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
SetRtiTarget(botAI, "circle", olm);
|
SetRtiTarget(botAI, "circle", olm);
|
||||||
|
|
||||||
if (bot->GetTarget() != olm->GetGUID())
|
if (bot->GetTarget() != olm->GetGUID())
|
||||||
{
|
|
||||||
bot->SetSelection(olm->GetGUID());
|
|
||||||
return Attack(olm);
|
return Attack(olm);
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Target priority 3a: Krosh (ranged only)
|
// Target priority 3a: Krosh (ranged only)
|
||||||
Unit* krosh = AI_VALUE2(Unit*, "find target", "krosh firehand");
|
Unit* krosh = AI_VALUE2(Unit*, "find target", "krosh firehand");
|
||||||
if (krosh && krosh->IsAlive() && botAI->IsRanged(bot))
|
if (krosh && botAI->IsRanged(bot))
|
||||||
{
|
{
|
||||||
Position safePos;
|
Position safePos;
|
||||||
if (TryGetNewSafePosition(botAI, bot, safePos))
|
if (TryGetNewSafePosition(botAI, bot, safePos))
|
||||||
{
|
{
|
||||||
bot->AttackStop();
|
bot->AttackStop();
|
||||||
bot->InterruptNonMeleeSpells(false);
|
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);
|
false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
SetRtiTarget(botAI, "triangle", krosh);
|
SetRtiTarget(botAI, "triangle", krosh);
|
||||||
|
|
||||||
if (bot->GetTarget() != krosh->GetGUID())
|
if (bot->GetTarget() != krosh->GetGUID())
|
||||||
{
|
|
||||||
bot->SetSelection(krosh->GetGUID());
|
|
||||||
return Attack(krosh);
|
return Attack(krosh);
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Target priority 3b: Kiggler
|
// Target priority 3b: Kiggler
|
||||||
Unit* kiggler = AI_VALUE2(Unit*, "find target", "kiggler the crazed");
|
Unit* kiggler = AI_VALUE2(Unit*, "find target", "kiggler the crazed");
|
||||||
if (kiggler && kiggler->IsAlive())
|
if (kiggler)
|
||||||
{
|
{
|
||||||
Position safePos;
|
Position safePos;
|
||||||
if (TryGetNewSafePosition(botAI, bot, safePos))
|
if (TryGetNewSafePosition(botAI, bot, safePos))
|
||||||
{
|
{
|
||||||
bot->AttackStop();
|
bot->AttackStop();
|
||||||
bot->InterruptNonMeleeSpells(false);
|
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);
|
false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
SetRtiTarget(botAI, "diamond", kiggler);
|
SetRtiTarget(botAI, "diamond", kiggler);
|
||||||
|
|
||||||
if (bot->GetTarget() != kiggler->GetGUID())
|
if (bot->GetTarget() != kiggler->GetGUID())
|
||||||
{
|
|
||||||
bot->SetSelection(kiggler->GetGUID());
|
|
||||||
return Attack(kiggler);
|
return Attack(kiggler);
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Target priority 4: Maulgar
|
// Target priority 4: Maulgar
|
||||||
Unit* maulgar = AI_VALUE2(Unit*, "find target", "high king maulgar");
|
Unit* maulgar = AI_VALUE2(Unit*, "find target", "high king maulgar");
|
||||||
if (maulgar && maulgar->IsAlive())
|
if (maulgar)
|
||||||
{
|
{
|
||||||
Position safePos;
|
Position safePos;
|
||||||
if (TryGetNewSafePosition(botAI, bot, safePos))
|
if (TryGetNewSafePosition(botAI, bot, safePos))
|
||||||
{
|
{
|
||||||
bot->AttackStop();
|
bot->AttackStop();
|
||||||
bot->InterruptNonMeleeSpells(false);
|
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);
|
false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
SetRtiTarget(botAI, "square", maulgar);
|
SetRtiTarget(botAI, "square", maulgar);
|
||||||
|
|
||||||
if (bot->GetTarget() != maulgar->GetGUID())
|
if (bot->GetTarget() != maulgar->GetGUID())
|
||||||
{
|
|
||||||
bot->SetSelection(maulgar->GetGUID());
|
|
||||||
return Attack(maulgar);
|
return Attack(maulgar);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
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
|
// Avoid Whirlwind and Blast Wave and generally try to stay near the center of the room
|
||||||
bool HighKingMaulgarHealerFindSafePositionAction::Execute(Event event)
|
bool HighKingMaulgarHealerFindSafePositionAction::Execute(Event event)
|
||||||
{
|
{
|
||||||
const Location& fightCenter = GruulsLairLocations::MaulgarRoomCenter;
|
const Position& center = MAULGAR_ROOM_CENTER;
|
||||||
const float maxDistanceFromFight = 50.0f;
|
const float maxDistanceFromCenter = 50.0f;
|
||||||
float distToFight = bot->GetExactDist2d(fightCenter.x, fightCenter.y);
|
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 angle = atan2(bot->GetPositionY() - center.GetPositionY(), bot->GetPositionX() - center.GetPositionX());
|
||||||
float destX = fightCenter.x + 40.0f * cos(angle);
|
float destX = center.GetPositionX() + 40.0f * cos(angle);
|
||||||
float destY = fightCenter.y + 40.0f * sin(angle);
|
float destY = center.GetPositionY() + 40.0f * sin(angle);
|
||||||
float destZ = fightCenter.z;
|
float destZ = center.GetPositionZ();
|
||||||
|
|
||||||
if (!bot->GetMap()->CheckCollisionAndGetValidCoords(bot, bot->GetPositionX(), bot->GetPositionY(),
|
if (!bot->GetMap()->CheckCollisionAndGetValidCoords(bot, bot->GetPositionX(), bot->GetPositionY(),
|
||||||
bot->GetPositionZ(), destX, destY, destZ))
|
bot->GetPositionZ(), destX, destY, destZ))
|
||||||
return false;
|
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);
|
MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -362,7 +323,7 @@ bool HighKingMaulgarHealerFindSafePositionAction::Execute(Event event)
|
|||||||
{
|
{
|
||||||
bot->AttackStop();
|
bot->AttackStop();
|
||||||
bot->InterruptNonMeleeSpells(false);
|
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);
|
false, false, false, false, MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -373,6 +334,8 @@ bool HighKingMaulgarHealerFindSafePositionAction::Execute(Event event)
|
|||||||
bool HighKingMaulgarRunAwayFromWhirlwindAction::Execute(Event event)
|
bool HighKingMaulgarRunAwayFromWhirlwindAction::Execute(Event event)
|
||||||
{
|
{
|
||||||
Unit* maulgar = AI_VALUE2(Unit*, "find target", "high king maulgar");
|
Unit* maulgar = AI_VALUE2(Unit*, "find target", "high king maulgar");
|
||||||
|
if (!maulgar)
|
||||||
|
return false;
|
||||||
|
|
||||||
const float safeDistance = 10.0f;
|
const float safeDistance = 10.0f;
|
||||||
float distance = bot->GetExactDist2d(maulgar);
|
float distance = bot->GetExactDist2d(maulgar);
|
||||||
@@ -395,7 +358,7 @@ bool HighKingMaulgarRunAwayFromWhirlwindAction::Execute(Event event)
|
|||||||
{
|
{
|
||||||
bot->AttackStop();
|
bot->AttackStop();
|
||||||
bot->InterruptNonMeleeSpells(true);
|
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);
|
MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -439,7 +402,7 @@ bool HighKingMaulgarBanishFelstalkerAction::Execute(Event event)
|
|||||||
if (warlockIndex >= 0 && warlockIndex < felStalkers.size())
|
if (warlockIndex >= 0 && warlockIndex < felStalkers.size())
|
||||||
{
|
{
|
||||||
Unit* assignedFelStalker = felStalkers[warlockIndex];
|
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);
|
return botAI->CastSpell("banish", assignedFelStalker);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -528,40 +491,33 @@ bool HighKingMaulgarMisdirectOlmAndBlindeyeAction::Execute(Event event)
|
|||||||
// Gruul the Dragonkiller Actions
|
// Gruul the Dragonkiller Actions
|
||||||
|
|
||||||
// Position in center of the room
|
// 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");
|
Unit* gruul = AI_VALUE2(Unit*, "find target", "gruul the dragonkiller");
|
||||||
|
if (!gruul)
|
||||||
|
return false;
|
||||||
|
|
||||||
if (bot->GetVictim() != gruul)
|
if (bot->GetVictim() != gruul)
|
||||||
return Attack(gruul);
|
return Attack(gruul);
|
||||||
|
|
||||||
if (gruul->GetVictim() == bot)
|
if (gruul->GetVictim() == bot)
|
||||||
{
|
{
|
||||||
const Location& tankPosition = GruulsLairLocations::GruulTankPosition;
|
const Position& position = GRUUL_TANK_POSITION;
|
||||||
const float maxDistance = 3.0f;
|
const float maxDistance = 5.0f;
|
||||||
|
|
||||||
float dX = tankPosition.x - bot->GetPositionX();
|
float dX = position.GetPositionX() - bot->GetPositionX();
|
||||||
float dY = tankPosition.y - bot->GetPositionY();
|
float dY = position.GetPositionY() - bot->GetPositionY();
|
||||||
float distanceToTankPosition = bot->GetExactDist2d(tankPosition.x, tankPosition.y);
|
float distanceToTankPosition = bot->GetExactDist2d(position.GetPositionX(), position.GetPositionY());
|
||||||
|
|
||||||
if (distanceToTankPosition > maxDistance)
|
if (distanceToTankPosition > maxDistance)
|
||||||
{
|
{
|
||||||
float step = std::min(maxDistance, distanceToTankPosition);
|
float step = std::min(maxDistance, distanceToTankPosition);
|
||||||
float moveX = bot->GetPositionX() + (dX / distanceToTankPosition) * maxDistance;
|
float moveX = bot->GetPositionX() + (dX / distanceToTankPosition) * maxDistance;
|
||||||
float moveY = bot->GetPositionY() + (dY / distanceToTankPosition) * maxDistance;
|
float moveY = bot->GetPositionY() + (dY / distanceToTankPosition) * maxDistance;
|
||||||
const float moveZ = tankPosition.z;
|
const float moveZ = position.GetPositionZ();
|
||||||
return MoveTo(bot->GetMapId(), moveX, moveY, moveZ, false, false, false, false,
|
return MoveTo(GRUULS_LAIR_MAP_ID, moveX, moveY, moveZ, false, false, false, false,
|
||||||
MovementPriority::MOVEMENT_COMBAT, true, 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;
|
return false;
|
||||||
@@ -579,16 +535,16 @@ bool GruulTheDragonkillerSpreadRangedAction::Execute(Event event)
|
|||||||
static std::unordered_map<ObjectGuid, bool> hasReachedInitialPosition;
|
static std::unordered_map<ObjectGuid, bool> hasReachedInitialPosition;
|
||||||
|
|
||||||
Unit* gruul = AI_VALUE2(Unit*, "find target", "gruul the dragonkiller");
|
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();
|
initialPositions.erase(bot->GetGUID());
|
||||||
hasReachedInitialPosition.clear();
|
hasReachedInitialPosition.erase(bot->GetGUID());
|
||||||
}
|
}
|
||||||
|
|
||||||
const Location& tankPosition = GruulsLairLocations::GruulTankPosition;
|
const Position& position = GRUUL_TANK_POSITION;
|
||||||
const float centerX = tankPosition.x;
|
const float centerX = position.GetPositionX();
|
||||||
const float centerY = tankPosition.y;
|
const float centerY = position.GetPositionY();
|
||||||
float centerZ = bot->GetPositionZ();
|
const float centerZ = position.GetPositionZ();
|
||||||
const float minRadius = 25.0f;
|
const float minRadius = 25.0f;
|
||||||
const float maxRadius = 40.0f;
|
const float maxRadius = 40.0f;
|
||||||
|
|
||||||
@@ -642,7 +598,7 @@ bool GruulTheDragonkillerSpreadRangedAction::Execute(Event event)
|
|||||||
bot->GetPositionY(), bot->GetPositionZ(), destX, destY, destZ))
|
bot->GetPositionY(), bot->GetPositionZ(), destX, destY, destZ))
|
||||||
return false;
|
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);
|
MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -85,10 +85,10 @@ public:
|
|||||||
bool Execute(Event event) override;
|
bool Execute(Event event) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class GruulTheDragonkillerMainTankPositionBossAction : public AttackAction
|
class GruulTheDragonkillerTanksPositionBossAction : public AttackAction
|
||||||
{
|
{
|
||||||
public:
|
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;
|
bool Execute(Event event) override;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -8,18 +8,11 @@
|
|||||||
#include "HunterActions.h"
|
#include "HunterActions.h"
|
||||||
#include "MageActions.h"
|
#include "MageActions.h"
|
||||||
#include "Playerbots.h"
|
#include "Playerbots.h"
|
||||||
|
#include "ReachTargetActions.h"
|
||||||
#include "WarriorActions.h"
|
#include "WarriorActions.h"
|
||||||
|
|
||||||
using namespace GruulsLairHelpers;
|
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)
|
float HighKingMaulgarDisableTankAssistMultiplier::GetValue(Action* action)
|
||||||
{
|
{
|
||||||
if (IsAnyOgreBossAlive(botAI) && dynamic_cast<TankAssistAction*>(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");
|
Unit* blindeye = AI_VALUE2(Unit*, "find target", "blindeye the seer");
|
||||||
|
|
||||||
if (maulgar && maulgar->HasAura(SPELL_WHIRLWIND) &&
|
if (maulgar && maulgar->HasAura(SPELL_WHIRLWIND) &&
|
||||||
(!kiggler || !kiggler->IsAlive()) &&
|
!kiggler && !krosh && !olm && !blindeye)
|
||||||
(!krosh || !krosh->IsAlive()) &&
|
|
||||||
(!olm || !olm->IsAlive()) &&
|
|
||||||
(!blindeye || !blindeye->IsAlive()))
|
|
||||||
{
|
{
|
||||||
if (IsChargeAction(action) || (dynamic_cast<MovementAction*>(action) &&
|
if (dynamic_cast<CastReachTargetSpellAction*>(action) ||
|
||||||
|
(dynamic_cast<MovementAction*>(action) &&
|
||||||
!dynamic_cast<HighKingMaulgarRunAwayFromWhirlwindAction*>(action)))
|
!dynamic_cast<HighKingMaulgarRunAwayFromWhirlwindAction*>(action)))
|
||||||
return 0.0f;
|
return 0.0f;
|
||||||
}
|
}
|
||||||
@@ -57,7 +48,8 @@ float HighKingMaulgarDisableArcaneShotOnKroshMultiplier::GetValue(Action* action
|
|||||||
Unit* krosh = AI_VALUE2(Unit*, "find target", "krosh firehand");
|
Unit* krosh = AI_VALUE2(Unit*, "find target", "krosh firehand");
|
||||||
Unit* target = AI_VALUE(Unit*, "current target");
|
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 0.0f;
|
||||||
|
|
||||||
return 1.0f;
|
return 1.0f;
|
||||||
@@ -101,8 +93,9 @@ float GruulTheDragonkillerGroundSlamMultiplier::GetValue(Action* action)
|
|||||||
if (bot->HasAura(SPELL_GROUND_SLAM_1) ||
|
if (bot->HasAura(SPELL_GROUND_SLAM_1) ||
|
||||||
bot->HasAura(SPELL_GROUND_SLAM_2))
|
bot->HasAura(SPELL_GROUND_SLAM_2))
|
||||||
{
|
{
|
||||||
if ((dynamic_cast<MovementAction*>(action) && !dynamic_cast<GruulTheDragonkillerShatterSpreadAction*>(action)) ||
|
if ((dynamic_cast<MovementAction*>(action) &&
|
||||||
IsChargeAction(action))
|
!dynamic_cast<GruulTheDragonkillerShatterSpreadAction*>(action)) ||
|
||||||
|
dynamic_cast<CastReachTargetSpellAction*>(action))
|
||||||
return 0.0f;
|
return 0.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ public:
|
|||||||
creators["high king maulgar misdirect olm and blindeye"] = &RaidGruulsLairActionContext::high_king_maulgar_misdirect_olm_and_blindeye;
|
creators["high king maulgar misdirect olm and blindeye"] = &RaidGruulsLairActionContext::high_king_maulgar_misdirect_olm_and_blindeye;
|
||||||
|
|
||||||
// Gruul the Dragonkiller
|
// 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 spread ranged"] = &RaidGruulsLairActionContext::gruul_the_dragonkiller_spread_ranged;
|
||||||
creators["gruul the dragonkiller shatter spread"] = &RaidGruulsLairActionContext::gruul_the_dragonkiller_shatter_spread;
|
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); }
|
static Action* high_king_maulgar_misdirect_olm_and_blindeye(PlayerbotAI* botAI) { return new HighKingMaulgarMisdirectOlmAndBlindeyeAction(botAI); }
|
||||||
|
|
||||||
// Gruul the Dragonkiller
|
// 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_spread_ranged(PlayerbotAI* botAI) { return new GruulTheDragonkillerSpreadRangedAction(botAI); }
|
||||||
static Action* gruul_the_dragonkiller_shatter_spread(PlayerbotAI* botAI) { return new GruulTheDragonkillerShatterSpreadAction(botAI); }
|
static Action* gruul_the_dragonkiller_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;
|
creators["high king maulgar pulling olm and blindeye"] = &RaidGruulsLairTriggerContext::high_king_maulgar_pulling_olm_and_blindeye;
|
||||||
|
|
||||||
// Gruul the Dragonkiller
|
// 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 tanks"] = &RaidGruulsLairTriggerContext::gruul_the_dragonkiller_boss_engaged_by_tanks;
|
||||||
creators["gruul the dragonkiller boss engaged by range"] = &RaidGruulsLairTriggerContext::gruul_the_dragonkiller_boss_engaged_by_range;
|
creators["gruul the dragonkiller boss engaged by ranged"] = &RaidGruulsLairTriggerContext::gruul_the_dragonkiller_boss_engaged_by_ranged;
|
||||||
creators["gruul the dragonkiller incoming shatter"] = &RaidGruulsLairTriggerContext::gruul_the_dragonkiller_incoming_shatter;
|
creators["gruul the dragonkiller 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); }
|
static Trigger* high_king_maulgar_pulling_olm_and_blindeye(PlayerbotAI* botAI) { return new HighKingMaulgarPullingOlmAndBlindeyeTrigger(botAI); }
|
||||||
|
|
||||||
// Gruul the Dragonkiller
|
// 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_tanks(PlayerbotAI* botAI) { return new GruulTheDragonkillerBossEngagedByTanksTrigger(botAI); }
|
||||||
static Trigger* gruul_the_dragonkiller_boss_engaged_by_range(PlayerbotAI* botAI) { return new GruulTheDragonkillerBossEngagedByRangeTrigger(botAI); }
|
static Trigger* gruul_the_dragonkiller_boss_engaged_by_ranged(PlayerbotAI* botAI) { return new GruulTheDragonkillerBossEngagedByRangedTrigger(botAI); }
|
||||||
static Trigger* gruul_the_dragonkiller_incoming_shatter(PlayerbotAI* botAI) { return new GruulTheDragonkillerIncomingShatterTrigger(botAI); }
|
static Trigger* gruul_the_dragonkiller_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) }));
|
NextAction("high king maulgar misdirect olm and blindeye", ACTION_RAID + 2) }));
|
||||||
|
|
||||||
// Gruul the Dragonkiller
|
// Gruul the Dragonkiller
|
||||||
triggers.push_back(new TriggerNode("gruul the dragonkiller boss engaged by main tank", {
|
triggers.push_back(new TriggerNode("gruul the dragonkiller boss engaged by tanks", {
|
||||||
NextAction("gruul the dragonkiller main tank position boss", ACTION_RAID + 1) }));
|
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) }));
|
NextAction("gruul the dragonkiller spread ranged", ACTION_RAID + 1) }));
|
||||||
|
|
||||||
triggers.push_back(new TriggerNode("gruul the dragonkiller incoming shatter", {
|
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");
|
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()
|
bool HighKingMaulgarIsFirstAssistTankTrigger::IsActive()
|
||||||
{
|
{
|
||||||
Unit* olm = AI_VALUE2(Unit*, "find target", "olm the summoner");
|
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()
|
bool HighKingMaulgarIsSecondAssistTankTrigger::IsActive()
|
||||||
{
|
{
|
||||||
Unit* blindeye = AI_VALUE2(Unit*, "find target", "blindeye the seer");
|
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()
|
bool HighKingMaulgarIsMageTankTrigger::IsActive()
|
||||||
{
|
{
|
||||||
Unit* krosh = AI_VALUE2(Unit*, "find target", "krosh firehand");
|
Unit* krosh = AI_VALUE2(Unit*, "find target", "krosh firehand");
|
||||||
|
|
||||||
return IsKroshMageTank(botAI, bot) && krosh && krosh->IsAlive();
|
return IsKroshMageTank(botAI, bot) && krosh;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HighKingMaulgarIsMoonkinTankTrigger::IsActive()
|
bool HighKingMaulgarIsMoonkinTankTrigger::IsActive()
|
||||||
{
|
{
|
||||||
Unit* kiggler = AI_VALUE2(Unit*, "find target", "kiggler the crazed");
|
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()
|
bool HighKingMaulgarDeterminingKillOrderTrigger::IsActive()
|
||||||
@@ -50,11 +50,11 @@ bool HighKingMaulgarDeterminingKillOrderTrigger::IsActive()
|
|||||||
Unit* krosh = AI_VALUE2(Unit*, "find target", "krosh firehand");
|
Unit* krosh = AI_VALUE2(Unit*, "find target", "krosh firehand");
|
||||||
|
|
||||||
return (botAI->IsDps(bot) || botAI->IsTank(bot)) &&
|
return (botAI->IsDps(bot) || botAI->IsTank(bot)) &&
|
||||||
!(botAI->IsMainTank(bot) && maulgar && maulgar->IsAlive()) &&
|
!(botAI->IsMainTank(bot) && maulgar) &&
|
||||||
!(botAI->IsAssistTankOfIndex(bot, 0) && olm && olm->IsAlive()) &&
|
!(botAI->IsAssistTankOfIndex(bot, 0, false) && olm) &&
|
||||||
!(botAI->IsAssistTankOfIndex(bot, 1) && blindeye && blindeye->IsAlive()) &&
|
!(botAI->IsAssistTankOfIndex(bot, 1, false) && blindeye) &&
|
||||||
!(IsKroshMageTank(botAI, bot) && krosh && krosh->IsAlive()) &&
|
!(IsKroshMageTank(botAI, bot) && krosh) &&
|
||||||
!(IsKigglerMoonkinTank(botAI, bot) && kiggler && kiggler->IsAlive());
|
!(IsKigglerMoonkinTank(botAI, bot) && kiggler);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HighKingMaulgarHealerInDangerTrigger::IsActive()
|
bool HighKingMaulgarHealerInDangerTrigger::IsActive()
|
||||||
@@ -66,7 +66,7 @@ bool HighKingMaulgarBossChannelingWhirlwindTrigger::IsActive()
|
|||||||
{
|
{
|
||||||
Unit* maulgar = AI_VALUE2(Unit*, "find target", "high king maulgar");
|
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);
|
!botAI->IsMainTank(bot);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,7 +74,7 @@ bool HighKingMaulgarWildFelstalkerSpawnedTrigger::IsActive()
|
|||||||
{
|
{
|
||||||
Unit* felStalker = AI_VALUE2(Unit*, "find target", "wild fel stalker");
|
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()
|
bool HighKingMaulgarPullingOlmAndBlindeyeTrigger::IsActive()
|
||||||
@@ -120,12 +120,12 @@ bool HighKingMaulgarPullingOlmAndBlindeyeTrigger::IsActive()
|
|||||||
switch (hunterIndex)
|
switch (hunterIndex)
|
||||||
{
|
{
|
||||||
case 0:
|
case 0:
|
||||||
return olm && olm->IsAlive() && olm->GetHealthPct() > 98.0f &&
|
return olm && olm->GetHealthPct() > 98.0f &&
|
||||||
olmTank && olmTank->IsAlive() && botAI->CanCastSpell("misdirection", olmTank);
|
olmTank && botAI->CanCastSpell("misdirection", olmTank);
|
||||||
|
|
||||||
case 1:
|
case 1:
|
||||||
return blindeye && blindeye->IsAlive() && blindeye->GetHealthPct() > 90.0f &&
|
return blindeye && blindeye->GetHealthPct() > 90.0f &&
|
||||||
blindeyeTank && blindeyeTank->IsAlive() && botAI->CanCastSpell("misdirection", blindeyeTank);
|
blindeyeTank && botAI->CanCastSpell("misdirection", blindeyeTank);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
@@ -136,25 +136,24 @@ bool HighKingMaulgarPullingOlmAndBlindeyeTrigger::IsActive()
|
|||||||
|
|
||||||
// Gruul the Dragonkiller Triggers
|
// Gruul the Dragonkiller Triggers
|
||||||
|
|
||||||
bool GruulTheDragonkillerBossEngagedByMainTankTrigger::IsActive()
|
bool GruulTheDragonkillerBossEngagedByTanksTrigger::IsActive()
|
||||||
{
|
{
|
||||||
Unit* gruul = AI_VALUE2(Unit*, "find target", "gruul the dragonkiller");
|
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");
|
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()
|
bool GruulTheDragonkillerIncomingShatterTrigger::IsActive()
|
||||||
{
|
{
|
||||||
Unit* gruul = AI_VALUE2(Unit*, "find target", "gruul the dragonkiller");
|
Unit* gruul = AI_VALUE2(Unit*, "find target", "gruul the dragonkiller");
|
||||||
|
|
||||||
return gruul && gruul->IsAlive() &&
|
return gruul && (bot->HasAura(SPELL_GROUND_SLAM_1) ||
|
||||||
(bot->HasAura(SPELL_GROUND_SLAM_1) ||
|
|
||||||
bot->HasAura(SPELL_GROUND_SLAM_2));
|
bot->HasAura(SPELL_GROUND_SLAM_2));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,17 +73,17 @@ public:
|
|||||||
bool IsActive() override;
|
bool IsActive() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class GruulTheDragonkillerBossEngagedByMainTankTrigger : public Trigger
|
class GruulTheDragonkillerBossEngagedByTanksTrigger : public Trigger
|
||||||
{
|
{
|
||||||
public:
|
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;
|
bool IsActive() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class GruulTheDragonkillerBossEngagedByRangeTrigger : public Trigger
|
class GruulTheDragonkillerBossEngagedByRangedTrigger : public Trigger
|
||||||
{
|
{
|
||||||
public:
|
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;
|
bool IsActive() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -5,20 +5,17 @@
|
|||||||
#include "Unit.h"
|
#include "Unit.h"
|
||||||
|
|
||||||
namespace GruulsLairHelpers
|
namespace GruulsLairHelpers
|
||||||
{
|
|
||||||
namespace GruulsLairLocations
|
|
||||||
{
|
{
|
||||||
// Olm does not chase properly due to the Core's caster movement issues
|
// Olm does not chase properly due to the Core's caster movement issues
|
||||||
// Thus, the below "OlmTankPosition" is beyond the actual desired tanking location
|
// 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
|
// 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
|
// "MaulgarRoomCenter" is to keep healers in a centralized location
|
||||||
const Location MaulgarTankPosition = { 90.686f, 167.047f, -13.234f };
|
const Position MAULGAR_TANK_POSITION = { 90.686f, 167.047f, -13.234f };
|
||||||
const Location OlmTankPosition = { 87.485f, 234.942f, -3.635f };
|
const Position OLM_TANK_POSITION = { 87.485f, 234.942f, -3.635f };
|
||||||
const Location BlindeyeTankPosition = { 99.681f, 213.989f, -10.345f };
|
const Position BLINDEYE_TANK_POSITION = { 99.681f, 213.989f, -10.345f };
|
||||||
const Location KroshTankPosition = { 116.880f, 166.208f, -14.231f };
|
const Position KROSH_TANK_POSITION = { 116.880f, 166.208f, -14.231f };
|
||||||
const Location MaulgarRoomCenter = { 88.754f, 150.759f, -11.569f };
|
const Position MAULGAR_ROOM_CENTER = { 88.754f, 150.759f, -11.569f };
|
||||||
const Location GruulTankPosition = { 241.238f, 365.025f, -4.220f };
|
const Position GRUUL_TANK_POSITION = { 241.238f, 365.025f, -4.220f };
|
||||||
}
|
|
||||||
|
|
||||||
bool IsAnyOgreBossAlive(PlayerbotAI* botAI)
|
bool IsAnyOgreBossAlive(PlayerbotAI* botAI)
|
||||||
{
|
{
|
||||||
@@ -42,75 +39,34 @@ namespace GruulsLairHelpers
|
|||||||
return false;
|
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)
|
bool IsKroshMageTank(PlayerbotAI* botAI, Player* bot)
|
||||||
{
|
{
|
||||||
Group* group = bot->GetGroup();
|
Group* group = bot->GetGroup();
|
||||||
if (!group)
|
if (!group)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
Player* highestHpMage = nullptr;
|
// (1) First loop: Return the first assistant Mage (real player or bot)
|
||||||
uint32 highestHp = 0;
|
|
||||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||||
{
|
{
|
||||||
Player* member = ref->GetSource();
|
Player* member = ref->GetSource();
|
||||||
if (!member || !member->IsAlive() || !GET_PLAYERBOT_AI(member))
|
if (!member || !member->IsAlive() || member->getClass() != CLASS_MAGE)
|
||||||
continue;
|
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();
|
uint32 hp = member->GetMaxHealth();
|
||||||
if (!highestHpMage || hp > highestHp)
|
if (!highestHpMage || hp > highestHp)
|
||||||
{
|
{
|
||||||
@@ -118,8 +74,8 @@ namespace GruulsLairHelpers
|
|||||||
highestHp = hp;
|
highestHp = hp;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
// (3) Return the found Mage tank, or nullptr if none found
|
||||||
return highestHpMage == bot;
|
return highestHpMage == bot;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -129,20 +85,28 @@ namespace GruulsLairHelpers
|
|||||||
if (!group)
|
if (!group)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
Player* highestHpMoonkin = nullptr;
|
// (1) First loop: Return the first assistant Moonkin (real player or bot)
|
||||||
uint32 highestHp = 0;
|
|
||||||
|
|
||||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||||
{
|
{
|
||||||
Player* member = ref->GetSource();
|
Player* member = ref->GetSource();
|
||||||
if (!member || !member->IsAlive() || !GET_PLAYERBOT_AI(member))
|
if (!member || !member->IsAlive() || member->getClass() != CLASS_DRUID)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (member->getClass() == CLASS_DRUID)
|
if (group->IsAssistant(member->GetGUID()) &&
|
||||||
{
|
AiFactory::GetPlayerSpecTab(member) == DRUID_TAB_BALANCE)
|
||||||
int tab = AiFactory::GetPlayerSpecTab(member);
|
return member == bot;
|
||||||
if (tab == DRUID_TAB_BALANCE)
|
}
|
||||||
|
|
||||||
|
// (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();
|
uint32 hp = member->GetMaxHealth();
|
||||||
if (!highestHpMoonkin || hp > highestHp)
|
if (!highestHpMoonkin || hp > highestHp)
|
||||||
{
|
{
|
||||||
@@ -150,9 +114,8 @@ namespace GruulsLairHelpers
|
|||||||
highestHp = hp;
|
highestHp = hp;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// (3) Return the found Moonkin tank, or nullptr if none found
|
||||||
return highestHpMoonkin == bot;
|
return highestHpMoonkin == bot;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
#define RAID_GRUULSLAIRHELPERS_H
|
#define RAID_GRUULSLAIRHELPERS_H
|
||||||
|
|
||||||
#include "PlayerbotAI.h"
|
#include "PlayerbotAI.h"
|
||||||
#include "RtiTargetValue.h"
|
|
||||||
|
|
||||||
namespace GruulsLairHelpers
|
namespace GruulsLairHelpers
|
||||||
{
|
{
|
||||||
@@ -17,9 +16,6 @@ namespace GruulsLairHelpers
|
|||||||
// Hunter
|
// Hunter
|
||||||
SPELL_MISDIRECTION = 35079,
|
SPELL_MISDIRECTION = 35079,
|
||||||
|
|
||||||
// Warlock
|
|
||||||
SPELL_BANISH = 18647, // Rank 2
|
|
||||||
|
|
||||||
// Gruul the Dragonkiller
|
// Gruul the Dragonkiller
|
||||||
SPELL_GROUND_SLAM_1 = 33525,
|
SPELL_GROUND_SLAM_1 = 33525,
|
||||||
SPELL_GROUND_SLAM_2 = 39187,
|
SPELL_GROUND_SLAM_2 = 39187,
|
||||||
@@ -30,33 +26,20 @@ namespace GruulsLairHelpers
|
|||||||
NPC_WILD_FEL_STALKER = 18847,
|
NPC_WILD_FEL_STALKER = 18847,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
constexpr uint32 GRUULS_LAIR_MAP_ID = 565;
|
||||||
|
|
||||||
bool IsAnyOgreBossAlive(PlayerbotAI* botAI);
|
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 IsKroshMageTank(PlayerbotAI* botAI, Player* bot);
|
||||||
bool IsKigglerMoonkinTank(PlayerbotAI* botAI, Player* bot);
|
bool IsKigglerMoonkinTank(PlayerbotAI* botAI, Player* bot);
|
||||||
bool IsPositionSafe(PlayerbotAI* botAI, Player* bot, Position pos);
|
bool IsPositionSafe(PlayerbotAI* botAI, Player* bot, Position pos);
|
||||||
bool TryGetNewSafePosition(PlayerbotAI* botAI, Player* bot, Position& outPos);
|
bool TryGetNewSafePosition(PlayerbotAI* botAI, Player* bot, Position& outPos);
|
||||||
|
|
||||||
struct Location
|
extern const Position MAULGAR_TANK_POSITION;
|
||||||
{
|
extern const Position OLM_TANK_POSITION;
|
||||||
float x, y, z;
|
extern const Position BLINDEYE_TANK_POSITION;
|
||||||
};
|
extern const Position KROSH_TANK_POSITION;
|
||||||
|
extern const Position MAULGAR_ROOM_CENTER;
|
||||||
namespace GruulsLairLocations
|
extern const Position GRUUL_TANK_POSITION;
|
||||||
{
|
|
||||||
extern const Location MaulgarTankPosition;
|
|
||||||
extern const Location OlmTankPosition;
|
|
||||||
extern const Location BlindeyeTankPosition;
|
|
||||||
extern const Location KroshTankPosition;
|
|
||||||
extern const Location MaulgarRoomCenter;
|
|
||||||
extern const Location GruulTankPosition;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
#include "RaidKarazhanHelpers.h"
|
#include "RaidKarazhanHelpers.h"
|
||||||
#include "Playerbots.h"
|
#include "Playerbots.h"
|
||||||
#include "PlayerbotTextMgr.h"
|
#include "PlayerbotTextMgr.h"
|
||||||
|
#include "RaidBossHelpers.h"
|
||||||
|
|
||||||
using namespace KarazhanHelpers;
|
using namespace KarazhanHelpers;
|
||||||
|
|
||||||
@@ -44,7 +45,7 @@ bool AttumenTheHuntsmanMarkTargetAction::Execute(Event event)
|
|||||||
Unit* attumenMounted = GetFirstAliveUnitByEntry(botAI, NPC_ATTUMEN_THE_HUNTSMAN_MOUNTED);
|
Unit* attumenMounted = GetFirstAliveUnitByEntry(botAI, NPC_ATTUMEN_THE_HUNTSMAN_MOUNTED);
|
||||||
if (attumenMounted)
|
if (attumenMounted)
|
||||||
{
|
{
|
||||||
if (IsInstanceTimerManager(botAI, bot))
|
if (IsMechanicTrackerBot(botAI, bot, KARAZHAN_MAP_ID, nullptr))
|
||||||
MarkTargetWithStar(bot, attumenMounted);
|
MarkTargetWithStar(bot, attumenMounted);
|
||||||
|
|
||||||
SetRtiTarget(botAI, "star", attumenMounted);
|
SetRtiTarget(botAI, "star", attumenMounted);
|
||||||
@@ -57,7 +58,7 @@ bool AttumenTheHuntsmanMarkTargetAction::Execute(Event event)
|
|||||||
}
|
}
|
||||||
else if (Unit* midnight = AI_VALUE2(Unit*, "find target", "midnight"))
|
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);
|
MarkTargetWithStar(bot, midnight);
|
||||||
|
|
||||||
if (!botAI->IsAssistTankOfIndex(bot, 0))
|
if (!botAI->IsAssistTankOfIndex(bot, 0))
|
||||||
@@ -180,7 +181,7 @@ bool MoroesMarkTargetAction::Execute(Event event)
|
|||||||
|
|
||||||
if (target)
|
if (target)
|
||||||
{
|
{
|
||||||
if (IsInstanceTimerManager(botAI, bot))
|
if (IsMechanicTrackerBot(botAI, bot, KARAZHAN_MAP_ID, nullptr))
|
||||||
MarkTargetWithSkull(bot, target);
|
MarkTargetWithSkull(bot, target);
|
||||||
|
|
||||||
SetRtiTarget(botAI, "skull", target);
|
SetRtiTarget(botAI, "skull", target);
|
||||||
@@ -405,7 +406,7 @@ bool TheCuratorMarkAstralFlareAction::Execute(Event event)
|
|||||||
if (!flare)
|
if (!flare)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (IsInstanceTimerManager(botAI, bot))
|
if (IsMechanicTrackerBot(botAI, bot, KARAZHAN_MAP_ID, nullptr))
|
||||||
MarkTargetWithSkull(bot, flare);
|
MarkTargetWithSkull(bot, flare);
|
||||||
|
|
||||||
SetRtiTarget(botAI, "skull", flare);
|
SetRtiTarget(botAI, "skull", flare);
|
||||||
@@ -469,11 +470,11 @@ bool TheCuratorSpreadRangedAction::Execute(Event event)
|
|||||||
// Prioritize (1) Demon Chains, (2) Kil'rek, (3) Illhoof
|
// Prioritize (1) Demon Chains, (2) Kil'rek, (3) Illhoof
|
||||||
bool TerestianIllhoofMarkTargetAction::Execute(Event event)
|
bool TerestianIllhoofMarkTargetAction::Execute(Event event)
|
||||||
{
|
{
|
||||||
Unit* demonChains = AI_VALUE2(Unit*, "find target", "demon chains");
|
Unit* demonChains = GetFirstAliveUnitByEntry(botAI, NPC_DEMON_CHAINS);
|
||||||
Unit* kilrek = AI_VALUE2(Unit*, "find target", "kil'rek");
|
Unit* kilrek = GetFirstAliveUnitByEntry(botAI, NPC_KILREK);
|
||||||
Unit* illhoof = AI_VALUE2(Unit*, "find target", "terestian illhoof");
|
Unit* illhoof = AI_VALUE2(Unit*, "find target", "terestian illhoof");
|
||||||
Unit* target = GetFirstAliveUnit({demonChains, kilrek, illhoof});
|
|
||||||
|
|
||||||
|
Unit* target = GetFirstAliveUnit({demonChains, kilrek, illhoof});
|
||||||
if (target)
|
if (target)
|
||||||
MarkTargetWithSkull(bot, target);
|
MarkTargetWithSkull(bot, target);
|
||||||
|
|
||||||
@@ -1007,7 +1008,7 @@ bool NetherspiteManageTimersAndTrackersAction::Execute(Event event)
|
|||||||
if (netherspite->GetHealth() == netherspite->GetMaxHealth() &&
|
if (netherspite->GetHealth() == netherspite->GetMaxHealth() &&
|
||||||
!netherspite->HasAura(SPELL_GREEN_BEAM_HEAL))
|
!netherspite->HasAura(SPELL_GREEN_BEAM_HEAL))
|
||||||
{
|
{
|
||||||
if (IsInstanceTimerManager(botAI, bot))
|
if (IsMechanicTrackerBot(botAI, bot, KARAZHAN_MAP_ID, nullptr))
|
||||||
netherspiteDpsWaitTimer.insert_or_assign(instanceId, now);
|
netherspiteDpsWaitTimer.insert_or_assign(instanceId, now);
|
||||||
|
|
||||||
if (botAI->IsTank(bot) && !bot->HasAura(SPELL_RED_BEAM_DEBUFF))
|
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))
|
else if (netherspite->HasAura(SPELL_NETHERSPITE_BANISHED))
|
||||||
{
|
{
|
||||||
if (IsInstanceTimerManager(botAI, bot))
|
if (IsMechanicTrackerBot(botAI, bot, KARAZHAN_MAP_ID, nullptr))
|
||||||
netherspiteDpsWaitTimer.erase(instanceId);
|
netherspiteDpsWaitTimer.erase(instanceId);
|
||||||
|
|
||||||
if (botAI->IsTank(bot))
|
if (botAI->IsTank(bot))
|
||||||
@@ -1029,7 +1030,7 @@ bool NetherspiteManageTimersAndTrackersAction::Execute(Event event)
|
|||||||
}
|
}
|
||||||
else if (!netherspite->HasAura(SPELL_NETHERSPITE_BANISHED))
|
else if (!netherspite->HasAura(SPELL_NETHERSPITE_BANISHED))
|
||||||
{
|
{
|
||||||
if (IsInstanceTimerManager(botAI, bot))
|
if (IsMechanicTrackerBot(botAI, bot, KARAZHAN_MAP_ID, nullptr))
|
||||||
netherspiteDpsWaitTimer.try_emplace(instanceId, now);
|
netherspiteDpsWaitTimer.try_emplace(instanceId, now);
|
||||||
|
|
||||||
if (botAI->IsTank(bot) && bot->HasAura(SPELL_RED_BEAM_DEBUFF))
|
if (botAI->IsTank(bot) && bot->HasAura(SPELL_RED_BEAM_DEBUFF))
|
||||||
@@ -1458,7 +1459,7 @@ bool NightbaneManageTimersAndTrackersAction::Execute(Event event)
|
|||||||
if (botAI->IsRanged(bot))
|
if (botAI->IsRanged(bot))
|
||||||
nightbaneRangedStep.erase(botGuid);
|
nightbaneRangedStep.erase(botGuid);
|
||||||
|
|
||||||
if (IsInstanceTimerManager(botAI, bot))
|
if (IsMechanicTrackerBot(botAI, bot, KARAZHAN_MAP_ID, nullptr))
|
||||||
nightbaneDpsWaitTimer.erase(instanceId);
|
nightbaneDpsWaitTimer.erase(instanceId);
|
||||||
}
|
}
|
||||||
// Erase flight phase timer and Rain of Bones tracker on ground phase and start DPS wait timer
|
// 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);
|
nightbaneRainOfBonesHit.erase(botGuid);
|
||||||
|
|
||||||
if (IsInstanceTimerManager(botAI, bot))
|
if (IsMechanicTrackerBot(botAI, bot, KARAZHAN_MAP_ID, nullptr))
|
||||||
{
|
{
|
||||||
nightbaneFlightPhaseStartTimer.erase(instanceId);
|
nightbaneFlightPhaseStartTimer.erase(instanceId);
|
||||||
nightbaneDpsWaitTimer.try_emplace(instanceId, now);
|
nightbaneDpsWaitTimer.try_emplace(instanceId, now);
|
||||||
@@ -1482,7 +1483,7 @@ bool NightbaneManageTimersAndTrackersAction::Execute(Event event)
|
|||||||
if (botAI->IsRanged(bot))
|
if (botAI->IsRanged(bot))
|
||||||
nightbaneRangedStep.erase(botGuid);
|
nightbaneRangedStep.erase(botGuid);
|
||||||
|
|
||||||
if (IsInstanceTimerManager(botAI, bot))
|
if (IsMechanicTrackerBot(botAI, bot, KARAZHAN_MAP_ID, nullptr))
|
||||||
{
|
{
|
||||||
nightbaneDpsWaitTimer.erase(instanceId);
|
nightbaneDpsWaitTimer.erase(instanceId);
|
||||||
nightbaneFlightPhaseStartTimer.try_emplace(instanceId, now);
|
nightbaneFlightPhaseStartTimer.try_emplace(instanceId, now);
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
#include "MageActions.h"
|
#include "MageActions.h"
|
||||||
#include "Playerbots.h"
|
#include "Playerbots.h"
|
||||||
#include "PriestActions.h"
|
#include "PriestActions.h"
|
||||||
|
#include "RaidBossHelpers.h"
|
||||||
#include "ReachTargetActions.h"
|
#include "ReachTargetActions.h"
|
||||||
#include "RogueActions.h"
|
#include "RogueActions.h"
|
||||||
#include "ShamanActions.h"
|
#include "ShamanActions.h"
|
||||||
@@ -242,6 +243,9 @@ float PrinceMalchezaarEnfeebleKeepDistanceMultiplier::GetValue(Action* action)
|
|||||||
|
|
||||||
if (bot->HasAura(SPELL_ENFEEBLE))
|
if (bot->HasAura(SPELL_ENFEEBLE))
|
||||||
{
|
{
|
||||||
|
if (dynamic_cast<CastReachTargetSpellAction*>(action))
|
||||||
|
return 0.0f;
|
||||||
|
|
||||||
if (dynamic_cast<MovementAction*>(action) &&
|
if (dynamic_cast<MovementAction*>(action) &&
|
||||||
!dynamic_cast<PrinceMalchezaarEnfeebledAvoidHazardAction*>(action))
|
!dynamic_cast<PrinceMalchezaarEnfeebledAvoidHazardAction*>(action))
|
||||||
return 0.0f;
|
return 0.0f;
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
#include "RaidKarazhanHelpers.h"
|
#include "RaidKarazhanHelpers.h"
|
||||||
#include "RaidKarazhanActions.h"
|
#include "RaidKarazhanActions.h"
|
||||||
#include "Playerbots.h"
|
#include "Playerbots.h"
|
||||||
|
#include "RaidBossHelpers.h"
|
||||||
|
|
||||||
using namespace KarazhanHelpers;
|
using namespace KarazhanHelpers;
|
||||||
|
|
||||||
@@ -40,7 +41,7 @@ bool AttumenTheHuntsmanAttumenIsMountedTrigger::IsActive()
|
|||||||
|
|
||||||
bool AttumenTheHuntsmanBossWipesAggroWhenMountingTrigger::IsActive()
|
bool AttumenTheHuntsmanBossWipesAggroWhenMountingTrigger::IsActive()
|
||||||
{
|
{
|
||||||
if (!IsInstanceTimerManager(botAI, bot))
|
if (!IsMechanicTrackerBot(botAI, bot, KARAZHAN_MAP_ID, nullptr))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
Unit* midnight = AI_VALUE2(Unit*, "find target", "midnight");
|
Unit* midnight = AI_VALUE2(Unit*, "find target", "midnight");
|
||||||
@@ -110,7 +111,7 @@ bool BigBadWolfBossIsChasingLittleRedRidingHoodTrigger::IsActive()
|
|||||||
|
|
||||||
bool RomuloAndJulianneBothBossesRevivedTrigger::IsActive()
|
bool RomuloAndJulianneBothBossesRevivedTrigger::IsActive()
|
||||||
{
|
{
|
||||||
if (!IsInstanceTimerManager(botAI, bot))
|
if (!IsMechanicTrackerBot(botAI, bot, KARAZHAN_MAP_ID, nullptr))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
Unit* romulo = AI_VALUE2(Unit*, "find target", "romulo");
|
Unit* romulo = AI_VALUE2(Unit*, "find target", "romulo");
|
||||||
@@ -126,7 +127,7 @@ bool RomuloAndJulianneBothBossesRevivedTrigger::IsActive()
|
|||||||
|
|
||||||
bool WizardOfOzNeedTargetPriorityTrigger::IsActive()
|
bool WizardOfOzNeedTargetPriorityTrigger::IsActive()
|
||||||
{
|
{
|
||||||
if (!IsInstanceTimerManager(botAI, bot))
|
if (!IsMechanicTrackerBot(botAI, bot, KARAZHAN_MAP_ID, nullptr))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
Unit* dorothee = AI_VALUE2(Unit*, "find target", "dorothee");
|
Unit* dorothee = AI_VALUE2(Unit*, "find target", "dorothee");
|
||||||
@@ -178,7 +179,7 @@ bool TheCuratorBossAstralFlaresCastArcingSearTrigger::IsActive()
|
|||||||
|
|
||||||
bool TerestianIllhoofNeedTargetPriorityTrigger::IsActive()
|
bool TerestianIllhoofNeedTargetPriorityTrigger::IsActive()
|
||||||
{
|
{
|
||||||
if (!IsInstanceTimerManager(botAI, bot))
|
if (!IsMechanicTrackerBot(botAI, bot, KARAZHAN_MAP_ID, nullptr))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
Unit* illhoof = AI_VALUE2(Unit*, "find target", "terestian illhoof");
|
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
|
// Exclusion of Banish is so the player may Banish elementals if they wish
|
||||||
bool ShadeOfAranConjuredElementalsSummonedTrigger::IsActive()
|
bool ShadeOfAranConjuredElementalsSummonedTrigger::IsActive()
|
||||||
{
|
{
|
||||||
if (!IsInstanceTimerManager(botAI, bot))
|
if (!IsMechanicTrackerBot(botAI, bot, KARAZHAN_MAP_ID, nullptr))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
Unit* elemental = AI_VALUE2(Unit*, "find target", "conjured elemental");
|
Unit* elemental = AI_VALUE2(Unit*, "find target", "conjured elemental");
|
||||||
@@ -279,7 +280,7 @@ bool NetherspiteBossIsBanishedTrigger::IsActive()
|
|||||||
|
|
||||||
bool NetherspiteNeedToManageTimersAndTrackersTrigger::IsActive()
|
bool NetherspiteNeedToManageTimersAndTrackersTrigger::IsActive()
|
||||||
{
|
{
|
||||||
if (!botAI->IsTank(bot) && !IsInstanceTimerManager(botAI, bot))
|
if (!botAI->IsTank(bot) && !IsMechanicTrackerBot(botAI, bot, KARAZHAN_MAP_ID, nullptr))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
Unit* netherspite = AI_VALUE2(Unit*, "find target", "netherspite");
|
Unit* netherspite = AI_VALUE2(Unit*, "find target", "netherspite");
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
#include "RaidKarazhanHelpers.h"
|
#include "RaidKarazhanHelpers.h"
|
||||||
#include "RaidKarazhanActions.h"
|
#include "RaidKarazhanActions.h"
|
||||||
#include "Playerbots.h"
|
#include "Playerbots.h"
|
||||||
#include "RtiTargetValue.h"
|
|
||||||
|
|
||||||
namespace KarazhanHelpers
|
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_FLIGHT_STACK_POSITION = { -11159.555f, -1893.526f, 91.473f }; // Broken Barrel
|
||||||
const Position NIGHTBANE_RAIN_OF_BONES_POSITION = { -11165.233f, -1911.123f, 91.473f };
|
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)
|
Unit* GetFirstAliveUnit(const std::vector<Unit*>& units)
|
||||||
{
|
{
|
||||||
for (Unit* unit : units)
|
for (Unit* unit : units)
|
||||||
@@ -132,44 +62,6 @@ namespace KarazhanHelpers
|
|||||||
return nullptr;
|
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)
|
bool IsFlameWreathActive(PlayerbotAI* botAI, Player* bot)
|
||||||
{
|
{
|
||||||
Unit* aran = botAI->GetAiObjectContext()->GetValue<Unit*>("find target", "shade of aran")->Get();
|
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 = 15550,
|
||||||
NPC_ATTUMEN_THE_HUNTSMAN_MOUNTED = 16152,
|
NPC_ATTUMEN_THE_HUNTSMAN_MOUNTED = 16152,
|
||||||
|
|
||||||
|
// Terestian Illhoof
|
||||||
|
NPC_TERESTIAN_ILLHOOF = 15688,
|
||||||
|
NPC_DEMON_CHAINS = 17248,
|
||||||
|
NPC_KILREK = 17229,
|
||||||
|
|
||||||
// Shade of Aran
|
// Shade of Aran
|
||||||
NPC_CONJURED_ELEMENTAL = 17167,
|
NPC_CONJURED_ELEMENTAL = 17167,
|
||||||
|
|
||||||
@@ -74,8 +79,8 @@ namespace KarazhanHelpers
|
|||||||
NPC_NETHERSPITE_INFERNAL = 17646,
|
NPC_NETHERSPITE_INFERNAL = 17646,
|
||||||
};
|
};
|
||||||
|
|
||||||
const uint32 KARAZHAN_MAP_ID = 532;
|
constexpr uint32 KARAZHAN_MAP_ID = 532;
|
||||||
const float NIGHTBANE_FLIGHT_Z = 95.0f;
|
constexpr float NIGHTBANE_FLIGHT_Z = 95.0f;
|
||||||
|
|
||||||
// Attumen the Huntsman
|
// Attumen the Huntsman
|
||||||
extern std::unordered_map<uint32, time_t> attumenDpsWaitTimer;
|
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_FLIGHT_STACK_POSITION;
|
||||||
extern const Position NIGHTBANE_RAIN_OF_BONES_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* GetFirstAliveUnit(const std::vector<Unit*>& units);
|
||||||
Unit* GetFirstAliveUnitByEntry(PlayerbotAI* botAI, uint32 entry);
|
|
||||||
Unit* GetNearestPlayerInRadius(Player* bot, float radius);
|
|
||||||
bool IsFlameWreathActive(PlayerbotAI* botAI, Player* bot);
|
bool IsFlameWreathActive(PlayerbotAI* botAI, Player* bot);
|
||||||
std::vector<Player*> GetRedBlockers(PlayerbotAI* botAI, Player* bot);
|
std::vector<Player*> GetRedBlockers(PlayerbotAI* botAI, Player* bot);
|
||||||
std::vector<Player*> GetBlueBlockers(PlayerbotAI* botAI, Player* bot);
|
std::vector<Player*> GetBlueBlockers(PlayerbotAI* botAI, Player* bot);
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
#include "ObjectAccessor.h"
|
#include "ObjectAccessor.h"
|
||||||
#include "ObjectGuid.h"
|
#include "ObjectGuid.h"
|
||||||
#include "Playerbots.h"
|
#include "Playerbots.h"
|
||||||
|
#include "RaidBossHelpers.h"
|
||||||
|
|
||||||
using namespace MagtheridonHelpers;
|
using namespace MagtheridonHelpers;
|
||||||
|
|
||||||
@@ -14,46 +15,45 @@ bool MagtheridonMainTankAttackFirstThreeChannelersAction::Execute(Event event)
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
Creature* channelerSquare = GetChanneler(bot, SOUTH_CHANNELER);
|
Creature* channelerSquare = GetChanneler(bot, SOUTH_CHANNELER);
|
||||||
if (channelerSquare && channelerSquare->IsAlive())
|
if (channelerSquare)
|
||||||
MarkTargetWithSquare(bot, channelerSquare);
|
MarkTargetWithSquare(bot, channelerSquare);
|
||||||
|
|
||||||
Creature* channelerStar = GetChanneler(bot, WEST_CHANNELER);
|
Creature* channelerStar = GetChanneler(bot, WEST_CHANNELER);
|
||||||
if (channelerStar && channelerStar->IsAlive())
|
if (channelerStar)
|
||||||
MarkTargetWithStar(bot, channelerStar);
|
MarkTargetWithStar(bot, channelerStar);
|
||||||
|
|
||||||
Creature* channelerCircle = GetChanneler(bot, EAST_CHANNELER);
|
Creature* channelerCircle = GetChanneler(bot, EAST_CHANNELER);
|
||||||
if (channelerCircle && channelerCircle->IsAlive())
|
if (channelerCircle)
|
||||||
MarkTargetWithCircle(bot, channelerCircle);
|
MarkTargetWithCircle(bot, channelerCircle);
|
||||||
|
|
||||||
// After first three channelers are dead, wait for Magtheridon to activate
|
// After first three channelers are dead, wait for Magtheridon to activate
|
||||||
if ((!channelerSquare || !channelerSquare->IsAlive()) &&
|
if (!channelerSquare && !channelerStar && !channelerCircle)
|
||||||
(!channelerStar || !channelerStar->IsAlive()) &&
|
|
||||||
(!channelerCircle || !channelerCircle->IsAlive()))
|
|
||||||
{
|
{
|
||||||
const Location& position = MagtheridonsLairLocations::WaitingForMagtheridonPosition;
|
const Position& position = WAITING_FOR_MAGTHERIDON_POSITION;
|
||||||
if (!bot->IsWithinDist2d(position.x, position.y, 2.0f))
|
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);
|
MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
bot->SetFacingTo(position.orientation);
|
bot->SetFacingTo(position.GetOrientation());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Creature* currentTarget = nullptr;
|
Creature* currentTarget = nullptr;
|
||||||
std::string rtiName;
|
std::string rtiName;
|
||||||
if (channelerSquare && channelerSquare->IsAlive())
|
if (channelerSquare)
|
||||||
{
|
{
|
||||||
currentTarget = channelerSquare;
|
currentTarget = channelerSquare;
|
||||||
rtiName = "square";
|
rtiName = "square";
|
||||||
}
|
}
|
||||||
else if (channelerStar && channelerStar->IsAlive())
|
else if (channelerStar)
|
||||||
{
|
{
|
||||||
currentTarget = channelerStar;
|
currentTarget = channelerStar;
|
||||||
rtiName = "star";
|
rtiName = "star";
|
||||||
}
|
}
|
||||||
else if (channelerCircle && channelerCircle->IsAlive())
|
else if (channelerCircle)
|
||||||
{
|
{
|
||||||
currentTarget = channelerCircle;
|
currentTarget = channelerCircle;
|
||||||
rtiName = "circle";
|
rtiName = "circle";
|
||||||
@@ -70,7 +70,7 @@ bool MagtheridonMainTankAttackFirstThreeChannelersAction::Execute(Event event)
|
|||||||
bool MagtheridonFirstAssistTankAttackNWChannelerAction::Execute(Event event)
|
bool MagtheridonFirstAssistTankAttackNWChannelerAction::Execute(Event event)
|
||||||
{
|
{
|
||||||
Creature* channelerDiamond = GetChanneler(bot, NORTHWEST_CHANNELER);
|
Creature* channelerDiamond = GetChanneler(bot, NORTHWEST_CHANNELER);
|
||||||
if (!channelerDiamond || !channelerDiamond->IsAlive())
|
if (!channelerDiamond)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
MarkTargetWithDiamond(bot, channelerDiamond);
|
MarkTargetWithDiamond(bot, channelerDiamond);
|
||||||
@@ -81,18 +81,18 @@ bool MagtheridonFirstAssistTankAttackNWChannelerAction::Execute(Event event)
|
|||||||
|
|
||||||
if (channelerDiamond->GetVictim() == bot)
|
if (channelerDiamond->GetVictim() == bot)
|
||||||
{
|
{
|
||||||
const Location& position = MagtheridonsLairLocations::NWChannelerTankPosition;
|
const Position& position = NW_CHANNELER_TANK_POSITION;
|
||||||
const float maxDistance = 3.0f;
|
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 dX = position.GetPositionX() - bot->GetPositionX();
|
||||||
float dY = position.y - bot->GetPositionY();
|
float dY = position.GetPositionY() - bot->GetPositionY();
|
||||||
float dist = sqrt(dX * dX + dY * dY);
|
float moveX = bot->GetPositionX() + (dX / distanceToPosition) * maxDistance;
|
||||||
float moveX = bot->GetPositionX() + (dX / dist) * maxDistance;
|
float moveY = bot->GetPositionY() + (dY / distanceToPosition) * maxDistance;
|
||||||
float moveY = bot->GetPositionY() + (dY / dist) * 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);
|
MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -103,7 +103,7 @@ bool MagtheridonFirstAssistTankAttackNWChannelerAction::Execute(Event event)
|
|||||||
bool MagtheridonSecondAssistTankAttackNEChannelerAction::Execute(Event event)
|
bool MagtheridonSecondAssistTankAttackNEChannelerAction::Execute(Event event)
|
||||||
{
|
{
|
||||||
Creature* channelerTriangle = GetChanneler(bot, NORTHEAST_CHANNELER);
|
Creature* channelerTriangle = GetChanneler(bot, NORTHEAST_CHANNELER);
|
||||||
if (!channelerTriangle || !channelerTriangle->IsAlive())
|
if (!channelerTriangle)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
MarkTargetWithTriangle(bot, channelerTriangle);
|
MarkTargetWithTriangle(bot, channelerTriangle);
|
||||||
@@ -114,18 +114,18 @@ bool MagtheridonSecondAssistTankAttackNEChannelerAction::Execute(Event event)
|
|||||||
|
|
||||||
if (channelerTriangle->GetVictim() == bot)
|
if (channelerTriangle->GetVictim() == bot)
|
||||||
{
|
{
|
||||||
const Location& position = MagtheridonsLairLocations::NEChannelerTankPosition;
|
const Position& position = NE_CHANNELER_TANK_POSITION;
|
||||||
const float maxDistance = 3.0f;
|
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 dX = position.GetPositionX() - bot->GetPositionX();
|
||||||
float dY = position.y - bot->GetPositionY();
|
float dY = position.GetPositionY() - bot->GetPositionY();
|
||||||
float dist = sqrt(dX * dX + dY * dY);
|
float moveX = bot->GetPositionX() + (dX / distanceToPosition) * maxDistance;
|
||||||
float moveX = bot->GetPositionX() + (dX / dist) * maxDistance;
|
float moveY = bot->GetPositionY() + (dY / distanceToPosition) * maxDistance;
|
||||||
float moveY = bot->GetPositionY() + (dY / dist) * 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);
|
MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -175,7 +175,7 @@ bool MagtheridonMisdirectHellfireChannelers::Execute(Event event)
|
|||||||
switch (hunterIndex)
|
switch (hunterIndex)
|
||||||
{
|
{
|
||||||
case 0:
|
case 0:
|
||||||
if (mainTank && channelerStar && channelerStar->IsAlive() &&
|
if (mainTank && channelerStar &&
|
||||||
channelerStar->GetVictim() != mainTank)
|
channelerStar->GetVictim() != mainTank)
|
||||||
{
|
{
|
||||||
if (botAI->CanCastSpell("misdirection", mainTank))
|
if (botAI->CanCastSpell("misdirection", mainTank))
|
||||||
@@ -190,7 +190,7 @@ bool MagtheridonMisdirectHellfireChannelers::Execute(Event event)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case 1:
|
case 1:
|
||||||
if (mainTank && channelerCircle && channelerCircle->IsAlive() &&
|
if (mainTank && channelerCircle &&
|
||||||
channelerCircle->GetVictim() != mainTank)
|
channelerCircle->GetVictim() != mainTank)
|
||||||
{
|
{
|
||||||
if (botAI->CanCastSpell("misdirection", mainTank))
|
if (botAI->CanCastSpell("misdirection", mainTank))
|
||||||
@@ -215,91 +215,70 @@ bool MagtheridonAssignDPSPriorityAction::Execute(Event event)
|
|||||||
{
|
{
|
||||||
// Listed in order of priority
|
// Listed in order of priority
|
||||||
Creature* channelerSquare = GetChanneler(bot, SOUTH_CHANNELER);
|
Creature* channelerSquare = GetChanneler(bot, SOUTH_CHANNELER);
|
||||||
if (channelerSquare && channelerSquare->IsAlive())
|
if (channelerSquare)
|
||||||
{
|
{
|
||||||
SetRtiTarget(botAI, "square", channelerSquare);
|
SetRtiTarget(botAI, "square", channelerSquare);
|
||||||
|
|
||||||
if (bot->GetTarget() != channelerSquare->GetGUID())
|
if (bot->GetTarget() != channelerSquare->GetGUID())
|
||||||
{
|
|
||||||
bot->SetSelection(channelerSquare->GetGUID());
|
|
||||||
return Attack(channelerSquare);
|
return Attack(channelerSquare);
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Creature* channelerStar = GetChanneler(bot, WEST_CHANNELER);
|
Creature* channelerStar = GetChanneler(bot, WEST_CHANNELER);
|
||||||
if (channelerStar && channelerStar->IsAlive())
|
if (channelerStar)
|
||||||
{
|
{
|
||||||
SetRtiTarget(botAI, "star", channelerStar);
|
SetRtiTarget(botAI, "star", channelerStar);
|
||||||
|
|
||||||
if (bot->GetTarget() != channelerStar->GetGUID())
|
if (bot->GetTarget() != channelerStar->GetGUID())
|
||||||
{
|
|
||||||
bot->SetSelection(channelerStar->GetGUID());
|
|
||||||
return Attack(channelerStar);
|
return Attack(channelerStar);
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Creature* channelerCircle = GetChanneler(bot, EAST_CHANNELER);
|
Creature* channelerCircle = GetChanneler(bot, EAST_CHANNELER);
|
||||||
if (channelerCircle && channelerCircle->IsAlive())
|
if (channelerCircle)
|
||||||
{
|
{
|
||||||
SetRtiTarget(botAI, "circle", channelerCircle);
|
SetRtiTarget(botAI, "circle", channelerCircle);
|
||||||
|
|
||||||
if (bot->GetTarget() != channelerCircle->GetGUID())
|
if (bot->GetTarget() != channelerCircle->GetGUID())
|
||||||
{
|
|
||||||
bot->SetSelection(channelerCircle->GetGUID());
|
|
||||||
return Attack(channelerCircle);
|
return Attack(channelerCircle);
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Creature* channelerDiamond = GetChanneler(bot, NORTHWEST_CHANNELER);
|
Creature* channelerDiamond = GetChanneler(bot, NORTHWEST_CHANNELER);
|
||||||
if (channelerDiamond && channelerDiamond->IsAlive())
|
if (channelerDiamond)
|
||||||
{
|
{
|
||||||
SetRtiTarget(botAI, "diamond", channelerDiamond);
|
SetRtiTarget(botAI, "diamond", channelerDiamond);
|
||||||
|
|
||||||
if (bot->GetTarget() != channelerDiamond->GetGUID())
|
if (bot->GetTarget() != channelerDiamond->GetGUID())
|
||||||
{
|
|
||||||
bot->SetSelection(channelerDiamond->GetGUID());
|
|
||||||
return Attack(channelerDiamond);
|
return Attack(channelerDiamond);
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Creature* channelerTriangle = GetChanneler(bot, NORTHEAST_CHANNELER);
|
Creature* channelerTriangle = GetChanneler(bot, NORTHEAST_CHANNELER);
|
||||||
if (channelerTriangle && channelerTriangle->IsAlive())
|
if (channelerTriangle)
|
||||||
{
|
{
|
||||||
SetRtiTarget(botAI, "triangle", channelerTriangle);
|
SetRtiTarget(botAI, "triangle", channelerTriangle);
|
||||||
|
|
||||||
if (bot->GetTarget() != channelerTriangle->GetGUID())
|
if (bot->GetTarget() != channelerTriangle->GetGUID())
|
||||||
{
|
|
||||||
bot->SetSelection(channelerTriangle->GetGUID());
|
|
||||||
return Attack(channelerTriangle);
|
return Attack(channelerTriangle);
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Unit* magtheridon = AI_VALUE2(Unit*, "find target", "magtheridon");
|
Unit* magtheridon = AI_VALUE2(Unit*, "find target", "magtheridon");
|
||||||
if (magtheridon && !magtheridon->HasAura(SPELL_SHADOW_CAGE) &&
|
if (magtheridon && !magtheridon->HasAura(SPELL_SHADOW_CAGE) &&
|
||||||
(!channelerSquare || !channelerSquare->IsAlive()) &&
|
!channelerSquare && !channelerStar && !channelerCircle &&
|
||||||
(!channelerStar || !channelerStar->IsAlive()) &&
|
!channelerDiamond && !channelerTriangle)
|
||||||
(!channelerCircle || !channelerCircle->IsAlive()) &&
|
|
||||||
(!channelerDiamond || !channelerDiamond->IsAlive()) &&
|
|
||||||
(!channelerTriangle || !channelerTriangle->IsAlive()))
|
|
||||||
{
|
{
|
||||||
SetRtiTarget(botAI, "cross", magtheridon);
|
SetRtiTarget(botAI, "cross", magtheridon);
|
||||||
|
|
||||||
if (bot->GetTarget() != magtheridon->GetGUID())
|
if (bot->GetTarget() != magtheridon->GetGUID())
|
||||||
{
|
|
||||||
bot->SetSelection(magtheridon->GetGUID());
|
|
||||||
return Attack(magtheridon);
|
return Attack(magtheridon);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -343,15 +322,15 @@ bool MagtheridonWarlockCCBurningAbyssalAction::Execute(Event event)
|
|||||||
if (warlockIndex >= 0 && warlockIndex < abyssals.size())
|
if (warlockIndex >= 0 && warlockIndex < abyssals.size())
|
||||||
{
|
{
|
||||||
Unit* assignedAbyssal = abyssals[warlockIndex];
|
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);
|
return botAI->CastSpell("banish", assignedAbyssal);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (size_t i = warlocks.size(); i < abyssals.size(); ++i)
|
for (size_t i = warlocks.size(); i < abyssals.size(); ++i)
|
||||||
{
|
{
|
||||||
Unit* excessAbyssal = abyssals[i];
|
Unit* excessAbyssal = abyssals[i];
|
||||||
if (!excessAbyssal->HasAura(SPELL_BANISH) && !excessAbyssal->HasAura(SPELL_FEAR) &&
|
if (!botAI->HasAura("banish", excessAbyssal) && !botAI->HasAura("fear", excessAbyssal) &&
|
||||||
botAI->CanCastSpell(SPELL_FEAR, excessAbyssal, true))
|
botAI->CanCastSpell("fear", excessAbyssal))
|
||||||
return botAI->CastSpell("fear", excessAbyssal);
|
return botAI->CastSpell("fear", excessAbyssal);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -373,22 +352,20 @@ bool MagtheridonMainTankPositionBossAction::Execute(Event event)
|
|||||||
|
|
||||||
if (magtheridon->GetVictim() == bot)
|
if (magtheridon->GetVictim() == bot)
|
||||||
{
|
{
|
||||||
const Location& position = MagtheridonsLairLocations::MagtheridonTankPosition;
|
const Position& position = MAGTHERIDON_TANK_POSITION;
|
||||||
const float maxDistance = 2.0f;
|
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 dX = position.GetPositionX() - bot->GetPositionX();
|
||||||
float dY = position.y - bot->GetPositionY();
|
float dY = position.GetPositionY() - bot->GetPositionY();
|
||||||
float dist = sqrt(dX * dX + dY * dY);
|
float moveX = bot->GetPositionX() + (dX / distanceToPosition) * maxDistance;
|
||||||
float moveX = bot->GetPositionX() + (dX / dist) * maxDistance;
|
float moveY = bot->GetPositionY() + (dY / distanceToPosition) * maxDistance;
|
||||||
float moveY = bot->GetPositionY() + (dY / dist) * 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);
|
MovementPriority::MOVEMENT_COMBAT, true, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
bot->SetFacingTo(position.orientation);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@@ -440,13 +417,13 @@ bool MagtheridonSpreadRangedAction::Execute(Event event)
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool isHealer = botAI->IsHeal(bot);
|
bool isHealer = botAI->IsHeal(bot);
|
||||||
const Location& center = isHealer
|
const Position& center = isHealer
|
||||||
? MagtheridonsLairLocations::HealerSpreadPosition
|
? HEALER_SPREAD_POSITION
|
||||||
: MagtheridonsLairLocations::RangedSpreadPosition;
|
: RANGED_SPREAD_POSITION;
|
||||||
float maxSpreadRadius = isHealer ? 15.0f : 20.0f;
|
float maxSpreadRadius = isHealer ? 15.0f : 20.0f;
|
||||||
float centerX = center.x;
|
float centerX = center.GetPositionX();
|
||||||
float centerY = center.y;
|
float centerY = center.GetPositionY();
|
||||||
float centerZ = bot->GetPositionZ();
|
float centerZ = center.GetPositionZ();
|
||||||
const float radiusBuffer = 3.0f;
|
const float radiusBuffer = 3.0f;
|
||||||
|
|
||||||
if (!initialPositions.count(bot->GetGUID()))
|
if (!initialPositions.count(bot->GetGUID()))
|
||||||
@@ -479,7 +456,7 @@ bool MagtheridonSpreadRangedAction::Execute(Event event)
|
|||||||
|
|
||||||
bot->AttackStop();
|
bot->AttackStop();
|
||||||
bot->InterruptNonMeleeSpells(false);
|
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);
|
MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||||
}
|
}
|
||||||
hasReachedInitialPosition[bot->GetGUID()] = true;
|
hasReachedInitialPosition[bot->GetGUID()] = true;
|
||||||
@@ -499,7 +476,7 @@ bool MagtheridonSpreadRangedAction::Execute(Event event)
|
|||||||
{
|
{
|
||||||
bot->AttackStop();
|
bot->AttackStop();
|
||||||
bot->InterruptNonMeleeSpells(false);
|
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);
|
MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -593,7 +570,7 @@ bool MagtheridonUseManticronCubeAction::HandleWaitingPhase(const CubeInfo& cubeI
|
|||||||
{
|
{
|
||||||
bot->AttackStop();
|
bot->AttackStop();
|
||||||
bot->InterruptNonMeleeSpells(true);
|
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);
|
MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -603,7 +580,7 @@ bool MagtheridonUseManticronCubeAction::HandleWaitingPhase(const CubeInfo& cubeI
|
|||||||
float fallbackY = cubeInfo.y + sin(angle) * safeWaitDistance;
|
float fallbackY = cubeInfo.y + sin(angle) * safeWaitDistance;
|
||||||
float fallbackZ = bot->GetPositionZ();
|
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);
|
MovementPriority::MOVEMENT_COMBAT, true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -638,7 +615,7 @@ bool MagtheridonUseManticronCubeAction::HandleCubeInteraction(const CubeInfo& cu
|
|||||||
|
|
||||||
bot->AttackStop();
|
bot->AttackStop();
|
||||||
bot->InterruptNonMeleeSpells(true);
|
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);
|
MovementPriority::MOVEMENT_FORCED, true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -663,14 +640,14 @@ bool MagtheridonManageTimersAndAssignmentsAction::Execute(Event event)
|
|||||||
magtheridon->FindCurrentSpellBySpellId(SPELL_BLAST_NOVA);
|
magtheridon->FindCurrentSpellBySpellId(SPELL_BLAST_NOVA);
|
||||||
bool lastBlastNova = lastBlastNovaState[instanceId];
|
bool lastBlastNova = lastBlastNovaState[instanceId];
|
||||||
|
|
||||||
if (lastBlastNova && !blastNovaActive && IsInstanceTimerManager(botAI, bot))
|
if (lastBlastNova && !blastNovaActive && IsMechanicTrackerBot(botAI, bot, MAGTHERIDON_MAP_ID, nullptr))
|
||||||
blastNovaTimer[instanceId] = now;
|
blastNovaTimer[instanceId] = now;
|
||||||
|
|
||||||
lastBlastNovaState[instanceId] = blastNovaActive;
|
lastBlastNovaState[instanceId] = blastNovaActive;
|
||||||
|
|
||||||
if (!magtheridon->HasAura(SPELL_SHADOW_CAGE))
|
if (!magtheridon->HasAura(SPELL_SHADOW_CAGE))
|
||||||
{
|
{
|
||||||
if (IsInstanceTimerManager(botAI, bot))
|
if (IsMechanicTrackerBot(botAI, bot, MAGTHERIDON_MAP_ID, nullptr))
|
||||||
{
|
{
|
||||||
spreadWaitTimer.try_emplace(instanceId, now);
|
spreadWaitTimer.try_emplace(instanceId, now);
|
||||||
blastNovaTimer.try_emplace(instanceId, now);
|
blastNovaTimer.try_emplace(instanceId, now);
|
||||||
@@ -679,11 +656,12 @@ bool MagtheridonManageTimersAndAssignmentsAction::Execute(Event event)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
MagtheridonSpreadRangedAction::initialPositions.clear();
|
ObjectGuid guid = bot->GetGUID();
|
||||||
MagtheridonSpreadRangedAction::hasReachedInitialPosition.clear();
|
MagtheridonSpreadRangedAction::initialPositions.erase(guid);
|
||||||
botToCubeAssignment.clear();
|
MagtheridonSpreadRangedAction::hasReachedInitialPosition.erase(guid);
|
||||||
|
botToCubeAssignment.erase(guid);
|
||||||
|
|
||||||
if (IsInstanceTimerManager(botAI, bot))
|
if (IsMechanicTrackerBot(botAI, bot, MAGTHERIDON_MAP_ID, nullptr))
|
||||||
{
|
{
|
||||||
spreadWaitTimer.erase(instanceId);
|
spreadWaitTimer.erase(instanceId);
|
||||||
blastNovaTimer.erase(instanceId);
|
blastNovaTimer.erase(instanceId);
|
||||||
|
|||||||
@@ -6,8 +6,6 @@
|
|||||||
#include "AttackAction.h"
|
#include "AttackAction.h"
|
||||||
#include "MovementActions.h"
|
#include "MovementActions.h"
|
||||||
|
|
||||||
using namespace MagtheridonHelpers;
|
|
||||||
|
|
||||||
class MagtheridonMainTankAttackFirstThreeChannelersAction : public AttackAction
|
class MagtheridonMainTankAttackFirstThreeChannelersAction : public AttackAction
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -85,8 +83,8 @@ public:
|
|||||||
private:
|
private:
|
||||||
bool HandleCubeRelease(Unit* magtheridon, GameObject* cube);
|
bool HandleCubeRelease(Unit* magtheridon, GameObject* cube);
|
||||||
bool ShouldActivateCubeLogic(Unit* magtheridon);
|
bool ShouldActivateCubeLogic(Unit* magtheridon);
|
||||||
bool HandleWaitingPhase(const CubeInfo& cubeInfo);
|
bool HandleWaitingPhase(const MagtheridonHelpers::CubeInfo& cubeInfo);
|
||||||
bool HandleCubeInteraction(const CubeInfo& cubeInfo, GameObject* cube);
|
bool HandleCubeInteraction(const MagtheridonHelpers::CubeInfo& cubeInfo, GameObject* cube);
|
||||||
};
|
};
|
||||||
|
|
||||||
class MagtheridonManageTimersAndAssignmentsAction : public Action
|
class MagtheridonManageTimersAndAssignmentsAction : public Action
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
#include "GenericSpellActions.h"
|
#include "GenericSpellActions.h"
|
||||||
#include "Playerbots.h"
|
#include "Playerbots.h"
|
||||||
#include "WarlockActions.h"
|
#include "WarlockActions.h"
|
||||||
|
#include "WipeAction.h"
|
||||||
|
|
||||||
using namespace MagtheridonHelpers;
|
using namespace MagtheridonHelpers;
|
||||||
|
|
||||||
@@ -24,9 +25,9 @@ float MagtheridonUseManticronCubeMultiplier::GetValue(Action* action)
|
|||||||
auto it = botToCubeAssignment.find(bot->GetGUID());
|
auto it = botToCubeAssignment.find(bot->GetGUID());
|
||||||
if (it != botToCubeAssignment.end())
|
if (it != botToCubeAssignment.end())
|
||||||
{
|
{
|
||||||
if (dynamic_cast<MagtheridonUseManticronCubeAction*>(action))
|
if (dynamic_cast<WipeAction*>(action))
|
||||||
return 1.0f;
|
return 1.0f;
|
||||||
|
else if (!dynamic_cast<MagtheridonUseManticronCubeAction*>(action))
|
||||||
return 0.0f;
|
return 0.0f;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -41,28 +42,31 @@ float MagtheridonWaitToAttackMultiplier::GetValue(Action* action)
|
|||||||
if (!magtheridon || magtheridon->HasAura(SPELL_SHADOW_CAGE))
|
if (!magtheridon || magtheridon->HasAura(SPELL_SHADOW_CAGE))
|
||||||
return 1.0f;
|
return 1.0f;
|
||||||
|
|
||||||
|
if (botAI->IsMainTank(bot))
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
const uint8 dpsWaitSeconds = 6;
|
const uint8 dpsWaitSeconds = 6;
|
||||||
auto it = dpsWaitTimer.find(magtheridon->GetMap()->GetInstanceId());
|
auto it = dpsWaitTimer.find(magtheridon->GetMap()->GetInstanceId());
|
||||||
if (it == dpsWaitTimer.end() ||
|
if (it == dpsWaitTimer.end() ||
|
||||||
(time(nullptr) - it->second) < dpsWaitSeconds)
|
(time(nullptr) - it->second) < dpsWaitSeconds)
|
||||||
{
|
{
|
||||||
if (!botAI->IsMainTank(bot) && (dynamic_cast<AttackAction*>(action) ||
|
if (dynamic_cast<AttackAction*>(action) ||
|
||||||
(!botAI->IsHeal(bot) && dynamic_cast<CastSpellAction*>(action))))
|
(!botAI->IsHeal(bot) && dynamic_cast<CastSpellAction*>(action)))
|
||||||
return 0.0f;
|
return 0.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 1.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)
|
float MagtheridonDisableOffTankAssistMultiplier::GetValue(Action* action)
|
||||||
{
|
{
|
||||||
Unit* magtheridon = AI_VALUE2(Unit*, "find target", "magtheridon");
|
Unit* magtheridon = AI_VALUE2(Unit*, "find target", "magtheridon");
|
||||||
Unit* channeler = AI_VALUE2(Unit*, "find target", "hellfire channeler");
|
|
||||||
if (!magtheridon)
|
if (!magtheridon)
|
||||||
return 1.0f;
|
return 1.0f;
|
||||||
|
|
||||||
|
if (bot->GetVictim() == nullptr)
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
if ((botAI->IsAssistTankOfIndex(bot, 0) || botAI->IsAssistTankOfIndex(bot, 1)) &&
|
if ((botAI->IsAssistTankOfIndex(bot, 0) || botAI->IsAssistTankOfIndex(bot, 1)) &&
|
||||||
dynamic_cast<TankAssistAction*>(action))
|
dynamic_cast<TankAssistAction*>(action))
|
||||||
return 0.0f;
|
return 0.0f;
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ bool MagtheridonNWChannelerEngagedByFirstAssistTankTrigger::IsActive()
|
|||||||
Creature* channelerDiamond = GetChanneler(bot, NORTHWEST_CHANNELER);
|
Creature* channelerDiamond = GetChanneler(bot, NORTHWEST_CHANNELER);
|
||||||
|
|
||||||
return magtheridon && botAI->IsAssistTankOfIndex(bot, 0) &&
|
return magtheridon && botAI->IsAssistTankOfIndex(bot, 0) &&
|
||||||
channelerDiamond && channelerDiamond->IsAlive();
|
channelerDiamond;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MagtheridonNEChannelerEngagedBySecondAssistTankTrigger::IsActive()
|
bool MagtheridonNEChannelerEngagedBySecondAssistTankTrigger::IsActive()
|
||||||
@@ -27,7 +27,7 @@ bool MagtheridonNEChannelerEngagedBySecondAssistTankTrigger::IsActive()
|
|||||||
Creature* channelerTriangle = GetChanneler(bot, NORTHEAST_CHANNELER);
|
Creature* channelerTriangle = GetChanneler(bot, NORTHEAST_CHANNELER);
|
||||||
|
|
||||||
return magtheridon && botAI->IsAssistTankOfIndex(bot, 1) &&
|
return magtheridon && botAI->IsAssistTankOfIndex(bot, 1) &&
|
||||||
channelerTriangle && channelerTriangle->IsAlive();
|
channelerTriangle;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MagtheridonPullingWestAndEastChannelersTrigger::IsActive()
|
bool MagtheridonPullingWestAndEastChannelersTrigger::IsActive()
|
||||||
@@ -38,8 +38,7 @@ bool MagtheridonPullingWestAndEastChannelersTrigger::IsActive()
|
|||||||
Creature* channelerCircle = GetChanneler(bot, EAST_CHANNELER);
|
Creature* channelerCircle = GetChanneler(bot, EAST_CHANNELER);
|
||||||
|
|
||||||
return magtheridon && bot->getClass() == CLASS_HUNTER &&
|
return magtheridon && bot->getClass() == CLASS_HUNTER &&
|
||||||
((channelerStar && channelerStar->IsAlive()) ||
|
(channelerStar || channelerCircle);
|
||||||
(channelerCircle && channelerCircle->IsAlive()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MagtheridonDeterminingKillOrderTrigger::IsActive()
|
bool MagtheridonDeterminingKillOrderTrigger::IsActive()
|
||||||
@@ -51,12 +50,11 @@ bool MagtheridonDeterminingKillOrderTrigger::IsActive()
|
|||||||
Creature* channelerTriangle = GetChanneler(bot, NORTHEAST_CHANNELER);
|
Creature* channelerTriangle = GetChanneler(bot, NORTHEAST_CHANNELER);
|
||||||
|
|
||||||
if (!magtheridon || botAI->IsHeal(bot) || botAI->IsMainTank(bot) ||
|
if (!magtheridon || botAI->IsHeal(bot) || botAI->IsMainTank(bot) ||
|
||||||
(botAI->IsAssistTankOfIndex(bot, 0) && channelerDiamond && channelerDiamond->IsAlive()) ||
|
(botAI->IsAssistTankOfIndex(bot, 0) && channelerDiamond) ||
|
||||||
(botAI->IsAssistTankOfIndex(bot, 1) && channelerTriangle && channelerTriangle->IsAlive()))
|
(botAI->IsAssistTankOfIndex(bot, 1) && channelerTriangle))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return (channeler && channeler->IsAlive()) || (magtheridon &&
|
return channeler || (magtheridon && !magtheridon->HasAura(SPELL_SHADOW_CAGE));
|
||||||
!magtheridon->HasAura(SPELL_SHADOW_CAGE));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MagtheridonBurningAbyssalSpawnedTrigger::IsActive()
|
bool MagtheridonBurningAbyssalSpawnedTrigger::IsActive()
|
||||||
@@ -84,10 +82,8 @@ bool MagtheridonBossEngagedByMainTankTrigger::IsActive()
|
|||||||
bool MagtheridonBossEngagedByRangedTrigger::IsActive()
|
bool MagtheridonBossEngagedByRangedTrigger::IsActive()
|
||||||
{
|
{
|
||||||
Unit* magtheridon = AI_VALUE2(Unit*, "find target", "magtheridon");
|
Unit* magtheridon = AI_VALUE2(Unit*, "find target", "magtheridon");
|
||||||
Unit* channeler = AI_VALUE2(Unit*, "find target", "hellfire channeler");
|
|
||||||
|
|
||||||
return magtheridon && botAI->IsRanged(bot) &&
|
return magtheridon && !magtheridon->HasAura(SPELL_SHADOW_CAGE) && botAI->IsRanged(bot);
|
||||||
!(channeler && channeler->IsAlive());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MagtheridonIncomingBlastNovaTrigger::IsActive()
|
bool MagtheridonIncomingBlastNovaTrigger::IsActive()
|
||||||
@@ -122,7 +118,5 @@ bool MagtheridonIncomingBlastNovaTrigger::IsActive()
|
|||||||
|
|
||||||
bool MagtheridonNeedToManageTimersAndAssignmentsTrigger::IsActive()
|
bool MagtheridonNeedToManageTimersAndAssignmentsTrigger::IsActive()
|
||||||
{
|
{
|
||||||
Unit* magtheridon = AI_VALUE2(Unit*, "find target", "magtheridon");
|
return AI_VALUE2(Unit*, "find target", "magtheridon");
|
||||||
|
|
||||||
return magtheridon;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,22 +1,18 @@
|
|||||||
#include "RaidMagtheridonHelpers.h"
|
#include "RaidMagtheridonHelpers.h"
|
||||||
#include "Creature.h"
|
#include "Creature.h"
|
||||||
#include "GameObject.h"
|
#include "GameObject.h"
|
||||||
#include "GroupReference.h"
|
|
||||||
#include "Map.h"
|
#include "Map.h"
|
||||||
#include "ObjectGuid.h"
|
#include "ObjectGuid.h"
|
||||||
#include "Playerbots.h"
|
#include "Playerbots.h"
|
||||||
|
|
||||||
namespace MagtheridonHelpers
|
namespace MagtheridonHelpers
|
||||||
{
|
{
|
||||||
namespace MagtheridonsLairLocations
|
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 Location WaitingForMagtheridonPosition = { 1.359f, 2.048f, -0.406f, 3.135f };
|
const Position NW_CHANNELER_TANK_POSITION = { -11.764f, 30.818f, -0.411f, 0.0f };
|
||||||
const Location MagtheridonTankPosition = { 22.827f, 2.105f, -0.406f, 3.135f };
|
const Position NE_CHANNELER_TANK_POSITION = { -12.490f, -26.211f, -0.411f, 0.0f };
|
||||||
const Location NWChannelerTankPosition = { -11.764f, 30.818f, -0.411f, 0.0f };
|
const Position RANGED_SPREAD_POSITION = { -14.890f, 1.995f, -0.406f, 0.0f };
|
||||||
const Location NEChannelerTankPosition = { -12.490f, -26.211f, -0.411f, 0.0f };
|
const Position HEALER_SPREAD_POSITION = { -2.265f, 1.874f, -0.404f, 0.0f };
|
||||||
const Location RangedSpreadPosition = { -14.890f, 1.995f, -0.406f, 0.0f };
|
|
||||||
const Location HealerSpreadPosition = { -2.265f, 1.874f, -0.404f, 0.0f };
|
|
||||||
}
|
|
||||||
|
|
||||||
// Identify channelers by their database GUIDs
|
// Identify channelers by their database GUIDs
|
||||||
Creature* GetChanneler(Player* bot, uint32 dbGuid)
|
Creature* GetChanneler(Player* bot, uint32 dbGuid)
|
||||||
@@ -29,63 +25,11 @@ namespace MagtheridonHelpers
|
|||||||
if (it == map->GetCreatureBySpawnIdStore().end())
|
if (it == map->GetCreatureBySpawnIdStore().end())
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
return it->second;
|
Creature* channeler = it->second;
|
||||||
}
|
if (!channeler->IsAlive())
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
void MarkTargetWithIcon(Player* bot, Unit* target, uint8 iconId)
|
return channeler;
|
||||||
{
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::vector<uint32> MANTICRON_CUBE_DB_GUIDS = { 43157, 43158, 43159, 43160, 43161 };
|
const std::vector<uint32> MANTICRON_CUBE_DB_GUIDS = { 43157, 43158, 43159, 43160, 43161 };
|
||||||
@@ -208,19 +152,4 @@ namespace MagtheridonHelpers
|
|||||||
|
|
||||||
return true;
|
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 "Group.h"
|
||||||
#include "ObjectGuid.h"
|
#include "ObjectGuid.h"
|
||||||
#include "PlayerbotAI.h"
|
#include "PlayerbotAI.h"
|
||||||
#include "RtiTargetValue.h"
|
|
||||||
|
|
||||||
namespace MagtheridonHelpers
|
namespace MagtheridonHelpers
|
||||||
{
|
{
|
||||||
@@ -19,10 +18,6 @@ namespace MagtheridonHelpers
|
|||||||
SPELL_BLAST_NOVA = 30616,
|
SPELL_BLAST_NOVA = 30616,
|
||||||
SPELL_SHADOW_GRASP = 30410,
|
SPELL_SHADOW_GRASP = 30410,
|
||||||
|
|
||||||
// Warlock
|
|
||||||
SPELL_BANISH = 18647,
|
|
||||||
SPELL_FEAR = 6215,
|
|
||||||
|
|
||||||
// Hunter
|
// Hunter
|
||||||
SPELL_MISDIRECTION = 35079,
|
SPELL_MISDIRECTION = 35079,
|
||||||
};
|
};
|
||||||
@@ -38,6 +33,7 @@ namespace MagtheridonHelpers
|
|||||||
GO_BLAZE = 181832,
|
GO_BLAZE = 181832,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
constexpr uint32 MAGTHERIDON_MAP_ID = 544;
|
||||||
constexpr uint32 SOUTH_CHANNELER = 90978;
|
constexpr uint32 SOUTH_CHANNELER = 90978;
|
||||||
constexpr uint32 WEST_CHANNELER = 90979;
|
constexpr uint32 WEST_CHANNELER = 90979;
|
||||||
constexpr uint32 NORTHWEST_CHANNELER = 90980;
|
constexpr uint32 NORTHWEST_CHANNELER = 90980;
|
||||||
@@ -45,31 +41,14 @@ namespace MagtheridonHelpers
|
|||||||
constexpr uint32 NORTHEAST_CHANNELER = 90981;
|
constexpr uint32 NORTHEAST_CHANNELER = 90981;
|
||||||
|
|
||||||
Creature* GetChanneler(Player* bot, uint32 dbGuid);
|
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 IsSafeFromMagtheridonHazards(PlayerbotAI* botAI, Player* bot, float x, float y, float z);
|
||||||
bool IsInstanceTimerManager(PlayerbotAI* botAI, Player* bot);
|
|
||||||
|
|
||||||
struct Location
|
extern const Position WAITING_FOR_MAGTHERIDON_POSITION;
|
||||||
{
|
extern const Position MAGTHERIDON_TANK_POSITION;
|
||||||
float x, y, z, orientation;
|
extern const Position NW_CHANNELER_TANK_POSITION;
|
||||||
};
|
extern const Position NE_CHANNELER_TANK_POSITION;
|
||||||
|
extern const Position RANGED_SPREAD_POSITION;
|
||||||
namespace MagtheridonsLairLocations
|
extern const Position HEALER_SPREAD_POSITION;
|
||||||
{
|
|
||||||
extern const Location WaitingForMagtheridonPosition;
|
|
||||||
extern const Location MagtheridonTankPosition;
|
|
||||||
extern const Location NWChannelerTankPosition;
|
|
||||||
extern const Location NEChannelerTankPosition;
|
|
||||||
extern const Location RangedSpreadPosition;
|
|
||||||
extern const Location HealerSpreadPosition;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct CubeInfo
|
struct CubeInfo
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
#define _PLAYERBOT_RAIDMCACTIONCONTEXT_H
|
#define _PLAYERBOT_RAIDMCACTIONCONTEXT_H
|
||||||
|
|
||||||
#include "Action.h"
|
#include "Action.h"
|
||||||
|
#include "BossAuraActions.h"
|
||||||
#include "NamedObjectContext.h"
|
#include "NamedObjectContext.h"
|
||||||
#include "RaidMcActions.h"
|
#include "RaidMcActions.h"
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
#define _PLAYERBOT_RAIDMCTRIGGERCONTEXT_H
|
#define _PLAYERBOT_RAIDMCTRIGGERCONTEXT_H
|
||||||
|
|
||||||
#include "AiObjectContext.h"
|
#include "AiObjectContext.h"
|
||||||
|
#include "BossAuraTriggers.h"
|
||||||
#include "NamedObjectContext.h"
|
#include "NamedObjectContext.h"
|
||||||
#include "RaidMcTriggers.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-os"] = &RaidStrategyContext::wotlk_os;
|
||||||
creators["wotlk-eoe"] = &RaidStrategyContext::wotlk_eoe;
|
creators["wotlk-eoe"] = &RaidStrategyContext::wotlk_eoe;
|
||||||
creators["voa"] = &RaidStrategyContext::voa;
|
creators["voa"] = &RaidStrategyContext::voa;
|
||||||
creators["uld"] = &RaidStrategyContext::uld;
|
creators["ulduar"] = &RaidStrategyContext::ulduar;
|
||||||
creators["onyxia"] = &RaidStrategyContext::onyxia;
|
creators["onyxia"] = &RaidStrategyContext::onyxia;
|
||||||
creators["icc"] = &RaidStrategyContext::icc;
|
creators["icc"] = &RaidStrategyContext::icc;
|
||||||
}
|
}
|
||||||
@@ -45,7 +45,7 @@ private:
|
|||||||
static Strategy* wotlk_eoe(PlayerbotAI* botAI) { return new RaidEoEStrategy(botAI); }
|
static Strategy* wotlk_eoe(PlayerbotAI* botAI) { return new RaidEoEStrategy(botAI); }
|
||||||
static Strategy* voa(PlayerbotAI* botAI) { return new RaidVoAStrategy(botAI); }
|
static Strategy* voa(PlayerbotAI* botAI) { return new RaidVoAStrategy(botAI); }
|
||||||
static Strategy* onyxia(PlayerbotAI* botAI) { return new RaidOnyxiaStrategy(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); }
|
static Strategy* icc(PlayerbotAI* botAI) { return new RaidIccStrategy(botAI); }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,6 @@
|
|||||||
#include "GameObject.h"
|
#include "GameObject.h"
|
||||||
#include "Group.h"
|
#include "Group.h"
|
||||||
#include "LastMovementValue.h"
|
#include "LastMovementValue.h"
|
||||||
#include "ObjectDefines.h"
|
|
||||||
#include "ObjectGuid.h"
|
#include "ObjectGuid.h"
|
||||||
#include "PlayerbotAI.h"
|
#include "PlayerbotAI.h"
|
||||||
#include "PlayerbotAIConfig.h"
|
#include "PlayerbotAIConfig.h"
|
||||||
@@ -19,11 +18,9 @@
|
|||||||
#include "Position.h"
|
#include "Position.h"
|
||||||
#include "RaidUlduarBossHelper.h"
|
#include "RaidUlduarBossHelper.h"
|
||||||
#include "RaidUlduarScripts.h"
|
#include "RaidUlduarScripts.h"
|
||||||
#include "RaidUlduarStrategy.h"
|
|
||||||
#include "RtiValue.h"
|
#include "RtiValue.h"
|
||||||
#include "ScriptedCreature.h"
|
#include "ScriptedCreature.h"
|
||||||
#include "ServerFacade.h"
|
#include "ServerFacade.h"
|
||||||
#include "SharedDefines.h"
|
|
||||||
#include "Unit.h"
|
#include "Unit.h"
|
||||||
#include "Vehicle.h"
|
#include "Vehicle.h"
|
||||||
#include <RtiTargetValue.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 "RaidUlduarStrategy.h"
|
||||||
|
|
||||||
#include "RaidUlduarMultipliers.h"
|
|
||||||
|
|
||||||
void RaidUlduarStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
void RaidUlduarStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||||
{
|
{
|
||||||
//
|
//
|
||||||
@@ -316,8 +314,3 @@ void RaidUlduarStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
|||||||
"yogg-saron phase 3 positioning trigger",
|
"yogg-saron phase 3 positioning trigger",
|
||||||
{ NextAction("yogg-saron phase 3 positioning action", ACTION_RAID) }));
|
{ 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
|
#define _PLAYERBOT_RAIDULDUARSTRATEGY_H
|
||||||
|
|
||||||
#include "AiObjectContext.h"
|
#include "AiObjectContext.h"
|
||||||
#include "Multiplier.h"
|
|
||||||
#include "Strategy.h"
|
#include "Strategy.h"
|
||||||
|
|
||||||
class RaidUlduarStrategy : public Strategy
|
class RaidUlduarStrategy : public Strategy
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
RaidUlduarStrategy(PlayerbotAI* ai) : Strategy(ai) {}
|
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 InitTriggers(std::vector<TriggerNode*>& triggers) override;
|
||||||
virtual void InitMultipliers(std::vector<Multiplier*>& multipliers) override;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -1634,7 +1634,7 @@ bool VezaxShadowCrashTrigger::IsActive()
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return botAI->HasAura(SPELL_SHADOW_CRASH, bot);
|
return botAI->HasAura(SPELL_VEZAX_SHADOW_CRASH, bot);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VezaxMarkOfTheFacelessTrigger::IsActive()
|
bool VezaxMarkOfTheFacelessTrigger::IsActive()
|
||||||
|
|||||||
@@ -3,187 +3,9 @@
|
|||||||
|
|
||||||
#include "EventMap.h"
|
#include "EventMap.h"
|
||||||
#include "GenericTriggers.h"
|
#include "GenericTriggers.h"
|
||||||
#include "PlayerbotAIConfig.h"
|
|
||||||
#include "RaidUlduarBossHelper.h"
|
#include "RaidUlduarBossHelper.h"
|
||||||
#include "Trigger.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
|
// Flame Levi
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
#include "ChatHelper.h"
|
|
||||||
#include "RaidUlduarBossHelper.h"
|
#include "RaidUlduarBossHelper.h"
|
||||||
#include "ObjectAccessor.h"
|
#include "ObjectAccessor.h"
|
||||||
#include "GameObject.h"
|
#include "GameObject.h"
|
||||||
@@ -9,6 +8,44 @@
|
|||||||
#include "Playerbots.h"
|
#include "Playerbots.h"
|
||||||
#include "World.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
|
// Prevent harpoon spam
|
||||||
std::unordered_map<ObjectGuid, time_t> RazorscaleBossHelper::_harpoonCooldowns;
|
std::unordered_map<ObjectGuid, time_t> RazorscaleBossHelper::_harpoonCooldowns;
|
||||||
// Prevent role assignment spam
|
// 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
|
#define _PLAYERBOT_RAIDVOAACTIONCONTEXT_H
|
||||||
|
|
||||||
#include "Action.h"
|
#include "Action.h"
|
||||||
|
#include "BossAuraActions.h"
|
||||||
#include "NamedObjectContext.h"
|
#include "NamedObjectContext.h"
|
||||||
#include "RaidVoAActions.h"
|
#include "RaidVoAActions.h"
|
||||||
#include "PlayerbotAI.h"
|
#include "PlayerbotAI.h"
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
#define _PLAYERBOT_RAIDVOATRIGGERCONTEXT_H
|
#define _PLAYERBOT_RAIDVOATRIGGERCONTEXT_H
|
||||||
|
|
||||||
#include "AiObjectContext.h"
|
#include "AiObjectContext.h"
|
||||||
|
#include "BossAuraTriggers.h"
|
||||||
#include "NamedObjectContext.h"
|
#include "NamedObjectContext.h"
|
||||||
#include "RaidVoATriggers.h"
|
#include "RaidVoATriggers.h"
|
||||||
|
|
||||||
|
|||||||
@@ -15,8 +15,6 @@
|
|||||||
#include "PaladinAiObjectContext.h"
|
#include "PaladinAiObjectContext.h"
|
||||||
#include "Playerbots.h"
|
#include "Playerbots.h"
|
||||||
#include "PriestAiObjectContext.h"
|
#include "PriestAiObjectContext.h"
|
||||||
#include "RaidUlduarActionContext.h"
|
|
||||||
#include "RaidUlduarTriggerContext.h"
|
|
||||||
#include "RogueAiObjectContext.h"
|
#include "RogueAiObjectContext.h"
|
||||||
#include "ShamanAiObjectContext.h"
|
#include "ShamanAiObjectContext.h"
|
||||||
#include "SharedValueContext.h"
|
#include "SharedValueContext.h"
|
||||||
@@ -49,6 +47,8 @@
|
|||||||
#include "Ai/Raid/VaultOfArchavon/RaidVoATriggerContext.h"
|
#include "Ai/Raid/VaultOfArchavon/RaidVoATriggerContext.h"
|
||||||
#include "Ai/Raid/ObsidianSanctum/RaidOsActionContext.h"
|
#include "Ai/Raid/ObsidianSanctum/RaidOsActionContext.h"
|
||||||
#include "Ai/Raid/ObsidianSanctum/RaidOsTriggerContext.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/RaidOnyxiaActionContext.h"
|
||||||
#include "Ai/Raid/Onyxia/RaidOnyxiaTriggerContext.h"
|
#include "Ai/Raid/Onyxia/RaidOnyxiaTriggerContext.h"
|
||||||
#include "Ai/Raid/Icecrown/RaidIccActionContext.h"
|
#include "Ai/Raid/Icecrown/RaidIccActionContext.h"
|
||||||
|
|||||||
@@ -1585,7 +1585,7 @@ void PlayerbotAI::ApplyInstanceStrategies(uint32 mapId, bool tellMaster)
|
|||||||
strategyName = "wotlk-hol"; // Halls of Lightning
|
strategyName = "wotlk-hol"; // Halls of Lightning
|
||||||
break;
|
break;
|
||||||
case 603:
|
case 603:
|
||||||
strategyName = "uld"; // Ulduar
|
strategyName = "ulduar"; // Ulduar
|
||||||
break;
|
break;
|
||||||
case 604:
|
case 604:
|
||||||
strategyName = "wotlk-gd"; // Gundrak
|
strategyName = "wotlk-gd"; // Gundrak
|
||||||
|
|||||||
Reference in New Issue
Block a user