mirror of
https://github.com/mod-playerbots/mod-playerbots
synced 2025-11-29 15:58:20 +08:00
@@ -216,6 +216,9 @@ AiPlayerbot.AutoEquipUpgradeLoot = 1
|
||||
# Default: 1.1 (Equip when the equipment score is 1.1 times higher than the current)
|
||||
AiPlayerbot.EquipUpgradeThreshold = 1.1
|
||||
|
||||
# Two rounds of equipment initialization to create more suitable gear
|
||||
AiPlayerbot.TwoRoundsGearInit = 0
|
||||
|
||||
#
|
||||
#
|
||||
#
|
||||
@@ -821,13 +824,25 @@ AiPlayerbot.PremadeSpecGlyph.3.0 = 42912,43350,42902,43351,43338,45732
|
||||
AiPlayerbot.PremadeSpecLink.3.0.60 = 51200201505112243100511351
|
||||
AiPlayerbot.PremadeSpecLink.3.0.80 = 51200201505112253100531351-015305021
|
||||
AiPlayerbot.PremadeSpecName.3.1 = mm pve
|
||||
AiPlayerbot.PremadeSpecGlyph.3.1 = 42912,43350,42915,43351,43338,45732
|
||||
AiPlayerbot.PremadeSpecLink.3.1.60 = -015305101230013233135030051
|
||||
AiPlayerbot.PremadeSpecGlyph.3.1 = 42912,43350,42914,43351,43338,45732
|
||||
AiPlayerbot.PremadeSpecLink.3.1.60 = -015305101230013233125031051
|
||||
AiPlayerbot.PremadeSpecLink.3.1.80 = 502-035305101230013233135031351-5000002
|
||||
AiPlayerbot.PremadeSpecName.3.2 = surv pve
|
||||
AiPlayerbot.PremadeSpecGlyph.3.2 = 42912,43350,45731,43351,43338,45732
|
||||
AiPlayerbot.PremadeSpecLink.3.2.60 = --5000032500033330502135001331
|
||||
AiPlayerbot.PremadeSpecLink.3.2.80 = -005305101-5000032500033330522135301331
|
||||
AiPlayerbot.PremadeSpecLink.3.2.60 = --5000032500033330502135201311
|
||||
AiPlayerbot.PremadeSpecLink.3.2.80 = -005305101-5000032500033330532135301321
|
||||
|
||||
# HUNTER PET
|
||||
#
|
||||
# Ferocity
|
||||
AiPlayerbot.PremadeHunterPetLink.0.16 = 2100003030103010101
|
||||
AiPlayerbot.PremadeHunterPetLink.0.20 = 2100013030103010122
|
||||
# Tenacity
|
||||
AiPlayerbot.PremadeHunterPetLink.1.16 = 21103000300120101001
|
||||
AiPlayerbot.PremadeHunterPetLink.1.20 = 21303010300120101002
|
||||
# Cunning
|
||||
AiPlayerbot.PremadeHunterPetLink.2.16 = 2100020330000211001
|
||||
AiPlayerbot.PremadeHunterPetLink.2.20 = 21000203300002110221
|
||||
|
||||
#
|
||||
#
|
||||
@@ -965,7 +980,7 @@ AiPlayerbot.PremadeSpecLink.9.0.70 = 2350022001113510053500131151--55
|
||||
AiPlayerbot.PremadeSpecLink.9.0.80 = 2350022001113510253500331151--5500000501
|
||||
AiPlayerbot.PremadeSpecName.9.1 = emo pve
|
||||
AiPlayerbot.PremadeSpecGlyph.9.1 = 45785,43390,50077,43394,43393,42459
|
||||
AiPlayerbot.PremadeSpecLink.9.1.60 = -003203301135112530131201-55
|
||||
AiPlayerbot.PremadeSpecLink.9.1.60 = -003203301135112530135201051
|
||||
AiPlayerbot.PremadeSpecLink.9.1.70 = -003203301135112530135201051-55
|
||||
AiPlayerbot.PremadeSpecLink.9.1.80 = -003203301135112530135221351-55000005
|
||||
AiPlayerbot.PremadeSpecName.9.2 = destro pve
|
||||
@@ -1165,11 +1180,11 @@ AiPlayerbot.RandomClassSpecIndex.8.2 = 2
|
||||
#
|
||||
#
|
||||
|
||||
AiPlayerbot.RandomClassSpecProb.9.0 = 40
|
||||
AiPlayerbot.RandomClassSpecProb.9.0 = 45
|
||||
AiPlayerbot.RandomClassSpecIndex.9.0 = 0
|
||||
AiPlayerbot.RandomClassSpecProb.9.1 = 40
|
||||
AiPlayerbot.RandomClassSpecProb.9.1 = 45
|
||||
AiPlayerbot.RandomClassSpecIndex.9.1 = 1
|
||||
AiPlayerbot.RandomClassSpecProb.9.2 = 20
|
||||
AiPlayerbot.RandomClassSpecProb.9.2 = 10
|
||||
AiPlayerbot.RandomClassSpecIndex.9.2 = 2
|
||||
|
||||
#
|
||||
|
||||
@@ -5257,6 +5257,7 @@ bool PlayerbotAI::EqualLowercaseName(std::string s1, std::string s2)
|
||||
return true;
|
||||
}
|
||||
|
||||
// A custom CanEquipItem (remove AutoUnequipOffhand in FindEquipSlot to prevent unequip on `item usage` calculation)
|
||||
InventoryResult PlayerbotAI::CanEquipItem(uint8 slot, uint16& dest, Item* pItem, bool swap, bool not_loading) const
|
||||
{
|
||||
dest = 0;
|
||||
@@ -5277,11 +5278,9 @@ InventoryResult PlayerbotAI::CanEquipItem(uint8 slot, uint16& dest, Item* pItem,
|
||||
if (pItem->IsBindedNotWith(bot))
|
||||
return EQUIP_ERR_DONT_OWN_THAT_ITEM;
|
||||
|
||||
// Yunfan: skip it
|
||||
// // check count of items (skip for auto move for same player from bank)
|
||||
// InventoryResult res = bot->CanTakeMoreSimilarItems(pItem);
|
||||
// if (res != EQUIP_ERR_OK)
|
||||
// return res;
|
||||
InventoryResult res = bot->CanTakeMoreSimilarItems(pItem);
|
||||
if (res != EQUIP_ERR_OK)
|
||||
return res;
|
||||
|
||||
ScalingStatDistributionEntry const* ssd =
|
||||
pProto->ScalingStatDistribution
|
||||
@@ -5300,7 +5299,7 @@ InventoryResult PlayerbotAI::CanEquipItem(uint8 slot, uint16& dest, Item* pItem,
|
||||
if (!bot->CanUseAttackType(bot->GetAttackBySlot(eslot)))
|
||||
return EQUIP_ERR_NOT_WHILE_DISARMED;
|
||||
|
||||
InventoryResult res = bot->CanUseItem(pItem, not_loading);
|
||||
res = bot->CanUseItem(pItem, not_loading);
|
||||
if (res != EQUIP_ERR_OK)
|
||||
return res;
|
||||
|
||||
|
||||
@@ -331,6 +331,17 @@ bool PlayerbotAIConfig::Initialize()
|
||||
parsedSpecLinkOrder[cls][spec][level] = ParseTempTalentsOrder(cls, premadeSpecLink[cls][spec][level]);
|
||||
}
|
||||
}
|
||||
for (uint32 spec = 0; spec < 3; ++spec)
|
||||
{
|
||||
for (uint32 points = 0; points < 21; ++points)
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << "AiPlayerbot.PremadeHunterPetLink." << spec << "." << points;
|
||||
premadeHunterPetLink[spec][points] = sConfigMgr->GetOption<std::string>(os.str().c_str(), "", false);
|
||||
parsedHunterPetLinkOrder[spec][points] =
|
||||
ParseTempPetTalentsOrder(spec, premadeHunterPetLink[spec][points]);
|
||||
}
|
||||
}
|
||||
for (uint32 spec = 0; spec < MAX_SPECNO; ++spec)
|
||||
{
|
||||
std::ostringstream os;
|
||||
@@ -460,6 +471,7 @@ bool PlayerbotAIConfig::Initialize()
|
||||
autoPickReward = sConfigMgr->GetOption<std::string>("AiPlayerbot.AutoPickReward", "yes");
|
||||
autoEquipUpgradeLoot = sConfigMgr->GetOption<bool>("AiPlayerbot.AutoEquipUpgradeLoot", true);
|
||||
equipUpgradeThreshold = sConfigMgr->GetOption<float>("AiPlayerbot.EquipUpgradeThreshold", 1.1f);
|
||||
twoRoundsGearInit = sConfigMgr->GetOption<bool>("AiPlayerbot.TwoRoundsGearInit", false);
|
||||
syncQuestWithPlayer = sConfigMgr->GetOption<bool>("AiPlayerbot.SyncQuestWithPlayer", true);
|
||||
syncQuestForPlayer = sConfigMgr->GetOption<bool>("AiPlayerbot.SyncQuestForPlayer", false);
|
||||
autoTrainSpells = sConfigMgr->GetOption<std::string>("AiPlayerbot.AutoTrainSpells", "yes");
|
||||
@@ -484,13 +496,13 @@ bool PlayerbotAIConfig::Initialize()
|
||||
selfBotLevel = sConfigMgr->GetOption<int32>("AiPlayerbot.SelfBotLevel", 1);
|
||||
|
||||
RandomPlayerbotFactory::CreateRandomBots();
|
||||
if (sPlayerbotAIConfig->addClassCommand)
|
||||
sRandomPlayerbotMgr->PrepareAddclassCache();
|
||||
|
||||
if (World::IsStopped())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (sPlayerbotAIConfig->addClassCommand)
|
||||
sRandomPlayerbotMgr->PrepareAddclassCache();
|
||||
|
||||
sRandomItemMgr->Init();
|
||||
sRandomItemMgr->InitAfterAhBot();
|
||||
sPlayerbotTextMgr->LoadBotTexts();
|
||||
@@ -760,3 +772,47 @@ std::vector<std::vector<uint32>> PlayerbotAIConfig::ParseTempTalentsOrder(uint32
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
std::vector<std::vector<uint32>> PlayerbotAIConfig::ParseTempPetTalentsOrder(uint32 spec, std::string tab_link)
|
||||
{
|
||||
// check bad link
|
||||
// uint32 classMask = 1 << (cls - 1);
|
||||
std::vector<TalentEntry const*> spells;
|
||||
std::vector<std::vector<uint32>> orders;
|
||||
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)
|
||||
continue;
|
||||
|
||||
if (!((1 << spec) & talentTabInfo->petTalentMask))
|
||||
continue;
|
||||
// skip some duplicate spells like dash/dive
|
||||
if (talentInfo->TalentID == 2201 || talentInfo->TalentID == 2208 || talentInfo->TalentID == 2219 || talentInfo->TalentID == 2203)
|
||||
continue;
|
||||
|
||||
spells.push_back(talentInfo);
|
||||
}
|
||||
std::sort(spells.begin(), spells.end(),
|
||||
[&](TalentEntry const* lhs, TalentEntry const* rhs)
|
||||
{ return lhs->Row != rhs->Row ? lhs->Row < rhs->Row : lhs->Col < rhs->Col; });
|
||||
for (int i = 0; i < tab_link.size(); i++)
|
||||
{
|
||||
if (i >= spells.size())
|
||||
{
|
||||
break;
|
||||
}
|
||||
int lvl = tab_link[i] - '0';
|
||||
if (lvl == 0)
|
||||
continue;
|
||||
orders.push_back({spells[i]->Row, spells[i]->Col, (uint32)lvl});
|
||||
}
|
||||
// sort by talent tab size
|
||||
std::sort(orders.begin(), orders.end(), [&](auto& lhs, auto& rhs) { return lhs.size() > rhs.size(); });
|
||||
|
||||
return orders;
|
||||
}
|
||||
|
||||
@@ -195,7 +195,9 @@ public:
|
||||
std::string premadeSpecGlyph[MAX_CLASSES][MAX_SPECNO];
|
||||
std::vector<uint32> parsedSpecGlyph[MAX_CLASSES][MAX_SPECNO];
|
||||
std::string premadeSpecLink[MAX_CLASSES][MAX_SPECNO][MAX_LEVEL];
|
||||
std::string premadeHunterPetLink[3][21];
|
||||
std::vector<std::vector<uint32>> parsedSpecLinkOrder[MAX_CLASSES][MAX_SPECNO][MAX_LEVEL];
|
||||
std::vector<std::vector<uint32>> parsedHunterPetLinkOrder[3][21];
|
||||
uint32 randomClassSpecProb[MAX_CLASSES][MAX_SPECNO];
|
||||
uint32 randomClassSpecIndex[MAX_CLASSES][MAX_SPECNO];
|
||||
|
||||
@@ -269,6 +271,7 @@ public:
|
||||
std::string autoPickReward;
|
||||
bool autoEquipUpgradeLoot;
|
||||
float equipUpgradeThreshold;
|
||||
bool twoRoundsGearInit;
|
||||
bool syncQuestWithPlayer;
|
||||
bool syncQuestForPlayer;
|
||||
std::string autoTrainSpells;
|
||||
@@ -325,6 +328,7 @@ public:
|
||||
|
||||
void loadWorldBuf(uint32 factionId, uint32 classId, uint32 minLevel, uint32 maxLevel);
|
||||
static std::vector<std::vector<uint32>> ParseTempTalentsOrder(uint32 cls, std::string temp_talents_order);
|
||||
static std::vector<std::vector<uint32>> ParseTempPetTalentsOrder(uint32 spec, std::string temp_talents_order);
|
||||
};
|
||||
|
||||
#define sPlayerbotAIConfig PlayerbotAIConfig::instance()
|
||||
|
||||
@@ -724,14 +724,14 @@ std::string const PlayerbotHolder::ProcessBotCommand(std::string const cmd, Obje
|
||||
sPlayerbotAIConfig->autoInitEquipLevelLimitRatio;
|
||||
PlayerbotFactory factory(bot, master->GetLevel(), ITEM_QUALITY_LEGENDARY, mixedGearScore);
|
||||
factory.Randomize(false);
|
||||
return "ok, gear score limit: " + std::to_string(mixedGearScore / (ITEM_QUALITY_EPIC + 1)) +
|
||||
return "ok, gear score limit: " + std::to_string(mixedGearScore / PlayerbotAI::GetItemScoreMultiplier(ItemQualities(ITEM_QUALITY_EPIC))) +
|
||||
"(for epic)";
|
||||
}
|
||||
else if (cmd.starts_with("init=") && sscanf(cmd.c_str(), "init=%d", &gs) != -1)
|
||||
{
|
||||
PlayerbotFactory factory(bot, master->GetLevel(), ITEM_QUALITY_LEGENDARY, gs);
|
||||
factory.Randomize(false);
|
||||
return "ok, gear score limit: " + std::to_string(gs / (ITEM_QUALITY_EPIC + 1)) + "(for epic)";
|
||||
return "ok, gear score limit: " + std::to_string(gs / PlayerbotAI::GetItemScoreMultiplier(ItemQualities(ITEM_QUALITY_EPIC))) + "(for epic)";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -845,6 +845,22 @@ std::vector<std::string> PlayerbotHolder::HandlePlayerbotCommand(char const* arg
|
||||
|
||||
if (!strncmp(cmd, "initself=", 9))
|
||||
{
|
||||
if (!strcmp(cmd, "initself=uncommon"))
|
||||
{
|
||||
if (master->GetSession()->GetSecurity() >= SEC_GAMEMASTER)
|
||||
{
|
||||
// OnBotLogin(master);
|
||||
PlayerbotFactory factory(master, master->GetLevel(), ITEM_QUALITY_UNCOMMON);
|
||||
factory.Randomize(false);
|
||||
messages.push_back("initself ok");
|
||||
return messages;
|
||||
}
|
||||
else
|
||||
{
|
||||
messages.push_back("ERROR: Only GM can use this command.");
|
||||
return messages;
|
||||
}
|
||||
}
|
||||
if (!strcmp(cmd, "initself=rare"))
|
||||
{
|
||||
if (master->GetSession()->GetSecurity() >= SEC_GAMEMASTER)
|
||||
@@ -877,6 +893,22 @@ std::vector<std::string> PlayerbotHolder::HandlePlayerbotCommand(char const* arg
|
||||
return messages;
|
||||
}
|
||||
}
|
||||
if (!strcmp(cmd, "initself=legendary"))
|
||||
{
|
||||
if (master->GetSession()->GetSecurity() >= SEC_GAMEMASTER)
|
||||
{
|
||||
// OnBotLogin(master);
|
||||
PlayerbotFactory factory(master, master->GetLevel(), ITEM_QUALITY_LEGENDARY);
|
||||
factory.Randomize(false);
|
||||
messages.push_back("initself ok");
|
||||
return messages;
|
||||
}
|
||||
else
|
||||
{
|
||||
messages.push_back("ERROR: Only GM can use this command.");
|
||||
return messages;
|
||||
}
|
||||
}
|
||||
int32 gs;
|
||||
if (sscanf(cmd, "initself=%d", &gs) != -1)
|
||||
{
|
||||
|
||||
@@ -370,7 +370,7 @@ void RandomPlayerbotMgr::UpdateAIInternal(uint32 elapsed, bool /*minimal*/)
|
||||
break;
|
||||
}
|
||||
|
||||
if (loginBots)
|
||||
if (loginBots && botLoading.empty())
|
||||
{
|
||||
loginBots += updateBots;
|
||||
loginBots = std::min(loginBots, maxNewBots);
|
||||
@@ -1041,6 +1041,9 @@ bool RandomPlayerbotMgr::ProcessBot(uint32 bot)
|
||||
|
||||
SetEventValue(bot, "login", 0, 0);
|
||||
|
||||
if (!player->IsInWorld())
|
||||
return false;
|
||||
|
||||
if (player->GetGroup() || player->HasUnitState(UNIT_STATE_IN_FLIGHT))
|
||||
return false;
|
||||
|
||||
|
||||
@@ -39,6 +39,8 @@
|
||||
|
||||
#define PLAYER_SKILL_INDEX(x) (PLAYER_SKILL_INFO_1_1 + ((x)*3))
|
||||
|
||||
const uint64 diveMask = (1LL << 7) | (1LL << 44) | (1LL << 37) | (1LL << 38) | (1LL << 26) | (1LL << 30) | (1LL << 27) |
|
||||
(1LL << 33) | (1LL << 24) | (1LL << 34);
|
||||
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,
|
||||
@@ -233,6 +235,7 @@ void PlayerbotFactory::Randomize(bool incremental)
|
||||
pmo = sPerformanceMonitor->start(PERF_MON_RNDBOT, "PlayerbotFactory_Spells1");
|
||||
LOG_DEBUG("playerbots", "Initializing spells (step 1)...");
|
||||
// bot->LearnDefaultSkills();
|
||||
bot->LearnDefaultSkills();
|
||||
InitClassSpells();
|
||||
InitAvailableSpells();
|
||||
if (pmo)
|
||||
@@ -286,7 +289,7 @@ void PlayerbotFactory::Randomize(bool incremental)
|
||||
LOG_DEBUG("playerbots", "Initializing equipmemt...");
|
||||
if (!sPlayerbotAIConfig->equipmentPersistence || bot->GetLevel() < sPlayerbotAIConfig->equipmentPersistenceLevel)
|
||||
{
|
||||
InitEquipment(incremental);
|
||||
InitEquipment(incremental, incremental ? false : sPlayerbotAIConfig->twoRoundsGearInit);
|
||||
}
|
||||
// bot->SaveToDB(false, false);
|
||||
if (pmo)
|
||||
@@ -423,7 +426,10 @@ void PlayerbotFactory::Refresh()
|
||||
InitFood();
|
||||
InitReagents();
|
||||
// InitPotions();
|
||||
InitTalentsTree(true, true, true);
|
||||
if (!sPlayerbotAIConfig->equipmentPersistence || bot->GetLevel() < sPlayerbotAIConfig->equipmentPersistenceLevel)
|
||||
{
|
||||
InitTalentsTree(true, true, true);
|
||||
}
|
||||
InitPet();
|
||||
InitPetTalents();
|
||||
InitClassSpells();
|
||||
@@ -611,8 +617,9 @@ void PlayerbotFactory::InitPetTalents()
|
||||
// pet_family->petTalentType);
|
||||
return;
|
||||
}
|
||||
// pet->resetTalents();
|
||||
std::unordered_map<uint32, std::vector<TalentEntry const*>> spells;
|
||||
bool diveTypePet = (1LL << ci->family) & diveMask;
|
||||
|
||||
for (uint32 i = 0; i < sTalentStore.GetNumRows(); ++i)
|
||||
{
|
||||
TalentEntry const* talentInfo = sTalentStore.LookupEntry(i);
|
||||
@@ -624,49 +631,115 @@ void PlayerbotFactory::InitPetTalents()
|
||||
// prevent learn talent for different family (cheating)
|
||||
if (!((1 << pet_family->petTalentType) & talentTabInfo->petTalentMask))
|
||||
continue;
|
||||
|
||||
bool diveClass = talentInfo->TalentID == 2201 || talentInfo->TalentID == 2208 || talentInfo->TalentID == 2219 ||
|
||||
talentInfo->TalentID == 2203;
|
||||
if (diveClass && !diveTypePet)
|
||||
continue;
|
||||
bool dashClass = talentInfo->TalentID == 2119 || talentInfo->TalentID == 2207 || talentInfo->TalentID == 2111 ||
|
||||
talentInfo->TalentID == 2109;
|
||||
if (dashClass && diveTypePet)
|
||||
continue;
|
||||
spells[talentInfo->Row].push_back(talentInfo);
|
||||
}
|
||||
|
||||
uint32 curTalentPoints = pet->GetFreeTalentPoints();
|
||||
std::vector<std::vector<uint32>> order =
|
||||
sPlayerbotAIConfig->parsedHunterPetLinkOrder[pet_family->petTalentType][20];
|
||||
uint32 maxTalentPoints = pet->GetMaxTalentPointsForLevel(pet->GetLevel());
|
||||
int row = 0;
|
||||
// LOG_INFO("playerbots", "{} learning, max talent points: {}, cur: {}", bot->GetName().c_str(), maxTalentPoints,
|
||||
// curTalentPoints);
|
||||
for (auto i = spells.begin(); i != spells.end(); ++i, ++row)
|
||||
{
|
||||
std::vector<TalentEntry const*>& spells_row = i->second;
|
||||
if (spells_row.empty())
|
||||
{
|
||||
LOG_INFO("playerbots", "{}: No spells for talent row {}", bot->GetName().c_str(), i->first);
|
||||
continue;
|
||||
}
|
||||
int attemptCount = 0;
|
||||
// keep learning for the last row
|
||||
while (!spells_row.empty() &&
|
||||
((((int)maxTalentPoints - (int)pet->GetFreeTalentPoints()) < 3 * (row + 1)) || (row == 5)) &&
|
||||
attemptCount++ < 10 && pet->GetFreeTalentPoints())
|
||||
{
|
||||
int index = urand(0, spells_row.size() - 1);
|
||||
TalentEntry const* talentInfo = spells_row[index];
|
||||
int maxRank = 0;
|
||||
for (int rank = 0; rank < std::min((uint32)MAX_TALENT_RANK, (uint32)pet->GetFreeTalentPoints()); ++rank)
|
||||
{
|
||||
uint32 spellId = talentInfo->RankID[rank];
|
||||
if (!spellId)
|
||||
continue;
|
||||
|
||||
maxRank = rank;
|
||||
}
|
||||
// LOG_INFO("playerbots", "{} learn pet talent {}({})", bot->GetName().c_str(), talentInfo->TalentID,
|
||||
// maxRank);
|
||||
if (talentInfo->DependsOn)
|
||||
if (order.empty())
|
||||
{
|
||||
int row = 0;
|
||||
for (auto i = spells.begin(); i != spells.end(); ++i, ++row)
|
||||
{
|
||||
std::vector<TalentEntry const*>& spells_row = i->second;
|
||||
if (spells_row.empty())
|
||||
{
|
||||
bot->LearnPetTalent(pet->GetGUID(), talentInfo->DependsOn,
|
||||
std::min(talentInfo->DependsOnRank, bot->GetFreeTalentPoints() - 1));
|
||||
LOG_INFO("playerbots", "{}: No spells for talent row {}", bot->GetName().c_str(), i->first);
|
||||
continue;
|
||||
}
|
||||
int attemptCount = 0;
|
||||
// keep learning for the last row
|
||||
while (!spells_row.empty() &&
|
||||
((((int)maxTalentPoints - (int)pet->GetFreeTalentPoints()) < 3 * (row + 1)) || (row == 5)) &&
|
||||
attemptCount++ < 10 && pet->GetFreeTalentPoints())
|
||||
{
|
||||
int index = urand(0, spells_row.size() - 1);
|
||||
TalentEntry const* talentInfo = spells_row[index];
|
||||
int maxRank = 0;
|
||||
for (int rank = 0; rank < std::min((uint32)MAX_TALENT_RANK, (uint32)pet->GetFreeTalentPoints()); ++rank)
|
||||
{
|
||||
uint32 spellId = talentInfo->RankID[rank];
|
||||
if (!spellId)
|
||||
continue;
|
||||
|
||||
maxRank = rank;
|
||||
}
|
||||
// LOG_INFO("playerbots", "{} learn pet talent {}({})", bot->GetName().c_str(), talentInfo->TalentID,
|
||||
// maxRank);
|
||||
if (talentInfo->DependsOn)
|
||||
{
|
||||
bot->LearnPetTalent(pet->GetGUID(), talentInfo->DependsOn,
|
||||
std::min(talentInfo->DependsOnRank, bot->GetFreeTalentPoints() - 1));
|
||||
}
|
||||
bot->LearnPetTalent(pet->GetGUID(), talentInfo->TalentID, maxRank);
|
||||
spells_row.erase(spells_row.begin() + index);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
uint32 spec = pet_family->petTalentType;
|
||||
uint32 startPoints = pet->GetMaxTalentPointsForLevel(pet->GetLevel());
|
||||
while (startPoints > 1 && startPoints < 20 &&
|
||||
sPlayerbotAIConfig->parsedHunterPetLinkOrder[spec][startPoints].size() == 0)
|
||||
{
|
||||
startPoints--;
|
||||
}
|
||||
|
||||
for (uint32 points = startPoints; points <= 20; points++)
|
||||
{
|
||||
if (sPlayerbotAIConfig->parsedHunterPetLinkOrder[spec][points].size() == 0)
|
||||
continue;
|
||||
for (std::vector<uint32>& p : sPlayerbotAIConfig->parsedHunterPetLinkOrder[spec][points])
|
||||
{
|
||||
uint32 row = p[0], col = p[1], lvl = p[2];
|
||||
uint32 talentID = 0;
|
||||
uint32 learnLevel = 0;
|
||||
std::vector<TalentEntry const*>& spell = spells[row];
|
||||
for (TalentEntry const* talentInfo : spell)
|
||||
{
|
||||
if (talentInfo->Col != col)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (talentInfo->DependsOn)
|
||||
{
|
||||
bot->LearnPetTalent(pet->GetGUID(), talentInfo->DependsOn,
|
||||
std::min(talentInfo->DependsOnRank, bot->GetFreeTalentPoints() - 1));
|
||||
}
|
||||
talentID = talentInfo->TalentID;
|
||||
|
||||
uint32 currentTalentRank = 0;
|
||||
for (uint8 rank = 0; rank < MAX_TALENT_RANK; ++rank)
|
||||
{
|
||||
if (talentInfo->RankID[rank] && pet->HasSpell(talentInfo->RankID[rank]))
|
||||
{
|
||||
currentTalentRank = rank + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
learnLevel = std::min(lvl, pet->GetFreeTalentPoints() + currentTalentRank) - 1;
|
||||
}
|
||||
bot->LearnPetTalent(pet->GetGUID(), talentID, learnLevel);
|
||||
if (pet->GetFreeTalentPoints() == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (pet->GetFreeTalentPoints() == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
bot->LearnPetTalent(pet->GetGUID(), talentInfo->TalentID, maxRank);
|
||||
spells_row.erase(spells_row.begin() + index);
|
||||
}
|
||||
}
|
||||
bot->SendTalentsInfoData(true);
|
||||
@@ -816,7 +889,7 @@ void PlayerbotFactory::ClearEverything()
|
||||
ClearSpells();
|
||||
ClearInventory();
|
||||
ResetQuests();
|
||||
bot->SaveToDB(false, false);
|
||||
// bot->SaveToDB(false, false);
|
||||
}
|
||||
|
||||
void PlayerbotFactory::ClearSpells()
|
||||
@@ -881,12 +954,18 @@ void PlayerbotFactory::InitTalentsTree(bool increment /*false*/, bool use_templa
|
||||
/// @todo: match current talent with template
|
||||
specTab = AiFactory::GetPlayerSpecTab(bot);
|
||||
/// @todo: fix cat druid hardcode
|
||||
if (bot->getClass() == CLASS_DRUID && specTab == DRUID_TAB_FERAL && bot->GetLevel() >= 20 && !bot->HasAura(16931))
|
||||
if (bot->getClass() == CLASS_DRUID && specTab == DRUID_TAB_FERAL && bot->GetLevel() >= 20 &&
|
||||
!bot->HasAura(16931))
|
||||
specTab = 3;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
uint32 point = urand(1, 100);
|
||||
uint32 pointSum = 0;
|
||||
for (int i = 0; i < MAX_SPECNO; i++)
|
||||
{
|
||||
pointSum += sPlayerbotAIConfig->randomClassSpecProb[cls][i];
|
||||
}
|
||||
uint32 point = urand(1, pointSum);
|
||||
uint32 currentP = 0;
|
||||
int i;
|
||||
for (i = 0; i < MAX_SPECNO; i++)
|
||||
@@ -1291,7 +1370,8 @@ bool PlayerbotFactory::CanEquipItem(ItemTemplate const* proto)
|
||||
return true;
|
||||
|
||||
uint32 requiredLevel = proto->RequiredLevel;
|
||||
bool hasItem = bot->HasItemCount(proto->ItemId, 1, true);
|
||||
// disable since bad performance
|
||||
bool hasItem = bot->HasItemCount(proto->ItemId, 1, false);
|
||||
// bot->GetItemCount()
|
||||
// !requiredLevel -> it's a quest reward item
|
||||
if (!requiredLevel && hasItem)
|
||||
@@ -1447,7 +1527,7 @@ void Shuffle(std::vector<uint32>& items)
|
||||
// }
|
||||
// }
|
||||
|
||||
void PlayerbotFactory::InitEquipment(bool incremental)
|
||||
void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance)
|
||||
{
|
||||
std::unordered_map<uint8, std::vector<uint32>> items;
|
||||
// int tab = AiFactory::GetPlayerSpecTab(bot);
|
||||
@@ -1467,6 +1547,7 @@ void PlayerbotFactory::InitEquipment(bool incremental)
|
||||
else if (blevel == 80)
|
||||
delta = 9;
|
||||
|
||||
StatsWeightCalculator calculator(bot);
|
||||
for (uint8 slot = 0; slot < EQUIPMENT_SLOT_END; ++slot)
|
||||
{
|
||||
if (slot == EQUIPMENT_SLOT_TABARD || slot == EQUIPMENT_SLOT_BODY)
|
||||
@@ -1484,6 +1565,15 @@ void PlayerbotFactory::InitEquipment(bool incremental)
|
||||
if (level < 20 && (slot == EQUIPMENT_SLOT_FINGER1 || slot == EQUIPMENT_SLOT_FINGER2))
|
||||
continue;
|
||||
|
||||
Item* oldItem = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot);
|
||||
|
||||
if (second_chance && oldItem)
|
||||
{
|
||||
bot->DestroyItem(INVENTORY_SLOT_BAG_0, slot, true);
|
||||
}
|
||||
|
||||
oldItem = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot);
|
||||
|
||||
uint32 desiredQuality = itemQuality;
|
||||
if (urand(0, 100) < 100 * sPlayerbotAIConfig->randomGearLoweringChance && desiredQuality > ITEM_QUALITY_NORMAL)
|
||||
{
|
||||
@@ -1498,10 +1588,13 @@ void PlayerbotFactory::InitEquipment(bool incremental)
|
||||
{
|
||||
for (uint32 itemId : sRandomItemMgr->GetCachedEquipments(requiredLevel, inventoryType))
|
||||
{
|
||||
if (itemId == 46978)
|
||||
{ // shaman earth ring totem
|
||||
if (itemId == 46978) // shaman earth ring totem
|
||||
{
|
||||
continue;
|
||||
}
|
||||
uint32 skipProb = 25;
|
||||
if (urand(1, 100) <= skipProb)
|
||||
continue;
|
||||
|
||||
// disable next expansion gear
|
||||
if (sPlayerbotAIConfig->limitGearExpansion && bot->GetLevel() <= 60 && itemId >= 23728)
|
||||
@@ -1527,10 +1620,10 @@ void PlayerbotFactory::InitEquipment(bool incremental)
|
||||
|
||||
if (proto->Quality != desiredQuality)
|
||||
continue;
|
||||
// delay heavy check
|
||||
// if (!CanEquipItem(proto))
|
||||
// continue;
|
||||
|
||||
if (!CanEquipItem(proto))
|
||||
continue;
|
||||
|
||||
if (proto->Class == ITEM_CLASS_ARMOR &&
|
||||
(slot == EQUIPMENT_SLOT_HEAD || slot == EQUIPMENT_SLOT_SHOULDERS ||
|
||||
slot == EQUIPMENT_SLOT_CHEST || slot == EQUIPMENT_SLOT_WAIST ||
|
||||
@@ -1545,95 +1638,153 @@ void PlayerbotFactory::InitEquipment(bool incremental)
|
||||
if (slot == EQUIPMENT_SLOT_OFFHAND && bot->getClass() == CLASS_ROGUE &&
|
||||
proto->Class != ITEM_CLASS_WEAPON)
|
||||
continue;
|
||||
|
||||
uint16 dest = 0;
|
||||
if (CanEquipUnseenItem(slot, dest, itemId))
|
||||
items[slot].push_back(itemId);
|
||||
// delay heavy check
|
||||
// uint16 dest = 0;
|
||||
// if (CanEquipUnseenItem(slot, dest, itemId))
|
||||
items[slot].push_back(itemId);
|
||||
}
|
||||
}
|
||||
if (items[slot].size() >= 25)
|
||||
break;
|
||||
}
|
||||
} while (items[slot].size() < 25 && desiredQuality-- > ITEM_QUALITY_NORMAL);
|
||||
}
|
||||
|
||||
StatsWeightCalculator calculator(bot);
|
||||
for (uint8 slot = 0; slot < EQUIPMENT_SLOT_END; ++slot)
|
||||
{
|
||||
if (slot == EQUIPMENT_SLOT_TABARD || slot == EQUIPMENT_SLOT_BODY)
|
||||
continue;
|
||||
|
||||
if (level < 40 && (slot == EQUIPMENT_SLOT_TRINKET1 || slot == EQUIPMENT_SLOT_TRINKET2))
|
||||
continue;
|
||||
|
||||
if (level < 25 && slot == EQUIPMENT_SLOT_NECK)
|
||||
continue;
|
||||
|
||||
if (level < 25 && slot == EQUIPMENT_SLOT_HEAD)
|
||||
continue;
|
||||
|
||||
std::vector<uint32>& ids = items[slot];
|
||||
if (ids.empty())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
Item* oldItem = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot);
|
||||
|
||||
if (incremental && !IsDesiredReplacement(oldItem))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
float bestScoreForSlot = -1;
|
||||
uint32 bestItemForSlot = 0;
|
||||
for (int index = 0; index < ids.size(); index++)
|
||||
{
|
||||
uint32 skipProb = 25;
|
||||
if (urand(0, 100) <= skipProb)
|
||||
continue;
|
||||
|
||||
uint32 newItemId = ids[index];
|
||||
|
||||
uint16 dest;
|
||||
|
||||
ItemTemplate const* proto = sObjectMgr->GetItemTemplate(newItemId);
|
||||
|
||||
if (!CanEquipItem(proto))
|
||||
continue;
|
||||
|
||||
if (oldItem && oldItem->GetTemplate()->ItemId == newItemId)
|
||||
continue;
|
||||
|
||||
if (!CanEquipUnseenItem(slot, dest, newItemId))
|
||||
continue;
|
||||
|
||||
float cur_score = calculator.CalculateItem(newItemId);
|
||||
if (cur_score > bestScoreForSlot)
|
||||
{
|
||||
// delay heavy check to here
|
||||
if (!CanEquipItem(proto))
|
||||
continue;
|
||||
uint16 dest;
|
||||
if (!CanEquipUnseenItem(slot, dest, newItemId))
|
||||
continue;
|
||||
bestScoreForSlot = cur_score;
|
||||
bestItemForSlot = newItemId;
|
||||
}
|
||||
}
|
||||
|
||||
if (bestItemForSlot == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (oldItem)
|
||||
{
|
||||
bot->DestroyItem(INVENTORY_SLOT_BAG_0, slot, true);
|
||||
}
|
||||
uint16 dest;
|
||||
if (!CanEquipUnseenItem(slot, dest, bestItemForSlot))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
Item* newItem = bot->EquipNewItem(dest, bestItemForSlot, true);
|
||||
if (newItem)
|
||||
|
||||
if (incremental && oldItem)
|
||||
{
|
||||
newItem->AddToWorld();
|
||||
newItem->AddToUpdateQueueOf(bot);
|
||||
// bot->AutoUnequipOffhandIfNeed();
|
||||
// EnchantItem(newItem);
|
||||
float old_score = calculator.CalculateItem(oldItem->GetEntry());
|
||||
if (bestScoreForSlot < 1.2f * old_score)
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
if (oldItem)
|
||||
{
|
||||
uint8 bagIndex = oldItem->GetBagSlot();
|
||||
uint8 slot = oldItem->GetSlot();
|
||||
uint8 dstBag = NULL_BAG;
|
||||
|
||||
WorldPacket packet(CMSG_AUTOSTORE_BAG_ITEM, 3);
|
||||
packet << bagIndex << slot << dstBag;
|
||||
bot->GetSession()->HandleAutoStoreBagItemOpcode(packet);
|
||||
}
|
||||
|
||||
oldItem = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot);
|
||||
// fail to store in bag
|
||||
if (oldItem)
|
||||
continue;
|
||||
|
||||
Item* newItem = bot->EquipNewItem(dest, bestItemForSlot, true);
|
||||
bot->AutoUnequipOffhandIfNeed();
|
||||
// if (newItem)
|
||||
// {
|
||||
// newItem->AddToWorld();
|
||||
// newItem->AddToUpdateQueueOf(bot);
|
||||
// }
|
||||
}
|
||||
// Secondary init for better equips
|
||||
/// @todo: clean up duplicate code
|
||||
if (second_chance)
|
||||
{
|
||||
for (uint8 slot = 0; slot < EQUIPMENT_SLOT_END; ++slot)
|
||||
{
|
||||
if (slot == EQUIPMENT_SLOT_TABARD || slot == EQUIPMENT_SLOT_BODY)
|
||||
continue;
|
||||
|
||||
if (level < 40 && (slot == EQUIPMENT_SLOT_TRINKET1 || slot == EQUIPMENT_SLOT_TRINKET2))
|
||||
continue;
|
||||
|
||||
if (level < 30 && slot == EQUIPMENT_SLOT_NECK)
|
||||
continue;
|
||||
|
||||
if (level < 25 && slot == EQUIPMENT_SLOT_HEAD)
|
||||
continue;
|
||||
|
||||
if (level < 20 && (slot == EQUIPMENT_SLOT_FINGER1 || slot == EQUIPMENT_SLOT_FINGER2))
|
||||
continue;
|
||||
|
||||
if (Item* oldItem = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, slot))
|
||||
bot->DestroyItem(INVENTORY_SLOT_BAG_0, slot, true);
|
||||
|
||||
std::vector<uint32>& ids = items[slot];
|
||||
if (ids.empty())
|
||||
continue;
|
||||
|
||||
float bestScoreForSlot = -1;
|
||||
uint32 bestItemForSlot = 0;
|
||||
for (int index = 0; index < ids.size(); index++)
|
||||
{
|
||||
uint32 newItemId = ids[index];
|
||||
|
||||
ItemTemplate const* proto = sObjectMgr->GetItemTemplate(newItemId);
|
||||
|
||||
float cur_score = calculator.CalculateItem(newItemId);
|
||||
if (cur_score > bestScoreForSlot)
|
||||
{
|
||||
// delay heavy check to here
|
||||
if (!CanEquipItem(proto))
|
||||
continue;
|
||||
uint16 dest;
|
||||
if (!CanEquipUnseenItem(slot, dest, newItemId))
|
||||
continue;
|
||||
bestScoreForSlot = cur_score;
|
||||
bestItemForSlot = newItemId;
|
||||
}
|
||||
}
|
||||
|
||||
if (bestItemForSlot == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
uint16 dest;
|
||||
if (!CanEquipUnseenItem(slot, dest, bestItemForSlot))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
Item* newItem = bot->EquipNewItem(dest, bestItemForSlot, true);
|
||||
bot->AutoUnequipOffhandIfNeed();
|
||||
// if (newItem)
|
||||
// {
|
||||
// newItem->AddToWorld();
|
||||
// newItem->AddToUpdateQueueOf(bot);
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1805,11 +1956,11 @@ void PlayerbotFactory::InitBags(bool destroyOld)
|
||||
continue;
|
||||
}
|
||||
Item* newItem = bot->EquipNewItem(dest, newItemId, true);
|
||||
if (newItem)
|
||||
{
|
||||
newItem->AddToWorld();
|
||||
newItem->AddToUpdateQueueOf(bot);
|
||||
}
|
||||
// if (newItem)
|
||||
// {
|
||||
// newItem->AddToWorld();
|
||||
// newItem->AddToUpdateQueueOf(bot);
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1909,7 +2060,8 @@ bool PlayerbotFactory::CanEquipUnseenItem(uint8 slot, uint16& dest, uint32 item)
|
||||
|
||||
if (Item* pItem = Item::CreateItem(item, 1, bot, false, 0, true))
|
||||
{
|
||||
InventoryResult result = bot->CanEquipItem(slot, dest, pItem, true, false);
|
||||
InventoryResult result = botAI ? botAI->CanEquipItem(slot, dest, pItem, true, true)
|
||||
: bot->CanEquipItem(slot, dest, pItem, true, true);
|
||||
pItem->RemoveFromUpdateQueueOf(bot);
|
||||
delete pItem;
|
||||
return result == EQUIP_ERR_OK;
|
||||
@@ -2164,8 +2316,6 @@ void PlayerbotFactory::SetRandomSkill(uint16 id)
|
||||
|
||||
void PlayerbotFactory::InitAvailableSpells()
|
||||
{
|
||||
bot->LearnDefaultSkills();
|
||||
|
||||
if (trainerIdCache.empty())
|
||||
{
|
||||
CreatureTemplateContainer const* creatureTemplateContainer = sObjectMgr->GetCreatureTemplates();
|
||||
@@ -2183,6 +2333,8 @@ void PlayerbotFactory::InitAvailableSpells()
|
||||
trainerIdCache.push_back(trainerId);
|
||||
}
|
||||
}
|
||||
// uint32 learnedCounter = 0;
|
||||
// uint32 oktest = 0;
|
||||
for (uint32 trainerId : trainerIdCache)
|
||||
{
|
||||
TrainerSpellData const* trainer_spells = sObjectMgr->GetNpcTrainerSpells(trainerId);
|
||||
@@ -2226,16 +2378,19 @@ void PlayerbotFactory::InitAvailableSpells()
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// oktest++;
|
||||
if (tSpell->learnedSpell[0])
|
||||
{
|
||||
bot->learnSpell(tSpell->learnedSpell[0], false);
|
||||
}
|
||||
else
|
||||
{
|
||||
botAI->CastSpell(tSpell->spell, bot);
|
||||
}
|
||||
// else
|
||||
// {
|
||||
// botAI->CastSpell(tSpell->spell, bot);
|
||||
// }
|
||||
}
|
||||
// LOG_INFO("playerbots", "C: {}, ok: {}", ++learnedCounter, oktest);
|
||||
// if (++learnedCounter > 20)
|
||||
// break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2645,7 +2800,10 @@ void PlayerbotFactory::InitAmmo()
|
||||
bot->SetAmmo(entry);
|
||||
}
|
||||
|
||||
uint32 PlayerbotFactory::CalcMixedGearScore(uint32 gs, uint32 quality) { return gs * PlayerbotAI::GetItemScoreMultiplier(ItemQualities(quality)); }
|
||||
uint32 PlayerbotFactory::CalcMixedGearScore(uint32 gs, uint32 quality)
|
||||
{
|
||||
return gs * PlayerbotAI::GetItemScoreMultiplier(ItemQualities(quality));
|
||||
}
|
||||
|
||||
void PlayerbotFactory::InitMounts()
|
||||
{
|
||||
@@ -2813,7 +2971,8 @@ std::vector<uint32> PlayerbotFactory::GetCurrentGemsCount()
|
||||
Item* pItem2 = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, i);
|
||||
if (pItem2 && !pItem2->IsBroken() && pItem2->HasSocket())
|
||||
{
|
||||
for (uint32 enchant_slot = SOCK_ENCHANTMENT_SLOT; enchant_slot <= PRISMATIC_ENCHANTMENT_SLOT; ++enchant_slot)
|
||||
for (uint32 enchant_slot = SOCK_ENCHANTMENT_SLOT; enchant_slot <= PRISMATIC_ENCHANTMENT_SLOT;
|
||||
++enchant_slot)
|
||||
{
|
||||
if (enchant_slot == BONUS_ENCHANTMENT_SLOT)
|
||||
continue;
|
||||
@@ -3321,10 +3480,10 @@ void PlayerbotFactory::InitInventoryEquip()
|
||||
|
||||
if (proto->Class == ITEM_CLASS_WEAPON && !CanEquipWeapon(proto))
|
||||
continue;
|
||||
|
||||
|
||||
if (proto->Quality != desiredQuality)
|
||||
continue;
|
||||
|
||||
|
||||
if (!CanEquipItem(proto))
|
||||
continue;
|
||||
|
||||
@@ -3771,7 +3930,9 @@ void PlayerbotFactory::ApplyEnchantAndGemsNew(bool destoryOld)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (enchant->requiredSkill && (!bot->HasSkill(enchant->requiredSkill) || (bot->GetSkillValue(enchant->requiredSkill) < enchant->requiredSkillValue)))
|
||||
if (enchant->requiredSkill &&
|
||||
(!bot->HasSkill(enchant->requiredSkill) ||
|
||||
(bot->GetSkillValue(enchant->requiredSkill) < enchant->requiredSkillValue)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -3808,7 +3969,7 @@ void PlayerbotFactory::ApplyEnchantAndGemsNew(bool destoryOld)
|
||||
int32 enchantIdChosen = -1;
|
||||
int32 colorChosen;
|
||||
float bestGemScore = -1;
|
||||
for (uint32 &enchantGem : availableGems)
|
||||
for (uint32& enchantGem : availableGems)
|
||||
{
|
||||
ItemTemplate const* gemTemplate = sObjectMgr->GetItemTemplate(enchantGem);
|
||||
if (!gemTemplate)
|
||||
@@ -3818,7 +3979,7 @@ void PlayerbotFactory::ApplyEnchantAndGemsNew(bool destoryOld)
|
||||
if (!gemProperties)
|
||||
continue;
|
||||
|
||||
if ((socketColor & gemProperties->color) == 0)
|
||||
if ((socketColor & gemProperties->color) == 0 && gemProperties->color == 1) // meta socket
|
||||
continue;
|
||||
|
||||
uint32 enchant_id = gemProperties->spellitemenchantement;
|
||||
@@ -3829,6 +3990,7 @@ void PlayerbotFactory::ApplyEnchantAndGemsNew(bool destoryOld)
|
||||
StatsWeightCalculator calculator(bot);
|
||||
float score = calculator.CalculateEnchant(enchant_id);
|
||||
if (curCount[0] != 0)
|
||||
{
|
||||
// Ensure meta gem activation
|
||||
for (int i = 1; i < curCount.size(); i++)
|
||||
{
|
||||
@@ -3838,6 +4000,9 @@ void PlayerbotFactory::ApplyEnchantAndGemsNew(bool destoryOld)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (socketColor & gemProperties->color)
|
||||
score *= 1.2;
|
||||
if (score > bestGemScore)
|
||||
{
|
||||
enchantIdChosen = enchant_id;
|
||||
|
||||
@@ -122,7 +122,7 @@ public:
|
||||
static void InitTalentsByParsedSpecLink(Player* bot, std::vector<std::vector<uint32>> parsedSpecLink, bool reset);
|
||||
void InitAvailableSpells();
|
||||
void InitClassSpells();
|
||||
void InitEquipment(bool incremental);
|
||||
void InitEquipment(bool incremental, bool second_chance = false);
|
||||
void InitPet();
|
||||
void InitAmmo();
|
||||
static uint32 CalcMixedGearScore(uint32 gs, uint32 quality);
|
||||
|
||||
@@ -233,7 +233,7 @@ bool StatsCollector::SpecialSpellFilter(uint32 spellId) {
|
||||
if (type_ != CollectorType::SPELL_HEAL)
|
||||
stats[STATS_TYPE_CRIT] += 50;
|
||||
return true;
|
||||
case 59620:
|
||||
case 59620: // Berserk
|
||||
if (type_ == CollectorType::MELEE)
|
||||
stats[STATS_TYPE_ATTACK_POWER] += 120;
|
||||
return true;
|
||||
@@ -254,6 +254,10 @@ bool StatsCollector::SpecialSpellFilter(uint32 spellId) {
|
||||
/// Noticing that heroic item can not be triggered, probably a bug to report to AC
|
||||
if (type_ == CollectorType::SPELL_HEAL)
|
||||
return true;
|
||||
break;
|
||||
case 71903: // Shadowmourne
|
||||
stats[STATS_TYPE_STRENGTH] += 200;
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -32,6 +32,15 @@ StatsWeightCalculator::StatsWeightCalculator(Player* player) : player_(player)
|
||||
cls = player->getClass();
|
||||
tab = AiFactory::GetPlayerSpecTab(player);
|
||||
|
||||
if (cls == CLASS_DEATH_KNIGHT && tab == DEATHKNIGHT_TAB_UNHOLY)
|
||||
hitOverflowType_ = CollectorType::SPELL;
|
||||
else if (cls == CLASS_SHAMAN && tab == SHAMAN_TAB_ENHANCEMENT)
|
||||
hitOverflowType_ = CollectorType::SPELL;
|
||||
else if (cls == CLASS_ROGUE)
|
||||
hitOverflowType_ = CollectorType::SPELL;
|
||||
else
|
||||
hitOverflowType_ = type_;
|
||||
|
||||
enable_overflow_penalty_ = true;
|
||||
enable_item_set_bonus_ = true;
|
||||
enable_quality_blend_ = true;
|
||||
@@ -58,6 +67,9 @@ float StatsWeightCalculator::CalculateItem(uint32 itemId)
|
||||
|
||||
collector_->CollectItemStats(proto);
|
||||
|
||||
if (enable_overflow_penalty_)
|
||||
ApplyOverflowPenalty(player_);
|
||||
|
||||
GenerateWeights(player_);
|
||||
for (uint32 i = 0; i < STATS_TYPE_MAX; i++)
|
||||
{
|
||||
@@ -89,6 +101,9 @@ float StatsWeightCalculator::CalculateEnchant(uint32 enchantId)
|
||||
|
||||
collector_->CollectEnchantStats(enchant);
|
||||
|
||||
if (enable_overflow_penalty_)
|
||||
ApplyOverflowPenalty(player_);
|
||||
|
||||
GenerateWeights(player_);
|
||||
for (uint32 i = 0; i < STATS_TYPE_MAX; i++)
|
||||
{
|
||||
@@ -102,9 +117,7 @@ void StatsWeightCalculator::GenerateWeights(Player* player)
|
||||
{
|
||||
GenerateBasicWeights(player);
|
||||
GenerateAdditionalWeights(player);
|
||||
|
||||
if (enable_overflow_penalty_)
|
||||
ApplyOverflowPenalty(player);
|
||||
ApplyWeightFinetune(player);
|
||||
}
|
||||
|
||||
void StatsWeightCalculator::GenerateBasicWeights(Player* player)
|
||||
@@ -219,15 +232,15 @@ void StatsWeightCalculator::GenerateBasicWeights(Player* player)
|
||||
}
|
||||
else if (cls == CLASS_PALADIN && tab == PALADIN_TAB_RETRIBUTION) // retribution
|
||||
{
|
||||
stats_weights_[STATS_TYPE_AGILITY] += 1.1f;
|
||||
stats_weights_[STATS_TYPE_AGILITY] += 1.3f;
|
||||
stats_weights_[STATS_TYPE_STRENGTH] += 2.5f;
|
||||
stats_weights_[STATS_TYPE_INTELLECT] += 0.15f;
|
||||
stats_weights_[STATS_TYPE_ATTACK_POWER] += 1.0f;
|
||||
stats_weights_[STATS_TYPE_SPELL_POWER] += 0.3f;
|
||||
stats_weights_[STATS_TYPE_ARMOR_PENETRATION] += 0.5f;
|
||||
stats_weights_[STATS_TYPE_ARMOR_PENETRATION] += 0.7f;
|
||||
stats_weights_[STATS_TYPE_HIT] += 1.9f;
|
||||
stats_weights_[STATS_TYPE_CRIT] += 1.2f;
|
||||
stats_weights_[STATS_TYPE_HASTE] += 1.3f;
|
||||
stats_weights_[STATS_TYPE_CRIT] += 1.3f;
|
||||
stats_weights_[STATS_TYPE_HASTE] += 1.2f;
|
||||
stats_weights_[STATS_TYPE_EXPERTISE] += 2.0f;
|
||||
stats_weights_[STATS_TYPE_MELEE_DPS] += 7.0f;
|
||||
}
|
||||
@@ -414,12 +427,12 @@ void StatsWeightCalculator::CalculateSocketBonus(Player* player, ItemTemplate co
|
||||
|
||||
void StatsWeightCalculator::CalculateItemTypePenalty(ItemTemplate const* proto)
|
||||
{
|
||||
// penalty for different type armor
|
||||
if (proto->Class == ITEM_CLASS_ARMOR && proto->SubClass >= ITEM_SUBCLASS_ARMOR_CLOTH &&
|
||||
proto->SubClass <= ITEM_SUBCLASS_ARMOR_PLATE && NotBestArmorType(proto->SubClass))
|
||||
{
|
||||
weight_ *= 0.8;
|
||||
}
|
||||
// // penalty for different type armor
|
||||
// if (proto->Class == ITEM_CLASS_ARMOR && proto->SubClass >= ITEM_SUBCLASS_ARMOR_CLOTH &&
|
||||
// proto->SubClass <= ITEM_SUBCLASS_ARMOR_PLATE && NotBestArmorType(proto->SubClass))
|
||||
// {
|
||||
// weight_ *= 1.0;
|
||||
// }
|
||||
// double hand
|
||||
if (proto->Class == ITEM_CLASS_WEAPON)
|
||||
{
|
||||
@@ -444,7 +457,7 @@ void StatsWeightCalculator::CalculateItemTypePenalty(ItemTemplate const* proto)
|
||||
}
|
||||
// spec with double hand
|
||||
// fury without duel wield, arms, bear, retribution, blood dk
|
||||
if (isDoubleHand &&
|
||||
if (!isDoubleHand &&
|
||||
((cls == CLASS_HUNTER && !player_->CanDualWield()) ||
|
||||
(cls == CLASS_WARRIOR && tab == WARRIOR_TAB_FURY && !player_->CanDualWield()) ||
|
||||
(cls == CLASS_WARRIOR && tab == WARRIOR_TAB_ARMS) || (cls == CLASS_DRUID && tab == DRUID_TAB_FERAL) ||
|
||||
@@ -452,13 +465,13 @@ void StatsWeightCalculator::CalculateItemTypePenalty(ItemTemplate const* proto)
|
||||
(cls == CLASS_DEATH_KNIGHT && tab == DEATHKNIGHT_TAB_BLOOD) ||
|
||||
(cls == CLASS_SHAMAN && tab == SHAMAN_TAB_ENHANCEMENT && !player_->CanDualWield())))
|
||||
{
|
||||
weight_ *= 10;
|
||||
weight_ *= 0.1;
|
||||
}
|
||||
// fury with titan's grip
|
||||
if (isDoubleHand && proto->SubClass != ITEM_SUBCLASS_WEAPON_POLEARM &&
|
||||
if ((!isDoubleHand || proto->SubClass == ITEM_SUBCLASS_WEAPON_POLEARM || proto->SubClass == ITEM_SUBCLASS_WEAPON_STAFF) &&
|
||||
(cls == CLASS_WARRIOR && tab == WARRIOR_TAB_FURY && player_->CanTitanGrip()))
|
||||
{
|
||||
weight_ *= 10;
|
||||
weight_ *= 0.1;
|
||||
}
|
||||
}
|
||||
if (proto->Class == ITEM_CLASS_WEAPON)
|
||||
@@ -481,6 +494,9 @@ void StatsWeightCalculator::CalculateItemTypePenalty(ItemTemplate const* proto)
|
||||
{
|
||||
weight_ *= 1.1;
|
||||
}
|
||||
bool slowDelay = proto->Delay > 2500;
|
||||
if (cls == CLASS_SHAMAN && tab == SHAMAN_TAB_ENHANCEMENT && slowDelay)
|
||||
weight_ *= 1.1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -505,37 +521,57 @@ void StatsWeightCalculator::ApplyOverflowPenalty(Player* player)
|
||||
{
|
||||
{
|
||||
float hit_current, hit_overflow;
|
||||
if (type_ == CollectorType::SPELL)
|
||||
float validPoints;
|
||||
// m_modMeleeHitChance = (float)GetTotalAuraModifier(SPELL_AURA_MOD_HIT_CHANCE);
|
||||
// m_modMeleeHitChance += GetRatingBonusValue(CR_HIT_MELEE);
|
||||
if (hitOverflowType_ == CollectorType::SPELL)
|
||||
{
|
||||
hit_current = player->GetRatingBonusValue(CR_HIT_SPELL);
|
||||
hit_current = player->GetTotalAuraModifier(SPELL_AURA_MOD_SPELL_HIT_CHANCE);
|
||||
hit_current += player->GetRatingBonusValue(CR_HIT_SPELL);
|
||||
hit_overflow = SPELL_HIT_OVERFLOW;
|
||||
if (hit_overflow > hit_current)
|
||||
validPoints = (hit_overflow - hit_current) / player->GetRatingMultiplier(CR_HIT_SPELL);
|
||||
else
|
||||
validPoints = 0;
|
||||
}
|
||||
else if (type_ == CollectorType::MELEE)
|
||||
else if (hitOverflowType_ == CollectorType::MELEE)
|
||||
{
|
||||
hit_current = player->GetRatingBonusValue(CR_HIT_MELEE);
|
||||
hit_current = player->GetTotalAuraModifier(SPELL_AURA_MOD_HIT_CHANCE);
|
||||
hit_current += player->GetRatingBonusValue(CR_HIT_MELEE);
|
||||
hit_overflow = MELEE_HIT_OVERFLOW;
|
||||
if (hit_overflow > hit_current)
|
||||
validPoints = (hit_overflow - hit_current) / player->GetRatingMultiplier(CR_HIT_MELEE);
|
||||
else
|
||||
validPoints = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
hit_current = player->GetRatingBonusValue(CR_HIT_RANGED);
|
||||
hit_current = player->GetTotalAuraModifier(SPELL_AURA_MOD_HIT_CHANCE);
|
||||
hit_current += player->GetRatingBonusValue(CR_HIT_RANGED);
|
||||
hit_overflow = RANGED_HIT_OVERFLOW;
|
||||
if (hit_overflow > hit_current)
|
||||
validPoints = (hit_overflow - hit_current) / player->GetRatingMultiplier(CR_HIT_RANGED);
|
||||
else
|
||||
validPoints = 0;
|
||||
}
|
||||
if (hit_current >= hit_overflow)
|
||||
stats_weights_[STATS_TYPE_HIT] = 0.0f;
|
||||
else if (hit_current >= hit_overflow * 0.8)
|
||||
stats_weights_[STATS_TYPE_HIT] /= 1.5;
|
||||
collector_->stats[STATS_TYPE_HIT] = std::min(collector_->stats[STATS_TYPE_HIT], (int)validPoints);
|
||||
}
|
||||
|
||||
{
|
||||
if (type_ == CollectorType::MELEE)
|
||||
{
|
||||
float expertise_current, expertise_overflow;
|
||||
expertise_current = player->GetRatingBonusValue(CR_EXPERTISE);
|
||||
expertise_current = player->GetUInt32Value(PLAYER_EXPERTISE);
|
||||
expertise_current += player->GetRatingBonusValue(CR_EXPERTISE);
|
||||
expertise_overflow = EXPERTISE_OVERFLOW;
|
||||
if (expertise_current >= expertise_overflow)
|
||||
stats_weights_[STATS_TYPE_EXPERTISE] = 0.0f;
|
||||
else if (expertise_current >= expertise_overflow * 0.8)
|
||||
stats_weights_[STATS_TYPE_EXPERTISE] /= 1.5;
|
||||
|
||||
float validPoints;
|
||||
if (expertise_overflow > expertise_current)
|
||||
validPoints = (expertise_overflow - expertise_current) / player->GetRatingMultiplier(CR_EXPERTISE);
|
||||
else
|
||||
validPoints = 0;
|
||||
|
||||
collector_->stats[STATS_TYPE_EXPERTISE] = std::min(collector_->stats[STATS_TYPE_EXPERTISE], (int)validPoints);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -545,10 +581,14 @@ void StatsWeightCalculator::ApplyOverflowPenalty(Player* player)
|
||||
float defense_current, defense_overflow;
|
||||
defense_current = player->GetRatingBonusValue(CR_DEFENSE_SKILL);
|
||||
defense_overflow = DEFENSE_OVERFLOW;
|
||||
if (defense_current >= defense_overflow)
|
||||
stats_weights_[STATS_TYPE_DEFENSE] /= 2;
|
||||
else if (defense_current >= defense_overflow * 0.8)
|
||||
stats_weights_[STATS_TYPE_DEFENSE] /= 1.5;
|
||||
|
||||
float validPoints;
|
||||
if (defense_overflow > defense_current)
|
||||
validPoints = (defense_overflow - defense_current) / player->GetRatingMultiplier(CR_DEFENSE_SKILL);
|
||||
else
|
||||
validPoints = 0;
|
||||
|
||||
collector_->stats[STATS_TYPE_DEFENSE] = std::min(collector_->stats[STATS_TYPE_DEFENSE], (int)validPoints);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -558,10 +598,27 @@ void StatsWeightCalculator::ApplyOverflowPenalty(Player* player)
|
||||
float armor_penetration_current, armor_penetration_overflow;
|
||||
armor_penetration_current = player->GetRatingBonusValue(CR_ARMOR_PENETRATION);
|
||||
armor_penetration_overflow = ARMOR_PENETRATION_OVERFLOW;
|
||||
if (armor_penetration_current >= armor_penetration_overflow)
|
||||
stats_weights_[STATS_TYPE_ARMOR_PENETRATION] = 0.0f;
|
||||
if (armor_penetration_current >= armor_penetration_overflow * 0.8)
|
||||
stats_weights_[STATS_TYPE_ARMOR_PENETRATION] /= 1.5;
|
||||
|
||||
float validPoints;
|
||||
if (armor_penetration_overflow > armor_penetration_current)
|
||||
validPoints = (armor_penetration_overflow - armor_penetration_current) / player->GetRatingMultiplier(CR_ARMOR_PENETRATION);
|
||||
else
|
||||
validPoints = 0;
|
||||
|
||||
collector_->stats[STATS_TYPE_ARMOR_PENETRATION] = std::min(collector_->stats[STATS_TYPE_ARMOR_PENETRATION], (int)validPoints);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void StatsWeightCalculator::ApplyWeightFinetune(Player* player)
|
||||
{
|
||||
{
|
||||
if (type_ == CollectorType::MELEE || type_ == CollectorType::RANGED)
|
||||
{
|
||||
float armor_penetration_current, armor_penetration_overflow;
|
||||
armor_penetration_current = player->GetRatingBonusValue(CR_ARMOR_PENETRATION);
|
||||
if (armor_penetration_current > 50)
|
||||
stats_weights_[STATS_TYPE_ARMOR_PENETRATION] *= 1.2f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
|
||||
enum StatsOverflowThreshold
|
||||
{
|
||||
SPELL_HIT_OVERFLOW = 17,
|
||||
SPELL_HIT_OVERFLOW = 14,
|
||||
MELEE_HIT_OVERFLOW = 8,
|
||||
RANGED_HIT_OVERFLOW = 8,
|
||||
EXPERTISE_OVERFLOW = 26,
|
||||
@@ -48,10 +48,12 @@ private:
|
||||
bool NotBestArmorType(uint32 item_subclass_armor);
|
||||
|
||||
void ApplyOverflowPenalty(Player* player);
|
||||
void ApplyWeightFinetune(Player* player);
|
||||
|
||||
private:
|
||||
Player* player_;
|
||||
CollectorType type_;
|
||||
CollectorType hitOverflowType_;
|
||||
std::unique_ptr<StatsCollector> collector_;
|
||||
uint8 cls;
|
||||
int tab;
|
||||
|
||||
@@ -11,13 +11,13 @@
|
||||
std::map<uint32, SkillLineAbilityEntry const*> ListSpellsAction::skillSpells;
|
||||
std::set<uint32> ListSpellsAction::vendorItems;
|
||||
|
||||
bool CompareSpells(std::pair<uint32, std::string>& s1, std::pair<uint32, std::string>& s2)
|
||||
bool CompareSpells(const std::pair<uint32, std::string>& s1, const std::pair<uint32, std::string>& s2)
|
||||
{
|
||||
SpellInfo const* si1 = sSpellMgr->GetSpellInfo(s1.first);
|
||||
SpellInfo const* si2 = sSpellMgr->GetSpellInfo(s2.first);
|
||||
if (!si1 || !si2)
|
||||
{
|
||||
LOG_ERROR("playerbots", "SpellInfo missing.");
|
||||
LOG_ERROR("playerbots", "SpellInfo missing. {} {}", s1.first, s2.first);
|
||||
return false;
|
||||
}
|
||||
uint32 p1 = si1->SchoolMask * 20000;
|
||||
@@ -54,7 +54,7 @@ bool CompareSpells(std::pair<uint32, std::string>& s1, std::pair<uint32, std::st
|
||||
|
||||
if (p1 == p2)
|
||||
{
|
||||
return strcmp(si1->SpellName[0], si1->SpellName[1]) > 0;
|
||||
return strcmp(si1->SpellName[0], si2->SpellName[0]) > 0;
|
||||
}
|
||||
|
||||
return p1 > p2;
|
||||
@@ -273,7 +273,11 @@ std::vector<std::pair<uint32, std::string>> ListSpellsAction::GetSpellList(std::
|
||||
|
||||
if (out.str().empty())
|
||||
continue;
|
||||
|
||||
|
||||
if (itr->first == 0)
|
||||
{
|
||||
LOG_ERROR("playerbots", "?! {}", itr->first);
|
||||
}
|
||||
spells.push_back(std::pair<uint32, std::string>(itr->first, out.str()));
|
||||
alreadySeenList += spellInfo->SpellName[0];
|
||||
alreadySeenList += ",";
|
||||
|
||||
@@ -41,12 +41,13 @@ DpsHunterStrategy::DpsHunterStrategy(PlayerbotAI* botAI) : GenericHunterStrategy
|
||||
NextAction** DpsHunterStrategy::getDefaultActions()
|
||||
{
|
||||
return NextAction::array(
|
||||
0, new NextAction("kill shot", ACTION_DEFAULT + 0.6f), new NextAction("chimera shot", ACTION_DEFAULT + 0.5f),
|
||||
new NextAction("explosive shot", ACTION_DEFAULT + 0.4f), new NextAction("aimed shot", ACTION_DEFAULT + 0.3f),
|
||||
/*new NextAction("arcane shot", ACTION_DEFAULT + 0.2f),*/ new NextAction("steady shot", ACTION_DEFAULT + 0.1f),
|
||||
0, new NextAction("kill shot", ACTION_DEFAULT + 0.8f), new NextAction("chimera shot", ACTION_DEFAULT + 0.7f),
|
||||
new NextAction("explosive shot", ACTION_DEFAULT + 0.6f), new NextAction("aimed shot", ACTION_DEFAULT + 0.5f),
|
||||
new NextAction("silencing shot", ACTION_DEFAULT + 0.4f),
|
||||
new NextAction("kill command", ACTION_DEFAULT + 0.3f),
|
||||
// new NextAction("arcane shot", ACTION_DEFAULT + 0.2f),
|
||||
new NextAction("steady shot", ACTION_DEFAULT + 0.1f),
|
||||
new NextAction("auto shot", ACTION_DEFAULT), nullptr);
|
||||
// return NextAction::array(0, new NextAction("explosive shot", 11.0f), new NextAction("auto shot", 10.0f), new
|
||||
// NextAction("auto attack", 9.0f), nullptr);
|
||||
}
|
||||
|
||||
void DpsHunterStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
|
||||
@@ -95,7 +95,7 @@ void GenericHunterStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
triggers.push_back(new TriggerNode("enemy too close for auto shot",
|
||||
NextAction::array(0, new NextAction("flee", ACTION_MOVE + 9), nullptr)));
|
||||
triggers.push_back(
|
||||
new TriggerNode("misdirection on main tank",
|
||||
new TriggerNode("low tank threat",
|
||||
NextAction::array(0, new NextAction("misdirection on main tank", ACTION_HIGH + 7), NULL)));
|
||||
triggers.push_back(
|
||||
new TriggerNode("low health", NextAction::array(0, new NextAction("deterrence", ACTION_HIGH + 5), nullptr)));
|
||||
|
||||
@@ -78,6 +78,9 @@ END_SPELL_ACTION()
|
||||
BEGIN_RANGED_SPELL_ACTION(CastKillShotAction, "kill shot")
|
||||
END_SPELL_ACTION()
|
||||
|
||||
BEGIN_RANGED_SPELL_ACTION(CastSilencingShotAction, "silencing shot")
|
||||
END_SPELL_ACTION()
|
||||
|
||||
BEGIN_RANGED_SPELL_ACTION(CastTranquilizingShotAction, "tranquilizing shot")
|
||||
END_SPELL_ACTION()
|
||||
|
||||
@@ -139,6 +142,14 @@ public:
|
||||
std::string const GetTargetName() override { return "pet target"; }
|
||||
};
|
||||
|
||||
class CastKillCommandAction : public CastAuraSpellAction
|
||||
{
|
||||
public:
|
||||
CastKillCommandAction(PlayerbotAI* botAI) : CastAuraSpellAction(botAI, "kill command") {}
|
||||
|
||||
std::string const GetTargetName() override { return "pet target"; }
|
||||
};
|
||||
|
||||
class CastRevivePetAction : public CastBuffSpellAction
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -143,6 +143,7 @@ public:
|
||||
creators["scorpid sting"] = &HunterAiObjectContextInternal::scorpid_sting;
|
||||
creators["hunter's mark"] = &HunterAiObjectContextInternal::hunters_mark;
|
||||
creators["mend pet"] = &HunterAiObjectContextInternal::mend_pet;
|
||||
creators["kill command"] = &HunterAiObjectContextInternal::kill_command;
|
||||
creators["revive pet"] = &HunterAiObjectContextInternal::revive_pet;
|
||||
creators["call pet"] = &HunterAiObjectContextInternal::call_pet;
|
||||
creators["black arrow"] = &HunterAiObjectContextInternal::black_arrow;
|
||||
@@ -171,6 +172,7 @@ public:
|
||||
creators["steady shot"] = &HunterAiObjectContextInternal::steady_shot;
|
||||
creators["kill shot"] = &HunterAiObjectContextInternal::kill_shot;
|
||||
creators["misdirection on main tank"] = &HunterAiObjectContextInternal::misdirection_on_main_tank;
|
||||
creators["silencing shot"] = &HunterAiObjectContextInternal::silencing_shot;
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -196,6 +198,7 @@ private:
|
||||
static Action* scorpid_sting(PlayerbotAI* botAI) { return new CastScorpidStingAction(botAI); }
|
||||
static Action* hunters_mark(PlayerbotAI* botAI) { return new CastHuntersMarkAction(botAI); }
|
||||
static Action* mend_pet(PlayerbotAI* botAI) { return new CastMendPetAction(botAI); }
|
||||
static Action* kill_command(PlayerbotAI* botAI) { return new CastKillCommandAction(botAI); }
|
||||
static Action* revive_pet(PlayerbotAI* botAI) { return new CastRevivePetAction(botAI); }
|
||||
static Action* call_pet(PlayerbotAI* botAI) { return new CastCallPetAction(botAI); }
|
||||
static Action* black_arrow(PlayerbotAI* botAI) { return new CastBlackArrow(botAI); }
|
||||
@@ -217,6 +220,8 @@ private:
|
||||
static Action* steady_shot(PlayerbotAI* ai) { return new CastSteadyShotAction(ai); }
|
||||
static Action* kill_shot(PlayerbotAI* ai) { return new CastKillShotAction(ai); }
|
||||
static Action* misdirection_on_main_tank(PlayerbotAI* ai) { return new CastMisdirectionOnMainTankAction(ai); }
|
||||
static Action* silencing_shot(PlayerbotAI* ai) { return new CastSilencingShotAction(ai); }
|
||||
|
||||
};
|
||||
|
||||
HunterAiObjectContext::HunterAiObjectContext(PlayerbotAI* botAI) : AiObjectContext(botAI)
|
||||
|
||||
@@ -83,18 +83,20 @@ DpsPaladinStrategy::DpsPaladinStrategy(PlayerbotAI* botAI) : GenericPaladinStrat
|
||||
NextAction** DpsPaladinStrategy::getDefaultActions()
|
||||
{
|
||||
return NextAction::array(0,
|
||||
new NextAction("crusader strike", ACTION_DEFAULT + 0.6f),
|
||||
new NextAction("hammer of wrath", ACTION_DEFAULT + 0.5f),
|
||||
new NextAction("hammer of wrath", ACTION_DEFAULT + 0.6f),
|
||||
new NextAction("crusader strike", ACTION_DEFAULT + 0.5f),
|
||||
new NextAction("judgement of wisdom", ACTION_DEFAULT + 0.4f),
|
||||
new NextAction("divine storm", ACTION_DEFAULT + 0.3f),
|
||||
new NextAction("consecration", ACTION_DEFAULT + 0.1f),
|
||||
new NextAction("melee", ACTION_DEFAULT), NULL);
|
||||
new NextAction("melee", ACTION_DEFAULT), nullptr);
|
||||
}
|
||||
|
||||
void DpsPaladinStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
{
|
||||
GenericPaladinStrategy::InitTriggers(triggers);
|
||||
|
||||
// triggers.push_back(new TriggerNode(
|
||||
// "enough mana", NextAction::array(0, new NextAction("consecration", ACTION_DEFAULT + 0.2f), nullptr)));
|
||||
triggers.push_back(
|
||||
new TriggerNode("art of war", NextAction::array(0, new NextAction("exorcism", ACTION_DEFAULT + 0.2f), nullptr)));
|
||||
triggers.push_back(
|
||||
|
||||
@@ -83,7 +83,7 @@ void AssassinationRogueStrategy::InitTriggers(std::vector<TriggerNode*>& trigger
|
||||
new TriggerNode("medium aoe", NextAction::array(0, new NextAction("fan of knives", ACTION_NORMAL + 5), NULL)));
|
||||
|
||||
triggers.push_back(new TriggerNode(
|
||||
"tricks of the trade on main tank",
|
||||
"low tank threat",
|
||||
NextAction::array(0, new NextAction("tricks of the trade on main tank", ACTION_HIGH + 7), NULL)));
|
||||
|
||||
triggers.push_back(new TriggerNode(
|
||||
|
||||
@@ -141,7 +141,7 @@ void DpsRogueStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||
NextAction::array(0, new NextAction("expose armor", ACTION_HIGH + 3), nullptr)));
|
||||
|
||||
triggers.push_back(new TriggerNode(
|
||||
"tricks of the trade on main tank",
|
||||
"low tank threat",
|
||||
NextAction::array(0, new NextAction("tricks of the trade on main tank", ACTION_HIGH + 7), nullptr)));
|
||||
}
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "Playerbots.h"
|
||||
#include "SharedDefines.h"
|
||||
#include "TemporarySummon.h"
|
||||
#include "ThreatMgr.h"
|
||||
#include "Timer.h"
|
||||
|
||||
bool LowManaTrigger::IsActive()
|
||||
@@ -185,6 +186,22 @@ bool MyAttackerCountTrigger::IsActive()
|
||||
return AI_VALUE2(bool, "combat", "self target") && AI_VALUE(uint8, "my attacker count") >= amount;
|
||||
}
|
||||
|
||||
bool LowTankThreatTrigger::IsActive()
|
||||
{
|
||||
Unit* mt = AI_VALUE(Unit*, "main tank");
|
||||
if (!mt)
|
||||
return false;
|
||||
|
||||
Unit* current_target = AI_VALUE(Unit*, "current target");
|
||||
if (!current_target)
|
||||
return false;
|
||||
|
||||
ThreatMgr& mgr = current_target->GetThreatMgr();
|
||||
float threat = mgr.GetThreat(bot);
|
||||
float tankThreat = mgr.GetThreat(mt);
|
||||
return tankThreat == 0.0f || threat > tankThreat * 0.5f;
|
||||
}
|
||||
|
||||
bool AoeTrigger::IsActive()
|
||||
{
|
||||
Unit* current_target = AI_VALUE(Unit*, "current target");
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
#include "HealthTriggers.h"
|
||||
#include "RangeTriggers.h"
|
||||
#include "Trigger.h"
|
||||
|
||||
class PlayerbotAI;
|
||||
class Unit;
|
||||
@@ -248,6 +249,13 @@ public:
|
||||
MediumThreatTrigger(PlayerbotAI* botAI) : MyAttackerCountTrigger(botAI, 2) {}
|
||||
};
|
||||
|
||||
class LowTankThreatTrigger : public Trigger
|
||||
{
|
||||
public:
|
||||
LowTankThreatTrigger(PlayerbotAI* botAI) : Trigger(botAI, "low tank threat") {}
|
||||
bool IsActive() override;
|
||||
};
|
||||
|
||||
class AoeTrigger : public AttackerCountTrigger
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -102,6 +102,7 @@ public:
|
||||
creators["combo points not full and high energy"] = &TriggerContext::ComboPointsNotFullAndHighEnergy;
|
||||
|
||||
creators["medium threat"] = &TriggerContext::MediumThreat;
|
||||
creators["low tank threat"] = &TriggerContext::low_tank_threat;
|
||||
|
||||
creators["dead"] = &TriggerContext::Dead;
|
||||
creators["corpse near"] = &TriggerContext::corpse_near;
|
||||
@@ -321,6 +322,8 @@ private:
|
||||
static Trigger* ComboPointsNotFull(PlayerbotAI* botAI) { return new ComboPointsNotFullTrigger(botAI); }
|
||||
static Trigger* ComboPointsNotFullAndHighEnergy(PlayerbotAI* botAI) { return new TwoTriggers(botAI, "combo points not full", "high energy available"); }
|
||||
static Trigger* MediumThreat(PlayerbotAI* botAI) { return new MediumThreatTrigger(botAI); }
|
||||
static Trigger* low_tank_threat(PlayerbotAI* botAI) { return new LowTankThreatTrigger(botAI); }
|
||||
// static Trigger* MediumThreat(PlayerbotAI* botAI) { return new MediumThreatTrigger(botAI); }
|
||||
static Trigger* Dead(PlayerbotAI* botAI) { return new DeadTrigger(botAI); }
|
||||
static Trigger* corpse_near(PlayerbotAI* botAI) { return new CorpseNearTrigger(botAI); }
|
||||
static Trigger* PartyMemberDead(PlayerbotAI* botAI) { return new PartyMemberDeadTrigger(botAI); }
|
||||
|
||||
@@ -166,7 +166,7 @@ ItemUsage ItemUsageValue::QueryItemUsageForEquip(ItemTemplate const* itemProto)
|
||||
return ITEM_USAGE_NONE;
|
||||
|
||||
uint16 dest;
|
||||
InventoryResult result = botAI->CanEquipItem(NULL_SLOT, dest, pItem, true, false);
|
||||
InventoryResult result = botAI->CanEquipItem(NULL_SLOT, dest, pItem, true, true);
|
||||
pItem->RemoveFromUpdateQueueOf(bot);
|
||||
delete pItem;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user