From 5910866362884cb68422382ea77cd6f9932e01cc Mon Sep 17 00:00:00 2001 From: Yunfan Li <56597220+liyunfan1223@users.noreply.github.com> Date: Tue, 20 May 2025 23:24:33 +0800 Subject: [PATCH] Calculation of the power of items with random properties (#1312) * Score calculation of item random property * Equip auto repair on repop * Item random property calculation * Random Property calculation --- src/ChatHelper.cpp | 43 +++++++ src/ChatHelper.h | 6 + src/factory/PlayerbotFactory.cpp | 2 +- src/factory/StatsCollector.cpp | 13 +- src/factory/StatsCollector.h | 2 +- src/factory/StatsWeightCalculator.cpp | 123 ++++++++++++++----- src/factory/StatsWeightCalculator.h | 11 +- src/strategy/ItemVisitors.h | 14 +++ src/strategy/actions/EquipAction.cpp | 48 +++++--- src/strategy/actions/LootRollAction.cpp | 15 ++- src/strategy/actions/OutfitAction.cpp | 2 +- src/strategy/actions/ReleaseSpiritAction.cpp | 2 + src/strategy/actions/TellLosAction.cpp | 21 ++-- src/strategy/values/ItemUsageValue.cpp | 21 +++- src/strategy/values/ItemUsageValue.h | 2 +- 15 files changed, 253 insertions(+), 72 deletions(-) diff --git a/src/ChatHelper.cpp b/src/ChatHelper.cpp index 1edf45c2..9ba3337f 100644 --- a/src/ChatHelper.cpp +++ b/src/ChatHelper.cpp @@ -305,6 +305,49 @@ ItemIds ChatHelper::parseItems(std::string const text) return itemIds; } +ItemWithRandomProperty ChatHelper::parseItemWithRandomProperty(std::string const text) +{ + ItemWithRandomProperty res; + + size_t itemStart = text.find("Hitem:"); + if (itemStart == std::string::npos) + return res; + + itemStart += 6; + if (itemStart >= text.length()) + return res; + + size_t colonPos = text.find(':', itemStart); + if (colonPos == std::string::npos) + return res; + + std::string itemIdStr = text.substr(itemStart, colonPos - itemStart); + res.itemId = atoi(itemIdStr.c_str()); + + std::vector params; + size_t currentPos = colonPos + 1; + + while (currentPos < text.length()) { + size_t nextColon = text.find(':', currentPos); + if (nextColon == std::string::npos) { + size_t hTag = text.find("|h", currentPos); + if (hTag != std::string::npos) { + params.push_back(text.substr(currentPos, hTag - currentPos)); + } + break; + } + + params.push_back(text.substr(currentPos, nextColon - currentPos)); + currentPos = nextColon + 1; + } + + if (params.size() >= 6) { + res.randomPropertyId = atoi(params[5].c_str()); + } + + return res; +} + std::string const ChatHelper::FormatQuest(Quest const* quest) { if (!quest) diff --git a/src/ChatHelper.h b/src/ChatHelper.h index 9a395f76..20b62f3a 100644 --- a/src/ChatHelper.h +++ b/src/ChatHelper.h @@ -25,6 +25,11 @@ struct ItemTemplate; typedef std::set ItemIds; typedef std::set SpellIds; +struct ItemWithRandomProperty { + uint32 itemId{0}; + int32 randomPropertyId{0}; +}; + class ChatHelper : public PlayerbotAIAware { public: @@ -33,6 +38,7 @@ public: static std::string const formatMoney(uint32 copper); static uint32 parseMoney(std::string const text); static ItemIds parseItems(std::string const text); + static ItemWithRandomProperty parseItemWithRandomProperty(std::string const text); uint32 parseSpell(std::string const text); static std::string parseValue(const std::string& type, const std::string& text); diff --git a/src/factory/PlayerbotFactory.cpp b/src/factory/PlayerbotFactory.cpp index 3eb165db..4f8ac52b 100644 --- a/src/factory/PlayerbotFactory.cpp +++ b/src/factory/PlayerbotFactory.cpp @@ -1742,7 +1742,7 @@ void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance) if (incremental && oldItem) { - float old_score = calculator.CalculateItem(oldItem->GetEntry()); + float old_score = calculator.CalculateItem(oldItem->GetEntry(), oldItem->GetItemRandomPropertyId()); if (bestScoreForSlot < 1.2f * old_score) continue; } diff --git a/src/factory/StatsCollector.cpp b/src/factory/StatsCollector.cpp index 9a562b9a..3ce59a71 100644 --- a/src/factory/StatsCollector.cpp +++ b/src/factory/StatsCollector.cpp @@ -3,6 +3,7 @@ #include #include "DBCStores.h" +#include "ItemEnchantmentMgr.h" #include "ItemTemplate.h" #include "ObjectMgr.h" #include "PlayerbotAI.h" @@ -205,7 +206,7 @@ void StatsCollector::CollectSpellStats(uint32 spellId, float multiplier, int32 s } } -void StatsCollector::CollectEnchantStats(SpellItemEnchantmentEntry const* enchant) +void StatsCollector::CollectEnchantStats(SpellItemEnchantmentEntry const* enchant, uint32 default_enchant_amount) { for (int s = 0; s < MAX_SPELL_ITEM_ENCHANTMENT_EFFECTS; ++s) { @@ -231,6 +232,10 @@ void StatsCollector::CollectEnchantStats(SpellItemEnchantmentEntry const* enchan } case ITEM_ENCHANTMENT_TYPE_STAT: { + // for item random suffix + if (!enchant_amount) + enchant_amount = default_enchant_amount; + if (!enchant_amount) { break; @@ -250,7 +255,7 @@ bool StatsCollector::SpecialSpellFilter(uint32 spellId) // trinket switch (spellId) { - case 60764: // Totem of Splintering + case 60764: // Totem of Splintering if (type_ & (CollectorType::SPELL)) return true; break; @@ -744,7 +749,7 @@ void StatsCollector::HandleApplyAura(const SpellEffectInfo& effectInfo, float mu int32 StatsCollector::AverageValue(const SpellEffectInfo& effectInfo) { - //float basePointsPerLevel = effectInfo.RealPointsPerLevel; //not used, line marked for removal. + // float basePointsPerLevel = effectInfo.RealPointsPerLevel; //not used, line marked for removal. int32 basePoints = effectInfo.BasePoints; int32 randomPoints = int32(effectInfo.DieSides); @@ -767,7 +772,7 @@ bool StatsCollector::CheckSpellValidation(uint32 spellFamilyName, flag96 spelFal { if (PlayerbotAI::Class2SpellFamilyName(cls_) != spellFamilyName) return false; - + bool isHealingSpell = PlayerbotAI::IsHealingSpell(spellFamilyName, spelFalimyFlags); // strict to healer if (strict && (type_ & CollectorType::SPELL_HEAL)) diff --git a/src/factory/StatsCollector.h b/src/factory/StatsCollector.h index 99f100ec..797d7150 100644 --- a/src/factory/StatsCollector.h +++ b/src/factory/StatsCollector.h @@ -66,7 +66,7 @@ public: void Reset(); void CollectItemStats(ItemTemplate const* proto); void CollectSpellStats(uint32 spellId, float multiplier = 1.0f, int32 spellCooldown = -1); - void CollectEnchantStats(SpellItemEnchantmentEntry const* enchant); + void CollectEnchantStats(SpellItemEnchantmentEntry const* enchant, uint32 default_enchant_amount = 0); bool CanBeTriggeredByType(SpellInfo const* spellInfo, uint32 procFlags, bool strict = true); bool CheckSpellValidation(uint32 spellFamilyName, flag96 spelFalimyFlags, bool strict = true); diff --git a/src/factory/StatsWeightCalculator.cpp b/src/factory/StatsWeightCalculator.cpp index 255fa89d..5c410494 100644 --- a/src/factory/StatsWeightCalculator.cpp +++ b/src/factory/StatsWeightCalculator.cpp @@ -9,6 +9,7 @@ #include "AiFactory.h" #include "DBCStores.h" +#include "ItemEnchantmentMgr.h" #include "ItemTemplate.h" #include "ObjectMgr.h" #include "PlayerbotAI.h" @@ -59,7 +60,7 @@ void StatsWeightCalculator::Reset() } } -float StatsWeightCalculator::CalculateItem(uint32 itemId) +float StatsWeightCalculator::CalculateItem(uint32 itemId, int32 randomPropertyIds) { ItemTemplate const* proto = &sObjectMgr->GetItemTemplateStore()->at(itemId); @@ -69,6 +70,9 @@ float StatsWeightCalculator::CalculateItem(uint32 itemId) Reset(); collector_->CollectItemStats(proto); + + if (randomPropertyIds != 0) + CalculateRandomProperty(randomPropertyIds, itemId); if (enable_overflow_penalty_) ApplyOverflowPenalty(player_); @@ -116,6 +120,53 @@ float StatsWeightCalculator::CalculateEnchant(uint32 enchantId) return weight_; } +void StatsWeightCalculator::CalculateRandomProperty(int32 randomPropertyId, uint32 itemId) +{ + if (randomPropertyId > 0) + { + ItemRandomPropertiesEntry const* item_rand = sItemRandomPropertiesStore.LookupEntry(randomPropertyId); + if (!item_rand) + { + return; + } + + for (uint32 i = PROP_ENCHANTMENT_SLOT_0; i < MAX_ENCHANTMENT_SLOT; ++i) + { + uint32 enchantId = item_rand->Enchantment[i - PROP_ENCHANTMENT_SLOT_0]; + SpellItemEnchantmentEntry const* enchant = sSpellItemEnchantmentStore.LookupEntry(enchantId); + if (enchant) + collector_->CollectEnchantStats(enchant); + } + } + else + { + ItemRandomSuffixEntry const* item_rand = sItemRandomSuffixStore.LookupEntry(-randomPropertyId); + if (!item_rand) + { + return; + } + + for (uint32 i = PROP_ENCHANTMENT_SLOT_0; i < MAX_ENCHANTMENT_SLOT; ++i) + { + uint32 enchantId = item_rand->Enchantment[i - PROP_ENCHANTMENT_SLOT_0]; + SpellItemEnchantmentEntry const* enchant = sSpellItemEnchantmentStore.LookupEntry(enchantId); + uint32 enchant_amount = 0; + + for (int k = 0; k < MAX_ITEM_ENCHANTMENT_EFFECTS; ++k) + { + if (item_rand->Enchantment[k] == enchantId) + { + enchant_amount = uint32((item_rand->AllocationPct[k] * GenerateEnchSuffixFactor(itemId)) / 10000); + break; + } + } + + if (enchant) + collector_->CollectEnchantStats(enchant, enchant_amount); + } + } +} + void StatsWeightCalculator::GenerateWeights(Player* player) { GenerateBasicWeights(player); @@ -293,8 +344,8 @@ void StatsWeightCalculator::GenerateBasicWeights(Player* player) stats_weights_[STATS_TYPE_CRIT] += 0.8f; stats_weights_[STATS_TYPE_HASTE] += 1.0f; } - else if ((cls == CLASS_PALADIN && tab == PALADIN_TAB_HOLY) || // holy - (cls == CLASS_SHAMAN && tab == SHAMAN_TAB_RESTORATION)) // heal + else if ((cls == CLASS_PALADIN && tab == PALADIN_TAB_HOLY) || // holy + (cls == CLASS_SHAMAN && tab == SHAMAN_TAB_RESTORATION)) // heal { stats_weights_[STATS_TYPE_INTELLECT] += 0.9f; stats_weights_[STATS_TYPE_SPIRIT] += 0.15f; @@ -303,7 +354,7 @@ void StatsWeightCalculator::GenerateBasicWeights(Player* player) stats_weights_[STATS_TYPE_CRIT] += 0.6f; stats_weights_[STATS_TYPE_HASTE] += 0.8f; } - else if ((cls == CLASS_PRIEST && tab != PRIEST_TAB_SHADOW) || // discipline / holy + else if ((cls == CLASS_PRIEST && tab != PRIEST_TAB_SHADOW) || // discipline / holy (cls == CLASS_DRUID && tab == DRUID_TAB_RESTORATION)) { stats_weights_[STATS_TYPE_INTELLECT] += 0.8f; @@ -464,9 +515,9 @@ void StatsWeightCalculator::CalculateItemTypePenalty(ItemTemplate const* proto) // { // weight_ *= 1.0; // } - // double hand if (proto->Class == ITEM_CLASS_WEAPON) { + // double hand bool isDoubleHand = proto->Class == ITEM_CLASS_WEAPON && !(ITEM_SUBCLASS_MASK_SINGLE_HAND & (1 << proto->SubClass)) && !(ITEM_SUBCLASS_MASK_WEAPON_RANGED & (1 << proto->SubClass)); @@ -474,29 +525,41 @@ void StatsWeightCalculator::CalculateItemTypePenalty(ItemTemplate const* proto) if (isDoubleHand) { weight_ *= 0.5; - } - // spec without double hand - // enhancement, rogue, ice dk, unholy dk, shield tank, fury warrior without titan's grip but with duel wield - if (isDoubleHand && - ((cls == CLASS_SHAMAN && tab == SHAMAN_TAB_ENHANCEMENT && player_->CanDualWield()) || - (cls == CLASS_ROGUE) || (cls == CLASS_DEATH_KNIGHT && tab == DEATHKNIGHT_TAB_FROST) || - (cls == CLASS_WARRIOR && tab == WARRIOR_TAB_FURY && !player_->CanTitanGrip() && player_->CanDualWield()) || - (cls == CLASS_WARRIOR && tab == WARRIOR_TAB_PROTECTION) || - (cls == CLASS_PALADIN && tab == PALADIN_TAB_PROTECTION))) - { - weight_ *= 0.1; + // spec without double hand + // enhancement, rogue, ice dk, unholy dk, shield tank, fury warrior without titan's grip but with duel wield + if (((cls == CLASS_SHAMAN && tab == SHAMAN_TAB_ENHANCEMENT && player_->CanDualWield()) || + (cls == CLASS_ROGUE) || (cls == CLASS_DEATH_KNIGHT && tab == DEATHKNIGHT_TAB_FROST) || + (cls == CLASS_WARRIOR && tab == WARRIOR_TAB_FURY && !player_->CanTitanGrip() && player_->CanDualWield()) || + (cls == CLASS_WARRIOR && tab == WARRIOR_TAB_PROTECTION) || + (cls == CLASS_PALADIN && tab == PALADIN_TAB_PROTECTION))) + { + weight_ *= 0.1; + } + } // spec with double hand // fury without duel wield, arms, bear, retribution, blood dk - if (!isDoubleHand && - ((cls == CLASS_HUNTER && !player_->CanDualWield()) || - (cls == CLASS_WARRIOR && tab == WARRIOR_TAB_FURY && !player_->CanDualWield()) || - (cls == CLASS_WARRIOR && tab == WARRIOR_TAB_ARMS) || (cls == CLASS_DRUID && tab == DRUID_TAB_FERAL) || - (cls == CLASS_PALADIN && tab == PALADIN_TAB_RETRIBUTION) || - (cls == CLASS_DEATH_KNIGHT && tab == DEATHKNIGHT_TAB_BLOOD) || - (cls == CLASS_SHAMAN && tab == SHAMAN_TAB_ENHANCEMENT && !player_->CanDualWield()))) + if (!isDoubleHand) { - weight_ *= 0.1; + if ((cls == CLASS_HUNTER && !player_->CanDualWield()) || + (cls == CLASS_WARRIOR && tab == WARRIOR_TAB_FURY && !player_->CanDualWield()) || + (cls == CLASS_WARRIOR && tab == WARRIOR_TAB_ARMS) || (cls == CLASS_DRUID && tab == DRUID_TAB_FERAL) || + (cls == CLASS_PALADIN && tab == PALADIN_TAB_RETRIBUTION) || + (cls == CLASS_DEATH_KNIGHT && tab == DEATHKNIGHT_TAB_BLOOD) || + (cls == CLASS_SHAMAN && tab == SHAMAN_TAB_ENHANCEMENT && !player_->CanDualWield())) + { + weight_ *= 0.1; + } + // caster's main hand (cannot duel weapon but can equip two-hands stuff) + if (cls == CLASS_MAGE || + cls == CLASS_PRIEST || + cls == CLASS_WARLOCK || + cls == CLASS_DRUID || + (cls == CLASS_SHAMAN && !player_->CanDualWield())) + { + weight_ *= 0.65; + } + } // fury with titan's grip if ((!isDoubleHand || proto->SubClass == ITEM_SUBCLASS_WEAPON_POLEARM || @@ -505,15 +568,18 @@ void StatsWeightCalculator::CalculateItemTypePenalty(ItemTemplate const* proto) { weight_ *= 0.1; } + if (cls == CLASS_HUNTER && proto->SubClass == ITEM_SUBCLASS_WEAPON_THROWN) { weight_ *= 0.1; } + if (cls == CLASS_ROGUE && (tab == ROGUE_TAB_ASSASSINATION || tab == ROGUE_TAB_SUBTLETY) && proto->SubClass != ITEM_SUBCLASS_WEAPON_DAGGER) { weight_ *= 0.5; } + if (cls == CLASS_ROGUE && player_->HasAura(13964) && (proto->SubClass == ITEM_SUBCLASS_WEAPON_SWORD || proto->SubClass == ITEM_SUBCLASS_WEAPON_AXE)) { @@ -559,12 +625,13 @@ void StatsWeightCalculator::ApplyOverflowPenalty(Player* player) if (hitOverflowType_ & CollectorType::SPELL) { hit_current = player->GetTotalAuraModifier(SPELL_AURA_MOD_SPELL_HIT_CHANCE); - hit_current += player->GetTotalAuraModifier(SPELL_AURA_MOD_INCREASES_SPELL_PCT_TO_HIT); // suppression (18176) + hit_current += + player->GetTotalAuraModifier(SPELL_AURA_MOD_INCREASES_SPELL_PCT_TO_HIT); // suppression (18176) hit_current += player->GetRatingBonusValue(CR_HIT_SPELL); - if (cls == CLASS_PRIEST && tab == PRIEST_TAB_SHADOW && player->HasAura(15835)) // Shadow Focus + if (cls == CLASS_PRIEST && tab == PRIEST_TAB_SHADOW && player->HasAura(15835)) // Shadow Focus hit_current += 3; - if (cls == CLASS_MAGE && tab == MAGE_TAB_ARCANE && player->HasAura(12840)) // Arcane Focus + if (cls == CLASS_MAGE && tab == MAGE_TAB_ARCANE && player->HasAura(12840)) // Arcane Focus hit_current += 3; hit_overflow = SPELL_HIT_OVERFLOW; @@ -657,7 +724,7 @@ void StatsWeightCalculator::ApplyWeightFinetune(Player* player) { if (type_ & (CollectorType::MELEE | CollectorType::RANGED)) { - float armor_penetration_current/*, armor_penetration_overflow*/; //not used, line marked for removal. + float armor_penetration_current /*, armor_penetration_overflow*/; // not used, line marked for removal. armor_penetration_current = player->GetRatingBonusValue(CR_ARMOR_PENETRATION); if (armor_penetration_current > 50) stats_weights_[STATS_TYPE_ARMOR_PENETRATION] *= 1.2f; diff --git a/src/factory/StatsWeightCalculator.h b/src/factory/StatsWeightCalculator.h index d978b9c8..10640d30 100644 --- a/src/factory/StatsWeightCalculator.h +++ b/src/factory/StatsWeightCalculator.h @@ -28,18 +28,19 @@ class StatsWeightCalculator public: StatsWeightCalculator(Player* player); void Reset(); - float CalculateItem(uint32 itemId); + float CalculateItem(uint32 itemId, int32 randomPropertyId = 0); float CalculateEnchant(uint32 enchantId); - + void SetOverflowPenalty(bool apply) { enable_overflow_penalty_ = apply; } void SetItemSetBonus(bool apply) { enable_item_set_bonus_ = apply; } void SetQualityBlend(bool apply) { enable_quality_blend_ = apply; } - -private: + + private: void GenerateWeights(Player* player); void GenerateBasicWeights(Player* player); void GenerateAdditionalWeights(Player* player); - + + void CalculateRandomProperty(int32 randomPropertyId, uint32 itemId); void CalculateItemSetMod(Player* player, ItemTemplate const* proto); void CalculateSocketBonus(Player* player, ItemTemplate const* proto); diff --git a/src/strategy/ItemVisitors.h b/src/strategy/ItemVisitors.h index cc46d877..00941488 100644 --- a/src/strategy/ItemVisitors.h +++ b/src/strategy/ItemVisitors.h @@ -232,6 +232,20 @@ public: } }; +class CollectItemsVisitor : public IterateItemsVisitor +{ +public: + CollectItemsVisitor() : IterateItemsVisitor() {} + + std::vector items; + + bool Visit(Item* item) override + { + items.push_back(item); + return true; + } +}; + class ItemCountByQuality : public IterateItemsVisitor { public: diff --git a/src/strategy/actions/EquipAction.cpp b/src/strategy/actions/EquipAction.cpp index 3b4c3880..a2a390b8 100644 --- a/src/strategy/actions/EquipAction.cpp +++ b/src/strategy/actions/EquipAction.cpp @@ -8,6 +8,7 @@ #include "Event.h" #include "ItemCountValue.h" #include "ItemUsageValue.h" +#include "ItemVisitors.h" #include "Playerbots.h" #include "StatsWeightCalculator.h" @@ -311,19 +312,28 @@ bool EquipUpgradesAction::Execute(Event event) return false; } - ListItemsVisitor visitor; + CollectItemsVisitor visitor; IterateItems(&visitor, ITERATE_ITEMS_IN_BAGS); ItemIds items; - for (std::map::iterator i = visitor.items.begin(); i != visitor.items.end(); ++i) + for (auto i = visitor.items.begin(); i != visitor.items.end(); ++i) { - ItemUsage usage = AI_VALUE2(ItemUsage, "item usage", i->first); + Item* item = *i; + if (!item) + break; + int32 randomProperty = item->GetItemRandomPropertyId(); + uint32 itemId = item->GetTemplate()->ItemId; + std::string itemUsageParam; + if (randomProperty != 0) { + itemUsageParam = std::to_string(itemId) + "," + std::to_string(randomProperty); + } else { + itemUsageParam = std::to_string(itemId); + } + ItemUsage usage = AI_VALUE2(ItemUsage, "item usage", itemUsageParam); + if (usage == ITEM_USAGE_EQUIP || usage == ITEM_USAGE_REPLACE || usage == ITEM_USAGE_BAD_EQUIP) { - // LOG_INFO("playerbots", "Bot {} <{}> EquipUpgradesAction {} ({})", bot->GetGUID().ToString().c_str(), - // bot->GetName().c_str(), i->first, usage == 1 ? "no item in slot" : usage == 2 ? "replace" : usage == 3 ? - // "wrong item but empty slot" : ""); - items.insert(i->first); + items.insert(itemId); } } @@ -333,21 +343,31 @@ bool EquipUpgradesAction::Execute(Event event) bool EquipUpgradeAction::Execute(Event event) { - ListItemsVisitor visitor; + CollectItemsVisitor visitor; IterateItems(&visitor, ITERATE_ITEMS_IN_BAGS); ItemIds items; - for (std::map::iterator i = visitor.items.begin(); i != visitor.items.end(); ++i) + for (auto i = visitor.items.begin(); i != visitor.items.end(); ++i) { - ItemUsage usage = AI_VALUE2(ItemUsage, "item usage", i->first); + Item* item = *i; + if (!item) + break; + int32 randomProperty = item->GetItemRandomPropertyId(); + uint32 itemId = item->GetTemplate()->ItemId; + std::string itemUsageParam; + if (randomProperty != 0) { + itemUsageParam = std::to_string(itemId) + "," + std::to_string(randomProperty); + } else { + itemUsageParam = std::to_string(itemId); + } + ItemUsage usage = AI_VALUE2(ItemUsage, "item usage", itemUsageParam); + if (usage == ITEM_USAGE_EQUIP || usage == ITEM_USAGE_REPLACE || usage == ITEM_USAGE_BAD_EQUIP) { - // LOG_INFO("playerbots", "Bot {} <{}> EquipUpgradeAction item {} ({})", bot->GetGUID().ToString().c_str(), - // bot->GetName().c_str(), i->first, usage == 1 ? "no item in slot" : usage == 2 ? "replace" : usage == 3 ? - // "wrong item but empty slot" : ""); - items.insert(i->first); + items.insert(itemId); } } + EquipItems(items); return true; } diff --git a/src/strategy/actions/LootRollAction.cpp b/src/strategy/actions/LootRollAction.cpp index 0fa28050..e79f43eb 100644 --- a/src/strategy/actions/LootRollAction.cpp +++ b/src/strategy/actions/LootRollAction.cpp @@ -9,6 +9,7 @@ #include "Group.h" #include "ItemUsageValue.h" #include "LootAction.h" +#include "ObjectMgr.h" #include "PlayerbotAIConfig.h" #include "Playerbots.h" @@ -30,12 +31,24 @@ bool LootRollAction::Execute(Event event) ObjectGuid guid = roll->itemGUID; uint32 slot = roll->itemSlot; uint32 itemId = roll->itemid; + int32 randomProperty = 0; + if (roll->itemRandomPropId) + randomProperty = roll->itemRandomPropId; + else if (roll->itemRandomSuffix) + randomProperty = -((int)roll->itemRandomSuffix); RollVote vote = PASS; ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId); if (!proto) continue; - ItemUsage usage = AI_VALUE2(ItemUsage, "item usage", itemId); + + std::string itemUsageParam; + if (randomProperty != 0) { + itemUsageParam = std::to_string(itemId) + "," + std::to_string(randomProperty); + } else { + itemUsageParam = std::to_string(itemId); + } + ItemUsage usage = AI_VALUE2(ItemUsage, "item usage", itemUsageParam); // Armor Tokens are classed as MISC JUNK (Class 15, Subclass 0), luckily no other items I found have class bits and epic quality. if (proto->Class == ITEM_CLASS_MISC && proto->SubClass == ITEM_SUBCLASS_JUNK && proto->Quality == ITEM_QUALITY_EPIC) diff --git a/src/strategy/actions/OutfitAction.cpp b/src/strategy/actions/OutfitAction.cpp index f3c0c715..32a8424e 100644 --- a/src/strategy/actions/OutfitAction.cpp +++ b/src/strategy/actions/OutfitAction.cpp @@ -183,7 +183,7 @@ void OutfitAction::Update(std::string const name) { ListItemsVisitor visitor; IterateItems(&visitor, ITERATE_ITEMS_IN_EQUIP); - + ItemIds items; for (std::map::iterator i = visitor.items.begin(); i != visitor.items.end(); ++i) items.insert(i->first); diff --git a/src/strategy/actions/ReleaseSpiritAction.cpp b/src/strategy/actions/ReleaseSpiritAction.cpp index 554c0ce4..bc44e16f 100644 --- a/src/strategy/actions/ReleaseSpiritAction.cpp +++ b/src/strategy/actions/ReleaseSpiritAction.cpp @@ -43,6 +43,7 @@ bool ReleaseSpiritAction::Execute(Event event) botAI->TellMasterNoFacing(message); IncrementDeathCount(); + bot->DurabilityRepairAll(false, 1.0f, false); LogRelease("released"); WorldPacket releasePacket(CMSG_REPOP_REQUEST); @@ -79,6 +80,7 @@ void ReleaseSpiritAction::LogRelease(const std::string& releaseMsg, bool isAutoR bool AutoReleaseSpiritAction::Execute(Event event) { IncrementDeathCount(); + bot->DurabilityRepairAll(false, 1.0f, false); LogRelease("auto released", true); WorldPacket packet(CMSG_REPOP_REQUEST); diff --git a/src/strategy/actions/TellLosAction.cpp b/src/strategy/actions/TellLosAction.cpp index 03dd3569..125b7df4 100644 --- a/src/strategy/actions/TellLosAction.cpp +++ b/src/strategy/actions/TellLosAction.cpp @@ -140,17 +140,16 @@ bool TellEstimatedDpsAction::Execute(Event event) bool TellCalculateItemAction::Execute(Event event) { std::string const text = event.getParam(); - ItemIds ids = chat->parseItems(text); + ItemWithRandomProperty item = chat->parseItemWithRandomProperty(text); StatsWeightCalculator calculator(bot); - for (const uint32 &id : ids) - { - const ItemTemplate* proto = sObjectMgr->GetItemTemplate(id); - if (!proto) - continue; - float score = calculator.CalculateItem(id); - std::ostringstream out; - out << "Calculated score of " << chat->FormatItem(proto) << " : " << score; - botAI->TellMasterNoFacing(out.str()); - } + + const ItemTemplate* proto = sObjectMgr->GetItemTemplate(item.itemId); + if (!proto) + return false; + float score = calculator.CalculateItem(item.itemId, item.randomPropertyId); + + std::ostringstream out; + out << "Calculated score of " << chat->FormatItem(proto) << " : " << score; + botAI->TellMasterNoFacing(out.str()); return true; } \ No newline at end of file diff --git a/src/strategy/values/ItemUsageValue.cpp b/src/strategy/values/ItemUsageValue.cpp index 65ddb75c..bea4d9cc 100644 --- a/src/strategy/values/ItemUsageValue.cpp +++ b/src/strategy/values/ItemUsageValue.cpp @@ -8,6 +8,7 @@ #include "AiFactory.h" #include "ChatHelper.h" #include "GuildTaskMgr.h" +#include "Item.h" #include "LootObjectStack.h" #include "PlayerbotAIConfig.h" #include "PlayerbotFactory.h" @@ -18,7 +19,16 @@ ItemUsage ItemUsageValue::Calculate() { - uint32 itemId = atoi(qualifier.c_str()); + uint32 itemId = 0; + uint32 randomPropertyId = 0; + size_t pos = qualifier.find(","); + if (pos != std::string::npos) { + itemId = atoi(qualifier.substr(0, pos).c_str()); + randomPropertyId = atoi(qualifier.substr(pos + 1).c_str()); + } else { + itemId = atoi(qualifier.c_str()); + } + if (!itemId) return ITEM_USAGE_NONE; @@ -89,7 +99,7 @@ ItemUsage ItemUsageValue::Calculate() if (bot->GetGuildId() && sGuildTaskMgr->IsGuildTaskItem(itemId, bot->GetGuildId())) return ITEM_USAGE_GUILD_TASK; - ItemUsage equip = QueryItemUsageForEquip(proto); + ItemUsage equip = QueryItemUsageForEquip(proto, randomPropertyId); if (equip != ITEM_USAGE_NONE) return equip; @@ -224,7 +234,7 @@ ItemUsage ItemUsageValue::Calculate() return ITEM_USAGE_NONE; } -ItemUsage ItemUsageValue::QueryItemUsageForEquip(ItemTemplate const* itemProto) +ItemUsage ItemUsageValue::QueryItemUsageForEquip(ItemTemplate const* itemProto, int32 randomPropertyId) { if (bot->CanUseItem(itemProto) != EQUIP_ERR_OK) return ITEM_USAGE_NONE; @@ -296,7 +306,8 @@ ItemUsage ItemUsageValue::QueryItemUsageForEquip(ItemTemplate const* itemProto) calculator.SetItemSetBonus(false); calculator.SetOverflowPenalty(false); - float itemScore = calculator.CalculateItem(itemProto->ItemId); + float itemScore = calculator.CalculateItem(itemProto->ItemId, randomPropertyId); + if (itemScore) shouldEquip = true; @@ -380,7 +391,7 @@ ItemUsage ItemUsageValue::QueryItemUsageForEquip(ItemTemplate const* itemProto) } ItemTemplate const* oldItemProto = oldItem->GetTemplate(); - float oldScore = calculator.CalculateItem(oldItemProto->ItemId); + float oldScore = calculator.CalculateItem(oldItemProto->ItemId, oldItem->GetInt32Value(ITEM_FIELD_RANDOM_PROPERTIES_ID)); if (oldItem) { // uint32 oldStatWeight = sRandomItemMgr->GetLiveStatWeight(bot, oldItemProto->ItemId); diff --git a/src/strategy/values/ItemUsageValue.h b/src/strategy/values/ItemUsageValue.h index 928b2b4a..b1c32d13 100644 --- a/src/strategy/values/ItemUsageValue.h +++ b/src/strategy/values/ItemUsageValue.h @@ -43,7 +43,7 @@ public: ItemUsage Calculate() override; private: - ItemUsage QueryItemUsageForEquip(ItemTemplate const* proto); + ItemUsage QueryItemUsageForEquip(ItemTemplate const* proto, int32 randomPropertyId = 0); uint32 GetSmallestBagSize(); bool IsItemUsefulForQuest(Player* player, ItemTemplate const* proto); bool IsItemNeededForSkill(ItemTemplate const* proto);