Big update.

This commit is contained in:
UltraNix
2022-03-12 22:27:09 +01:00
parent b3d00ccb26
commit b952636f0d
843 changed files with 1534330 additions and 99 deletions

696
src/AiFactory.cpp Normal file
View File

@@ -0,0 +1,696 @@
/*
* 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 "Playerbots.h"
#include "Engine.h"
#include "Group.h"
#include "DKAiObjectContext.h"
#include "PriestAiObjectContext.h"
#include "MageAiObjectContext.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))
{
std::map<uint8, uint32> tabs = GetPlayerSpecTabs(bot);
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;
for (uint32 i = 0; i < 3; i++)
tabs[i] = 0;
uint32 classMask = bot->getClassMask();
for (uint32 i = 0; i < sTalentStore.GetNumRows(); ++i)
{
TalentEntry const *talentInfo = sTalentStore.LookupEntry(i);
if (!talentInfo)
continue;
TalentTabEntry const *talentTabInfo = sTalentTabStore.LookupEntry(talentInfo->TalentTab);
if (!talentTabInfo)
continue;
if ((classMask & talentTabInfo->ClassMask) == 0)
continue;
uint32 maxRank = 0;
for (int32 rank = MAX_TALENT_RANK - 1; rank >= 0; --rank)
{
if (!talentInfo->RankID[rank])
continue;
uint32 spellid = talentInfo->RankID[rank];
if (spellid && bot->HasSpell(spellid))
maxRank = rank + 1;
}
tabs[talentTabInfo->tabpage] += maxRank;
}
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 std::move(specName);
}
void AiFactory::AddDefaultCombatStrategies(Player* player, PlayerbotAI* const facade, Engine* engine)
{
uint8 tab = GetPlayerSpecTab(player);
if (!player->InBattleground())
{
engine->addStrategies("racials", "chat", "default", "potions", "cast time", "conserve mana", "duel", nullptr);
}
switch (player->getClass())
{
case CLASS_PRIEST:
if (tab == 2)
{
engine->addStrategies("dps", "shadow debuff", "shadow aoe", "threat", nullptr);
}
else if (tab == 0)
{
engine->addStrategies("holy", "shadow debuff", "shadow aoe", "threat", nullptr);
//if (player->getLevel() >= 4)
//engine->addStrategy("dps debuff");
}
else
engine->addStrategies("heal", "threat", nullptr);
engine->addStrategies("dps assist", "flee", "cure", "ranged", "cc", nullptr);
break;
case CLASS_MAGE:
if (tab == 0)
engine->addStrategies("arcane", "threat", nullptr);
else if (tab == 1)
engine->addStrategies("fire", "fire aoe", "threat", nullptr);
else
engine->addStrategies("frost", "frost aoe", "threat", "dps aoe", nullptr);
engine->addStrategies("dps", "dps assist", "flee", "cure", "ranged", "cc", nullptr);
break;
case CLASS_WARRIOR:
if (tab == 2)
engine->addStrategies("tank", "tank assist", "aoe", "close", "mark rti", nullptr);
else if (player->getLevel() < 30 || tab == 0)
engine->addStrategies("arms", "aoe", "dps assist", "threat", "close", nullptr);
else
engine->addStrategies("fury", "aoe", "dps assist", "threat", "close", nullptr);
break;
case CLASS_SHAMAN:
if (tab == 0)
engine->addStrategies("caster", "caster aoe", "bmana", "threat", "flee", "ranged", nullptr);
else if (tab == 2)
engine->addStrategies("heal", "bmana", "flee", "ranged", nullptr);
else
engine->addStrategies("dps", "melee aoe", "bdps", "threat", "close", nullptr);
engine->addStrategies("dps assist", "cure", "totems", nullptr);
break;
case CLASS_PALADIN:
if (tab == 1)
engine->addStrategies("tank", "tank assist", "bthreat", "cure", "barmor", "bstats", "close", "cc", nullptr);
else if (tab == 0)
engine->addStrategies("heal", "bmana", "dps assist", "cure", "flee", "barmor", nullptr);
else
engine->addStrategies("dps", "bdps", "dps assist", "cure", "baoe", "close", "cc", nullptr);
if (player->getLevel() < 14)
{
engine->addStrategy("bdps");
}
if (player->getLevel() < 16)
{
engine->addStrategy("barmor");
}
break;
case CLASS_DRUID:
if (tab == 0)
{
engine->addStrategies("caster", "cure", "caster aoe", "threat", "flee", "dps assist", "ranged", "cc", nullptr);
if (player->getLevel() > 19)
engine->addStrategy("caster debuff");
}
else if (tab == 2)
engine->addStrategies("heal", "cure", "flee", "dps assist", "ranged", "cc", nullptr);
else
{
engine->removeStrategy("ranged");
engine->addStrategies("bear", "tank assist", "flee", "close", nullptr);
}
break;
case CLASS_HUNTER:
engine->addStrategies("dps", "bdps", "threat", "dps assist", "ranged", "pet", "cc", nullptr);
if (player->getLevel() > 19)
engine->addStrategy("dps debuff");
break;
case CLASS_ROGUE:
engine->addStrategies("dps", "threat", "dps assist", "aoe", "close", "cc", "behind", "stealth", nullptr);
break;
case CLASS_WARLOCK:
if (player->getLevel() > 19)
engine->addStrategy("dps debuff");
engine->addStrategies("dps assist", "dps", "flee", "ranged", "cc", "pet", "threat", nullptr);
break;
case CLASS_DEATH_KNIGHT:
if (tab == 0)
engine->addStrategies("blood", nullptr);
else if (tab == 1)
engine->addStrategies("frost", "frost aoe", "dps assist", "threat", nullptr);
else
engine->addStrategies("unholy", "unholy aoe", "dps assist", "threat", nullptr);
engine->addStrategies("dps assist", "flee", "close", "cc", nullptr);
break;
}
if (facade->IsRealPlayer() || sRandomPlayerbotMgr->IsRandomBot(player))
{
if (!player->GetGroup())
{
engine->ChangeStrategy(sPlayerbotAIConfig->randomBotCombatStrategies);
engine->addStrategy("flee");
engine->addStrategy("boost");
if (player->getClass() == CLASS_WARLOCK)
{
engine->removeStrategy("ranged");
}
if (player->getClass() == CLASS_DRUID && tab == 2)
{
engine->addStrategies("caster", "caster aoe", nullptr);
}
if (player->getClass() == CLASS_DRUID && tab == 1 && urand(0, 100) > 50 && player->getLevel() > 19)
{
engine->addStrategy("dps");
}
if (player->getClass() == CLASS_PRIEST && tab == 1)
{
engine->removeStrategy("heal");
engine->addStrategies("holy", "shadow debuff", "shadow aoe", "threat", nullptr);
}
if (player->getClass() == CLASS_SHAMAN && tab == 2)
{
engine->addStrategies("caster", "caster aoe", nullptr);
}
if (player->getClass() == CLASS_PALADIN && tab == 0)
{
engine->addStrategies("dps", "close", nullptr);
}
}
}
else
{
engine->ChangeStrategy(sPlayerbotAIConfig->combatStrategies);
}
// Battleground switch
if (player->InBattleground())
{
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", "bstats", nullptr);
else if (tab == 0)
nonCombatEngine->addStrategies("dps assist", "barmor", "bmana", nullptr);
else
nonCombatEngine->addStrategies("dps assist", "baoe", "bdps", nullptr);
nonCombatEngine->addStrategies("cure", nullptr);
if (player->getLevel() < 14)
nonCombatEngine->addStrategies("bdps", nullptr);
if (player->getLevel() < 16)
nonCombatEngine->addStrategies("barmor", nullptr);
break;
case CLASS_HUNTER:
nonCombatEngine->addStrategies("bdps", "dps assist", 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 == 1)
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:
nonCombatEngine->addStrategies("pet", "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", "emote", "conserve mana", "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 (!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");
}
if (sPlayerbotAIConfig->randomBotJoinLfg)
nonCombatEngine->addStrategy("lfg");
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("grind");
nonCombatEngine->addStrategy("group");
nonCombatEngine->addStrategy("guild");
if (sPlayerbotAIConfig->autoDoQuests)
{
nonCombatEngine->addStrategy("travel");
nonCombatEngine->addStrategy("rpg");
}
if (masterBotAI)
nonCombatEngine->addStrategy("maintenance");
nonCombatEngine->ChangeStrategy(sPlayerbotAIConfig->randomBotNonCombatStrategies);
}
else
nonCombatEngine->ChangeStrategy(sPlayerbotAIConfig->nonCombatStrategies);
}
}
}
}
else
{
nonCombatEngine->ChangeStrategy(sPlayerbotAIConfig->nonCombatStrategies);
}
// Battleground switch
if (player->InBattleground())
{
nonCombatEngine->addStrategies("nc", "chat", "default", "buff", "food", "mount", "pvp", "collision", "dps assist", "attack tagged", "emote", 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;
}

36
src/AiFactory.h Normal file
View File

@@ -0,0 +1,36 @@
/*
* 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.
*/
#ifndef _PLAYERBOT_AIFACTORY_H
#define _PLAYERBOT_AIFACTORY_H
#include "Common.h"
#include <map>
class AiObjectContext;
class Engine;
class Player;
class PlayerbotAI;
enum BotRoles : uint8;
class AiFactory
{
public:
static AiObjectContext* createAiObjectContext(Player* player, PlayerbotAI* botAI);
static Engine* createCombatEngine(Player* player, PlayerbotAI* const facade, AiObjectContext* aiObjectContext);
static Engine* createNonCombatEngine(Player* player, PlayerbotAI* const facade, AiObjectContext* aiObjectContext);
static Engine* createDeadEngine(Player* player, PlayerbotAI* const facade, AiObjectContext* aibjectContext);
static void AddDefaultNonCombatStrategies(Player* player, PlayerbotAI* const facade, Engine* nonCombatEngine);
static void AddDefaultDeadStrategies(Player* player, PlayerbotAI* const facade, Engine* deadEngine);
static void AddDefaultCombatStrategies(Player* player, PlayerbotAI* const facade, Engine* engine);
static uint8 GetPlayerSpecTab(Player* player);
static std::map<uint8, uint32> GetPlayerSpecTabs(Player* player);
static BotRoles GetPlayerRoles(Player* player);
static std::string GetPlayerSpecName(Player* player);
};
#endif

298
src/ChatFilter.cpp Normal file
View File

@@ -0,0 +1,298 @@
/*
* 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 "ChatFilter.h"
#include "Group.h"
#include "Playerbots.h"
#include "RtiTargetValue.h"
std::string const ChatFilter::Filter(std::string& message)
{
if (message.find("@") == std::string::npos)
return message;
return message.substr(message.find(" ") + 1);
}
class StrategyChatFilter : public ChatFilter
{
public:
StrategyChatFilter(PlayerbotAI* botAI) : ChatFilter(botAI) { }
std::string const Filter(std::string& message) override
{
Player* bot = botAI->GetBot();
bool tank = message.find("@tank") == 0;
if (tank && !botAI->IsTank(bot))
return "";
bool dps = message.find("@dps") == 0;
if (dps && (botAI->IsTank(bot) || botAI->IsHeal(bot)))
return "";
bool heal = message.find("@heal") == 0;
if (heal && !botAI->IsHeal(bot))
return "";
bool ranged = message.find("@ranged") == 0;
if (ranged && !botAI->IsRanged(bot))
return "";
bool melee = message.find("@melee") == 0;
if (melee && botAI->IsRanged(bot))
return "";
if (tank || dps || heal || ranged || melee)
return ChatFilter::Filter(message);
return message;
}
};
class LevelChatFilter : public ChatFilter
{
public:
LevelChatFilter(PlayerbotAI* botAI) : ChatFilter(botAI) { }
std::string const Filter(std::string& message) override
{
Player* bot = botAI->GetBot();
if (message[0] != '@')
return message;
if (message.find("-") != std::string::npos)
{
uint32 fromLevel = atoi(message.substr(message.find("@") + 1, message.find("-")).c_str());
uint32 toLevel = atoi(message.substr(message.find("-") + 1, message.find(" ")).c_str());
if (bot->getLevel() >= fromLevel && bot->getLevel() <= toLevel)
return ChatFilter::Filter(message);
return message;
}
uint32 level = atoi(message.substr(message.find("@") + 1, message.find(" ")).c_str());
if (bot->getLevel() == level)
return ChatFilter::Filter(message);
return message;
}
};
class CombatTypeChatFilter : public ChatFilter
{
public:
CombatTypeChatFilter(PlayerbotAI* botAI) : ChatFilter(botAI) { }
std::string const Filter(std::string& message) override
{
Player* bot = botAI->GetBot();
bool melee = message.find("@melee") == 0;
bool ranged = message.find("@ranged") == 0;
if (!melee && !ranged)
return message;
switch (bot->getClass())
{
case CLASS_WARRIOR:
case CLASS_PALADIN:
case CLASS_ROGUE:
case CLASS_DEATH_KNIGHT:
if (ranged)
return "";
break;
case CLASS_HUNTER:
case CLASS_PRIEST:
case CLASS_MAGE:
case CLASS_WARLOCK:
if (melee)
return "";
break;
case CLASS_DRUID:
if (ranged && botAI->IsTank(bot))
return "";
if (melee && !botAI->IsTank(bot))
return "";
break;
case CLASS_SHAMAN:
if (melee && botAI->IsHeal(bot))
return "";
if (ranged && !botAI->IsHeal(bot))
return "";
break;
}
return ChatFilter::Filter(message);
}
};
class RtiChatFilter : public ChatFilter
{
public:
RtiChatFilter(PlayerbotAI* botAI) : ChatFilter(botAI)
{
rtis.push_back("@star");
rtis.push_back("@circle");
rtis.push_back("@diamond");
rtis.push_back("@triangle");
rtis.push_back("@moon");
rtis.push_back("@square");
rtis.push_back("@cross");
rtis.push_back("@skull");
}
std::string const Filter(std::string& message) override
{
Player* bot = botAI->GetBot();
Group* group = bot->GetGroup();
if (!group)
return message;
bool found = false;
bool isRti = false;
for (std::vector<std::string>::iterator i = rtis.begin(); i != rtis.end(); i++)
{
std::string const rti = *i;
bool isRti = message.find(rti) == 0;
if (!isRti)
continue;
ObjectGuid rtiTarget = group->GetTargetIcon(RtiTargetValue::GetRtiIndex(rti.substr(1)));
if (bot->GetGUID() == rtiTarget)
return ChatFilter::Filter(message);
Unit* target = *botAI->GetAiObjectContext()->GetValue<Unit*>("current target");
if (!target)
return "";
if (target->GetGUID() != rtiTarget)
return "";
found |= isRti;
if (found)
break;
}
if (found)
return ChatFilter::Filter(message);
return message;
}
private:
std::vector<std::string> rtis;
};
class ClassChatFilter : public ChatFilter
{
public:
ClassChatFilter(PlayerbotAI* botAI) : ChatFilter(botAI)
{
classNames["@death_knight"] = CLASS_DEATH_KNIGHT;
classNames["@druid"] = CLASS_DRUID;
classNames["@hunter"] = CLASS_HUNTER;
classNames["@mage"] = CLASS_MAGE;
classNames["@paladin"] = CLASS_PALADIN;
classNames["@priest"] = CLASS_PRIEST;
classNames["@rogue"] = CLASS_ROGUE;
classNames["@shaman"] = CLASS_SHAMAN;
classNames["@warlock"] = CLASS_WARLOCK;
classNames["@warrior"] = CLASS_WARRIOR;
}
std::string const Filter(std::string& message) override
{
Player* bot = botAI->GetBot();
bool found = false;
bool isClass = false;
for (std::map<std::string, uint8>::iterator i = classNames.begin(); i != classNames.end(); i++)
{
bool isClass = message.find(i->first) == 0;
if (isClass && bot->getClass() != i->second)
return "";
found |= isClass;
if (found)
break;
}
if (found)
return ChatFilter::Filter(message);
return message;
}
private:
std::map<std::string, uint8> classNames;
};
class SubGroupChatFilter : public ChatFilter
{
public:
SubGroupChatFilter(PlayerbotAI* botAI) : ChatFilter(botAI) { }
std::string const Filter(std::string& message) override
{
Player* bot = botAI->GetBot();
if (message.find("@group") == 0)
{
std::string const pnum = message.substr(6, message.find(" "));
uint32 from = atoi(pnum.c_str());
uint32 to = from;
if (pnum.find("-") != std::string::npos)
{
from = atoi(pnum.substr(pnum.find("@") + 1, pnum.find("-")).c_str());
to = atoi(pnum.substr(pnum.find("-") + 1, pnum.find(" ")).c_str());
}
if (!bot->GetGroup())
return message;
uint32 sg = bot->GetSubGroup() + 1;
if (sg >= from && sg <= to)
return ChatFilter::Filter(message);
}
return message;
}
};
CompositeChatFilter::CompositeChatFilter(PlayerbotAI* botAI) : ChatFilter(botAI)
{
filters.push_back(new StrategyChatFilter(botAI));
filters.push_back(new ClassChatFilter(botAI));
filters.push_back(new RtiChatFilter(botAI));
filters.push_back(new CombatTypeChatFilter(botAI));
filters.push_back(new LevelChatFilter(botAI));
filters.push_back(new SubGroupChatFilter(botAI));
}
CompositeChatFilter::~CompositeChatFilter()
{
for (std::vector<ChatFilter*>::iterator i = filters.begin(); i != filters.end(); i++)
delete (*i);
}
std::string const CompositeChatFilter::Filter(std::string& message)
{
for (uint32 j = 0; j < filters.size(); ++j)
{
for (std::vector<ChatFilter*>::iterator i = filters.begin(); i != filters.end(); i++)
{
message = (*i)->Filter(message);
if (message.empty())
break;
}
}
return message;
}

36
src/ChatFilter.h Normal file
View File

@@ -0,0 +1,36 @@
/*
* 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.
*/
#ifndef _PLAYERBOT_CHATFILTER_H
#define _PLAYERBOT_CHATFILTER_H
#include "Common.h"
#include "PlayerbotAIAware.h"
#include <vector>
class PlayerbotAI;
class ChatFilter : public PlayerbotAIAware
{
public:
ChatFilter(PlayerbotAI* botAI) : PlayerbotAIAware(botAI) { }
virtual ~ChatFilter() { }
virtual std::string const Filter(std::string& message);
};
class CompositeChatFilter : public ChatFilter
{
public:
CompositeChatFilter(PlayerbotAI* botAI);
virtual ~CompositeChatFilter();
std::string const Filter(std::string& message) override;
private:
std::vector<ChatFilter*> filters;
};
#endif

569
src/ChatHelper.cpp Normal file
View File

@@ -0,0 +1,569 @@
/*
* 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 "ChatHelper.h"
#include "AiFactory.h"
#include "Playerbots.h"
#include "SpellInfo.h"
std::map<std::string, uint32> ChatHelper::consumableSubClasses;
std::map<std::string, uint32> ChatHelper::tradeSubClasses;
std::map<std::string, uint32> ChatHelper::itemQualities;
std::map<std::string, uint32> ChatHelper::projectileSubClasses;
std::map<std::string, uint32> ChatHelper::slots;
std::map<std::string, uint32> ChatHelper::skills;
std::map<std::string, ChatMsg> ChatHelper::chats;
std::map<uint8, std::string> ChatHelper::classes;
std::map<uint8, std::string> ChatHelper::races;
std::map<uint8, std::map<uint8, std::string> > ChatHelper::specs;
template<class T>
static bool substrContainsInMap(std::string const searchTerm, std::map<std::string, T> searchIn)
{
for (typename std::map<std::string, T>::iterator i = searchIn.begin(); i != searchIn.end(); ++i)
{
std::string const term = i->first;
if (term.size() > 1 && searchTerm.find(term) != std::string::npos)
return true;
}
return false;
}
ChatHelper::ChatHelper(PlayerbotAI* botAI) : PlayerbotAIAware(botAI)
{
itemQualities["poor"] = ITEM_QUALITY_POOR;
itemQualities["gray"] = ITEM_QUALITY_POOR;
itemQualities["normal"] = ITEM_QUALITY_NORMAL;
itemQualities["white"] = ITEM_QUALITY_NORMAL;
itemQualities["uncommon"] = ITEM_QUALITY_UNCOMMON;
itemQualities["green"] = ITEM_QUALITY_UNCOMMON;
itemQualities["rare"] = ITEM_QUALITY_RARE;
itemQualities["blue"] = ITEM_QUALITY_RARE;
itemQualities["epic"] = ITEM_QUALITY_EPIC;
itemQualities["violet"] = ITEM_QUALITY_EPIC;
itemQualities["legendary"] = ITEM_QUALITY_LEGENDARY;
itemQualities["yellow"] = ITEM_QUALITY_LEGENDARY;
consumableSubClasses["potion"] = ITEM_SUBCLASS_POTION;
consumableSubClasses["elixir"] = ITEM_SUBCLASS_ELIXIR;
consumableSubClasses["flask"] = ITEM_SUBCLASS_FLASK;
consumableSubClasses["scroll"] = ITEM_SUBCLASS_SCROLL;
consumableSubClasses["food"] = ITEM_SUBCLASS_FOOD;
consumableSubClasses["bandage"] = ITEM_SUBCLASS_BANDAGE;
consumableSubClasses["enchant"] = ITEM_SUBCLASS_CONSUMABLE_OTHER;
projectileSubClasses["arrows"] = ITEM_SUBCLASS_ARROW;
projectileSubClasses["bullets"] = ITEM_SUBCLASS_BULLET;
//tradeSubClasses["cloth"] = ITEM_SUBCLASS_CLOTH;
//tradeSubClasses["leather"] = ITEM_SUBCLASS_LEATHER;
//tradeSubClasses["metal"] = ITEM_SUBCLASS_METAL_STONE;
//tradeSubClasses["stone"] = ITEM_SUBCLASS_METAL_STONE;
//tradeSubClasses["ore"] = ITEM_SUBCLASS_METAL_STONE;
//tradeSubClasses["meat"] = ITEM_SUBCLASS_MEAT;
//tradeSubClasses["herb"] = ITEM_SUBCLASS_HERB;
//tradeSubClasses["elemental"] = ITEM_SUBCLASS_ELEMENTAL;
//tradeSubClasses["disenchants"] = ITEM_SUBCLASS_ENCHANTING;
//tradeSubClasses["enchanting"] = ITEM_SUBCLASS_ENCHANTING;
//tradeSubClasses["gems"] = ITEM_SUBCLASS_JEWELCRAFTING;
//tradeSubClasses["jewels"] = ITEM_SUBCLASS_JEWELCRAFTING;
//tradeSubClasses["jewelcrafting"] = ITEM_SUBCLASS_JEWELCRAFTING;
slots["head"] = EQUIPMENT_SLOT_HEAD;
slots["neck"] = EQUIPMENT_SLOT_NECK;
slots["shoulder"] = EQUIPMENT_SLOT_SHOULDERS;
slots["shirt"] = EQUIPMENT_SLOT_BODY;
slots["chest"] = EQUIPMENT_SLOT_CHEST;
slots["waist"] = EQUIPMENT_SLOT_WAIST;
slots["legs"] = EQUIPMENT_SLOT_LEGS;
slots["feet"] = EQUIPMENT_SLOT_FEET;
slots["wrist"] = EQUIPMENT_SLOT_WRISTS;
slots["hands"] = EQUIPMENT_SLOT_HANDS;
slots["finger 1"] = EQUIPMENT_SLOT_FINGER1;
slots["finger 2"] = EQUIPMENT_SLOT_FINGER2;
slots["trinket 1"] = EQUIPMENT_SLOT_TRINKET1;
slots["trinket 2"] = EQUIPMENT_SLOT_TRINKET2;
slots["back"] = EQUIPMENT_SLOT_BACK;
slots["main hand"] = EQUIPMENT_SLOT_MAINHAND;
slots["off hand"] = EQUIPMENT_SLOT_OFFHAND;
slots["ranged"] = EQUIPMENT_SLOT_RANGED;
slots["tabard"] = EQUIPMENT_SLOT_TABARD;
skills["first aid"] = SKILL_FIRST_AID;
skills["fishing"] = SKILL_FISHING;
skills["cooking"] = SKILL_COOKING;
skills["alchemy"] = SKILL_ALCHEMY;
skills["enchanting"] = SKILL_ENCHANTING;
skills["engineering"] = SKILL_ENGINEERING;
skills["leatherworking"] = SKILL_LEATHERWORKING;
skills["blacksmithing"] = SKILL_BLACKSMITHING;
skills["tailoring"] = SKILL_TAILORING;
skills["herbalism"] = SKILL_HERBALISM;
skills["mining"] = SKILL_MINING;
skills["skinning"] = SKILL_SKINNING;
skills["jewelcrafting"] = SKILL_JEWELCRAFTING;
chats["party"] = CHAT_MSG_PARTY;
chats["p"] = CHAT_MSG_PARTY;
chats["guild"] = CHAT_MSG_GUILD;
chats["g"] = CHAT_MSG_GUILD;
chats["raid"] = CHAT_MSG_RAID;
chats["r"] = CHAT_MSG_RAID;
chats["whisper"] = CHAT_MSG_WHISPER;
chats["w"] = CHAT_MSG_WHISPER;
classes[CLASS_DRUID] = "druid";
specs[CLASS_DRUID][0] = "balance";
specs[CLASS_DRUID][1] = "feral combat";
specs[CLASS_DRUID][2] = "restoration";
classes[CLASS_HUNTER] = "hunter";
specs[CLASS_HUNTER][0] = "beast mastery";
specs[CLASS_HUNTER][1] = "marksmanship";
specs[CLASS_HUNTER][2] = "survival";
classes[CLASS_MAGE] = "mage";
specs[CLASS_MAGE][0] = "arcane";
specs[CLASS_MAGE][1] = "fire";
specs[CLASS_MAGE][2] = "frost";
classes[CLASS_PALADIN] = "paladin";
specs[CLASS_PALADIN][0] = "holy";
specs[CLASS_PALADIN][1] = "protection";
specs[CLASS_PALADIN][2] = "retribution";
classes[CLASS_PRIEST] = "priest";
specs[CLASS_PRIEST][0] = "discipline";
specs[CLASS_PRIEST][1] = "holy";
specs[CLASS_PRIEST][2] = "shadow";
classes[CLASS_ROGUE] = "rogue";
specs[CLASS_ROGUE][0] = "assasination";
specs[CLASS_ROGUE][1] = "combat";
specs[CLASS_ROGUE][2] = "subtlety";
classes[CLASS_SHAMAN] = "shaman";
specs[CLASS_SHAMAN][0] = "elemental";
specs[CLASS_SHAMAN][1] = "enhancement";
specs[CLASS_SHAMAN][2] = "restoration";
classes[CLASS_WARLOCK] = "warlock";
specs[CLASS_WARLOCK][0] = "affliction";
specs[CLASS_WARLOCK][1] = "demonology";
specs[CLASS_WARLOCK][2] = "destruction";
classes[CLASS_WARRIOR] = "warrior";
specs[CLASS_WARRIOR][0] = "arms";
specs[CLASS_WARRIOR][1] = "fury";
specs[CLASS_WARRIOR][2] = "protection";
classes[CLASS_DEATH_KNIGHT] = "dk";
specs[CLASS_DEATH_KNIGHT][0] = "blood";
specs[CLASS_DEATH_KNIGHT][1] = "frost";
specs[CLASS_DEATH_KNIGHT][2] = "unholy";
races[RACE_DWARF] = "Dwarf";
races[RACE_GNOME] = "Gnome";
races[RACE_HUMAN] = "Human";
races[RACE_NIGHTELF] = "Night Elf";
races[RACE_ORC] = "Orc";
races[RACE_TAUREN] = "Tauren";
races[RACE_TROLL] = "Troll";
races[RACE_UNDEAD_PLAYER] = "Undead";
races[RACE_BLOODELF] = "Blood Elf";
races[RACE_DRAENEI] = "Draenei";
}
std::string const ChatHelper::formatMoney(uint32 copper)
{
std::ostringstream out;
if (!copper)
{
out << "0";
return out.str();
}
uint32 gold = uint32(copper / 10000);
copper -= (gold * 10000);
uint32 silver = uint32(copper / 100);
copper -= (silver * 100);
bool space = false;
if (gold > 0)
{
out << gold << "g";
space = true;
}
if (silver > 0 && gold < 50)
{
if (space)
out << " ";
out << silver << "s";
space = true;
}
if (copper > 0 && gold < 10)
{
if (space)
out << " ";
out << copper << "c";
}
return out.str();
}
uint32 ChatHelper::parseMoney(std::string const text)
{
// if user specified money in ##g##s##c format
std::string acum = "";
uint32 copper = 0;
for (uint8 i = 0; i < text.length(); i++)
{
if (text[i] == 'g')
{
copper += (atol(acum.c_str()) * 100 * 100);
acum = "";
}
else if (text[i] == 'c')
{
copper += atol(acum.c_str());
acum = "";
}
else if (text[i] == 's')
{
copper += (atol(acum.c_str()) * 100);
acum = "";
}
else if (text[i] == ' ')
break;
else if (text[i] >= 48 && text[i] <= 57)
acum += text[i];
else
{
copper = 0;
break;
}
}
return copper;
}
ItemIds ChatHelper::parseItems(std::string const text)
{
ItemIds itemIds;
uint8 pos = 0;
while (true)
{
uint32 i = text.find("Hitem:", pos);
if (i == -1)
break;
pos = i + 6;
uint32 endPos = text.find(':', pos);
if (endPos == -1)
break;
std::string const idC = text.substr(pos, endPos - pos);
uint32 id = atol(idC.c_str());
pos = endPos;
if (id)
itemIds.insert(id);
}
return itemIds;
}
std::string const ChatHelper::FormatQuest(Quest const* quest)
{
std::ostringstream out;
out << "|cFFFFFF00|Hquest:" << quest->GetQuestId() << ':' << quest->GetQuestLevel() << "|h[" << quest->GetTitle() << "]|h|r";
return out.str();
}
std::string const ChatHelper::FormatGameobject(GameObject* go)
{
std::ostringstream out;
out << "|cFFFFFF00|Hfound:" << go->GetGUID().GetRawValue() << ":" << go->GetEntry() << ":" << "|h[" << go->GetGOInfo()->name << "]|h|r";
return out.str();
}
std::string const ChatHelper::FormatWorldobject(WorldObject* wo)
{
std::ostringstream out;
out << "|cFFFFFF00|Hfound:" << wo->GetGUID().GetRawValue() << ":" << wo->GetEntry() << ":" << "|h[";
out << (wo->ToGameObject() ? ((GameObject*)wo)->GetGOInfo()->name : wo->GetName()) << "]|h|r";
return out.str();
}
std::string const ChatHelper::FormatWorldEntry(int32 entry)
{
CreatureTemplate const* cInfo = nullptr;
GameObjectTemplate const* gInfo = nullptr;
if (entry > 0)
cInfo = sObjectMgr->GetCreatureTemplate(entry);
else
gInfo = sObjectMgr->GetGameObjectTemplate(entry * -1);
std::ostringstream out;
out << "|cFFFFFF00|Hentry:" << abs(entry) << ":" << "|h[";
if (entry < 0 && gInfo)
out << gInfo->name;
else if (entry > 0 && cInfo)
out << cInfo->Name;
else
out << "unknown";
out << "]|h|r";
return out.str();
}
std::string const ChatHelper::FormatSpell(SpellInfo const* spellInfo)
{
std::ostringstream out;
out << "|cffffffff|Hspell:" << spellInfo->Id << "|h[" << spellInfo->SpellName[LOCALE_enUS] << "]|h|r";
return out.str();
}
std::string const ChatHelper::FormatItem(ItemTemplate const* proto, uint32 count, uint32 total)
{
char color[32];
sprintf(color, "%x", ItemQualityColors[proto->Quality]);
std::ostringstream out;
out << "|c" << color << "|Hitem:" << proto->ItemId
<< ":0:0:0:0:0:0:0" << "|h[" << proto->Name1
<< "]|h|r";
if (count > 1)
out << "x" << count;
if (total > 0)
out << " (" << total << ")";
return out.str();
}
std::string const ChatHelper::FormatQItem(uint32 itemId)
{
char color[32];
sprintf(color, "%x", ItemQualityColors[0]);
std::ostringstream out;
out << "|c" << color << "|Hitem:" << itemId << ":0:0:0:0:0:0:0"
<< "|h[item"
<< "]|h|r";
return out.str();
}
ChatMsg ChatHelper::parseChat(std::string const text)
{
if (chats.find(text) != chats.end())
return chats[text];
return CHAT_MSG_SYSTEM;
}
std::string const ChatHelper::FormatChat(ChatMsg chat)
{
switch (chat)
{
case CHAT_MSG_GUILD:
return "guild";
case CHAT_MSG_PARTY:
return "party";
case CHAT_MSG_WHISPER:
return "whisper";
case CHAT_MSG_RAID:
return "raid";
}
return "unknown";
}
uint32 ChatHelper::parseSpell(std::string const text)
{
PlayerbotChatHandler handler(botAI->GetBot());
return handler.extractSpellId(text);
}
GuidVector ChatHelper::parseGameobjects(std::string const text)
{
GuidVector gos;
// Link format
// |cFFFFFF00|Hfound:" << guid << ':' << entry << ':' << "|h[" << gInfo->name << "]|h|r";
// |cFFFFFF00|Hfound:9582:1731|h[Copper Vein]|h|r
uint8 pos = 0;
while (true)
{
// extract GO guid
uint32 i = text.find("Hfound:", pos); // base H = 11
if (i == -1) // break if error
break;
pos = i + 7; //start of window in text 11 + 7 = 18
uint32 endPos = text.find(':', pos); // end of window in text 22
if (endPos == -1) //break if error
break;
std::istringstream stream(text.substr(pos, endPos - pos));
uint64 guid;
stream >> guid;
// extract GO entry
pos = endPos + 1;
endPos = text.find(':', pos); // end of window in text
if (endPos == -1) //break if error
break;
std::string const entryC = text.substr(pos, endPos - pos); // get std::string const within window i.e entry
uint32 entry = atol(entryC.c_str()); // convert ascii to float
ObjectGuid lootCurrent = ObjectGuid(guid);
if (guid)
gos.push_back(lootCurrent);
}
return gos;
}
std::string const ChatHelper::FormatQuestObjective(std::string const name, uint32 available, uint32 required)
{
std::ostringstream out;
out << "|cFFFFFFFF" << name << (available >= required ? "|c0000FF00: " : "|c00FF0000: ")
<< available << "/" << required << "|r";
return out.str();
}
uint32 ChatHelper::parseItemQuality(std::string const text)
{
if (itemQualities.find(text) == itemQualities.end())
return MAX_ITEM_QUALITY;
return itemQualities[text];
}
bool ChatHelper::parseItemClass(std::string const text, uint32* itemClass, uint32* itemSubClass)
{
if (text == "questitem")
{
*itemClass = ITEM_CLASS_QUEST;
*itemSubClass = ITEM_SUBCLASS_QUEST;
return true;
}
if (consumableSubClasses.find(text) != consumableSubClasses.end())
{
*itemClass = ITEM_CLASS_CONSUMABLE;
*itemSubClass = consumableSubClasses[text];
return true;
}
if (tradeSubClasses.find(text) != tradeSubClasses.end())
{
*itemClass = ITEM_CLASS_TRADE_GOODS;
*itemSubClass = tradeSubClasses[text];
return true;
}
if (projectileSubClasses.find(text) != projectileSubClasses.end())
{
*itemClass = ITEM_CLASS_PROJECTILE;
*itemSubClass = projectileSubClasses[text];
return true;
}
return false;
}
uint32 ChatHelper::parseSlot(std::string const text)
{
if (slots.find(text) != slots.end())
return slots[text];
return EQUIPMENT_SLOT_END;
}
bool ChatHelper::parseable(std::string const text)
{
return text.find("|H") != std::string::npos || text == "questitem" || text == "ammo" || substrContainsInMap<uint32>(text, consumableSubClasses) ||
substrContainsInMap<uint32>(text, tradeSubClasses) || substrContainsInMap<uint32>(text, itemQualities) || substrContainsInMap<uint32>(text, slots) ||
substrContainsInMap<ChatMsg>(text, chats) || substrContainsInMap<uint32>(text, skills) || parseMoney(text) > 0;
}
std::string const ChatHelper::FormatClass(Player* player, int8 spec)
{
uint8 cls = player->getClass();
std::ostringstream out;
out << specs[cls][spec] << " (";
std::map<uint8, uint32> tabs = AiFactory::GetPlayerSpecTabs(player);
uint32 c0 = tabs[0];
uint32 c1 = tabs[1];
uint32 c2 = tabs[2];
out << (c0 ? "|h|cff00ff00" : "") << c0 << "|h|cffffffff/";
out << (c1 ? "|h|cff00ff00" : "") << c1 << "|h|cffffffff/";
out << (c2 ? "|h|cff00ff00" : "") << c2 << "|h|cffffffff";
out << ") " << classes[cls];
return out.str();
}
std::string const ChatHelper::FormatClass(uint8 cls)
{
return classes[cls];
}
std::string const ChatHelper::FormatRace(uint8 race)
{
return races[race];
}
uint32 ChatHelper::parseSkill(std::string const text)
{
if (skills.find(text) != skills.end())
return skills[text];
return SKILL_NONE;
}
std::string const ChatHelper::FormatSkill(uint32 skill)
{
for (std::map<std::string, uint32>::iterator i = skills.begin(); i != skills.end(); ++i)
{
if (i->second == skill)
return i->first;
}
return "";
}
std::string const ChatHelper::FormatBoolean(bool flag)
{
return flag ? "|cff00ff00ON|r" : "|cffffff00OFF|r";
}
void ChatHelper::eraseAllSubStr(std::string& mainStr, std::string const toErase)
{
size_t pos = std::string::npos;
// Search for the substring in std::string const in a loop untill nothing is found
while ((pos = mainStr.find(toErase)) != std::string::npos)
{
// If found then erase it from std::string
mainStr.erase(pos, toErase.length());
}
}

77
src/ChatHelper.h Normal file
View File

@@ -0,0 +1,77 @@
/*
* 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.
*/
#ifndef _PLAYERBOT_CHATHELPER_H
#define _PLAYERBOT_CHATHELPER_H
#include "Common.h"
#include "ObjectGuid.h"
#include "PlayerbotAIAware.h"
#include "SharedDefines.h"
#include <map>
class GameObject;
class Quest;
class Player;
class PlayerbotAI;
class SpellInfo;
class WorldObject;
struct ItemTemplate;
typedef std::set<uint32> ItemIds;
typedef std::set<uint32> SpellIds;
class ChatHelper : public PlayerbotAIAware
{
public:
ChatHelper(PlayerbotAI* botAI);
static std::string const formatMoney(uint32 copper);
static uint32 parseMoney(std::string const text);
static ItemIds parseItems(std::string const text);
uint32 parseSpell(std::string const text);
static std::string const FormatQuest(Quest const* quest);
static std::string const FormatItem(ItemTemplate const* proto, uint32 count = 0, uint32 total = 0);
static std::string const FormatQItem(uint32 itemId);
static std::string const FormatSpell(SpellInfo const* spellInfo);
static std::string const FormatGameobject(GameObject* go);
static std::string const FormatWorldobject(WorldObject* wo);
static std::string const FormatWorldEntry(int32 entry);
static std::string const FormatQuestObjective(std::string const name, uint32 available, uint32 required);
static GuidVector parseGameobjects(std::string const text);
static ChatMsg parseChat(std::string const text);
static std::string const FormatChat(ChatMsg chat);
static std::string const FormatClass(Player* player, int8 spec);
static std::string const FormatClass(uint8 cls);
static std::string const FormatRace(uint8 race);
static std::string const FormatSkill(uint32 skill);
static std::string const FormatBoolean(bool flag);
static uint32 parseItemQuality(std::string const text);
static bool parseItemClass(std::string const text, uint32* itemClass, uint32* itemSubClass);
static uint32 parseSlot(std::string const text);
uint32 parseSkill(std::string const text);
static bool parseable(std::string const text);
void eraseAllSubStr(std::string& mainStr, std::string const toErase);
private:
static std::map<std::string, uint32> consumableSubClasses;
static std::map<std::string, uint32> tradeSubClasses;
static std::map<std::string, uint32> itemQualities;
static std::map<std::string, uint32> projectileSubClasses;
static std::map<std::string, uint32> slots;
static std::map<std::string, uint32> skills;
static std::map<std::string, ChatMsg> chats;
static std::map<uint8, std::string> classes;
static std::map<uint8, std::string> races;
static std::map<uint8, std::map<uint8, std::string>> specs;
};
#endif

171
src/FleeManager.cpp Normal file
View File

@@ -0,0 +1,171 @@
/*
* 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 "FleeManager.h"
#include "Playerbots.h"
#include "ServerFacade.h"
FleeManager::FleeManager(Player* bot, float maxAllowedDistance, float followAngle, bool forceMaxDistance, WorldPosition startPosition) :
bot(bot), maxAllowedDistance(maxAllowedDistance), followAngle(followAngle), forceMaxDistance(forceMaxDistance), startPosition(startPosition ? startPosition : WorldPosition(bot))
{
}
void FleeManager::calculateDistanceToCreatures(FleePoint *point)
{
point->minDistance = -1.0f;
point->sumDistance = 0.0f;
PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot);
GuidVector units = *botAI->GetAiObjectContext()->GetValue<GuidVector>("possible targets no los");
for (GuidVector::iterator i = units.begin(); i != units.end(); ++i)
{
Unit* unit = botAI->GetUnit(*i);
if (!unit)
continue;
float d = sServerFacade->GetDistance2d(unit, point->x, point->y);
point->sumDistance += d;
if (point->minDistance < 0 || point->minDistance > d)
point->minDistance = d;
}
}
bool intersectsOri(float angle, std::vector<float>& angles, float angleIncrement)
{
for (std::vector<float>::iterator i = angles.begin(); i != angles.end(); ++i)
{
float ori = *i;
if (abs(angle - ori) < angleIncrement)
return true;
}
return false;
}
void FleeManager::calculatePossibleDestinations(std::vector<FleePoint*> &points)
{
PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot);
Unit* target = *botAI->GetAiObjectContext()->GetValue<Unit*>("current target");
float botPosX = startPosition.getX();
float botPosY = startPosition.getY();
float botPosZ = startPosition.getZ();
FleePoint start(botAI, botPosX, botPosY, botPosZ);
calculateDistanceToCreatures(&start);
std::vector<float> enemyOri;
GuidVector units = *botAI->GetAiObjectContext()->GetValue<GuidVector>("possible targets no los");
for (GuidVector::iterator i = units.begin(); i != units.end(); ++i)
{
Unit* unit = botAI->GetUnit(*i);
if (!unit)
continue;
float ori = bot->GetAngle(unit);
enemyOri.push_back(ori);
}
float distIncrement = std::max(sPlayerbotAIConfig->followDistance, (maxAllowedDistance - sPlayerbotAIConfig->tooCloseDistance) / 10.0f);
for (float dist = maxAllowedDistance; dist >= sPlayerbotAIConfig->tooCloseDistance ; dist -= distIncrement)
{
float angleIncrement = std::max(M_PI / 20, M_PI / 4 / (1.0 + dist - sPlayerbotAIConfig->tooCloseDistance));
for (float add = 0.0f; add < M_PI / 4 + angleIncrement; add += angleIncrement)
{
for (float angle = add; angle < add + 2 * static_cast<float>(M_PI) + angleIncrement; angle += static_cast<float>(M_PI) / 4)
{
if (intersectsOri(angle, enemyOri, angleIncrement))
continue;
float x = botPosX + cos(angle) * maxAllowedDistance, y = botPosY + sin(angle) * maxAllowedDistance, z = botPosZ + CONTACT_DISTANCE;
if (forceMaxDistance && sServerFacade->IsDistanceLessThan(sServerFacade->GetDistance2d(bot, x, y), maxAllowedDistance - sPlayerbotAIConfig->tooCloseDistance))
continue;
bot->UpdateAllowedPositionZ(x, y, z);
Map* map = startPosition.getMap();
if (map && map->IsInWater(bot->GetPhaseMask(), x, y, z, bot->GetCollisionHeight()))
continue;
if (!bot->IsWithinLOS(x, y, z) || (target && !target->IsWithinLOS(x, y, z)))
continue;
FleePoint* point = new FleePoint(botAI, x, y, z);
calculateDistanceToCreatures(point);
if (sServerFacade->IsDistanceGreaterOrEqualThan(point->minDistance - start.minDistance, sPlayerbotAIConfig->followDistance))
points.push_back(point);
else
delete point;
}
}
}
}
void FleeManager::cleanup(std::vector<FleePoint*> &points)
{
for (std::vector<FleePoint*>::iterator i = points.begin(); i != points.end(); i++)
{
delete *i;
}
points.clear();
}
bool FleeManager::isBetterThan(FleePoint* point, FleePoint* other)
{
return point->sumDistance - other->sumDistance > 0;
}
FleePoint* FleeManager::selectOptimalDestination(std::vector<FleePoint*> &points)
{
FleePoint* best = nullptr;
for (std::vector<FleePoint*>::iterator i = points.begin(); i != points.end(); i++)
{
FleePoint* point = *i;
if (!best || isBetterThan(point, best))
best = point;
}
return best;
}
bool FleeManager::CalculateDestination(float* rx, float* ry, float* rz)
{
std::vector<FleePoint*> points;
calculatePossibleDestinations(points);
FleePoint* point = selectOptimalDestination(points);
if (!point)
{
cleanup(points);
return false;
}
*rx = point->x;
*ry = point->y;
*rz = point->z;
cleanup(points);
return true;
}
bool FleeManager::isUseful()
{
PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot);
GuidVector units = *botAI->GetAiObjectContext()->GetValue<GuidVector>("possible targets no los");
for (GuidVector::iterator i = units.begin(); i != units.end(); ++i)
{
Creature* creature = botAI->GetCreature(*i);
if (!creature)
continue;
if (startPosition.sqDistance(WorldPosition(creature)) < creature->GetAttackDistance(bot) * creature->GetAttackDistance(bot))
return true;
// float d = sServerFacade->GetDistance2d(unit, bot);
// if (sServerFacade->IsDistanceLessThan(d, sPlayerbotAIConfig->aggroDistance)) return true;
}
return false;
}

54
src/FleeManager.h Normal file
View File

@@ -0,0 +1,54 @@
/*
* 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.
*/
#ifndef _PLAYERBOT_FLEEMANAGER_H
#define _PLAYERBOT_FLEEMANAGER_H
#include "Common.h"
#include "TravelMgr.h"
#include <vector>
class Player;
class PlayerbotAI;
class FleePoint
{
public:
FleePoint(PlayerbotAI* botAI, float x, float y, float z) : botAI(botAI), sumDistance(0.0f), minDistance(0.0f), x(x), y(y), z(z) { }
float x;
float y;
float z;
float sumDistance;
float minDistance;
private:
PlayerbotAI* botAI;
};
class FleeManager
{
public:
FleeManager(Player* bot, float maxAllowedDistance, float followAngle, bool forceMaxDistance = false, WorldPosition startPosition = WorldPosition());
bool CalculateDestination(float* rx, float* ry, float* rz);
bool isUseful();
private:
void calculatePossibleDestinations(std::vector<FleePoint*> &points);
void calculateDistanceToCreatures(FleePoint *point);
void cleanup(std::vector<FleePoint*> &points);
FleePoint* selectOptimalDestination(std::vector<FleePoint*> &points);
bool isBetterThan(FleePoint* point, FleePoint* other);
Player* bot;
float maxAllowedDistance;
float followAngle;
bool forceMaxDistance;
WorldPosition startPosition;
};
#endif

1198
src/GuildTaskMgr.cpp Normal file

File diff suppressed because it is too large Load Diff

59
src/GuildTaskMgr.h Normal file
View File

@@ -0,0 +1,59 @@
/*
* 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.
*/
#ifndef _PLAYERBOT_GUILDTASKMGR_H
#define _PLAYERBOT_GUILDTASKMGR_H
#include "Common.h"
#include "Transaction.h"
#include <map>
class ChatHandler;
class Player;
class Unit;
class GuildTaskMgr
{
public:
GuildTaskMgr() { };
virtual ~GuildTaskMgr() { };
static GuildTaskMgr* instance()
{
static GuildTaskMgr instance;
return &instance;
}
void Update(Player* owner, Player* guildMaster);
static bool HandleConsoleCommand(ChatHandler* handler, char const* args);
bool IsGuildTaskItem(uint32 itemId, uint32 guildId);
bool CheckItemTask(uint32 itemId, uint32 obtained, Player* owner, Player* bot, bool byMail = false);
void CheckKillTask(Player* owner, Unit* victim);
void CheckKillTaskInternal(Player* owner, Unit* victim);
bool CheckTaskTransfer(std::string const text, Player* owner, Player* bot);
private:
std::map<uint32, uint32> GetTaskValues(uint32 owner, std::string const type, uint32* validIn = nullptr);
uint32 GetTaskValue(uint32 owner, uint32 guildId, std::string const type, uint32* validIn = nullptr);
uint32 SetTaskValue(uint32 owner, uint32 guildId, std::string const type, uint32 value, uint32 validIn);
uint32 CreateTask(Player* owner, uint32 guildId);
bool SendAdvertisement(CharacterDatabaseTransaction& trans, uint32 owner, uint32 guildId);
bool SendItemAdvertisement(CharacterDatabaseTransaction& trans, uint32 itemId, uint32 owner, uint32 guildId, uint32 validIn);
bool SendKillAdvertisement(CharacterDatabaseTransaction& trans, uint32 creatureId, uint32 owner, uint32 guildId, uint32 validIn);
bool SendThanks(CharacterDatabaseTransaction& trans, uint32 owner, uint32 guildId, uint32 payment);
bool Reward(CharacterDatabaseTransaction& trans, uint32 owner, uint32 guildId);
bool CreateItemTask(Player* owner, uint32 guildId);
bool CreateKillTask(Player* owner, uint32 guildId);
uint32 GetMaxItemTaskCount(uint32 itemId);
void CleanupAdverts();
void RemoveDuplicatedAdverts();
void DeleteMail(std::vector<uint32> buffer);
void SendCompletionMessage(Player* player, std::string const verb);
};
#define sGuildTaskMgr GuildTaskMgr::instance()
#endif

52
src/Helpers.cpp Normal file
View File

@@ -0,0 +1,52 @@
/*
* 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 "Helpers.h"
char* strstri(char const* haystack, char const* needle)
{
if (!*needle)
{
return (char*)haystack;
}
for (; *haystack; ++haystack)
{
if (tolower(*haystack) == tolower(*needle))
{
char const* h = haystack, * n = needle;
for (; *h && *n; ++h, ++n)
{
if (tolower(*h) != tolower(*n))
{
break;
}
}
if (!*n)
{
return (char*)haystack;
}
}
}
return 0;
}
std::string& ltrim(std::string& s)
{
s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](int c) {return !std::isspace(c); }));
return s;
}
std::string& rtrim(std::string& s)
{
s.erase(std::find_if(s.rbegin(), s.rend(), [](int c) {return !std::isspace(c); }).base(), s.end());
return s;
}
std::string& trim(std::string& s)
{
return ltrim(rtrim(s));
}

50
src/Helpers.h Normal file
View File

@@ -0,0 +1,50 @@
/*
* 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.
*/
#ifndef _PLAYERBOT_HELPERS_H
#define _PLAYERBOT_HELPERS_H
#include "Common.h"
#include <map>
#include <vector>
#include <functional>
#include <cctype>
#include <locale>
#include <sstream>
void split(std::vector<std::string>& dest, std::string const str, char const* delim)
{
char* pTempStr = strdup(str.c_str());
char* pWord = strtok(pTempStr, delim);
while (pWord != nullptr)
{
dest.push_back(pWord);
pWord = strtok(nullptr, delim);
}
free(pTempStr);
}
std::vector<std::string>& split(std::string const s, char delim, std::vector<std::string>& elems)
{
std::stringstream ss(s);
std::string item;
while (getline(ss, item, delim))
{
elems.push_back(item);
}
return elems;
}
std::vector<std::string> split(std::string const s, char delim)
{
std::vector<std::string> elems;
return split(s, delim, elems);
}
#endif

44
src/LazyCalculatedValue.h Normal file
View File

@@ -0,0 +1,44 @@
/*
* 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.
*/
#ifndef _PLAYERBOT_LAZYCALCULATEDVALUE_H
#define _PLAYERBOT_LAZYCALCULATEDVALUE_H
template <class TValue, class TOwner>
class LazyCalculatedValue
{
public:
typedef TValue (TOwner::*Calculator)();
public:
LazyCalculatedValue(TOwner* owner, Calculator calculator) : calculator(calculator), owner(owner)
{
Reset();
}
public:
TValue GetValue()
{
if (!calculated)
{
value = (owner->*calculator)();
calculated = true;
}
return value;
}
void Reset()
{
calculated = false;
}
protected:
Calculator calculator;
TOwner* owner;
bool calculated;
TValue value;
};
#endif

247
src/LootObjectStack.cpp Normal file
View File

@@ -0,0 +1,247 @@
/*
* 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 "LootObjectStack.h"
#include "LootMgr.h"
#include "Playerbots.h"
#define MAX_LOOT_OBJECT_COUNT 10
LootTarget::LootTarget(ObjectGuid guid) : guid(guid), asOfTime(time(nullptr))
{
}
LootTarget::LootTarget(LootTarget const& other)
{
guid = other.guid;
asOfTime = other.asOfTime;
}
LootTarget& LootTarget::operator=(LootTarget const& other)
{
if ((void*)this == (void*)&other)
return *this;
guid = other.guid;
asOfTime = other.asOfTime;
return *this;
}
bool LootTarget::operator<(LootTarget const& other) const
{
return guid < other.guid;
}
void LootTargetList::shrink(time_t fromTime)
{
for (std::set<LootTarget>::iterator i = begin(); i != end(); )
{
if (i->asOfTime <= fromTime)
erase(i++);
else
++i;
}
}
LootObject::LootObject(Player* bot, ObjectGuid guid) : guid(), skillId(SKILL_NONE), reqSkillValue(0), reqItem(0)
{
Refresh(bot, guid);
}
void LootObject::Refresh(Player* bot, ObjectGuid lootGUID)
{
skillId = SKILL_NONE;
reqSkillValue = 0;
reqItem = 0;
guid.Clear();
PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot);
Creature* creature = botAI->GetCreature(lootGUID);
if (creature && creature->getDeathState() == CORPSE)
{
if (creature->HasFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE))
guid = lootGUID;
if (creature->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE))
{
skillId = creature->GetCreatureTemplate()->GetRequiredLootSkill();
uint32 targetLevel = creature->getLevel();
reqSkillValue = targetLevel < 10 ? 1 : targetLevel < 20 ? (targetLevel - 10) * 10 : targetLevel * 5;
if (botAI->HasSkill((SkillType) skillId) && bot->GetSkillValue(skillId) >= reqSkillValue)
guid = lootGUID;
}
return;
}
GameObject* go = botAI->GetGameObject(lootGUID);
if (go && go->isSpawned() && go->GetGoState() == GO_STATE_READY)
{
uint32 lockId = go->GetGOInfo()->GetLockId();
LockEntry const* lockInfo = sLockStore.LookupEntry(lockId);
if (!lockInfo)
return;
for (uint8 i = 0; i < 8; ++i)
{
switch (lockInfo->Type[i])
{
case LOCK_KEY_ITEM:
if (lockInfo->Index[i] > 0)
{
reqItem = lockInfo->Index[i];
guid = lootGUID;
}
break;
case LOCK_KEY_SKILL:
if (SkillByLockType(LockType(lockInfo->Index[i])) > 0)
{
skillId = SkillByLockType(LockType(lockInfo->Index[i]));
reqSkillValue = std::max((uint32)1, lockInfo->Skill[i]);
guid = lootGUID;
}
break;
case LOCK_KEY_NONE:
guid = lootGUID;
break;
}
}
}
}
WorldObject* LootObject::GetWorldObject(Player* bot)
{
Refresh(bot, guid);
PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot);
Creature* creature = botAI->GetCreature(guid);
if (creature && creature->getDeathState() == CORPSE)
return creature;
GameObject* go = botAI->GetGameObject(guid);
if (go && go->isSpawned())
return go;
return nullptr;
}
LootObject::LootObject(LootObject const& other)
{
guid = other.guid;
skillId = other.skillId;
reqSkillValue = other.reqSkillValue;
reqItem = other.reqItem;
}
bool LootObject::IsLootPossible(Player* bot)
{
if (IsEmpty() || !GetWorldObject(bot))
return false;
PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot);
if (reqItem && !bot->HasItemCount(reqItem, 1))
return false;
if (abs(GetWorldObject(bot)->GetPositionZ() - bot->GetPositionZ()) > INTERACTION_DISTANCE)
return false;
Creature* creature = botAI->GetCreature(guid);
if (creature && creature->getDeathState() == CORPSE)
{
if (!creature->loot.hasItemFor(bot) && skillId != SKILL_SKINNING)
return false;
}
if (skillId == SKILL_NONE)
return true;
if (skillId == SKILL_FISHING)
return false;
if (!botAI->HasSkill((SkillType)skillId))
return false;
if (!reqSkillValue)
return true;
uint32 skillValue = uint32(bot->GetSkillValue(skillId));
if (reqSkillValue > skillValue)
return false;
if (skillId == SKILL_MINING && !bot->HasItemCount(2901, 1))
return false;
if (skillId == SKILL_SKINNING && !bot->HasItemCount(7005, 1))
return false;
return true;
}
bool LootObjectStack::Add(ObjectGuid guid)
{
if (!availableLoot.insert(guid).second)
return false;
if (availableLoot.size() < MAX_LOOT_OBJECT_COUNT)
return true;
std::vector<LootObject> ordered = OrderByDistance();
for (size_t i = MAX_LOOT_OBJECT_COUNT; i < ordered.size(); i++)
Remove(ordered[i].guid);
return true;
}
void LootObjectStack::Remove(ObjectGuid guid)
{
LootTargetList::iterator i = availableLoot.find(guid);
if (i != availableLoot.end())
availableLoot.erase(i);
}
void LootObjectStack::Clear()
{
availableLoot.clear();
}
bool LootObjectStack::CanLoot(float maxDistance)
{
std::vector<LootObject> ordered = OrderByDistance(maxDistance);
return !ordered.empty();
}
LootObject LootObjectStack::GetLoot(float maxDistance)
{
std::vector<LootObject> ordered = OrderByDistance(maxDistance);
return ordered.empty() ? LootObject() : *ordered.begin();
}
std::vector<LootObject> LootObjectStack::OrderByDistance(float maxDistance)
{
availableLoot.shrink(time(nullptr) - 30);
std::map<float, LootObject> sortedMap;
LootTargetList safeCopy(availableLoot);
for (LootTargetList::iterator i = safeCopy.begin(); i != safeCopy.end(); i++)
{
ObjectGuid guid = i->guid;
LootObject lootObject(bot, guid);
if (!lootObject.IsLootPossible(bot))
continue;
float distance = bot->GetDistance(lootObject.GetWorldObject(bot));
if (!maxDistance || distance <= maxDistance)
sortedMap[distance] = lootObject;
}
std::vector<LootObject> result;
for (std::map<float, LootObject>::iterator i = sortedMap.begin(); i != sortedMap.end(); i++)
result.push_back(i->second);
return result;
}

82
src/LootObjectStack.h Normal file
View File

@@ -0,0 +1,82 @@
/*
* 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.
*/
#ifndef _PLAYERBOT_LOOTOBJECTSTACK_H
#define _PLAYERBOT_LOOTOBJECTSTACK_H
#include "ObjectGuid.h"
class AiObjectContext;
class Player;
class WorldObject;
struct ItemTemplate;
class LootStrategy
{
public:
LootStrategy() { }
virtual ~LootStrategy() { };
virtual bool CanLoot(ItemTemplate const* proto, AiObjectContext* context) = 0;
virtual std::string const GetName() = 0;
};
class LootObject
{
public:
LootObject() : skillId(0), reqSkillValue(0), reqItem(0) { }
LootObject(Player* bot, ObjectGuid guid);
LootObject(LootObject const& other);
bool IsEmpty() { return !guid; }
bool IsLootPossible(Player* bot);
void Refresh(Player* bot, ObjectGuid guid);
WorldObject* GetWorldObject(Player* bot);
ObjectGuid guid;
uint32 skillId;
uint32 reqSkillValue;
uint32 reqItem;
};
class LootTarget
{
public:
LootTarget(ObjectGuid guid);
LootTarget(LootTarget const& other);
public:
LootTarget& operator=(LootTarget const& other);
bool operator<(LootTarget const& other) const;
public:
ObjectGuid guid;
time_t asOfTime;
};
class LootTargetList : public std::set<LootTarget>
{
public:
void shrink(time_t fromTime);
};
class LootObjectStack
{
public:
LootObjectStack(Player* bot) : bot(bot) { }
bool Add(ObjectGuid guid);
void Remove(ObjectGuid guid);
void Clear();
bool CanLoot(float maxDistance);
LootObject GetLoot(float maxDistance = 0);
private:
std::vector<LootObject> OrderByDistance(float maxDistance = 0);
Player* bot;
LootTargetList availableLoot;
};
#endif

View File

@@ -1,15 +0,0 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3
*/
// From SC
void AddMyPlayerScripts();
// Add all
// cf. the naming convention https://github.com/azerothcore/azerothcore-wotlk/blob/master/doc/changelog/master.md#how-to-upgrade-4
// additionally replace all '-' in the module folder name with '_' here
void Addskeleton_moduleScripts()
{
AddMyPlayerScripts();
}

View File

@@ -1,29 +0,0 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3
*/
#include "ScriptMgr.h"
#include "Player.h"
#include "Config.h"
#include "Chat.h"
// Add player scripts
class MyPlayer : public PlayerScript
{
public:
MyPlayer() : PlayerScript("MyPlayer") { }
void OnLogin(Player* player) override
{
if (sConfigMgr->GetOption<bool>("MyModule.Enable", false))
{
ChatHandler(player->GetSession()).SendSysMessage("Hello World from Skeleton-Module!");
}
}
};
// Add all scripts in one
void AddMyPlayerScripts()
{
new MyPlayer();
}

257
src/PerformanceMonitor.cpp Normal file
View File

@@ -0,0 +1,257 @@
/*
* 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 "PerformanceMonitor.h"
#include "Playerbots.h"
PerformanceMonitorOperation* PerformanceMonitor::start(PerformanceMetric metric, std::string const name, PerformanceStack* stack)
{
if (!sPlayerbotAIConfig->perfMonEnabled)
return nullptr;
std::string stackName = name;
if (stack)
{
if (!stack->empty())
{
std::ostringstream out;
out << stackName << " [";
for (std::vector<std::string>::reverse_iterator i = stack->rbegin(); i != stack->rend(); ++i)
out << *i << (std::next(i) == stack->rend() ? "" : "|");
out << "]";
stackName = out.str().c_str();
}
stack->push_back(name);
}
std::lock_guard<std::mutex> guard(lock);
PerformanceData* pd = data[metric][stackName];
if (!pd)
{
pd = new PerformanceData();
pd->minTime = 0;
pd->maxTime = 0;
pd->totalTime = 0;
pd->count = 0;
data[metric][stackName] = pd;
}
return new PerformanceMonitorOperation(pd, name, stack);
}
void PerformanceMonitor::PrintStats(bool perTick, bool fullStack)
{
if (data.empty())
return;
uint32 total = 0;
if (!perTick)
{
for (auto& map : data[PERF_MON_TOTAL])
if (map.first.find("PlayerbotAI::UpdateAIInternal") != std::string::npos)
total += map.second->totalTime;
LOG_INFO("playerbots", "--------------------------------------[TOTAL BOT]------------------------------------------------------");
LOG_INFO("playerbots", "percentage time | min .. max ( avg of count ) - type : name ");
for (std::map<PerformanceMetric, std::map<std::string, PerformanceData*>>::iterator i = data.begin(); i != data.end(); ++i)
{
std::map<std::string, PerformanceData*> pdMap = i->second;
std::string key;
switch (i->first)
{
case PERF_MON_TRIGGER:
key = "T";
break;
case PERF_MON_VALUE:
key = "V";
break;
case PERF_MON_ACTION:
key = "A";
break;
case PERF_MON_RNDBOT:
key = "RndBot";
break;
case PERF_MON_TOTAL:
key = "Total";
break;
default:
key = "?";
break;
}
std::vector<std::string> names;
for (std::map<std::string, PerformanceData*>::iterator j = pdMap.begin(); j != pdMap.end(); ++j)
{
if (key == "Total" && j->first.find("PlayerbotAI::UpdateAIInternal") == std::string::npos)
continue;
names.push_back(j->first);
}
std::sort(names.begin(), names.end(), [pdMap](std::string const i, std::string const j)
{
return pdMap.at(i)->totalTime < pdMap.at(j)->totalTime;
});
for (auto& name : names)
{
PerformanceData* pd = pdMap[name];
float perc = (float)pd->totalTime / (float)total * 100.0f;
float secs = (float)pd->totalTime / 1000.0f;
float avg = (float)pd->totalTime / (float)pd->count;
std::string disName = name;
if (!fullStack && disName.find("|") != std::string::npos)
disName = disName.substr(0, disName.find("|")) + "]";
if (avg >= 0.5f || pd->maxTime > 10)
{
LOG_INFO("playerbots", "%7.3f%% %10.3fs | %6u .. %6u (%9.4f of %10u) - {} : {}"
, perc
, secs
, pd->minTime
, pd->maxTime
, avg
, pd->count
, key.c_str()
, disName.c_str());
}
}
LOG_INFO("playerbots", " ");
}
}
else
{
float totalCount = data[PERF_MON_TOTAL]["RandomPlayerbotMgr::FullTick"]->count;
total = data[PERF_MON_TOTAL]["RandomPlayerbotMgr::FullTick"]->totalTime;
LOG_INFO("playerbots", " ");
LOG_INFO("playerbots", " ");
LOG_INFO("playerbots", "---------------------------------------[PER TICK]------------------------------------------------------");
LOG_INFO("playerbots", "percentage time | min .. max ( avg of count ) - type : name ");
for (std::map<PerformanceMetric, std::map<std::string, PerformanceData*>>::iterator i = data.begin(); i != data.end(); ++i)
{
std::map<std::string, PerformanceData*> pdMap = i->second;
std::string key;
switch (i->first)
{
case PERF_MON_TRIGGER:
key = "T";
break;
case PERF_MON_VALUE:
key = "V";
break;
case PERF_MON_ACTION:
key = "A";
break;
case PERF_MON_RNDBOT:
key = "RndBot";
break;
case PERF_MON_TOTAL:
key = "Total";
break;
default:
key = "?";
}
std::vector<std::string> names;
for (std::map<std::string, PerformanceData*>::iterator j = pdMap.begin(); j != pdMap.end(); ++j)
{
names.push_back(j->first);
}
std::sort(names.begin(), names.end(), [pdMap](std::string const i, std::string const j)
{
return pdMap.at(i)->totalTime < pdMap.at(j)->totalTime;
});
for (auto& name : names)
{
PerformanceData* pd = pdMap[name];
float perc = (float)pd->totalTime / (float)total * 100.0f;
uint32 secs = pd->totalTime / totalCount;
float avg = (float)pd->totalTime / (float)pd->count;
float amount = (float)pd->count / (float)totalCount;
std::string disName = name;
if (!fullStack && disName.find("|") != std::string::npos)
disName = disName.substr(0, disName.find("|")) + "]";
if (avg >= 0.5f || pd->maxTime > 10)
{
LOG_INFO("playerbots", "%7.3f%% %9ums | %6u .. %6u (%9.4f of %10.3f) - {} : {}"
, perc
, secs
, pd->minTime
, pd->maxTime
, avg
, amount
, key.c_str()
, disName.c_str());
}
}
LOG_INFO("playerbots", " ");
}
}
}
void PerformanceMonitor::Reset()
{
for (std::map<PerformanceMetric, std::map<std::string, PerformanceData*> >::iterator i = data.begin(); i != data.end(); ++i)
{
std::map<std::string, PerformanceData*> pdMap = i->second;
for (std::map<std::string, PerformanceData*>::iterator j = pdMap.begin(); j != pdMap.end(); ++j)
{
PerformanceData* pd = j->second;
std::lock_guard<std::mutex> guard(pd->lock);
pd->minTime = 0;
pd->maxTime = 0;
pd->totalTime = 0;
pd->count = 0;
}
}
}
PerformanceMonitorOperation::PerformanceMonitorOperation(PerformanceData* data, std::string const name, PerformanceStack* stack) : data(data), name(name), stack(stack)
{
started = (std::chrono::time_point_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now())).time_since_epoch();
}
void PerformanceMonitorOperation::finish()
{
std::chrono::milliseconds finished = (std::chrono::time_point_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now())).time_since_epoch();
uint32 elapsed = (finished - started).count();
std::lock_guard<std::mutex> guard(data->lock);
if (elapsed > 0)
{
if (!data->minTime || data->minTime > elapsed)
data->minTime = elapsed;
if (!data->maxTime || data->maxTime < elapsed)
data->maxTime = elapsed;
data->totalTime += elapsed;
}
++data->count;
if (stack)
{
stack->erase(std::remove(stack->begin(), stack->end(), name), stack->end());
}
delete this;
}

72
src/PerformanceMonitor.h Normal file
View File

@@ -0,0 +1,72 @@
/*
* 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.
*/
#ifndef _PLAYERBOT_PERFORMANCEMONITOR_H
#define _PLAYERBOT_PERFORMANCEMONITOR_H
#include "Common.h"
#include <chrono>
#include <ctime>
#include <map>
#include <mutex>
#include <vector>
typedef std::vector<std::string> PerformanceStack;
struct PerformanceData
{
uint32 minTime;
uint32 maxTime;
uint32 totalTime;
uint32 count;
std::mutex lock;
};
enum PerformanceMetric
{
PERF_MON_TRIGGER,
PERF_MON_VALUE,
PERF_MON_ACTION,
PERF_MON_RNDBOT,
PERF_MON_TOTAL
};
class PerformanceMonitorOperation
{
public:
PerformanceMonitorOperation(PerformanceData* data, std::string const name, PerformanceStack* stack);
void finish();
private:
PerformanceData* data;
std::string const name;
PerformanceStack* stack;
std::chrono::milliseconds started;
};
class PerformanceMonitor
{
public:
PerformanceMonitor() { };
virtual ~PerformanceMonitor() { };
static PerformanceMonitor* instance()
{
static PerformanceMonitor instance;
return &instance;
}
public:
PerformanceMonitorOperation* start(PerformanceMetric metric, std::string const name, PerformanceStack* stack = nullptr);
void PrintStats(bool perTick = false, bool fullStack = false);
void Reset();
private:
std::map<PerformanceMetric, std::map<std::string, PerformanceData*> > data;
std::mutex lock;
};
#define sPerformanceMonitor PerformanceMonitor::instance()
#endif

3408
src/PlayerbotAI.cpp Normal file

File diff suppressed because it is too large Load Diff

392
src/PlayerbotAI.h Normal file
View File

@@ -0,0 +1,392 @@
/*
* 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.
*/
#ifndef _PLAYERBOT_PLAYERbotAI_H
#define _PLAYERBOT_PLAYERbotAI_H
#include "Chat.h"
#include "ChatHelper.h"
#include "ChatFilter.h"
#include "Common.h"
#include "Event.h"
#include "PlayerbotAIBase.h"
#include "PlayerbotAIConfig.h"
#include "PlayerbotSecurity.h"
#include "WorldPacket.h"
#include <stack>
#include <queue>
class AiObjectContext;
class Creature;
class Engine;
class ExternalEventHelper;
class Gameobject;
class Item;
class ObjectGuid;
class Player;
class PlayerbotMgr;
class Spell;
class SpellInfo;
class Unit;
class WorldObject;
class WorldPosition;
struct CreatureData;
struct GameObjectData;
enum StrategyType : uint32;
enum HealingItemDisplayId
{
HEALTHSTONE_DISPLAYID = 8026,
MAJOR_HEALING_POTION = 24152,
WHIPPER_ROOT_TUBER = 21974,
NIGHT_DRAGON_BREATH = 21975,
LIMITED_INVULNERABILITY_POTION = 24213,
GREATER_DREAMLESS_SLEEP_POTION = 17403,
SUPERIOR_HEALING_POTION = 15714,
CRYSTAL_RESTORE = 2516,
DREAMLESS_SLEEP_POTION = 17403,
GREATER_HEALING_POTION = 15713,
HEALING_POTION = 15712,
LESSER_HEALING_POTION = 15711,
DISCOLORED_HEALING_POTION = 15736,
MINOR_HEALING_POTION = 15710,
VOLATILE_HEALING_POTION = 24212,
SUPER_HEALING_POTION = 37807,
CRYSTAL_HEALING_POTION = 47132,
FEL_REGENERATION_POTION = 37864,
MAJOR_DREAMLESS_SLEEP_POTION = 37845
};
enum BotState
{
BOT_STATE_COMBAT = 0,
BOT_STATE_NON_COMBAT = 1,
BOT_STATE_DEAD = 2,
BOT_STATE_MAX
};
bool IsAlliance(uint8 race);
class PlayerbotChatHandler: protected ChatHandler
{
public:
explicit PlayerbotChatHandler(Player* pMasterPlayer);
void sysmessage(std::string const str) { SendSysMessage(str.c_str()); }
uint32 extractQuestId(std::string const str);
uint32 extractSpellId(std::string const str)
{
char* source = (char*)str.c_str();
return extractSpellIdFromLink(source);
}
};
class MinValueCalculator
{
public:
MinValueCalculator(float def = 0.0f) : param(nullptr), minValue(def) { }
void probe(float value, void* p)
{
if (!param || minValue >= value)
{
minValue = value;
param = p;
}
}
void* param;
float minValue;
};
enum RoguePoisonDisplayId
{
DEADLY_POISON_DISPLAYID = 13707,
INSTANT_POISON_DISPLAYID = 13710,
WOUND_POISON_DISPLAYID = 37278
};
enum SharpeningStoneDisplayId
{
ROUGH_SHARPENING_DISPLAYID = 24673,
COARSE_SHARPENING_DISPLAYID = 24674,
HEAVY_SHARPENING_DISPLAYID = 24675,
SOLID_SHARPENING_DISPLAYID = 24676,
DENSE_SHARPENING_DISPLAYID = 24677,
CONSECRATED_SHARPENING_DISPLAYID = 24674, // will not be used because bot can not know if it will face undead targets
ELEMENTAL_SHARPENING_DISPLAYID = 21072,
FEL_SHARPENING_DISPLAYID = 39192,
ADAMANTITE_SHARPENING_DISPLAYID = 39193
};
enum WeightStoneDisplayId
{
ROUGH_WEIGHTSTONE_DISPLAYID = 24683,
COARSE_WEIGHTSTONE_DISPLAYID = 24684,
HEAVY_WEIGHTSTONE_DISPLAYID = 24685,
SOLID_WEIGHTSTONE_DISPLAYID = 24686,
DENSE_WEIGHTSTONE_DISPLAYID = 24687,
FEL_WEIGHTSTONE_DISPLAYID = 39548,
ADAMANTITE_WEIGHTSTONE_DISPLAYID = 39549
};
enum WizardOilDisplayId
{
MINOR_WIZARD_OIL = 9731,
LESSER_WIZARD_OIL = 47903,
BRILLIANT_WIZARD_OIL = 47901,
WIZARD_OIL = 47905,
SUPERIOR_WIZARD_OIL = 47904,
/// Blessed Wizard Oil = 26865 //scourge inv
};
enum ManaOilDisplayId
{
MINOR_MANA_OIL = 34492,
LESSER_MANA_OIL = 47902,
BRILLIANT_MANA_OIL = 41488,
SUPERIOR_MANA_OIL = 36862
};
enum ShieldWardDisplayId
{
LESSER_WARD_OFSHIELDING = 38759,
GREATER_WARD_OFSHIELDING = 38760
};
enum class BotTypeNumber : uint8
{
ACTIVITY_TYPE_NUMBER = 1,
GROUPER_TYPE_NUMBER = 2,
GUILDER_TYPE_NUMBER = 3,
};
enum class GrouperType : uint8
{
SOLO = 0,
MEMBER = 1,
LEADER_2 = 2,
LEADER_3 = 3,
LEADER_4 = 4,
LEADER_5 = 5
};
enum class GuilderType : uint8
{
SOLO = 0,
TINY = 30,
SMALL = 50,
MEDIUM = 70,
LARGE = 120,
HUGE = 250
};
enum ActivityType
{
GRIND_ACTIVITY = 1,
RPG_ACTIVITY = 2,
TRAVEL_ACTIVITY = 3,
OUT_OF_PARTY_ACTIVITY = 4,
PACKET_ACTIVITY = 5,
DETAILED_MOVE_ACTIVITY = 6,
PARTY_ACTIVITY = 7,
ALL_ACTIVITY = 8,
MAX_ACTIVITY_TYPE
};
enum BotRoles : uint8
{
BOT_ROLE_NONE = 0x00,
BOT_ROLE_TANK = 0x01,
BOT_ROLE_HEALER = 0x02,
BOT_ROLE_DPS = 0x04
};
class PacketHandlingHelper
{
public:
void AddHandler(uint16 opcode, std::string const handler);
void Handle(ExternalEventHelper &helper);
void AddPacket(WorldPacket const& packet);
private:
std::map<uint16, std::string> handlers;
std::stack<WorldPacket> queue;
};
class ChatCommandHolder
{
public:
ChatCommandHolder(std::string const command, Player* owner = nullptr, uint32 type = CHAT_MSG_WHISPER, time_t time = 0) : command(command), owner(owner), type(type), time(time) { }
ChatCommandHolder(ChatCommandHolder const& other) : command(other.command), owner(other.owner), type(other.type), time(other.time) { }
std::string const GetCommand() { return command; }
Player* GetOwner() { return owner; }
uint32 GetType() { return type; }
time_t GetTime() { return time; }
private:
std::string const command;
Player* owner;
uint32 type;
time_t time;
};
class PlayerbotAI : public PlayerbotAIBase
{
public:
PlayerbotAI();
PlayerbotAI(Player* bot);
virtual ~PlayerbotAI();
void UpdateAI(uint32 elapsed, bool minimal = false) override;
void UpdateAIInternal(uint32 elapsed, bool minimal = false) override;
std::string const HandleRemoteCommand(std::string const command);
void HandleCommand(uint32 type, std::string const text, Player* fromPlayer);
void HandleBotOutgoingPacket(WorldPacket const& packet);
void HandleMasterIncomingPacket(WorldPacket const& packet);
void HandleMasterOutgoingPacket(WorldPacket const& packet);
void HandleTeleportAck();
void ChangeEngine(BotState type);
void DoNextAction(bool minimal = false);
virtual bool DoSpecificAction(std::string const name, Event event = Event(), bool silent = false, std::string const qualifier = "");
void ChangeStrategy(std::string const name, BotState type);
void ClearStrategies(BotState type);
std::vector<std::string> GetStrategies(BotState type);
bool ContainsStrategy(StrategyType type);
bool HasStrategy(std::string const name, BotState type);
BotState GetState() { return currentState; };
void ResetStrategies(bool load = true);
void ReInitCurrentEngine();
void Reset(bool full = false);
bool IsTank(Player* player);
bool IsHeal(Player* player);
bool IsRanged(Player* player);
Creature* GetCreature(ObjectGuid guid);
Unit* GetUnit(ObjectGuid guid);
Player* GetPlayer(ObjectGuid guid);
static Unit* GetUnit(CreatureData const* creatureData);
GameObject* GetGameObject(ObjectGuid guid);
static GameObject* GetGameObject(GameObjectData const* gameObjectData);
WorldObject* GetWorldObject(ObjectGuid guid);
bool TellMaster(std::ostringstream& stream, PlayerbotSecurityLevel securityLevel = PLAYERBOT_SECURITY_ALLOW_ALL);
bool TellMaster(std::string const text, PlayerbotSecurityLevel securityLevel = PLAYERBOT_SECURITY_ALLOW_ALL);
bool TellMasterNoFacing(std::ostringstream& stream, PlayerbotSecurityLevel securityLevel = PLAYERBOT_SECURITY_ALLOW_ALL);
bool TellMasterNoFacing(std::string const text, PlayerbotSecurityLevel securityLevel = PLAYERBOT_SECURITY_ALLOW_ALL);
bool TellError(std::string const text, PlayerbotSecurityLevel securityLevel = PLAYERBOT_SECURITY_ALLOW_ALL);
void SpellInterrupted(uint32 spellid);
int32 CalculateGlobalCooldown(uint32 spellid);
void InterruptSpell();
void RemoveAura(std::string const name);
void RemoveShapeshift();
void WaitForSpellCast(Spell* spell);
bool PlaySound(uint32 emote);
bool PlayEmote(uint32 emote);
void Ping(float x, float y);
Item* FindPoison() const;
Item* FindBandage() const;
Item* FindConsumable(uint32 displayId) const;
Item* FindStoneFor(Item* weapon) const;
Item* FindOilFor(Item* weapon) const;
void ImbueItem(Item* item, uint32 targetFlag, ObjectGuid targetGUID);
void ImbueItem(Item* item, uint8 targetInventorySlot);
void ImbueItem(Item* item, Unit* target);
void ImbueItem(Item* item);
void EnchantItemT(uint32 spellid, uint8 slot);
uint32 GetBuffedCount(Player* player, std::string const spellname);
virtual bool CanCastSpell(std::string const name, Unit* target, Item* itemTarget = nullptr);
virtual bool CastSpell(std::string const name, Unit* target, Item* itemTarget = nullptr);
virtual bool HasAura(std::string const spellName, Unit* player, bool maxStack = false);
virtual bool HasAnyAuraOf(Unit* player, ...);
virtual bool IsInterruptableSpellCasting(Unit* player, std::string const spell);
virtual bool HasAuraToDispel(Unit* player, uint32 dispelType);
bool CanCastSpell(uint32 spellid, Unit* target, bool checkHasSpell = true, Item* itemTarget = nullptr);
bool CanCastSpell(uint32 spellid, GameObject* goTarget, uint8 effectMask, bool checkHasSpell = true);
bool CanCastSpell(uint32 spellid, float x, float y, float z, uint8 effectMask, bool checkHasSpell = true, Item* itemTarget = nullptr);
bool HasAura(uint32 spellId, Unit const* player);
bool CastSpell(uint32 spellId, Unit* target, Item* itemTarget = nullptr);
bool CastSpell(uint32 spellId, float x, float y, float z, Item* itemTarget = nullptr);
bool canDispel(SpellInfo const* spellInfo, uint32 dispelType);
bool CanCastVehicleSpell(uint32 spellid, Unit* target);
bool CastVehicleSpell(uint32 spellId, Unit* target);
bool CastVehicleSpell(uint32 spellId, float x, float y, float z);
bool IsInVehicle(bool canControl = false, bool canCast = false, bool canAttack = false, bool canTurn = false, bool fixed = false);
uint32 GetEquipGearScore(Player* player, bool withBags, bool withBank);
bool HasSkill(SkillType skill);
bool IsAllowedCommand(std::string const text);
float GetRange(std::string const type);
Player* GetBot() { return bot; }
Player* GetMaster() { return master; }
//Checks if the bot is really a player. Players always have themselves as master.
bool IsRealPlayer() { return master ? (master == bot) : false; }
//Bot has a master that is a player.
bool HasRealPlayerMaster();
//Bot has a master that is activly playing.
bool HasActivePlayerMaster();
//Get the group leader or the master of the bot.
//Checks if the bot is summoned as alt of a player
bool IsAlt();
Player* GetGroupMaster();
//Returns a semi-random (cycling) number that is fixed for each bot.
uint32 GetFixedBotNumer(BotTypeNumber typeNumber, uint32 maxNum = 100, float cyclePerMin = 1);
GrouperType GetGrouperType();
GuilderType GetGuilderType();
bool HasPlayerNearby(WorldPosition* pos, float range = sPlayerbotAIConfig->reactDistance);
bool HasPlayerNearby(float range = sPlayerbotAIConfig->reactDistance);
bool HasManyPlayersNearby(uint32 trigerrValue = 20, float range = sPlayerbotAIConfig->sightDistance);
bool AllowActive(ActivityType activityType);
bool AllowActivity(ActivityType activityType = ALL_ACTIVITY, bool checkNow = false);
bool HasCheat(BotCheatMask mask) { return ((uint32)mask & (uint32)cheatMask) != 0 || ((uint32)mask & (uint32)sPlayerbotAIConfig->botCheatMask) != 0; }
BotCheatMask GetCheat() { return cheatMask; }
void SetCheat(BotCheatMask mask) { cheatMask = mask; }
void SetMaster(Player* newMaster) { master = newMaster; }
AiObjectContext* GetAiObjectContext() { return aiObjectContext; }
ChatHelper* GetChatHelper() { return &chatHelper; }
bool IsOpposing(Player* player);
static bool IsOpposing(uint8 race1, uint8 race2);
PlayerbotSecurity* GetSecurity() { return &security; }
private:
void _fillGearScoreData(Player* player, Item* item, std::vector<uint32>* gearScore, uint32& twoHandScore);
bool IsTellAllowed(PlayerbotSecurityLevel securityLevel = PLAYERBOT_SECURITY_ALLOW_ALL);
protected:
Player* bot;
Player* master;
uint32 accountId;
AiObjectContext* aiObjectContext;
Engine* currentEngine;
Engine* engines[BOT_STATE_MAX];
BotState currentState;
ChatHelper chatHelper;
std::queue<ChatCommandHolder> chatCommands;
PacketHandlingHelper botOutgoingPacketHandlers;
PacketHandlingHelper masterIncomingPacketHandlers;
PacketHandlingHelper masterOutgoingPacketHandlers;
CompositeChatFilter chatFilter;
PlayerbotSecurity security;
std::map<std::string, time_t> whispers;
std::pair<ChatMsg, time_t> currentChat;
static std::set<std::string> unsecuredCommands;
bool allowActive[MAX_ACTIVITY_TYPE];
time_t allowActiveCheckTimer[MAX_ACTIVITY_TYPE];
bool inCombat = false;
BotCheatMask cheatMask = BotCheatMask::none;
};
#endif

19
src/PlayerbotAIAware.h Normal file
View File

@@ -0,0 +1,19 @@
/*
* 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.
*/
#ifndef _PLAYERBOT_PLAYERbotAIAWARE_H
#define _PLAYERBOT_PLAYERbotAIAWARE_H
class PlayerbotAI;
class PlayerbotAIAware
{
public:
PlayerbotAIAware(PlayerbotAI* botAI) : botAI(botAI) { }
protected:
PlayerbotAI* botAI;
};
#endif

67
src/PlayerbotAIBase.cpp Normal file
View File

@@ -0,0 +1,67 @@
/*
* 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 "PlayerbotAIBase.h"
#include "Playerbots.h"
PlayerbotAIBase::PlayerbotAIBase(bool isBotAI) : nextAICheckDelay(0), _isBotAI(isBotAI)
{
}
void PlayerbotAIBase::UpdateAI(uint32 elapsed, bool minimal)
{
if (nextAICheckDelay > elapsed)
nextAICheckDelay -= elapsed;
else
nextAICheckDelay = 0;
if (!CanUpdateAI())
return;
UpdateAIInternal(elapsed, minimal);
YieldThread();
}
void PlayerbotAIBase::SetNextCheckDelay(uint32 const delay)
{
if (nextAICheckDelay < delay)
LOG_DEBUG("playerbots", "Setting lesser delay {} -> {}", nextAICheckDelay, delay);
nextAICheckDelay = delay;
if (nextAICheckDelay > sPlayerbotAIConfig->globalCoolDown)
LOG_DEBUG("playerbots", "std::set next check delay: {}", nextAICheckDelay);
}
void PlayerbotAIBase::IncreaseNextCheckDelay(uint32 delay)
{
nextAICheckDelay += delay;
if (nextAICheckDelay > sPlayerbotAIConfig->globalCoolDown)
LOG_DEBUG("playerbots", "increase next check delay: {}", nextAICheckDelay);
}
bool PlayerbotAIBase::CanUpdateAI()
{
return nextAICheckDelay < 100;
}
void PlayerbotAIBase::YieldThread(bool delay)
{
if (nextAICheckDelay < sPlayerbotAIConfig->reactDelay)
nextAICheckDelay = sPlayerbotAIConfig->reactDelay;
if (delay && nextAICheckDelay <= sPlayerbotAIConfig->reactDelay * 5)
nextAICheckDelay = sPlayerbotAIConfig->reactDelay * 5;
}
bool PlayerbotAIBase::IsActive()
{
return nextAICheckDelay < sPlayerbotAIConfig->maxWaitForMove;
}
bool PlayerbotAIBase::IsBotAI() const
{
return _isBotAI;
}

31
src/PlayerbotAIBase.h Normal file
View File

@@ -0,0 +1,31 @@
/*
* 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.
*/
#ifndef _PLAYERBOT_PLAYERBOTAIBASE_H
#define _PLAYERBOT_PLAYERBOTAIBASE_H
#include "Define.h"
class PlayerbotAIBase
{
public:
PlayerbotAIBase(bool isBotAI);
bool CanUpdateAI();
void SetNextCheckDelay(uint32 const delay);
void IncreaseNextCheckDelay(uint32 delay);
void YieldThread(bool delay = false);
virtual void UpdateAI(uint32 elapsed, bool minimal = false);
virtual void UpdateAIInternal(uint32 elapsed, bool minimal = false) = 0;
bool IsActive();
bool IsBotAI() const;
protected:
uint32 nextAICheckDelay;
private:
bool _isBotAI;
};
#endif

481
src/PlayerbotAIConfig.cpp Normal file
View File

@@ -0,0 +1,481 @@
/*
* 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 "PlayerbotAIConfig.h"
#include "Config.h"
#include "Playerbots.h"
#include "PlayerbotFactory.h"
#include "RandomItemMgr.h"
#include "RandomPlayerbotFactory.h"
#include "TalentSpec.h"
#include <iostream>
template <class T>
void LoadList(std::string const value, T& list)
{
std::vector<std::string> ids = split(value, ',');
for (std::vector<std::string>::iterator i = ids.begin(); i != ids.end(); i++)
{
uint32 id = atoi((*i).c_str());
if (!id)
continue;
list.push_back(id);
}
}
template <class T>
void LoadListString(std::string const value, T& list)
{
std::vector<std::string> strings = split(value, ',');
for (std::vector<std::string>::iterator i = strings.begin(); i != strings.end(); i++)
{
std::string const string = *i;
if (string.empty())
continue;
list.push_back(string);
}
}
bool PlayerbotAIConfig::Initialize()
{
LOG_INFO("server.loading", "Initializing AI Playerbots by ike3, based on the original Playerbots by blueboy");
enabled = sConfigMgr->GetBoolDefault("AiPlayerbot.Enabled", true);
if (!enabled)
{
LOG_INFO("server.loading", "AI Playerbots is Disabled in aiplayerbot.conf");
return false;
}
globalCoolDown = sConfigMgr->GetIntDefault("AiPlayerbot.GlobalCooldown", 1500);
maxWaitForMove = sConfigMgr->GetIntDefault("AiPlayerbot.MaxWaitForMove", 5000);
expireActionTime = sConfigMgr->GetIntDefault("AiPlayerbot.ExpireActionTime", 5000);
dispelAuraDuration = sConfigMgr->GetIntDefault("AiPlayerbot.DispelAuraDuration", 7000);
reactDelay = sConfigMgr->GetIntDefault("AiPlayerbot.ReactDelay", 500);
passiveDelay = sConfigMgr->GetIntDefault("AiPlayerbot.PassiveDelay", 10000);
repeatDelay = sConfigMgr->GetIntDefault("AiPlayerbot.RepeatDelay", 5000);
errorDelay = sConfigMgr->GetIntDefault("AiPlayerbot.ErrorDelay", 5000);
rpgDelay = sConfigMgr->GetIntDefault("AiPlayerbot.RpgDelay", 10000);
sitDelay = sConfigMgr->GetIntDefault("AiPlayerbot.SitDelay", 30000);
returnDelay = sConfigMgr->GetIntDefault("AiPlayerbot.ReturnDelay", 7000);
lootDelay = sConfigMgr->GetIntDefault("AiPlayerbot.LootDelay", 1000);
farDistance = sConfigMgr->GetFloatDefault("AiPlayerbot.FarDistance", 20.0f);
sightDistance = sConfigMgr->GetFloatDefault("AiPlayerbot.SightDistance", 75.0f);
spellDistance = sConfigMgr->GetFloatDefault("AiPlayerbot.SpellDistance", 22.0f);
shootDistance = sConfigMgr->GetFloatDefault("AiPlayerbot.ShootDistance", 25.0f);
healDistance = sConfigMgr->GetFloatDefault("AiPlayerbot.HealDistance", 20.0f);
lootDistance = sConfigMgr->GetFloatDefault("AiPlayerbot.LootDistance", 15.0f);
fleeDistance = sConfigMgr->GetFloatDefault("AiPlayerbot.FleeDistance", 7.5f);
aggroDistance = sConfigMgr->GetFloatDefault("AiPlayerbot.AggroDistance", 22.0f);
tooCloseDistance = sConfigMgr->GetFloatDefault("AiPlayerbot.TooCloseDistance", 5.0f);
meleeDistance = sConfigMgr->GetFloatDefault("AiPlayerbot.MeleeDistance", 1.5f);
followDistance = sConfigMgr->GetFloatDefault("AiPlayerbot.FollowDistance", 1.5f);
whisperDistance = sConfigMgr->GetFloatDefault("AiPlayerbot.WhisperDistance", 6000.0f);
contactDistance = sConfigMgr->GetFloatDefault("AiPlayerbot.ContactDistance", 0.5f);
aoeRadius = sConfigMgr->GetFloatDefault("AiPlayerbot.AoeRadius", 5.0f);
rpgDistance = sConfigMgr->GetFloatDefault("AiPlayerbot.RpgDistance", 200.0f);
grindDistance = sConfigMgr->GetFloatDefault("AiPlayerbot.GrindDistance", 75.0f);
reactDistance = sConfigMgr->GetFloatDefault("AiPlayerbot.ReactDistance", 150.0f);
criticalHealth = sConfigMgr->GetIntDefault("AiPlayerbot.CriticalHealth", 20);
lowHealth = sConfigMgr->GetIntDefault("AiPlayerbot.LowHealth", 45);
mediumHealth = sConfigMgr->GetIntDefault("AiPlayerbot.MediumHealth", 65);
almostFullHealth = sConfigMgr->GetIntDefault("AiPlayerbot.AlmostFullHealth", 85);
lowMana = sConfigMgr->GetIntDefault("AiPlayerbot.LowMana", 15);
mediumMana = sConfigMgr->GetIntDefault("AiPlayerbot.MediumMana", 40);
randomGearLoweringChance = sConfigMgr->GetFloatDefault("AiPlayerbot.RandomGearLoweringChance", 0.15f);
randomBotMaxLevelChance = sConfigMgr->GetFloatDefault("AiPlayerbot.RandomBotMaxLevelChance", 0.15f);
randomBotRpgChance = sConfigMgr->GetFloatDefault("AiPlayerbot.RandomBotRpgChance", 0.20f);
iterationsPerTick = sConfigMgr->GetIntDefault("AiPlayerbot.IterationsPerTick", 100);
allowGuildBots = sConfigMgr->GetBoolDefault("AiPlayerbot.AllowGuildBots", true);
randomBotMapsAsString = sConfigMgr->GetStringDefault("AiPlayerbot.RandomBotMaps", "0,1,530,571");
LoadList<std::vector<uint32>>(randomBotMapsAsString, randomBotMaps);
LoadList<std::vector<uint32>>(sConfigMgr->GetStringDefault("AiPlayerbot.RandomBotQuestItems", "6948,5175,5176,5177,5178,16309,12382,13704,11000"), randomBotQuestItems);
LoadList<std::vector<uint32>>(sConfigMgr->GetStringDefault("AiPlayerbot.RandomBotSpellIds", "1"), randomBotSpellIds);
LoadList<std::vector<uint32>>(sConfigMgr->GetStringDefault("AiPlayerbot.PvpProhibitedZoneIds", "2255,656,2361,2362,2363,976,35,2268,3425,392,541,1446,3828,3712,3738,3565,3539,3623,4152,3988,4658,4284,4418,4436,4275,4323"), pvpProhibitedZoneIds);
LoadList<std::vector<uint32>>(sConfigMgr->GetStringDefault("AiPlayerbot.RandomBotQuestIds", "7848,3802,5505,6502,7761"), randomBotQuestIds);
botAutologin = sConfigMgr->GetBoolDefault("AiPlayerbot.BotAutologin", false);
randomBotAutologin = sConfigMgr->GetBoolDefault("AiPlayerbot.RandomBotAutologin", true);
minRandomBots = sConfigMgr->GetIntDefault("AiPlayerbot.MinRandomBots", 50);
maxRandomBots = sConfigMgr->GetIntDefault("AiPlayerbot.MaxRandomBots", 200);
randomBotUpdateInterval = sConfigMgr->GetIntDefault("AiPlayerbot.RandomBotUpdateInterval", MINUTE);
randomBotCountChangeMinInterval = sConfigMgr->GetIntDefault("AiPlayerbot.RandomBotCountChangeMinInterval", 30 * MINUTE);
randomBotCountChangeMaxInterval = sConfigMgr->GetIntDefault("AiPlayerbot.RandomBotCountChangeMaxInterval", 2 * HOUR);
minRandomBotInWorldTime = sConfigMgr->GetIntDefault("AiPlayerbot.MinRandomBotInWorldTime", 2 * HOUR);
maxRandomBotInWorldTime = sConfigMgr->GetIntDefault("AiPlayerbot.MaxRandomBotInWorldTime", 12 * HOUR);
minRandomBotRandomizeTime = sConfigMgr->GetIntDefault("AiPlayerbot.MinRandomBotRandomizeTime", 2 * HOUR);
maxRandomBotRandomizeTime = sConfigMgr->GetIntDefault("AiPlayerbot.MaxRandomRandomizeTime", 14 * 24 * HOUR);
minRandomBotChangeStrategyTime = sConfigMgr->GetIntDefault("AiPlayerbot.MinRandomBotChangeStrategyTime", 30 * MINUTE);
maxRandomBotChangeStrategyTime = sConfigMgr->GetIntDefault("AiPlayerbot.MaxRandomBotChangeStrategyTime", 2 * HOUR);
minRandomBotReviveTime = sConfigMgr->GetIntDefault("AiPlayerbot.MinRandomBotReviveTime", MINUTE);
maxRandomBotReviveTime = sConfigMgr->GetIntDefault("AiPlayerbot.MaxRandomBotReviveTime", 5 * MINUTE);
randomBotTeleportDistance = sConfigMgr->GetIntDefault("AiPlayerbot.RandomBotTeleportDistance", 100);
randomBotsPerInterval = sConfigMgr->GetIntDefault("AiPlayerbot.RandomBotsPerInterval", MINUTE);
minRandomBotsPriceChangeInterval = sConfigMgr->GetIntDefault("AiPlayerbot.MinRandomBotsPriceChangeInterval", 2 * HOUR);
maxRandomBotsPriceChangeInterval = sConfigMgr->GetIntDefault("AiPlayerbot.MaxRandomBotsPriceChangeInterval", 48 * HOUR);
randomBotJoinLfg = sConfigMgr->GetBoolDefault("AiPlayerbot.RandomBotJoinLfg", true);
randomBotJoinBG = sConfigMgr->GetBoolDefault("AiPlayerbot.RandomBotJoinBG", true);
logInGroupOnly = sConfigMgr->GetBoolDefault("AiPlayerbot.LogInGroupOnly", true);
logValuesPerTick = sConfigMgr->GetBoolDefault("AiPlayerbot.LogValuesPerTick", false);
fleeingEnabled = sConfigMgr->GetBoolDefault("AiPlayerbot.FleeingEnabled", true);
summonAtInnkeepersEnabled = sConfigMgr->GetBoolDefault("AiPlayerbot.SummonAtInnkeepersEnabled", true);
randomBotMinLevel = sConfigMgr->GetIntDefault("AiPlayerbot.RandomBotMinLevel", 1);
randomBotMaxLevel = sConfigMgr->GetIntDefault("AiPlayerbot.RandomBotMaxLevel", 80);
randomBotLoginAtStartup = sConfigMgr->GetBoolDefault("AiPlayerbot.RandomBotLoginAtStartup", true);
randomBotTeleLevel = sConfigMgr->GetIntDefault("AiPlayerbot.RandomBotTeleLevel", 5);
openGoSpell = sConfigMgr->GetIntDefault("AiPlayerbot.OpenGoSpell", 6477);
randomChangeMultiplier = sConfigMgr->GetFloatDefault("AiPlayerbot.RandomChangeMultiplier", 1.0);
randomBotCombatStrategies = sConfigMgr->GetStringDefault("AiPlayerbot.RandomBotCombatStrategies", "-threat");
randomBotNonCombatStrategies = sConfigMgr->GetStringDefault("AiPlayerbot.RandomBotNonCombatStrategies", "");
combatStrategies = sConfigMgr->GetStringDefault("AiPlayerbot.CombatStrategies", "+custom::say");
nonCombatStrategies = sConfigMgr->GetStringDefault("AiPlayerbot.NonCombatStrategies", "+custom::say,+return");
commandPrefix = sConfigMgr->GetStringDefault("AiPlayerbot.CommandPrefix", "");
commandSeparator = sConfigMgr->GetStringDefault("AiPlayerbot.CommandSeparator", "\\\\");
commandServerPort = sConfigMgr->GetIntDefault("AiPlayerbot.CommandServerPort", 8888);
perfMonEnabled = sConfigMgr->GetBoolDefault("AiPlayerbot.PerfMonEnabled", false);
LOG_INFO("server.loading", "---------------------------------------");
LOG_INFO("server.loading", " Loading TalentSpecs ");
LOG_INFO("server.loading", "---------------------------------------");
for (uint32 cls = 1; cls < MAX_CLASSES; ++cls)
{
classSpecs[cls] = ClassSpecs(1 << (cls - 1));
for (uint32 spec = 0; spec < MAX_LEVEL; ++spec)
{
std::ostringstream os;
os << "AiPlayerbot.PremadeSpecName." << cls << "." << spec;
std::string const specName = sConfigMgr->GetStringDefault(os.str().c_str(), "", false);
if (!specName.empty())
{
std::ostringstream os;
os << "AiPlayerbot.PremadeSpecProb." << cls << "." << spec;
uint32 probability = sConfigMgr->GetIntDefault(os.str().c_str(), 100, false);
TalentPath talentPath(spec, specName, probability);
for (uint32 level = 10; level <= 80; level++)
{
std::ostringstream os;
os << "AiPlayerbot.PremadeSpecLink." << cls << "." << spec << "." << level;
std::string specLink = sConfigMgr->GetStringDefault(os.str().c_str(), "", false);
specLink = specLink.substr(0, specLink.find("#", 0));;
specLink = specLink.substr(0, specLink.find(" ", 0));;
if (!specLink.empty())
{
std::ostringstream out;
// Ignore bad specs.
if (!classSpecs[cls].baseSpec.CheckTalentLink(specLink, &out))
{
LOG_ERROR("playerbots", "Error with premade spec link: {}", specLink.c_str());
LOG_ERROR("playerbots", "{}", out.str().c_str());
continue;
}
TalentSpec linkSpec(&classSpecs[cls].baseSpec, specLink);
if (!linkSpec.CheckTalents(level, &out))
{
LOG_ERROR("playerbots", "Error with premade spec: {}", specLink.c_str());
LOG_ERROR("playerbots", "{}", out.str().c_str());
continue;
}
talentPath.talentSpec.push_back(linkSpec);
}
}
// Only add paths that have atleast 1 spec.
if (talentPath.talentSpec.size() > 0)
classSpecs[cls].talentPath.push_back(talentPath);
}
}
}
botCheats.clear();
LoadListString<std::vector<std::string>>(sConfigMgr->GetStringDefault("AiPlayerbot.BotCheats", "taxi"), botCheats);
botCheatMask = 0;
if (std::find(botCheats.begin(), botCheats.end(), "taxi") != botCheats.end())
botCheatMask |= (uint32)BotCheatMask::taxi;
if (std::find(botCheats.begin(), botCheats.end(), "gold") != botCheats.end())
botCheatMask |= (uint32)BotCheatMask::gold;
if (std::find(botCheats.begin(), botCheats.end(), "health") != botCheats.end())
botCheatMask |= (uint32)BotCheatMask::health;
if (std::find(botCheats.begin(), botCheats.end(), "mana") != botCheats.end())
botCheatMask |= (uint32)BotCheatMask::mana;
if (std::find(botCheats.begin(), botCheats.end(), "power") != botCheats.end())
botCheatMask |= (uint32)BotCheatMask::power;
LoadListString<std::vector<std::string>>(sConfigMgr->GetStringDefault("AiPlayerbot.AllowedLogFiles", ""), allowedLogFiles);
worldBuffs.clear();
for (uint32 factionId = 0; factionId < 3; factionId++)
{
for (uint32 classId = 0; classId < MAX_CLASSES; classId++)
{
for (uint32 minLevel = 0; minLevel < MAX_LEVEL; minLevel++)
{
for (uint32 maxLevel = 0; maxLevel < MAX_LEVEL; maxLevel++)
{
loadWorldBuf(factionId, classId, minLevel, maxLevel);
}
}
}
}
randomBotAccountPrefix = sConfigMgr->GetStringDefault("AiPlayerbot.RandomBotAccountPrefix", "rndbot");
randomBotAccountCount = sConfigMgr->GetIntDefault("AiPlayerbot.RandomBotAccountCount", 200);
deleteRandomBotAccounts = sConfigMgr->GetBoolDefault("AiPlayerbot.DeleteRandomBotAccounts", false);
randomBotGuildCount = sConfigMgr->GetIntDefault("AiPlayerbot.RandomBotGuildCount", 20);
deleteRandomBotGuilds = sConfigMgr->GetBoolDefault("AiPlayerbot.DeleteRandomBotGuilds", false);
guildTaskEnabled = sConfigMgr->GetBoolDefault("AiPlayerbot.EnableGuildTasks", true);
minGuildTaskChangeTime = sConfigMgr->GetIntDefault("AiPlayerbot.MinGuildTaskChangeTime", 3 * 24 * 3600);
maxGuildTaskChangeTime = sConfigMgr->GetIntDefault("AiPlayerbot.MaxGuildTaskChangeTime", 4 * 24 * 3600);
minGuildTaskAdvertisementTime = sConfigMgr->GetIntDefault("AiPlayerbot.MinGuildTaskAdvertisementTime", 300);
maxGuildTaskAdvertisementTime = sConfigMgr->GetIntDefault("AiPlayerbot.MaxGuildTaskAdvertisementTime", 12 * 3600);
minGuildTaskRewardTime = sConfigMgr->GetIntDefault("AiPlayerbot.MinGuildTaskRewardTime", 300);
maxGuildTaskRewardTime = sConfigMgr->GetIntDefault("AiPlayerbot.MaxGuildTaskRewardTime", 3600);
guildTaskAdvertCleanupTime = sConfigMgr->GetIntDefault("AiPlayerbot.GuildTaskAdvertCleanupTime", 300);
guildTaskKillTaskDistance = sConfigMgr->GetIntDefault("AiPlayerbot.GuildTaskKillTaskDistance", 2000);
targetPosRecalcDistance = sConfigMgr->GetFloatDefault("AiPlayerbot.TargetPosRecalcDistance", 0.1f);
// cosmetics (by lidocain)
randomBotShowCloak = sConfigMgr->GetBoolDefault("AiPlayerbot.RandomBotShowCloak", true);
randomBotShowHelmet = sConfigMgr->GetBoolDefault("AiPlayerbot.RandomBotShowHelmet", true);
// SPP switches
enableGreet = sConfigMgr->GetBoolDefault("AiPlayerbot.EnableGreet", true);
disableRandomLevels = sConfigMgr->GetBoolDefault("AiPlayerbot.DisableRandomLevels", false);
randomBotRandomPassword = sConfigMgr->GetBoolDefault("AiPlayerbot.RandomBotRandomPassword", true);
playerbotsXPrate = sConfigMgr->GetIntDefault("AiPlayerbot.KillXPRate", 1);
botActiveAlone = sConfigMgr->GetIntDefault("AiPlayerbot.BotActiveAlone", 10);
randombotsWalkingRPG = sConfigMgr->GetBoolDefault("AiPlayerbot.RandombotsWalkingRPG", false);
randombotsWalkingRPGInDoors = sConfigMgr->GetBoolDefault("AiPlayerbot.RandombotsWalkingRPG.InDoors", false);
minEnchantingBotLevel = sConfigMgr->GetIntDefault("AiPlayerbot.MinEnchantingBotLevel", 60);
randombotStartingLevel = sConfigMgr->GetIntDefault("AiPlayerbot.RandombotStartingLevel", 5);
gearscorecheck = sConfigMgr->GetBoolDefault("AiPlayerbot.GearScoreCheck", false);
randomBotPreQuests = sConfigMgr->GetBoolDefault("AiPlayerbot.PreQuests", true);
// SPP automation
autoPickReward = sConfigMgr->GetStringDefault("AiPlayerbot.AutoPickReward", "yes");
autoEquipUpgradeLoot = sConfigMgr->GetBoolDefault("AiPlayerbot.AutoEquipUpgradeLoot", true);
syncQuestWithPlayer = sConfigMgr->GetBoolDefault("AiPlayerbot.SyncQuestWithPlayer", false);
syncQuestForPlayer = sConfigMgr->GetBoolDefault("AiPlayerbot.SyncQuestForPlayer", false);
autoTrainSpells = sConfigMgr->GetStringDefault("AiPlayerbot.AutoTrainSpells", "yes");
autoPickTalents = sConfigMgr->GetStringDefault("AiPlayerbot.AutoPickTalents", "full");
autoLearnTrainerSpells = sConfigMgr->GetBoolDefault("AiPlayerbot.AutoLearnTrainerSpells", false);
autoLearnQuestSpells = sConfigMgr->GetBoolDefault("AiPlayerbot.AutoLearnQuestSpells", false);
autoDoQuests = sConfigMgr->GetBoolDefault("AiPlayerbot.AutoDoQuests", false);
syncLevelWithPlayers = sConfigMgr->GetBoolDefault("AiPlayerbot.SyncLevelWithPlayers", false);
randomBotSayWithoutMaster = sConfigMgr->GetBoolDefault("AiPlayerbot.RandomBotSayWithoutMaster", false);
randomBotGroupNearby = sConfigMgr->GetBoolDefault("AiPlayerbot.RandomBotGroupNearby", true);
// arena
randomBotArenaTeamCount = sConfigMgr->GetIntDefault("AiPlayerbot.RandomBotArenaTeamCount", 20);
deleteRandomBotArenaTeams = sConfigMgr->GetBoolDefault("AiPlayerbot.DeleteRandomBotArenaTeams", false);
selfBotLevel = sConfigMgr->GetIntDefault("AiPlayerbot.SelfBotLevel", 1);
RandomPlayerbotFactory::CreateRandomBots();
PlayerbotFactory::Init();
sRandomItemMgr->Init();
sRandomItemMgr->InitAfterAhBot();
if (!sPlayerbotAIConfig->autoDoQuests)
{
LOG_INFO("server.loading", "Loading Quest Detail Data...");
sTravelMgr->LoadQuestTravelTable();
}
if (sPlayerbotAIConfig->randomBotJoinBG)
sRandomPlayerbotMgr->LoadBattleMastersCache();
LOG_INFO("server.loading", "---------------------------------------");
LOG_INFO("server.loading", " AI Playerbots initialized ");
LOG_INFO("server.loading", "---------------------------------------");
return true;
}
bool PlayerbotAIConfig::IsInRandomAccountList(uint32 id)
{
return find(randomBotAccounts.begin(), randomBotAccounts.end(), id) != randomBotAccounts.end();
}
bool PlayerbotAIConfig::IsInRandomQuestItemList(uint32 id)
{
return find(randomBotQuestItems.begin(), randomBotQuestItems.end(), id) != randomBotQuestItems.end();
}
bool PlayerbotAIConfig::IsInPvpProhibitedZone(uint32 id)
{
return find(pvpProhibitedZoneIds.begin(), pvpProhibitedZoneIds.end(), id) != pvpProhibitedZoneIds.end();
}
std::string const PlayerbotAIConfig::GetTimestampStr()
{
time_t t = time(nullptr);
tm* aTm = localtime(&t);
// YYYY year
// MM month (2 digits 01-12)
// DD day (2 digits 01-31)
// HH hour (2 digits 00-23)
// MM minutes (2 digits 00-59)
// SS seconds (2 digits 00-59)
char buf[20];
snprintf(buf, 20, "%04d-%02d-%02d %02d-%02d-%02d", aTm->tm_year + 1900, aTm->tm_mon + 1, aTm->tm_mday, aTm->tm_hour, aTm->tm_min, aTm->tm_sec);
return std::string(buf);
}
bool PlayerbotAIConfig::openLog(std::string const fileName, char const* mode)
{
if (!hasLog(fileName))
return false;
auto logFileIt = logFiles.find(fileName);
if (logFileIt == logFiles.end())
{
logFiles.insert(std::make_pair(fileName, std::make_pair(nullptr, false)));
logFileIt = logFiles.find(fileName);
}
FILE* file = logFileIt->second.first;
bool fileOpen = logFileIt->second.second;
if (fileOpen) //close log file
fclose(file);
std::string m_logsDir = sConfigMgr->GetOption<std::string>("LogsDir", "", false);
if (!m_logsDir.empty())
{
if ((m_logsDir.at(m_logsDir.length() - 1) != '/') && (m_logsDir.at(m_logsDir.length() - 1) != '\\'))
m_logsDir.append("/");
}
file = fopen((m_logsDir + fileName).c_str(), mode);
fileOpen = true;
logFileIt->second.first = file;
logFileIt->second.second = fileOpen;
return true;
}
void PlayerbotAIConfig::log(std::string const fileName, char const* str, ...)
{
if (!str)
return;
std::lock_guard<std::mutex> guard(m_logMtx);
if (!isLogOpen(fileName) && !openLog(fileName, "a"))
return;
FILE* file = logFiles.find(fileName)->second.first;
va_list ap;
va_start(ap, str);
vfprintf(file, str, ap);
fprintf(file, "\n");
va_end(ap);
fflush(file);
fflush(stdout);
}
void PlayerbotAIConfig::loadWorldBuf(uint32 factionId1, uint32 classId1, uint32 minLevel1, uint32 maxLevel1)
{
std::vector<uint32> buffs;
std::ostringstream os;
os << "AiPlayerbot.WorldBuff." << factionId1 << "." << classId1 << "." << minLevel1 << "." << maxLevel1;
LoadList<std::vector<uint32>>(sConfigMgr->GetStringDefault(os.str().c_str(), "", false), buffs);
for (auto buff : buffs)
{
worldBuff wb = { buff, factionId1, classId1, minLevel1, maxLevel1 };
worldBuffs.push_back(wb);
}
if (maxLevel1 == 0)
{
std::ostringstream os;
os << "AiPlayerbot.WorldBuff." << factionId1 << "." << classId1 << "." << minLevel1;
LoadList<std::vector<uint32>>(sConfigMgr->GetStringDefault(os.str().c_str(), "", false), buffs);
for (auto buff : buffs)
{
worldBuff wb = { buff, factionId1, classId1, minLevel1, maxLevel1 };
worldBuffs.push_back(wb);
}
}
if (maxLevel1 == 0 && minLevel1 == 0)
{
std::ostringstream os;
os << "AiPlayerbot.WorldBuff." << factionId1 << "." << factionId1 << "." << classId1;
LoadList<std::vector<uint32>>(sConfigMgr->GetStringDefault(os.str().c_str(), "", false), buffs);
for (auto buff : buffs)
{
worldBuff wb = { buff, factionId1, classId1, minLevel1, maxLevel1 };
worldBuffs.push_back(wb);
}
}
if (classId1 == 0 && maxLevel1 == 0 && minLevel1 == 0)
{
std::ostringstream os;
os << "AiPlayerbot.WorldBuff." << factionId1;
LoadList<std::vector<uint32>>(sConfigMgr->GetStringDefault(os.str().c_str(), "", false), buffs);
for (auto buff : buffs)
{
worldBuff wb = { buff, factionId1, classId1, minLevel1, maxLevel1 };
worldBuffs.push_back(wb);
}
}
if (factionId1 == 0 && classId1 == 0 && maxLevel1 == 0 && minLevel1 == 0)
{
std::ostringstream os;
os << "AiPlayerbot.WorldBuff";
LoadList<std::vector<uint32>>(sConfigMgr->GetStringDefault(os.str().c_str(), "", false), buffs);
for (auto buff : buffs)
{
worldBuff wb = { buff, factionId1, classId1, minLevel1, maxLevel1 };
worldBuffs.push_back(wb);
}
}
}

172
src/PlayerbotAIConfig.h Normal file
View File

@@ -0,0 +1,172 @@
/*
* 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.
*/
#ifndef _PLAYERBOT_PLAYERbotAICONFIG_H
#define _PLAYERBOT_PLAYERbotAICONFIG_H
#include "Common.h"
#include "SharedDefines.h"
#include "TalentSpec.h"
#include <mutex>
enum class BotCheatMask : uint32
{
none = 0,
taxi = 1,
gold = 2,
health = 4,
mana = 8,
power = 16,
maxMask = 32
};
class PlayerbotAIConfig
{
public:
PlayerbotAIConfig() { };
static PlayerbotAIConfig* instance()
{
static PlayerbotAIConfig instance;
return &instance;
}
bool Initialize();
bool IsInRandomAccountList(uint32 id);
bool IsInRandomQuestItemList(uint32 id);
bool IsInPvpProhibitedZone(uint32 id);
bool enabled;
bool allowGuildBots;
uint32 globalCoolDown, reactDelay, maxWaitForMove, expireActionTime, dispelAuraDuration, passiveDelay, repeatDelay,
errorDelay, rpgDelay, sitDelay, returnDelay, lootDelay;
float sightDistance, spellDistance, reactDistance, grindDistance, lootDistance, shootDistance,
fleeDistance, tooCloseDistance, meleeDistance, followDistance, whisperDistance, contactDistance,
aoeRadius, rpgDistance, targetPosRecalcDistance, farDistance, healDistance, aggroDistance;
uint32 criticalHealth, lowHealth, mediumHealth, almostFullHealth;
uint32 lowMana, mediumMana;
uint32 openGoSpell;
bool randomBotAutologin;
bool botAutologin;
std::string randomBotMapsAsString;
std::vector<uint32> randomBotMaps;
std::vector<uint32> randomBotQuestItems;
std::vector<uint32> randomBotAccounts;
std::vector<uint32> randomBotSpellIds;
std::vector<uint32> randomBotQuestIds;
uint32 randomBotTeleportDistance;
float randomGearLoweringChance;
float randomBotMaxLevelChance;
float randomBotRpgChance;
uint32 minRandomBots, maxRandomBots;
uint32 randomBotUpdateInterval, randomBotCountChangeMinInterval, randomBotCountChangeMaxInterval;
uint32 minRandomBotInWorldTime, maxRandomBotInWorldTime;
uint32 minRandomBotRandomizeTime, maxRandomBotRandomizeTime;
uint32 minRandomBotChangeStrategyTime, maxRandomBotChangeStrategyTime;
uint32 minRandomBotReviveTime, maxRandomBotReviveTime;
uint32 minRandomBotPvpTime, maxRandomBotPvpTime;
uint32 randomBotsPerInterval;
uint32 minRandomBotsPriceChangeInterval, maxRandomBotsPriceChangeInterval;
bool randomBotJoinLfg;
bool randomBotJoinBG;
bool randomBotLoginAtStartup;
uint32 randomBotTeleLevel;
bool logInGroupOnly, logValuesPerTick;
bool fleeingEnabled;
bool summonAtInnkeepersEnabled;
std::string combatStrategies, nonCombatStrategies;
std::string randomBotCombatStrategies, randomBotNonCombatStrategies;
uint32 randomBotMinLevel, randomBotMaxLevel;
float randomChangeMultiplier;
uint32 specProbability[MAX_CLASSES][10];
std::string premadeLevelSpec[MAX_CLASSES][10][91]; //lvl 10 - 100
ClassSpecs classSpecs[MAX_CLASSES];
std::string commandPrefix, commandSeparator;
std::string randomBotAccountPrefix;
uint32 randomBotAccountCount;
bool randomBotRandomPassword;
bool deleteRandomBotAccounts;
uint32 randomBotGuildCount;
bool deleteRandomBotGuilds;
std::vector<uint32> randomBotGuilds;
std::vector<uint32> pvpProhibitedZoneIds;
bool randombotsWalkingRPG;
bool randombotsWalkingRPGInDoors;
uint32 minEnchantingBotLevel;
uint32 randombotStartingLevel;
bool gearscorecheck;
bool randomBotPreQuests;
bool guildTaskEnabled;
uint32 minGuildTaskChangeTime, maxGuildTaskChangeTime;
uint32 minGuildTaskAdvertisementTime, maxGuildTaskAdvertisementTime;
uint32 minGuildTaskRewardTime, maxGuildTaskRewardTime;
uint32 guildTaskAdvertCleanupTime;
uint32 guildTaskKillTaskDistance;
uint32 iterationsPerTick;
std::mutex m_logMtx;
std::vector<std::string> allowedLogFiles;
std::unordered_map<std::string, std::pair<FILE*, bool>> logFiles;
std::vector<std::string> botCheats;
uint32 botCheatMask = 0;
struct worldBuff
{
uint32 spellId;
uint32 factionId = 0;
uint32 classId = 0;
uint32 minLevel = 0;
uint32 maxLevel = 0;
};
std::vector<worldBuff> worldBuffs;
uint32 commandServerPort;
bool perfMonEnabled;
bool enableGreet;
bool randomBotShowHelmet;
bool randomBotShowCloak;
bool disableRandomLevels;
uint32 playerbotsXPrate;
uint32 botActiveAlone;
std::string autoPickReward;
bool autoEquipUpgradeLoot;
bool syncQuestWithPlayer;
bool syncQuestForPlayer;
std::string autoTrainSpells;
std::string autoPickTalents;
bool autoLearnTrainerSpells;
bool autoDoQuests;
bool syncLevelWithPlayers;
bool autoLearnQuestSpells;
bool randomBotSayWithoutMaster;
bool randomBotGroupNearby;
uint32 tweakValue; //Debugging config
uint32 randomBotArenaTeamCount;
bool deleteRandomBotArenaTeams;
std::vector<uint32> randomBotArenaTeams;
uint32 selfBotLevel;
std::string const GetTimestampStr();
bool hasLog(std::string const fileName) { return std::find(allowedLogFiles.begin(), allowedLogFiles.end(), fileName) != allowedLogFiles.end(); };
bool openLog(std::string const fileName, char const* mode = "a");
bool isLogOpen(std::string const fileName) { auto it = logFiles.find(fileName); return it != logFiles.end() && it->second.second; }
void log(std::string const fileName, const char* str, ...);
void loadWorldBuf(uint32 factionId, uint32 classId, uint32 minLevel, uint32 maxLevel);
};
#define sPlayerbotAIConfig PlayerbotAIConfig::instance()
#endif

View File

@@ -0,0 +1,99 @@
/*
* 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 "PlayerbotCommandServer.h"
#include "IoContext.h"
#include "Playerbots.h"
#include <cstdlib>
#include <iostream>
#include <boost/bind.hpp>
#include <boost/smart_ptr.hpp>
#include <boost/asio.hpp>
#include <boost/thread/thread.hpp>
using boost::asio::ip::tcp;
typedef boost::shared_ptr<tcp::socket> socket_ptr;
bool ReadLine(socket_ptr sock, std::string* buffer, std::string* line)
{
// Do the real reading from fd until buffer has '\n'.
std::string::iterator pos;
while ((pos = find(buffer->begin(), buffer->end(), '\n')) == buffer->end())
{
char buf[1025];
boost::system::error_code error;
size_t n = sock->read_some(boost::asio::buffer(buf), error);
if (n == -1 || error == boost::asio::error::eof)
return false;
else if (error)
throw boost::system::system_error(error); // Some other error.
buf[n] = 0;
*buffer += buf;
}
*line = std::string(buffer->begin(), pos);
*buffer = std::string(pos + 1, buffer->end());
return true;
}
void session(socket_ptr sock)
{
try
{
std::string buffer, request;
while (ReadLine(sock, &buffer, &request))
{
std::string const response = sRandomPlayerbotMgr->HandleRemoteCommand(request) + "\n";
boost::asio::write(*sock, boost::asio::buffer(response.c_str(), response.size()));
request = "";
}
}
catch (std::exception& e)
{
LOG_ERROR("playerbots", "{}", e.what());
}
}
void server(Acore::Asio::IoContext& io_service, short port)
{
tcp::acceptor a(io_service, tcp::endpoint(tcp::v4(), port));
for (;;)
{
socket_ptr sock(new tcp::socket(io_service));
a.accept(*sock);
boost::thread t(boost::bind(session, sock));
}
}
void Run()
{
if (!sPlayerbotAIConfig->commandServerPort)
{
return;
}
std::ostringstream s;
s << "Starting Playerbots Command Server on port " << sPlayerbotAIConfig->commandServerPort;
LOG_INFO("playerbots", "{}", s.str().c_str());
try
{
Acore::Asio::IoContext io_service;
server(io_service, sPlayerbotAIConfig->commandServerPort);
}
catch (std::exception& e)
{
LOG_ERROR("playerbots", "{}", e.what());
}
}
void PlayerbotCommandServer::Start()
{
std::thread serverThread(Run);
serverThread.detach();
}

View File

@@ -0,0 +1,24 @@
/*
* 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.
*/
#ifndef _PLAYERBOT_PLAYERBOTCOMMANDSERVER_H
#define _PLAYERBOT_PLAYERBOTCOMMANDSERVER_H
class PlayerbotCommandServer
{
public:
PlayerbotCommandServer() { }
virtual ~PlayerbotCommandServer() { }
static PlayerbotCommandServer* instance()
{
static PlayerbotCommandServer instance;
return &instance;
}
void Start();
};
#define sPlayerbotCommandServer PlayerbotCommandServer::instance()
#endif

88
src/PlayerbotDbStore.cpp Normal file
View File

@@ -0,0 +1,88 @@
/*
* 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 "PlayerbotDbStore.h"
#include "Playerbots.h"
#include <iostream>
void PlayerbotDbStore::Load(PlayerbotAI* botAI)
{
ObjectGuid::LowType guid = botAI->GetBot()->GetGUID().GetCounter();
PlayerbotsDatabasePreparedStatement* stmt = PlayerbotsDatabase.GetPreparedStatement(PLAYERBOTS_SEL_DB_STORE);
stmt->SetData(0, guid);
if (PreparedQueryResult result = PlayerbotsDatabase.Query(stmt))
{
botAI->ClearStrategies(BOT_STATE_COMBAT);
botAI->ClearStrategies(BOT_STATE_NON_COMBAT);
botAI->ChangeStrategy("+chat", BOT_STATE_COMBAT);
botAI->ChangeStrategy("+chat", BOT_STATE_NON_COMBAT);
std::vector<std::string> values;
do
{
Field* fields = result->Fetch();
std::string const key = fields[0].Get<std::string>();
std::string const value = fields[1].Get<std::string>();
if (key == "value")
values.push_back(value);
else if (key == "co")
botAI->ChangeStrategy(value, BOT_STATE_COMBAT);
else if (key == "nc")
botAI->ChangeStrategy(value, BOT_STATE_NON_COMBAT);
else if (key == "dead")
botAI->ChangeStrategy(value, BOT_STATE_DEAD);
}
while (result->NextRow());
botAI->GetAiObjectContext()->Load(values);
}
}
void PlayerbotDbStore::Save(PlayerbotAI* botAI)
{
ObjectGuid::LowType guid = botAI->GetBot()->GetGUID().GetCounter();
Reset(botAI);
std::vector<std::string> data = botAI->GetAiObjectContext()->Save();
for (std::vector<std::string>::iterator i = data.begin(); i != data.end(); ++i)
{
SaveValue(guid, "value", *i);
}
SaveValue(guid, "co", FormatStrategies("co", botAI->GetStrategies(BOT_STATE_COMBAT)));
SaveValue(guid, "nc", FormatStrategies("nc", botAI->GetStrategies(BOT_STATE_NON_COMBAT)));
SaveValue(guid, "dead", FormatStrategies("dead", botAI->GetStrategies(BOT_STATE_DEAD)));
}
std::string const PlayerbotDbStore::FormatStrategies(std::string const type, std::vector<std::string> strategies)
{
std::ostringstream out;
for (std::vector<std::string>::iterator i = strategies.begin(); i != strategies.end(); ++i)
out << "+" << (*i).c_str() << ",";
std::string const res = out.str();
return res.substr(0, res.size() - 1);
}
void PlayerbotDbStore::Reset(PlayerbotAI* botAI)
{
ObjectGuid::LowType guid = botAI->GetBot()->GetGUID().GetCounter();
PlayerbotsDatabasePreparedStatement* stmt = PlayerbotsDatabase.GetPreparedStatement(PLAYERBOTS_DEL_CUSTOM_STRATEGY);
stmt->SetData(0, guid);
PlayerbotsDatabase.Execute(stmt);
}
void PlayerbotDbStore::SaveValue(uint32 guid, std::string const key, std::string const value)
{
PlayerbotsDatabasePreparedStatement* stmt = PlayerbotsDatabase.GetPreparedStatement(PLAYERBOTS_INS_DB_STORE);
stmt->SetData(0, guid);
stmt->SetData(1, key);
stmt->SetData(2, value);
PlayerbotsDatabase.Execute(stmt);
}

36
src/PlayerbotDbStore.h Normal file
View File

@@ -0,0 +1,36 @@
/*
* 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.
*/
#ifndef _PLAYERBOT_PLAYERBOTDBSTORE_H
#define _PLAYERBOT_PLAYERBOTDBSTORE_H
#include "Common.h"
#include <vector>
class PlayerbotAI;
class PlayerbotDbStore
{
public:
PlayerbotDbStore() { }
virtual ~PlayerbotDbStore() { }
static PlayerbotDbStore* instance()
{
static PlayerbotDbStore instance;
return &instance;
}
void Save(PlayerbotAI* botAI);
void Load(PlayerbotAI* botAI);
void Reset(PlayerbotAI* botAI);
private:
void SaveValue(uint32 guid, std::string const key, std::string const value);
std::string const FormatStrategies(std::string const type, std::vector<std::string> strategies);
};
#define sPlayerbotDbStore PlayerbotDbStore::instance()
#endif

2667
src/PlayerbotFactory.cpp Normal file

File diff suppressed because it is too large Load Diff

176
src/PlayerbotFactory.h Normal file
View File

@@ -0,0 +1,176 @@
/*
* 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.
*/
#ifndef _PLAYERBOT_PLAYERBOTFACTORY_H
#define _PLAYERBOT_PLAYERBOTFACTORY_H
#include "InventoryAction.h"
class Item;
class Player;
struct ItemTemplate;
struct EnchantTemplate
{
uint8 ClassId;
uint8 SpecId;
uint32 SpellId;
uint8 SlotId;
};
typedef std::vector<EnchantTemplate> EnchantContainer;
//TODO: more spec/role
/* classid+talenttree
enum spec : uint8
{
WARRIOR ARMS = 10,
WARRIOR FURY = 11,
WARRIOR PROT = 12,
ROLE_HEALER = 1,
ROLE_MDPS = 2,
ROLE_CDPS = 3,
};
*/
/*enum roles : uint8
{
ROLE_TANK = 0,
ROLE_HEALER = 1,
ROLE_MDPS = 2,
ROLE_CDPS = 3
};*/
enum PriorizedConsumables
{
CONSUM_ID_ROUGH_WEIGHTSTONE = 3239,
CONSUM_ID_COARSE_WEIGHTSTONE = 3239,
CONSUM_ID_HEAVY_WEIGHTSTONE = 3241,
CONSUM_ID_SOLID_WEIGHTSTONE = 7965,
CONSUM_ID_DENSE_WEIGHTSTONE = 12643,
CONSUM_ID_FEL_WEIGHTSTONE = 28420,
CONSUM_ID_ADAMANTITE_WEIGHTSTONE = 28421,
CONSUM_ID_ROUGH_SHARPENING_STONE = 2862,
CONSUM_ID_COARSE_SHARPENING_STONE = 2863,
CONSUM_ID_HEAVY_SHARPENING_STONE = 2871,
CONSUM_ID_SOL_SHARPENING_STONE = 7964,
CONSUM_ID_DENSE_SHARPENING_STONE = 12404,
CONSUM_ID_ELEMENTAL_SHARPENING_STONE = 18262,
CONSUM_ID_CONSECRATED_SHARPENING_STONE = 23122,
CONSUM_ID_FEL_SHARPENING_STONE = 23528,
CONSUM_ID_ADAMANTITE_SHARPENING_STONE = 23529,
CONSUM_ID_LINEN_BANDAGE = 1251,
CONSUM_ID_HEAVY_LINEN_BANDAGE = 2581,
CONSUM_ID_WOOL_BANDAGE = 3530,
CONSUM_ID_HEAVY_WOOL_BANDAGE = 3531,
CONSUM_ID_SILK_BANDAGE = 6450,
CONSUM_ID_HEAVY_SILK_BANDAGE = 6451,
CONSUM_ID_MAGEWEAVE_BANDAGE = 8544,
CONSUM_ID_HEAVY_MAGEWEAVE_BANDAGE = 8545,
CONSUM_ID_RUNECLOTH_BANDAGE = 14529,
CONSUM_ID_HEAVY_RUNECLOTH_BANDAGE = 14530,
CONSUM_ID_NETHERWEAVE_BANDAGE = 21990,
CONSUM_ID_HEAVY_NETHERWEAVE_BANDAGE = 21991,
CONSUM_ID_BRILLIANT_MANA_OIL = 20748,
CONSUM_ID_MINOR_MANA_OIL = 20745,
CONSUM_ID_SUPERIOR_MANA_OIL = 22521,
CONSUM_ID_LESSER_MANA_OIL = 20747,
CONSUM_ID_BRILLIANT_WIZARD_OIL = 20749,
CONSUM_ID_MINOR_WIZARD_OIL = 20744,
CONSUM_ID_SUPERIOR_WIZARD_OIL = 22522,
CONSUM_ID_WIZARD_OIL = 20750,
CONSUM_ID_LESSER_WIZARD_OIL = 20746,
CONSUM_ID_INSTANT_POISON = 6947,
CONSUM_ID_INSTANT_POISON_II = 6949,
CONSUM_ID_INSTANT_POISON_III = 6950,
CONSUM_ID_INSTANT_POISON_IV = 8926,
CONSUM_ID_INSTANT_POISON_V = 8927,
CONSUM_ID_INSTANT_POISON_VI = 8928,
CONSUM_ID_INSTANT_POISON_VII = 21927,
CONSUM_ID_DEADLY_POISON = 2892,
CONSUM_ID_DEADLY_POISON_II = 2893,
CONSUM_ID_DEADLY_POISON_III = 8984,
CONSUM_ID_DEADLY_POISON_IV = 8985,
CONSUM_ID_DEADLY_POISON_V = 20844,
CONSUM_ID_DEADLY_POISON_VI = 22053,
CONSUM_ID_DEADLY_POISON_VII = 22054
};
#define MAX_CONSUM_ID 28
class PlayerbotFactory : public InventoryAction
{
public:
PlayerbotFactory(Player* bot, uint32 level, uint32 itemQuality = 0);
static ObjectGuid GetRandomBot();
static void Init();
void Refresh();
void Randomize(bool incremental);
static std::list<uint32> classQuestIds;
void InitSkills();
static uint32 tradeSkills[];
private:
void Prepare();
void InitSecondEquipmentSet();
void InitEquipment(bool incremental);
void InitEquipmentNew(bool incremental);
bool CanEquipItem(ItemTemplate const* proto, uint32 desiredQuality);
bool CanEquipUnseenItem(uint8 slot, uint16& dest, uint32 item);
void InitTradeSkills();
void UpdateTradeSkills();
void SetRandomSkill(uint16 id);
void InitSpells();
void ClearSpells();
void ClearSkills();
void InitAvailableSpells();
void InitSpecialSpells();
void InitTalentsTree(bool incremental);
void InitTalents(uint32 specNo);
void InitQuests(std::list<uint32>& questMap);
void InitPet();
void ClearInventory();
void ClearAllItems();
void ResetQuests();
void InitAmmo();
void InitMounts();
void InitPotions();
void InitFood();
void InitReagents();
bool CanEquipArmor(ItemTemplate const* proto);
bool CanEquipWeapon(ItemTemplate const* proto);
void EnchantItem(Item* item);
void AddItemStats(uint32 mod, uint8& sp, uint8& ap, uint8& tank);
bool CheckItemStats(uint8 sp, uint8 ap, uint8 tank);
void CancelAuras();
bool IsDesiredReplacement(Item* item);
void InitBags();
void InitInventory();
void InitInventoryTrade();
void InitInventoryEquip();
void InitInventorySkill();
Item* StoreItem(uint32 itemId, uint32 count);
void InitGuild();
void InitArenaTeam();
void InitImmersive();
void AddConsumables();
static void AddPrevQuests(uint32 questId, std::list<uint32>& questIds);
void LoadEnchantContainer();
void ApplyEnchantTemplate();
void ApplyEnchantTemplate(uint8 spec);
EnchantContainer::const_iterator GetEnchantContainerBegin() { return m_EnchantContainer.begin(); }
EnchantContainer::const_iterator GetEnchantContainerEnd() { return m_EnchantContainer.end(); }
uint32 level;
uint32 itemQuality;
static std::list<uint32> specialQuestIds;
protected:
EnchantContainer m_EnchantContainer;
};
#endif

1070
src/PlayerbotMgr.cpp Normal file

File diff suppressed because it is too large Load Diff

111
src/PlayerbotMgr.h Normal file
View File

@@ -0,0 +1,111 @@
/*
* 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.
*/
#ifndef _PLAYERBOT_PLAYERBOTMGR_H
#define _PLAYERBOT_PLAYERBOTMGR_H
#include "Common.h"
#include "QueryHolder.h"
#include "QueryResult.h"
#include "Player.h"
#include "PlayerbotAIBase.h"
class ChatHandler;
class PlayerbotAI;
class PlayerbotLoginQueryHolder;
class WorldPacket;
typedef std::map<ObjectGuid, Player*> PlayerBotMap;
typedef std::map<std::string, std::set<std::string> > PlayerBotErrorMap;
class PlayerbotHolder : public PlayerbotAIBase
{
public:
PlayerbotHolder();
virtual ~PlayerbotHolder() { };
void AddPlayerBot(ObjectGuid guid, uint32 masterAccountId);
void HandlePlayerBotLoginCallback(PlayerbotLoginQueryHolder const& holder);
void LogoutPlayerBot(ObjectGuid guid);
void DisablePlayerBot(ObjectGuid guid);
Player* GetPlayerBot(ObjectGuid guid) const;
Player* GetPlayerBot(ObjectGuid::LowType lowGuid) const;
PlayerBotMap::const_iterator GetPlayerBotsBegin() const { return playerBots.begin(); }
PlayerBotMap::const_iterator GetPlayerBotsEnd() const { return playerBots.end(); }
void UpdateAIInternal(uint32 elapsed, bool minimal = false) override { };
void UpdateSessions();
void HandleBotPackets(WorldSession* session);
void LogoutAllBots();
void OnBotLogin(Player* const bot);
std::vector<std::string> HandlePlayerbotCommand(char const* args, Player* master = nullptr);
std::string const ProcessBotCommand(std::string const cmd, ObjectGuid guid, ObjectGuid masterguid, bool admin, uint32 masterAccountId, uint32 masterGuildId);
uint32 GetAccountId(std::string const name);
uint32 GetAccountId(ObjectGuid guid);
std::string const ListBots(Player* master);
protected:
virtual void OnBotLoginInternal(Player* const bot) = 0;
PlayerBotMap playerBots;
};
class PlayerbotMgr : public PlayerbotHolder
{
public:
PlayerbotMgr(Player* const master);
virtual ~PlayerbotMgr();
static bool HandlePlayerbotMgrCommand(ChatHandler* handler, char const* args);
void HandleMasterIncomingPacket(WorldPacket const& packet);
void HandleMasterOutgoingPacket(WorldPacket const& packet);
void HandleCommand(uint32 type, std::string const text);
void OnPlayerLogin(Player* player);
void CancelLogout();
void UpdateAIInternal(uint32 elapsed, bool minimal = false) override;
void TellError(std::string const botName, std::string const text);
Player* GetMaster() const { return master; };
void SaveToDB();
protected:
void OnBotLoginInternal(Player* const bot) override;
void CheckTellErrors(uint32 elapsed);
private:
Player* const master;
PlayerBotErrorMap errors;
time_t lastErrorTell;
};
class PlayerbotsMgr
{
public:
PlayerbotsMgr() { }
~PlayerbotsMgr() { }
static PlayerbotsMgr* instance()
{
static PlayerbotsMgr instance;
return &instance;
}
void AddPlayerbotData(Player* player, bool isBotAI);
void RemovePlayerBotData(ObjectGuid const& guid);
PlayerbotAI* GetPlayerbotAI(Player* player);
PlayerbotMgr* GetPlayerbotMgr(Player* player);
private:
std::unordered_map<ObjectGuid, PlayerbotAIBase*> _playerbotsMap;
};
#define sPlayerbotsMgr PlayerbotsMgr::instance()
#endif

265
src/PlayerbotSecurity.cpp Normal file
View File

@@ -0,0 +1,265 @@
/*
* 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 "PlayerbotSecurity.h"
#include "LFGMgr.h"
#include "Playerbots.h"
PlayerbotSecurity::PlayerbotSecurity(Player* const bot) : bot(bot)
{
if (bot)
account = sCharacterCache->GetCharacterAccountIdByGuid(bot->GetGUID());
}
PlayerbotSecurityLevel PlayerbotSecurity::LevelFor(Player* from, DenyReason* reason, bool ignoreGroup)
{
if (from->GetSession()->GetSecurity() >= SEC_GAMEMASTER)
return PLAYERBOT_SECURITY_ALLOW_ALL;
PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot);
if (botAI->IsOpposing(from))
{
if (reason)
*reason = PLAYERBOT_DENY_OPPOSING;
return PLAYERBOT_SECURITY_DENY_ALL;
}
if (sPlayerbotAIConfig->IsInRandomAccountList(account))
{
if (botAI->IsOpposing(from))
{
if (reason)
*reason = PLAYERBOT_DENY_OPPOSING;
return PLAYERBOT_SECURITY_DENY_ALL;
}
if (sLFGMgr->GetState(bot->GetGUID()) != lfg::LFG_STATE_NONE)
{
if (!bot->GetGuildId() || bot->GetGuildId() != from->GetGuildId())
{
if (reason)
*reason = PLAYERBOT_DENY_LFG;
return PLAYERBOT_SECURITY_TALK;
}
}
Group* group = from->GetGroup();
if (group)
{
for (GroupReference* gref = group->GetFirstMember(); gref; gref = gref->next())
{
Player* player = gref->GetSource();
if (player == bot && !ignoreGroup)
return PLAYERBOT_SECURITY_ALLOW_ALL;
}
}
if ((int32)bot->getLevel() - (int8)from->getLevel() > 30)
{
if (!bot->GetGuildId() || bot->GetGuildId() != from->GetGuildId())
{
if (reason)
*reason = PLAYERBOT_DENY_LOW_LEVEL;
return PLAYERBOT_SECURITY_TALK;
}
}
int32 botGS = (int32)botAI->GetEquipGearScore(bot, false, false);
int32 fromGS = (int32)botAI->GetEquipGearScore(from, false, false);
if (sPlayerbotAIConfig->gearscorecheck)
{
if (botGS && bot->getLevel() > 15 && botGS > fromGS &&
static_cast<float>(100 * (botGS - fromGS) / botGS) >= static_cast<float>(12 * sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL) / from->getLevel()))
{
if (reason) *reason = PLAYERBOT_DENY_GEARSCORE;
return PLAYERBOT_SECURITY_TALK;
}
}
if (bot->InBattlegroundQueue())
{
if (!bot->GetGuildId() || bot->GetGuildId() != from->GetGuildId())
{
if (reason)
*reason = PLAYERBOT_DENY_BG;
return PLAYERBOT_SECURITY_TALK;
}
}
/*if (bot->isDead())
{
if (reason)
*reason = PLAYERBOT_DENY_DEAD;
return PLAYERBOT_SECURITY_TALK;
}*/
group = bot->GetGroup();
if (!group)
{
/*if (bot->GetMapId() != from->GetMapId() || bot->GetDistance(from) > sPlayerbotAIConfig->whisperDistance)
{
if (!bot->GetGuildId() || bot->GetGuildId() != from->GetGuildId())
{
if (reason)
*reason = PLAYERBOT_DENY_FAR;
return PLAYERBOT_SECURITY_TALK;
}
}*/
if (reason)
*reason = PLAYERBOT_DENY_INVITE;
return PLAYERBOT_SECURITY_INVITE;
}
for (GroupReference* gref = group->GetFirstMember(); gref; gref = gref->next())
{
Player* player = gref->GetSource();
if (player == from)
return PLAYERBOT_SECURITY_ALLOW_ALL;
}
if (group->IsFull())
{
if (reason)
*reason = PLAYERBOT_DENY_FULL_GROUP;
return PLAYERBOT_SECURITY_TALK;
}
if (group->GetLeaderGUID() != bot->GetGUID())
{
if (reason)
*reason = PLAYERBOT_DENY_NOT_LEADER;
return PLAYERBOT_SECURITY_TALK;
}
else
{
if (reason)
*reason = PLAYERBOT_DENY_IS_LEADER;
return PLAYERBOT_SECURITY_INVITE;
}
if (reason)
*reason = PLAYERBOT_DENY_INVITE;
return PLAYERBOT_SECURITY_INVITE;
}
return PLAYERBOT_SECURITY_ALLOW_ALL;
}
bool PlayerbotSecurity::CheckLevelFor(PlayerbotSecurityLevel level, bool silent, Player* from, bool ignoreGroup)
{
DenyReason reason = PLAYERBOT_DENY_NONE;
PlayerbotSecurityLevel realLevel = LevelFor(from, &reason, ignoreGroup);
if (realLevel >= level)
return true;
PlayerbotAI* fromBotAI = GET_PLAYERBOT_AI(from);
if (silent || (fromBotAI && !fromBotAI->IsRealPlayer()))
return false;
PlayerbotAI* botAI = GET_PLAYERBOT_AI(bot);
Player* master = botAI->GetMaster();
if (master && botAI && botAI->IsOpposing(master) && master->GetSession()->GetSecurity() < SEC_GAMEMASTER)
return false;
std::ostringstream out;
switch (realLevel)
{
case PLAYERBOT_SECURITY_DENY_ALL:
out << "I'm kind of busy now";
break;
case PLAYERBOT_SECURITY_TALK:
switch (reason)
{
case PLAYERBOT_DENY_NONE:
out << "I'll do it later";
break;
case PLAYERBOT_DENY_LOW_LEVEL:
out << "You are too low level: |cffff0000" << (uint32)from->getLevel() << "|cffffffff/|cff00ff00" << (uint32)bot->getLevel();
break;
case PLAYERBOT_DENY_GEARSCORE:
{
int botGS = (int)botAI->GetEquipGearScore(bot, false, false);
int fromGS = (int)botAI->GetEquipGearScore(from, false, false);
int diff = (100 * (botGS - fromGS) / botGS);
int req = 12 * sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL) / from->getLevel();
out << "Your gearscore is too low: |cffff0000" << fromGS << "|cffffffff/|cff00ff00" << botGS << " |cffff0000" << diff << "%|cffffffff/|cff00ff00" << req << "%";
}
break;
case PLAYERBOT_DENY_NOT_YOURS:
out << "I have a master already";
break;
case PLAYERBOT_DENY_IS_BOT:
out << "You are a bot";
break;
case PLAYERBOT_DENY_OPPOSING:
out << "You are the enemy";
break;
case PLAYERBOT_DENY_DEAD:
out << "I'm dead. Will do it later";
break;
case PLAYERBOT_DENY_INVITE:
out << "Invite me to your group first";
break;
case PLAYERBOT_DENY_FAR:
{
out << "You must be closer to invite me to your group. I am in ";
if (AreaTableEntry const* entry = sAreaTableStore.LookupEntry(bot->GetAreaId()))
{
out << " |cffffffff(|cffff0000" << entry->area_name[0] << "|cffffffff)";
}
}
break;
case PLAYERBOT_DENY_FULL_GROUP:
out << "I am in a full group. Will do it later";
break;
case PLAYERBOT_DENY_IS_LEADER:
out << "I am currently leading a group. I can invite you if you want.";
break;
case PLAYERBOT_DENY_NOT_LEADER:
out << "I am in a group with " << botAI->GetGroupMaster()->GetName() << ". You can ask him for invite.";
break;
case PLAYERBOT_DENY_BG:
out << "I am in a queue for BG. Will do it later";
break;
case PLAYERBOT_DENY_LFG:
out << "I am in a queue for dungeon. Will do it later";
break;
default:
out << "I can't do that";
break;
}
break;
case PLAYERBOT_SECURITY_INVITE:
out << "Invite me to your group first";
break;
default:
out << "I can't do that";
break;
}
std::string const text = out.str();
ObjectGuid guid = from->GetGUID();
time_t lastSaid = whispers[guid][text];
if (!lastSaid || (time(nullptr) - lastSaid) >= sPlayerbotAIConfig->repeatDelay / 1000)
{
whispers[guid][text] = time(nullptr);
bot->Whisper(text, LANG_UNIVERSAL, from);
}
return false;
}

55
src/PlayerbotSecurity.h Normal file
View File

@@ -0,0 +1,55 @@
/*
* 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.
*/
#ifndef _PLAYERBOT_PLAYERBOTSECURITY_H
#define _PLAYERBOT_PLAYERBOTSECURITY_H
#include "Common.h"
#include "ObjectGuid.h"
#include <map>
class Player;
enum PlayerbotSecurityLevel : uint32
{
PLAYERBOT_SECURITY_DENY_ALL = 0,
PLAYERBOT_SECURITY_TALK = 1,
PLAYERBOT_SECURITY_INVITE = 2,
PLAYERBOT_SECURITY_ALLOW_ALL = 3
};
enum DenyReason
{
PLAYERBOT_DENY_NONE,
PLAYERBOT_DENY_LOW_LEVEL,
PLAYERBOT_DENY_GEARSCORE,
PLAYERBOT_DENY_NOT_YOURS,
PLAYERBOT_DENY_IS_BOT,
PLAYERBOT_DENY_OPPOSING,
PLAYERBOT_DENY_DEAD,
PLAYERBOT_DENY_FAR,
PLAYERBOT_DENY_INVITE,
PLAYERBOT_DENY_FULL_GROUP,
PLAYERBOT_DENY_NOT_LEADER,
PLAYERBOT_DENY_IS_LEADER,
PLAYERBOT_DENY_BG,
PLAYERBOT_DENY_LFG
};
class PlayerbotSecurity
{
public:
PlayerbotSecurity(Player* const bot);
PlayerbotSecurityLevel LevelFor(Player* from, DenyReason* reason = nullptr, bool ignoreGroup = false);
bool CheckLevelFor(PlayerbotSecurityLevel level, bool silent, Player* from, bool ignoreGroup = false);
private:
Player* const bot;
uint32 account;
std::map<ObjectGuid, std::map<std::string, time_t> > whispers;
};
#endif

49
src/PlayerbotTextMgr.cpp Normal file
View File

@@ -0,0 +1,49 @@
/*
* 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 "PlayerbotTextMgr.h"
#include "Playerbots.h"
void replaceAll(std::string& str, std::string const from, std::string const to);
void PlayerbotTextMgr::LoadTemplates()
{
LOG_INFO("playerbots", "Loading playerbots texts...");
uint32 count = 0;
if (PreparedQueryResult result = PlayerbotsDatabase.Query(PlayerbotsDatabase.GetPreparedStatement(PLAYERBOTS_SEL_TEXT)))
{
do
{
Field* fields = result->Fetch();
std::string const key = fields[0].Get<std::string>();
std::string const text = fields[1].Get<std::string>();
templates[key].push_back(text);
++count;
}
while (result->NextRow());
}
LOG_INFO("playerbots", "{} playerbots texts loaded", count);
}
std::string const PlayerbotTextMgr::Format(std::string const key, std::map<std::string, std::string> placeholders)
{
if (templates.empty())
LoadTemplates();
std::vector<std::string>& list = templates[key];
if (list.empty())
{
std::ostringstream out;
out << "Unknown text: " << key;
return out.str();
}
std::string str = list[urand(0, list.size() - 1)];
for (std::map<std::string, std::string>::iterator i = placeholders.begin(); i != placeholders.end(); ++i)
replaceAll(str, i->first, i->second);
return str;
}

34
src/PlayerbotTextMgr.h Normal file
View File

@@ -0,0 +1,34 @@
/*
* 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.
*/
#ifndef _PLAYERBOT_PLAYERBOTTEXTMGR_H
#define _PLAYERBOT_PLAYERBOTTEXTMGR_H
#include "Common.h"
#include <map>
#include <vector>
class PlayerbotTextMgr
{
public:
PlayerbotTextMgr() { };
virtual ~PlayerbotTextMgr() { };
static PlayerbotTextMgr* instance()
{
static PlayerbotTextMgr instance;
return &instance;
}
std::string const Format(std::string const key, std::map<std::string, std::string> placeholders);
private:
void LoadTemplates();
std::map<std::string, std::vector<std::string>> templates;
};
#define sPlayerbotTextMgr PlayerbotTextMgr::instance()
#endif

336
src/Playerbots.cpp Normal file
View File

@@ -0,0 +1,336 @@
/*
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by the
* Free Software Foundation; either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "cs_playerbots.h"
#include "Channel.h"
#include "Config.h"
#include "DatabaseEnv.h"
#include "DatabaseLoader.h"
#include "GuildTaskMgr.h"
#include "Metric.h"
#include "Playerbots.h"
#include "RandomPlayerbotMgr.h"
#include "ScriptMgr.h"
class PlayerbotsDatabaseScript : public DatabaseScript
{
public:
PlayerbotsDatabaseScript() : DatabaseScript("PlayerbotsDatabaseScript") { }
bool OnDatabasesLoading() override
{
DatabaseLoader playerbotLoader("server.playerbots");
playerbotLoader.SetUpdateFlags(sConfigMgr->GetOption<bool>("Playerbots.Updates.EnableDatabases", true) ? DatabaseLoader::DATABASE_PLAYERBOTS : 0);
playerbotLoader.AddDatabase(PlayerbotsDatabase, "Playerbots");
return playerbotLoader.Load();
}
void OnDatabasesKeepAlive() override
{
PlayerbotsDatabase.KeepAlive();
}
void OnDatabasesClosing() override
{
PlayerbotsDatabase.Close();
}
void OnDatabaseWarnAboutSyncQueries(bool apply) override
{
PlayerbotsDatabase.WarnAboutSyncQueries(apply);
}
void OnDatabaseSelectIndexLogout(Player* player, uint32& statementIndex, uint32& statementParam) override
{
statementIndex = CHAR_UPD_CHAR_ONLINE;
statementParam = player->GetGUID().GetCounter();
}
void OnDatabaseGetDBRevision(std::string& revision) override
{
if (QueryResult resultPlayerbot = PlayerbotsDatabase.Query("SELECT date FROM version_db_playerbots ORDER BY date DESC LIMIT 1"))
{
Field* fields = resultPlayerbot->Fetch();
revision = fields[0].Get<std::string>();
}
if (revision.empty())
{
revision = "Unknown Playerbots Database Revision";
}
}
};
class PlayerbotsMetricScript : public MetricScript
{
public:
PlayerbotsMetricScript() : MetricScript("PlayerbotsMetricScript") { }
void OnMetricLogging() override
{
if (sMetric->IsEnabled())
{
sMetric->LogValue("db_queue_playerbots", uint64(PlayerbotsDatabase.QueueSize()), {});
}
}
};
class PlayerbotsPlayerScript : public PlayerScript
{
public:
PlayerbotsPlayerScript() : PlayerScript("PlayerbotsPlayerScript") { }
void OnLogin(Player* player) override
{
if (!player->GetSession()->IsBot())
{
sPlayerbotsMgr->AddPlayerbotData(player, false);
sRandomPlayerbotMgr->OnPlayerLogin(player);
}
}
void OnAfterUpdate(Player* player, uint32 diff) override
{
if (PlayerbotAI* botAI = GET_PLAYERBOT_AI(player))
{
botAI->UpdateAI(diff);
}
if (PlayerbotMgr* playerbotMgr = GET_PLAYERBOT_MGR(player))
{
playerbotMgr->UpdateAI(diff);
}
}
bool CanPlayerUseChat(Player* player, uint32 type, uint32 /*lang*/, std::string& msg, Player* receiver) override
{
if (type == CHAT_MSG_WHISPER)
{
if (PlayerbotAI* botAI = GET_PLAYERBOT_AI(receiver))
{
botAI->HandleCommand(type, msg, player);
player->ResetSpeakTimers();
return false;
}
}
return true;
}
void OnChat(Player* player, uint32 type, uint32 /*lang*/, std::string& msg, Group* group) override
{
for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next())
{
if (Player* member = itr->GetSource())
{
if (PlayerbotAI* botAI = GET_PLAYERBOT_AI(member))
{
botAI->HandleCommand(type, msg, player);
player->ResetSpeakTimers();
}
}
}
}
void OnChat(Player* player, uint32 type, uint32 /*lang*/, std::string& msg) override
{
if (type == CHAT_MSG_GUILD)
{
if (PlayerbotMgr* playerbotMgr = GET_PLAYERBOT_MGR(player))
{
for (PlayerBotMap::const_iterator it = playerbotMgr->GetPlayerBotsBegin(); it != playerbotMgr->GetPlayerBotsEnd(); ++it)
{
if (Player* const bot = it->second)
{
if (bot->GetGuildId() == player->GetGuildId())
{
GET_PLAYERBOT_AI(bot)->HandleCommand(type, msg, player);
}
}
}
}
}
}
void OnChat(Player* player, uint32 type, uint32 /*lang*/, std::string& msg, Channel* channel) override
{
if (PlayerbotMgr* playerbotMgr = GET_PLAYERBOT_MGR(player))
{
if (channel->GetFlags() & 0x18)
{
playerbotMgr->HandleCommand(type, msg);
}
}
sRandomPlayerbotMgr->HandleCommand(type, msg, player);
}
};
class PlayerbotsMiscScript : public MiscScript
{
public:
PlayerbotsMiscScript() : MiscScript("PlayerbotsMiscScript") { }
void OnDestructPlayer(Player* player) override
{
if (PlayerbotAI* botAI = GET_PLAYERBOT_AI(player))
{
delete botAI;
}
if (PlayerbotMgr* playerbotMgr = GET_PLAYERBOT_MGR(player))
{
delete playerbotMgr;
}
}
};
class PlayerbotsServerScript : public ServerScript
{
public:
PlayerbotsServerScript() : ServerScript("PlayerbotsServerScript") { }
void OnPacketReceived(WorldSession* session, WorldPacket const& packet) override
{
if (Player* player = session->GetPlayer())
if (PlayerbotMgr* playerbotMgr = GET_PLAYERBOT_MGR(player))
playerbotMgr->HandleMasterIncomingPacket(packet);
}
};
class PlayerbotsWorldScript : public WorldScript
{
public:
PlayerbotsWorldScript() : WorldScript("PlayerbotsWorldScript") { }
void OnBeforeWorldInitialized() override
{
uint32 oldMSTime = getMSTime();
LOG_INFO("server.loading", " ");
LOG_INFO("server.loading", "Load Playerbots Config...");
sPlayerbotAIConfig->Initialize();
LOG_INFO("server.loading", ">> Loaded playerbots config in {} ms", GetMSTimeDiffToNow(oldMSTime));
LOG_INFO("server.loading", " ");
}
};
class PlayerbotsScript : public PlayerbotScript
{
public:
PlayerbotsScript() : PlayerbotScript("PlayerbotsScript") { }
bool OnPlayerbotCheckLFGQueue(lfg::Lfg5Guids const& guidsList) override
{
bool nonBotFound = false;
for (ObjectGuid const& guid : guidsList.guids)
{
Player* player = ObjectAccessor::FindPlayer(guid);
if (guid.IsGroup() || (player && !GET_PLAYERBOT_AI(player)))
{
nonBotFound = true;
break;
}
}
return nonBotFound;
}
void OnPlayerbotCheckKillTask(Player* player, Unit* victim) override
{
if (player)
sGuildTaskMgr->CheckKillTask(player, victim);
}
void OnPlayerbotCheckPetitionAccount(Player* player, bool& found) override
{
if (found && GET_PLAYERBOT_AI(player))
found = false;
}
bool OnPlayerbotCheckUpdatesToSend(Player* player) override
{
if (PlayerbotAI* botAI = GET_PLAYERBOT_AI(player))
return botAI->IsRealPlayer();
return true;
}
void OnPlayerbotPacketSent(Player* player, WorldPacket const* packet) override
{
if (!player)
return;
if (PlayerbotAI* botAI = GET_PLAYERBOT_AI(player))
{
botAI->HandleBotOutgoingPacket(*packet);
}
else if (PlayerbotMgr* playerbotMgr = GET_PLAYERBOT_MGR(player))
{
playerbotMgr->HandleMasterOutgoingPacket(*packet);
}
}
void OnPlayerbotUpdate(uint32 diff) override
{
sRandomPlayerbotMgr->UpdateAI(diff);
sRandomPlayerbotMgr->UpdateSessions();
}
void OnPlayerbotUpdateSessions(Player* player) override
{
if (player)
if (PlayerbotMgr* playerbotMgr = GET_PLAYERBOT_MGR(player))
playerbotMgr->UpdateSessions();
}
void OnPlayerbotLogout(Player* player) override
{
if (PlayerbotMgr* playerbotMgr = GET_PLAYERBOT_MGR(player))
{
PlayerbotAI* botAI = GET_PLAYERBOT_AI(player);
if (!botAI || botAI->IsRealPlayer())
{
playerbotMgr->LogoutAllBots();
}
}
sRandomPlayerbotMgr->OnPlayerLogout(player);
}
void OnPlayerbotLogoutBots() override
{
sRandomPlayerbotMgr->LogoutAllBots();
}
};
void AddPlayerbotsScripts()
{
new PlayerbotsDatabaseScript();
new PlayerbotsMetricScript();
new PlayerbotsPlayerScript();
new PlayerbotsMiscScript();
new PlayerbotsServerScript();
new PlayerbotsWorldScript();
new PlayerbotsScript();
AddSC_playerbots_commandscript();
}

47
src/Playerbots.h Normal file
View File

@@ -0,0 +1,47 @@
/*
* 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.
*/
#ifndef _PLAYERBOT_H
#define _PLAYERBOT_H
#include "AiObjectContext.h"
#include "Group.h"
#include "Pet.h"
#include "PlayerbotAI.h"
#include "PlayerbotAIConfig.h"
#include "PlayerbotMgr.h"
#include "RandomPlayerbotMgr.h"
#include "SharedValueContext.h"
#include "Spell.h"
#include "TravelNode.h"
std::vector<std::string> split(std::string const s, char delim);
void split(std::vector<std::string>& dest, std::string const str, char const* delim);
#ifndef WIN32
int strcmpi(char const* s1, char const* s2);
#endif
#define CAST_ANGLE_IN_FRONT (2.f * static_cast<float>(M_PI) / 3.f)
#define EMOTE_ANGLE_IN_FRONT (2.f * static_cast<float>(M_PI) / 6.f)
#define GET_PLAYERBOT_AI(object) sPlayerbotsMgr->GetPlayerbotAI(object)
#define GET_PLAYERBOT_MGR(object) sPlayerbotsMgr->GetPlayerbotMgr(object)
#define AI_VALUE(type, name) context->GetValue<type>(name)->Get()
#define AI_VALUE2(type, name, param) context->GetValue<type>(name, param)->Get()
#define AI_VALUE_LAZY(type, name) context->GetValue<type>(name)->LazyGet()
#define AI_VALUE2_LAZY(type, name, param) context->GetValue<type>(name, param)->LazyGet()
#define SET_AI_VALUE(type, name, value) context->GetValue<type>(name)->Set(value)
#define SET_AI_VALUE2(type, name, param, value) context->GetValue<type>(name, param)->Set(value)
#define RESET_AI_VALUE(type, name) context->GetValue<type>(name)->Reset()
#define RESET_AI_VALUE2(type, name, param) context->GetValue<type>(name, param)->Reset()
#define PAI_VALUE(type, name) sPlayerbotsMgr->GetPlayerbotAI(player)->GetAiObjectContext()->GetValue<type>(name)->Get()
#define PAI_VALUE2(type, name, param) sPlayerbotsMgr->GetPlayerbotAI(player)->GetAiObjectContext()->GetValue<type>(name, param)->Get()
#define GAI_VALUE(type, name) sSharedValueContext->getGlobalValue<type>(name)->Get()
#define GAI_VALUE2(type, name, param) sSharedValueContext->getGlobalValue<type>(name, param)->Get()
#endif

2720
src/RandomItemMgr.cpp Normal file

File diff suppressed because it is too large Load Diff

204
src/RandomItemMgr.h Normal file
View File

@@ -0,0 +1,204 @@
/*
* 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.
*/
#ifndef _PLAYERBOT_RANDOMITEMMGR_H
#define _PLAYERBOT_RANDOMITEMMGR_H
#include "AiFactory.h"
#include "Common.h"
#include "ItemTemplate.h"
#include <map>
#include <set>
#include <vector>
class ChatHandler;
struct ItemTemplate;
enum EquipmentSlots : uint32;
enum RandomItemType
{
RANDOM_ITEM_GUILD_TASK,
RANDOM_ITEM_GUILD_TASK_REWARD_EQUIP_BLUE,
RANDOM_ITEM_GUILD_TASK_REWARD_EQUIP_GREEN,
RANDOM_ITEM_GUILD_TASK_REWARD_TRADE,
RANDOM_ITEM_GUILD_TASK_REWARD_TRADE_RARE
};
#define MAX_STAT_SCALES 32
enum ItemSource
{
ITEM_SOURCE_NONE,
ITEM_SOURCE_DROP,
ITEM_SOURCE_VENDOR,
ITEM_SOURCE_QUEST,
ITEM_SOURCE_CRAFT,
ITEM_SOURCE_PVP
};
struct WeightScaleInfo
{
uint32 id;
std::string name;
};
struct WeightScaleStat
{
std::string stat;
uint32 weight;
};
struct StatWeight
{
uint32 id;
uint32 weight;
};
struct ItemInfoEntry
{
ItemInfoEntry() : minLevel(0), source(0), sourceId(0), team(0), repRank(0), repFaction(0), quality(0), slot(0), itemId(0)
{
for (uint8 i = 1; i <= MAX_STAT_SCALES; ++i)
{
weights[i] = 0;
}
}
std::map<uint32, uint32> weights;
uint32 minLevel;
uint32 source;
uint32 sourceId;
uint32 team;
uint32 repRank;
uint32 repFaction;
uint32 quality;
uint32 slot;
uint32 itemId;
};
typedef std::vector<WeightScaleStat> WeightScaleStats;
//typedef std::map<WeightScaleInfo, WeightScaleStats> WeightScaleList;
struct WeightScale
{
WeightScaleInfo info;
WeightScaleStats stats;
};
//typedef map<uint32, WeightScale> WeightScales;
class RandomItemPredicate
{
public:
virtual ~RandomItemPredicate() { };
virtual bool Apply(ItemTemplate const* proto) = 0;
};
typedef std::vector<uint32> RandomItemList;
typedef std::map<RandomItemType, RandomItemList> RandomItemCache;
class BotEquipKey
{
public:
BotEquipKey() : level(0), clazz(0), slot(0), quality(0), key(GetKey()) { }
BotEquipKey(uint32 level, uint8 clazz, uint8 slot, uint32 quality) : level(level), clazz(clazz), slot(slot), quality(quality), key(GetKey()) { }
BotEquipKey(BotEquipKey const& other) : level(other.level), clazz(other.clazz), slot(other.slot), quality(other.quality), key(GetKey()) { }
bool operator<(BotEquipKey const& other) const
{
return other.key < this->key;
}
uint32 level;
uint8 clazz;
uint8 slot;
uint32 quality;
uint64 key;
private:
uint64 GetKey();
};
typedef std::map<BotEquipKey, RandomItemList> BotEquipCache;
class RandomItemMgr
{
public:
RandomItemMgr();
virtual ~RandomItemMgr();
static RandomItemMgr* instance()
{
static RandomItemMgr instance;
return &instance;
}
public:
void Init();
void InitAfterAhBot();
static bool HandleConsoleCommand(ChatHandler* handler, char const* args);
RandomItemList Query(uint32 level, RandomItemType type, RandomItemPredicate* predicate);
RandomItemList Query(uint32 level, uint8 clazz, uint8 slot, uint32 quality);
uint32 GetUpgrade(Player* player, std::string spec, uint8 slot, uint32 quality, uint32 itemId);
std::vector<uint32> GetUpgradeList(Player* player, std::string spec, uint8 slot, uint32 quality, uint32 itemId, uint32 amount = 1);
bool HasStatWeight(uint32 itemId);
uint32 GetMinLevelFromCache(uint32 itemId);
uint32 GetStatWeight(Player* player, uint32 itemId);
uint32 GetLiveStatWeight(Player* player, uint32 itemId);
uint32 GetRandomItem(uint32 level, RandomItemType type, RandomItemPredicate* predicate = nullptr);
uint32 GetAmmo(uint32 level, uint32 subClass);
uint32 GetRandomPotion(uint32 level, uint32 effect);
uint32 GetRandomFood(uint32 level, uint32 category);
uint32 GetFood(uint32 level, uint32 category);
uint32 GetRandomTrade(uint32 level);
uint32 CalculateStatWeight(uint8 playerclass, uint8 spec, ItemTemplate const* proto);
uint32 CalculateSingleStatWeight(uint8 playerclass, uint8 spec, std::string stat, uint32 value);
bool CanEquipArmor(uint8 clazz, uint32 level, ItemTemplate const* proto);
bool ShouldEquipArmorForSpec(uint8 playerclass, uint8 spec, ItemTemplate const* proto);
bool CanEquipWeapon(uint8 clazz, ItemTemplate const* proto);
bool ShouldEquipWeaponForSpec(uint8 playerclass, uint8 spec, ItemTemplate const* proto);
float GetItemRarity(uint32 itemId);
uint32 GetQuestIdForItem(uint32 itemId);
std::vector<uint32> GetQuestIdsForItem(uint32 itemId);
static bool IsUsedBySkill(ItemTemplate const* proto, uint32 skillId);
private:
void BuildRandomItemCache();
void BuildEquipCache();
void BuildItemInfoCache();
void BuildAmmoCache();
void BuildFoodCache();
void BuildPotionCache();
void BuildTradeCache();
void BuildRarityCache();
bool CanEquipItem(BotEquipKey key, ItemTemplate const* proto);
bool CanEquipItemNew(ItemTemplate const* proto);
void AddItemStats(uint32 mod, uint8& sp, uint8& ap, uint8& tank);
bool CheckItemStats(uint8 clazz, uint8 sp, uint8 ap, uint8 tank);
private:
std::map<uint32, RandomItemCache> randomItemCache;
std::map<RandomItemType, RandomItemPredicate*> predicates;
BotEquipCache equipCache;
std::map<EquipmentSlots, std::set<InventoryType>> viableSlots;
std::map<uint32, std::map<uint32, uint32> > ammoCache;
std::map<uint32, std::map<uint32, std::vector<uint32> > > potionCache;
std::map<uint32, std::map<uint32, std::vector<uint32> > > foodCache;
std::map<uint32, std::vector<uint32> > tradeCache;
std::map<uint32, float> rarityCache;
std::map<uint8, WeightScale> m_weightScales[MAX_CLASSES];
std::map<std::string, uint32 > weightStatLink;
std::map<std::string, uint32 > weightRatingLink;
std::map<uint32, ItemInfoEntry> itemInfoCache;
static std::set<uint32> itemCache;
};
#define sRandomItemMgr RandomItemMgr::instance()
#endif

View File

@@ -0,0 +1,731 @@
/*
* 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 "RandomPlayerbotFactory.h"
#include "ArenaTeamMgr.h"
#include "AccountMgr.h"
#include "GuildMgr.h"
#include "Playerbots.h"
#include "PlayerbotFactory.h"
#include "SocialMgr.h"
std::map<uint8, std::vector<uint8>> RandomPlayerbotFactory::availableRaces;
RandomPlayerbotFactory::RandomPlayerbotFactory(uint32 accountId) : accountId(accountId)
{
availableRaces[CLASS_WARRIOR].push_back(RACE_HUMAN);
availableRaces[CLASS_WARRIOR].push_back(RACE_NIGHTELF);
availableRaces[CLASS_WARRIOR].push_back(RACE_GNOME);
availableRaces[CLASS_WARRIOR].push_back(RACE_DWARF);
availableRaces[CLASS_WARRIOR].push_back(RACE_ORC);
availableRaces[CLASS_WARRIOR].push_back(RACE_UNDEAD_PLAYER);
availableRaces[CLASS_WARRIOR].push_back(RACE_TAUREN);
availableRaces[CLASS_WARRIOR].push_back(RACE_TROLL);
availableRaces[CLASS_WARRIOR].push_back(RACE_DRAENEI);
availableRaces[CLASS_PALADIN].push_back(RACE_HUMAN);
availableRaces[CLASS_PALADIN].push_back(RACE_DWARF);
availableRaces[CLASS_PALADIN].push_back(RACE_DRAENEI);
availableRaces[CLASS_PALADIN].push_back(RACE_BLOODELF);
availableRaces[CLASS_ROGUE].push_back(RACE_HUMAN);
availableRaces[CLASS_ROGUE].push_back(RACE_DWARF);
availableRaces[CLASS_ROGUE].push_back(RACE_NIGHTELF);
availableRaces[CLASS_ROGUE].push_back(RACE_GNOME);
availableRaces[CLASS_ROGUE].push_back(RACE_ORC);
availableRaces[CLASS_ROGUE].push_back(RACE_TROLL);
availableRaces[CLASS_ROGUE].push_back(RACE_BLOODELF);
availableRaces[CLASS_PRIEST].push_back(RACE_HUMAN);
availableRaces[CLASS_PRIEST].push_back(RACE_DWARF);
availableRaces[CLASS_PRIEST].push_back(RACE_NIGHTELF);
availableRaces[CLASS_PRIEST].push_back(RACE_TROLL);
availableRaces[CLASS_PRIEST].push_back(RACE_UNDEAD_PLAYER);
availableRaces[CLASS_PRIEST].push_back(RACE_DRAENEI);
availableRaces[CLASS_PRIEST].push_back(RACE_BLOODELF);
availableRaces[CLASS_MAGE].push_back(RACE_HUMAN);
availableRaces[CLASS_MAGE].push_back(RACE_GNOME);
availableRaces[CLASS_MAGE].push_back(RACE_UNDEAD_PLAYER);
availableRaces[CLASS_MAGE].push_back(RACE_TROLL);
availableRaces[CLASS_MAGE].push_back(RACE_DRAENEI);
availableRaces[CLASS_MAGE].push_back(RACE_BLOODELF);
availableRaces[CLASS_WARLOCK].push_back(RACE_HUMAN);
availableRaces[CLASS_WARLOCK].push_back(RACE_GNOME);
availableRaces[CLASS_WARLOCK].push_back(RACE_UNDEAD_PLAYER);
availableRaces[CLASS_WARLOCK].push_back(RACE_ORC);
availableRaces[CLASS_WARLOCK].push_back(RACE_BLOODELF);
availableRaces[CLASS_SHAMAN].push_back(RACE_ORC);
availableRaces[CLASS_SHAMAN].push_back(RACE_TAUREN);
availableRaces[CLASS_SHAMAN].push_back(RACE_TROLL);
availableRaces[CLASS_SHAMAN].push_back(RACE_DRAENEI);
availableRaces[CLASS_HUNTER].push_back(RACE_DWARF);
availableRaces[CLASS_HUNTER].push_back(RACE_NIGHTELF);
availableRaces[CLASS_HUNTER].push_back(RACE_ORC);
availableRaces[CLASS_HUNTER].push_back(RACE_TAUREN);
availableRaces[CLASS_HUNTER].push_back(RACE_TROLL);
availableRaces[CLASS_HUNTER].push_back(RACE_DRAENEI);
availableRaces[CLASS_HUNTER].push_back(RACE_BLOODELF);
availableRaces[CLASS_DRUID].push_back(RACE_NIGHTELF);
availableRaces[CLASS_DRUID].push_back(RACE_TAUREN);
availableRaces[CLASS_DEATH_KNIGHT].push_back(RACE_NIGHTELF);
availableRaces[CLASS_DEATH_KNIGHT].push_back(RACE_TAUREN);
availableRaces[CLASS_DEATH_KNIGHT].push_back(RACE_HUMAN);
availableRaces[CLASS_DEATH_KNIGHT].push_back(RACE_ORC);
availableRaces[CLASS_DEATH_KNIGHT].push_back(RACE_UNDEAD_PLAYER);
availableRaces[CLASS_DEATH_KNIGHT].push_back(RACE_TROLL);
availableRaces[CLASS_DEATH_KNIGHT].push_back(RACE_BLOODELF);
availableRaces[CLASS_DEATH_KNIGHT].push_back(RACE_DRAENEI);
availableRaces[CLASS_DEATH_KNIGHT].push_back(RACE_GNOME);
availableRaces[CLASS_DEATH_KNIGHT].push_back(RACE_DWARF);
}
Player* RandomPlayerbotFactory::CreateRandomBot(WorldSession* session, uint8 cls, std::unordered_map<uint8, std::vector<std::string>> names)
{
LOG_DEBUG("playerbots", "Creating new random bot for class {}", cls);
uint8 gender = rand() % 2 ? GENDER_MALE : GENDER_FEMALE;
uint8 race = availableRaces[cls][urand(0, availableRaces[cls].size() - 1)];
std::string name;
if (names.empty())
name = CreateRandomBotName(gender);
else
{
if (names[gender].empty())
return nullptr;
uint32 i = urand(0, names[gender].size() - 1);
name = names[gender][i];
std::swap(names[gender][i], names[gender].back());
names[gender].pop_back();
}
if (name.empty())
return nullptr;
std::vector<uint8> skinColors, facialHairTypes;
std::vector<std::pair<uint8,uint8>> faces, hairs;
for (CharSectionsEntry const* charSection : sCharSectionsStore)
{
if (charSection->Race != race || charSection->Gender != gender)
continue;
switch (charSection->GenType)
{
case SECTION_TYPE_SKIN:
skinColors.push_back(charSection->Color);
break;
case SECTION_TYPE_FACE:
faces.push_back(std::pair<uint8, uint8>(charSection->Type, charSection->Color));
break;
case SECTION_TYPE_FACIAL_HAIR:
facialHairTypes.push_back(charSection->Type);
break;
case SECTION_TYPE_HAIR:
hairs.push_back(std::pair<uint8, uint8>(charSection->Type, charSection->Color));
break;
}
}
uint8 skinColor = skinColors[urand(0, skinColors.size() - 1)];
std::pair<uint8, uint8> face = faces[urand(0, faces.size() - 1)];
std::pair<uint8, uint8> hair = hairs[urand(0, hairs.size() - 1)];
bool excludeCheck = (race == RACE_TAUREN) || (race == RACE_DRAENEI) || (gender == GENDER_FEMALE && race != RACE_NIGHTELF && race != RACE_UNDEAD_PLAYER);
uint8 facialHair = excludeCheck ? 0 : facialHairTypes[urand(0, facialHairTypes.size() - 1)];
std::unique_ptr<CharacterCreateInfo> characterInfo = std::make_unique<CharacterCreateInfo>(name, race, cls, gender, face.second, face.first, hair.first, hair.second, facialHair);
Player* player = new Player(session);
player->GetMotionMaster()->Initialize();
if (!player->Create(sObjectMgr->GetGenerator<HighGuid::Player>().Generate(), characterInfo.get()))
{
player->CleanupsBeforeDelete();
delete player;
LOG_ERROR("playerbots", "Unable to create random bot for account {} - name: \"{}\"; race: {}; class: {}", accountId, name.c_str(), race, cls);
return nullptr;
}
player->setCinematic(2);
player->SetAtLoginFlag(AT_LOGIN_NONE);
LOG_DEBUG("playerbots", "Random bot created for account {} - name: \"{}\"; race: {}; class: {}", accountId, name.c_str(), race, cls);
return player;
}
std::string const RandomPlayerbotFactory::CreateRandomBotName(uint8 gender)
{
std::string botName = "";
QueryResult result = CharacterDatabase.Query("SELECT MAX(name_id) FROM playerbots_names");
if (!result)
{
LOG_ERROR("playerbots", "No more names left for random bots");
return std::move(botName);
}
Field* fields = result->Fetch();
uint32 maxId = fields[0].Get<uint32>();
result = CharacterDatabase.Query("SELECT n.name FROM playerbots_names n "
"LEFT OUTER JOIN characters e ON e.name = n.name WHERE e.guid IS NULL AND n.gender = {} ORDER BY RAND() LIMIT 1", gender);
if (!result)
{
LOG_ERROR("playerbots", "No more names left for random bots");
return std::move(botName);
}
fields = result->Fetch();
botName = fields[0].Get<std::string>();
return std::move(botName);
}
void RandomPlayerbotFactory::CreateRandomBots()
{
// check if scheduled for delete
bool delAccs = false;
bool delFriends = false;
PlayerbotsDatabasePreparedStatement* stmt = PlayerbotsDatabase.GetPreparedStatement(PLAYERBOTS_SEL_RANDOM_BOTS_VALUE);
stmt->SetData(0, "bot_delete");
if (PreparedQueryResult result = PlayerbotsDatabase.Query(stmt))
{
delAccs = true;
Field* fields = result->Fetch();
uint32 deleteType = fields[0].Get<uint32>();
if (deleteType > 1)
delFriends = true;
PlayerbotsDatabase.Execute("DELETE FROM playerbots_random_bots WHERE event = 'bot_delete'");
}
if (sPlayerbotAIConfig->deleteRandomBotAccounts || delAccs)
{
std::vector<uint32> botAccounts;
std::vector<uint32> botFriends;
for (uint32 accountNumber = 0; accountNumber < sPlayerbotAIConfig->randomBotAccountCount; ++accountNumber)
{
std::ostringstream out;
out << sPlayerbotAIConfig->randomBotAccountPrefix << accountNumber;
std::string const accountName = out.str();
if (uint32 accountId = AccountMgr::GetId(accountName))
botAccounts.push_back(accountId);
}
if (!delFriends)
LOG_INFO("playerbots", "Deleting random bot characters without friends/guild...");
else
LOG_INFO("playerbots", "Deleting all random bot characters...");
// load list of friends
if (!delFriends)
{
QueryResult result = CharacterDatabase.Query("SELECT friend FROM character_social WHERE flags={}", SOCIAL_FLAG_FRIEND);
if (result)
{
do
{
Field* fields = result->Fetch();
uint32 guidlow = fields[0].Get<uint32>();
botFriends.push_back(guidlow);
} while (result->NextRow());
}
}
std::vector<std::future<void>> dels;
QueryResult results = LoginDatabase.Query("SELECT id FROM account WHERE username LIKE ''{}'%%'", sPlayerbotAIConfig->randomBotAccountPrefix.c_str());
if (results)
{
do
{
Field* fields = results->Fetch();
uint32 accId = fields[0].Get<uint32>();
if (!delFriends)
{
// existing characters list
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARS_BY_ACCOUNT_ID);
stmt->SetData(0, accId);
if (PreparedQueryResult result = CharacterDatabase.Query(stmt))
{
do
{
Field* fields = result->Fetch();
uint32 guidlow = fields[0].Get<uint32>();
ObjectGuid guid = ObjectGuid::Create<HighGuid::Player>(guidlow);
// if bot is someone's friend - don't delete it
if ((find(botFriends.begin(), botFriends.end(), guidlow) != botFriends.end()) && !delFriends)
continue;
// if bot is in someone's guild - don't delete it
uint32 guildId = sCharacterCache->GetCharacterGuildIdByGuid(guid);
if (guildId && !delFriends)
{
Guild* guild = sGuildMgr->GetGuildById(guildId);
uint32 accountId = sRandomPlayerbotMgr->GetAccountId(guild->GetLeaderGUID());
if (find(botAccounts.begin(), botAccounts.end(), accountId) == botAccounts.end())
continue;
}
Player::DeleteFromDB(guidlow, accId, false, true); // no need to update realm characters
} while (result->NextRow());
}
}
else
dels.push_back(std::async([accId]
{
AccountMgr::DeleteAccount(accId);
}));
} while (results->NextRow());
}
for (uint32 i = 0; i < dels.size(); i++)
{
dels[i].wait();
}
PlayerbotsDatabase.Execute(PlayerbotsDatabase.GetPreparedStatement(PLAYERBOTS_DEL_RANDOM_BOTS));
LOG_INFO("playerbots", "Random bot characters deleted");
}
uint32 totalAccCount = sPlayerbotAIConfig->randomBotAccountCount;
LOG_INFO("server.loading", "Creating random bot accounts...");
std::vector<std::future<void>> account_creations;
for (uint32 accountNumber = 0; accountNumber < sPlayerbotAIConfig->randomBotAccountCount; ++accountNumber)
{
std::ostringstream out;
out << sPlayerbotAIConfig->randomBotAccountPrefix << accountNumber;
std::string const accountName = out.str();
LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_GET_ACCOUNT_ID_BY_USERNAME);
stmt->SetData(0, accountName);
PreparedQueryResult result = LoginDatabase.Query(stmt);
if (result)
{
continue;
}
std::string password = "";
if (sPlayerbotAIConfig->randomBotRandomPassword)
{
for (int i = 0; i < 10; i++)
{
password += (char) urand('!', 'z');
}
}
else
password = accountName;
account_creations.push_back(std::async([accountName, password]
{
AccountMgr::CreateAccount(accountName, password);
}));
LOG_DEBUG("playerbots", "Account {} created for random bots", accountName.c_str());
}
for (uint32 i = 0; i < account_creations.size(); i++)
{
account_creations[i].wait();
}
//LoginDatabase.Execute("UPDATE account SET expansion = {} WHERE username LIKE ''{}'%%'", EXPANSION_WRATH_OF_THE_LICH_KING, sPlayerbotAIConfig->randomBotAccountPrefix.c_str());
LOG_INFO("server.loading", "Loading available names...");
std::unordered_map<uint8,std::vector<std::string>> names;
QueryResult result = CharacterDatabase.Query("SELECT n.gender, n.name FROM playerbots_names n LEFT OUTER JOIN characters e ON e.name = n.name WHERE e.guid IS NULL");
if (!result)
{
LOG_ERROR("server.loading", "No more names left for random bots");
return;
}
do
{
Field* fields = result->Fetch();
uint8 gender = fields[0].Get<uint8>();
std::string const bname = fields[1].Get<std::string>();
names[gender].push_back(bname);
} while (result->NextRow());
LOG_INFO("playerbots", "Creating random bot characters...");
uint32 totalRandomBotChars = 0;
uint32 totalCharCount = sPlayerbotAIConfig->randomBotAccountCount * 10;
std::vector<std::pair<Player*, uint32>> playerBots;
std::vector<WorldSession*> sessionBots;
for (uint32 accountNumber = 0; accountNumber < sPlayerbotAIConfig->randomBotAccountCount; ++accountNumber)
{
std::ostringstream out;
out << sPlayerbotAIConfig->randomBotAccountPrefix << accountNumber;
std::string const accountName = out.str();
LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_GET_ACCOUNT_ID_BY_USERNAME);
stmt->SetData(0, accountName);
PreparedQueryResult result = LoginDatabase.Query(stmt);
if (!result)
continue;
Field* fields = result->Fetch();
uint32 accountId = fields[0].Get<uint32>();
sPlayerbotAIConfig->randomBotAccounts.push_back(accountId);
uint32 count = AccountMgr::GetCharactersCount(accountId);
if (count >= 10)
{
totalRandomBotChars += count;
continue;
}
RandomPlayerbotFactory factory(accountId);
WorldSession* session = new WorldSession(accountId, "", nullptr, SEC_PLAYER, EXPANSION_WRATH_OF_THE_LICH_KING, time_t(0), LOCALE_enUS, 0, false, false, 0, true);
sessionBots.push_back(session);
for (uint8 cls = CLASS_WARRIOR; cls < MAX_CLASSES - count; ++cls)
{
if (cls != 10)
if (Player* playerBot = factory.CreateRandomBot(session, cls, names))
playerBots.emplace_back(playerBot, accountId);
}
totalRandomBotChars += AccountMgr::GetCharactersCount(accountId);
}
std::vector<std::future<void>> bot_creations;
for (auto const& itr : playerBots)
{
Player* player = itr.first;
uint32 accountId = itr.second;
bot_creations.push_back(std::async([player, accountId]
{
player->SaveToDB(true, false);
sCharacterCache->AddCharacterCacheEntry(player->GetGUID(), accountId, player->GetName(), player->getGender(), player->getRace(), player->getClass(), player->getLevel());
player->CleanupsBeforeDelete();
delete player;
}));
}
for (uint32 i = 0; i < bot_creations.size(); i++)
{
bot_creations[i].wait();
}
for (WorldSession* session : sessionBots)
delete session;
LOG_INFO("server.loading", "{} random bot accounts with {} characters available", sPlayerbotAIConfig->randomBotAccounts.size(), totalRandomBotChars);
}
void RandomPlayerbotFactory::CreateRandomGuilds()
{
std::vector<uint32> randomBots;
PlayerbotsDatabasePreparedStatement* stmt = PlayerbotsDatabase.GetPreparedStatement(PLAYERBOTS_SEL_RANDOM_BOTS_BOT);
stmt->SetData(0, "add");
if (PreparedQueryResult result = PlayerbotsDatabase.Query(stmt))
{
do
{
Field* fields = result->Fetch();
uint32 bot = fields[0].Get<uint32>();
randomBots.push_back(bot);
}
while (result->NextRow());
}
if (sPlayerbotAIConfig->deleteRandomBotGuilds)
{
LOG_INFO("playerbots", "Deleting random bot guilds...");
for (std::vector<uint32>::iterator i = randomBots.begin(); i != randomBots.end(); ++i)
{
if (Guild* guild = sGuildMgr->GetGuildByLeader(ObjectGuid::Create<HighGuid::Player>(*i)))
guild->Disband();
}
LOG_INFO("playerbots", "Random bot guilds deleted");
}
uint32 guildNumber = 0;
GuidVector availableLeaders;
for (std::vector<uint32>::iterator i = randomBots.begin(); i != randomBots.end(); ++i)
{
ObjectGuid leader = ObjectGuid::Create<HighGuid::Player>(*i);
if (Guild* guild = sGuildMgr->GetGuildByLeader(leader))
{
++guildNumber;
sPlayerbotAIConfig->randomBotGuilds.push_back(guild->GetId());
}
else
{
Player* player = ObjectAccessor::FindPlayer(leader);
if (player && !player->GetGuildId())
availableLeaders.push_back(leader);
}
}
for (; guildNumber < sPlayerbotAIConfig->randomBotGuildCount; ++guildNumber)
{
std::string const guildName = CreateRandomGuildName();
if (guildName.empty())
continue;
if (availableLeaders.empty())
{
LOG_ERROR("playerbots", "No leaders for random guilds available");
continue;
}
uint32 index = urand(0, availableLeaders.size() - 1);
ObjectGuid leader = availableLeaders[index];
Player* player = ObjectAccessor::FindPlayer(leader);
if (!player)
{
LOG_ERROR("playerbots", "Cannot find player for leader {}", player->GetName().c_str());
continue;
}
Guild* guild = new Guild();
if (!guild->Create(player, guildName))
{
LOG_ERROR("playerbots", "Error creating guild {}", guildName.c_str());
delete guild;
continue;
}
sGuildMgr->AddGuild(guild);
// create random emblem
uint32 st, cl, br, bc, bg;
bg = urand(0, 51);
bc = urand(0, 17);
cl = urand(0, 17);
br = urand(0, 7);
st = urand(0, 180);
EmblemInfo emblemInfo(st, cl, br, bc, bg);
guild->HandleSetEmblem(emblemInfo);
sPlayerbotAIConfig->randomBotGuilds.push_back(guild->GetId());
}
LOG_INFO("playerbots", "{} random bot guilds available", guildNumber);
}
std::string const RandomPlayerbotFactory::CreateRandomGuildName()
{
std::string guildName = "";
QueryResult result = CharacterDatabase.Query("SELECT MAX(name_id) FROM playerbots_guild_names");
if (!result)
{
LOG_ERROR("playerbots", "No more names left for random guilds");
return std::move(guildName);
}
Field* fields = result->Fetch();
uint32 maxId = fields[0].Get<uint32>();
uint32 id = urand(0, maxId);
result = CharacterDatabase.Query("SELECT n.name FROM playerbots_guild_names n "
"LEFT OUTER JOIN guild e ON e.name = n.name WHERE e.guildid IS NULL AND n.name_id >= {} LIMIT 1", id);
if (!result)
{
LOG_ERROR("playerbots", "No more names left for random guilds");
return std::move(guildName);
}
fields = result->Fetch();
guildName = fields[0].Get<std::string>();
return std::move(guildName);
}
void RandomPlayerbotFactory::CreateRandomArenaTeams()
{
std::vector<uint32> randomBots;
PlayerbotsDatabasePreparedStatement* stmt = PlayerbotsDatabase.GetPreparedStatement(PLAYERBOTS_SEL_RANDOM_BOTS_BOT);
stmt->SetData(0, "add");
if (PreparedQueryResult result = PlayerbotsDatabase.Query(stmt))
{
do
{
Field* fields = result->Fetch();
uint32 bot = fields[0].Get<uint32>();
randomBots.push_back(bot);
}
while (result->NextRow());
}
if (sPlayerbotAIConfig->deleteRandomBotArenaTeams)
{
LOG_INFO("playerbots", "Deleting random bot arena teams...");
for (std::vector<uint32>::iterator i = randomBots.begin(); i != randomBots.end(); ++i)
{
ObjectGuid captain = ObjectGuid::Create<HighGuid::Player>(*i);
ArenaTeam* arenateam = sArenaTeamMgr->GetArenaTeamByCaptain(captain);
if (arenateam)
//sObjectMgr->RemoveArenaTeam(arenateam->GetId());
arenateam->Disband(nullptr);
}
LOG_INFO("playerbots", "Random bot arena teams deleted");
}
uint32 arenaTeamNumber = 0;
GuidVector availableCaptains;
for (std::vector<uint32>::iterator i = randomBots.begin(); i != randomBots.end(); ++i)
{
ObjectGuid captain = ObjectGuid::Create<HighGuid::Player>(*i);
ArenaTeam* arenateam = sArenaTeamMgr->GetArenaTeamByCaptain(captain);
if (arenateam)
{
++arenaTeamNumber;
sPlayerbotAIConfig->randomBotArenaTeams.push_back(arenateam->GetId());
}
else
{
Player* player = ObjectAccessor::FindConnectedPlayer(captain);
if (!arenateam && player && player->getLevel() >= 70)
availableCaptains.push_back(captain);
}
}
for (; arenaTeamNumber < sPlayerbotAIConfig->randomBotArenaTeamCount; ++arenaTeamNumber)
{
std::string const arenaTeamName = CreateRandomArenaTeamName();
if (arenaTeamName.empty())
continue;
if (availableCaptains.empty())
{
LOG_ERROR("playerbots", "No captains for random arena teams available");
continue;
}
uint32 index = urand(0, availableCaptains.size() - 1);
ObjectGuid captain = availableCaptains[index];
Player* player = ObjectAccessor::FindConnectedPlayer(captain);
if (!player)
{
LOG_ERROR("playerbots", "Cannot find player for captain {}", captain.ToString().c_str());
continue;
}
if (player->getLevel() < 70)
{
LOG_ERROR("playerbots", "Bot {} must be level 70 to create an arena team", captain.ToString().c_str());
continue;
}
QueryResult results = CharacterDatabase.Query("SELECT `type` FROM playerbots_arena_team_names WHERE name = '{}'", arenaTeamName.c_str());
if (!results)
{
LOG_ERROR("playerbots", "No valid types for arena teams");
return;
}
Field* fields = results->Fetch();
uint8 slot = fields[0].Get<uint8>();
ArenaType type;
switch (slot)
{
case 2:
type = ARENA_TYPE_2v2;
break;
case 3:
type = ARENA_TYPE_3v3;
break;
case 5:
type = ARENA_TYPE_5v5;
break;
}
ArenaTeam* arenateam = new ArenaTeam();
if (!arenateam->Create(player->GetGUID(), type, arenaTeamName, 0, 0, 0, 0, 0))
{
LOG_ERROR("playerbots", "Error creating arena team {}", arenaTeamName.c_str());
continue;
}
arenateam->SetCaptain(player->GetGUID());
// set random rating
arenateam->SetRatingForAll(urand(1500, 2700));
// set random emblem
uint32 backgroundColor = urand(0xFF000000, 0xFFFFFFFF);
uint32 emblemStyle = urand(0, 5);
uint32 emblemColor = urand(0xFF000000, 0xFFFFFFFF);
uint32 borderStyle = urand(0, 5);
uint32 borderColor = urand(0xFF000000, 0xFFFFFFFF);
arenateam->SetEmblem(backgroundColor, emblemStyle, emblemColor, borderStyle, borderColor);
// set random kills (wip)
//arenateam->SetStats(STAT_TYPE_GAMES_WEEK, urand(0, 30));
//arenateam->SetStats(STAT_TYPE_WINS_WEEK, urand(0, arenateam->GetStats().games_week));
//arenateam->SetStats(STAT_TYPE_GAMES_SEASON, urand(arenateam->GetStats().games_week, arenateam->GetStats().games_week * 5));
//arenateam->SetStats(STAT_TYPE_WINS_SEASON, urand(arenateam->GetStats().wins_week, arenateam->GetStats().games
arenateam->SaveToDB();
sArenaTeamMgr->AddArenaTeam(arenateam);
sPlayerbotAIConfig->randomBotArenaTeams.push_back(arenateam->GetId());
}
LOG_INFO("playerbots", "{} random bot arena teams available", arenaTeamNumber);
}
std::string const RandomPlayerbotFactory::CreateRandomArenaTeamName()
{
std::string arenaTeamName = "";
QueryResult result = CharacterDatabase.Query("SELECT MAX(name_id) FROM playerbots_arena_team_names");
if (!result)
{
LOG_ERROR("playerbots", "No more names left for random arena teams");
return std::move(arenaTeamName);
}
Field* fields = result->Fetch();
uint32 maxId = fields[0].Get<uint32>();
uint32 id = urand(0, maxId);
result = CharacterDatabase.Query("SELECT n.name FROM playerbots_arena_team_names n LEFT OUTER JOIN arena_team e ON e.name = n.name "
"WHERE e.arenateamid IS NULL AND n.name_id >= {} LIMIT 1", id);
if (!result)
{
LOG_ERROR("playerbots", "No more names left for random arena teams");
return std::move(arenaTeamName);
}
fields = result->Fetch();
arenaTeamName = fields[0].Get<std::string>();
return std::move(arenaTeamName);
}

View File

@@ -0,0 +1,36 @@
/*
* 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.
*/
#ifndef _PLAYERBOT_RANDOMPLAYERBOTFACTORY_H
#define _PLAYERBOT_RANDOMPLAYERBOTFACTORY_H
#include "Common.h"
#include <map>
#include <unordered_map>
class Player;
class WorldSession;
class RandomPlayerbotFactory
{
public:
RandomPlayerbotFactory(uint32 accountId);
virtual ~RandomPlayerbotFactory() { }
Player* CreateRandomBot(WorldSession* session, uint8 cls, std::unordered_map<uint8, std::vector<std::string>> names);
static void CreateRandomBots();
static void CreateRandomGuilds();
static void CreateRandomArenaTeams();
static std::string const CreateRandomGuildName();
private:
std::string const CreateRandomBotName(uint8 gender);
static std::string const CreateRandomArenaTeamName();
uint32 accountId;
static std::map<uint8, std::vector<uint8>> availableRaces;
};
#endif

2242
src/RandomPlayerbotMgr.cpp Normal file

File diff suppressed because it is too large Load Diff

134
src/RandomPlayerbotMgr.h Normal file
View File

@@ -0,0 +1,134 @@
/*
* 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.
*/
#ifndef _PLAYERBOT_RANDOMPLAYERBOTMGR_H
#define _PLAYERBOT_RANDOMPLAYERBOTMGR_H
#include "PlayerbotMgr.h"
class ChatHandler;
class PerformanceMonitorOperation;
class WorldLocation;
class CachedEvent
{
public:
CachedEvent() : value(0), lastChangeTime(0), validIn(0), data("") { }
CachedEvent(const CachedEvent& other) : value(other.value), lastChangeTime(other.lastChangeTime), validIn(other.validIn), data(other.data) { }
CachedEvent(uint32 value, uint32 lastChangeTime, uint32 validIn, std::string const data = "") : value(value), lastChangeTime(lastChangeTime), validIn(validIn), data(data) { }
bool IsEmpty() { return !lastChangeTime; }
public:
uint32 value;
uint32 lastChangeTime;
uint32 validIn;
std::string data;
};
class RandomPlayerbotMgr : public PlayerbotHolder
{
public:
RandomPlayerbotMgr();
virtual ~RandomPlayerbotMgr();
static RandomPlayerbotMgr* instance()
{
static RandomPlayerbotMgr instance;
return &instance;
}
void LogPlayerLocation();
void UpdateAIInternal(uint32 elapsed, bool minimal = false) override;
public:
uint32 activeBots = 0;
static bool HandlePlayerbotConsoleCommand(ChatHandler* handler, char const* args);
bool IsRandomBot(Player* bot);
bool IsRandomBot(ObjectGuid::LowType bot);
void Randomize(Player* bot);
void RandomizeFirst(Player* bot);
void IncreaseLevel(Player* bot);
void ScheduleTeleport(uint32 bot, uint32 time = 0);
void ScheduleChangeStrategy(uint32 bot, uint32 time = 0);
void HandleCommand(uint32 type, std::string const text, Player* fromPlayer);
std::string const HandleRemoteCommand(std::string const request);
void OnPlayerLogout(Player* player);
void OnPlayerLogin(Player* player);
void OnPlayerLoginError(uint32 bot);
Player* GetRandomPlayer();
std::vector<Player*> GetPlayers() { return players; };
PlayerBotMap GetAllBots() { return playerBots; };
void PrintStats();
double GetBuyMultiplier(Player* bot);
double GetSellMultiplier(Player* bot);
void AddTradeDiscount(Player* bot, Player* master, int32 value);
void SetTradeDiscount(Player* bot, Player* master, uint32 value);
uint32 GetTradeDiscount(Player* bot, Player* master);
void Refresh(Player* bot);
void RandomTeleportForLevel(Player* bot);
void RandomTeleportForRpg(Player* bot);
uint32 GetMaxAllowedBotCount();
bool ProcessBot(Player* player);
void Revive(Player* player);
void ChangeStrategy(Player* player);
uint32 GetValue(Player* bot, std::string const type);
uint32 GetValue(uint32 bot, std::string const type);
std::string const GetData(uint32 bot, std::string const type);
void SetValue(uint32 bot, std::string const type, uint32 value, std::string const data = "");
void SetValue(Player* bot, std::string const type, uint32 value, std::string const data = "");
void Remove(Player* bot);
ObjectGuid const GetBattleMasterGUID(Player* bot, BattlegroundTypeId bgTypeId);
CreatureData const* GetCreatureDataByEntry(uint32 entry);
void LoadBattleMastersCache();
std::map<uint32, std::map<uint32, std::map<TeamId, bool>>> NeedBots;
std::map<uint32, std::map<uint32, std::map<TeamId, uint32>>> BgBots;
std::map<uint32, std::map<uint32, std::map<TeamId, uint32>>> VisualBots;
std::map<uint32, std::map<uint32, std::map<TeamId, uint32>>> BgPlayers;
std::map<uint32, std::map<uint32, std::map<TeamId, std::map<TeamId, uint32>>>> ArenaBots;
std::map<uint32, std::map<uint32, std::map<uint32, uint32>>> Rating;
std::map<uint32, std::map<uint32, std::map<uint32, uint32>>> Supporters;
std::map<TeamId, std::vector<uint32>> LfgDungeons;
void CheckBgQueue();
void CheckLfgQueue();
void CheckPlayers();
std::map<TeamId, std::map<BattlegroundTypeId, std::vector<uint32>>> getBattleMastersCache() { return BattleMastersCache; }
protected:
void OnBotLoginInternal(Player* const bot) override;
private:
uint32 GetEventValue(uint32 bot, std::string const event);
std::string const GetEventData(uint32 bot, std::string const event);
uint32 SetEventValue(uint32 bot, std::string const event, uint32 value, uint32 validIn, std::string const data = "");
std::list<uint32> GetBots();
std::vector<uint32> GetBgBots(uint32 bracket);
time_t BgCheckTimer;
time_t LfgCheckTimer;
time_t PlayersCheckTimer;
uint32 AddRandomBots();
bool ProcessBot(uint32 bot);
void ScheduleRandomize(uint32 bot, uint32 time);
void RandomTeleport(Player* bot);
void RandomTeleport(Player* bot, std::vector<WorldLocation>& locs, bool hearth = false);
uint32 GetZoneLevel(uint16 mapId, float teleX, float teleY, float teleZ);
void PrepareTeleportCache();
typedef void(RandomPlayerbotMgr::*ConsoleCommandHandler)(Player*);
std::vector<Player*> players;
uint32 processTicks;
std::map<uint8, std::vector<WorldLocation>> locsPerLevelCache;
std::map<uint32, std::vector<WorldLocation>> rpgLocsCache;
std::map<uint32, std::map<uint32, std::vector<WorldLocation>>> rpgLocsCacheLevel;
std::map<TeamId, std::map<BattlegroundTypeId, std::vector<uint32>>> BattleMastersCache;
std::map<uint32, std::map<std::string, CachedEvent>> eventCache;
std::list<uint32> currentBots;
uint32 bgBotsCount;
uint32 playersLevel;
PerformanceMonitorOperation* totalPmo;
};
#define sRandomPlayerbotMgr RandomPlayerbotMgr::instance()
#endif

72
src/ServerFacade.cpp Normal file
View File

@@ -0,0 +1,72 @@
/*
* 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 "ServerFacade.h"
#include "Playerbots.h"
#include "TargetedMovementGenerator.h"
float ServerFacade::GetDistance2d(Unit* unit, WorldObject* wo)
{
ASSERT_NOTNULL(unit);
ASSERT_NOTNULL(wo);
float dist = unit->GetDistance2d(wo);
return std::round(dist * 10.0f) / 10.0f;
}
float ServerFacade::GetDistance2d(Unit *unit, float x, float y)
{
float dist = unit->GetDistance2d(x, y);
return std::round(dist * 10.0f) / 10.0f;
}
bool ServerFacade::IsDistanceLessThan(float dist1, float dist2)
{
return dist1 - dist2 < sPlayerbotAIConfig->targetPosRecalcDistance;
}
bool ServerFacade::IsDistanceGreaterThan(float dist1, float dist2)
{
return dist1 - dist2 > sPlayerbotAIConfig->targetPosRecalcDistance;
}
bool ServerFacade::IsDistanceGreaterOrEqualThan(float dist1, float dist2)
{
return !IsDistanceLessThan(dist1, dist2);
}
bool ServerFacade::IsDistanceLessOrEqualThan(float dist1, float dist2)
{
return !IsDistanceGreaterThan(dist1, dist2);
}
void ServerFacade::SetFacingTo(Player* bot, WorldObject* wo, bool force)
{
float angle = bot->GetAngle(wo);
if (!force && bot->isMoving())
bot->SetFacingTo(bot->GetAngle(wo));
else
{
bot->SetOrientation(angle);
bot->SendMovementFlagUpdate();
}
}
Unit* ServerFacade::GetChaseTarget(Unit* target)
{
MovementGenerator* movementGen = target->GetMotionMaster()->top();
if (movementGen && movementGen->GetMovementGeneratorType() == CHASE_MOTION_TYPE)
{
if (target->GetTypeId() == TYPEID_PLAYER)
{
return static_cast<ChaseMovementGenerator<Player> const*>(movementGen)->GetTarget();
}
else
{
return static_cast<ChaseMovementGenerator<Creature> const*>(movementGen)->GetTarget();
}
}
return nullptr;
}

40
src/ServerFacade.h Normal file
View File

@@ -0,0 +1,40 @@
/*
* 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.
*/
#ifndef _PLAYERBOT_SERVERFACADE_H
#define _PLAYERBOT_SERVERFACADE_H
#include "Common.h"
class Player;
class Unit;
class WorldObject;
class ServerFacade
{
public:
ServerFacade() { };
virtual ~ServerFacade() { };
static ServerFacade* instance()
{
static ServerFacade instance;
return &instance;
}
public:
float GetDistance2d(Unit* unit, WorldObject* wo);
float GetDistance2d(Unit* unit, float x, float y);
bool IsDistanceLessThan(float dist1, float dist2);
bool IsDistanceGreaterThan(float dist1, float dist2);
bool IsDistanceGreaterOrEqualThan(float dist1, float dist2);
bool IsDistanceLessOrEqualThan(float dist1, float dist2);
void SetFacingTo(Player* bot, WorldObject* wo, bool force = false);
Unit* GetChaseTarget(Unit* target);
};
#define sServerFacade ServerFacade::instance()
#endif

517
src/Talentspec.cpp Normal file
View File

@@ -0,0 +1,517 @@
/*
* 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 "Talentspec.h"
#include "Event.h"
#include "Playerbots.h"
uint32 TalentSpec::TalentListEntry::tabPage() const
{
return talentTabInfo->TalentTabID == 41 ? 1 : talentTabInfo->tabpage;
}
//Checks a talent link on basic validity.
bool TalentSpec::CheckTalentLink(std::string const link, std::ostringstream* out)
{
std::string const validChar = "-";
std::string const validNums = "012345";
uint32 nums = 0;
for (char const& c : link)
{
if (validChar.find(c) == std::string::npos && validNums.find(c) == std::string::npos)
{
*out << "talent link is invalid. Must be in format 0-0-0";
return false;
}
if (validNums.find(c) != std::string::npos)
nums++;
}
if (nums == 0)
{
*out << "talent link is invalid. Needs atleast one number.";
return false;
}
return true;
}
uint32 TalentSpec::LeveltoPoints(uint32 level) const
{
uint32 talentPointsForLevel = level < 10 ? 0 : level - 9;
return uint32(talentPointsForLevel * sWorld->getRate(RATE_TALENT));
}
uint32 TalentSpec::PointstoLevel(uint32 points) const
{
return uint32(ceil(points / sWorld->getRate(RATE_TALENT))) + 9;
}
TalentSpec::TalentSpec(uint32 classMask)
{
GetTalents(classMask);
}
TalentSpec::TalentSpec(TalentSpec* base, std::string const link)
{
talents = base->talents;
ReadTalents(link);
}
TalentSpec::TalentSpec(Player* bot)
{
GetTalents(bot->getClassMask());
ReadTalents(bot);
}
TalentSpec::TalentSpec(Player* bot, std::string const link)
{
GetTalents(bot->getClassMask());
ReadTalents(link);
}
//Check the talentspec for errors.
bool TalentSpec::CheckTalents(uint32 level, std::ostringstream* out)
{
for (auto& entry : talents)
{
if (entry.rank > entry.maxRank)
{
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(entry.talentInfo->RankID[0]);
*out << "spec is not for this class. " << spellInfo->SpellName[0] << " has " << (entry.rank - entry.maxRank) << " points above max rank.";
return false;
}
if (entry.rank > 0 && entry.talentInfo->DependsOn)
{
if (sTalentStore.LookupEntry(entry.talentInfo->DependsOn))
{
bool found = false;
SpellInfo const* spellInfodep = nullptr;
for (auto& dep : talents)
if (dep.talentInfo->TalentID == entry.talentInfo->DependsOn)
{
spellInfodep = sSpellMgr->GetSpellInfo(dep.talentInfo->RankID[0]);
if (dep.rank >= entry.talentInfo->DependsOnRank)
found = true;
}
if (!found)
{
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(entry.talentInfo->RankID[0]);
*out << "spec is invalid. Talent:" << spellInfo->SpellName[0] << " needs: " << spellInfodep->SpellName[0] << " at rank: " << entry.talentInfo->DependsOnRank;
return false;
}
}
}
}
for (uint8 i = 0; i < 3; i++)
{
std::vector<TalentListEntry> talentTree = GetTalentTree(i);
uint32 points = 0;
for (auto& entry : talentTree)
{
if (entry.rank > 0 && entry.talentInfo->Row * 5 > points)
{
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(entry.talentInfo->RankID[0]);
*out << "spec is is invalid. Talent " << spellInfo->SpellName[0] << " is selected with only " << points << " in row below it.";
return false;
}
points += entry.rank;
}
}
if (points > LeveltoPoints(level))
{
*out << "spec is for a higher level. (" << PointstoLevel(points) << ")";
return false;
}
return true;
}
//Set the talents for the bots to the current spec.
void TalentSpec::ApplyTalents(Player* bot, std::ostringstream* out)
{
for (auto& entry : talents)
for (uint8 rank = 0; rank < MAX_TALENT_RANK; ++rank)
{
uint32 spellId = entry.talentInfo->RankID[rank];
if (!spellId)
continue;
if (bot->HasSpell(spellId) && entry.rank - 1 != rank)
{
bot->removeSpell(spellId, false, false);
}
else if (!bot->HasSpell(spellId) && entry.rank - 1 == rank)
{
bot->learnSpell(spellId);
}
}
}
//Returns a base talentlist for a class.
void TalentSpec::GetTalents(uint32 classMask)
{
TalentListEntry entry;
for (uint32 i = 0; i < sTalentStore.GetNumRows(); ++i)
{
TalentEntry const* talentInfo = sTalentStore.LookupEntry(i);
if (!talentInfo)
continue;
TalentTabEntry const* talentTabInfo = sTalentTabStore.LookupEntry(talentInfo->TalentTab);
if (!talentTabInfo)
continue;
if ((classMask & talentTabInfo->ClassMask) == 0)
continue;
entry.entry = i;
entry.rank = 0;
entry.talentInfo = talentInfo;
entry.talentTabInfo = talentTabInfo;
for (uint8 rank = 0; rank < MAX_TALENT_RANK; ++rank)
{
uint32 spellId = talentInfo->RankID[rank];
if (!spellId)
continue;
entry.maxRank = rank + 1;
}
talents.push_back(entry);
}
SortTalents(talents, SORT_BY_DEFAULT);
}
//Sorts a talent list by page, row, column.
bool sortTalentMap(TalentSpec::TalentListEntry i, TalentSpec::TalentListEntry j, uint32* tabSort)
{
uint32 itab = i.tabPage();
uint32 jtab = j.tabPage();
if (tabSort[itab] < tabSort[jtab])
return true;
if (tabSort[itab] > tabSort[jtab])
return false;
if (i.talentInfo->Row < j.talentInfo->Row)
return true;
if (i.talentInfo->Row > j.talentInfo->Row)
return false;
if (i.talentInfo->Col < j.talentInfo->Col)
return true;
return false;
}
void TalentSpec::SortTalents(uint32 sortBy)
{
SortTalents(talents, sortBy);
}
//Sort the talents.
void TalentSpec::SortTalents(std::vector<TalentListEntry>& talents, uint32 sortBy)
{
switch (sortBy)
{
case SORT_BY_DEFAULT:
{
uint32 tabSort[] = { 0, 1, 2 };
std::sort(talents.begin(), talents.end(), [&tabSort](TalentSpec::TalentListEntry i, TalentSpec::TalentListEntry j)
{
return sortTalentMap(i, j, tabSort);
});
break;
}
case SORT_BY_POINTS_TREE:
{
uint32 tabSort[] = { GetTalentPoints(talents, 0) * 100 - urand(0, 99), GetTalentPoints(talents, 1) * 100 - urand(0, 99), GetTalentPoints(talents, 2) * 100 - urand(0, 99) };
std::sort(talents.begin(), talents.end(), [&tabSort](TalentSpec::TalentListEntry i, TalentSpec::TalentListEntry j)
{
return sortTalentMap(i, j, tabSort);
});
break;
}
}
}
//Set the talent ranks to the current rank of the player.
void TalentSpec::ReadTalents(Player* bot)
{
for (auto& entry : talents)
for (uint8 rank = 0; rank < MAX_TALENT_RANK; ++rank)
{
uint32 spellId = entry.talentInfo->RankID[rank];
if (!spellId)
continue;
if (bot->HasSpell(spellId))
{
entry.rank = rank + 1;
points += 1;
}
}
}
//Set the talent ranks to the ranks of the link.
void TalentSpec::ReadTalents(std::string const link)
{
uint32 rank = 0;
uint32 pos = 0;
uint32 tab = 0;
std::string chr;
if (link.substr(pos, 1) == "-")
{
pos++;
tab++;
}
if (link.substr(pos, 1) == "-")
{
pos++;
tab++;
}
for (auto& entry : talents)
{
if (entry.tabPage() == tab)
{
chr = link.substr(pos, 1);
if (chr == " " || chr == "#")
break;
entry.rank = stoi(chr);
points += entry.rank;
pos++;
if (pos <= link.size() && link.substr(pos, 1) == "-")
{
pos++;
tab++;
}
if (pos <= link.size() && link.substr(pos, 1) == "-")
{
pos++;
tab++;
}
}
if (pos > link.size() - 1)
break;
}
}
//Returns only a specific tree from a talent list.
std::vector<TalentSpec::TalentListEntry> TalentSpec::GetTalentTree(uint32 tabpage)
{
std::vector<TalentListEntry> retList;
for (auto& entry : talents)
if (entry.tabPage() == tabpage)
retList.push_back(entry);
return std::move(retList);
}
uint32 TalentSpec::GetTalentPoints(int32 tabpage)
{
return GetTalentPoints(talents, tabpage);
};
//Counts the point in a talent list.
uint32 TalentSpec::GetTalentPoints(std::vector<TalentListEntry>& talents, int32 tabpage)
{
if (tabpage == -1)
return points;
uint32 tPoints = 0;
for (auto& entry : talents)
if (entry.tabPage() == tabpage)
tPoints = tPoints + entry.rank;
return tPoints;
}
//Generates a wow-head link from a talent list.
std::string const TalentSpec::GetTalentLink()
{
std::string link = "";
std::string treeLink[3];
uint32 points[3];
uint32 curPoints = 0;
for (uint8 i = 0; i < 3; i++)
{
points[i] = GetTalentPoints(i);
for (auto& entry : GetTalentTree(i))
{
curPoints += entry.rank;
treeLink[i] += std::to_string(entry.rank);
if (curPoints >= points[i])
{
curPoints = 0;
break;
}
}
}
link = treeLink[0];
if (treeLink[1] != "0" || treeLink[2] != "0")
link = link + "-" + treeLink[1];
if (treeLink[2] != "0")
link = link + "-" + treeLink[2];
return std::move(link);
}
uint32 TalentSpec::highestTree()
{
uint32 p1 = GetTalentPoints(0);
uint32 p2 = GetTalentPoints(1);
uint32 p3 = GetTalentPoints(2);
if (p1 > p2 && p1 > p3)
return 0;
if (p2 > p1 && p2 > p3)
return 1;
if (p3 > p1 && p3 > p2)
return 2;
if (p1 > p2 || p1 > p3)
return 0;
if (p2 > p3 || p2 > p1)
return 1;
return 0;
}
std::string const TalentSpec::FormatSpec(Player* bot)
{
uint8 cls = bot->getClass();
std::ostringstream out;
//out << chathelper:: specs[cls][highestTree()] << " (";
uint32 c0 = GetTalentPoints(0);
uint32 c1 = GetTalentPoints(1);
uint32 c2 = GetTalentPoints(2);
out << (c0 ? "|h|cff00ff00" : "") << c0 << "|h|cffffffff/";
out << (c1 ? "|h|cff00ff00" : "") << c1 << "|h|cffffffff/";
out << (c2 ? "|h|cff00ff00" : "") << c2 << "|h|cffffffff";
return out.str();
}
//Removes talentpoints to match the level
void TalentSpec::CropTalents(uint32 level)
{
if (points <= LeveltoPoints(level))
return;
SortTalents(talents, SORT_BY_POINTS_TREE);
uint32 points = 0;
for (auto& entry : talents)
{
if (points + entry.rank > LeveltoPoints(level))
entry.rank = std::max(0u, LeveltoPoints(level) - points);
points += entry.rank;
}
SortTalents(talents, SORT_BY_DEFAULT);
}
//Substracts ranks. Follows the sorting of the newList.
std::vector<TalentSpec::TalentListEntry> TalentSpec::SubTalentList(std::vector<TalentListEntry>& oldList, std::vector<TalentListEntry>& newList, uint32 reverse = SUBSTRACT_OLD_NEW)
{
std::vector<TalentSpec::TalentListEntry> deltaList = newList;
for (auto& newentry : deltaList)
for (auto& oldentry : oldList)
if (oldentry.entry == newentry.entry)
{
if (reverse == ABSOLUTE_DIST)
newentry.rank = std::abs(int32(newentry.rank - oldentry.rank));
else if (reverse == ADDED_POINTS || reverse == REMOVED_POINTS)
newentry.rank = std::max(0u, (newentry.rank - oldentry.rank) * (reverse / 2));
else
newentry.rank = (newentry.rank - oldentry.rank) * reverse;
}
return deltaList;
}
bool TalentSpec::isEarlierVersionOf(TalentSpec& newSpec)
{
for (auto& newentry : newSpec.talents)
for (auto& oldentry : talents)
if (oldentry.entry == newentry.entry)
if (oldentry.rank > newentry.rank)
return false;
return true;
}
//Modifies current talents towards new talents up to a maxium of points.
void TalentSpec::ShiftTalents(TalentSpec* currentSpec, uint32 level)
{
uint32 currentPoints = currentSpec->GetTalentPoints();
if (points >= LeveltoPoints(level)) //We have no more points to spend. Better reset and crop
{
CropTalents(level);
return;
}
SortTalents(SORT_BY_POINTS_TREE); //Apply points first to the largest new tree.
std::vector<TalentSpec::TalentListEntry> deltaList = SubTalentList(currentSpec->talents, talents);
for (auto& entry : deltaList)
{
if (entry.rank < 0) //We have to remove talents. Might as well reset and crop the new list.
{
CropTalents(level);
return;
}
}
//Start from the current spec.
talents = currentSpec->talents;
for (auto& entry : deltaList)
{
if (entry.rank + points > LeveltoPoints(level)) //Running out of points. Only apply what we have left.
entry.rank = std::max(0, std::abs(int32(LeveltoPoints(level) - points)));
for (auto& subentry : talents)
if (entry.entry == subentry.entry)
subentry.rank = subentry.rank + entry.rank;
points = points + entry.rank;
}
}

103
src/Talentspec.h Normal file
View File

@@ -0,0 +1,103 @@
/*
* 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.
*/
#ifndef _PLAYERBOT_TALENTSPEC_H
#define _PLAYERBOT_TALENTSPEC_H
#include "Action.h"
struct TalentEntry;
struct TalentTabEntry;
#define SORT_BY_DEFAULT 0
#define SORT_BY_POINTS_TREE 1
#define ABSOLUTE_DIST 0
#define SUBSTRACT_OLD_NEW 1
#define SUBSTRACT_NEW_OLD -1
#define ADDED_POINTS 2
#define REMOVED_POINTS -2
class TalentSpec
{
public:
struct TalentListEntry
{
uint32 entry;
uint32 rank;
uint32 maxRank;
TalentEntry const* talentInfo;
TalentTabEntry const* talentTabInfo;
uint32 tabPage() const;
};
TalentSpec() { };
TalentSpec(uint32 classMask);
TalentSpec(TalentSpec* base, std::string const link);
TalentSpec(Player* bot);
TalentSpec(Player* bot, std::string const link);
uint32 points = 0;
std::vector<TalentListEntry> talents;
bool CheckTalentLink(std::string const link, std::ostringstream* out);
virtual bool CheckTalents(uint32 maxPoints, std::ostringstream* out);
void CropTalents(uint32 level);
void ShiftTalents(TalentSpec* oldTalents, uint32 level);
void ApplyTalents(Player* bot, std::ostringstream* out);
uint32 GetTalentPoints(std::vector<TalentListEntry>& talents, int32 tabpage = -1);
uint32 GetTalentPoints(int32 tabpage = -1);
bool isEarlierVersionOf(TalentSpec& newSpec);
std::string const GetTalentLink();
uint32 highestTree();
std::string const FormatSpec(Player* bot);
protected:
uint32 LeveltoPoints(uint32 level) const;
uint32 PointstoLevel(uint32 points) const;
void GetTalents(uint32 classMask);
void SortTalents(std::vector<TalentListEntry>& talents, uint32 sortBy);
void SortTalents(uint32 sortBy);
void ReadTalents(Player* bot);
void ReadTalents(std::string const link);
std::vector<TalentListEntry> GetTalentTree(uint32 tabpage);
std::vector<TalentListEntry> SubTalentList(std::vector<TalentListEntry>& oldList, std::vector<TalentListEntry>& newList, uint32 reverse);
};
class TalentPath
{
public:
TalentPath(uint32 pathId, std::string const pathName, uint32 pathProbability)
{
id = pathId;
name = pathName;
probability = pathProbability;
};
uint32 id = 0;
std::string name = "";
uint32 probability = 100;
std::vector<TalentSpec> talentSpec;
};
class ClassSpecs
{
public:
ClassSpecs() { };
ClassSpecs(uint32 specsClassMask)
{
classMask = specsClassMask;
baseSpec = TalentSpec(specsClassMask);
}
uint32 classMask = 0;
TalentSpec baseSpec;
std::vector<TalentPath> talentPath;
};
#endif

4343
src/TravelMgr.cpp Normal file

File diff suppressed because it is too large Load Diff

988
src/TravelMgr.h Normal file
View File

@@ -0,0 +1,988 @@
/*
* 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.
*/
#ifndef _PLAYERBOT_TRAVELMGR_H
#define _PLAYERBOT_TRAVELMGR_H
#include "AiObject.h"
#include "CreatureData.h"
#include "GameObject.h"
#include "GridDefines.h"
#include "PlayerbotAIConfig.h"
#include <boost/functional/hash.hpp>
#include <random>
class GuidPosition;
class ObjectGuid;
class Quest;
class Player;
class PlayerbotAI;
struct QuestStatusData;
namespace G3D
{
class Vector2;
class Vector3;
class Vector4;
}
//Constructor types for WorldPosition
enum WorldPositionConst
{
WP_RANDOM = 0,
WP_CENTROID = 1,
WP_MEAN_CENTROID = 2,
WP_CLOSEST = 3
};
enum TravelState
{
TRAVEL_STATE_IDLE = 0,
TRAVEL_STATE_TRAVEL_PICK_UP_QUEST = 1,
TRAVEL_STATE_WORK_PICK_UP_QUEST = 2,
TRAVEL_STATE_TRAVEL_DO_QUEST = 3,
TRAVEL_STATE_WORK_DO_QUEST = 4,
TRAVEL_STATE_TRAVEL_HAND_IN_QUEST = 5,
TRAVEL_STATE_WORK_HAND_IN_QUEST = 6,
TRAVEL_STATE_TRAVEL_RPG = 7,
TRAVEL_STATE_TRAVEL_EXPLORE = 8,
MAX_TRAVEL_STATE
};
enum TravelStatus
{
TRAVEL_STATUS_NONE = 0,
TRAVEL_STATUS_PREPARE = 1,
TRAVEL_STATUS_TRAVEL = 2,
TRAVEL_STATUS_WORK = 3,
TRAVEL_STATUS_COOLDOWN = 4,
TRAVEL_STATUS_EXPIRED = 5,
MAX_TRAVEL_STATUS
};
class QuestTravelDestination;
//A quest destination container for quick lookup of all destinations related to a quest.
struct QuestContainer
{
std::vector<QuestTravelDestination*> questGivers;
std::vector<QuestTravelDestination*> questTakers;
std::vector<QuestTravelDestination*> questObjectives;
};
typedef std::pair<int32, int32> mGridCoord;
//Extension of WorldLocation with distance functions.
class WorldPosition : public WorldLocation
{
public:
//Constructors
WorldPosition() : WorldLocation() { };
WorldPosition(WorldLocation const& loc) : WorldLocation(loc) { }
WorldPosition(WorldPosition const& pos) : WorldLocation(pos), visitors(pos.visitors) { }
WorldPosition(std::string const str);
WorldPosition(uint32 mapid, float x, float y, float z = 0.f, float orientation = 0.f) : WorldLocation(mapid, x, y, z, orientation) { }
WorldPosition(uint32 mapId, const Position& pos);
WorldPosition(WorldObject const* wo);
WorldPosition(std::vector<WorldPosition*> list, WorldPositionConst conType);
WorldPosition(std::vector<WorldPosition> list, WorldPositionConst conType);
WorldPosition(uint32 mapid, GridCoord grid);
WorldPosition(uint32 mapid, CellCoord cell);
WorldPosition(uint32 mapid, mGridCoord grid);
//Setters
void set(const WorldLocation& pos);
void setMapId(uint32 id);
void setX(float x);
void setY(float y);
void setZ(float z);
void setO(float o);
void addVisitor()
{
++visitors;
}
void remVisitor()
{
--visitors;
}
//Getters
operator bool() const;
bool operator==(WorldPosition const& p1);
bool operator!=(WorldPosition const& p1);
WorldPosition& operator+=(WorldPosition const& p1);
WorldPosition& operator-=(WorldPosition const& p1);
uint32 getMapId();
float getX();
float getY();
float getZ();
float getO();
G3D::Vector3 getVector3();
std::string const print();
std::string const to_string();
void printWKT(std::vector<WorldPosition> points, std::ostringstream& out, uint32 dim = 0, bool loop = false);
void printWKT(std::ostringstream& out) { printWKT({ *this }, out); }
uint32 getVisitors() { return visitors; }
bool isOverworld();
bool isInWater();
bool isUnderWater();
WorldPosition relPoint(WorldPosition* center);
WorldPosition offset(WorldPosition* center);
float size();
//Slow distance function using possible map transfers.
float distance(WorldPosition* center);
float distance(WorldPosition center)
{
return distance(&center);
}
float fDist(WorldPosition* center);
float fDist(WorldPosition center)
{
return fDist(&center);
}
template<class T>
std::pair<T, WorldPosition> closest(std::vector<std::pair<T, WorldPosition>> list)
{
return *std::min_element(list.begin(), list.end(), [this](std::pair<T, WorldPosition> i, std::pair<T, WorldPosition> j)
{
return this->distance(i.second) < this->distance(j.second);
});
}
template<class T>
std::pair<T, WorldPosition> closest(std::vector<T> list)
{
return closest(GetPosList(list));
}
//Returns the closest point from the list.
WorldPosition* closest(std::vector<WorldPosition*> list)
{
return *std::min_element(list.begin(), list.end(), [this](WorldPosition* i, WorldPosition* j) { return this->distance(i) < this->distance(j); });
}
WorldPosition closest(std::vector<WorldPosition> list)
{
return *std::min_element(list.begin(), list.end(), [this](WorldPosition i, WorldPosition j) { return this->distance(i) < this->distance(j); });
}
//Quick square distance in 2d plane.
float sqDistance2d(WorldPosition center)
{
return (getX() - center.getX()) * (getX() - center.getX()) + (getY() - center.getY()) * (getY() - center.getY());
}
//Quick square distance calculation without map check. Used for getting the minimum distant points.
float sqDistance(WorldPosition center)
{
return (getX() - center.getX()) * (getX() - center.getX()) + (getY() - center.getY()) *
(getY() - center.getY()) + (getZ() - center.getZ()) * (getZ() - center.getZ());
}
float sqDistance2d(WorldPosition* center)
{
return (getX() - center->getX()) * (getX() - center->getX()) + (getY() - center->getY()) * (getY() - center->getY());
}
float sqDistance(WorldPosition* center)
{
return (getX() - center->getX()) * (getX() - center->getX()) + (getY() - center->getY()) *
(getY() - center->getY()) + (getZ() - center->getZ()) * (getZ() - center->getZ());
}
//Returns the closest point of the list. Fast but only works for the same map.
WorldPosition* closestSq(std::vector<WorldPosition*> list)
{
return *std::min_element(list.begin(), list.end(), [this](WorldPosition* i, WorldPosition* j) { return this->sqDistance(i) < this->sqDistance(j); });
}
WorldPosition closestSq(std::vector<WorldPosition> list)
{
return *std::min_element(list.begin(), list.end(), [this](WorldPosition i, WorldPosition j) { return this->sqDistance(i) < this->sqDistance(j); });
}
float getAngleTo(WorldPosition endPos)
{
float ang = atan2(endPos.getY() - getY(), endPos.getX() - getX());
return (ang >= 0) ? ang : 2 * static_cast<float>(M_PI) + ang;
}
float getAngleBetween(WorldPosition dir1, WorldPosition dir2)
{
return abs(getAngleTo(dir1) - getAngleTo(dir2));
}
WorldPosition lastInRange(std::vector<WorldPosition> list, float minDist = -1.f, float maxDist = -1.f);
WorldPosition firstOutRange(std::vector<WorldPosition> list, float minDist = -1.f, float maxDist = -1.f);
float mSign(WorldPosition* p1, WorldPosition* p2)
{
return(getX() - p2->getX()) * (p1->getY() - p2->getY()) - (p1->getX() - p2->getX()) * (getY() - p2->getY());
}
bool isInside(WorldPosition* p1, WorldPosition* p2, WorldPosition* p3);
//Map functions. Player independent.
MapEntry const* getMapEntry();
uint32 getInstanceId();
Map* getMap();
const float getHeight();
std::set<Transport*> getTransports(uint32 entry = 0);
GridCoord getGridCoord() { return Acore::ComputeGridCoord(getX(), getY()); };
std::vector<GridCoord> getGridCoord(WorldPosition secondPos);
std::vector<WorldPosition> fromGridCoord(GridCoord GridCoord);
CellCoord getCellCoord() { return Acore::ComputeCellCoord(getX(), getY()); }
std::vector<WorldPosition> fromCellCoord(CellCoord cellCoord);
std::vector<WorldPosition> gridFromCellCoord(CellCoord cellCoord);
mGridCoord getmGridCoord()
{
return std::make_pair((int32)(CENTER_GRID_ID - getX() / SIZE_OF_GRIDS), (int32)(CENTER_GRID_ID - getY() / SIZE_OF_GRIDS));
}
std::vector<mGridCoord> getmGridCoords(WorldPosition secondPos);
std::vector<WorldPosition> frommGridCoord(mGridCoord GridCoord);
void loadMapAndVMap(uint32 mapId, uint8 x, uint8 y);
void loadMapAndVMap()
{
loadMapAndVMap(getMapId(), getmGridCoord().first, getmGridCoord().second);
}
void loadMapAndVMaps(WorldPosition secondPos);
//Display functions
WorldPosition getDisplayLocation();
float getDisplayX()
{
return getDisplayLocation().getY() * -1.0;
}
float getDisplayY()
{
return getDisplayLocation().getX();
}
uint16 getAreaId();
AreaTableEntry const* getArea();
std::string const getAreaName(bool fullName = true, bool zoneName = false);
std::vector<WorldPosition> fromPointsArray(std::vector<G3D::Vector3> path);
//Pathfinding
std::vector<WorldPosition> getPathStepFrom(WorldPosition startPos, Unit* bot);
std::vector<WorldPosition> getPathFromPath(std::vector<WorldPosition> startPath, Unit* bot, uint8 maxAttempt = 40);
std::vector<WorldPosition> getPathFrom(WorldPosition startPos, Unit* bot)
{
return getPathFromPath({ startPos }, bot);
}
std::vector<WorldPosition> getPathTo(WorldPosition endPos, Unit* bot)
{
return endPos.getPathFrom(*this, bot);
}
bool isPathTo(std::vector<WorldPosition> path, float maxDistance = sPlayerbotAIConfig->targetPosRecalcDistance) { return !path.empty() && distance(path.back()) < maxDistance; };
bool cropPathTo(std::vector<WorldPosition>& path, float maxDistance = sPlayerbotAIConfig->targetPosRecalcDistance);
bool canPathTo(WorldPosition endPos, Unit* bot) { return endPos.isPathTo(getPathTo(endPos, bot)); }
float getPathLength(std::vector<WorldPosition> points)
{
float dist = 0.f;
for (auto& p : points)
{
if (&p == &points.front())
dist = 0.f;
else
dist += std::prev(&p, 1)->distance(p);
}
return dist;
}
bool GetReachableRandomPointOnGround(Player* bot, float radius, bool randomRange = true);
uint32 getUnitsAggro(GuidVector& units, Player* bot);
//Creatures
std::vector<CreatureData const*> getCreaturesNear(float radius = 0, uint32 entry = 0);
//GameObjects
std::vector<GameObjectData const*> getGameObjectsNear(float radius = 0, uint32 entry = 0);
private:
uint32 visitors = 0;
};
inline ByteBuffer& operator<<(ByteBuffer& b, WorldPosition& guidP)
{
b << guidP.getMapId();
b << guidP.getX();
b << guidP.getY();
b << guidP.getZ();
b << guidP.getO();
b << guidP.getVisitors();
return b;
}
inline ByteBuffer& operator>>(ByteBuffer& b, WorldPosition& g)
{
uint32 mapid;
float coord_x;
float coord_y;
float coord_z;
float orientation;
uint32 visitors = 0;
b >> mapid;
b >> coord_x;
b >> coord_y;
b >> coord_z;
b >> orientation;
b >> visitors;
return b;
}
//Generic creature finder
class FindPointCreatureData
{
public:
FindPointCreatureData(WorldPosition point1 = WorldPosition(), float radius1 = 0, uint32 entry1 = 0) { point = point1; radius = radius1; entry = entry1; }
void operator()(CreatureData const& dataPair);
std::vector<CreatureData const*> GetResult() const { return data; };
private:
WorldPosition point;
float radius;
uint32 entry;
std::vector<CreatureData const*> data;
};
//Generic gameObject finder
class FindPointGameObjectData
{
public:
FindPointGameObjectData(WorldPosition point1 = WorldPosition(), float radius1 = 0, uint32 entry1 = 0) { point = point1; radius = radius1; entry = entry1; }
void operator()(GameObjectData const& dataPair);
std::vector<GameObjectData const*> GetResult() const { return data; };
private:
WorldPosition point;
float radius;
uint32 entry;
std::vector<GameObjectData const*> data;
};
class GuidPosition : public ObjectGuid, public WorldPosition
{
public:
GuidPosition() : ObjectGuid(), WorldPosition(), loadedFromDB(false) { }
GuidPosition(WorldObject* wo);
GuidPosition(CreatureData const& creData);
GuidPosition(GameObjectData const& goData);
CreatureTemplate const* GetCreatureTemplate();
GameObjectTemplate const* GetGameObjectTemplate();
WorldObject* GetWorldObject();
Creature* GetCreature();
Unit* GetUnit();
GameObject* GetGameObject();
Player* GetPlayer();
bool HasNpcFlag(NPCFlags flag);
bool isDead(); //For loaded grids check if the unit/object is unloaded/dead.
operator bool() const { return !IsEmpty(); }
bool operator==(ObjectGuid const& guid) const { return GetRawValue() == guid.GetRawValue(); }
bool operator!=(ObjectGuid const& guid) const { return GetRawValue() != guid.GetRawValue(); }
bool operator<(ObjectGuid const& guid) const { return GetRawValue() < guid.GetRawValue(); }
private:
bool loadedFromDB;
};
template<class T>
std::vector<std::pair<T, WorldPosition>> GetPosList(std::vector<T> oList)
{
std::vector<std::pair<T, WorldPosition>> retList;
for (auto& obj : oList)
retList.push_back(std::make_pair(obj, WorldPosition(obj)));
return std::move(retList);
};
template<class T>
std::vector<std::pair<T, WorldPosition>> GetPosVector(std::vector<T> oList)
{
std::vector<std::pair<T, WorldPosition>> retList;
for (auto& obj : oList)
retList.push_back(make_pair(obj, WorldPosition(obj)));
return std::move(retList);
};
class mapTransfer
{
public:
mapTransfer(WorldPosition pointFrom1, WorldPosition pointTo1, float portalLength1 = 0.1f)
: pointFrom(pointFrom1), pointTo(pointTo1), portalLength(portalLength1) { }
bool isFrom(WorldPosition point)
{
return point.getMapId() == pointFrom.getMapId();
}
bool isTo(WorldPosition point)
{
return point.getMapId() == pointTo.getMapId();
}
WorldPosition* getPointFrom()
{
return &pointFrom;
}
WorldPosition* getPointTo()
{
return &pointTo;
}
bool isUseful(WorldPosition point)
{
return isFrom(point) || isTo(point);
}
float distance(WorldPosition point)
{
return isUseful(point) ? (isFrom(point) ? point.distance(pointFrom) : point.distance(pointTo)) : 200000;
}
bool isUseful(WorldPosition start, WorldPosition end)
{
return isFrom(start) && isTo(end);
}
float distance(WorldPosition start, WorldPosition end)
{
return (isUseful(start, end) ? (start.distance(pointFrom) + portalLength + pointTo.distance(end)) : 200000);
}
float fDist(WorldPosition start, WorldPosition end);
private:
WorldPosition pointFrom;
WorldPosition pointTo;
float portalLength = 0.1f;
};
//A destination for a bot to travel to and do something.
class TravelDestination
{
public:
TravelDestination() { }
TravelDestination(float radiusMin1, float radiusMax1)
{
radiusMin = radiusMin1; radiusMax = radiusMax1;
}
TravelDestination(std::vector<WorldPosition*> points1, float radiusMin1, float radiusMax1)
{
points = points1; radiusMin = radiusMin1; radiusMax = radiusMax1;
}
void addPoint(WorldPosition* pos)
{
points.push_back(pos);
}
void setExpireDelay(uint32 delay)
{
expireDelay = delay;
}
void setCooldownDelay(uint32 delay)
{
cooldownDelay = delay;
}
void setMaxVisitors(uint32 maxVisitors1 = 0, uint32 maxVisitorsPerPoint1 = 0)
{
maxVisitors = maxVisitors1;
maxVisitorsPerPoint = maxVisitorsPerPoint1;
}
std::vector<WorldPosition*> getPoints(bool ignoreFull = false);
uint32 getExpireDelay() { return expireDelay; }
uint32 getCooldownDelay() { return cooldownDelay; }
void addVisitor()
{
++visitors;
}
void remVisitor()
{
--visitors;
}
uint32 getVisitors()
{
return visitors;
}
virtual Quest const* GetQuestTemplate() { return nullptr; }
virtual bool isActive(Player* bot) { return false; }
bool isFull(bool ignoreFull = false);
virtual std::string const getName() { return "TravelDestination"; }
virtual int32 getEntry() { return 0; }
virtual std::string const getTitle() { return "generic travel destination"; }
WorldPosition* nearestPoint(WorldPosition* pos);
float distanceTo(WorldPosition* pos) { return nearestPoint(pos)->distance(pos); }
bool onMap(WorldPosition* pos) { return nearestPoint(pos)->getMapId() == pos->getMapId(); }
virtual bool isIn(WorldPosition* pos, float radius = 0.f) { return onMap(pos) && distanceTo(pos) <= (radius? radius : radiusMin); }
virtual bool isOut(WorldPosition* pos, float radius = 0.f) { return !onMap(pos) || distanceTo(pos) > (radius? radius : radiusMax); }
float getRadiusMin() { return radiusMin; }
std::vector<WorldPosition*> touchingPoints(WorldPosition* pos);
std::vector<WorldPosition*> sortedPoints(WorldPosition* pos);
std::vector<WorldPosition*> nextPoint(WorldPosition* pos, bool ignoreFull = true);
protected:
std::vector<WorldPosition*> points;
float radiusMin = 0;
float radiusMax = 0;
uint32 visitors = 0;
uint32 maxVisitors = 0;
uint32 maxVisitorsPerPoint = 0;
uint32 expireDelay = 5 * 1000;
uint32 cooldownDelay = 60 * 1000;
};
//A travel target that is always inactive and jumps to cooldown.
class NullTravelDestination : public TravelDestination
{
public:
NullTravelDestination(uint32 cooldownDelay1 = 5 * 60 * 1000) : TravelDestination()
{
cooldownDelay = cooldownDelay1;
};
Quest const* GetQuestTemplate() override { return nullptr; }
bool isActive(Player* bot) override { return false; }
std::string const getName() override { return "NullTravelDestination"; }
std::string const getTitle() override { return "no destination"; }
bool isIn(WorldPosition* pos, float radius = 0.f) override { return true; }
bool isOut(WorldPosition* pos, float radius = 0.f) override { return false; }
};
//A travel target specifically related to a quest.
class QuestTravelDestination : public TravelDestination
{
public:
QuestTravelDestination(uint32 questId1, float radiusMin1, float radiusMax1);
Quest const* GetQuestTemplate() override { return questTemplate; }
bool isActive(Player* bot) override;
std::string const getName() override { return "QuestTravelDestination"; }
int32 getEntry() override { return 0; }
std::string const getTitle() override;
protected:
uint32 questId;
Quest const* questTemplate;
};
//A quest giver or taker.
class QuestRelationTravelDestination : public QuestTravelDestination
{
public:
QuestRelationTravelDestination(uint32 quest_id1, uint32 entry1, uint32 relation1, float radiusMin1, float radiusMax1) :
QuestTravelDestination(quest_id1, radiusMin1, radiusMax1)
{
entry = entry1;
relation = relation1;
}
bool isActive(Player* bot) override;
std::string const getName() override { return "QuestRelationTravelDestination"; }
int32 getEntry() override { return entry; }
std::string const getTitle() override;
virtual uint32 getRelation() { return relation; }
private:
uint32 relation;
int32 entry;
};
//A quest objective (creature/gameobject to grind/loot)
class QuestObjectiveTravelDestination : public QuestTravelDestination
{
public:
QuestObjectiveTravelDestination(uint32 quest_id1, uint32 entry1, uint32 objective1, float radiusMin1, float radiusMax1, uint32 itemId1 = 0) :
QuestTravelDestination(quest_id1, radiusMin1, radiusMax1)
{
objective = objective1;
entry = entry1;
itemId = itemId1;
}
bool isCreature();
uint32 ReqCreature();
uint32 ReqGOId();
uint32 ReqCount();
bool isActive(Player* bot) override;
std::string const getName() override { return "QuestObjectiveTravelDestination"; }
int32 getEntry() override { return entry; }
std::string const getTitle() override;
private:
uint32 objective;
uint32 entry;
uint32 itemId = 0;
};
//A location with rpg target(s) based on race and level
class RpgTravelDestination : public TravelDestination
{
public:
RpgTravelDestination(uint32 entry1, float radiusMin1, float radiusMax1) : TravelDestination(radiusMin1, radiusMax1)
{
entry = entry1;
}
bool isActive(Player* bot) override;
virtual CreatureTemplate const* GetCreatureTemplate();
std::string const getName() override { return "RpgTravelDestination"; }
int32 getEntry() override { return 0; }
std::string const getTitle() override;
protected:
uint32 entry;
};
//A location with zone exploration target(s)
class ExploreTravelDestination : public TravelDestination
{
public:
ExploreTravelDestination(uint32 areaId1, float radiusMin1, float radiusMax1) : TravelDestination(radiusMin1, radiusMax1)
{
areaId = areaId1;
}
bool isActive(Player* bot) override;
std::string const getName() override { return "ExploreTravelDestination"; }
int32 getEntry() override { return 0; }
std::string const getTitle() override;
virtual uint32 getAreaId() { return areaId; }
protected:
uint32 areaId;
};
//A location with zone exploration target(s)
class GrindTravelDestination : public TravelDestination
{
public:
GrindTravelDestination(int32 entry1, float radiusMin1, float radiusMax1) : TravelDestination(radiusMin1, radiusMax1)
{
entry = entry1;
}
bool isActive(Player* bot) override;
virtual CreatureTemplate const* GetCreatureTemplate();
std::string const getName() override { return "GrindTravelDestination"; }
int32 getEntry() override { return entry; }
std::string const getTitle() override;
protected:
int32 entry;
};
//A location with a boss
class BossTravelDestination : public TravelDestination
{
public:
BossTravelDestination(int32 entry1, float radiusMin1, float radiusMax1) : TravelDestination(radiusMin1, radiusMax1)
{
entry = entry1;
cooldownDelay = 1000;
}
bool isActive(Player* bot) override;
CreatureTemplate const* getCreatureTemplate();
std::string const getName() override { return "BossTravelDestination"; }
int32 getEntry() override { return entry; }
std::string const getTitle() override;
protected:
int32 entry;
};
//Current target and location for the bot to travel to.
//The flow is as follows:
//PREPARE (wait until no loot is near)
//TRAVEL (move towards target until close enough) (rpg and grind is disabled)
//WORK (grind/rpg until the target is no longer active) (rpg and grind is enabled on quest mobs)
//COOLDOWN (wait some time free to do what the bot wants)
//EXPIRE (if any of the above actions take too long pick a new target)
class TravelTarget : AiObject
{
public:
TravelTarget(PlayerbotAI* botAI) : AiObject(botAI), m_status(TRAVEL_STATUS_NONE), startTime(getMSTime()) { };
TravelTarget(PlayerbotAI* botAI, TravelDestination* tDestination1, WorldPosition* wPosition1) : AiObject(botAI), m_status(TRAVEL_STATUS_NONE), startTime(getMSTime())
{
setTarget(tDestination1, wPosition1);
}
~TravelTarget();
void setTarget(TravelDestination* tDestination1, WorldPosition* wPosition1, bool groupCopy1 = false);
void setStatus(TravelStatus status);
void setExpireIn(uint32 expireMs)
{
statusTime = getExpiredTime() + expireMs;
}
void incRetry(bool isMove)
{
if (isMove)
++moveRetryCount;
else
++extendRetryCount;
}
void setRetry(bool isMove, uint32 newCount = 0)
{
if (isMove)
moveRetryCount = newCount;
else
extendRetryCount = newCount;
}
void setForced(bool forced1)
{
forced = forced1;
}
void setRadius(float radius1)
{
radius = radius1;
}
void copyTarget(TravelTarget* target);
void addVisitors();
void releaseVisitors();
float distance(Player* bot);
WorldPosition* getPosition();
TravelDestination* getDestination();
uint32 getEntry()
{
if (!tDestination)
return 0;
return tDestination->getEntry();
}
PlayerbotAI* getAi()
{
return botAI;
}
uint32 getExpiredTime()
{
return getMSTime() - startTime;
}
uint32 getTimeLeft()
{
return statusTime - getExpiredTime();
}
uint32 getMaxTravelTime();
uint32 getRetryCount(bool isMove)
{
return isMove ? moveRetryCount: extendRetryCount;
}
bool isTraveling();
bool isActive();
bool isWorking();
bool isPreparing();
bool isMaxRetry(bool isMove)
{
return isMove ? (moveRetryCount > 5) : (extendRetryCount > 5);
}
TravelStatus getStatus()
{
return m_status;
}
TravelState getTravelState();
bool isGroupCopy()
{
return groupCopy;
}
bool isForced()
{
return forced;
}
protected:
TravelStatus m_status;
uint32 startTime;
uint32 statusTime = 0;
bool forced = false;
float radius = 0.f;
bool groupCopy = false;
bool visitor = true;
uint32 extendRetryCount = 0;
uint32 moveRetryCount = 0;
TravelDestination* tDestination = nullptr;
WorldPosition* wPosition = nullptr;
};
//General container for all travel destinations.
class TravelMgr
{
public:
TravelMgr() { };
static TravelMgr* instance()
{
static TravelMgr instance;
return &instance;
}
void Clear();
void LoadQuestTravelTable();
template <class D, class W, class URBG>
void weighted_shuffle(D first, D last, W first_weight, W last_weight, URBG&& g)
{
while (first != last && first_weight != last_weight)
{
std::discrete_distribution<int>dd(first_weight, last_weight);
auto i = dd(g);
if (i)
{
std::swap(*first, *std::next(first, i));
std::swap(*first_weight, *std::next(first_weight, i));
}
++first;
++first_weight;
}
}
std::vector<WorldPosition*> getNextPoint(WorldPosition* center, std::vector<WorldPosition*> points, uint32 amount = 1);
std::vector<WorldPosition> getNextPoint(WorldPosition center, std::vector<WorldPosition> points, uint32 amount = 1);
QuestStatusData* getQuestStatus(Player* bot, uint32 questId);
bool getObjectiveStatus(Player* bot, Quest const* pQuest, uint32 objective);
uint32 getDialogStatus(Player* pPlayer, int32 questgiver, Quest const* pQuest);
std::vector<TravelDestination*> getQuestTravelDestinations(Player* bot, int32 questId = -1, bool ignoreFull = false, bool ignoreInactive = false, float maxDistance = 5000, bool ignoreObjectives = false);
std::vector<TravelDestination*> getRpgTravelDestinations(Player* bot, bool ignoreFull = false, bool ignoreInactive = false, float maxDistance = 5000);
std::vector<TravelDestination*> getExploreTravelDestinations(Player* bot, bool ignoreFull = false, bool ignoreInactive = false);
std::vector<TravelDestination*> getGrindTravelDestinations(Player* bot, bool ignoreFull = false, bool ignoreInactive = false, float maxDistance = 5000);
std::vector<TravelDestination*> getBossTravelDestinations(Player* bot, bool ignoreFull = false, bool ignoreInactive = false, float maxDistance = 25000);
void setNullTravelTarget(Player* player);
void addMapTransfer(WorldPosition start, WorldPosition end, float portalDistance = 0.1f, bool makeShortcuts = true);
void loadMapTransfers();
float mapTransDistance(WorldPosition start, WorldPosition end);
float fastMapTransDistance(WorldPosition start, WorldPosition end);
NullTravelDestination* nullTravelDestination = new NullTravelDestination();
WorldPosition* nullWorldPosition = new WorldPosition();
void addBadVmap(uint32 mapId, uint8 x, uint8 y)
{
badVmap.push_back(std::make_tuple(mapId, x, y));
}
void addBadMmap(uint32 mapId, uint8 x, uint8 y)
{
badMmap.push_back(std::make_tuple(mapId, x, y));
}
bool isBadVmap(uint32 mapId, uint8 x, uint8 y)
{
return std::find(badVmap.begin(), badVmap.end(), std::make_tuple(mapId, x, y)) != badVmap.end();
}
bool isBadMmap(uint32 mapId, uint8 x, uint8 y)
{
return std::find(badMmap.begin(), badMmap.end(), std::make_tuple(mapId, x, y)) != badMmap.end();
}
void printGrid(uint32 mapId, int x, int y, std::string const type);
void printObj(WorldObject* obj, std::string const type);
//protected:
void logQuestError(uint32 errorNr, Quest* quest, uint32 objective = 0, uint32 unitId = 0, uint32 itemId = 0);
std::vector<uint32> avoidLoaded;
std::vector<QuestTravelDestination*> questGivers;
std::vector<RpgTravelDestination*> rpgNpcs;
std::vector<GrindTravelDestination*> grindMobs;
std::vector<BossTravelDestination*> bossMobs;
std::unordered_map<uint32, ExploreTravelDestination*> exploreLocs;
std::unordered_map<uint32, QuestContainer*> quests;
std::vector<std::tuple<uint32, uint8, uint8>> badVmap, badMmap;
std::unordered_map<std::pair<uint32, uint32>, std::vector<mapTransfer>, boost::hash<std::pair<uint32, uint32>>> mapTransfersMap;
};
#define sTravelMgr TravelMgr::instance()
#endif

2508
src/TravelNode.cpp Normal file

File diff suppressed because it is too large Load Diff

578
src/TravelNode.h Normal file
View File

@@ -0,0 +1,578 @@
/*
* 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.
*/
#ifndef _PLAYERBOT_TRAVELNODE_H
#define _PLAYERBOT_TRAVELNODE_H
#include "TravelMgr.h"
#include <shared_mutex>
//THEORY
//
// Pathfinding in (c)mangos is based on detour recast an opensource nashmesh creation and pathfinding codebase.
// This system is used for mob and npc pathfinding and in this codebase also for bots.
// Because mobs and npc movement is based on following a player or a set path the PathGenerator is limited to 296y.
// This means that when trying to find a path from A to B distances beyond 296y will be a best guess often moving in a straight path.
// Bots would get stuck moving from Northshire to Stormwind because there is no 296y path that doesn't go (initially) the wrong direction.
//
// To remedy this limitation without altering the PathGenerator limits too much this node system was introduced.
//
// <S> ---> [N1] ---> [N2] ---> [N3] ---> <E>
//
// Bot at <S> wants to move to <E>
// [N1],[N2],[N3] are predefined nodes for wich we know we can move from [N1] to [N2] and from [N2] to [N3] but not from [N1] to [N3]
// If we can move fom [S] to [N1] and from [N3] to [E] we have a complete route to travel.
//
// Termonology:
// Node: a location on a map for which we know bots are likely to want to travel to or need to travel past to reach other nodes.
// Link: the connection between two nodes. A link signifies that the bot can travel from one node to another. A link is one-directional.
// Path: the waypointpath returned by the standard PathGenerator to move from one node (or position) to another. A path can be imcomplete or empty which means there is no link.
// Route: the list of nodes that give the shortest route from a node to a distant node. Routes are calculated using a standard A* search based on links.
//
// On server start saved nodes and links are loaded. Paths and routes are calculated on the fly but saved for future use.
// Nodes can be added and removed realtime however because bots access the nodes from different threads this requires a locking mechanism.
//
// Initially the current nodes have been made:
// Flightmasters and Inns (Bots can use these to fast-travel so eventually they will be included in the route calculation)
// WorldBosses and Unique bosses in instances (These are a logical places bots might want to go in instances)
// Player start spawns (Obviously all lvl1 bots will spawn and move from here)
// Area triggers locations with teleport and their teleport destinations (These used to travel in or between maps)
// Transports including elevators (Again used to travel in and in maps)
// (sub)Zone means (These are the center most point for each sub-zone which is good for global coverage)
//
// To increase coverage/linking extra nodes can be automatically be created.
// Current implentation places nodes on paths (including complete) at sub-zone transitions or randomly.
// After calculating possible links the node is removed if it does not create local coverage.
//
enum class TravelNodePathType : uint8
{
none = 0,
walk = 1,
portal = 2,
transport = 3,
flightPath = 4,
teleportSpell = 5
};
//A connection between two nodes.
class TravelNodePath
{
public:
//Legacy Constructor for travelnodestore
//TravelNodePath(float distance1, float extraCost1, bool portal1 = false, uint32 portalId1 = 0, bool transport1 = false, bool calculated = false, uint8 maxLevelMob1 = 0, uint8 maxLevelAlliance1 = 0, uint8 maxLevelHorde1 = 0, float swimDistance1 = 0, bool flightPath1 = false);
//Constructor
TravelNodePath(float distance = 0.1f, float extraCost = 0, uint8 pathType = (uint8)TravelNodePathType::walk, uint32 pathObject = 0, bool calculated = false,
std::vector<uint8> maxLevelCreature = { 0, 0, 0 }, float swimDistance = 0)
: distance(distance), extraCost(extraCost), pathType(TravelNodePathType(pathType)), pathObject(pathObject), calculated(calculated),
maxLevelCreature(maxLevelCreature), swimDistance(swimDistance)
{
if (pathType != (uint8)TravelNodePathType::walk)
complete = true;
};
TravelNodePath(TravelNodePath* basePath)
{
complete = basePath->complete;
path = basePath->path;
extraCost = basePath->extraCost;
calculated = basePath->calculated;
distance = basePath->distance;
maxLevelCreature = basePath->maxLevelCreature;
swimDistance = basePath->swimDistance;
pathType = basePath->pathType;
pathObject = basePath->pathObject;
};
// Getters
bool getComplete() { return complete || pathType != TravelNodePathType::walk; }
std::vector<WorldPosition> getPath() { return path; }
TravelNodePathType getPathType() { return pathType; }
uint32 getPathObject() { return pathObject; }
float getDistance() { return distance; }
float getSwimDistance() { return swimDistance; }
float getExtraCost(){ return extraCost; }
std::vector<uint8> getMaxLevelCreature(){ return maxLevelCreature; }
void setCalculated(bool calculated1 = true) { calculated = calculated1; }
bool getCalculated() { return calculated; }
std::string const print();
//Setters
void setComplete(bool complete1)
{
complete = complete1;
}
void setPath(std::vector<WorldPosition> path1)
{
path = path1;
}
void setPathAndCost(std::vector<WorldPosition> path1, float speed)
{
setPath(path1);
calculateCost(true);
extraCost = distance / speed;
}
//void setPortal(bool portal1, uint32 portalId1 = 0) { portal = portal1; portalId = portalId1; }
//void setTransport(bool transport1) { transport = transport1; }
void setPathType(TravelNodePathType pathType1)
{
pathType = pathType1;
}
void setPathObject(uint32 pathObject1)
{
pathObject = pathObject1;
}
void calculateCost(bool distanceOnly = false);
float getCost(Player* bot = nullptr, uint32 cGold = 0);
uint32 getPrice();
private:
//Does the path have all the points to get to the destination?
bool complete = false;
//List of WorldPositions to get to the destination.
std::vector<WorldPosition> path = {};
//The extra (loading/transport) time it takes to take this path.
float extraCost = 0;
bool calculated = false;
//Derived distance in yards
float distance = 0.1f;
//Calculated mobs level along the way.
std::vector<uint8> maxLevelCreature = { 0, 0, 0 }; //mobs, horde, alliance
//Calculated swiming distances along the way.
float swimDistance = 0;
TravelNodePathType pathType = TravelNodePathType::walk;
uint32 pathObject = 0;
/*
//Is the path a portal/teleport to the destination?
bool portal = false;
//Area trigger Id
uint32 portalId = 0;
//Is the path transport based?
bool transport = false;
// Is the path a flightpath?
bool flightPath = false;
*/
};
//A waypoint to travel from or to.
//Each node knows which other nodes can be reached without help.
class TravelNode
{
public:
//Constructors
TravelNode() { };
TravelNode(WorldPosition point1, std::string const nodeName1 = "Travel Node", bool important1 = false)
{
nodeName = nodeName1;
point = point1;
important = important1;
}
TravelNode(TravelNode* baseNode)
{
nodeName = baseNode->nodeName;
point = baseNode->point;
important = baseNode->important;
}
//Setters
void setLinked(bool linked1) { linked = linked1; }
void setPoint(WorldPosition point1) { point = point1; }
//Getters
std::string const getName() { return nodeName; };
WorldPosition* getPosition() { return &point; };
std::unordered_map<TravelNode*, TravelNodePath>* getPaths() { return &paths; }
std::unordered_map<TravelNode*, TravelNodePath*>* getLinks() { return &links; }
bool isImportant() { return important; };
bool isLinked() { return linked; }
bool isTransport()
{
for (auto const& link : *getLinks())
if (link.second->getPathType() == TravelNodePathType::transport)
return true;
return false;
}
uint32 getTransportId()
{
for (auto const& link : *getLinks())
if (link.second->getPathType() == TravelNodePathType::transport)
return link.second->getPathObject();
return false;
}
bool isPortal()
{
for (auto const& link : *getLinks())
if (link.second->getPathType() == TravelNodePathType::portal)
return true;
return false;
}
bool isWalking()
{
for (auto link : *getLinks())
if (link.second->getPathType() == TravelNodePathType::walk)
return true;
return false;
}
//WorldLocation shortcuts
uint32 getMapId() { return point.getMapId(); }
float getX() { return point.getX(); }
float getY() { return point.getY(); }
float getZ() { return point.getZ(); }
float getO() { return point.getO(); }
float getDistance(WorldPosition pos) { return point.distance(pos); }
float getDistance(TravelNode* node) { return point.distance(node->getPosition()); }
float fDist(TravelNode* node) { return point.fDist(node->getPosition()); }
float fDist(WorldPosition pos) { return point.fDist(pos); }
TravelNodePath* setPathTo(TravelNode* node, TravelNodePath path = TravelNodePath(), bool isLink = true)
{
if (this != node)
{
paths[node] = path;
if (isLink)
links[node] = &paths[node];
return &paths[node];
}
return nullptr;
}
bool hasPathTo(TravelNode* node) { return paths.find(node) != paths.end(); }
TravelNodePath* getPathTo(TravelNode* node) { return &paths[node]; }
bool hasCompletePathTo(TravelNode* node) { return hasPathTo(node) && getPathTo(node)->getComplete(); }
TravelNodePath* buildPath(TravelNode* endNode, Unit* bot, bool postProcess = false);
void setLinkTo(TravelNode* node, float distance = 0.1f)
{
if (this != node)
{
if (!hasPathTo(node))
setPathTo(node, TravelNodePath(distance));
else
links[node] = &paths[node];
}
}
bool hasLinkTo(TravelNode* node) { return links.find(node) != links.end(); }
float linkCostTo(TravelNode* node) { return paths.find(node)->second.getDistance(); }
float linkDistanceTo(TravelNode* node) { return paths.find(node)->second.getDistance(); }
void removeLinkTo(TravelNode* node, bool removePaths = false);
bool isEqual(TravelNode* compareNode);
//Removes links to other nodes that can also be reached by passing another node.
bool isUselessLink(TravelNode* farNode);
void cropUselessLink(TravelNode* farNode);
bool cropUselessLinks();
//Returns all nodes that can be reached from this node.
std::vector<TravelNode*> getNodeMap(bool importantOnly = false, std::vector<TravelNode*> ignoreNodes = {});
// Checks if it is even possible to route to this node.
bool hasRouteTo(TravelNode* node)
{
if (routes.empty())
for (auto mNode : getNodeMap())
routes[mNode] = true;
return routes.find(node) != routes.end();
};
void print(bool printFailed = true);
protected:
//Logical name of the node
std::string nodeName;
//WorldPosition of the node.
WorldPosition point;
//List of paths to other nodes.
std::unordered_map<TravelNode*, TravelNodePath> paths;
//List of links to other nodes.
std::unordered_map<TravelNode*, TravelNodePath*> links;
//List of nodes and if there is 'any' route possible
std::unordered_map<TravelNode*, bool> routes;
//This node should not be removed
bool important = false;
//This node has been checked for nearby links
bool linked = false;
//This node is a (moving) transport.
//bool transport = false;
//Entry of transport.
//uint32 transportId = 0;
};
class PortalNode : public TravelNode
{
public:
PortalNode(TravelNode* baseNode) : TravelNode(baseNode) { };
void SetPortal(TravelNode* baseNode, TravelNode* endNode, uint32 portalSpell)
{
nodeName = baseNode->getName();
point = *baseNode->getPosition();
paths.clear();
links.clear();
TravelNodePath path(0.1f, 0.1f, (uint8)TravelNodePathType::teleportSpell, portalSpell, true);
setPathTo(endNode, path);
};
};
//Route step type
enum PathNodeType
{
NODE_PREPATH = 0,
NODE_PATH = 1,
NODE_NODE = 2,
NODE_PORTAL = 3,
NODE_TRANSPORT = 4,
NODE_FLIGHTPATH = 5,
NODE_TELEPORT = 6
};
struct PathNodePoint
{
WorldPosition point;
PathNodeType type = NODE_PATH;
uint32 entry = 0;
};
//A complete list of points the bots has to walk to or teleport to.
class TravelPath
{
public:
TravelPath() { };
TravelPath(std::vector<PathNodePoint> fullPath1) { fullPath = fullPath1; }
TravelPath(std::vector<WorldPosition> path, PathNodeType type = NODE_PATH, uint32 entry = 0)
{
addPath(path, type, entry);
}
void addPoint(PathNodePoint point) { fullPath.push_back(point); }
void addPoint(WorldPosition point, PathNodeType type = NODE_PATH, uint32 entry = 0) { fullPath.push_back(PathNodePoint{ point, type, entry }); }
void addPath(std::vector<WorldPosition> path, PathNodeType type = NODE_PATH, uint32 entry = 0) { for (auto& p : path) { fullPath.push_back(PathNodePoint{ p, type, entry }); }; }
void addPath(std::vector<PathNodePoint> newPath) { fullPath.insert(fullPath.end(), newPath.begin(), newPath.end()); }
void clear() { fullPath.clear(); }
bool empty() { return fullPath.empty(); }
std::vector<PathNodePoint> getPath() { return fullPath; }
WorldPosition getFront() {return fullPath.front().point; }
WorldPosition getBack() { return fullPath.back().point; }
std::vector<WorldPosition> getPointPath()
{
std::vector<WorldPosition> retVec;
for (auto const& p : fullPath)
retVec.push_back(p.point);
return retVec;
};
bool makeShortCut(WorldPosition startPos, float maxDist);
bool shouldMoveToNextPoint(WorldPosition startPos, std::vector<PathNodePoint>::iterator beg, std::vector<PathNodePoint>::iterator ed, std::vector<PathNodePoint>::iterator p, float& moveDist, float maxDist);
WorldPosition getNextPoint(WorldPosition startPos, float maxDist, TravelNodePathType& pathType, uint32& entry);
std::ostringstream const print();
private:
std::vector<PathNodePoint> fullPath;
};
//An stored A* search that gives a complete route from one node to another.
class TravelNodeRoute
{
public:
TravelNodeRoute() { }
TravelNodeRoute(std::vector<TravelNode*> nodes1) { nodes = nodes1; /*currentNode = route.begin();*/ }
bool isEmpty() { return nodes.empty(); }
bool hasNode(TravelNode* node) { return findNode(node) != nodes.end(); }
float getTotalDistance();
std::vector<TravelNode*> getNodes() { return nodes; }
TravelPath buildPath(std::vector<WorldPosition> pathToStart = {}, std::vector<WorldPosition> pathToEnd = {}, Unit* bot = nullptr);
std::ostringstream const print();
private:
std::vector<TravelNode*>::iterator findNode(TravelNode* node) { return std::find(nodes.begin(), nodes.end(), node); }
std::vector<TravelNode*> nodes;
};
//A node container to aid A* calculations with nodes.
class TravelNodeStub
{
public:
TravelNodeStub(TravelNode* dataNode1) { dataNode = dataNode1; }
TravelNode* dataNode;
float m_f = 0.0, m_g = 0.0, m_h = 0.0;
bool open = false, close = false;
TravelNodeStub* parent = nullptr;
uint32 currentGold = 0;
};
//The container of all nodes.
class TravelNodeMap
{
public:
TravelNodeMap() { };
TravelNodeMap(TravelNodeMap* baseMap);
static TravelNodeMap* instance()
{
static TravelNodeMap instance;
return &instance;
}
TravelNode* addNode(WorldPosition pos, std::string const preferedName = "Travel Node", bool isImportant = false, bool checkDuplicate = true, bool transport = false, uint32 transportId = 0);
void removeNode(TravelNode* node);
bool removeNodes()
{
if (m_nMapMtx.try_lock_for(std::chrono::seconds(10)))
{
for (auto& node : m_nodes)
removeNode(node);
m_nMapMtx.unlock();
return true;
}
return false;
};
void fullLinkNode(TravelNode* startNode, Unit* bot);
//Get all nodes
std::vector<TravelNode*> getNodes() { return m_nodes; }
std::vector<TravelNode*> getNodes(WorldPosition pos, float range = -1);
//Find nearest node.
TravelNode* getNode(TravelNode* sameNode)
{
for (auto& node : m_nodes)
{
if (node->getName() == sameNode->getName() && node->getPosition() == sameNode->getPosition())
return node;
}
return nullptr;
}
TravelNode* getNode(WorldPosition pos, std::vector<WorldPosition>& ppath, Unit* bot = nullptr, float range = -1);
TravelNode* getNode(WorldPosition pos, Unit* bot = nullptr, float range = -1)
{
std::vector<WorldPosition> ppath;
return getNode(pos, ppath, bot, range);
}
//Get Random Node
TravelNode* getRandomNode(WorldPosition pos)
{
std::vector<TravelNode*> rNodes = getNodes(pos);
if (rNodes.empty())
return nullptr;
return rNodes[urand(0, rNodes.size() - 1)];
}
//Finds the best nodePath between two nodes
TravelNodeRoute getRoute(TravelNode* start, TravelNode* goal, Player* bot = nullptr);
//Find the best node between two positions
TravelNodeRoute getRoute(WorldPosition startPos, WorldPosition endPos, std::vector<WorldPosition>& startPath, Player* bot = nullptr);
//Find the full path between those locations
static TravelPath getFullPath(WorldPosition startPos, WorldPosition endPos, Player* bot = nullptr);
//Manage/update nodes
void manageNodes(Unit* bot, bool mapFull = false);
void setHasToGen() { hasToGen = true; }
void generateNpcNodes();
void generateStartNodes();
void generateAreaTriggerNodes();
void generateNodes();
void generateTransportNodes();
void generateZoneMeanNodes();
void generateWalkPaths();
void removeLowNodes();
void removeUselessPaths();
void calculatePathCosts();
void generateTaxiPaths();
void generatePaths();
void generateAll();
void printMap();
void printNodeStore();
void saveNodeStore();
void loadNodeStore();
bool cropUselessNode(TravelNode* startNode);
TravelNode* addZoneLinkNode(TravelNode* startNode);
TravelNode* addRandomExtNode(TravelNode* startNode);
void calcMapOffset();
WorldPosition getMapOffset(uint32 mapId);
std::shared_timed_mutex m_nMapMtx;
std::unordered_map<ObjectGuid, std::unordered_map<uint32, TravelNode*>> teleportNodes;
private:
std::vector<TravelNode*> m_nodes;
std::vector<std::pair<uint32, WorldPosition>> mapOffsets;
bool hasToSave = false;
bool hasToGen = false;
bool hasToFullGen = false;
};
#define sTravelNodeMap TravelNodeMap::instance()
#endif

89
src/cs_playerbots.cpp Normal file
View File

@@ -0,0 +1,89 @@
/*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Chat.h"
#include "GuildTaskMgr.h"
#include "PerformanceMonitor.h"
#include "PlayerbotMgr.h"
#include "RandomPlayerbotMgr.h"
#include "ScriptMgr.h"
class playerbots_commandscript : public CommandScript
{
public:
playerbots_commandscript() : CommandScript("playerbots_commandscript") { }
std::vector<ChatCommand> GetCommands() const override
{
static std::vector<ChatCommand> playerbotsCommandTable =
{
{ "bot", SEC_PLAYER, false, &HandlePlayerbotCommand, nullptr },
{ "gtask", SEC_GAMEMASTER, true, &HandleGuildTaskCommand, nullptr },
{ "pmon", SEC_GAMEMASTER, true, &HandlePerfMonCommand, nullptr },
{ "rndbot", SEC_GAMEMASTER, true, &HandleRandomPlayerbotCommand, nullptr }
};
static std::vector<ChatCommand> commandTable =
{
{ "playerbots", SEC_PLAYER, true, nullptr, "", playerbotsCommandTable },
};
return commandTable;
}
static bool HandlePlayerbotCommand(ChatHandler* handler, char const* args)
{
return PlayerbotMgr::HandlePlayerbotMgrCommand(handler, args);
}
static bool HandleRandomPlayerbotCommand(ChatHandler* handler, char const* args)
{
return RandomPlayerbotMgr::HandlePlayerbotConsoleCommand(handler, args);
}
static bool HandleGuildTaskCommand(ChatHandler* handler, char const* args)
{
return GuildTaskMgr::HandleConsoleCommand(handler, args);
}
static bool HandlePerfMonCommand(ChatHandler* handler, char const* args)
{
if (!strcmp(args, "reset"))
{
sPerformanceMonitor->Reset();
return true;
}
if (!strcmp(args, "tick"))
{
sPerformanceMonitor->PrintStats(true, false);
return true;
}
if (!strcmp(args, "stack"))
{
sPerformanceMonitor->PrintStats(false, true);
return true;
}
sPerformanceMonitor->PrintStats();
return true;
}
};
void AddSC_playerbots_commandscript()
{
new playerbots_commandscript();
}

1
src/cs_playerbots.h Normal file
View File

@@ -0,0 +1 @@
void AddSC_playerbots_commandscript();

25
src/playerbots_loader.cpp Normal file
View File

@@ -0,0 +1,25 @@
/*
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
// From SC
void AddPlayerbotsScripts();
// Add all
void Addmod_playerbotsScripts()
{
AddPlayerbotsScripts();
}

111
src/strategy/Action.cpp Normal file
View File

@@ -0,0 +1,111 @@
/*
* 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 "Action.h"
#include "Playerbots.h"
uint32 NextAction::size(NextAction** actions)
{
if (!actions)
return 0;
uint32 size = 0;
for (size = 0; actions[size];)
++size;
return size;
}
NextAction** NextAction::clone(NextAction** actions)
{
if (!actions)
return nullptr;
uint32 size = NextAction::size(actions);
NextAction** res = new NextAction*[size + 1];
for (uint32 i = 0; i < size; i++)
res[i] = new NextAction(*actions[i]);
res[size] = nullptr;
return res;
}
NextAction** NextAction::merge(NextAction** left, NextAction** right)
{
uint32 leftSize = NextAction::size(left);
uint32 rightSize = NextAction::size(right);
NextAction** res = new NextAction*[leftSize + rightSize + 1];
for (uint32 i = 0; i < leftSize; i++)
res[i] = new NextAction(*left[i]);
for (uint32 i = 0; i < rightSize; i++)
res[leftSize + i] = new NextAction(*right[i]);
res[leftSize + rightSize] = nullptr;
NextAction::destroy(left);
NextAction::destroy(right);
return res;
}
NextAction** NextAction::array(uint32 nil, ...)
{
va_list vl;
va_start(vl, nil);
uint32 size = 0;
NextAction* cur = nullptr;
do
{
cur = va_arg(vl, NextAction*);
++size;
}
while (cur);
va_end(vl);
NextAction** res = new NextAction*[size];
va_start(vl, nil);
for (uint32 i = 0; i < size; i++)
res[i] = va_arg(vl, NextAction*);
va_end(vl);
return res;
}
void NextAction::destroy(NextAction** actions)
{
if (!actions)
return;
for (uint32 i=0; actions[i]; i++)
delete actions[i];
delete[] actions;
}
Value<Unit*>* Action::GetTargetValue()
{
return context->GetValue<Unit*>(GetTargetName());
}
Unit* Action::GetTarget()
{
return GetTargetValue()->Get();
}
ActionBasket::ActionBasket(ActionNode* action, float relevance, bool skipPrerequisites, Event event) :
action(action), relevance(relevance), skipPrerequisites(skipPrerequisites), event(event), created(time(nullptr))
{
}
bool ActionBasket::isExpired(time_t secs)
{
return time(nullptr) - created >= secs;
}

122
src/strategy/Action.h Normal file
View File

@@ -0,0 +1,122 @@
/*
* 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.
*/
#ifndef _PLAYERBOT_ACTION_H
#define _PLAYERBOT_ACTION_H
#include "AiObject.h"
#include "Common.h"
#include "Event.h"
#include "Value.h"
class PlayerbotAI;
class Unit;
class NextAction
{
public:
NextAction(std::string const name, float relevance = 0.0f) : name(name), relevance(relevance) { }
NextAction(NextAction const& o) : name(o.name), relevance(o.relevance) { }
std::string const getName() { return name; }
float getRelevance() {return relevance;}
static uint32 size(NextAction** actions);
static NextAction** clone(NextAction** actions);
static NextAction** merge(NextAction** what, NextAction** with);
static NextAction** array(uint32 nil,...);
static void destroy(NextAction** actions);
private:
float relevance;
std::string const name;
};
class Action : public AiNamedObject
{
public:
enum class ActionThreatType
{
None = 0,
Single = 1,
Aoe = 2
};
Action(PlayerbotAI* botAI, std::string const name = "action") : verbose(false), AiNamedObject(botAI, name) { }
virtual ~Action(void) { }
virtual bool Execute(Event event) { return true; }
virtual bool isPossible() { return true; }
virtual bool isUseful() { return true; }
virtual NextAction** getPrerequisites() { return nullptr; }
virtual NextAction** getAlternatives() { return nullptr; }
virtual NextAction** getContinuers() { return nullptr; }
virtual ActionThreatType getThreatType() { return ActionThreatType::None; }
void Update() { }
void Reset() { }
virtual Unit* GetTarget();
virtual Value<Unit*>* GetTargetValue();
virtual std::string const GetTargetName() { return "self target"; }
void MakeVerbose() { verbose = true; }
void setRelevance(uint32 relevance1) { relevance = relevance1; };
virtual float getRelevance() { return relevance; }
protected:
bool verbose;
float relevance = 0;
};
class ActionNode
{
public:
ActionNode(std::string const name, NextAction** prerequisites = nullptr, NextAction** alternatives = nullptr, NextAction** continuers = nullptr) :
action(nullptr), name(name), prerequisites(prerequisites), alternatives(alternatives), continuers(continuers) { }
virtual ~ActionNode()
{
NextAction::destroy(prerequisites);
NextAction::destroy(alternatives);
NextAction::destroy(continuers);
}
Action* getAction() { return action; }
void setAction(Action* action) { this->action = action; }
std::string const getName() { return name; }
NextAction** getContinuers() { return NextAction::merge(NextAction::clone(continuers), action->getContinuers()); }
NextAction** getAlternatives() { return NextAction::merge(NextAction::clone(alternatives), action->getAlternatives()); }
NextAction** getPrerequisites() { return NextAction::merge(NextAction::clone(prerequisites), action->getPrerequisites()); }
private:
std::string const name;
Action* action;
NextAction** continuers;
NextAction** alternatives;
NextAction** prerequisites;
};
class ActionBasket
{
public:
ActionBasket(ActionNode* action, float relevance, bool skipPrerequisites, Event event);
virtual ~ActionBasket(void) { }
float getRelevance() {return relevance;}
ActionNode* getAction() {return action;}
Event getEvent() { return event; }
bool isSkipPrerequisites() { return skipPrerequisites; }
void AmendRelevance(float k) { relevance *= k; }
void setRelevance(float relevance) { this->relevance = relevance; }
bool isExpired(time_t secs);
private:
ActionNode* action;
float relevance;
bool skipPrerequisites;
Event event;
time_t created;
};
#endif

15
src/strategy/AiObject.cpp Normal file
View File

@@ -0,0 +1,15 @@
/*
* 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 "AiObject.h"
#include "Playerbots.h"
AiObject::AiObject(PlayerbotAI* botAI) : PlayerbotAIAware(botAI), bot(botAI->GetBot()), context(botAI->GetAiObjectContext()), chat(botAI->GetChatHelper())
{
}
Player* AiObject::GetMaster()
{
return botAI->GetMaster();
}

497
src/strategy/AiObject.h Normal file
View File

@@ -0,0 +1,497 @@
/*
* 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.
*/
#ifndef _PLAYERBOT_AIOBJECT_H
#define _PLAYERBOT_AIOBJECT_H
#include "Common.h"
#include "PlayerbotAIAware.h"
class AiObjectContext;
class ChatHelper;
class Player;
class PlayerbotAI;
class AiObject : public PlayerbotAIAware
{
public:
AiObject(PlayerbotAI* botAI);
protected:
Player* bot;
Player* GetMaster();
AiObjectContext* context;
ChatHelper* chat;
};
class AiNamedObject : public AiObject
{
public:
AiNamedObject(PlayerbotAI* botAI, std::string const name) : AiObject(botAI), name(name) { }
public:
virtual std::string const getName() { return name; }
protected:
std::string const name;
};
//
// TRIGGERS
//
#define NEXT_TRIGGERS(name, relevance) \
virtual NextAction* getNextAction() { return new NextAction(name, relevance); }
#define BEGIN_TRIGGER(clazz, super) \
class clazz : public super \
{ \
public: \
clazz(PlayerbotAI* botAI) : super(botAI) { } \
bool IsActive() override; \
#define END_TRIGGER() \
};
#define BUFF_TRIGGER(clazz, spell) \
class clazz : public BuffTrigger \
{ \
public: \
clazz(PlayerbotAI* botAI) : BuffTrigger(botAI, spell) { } \
}
#define BUFF_TRIGGER_A(clazz, spell) \
class clazz : public BuffTrigger \
{ \
public: \
clazz(PlayerbotAI* botAI) : BuffTrigger(botAI, spell) { } \
bool IsActive() override; \
}
#define BUFF_PARTY_TRIGGER(clazz, spell) \
class clazz : public BuffOnPartyTrigger \
{ \
public: \
clazz(PlayerbotAI* botAI) : BuffOnPartyTrigger(botAI, spell) { } \
}
#define BUFF_PARTY_TRIGGER_A(clazz, spell) \
class clazz : public BuffOnPartyTrigger \
{ \
public: \
clazz(PlayerbotAI* botAI) : BuffOnPartyTrigger(botAI, spell) { } \
bool IsActive() override; \
}
#define DEBUFF_TRIGGER(clazz, spell) \
class clazz : public DebuffTrigger \
{ \
public: \
clazz(PlayerbotAI* botAI) : DebuffTrigger(botAI, spell) { } \
}
#define DEBUFF_TRIGGER_A(clazz, spell) \
class clazz : public DebuffTrigger \
{ \
public: \
clazz(PlayerbotAI* botAI) : DebuffTrigger(botAI, spell) { } \
bool IsActive() override; \
}
#define DEBUFF_ENEMY_TRIGGER(clazz, spell) \
class clazz : public DebuffOnAttackerTrigger \
{ \
public: \
clazz(PlayerbotAI* botAI) : DebuffOnAttackerTrigger(botAI, spell) { } \
}
#define DEBUFF_ENEMY_TRIGGER_A(clazz, spell) \
class clazz : public DebuffOnAttackerTrigger \
{ \
public: \
clazz(PlayerbotAI* botAI) : DebuffOnAttackerTrigger(botAI, spell) { } \
bool IsActive() override; \
}
#define CURE_TRIGGER(clazz, spell, dispel) \
class clazz : public NeedCureTrigger \
{ \
public: \
clazz(PlayerbotAI* botAI) : NeedCureTrigger(botAI, spell, dispel) { } \
}
#define CURE_PARTY_TRIGGER(clazz, spell, dispel) \
class clazz : public PartyMemberNeedCureTrigger \
{ \
public: \
clazz(PlayerbotAI* botAI) : PartyMemberNeedCureTrigger(botAI, spell, dispel) { } \
}
#define CAN_CAST_TRIGGER(clazz, spell) \
class clazz : public SpellCanBeCastTrigger \
{ \
public: \
clazz(PlayerbotAI* botAI) : SpellCanBeCastTrigger(botAI, spell) { } \
}
#define CAN_CAST_TRIGGER_A(clazz, spell) \
class clazz : public SpellCanBeCastTrigger \
{ \
public: \
clazz(PlayerbotAI* botAI) : SpellCanBeCastTrigger(botAI, spell) { } \
bool IsActive() override; \
}
#define INTERRUPT_TRIGGER(clazz, spell) \
class clazz : public InterruptSpellTrigger \
{ \
public: \
clazz(PlayerbotAI* botAI) : InterruptSpellTrigger(botAI, spell) { } \
}
#define INTERRUPT_TRIGGER_A(clazz, spell) \
class clazz : public InterruptSpellTrigger \
{ \
public: \
clazz(PlayerbotAI* botAI) : InterruptSpellTrigger(botAI, spell) { } \
bool IsActive() override; \
}
#define HAS_AURA_TRIGGER(clazz, spell) \
class clazz : public HasAuraTrigger \
{ \
public: \
clazz(PlayerbotAI* botAI) : HasAuraTrigger(botAI, spell) { } \
}
#define HAS_AURA_TRIGGER_A(clazz, spell) \
class clazz : public HasAuraTrigger \
{ \
public: \
clazz(PlayerbotAI* botAI) : HasAuraTrigger(botAI, spell) { } \
bool IsActive() override; \
}
#define SNARE_TRIGGER(clazz, spell) \
class clazz : public SnareTargetTrigger \
{ \
public: \
clazz(PlayerbotAI* botAI) : SnareTargetTrigger(botAI, spell) { } \
}
#define SNARE_TRIGGER_A(clazz, spell) \
class clazz : public SnareTargetTrigger \
{ \
public: \
clazz(PlayerbotAI* botAI) : SnareTargetTrigger(botAI, spell) { } \
bool IsActive() override; \
}
#define PROTECT_TRIGGER(clazz, spell) \
class clazz : public ProtectPartyMemberTrigger \
{ \
public: \
clazz(PlayerbotAI* botAI) : ProtectPartyMemberTrigger(botAI) { } \
}
#define DEFLECT_TRIGGER(clazz, spell) \
class clazz : public DeflectSpellTrigger \
{ \
public: \
clazz(PlayerbotAI* botAI) : DeflectSpellTrigger(botAI, spell) { } \
}
#define BOOST_TRIGGER(clazz, spell) \
class clazz : public BoostTrigger \
{ \
public: \
clazz(PlayerbotAI* botAI) : BoostTrigger(botAI, spell) { } \
}
#define BOOST_TRIGGER_A(clazz, spell) \
class clazz : public BoostTrigger \
{ \
public: \
clazz(PlayerbotAI* botAI) : BoostTrigger(botAI, spell) { } \
bool IsActive() override; \
}
#define INTERRUPT_HEALER_TRIGGER(clazz, spell) \
class clazz : public InterruptEnemyHealerTrigger \
{ \
public: \
clazz(PlayerbotAI* botAI) : InterruptEnemyHealerTrigger(botAI, spell) { } \
}
#define INTERRUPT_HEALER_TRIGGER_A(clazz, spell) \
class clazz : public InterruptEnemyHealerTrigger \
{ \
public: \
clazz(PlayerbotAI* botAI) : InterruptEnemyHealerTrigger(botAI, spell) { } \
bool IsActive() override; \
}
#define CC_TRIGGER(clazz, spell) \
class clazz : public HasCcTargetTrigger \
{ \
public: \
clazz(PlayerbotAI* botAI) : HasCcTargetTrigger(botAI, spell) { } \
}
//
// ACTIONS
//
#define MELEE_ACTION(clazz, spell) \
class clazz : public CastMeleeSpellAction \
{ \
public: \
clazz(PlayerbotAI* botAI) : CastMeleeSpellAction(botAI, spell) { } \
}
#define MELEE_ACTION_U(clazz, spell, useful) \
class clazz : public CastMeleeSpellAction \
{ \
public: \
clazz(PlayerbotAI* botAI) : CastMeleeSpellAction(botAI, spell) { } \
bool isUseful() override { return useful; } \
}
#define SPELL_ACTION(clazz, spell) \
class clazz : public CastSpellAction \
{ \
public: \
clazz(PlayerbotAI* botAI) : CastSpellAction(botAI, spell) { } \
}
#define SPELL_ACTION_U(clazz, spell, useful) \
class clazz : public CastSpellAction \
{ \
public: \
clazz(PlayerbotAI* botAI) : CastSpellAction(botAI, spell) { } \
bool isUseful() override { return useful; } \
}
#define HEAL_ACTION(clazz, spell) \
class clazz : public CastHealingSpellAction \
{ \
public: \
clazz(PlayerbotAI* botAI) : CastHealingSpellAction(botAI, spell) { } \
}
#define HEAL_ACTION_U(clazz, spell, useful) \
class clazz : public CastHealingSpellAction \
{ \
public: \
clazz(PlayerbotAI* botAI) : CastHealingSpellAction(botAI, spell) { } \
bool isUseful() override { return useful; } \
}
#define HEAL_PARTY_ACTION(clazz, spell) \
class clazz : public HealPartyMemberAction \
{ \
public: \
clazz(PlayerbotAI* botAI) : HealPartyMemberAction(botAI, spell) { } \
}
#define AOE_HEAL_ACTION(clazz, spell) \
class clazz : public CastAoeHealSpellAction \
{ \
public: \
clazz(PlayerbotAI* botAI) : CastAoeHealSpellAction(botAI, spell) { } \
}
#define BUFF_ACTION(clazz, spell) \
class clazz : public CastBuffSpellAction \
{ \
public: \
clazz(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, spell) { } \
}
#define BUFF_ACTION_U(clazz, spell, useful) \
class clazz : public CastBuffSpellAction \
{ \
public: \
clazz(PlayerbotAI* botAI) : CastBuffSpellAction(botAI, spell) { } \
bool isUseful() override { return useful; } \
}
#define BUFF_PARTY_ACTION(clazz, spell) \
class clazz : public BuffOnPartyAction \
{ \
public: \
clazz(PlayerbotAI* botAI) : BuffOnPartyAction(botAI, spell) { } \
}
#define CURE_ACTION(clazz, spell) \
class clazz : public CastCureSpellAction \
{ \
public: \
clazz(PlayerbotAI* botAI) : CastCureSpellAction(botAI, spell) { } \
}
#define CURE_PARTY_ACTION(clazz, spell, dispel) \
class clazz : public CurePartyMemberAction \
{ \
public: \
clazz(PlayerbotAI* botAI) : CurePartyMemberAction(botAI, spell, dispel) { } \
}
#define RESS_ACTION(clazz, spell) \
class clazz : public ResurrectPartyMemberAction \
{ \
public: \
clazz(PlayerbotAI* botAI) : ResurrectPartyMemberAction(botAI, spell) { } \
}
#define DEBUFF_ACTION(clazz, spell) \
class clazz : public CastDebuffSpellAction \
{ \
public: \
clazz(PlayerbotAI* botAI) : CastDebuffSpellAction(botAI, spell) { } \
}
#define DEBUFF_ACTION_U(clazz, spell, useful) \
class clazz : public CastDebuffSpellAction \
{ \
public: \
clazz(PlayerbotAI* botAI) : CastDebuffSpellAction(botAI, spell) { } \
bool isUseful() override { return useful; } \
}
#define DEBUFF_ACTION_R(clazz, spell, distance) \
class clazz : public CastDebuffSpellAction \
{ \
public: \
clazz(PlayerbotAI* botAI) : CastDebuffSpellAction(botAI, spell) \
{ \
range = distance; \
} \
}
#define DEBUFF_ENEMY_ACTION(clazz, spell) \
class clazz : public CastDebuffSpellOnAttackerAction \
{ \
public: \
clazz(PlayerbotAI* botAI) : CastDebuffSpellOnAttackerAction(botAI, spell) { } \
}
#define REACH_ACTION(clazz, spell, range) \
class clazz : public CastReachTargetSpellAction \
{ \
public: \
clazz(PlayerbotAI* botAI) : CastReachTargetSpellAction(botAI, spell, range) { } \
}
#define REACH_ACTION_U(clazz, spell, range, useful) \
class clazz : public CastReachTargetSpellAction \
{ \
public: \
clazz(PlayerbotAI* botAI) : CastReachTargetSpellAction(botAI, spell, range) { } \
bool isUseful() override { return useful; } \
}
#define ENEMY_HEALER_ACTION(clazz, spell) \
class clazz : public CastSpellOnEnemyHealerAction \
{ \
public: \
clazz(PlayerbotAI* botAI) : CastSpellOnEnemyHealerAction(botAI, spell) { } \
}
#define SNARE_ACTION(clazz, spell) \
class clazz : public CastSnareSpellAction \
{ \
public: \
clazz(PlayerbotAI* botAI) : CastSnareSpellAction(botAI, spell) { } \
}
#define CC_ACTION(clazz, spell) \
class clazz : public CastCrowdControlSpellAction \
{ \
public: \
clazz(PlayerbotAI* botAI) : CastCrowdControlSpellAction(botAI, spell) { } \
}
#define PROTECT_ACTION(clazz, spell) \
class clazz : public CastProtectSpellAction \
{ \
public: \
clazz(PlayerbotAI* botAI) : CastProtectSpellAction(botAI, spell) { } \
}
#define END_RANGED_SPELL_ACTION() \
};
#define BEGIN_SPELL_ACTION(clazz, name) \
class clazz : public CastSpellAction \
{ \
public: \
clazz(PlayerbotAI* botAI) : CastSpellAction(botAI, name) { } \
#define END_SPELL_ACTION() \
};
#define BEGIN_DEBUFF_ACTION(clazz, name) \
class clazz : public CastDebuffSpellAction \
{ \
public: \
clazz(PlayerbotAI* botAI) : CastDebuffSpellAction(botAI, name) { } \
#define BEGIN_RANGED_SPELL_ACTION(clazz, name) \
class clazz : public CastSpellAction \
{ \
public: \
clazz(PlayerbotAI* botAI) : CastSpellAction(botAI, name) { } \
#define BEGIN_MELEE_SPELL_ACTION(clazz, name) \
class clazz : public CastMeleeSpellAction \
{ \
public: \
clazz(PlayerbotAI* botAI) : CastMeleeSpellAction(botAI, name) { } \
#define END_RANGED_SPELL_ACTION() \
};
#define BEGIN_BUFF_ON_PARTY_ACTION(clazz, name) \
class clazz : public BuffOnPartyAction \
{ \
public: \
clazz(PlayerbotAI* botAI) : BuffOnPartyAction(botAI, name) { }
//
// Action node
//
// node_name , action, prerequisite
#define ACTION_NODE_P(name, spell, pre) \
static ActionNode* name(PlayerbotAI* botAI) \
{ \
return new ActionNode(spell, \
/*P*/ NextAction::array(0, new NextAction(pre), nullptr), \
/*A*/ nullptr, \
/*C*/ nullptr); \
}
// node_name , action, alternative
#define ACTION_NODE_A(name, spell, alt) \
static ActionNode* name(PlayerbotAI* botAI) \
{ \
return new ActionNode(spell, \
/*P*/ nullptr, \
/*A*/ NextAction::array(0, new NextAction(alt), nullptr), \
/*C*/ nullptr); \
}
// node_name , action, continuer
#define ACTION_NODE_C(name, spell, con) \
static ActionNode* name(PlayerbotAI* botAI) \
{ \
return new ActionNode(spell, \
/*P*/ nullptr, \
/*A*/ nullptr, \
/*C*/ NextAction::array(0, new NextAction(con), nullptr); \
}
#endif

View File

@@ -0,0 +1,162 @@
/*
* 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 "AiObjectContext.h"
#include "StrategyContext.h"
#include "ActionContext.h"
#include "ChatActionContext.h"
#include "WorldPacketActionContext.h"
#include "ChatTriggerContext.h"
#include "TriggerContext.h"
#include "SharedValueContext.h"
#include "WorldPacketTriggerContext.h"
#include "ValueContext.h"
#include "Playerbots.h"
AiObjectContext::AiObjectContext(PlayerbotAI* botAI) : PlayerbotAIAware(botAI)
{
strategyContexts.Add(new StrategyContext());
strategyContexts.Add(new MovementStrategyContext());
strategyContexts.Add(new AssistStrategyContext());
strategyContexts.Add(new QuestStrategyContext());
actionContexts.Add(new ActionContext());
actionContexts.Add(new ChatActionContext());
actionContexts.Add(new WorldPacketActionContext());
triggerContexts.Add(new TriggerContext());
triggerContexts.Add(new ChatTriggerContext());
triggerContexts.Add(new WorldPacketTriggerContext());
valueContexts.Add(new ValueContext());
valueContexts.Add(sSharedValueContext);
}
void AiObjectContext::Update()
{
strategyContexts.Update();
triggerContexts.Update();
actionContexts.Update();
valueContexts.Update();
}
void AiObjectContext::Reset()
{
strategyContexts.Reset();
triggerContexts.Reset();
actionContexts.Reset();
valueContexts.Reset();
}
std::vector<std::string> AiObjectContext::Save()
{
std::vector<std::string> result;
std::set<std::string> names = valueContexts.GetCreated();
for (std::set<std::string>::iterator i = names.begin(); i != names.end(); ++i)
{
UntypedValue* value = GetUntypedValue(*i);
if (!value)
continue;
std::string const data = value->Save();
if (data == "?")
continue;
std::string const name = *i;
std::ostringstream out;
out << name;
out << ">" << data;
result.push_back(out.str());
}
return std::move(result);
}
void AiObjectContext::Load(std::vector<std::string> data)
{
for (std::vector<std::string>::iterator i = data.begin(); i != data.end(); ++i)
{
std::string const row = *i;
std::vector<std::string> parts = split(row, '>');
if (parts.size() != 2)
continue;
std::string const name = parts[0];
std::string const text = parts[1];
UntypedValue* value = GetUntypedValue(name);
if (!value)
continue;
value->Load(text);
}
}
Strategy* AiObjectContext::GetStrategy(std::string const name)
{
return strategyContexts.GetContextObject(name, botAI);
}
std::set<std::string> AiObjectContext::GetSiblingStrategy(std::string const name)
{
return strategyContexts.GetSiblings(name);
}
Trigger* AiObjectContext::GetTrigger(std::string const name)
{
return triggerContexts.GetContextObject(name, botAI);
}
Action* AiObjectContext::GetAction(std::string const name)
{
return actionContexts.GetContextObject(name, botAI);
}
UntypedValue* AiObjectContext::GetUntypedValue(std::string const name)
{
return valueContexts.GetContextObject(name, botAI);
}
std::set<std::string> AiObjectContext::GetValues()
{
return valueContexts.GetCreated();
}
std::set<std::string> AiObjectContext::GetSupportedStrategies()
{
return strategyContexts.supports();
}
std::set<std::string> AiObjectContext::GetSupportedActions()
{
return actionContexts.supports();
}
std::string const AiObjectContext::FormatValues()
{
std::ostringstream out;
std::set<std::string> names = valueContexts.GetCreated();
for (std::set<std::string>::iterator i = names.begin(); i != names.end(); ++i, out << "|")
{
UntypedValue* value = GetUntypedValue(*i);
if (!value)
continue;
std::string const text = value->Format();
if (text == "?")
continue;
out << "{" << *i << "=" << text << "}";
}
return out.str();
}
void AiObjectContext::AddShared(NamedObjectContext<UntypedValue>* sharedValues)
{
valueContexts.Add(sharedValues);
}

View File

@@ -0,0 +1,70 @@
/*
* 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.
*/
#ifndef _PLAYERBOT_AIOBJECTCONTEXT_H
#define _PLAYERBOT_AIOBJECTCONTEXT_H
#include "Common.h"
#include "NamedObjectContext.h"
#include "PlayerbotAIAware.h"
#include "Strategy.h"
#include "Trigger.h"
#include "Value.h"
class PlayerbotAI;
class AiObjectContext : public PlayerbotAIAware
{
public:
AiObjectContext(PlayerbotAI* botAI);
virtual ~AiObjectContext() { }
virtual Strategy* GetStrategy(std::string const name);
virtual std::set<std::string> GetSiblingStrategy(std::string const name);
virtual Trigger* GetTrigger(std::string const name);
virtual Action* GetAction(std::string const name);
virtual UntypedValue* GetUntypedValue(std::string const name);
template<class T>
Value<T>* GetValue(std::string const name)
{
return dynamic_cast<Value<T>*>(GetUntypedValue(name));
}
template<class T>
Value<T>* GetValue(std::string const name, std::string const param)
{
return GetValue<T>((std::string(name) + "::" + param));
}
template<class T>
Value<T>* GetValue(std::string const name, int32 param)
{
std::ostringstream out;
out << param;
return GetValue<T>(name, out.str());
}
std::set<std::string> GetValues();
std::set<std::string> GetSupportedStrategies();
std::set<std::string> GetSupportedActions();
std::string const FormatValues();
virtual void Update();
virtual void Reset();
virtual void AddShared(NamedObjectContext<UntypedValue>* sharedValues);
std::vector<std::string> Save();
void Load(std::vector<std::string> data);
std::vector<std::string> performanceStack;
protected:
NamedObjectContextList<Strategy> strategyContexts;
NamedObjectContextList<Action> actionContexts;
NamedObjectContextList<Trigger> triggerContexts;
NamedObjectContextList<UntypedValue> valueContexts;
};
#endif

View File

@@ -0,0 +1,111 @@
/*
* 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 "CustomStrategy.h"
#include "Playerbots.h"
#include <regex>
std::map<std::string, std::string> CustomStrategy::actionLinesCache;
NextAction* toNextAction(std::string const action)
{
std::vector<std::string> tokens = split(action, '!');
if (tokens.size() == 2 && !tokens[0].empty())
return new NextAction(tokens[0], atof(tokens[1].c_str()));
else if (tokens.size() == 1 && !tokens[0].empty())
return new NextAction(tokens[0], ACTION_NORMAL);
LOG_ERROR("playerbots", "Invalid action {}", action.c_str());
return nullptr;
}
NextAction** toNextActionArray(std::string const actions)
{
std::vector<std::string> tokens = split(actions, ',');
NextAction** res = new NextAction*[tokens.size() + 1];
uint32 index = 0;
for (std::vector<std::string>::iterator i = tokens.begin(); i != tokens.end(); ++i)
{
if (NextAction* na = toNextAction(*i))
res[index++] = na;
}
res[index++] = nullptr;
return res;
}
TriggerNode* toTriggerNode(std::string const actionLine)
{
std::vector<std::string> tokens = split(actionLine, '>');
if (tokens.size() == 2)
return new TriggerNode(tokens[0], toNextActionArray(tokens[1]));
LOG_ERROR("playerbots", "Invalid action line {}", actionLine.c_str());
return nullptr;
}
CustomStrategy::CustomStrategy(PlayerbotAI* botAI) : Strategy(botAI), Qualified()
{
}
void CustomStrategy::InitTriggers(std::vector<TriggerNode*> &triggers)
{
if (actionLines.empty())
{
if (actionLinesCache[qualifier].empty())
{
LoadActionLines((uint32)botAI->GetBot()->GetGUID().GetCounter());
if (actionLines.empty())
LoadActionLines(0);
}
else
{
std::vector<std::string> tokens = split(actionLinesCache[qualifier], '\n');
std::regex tpl("\\(nullptr,\\s*'.+',\\s*'(.+)'\\)(,|;)");
for (std::vector<std::string>::iterator i = tokens.begin(); i != tokens.end(); ++i)
{
std::string const line = *i;
for (std::sregex_iterator j = std::sregex_iterator(line.begin(), line.end(), tpl); j != std::sregex_iterator(); ++j)
{
std::smatch match = *j;
std::string const actionLine = match[1].str();
if (!actionLine.empty())
actionLines.push_back(actionLine);
}
}
}
}
for (std::vector<std::string>::iterator i = actionLines.begin(); i != actionLines.end(); ++i)
{
if (TriggerNode* tn = toTriggerNode(*i))
triggers.push_back(tn);
}
}
void CustomStrategy::LoadActionLines(uint32 owner)
{
PlayerbotsDatabasePreparedStatement* stmt = PlayerbotsDatabase.GetPreparedStatement(PLAYERBOTS_SEL_CUSTOM_STRATEGY_BY_OWNER_AND_NAME);
stmt->SetData(0, owner);
stmt->SetData(1, qualifier);
PreparedQueryResult result = PlayerbotsDatabase.Query(stmt);
if (result)
{
do
{
Field* fields = result->Fetch();
std::string const action = fields[0].Get<std::string>();
actionLines.push_back(action);
}
while (result->NextRow());
}
}
void CustomStrategy::Reset()
{
actionLines.clear();
actionLinesCache[qualifier].clear();
}

View File

@@ -0,0 +1,30 @@
/*
* 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.
*/
#ifndef _PLAYERBOT_CUSTOMSTRATEGY_H
#define _PLAYERBOT_CUSTOMSTRATEGY_H
#include "Strategy.h"
#include <map>
class PlayerbotAI;
class CustomStrategy : public Strategy, public Qualified
{
public:
CustomStrategy(PlayerbotAI* botAI);
void InitTriggers(std::vector<TriggerNode*> &triggers) override;
std::string const getName() override { return std::string("custom::" + qualifier); }
void Reset();
static std::map<std::string, std::string> actionLinesCache;
private:
std::vector<std::string> actionLines;
void LoadActionLines(uint32 owner);
};
#endif

657
src/strategy/Engine.cpp Normal file
View File

@@ -0,0 +1,657 @@
/*
* 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 "Engine.h"
#include "Action.h"
#include "Event.h"
#include "Queue.h"
#include "PerformanceMonitor.h"
#include "Playerbots.h"
Engine::Engine(PlayerbotAI* botAI, AiObjectContext* factory) : PlayerbotAIAware(botAI), aiObjectContext(factory)
{
lastRelevance = 0.0f;
testMode = false;
}
bool ActionExecutionListeners::Before(Action* action, Event event)
{
bool result = true;
for (std::list<ActionExecutionListener*>::iterator i = listeners.begin(); i != listeners.end(); i++)
{
result &= (*i)->Before(action, event);
}
return result;
}
void ActionExecutionListeners::After(Action* action, bool executed, Event event)
{
for (std::list<ActionExecutionListener*>::iterator i = listeners.begin(); i != listeners.end(); i++)
{
(*i)->After(action, executed, event);
}
}
bool ActionExecutionListeners::OverrideResult(Action* action, bool executed, Event event)
{
bool result = executed;
for (std::list<ActionExecutionListener*>::iterator i = listeners.begin(); i != listeners.end(); i++)
{
result = (*i)->OverrideResult(action, result, event);
}
return result;
}
bool ActionExecutionListeners::AllowExecution(Action* action, Event event)
{
bool result = true;
for (std::list<ActionExecutionListener*>::iterator i = listeners.begin(); i != listeners.end(); i++)
{
result &= (*i)->AllowExecution(action, event);
}
return result;
}
ActionExecutionListeners::~ActionExecutionListeners()
{
for (std::list<ActionExecutionListener*>::iterator i = listeners.begin(); i != listeners.end(); i++)
{
delete *i;
}
listeners.clear();
}
Engine::~Engine(void)
{
Reset();
strategies.clear();
}
void Engine::Reset()
{
ActionNode* action = nullptr;
do
{
action = queue.Pop();
if (!action)
break;
delete action;
} while (true);
for (std::vector<TriggerNode*>::iterator i = triggers.begin(); i != triggers.end(); i++)
{
TriggerNode* trigger = *i;
delete trigger;
}
triggers.clear();
for (std::vector<Multiplier*>::iterator i = multipliers.begin(); i != multipliers.end(); i++)
{
Multiplier* multiplier = *i;
delete multiplier;
}
multipliers.clear();
}
void Engine::Init()
{
Reset();
for (std::map<std::string, Strategy*>::iterator i = strategies.begin(); i != strategies.end(); i++)
{
Strategy* strategy = i->second;
strategy->InitMultipliers(multipliers);
strategy->InitTriggers(triggers);
Event emptyEvent;
MultiplyAndPush(strategy->getDefaultActions(), 0.0f, false, emptyEvent, "default");
}
if (testMode)
{
FILE* file = fopen("test.log", "w");
fprintf(file, "\n");
fclose(file);
}
}
bool Engine::DoNextAction(Unit* unit, uint32 depth, bool minimal)
{
LogAction("--- AI Tick ---");
if (sPlayerbotAIConfig->logValuesPerTick)
LogValues();
bool actionExecuted = false;
ActionBasket* basket = nullptr;
time_t currentTime = time(nullptr);
aiObjectContext->Update();
ProcessTriggers(minimal);
uint32 iterations = 0;
uint32 iterationsPerTick = queue.Size() * (minimal ? 2 : sPlayerbotAIConfig->iterationsPerTick);
do
{
basket = queue.Peek();
if (basket)
{
float relevance = basket->getRelevance(); // just for reference
bool skipPrerequisites = basket->isSkipPrerequisites();
if (minimal && (relevance < 100))
continue;
Event event = basket->getEvent();
// NOTE: queue.Pop() deletes basket
ActionNode* actionNode = queue.Pop();
Action* action = InitializeAction(actionNode);
if (action)
action->setRelevance(relevance);
if (!action)
{
LogAction("A:%s - UNKNOWN", actionNode->getName().c_str());
}
else if (action->isUseful())
{
for (std::vector<Multiplier*>::iterator i = multipliers.begin(); i!= multipliers.end(); i++)
{
Multiplier* multiplier = *i;
relevance *= multiplier->GetValue(action);
action->setRelevance(relevance);
if (!relevance)
{
LogAction("Multiplier %s made action %s useless", multiplier->getName().c_str(), action->getName().c_str());
break;
}
}
if (action->isPossible() && relevance)
{
if (!skipPrerequisites)
{
LogAction("A:%s - PREREQ", action->getName().c_str());
if (MultiplyAndPush(actionNode->getPrerequisites(), relevance + 0.02, false, event, "prereq"))
{
PushAgain(actionNode, relevance + 0.01, event);
continue;
}
}
PerformanceMonitorOperation* pmo = sPerformanceMonitor->start(PERF_MON_ACTION, action->getName(), &aiObjectContext->performanceStack);
actionExecuted = ListenAndExecute(action, event);
if (pmo)
pmo->finish();
if (actionExecuted)
{
LogAction("A:%s - OK", action->getName().c_str());
MultiplyAndPush(actionNode->getContinuers(), 0, false, event, "cont");
lastRelevance = relevance;
delete actionNode;
break;
}
else
{
LogAction("A:%s - FAILED", action->getName().c_str());
MultiplyAndPush(actionNode->getAlternatives(), relevance + 0.03, false, event, "alt");
}
}
else
{
LogAction("A:%s - IMPOSSIBLE", action->getName().c_str());
MultiplyAndPush(actionNode->getAlternatives(), relevance + 0.03, false, event, "alt");
}
}
else
{
lastRelevance = relevance;
LogAction("A:%s - USELESS", action->getName().c_str());
}
delete actionNode;
}
}
while (basket && ++iterations <= iterationsPerTick);
if (!basket)
{
lastRelevance = 0.0f;
PushDefaultActions();
if (queue.Peek() && depth < 2)
return DoNextAction(unit, depth + 1, minimal);
}
// MEMORY FIX TEST
/*
do
{
basket = queue.Peek();
if (basket)
{
// NOTE: queue.Pop() deletes basket
delete queue.Pop();
}
}
while (basket);
*/
if (time(nullptr) - currentTime > 1)
{
LogAction("too long execution");
}
if (!actionExecuted)
LogAction("no actions executed");
queue.RemoveExpired();
return actionExecuted;
}
ActionNode* Engine::CreateActionNode(std::string const name)
{
for (std::map<std::string, Strategy*>::iterator i = strategies.begin(); i != strategies.end(); i++)
{
if (ActionNode* node = i->second->GetAction(name))
return node;
}
return new ActionNode (name,
/*P*/ nullptr,
/*A*/ nullptr,
/*C*/ nullptr);
}
bool Engine::MultiplyAndPush(NextAction** actions, float forceRelevance, bool skipPrerequisites, Event event, char const* pushType)
{
bool pushed = false;
if (actions)
{
for (uint32 j = 0; actions[j]; j++)
{
if (NextAction* nextAction = actions[j])
{
ActionNode* action = CreateActionNode(nextAction->getName());
InitializeAction(action);
float k = nextAction->getRelevance();
if (forceRelevance > 0.0f)
{
k = forceRelevance;
}
if (k > 0)
{
LogAction("PUSH:%s - %f (%s)", action->getName().c_str(), k, pushType);
queue.Push(new ActionBasket(action, k, skipPrerequisites, event));
pushed = true;
}
else
{
delete action;
}
delete nextAction;
}
else
break;
}
delete[] actions;
}
return pushed;
}
ActionResult Engine::ExecuteAction(std::string const name, Event event, std::string const qualifier)
{
bool result = false;
ActionNode* actionNode = CreateActionNode(name);
if (!actionNode)
return ACTION_RESULT_UNKNOWN;
Action* action = InitializeAction(actionNode);
if (!action)
{
delete actionNode;
return ACTION_RESULT_UNKNOWN;
}
if (!qualifier.empty())
{
if (Qualified* q = dynamic_cast<Qualified*>(action))
q->Qualify(qualifier);
}
if (!action->isPossible())
{
delete actionNode;
return ACTION_RESULT_IMPOSSIBLE;
}
if (!action->isUseful())
{
delete actionNode;
return ACTION_RESULT_USELESS;
}
action->MakeVerbose();
result = ListenAndExecute(action, event);
MultiplyAndPush(action->getContinuers(), 0.0f, false, event, "default");
delete actionNode;
return result ? ACTION_RESULT_OK : ACTION_RESULT_FAILED;
}
void Engine::addStrategy(std::string const name)
{
removeStrategy(name);
if (Strategy* strategy = aiObjectContext->GetStrategy(name))
{
std::set<std::string> siblings = aiObjectContext->GetSiblingStrategy(name);
for (std::set<std::string>::iterator i = siblings.begin(); i != siblings.end(); i++)
removeStrategy(*i);
LogAction("S:+%s", strategy->getName().c_str());
strategies[strategy->getName()] = strategy;
}
Init();
}
void Engine::addStrategies(std::string first, ...)
{
addStrategy(first);
va_list vl;
va_start(vl, first);
const char* cur;
do
{
cur = va_arg(vl, const char*);
if (cur)
addStrategy(cur);
}
while (cur);
va_end(vl);
}
bool Engine::removeStrategy(std::string const name)
{
std::map<std::string, Strategy*>::iterator i = strategies.find(name);
if (i == strategies.end())
return false;
LogAction("S:-%s", name.c_str());
strategies.erase(i);
Init();
return true;
}
void Engine::removeAllStrategies()
{
strategies.clear();
Init();
}
void Engine::toggleStrategy(std::string const name)
{
if (!removeStrategy(name))
addStrategy(name);
}
bool Engine::HasStrategy(std::string const name)
{
return strategies.find(name) != strategies.end();
}
void Engine::ProcessTriggers(bool minimal)
{
std::map<Trigger*, Event> fires;
for (std::vector<TriggerNode*>::iterator i = triggers.begin(); i != triggers.end(); i++)
{
TriggerNode* node = *i;
if (!node)
continue;
Trigger* trigger = node->getTrigger();
if (!trigger)
{
trigger = aiObjectContext->GetTrigger(node->getName());
node->setTrigger(trigger);
}
if (!trigger)
continue;
if (testMode || trigger->needCheck())
{
if (minimal && node->getFirstRelevance() < 100)
continue;
PerformanceMonitorOperation* pmo = sPerformanceMonitor->start(PERF_MON_TRIGGER, trigger->getName(), &aiObjectContext->performanceStack);
Event event = trigger->Check();
if (pmo)
pmo->finish();
if (!event)
continue;
fires[trigger] = event;
LogAction("T:%s", trigger->getName().c_str());
}
}
for (std::vector<TriggerNode*>::iterator i = triggers.begin(); i != triggers.end(); i++)
{
TriggerNode* node = *i;
Trigger* trigger = node->getTrigger();
Event event = fires[trigger];
if (!event)
continue;
MultiplyAndPush(node->getHandlers(), 0.0f, false, event, "trigger");
}
for (std::vector<TriggerNode*>::iterator i = triggers.begin(); i != triggers.end(); i++)
{
if (Trigger* trigger = (*i)->getTrigger())
trigger->Reset();
}
}
void Engine::PushDefaultActions()
{
for (std::map<std::string, Strategy*>::iterator i = strategies.begin(); i != strategies.end(); i++)
{
Strategy* strategy = i->second;
Event emptyEvent;
MultiplyAndPush(strategy->getDefaultActions(), 0.0f, false, emptyEvent, "default");
}
}
std::string const Engine::ListStrategies()
{
std::string s = "Strategies: ";
if (strategies.empty())
return std::move(s);
for (std::map<std::string, Strategy*>::iterator i = strategies.begin(); i != strategies.end(); i++)
{
s.append(i->first);
s.append(", ");
}
return s.substr(0, s.length() - 2);
}
std::vector<std::string> Engine::GetStrategies()
{
std::vector<std::string> result;
for (std::map<std::string, Strategy*>::iterator i = strategies.begin(); i != strategies.end(); i++)
{
result.push_back(i->first);
}
return std::move(result);
}
void Engine::PushAgain(ActionNode* actionNode, float relevance, Event event)
{
NextAction** nextAction = new NextAction*[2];
nextAction[0] = new NextAction(actionNode->getName(), relevance);
nextAction[1] = nullptr;
MultiplyAndPush(nextAction, relevance, true, event, "again");
delete actionNode;
}
bool Engine::ContainsStrategy(StrategyType type)
{
for (std::map<std::string, Strategy*>::iterator i = strategies.begin(); i != strategies.end(); i++)
{
if (i->second->GetType() & type)
return true;
}
return false;
}
Action* Engine::InitializeAction(ActionNode* actionNode)
{
Action* action = actionNode->getAction();
if (!action)
{
action = aiObjectContext->GetAction(actionNode->getName());
actionNode->setAction(action);
}
return action;
}
bool Engine::ListenAndExecute(Action* action, Event event)
{
bool actionExecuted = false;
if (actionExecutionListeners.Before(action, event))
{
actionExecuted = actionExecutionListeners.AllowExecution(action, event) ? action->Execute(event) : true;
}
if (botAI->HasStrategy("debug", BOT_STATE_NON_COMBAT))
{
std::ostringstream out;
out << "do: ";
out << action->getName();
if (actionExecuted)
out << " 1 (";
else
out << " 0 (";
out << action->getRelevance() << ")";
if (!event.GetSource().empty())
out << " [" << event.GetSource() << "]";
botAI->TellMasterNoFacing(out);
}
actionExecuted = actionExecutionListeners.OverrideResult(action, actionExecuted, event);
actionExecutionListeners.After(action, actionExecuted, event);
return actionExecuted;
}
void Engine::LogAction(char const* format, ...)
{
char buf[1024];
va_list ap;
va_start(ap, format);
vsprintf(buf, format, ap);
va_end(ap);
lastAction += "|";
lastAction += buf;
if (lastAction.size() > 512)
{
lastAction = lastAction.substr(512);
size_t pos = lastAction.find("|");
lastAction = (pos == std::string::npos ? "" : lastAction.substr(pos));
}
if (testMode)
{
FILE* file = fopen("test.log", "a");
fprintf(file, "'{}'", buf);
fprintf(file, "\n");
fclose(file);
}
else
{
Player* bot = botAI->GetBot();
if (sPlayerbotAIConfig->logInGroupOnly && !bot->GetGroup())
return;
LOG_INFO("playerbots", "{} {}", bot->GetName().c_str(), buf);
}
}
void Engine::ChangeStrategy(std::string const names)
{
std::vector<std::string> splitted = split(names, ',');
for (std::vector<std::string>::iterator i = splitted.begin(); i != splitted.end(); i++)
{
char const* name = i->c_str();
switch (name[0])
{
case '+':
addStrategy(name+1);
break;
case '-':
removeStrategy(name+1);
break;
case '~':
toggleStrategy(name+1);
break;
case '?':
botAI->TellMaster(ListStrategies());
break;
}
}
}
void Engine::LogValues()
{
if (testMode)
return;
Player* bot = botAI->GetBot();
if (sPlayerbotAIConfig->logInGroupOnly && !bot->GetGroup())
return;
std::string const text = botAI->GetAiObjectContext()->FormatValues();
LOG_DEBUG("playerbots", "Values for {}: {}", bot->GetName().c_str(), text.c_str());
}

127
src/strategy/Engine.h Normal file
View File

@@ -0,0 +1,127 @@
/*
* 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.
*/
#ifndef _PLAYERBOT_ENGINE_H
#define _PLAYERBOT_ENGINE_H
#include "Multiplier.h"
#include "Queue.h"
#include "PlayerbotAIAware.h"
#include "Strategy.h"
#include "Trigger.h"
#include <map>
class Action;
class ActionNode;
class AiObjectContext;
class Event;
class NextAction;
class PlayerbotAI;
enum ActionResult
{
ACTION_RESULT_UNKNOWN,
ACTION_RESULT_OK,
ACTION_RESULT_IMPOSSIBLE,
ACTION_RESULT_USELESS,
ACTION_RESULT_FAILED
};
class ActionExecutionListener
{
public:
virtual ~ActionExecutionListener() { };
virtual bool Before(Action* action, Event event) = 0;
virtual bool AllowExecution(Action* action, Event event) = 0;
virtual void After(Action* action, bool executed, Event event) = 0;
virtual bool OverrideResult(Action* action, bool executed, Event event) = 0;
};
class ActionExecutionListeners : public ActionExecutionListener
{
public:
virtual ~ActionExecutionListeners();
bool Before(Action* action, Event event) override;
bool AllowExecution(Action* action, Event event) override;
void After(Action* action, bool executed, Event event) override;
bool OverrideResult(Action* action, bool executed, Event event) override;
void Add(ActionExecutionListener* listener)
{
listeners.push_back(listener);
}
void Remove(ActionExecutionListener* listener)
{
listeners.remove(listener);
}
private:
std::list<ActionExecutionListener*> listeners;
};
class Engine : public PlayerbotAIAware
{
public:
Engine(PlayerbotAI* botAI, AiObjectContext* factory);
void Init();
void addStrategy(std::string const name);
void addStrategies(std::string first, ...);
bool removeStrategy(std::string const name);
bool HasStrategy(std::string const name);
void removeAllStrategies();
void toggleStrategy(std::string const name);
std::string const ListStrategies();
std::vector<std::string> GetStrategies();
bool ContainsStrategy(StrategyType type);
void ChangeStrategy(std::string const names);
std::string const GetLastAction() { return lastAction; }
virtual bool DoNextAction(Unit*, uint32 depth = 0, bool minimal = false);
ActionResult ExecuteAction(std::string const name, Event event = Event(), std::string const qualifier = "");
void AddActionExecutionListener(ActionExecutionListener* listener)
{
actionExecutionListeners.Add(listener);
}
void removeActionExecutionListener(ActionExecutionListener* listener)
{
actionExecutionListeners.Remove(listener);
}
virtual ~Engine(void);
bool testMode;
private:
bool MultiplyAndPush(NextAction** actions, float forceRelevance, bool skipPrerequisites, Event event, const char* pushType);
void Reset();
void ProcessTriggers(bool minimal);
void PushDefaultActions();
void PushAgain(ActionNode* actionNode, float relevance, Event event);
ActionNode* CreateActionNode(std::string const name);
Action* InitializeAction(ActionNode* actionNode);
bool ListenAndExecute(Action* action, Event event);
void LogAction(char const* format, ...);
void LogValues();
ActionExecutionListeners actionExecutionListeners;
protected:
Queue queue;
std::vector<TriggerNode*> triggers;
std::vector<Multiplier*> multipliers;
AiObjectContext* aiObjectContext;
std::map<std::string, Strategy*> strategies;
float lastRelevance;
std::string lastAction;
};
#endif

25
src/strategy/Event.cpp Normal file
View File

@@ -0,0 +1,25 @@
/*
* 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 "Event.h"
#include "Playerbots.h"
Event::Event(std::string const source, ObjectGuid object, Player* owner) : source(source), owner(owner)
{
packet << object;
}
ObjectGuid Event::getObject()
{
if (packet.empty())
return ObjectGuid::Empty;
WorldPacket p(packet);
p.rpos(0);
ObjectGuid guid;
p >> guid;
return guid;
}

38
src/strategy/Event.h Normal file
View File

@@ -0,0 +1,38 @@
/*
* 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.
*/
#ifndef _PLAYERBOT_EVENT_H
#define _PLAYERBOT_EVENT_H
#include "WorldPacket.h"
class ObjectGuid;
class Player;
class Event
{
public:
Event(Event const& other) : source(other.source), param(other.param), packet(other.packet), owner(other.owner) { }
Event() { }
Event(std::string const source) : source(source) { }
Event(std::string const source, std::string const param, Player* owner = nullptr) : source(source), param(param), owner(owner) { }
Event(std::string const source, WorldPacket& packet, Player* owner = nullptr) : source(source), packet(packet), owner(owner) { }
Event(std::string const source, ObjectGuid object, Player* owner = nullptr);
virtual ~Event() { }
std::string const GetSource() { return source; }
std::string const getParam() { return param; }
WorldPacket& getPacket() { return packet; }
ObjectGuid getObject();
Player* getOwner() { return owner; }
bool operator! () const { return source.empty(); }
protected:
std::string source;
std::string param;
WorldPacket packet;
Player* owner = nullptr;
};
#endif

View File

@@ -0,0 +1,64 @@
/*
* 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 "ExternalEventHelper.h"
#include "Trigger.h"
#include "ChatHelper.h"
#include "Playerbots.h"
bool ExternalEventHelper::ParseChatCommand(std::string const command, Player* owner)
{
if (HandleCommand(command, "", owner))
return true;
size_t i = std::string::npos;
while (true)
{
size_t found = command.rfind(" ", i);
if (found == std::string::npos || !found)
break;
std::string const name = command.substr(0, found);
std::string const param = command.substr(found + 1);
i = found - 1;
if (HandleCommand(name, param, owner))
return true;
}
if (!ChatHelper::parseable(command))
return false;
HandleCommand("c", command, owner);
HandleCommand("t", command, owner);
return true;
}
void ExternalEventHelper::HandlePacket(std::map<uint16, std::string>& handlers, WorldPacket const& packet, Player* owner)
{
uint16 opcode = packet.GetOpcode();
std::string const name = handlers[opcode];
if (name.empty())
return;
Trigger* trigger = aiObjectContext->GetTrigger(name);
if (!trigger)
return;
WorldPacket p(packet);
trigger->ExternalEvent(p, owner);
}
bool ExternalEventHelper::HandleCommand(std::string const name, std::string const param, Player* owner)
{
Trigger* trigger = aiObjectContext->GetTrigger(name);
if (!trigger)
return false;
trigger->ExternalEvent(param, owner);
return true;
}

View File

@@ -0,0 +1,29 @@
/*
* 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.
*/
#ifndef _PLAYERBOT_EXTERNALEVENTHELPER_H
#define _PLAYERBOT_EXTERNALEVENTHELPER_H
#include "Common.h"
#include <map>
class AiObjectContext;
class Player;
class WorldPacket;
class ExternalEventHelper
{
public:
ExternalEventHelper(AiObjectContext* aiObjectContext) : aiObjectContext(aiObjectContext) { }
bool ParseChatCommand(std::string const command, Player* owner = nullptr);
void HandlePacket(std::map<uint16, std::string>& handlers, WorldPacket const& packet, Player* owner = nullptr);
bool HandleCommand(std::string const name, std::string const param, Player* owner = nullptr);
private:
AiObjectContext* aiObjectContext;
};
#endif

View File

@@ -0,0 +1,87 @@
/*
* 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 "ItemVisitors.h"
#include "Playerbots.h"
bool FindUsableItemVisitor::Visit(Item* item)
{
if (bot->CanUseItem(item->GetTemplate()) == EQUIP_ERR_OK)
return FindItemVisitor::Visit(item);
return true;
}
bool FindPotionVisitor::Accept(ItemTemplate const* proto)
{
if (proto->Class == ITEM_CLASS_CONSUMABLE && (proto->SubClass == ITEM_SUBCLASS_POTION || proto->SubClass == ITEM_SUBCLASS_FLASK))
{
for (uint8 j = 0; j < MAX_ITEM_PROTO_SPELLS; j++)
{
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(proto->Spells[j].SpellId);
if (!spellInfo)
return false;
for (uint8 i = 0; i < 3; i++)
{
if (spellInfo->Effects[i].Effect == effectId)
return true;
}
}
}
return false;
}
bool FindMountVisitor::Accept(ItemTemplate const* proto)
{
for (uint8 j = 0; j < MAX_ITEM_PROTO_SPELLS; j++)
{
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(proto->Spells[j].SpellId);
if (!spellInfo)
return false;
for (uint8 i = 0; i < 3; i++)
{
if (spellInfo->Effects[i].ApplyAuraName == SPELL_AURA_MOUNTED)
return true;
}
}
return false;
}
bool FindPetVisitor::Accept(ItemTemplate const* proto)
{
if (proto->Class == ITEM_CLASS_MISC)
{
for (uint8 j = 0; j < MAX_ITEM_PROTO_SPELLS; j++)
{
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(proto->Spells[j].SpellId);
if (!spellInfo)
return false;
for (uint8 i = 0; i < 3; i++)
{
if (spellInfo->Effects[i].Effect == SPELL_EFFECT_SUMMON_PET)
return true;
}
}
}
return false;
}
FindItemUsageVisitor::FindItemUsageVisitor(Player* bot, ItemUsage usage) : FindUsableItemVisitor(bot), usage(usage)
{
context = GET_PLAYERBOT_AI(bot)->GetAiObjectContext();
};
bool FindItemUsageVisitor::Accept(ItemTemplate const* proto)
{
if (AI_VALUE2(ItemUsage, "item usage", proto->ItemId) == usage)
return true;
return false;
}

414
src/strategy/ItemVisitors.h Normal file
View File

@@ -0,0 +1,414 @@
/*
* 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.
*/
#ifndef _PLAYERBOT_ITEMVISITORS_H
#define _PLAYERBOT_ITEMVISITORS_H
#include "ChatHelper.h"
#include "Common.h"
#include "Item.h"
#include "ItemUsageValue.h"
class AiObjectContext;
class Player;
char* strstri(char const* str1, char const* str2);
enum IterateItemsMask : uint32
{
ITERATE_ITEMS_IN_BAGS = 1,
ITERATE_ITEMS_IN_EQUIP = 2,
ITERATE_ITEMS_IN_BANK = 4,
ITERATE_ALL_ITEMS = 255
};
class IterateItemsVisitor
{
public:
IterateItemsVisitor() { }
virtual bool Visit(Item* item) = 0;
};
class FindItemVisitor : public IterateItemsVisitor
{
public:
FindItemVisitor() : IterateItemsVisitor(), result() { }
bool Visit(Item* item) override
{
if (!Accept(item->GetTemplate()))
return true;
result.push_back(item);
return true;
}
std::vector<Item*>& GetResult() { return result; }
protected:
virtual bool Accept(ItemTemplate const* proto) = 0;
private:
std::vector<Item*> result;
};
class FindUsableItemVisitor : public FindItemVisitor
{
public:
FindUsableItemVisitor(Player* bot) : FindItemVisitor(), bot(bot) { }
bool Visit(Item* item) override;
private:
Player* bot;
};
class FindItemsByQualityVisitor : public IterateItemsVisitor
{
public:
FindItemsByQualityVisitor(uint32 quality, uint32 count) : IterateItemsVisitor(), quality(quality), count(count) { }
bool Visit(Item* item) override
{
if (item->GetTemplate()->Quality != quality)
return true;
if (result.size() >= (size_t)count)
return false;
result.push_back(item);
return true;
}
std::vector<Item*>& GetResult()
{
return result;
}
private:
uint32 quality;
uint32 count;
std::vector<Item*> result;
};
class FindItemsToTradeByQualityVisitor : public FindItemsByQualityVisitor
{
public:
FindItemsToTradeByQualityVisitor(uint32 quality, uint32 count) : FindItemsByQualityVisitor(quality, count) { }
bool Visit(Item* item) override
{
if (item->IsSoulBound())
return true;
return FindItemsByQualityVisitor::Visit(item);
}
};
class FindItemsToTradeByClassVisitor : public IterateItemsVisitor
{
public:
FindItemsToTradeByClassVisitor(uint32 itemClass, uint32 itemSubClass, uint32 count)
: IterateItemsVisitor(), count(count), itemClass(itemClass), itemSubClass(itemSubClass) { }
bool Visit(Item* item) override
{
if (item->IsSoulBound())
return true;
if (item->GetTemplate()->Class != itemClass || item->GetTemplate()->SubClass != itemSubClass)
return true;
if (result.size() >= (size_t)count)
return false;
result.push_back(item);
return true;
}
std::vector<Item*>& GetResult()
{
return result;
}
private:
uint32 itemClass;
uint32 itemSubClass;
uint32 count;
std::vector<Item*> result;
};
class QueryItemCountVisitor : public IterateItemsVisitor
{
public:
QueryItemCountVisitor(uint32 itemId) : count(0), itemId(itemId) { }
bool Visit(Item* item) override
{
if (item->GetTemplate()->ItemId == itemId)
count += item->GetCount();
return true;
}
uint32 GetCount() { return count; }
protected:
uint32 count;
uint32 itemId;
};
class QueryNamedItemCountVisitor : public QueryItemCountVisitor
{
public:
QueryNamedItemCountVisitor(std::string const name) : QueryItemCountVisitor(0), name(name) { }
bool Visit(Item* item) override
{
ItemTemplate const* proto = item->GetTemplate();
if (proto && proto->Name1.c_str() && strstri(proto->Name1.c_str(), name.c_str()))
count += item->GetCount();
return true;
}
private:
std::string const name;
};
class FindNamedItemVisitor : public FindItemVisitor
{
public:
FindNamedItemVisitor(Player* bot, std::string const name) : FindItemVisitor(), name(name) { }
bool Accept(ItemTemplate const* proto) override
{
return proto && proto->Name1.c_str() && strstri(proto->Name1.c_str(), name.c_str());
}
private:
std::string const name;
};
class FindItemByIdVisitor : public FindItemVisitor
{
public:
FindItemByIdVisitor(uint32 id) : FindItemVisitor(), id(id) { }
bool Accept(ItemTemplate const* proto) override
{
return proto->ItemId == id;
}
private:
uint32 id;
};
class FindItemByIdsVisitor : public FindItemVisitor
{
public:
FindItemByIdsVisitor(ItemIds ids) : FindItemVisitor(), ids(ids) { }
bool Accept(ItemTemplate const* proto) override
{
return ids.find(proto->ItemId) != ids.end();
}
private:
ItemIds ids;
};
class ListItemsVisitor : public IterateItemsVisitor
{
public:
ListItemsVisitor() : IterateItemsVisitor() { }
std::map<uint32, uint32> items;
std::map<uint32, bool> soulbound;
bool Visit(Item* item) override
{
uint32 id = item->GetTemplate()->ItemId;
if (items.find(id) == items.end())
items[id] = 0;
items[id] += item->GetCount();
soulbound[id] = item->IsSoulBound();
return true;
}
};
class ItemCountByQuality : public IterateItemsVisitor
{
public:
ItemCountByQuality() : IterateItemsVisitor()
{
for (uint32 i = 0; i < MAX_ITEM_QUALITY; ++i)
count[i] = 0;
}
bool Visit(Item* item) override
{
++count[item->GetTemplate()->Quality];
return true;
}
public:
std::map<uint32, uint32> count;
};
class FindPotionVisitor : public FindUsableItemVisitor
{
public:
FindPotionVisitor(Player* bot, uint32 effectId) : FindUsableItemVisitor(bot), effectId(effectId) { }
bool Accept(ItemTemplate const* proto) override;
private:
uint32 effectId;
};
class FindFoodVisitor : public FindUsableItemVisitor
{
public:
FindFoodVisitor(Player* bot, uint32 spellCategory, bool conjured = false) : FindUsableItemVisitor(bot),
spellCategory(spellCategory), conjured(conjured) { }
bool Accept(ItemTemplate const* proto) override
{
return proto->Class == ITEM_CLASS_CONSUMABLE && (proto->SubClass == ITEM_SUBCLASS_CONSUMABLE || proto->SubClass == ITEM_SUBCLASS_FOOD) &&
proto->Spells[0].SpellCategory == spellCategory && (!conjured || proto->IsConjuredConsumable());
}
private:
uint32 spellCategory;
bool conjured;
};
class FindMountVisitor : public FindUsableItemVisitor
{
public:
FindMountVisitor(Player* bot) : FindUsableItemVisitor(bot) { }
bool Accept(ItemTemplate const* proto) override;
private:
uint32 effectId;
};
class FindPetVisitor : public FindUsableItemVisitor
{
public:
FindPetVisitor(Player* bot) : FindUsableItemVisitor(bot) { }
bool Accept(ItemTemplate const* proto) override;
};
class FindAmmoVisitor : public FindUsableItemVisitor
{
public:
FindAmmoVisitor(Player* bot, uint32 weaponType) : FindUsableItemVisitor(bot), weaponType(weaponType) { }
bool Accept(ItemTemplate const* proto)override
{
if (proto->Class == ITEM_CLASS_PROJECTILE)
{
uint32 subClass = 0;
switch (weaponType)
{
case ITEM_SUBCLASS_WEAPON_GUN:
subClass = ITEM_SUBCLASS_BULLET;
break;
case ITEM_SUBCLASS_WEAPON_BOW:
case ITEM_SUBCLASS_WEAPON_CROSSBOW:
subClass = ITEM_SUBCLASS_ARROW;
break;
}
if (!subClass)
return false;
if (proto->SubClass == subClass)
return true;
}
return false;
}
private:
uint32 weaponType;
};
class FindQuestItemVisitor : public FindUsableItemVisitor
{
public:
FindQuestItemVisitor(Player* bot) : FindUsableItemVisitor(bot) { }
bool Accept(ItemTemplate const* proto) override
{
if (proto->Class == ITEM_CLASS_QUEST)
{
return true;
}
return false;
}
};
class FindRecipeVisitor : public FindUsableItemVisitor
{
public:
FindRecipeVisitor(Player* bot, SkillType skill = SKILL_NONE) : FindUsableItemVisitor(bot), skill(skill) { };
bool Accept(ItemTemplate const* proto) override
{
if (proto->Class == ITEM_CLASS_RECIPE)
{
if (skill == SKILL_NONE)
return true;
switch (proto->SubClass)
{
case ITEM_SUBCLASS_LEATHERWORKING_PATTERN:
return skill == SKILL_LEATHERWORKING;
case ITEM_SUBCLASS_TAILORING_PATTERN:
return skill == SKILL_TAILORING;
case ITEM_SUBCLASS_ENGINEERING_SCHEMATIC:
return skill == SKILL_ENGINEERING;
case ITEM_SUBCLASS_BLACKSMITHING:
return skill == SKILL_BLACKSMITHING;
case ITEM_SUBCLASS_COOKING_RECIPE:
return skill == SKILL_COOKING;
case ITEM_SUBCLASS_ALCHEMY_RECIPE:
return skill == SKILL_ALCHEMY;
case ITEM_SUBCLASS_FIRST_AID_MANUAL:
return skill == SKILL_FIRST_AID;
case ITEM_SUBCLASS_ENCHANTING_FORMULA:
return skill == SKILL_ENCHANTING;
case ITEM_SUBCLASS_FISHING_MANUAL:
return skill == SKILL_FISHING;
}
}
return false;
}
private:
SkillType skill;
};
class FindItemUsageVisitor : public FindUsableItemVisitor
{
public:
FindItemUsageVisitor(Player* bot, ItemUsage usage = ITEM_USAGE_NONE);
bool Accept(ItemTemplate const* proto) override;
private:
AiObjectContext* context;
ItemUsage usage;
};
#endif

22
src/strategy/Multiplier.h Normal file
View File

@@ -0,0 +1,22 @@
/*
* 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.
*/
#ifndef _PLAYERBOT_MULTIPLIER_H
#define _PLAYERBOT_MULTIPLIER_H
#include "AiObject.h"
class Action;
class PlayerbotAI;
class Multiplier : public AiNamedObject
{
public:
Multiplier(PlayerbotAI* botAI, std::string const name) : AiNamedObject(botAI, name) {}
virtual ~Multiplier() { }
virtual float GetValue(Action* action) { return 1.0f; }
};
#endif

View File

@@ -0,0 +1,33 @@
/*
* 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 "NamedObjectContext.h"
#include "Playerbots.h"
void Qualified::Qualify(int qual)
{
std::ostringstream out;
out << qual;
qualifier = out.str();
}
std::string const Qualified::MultiQualify(std::vector<std::string> qualifiers)
{
std::ostringstream out;
for (auto& qualifier : qualifiers)
out << qualifier << (&qualifier != &qualifiers.back() ? " " : "");
return out.str();
}
std::vector<std::string> Qualified::getMultiQualifiers(std::string const qualifier1)
{
std::istringstream iss(qualifier1);
return { std::istream_iterator<std::string>{iss}, std::istream_iterator<std::string>{} };
}
int32 Qualified::getMultiQualifier(std::string const qualifier1, uint32 pos)
{
return std::stoi(getMultiQualifiers(qualifier1)[pos]);
}

View File

@@ -0,0 +1,282 @@
/*
* 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.
*/
#ifndef _PLAYERBOT_NAMEDOBJECTCONEXT_H
#define _PLAYERBOT_NAMEDOBJECTCONEXT_H
#include "Common.h"
#include <map>
#include <set>
#include <list>
#include <vector>
class PlayerbotAI;
class Qualified
{
public:
Qualified() { };
Qualified(std::string const qualifier) : qualifier(qualifier) { }
Qualified(int32 qualifier1)
{
Qualify(qualifier1);
}
virtual void Qualify(int qual);
virtual void Qualify(std::string const qual)
{
qualifier = qual;
}
std::string const getQualifier() { return qualifier; }
static std::string const MultiQualify(std::vector<std::string> qualifiers);
static std::vector<std::string> getMultiQualifiers(std::string const qualifier1);
static int32 getMultiQualifier(std::string const qualifier1, uint32 pos);
protected:
std::string qualifier;
};
template <class T>
class NamedObjectFactory
{
protected:
typedef T*(*ActionCreator)(PlayerbotAI* botAI);
std::map<std::string, ActionCreator> creators;
public:
T* create(std::string name, PlayerbotAI* botAI)
{
size_t found = name.find("::");
std::string qualifier;
if (found != std::string::npos)
{
qualifier = name.substr(found + 2);
name = name.substr(0, found);
}
if (creators.find(name) == creators.end())
return nullptr;
ActionCreator creator = creators[name];
if (!creator)
return nullptr;
T* object = (*creator)(botAI);
Qualified* q = dynamic_cast<Qualified*>(object);
if (q && found != std::string::npos)
q->Qualify(qualifier);
return object;
}
std::set<std::string> supports()
{
std::set<std::string> keys;
for (typename std::map<std::string, ActionCreator>::iterator it = creators.begin(); it != creators.end(); it++)
keys.insert(it->first);
return std::move(keys);
}
};
template <class T>
class NamedObjectContext : public NamedObjectFactory<T>
{
public:
NamedObjectContext(bool shared = false, bool supportsSiblings = false) :
NamedObjectFactory<T>(), shared(shared), supportsSiblings(supportsSiblings) { }
virtual ~NamedObjectContext()
{
Clear();
}
T* create(std::string const name, PlayerbotAI* botAI)
{
if (created.find(name) == created.end())
return created[name] = NamedObjectFactory<T>::create(name, botAI);
return created[name];
}
void Clear()
{
for (typename std::map<std::string, T*>::iterator i = created.begin(); i != created.end(); i++)
{
if (i->second)
delete i->second;
}
created.clear();
}
void Update()
{
for (typename std::map<std::string, T*>::iterator i = created.begin(); i != created.end(); i++)
{
if (i->second)
i->second->Update();
}
}
void Reset()
{
for (typename std::map<std::string, T*>::iterator i = created.begin(); i != created.end(); i++)
{
if (i->second)
i->second->Reset();
}
}
bool IsShared() { return shared; }
bool IsSupportsSiblings() { return supportsSiblings; }
std::set<std::string> GetCreated()
{
std::set<std::string> keys;
for (typename std::map<std::string, T*>::iterator it = created.begin(); it != created.end(); it++)
keys.insert(it->first);
return std::move(keys);
}
protected:
std::map<std::string, T*> created;
bool shared;
bool supportsSiblings;
};
template <class T>
class NamedObjectContextList
{
public:
virtual ~NamedObjectContextList()
{
for (typename std::vector<NamedObjectContext<T>*>::iterator i = contexts.begin(); i != contexts.end(); i++)
{
NamedObjectContext<T>* context = *i;
if (!context->IsShared())
delete context;
}
}
void Add(NamedObjectContext<T>* context)
{
contexts.push_back(context);
}
T* GetContextObject(std::string const name, PlayerbotAI* botAI)
{
for (typename std::vector<NamedObjectContext<T>*>::iterator i = contexts.begin(); i != contexts.end(); i++)
{
if (T* object = (*i)->create(name, botAI))
return object;
}
return nullptr;
}
void Update()
{
for (typename std::vector<NamedObjectContext<T>*>::iterator i = contexts.begin(); i != contexts.end(); i++)
{
if (!(*i)->IsShared())
(*i)->Update();
}
}
void Reset()
{
for (typename std::vector<NamedObjectContext<T>*>::iterator i = contexts.begin(); i != contexts.end(); i++)
{
(*i)->Reset();
}
}
std::set<std::string> GetSiblings(std::string const name)
{
for (typename std::vector<NamedObjectContext<T>*>::iterator i = contexts.begin(); i != contexts.end(); i++)
{
if (!(*i)->IsSupportsSiblings())
continue;
std::set<std::string> supported = (*i)->supports();
std::set<std::string>::iterator found = supported.find(name);
if (found == supported.end())
continue;
supported.erase(found);
return supported;
}
return std::move(std::set<std::string>());
}
std::set<std::string> supports()
{
std::set<std::string> result;
for (typename std::vector<NamedObjectContext<T>*>::iterator i = contexts.begin(); i != contexts.end(); i++)
{
std::set<std::string> supported = (*i)->supports();
for (std::set<std::string>::iterator j = supported.begin(); j != supported.end(); j++)
result.insert(*j);
}
return std::move(result);
}
std::set<std::string> GetCreated()
{
std::set<std::string> result;
for (typename std::vector<NamedObjectContext<T>*>::iterator i = contexts.begin(); i != contexts.end(); i++)
{
std::set<std::string> createdKeys = (*i)->GetCreated();
for (std::set<std::string>::iterator j = createdKeys.begin(); j != createdKeys.end(); j++)
result.insert(*j);
}
return std::move(result);
}
private:
std::vector<NamedObjectContext<T>*> contexts;
};
template <class T>
class NamedObjectFactoryList
{
public:
virtual ~NamedObjectFactoryList()
{
for (typename std::list<NamedObjectFactory<T>*>::iterator i = factories.begin(); i != factories.end(); i++)
delete *i;
}
void Add(NamedObjectFactory<T>* context)
{
factories.push_front(context);
}
T* GetContextObject(std::string const name, PlayerbotAI* botAI)
{
for (typename std::list<NamedObjectFactory<T>*>::iterator i = factories.begin(); i != factories.end(); i++)
{
if (T* object = (*i)->create(name, botAI))
return object;
}
return nullptr;
}
private:
std::list<NamedObjectFactory<T>*> factories;
};
#endif

View File

@@ -0,0 +1,50 @@
/*
* 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 "PassiveMultiplier.h"
#include "Action.h"
#include "AiObjectContext.h"
std::vector<std::string> PassiveMultiplier::allowedActions;
std::vector<std::string> PassiveMultiplier::allowedParts;
PassiveMultiplier::PassiveMultiplier(PlayerbotAI* botAI) : Multiplier(botAI, "passive")
{
if (allowedActions.empty())
{
allowedActions.push_back("co");
allowedActions.push_back("nc");
allowedActions.push_back("reset botAI");
allowedActions.push_back("check mount state");
}
if (allowedParts.empty())
{
allowedParts.push_back("follow");
allowedParts.push_back("stay");
allowedParts.push_back("chat shortcut");
}
}
float PassiveMultiplier::GetValue(Action* action)
{
if (!action)
return 1.0f;
std::string const name = action->getName();
for (std::vector<std::string>::iterator i = allowedActions.begin(); i != allowedActions.end(); i++)
{
if (name == *i)
return 1.0f;
}
for (std::vector<std::string>::iterator i = allowedParts.begin(); i != allowedParts.end(); i++)
{
if (name.find(*i) != std::string::npos)
return 1.0f;
}
return 0;
}

View File

@@ -0,0 +1,27 @@
/*
* 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.
*/
#ifndef _PLAYERBOT_PASSIVEMULTIPLIER_H
#define _PLAYERBOT_PASSIVEMULTIPLIER_H
#include "Multiplier.h"
#include <vector>
class Action;
class PlayerbotAI;
class PassiveMultiplier : public Multiplier
{
public:
PassiveMultiplier(PlayerbotAI* botAI);
float GetValue(Action* action) override;
private:
static std::vector<std::string> allowedActions;
static std::vector<std::string> allowedParts;
};
#endif

106
src/strategy/Queue.cpp Normal file
View File

@@ -0,0 +1,106 @@
/*
* 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 "Queue.h"
#include "AiObjectContext.h"
#include "Log.h"
#include "PlayerbotAIConfig.h"
void Queue::Push(ActionBasket *action)
{
if (action)
{
for (std::list<ActionBasket*>::iterator iter = actions.begin(); iter != actions.end(); iter++)
{
ActionBasket* basket = *iter;
if (action->getAction()->getName() == basket->getAction()->getName())
{
if (basket->getRelevance() < action->getRelevance())
basket->setRelevance(action->getRelevance());
if (ActionNode* actionNode = action->getAction())
delete actionNode;
delete action;
return;
}
}
actions.push_back(action);
}
}
ActionNode* Queue::Pop()
{
float max = -1;
ActionBasket* selection = nullptr;
for (std::list<ActionBasket*>::iterator iter = actions.begin(); iter != actions.end(); iter++)
{
ActionBasket* basket = *iter;
if (basket->getRelevance() > max)
{
max = basket->getRelevance();
selection = basket;
}
}
if (selection != nullptr)
{
ActionNode* action = selection->getAction();
actions.remove(selection);
delete selection;
return action;
}
return nullptr;
}
ActionBasket* Queue::Peek()
{
float max = -1;
ActionBasket* selection = nullptr;
for (std::list<ActionBasket*>::iterator iter = actions.begin(); iter != actions.end(); iter++)
{
ActionBasket* basket = *iter;
if (basket->getRelevance() > max)
{
max = basket->getRelevance();
selection = basket;
}
}
return selection;
}
uint32 Queue::Size()
{
return actions.size();
}
void Queue::RemoveExpired()
{
std::list<ActionBasket*> expired;
for (std::list<ActionBasket*>::iterator iter = actions.begin(); iter != actions.end(); iter++)
{
ActionBasket* basket = *iter;
if (sPlayerbotAIConfig->expireActionTime && basket->isExpired(sPlayerbotAIConfig->expireActionTime / 1000))
expired.push_back(basket);
}
for (std::list<ActionBasket*>::iterator iter = expired.begin(); iter != expired.end(); iter++)
{
ActionBasket* basket = *iter;
actions.remove(basket);
if (ActionNode* action = basket->getAction())
{
LOG_DEBUG("playerbots", "Action {} is expired", action->getName().c_str());
delete action;
}
delete basket;
}
}

27
src/strategy/Queue.h Normal file
View File

@@ -0,0 +1,27 @@
/*
* 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.
*/
#ifndef _PLAYERBOT_QUEUE_H
#define _PLAYERBOT_QUEUE_H
#include "Common.h"
#include "Action.h"
class Queue
{
public:
Queue(void) { }
~Queue(void) { }
void Push(ActionBasket *action);
ActionNode* Pop();
ActionBasket* Peek();
uint32 Size();
void RemoveExpired();
private:
std::list<ActionBasket*> actions;
};
#endif

125
src/strategy/Strategy.cpp Normal file
View File

@@ -0,0 +1,125 @@
/*
* 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 "Strategy.h"
#include "Playerbots.h"
class ActionNodeFactoryInternal : public NamedObjectFactory<ActionNode>
{
public:
ActionNodeFactoryInternal()
{
creators["melee"] = &melee;
creators["healthstone"] = &healthstone;
creators["be near"] = &follow_master_random;
creators["attack anything"] = &attack_anything;
creators["move random"] = &move_random;
creators["move to loot"] = &move_to_loot;
creators["food"] = &food;
creators["drink"] = &drink;
creators["mana potion"] = &mana_potion;
creators["healing potion"] = &healing_potion;
creators["flee"] = &flee;
}
private:
static ActionNode* melee(PlayerbotAI* botAI)
{
return new ActionNode ("melee",
/*P*/ nullptr,
/*A*/ nullptr,
/*C*/ nullptr);
}
static ActionNode* healthstone(PlayerbotAI* botAI)
{
return new ActionNode ("healthstone",
/*P*/ nullptr,
/*A*/ NextAction::array(0, new NextAction("healing potion"), nullptr),
/*C*/ nullptr);
}
static ActionNode* follow_master_random(PlayerbotAI* botAI)
{
return new ActionNode ("be near",
/*P*/ nullptr,
/*A*/ NextAction::array(0, new NextAction("follow"), nullptr),
/*C*/ nullptr);
}
static ActionNode* attack_anything(PlayerbotAI* botAI)
{
return new ActionNode ("attack anything",
/*P*/ nullptr,
/*A*/ nullptr,
/*C*/ nullptr);
}
static ActionNode* move_random(PlayerbotAI* botAI)
{
return new ActionNode ("move random",
/*P*/ nullptr,
/*A*/ NextAction::array(0, new NextAction("stay line"), nullptr),
/*C*/ nullptr);
}
static ActionNode* move_to_loot(PlayerbotAI* botAI)
{
return new ActionNode ("move to loot",
/*P*/ nullptr,
/*A*/ nullptr,
/*C*/ nullptr);
}
static ActionNode* food(PlayerbotAI* botAI)
{
return new ActionNode ("food",
/*P*/ nullptr,
/*A*/ nullptr,
/*C*/ nullptr);
}
static ActionNode* drink(PlayerbotAI* botAI)
{
return new ActionNode ("drink",
/*P*/ nullptr,
/*A*/ nullptr,
/*C*/ nullptr);
}
static ActionNode* mana_potion(PlayerbotAI* botAI)
{
return new ActionNode ("mana potion",
/*P*/ nullptr,
/*A*/ NextAction::array(0, new NextAction("drink"), nullptr),
/*C*/ nullptr);
}
static ActionNode* healing_potion(PlayerbotAI* botAI)
{
return new ActionNode ("healing potion",
/*P*/ nullptr,
/*A*/ NextAction::array(0, new NextAction("food"), nullptr),
/*C*/ nullptr);
}
static ActionNode* flee(PlayerbotAI* botAI)
{
return new ActionNode ("flee",
/*P*/ nullptr,
/*A*/ nullptr,
/*C*/ nullptr);
}
};
Strategy::Strategy(PlayerbotAI* botAI) : PlayerbotAIAware(botAI)
{
actionNodeFactories.Add(new ActionNodeFactoryInternal());
}
ActionNode* Strategy::GetAction(std::string const name)
{
return actionNodeFactories.GetContextObject(name, botAI);
}

59
src/strategy/Strategy.h Normal file
View File

@@ -0,0 +1,59 @@
/*
* 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.
*/
#ifndef _PLAYERBOT_STRATEGY_H
#define _PLAYERBOT_STRATEGY_H
#include "Action.h"
#include "NamedObjectContext.h"
#include "Multiplier.h"
#include "Trigger.h"
#include "PlayerbotAIAware.h"
enum StrategyType : uint32
{
STRATEGY_TYPE_GENERIC = 0,
STRATEGY_TYPE_COMBAT = 1,
STRATEGY_TYPE_NONCOMBAT = 2,
STRATEGY_TYPE_TANK = 4,
STRATEGY_TYPE_DPS = 8,
STRATEGY_TYPE_HEAL = 16,
STRATEGY_TYPE_RANGED = 32,
STRATEGY_TYPE_MELEE = 64
};
enum ActionPriority
{
ACTION_IDLE = 0,
ACTION_NORMAL = 10,
ACTION_HIGH = 20,
ACTION_MOVE = 30,
ACTION_INTERRUPT = 40,
ACTION_DISPEL = 50,
ACTION_LIGHT_HEAL = 60,
ACTION_MEDIUM_HEAL = 70,
ACTION_CRITICAL_HEAL = 80,
ACTION_EMERGENCY = 90
};
class Strategy : public PlayerbotAIAware
{
public:
Strategy(PlayerbotAI* botAI);
virtual ~Strategy() { }
virtual NextAction** getDefaultActions() { return nullptr; }
virtual void InitTriggers(std::vector<TriggerNode*> &triggers) { }
virtual void InitMultipliers(std::vector<Multiplier*> &multipliers) { }
virtual std::string const getName() = 0;
virtual uint32 GetType() const { return STRATEGY_TYPE_GENERIC; }
virtual ActionNode* GetAction(std::string const name);
void Update() { }
void Reset() { }
protected:
NamedObjectFactoryList<ActionNode> actionNodeFactories;
};
#endif

View File

@@ -0,0 +1,221 @@
/*
* 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.
*/
#ifndef _PLAYERBOT_STRATEGYCONTEXT_H
#define _PLAYERBOT_STRATEGYCONTEXT_H
#include "CustomStrategy.h"
#include "NamedObjectContext.h"
#include "AttackEnemyPlayersStrategy.h"
#include "BattlegroundStrategy.h"
#include "CastTimeStrategy.h"
#include "ChatCommandHandlerStrategy.h"
#include "ConserveManaStrategy.h"
#include "DeadStrategy.h"
#include "DebugStrategy.h"
#include "DpsAssistStrategy.h"
#include "DuelStrategy.h"
#include "EmoteStrategy.h"
#include "FleeStrategy.h"
#include "FollowMasterStrategy.h"
#include "GrindingStrategy.h"
#include "GroupStrategy.h"
#include "GuardStrategy.h"
#include "GuildStrategy.h"
#include "KiteStrategy.h"
#include "LfgStrategy.h"
#include "LootNonCombatStrategy.h"
#include "MaintenanceStrategy.h"
#include "MarkRtiStrategy.h"
#include "MeleeCombatStrategy.h"
#include "NonCombatStrategy.h"
#include "QuestStrategies.h"
#include "PassiveStrategy.h"
#include "PullStrategy.h"
#include "RacialsStrategy.h"
#include "RangedCombatStrategy.h"
#include "ReturnStrategy.h"
#include "RpgStrategy.h"
#include "RTSCStrategy.h"
#include "RunawayStrategy.h"
#include "StayStrategy.h"
#include "TankAssistStrategy.h"
#include "TellTargetStrategy.h"
#include "ThreatStrategy.h"
#include "TravelStrategy.h"
#include "UseFoodStrategy.h"
#include "UsePotionsStrategy.h"
#include "WorldPacketHandlerStrategy.h"
class StrategyContext : public NamedObjectContext<Strategy>
{
public:
StrategyContext()
{
creators["racials"] = &StrategyContext::racials;
creators["loot"] = &StrategyContext::loot;
creators["gather"] = &StrategyContext::gather;
creators["emote"] = &StrategyContext::emote;
creators["passive"] = &StrategyContext::passive;
creators["conserve mana"] = &StrategyContext::conserve_mana;
creators["food"] = &StrategyContext::food;
creators["chat"] = &StrategyContext::chat;
creators["default"] = &StrategyContext::world_packet;
creators["ready check"] = &StrategyContext::ready_check;
creators["dead"] = &StrategyContext::dead;
creators["flee"] = &StrategyContext::flee;
creators["duel"] = &StrategyContext::duel;
creators["start duel"] = &StrategyContext::start_duel;
creators["kite"] = &StrategyContext::kite;
creators["potions"] = &StrategyContext::potions;
creators["cast time"] = &StrategyContext::cast_time;
creators["threat"] = &StrategyContext::threat;
creators["tell target"] = &StrategyContext::tell_target;
creators["pvp"] = &StrategyContext::pvp;
creators["return"] = &StrategyContext::_return;
creators["lfg"] = &StrategyContext::lfg;
creators["custom"] = &StrategyContext::custom;
creators["reveal"] = &StrategyContext::reveal;
creators["collision"] = &StrategyContext::collision;
creators["rpg"] = &StrategyContext::rpg;
creators["travel"] = &StrategyContext::travel;
creators["explore"] = &StrategyContext::explore;
creators["map"] = &StrategyContext::map;
creators["map full"] = &StrategyContext::map_full;
creators["sit"] = &StrategyContext::sit;
creators["mark rti"] = &StrategyContext::mark_rti;
creators["ads"] = &StrategyContext::possible_adds;
creators["close"] = &StrategyContext::close;
creators["ranged"] = &StrategyContext::ranged;
creators["behind"] = &StrategyContext::behind;
creators["bg"] = &StrategyContext::bg;
creators["Battleground"] = &StrategyContext::Battleground;
creators["warsong"] = &StrategyContext::warsong;
creators["alterac"] = &StrategyContext::alterac;
creators["arathi"] = &StrategyContext::arathi;
creators["eye"] = &StrategyContext::eye;
creators["isle"] = &StrategyContext::isle;
creators["arena"] = &StrategyContext::arena;
creators["mount"] = &StrategyContext::mount;
creators["rtsc"] = &StrategyContext::rtsc;
creators["attack tagged"] = &StrategyContext::attack_tagged;
creators["debug"] = &StrategyContext::debug;
creators["debug move"] = &StrategyContext::debug_move;
creators["debug rpg"] = &StrategyContext::debug_rpg;
creators["debug spell"] = &StrategyContext::debug_spell;
creators["maintenance"] = &StrategyContext::maintenance;
creators["group"] = &StrategyContext::group;
creators["guild"] = &StrategyContext::guild;
creators["grind"] = &StrategyContext::grind;
}
private:
static Strategy* behind(PlayerbotAI* botAI) { return new SetBehindCombatStrategy(botAI); }
static Strategy* ranged(PlayerbotAI* botAI) { return new RangedCombatStrategy(botAI); }
static Strategy* close(PlayerbotAI* botAI) { return new MeleeCombatStrategy(botAI); }
static Strategy* mark_rti(PlayerbotAI* botAI) { return new MarkRtiStrategy(botAI); }
static Strategy* tell_target(PlayerbotAI* botAI) { return new TellTargetStrategy(botAI); }
static Strategy* threat(PlayerbotAI* botAI) { return new ThreatStrategy(botAI); }
static Strategy* cast_time(PlayerbotAI* botAI) { return new CastTimeStrategy(botAI); }
static Strategy* potions(PlayerbotAI* botAI) { return new UsePotionsStrategy(botAI); }
static Strategy* kite(PlayerbotAI* botAI) { return new KiteStrategy(botAI); }
static Strategy* duel(PlayerbotAI* botAI) { return new DuelStrategy(botAI); }
static Strategy* start_duel(PlayerbotAI* botAI) { return new StartDuelStrategy(botAI); }
static Strategy* flee(PlayerbotAI* botAI) { return new FleeStrategy(botAI); }
static Strategy* dead(PlayerbotAI* botAI) { return new DeadStrategy(botAI); }
static Strategy* racials(PlayerbotAI* botAI) { return new RacialsStrategy(botAI); }
static Strategy* loot(PlayerbotAI* botAI) { return new LootNonCombatStrategy(botAI); }
static Strategy* gather(PlayerbotAI* botAI) { return new GatherStrategy(botAI); }
static Strategy* emote(PlayerbotAI* botAI) { return new EmoteStrategy(botAI); }
static Strategy* passive(PlayerbotAI* botAI) { return new PassiveStrategy(botAI); }
static Strategy* conserve_mana(PlayerbotAI* botAI) { return new ConserveManaStrategy(botAI); }
static Strategy* food(PlayerbotAI* botAI) { return new UseFoodStrategy(botAI); }
static Strategy* chat(PlayerbotAI* botAI) { return new ChatCommandHandlerStrategy(botAI); }
static Strategy* world_packet(PlayerbotAI* botAI) { return new WorldPacketHandlerStrategy(botAI); }
static Strategy* ready_check(PlayerbotAI* botAI) { return new ReadyCheckStrategy(botAI); }
static Strategy* pvp(PlayerbotAI* botAI) { return new AttackEnemyPlayersStrategy(botAI); }
static Strategy* _return(PlayerbotAI* botAI) { return new ReturnStrategy(botAI); }
static Strategy* lfg(PlayerbotAI* botAI) { return new LfgStrategy(botAI); }
static Strategy* custom(PlayerbotAI* botAI) { return new CustomStrategy(botAI); }
static Strategy* reveal(PlayerbotAI* botAI) { return new RevealStrategy(botAI); }
static Strategy* collision(PlayerbotAI* botAI) { return new CollisionStrategy(botAI); }
static Strategy* rpg(PlayerbotAI* botAI) { return new RpgStrategy(botAI); }
static Strategy* travel(PlayerbotAI* botAI) { return new TravelStrategy(botAI); }
static Strategy* explore(PlayerbotAI* botAI) { return new ExploreStrategy(botAI); }
static Strategy* map(PlayerbotAI* botAI) { return new MapStrategy(botAI); }
static Strategy* map_full(PlayerbotAI* botAI) { return new MapFullStrategy(botAI); }
static Strategy* sit(PlayerbotAI* botAI) { return new SitStrategy(botAI); }
static Strategy* possible_adds(PlayerbotAI* botAI) { return new PossibleAddsStrategy(botAI); }
static Strategy* mount(PlayerbotAI* botAI) { return new MountStrategy(botAI); }
static Strategy* bg(PlayerbotAI* botAI) { return new BGStrategy(botAI); }
static Strategy* Battleground(PlayerbotAI* botAI) { return new BattlegroundStrategy(botAI); }
static Strategy* warsong(PlayerbotAI* botAI) { return new WarsongStrategy(botAI); }
static Strategy* alterac(PlayerbotAI* botAI) { return new AlteracStrategy(botAI); }
static Strategy* arathi(PlayerbotAI* botAI) { return new ArathiStrategy(botAI); }
static Strategy* eye(PlayerbotAI* botAI) { return new EyeStrategy(botAI); }
static Strategy* isle(PlayerbotAI* botAI) { return new IsleStrategy(botAI); }
static Strategy* arena(PlayerbotAI* botAI) { return new ArenaStrategy(botAI); }
static Strategy* rtsc(PlayerbotAI* botAI) { return new RTSCStrategy(botAI); }
static Strategy* attack_tagged(PlayerbotAI* botAI) { return new AttackTaggedStrategy(botAI); }
static Strategy* debug(PlayerbotAI* botAI) { return new DebugStrategy(botAI); }
static Strategy* debug_move(PlayerbotAI* botAI) { return new DebugMoveStrategy(botAI); }
static Strategy* debug_rpg(PlayerbotAI* botAI) { return new DebugRpgStrategy(botAI); }
static Strategy* debug_spell(PlayerbotAI* botAI) { return new DebugSpellStrategy(botAI); }
static Strategy* maintenance(PlayerbotAI* botAI) { return new MaintenanceStrategy(botAI); }
static Strategy* group(PlayerbotAI* botAI) { return new GroupStrategy(botAI); }
static Strategy* guild (PlayerbotAI* botAI) { return new GuildStrategy(botAI); }
static Strategy* grind(PlayerbotAI* botAI) { return new GrindingStrategy(botAI); }
};
class MovementStrategyContext : public NamedObjectContext<Strategy>
{
public:
MovementStrategyContext() : NamedObjectContext<Strategy>(false, true)
{
creators["follow"] = &MovementStrategyContext::follow_master;
creators["stay"] = &MovementStrategyContext::stay;
creators["runaway"] = &MovementStrategyContext::runaway;
creators["flee from adds"] = &MovementStrategyContext::flee_from_adds;
creators["guard"] = &MovementStrategyContext::guard;
}
private:
static Strategy* guard(PlayerbotAI* botAI) { return new GuardStrategy(botAI); }
static Strategy* follow_master(PlayerbotAI* botAI) { return new FollowMasterStrategy(botAI); }
static Strategy* stay(PlayerbotAI* botAI) { return new StayStrategy(botAI); }
static Strategy* runaway(PlayerbotAI* botAI) { return new RunawayStrategy(botAI); }
static Strategy* flee_from_adds(PlayerbotAI* botAI) { return new FleeFromAddsStrategy(botAI); }
};
class AssistStrategyContext : public NamedObjectContext<Strategy>
{
public:
AssistStrategyContext() : NamedObjectContext<Strategy>(false, true)
{
creators["dps assist"] = &AssistStrategyContext::dps_assist;
creators["dps aoe"] = &AssistStrategyContext::dps_aoe;
creators["tank assist"] = &AssistStrategyContext::tank_assist;
}
private:
static Strategy* dps_assist(PlayerbotAI* botAI) { return new DpsAssistStrategy(botAI); }
static Strategy* dps_aoe(PlayerbotAI* botAI) { return new DpsAoeStrategy(botAI); }
static Strategy* tank_assist(PlayerbotAI* botAI) { return new TankAssistStrategy(botAI); }
};
class QuestStrategyContext : public NamedObjectContext<Strategy>
{
public:
QuestStrategyContext() : NamedObjectContext<Strategy>(false, true)
{
creators["quest"] = &QuestStrategyContext::quest;
creators["accept all quests"] = &QuestStrategyContext::accept_all_quests;
}
private:
static Strategy* quest(PlayerbotAI* botAI) { return new DefaultQuestStrategy(botAI); }
static Strategy* accept_all_quests(PlayerbotAI* botAI) { return new AcceptAllQuestsStrategy(botAI); }
};
#endif

49
src/strategy/Trigger.cpp Normal file
View File

@@ -0,0 +1,49 @@
/*
* 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 "Trigger.h"
#include "Event.h"
#include "Playerbots.h"
Trigger::Trigger(PlayerbotAI* botAI, std::string const name, int32 checkInterval) :
AiNamedObject(botAI, name), checkInterval(checkInterval), lastCheckTime(time(nullptr) - rand() % checkInterval)
{
}
Event Trigger::Check()
{
if (IsActive())
{
Event event(getName());
return event;
}
Event event;
return event;
}
Value<Unit*>* Trigger::GetTargetValue()
{
return context->GetValue<Unit*>(GetTargetName());
}
Unit* Trigger::GetTarget()
{
return GetTargetValue()->Get();
}
bool Trigger::needCheck()
{
if (checkInterval < 2)
return true;
time_t now = time(nullptr);
if (!lastCheckTime || now - lastCheckTime >= checkInterval)
{
lastCheckTime = now;
return true;
}
return false;
}

69
src/strategy/Trigger.h Normal file
View File

@@ -0,0 +1,69 @@
/*
* 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.
*/
#ifndef _PLAYERBOT_TRIGGER_H
#define _PLAYERBOT_TRIGGER_H
#include "Common.h"
#include "Action.h"
class PlayerbotAI;
class Unit;
class Trigger : public AiNamedObject
{
public:
Trigger(PlayerbotAI* botAI, std::string const name = "trigger", int32 checkInterval = 1);
virtual ~Trigger() { }
virtual Event Check();
virtual void ExternalEvent(std::string const param, Player* owner = nullptr) { }
virtual void ExternalEvent(WorldPacket& packet, Player* owner = nullptr) { }
virtual bool IsActive() { return false; }
virtual NextAction** getHandlers() { return nullptr; }
void Update() { }
virtual void Reset() { }
virtual Unit* GetTarget();
virtual Value<Unit*>* GetTargetValue();
virtual std::string const GetTargetName() { return "self target"; }
bool needCheck();
protected:
int32 checkInterval;
time_t lastCheckTime;
};
class TriggerNode
{
public:
TriggerNode(std::string const name, NextAction** handlers = nullptr) : name(name), handlers(handlers), trigger(nullptr) { }
virtual ~TriggerNode()
{
NextAction::destroy(handlers);
}
Trigger* getTrigger() { return trigger; }
void setTrigger(Trigger* trigger) { this->trigger = trigger; }
std::string const getName() { return name; }
NextAction** getHandlers()
{
return NextAction::merge(NextAction::clone(handlers), trigger->getHandlers());
}
float getFirstRelevance()
{
return handlers[0] ? handlers[0]->getRelevance() : -1;
}
private:
Trigger* trigger;
NextAction** handlers;
std::string const name;
};
#endif

111
src/strategy/Value.cpp Normal file
View File

@@ -0,0 +1,111 @@
/*
* 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 "Value.h"
#include "PerformanceMonitor.h"
#include "Playerbots.h"
UnitCalculatedValue::UnitCalculatedValue(PlayerbotAI* botAI, std::string const name, int32 checkInterval) : CalculatedValue<Unit*>(botAI, name, checkInterval)
{
lastCheckTime = time(nullptr) - checkInterval / 2;
}
std::string const UnitCalculatedValue::Format()
{
Unit* unit = Calculate();
return unit ? unit->GetName() : "<none>";
}
std::string const UnitManualSetValue::Format()
{
Unit* unit = Get();
return unit ? unit->GetName() : "<none>";
}
std::string const Uint8CalculatedValue::Format()
{
std::ostringstream out;
out << Calculate();
return out.str();
}
std::string const Uint32CalculatedValue::Format()
{
std::ostringstream out;
out << Calculate();
return out.str();
}
std::string const FloatCalculatedValue::Format()
{
std::ostringstream out;
out << Calculate();
return out.str();
}
CDPairCalculatedValue::CDPairCalculatedValue(PlayerbotAI* botAI, std::string const name, int32 checkInterval) :
CalculatedValue<CreatureData const*>(botAI, name, checkInterval)
{
lastCheckTime = time(nullptr) - checkInterval / 2;
}
std::string const CDPairCalculatedValue::Format()
{
CreatureData const* creatureData = Calculate();
CreatureTemplate const* bmTemplate = sObjectMgr->GetCreatureTemplate(creatureData->id1);
return bmTemplate ? bmTemplate->Name : "<none>";
}
CDPairListCalculatedValue::CDPairListCalculatedValue(PlayerbotAI* botAI, std::string const name, int32 checkInterval) :
CalculatedValue<std::vector<CreatureData const*>>(botAI, name, checkInterval)
{
lastCheckTime = time(nullptr) - checkInterval / 2;
}
std::string const CDPairListCalculatedValue::Format()
{
std::ostringstream out; out << "{";
std::vector<CreatureData const*> cdPairs = Calculate();
for (CreatureData const* cdPair : cdPairs)
{
out << cdPair->id1 << ",";
}
out << "}";
return out.str();
}
ObjectGuidCalculatedValue::ObjectGuidCalculatedValue(PlayerbotAI* botAI, std::string const name, int32 checkInterval) :
CalculatedValue<ObjectGuid>(botAI, name, checkInterval)
{
lastCheckTime = time(nullptr) - checkInterval / 2;
}
std::string const ObjectGuidCalculatedValue::Format()
{
ObjectGuid guid = Calculate();
return guid ? std::to_string(guid.GetRawValue()) : "<none>";
}
ObjectGuidListCalculatedValue::ObjectGuidListCalculatedValue(PlayerbotAI* botAI, std::string const name, int32 checkInterval) :
CalculatedValue<GuidVector>(botAI, name, checkInterval)
{
lastCheckTime = time(nullptr) - checkInterval / 2;
}
std::string const ObjectGuidListCalculatedValue::Format()
{
std::ostringstream out;
out << "{";
GuidVector guids = Calculate();
for (GuidVector::iterator i = guids.begin(); i != guids.end(); ++i)
{
ObjectGuid guid = *i;
out << guid.GetRawValue() << ",";
}
out << "}";
return out.str();
}

319
src/strategy/Value.h Normal file
View File

@@ -0,0 +1,319 @@
/*
* 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.
*/
#ifndef _PLAYERBOT_VALUE_H
#define _PLAYERBOT_VALUE_H
#include "AiObject.h"
#include "ObjectGuid.h"
#include "PerformanceMonitor.h"
#include <time.h>
class PlayerbotAI;
class Unit;
struct CreatureData;
class UntypedValue : public AiNamedObject
{
public:
UntypedValue(PlayerbotAI* botAI, std::string const name) : AiNamedObject(botAI, name) { }
virtual void Update() { }
virtual void Reset() { }
virtual std::string const Format() { return "?"; }
virtual std::string const Save() { return "?"; }
virtual bool Load(std::string const value) { return false; }
};
template<class T>
class Value
{
public:
virtual ~Value() { }
virtual T Get() = 0;
virtual T LazyGet() = 0;
virtual void Reset() { }
virtual void Set(T value) = 0;
operator T() { return Get(); }
};
template<class T>
class CalculatedValue : public UntypedValue, public Value<T>
{
public:
CalculatedValue(PlayerbotAI* botAI, std::string const name = "value", uint32 checkInterval = 1) : UntypedValue(botAI, name),
checkInterval(checkInterval), lastCheckTime(0) { }
virtual ~CalculatedValue() { }
T Get() override
{
time_t now = time(nullptr);
if (!lastCheckTime || checkInterval < 2 || now - lastCheckTime >= checkInterval / 2)
{
lastCheckTime = now;
PerformanceMonitorOperation* pmo = sPerformanceMonitor->start(PERF_MON_VALUE, this->getName(), this->context ? &this->context->performanceStack : nullptr);
value = Calculate();
if (pmo)
pmo->finish();
}
return value;
}
T LazyGet() override
{
if (!lastCheckTime)
return Get();
return value;
}
void Set(T val) override { value = val; }
void Update() override {}
void Reset() override { lastCheckTime = 0; }
protected:
virtual T Calculate() = 0;
uint32 checkInterval;
time_t lastCheckTime;
T value;
};
template <class T>
class SingleCalculatedValue : public CalculatedValue<T>
{
public:
SingleCalculatedValue(PlayerbotAI* botAI, std::string const name = "value") : CalculatedValue<T>(botAI, name)
{
this->Reset();
}
T Get() override
{
time_t now = time(0);
if (!this->lastCheckTime)
{
this->lastCheckTime = now;
PerformanceMonitorOperation* pmo = sPerformanceMonitor->start(PERF_MON_VALUE, this->getName(), this->context ? &this->context->performanceStack : nullptr);
this->value = this->Calculate();
if (pmo)
pmo->finish();
}
return this->value;
}
};
template<class T>
class MemoryCalculatedValue : public CalculatedValue<T>
{
public:
MemoryCalculatedValue(PlayerbotAI* botAI, std::string const name = "value", int32 checkInterval = 1) : CalculatedValue<T>(botAI, name, checkInterval)
{
lastChangeTime = time(0);
}
virtual bool EqualToLast(T value) = 0;
virtual bool CanCheckChange()
{
return time(0) - lastChangeTime < minChangeInterval || EqualToLast(this->value);
}
virtual bool UpdateChange()
{
if (CanCheckChange())
return false;
lastChangeTime = time(0);
lastValue = this->value;
return true;
}
void Set(T value) override
{
CalculatedValue<T>::Set(this->value);
UpdateChange();
}
T Get() override
{
this->value = CalculatedValue<T>::Get();
UpdateChange();
return this->value;
}
T LazyGet() override
{
return this->value;
}
time_t LastChangeOn()
{
Get();
UpdateChange();
return lastChangeTime;
}
uint32 LastChangeDelay() { return time(0) - LastChangeOn(); }
void Reset() override
{
CalculatedValue<T>::Reset();
lastChangeTime = time(0);
}
protected:
T lastValue;
uint32 minChangeInterval = 0;
time_t lastChangeTime;
};
template<class T>
class LogCalculatedValue : public MemoryCalculatedValue<T>
{
public:
LogCalculatedValue(PlayerbotAI* botAI, std::string const name = "value", int32 checkInterval = 1) : MemoryCalculatedValue<T>(botAI, name, checkInterval) { }
bool UpdateChange() override
{
if (MemoryCalculatedValue<T>::UpdateChange())
return false;
valueLog.push_back(std::make_pair(this->value, time(0)));
if (valueLog.size() > logLength)
valueLog.pop_front();
return true;
}
std::list<std::pair<T, time_t>> ValueLog() { return valueLog; }
void Reset() override
{
MemoryCalculatedValue<T>::Reset();
valueLog.clear();
}
protected:
std::list<std::pair<T, time_t>> valueLog;
uint8 logLength = 10;
};
class Uint8CalculatedValue : public CalculatedValue<uint8>
{
public:
Uint8CalculatedValue(PlayerbotAI* botAI, std::string const name = "value", uint32 checkInterval = 1) :
CalculatedValue<uint8>(botAI, name, checkInterval) { }
std::string const Format() override;
};
class Uint32CalculatedValue : public CalculatedValue<uint32>
{
public:
Uint32CalculatedValue(PlayerbotAI* botAI, std::string const name = "value", int checkInterval = 1) :
CalculatedValue<uint32>(botAI, name, checkInterval) { }
std::string const Format() override;
};
class FloatCalculatedValue : public CalculatedValue<float>
{
public:
FloatCalculatedValue(PlayerbotAI* botAI, std::string const name = "value", int checkInterval = 1) :
CalculatedValue<float>(botAI, name, checkInterval) { }
std::string const Format() override;
};
class BoolCalculatedValue : public CalculatedValue<bool>
{
public:
BoolCalculatedValue(PlayerbotAI* botAI, std::string const name = "value", int checkInterval = 1) :
CalculatedValue<bool>(botAI, name, checkInterval) { }
std::string const Format() override
{
return Calculate() ? "true" : "false";
}
};
class UnitCalculatedValue : public CalculatedValue<Unit*>
{
public:
UnitCalculatedValue(PlayerbotAI* botAI, std::string const name = "value", int32 checkInterval = 1);
std::string const Format() override;
};
class CDPairCalculatedValue : public CalculatedValue<CreatureData const*>
{
public:
CDPairCalculatedValue(PlayerbotAI* botAI, std::string const name = "value", int32 checkInterval = 1);
std::string const Format() override;
};
class CDPairListCalculatedValue : public CalculatedValue<std::vector<CreatureData const*>>
{
public:
CDPairListCalculatedValue(PlayerbotAI* botAI, std::string const name = "value", int32 checkInterval = 1);
std::string const Format() override;
};
class ObjectGuidCalculatedValue : public CalculatedValue<ObjectGuid>
{
public:
ObjectGuidCalculatedValue(PlayerbotAI* botAI, std::string const name = "value", int32 checkInterval = 1);
std::string const Format() override;
};
class ObjectGuidListCalculatedValue : public CalculatedValue<GuidVector>
{
public:
ObjectGuidListCalculatedValue(PlayerbotAI* botAI, std::string const name = "value", int32 checkInterval = 1);
std::string const Format() override;
};
template<class T>
class ManualSetValue : public UntypedValue, public Value<T>
{
public:
ManualSetValue(PlayerbotAI* botAI, T defaultValue, std::string const name = "value") :
UntypedValue(botAI, name), value(defaultValue), defaultValue(defaultValue) { }
virtual ~ManualSetValue() { }
T Get() override { return value; }
T LazyGet() override { return value; }
void Set(T val) override { value = val; }
void Update() override {}
void Reset() override
{
value = defaultValue;
}
protected:
T value;
T defaultValue;
};
class UnitManualSetValue : public ManualSetValue<Unit*>
{
public:
UnitManualSetValue(PlayerbotAI* botAI, Unit* defaultValue, std::string const name = "value") :
ManualSetValue<Unit*>(botAI, defaultValue, name) { }
std::string const Format() override;
};
#endif

View File

@@ -0,0 +1,24 @@
/*
* 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 "AcceptBattlegroundInvitationAction.h"
#include "Event.h"
#include "Playerbots.h"
bool AcceptBgInvitationAction::Execute(Event event)
{
uint8 type = 0; // arenatype if arena
uint8 unk2 = 0; // unk, can be 0x0 (may be if was invited?) and 0x1
uint32 bgTypeId_ = BATTLEGROUND_WS; // type id from dbc
uint16 unk = 0x1F90; // 0x1F90 constant?*/
uint8 action = 1;
WorldPacket packet(CMSG_BATTLEFIELD_PORT, 20);
packet << type << unk2 << (uint32)bgTypeId_ << unk << action;
//packet << bgTypeId_ << action;
bot->GetSession()->HandleBattleFieldPortOpcode(packet);
botAI->ResetStrategies();
return true;
}

View File

@@ -0,0 +1,20 @@
/*
* 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.
*/
#ifndef _PLAYERBOT_ACCEPTBATTLEGROUNDINVITATIONACTION_H
#define _PLAYERBOT_ACCEPTBATTLEGROUNDINVITATIONACTION_H
#include "Action.h"
class PlayerbotAI;
class AcceptBgInvitationAction : public Action
{
public:
AcceptBgInvitationAction(PlayerbotAI* botAI) : Action(botAI, "accept bg invitatio") { }
bool Execute(Event event) override;
};
#endif

View File

@@ -0,0 +1,24 @@
/*
* 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 "AcceptDuelAction.h"
#include "Event.h"
#include "Playerbots.h"
bool AcceptDuelAction::Execute(Event event)
{
WorldPacket p(event.getPacket());
ObjectGuid flagGuid;
p >> flagGuid;
ObjectGuid playerGuid;
p >> playerGuid;
WorldPacket packet(CMSG_DUEL_ACCEPTED, 8);
packet << flagGuid;
bot->GetSession()->HandleDuelAcceptedOpcode(packet);
botAI->ResetStrategies();
return true;
}

View File

@@ -0,0 +1,20 @@
/*
* 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.
*/
#ifndef _PLAYERBOT_ACCEPTDUELACTION_H
#define _PLAYERBOT_ACCEPTDUELACTION_H
#include "Action.h"
class PlayerbotAI;
class AcceptDuelAction : public Action
{
public:
AcceptDuelAction(PlayerbotAI* botAI) : Action(botAI, "accept duel") { }
bool Execute(Event event) override;
};
#endif

View File

@@ -0,0 +1,45 @@
/*
* 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 "AcceptInvitationAction.h"
#include "Event.h"
#include "Playerbots.h"
#include "PlayerbotSecurity.h"
bool AcceptInvitationAction::Execute(Event event)
{
Group* grp = bot->GetGroupInvite();
if (!grp)
return false;
Player* inviter = ObjectAccessor::FindPlayer(grp->GetLeaderGUID());
if (!inviter)
return false;
if (!botAI->GetSecurity()->CheckLevelFor(PLAYERBOT_SECURITY_INVITE, false, inviter))
{
WorldPacket data(SMSG_GROUP_DECLINE, 10);
data << bot->GetName();
inviter->SendDirectMessage(&data);
bot->UninviteFromGroup();
return false;
}
WorldPacket p;
uint32 roles_mask = 0;
p << roles_mask;
bot->GetSession()->HandleGroupAcceptOpcode(p);
if (sRandomPlayerbotMgr->IsRandomBot(bot))
botAI->SetMaster(inviter);
//else
//sPlayerbotDbStore->Save(botAI);
botAI->ResetStrategies();
botAI->ChangeStrategy("+follow,-lfg,-bg", BOT_STATE_NON_COMBAT);
botAI->Reset();
botAI->TellMaster("Hello");
return true;
}

View File

@@ -0,0 +1,20 @@
/*
* 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.
*/
#ifndef _PLAYERBOT_ACCEPTINVITATIONACTION_H
#define _PLAYERBOT_ACCEPTINVITATIONACTION_H
#include "Action.h"
class PlayerbotAI;
class AcceptInvitationAction : public Action
{
public:
AcceptInvitationAction(PlayerbotAI* botAI) : Action(botAI, "accept invitation") { }
bool Execute(Event event) override;
};
#endif

View File

@@ -0,0 +1,128 @@
/*
* 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 "AcceptQuestAction.h"
#include "Event.h"
#include "Playerbots.h"
void AcceptAllQuestsAction::ProcessQuest(Quest const* quest, WorldObject* questGiver)
{
AcceptQuest(quest, questGiver->GetGUID());
bot->PlayDistanceSound(620);
}
bool AcceptQuestAction::Execute(Event event)
{
Player* master = GetMaster();
if (!master)
return false;
Player* bot = botAI->GetBot();
ObjectGuid guid;
uint32 quest = 0;
std::string const text = event.getParam();
PlayerbotChatHandler ch(master);
quest = ch.extractQuestId(text);
if (event.getPacket().empty())
{
GuidVector npcs = AI_VALUE(GuidVector, "nearest npcs");
for (GuidVector::iterator i = npcs.begin(); i != npcs.end(); i++)
{
Unit* unit = botAI->GetUnit(*i);
if (unit && quest && unit->hasQuest(quest))
{
guid = unit->GetGUID();
break;
}
if (unit && text == "*" && bot->GetDistance(unit) <= INTERACTION_DISTANCE)
QuestAction::ProcessQuests(unit);
}
GuidVector gos = AI_VALUE(GuidVector, "nearest game objects");
for (GuidVector::iterator i = gos.begin(); i != gos.end(); i++)
{
GameObject* go = botAI->GetGameObject(*i);
if (go && quest && go->hasQuest(quest))
{
guid = go->GetGUID();
break;
}
if (go && text == "*" && bot->GetDistance(go) <= INTERACTION_DISTANCE)
QuestAction::ProcessQuests(go);
}
}
else
{
WorldPacket& p = event.getPacket();
p.rpos(0);
p >> guid >> quest;
}
if (!quest || !guid)
return false;
Quest const* qInfo = sObjectMgr->GetQuestTemplate(quest);
if (!qInfo)
return false;
return AcceptQuest(qInfo, guid);
}
bool AcceptQuestShareAction::Execute(Event event)
{
Player* master = GetMaster();
Player* bot = botAI->GetBot();
WorldPacket& p = event.getPacket();
p.rpos(0);
uint32 quest;
p >> quest;
Quest const* qInfo = sObjectMgr->GetQuestTemplate(quest);
if (!qInfo || !bot->GetDivider())
return false;
quest = qInfo->GetQuestId();
if (!bot->CanTakeQuest(qInfo, false))
{
// can't take quest
bot->SetDivider(ObjectGuid::Empty);
botAI->TellError("I can't take this quest");
return false;
}
if (!bot->GetDivider().IsEmpty())
{
// send msg to quest giving player
master->SendPushToPartyResponse(bot, QUEST_PARTY_MSG_ACCEPT_QUEST);
bot->SetDivider(ObjectGuid::Empty);
}
if (bot->CanAddQuest( qInfo, false))
{
bot->AddQuest(qInfo, master);
if (bot->CanCompleteQuest(quest))
bot->CompleteQuest(quest);
// Runsttren: did not add typeid switch from WorldSession::HandleQuestgiverAcceptQuestOpcode!
// I think it's not needed, cause typeid should be TYPEID_PLAYER - and this one is not handled
// there and there is no default case also.
if (qInfo->GetSrcSpell() > 0)
{
bot->CastSpell( bot, qInfo->GetSrcSpell(), true);
}
botAI->TellMaster("Quest accepted");
return true;
}
return false;
}

View File

@@ -0,0 +1,37 @@
/*
* 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.
*/
#ifndef _PLAYERBOT_ACCEPTQUESTACTION_H
#define _PLAYERBOT_ACCEPTQUESTACTION_H
#include "QuestAction.h"
class Quest;
class PlayerbotAI;
class WorldObject;
class AcceptAllQuestsAction : public QuestAction
{
public:
AcceptAllQuestsAction(PlayerbotAI* botAI, std::string const name = "accept all quests") : QuestAction(botAI, name) { }
protected:
void ProcessQuest(Quest const* quest, WorldObject* questGiver) override;
};
class AcceptQuestAction : public AcceptAllQuestsAction
{
public:
AcceptQuestAction(PlayerbotAI* botAI) : AcceptAllQuestsAction(botAI, "accept quest") { }
bool Execute(Event event) override;
};
class AcceptQuestShareAction : public Action
{
public:
AcceptQuestShareAction(PlayerbotAI* botAI) : Action(botAI, "accept quest share") { }
bool Execute(Event event) override;
};
#endif

View File

@@ -0,0 +1,27 @@
/*
* 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 "AcceptResurrectAction.h"
#include "Event.h"
#include "Playerbots.h"
bool AcceptResurrectAction::Execute(Event event)
{
if (bot->IsAlive())
return false;
WorldPacket p(event.getPacket());
p.rpos(0);
ObjectGuid guid;
p >> guid;
WorldPacket packet(CMSG_RESURRECT_RESPONSE, 8 + 1);
packet << guid;
packet << uint8(1); // accept
bot->GetSession()->HandleResurrectResponseOpcode(packet); // queue the packet to get around race condition
botAI->ChangeEngine(BOT_STATE_NON_COMBAT);
return true;
}

View File

@@ -0,0 +1,20 @@
/*
* 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.
*/
#ifndef _PLAYERBOT_ACCEPTRESURRECTACTION_H
#define _PLAYERBOT_ACCEPTRESURRECTACTION_H
#include "Action.h"
class PlayerbotAI;
class AcceptResurrectAction : public Action
{
public:
AcceptResurrectAction(PlayerbotAI* botAI) : Action(botAI, "accept resurrect") { }
bool Execute(Event event) override;
};
#endif

View File

@@ -0,0 +1,382 @@
/*
* 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.
*/
#ifndef _PLAYERBOT_ACTIONCONTEXT_H
#define _PLAYERBOT_ACTIONCONTEXT_H
#include "AddLootAction.h"
#include "AttackAction.h"
#include "AutoLearnSpellAction.h"
#include "BattlegroundTactics.h"
#include "BattlegroundJoinAction.h"
#include "BuyAction.h"
#include "CastCustomSpellAction.h"
#include "ChangeStrategyAction.h"
#include "ChangeTalentsAction.h"
#include "CheckMailAction.h"
#include "CheckValuesAction.h"
#include "ChooseTargetActions.h"
#include "ChooseTravelTargetAction.h"
#include "ChooseRpgTargetAction.h"
#include "CombatActions.h"
#include "DelayAction.h"
#include "DestroyItemAction.h"
#include "EmoteAction.h"
#include "GenericActions.h"
#include "GenericSpellActions.h"
#include "GiveItemAction.h"
#include "GreetAction.h"
#include "GuildAcceptAction.h"
#include "GuildCreateActions.h"
#include "GuildManagementActions.h"
#include "ImbueAction.h"
#include "InviteToGroupAction.h"
#include "LeaveGroupAction.h"
#include "FollowActions.h"
#include "LootAction.h"
#include "MovementActions.h"
#include "MoveToRpgTargetAction.h"
#include "MoveToTravelTargetAction.h"
#include "NonCombatActions.h"
#include "OutfitAction.h"
#include "PositionAction.h"
#include "RandomBotUpdateAction.h"
#include "ReachTargetActions.h"
#include "ReleaseSpiritAction.h"
#include "RemoveAuraAction.h"
#include "ResetInstancesAction.h"
#include "RevealGatheringItemAction.h"
#include "RpgAction.h"
#include "RpgSubActions.h"
#include "RtiAction.h"
#include "SayAction.h"
#include "StayActions.h"
#include "SuggestWhatToDoAction.h"
#include "TravelAction.h"
#include "XpGainAction.h"
#include "VehicleActions.h"
#include "WorldBuffAction.h"
class PlayerbotAI;
class ActionContext : public NamedObjectContext<Action>
{
public:
ActionContext()
{
creators["mark rti"] = &ActionContext::mark_rti;
creators["set return position"] = &ActionContext::set_return_position;
creators["rpg"] = &ActionContext::rpg;
creators["crpg"] = &ActionContext::crpg;
creators["choose rpg target"] = &ActionContext::choose_rpg_target;
creators["move to rpg target"] = &ActionContext::move_to_rpg_target;
creators["travel"] = &ActionContext::travel;
creators["choose travel target"] = &ActionContext::choose_travel_target;
creators["move to travel target"] = &ActionContext::move_to_travel_target;
creators["move out of collision"] = &ActionContext::move_out_of_collision;
creators["move out of collision"] = &ActionContext::move_out_of_collision;
creators["move random"] = &ActionContext::move_random;
creators["attack"] = &ActionContext::melee;
creators["melee"] = &ActionContext::melee;
creators["switch to melee"] = &ActionContext::switch_to_melee;
creators["switch to ranged"] = &ActionContext::switch_to_ranged;
creators["reach spell"] = &ActionContext::ReachSpell;
creators["reach melee"] = &ActionContext::ReachMelee;
creators["reach party member to heal"] = &ActionContext::reach_party_member_to_heal;
creators["flee"] = &ActionContext::flee;
creators["flee with pet"] = &ActionContext::flee_with_pet;
creators["gift of the naaru"] = &ActionContext::gift_of_the_naaru;
creators["shoot"] = &ActionContext::shoot;
creators["lifeblood"] = &ActionContext::lifeblood;
creators["arcane torrent"] = &ActionContext::arcane_torrent;
creators["end pull"] = &ActionContext::end_pull;
creators["healthstone"] = &ActionContext::healthstone;
creators["healing potion"] = &ActionContext::healing_potion;
creators["mana potion"] = &ActionContext::mana_potion;
creators["food"] = &ActionContext::food;
creators["drink"] = &ActionContext::drink;
creators["tank assist"] = &ActionContext::tank_assist;
creators["dps assist"] = &ActionContext::dps_assist;
creators["dps aoe"] = &ActionContext::dps_aoe;
creators["attack rti target"] = &ActionContext::attack_rti_target;
creators["loot"] = &ActionContext::loot;
creators["add loot"] = &ActionContext::add_loot;
creators["add gathering loot"] = &ActionContext::add_gathering_loot;
creators["add all loot"] = &ActionContext::add_all_loot;
creators["release loot"] = &ActionContext::release_loot;
creators["shoot"] = &ActionContext::shoot;
creators["follow"] = &ActionContext::follow;
creators["flee to master"] = &ActionContext::flee_to_master;
creators["runaway"] = &ActionContext::runaway;
creators["stay"] = &ActionContext::stay;
creators["sit"] = &ActionContext::sit;
creators["attack anything"] = &ActionContext::attack_anything;
creators["attack least hp target"] = &ActionContext::attack_least_hp_target;
creators["attack enemy player"] = &ActionContext::attack_enemy_player;
creators["emote"] = &ActionContext::emote;
creators["talk"] = &ActionContext::talk;
creators["suggest what to do"] = &ActionContext::suggest_what_to_do;
creators["suggest trade"] = &ActionContext::suggest_trade;
creators["return"] = &ActionContext::_return;
creators["move to loot"] = &ActionContext::move_to_loot;
creators["open loot"] = &ActionContext::open_loot;
creators["guard"] = &ActionContext::guard;
creators["move out of enemy contact"] = &ActionContext::move_out_of_enemy_contact;
creators["set facing"] = &ActionContext::set_facing;
creators["set behind"] = &ActionContext::set_behind;
creators["attack duel opponent"] = &ActionContext::attack_duel_opponent;
creators["drop target"] = &ActionContext::drop_target;
creators["check mail"] = &ActionContext::check_mail;
creators["say"] = &ActionContext::say;
creators["reveal gathering item"] = &ActionContext::reveal_gathering_item;
creators["outfit"] = &ActionContext::outfit;
creators["random bot update"] = &ActionContext::random_bot_update;
creators["delay"] = &ActionContext::delay;
creators["greet"] = &ActionContext::greet;
creators["check values"] = &ActionContext::check_values;
creators["ra"] = &ActionContext::ra;
creators["give food"] = &ActionContext::give_food;
creators["give water"] = &ActionContext::give_water;
creators["apply poison"] = &ActionContext::apply_poison;
creators["apply stone"] = &ActionContext::apply_stone;
creators["apply oil"] = &ActionContext::apply_oil;
creators["try emergency"] = &ActionContext::try_emergency;
creators["mount"] = &ActionContext::mount;
creators["war stomp"] = &ActionContext::war_stomp;
creators["auto talents"] = &ActionContext::auto_talents;
creators["auto learn spell"] = &ActionContext::auto_learn_spell;
creators["xp gain"] = &ActionContext::xp_gain;
creators["invite nearby"] = &ActionContext::invite_nearby;
creators["invite guild"] = &ActionContext::invite_guild;
creators["leave far away"] = &ActionContext::leave_far_away;
creators["move to dark portal"] = &ActionContext::move_to_dark_portal;
creators["move from dark portal"] = &ActionContext::move_from_dark_portal;
creators["use dark portal azeroth"] = &ActionContext::use_dark_portal_azeroth;
creators["world buff"] = &ActionContext::world_buff;
creators["hearthstone"] = &ActionContext::hearthstone;
creators["cast random spell"] = &ActionContext::cast_random_spell;
creators["free bg join"] = &ActionContext::free_bg_join;
creators["use random recipe"] = &ActionContext::use_random_recipe;
creators["use random quest item"] = &ActionContext::use_random_quest_item;
creators["craft random item"] = &ActionContext::craft_random_item;
creators["smart destroy item"] = &ActionContext::smart_destroy_item;
creators["disenchant random item"] = &ActionContext::disenchant_random_item;
creators["enchant random item"] = &ActionContext::enchant_random_item;
creators["reset instances"] = &ActionContext::reset_instances;
creators["buy petition"] = &ActionContext::buy_petition;
creators["offer petition"] = &ActionContext::offer_petition;
creators["offer petition nearby"] = &ActionContext::offer_petition_nearby;
creators["turn in petition"] = &ActionContext::turn_in_petition;
creators["buy tabard"] = &ActionContext::buy_tabard;
creators["guild manage nearby"] = &ActionContext::guild_manage_nearby;
// BG Tactics
creators["bg tactics"] = &ActionContext::bg_tactics;
creators["bg move to start"] = &ActionContext::bg_move_to_start;
creators["bg move to objective"] = &ActionContext::bg_move_to_objective;
creators["bg select objective"] = &ActionContext::bg_select_objective;
creators["bg check objective"] = &ActionContext::bg_check_objective;
creators["bg attack fc"] = &ActionContext::bg_attack_fc;
creators["bg protect fc"] = &ActionContext::bg_protect_fc;
creators["bg use buff"] = &ActionContext::bg_use_buff;
creators["attack enemy flag carrier"] = &ActionContext::attack_enemy_fc;
creators["bg check flag"] = &ActionContext::bg_check_flag;
// Vehicles
creators["enter vehicle"] = &ActionContext::enter_vehicle;
creators["leave vehicle"] = &ActionContext::leave_vehicle;
creators["hurl boulder"] = &ActionContext::hurl_boulder;
creators["ram"] = &ActionContext::ram;
creators["steam rush"] = &ActionContext::steam_rush;
creators["steam blast"] = &ActionContext::steam_blast;
creators["napalm"] = &ActionContext::napalm;
creators["fire cannon"] = &ActionContext::fire_cannon;
creators["incendiary rocket"] = &ActionContext::incendiary_rocket;
creators["rocket blast"] = &ActionContext::rocket_blast;
creators["blade salvo"] = &ActionContext::blade_salvo;
creators["glaive throw"] = &ActionContext::glaive_throw;
//Rpg
creators["rpg stay"] = &ActionContext::rpg_stay;
creators["rpg work"] = &ActionContext::rpg_work;
creators["rpg emote"] = &ActionContext::rpg_emote;
creators["rpg cancel"] = &ActionContext::rpg_cancel;
creators["rpg taxi"] = &ActionContext::rpg_taxi;
creators["rpg discover"] = &ActionContext::rpg_discover;
creators["rpg start quest"] = &ActionContext::rpg_start_quest;
creators["rpg end quest"] = &ActionContext::rpg_end_quest;
creators["rpg buy"] = &ActionContext::rpg_buy;
creators["rpg sell"] = &ActionContext::rpg_sell;
creators["rpg repair"] = &ActionContext::rpg_repair;
creators["rpg train"] = &ActionContext::rpg_train;
creators["rpg heal"] = &ActionContext::rpg_heal;
creators["rpg home bind"] = &ActionContext::rpg_home_bind;
creators["rpg queue bg"] = &ActionContext::rpg_queue_bg;
creators["rpg buy petition"] = &ActionContext::rpg_buy_petition;
creators["rpg use"] = &ActionContext::rpg_use;
creators["rpg spell"] = &ActionContext::rpg_spell;
creators["rpg craft"] = &ActionContext::rpg_craft;
creators["rpg trade useful"] = &ActionContext::rpg_trade_useful;
creators["rpg duel"] = &ActionContext::rpg_duel;
}
private:
static Action* give_water(PlayerbotAI* botAI) { return new GiveWaterAction(botAI); }
static Action* give_food(PlayerbotAI* botAI) { return new GiveFoodAction(botAI); }
static Action* ra(PlayerbotAI* botAI) { return new RemoveAuraAction(botAI); }
static Action* mark_rti(PlayerbotAI* botAI) { return new MarkRtiAction(botAI); }
static Action* set_return_position(PlayerbotAI* botAI) { return new SetReturnPositionAction(botAI); }
static Action* rpg(PlayerbotAI* botAI) { return new RpgAction(botAI); }
static Action* crpg(PlayerbotAI* botAI) { return new CRpgAction(botAI); }
static Action* choose_rpg_target(PlayerbotAI* botAI) { return new ChooseRpgTargetAction(botAI); }
static Action* move_to_rpg_target(PlayerbotAI* botAI) { return new MoveToRpgTargetAction(botAI); }
static Action* travel(PlayerbotAI* botAI) { return new TravelAction(botAI); }
static Action* choose_travel_target(PlayerbotAI* botAI) { return new ChooseTravelTargetAction(botAI); }
static Action* move_to_travel_target(PlayerbotAI* botAI) { return new MoveToTravelTargetAction(botAI); }
static Action* move_out_of_collision(PlayerbotAI* botAI) { return new MoveOutOfCollisionAction(botAI); }
static Action* move_random(PlayerbotAI* botAI) { return new MoveRandomAction(botAI); }
static Action* check_values(PlayerbotAI* botAI) { return new CheckValuesAction(botAI); }
static Action* greet(PlayerbotAI* botAI) { return new GreetAction(botAI); }
static Action* check_mail(PlayerbotAI* botAI) { return new CheckMailAction(botAI); }
static Action* drop_target(PlayerbotAI* botAI) { return new DropTargetAction(botAI); }
static Action* attack_duel_opponent(PlayerbotAI* botAI) { return new AttackDuelOpponentAction(botAI); }
static Action* guard(PlayerbotAI* botAI) { return new GuardAction(botAI); }
static Action* open_loot(PlayerbotAI* botAI) { return new OpenLootAction(botAI); }
static Action* move_to_loot(PlayerbotAI* botAI) { return new MoveToLootAction(botAI); }
static Action* _return(PlayerbotAI* botAI) { return new ReturnAction(botAI); }
static Action* shoot(PlayerbotAI* botAI) { return new CastShootAction(botAI); }
static Action* melee(PlayerbotAI* botAI) { return new MeleeAction(botAI); }
static Action* switch_to_melee(PlayerbotAI* botAI) { return new SwitchToMeleeAction(botAI); }
static Action* switch_to_ranged(PlayerbotAI* botAI) { return new SwitchToRangedAction(botAI); }
static Action* ReachSpell(PlayerbotAI* botAI) { return new ReachSpellAction(botAI); }
static Action* ReachMelee(PlayerbotAI* botAI) { return new ReachMeleeAction(botAI); }
static Action* reach_party_member_to_heal(PlayerbotAI* botAI) { return new ReachPartyMemberToHealAction(botAI); }
static Action* flee(PlayerbotAI* botAI) { return new FleeAction(botAI); }
static Action* flee_with_pet(PlayerbotAI* botAI) { return new FleeWithPetAction(botAI); }
static Action* gift_of_the_naaru(PlayerbotAI* botAI) { return new CastGiftOfTheNaaruAction(botAI); }
static Action* lifeblood(PlayerbotAI* botAI) { return new CastLifeBloodAction(botAI); }
static Action* arcane_torrent(PlayerbotAI* botAI) { return new CastArcaneTorrentAction(botAI); }
static Action* mana_tap(PlayerbotAI* botAI) { return new CastManaTapAction(botAI); }
static Action* end_pull(PlayerbotAI* botAI) { return new ChangeCombatStrategyAction(botAI, "-pull"); }
static Action* emote(PlayerbotAI* botAI) { return new EmoteAction(botAI); }
static Action* talk(PlayerbotAI* botAI) { return new TalkAction(botAI); }
static Action* suggest_what_to_do(PlayerbotAI* botAI) { return new SuggestWhatToDoAction(botAI); }
static Action* suggest_trade(PlayerbotAI* botAI) { return new SuggestTradeAction(botAI); }
static Action* attack_anything(PlayerbotAI* botAI) { return new AttackAnythingAction(botAI); }
static Action* attack_least_hp_target(PlayerbotAI* botAI) { return new AttackLeastHpTargetAction(botAI); }
static Action* attack_enemy_player(PlayerbotAI* botAI) { return new AttackEnemyPlayerAction(botAI); }
static Action* stay(PlayerbotAI* botAI) { return new StayAction(botAI); }
static Action* sit(PlayerbotAI* botAI) { return new SitAction(botAI); }
static Action* runaway(PlayerbotAI* botAI) { return new RunAwayAction(botAI); }
static Action* follow(PlayerbotAI* botAI) { return new FollowAction(botAI); }
static Action* flee_to_master(PlayerbotAI* botAI) { return new FleeToMasterAction(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_all_loot(PlayerbotAI* botAI) { return new AddAllLootAction(botAI); }
static Action* loot(PlayerbotAI* botAI) { return new LootAction(botAI); }
static Action* release_loot(PlayerbotAI* botAI) { return new ReleaseLootAction(botAI); }
static Action* dps_assist(PlayerbotAI* botAI) { return new DpsAssistAction(botAI); }
static Action* dps_aoe(PlayerbotAI* botAI) { return new DpsAoeAction(botAI); }
static Action* attack_rti_target(PlayerbotAI* botAI) { return new AttackRtiTargetAction(botAI); }
static Action* tank_assist(PlayerbotAI* botAI) { return new TankAssistAction(botAI); }
static Action* drink(PlayerbotAI* botAI) { return new DrinkAction(botAI); }
static Action* food(PlayerbotAI* botAI) { return new EatAction(botAI); }
static Action* mana_potion(PlayerbotAI* botAI) { return new UseManaPotion(botAI); }
static Action* healing_potion(PlayerbotAI* botAI) { return new UseHealingPotion(botAI); }
static Action* healthstone(PlayerbotAI* botAI) { return new UseItemAction(botAI, "healthstone"); }
static Action* move_out_of_enemy_contact(PlayerbotAI* botAI) { return new MoveOutOfEnemyContactAction(botAI); }
static Action* set_facing(PlayerbotAI* botAI) { return new SetFacingTargetAction(botAI); }
static Action* set_behind(PlayerbotAI* botAI) { return new SetBehindTargetAction(botAI); }
static Action* say(PlayerbotAI* botAI) { return new SayAction(botAI); }
static Action* reveal_gathering_item(PlayerbotAI* botAI) { return new RevealGatheringItemAction(botAI); }
static Action* outfit(PlayerbotAI* botAI) { return new OutfitAction(botAI); }
static Action* random_bot_update(PlayerbotAI* botAI) { return new RandomBotUpdateAction(botAI); }
static Action* delay(PlayerbotAI* botAI) { return new DelayAction(botAI); }
static Action* apply_poison(PlayerbotAI* botAI) { return new ImbueWithPoisonAction(botAI); }
static Action* apply_oil(PlayerbotAI* botAI) { return new ImbueWithOilAction(botAI); }
static Action* apply_stone(PlayerbotAI* botAI) { return new ImbueWithStoneAction(botAI); }
static Action* try_emergency(PlayerbotAI* botAI) { return new TryEmergencyAction(botAI); }
static Action* mount(PlayerbotAI* botAI) { return new CastSpellAction(botAI, "mount"); }
static Action* war_stomp(PlayerbotAI* botAI) { return new CastWarStompAction(botAI); }
static Action* auto_talents(PlayerbotAI* botAI) { return new AutoSetTalentsAction(botAI); }
static Action* auto_learn_spell(PlayerbotAI* botAI) { return new AutoLearnSpellAction(botAI); }
static Action* xp_gain(PlayerbotAI* botAI) { return new XpGainAction(botAI); }
static Action* invite_nearby(PlayerbotAI* botAI) { return new InviteNearbyToGroupAction(botAI); }
static Action* invite_guild(PlayerbotAI* botAI) { return new InviteGuildToGroupAction(botAI); }
static Action* leave_far_away(PlayerbotAI* botAI) { return new LeaveFarAwayAction(botAI); }
static Action* move_to_dark_portal(PlayerbotAI* botAI) { return new MoveToDarkPortalAction(botAI); }
static Action* use_dark_portal_azeroth(PlayerbotAI* botAI) { return new DarkPortalAzerothAction(botAI); }
static Action* move_from_dark_portal(PlayerbotAI* botAI) { return new MoveFromDarkPortalAction(botAI); }
static Action* world_buff(PlayerbotAI* botAI) { return new WorldBuffAction(botAI); }
static Action* hearthstone(PlayerbotAI* botAI) { return new UseHearthStone(botAI); }
static Action* cast_random_spell(PlayerbotAI* botAI) { return new CastRandomSpellAction(botAI); }
static Action* free_bg_join(PlayerbotAI* botAI) { return new FreeBGJoinAction(botAI); }
static Action* use_random_recipe(PlayerbotAI* botAI) { return new UseRandomRecipe(botAI); }
static Action* use_random_quest_item(PlayerbotAI* botAI) { return new UseRandomQuestItem(botAI); }
static Action* craft_random_item(PlayerbotAI* botAI) { return new CraftRandomItemAction(botAI); }
static Action* smart_destroy_item(PlayerbotAI* botAI) { return new SmartDestroyItemAction(botAI); }
static Action* disenchant_random_item(PlayerbotAI* botAI) { return new DisEnchantRandomItemAction(botAI); }
static Action* enchant_random_item(PlayerbotAI* botAI) { return new EnchantRandomItemAction(botAI); }
static Action* reset_instances(PlayerbotAI* botAI) { return new ResetInstancesAction(botAI); }
static Action* buy_petition(PlayerbotAI* botAI) { return new BuyPetitionAction(botAI); }
static Action* offer_petition(PlayerbotAI* botAI) { return new PetitionOfferAction(botAI); }
static Action* offer_petition_nearby(PlayerbotAI* botAI) { return new PetitionOfferNearbyAction(botAI); }
static Action* turn_in_petition(PlayerbotAI* botAI) { return new PetitionTurnInAction(botAI); }
static Action* buy_tabard(PlayerbotAI* botAI) { return new BuyTabardAction(botAI); }
static Action* guild_manage_nearby(PlayerbotAI* botAI) { return new GuildManageNearbyAction(botAI); }
// BG Tactics
static Action* bg_tactics(PlayerbotAI* botAI) { return new BGTactics(botAI); }
static Action* bg_move_to_start(PlayerbotAI* botAI) { return new BGTactics(botAI, "move to start"); }
static Action* bg_move_to_objective(PlayerbotAI* botAI) { return new BGTactics(botAI, "move to objective"); }
static Action* bg_select_objective(PlayerbotAI* botAI) { return new BGTactics(botAI, "select objective"); }
static Action* bg_check_objective(PlayerbotAI* botAI) { return new BGTactics(botAI, "check objective"); }
static Action* bg_attack_fc(PlayerbotAI* botAI) { return new BGTactics(botAI, "attack fc"); }
static Action* bg_protect_fc(PlayerbotAI* botAI) { return new BGTactics(botAI, "protect fc"); }
static Action* attack_enemy_fc(PlayerbotAI* botAI) { return new AttackEnemyFlagCarrierAction(botAI); }
static Action* bg_use_buff(PlayerbotAI* botAI) { return new BGTactics(botAI, "use buff"); }
static Action* bg_check_flag(PlayerbotAI* botAI) { return new BGTactics(botAI, "check flag"); }
// Vehicles
static Action* enter_vehicle(PlayerbotAI* botAI) { return new EnterVehicleAction(botAI); }
static Action* leave_vehicle(PlayerbotAI* botAI) { return new LeaveVehicleAction(botAI); }
static Action* hurl_boulder(PlayerbotAI* botAI) { return new CastHurlBoulderAction(botAI); }
static Action* ram(PlayerbotAI* botAI) { return new CastRamAction(botAI); }
static Action* steam_blast(PlayerbotAI* botAI) { return new CastSteamBlastAction(botAI); }
static Action* steam_rush(PlayerbotAI* botAI) { return new CastSteamRushAction(botAI); }
static Action* napalm(PlayerbotAI* botAI) { return new CastNapalmAction(botAI); }
static Action* fire_cannon(PlayerbotAI* botAI) { return new CastFireCannonAction(botAI); }
static Action* incendiary_rocket(PlayerbotAI* botAI) { return new CastIncendiaryRocketAction(botAI); }
static Action* rocket_blast(PlayerbotAI* botAI) { return new CastRocketBlastAction(botAI); }
static Action* glaive_throw(PlayerbotAI* botAI) { return new CastGlaiveThrowAction(botAI); }
static Action* blade_salvo(PlayerbotAI* botAI) { return new CastBladeSalvoAction(botAI); }
//Rpg
static Action* rpg_stay(PlayerbotAI* botAI) { return new RpgStayAction(botAI); }
static Action* rpg_work(PlayerbotAI* botAI) { return new RpgWorkAction(botAI); }
static Action* rpg_emote(PlayerbotAI* botAI) { return new RpgEmoteAction(botAI); }
static Action* rpg_cancel(PlayerbotAI* botAI) { return new RpgCancelAction(botAI); }
static Action* rpg_taxi(PlayerbotAI* botAI) { return new RpgTaxiAction(botAI); }
static Action* rpg_discover(PlayerbotAI* botAI) { return new RpgDiscoverAction(botAI); }
static Action* rpg_start_quest(PlayerbotAI* botAI) { return new RpgStartQuestAction(botAI); }
static Action* rpg_end_quest(PlayerbotAI* botAI) { return new RpgEndQuestAction(botAI); }
static Action* rpg_buy(PlayerbotAI* botAI) { return new RpgBuyAction(botAI); }
static Action* rpg_sell(PlayerbotAI* botAI) { return new RpgSellAction(botAI); }
static Action* rpg_repair(PlayerbotAI* botAI) { return new RpgRepairAction(botAI); }
static Action* rpg_train(PlayerbotAI* botAI) { return new RpgTrainAction(botAI); }
static Action* rpg_heal(PlayerbotAI* botAI) { return new RpgHealAction(botAI); }
static Action* rpg_home_bind(PlayerbotAI* botAI) { return new RpgHomeBindAction(botAI); }
static Action* rpg_queue_bg(PlayerbotAI* botAI) { return new RpgQueueBgAction(botAI); }
static Action* rpg_buy_petition(PlayerbotAI* botAI) { return new RpgBuyPetitionAction(botAI); }
static Action* rpg_use(PlayerbotAI* botAI) { return new RpgUseAction(botAI); }
static Action* rpg_spell(PlayerbotAI* botAI) { return new RpgSpellAction(botAI); }
static Action* rpg_craft(PlayerbotAI* botAI) { return new RpgCraftAction(botAI); }
static Action* rpg_trade_useful(PlayerbotAI* botAI) { return new RpgTradeUsefulAction(botAI); }
static Action* rpg_duel(PlayerbotAI* botAI) { return new RpgDuelAction(botAI); }
};
#endif

View File

@@ -0,0 +1,86 @@
/*
* 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 "AddLootAction.h"
#include "CellImpl.h"
#include "Event.h"
#include "GridNotifiers.h"
#include "GridNotifiersImpl.h"
#include "LootObjectStack.h"
#include "Playerbots.h"
#include "ServerFacade.h"
bool AddLootAction::Execute(Event event)
{
ObjectGuid guid = event.getObject();
if (!guid)
return false;
return AI_VALUE(LootObjectStack*, "available loot")->Add(guid);
}
bool AddAllLootAction::Execute(Event event)
{
bool added = false;
GuidVector gos = context->GetValue<GuidVector>("nearest game objects")->Get();
for (GuidVector::iterator i = gos.begin(); i != gos.end(); i++)
added |= AddLoot(*i);
GuidVector corpses = context->GetValue<GuidVector>("nearest corpses")->Get();
for (GuidVector::iterator i = corpses.begin(); i != corpses.end(); i++)
added |= AddLoot(*i);
return added;
}
bool AddLootAction::isUseful()
{
return true;
}
bool AddAllLootAction::isUseful()
{
return true;
}
bool AddAllLootAction::AddLoot(ObjectGuid guid)
{
return AI_VALUE(LootObjectStack*, "available loot")->Add(guid);
}
bool AddGatheringLootAction::AddLoot(ObjectGuid guid)
{
LootObject loot(bot, guid);
WorldObject* wo = loot.GetWorldObject(bot);
if (loot.IsEmpty() || !wo)
return false;
if (!bot->IsWithinLOSInMap(wo))
return false;
if (loot.skillId == SKILL_NONE)
return false;
if (!loot.IsLootPossible(bot))
return false;
if (sServerFacade->IsDistanceGreaterThan(sServerFacade->GetDistance2d(bot, wo), INTERACTION_DISTANCE))
{
std::list<Unit*> targets;
Acore::AnyUnfriendlyUnitInObjectRangeCheck u_check(bot, bot, sPlayerbotAIConfig->lootDistance);
Acore::UnitListSearcher<Acore::AnyUnfriendlyUnitInObjectRangeCheck> searcher(bot, targets, u_check);
Cell::VisitAllObjects(bot, searcher, sPlayerbotAIConfig->lootDistance * 1.5f);
if (!targets.empty())
{
std::ostringstream out;
out << "Kill that " << targets.front()->GetName() << " so I can loot freely";
botAI->TellError(out.str());
return false;
}
}
return AddAllLootAction::AddLoot(guid);
}

View File

@@ -0,0 +1,41 @@
/*
* 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.
*/
#ifndef _PLAYERBOT_ADDLOOTACTION_H
#define _PLAYERBOT_ADDLOOTACTION_H
#include "Action.h"
class ObjectGuid;
class PlayerbotAI;
class AddLootAction : public Action
{
public:
AddLootAction(PlayerbotAI* botAI) : Action(botAI, "add loot") { }
bool Execute(Event event) override;
bool isUseful() override;
};
class AddAllLootAction : public Action
{
public:
AddAllLootAction(PlayerbotAI* botAI, std::string const name = "add all loot") : Action(botAI, name) { }
bool Execute(Event event) override;
bool isUseful() override;
protected:
virtual bool AddLoot(ObjectGuid guid);
};
class AddGatheringLootAction : public AddAllLootAction
{
public:
AddGatheringLootAction(PlayerbotAI* botAI) : AddAllLootAction(botAI, "add gathering loot") { }
protected:
bool AddLoot(ObjectGuid guid) override;
};
#endif

Some files were not shown because too many files have changed in this diff Show More