[HOT FIX] MS build issues regarding folder / command lenght usage or rc.exe (#2038)

This commit is contained in:
bashermens
2026-01-19 22:45:28 +01:00
committed by GitHub
parent fd07e02a8a
commit 41c53365ae
1119 changed files with 27 additions and 27 deletions

View File

@@ -0,0 +1,695 @@
#include "RaidMagtheridonActions.h"
#include "RaidMagtheridonHelpers.h"
#include "Creature.h"
#include "ObjectAccessor.h"
#include "ObjectGuid.h"
#include "Playerbots.h"
using namespace MagtheridonHelpers;
bool MagtheridonMainTankAttackFirstThreeChannelersAction::Execute(Event event)
{
Unit* magtheridon = AI_VALUE2(Unit*, "find target", "magtheridon");
if (!magtheridon)
return false;
Creature* channelerSquare = GetChanneler(bot, SOUTH_CHANNELER);
if (channelerSquare && channelerSquare->IsAlive())
MarkTargetWithSquare(bot, channelerSquare);
Creature* channelerStar = GetChanneler(bot, WEST_CHANNELER);
if (channelerStar && channelerStar->IsAlive())
MarkTargetWithStar(bot, channelerStar);
Creature* channelerCircle = GetChanneler(bot, EAST_CHANNELER);
if (channelerCircle && channelerCircle->IsAlive())
MarkTargetWithCircle(bot, channelerCircle);
// After first three channelers are dead, wait for Magtheridon to activate
if ((!channelerSquare || !channelerSquare->IsAlive()) &&
(!channelerStar || !channelerStar->IsAlive()) &&
(!channelerCircle || !channelerCircle->IsAlive()))
{
const Location& position = MagtheridonsLairLocations::WaitingForMagtheridonPosition;
if (!bot->IsWithinDist2d(position.x, position.y, 2.0f))
{
return MoveTo(bot->GetMapId(), position.x, position.y, position.z, false, false, false, false,
MovementPriority::MOVEMENT_COMBAT, true, false);
}
bot->SetFacingTo(position.orientation);
return true;
}
Creature* currentTarget = nullptr;
std::string rtiName;
if (channelerSquare && channelerSquare->IsAlive())
{
currentTarget = channelerSquare;
rtiName = "square";
}
else if (channelerStar && channelerStar->IsAlive())
{
currentTarget = channelerStar;
rtiName = "star";
}
else if (channelerCircle && channelerCircle->IsAlive())
{
currentTarget = channelerCircle;
rtiName = "circle";
}
SetRtiTarget(botAI, rtiName, currentTarget);
if (currentTarget && bot->GetVictim() != currentTarget)
return Attack(currentTarget);
return false;
}
bool MagtheridonFirstAssistTankAttackNWChannelerAction::Execute(Event event)
{
Creature* channelerDiamond = GetChanneler(bot, NORTHWEST_CHANNELER);
if (!channelerDiamond || !channelerDiamond->IsAlive())
return false;
MarkTargetWithDiamond(bot, channelerDiamond);
SetRtiTarget(botAI, "diamond", channelerDiamond);
if (bot->GetVictim() != channelerDiamond)
return Attack(channelerDiamond);
if (channelerDiamond->GetVictim() == bot)
{
const Location& position = MagtheridonsLairLocations::NWChannelerTankPosition;
const float maxDistance = 3.0f;
if (bot->GetExactDist2d(position.x, position.y) > maxDistance)
{
float dX = position.x - bot->GetPositionX();
float dY = position.y - bot->GetPositionY();
float dist = sqrt(dX * dX + dY * dY);
float moveX = bot->GetPositionX() + (dX / dist) * maxDistance;
float moveY = bot->GetPositionY() + (dY / dist) * maxDistance;
return MoveTo(bot->GetMapId(), moveX, moveY, position.z, false, false, false, false,
MovementPriority::MOVEMENT_COMBAT, true, false);
}
}
return false;
}
bool MagtheridonSecondAssistTankAttackNEChannelerAction::Execute(Event event)
{
Creature* channelerTriangle = GetChanneler(bot, NORTHEAST_CHANNELER);
if (!channelerTriangle || !channelerTriangle->IsAlive())
return false;
MarkTargetWithTriangle(bot, channelerTriangle);
SetRtiTarget(botAI, "triangle", channelerTriangle);
if (bot->GetVictim() != channelerTriangle)
return Attack(channelerTriangle);
if (channelerTriangle->GetVictim() == bot)
{
const Location& position = MagtheridonsLairLocations::NEChannelerTankPosition;
const float maxDistance = 3.0f;
if (bot->GetExactDist2d(position.x, position.y) > maxDistance)
{
float dX = position.x - bot->GetPositionX();
float dY = position.y - bot->GetPositionY();
float dist = sqrt(dX * dX + dY * dY);
float moveX = bot->GetPositionX() + (dX / dist) * maxDistance;
float moveY = bot->GetPositionY() + (dY / dist) * maxDistance;
return MoveTo(bot->GetMapId(), moveX, moveY, position.z, false, false, false, false,
MovementPriority::MOVEMENT_COMBAT, true, false);
}
}
return false;
}
// Misdirect West & East Channelers to Main Tank
bool MagtheridonMisdirectHellfireChannelers::Execute(Event event)
{
Group* group = bot->GetGroup();
if (!group)
return false;
std::vector<Player*> hunters;
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
{
Player* member = ref->GetSource();
if (member && member->IsAlive() && member->getClass() == CLASS_HUNTER && GET_PLAYERBOT_AI(member))
hunters.push_back(member);
}
int hunterIndex = -1;
for (size_t i = 0; i < hunters.size(); ++i)
{
if (hunters[i] == bot)
{
hunterIndex = static_cast<int>(i);
break;
}
}
Player* mainTank = nullptr;
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
{
Player* member = ref->GetSource();
if (member && member->IsAlive() && botAI->IsMainTank(member))
{
mainTank = member;
break;
}
}
Creature* channelerStar = GetChanneler(bot, WEST_CHANNELER);
Creature* channelerCircle = GetChanneler(bot, EAST_CHANNELER);
switch (hunterIndex)
{
case 0:
if (mainTank && channelerStar && channelerStar->IsAlive() &&
channelerStar->GetVictim() != mainTank)
{
if (botAI->CanCastSpell("misdirection", mainTank))
return botAI->CastSpell("misdirection", mainTank);
if (!bot->HasAura(SPELL_MISDIRECTION))
return false;
if (botAI->CanCastSpell("steady shot", channelerStar))
return botAI->CastSpell("steady shot", channelerStar);
}
break;
case 1:
if (mainTank && channelerCircle && channelerCircle->IsAlive() &&
channelerCircle->GetVictim() != mainTank)
{
if (botAI->CanCastSpell("misdirection", mainTank))
return botAI->CastSpell("misdirection", mainTank);
if (!bot->HasAura(SPELL_MISDIRECTION))
return false;
if (botAI->CanCastSpell("steady shot", channelerCircle))
return botAI->CastSpell("steady shot", channelerCircle);
}
break;
default:
break;
}
return false;
}
bool MagtheridonAssignDPSPriorityAction::Execute(Event event)
{
// Listed in order of priority
Creature* channelerSquare = GetChanneler(bot, SOUTH_CHANNELER);
if (channelerSquare && channelerSquare->IsAlive())
{
SetRtiTarget(botAI, "square", channelerSquare);
if (bot->GetTarget() != channelerSquare->GetGUID())
{
bot->SetSelection(channelerSquare->GetGUID());
return Attack(channelerSquare);
}
return false;
}
Creature* channelerStar = GetChanneler(bot, WEST_CHANNELER);
if (channelerStar && channelerStar->IsAlive())
{
SetRtiTarget(botAI, "star", channelerStar);
if (bot->GetTarget() != channelerStar->GetGUID())
{
bot->SetSelection(channelerStar->GetGUID());
return Attack(channelerStar);
}
return false;
}
Creature* channelerCircle = GetChanneler(bot, EAST_CHANNELER);
if (channelerCircle && channelerCircle->IsAlive())
{
SetRtiTarget(botAI, "circle", channelerCircle);
if (bot->GetTarget() != channelerCircle->GetGUID())
{
bot->SetSelection(channelerCircle->GetGUID());
return Attack(channelerCircle);
}
return false;
}
Creature* channelerDiamond = GetChanneler(bot, NORTHWEST_CHANNELER);
if (channelerDiamond && channelerDiamond->IsAlive())
{
SetRtiTarget(botAI, "diamond", channelerDiamond);
if (bot->GetTarget() != channelerDiamond->GetGUID())
{
bot->SetSelection(channelerDiamond->GetGUID());
return Attack(channelerDiamond);
}
return false;
}
Creature* channelerTriangle = GetChanneler(bot, NORTHEAST_CHANNELER);
if (channelerTriangle && channelerTriangle->IsAlive())
{
SetRtiTarget(botAI, "triangle", channelerTriangle);
if (bot->GetTarget() != channelerTriangle->GetGUID())
{
bot->SetSelection(channelerTriangle->GetGUID());
return Attack(channelerTriangle);
}
return false;
}
Unit* magtheridon = AI_VALUE2(Unit*, "find target", "magtheridon");
if (magtheridon && !magtheridon->HasAura(SPELL_SHADOW_CAGE) &&
(!channelerSquare || !channelerSquare->IsAlive()) &&
(!channelerStar || !channelerStar->IsAlive()) &&
(!channelerCircle || !channelerCircle->IsAlive()) &&
(!channelerDiamond || !channelerDiamond->IsAlive()) &&
(!channelerTriangle || !channelerTriangle->IsAlive()))
{
SetRtiTarget(botAI, "cross", magtheridon);
if (bot->GetTarget() != magtheridon->GetGUID())
{
bot->SetSelection(magtheridon->GetGUID());
return Attack(magtheridon);
}
}
return false;
}
// Assign Burning Abyssals to Warlocks to Banish
// Burning Abyssals in excess of Warlocks in party will be Feared
bool MagtheridonWarlockCCBurningAbyssalAction::Execute(Event event)
{
Group* group = bot->GetGroup();
if (!group)
return false;
const GuidVector& npcs = AI_VALUE(GuidVector, "nearest hostile npcs");
std::vector<Unit*> abyssals;
for (auto const& npc : npcs)
{
Unit* unit = botAI->GetUnit(npc);
if (unit && unit->GetEntry() == NPC_BURNING_ABYSSAL && unit->IsAlive())
abyssals.push_back(unit);
}
std::vector<Player*> warlocks;
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
{
Player* member = ref->GetSource();
if (member && member->IsAlive() && member->getClass() == CLASS_WARLOCK && GET_PLAYERBOT_AI(member))
warlocks.push_back(member);
}
int warlockIndex = -1;
for (size_t i = 0; i < warlocks.size(); ++i)
{
if (warlocks[i] == bot)
{
warlockIndex = static_cast<int>(i);
break;
}
}
if (warlockIndex >= 0 && warlockIndex < abyssals.size())
{
Unit* assignedAbyssal = abyssals[warlockIndex];
if (!assignedAbyssal->HasAura(SPELL_BANISH) && botAI->CanCastSpell(SPELL_BANISH, assignedAbyssal, true))
return botAI->CastSpell("banish", assignedAbyssal);
}
for (size_t i = warlocks.size(); i < abyssals.size(); ++i)
{
Unit* excessAbyssal = abyssals[i];
if (!excessAbyssal->HasAura(SPELL_BANISH) && !excessAbyssal->HasAura(SPELL_FEAR) &&
botAI->CanCastSpell(SPELL_FEAR, excessAbyssal, true))
return botAI->CastSpell("fear", excessAbyssal);
}
return false;
}
// Main tank will back up to the Northern point of the room
bool MagtheridonMainTankPositionBossAction::Execute(Event event)
{
Unit* magtheridon = AI_VALUE2(Unit*, "find target", "magtheridon");
if (!magtheridon)
return false;
MarkTargetWithCross(bot, magtheridon);
SetRtiTarget(botAI, "cross", magtheridon);
if (bot->GetVictim() != magtheridon)
return Attack(magtheridon);
if (magtheridon->GetVictim() == bot)
{
const Location& position = MagtheridonsLairLocations::MagtheridonTankPosition;
const float maxDistance = 2.0f;
if (bot->GetExactDist2d(position.x, position.y) > maxDistance)
{
float dX = position.x - bot->GetPositionX();
float dY = position.y - bot->GetPositionY();
float dist = sqrt(dX * dX + dY * dY);
float moveX = bot->GetPositionX() + (dX / dist) * maxDistance;
float moveY = bot->GetPositionY() + (dY / dist) * maxDistance;
return MoveTo(bot->GetMapId(), moveX, moveY, position.z, false, false, false, false,
MovementPriority::MOVEMENT_COMBAT, true, true);
}
bot->SetFacingTo(position.orientation);
}
return false;
}
// Ranged DPS will remain within 25 yards of the center of the room
// Healers will remain within 15 yards of a position that is between ranged DPS and the boss
std::unordered_map<ObjectGuid, Position> MagtheridonSpreadRangedAction::initialPositions;
std::unordered_map<ObjectGuid, bool> MagtheridonSpreadRangedAction::hasReachedInitialPosition;
bool MagtheridonSpreadRangedAction::Execute(Event event)
{
Unit* magtheridon = AI_VALUE2(Unit*, "find target", "magtheridon");
if (!magtheridon)
return false;
Group* group = bot->GetGroup();
if (!group)
return false;
const uint32 instanceId = magtheridon->GetMap()->GetInstanceId();
// Wait for 6 seconds after Magtheridon activates to spread
const uint8 spreadWaitSeconds = 6;
auto it = spreadWaitTimer.find(instanceId);
if (it == spreadWaitTimer.end() ||
(time(nullptr) - it->second) < spreadWaitSeconds)
return false;
auto cubeIt = botToCubeAssignment.find(bot->GetGUID());
if (cubeIt != botToCubeAssignment.end())
{
time_t now = time(nullptr);
auto timerIt = blastNovaTimer.find(instanceId);
if (timerIt != blastNovaTimer.end())
{
time_t lastBlastNova = timerIt->second;
if (now - lastBlastNova >= 49)
return false;
}
}
std::vector<Player*> members;
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
{
Player* member = ref->GetSource();
if (member && member->IsAlive())
members.push_back(member);
}
bool isHealer = botAI->IsHeal(bot);
const Location& center = isHealer
? MagtheridonsLairLocations::HealerSpreadPosition
: MagtheridonsLairLocations::RangedSpreadPosition;
float maxSpreadRadius = isHealer ? 15.0f : 20.0f;
float centerX = center.x;
float centerY = center.y;
float centerZ = bot->GetPositionZ();
const float radiusBuffer = 3.0f;
if (!initialPositions.count(bot->GetGUID()))
{
auto it = std::find(members.begin(), members.end(), bot);
uint8 botIndex = (it != members.end()) ? std::distance(members.begin(), it) : 0;
uint8 count = members.size();
float angle = 2 * M_PI * botIndex / count;
float radius = static_cast<float>(rand()) / RAND_MAX * maxSpreadRadius;
float targetX = centerX + radius * cos(angle);
float targetY = centerY + radius * sin(angle);
initialPositions[bot->GetGUID()] = Position(targetX, targetY, centerZ);
hasReachedInitialPosition[bot->GetGUID()] = false;
}
Position targetPosition = initialPositions[bot->GetGUID()];
if (!hasReachedInitialPosition[bot->GetGUID()])
{
if (!bot->IsWithinDist2d(targetPosition.GetPositionX(), targetPosition.GetPositionY(), 2.0f))
{
float destX = targetPosition.GetPositionX();
float destY = targetPosition.GetPositionY();
float destZ = targetPosition.GetPositionZ();
if (!bot->GetMap()->CheckCollisionAndGetValidCoords(bot, bot->GetPositionX(),
bot->GetPositionY(), bot->GetPositionZ(), destX, destY, destZ))
return false;
bot->AttackStop();
bot->InterruptNonMeleeSpells(false);
return MoveTo(bot->GetMapId(), destX, destY, destZ, false, false, false, false,
MovementPriority::MOVEMENT_COMBAT, true, false);
}
hasReachedInitialPosition[bot->GetGUID()] = true;
}
float distToCenter = bot->GetExactDist2d(centerX, centerY);
if (distToCenter > maxSpreadRadius + radiusBuffer)
{
float angle = static_cast<float>(rand()) / RAND_MAX * 2.0f * M_PI;
float radius = static_cast<float>(rand()) / RAND_MAX * maxSpreadRadius;
float targetX = centerX + radius * cos(angle);
float targetY = centerY + radius * sin(angle);
if (bot->GetMap()->CheckCollisionAndGetValidCoords(bot, bot->GetPositionX(), bot->GetPositionY(),
bot->GetPositionZ(), targetX, targetY, centerZ))
{
bot->AttackStop();
bot->InterruptNonMeleeSpells(false);
return MoveTo(bot->GetMapId(), targetX, targetY, centerZ, false, false, false, false,
MovementPriority::MOVEMENT_COMBAT, true, false);
}
}
return false;
}
// For bots that are assigned to click cubes
// Magtheridon casts Blast Nova every 54.35 to 55.40s, with a 2s cast time
bool MagtheridonUseManticronCubeAction::Execute(Event event)
{
Unit* magtheridon = AI_VALUE2(Unit*, "find target", "magtheridon");
if (!magtheridon)
return false;
auto it = botToCubeAssignment.find(bot->GetGUID());
const CubeInfo& cubeInfo = it->second;
GameObject* cube = botAI->GetGameObject(cubeInfo.guid);
if (!cube)
return false;
// Release cubes after Blast Nova is interrupted
if (HandleCubeRelease(magtheridon, cube))
return true;
// Check if cube logic should be active (49+ second rule)
if (!ShouldActivateCubeLogic(magtheridon))
return false;
// Handle active cube logic based on Blast Nova casting state
bool blastNovaActive = magtheridon->HasUnitState(UNIT_STATE_CASTING) &&
magtheridon->FindCurrentSpellBySpellId(SPELL_BLAST_NOVA);
if (!blastNovaActive)
// After 49 seconds, wait at safe distance from cube
return HandleWaitingPhase(cubeInfo);
else
// Blast Nova is casting - move to and click cube
return HandleCubeInteraction(cubeInfo, cube);
return false;
}
bool MagtheridonUseManticronCubeAction::HandleCubeRelease(Unit* magtheridon, GameObject* cube)
{
if (bot->HasAura(SPELL_SHADOW_GRASP) &&
!(magtheridon->HasUnitState(UNIT_STATE_CASTING) &&
magtheridon->FindCurrentSpellBySpellId(SPELL_BLAST_NOVA)))
{
uint32 delay = urand(200, 3000);
botAI->AddTimedEvent(
[this]
{
botAI->Reset();
},
delay);
botAI->SetNextCheckDelay(delay + 50);
return true;
}
return false;
}
bool MagtheridonUseManticronCubeAction::ShouldActivateCubeLogic(Unit* magtheridon)
{
auto timerIt = blastNovaTimer.find(magtheridon->GetMap()->GetInstanceId());
if (timerIt == blastNovaTimer.end())
return false;
time_t now = time(nullptr);
time_t lastBlastNova = timerIt->second;
return (now - lastBlastNova >= 49);
}
bool MagtheridonUseManticronCubeAction::HandleWaitingPhase(const CubeInfo& cubeInfo)
{
const float safeWaitDistance = 8.0f;
float cubeDist = bot->GetExactDist2d(cubeInfo.x, cubeInfo.y);
if (fabs(cubeDist - safeWaitDistance) > 1.0f)
{
for (int i = 0; i < 12; ++i)
{
float angle = i * M_PI / 6.0f;
float targetX = cubeInfo.x + cos(angle) * safeWaitDistance;
float targetY = cubeInfo.y + sin(angle) * safeWaitDistance;
float targetZ = bot->GetPositionZ();
if (IsSafeFromMagtheridonHazards(botAI, bot, targetX, targetY, targetZ))
{
bot->AttackStop();
bot->InterruptNonMeleeSpells(true);
return MoveTo(bot->GetMapId(), targetX, targetY, targetZ, false, false, false, false,
MovementPriority::MOVEMENT_COMBAT, true, false);
}
}
float angle = static_cast<float>(rand()) / RAND_MAX * 2.0f * M_PI;
float fallbackX = cubeInfo.x + cos(angle) * safeWaitDistance;
float fallbackY = cubeInfo.y + sin(angle) * safeWaitDistance;
float fallbackZ = bot->GetPositionZ();
return MoveTo(bot->GetMapId(), fallbackX, fallbackY, fallbackZ, false, false, false, false,
MovementPriority::MOVEMENT_COMBAT, true, false);
}
return true;
}
bool MagtheridonUseManticronCubeAction::HandleCubeInteraction(const CubeInfo& cubeInfo, GameObject* cube)
{
const float interactDistance = 1.0f;
float cubeDist = bot->GetExactDist2d(cubeInfo.x, cubeInfo.y);
if (cubeDist > interactDistance)
{
if (cubeDist <= interactDistance + 1.0f)
{
uint32 delay = urand(200, 1500);
botAI->AddTimedEvent(
[this, cube]
{
bot->StopMoving();
cube->Use(bot);
},
delay);
botAI->SetNextCheckDelay(delay + 50);
return true;
}
float angle = atan2(cubeInfo.y - bot->GetPositionY(), cubeInfo.x - bot->GetPositionX());
float targetX = cubeInfo.x - cos(angle) * interactDistance;
float targetY = cubeInfo.y - sin(angle) * interactDistance;
float targetZ = bot->GetPositionZ();
bot->AttackStop();
bot->InterruptNonMeleeSpells(true);
return MoveTo(bot->GetMapId(), targetX, targetY, targetZ, false, false, false, false,
MovementPriority::MOVEMENT_FORCED, true, false);
}
return false;
}
// The Blast Nova timer resets when Magtheridon stops casting it, which is needed to ensure that bots use cubes.
// However, Magtheridon's Blast Nova cooldown actually runs from when he starts casting it. This means that if a Blast Nova
// is not interrupted or takes too long to interrupt, the timer will be thrown off for the rest of the encounter.
// Correcting this issue is complicated and probably would need some rewriting--I have not done so and
// and view the current solution as sufficient since in TBC a missed Blast Nova would be a guaranteed wipe anyway.
bool MagtheridonManageTimersAndAssignmentsAction::Execute(Event event)
{
Unit* magtheridon = AI_VALUE2(Unit*, "find target", "magtheridon");
if (!magtheridon)
return false;
const uint32 instanceId = magtheridon->GetMap()->GetInstanceId();
const time_t now = time(nullptr);
bool blastNovaActive = magtheridon->HasUnitState(UNIT_STATE_CASTING) &&
magtheridon->FindCurrentSpellBySpellId(SPELL_BLAST_NOVA);
bool lastBlastNova = lastBlastNovaState[instanceId];
if (lastBlastNova && !blastNovaActive && IsInstanceTimerManager(botAI, bot))
blastNovaTimer[instanceId] = now;
lastBlastNovaState[instanceId] = blastNovaActive;
if (!magtheridon->HasAura(SPELL_SHADOW_CAGE))
{
if (IsInstanceTimerManager(botAI, bot))
{
spreadWaitTimer.try_emplace(instanceId, now);
blastNovaTimer.try_emplace(instanceId, now);
dpsWaitTimer.try_emplace(instanceId, now);
}
}
else
{
MagtheridonSpreadRangedAction::initialPositions.clear();
MagtheridonSpreadRangedAction::hasReachedInitialPosition.clear();
botToCubeAssignment.clear();
if (IsInstanceTimerManager(botAI, bot))
{
spreadWaitTimer.erase(instanceId);
blastNovaTimer.erase(instanceId);
dpsWaitTimer.erase(instanceId);
}
}
return false;
}

View File

@@ -0,0 +1,100 @@
#ifndef _PLAYERBOT_RAIDMAGTHERIDONACTIONS_H
#define _PLAYERBOT_RAIDMAGTHERIDONACTIONS_H
#include "RaidMagtheridonHelpers.h"
#include "Action.h"
#include "AttackAction.h"
#include "MovementActions.h"
using namespace MagtheridonHelpers;
class MagtheridonMainTankAttackFirstThreeChannelersAction : public AttackAction
{
public:
MagtheridonMainTankAttackFirstThreeChannelersAction(PlayerbotAI* botAI, std::string const name = "magtheridon main tank attack first three channelers") : AttackAction(botAI, name) {};
bool Execute(Event event) override;
};
class MagtheridonFirstAssistTankAttackNWChannelerAction : public AttackAction
{
public:
MagtheridonFirstAssistTankAttackNWChannelerAction(PlayerbotAI* botAI, std::string const name = "magtheridon first assist tank attack nw channeler") : AttackAction(botAI, name) {};
bool Execute(Event event) override;
};
class MagtheridonSecondAssistTankAttackNEChannelerAction : public AttackAction
{
public:
MagtheridonSecondAssistTankAttackNEChannelerAction(PlayerbotAI* botAI, std::string const name = "magtheridon second assist tank attack ne channeler") : AttackAction(botAI, name) {};
bool Execute(Event event) override;
};
class MagtheridonMisdirectHellfireChannelers : public AttackAction
{
public:
MagtheridonMisdirectHellfireChannelers(PlayerbotAI* botAI, std::string const name = "magtheridon misdirect hellfire channelers") : AttackAction(botAI, name) {};
bool Execute(Event event) override;
};
class MagtheridonAssignDPSPriorityAction : public AttackAction
{
public:
MagtheridonAssignDPSPriorityAction(PlayerbotAI* botAI, std::string const name = "magtheridon assign dps priority") : AttackAction(botAI, name) {};
bool Execute(Event event) override;
};
class MagtheridonWarlockCCBurningAbyssalAction : public AttackAction
{
public:
MagtheridonWarlockCCBurningAbyssalAction(PlayerbotAI* botAI, std::string const name = "magtheridon warlock cc burning abyssal") : AttackAction(botAI, name) {};
bool Execute(Event event) override;
};
class MagtheridonMainTankPositionBossAction : public AttackAction
{
public:
MagtheridonMainTankPositionBossAction(PlayerbotAI* botAI, std::string const name = "magtheridon main tank position boss") : AttackAction(botAI, name) {};
bool Execute(Event event) override;
};
class MagtheridonSpreadRangedAction : public MovementAction
{
public:
static std::unordered_map<ObjectGuid, Position> initialPositions;
static std::unordered_map<ObjectGuid, bool> hasReachedInitialPosition;
MagtheridonSpreadRangedAction(PlayerbotAI* botAI, std::string const name = "magtheridon spread ranged") : MovementAction(botAI, name) {};
bool Execute(Event event) override;
};
class MagtheridonUseManticronCubeAction : public MovementAction
{
public:
MagtheridonUseManticronCubeAction(PlayerbotAI* botAI, std::string const name = "magtheridon use manticron cube") : MovementAction(botAI, name) {};
bool Execute(Event event) override;
private:
bool HandleCubeRelease(Unit* magtheridon, GameObject* cube);
bool ShouldActivateCubeLogic(Unit* magtheridon);
bool HandleWaitingPhase(const CubeInfo& cubeInfo);
bool HandleCubeInteraction(const CubeInfo& cubeInfo, GameObject* cube);
};
class MagtheridonManageTimersAndAssignmentsAction : public Action
{
public:
MagtheridonManageTimersAndAssignmentsAction(PlayerbotAI* botAI, std::string const name = "magtheridon manage timers and assignments") : Action(botAI, name) {};
bool Execute(Event event) override;
};
#endif

View File

@@ -0,0 +1,71 @@
#include <unordered_map>
#include <ctime>
#include "RaidMagtheridonMultipliers.h"
#include "RaidMagtheridonActions.h"
#include "RaidMagtheridonHelpers.h"
#include "ChooseTargetActions.h"
#include "GenericSpellActions.h"
#include "Playerbots.h"
#include "WarlockActions.h"
using namespace MagtheridonHelpers;
// Don't do anything other than clicking cubes when Magtheridon is casting Blast Nova
float MagtheridonUseManticronCubeMultiplier::GetValue(Action* action)
{
Unit* magtheridon = AI_VALUE2(Unit*, "find target", "magtheridon");
if (!magtheridon)
return 1.0f;
if (magtheridon->HasUnitState(UNIT_STATE_CASTING) &&
magtheridon->FindCurrentSpellBySpellId(SPELL_BLAST_NOVA))
{
auto it = botToCubeAssignment.find(bot->GetGUID());
if (it != botToCubeAssignment.end())
{
if (dynamic_cast<MagtheridonUseManticronCubeAction*>(action))
return 1.0f;
return 0.0f;
}
}
return 1.0f;
}
// Bots will wait for 6 seconds after Magtheridon becomes attackable before engaging
float MagtheridonWaitToAttackMultiplier::GetValue(Action* action)
{
Unit* magtheridon = AI_VALUE2(Unit*, "find target", "magtheridon");
if (!magtheridon || magtheridon->HasAura(SPELL_SHADOW_CAGE))
return 1.0f;
const uint8 dpsWaitSeconds = 6;
auto it = dpsWaitTimer.find(magtheridon->GetMap()->GetInstanceId());
if (it == dpsWaitTimer.end() ||
(time(nullptr) - it->second) < dpsWaitSeconds)
{
if (!botAI->IsMainTank(bot) && (dynamic_cast<AttackAction*>(action) ||
(!botAI->IsHeal(bot) && dynamic_cast<CastSpellAction*>(action))))
return 0.0f;
}
return 1.0f;
}
// No tank assist for offtanks during the channeler phase
// So they don't try to pull channelers from each other or the main tank
float MagtheridonDisableOffTankAssistMultiplier::GetValue(Action* action)
{
Unit* magtheridon = AI_VALUE2(Unit*, "find target", "magtheridon");
Unit* channeler = AI_VALUE2(Unit*, "find target", "hellfire channeler");
if (!magtheridon)
return 1.0f;
if ((botAI->IsAssistTankOfIndex(bot, 0) || botAI->IsAssistTankOfIndex(bot, 1)) &&
dynamic_cast<TankAssistAction*>(action))
return 0.0f;
return 1.0f;
}

View File

@@ -0,0 +1,27 @@
#ifndef _PLAYERBOT_RAIDMAGTHERIDONMULTIPLIERS_H
#define _PLAYERBOT_RAIDMAGTHERIDONMULTIPLIERS_H
#include "Multiplier.h"
class MagtheridonUseManticronCubeMultiplier : public Multiplier
{
public:
MagtheridonUseManticronCubeMultiplier(PlayerbotAI* botAI) : Multiplier(botAI, "magtheridon use manticron cube multiplier") {}
float GetValue(Action* action) override;
};
class MagtheridonWaitToAttackMultiplier : public Multiplier
{
public:
MagtheridonWaitToAttackMultiplier(PlayerbotAI* botAI) : Multiplier(botAI, "magtheridon wait to attack multiplier") {}
float GetValue(Action* action) override;
};
class MagtheridonDisableOffTankAssistMultiplier : public Multiplier
{
public:
MagtheridonDisableOffTankAssistMultiplier(PlayerbotAI* botAI) : Multiplier(botAI, "magtheridon disable off tank assist multiplier") {}
float GetValue(Action* action) override;
};
#endif

View File

@@ -0,0 +1,37 @@
#ifndef _PLAYERBOT_RAIDMAGTHERIDONACTIONCONTEXT_H
#define _PLAYERBOT_RAIDMAGTHERIDONACTIONCONTEXT_H
#include "RaidMagtheridonActions.h"
#include "NamedObjectContext.h"
class RaidMagtheridonActionContext : public NamedObjectContext<Action>
{
public:
RaidMagtheridonActionContext()
{
creators["magtheridon main tank attack first three channelers"] = &RaidMagtheridonActionContext::magtheridon_main_tank_attack_first_three_channelers;
creators["magtheridon first assist tank attack nw channeler"] = &RaidMagtheridonActionContext::magtheridon_first_assist_tank_attack_nw_channeler;
creators["magtheridon second assist tank attack ne channeler"] = &RaidMagtheridonActionContext::magtheridon_second_assist_tank_attack_ne_channeler;
creators["magtheridon misdirect hellfire channelers"] = &RaidMagtheridonActionContext::magtheridon_misdirect_hellfire_channelers;
creators["magtheridon assign dps priority"] = &RaidMagtheridonActionContext::magtheridon_assign_dps_priority;
creators["magtheridon warlock cc burning abyssal"] = &RaidMagtheridonActionContext::magtheridon_warlock_cc_burning_abyssal;
creators["magtheridon main tank position boss"] = &RaidMagtheridonActionContext::magtheridon_main_tank_position_boss;
creators["magtheridon spread ranged"] = &RaidMagtheridonActionContext::magtheridon_spread_ranged;
creators["magtheridon use manticron cube"] = &RaidMagtheridonActionContext::magtheridon_use_manticron_cube;
creators["magtheridon manage timers and assignments"] = &RaidMagtheridonActionContext::magtheridon_manage_timers_and_assignments;
}
private:
static Action* magtheridon_main_tank_attack_first_three_channelers(PlayerbotAI* botAI) { return new MagtheridonMainTankAttackFirstThreeChannelersAction(botAI); }
static Action* magtheridon_first_assist_tank_attack_nw_channeler(PlayerbotAI* botAI) { return new MagtheridonFirstAssistTankAttackNWChannelerAction(botAI); }
static Action* magtheridon_second_assist_tank_attack_ne_channeler(PlayerbotAI* botAI) { return new MagtheridonSecondAssistTankAttackNEChannelerAction(botAI); }
static Action* magtheridon_misdirect_hellfire_channelers(PlayerbotAI* botAI) { return new MagtheridonMisdirectHellfireChannelers(botAI); }
static Action* magtheridon_assign_dps_priority(PlayerbotAI* botAI) { return new MagtheridonAssignDPSPriorityAction(botAI); }
static Action* magtheridon_warlock_cc_burning_abyssal(PlayerbotAI* botAI) { return new MagtheridonWarlockCCBurningAbyssalAction(botAI); }
static Action* magtheridon_main_tank_position_boss(PlayerbotAI* botAI) { return new MagtheridonMainTankPositionBossAction(botAI); }
static Action* magtheridon_spread_ranged(PlayerbotAI* botAI) { return new MagtheridonSpreadRangedAction(botAI); }
static Action* magtheridon_use_manticron_cube(PlayerbotAI* botAI) { return new MagtheridonUseManticronCubeAction(botAI); }
static Action* magtheridon_manage_timers_and_assignments(PlayerbotAI* botAI) { return new MagtheridonManageTimersAndAssignmentsAction(botAI); }
};
#endif

View File

@@ -0,0 +1,37 @@
#ifndef _PLAYERBOT_RAIDMAGTHERIDONTRIGGERCONTEXT_H
#define _PLAYERBOT_RAIDMAGTHERIDONTRIGGERCONTEXT_H
#include "RaidMagtheridonTriggers.h"
#include "AiObjectContext.h"
class RaidMagtheridonTriggerContext : public NamedObjectContext<Trigger>
{
public:
RaidMagtheridonTriggerContext() : NamedObjectContext<Trigger>()
{
creators["magtheridon first three channelers engaged by main tank"] = &RaidMagtheridonTriggerContext::magtheridon_first_three_channelers_engaged_by_main_tank;
creators["magtheridon nw channeler engaged by first assist tank"] = &RaidMagtheridonTriggerContext::magtheridon_nw_channeler_engaged_by_first_assist_tank;
creators["magtheridon ne channeler engaged by second assist tank"] = &RaidMagtheridonTriggerContext::magtheridon_ne_channeler_engaged_by_second_assist_tank;
creators["magtheridon pulling west and east channelers"] = &RaidMagtheridonTriggerContext::magtheridon_pull_west_and_east_channelers;
creators["magtheridon determining kill order"] = &RaidMagtheridonTriggerContext::magtheridon_determining_kill_order;
creators["magtheridon burning abyssal spawned"] = &RaidMagtheridonTriggerContext::magtheridon_burning_abyssal_spawned;
creators["magtheridon boss engaged by main tank"] = &RaidMagtheridonTriggerContext::magtheridon_boss_engaged_by_main_tank;
creators["magtheridon boss engaged by ranged"] = &RaidMagtheridonTriggerContext::magtheridon_boss_engaged_by_ranged;
creators["magtheridon incoming blast nova"] = &RaidMagtheridonTriggerContext::magtheridon_incoming_blast_nova;
creators["magtheridon need to manage timers and assignments"] = &RaidMagtheridonTriggerContext::magtheridon_need_to_manage_timers_and_assignments;
}
private:
static Trigger* magtheridon_first_three_channelers_engaged_by_main_tank(PlayerbotAI* botAI) { return new MagtheridonFirstThreeChannelersEngagedByMainTankTrigger(botAI); }
static Trigger* magtheridon_nw_channeler_engaged_by_first_assist_tank(PlayerbotAI* botAI) { return new MagtheridonNWChannelerEngagedByFirstAssistTankTrigger(botAI); }
static Trigger* magtheridon_ne_channeler_engaged_by_second_assist_tank(PlayerbotAI* botAI) { return new MagtheridonNEChannelerEngagedBySecondAssistTankTrigger(botAI); }
static Trigger* magtheridon_pull_west_and_east_channelers(PlayerbotAI* botAI) { return new MagtheridonPullingWestAndEastChannelersTrigger(botAI); }
static Trigger* magtheridon_determining_kill_order(PlayerbotAI* botAI) { return new MagtheridonDeterminingKillOrderTrigger(botAI); }
static Trigger* magtheridon_burning_abyssal_spawned(PlayerbotAI* botAI) { return new MagtheridonBurningAbyssalSpawnedTrigger(botAI); }
static Trigger* magtheridon_boss_engaged_by_main_tank(PlayerbotAI* botAI) { return new MagtheridonBossEngagedByMainTankTrigger(botAI); }
static Trigger* magtheridon_boss_engaged_by_ranged(PlayerbotAI* botAI) { return new MagtheridonBossEngagedByRangedTrigger(botAI); }
static Trigger* magtheridon_incoming_blast_nova(PlayerbotAI* botAI) { return new MagtheridonIncomingBlastNovaTrigger(botAI); }
static Trigger* magtheridon_need_to_manage_timers_and_assignments(PlayerbotAI* botAI) { return new MagtheridonNeedToManageTimersAndAssignmentsTrigger(botAI); }
};
#endif

View File

@@ -0,0 +1,42 @@
#include "RaidMagtheridonStrategy.h"
#include "RaidMagtheridonMultipliers.h"
void RaidMagtheridonStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
triggers.push_back(new TriggerNode("magtheridon incoming blast nova", {
NextAction("magtheridon use manticron cube", ACTION_EMERGENCY + 10) }));
triggers.push_back(new TriggerNode("magtheridon need to manage timers and assignments", {
NextAction("magtheridon manage timers and assignments", ACTION_EMERGENCY + 1) }));
triggers.push_back(new TriggerNode("magtheridon burning abyssal spawned", {
NextAction("magtheridon warlock cc burning abyssal", ACTION_RAID + 3) }));
triggers.push_back(new TriggerNode("magtheridon boss engaged by ranged", {
NextAction("magtheridon spread ranged", ACTION_RAID + 2) }));
triggers.push_back(new TriggerNode("magtheridon pulling west and east channelers", {
NextAction("magtheridon misdirect hellfire channelers", ACTION_RAID + 2) }));
triggers.push_back(new TriggerNode("magtheridon boss engaged by main tank", {
NextAction("magtheridon main tank position boss", ACTION_RAID + 2) }));
triggers.push_back(new TriggerNode("magtheridon first three channelers engaged by main tank", {
NextAction("magtheridon main tank attack first three channelers", ACTION_RAID + 1) }));
triggers.push_back(new TriggerNode("magtheridon nw channeler engaged by first assist tank", {
NextAction("magtheridon first assist tank attack nw channeler", ACTION_RAID + 1) }));
triggers.push_back(new TriggerNode("magtheridon ne channeler engaged by second assist tank", {
NextAction("magtheridon second assist tank attack ne channeler", ACTION_RAID + 1) }));
triggers.push_back(new TriggerNode("magtheridon determining kill order", {
NextAction("magtheridon assign dps priority", ACTION_RAID + 1) }));
}
void RaidMagtheridonStrategy::InitMultipliers(std::vector<Multiplier*>& multipliers)
{
multipliers.push_back(new MagtheridonUseManticronCubeMultiplier(botAI));
multipliers.push_back(new MagtheridonWaitToAttackMultiplier(botAI));
multipliers.push_back(new MagtheridonDisableOffTankAssistMultiplier(botAI));
}

