Skip to content

Commit

Permalink
Add items with random stats to random equipment
Browse files Browse the repository at this point in the history
vanilla tested, tbc todo test
to use it need to regenerate equip_cache table, by emptying it and restarting server (takes several minutes)
  • Loading branch information
celguar committed May 19, 2024
1 parent 71a0929 commit 40f2375
Show file tree
Hide file tree
Showing 3 changed files with 163 additions and 51 deletions.
21 changes: 19 additions & 2 deletions playerbot/PlayerbotFactory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1810,8 +1810,8 @@ void PlayerbotFactory::InitEquipment(bool incremental, bool syncWithMaster, bool
// sort items based on stat value, ilvl or quality
std::sort(ids.begin(), ids.end(), [specId](int a, int b)
{
uint32 baseCompareA = sRandomItemMgr.GetStatWeight(a, specId) * 1000;
uint32 baseCompareB = sRandomItemMgr.GetStatWeight(b, specId) * 1000;
uint32 baseCompareA = (sRandomItemMgr.GetStatWeight(a, specId) + sRandomItemMgr.GetBestRandomEnchantStatWeight(a, specId)) * 1000;
uint32 baseCompareB = (sRandomItemMgr.GetStatWeight(b, specId) + sRandomItemMgr.GetBestRandomEnchantStatWeight(b, specId)) * 1000;
if (baseCompareA < baseCompareB)
return true;

Expand Down Expand Up @@ -1939,6 +1939,16 @@ void PlayerbotFactory::InitEquipment(bool incremental, bool syncWithMaster, bool
if (newStatValue <= 0)
continue;

// Add random enchant value (the best one)
uint32 randomEnchBestId = 0;
uint32 randomEnchBestValue = 0;
if (proto->RandomProperty)
{
randomEnchBestId = sRandomItemMgr.CalculateBestRandomEnchantId(bot->getClass(), specId, newItemId);
randomEnchBestValue = sRandomItemMgr.CalculateEnchantWeight(bot->getClass(), specId, randomEnchBestId);
newStatValue += randomEnchBestValue;
}

// skip off hand if main hand is worse
#ifdef MANGOSBOT_ZERO
if (proto->IsWeapon() && slot == EQUIPMENT_SLOT_OFFHAND && (bot->getClass() == CLASS_ROGUE || specId == 2))
Expand Down Expand Up @@ -2008,6 +2018,13 @@ void PlayerbotFactory::InitEquipment(bool incremental, bool syncWithMaster, bool
Item* pItem = bot->EquipNewItem(eDest, newItemId, true);
if (pItem)
{
if (randomEnchBestId)
{
// overwrite random generated property
pItem->SetItemRandomProperties(randomEnchBestId);
// update for inspect
bot->SetVisibleItemSlot(pItem->GetSlot(), pItem);
}
pItem->SetOwnerGuid(bot->GetObjectGuid());
EnchantItem(pItem);
//AddGems(pItem);
Expand Down
188 changes: 139 additions & 49 deletions playerbot/RandomItemMgr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
#include "playerbot/ServerFacade.h"
#include "strategy/values/LootValues.h"

#include "Entities/ItemEnchantmentMgr.h"

char * strstri (const char* str1, const char* str2);

uint64 BotEquipKey::GetKey()
Expand Down Expand Up @@ -140,6 +142,7 @@ void RandomItemMgr::Init()
BuildPotionCache();
BuildFoodCache();
BuildTradeCache();
LoadRandomEnchantments();
}

void RandomItemMgr::InitAfterAhBot()
Expand Down Expand Up @@ -311,39 +314,6 @@ bool RandomItemMgr::CanEquipItem(BotEquipKey key, ItemPrototype const* proto)
if (!requiredLevel)
return false;

/*if (!requiredLevel)
requiredLevel = key.level;*/

// test
return true;

uint32 level = key.level;
uint32 delta = 2;
if (level < 15)
delta = urand(7, 15);
else if (proto->Class == ITEM_CLASS_WEAPON || proto->SubClass == ITEM_SUBCLASS_ARMOR_SHIELD)
delta = urand(2, 3);
else if (!(level % 10) || (level % 10) == 9)
delta = 2;
else if (level < 40)
delta = urand(5, 10);
else if (level < 60)
delta = urand(3, 7);
else if (level < 70)
delta = urand(2, 5);
else if (level < 80)
delta = urand(2, 4);

if (key.quality > ITEM_QUALITY_NORMAL &&
(requiredLevel > level || requiredLevel < (level - delta)))
return false;

for (uint32 gap = 60; gap <= 80; gap += 10)
{
if (level > gap && requiredLevel <= gap)
return false;
}

return true;
}

Expand Down Expand Up @@ -1052,11 +1022,11 @@ void RandomItemMgr::BuildItemInfoCache()
proto->RequiredCityRank > 0)
continue;*/

#ifndef MANGOSBOT_ZERO
// skip random enchant items
if (proto->RandomProperty)
continue;

#ifndef MANGOSBOT_ZERO
if (proto->RandomSuffix)
continue;
#endif
Expand Down Expand Up @@ -1388,6 +1358,10 @@ void RandomItemMgr::BuildItemInfoCache()
if (!statW && cacheInfo->slot == EQUIPMENT_SLOT_RANGED && proto->SubClass == ITEM_SUBCLASS_WEAPON_WAND && (clazz == CLASS_PRIEST || clazz == CLASS_MAGE || clazz == CLASS_WARLOCK))
statW = 1;

// Random properties
if (!statW && proto->RandomProperty)
statW = 1;

// set stat weight = 1 for items that can be equipped but have no proper stats
//statWeight.weight = statW;
// save item statWeight into ItemCache
Expand Down Expand Up @@ -1695,7 +1669,6 @@ uint32 RandomItemMgr::CalculateStatWeight(uint8 playerclass, uint8 spec, ItemPro
// generic spell damage
if (spellproto->EffectMiscValue[j] == SPELL_SCHOOL_MASK_MAGIC)
{
isSpellDamageItem = true;
effectAuraDamageStatWeight += CalculateSingleStatWeight(playerclass, spec, "splpwr", spellDamage);
}
else
Expand All @@ -1716,9 +1689,6 @@ uint32 RandomItemMgr::CalculateStatWeight(uint8 playerclass, uint8 spec, ItemPro
if ((spellproto->EffectMiscValue[j] & SPELL_SCHOOL_MASK_NATURE) != 0)
specialDamage += CalculateSingleStatWeight(playerclass, spec, "natsplpwr", spellDamage);

if (!isWhitelist && !specialDamage && isSpellDamageItem)
return 0;

effectAuraDamageStatWeight += specialDamage;
}
}
Expand Down Expand Up @@ -2113,6 +2083,70 @@ uint32 RandomItemMgr::CalculateStatWeight(uint8 playerclass, uint8 spec, ItemPro
return statWeight;
}

uint32 RandomItemMgr::CalculateRandomEnchantId(uint8 playerclass, uint8 spec, ItemPrototype const* proto)
{
if (!proto)
return 0;

// Random Property case
if (proto->RandomProperty)
{
uint32 randomPropId = GetItemEnchantMod(proto->RandomProperty);
ItemRandomPropertiesEntry const* random_id = sItemRandomPropertiesStore.LookupEntry(randomPropId);
if (!random_id)
{
sLog.outErrorDb("Enchantment id #%u used but it doesn't have records in 'ItemRandomProperties.dbc'", randomPropId);
return 0;
}

// check stats
if (CalculateEnchantWeight(playerclass, spec, random_id->ID))
return random_id->ID;
}

return 0;
}

uint32 RandomItemMgr::CalculateBestRandomEnchantId(uint8 playerclass, uint8 spec, uint32 itemId)
{
if (!itemId)
return 0;

ItemPrototype const* proto = sObjectMgr.GetItemPrototype(itemId);
if (!proto)
return 0;

std::map<uint32, std::vector<uint32> >::const_iterator tab = randomEnchantsCache.find(proto->RandomProperty);
if (tab == randomEnchantsCache.end())
return 0;

uint32 bestScore = 0;
uint32 bestId = 0;

const std::vector<uint32> propList = tab->second;
for (auto propId : propList)
{
ItemRandomPropertiesEntry const* random_id = sItemRandomPropertiesStore.LookupEntry(propId);
if (!random_id)
continue;

uint32 currScore = 0;
for (uint32 i = PROP_ENCHANTMENT_SLOT_0; i < PROP_ENCHANTMENT_SLOT_0 + 3; ++i)
{
uint32 enchantId = random_id->enchant_id[i - PROP_ENCHANTMENT_SLOT_0];
currScore += CalculateEnchantWeight(playerclass, spec, enchantId);
}

if (currScore > bestScore)
{
bestScore = currScore;
bestId = random_id->ID;;
}
}

return bestId;
}

uint32 RandomItemMgr::CalculateEnchantWeight(uint8 playerclass, uint8 spec, uint32 enchantId)
{
if (!enchantId)
Expand Down Expand Up @@ -2193,9 +2227,6 @@ uint32 RandomItemMgr::CalculateEnchantWeight(uint8 playerclass, uint8 spec, uint
if ((spellInfo->EffectMiscValue[j] & SPELL_SCHOOL_MASK_NATURE) != 0)
specialDamage += CalculateSingleStatWeight(playerclass, spec, "natsplpwr", spellDamage);

if (!specialDamage)
return 0;

weight += specialDamage;
}
}
Expand Down Expand Up @@ -2228,9 +2259,6 @@ uint32 RandomItemMgr::CalculateEnchantWeight(uint8 playerclass, uint8 spec, uint
if ((spellInfo->EffectMiscValue[j] & SPELL_SCHOOL_MASK_NATURE) != 0)
specialDamage += CalculateSingleStatWeight(playerclass, spec, "natsplpwr", spellDamage);

if (!specialDamage)
return 0;

weight += specialDamage;
}
}
Expand Down Expand Up @@ -2936,6 +2964,42 @@ uint32 RandomItemMgr::GetStatWeight(uint32 itemId, uint32 specId)
return statWeight;
}

uint32 RandomItemMgr::GetBestRandomEnchantStatWeight(uint32 itemId, uint32 specId)
{
if (!specId || !itemId)
return 0;

if (!itemInfoCache[itemId])
return 0;

if (!m_weightScales[specId].info.id)
return 0;

uint8 plrClass = 0;
uint32 statWeight = 0;

for (auto itr : m_weightScales)
{
if (itr.second.info.id == specId)
plrClass = itr.second.info.classId;
}

if (!plrClass)
return 0;

std::map<uint32, ItemInfoEntry*>::iterator itr = itemInfoCache.find(itemId);
if (itr != itemInfoCache.end())
{
uint32 bestEnch = CalculateBestRandomEnchantId(plrClass, specId, itemId);
if (bestEnch)
{
statWeight = CalculateEnchantWeight(plrClass, specId, bestEnch);
}
}

return statWeight;
}

uint32 RandomItemMgr::GetLiveStatWeight(Player* player, uint32 itemId, uint32 specId)
{
if (!player || !itemId)
Expand Down Expand Up @@ -3057,10 +3121,6 @@ uint32 RandomItemMgr::GetLiveStatWeight(Player* player, uint32 itemId, uint32 sp
info->slot == EQUIPMENT_SLOT_FINGER2))
return 0;

// skip items that only fit in slot, but not stats
if (!itemId && info->weights[specId] == 1 && player->GetLevel() > 20)
return 0;

// check if item stat score is the best among class specs
/*uint32 bestSpecId = 0;
uint32 bestSpecScore = 0;
Expand Down Expand Up @@ -3200,7 +3260,7 @@ void RandomItemMgr::BuildEquipCache()
continue;

// only accept "useless" items if bot level <= 30
if (statWeight == 1 && level > 30)
if (statWeight == 1 && level > 30 && !proto->RandomProperty)
continue;

uint32 minLevel = GetMinLevelFromCache(itemId);
Expand Down Expand Up @@ -3757,6 +3817,36 @@ void RandomItemMgr::BuildRarityCache()
}
}

void RandomItemMgr::LoadRandomEnchantments()
{
randomEnchantsCache.clear();

uint32 count = 0;
auto queryResult = WorldDatabase.Query("SELECT entry, ench, chance FROM item_enchantment_template");

if (queryResult)
{
do
{
Field* fields = queryResult->Fetch();
uint32 entry = fields[0].GetUInt32();
uint32 ench = fields[1].GetUInt32();
float chance = fields[2].GetFloat();

if (chance > 0.000001f && chance <= 100.0f)
randomEnchantsCache[entry].push_back(ench);

++count;
} while (queryResult->NextRow());

sLog.outString(">> Loaded %u Item Enchantment definitions", count);
}
else
sLog.outErrorDb(">> Loaded 0 Item Enchantment definitions. DB table `item_enchantment_template` is empty.");

sLog.outString();
}

float RandomItemMgr::GetItemRarity(uint32 itemId)
{
return rarityCache[itemId];
Expand Down
5 changes: 5 additions & 0 deletions playerbot/RandomItemMgr.h
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ class RandomItemMgr
uint32 GetStatWeight(Player* player, uint32 itemId);
uint32 GetLiveStatWeight(Player* player, uint32 itemId, uint32 specId = 0);
uint32 GetStatWeight(uint32 itemId, uint32 specId);
uint32 GetBestRandomEnchantStatWeight(uint32 itemId, uint32 specId);
uint32 GetRandomItem(uint32 level, RandomItemType type, RandomItemPredicate* predicate = NULL);
uint32 GetAmmo(uint32 level, uint32 subClass);
uint32 GetRandomPotion(uint32 level, uint32 effect);
Expand All @@ -164,6 +165,8 @@ class RandomItemMgr
uint32 GetRandomTrade(uint32 level);
std::vector<uint32> GetGemsList();

uint32 CalculateRandomEnchantId(uint8 playerclass, uint8 spec, ItemPrototype const* proto);
uint32 CalculateBestRandomEnchantId(uint8 playerclass, uint8 spec, uint32 itemId);
uint32 CalculateEnchantWeight(uint8 playerclass, uint8 spec, uint32 enchantId);
uint32 CalculateRandomPropertyWeight(uint8 playerclass, uint8 spec, int32 randomPropertyId);
uint32 CalculateGemWeight(uint8 playerclass, uint8 spec, uint32 gemId);
Expand Down Expand Up @@ -194,6 +197,7 @@ class RandomItemMgr
void BuildPotionCache();
void BuildTradeCache();
void BuildRarityCache();
void LoadRandomEnchantments();
bool CanEquipItem(BotEquipKey key, ItemPrototype const* proto);
bool CanEquipItemNew(ItemPrototype const* proto);
void AddItemStats(uint32 mod, uint8 &sp, uint8 &ap, uint8 &tank);
Expand All @@ -214,6 +218,7 @@ class RandomItemMgr
std::map<uint32, std::string > ItemStatLink;
std::map<std::string, uint32 > weightRatingLink;
std::map<uint32, ItemInfoEntry*> itemInfoCache;
std::map<uint32, std::vector<uint32> > randomEnchantsCache;
};

#define sRandomItemMgr RandomItemMgr::instance()
Expand Down

0 comments on commit 40f2375

Please sign in to comment.