aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorShauren <shauren.trinity@gmail.com>2025-10-05 15:13:57 +0200
committerShauren <shauren.trinity@gmail.com>2025-10-05 15:13:57 +0200
commit8fff15ba7c63bc12fa00664bfe067bbc9586d3f2 (patch)
tree95187d99a0ad90779c838da3f603ae5db7020c10 /src
parentffbda978f32f1a70d33469b9f5b0644cc418e8ea (diff)
Core/Items: Implemented new item bonus types: limit category and pvp item level
Diffstat (limited to 'src')
-rw-r--r--src/server/game/DataStores/DBCEnums.h3
-rw-r--r--src/server/game/Entities/Item/Item.cpp46
-rw-r--r--src/server/game/Entities/Item/Item.h7
-rw-r--r--src/server/game/Entities/Player/Player.cpp47
-rw-r--r--src/server/game/Entities/Player/Player.h3
-rw-r--r--src/server/game/Handlers/ItemHandler.cpp16
6 files changed, 97 insertions, 25 deletions
diff --git a/src/server/game/DataStores/DBCEnums.h b/src/server/game/DataStores/DBCEnums.h
index dc68d9fc594..542470a7bbf 100644
--- a/src/server/game/DataStores/DBCEnums.h
+++ b/src/server/game/DataStores/DBCEnums.h
@@ -1216,8 +1216,11 @@ enum ItemBonusType
ITEM_BONUS_OVERRIDE_NAME = 31, // ItemNameDescription id
ITEM_BONUS_ITEM_BONUS_LIST_GROUP = 34,
ITEM_BONUS_ITEM_LIMIT_CATEGORY = 35,
+ ITEM_BONUS_PVP_ITEM_LEVEL_INCREMENT = 36,
ITEM_BONUS_ITEM_CONVERSION = 37,
ITEM_BONUS_ITEM_HISTORY_SLOT = 38,
+ ITEM_BONUS_PVP_ITEM_LEVEL_BASE = 43,
+ ITEM_BONUS_BONDING_WITH_PRIORITY = 47,
};
enum class ItemCollectionType : uint8
diff --git a/src/server/game/Entities/Item/Item.cpp b/src/server/game/Entities/Item/Item.cpp
index 5401c0c9c45..6df3558d66b 100644
--- a/src/server/game/Entities/Item/Item.cpp
+++ b/src/server/game/Entities/Item/Item.cpp
@@ -1677,7 +1677,13 @@ uint8 Item::GetGemCountWithLimitCategory(uint32 limitCategory) const
if (!gemProto)
return false;
- return gemProto->GetItemLimitCategory() == limitCategory;
+ BonusData gemBonus;
+ gemBonus.Initialize(gemProto);
+
+ for (uint16 bonusListID : gemData.BonusListIDs)
+ gemBonus.AddBonusList(bonusListID);
+
+ return gemBonus.LimitCategory == limitCategory;
}));
}
@@ -2339,7 +2345,13 @@ uint32 Item::GetItemLevel(ItemTemplate const* itemTemplate, BonusData const& bon
uint32 itemLevelBeforeUpgrades = itemLevel;
if (pvpBonus)
+ {
+ if (bonusData.PvpItemLevel)
+ itemLevel = bonusData.PvpItemLevel;
+
+ itemLevel += bonusData.PvpItemLevelBonus;
itemLevel += sDB2Manager.GetPvpItemLevelBonus(itemTemplate->GetId());
+ }
if (itemTemplate->GetInventoryType() != INVTYPE_NON_EQUIP)
{
@@ -2919,6 +2931,9 @@ void BonusData::Initialize(ItemTemplate const* proto)
Suffix = 0;
RequiredLevelCurve = 0;
+ PvpItemLevel = 0;
+ PvpItemLevelBonus = 0;
+
EffectCount = 0;
for (ItemEffectEntry const* itemEffect : proto->Effects)
Effects[EffectCount++] = itemEffect;
@@ -2926,6 +2941,8 @@ void BonusData::Initialize(ItemTemplate const* proto)
for (std::size_t i = EffectCount; i < Effects.size(); ++i)
Effects[i] = nullptr;
+ LimitCategory = proto->GetItemLimitCategory();
+
CanDisenchant = !proto->HasFlag(ITEM_FLAG_NO_DISENCHANT);
CanScrap = proto->HasFlag(ITEM_FLAG4_SCRAPABLE);
@@ -2935,7 +2952,10 @@ void BonusData::Initialize(ItemTemplate const* proto)
_state.ScalingStatDistributionPriority = std::numeric_limits<int32>::max();
_state.AzeriteTierUnlockSetPriority = std::numeric_limits<int32>::max();
_state.RequiredLevelCurvePriority = std::numeric_limits<int32>::max();
+ _state.PvpItemLevelPriority = std::numeric_limits<int32>::max();
+ _state.BondingPriority = std::numeric_limits<int32>::max();
_state.HasQualityBonus = false;
+ _state.HasItemLimitCategory = false;
}
void BonusData::Initialize(WorldPackets::Item::ItemInstance const& itemInstance)
@@ -3072,5 +3092,29 @@ void BonusData::AddBonus(uint32 type, std::array<int32, 4> const& values)
ContentTuningId = static_cast<uint32>(values[1]);
}
break;
+ case ITEM_BONUS_ITEM_LIMIT_CATEGORY:
+ if (!_state.HasItemLimitCategory)
+ {
+ LimitCategory = values[0];
+ _state.HasItemLimitCategory = true;
+ }
+ break;
+ case ITEM_BONUS_PVP_ITEM_LEVEL_INCREMENT:
+ PvpItemLevelBonus += values[0];
+ break;
+ case ITEM_BONUS_PVP_ITEM_LEVEL_BASE:
+ if (values[1] < _state.PvpItemLevelPriority)
+ {
+ PvpItemLevel = values[0];
+ _state.PvpItemLevelPriority = values[1];
+ }
+ break;
+ case ITEM_BONUS_BONDING_WITH_PRIORITY:
+ if (values[1] < _state.BondingPriority)
+ {
+ Bonding = static_cast<ItemBondingType>(values[0]);
+ _state.BondingPriority = values[1];
+ }
+ break;
}
}
diff --git a/src/server/game/Entities/Item/Item.h b/src/server/game/Entities/Item/Item.h
index d8d03f1e709..d7bb941284d 100644
--- a/src/server/game/Entities/Item/Item.h
+++ b/src/server/game/Entities/Item/Item.h
@@ -80,8 +80,11 @@ struct BonusData
int32 AzeriteTierUnlockSetId;
uint32 Suffix;
int32 RequiredLevelCurve;
+ uint16 PvpItemLevel;
+ int16 PvpItemLevelBonus;
std::array<ItemEffectEntry const*, 13> Effects;
std::size_t EffectCount;
+ uint32 LimitCategory;
bool CanDisenchant;
bool CanScrap;
bool HasFixedLevel;
@@ -100,7 +103,10 @@ private:
int32 ScalingStatDistributionPriority;
int32 AzeriteTierUnlockSetPriority;
int32 RequiredLevelCurvePriority;
+ int32 PvpItemLevelPriority;
+ int32 BondingPriority;
bool HasQualityBonus;
+ bool HasItemLimitCategory;
} _state;
};
@@ -347,6 +353,7 @@ class TC_GAME_API Item : public Object
static ItemDisenchantLootEntry const* GetBaseDisenchantLoot(ItemTemplate const* itemTemplate, uint32 quality, uint32 itemLevel);
void SetFixedLevel(uint8 level);
std::span<ItemEffectEntry const* const> GetEffects() const { return { _bonusData.Effects.data(), _bonusData.EffectCount }; }
+ uint32 GetItemLimitCategory() const { return _bonusData.LimitCategory; }
// Item Refund system
void SetNotRefundable(Player* owner, bool changestate = true, CharacterDatabaseTransaction* trans = nullptr, bool addToCollection = true);
diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp
index 0bcdbbd1ec7..f8ab0096a05 100644
--- a/src/server/game/Entities/Player/Player.cpp
+++ b/src/server/game/Entities/Player/Player.cpp
@@ -9564,9 +9564,8 @@ uint32 Player::GetItemCountWithLimitCategory(uint32 limitCategory, Item* skipIte
ForEachItem(ItemSearchLocation::Everywhere, [&count, limitCategory, skipItem](Item* item)
{
if (item != skipItem)
- if (ItemTemplate const* pProto = item->GetTemplate())
- if (pProto->GetItemLimitCategory() == limitCategory)
- count += item->GetCount();
+ if (item->GetItemLimitCategory() == limitCategory)
+ count += item->GetCount();
return ItemSearchCallbackResult::Continue;
});
@@ -9928,7 +9927,7 @@ bool Player::HasItemWithLimitCategoryEquipped(uint32 limitCategory, uint32 count
if (pItem->GetSlot() == except_slot)
return ItemSearchCallbackResult::Continue;
- if (pItem->GetTemplate()->GetItemLimitCategory() != limitCategory)
+ if (pItem->GetItemLimitCategory() != limitCategory)
return ItemSearchCallbackResult::Continue;
tempcount += pItem->GetCount();
@@ -9972,8 +9971,10 @@ InventoryResult Player::CanTakeMoreSimilarItems(uint32 entry, uint32 count, Item
if (pItem && pItem->m_lootGenerated)
return EQUIP_ERR_LOOT_GONE;
+ uint32 limitCategory = pItem ? pItem->GetItemLimitCategory() : pProto->GetItemLimitCategory();
+
// no maximum
- if ((pProto->GetMaxCount() <= 0 && pProto->GetItemLimitCategory() == 0) || pProto->GetMaxCount() == 2147483647)
+ if ((pProto->GetMaxCount() <= 0 && limitCategory == 0) || pProto->GetMaxCount() == 2147483647)
return EQUIP_ERR_OK;
if (pProto->GetMaxCount() > 0)
@@ -9988,9 +9989,9 @@ InventoryResult Player::CanTakeMoreSimilarItems(uint32 entry, uint32 count, Item
}
// check unique-equipped limit
- if (pProto->GetItemLimitCategory())
+ if (limitCategory)
{
- ItemLimitCategoryEntry const* limitEntry = sItemLimitCategoryStore.LookupEntry(pProto->GetItemLimitCategory());
+ ItemLimitCategoryEntry const* limitEntry = sItemLimitCategoryStore.LookupEntry(limitCategory);
if (!limitEntry)
{
if (no_space_count)
@@ -10001,7 +10002,7 @@ InventoryResult Player::CanTakeMoreSimilarItems(uint32 entry, uint32 count, Item
if (limitEntry->Flags == ITEM_LIMIT_CATEGORY_MODE_HAVE)
{
uint8 limitQuantity = GetItemLimitCategoryQuantity(limitEntry);
- uint32 curcount = GetItemCountWithLimitCategory(pProto->GetItemLimitCategory(), pItem);
+ uint32 curcount = GetItemCountWithLimitCategory(limitCategory, pItem);
if (curcount + count > uint32(limitQuantity))
{
if (no_space_count)
@@ -13027,8 +13028,10 @@ void Player::SendEquipError(InventoryResult msg, Item const* item1 /*= nullptr*/
case EQUIP_ERR_ITEM_MAX_LIMIT_CATEGORY_SOCKETED_EXCEEDED_IS:
case EQUIP_ERR_ITEM_MAX_LIMIT_CATEGORY_EQUIPPED_EXCEEDED_IS:
{
- ItemTemplate const* proto = item1 ? item1->GetTemplate() : sObjectMgr->GetItemTemplate(itemId);
- failure.LimitCategory = proto ? proto->GetItemLimitCategory() : 0;
+ if (item1)
+ failure.LimitCategory = item1->GetItemLimitCategory();
+ else if (ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId))
+ failure.LimitCategory = proto->GetItemLimitCategory();
break;
}
default:
@@ -27014,7 +27017,7 @@ InventoryResult Player::CanEquipUniqueItem(Item* pItem, uint8 eslot, uint32 limi
ItemTemplate const* pProto = pItem->GetTemplate();
// proto based limitations
- if (InventoryResult res = CanEquipUniqueItem(pProto, eslot, limit_count))
+ if (InventoryResult res = CanEquipUniqueItem(pProto, *pItem->GetBonus(), eslot, limit_count))
return res;
// check unique-equipped on gems
@@ -27024,18 +27027,24 @@ InventoryResult Player::CanEquipUniqueItem(Item* pItem, uint8 eslot, uint32 limi
if (!pGem)
continue;
+ BonusData gemBonus;
+ gemBonus.Initialize(pGem);
+
+ for (uint16 bonusListID : gemData.BonusListIDs)
+ gemBonus.AddBonusList(bonusListID);
+
// include for check equip another gems with same limit category for not equipped item (and then not counted)
- uint32 gem_limit_count = !pItem->IsEquipped() && pGem->GetItemLimitCategory()
- ? pItem->GetGemCountWithLimitCategory(pGem->GetItemLimitCategory()) : 1;
+ uint32 gem_limit_count = !pItem->IsEquipped() && gemBonus.LimitCategory
+ ? pItem->GetGemCountWithLimitCategory(gemBonus.LimitCategory) : 1;
- if (InventoryResult res = CanEquipUniqueItem(pGem, eslot, gem_limit_count))
+ if (InventoryResult res = CanEquipUniqueItem(pGem, gemBonus, eslot, gem_limit_count))
return res;
}
return EQUIP_ERR_OK;
}
-InventoryResult Player::CanEquipUniqueItem(ItemTemplate const* itemProto, uint8 except_slot, uint32 limit_count) const
+InventoryResult Player::CanEquipUniqueItem(ItemTemplate const* itemProto, BonusData const& itemBonus, uint8 except_slot, uint32 limit_count) const
{
// check unique-equipped on item
if (itemProto->HasFlag(ITEM_FLAG_UNIQUE_EQUIPPABLE))
@@ -27046,9 +27055,9 @@ InventoryResult Player::CanEquipUniqueItem(ItemTemplate const* itemProto, uint8
}
// check unique-equipped limit
- if (itemProto->GetItemLimitCategory())
+ if (itemBonus.LimitCategory)
{
- ItemLimitCategoryEntry const* limitEntry = sItemLimitCategoryStore.LookupEntry(itemProto->GetItemLimitCategory());
+ ItemLimitCategoryEntry const* limitEntry = sItemLimitCategoryStore.LookupEntry(itemBonus.LimitCategory);
if (!limitEntry)
return EQUIP_ERR_NOT_EQUIPPABLE;
@@ -27059,9 +27068,9 @@ InventoryResult Player::CanEquipUniqueItem(ItemTemplate const* itemProto, uint8
return EQUIP_ERR_ITEM_MAX_LIMIT_CATEGORY_EQUIPPED_EXCEEDED_IS;
// there is an equip limit on this item
- if (HasItemWithLimitCategoryEquipped(itemProto->GetItemLimitCategory(), limitQuantity - limit_count + 1, except_slot))
+ if (HasItemWithLimitCategoryEquipped(itemBonus.LimitCategory, limitQuantity - limit_count + 1, except_slot))
return EQUIP_ERR_ITEM_MAX_LIMIT_CATEGORY_EQUIPPED_EXCEEDED_IS;
- else if (HasGemWithLimitCategoryEquipped(itemProto->GetItemLimitCategory(), limitQuantity - limit_count + 1, except_slot))
+ else if (HasGemWithLimitCategoryEquipped(itemBonus.LimitCategory, limitQuantity - limit_count + 1, except_slot))
return EQUIP_ERR_ITEM_MAX_COUNT_EQUIPPED_SOCKETED;
}
diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h
index 2b615518381..d6abd1c4104 100644
--- a/src/server/game/Entities/Player/Player.h
+++ b/src/server/game/Entities/Player/Player.h
@@ -45,6 +45,7 @@ struct AzeriteItemMilestonePowerEntry;
struct AzeritePowerEntry;
struct BarberShopStyleEntry;
struct BattlegroundTemplate;
+struct BonusData;
struct CharTitlesEntry;
struct ChatChannelsEntry;
struct ChrSpecializationEntry;
@@ -1490,7 +1491,7 @@ class TC_GAME_API Player final : public Unit, public GridObject<Player>
InventoryResult CanEquipChildItem(Item* parentItem) const;
InventoryResult CanEquipUniqueItem(Item* pItem, uint8 except_slot = NULL_SLOT, uint32 limit_count = 1) const;
- InventoryResult CanEquipUniqueItem(ItemTemplate const* itemProto, uint8 except_slot = NULL_SLOT, uint32 limit_count = 1) const;
+ InventoryResult CanEquipUniqueItem(ItemTemplate const* itemProto, BonusData const& itemBonus, uint8 except_slot = NULL_SLOT, uint32 limit_count = 1) const;
InventoryResult CanUnequipItems(uint32 item, uint32 count) const;
InventoryResult CanUnequipItem(uint16 src, bool swap) const;
InventoryResult CanBankItem(uint8 bag, uint8 slot, ItemPosCountVec& dest, Item* pItem, bool swap, bool not_loading = true, bool reagentBankOnly = false) const;
diff --git a/src/server/game/Handlers/ItemHandler.cpp b/src/server/game/Handlers/ItemHandler.cpp
index a8602605c7d..329849c5e34 100644
--- a/src/server/game/Handlers/ItemHandler.cpp
+++ b/src/server/game/Handlers/ItemHandler.cpp
@@ -1048,9 +1048,9 @@ void WorldSession::HandleSocketGems(WorldPackets::Item::SocketGems& socketGems)
// unique limit type item
int32 limit_newcount = 0;
- if (iGemProto->GetItemLimitCategory())
+ if (gems[i]->GetItemLimitCategory())
{
- if (ItemLimitCategoryEntry const* limitEntry = sItemLimitCategoryStore.LookupEntry(iGemProto->GetItemLimitCategory()))
+ if (ItemLimitCategoryEntry const* limitEntry = sItemLimitCategoryStore.LookupEntry(gems[i]->GetItemLimitCategory()))
{
// NOTE: limitEntry->Flags is not checked because if item has limit then it is applied in equip case
for (int j = 0; j < MAX_GEM_SOCKETS; ++j)
@@ -1058,15 +1058,23 @@ void WorldSession::HandleSocketGems(WorldPackets::Item::SocketGems& socketGems)
if (gems[j])
{
// new gem
- if (iGemProto->GetItemLimitCategory() == gems[j]->GetTemplate()->GetItemLimitCategory())
+ if (gems[i]->GetItemLimitCategory() == gems[j]->GetItemLimitCategory())
++limit_newcount;
}
else if (oldGemData[j])
{
// existing gem
if (ItemTemplate const* jProto = sObjectMgr->GetItemTemplate(oldGemData[j]->ItemID))
- if (iGemProto->GetItemLimitCategory() == jProto->GetItemLimitCategory())
+ {
+ BonusData oldGemBonus;
+ oldGemBonus.Initialize(jProto);
+
+ for (uint16 bonusListID : oldGemData[j]->BonusListIDs)
+ oldGemBonus.AddBonusList(bonusListID);
+
+ if (gems[i]->GetItemLimitCategory() == oldGemBonus.LimitCategory)
++limit_newcount;
+ }
}
}