View File

@@ -0,0 +1,18 @@
#ifndef _PLAYERBOT_RAIDMAGTHERIDONSTRATEGY_H
#define _PLAYERBOT_RAIDMAGTHERIDONSTRATEGY_H
#include "Strategy.h"
#include "Multiplier.h"
class RaidMagtheridonStrategy : public Strategy
{
public:
RaidMagtheridonStrategy(PlayerbotAI* botAI) : Strategy(botAI) {}
std::string const getName() override { return "magtheridon"; }
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
void InitMultipliers(std::vector<Multiplier*>& multipliers) override;
};
#endif

View File

@@ -0,0 +1,128 @@
#include "RaidMagtheridonTriggers.h"
#include "RaidMagtheridonHelpers.h"
#include "Playerbots.h"
using namespace MagtheridonHelpers;
bool MagtheridonFirstThreeChannelersEngagedByMainTankTrigger::IsActive()
{
Unit* magtheridon = AI_VALUE2(Unit*, "find target", "magtheridon");
return magtheridon && botAI->IsMainTank(bot) &&
magtheridon->HasAura(SPELL_SHADOW_CAGE);
}
bool MagtheridonNWChannelerEngagedByFirstAssistTankTrigger::IsActive()
{
Unit* magtheridon = AI_VALUE2(Unit*, "find target", "magtheridon");
Creature* channelerDiamond = GetChanneler(bot, NORTHWEST_CHANNELER);
return magtheridon && botAI->IsAssistTankOfIndex(bot, 0) &&
channelerDiamond && channelerDiamond->IsAlive();
}
bool MagtheridonNEChannelerEngagedBySecondAssistTankTrigger::IsActive()
{
Unit* magtheridon = AI_VALUE2(Unit*, "find target", "magtheridon");
Creature* channelerTriangle = GetChanneler(bot, NORTHEAST_CHANNELER);
return magtheridon && botAI->IsAssistTankOfIndex(bot, 1) &&
channelerTriangle && channelerTriangle->IsAlive();
}
bool MagtheridonPullingWestAndEastChannelersTrigger::IsActive()
{
Unit* magtheridon = AI_VALUE2(Unit*, "find target", "magtheridon");
Creature* channelerStar = GetChanneler(bot, WEST_CHANNELER);
Creature* channelerCircle = GetChanneler(bot, EAST_CHANNELER);
return magtheridon && bot->getClass() == CLASS_HUNTER &&
((channelerStar && channelerStar->IsAlive()) ||
(channelerCircle && channelerCircle->IsAlive()));
}
bool MagtheridonDeterminingKillOrderTrigger::IsActive()
{
Unit* magtheridon = AI_VALUE2(Unit*, "find target", "magtheridon");
Unit* channeler = AI_VALUE2(Unit*, "find target", "hellfire channeler");
Creature* channelerDiamond = GetChanneler(bot, NORTHWEST_CHANNELER);
Creature* channelerTriangle = GetChanneler(bot, NORTHEAST_CHANNELER);
if (!magtheridon || botAI->IsHeal(bot) || botAI->IsMainTank(bot) ||
(botAI->IsAssistTankOfIndex(bot, 0) && channelerDiamond && channelerDiamond->IsAlive()) ||
(botAI->IsAssistTankOfIndex(bot, 1) && channelerTriangle && channelerTriangle->IsAlive()))
return false;
return (channeler && channeler->IsAlive()) || (magtheridon &&
!magtheridon->HasAura(SPELL_SHADOW_CAGE));
}
bool MagtheridonBurningAbyssalSpawnedTrigger::IsActive()
{
Unit* magtheridon = AI_VALUE2(Unit*, "find target", "magtheridon");
if (!magtheridon || bot->getClass() != CLASS_WARLOCK)
return false;
const GuidVector& npcs = AI_VALUE(GuidVector, "nearest hostile npcs");
return std::any_of(npcs.begin(), npcs.end(), [this](const ObjectGuid& npc)
{
Unit* unit = botAI->GetUnit(npc);
return unit && unit->GetEntry() == NPC_BURNING_ABYSSAL;
});
}
bool MagtheridonBossEngagedByMainTankTrigger::IsActive()
{
Unit* magtheridon = AI_VALUE2(Unit*, "find target", "magtheridon");
return magtheridon && botAI->IsMainTank(bot) &&
!magtheridon->HasAura(SPELL_SHADOW_CAGE);
}
bool MagtheridonBossEngagedByRangedTrigger::IsActive()
{
Unit* magtheridon = AI_VALUE2(Unit*, "find target", "magtheridon");
Unit* channeler = AI_VALUE2(Unit*, "find target", "hellfire channeler");
return magtheridon && botAI->IsRanged(bot) &&
!(channeler && channeler->IsAlive());
}
bool MagtheridonIncomingBlastNovaTrigger::IsActive()
{
Unit* magtheridon = AI_VALUE2(Unit*, "find target", "magtheridon");
Group* group = bot->GetGroup();
if (!group || !magtheridon || magtheridon->HasAura(SPELL_SHADOW_CAGE))
return false;
bool needsReassign = botToCubeAssignment.empty();
if (!needsReassign)
{
for (auto const& pair : botToCubeAssignment)
{
Player* assigned = ObjectAccessor::FindPlayer(pair.first);
if (!assigned || !assigned->IsAlive())
{
needsReassign = true;
break;
}
}
}
if (needsReassign)
{
std::vector<CubeInfo> cubes = GetAllCubeInfosByDbGuids(bot->GetMap(), MANTICRON_CUBE_DB_GUIDS);
AssignBotsToCubesByGuidAndCoords(group, cubes, botAI);
}
return botToCubeAssignment.find(bot->GetGUID()) != botToCubeAssignment.end();
}
bool MagtheridonNeedToManageTimersAndAssignmentsTrigger::IsActive()
{
Unit* magtheridon = AI_VALUE2(Unit*, "find target", "magtheridon");
return magtheridon;
}

