diff --git a/conf/playerbots.conf.dist b/conf/playerbots.conf.dist index 7b624fe8..39a3a044 100644 --- a/conf/playerbots.conf.dist +++ b/conf/playerbots.conf.dist @@ -667,7 +667,7 @@ AiPlayerbot.IncrementalGearInit = 1 AiPlayerbot.MinEnchantingBotLevel = 60 # Enable expansion limitation for bot enchants -# If enabled, bots will not use TBC enchants until level 61 or WotLK enchanges until level 71 +# If enabled, bots will not use TBC enchants until level 61 or WotLK enchants until level 71 # Default: 1 (enabled) AiPlayerbot.LimitEnchantExpansion = 1 @@ -709,6 +709,10 @@ AiPlayerbot.DefaultPetStance = 1 # Default = 0 (disabled) AiPlayerbot.PetChatCommandDebug = 0 +# Prohibit hunter bots from creating pets with any family ID listed below in ExcludedHunterPetFamilies +# See the creature_family database table for all pet families by ID (note: ID for spiders is 3) +AiPlayerbot.ExcludedHunterPetFamilies = "" + # # # diff --git a/src/PlayerbotAI.cpp b/src/PlayerbotAI.cpp index 8060510c..48c44ae0 100644 --- a/src/PlayerbotAI.cpp +++ b/src/PlayerbotAI.cpp @@ -5105,13 +5105,13 @@ Item* PlayerbotAI::FindAmmo() const } // Find Consumable -Item* PlayerbotAI::FindConsumable(uint32 displayId) const +Item* PlayerbotAI::FindConsumable(uint32 itemId) const { return FindItemInInventory( - [displayId](ItemTemplate const* pItemProto) -> bool + [itemId](ItemTemplate const* pItemProto) -> bool { return (pItemProto->Class == ITEM_CLASS_CONSUMABLE || pItemProto->Class == ITEM_CLASS_TRADE_GOODS) && - pItemProto->DisplayInfoID == displayId; + pItemProto->ItemId == itemId; }); } @@ -5171,36 +5171,34 @@ Item* PlayerbotAI::FindLockedItem() const }); } -static const uint32 uPriorizedSharpStoneIds[8] = {ADAMANTITE_SHARPENING_DISPLAYID, FEL_SHARPENING_DISPLAYID, - ELEMENTAL_SHARPENING_DISPLAYID, DENSE_SHARPENING_DISPLAYID, - SOLID_SHARPENING_DISPLAYID, HEAVY_SHARPENING_DISPLAYID, - COARSE_SHARPENING_DISPLAYID, ROUGH_SHARPENING_DISPLAYID}; - -static const uint32 uPriorizedWeightStoneIds[7] = {ADAMANTITE_WEIGHTSTONE_DISPLAYID, FEL_WEIGHTSTONE_DISPLAYID, - DENSE_WEIGHTSTONE_DISPLAYID, SOLID_WEIGHTSTONE_DISPLAYID, - HEAVY_WEIGHTSTONE_DISPLAYID, COARSE_WEIGHTSTONE_DISPLAYID, - ROUGH_WEIGHTSTONE_DISPLAYID}; - -/** - * FindStoneFor() - * return Item* Returns sharpening/weight stone item eligible to enchant a bot weapon - * - * params:weapon Item* the weap�n the function should search and return a enchanting item for - * return nullptr if no relevant item is found in bot inventory, else return a sharpening or weight - * stone based on the weapon subclass - * - */ Item* PlayerbotAI::FindStoneFor(Item* weapon) const { + if (!weapon) + return nullptr; + + const ItemTemplate* item_template = weapon->GetTemplate(); + if (!item_template) + return nullptr; + +static const std::vector uPrioritizedSharpStoneIds = { + ADAMANTITE_SHARPENING_STONE, FEL_SHARPENING_STONE, ELEMENTAL_SHARPENING_STONE, DENSE_SHARPENING_STONE, + SOLID_SHARPENING_STONE, HEAVY_SHARPENING_STONE, COARSE_SHARPENING_STONE, ROUGH_SHARPENING_STONE +}; + +static const std::vector uPrioritizedWeightStoneIds = { + ADAMANTITE_WEIGHTSTONE, FEL_WEIGHTSTONE, DENSE_WEIGHTSTONE, SOLID_WEIGHTSTONE, + HEAVY_WEIGHTSTONE, COARSE_WEIGHTSTONE, ROUGH_WEIGHTSTONE +}; + Item* stone = nullptr; ItemTemplate const* pProto = weapon->GetTemplate(); if (pProto && (pProto->SubClass == ITEM_SUBCLASS_WEAPON_SWORD || pProto->SubClass == ITEM_SUBCLASS_WEAPON_SWORD2 || pProto->SubClass == ITEM_SUBCLASS_WEAPON_AXE || pProto->SubClass == ITEM_SUBCLASS_WEAPON_AXE2 || - pProto->SubClass == ITEM_SUBCLASS_WEAPON_DAGGER)) + pProto->SubClass == ITEM_SUBCLASS_WEAPON_DAGGER || pProto->SubClass == ITEM_SUBCLASS_WEAPON_POLEARM)) { - for (uint8 i = 0; i < std::size(uPriorizedSharpStoneIds); ++i) + for (uint8 i = 0; i < std::size(uPrioritizedSharpStoneIds); ++i) { - stone = FindConsumable(uPriorizedSharpStoneIds[i]); + stone = FindConsumable(uPrioritizedSharpStoneIds[i]); if (stone) { return stone; @@ -5208,11 +5206,12 @@ Item* PlayerbotAI::FindStoneFor(Item* weapon) const } } else if (pProto && - (pProto->SubClass == ITEM_SUBCLASS_WEAPON_MACE || pProto->SubClass == ITEM_SUBCLASS_WEAPON_MACE2)) + (pProto->SubClass == ITEM_SUBCLASS_WEAPON_MACE || pProto->SubClass == ITEM_SUBCLASS_WEAPON_MACE2 || + pProto->SubClass == ITEM_SUBCLASS_WEAPON_STAFF || pProto->SubClass == ITEM_SUBCLASS_WEAPON_FIST)) { - for (uint8 i = 0; i < std::size(uPriorizedWeightStoneIds); ++i) + for (uint8 i = 0; i < std::size(uPrioritizedWeightStoneIds); ++i) { - stone = FindConsumable(uPriorizedWeightStoneIds[i]); + stone = FindConsumable(uPrioritizedWeightStoneIds[i]); if (stone) { return stone; @@ -5225,6 +5224,7 @@ Item* PlayerbotAI::FindStoneFor(Item* weapon) const Item* PlayerbotAI::FindOilFor(Item* weapon) const { + if (!weapon) return nullptr; @@ -5232,35 +5232,60 @@ Item* PlayerbotAI::FindOilFor(Item* weapon) const if (!item_template) return nullptr; - // static const will only get created once whatever the call amout - static const std::vector uPriorizedWizardOilIds = { - MINOR_WIZARD_OIL, MINOR_MANA_OIL, LESSER_WIZARD_OIL, LESSER_MANA_OIL, BRILLIANT_WIZARD_OIL, - BRILLIANT_MANA_OIL, WIZARD_OIL, SUPERIOR_MANA_OIL, SUPERIOR_WIZARD_OIL}; + static const std::vector uPrioritizedWizardOilIds = { + BRILLIANT_WIZARD_OIL, SUPERIOR_WIZARD_OIL, WIZARD_OIL, LESSER_WIZARD_OIL, MINOR_WIZARD_OIL, + BRILLIANT_MANA_OIL, SUPERIOR_MANA_OIL, LESSER_MANA_OIL, MINOR_MANA_OIL}; - // static const will only get created once whatever the call amout - static const std::vector uPriorizedManaOilIds = { - MINOR_MANA_OIL, MINOR_WIZARD_OIL, LESSER_MANA_OIL, LESSER_WIZARD_OIL, BRILLIANT_MANA_OIL, - BRILLIANT_WIZARD_OIL, SUPERIOR_MANA_OIL, WIZARD_OIL, SUPERIOR_WIZARD_OIL}; + static const std::vector uPrioritizedManaOilIds = { + BRILLIANT_MANA_OIL, SUPERIOR_MANA_OIL, LESSER_MANA_OIL, MINOR_MANA_OIL, + BRILLIANT_WIZARD_OIL, SUPERIOR_WIZARD_OIL, WIZARD_OIL, LESSER_WIZARD_OIL, MINOR_WIZARD_OIL}; Item* oil = nullptr; - if (item_template->SubClass == ITEM_SUBCLASS_WEAPON_SWORD || - item_template->SubClass == ITEM_SUBCLASS_WEAPON_STAFF || item_template->SubClass == ITEM_SUBCLASS_WEAPON_DAGGER) + int botClass = bot->getClass(); + int specTab = AiFactory::GetPlayerSpecTab(bot); + + const std::vector* prioritizedOils = nullptr; + switch (botClass) { - for (const auto& id : uPriorizedWizardOilIds) - { - oil = FindConsumable(id); - if (oil) - return oil; - } + case CLASS_PRIEST: + prioritizedOils = (specTab == 2) ? &uPrioritizedWizardOilIds : &uPrioritizedManaOilIds; + break; + case CLASS_MAGE: + prioritizedOils = &uPrioritizedWizardOilIds; + break; + case CLASS_DRUID: + if (specTab == 0) // Balance + prioritizedOils = &uPrioritizedWizardOilIds; + else if (specTab == 1) // Feral + prioritizedOils = nullptr; + else // Restoration (specTab == 2) or any other/unspecified spec + prioritizedOils = &uPrioritizedManaOilIds; + break; + case CLASS_HUNTER: + prioritizedOils = &uPrioritizedManaOilIds; + break; + case CLASS_PALADIN: + if (specTab == 1) // Protection + prioritizedOils = &uPrioritizedWizardOilIds; + else if (specTab == 2) // Retribution + prioritizedOils = nullptr; + else // Holy (specTab == 0) or any other/unspecified spec + prioritizedOils = &uPrioritizedManaOilIds; + break; + default: + prioritizedOils = &uPrioritizedManaOilIds; + break; } - else if (item_template->SubClass == ITEM_SUBCLASS_WEAPON_MACE || - item_template->SubClass == ITEM_SUBCLASS_WEAPON_MACE2) + + if (prioritizedOils) { - for (const auto& id : uPriorizedManaOilIds) + for (const auto& id : *prioritizedOils) { oil = FindConsumable(id); if (oil) + { return oil; + } } } diff --git a/src/PlayerbotAI.h b/src/PlayerbotAI.h index 758f05ba..3fa7b5b6 100644 --- a/src/PlayerbotAI.h +++ b/src/PlayerbotAI.h @@ -46,27 +46,27 @@ struct GameObjectData; enum StrategyType : uint32; -enum HealingItemDisplayId +enum HealingItemId { - HEALTHSTONE_DISPLAYID = 8026, - MAJOR_HEALING_POTION = 24152, - WHIPPER_ROOT_TUBER = 21974, - NIGHT_DRAGON_BREATH = 21975, - LIMITED_INVULNERABILITY_POTION = 24213, - GREATER_DREAMLESS_SLEEP_POTION = 17403, - SUPERIOR_HEALING_POTION = 15714, - CRYSTAL_RESTORE = 2516, - DREAMLESS_SLEEP_POTION = 17403, - GREATER_HEALING_POTION = 15713, - HEALING_POTION = 15712, - LESSER_HEALING_POTION = 15711, - DISCOLORED_HEALING_POTION = 15736, - MINOR_HEALING_POTION = 15710, - VOLATILE_HEALING_POTION = 24212, - SUPER_HEALING_POTION = 37807, - CRYSTAL_HEALING_POTION = 47132, - FEL_REGENERATION_POTION = 37864, - MAJOR_DREAMLESS_SLEEP_POTION = 37845 + HEALTHSTONE = 5512, + MAJOR_HEALING_POTION = 13446, + WHIPPER_ROOT_TUBER = 11951, + NIGHT_DRAGON_BREATH = 11952, + LIMITED_INVULNERABILITY_POTION = 3387, + GREATER_DREAMLESS_SLEEP_POTION = 22886, + SUPERIOR_HEALING_POTION = 3928, + CRYSTAL_RESTORE = 11564, + DREAMLESS_SLEEP_POTION = 12190, + GREATER_HEALING_POTION = 1710, + HEALING_POTION = 929, + LESSER_HEALING_POTION = 858, + DISCOLORED_HEALING_POTION = 3391, + MINOR_HEALING_POTION = 118, + VOLATILE_HEALING_POTION = 28100, + SUPER_HEALING_POTION = 22829, + CRYSTAL_HEALING_POTION = 13462, + FEL_REGENERATION_POTION = 28101, + MAJOR_DREAMLESS_SLEEP_POTION = 20002 }; enum BotState @@ -152,6 +152,7 @@ static std::map ChatChannelSourceStr = { {SRC_RAID, "SRC_RAID"}, {SRC_UNDEFINED, "SRC_UNDEFINED"}}; + enum ChatChannelId { GENERAL = 1, @@ -162,60 +163,66 @@ enum ChatChannelId GUILD_RECRUITMENT = 25, }; -enum RoguePoisonDisplayId +enum RoguePoisonId { - DEADLY_POISON_DISPLAYID = 13707, - INSTANT_POISON_DISPLAYID = 13710, - WOUND_POISON_DISPLAYID = 37278 + INSTANT_POISON = 6947, + INSTANT_POISON_II = 6949, + INSTANT_POISON_III = 6950, + INSTANT_POISON_IV = 8926, + INSTANT_POISON_V = 8927, + INSTANT_POISON_VI = 8928, + INSTANT_POISON_VII = 21927, + INSTANT_POISON_VIII = 43230, + INSTANT_POISON_IX = 43231, + DEADLY_POISON = 2892, + DEADLY_POISON_II = 2893, + DEADLY_POISON_III = 8984, + DEADLY_POISON_IV = 8985, + DEADLY_POISON_V = 20844, + DEADLY_POISON_VI = 22053, + DEADLY_POISON_VII = 22054, + DEADLY_POISON_VIII = 43232, + DEADLY_POISON_IX = 43233 }; -enum SharpeningStoneDisplayId +enum SharpeningStoneId { - ROUGH_SHARPENING_DISPLAYID = 24673, - COARSE_SHARPENING_DISPLAYID = 24674, - HEAVY_SHARPENING_DISPLAYID = 24675, - SOLID_SHARPENING_DISPLAYID = 24676, - DENSE_SHARPENING_DISPLAYID = 24677, - CONSECRATED_SHARPENING_DISPLAYID = - 24674, // will not be used because bot can not know if it will face undead targets - ELEMENTAL_SHARPENING_DISPLAYID = 21072, - FEL_SHARPENING_DISPLAYID = 39192, - ADAMANTITE_SHARPENING_DISPLAYID = 39193 + ROUGH_SHARPENING_STONE = 2862, + COARSE_SHARPENING_STONE = 2863, + HEAVY_SHARPENING_STONE = 2871, + SOLID_SHARPENING_STONE = 7964, + DENSE_SHARPENING_STONE = 12404, + ELEMENTAL_SHARPENING_STONE = 18262, + FEL_SHARPENING_STONE = 23528, + ADAMANTITE_SHARPENING_STONE = 23529 }; -enum WeightStoneDisplayId +enum WeightstoneId { - ROUGH_WEIGHTSTONE_DISPLAYID = 24683, - COARSE_WEIGHTSTONE_DISPLAYID = 24684, - HEAVY_WEIGHTSTONE_DISPLAYID = 24685, - SOLID_WEIGHTSTONE_DISPLAYID = 24686, - DENSE_WEIGHTSTONE_DISPLAYID = 24687, - FEL_WEIGHTSTONE_DISPLAYID = 39548, - ADAMANTITE_WEIGHTSTONE_DISPLAYID = 39549 + ROUGH_WEIGHTSTONE = 3239, + COARSE_WEIGHTSTONE = 3240, + HEAVY_WEIGHTSTONE = 3241, + SOLID_WEIGHTSTONE = 7965, + DENSE_WEIGHTSTONE = 12643, + FEL_WEIGHTSTONE = 28420, + ADAMANTITE_WEIGHTSTONE = 28421 }; -enum WizardOilDisplayId +enum WizardOilId { - MINOR_WIZARD_OIL = 9731, - LESSER_WIZARD_OIL = 47903, - BRILLIANT_WIZARD_OIL = 47901, - WIZARD_OIL = 47905, - SUPERIOR_WIZARD_OIL = 47904, - /// Blessed Wizard Oil = 26865 //scourge inv + MINOR_WIZARD_OIL = 20744, + LESSER_WIZARD_OIL = 20746, + WIZARD_OIL = 20750, + BRILLIANT_WIZARD_OIL = 20749, + SUPERIOR_WIZARD_OIL = 22522 }; -enum ManaOilDisplayId +enum ManaOilId { - MINOR_MANA_OIL = 34492, - LESSER_MANA_OIL = 47902, - BRILLIANT_MANA_OIL = 41488, - SUPERIOR_MANA_OIL = 36862 -}; - -enum ShieldWardDisplayId -{ - LESSER_WARD_OFSHIELDING = 38759, - GREATER_WARD_OFSHIELDING = 38760 + MINOR_MANA_OIL = 20745, + LESSER_MANA_OIL = 20747, + BRILLIANT_MANA_OIL = 20748, + SUPERIOR_MANA_OIL = 22521 }; enum class BotTypeNumber : uint8 @@ -472,7 +479,7 @@ public: Item* FindBandage() const; Item* FindOpenableItem() const; Item* FindLockedItem() const; - Item* FindConsumable(uint32 displayId) const; + Item* FindConsumable(uint32 itemId) const; Item* FindStoneFor(Item* weapon) const; Item* FindOilFor(Item* weapon) const; void ImbueItem(Item* item, uint32 targetFlag, ObjectGuid targetGUID); diff --git a/src/PlayerbotAIConfig.cpp b/src/PlayerbotAIConfig.cpp index fd6fc7e8..b081fbf0 100644 --- a/src/PlayerbotAIConfig.cpp +++ b/src/PlayerbotAIConfig.cpp @@ -632,6 +632,9 @@ bool PlayerbotAIConfig::Initialize() sPlayerbotDungeonSuggestionMgr->LoadDungeonSuggestions(); } + excludedHunterPetFamilies.clear(); + LoadList>(sConfigMgr->GetOption("AiPlayerbot.ExcludedHunterPetFamilies", ""), excludedHunterPetFamilies); + LOG_INFO("server.loading", "---------------------------------------"); LOG_INFO("server.loading", " AI Playerbots initialized "); LOG_INFO("server.loading", "---------------------------------------"); diff --git a/src/PlayerbotAIConfig.h b/src/PlayerbotAIConfig.h index 0c7a3f62..a9a12fba 100644 --- a/src/PlayerbotAIConfig.h +++ b/src/PlayerbotAIConfig.h @@ -408,6 +408,8 @@ public: bool restrictHealerDPS = false; std::vector restrictedHealerDPSMaps; bool IsRestrictedHealerDPSMap(uint32 mapId) const; + + std::vector excludedHunterPetFamilies; }; #define sPlayerbotAIConfig PlayerbotAIConfig::instance() diff --git a/src/PlayerbotMgr.cpp b/src/PlayerbotMgr.cpp index ee6c6926..e00ab29e 100644 --- a/src/PlayerbotMgr.cpp +++ b/src/PlayerbotMgr.cpp @@ -36,6 +36,8 @@ #include "BroadcastHelper.h" #include "PlayerbotDbStore.h" #include "WorldSessionMgr.h" +#include "DatabaseEnv.h" // Added for gender choice +#include // Added for gender choice class BotInitGuard { @@ -837,6 +839,18 @@ std::string const PlayerbotHolder::ProcessBotCommand(std::string const cmd, Obje return "unknown command"; } +// Added for gender choice : Returns the gender of an offline character: 0 = male, 1 = female. +static uint8 GetOfflinePlayerGender(ObjectGuid guid) +{ + QueryResult result = CharacterDatabase.Query( + "SELECT gender FROM characters WHERE guid = {}", guid.GetCounter()); + + if (result) + return (*result)[0].Get(); // 0 = male, 1 = female + + return GENDER_MALE; // fallback value +} + bool PlayerbotMgr::HandlePlayerbotMgrCommand(ChatHandler* handler, char const* args) { if (!sPlayerbotAIConfig->enabled) @@ -879,15 +893,17 @@ std::vector PlayerbotHolder::HandlePlayerbotCommand(char const* arg if (!*args) { messages.push_back("usage: list/reload/tweak/self or add/addaccount/init/remove PLAYERNAME\n"); - messages.push_back("usage: addclass CLASSNAME"); + messages.push_back("usage: addclass CLASSNAME [male|female|0|1]"); return messages; } char* cmd = strtok((char*)args, " "); char* charname = strtok(nullptr, " "); + char* genderArg = strtok(nullptr, " "); // Added for gender choice [male|female|0|1] optionnel + if (!cmd) { - messages.push_back("usage: list/reload/tweak/self or add/init/remove PLAYERNAME or addclass CLASSNAME"); + messages.push_back("usage: list/reload/tweak/self or add/init/remove PLAYERNAME or addclass CLASSNAME [male|female]"); return messages; } @@ -1110,6 +1126,24 @@ std::vector PlayerbotHolder::HandlePlayerbotCommand(char const* arg messages.push_back("Error: Invalid Class. Try again."); return messages; } + // Added for gender choice : Parsing gender + int8 gender = -1; // -1 = gender will be random + if (genderArg) + { + std::string g = genderArg; + std::transform(g.begin(), g.end(), g.begin(), ::tolower); + + if (g == "male" || g == "0") + gender = GENDER_MALE; // 0 + else if (g == "female" || g == "1") + gender = GENDER_FEMALE; // 1 + else + { + messages.push_back("Unknown gender : " + g + " (male/female/0/1)"); + return messages; + } + } //end + if (claz == 6 && master->GetLevel() < sWorld->getIntConfig(CONFIG_START_HEROIC_PLAYER_LEVEL)) { messages.push_back("Your level is too low to summon Deathknight"); @@ -1119,6 +1153,9 @@ std::vector PlayerbotHolder::HandlePlayerbotCommand(char const* arg const std::unordered_set &guidCache = sRandomPlayerbotMgr->addclassCache[RandomPlayerbotMgr::GetTeamClassIdx(teamId == TEAM_ALLIANCE, claz)]; for (const ObjectGuid &guid: guidCache) { + // If the user requested a specific gender, skip any character that doesn't match. + if (gender != -1 && GetOfflinePlayerGender(guid) != gender) + continue; if (botLoading.find(guid) != botLoading.end()) continue; if (ObjectAccessor::FindConnectedPlayer(guid)) diff --git a/src/Playerbots.cpp b/src/Playerbots.cpp index b2fc0cd2..07b2bf94 100644 --- a/src/Playerbots.cpp +++ b/src/Playerbots.cpp @@ -197,21 +197,13 @@ public: sRandomPlayerbotMgr->HandleCommand(type, msg, player); } - bool OnPlayerBeforeCriteriaProgress(Player* player, AchievementCriteriaEntry const* /*criteria*/) override + bool OnPlayerBeforeAchievementComplete(Player* player, AchievementEntry const* achievement) override { - if (sRandomPlayerbotMgr->IsRandomBot(player)) + if (sRandomPlayerbotMgr->IsRandomBot(player) && (achievement->flags == 256 || achievement->flags == 768)) { return false; } - return true; - } - bool OnPlayerBeforeAchievementComplete(Player* player, AchievementEntry const* /*achievement*/) override - { - if (sRandomPlayerbotMgr->IsRandomBot(player)) - { - return false; - } return true; } diff --git a/src/factory/PlayerbotFactory.cpp b/src/factory/PlayerbotFactory.cpp index b43ee055..0fe95176 100644 --- a/src/factory/PlayerbotFactory.cpp +++ b/src/factory/PlayerbotFactory.cpp @@ -414,7 +414,7 @@ void PlayerbotFactory::Randomize(bool incremental) pmo = sPerformanceMonitor->start(PERF_MON_RNDBOT, "PlayerbotFactory_Consumable"); LOG_DEBUG("playerbots", "Initializing consumables..."); - AddConsumables(); + InitConsumables(); if (pmo) pmo->finish(); @@ -482,11 +482,8 @@ void PlayerbotFactory::Refresh() InitAmmo(); InitFood(); InitReagents(); - // InitPotions(); - if (!sPlayerbotAIConfig->equipmentPersistence || bot->GetLevel() < sPlayerbotAIConfig->equipmentPersistenceLevel) - { - InitTalentsTree(true, true, true); - } + InitConsumables(); + InitPotions(); InitPet(); InitPetTalents(); InitClassSpells(); @@ -496,7 +493,10 @@ void PlayerbotFactory::Refresh() InitSpecialSpells(); InitMounts(); InitKeyring(); - InitPotions(); + if (!sPlayerbotAIConfig->equipmentPersistence || bot->GetLevel() < sPlayerbotAIConfig->equipmentPersistenceLevel) + { + InitTalentsTree(true, true, true); + } if (bot->GetLevel() >= sPlayerbotAIConfig->minEnchantingBotLevel) { ApplyEnchantAndGemsNew(); @@ -510,151 +510,218 @@ void PlayerbotFactory::Refresh() // bot->SaveToDB(false, false); } -void PlayerbotFactory::AddConsumables() +void PlayerbotFactory::InitConsumables() { + int specTab = AiFactory::GetPlayerSpecTab(bot); + std::vector> items; + switch (bot->getClass()) { case CLASS_PRIEST: - case CLASS_MAGE: - case CLASS_WARLOCK: { - if (level >= 5 && level < 20) + // Discipline or Holy: Mana Oil + if (specTab == 0 || specTab == 1) { - StoreItem(CONSUM_ID_MINOR_WIZARD_OIL, 5); + std::vector mana_oils = {BRILLIANT_MANA_OIL, SUPERIOR_MANA_OIL, LESSER_MANA_OIL, MINOR_MANA_OIL}; + for (uint32 itemId : mana_oils) + { + ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId); + if (proto->RequiredLevel > level || level > 75) + continue; + items.push_back({itemId, 4}); + break; + } } - - if (level >= 20 && level < 40) + // Shadow: Wizard Oil + if (specTab == 2) { - StoreItem(CONSUM_ID_MINOR_MANA_OIL, 5); - StoreItem(CONSUM_ID_MINOR_WIZARD_OIL, 5); + std::vector wizard_oils = {BRILLIANT_WIZARD_OIL, SUPERIOR_WIZARD_OIL, WIZARD_OIL, LESSER_WIZARD_OIL, MINOR_WIZARD_OIL}; + for (uint32 itemId : wizard_oils) + { + ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId); + if (proto->RequiredLevel > level || level > 75) + continue; + items.push_back({itemId, 4}); + break; + } } - - if (level >= 40 && level < 45) + break; + } + case CLASS_MAGE: + { + // Always Wizard Oil + std::vector wizard_oils = {BRILLIANT_WIZARD_OIL, SUPERIOR_WIZARD_OIL, WIZARD_OIL, LESSER_WIZARD_OIL, MINOR_WIZARD_OIL}; + for (uint32 itemId : wizard_oils) { - StoreItem(CONSUM_ID_MINOR_MANA_OIL, 5); - StoreItem(CONSUM_ID_WIZARD_OIL, 5); + ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId); + if (proto->RequiredLevel > level || level > 75) + continue; + items.push_back({itemId, 4}); + break; } - - if (level >= 45 && level < 52) + break; + } + case CLASS_DRUID: + { + // Balance: Wizard Oil + if (specTab == 0) { - StoreItem(CONSUM_ID_BRILLIANT_MANA_OIL, 5); - StoreItem(CONSUM_ID_BRILLIANT_WIZARD_OIL, 5); + std::vector wizard_oils = {BRILLIANT_WIZARD_OIL, SUPERIOR_WIZARD_OIL, WIZARD_OIL, LESSER_WIZARD_OIL, MINOR_WIZARD_OIL}; + for (uint32 itemId : wizard_oils) + { + ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId); + if (proto->RequiredLevel > level || level > 75) + continue; + items.push_back({itemId, 4}); + break; + } } - if (level >= 52 && level < 58) + // Feral: Sharpening Stones & Weightstones + else if (specTab == 1) { - StoreItem(CONSUM_ID_SUPERIOR_MANA_OIL, 5); - StoreItem(CONSUM_ID_BRILLIANT_WIZARD_OIL, 5); + std::vector sharpening_stones = {ADAMANTITE_SHARPENING_STONE, FEL_SHARPENING_STONE, DENSE_SHARPENING_STONE, SOLID_SHARPENING_STONE, HEAVY_SHARPENING_STONE, COARSE_SHARPENING_STONE, ROUGH_SHARPENING_STONE}; + std::vector weightstones = {ADAMANTITE_WEIGHTSTONE, FEL_WEIGHTSTONE, DENSE_WEIGHTSTONE, SOLID_WEIGHTSTONE, HEAVY_WEIGHTSTONE, COARSE_WEIGHTSTONE, ROUGH_WEIGHTSTONE}; + for (uint32 itemId : sharpening_stones) + { + ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId); + if (proto->RequiredLevel > level || level > 75) + continue; + items.push_back({itemId, 20}); + break; + } + for (uint32 itemId : weightstones) + { + ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId); + if (proto->RequiredLevel > level || level > 75) + continue; + items.push_back({itemId, 20}); + break; + } } - - if (level >= 58) + // Restoration: Mana Oil + else if (specTab == 2) { - StoreItem(CONSUM_ID_SUPERIOR_MANA_OIL, 5); - StoreItem(CONSUM_ID_SUPERIOR_WIZARD_OIL, 5); + std::vector mana_oils = {BRILLIANT_MANA_OIL, SUPERIOR_MANA_OIL, LESSER_MANA_OIL, MINOR_MANA_OIL}; + for (uint32 itemId : mana_oils) + { + ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId); + if (proto->RequiredLevel > level || level > 75) + continue; + items.push_back({itemId, 4}); + break; + } } break; } case CLASS_PALADIN: + { + // Holy: Mana Oil + if (specTab == 0) + { + std::vector mana_oils = {BRILLIANT_MANA_OIL, SUPERIOR_MANA_OIL, LESSER_MANA_OIL, MINOR_MANA_OIL}; + for (uint32 itemId : mana_oils) + { + ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId); + if (proto->RequiredLevel > level || level > 75) + continue; + items.push_back({itemId, 4}); + break; + } + } + // Protection: Wizard Oil (Protection prioritizes Superior over Brilliant) + else if (specTab == 1) + { + std::vector wizard_oils = {BRILLIANT_WIZARD_OIL, SUPERIOR_WIZARD_OIL, WIZARD_OIL, LESSER_WIZARD_OIL, MINOR_WIZARD_OIL}; + for (uint32 itemId : wizard_oils) + { + ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId); + if (proto->RequiredLevel > level || level > 75) + continue; + items.push_back({itemId, 4}); + break; + } + } + // Retribution: Sharpening Stones & Weightstones + else if (specTab == 2) + { + std::vector sharpening_stones = {ADAMANTITE_SHARPENING_STONE, FEL_SHARPENING_STONE, DENSE_SHARPENING_STONE, SOLID_SHARPENING_STONE, HEAVY_SHARPENING_STONE, COARSE_SHARPENING_STONE, ROUGH_SHARPENING_STONE}; + std::vector weightstones = {ADAMANTITE_WEIGHTSTONE, FEL_WEIGHTSTONE, DENSE_WEIGHTSTONE, SOLID_WEIGHTSTONE, HEAVY_WEIGHTSTONE, COARSE_WEIGHTSTONE, ROUGH_WEIGHTSTONE}; + for (uint32 itemId : sharpening_stones) + { + ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId); + if (proto->RequiredLevel > level || level > 75) + continue; + items.push_back({itemId, 20}); + break; + } + for (uint32 itemId : weightstones) + { + ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId); + if (proto->RequiredLevel > level || level > 75) + continue; + items.push_back({itemId, 20}); + break; + } + } + break; + } case CLASS_WARRIOR: case CLASS_HUNTER: { - if (level >= 1 && level < 5) + // Sharpening Stones & Weightstones + std::vector sharpening_stones = {ADAMANTITE_SHARPENING_STONE, FEL_SHARPENING_STONE, DENSE_SHARPENING_STONE, SOLID_SHARPENING_STONE, HEAVY_SHARPENING_STONE, COARSE_SHARPENING_STONE, ROUGH_SHARPENING_STONE}; + std::vector weightstones = {ADAMANTITE_WEIGHTSTONE, FEL_WEIGHTSTONE, DENSE_WEIGHTSTONE, SOLID_WEIGHTSTONE, HEAVY_WEIGHTSTONE, COARSE_WEIGHTSTONE, ROUGH_WEIGHTSTONE}; + for (uint32 itemId : sharpening_stones) { - StoreItem(CONSUM_ID_ROUGH_SHARPENING_STONE, 5); - StoreItem(CONSUM_ID_ROUGH_WEIGHTSTONE, 5); + ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId); + if (proto->RequiredLevel > level || level > 75) + continue; + items.push_back({itemId, 20}); + break; } - - if (level >= 5 && level < 15) + for (uint32 itemId : weightstones) { - StoreItem(CONSUM_ID_COARSE_WEIGHTSTONE, 5); - StoreItem(CONSUM_ID_COARSE_SHARPENING_STONE, 5); - } - - if (level >= 15 && level < 25) - { - StoreItem(CONSUM_ID_HEAVY_WEIGHTSTONE, 5); - StoreItem(CONSUM_ID_HEAVY_SHARPENING_STONE, 5); - } - - if (level >= 25 && level < 35) - { - StoreItem(CONSUM_ID_SOL_SHARPENING_STONE, 5); - StoreItem(CONSUM_ID_SOLID_WEIGHTSTONE, 5); - } - - if (level >= 35 && level < 50) - { - StoreItem(CONSUM_ID_DENSE_WEIGHTSTONE, 5); - StoreItem(CONSUM_ID_DENSE_SHARPENING_STONE, 5); - } - - if (level >= 50 && level < 60) - { - StoreItem(CONSUM_ID_FEL_SHARPENING_STONE, 5); - StoreItem(CONSUM_ID_FEL_WEIGHTSTONE, 5); - } - - if (level >= 60) - { - StoreItem(CONSUM_ID_ADAMANTITE_WEIGHTSTONE, 5); - StoreItem(CONSUM_ID_ADAMANTITE_SHARPENING_STONE, 5); + ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId); + if (proto->RequiredLevel > level || level > 75) + continue; + items.push_back({itemId, 20}); + break; } break; } case CLASS_ROGUE: { - if (level >= 20 && level < 28) + // Poisons + std::vector instant_poisons = {INSTANT_POISON_IX, INSTANT_POISON_VIII, INSTANT_POISON_VII, INSTANT_POISON_VI, INSTANT_POISON_V, INSTANT_POISON_IV, INSTANT_POISON_III, INSTANT_POISON_II, INSTANT_POISON}; + std::vector deadly_poisons = {DEADLY_POISON_IX, DEADLY_POISON_VIII, DEADLY_POISON_VII, DEADLY_POISON_VI, DEADLY_POISON_V, DEADLY_POISON_IV, DEADLY_POISON_III, DEADLY_POISON_II, DEADLY_POISON}; + for (uint32 itemId : deadly_poisons) { - StoreItem(CONSUM_ID_INSTANT_POISON, 5); + ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId); + if (proto->RequiredLevel > level) + continue; + items.push_back({itemId, 20}); + break; } - - if (level >= 28 && level < 30) + for (uint32 itemId : instant_poisons) { - StoreItem(CONSUM_ID_INSTANT_POISON_II, 5); - } - - if (level >= 30 && level < 36) - { - StoreItem(CONSUM_ID_DEADLY_POISON, 5); - StoreItem(CONSUM_ID_INSTANT_POISON_II, 5); - } - - if (level >= 36 && level < 44) - { - StoreItem(CONSUM_ID_DEADLY_POISON_II, 5); - StoreItem(CONSUM_ID_INSTANT_POISON_III, 5); - } - - if (level >= 44 && level < 52) - { - StoreItem(CONSUM_ID_DEADLY_POISON_III, 5); - StoreItem(CONSUM_ID_INSTANT_POISON_IV, 5); - } - if (level >= 52 && level < 60) - { - StoreItem(CONSUM_ID_DEADLY_POISON_IV, 5); - StoreItem(CONSUM_ID_INSTANT_POISON_V, 5); - } - - if (level >= 60 && level < 62) - { - StoreItem(CONSUM_ID_DEADLY_POISON_V, 5); - StoreItem(CONSUM_ID_INSTANT_POISON_VI, 5); - } - - if (level >= 62 && level < 68) - { - StoreItem(CONSUM_ID_DEADLY_POISON_VI, 5); - StoreItem(CONSUM_ID_INSTANT_POISON_VI, 5); - } - - if (level >= 68) - { - StoreItem(CONSUM_ID_DEADLY_POISON_VII, 5); - StoreItem(CONSUM_ID_INSTANT_POISON_VII, 5); + ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId); + if (proto->RequiredLevel > level) + continue; + items.push_back({itemId, 20}); + break; } break; } + default: + break; } + + for (std::pair item : items) + { + int count = (int)item.second - (int)bot->GetItemCount(item.first); + if (count > 0) + StoreItem(item.first, count); + } } void PlayerbotFactory::InitPetTalents() @@ -828,6 +895,7 @@ void PlayerbotFactory::InitPet() std::vector ids; CreatureTemplateContainer const* creatures = sObjectMgr->GetCreatureTemplates(); + for (CreatureTemplateContainer::const_iterator itr = creatures->begin(); itr != creatures->end(); ++itr) { if (!itr->second.IsTameable(bot->CanTameExoticPets())) @@ -843,6 +911,12 @@ void PlayerbotFactory::InitPet() if (onlyWolf && itr->second.family != CREATURE_FAMILY_WOLF) continue; + // Exclude configured pet families + if (std::find(sPlayerbotAIConfig->excludedHunterPetFamilies.begin(), + sPlayerbotAIConfig->excludedHunterPetFamilies.end(), + itr->second.family) != sPlayerbotAIConfig->excludedHunterPetFamilies.end()) + continue; + ids.push_back(itr->first); } @@ -3220,102 +3294,97 @@ void PlayerbotFactory::InitFood() void PlayerbotFactory::InitReagents() { - int level = bot->GetLevel(); + int specTab = AiFactory::GetPlayerSpecTab(bot); std::vector> items; switch (bot->getClass()) { - case CLASS_ROGUE: - { - std::vector instant_poison_ids = {43231, 43230, 21927, 8928, 8927, 8926, 6950, 6949, 6947}; - std::vector deadly_poison_ids = {43233, 43232, 22054, 22053, 20844, 8985, 8984, 2893, 2892}; - for (int& itemId : deadly_poison_ids) - { - ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId); - if (proto->RequiredLevel > bot->GetLevel()) - continue; - items.push_back({itemId, 20}); // deadly poison - break; - } - for (int& itemId : instant_poison_ids) - { - ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId); - if (proto->RequiredLevel > bot->GetLevel()) - continue; - items.push_back({itemId, 20}); // instant poison - break; - } - } - break; - case CLASS_SHAMAN: - // items.push_back({46978, 1}); // Totem - items.push_back({5175, 1}); // Earth Totem - items.push_back({5176, 1}); // Flame Totem - items.push_back({5177, 1}); // Water Totem - items.push_back({5178, 1}); // Air Totem - if (bot->GetLevel() >= 30) - items.push_back({17030, 40}); // Ankh - break; - case CLASS_WARLOCK: - items.push_back({6265, 5}); // shard - break; - case CLASS_PRIEST: - if (level >= 48 && level < 60) - { - items.push_back({17028, 40}); // Wild Berries - } - else if (level >= 60 && level < 80) - { - items.push_back({17029, 40}); // Wild Berries - } - else if (level >= 80) - { - items.push_back({44615, 40}); // Wild Berries - } - break; - case CLASS_MAGE: - items.push_back({17020, 40}); // Arcane Powder - items.push_back({17031, 40}); // portal - items.push_back({17032, 40}); // portal + case CLASS_DEATH_KNIGHT: + if (level >= 56) + items.push_back({37201, 40}); // Corpse Dust break; case CLASS_DRUID: if (level >= 20 && level < 30) + items.push_back({17034, 20}); // Maple Seed + else if (level >= 30 && level < 40) + items.push_back({17035, 20}); // Stranglethorn Seed + else if (level >= 40 && level < 50) + items.push_back({17036, 20}); // Ashwood Seed + else if (level >= 50 && level < 60) { - items.push_back({17034, 40}); + items.push_back({17037, 20}); // Hornbeam Seed + items.push_back({17021, 20}); // Wild Berries } - if (level >= 30 && level < 40) + else if (level >= 60 && level < 69) { - items.push_back({17035, 40}); + items.push_back({17038, 20}); // Ironwood Seed + items.push_back({17026, 20}); // Wild Thornroot } - if (level >= 40 && level < 50) + else if (level == 69) { - items.push_back({17036, 40}); + items.push_back({22147, 20}); // Flintweed Seed + items.push_back({17026, 20}); // Wild Thornroot } - if (level >= 50 && level < 60) + else if (level >= 70 && level < 79) { - items.push_back({17037, 40}); - items.push_back({17021, 40}); + items.push_back({22147, 20}); // Flintweed Seed + items.push_back({22148, 20}); // Wild Quillvine } - if (level >= 60 && level < 70) + else if (level == 79) { - items.push_back({17038, 40}); - items.push_back({17026, 40}); + items.push_back({44614, 20}); // Starleaf Seed + items.push_back({22148, 20}); // Wild Quillvine } - if (level >= 70 && level < 80) + else if (level >= 80) { - items.push_back({22147, 40}); - items.push_back({22148, 40}); - } - if (level >= 80) - { - items.push_back({44614, 40}); - items.push_back({44605, 40}); + items.push_back({44614, 20}); // Starleaf Seed + items.push_back({44605, 20}); // Wild Spineleaf } break; + case CLASS_MAGE: + if (level >= 20) + items.push_back({17031, 20}); // Rune of Teleportation + if (level >= 40) + items.push_back({17032, 20}); // Rune of Portals + if (level >= 56) + items.push_back({17020, 20}); // Arcane Powder + break; case CLASS_PALADIN: - items.push_back({21177, 100}); + if (level >= 52) + items.push_back({21177, 80}); // Symbol of Kings break; - case CLASS_DEATH_KNIGHT: - items.push_back({37201, 40}); + case CLASS_PRIEST: + if (level >= 48 && level < 56) + items.push_back({17028, 40}); // Holy Candle + else if (level >= 56 && level < 60) + { + items.push_back({17028, 20}); // Holy Candle + items.push_back({17029, 20}); // Sacred Candle + } + else if (level >= 60 && level < 77) + items.push_back({17029, 40}); // Sacred Candle + else if (level >= 77 && level < 80) + { + items.push_back({17029, 20}); // Sacred Candle + items.push_back({44615, 20}); // Devout Candle + } + else if (level >= 80) + items.push_back({44615, 40}); // Devout Candle + break; + case CLASS_SHAMAN: + if (level >= 4) + items.push_back({5175, 1}); // Earth Totem + if (level >= 10) + items.push_back({5176, 1}); // Flame Totem + if (level >= 20) + items.push_back({5177, 1}); // Water Totem + if (level >= 30) + { + items.push_back({5178, 1}); // Air Totem + items.push_back({17030, 20}); // Ankh + } + break; + case CLASS_WARLOCK: + items.push_back({6265, 5}); // Soul Shard break; default: break; @@ -3328,10 +3397,73 @@ void PlayerbotFactory::InitReagents() } } +void PlayerbotFactory::CleanupConsumables() // remove old consumables as part of randombot level-up maintenance +{ + std::vector itemsToDelete; + + std::vector items; + for (uint32 i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END; ++i) + if (Item* item = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, i)) + items.push_back(item); + + for (uint32 i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i) + if (Bag* bag = (Bag*)bot->GetItemByPos(INVENTORY_SLOT_BAG_0, i)) + for (uint32 j = 0; j < bag->GetBagSize(); ++j) + if (Item* item = bag->GetItemByPos(j)) + items.push_back(item); + + for (Item* item : items) + { + ItemTemplate const* proto = item->GetTemplate(); + if (!proto) continue; + + // Remove ammo + if (proto->Class == ITEM_CLASS_PROJECTILE) + itemsToDelete.push_back(item); + + // Remove food/drink + if (proto->Class == ITEM_CLASS_CONSUMABLE && proto->SubClass == ITEM_SUBCLASS_FOOD) + itemsToDelete.push_back(item); + + // Remove potions + if (proto->Class == ITEM_CLASS_CONSUMABLE && proto->SubClass == ITEM_SUBCLASS_POTION) + itemsToDelete.push_back(item); + + // Remove reagents + if (proto->Class == ITEM_CLASS_REAGENT || (proto->Class == ITEM_CLASS_MISC && proto->SubClass == ITEM_SUBCLASS_REAGENT)) + itemsToDelete.push_back(item); + } + + std::set idsToDelete = { + BRILLIANT_MANA_OIL, SUPERIOR_MANA_OIL, LESSER_MANA_OIL, MINOR_MANA_OIL, + BRILLIANT_WIZARD_OIL, SUPERIOR_WIZARD_OIL, WIZARD_OIL, LESSER_WIZARD_OIL, MINOR_WIZARD_OIL, + ADAMANTITE_SHARPENING_STONE, FEL_SHARPENING_STONE, DENSE_SHARPENING_STONE, SOLID_SHARPENING_STONE, + HEAVY_SHARPENING_STONE, COARSE_SHARPENING_STONE, ROUGH_SHARPENING_STONE, + ADAMANTITE_WEIGHTSTONE, FEL_WEIGHTSTONE, DENSE_WEIGHTSTONE, SOLID_WEIGHTSTONE, + HEAVY_WEIGHTSTONE, COARSE_WEIGHTSTONE, ROUGH_WEIGHTSTONE, + INSTANT_POISON_IX, INSTANT_POISON_VIII, INSTANT_POISON_VII, INSTANT_POISON_VI, INSTANT_POISON_V, + INSTANT_POISON_IV, INSTANT_POISON_III, INSTANT_POISON_II, INSTANT_POISON, + DEADLY_POISON_IX, DEADLY_POISON_VIII, DEADLY_POISON_VII, DEADLY_POISON_VI, DEADLY_POISON_V, + DEADLY_POISON_IV, DEADLY_POISON_III, DEADLY_POISON_II, DEADLY_POISON + }; + + for (Item* item : items) + { + ItemTemplate const* proto = item->GetTemplate(); + if (!proto) continue; + + if (idsToDelete.find(proto->ItemId) != idsToDelete.end()) + itemsToDelete.push_back(item); + } + + for (Item* item : itemsToDelete) + bot->DestroyItem(item->GetBagSlot(), item->GetSlot(), true); +} + void PlayerbotFactory::InitGlyphs(bool increment) { bot->InitGlyphsForLevel(); - if (!increment && + if (!increment && botAI && botAI->GetAiObjectContext()->GetValue("custom_glyphs")->Get()) return; // // Added for custom Glyphs - custom glyphs flag test diff --git a/src/factory/PlayerbotFactory.h b/src/factory/PlayerbotFactory.h index a76a2a32..cfbc41ba 100644 --- a/src/factory/PlayerbotFactory.h +++ b/src/factory/PlayerbotFactory.h @@ -45,63 +45,6 @@ enum spec : uint8 ROLE_CDPS = 3 };*/ -enum PriorizedConsumables -{ - CONSUM_ID_ROUGH_WEIGHTSTONE = 3239, - CONSUM_ID_COARSE_WEIGHTSTONE = 3239, - CONSUM_ID_HEAVY_WEIGHTSTONE = 3241, - CONSUM_ID_SOLID_WEIGHTSTONE = 7965, - CONSUM_ID_DENSE_WEIGHTSTONE = 12643, - CONSUM_ID_FEL_WEIGHTSTONE = 28420, - CONSUM_ID_ADAMANTITE_WEIGHTSTONE = 28421, - CONSUM_ID_ROUGH_SHARPENING_STONE = 2862, - CONSUM_ID_COARSE_SHARPENING_STONE = 2863, - CONSUM_ID_HEAVY_SHARPENING_STONE = 2871, - CONSUM_ID_SOL_SHARPENING_STONE = 7964, - CONSUM_ID_DENSE_SHARPENING_STONE = 12404, - CONSUM_ID_ELEMENTAL_SHARPENING_STONE = 18262, - CONSUM_ID_CONSECRATED_SHARPENING_STONE = 23122, - CONSUM_ID_FEL_SHARPENING_STONE = 23528, - CONSUM_ID_ADAMANTITE_SHARPENING_STONE = 23529, - CONSUM_ID_LINEN_BANDAGE = 1251, - CONSUM_ID_HEAVY_LINEN_BANDAGE = 2581, - CONSUM_ID_WOOL_BANDAGE = 3530, - CONSUM_ID_HEAVY_WOOL_BANDAGE = 3531, - CONSUM_ID_SILK_BANDAGE = 6450, - CONSUM_ID_HEAVY_SILK_BANDAGE = 6451, - CONSUM_ID_MAGEWEAVE_BANDAGE = 8544, - CONSUM_ID_HEAVY_MAGEWEAVE_BANDAGE = 8545, - CONSUM_ID_RUNECLOTH_BANDAGE = 14529, - CONSUM_ID_HEAVY_RUNECLOTH_BANDAGE = 14530, - CONSUM_ID_NETHERWEAVE_BANDAGE = 21990, - CONSUM_ID_HEAVY_NETHERWEAVE_BANDAGE = 21991, - CONSUM_ID_BRILLIANT_MANA_OIL = 20748, - CONSUM_ID_MINOR_MANA_OIL = 20745, - CONSUM_ID_SUPERIOR_MANA_OIL = 22521, - CONSUM_ID_LESSER_MANA_OIL = 20747, - CONSUM_ID_BRILLIANT_WIZARD_OIL = 20749, - CONSUM_ID_MINOR_WIZARD_OIL = 20744, - CONSUM_ID_SUPERIOR_WIZARD_OIL = 22522, - CONSUM_ID_WIZARD_OIL = 20750, - CONSUM_ID_LESSER_WIZARD_OIL = 20746, - CONSUM_ID_INSTANT_POISON = 6947, - CONSUM_ID_INSTANT_POISON_II = 6949, - CONSUM_ID_INSTANT_POISON_III = 6950, - CONSUM_ID_INSTANT_POISON_IV = 8926, - CONSUM_ID_INSTANT_POISON_V = 8927, - CONSUM_ID_INSTANT_POISON_VI = 8928, - CONSUM_ID_INSTANT_POISON_VII = 21927, - CONSUM_ID_DEADLY_POISON = 2892, - CONSUM_ID_DEADLY_POISON_II = 2893, - CONSUM_ID_DEADLY_POISON_III = 8984, - CONSUM_ID_DEADLY_POISON_IV = 8985, - CONSUM_ID_DEADLY_POISON_V = 20844, - CONSUM_ID_DEADLY_POISON_VI = 22053, - CONSUM_ID_DEADLY_POISON_VII = 22054 -}; - -#define MAX_CONSUM_ID 28 - class PlayerbotFactory { public: @@ -128,8 +71,10 @@ public: void InitAmmo(); static uint32 CalcMixedGearScore(uint32 gs, uint32 quality); void InitPetTalents(); - + void CleanupConsumables(); void InitReagents(); + void InitConsumables(); + void InitPotions(); void InitGlyphs(bool increment = false); void InitFood(); void InitMounts(); @@ -140,7 +85,6 @@ public: void InitKeyring(); void InitReputation(); void InitAttunementQuests(); - void InitPotions(); private: void Prepare(); @@ -177,7 +121,6 @@ private: void InitGuild(); void InitArenaTeam(); void InitImmersive(); - void AddConsumables(); static void AddPrevQuests(uint32 questId, std::list& questIds); void LoadEnchantContainer(); void ApplyEnchantTemplate(); diff --git a/src/strategy/actions/ActionContext.h b/src/strategy/actions/ActionContext.h index 2beee322..7d458e2b 100644 --- a/src/strategy/actions/ActionContext.h +++ b/src/strategy/actions/ActionContext.h @@ -38,6 +38,7 @@ #include "InviteToGroupAction.h" #include "LeaveGroupAction.h" #include "LootAction.h" +#include "LootRollAction.h" #include "MoveToRpgTargetAction.h" #include "MoveToTravelTargetAction.h" #include "MovementActions.h" @@ -190,6 +191,7 @@ public: creators["buy tabard"] = &ActionContext::buy_tabard; creators["guild manage nearby"] = &ActionContext::guild_manage_nearby; creators["clean quest log"] = &ActionContext::clean_quest_log; + creators["roll"] = &ActionContext::roll_action; creators["cancel channel"] = &ActionContext::cancel_channel; // BG Tactics @@ -378,6 +380,7 @@ private: static Action* buy_tabard(PlayerbotAI* botAI) { return new BuyTabardAction(botAI); } static Action* guild_manage_nearby(PlayerbotAI* botAI) { return new GuildManageNearbyAction(botAI); } static Action* clean_quest_log(PlayerbotAI* botAI) { return new CleanQuestLogAction(botAI); } + static Action* roll_action(PlayerbotAI* botAI) { return new RollAction(botAI); } // BG Tactics static Action* bg_tactics(PlayerbotAI* botAI) { return new BGTactics(botAI); } diff --git a/src/strategy/actions/AutoMaintenanceOnLevelupAction.cpp b/src/strategy/actions/AutoMaintenanceOnLevelupAction.cpp index 600c5d14..9bced53b 100644 --- a/src/strategy/actions/AutoMaintenanceOnLevelupAction.cpp +++ b/src/strategy/actions/AutoMaintenanceOnLevelupAction.cpp @@ -157,16 +157,23 @@ void AutoMaintenanceOnLevelupAction::LearnSpell(uint32 spellId, std::ostringstre void AutoMaintenanceOnLevelupAction::AutoUpgradeEquip() { if (!sPlayerbotAIConfig->autoUpgradeEquip || !sRandomPlayerbotMgr->IsRandomBot(bot)) - { return; - } + PlayerbotFactory factory(bot, bot->GetLevel()); + + // Clean up old consumables before adding new ones + factory.CleanupConsumables(); + + factory.InitAmmo(); + factory.InitReagents(); + factory.InitFood(); + factory.InitConsumables(); + factory.InitPotions(); + if (!sPlayerbotAIConfig->equipmentPersistence || bot->GetLevel() < sPlayerbotAIConfig->equipmentPersistenceLevel) { if (sPlayerbotAIConfig->incrementalGearInit) factory.InitEquipment(true); } - factory.InitAmmo(); - return; } diff --git a/src/strategy/actions/BossAuraActions.cpp b/src/strategy/actions/BossAuraActions.cpp index ca77d634..9e42698e 100644 --- a/src/strategy/actions/BossAuraActions.cpp +++ b/src/strategy/actions/BossAuraActions.cpp @@ -50,3 +50,16 @@ bool BossNatureResistanceAction::Execute(Event event) botAI->ChangeStrategy(ADD_STRATEGY_CHAR + hunterNatureResistanceStrategy.getName(), BotState::BOT_STATE_COMBAT); return true; } + +bool BossShadowResistanceAction::isUseful() +{ + BossShadowResistanceTrigger bossShadowResistanceTrigger(botAI, bossName); + return bossShadowResistanceTrigger.IsActive(); +} + +bool BossShadowResistanceAction::Execute(Event event) +{ + PaladinShadowResistanceStrategy paladinShadowResistanceStrategy(botAI); + botAI->ChangeStrategy(ADD_STRATEGY_CHAR + paladinShadowResistanceStrategy.getName(), BotState::BOT_STATE_COMBAT); + return true; +} diff --git a/src/strategy/actions/BossAuraActions.h b/src/strategy/actions/BossAuraActions.h index b1bc0dd9..d40b3c76 100644 --- a/src/strategy/actions/BossAuraActions.h +++ b/src/strategy/actions/BossAuraActions.h @@ -54,4 +54,18 @@ private: std::string bossName; }; +class BossShadowResistanceAction : public Action +{ +public: + BossShadowResistanceAction(PlayerbotAI* botAI, std::string const bossName) + : Action(botAI, bossName + " shadow resistance action"), bossName(bossName) + { + } + bool Execute(Event event) override; + bool isUseful() override; + +private: + std::string bossName; +}; + #endif diff --git a/src/strategy/actions/ChatActionContext.h b/src/strategy/actions/ChatActionContext.h index 9de7c511..4521292e 100644 --- a/src/strategy/actions/ChatActionContext.h +++ b/src/strategy/actions/ChatActionContext.h @@ -36,6 +36,7 @@ #include "ListSpellsAction.h" #include "LogLevelAction.h" #include "LootStrategyAction.h" +#include "LootRollAction.h" #include "MailAction.h" #include "NamedObjectContext.h" #include "NewRpgAction.h" @@ -192,10 +193,11 @@ public: creators["calc"] = &ChatActionContext::calc; creators["wipe"] = &ChatActionContext::wipe; creators["tame"] = &ChatActionContext::tame; - creators["glyphs"] = &ChatActionContext::glyphs; // Added for custom Glyphs - creators["glyph equip"] = &ChatActionContext::glyph_equip; // Added for custom Glyphs + creators["glyphs"] = &ChatActionContext::glyphs; // Added for custom Glyphs + creators["glyph equip"] = &ChatActionContext::glyph_equip; // Added for custom Glyphs creators["pet"] = &ChatActionContext::pet; creators["pet attack"] = &ChatActionContext::pet_attack; + creators["roll"] = &ChatActionContext::roll_action; } private: @@ -303,10 +305,11 @@ private: static Action* calc(PlayerbotAI* ai) { return new TellCalculateItemAction(ai); } static Action* wipe(PlayerbotAI* ai) { return new WipeAction(ai); } static Action* tame(PlayerbotAI* botAI) { return new TameAction(botAI); } - static Action* glyphs(PlayerbotAI* botAI) { return new TellGlyphsAction(botAI); } // Added for custom Glyphs - static Action* glyph_equip(PlayerbotAI* ai) { return new EquipGlyphsAction(ai); } // Added for custom Glyphs + static Action* glyphs(PlayerbotAI* botAI) { return new TellGlyphsAction(botAI); } // Added for custom Glyphs + static Action* glyph_equip(PlayerbotAI* ai) { return new EquipGlyphsAction(ai); } // Added for custom Glyphs static Action* pet(PlayerbotAI* botAI) { return new PetAction(botAI); } static Action* pet_attack(PlayerbotAI* botAI) { return new PetAction(botAI, "attack"); } + static Action* roll_action(PlayerbotAI* botAI) { return new RollAction(botAI); } }; #endif diff --git a/src/strategy/actions/ImbueAction.cpp b/src/strategy/actions/ImbueAction.cpp index 158bd099..55ee5829 100644 --- a/src/strategy/actions/ImbueAction.cpp +++ b/src/strategy/actions/ImbueAction.cpp @@ -19,22 +19,51 @@ bool ImbueWithPoisonAction::Execute(Event event) if (bot->HasAura(SPELL_AURA_MOD_STEALTH)) bot->RemoveAurasByType(SPELL_AURA_MOD_STEALTH); - // hp check if (bot->getStandState() != UNIT_STAND_STATE_STAND) bot->SetStandState(UNIT_STAND_STATE_STAND); - // Search and apply poison to weapons - // Mainhand ... + static const std::vector prioritizedInstantPoisons = { + INSTANT_POISON_IX, INSTANT_POISON_VIII, INSTANT_POISON_VII, INSTANT_POISON_VI, INSTANT_POISON_V, INSTANT_POISON_IV, + INSTANT_POISON_III, INSTANT_POISON_II, INSTANT_POISON + }; + + static const std::vector prioritizedDeadlyPoisons = { + DEADLY_POISON_IX, DEADLY_POISON_VIII, DEADLY_POISON_VII, DEADLY_POISON_VI, DEADLY_POISON_V, DEADLY_POISON_IV, + DEADLY_POISON_III, DEADLY_POISON_II, DEADLY_POISON + }; + + // Check if we have any deadly or instant poisons + Item* deadlyPoison = nullptr; + for (auto id : prioritizedDeadlyPoisons) + { + deadlyPoison = botAI->FindConsumable(id); + if (deadlyPoison) break; + } + + Item* instantPoison = nullptr; + for (auto id : prioritizedInstantPoisons) + { + instantPoison = botAI->FindConsumable(id); + if (instantPoison) break; + } + + // Mainhand Item* poison = nullptr; Item* weapon = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_MAINHAND); if (weapon && weapon->GetEnchantmentId(TEMP_ENCHANTMENT_SLOT) == 0) { - poison = botAI->FindConsumable(INSTANT_POISON_DISPLAYID); - if (!poison) - poison = botAI->FindConsumable(DEADLY_POISON_DISPLAYID); - - if (!poison) - poison = botAI->FindConsumable(WOUND_POISON_DISPLAYID); + if (instantPoison && deadlyPoison) + { + poison = instantPoison; + } + else if (deadlyPoison) + { + poison = deadlyPoison; + } + else if (instantPoison) + { + poison = instantPoison; + } if (poison) { @@ -43,16 +72,23 @@ bool ImbueWithPoisonAction::Execute(Event event) } } - //... and offhand + // Offhand + poison = nullptr; weapon = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND); if (weapon && weapon->GetEnchantmentId(TEMP_ENCHANTMENT_SLOT) == 0) { - poison = botAI->FindConsumable(DEADLY_POISON_DISPLAYID); - if (!poison) - poison = botAI->FindConsumable(WOUND_POISON_DISPLAYID); - - if (!poison) - poison = botAI->FindConsumable(INSTANT_POISON_DISPLAYID); + if (deadlyPoison && instantPoison) + { + poison = deadlyPoison; + } + else if (instantPoison) + { + poison = instantPoison; + } + else if (deadlyPoison) + { + poison = deadlyPoison; + } if (poison) { @@ -141,8 +177,8 @@ bool ImbueWithOilAction::Execute(Event event) return true; } -static const uint32 uPriorizedHealingItemIds[19] = { - HEALTHSTONE_DISPLAYID, +static const uint32 uPrioritizedHealingItemIds[19] = { + HEALTHSTONE, FEL_REGENERATION_POTION, SUPER_HEALING_POTION, CRYSTAL_HEALING_POTION, @@ -182,9 +218,9 @@ bool TryEmergencyAction::Execute(Event event) } // Else loop over the list of health consumable to pick one - for (uint8 i = 0; i < std::size(uPriorizedHealingItemIds); ++i) + for (uint8 i = 0; i < std::size(uPrioritizedHealingItemIds); ++i) { - if (Item* healthItem = botAI->FindConsumable(uPriorizedHealingItemIds[i])) + if (Item* healthItem = botAI->FindConsumable(uPrioritizedHealingItemIds[i])) { botAI->ImbueItem(healthItem); } diff --git a/src/strategy/actions/LootRollAction.cpp b/src/strategy/actions/LootRollAction.cpp index 53007b4e..a73e5c75 100644 --- a/src/strategy/actions/LootRollAction.cpp +++ b/src/strategy/actions/LootRollAction.cpp @@ -224,3 +224,37 @@ bool RollUniqueCheck(ItemTemplate const* proto, Player* bot) } return false; // Item is not equipped or in bags, roll for it } + +bool RollAction::Execute(Event event) +{ + std::string link = event.getParam(); + + if (link.empty()) + { + bot->DoRandomRoll(0,100); + return false; + } + ItemIds itemIds = chat->parseItems(link); + if (itemIds.empty()) + return false; + uint32 itemId = *itemIds.begin(); + ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId); + if (!proto) + { + return false; + } + std::string itemUsageParam; + itemUsageParam = std::to_string(itemId); + + ItemUsage usage = AI_VALUE2(ItemUsage, "item usage", itemUsageParam); + switch (proto->Class) + { + case ITEM_CLASS_WEAPON: + case ITEM_CLASS_ARMOR: + if (usage == ITEM_USAGE_EQUIP || usage == ITEM_USAGE_REPLACE || usage == ITEM_USAGE_BAD_EQUIP) + { + bot->DoRandomRoll(0,100); + } + } + return true; +} diff --git a/src/strategy/actions/LootRollAction.h b/src/strategy/actions/LootRollAction.h index bb0c3d89..2582274c 100644 --- a/src/strategy/actions/LootRollAction.h +++ b/src/strategy/actions/LootRollAction.h @@ -37,4 +37,12 @@ public: bool Execute(Event event) override; }; +class RollAction : public Action +{ +public: + RollAction(PlayerbotAI* botAI) : Action(botAI, "roll") {} + + bool Execute(Event event) override; +}; + #endif diff --git a/src/strategy/actions/TrainerAction.cpp b/src/strategy/actions/TrainerAction.cpp index f2423963..655208bf 100644 --- a/src/strategy/actions/TrainerAction.cpp +++ b/src/strategy/actions/TrainerAction.cpp @@ -175,6 +175,8 @@ bool MaintenanceAction::Execute(Event event) factory.InitAmmo(); factory.InitFood(); factory.InitReagents(); + factory.InitConsumables(); + factory.InitPotions(); factory.InitTalentsTree(true); factory.InitPet(); factory.InitPetTalents(); @@ -186,7 +188,6 @@ bool MaintenanceAction::Execute(Event event) factory.InitMounts(); factory.InitGlyphs(false); factory.InitKeyring(); - factory.InitPotions(); if (bot->GetLevel() >= sPlayerbotAIConfig->minEnchantingBotLevel) factory.ApplyEnchantAndGemsNew(); diff --git a/src/strategy/druid/GenericDruidNonCombatStrategy.cpp b/src/strategy/druid/GenericDruidNonCombatStrategy.cpp index 4ca6c15a..10f2314e 100644 --- a/src/strategy/druid/GenericDruidNonCombatStrategy.cpp +++ b/src/strategy/druid/GenericDruidNonCombatStrategy.cpp @@ -6,6 +6,7 @@ #include "GenericDruidNonCombatStrategy.h" #include "Playerbots.h" +#include "AiFactory.h" class GenericDruidNonCombatStrategyActionNodeFactory : public NamedObjectFactory { @@ -109,18 +110,16 @@ void GenericDruidNonCombatStrategy::InitTriggers(std::vector& trig { NonCombatStrategy::InitTriggers(triggers); - triggers.push_back( - new TriggerNode("mark of the wild", NextAction::array(0, new NextAction("mark of the wild", 14.0f), nullptr))); + triggers.push_back(new TriggerNode("mark of the wild", NextAction::array(0, new NextAction("mark of the wild", 14.0f), nullptr))); // triggers.push_back(new TriggerNode("thorns", NextAction::array(0, new NextAction("thorns", 12.0f), nullptr))); // triggers.push_back(new TriggerNode("cure poison", NextAction::array(0, new NextAction("abolish poison", 21.0f), // nullptr))); - triggers.push_back(new TriggerNode( - "party member cure poison", NextAction::array(0, new NextAction("abolish poison on party", 20.0f), nullptr))); - triggers.push_back(new TriggerNode( - "party member dead", NextAction::array(0, new NextAction("revive", ACTION_CRITICAL_HEAL + 10), nullptr))); + triggers.push_back(new TriggerNode("party member cure poison", NextAction::array(0, new NextAction("abolish poison on party", 20.0f), nullptr))); + triggers.push_back(new TriggerNode("party member dead", NextAction::array(0, new NextAction("revive", ACTION_CRITICAL_HEAL + 10), nullptr))); // triggers.push_back(new TriggerNode("low mana", NextAction::array(0, new NextAction("innervate", ACTION_EMERGENCY // + 5), nullptr))); triggers.push_back(new TriggerNode("swimming", NextAction::array(0, new NextAction("aquatic // form", 1.0f), nullptr))); + triggers.push_back(new TriggerNode("often", NextAction::array(0, new NextAction("apply oil", 1.0f), nullptr))); triggers.push_back( @@ -155,6 +154,36 @@ void GenericDruidNonCombatStrategy::InitTriggers(std::vector& trig NextAction::array(0, new NextAction("remove curse on party", ACTION_DISPEL + 7), nullptr))); triggers.push_back( new TriggerNode("new pet", NextAction::array(0, new NextAction("set pet stance", 60.0f), nullptr))); + + triggers.push_back(new TriggerNode("party member critical health", NextAction::array(0, + new NextAction("wild growth on party", ACTION_MEDIUM_HEAL + 7), + new NextAction("regrowth on party", ACTION_MEDIUM_HEAL + 6), + new NextAction("rejuvenation on party", ACTION_MEDIUM_HEAL + 5), + nullptr))); + triggers.push_back(new TriggerNode("party member low health", NextAction::array(0, + new NextAction("wild growth on party", ACTION_MEDIUM_HEAL + 5), + new NextAction("regrowth on party", ACTION_MEDIUM_HEAL + 4), + new NextAction("rejuvenation on party", ACTION_MEDIUM_HEAL + 3), + nullptr))); + triggers.push_back(new TriggerNode("party member medium health", NextAction::array(0, + new NextAction("wild growth on party", ACTION_MEDIUM_HEAL + 3), + new NextAction("regrowth on party", ACTION_MEDIUM_HEAL + 2), + new NextAction("rejuvenation on party", ACTION_MEDIUM_HEAL + 1), + nullptr))); + triggers.push_back(new TriggerNode("party member almost full health", NextAction::array(0, + new NextAction("wild growth on party", ACTION_LIGHT_HEAL + 3), + new NextAction("rejuvenation on party", ACTION_LIGHT_HEAL + 2), + nullptr))); + triggers.push_back(new TriggerNode("party member remove curse", NextAction::array(0, + new NextAction("remove curse on party", ACTION_DISPEL + 7), + nullptr))); + + int specTab = AiFactory::GetPlayerSpecTab(botAI->GetBot()); + if (specTab == 0 || specTab == 2) // Balance or Restoration + triggers.push_back(new TriggerNode("often", NextAction::array(0, new NextAction("apply oil", 1.0f), nullptr))); + if (specTab == 1) // Feral + triggers.push_back(new TriggerNode("often", NextAction::array(0, new NextAction("apply stone", 1.0f), nullptr))); + } GenericDruidBuffStrategy::GenericDruidBuffStrategy(PlayerbotAI* botAI) : NonCombatStrategy(botAI) @@ -166,11 +195,13 @@ void GenericDruidBuffStrategy::InitTriggers(std::vector& triggers) { NonCombatStrategy::InitTriggers(triggers); - triggers.push_back( - new TriggerNode("mark of the wild on party", - NextAction::array(0, new NextAction("mark of the wild on party", 13.0f), nullptr))); - triggers.push_back(new TriggerNode("thorns on main tank", - NextAction::array(0, new NextAction("thorns on main tank", 11.0f), nullptr))); - triggers.push_back(new TriggerNode("thorns", - NextAction::array(0, new NextAction("thorns", 10.0f), nullptr))); + triggers.push_back(new TriggerNode("mark of the wild on party", NextAction::array(0, + new NextAction("mark of the wild on party", 13.0f), + nullptr))); + triggers.push_back(new TriggerNode("thorns on main tank", NextAction::array(0, + new NextAction("thorns on main tank", 11.0f), + nullptr))); + triggers.push_back(new TriggerNode("thorns", NextAction::array(0, + new NextAction("thorns", 10.0f), + nullptr))); } diff --git a/src/strategy/generic/ChatCommandHandlerStrategy.cpp b/src/strategy/generic/ChatCommandHandlerStrategy.cpp index 681cfacc..c4fce31e 100644 --- a/src/strategy/generic/ChatCommandHandlerStrategy.cpp +++ b/src/strategy/generic/ChatCommandHandlerStrategy.cpp @@ -107,6 +107,7 @@ void ChatCommandHandlerStrategy::InitTriggers(std::vector& trigger triggers.push_back(new TriggerNode("glyph equip", NextAction::array(0, new NextAction("glyph equip", relevance), nullptr))); // Added for custom Glyphs triggers.push_back(new TriggerNode("pet", NextAction::array(0, new NextAction("pet", relevance), nullptr))); triggers.push_back(new TriggerNode("pet attack", NextAction::array(0, new NextAction("pet attack", relevance), nullptr))); + triggers.push_back(new TriggerNode("roll", NextAction::array(0, new NextAction("roll", relevance), nullptr))); } ChatCommandHandlerStrategy::ChatCommandHandlerStrategy(PlayerbotAI* botAI) : PassTroughStrategy(botAI) diff --git a/src/strategy/hunter/GenericHunterNonCombatStrategy.cpp b/src/strategy/hunter/GenericHunterNonCombatStrategy.cpp index 7167cd17..f4084922 100644 --- a/src/strategy/hunter/GenericHunterNonCombatStrategy.cpp +++ b/src/strategy/hunter/GenericHunterNonCombatStrategy.cpp @@ -44,15 +44,14 @@ void GenericHunterNonCombatStrategy::InitTriggers(std::vector& tri { NonCombatStrategy::InitTriggers(triggers); - triggers.push_back( - new TriggerNode("trueshot aura", NextAction::array(0, new NextAction("trueshot aura", 2.0f), nullptr))); - triggers.push_back(new TriggerNode("often", NextAction::array(0, new NextAction("apply oil", 1.0f), nullptr))); - triggers.push_back( - new TriggerNode("low ammo", NextAction::array(0, new NextAction("say::low ammo", ACTION_NORMAL), nullptr))); - triggers.push_back( - new TriggerNode("no track", NextAction::array(0, new NextAction("track humanoids", ACTION_NORMAL), nullptr))); - triggers.push_back(new TriggerNode("no ammo", - NextAction::array(0, new NextAction("equip upgrades", ACTION_HIGH + 1), nullptr))); + triggers.push_back(new TriggerNode("trueshot aura", NextAction::array(0, new NextAction("trueshot aura", 2.0f), nullptr))); + triggers.push_back(new TriggerNode("often", NextAction::array(0, + new NextAction("apply stone", 1.0f), + new NextAction("apply oil", 1.0f), + nullptr))); + triggers.push_back(new TriggerNode("low ammo", NextAction::array(0, new NextAction("say::low ammo", ACTION_NORMAL), nullptr))); + triggers.push_back(new TriggerNode("no track", NextAction::array(0, new NextAction("track humanoids", ACTION_NORMAL), nullptr))); + triggers.push_back(new TriggerNode("no ammo", NextAction::array(0, new NextAction("equip upgrades", ACTION_HIGH + 1), nullptr))); // triggers.push_back(new TriggerNode("no ammo", NextAction::array(0, new NextAction("switch to melee", // ACTION_NORMAL + 1), new NextAction("say::no ammo", ACTION_NORMAL), nullptr))); triggers.push_back(new // TriggerNode("has ammo", NextAction::array(0, new NextAction("switch to ranged", ACTION_NORMAL), nullptr))); @@ -61,14 +60,9 @@ void GenericHunterNonCombatStrategy::InitTriggers(std::vector& tri void HunterPetStrategy::InitTriggers(std::vector& triggers) { triggers.push_back(new TriggerNode("no pet", NextAction::array(0, new NextAction("call pet", 60.0f), nullptr))); - triggers.push_back( - new TriggerNode("has pet", NextAction::array(0, new NextAction("toggle pet spell", 60.0f), nullptr))); - triggers.push_back( - new TriggerNode("new pet", NextAction::array(0, new NextAction("set pet stance", 60.0f), nullptr))); - triggers.push_back( - new TriggerNode("pet not happy", NextAction::array(0, new NextAction("feed pet", 60.0f), nullptr))); - triggers.push_back( - new TriggerNode("hunters pet medium health", NextAction::array(0, new NextAction("mend pet", 60.0f), nullptr))); - triggers.push_back( - new TriggerNode("hunters pet dead", NextAction::array(0, new NextAction("revive pet", 60.0f), nullptr))); + triggers.push_back(new TriggerNode("has pet", NextAction::array(0, new NextAction("toggle pet spell", 60.0f), nullptr))); + triggers.push_back(new TriggerNode("new pet", NextAction::array(0, new NextAction("set pet stance", 60.0f), nullptr))); + triggers.push_back(new TriggerNode("pet not happy", NextAction::array(0, new NextAction("feed pet", 60.0f), nullptr))); + triggers.push_back(new TriggerNode("hunters pet medium health", NextAction::array(0, new NextAction("mend pet", 60.0f), nullptr))); + triggers.push_back(new TriggerNode("hunters pet dead", NextAction::array(0, new NextAction("revive pet", 60.0f), nullptr))); } diff --git a/src/strategy/paladin/GenericPaladinNonCombatStrategy.cpp b/src/strategy/paladin/GenericPaladinNonCombatStrategy.cpp index d19320bd..ad2fcaf3 100644 --- a/src/strategy/paladin/GenericPaladinNonCombatStrategy.cpp +++ b/src/strategy/paladin/GenericPaladinNonCombatStrategy.cpp @@ -7,6 +7,7 @@ #include "GenericPaladinStrategyActionNodeFactory.h" #include "Playerbots.h" +#include "AiFactory.h" GenericPaladinNonCombatStrategy::GenericPaladinNonCombatStrategy(PlayerbotAI* botAI) : NonCombatStrategy(botAI) { @@ -17,14 +18,15 @@ void GenericPaladinNonCombatStrategy::InitTriggers(std::vector& tr { NonCombatStrategy::InitTriggers(triggers); - triggers.push_back(new TriggerNode( - "party member dead", NextAction::array(0, new NextAction("redemption", ACTION_CRITICAL_HEAL + 10), nullptr))); - triggers.push_back(new TriggerNode("party member almost full health", - NextAction::array(0, new NextAction("flash of light on party", 25.0f), NULL))); - triggers.push_back(new TriggerNode("party member medium health", - NextAction::array(0, new NextAction("flash of light on party", 26.0f), NULL))); - triggers.push_back(new TriggerNode("party member low health", - NextAction::array(0, new NextAction("holy light on party", 27.0f), NULL))); - triggers.push_back(new TriggerNode("party member critical health", - NextAction::array(0, new NextAction("holy light on party", 28.0f), NULL))); + triggers.push_back(new TriggerNode("party member dead", NextAction::array(0, new NextAction("redemption", ACTION_CRITICAL_HEAL + 10), nullptr))); + triggers.push_back(new TriggerNode("party member almost full health", NextAction::array(0, new NextAction("flash of light on party", 25.0f), nullptr))); + triggers.push_back(new TriggerNode("party member medium health", NextAction::array(0, new NextAction("flash of light on party", 26.0f), nullptr))); + triggers.push_back(new TriggerNode("party member low health", NextAction::array(0, new NextAction("holy light on party", 27.0f), nullptr))); + triggers.push_back(new TriggerNode("party member critical health", NextAction::array(0, new NextAction("holy light on party", 28.0f), nullptr))); + + int specTab = AiFactory::GetPlayerSpecTab(botAI->GetBot()); + if (specTab == 0 || specTab == 1) // Holy or Protection + triggers.push_back(new TriggerNode("often", NextAction::array(0, new NextAction("apply oil", 1.0f), nullptr))); + if (specTab == 2) // Retribution + triggers.push_back(new TriggerNode("often", NextAction::array(0, new NextAction("apply stone", 1.0f), nullptr))); } diff --git a/src/strategy/raids/ulduar/RaidUlduarStrategy.cpp b/src/strategy/raids/ulduar/RaidUlduarStrategy.cpp index b7f0ba41..1b08fa7a 100644 --- a/src/strategy/raids/ulduar/RaidUlduarStrategy.cpp +++ b/src/strategy/raids/ulduar/RaidUlduarStrategy.cpp @@ -240,6 +240,21 @@ void RaidUlduarStrategy::InitTriggers(std::vector& triggers) triggers.push_back(new TriggerNode( "vezax mark of the faceless trigger", NextAction::array(0, new NextAction("vezax mark of the faceless action", ACTION_RAID), nullptr))); + + triggers.push_back(new TriggerNode( + "vezax shadow resistance trigger", + NextAction::array(0, new NextAction("vezax shadow resistance action", ACTION_RAID), nullptr))); + + // + // Yogg-Saron + // + triggers.push_back(new TriggerNode( + "sara shadow resistance trigger", + NextAction::array(0, new NextAction("sara shadow resistance action", ACTION_RAID), nullptr))); + + triggers.push_back(new TriggerNode( + "yogg-saron shadow resistance trigger", + NextAction::array(0, new NextAction("yogg-saron shadow resistance action", ACTION_RAID), nullptr))); } void RaidUlduarStrategy::InitMultipliers(std::vector& multipliers) diff --git a/src/strategy/triggers/BossAuraTriggers.cpp b/src/strategy/triggers/BossAuraTriggers.cpp index f9e705f8..a91976d7 100644 --- a/src/strategy/triggers/BossAuraTriggers.cpp +++ b/src/strategy/triggers/BossAuraTriggers.cpp @@ -23,7 +23,9 @@ bool BossFireResistanceTrigger::IsActive() return false; // Check if bot have fire resistance aura - if (bot->HasAura(SPELL_FIRE_RESISTANCE_AURA)) + if (bot->HasAura(SPELL_FIRE_RESISTANCE_AURA_RANK_5) || bot->HasAura(SPELL_FIRE_RESISTANCE_AURA_RANK_4) || + bot->HasAura(SPELL_FIRE_RESISTANCE_AURA_RANK_3) || bot->HasAura(SPELL_FIRE_RESISTANCE_AURA_RANK_2) || + bot->HasAura(SPELL_FIRE_RESISTANCE_AURA_RANK_1)) return false; // Check if bot dont have already have fire resistance strategy @@ -32,7 +34,11 @@ bool BossFireResistanceTrigger::IsActive() return false; // Check that the bot actually knows the spell - if (!bot->HasActiveSpell(SPELL_FIRE_RESISTANCE_AURA)) + if (!bot->HasActiveSpell(SPELL_FIRE_RESISTANCE_AURA_RANK_5) && + !bot->HasActiveSpell(SPELL_FIRE_RESISTANCE_AURA_RANK_4) && + !bot->HasActiveSpell(SPELL_FIRE_RESISTANCE_AURA_RANK_3) && + !bot->HasActiveSpell(SPELL_FIRE_RESISTANCE_AURA_RANK_2) && + !bot->HasActiveSpell(SPELL_FIRE_RESISTANCE_AURA_RANK_1)) return false; // Get the group and ensure it's a raid group @@ -47,7 +53,7 @@ bool BossFireResistanceTrigger::IsActive() if (!member || !member->IsAlive()) continue; - // Check if the member is a hunter + // Check if the member is a paladin if (member->getClass() == CLASS_PALADIN) { // Return true only if the current bot is the first alive paladin @@ -70,7 +76,9 @@ bool BossFrostResistanceTrigger::IsActive() return false; // Check if bot have frost resistance aura - if (bot->HasAura(SPELL_FROST_RESISTANCE_AURA)) + if (bot->HasAura(SPELL_FROST_RESISTANCE_AURA_RANK_5) || bot->HasAura(SPELL_FROST_RESISTANCE_AURA_RANK_4) || + bot->HasAura(SPELL_FROST_RESISTANCE_AURA_RANK_3) || bot->HasAura(SPELL_FROST_RESISTANCE_AURA_RANK_2) || + bot->HasAura(SPELL_FROST_RESISTANCE_AURA_RANK_1)) return false; // Check if bot dont have already have frost resistance strategy @@ -79,7 +87,11 @@ bool BossFrostResistanceTrigger::IsActive() return false; // Check that the bot actually knows the spell - if (!bot->HasActiveSpell(SPELL_FROST_RESISTANCE_AURA)) + if (!bot->HasActiveSpell(SPELL_FROST_RESISTANCE_AURA_RANK_5) && + !bot->HasActiveSpell(SPELL_FROST_RESISTANCE_AURA_RANK_4) && + !bot->HasActiveSpell(SPELL_FROST_RESISTANCE_AURA_RANK_3) && + !bot->HasActiveSpell(SPELL_FROST_RESISTANCE_AURA_RANK_2) && + !bot->HasActiveSpell(SPELL_FROST_RESISTANCE_AURA_RANK_1)) return false; // Get the group and ensure it's a raid group @@ -94,7 +106,7 @@ bool BossFrostResistanceTrigger::IsActive() if (!member || !member->IsAlive()) continue; - // Check if the member is a hunter + // Check if the member is a paladin if (member->getClass() == CLASS_PALADIN) { // Return true only if the current bot is the first alive paladin @@ -121,7 +133,8 @@ bool BossNatureResistanceTrigger::IsActive() return false; // Check if bot have nature resistance aura - if (bot->HasAura(SPELL_ASPECT_OF_THE_WILD)) + if (bot->HasAura(SPELL_ASPECT_OF_THE_WILD_RANK_4) || bot->HasAura(SPELL_ASPECT_OF_THE_WILD_RANK_3) || + bot->HasAura(SPELL_ASPECT_OF_THE_WILD_RANK_2) || bot->HasAura(SPELL_ASPECT_OF_THE_WILD_RANK_1)) return false; // Check if bot dont have already setted nature resistance aura @@ -130,7 +143,10 @@ bool BossNatureResistanceTrigger::IsActive() return false; // Check that the bot actually knows Aspect of the Wild - if (!bot->HasActiveSpell(SPELL_ASPECT_OF_THE_WILD)) + if (!bot->HasActiveSpell(SPELL_ASPECT_OF_THE_WILD_RANK_4) && + !bot->HasActiveSpell(SPELL_ASPECT_OF_THE_WILD_RANK_3) && + !bot->HasActiveSpell(SPELL_ASPECT_OF_THE_WILD_RANK_2) && + !bot->HasActiveSpell(SPELL_ASPECT_OF_THE_WILD_RANK_1)) return false; // Get the group and ensure it's a raid group @@ -155,3 +171,58 @@ bool BossNatureResistanceTrigger::IsActive() return false; } + +bool BossShadowResistanceTrigger::IsActive() +{ + // Check boss and it is alive + Unit* boss = AI_VALUE2(Unit*, "find target", bossName); + if (!boss || !boss->IsAlive()) + return false; + + // Check if bot is paladin + if (bot->getClass() != CLASS_PALADIN) + return false; + + // Check if bot have shadow resistance aura + if (bot->HasAura(SPELL_SHADOW_RESISTANCE_AURA_RANK_5) || + bot->HasAura(SPELL_SHADOW_RESISTANCE_AURA_RANK_4) || + bot->HasAura(SPELL_SHADOW_RESISTANCE_AURA_RANK_3) || + bot->HasAura(SPELL_SHADOW_RESISTANCE_AURA_RANK_2) || + bot->HasAura(SPELL_SHADOW_RESISTANCE_AURA_RANK_1)) + return false; + + // Check if bot dont have already have shadow resistance strategy + PaladinShadowResistanceStrategy paladinShadowResistanceStrategy(botAI); + if (botAI->HasStrategy(paladinShadowResistanceStrategy.getName(), BotState::BOT_STATE_COMBAT)) + return false; + + // Check that the bot actually knows the spell + if (!bot->HasActiveSpell(SPELL_SHADOW_RESISTANCE_AURA_RANK_5) && + !bot->HasActiveSpell(SPELL_SHADOW_RESISTANCE_AURA_RANK_4) && + !bot->HasActiveSpell(SPELL_SHADOW_RESISTANCE_AURA_RANK_3) && + !bot->HasActiveSpell(SPELL_SHADOW_RESISTANCE_AURA_RANK_2) && + !bot->HasActiveSpell(SPELL_SHADOW_RESISTANCE_AURA_RANK_1)) + return false; + + // Get the group and ensure it's a raid group + Group* group = bot->GetGroup(); + if (!group || !group->isRaidGroup()) + return false; + + // Iterate through group members to find the first alive paladin + for (GroupReference* gref = group->GetFirstMember(); gref; gref = gref->next()) + { + Player* member = gref->GetSource(); + if (!member || !member->IsAlive()) + continue; + + // Check if the member is a paladin + if (member->getClass() == CLASS_PALADIN) + { + // Return true only if the current bot is the first alive paladin + return member == bot; + } + } + + return false; +} diff --git a/src/strategy/triggers/BossAuraTriggers.h b/src/strategy/triggers/BossAuraTriggers.h index e88b37a5..63e4002f 100644 --- a/src/strategy/triggers/BossAuraTriggers.h +++ b/src/strategy/triggers/BossAuraTriggers.h @@ -12,9 +12,25 @@ class PlayerbotAI; enum BossAuraIDs { - SPELL_FROST_RESISTANCE_AURA = 48945, - SPELL_FIRE_RESISTANCE_AURA = 48947, - SPELL_ASPECT_OF_THE_WILD = 49071, + SPELL_SHADOW_RESISTANCE_AURA_RANK_1 = 19876, + SPELL_FROST_RESISTANCE_AURA_RANK_1 = 19888, + SPELL_FIRE_RESISTANCE_AURA_RANK_1 = 19891, + SPELL_SHADOW_RESISTANCE_AURA_RANK_2 = 19895, + SPELL_SHADOW_RESISTANCE_AURA_RANK_3 = 19896, + SPELL_FROST_RESISTANCE_AURA_RANK_2 = 19897, + SPELL_FROST_RESISTANCE_AURA_RANK_3 = 19898, + SPELL_FIRE_RESISTANCE_AURA_RANK_2 = 19899, + SPELL_FIRE_RESISTANCE_AURA_RANK_3 = 19900, + SPELL_ASPECT_OF_THE_WILD_RANK_1 = 20043, + SPELL_ASPECT_OF_THE_WILD_RANK_2 = 20190, + SPELL_ASPECT_OF_THE_WILD_RANK_3 = 27045, + SPELL_SHADOW_RESISTANCE_AURA_RANK_4 = 27151, + SPELL_FROST_RESISTANCE_AURA_RANK_4 = 27152, + SPELL_FIRE_RESISTANCE_AURA_RANK_4 = 27153, + SPELL_SHADOW_RESISTANCE_AURA_RANK_5 = 48943, + SPELL_FROST_RESISTANCE_AURA_RANK_5 = 48945, + SPELL_FIRE_RESISTANCE_AURA_RANK_5 = 48947, + SPELL_ASPECT_OF_THE_WILD_RANK_4 = 49071 }; class BossFireResistanceTrigger : public Trigger @@ -47,7 +63,20 @@ class BossNatureResistanceTrigger : public Trigger { public: BossNatureResistanceTrigger(PlayerbotAI* ai, std::string const bossName) - : Trigger(ai, "kologarn nature resistance trigger"), bossName(bossName) + : Trigger(ai, " nature resistance trigger"), bossName(bossName) + { + } + bool IsActive() override; + +private: + std::string bossName; +}; + +class BossShadowResistanceTrigger : public Trigger +{ +public: + BossShadowResistanceTrigger(PlayerbotAI* ai, std::string const bossName) + : Trigger(ai, " shadow resistance trigger"), bossName(bossName) { } bool IsActive() override; diff --git a/src/strategy/triggers/ChatTriggerContext.h b/src/strategy/triggers/ChatTriggerContext.h index cd04375b..a06a94a6 100644 --- a/src/strategy/triggers/ChatTriggerContext.h +++ b/src/strategy/triggers/ChatTriggerContext.h @@ -138,6 +138,7 @@ public: creators["glyph equip"] = &ChatTriggerContext::glyph_equip; // Added for custom Glyphs creators["pet"] = &ChatTriggerContext::pet; creators["pet attack"] = &ChatTriggerContext::pet_attack; + creators["roll"] = &ChatTriggerContext::roll_action; } private: @@ -255,6 +256,7 @@ private: static Trigger* glyph_equip(PlayerbotAI* ai) { return new ChatCommandTrigger(ai, "glyph equip"); } // Added for custom Glyphs static Trigger* pet(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "pet"); } static Trigger* pet_attack(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "pet attack"); } + static Trigger* roll_action(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "roll"); } }; #endif