/* * 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 "SuggestWhatToDoAction.h" #include "ChannelMgr.h" #include "Event.h" #include "ItemVisitors.h" #include "AiFactory.h" #include "ChatHelper.h" #include "Playerbots.h" #include "PlayerbotTextMgr.h" std::map SuggestWhatToDoAction::instances; std::map SuggestWhatToDoAction::factions; SuggestWhatToDoAction::SuggestWhatToDoAction(PlayerbotAI* botAI, std::string const name) : InventoryAction(botAI, name) { suggestions.push_back(&SuggestWhatToDoAction::instance); suggestions.push_back(&SuggestWhatToDoAction::specificQuest); suggestions.push_back(&SuggestWhatToDoAction::grindReputation); suggestions.push_back(&SuggestWhatToDoAction::something); } bool SuggestWhatToDoAction::Execute(Event event) { if (!sRandomPlayerbotMgr->IsRandomBot(bot) || bot->GetGroup() || bot->GetInstanceId()) return false; uint32 index = rand() % suggestions.size(); (this->*suggestions[index])(); std::string const qualifier = "suggest what to do"; time_t lastSaid = AI_VALUE2(time_t, "last said", qualifier); botAI->GetAiObjectContext()->GetValue("last said", qualifier)->Set(time(nullptr) + urand(1, 60)); return true; } void SuggestWhatToDoAction::instance() { if (instances.empty()) { instances["Ragefire Chasm"] = 15; instances["Deadmines"] = 18; instances["Wailing Caverns"] = 18; instances["Shadowfang Keep"] = 25; instances["Blackfathom Deeps"] = 20; instances["Stockade"] = 20; instances["Gnomeregan"] = 35; instances["Razorfen Kraul"] = 35; instances["Maraudon"] = 50; instances["Scarlet Monestery"] = 40; instances["Uldaman"] = 45; instances["Dire Maul"] = 58; instances["Scholomance"] = 59; instances["Razorfen Downs"] = 40; instances["Strathholme"] = 59; instances["Zul'Farrak"] = 45; instances["Blackrock Depths"] = 55; instances["Temple of Atal'Hakkar"] = 55; instances["Lower Blackrock Spire"] = 57; instances["Hellfire Citidel"] = 65; instances["Coilfang Reservoir"] = 65; instances["Auchindoun"] = 65; instances["Cavens of Time"] = 68; instances["Tempest Keep"] = 69; instances["Magister's Terrace"] = 70; instances["Utgarde Keep"] = 75; instances["The Nexus"] = 75; instances["Ahn'kahet: The Old Kingdom"] = 75; instances["Azjol-Nerub"] = 75; instances["Drak'Tharon Keep"] = 75; instances["Violet Hold"] = 80; instances["Gundrak"] = 77; instances["Halls of Stone"] = 77; instances["Halls of Lightning"] = 77; instances["Oculus"] = 77; instances["Utgarde Pinnacle"] = 77; instances["Trial of the Champion"] = 80; instances["Forge of Souls"] = 80; instances["Pit of Saron"] = 80; instances["Halls of Reflection"] = 80; } std::vector allowedInstances; for (std::map::iterator i = instances.begin(); i != instances.end(); ++i) { if (bot->getLevel() >= i->second) allowedInstances.push_back(i->first); } if (allowedInstances.empty()) return; std::map placeholders; placeholders["%role"] = chat->FormatClass(bot, AiFactory::GetPlayerSpecTab(bot)); std::ostringstream itemout; itemout << "|c00b000b0" << allowedInstances[urand(0, allowedInstances.size() - 1)] << "|r"; placeholders["%instance"] = itemout.str(); spam(sPlayerbotTextMgr->Format("suggest_instance", placeholders)); } std::vector SuggestWhatToDoAction::GetIncompletedQuests() { std::vector result; for (uint16 slot = 0; slot < MAX_QUEST_LOG_SIZE; ++slot) { uint32 questId = bot->GetQuestSlotQuestId(slot); if (!questId) continue; QuestStatus status = bot->GetQuestStatus(questId); if (status == QUEST_STATUS_INCOMPLETE || status == QUEST_STATUS_NONE) result.push_back(questId); } return std::move(result); } void SuggestWhatToDoAction::specificQuest() { std::vector quests = GetIncompletedQuests(); if (quests.empty()) return; uint32 index = rand() % quests.size(); Quest const* quest = sObjectMgr->GetQuestTemplate(quests[index]); std::map placeholders; placeholders["%role"] = chat->FormatClass(bot, AiFactory::GetPlayerSpecTab(bot)); placeholders["%quest"] = chat->FormatQuest(quest); spam(sPlayerbotTextMgr->Format("suggest_quest", placeholders)); } void SuggestWhatToDoAction::grindReputation() { if (factions.empty()) { factions["Argent Dawn"] = 60; factions["Bloodsail Buccaneers"] = 40; factions["Brood of Nozdormu"] = 60; factions["Cenarion Circle"] = 55; factions["Darkmoon Faire"] = 20; factions["Hydraxian Waterlords"] = 60; factions["Ravenholdt"] = 20; factions["Thorium Brotherhood"] = 40; factions["Timbermaw Hold"] = 50; factions["Wintersaber Trainers"] = 50; factions["Booty Bay"] = 30; factions["Everlook"] = 40; factions["Gadgetzan"] = 50; factions["Ratchet"] = 20; factions["Ashtongue Deathsworn"] = 70; factions["Cenarion Expedition"] = 62; factions["The Consortium"] = 65; factions["Honor Hold"] = 66; factions["Keepers of Time"] = 68; factions["Netherwing"] = 65; factions["Ogri'la"] = 65; factions["The Scale of the Sands"] = 65; factions["Sporeggar"] = 65; factions["Tranquillien"] = 10; factions["The Violet Eye"] = 70; factions["Argent Crusade"] = 75; factions["Ashen Verdict"] = 75; factions["The Kalu'ak"] = 72; factions["Kirin Tor"] = 75; factions["Knights of the Ebon Blade"] = 77; factions["The Sons of Hodir"] = 78; factions["The Wyrmrest Accord"] = 77; } std::vector levels; levels.push_back("honored"); levels.push_back("revered"); levels.push_back("exalted"); std::vector allowedFactions; for (std::map::iterator i = factions.begin(); i != factions.end(); ++i) { if (bot->getLevel() >= i->second) allowedFactions.push_back(i->first); } if (allowedFactions.empty()) return; std::map placeholders; placeholders["%role"] = chat->FormatClass(bot, AiFactory::GetPlayerSpecTab(bot)); placeholders["%level"] = levels[urand(0, 2)]; std::ostringstream rnd; rnd << urand(1, 5) << "K"; placeholders["%rndK"] = rnd.str(); std::ostringstream itemout; itemout << "|c004040b0" << allowedFactions[urand(0, allowedFactions.size() - 1)] << "|r"; placeholders["{}action"] = itemout.str(); spam(sPlayerbotTextMgr->Format("suggest_faction", placeholders)); } void SuggestWhatToDoAction::something() { std::map placeholders; placeholders["%role"] = chat->FormatClass(bot, AiFactory::GetPlayerSpecTab(bot)); AreaTableEntry const* entry = sAreaTableStore.LookupEntry(bot->GetAreaId()); if (!entry) return; std::ostringstream out; out << "|cffb04040" << entry->area_name[0] << "|r"; placeholders["%zone"] = out.str(); spam(sPlayerbotTextMgr->Format("suggest_something", placeholders)); } void SuggestWhatToDoAction::spam(std::string const msg, uint32 channelId) { std::set said; for (uint32 i = 0; i < sChatChannelsStore.GetNumRows(); ++i) { ChatChannelsEntry const* channel = sChatChannelsStore.LookupEntry(i); if (!channel || channel->ChannelID != channelId) continue; for (AreaTableEntry const* area : sAreaTableStore) { char channelName[255]; snprintf(channelName, 255, channel->pattern[0], area->area_name[0]); if (said.find(channelName) != said.end()) continue; said.insert(channelName); if (ChannelMgr* cMgr = ChannelMgr::forTeam(bot->GetTeamId())) { if (Channel* chn = cMgr->GetJoinChannel(channelName, channel->ChannelID)) { chn->JoinChannel(bot, ""); chn->Say(bot->GetGUID(), msg.c_str(), LANG_UNIVERSAL); } } } } } class FindTradeItemsVisitor : public IterateItemsVisitor { public: FindTradeItemsVisitor(uint32 quality) : quality(quality), IterateItemsVisitor() { } bool Visit(Item* item) override { ItemTemplate const* proto = item->GetTemplate(); if (proto->Quality != quality) return true; if (proto->Class == ITEM_CLASS_TRADE_GOODS && proto->Bonding == NO_BIND) { if (proto->Quality == ITEM_QUALITY_NORMAL && item->GetCount() > 1 && item->GetCount() == item->GetMaxStackCount()) stacks.push_back(proto->ItemId); items.push_back(proto->ItemId); count[proto->ItemId] += item->GetCount(); } return true; } std::map count; std::vector stacks; std::vector items; private: uint32 quality; }; SuggestTradeAction::SuggestTradeAction(PlayerbotAI* botAI) : SuggestWhatToDoAction(botAI, "suggest trade") { } bool SuggestTradeAction::Execute(Event event) { if (!sRandomPlayerbotMgr->IsRandomBot(bot) || bot->GetGroup() || bot->GetInstanceId()) return false; uint32 quality = urand(0, 100); if (quality > 95) quality = ITEM_QUALITY_LEGENDARY; else if (quality > 90) quality = ITEM_QUALITY_EPIC; else if (quality > 75) quality = ITEM_QUALITY_RARE; else if (quality > 50) quality = ITEM_QUALITY_UNCOMMON; else quality = ITEM_QUALITY_NORMAL; uint32 item = 0, count = 0; while (quality-- > ITEM_QUALITY_POOR) { FindTradeItemsVisitor visitor(quality); IterateItems(&visitor); if (!visitor.stacks.empty()) { uint32 index = urand(0, visitor.stacks.size() - 1); item = visitor.stacks[index]; } if (!item) { if (!visitor.items.empty()) { uint32 index = urand(0, visitor.items.size() - 1); item = visitor.items[index]; } } if (item) { count = visitor.count[item]; break; } } if (!item || !count) return false; ItemTemplate const* proto = sObjectMgr->GetItemTemplate(item); if (!proto) return false; uint32 price = proto->SellPrice * sRandomPlayerbotMgr->GetSellMultiplier(bot) * count; if (!price) return false; std::map placeholders; placeholders["%item"] = chat->FormatItem(proto, count); placeholders["%gold"] = chat->formatMoney(price); spam(sPlayerbotTextMgr->Format("suggest_sell", placeholders)); return true; } bool SuggestWhatToDoAction::isUseful() { std::string const qualifier = "suggest what to do"; time_t lastSaid = AI_VALUE2(time_t, "last said", qualifier); return (time(nullptr) - lastSaid) > 30; }