View File

@@ -0,0 +1,77 @@
#ifndef _PLAYERBOT_RAIDMAGTHERIDONTRIGGERS_H
#define _PLAYERBOT_RAIDMAGTHERIDONTRIGGERS_H
#include "Trigger.h"
#include "PlayerbotAI.h"
class MagtheridonFirstThreeChannelersEngagedByMainTankTrigger : public Trigger
{
public:
MagtheridonFirstThreeChannelersEngagedByMainTankTrigger(PlayerbotAI* botAI) : Trigger(botAI, "magtheridon first three channelers engaged by main tank") {};
bool IsActive() override;
};
class MagtheridonNWChannelerEngagedByFirstAssistTankTrigger : public Trigger
{
public:
MagtheridonNWChannelerEngagedByFirstAssistTankTrigger(PlayerbotAI* botAI) : Trigger(botAI, "magtheridon nw channeler engaged by first assist tank") {};
bool IsActive() override;
};
class MagtheridonNEChannelerEngagedBySecondAssistTankTrigger : public Trigger
{
public:
MagtheridonNEChannelerEngagedBySecondAssistTankTrigger(PlayerbotAI* botAI) : Trigger(botAI, "magtheridon ne channeler engaged by second assist tank") {};
bool IsActive() override;
};
class MagtheridonPullingWestAndEastChannelersTrigger : public Trigger
{
public:
MagtheridonPullingWestAndEastChannelersTrigger(PlayerbotAI* botAI) : Trigger(botAI, "magtheridon pulling west and east channelers") {};
bool IsActive() override;
};
class MagtheridonDeterminingKillOrderTrigger : public Trigger
{
public:
MagtheridonDeterminingKillOrderTrigger(PlayerbotAI* botAI) : Trigger(botAI, "magtheridon determining kill order") {};
bool IsActive() override;
};
class MagtheridonBurningAbyssalSpawnedTrigger : public Trigger
{
public:
MagtheridonBurningAbyssalSpawnedTrigger(PlayerbotAI* botAI) : Trigger(botAI, "magtheridon burning abyssal spawned") {};
bool IsActive() override;
};
class MagtheridonBossEngagedByMainTankTrigger : public Trigger
{
public:
MagtheridonBossEngagedByMainTankTrigger(PlayerbotAI* botAI) : Trigger(botAI, "magtheridon boss engaged by main tank") {};
bool IsActive() override;
};
class MagtheridonBossEngagedByRangedTrigger : public Trigger
{
public:
MagtheridonBossEngagedByRangedTrigger(PlayerbotAI* botAI) : Trigger(botAI, "magtheridon boss engaged by ranged") {};
bool IsActive() override;
};
class MagtheridonIncomingBlastNovaTrigger : public Trigger
{
public:
MagtheridonIncomingBlastNovaTrigger(PlayerbotAI* botAI) : Trigger(botAI, "magtheridon incoming blast nova") {};
bool IsActive() override;
};
class MagtheridonNeedToManageTimersAndAssignmentsTrigger : public Trigger
{
public:
MagtheridonNeedToManageTimersAndAssignmentsTrigger(PlayerbotAI* botAI) : Trigger(botAI, "magtheridon need to manage timers and assignments") {};
bool IsActive() override;
};
#endif

