From 3a4e8e729b4b9426bbc4d00c1136aa167654107f Mon Sep 17 00:00:00 2001 From: avirar Date: Thu, 27 Feb 2025 01:55:16 +1100 Subject: [PATCH] Allow bots to enter all TBC dungeons (rep/keys/quest attunements) (#1014) Update will allow any adequately leveled bot to queue for all random and specific TBC dungeons. Normal and Heroic modes that require attunement or keys will be accessible by bots. InitReputation: Level 70+ Bots will receive Honored rank for all the required factions needed for Heroic keys to function. I've placed the calls to InitRep before InitMounts, I feel we can do something with that in the future. Randomised rep values might allow bots to obtain other mounts? InitKeyring: All bots receive all non-rep required keys (Shattered Halls Key, Shadow Labyrinth Key, Key to the Arcatraz), and bots with the required rep (Honored) get the other keys. InitAttunementQuests: Level 60+ bots complete the Caverns of Time and Magister's Terrace attunement quests. Updated acore_playerbots SQL; playerbots_dungeon_suggestion_definition; max_level increased to 73 (from 70) for all TBC Heroics and Magister's Terrace nomal. Allows more level appropriate bots to join. --- .../updates/db_playerbots/2025_02_24_00.sql | 4 + src/factory/PlayerbotFactory.cpp | 178 ++++++++++++++++++ src/factory/PlayerbotFactory.h | 3 + src/strategy/actions/TrainerAction.cpp | 8 +- 4 files changed, 191 insertions(+), 2 deletions(-) create mode 100644 data/sql/playerbots/updates/db_playerbots/2025_02_24_00.sql diff --git a/data/sql/playerbots/updates/db_playerbots/2025_02_24_00.sql b/data/sql/playerbots/updates/db_playerbots/2025_02_24_00.sql new file mode 100644 index 00000000..3aecf2a5 --- /dev/null +++ b/data/sql/playerbots/updates/db_playerbots/2025_02_24_00.sql @@ -0,0 +1,4 @@ +-- Update max_level for TBC Heroic dungeons in `playerbots_dungeon_suggestion_definition` +UPDATE `playerbots_dungeon_suggestion_definition` +SET `max_level` = 73 +WHERE `id` IN (40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56); diff --git a/src/factory/PlayerbotFactory.cpp b/src/factory/PlayerbotFactory.cpp index 095a0266..73fc187f 100644 --- a/src/factory/PlayerbotFactory.cpp +++ b/src/factory/PlayerbotFactory.cpp @@ -33,6 +33,7 @@ #include "Playerbots.h" #include "RandomItemMgr.h" #include "RandomPlayerbotFactory.h" +#include "ReputationMgr.h" #include "SharedDefines.h" #include "SpellAuraDefines.h" #include "StatsWeightCalculator.h" @@ -251,6 +252,14 @@ void PlayerbotFactory::Randomize(bool incremental) { pmo = sPerformanceMonitor->start(PERF_MON_RNDBOT, "PlayerbotFactory_Quests"); InitInstanceQuests(); + InitAttunementQuests(); + if (pmo) + pmo->finish(); + } + else + { + pmo = sPerformanceMonitor->start(PERF_MON_RNDBOT, "PlayerbotFactory_Quests"); + InitAttunementQuests(); if (pmo) pmo->finish(); } @@ -296,6 +305,12 @@ void PlayerbotFactory::Randomize(bool incremental) if (pmo) pmo->finish(); + pmo = sPerformanceMonitor->start(PERF_MON_RNDBOT, "PlayerbotFactory_Reputation"); + LOG_DEBUG("playerbots", "Initializing reputation..."); + InitReputation(); + if (pmo) + pmo->finish(); + pmo = sPerformanceMonitor->start(PERF_MON_RNDBOT, "PlayerbotFactory_Mounts"); LOG_DEBUG("playerbots", "Initializing mounts..."); InitMounts(); @@ -360,6 +375,12 @@ void PlayerbotFactory::Randomize(bool incremental) if (pmo) pmo->finish(); + pmo = sPerformanceMonitor->start(PERF_MON_RNDBOT, "PlayerbotFactory_Keys"); + LOG_DEBUG("playerbots", "Initializing keys..."); + InitKeyring(); + if (pmo) + pmo->finish(); + pmo = sPerformanceMonitor->start(PERF_MON_RNDBOT, "PlayerbotFactory_EqSets"); LOG_DEBUG("playerbots", "Initializing second equipment set..."); // InitSecondEquipmentSet(); @@ -449,6 +470,7 @@ void PlayerbotFactory::Refresh() // { // InitEquipment(true); // } + InitAttunementQuests(); ClearInventory(); InitAmmo(); InitFood(); @@ -463,7 +485,9 @@ void PlayerbotFactory::Refresh() InitClassSpells(); InitAvailableSpells(); InitSkills(); + InitReputation(); InitMounts(); + InitKeyring(); if (bot->GetLevel() >= sPlayerbotAIConfig->minEnchantingBotLevel) { ApplyEnchantAndGemsNew(); @@ -4223,3 +4247,157 @@ void PlayerbotFactory::IterateItemsInBank(IterateItemsVisitor* visitor) } } } + +void PlayerbotFactory::InitKeyring() +{ + if (!bot) + return; + + ReputationMgr& repMgr = bot->GetReputationMgr(); // Reference, use . instead of -> + + std::vector> keysToCheck; + + // Reputation-based Keys (Honored requirement) + if (repMgr.GetRank(sFactionStore.LookupEntry(1011)) >= REP_HONORED && !bot->HasItemCount(30633, 1)) + keysToCheck.emplace_back(1011, 30633); // Lower City - Auchenai Key + if (repMgr.GetRank(sFactionStore.LookupEntry(942)) >= REP_HONORED && !bot->HasItemCount(30623, 1)) + keysToCheck.emplace_back(942, 30623); // Cenarion Expedition - Reservoir Key + if (repMgr.GetRank(sFactionStore.LookupEntry(989)) >= REP_HONORED && !bot->HasItemCount(30635, 1)) + keysToCheck.emplace_back(989, 30635); // Keepers of Time - Key of Time + if (repMgr.GetRank(sFactionStore.LookupEntry(935)) >= REP_HONORED && !bot->HasItemCount(30634, 1)) + keysToCheck.emplace_back(935, 30634); // The Sha'tar - Warpforged Key + + // Faction-specific Keys (Honored requirement) + if (bot->GetTeamId() == TEAM_ALLIANCE && repMgr.GetRank(sFactionStore.LookupEntry(946)) >= REP_HONORED && !bot->HasItemCount(30622, 1)) + keysToCheck.emplace_back(946, 30622); // Honor Hold - Flamewrought Key (Alliance) + if (bot->GetTeamId() == TEAM_HORDE && repMgr.GetRank(sFactionStore.LookupEntry(947)) >= REP_HONORED && !bot->HasItemCount(30637, 1)) + keysToCheck.emplace_back(947, 30637); // Thrallmar - Flamewrought Key (Horde) + + // Keys that do not require Rep or Faction + // Shattered Halls Key, Shadow Labyrinth Key, Key to the Arcatraz, Master's Key + std::vector nonRepKeys = {28395, 27991, 31084, 24490}; + for (uint32 keyId : nonRepKeys) + { + if (!bot->HasItemCount(keyId, 1)) + keysToCheck.emplace_back(0, keyId); + } + + // Assign keys + for (auto const& keyPair : keysToCheck) + { + uint32 keyId = keyPair.second; + if (keyId > 0) + { + if (Item* newItem = StoreNewItemInInventorySlot(bot,keyId, 1)) + { + newItem->AddToUpdateQueueOf(bot); + } + } + } +} +void PlayerbotFactory::InitReputation() +{ + if (!bot) + return; + + if (bot->GetLevel() < 70) + return; // Only apply for level 70+ bots + + ReputationMgr& repMgr = bot->GetReputationMgr(); + + // List of factions that require Honored reputation for heroic keys + std::vector factions = { + 1011, // Lower City + 942, // Cenarion Expedition + 989, // Keepers of Time + 935 // The Sha'tar + }; + + // Add faction-specific reputation + if (bot->GetTeamId() == TEAM_ALLIANCE) + factions.push_back(946); // Honor Hold (Alliance) + else if (bot->GetTeamId() == TEAM_HORDE) + factions.push_back(947); // Thrallmar (Horde) + + // Set reputation to Honored for each required faction + for (uint32 factionId : factions) + { + FactionEntry const* factionEntry = sFactionStore.LookupEntry(factionId); + if (!factionEntry) + continue; + + // Bottom of Honored rank + int32 honoredRep = ReputationMgr::ReputationRankToStanding(static_cast(REP_HONORED - 1)) + 1; + + // Get bot's current reputation with this faction + int32 currentRep = repMgr.GetReputation(factionEntry); + + // Only set reputation if it's lower than the required Honored value + if (currentRep < honoredRep) + { + repMgr.SetReputation(factionEntry, honoredRep); + } + } +} + +void PlayerbotFactory::InitAttunementQuests() +{ + uint32 level = bot->GetLevel(); + if (level < 55) + return; // Only apply for level 55+ bots + + uint32 currentXP = bot->GetUInt32Value(PLAYER_XP); + + // List of attunement quest IDs + std::list attunementQuestsTBC = { + // Caverns of Time - Part 1 + 10279, // To The Master's Lair + 10277, // The Caverns of Time + + // Caverns of Time - Part 2 (Escape from Durnholde Keep) + 10282, // Old Hillsbrad + 10283, // Taretha's Diversion + 10284, // Escape from Durnholde + 10285, // Return to Andormu + + // Caverns of Time - Part 2 (The Black Morass) + 10296, // The Black Morass + 10297, // The Opening of the Dark Portal + 10298, // Hero of the Brood + + // Magister's Terrace Attunement + 11481, // Crisis at the Sunwell + 11482, // Duty Calls + 11488, // Magisters' Terrace + 11490, // The Scryer's Scryer + 11492 // Hard to Kill + }; + + // Complete all level-appropriate attunement quests for the bot + if (level >= 60) + { + std::list questsToComplete; + + // Check each quest status before adding to the completion list + for (uint32 questId : attunementQuestsTBC) + { + QuestStatus questStatus = bot->GetQuestStatus(questId); + + if (questStatus == QUEST_STATUS_NONE) // Quest not yet taken/completed + { + questsToComplete.push_back(questId); + } + } + + // Only complete quests that haven't been finished yet + if (!questsToComplete.empty()) + { + InitQuests(questsToComplete); + } + } + + // Reset XP so bot's level remains unchanged + bot->GiveLevel(level); + bot->SetUInt32Value(PLAYER_XP, currentXP); +} + diff --git a/src/factory/PlayerbotFactory.h b/src/factory/PlayerbotFactory.h index c17d2ab4..96fa9cac 100644 --- a/src/factory/PlayerbotFactory.h +++ b/src/factory/PlayerbotFactory.h @@ -137,6 +137,9 @@ public: void ApplyEnchantAndGemsNew(bool destoryOld = true); void InitInstanceQuests(); void UnbindInstance(); + void InitKeyring(); + void InitReputation(); + void InitAttunementQuests(); private: void Prepare(); diff --git a/src/strategy/actions/TrainerAction.cpp b/src/strategy/actions/TrainerAction.cpp index 5124ca72..d25a7248 100644 --- a/src/strategy/actions/TrainerAction.cpp +++ b/src/strategy/actions/TrainerAction.cpp @@ -162,8 +162,10 @@ bool MaintenanceAction::Execute(Event event) botAI->TellError("maintenance command is not allowed, please check the configuration."); return false; } + botAI->TellMaster("I'm maintaining"); PlayerbotFactory factory(bot, bot->GetLevel()); + factory.InitAttunementQuests(); factory.InitBags(false); factory.InitAmmo(); factory.InitFood(); @@ -174,15 +176,17 @@ bool MaintenanceAction::Execute(Event event) factory.InitClassSpells(); factory.InitAvailableSpells(); factory.InitSkills(); + factory.InitReputation(); factory.InitSpecialSpells(); factory.InitMounts(); factory.InitGlyphs(true); + factory.InitKeyring(); if (bot->GetLevel() >= sPlayerbotAIConfig->minEnchantingBotLevel) - { factory.ApplyEnchantAndGemsNew(); - } + bot->DurabilityRepairAll(false, 1.0f, false); bot->SendTalentsInfoData(false); + return true; }