From 938872564a89542f0c28feba9cc1db6a429fee47 Mon Sep 17 00:00:00 2001 From: brighton-chi Date: Fri, 1 Aug 2025 12:28:13 -0500 Subject: [PATCH] Revise bot logic for initializing and using consumables (#1483) Bots will now add level- and spec-appropriate oils and stones when maintaining and, with respect to randombots, leveling. All bots (other than those with class-specific temporary weapon enchants) will apply oils and stones to their weapons. General clean-ups to associated code were made. --- src/PlayerbotAI.cpp | 119 +++-- src/PlayerbotAI.h | 131 ++--- src/factory/PlayerbotFactory.cpp | 503 +++++++++++------- src/factory/PlayerbotFactory.h | 63 +-- .../AutoMaintenanceOnLevelupAction.cpp | 15 +- src/strategy/actions/ImbueAction.cpp | 76 ++- src/strategy/actions/TrainerAction.cpp | 3 +- .../druid/GenericDruidNonCombatStrategy.cpp | 84 ++- .../hunter/GenericHunterNonCombatStrategy.cpp | 29 +- .../GenericPaladinNonCombatStrategy.cpp | 22 +- 10 files changed, 591 insertions(+), 454 deletions(-) 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/factory/PlayerbotFactory.cpp b/src/factory/PlayerbotFactory.cpp index f0fa3e0a..6c2c0fe3 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() @@ -3220,102 +3287,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,6 +3390,69 @@ 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(); 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/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/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/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 bc897948..02d975f0 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,50 +110,43 @@ 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(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))); - 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), NULL))); - - 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) @@ -164,11 +158,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/hunter/GenericHunterNonCombatStrategy.cpp b/src/strategy/hunter/GenericHunterNonCombatStrategy.cpp index 203c2fea..382b7db0 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,12 +60,8 @@ 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("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("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))); }