Merge pull request #2175 from mod-playerbots/test-staging

Test staging
This commit is contained in:
Keleborn 2026-03-06 07:55:29 -08:00 committed by GitHub
commit 652c7a1996
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
95 changed files with 7433 additions and 2934 deletions

View File

@ -8,7 +8,7 @@
<div align="center"> <div align="center">
<img src="icon.png" alt="Playerbots Icon" width="700px"> <img src="banner.png" alt="Playerbots Banner" width="700px">
</div> </div>
<div align="center"> <div align="center">

BIN
banner.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 204 KiB

File diff suppressed because it is too large Load Diff

View File

@ -4,15 +4,19 @@
-- zhTW, esES, esMX, ruRU) -- zhTW, esES, esMX, ruRU)
-- ######################################################### -- #########################################################
DELETE FROM ai_playerbot_texts WHERE name IN ('pvp_currency', 'pvp_arena_team', 'pvp_no_arena_team');
DELETE FROM ai_playerbot_texts_chance WHERE name IN ('pvp_currency', 'pvp_arena_team', 'pvp_no_arena_team');
-- --------------------------------------------------------- -- ---------------------------------------------------------
-- pvp_currency -- pvp_currency
-- [PVP] Arena points: %arena_points | Honor Points: %honor_points -- [PVP] Arena points: %arena_points | Honor Points: %honor_points
-- --------------------------------------------------------- -- ---------------------------------------------------------
INSERT INTO `ai_playerbot_texts` INSERT INTO `ai_playerbot_texts`
(`name`, `text`, `say_type`, `reply_type`, (`id`, `name`, `text`, `say_type`, `reply_type`,
`text_loc1`, `text_loc2`, `text_loc3`, `text_loc4`, `text_loc1`, `text_loc2`, `text_loc3`, `text_loc4`,
`text_loc5`, `text_loc6`, `text_loc7`, `text_loc8`) `text_loc5`, `text_loc6`, `text_loc7`, `text_loc8`)
SELECT VALUES (
1737,
'pvp_currency', 'pvp_currency',
'[PVP] Arena points: %arena_points | Honor Points: %honor_points', '[PVP] Arena points: %arena_points | Honor Points: %honor_points',
0, 0, 0, 0,
@ -31,20 +35,20 @@ SELECT
-- esMX -- esMX
'[PVP] Puntos de arena: %arena_points | Puntos de honor: %honor_points', '[PVP] Puntos de arena: %arena_points | Puntos de honor: %honor_points',
-- ruRU -- ruRU
'[PVP] Очки арены: %arena_points | Очки чести: %honor_points' '[PVP] Очки арены: %arena_points | Очки чести: %honor_points');
WHERE NOT EXISTS (
SELECT 1 FROM `ai_playerbot_texts` WHERE `name` = 'pvp_currency' INSERT INTO ai_playerbot_texts_chance (name, probability) VALUES ('pvp_currency', 100);
);
-- --------------------------------------------------------- -- ---------------------------------------------------------
-- pvp_arena_team -- pvp_arena_team
-- [PVP] %bracket: <%team_name> (rating %team_rating) -- [PVP] %bracket: <%team_name> (rating %team_rating)
-- --------------------------------------------------------- -- ---------------------------------------------------------
INSERT INTO `ai_playerbot_texts` INSERT INTO `ai_playerbot_texts`
(`name`, `text`, `say_type`, `reply_type`, (`id`, `name`, `text`, `say_type`, `reply_type`,
`text_loc1`, `text_loc2`, `text_loc3`, `text_loc4`, `text_loc1`, `text_loc2`, `text_loc3`, `text_loc4`,
`text_loc5`, `text_loc6`, `text_loc7`, `text_loc8`) `text_loc5`, `text_loc6`, `text_loc7`, `text_loc8`)
SELECT VALUES (
1738,
'pvp_arena_team', 'pvp_arena_team',
'[PVP] %bracket: <%team_name> (rating %team_rating)', '[PVP] %bracket: <%team_name> (rating %team_rating)',
0, 0, 0, 0,
@ -63,20 +67,20 @@ SELECT
-- esMX -- esMX
'[PVP] %bracket: <%team_name> (índice %team_rating)', '[PVP] %bracket: <%team_name> (índice %team_rating)',
-- ruRU -- ruRU
'[PVP] %bracket: <%team_name> (рейтинг %team_rating)' '[PVP] %bracket: <%team_name> (рейтинг %team_rating)');
WHERE NOT EXISTS (
SELECT 1 FROM `ai_playerbot_texts` WHERE `name` = 'pvp_arena_team' INSERT INTO ai_playerbot_texts_chance (name, probability) VALUES ('pvp_arena_team', 100);
);
-- --------------------------------------------------------- -- ---------------------------------------------------------
-- pvp_no_arena_team -- pvp_no_arena_team
-- [PVP] I have no Arena Team. -- [PVP] I have no Arena Team.
-- --------------------------------------------------------- -- ---------------------------------------------------------
INSERT INTO `ai_playerbot_texts` INSERT INTO `ai_playerbot_texts`
(`name`, `text`, `say_type`, `reply_type`, (`id`, `name`, `text`, `say_type`, `reply_type`,
`text_loc1`, `text_loc2`, `text_loc3`, `text_loc4`, `text_loc1`, `text_loc2`, `text_loc3`, `text_loc4`,
`text_loc5`, `text_loc6`, `text_loc7`, `text_loc8`) `text_loc5`, `text_loc6`, `text_loc7`, `text_loc8`)
SELECT VALUES (
1739,
'pvp_no_arena_team', 'pvp_no_arena_team',
'[PVP] I have no Arena Team.', '[PVP] I have no Arena Team.',
0, 0, 0, 0,
@ -95,7 +99,6 @@ SELECT
-- esMX -- esMX
'[PVP] No tengo equipo de arena.', '[PVP] No tengo equipo de arena.',
-- ruRU -- ruRU
'[PVP] У меня нет команды арены.' '[PVP] У меня нет команды арены.');
WHERE NOT EXISTS (
SELECT 1 FROM `ai_playerbot_texts` WHERE `name` = 'pvp_no_arena_team' INSERT INTO ai_playerbot_texts_chance (name, probability) VALUES ('pvp_no_arena_team', 100);
);

BIN
icon.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 204 KiB

After

Width:  |  Height:  |  Size: 125 KiB

View File

@ -24,8 +24,6 @@ bool BGJoinAction::Execute(Event /*event*/)
BattlegroundQueueTypeId queueTypeId = (BattlegroundQueueTypeId)bgList[urand(0, bgList.size() - 1)]; BattlegroundQueueTypeId queueTypeId = (BattlegroundQueueTypeId)bgList[urand(0, bgList.size() - 1)];
BattlegroundTypeId bgTypeId = BattlegroundMgr::BGTemplateId(queueTypeId); BattlegroundTypeId bgTypeId = BattlegroundMgr::BGTemplateId(queueTypeId);
BattlegroundBracketId bracketId;
bool isArena = false;
bool isRated = false; bool isRated = false;
Battleground* bg = sBattlegroundMgr->GetBattlegroundTemplate(bgTypeId); Battleground* bg = sBattlegroundMgr->GetBattlegroundTemplate(bgTypeId);
@ -37,12 +35,8 @@ bool BGJoinAction::Execute(Event /*event*/)
if (!pvpDiff) if (!pvpDiff)
return false; return false;
bracketId = pvpDiff->GetBracketId();
if (ArenaType type = ArenaType(BattlegroundMgr::BGArenaType(queueTypeId))) if (ArenaType type = ArenaType(BattlegroundMgr::BGArenaType(queueTypeId)))
{ {
isArena = true;
std::vector<uint32>::iterator i = find(ratedList.begin(), ratedList.end(), queueTypeId); std::vector<uint32>::iterator i = find(ratedList.begin(), ratedList.end(), queueTypeId);
if (i != ratedList.end()) if (i != ratedList.end())
isRated = true; isRated = true;
@ -408,8 +402,6 @@ bool BGJoinAction::JoinQueue(uint32 type)
bracketId = pvpDiff->GetBracketId(); bracketId = pvpDiff->GetBracketId();
uint32 BracketSize = bg->GetMaxPlayersPerTeam() * 2;
uint32 TeamSize = bg->GetMaxPlayersPerTeam();
TeamId teamId = bot->GetTeamId(); TeamId teamId = bot->GetTeamId();
// check if already in queue // check if already in queue
@ -486,8 +478,6 @@ bool BGJoinAction::JoinQueue(uint32 type)
if (isArena) if (isArena)
{ {
isArena = true; isArena = true;
BracketSize = type * 2;
TeamSize = type;
isRated = botAI->GetAiObjectContext()->GetValue<uint32>("arena type")->Get(); isRated = botAI->GetAiObjectContext()->GetValue<uint32>("arena type")->Get();
if (joinAsGroup) if (joinAsGroup)

View File

@ -2497,7 +2497,6 @@ bool BGTactics::selectObjective(bool reset)
EYBotStrategy strategyHorde = static_cast<EYBotStrategy>(GetBotStrategyForTeam(bg, TEAM_HORDE)); EYBotStrategy strategyHorde = static_cast<EYBotStrategy>(GetBotStrategyForTeam(bg, TEAM_HORDE));
EYBotStrategy strategyAlliance = static_cast<EYBotStrategy>(GetBotStrategyForTeam(bg, TEAM_ALLIANCE)); EYBotStrategy strategyAlliance = static_cast<EYBotStrategy>(GetBotStrategyForTeam(bg, TEAM_ALLIANCE));
EYBotStrategy strategy = (team == TEAM_ALLIANCE) ? strategyAlliance : strategyHorde; EYBotStrategy strategy = (team == TEAM_ALLIANCE) ? strategyAlliance : strategyHorde;
EYBotStrategy enemyStrategy = (team == TEAM_ALLIANCE) ? strategyHorde : strategyAlliance;
auto IsOwned = [&](uint32 nodeId) -> bool auto IsOwned = [&](uint32 nodeId) -> bool
{ return eyeOfTheStormBG->GetCapturePointInfo(nodeId)._ownerTeamId == team; }; { return eyeOfTheStormBG->GetCapturePointInfo(nodeId)._ownerTeamId == team; };
@ -3231,7 +3230,6 @@ bool BGTactics::selectObjectiveWp(std::vector<BattleBotPath*> const& vPaths)
if (bgType == BATTLEGROUND_RB) if (bgType == BATTLEGROUND_RB)
bgType = bg->GetBgTypeID(true); bgType = bg->GetBgTypeID(true);
PositionMap& posMap = context->GetValue<PositionMap&>("position")->Get();
PositionInfo pos = context->GetValue<PositionMap&>("position")->Get()["bg objective"]; PositionInfo pos = context->GetValue<PositionMap&>("position")->Get()["bg objective"];
if (!pos.isSet()) if (!pos.isSet())
return false; return false;

View File

@ -68,9 +68,7 @@ bool FollowChatShortcutAction::Execute(Event /*event*/)
std::string const target = formation->GetTargetName(); std::string const target = formation->GetTargetName();
bool moved = false; bool moved = false;
if (!target.empty()) if (!target.empty())
{
moved = Follow(AI_VALUE(Unit*, target)); moved = Follow(AI_VALUE(Unit*, target));
}
else else
{ {
WorldLocation loc = formation->GetLocation(); WorldLocation loc = formation->GetLocation();
@ -83,9 +81,7 @@ bool FollowChatShortcutAction::Execute(Event /*event*/)
} }
if (Pet* pet = bot->GetPet()) if (Pet* pet = bot->GetPet())
{
botAI->PetFollow(); botAI->PetFollow();
}
if (moved) if (moved)
{ {

View File

@ -112,7 +112,6 @@ float ChooseRpgTargetAction::getMaxRelevance(GuidPosition guidP)
bool ChooseRpgTargetAction::Execute(Event /*event*/) bool ChooseRpgTargetAction::Execute(Event /*event*/)
{ {
//TravelTarget* travelTarget = AI_VALUE(TravelTarget*, "travel target"); //not used, line marked for removal.
Player* master = botAI->GetMaster(); Player* master = botAI->GetMaster();
GuidPosition masterRpgTarget; GuidPosition masterRpgTarget;
if (master && master != bot && GET_PLAYERBOT_AI(master) && master->GetMapId() == bot->GetMapId() && !master->IsBeingTeleported()) if (master && master != bot && GET_PLAYERBOT_AI(master) && master->GetMapId() == bot->GetMapId() && !master->IsBeingTeleported())
@ -124,7 +123,6 @@ bool ChooseRpgTargetAction::Execute(Event /*event*/)
master = nullptr; master = nullptr;
std::unordered_map<ObjectGuid, uint32> targets; std::unordered_map<ObjectGuid, uint32> targets;
// uint32 num = 0; //not used, line marked for removal.
GuidVector possibleTargets = AI_VALUE(GuidVector, "possible rpg targets"); GuidVector possibleTargets = AI_VALUE(GuidVector, "possible rpg targets");
GuidVector possibleObjects = AI_VALUE(GuidVector, "nearest game objects no los"); GuidVector possibleObjects = AI_VALUE(GuidVector, "nearest game objects no los");
GuidVector possiblePlayers = AI_VALUE(GuidVector, "nearest friendly players"); GuidVector possiblePlayers = AI_VALUE(GuidVector, "nearest friendly players");

View File

@ -232,15 +232,6 @@ void ChooseTravelTargetAction::ReportTravelTarget(TravelTarget* newTarget, Trave
QuestTravelDestination* QuestDestination = (QuestTravelDestination*)destination; QuestTravelDestination* QuestDestination = (QuestTravelDestination*)destination;
Quest const* quest = QuestDestination->GetQuestTemplate(); Quest const* quest = QuestDestination->GetQuestTemplate();
WorldPosition botLocation(bot); WorldPosition botLocation(bot);
CreatureTemplate const* cInfo = nullptr;
GameObjectTemplate const* gInfo = nullptr;
if (destination->getEntry() > 0)
cInfo = sObjectMgr->GetCreatureTemplate(destination->getEntry());
else
gInfo = sObjectMgr->GetGameObjectTemplate(destination->getEntry() * -1);
std::string Sub; std::string Sub;
if (newTarget->isGroupCopy()) if (newTarget->isGroupCopy())
@ -823,10 +814,6 @@ char* strstri(char const* haystack, char const* needle);
TravelDestination* ChooseTravelTargetAction::FindDestination(Player* bot, std::string const name, bool zones, bool npcs, bool quests, bool mobs, bool bosses) TravelDestination* ChooseTravelTargetAction::FindDestination(Player* bot, std::string const name, bool zones, bool npcs, bool quests, bool mobs, bool bosses)
{ {
PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot);
// AiObjectContext* context = botAI->GetAiObjectContext(); //not used, line marked for removal.
std::vector<TravelDestination*> dests; std::vector<TravelDestination*> dests;
//Quests //Quests

View File

@ -520,8 +520,8 @@ bool DebugAction::Execute(Event event)
botPos.setY(botPos.GetPositionY() + (dy - 5) * 5); botPos.setY(botPos.GetPositionY() + (dy - 5) * 5);
botPos.setZ(botPos.getHeight()); botPos.setZ(botPos.getHeight());
Creature* wpCreature = bot->SummonCreature(effect, botPos.GetPositionX(), botPos.GetPositionY(), botPos.GetPositionZ(), 0, bot->SummonCreature(effect, botPos.GetPositionX(), botPos.GetPositionY(), botPos.GetPositionZ(), 0,
TEMPSUMMON_TIMED_DESPAWN, 10000.0f); TEMPSUMMON_TIMED_DESPAWN, 10000.0f);
} }
} }
return true; return true;

View File

@ -85,8 +85,8 @@ void EquipAction::EquipItem(Item* item)
if (itemProto->Class == ITEM_CLASS_CONTAINER) if (itemProto->Class == ITEM_CLASS_CONTAINER)
{ {
// Attempt to equip as a bag // Attempt to equip as a bag
Bag* pBag = reinterpret_cast<Bag*>(item);
uint8 newBagSlot = GetSmallestBagSlot(); uint8 newBagSlot = GetSmallestBagSlot();
if (newBagSlot > 0) if (newBagSlot > 0)
{ {
uint16 src = ((bagIndex << 8) | slot); uint16 src = ((bagIndex << 8) | slot);

View File

@ -262,7 +262,7 @@ bool MoveNearWaterAction::isUseful()
FishingSpotValue* fishingSpotValueObject = (FishingSpotValue*)context->GetValue<WorldPosition>("fishing spot"); FishingSpotValue* fishingSpotValueObject = (FishingSpotValue*)context->GetValue<WorldPosition>("fishing spot");
WorldPosition pos = fishingSpotValueObject->Get(); WorldPosition pos = fishingSpotValueObject->Get();
return !pos.IsValid() || fishingSpotValueObject->IsStale(FISHING_LOCATION_TIMEOUT) || return !pos.IsValid() || fishingSpotValueObject->IsStale(FISHING_LOCATION_TIMEOUT) ||
bot->GetExactDist(&pos) < 0.1f; bot->GetExactDist(&pos) > 0.1f;
} }
@ -292,7 +292,6 @@ bool MoveNearWaterAction::isPossible()
// Water spot is out of range, lets look for a spot to move to for the fishing hole. // Water spot is out of range, lets look for a spot to move to for the fishing hole.
if (distance > MAX_DISTANCE_TO_WATER || distance < MIN_DISTANCE_TO_WATER) if (distance > MAX_DISTANCE_TO_WATER || distance < MIN_DISTANCE_TO_WATER)
{ {
float angle = bot->GetAngle(fishingHole.GetPositionX(), fishingHole.GetPositionY());
WorldPosition landSpot = FindLandRadialFromPosition(botAI, fishingHole, MIN_DISTANCE_TO_WATER, MAX_DISTANCE_TO_WATER, SEARCH_INCREMENT, fishingSearchWindow, 32); WorldPosition landSpot = FindLandRadialFromPosition(botAI, fishingHole, MIN_DISTANCE_TO_WATER, MAX_DISTANCE_TO_WATER, SEARCH_INCREMENT, fishingSearchWindow, 32);
if (landSpot.IsValid()) if (landSpot.IsValid())
{ {
@ -323,7 +322,6 @@ bool MoveNearWaterAction::isPossible()
if (!water.IsValid()) if (!water.IsValid())
return false; return false;
bool hasLOS = bot->IsWithinLOS(water.GetPositionX(), water.GetPositionY(), water.GetPositionZ());
float angle = bot->GetAngle(water.GetPositionX(), water.GetPositionY()); float angle = bot->GetAngle(water.GetPositionX(), water.GetPositionY());
WorldPosition landSpot = WorldPosition landSpot =
FindLandFromPosition(botAI, 0.0f, MAX_DISTANCE_TO_WATER, 1.0f, angle, water, fishingSearchWindow, false); FindLandFromPosition(botAI, 0.0f, MAX_DISTANCE_TO_WATER, 1.0f, angle, water, fishingSearchWindow, false);

View File

@ -28,7 +28,6 @@ bool GiveItemAction::Execute(Event /*event*/)
if (receiverAi->GetAiObjectContext()->GetValue<uint32>("item count", item)->Get()) if (receiverAi->GetAiObjectContext()->GetValue<uint32>("item count", item)->Get())
return true; return true;
bool moved = false;
std::vector<Item*> items = InventoryAction::parseItems(item, ITERATE_ITEMS_IN_BAGS); std::vector<Item*> items = InventoryAction::parseItems(item, ITERATE_ITEMS_IN_BAGS);
for (Item* item : items) for (Item* item : items)
{ {
@ -42,7 +41,6 @@ bool GiveItemAction::Execute(Event /*event*/)
bot->MoveItemFromInventory(item->GetBagSlot(), item->GetSlot(), true); bot->MoveItemFromInventory(item->GetBagSlot(), item->GetSlot(), true);
item->SetOwnerGUID(target->GetGUID()); item->SetOwnerGUID(target->GetGUID());
receiver->MoveItemToInventory(dest, item, true); receiver->MoveItemToInventory(dest, item, true);
moved = true;
std::ostringstream out; std::ostringstream out;
out << "Got " << chat->FormatItem(item->GetTemplate(), item->GetCount()) << " from " << bot->GetName(); out << "Got " << chat->FormatItem(item->GetTemplate(), item->GetCount()) << " from " << bot->GetName();

View File

@ -17,7 +17,6 @@
bool BuyPetitionAction::Execute(Event /*event*/) bool BuyPetitionAction::Execute(Event /*event*/)
{ {
GuidVector vendors = botAI->GetAiObjectContext()->GetValue<GuidVector>("nearest npcs")->Get(); GuidVector vendors = botAI->GetAiObjectContext()->GetValue<GuidVector>("nearest npcs")->Get();
bool vendored = false, result = false;
for (GuidVector::iterator i = vendors.begin(); i != vendors.end(); ++i) for (GuidVector::iterator i = vendors.begin(); i != vendors.end(); ++i)
{ {
ObjectGuid vendorguid = *i; ObjectGuid vendorguid = *i;
@ -97,7 +96,6 @@ bool BuyPetitionAction::canBuyPetition(Player* bot)
bool PetitionOfferAction::Execute(Event event) bool PetitionOfferAction::Execute(Event event)
{ {
uint32 petitionEntry = 5863; // GUILD_CHARTER
std::vector<Item*> petitions = AI_VALUE2(std::vector<Item*>, "inventory items", chat->FormatQItem(5863)); std::vector<Item*> petitions = AI_VALUE2(std::vector<Item*>, "inventory items", chat->FormatQItem(5863));
if (petitions.empty()) if (petitions.empty())
@ -212,7 +210,6 @@ bool PetitionOfferNearbyAction::isUseful()
bool PetitionTurnInAction::Execute(Event /*event*/) bool PetitionTurnInAction::Execute(Event /*event*/)
{ {
GuidVector vendors = botAI->GetAiObjectContext()->GetValue<GuidVector>("nearest npcs")->Get(); GuidVector vendors = botAI->GetAiObjectContext()->GetValue<GuidVector>("nearest npcs")->Get();
bool vendored = false, result = false;
std::vector<Item*> petitions = AI_VALUE2(std::vector<Item*>, "inventory items", chat->FormatQItem(5863)); std::vector<Item*> petitions = AI_VALUE2(std::vector<Item*>, "inventory items", chat->FormatQItem(5863));
if (petitions.empty()) if (petitions.empty())

View File

@ -167,8 +167,6 @@ std::vector<Player*> InviteGuildToGroupAction::getGuildMembers()
bool InviteGuildToGroupAction::Execute(Event /*event*/) bool InviteGuildToGroupAction::Execute(Event /*event*/)
{ {
Guild* guild = sGuildMgr->GetGuildById(bot->GetGuildId());
for (auto& member : getGuildMembers()) for (auto& member : getGuildMembers())
{ {
Player* player = member; Player* player = member;

View File

@ -27,7 +27,6 @@ bool LootRollAction::Execute(Event /*event*/)
continue; continue;
} }
ObjectGuid guid = roll->itemGUID; ObjectGuid guid = roll->itemGUID;
uint32 slot = roll->itemSlot;
uint32 itemId = roll->itemid; uint32 itemId = roll->itemid;
int32 randomProperty = 0; int32 randomProperty = 0;
if (roll->itemRandomPropId) if (roll->itemRandomPropId)
@ -184,7 +183,6 @@ bool MasterLootRollAction::Execute(Event event)
if (!group) if (!group)
return false; return false;
RollVote vote = CalculateRollVote(proto);
group->CountRollVote(bot->GetGUID(), creatureGuid, CalculateRollVote(proto)); group->CountRollVote(bot->GetGUID(), creatureGuid, CalculateRollVote(proto));
return true; return true;

View File

@ -16,7 +16,6 @@ bool LootStrategyAction::Execute(Event event)
{ {
std::string const strategy = event.getParam(); std::string const strategy = event.getParam();
LootObjectStack* lootItems = AI_VALUE(LootObjectStack*, "available loot");
std::set<uint32>& alwaysLootItems = AI_VALUE(std::set<uint32>&, "always loot list"); std::set<uint32>& alwaysLootItems = AI_VALUE(std::set<uint32>&, "always loot list");
Value<LootStrategy*>* lootStrategy = context->GetValue<LootStrategy*>("loot strategy"); Value<LootStrategy*>* lootStrategy = context->GetValue<LootStrategy*>("loot strategy");

View File

@ -134,7 +134,7 @@ public:
private: private:
bool CheckBagSpace(Player* bot) bool CheckBagSpace(Player* bot)
{ {
uint32 totalused = 0, total = 16; uint32 totalused = 0;
for (uint8 slot = INVENTORY_SLOT_ITEM_START; slot < INVENTORY_SLOT_ITEM_END; slot++) for (uint8 slot = INVENTORY_SLOT_ITEM_START; slot < INVENTORY_SLOT_ITEM_END; slot++)
if (bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot)) if (bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot))
++totalused; ++totalused;

View File

@ -93,9 +93,6 @@ bool MovementAction::MoveNear(WorldObject* target, float distance, MovementPrior
distance += target->GetCombatReach(); distance += target->GetCombatReach();
float x = target->GetPositionX();
float y = target->GetPositionY();
float z = target->GetPositionZ();
float followAngle = GetFollowAngle(); float followAngle = GetFollowAngle();
for (float angle = followAngle; angle <= followAngle + static_cast<float>(2 * M_PI); for (float angle = followAngle; angle <= followAngle + static_cast<float>(2 * M_PI);
@ -113,7 +110,6 @@ bool MovementAction::MoveNear(WorldObject* target, float distance, MovementPrior
return true; return true;
} }
// botAI->TellError("All paths not in LOS");
return false; return false;
} }
@ -122,9 +118,6 @@ bool MovementAction::MoveToLOS(WorldObject* target, bool ranged)
if (!target) if (!target)
return false; return false;
// std::ostringstream out; out << "Moving to LOS!";
// bot->Say(out.str(), LANG_UNIVERSAL);
float x = target->GetPositionX(); float x = target->GetPositionX();
float y = target->GetPositionY(); float y = target->GetPositionY();
float z = target->GetPositionZ(); float z = target->GetPositionZ();
@ -257,7 +250,6 @@ bool MovementAction::MoveTo(uint32 mapId, float x, float y, float z, bool idle,
// bot->CastStop(); // bot->CastStop();
// botAI->InterruptSpell(); // botAI->InterruptSpell();
// } // }
G3D::Vector3 endP = path.back();
DoMovePoint(bot, x, y, z, generatePath, backwards); DoMovePoint(bot, x, y, z, generatePath, backwards);
float delay = 1000.0f * MoveDelay(distance, backwards); float delay = 1000.0f * MoveDelay(distance, backwards);
if (lessDelay) if (lessDelay)
@ -772,8 +764,6 @@ bool MovementAction::MoveTo(WorldObject* target, float distance, MovementPriorit
float by = bot->GetPositionY(); float by = bot->GetPositionY();
float bz = bot->GetPositionZ(); float bz = bot->GetPositionZ();
float tx = target->GetPositionX();
float ty = target->GetPositionY();
float tz = target->GetPositionZ(); float tz = target->GetPositionZ();
float distanceToTarget = bot->GetDistance(target); float distanceToTarget = bot->GetDistance(target);
@ -805,10 +795,6 @@ bool MovementAction::ReachCombatTo(Unit* target, float distance)
if (!IsMovingAllowed(target)) if (!IsMovingAllowed(target))
return false; return false;
float bx = bot->GetPositionX();
float by = bot->GetPositionY();
float bz = bot->GetPositionZ();
float tx = target->GetPositionX(); float tx = target->GetPositionX();
float ty = target->GetPositionY(); float ty = target->GetPositionY();
float tz = target->GetPositionZ(); float tz = target->GetPositionZ();
@ -1413,7 +1399,6 @@ bool MovementAction::Flee(Unit* target)
if (botAI->IsTank(player)) if (botAI->IsTank(player))
{ {
float distanceToTank = ServerFacade::instance().GetDistance2d(bot, player); float distanceToTank = ServerFacade::instance().GetDistance2d(bot, player);
float distanceToTarget = ServerFacade::instance().GetDistance2d(bot, target);
if (distanceToTank < fleeDistance) if (distanceToTank < fleeDistance)
{ {
fleeTarget = player; fleeTarget = player;
@ -1434,8 +1419,6 @@ bool MovementAction::Flee(Unit* target)
else // bot is not targeted, try to flee dps/healers else // bot is not targeted, try to flee dps/healers
{ {
bool isHealer = botAI->IsHeal(bot); bool isHealer = botAI->IsHeal(bot);
bool isDps = !isHealer && !botAI->IsTank(bot);
bool isTank = botAI->IsTank(bot);
bool needHealer = !isHealer && AI_VALUE2(uint8, "health", "self target") < 50; bool needHealer = !isHealer && AI_VALUE2(uint8, "health", "self target") < 50;
bool isRanged = botAI->IsRanged(bot); bool isRanged = botAI->IsRanged(bot);
@ -2771,9 +2754,7 @@ bool MoveRandomAction::Execute(Event /*event*/)
float angle = (float)rand_norm() * static_cast<float>(M_PI); float angle = (float)rand_norm() * static_cast<float>(M_PI);
x += urand(0, distance) * cos(angle); x += urand(0, distance) * cos(angle);
y += urand(0, distance) * sin(angle); y += urand(0, distance) * sin(angle);
float ox = x;
float oy = y;
float oz = z;
if (!bot->GetMap()->CheckCollisionAndGetValidCoords(bot, bot->GetPositionX(), bot->GetPositionY(), if (!bot->GetMap()->CheckCollisionAndGetValidCoords(bot, bot->GetPositionX(), bot->GetPositionY(),
bot->GetPositionZ(), x, y, z)) bot->GetPositionZ(), x, y, z))
continue; continue;
@ -2894,10 +2875,8 @@ bool MoveAwayFromCreatureAction::isPossible() { return bot->CanFreeMove(); }
bool MoveAwayFromPlayerWithDebuffAction::Execute(Event /*event*/) bool MoveAwayFromPlayerWithDebuffAction::Execute(Event /*event*/)
{ {
Player* closestPlayer = nullptr; Group* const group = bot->GetGroup();
float minDistance = 0.0f;
Group* group = bot->GetGroup();
if (!group) if (!group)
return false; return false;

View File

@ -332,7 +332,6 @@ public:
private: private:
uint32 spellId; uint32 spellId;
float range; float range;
bool alive;
}; };
#endif #endif

View File

@ -20,7 +20,6 @@ public:
bool Execute(Event event) override; bool Execute(Event event) override;
private: private:
bool warningEnabled = true;
std::string defaultCmd; std::string defaultCmd;
}; };

View File

@ -16,7 +16,6 @@ void QueryQuestAction::TellObjective(std::string const name, uint32 available, u
bool QueryQuestAction::Execute(Event event) bool QueryQuestAction::Execute(Event event)
{ {
Player* requester = event.getOwner() ? event.getOwner() : GetMaster();
Player* bot = botAI->GetBot(); Player* bot = botAI->GetBot();
WorldPosition botPos(bot); WorldPosition botPos(bot);
WorldPosition* ptr_botpos = &botPos; WorldPosition* ptr_botpos = &botPos;

View File

@ -352,7 +352,6 @@ bool QuestUpdateAddItemAction::Execute(Event event)
uint32 itemId, count; uint32 itemId, count;
p >> itemId >> count; p >> itemId >> count;
Player* requester = event.getOwner() ? event.getOwner() : GetMaster();
auto const* itemPrototype = sObjectMgr->GetItemTemplate(itemId); auto const* itemPrototype = sObjectMgr->GetItemTemplate(itemId);
if (itemPrototype) if (itemPrototype)
{ {
@ -407,8 +406,6 @@ bool QuestItemPushResultAction::Execute(Event event)
if (!quest) if (!quest)
return false; return false;
const QuestStatusData& q_status = bot->getQuestStatusMap().at(questId);
for (int i = 0; i < QUEST_ITEM_OBJECTIVES_COUNT; i++) for (int i = 0; i < QUEST_ITEM_OBJECTIVES_COUNT; i++)
{ {
uint32 itemId = quest->RequiredItemId[i]; uint32 itemId = quest->RequiredItemId[i];
@ -448,8 +445,6 @@ bool QuestUpdateFailedTimerAction::Execute(Event event)
uint32 questId; uint32 questId;
p >> questId; p >> questId;
Player* requester = event.getOwner() ? event.getOwner() : GetMaster();
Quest const* qInfo = sObjectMgr->GetQuestTemplate(questId); Quest const* qInfo = sObjectMgr->GetQuestTemplate(questId);
if (qInfo) if (qInfo)

View File

@ -28,7 +28,7 @@ bool RememberTaxiAction::Execute(Event event)
case CMSG_ACTIVATETAXIEXPRESS: case CMSG_ACTIVATETAXIEXPRESS:
{ {
ObjectGuid guid; ObjectGuid guid;
uint32 node_count, totalcost; uint32 node_count;
p >> guid >> node_count; p >> guid >> node_count;
LastMovement& movement = context->GetValue<LastMovement&>("last taxi")->Get(); LastMovement& movement = context->GetValue<LastMovement&>("last taxi")->Get();

View File

@ -156,7 +156,6 @@ bool SayAction::isUseful()
void ChatReplyAction::ChatReplyDo(Player* bot, uint32& type, uint32& guid1, uint32& guid2, std::string& msg, std::string& chanName, std::string& name) void ChatReplyAction::ChatReplyDo(Player* bot, uint32& type, uint32& guid1, uint32& guid2, std::string& msg, std::string& chanName, std::string& name)
{ {
ChatReplyType replyType = REPLY_NOT_UNDERSTAND; // default not understand
std::string respondsText = ""; std::string respondsText = "";
// if we're just commanding bots around, don't respond... // if we're just commanding bots around, don't respond...

View File

@ -10,8 +10,10 @@
bool SecurityCheckAction::isUseful() bool SecurityCheckAction::isUseful()
{ {
return sRandomPlayerbotMgr.IsRandomBot(bot) && botAI->GetMaster() && return RandomPlayerbotMgr::instance().IsRandomBot(bot)
botAI->GetMaster()->GetSession()->GetSecurity() < SEC_GAMEMASTER && !GET_PLAYERBOT_AI(botAI->GetMaster()); && botAI->GetMaster()
&& botAI->GetMaster()->GetSession()->GetSecurity() < SEC_GAMEMASTER
&& !GET_PLAYERBOT_AI(botAI->GetMaster());
} }
bool SecurityCheckAction::Execute(Event /*event*/) bool SecurityCheckAction::Execute(Event /*event*/)

View File

@ -66,7 +66,8 @@ bool SetCraftAction::Execute(Event event)
if (!spellInfo) if (!spellInfo)
continue; continue;
if (SkillLineAbilityEntry const* skillLine = skillSpells[spellId]) SkillLineAbilityEntry const* skillLine = skillSpells[spellId];
if (skillLine != nullptr)
{ {
for (uint8 i = 0; i < 3; ++i) for (uint8 i = 0; i < 3; ++i)
{ {

View File

@ -26,20 +26,10 @@ bool SetHomeAction::Execute(Event /*event*/)
if (Unit* unit = botAI->GetUnit(selection)) if (Unit* unit = botAI->GetUnit(selection))
if (unit->HasNpcFlag(UNIT_NPC_FLAG_INNKEEPER)) if (unit->HasNpcFlag(UNIT_NPC_FLAG_INNKEEPER))
{ {
if (isRpgAction) Creature* creature = botAI->GetCreature(selection);
{ bot->GetSession()->SendBindPoint(creature);
Creature* creature = botAI->GetCreature(selection); botAI->TellMaster("This inn is my new home");
bot->GetSession()->SendBindPoint(creature); return true;
botAI->TellMaster("This inn is my new home");
return true;
}
else
{
Creature* creature = botAI->GetCreature(selection);
bot->GetSession()->SendBindPoint(creature);
botAI->TellMaster("This inn is my new home");
return true;
}
} }
GuidVector npcs = AI_VALUE(GuidVector, "nearest npcs"); GuidVector npcs = AI_VALUE(GuidVector, "nearest npcs");

View File

@ -40,9 +40,8 @@ bool ShareQuestAction::Execute(Event event)
return false; return false;
} }
bool AutoShareQuestAction::Execute(Event event) bool AutoShareQuestAction::Execute(Event /*event*/)
{ {
Player* requester = event.getOwner() ? event.getOwner() : GetMaster();
bool shared = false; bool shared = false;
for (uint8 slot = 0; slot < MAX_QUEST_LOG_SIZE; ++slot) for (uint8 slot = 0; slot < MAX_QUEST_LOG_SIZE; ++slot)

View File

@ -47,7 +47,6 @@ bool StayAction::isUseful()
PositionInfo stayPosition = AI_VALUE(PositionMap&, "position")["stay"]; PositionInfo stayPosition = AI_VALUE(PositionMap&, "position")["stay"];
if (stayPosition.isSet()) if (stayPosition.isSet())
{ {
const float distance = bot->GetDistance(stayPosition.x, stayPosition.y, stayPosition.z);
if (sPlayerbotAIConfig.followDistance) if (sPlayerbotAIConfig.followDistance)
return false; return false;
} }

View File

@ -63,7 +63,6 @@ bool SuggestWhatToDoAction::Execute(Event /*event*/)
fnct_ptr(); fnct_ptr();
std::string const qualifier = "suggest what to do"; std::string const qualifier = "suggest what to do";
time_t lastSaid = AI_VALUE2(time_t, "last said", qualifier);
botAI->GetAiObjectContext()->GetValue<time_t>("last said", qualifier)->Set(time(nullptr) + urand(1, 60)); botAI->GetAiObjectContext()->GetValue<time_t>("last said", qualifier)->Set(time(nullptr) + urand(1, 60));
return true; return true;
@ -221,7 +220,7 @@ void SuggestWhatToDoAction::thunderfury()
class FindTradeItemsVisitor : public IterateItemsVisitor class FindTradeItemsVisitor : public IterateItemsVisitor
{ {
public: public:
FindTradeItemsVisitor(uint32 quality) : quality(quality), IterateItemsVisitor() {} FindTradeItemsVisitor(uint32 quality) : IterateItemsVisitor(), quality(quality) {}
bool Visit(Item* item) override bool Visit(Item* item) override
{ {

View File

@ -231,7 +231,6 @@ void TalkToQuestGiverAction::AskToSelectReward(Quest const* quest, std::ostrings
for (uint8 i = 0; i < quest->GetRewChoiceItemsCount(); ++i) for (uint8 i = 0; i < quest->GetRewChoiceItemsCount(); ++i)
{ {
ItemTemplate const* item = sObjectMgr->GetItemTemplate(quest->RewardChoiceItemId[i]); ItemTemplate const* item = sObjectMgr->GetItemTemplate(quest->RewardChoiceItemId[i]);
ItemUsage usage = AI_VALUE2(ItemUsage, "item usage", quest->RewardChoiceItemId[i]);
if (!forEquip || BestRewards(quest).count(i) > 0) if (!forEquip || BestRewards(quest).count(i) > 0)
{ {
@ -248,7 +247,6 @@ bool TurnInQueryQuestAction::Execute(Event event)
WorldPacket pakcet = event.getPacket(); WorldPacket pakcet = event.getPacket();
ObjectGuid guid; ObjectGuid guid;
uint32 questId; uint32 questId;
ObjectGuid unk1;
pakcet >> guid >> questId; pakcet >> guid >> questId;
Object* object = Object* object =
ObjectAccessor::GetObjectByTypeMask(*bot, guid, TYPEMASK_UNIT | TYPEMASK_GAMEOBJECT | TYPEMASK_ITEM); ObjectAccessor::GetObjectByTypeMask(*bot, guid, TYPEMASK_UNIT | TYPEMASK_GAMEOBJECT | TYPEMASK_ITEM);

View File

@ -51,7 +51,6 @@ bool TameAction::Execute(Event event)
{ {
std::set<std::string> normalFamilies; std::set<std::string> normalFamilies;
std::set<std::string> exoticFamilies; std::set<std::string> exoticFamilies;
Player* bot = botAI->GetBot();
// Loop over all creature templates and collect tameable families // Loop over all creature templates and collect tameable families
CreatureTemplateContainer const* creatures = sObjectMgr->GetCreatureTemplates(); CreatureTemplateContainer const* creatures = sObjectMgr->GetCreatureTemplates();

View File

@ -31,8 +31,14 @@ bool TaxiAction::Execute(Event event)
GuidVector units = *context->GetValue<GuidVector>("nearest npcs"); GuidVector units = *context->GetValue<GuidVector>("nearest npcs");
for (ObjectGuid const guid : units) for (ObjectGuid const guid : units)
{ {
Creature* npc = bot->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_FLIGHTMASTER); Creature* npc = ObjectAccessor::GetCreature(*bot, guid);
if (!npc) if (!npc || !npc->IsAlive())
continue;
if (!(npc->GetNpcFlags() & UNIT_NPC_FLAG_FLIGHTMASTER))
continue;
if (bot->GetDistance(npc) > sPlayerbotAIConfig.farDistance)
continue; continue;
uint32 curloc = sObjectMgr->GetNearestTaxiNode(npc->GetPositionX(), npc->GetPositionY(), npc->GetPositionZ(), uint32 curloc = sObjectMgr->GetNearestTaxiNode(npc->GetPositionX(), npc->GetPositionY(), npc->GetPositionZ(),
@ -50,21 +56,17 @@ bool TaxiAction::Execute(Event event)
} }
} }
// stagger bot takeoff
uint32 delayMin = sConfigMgr->GetOption<uint32>("AiPlayerbot.BotTaxiDelayMinMs", 350u, false);
uint32 delayMax = sConfigMgr->GetOption<uint32>("AiPlayerbot.BotTaxiDelayMaxMs", 5000u, false);
uint32 gapMs = sConfigMgr->GetOption<uint32>("AiPlayerbot.BotTaxiGapMs", 200u, false);
uint32 gapJitterMs = sConfigMgr->GetOption<uint32>("AiPlayerbot.BotTaxiGapJitterMs", 100u, false);
// Only for follower bots // Only for follower bots
if (botAI->HasRealPlayerMaster()) if (botAI->HasRealPlayerMaster())
{ {
uint32 index = botAI->GetGroupSlotIndex(bot); uint32 index = botAI->GetGroupSlotIndex(bot);
uint32 delay = delayMin + index * gapMs + urand(0, gapJitterMs); uint32 delay = sPlayerbotAIConfig.botTaxiDelayMin +
index * sPlayerbotAIConfig.botTaxiGapMs +
urand(0, sPlayerbotAIConfig.botTaxiGapJitterMs);
delay = std::min(delay, delayMax); delay = std::min(delay, sPlayerbotAIConfig.botTaxiDelayMax);
// Store the npcs GUID so we can re-acquire the pointer later // Store the NPC's GUID so we can re-acquire the pointer later
ObjectGuid npcGuid = npc->GetGUID(); ObjectGuid npcGuid = npc->GetGUID();
// schedule the take-off // schedule the take-off

View File

@ -95,7 +95,6 @@ bool TellAuraAction::Execute(Event /*event*/)
std::string caster_name = caster ? caster->GetName() : "unknown"; std::string caster_name = caster ? caster->GetName() : "unknown";
bool is_area = aura->IsArea(); bool is_area = aura->IsArea();
int32 duration = aura->GetDuration(); int32 duration = aura->GetDuration();
const SpellInfo* spellInfo = aura->GetSpellInfo();
int32 spellId = aura->GetSpellInfo()->Id; int32 spellId = aura->GetSpellInfo()->Id;
bool isPositive = aura->GetSpellInfo()->IsPositive(); bool isPositive = aura->GetSpellInfo()->IsPositive();
sLog->outMessage("playerbot", LOG_LEVEL_DEBUG, sLog->outMessage("playerbot", LOG_LEVEL_DEBUG,

View File

@ -64,8 +64,6 @@ bool TradeStatusAction::Execute(Event event)
uint32 discount = sRandomPlayerbotMgr.GetTradeDiscount(bot, trader); uint32 discount = sRandomPlayerbotMgr.GetTradeDiscount(bot, trader);
if (CheckTrade()) if (CheckTrade())
{ {
int32 botMoney = CalculateCost(bot, true);
std::map<uint32, uint32> givenItemIds, takenItemIds; std::map<uint32, uint32> givenItemIds, takenItemIds;
for (uint32 slot = 0; slot < TRADE_SLOT_TRADED_COUNT; ++slot) for (uint32 slot = 0; slot < TRADE_SLOT_TRADED_COUNT; ++slot)
{ {
@ -161,15 +159,11 @@ bool TradeStatusAction::CheckTrade()
if (!botAI->HasActivePlayerMaster() && GET_PLAYERBOT_AI(bot->GetTrader())) if (!botAI->HasActivePlayerMaster() && GET_PLAYERBOT_AI(bot->GetTrader()))
{ {
bool isGivingItem = false;
for (uint32 slot = 0; slot < TRADE_SLOT_TRADED_COUNT; ++slot) for (uint32 slot = 0; slot < TRADE_SLOT_TRADED_COUNT; ++slot)
{ {
Item* item = bot->GetTradeData()->GetItem((TradeSlots)slot); Item* item = bot->GetTradeData()->GetItem((TradeSlots)slot);
if (item) if (item)
{
isGivingItem = true;
break; break;
}
} }
bool isGettingItem = false; bool isGettingItem = false;
for (uint32 slot = 0; slot < TRADE_SLOT_TRADED_COUNT; ++slot) for (uint32 slot = 0; slot < TRADE_SLOT_TRADED_COUNT; ++slot)

View File

@ -134,8 +134,6 @@ bool MoveFromDarkPortalAction::Execute(Event /*event*/)
if (bot->GetTeamId() == TEAM_ALLIANCE) if (bot->GetTeamId() == TEAM_ALLIANCE)
return MoveTo(530, -319.261f, 1027.213, 54.172638f, false, true); return MoveTo(530, -319.261f, 1027.213, 54.172638f, false, true);
else
return MoveTo(530, -180.444f, 1027.947, 54.181538f, false, true);
return false; return MoveTo(530, -180.444f, 1027.947, 54.181538f, false, true);
} }

View File

@ -69,7 +69,6 @@ bool UseItemAction::UseItem(Item* item, ObjectGuid goGuid, Item* itemTarget, Uni
uint8 bagIndex = item->GetBagSlot(); uint8 bagIndex = item->GetBagSlot();
uint8 slot = item->GetSlot(); uint8 slot = item->GetSlot();
uint8 spell_index = 0;
uint8 cast_count = 1; uint8 cast_count = 1;
ObjectGuid item_guid = item->GetGUID(); ObjectGuid item_guid = item->GetGUID();
uint32 glyphIndex = 0; uint32 glyphIndex = 0;

View File

@ -108,7 +108,7 @@ std::string const WhoAction::QuerySkill(std::string const text)
return out.str(); return out.str();
} }
std::string const WhoAction::QuerySpec(std::string const text) std::string const WhoAction::QuerySpec(std::string const /*text*/)
{ {
std::ostringstream out; std::ostringstream out;

View File

@ -8,11 +8,13 @@
bool WipeAction::Execute(Event event) bool WipeAction::Execute(Event event)
{ {
Player* master = event.getOwner(); Player* const owner = event.getOwner();
Player* const master = this->botAI->GetMaster();
if (botAI->GetMaster()->GetGUID() != event.getOwner()->GetGUID()) if (owner != nullptr && master != nullptr && master->GetGUID() != owner->GetGUID())
return false; return false;
bot->Kill(bot, bot); bot->Kill(bot, bot);
return true; return true;
} }

View File

@ -8,7 +8,7 @@
#include "LootValues.h" #include "LootValues.h"
#include "NamedObjectContext.h" #include "NamedObjectContext.h"
#include "Playerbots.h" #include "PlayerbotAI.h"
#include "PvpValues.h" #include "PvpValues.h"
#include "QuestValues.h" #include "QuestValues.h"

View File

@ -11,11 +11,6 @@
#include "ServerFacade.h" #include "ServerFacade.h"
#include "SharedDefines.h" #include "SharedDefines.h"
static float GetSpeedInMotion(Unit* target)
{
return target->GetSpeed(Movement::SelectSpeedType(target->GetUnitMovementFlags()));
}
bool EnemyTooCloseForSpellTrigger::IsActive() bool EnemyTooCloseForSpellTrigger::IsActive()
{ {
Unit* target = AI_VALUE(Unit*, "current target"); Unit* target = AI_VALUE(Unit*, "current target");

View File

@ -144,8 +144,6 @@ UnitPosition MultiLineUnitPlacer::Place(FormationUnit* unit, uint32 index, uint3
uint32 lineNo = index / 6; uint32 lineNo = index / 6;
uint32 indexInLine = index % 6; uint32 indexInLine = index % 6;
uint32 lineSize = std::max(count - lineNo * 6, uint32(6)); uint32 lineSize = std::max(count - lineNo * 6, uint32(6));
float x = cos(orientation) * sPlayerbotAIConfig.followDistance * lineNo;
float y = sin(orientation) * sPlayerbotAIConfig.followDistance * lineNo;
return placer.Place(unit, indexInLine, lineSize); return placer.Place(unit, indexInLine, lineSize);
} }
@ -160,17 +158,13 @@ UnitPosition SingleLineUnitPlacer::Place(FormationUnit* unit, uint32 index, uint
void FormationSlot::Move(float dx, float dy) void FormationSlot::Move(float dx, float dy)
{ {
for (FormationUnit* unit : units) for (FormationUnit* unit : units)
{
unit->SetLocation(unit->GetX() + dx, unit->GetY() + dy); unit->SetLocation(unit->GetX() + dx, unit->GetY() + dy);
}
} }
FormationSlot::~FormationSlot() FormationSlot::~FormationSlot()
{ {
for (FormationUnit* unit : units) for (FormationUnit* unit : units)
{
delete unit; delete unit;
}
units.clear(); units.clear();
} }

View File

@ -30,7 +30,6 @@ Unit* GrindTargetValue::Calculate()
Unit* GrindTargetValue::FindTargetForGrinding(uint32 assistCount) Unit* GrindTargetValue::FindTargetForGrinding(uint32 assistCount)
{ {
uint32 memberCount = 1;
Group* group = bot->GetGroup(); Group* group = bot->GetGroup();
Player* master = GetMaster(); Player* master = GetMaster();
@ -65,7 +64,6 @@ Unit* GrindTargetValue::FindTargetForGrinding(uint32 assistCount)
if (!unit->IsInWorld() || unit->IsDuringRemoveFromWorld()) if (!unit->IsInWorld() || unit->IsDuringRemoveFromWorld())
continue; continue;
auto& rep = bot->ToPlayer()->GetReputationMgr();
if (unit->ToCreature() && !unit->ToCreature()->GetCreatureTemplate()->lootid && if (unit->ToCreature() && !unit->ToCreature()->GetCreatureTemplate()->lootid &&
bot->GetReactionTo(unit) >= REP_NEUTRAL) bot->GetReactionTo(unit) >= REP_NEUTRAL)
continue; continue;

View File

@ -69,8 +69,10 @@ ItemUsage ItemUsageValue::Calculate()
if (proto->Class == ITEM_CLASS_KEY) if (proto->Class == ITEM_CLASS_KEY)
return ITEM_USAGE_USE; return ITEM_USAGE_USE;
const uint32_t maxCount = proto->MaxCount;
if (proto->Class == ITEM_CLASS_CONSUMABLE && if (proto->Class == ITEM_CLASS_CONSUMABLE &&
(proto->MaxCount == 0 || bot->GetItemCount(itemId, false) < proto->MaxCount)) (maxCount == 0 || bot->GetItemCount(itemId, false) < maxCount))
{ {
std::string const foodType = GetConsumableType(proto, bot->GetPower(POWER_MANA)); std::string const foodType = GetConsumableType(proto, bot->GetPower(POWER_MANA));

View File

@ -6,7 +6,7 @@
#include "LeastHpTargetValue.h" #include "LeastHpTargetValue.h"
#include "AttackersValue.h" #include "AttackersValue.h"
#include "PlayerbotAI.h" #include "Playerbots.h"
class FindLeastHpTargetStrategy : public FindNonCcTargetStrategy class FindLeastHpTargetStrategy : public FindNonCcTargetStrategy
{ {
@ -15,7 +15,6 @@ public:
void CheckAttacker(Unit* attacker, ThreatMgr* threatMgr) override void CheckAttacker(Unit* attacker, ThreatMgr* threatMgr) override
{ {
Player* bot = botAI->GetBot();
if (IsCcTarget(attacker)) if (IsCcTarget(attacker))
return; return;
@ -30,5 +29,6 @@ protected:
Unit* LeastHpTargetValue::Calculate() Unit* LeastHpTargetValue::Calculate()
{ {
FindLeastHpTargetStrategy strategy(botAI); FindLeastHpTargetStrategy strategy(botAI);
return FindTarget(&strategy); return FindTarget(&strategy);
} }

View File

@ -8,7 +8,6 @@
#include "NamedObjectContext.h" #include "NamedObjectContext.h"
#include "PartyMemberValue.h" #include "PartyMemberValue.h"
#include "PlayerbotAIConfig.h"
class PlayerbotAI; class PlayerbotAI;
class Unit; class Unit;
@ -16,8 +15,7 @@ class Unit;
class PartyMemberWithoutAuraValue : public PartyMemberValue, public Qualified class PartyMemberWithoutAuraValue : public PartyMemberValue, public Qualified
{ {
public: public:
PartyMemberWithoutAuraValue(PlayerbotAI* botAI, std::string const name = "party member without aura", PartyMemberWithoutAuraValue(PlayerbotAI* botAI, std::string const name = "party member without aura")
float range = sPlayerbotAIConfig.sightDistance)
: PartyMemberValue(botAI, name) : PartyMemberValue(botAI, name)
{ {
} }

View File

@ -5,15 +5,14 @@
#include "PossibleRpgTargetsValue.h" #include "PossibleRpgTargetsValue.h"
#include "AiObjectContext.h" #include "CellImpl.h"
#include "GridNotifiers.h"
#include "GridNotifiersImpl.h"
#include "ObjectGuid.h" #include "ObjectGuid.h"
#include "Playerbots.h"
#include "ServerFacade.h" #include "ServerFacade.h"
#include "SharedDefines.h" #include "SharedDefines.h"
#include "NearestGameObjects.h" #include "NearestGameObjects.h"
#include "GridNotifiers.h"
#include "GridNotifiersImpl.h"
#include "CellImpl.h"
#include "TravelMgr.h"
std::vector<uint32> PossibleRpgTargetsValue::allowedNpcFlags; std::vector<uint32> PossibleRpgTargetsValue::allowedNpcFlags;
@ -74,15 +73,9 @@ bool PossibleRpgTargetsValue::AcceptUnit(Unit* unit)
} }
TravelTarget* travelTarget = context->GetValue<TravelTarget*>("travel target")->Get(); TravelTarget* travelTarget = context->GetValue<TravelTarget*>("travel target")->Get();
if (travelTarget && travelTarget->getDestination() &&
if ( travelTarget->getDestination()->getEntry() == unit->GetEntry())
travelTarget != nullptr
&& travelTarget->getDestination()
&& (uint32_t)travelTarget->getDestination()->getEntry() == unit->GetEntry()
)
{
return true; return true;
}
if (urand(1, 100) < 25 && unit->IsFriendlyTo(bot)) if (urand(1, 100) < 25 && unit->IsFriendlyTo(bot))
return true; return true;

View File

@ -14,7 +14,6 @@ Unit* SnareTargetValue::Calculate()
std::string const spell = qualifier; std::string const spell = qualifier;
GuidVector attackers = botAI->GetAiObjectContext()->GetValue<GuidVector>("attackers")->Get(); GuidVector attackers = botAI->GetAiObjectContext()->GetValue<GuidVector>("attackers")->Get();
Unit* target = botAI->GetAiObjectContext()->GetValue<Unit*>("current target")->Get();
for (ObjectGuid const guid : attackers) for (ObjectGuid const guid : attackers)
{ {
Unit* unit = botAI->GetUnit(guid); Unit* unit = botAI->GetUnit(guid);

View File

@ -208,7 +208,6 @@ uint8 BagSpaceValue::Calculate()
++totalused; ++totalused;
} }
uint32 totalfree = 16 - totalused;
for (uint8 bag = INVENTORY_SLOT_BAG_START; bag < INVENTORY_SLOT_BAG_END; ++bag) for (uint8 bag = INVENTORY_SLOT_BAG_START; bag < INVENTORY_SLOT_BAG_END; ++bag)
{ {
const Bag* const pBag = (Bag*)bot->GetItemByPos(INVENTORY_SLOT_BAG_0, bag); const Bag* const pBag = (Bag*)bot->GetItemByPos(INVENTORY_SLOT_BAG_0, bag);
@ -218,7 +217,6 @@ uint8 BagSpaceValue::Calculate()
if (pBagProto->Class == ITEM_CLASS_CONTAINER && pBagProto->SubClass == ITEM_SUBCLASS_CONTAINER) if (pBagProto->Class == ITEM_CLASS_CONTAINER && pBagProto->SubClass == ITEM_SUBCLASS_CONTAINER)
{ {
total += pBag->GetBagSize(); total += pBag->GetBagSize();
totalfree += pBag->GetFreeSlots();
totalused += pBag->GetBagSize() - pBag->GetFreeSlots(); totalused += pBag->GetBagSize() - pBag->GetFreeSlots();
} }
} }

View File

@ -16,7 +16,6 @@
#include "ObjectAccessor.h" #include "ObjectAccessor.h"
using ai::buff::MakeAuraQualifierForBuff; using ai::buff::MakeAuraQualifierForBuff;
using ai::buff::UpgradeToGroupIfAppropriate;
// Helper : detect tank role on the target (player bot or not) return true if spec is tank or if the bot have tank strategies (bear/tank/tank face). // Helper : detect tank role on the target (player bot or not) return true if spec is tank or if the bot have tank strategies (bear/tank/tank face).
static inline bool IsTankRole(Player* p) static inline bool IsTankRole(Player* p)
@ -53,11 +52,6 @@ static inline bool IsOnlyPaladinInGroup(Player* bot)
return pals == 1u; return pals == 1u;
} }
static inline bool GroupHasTankOfClass(Group* g, uint8 classId)
{
return GroupHasTankOfClass(g, static_cast<Classes>(classId));
}
inline std::string const GetActualBlessingOfMight(Unit* target) inline std::string const GetActualBlessingOfMight(Unit* target)
{ {
if (!target->ToPlayer()) if (!target->ToPlayer())

View File

@ -283,20 +283,6 @@ static uint32 GetRequiredTotemSpellId(PlayerbotAI* ai, const char* strategies[],
return 0; // No relevant strategy active, or bot doesn't know any rank return 0; // No relevant strategy active, or bot doesn't know any rank
} }
// Get the spellId of the currently summoned totem in the slot
static uint32 GetSummonedTotemSpellId(Player* bot, uint8 slot)
{
ObjectGuid guid = bot->m_SummonSlot[slot];
if (guid.IsEmpty())
return 0;
Creature* totem = bot->GetMap()->GetCreature(guid);
if (!totem)
return 0;
return totem->GetUInt32Value(UNIT_CREATED_BY_SPELL);
}
bool NoEarthTotemTrigger::IsActive() bool NoEarthTotemTrigger::IsActive()
{ {
// Check if the bot has Stoneskin Totem (required level 4) and prevents the trigger firing if it doesn't // Check if the bot has Stoneskin Totem (required level 4) and prevents the trigger firing if it doesn't

View File

@ -359,10 +359,8 @@ class SetTotemTrigger : public Trigger
public: public:
// Template constructor: infers N (size of the id array) at compile time // Template constructor: infers N (size of the id array) at compile time
template <size_t N> template <size_t N>
SetTotemTrigger(PlayerbotAI* ai, std::string const& spellName, uint32 requiredSpellId, SetTotemTrigger(PlayerbotAI* ai, std::string const& spellName, const uint32 (&ids)[N], int actionButtonId)
const uint32 (&ids)[N], int actionButtonId)
: Trigger(ai, "set " + spellName) : Trigger(ai, "set " + spellName)
, requiredSpellId(requiredSpellId)
, totemSpellIds(ids) , totemSpellIds(ids)
, totemSpellIdsCount(N) , totemSpellIdsCount(N)
, actionButtonId(actionButtonId) , actionButtonId(actionButtonId)
@ -370,7 +368,6 @@ public:
bool IsActive() override; bool IsActive() override;
private: private:
uint32 requiredSpellId;
uint32 const* totemSpellIds; uint32 const* totemSpellIds;
size_t totemSpellIdsCount; size_t totemSpellIdsCount;
int actionButtonId; int actionButtonId;
@ -380,119 +377,119 @@ class SetStrengthOfEarthTotemTrigger : public SetTotemTrigger
{ {
public: public:
SetStrengthOfEarthTotemTrigger(PlayerbotAI* ai) SetStrengthOfEarthTotemTrigger(PlayerbotAI* ai)
: SetTotemTrigger(ai, "strength of earth totem", SPELL_STRENGTH_OF_EARTH_TOTEM_RANK_1, STRENGTH_OF_EARTH_TOTEM, TOTEM_BAR_SLOT_EARTH) {} : SetTotemTrigger(ai, "strength of earth totem", STRENGTH_OF_EARTH_TOTEM, TOTEM_BAR_SLOT_EARTH) {}
}; };
class SetStoneskinTotemTrigger : public SetTotemTrigger class SetStoneskinTotemTrigger : public SetTotemTrigger
{ {
public: public:
SetStoneskinTotemTrigger(PlayerbotAI* ai) SetStoneskinTotemTrigger(PlayerbotAI* ai)
: SetTotemTrigger(ai, "stoneskin totem", SPELL_STONESKIN_TOTEM_RANK_1, STONESKIN_TOTEM, TOTEM_BAR_SLOT_EARTH) {} : SetTotemTrigger(ai, "stoneskin totem", STONESKIN_TOTEM, TOTEM_BAR_SLOT_EARTH) {}
}; };
class SetTremorTotemTrigger : public SetTotemTrigger class SetTremorTotemTrigger : public SetTotemTrigger
{ {
public: public:
SetTremorTotemTrigger(PlayerbotAI* ai) SetTremorTotemTrigger(PlayerbotAI* ai)
: SetTotemTrigger(ai, "tremor totem", SPELL_TREMOR_TOTEM_RANK_1, TREMOR_TOTEM, TOTEM_BAR_SLOT_EARTH) {} : SetTotemTrigger(ai, "tremor totem", TREMOR_TOTEM, TOTEM_BAR_SLOT_EARTH) {}
}; };
class SetEarthbindTotemTrigger : public SetTotemTrigger class SetEarthbindTotemTrigger : public SetTotemTrigger
{ {
public: public:
SetEarthbindTotemTrigger(PlayerbotAI* ai) SetEarthbindTotemTrigger(PlayerbotAI* ai)
: SetTotemTrigger(ai, "earthbind totem", SPELL_EARTHBIND_TOTEM_RANK_1, EARTHBIND_TOTEM, TOTEM_BAR_SLOT_EARTH) {} : SetTotemTrigger(ai, "earthbind totem", EARTHBIND_TOTEM, TOTEM_BAR_SLOT_EARTH) {}
}; };
class SetSearingTotemTrigger : public SetTotemTrigger class SetSearingTotemTrigger : public SetTotemTrigger
{ {
public: public:
SetSearingTotemTrigger(PlayerbotAI* ai) SetSearingTotemTrigger(PlayerbotAI* ai)
: SetTotemTrigger(ai, "searing totem", SPELL_SEARING_TOTEM_RANK_1, SEARING_TOTEM, TOTEM_BAR_SLOT_FIRE) {} : SetTotemTrigger(ai, "searing totem", SEARING_TOTEM, TOTEM_BAR_SLOT_FIRE) {}
}; };
class SetMagmaTotemTrigger : public SetTotemTrigger class SetMagmaTotemTrigger : public SetTotemTrigger
{ {
public: public:
SetMagmaTotemTrigger(PlayerbotAI* ai) SetMagmaTotemTrigger(PlayerbotAI* ai)
: SetTotemTrigger(ai, "magma totem", SPELL_MAGMA_TOTEM_RANK_1, MAGMA_TOTEM, TOTEM_BAR_SLOT_FIRE) {} : SetTotemTrigger(ai, "magma totem", MAGMA_TOTEM, TOTEM_BAR_SLOT_FIRE) {}
}; };
class SetFlametongueTotemTrigger : public SetTotemTrigger class SetFlametongueTotemTrigger : public SetTotemTrigger
{ {
public: public:
SetFlametongueTotemTrigger(PlayerbotAI* ai) SetFlametongueTotemTrigger(PlayerbotAI* ai)
: SetTotemTrigger(ai, "flametongue totem", SPELL_FLAMETONGUE_TOTEM_RANK_1, FLAMETONGUE_TOTEM, TOTEM_BAR_SLOT_FIRE) {} : SetTotemTrigger(ai, "flametongue totem", FLAMETONGUE_TOTEM, TOTEM_BAR_SLOT_FIRE) {}
}; };
class SetTotemOfWrathTrigger : public SetTotemTrigger class SetTotemOfWrathTrigger : public SetTotemTrigger
{ {
public: public:
SetTotemOfWrathTrigger(PlayerbotAI* ai) SetTotemOfWrathTrigger(PlayerbotAI* ai)
: SetTotemTrigger(ai, "totem of wrath", SPELL_TOTEM_OF_WRATH_RANK_1, TOTEM_OF_WRATH, TOTEM_BAR_SLOT_FIRE) {} : SetTotemTrigger(ai, "totem of wrath", TOTEM_OF_WRATH, TOTEM_BAR_SLOT_FIRE) {}
}; };
class SetFrostResistanceTotemTrigger : public SetTotemTrigger class SetFrostResistanceTotemTrigger : public SetTotemTrigger
{ {
public: public:
SetFrostResistanceTotemTrigger(PlayerbotAI* ai) SetFrostResistanceTotemTrigger(PlayerbotAI* ai)
: SetTotemTrigger(ai, "frost resistance totem", SPELL_FROST_RESISTANCE_TOTEM_RANK_1, FROST_RESISTANCE_TOTEM, TOTEM_BAR_SLOT_FIRE) {} : SetTotemTrigger(ai, "frost resistance totem", FROST_RESISTANCE_TOTEM, TOTEM_BAR_SLOT_FIRE) {}
}; };
class SetHealingStreamTotemTrigger : public SetTotemTrigger class SetHealingStreamTotemTrigger : public SetTotemTrigger
{ {
public: public:
SetHealingStreamTotemTrigger(PlayerbotAI* ai) SetHealingStreamTotemTrigger(PlayerbotAI* ai)
: SetTotemTrigger(ai, "healing stream totem", SPELL_HEALING_STREAM_TOTEM_RANK_1, HEALING_STREAM_TOTEM, TOTEM_BAR_SLOT_WATER) {} : SetTotemTrigger(ai, "healing stream totem", HEALING_STREAM_TOTEM, TOTEM_BAR_SLOT_WATER) {}
}; };
class SetManaSpringTotemTrigger : public SetTotemTrigger class SetManaSpringTotemTrigger : public SetTotemTrigger
{ {
public: public:
SetManaSpringTotemTrigger(PlayerbotAI* ai) SetManaSpringTotemTrigger(PlayerbotAI* ai)
: SetTotemTrigger(ai, "mana spring totem", SPELL_MANA_SPRING_TOTEM_RANK_1, MANA_SPRING_TOTEM, TOTEM_BAR_SLOT_WATER) {} : SetTotemTrigger(ai, "mana spring totem", MANA_SPRING_TOTEM, TOTEM_BAR_SLOT_WATER) {}
}; };
class SetCleansingTotemTrigger : public SetTotemTrigger class SetCleansingTotemTrigger : public SetTotemTrigger
{ {
public: public:
SetCleansingTotemTrigger(PlayerbotAI* ai) SetCleansingTotemTrigger(PlayerbotAI* ai)
: SetTotemTrigger(ai, "cleansing totem", SPELL_CLEANSING_TOTEM_RANK_1, CLEANSING_TOTEM, TOTEM_BAR_SLOT_WATER) {} : SetTotemTrigger(ai, "cleansing totem", CLEANSING_TOTEM, TOTEM_BAR_SLOT_WATER) {}
}; };
class SetFireResistanceTotemTrigger : public SetTotemTrigger class SetFireResistanceTotemTrigger : public SetTotemTrigger
{ {
public: public:
SetFireResistanceTotemTrigger(PlayerbotAI* ai) SetFireResistanceTotemTrigger(PlayerbotAI* ai)
: SetTotemTrigger(ai, "fire resistance totem", SPELL_FIRE_RESISTANCE_TOTEM_RANK_1, FIRE_RESISTANCE_TOTEM, TOTEM_BAR_SLOT_WATER) {} : SetTotemTrigger(ai, "fire resistance totem", FIRE_RESISTANCE_TOTEM, TOTEM_BAR_SLOT_WATER) {}
}; };
class SetWrathOfAirTotemTrigger : public SetTotemTrigger class SetWrathOfAirTotemTrigger : public SetTotemTrigger
{ {
public: public:
SetWrathOfAirTotemTrigger(PlayerbotAI* ai) SetWrathOfAirTotemTrigger(PlayerbotAI* ai)
: SetTotemTrigger(ai, "wrath of air totem", SPELL_WRATH_OF_AIR_TOTEM_RANK_1, WRATH_OF_AIR_TOTEM, TOTEM_BAR_SLOT_AIR) {} : SetTotemTrigger(ai, "wrath of air totem", WRATH_OF_AIR_TOTEM, TOTEM_BAR_SLOT_AIR) {}
}; };
class SetWindfuryTotemTrigger : public SetTotemTrigger class SetWindfuryTotemTrigger : public SetTotemTrigger
{ {
public: public:
SetWindfuryTotemTrigger(PlayerbotAI* ai) SetWindfuryTotemTrigger(PlayerbotAI* ai)
: SetTotemTrigger(ai, "windfury totem", SPELL_WINDFURY_TOTEM_RANK_1, WINDFURY_TOTEM, TOTEM_BAR_SLOT_AIR) {} : SetTotemTrigger(ai, "windfury totem", WINDFURY_TOTEM, TOTEM_BAR_SLOT_AIR) {}
}; };
class SetNatureResistanceTotemTrigger : public SetTotemTrigger class SetNatureResistanceTotemTrigger : public SetTotemTrigger
{ {
public: public:
SetNatureResistanceTotemTrigger(PlayerbotAI* ai) SetNatureResistanceTotemTrigger(PlayerbotAI* ai)
: SetTotemTrigger(ai, "nature resistance totem", SPELL_NATURE_RESISTANCE_TOTEM_RANK_1, NATURE_RESISTANCE_TOTEM, TOTEM_BAR_SLOT_AIR) {} : SetTotemTrigger(ai, "nature resistance totem", NATURE_RESISTANCE_TOTEM, TOTEM_BAR_SLOT_AIR) {}
}; };
class SetGroundingTotemTrigger : public SetTotemTrigger class SetGroundingTotemTrigger : public SetTotemTrigger
{ {
public: public:
SetGroundingTotemTrigger(PlayerbotAI* ai) SetGroundingTotemTrigger(PlayerbotAI* ai)
: SetTotemTrigger(ai, "grounding totem", SPELL_GROUNDING_TOTEM_RANK_1, GROUNDING_TOTEM, TOTEM_BAR_SLOT_AIR) {} : SetTotemTrigger(ai, "grounding totem", GROUNDING_TOTEM, TOTEM_BAR_SLOT_AIR) {}
}; };
#endif #endif

View File

@ -17,11 +17,9 @@ bool MoveFromBronjahmAction::Execute(Event /*event*/)
bool AttackCorruptedSoulFragmentAction::Execute(Event /*event*/) bool AttackCorruptedSoulFragmentAction::Execute(Event /*event*/)
{ {
Unit* currentTarget = AI_VALUE(Unit*, "current target");
GuidVector targets = AI_VALUE(GuidVector, "possible targets"); GuidVector targets = AI_VALUE(GuidVector, "possible targets");
// If no valid skull target, search for corrupted soul fragment // If no valid skull target, search for corrupted soul fragment
Unit* empoweredPrince = nullptr;
for (auto i = targets.begin(); i != targets.end(); ++i) for (auto i = targets.begin(); i != targets.end(); ++i)
{ {
Unit* unit = botAI->GetUnit(*i); Unit* unit = botAI->GetUnit(*i);
@ -30,8 +28,6 @@ bool AttackCorruptedSoulFragmentAction::Execute(Event /*event*/)
if (unit->GetEntry() == NPC_CORRUPTED_SOUL_FRAGMENT) if (unit->GetEntry() == NPC_CORRUPTED_SOUL_FRAGMENT)
{ {
empoweredPrince = unit;
// Mark corrupted soul fragment with skull if in group and not already marked // Mark corrupted soul fragment with skull if in group and not already marked
if (Group* group = bot->GetGroup()) if (Group* group = bot->GetGroup())
{ {

View File

@ -23,7 +23,6 @@ bool AttackSnakeWrapAction::Execute(Event /*event*/)
Unit* boss = AI_VALUE2(Unit*, "find target", "slad'ran"); Unit* boss = AI_VALUE2(Unit*, "find target", "slad'ran");
if (!boss) { return false; } if (!boss) { return false; }
Unit* snakeWrap = nullptr;
// Target is not findable from threat table using AI_VALUE2(), // Target is not findable from threat table using AI_VALUE2(),
// therefore need to search manually for the unit name // therefore need to search manually for the unit name
GuidVector targets = AI_VALUE(GuidVector, "possible targets no los"); GuidVector targets = AI_VALUE(GuidVector, "possible targets no los");

View File

@ -55,10 +55,6 @@ bool ToCLanceAction::Execute(Event /*event*/)
// If we found the lance, equip it // If we found the lance, equip it
if (lanceItem) if (lanceItem)
{ {
// Store the lance's current position
uint8 srcBag = lanceItem->GetBagSlot();
uint8 srcSlot = lanceItem->GetSlot();
// First unequip current weapon if it exists // First unequip current weapon if it exists
if (oldWeapon) if (oldWeapon)
bot->SwapItem(oldWeapon->GetPos(), lanceItem->GetPos()); bot->SwapItem(oldWeapon->GetPos(), lanceItem->GetPos());

View File

@ -399,7 +399,9 @@ bool HighKingMaulgarBanishFelstalkerAction::Execute(Event /*event*/)
} }
} }
if (warlockIndex >= 0 && warlockIndex < felStalkers.size()) const int64_t felStalkersSize = felStalkers.size();
if (warlockIndex >= 0 && warlockIndex < felStalkersSize)
{ {
Unit* assignedFelStalker = felStalkers[warlockIndex]; Unit* assignedFelStalker = felStalkers[warlockIndex];
if (!botAI->HasAura("banish", assignedFelStalker) && botAI->CanCastSpell("banish", assignedFelStalker)) if (!botAI->HasAura("banish", assignedFelStalker) && botAI->CanCastSpell("banish", assignedFelStalker))
@ -511,7 +513,6 @@ bool GruulTheDragonkillerTanksPositionBossAction::Execute(Event /*event*/)
if (distanceToTankPosition > maxDistance) if (distanceToTankPosition > maxDistance)
{ {
float step = std::min(maxDistance, distanceToTankPosition);
float moveX = bot->GetPositionX() + (dX / distanceToTankPosition) * maxDistance; float moveX = bot->GetPositionX() + (dX / distanceToTankPosition) * maxDistance;
float moveY = bot->GetPositionY() + (dY / distanceToTankPosition) * maxDistance; float moveY = bot->GetPositionY() + (dY / distanceToTankPosition) * maxDistance;
const float moveZ = position.GetPositionZ(); const float moveZ = position.GetPositionZ();

View File

@ -1034,8 +1034,6 @@ bool IccDbsTankPositionAction::CrowdControlBloodBeasts()
NPC_BLOOD_BEAST4}; NPC_BLOOD_BEAST4};
const GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); const GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs");
bool appliedCC = false;
for (auto const& npc : npcs) for (auto const& npc : npcs)
{ {
Unit* unit = botAI->GetUnit(npc); Unit* unit = botAI->GetUnit(npc);
@ -1054,73 +1052,43 @@ bool IccDbsTankPositionAction::CrowdControlBloodBeasts()
{ {
case CLASS_MAGE: case CLASS_MAGE:
if (!botAI->HasAura("Frost Nova", unit)) if (!botAI->HasAura("Frost Nova", unit))
{
botAI->CastSpell("Frost Nova", unit); botAI->CastSpell("Frost Nova", unit);
appliedCC = true;
}
break; break;
case CLASS_DRUID: case CLASS_DRUID:
if (!botAI->HasAura("Entangling Roots", unit)) if (!botAI->HasAura("Entangling Roots", unit))
{
botAI->CastSpell("Entangling Roots", unit); botAI->CastSpell("Entangling Roots", unit);
appliedCC = true;
}
break; break;
case CLASS_PALADIN: case CLASS_PALADIN:
if (!botAI->HasAura("Hammer of Justice", unit)) if (!botAI->HasAura("Hammer of Justice", unit))
{
botAI->CastSpell("Hammer of Justice", unit); botAI->CastSpell("Hammer of Justice", unit);
appliedCC = true;
}
break; break;
case CLASS_WARRIOR: case CLASS_WARRIOR:
if (!botAI->HasAura("Hamstring", unit)) if (!botAI->HasAura("Hamstring", unit))
{
botAI->CastSpell("Hamstring", unit); botAI->CastSpell("Hamstring", unit);
appliedCC = true;
}
break; break;
case CLASS_HUNTER: case CLASS_HUNTER:
if (!botAI->HasAura("Concussive Shot", unit)) if (!botAI->HasAura("Concussive Shot", unit))
{
botAI->CastSpell("Concussive Shot", unit); botAI->CastSpell("Concussive Shot", unit);
appliedCC = true;
}
break; break;
case CLASS_ROGUE: case CLASS_ROGUE:
if (!botAI->HasAura("Kidney Shot", unit)) if (!botAI->HasAura("Kidney Shot", unit))
{
botAI->CastSpell("Kidney Shot", unit); botAI->CastSpell("Kidney Shot", unit);
appliedCC = true;
}
break; break;
case CLASS_SHAMAN: case CLASS_SHAMAN:
if (!botAI->HasAura("Frost Shock", unit)) if (!botAI->HasAura("Frost Shock", unit))
{
botAI->CastSpell("Frost Shock", unit); botAI->CastSpell("Frost Shock", unit);
appliedCC = true;
}
break; break;
case CLASS_DEATH_KNIGHT: case CLASS_DEATH_KNIGHT:
if (!botAI->HasAura("Chains of Ice", unit)) if (!botAI->HasAura("Chains of Ice", unit))
{
botAI->CastSpell("Chains of Ice", unit); botAI->CastSpell("Chains of Ice", unit);
appliedCC = true;
}
break; break;
case CLASS_PRIEST: case CLASS_PRIEST:
if (!botAI->HasAura("Psychic Scream", unit)) if (!botAI->HasAura("Psychic Scream", unit))
{
botAI->CastSpell("Psychic Scream", unit); botAI->CastSpell("Psychic Scream", unit);
appliedCC = true;
}
break; break;
case CLASS_WARLOCK: case CLASS_WARLOCK:
if (!botAI->HasAura("Fear", unit)) if (!botAI->HasAura("Fear", unit))
{
botAI->CastSpell("Fear", unit); botAI->CastSpell("Fear", unit);
appliedCC = true;
}
break; break;
default: default:
break; break;
@ -1464,7 +1432,6 @@ int IccFestergutGroupPositionAction::CalculatePositionIndex(Group* group)
else else
{ {
// Fill remaining spots in second row // Fill remaining spots in second row
int spotsInFirstRow = 6;
int spotsInSecondRow = healerSpotsUsed - 6; int spotsInSecondRow = healerSpotsUsed - 6;
int remainingInSecondRow = 6 - spotsInSecondRow; int remainingInSecondRow = 6 - spotsInSecondRow;
@ -1511,7 +1478,6 @@ int IccFestergutGroupPositionAction::CalculatePositionIndex(Group* group)
bool IccFestergutSporeAction::Execute(Event /*event*/) bool IccFestergutSporeAction::Execute(Event /*event*/)
{ {
constexpr float POSITION_TOLERANCE = 4.0f; constexpr float POSITION_TOLERANCE = 4.0f;
constexpr float SPREAD_RADIUS = 2.0f;
// Check if bot has spore // Check if bot has spore
bool hasSpore = bot->HasAura(SPELL_GAS_SPORE); // gas spore bool hasSpore = bot->HasAura(SPELL_GAS_SPORE); // gas spore
@ -1661,7 +1627,7 @@ bool IccRotfaceTankPositionAction::PositionMainTankAndMelee(Unit* boss)
{ {
bool isBossCasting = false; bool isBossCasting = false;
if (boss && boss->HasUnitState(UNIT_STATE_CASTING) && boss->GetCurrentSpell(SPELL_SLIME_SPRAY)) if (boss && boss->HasUnitState(UNIT_STATE_CASTING) && boss->GetCurrentSpell(SPELL_SLIME_SPRAY))
bool isBossCasting = true; isBossCasting = true;
if (bot->GetExactDist2d(ICC_ROTFACE_CENTER_POSITION) > 7.0f && botAI->HasAggro(boss) && botAI->IsMainTank(bot)) if (bot->GetExactDist2d(ICC_ROTFACE_CENTER_POSITION) > 7.0f && botAI->HasAggro(boss) && botAI->IsMainTank(bot))
MoveTo(bot->GetMapId(), ICC_ROTFACE_CENTER_POSITION.GetPositionX(), MoveTo(bot->GetMapId(), ICC_ROTFACE_CENTER_POSITION.GetPositionX(),
@ -1829,21 +1795,7 @@ bool IccRotfaceGroupPositionAction::Execute(Event /*event*/)
return false; return false;
const GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); const GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs");
bool floodPresent = false;
for (auto const& npc : npcs)
{
Unit* unit = botAI->GetUnit(npc);
if (!unit || !botAI->HasAura("Ooze Flood", unit))
continue;
float puddleDistance = bot->GetExactDist2d(unit);
if (puddleDistance < 30.0f)
floodPresent = true;
}
Unit* bigOoze = AI_VALUE2(Unit*, "find target", "big ooze");
bool hasOozeFlood = botAI->HasAura("Ooze Flood", bot); bool hasOozeFlood = botAI->HasAura("Ooze Flood", bot);
Unit* smallOoze = AI_VALUE2(Unit*, "find target", "little ooze"); Unit* smallOoze = AI_VALUE2(Unit*, "find target", "little ooze");
bool hasMutatedInfection = botAI->HasAura("Mutated Infection", bot); bool hasMutatedInfection = botAI->HasAura("Mutated Infection", bot);
@ -2014,7 +1966,7 @@ bool IccRotfaceGroupPositionAction::PositionRangedAndHealers(Unit* boss,Unit *sm
Difficulty diff = bot->GetRaidDifficulty(); Difficulty diff = bot->GetRaidDifficulty();
bool isBossCasting = false; bool isBossCasting = false;
if (boss && boss->HasUnitState(UNIT_STATE_CASTING) && boss->GetCurrentSpell(SPELL_SLIME_SPRAY)) if (boss && boss->HasUnitState(UNIT_STATE_CASTING) && boss->GetCurrentSpell(SPELL_SLIME_SPRAY))
bool isBossCasting = true; isBossCasting = true;
bool isHeroic = (diff == RAID_DIFFICULTY_10MAN_HEROIC || diff == RAID_DIFFICULTY_25MAN_HEROIC); bool isHeroic = (diff == RAID_DIFFICULTY_10MAN_HEROIC || diff == RAID_DIFFICULTY_25MAN_HEROIC);
@ -2070,7 +2022,6 @@ bool IccRotfaceGroupPositionAction::FindAndMoveFromClosestMember(Unit* boss, Uni
const float maxMoveDistance = 12.0f; // Limit maximum movement distance const float maxMoveDistance = 12.0f; // Limit maximum movement distance
const float puddleSafeDistance = 30.0f; // Minimum distance to stay away from puddle const float puddleSafeDistance = 30.0f; // Minimum distance to stay away from puddle
const float minCenterDistance = 20.0f; // Minimum distance from center position const float minCenterDistance = 20.0f; // Minimum distance from center position
const bool isRanged = botAI->IsRanged(bot) || botAI->IsHeal(bot);
// Ranged: spread from other members // Ranged: spread from other members
const GuidVector members = AI_VALUE(GuidVector, "group members"); const GuidVector members = AI_VALUE(GuidVector, "group members");
@ -2233,7 +2184,6 @@ bool IccRotfaceMoveAwayFromExplosionAction::MoveToRandomSafeLocation()
// Move in increments of 5.0f towards the calculated position // Move in increments of 5.0f towards the calculated position
float currentX = bot->GetPositionX(); float currentX = bot->GetPositionX();
float currentY = bot->GetPositionY(); float currentY = bot->GetPositionY();
float currentZ = bot->GetPositionZ();
float directionX = moveX - currentX; float directionX = moveX - currentX;
float directionY = moveY - currentY; float directionY = moveY - currentY;
@ -2276,7 +2226,6 @@ Unit* IccPutricideGrowingOozePuddleAction::FindClosestThreateningPuddle()
Unit* closestPuddle = nullptr; Unit* closestPuddle = nullptr;
float closestDistance = FLT_MAX; float closestDistance = FLT_MAX;
float closestSafeDistance = BASE_RADIUS;
for (auto const& npc : npcs) for (auto const& npc : npcs)
{ {
@ -2293,7 +2242,6 @@ Unit* IccPutricideGrowingOozePuddleAction::FindClosestThreateningPuddle()
if (currentDistance < safeDistance && currentDistance < closestDistance) if (currentDistance < safeDistance && currentDistance < closestDistance)
{ {
closestDistance = currentDistance; closestDistance = currentDistance;
closestSafeDistance = safeDistance;
closestPuddle = unit; closestPuddle = unit;
} }
} }
@ -3648,7 +3596,6 @@ bool IccBpcKineticBombAction::Execute(Event /*event*/)
Unit* IccBpcKineticBombAction::FindOptimalKineticBomb() Unit* IccBpcKineticBombAction::FindOptimalKineticBomb()
{ {
static constexpr float MAX_HEIGHT_DIFF = 20.0f;
static constexpr std::array<uint32_t, 4> KINETIC_BOMB_ENTRIES = {NPC_KINETIC_BOMB1, NPC_KINETIC_BOMB2, static constexpr std::array<uint32_t, 4> KINETIC_BOMB_ENTRIES = {NPC_KINETIC_BOMB1, NPC_KINETIC_BOMB2,
NPC_KINETIC_BOMB3, NPC_KINETIC_BOMB4}; NPC_KINETIC_BOMB3, NPC_KINETIC_BOMB4};
@ -4012,7 +3959,6 @@ bool IccBqlGroupPositionAction::HandleShadowsMovement()
// Find closest safe point by searching in both directions from closest point // Find closest safe point by searching in both directions from closest point
Position safeMoveTarget = closestPoint; Position safeMoveTarget = closestPoint;
float safeMoveTargetDist = FLT_MAX;
bool foundSafe = closestIsSafe; bool foundSafe = closestIsSafe;
// Only search for safe spots if the closest point isn't already safe // Only search for safe spots if the closest point isn't already safe
@ -4083,7 +4029,6 @@ bool IccBqlGroupPositionAction::HandleShadowsMovement()
if (foundSafe) if (foundSafe)
{ {
// If we found a safe point, penalize based on travel distance along the curve to reach it // If we found a safe point, penalize based on travel distance along the curve to reach it
float stepsToCurve = minDist / 2.0f; // Approximate steps to reach the curve
float safeDist = bot->GetExactDist2d(safeMoveTarget); float safeDist = bot->GetExactDist2d(safeMoveTarget);
// Add distance penalty based on how far we need to move along the curve // Add distance penalty based on how far we need to move along the curve
@ -4364,7 +4309,6 @@ bool IccBqlGroupPositionAction::HandleGroupPosition(Unit* boss, Aura* frenzyAura
rangedBots.erase(std::remove(rangedBots.begin(), rangedBots.end(), h), rangedBots.end()); rangedBots.erase(std::remove(rangedBots.begin(), rangedBots.end(), h), rangedBots.end());
// Distribute remaining ranged evenly // Distribute remaining ranged evenly
size_t totalRanged = leftSide.size() + rightSide.size() + rangedBots.size();
size_t leftCount = leftSide.size(); size_t leftCount = leftSide.size();
size_t rightCount = rightSide.size(); size_t rightCount = rightSide.size();
for (Player* p : rangedBots) for (Player* p : rangedBots)
@ -5562,7 +5506,6 @@ bool IccValithriaDreamCloudAction::Execute(Event /*event*/)
auto it = std::find(dreamBots.begin(), dreamBots.end(), bot); auto it = std::find(dreamBots.begin(), dreamBots.end(), bot);
if (it == dreamBots.end()) if (it == dreamBots.end())
return false; return false;
size_t myIndex = std::distance(dreamBots.begin(), it);
// Check if all dream bots are stacked within 3f of the current leader (lowest guid) // Check if all dream bots are stacked within 3f of the current leader (lowest guid)
constexpr float STACK_RADIUS = 2.0f; constexpr float STACK_RADIUS = 2.0f;
@ -6699,7 +6642,6 @@ bool IccSindragosaFrostBombAction::Execute(Event /*event*/)
} }
} }
Unit* losTomb = myTombs[bestIdx]; Unit* losTomb = myTombs[bestIdx];
ObjectGuid losTombGuid = myTombGuids[bestIdx];
// Calculate position for LOS (stand at least 6.5f behind the tomb from the bomb) // Calculate position for LOS (stand at least 6.5f behind the tomb from the bomb)
float angle = marker->GetAngle(losTomb); float angle = marker->GetAngle(losTomb);
@ -6782,7 +6724,6 @@ bool IccSindragosaFrostBombAction::Execute(Event /*event*/)
// Clear the marker for our group's icon // Clear the marker for our group's icon
group->SetTargetIcon(iconIndex, bot->GetGUID(), ObjectGuid::Empty); group->SetTargetIcon(iconIndex, bot->GetGUID(), ObjectGuid::Empty);
} }
Unit* boss = AI_VALUE2(Unit*, "find target", "sindragosa");
bot->AttackStop(); bot->AttackStop();
return true; return true;
} }
@ -7021,10 +6962,7 @@ bool IccLichKingWinterAction::Execute(Event /*event*/)
{ {
const ObjectGuid currentSkullTarget = group->GetTargetIcon(7); const ObjectGuid currentSkullTarget = group->GetTargetIcon(7);
if (!currentSkullTarget.IsEmpty()) if (!currentSkullTarget.IsEmpty())
{
Unit* skullTarget = ObjectAccessor::GetUnit(*bot, currentSkullTarget);
group->SetTargetIcon(7, bot->GetGUID(), ObjectGuid::Empty); group->SetTargetIcon(7, bot->GetGUID(), ObjectGuid::Empty);
}
} }
if (isVictim) if (isVictim)
@ -7802,7 +7740,6 @@ bool IccLichKingAddsAction::Execute(Event /*event*/)
//------CHEAT------- //------CHEAT-------
} }
Unit* spiritWarden = AI_VALUE2(Unit*, "find target", "spirit warden");
bool hasPlague = botAI->HasAura("Necrotic Plague", bot); bool hasPlague = botAI->HasAura("Necrotic Plague", bot);
Unit* terenasMenethilHC = bot->FindNearestCreature(NPC_TERENAS_MENETHIL_HC, 55.0f); Unit* terenasMenethilHC = bot->FindNearestCreature(NPC_TERENAS_MENETHIL_HC, 55.0f);
@ -8475,8 +8412,6 @@ bool IccLichKingAddsAction::HandleAssistTankAddManagement(Unit* boss, Difficulty
// In heroic mode, stay at melee position // In heroic mode, stay at melee position
if (diff && (diff == RAID_DIFFICULTY_10MAN_HEROIC || diff == RAID_DIFFICULTY_25MAN_HEROIC)) if (diff && (diff == RAID_DIFFICULTY_10MAN_HEROIC || diff == RAID_DIFFICULTY_25MAN_HEROIC))
{ {
Unit* mainTank = AI_VALUE(Unit*, "main tank");
if (bot->GetExactDist2d(ICC_LICH_KING_ASSISTHC_POSITION.GetPositionX(), if (bot->GetExactDist2d(ICC_LICH_KING_ASSISTHC_POSITION.GetPositionX(),
ICC_LICH_KING_ASSISTHC_POSITION.GetPositionY()) > 2.0f) ICC_LICH_KING_ASSISTHC_POSITION.GetPositionY()) > 2.0f)
{ {
@ -8713,7 +8648,6 @@ void IccLichKingAddsAction::HandleDefileMechanics(Unit* boss, Difficulty diff)
// Gather all defile units // Gather all defile units
std::vector<Unit*> defiles; std::vector<Unit*> defiles;
Unit* closestDefile = nullptr;
float closestDistance = std::numeric_limits<float>::max(); float closestDistance = std::numeric_limits<float>::max();
GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs");
@ -8725,10 +8659,7 @@ void IccLichKingAddsAction::HandleDefileMechanics(Unit* boss, Difficulty diff)
defiles.push_back(unit); defiles.push_back(unit);
float dist = bot->GetDistance(unit); float dist = bot->GetDistance(unit);
if (dist < closestDistance) if (dist < closestDistance)
{
closestDistance = dist; closestDistance = dist;
closestDefile = unit;
}
} }
} }
@ -9068,7 +8999,6 @@ void IccLichKingAddsAction::HandleValkyrMarking(const std::vector<Unit*>& grabbi
std::sort(sortedValkyrs.begin(), sortedValkyrs.end(), [](Unit* a, Unit* b) { return a->GetGUID() < b->GetGUID(); }); std::sort(sortedValkyrs.begin(), sortedValkyrs.end(), [](Unit* a, Unit* b) { return a->GetGUID() < b->GetGUID(); });
static constexpr uint8_t ICON_INDICES[] = {7, 6, 0}; // Skull, Cross, Star static constexpr uint8_t ICON_INDICES[] = {7, 6, 0}; // Skull, Cross, Star
static constexpr const char* ICON_NAMES[] = {"skull", "cross", "star"};
// In heroic mode, clean up invalid markers for all possible icons // In heroic mode, clean up invalid markers for all possible icons
if (diff && (diff == RAID_DIFFICULTY_10MAN_HEROIC || diff == RAID_DIFFICULTY_25MAN_HEROIC)) if (diff && (diff == RAID_DIFFICULTY_10MAN_HEROIC || diff == RAID_DIFFICULTY_25MAN_HEROIC))

View File

@ -24,8 +24,6 @@
// LK global variables // LK global variables
namespace namespace
{ {
uint32 g_lastPlagueTime = 0;
bool g_plagueAllowedToCure = false;
std::map<ObjectGuid, uint32> g_plagueTimes; std::map<ObjectGuid, uint32> g_plagueTimes;
std::map<ObjectGuid, bool> g_allowCure; std::map<ObjectGuid, bool> g_allowCure;
std::mutex g_plagueMutex; // Lock before accessing shared variables std::mutex g_plagueMutex; // Lock before accessing shared variables

View File

@ -1102,7 +1102,6 @@ bool IccLichKingShadowTrapTrigger::IsActive()
// search for all nearby traps // search for all nearby traps
GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs");
std::vector<Unit*> nearbyTraps; std::vector<Unit*> nearbyTraps;
bool needToMove = false;
for (auto& npc : npcs) for (auto& npc : npcs)
{ {

View File

@ -870,7 +870,6 @@ bool NetherspiteAvoidBeamAndVoidZoneAction::Execute(Event /*event*/)
if (!netherspite) if (!netherspite)
return false; return false;
auto [redBlocker, greenBlocker, blueBlocker] = GetCurrentBeamBlockers(botAI, bot);
std::vector<Unit*> voidZones = GetAllVoidZones(botAI, bot); std::vector<Unit*> voidZones = GetAllVoidZones(botAI, bot);
bool nearVoidZone = !IsSafePosition(bot->GetPositionX(), bot->GetPositionY(), bool nearVoidZone = !IsSafePosition(bot->GetPositionX(), bot->GetPositionY(),

View File

@ -8,9 +8,6 @@ bool SartharionTankPositionAction::Execute(Event /*event*/)
Unit* boss = AI_VALUE2(Unit*, "find target", "sartharion"); Unit* boss = AI_VALUE2(Unit*, "find target", "sartharion");
if (!boss) { return false; } if (!boss) { return false; }
// Unit* shadron = AI_VALUE2(Unit*, "find target", "shadron");
// Unit* tenebron = AI_VALUE2(Unit*, "find target", "tenebron");
// Unit* vesperon = AI_VALUE2(Unit*, "find target", "vesperon");
Unit* shadron = nullptr; Unit* shadron = nullptr;
Unit* tenebron = nullptr; Unit* tenebron = nullptr;
Unit* vesperon = nullptr; Unit* vesperon = nullptr;
@ -96,9 +93,7 @@ bool AvoidTwilightFissureAction::Execute(Event /*event*/)
{ {
float currentDistance = bot->GetDistance2d(unit); float currentDistance = bot->GetDistance2d(unit);
if (currentDistance < radius) if (currentDistance < radius)
{
return MoveAway(unit, radius - currentDistance); return MoveAway(unit, radius - currentDistance);
}
} }
} }
return false; return false;
@ -127,39 +122,29 @@ bool AvoidFlameTsunamiAction::Execute(Event /*event*/)
{ {
bool wavePassed = currentPos.GetPositionX() > unit->GetPositionX(); bool wavePassed = currentPos.GetPositionX() > unit->GetPositionX();
if (wavePassed) if (wavePassed)
{
return false; return false;
}
if (bot->GetExactDist2d(currentPos.GetPositionX(), TSUNAMI_RIGHT_SAFE_ALL) > looseDistance) if (bot->GetExactDist2d(currentPos.GetPositionX(), TSUNAMI_RIGHT_SAFE_ALL) > looseDistance)
{
return MoveTo(OS_MAP_ID, currentPos.GetPositionX(), TSUNAMI_RIGHT_SAFE_ALL, currentPos.GetPositionZ(), return MoveTo(OS_MAP_ID, currentPos.GetPositionX(), TSUNAMI_RIGHT_SAFE_ALL, currentPos.GetPositionZ(),
false, false, false, false, MovementPriority::MOVEMENT_COMBAT); false, false, false, false, MovementPriority::MOVEMENT_COMBAT);
}
} }
else // LEFT WAVE else // LEFT WAVE
{ {
bool wavePassed = currentPos.GetPositionX() < unit->GetPositionX(); bool wavePassed = currentPos.GetPositionX() < unit->GetPositionX();
if (wavePassed) if (wavePassed)
{
return false; return false;
}
if (botAI->IsMelee(bot)) if (botAI->IsMelee(bot))
{ {
if (bot->GetExactDist2d(currentPos.GetPositionX(), TSUNAMI_LEFT_SAFE_MELEE) > looseDistance) if (bot->GetExactDist2d(currentPos.GetPositionX(), TSUNAMI_LEFT_SAFE_MELEE) > looseDistance)
{
return MoveTo(OS_MAP_ID, currentPos.GetPositionX(), TSUNAMI_LEFT_SAFE_MELEE, currentPos.GetPositionZ(), return MoveTo(OS_MAP_ID, currentPos.GetPositionX(), TSUNAMI_LEFT_SAFE_MELEE, currentPos.GetPositionZ(),
false, false, false, false, MovementPriority::MOVEMENT_COMBAT); false, false, false, false, MovementPriority::MOVEMENT_COMBAT);
}
} }
else // Ranged/healers else // Ranged/healers
{ {
if (bot->GetExactDist2d(currentPos.GetPositionX(), TSUNAMI_LEFT_SAFE_RANGED) > looseDistance) if (bot->GetExactDist2d(currentPos.GetPositionX(), TSUNAMI_LEFT_SAFE_RANGED) > looseDistance)
{
return MoveTo(OS_MAP_ID, currentPos.GetPositionX(), TSUNAMI_LEFT_SAFE_RANGED, currentPos.GetPositionZ(), return MoveTo(OS_MAP_ID, currentPos.GetPositionX(), TSUNAMI_LEFT_SAFE_RANGED, currentPos.GetPositionZ(),
false, false, false, false, MovementPriority::MOVEMENT_COMBAT); false, false, false, false, MovementPriority::MOVEMENT_COMBAT);
}
} }
} }
} }
@ -178,30 +163,18 @@ bool SartharionAttackPriorityAction::Execute(Event /*event*/)
Unit* target = nullptr; Unit* target = nullptr;
if (acolyte) if (acolyte)
{
target = acolyte; target = acolyte;
}
else if (vesperon) else if (vesperon)
{
target = vesperon; target = vesperon;
}
else if (tenebron) else if (tenebron)
{
target = tenebron; target = tenebron;
}
else if (shadron) else if (shadron)
{
target = shadron; target = shadron;
}
else if (sartharion) else if (sartharion)
{
target = sartharion; target = sartharion;
}
if (target && AI_VALUE(Unit*, "current target") != target) if (target && AI_VALUE(Unit*, "current target") != target)
{
return Attack(target); return Attack(target);
}
return false; return false;
} }
@ -215,9 +188,7 @@ bool EnterTwilightPortalAction::Execute(Event /*event*/)
if (!portal) { return false; } if (!portal) { return false; }
if (!portal->IsAtInteractDistance(bot)) if (!portal->IsAtInteractDistance(bot))
{
return MoveTo(portal, fmaxf(portal->GetInteractionDistance() - 1.0f, 0.0f)); return MoveTo(portal, fmaxf(portal->GetInteractionDistance() - 1.0f, 0.0f));
}
// Go through portal // Go through portal
WorldPacket data1(CMSG_GAMEOBJ_USE); WorldPacket data1(CMSG_GAMEOBJ_USE);
@ -230,12 +201,11 @@ bool EnterTwilightPortalAction::Execute(Event /*event*/)
bool ExitTwilightPortalAction::Execute(Event /*event*/) bool ExitTwilightPortalAction::Execute(Event /*event*/)
{ {
GameObject* portal = bot->FindNearestGameObject(GO_NORMAL_PORTAL, 100.0f); GameObject* portal = bot->FindNearestGameObject(GO_NORMAL_PORTAL, 100.0f);
if (!portal) { return false; } if (!portal)
return false;
if (!portal->IsAtInteractDistance(bot)) if (!portal->IsAtInteractDistance(bot))
{
return MoveTo(portal, fmaxf(portal->GetInteractionDistance() - 1.0f, 0.0f)); return MoveTo(portal, fmaxf(portal->GetInteractionDistance() - 1.0f, 0.0f));
}
// Go through portal // Go through portal
WorldPacket data1(CMSG_GAMEOBJ_USE); WorldPacket data1(CMSG_GAMEOBJ_USE);

View File

@ -77,39 +77,101 @@ void SetRtiTarget(PlayerbotAI* botAI, const std::string& rtiName, Unit* target)
// Intended for purposes of storing and erasing timers and trackers in associative containers // Intended for purposes of storing and erasing timers and trackers in associative containers
bool IsMechanicTrackerBot(PlayerbotAI* botAI, Player* bot, uint32 mapId, Player* exclude) bool IsMechanicTrackerBot(PlayerbotAI* botAI, Player* bot, uint32 mapId, Player* exclude)
{ {
if (Group* group = bot->GetGroup()) if (!botAI->IsDps(bot) || !bot->IsAlive() || bot->GetMapId() != mapId)
{ return false;
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
{
Player* member = ref->GetSource();
if (!member || !member->IsAlive() || member->GetMapId() != mapId ||
!GET_PLAYERBOT_AI(member) || !botAI->IsDps(member))
continue;
if (member != exclude) Group* group = bot->GetGroup();
return member == bot; if (!group)
} return false;
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
{
Player* member = ref->GetSource();
if (!member || !member->IsAlive() || member->GetMapId() != mapId || member == exclude)
continue;
PlayerbotAI* memberAI = GET_PLAYERBOT_AI(member);
if (!memberAI || !memberAI->IsDps(member))
continue;
return member == bot;
} }
return false; return false;
} }
// Return the first matching alive unit from a cell search of nearby npcs // Requires the main tank to be alive
// More responsive than "find target," but performance cost is much higher // Note that IsMainTank() will return the player with the main tank flag, even if dead
// Re: using the third parameter (false by default), some units are never considered Player* GetGroupMainTank(PlayerbotAI* botAI, Player* bot)
// to be in combat (e.g., totems) {
Unit* GetFirstAliveUnitByEntry(PlayerbotAI* botAI, uint32 entry, bool requireInCombat) Group* group = bot->GetGroup();
if (!group)
return nullptr;
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
{
Player* member = ref->GetSource();
if (!member || !member->IsAlive())
continue;
if (botAI->IsMainTank(member))
return member;
}
return nullptr;
}
// Returns the alive assist tank of the specified index (0 = first, 1 = second, etc.)
// Priority: Assistants first, then Non-Assistants.
Player* GetGroupAssistTank(PlayerbotAI* botAI, Player* bot, uint8 index)
{
Group* group = bot->GetGroup();
if (!group)
return nullptr;
uint8 assistantCount = 0;
std::vector<Player*> nonAssistantTanks;
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
{
Player* member = ref->GetSource();
if (!member || !member->IsAlive())
continue;
if (botAI->IsAssistTank(member))
{
if (group->IsAssistant(member->GetGUID()))
{
if (assistantCount == index)
return member;
assistantCount++;
}
else
{
nonAssistantTanks.push_back(member);
}
}
}
// If the index wasn't found among assistants, check the non-assistants that were saved
uint8 nonAssistantIndex = index - assistantCount;
if (nonAssistantIndex < nonAssistantTanks.size())
return nonAssistantTanks[nonAssistantIndex];
return nullptr;
}
// Return the first matching alive unit from PossibleTargetsValue within sightDistance from config
Unit* GetFirstAliveUnitByEntry(PlayerbotAI* botAI, uint32 entry)
{ {
auto const& npcs = auto const& npcs =
botAI->GetAiObjectContext()->GetValue<GuidVector>("nearest npcs")->Get(); botAI->GetAiObjectContext()->GetValue<GuidVector>("possible targets no los")->Get();
for (auto const& npcGuid : npcs) for (auto const& npcGuid : npcs)
{ {
Unit* unit = botAI->GetUnit(npcGuid); Unit* unit = botAI->GetUnit(npcGuid);
if (unit && unit->IsAlive() && unit->GetEntry() == entry) if (unit && unit->IsAlive() && unit->GetEntry() == entry)
{ return unit;
if (!requireInCombat || unit->IsInCombat())
return unit;
}
} }
return nullptr; return nullptr;

View File

@ -15,7 +15,10 @@ void MarkTargetWithCross(Player* bot, Unit* target);
void MarkTargetWithMoon(Player* bot, Unit* target); void MarkTargetWithMoon(Player* bot, Unit* target);
void SetRtiTarget(PlayerbotAI* botAI, const std::string& rtiName, Unit* target); void SetRtiTarget(PlayerbotAI* botAI, const std::string& rtiName, Unit* target);
bool IsMechanicTrackerBot(PlayerbotAI* botAI, Player* bot, uint32 mapId, Player* exclude = nullptr); bool IsMechanicTrackerBot(PlayerbotAI* botAI, Player* bot, uint32 mapId, Player* exclude = nullptr);
Unit* GetFirstAliveUnitByEntry(PlayerbotAI* botAI, uint32 entry, bool requireInCombat = false); Player* GetGroupMainTank(PlayerbotAI* botAI, Player* bot);
Player* GetGroupAssistTank(PlayerbotAI* botAI, Player* bot, uint8 index);
Unit* GetFirstAliveUnitByEntry(
PlayerbotAI* botAI, uint32 entry);
Unit* GetNearestPlayerInRadius(Player* bot, float radius); Unit* GetNearestPlayerInRadius(Player* bot, float radius);
#endif #endif

View File

@ -6,9 +6,10 @@
#include "RaidMcStrategy.h" #include "RaidMcStrategy.h"
#include "RaidBwlStrategy.h" #include "RaidBwlStrategy.h"
#include "RaidKarazhanStrategy.h" #include "RaidKarazhanStrategy.h"
#include "RaidMagtheridonStrategy.h"
#include "RaidGruulsLairStrategy.h" #include "RaidGruulsLairStrategy.h"
#include "RaidMagtheridonStrategy.h"
#include "RaidSSCStrategy.h" #include "RaidSSCStrategy.h"
#include "RaidTempestKeepStrategy.h"
#include "RaidOsStrategy.h" #include "RaidOsStrategy.h"
#include "RaidEoEStrategy.h" #include "RaidEoEStrategy.h"
#include "RaidVoAStrategy.h" #include "RaidVoAStrategy.h"
@ -25,9 +26,10 @@ public:
creators["moltencore"] = &RaidStrategyContext::moltencore; creators["moltencore"] = &RaidStrategyContext::moltencore;
creators["bwl"] = &RaidStrategyContext::bwl; creators["bwl"] = &RaidStrategyContext::bwl;
creators["karazhan"] = &RaidStrategyContext::karazhan; creators["karazhan"] = &RaidStrategyContext::karazhan;
creators["magtheridon"] = &RaidStrategyContext::magtheridon;
creators["gruulslair"] = &RaidStrategyContext::gruulslair; creators["gruulslair"] = &RaidStrategyContext::gruulslair;
creators["magtheridon"] = &RaidStrategyContext::magtheridon;
creators["ssc"] = &RaidStrategyContext::ssc; creators["ssc"] = &RaidStrategyContext::ssc;
creators["tempestkeep"] = &RaidStrategyContext::tempestkeep;
creators["wotlk-os"] = &RaidStrategyContext::wotlk_os; creators["wotlk-os"] = &RaidStrategyContext::wotlk_os;
creators["wotlk-eoe"] = &RaidStrategyContext::wotlk_eoe; creators["wotlk-eoe"] = &RaidStrategyContext::wotlk_eoe;
creators["voa"] = &RaidStrategyContext::voa; creators["voa"] = &RaidStrategyContext::voa;
@ -41,9 +43,10 @@ private:
static Strategy* moltencore(PlayerbotAI* botAI) { return new RaidMcStrategy(botAI); } static Strategy* moltencore(PlayerbotAI* botAI) { return new RaidMcStrategy(botAI); }
static Strategy* bwl(PlayerbotAI* botAI) { return new RaidBwlStrategy(botAI); } static Strategy* bwl(PlayerbotAI* botAI) { return new RaidBwlStrategy(botAI); }
static Strategy* karazhan(PlayerbotAI* botAI) { return new RaidKarazhanStrategy(botAI); } static Strategy* karazhan(PlayerbotAI* botAI) { return new RaidKarazhanStrategy(botAI); }
static Strategy* magtheridon(PlayerbotAI* botAI) { return new RaidMagtheridonStrategy(botAI); }
static Strategy* gruulslair(PlayerbotAI* botAI) { return new RaidGruulsLairStrategy(botAI); } static Strategy* gruulslair(PlayerbotAI* botAI) { return new RaidGruulsLairStrategy(botAI); }
static Strategy* magtheridon(PlayerbotAI* botAI) { return new RaidMagtheridonStrategy(botAI); }
static Strategy* ssc(PlayerbotAI* botAI) { return new RaidSSCStrategy(botAI); } static Strategy* ssc(PlayerbotAI* botAI) { return new RaidSSCStrategy(botAI); }
static Strategy* tempestkeep(PlayerbotAI* botAI) { return new RaidTempestKeepStrategy(botAI); }
static Strategy* wotlk_os(PlayerbotAI* botAI) { return new RaidOsStrategy(botAI); } static Strategy* wotlk_os(PlayerbotAI* botAI) { return new RaidOsStrategy(botAI); }
static Strategy* wotlk_eoe(PlayerbotAI* botAI) { return new RaidEoEStrategy(botAI); } static Strategy* wotlk_eoe(PlayerbotAI* botAI) { return new RaidEoEStrategy(botAI); }
static Strategy* voa(PlayerbotAI* botAI) { return new RaidVoAStrategy(botAI); } static Strategy* voa(PlayerbotAI* botAI) { return new RaidVoAStrategy(botAI); }

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,413 @@
#ifndef _PLAYERBOT_RAIDTEMPESTKEEPACTIONS_H
#define _PLAYERBOT_RAIDTEMPESTKEEPACTIONS_H
#include "RaidTempestKeepHelpers.h"
#include "RaidTempestKeepKaelthasBossAI.h"
#include "Action.h"
#include "AttackAction.h"
#include "MovementActions.h"
using namespace TempestKeepHelpers;
// Trash
class CrimsonHandCenturionCastPolymorphAction : public Action
{
public:
CrimsonHandCenturionCastPolymorphAction(
PlayerbotAI* botAI, std::string const name = "crimson hand centurion cast polymorph") : Action(botAI, name) {}
bool Execute(Event event) override;
};
// Al'ar <Phoenix God>
class AlarMisdirectBossToMainTankAction : public AttackAction
{
public:
AlarMisdirectBossToMainTankAction(
PlayerbotAI* botAI, std::string const name = "al'ar misdirect boss to main tank") : AttackAction(botAI, name) {}
bool Execute(Event event) override;
};
class AlarBossTanksMoveBetweenPlatformsAction : public AttackAction
{
public:
AlarBossTanksMoveBetweenPlatformsAction(
PlayerbotAI* botAI, std::string const name = "al'ar boss tanks move between platforms") : AttackAction(botAI, name) {}
bool Execute(Event event) override;
private:
bool PositionMainTank(Unit* alar, int8 locationIndex);
bool PositionAssistTank(Unit* alar, int8 locationIndex);
};
class AlarMeleeDpsMoveBetweenPlatformsAction : public AttackAction
{
public:
AlarMeleeDpsMoveBetweenPlatformsAction(
PlayerbotAI* botAI, std::string const name = "al'ar melee dps move between platforms") : AttackAction(botAI, name) {}
bool Execute(Event event) override;
};
class AlarRangedAndEmberTankMoveUnderPlatformsAction : public AttackAction
{
public:
AlarRangedAndEmberTankMoveUnderPlatformsAction(
PlayerbotAI* botAI, std::string const name = "al'ar ranged and ember tank move under platforms") : AttackAction(botAI, name) {}
bool Execute(Event event) override;
};
class AlarAssistTanksPickUpEmbersAction : public AttackAction
{
public:
AlarAssistTanksPickUpEmbersAction(
PlayerbotAI* botAI, std::string const name = "al'ar assist tanks pick up embers") : AttackAction(botAI, name) {}
bool Execute(Event event) override;
private:
bool HandlePhase1Embers(Unit* alar);
bool HandlePhase2Embers(Unit* alar);
};
class AlarRangedDpsPrioritizeEmbersAction : public AttackAction
{
public:
AlarRangedDpsPrioritizeEmbersAction(
PlayerbotAI* botAI, std::string const name = "al'ar ranged dps prioritize embers") : AttackAction(botAI, name) {}
bool Execute(Event event) override;
};
class AlarJumpFromPlatformAction : public MovementAction
{
public:
AlarJumpFromPlatformAction(
PlayerbotAI* botAI, std::string const name = "al'ar jump from platform") : MovementAction(botAI, name) {}
bool Execute(Event event) override;
};
class AlarMoveAwayFromRebirthAction : public MovementAction
{
public:
AlarMoveAwayFromRebirthAction(
PlayerbotAI* botAI, std::string const name = "al'ar move away from rebirth") : MovementAction(botAI, name) {}
bool Execute(Event event) override;
};
class AlarSwapTanksOnBossAction : public AttackAction
{
public:
AlarSwapTanksOnBossAction(
PlayerbotAI* botAI, std::string const name = "al'ar swap tanks on boss") : AttackAction(botAI, name) {}
bool Execute(Event event) override;
};
class AlarAvoidFlamePatchesAndDiveBombsAction : public MovementAction
{
public:
AlarAvoidFlamePatchesAndDiveBombsAction(
PlayerbotAI* botAI, std::string const name = "al'ar avoid flame patches and dive bombs") : MovementAction(botAI, name) {}
bool Execute(Event event) override;
private:
bool AvoidFlamePatch();
bool HandleDiveBomb(Unit* alar);
};
class AlarReturnToRoomCenterAction : public MovementAction
{
public:
AlarReturnToRoomCenterAction(
PlayerbotAI* botAI, std::string const name = "al'ar return to room center") : MovementAction(botAI, name) {}
bool Execute(Event event) override;
};
class AlarManagePhaseTrackerAction : public Action
{
public:
AlarManagePhaseTrackerAction(
PlayerbotAI* botAI, std::string const name = "al'ar manage phase tracker") : Action(botAI, name) {}
bool Execute(Event event) override;
};
// Void Reaver
class VoidReaverTanksPositionBossAction : public AttackAction
{
public:
VoidReaverTanksPositionBossAction(
PlayerbotAI* botAI, std::string const name = "void reaver tanks position boss") : AttackAction(botAI, name) {}
bool Execute(Event event) override;
};
class VoidReaverUseAggroDumpAbilityAction : public Action
{
public:
VoidReaverUseAggroDumpAbilityAction(
PlayerbotAI* botAI, std::string const name = "void reaver use aggro dump ability") : Action(botAI, name) {}
bool Execute(Event event) override;
};
class VoidReaverSpreadRangedAction : public MovementAction
{
public:
VoidReaverSpreadRangedAction(
PlayerbotAI* botAI, std::string const name = "void reaver spread ranged") : MovementAction(botAI, name) {}
bool Execute(Event event) override;
private:
int GetHealerIndex(Group* group, int& healerCount);
int GetRangedDpsIndex(Group* group, int& rangedDpsCount);
};
class VoidReaverAvoidArcaneOrbAction : public MovementAction
{
public:
VoidReaverAvoidArcaneOrbAction(
PlayerbotAI* botAI, std::string const name = "void reaver avoid arcane orb") : MovementAction(botAI, name) {}
bool Execute(Event event) override;
};
class VoidReaverEraseTrackersAction : public Action
{
public:
VoidReaverEraseTrackersAction(
PlayerbotAI* botAI, std::string const name = "void reaver erase trackers") : Action(botAI, name) {}
bool Execute(Event event) override;
};
// High Astromancer Solarian
class HighAstromancerSolarianRangedLeaveSpaceForMeleeAction : public MovementAction
{
public:
HighAstromancerSolarianRangedLeaveSpaceForMeleeAction(
PlayerbotAI* botAI, std::string const name = "high astromancer solarian ranged leave space for melee") : MovementAction(botAI, name) {}
bool Execute(Event event) override;
};
class HighAstromancerSolarianMoveAwayFromGroupAction : public MovementAction
{
public:
HighAstromancerSolarianMoveAwayFromGroupAction(
PlayerbotAI* botAI, std::string const name = "high astromancer solarian move away from group") : MovementAction(botAI, name) {}
bool Execute(Event event) override;
};
class HighAstromancerSolarianStackForAoeAction : public MovementAction
{
public:
HighAstromancerSolarianStackForAoeAction(
PlayerbotAI* botAI, std::string const name = "high astromancer solarian stack for aoe") : MovementAction(botAI, name) {}
bool Execute(Event event) override;
};
class HighAstromancerSolarianTargetSolariumPriestsAction : public AttackAction
{
public:
HighAstromancerSolarianTargetSolariumPriestsAction(
PlayerbotAI* botAI, std::string const name = "high astromancer solarian target solarium priests") : AttackAction(botAI, name) {}
bool Execute(Event event) override;
private:
std::pair<Unit*, Unit*> GetSolariumPriests(PlayerbotAI* botAI);
std::vector<Player*> GetMeleeBots(Group* group);
Unit* AssignSolariumPriestsToBots(const std::pair<Unit*, Unit*>& priestsPair, const std::vector<Player*>& meleeMembers);
};
class HighAstromancerSolarianCastFearWardOnMainTankAction : public Action
{
public:
HighAstromancerSolarianCastFearWardOnMainTankAction(
PlayerbotAI* botAI, std::string const name = "high astromancer solarian cast fear ward on main tank") : Action(botAI, name) {}
bool Execute(Event event) override;
};
// Kael'thas Sunstrider <Lord of the Blood Elves>
class KaelthasSunstriderKiteThaladredAction : public MovementAction
{
public:
KaelthasSunstriderKiteThaladredAction(
PlayerbotAI* botAI) : MovementAction(botAI, "kael'thas sunstrider kite thaladred") {}
bool Execute(Event event) override;
};
class KaelthasSunstriderMisdirectAdvisorsToTanksAction : public AttackAction
{
public:
KaelthasSunstriderMisdirectAdvisorsToTanksAction(
PlayerbotAI* botAI, std::string const name = "kael'thas sunstrider misdirect advisors to tanks") : AttackAction(botAI, name) {}
bool Execute(Event event) override;
};
class KaelthasSunstriderMainTankPositionSanguinarAction : public AttackAction
{
public:
KaelthasSunstriderMainTankPositionSanguinarAction(
PlayerbotAI* botAI, std::string const name = "kael'thas sunstrider main tank position sanguinar") : AttackAction(botAI, name) {}
bool Execute(Event event) override;
};
class KaelthasSunstriderCastFearWardOnSanguinarTankAction : public Action
{
public:
KaelthasSunstriderCastFearWardOnSanguinarTankAction(
PlayerbotAI* botAI, std::string const name = "kael'thas sunstrider cast fear ward on sanguinar tank") : Action(botAI, name) {}
bool Execute(Event event) override;
};
class KaelthasSunstriderWarlockTankPositionCapernianAction : public AttackAction
{
public:
KaelthasSunstriderWarlockTankPositionCapernianAction(
PlayerbotAI* botAI, std::string const name = "kael'thas sunstrider warlock tank position capernian") : AttackAction(botAI, name) {}
bool Execute(Event event) override;
};
class KaelthasSunstriderSpreadAndMoveAwayFromCapernianAction : public MovementAction
{
public:
KaelthasSunstriderSpreadAndMoveAwayFromCapernianAction(
PlayerbotAI* botAI, std::string const name = "kael'thas sunstrider spread and move away from capernian") : MovementAction(botAI, name) {}
bool Execute(Event event) override;
private:
bool RangedBotsDisperse(boss_kaelthas* kaelAI, Unit* capernian);
bool MeleeStayBackFromCapernian(Unit* capernian);
};
class KaelthasSunstriderFirstAssistTankPositionTelonicusAction : public AttackAction
{
public:
KaelthasSunstriderFirstAssistTankPositionTelonicusAction(
PlayerbotAI* botAI, std::string const name = "kael'thas sunstrider first assist tank position telonicus") : AttackAction(botAI, name) {}
bool Execute(Event event) override;
};
class KaelthasSunstriderHandleAdvisorRolesInPhase3Action : public MovementAction
{
public:
KaelthasSunstriderHandleAdvisorRolesInPhase3Action(
PlayerbotAI* botAI, std::string const name = "kael'thas sunstrider handle advisor roles in phase 3") : MovementAction(botAI, name) {}
bool Execute(Event event) override;
};
class KaelthasSunstriderAssignAdvisorDpsPriorityAction : public AttackAction
{
public:
KaelthasSunstriderAssignAdvisorDpsPriorityAction(
PlayerbotAI* botAI, std::string const name = "kael'thas sunstrider assign advisor dps priority") : AttackAction(botAI, name) {}
bool Execute(Event event) override;
};
class KaelthasSunstriderManageAdvisorDpsTimerAction : public Action
{
public:
KaelthasSunstriderManageAdvisorDpsTimerAction(
PlayerbotAI* botAI, std::string const name = "kael'thas sunstrider manage advisor dps timer") : Action(botAI, name) {}
bool Execute(Event event) override;
};
class KaelthasSunstriderAssignLegendaryWeaponDpsPriorityAction : public AttackAction
{
public:
KaelthasSunstriderAssignLegendaryWeaponDpsPriorityAction(
PlayerbotAI* botAI, std::string const name = "kael'thas sunstrider assign legendary weapon dps priority") : AttackAction(botAI, name) {}
bool Execute(Event event) override;
};
class KaelthasSunstriderMoveDevastationAwayAction : public AttackAction
{
public:
KaelthasSunstriderMoveDevastationAwayAction(
PlayerbotAI* botAI, std::string const name = "kael'thas sunstrider move devastation away") : AttackAction(botAI, name) {}
bool Execute(Event event) override;
};
class KaelthasSunstriderLootLegendaryWeaponsAction : public MovementAction
{
public:
KaelthasSunstriderLootLegendaryWeaponsAction(
PlayerbotAI* botAI) : MovementAction(botAI, "kael'thas sunstrider loot legendary weapons") {}
bool Execute(Event event) override;
private:
bool ShouldBotLootWeapon(uint32 weaponEntry);
bool LootWeapon(uint32 weaponEntry, uint32 itemId);
};
class KaelthasSunstriderUseLegendaryWeaponsAction : public Action
{
public:
KaelthasSunstriderUseLegendaryWeaponsAction(
PlayerbotAI* botAI) : Action(botAI, "kael'thas sunstrider use legendary weapons") {}
bool Execute(Event event) override;
private:
bool UsePhaseshiftBulwark();
bool UseStaffOfDisintegration();
bool UseNetherstrandLongbow();
bool UseEquippedItemWithPacket(Item* item);
};
class KaelthasSunstriderReequipGearAction : public Action
{
public:
KaelthasSunstriderReequipGearAction(
PlayerbotAI* botAI) : Action(botAI, "kael'thas sunstrider reequip gear") {}
bool Execute(Event event) override;
};
class KaelthasSunstriderMainTankPositionBossAction : public AttackAction
{
public:
KaelthasSunstriderMainTankPositionBossAction(
PlayerbotAI* botAI, std::string const name = "kael'thas sunstrider main tank position boss") : AttackAction(botAI, name) {}
bool Execute(Event event) override;
};
class KaelthasSunstriderAvoidFlameStrikeAction : public MovementAction
{
public:
KaelthasSunstriderAvoidFlameStrikeAction(
PlayerbotAI* botAI, std::string const name = "kael'thas sunstrider avoid flame strike") : MovementAction(botAI, name) {}
bool Execute(Event event) override;
};
class KaelthasSunstriderHandlePhoenixesAndEggsAction : public AttackAction
{
public:
KaelthasSunstriderHandlePhoenixesAndEggsAction(
PlayerbotAI* botAI, std::string const name = "kael'thas sunstrider handle phoenixes and eggs") : AttackAction(botAI, name) {}
bool Execute(Event event) override;
private:
bool AssistTanksPickUpPhoenixes();
bool NonTanksDestroyEggsAndAvoidPhoenixes();
};
class KaelthasSunstriderBreakMindControlAction : public AttackAction
{
public:
KaelthasSunstriderBreakMindControlAction(
PlayerbotAI* botAI, std::string const name = "kael'thas sunstrider break mind control") : AttackAction(botAI, name) {}
bool Execute(Event event) override;
};
class KaelthasSunstriderBreakThroughShockBarrierAction : public AttackAction
{
public:
KaelthasSunstriderBreakThroughShockBarrierAction(
PlayerbotAI* botAI, std::string const name = "kael'thas sunstrider break through shock barrier") : AttackAction(botAI, name) {}
bool Execute(Event event) override;
};
class KaelthasSunstriderSpreadOutInMidairAction : public MovementAction
{
public:
KaelthasSunstriderSpreadOutInMidairAction(
PlayerbotAI* botAI, std::string const name = "kael'thas sunstrider spread out in midair") : MovementAction(botAI, name) {}
bool Execute(Event event) override;
};
#endif

View File

@ -0,0 +1,422 @@
#include "RaidTempestKeepMultipliers.h"
#include "RaidTempestKeepActions.h"
#include "RaidTempestKeepHelpers.h"
#include "RaidTempestKeepKaelthasBossAI.h"
#include "ChooseTargetActions.h"
#include "DKActions.h"
#include "DruidActions.h"
#include "DruidBearActions.h"
#include "EquipAction.h"
#include "FollowActions.h"
#include "HunterActions.h"
#include "MageActions.h"
#include "PaladinActions.h"
#include "Playerbots.h"
#include "RogueActions.h"
#include "ShamanActions.h"
#include "WarlockActions.h"
#include "WarriorActions.h"
// Al'ar <Phoenix God>
float AlarMoveBetweenPlatformsMultiplier::GetValue(Action* action)
{
Unit* alar = AI_VALUE2(Unit*, "find target", "al'ar");
if (!alar)
return 1.0f;
if (isAlarInPhase2[alar->GetMap()->GetInstanceId()])
return 1.0f;
if (dynamic_cast<ReachTargetAction*>(action) ||
dynamic_cast<TankFaceAction*>(action) ||
dynamic_cast<CastKillingSpreeAction*>(action) ||
dynamic_cast<CastDisengageAction*>(action) ||
dynamic_cast<CastBlinkBackAction*>(action))
return 0.0f;
if (botAI->IsDps(bot) &&
dynamic_cast<CastReachTargetSpellAction*>(action))
return 0.0f;
return 1.0f;
}
float AlarDisableDisperseMultiplier::GetValue(Action* action)
{
if (!AI_VALUE2(Unit*, "find target", "al'ar"))
return 1.0f;
if (dynamic_cast<CombatFormationMoveAction*>(action) &&
!dynamic_cast<TankFaceAction*>(action) &&
!dynamic_cast<SetBehindTargetAction*>(action))
return 0.0f;
if (dynamic_cast<FollowAction*>(action) ||
dynamic_cast<FleeAction*>(action))
return 0.0f;
return 1.0f;
}
float AlarDisableTankAssistMultiplier::GetValue(Action* action)
{
if (bot->GetVictim() == nullptr)
return 1.0f;
if (!botAI->IsTank(bot))
return 1.0f;
if (!AI_VALUE2(Unit*, "find target", "al'ar"))
return 1.0f;
if (dynamic_cast<TankAssistAction*>(action))
return 0.0f;
return 1.0f;
}
float AlarStayAwayFromRebirthMultiplier::GetValue(Action* action)
{
Unit* alar = AI_VALUE2(Unit*, "find target", "al'ar");
if (!alar)
return 1.0f;
Creature* alarCreature = alar->ToCreature();
if (!alarCreature || alarCreature->GetReactState() != REACT_PASSIVE)
return 1.0f;
if (dynamic_cast<MovementAction*>(action) &&
!dynamic_cast<AlarMoveAwayFromRebirthAction*>(action) &&
!dynamic_cast<AlarAvoidFlamePatchesAndDiveBombsAction*>(action))
return 0.0f;
return 1.0f;
}
float AlarPhase2NoTankingIfArmorMeltedMultiplier::GetValue(Action* action)
{
if (!bot->HasAura(SPELL_MELT_ARMOR))
return 1.0f;
Unit* alar = AI_VALUE2(Unit*, "find target", "al'ar");
if (!alar || bot->GetTarget() != alar->GetGUID())
return 1.0f;
if (dynamic_cast<CastTauntAction*>(action) ||
dynamic_cast<CastGrowlAction*>(action) ||
dynamic_cast<CastHandOfReckoningAction*>(action) ||
dynamic_cast<CastDarkCommandAction*>(action))
return 0.0f;
return 1.0f;
}
// Void Reaver
float VoidReaverMaintainPositionsMultiplier::GetValue(Action* action)
{
if (!AI_VALUE2(Unit*, "find target", "void reaver"))
return 1.0f;
if (dynamic_cast<CombatFormationMoveAction*>(action) &&
!dynamic_cast<SetBehindTargetAction*>(action))
return 0.0f;
return 1.0f;
}
// High Astromancer Solarian
float HighAstromancerSolarianMaintainPositionMultiplier::GetValue(Action* action)
{
Unit* astromancer = AI_VALUE2(Unit*, "find target", "high astromancer solarian");
if (!astromancer || astromancer->HasAura(SPELL_SOLARIAN_TRANSFORM))
return 1.0f;
if (botAI->IsRanged(bot) &&
(dynamic_cast<CombatFormationMoveAction*>(action) ||
dynamic_cast<FleeAction*>(action) ||
dynamic_cast<CastBlinkBackAction*>(action) ||
dynamic_cast<CastDisengageAction*>(action)))
return 0.0f;
if (!bot->HasAura(SPELL_WRATH_OF_THE_ASTROMANCER))
return 1.0f;
if (dynamic_cast<CastReachTargetSpellAction*>(action) ||
(dynamic_cast<MovementAction*>(action) &&
!dynamic_cast<HighAstromancerSolarianMoveAwayFromGroupAction*>(action)))
return 0.0f;
return 1.0f;
}
float HighAstromancerSolarianDisableTankAssistMultiplier::GetValue(Action* action)
{
if (bot->GetVictim() == nullptr)
return 1.0f;
if (!botAI->IsTank(bot))
return 1.0f;
if (!AI_VALUE2(Unit*, "find target", "solarium priest"))
return 1.0f;
if (dynamic_cast<TankAssistAction*>(action))
return 0.0f;
return 1.0f;
}
// Kael'thas Sunstrider <Lord of the Blood Elves>
float KaelthasSunstriderWaitForDpsMultiplier::GetValue(Action* action)
{
Unit* kaelthas = AI_VALUE2(Unit*, "find target", "kael'thas sunstrider");
if (!kaelthas)
return 1.0f;
boss_kaelthas* kaelAI = dynamic_cast<boss_kaelthas*>(kaelthas->GetAI());
if (!kaelAI || kaelAI->GetPhase() != PHASE_SINGLE_ADVISOR)
return 1.0f;
if (dynamic_cast<KaelthasSunstriderMisdirectAdvisorsToTanksAction*>(action))
return 1.0f;
const time_t now = std::time(nullptr);
constexpr uint8 dpsWaitSeconds = 10;
auto it = advisorDpsWaitTimer.find(kaelthas->GetMap()->GetInstanceId());
if (it == advisorDpsWaitTimer.end() || (now - it->second) < dpsWaitSeconds)
{
Unit* sanguinar = AI_VALUE2(Unit*, "find target", "lord sanguinar");
Unit* capernian = AI_VALUE2(Unit*, "find target", "grand astromancer capernian");
Unit* telonicus = AI_VALUE2(Unit*, "find target", "master engineer telonicus");
auto isAdvisorActive = [](Unit* advisor)
{
return advisor && !advisor->HasUnitFlag(UNIT_FLAG_NON_ATTACKABLE) &&
!advisor->HasAura(SPELL_PERMANENT_FEIGN_DEATH);
};
if ((isAdvisorActive(sanguinar) && botAI->IsMainTank(bot)) ||
(isAdvisorActive(telonicus) && botAI->IsAssistTankOfIndex(bot, 0, true)) ||
(isAdvisorActive(capernian) && (botAI->IsMainTank(bot) || GetCapernianTank(bot) == bot)))
return 1.0f;
bool shouldHoldDps =
(isAdvisorActive(sanguinar) && !botAI->IsMainTank(bot)) ||
(isAdvisorActive(telonicus) && !botAI->IsAssistTankOfIndex(bot, 0, true)) ||
(isAdvisorActive(capernian) && !botAI->IsMainTank(bot) && GetCapernianTank(bot) != bot);
if (shouldHoldDps &&
(dynamic_cast<AttackAction*>(action) ||
(dynamic_cast<CastSpellAction*>(action) &&
!dynamic_cast<CastHealingSpellAction*>(action))))
return 0.0f;
}
return 1.0f;
}
float KaelthasSunstriderKiteThaladredMultiplier::GetValue(Action* action)
{
Unit* kaelthas = AI_VALUE2(Unit*, "find target", "kael'thas sunstrider");
if (!kaelthas)
return 1.0f;
boss_kaelthas* kaelAI = dynamic_cast<boss_kaelthas*>(kaelthas->GetAI());
if (!kaelAI)
return 1.0f;
if (botAI->IsTank(bot) && kaelAI->GetPhase() == PHASE_ALL_ADVISORS)
return 1.0f;
Unit* thaladred = AI_VALUE2(Unit*, "find target", "thaladred the darkener");
if (!thaladred || thaladred->GetVictim() != bot ||
thaladred->HasAura(SPELL_PERMANENT_FEIGN_DEATH))
return 1.0f;
if (dynamic_cast<MovementAction*>(action) &&
!dynamic_cast<KaelthasSunstriderKiteThaladredAction*>(action))
return 0.0f;
return 1.0f;
}
float KaelthasSunstriderControlMisdirectionMultiplier::GetValue(Action* action)
{
if (bot->getClass() != CLASS_HUNTER)
return 1.0f;
Unit* kaelthas = AI_VALUE2(Unit*, "find target", "kael'thas sunstrider");
if (!kaelthas)
return 1.0f;
boss_kaelthas* kaelAI = dynamic_cast<boss_kaelthas*>(kaelthas->GetAI());
if (!kaelAI || kaelAI->GetPhase() == PHASE_FINAL)
return 1.0f;
if (dynamic_cast<CastMisdirectionOnMainTankAction*>(action))
return 0.0f;
return 1.0f;
}
float KaelthasSunstriderKeepDistanceFromCapernianMultiplier::GetValue(Action* action)
{
Unit* kaelthas = AI_VALUE2(Unit*, "find target", "kael'thas sunstrider");
if (!kaelthas)
return 1.0f;
boss_kaelthas* kaelAI = dynamic_cast<boss_kaelthas*>(kaelthas->GetAI());
if (!kaelAI || kaelAI->GetPhase() != PHASE_SINGLE_ADVISOR)
return 1.0f;
Unit* capernian = AI_VALUE2(Unit*, "find target", "grand astromancer capernian");
if (!capernian || capernian->HasUnitFlag(UNIT_FLAG_NON_ATTACKABLE) ||
capernian->HasAura(SPELL_PERMANENT_FEIGN_DEATH))
return 1.0f;
if (dynamic_cast<MovementAction*>(action) &&
!dynamic_cast<AttackAction*>(action) &&
!dynamic_cast<KaelthasSunstriderSpreadAndMoveAwayFromCapernianAction*>(action))
return 0.0f;
return 1.0f;
}
float KaelthasSunstriderManageWeaponTankingMultiplier::GetValue(Action* action)
{
if (!botAI->IsTank(bot))
return 1.0f;
Unit* kaelthas = AI_VALUE2(Unit*, "find target", "kael'thas sunstrider");
if (!kaelthas)
return 1.0f;
boss_kaelthas* kaelAI = dynamic_cast<boss_kaelthas*>(kaelthas->GetAI());
if (!kaelAI)
return 1.0f;
if (kaelAI->GetPhase() != PHASE_WEAPONS &&
dynamic_cast<TankFaceAction*>(action))
return 0.0f;
if (!botAI->IsMainTank(bot))
return 1.0f;
// Try to keep main tank from grabbing aggro on any weapon other than the axe
if (kaelAI->GetPhase() == PHASE_WEAPONS &&
(dynamic_cast<TankAssistAction*>(action) ||
dynamic_cast<CastTauntAction*>(action) ||
dynamic_cast<CastChallengingShoutAction*>(action) ||
dynamic_cast<CastThunderClapAction*>(action) ||
dynamic_cast<CastShockwaveAction*>(action) ||
dynamic_cast<CastCleaveAction*>(action) ||
dynamic_cast<CastGrowlAction*>(action) ||
dynamic_cast<CastSwipeAction*>(action) ||
dynamic_cast<CastHandOfReckoningAction*>(action) ||
dynamic_cast<CastAvengersShieldAction*>(action) ||
dynamic_cast<CastConsecrationAction*>(action) ||
dynamic_cast<CastDarkCommandAction*>(action) ||
dynamic_cast<CastDeathAndDecayAction*>(action) ||
dynamic_cast<CastPestilenceAction*>(action) ||
dynamic_cast<CastBloodBoilAction*>(action)))
return 0.0f;
return 1.0f;
}
float KaelthasSunstriderDisableAdvisorTankAssistMultiplier::GetValue(Action* action)
{
if (bot->GetVictim() == nullptr || !botAI->IsTank(bot))
return 1.0f;
Unit* kaelthas = AI_VALUE2(Unit*, "find target", "kael'thas sunstrider");
if (!kaelthas)
return 1.0f;
boss_kaelthas* kaelAI = dynamic_cast<boss_kaelthas*>(kaelthas->GetAI());
if (!kaelAI)
return 1.0f;
if (kaelAI->GetPhase() != PHASE_SINGLE_ADVISOR &&
kaelAI->GetPhase() != PHASE_ALL_ADVISORS)
return 1.0f;
if (dynamic_cast<TankAssistAction*>(action))
return 0.0f;
return 1.0f;
}
float KaelthasSunstriderDisableDisperseMultiplier::GetValue(Action* action)
{
if (!AI_VALUE2(Unit*, "find target", "kael'thas sunstrider"))
return 1.0f;
if (dynamic_cast<CombatFormationMoveAction*>(action) &&
!dynamic_cast<TankFaceAction*>(action) &&
!dynamic_cast<SetBehindTargetAction*>(action))
return 0.0f;
return 1.0f;
}
// Bloodlust/Heroism and other major cooldowns should be used at the start of Phase 3
float KaelthasSunstriderDelayCooldownsMultiplier::GetValue(Action* action)
{
Unit* kaelthas = AI_VALUE2(Unit*, "find target", "kael'thas sunstrider");
if (!kaelthas)
return 1.0f;
boss_kaelthas* kaelAI = dynamic_cast<boss_kaelthas*>(kaelthas->GetAI());
if (!kaelAI || kaelAI->GetPhase() == PHASE_ALL_ADVISORS ||
kaelAI->GetPhase() == PHASE_FINAL)
return 1.0f;
if (bot->getClass() == CLASS_SHAMAN &&
(dynamic_cast<CastBloodlustAction*>(action) ||
dynamic_cast<CastHeroismAction*>(action)))
return 0.0f;
if (botAI->IsDps(bot) &&
(dynamic_cast<CastMetamorphosisAction*>(action) ||
dynamic_cast<CastAdrenalineRushAction*>(action) ||
dynamic_cast<CastBladeFlurryAction*>(action) ||
dynamic_cast<CastIcyVeinsAction*>(action) ||
dynamic_cast<CastColdSnapAction*>(action) ||
dynamic_cast<CastArcanePowerAction*>(action) ||
dynamic_cast<CastPresenceOfMindAction*>(action) ||
dynamic_cast<CastCombustionAction*>(action) ||
dynamic_cast<CastRapidFireAction*>(action) ||
dynamic_cast<CastReadinessAction*>(action) ||
dynamic_cast<CastAvengingWrathAction*>(action) ||
dynamic_cast<CastElementalMasteryAction*>(action) ||
dynamic_cast<CastFeralSpiritAction*>(action) ||
dynamic_cast<CastFireElementalTotemAction*>(action) ||
dynamic_cast<CastFireElementalTotemMeleeAction*>(action) ||
dynamic_cast<CastForceOfNatureAction*>(action) ||
dynamic_cast<CastArmyOfTheDeadAction*>(action) ||
dynamic_cast<CastSummonGargoyleAction*>(action) ||
dynamic_cast<CastBerserkingAction*>(action) ||
dynamic_cast<CastBloodFuryAction*>(action) ||
dynamic_cast<UseTrinketAction*>(action)))
return 0.0f;
return 1.0f;
}
float KaelthasSunstriderStaySpreadDuringGravityLapseMultiplier::GetValue(Action* action)
{
if (!bot->HasAura(SPELL_GRAVITY_LAPSE))
return 1.0f;
if (dynamic_cast<MovementAction*>(action) &&
!dynamic_cast<KaelthasSunstriderSpreadOutInMidairAction*>(action))
return 0.0f;
return 1.0f;
}

View File

@ -0,0 +1,150 @@
#ifndef _PLAYERBOT_RAIDTEMPESTKEEPMULTIPLIERS_H
#define _PLAYERBOT_RAIDTEMPESTKEEPMULTIPLIERS_H
#include "Multiplier.h"
// Al'ar <Phoenix God>
class AlarMoveBetweenPlatformsMultiplier : public Multiplier
{
public:
AlarMoveBetweenPlatformsMultiplier(
PlayerbotAI* botAI) : Multiplier(botAI, "al'ar move between platforms multiplier") {}
virtual float GetValue(Action* action);
};
class AlarDisableDisperseMultiplier : public Multiplier
{
public:
AlarDisableDisperseMultiplier(
PlayerbotAI* botAI) : Multiplier(botAI, "al'ar disable disperse multiplier") {}
virtual float GetValue(Action* action);
};
class AlarDisableTankAssistMultiplier : public Multiplier
{
public:
AlarDisableTankAssistMultiplier(
PlayerbotAI* botAI) : Multiplier(botAI, "al'ar disable tank assist multiplier") {}
virtual float GetValue(Action* action);
};
class AlarStayAwayFromRebirthMultiplier : public Multiplier
{
public:
AlarStayAwayFromRebirthMultiplier(
PlayerbotAI* botAI) : Multiplier(botAI, "al'ar stay away from rebirth multiplier") {}
virtual float GetValue(Action* action);
};
class AlarPhase2NoTankingIfArmorMeltedMultiplier : public Multiplier
{
public:
AlarPhase2NoTankingIfArmorMeltedMultiplier(
PlayerbotAI* botAI) : Multiplier(botAI, "al'ar phase 2 no tanking if armor melted multiplier") {}
virtual float GetValue(Action* action);
};
// Void Reaver
class VoidReaverMaintainPositionsMultiplier : public Multiplier
{
public:
VoidReaverMaintainPositionsMultiplier(
PlayerbotAI* botAI) : Multiplier(botAI, "void reaver maintain positions multiplier") {}
virtual float GetValue(Action* action);
};
// High Astromancer Solarian
class HighAstromancerSolarianDisableTankAssistMultiplier : public Multiplier
{
public:
HighAstromancerSolarianDisableTankAssistMultiplier(
PlayerbotAI* botAI) : Multiplier(botAI, "high astromancer solarian disable tank assist multiplier") {}
virtual float GetValue(Action* action);
};
class HighAstromancerSolarianMaintainPositionMultiplier : public Multiplier
{
public:
HighAstromancerSolarianMaintainPositionMultiplier(
PlayerbotAI* botAI) : Multiplier(botAI, "high astromancer solarian maintain position multiplier") {}
virtual float GetValue(Action* action);
};
// Kael'thas Sunstrider <Lord of the Blood Elves>
class KaelthasSunstriderWaitForDpsMultiplier : public Multiplier
{
public:
KaelthasSunstriderWaitForDpsMultiplier(
PlayerbotAI* botAI) : Multiplier(botAI, "kael'thas sunstrider wait for dps multiplier") {}
virtual float GetValue(Action* action);
};
class KaelthasSunstriderKiteThaladredMultiplier : public Multiplier
{
public:
KaelthasSunstriderKiteThaladredMultiplier(
PlayerbotAI* botAI) : Multiplier(botAI, "kael'thas sunstrider kite thaladred multiplier") {}
virtual float GetValue(Action* action);
};
class KaelthasSunstriderControlMisdirectionMultiplier : public Multiplier
{
public:
KaelthasSunstriderControlMisdirectionMultiplier(
PlayerbotAI* botAI) : Multiplier(botAI, "kael'thas sunstrider control misdirection multiplier") {}
virtual float GetValue(Action* action);
};
class KaelthasSunstriderKeepDistanceFromCapernianMultiplier : public Multiplier
{
public:
KaelthasSunstriderKeepDistanceFromCapernianMultiplier(
PlayerbotAI* botAI) : Multiplier(botAI, "kael'thas sunstrider keep distance from capernian multiplier") {}
virtual float GetValue(Action* action);
};
class KaelthasSunstriderManageWeaponTankingMultiplier : public Multiplier
{
public:
KaelthasSunstriderManageWeaponTankingMultiplier(
PlayerbotAI* botAI) : Multiplier(botAI, "kael'thas sunstrider manage weapon tanking multiplier") {}
virtual float GetValue(Action* action);
};
class KaelthasSunstriderDisableAdvisorTankAssistMultiplier : public Multiplier
{
public:
KaelthasSunstriderDisableAdvisorTankAssistMultiplier(
PlayerbotAI* botAI) : Multiplier(botAI, "kael'thas sunstrider disable advisor tank assist multiplier") {}
virtual float GetValue(Action* action);
};
class KaelthasSunstriderDisableDisperseMultiplier : public Multiplier
{
public:
KaelthasSunstriderDisableDisperseMultiplier(
PlayerbotAI* botAI) : Multiplier(botAI, "kael'thas sunstrider disable disperse multiplier") {}
virtual float GetValue(Action* action);
};
class KaelthasSunstriderDelayCooldownsMultiplier : public Multiplier
{
public:
KaelthasSunstriderDelayCooldownsMultiplier(
PlayerbotAI* botAI) : Multiplier(botAI, "kael'thas sunstrider delay cooldowns multiplier") {}
virtual float GetValue(Action* action);
};
class KaelthasSunstriderStaySpreadDuringGravityLapseMultiplier : public Multiplier
{
public:
KaelthasSunstriderStaySpreadDuringGravityLapseMultiplier(
PlayerbotAI* botAI) : Multiplier(botAI, "kael'thas sunstrider stay spread during gravity lapse multiplier") {}
virtual float GetValue(Action* action);
};
#endif

View File

@ -0,0 +1,289 @@
#ifndef _PLAYERBOT_RAIDTEMPESTKEEPACTIONCONTEXT_H
#define _PLAYERBOT_RAIDTEMPESTKEEPACTIONCONTEXT_H
#include "RaidTempestKeepActions.h"
#include "NamedObjectContext.h"
class RaidTempestKeepActionContext : public NamedObjectContext<Action>
{
public:
RaidTempestKeepActionContext()
{
// Trash
creators["crimson hand centurion cast polymorph"] =
&RaidTempestKeepActionContext::crimson_hand_centurion_cast_polymorph;
// Al'ar <Phoenix God>
creators["al'ar misdirect boss to main tank"] =
&RaidTempestKeepActionContext::alar_misdirect_boss_to_main_tank;
creators["al'ar boss tanks move between platforms"] =
&RaidTempestKeepActionContext::alar_boss_tanks_move_between_platforms;
creators["al'ar melee dps move between platforms"] =
&RaidTempestKeepActionContext::alar_melee_dps_move_between_platforms;
creators["al'ar ranged and ember tank move under platforms"] =
&RaidTempestKeepActionContext::alar_ranged_and_ember_tank_move_under_platforms;
creators["al'ar assist tanks pick up embers"] =
&RaidTempestKeepActionContext::alar_assist_tanks_pick_up_embers;
creators["al'ar ranged dps prioritize embers"] =
&RaidTempestKeepActionContext::alar_ranged_dps_prioritize_embers;
creators["al'ar jump from platform"] =
&RaidTempestKeepActionContext::alar_jump_from_platform;
creators["al'ar move away from rebirth"] =
&RaidTempestKeepActionContext::alar_move_away_from_rebirth;
creators["al'ar swap tanks on boss"] =
&RaidTempestKeepActionContext::alar_swap_tanks_on_boss;
creators["al'ar avoid flame patches and dive bombs"] =
&RaidTempestKeepActionContext::alar_avoid_flame_patches_and_dive_bombs;
creators["al'ar return to room center"] =
&RaidTempestKeepActionContext::alar_return_to_room_center;
creators["al'ar manage phase tracker"] =
&RaidTempestKeepActionContext::alar_manage_phase_tracker;
// Void Reaver
creators["void reaver tanks position boss"] =
&RaidTempestKeepActionContext::void_reaver_tanks_position_boss;
creators["void reaver use aggro dump ability"] =
&RaidTempestKeepActionContext::void_reaver_use_aggro_dump_ability;
creators["void reaver spread ranged"] =
&RaidTempestKeepActionContext::void_reaver_spread_ranged;
creators["void reaver avoid arcane orb"] =
&RaidTempestKeepActionContext::void_reaver_avoid_arcane_orb;
creators["void reaver erase trackers"] =
&RaidTempestKeepActionContext::void_reaver_erase_trackers;
// High Astromancer Solarian
creators["high astromancer solarian ranged leave space for melee"] =
&RaidTempestKeepActionContext::high_astromancer_solarian_ranged_leave_space_for_melee;
creators["high astromancer solarian move away from group"] =
&RaidTempestKeepActionContext::high_astromancer_solarian_move_away_from_group;
creators["high astromancer solarian stack for aoe"] =
&RaidTempestKeepActionContext::high_astromancer_solarian_stack_for_aoe;
creators["high astromancer solarian target solarium priests"] =
&RaidTempestKeepActionContext::high_astromancer_solarian_target_solarium_priests;
creators["high astromancer solarian cast fear ward on main tank"] =
&RaidTempestKeepActionContext::high_astromancer_solarian_cast_fear_ward_on_main_tank;
// Kael'thas Sunstrider <Lord of the Blood Elves>
creators["kael'thas sunstrider kite thaladred"] =
&RaidTempestKeepActionContext::kaelthas_sunstrider_kite_thaladred;
creators["kael'thas sunstrider misdirect advisors to tanks"] =
&RaidTempestKeepActionContext::kaelthas_sunstrider_misdirect_advisors_to_tanks;
creators["kael'thas sunstrider main tank position sanguinar"] =
&RaidTempestKeepActionContext::kaelthas_sunstrider_main_tank_position_sanguinar;
creators["kael'thas sunstrider cast fear ward on sanguinar tank"] =
&RaidTempestKeepActionContext::kaelthas_sunstrider_cast_fear_ward_on_sanguinar_tank;
creators["kael'thas sunstrider warlock tank position capernian"] =
&RaidTempestKeepActionContext::kaelthas_sunstrider_warlock_tank_position_capernian;
creators["kael'thas sunstrider spread and move away from capernian"] =
&RaidTempestKeepActionContext::kaelthas_sunstrider_spread_and_move_away_from_capernian;
creators["kael'thas sunstrider first assist tank position telonicus"] =
&RaidTempestKeepActionContext::kaelthas_sunstrider_first_assist_tank_position_telonicus;
creators["kael'thas sunstrider handle advisor roles in phase 3"] =
&RaidTempestKeepActionContext::kaelthas_sunstrider_handle_advisor_roles_in_phase_3;
creators["kael'thas sunstrider assign advisor dps priority"] =
&RaidTempestKeepActionContext::kaelthas_sunstrider_assign_advisor_dps_priority;
creators["kael'thas sunstrider manage advisor dps timer"] =
&RaidTempestKeepActionContext::kaelthas_sunstrider_manage_advisor_dps_timer;
creators["kael'thas sunstrider assign legendary weapon dps priority"] =
&RaidTempestKeepActionContext::kaelthas_sunstrider_assign_legendary_weapon_dps_priority;
creators["kael'thas sunstrider main tank move devastation away"] =
&RaidTempestKeepActionContext::kaelthas_sunstrider_main_tank_move_devastation_away;
creators["kael'thas sunstrider loot legendary weapons"] =
&RaidTempestKeepActionContext::kaelthas_sunstrider_loot_legendary_weapons;
creators["kael'thas sunstrider use legendary weapons"] =
&RaidTempestKeepActionContext::kaelthas_sunstrider_use_legendary_weapons;
creators["kael'thas sunstrider reequip gear"] =
&RaidTempestKeepActionContext::kaelthas_sunstrider_reequip_gear;
creators["kael'thas sunstrider main tank position boss"] =
&RaidTempestKeepActionContext::kaelthas_sunstrider_main_tank_position_boss;
creators["kael'thas sunstrider avoid flame strike"] =
&RaidTempestKeepActionContext::kaelthas_sunstrider_avoid_flame_strike;
creators["kael'thas sunstrider handle phoenixes and eggs"] =
&RaidTempestKeepActionContext::kaelthas_sunstrider_handle_phoenixes_and_eggs;
creators["kael'thas sunstrider break mind control"] =
&RaidTempestKeepActionContext::kaelthas_sunstrider_break_mind_control;
creators["kael'thas sunstrider break through shock barrier"] =
&RaidTempestKeepActionContext::kaelthas_sunstrider_break_through_shock_barrier;
creators["kael'thas sunstrider spread out in midair"] =
&RaidTempestKeepActionContext::kaelthas_sunstrider_spread_out_in_midair;
}
private:
// Trash
static Action* crimson_hand_centurion_cast_polymorph(
PlayerbotAI* botAI) { return new CrimsonHandCenturionCastPolymorphAction(botAI); }
// Al'ar <Phoenix God>
static Action* alar_misdirect_boss_to_main_tank(
PlayerbotAI* botAI) { return new AlarMisdirectBossToMainTankAction(botAI); }
static Action* alar_boss_tanks_move_between_platforms(
PlayerbotAI* botAI) { return new AlarBossTanksMoveBetweenPlatformsAction(botAI); }
static Action* alar_melee_dps_move_between_platforms(
PlayerbotAI* botAI) { return new AlarMeleeDpsMoveBetweenPlatformsAction(botAI); }
static Action* alar_ranged_and_ember_tank_move_under_platforms(
PlayerbotAI* botAI) { return new AlarRangedAndEmberTankMoveUnderPlatformsAction(botAI); }
static Action* alar_assist_tanks_pick_up_embers(
PlayerbotAI* botAI) { return new AlarAssistTanksPickUpEmbersAction(botAI); }
static Action* alar_ranged_dps_prioritize_embers(
PlayerbotAI* botAI) { return new AlarRangedDpsPrioritizeEmbersAction(botAI); }
static Action* alar_jump_from_platform(
PlayerbotAI* botAI) { return new AlarJumpFromPlatformAction(botAI); }
static Action* alar_move_away_from_rebirth(
PlayerbotAI* botAI) { return new AlarMoveAwayFromRebirthAction(botAI); }
static Action* alar_swap_tanks_on_boss(
PlayerbotAI* botAI) { return new AlarSwapTanksOnBossAction(botAI); }
static Action* alar_avoid_flame_patches_and_dive_bombs(
PlayerbotAI* botAI) { return new AlarAvoidFlamePatchesAndDiveBombsAction(botAI); }
static Action* alar_return_to_room_center(
PlayerbotAI* botAI) { return new AlarReturnToRoomCenterAction(botAI); }
static Action* alar_manage_phase_tracker(
PlayerbotAI* botAI) { return new AlarManagePhaseTrackerAction(botAI); }
// Void Reaver
static Action* void_reaver_tanks_position_boss(
PlayerbotAI* botAI) { return new VoidReaverTanksPositionBossAction(botAI); }
static Action* void_reaver_use_aggro_dump_ability(
PlayerbotAI* botAI) { return new VoidReaverUseAggroDumpAbilityAction(botAI); }
static Action* void_reaver_spread_ranged(
PlayerbotAI* botAI) { return new VoidReaverSpreadRangedAction(botAI); }
static Action* void_reaver_avoid_arcane_orb(
PlayerbotAI* botAI) { return new VoidReaverAvoidArcaneOrbAction(botAI); }
static Action* void_reaver_erase_trackers(
PlayerbotAI* botAI) { return new VoidReaverEraseTrackersAction(botAI); }
// High Astromancer Solarian
static Action* high_astromancer_solarian_ranged_leave_space_for_melee(
PlayerbotAI* botAI) { return new HighAstromancerSolarianRangedLeaveSpaceForMeleeAction(botAI); }
static Action* high_astromancer_solarian_move_away_from_group(
PlayerbotAI* botAI) { return new HighAstromancerSolarianMoveAwayFromGroupAction(botAI); }
static Action* high_astromancer_solarian_stack_for_aoe(
PlayerbotAI* botAI) { return new HighAstromancerSolarianStackForAoeAction(botAI); }
static Action* high_astromancer_solarian_target_solarium_priests(
PlayerbotAI* botAI) { return new HighAstromancerSolarianTargetSolariumPriestsAction(botAI); }
static Action* high_astromancer_solarian_cast_fear_ward_on_main_tank(
PlayerbotAI* botAI) { return new HighAstromancerSolarianCastFearWardOnMainTankAction(botAI); }
// Kael'thas Sunstrider <Lord of the Blood Elves>
static Action* kaelthas_sunstrider_kite_thaladred(
PlayerbotAI* botAI) { return new KaelthasSunstriderKiteThaladredAction(botAI); }
static Action* kaelthas_sunstrider_misdirect_advisors_to_tanks(
PlayerbotAI* botAI) { return new KaelthasSunstriderMisdirectAdvisorsToTanksAction(botAI); }
static Action* kaelthas_sunstrider_main_tank_position_sanguinar(
PlayerbotAI* botAI) { return new KaelthasSunstriderMainTankPositionSanguinarAction(botAI); }
static Action* kaelthas_sunstrider_cast_fear_ward_on_sanguinar_tank(
PlayerbotAI* botAI) { return new KaelthasSunstriderCastFearWardOnSanguinarTankAction(botAI); }
static Action* kaelthas_sunstrider_warlock_tank_position_capernian(
PlayerbotAI* botAI) { return new KaelthasSunstriderWarlockTankPositionCapernianAction(botAI); }
static Action* kaelthas_sunstrider_spread_and_move_away_from_capernian(
PlayerbotAI* botAI) { return new KaelthasSunstriderSpreadAndMoveAwayFromCapernianAction(botAI); }
static Action* kaelthas_sunstrider_first_assist_tank_position_telonicus(
PlayerbotAI* botAI) { return new KaelthasSunstriderFirstAssistTankPositionTelonicusAction(botAI); }
static Action* kaelthas_sunstrider_handle_advisor_roles_in_phase_3(
PlayerbotAI* botAI) { return new KaelthasSunstriderHandleAdvisorRolesInPhase3Action(botAI); }
static Action* kaelthas_sunstrider_assign_advisor_dps_priority(
PlayerbotAI* botAI) { return new KaelthasSunstriderAssignAdvisorDpsPriorityAction(botAI); }
static Action* kaelthas_sunstrider_manage_advisor_dps_timer(
PlayerbotAI* botAI) { return new KaelthasSunstriderManageAdvisorDpsTimerAction(botAI); }
static Action* kaelthas_sunstrider_assign_legendary_weapon_dps_priority(
PlayerbotAI* botAI) { return new KaelthasSunstriderAssignLegendaryWeaponDpsPriorityAction(botAI); }
static Action* kaelthas_sunstrider_main_tank_move_devastation_away(
PlayerbotAI* botAI) { return new KaelthasSunstriderMoveDevastationAwayAction(botAI); }
static Action* kaelthas_sunstrider_loot_legendary_weapons(
PlayerbotAI* botAI) { return new KaelthasSunstriderLootLegendaryWeaponsAction(botAI); }
static Action* kaelthas_sunstrider_use_legendary_weapons(
PlayerbotAI* botAI) { return new KaelthasSunstriderUseLegendaryWeaponsAction(botAI); }
static Action* kaelthas_sunstrider_reequip_gear(
PlayerbotAI* botAI) { return new KaelthasSunstriderReequipGearAction(botAI); }
static Action* kaelthas_sunstrider_main_tank_position_boss(
PlayerbotAI* botAI) { return new KaelthasSunstriderMainTankPositionBossAction(botAI); }
static Action* kaelthas_sunstrider_avoid_flame_strike(
PlayerbotAI* botAI) { return new KaelthasSunstriderAvoidFlameStrikeAction(botAI); }
static Action* kaelthas_sunstrider_handle_phoenixes_and_eggs(
PlayerbotAI* botAI) { return new KaelthasSunstriderHandlePhoenixesAndEggsAction(botAI); }
static Action* kaelthas_sunstrider_break_mind_control(
PlayerbotAI* botAI) { return new KaelthasSunstriderBreakMindControlAction(botAI); }
static Action* kaelthas_sunstrider_break_through_shock_barrier(
PlayerbotAI* botAI) { return new KaelthasSunstriderBreakThroughShockBarrierAction(botAI); }
static Action* kaelthas_sunstrider_spread_out_in_midair(
PlayerbotAI* botAI) { return new KaelthasSunstriderSpreadOutInMidairAction(botAI); }
};
#endif

View File

@ -0,0 +1,265 @@
#ifndef _PLAYERBOT_RAIDTEMPESTKEEPTRIGGERCONTEXT_H
#define _PLAYERBOT_RAIDTEMPESTKEEPTRIGGERCONTEXT_H
#include "RaidTempestKeepTriggers.h"
#include "AiObjectContext.h"
class RaidTempestKeepTriggerContext : public NamedObjectContext<Trigger>
{
public:
RaidTempestKeepTriggerContext()
{
// Trash
creators["crimson hand centurion casts arcane volley"] =
&RaidTempestKeepTriggerContext::crimson_hand_centurion_casts_arcane_volley;
// Al'ar <Phoenix God>
creators["al'ar pulling boss"] =
&RaidTempestKeepTriggerContext::alar_pulling_boss;
creators["al'ar boss is flying between platforms"] =
&RaidTempestKeepTriggerContext::alar_boss_is_flying_between_platforms;
creators["al'ar embers of al'ar explode upon death"] =
&RaidTempestKeepTriggerContext::alar_embers_of_alar_explode_upon_death;
creators["al'ar killing embers of al'ar damages boss"] =
&RaidTempestKeepTriggerContext::alar_killing_embers_of_alar_damages_boss;
creators["al'ar incoming flame quills"] =
&RaidTempestKeepTriggerContext::alar_incoming_flame_quills;
creators["al'ar rising from the ashes"] =
&RaidTempestKeepTriggerContext::alar_rising_from_the_ashes;
creators["al'ar everything is on fire in phase 2"] =
&RaidTempestKeepTriggerContext::alar_everything_is_on_fire_in_phase_2;
creators["al'ar phase 2 encounter is at room center"] =
&RaidTempestKeepTriggerContext::alar_phase_2_encounter_is_at_room_center;
creators["al'ar strategy changes between phases"] =
&RaidTempestKeepTriggerContext::alar_strategy_changes_between_phases;
// Void Reaver
creators["void reaver boss casts pounding"] =
&RaidTempestKeepTriggerContext::void_reaver_boss_casts_pounding;
creators["void reaver knock away reduces tank aggro"] =
&RaidTempestKeepTriggerContext::void_reaver_knock_away_reduces_tank_aggro;
creators["void reaver boss launches arcane orbs"] =
&RaidTempestKeepTriggerContext::void_reaver_boss_launches_arcane_orbs;
creators["void reaver arcane orb is incoming"] =
&RaidTempestKeepTriggerContext::void_reaver_arcane_orb_is_incoming;
creators["void reaver bot is not in combat"] =
&RaidTempestKeepTriggerContext::void_reaver_bot_is_not_in_combat;
// High Astromancer Solarian
creators["high astromancer solarian boss casts wrath of the astromancer"] =
&RaidTempestKeepTriggerContext::high_astromancer_solarian_boss_casts_wrath_of_the_astromancer;
creators["high astromancer solarian bot has wrath of the astromancer"] =
&RaidTempestKeepTriggerContext::high_astromancer_solarian_bot_has_wrath_of_the_astromancer;
creators["high astromancer solarian boss has vanished"] =
&RaidTempestKeepTriggerContext::high_astromancer_solarian_boss_has_vanished;
creators["high astromancer solarian solarium priests spawned"] =
&RaidTempestKeepTriggerContext::high_astromancer_solarian_solarium_priests_spawned;
creators["high astromancer solarian boss casts psychic scream"] =
&RaidTempestKeepTriggerContext::high_astromancer_solarian_boss_casts_psychic_scream;
// Kael'thas Sunstrider <Lord of the Blood Elves>
creators["kael'thas sunstrider thaladred is fixated on bot"] =
&RaidTempestKeepTriggerContext::kaelthas_sunstrider_thaladred_is_fixated_on_bot;
creators["kael'thas sunstrider pulling tankable advisors"] =
&RaidTempestKeepTriggerContext::kaelthas_sunstrider_pulling_tankable_advisors;
creators["kael'thas sunstrider sanguinar engaged by main tank"] =
&RaidTempestKeepTriggerContext::kaelthas_sunstrider_sanguinar_engaged_by_main_tank;
creators["kael'thas sunstrider sanguinar casts bellowing roar"] =
&RaidTempestKeepTriggerContext::kaelthas_sunstrider_sanguinar_casts_bellowing_roar;
creators["kael'thas sunstrider capernian should be tanked by a warlock"] =
&RaidTempestKeepTriggerContext::kaelthas_sunstrider_capernian_should_be_tanked_by_a_warlock;
creators["kael'thas sunstrider capernian casts arcane burst and conflagration"] =
&RaidTempestKeepTriggerContext::kaelthas_sunstrider_capernian_casts_arcane_burst_and_conflagration;
creators["kael'thas sunstrider telonicus engaged by first assist tank"] =
&RaidTempestKeepTriggerContext::kaelthas_sunstrider_telonicus_engaged_by_first_assist_tank;
creators["kael'thas sunstrider bots have specific roles in phase 3"] =
&RaidTempestKeepTriggerContext::kaelthas_sunstrider_bots_have_specific_roles_in_phase_3;
creators["kael'thas sunstrider determining advisor kill order"] =
&RaidTempestKeepTriggerContext::kaelthas_sunstrider_determining_advisor_kill_order;
creators["kael'thas sunstrider waiting for tanks to get aggro on advisors"] =
&RaidTempestKeepTriggerContext::kaelthas_sunstrider_waiting_for_tanks_to_get_aggro_on_advisors;
creators["kael'thas sunstrider legendary weapons are alive"] =
&RaidTempestKeepTriggerContext::kaelthas_sunstrider_legendary_weapons_are_alive;
creators["kael'thas sunstrider legendary axe casts whirlwind"] =
&RaidTempestKeepTriggerContext::kaelthas_sunstrider_legendary_axe_casts_whirlwind;
creators["kael'thas sunstrider legendary weapons are dead and lootable"] =
&RaidTempestKeepTriggerContext::kaelthas_sunstrider_legendary_weapons_are_dead_and_lootable;
creators["kael'thas sunstrider legendary weapons are equipped"] =
&RaidTempestKeepTriggerContext::kaelthas_sunstrider_legendary_weapons_are_equipped;
creators["kael'thas sunstrider legendary weapons were lost"] =
&RaidTempestKeepTriggerContext::kaelthas_sunstrider_legendary_weapons_were_lost;
creators["kael'thas sunstrider boss has entered the fight"] =
&RaidTempestKeepTriggerContext::kaelthas_sunstrider_boss_has_entered_the_fight;
creators["kael'thas sunstrider raid member is mind controlled"] =
&RaidTempestKeepTriggerContext::kaelthas_sunstrider_raid_member_is_mind_controlled;
creators["kael'thas sunstrider phoenixes and eggs are spawning"] =
&RaidTempestKeepTriggerContext::kaelthas_sunstrider_phoenixes_and_eggs_are_spawning;
creators["kael'thas sunstrider boss is casting pyroblast"] =
&RaidTempestKeepTriggerContext::kaelthas_sunstrider_boss_is_casting_pyroblast;
creators["kael'thas sunstrider boss is manipulating gravity"] =
&RaidTempestKeepTriggerContext::kaelthas_sunstrider_boss_is_manipulating_gravity;
}
private:
// Trash
static Trigger* crimson_hand_centurion_casts_arcane_volley(
PlayerbotAI* botAI) { return new CrimsonHandCenturionCastsArcaneVolleyTrigger(botAI); }
// Al'ar <Phoenix God>
static Trigger* alar_pulling_boss(
PlayerbotAI* botAI) { return new AlarPullingBossTrigger(botAI); }
static Trigger* alar_boss_is_flying_between_platforms(
PlayerbotAI* botAI) { return new AlarBossIsFlyingBetweenPlatformsTrigger(botAI); }
static Trigger* alar_embers_of_alar_explode_upon_death(
PlayerbotAI* botAI) { return new AlarEmbersOfAlarExplodeUponDeathTrigger(botAI); }
static Trigger* alar_killing_embers_of_alar_damages_boss(
PlayerbotAI* botAI) { return new AlarKillingEmbersOfAlarDamagesBossTrigger(botAI); }
static Trigger* alar_incoming_flame_quills(
PlayerbotAI* botAI) { return new AlarIncomingFlameQuillsTrigger(botAI); }
static Trigger* alar_rising_from_the_ashes(
PlayerbotAI* botAI) { return new AlarRisingFromTheAshesTrigger(botAI); }
static Trigger* alar_everything_is_on_fire_in_phase_2(
PlayerbotAI* botAI) { return new AlarEverythingIsOnFireInPhase2Trigger(botAI); }
static Trigger* alar_phase_2_encounter_is_at_room_center(
PlayerbotAI* botAI) { return new AlarPhase2EncounterIsAtRoomCenterTrigger(botAI); }
static Trigger* alar_strategy_changes_between_phases(
PlayerbotAI* botAI) { return new AlarStrategyChangesBetweenPhasesTrigger(botAI); }
// Void Reaver
static Trigger* void_reaver_boss_casts_pounding(
PlayerbotAI* botAI) { return new VoidReaverBossCastsPoundingTrigger(botAI); }
static Trigger* void_reaver_knock_away_reduces_tank_aggro(
PlayerbotAI* botAI) { return new VoidReaverKnockAwayReducesTankAggroTrigger(botAI); }
static Trigger* void_reaver_boss_launches_arcane_orbs(
PlayerbotAI* botAI) { return new VoidReaverBossLaunchesArcaneOrbsTrigger(botAI); }
static Trigger* void_reaver_arcane_orb_is_incoming(
PlayerbotAI* botAI) { return new VoidReaverArcaneOrbIsIncomingTrigger(botAI); }
static Trigger* void_reaver_bot_is_not_in_combat(
PlayerbotAI* botAI) { return new VoidReaverBotIsNotInCombatTrigger(botAI); }
// High Astromancer Solarian
static Trigger* high_astromancer_solarian_boss_casts_wrath_of_the_astromancer(
PlayerbotAI* botAI) { return new HighAstromancerSolarianBossCastsWrathOfTheAstromancerTrigger(botAI); }
static Trigger* high_astromancer_solarian_bot_has_wrath_of_the_astromancer(
PlayerbotAI* botAI) { return new HighAstromancerSolarianBotHasWrathOfTheAstromancerTrigger(botAI); }
static Trigger* high_astromancer_solarian_boss_has_vanished(
PlayerbotAI* botAI) { return new HighAstromancerSolarianBossHasVanishedTrigger(botAI); }
static Trigger* high_astromancer_solarian_solarium_priests_spawned(
PlayerbotAI* botAI) { return new HighAstromancerSolarianSolariumPriestsSpawnedTrigger(botAI); }
static Trigger* high_astromancer_solarian_boss_casts_psychic_scream(
PlayerbotAI* botAI) { return new HighAstromancerSolarianBossCastsPsychicScreamTrigger(botAI); }
// Kael'thas Sunstrider <Lord of the Blood Elves>
static Trigger* kaelthas_sunstrider_thaladred_is_fixated_on_bot(
PlayerbotAI* botAI) { return new KaelthasSunstriderThaladredIsFixatedOnBotTrigger(botAI); }
static Trigger* kaelthas_sunstrider_pulling_tankable_advisors(
PlayerbotAI* botAI) { return new KaelthasSunstriderPullingTankableAdvisorsTrigger(botAI); }
static Trigger* kaelthas_sunstrider_sanguinar_engaged_by_main_tank(
PlayerbotAI* botAI) { return new KaelthasSunstriderSanguinarEngagedByMainTankTrigger(botAI); }
static Trigger* kaelthas_sunstrider_sanguinar_casts_bellowing_roar(
PlayerbotAI* botAI) { return new KaelthasSunstriderSanguinarCastsBellowingRoarTrigger(botAI); }
static Trigger* kaelthas_sunstrider_capernian_should_be_tanked_by_a_warlock(
PlayerbotAI* botAI) { return new KaelthasSunstriderCapernianShouldBeTankedByAWarlockTrigger(botAI); }
static Trigger* kaelthas_sunstrider_capernian_casts_arcane_burst_and_conflagration(
PlayerbotAI* botAI) { return new KaelthasSunstriderCapernianCastsArcaneBurstAndConflagrationTrigger(botAI); }
static Trigger* kaelthas_sunstrider_telonicus_engaged_by_first_assist_tank(
PlayerbotAI* botAI) { return new KaelthasSunstriderTelonicusEngagedByFirstAssistTankTrigger(botAI); }
static Trigger* kaelthas_sunstrider_bots_have_specific_roles_in_phase_3(
PlayerbotAI* botAI) { return new KaelthasSunstriderBotsHaveSpecificRolesInPhase3Trigger(botAI); }
static Trigger* kaelthas_sunstrider_determining_advisor_kill_order(
PlayerbotAI* botAI) { return new KaelthasSunstriderDeterminingAdvisorKillOrderTrigger(botAI); }
static Trigger* kaelthas_sunstrider_waiting_for_tanks_to_get_aggro_on_advisors(
PlayerbotAI* botAI) { return new KaelthasSunstriderWaitingForTanksToGetAggroOnAdvisorsTrigger(botAI); }
static Trigger* kaelthas_sunstrider_legendary_weapons_are_alive(
PlayerbotAI* botAI) { return new KaelthasSunstriderLegendaryWeaponsAreAliveTrigger(botAI); }
static Trigger* kaelthas_sunstrider_legendary_axe_casts_whirlwind(
PlayerbotAI* botAI) { return new KaelthasSunstriderLegendaryAxeCastsWhirlwindTrigger(botAI); }
static Trigger* kaelthas_sunstrider_legendary_weapons_are_dead_and_lootable(
PlayerbotAI* botAI) { return new KaelthasSunstriderLegendaryWeaponsAreDeadAndLootableTrigger(botAI); }
static Trigger* kaelthas_sunstrider_legendary_weapons_are_equipped(
PlayerbotAI* botAI) { return new KaelthasSunstriderLegendaryWeaponsAreEquippedTrigger(botAI); }
static Trigger* kaelthas_sunstrider_legendary_weapons_were_lost(
PlayerbotAI* botAI) { return new KaelthasSunstriderLegendaryWeaponsWereLostTrigger(botAI); }
static Trigger* kaelthas_sunstrider_boss_has_entered_the_fight(
PlayerbotAI* botAI) { return new KaelthasSunstriderBossHasEnteredTheFightTrigger(botAI); }
static Trigger* kaelthas_sunstrider_raid_member_is_mind_controlled(
PlayerbotAI* botAI) { return new KaelthasSunstriderRaidMemberIsMindControlledTrigger(botAI); }
static Trigger* kaelthas_sunstrider_phoenixes_and_eggs_are_spawning(
PlayerbotAI* botAI) { return new KaelthasSunstriderPhoenixesAndEggsAreSpawningTrigger(botAI); }
static Trigger* kaelthas_sunstrider_boss_is_casting_pyroblast(
PlayerbotAI* botAI) { return new KaelthasSunstriderBossIsCastingPyroblastTrigger(botAI); }
static Trigger* kaelthas_sunstrider_boss_is_manipulating_gravity(
PlayerbotAI* botAI) { return new KaelthasSunstriderBossIsManipulatingGravityTrigger(botAI); }
};
#endif

View File

@ -0,0 +1,162 @@
#include "RaidTempestKeepStrategy.h"
#include "RaidTempestKeepMultipliers.h"
void RaidTempestKeepStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
{
// Trash
triggers.push_back(new TriggerNode("crimson hand centurion casts arcane volley", {
NextAction("crimson hand centurion cast polymorph", ACTION_RAID + 1) }));
// Al'ar <Phoenix God>
triggers.push_back(new TriggerNode("al'ar pulling boss", {
NextAction("al'ar misdirect boss to main tank", ACTION_EMERGENCY + 1) }));
triggers.push_back(new TriggerNode("al'ar boss is flying between platforms", {
NextAction("al'ar boss tanks move between platforms", ACTION_RAID + 1),
NextAction("al'ar melee dps move between platforms", ACTION_RAID + 1),
NextAction("al'ar ranged and ember tank move under platforms", ACTION_RAID + 4) }));
triggers.push_back(new TriggerNode("al'ar embers of al'ar explode upon death", {
NextAction("al'ar assist tanks pick up embers", ACTION_RAID + 3) }));
triggers.push_back(new TriggerNode("al'ar killing embers of al'ar damages boss", {
NextAction("al'ar ranged dps prioritize embers", ACTION_RAID + 2) }));
triggers.push_back(new TriggerNode("al'ar incoming flame quills", {
NextAction("al'ar jump from platform", ACTION_EMERGENCY + 7) }));
triggers.push_back(new TriggerNode("al'ar rising from the ashes", {
NextAction("al'ar move away from rebirth", ACTION_EMERGENCY + 7) }));
triggers.push_back(new TriggerNode("al'ar everything is on fire in phase 2", {
NextAction("al'ar swap tanks on boss", ACTION_EMERGENCY + 2),
NextAction("al'ar avoid flame patches and dive bombs", ACTION_EMERGENCY + 1) }));
triggers.push_back(new TriggerNode("al'ar phase 2 encounter is at room center", {
NextAction("al'ar return to room center", ACTION_RAID + 1) }));
triggers.push_back(new TriggerNode("al'ar strategy changes between phases", {
NextAction("al'ar manage phase tracker", ACTION_EMERGENCY + 10) }));
// Void Reaver
triggers.push_back(new TriggerNode("void reaver boss casts pounding", {
NextAction("void reaver tanks position boss", ACTION_RAID + 1) }));
triggers.push_back(new TriggerNode("void reaver knock away reduces tank aggro", {
NextAction("void reaver use aggro dump ability", ACTION_EMERGENCY + 6) }));
triggers.push_back(new TriggerNode("void reaver boss launches arcane orbs", {
NextAction("void reaver spread ranged", ACTION_RAID + 1) }));
triggers.push_back(new TriggerNode("void reaver arcane orb is incoming", {
NextAction("void reaver avoid arcane orb", ACTION_EMERGENCY + 1) }));
triggers.push_back(new TriggerNode("void reaver bot is not in combat", {
NextAction("void reaver erase trackers", ACTION_EMERGENCY + 11) }));
// High Astromancer Solarian
triggers.push_back(new TriggerNode("high astromancer solarian boss casts wrath of the astromancer", {
NextAction("high astromancer solarian ranged leave space for melee", ACTION_RAID + 1) }));
triggers.push_back(new TriggerNode("high astromancer solarian bot has wrath of the astromancer", {
NextAction("high astromancer solarian move away from group", ACTION_EMERGENCY + 6) }));
triggers.push_back(new TriggerNode("high astromancer solarian boss has vanished", {
NextAction("high astromancer solarian stack for aoe", ACTION_RAID + 1) }));
triggers.push_back(new TriggerNode("high astromancer solarian solarium priests spawned", {
NextAction("high astromancer solarian target solarium priests", ACTION_RAID + 1) }));
triggers.push_back(new TriggerNode("high astromancer solarian boss casts psychic scream", {
NextAction("high astromancer solarian cast fear ward on main tank", ACTION_RAID + 2) }));
// Kael'thas Sunstrider <Lord of the Blood Elves>
triggers.push_back(new TriggerNode("kael'thas sunstrider thaladred is fixated on bot", {
NextAction("kael'thas sunstrider kite thaladred", ACTION_EMERGENCY + 6) }));
triggers.push_back(new TriggerNode("kael'thas sunstrider pulling tankable advisors", {
NextAction("kael'thas sunstrider misdirect advisors to tanks", ACTION_EMERGENCY + 2) }));
triggers.push_back(new TriggerNode("kael'thas sunstrider sanguinar engaged by main tank", {
NextAction("kael'thas sunstrider main tank position sanguinar", ACTION_RAID + 1) }));
triggers.push_back(new TriggerNode("kael'thas sunstrider sanguinar casts bellowing roar", {
NextAction("kael'thas sunstrider cast fear ward on sanguinar tank", ACTION_RAID + 2) }));
triggers.push_back(new TriggerNode("kael'thas sunstrider capernian should be tanked by a warlock", {
NextAction("kael'thas sunstrider warlock tank position capernian", ACTION_RAID + 1) }));
triggers.push_back(new TriggerNode("kael'thas sunstrider capernian casts arcane burst and conflagration", {
NextAction("kael'thas sunstrider spread and move away from capernian", ACTION_RAID + 3) }));
triggers.push_back(new TriggerNode("kael'thas sunstrider telonicus engaged by first assist tank", {
NextAction("kael'thas sunstrider first assist tank position telonicus", ACTION_RAID + 1) }));
triggers.push_back(new TriggerNode("kael'thas sunstrider bots have specific roles in phase 3", {
NextAction("kael'thas sunstrider handle advisor roles in phase 3", ACTION_RAID + 2) }));
triggers.push_back(new TriggerNode("kael'thas sunstrider determining advisor kill order", {
NextAction("kael'thas sunstrider assign advisor dps priority", ACTION_RAID + 1) }));
triggers.push_back(new TriggerNode("kael'thas sunstrider waiting for tanks to get aggro on advisors", {
NextAction("kael'thas sunstrider manage advisor dps timer", ACTION_EMERGENCY + 10) }));
triggers.push_back(new TriggerNode("kael'thas sunstrider legendary weapons are alive", {
NextAction("kael'thas sunstrider assign legendary weapon dps priority", ACTION_RAID + 1) }));
triggers.push_back(new TriggerNode("kael'thas sunstrider legendary axe casts whirlwind", {
NextAction("kael'thas sunstrider main tank move devastation away", ACTION_EMERGENCY + 1) }));
triggers.push_back(new TriggerNode("kael'thas sunstrider legendary weapons are dead and lootable", {
NextAction("kael'thas sunstrider loot legendary weapons", ACTION_RAID) }));
triggers.push_back(new TriggerNode("kael'thas sunstrider legendary weapons are equipped", {
NextAction("kael'thas sunstrider use legendary weapons", ACTION_EMERGENCY + 6) }));
triggers.push_back(new TriggerNode("kael'thas sunstrider legendary weapons were lost", {
NextAction("kael'thas sunstrider reequip gear", ACTION_EMERGENCY + 11) }));
triggers.push_back(new TriggerNode("kael'thas sunstrider boss has entered the fight", {
NextAction("kael'thas sunstrider main tank position boss", ACTION_RAID + 1),
NextAction("kael'thas sunstrider avoid flame strike", ACTION_EMERGENCY + 8) }));
triggers.push_back(new TriggerNode("kael'thas sunstrider phoenixes and eggs are spawning", {
NextAction("kael'thas sunstrider handle phoenixes and eggs", ACTION_RAID + 1) }));
triggers.push_back(new TriggerNode("kael'thas sunstrider raid member is mind controlled", {
NextAction("kael'thas sunstrider break mind control", ACTION_EMERGENCY + 1) }));
triggers.push_back(new TriggerNode("kael'thas sunstrider boss is casting pyroblast", {
NextAction("kael'thas sunstrider break through shock barrier", ACTION_EMERGENCY + 7) }));
triggers.push_back(new TriggerNode("kael'thas sunstrider boss is manipulating gravity", {
NextAction("kael'thas sunstrider spread out in midair", ACTION_EMERGENCY + 1) }));
}
void RaidTempestKeepStrategy::InitMultipliers(std::vector<Multiplier*>& multipliers)
{
// Alar <Phoenix God>
multipliers.push_back(new AlarMoveBetweenPlatformsMultiplier(botAI));
multipliers.push_back(new AlarDisableDisperseMultiplier(botAI));
multipliers.push_back(new AlarDisableTankAssistMultiplier(botAI));
multipliers.push_back(new AlarStayAwayFromRebirthMultiplier(botAI));
multipliers.push_back(new AlarPhase2NoTankingIfArmorMeltedMultiplier(botAI));
// Void Reaver
multipliers.push_back(new VoidReaverMaintainPositionsMultiplier(botAI));
// High Astromancer Solarian
multipliers.push_back(new HighAstromancerSolarianDisableTankAssistMultiplier(botAI));
multipliers.push_back(new HighAstromancerSolarianMaintainPositionMultiplier(botAI));
// Kael'thas Sunstrider <Lord of the Blood Elves>
multipliers.push_back(new KaelthasSunstriderWaitForDpsMultiplier(botAI));
multipliers.push_back(new KaelthasSunstriderKiteThaladredMultiplier(botAI));
multipliers.push_back(new KaelthasSunstriderControlMisdirectionMultiplier(botAI));
multipliers.push_back(new KaelthasSunstriderKeepDistanceFromCapernianMultiplier(botAI));
multipliers.push_back(new KaelthasSunstriderManageWeaponTankingMultiplier(botAI));
multipliers.push_back(new KaelthasSunstriderDisableAdvisorTankAssistMultiplier(botAI));
multipliers.push_back(new KaelthasSunstriderDisableDisperseMultiplier(botAI));
multipliers.push_back(new KaelthasSunstriderDelayCooldownsMultiplier(botAI));
multipliers.push_back(new KaelthasSunstriderStaySpreadDuringGravityLapseMultiplier(botAI));
}

View File

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

View File

@ -0,0 +1,527 @@
#include "RaidTempestKeepTriggers.h"
#include "RaidTempestKeepHelpers.h"
#include "RaidTempestKeepActions.h"
#include "RaidTempestKeepKaelthasBossAI.h"
#include "Playerbots.h"
#include "RaidBossHelpers.h"
using namespace TempestKeepHelpers;
// Trash
bool CrimsonHandCenturionCastsArcaneVolleyTrigger::IsActive()
{
return bot->getClass() == CLASS_MAGE &&
AI_VALUE2(Unit*, "find target", "crimson hand centurion");
}
// Al'ar <Phoenix God>
bool AlarPullingBossTrigger::IsActive()
{
if (bot->getClass() != CLASS_HUNTER)
return false;
Unit* alar = AI_VALUE2(Unit*, "find target", "al'ar");
return alar && alar->GetHealthPct() > 98.0f;
}
bool AlarBossIsFlyingBetweenPlatformsTrigger::IsActive()
{
Unit* alar = AI_VALUE2(Unit*, "find target", "al'ar");
if (!alar || !alar->IsVisible())
return false;
if (isAlarInPhase2[alar->GetMap()->GetInstanceId()])
return false;
int8 locationIndex = GetAlarCurrentLocationIndex(alar);
if (locationIndex == LOCATION_NONE)
{
Position dest;
locationIndex = GetAlarDestinationLocationIndex(alar, dest);
}
return locationIndex != POINT_QUILL_OR_DIVE_IDX &&
locationIndex != POINT_MIDDLE_IDX;
}
bool AlarEmbersOfAlarExplodeUponDeathTrigger::IsActive()
{
return botAI->IsTank(bot) && AI_VALUE2(Unit*, "find target", "ember of al'ar");
}
bool AlarKillingEmbersOfAlarDamagesBossTrigger::IsActive()
{
return botAI->IsRangedDps(bot) &&
AI_VALUE2(Unit*, "find target", "ember of al'ar");
}
bool AlarIncomingFlameQuillsTrigger::IsActive()
{
Unit* alar = AI_VALUE2(Unit*, "find target", "al'ar");
if (!alar || isAlarInPhase2[alar->GetMap()->GetInstanceId()])
return false;
Position dest;
return GetAlarCurrentLocationIndex(alar) == POINT_QUILL_OR_DIVE_IDX ||
GetAlarDestinationLocationIndex(alar, dest) == POINT_QUILL_OR_DIVE_IDX;
}
bool AlarRisingFromTheAshesTrigger::IsActive()
{
Unit* alar = AI_VALUE2(Unit*, "find target", "al'ar");
if (!alar)
return false;
if (isAlarInPhase2[alar->GetMap()->GetInstanceId()])
return false;
Creature* alarCreature = alar->ToCreature();
return alarCreature && alarCreature->GetReactState() == REACT_PASSIVE;
}
bool AlarEverythingIsOnFireInPhase2Trigger::IsActive()
{
Unit* alar = AI_VALUE2(Unit*, "find target", "al'ar");
return alar && isAlarInPhase2[alar->GetMap()->GetInstanceId()];
}
bool AlarPhase2EncounterIsAtRoomCenterTrigger::IsActive()
{
if (bot->GetVictim())
return false;
Unit* alar = AI_VALUE2(Unit*, "find target", "al'ar");
if (!alar || !isAlarInPhase2[alar->GetMap()->GetInstanceId()])
return false;
Creature* alarCreature = alar->ToCreature();
if (alarCreature && alarCreature->GetReactState() == REACT_PASSIVE)
return false;
Position dest;
return GetAlarCurrentLocationIndex(alar) != POINT_QUILL_OR_DIVE_IDX &&
GetAlarDestinationLocationIndex(alar, dest) != POINT_QUILL_OR_DIVE_IDX;
}
bool AlarStrategyChangesBetweenPhasesTrigger::IsActive()
{
return botAI->IsDps(bot) && IsMechanicTrackerBot(botAI, bot, TEMPEST_KEEP_MAP_ID) &&
AI_VALUE2(Unit*, "find target", "al'ar");
}
// Void Reaver
bool VoidReaverBossCastsPoundingTrigger::IsActive()
{
if (!botAI->IsTank(bot))
return false;
Unit* voidReaver = AI_VALUE2(Unit*, "find target", "void reaver");
return voidReaver && voidReaver->GetVictim() == bot;
}
bool VoidReaverKnockAwayReducesTankAggroTrigger::IsActive()
{
if (botAI->IsTank(bot))
return false;
if (bot->getClass() == CLASS_DEATH_KNIGHT ||
bot->getClass() == CLASS_DRUID ||
bot->getClass() == CLASS_SHAMAN ||
bot->getClass() == CLASS_WARRIOR)
return false;
Unit* voidReaver = AI_VALUE2(Unit*, "find target", "void reaver");
return voidReaver && voidReaver->GetVictim() == bot;
}
bool VoidReaverBossLaunchesArcaneOrbsTrigger::IsActive()
{
if (!botAI->IsRanged(bot))
return false;
Unit* voidReaver = AI_VALUE2(Unit*, "find target", "void reaver");
return voidReaver && voidReaver->GetVictim() != bot;
}
bool VoidReaverArcaneOrbIsIncomingTrigger::IsActive()
{
if (botAI->IsTank(bot))
return false;
Unit* voidReaver = AI_VALUE2(Unit*, "find target", "void reaver");
if (!voidReaver || voidReaver->GetVictim() == bot)
return false;
auto it = voidReaverArcaneOrbs.find(bot->GetMap()->GetInstanceId());
if (it == voidReaverArcaneOrbs.end() || it->second.empty())
return false;
uint32 currentTime = getMSTime();
constexpr uint32 orbDuration = 7000;
constexpr float safeDistance = 22.0f;
for (auto const& orb : it->second)
{
if (getMSTimeDiff(orb.castTime, currentTime) <= orbDuration &&
bot->GetExactDist2d(orb.destination.GetPositionX(),
orb.destination.GetPositionY()) < safeDistance)
return true;
}
return false;
}
bool VoidReaverBotIsNotInCombatTrigger::IsActive()
{
return !bot->IsInCombat();
}
// High Astromancer Solarian
bool HighAstromancerSolarianBossCastsWrathOfTheAstromancerTrigger::IsActive()
{
if (bot->HasAura(SPELL_WRATH_OF_THE_ASTROMANCER))
return false;
if (!botAI->IsRanged(bot))
return false;
Unit* astromancer = AI_VALUE2(Unit*, "find target", "high astromancer solarian");
return astromancer && !astromancer->HasAura(SPELL_SOLARIAN_TRANSFORM);
}
bool HighAstromancerSolarianBotHasWrathOfTheAstromancerTrigger::IsActive()
{
return bot->HasAura(SPELL_WRATH_OF_THE_ASTROMANCER);
}
bool HighAstromancerSolarianBossHasVanishedTrigger::IsActive()
{
if (bot->HasAura(SPELL_WRATH_OF_THE_ASTROMANCER))
return false;
Unit* astromancer = AI_VALUE2(Unit*, "find target", "high astromancer solarian");
if (!astromancer)
return false;
Creature* astromancerCreature = astromancer->ToCreature();
return astromancerCreature &&
astromancerCreature->GetReactState() == REACT_PASSIVE;
}
bool HighAstromancerSolarianSolariumPriestsSpawnedTrigger::IsActive()
{
return botAI->IsMelee(bot) && AI_VALUE2(Unit*, "find target", "solarium priest");
}
bool HighAstromancerSolarianBossCastsPsychicScreamTrigger::IsActive()
{
if (bot->getClass() != CLASS_PRIEST)
return false;
Unit* astromancer = AI_VALUE2(Unit*, "find target", "high astromancer solarian");
return astromancer && astromancer->HasAura(SPELL_SOLARIAN_TRANSFORM);
}
// Kael'thas Sunstrider <Lord of the Blood Elves>
bool KaelthasSunstriderThaladredIsFixatedOnBotTrigger::IsActive()
{
Unit* kaelthas = AI_VALUE2(Unit*, "find target", "kael'thas sunstrider");
if (!kaelthas)
return false;
Unit* thaladred = AI_VALUE2(Unit*, "find target", "thaladred the darkener");
if (!thaladred || thaladred->GetVictim() != bot)
return false;
boss_kaelthas* kaelAI = dynamic_cast<boss_kaelthas*>(kaelthas->GetAI());
if (!kaelAI)
return false;
return !(botAI->IsTank(bot) && kaelAI->GetPhase() == PHASE_ALL_ADVISORS);
}
bool KaelthasSunstriderPullingTankableAdvisorsTrigger::IsActive()
{
if (bot->getClass() != CLASS_HUNTER)
return false;
Unit* kaelthas = AI_VALUE2(Unit*, "find target", "kael'thas sunstrider");
if (!kaelthas)
return false;
boss_kaelthas* kaelAI = dynamic_cast<boss_kaelthas*>(kaelthas->GetAI());
return kaelAI && (kaelAI->GetPhase() == PHASE_SINGLE_ADVISOR ||
kaelAI->GetPhase() == PHASE_ALL_ADVISORS);
}
bool KaelthasSunstriderSanguinarEngagedByMainTankTrigger::IsActive()
{
if (!botAI->IsMainTank(bot))
return false;
Unit* sanguinar = AI_VALUE2(Unit*, "find target", "lord sanguinar");
return sanguinar && !sanguinar->HasUnitFlag(UNIT_FLAG_NON_ATTACKABLE) &&
!sanguinar->HasAura(SPELL_PERMANENT_FEIGN_DEATH);
}
bool KaelthasSunstriderSanguinarCastsBellowingRoarTrigger::IsActive()
{
if (bot->getClass() != CLASS_PRIEST)
return false;
Unit* kaelthas = AI_VALUE2(Unit*, "find target", "kael'thas sunstrider");
if (!kaelthas)
return false;
boss_kaelthas* kaelAI = dynamic_cast<boss_kaelthas*>(kaelthas->GetAI());
if (!kaelAI)
return false;
if (kaelAI->GetPhase() != PHASE_SINGLE_ADVISOR &&
kaelAI->GetPhase() != PHASE_TRANSITION &&
kaelAI->GetPhase() != PHASE_ALL_ADVISORS)
return false;
Player* mainTank = GetGroupMainTank(botAI, bot);
if (!mainTank || mainTank->HasAura(SPELL_FEAR_WARD))
return false;
return botAI->CanCastSpell("fear ward", mainTank);
}
bool KaelthasSunstriderCapernianShouldBeTankedByAWarlockTrigger::IsActive()
{
if (bot->getClass() != CLASS_WARLOCK)
return false;
Unit* capernian = AI_VALUE2(Unit*, "find target", "grand astromancer capernian");
if (!capernian || capernian->HasUnitFlag(UNIT_FLAG_NON_ATTACKABLE) ||
capernian->HasAura(SPELL_PERMANENT_FEIGN_DEATH))
return false;
return GetCapernianTank(bot) == bot;
}
bool KaelthasSunstriderCapernianCastsArcaneBurstAndConflagrationTrigger::IsActive()
{
Unit* capernian = AI_VALUE2(Unit*, "find target", "grand astromancer capernian");
if (!capernian || capernian->HasUnitFlag(UNIT_FLAG_NON_ATTACKABLE) ||
capernian->HasAura(SPELL_PERMANENT_FEIGN_DEATH))
return false;
return GetCapernianTank(bot) != bot;
}
bool KaelthasSunstriderTelonicusEngagedByFirstAssistTankTrigger::IsActive()
{
if (!botAI->IsAssistTankOfIndex(bot, 0, false))
return false;
Unit* telonicus = AI_VALUE2(Unit*, "find target", "master engineer telonicus");
return telonicus && !telonicus->HasUnitFlag(UNIT_FLAG_NON_ATTACKABLE) &&
!telonicus->HasAura(SPELL_PERMANENT_FEIGN_DEATH);
}
bool KaelthasSunstriderBotsHaveSpecificRolesInPhase3Trigger::IsActive()
{
Unit* kaelthas = AI_VALUE2(Unit*, "find target", "kael'thas sunstrider");
if (!kaelthas)
return false;
if (!AI_VALUE2(Unit*, "find target", "master engineer telonicus") &&
!AI_VALUE2(Unit*, "find target", "lord sanguinar"))
return false;
boss_kaelthas* kaelAI = dynamic_cast<boss_kaelthas*>(kaelthas->GetAI());
if (!kaelAI || kaelAI->GetPhase() != PHASE_ALL_ADVISORS)
return false;
return botAI->IsAssistHealOfIndex(bot, 0, true) ||
botAI->IsMainTank(bot) || botAI->IsAssistTankOfIndex(bot, 0, true) ||
(bot->getClass() == CLASS_WARLOCK && GetCapernianTank(bot) == bot);
}
bool KaelthasSunstriderDeterminingAdvisorKillOrderTrigger::IsActive()
{
if (botAI->IsHeal(bot) ||
botAI->IsMainTank(bot) ||
botAI->IsAssistTankOfIndex(bot, 0, true))
return false;
Unit* kaelthas = AI_VALUE2(Unit*, "find target", "kael'thas sunstrider");
if (!kaelthas)
return false;
boss_kaelthas* kaelAI = dynamic_cast<boss_kaelthas*>(kaelthas->GetAI());
return kaelAI && (kaelAI->GetPhase() == PHASE_SINGLE_ADVISOR ||
kaelAI->GetPhase() == PHASE_ALL_ADVISORS);
}
bool KaelthasSunstriderWaitingForTanksToGetAggroOnAdvisorsTrigger::IsActive()
{
if (!botAI->IsDps(bot))
return false;
Unit* kaelthas = AI_VALUE2(Unit*, "find target", "kael'thas sunstrider");
if (!kaelthas)
return false;
boss_kaelthas* kaelAI = dynamic_cast<boss_kaelthas*>(kaelthas->GetAI());
if (!kaelAI || kaelAI->GetPhase() != PHASE_SINGLE_ADVISOR)
return false;
return IsMechanicTrackerBot(botAI, bot, TEMPEST_KEEP_MAP_ID, GetCapernianTank(bot));
}
bool KaelthasSunstriderLegendaryWeaponsAreAliveTrigger::IsActive()
{
if (botAI->IsMainTank(bot))
return false;
Unit* kaelthas = AI_VALUE2(Unit*, "find target", "kael'thas sunstrider");
if (!kaelthas)
return false;
boss_kaelthas* kaelAI = dynamic_cast<boss_kaelthas*>(kaelthas->GetAI());
return kaelAI && kaelAI->GetPhase() == PHASE_WEAPONS;
}
bool KaelthasSunstriderLegendaryAxeCastsWhirlwindTrigger::IsActive()
{
return botAI->IsMainTank(bot) &&
AI_VALUE2(Unit*, "find target", "devastation");
}
bool KaelthasSunstriderLegendaryWeaponsAreDeadAndLootableTrigger::IsActive()
{
Unit* kaelthas = AI_VALUE2(Unit*, "find target", "kael'thas sunstrider");
if (!kaelthas)
return false;
boss_kaelthas* kaelAI = dynamic_cast<boss_kaelthas*>(kaelthas->GetAI());
if (!kaelAI ||
(kaelAI->GetPhase() != PHASE_WEAPONS && kaelAI->GetPhase() != PHASE_ALL_ADVISORS))
return false;
Unit* axe = AI_VALUE2(Unit*, "find target", "devastation");
if (axe && axe->GetVictim() == bot)
return false;
return IsAnyLegendaryWeaponDead(bot);
}
bool KaelthasSunstriderLegendaryWeaponsAreEquippedTrigger::IsActive()
{
if (!AI_VALUE2(Unit*, "find target", "kael'thas sunstrider"))
return false;
return bot->HasItemCount(ITEM_STAFF_OF_DISINTEGRATION, 1, false) ||
bot->HasItemCount(ITEM_NETHERSTRAND_LONGBOW, 1, false) ||
bot->HasItemCount(ITEM_PHASESHIFT_BULWARK, 1, false);
}
bool KaelthasSunstriderLegendaryWeaponsWereLostTrigger::IsActive()
{
if (bot->GetMapId() != TEMPEST_KEEP_MAP_ID)
return false;
Map* map = bot->GetMap();
if (!map)
return false;
constexpr uint32 KAELTHAS_DB_GUID = 158218;
auto const& creatureStore = map->GetCreatureBySpawnIdStore();
auto it = creatureStore.find(KAELTHAS_DB_GUID);
if (it == creatureStore.end())
return false;
Creature* kaelthas = it->second;
if (!kaelthas || bot->GetExactDist2d(kaelthas) > 150.0f)
return false;
const std::array<uint8, 3> weaponSlots =
{
EQUIPMENT_SLOT_MAINHAND,
EQUIPMENT_SLOT_OFFHAND,
EQUIPMENT_SLOT_RANGED
};
for (uint8 slot : weaponSlots)
{
if (!bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot) &&
HasEquippableItemForSlot(bot, slot))
return true;
}
return false;
}
bool KaelthasSunstriderBossHasEnteredTheFightTrigger::IsActive()
{
Unit* kaelthas = AI_VALUE2(Unit*, "find target", "kael'thas sunstrider");
if (!kaelthas)
return false;
boss_kaelthas* kaelAI = dynamic_cast<boss_kaelthas*>(kaelthas->GetAI());
return kaelAI && kaelAI->GetPhase() == PHASE_FINAL;
}
bool KaelthasSunstriderPhoenixesAndEggsAreSpawningTrigger::IsActive()
{
Unit* kaelthas = AI_VALUE2(Unit*, "find target", "kael'thas sunstrider");
if (!kaelthas)
return false;
if (botAI->IsTank(bot) && kaelthas->GetVictim() == bot)
return false;
return AI_VALUE2(Unit*, "find target", "phoenix") ||
AI_VALUE2(Unit*, "find target", "phoenix egg");
}
bool KaelthasSunstriderRaidMemberIsMindControlledTrigger::IsActive()
{
Unit* kaelthas = AI_VALUE2(Unit*, "find target", "kael'thas sunstrider");
if (!kaelthas)
return false;
if (botAI->IsTank(bot) && kaelthas->GetVictim() == bot)
return false;
if (!bot->HasItemCount(ITEM_INFINITY_BLADE, 1, true))
return false;
if (Group* group = bot->GetGroup())
{
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
{
Player* member = ref->GetSource();
if (!member || !member->IsAlive())
continue;
if (member->HasAura(SPELL_KAELTHAS_MIND_CONTROL))
return true;
}
}
return false;
}
bool KaelthasSunstriderBossIsCastingPyroblastTrigger::IsActive()
{
if (!botAI->IsDps(bot))
return false;
Unit* kaelthas = AI_VALUE2(Unit*, "find target", "kael'thas sunstrider");
return kaelthas && kaelthas->HasAura(SPELL_SHOCK_BARRIER);
}
bool KaelthasSunstriderBossIsManipulatingGravityTrigger::IsActive()
{
return bot->HasAura(SPELL_GRAVITY_LAPSE);
}

View File

@ -0,0 +1,338 @@
#ifndef _PLAYERBOT_RAIDTEMPESTKEEPTRIGGERS_H
#define _PLAYERBOT_RAIDTEMPESTKEEPTRIGGERS_H
#include "Trigger.h"
// General
// Trash
class CrimsonHandCenturionCastsArcaneVolleyTrigger : public Trigger
{
public:
CrimsonHandCenturionCastsArcaneVolleyTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "crimson hand centurion casts arcane volley") {}
bool IsActive() override;
};
// Al'ar <Phoenix God>
class AlarPullingBossTrigger : public Trigger
{
public:
AlarPullingBossTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "al'ar pulling boss") {}
bool IsActive() override;
};
class AlarBossIsFlyingBetweenPlatformsTrigger : public Trigger
{
public:
AlarBossIsFlyingBetweenPlatformsTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "al'ar boss is flying between platforms") {}
bool IsActive() override;
};
class AlarEmbersOfAlarExplodeUponDeathTrigger : public Trigger
{
public:
AlarEmbersOfAlarExplodeUponDeathTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "al'ar embers of al'ar explode upon death") {}
bool IsActive() override;
};
class AlarKillingEmbersOfAlarDamagesBossTrigger : public Trigger
{
public:
AlarKillingEmbersOfAlarDamagesBossTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "al'ar killing embers of al'ar damages boss") {}
bool IsActive() override;
};
class AlarIncomingFlameQuillsTrigger : public Trigger
{
public:
AlarIncomingFlameQuillsTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "al'ar incoming flame quills") {}
bool IsActive() override;
};
class AlarRisingFromTheAshesTrigger : public Trigger
{
public:
AlarRisingFromTheAshesTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "al'ar rising from the ashes") {}
bool IsActive() override;
};
class AlarEverythingIsOnFireInPhase2Trigger : public Trigger
{
public:
AlarEverythingIsOnFireInPhase2Trigger(
PlayerbotAI* botAI) : Trigger(botAI, "al'ar everything is on fire in phase 2") {}
bool IsActive() override;
};
class AlarPhase2EncounterIsAtRoomCenterTrigger : public Trigger
{
public:
AlarPhase2EncounterIsAtRoomCenterTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "al'ar phase 2 encounter is at room center") {}
bool IsActive() override;
};
class AlarStrategyChangesBetweenPhasesTrigger : public Trigger
{
public:
AlarStrategyChangesBetweenPhasesTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "al'ar strategy changes between phases") {}
bool IsActive() override;
};
// Void Reaver
class VoidReaverBossCastsPoundingTrigger : public Trigger
{
public:
VoidReaverBossCastsPoundingTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "void reaver boss casts pounding") {}
bool IsActive() override;
};
class VoidReaverKnockAwayReducesTankAggroTrigger : public Trigger
{
public:
VoidReaverKnockAwayReducesTankAggroTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "void reaver knock away reduces tank aggro") {}
bool IsActive() override;
};
class VoidReaverBossLaunchesArcaneOrbsTrigger : public Trigger
{
public:
VoidReaverBossLaunchesArcaneOrbsTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "void reaver boss launches arcane orbs") {}
bool IsActive() override;
};
class VoidReaverArcaneOrbIsIncomingTrigger : public Trigger
{
public:
VoidReaverArcaneOrbIsIncomingTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "void reaver arcane orb is incoming") {}
bool IsActive() override;
};
class VoidReaverBotIsNotInCombatTrigger : public Trigger
{
public:
VoidReaverBotIsNotInCombatTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "void reaver bot is not in combat") {}
bool IsActive() override;
};
// High Astromancer Solarian
class HighAstromancerSolarianBossCastsWrathOfTheAstromancerTrigger : public Trigger
{
public:
HighAstromancerSolarianBossCastsWrathOfTheAstromancerTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "high astromancer solarian boss casts wrath of the astromancer") {}
bool IsActive() override;
};
class HighAstromancerSolarianBotHasWrathOfTheAstromancerTrigger : public Trigger
{
public:
HighAstromancerSolarianBotHasWrathOfTheAstromancerTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "high astromancer solarian bot has wrath of the astromancer") {}
bool IsActive() override;
};
class HighAstromancerSolarianBossHasVanishedTrigger : public Trigger
{
public:
HighAstromancerSolarianBossHasVanishedTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "high astromancer solarian boss has vanished") {}
bool IsActive() override;
};
class HighAstromancerSolarianSolariumPriestsSpawnedTrigger : public Trigger
{
public:
HighAstromancerSolarianSolariumPriestsSpawnedTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "high astromancer solarian solarium priests spawned") {}
bool IsActive() override;
};
class HighAstromancerSolarianBossCastsPsychicScreamTrigger : public Trigger
{
public:
HighAstromancerSolarianBossCastsPsychicScreamTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "high astromancer boss casts psychic scream") {}
bool IsActive() override;
};
// Kael'thas Sunstrider <Lord of the Blood Elves>
class KaelthasSunstriderThaladredIsFixatedOnBotTrigger : public Trigger
{
public:
KaelthasSunstriderThaladredIsFixatedOnBotTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "kael'thas sunstrider thaladred is fixated on bot") {}
bool IsActive() override;
};
class KaelthasSunstriderPullingTankableAdvisorsTrigger : public Trigger
{
public:
KaelthasSunstriderPullingTankableAdvisorsTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "kael'thas sunstrider pulling tankable advisors") {}
bool IsActive() override;
};
class KaelthasSunstriderSanguinarEngagedByMainTankTrigger : public Trigger
{
public:
KaelthasSunstriderSanguinarEngagedByMainTankTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "kael'thas sunstrider sanguinar engaged by main tank") {}
bool IsActive() override;
};
class KaelthasSunstriderSanguinarCastsBellowingRoarTrigger : public Trigger
{
public:
KaelthasSunstriderSanguinarCastsBellowingRoarTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "kael'thas sunstrider sanguinar casts bellowing roar") {}
bool IsActive() override;
};
class KaelthasSunstriderCapernianShouldBeTankedByAWarlockTrigger : public Trigger
{
public:
KaelthasSunstriderCapernianShouldBeTankedByAWarlockTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "kael'thas sunstrider capernian should be tanked by a warlock") {}
bool IsActive() override;
};
class KaelthasSunstriderCapernianCastsArcaneBurstAndConflagrationTrigger : public Trigger
{
public:
KaelthasSunstriderCapernianCastsArcaneBurstAndConflagrationTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "kael'thas sunstrider capernian casts arcane burst and conflagration") {}
bool IsActive() override;
};
class KaelthasSunstriderTelonicusEngagedByFirstAssistTankTrigger : public Trigger
{
public:
KaelthasSunstriderTelonicusEngagedByFirstAssistTankTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "kael'thas sunstrider telonicus engaged by first assist tank") {}
bool IsActive() override;
};
class KaelthasSunstriderBotsHaveSpecificRolesInPhase3Trigger : public Trigger
{
public:
KaelthasSunstriderBotsHaveSpecificRolesInPhase3Trigger(
PlayerbotAI* botAI) : Trigger(botAI, "kael'thas sunstrider bots have specific roles in phase 3") {}
bool IsActive() override;
};
class KaelthasSunstriderDeterminingAdvisorKillOrderTrigger : public Trigger
{
public:
KaelthasSunstriderDeterminingAdvisorKillOrderTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "kael'thas sunstrider determining advisor kill order") {}
bool IsActive() override;
};
class KaelthasSunstriderWaitingForTanksToGetAggroOnAdvisorsTrigger : public Trigger
{
public:
KaelthasSunstriderWaitingForTanksToGetAggroOnAdvisorsTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "kael'thas sunstrider waiting for tanks to get aggro on advisors") {}
bool IsActive() override;
};
class KaelthasSunstriderLegendaryWeaponsAreAliveTrigger : public Trigger
{
public:
KaelthasSunstriderLegendaryWeaponsAreAliveTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "kael'thas sunstrider legendary weapons are alive") {}
bool IsActive() override;
};
class KaelthasSunstriderLegendaryAxeCastsWhirlwindTrigger : public Trigger
{
public:
KaelthasSunstriderLegendaryAxeCastsWhirlwindTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "kael'thas sunstrider legendary axe casts whirlwind") {}
bool IsActive() override;
};
class KaelthasSunstriderLegendaryWeaponsAreDeadAndLootableTrigger : public Trigger
{
public:
KaelthasSunstriderLegendaryWeaponsAreDeadAndLootableTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "kael'thas sunstrider legendary weapons are dead and lootable") {}
bool IsActive() override;
};
class KaelthasSunstriderLegendaryWeaponsAreEquippedTrigger : public Trigger
{
public:
KaelthasSunstriderLegendaryWeaponsAreEquippedTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "kael'thas sunstrider legendary weapons are equipped") {}
bool IsActive() override;
};
class KaelthasSunstriderLegendaryWeaponsWereLostTrigger : public Trigger
{
public:
KaelthasSunstriderLegendaryWeaponsWereLostTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "kael'thas sunstrider legendary weapons were lost") {}
bool IsActive() override;
};
class KaelthasSunstriderBossHasEnteredTheFightTrigger : public Trigger
{
public:
KaelthasSunstriderBossHasEnteredTheFightTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "kael'thas sunstrider boss has entered the fight") {}
bool IsActive() override;
};
class KaelthasSunstriderPhoenixesAndEggsAreSpawningTrigger : public Trigger
{
public:
KaelthasSunstriderPhoenixesAndEggsAreSpawningTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "kael'thas sunstrider phoenixes and eggs are spawning") {}
bool IsActive() override;
};
class KaelthasSunstriderRaidMemberIsMindControlledTrigger : public Trigger
{
public:
KaelthasSunstriderRaidMemberIsMindControlledTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "kael'thas sunstrider raid member is mind controlled") {}
bool IsActive() override;
};
class KaelthasSunstriderBossIsCastingPyroblastTrigger : public Trigger
{
public:
KaelthasSunstriderBossIsCastingPyroblastTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "kael'thas sunstrider boss is casting pyroblast") {}
bool IsActive() override;
};
class KaelthasSunstriderBossIsManipulatingGravityTrigger : public Trigger
{
public:
KaelthasSunstriderBossIsManipulatingGravityTrigger(
PlayerbotAI* botAI) : Trigger(botAI, "kael'thas sunstrider boss is manipulating gravity") {}
bool IsActive() override;
};
#endif

View File

@ -0,0 +1,425 @@
#include "RaidTempestKeepHelpers.h"
#include "RaidTempestKeepActions.h"
#include "LootObjectStack.h"
#include "Playerbots.h"
#include "RaidBossHelpers.h"
namespace TempestKeepHelpers
{
// General
Unit* GetNearestNonTankPlayerInRadius(PlayerbotAI* botAI, Player* bot, float radius)
{
Unit* nearestPlayer = nullptr;
float nearestDistance = radius;
Group* group = bot->GetGroup();
if (!group)
return nullptr;
for (GroupReference* ref = group->GetFirstMember(); ref != nullptr; ref = ref->next())
{
Player* member = ref->GetSource();
if (!member || !member->IsAlive() || member == bot || botAI->IsTank(member))
continue;
float distance = bot->GetExactDist2d(member);
if (distance < nearestDistance)
{
nearestDistance = distance;
nearestPlayer = member;
}
}
return nearestPlayer;
}
std::vector<Unit*> GetAllHazardTriggers(Player* bot, uint32 npcEntry, float searchRadius)
{
std::vector<Unit*> hazardTriggers;
std::list<Creature*> creatureList;
bot->GetCreatureListWithEntryInGrid(creatureList, npcEntry, searchRadius);
for (Creature* creature : creatureList)
{
if (creature && creature->IsAlive())
hazardTriggers.push_back(creature);
}
return hazardTriggers;
}
Position FindSafestNearbyPosition(Player* bot, const std::vector<Unit*>& hazards,
float hazardRadius, const Position* center)
{
constexpr float searchStep = M_PI / 8.0f;
constexpr float minDistance = 2.0f;
constexpr float maxDistance = 30.0f;
constexpr float distanceStep = 1.0f;
Position bestPos;
float minMoveDistance = std::numeric_limits<float>::max();
bool foundSafe = false;
for (float distance = minDistance; distance <= maxDistance; distance += distanceStep)
{
for (float angle = 0.0f; angle < 2 * M_PI; angle += searchStep)
{
const Position& searchCenter = center ? *center : bot->GetPosition();
float x = searchCenter.GetPositionX() + distance * std::cos(angle);
float y = searchCenter.GetPositionY() + distance * std::sin(angle);
bool isSafe = true;
for (Unit* hazard : hazards)
{
if (hazard->GetExactDist2d(x, y) < hazardRadius)
{
isSafe = false;
break;
}
}
if (!isSafe)
continue;
Position testPos(x, y, bot->GetPositionZ());
bool pathSafe = IsPathSafeFromHazards(bot->GetPosition(), testPos, hazards, hazardRadius);
if (pathSafe || !foundSafe)
{
float moveDistance = bot->GetExactDist2d(x, y);
if (pathSafe && (!foundSafe || moveDistance < minMoveDistance))
{
bestPos = testPos;
minMoveDistance = moveDistance;
foundSafe = true;
}
else if (!foundSafe && moveDistance < minMoveDistance)
{
bestPos = testPos;
minMoveDistance = moveDistance;
}
}
}
if (foundSafe)
break;
}
return bestPos;
}
bool IsPathSafeFromHazards(
const Position& start, const Position& end, const std::vector<Unit*>& hazards, float hazardRadius)
{
constexpr uint8 numChecks = 10;
float dx = end.GetPositionX() - start.GetPositionX();
float dy = end.GetPositionY() - start.GetPositionY();
for (uint8 i = 1; i <= numChecks; ++i)
{
float ratio = static_cast<float>(i) / numChecks;
float checkX = start.GetPositionX() + dx * ratio;
float checkY = start.GetPositionY() + dy * ratio;
for (Unit* hazard : hazards)
{
float distToHazard = hazard->GetExactDist2d(checkX, checkY);
if (distToHazard < hazardRadius)
return false;
}
}
return true;
}
// Al'ar <Phoenix God>
const Position ALAR_PLATFORM_0 = { 335.638f, 59.4879f, 17.9319f }; // West Platform
const Position ALAR_PLATFORM_1 = { 388.751f, 31.7312f, 20.2636f }; // Northwest Platform
const Position ALAR_PLATFORM_2 = { 388.791f, -33.1059f, 20.2636f }; // Northeast Platform
const Position ALAR_PLATFORM_3 = { 332.723f, -61.159f, 17.9791f }; // East Platform
const std::array<Position, 4> PLATFORM_POSITIONS =
{
ALAR_PLATFORM_0,
ALAR_PLATFORM_1,
ALAR_PLATFORM_2,
ALAR_PLATFORM_3
};
const Position ALAR_GROUND_0 = { 336.439f, 48.181f, -2.389f }; // Ground counterpart to West Platform
const Position ALAR_GROUND_1 = { 379.122f, 25.146f, -2.385f }; // Ground counterpart to Northwest Platform
const Position ALAR_GROUND_2 = { 378.583f, -27.481f, -2.385f }; // Ground counterpart to Northeast Platform
const Position ALAR_GROUND_3 = { 331.631f, -49.716f, -2.389f }; // Ground counterpart to East Platform
const std::array<Position, 4> GROUND_POSITIONS =
{
ALAR_GROUND_0,
ALAR_GROUND_1,
ALAR_GROUND_2,
ALAR_GROUND_3
};
const Position ALAR_ROOM_CENTER = { 330.611f, -2.540f, -2.389f };
const Position ALAR_POINT_QUILL_OR_DIVE = { 332.000f, 0.010f, 43.000f };
const Position ALAR_POINT_MIDDLE = { 331.000f, 0.010f, -2.380f };
const Position ALAR_SE_RAMP_BASE = { 281.064f, -36.590f, -2.389f };
const Position ALAR_SW_RAMP_BASE = { 281.064f, 36.590f, -2.389f };
const Position ALAR_ROOM_S_CENTER = { 281.064f, 0.000f, -2.389f };
std::unordered_map<uint32, bool> lastRebirthState;
std::unordered_map<uint32, bool> isAlarInPhase2;
int8 GetAlarDestinationLocationIndex(Unit* alar, Position& dest)
{
if (!alar)
return LOCATION_NONE;
float x, y, z;
if (!alar->GetMotionMaster()->GetDestination(x, y, z))
return LOCATION_NONE;
dest.Relocate(x, y, z);
const std::array<Position, 6> locations =
{
ALAR_PLATFORM_0,
ALAR_PLATFORM_1,
ALAR_PLATFORM_2,
ALAR_PLATFORM_3,
ALAR_POINT_QUILL_OR_DIVE,
ALAR_POINT_MIDDLE,
};
float minDist = std::numeric_limits<float>::max();
int8 locationIndex = LOCATION_NONE;
for (int8 i = 0; i < TOTAL_ALAR_LOCATIONS; ++i)
{
float dist = dest.GetExactDist2d(&locations[i]);
if (dist < minDist)
{
minDist = dist;
locationIndex = i;
}
}
if (minDist > 0.1f)
return LOCATION_NONE;
return locationIndex;
}
int8 GetAlarCurrentLocationIndex(Unit* alar)
{
if (!alar)
return LOCATION_NONE;
const std::array<Position, 6> locations =
{
ALAR_PLATFORM_0,
ALAR_PLATFORM_1,
ALAR_PLATFORM_2,
ALAR_PLATFORM_3,
ALAR_POINT_QUILL_OR_DIVE,
ALAR_POINT_MIDDLE,
};
float minDist = std::numeric_limits<float>::max();
int8 locationIndex = LOCATION_NONE;
for (int8 i = 0; i < TOTAL_ALAR_LOCATIONS; ++i)
{
float dist = alar->GetPosition().GetExactDist2d(&locations[i]);
if (dist < minDist)
{
minDist = dist;
locationIndex = i;
}
}
if (minDist > 0.1f)
return LOCATION_NONE;
return locationIndex;
}
void GetClosestPlatformAndGround(const Position& botPos, int8& closestPlatform, Position& ground)
{
float minDist = std::numeric_limits<float>::max();
closestPlatform = -1;
for (int8 i = 0; i < 4; ++i)
{
float dist = botPos.GetExactDist2d(&PLATFORM_POSITIONS[i]);
if (dist < minDist)
{
minDist = dist;
closestPlatform = i;
}
}
ground = GROUND_POSITIONS[closestPlatform];
}
std::pair<Unit*, Unit*> GetFirstTwoEmbersOfAlar(PlayerbotAI* botAI)
{
Unit* firstEmber = nullptr;
Unit* secondEmber = nullptr;
for (auto const& guid :
botAI->GetAiObjectContext()->GetValue<GuidVector>("possible targets no los")->Get())
{
Unit* unit = botAI->GetUnit(guid);
if (unit && unit->IsAlive() && unit->GetEntry() == NPC_EMBER_OF_ALAR)
{
if (!firstEmber)
{
firstEmber = unit;
}
else if (!secondEmber)
{
secondEmber = unit;
break;
}
}
}
return { firstEmber, secondEmber };
}
Player* GetSecondEmberTank(PlayerbotAI* botAI)
{
Player* mainTank = GetGroupMainTank(botAI, botAI->GetBot());
Player* assistTank = GetGroupAssistTank(botAI, botAI->GetBot(), 0);
bool mainTankHasMelt = mainTank && mainTank->HasAura(SPELL_MELT_ARMOR);
bool assistTankHasMelt = assistTank && assistTank->HasAura(SPELL_MELT_ARMOR);
if (mainTankHasMelt)
return mainTank;
if (assistTankHasMelt || (!mainTankHasMelt && !assistTankHasMelt))
return assistTank;
return nullptr;
}
// Void Reaver
const Position VOID_REAVER_TANK_POSITION = { 423.845f, 371.733f, 14.897f };
std::unordered_map<ObjectGuid, bool> hasReachedVoidReaverPosition;
std::unordered_map<uint32, std::vector<ArcaneOrbData>> voidReaverArcaneOrbs;
// Kael'thas Sunstrider <Lord of the Blood Elves>
const Position SANGUINAR_TANK_POSITION = { 775.478f, 39.888f, 46.780f };
const Position SANGUINAR_WAITING_POSITION = { 761.850f, 27.459f, 46.779f };
const Position TELONICUS_TANK_POSITION = { 773.717f, 44.091f, 46.780f };
const Position TELONICUS_WAITING_POSITION = { 754.347f, 31.739f, 46.796f };
const Position ADVISOR_HEAL_POSITION = { 752.171f, 19.494f, 46.779f };
const Position CAPERNIAN_WAITING_POSITION = { 743.897f, -11.575f, 46.779f };
const Position KAELTHAS_TANK_POSITION = { 799.390f, -0.837f, 48.729f };
std::unordered_map<uint32, time_t> advisorDpsWaitTimer;
// (1) First priority is an assistant Warlock (real player or bot)
// (2) If no assistant Warlock, then look for any Warlock bot
Player* GetCapernianTank(Player* bot)
{
Group* group = bot->GetGroup();
if (!group)
return nullptr;
Player* fallbackWarlock = nullptr;
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
{
Player* member = ref->GetSource();
if (!member || !member->IsAlive() || member->getClass() != CLASS_WARLOCK)
continue;
if (group->IsAssistant(member->GetGUID()))
return member;
if (!fallbackWarlock && GET_PLAYERBOT_AI(member))
fallbackWarlock = member;
}
return fallbackWarlock;
}
// One Hunter will start on Sanguinar in Phase 3 with Melee to apply Armor Disruption
// (1) First priority is an assistant Hunter (real player or bot)
// (2) If no assistant Hunter, then look for any Hunter bot
bool IsDebuffHunter(Player* bot)
{
if (bot->getClass() != CLASS_HUNTER || !bot->IsAlive())
return false;
Group* group = bot->GetGroup();
if (!group)
return false;
Player* fallbackHunter = nullptr;
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
{
Player* member = ref->GetSource();
if (!member || !member->IsAlive() || member->getClass() != CLASS_HUNTER)
continue;
if (group->IsAssistant(member->GetGUID()))
return member == bot;
if (!fallbackHunter && GET_PLAYERBOT_AI(member))
fallbackHunter = member;
}
return fallbackHunter == bot;
}
bool IsAnyLegendaryWeaponDead(Player* bot)
{
static const std::array<uint32, 7> weaponEntries =
{
NPC_STAFF_OF_DISINTEGRATION,
NPC_COSMIC_INFUSER,
NPC_INFINITY_BLADES,
NPC_WARP_SLICER,
NPC_PHASESHIFT_BULWARK,
NPC_NETHERSTRAND_LONGBOW,
NPC_DEVASTATION
};
constexpr float searchRadius = 100.0f;
for (uint32 entry : weaponEntries)
{
Creature* weapon = bot->FindNearestCreature(entry, searchRadius, false);
if (weapon && !weapon->IsAlive())
return true;
}
return false;
}
bool HasEquippableItemForSlot(Player* bot, uint8 slot)
{
for (uint8 i = 0; i < 5; ++i)
{
uint8 bag = (i == 0) ? INVENTORY_SLOT_BAG_0 : (INVENTORY_SLOT_BAG_START + i - 1);
uint8 startSlot = (bag == INVENTORY_SLOT_BAG_0) ? INVENTORY_SLOT_ITEM_START : 0;
uint8 endSlot = (bag == INVENTORY_SLOT_BAG_0) ? INVENTORY_SLOT_ITEM_END :
(bot->GetBagByPos(bag) ? bot->GetBagByPos(bag)->GetBagSize() : 0);
for (uint8 bagSlot = startSlot; bagSlot < endSlot; ++bagSlot)
{
Item* item = bot->GetItemByPos(bag, bagSlot);
if (!item || !item->GetTemplate())
continue;
uint16 dest = 0;
if (bot->CanEquipItem(slot, dest, item, false) == EQUIP_ERR_OK)
return true;
}
}
return false;
}
}

View File

@ -0,0 +1,158 @@
#ifndef _PLAYERBOT_RAIDTEMPESTKEEPHELPERS_H_
#define _PLAYERBOT_RAIDTEMPESTKEEPHELPERS_H_
#include <ctime>
#include <unordered_map>
#include <vector>
#include "AiObject.h"
#include "Position.h"
#include "Unit.h"
namespace TempestKeepHelpers
{
enum TempestKeepSpells
{
// Trash
SPELL_ARCANE_FLURRY = 37268,
// Al'ar
SPELL_REBIRTH_PHASE2 = 34342,
SPELL_REBIRTH_DIVE = 35369,
SPELL_MELT_ARMOR = 35410,
// Void Reaver
SPELL_ARCANE_ORB = 34172,
// High Astromancer Solarian
SPELL_SOLARIAN_TRANSFORM = 39117,
SPELL_WRATH_OF_THE_ASTROMANCER = 42783,
// Kael'thas Sunstrider
SPELL_PERMANENT_FEIGN_DEATH = 29266,
SPELL_GRAVITY_LAPSE = 39432,
SPELL_KAEL_FULL_POWER = 36187,
SPELL_MENTAL_PROTECTION_FIELD = 36480, // Staff of Disintegration
SPELL_ARCANE_BARRIER = 36481, // Phaseshift Bulwark
SPELL_KAELTHAS_MIND_CONTROL = 36797,
SPELL_SHOCK_BARRIER = 36815,
SPELL_STAFF_FROSTBOLT = 36990,
// Hunter
SPELL_MISDIRECTION = 35079,
// Priest
SPELL_FEAR_WARD = 6346,
};
enum TempestKeepNPCs
{
// Al'ar
NPC_EMBER_OF_ALAR = 19551,
NPC_FLAME_PATCH = 20602,
// High Astromancer Solarian
NPC_SOLARIUM_PRIEST = 18806,
// Kael'thas Sunstrider
NPC_KAELTHAS_SUNSTRIDER = 19622,
NPC_NETHERSTRAND_LONGBOW = 21268,
NPC_DEVASTATION = 21269,
NPC_COSMIC_INFUSER = 21270,
NPC_INFINITY_BLADES = 21271, // Item is singular, but NPC is plural
NPC_WARP_SLICER = 21272,
NPC_PHASESHIFT_BULWARK = 21273,
NPC_STAFF_OF_DISINTEGRATION = 21274,
// NPC_NETHER_VAPOR = 21002, // Unimplemented in AC; method needed if fixed
NPC_PHOENIX = 21362,
NPC_PHOENIX_EGG = 21364,
NPC_FLAME_STRIKE_TRIGGER = 21369,
};
enum TempestKeepItems
{
// Kael'thas Sunstrider
ITEM_WARP_SLICER = 30311,
ITEM_INFINITY_BLADE = 30312,
ITEM_STAFF_OF_DISINTEGRATION = 30313,
ITEM_PHASESHIFT_BULWARK = 30314,
ITEM_DEVASTATION = 30316,
ITEM_COSMIC_INFUSER = 30317,
ITEM_NETHERSTRAND_LONGBOW = 30318,
ITEM_NETHER_SPIKES = 30319,
};
// General
constexpr uint32 TEMPEST_KEEP_MAP_ID = 550;
Unit* GetNearestNonTankPlayerInRadius(PlayerbotAI* botAI, Player* bot, float radius);
std::vector<Unit*> GetAllHazardTriggers(Player* bot, uint32 npcEntry, float searchRadius);
Position FindSafestNearbyPosition(Player* bot, const std::vector<Unit*>& hazards,
float hazardRadius, const Position* center = nullptr);
bool IsPathSafeFromHazards(
const Position& start, const Position& end, const std::vector<Unit*>& hazards,
float hazardRadius);
// Al'ar <Phoenix God>
enum AlarLocationIndex
{
PLATFORM_0_IDX,
PLATFORM_1_IDX,
PLATFORM_2_IDX,
PLATFORM_3_IDX,
POINT_QUILL_OR_DIVE_IDX,
POINT_MIDDLE_IDX,
LOCATION_NONE = -1
};
constexpr float ALAR_BALCONY_Z = 17.0f;
extern const Position ALAR_PLATFORM_0;
extern const Position ALAR_PLATFORM_1;
extern const Position ALAR_PLATFORM_2;
extern const Position ALAR_PLATFORM_3;
extern const std::array<Position, 4> PLATFORM_POSITIONS;
extern const Position ALAR_GROUND_0;
extern const Position ALAR_GROUND_1;
extern const Position ALAR_GROUND_2;
extern const Position ALAR_GROUND_3;
extern const std::array<Position, 4> GROUND_POSITIONS;
extern const Position ALAR_ROOM_CENTER;
extern const Position ALAR_POINT_QUILL_OR_DIVE;
extern const Position ALAR_POINT_MIDDLE;
extern const Position ALAR_SE_RAMP_BASE;
extern const Position ALAR_SW_RAMP_BASE;
extern const Position ALAR_ROOM_S_CENTER;
constexpr uint8 TOTAL_ALAR_LOCATIONS = 6;
extern std::unordered_map<uint32, bool> lastRebirthState;
extern std::unordered_map<uint32, bool> isAlarInPhase2;
int8 GetAlarDestinationLocationIndex(Unit* alar, Position& dest);
int8 GetAlarCurrentLocationIndex(Unit* alar);
void GetClosestPlatformAndGround(
const Position& botPos, int8& closestPlatform, Position& ground);
std::pair<Unit*, Unit*> GetFirstTwoEmbersOfAlar(PlayerbotAI* botAI);
Player* GetSecondEmberTank(PlayerbotAI* botAI);
// Void Reaver
extern const Position VOID_REAVER_TANK_POSITION;
extern std::unordered_map<ObjectGuid, bool> hasReachedVoidReaverPosition;
struct ArcaneOrbData
{
Position destination;
uint32 castTime;
};
extern std::unordered_map<uint32, std::vector<ArcaneOrbData>> voidReaverArcaneOrbs;
// Kael'thas Sunstrider <Lord of the Blood Elves>
extern const Position SANGUINAR_TANK_POSITION;
extern const Position SANGUINAR_WAITING_POSITION;
extern const Position TELONICUS_TANK_POSITION;
extern const Position TELONICUS_WAITING_POSITION;
extern const Position CAPERNIAN_WAITING_POSITION;
extern const Position ADVISOR_HEAL_POSITION;
extern const Position KAELTHAS_TANK_POSITION;
extern std::unordered_map<uint32, time_t> advisorDpsWaitTimer;
Player* GetCapernianTank(Player* bot);
bool IsDebuffHunter(Player* bot);
bool IsAnyLegendaryWeaponDead(Player* bot);
bool HasEquippableItemForSlot(Player* bot, uint8 slot);
}
#endif

View File

@ -0,0 +1,54 @@
#ifndef _PLAYERBOT_RAIDTEMPESTKEEPKAELTHASBOSSAI_H_
#define _PLAYERBOT_RAIDTEMPESTKEEPKAELTHASBOSSAI_H_
#include "ScriptedCreature.h"
enum KTYells
{
};
enum KTPhases
{
PHASE_NONE = 0,
PHASE_SINGLE_ADVISOR = 1,
PHASE_WEAPONS = 2,
PHASE_TRANSITION = 3,
PHASE_ALL_ADVISORS = 4,
PHASE_FINAL = 5
};
enum KTActions
{
};
struct boss_kaelthas : public BossAI
{
boss_kaelthas(Creature* creature);
void PrepareAdvisors();
void SetRoomState(GOState state);
void Reset() override;
void AttackStart(Unit* who) override;
void MoveInLineOfSight(Unit* who) override;
void KilledUnit(Unit* victim) override;
void JustSummoned(Creature* summon) override;
void SpellHit(Unit* caster, SpellInfo const* spell) override;
void MovementInform(uint32 type, uint32 point) override;
void ExecuteMiddleEvent();
void IntroduceNewAdvisor(KTYells talkIntroduction, KTActions kaelAction);
void PhaseEnchantedWeaponsExecute();
void PhaseAllAdvisorsExecute();
void PhaseKaelExecute();
void UpdateAI(uint32 diff) override;
bool CheckEvadeIfOutOfCombatArea() const override;
void JustDied(Unit* killer) override;
uint32 GetPhase() const { return _phase; } // This is the only addition to the existing class
private:
uint32 _phase;
uint8 _advisorsAlive;
bool _transitionSceneReached = false;
};
#endif

View File

@ -0,0 +1,47 @@
#include "RaidTempestKeepHelpers.h"
#include "ObjectAccessor.h"
#include "Player.h"
#include "ScriptMgr.h"
#include "Spell.h"
#include "Timer.h"
using namespace TempestKeepHelpers;
class BossListenerScript : public AllSpellScript
{
public:
BossListenerScript() : AllSpellScript("BossListenerScript") { }
void OnSpellCast(Spell* spell, Unit* caster, SpellInfo const* spellInfo, bool /*skipCheck*/) override
{
if (spellInfo->Id != SPELL_ARCANE_ORB)
return;
std::list<TargetInfo> const& targets = *spell->GetUniqueTargetInfo();
if (targets.empty())
return;
Player* target = ObjectAccessor::GetPlayer(*caster, targets.front().targetGUID);
if (!target)
return;
auto& orbs = voidReaverArcaneOrbs[caster->GetMap()->GetInstanceId()];
uint32 currentTime = getMSTime();
ArcaneOrbData orbData;
orbData.destination = target->GetPosition();
orbData.castTime = currentTime;
orbs.push_back(orbData);
orbs.erase(std::remove_if(orbs.begin(), orbs.end(),
[currentTime](const ArcaneOrbData& orb) {
return getMSTimeDiff(orb.castTime, currentTime) > 5000;
}), orbs.end());
}
};
void AddSC_TempestKeepBotScripts()
{
new BossListenerScript();
}

View File

@ -72,9 +72,7 @@ bool FlameLeviathanVehicleAction::Execute(Event /*event*/)
continue; continue;
} }
if (!target || bot->GetExactDist(target) > bot->GetExactDist(unit)) if (!target || bot->GetExactDist(target) > bot->GetExactDist(unit))
{
target = unit; target = unit;
}
} }
// Flame Leviathan is chasing me // Flame Leviathan is chasing me
if (flame && flame->GetVictim() == vehicleBase_) if (flame && flame->GetVictim() == vehicleBase_)
@ -419,9 +417,7 @@ bool RazorscaleAvoidDevouringFlameAction::Execute(Event /*event*/)
RazorscaleBossHelper razorscaleHelper(botAI); RazorscaleBossHelper razorscaleHelper(botAI);
if (!razorscaleHelper.UpdateBossAI()) if (!razorscaleHelper.UpdateBossAI())
{
return false; return false;
}
bool isMainTank = botAI->IsMainTank(bot); bool isMainTank = botAI->IsMainTank(bot);
const float flameRadius = 3.5f; const float flameRadius = 3.5f;
@ -433,9 +429,7 @@ bool RazorscaleAvoidDevouringFlameAction::Execute(Event /*event*/)
// Get the boss // Get the boss
Unit* boss = AI_VALUE2(Unit*, "find target", "razorscale"); Unit* boss = AI_VALUE2(Unit*, "find target", "razorscale");
if (!boss) if (!boss)
{
return false; return false;
}
GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs"); GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs");
Unit* closestFlame = nullptr; Unit* closestFlame = nullptr;
@ -458,15 +452,12 @@ bool RazorscaleAvoidDevouringFlameAction::Execute(Event /*event*/)
// Off tanks are following the main tank during grounded and should prioritise stacking // Off tanks are following the main tank during grounded and should prioritise stacking
if (razorscaleHelper.IsGroundPhase() && (botAI->IsTank(bot) && !botAI->IsMainTank(bot))) if (razorscaleHelper.IsGroundPhase() && (botAI->IsTank(bot) && !botAI->IsMainTank(bot)))
{
return false; return false;
}
// Handle movement from flames // Handle movement from flames
if (closestDistance < safeDistance) if (closestDistance < safeDistance)
{
return MoveAway(closestFlame, safeDistance); return MoveAway(closestFlame, safeDistance);
}
return false; return false;
} }
@ -486,9 +477,7 @@ bool RazorscaleAvoidDevouringFlameAction::isUseful()
{ {
float distance = bot->GetDistance2d(unit); float distance = bot->GetDistance2d(unit);
if (distance < safeDistance) if (distance < safeDistance)
{
return true; // Bot is within the danger distance return true; // Bot is within the danger distance
}
} }
} }
@ -522,9 +511,7 @@ bool RazorscaleAvoidSentinelAction::Execute(Event /*event*/)
// Move away if ranged and too close // Move away if ranged and too close
if (isRanged && bot->GetDistance2d(unit) < radius) if (isRanged && bot->GetDistance2d(unit) < radius)
{
movedAway = MoveAway(unit, radius) || movedAway; movedAway = MoveAway(unit, radius) || movedAway;
}
} }
} }
@ -547,9 +534,7 @@ bool RazorscaleAvoidSentinelAction::Execute(Event /*event*/)
// If there's no skull set yet, or the skull is on a different target, set the sentinel // If there's no skull set yet, or the skull is on a different target, set the sentinel
if (!currentSkullTarget || (lowestHealthSentinel->GetGUID() != currentSkullTarget)) if (!currentSkullTarget || (lowestHealthSentinel->GetGUID() != currentSkullTarget))
{
group->SetTargetIcon(skullIndex, bot->GetGUID(), lowestHealthSentinel->GetGUID()); group->SetTargetIcon(skullIndex, bot->GetGUID(), lowestHealthSentinel->GetGUID());
}
} }
break; // Stop after finding the first valid bot tank break; // Stop after finding the first valid bot tank
} }
@ -565,9 +550,7 @@ bool RazorscaleAvoidSentinelAction::Execute(Event /*event*/)
// If there's no skull set yet, or the skull is on a different target, set the sentinel // If there's no skull set yet, or the skull is on a different target, set the sentinel
if (!currentSkullTarget || (lowestHealthSentinel->GetGUID() != currentSkullTarget)) if (!currentSkullTarget || (lowestHealthSentinel->GetGUID() != currentSkullTarget))
{
group->SetTargetIcon(skullIndex, bot->GetGUID(), lowestHealthSentinel->GetGUID()); group->SetTargetIcon(skullIndex, bot->GetGUID(), lowestHealthSentinel->GetGUID());
}
} }
} }
@ -582,9 +565,7 @@ bool RazorscaleAvoidSentinelAction::isUseful()
// If this bot is the main tank, it should always try to mark // If this bot is the main tank, it should always try to mark
if (isMainTank) if (isMainTank)
{
return true; return true;
}
// If the main tank is a human, check if this bot is one of the first three valid bot tanks // If the main tank is a human, check if this bot is one of the first three valid bot tanks
if (mainTank && !GET_PLAYERBOT_AI(mainTank)) // Main tank is a human player if (mainTank && !GET_PLAYERBOT_AI(mainTank)) // Main tank is a human player
@ -592,9 +573,7 @@ bool RazorscaleAvoidSentinelAction::isUseful()
for (int i = 0; i < 3; ++i) for (int i = 0; i < 3; ++i)
{ {
if (botAI->IsAssistTankOfIndex(bot, i) && GET_PLAYERBOT_AI(bot)) // Bot is a valid tank if (botAI->IsAssistTankOfIndex(bot, i) && GET_PLAYERBOT_AI(bot)) // Bot is a valid tank
{
return true; // This bot should assist with marking return true; // This bot should assist with marking
}
} }
} }
@ -608,9 +587,7 @@ bool RazorscaleAvoidSentinelAction::isUseful()
if (unit && unit->GetEntry() == RazorscaleBossHelper::UNIT_DARK_RUNE_SENTINEL) if (unit && unit->GetEntry() == RazorscaleBossHelper::UNIT_DARK_RUNE_SENTINEL)
{ {
if (isRanged && bot->GetDistance2d(unit) < radius) if (isRanged && bot->GetDistance2d(unit) < radius)
{
return true; return true;
}
} }
} }
@ -633,9 +610,7 @@ bool RazorscaleAvoidWhirlwindAction::Execute(Event /*event*/)
{ {
float currentDistance = bot->GetDistance2d(unit); float currentDistance = bot->GetDistance2d(unit);
if (currentDistance < radius) if (currentDistance < radius)
{
return MoveAway(unit, radius); return MoveAway(unit, radius);
}
} }
} }
return false; return false;
@ -690,23 +665,17 @@ bool RazorscaleIgnoreBossAction::isUseful()
} }
if (!botAI->IsTank(bot)) if (!botAI->IsTank(bot))
{
return false; return false;
}
Group* group = bot->GetGroup(); Group* group = bot->GetGroup();
if (!group) if (!group)
{
return false; return false;
}
// Check if the boss is already set as the moon marker // Check if the boss is already set as the moon marker
int8 moonIndex = 4; // Moon marker index int8 moonIndex = 4; // Moon marker index
ObjectGuid currentMoonTarget = group->GetTargetIcon(moonIndex); ObjectGuid currentMoonTarget = group->GetTargetIcon(moonIndex);
if (currentMoonTarget == boss->GetGUID()) if (currentMoonTarget == boss->GetGUID())
{
return false; // Moon marker is already correctly set, no further action needed return false; // Moon marker is already correctly set, no further action needed
}
// Proceed to tank-specific logic // Proceed to tank-specific logic
Unit* mainTankUnit = AI_VALUE(Unit*, "main tank"); Unit* mainTankUnit = AI_VALUE(Unit*, "main tank");
@ -714,9 +683,7 @@ bool RazorscaleIgnoreBossAction::isUseful()
// If this bot is the main tank, it needs to set the moon marker // If this bot is the main tank, it needs to set the moon marker
if (mainTankUnit == bot) if (mainTankUnit == bot)
{
return true; return true;
}
// If the main tank is a human, check if this bot is the lowest-indexed bot tank // If the main tank is a human, check if this bot is the lowest-indexed bot tank
if (mainTank && !GET_PLAYERBOT_AI(mainTank)) // Main tank is a human player if (mainTank && !GET_PLAYERBOT_AI(mainTank)) // Main tank is a human player
@ -724,9 +691,7 @@ bool RazorscaleIgnoreBossAction::isUseful()
for (int i = 0; i < 3; ++i) // Only iterate through the first 3 indexes for (int i = 0; i < 3; ++i) // Only iterate through the first 3 indexes
{ {
if (botAI->IsAssistTankOfIndex(bot, i) && GET_PLAYERBOT_AI(bot)) // Valid bot tank if (botAI->IsAssistTankOfIndex(bot, i) && GET_PLAYERBOT_AI(bot)) // Valid bot tank
{
return true; // This bot should assign the marker return true; // This bot should assign the marker
}
} }
} }
} }
@ -1156,9 +1121,7 @@ bool IronAssemblyLightningTendrilsAction::Execute(Event /*event*/)
float currentDistance = bot->GetDistance2d(boss); float currentDistance = bot->GetDistance2d(boss);
if (currentDistance < radius) if (currentDistance < radius)
{
return MoveAway(boss, radius - currentDistance); return MoveAway(boss, radius - currentDistance);
}
return false; return false;
} }
@ -1180,9 +1143,7 @@ bool IronAssemblyOverloadAction::Execute(Event /*event*/)
float currentDistance = bot->GetDistance2d(boss); float currentDistance = bot->GetDistance2d(boss);
if (currentDistance < radius) if (currentDistance < radius)
{
return MoveAway(boss, radius - currentDistance); return MoveAway(boss, radius - currentDistance);
}
return false; return false;
} }
@ -1230,7 +1191,6 @@ bool KologarnMarkDpsTargetAction::Execute(Event /*event*/)
if (!target) if (!target)
continue; continue;
uint32 creatureId = target->GetEntry();
if (target->GetEntry() == NPC_RUBBLE && target->IsAlive()) if (target->GetEntry() == NPC_RUBBLE && target->IsAlive())
{ {
targetToMark = target; targetToMark = target;
@ -1252,21 +1212,15 @@ bool KologarnMarkDpsTargetAction::Execute(Event /*event*/)
{ {
Unit* boss = AI_VALUE2(Unit*, "find target", "kologarn"); Unit* boss = AI_VALUE2(Unit*, "find target", "kologarn");
if (boss && boss->IsAlive()) if (boss && boss->IsAlive())
{
targetToMark = boss; targetToMark = boss;
}
} }
if (!targetToMark) if (!targetToMark)
{
return false; // No target to mark return false; // No target to mark
}
Unit* leftArm = AI_VALUE2(Unit*, "find target", "left arm"); Unit* leftArm = AI_VALUE2(Unit*, "find target", "left arm");
if (leftArm && leftArm->IsAlive()) if (leftArm && leftArm->IsAlive())
{
targetToCcMark = leftArm; targetToCcMark = leftArm;
}
bool isMainTank = botAI->IsMainTank(bot); bool isMainTank = botAI->IsMainTank(bot);
Unit* mainTankUnit = AI_VALUE(Unit*, "main tank"); Unit* mainTankUnit = AI_VALUE(Unit*, "main tank");
@ -1284,13 +1238,10 @@ bool KologarnMarkDpsTargetAction::Execute(Event /*event*/)
{ {
group->SetTargetIcon(skullIndex, bot->GetGUID(), targetToMark->GetGUID()); group->SetTargetIcon(skullIndex, bot->GetGUID(), targetToMark->GetGUID());
if (targetToCcMark) if (targetToCcMark)
{
group->SetTargetIcon(moonIndex, bot->GetGUID(), targetToCcMark->GetGUID()); group->SetTargetIcon(moonIndex, bot->GetGUID(), targetToCcMark->GetGUID());
}
if (additionalTargetToMark) if (additionalTargetToMark)
{
group->SetTargetIcon(crossIndex, bot->GetGUID(), additionalTargetToMark->GetGUID()); group->SetTargetIcon(crossIndex, bot->GetGUID(), additionalTargetToMark->GetGUID());
}
return true; return true;
} }
@ -1305,13 +1256,11 @@ bool KologarnMarkDpsTargetAction::Execute(Event /*event*/)
{ {
group->SetTargetIcon(skullIndex, bot->GetGUID(), targetToMark->GetGUID()); group->SetTargetIcon(skullIndex, bot->GetGUID(), targetToMark->GetGUID());
if (targetToCcMark) if (targetToCcMark)
{
group->SetTargetIcon(moonIndex, bot->GetGUID(), targetToCcMark->GetGUID()); group->SetTargetIcon(moonIndex, bot->GetGUID(), targetToCcMark->GetGUID());
}
if (additionalTargetToMark) if (additionalTargetToMark)
{
group->SetTargetIcon(crossIndex, bot->GetGUID(), additionalTargetToMark->GetGUID()); group->SetTargetIcon(crossIndex, bot->GetGUID(), additionalTargetToMark->GetGUID());
}
return true; return true;
} }
} }
@ -1326,13 +1275,11 @@ bool KologarnMarkDpsTargetAction::Execute(Event /*event*/)
{ {
group->SetTargetIcon(skullIndex, bot->GetGUID(), targetToMark->GetGUID()); group->SetTargetIcon(skullIndex, bot->GetGUID(), targetToMark->GetGUID());
if (targetToCcMark) if (targetToCcMark)
{
group->SetTargetIcon(moonIndex, bot->GetGUID(), targetToCcMark->GetGUID()); group->SetTargetIcon(moonIndex, bot->GetGUID(), targetToCcMark->GetGUID());
}
if (additionalTargetToMark) if (additionalTargetToMark)
{
group->SetTargetIcon(crossIndex, bot->GetGUID(), additionalTargetToMark->GetGUID()); group->SetTargetIcon(crossIndex, bot->GetGUID(), additionalTargetToMark->GetGUID());
}
return true; return true;
} }
break; // Stop after finding the first valid bot tank break; // Stop after finding the first valid bot tank
@ -1379,17 +1326,11 @@ bool KologarnEyebeamAction::Execute(Event /*event*/)
bool runToLeftSide; bool runToLeftSide;
if (!distanceToLeftPoint) if (!distanceToLeftPoint)
{
runToLeftSide = true; runToLeftSide = true;
}
else if (!distanceToRightPoint) else if (!distanceToRightPoint)
{
runToLeftSide = false; runToLeftSide = false;
}
else else
{
runToLeftSide = distanceToRightPoint > distanceToLeftPoint; runToLeftSide = distanceToRightPoint > distanceToLeftPoint;
}
bool teleportedToPoint; bool teleportedToPoint;
KologarnEyebeamTrigger kologarnEyebeamTrigger(botAI); KologarnEyebeamTrigger kologarnEyebeamTrigger(botAI);
@ -1418,9 +1359,7 @@ bool KologarnEyebeamAction::isUseful()
{ {
KologarnEyebeamTrigger kologarnEyebeamTrigger(botAI); KologarnEyebeamTrigger kologarnEyebeamTrigger(botAI);
if (!kologarnEyebeamTrigger.IsActive()) if (!kologarnEyebeamTrigger.IsActive())
{
return false; return false;
}
return botAI->HasCheat(BotCheatMask::raid); return botAI->HasCheat(BotCheatMask::raid);
} }
@ -1447,9 +1386,7 @@ bool KologarnCrunchArmorAction::isUseful()
{ {
KologarnCrunchArmorTrigger kologarnCrunchArmorTrigger(botAI); KologarnCrunchArmorTrigger kologarnCrunchArmorTrigger(botAI);
if (!kologarnCrunchArmorTrigger.IsActive()) if (!kologarnCrunchArmorTrigger.IsActive())
{
return false; return false;
}
return botAI->HasCheat(BotCheatMask::raid); return botAI->HasCheat(BotCheatMask::raid);
} }
@ -1482,15 +1419,11 @@ bool HodirMoveSnowpackedIcicleAction::isUseful()
// Check boss and it is alive // Check boss and it is alive
Unit* boss = AI_VALUE2(Unit*, "find target", "hodir"); Unit* boss = AI_VALUE2(Unit*, "find target", "hodir");
if (!boss || !boss->IsAlive()) if (!boss || !boss->IsAlive())
{
return false; return false;
}
// Check if boss is casting Flash Freeze // Check if boss is casting Flash Freeze
if (!boss->HasUnitState(UNIT_STATE_CASTING) || !boss->FindCurrentSpellBySpellId(SPELL_FLASH_FREEZE)) if (!boss->HasUnitState(UNIT_STATE_CASTING) || !boss->FindCurrentSpellBySpellId(SPELL_FLASH_FREEZE))
{
return false; return false;
}
// Find the nearest Snowpacked Icicle Target // Find the nearest Snowpacked Icicle Target
Creature* target = bot->FindNearestCreature(NPC_SNOWPACKED_ICICLE, 100.0f); Creature* target = bot->FindNearestCreature(NPC_SNOWPACKED_ICICLE, 100.0f);
@ -1499,9 +1432,7 @@ bool HodirMoveSnowpackedIcicleAction::isUseful()
// Check that bot is stacked on Snowpacked Icicle // Check that bot is stacked on Snowpacked Icicle
if (bot->GetDistance2d(target->GetPositionX(), target->GetPositionY()) <= 5.0f) if (bot->GetDistance2d(target->GetPositionX(), target->GetPositionY()) <= 5.0f)
{
return false; return false;
}
return true; return true;
} }
@ -1612,42 +1543,26 @@ bool FreyaMarkDpsTargetAction::Execute(Event /*event*/)
continue; continue;
if (target->GetEntry() == NPC_EONARS_GIFT) if (target->GetEntry() == NPC_EONARS_GIFT)
{
eonarsGift = target; eonarsGift = target;
}
else if (target->GetEntry() == NPC_ANCIENT_CONSERVATOR) else if (target->GetEntry() == NPC_ANCIENT_CONSERVATOR)
{
ancientConservator = target; ancientConservator = target;
}
else if (target->GetEntry() == NPC_SNAPLASHER) else if (target->GetEntry() == NPC_SNAPLASHER)
{
snaplasher = target; snaplasher = target;
}
else if (target->GetEntry() == NPC_ANCIENT_WATER_SPIRIT) else if (target->GetEntry() == NPC_ANCIENT_WATER_SPIRIT)
{
ancientWaterSpirit = target; ancientWaterSpirit = target;
}
else if (target->GetEntry() == NPC_STORM_LASHER) else if (target->GetEntry() == NPC_STORM_LASHER)
{
stormLasher = target; stormLasher = target;
}
else if (target->GetEntry() == NPC_DETONATING_LASHER && !firstDetonatingLasher) else if (target->GetEntry() == NPC_DETONATING_LASHER && !firstDetonatingLasher)
{
firstDetonatingLasher = target; firstDetonatingLasher = target;
}
} }
// Check that eonars gift is need to be mark // Check that eonars gift is need to be mark
if (eonarsGift) if (eonarsGift)
{
targetToMark = eonarsGift; targetToMark = eonarsGift;
}
// Check that ancient conservator is need to be mark // Check that ancient conservator is need to be mark
if (ancientConservator && !targetToMark) if (ancientConservator && !targetToMark)
{
targetToMark = ancientConservator; targetToMark = ancientConservator;
}
// Check that trio of adds is need to be mark // Check that trio of adds is need to be mark
if ((snaplasher || ancientWaterSpirit || stormLasher) && !targetToMark) if ((snaplasher || ancientWaterSpirit || stormLasher) && !targetToMark)
@ -1679,14 +1594,10 @@ bool FreyaMarkDpsTargetAction::Execute(Event /*event*/)
// Check that detonating lasher is need to be mark // Check that detonating lasher is need to be mark
if (firstDetonatingLasher && !targetToMark) if (firstDetonatingLasher && !targetToMark)
{
targetToMark = firstDetonatingLasher; targetToMark = firstDetonatingLasher;
}
if (!targetToMark) if (!targetToMark)
{
return false; // No target to mark return false; // No target to mark
}
bool isMainTank = botAI->IsMainTank(bot); bool isMainTank = botAI->IsMainTank(bot);
Unit* mainTankUnit = AI_VALUE(Unit*, "main tank"); Unit* mainTankUnit = AI_VALUE(Unit*, "main tank");
@ -1778,9 +1689,7 @@ bool ThorimUnbalancingStrikeAction::isUseful()
{ {
ThorimUnbalancingStrikeTrigger thorimUnbalancingStrikeTrigger(botAI); ThorimUnbalancingStrikeTrigger thorimUnbalancingStrikeTrigger(botAI);
if (!thorimUnbalancingStrikeTrigger.IsActive()) if (!thorimUnbalancingStrikeTrigger.IsActive())
{
return false; return false;
}
return botAI->HasCheat(BotCheatMask::raid); return botAI->HasCheat(BotCheatMask::raid);
} }
@ -2188,23 +2097,15 @@ bool MimironShockBlastAction::Execute(Event /*event*/)
continue; continue;
if (target->GetEntry() == NPC_LEVIATHAN_MKII) if (target->GetEntry() == NPC_LEVIATHAN_MKII)
{
leviathanMkII = target; leviathanMkII = target;
}
else if (target->GetEntry() == NPC_VX001) else if (target->GetEntry() == NPC_VX001)
{
vx001 = target; vx001 = target;
}
else if (target->GetEntry() == NPC_AERIAL_COMMAND_UNIT) else if (target->GetEntry() == NPC_AERIAL_COMMAND_UNIT)
{
aerialCommandUnit = target; aerialCommandUnit = target;
}
} }
if (!leviathanMkII) if (!leviathanMkII)
{
return false; return false;
}
if (!vx001 && !aerialCommandUnit) if (!vx001 && !aerialCommandUnit)
{ {
@ -2213,9 +2114,8 @@ bool MimironShockBlastAction::Execute(Event /*event*/)
MoveAway(leviathanMkII, radius - currentDistance); MoveAway(leviathanMkII, radius - currentDistance);
if (botAI->IsMelee(bot)) if (botAI->IsMelee(bot))
{
botAI->SetNextCheckDelay(100); botAI->SetNextCheckDelay(100);
}
return true; return true;
} }
else else
@ -2261,15 +2161,11 @@ bool MimironP3Wx2LaserBarrageAction::Execute(Event /*event*/)
{ {
auto master = botAI->GetMaster(); auto master = botAI->GetMaster();
if (!master || !master->IsAlive()) if (!master || !master->IsAlive())
{
return false; return false;
}
if (bot->GetDistance2d(master) > 15.0f) if (bot->GetDistance2d(master) > 15.0f)
{
return bot->TeleportTo(master->GetMapId(), master->GetPositionX(), master->GetPositionY(), return bot->TeleportTo(master->GetMapId(), master->GetPositionX(), master->GetPositionY(),
master->GetPositionZ(), master->GetOrientation()); master->GetPositionZ(), master->GetOrientation());
}
return MoveTo(master->GetMapId(), master->GetPositionX(), master->GetPositionY(), master->GetPositionZ(), false, return MoveTo(master->GetMapId(), master->GetPositionX(), master->GetPositionY(), master->GetPositionZ(), false,
false, false, true, MovementPriority::MOVEMENT_COMBAT, true); false, false, true, MovementPriority::MOVEMENT_COMBAT, true);
@ -2294,18 +2190,14 @@ bool MimironRapidBurstAction::Execute(Event /*event*/)
continue; continue;
if (target->GetEntry() == NPC_LEVIATHAN_MKII) if (target->GetEntry() == NPC_LEVIATHAN_MKII)
{
leviathanMkII = target; leviathanMkII = target;
}
} }
Position targetPosition; Position targetPosition;
Group* group = bot->GetGroup(); Group* group = bot->GetGroup();
if (!group) if (!group)
{
return false; return false;
}
uint32 memberSpotNumber = 0; uint32 memberSpotNumber = 0;
for (GroupReference* gref = group->GetFirstMember(); gref; gref = gref->next()) for (GroupReference* gref = group->GetFirstMember(); gref; gref = gref->next())
@ -2359,28 +2251,21 @@ bool MimironRapidBurstAction::Execute(Event /*event*/)
memberSpotNumber++; memberSpotNumber++;
if (memberSpotNumber == 3) if (memberSpotNumber == 3)
{
memberSpotNumber = 0; memberSpotNumber = 0;
}
} }
MoveTo(bot->GetMapId(), targetPosition.GetPositionX(), targetPosition.GetPositionY(), targetPosition.GetPositionZ(), MoveTo(bot->GetMapId(), targetPosition.GetPositionX(), targetPosition.GetPositionY(), targetPosition.GetPositionZ(),
false, false, false, true, MovementPriority::MOVEMENT_FORCED, true, false); false, false, false, true, MovementPriority::MOVEMENT_FORCED, true, false);
if (AI_VALUE(float, "disperse distance") != 0.0f) if (AI_VALUE(float, "disperse distance") != 0.0f)
{
SET_AI_VALUE(float, "disperse distance", 0.0f); SET_AI_VALUE(float, "disperse distance", 0.0f);
}
TankFaceStrategy tankFaceStrategy(botAI); TankFaceStrategy tankFaceStrategy(botAI);
if (botAI->HasStrategy(tankFaceStrategy.getName(), BotState::BOT_STATE_COMBAT)) if (botAI->HasStrategy(tankFaceStrategy.getName(), BotState::BOT_STATE_COMBAT))
{
botAI->ChangeStrategy(REMOVE_STRATEGY_CHAR + tankFaceStrategy.getName(), BotState::BOT_STATE_COMBAT); botAI->ChangeStrategy(REMOVE_STRATEGY_CHAR + tankFaceStrategy.getName(), BotState::BOT_STATE_COMBAT);
}
if (botAI->HasStrategy(tankFaceStrategy.getName(), BotState::BOT_STATE_NON_COMBAT)) if (botAI->HasStrategy(tankFaceStrategy.getName(), BotState::BOT_STATE_NON_COMBAT))
{
botAI->ChangeStrategy(REMOVE_STRATEGY_CHAR + tankFaceStrategy.getName(), BotState::BOT_STATE_NON_COMBAT); botAI->ChangeStrategy(REMOVE_STRATEGY_CHAR + tankFaceStrategy.getName(), BotState::BOT_STATE_NON_COMBAT);
}
if (bot->GetDistance(targetPosition) > 1.0f) if (bot->GetDistance(targetPosition) > 1.0f)
return false; return false;
@ -2403,53 +2288,38 @@ bool MimironAerialCommandUnitAction::Execute(Event /*event*/)
continue; continue;
if (target->GetEntry() == NPC_AERIAL_COMMAND_UNIT) if (target->GetEntry() == NPC_AERIAL_COMMAND_UNIT)
{
boss = target; boss = target;
}
else if (target->GetEntry() == NPC_BOMB_BOT) else if (target->GetEntry() == NPC_BOMB_BOT)
{
bombBot = target; bombBot = target;
}
else if (target->GetEntry() == NPC_ASSAULT_BOT) else if (target->GetEntry() == NPC_ASSAULT_BOT)
{
assaultBot = target; assaultBot = target;
}
} }
if (botAI->IsMainTank(bot) || botAI->IsAssistTankOfIndex(bot, 0)) if (botAI->IsMainTank(bot) || botAI->IsAssistTankOfIndex(bot, 0))
{ {
Group* group = bot->GetGroup(); Group* group = bot->GetGroup();
if (!group) if (!group)
{
return false; return false;
}
if (bombBot) if (bombBot)
{
group->SetTargetIcon(RtiTargetValue::crossIndex, bot->GetGUID(), bombBot->GetGUID()); group->SetTargetIcon(RtiTargetValue::crossIndex, bot->GetGUID(), bombBot->GetGUID());
}
else if (boss) else if (boss)
{
group->SetTargetIcon(RtiTargetValue::crossIndex, bot->GetGUID(), boss->GetGUID()); group->SetTargetIcon(RtiTargetValue::crossIndex, bot->GetGUID(), boss->GetGUID());
}
if (assaultBot) if (assaultBot)
{ {
ObjectGuid skullTarget = group->GetTargetIcon(RtiTargetValue::skullIndex); ObjectGuid skullTarget = group->GetTargetIcon(RtiTargetValue::skullIndex);
Unit* skullUnit = botAI->GetUnit(skullTarget); Unit* skullUnit = botAI->GetUnit(skullTarget);
if (!skullTarget || !skullUnit || !skullUnit->IsAlive()) if (!skullTarget || !skullUnit || !skullUnit->IsAlive())
{
group->SetTargetIcon(RtiTargetValue::skullIndex, bot->GetGUID(), assaultBot->GetGUID()); group->SetTargetIcon(RtiTargetValue::skullIndex, bot->GetGUID(), assaultBot->GetGUID());
}
} }
return true; return true;
} }
if (AI_VALUE(float, "disperse distance") != 5.0f) if (AI_VALUE(float, "disperse distance") != 5.0f)
{
SET_AI_VALUE(float, "disperse distance", 5.0f); SET_AI_VALUE(float, "disperse distance", 5.0f);
}
botAI->GetAiObjectContext()->GetValue<std::string>("rti")->Set("cross"); botAI->GetAiObjectContext()->GetValue<std::string>("rti")->Set("cross");
return true; return true;
} }
@ -2476,10 +2346,8 @@ bool MimironRocketStrikeAction::Execute(Event /*event*/)
if (target->GetEntry() == NPC_LEVIATHAN_MKII) if (target->GetEntry() == NPC_LEVIATHAN_MKII)
leviathanMkII = target; leviathanMkII = target;
else if (target->GetEntry() == NPC_VX001) else if (target->GetEntry() == NPC_VX001)
vx001 = target; vx001 = target;
else if (target->GetEntry() == NPC_AERIAL_COMMAND_UNIT) else if (target->GetEntry() == NPC_AERIAL_COMMAND_UNIT)
aerialCommandUnit = target; aerialCommandUnit = target;
} }
@ -2536,23 +2404,15 @@ bool MimironPhase4MarkDpsAction::Execute(Event /*event*/)
continue; continue;
if (target->GetEntry() == NPC_LEVIATHAN_MKII) if (target->GetEntry() == NPC_LEVIATHAN_MKII)
{
leviathanMkII = target; leviathanMkII = target;
}
else if (target->GetEntry() == NPC_VX001) else if (target->GetEntry() == NPC_VX001)
{
vx001 = target; vx001 = target;
}
else if (target->GetEntry() == NPC_AERIAL_COMMAND_UNIT) else if (target->GetEntry() == NPC_AERIAL_COMMAND_UNIT)
{
aerialCommandUnit = target; aerialCommandUnit = target;
}
} }
if (!leviathanMkII || !vx001 || !aerialCommandUnit) if (!leviathanMkII || !vx001 || !aerialCommandUnit)
{
return false; return false;
}
if (botAI->IsMainTank(bot)) if (botAI->IsMainTank(bot))
{ {
@ -2563,31 +2423,27 @@ bool MimironPhase4MarkDpsAction::Execute(Event /*event*/)
highestHealth = leviathanMkII->GetHealth(); highestHealth = leviathanMkII->GetHealth();
highestHealthUnit = leviathanMkII; highestHealthUnit = leviathanMkII;
} }
if (vx001 && vx001->GetHealth() > highestHealth) if (vx001 && vx001->GetHealth() > highestHealth)
{ {
highestHealth = vx001->GetHealth(); highestHealth = vx001->GetHealth();
highestHealthUnit = vx001; highestHealthUnit = vx001;
} }
if (aerialCommandUnit && aerialCommandUnit->GetHealth() > highestHealth) if (aerialCommandUnit && aerialCommandUnit->GetHealth() > highestHealth)
{
highestHealthUnit = aerialCommandUnit; highestHealthUnit = aerialCommandUnit;
}
group->SetTargetIcon(RtiTargetValue::skullIndex, bot->GetGUID(), highestHealthUnit->GetGUID()); group->SetTargetIcon(RtiTargetValue::skullIndex, bot->GetGUID(), highestHealthUnit->GetGUID());
if (highestHealthUnit == leviathanMkII) if (highestHealthUnit == leviathanMkII)
{ {
if (AI_VALUE(std::string, "rti") == "skull") if (AI_VALUE(std::string, "rti") == "skull")
{
botAI->GetAiObjectContext()->GetValue<std::string>("rti")->Set("skull"); botAI->GetAiObjectContext()->GetValue<std::string>("rti")->Set("skull");
}
} }
else else
{ {
group->SetTargetIcon(RtiTargetValue::crossIndex, bot->GetGUID(), leviathanMkII->GetGUID()); group->SetTargetIcon(RtiTargetValue::crossIndex, bot->GetGUID(), leviathanMkII->GetGUID());
if (AI_VALUE(std::string, "rti") != "cross") if (AI_VALUE(std::string, "rti") != "cross")
{
botAI->GetAiObjectContext()->GetValue<std::string>("rti")->Set("cross"); botAI->GetAiObjectContext()->GetValue<std::string>("rti")->Set("cross");
}
} }
botAI->DoSpecificAction("attack rti target"); botAI->DoSpecificAction("attack rti target");
@ -2614,13 +2470,9 @@ bool MimironCheatAction::Execute(Event /*event*/)
continue; continue;
if (unit->GetEntry() == NPC_PROXIMITY_MINE) if (unit->GetEntry() == NPC_PROXIMITY_MINE)
{
unit->Kill(bot, unit); unit->Kill(bot, unit);
}
else if (unit->GetEntry() == NPC_BOMB_BOT) else if (unit->GetEntry() == NPC_BOMB_BOT)
{
unit->Kill(bot, unit); unit->Kill(bot, unit);
}
} }
return true; return true;
@ -2631,9 +2483,7 @@ bool VezaxCheatAction::Execute(Event /*event*/)
// Restore bot's mana to full // Restore bot's mana to full
uint32 maxMana = bot->GetMaxPower(POWER_MANA); uint32 maxMana = bot->GetMaxPower(POWER_MANA);
if (maxMana > 0) if (maxMana > 0)
{
bot->SetPower(POWER_MANA, maxMana); bot->SetPower(POWER_MANA, maxMana);
}
return true; return true;
} }
@ -2643,9 +2493,7 @@ bool VezaxShadowCrashAction::Execute(Event /*event*/)
// Find General Vezax boss // Find General Vezax boss
Unit* boss = AI_VALUE2(Unit*, "find target", "general vezax"); Unit* boss = AI_VALUE2(Unit*, "find target", "general vezax");
if (!boss || !boss->IsAlive()) if (!boss || !boss->IsAlive())
{
return false; return false;
}
// Get bot's current position relative to boss // Get bot's current position relative to boss
float bossX = boss->GetPositionX(); float bossX = boss->GetPositionX();
@ -2664,9 +2512,7 @@ bool VezaxShadowCrashAction::Execute(Event /*event*/)
// If too close or too far, adjust distance first // If too close or too far, adjust distance first
if (currentDistance < desiredDistance - 2.0f || currentDistance > desiredDistance + 2.0f) if (currentDistance < desiredDistance - 2.0f || currentDistance > desiredDistance + 2.0f)
{
currentDistance = desiredDistance; currentDistance = desiredDistance;
}
// Calculate movement increment - move in increments around the boss // Calculate movement increment - move in increments around the boss
float angleIncrement = M_PI / 10; float angleIncrement = M_PI / 10;
@ -2696,15 +2542,11 @@ bool YoggSaronOminousCloudCheatAction::Execute(Event /*event*/)
Unit* boss = yoggSaronTrigger.GetSaraIfAlive(); Unit* boss = yoggSaronTrigger.GetSaraIfAlive();
if (!boss) if (!boss)
{
return false; return false;
}
Creature* target = boss->FindNearestCreature(NPC_OMINOUS_CLOUD, 25.0f); Creature* target = boss->FindNearestCreature(NPC_OMINOUS_CLOUD, 25.0f);
if (!target || !target->IsAlive()) if (!target || !target->IsAlive())
{
return false; return false;
}
target->Kill(bot, target); target->Kill(bot, target);
return true; return true;
@ -2730,9 +2572,7 @@ bool YoggSaronMarkTargetAction::Execute(Event /*event*/)
{ {
Group* group = bot->GetGroup(); Group* group = bot->GetGroup();
if (!group) if (!group)
{
return false; return false;
}
YoggSaronTrigger yoggSaronTrigger(botAI); YoggSaronTrigger yoggSaronTrigger(botAI);
if (yoggSaronTrigger.IsPhase2()) if (yoggSaronTrigger.IsPhase2())
@ -2741,9 +2581,7 @@ bool YoggSaronMarkTargetAction::Execute(Event /*event*/)
{ {
Unit* crusherTentacle = bot->FindNearestCreature(NPC_CRUSHER_TENTACLE, 200.0f, true); Unit* crusherTentacle = bot->FindNearestCreature(NPC_CRUSHER_TENTACLE, 200.0f, true);
if (crusherTentacle) if (crusherTentacle)
{
crusherTentacle->Kill(bot, crusherTentacle); crusherTentacle->Kill(bot, crusherTentacle);
}
} }
ObjectGuid currentMoonTarget = group->GetTargetIcon(RtiTargetValue::moonIndex); ObjectGuid currentMoonTarget = group->GetTargetIcon(RtiTargetValue::moonIndex);
@ -2761,9 +2599,7 @@ bool YoggSaronMarkTargetAction::Execute(Event /*event*/)
{ {
nextPossibleTarget = bot->FindNearestCreature(NPC_CORRUPTOR_TENTACLE, 200.0f, true); nextPossibleTarget = bot->FindNearestCreature(NPC_CORRUPTOR_TENTACLE, 200.0f, true);
if (!nextPossibleTarget) if (!nextPossibleTarget)
{
return false; return false;
}
} }
if (currentSkullTarget) if (currentSkullTarget)
@ -2772,9 +2608,7 @@ bool YoggSaronMarkTargetAction::Execute(Event /*event*/)
if (currentSkullUnit && currentSkullUnit->IsAlive() && if (currentSkullUnit && currentSkullUnit->IsAlive() &&
currentSkullUnit->GetGUID() == nextPossibleTarget->GetGUID()) currentSkullUnit->GetGUID() == nextPossibleTarget->GetGUID())
{
return false; return false;
}
} }
group->SetTargetIcon(RtiTargetValue::skullIndex, bot->GetGUID(), nextPossibleTarget->GetGUID()); group->SetTargetIcon(RtiTargetValue::skullIndex, bot->GetGUID(), nextPossibleTarget->GetGUID());
@ -2783,15 +2617,11 @@ bool YoggSaronMarkTargetAction::Execute(Event /*event*/)
{ {
TankFaceStrategy tankFaceStrategy(botAI); TankFaceStrategy tankFaceStrategy(botAI);
if (botAI->HasStrategy(tankFaceStrategy.getName(), BotState::BOT_STATE_COMBAT)) if (botAI->HasStrategy(tankFaceStrategy.getName(), BotState::BOT_STATE_COMBAT))
{
botAI->ChangeStrategy(REMOVE_STRATEGY_CHAR + tankFaceStrategy.getName(), BotState::BOT_STATE_COMBAT); botAI->ChangeStrategy(REMOVE_STRATEGY_CHAR + tankFaceStrategy.getName(), BotState::BOT_STATE_COMBAT);
}
TankAssistStrategy tankAssistStrategy(botAI); TankAssistStrategy tankAssistStrategy(botAI);
if (!botAI->HasStrategy(tankAssistStrategy.getName(), BotState::BOT_STATE_COMBAT)) if (!botAI->HasStrategy(tankAssistStrategy.getName(), BotState::BOT_STATE_COMBAT))
{
botAI->ChangeStrategy(ADD_STRATEGY_CHAR + tankAssistStrategy.getName(), BotState::BOT_STATE_COMBAT); botAI->ChangeStrategy(ADD_STRATEGY_CHAR + tankAssistStrategy.getName(), BotState::BOT_STATE_COMBAT);
}
GuidVector targets = AI_VALUE(GuidVector, "nearest npcs"); GuidVector targets = AI_VALUE(GuidVector, "nearest npcs");
@ -2801,9 +2631,7 @@ bool YoggSaronMarkTargetAction::Execute(Event /*event*/)
{ {
Unit* unit = botAI->GetUnit(guid); Unit* unit = botAI->GetUnit(guid);
if (!unit || !unit->IsAlive()) if (!unit || !unit->IsAlive())
{
continue; continue;
}
if ((unit->GetEntry() == NPC_IMMORTAL_GUARDIAN || unit->GetEntry() == NPC_MARKED_IMMORTAL_GUARDIAN) && if ((unit->GetEntry() == NPC_IMMORTAL_GUARDIAN || unit->GetEntry() == NPC_MARKED_IMMORTAL_GUARDIAN) &&
unit->GetHealthPct() > 10) unit->GetHealthPct() > 10)
@ -2821,13 +2649,9 @@ bool YoggSaronMarkTargetAction::Execute(Event /*event*/)
// Added because lunatic gaze freeze all bots and they can't attack // Added because lunatic gaze freeze all bots and they can't attack
// If someone fix it then this cheat can be removed // If someone fix it then this cheat can be removed
if (botAI->HasCheat(BotCheatMask::raid)) if (botAI->HasCheat(BotCheatMask::raid))
{
lowestHealthUnit->Kill(bot, lowestHealthUnit); lowestHealthUnit->Kill(bot, lowestHealthUnit);
}
else else
{
group->SetTargetIcon(RtiTargetValue::skullIndex, bot->GetGUID(), lowestHealthUnit->GetGUID()); group->SetTargetIcon(RtiTargetValue::skullIndex, bot->GetGUID(), lowestHealthUnit->GetGUID());
}
return true; return true;
} }
@ -2835,9 +2659,7 @@ bool YoggSaronMarkTargetAction::Execute(Event /*event*/)
ObjectGuid currentSkullTarget = group->GetTargetIcon(RtiTargetValue::skullIndex); ObjectGuid currentSkullTarget = group->GetTargetIcon(RtiTargetValue::skullIndex);
Unit* currentSkullUnit = nullptr; Unit* currentSkullUnit = nullptr;
if (currentSkullTarget) if (currentSkullTarget)
{
currentSkullUnit = botAI->GetUnit(currentSkullTarget); currentSkullUnit = botAI->GetUnit(currentSkullTarget);
}
if (!currentSkullUnit || currentSkullUnit->GetEntry() != NPC_YOGG_SARON) if (!currentSkullUnit || currentSkullUnit->GetEntry() != NPC_YOGG_SARON)
{ {
@ -2859,17 +2681,13 @@ bool YoggSaronBrainLinkAction::Execute(Event /*event*/)
{ {
Group* group = bot->GetGroup(); Group* group = bot->GetGroup();
if (!group) if (!group)
{
return false; return false;
}
for (GroupReference* gref = group->GetFirstMember(); gref; gref = gref->next()) for (GroupReference* gref = group->GetFirstMember(); gref; gref = gref->next())
{ {
Player* player = gref->GetSource(); Player* player = gref->GetSource();
if (player && player->IsAlive() && player->HasAura(SPELL_BRAIN_LINK) && player->GetGUID() != bot->GetGUID()) if (player && player->IsAlive() && player->HasAura(SPELL_BRAIN_LINK) && player->GetGUID() != bot->GetGUID())
{
return MoveNear(player, 10.0f, MovementPriority::MOVEMENT_FORCED); return MoveNear(player, 10.0f, MovementPriority::MOVEMENT_FORCED);
}
} }
return false; return false;
@ -2879,17 +2697,13 @@ bool YoggSaronMoveToEnterPortalAction::Execute(Event /*event*/)
{ {
Group* group = bot->GetGroup(); Group* group = bot->GetGroup();
if (!group) if (!group)
{
return false; return false;
}
bool isInBrainRoomTeam = false; bool isInBrainRoomTeam = false;
int portalNumber = 0; int portalNumber = 0;
int brainRoomTeamCount = 10; int brainRoomTeamCount = 10;
if (bot->GetRaidDifficulty() == Difficulty::RAID_DIFFICULTY_10MAN_NORMAL) if (bot->GetRaidDifficulty() == Difficulty::RAID_DIFFICULTY_10MAN_NORMAL)
{
brainRoomTeamCount = 4; brainRoomTeamCount = 4;
}
Player* master = botAI->GetMaster(); Player* master = botAI->GetMaster();
if (master && !botAI->IsTank(master)) if (master && !botAI->IsTank(master))
@ -2902,9 +2716,7 @@ bool YoggSaronMoveToEnterPortalAction::Execute(Event /*event*/)
{ {
Player* member = gref->GetSource(); Player* member = gref->GetSource();
if (!member || !member->IsAlive() || botAI->IsTank(member) || botAI->GetMaster()->GetGUID() == member->GetGUID()) if (!member || !member->IsAlive() || botAI->IsTank(member) || botAI->GetMaster()->GetGUID() == member->GetGUID())
{
continue; continue;
}
portalNumber++; portalNumber++;
if (member->GetGUID() == bot->GetGUID()) if (member->GetGUID() == bot->GetGUID())
@ -2915,15 +2727,11 @@ bool YoggSaronMoveToEnterPortalAction::Execute(Event /*event*/)
brainRoomTeamCount--; brainRoomTeamCount--;
if (brainRoomTeamCount == 0) if (brainRoomTeamCount == 0)
{
break; break;
}
} }
if (!isInBrainRoomTeam) if (!isInBrainRoomTeam)
{
return false; return false;
}
Position assignedPortalPosition = yoggPortalLoc[portalNumber - 1]; Position assignedPortalPosition = yoggPortalLoc[portalNumber - 1];
@ -2980,33 +2788,24 @@ bool YoggSaronBossRoomMovementCheatAction::Execute(Event /*event*/)
{ {
FollowMasterStrategy followMasterStrategy(botAI); FollowMasterStrategy followMasterStrategy(botAI);
if (botAI->HasStrategy(followMasterStrategy.getName(), BotState::BOT_STATE_NON_COMBAT)) if (botAI->HasStrategy(followMasterStrategy.getName(), BotState::BOT_STATE_NON_COMBAT))
{
botAI->ChangeStrategy(REMOVE_STRATEGY_CHAR + followMasterStrategy.getName(), BotState::BOT_STATE_NON_COMBAT); botAI->ChangeStrategy(REMOVE_STRATEGY_CHAR + followMasterStrategy.getName(), BotState::BOT_STATE_NON_COMBAT);
}
if (!botAI->HasCheat(BotCheatMask::raid)) if (!botAI->HasCheat(BotCheatMask::raid))
{
return false; return false;
}
Group* group = bot->GetGroup(); Group* group = bot->GetGroup();
if (!group) if (!group)
{
return false; return false;
}
ObjectGuid currentSkullTarget = group->GetTargetIcon(RtiTargetValue::skullIndex); ObjectGuid currentSkullTarget = group->GetTargetIcon(RtiTargetValue::skullIndex);
if (!currentSkullTarget) if (!currentSkullTarget)
{
return false; return false;
}
Unit* currentSkullUnit = botAI->GetUnit(currentSkullTarget); Unit* currentSkullUnit = botAI->GetUnit(currentSkullTarget);
if (!currentSkullUnit || !currentSkullUnit->IsAlive()) if (!currentSkullUnit || !currentSkullUnit->IsAlive())
{
return false; return false;
}
return bot->TeleportTo(bot->GetMapId(), currentSkullUnit->GetPositionX(), currentSkullUnit->GetPositionY(), return bot->TeleportTo(bot->GetMapId(), currentSkullUnit->GetPositionX(), currentSkullUnit->GetPositionY(),
currentSkullUnit->GetPositionZ(), bot->GetOrientation()); currentSkullUnit->GetPositionZ(), bot->GetOrientation());
@ -3014,19 +2813,15 @@ bool YoggSaronBossRoomMovementCheatAction::Execute(Event /*event*/)
bool YoggSaronUsePortalAction::Execute(Event /*event*/) bool YoggSaronUsePortalAction::Execute(Event /*event*/)
{ {
Creature* assignedPortal = bot->FindNearestCreature(NPC_DESCEND_INTO_MADNESS, 2.0f, true); Creature* assignedPortal = bot->FindNearestCreature(NPC_DESCEND_INTO_MADNESS, 2.0f, true);
if (!assignedPortal) if (!assignedPortal)
{ return false;
return false;
}
FollowMasterStrategy followMasterStrategy(botAI); FollowMasterStrategy followMasterStrategy(botAI);
if (botAI->HasStrategy(followMasterStrategy.getName(), BotState::BOT_STATE_NON_COMBAT)) if (botAI->HasStrategy(followMasterStrategy.getName(), BotState::BOT_STATE_NON_COMBAT))
{ botAI->ChangeStrategy(ADD_STRATEGY_CHAR + followMasterStrategy.getName(), BotState::BOT_STATE_NON_COMBAT);
botAI->ChangeStrategy(ADD_STRATEGY_CHAR + followMasterStrategy.getName(), BotState::BOT_STATE_NON_COMBAT);
}
return assignedPortal->HandleSpellClick(bot); return assignedPortal->HandleSpellClick(bot);
} }
bool YoggSaronIllusionRoomAction::Execute(Event /*event*/) bool YoggSaronIllusionRoomAction::Execute(Event /*event*/)
@ -3067,15 +2862,11 @@ bool YoggSaronIllusionRoomAction::SetIllusionRtiTarget(YoggSaronTrigger yoggSaro
{ {
Unit* currentRtiTarget = yoggSaronTrigger.GetIllusionRoomRtiTarget(); Unit* currentRtiTarget = yoggSaronTrigger.GetIllusionRoomRtiTarget();
if (currentRtiTarget) if (currentRtiTarget)
{
return false; return false;
}
Unit* nextRtiTarget = yoggSaronTrigger.GetNextIllusionRoomRtiTarget(); Unit* nextRtiTarget = yoggSaronTrigger.GetNextIllusionRoomRtiTarget();
if (!nextRtiTarget) if (!nextRtiTarget)
{
return false; return false;
}
// If proper adds handling in illusion room will be implemented, then this can be removed // If proper adds handling in illusion room will be implemented, then this can be removed
if (botAI->HasCheat(BotCheatMask::raid)) if (botAI->HasCheat(BotCheatMask::raid))
@ -3090,9 +2881,7 @@ bool YoggSaronIllusionRoomAction::SetIllusionRtiTarget(YoggSaronTrigger yoggSaro
{ {
Group* group = bot->GetGroup(); Group* group = bot->GetGroup();
if (!group) if (!group)
{
return false; return false;
}
uint8 rtiIndex = RtiTargetValue::GetRtiIndex(AI_VALUE(std::string, "rti")); uint8 rtiIndex = RtiTargetValue::GetRtiIndex(AI_VALUE(std::string, "rti"));
group->SetTargetIcon(rtiIndex, bot->GetGUID(), nextRtiTarget->GetGUID()); group->SetTargetIcon(rtiIndex, bot->GetGUID(), nextRtiTarget->GetGUID());
@ -3104,23 +2893,17 @@ bool YoggSaronIllusionRoomAction::SetIllusionRtiTarget(YoggSaronTrigger yoggSaro
bool YoggSaronIllusionRoomAction::SetBrainRtiTarget(YoggSaronTrigger yoggSaronTrigger) bool YoggSaronIllusionRoomAction::SetBrainRtiTarget(YoggSaronTrigger yoggSaronTrigger)
{ {
if (AI_VALUE(std::string, "rti") == "square" || !yoggSaronTrigger.IsMasterIsInBrainRoom()) if (AI_VALUE(std::string, "rti") == "square" || !yoggSaronTrigger.IsMasterIsInBrainRoom())
{
return false; return false;
}
botAI->GetAiObjectContext()->GetValue<std::string>("rti")->Set("square"); botAI->GetAiObjectContext()->GetValue<std::string>("rti")->Set("square");
Group* group = bot->GetGroup(); Group* group = bot->GetGroup();
if (!group) if (!group)
{
return false; return false;
}
Creature* brain = bot->FindNearestCreature(NPC_BRAIN, 200.0f, true); Creature* brain = bot->FindNearestCreature(NPC_BRAIN, 200.0f, true);
if (!brain) if (!brain)
{
return false; return false;
}
group->SetTargetIcon(RtiTargetValue::squareIndex, bot->GetGUID(), brain->GetGUID()); group->SetTargetIcon(RtiTargetValue::squareIndex, bot->GetGUID(), brain->GetGUID());
@ -3160,7 +2943,6 @@ bool YoggSaronMoveToExitPortalAction::Execute(Event /*event*/)
if (botAI->HasCheat(BotCheatMask::raid)) if (botAI->HasCheat(BotCheatMask::raid))
bot->TeleportTo(bot->GetMapId(), portal->GetPositionX(), portal->GetPositionY(), portal->GetPositionZ(), bot->TeleportTo(bot->GetMapId(), portal->GetPositionX(), portal->GetPositionY(), portal->GetPositionZ(),
bot->GetOrientation()); bot->GetOrientation());
else else
MoveTo(bot->GetMapId(), portal->GetPositionX(), portal->GetPositionY(), portal->GetPositionZ(), false, MoveTo(bot->GetMapId(), portal->GetPositionX(), portal->GetPositionY(), portal->GetPositionZ(), false,
false, false, true, MovementPriority::MOVEMENT_FORCED, false, false, true, MovementPriority::MOVEMENT_FORCED,
@ -3189,9 +2971,7 @@ bool YoggSaronLunaticGazeAction::Execute(Event /*event*/)
{ {
if (AI_VALUE(std::string, "rti") != "cross") if (AI_VALUE(std::string, "rti") != "cross")
botAI->GetAiObjectContext()->GetValue<std::string>("rti")->Set("cross"); botAI->GetAiObjectContext()->GetValue<std::string>("rti")->Set("cross");
} }
return true; return true;
} }

View File

@ -852,8 +852,6 @@ bool ThorimMarkDpsTargetTrigger::IsActive()
Unit* runicColossus = AI_VALUE2(Unit*, "find target", "runic colossus"); Unit* runicColossus = AI_VALUE2(Unit*, "find target", "runic colossus");
Unit* ancientRuneGiant = AI_VALUE2(Unit*, "find target", "ancient rune giant"); Unit* ancientRuneGiant = AI_VALUE2(Unit*, "find target", "ancient rune giant");
Unit* ironHonorGuard = AI_VALUE2(Unit*, "find target", "iron ring guard");
Unit* ironRingGuard = AI_VALUE2(Unit*, "find target", "iron honor guard");
if (acolyte && acolyte->IsAlive() && (!currentCrossUnit || currentCrossUnit->GetEntry() != acolyte->GetEntry())) if (acolyte && acolyte->IsAlive() && (!currentCrossUnit || currentCrossUnit->GetEntry() != acolyte->GetEntry()))
return true; return true;
@ -1179,8 +1177,6 @@ bool MimironPhase1PositioningTrigger::IsActive()
} }
Unit* leviathanMkII = nullptr; Unit* leviathanMkII = nullptr;
Unit* vx001 = nullptr;
Unit* aerialCommandUnit = nullptr;
GuidVector targets = AI_VALUE(GuidVector, "possible targets"); GuidVector targets = AI_VALUE(GuidVector, "possible targets");
Unit* target = nullptr; Unit* target = nullptr;
@ -1192,19 +1188,14 @@ bool MimironPhase1PositioningTrigger::IsActive()
if (target->GetEntry() == NPC_LEVIATHAN_MKII) if (target->GetEntry() == NPC_LEVIATHAN_MKII)
leviathanMkII = target; leviathanMkII = target;
else if (target->GetEntry() == NPC_VX001) else if (target->GetEntry() == NPC_VX001)
return false; return false;
else if (target->GetEntry() == NPC_AERIAL_COMMAND_UNIT) else if (target->GetEntry() == NPC_AERIAL_COMMAND_UNIT)
return false; return false;
} }
if (!leviathanMkII || !leviathanMkII->IsAlive()) if (!leviathanMkII || !leviathanMkII->IsAlive())
{
return false; return false;
}
return AI_VALUE(float, "disperse distance") != 6.0f; return AI_VALUE(float, "disperse distance") != 6.0f;
} }
@ -1215,9 +1206,7 @@ bool MimironP3Wx2LaserBarrageTrigger::IsActive()
// Check boss and it is alive // Check boss and it is alive
if (!boss || !boss->IsAlive()) if (!boss || !boss->IsAlive())
{
return false; return false;
}
bool isCasting = boss->HasUnitState(UNIT_STATE_CASTING); bool isCasting = boss->HasUnitState(UNIT_STATE_CASTING);
bool isP3WX2LaserBarrage = boss->FindCurrentSpellBySpellId(SPELL_SPINNING_UP) || bool isP3WX2LaserBarrage = boss->FindCurrentSpellBySpellId(SPELL_SPINNING_UP) ||
@ -1230,9 +1219,7 @@ bool MimironP3Wx2LaserBarrageTrigger::IsActive()
boss->HasAura(SPELL_P3WX2_LASER_BARRAGE_AURA_1) || boss->HasAura(SPELL_P3WX2_LASER_BARRAGE_AURA_2); boss->HasAura(SPELL_P3WX2_LASER_BARRAGE_AURA_1) || boss->HasAura(SPELL_P3WX2_LASER_BARRAGE_AURA_2);
if ((!isCasting && !hasP3WX2LaserBarrageAura) || !isP3WX2LaserBarrage) if ((!isCasting && !hasP3WX2LaserBarrageAura) || !isP3WX2LaserBarrage)
{
return false; return false;
}
return true; return true;
} }
@ -1252,51 +1239,33 @@ bool MimironRapidBurstTrigger::IsActive()
continue; continue;
if (target->GetEntry() == NPC_LEVIATHAN_MKII) if (target->GetEntry() == NPC_LEVIATHAN_MKII)
{
leviathanMkII = target; leviathanMkII = target;
}
else if (target->GetEntry() == NPC_VX001) else if (target->GetEntry() == NPC_VX001)
{
vx001 = target; vx001 = target;
}
else if (target->GetEntry() == NPC_AERIAL_COMMAND_UNIT) else if (target->GetEntry() == NPC_AERIAL_COMMAND_UNIT)
{
aerialCommandUnit = target; aerialCommandUnit = target;
}
} }
if (!vx001 || !vx001->IsAlive()) if (!vx001 || !vx001->IsAlive())
{
return false; return false;
}
if (leviathanMkII && leviathanMkII->HasUnitState(UNIT_STATE_CASTING) && if (leviathanMkII && leviathanMkII->HasUnitState(UNIT_STATE_CASTING) &&
leviathanMkII->FindCurrentSpellBySpellId(SPELL_SHOCK_BLAST)) leviathanMkII->FindCurrentSpellBySpellId(SPELL_SHOCK_BLAST))
{
return false; return false;
}
if (botAI->IsMainTank(bot) && leviathanMkII && leviathanMkII->IsAlive() && leviathanMkII->GetVictim() != bot) if (botAI->IsMainTank(bot) && leviathanMkII && leviathanMkII->IsAlive() && leviathanMkII->GetVictim() != bot)
{
return false; return false;
}
if (botAI->IsMelee(bot) && !botAI->IsMainTank(bot) && leviathanMkII && aerialCommandUnit) if (botAI->IsMelee(bot) && !botAI->IsMainTank(bot) && leviathanMkII && aerialCommandUnit)
{
return false; return false;
}
MimironP3Wx2LaserBarrageTrigger mimironP3Wx2LaserBarrageTrigger(botAI); MimironP3Wx2LaserBarrageTrigger mimironP3Wx2LaserBarrageTrigger(botAI);
if (mimironP3Wx2LaserBarrageTrigger.IsActive()) if (mimironP3Wx2LaserBarrageTrigger.IsActive())
{
return false; return false;
}
Group* group = bot->GetGroup(); Group* group = bot->GetGroup();
if (!group) if (!group)
{
return false; return false;
}
uint32 memberSpotNumber = 0; uint32 memberSpotNumber = 0;
Position memberPosition; Position memberPosition;
@ -1355,9 +1324,7 @@ bool MimironRapidBurstTrigger::IsActive()
memberSpotNumber++; memberSpotNumber++;
if (memberSpotNumber == 3) if (memberSpotNumber == 3)
{
memberSpotNumber = 0; memberSpotNumber = 0;
}
} }
GuidVector npcs = AI_VALUE(GuidVector, "nearest npcs"); GuidVector npcs = AI_VALUE(GuidVector, "nearest npcs");
@ -1390,7 +1357,6 @@ bool MimironAerialCommandUnitTrigger::IsActive()
Unit* leviathanMkII = nullptr; Unit* leviathanMkII = nullptr;
Unit* vx001 = nullptr; Unit* vx001 = nullptr;
Unit* aerialCommandUnit = nullptr; Unit* aerialCommandUnit = nullptr;
//Unit* bombBot = nullptr;
Unit* assaultBot = nullptr; Unit* assaultBot = nullptr;
GuidVector targets = AI_VALUE(GuidVector, "possible targets"); GuidVector targets = AI_VALUE(GuidVector, "possible targets");
@ -1402,69 +1368,41 @@ bool MimironAerialCommandUnitTrigger::IsActive()
continue; continue;
if (target->GetEntry() == NPC_LEVIATHAN_MKII) if (target->GetEntry() == NPC_LEVIATHAN_MKII)
{
leviathanMkII = target; leviathanMkII = target;
}
else if (target->GetEntry() == NPC_VX001) else if (target->GetEntry() == NPC_VX001)
{
vx001 = target; vx001 = target;
}
else if (target->GetEntry() == NPC_AERIAL_COMMAND_UNIT) else if (target->GetEntry() == NPC_AERIAL_COMMAND_UNIT)
{
aerialCommandUnit = target; aerialCommandUnit = target;
}
//else if (target->GetEntry() == NPC_BOMB_BOT)
//{
// bombBot = target;
//}
else if (target->GetEntry() == NPC_ASSAULT_BOT) else if (target->GetEntry() == NPC_ASSAULT_BOT)
{
assaultBot = target; assaultBot = target;
}
} }
if (!aerialCommandUnit || !aerialCommandUnit->IsAlive() || leviathanMkII || vx001) if (!aerialCommandUnit || !aerialCommandUnit->IsAlive() || leviathanMkII || vx001)
{
return false; return false;
}
if (!botAI->IsRanged(bot) && !botAI->IsMainTank(bot) && !botAI->IsAssistTankOfIndex(bot, 0)) if (!botAI->IsRanged(bot) && !botAI->IsMainTank(bot) && !botAI->IsAssistTankOfIndex(bot, 0))
{
return false; return false;
}
if (botAI->IsMainTank(bot) || botAI->IsAssistTankOfIndex(bot, 0)) if (botAI->IsMainTank(bot) || botAI->IsAssistTankOfIndex(bot, 0))
{ {
Group* group = bot->GetGroup(); Group* group = bot->GetGroup();
if (!group) if (!group)
{
return false; return false;
}
ObjectGuid skullTarget = group->GetTargetIcon(RtiTargetValue::skullIndex); ObjectGuid skullTarget = group->GetTargetIcon(RtiTargetValue::skullIndex);
ObjectGuid crossTarget = group->GetTargetIcon(RtiTargetValue::crossIndex); ObjectGuid crossTarget = group->GetTargetIcon(RtiTargetValue::crossIndex);
//if (bombBot && bombBot->GetGUID() != crossTarget)
//{
// return true;
//}
if (!crossTarget || aerialCommandUnit->GetGUID() != crossTarget) if (!crossTarget || aerialCommandUnit->GetGUID() != crossTarget)
{
return true; return true;
}
else if (assaultBot && (!skullTarget || assaultBot->GetGUID() != skullTarget)) else if (assaultBot && (!skullTarget || assaultBot->GetGUID() != skullTarget))
{
return true; return true;
}
return false; return false;
} }
std::string rtiMark = AI_VALUE(std::string, "rti"); std::string rtiMark = AI_VALUE(std::string, "rti");
if (rtiMark != "cross") if (rtiMark != "cross")
{
return true; return true;
}
return false; return false;
} }
@ -1475,16 +1413,12 @@ bool MimironRocketStrikeTrigger::IsActive()
// Check boss and it is alive // Check boss and it is alive
if (!boss || !boss->IsAlive()) if (!boss || !boss->IsAlive())
{
return false; return false;
}
Creature* rocketStrikeN = bot->FindNearestCreature(NPC_ROCKET_STRIKE_N, 100.0f); Creature* rocketStrikeN = bot->FindNearestCreature(NPC_ROCKET_STRIKE_N, 100.0f);
if (!rocketStrikeN) if (!rocketStrikeN)
{
return false; return false;
}
return bot->GetDistance2d(rocketStrikeN->GetPositionX(), rocketStrikeN->GetPositionY()) <= 10.0f; return bot->GetDistance2d(rocketStrikeN->GetPositionX(), rocketStrikeN->GetPositionY()) <= 10.0f;
} }
@ -1497,9 +1431,7 @@ bool MimironPhase4MarkDpsTrigger::IsActive()
Group* group = bot->GetGroup(); Group* group = bot->GetGroup();
if (!group) if (!group)
{
return false; return false;
}
GuidVector targets = AI_VALUE(GuidVector, "possible targets"); GuidVector targets = AI_VALUE(GuidVector, "possible targets");
Unit* target = nullptr; Unit* target = nullptr;
@ -1510,23 +1442,15 @@ bool MimironPhase4MarkDpsTrigger::IsActive()
continue; continue;
if (target->GetEntry() == NPC_LEVIATHAN_MKII) if (target->GetEntry() == NPC_LEVIATHAN_MKII)
{
leviathanMkII = target; leviathanMkII = target;
}
else if (target->GetEntry() == NPC_VX001) else if (target->GetEntry() == NPC_VX001)
{
vx001 = target; vx001 = target;
}
else if (target->GetEntry() == NPC_AERIAL_COMMAND_UNIT) else if (target->GetEntry() == NPC_AERIAL_COMMAND_UNIT)
{
aerialCommandUnit = target; aerialCommandUnit = target;
}
} }
if (!leviathanMkII || !vx001 || !aerialCommandUnit) if (!leviathanMkII || !vx001 || !aerialCommandUnit)
{
return false; return false;
}
if (botAI->IsMainTank(bot)) if (botAI->IsMainTank(bot))
{ {
@ -1544,22 +1468,16 @@ bool MimironPhase4MarkDpsTrigger::IsActive()
highestHealthUnit = vx001; highestHealthUnit = vx001;
} }
if (aerialCommandUnit && aerialCommandUnit->GetHealth() > highestHealth) if (aerialCommandUnit && aerialCommandUnit->GetHealth() > highestHealth)
{
highestHealthUnit = aerialCommandUnit; highestHealthUnit = aerialCommandUnit;
}
ObjectGuid skullTarget = group->GetTargetIcon(RtiTargetValue::skullIndex); ObjectGuid skullTarget = group->GetTargetIcon(RtiTargetValue::skullIndex);
if (!skullTarget) if (!skullTarget)
{
return true; return true;
}
return highestHealthUnit->GetGUID() != skullTarget; return highestHealthUnit->GetGUID() != skullTarget;
} }
else else
{
return AI_VALUE(std::string, "rti") != "skull"; return AI_VALUE(std::string, "rti") != "skull";
}
return false; return false;
} }
@ -1567,14 +1485,10 @@ bool MimironPhase4MarkDpsTrigger::IsActive()
bool MimironCheatTrigger::IsActive() bool MimironCheatTrigger::IsActive()
{ {
if (!botAI->HasCheat(BotCheatMask::raid)) if (!botAI->HasCheat(BotCheatMask::raid))
{
return false; return false;
}
if (!botAI->IsMainTank(bot)) if (!botAI->IsMainTank(bot))
{
return false; return false;
}
GuidVector targets = AI_VALUE(GuidVector, "nearest npcs"); GuidVector targets = AI_VALUE(GuidVector, "nearest npcs");
for (const ObjectGuid& guid : targets) for (const ObjectGuid& guid : targets)
@ -1584,13 +1498,9 @@ bool MimironCheatTrigger::IsActive()
continue; continue;
if (unit->GetEntry() == NPC_PROXIMITY_MINE) if (unit->GetEntry() == NPC_PROXIMITY_MINE)
{
return true; return true;
}
else if (unit->GetEntry() == NPC_BOMB_BOT) else if (unit->GetEntry() == NPC_BOMB_BOT)
{
return true; return true;
}
} }
return false; return false;
@ -1599,22 +1509,16 @@ bool MimironCheatTrigger::IsActive()
bool VezaxCheatTrigger::IsActive() bool VezaxCheatTrigger::IsActive()
{ {
if (!botAI->HasCheat(BotCheatMask::raid)) if (!botAI->HasCheat(BotCheatMask::raid))
{
return false; return false;
}
Unit* boss = AI_VALUE2(Unit*, "find target", "general vezax"); Unit* boss = AI_VALUE2(Unit*, "find target", "general vezax");
// Check boss and it is alive // Check boss and it is alive
if (!boss || !boss->IsAlive()) if (!boss || !boss->IsAlive())
{
return false; return false;
}
if (!AI_VALUE2(bool, "has mana", "self target")) if (!AI_VALUE2(bool, "has mana", "self target"))
{
return false; return false;
}
return AI_VALUE2(uint8, "mana", "self target") < sPlayerbotAIConfig.lowMana; return AI_VALUE2(uint8, "mana", "self target") < sPlayerbotAIConfig.lowMana;
} }
@ -1625,9 +1529,7 @@ bool VezaxShadowCrashTrigger::IsActive()
// Check boss and it is alive // Check boss and it is alive
if (!boss || !boss->IsAlive()) if (!boss || !boss->IsAlive())
{
return false; return false;
}
return botAI->HasAura(SPELL_VEZAX_SHADOW_CRASH, bot); return botAI->HasAura(SPELL_VEZAX_SHADOW_CRASH, bot);
} }
@ -1638,14 +1540,10 @@ bool VezaxMarkOfTheFacelessTrigger::IsActive()
// Check boss and it is alive // Check boss and it is alive
if (!boss || !boss->IsAlive()) if (!boss || !boss->IsAlive())
{
return false; return false;
}
if (!botAI->HasAura(SPELL_MARK_OF_THE_FACELESS, bot)) if (!botAI->HasAura(SPELL_MARK_OF_THE_FACELESS, bot))
{
return false; return false;
}
float distance = bot->GetDistance2d(ULDUAR_VEZAX_MARK_OF_THE_FACELESS_SPOT.GetPositionX(), float distance = bot->GetDistance2d(ULDUAR_VEZAX_MARK_OF_THE_FACELESS_SPOT.GetPositionX(),
ULDUAR_VEZAX_MARK_OF_THE_FACELESS_SPOT.GetPositionY()); ULDUAR_VEZAX_MARK_OF_THE_FACELESS_SPOT.GetPositionY());
@ -1657,9 +1555,8 @@ Unit* YoggSaronTrigger::GetSaraIfAlive()
{ {
Unit* sara = AI_VALUE2(Unit*, "find target", "sara"); Unit* sara = AI_VALUE2(Unit*, "find target", "sara");
if (!sara || !sara->IsAlive()) if (!sara || !sara->IsAlive())
{
return nullptr; return nullptr;
}
return sara; return sara;
} }
@ -1689,9 +1586,7 @@ bool YoggSaronTrigger::IsYoggSaronFight()
Unit* yoggsaron = AI_VALUE2(Unit*, "find target", "yogg-saron"); Unit* yoggsaron = AI_VALUE2(Unit*, "find target", "yogg-saron");
if ((sara && sara->IsAlive()) || (yoggsaron && yoggsaron->IsAlive())) if ((sara && sara->IsAlive()) || (yoggsaron && yoggsaron->IsAlive()))
{
return true; return true;
}
return false; return false;
} }
@ -1699,24 +1594,16 @@ bool YoggSaronTrigger::IsYoggSaronFight()
bool YoggSaronTrigger::IsInIllusionRoom() bool YoggSaronTrigger::IsInIllusionRoom()
{ {
if (!IsInBrainLevel()) if (!IsInBrainLevel())
{
return false; return false;
}
if (IsInStormwindKeeperIllusion()) if (IsInStormwindKeeperIllusion())
{
return true; return true;
}
if (IsInIcecrownKeeperIllusion()) if (IsInIcecrownKeeperIllusion())
{
return true; return true;
}
if (IsInChamberOfTheAspectsIllusion()) if (IsInChamberOfTheAspectsIllusion())
{
return true; return true;
}
return false; return false;
} }
@ -1753,9 +1640,7 @@ bool YoggSaronTrigger::IsMasterIsInBrainRoom()
Player* master = botAI->GetMaster(); Player* master = botAI->GetMaster();
if (!master) if (!master)
{
return false; return false;
}
return master->GetDistance2d(ULDUAR_YOGG_SARON_BRAIN_ROOM_MIDDLE.GetPositionX(), return master->GetDistance2d(ULDUAR_YOGG_SARON_BRAIN_ROOM_MIDDLE.GetPositionX(),
ULDUAR_YOGG_SARON_BRAIN_ROOM_MIDDLE.GetPositionY()) < ULDUAR_YOGG_SARON_BRAIN_ROOM_MIDDLE.GetPositionY()) <
@ -1766,43 +1651,29 @@ bool YoggSaronTrigger::IsMasterIsInBrainRoom()
Position YoggSaronTrigger::GetIllusionRoomEntrancePosition() Position YoggSaronTrigger::GetIllusionRoomEntrancePosition()
{ {
if (IsInChamberOfTheAspectsIllusion()) if (IsInChamberOfTheAspectsIllusion())
{
return ULDUAR_YOGG_SARON_CHAMBER_OF_ASPECTS_ENTRANCE; return ULDUAR_YOGG_SARON_CHAMBER_OF_ASPECTS_ENTRANCE;
}
else if (IsInIcecrownKeeperIllusion()) else if (IsInIcecrownKeeperIllusion())
{
return ULDUAR_YOGG_SARON_ICECROWN_CITADEL_ENTRANCE; return ULDUAR_YOGG_SARON_ICECROWN_CITADEL_ENTRANCE;
}
else if (IsInStormwindKeeperIllusion()) else if (IsInStormwindKeeperIllusion())
{
return ULDUAR_YOGG_SARON_STORMWIND_KEEPER_ENTRANCE; return ULDUAR_YOGG_SARON_STORMWIND_KEEPER_ENTRANCE;
}
else else
{
return Position(); return Position();
}
} }
Unit* YoggSaronTrigger::GetIllusionRoomRtiTarget() Unit* YoggSaronTrigger::GetIllusionRoomRtiTarget()
{ {
Group* group = bot->GetGroup(); Group* group = bot->GetGroup();
if (!group) if (!group)
{
return nullptr; return nullptr;
}
int32_t rtiIndex = RtiTargetValue::GetRtiIndex(AI_VALUE(std::string, "rti")); int32_t rtiIndex = RtiTargetValue::GetRtiIndex(AI_VALUE(std::string, "rti"));
if (rtiIndex == -1) if (rtiIndex == -1)
{
return nullptr; // Invalid RTI mark return nullptr; // Invalid RTI mark
}
ObjectGuid currentRtiTarget = group->GetTargetIcon(rtiIndex); ObjectGuid currentRtiTarget = group->GetTargetIcon(rtiIndex);
Unit* currentRtiTargetUnit = botAI->GetUnit(currentRtiTarget); Unit* currentRtiTargetUnit = botAI->GetUnit(currentRtiTarget);
if (!currentRtiTargetUnit || !currentRtiTargetUnit->IsAlive()) if (!currentRtiTargetUnit || !currentRtiTargetUnit->IsAlive())
{
currentRtiTargetUnit = nullptr; currentRtiTargetUnit = nullptr;
}
return currentRtiTargetUnit; return currentRtiTargetUnit;
} }
@ -1812,13 +1683,10 @@ Unit* YoggSaronTrigger::GetNextIllusionRoomRtiTarget()
float detectionRadius = 0.0f; float detectionRadius = 0.0f;
if (IsInStormwindKeeperIllusion()) if (IsInStormwindKeeperIllusion())
detectionRadius = ULDUAR_YOGG_SARON_STORMWIND_KEEPER_RADIUS; detectionRadius = ULDUAR_YOGG_SARON_STORMWIND_KEEPER_RADIUS;
else if (IsInIcecrownKeeperIllusion()) else if (IsInIcecrownKeeperIllusion())
detectionRadius = ULDUAR_YOGG_SARON_ICECROWN_CITADEL_RADIUS; detectionRadius = ULDUAR_YOGG_SARON_ICECROWN_CITADEL_RADIUS;
else if (IsInChamberOfTheAspectsIllusion()) else if (IsInChamberOfTheAspectsIllusion())
detectionRadius = ULDUAR_YOGG_SARON_CHAMBER_OF_ASPECTS_RADIUS; detectionRadius = ULDUAR_YOGG_SARON_CHAMBER_OF_ASPECTS_RADIUS;
else else
return nullptr; return nullptr;
@ -1855,9 +1723,7 @@ Unit* YoggSaronTrigger::GetNextIllusionRoomRtiTarget()
} }
if (nextIllusionRoomRtiTarget) if (nextIllusionRoomRtiTarget)
{
return nextIllusionRoomRtiTarget; return nextIllusionRoomRtiTarget;
}
if (IsInStormwindKeeperIllusion()) if (IsInStormwindKeeperIllusion())
{ {
@ -1969,9 +1835,7 @@ bool YoggSaronMarkTargetTrigger::IsActive()
ObjectGuid currentMoonTarget = group->GetTargetIcon(RtiTargetValue::moonIndex); ObjectGuid currentMoonTarget = group->GetTargetIcon(RtiTargetValue::moonIndex);
Creature* yogg_saron = bot->FindNearestCreature(NPC_YOGG_SARON, 200.0f, true); Creature* yogg_saron = bot->FindNearestCreature(NPC_YOGG_SARON, 200.0f, true);
if (!currentMoonTarget || currentMoonTarget != yogg_saron->GetGUID()) if (!currentMoonTarget || currentMoonTarget != yogg_saron->GetGUID())
{
return true; return true;
}
ObjectGuid currentSkullTarget = group->GetTargetIcon(RtiTargetValue::skullIndex); ObjectGuid currentSkullTarget = group->GetTargetIcon(RtiTargetValue::skullIndex);
@ -2234,9 +2098,7 @@ bool YoggSaronPhase3PositioningTrigger::IsActive()
if (botAI->IsRanged(bot) && bot->GetDistance2d(ULDUAR_YOGG_SARON_PHASE_3_RANGED_SPOT.GetPositionX(), if (botAI->IsRanged(bot) && bot->GetDistance2d(ULDUAR_YOGG_SARON_PHASE_3_RANGED_SPOT.GetPositionX(),
ULDUAR_YOGG_SARON_PHASE_3_RANGED_SPOT.GetPositionY()) > 15.0f) ULDUAR_YOGG_SARON_PHASE_3_RANGED_SPOT.GetPositionY()) > 15.0f)
{
return true; return true;
}
if (botAI->IsMelee(bot) && !botAI->IsTank(bot) && if (botAI->IsMelee(bot) && !botAI->IsTank(bot) &&
bot->GetDistance2d(ULDUAR_YOGG_SARON_PHASE_3_MELEE_SPOT.GetPositionX(), bot->GetDistance2d(ULDUAR_YOGG_SARON_PHASE_3_MELEE_SPOT.GetPositionX(),

View File

@ -435,7 +435,6 @@ bool NewRpgTravelFlightAction::Execute(Event /*event*/)
botAI->rpgInfo.ChangeToIdle(); botAI->rpgInfo.ChangeToIdle();
return true; return true;
} }
const TaxiNodesEntry* entry = sTaxiNodesStore.LookupEntry(data.toNode);
if (bot->GetDistance(flightMaster) > INTERACTION_DISTANCE) if (bot->GetDistance(flightMaster) > INTERACTION_DISTANCE)
return MoveFarTo(flightMaster); return MoveFarTo(flightMaster);

View File

@ -360,7 +360,6 @@ bool NewRpgBaseAction::IsWithinInteractionDist(Object* questGiver)
case TYPEID_GAMEOBJECT: case TYPEID_GAMEOBJECT:
{ {
ObjectGuid guid = questGiver->GetGUID(); ObjectGuid guid = questGiver->GetGUID();
GameobjectTypes type = GAMEOBJECT_TYPE_QUESTGIVER;
if (GameObject* go = bot->GetMap()->GetGameObject(guid)) if (GameObject* go = bot->GetMap()->GetGameObject(guid))
{ {
if (go->IsWithinDistInMap(bot)) if (go->IsWithinDistInMap(bot))
@ -546,7 +545,9 @@ bool NewRpgBaseAction::OrganizeQuestLog()
continue; continue;
const Quest* quest = sObjectMgr->GetQuestTemplate(questId); const Quest* quest = sObjectMgr->GetQuestTemplate(questId);
if (quest->GetZoneOrSort() < 0 || (quest->GetZoneOrSort() > 0 && quest->GetZoneOrSort() != bot->GetZoneId())) const int64_t botZoneId = this->bot->GetZoneId();
if (quest->GetZoneOrSort() < 0 || (quest->GetZoneOrSort() > 0 && quest->GetZoneOrSort() != botZoneId))
{ {
LOG_DEBUG("playerbots", "[New RPG] {} drop quest {}", bot->GetName(), questId); LOG_DEBUG("playerbots", "[New RPG] {} drop quest {}", bot->GetName(), questId);
WorldPacket packet(CMSG_QUESTLOG_REMOVE_QUEST); WorldPacket packet(CMSG_QUESTLOG_REMOVE_QUEST);

View File

@ -60,7 +60,10 @@ void NewRpgInfo::ChangeToIdle()
data = Idle{}; data = Idle{};
} }
bool NewRpgInfo::CanChangeTo(NewRpgStatus status) { return true; } bool NewRpgInfo::CanChangeTo(NewRpgStatus)
{
return true;
}
void NewRpgInfo::Reset() void NewRpgInfo::Reset()
{ {

View File

@ -37,12 +37,14 @@
#include "Ai/Raid/BlackwingLair/RaidBwlTriggerContext.h" #include "Ai/Raid/BlackwingLair/RaidBwlTriggerContext.h"
#include "Ai/Raid/Karazhan/RaidKarazhanActionContext.h" #include "Ai/Raid/Karazhan/RaidKarazhanActionContext.h"
#include "Ai/Raid/Karazhan/RaidKarazhanTriggerContext.h" #include "Ai/Raid/Karazhan/RaidKarazhanTriggerContext.h"
#include "Ai/Raid/Magtheridon/RaidMagtheridonActionContext.h"
#include "Ai/Raid/Magtheridon/RaidMagtheridonTriggerContext.h"
#include "Ai/Raid/GruulsLair/RaidGruulsLairActionContext.h" #include "Ai/Raid/GruulsLair/RaidGruulsLairActionContext.h"
#include "Ai/Raid/GruulsLair/RaidGruulsLairTriggerContext.h" #include "Ai/Raid/GruulsLair/RaidGruulsLairTriggerContext.h"
#include "Ai/Raid/Magtheridon/RaidMagtheridonActionContext.h"
#include "Ai/Raid/Magtheridon/RaidMagtheridonTriggerContext.h"
#include "Ai/Raid/SerpentshrineCavern/RaidSSCActionContext.h" #include "Ai/Raid/SerpentshrineCavern/RaidSSCActionContext.h"
#include "Ai/Raid/SerpentshrineCavern/RaidSSCTriggerContext.h" #include "Ai/Raid/SerpentshrineCavern/RaidSSCTriggerContext.h"
#include "Ai/Raid/TempestKeep/RaidTempestKeepActionContext.h"
#include "Ai/Raid/TempestKeep/RaidTempestKeepTriggerContext.h"
#include "Ai/Raid/EyeOfEternity/RaidEoEActionContext.h" #include "Ai/Raid/EyeOfEternity/RaidEoEActionContext.h"
#include "Ai/Raid/EyeOfEternity/RaidEoETriggerContext.h" #include "Ai/Raid/EyeOfEternity/RaidEoETriggerContext.h"
#include "Ai/Raid/VaultOfArchavon/RaidVoAActionContext.h" #include "Ai/Raid/VaultOfArchavon/RaidVoAActionContext.h"
@ -115,9 +117,10 @@ void AiObjectContext::BuildSharedActionContexts(SharedNamedObjectContextList<Act
actionContexts.Add(new RaidMcActionContext()); actionContexts.Add(new RaidMcActionContext());
actionContexts.Add(new RaidBwlActionContext()); actionContexts.Add(new RaidBwlActionContext());
actionContexts.Add(new RaidKarazhanActionContext()); actionContexts.Add(new RaidKarazhanActionContext());
actionContexts.Add(new RaidMagtheridonActionContext());
actionContexts.Add(new RaidGruulsLairActionContext()); actionContexts.Add(new RaidGruulsLairActionContext());
actionContexts.Add(new RaidMagtheridonActionContext());
actionContexts.Add(new RaidSSCActionContext()); actionContexts.Add(new RaidSSCActionContext());
actionContexts.Add(new RaidTempestKeepActionContext());
actionContexts.Add(new RaidOsActionContext()); actionContexts.Add(new RaidOsActionContext());
actionContexts.Add(new RaidEoEActionContext()); actionContexts.Add(new RaidEoEActionContext());
actionContexts.Add(new RaidVoAActionContext()); actionContexts.Add(new RaidVoAActionContext());
@ -150,9 +153,10 @@ void AiObjectContext::BuildSharedTriggerContexts(SharedNamedObjectContextList<Tr
triggerContexts.Add(new RaidMcTriggerContext()); triggerContexts.Add(new RaidMcTriggerContext());
triggerContexts.Add(new RaidBwlTriggerContext()); triggerContexts.Add(new RaidBwlTriggerContext());
triggerContexts.Add(new RaidKarazhanTriggerContext()); triggerContexts.Add(new RaidKarazhanTriggerContext());
triggerContexts.Add(new RaidMagtheridonTriggerContext());
triggerContexts.Add(new RaidGruulsLairTriggerContext()); triggerContexts.Add(new RaidGruulsLairTriggerContext());
triggerContexts.Add(new RaidMagtheridonTriggerContext());
triggerContexts.Add(new RaidSSCTriggerContext()); triggerContexts.Add(new RaidSSCTriggerContext());
triggerContexts.Add(new RaidTempestKeepTriggerContext());
triggerContexts.Add(new RaidOsTriggerContext()); triggerContexts.Add(new RaidOsTriggerContext());
triggerContexts.Add(new RaidEoETriggerContext()); triggerContexts.Add(new RaidEoETriggerContext());
triggerContexts.Add(new RaidVoATriggerContext()); triggerContexts.Add(new RaidVoATriggerContext());

View File

@ -948,8 +948,6 @@ void PlayerbotFactory::InitPet()
continue; continue;
if (co->Name.size() > 21) if (co->Name.size() > 21)
continue; continue;
uint32 guid = map->GenerateLowGuid<HighGuid::Pet>();
uint32 pet_number = sObjectMgr->GeneratePetNumber();
if (bot->GetPetStable() && bot->GetPetStable()->CurrentPet) if (bot->GetPetStable() && bot->GetPetStable()->CurrentPet)
{ {
auto petGuid = bot->GetPetStable()->CurrentPet.value(); // To correct the build warnin in VS auto petGuid = bot->GetPetStable()->CurrentPet.value(); // To correct the build warnin in VS
@ -1905,7 +1903,7 @@ void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance)
if (oldItem) if (oldItem)
continue; continue;
Item* newItem = bot->EquipNewItem(dest, bestItemForSlot, true); bot->EquipNewItem(dest, bestItemForSlot, true);
bot->AutoUnequipOffhandIfNeed(); bot->AutoUnequipOffhandIfNeed();
// if (newItem) // if (newItem)
// { // {
@ -1936,7 +1934,7 @@ void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance)
(slot != EQUIPMENT_SLOT_RANGED)) (slot != EQUIPMENT_SLOT_RANGED))
continue; continue;
if (Item* oldItem = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot)) if (bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot) != nullptr)
bot->DestroyItem(INVENTORY_SLOT_BAG_0, slot, true); bot->DestroyItem(INVENTORY_SLOT_BAG_0, slot, true);
std::vector<uint32>& ids = items[slot]; std::vector<uint32>& ids = items[slot];
@ -1973,12 +1971,9 @@ void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance)
continue; continue;
Item* newItem = bot->EquipNewItem(dest, bestItemForSlot, true); Item* newItem = bot->EquipNewItem(dest, bestItemForSlot, true);
bot->EquipNewItem(dest, bestItemForSlot, true);
bot->AutoUnequipOffhandIfNeed(); bot->AutoUnequipOffhandIfNeed();
// if (newItem)
// {
// newItem->AddToWorld();
// newItem->AddToUpdateQueueOf(bot);
// }
} }
} }
} }
@ -1991,16 +1986,10 @@ bool PlayerbotFactory::IsDesiredReplacement(Item* item)
ItemTemplate const* proto = item->GetTemplate(); ItemTemplate const* proto = item->GetTemplate();
uint32 requiredLevel = proto->RequiredLevel; uint32 requiredLevel = proto->RequiredLevel;
if (!requiredLevel) if (!requiredLevel)
{
return true; return true;
}
// if (!requiredLevel)
// {
// requiredLevel = sRandomItemMgr.GetMinLevelFromCache(proto->ItemId);
// }
uint32 delta = 1 + (80 - bot->GetLevel()) / 10; uint32 delta = 1 + (80 - bot->GetLevel()) / 10;
return proto->Quality < ITEM_QUALITY_RARE || int32(bot->GetLevel() - requiredLevel) > delta; return proto->Quality < ITEM_QUALITY_RARE || (bot->GetLevel() - requiredLevel) > delta;
} }
inline Item* StoreNewItemInInventorySlot(Player* player, uint32 newItemId, uint32 count) inline Item* StoreNewItemInInventorySlot(Player* player, uint32 newItemId, uint32 count)
@ -2010,9 +1999,7 @@ inline Item* StoreNewItemInInventorySlot(Player* player, uint32 newItemId, uint3
if (msg == EQUIP_ERR_OK) if (msg == EQUIP_ERR_OK)
{ {
if (Item* newItem = player->StoreNewItem(vDest, newItemId, true, Item::GenerateItemRandomPropertyId(newItemId))) if (Item* newItem = player->StoreNewItem(vDest, newItemId, true, Item::GenerateItemRandomPropertyId(newItemId)))
{
return newItem; return newItem;
}
} }
return nullptr; return nullptr;
@ -2875,7 +2862,6 @@ void PlayerbotFactory::AddPrevQuests(uint32 questId, std::list<uint32>& questIds
void PlayerbotFactory::InitQuests(std::list<uint32>& questMap, bool withRewardItem) void PlayerbotFactory::InitQuests(std::list<uint32>& questMap, bool withRewardItem)
{ {
uint32 count = 0;
for (std::list<uint32>::iterator i = questMap.begin(); i != questMap.end(); ++i) for (std::list<uint32>::iterator i = questMap.begin(); i != questMap.end(); ++i)
{ {
uint32 questId = *i; uint32 questId = *i;
@ -3280,7 +3266,6 @@ void PlayerbotFactory::InitFood()
void PlayerbotFactory::InitReagents() void PlayerbotFactory::InitReagents()
{ {
int specTab = AiFactory::GetPlayerSpecTab(bot);
std::vector<std::pair<uint32, uint32>> items; std::vector<std::pair<uint32, uint32>> items;
switch (bot->getClass()) switch (bot->getClass())
{ {
@ -4336,15 +4321,11 @@ void PlayerbotFactory::ApplyEnchantAndGemsNew(bool destroyOld)
continue; continue;
if (!item->IsFitToSpellRequirements(spellInfo)) if (!item->IsFitToSpellRequirements(spellInfo))
{
continue; continue;
}
uint32 requiredLevel = spellInfo->BaseLevel; uint32 requiredLevel = spellInfo->BaseLevel;
if (requiredLevel > bot->GetLevel()) if (requiredLevel > bot->GetLevel())
{
continue; continue;
}
// disable next expansion enchantments // disable next expansion enchantments
if (sPlayerbotAIConfig.limitEnchantExpansion && bot->GetLevel() <= 60 && enchantSpell >= 27899) if (sPlayerbotAIConfig.limitEnchantExpansion && bot->GetLevel() <= 60 && enchantSpell >= 27899)
@ -4364,9 +4345,8 @@ void PlayerbotFactory::ApplyEnchantAndGemsNew(bool destroyOld)
SpellItemEnchantmentEntry const* enchant = sSpellItemEnchantmentStore.LookupEntry(enchant_id); SpellItemEnchantmentEntry const* enchant = sSpellItemEnchantmentStore.LookupEntry(enchant_id);
if (!enchant || (enchant->slot != PERM_ENCHANTMENT_SLOT && enchant->slot != TEMP_ENCHANTMENT_SLOT)) if (!enchant || (enchant->slot != PERM_ENCHANTMENT_SLOT && enchant->slot != TEMP_ENCHANTMENT_SLOT))
{
continue; continue;
}
if (enchant->requiredSkill && if (enchant->requiredSkill &&
(!bot->HasSkill(enchant->requiredSkill) || (!bot->HasSkill(enchant->requiredSkill) ||
(bot->GetSkillValue(enchant->requiredSkill) < enchant->requiredSkillValue))) (bot->GetSkillValue(enchant->requiredSkill) < enchant->requiredSkillValue)))
@ -4374,9 +4354,8 @@ void PlayerbotFactory::ApplyEnchantAndGemsNew(bool destroyOld)
continue; continue;
} }
if (enchant->requiredLevel > bot->GetLevel()) if (enchant->requiredLevel > bot->GetLevel())
{
continue; continue;
}
float score = calculator.CalculateEnchant(enchant_id); float score = calculator.CalculateEnchant(enchant_id);
if (score >= bestScore) if (score >= bestScore)
{ {
@ -4399,11 +4378,9 @@ void PlayerbotFactory::ApplyEnchantAndGemsNew(bool destroyOld)
{ {
uint8 socketColor = item->GetTemplate()->Socket[enchant_slot - SOCK_ENCHANTMENT_SLOT].Color; uint8 socketColor = item->GetTemplate()->Socket[enchant_slot - SOCK_ENCHANTMENT_SLOT].Color;
if (!socketColor) if (!socketColor)
{
continue; continue;
}
int32 enchantIdChosen = -1; int32 enchantIdChosen = -1;
int32 colorChosen;
bool jewelersGemChosen; bool jewelersGemChosen;
float bestGemScore = -1; float bestGemScore = -1;
for (uint32& enchantGem : availableGems) for (uint32& enchantGem : availableGems)
@ -4448,7 +4425,6 @@ void PlayerbotFactory::ApplyEnchantAndGemsNew(bool destroyOld)
if (score > bestGemScore) if (score > bestGemScore)
{ {
enchantIdChosen = enchant_id; enchantIdChosen = enchant_id;
colorChosen = gemProperties->color;
bestGemScore = score; bestGemScore = score;
jewelersGemChosen = isJewelersGem; jewelersGemChosen = isJewelersGem;
} }

View File

@ -1556,6 +1556,9 @@ void PlayerbotAI::ApplyInstanceStrategies(uint32 mapId, bool tellMaster)
case 548: case 548:
strategyName = "ssc"; // Serpentshrine Cavern strategyName = "ssc"; // Serpentshrine Cavern
break; break;
case 550:
strategyName = "tempestkeep"; // Tempest Keep
break;
case 565: case 565:
strategyName = "gruulslair"; // Gruul's Lair strategyName = "gruulslair"; // Gruul's Lair
break; break;
@ -1797,98 +1800,104 @@ bool PlayerbotAI::IsCombo(Player* player)
bool PlayerbotAI::IsRangedDps(Player* player, bool bySpec) { return IsRanged(player, bySpec) && IsDps(player, bySpec); } bool PlayerbotAI::IsRangedDps(Player* player, bool bySpec) { return IsRanged(player, bySpec) && IsDps(player, bySpec); }
bool PlayerbotAI::IsAssistHealOfIndex(Player* player, int index, bool ignoreDeadPlayers) bool PlayerbotAI::IsAssistHealOfIndex(Player* player, uint8 index, bool ignoreDeadPlayers)
{ {
if (!IsHeal(player))
return false;
if (ignoreDeadPlayers && !player->IsAlive())
return false;
Group* group = player->GetGroup(); Group* group = player->GetGroup();
if (!group) if (!group)
return false; return false;
int counter = 0; uint8 totalAssistants = 0;
uint8 assistantsBeforePlayer = 0;
uint8 nonAssistantsBeforePlayer = 0;
bool playerFound = false;
// First, assistants
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
{ {
Player* member = ref->GetSource(); Player* member = ref->GetSource();
if (!member) if (!member || (ignoreDeadPlayers && !member->IsAlive()) || !IsHeal(member))
continue; continue;
if (ignoreDeadPlayers && !member->IsAlive()) bool isAssistant = group->IsAssistant(member->GetGUID());
continue;
if (group->IsAssistant(member->GetGUID()) && IsHeal(member)) if (isAssistant)
totalAssistants++;
if (member == player)
playerFound = true;
else if (!playerFound)
{ {
if (index == counter) if (isAssistant)
return player == member; assistantsBeforePlayer++;
counter++; else
nonAssistantsBeforePlayer++;
} }
} }
// If not enough assistants, get non-assistants if (!playerFound)
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) return false;
{
Player* member = ref->GetSource();
if (!member)
continue;
if (ignoreDeadPlayers && !member->IsAlive()) // If the player is an assistant, their index is just the number of assistants before them.
continue; // If they are a non-assistant, their index is shifted by the total number of assistants.
uint8 playerIndex = group->IsAssistant(player->GetGUID())
? assistantsBeforePlayer : (totalAssistants + nonAssistantsBeforePlayer);
if (!group->IsAssistant(member->GetGUID()) && IsHeal(member)) return playerIndex == index;
{
if (index == counter)
return player == member;
counter++;
}
}
return false;
} }
bool PlayerbotAI::IsAssistRangedDpsOfIndex(Player* player, int index, bool ignoreDeadPlayers) bool PlayerbotAI::IsAssistRangedDpsOfIndex(Player* player, uint8 index, bool ignoreDeadPlayers)
{ {
if (!IsRangedDps(player))
return false;
if (ignoreDeadPlayers && !player->IsAlive())
return false;
Group* group = player->GetGroup(); Group* group = player->GetGroup();
if (!group) if (!group)
return false; return false;
int counter = 0; uint8 totalAssistants = 0;
uint8 assistantsBeforePlayer = 0;
uint8 nonAssistantsBeforePlayer = 0;
bool playerFound = false;
// First, assistants
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
{ {
Player* member = ref->GetSource(); Player* member = ref->GetSource();
if (!member) if (!member || (ignoreDeadPlayers && !member->IsAlive()) || !IsRangedDps(member))
continue; continue;
if (ignoreDeadPlayers && !member->IsAlive()) bool isAssistant = group->IsAssistant(member->GetGUID());
continue;
if (group->IsAssistant(member->GetGUID()) && IsRangedDps(member)) if (isAssistant)
totalAssistants++;
if (member == player)
playerFound = true;
else if (!playerFound)
{ {
if (index == counter) if (isAssistant)
return player == member; assistantsBeforePlayer++;
counter++; else
nonAssistantsBeforePlayer++;
} }
} }
// If not enough assistants, get non-assistants if (!playerFound)
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) return false;
{
Player* member = ref->GetSource();
if (!member)
continue;
if (ignoreDeadPlayers && !member->IsAlive()) // If the player is an assistant, their index is just the number of assistants before them.
continue; // If they are a non-assistant, their index is shifted by the total number of assistants.
uint8 playerIndex = group->IsAssistant(player->GetGUID())
? assistantsBeforePlayer : (totalAssistants + nonAssistantsBeforePlayer);
if (!group->IsAssistant(member->GetGUID()) && IsRangedDps(member)) return playerIndex == index;
{
if (index == counter)
return player == member;
counter++;
}
}
return false;
} }
bool PlayerbotAI::HasAggro(Unit* unit) bool PlayerbotAI::HasAggro(Unit* unit)
@ -2226,43 +2235,44 @@ bool PlayerbotAI::IsDps(Player* player, bool bySpec)
return false; return false;
} }
bool PlayerbotAI::IsMainTank(Player* player) bool PlayerbotAI::IsMainTank(Player* player, bool ignoreMemberFlag)
{ {
Group* group = player->GetGroup(); Group* group = player->GetGroup();
if (!group) if (!group)
{
return IsTank(player); return IsTank(player);
}
ObjectGuid mainTank = ObjectGuid(); ObjectGuid mainTank = ObjectGuid();
Group::MemberSlotList const& slots = group->GetMemberSlots();
for (Group::member_citerator itr = slots.begin(); itr != slots.end(); ++itr) // (1) Check for main tank flag (any class or spec)
if (!ignoreMemberFlag)
{ {
if (itr->flags & MEMBER_FLAG_MAINTANK) Group::MemberSlotList const& slots = group->GetMemberSlots();
for (Group::member_citerator itr = slots.begin(); itr != slots.end(); ++itr)
{ {
mainTank = itr->guid; if (itr->flags & MEMBER_FLAG_MAINTANK)
break; {
mainTank = itr->guid;
break;
}
} }
if (mainTank != ObjectGuid::Empty)
return player->GetGUID() == mainTank;
} }
if (mainTank != ObjectGuid::Empty) // (2) If no main tank flag, return the first tank
{ if (!IsTank(player) || !player->IsAlive())
return player->GetGUID() == mainTank; return false;
}
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
{ {
Player* member = ref->GetSource(); Player* member = ref->GetSource();
if (!member) if (!member)
{
continue; continue;
}
if (IsTank(member) && member->IsAlive()) if (IsTank(member) && member->IsAlive())
{
return player->GetGUID() == member->GetGUID(); return player->GetGUID() == member->GetGUID();
}
} }
return false; return false;
@ -2281,47 +2291,31 @@ bool PlayerbotAI::IsBotMainTank(Player* player)
return false; return false;
if (IsMainTank(player)) if (IsMainTank(player))
{
return true; return true;
}
Group* group = player->GetGroup(); Group* group = player->GetGroup();
if (!group) if (!group)
{ return true;
return true; // If no group, consider the bot as main tank
}
int32 botAssistTankIndex = GetAssistTankIndex(player); int32 botAssistTankIndex = GetAssistTankIndex(player);
if (botAssistTankIndex == -1) if (botAssistTankIndex == -1)
{
return false; return false;
}
for (GroupReference* gref = group->GetFirstMember(); gref; gref = gref->next()) for (GroupReference* gref = group->GetFirstMember(); gref; gref = gref->next())
{ {
Player* member = gref->GetSource(); Player* member = gref->GetSource();
if (!member) if (!member)
{
continue; continue;
}
int32 memberAssistTankIndex = GetAssistTankIndex(member); int32 memberAssistTankIndex = GetAssistTankIndex(member);
if (memberAssistTankIndex == -1) if (memberAssistTankIndex == -1)
{
continue; continue;
}
if (memberAssistTankIndex == botAssistTankIndex && player == member) if (memberAssistTankIndex == botAssistTankIndex && player == member)
{
return true; return true;
}
if (memberAssistTankIndex < botAssistTankIndex && member->GetSession()->IsBot()) if (memberAssistTankIndex < botAssistTankIndex && member->GetSession()->IsBot())
{
return false; return false;
}
return false;
} }
return false; return false;
@ -2331,73 +2325,76 @@ uint32 PlayerbotAI::GetGroupTankNum(Player* player)
{ {
Group* group = player->GetGroup(); Group* group = player->GetGroup();
if (!group) if (!group)
{
return 0; return 0;
}
uint32 result = 0; uint32 result = 0;
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
{ {
Player* member = ref->GetSource(); Player* member = ref->GetSource();
if (!member) if (!member)
{
continue; continue;
}
if (IsTank(member) && member->IsAlive()) if (IsTank(member) && member->IsAlive())
{
result++; result++;
}
} }
return result; return result;
} }
bool PlayerbotAI::IsAssistTank(Player* player) { return IsTank(player) && !IsMainTank(player); } bool PlayerbotAI::IsAssistTank(Player* player)
bool PlayerbotAI::IsAssistTankOfIndex(Player* player, int index, bool ignoreDeadPlayers)
{ {
return IsTank(player) && !IsMainTank(player);
}
bool PlayerbotAI::IsAssistTankOfIndex(Player* player, uint8 index, bool ignoreDeadPlayers)
{
if (!IsAssistTank(player))
return false;
if (ignoreDeadPlayers && !player->IsAlive())
return false;
Group* group = player->GetGroup(); Group* group = player->GetGroup();
if (!group) if (!group)
return false; return false;
int counter = 0; uint8 totalAssistants = 0;
uint8 assistantsBeforePlayer = 0;
uint8 nonAssistantsBeforePlayer = 0;
bool playerFound = false;
// First, assistants
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
{ {
Player* member = ref->GetSource(); Player* member = ref->GetSource();
if (!member) if (!member || (ignoreDeadPlayers && !member->IsAlive()) || !IsAssistTank(member))
continue; continue;
if (ignoreDeadPlayers && !member->IsAlive()) bool isAssistant = group->IsAssistant(member->GetGUID());
continue;
if (group->IsAssistant(member->GetGUID()) && IsAssistTank(member)) if (isAssistant)
totalAssistants++;
if (member == player)
playerFound = true;
else if (!playerFound)
{ {
if (index == counter) if (isAssistant)
return player == member; assistantsBeforePlayer++;
counter++; else
nonAssistantsBeforePlayer++;
} }
} }
// If not enough assistants, get non-assistants if (!playerFound)
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) return false;
{
Player* member = ref->GetSource();
if (!member)
continue;
if (ignoreDeadPlayers && !member->IsAlive()) // If the player is an assistant, their index is just the number of assistants before them.
continue; // If they are a non-assistant, their index is shifted by the total number of assistants.
uint8 playerIndex = group->IsAssistant(player->GetGUID())
? assistantsBeforePlayer : (totalAssistants + nonAssistantsBeforePlayer);
if (!group->IsAssistant(member->GetGUID()) && IsAssistTank(member)) return playerIndex == index;
{
if (index == counter)
return player == member;
counter++;
}
}
return false;
} }
namespace acore namespace acore

View File

@ -423,12 +423,12 @@ public:
static bool IsRangedDps(Player* player, bool bySpec = false); static bool IsRangedDps(Player* player, bool bySpec = false);
static bool IsCombo(Player* player); static bool IsCombo(Player* player);
static bool IsBotMainTank(Player* player); static bool IsBotMainTank(Player* player);
static bool IsMainTank(Player* player); static bool IsMainTank(Player* player, bool ignoreMemberFlag = false);
static uint32 GetGroupTankNum(Player* player); static uint32 GetGroupTankNum(Player* player);
static bool IsAssistTank(Player* player); static bool IsAssistTank(Player* player);
static bool IsAssistTankOfIndex(Player* player, int index, bool ignoreDeadPlayers = false); static bool IsAssistTankOfIndex(Player* player, uint8 index, bool ignoreDeadPlayers = false);
static bool IsAssistHealOfIndex(Player* player, int index, bool ignoreDeadPlayers = false); static bool IsAssistHealOfIndex(Player* player, uint8 index, bool ignoreDeadPlayers = false);
static bool IsAssistRangedDpsOfIndex(Player* player, int index, bool ignoreDeadPlayers = false); static bool IsAssistRangedDpsOfIndex(Player* player, uint8 index, bool ignoreDeadPlayers = false);
bool HasAggro(Unit* unit); bool HasAggro(Unit* unit);
static int32 GetAssistTankIndex(Player* player); static int32 GetAssistTankIndex(Player* player);
int32 GetGroupSlotIndex(Player* player); int32 GetGroupSlotIndex(Player* player);

View File

@ -71,7 +71,6 @@ class PlayerbotLoginQueryHolder : public LoginQueryHolder
{ {
private: private:
uint32 masterAccountId; uint32 masterAccountId;
PlayerbotHolder* playerbotHolder;
public: public:
PlayerbotLoginQueryHolder(uint32 masterAccount, uint32 accountId, ObjectGuid guid) PlayerbotLoginQueryHolder(uint32 masterAccount, uint32 accountId, ObjectGuid guid)
: LoginQueryHolder(accountId, guid), masterAccountId(masterAccount) : LoginQueryHolder(accountId, guid), masterAccountId(masterAccount)

View File

@ -994,7 +994,7 @@ void RandomPlayerbotMgr::CheckBgQueue()
// Arena logic // Arena logic
bool isRated = false; bool isRated = false;
if (uint8 arenaType = BattlegroundMgr::BGArenaType(queueTypeId)) if (BattlegroundMgr::BGArenaType(queueTypeId))
{ {
BattlegroundQueue& bgQueue = sBattlegroundMgr->GetBattlegroundQueue(queueTypeId); BattlegroundQueue& bgQueue = sBattlegroundMgr->GetBattlegroundQueue(queueTypeId);
GroupQueueInfo ginfo; GroupQueueInfo ginfo;
@ -1081,7 +1081,7 @@ void RandomPlayerbotMgr::CheckBgQueue()
BattlegroundData[queueTypeId][bracketId].minLevel = pvpDiff->minLevel; BattlegroundData[queueTypeId][bracketId].minLevel = pvpDiff->minLevel;
BattlegroundData[queueTypeId][bracketId].maxLevel = pvpDiff->maxLevel; BattlegroundData[queueTypeId][bracketId].maxLevel = pvpDiff->maxLevel;
if (uint8 arenaType = BattlegroundMgr::BGArenaType(queueTypeId)) if (BattlegroundMgr::BGArenaType(queueTypeId))
{ {
bool isRated = false; bool isRated = false;
BattlegroundQueue& bgQueue = sBattlegroundMgr->GetBattlegroundQueue(queueTypeId); BattlegroundQueue& bgQueue = sBattlegroundMgr->GetBattlegroundQueue(queueTypeId);
@ -1986,13 +1986,9 @@ void RandomPlayerbotMgr::PrepareTeleportCache()
for (int i = bracket.low; i <= bracket.high; i++) for (int i = bracket.low; i <= bracket.high; i++)
{ {
if (forHorde) if (forHorde)
{
hordeStarterPerLevelCache[i].push_back(loc); hordeStarterPerLevelCache[i].push_back(loc);
}
if (forAlliance) if (forAlliance)
{
allianceStarterPerLevelCache[i].push_back(loc); allianceStarterPerLevelCache[i].push_back(loc);
}
} }
} while (results->NextRow()); } while (results->NextRow());
@ -3146,10 +3142,10 @@ void RandomPlayerbotMgr::PrintStats()
uint32 heal = 0; uint32 heal = 0;
uint32 tank = 0; uint32 tank = 0;
uint32 active = 0; uint32 active = 0;
uint32 update = 0; /* uint32 update = 0;
uint32 randomize = 0; uint32 randomize = 0;
uint32 teleport = 0; uint32 teleport = 0;
uint32 changeStrategy = 0; uint32 changeStrategy = 0;*/
uint32 dead = 0; uint32 dead = 0;
uint32 combat = 0; uint32 combat = 0;
// uint32 revive = 0; //not used, line marked for removal. // uint32 revive = 0; //not used, line marked for removal.
@ -3189,7 +3185,7 @@ void RandomPlayerbotMgr::PrintStats()
if (botAI->AllowActivity()) if (botAI->AllowActivity())
++active; ++active;
/* TODO: Review statistics on rpg merge
if (botAI->GetAiObjectContext()->GetValue<bool>("random bot update")->Get()) if (botAI->GetAiObjectContext()->GetValue<bool>("random bot update")->Get())
++update; ++update;
@ -3202,7 +3198,7 @@ void RandomPlayerbotMgr::PrintStats()
if (!GetEventValue(botId, "change_strategy")) if (!GetEventValue(botId, "change_strategy"))
++changeStrategy; ++changeStrategy;
*/
if (bot->isDead()) if (bot->isDead())
{ {
++dead; ++dead;

View File

@ -1832,7 +1832,6 @@ std::vector<uint32> RandomItemMgr::GetUpgradeList(Player* player, std::string sp
// get old item statWeight // get old item statWeight
uint32 oldStatWeight = 0; uint32 oldStatWeight = 0;
uint32 specId = 0; uint32 specId = 0;
uint32 closestUpgrade = 0;
uint32 closestUpgradeWeight = 0; uint32 closestUpgradeWeight = 0;
std::vector<uint32> classspecs; std::vector<uint32> classspecs;
@ -1933,7 +1932,6 @@ std::vector<uint32> RandomItemMgr::GetUpgradeList(Player* player, std::string sp
// pick closest upgrade // pick closest upgrade
if (info.weights[specId] > closestUpgradeWeight) if (info.weights[specId] > closestUpgradeWeight)
{ {
closestUpgrade = info.itemId;
closestUpgradeWeight = info.weights[specId]; closestUpgradeWeight = info.weights[specId];
} }
} }

View File

@ -415,10 +415,10 @@ bool PlayerbotAIConfig::Initialize()
useFastFlyMountAtMinLevel = sConfigMgr->GetOption<int32>("AiPlayerbot.UseFastFlyMountAtMinLevel", 70); useFastFlyMountAtMinLevel = sConfigMgr->GetOption<int32>("AiPlayerbot.UseFastFlyMountAtMinLevel", 70);
// stagger bot flightpath takeoff // stagger bot flightpath takeoff
delayMin = sConfigMgr->GetOption<uint32>("AiPlayerbot.BotTaxiDelayMinMs", 350u); botTaxiDelayMin = sConfigMgr->GetOption<uint32>("AiPlayerbot.BotTaxiDelayMinMs", 350);
delayMax = sConfigMgr->GetOption<uint32>("AiPlayerbot.BotTaxiDelayMaxMs", 5000u); botTaxiDelayMax = sConfigMgr->GetOption<uint32>("AiPlayerbot.BotTaxiDelayMaxMs", 5000);
gapMs = sConfigMgr->GetOption<uint32>("AiPlayerbot.BotTaxiGapMs", 200u); botTaxiGapMs = sConfigMgr->GetOption<uint32>("AiPlayerbot.BotTaxiGapMs", 200);
gapJitterMs = sConfigMgr->GetOption<uint32>("AiPlayerbot.BotTaxiGapJitterMs", 100u); botTaxiGapJitterMs = sConfigMgr->GetOption<uint32>("AiPlayerbot.BotTaxiGapJitterMs", 100);
LOG_INFO("server.loading", "Loading TalentSpecs..."); LOG_INFO("server.loading", "Loading TalentSpecs...");

View File

@ -424,10 +424,10 @@ public:
uint32 useFastFlyMountAtMinLevel; uint32 useFastFlyMountAtMinLevel;
// stagger flightpath takeoff // stagger flightpath takeoff
uint32 delayMin; uint32 botTaxiDelayMin;
uint32 delayMax; uint32 botTaxiDelayMax;
uint32 gapMs; uint32 botTaxiGapMs;
uint32 gapJitterMs; uint32 botTaxiGapJitterMs;
std::string const GetTimestampStr(); std::string const GetTimestampStr();
bool hasLog(std::string const fileName) bool hasLog(std::string const fileName)

View File

@ -520,6 +520,8 @@ public:
void AddPlayerbotsSecureLoginScripts(); void AddPlayerbotsSecureLoginScripts();
void AddSC_TempestKeepBotScripts();
void AddPlayerbotsScripts() void AddPlayerbotsScripts()
{ {
new PlayerbotsDatabaseScript(); new PlayerbotsDatabaseScript();
@ -532,4 +534,5 @@ void AddPlayerbotsScripts()
AddPlayerbotsSecureLoginScripts(); AddPlayerbotsSecureLoginScripts();
AddPlayerbotsCommandscripts(); AddPlayerbotsCommandscripts();
PlayerBotsGuildValidationScript(); PlayerBotsGuildValidationScript();
AddSC_TempestKeepBotScripts();
} }