Yogg-Saron strategy (#1565)

* - wip

* - Added Yogg-Saron strategy

* - Added Yogg-Saron sanity strategy

* - WIP

* - WIP

* - WIP

* - WIP

* - Added Yogg-Saron strategy

* - code refactoring

* - Code fix after pr
This commit is contained in:
kadeshar
2025-08-18 12:02:19 +02:00
committed by GitHub
parent b369b1f9ae
commit 77c2354c3f
17 changed files with 2320 additions and 48 deletions

View File

@@ -1765,7 +1765,7 @@ bool PlayerbotAI::IsRangedDps(Player* player, bool bySpec) { return IsRanged(pla
bool PlayerbotAI::IsHealAssistantOfIndex(Player* player, int index)
{
Group* group = bot->GetGroup();
Group* group = player->GetGroup();
if (!group)
{
return false;
@@ -1777,6 +1777,11 @@ bool PlayerbotAI::IsHealAssistantOfIndex(Player* player, int index)
{
Player* member = ref->GetSource();
if (!member)
{
continue;
}
if (IsHeal(member)) // Check if the member is a healer
{
bool isAssistant = group->IsAssistant(member->GetGUID());
@@ -1796,7 +1801,7 @@ bool PlayerbotAI::IsHealAssistantOfIndex(Player* player, int index)
bool PlayerbotAI::IsRangedDpsAssistantOfIndex(Player* player, int index)
{
Group* group = bot->GetGroup();
Group* group = player->GetGroup();
if (!group)
{
return false;
@@ -1808,6 +1813,11 @@ bool PlayerbotAI::IsRangedDpsAssistantOfIndex(Player* player, int index)
{
Player* member = ref->GetSource();
if (!member)
{
continue;
}
if (IsRangedDps(member)) // Check if the member is a ranged DPS
{
bool isAssistant = group->IsAssistant(member->GetGUID());
@@ -1840,6 +1850,35 @@ bool PlayerbotAI::HasAggro(Unit* unit)
return false;
}
int32 PlayerbotAI::GetAssistTankIndex(Player* player)
{
Group* group = player->GetGroup();
if (!group)
{
return -1;
}
int counter = 0;
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
{
Player* member = ref->GetSource();
if (!member)
{
continue;
}
if (player == member)
{
return counter;
}
if (IsTank(member, true) && group->IsAssistant(member->GetGUID()))
{
counter++;
}
}
return 0;
}
int32 PlayerbotAI::GetGroupSlotIndex(Player* player)
{
Group* group = bot->GetGroup();
@@ -1851,6 +1890,12 @@ int32 PlayerbotAI::GetGroupSlotIndex(Player* player)
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
{
Player* member = ref->GetSource();
if (!member)
{
continue;
}
if (player == member)
{
return counter;
@@ -1875,6 +1920,12 @@ int32 PlayerbotAI::GetRangedIndex(Player* player)
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
{
Player* member = ref->GetSource();
if (!member)
{
continue;
}
if (player == member)
{
return counter;
@@ -1902,6 +1953,12 @@ int32 PlayerbotAI::GetClassIndex(Player* player, uint8 cls)
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
{
Player* member = ref->GetSource();
if (!member)
{
continue;
}
if (player == member)
{
return counter;
@@ -1928,6 +1985,12 @@ int32 PlayerbotAI::GetRangedDpsIndex(Player* player)
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
{
Player* member = ref->GetSource();
if (!member)
{
continue;
}
if (player == member)
{
return counter;
@@ -1955,6 +2018,12 @@ int32 PlayerbotAI::GetMeleeIndex(Player* player)
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
{
Player* member = ref->GetSource();
if (!member)
{
continue;
}
if (player == member)
{
return counter;
@@ -2126,6 +2195,12 @@ bool PlayerbotAI::IsMainTank(Player* player)
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
{
Player* member = ref->GetSource();
if (!member)
{
continue;
}
if (IsTank(member) && member->IsAlive())
{
return player->GetGUID() == member->GetGUID();
@@ -2134,6 +2209,62 @@ bool PlayerbotAI::IsMainTank(Player* player)
return false;
}
bool PlayerbotAI::IsBotMainTank(Player* player)
{
if (!player->GetSession()->IsBot() || !IsTank(player))
{
return false;
}
if (IsMainTank(player))
{
return true;
}
Group* group = player->GetGroup();
if (!group)
{
return true; // If no group, consider the bot as main tank
}
uint32 botAssistTankIndex = GetAssistTankIndex(player);
if (botAssistTankIndex == -1)
{
return false;
}
for (GroupReference* gref = group->GetFirstMember(); gref; gref = gref->next())
{
Player* member = gref->GetSource();
if (!member)
{
continue;
}
uint32 memberAssistTankIndex = GetAssistTankIndex(member);
if (memberAssistTankIndex == -1)
{
continue;
}
if (memberAssistTankIndex == botAssistTankIndex && player == member)
{
return true;
}
if (memberAssistTankIndex < botAssistTankIndex && member->GetSession()->IsBot())
{
return false;
}
return false;
}
return false;
}
uint32 PlayerbotAI::GetGroupTankNum(Player* player)
{
Group* group = player->GetGroup();
@@ -2145,6 +2276,12 @@ uint32 PlayerbotAI::GetGroupTankNum(Player* player)
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
{
Player* member = ref->GetSource();
if (!member)
{
continue;
}
if (IsTank(member) && member->IsAlive())
{
result++;
@@ -2157,7 +2294,7 @@ bool PlayerbotAI::IsAssistTank(Player* player) { return IsTank(player) && !IsMai
bool PlayerbotAI::IsAssistTankOfIndex(Player* player, int index)
{
Group* group = bot->GetGroup();
Group* group = player->GetGroup();
if (!group)
{
return false;
@@ -2166,6 +2303,12 @@ bool PlayerbotAI::IsAssistTankOfIndex(Player* player, int index)
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
{
Player* member = ref->GetSource();
if (!member)
{
continue;
}
if (group->IsAssistant(member->GetGUID()) && IsAssistTank(member))
{
if (index == counter)
@@ -2179,6 +2322,12 @@ bool PlayerbotAI::IsAssistTankOfIndex(Player* player, int index)
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
{
Player* member = ref->GetSource();
if (!member)
{
continue;
}
if (!group->IsAssistant(member->GetGUID()) && IsAssistTank(member))
{
if (index == counter)
@@ -2386,6 +2535,11 @@ std::vector<Player*> PlayerbotAI::GetPlayersInGroup()
{
Player* member = ref->GetSource();
if (!member)
{
continue;
}
if (GET_PLAYERBOT_AI(member) && !GET_PLAYERBOT_AI(member)->IsRealPlayer())
continue;

View File

@@ -423,13 +423,15 @@ public:
static bool IsCaster(Player* player, bool bySpec = false);
static bool IsRangedDps(Player* player, bool bySpec = false);
static bool IsCombo(Player* player);
static bool IsBotMainTank(Player* player);
static bool IsMainTank(Player* player);
static uint32 GetGroupTankNum(Player* player);
bool IsAssistTank(Player* player);
bool IsAssistTankOfIndex(Player* player, int index);
bool IsHealAssistantOfIndex(Player* player, int index);
bool IsRangedDpsAssistantOfIndex(Player* player, int index);
static bool IsAssistTank(Player* player);
static bool IsAssistTankOfIndex(Player* player, int index);
static bool IsHealAssistantOfIndex(Player* player, int index);
static bool IsRangedDpsAssistantOfIndex(Player* player, int index);
bool HasAggro(Unit* unit);
static int32 GetAssistTankIndex(Player* player);
int32 GetGroupSlotIndex(Player* player);
int32 GetRangedIndex(Player* player);
int32 GetClassIndex(Player* player, uint8 cls);

View File

@@ -22,6 +22,7 @@ bool BossFireResistanceAction::Execute(Event event)
{
PaladinFireResistanceStrategy paladinFireResistanceStrategy(botAI);
botAI->ChangeStrategy(ADD_STRATEGY_CHAR + paladinFireResistanceStrategy.getName(), BotState::BOT_STATE_COMBAT);
botAI->DoSpecificAction("fire resistance aura", Event(), true);
return true;
}
@@ -35,6 +36,7 @@ bool BossFrostResistanceAction::Execute(Event event)
{
PaladinFrostResistanceStrategy paladinFrostResistanceStrategy(botAI);
botAI->ChangeStrategy(ADD_STRATEGY_CHAR + paladinFrostResistanceStrategy.getName(), BotState::BOT_STATE_COMBAT);
botAI->DoSpecificAction("frost resistance aura", Event(), true);
return true;
}
@@ -48,6 +50,7 @@ bool BossNatureResistanceAction::Execute(Event event)
{
HunterNatureResistanceStrategy hunterNatureResistanceStrategy(botAI);
botAI->ChangeStrategy(ADD_STRATEGY_CHAR + hunterNatureResistanceStrategy.getName(), BotState::BOT_STATE_COMBAT);
botAI->DoSpecificAction("aspect of the wild", Event(), true);
return true;
}
@@ -61,5 +64,6 @@ bool BossShadowResistanceAction::Execute(Event event)
{
PaladinShadowResistanceStrategy paladinShadowResistanceStrategy(botAI);
botAI->ChangeStrategy(ADD_STRATEGY_CHAR + paladinShadowResistanceStrategy.getName(), BotState::BOT_STATE_COMBAT);
botAI->DoSpecificAction("shadow resistance aura", Event(), true);
return true;
}

View File

@@ -2767,3 +2767,177 @@ bool MoveFromGroupAction::Execute(Event event)
distance = 20.0f; // flee distance from config is too small for this
return MoveFromGroup(distance);
}
bool MoveAwayFromCreatureAction::Execute(Event event)
{
GuidVector targets = AI_VALUE(GuidVector, "nearest npcs");
Creature* nearestCreature = bot->FindNearestCreature(creatureId, range, alive);
// Find all creatures with the specified Id
std::vector<Unit*> creatures;
for (const auto& guid : targets)
{
Unit* unit = botAI->GetUnit(guid);
if (unit && (alive && unit->IsAlive()) && unit->GetEntry() == creatureId)
{
creatures.push_back(unit);
}
}
if (creatures.empty())
{
return false;
}
// Search for a safe position
const int directions = 8;
const float increment = 3.0f;
float bestX = bot->GetPositionX();
float bestY = bot->GetPositionY();
float bestZ = bot->GetPositionZ();
float maxSafetyScore = -1.0f;
for (int i = 0; i < directions; ++i)
{
float angle = (i * 2 * M_PI) / directions;
for (float distance = increment; distance <= 30.0f; distance += increment)
{
float moveX = bot->GetPositionX() + distance * cos(angle);
float moveY = bot->GetPositionY() + distance * sin(angle);
float moveZ = bot->GetPositionZ();
// Check creature distance constraints
bool isSafeFromCreatures = true;
float minCreatureDist = std::numeric_limits<float>::max();
for (Unit* creature : creatures)
{
float dist = creature->GetExactDist2d(moveX, moveY);
if (dist < range)
{
isSafeFromCreatures = false;
break;
}
if (dist < minCreatureDist)
{
minCreatureDist = dist;
}
}
if (isSafeFromCreatures && bot->IsWithinLOS(moveX, moveY, moveZ))
{
// A simple safety score: the minimum distance to any creature. Higher is better.
if (minCreatureDist > maxSafetyScore)
{
maxSafetyScore = minCreatureDist;
bestX = moveX;
bestY = moveY;
bestZ = moveZ;
}
}
}
}
// Move to the best position found
if (maxSafetyScore > 0.0f)
{
return MoveTo(bot->GetMapId(), bestX, bestY, bestZ, false, false, false, false,
MovementPriority::MOVEMENT_COMBAT);
}
return false;
}
bool MoveAwayFromCreatureAction::isPossible()
{
return bot->CanFreeMove();
}
bool MoveAwayFromPlayerWithDebuffAction::Execute(Event event)
{
Player* closestPlayer = nullptr;
float minDistance = 0.0f;
Group* group = bot->GetGroup();
if (!group)
{
return false;
}
std::vector<Player*> debuffedPlayers;
for (GroupReference* gref = group->GetFirstMember(); gref; gref = gref->next())
{
Player* player = gref->GetSource();
if (player && player->IsAlive() && player->HasAura(spellId))
{
debuffedPlayers.push_back(player);
}
}
if (debuffedPlayers.empty())
{
return false;
}
// Search for a safe position
const int directions = 8;
const float increment = 3.0f;
float bestX = bot->GetPositionX();
float bestY = bot->GetPositionY();
float bestZ = bot->GetPositionZ();
float maxSafetyScore = -1.0f;
for (int i = 0; i < directions; ++i)
{
float angle = (i * 2 * M_PI) / directions;
for (float distance = increment; distance <= (range + 5.0f); distance += increment)
{
float moveX = bot->GetPositionX() + distance * cos(angle);
float moveY = bot->GetPositionY() + distance * sin(angle);
float moveZ = bot->GetPositionZ();
// Check creature distance constraints
bool isSafeFromDebuffedPlayer = true;
float minDebuffedPlayerDistance = std::numeric_limits<float>::max();
for (Unit* debuffedPlayer : debuffedPlayers)
{
float dist = debuffedPlayer->GetExactDist2d(moveX, moveY);
if (dist < range)
{
isSafeFromDebuffedPlayer = false;
break;
}
if (dist < minDebuffedPlayerDistance)
{
minDebuffedPlayerDistance = dist;
}
}
if (isSafeFromDebuffedPlayer && bot->IsWithinLOS(moveX, moveY, moveZ))
{
// A simple safety score: the minimum distance to any debuffed player. Higher is better.
if (minDebuffedPlayerDistance > maxSafetyScore)
{
maxSafetyScore = minDebuffedPlayerDistance;
bestX = moveX;
bestY = moveY;
bestZ = moveZ;
}
}
}
}
// Move to the best position found
if (maxSafetyScore > 0.0f)
{
return MoveTo(bot->GetMapId(), bestX, bestY, bestZ, false, false, false, false,
MovementPriority::MOVEMENT_COMBAT, true);
}
return false;
}
bool MoveAwayFromPlayerWithDebuffAction::isPossible()
{
return bot->CanFreeMove();
}

View File

@@ -294,4 +294,34 @@ public:
bool Execute(Event event) override;
};
class MoveAwayFromCreatureAction : public MovementAction
{
public:
MoveAwayFromCreatureAction(PlayerbotAI* botAI, std::string name, uint32 creatureId, float range, bool alive = true)
: MovementAction(botAI, name), creatureId(creatureId), range(range), alive(alive) {}
bool Execute(Event event) override;
bool isPossible() override;
private:
uint32 creatureId;
float range;
bool alive;
};
class MoveAwayFromPlayerWithDebuffAction : public MovementAction
{
public:
MoveAwayFromPlayerWithDebuffAction(PlayerbotAI* botAI, std::string name, uint32 spellId, float range)
: MovementAction(botAI, name), spellId(spellId), range(range) {}
bool Execute(Event event) override;
bool isPossible() override;
private:
uint32 spellId;
float range;
bool alive;
};
#endif

View File

@@ -65,6 +65,24 @@ public:
creators["vezax cheat action"] = &RaidUlduarActionContext::vezax_cheat_action;
creators["vezax shadow crash action"] = &RaidUlduarActionContext::vezax_shadow_crash_action;
creators["vezax mark of the faceless action"] = &RaidUlduarActionContext::vezax_mark_of_the_faceless_action;
creators["vezax shadow resistance action"] = &RaidUlduarActionContext::vezax_shadow_resistance_action;
creators["sara shadow resistance action"] = &RaidUlduarActionContext::sara_shadow_resistance_action;
creators["yogg-saron shadow resistance action"] = &RaidUlduarActionContext::yogg_saron_shadow_resistance_action;
creators["yogg-saron ominous cloud cheat action"] = &RaidUlduarActionContext::yogg_saron_ominous_cloud_cheat_action;
creators["yogg-saron guardian positioning action"] = &RaidUlduarActionContext::yogg_saron_guardian_positioning_action;
creators["yogg-saron sanity action"] = &RaidUlduarActionContext::yogg_saron_sanity_action;
creators["yogg-saron death orb action"] = &RaidUlduarActionContext::yogg_saron_death_orb_action;
creators["yogg-saron malady of the mind action"] = &RaidUlduarActionContext::yogg_saron_malady_of_the_mind_action;
creators["yogg-saron mark target action"] = &RaidUlduarActionContext::yogg_saron_mark_target_action;
creators["yogg-saron brain link action"] = &RaidUlduarActionContext::yogg_saron_brain_link_action;
creators["yogg-saron move to enter portal action"] = &RaidUlduarActionContext::yogg_saron_move_to_enter_portal_action;
creators["yogg-saron use portal action"] = &RaidUlduarActionContext::yogg_saron_use_portal_action;
creators["yogg-saron fall from floor action"] = &RaidUlduarActionContext::yogg_saron_fall_from_floor_action;
creators["yogg-saron boss room movement cheat action"] = &RaidUlduarActionContext::yogg_saron_boss_room_movement_cheat_action;
creators["yogg-saron illusion room action"] = &RaidUlduarActionContext::yogg_saron_illusion_room_action;
creators["yogg-saron move to exit portal action"] = &RaidUlduarActionContext::yogg_saron_move_to_exit_portal_action;
creators["yogg-saron lunatic gaze action"] = &RaidUlduarActionContext::yogg_saron_lunatic_gaze_action;
creators["yogg-saron phase 3 positioning action"] = &RaidUlduarActionContext::yogg_saron_phase_3_positioning_action;
}
private:
@@ -117,6 +135,24 @@ private:
static Action* vezax_cheat_action(PlayerbotAI* ai) { return new VezaxCheatAction(ai); }
static Action* vezax_shadow_crash_action(PlayerbotAI* ai) { return new VezaxShadowCrashAction(ai); }
static Action* vezax_mark_of_the_faceless_action(PlayerbotAI* ai) { return new VezaxMarkOfTheFacelessAction(ai); }
static Action* vezax_shadow_resistance_action(PlayerbotAI* ai) { return new BossShadowResistanceAction(ai, "general vezax"); }
static Action* sara_shadow_resistance_action(PlayerbotAI* ai) { return new BossShadowResistanceAction(ai, "sara"); }
static Action* yogg_saron_shadow_resistance_action(PlayerbotAI* ai) { return new BossShadowResistanceAction(ai, "yogg-saron"); }
static Action* yogg_saron_ominous_cloud_cheat_action(PlayerbotAI* ai) { return new YoggSaronOminousCloudCheatAction(ai); }
static Action* yogg_saron_guardian_positioning_action(PlayerbotAI* ai) { return new YoggSaronGuardianPositioningAction(ai); }
static Action* yogg_saron_sanity_action(PlayerbotAI* ai) { return new YoggSaronSanityAction(ai); }
static Action* yogg_saron_death_orb_action(PlayerbotAI* ai) { return new YoggSaronDeathOrbAction(ai); }
static Action* yogg_saron_malady_of_the_mind_action(PlayerbotAI* ai) { return new YoggSaronMaladyOfTheMindAction(ai); }
static Action* yogg_saron_mark_target_action(PlayerbotAI* ai) { return new YoggSaronMarkTargetAction(ai); }
static Action* yogg_saron_brain_link_action(PlayerbotAI* ai) { return new YoggSaronBrainLinkAction(ai); }
static Action* yogg_saron_move_to_enter_portal_action(PlayerbotAI* ai) { return new YoggSaronMoveToEnterPortalAction(ai); }
static Action* yogg_saron_use_portal_action(PlayerbotAI* ai) { return new YoggSaronUsePortalAction(ai); }
static Action* yogg_saron_fall_from_floor_action(PlayerbotAI* ai) { return new YoggSaronFallFromFloorAction(ai); }
static Action* yogg_saron_boss_room_movement_cheat_action(PlayerbotAI* ai) { return new YoggSaronBossRoomMovementCheatAction(ai); }
static Action* yogg_saron_illusion_room_action(PlayerbotAI* ai) { return new YoggSaronIllusionRoomAction(ai); }
static Action* yogg_saron_move_to_exit_portal_action(PlayerbotAI* ai) { return new YoggSaronMoveToExitPortalAction(ai); }
static Action* yogg_saron_lunatic_gaze_action(PlayerbotAI* ai) { return new YoggSaronLunaticGazeAction(ai); }
static Action* yogg_saron_phase_3_positioning_action(PlayerbotAI* ai) { return new YoggSaronPhase3PositioningAction(ai); }
};
#endif

View File

@@ -20,13 +20,14 @@
#include "RaidUlduarBossHelper.h"
#include "RaidUlduarScripts.h"
#include "RaidUlduarStrategy.h"
#include "RaidUlduarTriggers.h"
#include "RtiValue.h"
#include "ScriptedCreature.h"
#include "ServerFacade.h"
#include "SharedDefines.h"
#include "Unit.h"
#include "Vehicle.h"
#include <RtiTargetValue.h>
#include <TankAssistStrategy.h>
const std::string ADD_STRATEGY_CHAR = "+";
const std::string REMOVE_STRATEGY_CHAR = "-";
@@ -42,6 +43,12 @@ const Position ULDUAR_KOLOGARN_RESTORE_POSITION = Position(1764.3749f, -24.02903
const Position ULDUAR_KOLOGARN_EYEBEAM_LEFT_POSITION = Position(1781.2051f, 9.34402f, 449.0f, 0.00087690353f);
const Position ULDUAR_KOLOGARN_EYEBEAM_RIGHT_POSITION = Position(1763.2561f, -24.44305f, 449.0f, 0.00087690353f);
const Position ULDUAR_THORIM_JUMP_START_POINT = Position(2137.137f, -291.19025f, 438.24753f, 1.7059844f);
const Position ULDUAR_YOGG_SARON_BOSS_ROOM_RESTORE_POINT = Position(1928.8923f, -24.871964f, 324.88956f, 6.247805f);
const Position yoggPortalLoc[] = {
{1970.48f, -9.75f, 325.5f}, {1992.76f, -10.21f, 325.5f}, {1995.53f, -39.78f, 325.5f}, {1969.25f, -42.00f, 325.5f},
{1960.62f, -32.00f, 325.5f}, {1981.98f, -5.69f, 325.5f}, {1982.78f, -45.73f, 325.5f}, {2000.66f, -29.68f, 325.5f},
{1999.88f, -19.61f, 325.5f}, {1961.37f, -19.54f, 325.5f}};
bool FlameLeviathanVehicleAction::Execute(Event event)
{
@@ -1817,24 +1824,24 @@ bool ThorimMarkDpsTargetAction::Execute(Event event)
if (!group)
return false;
ObjectGuid currentMoonTarget = group->GetTargetIcon(moonIndex);
ObjectGuid currentMoonTarget = group->GetTargetIcon(RtiTargetValue::moonIndex);
Unit* currentMoonUnit = botAI->GetUnit(currentMoonTarget);
Unit* boss = AI_VALUE2(Unit*, "find target", "thorim");
if (!currentMoonUnit && boss && boss->IsAlive() && boss->GetPositionZ() > ULDUAR_THORIM_AXIS_Z_FLOOR_THRESHOLD)
{
group->SetTargetIcon(moonIndex, bot->GetGUID(), boss->GetGUID());
group->SetTargetIcon(RtiTargetValue::moonIndex, bot->GetGUID(), boss->GetGUID());
}
if (currentMoonUnit && boss && currentMoonUnit->GetEntry() == boss->GetEntry() &&
boss->GetPositionZ() < ULDUAR_THORIM_AXIS_Z_FLOOR_THRESHOLD)
{
group->SetTargetIcon(skullIndex, bot->GetGUID(), boss->GetGUID());
group->SetTargetIcon(RtiTargetValue::skullIndex, bot->GetGUID(), boss->GetGUID());
return true;
}
if (botAI->IsMainTank(bot))
{
ObjectGuid currentSkullTarget = group->GetTargetIcon(skullIndex);
ObjectGuid currentSkullTarget = group->GetTargetIcon(RtiTargetValue::skullIndex);
Unit* currentSkullUnit = botAI->GetUnit(currentSkullTarget);
if (currentSkullUnit && !currentSkullUnit->IsAlive())
{
@@ -1855,7 +1862,7 @@ bool ThorimMarkDpsTargetAction::Execute(Event event)
}
else if (botAI->IsAssistTankOfIndex(bot, 0))
{
ObjectGuid currentCrossTarget = group->GetTargetIcon(crossIndex);
ObjectGuid currentCrossTarget = group->GetTargetIcon(RtiTargetValue::crossIndex);
Unit* currentCrossUnit = botAI->GetUnit(currentCrossTarget);
if (currentCrossUnit && !currentCrossUnit->IsAlive())
{
@@ -1891,13 +1898,13 @@ bool ThorimMarkDpsTargetAction::Execute(Event event)
if (botAI->IsMainTank(bot))
{
group->SetTargetIcon(skullIndex, bot->GetGUID(), targetToMark->GetGUID());
group->SetTargetIcon(RtiTargetValue::skullIndex, bot->GetGUID(), targetToMark->GetGUID());
return true;
}
if (botAI->IsAssistTankOfIndex(bot, 0))
{
group->SetTargetIcon(crossIndex, bot->GetGUID(), targetToMark->GetGUID());
group->SetTargetIcon(RtiTargetValue::crossIndex, bot->GetGUID(), targetToMark->GetGUID());
return true;
}
@@ -2438,20 +2445,20 @@ bool MimironAerialCommandUnitAction::Execute(Event event)
if (bombBot)
{
group->SetTargetIcon(crossIndex, bot->GetGUID(), bombBot->GetGUID());
group->SetTargetIcon(RtiTargetValue::crossIndex, bot->GetGUID(), bombBot->GetGUID());
}
else if (boss)
{
group->SetTargetIcon(crossIndex, bot->GetGUID(), boss->GetGUID());
group->SetTargetIcon(RtiTargetValue::crossIndex, bot->GetGUID(), boss->GetGUID());
}
if (assaultBot)
{
ObjectGuid skullTarget = group->GetTargetIcon(skullIndex);
ObjectGuid skullTarget = group->GetTargetIcon(RtiTargetValue::skullIndex);
Unit* skullUnit = botAI->GetUnit(skullTarget);
if (!skullTarget || !skullUnit || !skullUnit->IsAlive())
{
group->SetTargetIcon(skullIndex, bot->GetGUID(), assaultBot->GetGUID());
group->SetTargetIcon(RtiTargetValue::skullIndex, bot->GetGUID(), assaultBot->GetGUID());
}
}
@@ -2591,7 +2598,7 @@ bool MimironPhase4MarkDpsAction::Execute(Event event)
highestHealthUnit = aerialCommandUnit;
}
group->SetTargetIcon(skullIndex, bot->GetGUID(), highestHealthUnit->GetGUID());
group->SetTargetIcon(RtiTargetValue::skullIndex, bot->GetGUID(), highestHealthUnit->GetGUID());
if (highestHealthUnit == leviathanMkII)
{
if (AI_VALUE(std::string, "rti") == "skull")
@@ -2601,7 +2608,7 @@ bool MimironPhase4MarkDpsAction::Execute(Event event)
}
else
{
group->SetTargetIcon(crossIndex, bot->GetGUID(), leviathanMkII->GetGUID());
group->SetTargetIcon(RtiTargetValue::crossIndex, bot->GetGUID(), leviathanMkII->GetGUID());
if (AI_VALUE(std::string, "rti") != "cross")
{
botAI->GetAiObjectContext()->GetValue<std::string>("rti")->Set("cross");
@@ -2707,3 +2714,575 @@ bool VezaxMarkOfTheFacelessAction::Execute(Event event)
ULDUAR_VEZAX_MARK_OF_THE_FACELESS_SPOT.GetPositionZ(), false, false, false, true,
MovementPriority::MOVEMENT_FORCED, true, false);
}
bool YoggSaronOminousCloudCheatAction::Execute(Event event)
{
YoggSaronTrigger yoggSaronTrigger(botAI);
Unit* boss = yoggSaronTrigger.GetSaraIfAlive();
if (!boss)
{
return false;
}
Creature* target = boss->FindNearestCreature(NPC_OMINOUS_CLOUD, 25.0f);
if (!target || !target->IsAlive())
{
return false;
}
target->Kill(bot, target);
return true;
}
bool YoggSaronGuardianPositioningAction::Execute(Event event)
{
return MoveTo(bot->GetMapId(), ULDUAR_YOGG_SARON_MIDDLE.GetPositionX(), ULDUAR_YOGG_SARON_MIDDLE.GetPositionY(),
ULDUAR_YOGG_SARON_MIDDLE.GetPositionZ(), false, false, false, true,
MovementPriority::MOVEMENT_FORCED, true, false);
}
bool YoggSaronSanityAction::Execute(Event event)
{
Creature* sanityWell = bot->FindNearestCreature(NPC_SANITY_WELL, 200.0f);
return MoveTo(bot->GetMapId(), sanityWell->GetPositionX(), sanityWell->GetPositionY(), sanityWell->GetPositionZ(),
false, false, false, true, MovementPriority::MOVEMENT_FORCED,
true, false);
}
bool YoggSaronMarkTargetAction::Execute(Event event)
{
Group* group = bot->GetGroup();
if (!group)
{
return false;
}
YoggSaronTrigger yoggSaronTrigger(botAI);
if (yoggSaronTrigger.IsPhase2())
{
if (botAI->HasCheat(BotCheatMask::raid))
{
Unit* crusherTentacle = bot->FindNearestCreature(NPC_CRUSHER_TENTACLE, 200.0f, true);
if (crusherTentacle)
{
crusherTentacle->Kill(bot, crusherTentacle);
}
}
ObjectGuid currentMoonTarget = group->GetTargetIcon(RtiTargetValue::moonIndex);
Creature* yogg_saron = bot->FindNearestCreature(NPC_YOGG_SARON, 200.0f, true);
if (!currentMoonTarget || currentMoonTarget != yogg_saron->GetGUID())
{
group->SetTargetIcon(RtiTargetValue::moonIndex, bot->GetGUID(), yogg_saron->GetGUID());
return true;
}
ObjectGuid currentSkullTarget = group->GetTargetIcon(RtiTargetValue::skullIndex);
Creature* nextPossibleTarget = bot->FindNearestCreature(NPC_CONSTRICTOR_TENTACLE, 200.0f, true);
if (!nextPossibleTarget)
{
nextPossibleTarget = bot->FindNearestCreature(NPC_CORRUPTOR_TENTACLE, 200.0f, true);
if (!nextPossibleTarget)
{
return false;
}
}
if (currentSkullTarget)
{
Unit* currentSkullUnit = botAI->GetUnit(currentSkullTarget);
if (currentSkullUnit && currentSkullUnit->IsAlive() &&
currentSkullUnit->GetGUID() == nextPossibleTarget->GetGUID())
{
return false;
}
}
group->SetTargetIcon(RtiTargetValue::skullIndex, bot->GetGUID(), nextPossibleTarget->GetGUID());
}
else if (yoggSaronTrigger.IsPhase3())
{
TankFaceStrategy tankFaceStrategy(botAI);
if (botAI->HasStrategy(tankFaceStrategy.getName(), BotState::BOT_STATE_COMBAT))
{
botAI->ChangeStrategy(REMOVE_STRATEGY_CHAR + tankFaceStrategy.getName(), BotState::BOT_STATE_COMBAT);
}
TankAssistStrategy tankAssistStrategy(botAI);
if (!botAI->HasStrategy(tankAssistStrategy.getName(), BotState::BOT_STATE_COMBAT))
{
botAI->ChangeStrategy(ADD_STRATEGY_CHAR + tankAssistStrategy.getName(), BotState::BOT_STATE_COMBAT);
}
GuidVector targets = AI_VALUE(GuidVector, "nearest npcs");
int lowestHealth = std::numeric_limits<int>::max();
Unit* lowestHealthUnit = nullptr;
for (const ObjectGuid& guid : targets)
{
Unit* unit = botAI->GetUnit(guid);
if (!unit || !unit->IsAlive())
{
continue;
}
if ((unit->GetEntry() == NPC_IMMORTAL_GUARDIAN || unit->GetEntry() == NPC_MARKED_IMMORTAL_GUARDIAN) &&
unit->GetHealthPct() > 10)
{
if (unit->GetHealth() < lowestHealth)
{
lowestHealth = unit->GetHealth();
lowestHealthUnit = unit;
}
}
}
if (lowestHealthUnit)
{
// Added because lunatic gaze freeze all bots and they can't attack
// If someone fix it then this cheat can be removed
if (botAI->HasCheat(BotCheatMask::raid))
{
lowestHealthUnit->Kill(bot, lowestHealthUnit);
}
else
{
group->SetTargetIcon(RtiTargetValue::skullIndex, bot->GetGUID(), lowestHealthUnit->GetGUID());
}
return true;
}
ObjectGuid currentSkullTarget = group->GetTargetIcon(RtiTargetValue::skullIndex);
Unit* currentSkullUnit = nullptr;
if (currentSkullTarget)
{
currentSkullUnit = botAI->GetUnit(currentSkullTarget);
}
if (!currentSkullUnit || currentSkullUnit->GetEntry() != NPC_YOGG_SARON)
{
Unit* yoggsaron = AI_VALUE2(Unit*, "find target", "yogg-saron");
if (yoggsaron && yoggsaron->IsAlive())
{
group->SetTargetIcon(RtiTargetValue::skullIndex, bot->GetGUID(), yoggsaron->GetGUID());
return true;
}
}
return false;
}
return false;
}
bool YoggSaronBrainLinkAction::Execute(Event event)
{
Group* group = bot->GetGroup();
if (!group)
{
return false;
}
for (GroupReference* gref = group->GetFirstMember(); gref; gref = gref->next())
{
Player* player = gref->GetSource();
if (player && player->IsAlive() && player->HasAura(SPELL_BRAIN_LINK) && player->GetGUID() != bot->GetGUID())
{
return MoveNear(player, 10.0f, MovementPriority::MOVEMENT_FORCED);
}
}
return false;
}
bool YoggSaronMoveToEnterPortalAction::Execute(Event event)
{
Group* group = bot->GetGroup();
if (!group)
{
return false;
}
bool isInBrainRoomTeam = false;
int portalNumber = 0;
int brainRoomTeamCount = 10;
if (bot->GetRaidDifficulty() == Difficulty::RAID_DIFFICULTY_10MAN_NORMAL)
{
brainRoomTeamCount = 4;
}
Player* master = botAI->GetMaster();
if (master && !botAI->IsTank(master))
{
portalNumber++;
brainRoomTeamCount--;
}
for (GroupReference* gref = group->GetFirstMember(); gref; gref = gref->next())
{
Player* member = gref->GetSource();
if (!member || !member->IsAlive() || botAI->IsTank(member) || botAI->GetMaster()->GetGUID() == member->GetGUID())
{
continue;
}
portalNumber++;
if (member->GetGUID() == bot->GetGUID())
{
isInBrainRoomTeam = true;
break;
}
brainRoomTeamCount--;
if (brainRoomTeamCount == 0)
{
break;
}
}
if (!isInBrainRoomTeam)
{
return false;
}
Position assignedPortalPosition = yoggPortalLoc[portalNumber - 1];
botAI->GetAiObjectContext()->GetValue<std::string>("rti")->Set("diamond");
if (botAI->HasCheat(BotCheatMask::raid))
{
return bot->TeleportTo(bot->GetMapId(), assignedPortalPosition.GetPositionX(),
assignedPortalPosition.GetPositionY(),
assignedPortalPosition.GetPositionZ(), bot->GetOrientation());
}
else
{
return MoveNear(bot->GetMapId(), assignedPortalPosition.GetPositionX(),
assignedPortalPosition.GetPositionY(),
assignedPortalPosition.GetPositionZ(), sPlayerbotAIConfig->contactDistance,
MovementPriority::MOVEMENT_FORCED);
}
}
bool YoggSaronFallFromFloorAction::Execute(Event event)
{
std::string rtiMark = AI_VALUE(std::string, "rti");
if (rtiMark == "skull")
{
return bot->TeleportTo(bot->GetMapId(), ULDUAR_YOGG_SARON_BOSS_ROOM_RESTORE_POINT.GetPositionX(),
ULDUAR_YOGG_SARON_BOSS_ROOM_RESTORE_POINT.GetPositionY(),
ULDUAR_YOGG_SARON_BOSS_ROOM_RESTORE_POINT.GetPositionZ(),
ULDUAR_YOGG_SARON_BOSS_ROOM_RESTORE_POINT.GetOrientation());
}
if (rtiMark == "cross")
{
return bot->TeleportTo(bot->GetMapId(), ULDUAR_YOGG_SARON_STORMWIND_KEEPER_MIDDLE.GetPositionX(),
ULDUAR_YOGG_SARON_STORMWIND_KEEPER_MIDDLE.GetPositionY(),
ULDUAR_YOGG_SARON_STORMWIND_KEEPER_MIDDLE.GetPositionZ(),
bot->GetOrientation());
}
if (rtiMark == "circle")
{
return bot->TeleportTo(bot->GetMapId(), ULDUAR_YOGG_SARON_ICECROWN_CITADEL_MIDDLE.GetPositionX(),
ULDUAR_YOGG_SARON_ICECROWN_CITADEL_MIDDLE.GetPositionY(),
ULDUAR_YOGG_SARON_ICECROWN_CITADEL_MIDDLE.GetPositionZ(), bot->GetOrientation());
}
if (rtiMark == "star")
{
return bot->TeleportTo(bot->GetMapId(), ULDUAR_YOGG_SARON_CHAMBER_OF_ASPECTS_MIDDLE.GetPositionX(),
ULDUAR_YOGG_SARON_CHAMBER_OF_ASPECTS_MIDDLE.GetPositionY(),
ULDUAR_YOGG_SARON_CHAMBER_OF_ASPECTS_MIDDLE.GetPositionZ(), bot->GetOrientation());
}
return false;
}
bool YoggSaronBossRoomMovementCheatAction::Execute(Event event)
{
FollowMasterStrategy followMasterStrategy(botAI);
if (botAI->HasStrategy(followMasterStrategy.getName(), BotState::BOT_STATE_NON_COMBAT))
{
botAI->ChangeStrategy(REMOVE_STRATEGY_CHAR + followMasterStrategy.getName(), BotState::BOT_STATE_NON_COMBAT);
}
if (!botAI->HasCheat(BotCheatMask::raid))
{
return false;
}
Group* group = bot->GetGroup();
if (!group)
{
return false;
}
ObjectGuid currentSkullTarget = group->GetTargetIcon(RtiTargetValue::skullIndex);
if (!currentSkullTarget)
{
return false;
}
Unit* currentSkullUnit = botAI->GetUnit(currentSkullTarget);
if (!currentSkullUnit || !currentSkullUnit->IsAlive())
{
return false;
}
return bot->TeleportTo(bot->GetMapId(), currentSkullUnit->GetPositionX(), currentSkullUnit->GetPositionY(),
currentSkullUnit->GetPositionZ(), bot->GetOrientation());
}
bool YoggSaronUsePortalAction::Execute(Event event)
{
Creature* assignedPortal = bot->FindNearestCreature(NPC_DESCEND_INTO_MADNESS, 2.0f, true);
if (!assignedPortal)
{
return false;
}
FollowMasterStrategy followMasterStrategy(botAI);
if (botAI->HasStrategy(followMasterStrategy.getName(), BotState::BOT_STATE_NON_COMBAT))
{
botAI->ChangeStrategy(ADD_STRATEGY_CHAR + followMasterStrategy.getName(), BotState::BOT_STATE_NON_COMBAT);
}
return assignedPortal->HandleSpellClick(bot);
}
bool YoggSaronIllusionRoomAction::Execute(Event event)
{
YoggSaronTrigger yoggSaronTrigger(botAI);
bool resultSetRtiMark = SetRtiMark(yoggSaronTrigger);
bool resultSetIllusionRtiTarget = SetIllusionRtiTarget(yoggSaronTrigger);
bool resultSetBrainRtiTarget = SetBrainRtiTarget(yoggSaronTrigger);
return resultSetRtiMark || resultSetIllusionRtiTarget || resultSetBrainRtiTarget;
}
bool YoggSaronIllusionRoomAction::SetRtiMark(YoggSaronTrigger yoggSaronTrigger)
{
if (AI_VALUE(std::string, "rti") == "diamond")
{
if (yoggSaronTrigger.IsInStormwindKeeperIllusion())
{
botAI->GetAiObjectContext()->GetValue<std::string>("rti")->Set("cross");
return true;
}
else if (yoggSaronTrigger.IsInIcecrownKeeperIllusion())
{
botAI->GetAiObjectContext()->GetValue<std::string>("rti")->Set("circle");
return true;
}
else if (yoggSaronTrigger.IsInChamberOfTheAspectsIllusion())
{
botAI->GetAiObjectContext()->GetValue<std::string>("rti")->Set("star");
return true;
}
}
return false;
}
bool YoggSaronIllusionRoomAction::SetIllusionRtiTarget(YoggSaronTrigger yoggSaronTrigger)
{
Unit* currentRtiTarget = yoggSaronTrigger.GetIllusionRoomRtiTarget();
if (currentRtiTarget)
{
return false;
}
Unit* nextRtiTarget = yoggSaronTrigger.GetNextIllusionRoomRtiTarget();
if (!nextRtiTarget)
{
return false;
}
// If proper adds handling in illusion room will be implemented, then this can be removed
if (botAI->HasCheat(BotCheatMask::raid))
{
bot->TeleportTo(bot->GetMapId(), nextRtiTarget->GetPositionX(), nextRtiTarget->GetPositionY(),
nextRtiTarget->GetPositionZ(), bot->GetOrientation());
Unit::DealDamage(bot->GetSession()->GetPlayer(), nextRtiTarget, nextRtiTarget->GetHealth(), nullptr,
DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, nullptr, false, true);
}
else
{
Group* group = bot->GetGroup();
if (!group)
{
return false;
}
uint8 rtiIndex = RtiTargetValue::GetRtiIndex(AI_VALUE(std::string, "rti"));
group->SetTargetIcon(rtiIndex, bot->GetGUID(), nextRtiTarget->GetGUID());
}
return true;
}
bool YoggSaronIllusionRoomAction::SetBrainRtiTarget(YoggSaronTrigger yoggSaronTrigger)
{
if (AI_VALUE(std::string, "rti") == "square" || !yoggSaronTrigger.IsMasterIsInBrainRoom())
{
return false;
}
botAI->GetAiObjectContext()->GetValue<std::string>("rti")->Set("square");
Group* group = bot->GetGroup();
if (!group)
{
return false;
}
Creature* brain = bot->FindNearestCreature(NPC_BRAIN, 200.0f, true);
if (!brain)
{
return false;
}
group->SetTargetIcon(RtiTargetValue::squareIndex, bot->GetGUID(), brain->GetGUID());
Position entrancePosition = yoggSaronTrigger.GetIllusionRoomEntrancePosition();
if (botAI->HasCheat(BotCheatMask::raid))
{
if (Unit const* master = botAI->GetMaster())
{
Position masterPosition = master->GetPosition();
bot->TeleportTo(bot->GetMapId(), masterPosition.GetPositionX(), masterPosition.GetPositionY(),
masterPosition.GetPositionZ(), bot->GetOrientation());
}
else
{
bot->TeleportTo(bot->GetMapId(), entrancePosition.GetPositionX(), entrancePosition.GetPositionY(),
entrancePosition.GetPositionZ(), bot->GetOrientation());
}
}
else
{
MoveTo(bot->GetMapId(), entrancePosition.GetPositionX(), entrancePosition.GetPositionY(),
entrancePosition.GetPositionZ(), false, false, false, true, MovementPriority::MOVEMENT_FORCED, true,
false);
}
botAI->DoSpecificAction("attack rti target");
return true;
}
bool YoggSaronMoveToExitPortalAction::Execute(Event event)
{
GameObject* portal = bot->FindNearestGameObject(GO_FLEE_TO_THE_SURFACE_PORTAL, 100.0f);
if (!portal)
{
return false;
}
if (botAI->HasCheat(BotCheatMask::raid))
{
bot->TeleportTo(bot->GetMapId(), portal->GetPositionX(), portal->GetPositionY(), portal->GetPositionZ(),
bot->GetOrientation());
}
else
{
MoveTo(bot->GetMapId(), portal->GetPositionX(), portal->GetPositionY(), portal->GetPositionZ(), false,
false, false, true, MovementPriority::MOVEMENT_FORCED,
true, false);
}
if (bot->GetDistance2d(portal) > 2.0f)
{
return false;
}
portal->Use(bot);
botAI->GetAiObjectContext()->GetValue<std::string>("rti")->Set("skull");
return true;
}
bool YoggSaronLunaticGazeAction::Execute(Event event)
{
Unit* boss = AI_VALUE2(Unit*, "find target", "yogg-saron");
if (!boss || !boss->IsAlive())
{
return false;
}
float angle = bot->GetAngle(boss);
float newAngle = Position::NormalizeOrientation(angle + M_PI); // Add 180 degrees (PI radians)
bot->SetFacingTo(newAngle);
if (botAI->IsRangedDps(bot))
{
if (AI_VALUE(std::string, "rti") != "cross")
{
botAI->GetAiObjectContext()->GetValue<std::string>("rti")->Set("cross");
}
}
return true;
}
bool YoggSaronPhase3PositioningAction::Execute(Event event)
{
if (botAI->IsRanged(bot))
{
if (botAI->HasCheat(BotCheatMask::raid))
{
return bot->TeleportTo(bot->GetMapId(), ULDUAR_YOGG_SARON_PHASE_3_RANGED_SPOT.GetPositionX(),
ULDUAR_YOGG_SARON_PHASE_3_RANGED_SPOT.GetPositionY(),
ULDUAR_YOGG_SARON_PHASE_3_RANGED_SPOT.GetPositionZ(),
bot->GetOrientation());
}
else
{
return MoveTo(bot->GetMapId(), ULDUAR_YOGG_SARON_PHASE_3_RANGED_SPOT.GetPositionX(),
ULDUAR_YOGG_SARON_PHASE_3_RANGED_SPOT.GetPositionY(),
ULDUAR_YOGG_SARON_PHASE_3_RANGED_SPOT.GetPositionZ(), false,
false, false, true, MovementPriority::MOVEMENT_FORCED, true, false);
}
}
if (botAI->IsMelee(bot) && !botAI->IsTank(bot))
{
if (botAI->HasCheat(BotCheatMask::raid))
{
return bot->TeleportTo(bot->GetMapId(), ULDUAR_YOGG_SARON_PHASE_3_MELEE_SPOT.GetPositionX(),
ULDUAR_YOGG_SARON_PHASE_3_MELEE_SPOT.GetPositionY(),
ULDUAR_YOGG_SARON_PHASE_3_MELEE_SPOT.GetPositionZ(), bot->GetOrientation());
}
else
{
return MoveTo(bot->GetMapId(), ULDUAR_YOGG_SARON_PHASE_3_MELEE_SPOT.GetPositionX(),
ULDUAR_YOGG_SARON_PHASE_3_MELEE_SPOT.GetPositionY(),
ULDUAR_YOGG_SARON_PHASE_3_MELEE_SPOT.GetPositionZ(), false, false, false, true,
MovementPriority::MOVEMENT_FORCED, true, false);
}
}
if (botAI->IsTank(bot))
{
if (bot->GetDistance(ULDUAR_YOGG_SARON_PHASE_3_MELEE_SPOT) > 30.0f)
{
if (botAI->HasCheat(BotCheatMask::raid))
{
return bot->TeleportTo(bot->GetMapId(), ULDUAR_YOGG_SARON_PHASE_3_MELEE_SPOT.GetPositionX(),
ULDUAR_YOGG_SARON_PHASE_3_MELEE_SPOT.GetPositionY(),
ULDUAR_YOGG_SARON_PHASE_3_MELEE_SPOT.GetPositionZ(), bot->GetOrientation());
}
}
return MoveTo(bot->GetMapId(), ULDUAR_YOGG_SARON_PHASE_3_MELEE_SPOT.GetPositionX(),
ULDUAR_YOGG_SARON_PHASE_3_MELEE_SPOT.GetPositionY(),
ULDUAR_YOGG_SARON_PHASE_3_MELEE_SPOT.GetPositionZ(), false, false, false, true,
MovementPriority::MOVEMENT_FORCED, true, false);
}
return false;
}

View File

@@ -9,6 +9,7 @@
#include "PlayerbotAI.h"
#include "Playerbots.h"
#include "RaidUlduarBossHelper.h"
#include "RaidUlduarTriggers.h"
#include "Vehicle.h"
//
@@ -378,4 +379,125 @@ public:
bool Execute(Event event) override;
};
class YoggSaronOminousCloudCheatAction : public Action
{
public:
YoggSaronOminousCloudCheatAction(PlayerbotAI* ai) : Action(ai, "yogg-saron ominous cloud cheat action") {}
bool Execute(Event event) override;
};
class YoggSaronGuardianPositioningAction : public MovementAction
{
public:
YoggSaronGuardianPositioningAction(PlayerbotAI* ai) : MovementAction(ai, "yogg-saron guardian positioning action") {}
bool Execute(Event event) override;
};
class YoggSaronSanityAction : public MovementAction
{
public:
YoggSaronSanityAction(PlayerbotAI* ai) : MovementAction(ai, "yogg-saron sanity action") {}
bool Execute(Event event) override;
};
class YoggSaronDeathOrbAction : public MoveAwayFromCreatureAction
{
public:
YoggSaronDeathOrbAction(PlayerbotAI* ai) : MoveAwayFromCreatureAction(ai, "yogg-saron death orb action", NPC_DEATH_ORB, 10.0f) {}
};
class YoggSaronMaladyOfTheMindAction : public MoveAwayFromPlayerWithDebuffAction
{
public:
YoggSaronMaladyOfTheMindAction(PlayerbotAI* ai) : MoveAwayFromPlayerWithDebuffAction(ai, "yogg-saron malady of the mind action", SPELL_MALADY_OF_THE_MIND, 15.0f) {}
};
class YoggSaronMarkTargetAction : public Action
{
public:
YoggSaronMarkTargetAction(PlayerbotAI* ai) : Action(ai, "yogg-saron mark target action") {}
bool Execute(Event event) override;
};
class YoggSaronBrainLinkAction : public MovementAction
{
public:
YoggSaronBrainLinkAction(PlayerbotAI* ai) : MovementAction(ai, "yogg-saron brain link action") {}
bool Execute(Event event) override;
};
class YoggSaronMoveToEnterPortalAction : public MovementAction
{
public:
YoggSaronMoveToEnterPortalAction(PlayerbotAI* ai) : MovementAction(ai, "yogg-saron move to enter portal action") {}
bool Execute(Event event) override;
};
class YoggSaronFallFromFloorAction : public MovementAction
{
public:
YoggSaronFallFromFloorAction(PlayerbotAI* ai) : MovementAction(ai, "yogg-saron fall from floor action") {}
bool Execute(Event event) override;
};
class YoggSaronBossRoomMovementCheatAction : public MovementAction
{
public:
YoggSaronBossRoomMovementCheatAction(PlayerbotAI* ai) : MovementAction(ai, "yogg-saron boss room movement cheat action") {}
bool Execute(Event event) override;
};
class YoggSaronUsePortalAction : public Action
{
public:
YoggSaronUsePortalAction(PlayerbotAI* ai) : Action(ai, "yogg-saron use portal action") {}
bool Execute(Event event) override;
};
class YoggSaronIllusionRoomAction : public MovementAction
{
public:
YoggSaronIllusionRoomAction(PlayerbotAI* ai) : MovementAction(ai, "yogg-saron illusion room action") {}
bool Execute(Event event) override;
private:
bool SetRtiMark(YoggSaronTrigger yoggSaronTrigger);
bool SetIllusionRtiTarget(YoggSaronTrigger yoggSaronTrigger);
bool SetBrainRtiTarget(YoggSaronTrigger yoggSaronTrigger);
};
class YoggSaronMoveToExitPortalAction : public MovementAction
{
public:
YoggSaronMoveToExitPortalAction(PlayerbotAI* ai) : MovementAction(ai, "yogg-saron move to exit portal action") {}
bool Execute(Event event) override;
};
class YoggSaronLunaticGazeAction : public MovementAction
{
public:
YoggSaronLunaticGazeAction(PlayerbotAI* ai) : MovementAction(ai, "yogg-saron lunatic gaze action") {}
bool Execute(Event event) override;
};
class YoggSaronPhase3PositioningAction : public MovementAction
{
public:
YoggSaronPhase3PositioningAction(PlayerbotAI* ai) : MovementAction(ai, "yogg-saron phase 3 positioning action") {}
bool Execute(Event event) override;
};
#endif

View File

@@ -255,6 +255,66 @@ void RaidUlduarStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
triggers.push_back(new TriggerNode(
"yogg-saron shadow resistance trigger",
NextAction::array(0, new NextAction("yogg-saron shadow resistance action", ACTION_RAID), nullptr)));
triggers.push_back(new TriggerNode(
"yogg-saron ominous cloud cheat trigger",
NextAction::array(0, new NextAction("yogg-saron ominous cloud cheat action", ACTION_RAID), nullptr)));
triggers.push_back(new TriggerNode(
"yogg-saron guardian positioning trigger",
NextAction::array(0, new NextAction("yogg-saron guardian positioning action", ACTION_RAID), nullptr)));
triggers.push_back(new TriggerNode(
"yogg-saron sanity trigger",
NextAction::array(0, new NextAction("yogg-saron sanity action", ACTION_RAID + 1), nullptr)));
triggers.push_back(new TriggerNode(
"yogg-saron death orb trigger",
NextAction::array(0, new NextAction("yogg-saron death orb action", ACTION_RAID), nullptr)));
triggers.push_back(new TriggerNode(
"yogg-saron malady of the mind trigger",
NextAction::array(0, new NextAction("yogg-saron malady of the mind action", ACTION_RAID), nullptr)));
triggers.push_back(new TriggerNode(
"yogg-saron mark target trigger",
NextAction::array(0, new NextAction("yogg-saron mark target action", ACTION_RAID), nullptr)));
triggers.push_back(new TriggerNode(
"yogg-saron brain link trigger",
NextAction::array(0, new NextAction("yogg-saron brain link action", ACTION_RAID), nullptr)));
triggers.push_back(new TriggerNode(
"yogg-saron move to enter portal trigger",
NextAction::array(0, new NextAction("yogg-saron move to enter portal action", ACTION_RAID), nullptr)));
triggers.push_back(new TriggerNode(
"yogg-saron use portal trigger",
NextAction::array(0, new NextAction("yogg-saron use portal action", ACTION_RAID), nullptr)));
triggers.push_back(new TriggerNode(
"yogg-saron fall from floor trigger",
NextAction::array(0, new NextAction("yogg-saron fall from floor action", ACTION_RAID), nullptr)));
triggers.push_back(new TriggerNode(
"yogg-saron boss room movement cheat trigger",
NextAction::array(0, new NextAction("yogg-saron boss room movement cheat action", ACTION_RAID), nullptr)));
triggers.push_back(new TriggerNode(
"yogg-saron illusion room trigger",
NextAction::array(0, new NextAction("yogg-saron illusion room action", ACTION_RAID), nullptr)));
triggers.push_back(new TriggerNode(
"yogg-saron move to exit portal trigger",
NextAction::array(0, new NextAction("yogg-saron move to exit portal action", ACTION_RAID), nullptr)));
triggers.push_back(new TriggerNode(
"yogg-saron lunatic gaze trigger",
NextAction::array(0, new NextAction("yogg-saron lunatic gaze action", ACTION_EMERGENCY), nullptr)));
triggers.push_back(new TriggerNode(
"yogg-saron phase 3 positioning trigger",
NextAction::array(0, new NextAction("yogg-saron phase 3 positioning action", ACTION_RAID), nullptr)));
}
void RaidUlduarStrategy::InitMultipliers(std::vector<Multiplier*>& multipliers)

View File

@@ -67,6 +67,24 @@ public:
creators["vezax cheat trigger"] = &RaidUlduarTriggerContext::vezax_cheat_trigger;
creators["vezax shadow crash trigger"] = &RaidUlduarTriggerContext::vezax_shadow_crash_trigger;
creators["vezax mark of the faceless trigger"] = &RaidUlduarTriggerContext::vezax_mark_of_the_faceless_trigger;
creators["vezax shadow resistance trigger"] = &RaidUlduarTriggerContext::vezax_shadow_resistance_trigger;
creators["sara shadow resistance trigger"] = &RaidUlduarTriggerContext::sara_shadow_resistance_trigger;
creators["yogg-saron shadow resistance triggerr"] = &RaidUlduarTriggerContext::yogg_saron_shadow_resistance_trigger;
creators["yogg-saron ominous cloud cheat trigger"] = &RaidUlduarTriggerContext::yogg_saron_ominous_cloud_cheat_trigger;
creators["yogg-saron guardian positioning trigger"] = &RaidUlduarTriggerContext::yogg_saron_guardian_positioning_trigger;
creators["yogg-saron sanity trigger"] = &RaidUlduarTriggerContext::yogg_saron_sanity_trigger;
creators["yogg-saron death orb trigger"] = &RaidUlduarTriggerContext::yogg_saron_death_orb_trigger;
creators["yogg-saron malady of the mind trigger"] = &RaidUlduarTriggerContext::yogg_saron_malady_of_the_mind_trigger;
creators["yogg-saron mark target trigger"] = &RaidUlduarTriggerContext::yogg_saron_mark_target_trigger;
creators["yogg-saron brain link trigger"] = &RaidUlduarTriggerContext::yogg_saron_brain_link_trigger;
creators["yogg-saron move to enter portal trigger"] = &RaidUlduarTriggerContext::yogg_saron_move_to_enter_portal_trigger;
creators["yogg-saron use portal trigger"] = &RaidUlduarTriggerContext::yogg_saron_use_portal_trigger;
creators["yogg-saron fall from floor trigger"] = &RaidUlduarTriggerContext::yogg_saron_fall_from_floor_trigger;
creators["yogg-saron boss room movement cheat trigger"] = &RaidUlduarTriggerContext::yogg_saron_boss_room_movement_cheat_trigger;
creators["yogg-saron illusion room trigger"] = &RaidUlduarTriggerContext::yogg_saron_illusion_room_trigger;
creators["yogg-saron move to exit portal trigger"] = &RaidUlduarTriggerContext::yogg_saron_move_to_exit_portal_trigger;
creators["yogg-saron lunatic gaze trigger"] = &RaidUlduarTriggerContext::yogg_saron_lunatic_gaze_trigger;
creators["yogg-saron phase 3 positioning trigger"] = &RaidUlduarTriggerContext::yogg_saron_phase_3_positioning_trigger;
}
private:
@@ -120,7 +138,25 @@ private:
static Trigger* mimiron_cheat_trigger(PlayerbotAI* ai) { return new MimironCheatTrigger(ai); }
static Trigger* vezax_cheat_trigger(PlayerbotAI* ai) { return new VezaxCheatTrigger(ai); }
static Trigger* vezax_shadow_crash_trigger(PlayerbotAI* ai) { return new VezaxShadowCrashTrigger(ai); }
static Trigger* vezax_shadow_resistance_trigger(PlayerbotAI* ai) { return new BossShadowResistanceTrigger(ai, "general vezax"); }
static Trigger* sara_shadow_resistance_trigger(PlayerbotAI* ai) { return new BossShadowResistanceTrigger(ai, "sara"); }
static Trigger* yogg_saron_shadow_resistance_trigger(PlayerbotAI* ai) { return new BossShadowResistanceTrigger(ai, "yogg-saron"); }
static Trigger* vezax_mark_of_the_faceless_trigger(PlayerbotAI* ai) { return new VezaxMarkOfTheFacelessTrigger(ai); }
static Trigger* yogg_saron_ominous_cloud_cheat_trigger(PlayerbotAI* ai) { return new YoggSaronOminousCloudCheatTrigger(ai); }
static Trigger* yogg_saron_guardian_positioning_trigger(PlayerbotAI* ai) { return new YoggSaronGuardianPositioningTrigger(ai); }
static Trigger* yogg_saron_sanity_trigger(PlayerbotAI* ai) { return new YoggSaronSanityTrigger(ai); }
static Trigger* yogg_saron_death_orb_trigger(PlayerbotAI* ai) { return new YoggSaronDeathOrbTrigger(ai); }
static Trigger* yogg_saron_malady_of_the_mind_trigger(PlayerbotAI* ai) { return new YoggSaronMaladyOfTheMindTrigger(ai); }
static Trigger* yogg_saron_mark_target_trigger(PlayerbotAI* ai) { return new YoggSaronMarkTargetTrigger(ai); }
static Trigger* yogg_saron_brain_link_trigger(PlayerbotAI* ai) { return new YoggSaronBrainLinkTrigger(ai); }
static Trigger* yogg_saron_move_to_enter_portal_trigger(PlayerbotAI* ai) { return new YoggSaronMoveToEnterPortalTrigger(ai); }
static Trigger* yogg_saron_use_portal_trigger(PlayerbotAI* ai) { return new YoggSaronUsePortalTrigger(ai); }
static Trigger* yogg_saron_fall_from_floor_trigger(PlayerbotAI* ai) { return new YoggSaronFallFromFloorTrigger(ai); }
static Trigger* yogg_saron_boss_room_movement_cheat_trigger(PlayerbotAI* ai) { return new YoggSaronBossRoomMovementCheatTrigger(ai); }
static Trigger* yogg_saron_illusion_room_trigger(PlayerbotAI* ai) { return new YoggSaronIllusionRoomTrigger(ai); }
static Trigger* yogg_saron_move_to_exit_portal_trigger(PlayerbotAI* ai) { return new YoggSaronMoveToExitPortalTrigger(ai); }
static Trigger* yogg_saron_lunatic_gaze_trigger(PlayerbotAI* ai) { return new YoggSaronLunaticGazeTrigger(ai); }
static Trigger* yogg_saron_phase_3_positioning_trigger(PlayerbotAI* ai) { return new YoggSaronPhase3PositioningTrigger(ai); }
};
#endif

View File

@@ -12,11 +12,33 @@
#include "Trigger.h"
#include "Vehicle.h"
#include <MovementActions.h>
#include <FollowMasterStrategy.h>
#include <RtiTargetValue.h>
const std::vector<uint32> availableVehicles = {NPC_VEHICLE_CHOPPER, NPC_SALVAGED_DEMOLISHER,
NPC_SALVAGED_DEMOLISHER_TURRET, NPC_SALVAGED_SIEGE_ENGINE,
NPC_SALVAGED_SIEGE_ENGINE_TURRET};
const std::vector<uint32> illusionMobs =
{
NPC_INFLUENCE_TENTACLE,
NPC_RUBY_CONSORT,
NPC_AZURE_CONSORT,
NPC_BRONZE_CONSORT,
NPC_EMERALD_CONSORT,
NPC_OBSIDIAN_CONSORT,
NPC_ALEXTRASZA,
NPC_MALYGOS_ILLUSION,
NPC_NELTHARION,
NPC_YSERA,
NPC_DEATHSWORN_ZEALOT,
NPC_LICH_KING_ILLUSION,
NPC_IMMOLATED_CHAMPION,
NPC_SUIT_OF_ARMOR,
NPC_GARONA,
NPC_KING_LLANE
};
bool FlameLeviathanOnVehicleTrigger::IsActive()
{
Unit* vehicleBase = bot->GetVehicleBase();
@@ -448,8 +470,8 @@ bool KologarnAttackDpsTargetTrigger::IsActive()
if (!group)
return false;
ObjectGuid skullTarget = group->GetTargetIcon(skullIndex);
ObjectGuid crossTarget = group->GetTargetIcon(crossIndex);
ObjectGuid skullTarget = group->GetTargetIcon(RtiTargetValue::skullIndex);
ObjectGuid crossTarget = group->GetTargetIcon(RtiTargetValue::crossIndex);
if (crossTarget && (botAI->IsMainTank(bot) || botAI->IsAssistTankOfIndex(bot, 0)))
{
@@ -745,7 +767,7 @@ bool ThorimUnbalancingStrikeTrigger::IsActive()
Unit* boss = AI_VALUE2(Unit*, "find target", "thorim");
// Check boss and it is alive
if (!boss || !boss->IsAlive())
if (!boss || !boss->IsAlive() || !boss->IsHostileTo(bot))
return false;
return bot->HasAura(SPELL_UNBALANCING_STRIKE);
@@ -762,7 +784,7 @@ bool ThorimMarkDpsTargetTrigger::IsActive()
if (botAI->IsMainTank(bot))
{
ObjectGuid currentSkullTarget = group->GetTargetIcon(skullIndex);
ObjectGuid currentSkullTarget = group->GetTargetIcon(RtiTargetValue::skullIndex);
Unit* currentSkullUnit = botAI->GetUnit(currentSkullTarget);
if (currentSkullUnit && !currentSkullUnit->IsAlive())
{
@@ -783,13 +805,13 @@ bool ThorimMarkDpsTargetTrigger::IsActive()
Unit* boss = AI_VALUE2(Unit*, "find target", "thorim");
// Check boss and it is alive
if (!boss || !boss->IsAlive())
if (!boss || !boss->IsAlive() || !boss->IsHostileTo(bot))
return false;
if (boss->GetPositionZ() < ULDUAR_THORIM_AXIS_Z_FLOOR_THRESHOLD && (!currentSkullUnit || !currentSkullUnit->IsAlive()))
{
group->SetTargetIcon(skullIndex, bot->GetGUID(), boss->GetGUID());
group->SetTargetIcon(RtiTargetValue::skullIndex, bot->GetGUID(), boss->GetGUID());
return true;
}
@@ -811,7 +833,7 @@ bool ThorimMarkDpsTargetTrigger::IsActive()
if (mainTank && bot->GetDistance(mainTank) < 30.0f)
return false;
ObjectGuid currentCrossTarget = group->GetTargetIcon(crossIndex);
ObjectGuid currentCrossTarget = group->GetTargetIcon(RtiTargetValue::crossIndex);
Unit* currentCrossUnit = botAI->GetUnit(currentCrossTarget);
if (currentCrossUnit && !currentCrossUnit->IsAlive())
{
@@ -963,7 +985,7 @@ bool ThorimArenaPositioningTrigger::IsActive()
Unit* boss = AI_VALUE2(Unit*, "find target", "thorim");
// Check boss and it is alive
if (!boss || !boss->IsAlive())
if (!boss || !boss->IsAlive() || !boss->IsHostileTo(bot))
return false;
if (boss->GetPositionZ() < ULDUAR_THORIM_AXIS_Z_FLOOR_THRESHOLD)
@@ -1061,7 +1083,7 @@ bool ThorimPhase2PositioningTrigger::IsActive()
Unit* boss = AI_VALUE2(Unit*, "find target", "thorim");
// Check boss and it is alive
if (!boss || !boss->IsAlive())
if (!boss || !boss->IsAlive() || !boss->IsHostileTo(bot))
return false;
if (boss->GetPositionZ() > ULDUAR_THORIM_AXIS_Z_FLOOR_THRESHOLD)
@@ -1408,8 +1430,8 @@ bool MimironAerialCommandUnitTrigger::IsActive()
return false;
}
ObjectGuid skullTarget = group->GetTargetIcon(skullIndex);
ObjectGuid crossTarget = group->GetTargetIcon(crossIndex);
ObjectGuid skullTarget = group->GetTargetIcon(RtiTargetValue::skullIndex);
ObjectGuid crossTarget = group->GetTargetIcon(RtiTargetValue::crossIndex);
//if (bombBot && bombBot->GetGUID() != crossTarget)
//{
@@ -1515,7 +1537,7 @@ bool MimironPhase4MarkDpsTrigger::IsActive()
highestHealthUnit = aerialCommandUnit;
}
ObjectGuid skullTarget = group->GetTargetIcon(skullIndex);
ObjectGuid skullTarget = group->GetTargetIcon(RtiTargetValue::skullIndex);
if (!skullTarget)
{
return true;
@@ -1619,3 +1641,748 @@ bool VezaxMarkOfTheFacelessTrigger::IsActive()
return distance > 2.0f;
}
Unit* YoggSaronTrigger::GetSaraIfAlive()
{
Unit* sara = AI_VALUE2(Unit*, "find target", "sara");
if (!sara || !sara->IsAlive())
{
return nullptr;
}
return sara;
}
bool YoggSaronTrigger::IsPhase2()
{
Creature* target = bot->FindNearestCreature(NPC_YOGG_SARON, 200.0f, true);
return target && target->IsAlive() && target->HasAura(SPELL_SHADOW_BARRIER);
}
bool YoggSaronTrigger::IsPhase3()
{
Creature* target = bot->FindNearestCreature(NPC_YOGG_SARON, 200.0f, true);
Creature* guardian = bot->FindNearestCreature(NPC_GUARDIAN_OF_YS, 200.0f, true);
return target && target->IsAlive() && !target->HasAura(SPELL_SHADOW_BARRIER) && !guardian;
}
bool YoggSaronTrigger::IsInBrainLevel()
{
return bot->GetPositionZ() > 230.0f && bot->GetPositionZ() < 250.0f;
}
bool YoggSaronTrigger::IsYoggSaronFight()
{
Unit* sara = AI_VALUE2(Unit*, "find target", "sara");
Unit* yoggsaron = AI_VALUE2(Unit*, "find target", "yogg-saron");
if ((sara && sara->IsAlive()) || (yoggsaron && yoggsaron->IsAlive()))
{
return true;
}
}
bool YoggSaronTrigger::IsInIllusionRoom()
{
if (!IsInBrainLevel())
{
return false;
}
if (IsInStormwindKeeperIllusion())
{
return true;
}
if (IsInIcecrownKeeperIllusion())
{
return true;
}
if (IsInChamberOfTheAspectsIllusion())
{
return true;
}
return false;
}
bool YoggSaronTrigger::IsInStormwindKeeperIllusion()
{
return bot->GetDistance2d(ULDUAR_YOGG_SARON_STORMWIND_KEEPER_MIDDLE.GetPositionX(),
ULDUAR_YOGG_SARON_STORMWIND_KEEPER_MIDDLE.GetPositionY()) <
ULDUAR_YOGG_SARON_STORMWIND_KEEPER_RADIUS;
}
bool YoggSaronTrigger::IsInIcecrownKeeperIllusion()
{
return bot->GetDistance2d(ULDUAR_YOGG_SARON_ICECROWN_CITADEL_MIDDLE.GetPositionX(),
ULDUAR_YOGG_SARON_ICECROWN_CITADEL_MIDDLE.GetPositionY()) <
ULDUAR_YOGG_SARON_ICECROWN_CITADEL_RADIUS;
}
bool YoggSaronTrigger::IsInChamberOfTheAspectsIllusion()
{
return bot->GetDistance2d(ULDUAR_YOGG_SARON_CHAMBER_OF_ASPECTS_MIDDLE.GetPositionX(),
ULDUAR_YOGG_SARON_CHAMBER_OF_ASPECTS_MIDDLE.GetPositionY()) <
ULDUAR_YOGG_SARON_CHAMBER_OF_ASPECTS_RADIUS;
}
bool YoggSaronTrigger::IsMasterIsInIllusionGroup()
{
Player* master = botAI->GetMaster();
return master && !botAI->IsTank(master);
}
bool YoggSaronTrigger::IsMasterIsInBrainRoom()
{
Player* master = botAI->GetMaster();
if (!master)
{
return false;
}
return master->GetDistance2d(ULDUAR_YOGG_SARON_BRAIN_ROOM_MIDDLE.GetPositionX(),
ULDUAR_YOGG_SARON_BRAIN_ROOM_MIDDLE.GetPositionY()) <
ULDUAR_YOGG_SARON_BRAIN_ROOM_RADIUS &&
master->GetPositionZ() > 230.0f && master->GetPositionZ() < 250.0f;
}
Position YoggSaronTrigger::GetIllusionRoomEntrancePosition()
{
if (IsInChamberOfTheAspectsIllusion())
{
return ULDUAR_YOGG_SARON_CHAMBER_OF_ASPECTS_ENTRANCE;
}
else if (IsInIcecrownKeeperIllusion())
{
return ULDUAR_YOGG_SARON_ICECROWN_CITADEL_ENTRANCE;
}
else if (IsInStormwindKeeperIllusion())
{
return ULDUAR_YOGG_SARON_STORMWIND_KEEPER_ENTRANCE;
}
else
{
return Position();
}
}
Unit* YoggSaronTrigger::GetIllusionRoomRtiTarget()
{
Group* group = bot->GetGroup();
if (!group)
{
return nullptr;
}
uint8 rtiIndex = RtiTargetValue::GetRtiIndex(AI_VALUE(std::string, "rti"));
if (rtiIndex == -1)
{
return nullptr; // Invalid RTI mark
}
ObjectGuid currentRtiTarget = group->GetTargetIcon(rtiIndex);
Unit* currentRtiTargetUnit = botAI->GetUnit(currentRtiTarget);
if (!currentRtiTargetUnit || !currentRtiTargetUnit->IsAlive())
{
currentRtiTargetUnit = nullptr;
}
return currentRtiTargetUnit;
}
Unit* YoggSaronTrigger::GetNextIllusionRoomRtiTarget()
{
float detectionRadius = 0.0f;
if (IsInStormwindKeeperIllusion())
{
detectionRadius = ULDUAR_YOGG_SARON_STORMWIND_KEEPER_RADIUS;
}
else if (IsInIcecrownKeeperIllusion())
{
detectionRadius = ULDUAR_YOGG_SARON_ICECROWN_CITADEL_RADIUS;
}
else if (IsInChamberOfTheAspectsIllusion())
{
detectionRadius = ULDUAR_YOGG_SARON_CHAMBER_OF_ASPECTS_RADIUS;
}
else
{
return nullptr;
}
GuidVector targets = AI_VALUE(GuidVector, "nearest npcs");
if (botAI->HasCheat(BotCheatMask::raid))
{
for (const ObjectGuid& guid : targets)
{
Unit* unit = botAI->GetUnit(guid);
if (unit && unit->IsAlive() && unit->GetEntry() == NPC_LAUGHING_SKULL)
{
return unit;
}
}
}
float nearestDistance = std::numeric_limits<float>::max();
Unit* nextIllusionRoomRtiTarget = nullptr;
for (const uint32& creatureId : illusionMobs)
{
for (const ObjectGuid& guid : targets)
{
Unit* unit = botAI->GetUnit(guid);
if (unit && unit->IsAlive() && unit->GetEntry() == creatureId)
{
float distance = bot->GetDistance(unit);
if (distance < nearestDistance)
{
nextIllusionRoomRtiTarget = unit;
nearestDistance = distance;
}
}
}
}
if (nextIllusionRoomRtiTarget)
{
return nextIllusionRoomRtiTarget;
}
if (IsInStormwindKeeperIllusion())
{
Creature* target = bot->FindNearestCreature(NPC_SUIT_OF_ARMOR, detectionRadius, true);
if (target)
{
return target;
}
}
return nullptr;
}
bool YoggSaronOminousCloudCheatTrigger::IsActive()
{
if (!botAI->HasCheat(BotCheatMask::raid))
{
return false;
}
Unit* boss = GetSaraIfAlive();
if (!boss)
{
return false;
}
if (!botAI->IsBotMainTank(bot))
{
return false;
}
if (bot->GetDistance2d(boss->GetPositionX(), boss->GetPositionY()) > 50.0f)
{
return false;
}
Creature* target = boss->FindNearestCreature(NPC_OMINOUS_CLOUD, 25.0f, true);
return target;
}
bool YoggSaronGuardianPositioningTrigger::IsActive()
{
if (!GetSaraIfAlive())
{
return false;
}
if (!botAI->IsTank(bot))
{
return false;
}
GuidVector targets = AI_VALUE(GuidVector, "nearest npcs");
bool thereIsAnyGuardian = false;
for (const ObjectGuid& guid : targets)
{
Unit* unit = botAI->GetUnit(guid);
if (!unit || !unit->IsAlive())
{
continue;
}
if (unit->GetEntry() == NPC_GUARDIAN_OF_YS)
{
thereIsAnyGuardian = true;
ObjectGuid unitTargetGuid = unit->GetTarget();
Player* targetedPlayer = botAI->GetPlayer(unitTargetGuid);
if (!targetedPlayer || !botAI->IsTank(targetedPlayer))
{
return false;
}
}
}
return thereIsAnyGuardian &&
bot->GetDistance2d(ULDUAR_YOGG_SARON_MIDDLE.GetPositionX(), ULDUAR_YOGG_SARON_MIDDLE.GetPositionY()) > 1.0f;
}
bool YoggSaronSanityTrigger::IsActive()
{
Aura* sanityAura = bot->GetAura(SPELL_SANITY);
if (!sanityAura)
{
return false;
}
int sanityAuraStacks = sanityAura->GetStackAmount();
Creature* sanityWell = bot->FindNearestCreature(NPC_SANITY_WELL, 200.0f);
if (!sanityWell)
{
return false;
}
float distanceToSanityWell = bot->GetDistance(sanityWell);
return (distanceToSanityWell >= 1.0f && sanityAuraStacks < 40) ||
(distanceToSanityWell < 1.0f && sanityAuraStacks < 100);
}
bool YoggSaronDeathOrbTrigger::IsActive()
{
TooCloseToCreatureTrigger tooCloseToDeathOrbTrigger(botAI);
return IsPhase2() && tooCloseToDeathOrbTrigger.TooCloseToCreature(NPC_DEATH_ORB, 10.0f);
}
bool YoggSaronMaladyOfTheMindTrigger::IsActive()
{
TooCloseToPlayerWithDebuffTrigger tooCloseToPlayerWithDebuffTrigger(botAI);
return IsPhase2() && tooCloseToPlayerWithDebuffTrigger.TooCloseToPlayerWithDebuff(SPELL_MALADY_OF_THE_MIND, 15.0f) && botAI->CanMove();
}
bool YoggSaronMarkTargetTrigger::IsActive()
{
if (!IsYoggSaronFight())
{
return false;
}
if (!botAI->IsBotMainTank(bot))
{
return false;
}
Group* group = bot->GetGroup();
if (!group)
{
return false;
}
if (IsPhase2())
{
ObjectGuid currentMoonTarget = group->GetTargetIcon(RtiTargetValue::moonIndex);
Creature* yogg_saron = bot->FindNearestCreature(NPC_YOGG_SARON, 200.0f, true);
if (!currentMoonTarget || currentMoonTarget != yogg_saron->GetGUID())
{
return true;
}
ObjectGuid currentSkullTarget = group->GetTargetIcon(RtiTargetValue::skullIndex);
Creature* nextPossibleTarget = bot->FindNearestCreature(NPC_CONSTRICTOR_TENTACLE, 200.0f, true);
if (!nextPossibleTarget)
{
nextPossibleTarget = bot->FindNearestCreature(NPC_CORRUPTOR_TENTACLE, 200.0f, true);
if (!nextPossibleTarget)
{
return false;
}
}
if (currentSkullTarget)
{
Unit* currentSkullUnit = botAI->GetUnit(currentSkullTarget);
if (!currentSkullUnit)
{
return true;
}
if (currentSkullUnit->IsAlive() && currentSkullUnit->GetGUID() == nextPossibleTarget->GetGUID())
{
return false;
}
}
return true;
}
else if (IsPhase3())
{
ObjectGuid currentSkullTarget = group->GetTargetIcon(RtiTargetValue::skullIndex);
Unit* currentSkullUnit = nullptr;
if (currentSkullTarget)
{
currentSkullUnit = botAI->GetUnit(currentSkullTarget);
}
if (currentSkullUnit &&
(currentSkullUnit->GetEntry() == NPC_IMMORTAL_GUARDIAN ||
currentSkullUnit->GetEntry() == NPC_MARKED_IMMORTAL_GUARDIAN) &&
currentSkullUnit->GetHealthPct() > 10)
{
return false;
}
GuidVector targets = AI_VALUE(GuidVector, "nearest npcs");
for (const ObjectGuid& guid : targets)
{
Unit* unit = botAI->GetUnit(guid);
if (!unit || !unit->IsAlive())
{
continue;
}
if ((unit->GetEntry() == NPC_IMMORTAL_GUARDIAN || unit->GetEntry() == NPC_MARKED_IMMORTAL_GUARDIAN) &&
unit->GetHealthPct() > 10)
{
return true;
}
}
if (!currentSkullUnit || currentSkullUnit->GetEntry() != NPC_YOGG_SARON)
{
return true;
}
return false;
}
return false;
}
bool YoggSaronBrainLinkTrigger::IsActive()
{
TooFarFromPlayerWithAuraTrigger tooFarFromPlayerWithAuraTrigger(botAI);
return IsPhase2() && bot->HasAura(SPELL_BRAIN_LINK) &&
tooFarFromPlayerWithAuraTrigger.TooFarFromPlayerWithAura(SPELL_BRAIN_LINK, 20.0f, false);
}
bool YoggSaronMoveToEnterPortalTrigger::IsActive()
{
if (!IsPhase2())
{
return false;
}
Creature* portal = bot->FindNearestCreature(NPC_DESCEND_INTO_MADNESS, 100.0f, true);
if (!portal)
{
return false;
}
if (bot->GetDistance2d(portal->GetPositionX(), portal->GetPositionY()) < 2.0f)
{
return false;
}
if (AI_VALUE(std::string, "rti") != "skull")
{
return false;
}
Group* group = bot->GetGroup();
if (!group)
{
return false;
}
int brainRoomTeamCount = 10;
if (bot->GetRaidDifficulty() == Difficulty::RAID_DIFFICULTY_10MAN_NORMAL)
{
brainRoomTeamCount = 4;
}
if (IsMasterIsInIllusionGroup())
{
brainRoomTeamCount--;
}
for (GroupReference* gref = group->GetFirstMember(); gref; gref = gref->next())
{
Player* member = gref->GetSource();
if (!member || !member->IsAlive() || botAI->IsTank(member))
{
continue;
}
if (member->GetGUID() == bot->GetGUID())
{
return true;
}
brainRoomTeamCount--;
if (brainRoomTeamCount == 0)
{
break;
}
}
return false;
}
bool YoggSaronFallFromFloorTrigger::IsActive()
{
if (!IsYoggSaronFight())
{
return false;
}
std::string rtiMark = AI_VALUE(std::string, "rti");
if (rtiMark == "skull" && bot->GetPositionZ() < ULDUAR_YOGG_SARON_BOSS_ROOM_AXIS_Z_PATHING_ISSUE_DETECT)
{
return true;
}
if ((rtiMark == "cross" || rtiMark == "circle" || rtiMark == "star") && bot->GetPositionZ() < ULDUAR_YOGG_SARON_BRAIN_ROOM_AXIS_Z_PATHING_ISSUE_DETECT)
{
return true;
}
return false;
}
bool YoggSaronBossRoomMovementCheatTrigger::IsActive()
{
if (!IsYoggSaronFight() || !IsPhase2())
{
return false;
}
FollowMasterStrategy followMasterStrategy(botAI);
if (botAI->HasStrategy(followMasterStrategy.getName(), BotState::BOT_STATE_NON_COMBAT))
{
return true;
}
if (!botAI->HasCheat(BotCheatMask::raid))
{
return false;
}
if (AI_VALUE(std::string, "rti") != "skull")
{
return false;
}
Group* group = bot->GetGroup();
if (!group)
{
return false;
}
ObjectGuid currentSkullTarget = group->GetTargetIcon(RtiTargetValue::skullIndex);
if (!currentSkullTarget)
{
return false;
}
Unit* currentSkullUnit = botAI->GetUnit(currentSkullTarget);
if (!currentSkullUnit || !currentSkullUnit->IsAlive() || bot->GetDistance2d(currentSkullUnit->GetPositionX(), currentSkullUnit->GetPositionY()) < 40.0f)
{
return false;
}
return true;
}
bool YoggSaronUsePortalTrigger::IsActive()
{
if (!IsPhase2())
{
return false;
}
if (AI_VALUE(std::string, "rti") != "diamond")
{
return false;
}
return bot->FindNearestCreature(NPC_DESCEND_INTO_MADNESS, 2.0f, true) != nullptr;
}
bool YoggSaronIllusionRoomTrigger::IsActive()
{
if (!IsYoggSaronFight() || !IsInIllusionRoom() || AI_VALUE(std::string, "rti") == "square")
{
return false;
}
if (SetRtiMarkRequired())
{
return true;
}
if (SetRtiTargetRequired())
{
return true;
}
if (GoToBrainRoomRequired())
{
return true;
}
return false;
}
bool YoggSaronIllusionRoomTrigger::GoToBrainRoomRequired()
{
if (AI_VALUE(std::string, "rti") == "square")
{
return false;
}
return IsMasterIsInBrainRoom();
}
bool YoggSaronIllusionRoomTrigger::SetRtiMarkRequired()
{
return AI_VALUE(std::string, "rti") == "diamond";
}
bool YoggSaronIllusionRoomTrigger::SetRtiTargetRequired()
{
Unit const* currentRtiTarget = GetIllusionRoomRtiTarget();
if (currentRtiTarget != nullptr)
{
return false;
}
return GetNextIllusionRoomRtiTarget() != nullptr;
}
bool YoggSaronMoveToExitPortalTrigger::IsActive()
{
if (!IsYoggSaronFight() || !IsInBrainLevel())
{
return false;
}
Creature const* brain = bot->FindNearestCreature(NPC_BRAIN, 60.0f, true);
if (!brain || !brain->IsAlive())
{
return false;
}
if (brain->HasUnitState(UNIT_STATE_CASTING))
{
Spell* induceMadnessSpell = brain->GetCurrentSpell(CURRENT_GENERIC_SPELL);
if (induceMadnessSpell && induceMadnessSpell->m_spellInfo->Id == SPELL_INDUCE_MADNESS)
{
uint32 castingTimeLeft = induceMadnessSpell->GetCastTimeRemaining();
if ((botAI->HasCheat(BotCheatMask::raid) && castingTimeLeft < 6000) ||
(!botAI->HasCheat(BotCheatMask::raid) && castingTimeLeft < 8000))
{
return true;
}
}
}
else if (brain->GetHealth() < brain->GetMaxHealth() * 0.3f)
{
return true;
}
return false;
}
bool YoggSaronLunaticGazeTrigger::IsActive()
{
Unit* yoggsaron = AI_VALUE2(Unit*, "find target", "yogg-saron");
if (yoggsaron && yoggsaron->IsAlive() && yoggsaron->HasUnitState(UNIT_STATE_CASTING))
{
Spell* currentSpell = yoggsaron->GetCurrentSpell(CURRENT_CHANNELED_SPELL);
if (currentSpell && currentSpell->m_spellInfo->Id == SPELL_LUNATIC_GAZE_YS)
{
return true;
}
}
return false;
}
bool YoggSaronPhase3PositioningTrigger::IsActive()
{
if (!IsYoggSaronFight() || !IsPhase3())
{
return false;
}
YoggSaronSanityTrigger sanityTrigger(botAI);
if (sanityTrigger.IsActive())
{
return false;
}
if (botAI->IsRanged(bot) && bot->GetDistance2d(ULDUAR_YOGG_SARON_PHASE_3_RANGED_SPOT.GetPositionX(),
ULDUAR_YOGG_SARON_PHASE_3_RANGED_SPOT.GetPositionY()) > 15.0f)
{
return true;
}
if (botAI->IsMelee(bot) && !botAI->IsTank(bot) &&
bot->GetDistance2d(ULDUAR_YOGG_SARON_PHASE_3_MELEE_SPOT.GetPositionX(),
ULDUAR_YOGG_SARON_PHASE_3_MELEE_SPOT.GetPositionY()) > 15.0f)
{
return true;
}
if (botAI->IsTank(bot))
{
if (bot->GetDistance(ULDUAR_YOGG_SARON_PHASE_3_MELEE_SPOT) > 30.0f)
{
return true;
}
GuidVector targets = AI_VALUE(GuidVector, "nearest npcs");
bool thereIsAnyGuardian = false;
for (const ObjectGuid& guid : targets)
{
Unit* unit = botAI->GetUnit(guid);
if (!unit || !unit->IsAlive())
{
continue;
}
if (unit->GetEntry() == NPC_IMMORTAL_GUARDIAN || unit->GetEntry() == NPC_MARKED_IMMORTAL_GUARDIAN)
{
thereIsAnyGuardian = true;
ObjectGuid unitTargetGuid = unit->GetTarget();
Player* targetedPlayer = botAI->GetPlayer(unitTargetGuid);
if (!targetedPlayer || !botAI->IsTank(targetedPlayer))
{
return false;
}
}
}
if (thereIsAnyGuardian && bot->GetDistance2d(ULDUAR_YOGG_SARON_PHASE_3_MELEE_SPOT.GetPositionX(),
ULDUAR_YOGG_SARON_PHASE_3_MELEE_SPOT.GetPositionY()) > 3.0f)
{
return true;
}
}
return false;
}

View File

@@ -18,7 +18,7 @@ enum UlduarIDs
SPELL_OVERLOAD_25_MAN_2 = 61886,
SPELL_RUNE_OF_POWER = 64320,
//Kologarn
// Kologarn
NPC_RIGHT_ARM = 32934,
NPC_RUBBLE = 33768,
SPELL_CRUNCH_ARMOR = 64002,
@@ -27,13 +27,13 @@ enum UlduarIDs
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,
@@ -44,7 +44,7 @@ enum UlduarIDs
NPC_EONARS_GIFT = 33228,
GOBJECT_NATURE_BOMB = 194902,
//Thorim
// Thorim
NPC_DARK_RUNE_ACOLYTE_I = 32886,
NPC_CAPTURED_MERCENARY_SOLDIER_ALLY = 32885,
NPC_CAPTURED_MERCENARY_SOLDIER_HORDE = 32883,
@@ -62,7 +62,7 @@ enum UlduarIDs
NPC_IRON_HONOR_GUARD = 32875,
SPELL_UNBALANCING_STRIKE = 62130,
//Mimiron
// Mimiron
NPC_LEVIATHAN_MKII = 33432,
NPC_VX001 = 33651,
NPC_AERIAL_COMMAND_UNIT = 33670,
@@ -78,23 +78,73 @@ enum UlduarIDs
SPELL_P3WX2_LASER_BARRAGE_AURA_1 = 63274,
SPELL_P3WX2_LASER_BARRAGE_AURA_2 = 63300,
//General Vezax
// 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 int8 skullIndex = 7; // Skull
const int8 crossIndex = 6; // Cross
const int8 moonIndex = 4; // Moon
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);
@@ -123,6 +173,16 @@ const Position ULDUAR_MIMIRON_PHASE2_SIDE3RANGE_SPOT = Position(2754.1294f, 2553
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
@@ -448,4 +508,139 @@ public:
bool IsActive() override;
};
//
// Yogg-Saron
//
class YoggSaronTrigger : public Trigger
{
public:
YoggSaronTrigger(PlayerbotAI* ai, std::string const name = "yogg saron trigger", int32 checkInteval = 1)
: Trigger(ai, name, checkInteval) {}
bool IsYoggSaronFight();
bool IsPhase2();
bool IsPhase3();
bool IsInBrainLevel();
bool IsInIllusionRoom();
bool IsInStormwindKeeperIllusion();
bool IsInIcecrownKeeperIllusion();
bool IsInChamberOfTheAspectsIllusion();
bool IsMasterIsInIllusionGroup();
bool IsMasterIsInBrainRoom();
Position GetIllusionRoomEntrancePosition();
Unit* GetIllusionRoomRtiTarget();
Unit* GetNextIllusionRoomRtiTarget();
Unit* GetSaraIfAlive();
};
class YoggSaronOminousCloudCheatTrigger : public YoggSaronTrigger
{
public:
YoggSaronOminousCloudCheatTrigger(PlayerbotAI* ai) : YoggSaronTrigger(ai, "yogg-saron ominous cloud cheat trigger") {}
bool IsActive() override;
};
class YoggSaronGuardianPositioningTrigger : public YoggSaronTrigger
{
public:
YoggSaronGuardianPositioningTrigger(PlayerbotAI* ai) : YoggSaronTrigger(ai, "yogg-saron guardian positioning trigger") {}
bool IsActive() override;
};
class YoggSaronSanityTrigger : public YoggSaronTrigger
{
public:
YoggSaronSanityTrigger(PlayerbotAI* ai) : YoggSaronTrigger(ai, "yogg-saron sanity trigger") {}
bool IsActive() override;
};
class YoggSaronDeathOrbTrigger : public YoggSaronTrigger
{
public:
YoggSaronDeathOrbTrigger(PlayerbotAI* ai) : YoggSaronTrigger(ai, "yogg-saron death orb trigger") {}
bool IsActive() override;
};
class YoggSaronMaladyOfTheMindTrigger : public YoggSaronTrigger
{
public:
YoggSaronMaladyOfTheMindTrigger(PlayerbotAI* ai) : YoggSaronTrigger(ai, "yogg-saron malady of the mind trigger") {}
bool IsActive() override;
};
class YoggSaronMarkTargetTrigger : public YoggSaronTrigger
{
public:
YoggSaronMarkTargetTrigger(PlayerbotAI* ai) : YoggSaronTrigger(ai, "yogg-saron mark target trigger") {}
bool IsActive() override;
};
class YoggSaronBrainLinkTrigger : public YoggSaronTrigger
{
public:
YoggSaronBrainLinkTrigger(PlayerbotAI* ai) : YoggSaronTrigger(ai, "yogg-saron brain link trigger") {}
bool IsActive() override;
};
class YoggSaronMoveToEnterPortalTrigger : public YoggSaronTrigger
{
public:
YoggSaronMoveToEnterPortalTrigger(PlayerbotAI* ai) : YoggSaronTrigger(ai, "yogg-saron move to enter portal trigger") {}
bool IsActive() override;
};
class YoggSaronFallFromFloorTrigger : public YoggSaronTrigger
{
public:
YoggSaronFallFromFloorTrigger(PlayerbotAI* ai) : YoggSaronTrigger(ai, "yogg-saron fall from floor trigger") {}
bool IsActive() override;
};
class YoggSaronBossRoomMovementCheatTrigger : public YoggSaronTrigger
{
public:
YoggSaronBossRoomMovementCheatTrigger(PlayerbotAI* ai) : YoggSaronTrigger(ai, "yogg-saron boss room movement cheat trigger") {}
bool IsActive() override;
};
class YoggSaronUsePortalTrigger : public YoggSaronTrigger
{
public:
YoggSaronUsePortalTrigger(PlayerbotAI* ai) : YoggSaronTrigger(ai, "yogg-saron use portal trigger") {}
bool IsActive() override;
};
class YoggSaronIllusionRoomTrigger : public YoggSaronTrigger
{
public:
YoggSaronIllusionRoomTrigger(PlayerbotAI* ai) : YoggSaronTrigger(ai, "yogg-saron illusion room trigger") {}
bool IsActive() override;
private:
bool GoToBrainRoomRequired();
bool SetRtiMarkRequired();
bool SetRtiTargetRequired();
};
class YoggSaronMoveToExitPortalTrigger : public YoggSaronTrigger
{
public:
YoggSaronMoveToExitPortalTrigger(PlayerbotAI* ai) : YoggSaronTrigger(ai, "yogg-saron move to exit portal trigger") {}
bool IsActive() override;
};
class YoggSaronLunaticGazeTrigger : public YoggSaronTrigger
{
public:
YoggSaronLunaticGazeTrigger(PlayerbotAI* ai) : YoggSaronTrigger(ai, "yogg-saron lunatic gaze trigger") {}
bool IsActive() override;
};
class YoggSaronPhase3PositioningTrigger : public YoggSaronTrigger
{
public:
YoggSaronPhase3PositioningTrigger(PlayerbotAI* ai) : YoggSaronTrigger(ai, "yogg-saron phase 3 positioning trigger") {}
bool IsActive() override;
};
#endif

View File

@@ -15,7 +15,7 @@ bool BossFireResistanceTrigger::IsActive()
{
// Check boss and it is alive
Unit* boss = AI_VALUE2(Unit*, "find target", bossName);
if (!boss || !boss->IsAlive())
if (!boss || !boss->IsAlive() || boss->IsFriendlyTo(bot))
return false;
// Check if bot is paladin
@@ -68,7 +68,7 @@ bool BossFrostResistanceTrigger::IsActive()
{
// Check boss and it is alive
Unit* boss = AI_VALUE2(Unit*, "find target", bossName);
if (!boss || !boss->IsAlive())
if (!boss || !boss->IsAlive() || boss->IsFriendlyTo(bot))
return false;
// Check if bot is paladin
@@ -121,7 +121,7 @@ bool BossNatureResistanceTrigger::IsActive()
{
// Check boss and it is alive
Unit* boss = AI_VALUE2(Unit*, "find target", bossName);
if (!boss || !boss->IsAlive())
if (!boss || !boss->IsAlive() || boss->IsFriendlyTo(bot))
return false;
// Check if bot is alive
@@ -176,7 +176,7 @@ bool BossShadowResistanceTrigger::IsActive()
{
// Check boss and it is alive
Unit* boss = AI_VALUE2(Unit*, "find target", bossName);
if (!boss || !boss->IsAlive())
if (!boss || !boss->IsAlive() || boss->IsFriendlyTo(bot))
return false;
// Check if bot is paladin

View File

@@ -214,3 +214,84 @@ bool FarFromMasterTrigger::IsActive()
{
return sServerFacade->IsDistanceGreaterThan(AI_VALUE2(float, "distance", "master target"), distance);
}
bool TooCloseToCreatureTrigger::TooCloseToCreature(uint32 creatureId, float range, bool alive)
{
Creature* nearestCreature = bot->FindNearestCreature(creatureId, range, alive);
return nearestCreature != nullptr;
}
bool TooCloseToPlayerWithDebuffTrigger::TooCloseToPlayerWithDebuff(uint32 spellId, float range)
{
Group* group = bot->GetGroup();
if (!group)
{
return false;
}
std::vector<Player*> debuffedPlayers;
for (GroupReference* gref = group->GetFirstMember(); gref; gref = gref->next())
{
Player* player = gref->GetSource();
if (player && player->IsAlive() && player->HasAura(spellId))
{
debuffedPlayers.push_back(player);
}
}
if (debuffedPlayers.empty())
{
return false;
}
for (Unit* debuffedPlayer : debuffedPlayers)
{
float dist = debuffedPlayer->GetExactDist2d(bot->GetPositionX(), bot->GetPositionY());
if (dist < range)
{
return true;
}
}
return false;
}
bool TooFarFromPlayerWithAuraTrigger::TooFarFromPlayerWithAura(uint32 spellId, float range, bool selfInclude)
{
Group* group = bot->GetGroup();
if (!group)
{
return false;
}
std::vector<Player*> debuffedPlayers;
for (GroupReference* gref = group->GetFirstMember(); gref; gref = gref->next())
{
Player* player = gref->GetSource();
if (player && player->IsAlive() && player->HasAura(spellId) &&
(selfInclude || (!selfInclude && player->GetGUID() != bot->GetGUID())))
{
debuffedPlayers.push_back(player);
}
}
return !debuffedPlayers.empty();
if (debuffedPlayers.empty())
{
return false;
}
for (Unit* debuffedPlayer : debuffedPlayers)
{
float dist = debuffedPlayer->GetExactDist2d(bot->GetPositionX(), bot->GetPositionY());
if (dist > range)
{
return true;
}
}
return false;
}

View File

@@ -121,4 +121,28 @@ public:
OutOfReactRangeTrigger(PlayerbotAI* botAI) : FarFromMasterTrigger(botAI, "out of react range", 50.0f, 5) {}
};
class TooCloseToCreatureTrigger : public Trigger
{
public:
TooCloseToCreatureTrigger(PlayerbotAI* ai) : Trigger(ai, "too close to creature trigger") {}
bool TooCloseToCreature(uint32 creatureId, float range, bool alive = true);
};
class TooCloseToPlayerWithDebuffTrigger : public Trigger
{
public:
TooCloseToPlayerWithDebuffTrigger(PlayerbotAI* ai) : Trigger(ai, "too cloose to player with debuff trigger") {}
bool TooCloseToPlayerWithDebuff(uint32 spellId, float range);
};
class TooFarFromPlayerWithAuraTrigger : public Trigger
{
public:
TooFarFromPlayerWithAuraTrigger(PlayerbotAI* ai) : Trigger(ai, "too far from player with aura trigger") {}
bool TooFarFromPlayerWithAura(uint32 spellId, float range, bool selfInclude = false);
};
#endif

View File

@@ -21,6 +21,14 @@ public:
static int32 GetRtiIndex(std::string const rti);
Unit* Calculate() override;
static const int8 starIndex = 0;
static const int8 circleIndex = 1;
static const int8 diamondIndex = 2;
static const int8 triangleIndex = 3;
static const int8 moonIndex = 4;
static const int8 squareIndex = 5;
static const int8 crossIndex = 6;
static const int8 skullIndex = 7;
private:
std::string const type;

View File

@@ -556,7 +556,7 @@ private:
static UntypedValue* last_flee_timestamp(PlayerbotAI* ai) { return new LastFleeTimestampValue(ai); }
static UntypedValue* recently_flee_info(PlayerbotAI* ai) { return new RecentlyFleeInfo(ai); }
// -------------------------------------------------------
// Flag for cutom glyphs : true when /w bot glyph equip <20>
// Flag for cutom glyphs : true when /w bot glyph equip
// -------------------------------------------------------
static UntypedValue* custom_glyphs(PlayerbotAI* ai)
{