mirror of
https://git.vectorsigma.ru/public/mod-playerbots.git
synced 2026-07-29 04:48:45 +00:00
PVP Talents and InitGlyph changes
This PR adds 3 pvp specs for each class, as well as their glyphs. It also adds exceptions to the Initglyph function, based on pvp-based talents for each class.
conf\playerbots.conf.dist - Adds 3 pvp specs/glyphs for each class.
src\factory\PlayerbotFactory.cpp - InitGlyph in its current form is unable to correctly assign glyphs on specindexes (or tab) over 2 without an exception. That is why this exception already exists in the code:
if (bot->getClass() == CLASS_DRUID && tab == DRUID_TAB_FERAL && bot->GetLevel() >= 20 && !bot->HasAura(16931))
tab = 3;
This checks if the class is a Druid, if the tab is feral, if they are equal to or above level 20, and they don't have the Thick Hide talent. If all of these are true, then it manually sets the tab = 3. I first discovered this when I noticed that my frostfire mage would never be assigned the correct glyphs in the config - aka glyph of frosfire. It is because the frostfire spec is tab = 3, and no such logic exists. When I started adding the additional pvp specs, I noticed that they never would assign the correct glyphs. I had to add an exception to all pvp specs, and have them check for certain pvp related talents to correlate the tab manually. This is because tab is derived from the AiFactory::GetPlayerSpecTab(bot); function. The only possible tab values from this function are 0, 1, and 2.
**TLDR: Added code to support Frostfire Mage, dual-aura Blood DK, and all the PvP specs for correct glyph assignment.**
src\strategy\actions\ChangeTalentsAction.cpp: When you pick a spec with "talents spec" function, such as "talents spec arms pve", it will now correctly assign glyphs without the player having to execute the maintenance command. Setting the InitGlyphs to false removes prior glyphs.
src\strategy\actions\TrainerAction.cpp - Changed factory.InitGlyphs(true); to factory.InitGlyphs(false);. This makes it so all prior glyphs that were assigned are correctly deleted. I first noticed this when switching between specs and using the maintenance command - I had to login to the bot and manually delete the old glyphs, in order for the maintenance command to assign the new, correct glyphs.
379 lines
12 KiB
C++
379 lines
12 KiB
C++
/*
|
|
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
|
|
* and/or modify it under version 2 of the License, or (at your option), any later version.
|
|
*/
|
|
|
|
#include "ChangeTalentsAction.h"
|
|
|
|
#include "AiFactory.h"
|
|
#include "ChatHelper.h"
|
|
#include "Event.h"
|
|
#include "PlayerbotAIConfig.h"
|
|
#include "PlayerbotFactory.h"
|
|
#include "Playerbots.h"
|
|
|
|
bool ChangeTalentsAction::Execute(Event event)
|
|
{
|
|
std::string param = event.getParam();
|
|
|
|
std::ostringstream out;
|
|
|
|
if (!param.empty())
|
|
{
|
|
if (param.find("help") != std::string::npos)
|
|
{
|
|
out << TalentsHelp();
|
|
}
|
|
else if (param.find("switch") != std::string::npos)
|
|
{
|
|
if (param.find("switch 1") != std::string::npos)
|
|
{
|
|
bot->ActivateSpec(0);
|
|
out << "Active first talent";
|
|
botAI->ResetStrategies();
|
|
}
|
|
else if (param.find("switch 2") != std::string::npos)
|
|
{
|
|
if (bot->GetSpecsCount() == 1 && bot->GetLevel() >= sWorld->getIntConfig(CONFIG_MIN_DUALSPEC_LEVEL))
|
|
{
|
|
bot->CastSpell(bot, 63680, true, nullptr, nullptr, bot->GetGUID());
|
|
bot->CastSpell(bot, 63624, true, nullptr, nullptr, bot->GetGUID());
|
|
}
|
|
bot->ActivateSpec(1);
|
|
out << "Active second talent";
|
|
botAI->ResetStrategies();
|
|
}
|
|
}
|
|
else if (param.find("autopick") != std::string::npos)
|
|
{
|
|
PlayerbotFactory factory(bot, bot->GetLevel());
|
|
factory.InitTalentsTree(true);
|
|
out << "Auto pick talents";
|
|
botAI->ResetStrategies();
|
|
}
|
|
else if (param.find("spec list") != std::string::npos)
|
|
{
|
|
out << SpecList();
|
|
}
|
|
else if (param.find("spec ") != std::string::npos)
|
|
{
|
|
param = param.substr(5);
|
|
out << SpecPick(param);
|
|
botAI->ResetStrategies();
|
|
}
|
|
else if (param.find("apply ") != std::string::npos)
|
|
{
|
|
param = param.substr(6);
|
|
out << SpecApply(param);
|
|
botAI->ResetStrategies();
|
|
}
|
|
else
|
|
{
|
|
out << "Unknown command.";
|
|
}
|
|
}
|
|
else
|
|
{
|
|
uint32 tab = AiFactory::GetPlayerSpecTab(bot);
|
|
out << "My current talent spec is: "
|
|
<< "|h|cffffffff";
|
|
out << chat->FormatClass(bot, tab) << "\n";
|
|
out << TalentsHelp();
|
|
}
|
|
|
|
botAI->TellMaster(out);
|
|
|
|
return true;
|
|
}
|
|
|
|
std::string ChangeTalentsAction::TalentsHelp()
|
|
{
|
|
std::ostringstream out;
|
|
out << "Talents usage: talents switch <1/2>, talents autopick, talents spec list, "
|
|
"talents spec <specName>, talents apply <link>.";
|
|
return out.str();
|
|
}
|
|
|
|
std::string ChangeTalentsAction::SpecList()
|
|
{
|
|
int cls = bot->getClass();
|
|
int specFound = 0;
|
|
std::ostringstream out;
|
|
for (int specNo = 0; specNo < MAX_SPECNO; ++specNo)
|
|
{
|
|
if (sPlayerbotAIConfig->premadeSpecName[cls][specNo].size() == 0)
|
|
{
|
|
break;
|
|
}
|
|
specFound++;
|
|
std::ostringstream out;
|
|
std::vector<std::vector<uint32>> parsed = sPlayerbotAIConfig->parsedSpecLinkOrder[cls][specNo][80];
|
|
std::unordered_map<int, int> tabCount;
|
|
tabCount[0] = tabCount[1] = tabCount[2] = 0;
|
|
for (auto& item : parsed)
|
|
{
|
|
tabCount[item[0]] += item[3];
|
|
}
|
|
out << specFound << ". " << sPlayerbotAIConfig->premadeSpecName[cls][specNo] << " (";
|
|
out << tabCount[0] << "-" << tabCount[1] << "-" << tabCount[2] << ")";
|
|
botAI->TellMasterNoFacing(out.str());
|
|
}
|
|
out << "Total " << specFound << " specs found";
|
|
return out.str();
|
|
}
|
|
|
|
std::string ChangeTalentsAction::SpecPick(std::string param)
|
|
{
|
|
int cls = bot->getClass();
|
|
// int specFound = 0; //not used, line marked for removal.
|
|
for (int specNo = 0; specNo < MAX_SPECNO; ++specNo)
|
|
{
|
|
if (sPlayerbotAIConfig->premadeSpecName[cls][specNo].size() == 0)
|
|
{
|
|
break;
|
|
}
|
|
if (sPlayerbotAIConfig->premadeSpecName[cls][specNo] == param)
|
|
{
|
|
PlayerbotFactory::InitTalentsBySpecNo(bot, specNo, true);
|
|
|
|
PlayerbotFactory factory(bot, bot->GetLevel());
|
|
factory.InitGlyphs(false);
|
|
|
|
std::ostringstream out;
|
|
out << "Picking " << sPlayerbotAIConfig->premadeSpecName[cls][specNo];
|
|
return out.str();
|
|
}
|
|
}
|
|
std::ostringstream out;
|
|
out << "Spec " << param << " not found";
|
|
return out.str();
|
|
}
|
|
|
|
std::string ChangeTalentsAction::SpecApply(std::string param)
|
|
{
|
|
int cls = bot->getClass();
|
|
std::ostringstream out;
|
|
std::vector<std::vector<uint32>> parsedSpecLink = PlayerbotAIConfig::ParseTempTalentsOrder(cls, param);
|
|
if (parsedSpecLink.size() == 0)
|
|
{
|
|
out << "Invalid link " << param;
|
|
return out.str();
|
|
}
|
|
PlayerbotFactory::InitTalentsByParsedSpecLink(bot, parsedSpecLink, true);
|
|
out << "Applying " << param;
|
|
return out.str();
|
|
}
|
|
|
|
// std::vector<TalentPath*> ChangeTalentsAction::getPremadePaths(std::string const findName)
|
|
// {
|
|
// std::vector<TalentPath*> ret;
|
|
// // for (auto& path : sPlayerbotAIConfig->classSpecs[bot->getClass()].talentPath)
|
|
// // {
|
|
// // if (findName.empty() || path.name.find(findName) != std::string::npos)
|
|
// // {
|
|
// // ret.push_back(&path);
|
|
// // }
|
|
// // }
|
|
|
|
// return std::move(ret);
|
|
// }
|
|
|
|
// std::vector<TalentPath*> ChangeTalentsAction::getPremadePaths(TalentSpec* oldSpec)
|
|
// {
|
|
// std::vector<TalentPath*> ret;
|
|
|
|
// // for (auto& path : sPlayerbotAIConfig->classSpecs[bot->getClass()].talentPath)
|
|
// // {
|
|
// // TalentSpec newSpec = *GetBestPremadeSpec(path.id);
|
|
// // newSpec.CropTalents(bot->GetLevel());
|
|
// // if (oldSpec->isEarlierVersionOf(newSpec))
|
|
// // {
|
|
// // ret.push_back(&path);
|
|
// // }
|
|
// // }
|
|
|
|
// return std::move(ret);
|
|
// }
|
|
|
|
// TalentPath* ChangeTalentsAction::getPremadePath(uint32 id)
|
|
// {
|
|
// // for (auto& path : sPlayerbotAIConfig->classSpecs[bot->getClass()].talentPath)
|
|
// // {
|
|
// // if (id == path.id)
|
|
// // {
|
|
// // return &path;
|
|
// // }
|
|
// // }
|
|
|
|
// // return &sPlayerbotAIConfig->classSpecs[bot->getClass()].talentPath[0];
|
|
// return nullptr;
|
|
// }
|
|
|
|
// void ChangeTalentsAction::listPremadePaths(std::vector<TalentPath*> paths, std::ostringstream* out)
|
|
// {
|
|
// if (paths.size() == 0)
|
|
// {
|
|
// *out << "No predefined talents found..";
|
|
// }
|
|
|
|
// *out << "|h|cffffffff";
|
|
|
|
// for (auto path : paths)
|
|
// {
|
|
// *out << path->name << " (" << path->talentSpec.back().FormatSpec(bot) << "), ";
|
|
// }
|
|
|
|
// out->seekp(-2, out->cur);
|
|
// *out << ".";
|
|
// }
|
|
|
|
// TalentPath* ChangeTalentsAction::PickPremadePath(std::vector<TalentPath*> paths, bool useProbability)
|
|
// {
|
|
// uint32 totProbability = 0;
|
|
// uint32 curProbability = 0;
|
|
|
|
// if (paths.size() == 1)
|
|
// return paths[0];
|
|
|
|
// for (auto path : paths)
|
|
// {
|
|
// totProbability += useProbability ? path->probability : 1;
|
|
// }
|
|
|
|
// totProbability = urand(0, totProbability);
|
|
|
|
// for (auto path : paths)
|
|
// {
|
|
// curProbability += (useProbability ? path->probability : 1);
|
|
// if (curProbability >= totProbability)
|
|
// return path;
|
|
// }
|
|
|
|
// return paths[0];
|
|
// }
|
|
|
|
// bool ChangeTalentsAction::AutoSelectTalents(std::ostringstream* out)
|
|
// {
|
|
// // Does the bot have talentpoints?
|
|
// if (bot->GetLevel() < 10)
|
|
// {
|
|
// *out << "No free talent points.";
|
|
// return false;
|
|
// }
|
|
|
|
// uint32 specNo = sRandomPlayerbotMgr->GetValue(bot->GetGUID().GetCounter(), "specNo");
|
|
// uint32 specId = specNo - 1;
|
|
// std::string specLink = sRandomPlayerbotMgr->GetData(bot->GetGUID().GetCounter(), "specLink");
|
|
|
|
// //Continue the current spec
|
|
// if (specNo > 0)
|
|
// {
|
|
// TalentSpec newSpec = *GetBestPremadeSpec(specId);
|
|
// newSpec.CropTalents(bot->GetLevel());
|
|
// newSpec.ApplyTalents(bot, out);
|
|
// if (newSpec.GetTalentPoints() > 0)
|
|
// {
|
|
// *out << "Upgrading spec " << "|h|cffffffff" << getPremadePath(specId)->name << "" <<
|
|
// newSpec.FormatSpec(bot);
|
|
// }
|
|
// }
|
|
// else if (!specLink.empty())
|
|
// {
|
|
// TalentSpec newSpec(bot, specLink);
|
|
// newSpec.CropTalents(bot->GetLevel());
|
|
// newSpec.ApplyTalents(bot, out);
|
|
// if (newSpec.GetTalentPoints() > 0)
|
|
// {
|
|
// *out << "Upgrading saved spec "
|
|
// << "|h|cffffffff" << chat->FormatClass(bot, newSpec.highestTree()) << " (" <<
|
|
// newSpec.FormatSpec(bot) << ")";
|
|
// }
|
|
// }
|
|
|
|
// //Spec was not found or not sufficient
|
|
// if (bot->GetFreeTalentPoints() > 0 || (!specNo && specLink.empty()))
|
|
// {
|
|
// TalentSpec oldSpec(bot);
|
|
// std::vector<TalentPath*> paths = getPremadePaths(&oldSpec);
|
|
|
|
// if (paths.size() == 0) //No spec like the old one found. Pick any.
|
|
// {
|
|
// if (bot->CalculateTalentsPoints() > 0)
|
|
// *out << "No specs like the current spec found. ";
|
|
|
|
// paths = getPremadePaths("");
|
|
// }
|
|
|
|
// if (paths.size() == 0)
|
|
// {
|
|
// *out << "No predefined talents found for this class.";
|
|
// specId = -1;
|
|
// // specLink = "";
|
|
// }
|
|
// else if (paths.size() > 1 && false/*!sPlayerbotAIConfig->autoPickTalents*/ &&
|
|
// !sRandomPlayerbotMgr->IsRandomBot(bot))
|
|
// {
|
|
// *out << "Found multiple specs: ";
|
|
// listPremadePaths(paths, out);
|
|
// }
|
|
// else
|
|
// {
|
|
// specId = PickPremadePath(paths, sRandomPlayerbotMgr->IsRandomBot(bot))->id;
|
|
// TalentSpec newSpec = *GetBestPremadeSpec(specId);
|
|
// specLink = newSpec.GetTalentLink();
|
|
// newSpec.CropTalents(bot->GetLevel());
|
|
// newSpec.ApplyTalents(bot, out);
|
|
|
|
// if (paths.size() > 1)
|
|
// *out << "Found " << paths.size() << " possible specs to choose from. ";
|
|
|
|
// *out << "Apply spec " << "|h|cffffffff" << getPremadePath(specId)->name << " " <<
|
|
// newSpec.FormatSpec(bot);
|
|
// }
|
|
// }
|
|
|
|
// sRandomPlayerbotMgr->SetValue(bot->GetGUID().GetCounter(), "specNo", specId + 1);
|
|
|
|
// if (!specLink.empty() && specId == -1)
|
|
// sRandomPlayerbotMgr->SetValue(bot->GetGUID().GetCounter(), "specLink", 1, specLink);
|
|
// else
|
|
// sRandomPlayerbotMgr->SetValue(bot->GetGUID().GetCounter(), "specLink", 0);
|
|
|
|
// return (specNo == 0) ? false : true;
|
|
// }
|
|
|
|
// //Returns a pre-made talentspec that best suits the bots current talents.
|
|
// TalentSpec* ChangeTalentsAction::GetBestPremadeSpec(uint32 specId)
|
|
// {
|
|
// TalentPath* path = getPremadePath(specId);
|
|
// for (auto& spec : path->talentSpec)
|
|
// {
|
|
// if (spec.points >= bot->CalculateTalentsPoints())
|
|
// return &spec;
|
|
// }
|
|
|
|
// if (path->talentSpec.size())
|
|
// return &path->talentSpec.back();
|
|
|
|
// // return &sPlayerbotAIConfig->classSpecs[bot->getClassMask()].baseSpec;
|
|
// return nullptr;
|
|
// }
|
|
|
|
bool AutoSetTalentsAction::Execute(Event event)
|
|
{
|
|
std::ostringstream out;
|
|
|
|
if (!sPlayerbotAIConfig->autoPickTalents || !sRandomPlayerbotMgr->IsRandomBot(bot))
|
|
return false;
|
|
|
|
if (bot->GetFreeTalentPoints() <= 0)
|
|
return false;
|
|
|
|
PlayerbotFactory factory(bot, bot->GetLevel());
|
|
factory.InitTalentsTree(true, true, true);
|
|
factory.InitPetTalents();
|
|
botAI->TellMaster(out);
|
|
|
|
return true;
|
|
}
|