diff --git a/conf/playerbots.conf.dist b/conf/playerbots.conf.dist index 00125556..db894044 100644 --- a/conf/playerbots.conf.dist +++ b/conf/playerbots.conf.dist @@ -1552,41 +1552,12 @@ AiPlayerbot.PremadeSpecLink.11.6.80 = 05320021--230033312031500531353013251 # Applies a permanent buff to all bots simulating effects of spells, flasks, food, runes, etc. # Requires sending the command "nc +worldbuff" in chat to a bot (or a group of bots) to enable -# Numbers after the equal signs are spell IDs and may be customized -# See Randombots Default Talent Specs for more info on each spec; they are listed in that section by the names in the parentheticals (e.g., arms pve, fury pve) +# Each entry in the matrix should be formatted as follows: Entry:FactionID,ClassID,SpecID,MinimumLevel,MaximumLevel:SpellID1,SpellID2,etc.; +# Use 0 for any field to make it agnostic (e.g., 0 for FactionID means the entry will apply buffs to either faction) +# The default entries create a cross-faction list of level 80 buffs for each implemented pve spec from the "Premade Specs" section +# The default entries may be deleted or modified, and new custom entries may be added -AiPlayerbot.WorldBuff.0.1.0.80.80 = 53760,57358 #WARRIOR ARMS (arms pve) -AiPlayerbot.WorldBuff.0.1.1.80.80 = 53760,57358 #WARRIOR FURY (fury pve) -AiPlayerbot.WorldBuff.0.1.2.80.80 = 53758,57356 #WARRIOR PROTECTION (prot pve) -AiPlayerbot.WorldBuff.0.2.0.80.80 = 60347,53749,57332 #PALADIN HOLY (holy pve) -AiPlayerbot.WorldBuff.0.2.1.80.80 = 53758,57356 #PALADIN PROTECTION (prot pve) -AiPlayerbot.WorldBuff.0.2.2.80.80 = 53760,57371 #PALADIN RETRIBUTION (ret pve) -AiPlayerbot.WorldBuff.0.3.0.80.80 = 53760,57325 #HUNTER BEAST (bm pve) -AiPlayerbot.WorldBuff.0.3.1.80.80 = 53760,57358 #HUNTER MARKSMANSHIP (mm pve) -AiPlayerbot.WorldBuff.0.3.2.80.80 = 53760,57367 #HUNTER SURVIVAL (surv pve) -AiPlayerbot.WorldBuff.0.4.0.80.80 = 53760,57325 #ROGUE ASSASINATION (as pve) -AiPlayerbot.WorldBuff.0.4.1.80.80 = 53760,57358 #ROGUE COMBAT (combat pve) -AiPlayerbot.WorldBuff.0.4.2.80.80 = 53760,57367 #ROGUE SUBTLETY (subtlety pve) -AiPlayerbot.WorldBuff.0.5.0.80.80 = 53755,57327 #PRIEST DISCIPLINE (disc pve) -AiPlayerbot.WorldBuff.0.5.1.80.80 = 53755,57327 #PRIEST HOLY (holy pve) -AiPlayerbot.WorldBuff.0.5.2.80.80 = 53755,57327 #PRIEST SHADOW (shadow pve) -AiPlayerbot.WorldBuff.0.6.0.80.80 = 53758,57356 #DEATH KNIGHT BLOOD (blood pve) -AiPlayerbot.WorldBuff.0.6.1.80.80 = 53760,57358 #DEATH KNIGHT FROST (frost pve) -AiPlayerbot.WorldBuff.0.6.2.80.80 = 53760,57358 #DEATH KNIGHT UNHOLY (unholy pve) -AiPlayerbot.WorldBuff.0.6.3.80.80 = 53760,57371 #DEATH KNIGHT BLOOD DPS (double aura blood pve) -AiPlayerbot.WorldBuff.0.7.0.80.80 = 53755,57327 #SHAMAN ELEMENTAL (ele pve) -AiPlayerbot.WorldBuff.0.7.1.80.80 = 53760,57325 #SHAMAN ENHANCEMENT (enh pve) -AiPlayerbot.WorldBuff.0.7.2.80.80 = 53755,57327 #SHAMAN RESTORATION (resto pve) -AiPlayerbot.WorldBuff.0.8.0.80.80 = 53755,57327 #MAGE ARCANE (arcane pve) -AiPlayerbot.WorldBuff.0.8.1.80.80 = 53755,57327 #MAGE FIRE (fire pve) -AiPlayerbot.WorldBuff.0.8.2.80.80 = 53755,57327 #MAGE FROST (frost pve) -AiPlayerbot.WorldBuff.0.9.0.80.80 = 53755,57327 #WARLOCK AFFLICTION (affli pve) -AiPlayerbot.WorldBuff.0.9.1.80.80 = 53755,57327 #WARLOCK DEMONOLOGY (demo pve) -AiPlayerbot.WorldBuff.0.9.2.80.80 = 53755,57327 #WARLOCK DESTRUCTION (destro pve) -AiPlayerbot.WorldBuff.0.11.0.80.80 = 53755,57327 #DRUID BALANCE (balance pve) -AiPlayerbot.WorldBuff.0.11.1.80.80 = 53749,53763,57367 #DRUID FERAL BEAR (bear pve) -AiPlayerbot.WorldBuff.0.11.2.80.80 = 54212,57334 #DRUID RESTORATION (resto pve) -AiPlayerbot.WorldBuff.0.11.3.80.80 = 53760,57358 #DRUID FERAL CAT (cat pve) +AiPlayerbot.WorldBuffMatrix = # WARRIOR ARMS 1:0,1,0,80,80:53760,57358; # WARRIOR FURY 2:0,1,1,80,80:53760,57358; # WARRIOR PROTECTION 3:0,1,2,80,80:53758,57356; # PALADIN HOLY 4:0,2,0,80,80:53749,57332,60347; # PALADIN PROTECTION 5:0,2,1,80,80:53758,57356; # PALADIN RETRIBUTION 6:0,2,2,80,80:53760,57371; # HUNTER BEAST 7:0,3,0,80,80:53760,57325; # HUNTER MARKSMANSHIP 8:0,3,1,80,80:53760,57358; # HUNTER SURVIVAL 9:0,3,2,80,80:53760,57367; # ROGUE ASSASSINATION 10:0,4,0,80,80:53760,57325; # ROGUE COMBAT 11:0,4,1,80,80:53760,57358; # ROGUE SUBTLETY 12:0,4,2,80,80:53760,57367; # PRIEST DISCIPLINE 13:0,5,0,80,80:53755,57327; # PRIEST HOLY 14:0,5,1,80,80:53755,57327; # PRIEST SHADOW 15:0,5,2,80,80:53755,57327; # DEATH KNIGHT BLOOD 16:0,6,0,80,80:53758,57356; # DEATH KNIGHT FROST 17:0,6,1,80,80:53760,57358; # DEATH KNIGHT UNHOLY 18:0,6,2,80,80:53760,57358; # DEATH KNIGHT BLOOD DPS 19:0,6,3,80,80:53760,57371; # SHAMAN ELEMENTAL 20:0,7,0,80,80:53755,57327; # SHAMAN ENHANCEMENT 21:0,7,1,80,80:53760,57325; # SHAMAN RESTORATION 22:0,7,2,80,80:53755,57327; # MAGE ARCANE 23:0,8,0,80,80:53755,57327; # MAGE FIRE 24:0,8,1,80,80:53755,57327; # MAGE FROST 25:0,8,2,80,80:53755,57327; # WARLOCK AFFLICTION 26:0,9,0,80,80:53755,57327; # WARLOCK DEMONOLOGY 27:0,9,1,80,80:53755,57327; # WARLOCK DESTRUCTION 28:0,9,2,80,80:53755,57327; # DRUID BALANCE 29:0,11,0,80,80:53755,57327; # DRUID FERAL BEAR 30:0,11,1,80,80:53749,53763,57367; # DRUID RESTORATION 31:0,11,2,80,80:54212,57334; # DRUID FERAL CAT 32:0,11,3,80,80:53760,57358 # # diff --git a/src/PlayerbotAIConfig.cpp b/src/PlayerbotAIConfig.cpp index 86bf734b..8c32f371 100644 --- a/src/PlayerbotAIConfig.cpp +++ b/src/PlayerbotAIConfig.cpp @@ -471,25 +471,8 @@ bool PlayerbotAIConfig::Initialize() tradeActionExcludedPrefixes); worldBuffs.clear(); - - LOG_INFO("playerbots", "Loading Worldbuff..."); - for (uint32 factionId = 0; factionId < 3; factionId++) - { - for (uint32 classId = 0; classId < MAX_CLASSES; classId++) - { - for (uint32 specId = 0; specId <= MAX_WORLDBUFF_SPECNO; specId++) - { - for (uint32 minLevel = 0; minLevel <= randomBotMaxLevel; minLevel++) - { - for (uint32 maxLevel = minLevel; maxLevel <= randomBotMaxLevel; maxLevel++) - { - loadWorldBuff(factionId, classId, specId, minLevel, maxLevel); - } - loadWorldBuff(factionId, classId, specId, minLevel, 0); - } - } - } - } + loadWorldBuff(); + LOG_INFO("playerbots", "Loading World Buff Feature..."); randomBotAccountPrefix = sConfigMgr->GetOption("AiPlayerbot.RandomBotAccountPrefix", "rndbot"); randomBotAccountCount = sConfigMgr->GetOption("AiPlayerbot.RandomBotAccountCount", 0); @@ -742,88 +725,62 @@ void PlayerbotAIConfig::log(std::string const fileName, char const* str, ...) fflush(stdout); } -void PlayerbotAIConfig::loadWorldBuff(uint32 factionId1, uint32 classId1, uint32 specId1, uint32 minLevel1, uint32 maxLevel1) +void PlayerbotAIConfig::loadWorldBuff() { - std::vector buffs; + std::string matrix = sConfigMgr->GetOption("AiPlayerbot.WorldBuffMatrix", "", true); + if (matrix.empty()) + return; - std::ostringstream os; - os << "AiPlayerbot.WorldBuff." << factionId1 << "." << classId1 << "." << specId1 << "." << minLevel1 << "." << maxLevel1; + std::istringstream entryStream(matrix); + std::string entry; - LoadList>(sConfigMgr->GetOption(os.str().c_str(), "", false), buffs); - - for (auto buff : buffs) + while (std::getline(entryStream, entry, ';')) { - worldBuff wb = {buff, factionId1, classId1, specId1, minLevel1, maxLevel1}; - worldBuffs.push_back(wb); - } - if (maxLevel1 == 0) - { - std::ostringstream os; - os << "AiPlayerbot.WorldBuff." << factionId1 << "." << classId1 << "." << specId1 << "." << minLevel1; + entry.erase(0, entry.find_first_not_of(" \t\r\n")); + entry.erase(entry.find_last_not_of(" \t\r\n") + 1); - LoadList>(sConfigMgr->GetOption(os.str().c_str(), "", false), buffs); + size_t firstColon = entry.find(':'); + size_t secondColon = entry.find(':', firstColon + 1); - for (auto buff : buffs) + if (firstColon == std::string::npos || secondColon == std::string::npos) { - worldBuff wb = {buff, factionId1, classId1, specId1, minLevel1, maxLevel1}; - worldBuffs.push_back(wb); + LOG_ERROR("playerbots", "Malformed entry: [{}]", entry); + continue; } - } - if (maxLevel1 == 0 && minLevel1 == 0) - { - std::ostringstream os; - os << "AiPlayerbot.WorldBuff." << factionId1 << "." << factionId1 << "." << classId1 << "." << specId1; + std::string metaPart = entry.substr(firstColon + 1, secondColon - firstColon - 1); + std::string spellPart = entry.substr(secondColon + 1); - LoadList>(sConfigMgr->GetOption(os.str().c_str(), "", false), buffs); - - for (auto buff : buffs) + std::vector ids; + std::istringstream metaStream(metaPart); + std::string token; + while (std::getline(metaStream, token, ',')) { - worldBuff wb = {buff, factionId1, classId1, specId1, minLevel1, maxLevel1}; - worldBuffs.push_back(wb); + try { + ids.push_back(static_cast(std::stoi(token))); + } catch (...) { + LOG_ERROR("playerbots", "Invalid meta token in [{}]", entry); + break; + } } - } - if (maxLevel1 == 0 && minLevel1 == 0 && specId1 == 0) - { - std::ostringstream os; - os << "AiPlayerbot.WorldBuff." << factionId1 << "." << factionId1 << "." << classId1; - - LoadList>(sConfigMgr->GetOption(os.str().c_str(), "", false), buffs); - - for (auto buff : buffs) + if (ids.size() != 5) { - worldBuff wb = {buff, factionId1, classId1, specId1, minLevel1, maxLevel1}; - worldBuffs.push_back(wb); + LOG_ERROR("playerbots", "Entry [{}] has incomplete meta block", entry); + continue; } - } - if (classId1 == 0 && maxLevel1 == 0 && minLevel1 == 0 && specId1 == 0) - { - std::ostringstream os; - os << "AiPlayerbot.WorldBuff." << factionId1; - - LoadList>(sConfigMgr->GetOption(os.str().c_str(), "", false), buffs); - - for (auto buff : buffs) + std::istringstream spellStream(spellPart); + while (std::getline(spellStream, token, ',')) { - worldBuff wb = {buff, factionId1, classId1, specId1, minLevel1, maxLevel1}; - worldBuffs.push_back(wb); - } - } - - if (factionId1 == 0 && classId1 == 0 && maxLevel1 == 0 && minLevel1 == 0 && specId1 == 0) - { - std::ostringstream os; - os << "AiPlayerbot.WorldBuff"; - - LoadList>(sConfigMgr->GetOption(os.str().c_str(), "", false), buffs); - - for (auto buff : buffs) - { - worldBuff wb = {buff, factionId1, classId1, specId1, minLevel1, maxLevel1}; - worldBuffs.push_back(wb); + try { + uint32 spellId = static_cast(std::stoi(token)); + worldBuff wb = { spellId, ids[0], ids[1], ids[2], ids[3], ids[4] }; + worldBuffs.push_back(wb); + } catch (...) { + LOG_ERROR("playerbots", "Invalid spell ID in [{}]", entry); + } } } } diff --git a/src/PlayerbotAIConfig.h b/src/PlayerbotAIConfig.h index cfdf70ea..a5f1f3ea 100644 --- a/src/PlayerbotAIConfig.h +++ b/src/PlayerbotAIConfig.h @@ -57,7 +57,6 @@ enum NewRpgStatus : int }; #define MAX_SPECNO 20 -#define MAX_WORLDBUFF_SPECNO 3 class PlayerbotAIConfig { @@ -288,11 +287,11 @@ public: struct worldBuff { uint32 spellId; - uint32 factionId = 0; - uint32 classId = 0; - uint32 specId = 0; - uint32 minLevel = 0; - uint32 maxLevel = 0; + uint32 factionId; + uint32 classId; + uint32 specId; + uint32 minLevel; + uint32 maxLevel; }; std::vector worldBuffs; @@ -399,7 +398,8 @@ public: } void log(std::string const fileName, const char* str, ...); - void loadWorldBuff(uint32 factionId, uint32 classId, uint32 specId, uint32 minLevel, uint32 maxLevel); + void loadWorldBuff(); + static std::vector> ParseTempTalentsOrder(uint32 cls, std::string temp_talents_order); static std::vector> ParseTempPetTalentsOrder(uint32 spec, std::string temp_talents_order); }; diff --git a/src/strategy/actions/WorldBuffAction.cpp b/src/strategy/actions/WorldBuffAction.cpp index 6e0c0175..0659ee0c 100644 --- a/src/strategy/actions/WorldBuffAction.cpp +++ b/src/strategy/actions/WorldBuffAction.cpp @@ -4,7 +4,6 @@ */ #include "WorldBuffAction.h" - #include "AiFactory.h" #include "Event.h" #include "Playerbots.h" @@ -13,11 +12,12 @@ bool WorldBuffAction::Execute(Event event) { std::string const text = event.getParam(); - for (auto& wb : NeedWorldBuffs(bot)) + std::vector buffs = NeedWorldBuffs(bot); // Get matching buffs + + for (auto& wb : buffs) { bot->AddAura(wb, bot); } - return false; } @@ -70,7 +70,6 @@ std::vector WorldBuffAction::NeedWorldBuffs(Unit* unit) // If tank, effectiveSpec remains unchanged } - for (auto const& wb : sPlayerbotAIConfig->worldBuffs) { // Faction check