aboutsummaryrefslogtreecommitdiff
path: root/src/server/game/Entities
diff options
context:
space:
mode:
authorShauren <shauren.trinity@gmail.com>2016-07-05 22:07:35 +0200
committerShauren <shauren.trinity@gmail.com>2016-07-05 22:07:35 +0200
commitf7883bd5251a759da1ca8be3ba6f6cead36723ec (patch)
treeaffce8f9ad4b343ba936ad6effa2127ab7d831b1 /src/server/game/Entities
parentbc81ae70bc350a3decead610f1b17452bd44eec4 (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.cpp236
-rw-r--r--src/server/game/Entities/Item/Item.h58
-rw-r--r--src/server/game/Entities/Item/ItemTemplate.h5
-rw-r--r--src/server/game/Entities/Object/Object.cpp19
-rw-r--r--src/server/game/Entities/Player/CollectionMgr.cpp364
-rw-r--r--src/server/game/Entities/Player/CollectionMgr.h33
-rw-r--r--src/server/game/Entities/Player/Player.cpp98
-rw-r--r--src/server/game/Entities/Player/Player.h2
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);