/* * Copyright (C) 2016+ AzerothCore , 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 "PlayerbotFactory.h" #include "AccountMgr.h" #include "AiFactory.h" #include "ArenaTeamMgr.h" #include "GuildMgr.h" #include "MapMgr.h" #include "Playerbots.h" #include "PerformanceMonitor.h" #include "PlayerbotDbStore.h" #include "RandomItemMgr.h" #include "RandomPlayerbotFactory.h" #include "ItemVisitors.h" #include "InventoryAction.h" #include #define PLAYER_SKILL_INDEX(x) (PLAYER_SKILL_INFO_1_1 + ((x)*3)) uint32 PlayerbotFactory::tradeSkills[] = { SKILL_ALCHEMY, SKILL_ENCHANTING, SKILL_SKINNING, SKILL_TAILORING, SKILL_LEATHERWORKING, SKILL_ENGINEERING, SKILL_HERBALISM, SKILL_MINING, SKILL_BLACKSMITHING, SKILL_COOKING, SKILL_FIRST_AID, SKILL_FISHING, SKILL_JEWELCRAFTING }; std::list PlayerbotFactory::classQuestIds; std::list PlayerbotFactory::specialQuestIds; PlayerbotFactory::PlayerbotFactory(Player* bot, uint32 level, uint32 itemQuality) : level(level), itemQuality(itemQuality), InventoryAction(GET_PLAYERBOT_AI(bot), "factory") { } void PlayerbotFactory::Init() { if (sPlayerbotAIConfig->randomBotPreQuests) { ObjectMgr::QuestMap const& questTemplates = sObjectMgr->GetQuestTemplates(); for (ObjectMgr::QuestMap::const_iterator i = questTemplates.begin(); i != questTemplates.end(); ++i) { uint32 questId = i->first; Quest const* quest = i->second; if (!quest->GetRequiredClasses() || quest->IsRepeatable() || quest->GetMinLevel() < 10) continue; AddPrevQuests(questId, classQuestIds); classQuestIds.remove(questId); classQuestIds.push_back(questId); } } for (std::vector::iterator i = sPlayerbotAIConfig->randomBotQuestIds.begin(); i != sPlayerbotAIConfig->randomBotQuestIds.end(); ++i) { uint32 questId = *i; AddPrevQuests(questId, specialQuestIds); specialQuestIds.remove(questId); specialQuestIds.push_back(questId); } } void PlayerbotFactory::Prepare() { if (!itemQuality) { if (level < 20) itemQuality = urand(ITEM_QUALITY_NORMAL, ITEM_QUALITY_UNCOMMON); else if (level < 40) itemQuality = urand(ITEM_QUALITY_UNCOMMON, ITEM_QUALITY_RARE); else if (level < 60) itemQuality = urand(ITEM_QUALITY_UNCOMMON, ITEM_QUALITY_EPIC); else if (level < 70) itemQuality = urand(ITEM_QUALITY_RARE, ITEM_QUALITY_EPIC); else if (level < 80) itemQuality = urand(ITEM_QUALITY_RARE, ITEM_QUALITY_EPIC); else itemQuality = urand(ITEM_QUALITY_RARE, ITEM_QUALITY_EPIC); } if (bot->isDead()) bot->ResurrectPlayer(1.0f, false); bot->CombatStop(true); if (!sPlayerbotAIConfig->disableRandomLevels) { bot->SetLevel(level); } else if (bot->getLevel() < sPlayerbotAIConfig->randombotStartingLevel) { bot->SetLevel(sPlayerbotAIConfig->randombotStartingLevel); } if (!sPlayerbotAIConfig->randomBotShowHelmet || !urand(0, 4)) { bot->SetFlag(PLAYER_FLAGS, PLAYER_FLAGS_HIDE_HELM); } if (!sPlayerbotAIConfig->randomBotShowCloak || !urand(0, 4)) { bot->SetFlag(PLAYER_FLAGS, PLAYER_FLAGS_HIDE_CLOAK); } } void PlayerbotFactory::Randomize(bool incremental) { if (sPlayerbotAIConfig->disableRandomLevels) { return; } LOG_INFO("playerbots", "Preparing to {} randomize...", (incremental ? "incremental" : "full")); Prepare(); LOG_INFO("playerbots", "Resetting player..."); PerformanceMonitorOperation* pmo = sPerformanceMonitor->start(PERF_MON_RNDBOT, "PlayerbotFactory_Reset"); bot->resetTalents(true); ClearSkills(); ClearSpells(); if (!incremental) { ClearInventory(); ResetQuests(); } CancelAuras(); bot->SaveToDB(false, false); if (pmo) pmo->finish(); /* pmo = sPerformanceMonitor->start(PERF_MON_RNDBOT, "PlayerbotFactory_Immersive"); LOG_INFO("playerbots", "Initializing immersive..."); InitImmersive(); if (pmo) pmo->finish(); */ if (sPlayerbotAIConfig->randomBotPreQuests) { LOG_INFO("playerbots", "Initializing quests..."); pmo = sPerformanceMonitor->start(PERF_MON_RNDBOT, "PlayerbotFactory_Quests"); InitQuests(classQuestIds); InitQuests(specialQuestIds); // quest rewards boost bot level, so reduce back if (!sPlayerbotAIConfig->disableRandomLevels) { bot->SetLevel(level); } else if (bot->getLevel() < sPlayerbotAIConfig->randombotStartingLevel) { bot->SetLevel(sPlayerbotAIConfig->randombotStartingLevel); } ClearInventory(); bot->SetUInt32Value(PLAYER_XP, 0); CancelAuras(); bot->SaveToDB(false, false); if (pmo) pmo->finish(); } pmo = sPerformanceMonitor->start(PERF_MON_RNDBOT, "PlayerbotFactory_Spells1"); LOG_INFO("playerbots", "Initializing spells (step 1)..."); InitAvailableSpells(); if (pmo) pmo->finish(); LOG_INFO("playerbots", "Initializing skills (step 1)..."); pmo = sPerformanceMonitor->start(PERF_MON_RNDBOT, "PlayerbotFactory_Skills1"); InitSkills(); InitTradeSkills(); if (pmo) pmo->finish(); pmo = sPerformanceMonitor->start(PERF_MON_RNDBOT, "PlayerbotFactory_Talents"); LOG_INFO("playerbots", "Initializing talents..."); //InitTalentsTree(incremental); //sRandomPlayerbotMgr->SetValue(bot->GetGUID().GetCounter(), "specNo", 0); botAI->DoSpecificAction("auto talents"); sPlayerbotDbStore->Reset(botAI); botAI->ResetStrategies(false); // fix wrong stored strategy if (pmo) pmo->finish(); pmo = sPerformanceMonitor->start(PERF_MON_RNDBOT, "PlayerbotFactory_Spells2"); LOG_INFO("playerbots", "Initializing spells (step 2)..."); InitAvailableSpells(); InitSpecialSpells(); if (pmo) pmo->finish(); pmo = sPerformanceMonitor->start(PERF_MON_RNDBOT, "PlayerbotFactory_Mounts"); LOG_INFO("playerbots", "Initializing mounts..."); InitMounts(); if (pmo) pmo->finish(); pmo = sPerformanceMonitor->start(PERF_MON_RNDBOT, "PlayerbotFactory_Skills2"); LOG_INFO("playerbots", "Initializing skills (step 2)..."); UpdateTradeSkills(); bot->SaveToDB(false, false); if (pmo) pmo->finish(); pmo = sPerformanceMonitor->start(PERF_MON_RNDBOT, "PlayerbotFactory_Equip"); LOG_INFO("playerbots", "Initializing equipmemt..."); InitEquipment(incremental); if (pmo) pmo->finish(); if (bot->getLevel() >= sPlayerbotAIConfig->minEnchantingBotLevel) { pmo = sPerformanceMonitor->start(PERF_MON_RNDBOT, "PlayerbotFactory_Enchant"); LOG_INFO("playerbots", "Initializing enchant templates..."); LoadEnchantContainer(); if (pmo) pmo->finish(); } pmo = sPerformanceMonitor->start(PERF_MON_RNDBOT, "PlayerbotFactory_Bags"); LOG_INFO("playerbots", "Initializing bags..."); InitBags(); bot->SaveToDB(false, false); if (pmo) pmo->finish(); pmo = sPerformanceMonitor->start(PERF_MON_RNDBOT, "PlayerbotFactory_Ammo"); LOG_INFO("playerbots", "Initializing ammo..."); InitAmmo(); if (pmo) pmo->finish(); pmo = sPerformanceMonitor->start(PERF_MON_RNDBOT, "PlayerbotFactory_Food"); LOG_INFO("playerbots", "Initializing food..."); InitFood(); if (pmo) pmo->finish(); pmo = sPerformanceMonitor->start(PERF_MON_RNDBOT, "PlayerbotFactory_Potions"); LOG_INFO("playerbots", "Initializing potions..."); InitPotions(); if (pmo) pmo->finish(); pmo = sPerformanceMonitor->start(PERF_MON_RNDBOT, "PlayerbotFactory_Reagents"); LOG_INFO("playerbots", "Initializing reagents..."); InitReagents(); if (pmo) pmo->finish(); pmo = sPerformanceMonitor->start(PERF_MON_RNDBOT, "PlayerbotFactory_EqSets"); LOG_INFO("playerbots", "Initializing second equipment set..."); InitSecondEquipmentSet(); if (pmo) pmo->finish(); if (bot->getLevel() >= sPlayerbotAIConfig->minEnchantingBotLevel) { pmo = sPerformanceMonitor->start(PERF_MON_RNDBOT, "PlayerbotFactory_EnchantTemplate"); LOG_INFO("playerbots", "Initializing enchant templates..."); ApplyEnchantTemplate(); if (pmo) pmo->finish(); } pmo = sPerformanceMonitor->start(PERF_MON_RNDBOT, "PlayerbotFactory_Inventory"); LOG_INFO("playerbots", "Initializing inventory..."); InitInventory(); if (pmo) pmo->finish(); pmo = sPerformanceMonitor->start(PERF_MON_RNDBOT, "PlayerbotFactory_Consumable"); LOG_INFO("playerbots", "Initializing consumables..."); AddConsumables(); if (pmo) pmo->finish(); pmo = sPerformanceMonitor->start(PERF_MON_RNDBOT, "PlayerbotFactory_Guilds"); LOG_INFO("playerbots", "Initializing guilds..."); bot->SaveToDB(false, false); //thesawolf - save save save (hopefully avoids dupes) InitGuild(); if (pmo) pmo->finish(); if (bot->getLevel() >= 70) { pmo = sPerformanceMonitor->start(PERF_MON_RNDBOT, "PlayerbotFactory_Arenas"); LOG_INFO("playerbots", "Initializing arena teams..."); InitArenaTeam(); if (pmo) pmo->finish(); } if (bot->getLevel() >= 10) { pmo = sPerformanceMonitor->start(PERF_MON_RNDBOT, "PlayerbotFactory_Pet"); LOG_INFO("playerbots", "Initializing pet..."); InitPet(); if (pmo) pmo->finish(); } pmo = sPerformanceMonitor->start(PERF_MON_RNDBOT, "PlayerbotFactory_Save"); LOG_INFO("playerbots", "Saving to DB..."); if (incremental) { uint32 money = bot->GetMoney(); bot->SetMoney(money + 1000 * sqrt(urand(1, level * 5))); } else { bot->SetMoney(10000 * sqrt(urand(1, level * 5))); } bot->SaveToDB(false, false); LOG_INFO("playerbots", "Done."); if (pmo) pmo->finish(); } void PlayerbotFactory::Refresh() { Prepare(); InitAmmo(); InitFood(); InitPotions(); InitReagents(); //bot->SaveToDB(); } void PlayerbotFactory::AddConsumables() { switch (bot->getClass()) { case CLASS_PRIEST: case CLASS_MAGE: case CLASS_WARLOCK: { if (level >= 5 && level < 20) { StoreItem(CONSUM_ID_MINOR_WIZARD_OIL, 5); } if (level >= 20 && level < 40) { StoreItem(CONSUM_ID_MINOR_MANA_OIL, 5); StoreItem(CONSUM_ID_MINOR_WIZARD_OIL, 5); } if (level >= 40 && level < 45) { StoreItem(CONSUM_ID_MINOR_MANA_OIL, 5); StoreItem(CONSUM_ID_WIZARD_OIL, 5); } if (level >= 45 && level < 52) { StoreItem(CONSUM_ID_BRILLIANT_MANA_OIL, 5); StoreItem(CONSUM_ID_BRILLIANT_WIZARD_OIL, 5); } if (level >= 52 && level < 58) { StoreItem(CONSUM_ID_SUPERIOR_MANA_OIL, 5); StoreItem(CONSUM_ID_BRILLIANT_WIZARD_OIL, 5); } if (level >= 58) { StoreItem(CONSUM_ID_SUPERIOR_MANA_OIL, 5); StoreItem(CONSUM_ID_SUPERIOR_WIZARD_OIL, 5); } break; } case CLASS_PALADIN: case CLASS_WARRIOR: case CLASS_HUNTER: { if (level >= 1 && level < 5) { StoreItem(CONSUM_ID_ROUGH_SHARPENING_STONE, 5); StoreItem(CONSUM_ID_ROUGH_WEIGHTSTONE, 5); } if (level >= 5 && level < 15) { StoreItem(CONSUM_ID_COARSE_WEIGHTSTONE, 5); StoreItem(CONSUM_ID_COARSE_SHARPENING_STONE, 5); } if (level >= 15 && level < 25) { StoreItem(CONSUM_ID_HEAVY_WEIGHTSTONE, 5); StoreItem(CONSUM_ID_HEAVY_SHARPENING_STONE, 5); } if (level >= 25 && level < 35) { StoreItem(CONSUM_ID_SOL_SHARPENING_STONE, 5); StoreItem(CONSUM_ID_SOLID_WEIGHTSTONE, 5); } if (level >= 35 && level < 50) { StoreItem(CONSUM_ID_DENSE_WEIGHTSTONE, 5); StoreItem(CONSUM_ID_DENSE_SHARPENING_STONE, 5); } if (level >= 50 && level < 60) { StoreItem(CONSUM_ID_FEL_SHARPENING_STONE, 5); StoreItem(CONSUM_ID_FEL_WEIGHTSTONE, 5); } if (level >= 60) { StoreItem(CONSUM_ID_ADAMANTITE_WEIGHTSTONE, 5); StoreItem(CONSUM_ID_ADAMANTITE_SHARPENING_STONE, 5); } break; } case CLASS_ROGUE: { if (level >= 20 && level < 28) { StoreItem(CONSUM_ID_INSTANT_POISON, 5); } if (level >= 28 && level < 30) { StoreItem(CONSUM_ID_INSTANT_POISON_II, 5); } if (level >= 30 && level < 36) { StoreItem(CONSUM_ID_DEADLY_POISON, 5); StoreItem(CONSUM_ID_INSTANT_POISON_II, 5); } if (level >= 36 && level < 44) { StoreItem(CONSUM_ID_DEADLY_POISON_II, 5); StoreItem(CONSUM_ID_INSTANT_POISON_III, 5); } if (level >= 44 && level < 52) { StoreItem(CONSUM_ID_DEADLY_POISON_III, 5); StoreItem(CONSUM_ID_INSTANT_POISON_IV, 5); } if (level >= 52 && level < 60) { StoreItem(CONSUM_ID_DEADLY_POISON_IV, 5); StoreItem(CONSUM_ID_INSTANT_POISON_V, 5); } if (level >= 60 && level < 62) { StoreItem(CONSUM_ID_DEADLY_POISON_V, 5); StoreItem(CONSUM_ID_INSTANT_POISON_VI, 5); } if (level >= 62 && level < 68) { StoreItem(CONSUM_ID_DEADLY_POISON_VI, 5); StoreItem(CONSUM_ID_INSTANT_POISON_VI, 5); } if (level >= 68) { StoreItem(CONSUM_ID_DEADLY_POISON_VII, 5); StoreItem(CONSUM_ID_INSTANT_POISON_VII, 5); } break; } } } void PlayerbotFactory::InitPet() { Pet* pet = bot->GetPet(); if (!pet) { if (bot->getClass() != CLASS_HUNTER) return; Map* map = bot->GetMap(); if (!map) return; std::vector ids; CreatureTemplateContainer const* creatures = sObjectMgr->GetCreatureTemplates(); for (CreatureTemplateContainer::const_iterator itr = creatures->begin(); itr != creatures->end(); ++itr) { if (!itr->second.IsTameable(bot->CanTameExoticPets())) continue; if (itr->second.minlevel > bot->getLevel()) continue; ids.push_back(itr->first); } if (ids.empty()) { LOG_ERROR("playerbots", "No pets available for bot {} ({} level)", bot->GetName().c_str(), bot->getLevel()); return; } for (uint32 i = 0; i < 100; i++) { uint32 index = urand(0, ids.size() - 1); CreatureTemplate const* co = sObjectMgr->GetCreatureTemplate(ids[index]); if (!co) continue; uint32 guid = map->GenerateLowGuid(); uint32 pet_number = sObjectMgr->GeneratePetNumber(); pet = new Pet(bot, HUNTER_PET); if (!pet->Create(guid, bot->GetMap(), bot->GetPhaseMask(), co->Entry, pet_number)) { delete pet; pet = nullptr; continue; } pet->Relocate(bot); pet->SetOwnerGUID(bot->GetGUID()); pet->SetGuidValue(UNIT_FIELD_CREATEDBY, bot->GetGUID()); pet->SetFaction(bot->GetFaction()); pet->SetLevel(bot->getLevel()); pet->InitStatsForLevel(bot->getLevel()); pet->SetPower(POWER_HAPPINESS, HAPPINESS_LEVEL_SIZE * 2); pet->GetCharmInfo()->SetPetNumber(sObjectMgr->GeneratePetNumber(), true); pet->GetMap()->AddToMap(pet->ToCreature()); pet->InitPetCreateSpells(); pet->LearnPetPassives(); pet->CastPetAuras(true); pet->UpdateAllStats(); PetStable& petStable = bot->GetOrInitPetStable(); pet->FillPetInfo(&petStable.CurrentPet.emplace()); LOG_DEBUG("playerbots", "Bot {}: assign pet {} ({} level)", bot->GetName().c_str(), co->Entry, bot->getLevel()); pet->SavePetToDB(PET_SAVE_AS_CURRENT); bot->PetSpellInitialize(); break; } } if (pet) { pet->InitStatsForLevel(bot->getLevel()); pet->SetLevel(bot->getLevel()); pet->SetPower(POWER_HAPPINESS, HAPPINESS_LEVEL_SIZE * 2); pet->SetHealth(pet->GetMaxHealth()); } else { LOG_ERROR("playerbots", "Cannot create pet for bot {}", bot->GetName().c_str()); return; } for (PetSpellMap::const_iterator itr = pet->m_spells.begin(); itr != pet->m_spells.end(); ++itr) { if (itr->second.state == PETSPELL_REMOVED) continue; SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(itr->first); if (!spellInfo) continue; if (spellInfo->IsPassive()) continue; pet->ToggleAutocast(spellInfo, true); } } void PlayerbotFactory::ClearSkills() { for (uint32 i = 0; i < sizeof(tradeSkills) / sizeof(uint32); ++i) { bot->SetSkill(tradeSkills[i], 0, 0, 0); } bot->SetUInt32Value(PLAYER_SKILL_INDEX(0), 0); bot->SetUInt32Value(PLAYER_SKILL_INDEX(1), 0); } void PlayerbotFactory::ClearSpells() { bot->resetSpells(); } void PlayerbotFactory::ResetQuests() { ObjectMgr::QuestMap const& questTemplates = sObjectMgr->GetQuestTemplates(); for (ObjectMgr::QuestMap::const_iterator i = questTemplates.begin(); i != questTemplates.end(); ++i) { Quest const* quest = i->second; uint32 entry = quest->GetQuestId(); // remove all quest entries for 'entry' from quest log for (uint8 slot = 0; slot < MAX_QUEST_LOG_SIZE; ++slot) { uint32 quest = bot->GetQuestSlotQuestId(slot); if (quest == entry) { bot->SetQuestSlot(slot, 0); } } // reset rewarded for restart repeatable quest bot->getQuestStatusMap().erase(entry); //bot->getQuestStatusMap()[entry].m_rewarded = false; //bot->getQuestStatusMap()[entry].m_status = QUEST_STATUS_NONE; } //bot->UpdateForQuestWorldObjects(); CharacterDatabase.Execute("DELETE FROM character_queststatus WHERE guid = {}", bot->GetGUID().GetCounter()); } void PlayerbotFactory::InitSpells() { InitAvailableSpells(); } void PlayerbotFactory::InitTalentsTree(bool incremental) { uint32 specNo = sRandomPlayerbotMgr->GetValue(bot->GetGUID().GetCounter(), "specNo"); if (incremental && specNo) { specNo -= 1; } else { uint32 point = urand(0, 100); uint8 cls = bot->getClass(); uint32 p1 = sPlayerbotAIConfig->specProbability[cls][0]; uint32 p2 = p1 + sPlayerbotAIConfig->specProbability[cls][1]; specNo = (point < p1 ? 0 : (point < p2 ? 1 : 2)); sRandomPlayerbotMgr->SetValue(bot, "specNo", specNo + 1); } InitTalents(specNo); if (bot->GetFreeTalentPoints()) { InitTalents(2 - specNo); } } class DestroyItemsVisitor : public IterateItemsVisitor { public: DestroyItemsVisitor(Player* bot) : IterateItemsVisitor(), bot(bot) { } bool Visit(Item* item) override { uint32 id = item->GetTemplate()->ItemId; if (CanKeep(id)) { keep.insert(id); return true; } bot->DestroyItem(item->GetBagSlot(), item->GetSlot(), true); return true; } private: bool CanKeep(uint32 id) { if (keep.find(id) != keep.end()) return false; if (sPlayerbotAIConfig->IsInRandomQuestItemList(id)) return true; return false; } Player* bot; std::set keep; }; bool PlayerbotFactory::CanEquipArmor(ItemTemplate const* proto) { if (bot->HasSkill(SKILL_SHIELD) && proto->SubClass == ITEM_SUBCLASS_ARMOR_SHIELD) return true; if (bot->HasSkill(SKILL_PLATE_MAIL)) { if (proto->SubClass != ITEM_SUBCLASS_ARMOR_PLATE) return false; } else if (bot->HasSkill(SKILL_MAIL)) { if (proto->SubClass != ITEM_SUBCLASS_ARMOR_MAIL) return false; } else if (bot->HasSkill(SKILL_LEATHER)) { if (proto->SubClass != ITEM_SUBCLASS_ARMOR_LEATHER) return false; } if (proto->Quality <= ITEM_QUALITY_NORMAL) return true; for (uint8 slot = 0; slot < EQUIPMENT_SLOT_END; ++slot) { if (slot == EQUIPMENT_SLOT_TABARD || slot == EQUIPMENT_SLOT_BODY) continue; if (slot == EQUIPMENT_SLOT_OFFHAND && bot->getClass() == CLASS_ROGUE && proto->Class != ITEM_CLASS_WEAPON) continue; if (slot == EQUIPMENT_SLOT_OFFHAND && bot->getClass() == CLASS_PALADIN && proto->SubClass != ITEM_SUBCLASS_ARMOR_SHIELD) continue; } uint8 sp = 0; uint8 ap = 0; uint8 tank = 0; for (uint8 j = 0; j < MAX_ITEM_PROTO_STATS; ++j) { // for ItemStatValue != 0 if(!proto->ItemStat[j].ItemStatValue) continue; AddItemStats(proto->ItemStat[j].ItemStatType, sp, ap, tank); } return CheckItemStats(sp, ap, tank); } bool PlayerbotFactory::CheckItemStats(uint8 sp, uint8 ap, uint8 tank) { switch (bot->getClass()) { case CLASS_PRIEST: case CLASS_MAGE: case CLASS_WARLOCK: if (!sp || ap > sp || tank > sp) return false; break; case CLASS_PALADIN: case CLASS_WARRIOR: if ((!ap && !tank) || sp > ap || sp > tank) return false; break; case CLASS_HUNTER: case CLASS_ROGUE: if (!ap || sp > ap || sp > tank) return false; break; case CLASS_DEATH_KNIGHT: if ((!ap && !tank) || sp > ap || sp > tank) return false; break; } return sp || ap || tank; } void PlayerbotFactory::AddItemStats(uint32 mod, uint8 &sp, uint8 &ap, uint8 &tank) { switch (mod) { case ITEM_MOD_HEALTH: case ITEM_MOD_STAMINA: case ITEM_MOD_MANA: case ITEM_MOD_INTELLECT: case ITEM_MOD_SPIRIT: ++sp; break; } switch (mod) { case ITEM_MOD_AGILITY: case ITEM_MOD_STRENGTH: case ITEM_MOD_HEALTH: case ITEM_MOD_STAMINA: ++tank; break; } switch (mod) { case ITEM_MOD_HEALTH: case ITEM_MOD_STAMINA: case ITEM_MOD_AGILITY: case ITEM_MOD_STRENGTH: ++ap; break; } } bool PlayerbotFactory::CanEquipWeapon(ItemTemplate const* proto) { uint8 tab = AiFactory::GetPlayerSpecTab(bot); switch (bot->getClass()) { case CLASS_PRIEST: if (proto->SubClass != ITEM_SUBCLASS_WEAPON_STAFF && proto->SubClass != ITEM_SUBCLASS_WEAPON_WAND && proto->SubClass != ITEM_SUBCLASS_WEAPON_MACE) return false; break; case CLASS_MAGE: if (proto->SubClass != ITEM_SUBCLASS_WEAPON_STAFF && proto->SubClass != ITEM_SUBCLASS_WEAPON_WAND) return false; break; case CLASS_WARLOCK: if (proto->SubClass != ITEM_SUBCLASS_WEAPON_STAFF && proto->SubClass != ITEM_SUBCLASS_WEAPON_DAGGER && proto->SubClass != ITEM_SUBCLASS_WEAPON_WAND && proto->SubClass != ITEM_SUBCLASS_WEAPON_SWORD) return false; break; case CLASS_WARRIOR: if (tab == 1) //fury { if (proto->SubClass != ITEM_SUBCLASS_WEAPON_MACE && proto->SubClass != ITEM_SUBCLASS_WEAPON_SWORD && proto->SubClass != ITEM_SUBCLASS_WEAPON_AXE && proto->SubClass != ITEM_SUBCLASS_WEAPON_FIST && proto->SubClass != ITEM_SUBCLASS_WEAPON_GUN && proto->SubClass != ITEM_SUBCLASS_WEAPON_CROSSBOW && proto->SubClass != ITEM_SUBCLASS_WEAPON_BOW && proto->SubClass != ITEM_SUBCLASS_WEAPON_THROWN) return false; } if ((tab == 0) && (bot->getLevel() > 10)) // arms { if (proto->SubClass != ITEM_SUBCLASS_WEAPON_MACE2 && proto->SubClass != ITEM_SUBCLASS_WEAPON_SWORD2 && proto->SubClass != ITEM_SUBCLASS_WEAPON_AXE2 && proto->SubClass != ITEM_SUBCLASS_WEAPON_POLEARM && proto->SubClass != ITEM_SUBCLASS_WEAPON_GUN && proto->SubClass != ITEM_SUBCLASS_WEAPON_CROSSBOW && proto->SubClass != ITEM_SUBCLASS_WEAPON_BOW && proto->SubClass != ITEM_SUBCLASS_WEAPON_THROWN) return false; } else // prot + lowlvl { if (proto->SubClass != ITEM_SUBCLASS_WEAPON_MACE && proto->SubClass != ITEM_SUBCLASS_WEAPON_SWORD && proto->SubClass != ITEM_SUBCLASS_WEAPON_AXE && proto->SubClass != ITEM_SUBCLASS_WEAPON_GUN && proto->SubClass != ITEM_SUBCLASS_WEAPON_CROSSBOW && proto->SubClass != ITEM_SUBCLASS_WEAPON_BOW && proto->SubClass != ITEM_SUBCLASS_WEAPON_THROWN) return false; } break; case CLASS_PALADIN: if (proto->SubClass != ITEM_SUBCLASS_WEAPON_MACE2 && proto->SubClass != ITEM_SUBCLASS_WEAPON_SWORD2 && proto->SubClass != ITEM_SUBCLASS_WEAPON_MACE && proto->SubClass != ITEM_SUBCLASS_WEAPON_SWORD) return false; break; case CLASS_SHAMAN: if (tab == 1) //enh { if (proto->SubClass != ITEM_SUBCLASS_WEAPON_MACE && proto->SubClass != ITEM_SUBCLASS_WEAPON_FIST && proto->SubClass != ITEM_SUBCLASS_WEAPON_AXE && proto->SubClass != ITEM_SUBCLASS_WEAPON_AXE2 && proto->SubClass != ITEM_SUBCLASS_WEAPON_MACE2) return false; } else //ele,resto { if (proto->SubClass != ITEM_SUBCLASS_WEAPON_DAGGER && proto->SubClass != ITEM_SUBCLASS_WEAPON_MACE && proto->SubClass != ITEM_SUBCLASS_WEAPON_STAFF) return false; } break; case CLASS_DRUID: if (tab == 1) // feral { if (proto->SubClass != ITEM_SUBCLASS_WEAPON_MACE2 && proto->SubClass != ITEM_SUBCLASS_WEAPON_STAFF) return false; } else // balance, resto { if (proto->SubClass != ITEM_SUBCLASS_WEAPON_MACE && proto->SubClass != ITEM_SUBCLASS_WEAPON_DAGGER && proto->SubClass != ITEM_SUBCLASS_WEAPON_STAFF) return false; } break; case CLASS_HUNTER: if (proto->SubClass != ITEM_SUBCLASS_WEAPON_AXE2 && proto->SubClass != ITEM_SUBCLASS_WEAPON_SWORD2 && proto->SubClass != ITEM_SUBCLASS_WEAPON_POLEARM && proto->SubClass != ITEM_SUBCLASS_WEAPON_STAFF && proto->SubClass != ITEM_SUBCLASS_WEAPON_GUN && proto->SubClass != ITEM_SUBCLASS_WEAPON_CROSSBOW && proto->SubClass != ITEM_SUBCLASS_WEAPON_BOW) return false; break; case CLASS_ROGUE: if (tab == 0) // assa { if (proto->SubClass != ITEM_SUBCLASS_WEAPON_DAGGER && proto->SubClass != ITEM_SUBCLASS_WEAPON_GUN && proto->SubClass != ITEM_SUBCLASS_WEAPON_CROSSBOW && proto->SubClass != ITEM_SUBCLASS_WEAPON_BOW && proto->SubClass != ITEM_SUBCLASS_WEAPON_THROWN) return false; } else { if (proto->SubClass != ITEM_SUBCLASS_WEAPON_DAGGER && proto->SubClass != ITEM_SUBCLASS_WEAPON_FIST && proto->SubClass != ITEM_SUBCLASS_WEAPON_SWORD && proto->SubClass != ITEM_SUBCLASS_WEAPON_MACE && proto->SubClass != ITEM_SUBCLASS_WEAPON_GUN && proto->SubClass != ITEM_SUBCLASS_WEAPON_CROSSBOW && proto->SubClass != ITEM_SUBCLASS_WEAPON_BOW && proto->SubClass != ITEM_SUBCLASS_WEAPON_THROWN) return false; } break; case CLASS_DEATH_KNIGHT: if (proto->SubClass != ITEM_SUBCLASS_WEAPON_MACE2 && proto->SubClass != ITEM_SUBCLASS_WEAPON_POLEARM && proto->SubClass != ITEM_SUBCLASS_WEAPON_SWORD2 && proto->SubClass != ITEM_SUBCLASS_WEAPON_AXE2) return false; break; } return true; } bool PlayerbotFactory::CanEquipItem(ItemTemplate const* proto, uint32 desiredQuality) { if (proto->Duration & 0x80000000) return false; if (proto->Quality != desiredQuality) return false; if (proto->Bonding == BIND_QUEST_ITEM || proto->Bonding == BIND_WHEN_USE) return false; if (proto->Class == ITEM_CLASS_CONTAINER) return true; uint32 requiredLevel = proto->RequiredLevel; if (!requiredLevel) { requiredLevel = sRandomItemMgr->GetMinLevelFromCache(proto->ItemId); } if (!requiredLevel) return false; uint32 level = bot->getLevel(); uint32 delta = 2; if (level < 15) delta = urand(7, 15); else if (proto->Class == ITEM_CLASS_WEAPON || proto->SubClass == ITEM_SUBCLASS_ARMOR_SHIELD) delta = urand(2, 3); else if (!(level % 10) || (level % 10) == 9) delta = 2; else if (level < 40) delta = urand(5, 10); else if (level < 60) delta = urand(3, 7); else if (level < 70) delta = urand(2, 5); else if (level < 80) delta = urand(2, 4); if (desiredQuality > ITEM_QUALITY_NORMAL && (requiredLevel > level || requiredLevel < level - delta)) return false; for (uint32 gap = 60; gap <= 80; gap += 10) { if (level > gap && requiredLevel <= gap) return false; } return true; } void Shuffle(std::vector& items) { uint32 count = items.size(); for (uint32 i = 0; i < count * 5; i++) { int i1 = urand(0, count - 1); int i2 = urand(0, count - 1); uint32 item = items[i1]; items[i1] = items[i2]; items[i2] = item; } } void PlayerbotFactory::InitEquipmentNew(bool incremental) { if (incremental) { DestroyItemsVisitor visitor(bot); IterateItems(&visitor, (IterateItemsMask)(ITERATE_ITEMS_IN_BAGS | ITERATE_ITEMS_IN_BANK)); } else { DestroyItemsVisitor visitor(bot); IterateItems(&visitor, ITERATE_ALL_ITEMS); } std::string const& specName = AiFactory::GetPlayerSpecName(bot); if (specName.empty()) return; // look for upgrades for (uint8 slot = 0; slot < EQUIPMENT_SLOT_END; ++slot) { if (slot == EQUIPMENT_SLOT_TABARD && !bot->GetGuildId()) continue; bool isUpgrade = false; bool found = false; bool noItem = false; uint32 quality = urand(ITEM_QUALITY_UNCOMMON, ITEM_QUALITY_EPIC); uint32 attempts = 10; if (urand(0, 100) < 100 * sPlayerbotAIConfig->randomGearLoweringChance && quality > ITEM_QUALITY_NORMAL) { quality--; } // current item; Item* oldItem = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot); if (oldItem) isUpgrade = true; uint32 itemInSlot = isUpgrade ? oldItem->GetTemplate()->ItemId : 0; uint32 maxLevel = sPlayerbotAIConfig->randomBotMaxLevel; if (maxLevel > sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL)) maxLevel = sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL); uint32 minLevel = sPlayerbotAIConfig->randomBotMinLevel; if (minLevel < sWorld->getIntConfig(CONFIG_START_PLAYER_LEVEL)) minLevel = sWorld->getIntConfig(CONFIG_START_PLAYER_LEVEL); // test do { if (isUpgrade) { std::vector ids = sRandomItemMgr->GetUpgradeList(bot, specName, slot, 0, itemInSlot); if (!ids.empty()) Shuffle(ids); for (uint32 index = 0; index < ids.size(); ++index) { uint32 newItemId = ids[index]; if (incremental && !IsDesiredReplacement(oldItem)) { continue; } uint16 dest; if (!CanEquipUnseenItem(slot, dest, newItemId)) continue; if (oldItem) { bot->RemoveItem(INVENTORY_SLOT_BAG_0, slot, true); oldItem->DestroyForPlayer(bot); } Item* newItem = bot->EquipNewItem(dest, newItemId, true); if (newItem) { newItem->AddToWorld(); newItem->AddToUpdateQueueOf(bot); bot->AutoUnequipOffhandIfNeed(); EnchantItem(newItem); LOG_INFO("playerbots", "Bot {} {}:{} <{}>: Equip: {}, slot: {}, Old item: {}", bot->GetGUID().ToString().c_str(), IsAlliance(bot->getRace()) ? "A" : "H", bot->getLevel(), bot->GetName(), newItemId, slot, itemInSlot); found = true; break; } } } else { std::vector ids = sRandomItemMgr->GetUpgradeList(bot, specName, slot, quality, itemInSlot); if (!ids.empty()) Shuffle(ids); for (uint32 index = 0; index < ids.size(); ++index) { uint32 newItemId = ids[index]; uint16 dest; if (!CanEquipUnseenItem(slot, dest, newItemId)) continue; Item* newItem = bot->EquipNewItem(dest, newItemId, true); if (newItem) { bot->AutoUnequipOffhandIfNeed(); EnchantItem(newItem); found = true; LOG_INFO("playerbots", "Bot {} {}:{} <{}>: Equip: {}, slot: {}", bot->GetGUID().ToString().c_str(), IsAlliance(bot->getRace()) ? "A" : "H", bot->getLevel(), bot->GetName(), newItemId, slot); break; } } } quality--; } while (!found && quality != ITEM_QUALITY_POOR); if (!found) { LOG_INFO("playerbots", "Bot {} {}:{} <{}>: no item for slot {}", bot->GetGUID().ToString().c_str(), IsAlliance(bot->getRace()) ? "A" : "H", bot->getLevel(), bot->GetName(), slot); continue; } } } void PlayerbotFactory::InitEquipment(bool incremental) { //DestroyItemsVisitor visitor(bot); //IterateItems(&visitor, ITERATE_ALL_ITEMS); if (incremental) { //DestroyItemsVisitor visitor(bot); //IterateItems(&visitor, (IterateItemsMask)(ITERATE_ITEMS_IN_BAGS | ITERATE_ITEMS_IN_BANK)); } else { DestroyItemsVisitor visitor(bot); IterateItems(&visitor, ITERATE_ITEMS_IN_EQUIP); } for (uint8 slot = 0; slot < EQUIPMENT_SLOT_END; ++slot) { if (slot == EQUIPMENT_SLOT_TABARD && !bot->GetGuildId()) continue; bool found = false; uint32 quality = itemQuality; if (urand(0, 100) < 100 * sPlayerbotAIConfig->randomGearLoweringChance && quality > ITEM_QUALITY_NORMAL) { quality--; } do { std::vector ids = sRandomItemMgr->Query(level, bot->getClass(), slot, quality); if (!ids.empty()) { Acore::Containers::RandomShuffle(ids); std::random_device rd; } for (uint32 index = 0; index < ids.size(); ++index) { uint32 newItemId = ids[index]; Item* oldItem = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot); if (incremental && !IsDesiredReplacement(oldItem)) { continue; } if (sRandomItemMgr->HasStatWeight(newItemId) || (oldItem && sRandomItemMgr->HasStatWeight(oldItem->GetTemplate()->ItemId))) { if (!sRandomItemMgr->GetLiveStatWeight(bot, newItemId)) continue; if (oldItem) { if (sRandomItemMgr->GetLiveStatWeight(bot, newItemId) < sRandomItemMgr->GetLiveStatWeight(bot, oldItem->GetTemplate()->ItemId)) continue; } } uint16 dest; if (!CanEquipUnseenItem(slot, dest, newItemId)) continue; if (oldItem) { bot->RemoveItem(INVENTORY_SLOT_BAG_0, slot, true); oldItem->DestroyForPlayer(bot); } if (Item* newItem = bot->EquipNewItem(dest, newItemId, true)) { newItem->AddToWorld(); newItem->AddToUpdateQueueOf(bot); bot->AutoUnequipOffhandIfNeed(); EnchantItem(newItem); found = true; break; } } quality--; } while (!found && quality != ITEM_QUALITY_POOR); if (!found) { LOG_INFO("playerbots", "{}: no items to equip for slot {}", bot->GetName().c_str(), slot); continue; } } } bool PlayerbotFactory::IsDesiredReplacement(Item* item) { if (!item) return true; ItemTemplate const* proto = item->GetTemplate(); uint32 requiredLevel = proto->RequiredLevel; if (!requiredLevel) { requiredLevel = sRandomItemMgr->GetMinLevelFromCache(proto->ItemId); } uint32 delta = 1 + (80 - bot->getLevel()) / 10; return int32(bot->getLevel() - requiredLevel) > delta; } inline Item* StoreNewItemInInventorySlot(Player* player, uint32 newItemId, uint32 count) { ItemPosCountVec vDest; InventoryResult msg = player->CanStoreNewItem(INVENTORY_SLOT_BAG_0, NULL_SLOT, vDest, newItemId, count); if (msg == EQUIP_ERR_OK) { if (Item* newItem = player->StoreNewItem(vDest, newItemId, true, Item::GenerateItemRandomPropertyId(newItemId))) { return newItem; } } return nullptr; } void PlayerbotFactory::InitSecondEquipmentSet() { if (bot->getClass() == CLASS_MAGE || bot->getClass() == CLASS_WARLOCK || bot->getClass() == CLASS_PRIEST) return; std::map> items; uint32 desiredQuality = itemQuality; while (urand(0, 100) < 100 * sPlayerbotAIConfig->randomGearLoweringChance && desiredQuality > ITEM_QUALITY_NORMAL) { desiredQuality--; } ItemTemplateContainer const* itemTemplate = sObjectMgr->GetItemTemplateStore(); do { for (auto const& itr : *itemTemplate) { ItemTemplate const* proto = &itr.second; if (!proto) continue; if (!CanEquipItem(proto, desiredQuality)) continue; if (proto->Class == ITEM_CLASS_WEAPON) { //if (!CanEquipWeapon(proto)) // continue; if (sRandomItemMgr->HasStatWeight(proto->ItemId)) { if (!sRandomItemMgr->GetLiveStatWeight(bot, proto->ItemId)) continue; } Item* existingItem = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_MAINHAND); if (existingItem) { switch (existingItem->GetTemplate()->SubClass) { case ITEM_SUBCLASS_WEAPON_AXE: case ITEM_SUBCLASS_WEAPON_DAGGER: case ITEM_SUBCLASS_WEAPON_FIST: case ITEM_SUBCLASS_WEAPON_MACE: case ITEM_SUBCLASS_WEAPON_SWORD: if (proto->SubClass == ITEM_SUBCLASS_WEAPON_AXE || proto->SubClass == ITEM_SUBCLASS_WEAPON_DAGGER || proto->SubClass == ITEM_SUBCLASS_WEAPON_FIST || proto->SubClass == ITEM_SUBCLASS_WEAPON_MACE || proto->SubClass == ITEM_SUBCLASS_WEAPON_SWORD) continue; break; default: if (proto->SubClass != ITEM_SUBCLASS_WEAPON_AXE && proto->SubClass != ITEM_SUBCLASS_WEAPON_DAGGER && proto->SubClass != ITEM_SUBCLASS_WEAPON_FIST && proto->SubClass != ITEM_SUBCLASS_WEAPON_MACE && proto->SubClass != ITEM_SUBCLASS_WEAPON_SWORD) continue; break; } } } else if (proto->Class == ITEM_CLASS_ARMOR && proto->SubClass == ITEM_SUBCLASS_ARMOR_SHIELD) { //if (!CanEquipArmor(proto)) // continue; if (sRandomItemMgr->HasStatWeight(proto->ItemId)) { if (!sRandomItemMgr->GetLiveStatWeight(bot, proto->ItemId)) continue; } if (Item* existingItem = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND)) if (existingItem->GetTemplate()->SubClass == ITEM_SUBCLASS_ARMOR_SHIELD) continue; } else continue; items[proto->Class].push_back(itr.first); } } while (items[ITEM_CLASS_ARMOR].empty() && items[ITEM_CLASS_WEAPON].empty() && desiredQuality-- > ITEM_QUALITY_NORMAL); for (std::map>::iterator i = items.begin(); i != items.end(); ++i) { std::vector& ids = i->second; if (ids.empty()) { LOG_DEBUG("playerbots", "{}: no items to make second equipment set for slot {}", bot->GetName().c_str(), i->first); continue; } for (uint32 attempts = 0; attempts < 15; attempts++) { uint32 index = urand(0, ids.size() - 1); uint32 newItemId = ids[index]; if (Item* newItem = StoreNewItemInInventorySlot(bot, newItemId, 1)) { EnchantItem(newItem); newItem->AddToWorld(); newItem->AddToUpdateQueueOf(bot); break; } } } } void PlayerbotFactory::InitBags() { std::vector ids; ItemTemplateContainer const* itemTemplates = sObjectMgr->GetItemTemplateStore(); for (auto const& itr : *itemTemplates) { ItemTemplate const* proto = &itr.second; if (!proto || proto->Class != ITEM_CLASS_CONTAINER) continue; if (!CanEquipItem(proto, ITEM_QUALITY_NORMAL)) continue; ids.push_back(itr.first); } if (ids.empty()) { LOG_ERROR("playerbots", "{}: no bags found", bot->GetName().c_str()); return; } for (uint8 slot = INVENTORY_SLOT_BAG_START; slot < INVENTORY_SLOT_BAG_END; ++slot) { for (uint32 attempts = 0; attempts < 15; attempts++) { uint32 index = urand(0, ids.size() - 1); uint32 newItemId = ids[index]; uint16 dest; if (!CanEquipUnseenItem(slot, dest, newItemId)) continue; if (Item* newItem = bot->EquipNewItem(dest, newItemId, true)) { newItem->AddToWorld(); newItem->AddToUpdateQueueOf(bot); break; } } } } void PlayerbotFactory::EnchantItem(Item* item) { if (bot->getLevel() < sPlayerbotAIConfig->minEnchantingBotLevel) return; if (urand(0, 100) < 100 * sPlayerbotAIConfig->randomGearLoweringChance) return; ItemTemplate const* proto = item->GetTemplate(); uint32 itemLevel = proto->ItemLevel; std::vector ids; for (uint32 id = 1; id < sSpellMgr->GetSpellInfoStoreSize(); ++id) { SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(id); if (!spellInfo) continue; uint32 requiredLevel = spellInfo->BaseLevel; if (requiredLevel && (requiredLevel > itemLevel || requiredLevel < itemLevel - 35)) continue; if (spellInfo->MaxLevel && level > spellInfo->MaxLevel) continue; uint32 spellLevel = spellInfo->SpellLevel; if (spellLevel && (spellLevel > level || spellLevel < level - 10)) continue; for (uint8 j = 0; j < MAX_SPELL_EFFECTS; ++j) { if (spellInfo->Effects[j].Effect != SPELL_EFFECT_ENCHANT_ITEM) continue; uint32 enchant_id = spellInfo->Effects[j].MiscValue; if (!enchant_id) continue; SpellItemEnchantmentEntry const* enchant = sSpellItemEnchantmentStore.LookupEntry(enchant_id); if (!enchant || enchant->slot != PERM_ENCHANTMENT_SLOT) continue; SpellInfo const* enchantSpell = sSpellMgr->GetSpellInfo(enchant->spellid[0]); if (!enchantSpell || (enchantSpell->SpellLevel && enchantSpell->SpellLevel > level)) continue; uint8 sp = 0; uint8 ap = 0; uint8 tank = 0; for (uint8 i = ITEM_MOD_MANA; i < MAX_ITEM_MOD; ++i) { if (enchant->type[i] != ITEM_ENCHANTMENT_TYPE_STAT) continue; AddItemStats(enchant->spellid[i], sp, ap, tank); } if (!CheckItemStats(sp, ap, tank)) continue; if (enchant->EnchantmentCondition && !bot->EnchantmentFitsRequirements(enchant->EnchantmentCondition, -1)) continue; if (!item->IsFitToSpellRequirements(spellInfo)) continue; ids.push_back(enchant_id); } } if (ids.empty()) { LOG_DEBUG("playerbots", "{}: no enchantments found for item {}", bot->GetName().c_str(), item->GetTemplate()->ItemId); return; } uint32 index = urand(0, ids.size() - 1); uint32 id = ids[index]; SpellItemEnchantmentEntry const* enchant = sSpellItemEnchantmentStore.LookupEntry(id); if (!enchant) return; bot->ApplyEnchantment(item, PERM_ENCHANTMENT_SLOT, false); item->SetEnchantment(PERM_ENCHANTMENT_SLOT, id, 0, 0, bot->GetGUID()); bot->ApplyEnchantment(item, PERM_ENCHANTMENT_SLOT, true); } bool PlayerbotFactory::CanEquipUnseenItem(uint8 slot, uint16 &dest, uint32 item) { dest = 0; if (Item* pItem = Item::CreateItem(item, 1, bot)) { InventoryResult result = bot->CanEquipItem(slot, dest, pItem, true, false); pItem->RemoveFromUpdateQueueOf(bot); delete pItem; return result == EQUIP_ERR_OK; } return false; } void PlayerbotFactory::InitTradeSkills() { uint16 firstSkill = sRandomPlayerbotMgr->GetValue(bot, "firstSkill"); uint16 secondSkill = sRandomPlayerbotMgr->GetValue(bot, "secondSkill"); if (!firstSkill || !secondSkill) { std::vector firstSkills; std::vector secondSkills; switch (bot->getClass()) { case CLASS_WARRIOR: case CLASS_PALADIN: case CLASS_DEATH_KNIGHT: firstSkills.push_back(SKILL_MINING); secondSkills.push_back(SKILL_BLACKSMITHING); secondSkills.push_back(SKILL_ENGINEERING); secondSkills.push_back(SKILL_JEWELCRAFTING); break; case CLASS_SHAMAN: case CLASS_DRUID: case CLASS_HUNTER: case CLASS_ROGUE: firstSkills.push_back(SKILL_SKINNING); secondSkills.push_back(SKILL_LEATHERWORKING); break; default: firstSkills.push_back(SKILL_TAILORING); secondSkills.push_back(SKILL_ENCHANTING); } switch (urand(0, 6)) { case 0: firstSkill = SKILL_HERBALISM; secondSkill = SKILL_ALCHEMY; break; case 1: firstSkill = SKILL_HERBALISM; secondSkill = SKILL_MINING; break; case 2: firstSkill = SKILL_MINING; secondSkill = SKILL_SKINNING; break; case 3: firstSkill = SKILL_HERBALISM; secondSkill = SKILL_SKINNING; break; default: firstSkill = firstSkills[urand(0, firstSkills.size() - 1)]; secondSkill = secondSkills[urand(0, secondSkills.size() - 1)]; break; } sRandomPlayerbotMgr->SetValue(bot, "firstSkill", firstSkill); sRandomPlayerbotMgr->SetValue(bot, "secondSkill", secondSkill); } SetRandomSkill(SKILL_FIRST_AID); SetRandomSkill(SKILL_FISHING); SetRandomSkill(SKILL_COOKING); SetRandomSkill(firstSkill); SetRandomSkill(secondSkill); } void PlayerbotFactory::UpdateTradeSkills() { for (uint32 i = 0; i < sizeof(tradeSkills) / sizeof(uint32); ++i) { if (bot->GetSkillValue(tradeSkills[i]) == 1) bot->SetSkill(tradeSkills[i], 0, 0, 0); } } void PlayerbotFactory::InitSkills() { uint32 maxValue = level * 5; bot->UpdateSkillsForLevel(); uint16 step = bot->GetSkillValue(SKILL_RIDING) ? bot->GetSkillStep(SKILL_RIDING) : 1; if (bot->getLevel() >= 70) bot->SetSkill(SKILL_RIDING, step, 300, 300); else if (bot->getLevel() >= 60) bot->SetSkill(SKILL_RIDING, step, 225, 225); else if (bot->getLevel() >= 40) bot->SetSkill(SKILL_RIDING, step, 150, 150); else if (bot->getLevel() >= 20) bot->SetSkill(SKILL_RIDING, step, 75, 75); else bot->SetSkill(SKILL_RIDING, 0, 0, 0); uint32 skillLevel = bot->getLevel() < 40 ? 0 : 1; switch (bot->getClass()) { case CLASS_DRUID: SetRandomSkill(SKILL_MACES); SetRandomSkill(SKILL_STAVES); SetRandomSkill(SKILL_2H_MACES); SetRandomSkill(SKILL_DAGGERS); SetRandomSkill(SKILL_POLEARMS); SetRandomSkill(SKILL_FIST_WEAPONS); break; case CLASS_WARRIOR: SetRandomSkill(SKILL_SWORDS); SetRandomSkill(SKILL_AXES); SetRandomSkill(SKILL_BOWS); SetRandomSkill(SKILL_GUNS); SetRandomSkill(SKILL_MACES); SetRandomSkill(SKILL_2H_SWORDS); SetRandomSkill(SKILL_STAVES); SetRandomSkill(SKILL_2H_MACES); SetRandomSkill(SKILL_2H_AXES); SetRandomSkill(SKILL_DAGGERS); SetRandomSkill(SKILL_CROSSBOWS); SetRandomSkill(SKILL_POLEARMS); SetRandomSkill(SKILL_FIST_WEAPONS); SetRandomSkill(SKILL_THROWN); break; case CLASS_PALADIN: bot->SetSkill(SKILL_PLATE_MAIL, 0, skillLevel, skillLevel); SetRandomSkill(SKILL_SWORDS); SetRandomSkill(SKILL_AXES); SetRandomSkill(SKILL_MACES); SetRandomSkill(SKILL_2H_SWORDS); SetRandomSkill(SKILL_2H_MACES); SetRandomSkill(SKILL_2H_AXES); SetRandomSkill(SKILL_POLEARMS); break; case CLASS_PRIEST: SetRandomSkill(SKILL_MACES); SetRandomSkill(SKILL_STAVES); SetRandomSkill(SKILL_DAGGERS); SetRandomSkill(SKILL_WANDS); break; case CLASS_SHAMAN: SetRandomSkill(SKILL_AXES); SetRandomSkill(SKILL_MACES); SetRandomSkill(SKILL_STAVES); SetRandomSkill(SKILL_2H_MACES); SetRandomSkill(SKILL_2H_AXES); SetRandomSkill(SKILL_DAGGERS); SetRandomSkill(SKILL_FIST_WEAPONS); break; case CLASS_MAGE: SetRandomSkill(SKILL_SWORDS); SetRandomSkill(SKILL_STAVES); SetRandomSkill(SKILL_DAGGERS); SetRandomSkill(SKILL_WANDS); break; case CLASS_WARLOCK: SetRandomSkill(SKILL_SWORDS); SetRandomSkill(SKILL_STAVES); SetRandomSkill(SKILL_DAGGERS); SetRandomSkill(SKILL_WANDS); break; case CLASS_HUNTER: SetRandomSkill(SKILL_SWORDS); SetRandomSkill(SKILL_AXES); SetRandomSkill(SKILL_BOWS); SetRandomSkill(SKILL_GUNS); SetRandomSkill(SKILL_2H_SWORDS); SetRandomSkill(SKILL_STAVES); SetRandomSkill(SKILL_2H_AXES); SetRandomSkill(SKILL_DAGGERS); SetRandomSkill(SKILL_CROSSBOWS); SetRandomSkill(SKILL_POLEARMS); SetRandomSkill(SKILL_FIST_WEAPONS); SetRandomSkill(SKILL_THROWN); bot->SetSkill(SKILL_MAIL, 0, skillLevel, skillLevel); break; case CLASS_ROGUE: SetRandomSkill(SKILL_SWORDS); SetRandomSkill(SKILL_AXES); SetRandomSkill(SKILL_BOWS); SetRandomSkill(SKILL_GUNS); SetRandomSkill(SKILL_MACES); SetRandomSkill(SKILL_DAGGERS); SetRandomSkill(SKILL_CROSSBOWS); SetRandomSkill(SKILL_FIST_WEAPONS); SetRandomSkill(SKILL_THROWN); break; case CLASS_DEATH_KNIGHT: SetRandomSkill(SKILL_SWORDS); SetRandomSkill(SKILL_AXES); SetRandomSkill(SKILL_MACES); SetRandomSkill(SKILL_2H_SWORDS); SetRandomSkill(SKILL_2H_MACES); SetRandomSkill(SKILL_2H_AXES); SetRandomSkill(SKILL_POLEARMS); break; default: break; } switch (bot->getClass()) { case CLASS_WARRIOR: case CLASS_PALADIN: bot->SetSkill(SKILL_PLATE_MAIL, 1, skillLevel, skillLevel); break; case CLASS_SHAMAN: case CLASS_HUNTER: bot->SetSkill(SKILL_MAIL, 1, skillLevel, skillLevel); break; default: break; } } void PlayerbotFactory::SetRandomSkill(uint16 id) { uint32 maxValue = level * 5; // do not let skill go beyond limit even if maxlevel > blizzlike if (level > 60) { maxValue = (level + 10) * 5; } uint32 value = urand(maxValue - level, maxValue); uint32 curValue = bot->GetSkillValue(id); uint16 step = bot->GetSkillValue(id) ? bot->GetSkillStep(id) : 1; if (!bot->HasSkill(id) || value > curValue) bot->SetSkill(id, step, value, maxValue); } 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::InitSpecialSpells() { for (std::vector::iterator i = sPlayerbotAIConfig->randomBotSpellIds.begin(); i != sPlayerbotAIConfig->randomBotSpellIds.end(); ++i) { uint32 spellId = *i; bot->learnSpell(spellId); } } void PlayerbotFactory::InitTalents(uint32 specNo) { uint32 classMask = bot->getClassMask(); std::map > spells; 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 || talentTabInfo->tabpage != specNo) continue; if ((classMask & talentTabInfo->ClassMask) == 0) continue; spells[talentInfo->Row].push_back(talentInfo); } uint32 freePoints = bot->GetFreeTalentPoints(); for (std::map>::iterator i = spells.begin(); i != spells.end(); ++i) { std::vector& spells = i->second; if (spells.empty()) { LOG_ERROR("playerbots", "{}: No spells for talent row {}", bot->GetName().c_str(), i->first); continue; } uint32 attemptCount = 0; while (!spells.empty() && (int)freePoints - (int)bot->GetFreeTalentPoints() < 5 && attemptCount++ < 3 && bot->GetFreeTalentPoints()) { uint32 index = urand(0, spells.size() - 1); TalentEntry const* talentInfo = spells[index]; for (uint32 rank = 0; rank < MAX_TALENT_RANK && bot->GetFreeTalentPoints(); ++rank) { uint32 spellId = talentInfo->RankID[rank]; if (!spellId) continue; bot->learnSpell(spellId); if (uint32 talentCost = GetTalentSpellCost(spellId)) { uint32 free_points = freePoints - talentCost; bot->SetFreeTalentPoints(free_points > 0 ? free_points : 0); } } spells.erase(spells.begin() + index); } freePoints = bot->GetFreeTalentPoints(); } } ObjectGuid PlayerbotFactory::GetRandomBot() { GuidVector guids; for (std::vector::iterator i = sPlayerbotAIConfig->randomBotAccounts.begin(); i != sPlayerbotAIConfig->randomBotAccounts.end(); i++) { uint32 accountId = *i; if (!AccountMgr::GetCharactersCount(accountId)) continue; CharacterDatabasePreparedStatement * stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARS_BY_ACCOUNT_ID); stmt->SetData(0, accountId); PreparedQueryResult result = CharacterDatabase.Query(stmt); if (!result) continue; do { Field* fields = result->Fetch(); ObjectGuid guid = ObjectGuid::Create(fields[0].Get()); if (!ObjectAccessor::FindPlayer(guid)) guids.push_back(guid); } while (result->NextRow()); } if (guids.empty()) return ObjectGuid::Empty; uint32 index = urand(0, guids.size() - 1); return guids[index]; } void PlayerbotFactory::AddPrevQuests(uint32 questId, std::list& questIds) { Quest const *quest = sObjectMgr->GetQuestTemplate(questId); for (Quest::PrevQuests::const_iterator iter = quest->prevQuests.begin(); iter != quest->prevQuests.end(); ++iter) { uint32 prevId = abs(*iter); AddPrevQuests(prevId, questIds); questIds.remove(prevId); questIds.push_back(prevId); } } void PlayerbotFactory::InitQuests(std::list& questMap) { uint32 count = 0; for (std::list::iterator i = questMap.begin(); i != questMap.end(); ++i) { uint32 questId = *i; Quest const *quest = sObjectMgr->GetQuestTemplate(questId); if (!bot->SatisfyQuestClass(quest, false) || quest->GetMinLevel() > bot->getLevel() || !bot->SatisfyQuestRace(quest, false)) continue; bot->SetQuestStatus(questId, QUEST_STATUS_COMPLETE); bot->RewardQuest(quest, 0, bot, false); LOG_INFO("playerbots", "Bot {} ({} level) rewarded quest {}: {} (MinLevel={}, QuestLevel={})", bot->GetName().c_str(), bot->getLevel(), questId, quest->GetTitle().c_str(), quest->GetMinLevel(), quest->GetQuestLevel()); if (!(count++ % 10)) ClearInventory(); } ClearInventory(); } void PlayerbotFactory::ClearInventory() { DestroyItemsVisitor visitor(bot); IterateItems(&visitor); } void PlayerbotFactory::ClearAllItems() { DestroyItemsVisitor visitor(bot); IterateItems(&visitor, ITERATE_ALL_ITEMS); } void PlayerbotFactory::InitAmmo() { if (bot->getClass() != CLASS_HUNTER && bot->getClass() != CLASS_ROGUE && bot->getClass() != CLASS_WARRIOR) return; Item* const pItem = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_RANGED); if (!pItem) return; uint32 subClass = 0; switch (pItem->GetTemplate()->SubClass) { 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; uint32 entry = bot->GetUInt32Value(PLAYER_AMMO_ID); uint32 count = bot->GetItemCount(entry) / 200; uint32 maxCount = 5 + level / 10; if (!entry || count <= 2) { entry = sRandomItemMgr->GetAmmo(level, subClass); } if (count < maxCount) { for (uint32 i = 0; i < maxCount - count; i++) { if (Item* newItem = StoreNewItemInInventorySlot(bot, entry, 200)) newItem->AddToUpdateQueueOf(bot); } } bot->SetAmmo(entry); } void PlayerbotFactory::InitMounts() { uint32 firstmount = 20; uint32 secondmount = 40; uint32 thirdmount = 60; uint32 fourthmount = 70; if (bot->getLevel() < firstmount) return; std::map>> mounts; std::vector slow, fast, fslow, ffast; switch (bot->getRace()) { case RACE_HUMAN: slow = { 470, 6648, 458, 472 }; fast = { 23228, 23227, 23229 }; break; case RACE_ORC: slow = { 6654, 6653, 580 }; fast = { 23250, 23252, 23251 }; break; case RACE_DWARF: slow = { 6899, 6777, 6898 }; fast = { 23238, 23239, 23240 }; break; case RACE_NIGHTELF: slow = { 10789, 8394, 10793 }; fast = { 18766, 18767, 18902 }; break; case RACE_UNDEAD_PLAYER: slow = { 17463, 17464, 17462 }; fast = { 17465, 23246 }; break; case RACE_TAUREN: slow = { 18990, 18989 }; fast = { 23249, 23248, 23247 }; break; case RACE_GNOME: slow = { 10969, 17453, 10873, 17454 }; fast = { 23225, 23223, 23222 }; break; case RACE_TROLL: slow = { 8588, 10796, 8592, 472 }; fast = { 23241, 23242, 23243 }; break; case RACE_DRAENEI: slow = { 34406, 35711, 35710 }; fast = { 35713, 35712, 35714 }; break; case RACE_BLOODELF: slow = { 33660, 35020, 35022, 35018 }; fast = { 35025, 35025, 35027 }; break; } switch (bot->GetTeamId()) { case TEAM_ALLIANCE: fslow = { 32235, 32239, 32240 }; ffast = { 32242, 32289, 32290, 32292 }; break; case TEAM_HORDE: fslow = { 32244, 32245, 32243 }; ffast = { 32295, 32297, 32246, 32296 }; break; } mounts[bot->getRace()][0] = slow; mounts[bot->getRace()][1] = fast; mounts[bot->getRace()][2] = fslow; mounts[bot->getRace()][3] = ffast; for (uint32 type = 0; type < 4; type++) { if (bot->getLevel() < secondmount && type == 1) continue; if (bot->getLevel() < thirdmount && type == 2) continue; if (bot->getLevel() < fourthmount && type == 3) continue; uint32 index = urand(0, mounts[bot->getRace()][type].size() - 1); uint32 spell = mounts[bot->getRace()][type][index]; if (spell) { bot->learnSpell(spell); LOG_INFO("playerbots", "Bot {} ({}) learned {} mount {}", bot->GetGUID().ToString().c_str(), bot->getLevel(), type == 0 ? "slow" : (type == 1 ? "fast" : "flying"), spell); } } } void PlayerbotFactory::InitPotions() { uint32 effects[] = { SPELL_EFFECT_HEAL, SPELL_EFFECT_ENERGIZE }; for (uint8 i = 0; i < 2; ++i) { uint32 effect = effects[i]; FindPotionVisitor visitor(bot, effect); IterateItems(&visitor); if (!visitor.GetResult().empty()) continue; uint32 itemId = sRandomItemMgr->GetRandomPotion(level, effect); if (!itemId) { LOG_INFO("playerbots", "No potions (type {}) available for bot {} ({} level)", effect, bot->GetName().c_str(), bot->getLevel()); continue; } 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); } } void PlayerbotFactory::InitFood() { uint32 categories[] = { 11, 59 }; for (uint8 i = 0; i < 2; ++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; } 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); } } void PlayerbotFactory::InitReagents() { std::vector items; uint32 regCount = 1; 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 }; 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 }; break; case CLASS_WARLOCK: regCount = 10; if (bot->getLevel() > 9) items = { 6265 }; break; case CLASS_PRIEST: regCount = 3; if (bot->getLevel() > 48) items = { 17028 }; if (bot->getLevel() > 55) items = { 17028, 17029 }; break; case CLASS_ROGUE: regCount = 1; if (bot->getLevel() > 21) items = { 5140 }; if (bot->getLevel() > 33) items = { 5140, 5530 }; 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); } } } } void PlayerbotFactory::CancelAuras() { bot->RemoveAllAuras(); } void PlayerbotFactory::InitInventory() { InitInventoryTrade(); InitInventoryEquip(); InitInventorySkill(); } void PlayerbotFactory::InitInventorySkill() { if (bot->HasSkill(SKILL_MINING)) { StoreItem(2901, 1); // Mining Pick } if (bot->HasSkill(SKILL_BLACKSMITHING) || bot->HasSkill(SKILL_ENGINEERING)) { StoreItem(5956, 1); // Blacksmith Hammer } if (bot->HasSkill(SKILL_ENGINEERING)) { StoreItem(6219, 1); // Arclight Spanner } if (bot->HasSkill(SKILL_ENCHANTING)) { StoreItem(16207, 1); // Runed Arcanite Rod } if (bot->HasSkill(SKILL_SKINNING)) { StoreItem(7005, 1); // Skinning Knife } } Item* PlayerbotFactory::StoreItem(uint32 itemId, uint32 count) { ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId); ItemPosCountVec sDest; InventoryResult msg = bot->CanStoreNewItem(INVENTORY_SLOT_BAG_0, NULL_SLOT, sDest, itemId, count); if (msg != EQUIP_ERR_OK) return nullptr; return bot->StoreNewItem(sDest, itemId, true, Item::GenerateItemRandomPropertyId(itemId)); } void PlayerbotFactory::InitInventoryTrade() { uint32 itemId = sRandomItemMgr->GetRandomTrade(level); if (!itemId) { LOG_ERROR("playerbots", "No trade items available for bot {} ({} level)", bot->GetName().c_str(), bot->getLevel()); return; } ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId); if (!proto) return; uint32 count = 1, stacks = 1; switch (proto->Quality) { case ITEM_QUALITY_NORMAL: count = proto->GetMaxStackSize(); stacks = urand(1, 3); break; case ITEM_QUALITY_UNCOMMON: stacks = 1; int maxStackSize = proto->GetMaxStackSize()/2; uint32 max = std::max(1, maxStackSize); count = urand(1, max); break; } for (uint32 i = 0; i < stacks; i++) StoreItem(itemId, count); } void PlayerbotFactory::InitInventoryEquip() { std::vector ids; uint32 desiredQuality = itemQuality; if (urand(0, 100) < 100 * sPlayerbotAIConfig->randomGearLoweringChance && desiredQuality > ITEM_QUALITY_NORMAL) { desiredQuality--; } ItemTemplateContainer const* itemTemplates = sObjectMgr->GetItemTemplateStore(); for (auto const& itr : *itemTemplates) { ItemTemplate const* proto = &itr.second; if (!proto) continue; if ((proto->Class != ITEM_CLASS_ARMOR && proto->Class != ITEM_CLASS_WEAPON) || (proto->Bonding == BIND_WHEN_PICKED_UP || proto->Bonding == BIND_WHEN_USE)) continue; if (proto->Class == ITEM_CLASS_ARMOR && !CanEquipArmor(proto)) continue; if (proto->Class == ITEM_CLASS_WEAPON && !CanEquipWeapon(proto)) continue; if (!CanEquipItem(proto, desiredQuality)) continue; ids.push_back(itr.first); } uint32 maxCount = urand(0, 3); uint32 count = 0; for (uint32 attempts = 0; attempts < 15; attempts++) { uint32 index = urand(0, ids.size() - 1); if (index >= ids.size()) continue; uint32 itemId = ids[index]; if (StoreItem(itemId, 1) && count++ >= maxCount) break; } } void PlayerbotFactory::InitGuild() { bot->SaveToDB(false, false); // add guild tabard if (bot->GetGuildId() && !bot->HasItemCount(5976, 1)) StoreItem(5976, 1); if (bot->GetGuildId()) return; if (sPlayerbotAIConfig->randomBotGuilds.empty()) RandomPlayerbotFactory::CreateRandomGuilds(); std::vector guilds; for (std::vector::iterator i = sPlayerbotAIConfig->randomBotGuilds.begin(); i != sPlayerbotAIConfig->randomBotGuilds.end(); ++i) guilds.push_back(*i); if (guilds.empty()) { LOG_ERROR("playerbots", "No random guilds available"); return; } int index = urand(0, guilds.size() - 1); uint32 guildId = guilds[index]; Guild* guild = sGuildMgr->GetGuildById(guildId); if (!guild) { LOG_ERROR("playerbots", "Invalid guild {}", guildId); return; } if (guild->GetMemberSize() < urand(10, 15)) guild->AddMember(bot->GetGUID(), urand(GR_OFFICER, GR_INITIATE)); // add guild tabard if (bot->GetGuildId() && bot->getLevel() > 9 && urand(0, 4) && !bot->HasItemCount(5976, 1)) StoreItem(5976, 1); bot->SaveToDB(false, false); } void PlayerbotFactory::InitImmersive() { uint32 owner = bot->GetGUID().GetCounter(); std::map percentMap; bool initialized = false; for (uint32 i = STAT_STRENGTH; i < MAX_STATS; ++i) { Stats type = (Stats)i; std::ostringstream name; name << "immersive_stat_" << i; uint32 value = sRandomPlayerbotMgr->GetValue(owner, name.str()); if (value) initialized = true; percentMap[type] = value; } if (!initialized) { switch (bot->getClass()) { case CLASS_DRUID: case CLASS_SHAMAN: percentMap[STAT_STRENGTH] = 10; percentMap[STAT_INTELLECT] = 10; percentMap[STAT_SPIRIT] = 20; percentMap[STAT_AGILITY] = 30; percentMap[STAT_STAMINA] = 30; break; case CLASS_PALADIN: percentMap[STAT_STRENGTH] = 10; percentMap[STAT_INTELLECT] = 10; percentMap[STAT_SPIRIT] = 20; percentMap[STAT_AGILITY] = 50; percentMap[STAT_STAMINA] = 10; break; case CLASS_WARRIOR: percentMap[STAT_STRENGTH] = 10; percentMap[STAT_SPIRIT] = 20; percentMap[STAT_AGILITY] = 50; percentMap[STAT_STAMINA] = 20; break; case CLASS_ROGUE: case CLASS_HUNTER: percentMap[STAT_SPIRIT] = 40; percentMap[STAT_AGILITY] = 50; percentMap[STAT_STAMINA] = 10; break; case CLASS_MAGE: percentMap[STAT_INTELLECT] = 50; percentMap[STAT_SPIRIT] = 40; percentMap[STAT_STAMINA] = 10; break; case CLASS_PRIEST: percentMap[STAT_INTELLECT] = 50; percentMap[STAT_SPIRIT] = 40; percentMap[STAT_STAMINA] = 10; break; case CLASS_WARLOCK: percentMap[STAT_INTELLECT] = 50; percentMap[STAT_SPIRIT] = 10; percentMap[STAT_STAMINA] = 40; break; } for (uint8 i = 0; i < 5; i++) { Stats from = (Stats)urand(STAT_STRENGTH, MAX_STATS - 1); Stats to = (Stats)urand(STAT_STRENGTH, MAX_STATS - 1); int32 delta = urand(0, 5 + bot->getLevel() / 3); if (from != to && percentMap[to] + delta <= 100 && percentMap[from] - delta >= 0) { percentMap[to] += delta; percentMap[from] -= delta; } } for (uint8 i = STAT_STRENGTH; i < MAX_STATS; ++i) { Stats type = (Stats)i; std::ostringstream name; name << "immersive_stat_" << i; sRandomPlayerbotMgr->SetValue(owner, name.str(), percentMap[type]); } } } void PlayerbotFactory::InitArenaTeam() { if (!sPlayerbotAIConfig->IsInRandomAccountList(bot->GetSession()->GetAccountId())) return; if (sPlayerbotAIConfig->randomBotArenaTeams.empty()) RandomPlayerbotFactory::CreateRandomArenaTeams(); std::vector arenateams; for (std::vector::iterator i = sPlayerbotAIConfig->randomBotArenaTeams.begin(); i != sPlayerbotAIConfig->randomBotArenaTeams.end(); ++i) arenateams.push_back(*i); if (arenateams.empty()) { LOG_ERROR("playerbots", "No random arena team available"); return; } int index = urand(0, arenateams.size() - 1); uint32 arenateamID = arenateams[index]; ArenaTeam* arenateam = sArenaTeamMgr->GetArenaTeamById(arenateamID); if (!arenateam) { LOG_ERROR("playerbots", "Invalid arena team {}", arenateamID); return; } if (arenateam->GetMembersSize() < ((uint32)arenateam->GetType() * 2) && bot->getLevel() >= 70) { ObjectGuid capt = arenateam->GetCaptain(); Player* botcaptain = ObjectAccessor::FindPlayer(capt); if (botcaptain && botcaptain->GetTeamId() == bot->GetTeamId()) //need? { arenateam->AddMember(bot->GetGUID()); arenateam->SaveToDB(); } } bot->SaveToDB(false, false); } void PlayerbotFactory::ApplyEnchantTemplate() { uint8 tab = AiFactory::GetPlayerSpecTab(bot); switch (bot->getClass()) { case CLASS_WARRIOR: if (tab == 2) ApplyEnchantTemplate(12); else if (tab == 1) ApplyEnchantTemplate(11); else ApplyEnchantTemplate(10); break; case CLASS_DRUID: if (tab == 2) ApplyEnchantTemplate(112); else if (tab == 0) ApplyEnchantTemplate(110); else ApplyEnchantTemplate(111); break; case CLASS_SHAMAN: if (tab == 0) ApplyEnchantTemplate(70); else if (tab == 2) ApplyEnchantTemplate(71); else ApplyEnchantTemplate(72); break; case CLASS_PALADIN: if (tab == 0) ApplyEnchantTemplate(20); else if (tab == 2) ApplyEnchantTemplate(22); else if (tab == 1) ApplyEnchantTemplate(21); break; case CLASS_HUNTER: ApplyEnchantTemplate(30); break; case CLASS_ROGUE: ApplyEnchantTemplate(40); break; case CLASS_MAGE: ApplyEnchantTemplate(80); break; case CLASS_WARLOCK: ApplyEnchantTemplate(90); break; case CLASS_PRIEST: ApplyEnchantTemplate(50); break; } } void PlayerbotFactory::ApplyEnchantTemplate(uint8 spec) { for (EnchantContainer::const_iterator itr = GetEnchantContainerBegin(); itr != GetEnchantContainerEnd(); ++itr) if ((*itr).ClassId == bot->getClass() && (*itr).SpecId == spec) botAI->EnchantItemT((*itr).SpellId, (*itr).SlotId); } void PlayerbotFactory::LoadEnchantContainer() { m_EnchantContainer.clear(); PlayerbotsDatabasePreparedStatement* stmt = PlayerbotsDatabase.GetPreparedStatement(PLAYERBOTS_SEL_ENCHANTS); if (PreparedQueryResult result = PlayerbotsDatabase.Query(stmt)) { do { Field* fields = result->Fetch(); EnchantTemplate pEnchant; pEnchant.ClassId = fields[0].Get(); pEnchant.SpecId = fields[1].Get(); pEnchant.SpellId = fields[2].Get(); pEnchant.SlotId = fields[3].Get(); m_EnchantContainer.push_back(std::move(pEnchant)); } while (result->NextRow()); } }