mirror of
https://github.com/mod-playerbots/mod-playerbots
synced 2025-11-29 15:58:20 +08:00
Big update.
This commit is contained in:
696
src/AiFactory.cpp
Normal file
696
src/AiFactory.cpp
Normal 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
36
src/AiFactory.h
Normal 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
298
src/ChatFilter.cpp
Normal 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
36
src/ChatFilter.h
Normal 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
569
src/ChatHelper.cpp
Normal 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
77
src/ChatHelper.h
Normal 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
171
src/FleeManager.cpp
Normal 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
54
src/FleeManager.h
Normal 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
1198
src/GuildTaskMgr.cpp
Normal file
File diff suppressed because it is too large
Load Diff
59
src/GuildTaskMgr.h
Normal file
59
src/GuildTaskMgr.h
Normal 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
52
src/Helpers.cpp
Normal 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
50
src/Helpers.h
Normal 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
44
src/LazyCalculatedValue.h
Normal 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
247
src/LootObjectStack.cpp
Normal 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
82
src/LootObjectStack.h
Normal 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
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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
257
src/PerformanceMonitor.cpp
Normal 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
72
src/PerformanceMonitor.h
Normal 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
3408
src/PlayerbotAI.cpp
Normal file
File diff suppressed because it is too large
Load Diff
392
src/PlayerbotAI.h
Normal file
392
src/PlayerbotAI.h
Normal 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
19
src/PlayerbotAIAware.h
Normal 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
67
src/PlayerbotAIBase.cpp
Normal 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
31
src/PlayerbotAIBase.h
Normal 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
481
src/PlayerbotAIConfig.cpp
Normal 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
172
src/PlayerbotAIConfig.h
Normal 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
|
||||
99
src/PlayerbotCommandServer.cpp
Normal file
99
src/PlayerbotCommandServer.cpp
Normal 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();
|
||||
}
|
||||
24
src/PlayerbotCommandServer.h
Normal file
24
src/PlayerbotCommandServer.h
Normal 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
88
src/PlayerbotDbStore.cpp
Normal 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
36
src/PlayerbotDbStore.h
Normal 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
2667
src/PlayerbotFactory.cpp
Normal file
File diff suppressed because it is too large
Load Diff
176
src/PlayerbotFactory.h
Normal file
176
src/PlayerbotFactory.h
Normal 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
1070
src/PlayerbotMgr.cpp
Normal file
File diff suppressed because it is too large
Load Diff
111
src/PlayerbotMgr.h
Normal file
111
src/PlayerbotMgr.h
Normal 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
265
src/PlayerbotSecurity.cpp
Normal 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
55
src/PlayerbotSecurity.h
Normal 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
49
src/PlayerbotTextMgr.cpp
Normal 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
34
src/PlayerbotTextMgr.h
Normal 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
336
src/Playerbots.cpp
Normal 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
47
src/Playerbots.h
Normal 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
2720
src/RandomItemMgr.cpp
Normal file
File diff suppressed because it is too large
Load Diff
204
src/RandomItemMgr.h
Normal file
204
src/RandomItemMgr.h
Normal 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
|
||||
731
src/RandomPlayerbotFactory.cpp
Normal file
731
src/RandomPlayerbotFactory.cpp
Normal 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);
|
||||
}
|
||||
36
src/RandomPlayerbotFactory.h
Normal file
36
src/RandomPlayerbotFactory.h
Normal 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
2242
src/RandomPlayerbotMgr.cpp
Normal file
File diff suppressed because it is too large
Load Diff
134
src/RandomPlayerbotMgr.h
Normal file
134
src/RandomPlayerbotMgr.h
Normal 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
72
src/ServerFacade.cpp
Normal 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
40
src/ServerFacade.h
Normal 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
517
src/Talentspec.cpp
Normal 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
103
src/Talentspec.h
Normal 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
4343
src/TravelMgr.cpp
Normal file
File diff suppressed because it is too large
Load Diff
988
src/TravelMgr.h
Normal file
988
src/TravelMgr.h
Normal 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(¢er);
|
||||
}
|
||||
|
||||
float fDist(WorldPosition* center);
|
||||
float fDist(WorldPosition center)
|
||||
{
|
||||
return fDist(¢er);
|
||||
}
|
||||
|
||||
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
2508
src/TravelNode.cpp
Normal file
File diff suppressed because it is too large
Load Diff
578
src/TravelNode.h
Normal file
578
src/TravelNode.h
Normal 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
89
src/cs_playerbots.cpp
Normal 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
1
src/cs_playerbots.h
Normal file
@@ -0,0 +1 @@
|
||||
void AddSC_playerbots_commandscript();
|
||||
25
src/playerbots_loader.cpp
Normal file
25
src/playerbots_loader.cpp
Normal 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
111
src/strategy/Action.cpp
Normal 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
122
src/strategy/Action.h
Normal 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
15
src/strategy/AiObject.cpp
Normal 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
497
src/strategy/AiObject.h
Normal 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
|
||||
162
src/strategy/AiObjectContext.cpp
Normal file
162
src/strategy/AiObjectContext.cpp
Normal 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);
|
||||
}
|
||||
70
src/strategy/AiObjectContext.h
Normal file
70
src/strategy/AiObjectContext.h
Normal 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
|
||||
111
src/strategy/CustomStrategy.cpp
Normal file
111
src/strategy/CustomStrategy.cpp
Normal 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();
|
||||
}
|
||||
30
src/strategy/CustomStrategy.h
Normal file
30
src/strategy/CustomStrategy.h
Normal 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
657
src/strategy/Engine.cpp
Normal 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
127
src/strategy/Engine.h
Normal 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
25
src/strategy/Event.cpp
Normal 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
38
src/strategy/Event.h
Normal 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
|
||||
64
src/strategy/ExternalEventHelper.cpp
Normal file
64
src/strategy/ExternalEventHelper.cpp
Normal 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;
|
||||
}
|
||||
29
src/strategy/ExternalEventHelper.h
Normal file
29
src/strategy/ExternalEventHelper.h
Normal 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
|
||||
87
src/strategy/ItemVisitors.cpp
Normal file
87
src/strategy/ItemVisitors.cpp
Normal 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
414
src/strategy/ItemVisitors.h
Normal 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
22
src/strategy/Multiplier.h
Normal 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
|
||||
33
src/strategy/NamedObjectContext.cpp
Normal file
33
src/strategy/NamedObjectContext.cpp
Normal 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]);
|
||||
}
|
||||
282
src/strategy/NamedObjectContext.h
Normal file
282
src/strategy/NamedObjectContext.h
Normal 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
|
||||
50
src/strategy/PassiveMultiplier.cpp
Normal file
50
src/strategy/PassiveMultiplier.cpp
Normal 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;
|
||||
}
|
||||
27
src/strategy/PassiveMultiplier.h
Normal file
27
src/strategy/PassiveMultiplier.h
Normal 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
106
src/strategy/Queue.cpp
Normal 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
27
src/strategy/Queue.h
Normal 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
125
src/strategy/Strategy.cpp
Normal 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
59
src/strategy/Strategy.h
Normal 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
|
||||
221
src/strategy/StrategyContext.h
Normal file
221
src/strategy/StrategyContext.h
Normal 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
49
src/strategy/Trigger.cpp
Normal 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
69
src/strategy/Trigger.h
Normal 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
111
src/strategy/Value.cpp
Normal 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
319
src/strategy/Value.h
Normal 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
|
||||
24
src/strategy/actions/AcceptBattlegroundInvitationAction.cpp
Normal file
24
src/strategy/actions/AcceptBattlegroundInvitationAction.cpp
Normal 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;
|
||||
}
|
||||
20
src/strategy/actions/AcceptBattlegroundInvitationAction.h
Normal file
20
src/strategy/actions/AcceptBattlegroundInvitationAction.h
Normal 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
|
||||
24
src/strategy/actions/AcceptDuelAction.cpp
Normal file
24
src/strategy/actions/AcceptDuelAction.cpp
Normal 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;
|
||||
}
|
||||
20
src/strategy/actions/AcceptDuelAction.h
Normal file
20
src/strategy/actions/AcceptDuelAction.h
Normal 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
|
||||
45
src/strategy/actions/AcceptInvitationAction.cpp
Normal file
45
src/strategy/actions/AcceptInvitationAction.cpp
Normal 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;
|
||||
}
|
||||
20
src/strategy/actions/AcceptInvitationAction.h
Normal file
20
src/strategy/actions/AcceptInvitationAction.h
Normal 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
|
||||
128
src/strategy/actions/AcceptQuestAction.cpp
Normal file
128
src/strategy/actions/AcceptQuestAction.cpp
Normal 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;
|
||||
}
|
||||
37
src/strategy/actions/AcceptQuestAction.h
Normal file
37
src/strategy/actions/AcceptQuestAction.h
Normal 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
|
||||
27
src/strategy/actions/AcceptResurrectAction.cpp
Normal file
27
src/strategy/actions/AcceptResurrectAction.cpp
Normal 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;
|
||||
}
|
||||
20
src/strategy/actions/AcceptResurrectAction.h
Normal file
20
src/strategy/actions/AcceptResurrectAction.h
Normal 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
|
||||
382
src/strategy/actions/ActionContext.h
Normal file
382
src/strategy/actions/ActionContext.h
Normal 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
|
||||
86
src/strategy/actions/AddLootAction.cpp
Normal file
86
src/strategy/actions/AddLootAction.cpp
Normal 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);
|
||||
}
|
||||
41
src/strategy/actions/AddLootAction.h
Normal file
41
src/strategy/actions/AddLootAction.h
Normal 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
Reference in New Issue
Block a user