View File

@@ -0,0 +1,226 @@
#include "RaidMagtheridonHelpers.h"
#include "Creature.h"
#include "GameObject.h"
#include "GroupReference.h"
#include "Map.h"
#include "ObjectGuid.h"
#include "Playerbots.h"
namespace MagtheridonHelpers
{
namespace MagtheridonsLairLocations
{
const Location WaitingForMagtheridonPosition = { 1.359f, 2.048f, -0.406f, 3.135f };
const Location MagtheridonTankPosition = { 22.827f, 2.105f, -0.406f, 3.135f };
const Location NWChannelerTankPosition = { -11.764f, 30.818f, -0.411f, 0.0f };
const Location NEChannelerTankPosition = { -12.490f, -26.211f, -0.411f, 0.0f };
const Location RangedSpreadPosition = { -14.890f, 1.995f, -0.406f, 0.0f };
const Location HealerSpreadPosition = { -2.265f, 1.874f, -0.404f, 0.0f };
}
// Identify channelers by their database GUIDs
Creature* GetChanneler(Player* bot, uint32 dbGuid)
{
Map* map = bot->GetMap();
if (!map)
return nullptr;
auto it = map->GetCreatureBySpawnIdStore().find(dbGuid);
if (it == map->GetCreatureBySpawnIdStore().end())
return nullptr;
return it->second;
}
void MarkTargetWithIcon(Player* bot, Unit* target, uint8 iconId)
{
Group* group = bot->GetGroup();
if (!target || !group)
return;
ObjectGuid currentGuid = group->GetTargetIcon(iconId);
if (currentGuid != target->GetGUID())
group->SetTargetIcon(iconId, bot->GetGUID(), target->GetGUID());
}
void SetRtiTarget(PlayerbotAI* botAI, const std::string& rtiName, Unit* target)
{
if (!target)
return;
std::string currentRti = botAI->GetAiObjectContext()->GetValue<std::string>("rti")->Get();
Unit* currentTarget = botAI->GetAiObjectContext()->GetValue<Unit*>("rti target")->Get();
if (currentRti != rtiName || currentTarget != target)
{
botAI->GetAiObjectContext()->GetValue<std::string>("rti")->Set(rtiName);
botAI->GetAiObjectContext()->GetValue<Unit*>("rti target")->Set(target);
}
}
void MarkTargetWithSquare(Player* bot, Unit* target)
{
MarkTargetWithIcon(bot, target, RtiTargetValue::squareIndex);
}
void MarkTargetWithStar(Player* bot, Unit* target)
{
MarkTargetWithIcon(bot, target, RtiTargetValue::starIndex);
}
void MarkTargetWithCircle(Player* bot, Unit* target)
{
MarkTargetWithIcon(bot, target, RtiTargetValue::circleIndex);
}
void MarkTargetWithDiamond(Player* bot, Unit* target)
{
MarkTargetWithIcon(bot, target, RtiTargetValue::diamondIndex);
}
void MarkTargetWithTriangle(Player* bot, Unit* target)
{
MarkTargetWithIcon(bot, target, RtiTargetValue::triangleIndex);
}
void MarkTargetWithCross(Player* bot, Unit* target)
{
MarkTargetWithIcon(bot, target, RtiTargetValue::crossIndex);
}
const std::vector<uint32> MANTICRON_CUBE_DB_GUIDS = { 43157, 43158, 43159, 43160, 43161 };
// Get the positions of all Manticron Cubes by their database GUIDs
std::vector<CubeInfo> GetAllCubeInfosByDbGuids(Map* map, const std::vector<uint32>& cubeDbGuids)
{
std::vector<CubeInfo> cubes;
if (!map)
return cubes;
for (uint32 dbGuid : cubeDbGuids)
{
auto bounds = map->GetGameObjectBySpawnIdStore().equal_range(dbGuid);
if (bounds.first == bounds.second)
continue;
GameObject* go = bounds.first->second;
if (!go)
continue;
CubeInfo info;
info.guid = go->GetGUID();
info.x = go->GetPositionX();
info.y = go->GetPositionY();
info.z = go->GetPositionZ();
cubes.push_back(info);
}
return cubes;
}
std::unordered_map<ObjectGuid, CubeInfo> botToCubeAssignment;
void AssignBotsToCubesByGuidAndCoords(Group* group, const std::vector<CubeInfo>& cubes, PlayerbotAI* botAI)
{
botToCubeAssignment.clear();
if (!group)
return;
size_t cubeIndex = 0;
std::vector<Player*> candidates;
// Assign ranged DPS (excluding Warlocks) to cubes first
for (GroupReference* ref = group->GetFirstMember(); ref && cubeIndex < cubes.size(); ref = ref->next())
{
Player* member = ref->GetSource();
if (!member || !member->IsAlive() || !botAI->IsRangedDps(member, true) ||
member->getClass() == CLASS_WARLOCK || !GET_PLAYERBOT_AI(member))
continue;
candidates.push_back(member);
if (candidates.size() >= cubes.size())
break;
}
// If there are still cubes left, assign any other non-tank bots
if (candidates.size() < cubes.size())
{
for (GroupReference* ref = group->GetFirstMember();
ref && candidates.size() < cubes.size(); ref = ref->next())
{
Player* member = ref->GetSource();
if (!member || !member->IsAlive() || !GET_PLAYERBOT_AI(member) || botAI->IsTank(member))
continue;
if (std::find(candidates.begin(), candidates.end(), member) == candidates.end())
candidates.push_back(member);
}
}
for (Player* member : candidates)
{
if (cubeIndex >= cubes.size())
break;
if (!member || !member->IsAlive())
continue;
botToCubeAssignment[member->GetGUID()] = cubes[cubeIndex++];
}
}
std::unordered_map<uint32, bool> lastBlastNovaState;
std::unordered_map<uint32, time_t> blastNovaTimer;
std::unordered_map<uint32, time_t> spreadWaitTimer;
std::unordered_map<uint32, time_t> dpsWaitTimer;
bool IsSafeFromMagtheridonHazards(PlayerbotAI* botAI, Player* bot, float x, float y, float z)
{
// Debris
std::vector<Unit*> debrisHazards;
const GuidVector npcs = botAI->GetAiObjectContext()->GetValue<GuidVector>("nearest npcs")->Get();
for (auto const& npcGuid : npcs)
{
Unit* unit = botAI->GetUnit(npcGuid);
if (!unit || unit->GetEntry() != NPC_TARGET_TRIGGER)
continue;
debrisHazards.push_back(unit);
}
for (Unit* hazard : debrisHazards)
{
float dist = std::sqrt(std::pow(x - hazard->GetPositionX(), 2) + std::pow(y - hazard->GetPositionY(), 2));
if (dist < 9.0f)
return false;
}
// Conflagration
GuidVector gos = *botAI->GetAiObjectContext()->GetValue<GuidVector>("nearest game objects");
for (auto const& goGuid : gos)
{
GameObject* go = botAI->GetGameObject(goGuid);
if (!go || go->GetEntry() != GO_BLAZE)
continue;
float dist = std::sqrt(std::pow(x - go->GetPositionX(), 2) + std::pow(y - go->GetPositionY(), 2));
if (dist < 5.0f)
return false;
}
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

@@ -0,0 +1,90 @@
#ifndef _PLAYERBOT_RAIDMAGTHERIDONHELPERS_H
#define _PLAYERBOT_RAIDMAGTHERIDONHELPERS_H
#include <ctime>
#include <unordered_map>
#include <vector>
#include "Group.h"
#include "ObjectGuid.h"
#include "PlayerbotAI.h"
#include "RtiTargetValue.h"
namespace MagtheridonHelpers
{
enum MagtheridonSpells
{
// Magtheridon
SPELL_SHADOW_CAGE = 30205,
SPELL_BLAST_NOVA = 30616,
SPELL_SHADOW_GRASP = 30410,
// Warlock
SPELL_BANISH = 18647,
SPELL_FEAR = 6215,
// Hunter
SPELL_MISDIRECTION = 35079,
};
enum MagtheridonNPCs
{
NPC_BURNING_ABYSSAL = 17454,
NPC_TARGET_TRIGGER = 17474,
};
enum MagtheridonObjects
{
GO_BLAZE = 181832,
};
constexpr uint32 SOUTH_CHANNELER = 90978;
constexpr uint32 WEST_CHANNELER = 90979;
constexpr uint32 NORTHWEST_CHANNELER = 90980;
constexpr uint32 EAST_CHANNELER = 90982;
constexpr uint32 NORTHEAST_CHANNELER = 90981;
Creature* GetChanneler(Player* bot, uint32 dbGuid);
void MarkTargetWithIcon(Player* bot, Unit* target, uint8 iconId);
void MarkTargetWithSquare(Player* bot, Unit* target);
void MarkTargetWithStar(Player* bot, Unit* target);
void MarkTargetWithCircle(Player* bot, Unit* target);
void MarkTargetWithDiamond(Player* bot, Unit* target);
void MarkTargetWithTriangle(Player* bot, Unit* target);
void MarkTargetWithCross(Player* bot, Unit* target);
void SetRtiTarget(PlayerbotAI* botAI, const std::string& rtiName, Unit* target);
bool IsSafeFromMagtheridonHazards(PlayerbotAI* botAI, Player* bot, float x, float y, float z);
bool IsInstanceTimerManager(PlayerbotAI* botAI, Player* bot);
struct Location
{
float x, y, z, orientation;
};
namespace MagtheridonsLairLocations
{
extern const Location WaitingForMagtheridonPosition;
extern const Location MagtheridonTankPosition;
extern const Location NWChannelerTankPosition;
extern const Location NEChannelerTankPosition;
extern const Location RangedSpreadPosition;
extern const Location HealerSpreadPosition;
}
struct CubeInfo
{
ObjectGuid guid;
float x, y, z;
};
extern const std::vector<uint32> MANTICRON_CUBE_DB_GUIDS;
extern std::unordered_map<ObjectGuid, CubeInfo> botToCubeAssignment;
std::vector<CubeInfo> GetAllCubeInfosByDbGuids(Map* map, const std::vector<uint32>& cubeDbGuids);
void AssignBotsToCubesByGuidAndCoords(Group* group, const std::vector<CubeInfo>& cubes, PlayerbotAI* botAI);
extern std::unordered_map<uint32, bool> lastBlastNovaState;
extern std::unordered_map<uint32, time_t> blastNovaTimer;
extern std::unordered_map<uint32, time_t> spreadWaitTimer;
extern std::unordered_map<uint32, time_t> dpsWaitTimer;
}
#endif