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
This commit is contained in:
Yunfan Li
2025-05-20 23:24:33 +08:00
committed by GitHub
parent c5b185455c
commit 5910866362
15 changed files with 253 additions and 72 deletions

View File

@@ -305,6 +305,49 @@ ItemIds ChatHelper::parseItems(std::string const text)
return itemIds; 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<std::string> 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) std::string const ChatHelper::FormatQuest(Quest const* quest)
{ {
if (!quest) if (!quest)

View File

@@ -25,6 +25,11 @@ struct ItemTemplate;
typedef std::set<uint32> ItemIds; typedef std::set<uint32> ItemIds;
typedef std::set<uint32> SpellIds; typedef std::set<uint32> SpellIds;
struct ItemWithRandomProperty {
uint32 itemId{0};
int32 randomPropertyId{0};
};
class ChatHelper : public PlayerbotAIAware class ChatHelper : public PlayerbotAIAware
{ {
public: public:
@@ -33,6 +38,7 @@ public:
static std::string const formatMoney(uint32 copper); static std::string const formatMoney(uint32 copper);
static uint32 parseMoney(std::string const text); static uint32 parseMoney(std::string const text);
static ItemIds parseItems(std::string const text); static ItemIds parseItems(std::string const text);
static ItemWithRandomProperty parseItemWithRandomProperty(std::string const text);
uint32 parseSpell(std::string const text); uint32 parseSpell(std::string const text);
static std::string parseValue(const std::string& type, const std::string& text); static std::string parseValue(const std::string& type, const std::string& text);

View File

@@ -1742,7 +1742,7 @@ void PlayerbotFactory::InitEquipment(bool incremental, bool second_chance)
if (incremental && oldItem) 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) if (bestScoreForSlot < 1.2f * old_score)
continue; continue;
} }

View File

@@ -3,6 +3,7 @@
#include <cstdint> #include <cstdint>
#include "DBCStores.h" #include "DBCStores.h"
#include "ItemEnchantmentMgr.h"
#include "ItemTemplate.h" #include "ItemTemplate.h"
#include "ObjectMgr.h" #include "ObjectMgr.h"
#include "PlayerbotAI.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) 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: case ITEM_ENCHANTMENT_TYPE_STAT:
{ {
// for item random suffix
if (!enchant_amount)
enchant_amount = default_enchant_amount;
if (!enchant_amount) if (!enchant_amount)
{ {
break; break;
@@ -250,7 +255,7 @@ bool StatsCollector::SpecialSpellFilter(uint32 spellId)
// trinket // trinket
switch (spellId) switch (spellId)
{ {
case 60764: // Totem of Splintering case 60764: // Totem of Splintering
if (type_ & (CollectorType::SPELL)) if (type_ & (CollectorType::SPELL))
return true; return true;
break; break;
@@ -744,7 +749,7 @@ void StatsCollector::HandleApplyAura(const SpellEffectInfo& effectInfo, float mu
int32 StatsCollector::AverageValue(const SpellEffectInfo& effectInfo) 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 basePoints = effectInfo.BasePoints;
int32 randomPoints = int32(effectInfo.DieSides); int32 randomPoints = int32(effectInfo.DieSides);

View File

@@ -66,7 +66,7 @@ public:
void Reset(); void Reset();
void CollectItemStats(ItemTemplate const* proto); void CollectItemStats(ItemTemplate const* proto);
void CollectSpellStats(uint32 spellId, float multiplier = 1.0f, int32 spellCooldown = -1); 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 CanBeTriggeredByType(SpellInfo const* spellInfo, uint32 procFlags, bool strict = true);
bool CheckSpellValidation(uint32 spellFamilyName, flag96 spelFalimyFlags, bool strict = true); bool CheckSpellValidation(uint32 spellFamilyName, flag96 spelFalimyFlags, bool strict = true);

View File

@@ -9,6 +9,7 @@
#include "AiFactory.h" #include "AiFactory.h"
#include "DBCStores.h" #include "DBCStores.h"
#include "ItemEnchantmentMgr.h"
#include "ItemTemplate.h" #include "ItemTemplate.h"
#include "ObjectMgr.h" #include "ObjectMgr.h"
#include "PlayerbotAI.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); ItemTemplate const* proto = &sObjectMgr->GetItemTemplateStore()->at(itemId);
@@ -70,6 +71,9 @@ float StatsWeightCalculator::CalculateItem(uint32 itemId)
collector_->CollectItemStats(proto); collector_->CollectItemStats(proto);
if (randomPropertyIds != 0)
CalculateRandomProperty(randomPropertyIds, itemId);
if (enable_overflow_penalty_) if (enable_overflow_penalty_)
ApplyOverflowPenalty(player_); ApplyOverflowPenalty(player_);
@@ -116,6 +120,53 @@ float StatsWeightCalculator::CalculateEnchant(uint32 enchantId)
return weight_; 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) void StatsWeightCalculator::GenerateWeights(Player* player)
{ {
GenerateBasicWeights(player); GenerateBasicWeights(player);
@@ -293,8 +344,8 @@ void StatsWeightCalculator::GenerateBasicWeights(Player* player)
stats_weights_[STATS_TYPE_CRIT] += 0.8f; stats_weights_[STATS_TYPE_CRIT] += 0.8f;
stats_weights_[STATS_TYPE_HASTE] += 1.0f; stats_weights_[STATS_TYPE_HASTE] += 1.0f;
} }
else if ((cls == CLASS_PALADIN && tab == PALADIN_TAB_HOLY) || // holy else if ((cls == CLASS_PALADIN && tab == PALADIN_TAB_HOLY) || // holy
(cls == CLASS_SHAMAN && tab == SHAMAN_TAB_RESTORATION)) // heal (cls == CLASS_SHAMAN && tab == SHAMAN_TAB_RESTORATION)) // heal
{ {
stats_weights_[STATS_TYPE_INTELLECT] += 0.9f; stats_weights_[STATS_TYPE_INTELLECT] += 0.9f;
stats_weights_[STATS_TYPE_SPIRIT] += 0.15f; 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_CRIT] += 0.6f;
stats_weights_[STATS_TYPE_HASTE] += 0.8f; 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)) (cls == CLASS_DRUID && tab == DRUID_TAB_RESTORATION))
{ {
stats_weights_[STATS_TYPE_INTELLECT] += 0.8f; stats_weights_[STATS_TYPE_INTELLECT] += 0.8f;
@@ -464,9 +515,9 @@ void StatsWeightCalculator::CalculateItemTypePenalty(ItemTemplate const* proto)
// { // {
// weight_ *= 1.0; // weight_ *= 1.0;
// } // }
// double hand
if (proto->Class == ITEM_CLASS_WEAPON) if (proto->Class == ITEM_CLASS_WEAPON)
{ {
// double hand
bool isDoubleHand = proto->Class == ITEM_CLASS_WEAPON && bool isDoubleHand = proto->Class == ITEM_CLASS_WEAPON &&
!(ITEM_SUBCLASS_MASK_SINGLE_HAND & (1 << proto->SubClass)) && !(ITEM_SUBCLASS_MASK_SINGLE_HAND & (1 << proto->SubClass)) &&
!(ITEM_SUBCLASS_MASK_WEAPON_RANGED & (1 << proto->SubClass)); !(ITEM_SUBCLASS_MASK_WEAPON_RANGED & (1 << proto->SubClass));
@@ -474,29 +525,41 @@ void StatsWeightCalculator::CalculateItemTypePenalty(ItemTemplate const* proto)
if (isDoubleHand) if (isDoubleHand)
{ {
weight_ *= 0.5; weight_ *= 0.5;
} // spec without double hand
// spec without double hand // enhancement, rogue, ice dk, unholy dk, shield tank, fury warrior without titan's grip but with duel wield
// 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()) ||
if (isDoubleHand && (cls == CLASS_ROGUE) || (cls == CLASS_DEATH_KNIGHT && tab == DEATHKNIGHT_TAB_FROST) ||
((cls == CLASS_SHAMAN && tab == SHAMAN_TAB_ENHANCEMENT && player_->CanDualWield()) || (cls == CLASS_WARRIOR && tab == WARRIOR_TAB_FURY && !player_->CanTitanGrip() && player_->CanDualWield()) ||
(cls == CLASS_ROGUE) || (cls == CLASS_DEATH_KNIGHT && tab == DEATHKNIGHT_TAB_FROST) || (cls == CLASS_WARRIOR && tab == WARRIOR_TAB_PROTECTION) ||
(cls == CLASS_WARRIOR && tab == WARRIOR_TAB_FURY && !player_->CanTitanGrip() && player_->CanDualWield()) || (cls == CLASS_PALADIN && tab == PALADIN_TAB_PROTECTION)))
(cls == CLASS_WARRIOR && tab == WARRIOR_TAB_PROTECTION) || {
(cls == CLASS_PALADIN && tab == PALADIN_TAB_PROTECTION))) weight_ *= 0.1;
{ }
weight_ *= 0.1;
} }
// spec with double hand // spec with double hand
// fury without duel wield, arms, bear, retribution, blood dk // fury without duel wield, arms, bear, retribution, blood dk
if (!isDoubleHand && 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())))
{ {
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 // fury with titan's grip
if ((!isDoubleHand || proto->SubClass == ITEM_SUBCLASS_WEAPON_POLEARM || if ((!isDoubleHand || proto->SubClass == ITEM_SUBCLASS_WEAPON_POLEARM ||
@@ -505,15 +568,18 @@ void StatsWeightCalculator::CalculateItemTypePenalty(ItemTemplate const* proto)
{ {
weight_ *= 0.1; weight_ *= 0.1;
} }
if (cls == CLASS_HUNTER && proto->SubClass == ITEM_SUBCLASS_WEAPON_THROWN) if (cls == CLASS_HUNTER && proto->SubClass == ITEM_SUBCLASS_WEAPON_THROWN)
{ {
weight_ *= 0.1; weight_ *= 0.1;
} }
if (cls == CLASS_ROGUE && (tab == ROGUE_TAB_ASSASSINATION || tab == ROGUE_TAB_SUBTLETY) && if (cls == CLASS_ROGUE && (tab == ROGUE_TAB_ASSASSINATION || tab == ROGUE_TAB_SUBTLETY) &&
proto->SubClass != ITEM_SUBCLASS_WEAPON_DAGGER) proto->SubClass != ITEM_SUBCLASS_WEAPON_DAGGER)
{ {
weight_ *= 0.5; weight_ *= 0.5;
} }
if (cls == CLASS_ROGUE && player_->HasAura(13964) && if (cls == CLASS_ROGUE && player_->HasAura(13964) &&
(proto->SubClass == ITEM_SUBCLASS_WEAPON_SWORD || proto->SubClass == ITEM_SUBCLASS_WEAPON_AXE)) (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) if (hitOverflowType_ & CollectorType::SPELL)
{ {
hit_current = player->GetTotalAuraModifier(SPELL_AURA_MOD_SPELL_HIT_CHANCE); 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); 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; 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_current += 3;
hit_overflow = SPELL_HIT_OVERFLOW; hit_overflow = SPELL_HIT_OVERFLOW;
@@ -657,7 +724,7 @@ void StatsWeightCalculator::ApplyWeightFinetune(Player* player)
{ {
if (type_ & (CollectorType::MELEE | CollectorType::RANGED)) 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); armor_penetration_current = player->GetRatingBonusValue(CR_ARMOR_PENETRATION);
if (armor_penetration_current > 50) if (armor_penetration_current > 50)
stats_weights_[STATS_TYPE_ARMOR_PENETRATION] *= 1.2f; stats_weights_[STATS_TYPE_ARMOR_PENETRATION] *= 1.2f;

View File

@@ -28,18 +28,19 @@ class StatsWeightCalculator
public: public:
StatsWeightCalculator(Player* player); StatsWeightCalculator(Player* player);
void Reset(); void Reset();
float CalculateItem(uint32 itemId); float CalculateItem(uint32 itemId, int32 randomPropertyId = 0);
float CalculateEnchant(uint32 enchantId); float CalculateEnchant(uint32 enchantId);
void SetOverflowPenalty(bool apply) { enable_overflow_penalty_ = apply; } void SetOverflowPenalty(bool apply) { enable_overflow_penalty_ = apply; }
void SetItemSetBonus(bool apply) { enable_item_set_bonus_ = apply; } void SetItemSetBonus(bool apply) { enable_item_set_bonus_ = apply; }
void SetQualityBlend(bool apply) { enable_quality_blend_ = apply; } void SetQualityBlend(bool apply) { enable_quality_blend_ = apply; }
private: private:
void GenerateWeights(Player* player); void GenerateWeights(Player* player);
void GenerateBasicWeights(Player* player); void GenerateBasicWeights(Player* player);
void GenerateAdditionalWeights(Player* player); void GenerateAdditionalWeights(Player* player);
void CalculateRandomProperty(int32 randomPropertyId, uint32 itemId);
void CalculateItemSetMod(Player* player, ItemTemplate const* proto); void CalculateItemSetMod(Player* player, ItemTemplate const* proto);
void CalculateSocketBonus(Player* player, ItemTemplate const* proto); void CalculateSocketBonus(Player* player, ItemTemplate const* proto);

View File

@@ -232,6 +232,20 @@ public:
} }
}; };
class CollectItemsVisitor : public IterateItemsVisitor
{
public:
CollectItemsVisitor() : IterateItemsVisitor() {}
std::vector<Item*> items;
bool Visit(Item* item) override
{
items.push_back(item);
return true;
}
};
class ItemCountByQuality : public IterateItemsVisitor class ItemCountByQuality : public IterateItemsVisitor
{ {
public: public:

View File

@@ -8,6 +8,7 @@
#include "Event.h" #include "Event.h"
#include "ItemCountValue.h" #include "ItemCountValue.h"
#include "ItemUsageValue.h" #include "ItemUsageValue.h"
#include "ItemVisitors.h"
#include "Playerbots.h" #include "Playerbots.h"
#include "StatsWeightCalculator.h" #include "StatsWeightCalculator.h"
@@ -311,19 +312,28 @@ bool EquipUpgradesAction::Execute(Event event)
return false; return false;
} }
ListItemsVisitor visitor; CollectItemsVisitor visitor;
IterateItems(&visitor, ITERATE_ITEMS_IN_BAGS); IterateItems(&visitor, ITERATE_ITEMS_IN_BAGS);
ItemIds items; ItemIds items;
for (std::map<uint32, uint32>::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) if (usage == ITEM_USAGE_EQUIP || usage == ITEM_USAGE_REPLACE || usage == ITEM_USAGE_BAD_EQUIP)
{ {
// LOG_INFO("playerbots", "Bot {} <{}> EquipUpgradesAction {} ({})", bot->GetGUID().ToString().c_str(), items.insert(itemId);
// 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);
} }
} }
@@ -333,21 +343,31 @@ bool EquipUpgradesAction::Execute(Event event)
bool EquipUpgradeAction::Execute(Event event) bool EquipUpgradeAction::Execute(Event event)
{ {
ListItemsVisitor visitor; CollectItemsVisitor visitor;
IterateItems(&visitor, ITERATE_ITEMS_IN_BAGS); IterateItems(&visitor, ITERATE_ITEMS_IN_BAGS);
ItemIds items; ItemIds items;
for (std::map<uint32, uint32>::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) 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(), items.insert(itemId);
// 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);
} }
} }
EquipItems(items); EquipItems(items);
return true; return true;
} }

View File

@@ -9,6 +9,7 @@
#include "Group.h" #include "Group.h"
#include "ItemUsageValue.h" #include "ItemUsageValue.h"
#include "LootAction.h" #include "LootAction.h"
#include "ObjectMgr.h"
#include "PlayerbotAIConfig.h" #include "PlayerbotAIConfig.h"
#include "Playerbots.h" #include "Playerbots.h"
@@ -30,12 +31,24 @@ bool LootRollAction::Execute(Event event)
ObjectGuid guid = roll->itemGUID; ObjectGuid guid = roll->itemGUID;
uint32 slot = roll->itemSlot; uint32 slot = roll->itemSlot;
uint32 itemId = roll->itemid; uint32 itemId = roll->itemid;
int32 randomProperty = 0;
if (roll->itemRandomPropId)
randomProperty = roll->itemRandomPropId;
else if (roll->itemRandomSuffix)
randomProperty = -((int)roll->itemRandomSuffix);
RollVote vote = PASS; RollVote vote = PASS;
ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId); ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId);
if (!proto) if (!proto)
continue; 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. // 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) if (proto->Class == ITEM_CLASS_MISC && proto->SubClass == ITEM_SUBCLASS_JUNK && proto->Quality == ITEM_QUALITY_EPIC)

View File

@@ -43,6 +43,7 @@ bool ReleaseSpiritAction::Execute(Event event)
botAI->TellMasterNoFacing(message); botAI->TellMasterNoFacing(message);
IncrementDeathCount(); IncrementDeathCount();
bot->DurabilityRepairAll(false, 1.0f, false);
LogRelease("released"); LogRelease("released");
WorldPacket releasePacket(CMSG_REPOP_REQUEST); WorldPacket releasePacket(CMSG_REPOP_REQUEST);
@@ -79,6 +80,7 @@ void ReleaseSpiritAction::LogRelease(const std::string& releaseMsg, bool isAutoR
bool AutoReleaseSpiritAction::Execute(Event event) bool AutoReleaseSpiritAction::Execute(Event event)
{ {
IncrementDeathCount(); IncrementDeathCount();
bot->DurabilityRepairAll(false, 1.0f, false);
LogRelease("auto released", true); LogRelease("auto released", true);
WorldPacket packet(CMSG_REPOP_REQUEST); WorldPacket packet(CMSG_REPOP_REQUEST);

View File

@@ -140,17 +140,16 @@ bool TellEstimatedDpsAction::Execute(Event event)
bool TellCalculateItemAction::Execute(Event event) bool TellCalculateItemAction::Execute(Event event)
{ {
std::string const text = event.getParam(); std::string const text = event.getParam();
ItemIds ids = chat->parseItems(text); ItemWithRandomProperty item = chat->parseItemWithRandomProperty(text);
StatsWeightCalculator calculator(bot); StatsWeightCalculator calculator(bot);
for (const uint32 &id : ids)
{ const ItemTemplate* proto = sObjectMgr->GetItemTemplate(item.itemId);
const ItemTemplate* proto = sObjectMgr->GetItemTemplate(id); if (!proto)
if (!proto) return false;
continue; float score = calculator.CalculateItem(item.itemId, item.randomPropertyId);
float score = calculator.CalculateItem(id);
std::ostringstream out; std::ostringstream out;
out << "Calculated score of " << chat->FormatItem(proto) << " : " << score; out << "Calculated score of " << chat->FormatItem(proto) << " : " << score;
botAI->TellMasterNoFacing(out.str()); botAI->TellMasterNoFacing(out.str());
}
return true; return true;
} }

View File

@@ -8,6 +8,7 @@
#include "AiFactory.h" #include "AiFactory.h"
#include "ChatHelper.h" #include "ChatHelper.h"
#include "GuildTaskMgr.h" #include "GuildTaskMgr.h"
#include "Item.h"
#include "LootObjectStack.h" #include "LootObjectStack.h"
#include "PlayerbotAIConfig.h" #include "PlayerbotAIConfig.h"
#include "PlayerbotFactory.h" #include "PlayerbotFactory.h"
@@ -18,7 +19,16 @@
ItemUsage ItemUsageValue::Calculate() 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) if (!itemId)
return ITEM_USAGE_NONE; return ITEM_USAGE_NONE;
@@ -89,7 +99,7 @@ ItemUsage ItemUsageValue::Calculate()
if (bot->GetGuildId() && sGuildTaskMgr->IsGuildTaskItem(itemId, bot->GetGuildId())) if (bot->GetGuildId() && sGuildTaskMgr->IsGuildTaskItem(itemId, bot->GetGuildId()))
return ITEM_USAGE_GUILD_TASK; return ITEM_USAGE_GUILD_TASK;
ItemUsage equip = QueryItemUsageForEquip(proto); ItemUsage equip = QueryItemUsageForEquip(proto, randomPropertyId);
if (equip != ITEM_USAGE_NONE) if (equip != ITEM_USAGE_NONE)
return equip; return equip;
@@ -224,7 +234,7 @@ ItemUsage ItemUsageValue::Calculate()
return ITEM_USAGE_NONE; 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) if (bot->CanUseItem(itemProto) != EQUIP_ERR_OK)
return ITEM_USAGE_NONE; return ITEM_USAGE_NONE;
@@ -296,7 +306,8 @@ ItemUsage ItemUsageValue::QueryItemUsageForEquip(ItemTemplate const* itemProto)
calculator.SetItemSetBonus(false); calculator.SetItemSetBonus(false);
calculator.SetOverflowPenalty(false); calculator.SetOverflowPenalty(false);
float itemScore = calculator.CalculateItem(itemProto->ItemId); float itemScore = calculator.CalculateItem(itemProto->ItemId, randomPropertyId);
if (itemScore) if (itemScore)
shouldEquip = true; shouldEquip = true;
@@ -380,7 +391,7 @@ ItemUsage ItemUsageValue::QueryItemUsageForEquip(ItemTemplate const* itemProto)
} }
ItemTemplate const* oldItemProto = oldItem->GetTemplate(); 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) if (oldItem)
{ {
// uint32 oldStatWeight = sRandomItemMgr->GetLiveStatWeight(bot, oldItemProto->ItemId); // uint32 oldStatWeight = sRandomItemMgr->GetLiveStatWeight(bot, oldItemProto->ItemId);

View File

@@ -43,7 +43,7 @@ public:
ItemUsage Calculate() override; ItemUsage Calculate() override;
private: private:
ItemUsage QueryItemUsageForEquip(ItemTemplate const* proto); ItemUsage QueryItemUsageForEquip(ItemTemplate const* proto, int32 randomPropertyId = 0);
uint32 GetSmallestBagSize(); uint32 GetSmallestBagSize();
bool IsItemUsefulForQuest(Player* player, ItemTemplate const* proto); bool IsItemUsefulForQuest(Player* player, ItemTemplate const* proto);
bool IsItemNeededForSkill(ItemTemplate const* proto); bool IsItemNeededForSkill(ItemTemplate const* proto);