diff options
| author | Shauren <shauren.trinity@gmail.com> | 2016-07-05 22:07:35 +0200 |
|---|---|---|
| committer | Shauren <shauren.trinity@gmail.com> | 2016-07-05 22:07:35 +0200 |
| commit | f7883bd5251a759da1ca8be3ba6f6cead36723ec (patch) | |
| tree | affce8f9ad4b343ba936ad6effa2127ab7d831b1 /src/server/game/Entities | |
| parent | bc81ae70bc350a3decead610f1b17452bd44eec4 (diff) | |
Core/Transmog: Implemented transmog collection and updated transmog handling
Diffstat (limited to 'src/server/game/Entities')
| -rw-r--r-- | src/server/game/Entities/Item/Item.cpp | 236 | ||||
| -rw-r--r-- | src/server/game/Entities/Item/Item.h | 58 | ||||
| -rw-r--r-- | src/server/game/Entities/Item/ItemTemplate.h | 5 | ||||
| -rw-r--r-- | src/server/game/Entities/Object/Object.cpp | 19 | ||||
| -rw-r--r-- | src/server/game/Entities/Player/CollectionMgr.cpp | 364 | ||||
| -rw-r--r-- | src/server/game/Entities/Player/CollectionMgr.h | 33 | ||||
| -rw-r--r-- | src/server/game/Entities/Player/Player.cpp | 98 | ||||
| -rw-r--r-- | src/server/game/Entities/Player/Player.h | 2 |
8 files changed, 677 insertions, 138 deletions
diff --git a/src/server/game/Entities/Item/Item.cpp b/src/server/game/Entities/Item/Item.cpp index 9fbea011dd6..9bb17ec1fa5 100644 --- a/src/server/game/Entities/Item/Item.cpp +++ b/src/server/game/Entities/Item/Item.cpp @@ -32,6 +32,7 @@ #include "ItemPackets.h" #include "TradeData.h" #include "GameTables.h" +#include "CollectionMgr.h" void AddItemsSetItem(Player* player, Item* item) { @@ -234,6 +235,34 @@ bool ItemCanGoIntoBag(ItemTemplate const* pProto, ItemTemplate const* pBagProto) return false; } +ItemModifier const AppearanceModifierSlotBySpec[MAX_SPECIALIZATIONS] = +{ + ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_1, + ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_2, + ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_3, + ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_4 +}; + +static uint32 const AppearanceModifierMaskSpecSpecific = + (1 << ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_1) | + (1 << ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_2) | + (1 << ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_3) | + (1 << ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_4); + +ItemModifier const IllusionModifierSlotBySpec[MAX_SPECIALIZATIONS] = +{ + ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_1, + ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_2, + ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_3, + ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_4 +}; + +static uint32 const IllusionModifierMaskSpecSpecific = + (1 << ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_1) | + (1 << ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_2) | + (1 << ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_3) | + (1 << ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_4); + Item::Item() { m_objectType |= TYPEMASK_ITEM; @@ -356,9 +385,7 @@ void Item::SaveToDB(SQLTransaction& trans) stmt->setUInt16(++index, GetUInt32Value(ITEM_FIELD_DURABILITY)); stmt->setUInt32(++index, GetUInt32Value(ITEM_FIELD_CREATE_PLAYED_TIME)); stmt->setString(++index, m_text); - stmt->setUInt32(++index, GetModifier(ITEM_MODIFIER_TRANSMOG_ITEM_ID) | (GetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_MOD) << 24)); stmt->setUInt32(++index, GetModifier(ITEM_MODIFIER_UPGRADE_ID)); - stmt->setUInt32(++index, GetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION)); stmt->setUInt32(++index, GetModifier(ITEM_MODIFIER_BATTLE_PET_SPECIES_ID)); stmt->setUInt32(++index, GetModifier(ITEM_MODIFIER_BATTLE_PET_BREED_DATA)); stmt->setUInt32(++index, GetModifier(ITEM_MODIFIER_BATTLE_PET_LEVEL)); @@ -395,6 +422,43 @@ void Item::SaveToDB(SQLTransaction& trans) stmt->setUInt32(1 + i, 0); trans->Append(stmt); } + + static ItemModifier const transmogMods[10] = + { + ITEM_MODIFIER_TRANSMOG_APPEARANCE_ALL_SPECS, + ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_1, + ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_2, + ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_3, + ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_4, + + ITEM_MODIFIER_ENCHANT_ILLUSION_ALL_SPECS, + ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_1, + ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_2, + ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_3, + ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_4, + }; + + stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_INSTANCE_TRANSMOG); + stmt->setUInt64(0, GetGUID().GetCounter()); + trans->Append(stmt); + + if (std::find_if(std::begin(transmogMods), std::end(transmogMods), [this](ItemModifier modifier) { return GetModifier(modifier) != 0; }) != std::end(transmogMods)) + { + stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_ITEM_INSTANCE_TRANSMOG); + stmt->setUInt64(0, GetGUID().GetCounter()); + stmt->setUInt32(1, GetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_ALL_SPECS)); + stmt->setUInt32(2, GetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_1)); + stmt->setUInt32(3, GetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_2)); + stmt->setUInt32(4, GetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_3)); + stmt->setUInt32(5, GetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_4)); + stmt->setUInt32(6, GetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_ALL_SPECS)); + stmt->setUInt32(7, GetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_1)); + stmt->setUInt32(8, GetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_2)); + stmt->setUInt32(9, GetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_3)); + stmt->setUInt32(10, GetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_4)); + trans->Append(stmt); + } + break; } case ITEM_REMOVED: @@ -436,12 +500,16 @@ void Item::SaveToDB(SQLTransaction& trans) bool Item::LoadFromDB(ObjectGuid::LowType guid, ObjectGuid ownerGuid, Field* fields, uint32 entry) { - // 0 1 2 3 4 5 6 7 8 9 10 11 12 - //result = CharacterDatabase.PQuery("SELECT guid, itemEntry, creatorGuid, giftCreatorGuid, count, duration, charges, flags, enchantments, randomPropertyId, durability, playedTime, text, - // 13 14 15 16 17 18 19 20 - // transmogrification, upgradeId, enchantIllusion, battlePetSpeciesId, battlePetBreedData, battlePetLevel, battlePetDisplayId, bonusListIDs, - // 21 22 23 - // gemItemId1, gemItemId2, gemItemId3 FROM item_instance WHERE guid = '%u'", guid); + // 0 1 2 3 4 5 6 7 8 9 10 11 12 + // SELECT guid, itemEntry, creatorGuid, giftCreatorGuid, count, duration, charges, flags, enchantments, randomPropertyId, durability, playedTime, text, + // 13 14 15 16 17 18 + // upgradeId, battlePetSpeciesId, battlePetBreedData, battlePetLevel, battlePetDisplayId, bonusListIDs, + // 19 20 21 22 23 + // itemModifiedAppearanceAllSpecs, itemModifiedAppearanceSpec1, itemModifiedAppearanceSpec2, itemModifiedAppearanceSpec3, itemModifiedAppearanceSpec4, + // 24 25 26 27 28 + // spellItemEnchantmentAllSpecs, spellItemEnchantmentSpec1, spellItemEnchantmentSpec2, spellItemEnchantmentSpec3, spellItemEnchantmentSpec4, + // 29 30 31 + // gemItemId1, gemItemId2, gemItemId3 FROM item_instance // create item before any checks for store correct guid // and allow use "FSetState(ITEM_REMOVED); SaveToDB();" for deleting item from DB @@ -509,19 +577,32 @@ bool Item::LoadFromDB(ObjectGuid::LowType guid, ObjectGuid ownerGuid, Field* fie SetUInt32Value(ITEM_FIELD_CREATE_PLAYED_TIME, fields[11].GetUInt32()); SetText(fields[12].GetString()); - if (uint32 transmogEntry = fields[13].GetUInt32()) + SetModifier(ITEM_MODIFIER_UPGRADE_ID, fields[13].GetUInt32()); + SetModifier(ITEM_MODIFIER_BATTLE_PET_SPECIES_ID, fields[14].GetUInt32()); + SetModifier(ITEM_MODIFIER_BATTLE_PET_BREED_DATA, fields[15].GetUInt32()); + SetModifier(ITEM_MODIFIER_BATTLE_PET_LEVEL, fields[16].GetUInt16()); + SetModifier(ITEM_MODIFIER_BATTLE_PET_DISPLAY_ID, fields[17].GetUInt32()); + + Tokenizer bonusListIDs(fields[18].GetString(), ' '); + for (char const* token : bonusListIDs) { - SetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_MOD, (transmogEntry >> 24) & 0xFF); - SetModifier(ITEM_MODIFIER_TRANSMOG_ITEM_ID, transmogEntry & 0xFFFFFF); + uint32 bonusListID = atoul(token); + AddBonuses(bonusListID); } - SetModifier(ITEM_MODIFIER_UPGRADE_ID, fields[14].GetUInt32()); - SetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION, fields[15].GetUInt32()); - SetModifier(ITEM_MODIFIER_BATTLE_PET_SPECIES_ID, fields[16].GetUInt32()); - SetModifier(ITEM_MODIFIER_BATTLE_PET_BREED_DATA, fields[17].GetUInt32()); - SetModifier(ITEM_MODIFIER_BATTLE_PET_LEVEL, fields[18].GetUInt16()); - SetModifier(ITEM_MODIFIER_BATTLE_PET_DISPLAY_ID, fields[19].GetUInt32()); - - uint32 gemItemIds[3] = { fields[21].GetUInt32(), fields[22].GetUInt32(), fields[23].GetUInt32() }; + + SetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_ALL_SPECS, fields[19].GetUInt32()); + SetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_1, fields[20].GetUInt32()); + SetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_2, fields[21].GetUInt32()); + SetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_3, fields[22].GetUInt32()); + SetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_4, fields[23].GetUInt32()); + + SetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_ALL_SPECS, fields[24].GetUInt32()); + SetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_1, fields[25].GetUInt32()); + SetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_2, fields[26].GetUInt32()); + SetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_3, fields[27].GetUInt32()); + SetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_4, fields[28].GetUInt32()); + + uint32 gemItemIds[3] = { fields[29].GetUInt32(), fields[30].GetUInt32(), fields[31].GetUInt32() }; if (gemItemIds[0] || gemItemIds[1] || gemItemIds[2]) { // gem slots must be preserved, hence funky logic @@ -532,13 +613,6 @@ bool Item::LoadFromDB(ObjectGuid::LowType guid, ObjectGuid ownerGuid, Field* fie AddDynamicValue(ITEM_DYNAMIC_FIELD_GEMS, gemItemIds[2]); } - Tokenizer bonusListIDs(fields[20].GetString(), ' '); - for (char const* token : bonusListIDs) - { - uint32 bonusListID = atoul(token); - AddBonuses(bonusListID); - } - if (need_save) // normal item changed state set not work at loading { PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_ITEM_INSTANCE_ON_LOAD); @@ -1130,7 +1204,7 @@ void Item::BuildDynamicValuesUpdate(uint8 updateType, ByteBuffer* data, Player* arrayMask.SetCount(values.size()); for (std::size_t v = 0; v < values.size(); ++v) { - if (updateType != UPDATETYPE_VALUES || _dynamicChangesArrayMask[index].GetBit(v)) + if (updateType == UPDATETYPE_VALUES ? _dynamicChangesArrayMask[index].GetBit(v) : values[v]) { arrayMask.SetBit(v); buffer << uint32(values[v]); @@ -1149,7 +1223,6 @@ void Item::BuildDynamicValuesUpdate(uint8 updateType, ByteBuffer* data, Player* buffer << uint32(modifier); } } - } fieldBuffer << uint16(arrayMask.GetBlockCount()); @@ -1204,7 +1277,7 @@ void Item::DeleteRefundDataFromDB(SQLTransaction* trans) } } -void Item::SetNotRefundable(Player* owner, bool changestate /*=true*/, SQLTransaction* trans /*=NULL*/) +void Item::SetNotRefundable(Player* owner, bool changestate /*= true*/, SQLTransaction* trans /*= nullptr*/, bool addToCollection /*= true*/) { if (!HasFlag(ITEM_FIELD_FLAGS, ITEM_FIELD_FLAG_REFUNDABLE)) return; @@ -1224,6 +1297,8 @@ void Item::SetNotRefundable(Player* owner, bool changestate /*=true*/, SQLTransa DeleteRefundDataFromDB(trans); owner->DeleteRefundReference(GetGUID()); + if (addToCollection) + owner->GetSession()->GetCollectionMgr()->AddItemAppearance(this); } void Item::UpdatePlayedTime(Player* owner) @@ -1278,6 +1353,7 @@ void Item::ClearSoulboundTradeable(Player* currentOwner) if (allowedGUIDs.empty()) return; + currentOwner->GetSession()->GetCollectionMgr()->AddItemAppearance(this); allowedGUIDs.clear(); SetState(ITEM_CHANGED, currentOwner); PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_BOP_TRADE); @@ -1303,9 +1379,6 @@ bool Item::IsValidTransmogrificationTarget() const if (!proto) return false; - if (proto->GetQuality() == ITEM_QUALITY_LEGENDARY) - return false; - if (proto->GetClass() != ITEM_CLASS_ARMOR && proto->GetClass() != ITEM_CLASS_WEAPON) return false; @@ -1322,34 +1395,6 @@ bool Item::IsValidTransmogrificationTarget() const return true; } -bool Item::IsValidTransmogrificationSource(WorldPackets::Item::ItemInstance const& transmogrifier, BonusData const* bonus) -{ - ItemTemplate const* proto = sObjectMgr->GetItemTemplate(transmogrifier.ItemID); - if (!proto) - return false; - - if (proto->GetFlags2() & ITEM_FLAG2_CANNOT_TRANSMOG) - return false; - - if (proto->GetQuality() == ITEM_QUALITY_LEGENDARY) - return false; - - if (proto->GetClass() != ITEM_CLASS_ARMOR && - proto->GetClass() != ITEM_CLASS_WEAPON) - return false; - - if (proto->GetClass() == ITEM_CLASS_WEAPON && proto->GetSubClass() == ITEM_SUBCLASS_WEAPON_FISHING_POLE) - return false; - - if (proto->GetFlags2() & ITEM_FLAG2_CAN_TRANSMOG) - return true; - - if (!HasStats(transmogrifier, bonus)) - return false; - - return true; -} - bool Item::HasStats() const { if (GetItemRandomPropertyId() != 0) @@ -1456,18 +1501,18 @@ int32 const ItemTransmogrificationSlots[MAX_INVTYPE] = -1 // INVTYPE_RELIC }; -bool Item::CanTransmogrifyItemWithItem(Item const* transmogrified, WorldPackets::Item::ItemInstance const& transmogrifier, BonusData const* bonus) +bool Item::CanTransmogrifyItemWithItem(Item const* item, ItemModifiedAppearanceEntry const* itemModifiedAppearance) { - ItemTemplate const* source = sObjectMgr->GetItemTemplate(transmogrifier.ItemID); // source - ItemTemplate const* target = transmogrified->GetTemplate(); // dest + ItemTemplate const* source = sObjectMgr->GetItemTemplate(itemModifiedAppearance->ItemID); // source + ItemTemplate const* target = item->GetTemplate(); // dest if (!source || !target) return false; - if (sDB2Manager.GetItemDisplayId(source->GetId(), bonus->AppearanceModID) == transmogrified->GetDisplayId()) + if (itemModifiedAppearance == item->GetItemModifiedAppearance()) return false; - if (!IsValidTransmogrificationSource(transmogrifier, bonus) || !transmogrified->IsValidTransmogrificationTarget()) + if (!item->IsValidTransmogrificationTarget()) return false; if (source->GetClass() != target->GetClass()) @@ -1483,11 +1528,19 @@ bool Item::CanTransmogrifyItemWithItem(Item const* transmogrified, WorldPackets: if (source->GetSubClass() != target->GetSubClass()) { - if (source->GetClass() != ITEM_CLASS_WEAPON) - return false; - - if (GetTransmogrificationWeaponCategory(source) != GetTransmogrificationWeaponCategory(target)) - return false; + switch (source->GetClass()) + { + case ITEM_CLASS_WEAPON: + if (GetTransmogrificationWeaponCategory(source) != GetTransmogrificationWeaponCategory(target)) + return false; + break; + case ITEM_CLASS_ARMOR: + if (source->GetSubClass() != ITEM_SUBCLASS_ARMOR_COSMETIC) + return false; + break; + default: + return false; + } } if (source->GetInventoryType() != target->GetInventoryType()) @@ -1863,10 +1916,14 @@ int32 Item::GetItemStatValue(uint32 index, Player const* owner) const return _bonusData.ItemStatValue[index]; } -uint32 Item::GetDisplayId() const +uint32 Item::GetDisplayId(Player const* owner) const { - if (uint32 transmogrification = GetModifier(ITEM_MODIFIER_TRANSMOG_ITEM_ID)) - return sDB2Manager.GetItemDisplayId(transmogrification, GetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_MOD)); + ItemModifier transmogModifier = ITEM_MODIFIER_TRANSMOG_APPEARANCE_ALL_SPECS; + if (HasFlag(ITEM_FIELD_MODIFIERS_MASK, AppearanceModifierMaskSpecSpecific)) + transmogModifier = AppearanceModifierSlotBySpec[owner->GetActiveTalentGroup()]; + + if (ItemModifiedAppearanceEntry const* transmog = sItemModifiedAppearanceStore.LookupEntry(GetModifier(transmogModifier))) + return sDB2Manager.GetItemDisplayId(transmog->ItemID, transmog->AppearanceModID); return sDB2Manager.GetItemDisplayId(GetEntry(), GetAppearanceModId()); } @@ -1882,33 +1939,46 @@ void Item::SetModifier(ItemModifier modifier, uint32 value) ApplyModFlag(ITEM_FIELD_MODIFIERS_MASK, 1 << modifier, value != 0); } -uint32 Item::GetVisibleEntry() const +uint32 Item::GetVisibleEntry(Player const* owner) const { - if (uint32 transmogrification = GetModifier(ITEM_MODIFIER_TRANSMOG_ITEM_ID)) - return transmogrification; + ItemModifier transmogModifier = ITEM_MODIFIER_TRANSMOG_APPEARANCE_ALL_SPECS; + if (HasFlag(ITEM_FIELD_MODIFIERS_MASK, AppearanceModifierMaskSpecSpecific)) + transmogModifier = AppearanceModifierSlotBySpec[owner->GetActiveTalentGroup()]; + + if (ItemModifiedAppearanceEntry const* transmog = sItemModifiedAppearanceStore.LookupEntry(GetModifier(transmogModifier))) + return transmog->ItemID; return GetEntry(); } -uint16 Item::GetVisibleAppearanceModId() const +uint16 Item::GetVisibleAppearanceModId(Player const* owner) const { - if (GetModifier(ITEM_MODIFIER_TRANSMOG_ITEM_ID)) - return uint16(GetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_MOD)); + ItemModifier transmogModifier = ITEM_MODIFIER_TRANSMOG_APPEARANCE_ALL_SPECS; + if (HasFlag(ITEM_FIELD_MODIFIERS_MASK, AppearanceModifierMaskSpecSpecific)) + transmogModifier = AppearanceModifierSlotBySpec[owner->GetActiveTalentGroup()]; + + if (ItemModifiedAppearanceEntry const* transmog = sItemModifiedAppearanceStore.LookupEntry(GetModifier(transmogModifier))) + return transmog->AppearanceModID; return uint16(GetAppearanceModId()); } -uint32 Item::GetVisibleEnchantmentId() const +uint32 Item::GetVisibleEnchantmentId(Player const* owner) const { - if (uint32 enchantIllusion = GetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION)) + ItemModifier illusionModifier = ITEM_MODIFIER_TRANSMOG_APPEARANCE_ALL_SPECS; + if (HasFlag(ITEM_FIELD_MODIFIERS_MASK, IllusionModifierMaskSpecSpecific)) + illusionModifier = IllusionModifierSlotBySpec[owner->GetActiveTalentGroup()]; + + if (ItemModifiedAppearanceEntry const* transmog = sItemModifiedAppearanceStore.LookupEntry(GetModifier(illusionModifier))) + if (uint32 enchantIllusion = GetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_ALL_SPECS)) return enchantIllusion; return GetEnchantmentId(PERM_ENCHANTMENT_SLOT); } -uint16 Item::GetVisibleItemVisual() const +uint16 Item::GetVisibleItemVisual(Player const* owner) const { - if (SpellItemEnchantmentEntry const* enchant = sSpellItemEnchantmentStore.LookupEntry(GetVisibleEnchantmentId())) + if (SpellItemEnchantmentEntry const* enchant = sSpellItemEnchantmentStore.LookupEntry(GetVisibleEnchantmentId(owner))) return enchant->ItemVisual; return 0; diff --git a/src/server/game/Entities/Item/Item.h b/src/server/game/Entities/Item/Item.h index 7f52e3874fe..12a33f86a23 100644 --- a/src/server/game/Entities/Item/Item.h +++ b/src/server/game/Entities/Item/Item.h @@ -208,10 +208,12 @@ enum EnchantmentOffset enum EnchantmentSlotMask { - ENCHANTMENT_CAN_SOULBOUND = 0x01, - ENCHANTMENT_UNK1 = 0x02, - ENCHANTMENT_UNK2 = 0x04, - ENCHANTMENT_UNK3 = 0x08 + ENCHANTMENT_CAN_SOULBOUND = 0x01, + ENCHANTMENT_UNK1 = 0x02, + ENCHANTMENT_UNK2 = 0x04, + ENCHANTMENT_UNK3 = 0x08, + ENCHANTMENT_COLLECTABLE = 0x100, + ENCHANTMENT_HIDE_IF_NOT_COLLECTED = 0x200, }; enum ItemUpdateState @@ -224,14 +226,29 @@ enum ItemUpdateState enum ItemModifier { - ITEM_MODIFIER_TRANSMOG_APPEARANCE_MOD = 0, - ITEM_MODIFIER_TRANSMOG_ITEM_ID = 1, - ITEM_MODIFIER_UPGRADE_ID = 2, - ITEM_MODIFIER_BATTLE_PET_SPECIES_ID = 3, - ITEM_MODIFIER_BATTLE_PET_BREED_DATA = 4, // (breedId) | (breedQuality << 24) - ITEM_MODIFIER_BATTLE_PET_LEVEL = 5, - ITEM_MODIFIER_BATTLE_PET_DISPLAY_ID = 6, - ITEM_MODIFIER_ENCHANT_ILLUSION = 7, + ITEM_MODIFIER_TRANSMOG_APPEARANCE_ALL_SPECS = 0, + ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_1 = 1, + ITEM_MODIFIER_UPGRADE_ID = 2, + ITEM_MODIFIER_BATTLE_PET_SPECIES_ID = 3, + ITEM_MODIFIER_BATTLE_PET_BREED_DATA = 4, // (breedId) | (breedQuality << 24) + ITEM_MODIFIER_BATTLE_PET_LEVEL = 5, + ITEM_MODIFIER_BATTLE_PET_DISPLAY_ID = 6, + ITEM_MODIFIER_ENCHANT_ILLUSION_ALL_SPECS = 7, + ITEM_MODIFIER_ARTIFACT_APPEARANCE_ID = 8, + ITEM_MODIFIER_SCALING_STAT_DISTRIBUTION_FIXED_LEVEL = 9, + ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_1 = 10, + ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_2 = 11, + ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_2 = 12, + ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_3 = 13, + ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_3 = 13, + ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_4 = 15, + ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_4 = 16, + ITEM_MODIFIER_CHALLENGE_MAP_CHALLENGE_MODE_ID = 17, + ITEM_MODIFIER_CHALLENGE_KEYSTONE_LEVEL = 18, + ITEM_MODIFIER_CHALLENGE_KEYSTONE_AFFIX_ID_1 = 19, + ITEM_MODIFIER_CHALLENGE_KEYSTONE_AFFIX_ID_2 = 20, + ITEM_MODIFIER_CHALLENGE_KEYSTONE_AFFIX_ID_3 = 21, + ITEM_MODIFIER_CHALLENGE_KEYSTONE_IS_CHARGED = 22, MAX_ITEM_MODIFIERS }; @@ -239,6 +256,8 @@ enum ItemModifier #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]; struct BonusData { @@ -397,13 +416,13 @@ class TC_GAME_API Item : public Object uint32 GetAppearanceModId() const { return _bonusData.AppearanceModID; } uint32 GetArmor(Player const* owner) const { return GetTemplate()->GetArmor(GetItemLevel(owner)); } void GetDamage(Player const* owner, float& minDamage, float& maxDamage) const { GetTemplate()->GetDamage(GetItemLevel(owner), minDamage, maxDamage); } - uint32 GetDisplayId() const; + uint32 GetDisplayId(Player const* owner) const; ItemModifiedAppearanceEntry const* GetItemModifiedAppearance() const; float GetRepairCostMultiplier() const { return _bonusData.RepairCostMultiplier; } uint32 GetScalingStatDistribution() const { return _bonusData.ScalingStatDistribution; } // Item Refund system - void SetNotRefundable(Player* owner, bool changestate = true, SQLTransaction* trans = NULL); + void SetNotRefundable(Player* owner, bool changestate = true, SQLTransaction* trans = nullptr, bool addToCollection = true); void SetRefundRecipient(ObjectGuid const& guid) { m_refundRecipient = guid; } void SetPaidMoney(uint32 money) { m_paidMoney = money; } void SetPaidExtendedCost(uint32 iece) { m_paidExtendedCost = iece; } @@ -429,17 +448,16 @@ class TC_GAME_API Item : public Object uint32 GetScriptId() const { return GetTemplate()->ScriptId; } bool IsValidTransmogrificationTarget() const; - static bool IsValidTransmogrificationSource(WorldPackets::Item::ItemInstance const& transmogrifier, BonusData const* bonus); bool HasStats() const; static bool HasStats(WorldPackets::Item::ItemInstance const& itemInstance, BonusData const* bonus); - static bool CanTransmogrifyItemWithItem(Item const* transmogrified, WorldPackets::Item::ItemInstance const& transmogrifier, BonusData const* bonus); + static bool CanTransmogrifyItemWithItem(Item const* item, ItemModifiedAppearanceEntry const* itemModifiedAppearance); static uint32 GetSpecialPrice(ItemTemplate const* proto, uint32 minimumPrice = 10000); uint32 GetSpecialPrice(uint32 minimumPrice = 10000) const { return Item::GetSpecialPrice(GetTemplate(), minimumPrice); } - uint32 GetVisibleEntry() const; - uint16 GetVisibleAppearanceModId() const; - uint32 GetVisibleEnchantmentId() const; - uint16 GetVisibleItemVisual() const; + uint32 GetVisibleEntry(Player const* owner) const; + uint16 GetVisibleAppearanceModId(Player const* owner) const; + uint32 GetVisibleEnchantmentId(Player const* owner) const; + uint16 GetVisibleItemVisual(Player const* owner) const; static uint32 GetSellPrice(ItemTemplate const* proto, bool& success); diff --git a/src/server/game/Entities/Item/ItemTemplate.h b/src/server/game/Entities/Item/ItemTemplate.h index 2627f9cf328..756d21e7856 100644 --- a/src/server/game/Entities/Item/ItemTemplate.h +++ b/src/server/game/Entities/Item/ItemTemplate.h @@ -217,7 +217,7 @@ enum ItemFlags2 : uint32 ITEM_FLAG2_NEED_ROLL_DISABLED = 0x00000100, ITEM_FLAG2_CASTER_WEAPON = 0x00000200, ITEM_FLAG2_UNK6 = 0x00000400, - ITEM_FLAG2_UNK7 = 0x00000800, + ITEM_FLAG2_UNAVAILABLE_FOR_PLAYERS= 0x00000800, ITEM_FLAG2_UNK8 = 0x00001000, ITEM_FLAG2_UNK9 = 0x00002000, ITEM_FLAG2_HAS_NORMAL_PRICE = 0x00004000, @@ -490,7 +490,7 @@ enum ItemSubclassArmor ITEM_SUBCLASS_ARMOR_LEATHER = 2, ITEM_SUBCLASS_ARMOR_MAIL = 3, ITEM_SUBCLASS_ARMOR_PLATE = 4, - ITEM_SUBCLASS_ARMOR_BUCKLER = 5, // OBSOLETE + ITEM_SUBCLASS_ARMOR_COSMETIC = 5, ITEM_SUBCLASS_ARMOR_SHIELD = 6, ITEM_SUBCLASS_ARMOR_LIBRAM = 7, ITEM_SUBCLASS_ARMOR_IDOL = 8, @@ -769,6 +769,7 @@ struct TC_GAME_API ItemTemplate uint32 FlagsCu; float SpellPPMRate; std::unordered_set<uint32> Specializations[2]; // one set for 1-40 level range and another for 41-100 + uint32 ItemSpecClassMask; // helpers bool CanChangeEquipStateInCombat() const; diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp index b1a915e474e..028f954744f 100644 --- a/src/server/game/Entities/Object/Object.cpp +++ b/src/server/game/Entities/Object/Object.cpp @@ -813,7 +813,7 @@ void Object::BuildDynamicValuesUpdate(uint8 updateType, ByteBuffer* data, Player arrayMask.SetCount(values.size()); for (std::size_t v = 0; v < values.size(); ++v) { - if (updateType != UPDATETYPE_VALUES || _dynamicChangesArrayMask[index].GetBit(v)) + if (updateType == UPDATETYPE_VALUES ? _dynamicChangesArrayMask[index].GetBit(v) : values[v]) { arrayMask.SetBit(v); buffer << uint32(values[v]); @@ -1382,10 +1382,23 @@ void Object::AddDynamicValue(uint16 index, uint32 value) AddToObjectUpdateIfNeeded(); } -void Object::RemoveDynamicValue(uint16 index, uint32 /*value*/) +void Object::RemoveDynamicValue(uint16 index, uint32 value) { ASSERT(index < _dynamicValuesCount || PrintIndexError(index, false)); - /// TODO: Research if this is actually needed + + // TODO: Research if this is blizzlike to just set value to 0 + std::vector<uint32>& values = _dynamicValues[index]; + for (std::size_t i = 0; i < values.size(); ++i) + { + if (values[i] == value) + { + values[i] = 0; + _dynamicChangesMask.SetBit(index); + _dynamicChangesArrayMask[index].SetBit(i); + + AddToObjectUpdateIfNeeded(); + } + } } void Object::ClearDynamicValue(uint16 index) diff --git a/src/server/game/Entities/Player/CollectionMgr.cpp b/src/server/game/Entities/Player/CollectionMgr.cpp index 2def446ccfb..0dffcc6e8ad 100644 --- a/src/server/game/Entities/Player/CollectionMgr.cpp +++ b/src/server/game/Entities/Player/CollectionMgr.cpp @@ -16,7 +16,13 @@ */ #include "CollectionMgr.h" +#include "ObjectMgr.h" #include "Player.h" +#include "TransmogrificationPackets.h" + +CollectionMgr::CollectionMgr(WorldSession* owner) : _owner(owner), _appearances() +{ +} void CollectionMgr::LoadToys() { @@ -77,6 +83,14 @@ void CollectionMgr::ToySetFavorite(uint32 itemId, bool favorite) itr->second = favorite; } +void CollectionMgr::OnItemAdded(Item* item) +{ + if (sDB2Manager.GetHeirloomByItemId(item->GetEntry())) + AddHeirloom(item->GetEntry(), 0); + + AddItemAppearance(item); +} + void CollectionMgr::LoadAccountHeirlooms(PreparedQueryResult result) { if (!result) @@ -259,3 +273,353 @@ bool CollectionMgr::CanApplyHeirloomXpBonus(uint32 itemId, uint32 level) return level <= 60; } + +struct DynamicBitsetBlockOutputIterator : public std::iterator<std::output_iterator_tag, void, void, void, void> +{ + explicit DynamicBitsetBlockOutputIterator(std::function<void(uint32)>&& action) : _action(std::forward<std::function<void(uint32)>>(action)) { } + + DynamicBitsetBlockOutputIterator& operator=(uint32 value) + { + _action(value); + return *this; + } + + DynamicBitsetBlockOutputIterator& operator*() { return *this; } + DynamicBitsetBlockOutputIterator& operator++() { return *this; } + DynamicBitsetBlockOutputIterator operator++(int) { return *this; } + +private: + std::function<void(uint32)> _action; +}; + +void CollectionMgr::LoadItemAppearances() +{ + boost::to_block_range(_appearances, DynamicBitsetBlockOutputIterator([this](uint32 blockValue) + { + _owner->GetPlayer()->AddDynamicValue(PLAYER_DYNAMIC_FIELD_TRANSMOG, blockValue); + })); + + for (auto itr = _temporaryAppearances.begin(); itr != _temporaryAppearances.end(); ++itr) + _owner->GetPlayer()->AddDynamicValue(PLAYER_DYNAMIC_FIELD_CONDITIONAL_TRANSMOG, itr->first); +} + +void CollectionMgr::LoadAccountItemAppearances(PreparedQueryResult knownAppearances, PreparedQueryResult favoriteAppearances) +{ + if (knownAppearances) + { + std::vector<uint32> blocks; + do + { + Field* fields = knownAppearances->Fetch(); + uint16 blobIndex = fields[0].GetUInt16(); + if (blobIndex >= blocks.size()) + blocks.resize(blobIndex + 1); + + blocks[blobIndex] = fields[1].GetUInt32(); + + } while (knownAppearances->NextRow()); + + _appearances.init_from_block_range(blocks.begin(), blocks.end()); + } + + if (favoriteAppearances) + { + do + { + _favoriteAppearances[favoriteAppearances->Fetch()[0].GetUInt32()] = FavoriteAppearanceState::Unchanged; + } while (favoriteAppearances->NextRow()); + } + + // Static item appearances known by every player + static uint32 const hiddenAppearanceItems[3] = { 134110, 134111, 134112 }; + for (uint32 hiddenItem : hiddenAppearanceItems) + { + ItemModifiedAppearanceEntry const* hiddenAppearance = sDB2Manager.GetItemModifiedAppearance(hiddenItem, 0); + ASSERT(hiddenAppearance); + if (_appearances.size() <= hiddenAppearance->ID) + _appearances.resize(hiddenAppearance->ID + 1); + + _appearances.set(hiddenAppearance->ID); + } +} + +void CollectionMgr::SaveAccountItemAppearances(SQLTransaction& trans) +{ + uint16 blockIndex = 0; + boost::to_block_range(_appearances, DynamicBitsetBlockOutputIterator([this, &blockIndex, trans](uint32 blockValue) + { + if (blockValue) // this table is only appended/bits are set (never cleared) so don't save empty blocks + { + PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_BNET_ITEM_APPEARANCES); + stmt->setUInt32(0, GetOwner()->GetBattlenetAccountId()); + stmt->setUInt16(1, blockIndex); + stmt->setUInt32(2, blockValue); + trans->Append(stmt); + } + + ++blockIndex; + })); + + PreparedStatement* stmt; + for (auto itr = _favoriteAppearances.begin(); itr != _favoriteAppearances.end();) + { + switch (itr->second) + { + case FavoriteAppearanceState::New: + stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_BNET_ITEM_FAVORITE_APPEARANCE); + stmt->setUInt32(0, _owner->GetBattlenetAccountId()); + stmt->setUInt32(1, itr->first); + trans->Append(stmt); + itr->second = FavoriteAppearanceState::Unchanged; + ++itr; + break; + case FavoriteAppearanceState::Removed: + stmt = LoginDatabase.GetPreparedStatement(LOGIN_DEL_BNET_ITEM_FAVORITE_APPEARANCE); + stmt->setUInt32(0, _owner->GetBattlenetAccountId()); + stmt->setUInt32(1, itr->first); + trans->Append(stmt); + itr = _favoriteAppearances.erase(itr); + break; + case FavoriteAppearanceState::Unchanged: + ++itr; + break; + } + } +} + +uint32 const PlayerClassByArmorSubclass[MAX_ITEM_SUBCLASS_ARMOR] = +{ + CLASSMASK_ALL_PLAYABLE, //ITEM_SUBCLASS_ARMOR_MISCELLANEOUS + (1 << (CLASS_PRIEST - 1)) | (1 << (CLASS_MAGE - 1)) | (1 << (CLASS_WARLOCK - 1)), //ITEM_SUBCLASS_ARMOR_CLOTH + (1 << (CLASS_ROGUE - 1)) | (1 << (CLASS_MONK - 1)) | (1 << (CLASS_DRUID - 1)) | (1 << (CLASS_DEMON_HUNTER - 1)), //ITEM_SUBCLASS_ARMOR_LEATHER + (1 << (CLASS_HUNTER - 1)) | (1 << (CLASS_SHAMAN - 1)), //ITEM_SUBCLASS_ARMOR_MAIL + (1 << (CLASS_WARRIOR - 1)) | (1 << (CLASS_PALADIN - 1)) | (1 << (CLASS_DEATH_KNIGHT - 1)), //ITEM_SUBCLASS_ARMOR_PLATE + CLASSMASK_ALL_PLAYABLE, //ITEM_SUBCLASS_ARMOR_BUCKLER + (1 << (CLASS_WARRIOR - 1)) | (1 << (CLASS_PALADIN - 1)) | (1 << (CLASS_SHAMAN - 1)), //ITEM_SUBCLASS_ARMOR_SHIELD + 1 << (CLASS_PALADIN - 1), //ITEM_SUBCLASS_ARMOR_LIBRAM + 1 << (CLASS_DRUID - 1), //ITEM_SUBCLASS_ARMOR_IDOL + 1 << (CLASS_SHAMAN - 1), //ITEM_SUBCLASS_ARMOR_TOTEM + 1 << (CLASS_DEATH_KNIGHT - 1), //ITEM_SUBCLASS_ARMOR_SIGIL + (1 << (CLASS_PALADIN - 1)) | (1 << (CLASS_DEATH_KNIGHT - 1)) | (1 << (CLASS_SHAMAN - 1)) | (1 << (CLASS_DRUID - 1)), //ITEM_SUBCLASS_ARMOR_RELIC +}; + + +void CollectionMgr::AddItemAppearance(Item* item) +{ + if (!item->IsSoulBound()) + return; + + ItemModifiedAppearanceEntry const* itemModifiedAppearance = item->GetItemModifiedAppearance(); + if (!CanAddAppearance(itemModifiedAppearance)) + return; + + if (item->GetUInt32Value(ITEM_FIELD_FLAGS) & (ITEM_FIELD_FLAG_BOP_TRADEABLE | ITEM_FIELD_FLAG_REFUNDABLE)) + { + AddTemporaryAppearance(item->GetGUID(), itemModifiedAppearance); + return; + } + + AddItemAppearance(itemModifiedAppearance); +} + +void CollectionMgr::AddItemAppearance(uint32 itemId) +{ + ItemModifiedAppearanceEntry const* itemModifiedAppearance = sDB2Manager.GetItemModifiedAppearance(itemId, 0); + if (!CanAddAppearance(itemModifiedAppearance)) + return; + + AddItemAppearance(itemModifiedAppearance); +} + +bool CollectionMgr::CanAddAppearance(ItemModifiedAppearanceEntry const* itemModifiedAppearance) const +{ + if (!itemModifiedAppearance) + return false; + + if (itemModifiedAppearance->SourceType == 6 || itemModifiedAppearance->SourceType == 9) + return false; + + if (!sItemSearchNameStore.LookupEntry(itemModifiedAppearance->ItemID)) + return false; + + ItemTemplate const* itemTemplate = sObjectMgr->GetItemTemplate(itemModifiedAppearance->ItemID); + if (!itemTemplate) + return false; + + if (_owner->GetPlayer()->CanUseItem(itemTemplate) != EQUIP_ERR_OK) + return false; + + if (itemTemplate->GetFlags2() & ITEM_FLAG2_CANNOT_TRANSMOG || itemTemplate->GetQuality() == ITEM_QUALITY_ARTIFACT) + return false; + + switch (itemTemplate->GetClass()) + { + case ITEM_CLASS_WEAPON: + { + if (!(_owner->GetPlayer()->GetWeaponProficiency() & (1 << itemTemplate->GetSubClass()))) + return false; + if (itemTemplate->GetSubClass() == ITEM_SUBCLASS_WEAPON_EXOTIC || + itemTemplate->GetSubClass() == ITEM_SUBCLASS_WEAPON_EXOTIC2 || + itemTemplate->GetSubClass() == ITEM_SUBCLASS_WEAPON_MISCELLANEOUS || + itemTemplate->GetSubClass() == ITEM_SUBCLASS_WEAPON_THROWN || + itemTemplate->GetSubClass() == ITEM_SUBCLASS_WEAPON_SPEAR || + itemTemplate->GetSubClass() == ITEM_SUBCLASS_WEAPON_FISHING_POLE) + return false; + break; + } + case ITEM_CLASS_ARMOR: + { + switch (itemTemplate->GetInventoryType()) + { + case INVTYPE_BODY: + case INVTYPE_SHIELD: + case INVTYPE_CLOAK: + case INVTYPE_TABARD: + case INVTYPE_HOLDABLE: + break; + case INVTYPE_HEAD: + case INVTYPE_SHOULDERS: + case INVTYPE_CHEST: + case INVTYPE_WAIST: + case INVTYPE_LEGS: + case INVTYPE_FEET: + case INVTYPE_WRISTS: + case INVTYPE_HANDS: + case INVTYPE_ROBE: + if (itemTemplate->GetSubClass() == ITEM_SUBCLASS_ARMOR_MISCELLANEOUS) + return false; + break; + default: + return false; + } + if (itemTemplate->GetInventoryType() != INVTYPE_CLOAK) + if (!(PlayerClassByArmorSubclass[itemTemplate->GetSubClass()] & _owner->GetPlayer()->getClassMask())) + return false; + break; + } + default: + return false; + } + + if (itemTemplate->GetQuality() < ITEM_QUALITY_UNCOMMON) + if (!(itemTemplate->GetFlags2() & ITEM_FLAG2_CAN_TRANSMOG) || !(itemTemplate->GetFlags3() & 0x200000)) + return false; + + if (itemModifiedAppearance->ID < _appearances.size() && _appearances.test(itemModifiedAppearance->ID)) + return false; + + return true; +} + +void CollectionMgr::AddItemAppearance(ItemModifiedAppearanceEntry const* itemModifiedAppearance) +{ + if (_appearances.size() <= itemModifiedAppearance->ID) + { + uint32 numBlocks = _appearances.num_blocks(); + _appearances.resize(itemModifiedAppearance->ID + 1); + numBlocks = _appearances.num_blocks() - numBlocks; + while (numBlocks--) + _owner->GetPlayer()->AddDynamicValue(PLAYER_DYNAMIC_FIELD_TRANSMOG, 0); + } + + _appearances.set(itemModifiedAppearance->ID); + uint32 blockIndex = itemModifiedAppearance->ID / 32; + uint32 bitIndex = itemModifiedAppearance->ID % 32; + uint32 currentMask = _owner->GetPlayer()->GetDynamicValue(PLAYER_DYNAMIC_FIELD_TRANSMOG, blockIndex); + _owner->GetPlayer()->SetDynamicValue(PLAYER_DYNAMIC_FIELD_TRANSMOG, blockIndex, currentMask | (1 << bitIndex)); + auto temporaryAppearance = _temporaryAppearances.find(itemModifiedAppearance->ID); + if (temporaryAppearance != _temporaryAppearances.end()) + { + _owner->GetPlayer()->RemoveDynamicValue(PLAYER_DYNAMIC_FIELD_CONDITIONAL_TRANSMOG, itemModifiedAppearance->ID); + _temporaryAppearances.erase(temporaryAppearance); + } +} + +void CollectionMgr::AddTemporaryAppearance(ObjectGuid const& itemGuid, ItemModifiedAppearanceEntry const* itemModifiedAppearance) +{ + std::unordered_set<ObjectGuid>& itemsWithAppearance = _temporaryAppearances[itemModifiedAppearance->ID]; + if (itemsWithAppearance.empty()) + _owner->GetPlayer()->AddDynamicValue(PLAYER_DYNAMIC_FIELD_CONDITIONAL_TRANSMOG, itemModifiedAppearance->ID); + + itemsWithAppearance.insert(itemGuid); +} + +void CollectionMgr::RemoveTemporaryAppearance(Item* item) +{ + ItemModifiedAppearanceEntry const* itemModifiedAppearance = item->GetItemModifiedAppearance(); + if (!itemModifiedAppearance) + return; + + auto itr = _temporaryAppearances.find(itemModifiedAppearance->ID); + if (itr == _temporaryAppearances.end()) + return; + + itr->second.erase(item->GetGUID()); + if (itr->second.empty()) + { + _owner->GetPlayer()->RemoveDynamicValue(PLAYER_DYNAMIC_FIELD_CONDITIONAL_TRANSMOG, itemModifiedAppearance->ID); + _temporaryAppearances.erase(itr); + } +} + +std::pair<bool, bool> CollectionMgr::HasItemAppearance(uint32 itemModifiedAppearanceId) const +{ + if (itemModifiedAppearanceId < _appearances.size() && _appearances.test(itemModifiedAppearanceId)) + return{ true, false }; + + if (_temporaryAppearances.find(itemModifiedAppearanceId) != _temporaryAppearances.end()) + return{ true,true }; + + return{ false,false }; +} + +std::unordered_set<ObjectGuid> CollectionMgr::GetItemsProvidingTemporaryAppearance(uint32 itemModifiedAppearanceId) const +{ + auto temporaryAppearance = _temporaryAppearances.find(itemModifiedAppearanceId); + if (temporaryAppearance != _temporaryAppearances.end()) + return temporaryAppearance->second; + + return{}; +} + +void CollectionMgr::SetAppearanceIsFavorite(uint32 itemModifiedAppearanceId, bool apply) +{ + auto itr = _favoriteAppearances.find(itemModifiedAppearanceId); + if (apply) + { + if (itr == _favoriteAppearances.end()) + _favoriteAppearances[itemModifiedAppearanceId] = FavoriteAppearanceState::New; + else if (itr->second == FavoriteAppearanceState::Removed) + itr->second = FavoriteAppearanceState::Unchanged; + else + return; + } + else if (itr != _favoriteAppearances.end()) + { + if (itr->second == FavoriteAppearanceState::New) + _favoriteAppearances.erase(itemModifiedAppearanceId); + else + itr->second = FavoriteAppearanceState::Removed; + } + else + return; + + WorldPackets::Transmogrification::TransmogCollectionUpdate transmogCollectionUpdate; + transmogCollectionUpdate.IsFullUpdate = false; + transmogCollectionUpdate.IsSetFavorite = apply; + transmogCollectionUpdate.FavoriteAppearances.push_back(itemModifiedAppearanceId); + + _owner->SendPacket(transmogCollectionUpdate.Write()); +} + +void CollectionMgr::SendFavoriteAppearances() const +{ + WorldPackets::Transmogrification::TransmogCollectionUpdate transmogCollectionUpdate; + transmogCollectionUpdate.IsFullUpdate = true; + transmogCollectionUpdate.FavoriteAppearances.reserve(_favoriteAppearances.size()); + for (auto itr = _favoriteAppearances.begin(); itr != _favoriteAppearances.end(); ++itr) + if (itr->second != FavoriteAppearanceState::Removed) + transmogCollectionUpdate.FavoriteAppearances.push_back(itr->first); + + _owner->SendPacket(transmogCollectionUpdate.Write()); +} diff --git a/src/server/game/Entities/Player/CollectionMgr.h b/src/server/game/Entities/Player/CollectionMgr.h index 1b995be3646..2cb11bdab8b 100644 --- a/src/server/game/Entities/Player/CollectionMgr.h +++ b/src/server/game/Entities/Player/CollectionMgr.h @@ -19,6 +19,7 @@ #define CollectionMgr_h__ #include "WorldSession.h" +#include <boost/dynamic_bitset.hpp> enum HeirloomPlayerFlags { @@ -48,7 +49,7 @@ typedef std::map<uint32, HeirloomData> HeirloomContainer; class TC_GAME_API CollectionMgr { public: - explicit CollectionMgr(WorldSession* owner) : _owner(owner) { } + explicit CollectionMgr(WorldSession* owner); WorldSession* GetOwner() const { return _owner; } @@ -64,6 +65,8 @@ public: ToyBoxContainer const& GetAccountToys() const { return _toys; } + void OnItemAdded(Item* item); + // Account-wide heirlooms void LoadHeirlooms(); void LoadAccountHeirlooms(PreparedQueryResult result); @@ -79,11 +82,39 @@ public: // Account-wide mounts + // Appearances + void LoadItemAppearances(); + void LoadAccountItemAppearances(PreparedQueryResult knownAppearances, PreparedQueryResult favoriteAppearances); + void SaveAccountItemAppearances(SQLTransaction& trans); + void AddItemAppearance(Item* item); + void AddItemAppearance(uint32 itemId); + void RemoveTemporaryAppearance(Item* item); + // returns pair<hasAppearance, isTemporary> + std::pair<bool, bool> HasItemAppearance(uint32 itemModifiedAppearanceId) const; + std::unordered_set<ObjectGuid> GetItemsProvidingTemporaryAppearance(uint32 itemModifiedAppearanceId) const; + + enum class FavoriteAppearanceState + { + New, + Removed, + Unchanged + }; + + void SetAppearanceIsFavorite(uint32 itemModifiedAppearanceId, bool apply); + void SendFavoriteAppearances() const; + private: + bool CanAddAppearance(ItemModifiedAppearanceEntry const* itemModifiedAppearance) const; + void AddItemAppearance(ItemModifiedAppearanceEntry const* itemModifiedAppearance); + void AddTemporaryAppearance(ObjectGuid const& itemGuid, ItemModifiedAppearanceEntry const* itemModifiedAppearance); + WorldSession* _owner; ToyBoxContainer _toys; HeirloomContainer _heirlooms; + boost::dynamic_bitset<uint32> _appearances; + std::unordered_map<uint32, std::unordered_set<ObjectGuid>> _temporaryAppearances; + std::unordered_map<uint32, FavoriteAppearanceState> _favoriteAppearances; }; #endif // CollectionMgr_h__ diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 388fb2b6bab..a59624515dd 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -88,6 +88,7 @@ #include "SpellMgr.h" #include "SpellPackets.h" #include "TalentPackets.h" +#include "TransmogrificationPackets.h" #include "ToyPackets.h" #include "Transport.h" #include "UpdateData.h" @@ -3963,6 +3964,10 @@ void Player::DeleteFromDB(ObjectGuid playerguid, uint32 accountId, bool updateRe stmt->setUInt64(0, guid); trans->Append(stmt); + stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_INSTANCE_TRANSMOG_BY_OWNER); + stmt->setUInt64(0, guid); + trans->Append(stmt); + stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_INSTANCE_BY_OWNER); stmt->setUInt64(0, guid); trans->Append(stmt); @@ -4345,7 +4350,7 @@ Corpse* Player::CreateCorpse() { if (m_items[i]) { - iDisplayID = m_items[i]->GetDisplayId(); + iDisplayID = m_items[i]->GetDisplayId(this); iIventoryType = m_items[i]->GetTemplate()->GetInventoryType(); _cfi = iDisplayID | (iIventoryType << 24); @@ -10930,9 +10935,6 @@ InventoryResult Player::CanUseItem(Item* pItem, bool not_loading) const return EQUIP_ERR_PROFICIENCY_NEEDED; } - if (pProto->GetRequiredReputationFaction() && uint32(GetReputationRank(pProto->GetRequiredReputationFaction())) < pProto->GetRequiredReputationRank()) - return EQUIP_ERR_CANT_EQUIP_REPUTATION; - return EQUIP_ERR_OK; } } @@ -10946,6 +10948,9 @@ InventoryResult Player::CanUseItem(ItemTemplate const* proto) const if (!proto) return EQUIP_ERR_ITEM_NOT_FOUND; + if (proto->GetFlags2() & ITEM_FLAG2_UNAVAILABLE_FOR_PLAYERS) + return EQUIP_ERR_CANT_EQUIP_EVER; + if ((proto->GetFlags2() & ITEM_FLAG2_HORDE_ONLY) && GetTeam() != HORDE) return EQUIP_ERR_CANT_EQUIP_EVER; @@ -10973,6 +10978,9 @@ InventoryResult Player::CanUseItem(ItemTemplate const* proto) const if (proto->GetHolidayID() && !IsHolidayActive(proto->GetHolidayID())) return EQUIP_ERR_CLIENT_LOCKED_OUT; + if (proto->GetRequiredReputationFaction() && uint32(GetReputationRank(proto->GetRequiredReputationFaction())) < proto->GetRequiredReputationRank()) + return EQUIP_ERR_CANT_EQUIP_REPUTATION; + // learning (recipes, mounts, pets, etc.) if (proto->Effects.size() >= 2) if (proto->Effects[0]->SpellID == 483 || proto->Effects[0]->SpellID == 55884) @@ -11015,7 +11023,7 @@ InventoryResult Player::CanRollForItemInLFG(ItemTemplate const* proto, WorldObje if (proto->GetClass() == ITEM_CLASS_WEAPON && GetSkillValue(proto->GetSkill()) == 0) return EQUIP_ERR_PROFICIENCY_NEEDED; - if (proto->GetClass() == ITEM_CLASS_ARMOR && proto->GetSubClass() > ITEM_SUBCLASS_ARMOR_MISCELLANEOUS && proto->GetSubClass() < ITEM_SUBCLASS_ARMOR_BUCKLER && proto->GetInventoryType() != INVTYPE_CLOAK) + if (proto->GetClass() == ITEM_CLASS_ARMOR && proto->GetSubClass() > ITEM_SUBCLASS_ARMOR_MISCELLANEOUS && proto->GetSubClass() < ITEM_SUBCLASS_ARMOR_COSMETIC && proto->GetInventoryType() != INVTYPE_CLOAK) { if (_class == CLASS_WARRIOR || _class == CLASS_PALADIN || _class == CLASS_DEATH_KNIGHT) { @@ -11051,7 +11059,7 @@ InventoryResult Player::CanRollForItemInLFG(ItemTemplate const* proto, WorldObje } // Return stored item (if stored to stack, it can diff. from pItem). And pItem ca be deleted in this case. -Item* Player::StoreNewItem(ItemPosCountVec const& pos, uint32 itemId, bool update, int32 randomPropertyId /*= 0*/, GuidSet const& allowedLooters /*= GuidSet()*/, std::vector<int32> const& bonusListIDs /*= std::vector<int32>()*/) +Item* Player::StoreNewItem(ItemPosCountVec const& pos, uint32 itemId, bool update, int32 randomPropertyId /*= 0*/, GuidSet const& allowedLooters /*= GuidSet()*/, std::vector<int32> const& bonusListIDs /*= std::vector<int32>()*/, bool addToCollection /*= true*/) { uint32 count = 0; for (ItemPosCountVec::const_iterator itr = pos.begin(); itr != pos.end(); ++itr) @@ -11064,9 +11072,6 @@ Item* Player::StoreNewItem(ItemPosCountVec const& pos, uint32 itemId, bool updat UpdateCriteria(CRITERIA_TYPE_RECEIVE_EPIC_ITEM, itemId, count); UpdateCriteria(CRITERIA_TYPE_OWN_ITEM, itemId, 1); - if (sDB2Manager.GetHeirloomByItemId(itemId)) - GetSession()->GetCollectionMgr()->AddHeirloom(itemId, 0); - if (randomPropertyId) item->SetItemRandomProperties(randomPropertyId); @@ -11093,6 +11098,9 @@ Item* Player::StoreNewItem(ItemPosCountVec const& pos, uint32 itemId, bool updat stmt->setString(1, ss.str()); CharacterDatabase.Execute(stmt); } + + if (addToCollection) + GetSession()->GetCollectionMgr()->OnItemAdded(item); } return item; } @@ -11376,9 +11384,9 @@ void Player::SetVisibleItemSlot(uint8 slot, Item* pItem) { if (pItem) { - SetUInt32Value(PLAYER_VISIBLE_ITEM + VISIBLE_ITEM_ENTRY_OFFSET + (slot * 2), pItem->GetVisibleEntry()); - SetUInt16Value(PLAYER_VISIBLE_ITEM + VISIBLE_ITEM_ENCHANTMENT_OFFSET + (slot * 2), 0, pItem->GetVisibleAppearanceModId()); - SetUInt16Value(PLAYER_VISIBLE_ITEM + VISIBLE_ITEM_ENCHANTMENT_OFFSET + (slot * 2), 1, pItem->GetVisibleItemVisual()); + SetUInt32Value(PLAYER_VISIBLE_ITEM + VISIBLE_ITEM_ENTRY_OFFSET + (slot * 2), pItem->GetVisibleEntry(this)); + SetUInt16Value(PLAYER_VISIBLE_ITEM + VISIBLE_ITEM_ENCHANTMENT_OFFSET + (slot * 2), 0, pItem->GetVisibleAppearanceModId(this)); + SetUInt16Value(PLAYER_VISIBLE_ITEM + VISIBLE_ITEM_ENCHANTMENT_OFFSET + (slot * 2), 1, pItem->GetVisibleItemVisual(this)); } else { @@ -11394,7 +11402,10 @@ void Player::VisualizeItem(uint8 slot, Item* pItem) // check also BIND_WHEN_PICKED_UP and BIND_QUEST_ITEM for .additem or .additemset case by GM (not binded at adding to inventory) if (pItem->GetTemplate()->GetBonding() == BIND_WHEN_EQUIPED || pItem->GetTemplate()->GetBonding() == BIND_WHEN_PICKED_UP || pItem->GetTemplate()->GetBonding() == BIND_QUEST_ITEM) + { pItem->SetBinding(true); + GetSession()->GetCollectionMgr()->AddItemAppearance(pItem); + } TC_LOG_DEBUG("entities.player.items", "Player::SetVisibleItemSlot: Player '%s' (%s), Slot: %u, Item: %u", GetName().c_str(), GetGUID().ToString().c_str(), slot, pItem->GetEntry()); @@ -11505,8 +11516,9 @@ void Player::MoveItemFromInventory(uint8 bag, uint8 slot, bool update) { ItemRemovedQuestCheck(it->GetEntry(), it->GetCount()); RemoveItem(bag, slot, update); - it->SetNotRefundable(this, false); + it->SetNotRefundable(this, false, nullptr, false); it->RemoveFromUpdateQueueOf(this); + GetSession()->GetCollectionMgr()->RemoveTemporaryAppearance(it); if (it->IsInWorld()) { it->RemoveFromWorld(); @@ -13118,7 +13130,7 @@ void Player::ApplyEnchantment(Item* item, EnchantmentSlot slot, bool apply, bool // visualize enchantment at player and equipped items if (slot == PERM_ENCHANTMENT_SLOT) - SetUInt16Value(PLAYER_VISIBLE_ITEM + VISIBLE_ITEM_ENCHANTMENT_OFFSET + (item->GetSlot() * 2), 1, item->GetVisibleItemVisual()); + SetUInt16Value(PLAYER_VISIBLE_ITEM + VISIBLE_ITEM_ENCHANTMENT_OFFSET + (item->GetSlot() * 2), 1, item->GetVisibleItemVisual(this)); if (apply_dur) { @@ -16874,6 +16886,7 @@ bool Player::LoadFromDB(ObjectGuid guid, SQLQueryHolder *holder) _LoadSpells(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_SPELLS)); GetSession()->GetCollectionMgr()->LoadToys(); GetSession()->GetCollectionMgr()->LoadHeirlooms(); + GetSession()->GetCollectionMgr()->LoadItemAppearances(); LearnSpecializationSpells(); @@ -17271,10 +17284,16 @@ void Player::LoadCorpse(PreparedQueryResult result) void Player::_LoadInventory(PreparedQueryResult result, uint32 timeDiff) { - // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 - // SELECT ii.guid, ii.itemEntry, ii.creatorGuid, ii.giftCreatorGuid, ii.count, ii.duration, ii.charges, ii.flags, ii.enchantments, ii.randomPropertyId, ii.durability, ii.playedTime, ii.text, ii.transmogrification, ii.upgradeId - // 15 16 17 18 19 20 21 22 23 24 25 - // ii.enchantIllusion, battlePetSpeciesId, battlePetBreedData, battlePetLevel, battlePetDisplayId, ii.bonusListIDs, ig.gemItemId1, ig.gemItemId2, ig.gemItemId3, bag, slot FROM character_inventory ci JOIN item_instance ii ON ci.item = ii.guid WHERE ci.guid = ? ORDER BY bag, slot + // 0 1 2 3 4 5 6 7 8 9 10 11 12 + // SELECT guid, itemEntry, creatorGuid, giftCreatorGuid, count, duration, charges, flags, enchantments, randomPropertyId, durability, playedTime, text, + // 13 14 15 16 17 18 + // upgradeId, battlePetSpeciesId, battlePetBreedData, battlePetLevel, battlePetDisplayId, bonusListIDs, + // 19 20 21 22 23 + // itemModifiedAppearanceAllSpecs, itemModifiedAppearanceSpec1, itemModifiedAppearanceSpec2, itemModifiedAppearanceSpec3, itemModifiedAppearanceSpec4, + // 24 25 26 27 28 + // spellItemEnchantmentAllSpecs, spellItemEnchantmentSpec1, spellItemEnchantmentSpec2, spellItemEnchantmentSpec3, spellItemEnchantmentSpec4, + // 29 30 31 32 33 + // gemItemId1, gemItemId2, gemItemId3, bag, slot FROM character_inventory ci JOIN item_instance ii ON ci.item = ii.guid WHERE ci.guid = ? ORDER BY bag, slot //NOTE: the "order by `bag`" is important because it makes sure //the bagMap is filled before items in the bags are loaded //NOTE2: the "order by `slot`" is needed because mainhand weapons are (wrongly?) @@ -17296,10 +17315,11 @@ void Player::_LoadInventory(PreparedQueryResult result, uint32 timeDiff) Field* fields = result->Fetch(); if (Item* item = _LoadItem(trans, zoneId, timeDiff, fields)) { - ObjectGuid bagGuid = fields[24].GetUInt64() ? ObjectGuid::Create<HighGuid::Item>(fields[24].GetUInt64()) : ObjectGuid::Empty; - uint8 slot = fields[25].GetUInt8(); + ObjectGuid bagGuid = fields[32].GetUInt64() ? ObjectGuid::Create<HighGuid::Item>(fields[32].GetUInt64()) : ObjectGuid::Empty; + uint8 slot = fields[33].GetUInt8(); GetSession()->GetCollectionMgr()->CheckHeirloomUpgrades(item); + GetSession()->GetCollectionMgr()->AddItemAppearance(item); InventoryResult err = EQUIP_ERR_OK; // Item is not in bag @@ -17625,7 +17645,7 @@ void Player::_LoadMailedItems(Mail* mail) Item* item = NewItemOrBag(proto); - ObjectGuid ownerGuid = fields[24].GetUInt64() ? ObjectGuid::Create<HighGuid::Player>(fields[24].GetUInt64()) : ObjectGuid::Empty; + ObjectGuid ownerGuid = fields[32].GetUInt64() ? ObjectGuid::Create<HighGuid::Player>(fields[32].GetUInt64()) : ObjectGuid::Empty; if (!item->LoadFromDB(itemGuid, ownerGuid, fields, itemEntry)) { TC_LOG_ERROR("entities.player", "Player::_LoadMailedItems: Item (GUID: " UI64FMTD ") in mail (%u) doesn't exist, deleted from mail.", itemGuid, mail->messageID); @@ -17861,6 +17881,18 @@ void Player::_LoadQuestStatusRewarded(PreparedQueryResult result) if (!quest->IsDailyOrWeekly() && !quest->IsMonthly() && !quest->IsSeasonal()) if (uint32 questBit = sDB2Manager.GetQuestUniqueBitFlag(quest_id)) SetQuestCompletedBit(questBit, true); + + for (uint32 i = 0; i < quest->GetRewChoiceItemsCount(); ++i) + GetSession()->GetCollectionMgr()->AddItemAppearance(quest->RewardChoiceItemId[i]); + + for (uint32 i = 0; i < quest->GetRewItemsCount(); ++i) + GetSession()->GetCollectionMgr()->AddItemAppearance(quest->RewardItemId[i]); + + if (std::vector<QuestPackageItemEntry const*> const* questPackageItems = sDB2Manager.GetQuestPackageItems(quest->GetQuestPackageID())) + for (QuestPackageItemEntry const* questPackageItem : *questPackageItems) + if (ItemTemplate const* rewardProto = sObjectMgr->GetItemTemplate(questPackageItem->ItemID)) + if (rewardProto->ItemSpecClassMask & getClassMask()) + GetSession()->GetCollectionMgr()->AddItemAppearance(questPackageItem->ItemID); } m_RewardedQuests.insert(quest_id); @@ -18649,8 +18681,8 @@ void Player::SaveToDB(bool create /*=false*/) { if (Item* item = GetItemByPos(INVENTORY_SLOT_BAG_0, i)) { - ss << item->GetTemplate()->GetInventoryType() << ' ' << item->GetDisplayId() << ' '; - if (SpellItemEnchantmentEntry const* enchant = sSpellItemEnchantmentStore.LookupEntry(item->GetVisibleEnchantmentId())) + ss << item->GetTemplate()->GetInventoryType() << ' ' << item->GetDisplayId(this) << ' '; + if (SpellItemEnchantmentEntry const* enchant = sSpellItemEnchantmentStore.LookupEntry(item->GetVisibleEnchantmentId(this))) ss << enchant->ItemVisual; else ss << '0'; @@ -18790,8 +18822,8 @@ void Player::SaveToDB(bool create /*=false*/) { if (Item* item = GetItemByPos(INVENTORY_SLOT_BAG_0, i)) { - ss << item->GetTemplate()->GetInventoryType() << ' ' << item->GetDisplayId() << ' '; - if (SpellItemEnchantmentEntry const* enchant = sSpellItemEnchantmentStore.LookupEntry(item->GetVisibleEnchantmentId())) + ss << item->GetTemplate()->GetInventoryType() << ' ' << item->GetDisplayId(this) << ' '; + if (SpellItemEnchantmentEntry const* enchant = sSpellItemEnchantmentStore.LookupEntry(item->GetVisibleEnchantmentId(this))) ss << enchant->ItemVisual; else ss << '0'; @@ -18860,6 +18892,7 @@ void Player::SaveToDB(bool create /*=false*/) GetSession()->GetCollectionMgr()->SaveAccountToys(trans); GetSession()->GetBattlePetMgr()->SaveToDB(trans); GetSession()->GetCollectionMgr()->SaveAccountHeirlooms(trans); + GetSession()->GetCollectionMgr()->SaveAccountItemAppearances(trans); stmt = LoginDatabase.GetPreparedStatement(LOGIN_DEL_BNET_LAST_PLAYER_CHARACTERS); stmt->setUInt32(0, GetSession()->GetAccountId()); @@ -20232,7 +20265,7 @@ void Player::VehicleSpellInitialize() petSpells.TimeLimit = vehicle->IsSummon() ? vehicle->ToTempSummon()->GetTimer() : 0; petSpells.ReactState = vehicle->GetReactState(); petSpells.CommandState = COMMAND_FOLLOW; - petSpells.Flag = 0x800; + petSpells.Flag = 0x8; for (uint32 i = 0; i < MAX_SPELL_CONTROL_BAR; ++i) petSpells.ActionButtons[i] = MAKE_UNIT_ACTION_BUTTON(0, i + 8); @@ -20993,7 +21026,7 @@ inline bool Player::_StoreOrEquipNewItem(uint32 vendorslot, uint32 item, uint8 c } Item* it = bStore ? - StoreNewItem(vDest, item, true) : + StoreNewItem(vDest, item, true, Item::GenerateItemRandomPropertyId(item), {}, {}, false) : EquipNewItem(uiDest, item, true); if (it) { @@ -21020,6 +21053,8 @@ inline bool Player::_StoreOrEquipNewItem(uint32 vendorslot, uint32 item, uint8 c it->SaveRefundDataToDB(); AddRefundReference(it->GetGUID()); } + + GetSession()->GetCollectionMgr()->OnItemAdded(it); } return true; } @@ -22293,6 +22328,8 @@ void Player::SendInitialPacketsBeforeAddToMap() heirloomUpdate.Heirlooms = &GetSession()->GetCollectionMgr()->GetAccountHeirlooms(); SendDirectMessage(heirloomUpdate.Write()); + GetSession()->GetCollectionMgr()->SendFavoriteAppearances(); + WorldPackets::Character::InitialSetup initialSetup; initialSetup.ServerExpansionLevel = sWorld->getIntConfig(CONFIG_EXPANSION); SendDirectMessage(initialSetup.Write()); @@ -25199,6 +25236,10 @@ void Player::ActivateTalentGroup(ChrSpecializationEntry const* spec) SetPower(pw, 0); UpdateItemSetAuras(false); + // update visible transmog + for (uint8 i = EQUIPMENT_SLOT_START; i < EQUIPMENT_SLOT_END; ++i) + if (Item* equippedItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i)) + SetVisibleItemSlot(i, equippedItem); } void Player::ResetTimeSync() @@ -25411,7 +25452,8 @@ void Player::RefundItem(Item* item) SQLTransaction trans = CharacterDatabase.BeginTransaction(); // Delete any references to the refund data - item->SetNotRefundable(this, true, &trans); + item->SetNotRefundable(this, true, &trans, false); + GetSession()->GetCollectionMgr()->RemoveTemporaryAppearance(item); // Destroy item DestroyItem(item->GetBagSlot(), item->GetSlot(), true); diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index 3969e026aee..4f2df580d54 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -1366,7 +1366,7 @@ class TC_GAME_API Player : public Unit, public GridObject<Player> bool HasItemTotemCategory(uint32 TotemCategory) const; InventoryResult CanUseItem(ItemTemplate const* pItem) const; InventoryResult CanRollForItemInLFG(ItemTemplate const* item, WorldObject const* lootedObject) const; - Item* StoreNewItem(ItemPosCountVec const& pos, uint32 itemId, bool update, int32 randomPropertyId = 0, GuidSet const& allowedLooters = GuidSet(), std::vector<int32> const& bonusListIDs = std::vector<int32>()); + Item* StoreNewItem(ItemPosCountVec const& pos, uint32 itemId, bool update, int32 randomPropertyId = 0, GuidSet const& allowedLooters = GuidSet(), std::vector<int32> const& bonusListIDs = std::vector<int32>(), bool addToCollection = true); Item* StoreItem(ItemPosCountVec const& pos, Item* pItem, bool update); Item* EquipNewItem(uint16 pos, uint32 item, bool update); Item* EquipItem(uint16 pos, Item* pItem, bool update); |
