mirror of
https://github.com/mod-playerbots/mod-playerbots.git
synced 2026-01-23 05:26:22 +00:00
Compare commits
17 Commits
master_v16
...
cafbd4681e
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cafbd4681e | ||
|
|
f5c84ee7ff | ||
|
|
b6f882886d | ||
|
|
c1222da8b0 | ||
|
|
00cb177c86 | ||
|
|
5f697e806e | ||
|
|
934e73ae20 | ||
|
|
f4b4d8967f | ||
|
|
910b8a9c53 | ||
|
|
bb569b4d39 | ||
|
|
dde16674c3 | ||
|
|
e5b2791053 | ||
|
|
353c29dfc4 | ||
|
|
52c3e96641 | ||
|
|
38e2d8584b | ||
|
|
d5dbc4ddd7 | ||
|
|
2424f73bc4 |
@@ -630,7 +630,7 @@ AiPlayerbot.RandomBotHordeRatio = 50
|
|||||||
AiPlayerbot.DisableDeathKnightLogin = 0
|
AiPlayerbot.DisableDeathKnightLogin = 0
|
||||||
|
|
||||||
# Enable simulated expansion limitation for talents and glyphs
|
# Enable simulated expansion limitation for talents and glyphs
|
||||||
# If enabled, limits talent trees to 5 rows plus the middle talent of the 6th row for bots until level 61
|
# If enabled, limits talent trees to 5 rows plus the middle talent of the 6th row for bots until level 61
|
||||||
# and 7 rows plus the middle talent of the 8th row for bots from level 61 until level 71
|
# and 7 rows plus the middle talent of the 8th row for bots from level 61 until level 71
|
||||||
# Default: 0 (disabled)
|
# Default: 0 (disabled)
|
||||||
AiPlayerbot.LimitTalentsExpansion = 0
|
AiPlayerbot.LimitTalentsExpansion = 0
|
||||||
@@ -1185,7 +1185,7 @@ AiPlayerbot.DeleteRandomBotArenaTeams = 0
|
|||||||
AiPlayerbot.PvpProhibitedZoneIds = "2255,656,2361,2362,2363,976,35,2268,3425,392,541,1446,3828,3712,3738,3565,3539,3623,4152,3988,4658,4284,4418,4436,4275,4323,4395,3703,4298,3951"
|
AiPlayerbot.PvpProhibitedZoneIds = "2255,656,2361,2362,2363,976,35,2268,3425,392,541,1446,3828,3712,3738,3565,3539,3623,4152,3988,4658,4284,4418,4436,4275,4323,4395,3703,4298,3951"
|
||||||
|
|
||||||
# PvP Restricted Areas (bots don't pvp)
|
# PvP Restricted Areas (bots don't pvp)
|
||||||
AiPlayerbot.PvpProhibitedAreaIds = "976,35,392,2268,4161,4010,4317,4312,3649,3887,3958,3724,4080,3938,3754"
|
AiPlayerbot.PvpProhibitedAreaIds = "976,35,392,2268,4161,4010,4317,4312,3649,3887,3958,3724,4080,3938,3754,3786,3973"
|
||||||
|
|
||||||
# Improve reaction speeds in battlegrounds and arenas (may cause lag)
|
# Improve reaction speeds in battlegrounds and arenas (may cause lag)
|
||||||
AiPlayerbot.FastReactInBG = 1
|
AiPlayerbot.FastReactInBG = 1
|
||||||
|
|||||||
@@ -0,0 +1,255 @@
|
|||||||
|
DELETE FROM ai_playerbot_texts WHERE name IN (
|
||||||
|
'pet_usage_error',
|
||||||
|
'pet_no_pet_error',
|
||||||
|
'pet_stance_report',
|
||||||
|
'pet_no_target_error',
|
||||||
|
'pet_target_dead_error',
|
||||||
|
'pet_invalid_target_error',
|
||||||
|
'pet_pvp_prohibited_error',
|
||||||
|
'pet_attack_success',
|
||||||
|
'pet_attack_failed',
|
||||||
|
'pet_follow_success',
|
||||||
|
'pet_stay_success',
|
||||||
|
'pet_unknown_command_error',
|
||||||
|
'pet_stance_set_success',
|
||||||
|
'pet_type_pet',
|
||||||
|
'pet_type_guardian',
|
||||||
|
'pet_stance_aggressive',
|
||||||
|
'pet_stance_defensive',
|
||||||
|
'pet_stance_passive',
|
||||||
|
'pet_stance_unknown'
|
||||||
|
);
|
||||||
|
|
||||||
|
DELETE FROM ai_playerbot_texts_chance WHERE name IN (
|
||||||
|
'pet_usage_error',
|
||||||
|
'pet_no_pet_error',
|
||||||
|
'pet_stance_report',
|
||||||
|
'pet_no_target_error',
|
||||||
|
'pet_target_dead_error',
|
||||||
|
'pet_invalid_target_error',
|
||||||
|
'pet_pvp_prohibited_error',
|
||||||
|
'pet_attack_success',
|
||||||
|
'pet_attack_failed',
|
||||||
|
'pet_follow_success',
|
||||||
|
'pet_stay_success',
|
||||||
|
'pet_unknown_command_error',
|
||||||
|
'pet_stance_set_success',
|
||||||
|
'pet_type_pet',
|
||||||
|
'pet_type_guardian',
|
||||||
|
'pet_stance_aggressive',
|
||||||
|
'pet_stance_defensive',
|
||||||
|
'pet_stance_passive',
|
||||||
|
'pet_stance_unknown'
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO ai_playerbot_texts (id, name, text, say_type, reply_type, text_loc1, text_loc2, text_loc3, text_loc4, text_loc5, text_loc6, text_loc7, text_loc8) VALUES
|
||||||
|
(1717, 'pet_usage_error', "Usage: pet <aggressive|defensive|passive|stance|attack|follow|stay>", 0, 0,
|
||||||
|
"사용법: pet <aggressive|defensive|passive|stance|attack|follow|stay>",
|
||||||
|
"Utilisation: pet <aggressive|defensive|passive|stance|attack|follow|stay>",
|
||||||
|
"Verwendung: pet <aggressive|defensive|passive|stance|attack|follow|stay>",
|
||||||
|
"用法: pet <aggressive|defensive|passive|stance|attack|follow|stay>",
|
||||||
|
"用法: pet <aggressive|defensive|passive|stance|attack|follow|stay>",
|
||||||
|
"Uso: pet <aggressive|defensive|passive|stance|attack|follow|stay>",
|
||||||
|
"Uso: pet <aggressive|defensive|passive|stance|attack|follow|stay>",
|
||||||
|
"Использование: pet <aggressive|defensive|passive|stance|attack|follow|stay>"),
|
||||||
|
|
||||||
|
(1718, 'pet_no_pet_error', "You have no pet or guardian pet.", 0, 0,
|
||||||
|
"펫이나 수호자 펫이 없습니다.",
|
||||||
|
"Vous n'avez pas de familier ou gardien.",
|
||||||
|
"Du hast kein Tier oder Wächter.",
|
||||||
|
"你没有宠物或守护者宠物。",
|
||||||
|
"你沒有寵物或守護者寵物。",
|
||||||
|
"No tienes mascota o mascota guardián.",
|
||||||
|
"No tienes mascota o mascota guardián.",
|
||||||
|
"У вас нет питомца или защитника."),
|
||||||
|
|
||||||
|
(1719, 'pet_stance_report', "Current stance of %type \"%name\": %stance.", 0, 0,
|
||||||
|
"%type \"%name\"의 현재 태세: %stance.",
|
||||||
|
"Position actuelle du %type \"%name\": %stance.",
|
||||||
|
"Aktuelle Haltung des %type \"%name\": %stance.",
|
||||||
|
"%type \"%name\" 的当前姿态: %stance。",
|
||||||
|
"%type \"%name\" 的當前姿態: %stance。",
|
||||||
|
"Postura actual del %type \"%name\": %stance.",
|
||||||
|
"Postura actual del %type \"%name\": %stance.",
|
||||||
|
"Текущая позиция %type \"%name\": %stance."),
|
||||||
|
|
||||||
|
(1720, 'pet_no_target_error', "No valid target selected by master.", 0, 0,
|
||||||
|
"주인이 유효한 대상을 선택하지 않았습니다.",
|
||||||
|
"Aucune cible valide sélectionnée par le maître.",
|
||||||
|
"Kein gültiges Ziel vom Meister ausgewählt.",
|
||||||
|
"主人未选择有效目标。",
|
||||||
|
"主人未選擇有效目標。",
|
||||||
|
"No hay objetivo válido seleccionado por el maestro.",
|
||||||
|
"No hay objetivo válido seleccionado por el maestro.",
|
||||||
|
"Хозяин не выбрал действительную цель."),
|
||||||
|
|
||||||
|
(1721, 'pet_target_dead_error', "Target is not alive.", 0, 0,
|
||||||
|
"대상이 살아있지 않습니다.",
|
||||||
|
"La cible n'est pas vivante.",
|
||||||
|
"Das Ziel ist nicht am Leben.",
|
||||||
|
"目标未存活。",
|
||||||
|
"目標未存活。",
|
||||||
|
"El objetivo no está vivo.",
|
||||||
|
"El objetivo no está vivo.",
|
||||||
|
"Цель не жива."),
|
||||||
|
|
||||||
|
(1722, 'pet_invalid_target_error', "Target is not a valid attack target for the bot.", 0, 0,
|
||||||
|
"대상이 봇에게 유효한 공격 대상이 아닙니다.",
|
||||||
|
"La cible n'est pas une cible d'attaque valide pour le bot.",
|
||||||
|
"Das Ziel ist kein gültiges Angriffsziel für den Bot.",
|
||||||
|
"目标不是机器人的有效攻击目标。",
|
||||||
|
"目標不是機器人的有效攻擊目標。",
|
||||||
|
"El objetivo no es un objetivo de ataque válido para el bot.",
|
||||||
|
"El objetivo no es un objetivo de ataque válido para el bot.",
|
||||||
|
"Цель не является допустимой целью атаки для бота."),
|
||||||
|
|
||||||
|
(1723, 'pet_pvp_prohibited_error', "I cannot command my pet to attack players in PvP prohibited areas.", 0, 0,
|
||||||
|
"PvP 금지 지역에서는 펫에게 플레이어 공격 명령을 내릴 수 없습니다.",
|
||||||
|
"Je ne peux pas commander à mon familier d'attaquer des joueurs dans les zones où le PvP est interdit.",
|
||||||
|
"Ich kann meinem Tier nicht befehlen, Spieler in PvP-verbotenen Gebieten anzugreifen.",
|
||||||
|
"我不能命令我的宠物在禁止PvP的区域攻击玩家。",
|
||||||
|
"我不能命令我的寵物在禁止PvP的區域攻擊玩家。",
|
||||||
|
"No puedo ordenar a mi mascota atacar jugadores en áreas donde el PvP está prohibido.",
|
||||||
|
"No puedo ordenar a mi mascota atacar jugadores en áreas donde el PvP está prohibido.",
|
||||||
|
"Я не могу приказать своему питомцу атаковать игроков в зонах, где PvP запрещено."),
|
||||||
|
|
||||||
|
(1724, 'pet_attack_success', "Pet commanded to attack your target.", 0, 0,
|
||||||
|
"펫이 당신의 대상을 공격하도록 명령했습니다.",
|
||||||
|
"Le familier a reçu l'ordre d'attaquer votre cible.",
|
||||||
|
"Tier wurde befohlen, dein Ziel anzugreifen.",
|
||||||
|
"宠物已命令攻击你的目标。",
|
||||||
|
"寵物已命令攻擊你的目標。",
|
||||||
|
"Mascota ordenada a atacar tu objetivo.",
|
||||||
|
"Mascota ordenada a atacar tu objetivo.",
|
||||||
|
"Питомцу приказано атаковать вашу цель."),
|
||||||
|
|
||||||
|
(1725, 'pet_attack_failed', "Pet did not attack. (Already attacking or unable to attack target)", 0, 0,
|
||||||
|
"펫이 공격하지 않았습니다. (이미 공격 중이거나 대상 공격 불가)",
|
||||||
|
"Le familier n'a pas attaqué. (Attaque déjà en cours ou impossible d'attaquer la cible)",
|
||||||
|
"Tier hat nicht angegriffen. (Greift bereits an oder kann Ziel nicht angreifen)",
|
||||||
|
"宠物未攻击。(已在攻击或无法攻击目标)",
|
||||||
|
"寵物未攻擊。(已在攻擊或無法攻擊目標)",
|
||||||
|
"La mascota no atacó. (Ya está atacando o no puede atacar al objetivo)",
|
||||||
|
"La mascota no atacó. (Ya está atacando o no puede atacar al objetivo)",
|
||||||
|
"Питомец не атаковал. (Уже атакует или не может атаковать цель)"),
|
||||||
|
|
||||||
|
(1726, 'pet_follow_success', "Pet commanded to follow.", 0, 0,
|
||||||
|
"펫이 따라오도록 명령했습니다.",
|
||||||
|
"Le familier a reçu l'ordre de suivre.",
|
||||||
|
"Tier wurde befohlen zu folgen.",
|
||||||
|
"宠物已命令跟随。",
|
||||||
|
"寵物已命令跟隨。",
|
||||||
|
"Mascota ordenada a seguir.",
|
||||||
|
"Mascota ordenada a seguir.",
|
||||||
|
"Питомцу приказано следовать."),
|
||||||
|
|
||||||
|
(1727, 'pet_stay_success', "Pet commanded to stay.", 0, 0,
|
||||||
|
"펫이 머물도록 명령했습니다.",
|
||||||
|
"Le familier a reçu l'ordre de rester.",
|
||||||
|
"Tier wurde befohlen zu bleiben.",
|
||||||
|
"宠物已命令停留。",
|
||||||
|
"寵物已命令停留。",
|
||||||
|
"Mascota ordenada a quedarse.",
|
||||||
|
"Mascota ordenada a quedarse.",
|
||||||
|
"Питомцу приказано остаться."),
|
||||||
|
|
||||||
|
(1728, 'pet_unknown_command_error', "Unknown pet command: %param. Use: pet <aggressive|defensive|passive|stance|attack|follow|stay>", 0, 0,
|
||||||
|
"알 수 없는 펫 명령: %param. 사용법: pet <aggressive|defensive|passive|stance|attack|follow|stay>",
|
||||||
|
"Commande de familier inconnue: %param. Utilisation: pet <aggressive|defensive|passive|stance|attack|follow|stay>",
|
||||||
|
"Unbekannter Tierbefehl: %param. Verwendung: pet <aggressive|defensive|passive|stance|attack|follow|stay>",
|
||||||
|
"未知宠物命令: %param。用法: pet <aggressive|defensive|passive|stance|attack|follow|stay>",
|
||||||
|
"未知寵物命令: %param。用法: pet <aggressive|defensive|passive|stance|attack|follow|stay>",
|
||||||
|
"Comando de mascota desconocido: %param. Uso: pet <aggressive|defensive|passive|stance|attack|follow|stay>",
|
||||||
|
"Comando de mascota desconocido: %param. Uso: pet <aggressive|defensive|passive|stance|attack|follow|stay>",
|
||||||
|
"Неизвестная команда питомца: %param. Использование: pet <aggressive|defensive|passive|stance|attack|follow|stay>"),
|
||||||
|
|
||||||
|
(1729, 'pet_stance_set_success', "Pet stance set to %stance.", 0, 0,
|
||||||
|
"펫 태세가 %stance(으)로 설정되었습니다.",
|
||||||
|
"Position du familier définie sur %stance.",
|
||||||
|
"Tierhaltung auf %stance gesetzt.",
|
||||||
|
"宠物姿态设置为 %stance。",
|
||||||
|
"寵物姿態設置為 %stance。",
|
||||||
|
"Postura de mascota establecida en %stance.",
|
||||||
|
"Postura de mascota establecida en %stance.",
|
||||||
|
"Позиция питомца установлена на %stance."),
|
||||||
|
|
||||||
|
(1730, 'pet_type_pet', "pet", 0, 0,
|
||||||
|
"펫",
|
||||||
|
"familier",
|
||||||
|
"Tier",
|
||||||
|
"宠物",
|
||||||
|
"寵物",
|
||||||
|
"mascota",
|
||||||
|
"mascota",
|
||||||
|
"питомец"),
|
||||||
|
|
||||||
|
(1731, 'pet_type_guardian', "guardian", 0, 0,
|
||||||
|
"수호자",
|
||||||
|
"gardien",
|
||||||
|
"Wächter",
|
||||||
|
"守护者",
|
||||||
|
"守護者",
|
||||||
|
"guardián",
|
||||||
|
"guardián",
|
||||||
|
"защитник"),
|
||||||
|
|
||||||
|
(1732, 'pet_stance_aggressive', "aggressive", 0, 0,
|
||||||
|
"공격적",
|
||||||
|
"agressif",
|
||||||
|
"aggressiv",
|
||||||
|
"进攻",
|
||||||
|
"進攻",
|
||||||
|
"agresivo",
|
||||||
|
"agresivo",
|
||||||
|
"агрессивная"),
|
||||||
|
|
||||||
|
(1733, 'pet_stance_defensive', "defensive", 0, 0,
|
||||||
|
"방어적",
|
||||||
|
"défensif",
|
||||||
|
"defensiv",
|
||||||
|
"防御",
|
||||||
|
"防禦",
|
||||||
|
"defensivo",
|
||||||
|
"defensivo",
|
||||||
|
"защитная"),
|
||||||
|
|
||||||
|
(1734, 'pet_stance_passive', "passive", 0, 0,
|
||||||
|
"수동적",
|
||||||
|
"passif",
|
||||||
|
"passiv",
|
||||||
|
"被动",
|
||||||
|
"被動",
|
||||||
|
"pasivo",
|
||||||
|
"pasivo",
|
||||||
|
"пассивная"),
|
||||||
|
|
||||||
|
(1735, 'pet_stance_unknown', "unknown", 0, 0,
|
||||||
|
"알 수 없음",
|
||||||
|
"inconnu",
|
||||||
|
"unbekannt",
|
||||||
|
"未知",
|
||||||
|
"未知",
|
||||||
|
"desconocido",
|
||||||
|
"desconocido",
|
||||||
|
"неизвестная");
|
||||||
|
|
||||||
|
INSERT INTO ai_playerbot_texts_chance (name, probability) VALUES
|
||||||
|
('pet_usage_error', 100),
|
||||||
|
('pet_no_pet_error', 100),
|
||||||
|
('pet_stance_report', 100),
|
||||||
|
('pet_no_target_error', 100),
|
||||||
|
('pet_target_dead_error', 100),
|
||||||
|
('pet_invalid_target_error', 100),
|
||||||
|
('pet_pvp_prohibited_error', 100),
|
||||||
|
('pet_attack_success', 100),
|
||||||
|
('pet_attack_failed', 100),
|
||||||
|
('pet_follow_success', 100),
|
||||||
|
('pet_stay_success', 100),
|
||||||
|
('pet_unknown_command_error', 100),
|
||||||
|
('pet_stance_set_success', 100),
|
||||||
|
('pet_type_pet', 100),
|
||||||
|
('pet_type_guardian', 100),
|
||||||
|
('pet_stance_aggressive', 100),
|
||||||
|
('pet_stance_defensive', 100),
|
||||||
|
('pet_stance_passive', 100),
|
||||||
|
('pet_stance_unknown', 100);
|
||||||
@@ -365,7 +365,7 @@ void PlayerbotAI::UpdateAI(uint32 elapsed, bool minimal)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Update the bot's group status (moved to helper function)
|
// Update the bot's group status (moved to helper function)
|
||||||
UpdateAIGroupAndMaster();
|
UpdateAIGroupMaster();
|
||||||
|
|
||||||
// Update internal AI
|
// Update internal AI
|
||||||
UpdateAIInternal(elapsed, minimal);
|
UpdateAIInternal(elapsed, minimal);
|
||||||
@@ -373,7 +373,7 @@ void PlayerbotAI::UpdateAI(uint32 elapsed, bool minimal)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Helper function for UpdateAI to check group membership and handle removal if necessary
|
// Helper function for UpdateAI to check group membership and handle removal if necessary
|
||||||
void PlayerbotAI::UpdateAIGroupAndMaster()
|
void PlayerbotAI::UpdateAIGroupMaster()
|
||||||
{
|
{
|
||||||
if (!bot)
|
if (!bot)
|
||||||
return;
|
return;
|
||||||
@@ -420,7 +420,7 @@ void PlayerbotAI::UpdateAIGroupAndMaster()
|
|||||||
{
|
{
|
||||||
botAI->ChangeStrategy("+follow", BOT_STATE_NON_COMBAT);
|
botAI->ChangeStrategy("+follow", BOT_STATE_NON_COMBAT);
|
||||||
|
|
||||||
if (botAI->GetMaster() == botAI->GetGroupMaster())
|
if (botAI->GetMaster() == botAI->GetGroupLeader())
|
||||||
botAI->TellMaster("Hello, I follow you!");
|
botAI->TellMaster("Hello, I follow you!");
|
||||||
else
|
else
|
||||||
botAI->TellMaster(!urand(0, 2) ? "Hello!" : "Hi!");
|
botAI->TellMaster(!urand(0, 2) ? "Hello!" : "Hi!");
|
||||||
@@ -431,8 +431,6 @@ void PlayerbotAI::UpdateAIGroupAndMaster()
|
|||||||
botAI->ChangeStrategy("-follow", BOT_STATE_NON_COMBAT);
|
botAI->ChangeStrategy("-follow", BOT_STATE_NON_COMBAT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (!newMaster && !bot->InBattleground())
|
|
||||||
LeaveOrDisbandGroup();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1386,9 +1384,6 @@ void PlayerbotAI::DoNextAction(bool min)
|
|||||||
else if (bot->isAFK())
|
else if (bot->isAFK())
|
||||||
bot->ToggleAFK();
|
bot->ToggleAFK();
|
||||||
|
|
||||||
Group* group = bot->GetGroup();
|
|
||||||
PlayerbotAI* masterBotAI = nullptr;
|
|
||||||
|
|
||||||
if (master && master->IsInWorld())
|
if (master && master->IsInWorld())
|
||||||
{
|
{
|
||||||
float distance = sServerFacade->GetDistance2d(bot, master);
|
float distance = sServerFacade->GetDistance2d(bot, master);
|
||||||
@@ -1461,7 +1456,7 @@ void PlayerbotAI::ApplyInstanceStrategies(uint32 mapId, bool tellMaster)
|
|||||||
strategyName = "onyxia"; // Onyxia's Lair
|
strategyName = "onyxia"; // Onyxia's Lair
|
||||||
break;
|
break;
|
||||||
case 409:
|
case 409:
|
||||||
strategyName = "mc"; // Molten Core
|
strategyName = "moltencore"; // Molten Core
|
||||||
break;
|
break;
|
||||||
case 469:
|
case 469:
|
||||||
strategyName = "bwl"; // Blackwing Lair
|
strategyName = "bwl"; // Blackwing Lair
|
||||||
@@ -1477,6 +1472,7 @@ void PlayerbotAI::ApplyInstanceStrategies(uint32 mapId, bool tellMaster)
|
|||||||
break;
|
break;
|
||||||
case 544:
|
case 544:
|
||||||
strategyName = "magtheridon"; // Magtheridon's Lair
|
strategyName = "magtheridon"; // Magtheridon's Lair
|
||||||
|
break;
|
||||||
case 565:
|
case 565:
|
||||||
strategyName = "gruulslair"; // Gruul's Lair
|
strategyName = "gruulslair"; // Gruul's Lair
|
||||||
break;
|
break;
|
||||||
@@ -2248,7 +2244,7 @@ uint32 PlayerbotAI::GetGroupTankNum(Player* player)
|
|||||||
|
|
||||||
bool PlayerbotAI::IsAssistTank(Player* player) { return IsTank(player) && !IsMainTank(player); }
|
bool PlayerbotAI::IsAssistTank(Player* player) { return IsTank(player) && !IsMainTank(player); }
|
||||||
|
|
||||||
bool PlayerbotAI::IsAssistTankOfIndex(Player* player, int index)
|
bool PlayerbotAI::IsAssistTankOfIndex(Player* player, int index, bool ignoreDeadPlayers)
|
||||||
{
|
{
|
||||||
Group* group = player->GetGroup();
|
Group* group = player->GetGroup();
|
||||||
if (!group)
|
if (!group)
|
||||||
@@ -2265,6 +2261,9 @@ bool PlayerbotAI::IsAssistTankOfIndex(Player* player, int index)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ignoreDeadPlayers && !member->IsAlive())
|
||||||
|
continue;
|
||||||
|
|
||||||
if (group->IsAssistant(member->GetGUID()) && IsAssistTank(member))
|
if (group->IsAssistant(member->GetGUID()) && IsAssistTank(member))
|
||||||
{
|
{
|
||||||
if (index == counter)
|
if (index == counter)
|
||||||
@@ -2284,6 +2283,9 @@ bool PlayerbotAI::IsAssistTankOfIndex(Player* player, int index)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ignoreDeadPlayers && !member->IsAlive())
|
||||||
|
continue;
|
||||||
|
|
||||||
if (!group->IsAssistant(member->GetGUID()) && IsAssistTank(member))
|
if (!group->IsAssistant(member->GetGUID()) && IsAssistTank(member))
|
||||||
{
|
{
|
||||||
if (index == counter)
|
if (index == counter)
|
||||||
@@ -4092,7 +4094,7 @@ Player* PlayerbotAI::FindNewMaster()
|
|||||||
if (!group)
|
if (!group)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
Player* groupLeader = GetGroupMaster();
|
Player* groupLeader = GetGroupLeader();
|
||||||
PlayerbotAI* leaderBotAI = GET_PLAYERBOT_AI(groupLeader);
|
PlayerbotAI* leaderBotAI = GET_PLAYERBOT_AI(groupLeader);
|
||||||
if (!leaderBotAI || leaderBotAI->IsRealPlayer())
|
if (!leaderBotAI || leaderBotAI->IsRealPlayer())
|
||||||
return groupLeader;
|
return groupLeader;
|
||||||
@@ -4143,7 +4145,7 @@ bool PlayerbotAI::HasActivePlayerMaster() { return master && !GET_PLAYERBOT_AI(m
|
|||||||
|
|
||||||
bool PlayerbotAI::IsAlt() { return HasRealPlayerMaster() && !sRandomPlayerbotMgr->IsRandomBot(bot); }
|
bool PlayerbotAI::IsAlt() { return HasRealPlayerMaster() && !sRandomPlayerbotMgr->IsRandomBot(bot); }
|
||||||
|
|
||||||
Player* PlayerbotAI::GetGroupMaster()
|
Player* PlayerbotAI::GetGroupLeader()
|
||||||
{
|
{
|
||||||
if (!bot->InBattleground())
|
if (!bot->InBattleground())
|
||||||
if (Group* group = bot->GetGroup())
|
if (Group* group = bot->GetGroup())
|
||||||
|
|||||||
@@ -428,7 +428,7 @@ public:
|
|||||||
static bool IsMainTank(Player* player);
|
static bool IsMainTank(Player* player);
|
||||||
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);
|
static bool IsAssistTankOfIndex(Player* player, int index, bool ignoreDeadPlayers = false);
|
||||||
static bool IsHealAssistantOfIndex(Player* player, int index);
|
static bool IsHealAssistantOfIndex(Player* player, int index);
|
||||||
static bool IsRangedDpsAssistantOfIndex(Player* player, int index);
|
static bool IsRangedDpsAssistantOfIndex(Player* player, int index);
|
||||||
bool HasAggro(Unit* unit);
|
bool HasAggro(Unit* unit);
|
||||||
@@ -540,7 +540,7 @@ public:
|
|||||||
// Get the group leader or the master of the bot.
|
// Get the group leader or the master of the bot.
|
||||||
// Checks if the bot is summoned as alt of a player
|
// Checks if the bot is summoned as alt of a player
|
||||||
bool IsAlt();
|
bool IsAlt();
|
||||||
Player* GetGroupMaster();
|
Player* GetGroupLeader();
|
||||||
// Returns a semi-random (cycling) number that is fixed for each bot.
|
// Returns a semi-random (cycling) number that is fixed for each bot.
|
||||||
uint32 GetFixedBotNumer(uint32 maxNum = 100, float cyclePerMin = 1);
|
uint32 GetFixedBotNumer(uint32 maxNum = 100, float cyclePerMin = 1);
|
||||||
GrouperType GetGrouperType();
|
GrouperType GetGrouperType();
|
||||||
@@ -612,7 +612,7 @@ private:
|
|||||||
static void _fillGearScoreData(Player* player, Item* item, std::vector<uint32>* gearScore, uint32& twoHandScore,
|
static void _fillGearScoreData(Player* player, Item* item, std::vector<uint32>* gearScore, uint32& twoHandScore,
|
||||||
bool mixed = false);
|
bool mixed = false);
|
||||||
bool IsTellAllowed(PlayerbotSecurityLevel securityLevel = PLAYERBOT_SECURITY_ALLOW_ALL);
|
bool IsTellAllowed(PlayerbotSecurityLevel securityLevel = PLAYERBOT_SECURITY_ALLOW_ALL);
|
||||||
void UpdateAIGroupAndMaster();
|
void UpdateAIGroupMaster();
|
||||||
Item* FindItemInInventory(std::function<bool(ItemTemplate const*)> checkItem) const;
|
Item* FindItemInInventory(std::function<bool(ItemTemplate const*)> checkItem) const;
|
||||||
void HandleCommands();
|
void HandleCommands();
|
||||||
void HandleCommand(uint32 type, const std::string& text, Player& fromPlayer, const uint32 lang = LANG_UNIVERSAL);
|
void HandleCommand(uint32 type, const std::string& text, Player& fromPlayer, const uint32 lang = LANG_UNIVERSAL);
|
||||||
|
|||||||
@@ -165,7 +165,7 @@ bool PlayerbotAIConfig::Initialize()
|
|||||||
pvpProhibitedZoneIds);
|
pvpProhibitedZoneIds);
|
||||||
LoadList<std::vector<uint32>>(
|
LoadList<std::vector<uint32>>(
|
||||||
sConfigMgr->GetOption<std::string>("AiPlayerbot.PvpProhibitedAreaIds",
|
sConfigMgr->GetOption<std::string>("AiPlayerbot.PvpProhibitedAreaIds",
|
||||||
"976,35,392,2268,4161,4010,4317,4312,3649,3887,3958,3724,4080,3938,3754"),
|
"976,35,392,2268,4161,4010,4317,4312,3649,3887,3958,3724,4080,3938,3754,3786,3973"),
|
||||||
pvpProhibitedAreaIds);
|
pvpProhibitedAreaIds);
|
||||||
fastReactInBG = sConfigMgr->GetOption<bool>("AiPlayerbot.FastReactInBG", true);
|
fastReactInBG = sConfigMgr->GetOption<bool>("AiPlayerbot.FastReactInBG", true);
|
||||||
LoadList<std::vector<uint32>>(
|
LoadList<std::vector<uint32>>(
|
||||||
|
|||||||
@@ -251,9 +251,9 @@ bool PlayerbotSecurity::CheckLevelFor(PlayerbotSecurityLevel level, bool silent,
|
|||||||
out << "I am currently leading a group. I can invite you if you want.";
|
out << "I am currently leading a group. I can invite you if you want.";
|
||||||
break;
|
break;
|
||||||
case PLAYERBOT_DENY_NOT_LEADER:
|
case PLAYERBOT_DENY_NOT_LEADER:
|
||||||
if (botAI->GetGroupMaster())
|
if (botAI->GetGroupLeader())
|
||||||
{
|
{
|
||||||
out << "I am in a group with " << botAI->GetGroupMaster()->GetName()
|
out << "I am in a group with " << botAI->GetGroupLeader()->GetName()
|
||||||
<< ". You can ask him for invite.";
|
<< ". You can ask him for invite.";
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
#include "Metric.h"
|
#include "Metric.h"
|
||||||
#include "PlayerScript.h"
|
#include "PlayerScript.h"
|
||||||
#include "PlayerbotAIConfig.h"
|
#include "PlayerbotAIConfig.h"
|
||||||
|
#include "PlayerbotSpellCache.h"
|
||||||
#include "PlayerbotWorldThreadProcessor.h"
|
#include "PlayerbotWorldThreadProcessor.h"
|
||||||
#include "RandomPlayerbotMgr.h"
|
#include "RandomPlayerbotMgr.h"
|
||||||
#include "ScriptMgr.h"
|
#include "ScriptMgr.h"
|
||||||
@@ -82,12 +83,12 @@ public:
|
|||||||
PlayerbotsPlayerScript() : PlayerScript("PlayerbotsPlayerScript", {
|
PlayerbotsPlayerScript() : PlayerScript("PlayerbotsPlayerScript", {
|
||||||
PLAYERHOOK_ON_LOGIN,
|
PLAYERHOOK_ON_LOGIN,
|
||||||
PLAYERHOOK_ON_AFTER_UPDATE,
|
PLAYERHOOK_ON_AFTER_UPDATE,
|
||||||
PLAYERHOOK_ON_CHAT,
|
|
||||||
PLAYERHOOK_ON_CHAT_WITH_CHANNEL,
|
|
||||||
PLAYERHOOK_ON_CHAT_WITH_GROUP,
|
|
||||||
PLAYERHOOK_ON_BEFORE_CRITERIA_PROGRESS,
|
PLAYERHOOK_ON_BEFORE_CRITERIA_PROGRESS,
|
||||||
PLAYERHOOK_ON_BEFORE_ACHI_COMPLETE,
|
PLAYERHOOK_ON_BEFORE_ACHI_COMPLETE,
|
||||||
PLAYERHOOK_CAN_PLAYER_USE_PRIVATE_CHAT,
|
PLAYERHOOK_CAN_PLAYER_USE_PRIVATE_CHAT,
|
||||||
|
PLAYERHOOK_CAN_PLAYER_USE_GROUP_CHAT,
|
||||||
|
PLAYERHOOK_CAN_PLAYER_USE_GUILD_CHAT,
|
||||||
|
PLAYERHOOK_CAN_PLAYER_USE_CHANNEL_CHAT,
|
||||||
PLAYERHOOK_ON_GIVE_EXP,
|
PLAYERHOOK_ON_GIVE_EXP,
|
||||||
PLAYERHOOK_ON_BEFORE_TELEPORT
|
PLAYERHOOK_ON_BEFORE_TELEPORT
|
||||||
}) {}
|
}) {}
|
||||||
@@ -164,14 +165,17 @@ public:
|
|||||||
{
|
{
|
||||||
botAI->HandleCommand(type, msg, player);
|
botAI->HandleCommand(type, msg, player);
|
||||||
|
|
||||||
return false;
|
// hotfix; otherwise the server will crash when whispering logout
|
||||||
|
// https://github.com/mod-playerbots/mod-playerbots/pull/1838
|
||||||
|
// TODO: find the root cause and solve it. (does not happen in party chat)
|
||||||
|
if (msg == "logout")
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OnPlayerChat(Player* player, uint32 type, uint32 /*lang*/, std::string& msg, Group* group) override
|
bool OnPlayerCanUseChat(Player* player, uint32 type, uint32 /*lang*/, std::string& msg, Group* group) override
|
||||||
{
|
{
|
||||||
for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next())
|
for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next())
|
||||||
{
|
{
|
||||||
@@ -183,9 +187,10 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OnPlayerChat(Player* player, uint32 type, uint32 /*lang*/, std::string& msg) override
|
bool OnPlayerCanUseChat(Player* player, uint32 type, uint32 /*lang*/, std::string& msg, Guild* guild) override
|
||||||
{
|
{
|
||||||
if (type == CHAT_MSG_GUILD)
|
if (type == CHAT_MSG_GUILD)
|
||||||
{
|
{
|
||||||
@@ -204,9 +209,10 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OnPlayerChat(Player* player, uint32 type, uint32 /*lang*/, std::string& msg, Channel* channel) override
|
bool OnPlayerCanUseChat(Player* player, uint32 type, uint32 /*lang*/, std::string& msg, Channel* channel) override
|
||||||
{
|
{
|
||||||
if (PlayerbotMgr* playerbotMgr = GET_PLAYERBOT_MGR(player))
|
if (PlayerbotMgr* playerbotMgr = GET_PLAYERBOT_MGR(player))
|
||||||
{
|
{
|
||||||
@@ -217,6 +223,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
sRandomPlayerbotMgr->HandleCommand(type, msg, player);
|
sRandomPlayerbotMgr->HandleCommand(type, msg, player);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OnPlayerBeforeAchievementComplete(Player* player, AchievementEntry const* achievement) override
|
bool OnPlayerBeforeAchievementComplete(Player* player, AchievementEntry const* achievement) override
|
||||||
@@ -232,34 +239,39 @@ public:
|
|||||||
|
|
||||||
void OnPlayerGiveXP(Player* player, uint32& amount, Unit* /*victim*/, uint8 /*xpSource*/) override
|
void OnPlayerGiveXP(Player* player, uint32& amount, Unit* /*victim*/, uint8 /*xpSource*/) override
|
||||||
{
|
{
|
||||||
// early return
|
if (sPlayerbotAIConfig->randomBotXPRate == 1.0f || !player || !player->IsInWorld())
|
||||||
if (sPlayerbotAIConfig->randomBotXPRate == 1.0 || !player)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// no XP multiplier, when player is no bot.
|
PlayerbotAI* botAI = GET_PLAYERBOT_AI(player);
|
||||||
if (!player->GetSession()->IsBot() || !sRandomPlayerbotMgr->IsRandomBot(player))
|
if (!botAI || !sRandomPlayerbotMgr->IsRandomBot(player))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// no XP multiplier, when bot is in a group with a real player.
|
// No XP gain if master is a real player with XP gain disabled
|
||||||
|
if (const Player* master = botAI->GetMaster())
|
||||||
|
{
|
||||||
|
if (WorldSession* masterSession = master->GetSession();
|
||||||
|
masterSession && !masterSession->IsBot() && master->HasPlayerFlag(PLAYER_FLAGS_NO_XP_GAIN))
|
||||||
|
{
|
||||||
|
amount = 0; // disable XP multiplier
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// No XP multiplier if bot is in a group with at least one real player
|
||||||
if (Group* group = player->GetGroup())
|
if (Group* group = player->GetGroup())
|
||||||
{
|
{
|
||||||
for (GroupReference* gref = group->GetFirstMember(); gref; gref = gref->next())
|
for (GroupReference* gref = group->GetFirstMember(); gref; gref = gref->next())
|
||||||
{
|
{
|
||||||
Player* member = gref->GetSource();
|
if (Player* member = gref->GetSource())
|
||||||
if (!member)
|
|
||||||
{
|
{
|
||||||
continue;
|
if (!member->GetSession()->IsBot())
|
||||||
}
|
return;
|
||||||
|
|
||||||
if (!member->GetSession()->IsBot())
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// otherwise apply bot XP multiplier.
|
// Otherwise apply XP multiplier
|
||||||
amount = static_cast<uint32>(std::round(static_cast<float>(amount) * sPlayerbotAIConfig->randomBotXPRate));
|
amount = static_cast<uint32>(std::round(amount * sPlayerbotAIConfig->randomBotXPRate));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -331,6 +343,9 @@ public:
|
|||||||
|
|
||||||
LOG_INFO("server.loading", ">> Loaded playerbots config in {} ms", GetMSTimeDiffToNow(oldMSTime));
|
LOG_INFO("server.loading", ">> Loaded playerbots config in {} ms", GetMSTimeDiffToNow(oldMSTime));
|
||||||
LOG_INFO("server.loading", " ");
|
LOG_INFO("server.loading", " ");
|
||||||
|
|
||||||
|
sPlayerbotSpellCache->Initialize();
|
||||||
|
|
||||||
LOG_INFO("server.loading", "Playerbots World Thread Processor initialized");
|
LOG_INFO("server.loading", "Playerbots World Thread Processor initialized");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1480,10 +1480,10 @@ bool RandomPlayerbotMgr::ProcessBot(uint32 bot)
|
|||||||
if (!sRandomPlayerbotMgr->IsRandomBot(player))
|
if (!sRandomPlayerbotMgr->IsRandomBot(player))
|
||||||
update = false;
|
update = false;
|
||||||
|
|
||||||
if (player->GetGroup() && botAI->GetGroupMaster())
|
if (player->GetGroup() && botAI->GetGroupLeader())
|
||||||
{
|
{
|
||||||
PlayerbotAI* groupMasterBotAI = GET_PLAYERBOT_AI(botAI->GetGroupMaster());
|
PlayerbotAI* groupLeaderBotAI = GET_PLAYERBOT_AI(botAI->GetGroupLeader());
|
||||||
if (!groupMasterBotAI || groupMasterBotAI->IsRealPlayer())
|
if (!groupLeaderBotAI || groupLeaderBotAI->IsRealPlayer())
|
||||||
{
|
{
|
||||||
update = false;
|
update = false;
|
||||||
}
|
}
|
||||||
@@ -2594,17 +2594,14 @@ void RandomPlayerbotMgr::Refresh(Player* bot)
|
|||||||
|
|
||||||
bool RandomPlayerbotMgr::IsRandomBot(Player* bot)
|
bool RandomPlayerbotMgr::IsRandomBot(Player* bot)
|
||||||
{
|
{
|
||||||
if (bot && GET_PLAYERBOT_AI(bot))
|
if (!bot)
|
||||||
{
|
return false;
|
||||||
if (GET_PLAYERBOT_AI(bot)->IsRealPlayer())
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (bot)
|
|
||||||
{
|
|
||||||
return IsRandomBot(bot->GetGUID().GetCounter());
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot);
|
||||||
|
if (!botAI || botAI->IsRealPlayer())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return IsRandomBot(bot->GetGUID().GetCounter());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RandomPlayerbotMgr::IsRandomBot(ObjectGuid::LowType bot)
|
bool RandomPlayerbotMgr::IsRandomBot(ObjectGuid::LowType bot)
|
||||||
|
|||||||
45
src/database/PlayerbotSpellCache.cpp
Normal file
45
src/database/PlayerbotSpellCache.cpp
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
#include "PlayerbotSpellCache.h"
|
||||||
|
|
||||||
|
void PlayerbotSpellCache::Initialize()
|
||||||
|
{
|
||||||
|
LOG_INFO("playerbots",
|
||||||
|
"Playerbots: ListSpellsAction caches initialized");
|
||||||
|
for (uint32 j = 0; j < sSkillLineAbilityStore.GetNumRows(); ++j)
|
||||||
|
{
|
||||||
|
if (SkillLineAbilityEntry const* skillLine = sSkillLineAbilityStore.LookupEntry(j))
|
||||||
|
skillSpells[skillLine->Spell] = skillLine;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fill the vendorItems cache once from the world database.
|
||||||
|
QueryResult results = WorldDatabase.Query("SELECT item FROM npc_vendor WHERE maxcount = 0");
|
||||||
|
if (results)
|
||||||
|
{
|
||||||
|
do
|
||||||
|
{
|
||||||
|
Field* fields = results->Fetch();
|
||||||
|
int32 entry = fields[0].Get<int32>();
|
||||||
|
if (entry <= 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
vendorItems.insert(static_cast<uint32>(entry));
|
||||||
|
}
|
||||||
|
while (results->NextRow());
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_DEBUG("playerbots",
|
||||||
|
"ListSpellsAction: initialized caches (skillSpells={}, vendorItems={}).",
|
||||||
|
skillSpells.size(), vendorItems.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
SkillLineAbilityEntry const* PlayerbotSpellCache::GetSkillLine(uint32 spellId) const
|
||||||
|
{
|
||||||
|
auto itr = skillSpells.find(spellId);
|
||||||
|
if (itr != skillSpells.end())
|
||||||
|
return itr->second;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PlayerbotSpellCache::IsItemBuyable(uint32 itemId) const
|
||||||
|
{
|
||||||
|
return vendorItems.find(itemId) != vendorItems.end();
|
||||||
|
}
|
||||||
34
src/database/PlayerbotSpellCache.h
Normal file
34
src/database/PlayerbotSpellCache.h
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license, you may redistribute it
|
||||||
|
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _PLAYERBOT_PLAYERBOTSPELLCACHE_H
|
||||||
|
#define _PLAYERBOT_PLAYERBOTSPELLCACHE_H
|
||||||
|
|
||||||
|
#include "Playerbots.h"
|
||||||
|
|
||||||
|
class PlayerbotSpellCache
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static PlayerbotSpellCache* Instance()
|
||||||
|
{
|
||||||
|
static PlayerbotSpellCache instance;
|
||||||
|
return &instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Initialize(); // call once on startup
|
||||||
|
|
||||||
|
SkillLineAbilityEntry const* GetSkillLine(uint32 spellId) const;
|
||||||
|
bool IsItemBuyable(uint32 itemId) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
PlayerbotSpellCache() = default;
|
||||||
|
|
||||||
|
std::map<uint32, SkillLineAbilityEntry const*> skillSpells;
|
||||||
|
std::set<uint32> vendorItems;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define sPlayerbotSpellCache PlayerbotSpellCache::Instance()
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -4099,6 +4099,7 @@ void PlayerbotFactory::InitImmersive()
|
|||||||
|
|
||||||
void PlayerbotFactory::InitArenaTeam()
|
void PlayerbotFactory::InitArenaTeam()
|
||||||
{
|
{
|
||||||
|
|
||||||
if (!sPlayerbotAIConfig->IsInRandomAccountList(bot->GetSession()->GetAccountId()))
|
if (!sPlayerbotAIConfig->IsInRandomAccountList(bot->GetSession()->GetAccountId()))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -4185,10 +4186,34 @@ void PlayerbotFactory::InitArenaTeam()
|
|||||||
|
|
||||||
if (botcaptain && botcaptain->GetTeamId() == bot->GetTeamId()) // need?
|
if (botcaptain && botcaptain->GetTeamId() == bot->GetTeamId()) // need?
|
||||||
{
|
{
|
||||||
|
// Add bot to arena team
|
||||||
arenateam->AddMember(bot->GetGUID());
|
arenateam->AddMember(bot->GetGUID());
|
||||||
arenateam->SaveToDB();
|
|
||||||
|
// Only synchronize ratings once the team is full (avoid redundant work)
|
||||||
|
// The captain was added with incorrect ratings when the team was created,
|
||||||
|
// so we fix everyone's ratings once the roster is complete
|
||||||
|
if (arenateam->GetMembersSize() >= (uint32)arenateam->GetType())
|
||||||
|
{
|
||||||
|
uint32 teamRating = arenateam->GetRating();
|
||||||
|
|
||||||
|
// Use SetRatingForAll to align all members with team rating
|
||||||
|
arenateam->SetRatingForAll(teamRating);
|
||||||
|
|
||||||
|
// For bot-only teams, keep MMR synchronized with team rating
|
||||||
|
// This ensures matchmaking reflects the artificial team strength (1000-2000 range)
|
||||||
|
// instead of being influenced by the global CONFIG_ARENA_START_MATCHMAKER_RATING
|
||||||
|
for (auto& member : arenateam->GetMembers())
|
||||||
|
{
|
||||||
|
// Set MMR to match personal rating (which already matches team rating)
|
||||||
|
member.MatchMakerRating = member.PersonalRating;
|
||||||
|
member.MaxMMR = std::max(member.MaxMMR, member.PersonalRating);
|
||||||
|
}
|
||||||
|
// Force save all member data to database
|
||||||
|
arenateam->SaveToDB(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
arenateams.erase(arenateams.begin() + index);
|
arenateams.erase(arenateams.begin() + index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -121,7 +121,7 @@ public:
|
|||||||
creators["shoot"] = &ActionContext::shoot;
|
creators["shoot"] = &ActionContext::shoot;
|
||||||
creators["follow"] = &ActionContext::follow;
|
creators["follow"] = &ActionContext::follow;
|
||||||
creators["move from group"] = &ActionContext::move_from_group;
|
creators["move from group"] = &ActionContext::move_from_group;
|
||||||
creators["flee to master"] = &ActionContext::flee_to_master;
|
creators["flee to group leader"] = &ActionContext::flee_to_group_leader;
|
||||||
creators["runaway"] = &ActionContext::runaway;
|
creators["runaway"] = &ActionContext::runaway;
|
||||||
creators["stay"] = &ActionContext::stay;
|
creators["stay"] = &ActionContext::stay;
|
||||||
creators["sit"] = &ActionContext::sit;
|
creators["sit"] = &ActionContext::sit;
|
||||||
@@ -318,7 +318,7 @@ private:
|
|||||||
static Action* runaway(PlayerbotAI* botAI) { return new RunAwayAction(botAI); }
|
static Action* runaway(PlayerbotAI* botAI) { return new RunAwayAction(botAI); }
|
||||||
static Action* follow(PlayerbotAI* botAI) { return new FollowAction(botAI); }
|
static Action* follow(PlayerbotAI* botAI) { return new FollowAction(botAI); }
|
||||||
static Action* move_from_group(PlayerbotAI* botAI) { return new MoveFromGroupAction(botAI); }
|
static Action* move_from_group(PlayerbotAI* botAI) { return new MoveFromGroupAction(botAI); }
|
||||||
static Action* flee_to_master(PlayerbotAI* botAI) { return new FleeToMasterAction(botAI); }
|
static Action* flee_to_group_leader(PlayerbotAI* botAI) { return new FleeToGroupLeaderAction(botAI); }
|
||||||
static Action* add_gathering_loot(PlayerbotAI* botAI) { return new AddGatheringLootAction(botAI); }
|
static Action* add_gathering_loot(PlayerbotAI* botAI) { return new AddGatheringLootAction(botAI); }
|
||||||
static Action* add_loot(PlayerbotAI* botAI) { return new AddLootAction(botAI); }
|
static Action* add_loot(PlayerbotAI* botAI) { return new AddLootAction(botAI); }
|
||||||
static Action* add_all_loot(PlayerbotAI* botAI) { return new AddAllLootAction(botAI); }
|
static Action* add_all_loot(PlayerbotAI* botAI) { return new AddAllLootAction(botAI); }
|
||||||
|
|||||||
@@ -15,20 +15,19 @@
|
|||||||
#include "SharedDefines.h"
|
#include "SharedDefines.h"
|
||||||
#include "Unit.h"
|
#include "Unit.h"
|
||||||
|
|
||||||
bool AttackAction::Execute(Event event)
|
bool AttackAction::Execute(Event /*event*/)
|
||||||
{
|
{
|
||||||
Unit* target = GetTarget();
|
Unit* target = GetTarget();
|
||||||
if (!target)
|
if (!target)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!target->IsInWorld())
|
if (!target->IsInWorld())
|
||||||
{
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
return Attack(target);
|
return Attack(target);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AttackMyTargetAction::Execute(Event event)
|
bool AttackMyTargetAction::Execute(Event /*event*/)
|
||||||
{
|
{
|
||||||
Player* master = GetMaster();
|
Player* master = GetMaster();
|
||||||
if (!master)
|
if (!master)
|
||||||
@@ -51,7 +50,7 @@ bool AttackMyTargetAction::Execute(Event event)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AttackAction::Attack(Unit* target, bool with_pet /*true*/)
|
bool AttackAction::Attack(Unit* target, bool /*with_pet*/ /*true*/)
|
||||||
{
|
{
|
||||||
Unit* oldTarget = context->GetValue<Unit*>("current target")->Get();
|
Unit* oldTarget = context->GetValue<Unit*>("current target")->Get();
|
||||||
bool shouldMelee = bot->IsWithinMeleeRange(target) || botAI->IsMelee(bot);
|
bool shouldMelee = bot->IsWithinMeleeRange(target) || botAI->IsMelee(bot);
|
||||||
@@ -81,12 +80,15 @@ bool AttackAction::Attack(Unit* target, bool with_pet /*true*/)
|
|||||||
{
|
{
|
||||||
if (verbose)
|
if (verbose)
|
||||||
botAI->TellError(std::string(target->GetName()) + " is no longer in the world.");
|
botAI->TellError(std::string(target->GetName()) + " is no longer in the world.");
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((sPlayerbotAIConfig->IsInPvpProhibitedZone(bot->GetZoneId()) ||
|
// Check if bot OR target is in prohibited zone/area (skip for duels)
|
||||||
sPlayerbotAIConfig->IsInPvpProhibitedArea(bot->GetAreaId()))
|
if ((target->IsPlayer() || target->IsPet()) &&
|
||||||
&& (target->IsPlayer() || target->IsPet()))
|
(!bot->duel || bot->duel->Opponent != target) &&
|
||||||
|
(sPlayerbotAIConfig->IsPvpProhibited(bot->GetZoneId(), bot->GetAreaId()) ||
|
||||||
|
sPlayerbotAIConfig->IsPvpProhibited(target->GetZoneId(), target->GetAreaId())))
|
||||||
{
|
{
|
||||||
if (verbose)
|
if (verbose)
|
||||||
botAI->TellError("I cannot attack other players in PvP prohibited areas.");
|
botAI->TellError("I cannot attack other players in PvP prohibited areas.");
|
||||||
@@ -98,6 +100,7 @@ bool AttackAction::Attack(Unit* target, bool with_pet /*true*/)
|
|||||||
{
|
{
|
||||||
if (verbose)
|
if (verbose)
|
||||||
botAI->TellError(std::string(target->GetName()) + " is friendly to me.");
|
botAI->TellError(std::string(target->GetName()) + " is friendly to me.");
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,6 +108,7 @@ bool AttackAction::Attack(Unit* target, bool with_pet /*true*/)
|
|||||||
{
|
{
|
||||||
if (verbose)
|
if (verbose)
|
||||||
botAI->TellError(std::string(target->GetName()) + " is dead.");
|
botAI->TellError(std::string(target->GetName()) + " is dead.");
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -112,6 +116,7 @@ bool AttackAction::Attack(Unit* target, bool with_pet /*true*/)
|
|||||||
{
|
{
|
||||||
if (verbose)
|
if (verbose)
|
||||||
botAI->TellError(std::string(target->GetName()) + " is not in my sight.");
|
botAI->TellError(std::string(target->GetName()) + " is not in my sight.");
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -119,6 +124,7 @@ bool AttackAction::Attack(Unit* target, bool with_pet /*true*/)
|
|||||||
{
|
{
|
||||||
if (verbose)
|
if (verbose)
|
||||||
botAI->TellError("I am already attacking " + std::string(target->GetName()) + ".");
|
botAI->TellError("I am already attacking " + std::string(target->GetName()) + ".");
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -154,9 +160,8 @@ bool AttackAction::Attack(Unit* target, bool with_pet /*true*/)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (IsMovingAllowed() && !bot->HasInArc(CAST_ANGLE_IN_FRONT, target))
|
if (IsMovingAllowed() && !bot->HasInArc(CAST_ANGLE_IN_FRONT, target))
|
||||||
{
|
|
||||||
sServerFacade->SetFacingTo(bot, target);
|
sServerFacade->SetFacingTo(bot, target);
|
||||||
}
|
|
||||||
botAI->ChangeEngine(BOT_STATE_COMBAT);
|
botAI->ChangeEngine(BOT_STATE_COMBAT);
|
||||||
|
|
||||||
bot->Attack(target, shouldMelee);
|
bot->Attack(target, shouldMelee);
|
||||||
@@ -186,4 +191,4 @@ bool AttackAction::Attack(Unit* target, bool with_pet /*true*/)
|
|||||||
|
|
||||||
bool AttackDuelOpponentAction::isUseful() { return AI_VALUE(Unit*, "duel target"); }
|
bool AttackDuelOpponentAction::isUseful() { return AI_VALUE(Unit*, "duel target"); }
|
||||||
|
|
||||||
bool AttackDuelOpponentAction::Execute(Event event) { return Attack(AI_VALUE(Unit*, "duel target")); }
|
bool AttackDuelOpponentAction::Execute(Event /*event*/) { return Attack(AI_VALUE(Unit*, "duel target")); }
|
||||||
|
|||||||
@@ -311,7 +311,7 @@ bool ChooseRpgTargetAction::isFollowValid(Player* bot, WorldObject* target)
|
|||||||
bool ChooseRpgTargetAction::isFollowValid(Player* bot, WorldPosition pos)
|
bool ChooseRpgTargetAction::isFollowValid(Player* bot, WorldPosition pos)
|
||||||
{
|
{
|
||||||
PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot);
|
PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot);
|
||||||
Player* gmaster = botAI->GetGroupMaster();
|
Player* groupLeader = botAI->GetGroupLeader();
|
||||||
Player* realMaster = botAI->GetMaster();
|
Player* realMaster = botAI->GetMaster();
|
||||||
AiObjectContext* context = botAI->GetAiObjectContext();
|
AiObjectContext* context = botAI->GetAiObjectContext();
|
||||||
|
|
||||||
@@ -327,30 +327,30 @@ bool ChooseRpgTargetAction::isFollowValid(Player* bot, WorldPosition pos)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!gmaster || bot == gmaster)
|
if (!groupLeader || bot == groupLeader)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (!botAI->HasStrategy("follow", BOT_STATE_NON_COMBAT))
|
if (!botAI->HasStrategy("follow", BOT_STATE_NON_COMBAT))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (bot->GetDistance(gmaster) > sPlayerbotAIConfig->rpgDistance * 2)
|
if (bot->GetDistance(groupLeader) > sPlayerbotAIConfig->rpgDistance * 2)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
Formation* formation = AI_VALUE(Formation*, "formation");
|
Formation* formation = AI_VALUE(Formation*, "formation");
|
||||||
float distance = gmaster->GetDistance2d(pos.getX(), pos.getY());
|
float distance = groupLeader->GetDistance2d(pos.getX(), pos.getY());
|
||||||
|
|
||||||
if (!botAI->HasActivePlayerMaster() && distance < 50.0f)
|
if (!botAI->HasActivePlayerMaster() && distance < 50.0f)
|
||||||
{
|
{
|
||||||
Player* player = gmaster;
|
Player* player = groupLeader;
|
||||||
if (gmaster && !gmaster->isMoving() ||
|
if (groupLeader && !groupLeader->isMoving() ||
|
||||||
PAI_VALUE(WorldPosition, "last long move").distance(pos) < sPlayerbotAIConfig->reactDistance)
|
PAI_VALUE(WorldPosition, "last long move").distance(pos) < sPlayerbotAIConfig->reactDistance)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((inDungeon || !gmaster->HasPlayerFlag(PLAYER_FLAGS_RESTING)) && realMaster == gmaster && distance > 5.0f)
|
if ((inDungeon || !groupLeader->HasPlayerFlag(PLAYER_FLAGS_RESTING)) && realMaster == groupLeader && distance > 5.0f)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!gmaster->isMoving() && distance < 25.0f)
|
if (!groupLeader->isMoving() && distance < 25.0f)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (distance < formation->GetMaxDistance())
|
if (distance < formation->GetMaxDistance())
|
||||||
|
|||||||
@@ -180,7 +180,7 @@ void ChooseTravelTargetAction::getNewTarget(TravelTarget* newTarget, TravelTarge
|
|||||||
void ChooseTravelTargetAction::setNewTarget(TravelTarget* newTarget, TravelTarget* oldTarget)
|
void ChooseTravelTargetAction::setNewTarget(TravelTarget* newTarget, TravelTarget* oldTarget)
|
||||||
{
|
{
|
||||||
// Tell the master where we are going.
|
// Tell the master where we are going.
|
||||||
if (!bot->GetGroup() || (botAI->GetGroupMaster() == bot))
|
if (!bot->GetGroup() || (botAI->GetGroupLeader() == bot))
|
||||||
ReportTravelTarget(newTarget, oldTarget);
|
ReportTravelTarget(newTarget, oldTarget);
|
||||||
|
|
||||||
// If we are heading to a creature/npc clear it from the ignore list.
|
// If we are heading to a creature/npc clear it from the ignore list.
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ bool FollowAction::isUseful()
|
|||||||
if (!target.empty())
|
if (!target.empty())
|
||||||
fTarget = AI_VALUE(Unit*, target);
|
fTarget = AI_VALUE(Unit*, target);
|
||||||
else
|
else
|
||||||
fTarget = AI_VALUE(Unit*, "master target");
|
fTarget = AI_VALUE(Unit*, "group leader");
|
||||||
|
|
||||||
if (fTarget)
|
if (fTarget)
|
||||||
{
|
{
|
||||||
@@ -114,9 +114,9 @@ bool FollowAction::CanDeadFollow(Unit* target)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FleeToMasterAction::Execute(Event event)
|
bool FleeToGroupLeaderAction::Execute(Event event)
|
||||||
{
|
{
|
||||||
Unit* fTarget = AI_VALUE(Unit*, "master target");
|
Unit* fTarget = AI_VALUE(Unit*, "group leader");
|
||||||
bool canFollow = Follow(fTarget);
|
bool canFollow = Follow(fTarget);
|
||||||
if (!canFollow)
|
if (!canFollow)
|
||||||
{
|
{
|
||||||
@@ -146,22 +146,22 @@ bool FleeToMasterAction::Execute(Event event)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FleeToMasterAction::isUseful()
|
bool FleeToGroupLeaderAction::isUseful()
|
||||||
{
|
{
|
||||||
if (!botAI->GetGroupMaster())
|
if (!botAI->GetGroupLeader())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (botAI->GetGroupMaster() == bot)
|
if (botAI->GetGroupLeader() == bot)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
Unit* target = AI_VALUE(Unit*, "current target");
|
Unit* target = AI_VALUE(Unit*, "current target");
|
||||||
if (target && botAI->GetGroupMaster()->GetTarget() == target->GetGUID())
|
if (target && botAI->GetGroupLeader()->GetTarget() == target->GetGUID())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!botAI->HasStrategy("follow", BOT_STATE_NON_COMBAT))
|
if (!botAI->HasStrategy("follow", BOT_STATE_NON_COMBAT))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
Unit* fTarget = AI_VALUE(Unit*, "master target");
|
Unit* fTarget = AI_VALUE(Unit*, "group leader");
|
||||||
|
|
||||||
if (!CanDeadFollow(fTarget))
|
if (!CanDeadFollow(fTarget))
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -20,10 +20,10 @@ public:
|
|||||||
bool CanDeadFollow(Unit* target);
|
bool CanDeadFollow(Unit* target);
|
||||||
};
|
};
|
||||||
|
|
||||||
class FleeToMasterAction : public FollowAction
|
class FleeToGroupLeaderAction : public FollowAction
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
FleeToMasterAction(PlayerbotAI* botAI) : FollowAction(botAI, "flee to master") {}
|
FleeToGroupLeaderAction(PlayerbotAI* botAI) : FollowAction(botAI, "flee to group leader") {}
|
||||||
|
|
||||||
bool Execute(Event event) override;
|
bool Execute(Event event) override;
|
||||||
bool isUseful() override;
|
bool isUseful() override;
|
||||||
|
|||||||
@@ -141,7 +141,7 @@ bool InviteNearbyToGroupAction::isUseful()
|
|||||||
if (group->isRaidGroup() && group->IsFull())
|
if (group->isRaidGroup() && group->IsFull())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (botAI->GetGroupMaster() != bot)
|
if (botAI->GetGroupLeader() != bot)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
uint32 memberCount = group->GetMembersCount();
|
uint32 memberCount = group->GetMembersCount();
|
||||||
|
|||||||
@@ -109,22 +109,22 @@ bool LeaveFarAwayAction::isUseful()
|
|||||||
if (!bot->GetGroup())
|
if (!bot->GetGroup())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
Player* master = botAI->GetGroupMaster();
|
Player* groupLeader = botAI->GetGroupLeader();
|
||||||
Player* trueMaster = botAI->GetMaster();
|
Player* trueMaster = botAI->GetMaster();
|
||||||
if (!master || (bot == master && !botAI->IsRealPlayer()))
|
if (!groupLeader || (bot == groupLeader && !botAI->IsRealPlayer()))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
PlayerbotAI* masterBotAI = nullptr;
|
PlayerbotAI* groupLeaderBotAI = nullptr;
|
||||||
if (master)
|
if (groupLeader)
|
||||||
masterBotAI = GET_PLAYERBOT_AI(master);
|
groupLeaderBotAI = GET_PLAYERBOT_AI(groupLeader);
|
||||||
if (master && !masterBotAI)
|
if (groupLeader && !groupLeaderBotAI)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (trueMaster && !GET_PLAYERBOT_AI(trueMaster))
|
if (trueMaster && !GET_PLAYERBOT_AI(trueMaster))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (botAI->IsAlt() &&
|
if (botAI->IsAlt() &&
|
||||||
(!masterBotAI || masterBotAI->IsRealPlayer())) // Don't leave group when alt grouped with player master.
|
(!groupLeaderBotAI || groupLeaderBotAI->IsRealPlayer())) // Don't leave group when alt grouped with player groupLeader.
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (botAI->GetGrouperType() == GrouperType::SOLO)
|
if (botAI->GetGrouperType() == GrouperType::SOLO)
|
||||||
@@ -138,19 +138,19 @@ bool LeaveFarAwayAction::isUseful()
|
|||||||
if (dCount > 4 && !botAI->HasRealPlayerMaster())
|
if (dCount > 4 && !botAI->HasRealPlayerMaster())
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (bot->GetGuildId() == master->GetGuildId())
|
if (bot->GetGuildId() == groupLeader->GetGuildId())
|
||||||
{
|
{
|
||||||
if (bot->GetLevel() > master->GetLevel() + 5)
|
if (bot->GetLevel() > groupLeader->GetLevel() + 5)
|
||||||
{
|
{
|
||||||
if (AI_VALUE(bool, "should get money"))
|
if (AI_VALUE(bool, "should get money"))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (abs(int32(master->GetLevel() - bot->GetLevel())) > 4)
|
if (abs(int32(groupLeader->GetLevel() - bot->GetLevel())) > 4)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (bot->GetMapId() != master->GetMapId() || bot->GetDistance2d(master) >= 2 * sPlayerbotAIConfig->rpgDistance)
|
if (bot->GetMapId() != groupLeader->GetMapId() || bot->GetDistance2d(groupLeader) >= 2 * sPlayerbotAIConfig->rpgDistance)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,90 +7,43 @@
|
|||||||
|
|
||||||
#include "Event.h"
|
#include "Event.h"
|
||||||
#include "Playerbots.h"
|
#include "Playerbots.h"
|
||||||
|
#include "PlayerbotSpellCache.h"
|
||||||
|
|
||||||
std::map<uint32, SkillLineAbilityEntry const*> ListSpellsAction::skillSpells;
|
using SpellListEntry = std::pair<uint32, std::string>;
|
||||||
std::set<uint32> ListSpellsAction::vendorItems;
|
|
||||||
|
|
||||||
bool CompareSpells(const std::pair<uint32, std::string>& s1, const std::pair<uint32, std::string>& s2)
|
// CHANGE: Simplified and cheap comparator used in MapUpdater worker thread.
|
||||||
|
// It now avoids scanning the entire SkillLineAbilityStore for each comparison
|
||||||
|
// and only relies on spell school and spell name to keep sorting fast and bounded.
|
||||||
|
// lhs = the left element, rhs = the right element.
|
||||||
|
static bool CompareSpells(SpellListEntry const& lhSpell, SpellListEntry const& rhSpell)
|
||||||
{
|
{
|
||||||
SpellInfo const* si1 = sSpellMgr->GetSpellInfo(s1.first);
|
SpellInfo const* lhSpellInfo = sSpellMgr->GetSpellInfo(lhSpell.first);
|
||||||
SpellInfo const* si2 = sSpellMgr->GetSpellInfo(s2.first);
|
SpellInfo const* rhSpellInfo = sSpellMgr->GetSpellInfo(rhSpell.first);
|
||||||
if (!si1 || !si2)
|
|
||||||
|
if (!lhSpellInfo || !rhSpellInfo)
|
||||||
{
|
{
|
||||||
LOG_ERROR("playerbots", "SpellInfo missing. {} {}", s1.first, s2.first);
|
LOG_ERROR("playerbots", "SpellInfo missing for spell {} or {}", lhSpell.first, rhSpell.first);
|
||||||
return false;
|
// Fallback: order by spell id to keep comparator strict and deterministic.
|
||||||
}
|
return lhSpell.first < rhSpell.first;
|
||||||
uint32 p1 = si1->SchoolMask * 20000;
|
|
||||||
uint32 p2 = si2->SchoolMask * 20000;
|
|
||||||
|
|
||||||
uint32 skill1 = 0, skill2 = 0;
|
|
||||||
uint32 skillValue1 = 0, skillValue2 = 0;
|
|
||||||
for (uint32 j = 0; j < sSkillLineAbilityStore.GetNumRows(); ++j)
|
|
||||||
{
|
|
||||||
if (SkillLineAbilityEntry const* skillLine = sSkillLineAbilityStore.LookupEntry(j))
|
|
||||||
{
|
|
||||||
if (skillLine->Spell == s1.first)
|
|
||||||
{
|
|
||||||
skill1 = skillLine->SkillLine;
|
|
||||||
skillValue1 = skillLine->TrivialSkillLineRankLow;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (skillLine->Spell == s2.first)
|
|
||||||
{
|
|
||||||
skill2 = skillLine->SkillLine;
|
|
||||||
skillValue2 = skillLine->TrivialSkillLineRankLow;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (skill1 && skill2)
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
p1 += skill1 * 500;
|
uint32 lhsKey = lhSpellInfo->SchoolMask;
|
||||||
p2 += skill2 * 500;
|
uint32 rhsKey = rhSpellInfo->SchoolMask;
|
||||||
|
|
||||||
p1 += skillValue1;
|
if (lhsKey == rhsKey)
|
||||||
p2 += skillValue2;
|
|
||||||
|
|
||||||
if (p1 == p2)
|
|
||||||
{
|
{
|
||||||
return strcmp(si1->SpellName[0], si2->SpellName[0]) > 0;
|
// Defensive check: if DBC data is broken and spell names are nullptr,
|
||||||
}
|
// fall back to id ordering instead of risking a crash in std::strcmp.
|
||||||
|
if (!lhSpellInfo->SpellName[0] || !rhSpellInfo->SpellName[0])
|
||||||
|
return lhSpell.first < rhSpell.first;
|
||||||
|
|
||||||
return p1 > p2;
|
return std::strcmp(lhSpellInfo->SpellName[0], rhSpellInfo->SpellName[0]) > 0;
|
||||||
|
}
|
||||||
|
return lhsKey > rhsKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::pair<uint32, std::string>> ListSpellsAction::GetSpellList(std::string filter)
|
std::vector<std::pair<uint32, std::string>> ListSpellsAction::GetSpellList(std::string filter)
|
||||||
{
|
{
|
||||||
if (skillSpells.empty())
|
|
||||||
{
|
|
||||||
for (uint32 j = 0; j < sSkillLineAbilityStore.GetNumRows(); ++j)
|
|
||||||
{
|
|
||||||
if (SkillLineAbilityEntry const* skillLine = sSkillLineAbilityStore.LookupEntry(j))
|
|
||||||
skillSpells[skillLine->Spell] = skillLine;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (vendorItems.empty())
|
|
||||||
{
|
|
||||||
QueryResult results = WorldDatabase.Query("SELECT item FROM npc_vendor WHERE maxcount = 0");
|
|
||||||
if (results)
|
|
||||||
{
|
|
||||||
do
|
|
||||||
{
|
|
||||||
Field* fields = results->Fetch();
|
|
||||||
int32 entry = fields[0].Get<int32>();
|
|
||||||
if (entry <= 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
vendorItems.insert(entry);
|
|
||||||
} while (results->NextRow());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::ostringstream posOut;
|
|
||||||
std::ostringstream negOut;
|
|
||||||
|
|
||||||
uint32 skill = 0;
|
uint32 skill = 0;
|
||||||
|
|
||||||
std::vector<std::string> ss = split(filter, ' ');
|
std::vector<std::string> ss = split(filter, ' ');
|
||||||
@@ -99,13 +52,15 @@ std::vector<std::pair<uint32, std::string>> ListSpellsAction::GetSpellList(std::
|
|||||||
skill = chat->parseSkill(ss[0]);
|
skill = chat->parseSkill(ss[0]);
|
||||||
if (skill != SKILL_NONE)
|
if (skill != SKILL_NONE)
|
||||||
{
|
{
|
||||||
filter = ss.size() > 1 ? filter = ss[1] : "";
|
filter = ss.size() > 1 ? ss[1] : "";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ss[0] == "first" && ss[1] == "aid")
|
// Guard access to ss[1]/ss[2] to avoid out-of-bounds
|
||||||
|
// when the player only types "first" without "aid".
|
||||||
|
if (ss[0] == "first" && ss.size() > 1 && ss[1] == "aid")
|
||||||
{
|
{
|
||||||
skill = SKILL_FIRST_AID;
|
skill = SKILL_FIRST_AID;
|
||||||
filter = ss.size() > 2 ? filter = ss[2] : "";
|
filter = ss.size() > 2 ? ss[2] : "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,26 +70,57 @@ std::vector<std::pair<uint32, std::string>> ListSpellsAction::GetSpellList(std::
|
|||||||
|
|
||||||
uint32 minLevel = 0;
|
uint32 minLevel = 0;
|
||||||
uint32 maxLevel = 0;
|
uint32 maxLevel = 0;
|
||||||
if (filter.find("-") != std::string::npos)
|
if (filter.find('-') != std::string::npos)
|
||||||
{
|
{
|
||||||
std::vector<std::string> ff = split(filter, '-');
|
std::vector<std::string> ff = split(filter, '-');
|
||||||
minLevel = atoi(ff[0].c_str());
|
if (ff.size() >= 2)
|
||||||
maxLevel = atoi(ff[1].c_str());
|
{
|
||||||
filter = "";
|
minLevel = std::atoi(ff[0].c_str());
|
||||||
|
maxLevel = std::atoi(ff[1].c_str());
|
||||||
|
if (minLevel > maxLevel)
|
||||||
|
std::swap(minLevel, maxLevel);
|
||||||
|
}
|
||||||
|
filter.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool craftableOnly = false;
|
bool canCraftNow = false;
|
||||||
if (filter.find("+") != std::string::npos)
|
if (filter.find('+') != std::string::npos)
|
||||||
{
|
{
|
||||||
craftableOnly = true;
|
canCraftNow = true;
|
||||||
|
|
||||||
|
// Support "+<skill>" syntax (e.g. "spells +tailoring" or "spells tailoring+").
|
||||||
|
// If no explicit skill was detected yet, try to parse the filter (without '+')
|
||||||
|
// as a profession/skill name so that craftable-only filters still work with skills.
|
||||||
|
if (skill == SKILL_NONE)
|
||||||
|
{
|
||||||
|
std::string skillFilter = filter;
|
||||||
|
|
||||||
|
// Remove '+' before trying to interpret the first token as a skill name.
|
||||||
|
skillFilter.erase(remove(skillFilter.begin(), skillFilter.end(), '+'), skillFilter.end());
|
||||||
|
|
||||||
|
std::vector<std::string> skillTokens = split(skillFilter, ' ');
|
||||||
|
if (!skillTokens.empty())
|
||||||
|
{
|
||||||
|
uint32 parsedSkill = chat->parseSkill(skillTokens[0]);
|
||||||
|
if (parsedSkill != SKILL_NONE)
|
||||||
|
{
|
||||||
|
skill = parsedSkill;
|
||||||
|
|
||||||
|
// Any remaining text after the skill token becomes the "name" filter
|
||||||
|
// (e.g. "spells +tailoring cloth" -> skill = tailoring, filter = "cloth").
|
||||||
|
filter = skillTokens.size() > 1 ? skillTokens[1] : "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Finally remove '+' from the filter that will be used for name/range parsing.
|
||||||
filter.erase(remove(filter.begin(), filter.end(), '+'), filter.end());
|
filter.erase(remove(filter.begin(), filter.end(), '+'), filter.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32 slot = chat->parseSlot(filter);
|
uint32 slot = chat->parseSlot(filter);
|
||||||
if (slot != EQUIPMENT_SLOT_END)
|
if (slot != EQUIPMENT_SLOT_END)
|
||||||
filter = "";
|
filter.clear();
|
||||||
|
|
||||||
std::vector<std::pair<uint32, std::string>> spells;
|
std::vector<SpellListEntry> spells;
|
||||||
for (PlayerSpellMap::iterator itr = bot->GetSpellMap().begin(); itr != bot->GetSpellMap().end(); ++itr)
|
for (PlayerSpellMap::iterator itr = bot->GetSpellMap().begin(); itr != bot->GetSpellMap().end(); ++itr)
|
||||||
{
|
{
|
||||||
if (itr->second->State == PLAYERSPELL_REMOVED || !itr->second->Active)
|
if (itr->second->State == PLAYERSPELL_REMOVED || !itr->second->Active)
|
||||||
@@ -150,7 +136,7 @@ std::vector<std::pair<uint32, std::string>> ListSpellsAction::GetSpellList(std::
|
|||||||
if (spellInfo->IsPassive())
|
if (spellInfo->IsPassive())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
SkillLineAbilityEntry const* skillLine = skillSpells[itr->first];
|
SkillLineAbilityEntry const* skillLine = sPlayerbotSpellCache->GetSkillLine(itr->first);
|
||||||
if (skill != SKILL_NONE && (!skillLine || skillLine->SkillLine != skill))
|
if (skill != SKILL_NONE && (!skillLine || skillLine->SkillLine != skill))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@@ -162,7 +148,7 @@ std::vector<std::pair<uint32, std::string>> ListSpellsAction::GetSpellList(std::
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
bool first = true;
|
bool first = true;
|
||||||
int32 craftCount = -1;
|
int32 craftsPossible = -1;
|
||||||
std::ostringstream materials;
|
std::ostringstream materials;
|
||||||
for (uint32 x = 0; x < MAX_SPELL_REAGENTS; ++x)
|
for (uint32 x = 0; x < MAX_SPELL_REAGENTS; ++x)
|
||||||
{
|
{
|
||||||
@@ -189,12 +175,12 @@ std::vector<std::pair<uint32, std::string>> ListSpellsAction::GetSpellList(std::
|
|||||||
|
|
||||||
FindItemByIdVisitor visitor(itemid);
|
FindItemByIdVisitor visitor(itemid);
|
||||||
uint32 reagentsInInventory = InventoryAction::GetItemCount(&visitor);
|
uint32 reagentsInInventory = InventoryAction::GetItemCount(&visitor);
|
||||||
bool buyable = (vendorItems.find(itemid) != vendorItems.end());
|
bool buyable = sPlayerbotSpellCache->IsItemBuyable(itemid);
|
||||||
if (!buyable)
|
if (!buyable)
|
||||||
{
|
{
|
||||||
uint32 craftable = reagentsInInventory / reagentsRequired;
|
uint32 craftable = reagentsInInventory / reagentsRequired;
|
||||||
if (craftCount < 0 || craftCount > craftable)
|
if (craftsPossible < 0 || craftsPossible > static_cast<int32>(craftable))
|
||||||
craftCount = craftable;
|
craftsPossible = static_cast<int32>(craftable);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (reagentsInInventory)
|
if (reagentsInInventory)
|
||||||
@@ -205,8 +191,8 @@ std::vector<std::pair<uint32, std::string>> ListSpellsAction::GetSpellList(std::
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (craftCount < 0)
|
if (craftsPossible < 0)
|
||||||
craftCount = 0;
|
craftsPossible = 0;
|
||||||
|
|
||||||
std::ostringstream out;
|
std::ostringstream out;
|
||||||
bool filtered = false;
|
bool filtered = false;
|
||||||
@@ -218,8 +204,8 @@ std::vector<std::pair<uint32, std::string>> ListSpellsAction::GetSpellList(std::
|
|||||||
{
|
{
|
||||||
if (ItemTemplate const* proto = sObjectMgr->GetItemTemplate(spellInfo->Effects[i].ItemType))
|
if (ItemTemplate const* proto = sObjectMgr->GetItemTemplate(spellInfo->Effects[i].ItemType))
|
||||||
{
|
{
|
||||||
if (craftCount)
|
if (craftsPossible)
|
||||||
out << "|cffffff00(x" << craftCount << ")|r ";
|
out << "|cffffff00(x" << craftsPossible << ")|r ";
|
||||||
|
|
||||||
out << chat->FormatItem(proto);
|
out << chat->FormatItem(proto);
|
||||||
|
|
||||||
@@ -246,7 +232,7 @@ std::vector<std::pair<uint32, std::string>> ListSpellsAction::GetSpellList(std::
|
|||||||
if (filtered)
|
if (filtered)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (craftableOnly && !craftCount)
|
if (canCraftNow && !craftsPossible)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
out << materials.str();
|
out << materials.str();
|
||||||
@@ -275,10 +261,9 @@ std::vector<std::pair<uint32, std::string>> ListSpellsAction::GetSpellList(std::
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (itr->first == 0)
|
if (itr->first == 0)
|
||||||
{
|
|
||||||
LOG_ERROR("playerbots", "?! {}", itr->first);
|
LOG_ERROR("playerbots", "?! {}", itr->first);
|
||||||
}
|
|
||||||
spells.push_back(std::pair<uint32, std::string>(itr->first, out.str()));
|
spells.emplace_back(itr->first, out.str());
|
||||||
alreadySeenList += spellInfo->SpellName[0];
|
alreadySeenList += spellInfo->SpellName[0];
|
||||||
alreadySeenList += ",";
|
alreadySeenList += ",";
|
||||||
}
|
}
|
||||||
@@ -294,25 +279,28 @@ bool ListSpellsAction::Execute(Event event)
|
|||||||
|
|
||||||
std::string const filter = event.getParam();
|
std::string const filter = event.getParam();
|
||||||
|
|
||||||
std::vector<std::pair<uint32, std::string>> spells = GetSpellList(filter);
|
std::vector<SpellListEntry> spells = GetSpellList(filter);
|
||||||
|
|
||||||
|
if (spells.empty())
|
||||||
|
{
|
||||||
|
// CHANGE: Give early feedback when no spells match the filter.
|
||||||
|
botAI->TellMaster("No spells found.");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
botAI->TellMaster("=== Spells ===");
|
botAI->TellMaster("=== Spells ===");
|
||||||
|
|
||||||
std::sort(spells.begin(), spells.end(), CompareSpells);
|
std::sort(spells.begin(), spells.end(), CompareSpells);
|
||||||
|
|
||||||
uint32 count = 0;
|
// CHANGE: Send the full spell list again so client-side addons
|
||||||
for (std::vector<std::pair<uint32, std::string>>::iterator i = spells.begin(); i != spells.end(); ++i)
|
// (e.g. Multibot / Unbot) can reconstruct the
|
||||||
{
|
// complete spellbook for configuration. The heavy part that caused
|
||||||
|
// freezes before was the old CompareSpells implementation scanning
|
||||||
|
// the entire SkillLineAbility DBC on every comparison. With the new
|
||||||
|
// cheap comparator above, sending all lines here is safe and keeps
|
||||||
|
// behaviour compatible with existing addons.
|
||||||
|
for (std::vector<SpellListEntry>::const_iterator i = spells.begin(); i != spells.end(); ++i)
|
||||||
botAI->TellMasterNoFacing(i->second);
|
botAI->TellMasterNoFacing(i->second);
|
||||||
|
|
||||||
// if (++count >= 50)
|
|
||||||
// {
|
|
||||||
// std::ostringstream msg;
|
|
||||||
// msg << (spells.size() - 50) << " more...";
|
|
||||||
// botAI->TellMasterNoFacing(msg.str());
|
|
||||||
// break;
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -18,9 +18,8 @@ public:
|
|||||||
bool Execute(Event event) override;
|
bool Execute(Event event) override;
|
||||||
virtual std::vector<std::pair<uint32, std::string>> GetSpellList(std::string filter = "");
|
virtual std::vector<std::pair<uint32, std::string>> GetSpellList(std::string filter = "");
|
||||||
|
|
||||||
private:
|
static void InitSpellCaches();
|
||||||
static std::map<uint32, SkillLineAbilityEntry const*> skillSpells;
|
|
||||||
static std::set<uint32> vendorItems;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ bool MoveToTravelTargetAction::Execute(Event event)
|
|||||||
WorldLocation location = *target->getPosition();
|
WorldLocation location = *target->getPosition();
|
||||||
|
|
||||||
Group* group = bot->GetGroup();
|
Group* group = bot->GetGroup();
|
||||||
if (group && !urand(0, 1) && bot == botAI->GetGroupMaster() && !bot->IsInCombat())
|
if (group && !urand(0, 1) && bot == botAI->GetGroupLeader() && !bot->IsInCombat())
|
||||||
{
|
{
|
||||||
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -946,25 +946,36 @@ bool MovementAction::IsWaitingForLastMove(MovementPriority priority)
|
|||||||
|
|
||||||
bool MovementAction::IsMovingAllowed()
|
bool MovementAction::IsMovingAllowed()
|
||||||
{
|
{
|
||||||
// do not allow if not vehicle driver
|
// Most common checks: confused, stunned, fleeing, jumping, charging. All these
|
||||||
if (botAI->IsInVehicle() && !botAI->IsInVehicle(true))
|
// states are set when handling certain aura effects. We don't check against
|
||||||
|
// UNIT_STATE_ROOT here, because this state is used by vehicles.
|
||||||
|
if (bot->HasUnitState(UNIT_STATE_LOST_CONTROL))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (bot->isFrozen() || bot->IsPolymorphed() || (bot->isDead() && !bot->HasPlayerFlag(PLAYER_FLAGS_GHOST)) ||
|
// Death state (w/o spirit release) and Spirit of Redemption aura (priest)
|
||||||
bot->IsBeingTeleported() || bot->HasRootAura() || bot->HasSpiritOfRedemptionAura() || bot->HasConfuseAura() ||
|
if ((bot->isDead() && !bot->HasPlayerFlag(PLAYER_FLAGS_GHOST)) || bot->HasSpiritOfRedemptionAura())
|
||||||
bot->IsCharmed() || bot->HasStunAura() || bot->IsInFlight() || bot->HasUnitState(UNIT_STATE_LOST_CONTROL))
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
// Common CC effects, ordered by frequency: rooted > frozen > polymorphed
|
||||||
|
if (bot->IsRooted() || bot->isFrozen() || bot->IsPolymorphed())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Check for the MM controlled slot types: feared, confused, fleeing, etc.
|
||||||
if (bot->GetMotionMaster()->GetMotionSlotType(MOTION_SLOT_CONTROLLED) != NULL_MOTION_TYPE)
|
if (bot->GetMotionMaster()->GetMotionSlotType(MOTION_SLOT_CONTROLLED) != NULL_MOTION_TYPE)
|
||||||
{
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
// if (bot->HasUnitMovementFlag(MOVEMENTFLAG_FALLING))
|
// Traveling state: taxi flight and being teleported (relatively rare)
|
||||||
// {
|
if (bot->IsInFlight() || bot->GetMotionMaster()->GetCurrentMovementGeneratorType() == FLIGHT_MOTION_TYPE ||
|
||||||
// return false;
|
bot->IsBeingTeleported())
|
||||||
// }
|
return false;
|
||||||
return bot->GetMotionMaster()->GetCurrentMovementGeneratorType() != FLIGHT_MOTION_TYPE;
|
|
||||||
|
// Vehicle state: is in the vehicle and can control it (rare, content-specific).
|
||||||
|
// We need to check charmed state AFTER vehicle one, cuz that's how it works:
|
||||||
|
// passengers are set to charmed by vehicle with CHARM_TYPE_VEHICLE.
|
||||||
|
if ((bot->GetVehicle() && !botAI->IsInVehicle(true)) || bot->IsCharmed())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MovementAction::Follow(Unit* target, float distance) { return Follow(target, distance, GetFollowAngle()); }
|
bool MovementAction::Follow(Unit* target, float distance) { return Follow(target, distance, GetFollowAngle()); }
|
||||||
@@ -972,12 +983,10 @@ bool MovementAction::Follow(Unit* target, float distance) { return Follow(target
|
|||||||
void MovementAction::UpdateMovementState()
|
void MovementAction::UpdateMovementState()
|
||||||
{
|
{
|
||||||
const bool isCurrentlyRestricted = // see if the bot is currently slowed, rooted, or otherwise unable to move
|
const bool isCurrentlyRestricted = // see if the bot is currently slowed, rooted, or otherwise unable to move
|
||||||
|
bot->HasUnitState(UNIT_STATE_LOST_CONTROL) ||
|
||||||
|
bot->IsRooted() ||
|
||||||
bot->isFrozen() ||
|
bot->isFrozen() ||
|
||||||
bot->IsPolymorphed() ||
|
bot->IsPolymorphed();
|
||||||
bot->HasRootAura() ||
|
|
||||||
bot->HasStunAura() ||
|
|
||||||
bot->HasConfuseAura() ||
|
|
||||||
bot->HasUnitState(UNIT_STATE_LOST_CONTROL);
|
|
||||||
|
|
||||||
// no update movement flags while movement is current restricted.
|
// no update movement flags while movement is current restricted.
|
||||||
if (!isCurrentlyRestricted && bot->IsAlive())
|
if (!isCurrentlyRestricted && bot->IsAlive())
|
||||||
@@ -1102,7 +1111,7 @@ void MovementAction::UpdateMovementState()
|
|||||||
// {
|
// {
|
||||||
// if (Unit* pTarget = sServerFacade->GetChaseTarget(bot))
|
// if (Unit* pTarget = sServerFacade->GetChaseTarget(bot))
|
||||||
// {
|
// {
|
||||||
// if (pTarget != botAI->GetGroupMaster())
|
// if (pTarget != botAI->GetGroupLeader())
|
||||||
// return;
|
// return;
|
||||||
|
|
||||||
// if (!bot->IsWithinMeleeRange(pTarget))
|
// if (!bot->IsWithinMeleeRange(pTarget))
|
||||||
@@ -2663,7 +2672,7 @@ bool DisperseSetAction::Execute(Event event)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RunAwayAction::Execute(Event event) { return Flee(AI_VALUE(Unit*, "master target")); }
|
bool RunAwayAction::Execute(Event event) { return Flee(AI_VALUE(Unit*, "group leader")); }
|
||||||
|
|
||||||
bool MoveToLootAction::Execute(Event event)
|
bool MoveToLootAction::Execute(Event event)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -18,14 +18,14 @@ bool PetsAction::Execute(Event event)
|
|||||||
// Extract the command parameter from the event (e.g., "aggressive", "defensive", "attack", etc.)
|
// Extract the command parameter from the event (e.g., "aggressive", "defensive", "attack", etc.)
|
||||||
std::string param = event.getParam();
|
std::string param = event.getParam();
|
||||||
if (param.empty() && !defaultCmd.empty())
|
if (param.empty() && !defaultCmd.empty())
|
||||||
{
|
|
||||||
param = defaultCmd;
|
param = defaultCmd;
|
||||||
}
|
|
||||||
|
|
||||||
if (param.empty())
|
if (param.empty())
|
||||||
{
|
{
|
||||||
// If no parameter is provided, show usage instructions and return.
|
// If no parameter is provided, show usage instructions and return.
|
||||||
botAI->TellError("Usage: pet <aggressive|defensive|passive|stance|attack|follow|stay>");
|
std::string text = sPlayerbotTextMgr->GetBotTextOrDefault(
|
||||||
|
"pet_usage_error", "Usage: pet <aggressive|defensive|passive|stance|attack|follow|stay>", {});
|
||||||
|
botAI->TellError(text);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,7 +52,9 @@ bool PetsAction::Execute(Event event)
|
|||||||
// If no pets or guardians are found, notify and return.
|
// If no pets or guardians are found, notify and return.
|
||||||
if (targets.empty())
|
if (targets.empty())
|
||||||
{
|
{
|
||||||
botAI->TellError("You have no pet or guardian pet.");
|
std::string text = sPlayerbotTextMgr->GetBotTextOrDefault(
|
||||||
|
"pet_no_pet_error", "You have no pet or guardian pet.", {});
|
||||||
|
botAI->TellError(text);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,42 +65,54 @@ bool PetsAction::Execute(Event event)
|
|||||||
if (param == "aggressive")
|
if (param == "aggressive")
|
||||||
{
|
{
|
||||||
react = REACT_AGGRESSIVE;
|
react = REACT_AGGRESSIVE;
|
||||||
stanceText = "aggressive";
|
stanceText = sPlayerbotTextMgr->GetBotTextOrDefault(
|
||||||
|
"pet_stance_aggressive", "aggressive", {});
|
||||||
}
|
}
|
||||||
else if (param == "defensive")
|
else if (param == "defensive")
|
||||||
{
|
{
|
||||||
react = REACT_DEFENSIVE;
|
react = REACT_DEFENSIVE;
|
||||||
stanceText = "defensive";
|
stanceText = sPlayerbotTextMgr->GetBotTextOrDefault(
|
||||||
|
"pet_stance_defensive", "defensive", {});
|
||||||
}
|
}
|
||||||
else if (param == "passive")
|
else if (param == "passive")
|
||||||
{
|
{
|
||||||
react = REACT_PASSIVE;
|
react = REACT_PASSIVE;
|
||||||
stanceText = "passive";
|
stanceText = sPlayerbotTextMgr->GetBotTextOrDefault(
|
||||||
|
"pet_stance_passive", "passive", {});
|
||||||
}
|
}
|
||||||
// The "stance" command simply reports the current stance of each pet/guardian.
|
// The "stance" command simply reports the current stance of each pet/guardian.
|
||||||
else if (param == "stance")
|
else if (param == "stance")
|
||||||
{
|
{
|
||||||
for (Creature* target : targets)
|
for (Creature* target : targets)
|
||||||
{
|
{
|
||||||
std::string type = target->IsPet() ? "pet" : "guardian";
|
std::string type = target->IsPet() ?
|
||||||
|
sPlayerbotTextMgr->GetBotTextOrDefault("pet_type_pet", "pet", {}) :
|
||||||
|
sPlayerbotTextMgr->GetBotTextOrDefault("pet_type_guardian", "guardian", {});
|
||||||
std::string name = target->GetName();
|
std::string name = target->GetName();
|
||||||
std::string stance;
|
std::string stance;
|
||||||
switch (target->GetReactState())
|
switch (target->GetReactState())
|
||||||
{
|
{
|
||||||
case REACT_AGGRESSIVE:
|
case REACT_AGGRESSIVE:
|
||||||
stance = "aggressive";
|
stance = sPlayerbotTextMgr->GetBotTextOrDefault(
|
||||||
|
"pet_stance_aggressive", "aggressive", {});
|
||||||
break;
|
break;
|
||||||
case REACT_DEFENSIVE:
|
case REACT_DEFENSIVE:
|
||||||
stance = "defensive";
|
stance = sPlayerbotTextMgr->GetBotTextOrDefault(
|
||||||
|
"pet_stance_defensive", "defensive", {});
|
||||||
break;
|
break;
|
||||||
case REACT_PASSIVE:
|
case REACT_PASSIVE:
|
||||||
stance = "passive";
|
stance = sPlayerbotTextMgr->GetBotTextOrDefault(
|
||||||
|
"pet_stance_passive", "passive", {});
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
stance = "unknown";
|
stance = sPlayerbotTextMgr->GetBotTextOrDefault(
|
||||||
|
"pet_stance_unknown", "unknown", {});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
botAI->TellMaster("Current stance of " + type + " \"" + name + "\": " + stance + ".");
|
std::string text = sPlayerbotTextMgr->GetBotTextOrDefault(
|
||||||
|
"pet_stance_report", "Current stance of %type \"%name\": %stance.",
|
||||||
|
{{"type", type}, {"name", name}, {"stance", stance}});
|
||||||
|
botAI->TellMaster(text);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -113,25 +127,38 @@ bool PetsAction::Execute(Event event)
|
|||||||
{
|
{
|
||||||
ObjectGuid masterTargetGuid = master->GetTarget();
|
ObjectGuid masterTargetGuid = master->GetTarget();
|
||||||
if (!masterTargetGuid.IsEmpty())
|
if (!masterTargetGuid.IsEmpty())
|
||||||
{
|
|
||||||
targetUnit = botAI->GetUnit(masterTargetGuid);
|
targetUnit = botAI->GetUnit(masterTargetGuid);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If no valid target is selected, show an error and return.
|
// If no valid target is selected, show an error and return.
|
||||||
if (!targetUnit)
|
if (!targetUnit)
|
||||||
{
|
{
|
||||||
botAI->TellError("No valid target selected by master.");
|
std::string text = sPlayerbotTextMgr->GetBotTextOrDefault(
|
||||||
|
"pet_no_target_error", "No valid target selected by master.", {});
|
||||||
|
botAI->TellError(text);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!targetUnit->IsAlive())
|
if (!targetUnit->IsAlive())
|
||||||
{
|
{
|
||||||
botAI->TellError("Target is not alive.");
|
std::string text = sPlayerbotTextMgr->GetBotTextOrDefault(
|
||||||
|
"pet_target_dead_error", "Target is not alive.", {});
|
||||||
|
botAI->TellError(text);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!bot->IsValidAttackTarget(targetUnit))
|
if (!bot->IsValidAttackTarget(targetUnit))
|
||||||
{
|
{
|
||||||
botAI->TellError("Target is not a valid attack target for the bot.");
|
std::string text = sPlayerbotTextMgr->GetBotTextOrDefault(
|
||||||
|
"pet_invalid_target_error", "Target is not a valid attack target for the bot.", {});
|
||||||
|
botAI->TellError(text);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (sPlayerbotAIConfig->IsPvpProhibited(bot->GetZoneId(), bot->GetAreaId()) &&
|
||||||
|
(targetUnit->IsPlayer() || targetUnit->IsPet()) &&
|
||||||
|
(!bot->duel || bot->duel->Opponent != targetUnit))
|
||||||
|
{
|
||||||
|
std::string text = sPlayerbotTextMgr->GetBotTextOrDefault(
|
||||||
|
"pet_pvp_prohibited_error", "I cannot command my pet to attack players in PvP prohibited areas.", {});
|
||||||
|
botAI->TellError(text);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -182,9 +209,17 @@ bool PetsAction::Execute(Event event)
|
|||||||
}
|
}
|
||||||
// Inform the master if the command succeeded or failed.
|
// Inform the master if the command succeeded or failed.
|
||||||
if (didAttack && sPlayerbotAIConfig->petChatCommandDebug == 1)
|
if (didAttack && sPlayerbotAIConfig->petChatCommandDebug == 1)
|
||||||
botAI->TellMaster("Pet commanded to attack your target.");
|
{
|
||||||
|
std::string text = sPlayerbotTextMgr->GetBotTextOrDefault(
|
||||||
|
"pet_attack_success", "Pet commanded to attack your target.", {});
|
||||||
|
botAI->TellMaster(text);
|
||||||
|
}
|
||||||
else if (!didAttack)
|
else if (!didAttack)
|
||||||
botAI->TellError("Pet did not attack. (Already attacking or unable to attack target)");
|
{
|
||||||
|
std::string text = sPlayerbotTextMgr->GetBotTextOrDefault(
|
||||||
|
"pet_attack_failed", "Pet did not attack. (Already attacking or unable to attack target)", {});
|
||||||
|
botAI->TellError(text);
|
||||||
|
}
|
||||||
return didAttack;
|
return didAttack;
|
||||||
}
|
}
|
||||||
// The "follow" command makes all pets/guardians follow the bot.
|
// The "follow" command makes all pets/guardians follow the bot.
|
||||||
@@ -192,7 +227,11 @@ bool PetsAction::Execute(Event event)
|
|||||||
{
|
{
|
||||||
botAI->PetFollow();
|
botAI->PetFollow();
|
||||||
if (sPlayerbotAIConfig->petChatCommandDebug == 1)
|
if (sPlayerbotAIConfig->petChatCommandDebug == 1)
|
||||||
botAI->TellMaster("Pet commanded to follow.");
|
{
|
||||||
|
std::string text = sPlayerbotTextMgr->GetBotTextOrDefault(
|
||||||
|
"pet_follow_success", "Pet commanded to follow.", {});
|
||||||
|
botAI->TellMaster(text);
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
// The "stay" command causes all pets/guardians to stop and stay in place.
|
// The "stay" command causes all pets/guardians to stop and stay in place.
|
||||||
@@ -229,14 +268,20 @@ bool PetsAction::Execute(Event event)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (sPlayerbotAIConfig->petChatCommandDebug == 1)
|
if (sPlayerbotAIConfig->petChatCommandDebug == 1)
|
||||||
botAI->TellMaster("Pet commanded to stay.");
|
{
|
||||||
|
std::string text = sPlayerbotTextMgr->GetBotTextOrDefault(
|
||||||
|
"pet_stay_success", "Pet commanded to stay.", {});
|
||||||
|
botAI->TellMaster(text);
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
// Unknown command: show usage instructions and return.
|
// Unknown command: show usage instructions and return.
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
botAI->TellError("Unknown pet command: " + param +
|
std::string text = sPlayerbotTextMgr->GetBotTextOrDefault(
|
||||||
". Use: pet <aggressive|defensive|passive|stance|attack|follow|stay>");
|
"pet_unknown_command_error", "Unknown pet command: %param. Use: pet <aggressive|defensive|passive|stance|attack|follow|stay>",
|
||||||
|
{{"param", param}});
|
||||||
|
botAI->TellError(text);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -251,7 +296,12 @@ bool PetsAction::Execute(Event event)
|
|||||||
|
|
||||||
// Inform the master of the new stance if debug is enabled.
|
// Inform the master of the new stance if debug is enabled.
|
||||||
if (sPlayerbotAIConfig->petChatCommandDebug == 1)
|
if (sPlayerbotAIConfig->petChatCommandDebug == 1)
|
||||||
botAI->TellMaster("Pet stance set to " + stanceText + ".");
|
{
|
||||||
|
std::string text = sPlayerbotTextMgr->GetBotTextOrDefault(
|
||||||
|
"pet_stance_set_success", "Pet stance set to %stance.",
|
||||||
|
{{"stance", stanceText}});
|
||||||
|
botAI->TellMaster(text);
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -13,10 +13,10 @@ bool RandomBotUpdateAction::Execute(Event event)
|
|||||||
if (!sRandomPlayerbotMgr->IsRandomBot(bot))
|
if (!sRandomPlayerbotMgr->IsRandomBot(bot))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (bot->GetGroup() && botAI->GetGroupMaster())
|
if (bot->GetGroup() && botAI->GetGroupLeader())
|
||||||
{
|
{
|
||||||
PlayerbotAI* groupMasterBotAI = GET_PLAYERBOT_AI(botAI->GetGroupMaster());
|
PlayerbotAI* groupLeaderBotAI = GET_PLAYERBOT_AI(botAI->GetGroupLeader());
|
||||||
if (!groupMasterBotAI || groupMasterBotAI->IsRealPlayer())
|
if (!groupLeaderBotAI || groupLeaderBotAI->IsRealPlayer())
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -168,15 +168,15 @@ bool AutoReleaseSpiritAction::ShouldAutoRelease() const
|
|||||||
if (!bot->GetGroup())
|
if (!bot->GetGroup())
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
Player* groupMaster = botAI->GetGroupMaster();
|
Player* groupLeader = botAI->GetGroupLeader();
|
||||||
if (!groupMaster || groupMaster == bot)
|
if (!groupLeader || groupLeader == bot)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (!botAI->HasActivePlayerMaster())
|
if (!botAI->HasActivePlayerMaster())
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (botAI->HasActivePlayerMaster() &&
|
if (botAI->HasActivePlayerMaster() &&
|
||||||
groupMaster->GetMapId() == bot->GetMapId() &&
|
groupLeader->GetMapId() == bot->GetMapId() &&
|
||||||
bot->GetMap() &&
|
bot->GetMap() &&
|
||||||
(bot->GetMap()->IsRaid() || bot->GetMap()->IsDungeon()))
|
(bot->GetMap()->IsRaid() || bot->GetMap()->IsDungeon()))
|
||||||
{
|
{
|
||||||
@@ -184,7 +184,7 @@ bool AutoReleaseSpiritAction::ShouldAutoRelease() const
|
|||||||
}
|
}
|
||||||
|
|
||||||
return sServerFacade->IsDistanceGreaterThan(
|
return sServerFacade->IsDistanceGreaterThan(
|
||||||
AI_VALUE2(float, "distance", "master target"),
|
AI_VALUE2(float, "distance", "group leader"),
|
||||||
sPlayerbotAIConfig->sightDistance);
|
sPlayerbotAIConfig->sightDistance);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,4 +16,4 @@ bool ResetInstancesAction::Execute(Event event)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ResetInstancesAction::isUseful() { return botAI->GetGroupMaster() == bot; };
|
bool ResetInstancesAction::isUseful() { return botAI->GetGroupLeader() == bot; };
|
||||||
|
|||||||
@@ -17,14 +17,14 @@
|
|||||||
|
|
||||||
bool ReviveFromCorpseAction::Execute(Event event)
|
bool ReviveFromCorpseAction::Execute(Event event)
|
||||||
{
|
{
|
||||||
Player* master = botAI->GetGroupMaster();
|
Player* groupLeader = botAI->GetGroupLeader();
|
||||||
Corpse* corpse = bot->GetCorpse();
|
Corpse* corpse = bot->GetCorpse();
|
||||||
|
|
||||||
// follow master when master revives
|
// follow group Leader when group Leader revives
|
||||||
WorldPacket& p = event.getPacket();
|
WorldPacket& p = event.getPacket();
|
||||||
if (!p.empty() && p.GetOpcode() == CMSG_RECLAIM_CORPSE && master && !corpse && bot->IsAlive())
|
if (!p.empty() && p.GetOpcode() == CMSG_RECLAIM_CORPSE && groupLeader && !corpse && bot->IsAlive())
|
||||||
{
|
{
|
||||||
if (sServerFacade->IsDistanceLessThan(AI_VALUE2(float, "distance", "master target"),
|
if (sServerFacade->IsDistanceLessThan(AI_VALUE2(float, "distance", "group leader"),
|
||||||
sPlayerbotAIConfig->farDistance))
|
sPlayerbotAIConfig->farDistance))
|
||||||
{
|
{
|
||||||
if (!botAI->HasStrategy("follow", BOT_STATE_NON_COMBAT))
|
if (!botAI->HasStrategy("follow", BOT_STATE_NON_COMBAT))
|
||||||
@@ -43,10 +43,10 @@ bool ReviveFromCorpseAction::Execute(Event event)
|
|||||||
// time(nullptr))
|
// time(nullptr))
|
||||||
// return false;
|
// return false;
|
||||||
|
|
||||||
if (master)
|
if (groupLeader)
|
||||||
{
|
{
|
||||||
if (!GET_PLAYERBOT_AI(master) && master->isDead() && master->GetCorpse() &&
|
if (!GET_PLAYERBOT_AI(groupLeader) && groupLeader->isDead() && groupLeader->GetCorpse() &&
|
||||||
sServerFacade->IsDistanceLessThan(AI_VALUE2(float, "distance", "master target"),
|
sServerFacade->IsDistanceLessThan(AI_VALUE2(float, "distance", "group leader"),
|
||||||
sPlayerbotAIConfig->farDistance))
|
sPlayerbotAIConfig->farDistance))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -79,15 +79,15 @@ bool FindCorpseAction::Execute(Event event)
|
|||||||
if (bot->InBattleground())
|
if (bot->InBattleground())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
Player* master = botAI->GetGroupMaster();
|
Player* groupLeader = botAI->GetGroupLeader();
|
||||||
Corpse* corpse = bot->GetCorpse();
|
Corpse* corpse = bot->GetCorpse();
|
||||||
if (!corpse)
|
if (!corpse)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// if (master)
|
// if (groupLeader)
|
||||||
// {
|
// {
|
||||||
// if (!GET_PLAYERBOT_AI(master) &&
|
// if (!GET_PLAYERBOT_AI(groupLeader) &&
|
||||||
// sServerFacade->IsDistanceLessThan(AI_VALUE2(float, "distance", "master target"),
|
// sServerFacade->IsDistanceLessThan(AI_VALUE2(float, "distance", "group leader"),
|
||||||
// sPlayerbotAIConfig->farDistance)) return false;
|
// sPlayerbotAIConfig->farDistance)) return false;
|
||||||
// }
|
// }
|
||||||
|
|
||||||
@@ -110,20 +110,20 @@ bool FindCorpseAction::Execute(Event event)
|
|||||||
WorldPosition botPos(bot);
|
WorldPosition botPos(bot);
|
||||||
WorldPosition corpsePos(corpse);
|
WorldPosition corpsePos(corpse);
|
||||||
WorldPosition moveToPos = corpsePos;
|
WorldPosition moveToPos = corpsePos;
|
||||||
WorldPosition masterPos(master);
|
WorldPosition leaderPos(groupLeader);
|
||||||
|
|
||||||
float reclaimDist = CORPSE_RECLAIM_RADIUS - 5.0f;
|
float reclaimDist = CORPSE_RECLAIM_RADIUS - 5.0f;
|
||||||
float corpseDist = botPos.distance(corpsePos);
|
float corpseDist = botPos.distance(corpsePos);
|
||||||
int64 deadTime = time(nullptr) - corpse->GetGhostTime();
|
int64 deadTime = time(nullptr) - corpse->GetGhostTime();
|
||||||
|
|
||||||
bool moveToMaster = master && master != bot && masterPos.fDist(corpsePos) < reclaimDist;
|
bool moveToLeader = groupLeader && groupLeader != bot && leaderPos.fDist(corpsePos) < reclaimDist;
|
||||||
|
|
||||||
// Should we ressurect? If so, return false.
|
// Should we ressurect? If so, return false.
|
||||||
if (corpseDist < reclaimDist)
|
if (corpseDist < reclaimDist)
|
||||||
{
|
{
|
||||||
if (moveToMaster) // We are near master.
|
if (moveToLeader) // We are near group leader.
|
||||||
{
|
{
|
||||||
if (botPos.fDist(masterPos) < sPlayerbotAIConfig->spellDistance)
|
if (botPos.fDist(leaderPos) < sPlayerbotAIConfig->spellDistance)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
else if (deadTime > 8 * MINUTE) // We have walked too long already.
|
else if (deadTime > 8 * MINUTE) // We have walked too long already.
|
||||||
@@ -140,8 +140,8 @@ bool FindCorpseAction::Execute(Event event)
|
|||||||
// If we are getting close move to a save ressurrection spot instead of just the corpse.
|
// If we are getting close move to a save ressurrection spot instead of just the corpse.
|
||||||
if (corpseDist < sPlayerbotAIConfig->reactDistance)
|
if (corpseDist < sPlayerbotAIConfig->reactDistance)
|
||||||
{
|
{
|
||||||
if (moveToMaster)
|
if (moveToLeader)
|
||||||
moveToPos = masterPos;
|
moveToPos = leaderPos;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
FleeManager manager(bot, reclaimDist, 0.0, urand(0, 1), moveToPos);
|
FleeManager manager(bot, reclaimDist, 0.0, urand(0, 1), moveToPos);
|
||||||
@@ -215,12 +215,12 @@ GraveyardStruct const* SpiritHealerAction::GetGrave(bool startZone)
|
|||||||
if (!startZone && ClosestGrave)
|
if (!startZone && ClosestGrave)
|
||||||
return ClosestGrave;
|
return ClosestGrave;
|
||||||
|
|
||||||
if (botAI->HasStrategy("follow", BOT_STATE_NON_COMBAT) && botAI->GetGroupMaster() && botAI->GetGroupMaster() != bot)
|
if (botAI->HasStrategy("follow", BOT_STATE_NON_COMBAT) && botAI->GetGroupLeader() && botAI->GetGroupLeader() != bot)
|
||||||
{
|
{
|
||||||
Player* master = botAI->GetGroupMaster();
|
Player* groupLeader = botAI->GetGroupLeader();
|
||||||
if (master && master != bot)
|
if (groupLeader && groupLeader != bot)
|
||||||
{
|
{
|
||||||
ClosestGrave = sGraveyard->GetClosestGraveyard(master, bot->GetTeamId());
|
ClosestGrave = sGraveyard->GetClosestGraveyard(groupLeader, bot->GetTeamId());
|
||||||
|
|
||||||
if (ClosestGrave)
|
if (ClosestGrave)
|
||||||
return ClosestGrave;
|
return ClosestGrave;
|
||||||
|
|||||||
@@ -35,8 +35,8 @@ bool RewardAction::Execute(Event event)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Unit* mtar = AI_VALUE(Unit*, "master target");
|
Unit* groupLeaderUnit = AI_VALUE(Unit*, "group leader");
|
||||||
if (mtar && Reward(itemId, mtar))
|
if (groupLeaderUnit && Reward(itemId, groupLeaderUnit))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
botAI->TellError("Cannot talk to quest giver");
|
botAI->TellError("Cannot talk to quest giver");
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ void RpgHelper::setFacing(GuidPosition guidPosition)
|
|||||||
|
|
||||||
void RpgHelper::setDelay(bool waitForGroup)
|
void RpgHelper::setDelay(bool waitForGroup)
|
||||||
{
|
{
|
||||||
if (!botAI->HasRealPlayerMaster() || (waitForGroup && botAI->GetGroupMaster() == bot && bot->GetGroup()))
|
if (!botAI->HasRealPlayerMaster() || (waitForGroup && botAI->GetGroupLeader() == bot && bot->GetGroup()))
|
||||||
botAI->SetNextCheckDelay(sPlayerbotAIConfig->rpgDelay);
|
botAI->SetNextCheckDelay(sPlayerbotAIConfig->rpgDelay);
|
||||||
else
|
else
|
||||||
botAI->SetNextCheckDelay(sPlayerbotAIConfig->rpgDelay / 5);
|
botAI->SetNextCheckDelay(sPlayerbotAIConfig->rpgDelay / 5);
|
||||||
|
|||||||
@@ -22,8 +22,8 @@ bool SecurityCheckAction::Execute(Event event)
|
|||||||
ItemQualities threshold = group->GetLootThreshold();
|
ItemQualities threshold = group->GetLootThreshold();
|
||||||
if (method == MASTER_LOOT || method == FREE_FOR_ALL || threshold > ITEM_QUALITY_UNCOMMON)
|
if (method == MASTER_LOOT || method == FREE_FOR_ALL || threshold > ITEM_QUALITY_UNCOMMON)
|
||||||
{
|
{
|
||||||
if ((botAI->GetGroupMaster()->GetSession()->GetSecurity() == SEC_PLAYER) &&
|
if ((botAI->GetGroupLeader()->GetSession()->GetSecurity() == SEC_PLAYER) &&
|
||||||
(!bot->GetGuildId() || bot->GetGuildId() != botAI->GetGroupMaster()->GetGuildId()))
|
(!bot->GetGuildId() || bot->GetGuildId() != botAI->GetGroupLeader()->GetGuildId()))
|
||||||
{
|
{
|
||||||
botAI->TellError("I will play with this loot type only if I'm in your guild :/");
|
botAI->TellError("I will play with this loot type only if I'm in your guild :/");
|
||||||
botAI->ChangeStrategy("+passive,+stay", BOT_STATE_NON_COMBAT);
|
botAI->ChangeStrategy("+passive,+stay", BOT_STATE_NON_COMBAT);
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ bool OutOfReactRangeAction::Execute(Event event)
|
|||||||
|
|
||||||
bool OutOfReactRangeAction::isUseful()
|
bool OutOfReactRangeAction::isUseful()
|
||||||
{
|
{
|
||||||
bool canFollow = Follow(AI_VALUE(Unit*, "master target"));
|
bool canFollow = Follow(AI_VALUE(Unit*, "group leader"));
|
||||||
if (!canFollow)
|
if (!canFollow)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ bool MoveToDarkPortalAction::Execute(Event event)
|
|||||||
{
|
{
|
||||||
if (bot->GetGroup())
|
if (bot->GetGroup())
|
||||||
if (bot->GetGroup()->GetLeaderGUID() != bot->GetGUID() &&
|
if (bot->GetGroup()->GetLeaderGUID() != bot->GetGUID() &&
|
||||||
!GET_PLAYERBOT_AI(GET_PLAYERBOT_AI(bot)->GetGroupMaster()))
|
!GET_PLAYERBOT_AI(GET_PLAYERBOT_AI(bot)->GetGroupLeader()))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (bot->GetLevel() > 57)
|
if (bot->GetLevel() > 57)
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ void GroupStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
|||||||
{
|
{
|
||||||
triggers.push_back(new TriggerNode("often", NextAction::array(0, new NextAction("invite nearby", 4.0f), nullptr)));
|
triggers.push_back(new TriggerNode("often", NextAction::array(0, new NextAction("invite nearby", 4.0f), nullptr)));
|
||||||
triggers.push_back(new TriggerNode("random", NextAction::array(0, new NextAction("invite guild", 4.0f), nullptr)));
|
triggers.push_back(new TriggerNode("random", NextAction::array(0, new NextAction("invite guild", 4.0f), nullptr)));
|
||||||
triggers.push_back(new TriggerNode("often", NextAction::array(0, new NextAction("leave far away", 4.0f), nullptr)));
|
triggers.push_back(new TriggerNode("random", NextAction::array(0, new NextAction("leave far away", 4.0f), nullptr)));
|
||||||
triggers.push_back(
|
triggers.push_back(new TriggerNode("seldom", NextAction::array(0, new NextAction("reset instances", 1.0f), nullptr)));
|
||||||
new TriggerNode("seldom", NextAction::array(0, new NextAction("reset instances", 1.0f), nullptr)));
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,8 +5,6 @@
|
|||||||
|
|
||||||
#include "WorldPacketHandlerStrategy.h"
|
#include "WorldPacketHandlerStrategy.h"
|
||||||
|
|
||||||
#include "Playerbots.h"
|
|
||||||
|
|
||||||
void WorldPacketHandlerStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
void WorldPacketHandlerStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||||
{
|
{
|
||||||
PassTroughStrategy::InitTriggers(triggers);
|
PassTroughStrategy::InitTriggers(triggers);
|
||||||
@@ -69,7 +67,7 @@ void WorldPacketHandlerStrategy::InitTriggers(std::vector<TriggerNode*>& trigger
|
|||||||
triggers.push_back(new TriggerNode("questgiver quest details", NextAction::array(0, new NextAction("turn in query quest", relevance), nullptr)));
|
triggers.push_back(new TriggerNode("questgiver quest details", NextAction::array(0, new NextAction("turn in query quest", relevance), nullptr)));
|
||||||
|
|
||||||
// loot roll
|
// loot roll
|
||||||
triggers.push_back(new TriggerNode("very often", NextAction::array(0, new NextAction("loot roll", 10.0f), nullptr)));
|
triggers.push_back(new TriggerNode("very often", NextAction::array(0, new NextAction("loot roll", relevance), nullptr)));
|
||||||
}
|
}
|
||||||
|
|
||||||
WorldPacketHandlerStrategy::WorldPacketHandlerStrategy(PlayerbotAI* botAI) : PassTroughStrategy(botAI)
|
WorldPacketHandlerStrategy::WorldPacketHandlerStrategy(PlayerbotAI* botAI) : PassTroughStrategy(botAI)
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ public:
|
|||||||
RaidStrategyContext() : NamedObjectContext<Strategy>(false, true)
|
RaidStrategyContext() : NamedObjectContext<Strategy>(false, true)
|
||||||
{
|
{
|
||||||
creators["aq20"] = &RaidStrategyContext::aq20;
|
creators["aq20"] = &RaidStrategyContext::aq20;
|
||||||
creators["mc"] = &RaidStrategyContext::mc;
|
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["magtheridon"] = &RaidStrategyContext::magtheridon;
|
||||||
@@ -38,7 +38,7 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
static Strategy* aq20(PlayerbotAI* botAI) { return new RaidAq20Strategy(botAI); }
|
static Strategy* aq20(PlayerbotAI* botAI) { return new RaidAq20Strategy(botAI); }
|
||||||
static Strategy* mc(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* magtheridon(PlayerbotAI* botAI) { return new RaidMagtheridonStrategy(botAI); }
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#ifndef _PLAYERBOT_RAIDKARAZHANACTIONS_CONTEXT_H
|
#ifndef _PLAYERBOT_RAIDKARAZHANACTIONCONTEXT_H
|
||||||
#define _PLAYERBOT_RAIDKARAZHANACTIONS_CONTEXT_H
|
#define _PLAYERBOT_RAIDKARAZHANACTIONCONTEXT_H
|
||||||
|
|
||||||
#include "RaidKarazhanActions.h"
|
#include "RaidKarazhanActions.h"
|
||||||
#include "NamedObjectContext.h"
|
#include "NamedObjectContext.h"
|
||||||
@@ -9,77 +9,255 @@ class RaidKarazhanActionContext : public NamedObjectContext<Action>
|
|||||||
public:
|
public:
|
||||||
RaidKarazhanActionContext()
|
RaidKarazhanActionContext()
|
||||||
{
|
{
|
||||||
creators["karazhan attumen the huntsman stack behind"] = &RaidKarazhanActionContext::karazhan_attumen_the_huntsman_stack_behind;
|
// Trash
|
||||||
|
creators["mana warp stun creature before warp breach"] =
|
||||||
|
&RaidKarazhanActionContext::mana_warp_stun_creature_before_warp_breach;
|
||||||
|
|
||||||
creators["karazhan moroes mark target"] = &RaidKarazhanActionContext::karazhan_moroes_mark_target;
|
// Attumen the Huntsman
|
||||||
|
creators["attumen the huntsman mark target"] =
|
||||||
|
&RaidKarazhanActionContext::attumen_the_huntsman_mark_target;
|
||||||
|
|
||||||
creators["karazhan maiden of virtue position boss"] = &RaidKarazhanActionContext::karazhan_maiden_of_virtue_position_boss;
|
creators["attumen the huntsman split bosses"] =
|
||||||
creators["karazhan maiden of virtue position ranged"] = &RaidKarazhanActionContext::karazhan_maiden_of_virtue_position_ranged;
|
&RaidKarazhanActionContext::attumen_the_huntsman_split_bosses;
|
||||||
|
|
||||||
creators["karazhan big bad wolf position boss"] = &RaidKarazhanActionContext::karazhan_big_bad_wolf_position_boss;
|
creators["attumen the huntsman stack behind"] =
|
||||||
creators["karazhan big bad wolf run away"] = &RaidKarazhanActionContext::karazhan_big_bad_wolf_run_away;
|
&RaidKarazhanActionContext::attumen_the_huntsman_stack_behind;
|
||||||
|
|
||||||
creators["karazhan romulo and julianne mark target"] = &RaidKarazhanActionContext::karazhan_romulo_and_julianne_mark_target;
|
creators["attumen the huntsman manage dps timer"] =
|
||||||
|
&RaidKarazhanActionContext::attumen_the_huntsman_manage_dps_timer;
|
||||||
|
|
||||||
creators["karazhan wizard of oz mark target"] = &RaidKarazhanActionContext::karazhan_wizard_of_oz_mark_target;
|
// Moroes
|
||||||
creators["karazhan wizard of oz scorch strawman"] = &RaidKarazhanActionContext::karazhan_wizard_of_oz_scorch_strawman;
|
creators["moroes main tank attack boss"] =
|
||||||
|
&RaidKarazhanActionContext::moroes_main_tank_attack_boss;
|
||||||
|
|
||||||
creators["karazhan the curator mark target"] = &RaidKarazhanActionContext::karazhan_the_curator_mark_target;
|
creators["moroes mark target"] =
|
||||||
creators["karazhan the curator position boss"] = &RaidKarazhanActionContext::karazhan_the_curator_position_boss;
|
&RaidKarazhanActionContext::moroes_mark_target;
|
||||||
creators["karazhan the curator spread ranged"] = &RaidKarazhanActionContext::karazhan_the_curator_spread_ranged;
|
|
||||||
|
|
||||||
creators["karazhan terestian illhoof mark target"] = &RaidKarazhanActionContext::karazhan_terestian_illhoof_mark_target;
|
// Maiden of Virtue
|
||||||
|
creators["maiden of virtue move boss to healer"] =
|
||||||
|
&RaidKarazhanActionContext::maiden_of_virtue_move_boss_to_healer;
|
||||||
|
|
||||||
creators["karazhan shade of aran arcane explosion run away"] = &RaidKarazhanActionContext::karazhan_shade_of_aran_arcane_explosion_run_away;
|
creators["maiden of virtue position ranged"] =
|
||||||
creators["karazhan shade of aran flame wreath stop movement"] = &RaidKarazhanActionContext::karazhan_shade_of_aran_flame_wreath_stop_movement;
|
&RaidKarazhanActionContext::maiden_of_virtue_position_ranged;
|
||||||
creators["karazhan shade of aran mark conjured elemental"] = &RaidKarazhanActionContext::karazhan_shade_of_aran_mark_conjured_elemental;
|
|
||||||
creators["karazhan shade of aran spread ranged"] = &RaidKarazhanActionContext::karazhan_shade_of_aran_spread_ranged;
|
|
||||||
|
|
||||||
creators["karazhan netherspite block red beam"] = &RaidKarazhanActionContext::karazhan_netherspite_block_red_beam;
|
// The Big Bad Wolf
|
||||||
creators["karazhan netherspite block blue beam"] = &RaidKarazhanActionContext::karazhan_netherspite_block_blue_beam;
|
creators["big bad wolf position boss"] =
|
||||||
creators["karazhan netherspite block green beam"] = &RaidKarazhanActionContext::karazhan_netherspite_block_green_beam;
|
&RaidKarazhanActionContext::big_bad_wolf_position_boss;
|
||||||
creators["karazhan netherspite avoid beam and void zone"] = &RaidKarazhanActionContext::karazhan_netherspite_avoid_beam_and_void_zone;
|
|
||||||
creators["karazhan netherspite banish phase avoid void zone"] = &RaidKarazhanActionContext::karazhan_netherspite_banish_phase_avoid_void_zone;
|
|
||||||
|
|
||||||
creators["karazhan prince malchezaar non tank avoid hazard"] = &RaidKarazhanActionContext::karazhan_prince_malchezaar_non_tank_avoid_hazard;
|
creators["big bad wolf run away from boss"] =
|
||||||
creators["karazhan prince malchezaar tank avoid hazard"] = &RaidKarazhanActionContext::karazhan_prince_malchezaar_tank_avoid_hazard;
|
&RaidKarazhanActionContext::big_bad_wolf_run_away_from_boss;
|
||||||
|
|
||||||
|
// Romulo and Julianne
|
||||||
|
creators["romulo and julianne mark target"] =
|
||||||
|
&RaidKarazhanActionContext::romulo_and_julianne_mark_target;
|
||||||
|
|
||||||
|
// The Wizard of Oz
|
||||||
|
creators["wizard of oz mark target"] =
|
||||||
|
&RaidKarazhanActionContext::wizard_of_oz_mark_target;
|
||||||
|
|
||||||
|
creators["wizard of oz scorch strawman"] =
|
||||||
|
&RaidKarazhanActionContext::wizard_of_oz_scorch_strawman;
|
||||||
|
|
||||||
|
// The Curator
|
||||||
|
creators["the curator mark astral flare"] =
|
||||||
|
&RaidKarazhanActionContext::the_curator_mark_astral_flare;
|
||||||
|
|
||||||
|
creators["the curator position boss"] =
|
||||||
|
&RaidKarazhanActionContext::the_curator_position_boss;
|
||||||
|
|
||||||
|
creators["the curator spread ranged"] =
|
||||||
|
&RaidKarazhanActionContext::the_curator_spread_ranged;
|
||||||
|
|
||||||
|
// Terestian Illhoof
|
||||||
|
creators["terestian illhoof mark target"] =
|
||||||
|
&RaidKarazhanActionContext::terestian_illhoof_mark_target;
|
||||||
|
|
||||||
|
// Shade of Aran
|
||||||
|
creators["shade of aran run away from arcane explosion"] =
|
||||||
|
&RaidKarazhanActionContext::shade_of_aran_run_away_from_arcane_explosion;
|
||||||
|
|
||||||
|
creators["shade of aran stop moving during flame wreath"] =
|
||||||
|
&RaidKarazhanActionContext::shade_of_aran_stop_moving_during_flame_wreath;
|
||||||
|
|
||||||
|
creators["shade of aran mark conjured elemental"] =
|
||||||
|
&RaidKarazhanActionContext::shade_of_aran_mark_conjured_elemental;
|
||||||
|
|
||||||
|
creators["shade of aran ranged maintain distance"] =
|
||||||
|
&RaidKarazhanActionContext::shade_of_aran_ranged_maintain_distance;
|
||||||
|
|
||||||
|
// Netherspite
|
||||||
|
creators["netherspite block red beam"] =
|
||||||
|
&RaidKarazhanActionContext::netherspite_block_red_beam;
|
||||||
|
|
||||||
|
creators["netherspite block blue beam"] =
|
||||||
|
&RaidKarazhanActionContext::netherspite_block_blue_beam;
|
||||||
|
|
||||||
|
creators["netherspite block green beam"] =
|
||||||
|
&RaidKarazhanActionContext::netherspite_block_green_beam;
|
||||||
|
|
||||||
|
creators["netherspite avoid beam and void zone"] =
|
||||||
|
&RaidKarazhanActionContext::netherspite_avoid_beam_and_void_zone;
|
||||||
|
|
||||||
|
creators["netherspite banish phase avoid void zone"] =
|
||||||
|
&RaidKarazhanActionContext::netherspite_banish_phase_avoid_void_zone;
|
||||||
|
|
||||||
|
creators["netherspite manage timers and trackers"] =
|
||||||
|
&RaidKarazhanActionContext::netherspite_manage_timers_and_trackers;
|
||||||
|
|
||||||
|
// Prince Malchezaar
|
||||||
|
creators["prince malchezaar enfeebled avoid hazard"] =
|
||||||
|
&RaidKarazhanActionContext::prince_malchezaar_enfeebled_avoid_hazard;
|
||||||
|
|
||||||
|
creators["prince malchezaar non tank avoid infernal"] =
|
||||||
|
&RaidKarazhanActionContext::prince_malchezaar_non_tank_avoid_infernal;
|
||||||
|
|
||||||
|
creators["prince malchezaar main tank movement"] =
|
||||||
|
&RaidKarazhanActionContext::prince_malchezaar_main_tank_movement;
|
||||||
|
|
||||||
|
// Nightbane
|
||||||
|
creators["nightbane ground phase position boss"] =
|
||||||
|
&RaidKarazhanActionContext::nightbane_ground_phase_position_boss;
|
||||||
|
|
||||||
|
creators["nightbane ground phase rotate ranged positions"] =
|
||||||
|
&RaidKarazhanActionContext::nightbane_ground_phase_rotate_ranged_positions;
|
||||||
|
|
||||||
|
creators["nightbane cast fear ward on main tank"] =
|
||||||
|
&RaidKarazhanActionContext::nightbane_cast_fear_ward_on_main_tank;
|
||||||
|
|
||||||
|
creators["nightbane control pet aggression"] =
|
||||||
|
&RaidKarazhanActionContext::nightbane_control_pet_aggression;
|
||||||
|
|
||||||
|
creators["nightbane flight phase movement"] =
|
||||||
|
&RaidKarazhanActionContext::nightbane_flight_phase_movement;
|
||||||
|
|
||||||
|
creators["nightbane manage timers and trackers"] =
|
||||||
|
&RaidKarazhanActionContext::nightbane_manage_timers_and_trackers;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static Action* karazhan_attumen_the_huntsman_stack_behind(PlayerbotAI* botAI) { return new KarazhanAttumenTheHuntsmanStackBehindAction(botAI); }
|
// Trash
|
||||||
|
static Action* mana_warp_stun_creature_before_warp_breach(
|
||||||
|
PlayerbotAI* botAI) { return new ManaWarpStunCreatureBeforeWarpBreachAction(botAI); }
|
||||||
|
|
||||||
static Action* karazhan_moroes_mark_target(PlayerbotAI* botAI) { return new KarazhanMoroesMarkTargetAction(botAI); }
|
// Attumen the Huntsman
|
||||||
|
static Action* attumen_the_huntsman_mark_target(
|
||||||
|
PlayerbotAI* botAI) { return new AttumenTheHuntsmanMarkTargetAction(botAI); }
|
||||||
|
|
||||||
static Action* karazhan_maiden_of_virtue_position_boss(PlayerbotAI* botAI) { return new KarazhanMaidenOfVirtuePositionBossAction(botAI); }
|
static Action* attumen_the_huntsman_split_bosses(
|
||||||
static Action* karazhan_maiden_of_virtue_position_ranged(PlayerbotAI* botAI) { return new KarazhanMaidenOfVirtuePositionRangedAction(botAI); }
|
PlayerbotAI* botAI) { return new AttumenTheHuntsmanSplitBossesAction(botAI); }
|
||||||
|
|
||||||
static Action* karazhan_big_bad_wolf_position_boss(PlayerbotAI* botAI) { return new KarazhanBigBadWolfPositionBossAction(botAI); }
|
static Action* attumen_the_huntsman_stack_behind(
|
||||||
static Action* karazhan_big_bad_wolf_run_away(PlayerbotAI* botAI) { return new KarazhanBigBadWolfRunAwayAction(botAI); }
|
PlayerbotAI* botAI) { return new AttumenTheHuntsmanStackBehindAction(botAI); }
|
||||||
|
|
||||||
static Action* karazhan_romulo_and_julianne_mark_target(PlayerbotAI* botAI) { return new KarazhanRomuloAndJulianneMarkTargetAction(botAI); }
|
static Action* attumen_the_huntsman_manage_dps_timer(
|
||||||
|
PlayerbotAI* botAI) { return new AttumenTheHuntsmanManageDpsTimerAction(botAI); }
|
||||||
|
|
||||||
static Action* karazhan_wizard_of_oz_mark_target(PlayerbotAI* botAI) { return new KarazhanWizardOfOzMarkTargetAction(botAI); }
|
// Moroes
|
||||||
static Action* karazhan_wizard_of_oz_scorch_strawman(PlayerbotAI* botAI) { return new KarazhanWizardOfOzScorchStrawmanAction(botAI); }
|
static Action* moroes_main_tank_attack_boss(
|
||||||
|
PlayerbotAI* botAI) { return new MoroesMainTankAttackBossAction(botAI); }
|
||||||
|
|
||||||
static Action* karazhan_the_curator_mark_target(PlayerbotAI* botAI) { return new KarazhanTheCuratorMarkTargetAction(botAI); }
|
static Action* moroes_mark_target(
|
||||||
static Action* karazhan_the_curator_position_boss(PlayerbotAI* botAI) { return new KarazhanTheCuratorPositionBossAction(botAI); }
|
PlayerbotAI* botAI) { return new MoroesMarkTargetAction(botAI); }
|
||||||
static Action* karazhan_the_curator_spread_ranged(PlayerbotAI* botAI) { return new KarazhanTheCuratorSpreadRangedAction(botAI); }
|
|
||||||
|
|
||||||
static Action* karazhan_terestian_illhoof_mark_target(PlayerbotAI* botAI) { return new KarazhanTerestianIllhoofMarkTargetAction(botAI); }
|
// Maiden of Virtue
|
||||||
|
static Action* maiden_of_virtue_move_boss_to_healer(
|
||||||
|
PlayerbotAI* botAI) { return new MaidenOfVirtueMoveBossToHealerAction(botAI); }
|
||||||
|
|
||||||
static Action* karazhan_shade_of_aran_arcane_explosion_run_away(PlayerbotAI* botAI) { return new KarazhanShadeOfAranArcaneExplosionRunAwayAction(botAI); }
|
static Action* maiden_of_virtue_position_ranged(
|
||||||
static Action* karazhan_shade_of_aran_flame_wreath_stop_movement(PlayerbotAI* botAI) { return new KarazhanShadeOfAranFlameWreathStopMovementAction(botAI); }
|
PlayerbotAI* botAI) { return new MaidenOfVirtuePositionRangedAction(botAI); }
|
||||||
static Action* karazhan_shade_of_aran_mark_conjured_elemental(PlayerbotAI* botAI) { return new KarazhanShadeOfAranMarkConjuredElementalAction(botAI); }
|
|
||||||
static Action* karazhan_shade_of_aran_spread_ranged(PlayerbotAI* botAI) { return new KarazhanShadeOfAranSpreadRangedAction(botAI); }
|
|
||||||
|
|
||||||
static Action* karazhan_netherspite_block_red_beam(PlayerbotAI* botAI) { return new KarazhanNetherspiteBlockRedBeamAction(botAI); }
|
// The Big Bad Wolf
|
||||||
static Action* karazhan_netherspite_block_blue_beam(PlayerbotAI* botAI) { return new KarazhanNetherspiteBlockBlueBeamAction(botAI); }
|
static Action* big_bad_wolf_position_boss(
|
||||||
static Action* karazhan_netherspite_block_green_beam(PlayerbotAI* botAI) { return new KarazhanNetherspiteBlockGreenBeamAction(botAI); }
|
PlayerbotAI* botAI) { return new BigBadWolfPositionBossAction(botAI); }
|
||||||
static Action* karazhan_netherspite_avoid_beam_and_void_zone(PlayerbotAI* botAI) { return new KarazhanNetherspiteAvoidBeamAndVoidZoneAction(botAI); }
|
|
||||||
static Action* karazhan_netherspite_banish_phase_avoid_void_zone(PlayerbotAI* botAI) { return new KarazhanNetherspiteBanishPhaseAvoidVoidZoneAction(botAI); }
|
|
||||||
|
|
||||||
static Action* karazhan_prince_malchezaar_non_tank_avoid_hazard(PlayerbotAI* botAI) { return new KarazhanPrinceMalchezaarNonTankAvoidHazardAction(botAI); }
|
static Action* big_bad_wolf_run_away_from_boss(
|
||||||
static Action* karazhan_prince_malchezaar_tank_avoid_hazard(PlayerbotAI* botAI) { return new KarazhanPrinceMalchezaarTankAvoidHazardAction(botAI); }
|
PlayerbotAI* botAI) { return new BigBadWolfRunAwayFromBossAction(botAI); }
|
||||||
|
|
||||||
|
// Romulo and Julianne
|
||||||
|
static Action* romulo_and_julianne_mark_target(
|
||||||
|
PlayerbotAI* botAI) { return new RomuloAndJulianneMarkTargetAction(botAI); }
|
||||||
|
|
||||||
|
// The Wizard of Oz
|
||||||
|
static Action* wizard_of_oz_mark_target(
|
||||||
|
PlayerbotAI* botAI) { return new WizardOfOzMarkTargetAction(botAI); }
|
||||||
|
|
||||||
|
static Action* wizard_of_oz_scorch_strawman(
|
||||||
|
PlayerbotAI* botAI) { return new WizardOfOzScorchStrawmanAction(botAI); }
|
||||||
|
|
||||||
|
// The Curator
|
||||||
|
static Action* the_curator_mark_astral_flare(
|
||||||
|
PlayerbotAI* botAI) { return new TheCuratorMarkAstralFlareAction(botAI); }
|
||||||
|
|
||||||
|
static Action* the_curator_position_boss(
|
||||||
|
PlayerbotAI* botAI) { return new TheCuratorPositionBossAction(botAI); }
|
||||||
|
|
||||||
|
static Action* the_curator_spread_ranged(
|
||||||
|
PlayerbotAI* botAI) { return new TheCuratorSpreadRangedAction(botAI); }
|
||||||
|
|
||||||
|
// Terestian Illhoof
|
||||||
|
static Action* terestian_illhoof_mark_target(
|
||||||
|
PlayerbotAI* botAI) { return new TerestianIllhoofMarkTargetAction(botAI); }
|
||||||
|
|
||||||
|
// Shade of Aran
|
||||||
|
static Action* shade_of_aran_run_away_from_arcane_explosion(
|
||||||
|
PlayerbotAI* botAI) { return new ShadeOfAranRunAwayFromArcaneExplosionAction(botAI); }
|
||||||
|
|
||||||
|
static Action* shade_of_aran_stop_moving_during_flame_wreath(
|
||||||
|
PlayerbotAI* botAI) { return new ShadeOfAranStopMovingDuringFlameWreathAction(botAI); }
|
||||||
|
|
||||||
|
static Action* shade_of_aran_mark_conjured_elemental(
|
||||||
|
PlayerbotAI* botAI) { return new ShadeOfAranMarkConjuredElementalAction(botAI); }
|
||||||
|
|
||||||
|
static Action* shade_of_aran_ranged_maintain_distance(
|
||||||
|
PlayerbotAI* botAI) { return new ShadeOfAranRangedMaintainDistanceAction(botAI); }
|
||||||
|
|
||||||
|
// Netherspite
|
||||||
|
static Action* netherspite_block_red_beam(
|
||||||
|
PlayerbotAI* botAI) { return new NetherspiteBlockRedBeamAction(botAI); }
|
||||||
|
|
||||||
|
static Action* netherspite_block_blue_beam(
|
||||||
|
PlayerbotAI* botAI) { return new NetherspiteBlockBlueBeamAction(botAI); }
|
||||||
|
|
||||||
|
static Action* netherspite_block_green_beam(
|
||||||
|
PlayerbotAI* botAI) { return new NetherspiteBlockGreenBeamAction(botAI); }
|
||||||
|
|
||||||
|
static Action* netherspite_avoid_beam_and_void_zone(
|
||||||
|
PlayerbotAI* botAI) { return new NetherspiteAvoidBeamAndVoidZoneAction(botAI); }
|
||||||
|
|
||||||
|
static Action* netherspite_banish_phase_avoid_void_zone(
|
||||||
|
PlayerbotAI* botAI) { return new NetherspiteBanishPhaseAvoidVoidZoneAction(botAI); }
|
||||||
|
|
||||||
|
static Action* netherspite_manage_timers_and_trackers(
|
||||||
|
PlayerbotAI* botAI) { return new NetherspiteManageTimersAndTrackersAction(botAI); }
|
||||||
|
|
||||||
|
// Prince Malchezaar
|
||||||
|
static Action* prince_malchezaar_enfeebled_avoid_hazard(
|
||||||
|
PlayerbotAI* botAI) { return new PrinceMalchezaarEnfeebledAvoidHazardAction(botAI); }
|
||||||
|
|
||||||
|
static Action* prince_malchezaar_non_tank_avoid_infernal(
|
||||||
|
PlayerbotAI* botAI) { return new PrinceMalchezaarNonTankAvoidInfernalAction(botAI); }
|
||||||
|
|
||||||
|
static Action* prince_malchezaar_main_tank_movement(
|
||||||
|
PlayerbotAI* botAI) { return new PrinceMalchezaarMainTankMovementAction(botAI); }
|
||||||
|
|
||||||
|
// Nightbane
|
||||||
|
static Action* nightbane_ground_phase_position_boss(
|
||||||
|
PlayerbotAI* botAI) { return new NightbaneGroundPhasePositionBossAction(botAI); }
|
||||||
|
|
||||||
|
static Action* nightbane_ground_phase_rotate_ranged_positions(
|
||||||
|
PlayerbotAI* botAI) { return new NightbaneGroundPhaseRotateRangedPositionsAction(botAI); }
|
||||||
|
|
||||||
|
static Action* nightbane_cast_fear_ward_on_main_tank(
|
||||||
|
PlayerbotAI* botAI) { return new NightbaneCastFearWardOnMainTankAction(botAI); }
|
||||||
|
|
||||||
|
static Action* nightbane_control_pet_aggression(
|
||||||
|
PlayerbotAI* botAI) { return new NightbaneControlPetAggressionAction(botAI); }
|
||||||
|
|
||||||
|
static Action* nightbane_flight_phase_movement(
|
||||||
|
PlayerbotAI* botAI) { return new NightbaneFlightPhaseMovementAction(botAI); }
|
||||||
|
|
||||||
|
static Action* nightbane_manage_timers_and_trackers(
|
||||||
|
PlayerbotAI* botAI) { return new NightbaneManageTimersAndTrackersAction(botAI); }
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -2,217 +2,321 @@
|
|||||||
#define _PLAYERBOT_RAIDKARAZHANACTIONS_H
|
#define _PLAYERBOT_RAIDKARAZHANACTIONS_H
|
||||||
|
|
||||||
#include "Action.h"
|
#include "Action.h"
|
||||||
|
#include "AttackAction.h"
|
||||||
#include "MovementActions.h"
|
#include "MovementActions.h"
|
||||||
|
|
||||||
class KarazhanAttumenTheHuntsmanStackBehindAction : public MovementAction
|
class ManaWarpStunCreatureBeforeWarpBreachAction : public AttackAction
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
KarazhanAttumenTheHuntsmanStackBehindAction(PlayerbotAI* botAI, std::string const name = "karazhan attumen the huntsman stack behind") : MovementAction(botAI, name) {}
|
ManaWarpStunCreatureBeforeWarpBreachAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "mana warp stun creature before warp breach") : AttackAction(botAI, name) {}
|
||||||
bool Execute(Event event) override;
|
|
||||||
bool isUseful() override;
|
|
||||||
};
|
|
||||||
|
|
||||||
class KarazhanMoroesMarkTargetAction : public Action
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
KarazhanMoroesMarkTargetAction(PlayerbotAI* botAI, std::string const name = "karazhan moroes mark target") : Action(botAI, name) {}
|
|
||||||
|
|
||||||
bool Execute(Event event) override;
|
bool Execute(Event event) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class KarazhanMaidenOfVirtuePositionBossAction : public MovementAction
|
class AttumenTheHuntsmanMarkTargetAction : public AttackAction
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
KarazhanMaidenOfVirtuePositionBossAction(PlayerbotAI* botAI, std::string const name = "karazhan maiden of virtue position boss") : MovementAction(botAI, name) {}
|
AttumenTheHuntsmanMarkTargetAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "attumen the huntsman mark target") : AttackAction(botAI, name) {}
|
||||||
bool Execute(Event event) override;
|
bool Execute(Event event) override;
|
||||||
bool isUseful() override;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class KarazhanMaidenOfVirtuePositionRangedAction : public MovementAction
|
class AttumenTheHuntsmanSplitBossesAction : public AttackAction
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
KarazhanMaidenOfVirtuePositionRangedAction(PlayerbotAI* botAI, std::string const name = "karazhan maiden of virtue position ranged") : MovementAction(botAI, name) {}
|
AttumenTheHuntsmanSplitBossesAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "attumen the huntsman split bosses") : AttackAction(botAI, name) {}
|
||||||
bool Execute(Event event) override;
|
bool Execute(Event event) override;
|
||||||
bool isUseful() override;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class KarazhanBigBadWolfPositionBossAction : public MovementAction
|
class AttumenTheHuntsmanStackBehindAction : public MovementAction
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
KarazhanBigBadWolfPositionBossAction(PlayerbotAI* botAI, std::string const name = "karazhan big bad wolf position boss") : MovementAction(botAI, name) {}
|
AttumenTheHuntsmanStackBehindAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "attumen the huntsman stack behind") : MovementAction(botAI, name) {}
|
||||||
bool Execute(Event event) override;
|
bool Execute(Event event) override;
|
||||||
bool isUseful() override;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class KarazhanBigBadWolfRunAwayAction : public MovementAction
|
class AttumenTheHuntsmanManageDpsTimerAction : public Action
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
KarazhanBigBadWolfRunAwayAction(PlayerbotAI* botAI, std::string const name = "karazhan big bad wolf run away") : MovementAction(botAI, name) {}
|
AttumenTheHuntsmanManageDpsTimerAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "attumen the huntsman manage dps timer") : Action(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class MoroesMainTankAttackBossAction : public AttackAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
MoroesMainTankAttackBossAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "moroes main tank attack boss") : AttackAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class MoroesMarkTargetAction : public Action
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
MoroesMarkTargetAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "moroes mark target") : Action(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class MaidenOfVirtueMoveBossToHealerAction : public AttackAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
MaidenOfVirtueMoveBossToHealerAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "maiden of virtue move boss to healer") : AttackAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class MaidenOfVirtuePositionRangedAction : public MovementAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
MaidenOfVirtuePositionRangedAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "maiden of virtue position ranged") : MovementAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class BigBadWolfPositionBossAction : public AttackAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
BigBadWolfPositionBossAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "big bad wolf position boss") : AttackAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class BigBadWolfRunAwayFromBossAction : public MovementAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
BigBadWolfRunAwayFromBossAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "big bad wolf run away from boss") : MovementAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class RomuloAndJulianneMarkTargetAction : public Action
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
RomuloAndJulianneMarkTargetAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "romulo and julianne mark target") : Action(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class WizardOfOzMarkTargetAction : public Action
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
WizardOfOzMarkTargetAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "wizard of oz mark target") : Action(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class WizardOfOzScorchStrawmanAction : public Action
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
WizardOfOzScorchStrawmanAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "wizard of oz scorch strawman") : Action(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class TheCuratorMarkAstralFlareAction : public Action
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TheCuratorMarkAstralFlareAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "the curator mark astral flare") : Action(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class TheCuratorPositionBossAction : public AttackAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TheCuratorPositionBossAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "the curator position boss") : AttackAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class TheCuratorSpreadRangedAction : public MovementAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TheCuratorSpreadRangedAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "the curator spread ranged") : MovementAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class TerestianIllhoofMarkTargetAction : public Action
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TerestianIllhoofMarkTargetAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "terestian illhoof mark target") : Action(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ShadeOfAranRunAwayFromArcaneExplosionAction : public MovementAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ShadeOfAranRunAwayFromArcaneExplosionAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "shade of aran run away from arcane explosion") : MovementAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ShadeOfAranStopMovingDuringFlameWreathAction : public MovementAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ShadeOfAranStopMovingDuringFlameWreathAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "shade of aran stop moving during flame wreath") : MovementAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ShadeOfAranMarkConjuredElementalAction : public Action
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ShadeOfAranMarkConjuredElementalAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "shade of aran mark conjured elemental") : Action(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ShadeOfAranRangedMaintainDistanceAction : public MovementAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ShadeOfAranRangedMaintainDistanceAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "shade of aran ranged maintain distance") : MovementAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class NetherspiteBlockRedBeamAction : public MovementAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
NetherspiteBlockRedBeamAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "netherspite block red beam") : MovementAction(botAI, name) {}
|
||||||
bool Execute(Event event) override;
|
bool Execute(Event event) override;
|
||||||
bool isUseful() override;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
size_t currentIndex = 0;
|
Position GetPositionOnBeam(Unit* netherspite, Unit* portal, float distanceFromBoss);
|
||||||
|
std::unordered_map<ObjectGuid, bool> _wasBlockingRedBeam;
|
||||||
};
|
};
|
||||||
|
|
||||||
class KarazhanRomuloAndJulianneMarkTargetAction : public Action
|
class NetherspiteBlockBlueBeamAction : public MovementAction
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
KarazhanRomuloAndJulianneMarkTargetAction(PlayerbotAI* botAI, std::string const name = "karazhan romulo and julianne mark target") : Action(botAI, name) {}
|
NetherspiteBlockBlueBeamAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "netherspite block blue beam") : MovementAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::unordered_map<ObjectGuid, bool> _wasBlockingBlueBeam;
|
||||||
|
};
|
||||||
|
|
||||||
|
class NetherspiteBlockGreenBeamAction : public MovementAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
NetherspiteBlockGreenBeamAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "netherspite block green beam") : MovementAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::unordered_map<ObjectGuid, bool> _wasBlockingGreenBeam;
|
||||||
|
};
|
||||||
|
|
||||||
|
class NetherspiteAvoidBeamAndVoidZoneAction : public MovementAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
NetherspiteAvoidBeamAndVoidZoneAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "netherspite avoid beam and void zone") : MovementAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct BeamAvoid
|
||||||
|
{
|
||||||
|
Unit* portal;
|
||||||
|
float minDist, maxDist;
|
||||||
|
};
|
||||||
|
bool IsAwayFromBeams(float x, float y, const std::vector<BeamAvoid>& beams, Unit* netherspite);
|
||||||
|
};
|
||||||
|
|
||||||
|
class NetherspiteBanishPhaseAvoidVoidZoneAction : public MovementAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
NetherspiteBanishPhaseAvoidVoidZoneAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "netherspite banish phase avoid void zone") : MovementAction(botAI, name) {}
|
||||||
bool Execute(Event event) override;
|
bool Execute(Event event) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class KarazhanWizardOfOzMarkTargetAction : public Action
|
class NetherspiteManageTimersAndTrackersAction : public Action
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
KarazhanWizardOfOzMarkTargetAction(PlayerbotAI* botAI, std::string const name = "karazhan wizard of oz mark target") : Action(botAI, name) {}
|
NetherspiteManageTimersAndTrackersAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "netherspite manage timers and trackers") : Action(botAI, name) {}
|
||||||
bool Execute(Event event) override;
|
bool Execute(Event event) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class KarazhanWizardOfOzScorchStrawmanAction : public Action
|
class PrinceMalchezaarEnfeebledAvoidHazardAction : public MovementAction
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
KarazhanWizardOfOzScorchStrawmanAction(PlayerbotAI* botAI, std::string const name = "karazhan wizard of oz scorch strawman") : Action(botAI, name) {}
|
PrinceMalchezaarEnfeebledAvoidHazardAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "prince malchezaar enfeebled avoid hazard") : MovementAction(botAI, name) {}
|
||||||
bool Execute(Event event) override;
|
bool Execute(Event event) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class KarazhanTheCuratorMarkTargetAction : public Action
|
class PrinceMalchezaarNonTankAvoidInfernalAction : public MovementAction
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
KarazhanTheCuratorMarkTargetAction(PlayerbotAI* botAI, std::string const name = "karazhan the curator mark target") : Action(botAI, name) {}
|
PrinceMalchezaarNonTankAvoidInfernalAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "prince malchezaar non tank avoid infernal") : MovementAction(botAI, name) {}
|
||||||
bool Execute(Event event) override;
|
bool Execute(Event event) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class KarazhanTheCuratorPositionBossAction : public MovementAction
|
class PrinceMalchezaarMainTankMovementAction : public AttackAction
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
KarazhanTheCuratorPositionBossAction(PlayerbotAI* botAI, std::string const name = "karazhan the curator position boss") : MovementAction(botAI, name) {}
|
PrinceMalchezaarMainTankMovementAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "prince malchezaar main tank movement") : AttackAction(botAI, name) {}
|
||||||
bool Execute(Event event) override;
|
|
||||||
bool isUseful() override;
|
|
||||||
};
|
|
||||||
|
|
||||||
class KarazhanTheCuratorSpreadRangedAction : public MovementAction
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
KarazhanTheCuratorSpreadRangedAction(PlayerbotAI* botAI, std::string const name = "karazhan the curator spread ranged") : MovementAction(botAI, name) {}
|
|
||||||
|
|
||||||
bool Execute(Event event) override;
|
|
||||||
bool isUseful() override;
|
|
||||||
};
|
|
||||||
|
|
||||||
class KarazhanTerestianIllhoofMarkTargetAction : public Action
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
KarazhanTerestianIllhoofMarkTargetAction(PlayerbotAI* botAI, std::string const name = "karazhan terestian illhoof mark target") : Action(botAI, name) {}
|
|
||||||
|
|
||||||
bool Execute(Event event) override;
|
bool Execute(Event event) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class KarazhanShadeOfAranArcaneExplosionRunAwayAction : public MovementAction
|
class NightbaneGroundPhasePositionBossAction : public AttackAction
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
KarazhanShadeOfAranArcaneExplosionRunAwayAction(PlayerbotAI* botAI, std::string const name = "karazhan shade of aran arcane explosion run away") : MovementAction(botAI, name) {}
|
NightbaneGroundPhasePositionBossAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "nightbane ground phase position boss") : AttackAction(botAI, name) {}
|
||||||
bool Execute(Event event) override;
|
|
||||||
bool isUseful() override;
|
|
||||||
};
|
|
||||||
|
|
||||||
class KarazhanShadeOfAranFlameWreathStopMovementAction : public MovementAction
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
KarazhanShadeOfAranFlameWreathStopMovementAction(PlayerbotAI* botAI, std::string const name = "karazhan shade of aran flame wreath stop bot") : MovementAction(botAI, name) {}
|
|
||||||
|
|
||||||
bool Execute(Event event) override;
|
bool Execute(Event event) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class KarazhanShadeOfAranMarkConjuredElementalAction : public Action
|
class NightbaneGroundPhaseRotateRangedPositionsAction : public MovementAction
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
KarazhanShadeOfAranMarkConjuredElementalAction(PlayerbotAI* botAI, std::string const name = "karazhan shade of aran mark conjured elemental") : Action(botAI, name) {}
|
NightbaneGroundPhaseRotateRangedPositionsAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "nightbane ground phase rotate ranged positions") : MovementAction(botAI, name) {}
|
||||||
bool Execute(Event event) override;
|
bool Execute(Event event) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class KarazhanShadeOfAranSpreadRangedAction : public MovementAction
|
class NightbaneCastFearWardOnMainTankAction : public Action
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
KarazhanShadeOfAranSpreadRangedAction(PlayerbotAI* botAI, std::string const name = "karazhan shade of aran spread ranged") : MovementAction(botAI, name) {}
|
NightbaneCastFearWardOnMainTankAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "nightbane cast fear ward on main tank") : Action(botAI, name) {}
|
||||||
bool Execute(Event event) override;
|
bool Execute(Event event) override;
|
||||||
bool isUseful() override;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class KarazhanNetherspiteBlockRedBeamAction : public MovementAction
|
class NightbaneControlPetAggressionAction : public Action
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
KarazhanNetherspiteBlockRedBeamAction(PlayerbotAI* botAI, std::string const name = "karazhan netherspite block red beam") : MovementAction(botAI, name) {}
|
NightbaneControlPetAggressionAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "nightbane control pet aggression") : Action(botAI, name) {}
|
||||||
bool Execute(Event event) override;
|
bool Execute(Event event) override;
|
||||||
bool isUseful() override;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class KarazhanNetherspiteBlockBlueBeamAction : public MovementAction
|
class NightbaneFlightPhaseMovementAction : public MovementAction
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
KarazhanNetherspiteBlockBlueBeamAction(PlayerbotAI* botAI, std::string const name = "karazhan netherspite block blue beam") : MovementAction(botAI, name) {}
|
NightbaneFlightPhaseMovementAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "nightbane flight phase movement") : MovementAction(botAI, name) {}
|
||||||
bool Execute(Event event) override;
|
bool Execute(Event event) override;
|
||||||
bool isUseful() override;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class KarazhanNetherspiteBlockGreenBeamAction : public MovementAction
|
class NightbaneManageTimersAndTrackersAction : public Action
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
KarazhanNetherspiteBlockGreenBeamAction(PlayerbotAI* botAI, std::string const name = "karazhan netherspite block green beam") : MovementAction(botAI, name) {}
|
NightbaneManageTimersAndTrackersAction(
|
||||||
|
PlayerbotAI* botAI, std::string const name = "nightbane manage timers and trackers") : Action(botAI, name) {}
|
||||||
bool Execute(Event event) override;
|
bool Execute(Event event) override;
|
||||||
bool isUseful() override;
|
|
||||||
};
|
|
||||||
|
|
||||||
class KarazhanNetherspiteAvoidBeamAndVoidZoneAction : public MovementAction
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
KarazhanNetherspiteAvoidBeamAndVoidZoneAction(PlayerbotAI* botAI, std::string const name = "karazhan netherspite avoid beam and void zone") : MovementAction(botAI, name) {}
|
|
||||||
|
|
||||||
bool Execute(Event event) override;
|
|
||||||
bool isUseful() override;
|
|
||||||
};
|
|
||||||
|
|
||||||
class KarazhanNetherspiteBanishPhaseAvoidVoidZoneAction : public MovementAction
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
KarazhanNetherspiteBanishPhaseAvoidVoidZoneAction(PlayerbotAI* botAI, std::string const name = "karazhan netherspite banish phase avoid void zone") : MovementAction(botAI, name) {}
|
|
||||||
|
|
||||||
bool Execute(Event event) override;
|
|
||||||
bool isUseful() override;
|
|
||||||
};
|
|
||||||
|
|
||||||
class KarazhanPrinceMalchezaarNonTankAvoidHazardAction : public MovementAction
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
KarazhanPrinceMalchezaarNonTankAvoidHazardAction(PlayerbotAI* botAI, std::string const name = "karazhan prince malchezaar non-tank avoid hazard") : MovementAction(botAI, name) {}
|
|
||||||
|
|
||||||
bool Execute(Event event) override;
|
|
||||||
bool isUseful() override;
|
|
||||||
};
|
|
||||||
|
|
||||||
class KarazhanPrinceMalchezaarTankAvoidHazardAction : public MovementAction
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
KarazhanPrinceMalchezaarTankAvoidHazardAction(PlayerbotAI* botAI, std::string const name = "karazhan prince malchezaar tank avoid hazard") : MovementAction(botAI, name) {}
|
|
||||||
|
|
||||||
bool Execute(Event event) override;
|
|
||||||
bool isUseful() override;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -1,316 +1,356 @@
|
|||||||
#include <algorithm>
|
|
||||||
#include <map>
|
|
||||||
|
|
||||||
#include "RaidKarazhanHelpers.h"
|
#include "RaidKarazhanHelpers.h"
|
||||||
#include "RaidKarazhanActions.h"
|
#include "RaidKarazhanActions.h"
|
||||||
#include "AiObjectContext.h"
|
#include "Playerbots.h"
|
||||||
#include "PlayerbotMgr.h"
|
#include "RtiTargetValue.h"
|
||||||
#include "Position.h"
|
|
||||||
#include "Spell.h"
|
|
||||||
|
|
||||||
const Position KARAZHAN_MAIDEN_OF_VIRTUE_BOSS_POSITION = Position(-10945.881f, -2103.782f, 92.712f);
|
namespace KarazhanHelpers
|
||||||
const Position KARAZHAN_MAIDEN_OF_VIRTUE_RANGED_POSITION[8] =
|
|
||||||
{
|
{
|
||||||
{ -10931.178f, -2116.580f, 92.179f },
|
// Attumen the Huntsman
|
||||||
{ -10925.828f, -2102.425f, 92.180f },
|
std::unordered_map<uint32, time_t> attumenDpsWaitTimer;
|
||||||
{ -10933.089f, -2088.5017f, 92.180f },
|
// Big Bad Wolf
|
||||||
{ -10947.59f, -2082.8147f, 92.180f },
|
std::unordered_map<ObjectGuid, uint8> bigBadWolfRunIndex;
|
||||||
{ -10960.912f, -2090.4368f, 92.179f },
|
// Netherspite
|
||||||
{ -10966.017f, -2105.288f, 92.175f },
|
std::unordered_map<uint32, time_t> netherspiteDpsWaitTimer;
|
||||||
{ -10959.242f, -2119.6172f, 92.180f },
|
std::unordered_map<ObjectGuid, time_t> redBeamMoveTimer;
|
||||||
{ -10944.495f, -2123.857f, 92.180f },
|
std::unordered_map<ObjectGuid, bool> lastBeamMoveSideways;
|
||||||
};
|
// Nightbane
|
||||||
|
std::unordered_map<uint32, time_t> nightbaneDpsWaitTimer;
|
||||||
|
std::unordered_map<ObjectGuid, uint8> nightbaneTankStep;
|
||||||
|
std::unordered_map<ObjectGuid, uint8> nightbaneRangedStep;
|
||||||
|
std::unordered_map<uint32, time_t> nightbaneFlightPhaseStartTimer;
|
||||||
|
std::unordered_map<ObjectGuid, bool> nightbaneRainOfBonesHit;
|
||||||
|
|
||||||
const Position KARAZHAN_BIG_BAD_WOLF_BOSS_POSITION = Position(-10913.391f, -1773.508f, 90.477f);
|
const Position MAIDEN_OF_VIRTUE_BOSS_POSITION = { -10945.881f, -2103.782f, 92.712f };
|
||||||
const Position KARAZHAN_BIG_BAD_WOLF_RUN_POSITION[4] =
|
const Position MAIDEN_OF_VIRTUE_RANGED_POSITION[8] =
|
||||||
{
|
|
||||||
{ -10875.456f, -1779.036f, 90.477f },
|
|
||||||
{ -10872.281f, -1751.638f, 90.477f },
|
|
||||||
{ -10910.492f, -1747.401f, 90.477f },
|
|
||||||
{ -10913.391f, -1773.508f, 90.477f },
|
|
||||||
};
|
|
||||||
|
|
||||||
const Position KARAZHAN_THE_CURATOR_BOSS_POSITION = Position(-11139.463f, -1884.645f, 165.765f);
|
|
||||||
|
|
||||||
void RaidKarazhanHelpers::MarkTargetWithSkull(Unit* target)
|
|
||||||
{
|
|
||||||
if (!target)
|
|
||||||
{
|
{
|
||||||
return;
|
{ -10931.178f, -2116.580f, 92.179f },
|
||||||
}
|
{ -10925.828f, -2102.425f, 92.180f },
|
||||||
|
{ -10933.089f, -2088.502f, 92.180f },
|
||||||
|
{ -10947.590f, -2082.815f, 92.180f },
|
||||||
|
{ -10960.912f, -2090.437f, 92.179f },
|
||||||
|
{ -10966.017f, -2105.288f, 92.175f },
|
||||||
|
{ -10959.242f, -2119.617f, 92.180f },
|
||||||
|
{ -10944.495f, -2123.857f, 92.180f },
|
||||||
|
};
|
||||||
|
|
||||||
if (Group* group = bot->GetGroup())
|
const Position BIG_BAD_WOLF_BOSS_POSITION = { -10913.391f, -1773.508f, 90.477f };
|
||||||
|
const Position BIG_BAD_WOLF_RUN_POSITION[4] =
|
||||||
{
|
{
|
||||||
constexpr uint8_t skullIconId = 7;
|
{ -10875.456f, -1779.036f, 90.477f },
|
||||||
ObjectGuid skullGuid = group->GetTargetIcon(skullIconId);
|
{ -10872.281f, -1751.638f, 90.477f },
|
||||||
|
{ -10910.492f, -1747.401f, 90.477f },
|
||||||
|
{ -10913.391f, -1773.508f, 90.477f },
|
||||||
|
};
|
||||||
|
|
||||||
if (skullGuid != target->GetGUID())
|
const Position THE_CURATOR_BOSS_POSITION = { -11139.463f, -1884.645f, 165.765f };
|
||||||
|
|
||||||
|
const Position NIGHTBANE_TRANSITION_BOSS_POSITION = { -11160.646f, -1932.773f, 91.473f }; // near some ribs
|
||||||
|
const Position NIGHTBANE_FINAL_BOSS_POSITION = { -11173.530f, -1940.707f, 91.473f };
|
||||||
|
const Position NIGHTBANE_RANGED_POSITION1 = { -11145.949f, -1970.927f, 91.473f };
|
||||||
|
const Position NIGHTBANE_RANGED_POSITION2 = { -11143.594f, -1954.981f, 91.473f };
|
||||||
|
const Position NIGHTBANE_RANGED_POSITION3 = { -11159.778f, -1961.031f, 91.473f };
|
||||||
|
const Position NIGHTBANE_FLIGHT_STACK_POSITION = { -11159.555f, -1893.526f, 91.473f }; // Broken Barrel
|
||||||
|
const Position NIGHTBANE_RAIN_OF_BONES_POSITION = { -11165.233f, -1911.123f, 91.473f };
|
||||||
|
|
||||||
|
void MarkTargetWithIcon(Player* bot, Unit* target, uint8 iconId)
|
||||||
|
{
|
||||||
|
if (!target)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (Group* group = bot->GetGroup())
|
||||||
{
|
{
|
||||||
group->SetTargetIcon(skullIconId, bot->GetGUID(), target->GetGUID());
|
ObjectGuid currentGuid = group->GetTargetIcon(iconId);
|
||||||
}
|
if (currentGuid != target->GetGUID())
|
||||||
}
|
group->SetTargetIcon(iconId, bot->GetGUID(), target->GetGUID());
|
||||||
}
|
|
||||||
|
|
||||||
Unit* RaidKarazhanHelpers::GetFirstAliveUnit(const std::vector<Unit*>& units)
|
|
||||||
{
|
|
||||||
for (Unit* unit : units)
|
|
||||||
{
|
|
||||||
if (unit && unit->IsAlive())
|
|
||||||
{
|
|
||||||
return unit;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nullptr;
|
void MarkTargetWithSkull(Player* bot, Unit* target)
|
||||||
}
|
|
||||||
|
|
||||||
Unit* RaidKarazhanHelpers::GetFirstAliveUnitByEntry(uint32 entry)
|
|
||||||
{
|
|
||||||
const GuidVector npcs = AI_VALUE(GuidVector, "nearest hostile npcs");
|
|
||||||
|
|
||||||
for (auto const& npcGuid : npcs)
|
|
||||||
{
|
{
|
||||||
Unit* unit = botAI->GetUnit(npcGuid);
|
MarkTargetWithIcon(bot, target, RtiTargetValue::skullIndex);
|
||||||
|
}
|
||||||
|
|
||||||
if (unit && unit->IsAlive() && unit->GetEntry() == entry)
|
void MarkTargetWithSquare(Player* bot, Unit* target)
|
||||||
|
{
|
||||||
|
MarkTargetWithIcon(bot, target, RtiTargetValue::squareIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MarkTargetWithStar(Player* bot, Unit* target)
|
||||||
|
{
|
||||||
|
MarkTargetWithIcon(bot, target, RtiTargetValue::starIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MarkTargetWithCircle(Player* bot, Unit* target)
|
||||||
|
{
|
||||||
|
MarkTargetWithIcon(bot, target, RtiTargetValue::circleIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MarkTargetWithMoon(Player* bot, Unit* target)
|
||||||
|
{
|
||||||
|
MarkTargetWithIcon(bot, target, RtiTargetValue::moonIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetRtiTarget(PlayerbotAI* botAI, const std::string& rtiName, Unit* target)
|
||||||
|
{
|
||||||
|
if (!target)
|
||||||
|
return;
|
||||||
|
|
||||||
|
std::string currentRti = botAI->GetAiObjectContext()->GetValue<std::string>("rti")->Get();
|
||||||
|
Unit* currentTarget = botAI->GetAiObjectContext()->GetValue<Unit*>("rti target")->Get();
|
||||||
|
|
||||||
|
if (currentRti != rtiName || currentTarget != target)
|
||||||
{
|
{
|
||||||
return unit;
|
botAI->GetAiObjectContext()->GetValue<std::string>("rti")->Set(rtiName);
|
||||||
|
botAI->GetAiObjectContext()->GetValue<Unit*>("rti target")->Set(target);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nullptr;
|
// Only one bot is needed to set/reset mapwide timers
|
||||||
}
|
bool IsMapIDTimerManager(PlayerbotAI* botAI, Player* bot)
|
||||||
|
|
||||||
Unit* RaidKarazhanHelpers::GetNearestPlayerInRadius(float radius)
|
|
||||||
{
|
|
||||||
if (Group* group = bot->GetGroup())
|
|
||||||
{
|
{
|
||||||
for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next())
|
if (Group* group = bot->GetGroup())
|
||||||
{
|
{
|
||||||
Player* member = itr->GetSource();
|
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||||
|
|
||||||
if (!member || !member->IsAlive() || member == bot)
|
|
||||||
{
|
{
|
||||||
continue;
|
Player* member = ref->GetSource();
|
||||||
}
|
if (member && member->IsAlive() && botAI->IsDps(member) && GET_PLAYERBOT_AI(member))
|
||||||
|
return member == bot;
|
||||||
if (bot->GetExactDist2d(member) < radius)
|
|
||||||
{
|
|
||||||
return member;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return nullptr;
|
Unit* GetFirstAliveUnit(const std::vector<Unit*>& units)
|
||||||
}
|
|
||||||
|
|
||||||
bool RaidKarazhanHelpers::IsFlameWreathActive()
|
|
||||||
{
|
|
||||||
Unit* boss = AI_VALUE2(Unit*, "find target", "shade of aran");
|
|
||||||
Spell* currentSpell = boss ? boss->GetCurrentSpell(CURRENT_GENERIC_SPELL) : nullptr;
|
|
||||||
if (currentSpell && currentSpell->m_spellInfo && currentSpell->m_spellInfo->Id == SPELL_FLAME_WREATH)
|
|
||||||
{
|
{
|
||||||
return true;
|
for (Unit* unit : units)
|
||||||
}
|
|
||||||
|
|
||||||
if (Group* group = bot->GetGroup())
|
|
||||||
{
|
|
||||||
for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next())
|
|
||||||
{
|
{
|
||||||
Player* member = itr->GetSource();
|
if (unit && unit->IsAlive())
|
||||||
if (!member || !member->IsAlive())
|
return unit;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Unit* GetFirstAliveUnitByEntry(PlayerbotAI* botAI, uint32 entry)
|
||||||
|
{
|
||||||
|
const GuidVector npcs = botAI->GetAiObjectContext()->GetValue<GuidVector>("nearest hostile npcs")->Get();
|
||||||
|
for (auto const& npcGuid : npcs)
|
||||||
|
{
|
||||||
|
Unit* unit = botAI->GetUnit(npcGuid);
|
||||||
|
if (unit && unit->IsAlive() && unit->GetEntry() == entry)
|
||||||
|
return unit;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Unit* GetNearestPlayerInRadius(Player* bot, float radius)
|
||||||
|
{
|
||||||
|
Unit* nearestPlayer = nullptr;
|
||||||
|
float nearestDistance = radius;
|
||||||
|
|
||||||
|
if (Group* group = bot->GetGroup())
|
||||||
|
{
|
||||||
|
for (GroupReference* ref = group->GetFirstMember(); ref != nullptr; ref = ref->next())
|
||||||
{
|
{
|
||||||
continue;
|
Player* member = ref->GetSource();
|
||||||
}
|
if (!member || !member->IsAlive() || member == bot)
|
||||||
if (member->HasAura(SPELL_AURA_FLAME_WREATH))
|
continue;
|
||||||
{
|
|
||||||
return true;
|
float distance = bot->GetExactDist2d(member);
|
||||||
|
if (distance < nearestDistance)
|
||||||
|
{
|
||||||
|
nearestDistance = distance;
|
||||||
|
nearestPlayer = member;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return nearestPlayer;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
bool IsFlameWreathActive(PlayerbotAI* botAI, Player* bot)
|
||||||
}
|
|
||||||
|
|
||||||
// Red beam blockers: tank bots, no Nether Exhaustion Red
|
|
||||||
std::vector<Player*> RaidKarazhanHelpers::GetRedBlockers()
|
|
||||||
{
|
|
||||||
std::vector<Player*> redBlockers;
|
|
||||||
if (Group* group = bot->GetGroup())
|
|
||||||
{
|
{
|
||||||
for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next())
|
Unit* aran = botAI->GetAiObjectContext()->GetValue<Unit*>("find target", "shade of aran")->Get();
|
||||||
{
|
Spell* currentSpell = aran ? aran->GetCurrentSpell(CURRENT_GENERIC_SPELL) : nullptr;
|
||||||
Player* member = itr->GetSource();
|
|
||||||
if (!member || !member->IsAlive() || !botAI->IsTank(member) || !GET_PLAYERBOT_AI(member) ||
|
|
||||||
member->HasAura(SPELL_NETHER_EXHAUSTION_RED))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
redBlockers.push_back(member);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return redBlockers;
|
if (currentSpell && currentSpell->m_spellInfo &&
|
||||||
}
|
currentSpell->m_spellInfo->Id == SPELL_FLAME_WREATH_CAST)
|
||||||
|
return true;
|
||||||
|
|
||||||
// Blue beam blockers: non-Rogue/Warrior DPS bots, no Nether Exhaustion Blue and ≤25 stacks of Blue Beam debuff
|
if (Group* group = bot->GetGroup())
|
||||||
std::vector<Player*> RaidKarazhanHelpers::GetBlueBlockers()
|
|
||||||
{
|
|
||||||
std::vector<Player*> blueBlockers;
|
|
||||||
if (Group* group = bot->GetGroup())
|
|
||||||
{
|
|
||||||
for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next())
|
|
||||||
{
|
{
|
||||||
Player* member = itr->GetSource();
|
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||||
if (!member || !member->IsAlive() || !GET_PLAYERBOT_AI(member))
|
|
||||||
{
|
{
|
||||||
continue;
|
Player* member = ref->GetSource();
|
||||||
}
|
if (!member || !member->IsAlive())
|
||||||
bool isDps = botAI->IsDps(member);
|
continue;
|
||||||
bool isWarrior = member->getClass() == CLASS_WARRIOR;
|
|
||||||
bool isRogue = member->getClass() == CLASS_ROGUE;
|
if (member->HasAura(SPELL_FLAME_WREATH_AURA))
|
||||||
bool hasExhaustion = member->HasAura(SPELL_NETHER_EXHAUSTION_BLUE);
|
return true;
|
||||||
Aura* blueBuff = member->GetAura(SPELL_BLUE_BEAM_DEBUFF);
|
|
||||||
bool overStack = blueBuff && blueBuff->GetStackAmount() >= 26;
|
|
||||||
if (isDps && !isWarrior && !isRogue && !hasExhaustion && !overStack)
|
|
||||||
{
|
|
||||||
blueBlockers.push_back(member);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return blueBlockers;
|
// Red beam blockers: tank bots, no Nether Exhaustion Red
|
||||||
}
|
std::vector<Player*> GetRedBlockers(PlayerbotAI* botAI, Player* bot)
|
||||||
|
|
||||||
// Green beam blockers:
|
|
||||||
// (1) Rogue and non-tank Warrior bots, no Nether Exhaustion Green
|
|
||||||
// (2) Healer bots, no Nether Exhaustion Green and ≤25 stacks of Green Beam debuff
|
|
||||||
std::vector<Player*> RaidKarazhanHelpers::GetGreenBlockers()
|
|
||||||
{
|
|
||||||
std::vector<Player*> greenBlockers;
|
|
||||||
if (Group* group = bot->GetGroup())
|
|
||||||
{
|
{
|
||||||
for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next())
|
std::vector<Player*> redBlockers;
|
||||||
|
if (Group* group = bot->GetGroup())
|
||||||
{
|
{
|
||||||
Player* member = itr->GetSource();
|
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||||
if (!member || !member->IsAlive() || !GET_PLAYERBOT_AI(member))
|
|
||||||
{
|
{
|
||||||
continue;
|
Player* member = ref->GetSource();
|
||||||
}
|
if (!member || !member->IsAlive() || !botAI->IsTank(member) || !GET_PLAYERBOT_AI(member) ||
|
||||||
bool hasExhaustion = member->HasAura(SPELL_NETHER_EXHAUSTION_GREEN);
|
member->HasAura(SPELL_NETHER_EXHAUSTION_RED))
|
||||||
Aura* greenBuff = member->GetAura(SPELL_GREEN_BEAM_DEBUFF);
|
continue;
|
||||||
bool overStack = greenBuff && greenBuff->GetStackAmount() >= 26;
|
|
||||||
bool isRogue = member->getClass() == CLASS_ROGUE;
|
redBlockers.push_back(member);
|
||||||
bool isDpsWarrior = member->getClass() == CLASS_WARRIOR && botAI->IsDps(member);
|
|
||||||
bool eligibleRogueWarrior = (isRogue || isDpsWarrior) && !hasExhaustion;
|
|
||||||
bool isHealer = botAI->IsHeal(member);
|
|
||||||
bool eligibleHealer = isHealer && !hasExhaustion && !overStack;
|
|
||||||
if (eligibleRogueWarrior || eligibleHealer)
|
|
||||||
{
|
|
||||||
greenBlockers.push_back(member);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return redBlockers;
|
||||||
}
|
}
|
||||||
|
|
||||||
return greenBlockers;
|
// Blue beam blockers: non-Rogue/Warrior DPS bots, no Nether Exhaustion Blue and <24 stacks of Blue Beam debuff
|
||||||
}
|
std::vector<Player*> GetBlueBlockers(PlayerbotAI* botAI, Player* bot)
|
||||||
|
|
||||||
Position RaidKarazhanHelpers::GetPositionOnBeam(Unit* boss, Unit* portal, float distanceFromBoss)
|
|
||||||
{
|
|
||||||
float bx = boss->GetPositionX();
|
|
||||||
float by = boss->GetPositionY();
|
|
||||||
float bz = boss->GetPositionZ();
|
|
||||||
float px = portal->GetPositionX();
|
|
||||||
float py = portal->GetPositionY();
|
|
||||||
|
|
||||||
float dx = px - bx;
|
|
||||||
float dy = py - by;
|
|
||||||
float length = sqrt(dx*dx + dy*dy);
|
|
||||||
if (length == 0.0f)
|
|
||||||
{
|
{
|
||||||
return Position(bx, by, bz);
|
std::vector<Player*> blueBlockers;
|
||||||
|
if (Group* group = bot->GetGroup())
|
||||||
|
{
|
||||||
|
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||||
|
{
|
||||||
|
Player* member = ref->GetSource();
|
||||||
|
if (!member || !member->IsAlive() || !GET_PLAYERBOT_AI(member))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
bool hasExhaustion = member->HasAura(SPELL_NETHER_EXHAUSTION_BLUE);
|
||||||
|
Aura* blueBuff = member->GetAura(SPELL_BLUE_BEAM_DEBUFF);
|
||||||
|
bool overStack = blueBuff && blueBuff->GetStackAmount() >= 24;
|
||||||
|
|
||||||
|
bool isDps = botAI->IsDps(member);
|
||||||
|
bool isWarrior = member->getClass() == CLASS_WARRIOR;
|
||||||
|
bool isRogue = member->getClass() == CLASS_ROGUE;
|
||||||
|
|
||||||
|
if (isDps && !isWarrior && !isRogue && !hasExhaustion && !overStack)
|
||||||
|
blueBlockers.push_back(member);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return blueBlockers;
|
||||||
}
|
}
|
||||||
|
|
||||||
dx /= length;
|
// Green beam blockers:
|
||||||
dy /= length;
|
// (1) Prioritize Rogues and non-tank Warrior bots, no Nether Exhaustion Green
|
||||||
float targetX = bx + dx * distanceFromBoss;
|
// (2) Then assign Healer bots, no Nether Exhaustion Green and <24 stacks of Green Beam debuff
|
||||||
float targetY = by + dy * distanceFromBoss;
|
std::vector<Player*> GetGreenBlockers(PlayerbotAI* botAI, Player* bot)
|
||||||
float targetZ = bz;
|
|
||||||
|
|
||||||
return Position(targetX, targetY, targetZ);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::tuple<Player*, Player*, Player*> RaidKarazhanHelpers::GetCurrentBeamBlockers()
|
|
||||||
{
|
|
||||||
static ObjectGuid currentRedBlocker;
|
|
||||||
static ObjectGuid currentGreenBlocker;
|
|
||||||
static ObjectGuid currentBlueBlocker;
|
|
||||||
|
|
||||||
Player* redBlocker = nullptr;
|
|
||||||
Player* greenBlocker = nullptr;
|
|
||||||
Player* blueBlocker = nullptr;
|
|
||||||
|
|
||||||
std::vector<Player*> redBlockers = GetRedBlockers();
|
|
||||||
if (!redBlockers.empty())
|
|
||||||
{
|
{
|
||||||
auto it = std::find_if(redBlockers.begin(), redBlockers.end(), [](Player* p)
|
std::vector<Player*> greenBlockers;
|
||||||
|
if (Group* group = bot->GetGroup())
|
||||||
{
|
{
|
||||||
return p && p->GetGUID() == currentRedBlocker;
|
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||||
});
|
{
|
||||||
if (it != redBlockers.end())
|
Player* member = ref->GetSource();
|
||||||
|
if (!member || !member->IsAlive() || !GET_PLAYERBOT_AI(member))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
bool hasExhaustion = member->HasAura(SPELL_NETHER_EXHAUSTION_GREEN);
|
||||||
|
bool isRogue = member->getClass() == CLASS_ROGUE;
|
||||||
|
bool isDpsWarrior = member->getClass() == CLASS_WARRIOR && botAI->IsDps(member);
|
||||||
|
bool eligibleRogueWarrior = (isRogue || isDpsWarrior) && !hasExhaustion;
|
||||||
|
|
||||||
|
if (eligibleRogueWarrior)
|
||||||
|
greenBlockers.push_back(member);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||||
|
{
|
||||||
|
Player* member = ref->GetSource();
|
||||||
|
if (!member || !member->IsAlive() || !GET_PLAYERBOT_AI(member))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
bool hasExhaustion = member->HasAura(SPELL_NETHER_EXHAUSTION_GREEN);
|
||||||
|
Aura* greenBuff = member->GetAura(SPELL_GREEN_BEAM_DEBUFF);
|
||||||
|
bool overStack = greenBuff && greenBuff->GetStackAmount() >= 24;
|
||||||
|
bool isHealer = botAI->IsHeal(member);
|
||||||
|
bool eligibleHealer = isHealer && !hasExhaustion && !overStack;
|
||||||
|
|
||||||
|
if (eligibleHealer)
|
||||||
|
greenBlockers.push_back(member);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return greenBlockers;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::tuple<Player*, Player*, Player*> GetCurrentBeamBlockers(PlayerbotAI* botAI, Player* bot)
|
||||||
|
{
|
||||||
|
static ObjectGuid currentRedBlocker;
|
||||||
|
static ObjectGuid currentGreenBlocker;
|
||||||
|
static ObjectGuid currentBlueBlocker;
|
||||||
|
|
||||||
|
Player* redBlocker = nullptr;
|
||||||
|
Player* greenBlocker = nullptr;
|
||||||
|
Player* blueBlocker = nullptr;
|
||||||
|
|
||||||
|
std::vector<Player*> redBlockers = GetRedBlockers(botAI, bot);
|
||||||
|
if (!redBlockers.empty())
|
||||||
{
|
{
|
||||||
redBlocker = *it;
|
auto it = std::find_if(redBlockers.begin(), redBlockers.end(), [](Player* player)
|
||||||
|
{
|
||||||
|
return player && player->GetGUID() == currentRedBlocker;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (it != redBlockers.end())
|
||||||
|
redBlocker = *it;
|
||||||
|
else
|
||||||
|
redBlocker = redBlockers.front();
|
||||||
|
|
||||||
|
currentRedBlocker = redBlocker ? redBlocker->GetGUID() : ObjectGuid::Empty;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
redBlocker = redBlockers.front();
|
currentRedBlocker = ObjectGuid::Empty;
|
||||||
|
redBlocker = nullptr;
|
||||||
}
|
}
|
||||||
currentRedBlocker = redBlocker ? redBlocker->GetGUID() : ObjectGuid::Empty;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
currentRedBlocker = ObjectGuid::Empty;
|
|
||||||
redBlocker = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<Player*> greenBlockers = GetGreenBlockers();
|
std::vector<Player*> greenBlockers = GetGreenBlockers(botAI, bot);
|
||||||
if (!greenBlockers.empty())
|
if (!greenBlockers.empty())
|
||||||
{
|
|
||||||
auto it = std::find_if(greenBlockers.begin(), greenBlockers.end(), [](Player* p)
|
|
||||||
{
|
{
|
||||||
return p && p->GetGUID() == currentGreenBlocker;
|
auto it = std::find_if(greenBlockers.begin(), greenBlockers.end(), [](Player* player)
|
||||||
});
|
{
|
||||||
if (it != greenBlockers.end())
|
return player && player->GetGUID() == currentGreenBlocker;
|
||||||
{
|
});
|
||||||
greenBlocker = *it;
|
|
||||||
|
if (it != greenBlockers.end())
|
||||||
|
greenBlocker = *it;
|
||||||
|
else
|
||||||
|
greenBlocker = greenBlockers.front();
|
||||||
|
|
||||||
|
currentGreenBlocker = greenBlocker ? greenBlocker->GetGUID() : ObjectGuid::Empty;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
greenBlocker = greenBlockers.front();
|
currentGreenBlocker = ObjectGuid::Empty;
|
||||||
|
greenBlocker = nullptr;
|
||||||
}
|
}
|
||||||
currentGreenBlocker = greenBlocker ? greenBlocker->GetGUID() : ObjectGuid::Empty;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
currentGreenBlocker = ObjectGuid::Empty;
|
|
||||||
greenBlocker = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<Player*> blueBlockers = GetBlueBlockers();
|
std::vector<Player*> blueBlockers = GetBlueBlockers(botAI, bot);
|
||||||
if (!blueBlockers.empty())
|
if (!blueBlockers.empty())
|
||||||
{
|
{
|
||||||
auto it = std::find_if(blueBlockers.begin(), blueBlockers.end(), [](Player* p)
|
auto it = std::find_if(blueBlockers.begin(), blueBlockers.end(), [](Player* player)
|
||||||
{
|
{
|
||||||
return p && p->GetGUID() == currentBlueBlocker;
|
return player && player->GetGUID() == currentBlueBlocker;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (it != blueBlockers.end())
|
if (it != blueBlockers.end())
|
||||||
{
|
|
||||||
blueBlocker = *it;
|
blueBlocker = *it;
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
|
||||||
blueBlocker = blueBlockers.front();
|
blueBlocker = blueBlockers.front();
|
||||||
}
|
|
||||||
currentBlueBlocker = blueBlocker ? blueBlocker->GetGUID() : ObjectGuid::Empty;
|
currentBlueBlocker = blueBlocker ? blueBlocker->GetGUID() : ObjectGuid::Empty;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -319,91 +359,132 @@ std::tuple<Player*, Player*, Player*> RaidKarazhanHelpers::GetCurrentBeamBlocker
|
|||||||
blueBlocker = nullptr;
|
blueBlocker = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
return std::make_tuple(redBlocker, greenBlocker, blueBlocker);
|
return std::make_tuple(redBlocker, greenBlocker, blueBlocker);
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<Unit*> RaidKarazhanHelpers::GetAllVoidZones()
|
|
||||||
{
|
|
||||||
std::vector<Unit*> voidZones;
|
|
||||||
const float radius = 30.0f;
|
|
||||||
const GuidVector npcs = botAI->GetAiObjectContext()->GetValue<GuidVector>("nearest npcs")->Get();
|
|
||||||
for (auto const& npcGuid : npcs)
|
|
||||||
{
|
|
||||||
Unit* unit = botAI->GetUnit(npcGuid);
|
|
||||||
if (!unit || unit->GetEntry() != NPC_VOID_ZONE)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
float dist = bot->GetExactDist2d(unit);
|
|
||||||
if (dist < radius)
|
|
||||||
{
|
|
||||||
voidZones.push_back(unit);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return voidZones;
|
std::vector<Unit*> GetAllVoidZones(PlayerbotAI* botAI, Player* bot)
|
||||||
}
|
|
||||||
|
|
||||||
bool RaidKarazhanHelpers::IsSafePosition(float x, float y, float z,
|
|
||||||
const std::vector<Unit*>& hazards, float hazardRadius)
|
|
||||||
{
|
|
||||||
for (Unit* hazard : hazards)
|
|
||||||
{
|
{
|
||||||
float dist = std::sqrt(std::pow(x - hazard->GetPositionX(), 2) + std::pow(y - hazard->GetPositionY(), 2));
|
std::vector<Unit*> voidZones;
|
||||||
if (dist < hazardRadius)
|
const float radius = 30.0f;
|
||||||
|
const GuidVector npcs = botAI->GetAiObjectContext()->GetValue<GuidVector>("nearest npcs")->Get();
|
||||||
|
for (auto const& npcGuid : npcs)
|
||||||
{
|
{
|
||||||
return false;
|
Unit* unit = botAI->GetUnit(npcGuid);
|
||||||
|
if (!unit || unit->GetEntry() != NPC_VOID_ZONE)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
float dist = bot->GetExactDist2d(unit);
|
||||||
|
if (dist < radius)
|
||||||
|
voidZones.push_back(unit);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return voidZones;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
bool IsSafePosition(float x, float y, float z, const std::vector<Unit*>& hazards, float hazardRadius)
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<Unit*> RaidKarazhanHelpers::GetSpawnedInfernals() const
|
|
||||||
{
|
|
||||||
std::vector<Unit*> infernals;
|
|
||||||
const GuidVector npcs = botAI->GetAiObjectContext()->GetValue<GuidVector>("nearest npcs")->Get();
|
|
||||||
for (auto const& npcGuid : npcs)
|
|
||||||
{
|
{
|
||||||
Unit* unit = botAI->GetUnit(npcGuid);
|
for (Unit* hazard : hazards)
|
||||||
if (unit && unit->GetEntry() == NPC_NETHERSPITE_INFERNAL)
|
|
||||||
{
|
{
|
||||||
infernals.push_back(unit);
|
float dist = hazard->GetExactDist2d(x, y);
|
||||||
|
if (dist < hazardRadius)
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return infernals;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool RaidKarazhanHelpers::IsStraightPathSafe(const Position& start, const Position& target, const std::vector<Unit*>& hazards, float hazardRadius, float stepSize)
|
|
||||||
{
|
|
||||||
float sx = start.GetPositionX();
|
|
||||||
float sy = start.GetPositionY();
|
|
||||||
float sz = start.GetPositionZ();
|
|
||||||
float tx = target.GetPositionX();
|
|
||||||
float ty = target.GetPositionY();
|
|
||||||
float tz = target.GetPositionZ();
|
|
||||||
float totalDist = std::sqrt(std::pow(tx - sx, 2) + std::pow(ty - sy, 2));
|
|
||||||
if (totalDist == 0.0f)
|
|
||||||
{
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (float checkDist = 0.0f; checkDist <= totalDist; checkDist += stepSize)
|
std::vector<Unit*> GetSpawnedInfernals(PlayerbotAI* botAI)
|
||||||
{
|
{
|
||||||
float t = checkDist / totalDist;
|
std::vector<Unit*> infernals;
|
||||||
float checkX = sx + (tx - sx) * t;
|
const GuidVector npcs = botAI->GetAiObjectContext()->GetValue<GuidVector>("nearest npcs")->Get();
|
||||||
float checkY = sy + (ty - sy) * t;
|
for (auto const& npcGuid : npcs)
|
||||||
float checkZ = sz + (tz - sz) * t;
|
|
||||||
for (Unit* hazard : hazards)
|
|
||||||
{
|
{
|
||||||
float hazardDist = std::sqrt(std::pow(checkX - hazard->GetPositionX(), 2) + std::pow(checkY - hazard->GetPositionY(), 2));
|
Unit* unit = botAI->GetUnit(npcGuid);
|
||||||
if (hazardDist < hazardRadius)
|
if (unit && unit->GetEntry() == NPC_NETHERSPITE_INFERNAL)
|
||||||
{
|
infernals.push_back(unit);
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return infernals;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
bool IsStraightPathSafe(const Position& start, const Position& target, const std::vector<Unit*>& hazards,
|
||||||
|
float hazardRadius, float stepSize)
|
||||||
|
{
|
||||||
|
float sx = start.GetPositionX();
|
||||||
|
float sy = start.GetPositionY();
|
||||||
|
float sz = start.GetPositionZ();
|
||||||
|
float tx = target.GetPositionX();
|
||||||
|
float ty = target.GetPositionY();
|
||||||
|
float tz = target.GetPositionZ();
|
||||||
|
|
||||||
|
const float totalDist = start.GetExactDist2d(target.GetPositionX(), target.GetPositionY());
|
||||||
|
if (totalDist == 0.0f)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
for (float checkDist = 0.0f; checkDist <= totalDist; checkDist += stepSize)
|
||||||
|
{
|
||||||
|
float t = checkDist / totalDist;
|
||||||
|
float checkX = sx + (tx - sx) * t;
|
||||||
|
float checkY = sy + (ty - sy) * t;
|
||||||
|
float checkZ = sz + (tz - sz) * t;
|
||||||
|
for (Unit* hazard : hazards)
|
||||||
|
{
|
||||||
|
const float hx = checkX - hazard->GetPositionX();
|
||||||
|
const float hy = checkY - hazard->GetPositionY();
|
||||||
|
if ((hx*hx + hy*hy) < hazardRadius * hazardRadius)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TryFindSafePositionWithSafePath(
|
||||||
|
Player* bot, float originX, float originY, float originZ, float centerX, float centerY, float centerZ,
|
||||||
|
const std::vector<Unit*>& hazards, float safeDistance, float stepSize, uint8 numAngles,
|
||||||
|
float maxSampleDist, bool requireSafePath, float& bestDestX, float& bestDestY, float& bestDestZ)
|
||||||
|
{
|
||||||
|
float bestMoveDist = std::numeric_limits<float>::max();
|
||||||
|
bool found = false;
|
||||||
|
|
||||||
|
for (int i = 0; i < numAngles; ++i)
|
||||||
|
{
|
||||||
|
float angle = (2.0f * M_PI * i) / numAngles;
|
||||||
|
float dx = cos(angle);
|
||||||
|
float dy = sin(angle);
|
||||||
|
|
||||||
|
for (float dist = stepSize; dist <= maxSampleDist; dist += stepSize)
|
||||||
|
{
|
||||||
|
float x = centerX + dx * dist;
|
||||||
|
float y = centerY + dy * dist;
|
||||||
|
float z = centerZ;
|
||||||
|
float destX = x, destY = y, destZ = z;
|
||||||
|
if (!bot->GetMap()->CheckCollisionAndGetValidCoords(bot, centerX, centerY, centerZ,
|
||||||
|
destX, destY, destZ, true))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!IsSafePosition(destX, destY, destZ, hazards, safeDistance))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (requireSafePath)
|
||||||
|
{
|
||||||
|
if (!IsStraightPathSafe(Position(originX, originY, originZ), Position(destX, destY, destZ),
|
||||||
|
hazards, safeDistance, stepSize))
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const float moveDist = Position(originX, originY, originZ).GetExactDist2d(destX, destY);
|
||||||
|
if (moveDist < bestMoveDist)
|
||||||
|
{
|
||||||
|
bestMoveDist = moveDist;
|
||||||
|
bestDestX = destX;
|
||||||
|
bestDestY = destY;
|
||||||
|
bestDestZ = destZ;
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return found;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,85 +1,136 @@
|
|||||||
#ifndef _PLAYERBOT_RAIDKARAZHANHELPERS_H_
|
#ifndef _PLAYERBOT_RAIDKARAZHANHELPERS_H_
|
||||||
#define _PLAYERBOT_RAIDKARAZHANHELPERS_H_
|
#define _PLAYERBOT_RAIDKARAZHANHELPERS_H_
|
||||||
|
|
||||||
|
#include <ctime>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
#include "AiObject.h"
|
#include "AiObject.h"
|
||||||
#include "Playerbots.h"
|
|
||||||
#include "Position.h"
|
#include "Position.h"
|
||||||
|
#include "Unit.h"
|
||||||
|
|
||||||
enum KarazhanSpells
|
namespace KarazhanHelpers
|
||||||
{
|
{
|
||||||
// Maiden of Virtue
|
enum KarazhanSpells
|
||||||
SPELL_REPENTANCE = 29511,
|
{
|
||||||
|
// Maiden of Virtue
|
||||||
|
SPELL_REPENTANCE = 29511,
|
||||||
|
|
||||||
// Opera Event
|
// Opera Event
|
||||||
SPELL_LITTLE_RED_RIDING_HOOD = 30756,
|
SPELL_LITTLE_RED_RIDING_HOOD = 30756,
|
||||||
|
|
||||||
// Shade of Aran
|
// The Curator
|
||||||
SPELL_FLAME_WREATH = 30004,
|
SPELL_CURATOR_EVOCATION = 30254,
|
||||||
SPELL_AURA_FLAME_WREATH = 29946,
|
|
||||||
SPELL_ARCANE_EXPLOSION = 29973,
|
|
||||||
SPELL_WARLOCK_BANISH = 18647, // Rank 2
|
|
||||||
|
|
||||||
// Netherspite
|
// Shade of Aran
|
||||||
SPELL_GREEN_BEAM_DEBUFF = 30422,
|
SPELL_FLAME_WREATH_CAST = 30004,
|
||||||
SPELL_BLUE_BEAM_DEBUFF = 30423,
|
SPELL_FLAME_WREATH_AURA = 29946,
|
||||||
SPELL_NETHER_EXHAUSTION_RED = 38637,
|
SPELL_ARCANE_EXPLOSION = 29973,
|
||||||
SPELL_NETHER_EXHAUSTION_GREEN = 38638,
|
|
||||||
SPELL_NETHER_EXHAUSTION_BLUE = 38639,
|
|
||||||
SPELL_NETHERSPITE_BANISHED = 39833,
|
|
||||||
|
|
||||||
// Prince Malchezaar
|
// Netherspite
|
||||||
SPELL_ENFEEBLE = 30843,
|
SPELL_RED_BEAM_DEBUFF = 30421, // "Nether Portal - Perseverance" (player aura)
|
||||||
};
|
SPELL_GREEN_BEAM_DEBUFF = 30422, // "Nether Portal - Serenity" (player aura)
|
||||||
|
SPELL_BLUE_BEAM_DEBUFF = 30423, // "Nether Portal - Dominance" (player aura)
|
||||||
|
SPELL_GREEN_BEAM_HEAL = 30467, // "Nether Portal - Serenity" (Netherspite aura)
|
||||||
|
SPELL_NETHER_EXHAUSTION_RED = 38637,
|
||||||
|
SPELL_NETHER_EXHAUSTION_GREEN = 38638,
|
||||||
|
SPELL_NETHER_EXHAUSTION_BLUE = 38639,
|
||||||
|
SPELL_NETHERSPITE_BANISHED = 39833, // "Vortex Shade Black"
|
||||||
|
|
||||||
|
// Prince Malchezaar
|
||||||
|
SPELL_ENFEEBLE = 30843,
|
||||||
|
|
||||||
|
// Nightbane
|
||||||
|
SPELL_CHARRED_EARTH = 30129,
|
||||||
|
SPELL_BELLOWING_ROAR = 36922,
|
||||||
|
SPELL_RAIN_OF_BONES = 37091,
|
||||||
|
|
||||||
|
// Warlock
|
||||||
|
SPELL_WARLOCK_BANISH = 18647,
|
||||||
|
|
||||||
|
// Priest
|
||||||
|
SPELL_FEAR_WARD = 6346,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum KarazhanNPCs
|
||||||
|
{
|
||||||
|
// Trash
|
||||||
|
NPC_SPECTRAL_RETAINER = 16410,
|
||||||
|
NPC_MANA_WARP = 16530,
|
||||||
|
|
||||||
|
// Attumen the Huntsman
|
||||||
|
NPC_ATTUMEN_THE_HUNTSMAN = 15550,
|
||||||
|
NPC_ATTUMEN_THE_HUNTSMAN_MOUNTED = 16152,
|
||||||
|
|
||||||
|
// Shade of Aran
|
||||||
|
NPC_CONJURED_ELEMENTAL = 17167,
|
||||||
|
|
||||||
|
// Netherspite
|
||||||
|
NPC_VOID_ZONE = 16697,
|
||||||
|
NPC_GREEN_PORTAL = 17367, // "Nether Portal - Serenity <Healing Portal>"
|
||||||
|
NPC_BLUE_PORTAL = 17368, // "Nether Portal - Dominance <Damage Portal>"
|
||||||
|
NPC_RED_PORTAL = 17369, // "Nether Portal - Perseverance <Tanking Portal>"
|
||||||
|
|
||||||
|
// Prince Malchezaar
|
||||||
|
NPC_NETHERSPITE_INFERNAL = 17646,
|
||||||
|
};
|
||||||
|
|
||||||
|
const uint32 KARAZHAN_MAP_ID = 532;
|
||||||
|
const float NIGHTBANE_FLIGHT_Z = 95.0f;
|
||||||
|
|
||||||
enum KarazhanNpcs
|
|
||||||
{
|
|
||||||
// Attumen the Huntsman
|
// Attumen the Huntsman
|
||||||
NPC_ATTUMEN_THE_HUNTSMAN_MOUNTED = 16152,
|
extern std::unordered_map<uint32, time_t> attumenDpsWaitTimer;
|
||||||
|
// Big Bad Wolf
|
||||||
// Terestian Illhoof
|
extern std::unordered_map<ObjectGuid, uint8> bigBadWolfRunIndex;
|
||||||
NPC_KILREK = 17229,
|
|
||||||
NPC_DEMON_CHAINS = 17248,
|
|
||||||
|
|
||||||
// Shade of Aran
|
|
||||||
NPC_CONJURED_ELEMENTAL = 17167,
|
|
||||||
|
|
||||||
// Netherspite
|
// Netherspite
|
||||||
NPC_VOID_ZONE = 16697,
|
extern std::unordered_map<uint32, time_t> netherspiteDpsWaitTimer;
|
||||||
NPC_RED_PORTAL = 17369,
|
extern std::unordered_map<ObjectGuid, time_t> redBeamMoveTimer;
|
||||||
NPC_BLUE_PORTAL = 17368,
|
extern std::unordered_map<ObjectGuid, bool> lastBeamMoveSideways;
|
||||||
NPC_GREEN_PORTAL = 17367,
|
// Nightbane
|
||||||
|
extern std::unordered_map<uint32, time_t> nightbaneDpsWaitTimer;
|
||||||
|
extern std::unordered_map<ObjectGuid, uint8> nightbaneTankStep;
|
||||||
|
extern std::unordered_map<ObjectGuid, uint8> nightbaneRangedStep;
|
||||||
|
extern std::unordered_map<uint32, time_t> nightbaneFlightPhaseStartTimer;
|
||||||
|
extern std::unordered_map<ObjectGuid, bool> nightbaneRainOfBonesHit;
|
||||||
|
|
||||||
// Prince Malchezaar
|
extern const Position MAIDEN_OF_VIRTUE_BOSS_POSITION;
|
||||||
NPC_NETHERSPITE_INFERNAL = 17646,
|
extern const Position MAIDEN_OF_VIRTUE_RANGED_POSITION[8];
|
||||||
};
|
extern const Position BIG_BAD_WOLF_BOSS_POSITION;
|
||||||
|
extern const Position BIG_BAD_WOLF_RUN_POSITION[4];
|
||||||
|
extern const Position THE_CURATOR_BOSS_POSITION;
|
||||||
|
extern const Position NIGHTBANE_TRANSITION_BOSS_POSITION;
|
||||||
|
extern const Position NIGHTBANE_FINAL_BOSS_POSITION;
|
||||||
|
extern const Position NIGHTBANE_RANGED_POSITION1;
|
||||||
|
extern const Position NIGHTBANE_RANGED_POSITION2;
|
||||||
|
extern const Position NIGHTBANE_RANGED_POSITION3;
|
||||||
|
extern const Position NIGHTBANE_FLIGHT_STACK_POSITION;
|
||||||
|
extern const Position NIGHTBANE_RAIN_OF_BONES_POSITION;
|
||||||
|
|
||||||
extern const Position KARAZHAN_MAIDEN_OF_VIRTUE_BOSS_POSITION;
|
void MarkTargetWithIcon(Player* bot, Unit* target, uint8 iconId);
|
||||||
extern const Position KARAZHAN_MAIDEN_OF_VIRTUE_RANGED_POSITION[8];
|
void MarkTargetWithSkull(Player* bot, Unit* target);
|
||||||
extern const Position KARAZHAN_BIG_BAD_WOLF_BOSS_POSITION;
|
void MarkTargetWithSquare(Player* bot, Unit* target);
|
||||||
extern const Position KARAZHAN_BIG_BAD_WOLF_RUN_POSITION[4];
|
void MarkTargetWithStar(Player* bot, Unit* target);
|
||||||
extern const Position KARAZHAN_THE_CURATOR_BOSS_POSITION;
|
void MarkTargetWithCircle(Player* bot, Unit* target);
|
||||||
|
void MarkTargetWithMoon(Player* bot, Unit* target);
|
||||||
class RaidKarazhanHelpers : public AiObject
|
void SetRtiTarget(PlayerbotAI* botAI, const std::string& rtiName, Unit* target);
|
||||||
{
|
bool IsMapIDTimerManager(PlayerbotAI* botAI, Player* bot);
|
||||||
public:
|
Unit* GetFirstAliveUnit(const std::vector<Unit*>& units);
|
||||||
explicit RaidKarazhanHelpers(PlayerbotAI* botAI) : AiObject(botAI) {}
|
Unit* GetFirstAliveUnitByEntry(PlayerbotAI* botAI, uint32 entry);
|
||||||
|
Unit* GetNearestPlayerInRadius(Player* bot, float radius);
|
||||||
void MarkTargetWithSkull(Unit* /*target*/);
|
bool IsFlameWreathActive(PlayerbotAI* botAI, Player* bot);
|
||||||
Unit* GetFirstAliveUnit(const std::vector<Unit*>& /*units*/);
|
std::vector<Player*> GetRedBlockers(PlayerbotAI* botAI, Player* bot);
|
||||||
Unit* GetFirstAliveUnitByEntry(uint32 /*entry*/);
|
std::vector<Player*> GetBlueBlockers(PlayerbotAI* botAI, Player* bot);
|
||||||
Unit* GetNearestPlayerInRadius(float /*radius*/ = 5.0f);
|
std::vector<Player*> GetGreenBlockers(PlayerbotAI* botAI, Player* bot);
|
||||||
bool IsFlameWreathActive();
|
std::tuple<Player*, Player*, Player*> GetCurrentBeamBlockers(PlayerbotAI* botAI, Player* bot);
|
||||||
Position GetPositionOnBeam(Unit* boss, Unit* portal, float distanceFromBoss);
|
std::vector<Unit*> GetAllVoidZones(PlayerbotAI *botAI, Player* bot);
|
||||||
std::vector<Player*> GetRedBlockers();
|
bool IsSafePosition (float x, float y, float z, const std::vector<Unit*>& hazards, float hazardRadius);
|
||||||
std::vector<Player*> GetBlueBlockers();
|
std::vector<Unit*> GetSpawnedInfernals(PlayerbotAI* botAI);
|
||||||
std::vector<Player*> GetGreenBlockers();
|
bool IsStraightPathSafe(
|
||||||
std::tuple<Player*, Player*, Player*> GetCurrentBeamBlockers();
|
const Position& start, const Position& target,
|
||||||
std::vector<Unit*> GetAllVoidZones();
|
const std::vector<Unit*>& hazards, float hazardRadius, float stepSize);
|
||||||
bool IsSafePosition (float x, float y, float z,
|
bool TryFindSafePositionWithSafePath(
|
||||||
const std::vector<Unit*>& hazards, float hazardRadius);
|
Player* bot, float originX, float originY, float originZ, float centerX, float centerY, float centerZ,
|
||||||
std::vector<Unit*> GetSpawnedInfernals() const;
|
const std::vector<Unit*>& hazards, float safeDistance, float stepSize, uint8 numAngles,
|
||||||
bool IsStraightPathSafe(const Position& start, const Position& target,
|
float maxSampleDist, bool requireSafePath, float& bestDestX, float& bestDestY, float& bestDestZ);
|
||||||
const std::vector<Unit*>& hazards, float hazardRadius, float stepSize);
|
}
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -1,265 +1,359 @@
|
|||||||
#include "RaidKarazhanMultipliers.h"
|
#include "RaidKarazhanMultipliers.h"
|
||||||
#include "RaidKarazhanActions.h"
|
#include "RaidKarazhanActions.h"
|
||||||
#include "RaidKarazhanHelpers.h"
|
#include "RaidKarazhanHelpers.h"
|
||||||
#include "AiObjectContext.h"
|
|
||||||
#include "AttackAction.h"
|
#include "AttackAction.h"
|
||||||
#include "DruidBearActions.h"
|
#include "ChooseTargetActions.h"
|
||||||
#include "DruidCatActions.h"
|
#include "DruidActions.h"
|
||||||
|
#include "FollowActions.h"
|
||||||
|
#include "GenericActions.h"
|
||||||
|
#include "HunterActions.h"
|
||||||
|
#include "MageActions.h"
|
||||||
|
#include "Playerbots.h"
|
||||||
|
#include "PriestActions.h"
|
||||||
|
#include "ReachTargetActions.h"
|
||||||
#include "RogueActions.h"
|
#include "RogueActions.h"
|
||||||
#include "WarriorActions.h"
|
#include "ShamanActions.h"
|
||||||
|
|
||||||
static bool IsChargeAction(Action* action)
|
using namespace KarazhanHelpers;
|
||||||
{
|
|
||||||
return dynamic_cast<CastChargeAction*>(action) ||
|
|
||||||
dynamic_cast<CastInterceptAction*>(action) ||
|
|
||||||
dynamic_cast<CastFeralChargeBearAction*>(action) ||
|
|
||||||
dynamic_cast<CastFeralChargeCatAction*>(action);
|
|
||||||
}
|
|
||||||
|
|
||||||
float KarazhanAttumenTheHuntsmanMultiplier::GetValue(Action* action)
|
// Keep tanks from jumping back and forth between Attumen and Midnight
|
||||||
|
float AttumenTheHuntsmanDisableTankAssistMultiplier::GetValue(Action* action)
|
||||||
{
|
{
|
||||||
RaidKarazhanHelpers karazhanHelper(botAI);
|
Unit* midnight = AI_VALUE2(Unit*, "find target", "midnight");
|
||||||
Unit* boss = karazhanHelper.GetFirstAliveUnitByEntry(NPC_ATTUMEN_THE_HUNTSMAN_MOUNTED);
|
if (!midnight)
|
||||||
if (boss && !(botAI->IsTank(bot) && botAI->HasAggro(boss) && boss->GetVictim() == bot) &&
|
return 1.0f;
|
||||||
(dynamic_cast<MovementAction*>(action) &&
|
|
||||||
!dynamic_cast<KarazhanAttumenTheHuntsmanStackBehindAction*>(action)))
|
Unit* attumen = AI_VALUE2(Unit*, "find target", "attumen the huntsman");
|
||||||
{
|
if (!attumen)
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
if (bot->GetVictim() != nullptr && dynamic_cast<TankAssistAction*>(action))
|
||||||
return 0.0f;
|
return 0.0f;
|
||||||
|
|
||||||
|
return 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to get rid of jittering when bots are stacked behind Attumen
|
||||||
|
float AttumenTheHuntsmanStayStackedMultiplier::GetValue(Action* action)
|
||||||
|
{
|
||||||
|
Unit* attumenMounted = GetFirstAliveUnitByEntry(botAI, NPC_ATTUMEN_THE_HUNTSMAN_MOUNTED);
|
||||||
|
if (!attumenMounted)
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
if (!botAI->IsMainTank(bot) && attumenMounted->GetVictim() != bot)
|
||||||
|
{
|
||||||
|
if (dynamic_cast<CombatFormationMoveAction*>(action) ||
|
||||||
|
dynamic_cast<FleeAction*>(action) ||
|
||||||
|
dynamic_cast<CastBlinkBackAction*>(action) ||
|
||||||
|
dynamic_cast<CastDisengageAction*>(action) ||
|
||||||
|
dynamic_cast<CastReachTargetSpellAction*>(action))
|
||||||
|
return 0.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 1.0f;
|
return 1.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
float KarazhanBigBadWolfMultiplier::GetValue(Action* action)
|
// Give the main tank 8 seconds to grab aggro when Attumen mounts Midnight
|
||||||
|
// In reality it's shorter because it takes Attumen a few seconds to aggro after mounting
|
||||||
|
float AttumenTheHuntsmanWaitForDpsMultiplier::GetValue(Action* action)
|
||||||
{
|
{
|
||||||
Unit* boss = AI_VALUE2(Unit*, "find target", "the big bad wolf");
|
Unit* attumenMounted = GetFirstAliveUnitByEntry(botAI, NPC_ATTUMEN_THE_HUNTSMAN_MOUNTED);
|
||||||
if (!boss)
|
if (!attumenMounted)
|
||||||
{
|
|
||||||
return 1.0f;
|
return 1.0f;
|
||||||
}
|
|
||||||
|
|
||||||
if (bot->HasAura(SPELL_LITTLE_RED_RIDING_HOOD))
|
const time_t now = std::time(nullptr);
|
||||||
|
const uint8 dpsWaitSeconds = 8;
|
||||||
|
|
||||||
|
auto it = attumenDpsWaitTimer.find(KARAZHAN_MAP_ID);
|
||||||
|
if (it == attumenDpsWaitTimer.end() || (now - it->second) < dpsWaitSeconds)
|
||||||
{
|
{
|
||||||
if ((dynamic_cast<MovementAction*>(action) && !dynamic_cast<KarazhanBigBadWolfRunAwayAction*>(action)) ||
|
if (!botAI->IsMainTank(bot))
|
||||||
(dynamic_cast<AttackAction*>(action)))
|
|
||||||
{
|
{
|
||||||
return 0.0f;
|
if (dynamic_cast<AttackAction*>(action) || (dynamic_cast<CastSpellAction*>(action) &&
|
||||||
}
|
!dynamic_cast<CastHealingSpellAction*>(action)))
|
||||||
}
|
|
||||||
|
|
||||||
return 1.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
float KarazhanShadeOfAranMultiplier::GetValue(Action* action)
|
|
||||||
{
|
|
||||||
Unit* boss = AI_VALUE2(Unit*, "find target", "shade of aran");
|
|
||||||
if (!boss)
|
|
||||||
{
|
|
||||||
return 1.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (boss->HasUnitState(UNIT_STATE_CASTING) && boss->FindCurrentSpellBySpellId(SPELL_ARCANE_EXPLOSION))
|
|
||||||
{
|
|
||||||
if (IsChargeAction(action))
|
|
||||||
{
|
|
||||||
return 0.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dynamic_cast<MovementAction*>(action))
|
|
||||||
{
|
|
||||||
const float safeDistance = 20.0f;
|
|
||||||
if (bot->GetDistance2d(boss) >= safeDistance)
|
|
||||||
{
|
|
||||||
return 0.0f;
|
return 0.0f;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool flameWreathActive = boss->HasAura(SPELL_FLAME_WREATH);
|
return 1.0f;
|
||||||
if (!flameWreathActive && bot->GetGroup())
|
}
|
||||||
|
|
||||||
|
// The assist tank should stay on the boss to be 2nd on aggro and tank Hateful Bolts
|
||||||
|
float TheCuratorDisableTankAssistMultiplier::GetValue(Action* action)
|
||||||
|
{
|
||||||
|
Unit* curator = AI_VALUE2(Unit*, "find target", "the curator");
|
||||||
|
if (!curator)
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
if (bot->GetVictim() != nullptr && dynamic_cast<TankAssistAction*>(action))
|
||||||
|
return 0.0f;
|
||||||
|
|
||||||
|
return 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save Bloodlust/Heroism for Evocation (100% increased damage)
|
||||||
|
float TheCuratorDelayBloodlustAndHeroismMultiplier::GetValue(Action* action)
|
||||||
|
{
|
||||||
|
Unit* curator = AI_VALUE2(Unit*, "find target", "the curator");
|
||||||
|
if (!curator)
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
if (!curator->HasAura(SPELL_CURATOR_EVOCATION))
|
||||||
{
|
{
|
||||||
for (GroupReference* itr = bot->GetGroup()->GetFirstMember(); itr != nullptr; itr = itr->next())
|
if (dynamic_cast<CastBloodlustAction*>(action) ||
|
||||||
|
dynamic_cast<CastHeroismAction*>(action))
|
||||||
|
return 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't charge back in when running from Arcane Explosion
|
||||||
|
float ShadeOfAranArcaneExplosionDisableChargeMultiplier::GetValue(Action* action)
|
||||||
|
{
|
||||||
|
Unit* aran = AI_VALUE2(Unit*, "find target", "shade of aran");
|
||||||
|
if (!aran)
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
if (aran->HasUnitState(UNIT_STATE_CASTING) &&
|
||||||
|
aran->FindCurrentSpellBySpellId(SPELL_ARCANE_EXPLOSION))
|
||||||
|
{
|
||||||
|
if (dynamic_cast<CastReachTargetSpellAction*>(action))
|
||||||
|
return 0.0f;
|
||||||
|
|
||||||
|
if (bot->GetDistance2d(aran) >= 20.0f)
|
||||||
{
|
{
|
||||||
Player* member = itr->GetSource();
|
if (dynamic_cast<CombatFormationMoveAction*>(action) ||
|
||||||
if (member && member->HasAura(SPELL_AURA_FLAME_WREATH))
|
dynamic_cast<FleeAction*>(action) ||
|
||||||
{
|
dynamic_cast<FollowAction*>(action) ||
|
||||||
flameWreathActive = true;
|
dynamic_cast<ReachTargetAction*>(action) ||
|
||||||
break;
|
dynamic_cast<AvoidAoeAction*>(action))
|
||||||
}
|
return 0.0f;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (flameWreathActive)
|
|
||||||
|
return 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
// I will not move when Flame Wreath is cast or the raid blows up
|
||||||
|
float ShadeOfAranFlameWreathDisableMovementMultiplier::GetValue(Action* action)
|
||||||
|
{
|
||||||
|
Unit* aran = AI_VALUE2(Unit*, "find target", "shade of aran");
|
||||||
|
if (!aran)
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
if (IsFlameWreathActive(botAI, bot))
|
||||||
{
|
{
|
||||||
if (dynamic_cast<MovementAction*>(action) || IsChargeAction(action))
|
if (dynamic_cast<CombatFormationMoveAction*>(action) ||
|
||||||
|
dynamic_cast<FleeAction*>(action) ||
|
||||||
|
dynamic_cast<FollowAction*>(action) ||
|
||||||
|
dynamic_cast<ReachTargetAction*>(action) ||
|
||||||
|
dynamic_cast<AvoidAoeAction*>(action) ||
|
||||||
|
dynamic_cast<CastKillingSpreeAction*>(action) ||
|
||||||
|
dynamic_cast<CastBlinkBackAction*>(action) ||
|
||||||
|
dynamic_cast<CastDisengageAction*>(action) ||
|
||||||
|
dynamic_cast<CastReachTargetSpellAction*>(action))
|
||||||
|
return 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to rid of the jittering when blocking beams
|
||||||
|
float NetherspiteKeepBlockingBeamMultiplier::GetValue(Action* action)
|
||||||
|
{
|
||||||
|
Unit* netherspite = AI_VALUE2(Unit*, "find target", "netherspite");
|
||||||
|
if (!netherspite || netherspite->HasAura(SPELL_NETHERSPITE_BANISHED))
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
auto [redBlocker, greenBlocker, blueBlocker] = GetCurrentBeamBlockers(botAI, bot);
|
||||||
|
|
||||||
|
if (bot == redBlocker)
|
||||||
|
{
|
||||||
|
if (dynamic_cast<CombatFormationMoveAction*>(action))
|
||||||
|
return 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bot == blueBlocker)
|
||||||
|
{
|
||||||
|
if (dynamic_cast<CombatFormationMoveAction*>(action) ||
|
||||||
|
dynamic_cast<ReachTargetAction*>(action))
|
||||||
|
return 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bot == greenBlocker)
|
||||||
|
{
|
||||||
|
if (dynamic_cast<CombatFormationMoveAction*>(action) ||
|
||||||
|
dynamic_cast<ReachTargetAction*>(action) ||
|
||||||
|
dynamic_cast<FleeAction*>(action) ||
|
||||||
|
dynamic_cast<CastKillingSpreeAction*>(action) ||
|
||||||
|
dynamic_cast<CastReachTargetSpellAction*>(action))
|
||||||
|
return 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Give tanks 5 seconds to get aggro during phase transitions
|
||||||
|
float NetherspiteWaitForDpsMultiplier::GetValue(Action* action)
|
||||||
|
{
|
||||||
|
Unit* netherspite = AI_VALUE2(Unit*, "find target", "netherspite");
|
||||||
|
if (!netherspite || netherspite->HasAura(SPELL_NETHERSPITE_BANISHED))
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
const time_t now = std::time(nullptr);
|
||||||
|
const uint8 dpsWaitSeconds = 5;
|
||||||
|
|
||||||
|
auto it = netherspiteDpsWaitTimer.find(KARAZHAN_MAP_ID);
|
||||||
|
if (it == netherspiteDpsWaitTimer.end() || (now - it->second) < dpsWaitSeconds)
|
||||||
|
{
|
||||||
|
if (!botAI->IsTank(bot))
|
||||||
{
|
{
|
||||||
|
if (dynamic_cast<AttackAction*>(action) || (dynamic_cast<CastSpellAction*>(action) &&
|
||||||
|
!dynamic_cast<CastHealingSpellAction*>(action)))
|
||||||
return 0.0f;
|
return 0.0f;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 1.0f;
|
return 1.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
float KarazhanNetherspiteBlueAndGreenBeamMultiplier::GetValue(Action* action)
|
// Disable standard "avoid aoe" strategy, which may interfere with scripted avoidance
|
||||||
|
float PrinceMalchezaarDisableAvoidAoeMultiplier::GetValue(Action* action)
|
||||||
{
|
{
|
||||||
Unit* boss = AI_VALUE2(Unit*, "find target", "netherspite");
|
Unit* malchezaar = AI_VALUE2(Unit*, "find target", "prince malchezaar");
|
||||||
if (!boss || !boss->IsAlive())
|
if (!malchezaar)
|
||||||
{
|
|
||||||
return 1.0f;
|
return 1.0f;
|
||||||
}
|
|
||||||
|
|
||||||
if (dynamic_cast<AvoidAoeAction*>(action) || dynamic_cast<CastKillingSpreeAction*>(action))
|
|
||||||
{
|
|
||||||
return 0.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
RaidKarazhanHelpers karazhanHelper(botAI);
|
|
||||||
auto [redBlocker /*unused*/, greenBlocker, blueBlocker] = karazhanHelper.GetCurrentBeamBlockers();
|
|
||||||
bool isBlocker = (bot == greenBlocker || bot == blueBlocker);
|
|
||||||
if (isBlocker)
|
|
||||||
{
|
|
||||||
Unit* bluePortal = bot->FindNearestCreature(NPC_BLUE_PORTAL, 150.0f);
|
|
||||||
Unit* greenPortal = bot->FindNearestCreature(NPC_GREEN_PORTAL, 150.0f);
|
|
||||||
bool inBeam = false;
|
|
||||||
for (Unit* portal : {bluePortal, greenPortal})
|
|
||||||
{
|
|
||||||
if (!portal)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
float bx = boss->GetPositionX(), by = boss->GetPositionY();
|
|
||||||
float px = portal->GetPositionX(), py = portal->GetPositionY();
|
|
||||||
float dx = px - bx, dy = py - by;
|
|
||||||
float length = sqrt(dx*dx + dy*dy);
|
|
||||||
if (length == 0.0f)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
dx /= length; dy /= length;
|
|
||||||
float botdx = bot->GetPositionX() - bx, botdy = bot->GetPositionY() - by;
|
|
||||||
float t = (botdx * dx + botdy * dy);
|
|
||||||
float beamX = bx + dx * t, beamY = by + dy * t;
|
|
||||||
float distToBeam = sqrt(pow(bot->GetPositionX() - beamX, 2) + pow(bot->GetPositionY() - beamY, 2));
|
|
||||||
if (distToBeam < 0.3f && t > 0.0f && t < length)
|
|
||||||
{
|
|
||||||
inBeam = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (inBeam)
|
|
||||||
{
|
|
||||||
std::vector<Unit*> voidZones = karazhanHelper.GetAllVoidZones();
|
|
||||||
bool inVoidZone = false;
|
|
||||||
for (Unit* vz : voidZones)
|
|
||||||
{
|
|
||||||
if (bot->GetExactDist2d(vz) < 4.0f)
|
|
||||||
{
|
|
||||||
inVoidZone = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!inVoidZone)
|
|
||||||
{
|
|
||||||
if (dynamic_cast<MovementAction*>(action) || IsChargeAction(action))
|
|
||||||
{
|
|
||||||
return 0.0f;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 1.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
float KarazhanNetherspiteRedBeamMultiplier::GetValue(Action* action)
|
|
||||||
{
|
|
||||||
Unit* boss = AI_VALUE2(Unit*, "find target", "netherspite");
|
|
||||||
if (!boss || !boss->IsAlive())
|
|
||||||
{
|
|
||||||
return 1.0f;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dynamic_cast<AvoidAoeAction*>(action))
|
if (dynamic_cast<AvoidAoeAction*>(action))
|
||||||
{
|
|
||||||
return 0.0f;
|
return 0.0f;
|
||||||
|
|
||||||
|
return 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't run back into Shadow Nova when Enfeebled
|
||||||
|
float PrinceMalchezaarEnfeebleKeepDistanceMultiplier::GetValue(Action* action)
|
||||||
|
{
|
||||||
|
Unit* malchezaar = AI_VALUE2(Unit*, "find target", "prince malchezaar");
|
||||||
|
if (!malchezaar)
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
if (bot->HasAura(SPELL_ENFEEBLE))
|
||||||
|
{
|
||||||
|
if (dynamic_cast<MovementAction*>(action) &&
|
||||||
|
!dynamic_cast<PrinceMalchezaarEnfeebledAvoidHazardAction*>(action))
|
||||||
|
return 0.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
RaidKarazhanHelpers karazhanHelper(botAI);
|
return 1.0f;
|
||||||
auto [redBlocker, greenBlocker /*unused*/, blueBlocker /*unused*/] = karazhanHelper.GetCurrentBeamBlockers();
|
}
|
||||||
static std::map<ObjectGuid, uint32> beamMoveTimes;
|
|
||||||
static std::map<ObjectGuid, bool> lastBeamMoveSideways;
|
|
||||||
ObjectGuid botGuid = bot->GetGUID();
|
|
||||||
Unit* redPortal = bot->FindNearestCreature(NPC_RED_PORTAL, 150.0f);
|
|
||||||
if (bot == redBlocker && boss && redPortal)
|
|
||||||
{
|
|
||||||
Position blockingPos = karazhanHelper.GetPositionOnBeam(boss, redPortal, 18.0f);
|
|
||||||
float bx = boss->GetPositionX();
|
|
||||||
float by = boss->GetPositionY();
|
|
||||||
float px = redPortal->GetPositionX();
|
|
||||||
float py = redPortal->GetPositionY();
|
|
||||||
float dx = px - bx;
|
|
||||||
float dy = py - by;
|
|
||||||
float length = sqrt(dx*dx + dy*dy);
|
|
||||||
if (length != 0.0f)
|
|
||||||
{
|
|
||||||
dx /= length;
|
|
||||||
dy /= length;
|
|
||||||
float perpDx = -dy;
|
|
||||||
float perpDy = dx;
|
|
||||||
Position sidewaysPos(blockingPos.GetPositionX() + perpDx * 3.0f,
|
|
||||||
blockingPos.GetPositionY() + perpDy * 3.0f,
|
|
||||||
blockingPos.GetPositionZ());
|
|
||||||
|
|
||||||
uint32 intervalSecs = 5;
|
// Wait until Phase 3 to use Bloodlust/Heroism
|
||||||
if (beamMoveTimes[botGuid] == 0)
|
float PrinceMalchezaarDelayBloodlustAndHeroismMultiplier::GetValue(Action* action)
|
||||||
{
|
{
|
||||||
beamMoveTimes[botGuid] = time(nullptr);
|
Unit* malchezaar = AI_VALUE2(Unit*, "find target", "prince malchezaar");
|
||||||
lastBeamMoveSideways[botGuid] = false;
|
if (!malchezaar)
|
||||||
}
|
return 1.0f;
|
||||||
if (time(nullptr) - beamMoveTimes[botGuid] >= intervalSecs)
|
|
||||||
{
|
if (malchezaar->GetHealthPct() > 30.0f)
|
||||||
lastBeamMoveSideways[botGuid] = !lastBeamMoveSideways[botGuid];
|
{
|
||||||
beamMoveTimes[botGuid] = time(nullptr);
|
if (dynamic_cast<CastBloodlustAction*>(action) ||
|
||||||
}
|
dynamic_cast<CastHeroismAction*>(action))
|
||||||
Position targetPos = lastBeamMoveSideways[botGuid] ? sidewaysPos : blockingPos;
|
return 0.0f;
|
||||||
float distToTarget = bot->GetExactDist2d(targetPos.GetPositionX(), targetPos.GetPositionY());
|
}
|
||||||
const float positionTolerance = 1.5f;
|
|
||||||
if (distToTarget < positionTolerance)
|
return 1.0f;
|
||||||
{
|
}
|
||||||
if (dynamic_cast<MovementAction*>(action) || IsChargeAction(action))
|
|
||||||
{
|
// Pets tend to run out of bounds and cause skeletons to spawn off the map
|
||||||
return 0.0f;
|
// Pets also tend to pull adds from inside of the tower through the floor
|
||||||
}
|
// This multiplier DOES NOT impact Hunter and Warlock pets
|
||||||
}
|
// Hunter and Warlock pets are addressed in ControlPetAggressionAction
|
||||||
|
float NightbaneDisablePetsMultiplier::GetValue(Action* action)
|
||||||
|
{
|
||||||
|
Unit* nightbane = AI_VALUE2(Unit*, "find target", "nightbane");
|
||||||
|
if (!nightbane)
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
if (dynamic_cast<CastForceOfNatureAction*>(action) ||
|
||||||
|
dynamic_cast<CastFeralSpiritAction*>(action) ||
|
||||||
|
dynamic_cast<CastFireElementalTotemAction*>(action) ||
|
||||||
|
dynamic_cast<CastFireElementalTotemMeleeAction*>(action) ||
|
||||||
|
dynamic_cast<CastSummonWaterElementalAction*>(action) ||
|
||||||
|
dynamic_cast<CastShadowfiendAction*>(action))
|
||||||
|
return 0.0f;
|
||||||
|
|
||||||
|
if (nightbane->GetPositionZ() > NIGHTBANE_FLIGHT_Z)
|
||||||
|
{
|
||||||
|
if (dynamic_cast<PetAttackAction*>(action))
|
||||||
|
return 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Give the main tank 8 seconds to get aggro during phase transitions
|
||||||
|
float NightbaneWaitForDpsMultiplier::GetValue(Action* action)
|
||||||
|
{
|
||||||
|
Unit* nightbane = AI_VALUE2(Unit*, "find target", "nightbane");
|
||||||
|
if (!nightbane || nightbane->GetPositionZ() > NIGHTBANE_FLIGHT_Z)
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
const time_t now = std::time(nullptr);
|
||||||
|
const uint8 dpsWaitSeconds = 8;
|
||||||
|
|
||||||
|
auto it = nightbaneDpsWaitTimer.find(KARAZHAN_MAP_ID);
|
||||||
|
if (it == nightbaneDpsWaitTimer.end() || (now - it->second) < dpsWaitSeconds)
|
||||||
|
{
|
||||||
|
if (!botAI->IsMainTank(bot))
|
||||||
|
{
|
||||||
|
if (dynamic_cast<AttackAction*>(action) || (dynamic_cast<CastSpellAction*>(action) &&
|
||||||
|
!dynamic_cast<CastHealingSpellAction*>(action)))
|
||||||
|
return 0.0f;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 1.0f;
|
return 1.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
float KarazhanPrinceMalchezaarMultiplier::GetValue(Action* action)
|
// The "avoid aoe" strategy must be disabled for the main tank
|
||||||
|
// Otherwise, the main tank will spin Nightbane to avoid Charred Earth and wipe the raid
|
||||||
|
// It is also disabled for all bots during the flight phase
|
||||||
|
float NightbaneDisableAvoidAoeMultiplier::GetValue(Action* action)
|
||||||
{
|
{
|
||||||
Unit* boss = AI_VALUE2(Unit*, "find target", "prince malchezaar");
|
Unit* nightbane = AI_VALUE2(Unit*, "find target", "nightbane");
|
||||||
if (!boss || !boss->IsAlive())
|
if (!nightbane)
|
||||||
{
|
|
||||||
return 1.0f;
|
return 1.0f;
|
||||||
}
|
|
||||||
|
|
||||||
if (dynamic_cast<AvoidAoeAction*>(action))
|
if (nightbane->GetPositionZ() > NIGHTBANE_FLIGHT_Z || botAI->IsMainTank(bot))
|
||||||
{
|
{
|
||||||
return 0.0f;
|
if (dynamic_cast<AvoidAoeAction*>(action))
|
||||||
}
|
return 0.0f;
|
||||||
|
}
|
||||||
if (botAI->IsMelee(bot) && bot->HasAura(SPELL_ENFEEBLE) &&
|
|
||||||
!dynamic_cast<KarazhanPrinceMalchezaarNonTankAvoidHazardAction*>(action))
|
return 1.0f;
|
||||||
{
|
}
|
||||||
return 0.0f;
|
|
||||||
}
|
// Disable some movement actions that conflict with the strategies
|
||||||
|
float NightbaneDisableMovementMultiplier::GetValue(Action* action)
|
||||||
if (botAI->IsRanged(bot) && bot->HasAura(SPELL_ENFEEBLE) &&
|
{
|
||||||
(dynamic_cast<MovementAction*>(action) &&
|
Unit* nightbane = AI_VALUE2(Unit*, "find target", "nightbane");
|
||||||
!dynamic_cast<KarazhanPrinceMalchezaarNonTankAvoidHazardAction*>(action)))
|
if (!nightbane)
|
||||||
{
|
return 1.0f;
|
||||||
return 0.0f;
|
|
||||||
|
if (dynamic_cast<CastBlinkBackAction*>(action) ||
|
||||||
|
dynamic_cast<CastDisengageAction*>(action) ||
|
||||||
|
dynamic_cast<FleeAction*>(action))
|
||||||
|
return 0.0f;
|
||||||
|
|
||||||
|
// Disable CombatFormationMoveAction for all bots except:
|
||||||
|
// (1) main tank and (2) only during the ground phase, other melee
|
||||||
|
if (botAI->IsRanged(bot) ||
|
||||||
|
(botAI->IsMelee(bot) && !botAI->IsMainTank(bot) &&
|
||||||
|
nightbane->GetPositionZ() > NIGHTBANE_FLIGHT_Z))
|
||||||
|
{
|
||||||
|
if (dynamic_cast<CombatFormationMoveAction*>(action))
|
||||||
|
return 0.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 1.0f;
|
return 1.0f;
|
||||||
|
|||||||
@@ -3,45 +3,131 @@
|
|||||||
|
|
||||||
#include "Multiplier.h"
|
#include "Multiplier.h"
|
||||||
|
|
||||||
class KarazhanAttumenTheHuntsmanMultiplier : public Multiplier
|
class AttumenTheHuntsmanDisableTankAssistMultiplier : public Multiplier
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
KarazhanAttumenTheHuntsmanMultiplier(PlayerbotAI* botAI) : Multiplier(botAI, "karazhan attumen the huntsman multiplier") {}
|
AttumenTheHuntsmanDisableTankAssistMultiplier(
|
||||||
|
PlayerbotAI* botAI) : Multiplier(botAI, "attumen the huntsman disable tank assist multiplier") {}
|
||||||
virtual float GetValue(Action* action);
|
virtual float GetValue(Action* action);
|
||||||
};
|
};
|
||||||
|
|
||||||
class KarazhanBigBadWolfMultiplier : public Multiplier
|
class AttumenTheHuntsmanStayStackedMultiplier : public Multiplier
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
KarazhanBigBadWolfMultiplier(PlayerbotAI* botAI) : Multiplier(botAI, "karazhan big bad wolf multiplier") {}
|
AttumenTheHuntsmanStayStackedMultiplier(
|
||||||
|
PlayerbotAI* botAI) : Multiplier(botAI, "attumen the huntsman stay stacked multiplier") {}
|
||||||
virtual float GetValue(Action* action);
|
virtual float GetValue(Action* action);
|
||||||
};
|
};
|
||||||
|
|
||||||
class KarazhanShadeOfAranMultiplier : public Multiplier
|
class AttumenTheHuntsmanWaitForDpsMultiplier : public Multiplier
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
KarazhanShadeOfAranMultiplier(PlayerbotAI* botAI) : Multiplier(botAI, "karazhan shade of aran multiplier") {}
|
AttumenTheHuntsmanWaitForDpsMultiplier(
|
||||||
|
PlayerbotAI* botAI) : Multiplier(botAI, "attumen the huntsman wait for dps multiplier") {}
|
||||||
virtual float GetValue(Action* action);
|
virtual float GetValue(Action* action);
|
||||||
};
|
};
|
||||||
|
|
||||||
class KarazhanNetherspiteBlueAndGreenBeamMultiplier : public Multiplier
|
class TheCuratorDisableTankAssistMultiplier : public Multiplier
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
KarazhanNetherspiteBlueAndGreenBeamMultiplier(PlayerbotAI* botAI) : Multiplier(botAI, "karazhan netherspite blue and green beam multiplier") {}
|
TheCuratorDisableTankAssistMultiplier(
|
||||||
|
PlayerbotAI* botAI) : Multiplier(botAI, "the curator disable tank assist multiplier") {}
|
||||||
virtual float GetValue(Action* action);
|
virtual float GetValue(Action* action);
|
||||||
};
|
};
|
||||||
|
|
||||||
class KarazhanNetherspiteRedBeamMultiplier : public Multiplier
|
class TheCuratorDelayBloodlustAndHeroismMultiplier : public Multiplier
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
KarazhanNetherspiteRedBeamMultiplier(PlayerbotAI* botAI) : Multiplier(botAI, "karazhan netherspite red beam multiplier") {}
|
TheCuratorDelayBloodlustAndHeroismMultiplier(
|
||||||
|
PlayerbotAI* botAI) : Multiplier(botAI, "the curator delay bloodlust and heroism multiplier") {}
|
||||||
virtual float GetValue(Action* action);
|
virtual float GetValue(Action* action);
|
||||||
};
|
};
|
||||||
|
|
||||||
class KarazhanPrinceMalchezaarMultiplier : public Multiplier
|
class ShadeOfAranArcaneExplosionDisableChargeMultiplier : public Multiplier
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
KarazhanPrinceMalchezaarMultiplier(PlayerbotAI* botAI) : Multiplier(botAI, "karazhan prince malchezaar multiplier") {}
|
ShadeOfAranArcaneExplosionDisableChargeMultiplier(
|
||||||
|
PlayerbotAI* botAI) : Multiplier(botAI, "shade of aran arcane explosion disable charge multiplier") {}
|
||||||
|
virtual float GetValue(Action* action);
|
||||||
|
};
|
||||||
|
|
||||||
|
class ShadeOfAranFlameWreathDisableMovementMultiplier : public Multiplier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ShadeOfAranFlameWreathDisableMovementMultiplier(
|
||||||
|
PlayerbotAI* botAI) : Multiplier(botAI, "shade of aran flame wreath disable movement multiplier") {}
|
||||||
|
virtual float GetValue(Action* action);
|
||||||
|
};
|
||||||
|
|
||||||
|
class NetherspiteKeepBlockingBeamMultiplier : public Multiplier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
NetherspiteKeepBlockingBeamMultiplier(
|
||||||
|
PlayerbotAI* botAI) : Multiplier(botAI, "netherspite keep blocking beam multiplier") {}
|
||||||
|
virtual float GetValue(Action* action);
|
||||||
|
};
|
||||||
|
|
||||||
|
class NetherspiteWaitForDpsMultiplier : public Multiplier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
NetherspiteWaitForDpsMultiplier(
|
||||||
|
PlayerbotAI* botAI) : Multiplier(botAI, "netherspite wait for dps multiplier") {}
|
||||||
|
virtual float GetValue(Action* action);
|
||||||
|
};
|
||||||
|
|
||||||
|
class PrinceMalchezaarDisableAvoidAoeMultiplier : public Multiplier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
PrinceMalchezaarDisableAvoidAoeMultiplier(
|
||||||
|
PlayerbotAI* botAI) : Multiplier(botAI, "prince malchezaar disable avoid aoe multiplier") {}
|
||||||
|
virtual float GetValue(Action* action);
|
||||||
|
};
|
||||||
|
|
||||||
|
class PrinceMalchezaarEnfeebleKeepDistanceMultiplier : public Multiplier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
PrinceMalchezaarEnfeebleKeepDistanceMultiplier(
|
||||||
|
PlayerbotAI* botAI) : Multiplier(botAI, "prince malchezaar enfeeble keep distance multiplier") {}
|
||||||
|
virtual float GetValue(Action* action);
|
||||||
|
};
|
||||||
|
|
||||||
|
class PrinceMalchezaarDelayBloodlustAndHeroismMultiplier : public Multiplier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
PrinceMalchezaarDelayBloodlustAndHeroismMultiplier(
|
||||||
|
PlayerbotAI* botAI) : Multiplier(botAI, "prince malchezaar delay bloodlust and heroism multiplier") {}
|
||||||
|
virtual float GetValue(Action* action);
|
||||||
|
};
|
||||||
|
|
||||||
|
class NightbaneDisablePetsMultiplier : public Multiplier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
NightbaneDisablePetsMultiplier(
|
||||||
|
PlayerbotAI* botAI) : Multiplier(botAI, "nightbane disable pets multiplier") {}
|
||||||
|
virtual float GetValue(Action* action);
|
||||||
|
};
|
||||||
|
|
||||||
|
class NightbaneWaitForDpsMultiplier : public Multiplier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
NightbaneWaitForDpsMultiplier(
|
||||||
|
PlayerbotAI* botAI) : Multiplier(botAI, "nightbane wait for dps multiplier") {}
|
||||||
|
virtual float GetValue(Action* action);
|
||||||
|
};
|
||||||
|
|
||||||
|
class NightbaneDisableAvoidAoeMultiplier : public Multiplier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
NightbaneDisableAvoidAoeMultiplier(
|
||||||
|
PlayerbotAI* botAI) : Multiplier(botAI, "nightbane disable avoid aoe multiplier") {}
|
||||||
|
virtual float GetValue(Action* action);
|
||||||
|
};
|
||||||
|
|
||||||
|
class NightbaneDisableMovementMultiplier : public Multiplier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
NightbaneDisableMovementMultiplier(
|
||||||
|
PlayerbotAI* botAI) : Multiplier(botAI, "nightbane disable movement multiplier") {}
|
||||||
virtual float GetValue(Action* action);
|
virtual float GetValue(Action* action);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -3,79 +3,160 @@
|
|||||||
|
|
||||||
void RaidKarazhanStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
void RaidKarazhanStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||||
{
|
{
|
||||||
triggers.push_back(new TriggerNode(
|
// Trash
|
||||||
"karazhan attumen the huntsman", NextAction::array(0,
|
triggers.push_back(new TriggerNode("mana warp is about to explode",
|
||||||
new NextAction("karazhan attumen the huntsman stack behind", ACTION_RAID + 1),
|
NextAction::array(0, new NextAction("mana warp stun creature before warp breach", ACTION_EMERGENCY + 6), nullptr)
|
||||||
nullptr)));
|
));
|
||||||
|
|
||||||
triggers.push_back(new TriggerNode(
|
// Attumen the Huntsman
|
||||||
"karazhan moroes", NextAction::array(0,
|
triggers.push_back(new TriggerNode("attumen the huntsman need target priority",
|
||||||
new NextAction("karazhan moroes mark target", ACTION_RAID + 1),
|
NextAction::array(0, new NextAction("attumen the huntsman mark target", ACTION_RAID + 1), nullptr)
|
||||||
nullptr)));
|
));
|
||||||
|
triggers.push_back(new TriggerNode("attumen the huntsman attumen spawned",
|
||||||
|
NextAction::array(0, new NextAction("attumen the huntsman split bosses", ACTION_RAID + 2), nullptr)
|
||||||
|
));
|
||||||
|
triggers.push_back(new TriggerNode("attumen the huntsman attumen is mounted",
|
||||||
|
NextAction::array(0, new NextAction("attumen the huntsman stack behind", ACTION_RAID + 1), nullptr)
|
||||||
|
));
|
||||||
|
triggers.push_back(new TriggerNode("attumen the huntsman boss wipes aggro when mounting",
|
||||||
|
NextAction::array(0, new NextAction("attumen the huntsman manage dps timer", ACTION_RAID + 2), nullptr)
|
||||||
|
));
|
||||||
|
|
||||||
triggers.push_back(new TriggerNode(
|
// Moroes
|
||||||
"karazhan maiden of virtue", NextAction::array(0,
|
triggers.push_back(new TriggerNode("moroes boss engaged by main tank",
|
||||||
new NextAction("karazhan maiden of virtue position ranged", ACTION_RAID + 1),
|
NextAction::array(0, new NextAction("moroes main tank attack boss", ACTION_RAID + 1), nullptr)
|
||||||
new NextAction("karazhan maiden of virtue position boss", ACTION_RAID + 1),
|
));
|
||||||
nullptr)));
|
triggers.push_back(new TriggerNode("moroes need target priority",
|
||||||
|
NextAction::array(0, new NextAction("moroes mark target", ACTION_RAID + 1), nullptr)
|
||||||
|
));
|
||||||
|
|
||||||
triggers.push_back(new TriggerNode(
|
// Maiden of Virtue
|
||||||
"karazhan big bad wolf", NextAction::array(0,
|
triggers.push_back(new TriggerNode("maiden of virtue healers are stunned by repentance",
|
||||||
new NextAction("karazhan big bad wolf run away", ACTION_EMERGENCY + 6),
|
NextAction::array(0, new NextAction("maiden of virtue move boss to healer", ACTION_RAID + 1), nullptr)
|
||||||
new NextAction("karazhan big bad wolf position boss", ACTION_RAID + 1),
|
));
|
||||||
nullptr)));
|
triggers.push_back(new TriggerNode("maiden of virtue holy wrath deals chain damage",
|
||||||
|
NextAction::array(0, new NextAction("maiden of virtue position ranged", ACTION_RAID + 1), nullptr)
|
||||||
|
));
|
||||||
|
|
||||||
triggers.push_back(new TriggerNode(
|
// The Big Bad Wolf
|
||||||
"karazhan romulo and julianne", NextAction::array(0,
|
triggers.push_back(new TriggerNode("big bad wolf boss is chasing little red riding hood",
|
||||||
new NextAction("karazhan romulo and julianne mark target", ACTION_RAID + 1),
|
NextAction::array(0, new NextAction("big bad wolf run away from boss", ACTION_EMERGENCY + 6), nullptr)
|
||||||
nullptr)));
|
));
|
||||||
|
triggers.push_back(new TriggerNode("big bad wolf boss engaged by tank",
|
||||||
|
NextAction::array(0, new NextAction("big bad wolf position boss", ACTION_RAID + 1), nullptr)
|
||||||
|
));
|
||||||
|
|
||||||
triggers.push_back(new TriggerNode(
|
// Romulo and Julianne
|
||||||
"karazhan wizard of oz", NextAction::array(0,
|
triggers.push_back(new TriggerNode("romulo and julianne both bosses revived",
|
||||||
new NextAction("karazhan wizard of oz scorch strawman", ACTION_RAID + 2),
|
NextAction::array(0, new NextAction("romulo and julianne mark target", ACTION_RAID + 1), nullptr)
|
||||||
new NextAction("karazhan wizard of oz mark target", ACTION_RAID + 1),
|
));
|
||||||
nullptr)));
|
|
||||||
|
|
||||||
triggers.push_back(new TriggerNode(
|
// The Wizard of Oz
|
||||||
"karazhan the curator", NextAction::array(0,
|
triggers.push_back(new TriggerNode("wizard of oz need target priority",
|
||||||
new NextAction("karazhan the curator spread ranged", ACTION_RAID + 2),
|
NextAction::array(0, new NextAction("wizard of oz mark target", ACTION_RAID + 1), nullptr)
|
||||||
new NextAction("karazhan the curator position boss", ACTION_RAID + 2),
|
));
|
||||||
new NextAction("karazhan the curator mark target", ACTION_RAID + 1),
|
triggers.push_back(new TriggerNode("wizard of oz strawman is vulnerable to fire",
|
||||||
nullptr)));
|
NextAction::array(0, new NextAction("wizard of oz scorch strawman", ACTION_RAID + 2), nullptr)
|
||||||
|
));
|
||||||
|
|
||||||
triggers.push_back(new TriggerNode(
|
// The Curator
|
||||||
"karazhan terestian illhoof", NextAction::array(0,
|
triggers.push_back(new TriggerNode("the curator astral flare spawned",
|
||||||
new NextAction("karazhan terestian illhoof mark target", ACTION_RAID + 1),
|
NextAction::array(0, new NextAction("the curator mark astral flare", ACTION_RAID + 1), nullptr)
|
||||||
nullptr)));
|
));
|
||||||
|
triggers.push_back(new TriggerNode("the curator boss engaged by tanks",
|
||||||
|
NextAction::array(0, new NextAction("the curator position boss", ACTION_RAID + 2), nullptr)
|
||||||
|
));
|
||||||
|
triggers.push_back(new TriggerNode("the curator astral flares cast arcing sear",
|
||||||
|
NextAction::array(0, new NextAction("the curator spread ranged", ACTION_RAID + 2), nullptr)
|
||||||
|
));
|
||||||
|
|
||||||
triggers.push_back(new TriggerNode(
|
// Terestian Illhoof
|
||||||
"karazhan shade of aran", NextAction::array(0,
|
triggers.push_back(new TriggerNode("terestian illhoof need target priority",
|
||||||
new NextAction("karazhan shade of aran flame wreath stop movement", ACTION_EMERGENCY + 7),
|
NextAction::array(0, new NextAction("terestian illhoof mark target", ACTION_RAID + 1), nullptr)
|
||||||
new NextAction("karazhan shade of aran arcane explosion run away", ACTION_EMERGENCY + 6),
|
));
|
||||||
new NextAction("karazhan shade of aran spread ranged", ACTION_RAID + 2),
|
|
||||||
new NextAction("karazhan shade of aran mark conjured elemental", ACTION_RAID + 1),
|
|
||||||
nullptr)));
|
|
||||||
|
|
||||||
triggers.push_back(new TriggerNode(
|
// Shade of Aran
|
||||||
"karazhan netherspite", NextAction::array(0,
|
triggers.push_back(new TriggerNode("shade of aran arcane explosion is casting",
|
||||||
new NextAction("karazhan netherspite block red beam", ACTION_EMERGENCY + 8),
|
NextAction::array(0, new NextAction("shade of aran run away from arcane explosion", ACTION_EMERGENCY + 6), nullptr)
|
||||||
new NextAction("karazhan netherspite block blue beam", ACTION_EMERGENCY + 8),
|
));
|
||||||
new NextAction("karazhan netherspite block green beam", ACTION_EMERGENCY + 8),
|
triggers.push_back(new TriggerNode("shade of aran flame wreath is active",
|
||||||
new NextAction("karazhan netherspite avoid beam and void zone", ACTION_EMERGENCY + 7),
|
NextAction::array(0, new NextAction("shade of aran stop moving during flame wreath", ACTION_EMERGENCY + 7), nullptr)
|
||||||
new NextAction("karazhan netherspite banish phase avoid void zone", ACTION_RAID + 1),
|
));
|
||||||
nullptr)));
|
triggers.push_back(new TriggerNode("shade of aran conjured elementals summoned",
|
||||||
|
NextAction::array(0, new NextAction("shade of aran mark conjured elemental", ACTION_RAID + 1), nullptr)
|
||||||
|
));
|
||||||
|
triggers.push_back(new TriggerNode("shade of aran boss uses counterspell and blizzard",
|
||||||
|
NextAction::array(0, new NextAction("shade of aran ranged maintain distance", ACTION_RAID + 2), nullptr)
|
||||||
|
));
|
||||||
|
|
||||||
triggers.push_back(new TriggerNode(
|
// Netherspite
|
||||||
"karazhan prince malchezaar", NextAction::array(0,
|
triggers.push_back(new TriggerNode("netherspite red beam is active",
|
||||||
new NextAction("karazhan prince malchezaar non tank avoid hazard", ACTION_EMERGENCY + 6),
|
NextAction::array(0, new NextAction("netherspite block red beam", ACTION_EMERGENCY + 8), nullptr)
|
||||||
new NextAction("karazhan prince malchezaar tank avoid hazard", ACTION_EMERGENCY + 6),
|
));
|
||||||
nullptr)));
|
triggers.push_back(new TriggerNode("netherspite blue beam is active",
|
||||||
|
NextAction::array(0, new NextAction("netherspite block blue beam", ACTION_EMERGENCY + 8), nullptr)
|
||||||
|
));
|
||||||
|
triggers.push_back(new TriggerNode("netherspite green beam is active",
|
||||||
|
NextAction::array(0, new NextAction("netherspite block green beam", ACTION_EMERGENCY + 8), nullptr)
|
||||||
|
));
|
||||||
|
triggers.push_back(new TriggerNode("netherspite bot is not beam blocker",
|
||||||
|
NextAction::array(0, new NextAction("netherspite avoid beam and void zone", ACTION_EMERGENCY + 7), nullptr)
|
||||||
|
));
|
||||||
|
triggers.push_back(new TriggerNode("netherspite boss is banished",
|
||||||
|
NextAction::array(0, new NextAction("netherspite banish phase avoid void zone", ACTION_RAID + 1), nullptr)
|
||||||
|
));
|
||||||
|
triggers.push_back(new TriggerNode("netherspite need to manage timers and trackers",
|
||||||
|
NextAction::array(0, new NextAction("netherspite manage timers and trackers", ACTION_EMERGENCY + 10), nullptr)
|
||||||
|
));
|
||||||
|
|
||||||
|
// Prince Malchezaar
|
||||||
|
triggers.push_back(new TriggerNode("prince malchezaar bot is enfeebled",
|
||||||
|
NextAction::array(0, new NextAction("prince malchezaar enfeebled avoid hazard", ACTION_EMERGENCY + 6), nullptr)
|
||||||
|
));
|
||||||
|
triggers.push_back(new TriggerNode("prince malchezaar infernals are spawned",
|
||||||
|
NextAction::array(0, new NextAction("prince malchezaar non tank avoid infernal", ACTION_EMERGENCY + 1), nullptr)
|
||||||
|
));
|
||||||
|
triggers.push_back(new TriggerNode("prince malchezaar boss engaged by main tank",
|
||||||
|
NextAction::array(0, new NextAction("prince malchezaar main tank movement", ACTION_EMERGENCY + 6), nullptr)
|
||||||
|
));
|
||||||
|
|
||||||
|
// Nightbane
|
||||||
|
triggers.push_back(new TriggerNode("nightbane boss engaged by main tank",
|
||||||
|
NextAction::array(0, new NextAction("nightbane ground phase position boss", ACTION_RAID + 1), nullptr)
|
||||||
|
));
|
||||||
|
triggers.push_back(new TriggerNode("nightbane ranged bots are in charred earth",
|
||||||
|
NextAction::array(0, new NextAction("nightbane ground phase rotate ranged positions", ACTION_EMERGENCY + 1), nullptr)
|
||||||
|
));
|
||||||
|
triggers.push_back(new TriggerNode("nightbane main tank is susceptible to fear",
|
||||||
|
NextAction::array(0, new NextAction("nightbane cast fear ward on main tank", ACTION_RAID + 2), nullptr)
|
||||||
|
));
|
||||||
|
triggers.push_back(new TriggerNode("nightbane pets ignore collision to chase flying boss",
|
||||||
|
NextAction::array(0, new NextAction("nightbane control pet aggression", ACTION_RAID + 2), nullptr)
|
||||||
|
));
|
||||||
|
triggers.push_back(new TriggerNode("nightbane boss is flying",
|
||||||
|
NextAction::array(0, new NextAction("nightbane flight phase movement", ACTION_RAID + 1), nullptr)
|
||||||
|
));
|
||||||
|
triggers.push_back(new TriggerNode("nightbane need to manage timers and trackers",
|
||||||
|
NextAction::array(0, new NextAction("nightbane manage timers and trackers", ACTION_EMERGENCY + 10), nullptr)
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
void RaidKarazhanStrategy::InitMultipliers(std::vector<Multiplier*>& multipliers)
|
void RaidKarazhanStrategy::InitMultipliers(std::vector<Multiplier*>& multipliers)
|
||||||
{
|
{
|
||||||
multipliers.push_back(new KarazhanShadeOfAranMultiplier(botAI));
|
multipliers.push_back(new AttumenTheHuntsmanDisableTankAssistMultiplier(botAI));
|
||||||
multipliers.push_back(new KarazhanNetherspiteBlueAndGreenBeamMultiplier(botAI));
|
multipliers.push_back(new AttumenTheHuntsmanStayStackedMultiplier(botAI));
|
||||||
multipliers.push_back(new KarazhanNetherspiteRedBeamMultiplier(botAI));
|
multipliers.push_back(new AttumenTheHuntsmanWaitForDpsMultiplier(botAI));
|
||||||
multipliers.push_back(new KarazhanPrinceMalchezaarMultiplier(botAI));
|
multipliers.push_back(new TheCuratorDisableTankAssistMultiplier(botAI));
|
||||||
|
multipliers.push_back(new TheCuratorDelayBloodlustAndHeroismMultiplier(botAI));
|
||||||
|
multipliers.push_back(new ShadeOfAranArcaneExplosionDisableChargeMultiplier(botAI));
|
||||||
|
multipliers.push_back(new ShadeOfAranFlameWreathDisableMovementMultiplier(botAI));
|
||||||
|
multipliers.push_back(new NetherspiteKeepBlockingBeamMultiplier(botAI));
|
||||||
|
multipliers.push_back(new NetherspiteWaitForDpsMultiplier(botAI));
|
||||||
|
multipliers.push_back(new PrinceMalchezaarDisableAvoidAoeMultiplier(botAI));
|
||||||
|
multipliers.push_back(new PrinceMalchezaarEnfeebleKeepDistanceMultiplier(botAI));
|
||||||
|
multipliers.push_back(new PrinceMalchezaarDelayBloodlustAndHeroismMultiplier(botAI));
|
||||||
|
multipliers.push_back(new NightbaneDisablePetsMultiplier(botAI));
|
||||||
|
multipliers.push_back(new NightbaneWaitForDpsMultiplier(botAI));
|
||||||
|
multipliers.push_back(new NightbaneDisableAvoidAoeMultiplier(botAI));
|
||||||
|
multipliers.push_back(new NightbaneDisableMovementMultiplier(botAI));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
class RaidKarazhanStrategy : public Strategy
|
class RaidKarazhanStrategy : public Strategy
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
RaidKarazhanStrategy(PlayerbotAI* ai) : Strategy(ai) {}
|
RaidKarazhanStrategy(PlayerbotAI* botAI) : Strategy(botAI) {}
|
||||||
|
|
||||||
std::string const getName() override { return "karazhan"; }
|
std::string const getName() override { return "karazhan"; }
|
||||||
|
|
||||||
|
|||||||
@@ -9,31 +9,255 @@ class RaidKarazhanTriggerContext : public NamedObjectContext<Trigger>
|
|||||||
public:
|
public:
|
||||||
RaidKarazhanTriggerContext()
|
RaidKarazhanTriggerContext()
|
||||||
{
|
{
|
||||||
creators["karazhan attumen the huntsman"] = &RaidKarazhanTriggerContext::karazhan_attumen_the_huntsman;
|
// Trash
|
||||||
creators["karazhan moroes"] = &RaidKarazhanTriggerContext::karazhan_moroes;
|
creators["mana warp is about to explode"] =
|
||||||
creators["karazhan maiden of virtue"] = &RaidKarazhanTriggerContext::karazhan_maiden_of_virtue;
|
&RaidKarazhanTriggerContext::mana_warp_is_about_to_explode;
|
||||||
creators["karazhan big bad wolf"] = &RaidKarazhanTriggerContext::karazhan_big_bad_wolf;
|
|
||||||
creators["karazhan romulo and julianne"] = &RaidKarazhanTriggerContext::karazhan_romulo_and_julianne;
|
// Attumen the Huntsman
|
||||||
creators["karazhan wizard of oz"] = &RaidKarazhanTriggerContext::karazhan_wizard_of_oz;
|
creators["attumen the huntsman need target priority"] =
|
||||||
creators["karazhan the curator"] = &RaidKarazhanTriggerContext::karazhan_the_curator;
|
&RaidKarazhanTriggerContext::attumen_the_huntsman_need_target_priority;
|
||||||
creators["karazhan terestian illhoof"] = &RaidKarazhanTriggerContext::karazhan_terestian_illhoof;
|
|
||||||
creators["karazhan shade of aran"] = &RaidKarazhanTriggerContext::karazhan_shade_of_aran;
|
creators["attumen the huntsman attumen spawned"] =
|
||||||
creators["karazhan netherspite"] = &RaidKarazhanTriggerContext::karazhan_netherspite;
|
&RaidKarazhanTriggerContext::attumen_the_huntsman_attumen_spawned;
|
||||||
creators["karazhan prince malchezaar"] = &RaidKarazhanTriggerContext::karazhan_prince_malchezaar;
|
|
||||||
|
creators["attumen the huntsman attumen is mounted"] =
|
||||||
|
&RaidKarazhanTriggerContext::attumen_the_huntsman_attumen_is_mounted;
|
||||||
|
|
||||||
|
creators["attumen the huntsman boss wipes aggro when mounting"] =
|
||||||
|
&RaidKarazhanTriggerContext::attumen_the_huntsman_boss_wipes_aggro_when_mounting;
|
||||||
|
|
||||||
|
// Moroes
|
||||||
|
creators["moroes boss engaged by main tank"] =
|
||||||
|
&RaidKarazhanTriggerContext::moroes_boss_engaged_by_main_tank;
|
||||||
|
|
||||||
|
creators["moroes need target priority"] =
|
||||||
|
&RaidKarazhanTriggerContext::moroes_need_target_priority;
|
||||||
|
|
||||||
|
// Maiden of Virtue
|
||||||
|
creators["maiden of virtue healers are stunned by repentance"] =
|
||||||
|
&RaidKarazhanTriggerContext::maiden_of_virtue_healers_are_stunned_by_repentance;
|
||||||
|
|
||||||
|
creators["maiden of virtue holy wrath deals chain damage"] =
|
||||||
|
&RaidKarazhanTriggerContext::maiden_of_virtue_holy_wrath_deals_chain_damage;
|
||||||
|
|
||||||
|
// The Big Bad Wolf
|
||||||
|
creators["big bad wolf boss engaged by tank"] =
|
||||||
|
&RaidKarazhanTriggerContext::big_bad_wolf_boss_engaged_by_tank;
|
||||||
|
|
||||||
|
creators["big bad wolf boss is chasing little red riding hood"] =
|
||||||
|
&RaidKarazhanTriggerContext::big_bad_wolf_boss_is_chasing_little_red_riding_hood;
|
||||||
|
|
||||||
|
// Romulo and Julianne
|
||||||
|
creators["romulo and julianne both bosses revived"] =
|
||||||
|
&RaidKarazhanTriggerContext::romulo_and_julianne_both_bosses_revived;
|
||||||
|
|
||||||
|
// The Wizard of Oz
|
||||||
|
creators["wizard of oz need target priority"] =
|
||||||
|
&RaidKarazhanTriggerContext::wizard_of_oz_need_target_priority;
|
||||||
|
|
||||||
|
creators["wizard of oz strawman is vulnerable to fire"] =
|
||||||
|
&RaidKarazhanTriggerContext::wizard_of_oz_strawman_is_vulnerable_to_fire;
|
||||||
|
|
||||||
|
// The Curator
|
||||||
|
creators["the curator astral flare spawned"] =
|
||||||
|
&RaidKarazhanTriggerContext::the_curator_astral_flare_spawned;
|
||||||
|
|
||||||
|
creators["the curator boss engaged by tanks"] =
|
||||||
|
&RaidKarazhanTriggerContext::the_curator_boss_engaged_by_tanks;
|
||||||
|
|
||||||
|
creators["the curator astral flares cast arcing sear"] =
|
||||||
|
&RaidKarazhanTriggerContext::the_curator_astral_flares_cast_arcing_sear;
|
||||||
|
|
||||||
|
// Terestian Illhoof
|
||||||
|
creators["terestian illhoof need target priority"] =
|
||||||
|
&RaidKarazhanTriggerContext::terestian_illhoof_need_target_priority;
|
||||||
|
|
||||||
|
// Shade of Aran
|
||||||
|
creators["shade of aran arcane explosion is casting"] =
|
||||||
|
&RaidKarazhanTriggerContext::shade_of_aran_arcane_explosion_is_casting;
|
||||||
|
|
||||||
|
creators["shade of aran flame wreath is active"] =
|
||||||
|
&RaidKarazhanTriggerContext::shade_of_aran_flame_wreath_is_active;
|
||||||
|
|
||||||
|
creators["shade of aran conjured elementals summoned"] =
|
||||||
|
&RaidKarazhanTriggerContext::shade_of_aran_conjured_elementals_summoned;
|
||||||
|
|
||||||
|
creators["shade of aran boss uses counterspell and blizzard"] =
|
||||||
|
&RaidKarazhanTriggerContext::shade_of_aran_boss_uses_counterspell_and_blizzard;
|
||||||
|
|
||||||
|
// Netherspite
|
||||||
|
creators["netherspite red beam is active"] =
|
||||||
|
&RaidKarazhanTriggerContext::netherspite_red_beam_is_active;
|
||||||
|
|
||||||
|
creators["netherspite blue beam is active"] =
|
||||||
|
&RaidKarazhanTriggerContext::netherspite_blue_beam_is_active;
|
||||||
|
|
||||||
|
creators["netherspite green beam is active"] =
|
||||||
|
&RaidKarazhanTriggerContext::netherspite_green_beam_is_active;
|
||||||
|
|
||||||
|
creators["netherspite bot is not beam blocker"] =
|
||||||
|
&RaidKarazhanTriggerContext::netherspite_bot_is_not_beam_blocker;
|
||||||
|
|
||||||
|
creators["netherspite boss is banished"] =
|
||||||
|
&RaidKarazhanTriggerContext::netherspite_boss_is_banished;
|
||||||
|
|
||||||
|
creators["netherspite need to manage timers and trackers"] =
|
||||||
|
&RaidKarazhanTriggerContext::netherspite_need_to_manage_timers_and_trackers;
|
||||||
|
|
||||||
|
// Prince Malchezaar
|
||||||
|
creators["prince malchezaar bot is enfeebled"] =
|
||||||
|
&RaidKarazhanTriggerContext::prince_malchezaar_bot_is_enfeebled;
|
||||||
|
|
||||||
|
creators["prince malchezaar infernals are spawned"] =
|
||||||
|
&RaidKarazhanTriggerContext::prince_malchezaar_infernals_are_spawned;
|
||||||
|
|
||||||
|
creators["prince malchezaar boss engaged by main tank"] =
|
||||||
|
&RaidKarazhanTriggerContext::prince_malchezaar_boss_engaged_by_main_tank;
|
||||||
|
|
||||||
|
// Nightbane
|
||||||
|
creators["nightbane boss engaged by main tank"] =
|
||||||
|
&RaidKarazhanTriggerContext::nightbane_boss_engaged_by_main_tank;
|
||||||
|
|
||||||
|
creators["nightbane ranged bots are in charred earth"] =
|
||||||
|
&RaidKarazhanTriggerContext::nightbane_ranged_bots_are_in_charred_earth;
|
||||||
|
|
||||||
|
creators["nightbane main tank is susceptible to fear"] =
|
||||||
|
&RaidKarazhanTriggerContext::nightbane_main_tank_is_susceptible_to_fear;
|
||||||
|
|
||||||
|
creators["nightbane pets ignore collision to chase flying boss"] =
|
||||||
|
&RaidKarazhanTriggerContext::nightbane_pets_ignore_collision_to_chase_flying_boss;
|
||||||
|
|
||||||
|
creators["nightbane boss is flying"] =
|
||||||
|
&RaidKarazhanTriggerContext::nightbane_boss_is_flying;
|
||||||
|
|
||||||
|
creators["nightbane need to manage timers and trackers"] =
|
||||||
|
&RaidKarazhanTriggerContext::nightbane_need_to_manage_timers_and_trackers;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static Trigger* karazhan_attumen_the_huntsman(PlayerbotAI* botAI) { return new KarazhanAttumenTheHuntsmanTrigger(botAI); }
|
// Trash
|
||||||
static Trigger* karazhan_moroes(PlayerbotAI* botAI) { return new KarazhanMoroesTrigger(botAI); }
|
static Trigger* mana_warp_is_about_to_explode(
|
||||||
static Trigger* karazhan_maiden_of_virtue(PlayerbotAI* botAI) { return new KarazhanMaidenOfVirtueTrigger(botAI); }
|
PlayerbotAI* botAI) { return new ManaWarpIsAboutToExplodeTrigger(botAI); }
|
||||||
static Trigger* karazhan_big_bad_wolf(PlayerbotAI* botAI) { return new KarazhanBigBadWolfTrigger(botAI); }
|
|
||||||
static Trigger* karazhan_romulo_and_julianne(PlayerbotAI* botAI) { return new KarazhanRomuloAndJulianneTrigger(botAI); }
|
// Attumen the Huntsman
|
||||||
static Trigger* karazhan_wizard_of_oz(PlayerbotAI* botAI) { return new KarazhanWizardOfOzTrigger(botAI); }
|
static Trigger* attumen_the_huntsman_need_target_priority(
|
||||||
static Trigger* karazhan_the_curator(PlayerbotAI* botAI) { return new KarazhanTheCuratorTrigger(botAI); }
|
PlayerbotAI* botAI) { return new AttumenTheHuntsmanNeedTargetPriorityTrigger(botAI); }
|
||||||
static Trigger* karazhan_terestian_illhoof(PlayerbotAI* botAI) { return new KarazhanTerestianIllhoofTrigger(botAI); }
|
|
||||||
static Trigger* karazhan_shade_of_aran(PlayerbotAI* botAI) { return new KarazhanShadeOfAranTrigger(botAI); }
|
static Trigger* attumen_the_huntsman_attumen_spawned(
|
||||||
static Trigger* karazhan_netherspite(PlayerbotAI* botAI) { return new KarazhanNetherspiteTrigger(botAI); }
|
PlayerbotAI* botAI) { return new AttumenTheHuntsmanAttumenSpawnedTrigger(botAI); }
|
||||||
static Trigger* karazhan_prince_malchezaar(PlayerbotAI* botAI) { return new KarazhanPrinceMalchezaarTrigger(botAI); }
|
|
||||||
|
static Trigger* attumen_the_huntsman_attumen_is_mounted(
|
||||||
|
PlayerbotAI* botAI) { return new AttumenTheHuntsmanAttumenIsMountedTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* attumen_the_huntsman_boss_wipes_aggro_when_mounting(
|
||||||
|
PlayerbotAI* botAI) { return new AttumenTheHuntsmanBossWipesAggroWhenMountingTrigger(botAI); }
|
||||||
|
|
||||||
|
// Moroes
|
||||||
|
static Trigger* moroes_boss_engaged_by_main_tank(
|
||||||
|
PlayerbotAI* botAI) { return new MoroesBossEngagedByMainTankTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* moroes_need_target_priority(
|
||||||
|
PlayerbotAI* botAI) { return new MoroesNeedTargetPriorityTrigger(botAI); }
|
||||||
|
|
||||||
|
// Maiden of Virtue
|
||||||
|
static Trigger* maiden_of_virtue_healers_are_stunned_by_repentance(
|
||||||
|
PlayerbotAI* botAI) { return new MaidenOfVirtueHealersAreStunnedByRepentanceTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* maiden_of_virtue_holy_wrath_deals_chain_damage(
|
||||||
|
PlayerbotAI* botAI) { return new MaidenOfVirtueHolyWrathDealsChainDamageTrigger(botAI); }
|
||||||
|
|
||||||
|
// The Big Bad Wolf
|
||||||
|
static Trigger* big_bad_wolf_boss_engaged_by_tank(
|
||||||
|
PlayerbotAI* botAI) { return new BigBadWolfBossEngagedByTankTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* big_bad_wolf_boss_is_chasing_little_red_riding_hood(
|
||||||
|
PlayerbotAI* botAI) { return new BigBadWolfBossIsChasingLittleRedRidingHoodTrigger(botAI); }
|
||||||
|
|
||||||
|
// Romulo and Julianne
|
||||||
|
static Trigger* romulo_and_julianne_both_bosses_revived(
|
||||||
|
PlayerbotAI* botAI) { return new RomuloAndJulianneBothBossesRevivedTrigger(botAI); }
|
||||||
|
|
||||||
|
// The Wizard of Oz
|
||||||
|
static Trigger* wizard_of_oz_need_target_priority(
|
||||||
|
PlayerbotAI* botAI) { return new WizardOfOzNeedTargetPriorityTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* wizard_of_oz_strawman_is_vulnerable_to_fire(
|
||||||
|
PlayerbotAI* botAI) { return new WizardOfOzStrawmanIsVulnerableToFireTrigger(botAI); }
|
||||||
|
|
||||||
|
// The Curator
|
||||||
|
static Trigger* the_curator_astral_flare_spawned(
|
||||||
|
PlayerbotAI* botAI) { return new TheCuratorAstralFlareSpawnedTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* the_curator_boss_engaged_by_tanks(
|
||||||
|
PlayerbotAI* botAI) { return new TheCuratorBossEngagedByTanksTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* the_curator_astral_flares_cast_arcing_sear(
|
||||||
|
PlayerbotAI* botAI) { return new TheCuratorBossAstralFlaresCastArcingSearTrigger(botAI); }
|
||||||
|
|
||||||
|
// Terestian Illhoof
|
||||||
|
static Trigger* terestian_illhoof_need_target_priority(
|
||||||
|
PlayerbotAI* botAI) { return new TerestianIllhoofNeedTargetPriorityTrigger(botAI); }
|
||||||
|
|
||||||
|
// Shade of Aran
|
||||||
|
static Trigger* shade_of_aran_arcane_explosion_is_casting(
|
||||||
|
PlayerbotAI* botAI) { return new ShadeOfAranArcaneExplosionIsCastingTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* shade_of_aran_flame_wreath_is_active(
|
||||||
|
PlayerbotAI* botAI) { return new ShadeOfAranFlameWreathIsActiveTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* shade_of_aran_conjured_elementals_summoned(
|
||||||
|
PlayerbotAI* botAI) { return new ShadeOfAranConjuredElementalsSummonedTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* shade_of_aran_boss_uses_counterspell_and_blizzard(
|
||||||
|
PlayerbotAI* botAI) { return new ShadeOfAranBossUsesCounterspellAndBlizzardTrigger(botAI); }
|
||||||
|
|
||||||
|
// Netherspite
|
||||||
|
static Trigger* netherspite_red_beam_is_active(
|
||||||
|
PlayerbotAI* botAI) { return new NetherspiteRedBeamIsActiveTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* netherspite_blue_beam_is_active(
|
||||||
|
PlayerbotAI* botAI) { return new NetherspiteBlueBeamIsActiveTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* netherspite_green_beam_is_active(
|
||||||
|
PlayerbotAI* botAI) { return new NetherspiteGreenBeamIsActiveTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* netherspite_bot_is_not_beam_blocker(
|
||||||
|
PlayerbotAI* botAI) { return new NetherspiteBotIsNotBeamBlockerTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* netherspite_boss_is_banished(
|
||||||
|
PlayerbotAI* botAI) { return new NetherspiteBossIsBanishedTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* netherspite_need_to_manage_timers_and_trackers(
|
||||||
|
PlayerbotAI* botAI) { return new NetherspiteNeedToManageTimersAndTrackersTrigger(botAI); }
|
||||||
|
|
||||||
|
// Prince Malchezaar
|
||||||
|
static Trigger* prince_malchezaar_bot_is_enfeebled(
|
||||||
|
PlayerbotAI* botAI) { return new PrinceMalchezaarBotIsEnfeebledTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* prince_malchezaar_infernals_are_spawned(
|
||||||
|
PlayerbotAI* botAI) { return new PrinceMalchezaarInfernalsAreSpawnedTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* prince_malchezaar_boss_engaged_by_main_tank(
|
||||||
|
PlayerbotAI* botAI) { return new PrinceMalchezaarBossEngagedByMainTankTrigger(botAI); }
|
||||||
|
|
||||||
|
// Nightbane
|
||||||
|
static Trigger* nightbane_boss_engaged_by_main_tank(
|
||||||
|
PlayerbotAI* botAI) { return new NightbaneBossEngagedByMainTankTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* nightbane_ranged_bots_are_in_charred_earth(
|
||||||
|
PlayerbotAI* botAI) { return new NightbaneRangedBotsAreInCharredEarthTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* nightbane_main_tank_is_susceptible_to_fear(
|
||||||
|
PlayerbotAI* botAI) { return new NightbaneMainTankIsSusceptibleToFearTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* nightbane_pets_ignore_collision_to_chase_flying_boss(
|
||||||
|
PlayerbotAI* botAI) { return new NightbanePetsIgnoreCollisionToChaseFlyingBossTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* nightbane_boss_is_flying(
|
||||||
|
PlayerbotAI* botAI) { return new NightbaneBossIsFlyingTrigger(botAI); }
|
||||||
|
|
||||||
|
static Trigger* nightbane_need_to_manage_timers_and_trackers(
|
||||||
|
PlayerbotAI* botAI) { return new NightbaneNeedToManageTimersAndTrackersTrigger(botAI); }
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -3,17 +3,64 @@
|
|||||||
#include "RaidKarazhanActions.h"
|
#include "RaidKarazhanActions.h"
|
||||||
#include "Playerbots.h"
|
#include "Playerbots.h"
|
||||||
|
|
||||||
bool KarazhanAttumenTheHuntsmanTrigger::IsActive()
|
using namespace KarazhanHelpers;
|
||||||
{
|
|
||||||
RaidKarazhanHelpers helpers(botAI);
|
|
||||||
Unit* boss = helpers.GetFirstAliveUnitByEntry(NPC_ATTUMEN_THE_HUNTSMAN_MOUNTED);
|
|
||||||
|
|
||||||
return boss && boss->IsAlive();
|
bool ManaWarpIsAboutToExplodeTrigger::IsActive()
|
||||||
|
{
|
||||||
|
Unit* manaWarp = AI_VALUE2(Unit*, "find target", "mana warp");
|
||||||
|
return manaWarp && manaWarp->GetHealthPct() < 15;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool KarazhanMoroesTrigger::IsActive()
|
bool AttumenTheHuntsmanNeedTargetPriorityTrigger::IsActive()
|
||||||
{
|
{
|
||||||
|
if (botAI->IsHeal(bot))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Unit* midnight = AI_VALUE2(Unit*, "find target", "midnight");
|
||||||
|
return midnight != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AttumenTheHuntsmanAttumenSpawnedTrigger::IsActive()
|
||||||
|
{
|
||||||
|
if (!botAI->IsAssistTankOfIndex(bot, 0))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Unit* attumen = GetFirstAliveUnitByEntry(botAI, NPC_ATTUMEN_THE_HUNTSMAN);
|
||||||
|
return attumen != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AttumenTheHuntsmanAttumenIsMountedTrigger::IsActive()
|
||||||
|
{
|
||||||
|
if (botAI->IsMainTank(bot))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Unit* attumenMounted = GetFirstAliveUnitByEntry(botAI, NPC_ATTUMEN_THE_HUNTSMAN_MOUNTED);
|
||||||
|
return attumenMounted && attumenMounted->GetVictim() != bot;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AttumenTheHuntsmanBossWipesAggroWhenMountingTrigger::IsActive()
|
||||||
|
{
|
||||||
|
if (!IsMapIDTimerManager(botAI, bot))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Unit* midnight = AI_VALUE2(Unit*, "find target", "midnight");
|
||||||
|
return midnight != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MoroesBossEngagedByMainTankTrigger::IsActive()
|
||||||
|
{
|
||||||
|
if (!botAI->IsMainTank(bot))
|
||||||
|
return false;
|
||||||
|
|
||||||
Unit* moroes = AI_VALUE2(Unit*, "find target", "moroes");
|
Unit* moroes = AI_VALUE2(Unit*, "find target", "moroes");
|
||||||
|
return moroes != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MoroesNeedTargetPriorityTrigger::IsActive()
|
||||||
|
{
|
||||||
|
if (!botAI->IsDps(bot))
|
||||||
|
return false;
|
||||||
|
|
||||||
Unit* dorothea = AI_VALUE2(Unit*, "find target", "baroness dorothea millstipe");
|
Unit* dorothea = AI_VALUE2(Unit*, "find target", "baroness dorothea millstipe");
|
||||||
Unit* catriona = AI_VALUE2(Unit*, "find target", "lady catriona von'indi");
|
Unit* catriona = AI_VALUE2(Unit*, "find target", "lady catriona von'indi");
|
||||||
Unit* keira = AI_VALUE2(Unit*, "find target", "lady keira berrybuck");
|
Unit* keira = AI_VALUE2(Unit*, "find target", "lady keira berrybuck");
|
||||||
@@ -21,39 +68,67 @@ bool KarazhanMoroesTrigger::IsActive()
|
|||||||
Unit* robin = AI_VALUE2(Unit*, "find target", "lord robin daris");
|
Unit* robin = AI_VALUE2(Unit*, "find target", "lord robin daris");
|
||||||
Unit* crispin = AI_VALUE2(Unit*, "find target", "lord crispin ference");
|
Unit* crispin = AI_VALUE2(Unit*, "find target", "lord crispin ference");
|
||||||
|
|
||||||
return ((moroes && moroes->IsAlive()) ||
|
Unit* target = GetFirstAliveUnit({ dorothea, catriona, keira, rafe, robin, crispin });
|
||||||
(dorothea && dorothea->IsAlive()) ||
|
return target != nullptr;
|
||||||
(catriona && catriona->IsAlive()) ||
|
|
||||||
(keira && keira->IsAlive()) ||
|
|
||||||
(rafe && rafe->IsAlive()) ||
|
|
||||||
(robin && robin->IsAlive()) ||
|
|
||||||
(crispin && crispin->IsAlive()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool KarazhanMaidenOfVirtueTrigger::IsActive()
|
bool MaidenOfVirtueHealersAreStunnedByRepentanceTrigger::IsActive()
|
||||||
{
|
{
|
||||||
Unit* boss = AI_VALUE2(Unit*, "find target", "maiden of virtue");
|
if (!botAI->IsTank(bot))
|
||||||
|
return false;
|
||||||
|
|
||||||
return boss && boss->IsAlive();
|
Unit* maiden = AI_VALUE2(Unit*, "find target", "maiden of virtue");
|
||||||
|
return maiden && maiden->GetVictim() == bot;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool KarazhanBigBadWolfTrigger::IsActive()
|
bool MaidenOfVirtueHolyWrathDealsChainDamageTrigger::IsActive()
|
||||||
{
|
{
|
||||||
Unit* boss = AI_VALUE2(Unit*, "find target", "the big bad wolf");
|
if (!botAI->IsRanged(bot))
|
||||||
|
return false;
|
||||||
|
|
||||||
return boss && boss->IsAlive();
|
Unit* maiden = AI_VALUE2(Unit*, "find target", "maiden of virtue");
|
||||||
|
return maiden != nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool KarazhanRomuloAndJulianneTrigger::IsActive()
|
bool BigBadWolfBossEngagedByTankTrigger::IsActive()
|
||||||
{
|
{
|
||||||
Unit* julianne = AI_VALUE2(Unit*, "find target", "julianne");
|
if (!botAI->IsTank(bot) || bot->HasAura(SPELL_LITTLE_RED_RIDING_HOOD))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Unit* wolf = AI_VALUE2(Unit*, "find target", "the big bad wolf");
|
||||||
|
return wolf != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BigBadWolfBossIsChasingLittleRedRidingHoodTrigger::IsActive()
|
||||||
|
{
|
||||||
|
if (!bot->HasAura(SPELL_LITTLE_RED_RIDING_HOOD))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Unit* wolf = AI_VALUE2(Unit*, "find target", "the big bad wolf");
|
||||||
|
return wolf != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RomuloAndJulianneBothBossesRevivedTrigger::IsActive()
|
||||||
|
{
|
||||||
|
if (!IsMapIDTimerManager(botAI, bot))
|
||||||
|
return false;
|
||||||
|
|
||||||
Unit* romulo = AI_VALUE2(Unit*, "find target", "romulo");
|
Unit* romulo = AI_VALUE2(Unit*, "find target", "romulo");
|
||||||
|
if (!romulo)
|
||||||
|
return false;
|
||||||
|
|
||||||
return julianne && julianne->IsAlive() && romulo && romulo->IsAlive();
|
Unit* julianne = AI_VALUE2(Unit*, "find target", "julianne");
|
||||||
|
if (!julianne)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool KarazhanWizardOfOzTrigger::IsActive()
|
bool WizardOfOzNeedTargetPriorityTrigger::IsActive()
|
||||||
{
|
{
|
||||||
|
if (!IsMapIDTimerManager(botAI, bot))
|
||||||
|
return false;
|
||||||
|
|
||||||
Unit* dorothee = AI_VALUE2(Unit*, "find target", "dorothee");
|
Unit* dorothee = AI_VALUE2(Unit*, "find target", "dorothee");
|
||||||
Unit* tito = AI_VALUE2(Unit*, "find target", "tito");
|
Unit* tito = AI_VALUE2(Unit*, "find target", "tito");
|
||||||
Unit* roar = AI_VALUE2(Unit*, "find target", "roar");
|
Unit* roar = AI_VALUE2(Unit*, "find target", "roar");
|
||||||
@@ -61,45 +136,249 @@ bool KarazhanWizardOfOzTrigger::IsActive()
|
|||||||
Unit* tinhead = AI_VALUE2(Unit*, "find target", "tinhead");
|
Unit* tinhead = AI_VALUE2(Unit*, "find target", "tinhead");
|
||||||
Unit* crone = AI_VALUE2(Unit*, "find target", "the crone");
|
Unit* crone = AI_VALUE2(Unit*, "find target", "the crone");
|
||||||
|
|
||||||
return ((dorothee && dorothee->IsAlive()) ||
|
Unit* target = GetFirstAliveUnit({ dorothee, tito, roar, strawman, tinhead, crone });
|
||||||
(tito && tito->IsAlive()) ||
|
return target != nullptr;
|
||||||
(roar && roar->IsAlive()) ||
|
|
||||||
(strawman && strawman->IsAlive()) ||
|
|
||||||
(tinhead && tinhead->IsAlive()) ||
|
|
||||||
(crone && crone->IsAlive()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool KarazhanTheCuratorTrigger::IsActive()
|
bool WizardOfOzStrawmanIsVulnerableToFireTrigger::IsActive()
|
||||||
{
|
{
|
||||||
Unit* boss = AI_VALUE2(Unit*, "find target", "the curator");
|
if (bot->getClass() != CLASS_MAGE)
|
||||||
|
return false;
|
||||||
|
|
||||||
return boss && boss->IsAlive();
|
Unit* strawman = AI_VALUE2(Unit*, "find target", "strawman");
|
||||||
|
return strawman && strawman->IsAlive();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool KarazhanTerestianIllhoofTrigger::IsActive()
|
bool TheCuratorAstralFlareSpawnedTrigger::IsActive()
|
||||||
{
|
{
|
||||||
Unit* boss = AI_VALUE2(Unit*, "find target", "terestian illhoof");
|
if (!botAI->IsDps(bot))
|
||||||
|
return false;
|
||||||
|
|
||||||
return boss && boss->IsAlive();
|
Unit* flare = AI_VALUE2(Unit*, "find target", "astral flare");
|
||||||
|
return flare != nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool KarazhanShadeOfAranTrigger::IsActive()
|
bool TheCuratorBossEngagedByTanksTrigger::IsActive()
|
||||||
{
|
{
|
||||||
Unit* boss = AI_VALUE2(Unit*, "find target", "shade of aran");
|
if (!botAI->IsMainTank(bot) && !botAI->IsAssistTankOfIndex(bot, 0))
|
||||||
|
return false;
|
||||||
|
|
||||||
return boss && boss->IsAlive();
|
Unit* curator = AI_VALUE2(Unit*, "find target", "the curator");
|
||||||
|
return curator != nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool KarazhanNetherspiteTrigger::IsActive()
|
bool TheCuratorBossAstralFlaresCastArcingSearTrigger::IsActive()
|
||||||
{
|
{
|
||||||
Unit* boss = AI_VALUE2(Unit*, "find target", "netherspite");
|
if (!botAI->IsRanged(bot))
|
||||||
|
return false;
|
||||||
|
|
||||||
return boss && boss->IsAlive();
|
Unit* curator = AI_VALUE2(Unit*, "find target", "the curator");
|
||||||
|
return curator != nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool KarazhanPrinceMalchezaarTrigger::IsActive()
|
bool TerestianIllhoofNeedTargetPriorityTrigger::IsActive()
|
||||||
{
|
{
|
||||||
Unit* boss = AI_VALUE2(Unit*, "find target", "prince malchezaar");
|
if (!IsMapIDTimerManager(botAI, bot))
|
||||||
|
return false;
|
||||||
|
|
||||||
return boss && boss->IsAlive();
|
Unit* illhoof = AI_VALUE2(Unit*, "find target", "terestian illhoof");
|
||||||
|
return illhoof != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ShadeOfAranArcaneExplosionIsCastingTrigger::IsActive()
|
||||||
|
{
|
||||||
|
Unit* aran = AI_VALUE2(Unit*, "find target", "shade of aran");
|
||||||
|
return aran && aran->HasUnitState(UNIT_STATE_CASTING) &&
|
||||||
|
aran->FindCurrentSpellBySpellId(SPELL_ARCANE_EXPLOSION) &&
|
||||||
|
!IsFlameWreathActive(botAI, bot);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ShadeOfAranFlameWreathIsActiveTrigger::IsActive()
|
||||||
|
{
|
||||||
|
Unit* aran = AI_VALUE2(Unit*, "find target", "shade of aran");
|
||||||
|
return aran && IsFlameWreathActive(botAI, bot);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exclusion of Banish is so the player may Banish elementals if they wish
|
||||||
|
bool ShadeOfAranConjuredElementalsSummonedTrigger::IsActive()
|
||||||
|
{
|
||||||
|
if (!IsMapIDTimerManager(botAI, bot))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Unit* elemental = AI_VALUE2(Unit*, "find target", "conjured elemental");
|
||||||
|
return elemental && elemental->IsAlive() &&
|
||||||
|
!elemental->HasAura(SPELL_WARLOCK_BANISH);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ShadeOfAranBossUsesCounterspellAndBlizzardTrigger::IsActive()
|
||||||
|
{
|
||||||
|
if (!botAI->IsRanged(bot))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Unit* aran = AI_VALUE2(Unit*, "find target", "shade of aran");
|
||||||
|
return aran && !(aran->HasUnitState(UNIT_STATE_CASTING) &&
|
||||||
|
aran->FindCurrentSpellBySpellId(SPELL_ARCANE_EXPLOSION)) &&
|
||||||
|
!IsFlameWreathActive(botAI, bot);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NetherspiteRedBeamIsActiveTrigger::IsActive()
|
||||||
|
{
|
||||||
|
Unit* netherspite = AI_VALUE2(Unit*, "find target", "netherspite");
|
||||||
|
if (!netherspite || netherspite->HasAura(SPELL_NETHERSPITE_BANISHED))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Unit* redPortal = bot->FindNearestCreature(NPC_RED_PORTAL, 150.0f);
|
||||||
|
return redPortal != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NetherspiteBlueBeamIsActiveTrigger::IsActive()
|
||||||
|
{
|
||||||
|
Unit* netherspite = AI_VALUE2(Unit*, "find target", "netherspite");
|
||||||
|
if (!netherspite || netherspite->HasAura(SPELL_NETHERSPITE_BANISHED))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Unit* bluePortal = bot->FindNearestCreature(NPC_BLUE_PORTAL, 150.0f);
|
||||||
|
return bluePortal != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NetherspiteGreenBeamIsActiveTrigger::IsActive()
|
||||||
|
{
|
||||||
|
Unit* netherspite = AI_VALUE2(Unit*, "find target", "netherspite");
|
||||||
|
if (!netherspite || netherspite->HasAura(SPELL_NETHERSPITE_BANISHED))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Unit* greenPortal = bot->FindNearestCreature(NPC_GREEN_PORTAL, 150.0f);
|
||||||
|
return greenPortal != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NetherspiteBotIsNotBeamBlockerTrigger::IsActive()
|
||||||
|
{
|
||||||
|
Unit* netherspite = AI_VALUE2(Unit*, "find target", "netherspite");
|
||||||
|
if (!netherspite || netherspite->HasAura(SPELL_NETHERSPITE_BANISHED))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
auto [redBlocker, greenBlocker, blueBlocker] = GetCurrentBeamBlockers(botAI, bot);
|
||||||
|
return bot != redBlocker && bot != blueBlocker && bot != greenBlocker;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NetherspiteBossIsBanishedTrigger::IsActive()
|
||||||
|
{
|
||||||
|
Unit* netherspite = AI_VALUE2(Unit*, "find target", "netherspite");
|
||||||
|
if (!netherspite || !netherspite->HasAura(SPELL_NETHERSPITE_BANISHED))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
std::vector<Unit*> voidZones = GetAllVoidZones(botAI, bot);
|
||||||
|
for (Unit* vz : voidZones)
|
||||||
|
{
|
||||||
|
if (bot->GetExactDist2d(vz) < 4.0f)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NetherspiteNeedToManageTimersAndTrackersTrigger::IsActive()
|
||||||
|
{
|
||||||
|
if (!botAI->IsTank(bot) && !IsMapIDTimerManager(botAI, bot))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Unit* netherspite = AI_VALUE2(Unit*, "find target", "netherspite");
|
||||||
|
return netherspite != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PrinceMalchezaarBotIsEnfeebledTrigger::IsActive()
|
||||||
|
{
|
||||||
|
return bot->HasAura(SPELL_ENFEEBLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PrinceMalchezaarInfernalsAreSpawnedTrigger::IsActive()
|
||||||
|
{
|
||||||
|
if (botAI->IsMainTank(bot) || bot->HasAura(SPELL_ENFEEBLE))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Unit* malchezaar = AI_VALUE2(Unit*, "find target", "prince malchezaar");
|
||||||
|
return malchezaar != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PrinceMalchezaarBossEngagedByMainTankTrigger::IsActive()
|
||||||
|
{
|
||||||
|
if (!botAI->IsMainTank(bot))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Unit* malchezaar = AI_VALUE2(Unit*, "find target", "prince malchezaar");
|
||||||
|
return malchezaar != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NightbaneBossEngagedByMainTankTrigger::IsActive()
|
||||||
|
{
|
||||||
|
if (!botAI->IsMainTank(bot))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Unit* nightbane = AI_VALUE2(Unit*, "find target", "nightbane");
|
||||||
|
return nightbane && nightbane->GetPositionZ() <= NIGHTBANE_FLIGHT_Z;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NightbaneRangedBotsAreInCharredEarthTrigger::IsActive()
|
||||||
|
{
|
||||||
|
if (!botAI->IsRanged(bot))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Unit* nightbane = AI_VALUE2(Unit*, "find target", "nightbane");
|
||||||
|
return nightbane && nightbane->GetPositionZ() <= NIGHTBANE_FLIGHT_Z;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NightbaneMainTankIsSusceptibleToFearTrigger::IsActive()
|
||||||
|
{
|
||||||
|
if (bot->getClass() != CLASS_PRIEST)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Unit* nightbane = AI_VALUE2(Unit*, "find target", "nightbane");
|
||||||
|
if (!nightbane)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Player* mainTank = nullptr;
|
||||||
|
if (Group* group = bot->GetGroup())
|
||||||
|
{
|
||||||
|
for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next())
|
||||||
|
{
|
||||||
|
Player* member = ref->GetSource();
|
||||||
|
if (member && botAI->IsMainTank(member))
|
||||||
|
{
|
||||||
|
mainTank = member;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return mainTank && !mainTank->HasAura(SPELL_FEAR_WARD) &&
|
||||||
|
botAI->CanCastSpell("fear ward", mainTank);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NightbanePetsIgnoreCollisionToChaseFlyingBossTrigger::IsActive()
|
||||||
|
{
|
||||||
|
Unit* nightbane = AI_VALUE2(Unit*, "find target", "nightbane");
|
||||||
|
if (!nightbane)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Pet* pet = bot->GetPet();
|
||||||
|
return pet && pet->IsAlive();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NightbaneBossIsFlyingTrigger::IsActive()
|
||||||
|
{
|
||||||
|
Unit* nightbane = AI_VALUE2(Unit*, "find target", "nightbane");
|
||||||
|
if (!nightbane || nightbane->GetPositionZ() <= NIGHTBANE_FLIGHT_Z)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const time_t now = std::time(nullptr);
|
||||||
|
const uint8 flightPhaseDurationSeconds = 35;
|
||||||
|
|
||||||
|
return nightbaneFlightPhaseStartTimer.find(KARAZHAN_MAP_ID) != nightbaneFlightPhaseStartTimer.end() &&
|
||||||
|
(now - nightbaneFlightPhaseStartTimer[KARAZHAN_MAP_ID] < flightPhaseDurationSeconds);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NightbaneNeedToManageTimersAndTrackersTrigger::IsActive()
|
||||||
|
{
|
||||||
|
Unit* nightbane = AI_VALUE2(Unit*, "find target", "nightbane");
|
||||||
|
return nightbane != nullptr;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,80 +3,298 @@
|
|||||||
|
|
||||||
#include "Trigger.h"
|
#include "Trigger.h"
|
||||||
|
|
||||||
class KarazhanAttumenTheHuntsmanTrigger : public Trigger
|
class ManaWarpIsAboutToExplodeTrigger : public Trigger
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
KarazhanAttumenTheHuntsmanTrigger(PlayerbotAI* botAI) : Trigger(botAI, "karazhan attumen the huntsman") {}
|
ManaWarpIsAboutToExplodeTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "mana warp is about to explode") {}
|
||||||
bool IsActive() override;
|
bool IsActive() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class KarazhanMoroesTrigger : public Trigger
|
class AttumenTheHuntsmanNeedTargetPriorityTrigger : public Trigger
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
KarazhanMoroesTrigger(PlayerbotAI* botAI) : Trigger(botAI, "karazhan moroes") {}
|
AttumenTheHuntsmanNeedTargetPriorityTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "attumen the huntsman need target priority") {}
|
||||||
bool IsActive() override;
|
bool IsActive() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class KarazhanMaidenOfVirtueTrigger : public Trigger
|
class AttumenTheHuntsmanAttumenSpawnedTrigger : public Trigger
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
KarazhanMaidenOfVirtueTrigger(PlayerbotAI* botAI) : Trigger(botAI, "karazhan maiden of virtue") {}
|
AttumenTheHuntsmanAttumenSpawnedTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "attumen the huntsman attumen spawned") {}
|
||||||
bool IsActive() override;
|
bool IsActive() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class KarazhanBigBadWolfTrigger : public Trigger
|
class AttumenTheHuntsmanAttumenIsMountedTrigger : public Trigger
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
KarazhanBigBadWolfTrigger(PlayerbotAI* botAI) : Trigger(botAI, "karazhan big bad wolf") {}
|
AttumenTheHuntsmanAttumenIsMountedTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "attumen the huntsman attumen is mounted") {}
|
||||||
bool IsActive() override;
|
bool IsActive() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class KarazhanRomuloAndJulianneTrigger : public Trigger
|
class AttumenTheHuntsmanBossWipesAggroWhenMountingTrigger : public Trigger
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
KarazhanRomuloAndJulianneTrigger(PlayerbotAI* botAI) : Trigger(botAI, "karazhan romulo and julianne") {}
|
AttumenTheHuntsmanBossWipesAggroWhenMountingTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "attumen the huntsman boss wipes aggro when mounting") {}
|
||||||
bool IsActive() override;
|
bool IsActive() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class KarazhanWizardOfOzTrigger : public Trigger
|
class MoroesBossEngagedByMainTankTrigger : public Trigger
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
KarazhanWizardOfOzTrigger(PlayerbotAI* botAI) : Trigger(botAI, "karazhan wizard of oz") {}
|
MoroesBossEngagedByMainTankTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "moroes boss engaged by main tank") {}
|
||||||
bool IsActive() override;
|
bool IsActive() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class KarazhanTheCuratorTrigger : public Trigger
|
class MoroesNeedTargetPriorityTrigger : public Trigger
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
KarazhanTheCuratorTrigger(PlayerbotAI* botAI) : Trigger(botAI, "karazhan the curator") {}
|
MoroesNeedTargetPriorityTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "moroes need target priority") {}
|
||||||
bool IsActive() override;
|
bool IsActive() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class KarazhanTerestianIllhoofTrigger : public Trigger
|
class MaidenOfVirtueHealersAreStunnedByRepentanceTrigger : public Trigger
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
KarazhanTerestianIllhoofTrigger(PlayerbotAI* botAI) : Trigger(botAI, "karazhan terestian illhoof") {}
|
MaidenOfVirtueHealersAreStunnedByRepentanceTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "maiden of virtue healers are stunned by repentance") {}
|
||||||
bool IsActive() override;
|
bool IsActive() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class KarazhanShadeOfAranTrigger : public Trigger
|
class MaidenOfVirtueHolyWrathDealsChainDamageTrigger : public Trigger
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
KarazhanShadeOfAranTrigger(PlayerbotAI* botAI) : Trigger(botAI, "karazhan shade of aran") {}
|
MaidenOfVirtueHolyWrathDealsChainDamageTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "maiden of virtue holy wrath deals chain damage") {}
|
||||||
bool IsActive() override;
|
bool IsActive() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class KarazhanNetherspiteTrigger : public Trigger
|
class BigBadWolfBossEngagedByTankTrigger : public Trigger
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
KarazhanNetherspiteTrigger(PlayerbotAI* botAI) : Trigger(botAI, "karazhan netherspite") {}
|
BigBadWolfBossEngagedByTankTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "big bad wolf boss engaged by tank") {}
|
||||||
bool IsActive() override;
|
bool IsActive() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class KarazhanPrinceMalchezaarTrigger : public Trigger
|
class BigBadWolfBossIsChasingLittleRedRidingHoodTrigger : public Trigger
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
KarazhanPrinceMalchezaarTrigger(PlayerbotAI* botAI) : Trigger(botAI, "karazhan prince malchezaar") {}
|
BigBadWolfBossIsChasingLittleRedRidingHoodTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "big bad wolf boss is chasing little red riding hood") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class RomuloAndJulianneBothBossesRevivedTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
RomuloAndJulianneBothBossesRevivedTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "romulo and julianne both bosses revived") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class WizardOfOzNeedTargetPriorityTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
WizardOfOzNeedTargetPriorityTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "wizard of oz need target priority") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class WizardOfOzStrawmanIsVulnerableToFireTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
WizardOfOzStrawmanIsVulnerableToFireTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "wizard of oz strawman is vulnerable to fire") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
class TheCuratorAstralFlareSpawnedTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TheCuratorAstralFlareSpawnedTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "the curator astral flare spawned") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class TheCuratorBossEngagedByTanksTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TheCuratorBossEngagedByTanksTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "the curator boss engaged by tanks") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class TheCuratorBossAstralFlaresCastArcingSearTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TheCuratorBossAstralFlaresCastArcingSearTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "the curator astral flares cast arcing sear") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class TerestianIllhoofNeedTargetPriorityTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TerestianIllhoofNeedTargetPriorityTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "terestian illhoof need target priority") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ShadeOfAranArcaneExplosionIsCastingTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ShadeOfAranArcaneExplosionIsCastingTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "shade of aran arcane explosion is casting") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ShadeOfAranFlameWreathIsActiveTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ShadeOfAranFlameWreathIsActiveTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "shade of aran flame wreath is active") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ShadeOfAranConjuredElementalsSummonedTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ShadeOfAranConjuredElementalsSummonedTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "shade of aran conjured elementals summoned") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ShadeOfAranBossUsesCounterspellAndBlizzardTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ShadeOfAranBossUsesCounterspellAndBlizzardTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "shade of aran boss uses counterspell and blizzard") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class NetherspiteRedBeamIsActiveTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
NetherspiteRedBeamIsActiveTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "netherspite red beam is active") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class NetherspiteBlueBeamIsActiveTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
NetherspiteBlueBeamIsActiveTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "netherspite blue beam is active") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class NetherspiteGreenBeamIsActiveTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
NetherspiteGreenBeamIsActiveTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "netherspite green beam is active") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class NetherspiteBotIsNotBeamBlockerTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
NetherspiteBotIsNotBeamBlockerTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "netherspite bot is not beam blocker") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class NetherspiteBossIsBanishedTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
NetherspiteBossIsBanishedTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "netherspite boss is banished") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class NetherspiteNeedToManageTimersAndTrackersTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
NetherspiteNeedToManageTimersAndTrackersTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "netherspite need to manage timers and trackers") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class PrinceMalchezaarBotIsEnfeebledTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
PrinceMalchezaarBotIsEnfeebledTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "prince malchezaar bot is enfeebled") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class PrinceMalchezaarInfernalsAreSpawnedTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
PrinceMalchezaarInfernalsAreSpawnedTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "prince malchezaar infernals are spawned") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class PrinceMalchezaarBossEngagedByMainTankTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
PrinceMalchezaarBossEngagedByMainTankTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "prince malchezaar boss engaged by main tank") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class NightbaneBossEngagedByMainTankTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
NightbaneBossEngagedByMainTankTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "nightbane boss engaged by main tank") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class NightbaneRangedBotsAreInCharredEarthTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
NightbaneRangedBotsAreInCharredEarthTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "nightbane ranged bots are in charred earth") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class NightbaneMainTankIsSusceptibleToFearTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
NightbaneMainTankIsSusceptibleToFearTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "nightbane main tank is susceptible to fear") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class NightbanePetsIgnoreCollisionToChaseFlyingBossTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
NightbanePetsIgnoreCollisionToChaseFlyingBossTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "nightbane pets ignore collision to chase flying boss") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class NightbaneBossIsFlyingTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
NightbaneBossIsFlyingTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "nightbane boss is flying") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class NightbaneNeedToManageTimersAndTrackersTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
NightbaneNeedToManageTimersAndTrackersTrigger(
|
||||||
|
PlayerbotAI* botAI) : Trigger(botAI, "nightbane need to manage timers and trackers") {}
|
||||||
bool IsActive() override;
|
bool IsActive() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -10,13 +10,39 @@ class RaidMcActionContext : public NamedObjectContext<Action>
|
|||||||
public:
|
public:
|
||||||
RaidMcActionContext()
|
RaidMcActionContext()
|
||||||
{
|
{
|
||||||
creators["mc check should move from group"] = &RaidMcActionContext::check_should_move_from_group;
|
creators["mc lucifron shadow resistance"] = &RaidMcActionContext::lucifron_shadow_resistance;
|
||||||
|
creators["mc magmadar fire resistance"] = &RaidMcActionContext::magmadar_fire_resistance;
|
||||||
|
creators["mc gehennas shadow resistance"] = &RaidMcActionContext::gehennas_shadow_resistance;
|
||||||
|
creators["mc garr fire resistance"] = &RaidMcActionContext::garr_fire_resistance;
|
||||||
|
creators["mc baron geddon fire resistance"] = &RaidMcActionContext::baron_geddon_fire_resistance;
|
||||||
|
creators["mc move from group"] = &RaidMcActionContext::check_should_move_from_group;
|
||||||
creators["mc move from baron geddon"] = &RaidMcActionContext::move_from_baron_geddon;
|
creators["mc move from baron geddon"] = &RaidMcActionContext::move_from_baron_geddon;
|
||||||
|
creators["mc shazzrah move away"] = &RaidMcActionContext::shazzrah_move_away;
|
||||||
|
creators["mc sulfuron harbinger fire resistance"] = &RaidMcActionContext::sulfuron_harbinger_fire_resistance;
|
||||||
|
creators["mc golemagg fire resistance"] = &RaidMcActionContext::golemagg_fire_resistance;
|
||||||
|
creators["mc golemagg mark boss"] = &RaidMcActionContext::golemagg_mark_boss;
|
||||||
|
creators["mc golemagg main tank attack golemagg"] = &RaidMcActionContext::golemagg_main_tank_attack_golemagg;
|
||||||
|
creators["mc golemagg assist tank attack core rager"] = &RaidMcActionContext::golemagg_assist_tank_attack_core_rager;
|
||||||
|
creators["mc majordomo shadow resistance"] = &RaidMcActionContext::majordomo_shadow_resistance;
|
||||||
|
creators["mc ragnaros fire resistance"] = &RaidMcActionContext::ragnaros_fire_resistance;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static Action* check_should_move_from_group(PlayerbotAI* ai) { return new McCheckShouldMoveFromGroupAction(ai); }
|
static Action* lucifron_shadow_resistance(PlayerbotAI* botAI) { return new BossShadowResistanceAction(botAI, "lucifron"); }
|
||||||
static Action* move_from_baron_geddon(PlayerbotAI* ai) { return new McMoveFromBaronGeddonAction(ai); }
|
static Action* magmadar_fire_resistance(PlayerbotAI* botAI) { return new BossFireResistanceAction(botAI, "magmadar"); }
|
||||||
|
static Action* gehennas_shadow_resistance(PlayerbotAI* botAI) { return new BossShadowResistanceAction(botAI, "gehennas"); }
|
||||||
|
static Action* garr_fire_resistance(PlayerbotAI* botAI) { return new BossFireResistanceAction(botAI, "garr"); }
|
||||||
|
static Action* baron_geddon_fire_resistance(PlayerbotAI* botAI) { return new BossFireResistanceAction(botAI, "baron geddon"); }
|
||||||
|
static Action* check_should_move_from_group(PlayerbotAI* botAI) { return new McMoveFromGroupAction(botAI); }
|
||||||
|
static Action* move_from_baron_geddon(PlayerbotAI* botAI) { return new McMoveFromBaronGeddonAction(botAI); }
|
||||||
|
static Action* shazzrah_move_away(PlayerbotAI* botAI) { return new McShazzrahMoveAwayAction(botAI); }
|
||||||
|
static Action* sulfuron_harbinger_fire_resistance(PlayerbotAI* botAI) { return new BossFireResistanceAction(botAI, "sulfuron harbinger"); }
|
||||||
|
static Action* golemagg_fire_resistance(PlayerbotAI* botAI) { return new BossFireResistanceAction(botAI, "golemagg the incinerator"); }
|
||||||
|
static Action* golemagg_mark_boss(PlayerbotAI* botAI) { return new McGolemaggMarkBossAction(botAI); }
|
||||||
|
static Action* golemagg_main_tank_attack_golemagg(PlayerbotAI* botAI) { return new McGolemaggMainTankAttackGolemaggAction(botAI); }
|
||||||
|
static Action* golemagg_assist_tank_attack_core_rager(PlayerbotAI* botAI) { return new McGolemaggAssistTankAttackCoreRagerAction(botAI); }
|
||||||
|
static Action* majordomo_shadow_resistance(PlayerbotAI* botAI) { return new BossShadowResistanceAction(botAI, "majordomo executus"); }
|
||||||
|
static Action* ragnaros_fire_resistance(PlayerbotAI* botAI) { return new BossFireResistanceAction(botAI, "ragnaros"); }
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -1,43 +1,215 @@
|
|||||||
#include "RaidMcActions.h"
|
#include "RaidMcActions.h"
|
||||||
|
|
||||||
#include "Playerbots.h"
|
#include "Playerbots.h"
|
||||||
|
#include "RtiTargetValue.h"
|
||||||
|
#include "RaidMcTriggers.h"
|
||||||
|
#include "RaidMcHelpers.h"
|
||||||
|
|
||||||
bool McCheckShouldMoveFromGroupAction::Execute(Event event)
|
static constexpr float LIVING_BOMB_DISTANCE = 20.0f;
|
||||||
|
static constexpr float INFERNO_DISTANCE = 20.0f;
|
||||||
|
|
||||||
|
// don't get hit by Arcane Explosion but still be in casting range
|
||||||
|
static constexpr float ARCANE_EXPLOSION_DISTANCE = 26.0f;
|
||||||
|
|
||||||
|
// dedicated tank positions; prevents assist tanks from positioning Core Ragers on steep walls on pull
|
||||||
|
static const Position GOLEMAGG_TANK_POSITION{795.7308, -994.8848, -207.18661};
|
||||||
|
static const Position CORE_RAGER_TANK_POSITION{846.6453, -1019.0639, -198.9819};
|
||||||
|
|
||||||
|
static constexpr float GOLEMAGGS_TRUST_DISTANCE = 30.0f;
|
||||||
|
static constexpr float CORE_RAGER_STEP_DISTANCE = 5.0f;
|
||||||
|
|
||||||
|
using namespace MoltenCoreHelpers;
|
||||||
|
|
||||||
|
bool McMoveFromGroupAction::Execute(Event event)
|
||||||
{
|
{
|
||||||
if (bot->HasAura(20475)) // barron geddon's living bomb
|
return MoveFromGroup(LIVING_BOMB_DISTANCE);
|
||||||
{
|
|
||||||
if (!botAI->HasStrategy("move from group", BotState::BOT_STATE_COMBAT))
|
|
||||||
{
|
|
||||||
// add/remove from both for now as it will make it more obvious to
|
|
||||||
// player if this strat remains on after fight somehow
|
|
||||||
botAI->ChangeStrategy("+move from group", BOT_STATE_NON_COMBAT);
|
|
||||||
botAI->ChangeStrategy("+move from group", BOT_STATE_COMBAT);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (botAI->HasStrategy("move from group", BotState::BOT_STATE_COMBAT))
|
|
||||||
{
|
|
||||||
// add/remove from both for now as it will make it more obvious to
|
|
||||||
// player if this strat remains on after fight somehow
|
|
||||||
botAI->ChangeStrategy("-move from group", BOT_STATE_NON_COMBAT);
|
|
||||||
botAI->ChangeStrategy("-move from group", BOT_STATE_COMBAT);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool McMoveFromBaronGeddonAction::Execute(Event event)
|
bool McMoveFromBaronGeddonAction::Execute(Event event)
|
||||||
{
|
{
|
||||||
const float radius = 25.0f; // more than should be needed but bots keep trying to run back in
|
|
||||||
if (Unit* boss = AI_VALUE2(Unit*, "find target", "baron geddon"))
|
if (Unit* boss = AI_VALUE2(Unit*, "find target", "baron geddon"))
|
||||||
{
|
{
|
||||||
long distToTravel = radius - bot->GetDistance(boss);
|
float distToTravel = INFERNO_DISTANCE - bot->GetDistance2d(boss);
|
||||||
if (distToTravel > 0)
|
if (distToTravel > 0)
|
||||||
{
|
{
|
||||||
// float angle = bot->GetAngle(boss) + M_PI;
|
// Stop current spell first
|
||||||
// return Move(angle, distToTravel);
|
bot->AttackStop();
|
||||||
|
bot->InterruptNonMeleeSpells(false);
|
||||||
|
|
||||||
return MoveAway(boss, distToTravel);
|
return MoveAway(boss, distToTravel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool McShazzrahMoveAwayAction::Execute(Event event)
|
||||||
|
{
|
||||||
|
if (Unit* boss = AI_VALUE2(Unit*, "find target", "shazzrah"))
|
||||||
|
{
|
||||||
|
float distToTravel = ARCANE_EXPLOSION_DISTANCE - bot->GetDistance2d(boss);
|
||||||
|
if (distToTravel > 0)
|
||||||
|
return MoveAway(boss, distToTravel);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool McGolemaggMarkBossAction::Execute(Event event)
|
||||||
|
{
|
||||||
|
if (Unit* boss = AI_VALUE2(Unit*, "find target", "golemagg the incinerator"))
|
||||||
|
{
|
||||||
|
if (Group* group = bot->GetGroup())
|
||||||
|
{
|
||||||
|
ObjectGuid currentSkullGuid = group->GetTargetIcon(RtiTargetValue::skullIndex);
|
||||||
|
if (currentSkullGuid.IsEmpty() || currentSkullGuid != boss->GetGUID())
|
||||||
|
{
|
||||||
|
group->SetTargetIcon(RtiTargetValue::skullIndex, bot->GetGUID(), boss->GetGUID());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool McGolemaggTankAction::MoveUnitToPosition(Unit* target, const Position& tankPosition, float maxDistance,
|
||||||
|
float stepDistance)
|
||||||
|
{
|
||||||
|
if (bot->GetVictim() != target)
|
||||||
|
return Attack(target);
|
||||||
|
if (target->GetVictim() == bot)
|
||||||
|
{
|
||||||
|
float distanceToTankPosition = bot->GetExactDist2d(tankPosition.GetPositionX(), tankPosition.GetPositionY());
|
||||||
|
if (distanceToTankPosition > maxDistance)
|
||||||
|
{
|
||||||
|
float dX = tankPosition.GetPositionX() - bot->GetPositionX();
|
||||||
|
float dY = tankPosition.GetPositionY() - bot->GetPositionY();
|
||||||
|
float dist = sqrt(dX * dX + dY * dY);
|
||||||
|
float moveX = bot->GetPositionX() + (dX / dist) * stepDistance;
|
||||||
|
float moveY = bot->GetPositionY() + (dY / dist) * stepDistance;
|
||||||
|
return MoveTo(bot->GetMapId(), moveX, moveY, bot->GetPositionZ(), false, false,
|
||||||
|
false, false, MovementPriority::MOVEMENT_COMBAT, true,
|
||||||
|
true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (botAI->DoSpecificAction("taunt spell", Event(), true))
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool McGolemaggTankAction::FindCoreRagers(Unit*& coreRager1, Unit*& coreRager2) const
|
||||||
|
{
|
||||||
|
coreRager1 = coreRager2 = nullptr;
|
||||||
|
for (auto const& target : AI_VALUE(GuidVector, "possible targets no los"))
|
||||||
|
{
|
||||||
|
Unit* unit = botAI->GetUnit(target);
|
||||||
|
if (unit && unit->IsAlive() && unit->GetEntry() == NPC_CORE_RAGER)
|
||||||
|
{
|
||||||
|
if (coreRager1 == nullptr)
|
||||||
|
coreRager1 = unit;
|
||||||
|
else if (coreRager2 == nullptr)
|
||||||
|
{
|
||||||
|
coreRager2 = unit;
|
||||||
|
break; // There should be no third Core Rager.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return coreRager1 != nullptr && coreRager2 != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool McGolemaggMainTankAttackGolemaggAction::Execute(Event event)
|
||||||
|
{
|
||||||
|
// At this point, we know we are not the last living tank in the group.
|
||||||
|
if (Unit* boss = AI_VALUE2(Unit*, "find target", "golemagg the incinerator"))
|
||||||
|
{
|
||||||
|
Unit* coreRager1;
|
||||||
|
Unit* coreRager2;
|
||||||
|
if (!FindCoreRagers(coreRager1, coreRager2))
|
||||||
|
return false; // safety check
|
||||||
|
|
||||||
|
// We only need to move if the Core Ragers still have Golemagg's Trust
|
||||||
|
if (coreRager1->HasAura(SPELL_GOLEMAGGS_TRUST) || coreRager2->HasAura(SPELL_GOLEMAGGS_TRUST))
|
||||||
|
return MoveUnitToPosition(boss, GOLEMAGG_TANK_POSITION, boss->GetCombatReach());
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool McGolemaggAssistTankAttackCoreRagerAction::Execute(Event event)
|
||||||
|
{
|
||||||
|
Unit* boss = AI_VALUE2(Unit*, "find target", "golemagg the incinerator");
|
||||||
|
if (!boss)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Step 0: Filter additional assist tanks. We only need 2.
|
||||||
|
bool isFirstAssistTank = PlayerbotAI::IsAssistTankOfIndex(bot, 0, true);
|
||||||
|
bool isSecondAssistTank = PlayerbotAI::IsAssistTankOfIndex(bot, 1, true);
|
||||||
|
if (!isFirstAssistTank && !isSecondAssistTank)
|
||||||
|
return Attack(boss);
|
||||||
|
|
||||||
|
// Step 1: Find both Core Ragers
|
||||||
|
Unit* coreRager1;
|
||||||
|
Unit* coreRager2;
|
||||||
|
if (!FindCoreRagers(coreRager1, coreRager2))
|
||||||
|
return false; // safety check
|
||||||
|
|
||||||
|
// Step 2: Assign Core Rager to bot
|
||||||
|
Unit* myCoreRager = nullptr;
|
||||||
|
Unit* otherCoreRager = nullptr;
|
||||||
|
if (isFirstAssistTank)
|
||||||
|
{
|
||||||
|
myCoreRager = coreRager1;
|
||||||
|
otherCoreRager = coreRager2;
|
||||||
|
}
|
||||||
|
else // isSecondAssistTank is always true here
|
||||||
|
{
|
||||||
|
myCoreRager = coreRager2;
|
||||||
|
otherCoreRager = coreRager1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 3: Select the right target
|
||||||
|
if (myCoreRager->GetVictim() != bot)
|
||||||
|
{
|
||||||
|
// Step 3.1: My Core Rager isn't attacking me. Attack until it does.
|
||||||
|
if (bot->GetVictim() != myCoreRager)
|
||||||
|
return Attack(myCoreRager);
|
||||||
|
return botAI->DoSpecificAction("taunt spell", event, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
Unit* otherCoreRagerVictim = otherCoreRager->GetVictim();
|
||||||
|
if (otherCoreRagerVictim) // Core Rager victim can be NULL
|
||||||
|
{
|
||||||
|
// Step 3.2: Check if the other Core Rager isn't attacking its assist tank.
|
||||||
|
Player* otherCoreRagerPlayerVictim = otherCoreRagerVictim->ToPlayer();
|
||||||
|
if (otherCoreRagerPlayerVictim &&
|
||||||
|
!PlayerbotAI::IsAssistTankOfIndex(otherCoreRagerPlayerVictim, 0, true) &&
|
||||||
|
!PlayerbotAI::IsAssistTankOfIndex(otherCoreRagerPlayerVictim, 1, true))
|
||||||
|
{
|
||||||
|
// Assume we are the only assist tank or the other assist tank is dead => pick up other Core Rager!
|
||||||
|
if (bot->GetVictim() != otherCoreRager)
|
||||||
|
return Attack(otherCoreRager);
|
||||||
|
return botAI->DoSpecificAction("taunt spell", event, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bot->GetVictim() != myCoreRager)
|
||||||
|
return Attack(myCoreRager); // Step 3.3: Attack our Core Rager in case we previously switched in 3.2.
|
||||||
|
|
||||||
|
// Step 4: Prevent Golemagg's Trust on Core Ragers
|
||||||
|
if (myCoreRager->HasAura(SPELL_GOLEMAGGS_TRUST) ||
|
||||||
|
(otherCoreRagerVictim == bot && otherCoreRager->HasAura(SPELL_GOLEMAGGS_TRUST)))
|
||||||
|
{
|
||||||
|
// Step 4.1: Move Core Ragers to dedicated tank position (only if Golemagg is far enough away from said position)
|
||||||
|
float bossDistanceToCoreRagerTankPosition = boss->GetExactDist2d(
|
||||||
|
CORE_RAGER_TANK_POSITION.GetPositionX(), CORE_RAGER_TANK_POSITION.GetPositionY());
|
||||||
|
if (bossDistanceToCoreRagerTankPosition > GOLEMAGGS_TRUST_DISTANCE)
|
||||||
|
{
|
||||||
|
float distanceToTankPosition = bot->GetExactDist2d(CORE_RAGER_TANK_POSITION.GetPositionX(),
|
||||||
|
CORE_RAGER_TANK_POSITION.GetPositionY());
|
||||||
|
if (distanceToTankPosition > CORE_RAGER_STEP_DISTANCE)
|
||||||
|
return MoveUnitToPosition(myCoreRager, CORE_RAGER_TANK_POSITION, CORE_RAGER_STEP_DISTANCE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 4.2: if boss is too close to tank position, or we are already there, move away from Golemagg to try to out-range Golemagg's Trust
|
||||||
|
return MoveAway(boss, CORE_RAGER_STEP_DISTANCE, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,15 +1,16 @@
|
|||||||
#ifndef _PLAYERBOT_RAIDMCACTIONS_H
|
#ifndef _PLAYERBOT_RAIDMCACTIONS_H
|
||||||
#define _PLAYERBOT_RAIDMCACTIONS_H
|
#define _PLAYERBOT_RAIDMCACTIONS_H
|
||||||
|
|
||||||
|
#include "AttackAction.h"
|
||||||
#include "MovementActions.h"
|
#include "MovementActions.h"
|
||||||
#include "PlayerbotAI.h"
|
#include "PlayerbotAI.h"
|
||||||
#include "Playerbots.h"
|
#include "Playerbots.h"
|
||||||
|
|
||||||
class McCheckShouldMoveFromGroupAction : public Action
|
class McMoveFromGroupAction : public MovementAction
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
McCheckShouldMoveFromGroupAction(PlayerbotAI* botAI, std::string const name = "mc check should move from group")
|
McMoveFromGroupAction(PlayerbotAI* botAI, std::string const name = "mc move from group")
|
||||||
: Action(botAI, name) {}
|
: MovementAction(botAI, name) {}
|
||||||
bool Execute(Event event) override;
|
bool Execute(Event event) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -21,4 +22,46 @@ public:
|
|||||||
bool Execute(Event event) override;
|
bool Execute(Event event) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class McShazzrahMoveAwayAction : public MovementAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
McShazzrahMoveAwayAction(PlayerbotAI* botAI, std::string const name = "mc shazzrah move away")
|
||||||
|
: MovementAction(botAI, name) {}
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class McGolemaggMarkBossAction : public Action
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
McGolemaggMarkBossAction(PlayerbotAI* botAI, std::string const name = "mc golemagg mark boss")
|
||||||
|
: Action(botAI, name) {};
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class McGolemaggTankAction : public AttackAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
McGolemaggTankAction(PlayerbotAI* botAI, std::string const name)
|
||||||
|
: AttackAction(botAI, name) {}
|
||||||
|
protected:
|
||||||
|
bool MoveUnitToPosition(Unit* target, const Position& tankPosition, float maxDistance, float stepDistance = 3.0f);
|
||||||
|
bool FindCoreRagers(Unit*& coreRager1, Unit*& coreRager2) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
class McGolemaggMainTankAttackGolemaggAction : public McGolemaggTankAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
McGolemaggMainTankAttackGolemaggAction(PlayerbotAI* botAI, std::string const name = "mc golemagg main tank attack golemagg")
|
||||||
|
: McGolemaggTankAction(botAI, name) {};
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class McGolemaggAssistTankAttackCoreRagerAction : public McGolemaggTankAction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
McGolemaggAssistTankAttackCoreRagerAction(PlayerbotAI* botAI, std::string const name = "mc golemagg assist tank attack core rager")
|
||||||
|
: McGolemaggTankAction(botAI, name) {};
|
||||||
|
bool Execute(Event event) override;
|
||||||
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
22
src/strategy/raids/moltencore/RaidMcHelpers.h
Normal file
22
src/strategy/raids/moltencore/RaidMcHelpers.h
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
#ifndef _PLAYERBOT_RAIDMCHELPERS_H
|
||||||
|
#define _PLAYERBOT_RAIDMCHELPERS_H
|
||||||
|
|
||||||
|
namespace MoltenCoreHelpers
|
||||||
|
{
|
||||||
|
enum MoltenCoreNPCs
|
||||||
|
{
|
||||||
|
// Golemagg
|
||||||
|
NPC_CORE_RAGER = 11672,
|
||||||
|
};
|
||||||
|
enum MoltenCoreSpells
|
||||||
|
{
|
||||||
|
// Baron Geddon
|
||||||
|
SPELL_INFERNO = 19695,
|
||||||
|
SPELL_LIVING_BOMB = 20475,
|
||||||
|
|
||||||
|
// Golemagg
|
||||||
|
SPELL_GOLEMAGGS_TRUST = 20553,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
117
src/strategy/raids/moltencore/RaidMcMultipliers.cpp
Normal file
117
src/strategy/raids/moltencore/RaidMcMultipliers.cpp
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
#include "RaidMcMultipliers.h"
|
||||||
|
|
||||||
|
#include "Playerbots.h"
|
||||||
|
#include "ChooseTargetActions.h"
|
||||||
|
#include "GenericSpellActions.h"
|
||||||
|
#include "DruidActions.h"
|
||||||
|
#include "HunterActions.h"
|
||||||
|
#include "PaladinActions.h"
|
||||||
|
#include "ShamanActions.h"
|
||||||
|
#include "WarriorActions.h"
|
||||||
|
#include "DKActions.h"
|
||||||
|
#include "RaidMcActions.h"
|
||||||
|
#include "RaidMcHelpers.h"
|
||||||
|
|
||||||
|
using namespace MoltenCoreHelpers;
|
||||||
|
|
||||||
|
static bool IsDpsBotWithAoeAction(Player* bot, Action* action)
|
||||||
|
{
|
||||||
|
if (PlayerbotAI::IsDps(bot))
|
||||||
|
{
|
||||||
|
if (dynamic_cast<DpsAoeAction*>(action) || dynamic_cast<CastConsecrationAction*>(action) ||
|
||||||
|
dynamic_cast<CastStarfallAction*>(action) || dynamic_cast<CastWhirlwindAction*>(action) ||
|
||||||
|
dynamic_cast<CastMagmaTotemAction*>(action) || dynamic_cast<CastExplosiveTrapAction*>(action) ||
|
||||||
|
dynamic_cast<CastDeathAndDecayAction*>(action))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (auto castSpellAction = dynamic_cast<CastSpellAction*>(action))
|
||||||
|
{
|
||||||
|
if (castSpellAction->getThreatType() == Action::ActionThreatType::Aoe)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
float GarrDisableDpsAoeMultiplier::GetValue(Action* action)
|
||||||
|
{
|
||||||
|
if (AI_VALUE2(Unit*, "find target", "garr"))
|
||||||
|
{
|
||||||
|
if (IsDpsBotWithAoeAction(bot, action))
|
||||||
|
return 0.0f;
|
||||||
|
}
|
||||||
|
return 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool IsAllowedGeddonMovementAction(Action* action)
|
||||||
|
{
|
||||||
|
if (dynamic_cast<MovementAction*>(action) &&
|
||||||
|
!dynamic_cast<McMoveFromGroupAction*>(action) &&
|
||||||
|
!dynamic_cast<McMoveFromBaronGeddonAction*>(action))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (dynamic_cast<CastReachTargetSpellAction*>(action))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
float BaronGeddonAbilityMultiplier::GetValue(Action* action)
|
||||||
|
{
|
||||||
|
if (Unit* boss = AI_VALUE2(Unit*, "find target", "baron geddon"))
|
||||||
|
{
|
||||||
|
if (boss->HasAura(SPELL_INFERNO))
|
||||||
|
{
|
||||||
|
if (!IsAllowedGeddonMovementAction(action))
|
||||||
|
return 0.0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// No check for Baron Geddon, because bots may have the bomb even after Geddon died.
|
||||||
|
if (bot->HasAura(SPELL_LIVING_BOMB))
|
||||||
|
{
|
||||||
|
if (!IsAllowedGeddonMovementAction(action))
|
||||||
|
return 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool IsSingleLivingTankInGroup(Player* bot)
|
||||||
|
{
|
||||||
|
if (Group* group = bot->GetGroup())
|
||||||
|
{
|
||||||
|
for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next())
|
||||||
|
{
|
||||||
|
Player* member = itr->GetSource();
|
||||||
|
if (!member || !member->IsAlive() || member == bot)
|
||||||
|
continue;
|
||||||
|
if (PlayerbotAI::IsTank(member))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
float GolemaggMultiplier::GetValue(Action* action)
|
||||||
|
{
|
||||||
|
if (AI_VALUE2(Unit*, "find target", "golemagg the incinerator"))
|
||||||
|
{
|
||||||
|
if (PlayerbotAI::IsTank(bot) && IsSingleLivingTankInGroup(bot))
|
||||||
|
{
|
||||||
|
// Only one tank => Pick up Golemagg and the two Core Ragers
|
||||||
|
if (dynamic_cast<McGolemaggMainTankAttackGolemaggAction*>(action) ||
|
||||||
|
dynamic_cast<McGolemaggAssistTankAttackCoreRagerAction*>(action))
|
||||||
|
return 0.0f;
|
||||||
|
}
|
||||||
|
if (PlayerbotAI::IsAssistTank(bot))
|
||||||
|
{
|
||||||
|
// The first two assist tanks manage the Core Ragers. The remaining assist tanks attack the boss.
|
||||||
|
if (dynamic_cast<TankAssistAction*>(action))
|
||||||
|
return 0.0f;
|
||||||
|
}
|
||||||
|
if (IsDpsBotWithAoeAction(bot, action))
|
||||||
|
return 0.0f;
|
||||||
|
}
|
||||||
|
return 1.0f;
|
||||||
|
}
|
||||||
27
src/strategy/raids/moltencore/RaidMcMultipliers.h
Normal file
27
src/strategy/raids/moltencore/RaidMcMultipliers.h
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
#ifndef _PLAYERBOT_RAIDMCMULTIPLIERS_H
|
||||||
|
#define _PLAYERBOT_RAIDMCMULTIPLIERS_H
|
||||||
|
|
||||||
|
#include "Multiplier.h"
|
||||||
|
|
||||||
|
class GarrDisableDpsAoeMultiplier : public Multiplier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
GarrDisableDpsAoeMultiplier(PlayerbotAI* botAI) : Multiplier(botAI, "garr disable dps aoe multiplier") {}
|
||||||
|
float GetValue(Action* action) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class BaronGeddonAbilityMultiplier : public Multiplier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
BaronGeddonAbilityMultiplier(PlayerbotAI* botAI) : Multiplier(botAI, "baron geddon ability multiplier") {}
|
||||||
|
float GetValue(Action* action) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class GolemaggMultiplier : public Multiplier
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
GolemaggMultiplier(PlayerbotAI* botAI) : Multiplier(botAI, "golemagg multiplier") {}
|
||||||
|
float GetValue(Action* action) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -1,13 +1,81 @@
|
|||||||
#include "RaidMcStrategy.h"
|
#include "RaidMcStrategy.h"
|
||||||
|
|
||||||
|
#include "RaidMcMultipliers.h"
|
||||||
#include "Strategy.h"
|
#include "Strategy.h"
|
||||||
|
|
||||||
void RaidMcStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
void RaidMcStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||||
{
|
{
|
||||||
|
// Lucifron
|
||||||
|
triggers.push_back(
|
||||||
|
new TriggerNode("mc lucifron shadow resistance",
|
||||||
|
NextAction::array(0, new NextAction("mc lucifron shadow resistance", ACTION_RAID), nullptr)));
|
||||||
|
|
||||||
|
// Magmadar
|
||||||
|
// TODO: Fear ward / tremor totem, or general anti-fear strat development. Same as King Dred (Drak'Tharon) and faction commander (Nexus).
|
||||||
|
triggers.push_back(
|
||||||
|
new TriggerNode("mc magmadar fire resistance",
|
||||||
|
NextAction::array(0, new NextAction("mc magmadar fire resistance", ACTION_RAID), nullptr)));
|
||||||
|
|
||||||
|
// Gehennas
|
||||||
|
triggers.push_back(
|
||||||
|
new TriggerNode("mc gehennas shadow resistance",
|
||||||
|
NextAction::array(0, new NextAction("mc gehennas shadow resistance", ACTION_RAID), nullptr)));
|
||||||
|
|
||||||
|
// Garr
|
||||||
|
triggers.push_back(
|
||||||
|
new TriggerNode("mc garr fire resistance",
|
||||||
|
NextAction::array(0, new NextAction("mc garr fire resistance", ACTION_RAID), nullptr)));
|
||||||
|
|
||||||
|
// Baron Geddon
|
||||||
|
triggers.push_back(
|
||||||
|
new TriggerNode("mc baron geddon fire resistance",
|
||||||
|
NextAction::array(0, new NextAction("mc baron geddon fire resistance", ACTION_RAID), nullptr)));
|
||||||
triggers.push_back(
|
triggers.push_back(
|
||||||
new TriggerNode("mc living bomb debuff",
|
new TriggerNode("mc living bomb debuff",
|
||||||
NextAction::array(0, new NextAction("mc check should move from group", ACTION_RAID), nullptr)));
|
NextAction::array(0, new NextAction("mc move from group", ACTION_RAID), nullptr)));
|
||||||
triggers.push_back(
|
triggers.push_back(
|
||||||
new TriggerNode("mc baron geddon inferno",
|
new TriggerNode("mc baron geddon inferno",
|
||||||
NextAction::array(0, new NextAction("mc move from baron geddon", ACTION_RAID), nullptr)));
|
NextAction::array(0, new NextAction("mc move from baron geddon", ACTION_RAID), nullptr)));
|
||||||
|
|
||||||
|
// Shazzrah
|
||||||
|
triggers.push_back(
|
||||||
|
new TriggerNode("mc shazzrah ranged",
|
||||||
|
NextAction::array(0, new NextAction("mc shazzrah move away", ACTION_RAID), nullptr)));
|
||||||
|
|
||||||
|
// Sulfuron Harbinger
|
||||||
|
// Alternatively, shadow resistance is also possible.
|
||||||
|
triggers.push_back(
|
||||||
|
new TriggerNode("mc sulfuron harbinger fire resistance",
|
||||||
|
NextAction::array(0, new NextAction("mc sulfuron harbinger fire resistance", ACTION_RAID), nullptr)));
|
||||||
|
|
||||||
|
// Golemagg the Incinerator
|
||||||
|
triggers.push_back(
|
||||||
|
new TriggerNode("mc golemagg fire resistance",
|
||||||
|
NextAction::array(0, new NextAction("mc golemagg fire resistance", ACTION_RAID), nullptr)));
|
||||||
|
triggers.push_back(
|
||||||
|
new TriggerNode("mc golemagg mark boss",
|
||||||
|
NextAction::array(0, new NextAction("mc golemagg mark boss", ACTION_RAID), nullptr)));
|
||||||
|
triggers.push_back(
|
||||||
|
new TriggerNode("mc golemagg is main tank",
|
||||||
|
NextAction::array(0, new NextAction("mc golemagg main tank attack golemagg", ACTION_RAID), nullptr)));
|
||||||
|
triggers.push_back(
|
||||||
|
new TriggerNode("mc golemagg is assist tank",
|
||||||
|
NextAction::array(0, new NextAction("mc golemagg assist tank attack core rager", ACTION_RAID), nullptr)));
|
||||||
|
|
||||||
|
// Majordomo Executus
|
||||||
|
triggers.push_back(
|
||||||
|
new TriggerNode("mc majordomo shadow resistance",
|
||||||
|
NextAction::array(0, new NextAction("mc majordomo shadow resistance", ACTION_RAID), nullptr)));
|
||||||
|
|
||||||
|
// Ragnaros
|
||||||
|
triggers.push_back(
|
||||||
|
new TriggerNode("mc ragnaros fire resistance",
|
||||||
|
NextAction::array(0, new NextAction("mc ragnaros fire resistance", ACTION_RAID), nullptr)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void RaidMcStrategy::InitMultipliers(std::vector<Multiplier*>& multipliers)
|
||||||
|
{
|
||||||
|
multipliers.push_back(new GarrDisableDpsAoeMultiplier(botAI));
|
||||||
|
multipliers.push_back(new BaronGeddonAbilityMultiplier(botAI));
|
||||||
|
multipliers.push_back(new GolemaggMultiplier(botAI));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,10 +8,10 @@
|
|||||||
class RaidMcStrategy : public Strategy
|
class RaidMcStrategy : public Strategy
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
RaidMcStrategy(PlayerbotAI* ai) : Strategy(ai) {}
|
RaidMcStrategy(PlayerbotAI* botAI) : Strategy(botAI) {}
|
||||||
virtual std::string const getName() override { return "mc"; }
|
std::string const getName() override { return "moltencore"; }
|
||||||
virtual void InitTriggers(std::vector<TriggerNode*>& triggers) override;
|
void InitTriggers(std::vector<TriggerNode*>& triggers) override;
|
||||||
// virtual void InitMultipliers(std::vector<Multiplier*> &multipliers) override;
|
void InitMultipliers(std::vector<Multiplier*> &multipliers) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -10,13 +10,39 @@ class RaidMcTriggerContext : public NamedObjectContext<Trigger>
|
|||||||
public:
|
public:
|
||||||
RaidMcTriggerContext()
|
RaidMcTriggerContext()
|
||||||
{
|
{
|
||||||
|
creators["mc lucifron shadow resistance"] = &RaidMcTriggerContext::lucifron_shadow_resistance;
|
||||||
|
creators["mc magmadar fire resistance"] = &RaidMcTriggerContext::magmadar_fire_resistance;
|
||||||
|
creators["mc gehennas shadow resistance"] = &RaidMcTriggerContext::gehennas_shadow_resistance;
|
||||||
|
creators["mc garr fire resistance"] = &RaidMcTriggerContext::garr_fire_resistance;
|
||||||
|
creators["mc baron geddon fire resistance"] = &RaidMcTriggerContext::baron_geddon_fire_resistance;
|
||||||
creators["mc living bomb debuff"] = &RaidMcTriggerContext::living_bomb_debuff;
|
creators["mc living bomb debuff"] = &RaidMcTriggerContext::living_bomb_debuff;
|
||||||
creators["mc baron geddon inferno"] = &RaidMcTriggerContext::baron_geddon_inferno;
|
creators["mc baron geddon inferno"] = &RaidMcTriggerContext::baron_geddon_inferno;
|
||||||
|
creators["mc shazzrah ranged"] = &RaidMcTriggerContext::shazzrah_ranged;
|
||||||
|
creators["mc sulfuron harbinger fire resistance"] = &RaidMcTriggerContext::sulfuron_harbinger_fire_resistance;
|
||||||
|
creators["mc golemagg fire resistance"] = &RaidMcTriggerContext::golemagg_fire_resistance;
|
||||||
|
creators["mc golemagg mark boss"] = &RaidMcTriggerContext::golemagg_mark_boss;
|
||||||
|
creators["mc golemagg is main tank"] = &RaidMcTriggerContext::golemagg_is_main_tank;
|
||||||
|
creators["mc golemagg is assist tank"] = &RaidMcTriggerContext::golemagg_is_assist_tank;
|
||||||
|
creators["mc majordomo shadow resistance"] = &RaidMcTriggerContext::majordomo_shadow_resistance;
|
||||||
|
creators["mc ragnaros fire resistance"] = &RaidMcTriggerContext::ragnaros_fire_resistance;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static Trigger* living_bomb_debuff(PlayerbotAI* ai) { return new McLivingBombDebuffTrigger(ai); }
|
static Trigger* lucifron_shadow_resistance(PlayerbotAI* botAI) { return new BossShadowResistanceTrigger(botAI, "lucifron"); }
|
||||||
static Trigger* baron_geddon_inferno(PlayerbotAI* ai) { return new McBaronGeddonInfernoTrigger(ai); }
|
static Trigger* magmadar_fire_resistance(PlayerbotAI* botAI) { return new BossFireResistanceTrigger(botAI, "magmadar"); }
|
||||||
|
static Trigger* gehennas_shadow_resistance(PlayerbotAI* botAI) { return new BossShadowResistanceTrigger(botAI, "gehennas"); }
|
||||||
|
static Trigger* garr_fire_resistance(PlayerbotAI* botAI) { return new BossFireResistanceTrigger(botAI, "garr"); }
|
||||||
|
static Trigger* baron_geddon_fire_resistance(PlayerbotAI* botAI) { return new BossFireResistanceTrigger(botAI, "baron geddon"); }
|
||||||
|
static Trigger* living_bomb_debuff(PlayerbotAI* botAI) { return new McLivingBombDebuffTrigger(botAI); }
|
||||||
|
static Trigger* baron_geddon_inferno(PlayerbotAI* botAI) { return new McBaronGeddonInfernoTrigger(botAI); }
|
||||||
|
static Trigger* shazzrah_ranged(PlayerbotAI* botAI) { return new McShazzrahRangedTrigger(botAI); }
|
||||||
|
static Trigger* sulfuron_harbinger_fire_resistance(PlayerbotAI* botAI) { return new BossFireResistanceTrigger(botAI, "sulfuron harbinger"); }
|
||||||
|
static Trigger* golemagg_fire_resistance(PlayerbotAI* botAI) { return new BossFireResistanceTrigger(botAI, "golemagg the incinerator"); }
|
||||||
|
static Trigger* golemagg_mark_boss(PlayerbotAI* botAI) { return new McGolemaggMarkBossTrigger(botAI); }
|
||||||
|
static Trigger* golemagg_is_main_tank(PlayerbotAI* botAI) { return new McGolemaggIsMainTankTrigger(botAI); }
|
||||||
|
static Trigger* golemagg_is_assist_tank(PlayerbotAI* botAI) { return new McGolemaggIsAssistTankTrigger(botAI); }
|
||||||
|
static Trigger* majordomo_shadow_resistance(PlayerbotAI* botAI) { return new BossShadowResistanceTrigger(botAI, "majordomo executus"); }
|
||||||
|
static Trigger* ragnaros_fire_resistance(PlayerbotAI* botAI) { return new BossFireResistanceTrigger(botAI, "ragnaros"); }
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -1,22 +1,40 @@
|
|||||||
#include "RaidMcTriggers.h"
|
#include "RaidMcTriggers.h"
|
||||||
|
|
||||||
#include "SharedDefines.h"
|
#include "SharedDefines.h"
|
||||||
|
#include "RaidMcHelpers.h"
|
||||||
|
|
||||||
|
using namespace MoltenCoreHelpers;
|
||||||
|
|
||||||
bool McLivingBombDebuffTrigger::IsActive()
|
bool McLivingBombDebuffTrigger::IsActive()
|
||||||
{
|
{
|
||||||
// if bot has barron geddon's living bomb, we need to add strat, otherwise we need to remove
|
// No check for Baron Geddon, because bots may have the bomb even after Geddon died.
|
||||||
// only do when fighting baron geddon (to avoid modifying strat set by player outside this fight)
|
return bot->HasAura(SPELL_LIVING_BOMB);
|
||||||
if (Unit* boss = AI_VALUE2(Unit*, "find target", "baron geddon"))
|
|
||||||
{
|
|
||||||
if (boss->IsInCombat())
|
|
||||||
return bot->HasAura(20475) != botAI->HasStrategy("move from group", BotState::BOT_STATE_COMBAT);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool McBaronGeddonInfernoTrigger::IsActive()
|
bool McBaronGeddonInfernoTrigger::IsActive()
|
||||||
{
|
{
|
||||||
if (Unit* boss = AI_VALUE2(Unit*, "find target", "baron geddon"))
|
if (Unit* boss = AI_VALUE2(Unit*, "find target", "baron geddon"))
|
||||||
return boss->HasAura(19695);
|
return boss->HasAura(SPELL_INFERNO);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool McShazzrahRangedTrigger::IsActive()
|
||||||
|
{
|
||||||
|
return AI_VALUE2(Unit*, "find target", "shazzrah") && PlayerbotAI::IsRanged(bot);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool McGolemaggMarkBossTrigger::IsActive()
|
||||||
|
{
|
||||||
|
// any tank may mark the boss
|
||||||
|
return AI_VALUE2(Unit*, "find target", "golemagg the incinerator") && PlayerbotAI::IsTank(bot);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool McGolemaggIsMainTankTrigger::IsActive()
|
||||||
|
{
|
||||||
|
return AI_VALUE2(Unit*, "find target", "golemagg the incinerator") && PlayerbotAI::IsMainTank(bot);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool McGolemaggIsAssistTankTrigger::IsActive()
|
||||||
|
{
|
||||||
|
return AI_VALUE2(Unit*, "find target", "golemagg the incinerator") && PlayerbotAI::IsAssistTank(bot);
|
||||||
|
}
|
||||||
|
|||||||
@@ -19,4 +19,32 @@ public:
|
|||||||
bool IsActive() override;
|
bool IsActive() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class McShazzrahRangedTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
McShazzrahRangedTrigger(PlayerbotAI* botAI) : Trigger(botAI, "mc shazzrah ranged") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class McGolemaggMarkBossTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
McGolemaggMarkBossTrigger(PlayerbotAI* botAI) : Trigger(botAI, "mc golemagg mark boss") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class McGolemaggIsMainTankTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
McGolemaggIsMainTankTrigger(PlayerbotAI* botAI) : Trigger(botAI, "mc golemagg is main tank") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class McGolemaggIsAssistTankTrigger : public Trigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
McGolemaggIsAssistTankTrigger(PlayerbotAI* botAI) : Trigger(botAI, "mc golemagg is assist tank") {}
|
||||||
|
bool IsActive() override;
|
||||||
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -22,8 +22,8 @@ bool UnstealthTrigger::IsActive()
|
|||||||
return botAI->HasAura("stealth", bot) && !AI_VALUE(uint8, "attacker count") &&
|
return botAI->HasAura("stealth", bot) && !AI_VALUE(uint8, "attacker count") &&
|
||||||
(AI_VALUE2(bool, "moving", "self target") &&
|
(AI_VALUE2(bool, "moving", "self target") &&
|
||||||
((botAI->GetMaster() &&
|
((botAI->GetMaster() &&
|
||||||
sServerFacade->IsDistanceGreaterThan(AI_VALUE2(float, "distance", "master target"), 10.0f) &&
|
sServerFacade->IsDistanceGreaterThan(AI_VALUE2(float, "distance", "group leader"), 10.0f) &&
|
||||||
AI_VALUE2(bool, "moving", "master target")) ||
|
AI_VALUE2(bool, "moving", "group leader")) ||
|
||||||
!AI_VALUE(uint8, "attacker count")));
|
!AI_VALUE(uint8, "attacker count")));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -213,7 +213,7 @@ PartyMemberToHealOutOfSpellRangeTrigger::PartyMemberToHealOutOfSpellRangeTrigger
|
|||||||
|
|
||||||
bool FarFromMasterTrigger::IsActive()
|
bool FarFromMasterTrigger::IsActive()
|
||||||
{
|
{
|
||||||
return sServerFacade->IsDistanceGreaterThan(AI_VALUE2(float, "distance", "master target"), distance);
|
return sServerFacade->IsDistanceGreaterThan(AI_VALUE2(float, "distance", "group leader"), distance);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TooCloseToCreatureTrigger::TooCloseToCreature(uint32 creatureId, float range, bool alive)
|
bool TooCloseToCreatureTrigger::TooCloseToCreature(uint32 creatureId, float range, bool alive)
|
||||||
|
|||||||
@@ -33,18 +33,14 @@ GuidVector AttackersValue::Calculate()
|
|||||||
{
|
{
|
||||||
Unit* unit = botAI->GetUnit(target);
|
Unit* unit = botAI->GetUnit(target);
|
||||||
if (unit && IsValidTarget(unit, bot))
|
if (unit && IsValidTarget(unit, bot))
|
||||||
{
|
|
||||||
targets.insert(unit);
|
targets.insert(unit);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (Group* group = bot->GetGroup())
|
if (Group* group = bot->GetGroup())
|
||||||
{
|
{
|
||||||
ObjectGuid skullGuid = group->GetTargetIcon(7);
|
ObjectGuid skullGuid = group->GetTargetIcon(7);
|
||||||
Unit* skullTarget = botAI->GetUnit(skullGuid);
|
Unit* skullTarget = botAI->GetUnit(skullGuid);
|
||||||
if (skullTarget && IsValidTarget(skullTarget, bot))
|
if (skullTarget && IsValidTarget(skullTarget, bot))
|
||||||
{
|
|
||||||
targets.insert(skullTarget);
|
targets.insert(skullTarget);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (Unit* unit : targets)
|
for (Unit* unit : targets)
|
||||||
@@ -61,9 +57,7 @@ GuidVector AttackersValue::Calculate()
|
|||||||
{
|
{
|
||||||
Unit* unit = botAI->GetUnit(guid);
|
Unit* unit = botAI->GetUnit(guid);
|
||||||
if (unit && unit->IsPlayer() && IsValidTarget(unit, bot))
|
if (unit && unit->IsPlayer() && IsValidTarget(unit, bot))
|
||||||
{
|
|
||||||
result.push_back(unit->GetGUID());
|
result.push_back(unit->GetGUID());
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -107,13 +101,11 @@ void AttackersValue::AddAttackersOf(Player* player, std::unordered_set<Unit*>& t
|
|||||||
{
|
{
|
||||||
ThreatMgr* threatMgr = ref->GetSource();
|
ThreatMgr* threatMgr = ref->GetSource();
|
||||||
Unit* attacker = threatMgr->GetOwner();
|
Unit* attacker = threatMgr->GetOwner();
|
||||||
Unit* victim = attacker->GetVictim();
|
|
||||||
|
|
||||||
if (player->IsValidAttackTarget(attacker) &&
|
if (player->IsValidAttackTarget(attacker) &&
|
||||||
player->GetDistance2d(attacker) < sPlayerbotAIConfig->sightDistance)
|
player->GetDistance2d(attacker) < sPlayerbotAIConfig->sightDistance)
|
||||||
{
|
|
||||||
targets.insert(attacker);
|
targets.insert(attacker);
|
||||||
}
|
|
||||||
ref = ref->next();
|
ref = ref->next();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -142,57 +134,108 @@ bool AttackersValue::hasRealThreat(Unit* attacker)
|
|||||||
(attacker->GetThreatMgr().getCurrentVictim() || dynamic_cast<Player*>(attacker));
|
(attacker->GetThreatMgr().getCurrentVictim() || dynamic_cast<Player*>(attacker));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AttackersValue::IsPossibleTarget(Unit* attacker, Player* bot, float range)
|
bool AttackersValue::IsPossibleTarget(Unit* attacker, Player* bot, float /*range*/)
|
||||||
{
|
{
|
||||||
Creature* c = attacker->ToCreature();
|
|
||||||
bool rti = false;
|
|
||||||
if (attacker && bot->GetGroup())
|
|
||||||
rti = bot->GetGroup()->GetTargetIcon(7) == attacker->GetGUID();
|
|
||||||
|
|
||||||
PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot);
|
PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot);
|
||||||
|
if (!botAI)
|
||||||
|
return false;
|
||||||
|
|
||||||
bool leaderHasThreat = false;
|
// Basic check
|
||||||
if (attacker && bot->GetGroup() && botAI->GetMaster())
|
if (!attacker)
|
||||||
leaderHasThreat = attacker->GetThreatMgr().GetThreat(botAI->GetMaster());
|
return false;
|
||||||
|
|
||||||
bool isMemberBotGroup = false;
|
|
||||||
if (bot->GetGroup() && botAI->GetMaster())
|
|
||||||
{
|
|
||||||
PlayerbotAI* masterBotAI = GET_PLAYERBOT_AI(botAI->GetMaster());
|
|
||||||
if (masterBotAI && !masterBotAI->IsRealPlayer())
|
|
||||||
isMemberBotGroup = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// bool inCannon = botAI->IsInVehicle(false, true);
|
// bool inCannon = botAI->IsInVehicle(false, true);
|
||||||
// bool enemy = botAI->GetAiObjectContext()->GetValue<Unit*>("enemy player target")->Get();
|
// bool enemy = botAI->GetAiObjectContext()->GetValue<Unit*>("enemy player target")->Get();
|
||||||
|
|
||||||
return attacker && attacker->IsVisible() && attacker->IsInWorld() && attacker->GetMapId() == bot->GetMapId() &&
|
// Validity checks
|
||||||
!attacker->isDead() &&
|
if (!attacker->IsVisible() || !attacker->IsInWorld() || attacker->GetMapId() != bot->GetMapId())
|
||||||
!attacker->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NON_ATTACKABLE_2) &&
|
return false;
|
||||||
// (inCannon || !attacker->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE)) &&
|
|
||||||
// attacker->CanSeeOrDetect(bot) &&
|
if (attacker->isDead() || attacker->HasSpiritOfRedemptionAura())
|
||||||
// !(attacker->HasUnitState(UNIT_STATE_STUNNED) && botAI->HasAura("shackle undead", attacker)) &&
|
return false;
|
||||||
// !((attacker->IsPolymorphed() || botAI->HasAura("sap", attacker) || /*attacker->IsCharmed() ||*/
|
|
||||||
// attacker->isFeared()) && !rti) &&
|
// Flag checks
|
||||||
/*!sServerFacade->IsInRoots(attacker) &&*/
|
if (attacker->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NON_ATTACKABLE_2))
|
||||||
!attacker->IsFriendlyTo(bot) && !attacker->HasSpiritOfRedemptionAura() &&
|
return false;
|
||||||
// !(attacker->GetGUID().IsPet() && enemy) &&
|
|
||||||
!(attacker->GetCreatureType() == CREATURE_TYPE_CRITTER && !attacker->IsInCombat()) &&
|
if (attacker->HasUnitFlag(UNIT_FLAG_IMMUNE_TO_PC) || attacker->HasUnitFlag(UNIT_FLAG_NOT_SELECTABLE))
|
||||||
!attacker->HasUnitFlag(UNIT_FLAG_IMMUNE_TO_PC) && !attacker->HasUnitFlag(UNIT_FLAG_NOT_SELECTABLE) &&
|
return false;
|
||||||
bot->CanSeeOrDetect(attacker) &&
|
|
||||||
!(sPlayerbotAIConfig->IsPvpProhibited(attacker->GetZoneId(), attacker->GetAreaId()) &&
|
// Relationship checks
|
||||||
(attacker->GetGUID().IsPlayer() || attacker->GetGUID().IsPet())) &&
|
if (attacker->IsFriendlyTo(bot))
|
||||||
!(attacker->IsPlayer() && !attacker->IsPvP() && !attacker->IsFFAPvP() &&
|
return false;
|
||||||
(!bot->duel || bot->duel->Opponent != attacker)) &&
|
|
||||||
(!c ||
|
// Critter exception
|
||||||
(!c->IsInEvadeMode() &&
|
if (attacker->GetCreatureType() == CREATURE_TYPE_CRITTER && !attacker->IsInCombat())
|
||||||
((!isMemberBotGroup && botAI->HasStrategy("attack tagged", BOT_STATE_NON_COMBAT)) || leaderHasThreat ||
|
return false;
|
||||||
(!c->hasLootRecipient() &&
|
|
||||||
(!c->GetVictim() ||
|
// Visibility check
|
||||||
(c->GetVictim() &&
|
if (!bot->CanSeeOrDetect(attacker))
|
||||||
((!c->GetVictim()->IsPlayer() || bot->IsInSameGroupWith(c->GetVictim()->ToPlayer())) ||
|
return false;
|
||||||
(botAI->GetMaster() && c->GetVictim() == botAI->GetMaster()))))) ||
|
|
||||||
c->isTappedBy(bot))));
|
// PvP prohibition checks (skip for duels)
|
||||||
|
if ((attacker->GetGUID().IsPlayer() || attacker->GetGUID().IsPet()) &&
|
||||||
|
(!bot->duel || bot->duel->Opponent != attacker) &&
|
||||||
|
(sPlayerbotAIConfig->IsPvpProhibited(attacker->GetZoneId(), attacker->GetAreaId()) ||
|
||||||
|
sPlayerbotAIConfig->IsPvpProhibited(bot->GetZoneId(), bot->GetAreaId())))
|
||||||
|
{
|
||||||
|
// This will stop aggresive pets from starting an attack.
|
||||||
|
// This will stop currently attacking pets from continuing their attack.
|
||||||
|
// This will first require the bot to change from a combat strat. It will
|
||||||
|
// not be reached if the bot only switches targets, including NPC targets.
|
||||||
|
for (Unit::ControlSet::const_iterator itr = bot->m_Controlled.begin();
|
||||||
|
itr != bot->m_Controlled.end(); ++itr)
|
||||||
|
{
|
||||||
|
Creature* creature = dynamic_cast<Creature*>(*itr);
|
||||||
|
if (creature && creature->GetVictim() == attacker)
|
||||||
|
{
|
||||||
|
creature->AttackStop();
|
||||||
|
if (CharmInfo* charmInfo = creature->GetCharmInfo())
|
||||||
|
charmInfo->SetIsCommandAttack(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unflagged player check
|
||||||
|
if (attacker->IsPlayer() && !attacker->IsPvP() && !attacker->IsFFAPvP() &&
|
||||||
|
(!bot->duel || bot->duel->Opponent != attacker))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Creature-specific checks
|
||||||
|
Creature* c = attacker->ToCreature();
|
||||||
|
if (c)
|
||||||
|
{
|
||||||
|
if (c->IsInEvadeMode())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
bool leaderHasThreat = false;
|
||||||
|
if (bot->GetGroup() && botAI->GetMaster())
|
||||||
|
leaderHasThreat = attacker->GetThreatMgr().GetThreat(botAI->GetMaster());
|
||||||
|
|
||||||
|
bool isMemberBotGroup = false;
|
||||||
|
if (bot->GetGroup() && botAI->GetMaster())
|
||||||
|
{
|
||||||
|
PlayerbotAI* masterBotAI = GET_PLAYERBOT_AI(botAI->GetMaster());
|
||||||
|
if (masterBotAI && !masterBotAI->IsRealPlayer())
|
||||||
|
isMemberBotGroup = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool canAttack = (!isMemberBotGroup && botAI->HasStrategy("attack tagged", BOT_STATE_NON_COMBAT)) ||
|
||||||
|
leaderHasThreat ||
|
||||||
|
(!c->hasLootRecipient() &&
|
||||||
|
(!c->GetVictim() ||
|
||||||
|
(c->GetVictim() &&
|
||||||
|
((!c->GetVictim()->IsPlayer() || bot->IsInSameGroupWith(c->GetVictim()->ToPlayer())) ||
|
||||||
|
(botAI->GetMaster() && c->GetVictim() == botAI->GetMaster()))))) ||
|
||||||
|
c->isTappedBy(bot);
|
||||||
|
|
||||||
|
if (!canAttack)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AttackersValue::IsValidTarget(Unit* attacker, Player* bot)
|
bool AttackersValue::IsValidTarget(Unit* attacker, Player* bot)
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ class MeleeFormation : public FollowFormation
|
|||||||
public:
|
public:
|
||||||
MeleeFormation(PlayerbotAI* botAI) : FollowFormation(botAI, "melee") {}
|
MeleeFormation(PlayerbotAI* botAI) : FollowFormation(botAI, "melee") {}
|
||||||
|
|
||||||
std::string const GetTargetName() override { return "master target"; }
|
std::string const GetTargetName() override { return "group leader"; }
|
||||||
};
|
};
|
||||||
|
|
||||||
class QueueFormation : public FollowFormation
|
class QueueFormation : public FollowFormation
|
||||||
|
|||||||
@@ -3,8 +3,8 @@
|
|||||||
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "MasterTargetValue.h"
|
#include "GroupLeaderValue.h"
|
||||||
|
|
||||||
#include "Playerbots.h"
|
#include "Playerbots.h"
|
||||||
|
|
||||||
Unit* MasterTargetValue::Calculate() { return botAI->GetGroupMaster(); }
|
Unit* GroupLeaderValue::Calculate() { return botAI->GetGroupLeader(); }
|
||||||
@@ -3,18 +3,18 @@
|
|||||||
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
* and/or modify it under version 3 of the License, or (at your option), any later version.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef _PLAYERBOT_MASTERTARGETVALUE_H
|
#ifndef _PLAYERBOT_GROUPLEADERVALUE_H
|
||||||
#define _PLAYERBOT_MASTERTARGETVALUE_H
|
#define _PLAYERBOT_GROUPLEADERVALUE_H
|
||||||
|
|
||||||
#include "Value.h"
|
#include "Value.h"
|
||||||
|
|
||||||
class PlayerbotAI;
|
class PlayerbotAI;
|
||||||
class Unit;
|
class Unit;
|
||||||
|
|
||||||
class MasterTargetValue : public UnitCalculatedValue
|
class GroupLeaderValue : public UnitCalculatedValue
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
MasterTargetValue(PlayerbotAI* botAI, std::string const name = "master target") : UnitCalculatedValue(botAI, name)
|
GroupLeaderValue(PlayerbotAI* botAI, std::string const name = "group leader") : UnitCalculatedValue(botAI, name)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -28,7 +28,7 @@ GuidVector GroupMembersValue::Calculate()
|
|||||||
|
|
||||||
bool IsFollowingPartyValue::Calculate()
|
bool IsFollowingPartyValue::Calculate()
|
||||||
{
|
{
|
||||||
if (botAI->GetGroupMaster() == bot)
|
if (botAI->GetGroupLeader() == bot)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (botAI->HasStrategy("follow", BOT_STATE_NON_COMBAT))
|
if (botAI->HasStrategy("follow", BOT_STATE_NON_COMBAT))
|
||||||
@@ -39,15 +39,15 @@ bool IsFollowingPartyValue::Calculate()
|
|||||||
|
|
||||||
bool IsNearLeaderValue::Calculate()
|
bool IsNearLeaderValue::Calculate()
|
||||||
{
|
{
|
||||||
Player* groupMaster = botAI->GetGroupMaster();
|
Player* groupLeader = botAI->GetGroupLeader();
|
||||||
|
|
||||||
if (!groupMaster)
|
if (!groupLeader)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (groupMaster == bot)
|
if (groupLeader == bot)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
return sServerFacade->GetDistance2d(bot, botAI->GetGroupMaster()) < sPlayerbotAIConfig->sightDistance;
|
return sServerFacade->GetDistance2d(bot, botAI->GetGroupLeader()) < sPlayerbotAIConfig->sightDistance;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BoolANDValue::Calculate()
|
bool BoolANDValue::Calculate()
|
||||||
@@ -154,8 +154,8 @@ bool GroupReadyValue::Calculate()
|
|||||||
|
|
||||||
// We only wait for members that are in range otherwise we might be waiting for bots stuck in dead loops
|
// We only wait for members that are in range otherwise we might be waiting for bots stuck in dead loops
|
||||||
// forever.
|
// forever.
|
||||||
if (botAI->GetGroupMaster() &&
|
if (botAI->GetGroupLeader() &&
|
||||||
sServerFacade->GetDistance2d(member, botAI->GetGroupMaster()) > sPlayerbotAIConfig->sightDistance)
|
sServerFacade->GetDistance2d(member, botAI->GetGroupLeader()) > sPlayerbotAIConfig->sightDistance)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (member->GetHealthPct() < sPlayerbotAIConfig->almostFullHealth)
|
if (member->GetHealthPct() < sPlayerbotAIConfig->almostFullHealth)
|
||||||
|
|||||||
@@ -30,6 +30,7 @@
|
|||||||
#include "Formations.h"
|
#include "Formations.h"
|
||||||
#include "GrindTargetValue.h"
|
#include "GrindTargetValue.h"
|
||||||
#include "GroupValues.h"
|
#include "GroupValues.h"
|
||||||
|
#include "GroupLeaderValue.h"
|
||||||
#include "GuildValues.h"
|
#include "GuildValues.h"
|
||||||
#include "HasAvailableLootValue.h"
|
#include "HasAvailableLootValue.h"
|
||||||
#include "HasTotemValue.h"
|
#include "HasTotemValue.h"
|
||||||
@@ -51,7 +52,6 @@
|
|||||||
#include "LootStrategyValue.h"
|
#include "LootStrategyValue.h"
|
||||||
#include "MaintenanceValues.h"
|
#include "MaintenanceValues.h"
|
||||||
#include "ManaSaveLevelValue.h"
|
#include "ManaSaveLevelValue.h"
|
||||||
#include "MasterTargetValue.h"
|
|
||||||
#include "NearestAdsValue.h"
|
#include "NearestAdsValue.h"
|
||||||
#include "NearestCorpsesValue.h"
|
#include "NearestCorpsesValue.h"
|
||||||
#include "NearestFriendlyPlayersValue.h"
|
#include "NearestFriendlyPlayersValue.h"
|
||||||
@@ -130,7 +130,7 @@ public:
|
|||||||
creators["party member to resurrect"] = &ValueContext::party_member_to_resurrect;
|
creators["party member to resurrect"] = &ValueContext::party_member_to_resurrect;
|
||||||
creators["current target"] = &ValueContext::current_target;
|
creators["current target"] = &ValueContext::current_target;
|
||||||
creators["self target"] = &ValueContext::self_target;
|
creators["self target"] = &ValueContext::self_target;
|
||||||
creators["master target"] = &ValueContext::master;
|
creators["group leader"] = &ValueContext::group_leader;
|
||||||
creators["line target"] = &ValueContext::line_target;
|
creators["line target"] = &ValueContext::line_target;
|
||||||
creators["tank target"] = &ValueContext::tank_target;
|
creators["tank target"] = &ValueContext::tank_target;
|
||||||
creators["dps target"] = &ValueContext::dps_target;
|
creators["dps target"] = &ValueContext::dps_target;
|
||||||
@@ -439,7 +439,7 @@ private:
|
|||||||
static UntypedValue* current_target(PlayerbotAI* botAI) { return new CurrentTargetValue(botAI); }
|
static UntypedValue* current_target(PlayerbotAI* botAI) { return new CurrentTargetValue(botAI); }
|
||||||
static UntypedValue* old_target(PlayerbotAI* botAI) { return new CurrentTargetValue(botAI); }
|
static UntypedValue* old_target(PlayerbotAI* botAI) { return new CurrentTargetValue(botAI); }
|
||||||
static UntypedValue* self_target(PlayerbotAI* botAI) { return new SelfTargetValue(botAI); }
|
static UntypedValue* self_target(PlayerbotAI* botAI) { return new SelfTargetValue(botAI); }
|
||||||
static UntypedValue* master(PlayerbotAI* botAI) { return new MasterTargetValue(botAI); }
|
static UntypedValue* group_leader(PlayerbotAI* botAI) { return new GroupLeaderValue(botAI); }
|
||||||
static UntypedValue* line_target(PlayerbotAI* botAI) { return new LineTargetValue(botAI); }
|
static UntypedValue* line_target(PlayerbotAI* botAI) { return new LineTargetValue(botAI); }
|
||||||
static UntypedValue* tank_target(PlayerbotAI* botAI) { return new TankTargetValue(botAI); }
|
static UntypedValue* tank_target(PlayerbotAI* botAI) { return new TankTargetValue(botAI); }
|
||||||
static UntypedValue* dps_target(PlayerbotAI* botAI) { return new DpsTargetValue(botAI); }
|
static UntypedValue* dps_target(PlayerbotAI* botAI) { return new DpsTargetValue(botAI); }
|
||||||
|
|||||||
Reference in New Issue
Block a user