/*
* This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see .
*/
#ifndef TRINITYCORE_ITEM_H
#define TRINITYCORE_ITEM_H
#include "Object.h"
#include "Common.h"
#include "DatabaseEnvFwd.h"
#include "ItemDefines.h"
#include "ItemEnchantmentMgr.h"
#include "ItemTemplate.h"
class SpellInfo;
class Bag;
class Unit;
struct Loot;
namespace WorldPackets
{
namespace Item
{
struct ItemInstance;
}
}
#define MAX_GEM_SOCKETS MAX_ITEM_PROTO_SOCKETS// (BONUS_ENCHANTMENT_SLOT-SOCK_ENCHANTMENT_SLOT) and item proto size, equal value expected
#define MAX_ENCHANTMENT_OFFSET 3
enum ItemUpdateState
{
ITEM_UNCHANGED = 0,
ITEM_CHANGED = 1,
ITEM_NEW = 2,
ITEM_REMOVED = 3
};
#define MAX_ITEM_SPELLS 5
bool ItemCanGoIntoBag(ItemTemplate const* proto, ItemTemplate const* pBagProto);
extern ItemModifier const AppearanceModifierSlotBySpec[MAX_SPECIALIZATIONS];
extern ItemModifier const IllusionModifierSlotBySpec[MAX_SPECIALIZATIONS];
extern ItemModifier const SecondaryAppearanceModifierSlotBySpec[MAX_SPECIALIZATIONS];
extern int32 const ItemTransmogrificationSlots[MAX_INVTYPE];
struct BonusData
{
uint32 Quality;
int32 ItemLevelBonus;
int32 RequiredLevel;
int32 ItemStatType[MAX_ITEM_PROTO_STATS];
int32 StatPercentEditor[MAX_ITEM_PROTO_STATS];
float ItemStatSocketCostMultiplier[MAX_ITEM_PROTO_STATS];
uint32 SocketColor[MAX_ITEM_PROTO_SOCKETS];
ItemBondingType Bonding;
uint32 AppearanceModID;
float RepairCostMultiplier;
uint32 ContentTuningId;
uint32 PlayerLevelToItemLevelCurveId;
uint32 DisenchantLootId;
uint32 GemItemLevelBonus[MAX_ITEM_PROTO_SOCKETS];
int32 GemRelicType[MAX_ITEM_PROTO_SOCKETS];
uint16 GemRelicRankBonus[MAX_ITEM_PROTO_SOCKETS];
int32 RelicType;
int32 RequiredLevelOverride;
int32 AzeriteTierUnlockSetId;
uint32 Suffix;
int32 RequiredLevelCurve;
std::array Effects;
std::size_t EffectCount;
bool CanDisenchant;
bool CanScrap;
bool HasFixedLevel;
void Initialize(ItemTemplate const* proto);
void Initialize(WorldPackets::Item::ItemInstance const& itemInstance);
void AddBonusList(uint32 bonusListId);
void AddBonus(uint32 type, std::array const& values);
private:
struct
{
int32 SuffixPriority;
int32 AppearanceModPriority;
int32 DisenchantLootPriority;
int32 ScalingStatDistributionPriority;
int32 AzeriteTierUnlockSetPriority;
int32 RequiredLevelCurvePriority;
bool HasQualityBonus;
} _state;
};
struct ArtifactPowerData
{
uint32 ArtifactPowerId = 0;
uint8 PurchasedRank = 0;
uint8 CurrentRankWithBonus = 0;
};
struct ArtifactData
{
uint64 Xp = 0;
uint32 ArtifactAppearanceId = 0;
uint32 ArtifactTierId = 0;
std::vector ArtifactPowers;
};
struct AzeriteItemSelectedEssencesData
{
uint32 SpecializationId = 0;
std::array AzeriteEssenceId = { };
};
struct AzeriteItemData
{
uint64 Xp;
uint32 Level;
uint32 KnowledgeLevel;
std::vector AzeriteItemMilestonePowers;
std::vector UnlockedAzeriteEssences;
std::array SelectedAzeriteEssences = { };
};
struct AzeriteEmpoweredItemData
{
std::array SelectedAzeritePowers;
};
struct ItemAdditionalLoadInfo
{
static void Init(std::unordered_map* loadInfo, PreparedQueryResult artifactResult, PreparedQueryResult azeriteItemResult,
PreparedQueryResult azeriteItemMilestonePowersResult, PreparedQueryResult azeriteItemUnlockedEssencesResult, PreparedQueryResult azeriteEmpoweredItemResult);
Optional Artifact;
Optional AzeriteItem;
Optional AzeriteEmpoweredItem;
};
struct ItemDynamicFieldGems
{
uint32 ItemId;
uint16 BonusListIDs[16];
uint8 Context;
};
Item* NewItemOrBag(ItemTemplate const* proto);
class TC_GAME_API Item : public Object
{
friend void AddItemToUpdateQueueOf(Item* item, Player* player);
friend void RemoveItemFromUpdateQueueOf(Item* item, Player* player);
public:
static Item* CreateItem(uint32 itemEntry, uint32 count, ItemContext context, Player const* player = nullptr, bool addDefaultBonuses = true);
Item* CloneItem(uint32 count, Player const* player = nullptr) const;
Item();
~Item();
virtual bool Create(ObjectGuid::LowType guidlow, uint32 itemId, ItemContext context, Player const* owner);
std::string GetNameForLocaleIdx(LocaleConstant locale) const override;
ItemTemplate const* GetTemplate() const;
BonusData const* GetBonus() const { return &_bonusData; }
ObjectGuid GetOwnerGUID() const { return m_itemData->Owner; }
void SetOwnerGUID(ObjectGuid guid) { SetUpdateFieldValue(m_values.ModifyValue(&Item::m_itemData).ModifyValue(&UF::ItemData::Owner), guid); }
ObjectGuid GetContainedIn() const { return m_itemData->ContainedIn; }
void SetContainedIn(ObjectGuid guid) { SetUpdateFieldValue(m_values.ModifyValue(&Item::m_itemData).ModifyValue(&UF::ItemData::ContainedIn), guid); }
ObjectGuid GetCreator() const { return m_itemData->Creator; }
void SetCreator(ObjectGuid guid) { SetUpdateFieldValue(m_values.ModifyValue(&Item::m_itemData).ModifyValue(&UF::ItemData::Creator), guid); }
ObjectGuid GetGiftCreator() const { return m_itemData->GiftCreator; }
void SetGiftCreator(ObjectGuid guid) { SetUpdateFieldValue(m_values.ModifyValue(&Item::m_itemData).ModifyValue(&UF::ItemData::GiftCreator), guid); }
Player* GetOwner() const;
void SetExpiration(uint32 expiration) { SetUpdateFieldValue(m_values.ModifyValue(&Item::m_itemData).ModifyValue(&UF::ItemData::Expiration), expiration); }
ItemBondingType GetBonding() const { return _bonusData.Bonding; }
void SetBinding(bool val)
{
if (val)
SetItemFlag(ITEM_FIELD_FLAG_SOULBOUND);
else
RemoveItemFlag(ITEM_FIELD_FLAG_SOULBOUND);
}
bool HasItemFlag(ItemFieldFlags flag) const { return (*m_itemData->DynamicFlags & flag) != 0; }
void SetItemFlag(ItemFieldFlags flags) { SetUpdateFieldFlagValue(m_values.ModifyValue(&Item::m_itemData).ModifyValue(&UF::ItemData::DynamicFlags), flags); }
void RemoveItemFlag(ItemFieldFlags flags) { RemoveUpdateFieldFlagValue(m_values.ModifyValue(&Item::m_itemData).ModifyValue(&UF::ItemData::DynamicFlags), flags); }
void ReplaceAllItemFlags(ItemFieldFlags flags) { SetUpdateFieldValue(m_values.ModifyValue(&Item::m_itemData).ModifyValue(&UF::ItemData::DynamicFlags), flags); }
bool HasItemZoneFlag(ItemZoneFlags flag) const { return (*m_itemData->ZoneFlags & flag) != 0; }
void SetItemZoneFlag(ItemZoneFlags flags) { SetUpdateFieldFlagValue(m_values.ModifyValue(&Item::m_itemData).ModifyValue(&UF::ItemData::ZoneFlags), flags); }
void RemoveItemZoneFlag(ItemZoneFlags flags) { RemoveUpdateFieldFlagValue(m_values.ModifyValue(&Item::m_itemData).ModifyValue(&UF::ItemData::ZoneFlags), flags); }
void ReplaceAllItemZoneFlags(ItemZoneFlags flags) { SetUpdateFieldValue(m_values.ModifyValue(&Item::m_itemData).ModifyValue(&UF::ItemData::ZoneFlags), flags); }
bool IsSoulBound() const { return HasItemFlag(ITEM_FIELD_FLAG_SOULBOUND); }
bool IsBoundAccountWide() const { return GetTemplate()->HasFlag(ITEM_FLAG_IS_BOUND_TO_ACCOUNT); }
bool IsBattlenetAccountBound() const { return GetTemplate()->HasFlag(ITEM_FLAG2_BNET_ACCOUNT_TRADE_OK); }
bool IsBindedNotWith(Player const* player) const;
bool IsBoundByEnchant() const;
virtual void SaveToDB(CharacterDatabaseTransaction trans);
virtual bool LoadFromDB(ObjectGuid::LowType guid, ObjectGuid ownerGuid, Field* fields, uint32 entry);
void LoadAdditionalDataFromDB(Player const* owner, ItemAdditionalLoadInfo* addionalData);
void LoadArtifactData(Player const* owner, uint64 xp, uint32 artifactAppearanceId, uint32 artifactTier, std::vector& powers); // must be called after LoadFromDB to have gems (relics) initialized
void CheckArtifactRelicSlotUnlock(Player const* owner);
void AddBonuses(uint32 bonusListID);
std::vector const& GetBonusListIDs() const { return m_itemData->ItemBonusKey->BonusListIDs; }
void SetBonuses(std::vector bonusListIDs);
void ClearBonuses();
static void DeleteFromDB(CharacterDatabaseTransaction trans, ObjectGuid::LowType itemGuid);
virtual void DeleteFromDB(CharacterDatabaseTransaction trans);
static void DeleteFromInventoryDB(CharacterDatabaseTransaction trans, ObjectGuid::LowType itemGuid);
void DeleteFromInventoryDB(CharacterDatabaseTransaction trans);
void SaveRefundDataToDB();
void DeleteRefundDataFromDB(CharacterDatabaseTransaction* trans);
Bag* ToBag() { if (IsBag()) return reinterpret_cast(this); else return nullptr; }
Bag const* ToBag() const { if (IsBag()) return reinterpret_cast(this); else return nullptr; }
AzeriteItem* ToAzeriteItem() { return IsAzeriteItem() ? reinterpret_cast(this) : nullptr; }
AzeriteItem const* ToAzeriteItem() const { return IsAzeriteItem() ? reinterpret_cast(this) : nullptr; }
AzeriteEmpoweredItem* ToAzeriteEmpoweredItem() { return IsAzeriteEmpoweredItem() ? reinterpret_cast(this) : nullptr; }
AzeriteEmpoweredItem const* ToAzeriteEmpoweredItem() const { return IsAzeriteEmpoweredItem() ? reinterpret_cast(this) : nullptr; }
bool IsRefundable() const { return HasItemFlag(ITEM_FIELD_FLAG_REFUNDABLE); }
bool IsBOPTradeable() const { return HasItemFlag(ITEM_FIELD_FLAG_BOP_TRADEABLE); }
bool IsWrapped() const { return HasItemFlag(ITEM_FIELD_FLAG_WRAPPED); }
bool IsLocked() const { return !HasItemFlag(ITEM_FIELD_FLAG_UNLOCKED); }
bool IsBag() const { return GetTemplate()->GetInventoryType() == INVTYPE_BAG; }
bool IsAzeriteItem() const { return GetTypeId() == TYPEID_AZERITE_ITEM; }
bool IsAzeriteEmpoweredItem() const { return GetTypeId() == TYPEID_AZERITE_EMPOWERED_ITEM; }
bool IsCurrencyToken() const { return GetTemplate()->IsCurrencyToken(); }
bool IsNotEmptyBag() const;
bool IsBroken() const { return *m_itemData->MaxDurability > 0 && *m_itemData->Durability == 0; }
void SetDurability(uint32 durability) { SetUpdateFieldValue(m_values.ModifyValue(&Item::m_itemData).ModifyValue(&UF::ItemData::Durability), durability); }
void SetMaxDurability(uint32 maxDurability) { SetUpdateFieldValue(m_values.ModifyValue(&Item::m_itemData).ModifyValue(&UF::ItemData::MaxDurability), maxDurability); }
bool CanBeTraded(bool mail = false, bool trade = false) const;
void SetInTrade(bool b = true) { mb_in_trade = b; }
bool IsInTrade() const { return mb_in_trade; }
uint64 CalculateDurabilityRepairCost(float discount) const;
bool HasEnchantRequiredSkill(Player const* player) const;
uint32 GetEnchantRequiredLevel() const;
bool IsFitToSpellRequirements(SpellInfo const* spellInfo) const;
bool IsLimitedToAnotherMapOrZone(uint32 cur_mapId, uint32 cur_zoneId) const;
bool GemsFitSockets() const;
uint32 GetCount() const { return m_itemData->StackCount; }
void SetCount(uint32 value);
uint32 GetMaxStackCount() const { return GetTemplate()->GetMaxStackSize(); }
uint8 GetGemCountWithID(uint32 GemID) const;
uint8 GetGemCountWithLimitCategory(uint32 limitCategory) const;
InventoryResult CanBeMergedPartlyWith(ItemTemplate const* proto) const;
uint8 GetSlot() const {return m_slot;}
Bag* GetContainer() { return m_container; }
uint8 GetBagSlot() const;
void SetSlot(uint8 slot) { m_slot = slot; }
uint16 GetPos() const { return uint16(GetBagSlot()) << 8 | GetSlot(); }
void SetContainer(Bag* container) { m_container = container; }
bool IsInBag() const { return m_container != nullptr; }
bool IsEquipped() const;
uint32 GetSkill();
ItemRandomBonusListId GetItemRandomBonusListId() const { return m_randomBonusListId; }
void SetItemRandomBonusList(ItemRandomBonusListId bonusListId);
void SetEnchantment(EnchantmentSlot slot, uint32 id, uint32 duration, uint32 charges, ObjectGuid caster = ObjectGuid::Empty);
void SetEnchantmentDuration(EnchantmentSlot slot, uint32 duration, Player* owner);
void SetEnchantmentCharges(EnchantmentSlot slot, uint32 charges);
void ClearEnchantment(EnchantmentSlot slot);
uint32 GetEnchantmentId(EnchantmentSlot slot) const { return m_itemData->Enchantment[slot].ID; }
uint32 GetEnchantmentDuration(EnchantmentSlot slot) const { return m_itemData->Enchantment[slot].Duration; }
uint32 GetEnchantmentCharges(EnchantmentSlot slot) const { return m_itemData->Enchantment[slot].Charges; }
UF::SocketedGem const* GetGem(uint16 slot) const;
void SetGem(uint16 slot, ItemDynamicFieldGems const* gem, uint32 gemScalingLevel);
std::string const& GetText() const { return m_text; }
void SetText(std::string const& text) { m_text = text; }
void SendUpdateSockets();
void SendTimeUpdate(Player* owner);
void UpdateDuration(Player* owner, uint32 diff);
void SetCreatePlayedTime(uint32 createPlayedTime) { SetUpdateFieldValue(m_values.ModifyValue(&Item::m_itemData).ModifyValue(&UF::ItemData::CreatePlayedTime), createPlayedTime); }
void SetCreateTime(int64 createTime) { SetUpdateFieldValue(m_values.ModifyValue(&Item::m_itemData).ModifyValue(&UF::ItemData::CreateTime), createTime); }
// spell charges (signed but stored as unsigned)
int32 GetSpellCharges(ItemEffectEntry const* effect = nullptr) const;
void SetSpellCharges(ItemEffectEntry const* effect, int32 value);
std::unique_ptr m_loot;
bool m_lootGenerated;
Loot* GetLootForPlayer(Player const* /*player*/) const override { return m_loot.get(); }
// Update States
ItemUpdateState GetState() const { return uState; }
void SetState(ItemUpdateState state, Player* forplayer = nullptr);
bool IsInUpdateQueue() const { return uQueuePos != -1; }
uint16 GetQueuePos() const { return uQueuePos; }
void FSetState(ItemUpdateState state) // forced
{
uState = state;
}
bool hasQuest(uint32 quest_id) const override { return GetTemplate()->GetStartQuest() == quest_id; }
bool hasInvolvedQuest(uint32 /*quest_id*/) const override { return false; }
bool IsPotion() const { return GetTemplate()->IsPotion(); }
bool IsVellum() const { return GetTemplate()->IsVellum(); }
bool IsConjuredConsumable() const { return GetTemplate()->IsConjuredConsumable(); }
uint32 GetQuality() const { return _bonusData.Quality; }
uint32 GetItemLevel(Player const* owner) const;
static uint32 GetItemLevel(ItemTemplate const* itemTemplate, BonusData const& bonusData, uint32 level, uint32 fixedLevel,
uint32 minItemLevel, uint32 minItemLevelCutoff, uint32 maxItemLevel, bool pvpBonus, uint32 azeriteLevel);
int32 GetRequiredLevel() const;
int32 GetItemStatType(uint32 index) const { ASSERT(index < MAX_ITEM_PROTO_STATS); return _bonusData.ItemStatType[index]; }
float GetItemStatValue(uint32 index, Player const* owner) const;
uint32 GetSocketColor(uint32 index) const { ASSERT(index < MAX_ITEM_PROTO_SOCKETS); return _bonusData.SocketColor[index]; }
uint32 GetAppearanceModId() const { return m_itemData->ItemAppearanceModID; }
void SetAppearanceModId(uint32 appearanceModId) { SetUpdateFieldValue(m_values.ModifyValue(&Item::m_itemData).ModifyValue(&UF::ItemData::ItemAppearanceModID), appearanceModId); }
uint32 GetDisplayId(Player const* owner) const;
ItemModifiedAppearanceEntry const* GetItemModifiedAppearance() const;
float GetRepairCostMultiplier() const { return _bonusData.RepairCostMultiplier; }
uint32 GetScalingContentTuningId() const { return _bonusData.ContentTuningId; }
Optional GetDisenchantLootId() const;
Optional GetDisenchantSkillRequired() const;
static ItemDisenchantLootEntry const* GetBaseDisenchantLoot(ItemTemplate const* itemTemplate, uint32 quality, uint32 itemLevel);
void SetFixedLevel(uint8 level);
std::span GetEffects() const { return { _bonusData.Effects.data(), _bonusData.EffectCount }; }
// Item Refund system
void SetNotRefundable(Player* owner, bool changestate = true, CharacterDatabaseTransaction* trans = nullptr, bool addToCollection = true);
void SetRefundRecipient(ObjectGuid const& guid) { m_refundRecipient = guid; }
void SetPaidMoney(uint64 money) { m_paidMoney = money; }
void SetPaidExtendedCost(uint32 iece) { m_paidExtendedCost = iece; }
ObjectGuid const& GetRefundRecipient() const { return m_refundRecipient; }
uint64 GetPaidMoney() const { return m_paidMoney; }
uint32 GetPaidExtendedCost() const { return m_paidExtendedCost; }
uint32 GetPlayedTime() const;
bool IsRefundExpired() const;
// Soulbound trade system
void SetSoulboundTradeable(GuidSet const& allowedLooters);
void ClearSoulboundTradeable(Player* currentOwner);
bool CheckSoulboundTradeExpire();
void BuildUpdate(UpdateDataMapType&) override;
protected:
UF::UpdateFieldFlag GetUpdateFieldFlagsFor(Player const* target) const final;
void BuildValuesCreate(ByteBuffer* data, UF::UpdateFieldFlag flags, Player const* target) const override;
void BuildValuesUpdate(ByteBuffer* data, UF::UpdateFieldFlag flags, Player const* target) const override;
void ClearUpdateMask(bool remove) override;
public:
void BuildValuesUpdateWithFlag(ByteBuffer* data, UF::UpdateFieldFlag flags, Player const* target) const override;
void BuildValuesUpdateForPlayerWithMask(UpdateData* data, UF::ObjectData::Mask const& requestedObjectMask,
UF::ItemData::Mask const& requestedItemMask, Player const* target) const;
struct ValuesUpdateForPlayerWithMaskSender // sender compatible with MessageDistDeliverer
{
explicit ValuesUpdateForPlayerWithMaskSender(Item const* owner) : Owner(owner) { }
Item const* Owner;
UF::ObjectData::Base ObjectMask;
UF::ItemData::Base ItemMask;
void operator()(Player const* player) const;
};
bool AddToObjectUpdate() override;
void RemoveFromObjectUpdate() override;
uint32 GetScriptId() const { return GetTemplate()->ScriptId; }
bool IsValidTransmogrificationTarget() const;
static bool CanTransmogrifyItemWithItem(Item const* item, ItemModifiedAppearanceEntry const* itemModifiedAppearance);
uint32 GetBuyPrice(Player const* owner, bool& standardPrice) const;
static uint32 GetBuyPrice(ItemTemplate const* proto, uint32 quality, uint32 itemLevel, bool& standardPrice);
uint32 GetSellPrice(Player const* owner) const;
static uint32 GetSellPrice(ItemTemplate const* proto, uint32 quality, uint32 itemLevel);
uint32 GetVisibleEntry(Player const* owner) const;
uint16 GetVisibleAppearanceModId(Player const* owner) const;
uint32 GetVisibleModifiedAppearanceId(Player const* owner) const;
int32 GetVisibleSecondaryModifiedAppearanceId(Player const* owner) const;
uint32 GetVisibleEnchantmentId(Player const* owner) const;
uint16 GetVisibleItemVisual(Player const* owner) const;
uint32 GetModifier(ItemModifier modifier) const;
void SetModifier(ItemModifier modifier, uint32 value);
ObjectGuid GetChildItem() const { return m_childItem; }
void SetChildItem(ObjectGuid childItem) { m_childItem = childItem; }
bool IsArtifactDisabled() const;
UF::ArtifactPower const* GetArtifactPower(uint32 artifactPowerId) const;
void AddArtifactPower(ArtifactPowerData const* artifactPower);
void SetArtifactPower(uint16 artifactPowerId, uint8 purchasedRank, uint8 currentRankWithBonus);
void InitArtifactPowers(uint8 artifactId, uint8 artifactTier);
uint32 GetTotalUnlockedArtifactPowers() const;
uint32 GetTotalPurchasedArtifactPowers() const;
void ApplyArtifactPowerEnchantmentBonuses(EnchantmentSlot slot, uint32 enchantId, bool apply, Player* owner);
void CopyArtifactDataFromParent(Item* parent);
void SetArtifactXP(uint64 xp) { SetUpdateFieldValue(m_values.ModifyValue(&Item::m_itemData).ModifyValue(&UF::ItemData::ArtifactXP), xp); }
void GiveArtifactXp(uint64 amount, Item* sourceItem, uint32 artifactCategoryId);
ItemContext GetContext() const { return ItemContext(*m_itemData->Context); }
void SetContext(ItemContext context) { SetUpdateFieldValue(m_values.ModifyValue(&Item::m_itemData).ModifyValue(&UF::ItemData::Context), int32(context)); }
void SetPetitionId(uint32 petitionId) { SetUpdateFieldValue(m_values.ModifyValue(&Item::m_itemData).ModifyValue(&UF::ItemData::Enchantment, 0).ModifyValue(&UF::ItemEnchantment::ID), petitionId); }
void SetPetitionNumSignatures(uint32 signatures) { SetUpdateFieldValue(m_values.ModifyValue(&Item::m_itemData).ModifyValue(&UF::ItemData::Enchantment, 0).ModifyValue(&UF::ItemEnchantment::Duration), signatures); }
std::string GetDebugInfo() const override;
UF::UpdateField m_itemData;
protected:
BonusData _bonusData;
private:
std::string m_text;
uint8 m_slot;
Bag* m_container;
ItemUpdateState uState;
int16 uQueuePos;
bool mb_in_trade; // true if item is currently in trade-window
ObjectGuid m_refundRecipient;
uint64 m_paidMoney;
uint32 m_paidExtendedCost;
GuidSet allowedGUIDs;
ItemRandomBonusListId m_randomBonusListId; // store separately to easily find which bonus list is the one randomly given for stat rerolling
ObjectGuid m_childItem;
std::array m_gemScalingLevels;
int32 GetArtifactPowerIndex(uint32 artifactPowerId) const;
};
#endif