diff --git a/conf/playerbots.conf.dist b/conf/playerbots.conf.dist index 8b1bed28..d9002ec7 100644 --- a/conf/playerbots.conf.dist +++ b/conf/playerbots.conf.dist @@ -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 = \ No newline at end of file diff --git a/src/PlayerbotAI.cpp b/src/PlayerbotAI.cpp index 1c6e8229..f5f98c3e 100644 --- a/src/PlayerbotAI.cpp +++ b/src/PlayerbotAI.cpp @@ -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; } } diff --git a/src/PlayerbotAIConfig.cpp b/src/PlayerbotAIConfig.cpp index 8efdb364..ec637576 100644 --- a/src/PlayerbotAIConfig.cpp +++ b/src/PlayerbotAIConfig.cpp @@ -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(os.str().c_str(), 33); + + os.str(""); + os.clear(); + os << "AiPlayerbot.DefaultTalentsOrder." << cls << "." << spec; + std::string temp_talents_order = sConfigMgr->GetOption(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 split(const std::string &str, const std::string &pattern) +{ + std::vector 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> PlayerbotAIConfig::ParseTempTalentsOrder(std::string temp_talents_order) { + std::vector> res; + std::vector 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; +} \ No newline at end of file diff --git a/src/PlayerbotAIConfig.h b/src/PlayerbotAIConfig.h index c66eca2e..d372108c 100644 --- a/src/PlayerbotAIConfig.h +++ b/src/PlayerbotAIConfig.h @@ -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> ParseTempTalentsOrder(std::string temp_talents_order); }; #define sPlayerbotAIConfig PlayerbotAIConfig::instance() diff --git a/src/PlayerbotFactory.cpp b/src/PlayerbotFactory.cpp index d44a7b0e..34ee7e27 100644 --- a/src/PlayerbotFactory.cpp +++ b/src/PlayerbotFactory.cpp @@ -17,6 +17,7 @@ #include "InventoryAction.h" #include "SharedDefines.h" #include +#include #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 > 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& 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 items; - uint32 regCount = 1; - switch (bot->getClass()) + int level = bot->getLevel(); + std::vector> 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 instant_poison_ids = {43231, 43230, 21927, 8928, 8927, 8926, 6950, 6949, 6947}; + std::vector 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); } } diff --git a/src/PlayerbotMgr.cpp b/src/PlayerbotMgr.cpp index b3505c15..58b9a302 100644 --- a/src/PlayerbotMgr.cpp +++ b/src/PlayerbotMgr.cpp @@ -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 +#include 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 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 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 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()); + 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 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 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 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) { } diff --git a/src/PlayerbotMgr.h b/src/PlayerbotMgr.h index 27c56580..16e1aa95 100644 --- a/src/PlayerbotMgr.h +++ b/src/PlayerbotMgr.h @@ -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; diff --git a/src/strategy/actions/AutoLearnSpellAction.cpp b/src/strategy/actions/AutoLearnSpellAction.cpp index 9df06b0a..f1cc813b 100644 --- a/src/strategy/actions/AutoLearnSpellAction.cpp +++ b/src/strategy/actions/AutoLearnSpellAction.cpp @@ -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); + } } } }