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; }