Files
mod-playerbots/src/AiFactory.cpp
2024-02-14 01:01:28 +08:00

679 lines
23 KiB
C++

/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "AiFactory.h"
#include "BattlegroundMgr.h"
#include "Item.h"
#include "PlayerbotAI.h"
#include "Playerbots.h"
#include "Engine.h"
#include "Group.h"
#include "DKAiObjectContext.h"
#include "PriestAiObjectContext.h"
#include "MageAiObjectContext.h"
#include "SharedDefines.h"
#include "WarlockAiObjectContext.h"
#include "WarriorAiObjectContext.h"
#include "ShamanAiObjectContext.h"
#include "PaladinAiObjectContext.h"
#include "DruidAiObjectContext.h"
#include "HunterAiObjectContext.h"
#include "RogueAiObjectContext.h"
AiObjectContext* AiFactory::createAiObjectContext(Player* player, PlayerbotAI* botAI)
{
switch (player->getClass())
{
case CLASS_PRIEST:
return new PriestAiObjectContext(botAI);
case CLASS_MAGE:
return new MageAiObjectContext(botAI);
case CLASS_WARLOCK:
return new WarlockAiObjectContext(botAI);
case CLASS_WARRIOR:
return new WarriorAiObjectContext(botAI);
case CLASS_SHAMAN:
return new ShamanAiObjectContext(botAI);
case CLASS_PALADIN:
return new PaladinAiObjectContext(botAI);
case CLASS_DRUID:
return new DruidAiObjectContext(botAI);
case CLASS_HUNTER:
return new HunterAiObjectContext(botAI);
case CLASS_ROGUE:
return new RogueAiObjectContext(botAI);
case CLASS_DEATH_KNIGHT:
return new DKAiObjectContext(botAI);
}
return new AiObjectContext(botAI);
}
uint8 AiFactory::GetPlayerSpecTab(Player* bot)
{
std::map<uint8, uint32> tabs = GetPlayerSpecTabs(bot);
if (bot->getLevel() >= 10 && ((tabs[0] + tabs[1] + tabs[2]) > 0))
{
int8 tab = -1;
uint32 max = 0;
for (uint32 i = 0; i < uint32(3); i++)
{
if (tab == -1 || max < tabs[i])
{
tab = i;
max = tabs[i];
}
}
return tab;
}
else
{
uint8 tab = 0;
switch (bot->getClass())
{
case CLASS_MAGE:
tab = 1;
break;
case CLASS_PALADIN:
tab = 2;
break;
case CLASS_PRIEST:
tab = 1;
break;
}
return tab;
}
}
std::map<uint8, uint32> AiFactory::GetPlayerSpecTabs(Player* bot)
{
std::map<uint8, uint32> tabs = {{0, 0}, {0, 0}, {0, 0}};
const PlayerTalentMap& talentMap = bot->GetTalentMap();
for (PlayerTalentMap::const_iterator i = talentMap.begin(); i != talentMap.end(); ++i)
{
uint32 spellId = i->first;
if ((bot->GetActiveSpecMask() & i->second->specMask) == 0) {
continue;
}
TalentSpellPos const* talentPos = GetTalentSpellPos(spellId);
if(!talentPos)
continue;
TalentEntry const* talentInfo = sTalentStore.LookupEntry(talentPos->talent_id);
if (!talentInfo)
continue;
uint32 const* talentTabIds = GetTalentTabPages(bot->getClass());
if (talentInfo->TalentTab == talentTabIds[0]) tabs[0]++;
if (talentInfo->TalentTab == talentTabIds[1]) tabs[1]++;
if (talentInfo->TalentTab == talentTabIds[2]) tabs[2]++;
}
return tabs;
}
BotRoles AiFactory::GetPlayerRoles(Player* player)
{
BotRoles role = BOT_ROLE_NONE;
uint8 tab = GetPlayerSpecTab(player);
switch (player->getClass())
{
case CLASS_PRIEST:
if (tab == 2)
role = BOT_ROLE_DPS;
else
role = BOT_ROLE_HEALER;
break;
case CLASS_SHAMAN:
if (tab == 2)
role = BOT_ROLE_HEALER;
else
role = BOT_ROLE_DPS;
break;
case CLASS_WARRIOR:
if (tab == 2)
role = BOT_ROLE_TANK;
else
role = BOT_ROLE_DPS;
break;
case CLASS_PALADIN:
if (tab == 0)
role = BOT_ROLE_HEALER;
else if (tab == 1)
role = BOT_ROLE_TANK;
else if (tab == 2)
role = BOT_ROLE_DPS;
break;
case CLASS_DRUID:
if (tab == 0)
role = BOT_ROLE_DPS;
else if (tab == 1)
role = (BotRoles)(BOT_ROLE_TANK | BOT_ROLE_DPS);
else if (tab == 2)
role = BOT_ROLE_HEALER;
break;
default:
role = BOT_ROLE_DPS;
break;
}
return role;
}
std::string AiFactory::GetPlayerSpecName(Player* player)
{
std::string specName;
int tab = GetPlayerSpecTab(player);
switch (player->getClass())
{
case CLASS_PRIEST:
if (tab == 2)
specName = "shadow";
else if (tab == 1)
specName = "holy";
else
specName = "disc";
; break;
case CLASS_SHAMAN:
if (tab == 2)
specName = "resto";
else if (tab == 1)
specName = "enhance";
else
specName = "elem";
break;
case CLASS_WARRIOR:
if (tab == 2)
specName = "prot";
else if (tab == 1)
specName = "fury";
else
specName = "arms";
break;
case CLASS_PALADIN:
if (tab == 0)
specName = "holy";
else if (tab == 1)
specName = "prot";
else if (tab == 2)
specName = "retrib";
break;
case CLASS_DRUID:
if (tab == 0)
specName = "balance";
else if (tab == 1)
specName = "feraldps";
else if (tab == 2)
specName = "resto";
break;
case CLASS_ROGUE:
if (tab == 0)
specName = "assas";
else if (tab == 1)
specName = "combat";
else if (tab == 2)
specName = "subtle";
break;
case CLASS_HUNTER:
if (tab == 0)
specName = "beast";
else if (tab == 1)
specName = "marks";
else if (tab == 2)
specName = "surv";
break;
case CLASS_DEATH_KNIGHT:
if (tab == 0)
specName = "blooddps";
else if (tab == 1)
specName = "frostdps";
else if (tab == 2)
specName = "unholydps";
break;
case CLASS_MAGE:
if (tab == 0)
specName = "arcane";
else if (tab == 1)
specName = "fire";
else if (tab == 2)
specName = "frost";
break;
case CLASS_WARLOCK:
if (tab == 0)
specName = "afflic";
else if (tab == 1)
specName = "demo";
else if (tab == 2)
specName = "destro";
break;
default:
break;
}
return specName;
}
void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const facade, Engine* engine)
{
uint8 tab = GetPlayerSpecTab(player);
if (!player->InBattleground())
{
engine->addStrategies("racials", "chat", "default", "cast time", "duel", "boost", nullptr);
}
switch (player->getClass())
{
case CLASS_PRIEST:
if (tab == 2)
{
engine->addStrategies("dps", "shadow debuff", "shadow aoe", "threat", nullptr);
}
else
engine->addStrategies("heal", "threat", nullptr);
engine->addStrategies("dps assist", "cure", nullptr);
break;
case CLASS_MAGE:
if (tab == 0)
engine->addStrategies("arcane", "arcane aoe", "threat", nullptr);
else if (tab == 1)
engine->addStrategies("fire", "fire aoe", "threat", nullptr);
else
engine->addStrategies("frost", "frost aoe", "threat", nullptr);
engine->addStrategies("dps", "dps assist", "cure", nullptr);
break;
case CLASS_WARRIOR:
if (tab == 2)
engine->addStrategies("tank", "tank assist", "aoe", "mark rti", nullptr);
else if (player->getLevel() < 36 || tab == 0)
engine->addStrategies("arms", "aoe", "dps assist", "threat", /*"behind",*/ nullptr);
else
engine->addStrategies("fury", "aoe", "dps assist", "threat", /*"behind",*/ nullptr);
break;
case CLASS_SHAMAN:
if (tab == 0)
engine->addStrategies("caster", "caster aoe", "bmana", "threat", nullptr);
else if (tab == 2)
engine->addStrategies("heal", "bmana", nullptr);
else
engine->addStrategies("melee", "melee aoe", "bdps", "threat", nullptr);
engine->addStrategies("dps assist", "cure", "totems", nullptr);
break;
case CLASS_PALADIN:
if (tab == 1)
engine->addStrategies("tank", "tank assist", "bthreat", "barmor", "cure", nullptr);
else if (tab == 0)
engine->addStrategies("heal", "dps assist", "cure", "bcast", nullptr);
else
engine->addStrategies("dps", "dps assist", "cure", "baoe", nullptr);
break;
case CLASS_DRUID:
if (tab == 0)
{
engine->addStrategies("caster", "cure", "caster aoe", "threat", "dps assist", nullptr);
engine->addStrategy("caster debuff");
}
else if (tab == 2)
engine->addStrategies("heal", "cure", "dps assist", nullptr);
else
{
engine->removeStrategy("flee");
engine->addStrategies("bear", "tank assist", nullptr);
}
break;
case CLASS_HUNTER:
engine->addStrategies("dps", "aoe", "bdps", "threat", "dps assist", nullptr);
engine->addStrategy("dps debuff");
break;
case CLASS_ROGUE:
if (tab == ROGUE_TAB_ASSASSINATION) {
engine->addStrategies("melee", "threat", "dps assist", "aoe", /*"behind",*/ nullptr);
} else {
engine->addStrategies("dps", "threat", "dps assist", "aoe", /*"behind",*/ nullptr);
}
break;
case CLASS_WARLOCK:
engine->addStrategies("dps assist", "dps", "dps debuff", "aoe", "threat", nullptr);
break;
case CLASS_DEATH_KNIGHT:
if (tab == 0)
engine->addStrategies("blood", "tank assist", nullptr);
else if (tab == 1)
engine->addStrategies("frost", "frost aoe", "dps assist", "threat", nullptr);
else
engine->addStrategies("unholy", "unholy aoe", "dps assist", "threat", nullptr);
break;
}
if (facade->IsRealPlayer() || sRandomPlayerbotMgr->IsRandomBot(player))
{
if (!player->GetGroup())
{
// change for heal spec
engine->addStrategy("boost");
engine->addStrategy("dps assist");
engine->removeStrategy("threat");
// engine-
switch (player->getClass()) {
case CLASS_PRIEST: {
if (tab != PRIEST_TAB_SHADOW) {
engine->addStrategies("holy", "shadow debuff", "shadow aoe", nullptr);
}
break;
}
case CLASS_DRUID: {
if (tab == DRUID_TAB_RESTORATION) {
engine->addStrategies("caster", "caster aoe", nullptr);
engine->addStrategy("caster debuff");
}
break;
}
case CLASS_SHAMAN: {
if (tab == SHAMAN_TAB_RESTORATION) {
engine->addStrategies("caster", "caster aoe", "bmana", nullptr);
}
break;
}
case CLASS_PALADIN: {
if (tab == PALADIN_TAB_HOLY) {
engine->addStrategies("dps", "dps assist", "baoe", nullptr);
}
break;
}
default:
break;
}
}
}
else
{
engine->ChangeStrategy(sPlayerbotAIConfig->combatStrategies);
}
// Battleground switch
if (player->InBattleground() && player->GetBattleground())
{
BattlegroundTypeId bgType = player->GetBattlegroundTypeId();
if (bgType == BATTLEGROUND_RB)
bgType = player->GetBattleground()->GetBgTypeID(true);
if (bgType == BATTLEGROUND_WS)
engine->addStrategy("warsong");
if (bgType == BATTLEGROUND_AB)
engine->addStrategy("arathi");
if (bgType == BATTLEGROUND_AV)
engine->addStrategy("alterac");
if (bgType == BATTLEGROUND_EY)
engine->addStrategy("eye");
if (bgType == BATTLEGROUND_IC)
engine->addStrategy("isle");
if (player->InArena())
{
engine->addStrategy("arena");
}
engine->addStrategies("boost", "racials", "chat", "default", "aoe", "potions", "conserve mana", "cast time", "dps assist", nullptr);
engine->removeStrategy("custom::say");
engine->removeStrategy("flee");
engine->removeStrategy("threat");
engine->addStrategy("boost");
if ((player->getClass() == CLASS_DRUID && tab == 2) || (player->getClass() == CLASS_SHAMAN && tab == 2))
engine->addStrategies("caster", "caster aoe", nullptr);
if (player->getClass() == CLASS_DRUID && tab == 1)
engine->addStrategies(/*"behind",*/ "dps", nullptr);
if (player->getClass() == CLASS_ROGUE)
engine->addStrategies(/*"behind",*/ "stealth", nullptr);
}
}
Engine* AiFactory::createCombatEngine(Player* player, PlayerbotAI* const facade, AiObjectContext* aiObjectContext)
{
Engine* engine = new Engine(facade, aiObjectContext);
AddDefaultCombatStrategies(player, facade, engine);
return engine;
}
void AiFactory::AddDefaultNonCombatStrategies(Player* player, PlayerbotAI* const facade, Engine* nonCombatEngine)
{
uint8 tab = GetPlayerSpecTab(player);
switch (player->getClass())
{
case CLASS_PRIEST:
nonCombatEngine->addStrategies("dps assist", "cure", nullptr);
break;
case CLASS_PALADIN:
if (tab == 1) {
nonCombatEngine->addStrategies("bthreat", "tank assist", "barmor", nullptr);
if (player->GetLevel() >= 20) {
nonCombatEngine->addStrategy("bstats");
} else {
nonCombatEngine->addStrategy("bdps");
}
}
else if (tab == 0)
nonCombatEngine->addStrategies("dps assist", "bmana", "bcast", nullptr);
else
nonCombatEngine->addStrategies("dps assist", "bdps", "baoe",nullptr);
nonCombatEngine->addStrategies("cure", nullptr);
break;
case CLASS_HUNTER:
nonCombatEngine->addStrategies("bdps", "dps assist", "pet", nullptr);
break;
case CLASS_SHAMAN:
if (tab == 0 || tab == 2)
nonCombatEngine->addStrategy("bmana");
else
nonCombatEngine->addStrategy("bdps");
nonCombatEngine->addStrategies("dps assist", "cure", nullptr);
break;
case CLASS_MAGE:
if (tab == MAGE_TAB_ARCANE || tab == MAGE_TAB_FIRE)
nonCombatEngine->addStrategy("bdps");
else
nonCombatEngine->addStrategy("bmana");
nonCombatEngine->addStrategies("dps assist", "cure", nullptr);
break;
case CLASS_DRUID:
if (tab == 1)
nonCombatEngine->addStrategy("tank assist");
else
nonCombatEngine->addStrategies("dps assist", "cure", nullptr);
break;
case CLASS_WARRIOR:
if (tab == 2)
nonCombatEngine->addStrategy("tank assist");
else
nonCombatEngine->addStrategy("dps assist");
break;
case CLASS_WARLOCK:
if (tab == WARLOCK_TAB_AFFLICATION) {
nonCombatEngine->addStrategies("bmana", nullptr);
} else if (tab == WARLOCK_TAB_DEMONOLOGY) {
nonCombatEngine->addStrategies("bdps", nullptr);
} else if (tab == WARLOCK_TAB_DESTRUCTION) {
nonCombatEngine->addStrategies("bhealth", nullptr);
}
nonCombatEngine->addStrategies("dps assist", nullptr);
break;
case CLASS_DEATH_KNIGHT:
if (tab == 0)
nonCombatEngine->addStrategy("tank assist");
else
nonCombatEngine->addStrategy("dps assist");
break;
default:
nonCombatEngine->addStrategy("dps assist");
break;
}
if (!player->InBattleground())
{
nonCombatEngine->addStrategies("nc", "food", "chat", "follow",
"default", "quest", "loot", "gather", "duel", "buff", "mount", nullptr);
}
if ((facade->IsRealPlayer() || sRandomPlayerbotMgr->IsRandomBot(player)) && !player->InBattleground())
{
Player* master = facade->GetMaster();
// let 25% of free bots start duels.
if (!urand(0, 3))
nonCombatEngine->addStrategy("start duel");
if (sPlayerbotAIConfig->randomBotJoinLfg)
nonCombatEngine->addStrategy("lfg");
if (!player->GetGroup() || player->GetGroup()->GetLeaderGUID() == player->GetGUID())
{
// let 25% of random not grouped (or grp leader) bots help other players
// if (!urand(0, 3))
// nonCombatEngine->addStrategy("attack tagged");
nonCombatEngine->addStrategy("pvp");
// nonCombatEngine->addStrategy("collision");
nonCombatEngine->addStrategy("grind");
// nonCombatEngine->addStrategy("group");
// nonCombatEngine->addStrategy("guild");
if (sPlayerbotAIConfig->autoDoQuests)
{
// nonCombatEngine->addStrategy("travel");
nonCombatEngine->addStrategy("rpg");
} else {
nonCombatEngine->addStrategy("move random");
}
if (sPlayerbotAIConfig->randomBotJoinBG)
nonCombatEngine->addStrategy("bg");
// if (!master || GET_PLAYERBOT_AI(master))
// nonCombatEngine->addStrategy("maintenance");
nonCombatEngine->ChangeStrategy(sPlayerbotAIConfig->randomBotNonCombatStrategies);
}
else
{
if (facade)
{
if (master)
{
PlayerbotAI* masterBotAI = GET_PLAYERBOT_AI(master);
if (masterBotAI || sRandomPlayerbotMgr->IsRandomBot(player))
{
nonCombatEngine->addStrategy("pvp");
// nonCombatEngine->addStrategy("collision");
// nonCombatEngine->addStrategy("group");
// nonCombatEngine->addStrategy("guild");
// if (sPlayerbotAIConfig->autoDoQuests)
// {
// // nonCombatEngine->addStrategy("travel");
// nonCombatEngine->addStrategy("rpg");
// } else {
// nonCombatEngine->addStrategy("move random");
// }
// if (masterBotAI)
// nonCombatEngine->addStrategy("maintenance");
nonCombatEngine->ChangeStrategy(sPlayerbotAIConfig->randomBotNonCombatStrategies);
}
else {
nonCombatEngine->addStrategy("pvp");
nonCombatEngine->ChangeStrategy(sPlayerbotAIConfig->nonCombatStrategies);
}
}
}
}
}
else
{
nonCombatEngine->ChangeStrategy(sPlayerbotAIConfig->nonCombatStrategies);
}
// nonCombatEngine->addStrategy("battleground");
// nonCombatEngine->addStrategy("warsong");
// Battleground switch
if (player->InBattleground() && player->GetBattleground())
{
nonCombatEngine->addStrategies("nc", "chat", "default", "buff", "food", "mount", "pvp", "dps assist", "attack tagged", nullptr);
nonCombatEngine->removeStrategy("custom::say");
nonCombatEngine->removeStrategy("travel");
nonCombatEngine->removeStrategy("rpg");
nonCombatEngine->removeStrategy("grind");
BattlegroundTypeId bgType = player->GetBattlegroundTypeId();
if (bgType == BATTLEGROUND_RB)
bgType = player->GetBattleground()->GetBgTypeID(true);
if (bgType <= BATTLEGROUND_EY || bgType == BATTLEGROUND_IC) // do not add for not supported bg
nonCombatEngine->addStrategy("battleground");
if (bgType == BATTLEGROUND_WS)
nonCombatEngine->addStrategy("warsong");
if (bgType == BATTLEGROUND_AV)
nonCombatEngine->addStrategy("alterac");
if (bgType == BATTLEGROUND_AB)
nonCombatEngine->addStrategy("arathi");
if (bgType == BATTLEGROUND_EY)
nonCombatEngine->addStrategy("eye");
if (bgType == BATTLEGROUND_IC)
nonCombatEngine->addStrategy("isle");
if (player->InArena())
{
nonCombatEngine->addStrategy("arena");
nonCombatEngine->removeStrategy("mount");
}
}
}
Engine* AiFactory::createNonCombatEngine(Player* player, PlayerbotAI* const facade, AiObjectContext* aiObjectContext)
{
Engine* nonCombatEngine = new Engine(facade, aiObjectContext);
AddDefaultNonCombatStrategies(player, facade, nonCombatEngine);
return nonCombatEngine;
}
void AiFactory::AddDefaultDeadStrategies(Player* player, PlayerbotAI* const facade, Engine* deadEngine)
{
deadEngine->addStrategies("dead", "stay", "chat", "default", "follow", nullptr);
if (sRandomPlayerbotMgr->IsRandomBot(player) && !player->GetGroup())
{
deadEngine->removeStrategy("follow");
}
}
Engine* AiFactory::createDeadEngine(Player* player, PlayerbotAI* const facade, AiObjectContext* AiObjectContext)
{
Engine* deadEngine = new Engine(facade, AiObjectContext);
AddDefaultDeadStrategies(player, facade, deadEngine);
return deadEngine;
}