mirror of
https://github.com/mod-playerbots/mod-playerbots.git
synced 2026-01-28 07:56:24 +00:00
Compare commits
8 Commits
master_v16
...
bb569b4d39
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bb569b4d39 | ||
|
|
dde16674c3 | ||
|
|
e5b2791053 | ||
|
|
353c29dfc4 | ||
|
|
52c3e96641 | ||
|
|
38e2d8584b | ||
|
|
d5dbc4ddd7 | ||
|
|
2424f73bc4 |
@@ -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();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1461,7 +1459,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 +1475,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 +2247,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 +2264,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 +2286,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 +4097,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 +4148,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);
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -82,12 +82,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 +164,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 +186,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 +208,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 +222,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
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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); }
|
||||||
|
|||||||
@@ -84,9 +84,10 @@ bool AttackAction::Attack(Unit* target, bool with_pet /*true*/)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((sPlayerbotAIConfig->IsInPvpProhibitedZone(bot->GetZoneId()) ||
|
// Check if bot OR target is in prohibited zone/area
|
||||||
sPlayerbotAIConfig->IsInPvpProhibitedArea(bot->GetAreaId()))
|
if ((target->IsPlayer() || target->IsPet()) &&
|
||||||
&& (target->IsPlayer() || target->IsPet()))
|
(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.");
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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())
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1102,7 +1102,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 +2663,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)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -25,7 +25,9 @@ bool PetsAction::Execute(Event event)
|
|||||||
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 +54,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 +67,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;
|
||||||
}
|
}
|
||||||
@@ -121,17 +137,31 @@ bool PetsAction::Execute(Event event)
|
|||||||
// 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()))
|
||||||
|
{
|
||||||
|
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 +212,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 +230,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 +271,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 +299,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)));
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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); }
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -107,7 +107,6 @@ 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)
|
||||||
@@ -142,57 +141,107 @@ 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
|
||||||
|
if ((attacker->GetGUID().IsPlayer() || attacker->GetGUID().IsPet()) &&
|
||||||
|
(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