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:
Crow
2026-02-06 13:55:43 -06:00
committed by GitHub
parent bebac60c51
commit b31bda85ee
43 changed files with 891 additions and 1081 deletions

View File

@@ -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);
} }

View File

@@ -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;
}; };

View File

@@ -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;
} }

View File

@@ -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); }
}; };

View File

@@ -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); }
}; };

View File

@@ -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", {

View File

@@ -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));
} }

View File

@@ -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;
}; };

View File

@@ -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;
} }

View File

@@ -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

View File

@@ -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);

View File

@@ -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;

View File

@@ -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");

View File

@@ -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();

View File

@@ -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);

View File

@@ -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);

View File

@@ -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

View File

@@ -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;

View File

@@ -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;
} }

View File

@@ -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;
}
} }

View File

@@ -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
{ {

View File

@@ -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"

View File

@@ -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"

View 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;
}

View 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

View File

@@ -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); }
}; };

View File

@@ -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>

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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

View File

@@ -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));
}

View File

@@ -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

View File

@@ -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()

View File

@@ -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
// //

View File

@@ -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

View 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

View File

@@ -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"

View File

@@ -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"

View File

@@ -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"

View File

@@ -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