mirror of
https://github.com/mod-playerbots/mod-playerbots
synced 2025-11-29 15:58:20 +08:00
feat: addclass feature
This commit is contained in:
@@ -40,7 +40,7 @@ AiPlayerbot.PreQuests = 0
|
||||
|
||||
# Random bot count
|
||||
AiPlayerbot.MinRandomBots = 50
|
||||
AiPlayerbot.MaxRandomBots = 200
|
||||
AiPlayerbot.MaxRandomBots = 50
|
||||
AiPlayerbot.RandomBotMinLevel = 1
|
||||
AiPlayerbot.RandomBotMaxLevel = 80
|
||||
|
||||
@@ -76,7 +76,7 @@ AiPlayerbot.RandombotsWalkingRPG = 0
|
||||
AiPlayerbot.RandombotsWalkingRPG.InDoors = 0
|
||||
|
||||
# Bots greet to the players
|
||||
AiPlayerbot.EnableGreet = 1
|
||||
AiPlayerbot.EnableGreet = 0
|
||||
|
||||
# Show helmet and cloak on randombots (reset required)
|
||||
AiPlayerbot.RandomBotShowHelmet = 1
|
||||
@@ -84,7 +84,7 @@ AiPlayerbot.RandomBotShowCloak = 1
|
||||
|
||||
# Disable random levels for randombots
|
||||
# Every bots started on the specified level and level up by killing mobs.
|
||||
AiPlayerbot.DisableRandomLevels = 1
|
||||
AiPlayerbot.DisableRandomLevels = 0
|
||||
|
||||
# Set randombots starting level here if "AiPlayerbot.DisableRandomLevels" enabled
|
||||
# Recommended: 5+
|
||||
@@ -138,7 +138,7 @@ AiPlayerbot.SyncLevelWithPlayers = 0
|
||||
|
||||
# Give free food to random bots
|
||||
# Default: 1 (enabled)
|
||||
AiPlayerbot.FreeFood = 1
|
||||
AiPlayerbot.FreeFood = 0
|
||||
|
||||
# Bot automatically trains spells when talking to trainer (yes = train all available spells as long as the bot has the money, free = auto trains with no money cost, no = only list spells)
|
||||
AiPlayerbot.AutoTrainSpells = yes
|
||||
@@ -156,7 +156,7 @@ AiPlayerbot.AutoLearnQuestSpells = 0
|
||||
|
||||
# Random Bots will pick quests on their own and try to complete
|
||||
# Default: 0 (disabled)
|
||||
AiPlayerbot.AutoDoQuests = 1
|
||||
AiPlayerbot.AutoDoQuests = 0
|
||||
|
||||
##################################################################################
|
||||
# #
|
||||
@@ -656,3 +656,93 @@ Playerbots.Updates.EnableDatabases = 1
|
||||
|
||||
Appender.Playerbots=2,5,0,Playerbots.log,w
|
||||
Logger.playerbots=5,Playerbots
|
||||
|
||||
|
||||
##############################################
|
||||
# TalentSpec #
|
||||
##############################################
|
||||
|
||||
# Warrior
|
||||
AiPlayerbot.RandomClassSpecProbability.1.0 = 20
|
||||
AiPlayerbot.DefaultTalentsOrder.1.0 =
|
||||
AiPlayerbot.RandomClassSpecProbability.1.1 = 30
|
||||
AiPlayerbot.DefaultTalentsOrder.1.1 =
|
||||
AiPlayerbot.RandomClassSpecProbability.1.2 = 50
|
||||
AiPlayerbot.DefaultTalentsOrder.1.2 =
|
||||
# Paladin
|
||||
AiPlayerbot.RandomClassSpecProbability.2.0 = 20
|
||||
AiPlayerbot.DefaultTalentsOrder.2.0 =
|
||||
AiPlayerbot.RandomClassSpecProbability.2.1 = 50
|
||||
AiPlayerbot.DefaultTalentsOrder.2.1 =
|
||||
AiPlayerbot.RandomClassSpecProbability.2.2 = 30
|
||||
AiPlayerbot.DefaultTalentsOrder.2.2 =
|
||||
# Hunter
|
||||
AiPlayerbot.RandomClassSpecProbability.3.0 = 25
|
||||
AiPlayerbot.DefaultTalentsOrder.3.0 =
|
||||
AiPlayerbot.RandomClassSpecProbability.3.1 = 50
|
||||
AiPlayerbot.DefaultTalentsOrder.3.1 =
|
||||
AiPlayerbot.RandomClassSpecProbability.3.2 = 25
|
||||
AiPlayerbot.DefaultTalentsOrder.3.2 =
|
||||
# Rogue
|
||||
AiPlayerbot.RandomClassSpecProbability.4.0 = 40
|
||||
AiPlayerbot.DefaultTalentsOrder.4.0 = 0-0-2-5, 0-1-3-3, 0-1-0-3, 0-2-2-5, 0-3-2-5, 0-3-1-3, 0-4-1-1, 0-5-2-2, 0-5-1-5, 0-6-1-1, 0-2-1-2, 0-7-2-3, 0-7-0-3, 0-8-1-1, 0-8-0-3, 0-9-1-5, 0-10-1-1, 1-0-2-5, 1-1-3-5, 1-2-2-5, 1-3-2-3, 2-0-0-2
|
||||
AiPlayerbot.RandomClassSpecProbability.4.1 = 50
|
||||
AiPlayerbot.DefaultTalentsOrder.4.1 =
|
||||
AiPlayerbot.RandomClassSpecProbability.4.2 = 10
|
||||
AiPlayerbot.DefaultTalentsOrder.4.2 =
|
||||
# Priest
|
||||
AiPlayerbot.RandomClassSpecProbability.5.0 = 40
|
||||
AiPlayerbot.DefaultTalentsOrder.5.0 =
|
||||
AiPlayerbot.RandomClassSpecProbability.5.1 = 40
|
||||
AiPlayerbot.DefaultTalentsOrder.5.1 =
|
||||
AiPlayerbot.RandomClassSpecProbability.5.2 = 20
|
||||
AiPlayerbot.DefaultTalentsOrder.5.2 =
|
||||
# DeathKnight
|
||||
AiPlayerbot.RandomClassSpecProbability.6.0 = 33
|
||||
AiPlayerbot.DefaultTalentsOrder.6.0 =
|
||||
AiPlayerbot.RandomClassSpecProbability.6.1 = 33
|
||||
AiPlayerbot.DefaultTalentsOrder.6.1 =
|
||||
AiPlayerbot.RandomClassSpecProbability.6.2 = 33
|
||||
AiPlayerbot.DefaultTalentsOrder.6.2 =
|
||||
# Shaman
|
||||
AiPlayerbot.RandomClassSpecProbability.7.0 = 10
|
||||
AiPlayerbot.DefaultTalentsOrder.7.0 =
|
||||
AiPlayerbot.RandomClassSpecProbability.7.1 = 45
|
||||
AiPlayerbot.DefaultTalentsOrder.7.1 =
|
||||
AiPlayerbot.RandomClassSpecProbability.7.2 = 45
|
||||
AiPlayerbot.DefaultTalentsOrder.7.2 =
|
||||
# Mage
|
||||
AiPlayerbot.RandomClassSpecProbability.8.0 = 20
|
||||
AiPlayerbot.DefaultTalentsOrder.8.0 =
|
||||
AiPlayerbot.RandomClassSpecProbability.8.1 = 10
|
||||
AiPlayerbot.DefaultTalentsOrder.8.1 =
|
||||
AiPlayerbot.RandomClassSpecProbability.8.2 = 70
|
||||
AiPlayerbot.DefaultTalentsOrder.8.2 =
|
||||
# Warlock
|
||||
AiPlayerbot.RandomClassSpecProbability.9.0 = 33
|
||||
AiPlayerbot.DefaultTalentsOrder.9.0 =
|
||||
AiPlayerbot.RandomClassSpecProbability.9.1 = 33
|
||||
AiPlayerbot.DefaultTalentsOrder.9.1 =
|
||||
AiPlayerbot.RandomClassSpecProbability.9.2 = 33
|
||||
AiPlayerbot.DefaultTalentsOrder.9.2 =
|
||||
# Druid
|
||||
AiPlayerbot.RandomClassSpecProbability.11.0 = 10
|
||||
AiPlayerbot.DefaultTalentsOrder.11.0 =
|
||||
AiPlayerbot.RandomClassSpecProbability.11.1 = 45
|
||||
AiPlayerbot.DefaultTalentsOrder.11.1 =
|
||||
AiPlayerbot.RandomClassSpecProbability.11.2 = 45
|
||||
AiPlayerbot.DefaultTalentsOrder.11.2 =
|
||||
|
||||
# no idea - TC was requesting these.. so..
|
||||
AiPlayerbot.RandomClassSpecProbability.0.0 = 33
|
||||
AiPlayerbot.DefaultTalentsOrder.0.0 =
|
||||
AiPlayerbot.RandomClassSpecProbability.0.1 = 33
|
||||
AiPlayerbot.DefaultTalentsOrder.0.1 =
|
||||
AiPlayerbot.RandomClassSpecProbability.0.2 = 33
|
||||
AiPlayerbot.DefaultTalentsOrder.0.2 =
|
||||
AiPlayerbot.RandomClassSpecProbability.10.0 = 33
|
||||
AiPlayerbot.DefaultTalentsOrder.10.0 =
|
||||
AiPlayerbot.RandomClassSpecProbability.10.1 = 33
|
||||
AiPlayerbot.DefaultTalentsOrder.10.1 =
|
||||
AiPlayerbot.RandomClassSpecProbability.10.2 = 33
|
||||
AiPlayerbot.DefaultTalentsOrder.10.2 =
|
||||
@@ -1658,12 +1658,12 @@ bool PlayerbotAI::CanCastSpell(uint32 spellid, Unit* target, bool checkHasSpell,
|
||||
|
||||
if (!itemTarget)
|
||||
{
|
||||
bool positiveSpell = spellInfo->IsPositive();
|
||||
if (positiveSpell && bot->IsHostileTo(target))
|
||||
return false;
|
||||
// bool positiveSpell = spellInfo->IsPositive();
|
||||
// if (positiveSpell && bot->IsHostileTo(target))
|
||||
// return false;
|
||||
|
||||
if (!positiveSpell && bot->IsFriendlyTo(target))
|
||||
return false;
|
||||
// if (!positiveSpell && bot->IsFriendlyTo(target))
|
||||
// return false;
|
||||
|
||||
bool damage = false;
|
||||
for (uint8 i = EFFECT_0; i <= EFFECT_2; i++)
|
||||
@@ -2103,7 +2103,7 @@ bool PlayerbotAI::CastSpell(uint32 spellId, float x, float y, float z, Item* ite
|
||||
bot->StopMoving();
|
||||
SetNextCheckDelay(sPlayerbotAIConfig->globalCoolDown);
|
||||
spell->cancel();
|
||||
//delete spell;
|
||||
delete spell;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -2113,7 +2113,7 @@ bool PlayerbotAI::CastSpell(uint32 spellId, float x, float y, float z, Item* ite
|
||||
if (!loot.IsLootPossible(bot))
|
||||
{
|
||||
spell->cancel();
|
||||
//delete spell;
|
||||
delete spell;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -156,6 +156,24 @@ bool PlayerbotAIConfig::Initialize()
|
||||
LOG_INFO("server.loading", " Loading TalentSpecs ");
|
||||
LOG_INFO("server.loading", "---------------------------------------");
|
||||
|
||||
for (uint32 cls = 1; cls < MAX_CLASSES; ++cls)
|
||||
{
|
||||
for (uint32 spec = 0; spec < 3; ++spec)
|
||||
{
|
||||
std::ostringstream os; os << "AiPlayerbot.RandomClassSpecProbability." << cls << "." << spec;
|
||||
specProbability[cls][spec] = sConfigMgr->GetOption<uint32>(os.str().c_str(), 33);
|
||||
|
||||
os.str("");
|
||||
os.clear();
|
||||
os << "AiPlayerbot.DefaultTalentsOrder." << cls << "." << spec;
|
||||
std::string temp_talents_order = sConfigMgr->GetOption<std::string>(os.str().c_str(), "");
|
||||
defaultTalentsOrder[cls][spec] = ParseTempTalentsOrder(temp_talents_order);
|
||||
if (defaultTalentsOrder[cls][spec].size() > 0) {
|
||||
sLog->outMessage("playerbot", LOG_LEVEL_INFO, "default talents order for cls %d spec %d loaded.", cls, spec);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (uint32 cls = 1; cls < MAX_CLASSES; ++cls)
|
||||
{
|
||||
classSpecs[cls] = ClassSpecs(1 << (cls - 1));
|
||||
@@ -491,3 +509,37 @@ void PlayerbotAIConfig::loadWorldBuf(uint32 factionId1, uint32 classId1, uint32
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static std::vector<std::string> split(const std::string &str, const std::string &pattern)
|
||||
{
|
||||
std::vector<std::string> res;
|
||||
if(str == "")
|
||||
return res;
|
||||
//在字符串末尾也加入分隔符,方便截取最后一段
|
||||
std::string strs = str + pattern;
|
||||
size_t pos = strs.find(pattern);
|
||||
|
||||
while(pos != strs.npos)
|
||||
{
|
||||
std::string temp = strs.substr(0, pos);
|
||||
res.push_back(temp);
|
||||
//去掉已分割的字符串,在剩下的字符串中进行分割
|
||||
strs = strs.substr(pos+1, strs.size());
|
||||
pos = strs.find(pattern);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
std::vector<std::vector<uint32>> PlayerbotAIConfig::ParseTempTalentsOrder(std::string temp_talents_order) {
|
||||
std::vector<std::vector<uint32>> res;
|
||||
std::vector<std::string> pieces = split(temp_talents_order, ",");
|
||||
for (std::string piece : pieces) {
|
||||
uint32 tab, row, col, lvl;
|
||||
if (sscanf(piece.c_str(), "%u-%u-%u-%u", &tab, &row, &col, &lvl) == -1) {
|
||||
break;
|
||||
}
|
||||
res.push_back({tab, row, col, lvl});
|
||||
}
|
||||
return res;
|
||||
}
|
||||
@@ -170,7 +170,8 @@ class PlayerbotAIConfig
|
||||
void log(std::string const fileName, const char* str, ...);
|
||||
|
||||
void loadWorldBuf(uint32 factionId, uint32 classId, uint32 minLevel, uint32 maxLevel);
|
||||
|
||||
private:
|
||||
std::vector<std::vector<uint32>> ParseTempTalentsOrder(std::string temp_talents_order);
|
||||
};
|
||||
|
||||
#define sPlayerbotAIConfig PlayerbotAIConfig::instance()
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include "InventoryAction.h"
|
||||
#include "SharedDefines.h"
|
||||
#include <random>
|
||||
#include <utility>
|
||||
|
||||
#define PLAYER_SKILL_INDEX(x) (PLAYER_SKILL_INFO_1_1 + ((x)*3))
|
||||
#define ITEM_SUBCLASS_SINGLE_HAND (\
|
||||
@@ -133,9 +134,9 @@ void PlayerbotFactory::Randomize(bool incremental)
|
||||
bot->SaveToDB(false, false);
|
||||
if (!incremental)
|
||||
{
|
||||
ClearInventory();
|
||||
ResetQuests();
|
||||
}
|
||||
ClearInventory();
|
||||
bot->SaveToDB(false, false);
|
||||
|
||||
bot->GiveLevel(level);
|
||||
@@ -1820,73 +1821,6 @@ void PlayerbotFactory::InitAvailableSpells()
|
||||
}
|
||||
}
|
||||
}
|
||||
// bot->LearnDefaultSkills();
|
||||
// bot->LearnCustomSpells();
|
||||
// bot->learnQuestRewardedSpells();
|
||||
|
||||
// CreatureTemplateContainer const* creatures = sObjectMgr->GetCreatureTemplates();
|
||||
// for (CreatureTemplateContainer::const_iterator itr = creatures->begin(); itr != creatures->end(); ++itr)
|
||||
// {
|
||||
// if (itr->second.trainer_type != TRAINER_TYPE_TRADESKILLS && itr->second.trainer_type != TRAINER_TYPE_CLASS)
|
||||
// continue;
|
||||
|
||||
// if (itr->second.trainer_type == TRAINER_TYPE_CLASS && itr->second.trainer_class != bot->getClass())
|
||||
// continue;
|
||||
|
||||
// TrainerSpellData const* trainer_spells = sObjectMgr->GetNpcTrainerSpells(itr->second.Entry);
|
||||
// if (!trainer_spells)
|
||||
// continue;
|
||||
|
||||
// for (TrainerSpellMap::const_iterator iter = trainer_spells->spellList.begin(); iter != trainer_spells->spellList.end(); ++iter)
|
||||
// {
|
||||
// TrainerSpell const* tSpell = &iter->second;
|
||||
// if (!tSpell)
|
||||
// continue;
|
||||
|
||||
// TrainerSpellState state = bot->GetTrainerSpellState(tSpell);
|
||||
// if (state != TRAINER_SPELL_GREEN)
|
||||
// continue;
|
||||
|
||||
// SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(tSpell->spell);
|
||||
// if (!spellInfo)
|
||||
// continue;
|
||||
|
||||
// if (itr->second.trainer_type == TRAINER_TYPE_TRADESKILLS)
|
||||
// {
|
||||
// std::string const SpellName = spellInfo->SpellName[0];
|
||||
// if (spellInfo->Effects[EFFECT_1].Effect == SPELL_EFFECT_SKILL_STEP)
|
||||
// {
|
||||
// uint32 skill = spellInfo->Effects[EFFECT_1].MiscValue;
|
||||
// if (skill && !bot->HasSkill(skill))
|
||||
// {
|
||||
// SkillLineEntry const* pSkill = sSkillLineStore.LookupEntry(skill);
|
||||
// if (pSkill)
|
||||
// {
|
||||
// if (SpellName.find("Apprentice") != std::string::npos && pSkill->categoryId == SKILL_CATEGORY_PROFESSION || pSkill->categoryId == SKILL_CATEGORY_SECONDARY)
|
||||
// continue;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// // bool learned = false;
|
||||
// // for (uint8 j = 0; j < 3; ++j)
|
||||
// // {
|
||||
// // if (!tSpell->learnedSpell[j] && !bot->IsSpellFitByClassAndRace(tSpell->learnedSpell[j]))
|
||||
// // continue;
|
||||
|
||||
// // if (spellInfo->Effects[j].Effect == SPELL_EFFECT_LEARN_SPELL)
|
||||
// // {
|
||||
// // uint32 learnedSpell = spellInfo->Effects[j].TriggerSpell;
|
||||
// // bot->learnSpell(learnedSpell);
|
||||
// // learned = true;
|
||||
// // }
|
||||
// // }
|
||||
|
||||
// // if (!learned)
|
||||
// bot->learnSpell(tSpell->spell);
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
void PlayerbotFactory::InitClassSpells()
|
||||
@@ -1969,6 +1903,7 @@ void PlayerbotFactory::InitClassSpells()
|
||||
case CLASS_SHAMAN:
|
||||
bot->learnSpell(403, true);
|
||||
bot->learnSpell(331, true);
|
||||
bot->learnSpell(66747, false); // Totem of the Earthen Ring
|
||||
if (level >= 4) {
|
||||
bot->learnSpell(8071, false); // stoneskin totem
|
||||
}
|
||||
@@ -2347,182 +2282,148 @@ void PlayerbotFactory::InitPotions()
|
||||
|
||||
void PlayerbotFactory::InitFood()
|
||||
{
|
||||
uint32 categories[] = { 11, 59 };
|
||||
for (uint8 i = 0; i < 2; ++i)
|
||||
std::map<uint32, std::vector<uint32> > items;
|
||||
ItemTemplateContainer const* itemTemplateContainer = sObjectMgr->GetItemTemplateStore();
|
||||
for (ItemTemplateContainer::const_iterator i = itemTemplateContainer->begin(); i != itemTemplateContainer->end(); ++i)
|
||||
{
|
||||
uint32 category = categories[i];
|
||||
|
||||
FindFoodVisitor visitor(bot, category);
|
||||
IterateItems(&visitor);
|
||||
if (!visitor.GetResult().empty())
|
||||
continue;
|
||||
|
||||
uint32 itemId = sRandomItemMgr->GetFood(level, category);
|
||||
if (!itemId)
|
||||
{
|
||||
LOG_INFO("playerbots", "No food (category {}) available for bot {} ({} level)", category, bot->GetName().c_str(), bot->getLevel());
|
||||
continue;
|
||||
}
|
||||
|
||||
uint32 itemId = i->first;
|
||||
ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId);
|
||||
if (!proto)
|
||||
continue;
|
||||
|
||||
uint32 maxCount = proto->GetMaxStackSize();
|
||||
if (Item* newItem = StoreNewItemInInventorySlot(bot, itemId, urand(maxCount / 2, maxCount)))
|
||||
newItem->AddToUpdateQueueOf(bot);
|
||||
if (proto->Class != ITEM_CLASS_CONSUMABLE ||
|
||||
proto->SubClass != ITEM_SUBCLASS_FOOD ||
|
||||
(proto->Spells[0].SpellCategory != 11 && proto->Spells[0].SpellCategory != 59) ||
|
||||
proto->Bonding != NO_BIND)
|
||||
continue;
|
||||
|
||||
if (proto->RequiredLevel > bot->getLevel() || proto->RequiredLevel < bot->getLevel() - 9)
|
||||
continue;
|
||||
|
||||
if (proto->RequiredSkill && !bot->HasSkill(proto->RequiredSkill))
|
||||
continue;
|
||||
|
||||
if (proto->Area || proto->Map || proto->RequiredCityRank || proto->RequiredHonorRank)
|
||||
continue;
|
||||
|
||||
items[proto->Spells[0].SpellCategory].push_back(itemId);
|
||||
}
|
||||
|
||||
uint32 categories[] = { 11, 59 };
|
||||
for (int i = 0; i < sizeof(categories) / sizeof(uint32); ++i)
|
||||
{
|
||||
uint32 category = categories[i];
|
||||
std::vector<uint32>& ids = items[category];
|
||||
int tries = 0;
|
||||
for (int j = 0; j < 2; j++) {
|
||||
uint32 index = urand(0, ids.size() - 1);
|
||||
if (index >= ids.size())
|
||||
continue;
|
||||
|
||||
uint32 itemId = ids[index];
|
||||
ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId);
|
||||
// beer / wine ...
|
||||
if (proto->Spells[0].SpellId == 11007 || proto->Spells[0].SpellId == 11008 || proto->Spells[0].SpellId == 11009 ||
|
||||
proto->Spells[0].SpellId == 11629 || proto->Spells[0].SpellId == 50986)
|
||||
{
|
||||
tries++;
|
||||
if (tries > 5) {
|
||||
continue;
|
||||
}
|
||||
j--;
|
||||
continue;
|
||||
}
|
||||
// bot->StoreNewItemInBestSlots(itemId, urand(1, proto->GetMaxStackSize()));
|
||||
bot->StoreNewItemInBestSlots(itemId, proto->GetMaxStackSize());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PlayerbotFactory::InitReagents()
|
||||
{
|
||||
std::vector<uint32> items;
|
||||
uint32 regCount = 1;
|
||||
switch (bot->getClass())
|
||||
int level = bot->getLevel();
|
||||
std::vector<std::pair<uint32, uint32>> items;
|
||||
switch (bot->getClass())
|
||||
{
|
||||
case CLASS_MAGE:
|
||||
regCount = 2;
|
||||
if (bot->getLevel() > 11)
|
||||
items = { 17056 };
|
||||
if (bot->getLevel() > 19)
|
||||
items = { 17056, 17031 };
|
||||
if (bot->getLevel() > 35)
|
||||
items = { 17056, 17031, 17032 };
|
||||
if (bot->getLevel() > 55)
|
||||
items = { 17056, 17031, 17032, 17020 };
|
||||
break;
|
||||
case CLASS_DRUID:
|
||||
regCount = 2;
|
||||
if (bot->getLevel() > 19)
|
||||
items = { 17034 };
|
||||
if (bot->getLevel() > 29)
|
||||
items = { 17035 };
|
||||
if (bot->getLevel() > 39)
|
||||
items = { 17036 };
|
||||
if (bot->getLevel() > 49)
|
||||
items = { 17037, 17021 };
|
||||
if (bot->getLevel() > 59)
|
||||
items = { 17038, 17026 };
|
||||
if (bot->getLevel() > 69)
|
||||
items = { 22147, 22148 };
|
||||
break;
|
||||
case CLASS_PALADIN:
|
||||
regCount = 3;
|
||||
if (bot->getLevel() > 50)
|
||||
items = { 21177 };
|
||||
case CLASS_ROGUE:
|
||||
{
|
||||
std::vector<int> instant_poison_ids = {43231, 43230, 21927, 8928, 8927, 8926, 6950, 6949, 6947};
|
||||
std::vector<int> deadly_poison_ids = {43233, 43232, 22054, 22053, 20844, 8985, 8984, 2893, 2892};
|
||||
for (int& itemId: deadly_poison_ids) {
|
||||
ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId);
|
||||
if (proto->RequiredLevel > bot->getLevel())
|
||||
continue;
|
||||
items.push_back({itemId, 20}); // deadly poison
|
||||
break;
|
||||
}
|
||||
for (int& itemId: instant_poison_ids) {
|
||||
ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId);
|
||||
if (proto->RequiredLevel > bot->getLevel())
|
||||
continue;
|
||||
items.push_back({itemId, 20}); // instant poison
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case CLASS_SHAMAN:
|
||||
regCount = 1;
|
||||
if (bot->getLevel() > 22)
|
||||
items = { 17057 };
|
||||
if (bot->getLevel() > 28)
|
||||
items = { 17057, 17058 };
|
||||
if (bot->getLevel() > 29)
|
||||
items = { 17057, 17058, 17030 };
|
||||
// items.push_back({46978, 1}); // Totem
|
||||
if (bot->getLevel() >= 30)
|
||||
items.push_back({17030, 40}); // Ankh
|
||||
break;
|
||||
case CLASS_WARLOCK:
|
||||
regCount = 10;
|
||||
if (bot->getLevel() > 9)
|
||||
items = { 6265 };
|
||||
items.push_back({6265, 10}); // shard
|
||||
break;
|
||||
case CLASS_PRIEST:
|
||||
regCount = 3;
|
||||
if (bot->getLevel() > 48)
|
||||
items = { 17028 };
|
||||
if (bot->getLevel() > 55)
|
||||
items = { 17028, 17029 };
|
||||
if (level >= 48 && level < 60) {
|
||||
items.push_back({17028, 40});
|
||||
// bot->StoreNewItemInBestSlots(17028, 40); // Wild Berries
|
||||
} else if (level >= 60 && level < 80) {
|
||||
items.push_back({17029, 40});
|
||||
// bot->StoreNewItemInBestSlots(17029, 40); // Wild Berries
|
||||
} else if (level >= 80) {
|
||||
items.push_back({44615, 40});
|
||||
// bot->StoreNewItemInBestSlots(44615, 40); // Wild Berries
|
||||
}
|
||||
break;
|
||||
case CLASS_ROGUE:
|
||||
regCount = 1;
|
||||
if (bot->getLevel() > 21)
|
||||
items = { 5140 };
|
||||
if (bot->getLevel() > 33)
|
||||
items = { 5140, 5530 };
|
||||
case CLASS_MAGE:
|
||||
items.push_back({17020, 40});
|
||||
// bot->StoreNewItemInBestSlots(17020, 40); // Arcane Powder
|
||||
break;
|
||||
case CLASS_DRUID:
|
||||
if (level >= 20 && level < 30) {
|
||||
items.push_back({17034, 40});
|
||||
}
|
||||
if (level >= 30 && level < 40) {
|
||||
items.push_back({17035, 40});
|
||||
}
|
||||
if (level >= 40 && level < 50) {
|
||||
items.push_back({17036, 40});
|
||||
}
|
||||
if (level >= 50 && level < 60) {
|
||||
items.push_back({17037, 40});
|
||||
items.push_back({17021, 40});
|
||||
}
|
||||
if (level >= 60 && level < 70) {
|
||||
items.push_back({17038, 40});
|
||||
items.push_back({17026, 40});
|
||||
}
|
||||
if (level >= 70 && level < 80) {
|
||||
items.push_back({22147, 40});
|
||||
items.push_back({22148, 40});
|
||||
}
|
||||
if (level >= 80) {
|
||||
items.push_back({44614, 40});
|
||||
items.push_back({44605, 40});
|
||||
}
|
||||
break;
|
||||
case CLASS_PALADIN:
|
||||
items.push_back({21177, 100});
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/*for (uint32 itemID : items)
|
||||
{
|
||||
ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemID);
|
||||
if (!proto)
|
||||
{
|
||||
LOG_ERROR("playerbots", "No reagent (ItemId {}) found for bot {} (Class:{})", i, bot->GetGUID().ToString().c_str(), bot->getClass());
|
||||
continue;
|
||||
}
|
||||
|
||||
uint32 maxCount = proto->GetMaxStackSize();
|
||||
|
||||
QueryItemCountVisitor visitor(itemID);
|
||||
IterateItems(&visitor);
|
||||
if (visitor.GetCount() > maxCount) continue;
|
||||
|
||||
uint32 randCount = urand(maxCount / 2, maxCount * regCount);
|
||||
|
||||
Item* newItem = bot->StoreNewItemInInventorySlot(*i, randCount);
|
||||
if (newItem)
|
||||
newItem->AddToUpdateQueueOf(bot);
|
||||
|
||||
LOG_INFO("playerbots", "Bot {} got reagent {} x{}", bot->GetGUID().ToString().c_str(), proto->Name1.c_str(), randCount);
|
||||
}*/
|
||||
|
||||
for (PlayerSpellMap::iterator itr = bot->GetSpellMap().begin(); itr != bot->GetSpellMap().end(); ++itr)
|
||||
{
|
||||
uint32 spellId = itr->first;
|
||||
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId);
|
||||
if (!spellInfo)
|
||||
continue;
|
||||
|
||||
if (itr->second->State == PLAYERSPELL_REMOVED || itr->second->Active || spellInfo->IsPassive())
|
||||
continue;
|
||||
|
||||
if (spellInfo->Effects[0].Effect == SPELL_EFFECT_LEARN_SPELL)
|
||||
continue;
|
||||
|
||||
for (const auto& reagent : spellInfo->Reagent)
|
||||
{
|
||||
if (reagent)
|
||||
{
|
||||
ItemTemplate const* proto = sObjectMgr->GetItemTemplate(reagent);
|
||||
if (!proto)
|
||||
{
|
||||
LOG_ERROR("playerbots", "No reagent (ItemId {}) found for bot {} (Class:{})", reagent, bot->GetGUID().ToString().c_str(), bot->getClass());
|
||||
continue;
|
||||
}
|
||||
|
||||
uint32 maxCount = proto->GetMaxStackSize();
|
||||
|
||||
QueryItemCountVisitor visitor(reagent);
|
||||
IterateItems(&visitor);
|
||||
if (visitor.GetCount() > maxCount) continue;
|
||||
|
||||
uint32 randCount = urand(maxCount / 2, maxCount * regCount);
|
||||
|
||||
Item* newItem = StoreNewItemInInventorySlot(bot, reagent, randCount);
|
||||
if (newItem)
|
||||
newItem->AddToUpdateQueueOf(bot);
|
||||
|
||||
LOG_INFO("playerbots", "Bot {} got reagent {} x{}", bot->GetGUID().ToString().c_str(), proto->Name1.c_str(), randCount);
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& totem : spellInfo->Totem)
|
||||
{
|
||||
if (totem && !bot->HasItemCount(totem, 1))
|
||||
{
|
||||
ItemTemplate const* proto = sObjectMgr->GetItemTemplate(totem);
|
||||
if (!proto)
|
||||
{
|
||||
LOG_ERROR("playerbots", "No totem (ItemId {}) found for bot {} (Class:{})", totem, bot->GetGUID().ToString().c_str(), bot->getClass());
|
||||
continue;
|
||||
}
|
||||
|
||||
Item* newItem = StoreNewItemInInventorySlot(bot, totem, 1);
|
||||
if (newItem)
|
||||
newItem->AddToUpdateQueueOf(bot);
|
||||
|
||||
LOG_INFO("playerbots", "Bot {} got totem {} x{}", bot->GetGUID().ToString().c_str(), proto->Name1.c_str(), 1);
|
||||
}
|
||||
}
|
||||
for (std::pair item : items) {
|
||||
bot->StoreNewItemInBestSlots(item.first, item.second);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,11 +3,16 @@
|
||||
*/
|
||||
|
||||
#include "CharacterPackets.h"
|
||||
#include "ObjectAccessor.h"
|
||||
#include "ObjectMgr.h"
|
||||
#include "PlayerbotMgr.h"
|
||||
#include "Playerbots.h"
|
||||
#include "PlayerbotDbStore.h"
|
||||
#include "PlayerbotFactory.h"
|
||||
#include "WorldSession.h"
|
||||
#include "ChannelMgr.h"
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
|
||||
PlayerbotHolder::PlayerbotHolder() : PlayerbotAIBase(false)
|
||||
{
|
||||
@@ -417,6 +422,10 @@ void PlayerbotHolder::OnBotLogin(Player* const bot)
|
||||
|
||||
botAI->TellMaster("Hello!");
|
||||
|
||||
if (master && master->GetGroup() && !group) {
|
||||
master->GetGroup()->AddMember(bot);
|
||||
}
|
||||
|
||||
// bots join World chat if not solo oriented
|
||||
if (bot->getLevel() >= 10 && sRandomPlayerbotMgr->IsRandomBot(bot) && GET_PLAYERBOT_AI(bot) && GET_PLAYERBOT_AI(bot)->GetGrouperType() != GrouperType::SOLO)
|
||||
{
|
||||
@@ -609,7 +618,7 @@ std::vector<std::string> PlayerbotHolder::HandlePlayerbotCommand(char const* arg
|
||||
if (!*args)
|
||||
{
|
||||
messages.push_back("usage: list/reload/tweak/self or add/init/remove PLAYERNAME");
|
||||
return std::move(messages);
|
||||
return messages;
|
||||
}
|
||||
|
||||
char* cmd = strtok ((char*)args, " ");
|
||||
@@ -617,13 +626,13 @@ std::vector<std::string> PlayerbotHolder::HandlePlayerbotCommand(char const* arg
|
||||
if (!cmd)
|
||||
{
|
||||
messages.push_back("usage: list/reload/tweak/self or add/init/remove PLAYERNAME");
|
||||
return std::move(messages);
|
||||
return messages;
|
||||
}
|
||||
|
||||
if (!strcmp(cmd, "list"))
|
||||
{
|
||||
messages.push_back(ListBots(master));
|
||||
return std::move(messages);
|
||||
return messages;
|
||||
}
|
||||
|
||||
if (!strcmp(cmd, "reload"))
|
||||
@@ -663,10 +672,103 @@ std::vector<std::string> PlayerbotHolder::HandlePlayerbotCommand(char const* arg
|
||||
return messages;
|
||||
}
|
||||
|
||||
if (!strcmp(cmd, "lookup"))
|
||||
{
|
||||
messages.push_back(LookupBots(master));
|
||||
return messages;
|
||||
}
|
||||
|
||||
if (!strcmp(cmd, "addclass"))
|
||||
{
|
||||
uint8 claz;
|
||||
if (!strcmp(charname, "warrior"))
|
||||
{
|
||||
claz = 1;
|
||||
}
|
||||
else if (!strcmp(charname, "paladin"))
|
||||
{
|
||||
claz = 2;
|
||||
}
|
||||
else if (!strcmp(charname, "hunter"))
|
||||
{
|
||||
claz = 3;
|
||||
}
|
||||
else if (!strcmp(charname, "rogue"))
|
||||
{
|
||||
claz = 4;
|
||||
}
|
||||
else if (!strcmp(charname, "priest"))
|
||||
{
|
||||
claz = 5;
|
||||
}
|
||||
else if (!strcmp(charname, "shaman"))
|
||||
{
|
||||
claz = 7;
|
||||
}
|
||||
else if (!strcmp(charname, "mage"))
|
||||
{
|
||||
claz = 8;
|
||||
}
|
||||
else if (!strcmp(charname, "warlock"))
|
||||
{
|
||||
claz = 9;
|
||||
}
|
||||
else if (!strcmp(charname, "druid"))
|
||||
{
|
||||
claz = 11;
|
||||
}
|
||||
else if (!strcmp(charname, "dk"))
|
||||
{
|
||||
claz = 6;
|
||||
}
|
||||
else
|
||||
{
|
||||
messages.push_back("Error: Invalid Class. Try again.");
|
||||
return messages;
|
||||
}
|
||||
uint8 master_race = master->getRace();
|
||||
std::string race_limit;
|
||||
switch (master_race)
|
||||
{
|
||||
case 1:
|
||||
case 3:
|
||||
case 4:
|
||||
case 7:
|
||||
case 11:
|
||||
race_limit = "1, 3, 4, 7, 11";
|
||||
break;
|
||||
case 2:
|
||||
case 5:
|
||||
case 6:
|
||||
case 8:
|
||||
case 10:
|
||||
race_limit = "2, 5, 6, 8, 10";
|
||||
break;
|
||||
}
|
||||
QueryResult results = CharacterDatabase.Query("SELECT guid FROM characters WHERE name IN (SELECT name FROM playerbots_names) AND class = '{}' AND online = 0 AND race IN ({}) ORDER BY RAND() LIMIT 1", claz, race_limit);
|
||||
if (results)
|
||||
{
|
||||
Field* fields = results->Fetch();
|
||||
ObjectGuid guid = ObjectGuid(HighGuid::Player, fields[0].Get<uint32>());
|
||||
AddPlayerBot(guid, master->GetSession()->GetAccountId());
|
||||
|
||||
messages.push_back("addclass " + std::string(charname) + " ok");
|
||||
return messages;
|
||||
}
|
||||
messages.push_back("addclass failed.");
|
||||
return messages;
|
||||
}
|
||||
|
||||
if (!charname)
|
||||
{
|
||||
messages.push_back("usage: list/reload/tweak/self or add/init/remove PLAYERNAME");
|
||||
return std::move(messages);
|
||||
Player* tPlayer = ObjectAccessor::FindConnectedPlayer(master->GetTarget());
|
||||
if (tPlayer) {
|
||||
charname = new char[tPlayer->GetName().size() + 1];
|
||||
strcpy(charname, tPlayer->GetName().c_str());
|
||||
} else {
|
||||
messages.push_back("usage: list/reload/tweak/self or add/init/remove PLAYERNAME");
|
||||
return messages;
|
||||
}
|
||||
}
|
||||
|
||||
std::string const cmdStr = cmd;
|
||||
@@ -679,7 +781,7 @@ std::vector<std::string> PlayerbotHolder::HandlePlayerbotCommand(char const* arg
|
||||
if (!group)
|
||||
{
|
||||
messages.push_back("you must be in group");
|
||||
return std::move(messages);
|
||||
return messages;
|
||||
}
|
||||
|
||||
Group::MemberSlotList slots = group->GetMemberSlots();
|
||||
@@ -754,7 +856,7 @@ std::vector<std::string> PlayerbotHolder::HandlePlayerbotCommand(char const* arg
|
||||
messages.push_back(out.str());
|
||||
}
|
||||
|
||||
return std::move(messages);
|
||||
return messages;
|
||||
}
|
||||
|
||||
uint32 PlayerbotHolder::GetAccountId(std::string const name)
|
||||
@@ -870,6 +972,28 @@ std::string const PlayerbotHolder::ListBots(Player* master)
|
||||
return out.str();
|
||||
}
|
||||
|
||||
std::string const PlayerbotHolder::LookupBots(Player* master)
|
||||
{
|
||||
std::list<std::string> messages;
|
||||
messages.push_back("Classes Available:");
|
||||
messages.push_back("|TInterface\\icons\\INV_Sword_27.png:25:25:0:-1|t Warrior");
|
||||
messages.push_back("|TInterface\\icons\\INV_Hammer_01.png:25:25:0:-1|t Paladin");
|
||||
messages.push_back("|TInterface\\icons\\INV_Weapon_Bow_07.png:25:25:0:-1|t Hunter");
|
||||
messages.push_back("|TInterface\\icons\\INV_ThrowingKnife_04.png:25:25:0:-1|t Rogue");
|
||||
messages.push_back("|TInterface\\icons\\INV_Staff_30.png:25:25:0:-1|t Priest");
|
||||
messages.push_back("|TInterface\\icons\\inv_jewelry_talisman_04.png:25:25:0:-1|t Shaman");
|
||||
messages.push_back("|TInterface\\icons\\INV_staff_30.png:25:25:0:-1|t Mage");
|
||||
messages.push_back("|TInterface\\icons\\INV_staff_30.png:25:25:0:-1|t Warlock");
|
||||
messages.push_back("|TInterface\\icons\\Ability_Druid_Maul.png:25:25:0:-1|t Druid");
|
||||
messages.push_back("DK");
|
||||
messages.push_back("(Usage: .bot lookup CLASS)");
|
||||
std::string ret_msg;
|
||||
for (std::string msg: messages) {
|
||||
ret_msg += msg + "\n";
|
||||
}
|
||||
return ret_msg;
|
||||
}
|
||||
|
||||
PlayerbotMgr::PlayerbotMgr(Player* const master) : PlayerbotHolder(), master(master), lastErrorTell(0)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -47,6 +47,7 @@ class PlayerbotHolder : public PlayerbotAIBase
|
||||
uint32 GetAccountId(std::string const name);
|
||||
uint32 GetAccountId(ObjectGuid guid);
|
||||
std::string const ListBots(Player* master);
|
||||
std::string const LookupBots(Player* master);
|
||||
|
||||
protected:
|
||||
virtual void OnBotLoginInternal(Player* const bot) = 0;
|
||||
|
||||
@@ -40,54 +40,66 @@ void AutoLearnSpellAction::LearnSpells(std::ostringstream* out)
|
||||
void AutoLearnSpellAction::LearnTrainerSpells(std::ostringstream* out)
|
||||
{
|
||||
bot->LearnDefaultSkills();
|
||||
bot->LearnCustomSpells();
|
||||
|
||||
CreatureTemplateContainer const* creatures = sObjectMgr->GetCreatureTemplates();
|
||||
for (CreatureTemplateContainer::const_iterator itr = creatures->begin(); itr != creatures->end(); ++itr)
|
||||
CreatureTemplateContainer const* creatureTemplateContainer = sObjectMgr->GetCreatureTemplates();
|
||||
for (CreatureTemplateContainer::const_iterator i = creatureTemplateContainer->begin(); i != creatureTemplateContainer->end(); ++i)
|
||||
{
|
||||
if (itr->second.trainer_type != TRAINER_TYPE_CLASS && itr->second.trainer_type != TRAINER_TYPE_TRADESKILLS)
|
||||
CreatureTemplate const& co = i->second;
|
||||
if (co.trainer_type != TRAINER_TYPE_TRADESKILLS && co.trainer_type != TRAINER_TYPE_CLASS)
|
||||
continue;
|
||||
|
||||
if (itr->second.trainer_type == TRAINER_TYPE_CLASS && itr->second.trainer_class != bot->getClass())
|
||||
if (co.trainer_type == TRAINER_TYPE_CLASS && co.trainer_class != bot->getClass())
|
||||
continue;
|
||||
|
||||
TrainerSpellData const* trainer_spells = sObjectMgr->GetNpcTrainerSpells(itr->second.Entry);
|
||||
uint32 trainerId = co.Entry;
|
||||
|
||||
TrainerSpellData const* trainer_spells = sObjectMgr->GetNpcTrainerSpells(trainerId);
|
||||
if (!trainer_spells)
|
||||
trainer_spells = sObjectMgr->GetNpcTrainerSpells(trainerId);
|
||||
|
||||
if (!trainer_spells)
|
||||
continue;
|
||||
|
||||
for (TrainerSpellMap::const_iterator iter = trainer_spells->spellList.begin(); iter != trainer_spells->spellList.end(); ++iter)
|
||||
for (TrainerSpellMap::const_iterator itr = trainer_spells->spellList.begin(); itr != trainer_spells->spellList.end(); ++itr)
|
||||
{
|
||||
TrainerSpell const* tSpell = &iter->second;
|
||||
TrainerSpell const* tSpell = &itr->second;
|
||||
|
||||
if (!tSpell)
|
||||
continue;
|
||||
|
||||
if (!tSpell->learnedSpell[0] && !bot->IsSpellFitByClassAndRace(tSpell->learnedSpell[0]))
|
||||
continue;
|
||||
|
||||
TrainerSpellState state = bot->GetTrainerSpellState(tSpell);
|
||||
if (state != TRAINER_SPELL_GREEN)
|
||||
continue;
|
||||
|
||||
if (itr->second.trainer_type == TRAINER_TYPE_TRADESKILLS)
|
||||
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(tSpell->spell);
|
||||
bool learn = true;
|
||||
for (uint8 j = 0; j < 3; ++j)
|
||||
{
|
||||
SpellInfo const* spell = sSpellMgr->GetSpellInfo(tSpell->spell);
|
||||
if (spell)
|
||||
if (!tSpell->learnedSpell[j] && !bot->IsSpellFitByClassAndRace(tSpell->learnedSpell[j]))
|
||||
continue;
|
||||
|
||||
if (spellInfo->Effects[j].Effect == SPELL_EFFECT_PROFICIENCY ||
|
||||
spellInfo->Effects[j].Effect == SPELL_EFFECT_SKILL_STEP ||
|
||||
spellInfo->Effects[j].Effect == SPELL_EFFECT_DUAL_WIELD)
|
||||
{
|
||||
std::string const SpellName = spell->SpellName[0];
|
||||
if (spell->Effects[EFFECT_1].Effect == SPELL_EFFECT_SKILL_STEP)
|
||||
{
|
||||
uint32 skill = spell->Effects[EFFECT_1].MiscValue;
|
||||
if (skill)
|
||||
{
|
||||
SkillLineEntry const* pSkill = sSkillLineStore.LookupEntry(skill);
|
||||
if (pSkill)
|
||||
{
|
||||
if (SpellName.find("Apprentice") != std::string::npos && pSkill->categoryId == SKILL_CATEGORY_PROFESSION || pSkill->categoryId == SKILL_CATEGORY_SECONDARY)
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
learn = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!learn) {
|
||||
continue;
|
||||
}
|
||||
|
||||
LearnSpell(tSpell->spell, out);
|
||||
if (tSpell->learnedSpell[0]) {
|
||||
bot->learnSpell(tSpell->learnedSpell[0], false);
|
||||
}
|
||||
else {
|
||||
LOG_INFO("playerbots", "!tSpell->learnedSpell[0] {}", tSpell->spell);
|
||||
botAI->CastSpell(tSpell->spell, bot);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user