aboutsummaryrefslogtreecommitdiff
path: root/src/server/game
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
parentbc81ae70bc350a3decead610f1b17452bd44eec4 (diff)
Core/Transmog: Implemented transmog collection and updated transmog handling
Diffstat (limited to 'src/server/game')
-rw-r--r--src/server/game/DataStores/DB2Stores.cpp2
-rw-r--r--src/server/game/DataStores/DB2Stores.h1
-rw-r--r--src/server/game/DataStores/DB2Structure.h20
-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
-rw-r--r--src/server/game/Globals/ObjectMgr.cpp6
-rw-r--r--src/server/game/Guilds/Guild.cpp2
-rw-r--r--src/server/game/Guilds/GuildMgr.cpp16
-rw-r--r--src/server/game/Handlers/CharacterHandler.cpp39
-rw-r--r--src/server/game/Handlers/ItemHandler.cpp158
-rw-r--r--src/server/game/Handlers/SpellHandler.cpp4
-rw-r--r--src/server/game/Handlers/TransmogrificationHandler.cpp293
-rw-r--r--src/server/game/Handlers/VoidStorageHandler.cpp2
-rw-r--r--src/server/game/Server/Packets/AllPackets.h1
-rw-r--r--src/server/game/Server/Packets/ItemPackets.cpp30
-rw-r--r--src/server/game/Server/Packets/ItemPackets.h24
-rw-r--r--src/server/game/Server/Packets/TransmogrificationPackets.cpp54
-rw-r--r--src/server/game/Server/Packets/TransmogrificationPackets.h78
-rw-r--r--src/server/game/Server/Protocol/Opcodes.cpp6
-rw-r--r--src/server/game/Server/WorldSession.cpp11
-rw-r--r--src/server/game/Server/WorldSession.h10
-rw-r--r--src/server/game/Spells/Spell.cpp2
-rw-r--r--src/server/game/Spells/SpellEffects.cpp4
29 files changed, 1209 insertions, 369 deletions
diff --git a/src/server/game/DataStores/DB2Stores.cpp b/src/server/game/DataStores/DB2Stores.cpp
index f4a8d6423b8..2acf3205ddc 100644
--- a/src/server/game/DataStores/DB2Stores.cpp
+++ b/src/server/game/DataStores/DB2Stores.cpp
@@ -116,6 +116,7 @@ DB2Storage<ItemModifiedAppearanceEntry> sItemModifiedAppearanceStore("It
DB2Storage<ItemPriceBaseEntry> sItemPriceBaseStore("ItemPriceBase.db2", ItemPriceBaseMeta::Instance(), HOTFIX_SEL_ITEM_PRICE_BASE);
DB2Storage<ItemRandomPropertiesEntry> sItemRandomPropertiesStore("ItemRandomProperties.db2", ItemRandomPropertiesMeta::Instance(), HOTFIX_SEL_ITEM_RANDOM_PROPERTIES);
DB2Storage<ItemRandomSuffixEntry> sItemRandomSuffixStore("ItemRandomSuffix.db2", ItemRandomSuffixMeta::Instance(), HOTFIX_SEL_ITEM_RANDOM_SUFFIX);
+DB2Storage<ItemSearchNameEntry> sItemSearchNameStore("ItemSearchName.db2", ItemSearchNameMeta::Instance(), HOTFIX_SEL_ITEM_SEARCH_NAME);
DB2Storage<ItemSetEntry> sItemSetStore("ItemSet.db2", ItemSetMeta::Instance(), HOTFIX_SEL_ITEM_SET);
DB2Storage<ItemSetSpellEntry> sItemSetSpellStore("ItemSetSpell.db2", ItemSetSpellMeta::Instance(), HOTFIX_SEL_ITEM_SET_SPELL);
DB2SparseStorage<ItemSparseEntry> sItemSparseStore("Item-sparse.db2", ItemSparseMeta::Instance(), HOTFIX_SEL_ITEM_SPARSE);
@@ -374,6 +375,7 @@ void DB2Manager::LoadStores(std::string const& dataPath, uint32 defaultLocale)
LOAD_DB2(sItemPriceBaseStore);
LOAD_DB2(sItemRandomPropertiesStore);
LOAD_DB2(sItemRandomSuffixStore);
+ LOAD_DB2(sItemSearchNameStore);
LOAD_DB2(sItemSetStore);
LOAD_DB2(sItemSetSpellStore);
LOAD_DB2(sItemSparseStore);
diff --git a/src/server/game/DataStores/DB2Stores.h b/src/server/game/DataStores/DB2Stores.h
index b5a052428f4..fc8407eb96b 100644
--- a/src/server/game/DataStores/DB2Stores.h
+++ b/src/server/game/DataStores/DB2Stores.h
@@ -106,6 +106,7 @@ TC_GAME_API extern DB2Storage<ItemModifiedAppearanceEntry> sItemModifie
TC_GAME_API extern DB2Storage<ItemPriceBaseEntry> sItemPriceBaseStore;
TC_GAME_API extern DB2Storage<ItemRandomPropertiesEntry> sItemRandomPropertiesStore;
TC_GAME_API extern DB2Storage<ItemRandomSuffixEntry> sItemRandomSuffixStore;
+TC_GAME_API extern DB2Storage<ItemSearchNameEntry> sItemSearchNameStore;
TC_GAME_API extern DB2Storage<ItemSetEntry> sItemSetStore;
TC_GAME_API extern DB2Storage<ItemSetSpellEntry> sItemSetSpellStore;
TC_GAME_API extern DB2SparseStorage<ItemSparseEntry> sItemSparseStore;
diff --git a/src/server/game/DataStores/DB2Structure.h b/src/server/game/DataStores/DB2Structure.h
index d5ff75ce421..47e54d1f7af 100644
--- a/src/server/game/DataStores/DB2Structure.h
+++ b/src/server/game/DataStores/DB2Structure.h
@@ -1317,6 +1317,24 @@ struct ItemRandomSuffixEntry
uint16 AllocationPct[MAX_ITEM_RANDOM_PROPERTIES];
};
+struct ItemSearchNameEntry
+{
+ uint32 ID;
+ LocalizedString* Name;
+ uint32 Flags[3];
+ uint32 AllowableRace;
+ uint32 RequiredSpell;
+ uint16 RequiredReputationFaction;
+ uint16 RequiredSkill;
+ uint16 RequiredSkillRank;
+ uint16 ItemLevel;
+ uint8 Quality;
+ uint8 RequiredExpansion;
+ uint8 RequiredReputationRank;
+ uint8 RequiredLevel;
+ uint32 AllowableClass;
+};
+
#define MAX_ITEM_SET_ITEMS 17
struct ItemSetEntry
@@ -1717,7 +1735,7 @@ struct PhaseXPhaseGroupEntry
struct PlayerConditionEntry
{
- uint32 ID; // 1, 1, 1
+ uint32 ID;
uint32 RaceMask;
uint32 SkillLogic;
uint32 ReputationLogic;
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);
diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp
index 1466deda61d..0ea0f832405 100644
--- a/src/server/game/Globals/ObjectMgr.cpp
+++ b/src/server/game/Globals/ObjectMgr.cpp
@@ -2666,6 +2666,7 @@ void ObjectMgr::LoadItemTemplates()
itemTemplate.MaxMoneyLoot = 0;
itemTemplate.FlagsCu = 0;
itemTemplate.SpellPPMRate = 0.0f;
+ itemTemplate.ItemSpecClassMask = 0;
if (std::vector<ItemSpecOverrideEntry const*> const* itemSpecOverrides = sDB2Manager.GetItemSpecOverrides(sparse->ID))
{
@@ -2699,8 +2700,13 @@ void ObjectMgr::LoadItemTemplates()
continue;
if (ChrSpecializationEntry const* specialization = sChrSpecializationStore.LookupEntry(itemSpec->SpecID))
+ {
if ((1 << (specialization->ClassID - 1)) & sparse->AllowableClass)
+ {
+ itemTemplate.ItemSpecClassMask |= 1 << (specialization->ClassID - 1);
itemTemplate.Specializations[itemSpec->MaxLevel > 40].insert(itemSpec->SpecID);
+ }
+ }
}
}
}
diff --git a/src/server/game/Guilds/Guild.cpp b/src/server/game/Guilds/Guild.cpp
index b09b75be882..2bd97bd7728 100644
--- a/src/server/game/Guilds/Guild.cpp
+++ b/src/server/game/Guilds/Guild.cpp
@@ -2394,7 +2394,7 @@ void Guild::LoadBankTabFromDB(Field* fields)
bool Guild::LoadBankItemFromDB(Field* fields)
{
- uint8 tabId = fields[25].GetUInt8();
+ uint8 tabId = fields[33].GetUInt8();
if (tabId >= _GetPurchasedTabsSize())
{
TC_LOG_ERROR("guild", "Invalid tab for item (GUID: %u, id: #%u) in guild bank, skipped.",
diff --git a/src/server/game/Guilds/GuildMgr.cpp b/src/server/game/Guilds/GuildMgr.cpp
index 20cbf22d40e..48216a2809a 100644
--- a/src/server/game/Guilds/GuildMgr.cpp
+++ b/src/server/game/Guilds/GuildMgr.cpp
@@ -401,10 +401,16 @@ void GuildMgr::LoadGuilds()
// Delete orphan guild bank items
CharacterDatabase.DirectExecute("DELETE gbi FROM guild_bank_item gbi LEFT JOIN guild g ON gbi.guildId = g.guildId WHERE g.guildId IS NULL");
- // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
- // SELECT guid, itemEntry, creatorGuid, giftCreatorGuid, count, duration, charges, flags, enchantments, randomPropertyId, durability, playedTime, text, transmogrification, upgradeId, enchantIllusion,
- // 17 18 19 20 21 22 23 24 25 26
- // battlePetSpeciesId, battlePetBreedData, battlePetLevel, battlePetDisplayId, ig.gemItemId1, ig.gemItemId2, ig.gemItemId3, guildid, TabId, SlotId FROM guild_bank_item gbi INNER JOIN item_instance ii ON gbi.item_guid = ii.guid
+ // 0 1 2 3 4 5 6 7 8 9 10 11 12 13
+ // SELECT guid, itemEntry, creatorGuid, giftCreatorGuid, count, duration, charges, flags, enchantments, randomPropertyId, durability, playedTime, text, upgradeId,
+ // 14 15 16 17 18
+ // 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, guildid, TabId, SlotId FROM guild_bank_item gbi INNER JOIN item_instance ii ON gbi.item_guid = ii.guid
PreparedQueryResult result = CharacterDatabase.Query(CharacterDatabase.GetPreparedStatement(CHAR_SEL_GUILD_BANK_ITEMS));
if (!result)
@@ -417,7 +423,7 @@ void GuildMgr::LoadGuilds()
do
{
Field* fields = result->Fetch();
- uint64 guildId = fields[24].GetUInt64();
+ uint64 guildId = fields[32].GetUInt64();
if (Guild* guild = GetGuildById(guildId))
guild->LoadBankItemFromDB(fields);
diff --git a/src/server/game/Handlers/CharacterHandler.cpp b/src/server/game/Handlers/CharacterHandler.cpp
index 63a37b42efd..c3bea2abb34 100644
--- a/src/server/game/Handlers/CharacterHandler.cpp
+++ b/src/server/game/Handlers/CharacterHandler.cpp
@@ -1653,10 +1653,16 @@ void WorldSession::HandleEquipmentSetSave(WorldPackets::EquipmentSet::SaveEquipm
else
{
saveEquipmentSet.Set.Pieces[i].Clear();
- if (saveEquipmentSet.Set.Appearances[i] && !sItemModifiedAppearanceStore.LookupEntry(saveEquipmentSet.Set.Appearances[i]))
- return;
+ if (saveEquipmentSet.Set.Appearances[i])
+ {
+ if (!sItemModifiedAppearanceStore.LookupEntry(saveEquipmentSet.Set.Appearances[i]))
+ return;
- // TODO: validata whether appearance is known
+ bool hasAppearance, isTemporary;
+ std::tie(hasAppearance, isTemporary) = GetCollectionMgr()->HasItemAppearance(saveEquipmentSet.Set.Appearances[i]);
+ if (!hasAppearance)
+ return;
+ }
}
}
else
@@ -1672,6 +1678,33 @@ void WorldSession::HandleEquipmentSetSave(WorldPackets::EquipmentSet::SaveEquipm
saveEquipmentSet.Set.Enchants[0] = 0;
saveEquipmentSet.Set.Enchants[1] = 0;
}
+ else
+ {
+ auto validateIllusion = [this](uint32 enchantId) -> bool
+ {
+ SpellItemEnchantmentEntry const* illusion = sSpellItemEnchantmentStore.LookupEntry(enchantId);
+ if (!illusion)
+ return false;
+
+ if (!illusion->ItemVisual || !(illusion->Flags & ENCHANTMENT_COLLECTABLE))
+ return false;
+
+ if (PlayerConditionEntry const* condition = sPlayerConditionStore.LookupEntry(illusion->PlayerConditionID))
+ if (!sConditionMgr->IsPlayerMeetingCondition(_player, condition))
+ return false;
+
+ if (illusion->ScalingClassRestricted > 0 && uint8(illusion->ScalingClassRestricted) != _player->getClass())
+ return false;
+
+ return true;
+ };
+
+ if (saveEquipmentSet.Set.Enchants[0] && !validateIllusion(saveEquipmentSet.Set.Enchants[0]))
+ return;
+
+ if (saveEquipmentSet.Set.Enchants[1] && !validateIllusion(saveEquipmentSet.Set.Enchants[1]))
+ return;
+ }
_player->SetEquipmentSet(saveEquipmentSet.Set);
}
diff --git a/src/server/game/Handlers/ItemHandler.cpp b/src/server/game/Handlers/ItemHandler.cpp
index b5c8e6bdc67..938523afb8a 100644
--- a/src/server/game/Handlers/ItemHandler.cpp
+++ b/src/server/game/Handlers/ItemHandler.cpp
@@ -1096,164 +1096,6 @@ void WorldSession::HandleItemRefund(WorldPackets::Item::ItemPurchaseRefund& pack
GetPlayer()->RefundItem(item);
}
-void WorldSession::HandleTransmogrifyItems(WorldPackets::Item::TransmogrifyItems& transmogrifyItems)
-{
- Player* player = GetPlayer();
- // Validate
- if (!player->GetNPCIfCanInteractWith(transmogrifyItems.Npc, UNIT_NPC_FLAG_TRANSMOGRIFIER))
- {
- TC_LOG_DEBUG("network", "WORLD: HandleTransmogrifyItems - %s not found or player can't interact with it.", transmogrifyItems.Npc.ToString().c_str());
- return;
- }
-
- int64 cost = 0;
- std::unordered_map<Item*, Item*> transmogItems;
- std::unordered_map<Item*, std::pair<VoidStorageItem*, BonusData>> transmogVoidItems;
- std::vector<Item*> resetAppearanceItems;
-
- for (WorldPackets::Item::TransmogrifyItem const& transmogItem : transmogrifyItems.Items)
- {
- // slot of the transmogrified item
- if (transmogItem.Slot >= EQUIPMENT_SLOT_END)
- {
- TC_LOG_DEBUG("network", "WORLD: HandleTransmogrifyItems - Player (%s, name: %s) tried to transmogrify wrong slot (%u) when transmogrifying items.", player->GetGUID().ToString().c_str(), player->GetName().c_str(), transmogItem.Slot);
- return;
- }
-
- // transmogrified item
- Item* itemTransmogrified = player->GetItemByPos(INVENTORY_SLOT_BAG_0, transmogItem.Slot);
- if (!itemTransmogrified)
- {
- TC_LOG_DEBUG("network", "WORLD: HandleTransmogrifyItems - Player (%s, name: %s) tried to transmogrify an invalid item in a valid slot (slot: %u).", player->GetGUID().ToString().c_str(), player->GetName().c_str(), transmogItem.Slot);
- return;
- }
- if (player->CanUseItem(itemTransmogrified->GetTemplate()) != EQUIP_ERR_OK)
- {
- TC_LOG_DEBUG("network", "WORLD: HandleTransmogrifyItems - Player (%s, name: %s) tried to transmogrify an unequippable item in a valid slot (slot: %u).", player->GetGUID().ToString().c_str(), player->GetName().c_str(), transmogItem.Slot);
- return;
- }
-
- WorldPackets::Item::ItemInstance itemInstance;
- BonusData const* bonus = nullptr;
- if (transmogItem.SrcItemGUID)
- {
- // guid of the transmogrifier item
- Item* itemTransmogrifier = player->GetItemByGuid(*transmogItem.SrcItemGUID);
- if (!itemTransmogrifier)
- {
- TC_LOG_DEBUG("network", "WORLD: HandleTransmogrifyItems - Player (%s, name: %s) tried to transmogrify with an invalid item (%s).", player->GetGUID().ToString().c_str(), player->GetName().c_str(), transmogItem.SrcItemGUID->ToString().c_str());
- return;
- }
- if (player->CanUseItem(itemTransmogrifier->GetTemplate()) != EQUIP_ERR_OK)
- {
- TC_LOG_DEBUG("network", "WORLD: HandleTransmogrifyItems - Player (%s, name: %s) tried to transmogrify with an unequippable item (%s).", player->GetGUID().ToString().c_str(), player->GetName().c_str(), transmogItem.SrcItemGUID->ToString().c_str());
- return;
- }
-
- itemInstance.Initialize(itemTransmogrifier);
- bonus = itemTransmogrifier->GetBonus();
- transmogItems[itemTransmogrified] = itemTransmogrifier;
- }
- else if (transmogItem.SrcVoidItemGUID)
- {
- // guid of the transmogrifier item
- uint8 slot;
- VoidStorageItem* itemTransmogrifier = player->GetVoidStorageItem(transmogItem.SrcVoidItemGUID->GetCounter(), slot);
- if (!itemTransmogrifier)
- {
- TC_LOG_DEBUG("network", "WORLD: HandleTransmogrifyItems - Player (%s, name: %s) tried to transmogrify with an invalid void storage item (%s).", player->GetGUID().ToString().c_str(), player->GetName().c_str(), transmogItem.SrcVoidItemGUID->ToString().c_str());
- return;
- }
- ItemTemplate const * transmogrifierTemplate = sObjectMgr->GetItemTemplate(itemTransmogrifier->ItemEntry);
- if (player->CanUseItem(transmogrifierTemplate) != EQUIP_ERR_OK)
- {
- TC_LOG_DEBUG("network", "WORLD: HandleTransmogrifyItems - Player (%s, name: %s) tried to transmogrify with an unequippable void storage item (%s).", player->GetGUID().ToString().c_str(), player->GetName().c_str(), transmogItem.SrcVoidItemGUID->ToString().c_str());
- return;
- }
-
- itemInstance.Initialize(itemTransmogrifier);
- std::pair<VoidStorageItem*, BonusData>& transmogData = transmogVoidItems[itemTransmogrified];
- transmogData.first = itemTransmogrifier;
- transmogData.second.Initialize(itemInstance);
- bonus = &transmogData.second;
- }
- else
- {
- resetAppearanceItems.push_back(itemTransmogrified);
- continue;
- }
-
- // entry of transmogrifier and from packet
- if (itemInstance != transmogItem.Item)
- {
- TC_LOG_DEBUG("network", "WORLD: HandleTransmogrifyItems - Player (%s, name: %s) tried to transmogrify with an invalid item instance data for %s.", player->GetGUID().ToString().c_str(), player->GetName().c_str(), transmogItem.SrcItemGUID->ToString().c_str());
- return;
- }
-
- // validity of the transmogrification items
- if (!Item::CanTransmogrifyItemWithItem(itemTransmogrified, transmogItem.Item, bonus))
- {
- TC_LOG_DEBUG("network", "WORLD: HandleTransmogrifyItems - Player (%s, name: %s) failed CanTransmogrifyItemWithItem (%u with %u).", player->GetGUID().ToString().c_str(), player->GetName().c_str(), itemTransmogrified->GetEntry(), transmogItem.Item.ItemID);
- return;
- }
-
- // add cost
- cost += itemTransmogrified->GetSpecialPrice();
- }
-
- if (cost) // 0 cost if reverting look
- {
- if (!player->HasEnoughMoney(cost))
- return;
- player->ModifyMoney(-cost);
- }
-
- // Everything is fine, proceed
- for (auto& transmogPair : transmogItems)
- {
- Item* transmogrified = transmogPair.first;
- Item* transmogrifier = transmogPair.second;
-
- transmogrified->SetModifier(ITEM_MODIFIER_TRANSMOG_ITEM_ID, transmogrifier->GetEntry());
- transmogrified->SetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_MOD, transmogrifier->GetAppearanceModId());
- player->SetVisibleItemSlot(transmogrified->GetSlot(), transmogrified);
-
- transmogrified->SetNotRefundable(player);
- transmogrified->ClearSoulboundTradeable(player);
-
- transmogrifier->SetNotRefundable(player);
- transmogrifier->ClearSoulboundTradeable(player);
-
- if (transmogrifier->GetTemplate()->GetBonding() == BIND_WHEN_EQUIPED || transmogrifier->GetTemplate()->GetBonding() == BIND_WHEN_USE)
- transmogrifier->SetBinding(true);
-
- transmogrified->SetState(ITEM_CHANGED, player);
- }
-
- for (auto& transmogVoirPair : transmogVoidItems)
- {
- Item* transmogrified = transmogVoirPair.first;
- VoidStorageItem* transmogrifier = transmogVoirPair.second.first;
- BonusData& bonus = transmogVoirPair.second.second;
-
- transmogrified->SetModifier(ITEM_MODIFIER_TRANSMOG_ITEM_ID, transmogrifier->ItemEntry);
- transmogrified->SetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_MOD, bonus.AppearanceModID);
- player->SetVisibleItemSlot(transmogrified->GetSlot(), transmogrified);
-
- transmogrified->SetNotRefundable(player);
- transmogrified->ClearSoulboundTradeable(player);
- transmogrified->SetState(ITEM_CHANGED, player);
- }
-
- for (Item* item : resetAppearanceItems)
- {
- item->SetModifier(ITEM_MODIFIER_TRANSMOG_ITEM_ID, 0);
- item->SetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_MOD, 0);
- item->SetState(ITEM_CHANGED, player);
- player->SetVisibleItemSlot(item->GetSlot(), item);
- }
-}
-
bool WorldSession::CanUseBank(ObjectGuid bankerGUID) const
{
// bankerGUID parameter is optional, set to 0 by default.
diff --git a/src/server/game/Handlers/SpellHandler.cpp b/src/server/game/Handlers/SpellHandler.cpp
index 8ce4a88ef5a..4bfada8aab3 100644
--- a/src/server/game/Handlers/SpellHandler.cpp
+++ b/src/server/game/Handlers/SpellHandler.cpp
@@ -17,6 +17,7 @@
*/
#include "WorldSession.h"
+#include "CollectionMgr.h"
#include "Common.h"
#include "Config.h"
#include "GameObjectAI.h"
@@ -111,6 +112,7 @@ void WorldSession::HandleUseItemOpcode(WorldPackets::Spells::UseItem& packet)
{
item->SetState(ITEM_CHANGED, user);
item->SetBinding(true);
+ GetCollectionMgr()->AddItemAppearance(item);
}
}
@@ -544,7 +546,7 @@ void WorldSession::HandleMirrorImageDataRequest(WorldPackets::Spells::GetMirrorI
(slot == EQUIPMENT_SLOT_BACK && player->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_HIDE_CLOAK)))
itemDisplayId = 0;
else if (Item const* item = player->GetItemByPos(INVENTORY_SLOT_BAG_0, slot))
- itemDisplayId = item->GetDisplayId();
+ itemDisplayId = item->GetDisplayId(player);
else
itemDisplayId = 0;
diff --git a/src/server/game/Handlers/TransmogrificationHandler.cpp b/src/server/game/Handlers/TransmogrificationHandler.cpp
new file mode 100644
index 00000000000..0d8406beb45
--- /dev/null
+++ b/src/server/game/Handlers/TransmogrificationHandler.cpp
@@ -0,0 +1,293 @@
+/*
+ * Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "WorldSession.h"
+#include "CollectionMgr.h"
+#include "ObjectMgr.h"
+#include "Player.h"
+#include "TransmogrificationPackets.h"
+
+void WorldSession::HandleTransmogrifyItems(WorldPackets::Transmogrification::TransmogrifyItems& transmogrifyItems)
+{
+ Player* player = GetPlayer();
+ // Validate
+ if (!player->GetNPCIfCanInteractWith(transmogrifyItems.Npc, UNIT_NPC_FLAG_TRANSMOGRIFIER))
+ {
+ TC_LOG_DEBUG("network", "WORLD: HandleTransmogrifyItems - %s not found or player can't interact with it.", transmogrifyItems.Npc.ToString().c_str());
+ return;
+ }
+
+ int64 cost = 0;
+ std::unordered_map<Item*, uint32> transmogItems;
+ std::unordered_map<Item*, uint32> illusionItems;
+
+ std::vector<Item*> resetAppearanceItems;
+ std::vector<Item*> resetIllusionItems;
+ std::vector<uint32> bindAppearances;
+
+ for (WorldPackets::Transmogrification::TransmogrifyItem const& transmogItem : transmogrifyItems.Items)
+ {
+ // slot of the transmogrified item
+ if (transmogItem.Slot >= EQUIPMENT_SLOT_END)
+ {
+ TC_LOG_DEBUG("network", "WORLD: HandleTransmogrifyItems - Player (%s, name: %s) tried to transmogrify wrong slot (%u) when transmogrifying items.", player->GetGUID().ToString().c_str(), player->GetName().c_str(), transmogItem.Slot);
+ return;
+ }
+
+ // transmogrified item
+ Item* itemTransmogrified = player->GetItemByPos(INVENTORY_SLOT_BAG_0, transmogItem.Slot);
+ if (!itemTransmogrified)
+ {
+ TC_LOG_DEBUG("network", "WORLD: HandleTransmogrifyItems - Player (%s, name: %s) tried to transmogrify an invalid item in a valid slot (slot: %u).", player->GetGUID().ToString().c_str(), player->GetName().c_str(), transmogItem.Slot);
+ return;
+ }
+
+ if (transmogItem.ItemModifiedAppearanceID)
+ {
+ ItemModifiedAppearanceEntry const* itemModifiedAppearance = sItemModifiedAppearanceStore.LookupEntry(transmogItem.ItemModifiedAppearanceID);
+ if (!itemModifiedAppearance)
+ {
+ TC_LOG_DEBUG("network", "WORLD: HandleTransmogrifyItems - %s, Name: %s tried to transmogrify using invalid appearance (%d).", player->GetGUID().ToString().c_str(), player->GetName().c_str(), transmogItem.ItemModifiedAppearanceID);
+ return;
+ }
+
+ bool hasAppearance, isTemporary;
+ std::tie(hasAppearance, isTemporary) = GetCollectionMgr()->HasItemAppearance(transmogItem.ItemModifiedAppearanceID);
+ if (!hasAppearance)
+ {
+ TC_LOG_DEBUG("network", "WORLD: HandleTransmogrifyItems - %s, Name: %s tried to transmogrify using appearance he has not collected (%d).", player->GetGUID().ToString().c_str(), player->GetName().c_str(), transmogItem.ItemModifiedAppearanceID);
+ return;
+ }
+ ItemTemplate const* itemTemplate = sObjectMgr->GetItemTemplate(itemModifiedAppearance->ItemID);
+ if (player->CanUseItem(itemTemplate) != EQUIP_ERR_OK)
+ {
+ TC_LOG_DEBUG("network", "WORLD: HandleTransmogrifyItems - %s, Name: %s tried to transmogrify using appearance he can never use (%d).", player->GetGUID().ToString().c_str(), player->GetName().c_str(), transmogItem.ItemModifiedAppearanceID);
+ return;
+ }
+
+ // validity of the transmogrification items
+ if (!Item::CanTransmogrifyItemWithItem(itemTransmogrified, itemModifiedAppearance))
+ {
+ TC_LOG_DEBUG("network", "WORLD: HandleTransmogrifyItems - %s, Name: %s failed CanTransmogrifyItemWithItem (%u with appearance %d).", player->GetGUID().ToString().c_str(), player->GetName().c_str(), itemTransmogrified->GetEntry(), transmogItem.ItemModifiedAppearanceID);
+ return;
+ }
+
+ transmogItems[itemTransmogrified] = transmogItem.ItemModifiedAppearanceID;
+ if (isTemporary)
+ bindAppearances.push_back(transmogItem.ItemModifiedAppearanceID);
+
+ // add cost
+ cost += itemTransmogrified->GetSpecialPrice();
+ }
+ else
+ resetAppearanceItems.push_back(itemTransmogrified);
+
+ if (transmogItem.SpellItemEnchantmentID)
+ {
+ if (transmogItem.Slot != EQUIPMENT_SLOT_MAINHAND && transmogItem.Slot != EQUIPMENT_SLOT_OFFHAND)
+ {
+ TC_LOG_DEBUG("network", "WORLD: HandleTransmogrifyItems - %s, Name: %s tried to transmogrify illusion into non-weapon slot (%u).", player->GetGUID().ToString().c_str(), player->GetName().c_str(), transmogItem.Slot);
+ return;
+ }
+
+ SpellItemEnchantmentEntry const* illusion = sSpellItemEnchantmentStore.LookupEntry(transmogItem.SpellItemEnchantmentID);
+ if (!illusion)
+ {
+ TC_LOG_DEBUG("network", "WORLD: HandleTransmogrifyItems - %s, Name: %s tried to transmogrify illusion using invalid enchant (%d).", player->GetGUID().ToString().c_str(), player->GetName().c_str(), transmogItem.SpellItemEnchantmentID);
+ return;
+ }
+
+ if (!illusion->ItemVisual || !(illusion->Flags & ENCHANTMENT_COLLECTABLE))
+ {
+ TC_LOG_DEBUG("network", "WORLD: HandleTransmogrifyItems - %s, Name: %s tried to transmogrify illusion using not allowed enchant (%d).", player->GetGUID().ToString().c_str(), player->GetName().c_str(), transmogItem.SpellItemEnchantmentID);
+ return;
+ }
+
+ if (PlayerConditionEntry const* condition = sPlayerConditionStore.LookupEntry(illusion->PlayerConditionID))
+ {
+ if (!sConditionMgr->IsPlayerMeetingCondition(player, condition))
+ {
+ TC_LOG_DEBUG("network", "WORLD: HandleTransmogrifyItems - %s, Name: %s tried to transmogrify illusion using not collected enchant (%d).", player->GetGUID().ToString().c_str(), player->GetName().c_str(), transmogItem.SpellItemEnchantmentID);
+ return;
+ }
+ }
+
+ if (illusion->ScalingClassRestricted > 0 && uint8(illusion->ScalingClassRestricted) != player->getClass())
+ {
+ TC_LOG_DEBUG("network", "WORLD: HandleTransmogrifyItems - %s, Name: %s tried to transmogrify illusion using not allowed class enchant (%d).", player->GetGUID().ToString().c_str(), player->GetName().c_str(), transmogItem.SpellItemEnchantmentID);
+ return;
+ }
+
+ illusionItems[itemTransmogrified] = transmogItem.SpellItemEnchantmentID;
+ cost += illusion->TransmogCost;
+ }
+ else
+ resetIllusionItems.push_back(itemTransmogrified);
+ }
+
+ if (cost) // 0 cost if reverting look
+ {
+ if (!player->HasEnoughMoney(cost))
+ return;
+ player->ModifyMoney(-cost);
+ }
+
+ // Everything is fine, proceed
+ for (auto& transmogPair : transmogItems)
+ {
+ Item* transmogrified = transmogPair.first;
+
+ if (!transmogrifyItems.CurrentSpecOnly)
+ {
+ transmogrified->SetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_ALL_SPECS, transmogPair.second);
+ transmogrified->SetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_1, 0);
+ transmogrified->SetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_2, 0);
+ transmogrified->SetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_3, 0);
+ transmogrified->SetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_4, 0);
+ }
+ else
+ {
+ if (!transmogrified->GetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_1))
+ transmogrified->SetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_1, transmogrified->GetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_ALL_SPECS));
+ if (!transmogrified->GetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_2))
+ transmogrified->SetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_2, transmogrified->GetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_ALL_SPECS));
+ if (!transmogrified->GetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_3))
+ transmogrified->SetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_3, transmogrified->GetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_ALL_SPECS));
+ if (!transmogrified->GetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_4))
+ transmogrified->SetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_4, transmogrified->GetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_ALL_SPECS));
+ transmogrified->SetModifier(AppearanceModifierSlotBySpec[player->GetActiveTalentGroup()], transmogPair.second);
+ }
+
+ player->SetVisibleItemSlot(transmogrified->GetSlot(), transmogrified);
+
+ transmogrified->SetNotRefundable(player);
+ transmogrified->ClearSoulboundTradeable(player);
+ transmogrified->SetState(ITEM_CHANGED, player);
+ }
+
+ for (auto& illusionPair : illusionItems)
+ {
+ Item* transmogrified = illusionPair.first;
+
+ if (!transmogrifyItems.CurrentSpecOnly)
+ {
+ transmogrified->SetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_ALL_SPECS, illusionPair.second);
+ transmogrified->SetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_1, 0);
+ transmogrified->SetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_2, 0);
+ transmogrified->SetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_3, 0);
+ transmogrified->SetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_4, 0);
+ }
+ else
+ {
+ if (!transmogrified->GetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_1))
+ transmogrified->SetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_1, transmogrified->GetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_ALL_SPECS));
+ if (!transmogrified->GetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_2))
+ transmogrified->SetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_2, transmogrified->GetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_ALL_SPECS));
+ if (!transmogrified->GetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_3))
+ transmogrified->SetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_3, transmogrified->GetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_ALL_SPECS));
+ if (!transmogrified->GetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_4))
+ transmogrified->SetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_4, transmogrified->GetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_ALL_SPECS));
+ transmogrified->SetModifier(IllusionModifierSlotBySpec[player->GetActiveTalentGroup()], illusionPair.second);
+ }
+
+ player->SetVisibleItemSlot(transmogrified->GetSlot(), transmogrified);
+
+ transmogrified->SetNotRefundable(player);
+ transmogrified->ClearSoulboundTradeable(player);
+ transmogrified->SetState(ITEM_CHANGED, player);
+ }
+
+ for (Item* item : resetAppearanceItems)
+ {
+ if (!transmogrifyItems.CurrentSpecOnly)
+ {
+ item->SetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_ALL_SPECS, 0);
+ item->SetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_1, 0);
+ item->SetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_2, 0);
+ item->SetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_3, 0);
+ item->SetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_4, 0);
+ }
+ else
+ {
+ if (!item->GetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_1))
+ item->SetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_1, item->GetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_ALL_SPECS));
+ if (!item->GetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_2))
+ item->SetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_2, item->GetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_ALL_SPECS));
+ if (!item->GetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_3))
+ item->SetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_3, item->GetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_ALL_SPECS));
+ if (!item->GetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_4))
+ item->SetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_SPEC_4, item->GetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_ALL_SPECS));
+ item->SetModifier(AppearanceModifierSlotBySpec[player->GetActiveTalentGroup()], 0);
+ item->SetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_ALL_SPECS, 0);
+ }
+
+ item->SetState(ITEM_CHANGED, player);
+ player->SetVisibleItemSlot(item->GetSlot(), item);
+ }
+
+ for (Item* item : resetIllusionItems)
+ {
+ if (!transmogrifyItems.CurrentSpecOnly)
+ {
+ item->SetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_ALL_SPECS, 0);
+ item->SetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_1, 0);
+ item->SetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_2, 0);
+ item->SetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_3, 0);
+ item->SetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_4, 0);
+ }
+ else
+ {
+ if (!item->GetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_1))
+ item->SetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_1, item->GetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_ALL_SPECS));
+ if (!item->GetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_2))
+ item->SetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_2, item->GetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_ALL_SPECS));
+ if (!item->GetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_3))
+ item->SetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_3, item->GetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_ALL_SPECS));
+ if (!item->GetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_4))
+ item->SetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_SPEC_4, item->GetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_ALL_SPECS));
+ item->SetModifier(IllusionModifierSlotBySpec[player->GetActiveTalentGroup()], 0);
+ item->SetModifier(ITEM_MODIFIER_TRANSMOG_APPEARANCE_ALL_SPECS, 0);
+ }
+
+ item->SetState(ITEM_CHANGED, player);
+ player->SetVisibleItemSlot(item->GetSlot(), item);
+ }
+
+ for (uint32 itemModifedAppearanceId : bindAppearances)
+ {
+ std::unordered_set<ObjectGuid> itemsProvidingAppearance = GetCollectionMgr()->GetItemsProvidingTemporaryAppearance(itemModifedAppearanceId);
+ for (ObjectGuid const& itemGuid : itemsProvidingAppearance)
+ {
+ if (Item* item = player->GetItemByGuid(itemGuid))
+ {
+ item->SetNotRefundable(player);
+ item->ClearSoulboundTradeable(player);
+ GetCollectionMgr()->AddItemAppearance(item);
+ }
+ }
+ }
+}
+
+void WorldSession::HandleTransmogAppearanceSetFavorite(WorldPackets::Transmogrification::TransmogAppearanceSetFavorite& transmogAppearanceSetFavorite)
+{
+ bool hasAppearance, isTemporary;
+ std::tie(hasAppearance, isTemporary) = GetCollectionMgr()->HasItemAppearance(transmogAppearanceSetFavorite.ItemModifiedAppearanceID);
+ if (!hasAppearance || isTemporary)
+ return;
+
+ GetCollectionMgr()->SetAppearanceIsFavorite(transmogAppearanceSetFavorite.ItemModifiedAppearanceID, transmogAppearanceSetFavorite.IsFavorite);
+}
diff --git a/src/server/game/Handlers/VoidStorageHandler.cpp b/src/server/game/Handlers/VoidStorageHandler.cpp
index 637566d894a..6575f7b045c 100644
--- a/src/server/game/Handlers/VoidStorageHandler.cpp
+++ b/src/server/game/Handlers/VoidStorageHandler.cpp
@@ -16,6 +16,7 @@
*/
#include "Common.h"
+#include "CollectionMgr.h"
#include "WorldPacket.h"
#include "WorldSession.h"
#include "World.h"
@@ -196,6 +197,7 @@ void WorldSession::HandleVoidStorageTransfer(WorldPackets::VoidStorage::VoidStor
item->SetGuidValue(ITEM_FIELD_CREATOR, itemVS->CreatorGuid);
item->SetModifier(ITEM_MODIFIER_UPGRADE_ID, itemVS->ItemUpgradeId);
item->SetBinding(true);
+ GetCollectionMgr()->AddItemAppearance(item);
voidStorageTransferChanges.RemovedItems.push_back(ObjectGuid::Create<HighGuid::Item>(itemVS->ItemId));
diff --git a/src/server/game/Server/Packets/AllPackets.h b/src/server/game/Server/Packets/AllPackets.h
index 2c2aa528bb1..932b38bbd5e 100644
--- a/src/server/game/Server/Packets/AllPackets.h
+++ b/src/server/game/Server/Packets/AllPackets.h
@@ -70,6 +70,7 @@
#include "TotemPackets.h"
#include "ToyPackets.h"
#include "TradePackets.h"
+#include "TransmogrificationPackets.h"
#include "VehiclePackets.h"
#include "VoicePackets.h"
#include "VoidStoragePackets.h"
diff --git a/src/server/game/Server/Packets/ItemPackets.cpp b/src/server/game/Server/Packets/ItemPackets.cpp
index ffaa7f0a88b..3ae94dfb84a 100644
--- a/src/server/game/Server/Packets/ItemPackets.cpp
+++ b/src/server/game/Server/Packets/ItemPackets.cpp
@@ -503,36 +503,6 @@ WorldPacket const* WorldPackets::Item::ItemEnchantTimeUpdate::Write()
return &_worldPacket;
}
-ByteBuffer& operator>>(ByteBuffer& data, WorldPackets::Item::TransmogrifyItem& transmogItem)
-{
- if (data.ReadBit())
- transmogItem.SrcItemGUID = boost::in_place();
-
- if (data.ReadBit())
- transmogItem.SrcVoidItemGUID = boost::in_place();
-
- data >> transmogItem.Item;
- data >> transmogItem.Slot;
-
- if (transmogItem.SrcItemGUID.is_initialized())
- data >> *transmogItem.SrcItemGUID;
-
- if (transmogItem.SrcVoidItemGUID.is_initialized())
- data >> *transmogItem.SrcVoidItemGUID;
-
- return data;
-}
-
-void WorldPackets::Item::TransmogrifyItems::Read()
-{
- Items.resize(_worldPacket.read<uint32>());
- _worldPacket >> Npc;
- for (TransmogrifyItem& item : Items)
- _worldPacket >> item;
-
- CurrentSpecOnly = _worldPacket.ReadBit();
-}
-
void WorldPackets::Item::UseCritterItem::Read()
{
_worldPacket >> ItemGuid;
diff --git a/src/server/game/Server/Packets/ItemPackets.h b/src/server/game/Server/Packets/ItemPackets.h
index df2e5c9e1d0..384ba7eea5b 100644
--- a/src/server/game/Server/Packets/ItemPackets.h
+++ b/src/server/game/Server/Packets/ItemPackets.h
@@ -474,30 +474,6 @@ namespace WorldPackets
uint32 Slot = 0;
};
- struct TransmogrifyItem
- {
- Optional<ObjectGuid> SrcItemGUID;
- Optional<ObjectGuid> SrcVoidItemGUID;
- ItemInstance Item;
- uint32 Slot = 0;
- };
-
- class TransmogrifyItems final : public ClientPacket
- {
- public:
- enum
- {
- MAX_TRANSMOGRIFY_ITEMS = 11
- };
-
- TransmogrifyItems(WorldPacket&& packet) : ClientPacket(CMSG_TRANSMOGRIFY_ITEMS, std::move(packet)) { }
-
- void Read() override;
-
- ObjectGuid Npc;
- Array<TransmogrifyItem, MAX_TRANSMOGRIFY_ITEMS> Items;
- };
-
class UseCritterItem final : public ClientPacket
{
public:
diff --git a/src/server/game/Server/Packets/TransmogrificationPackets.cpp b/src/server/game/Server/Packets/TransmogrificationPackets.cpp
new file mode 100644
index 00000000000..55d4cec7c98
--- /dev/null
+++ b/src/server/game/Server/Packets/TransmogrificationPackets.cpp
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "TransmogrificationPackets.h"
+
+ByteBuffer& operator>>(ByteBuffer& data, WorldPackets::Transmogrification::TransmogrifyItem& transmogItem)
+{
+ data >> transmogItem.ItemModifiedAppearanceID;
+ data >> transmogItem.Slot;
+ data >> transmogItem.SpellItemEnchantmentID;
+
+ return data;
+}
+
+void WorldPackets::Transmogrification::TransmogrifyItems::Read()
+{
+ Items.resize(_worldPacket.read<uint32>());
+ _worldPacket >> Npc;
+ for (TransmogrifyItem& item : Items)
+ _worldPacket >> item;
+
+ CurrentSpecOnly = _worldPacket.ReadBit();
+}
+
+void WorldPackets::Transmogrification::TransmogAppearanceSetFavorite::Read()
+{
+ _worldPacket >> ItemModifiedAppearanceID;
+ IsFavorite = _worldPacket.ReadBit();
+}
+
+WorldPacket const* WorldPackets::Transmogrification::TransmogCollectionUpdate::Write()
+{
+ _worldPacket.WriteBit(IsFullUpdate);
+ _worldPacket.WriteBit(IsSetFavorite);
+ _worldPacket << uint32(FavoriteAppearances.size());
+ for (uint32 itemModifiedAppearanceId : FavoriteAppearances)
+ _worldPacket << uint32(itemModifiedAppearanceId);
+
+ return &_worldPacket;
+}
diff --git a/src/server/game/Server/Packets/TransmogrificationPackets.h b/src/server/game/Server/Packets/TransmogrificationPackets.h
new file mode 100644
index 00000000000..93305b1e0bf
--- /dev/null
+++ b/src/server/game/Server/Packets/TransmogrificationPackets.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef TransmogrificationPackets_h__
+#define TransmogrificationPackets_h__
+
+#include "Packet.h"
+#include "ObjectGuid.h"
+#include "PacketUtilities.h"
+
+namespace WorldPackets
+{
+ namespace Transmogrification
+ {
+ struct TransmogrifyItem
+ {
+ int32 ItemModifiedAppearanceID = 0;
+ uint32 Slot = 0;
+ int32 SpellItemEnchantmentID = 0;
+ };
+
+ class TransmogrifyItems final : public ClientPacket
+ {
+ public:
+ enum
+ {
+ MAX_TRANSMOGRIFY_ITEMS = 13
+ };
+
+ TransmogrifyItems(WorldPacket&& packet) : ClientPacket(CMSG_TRANSMOGRIFY_ITEMS, std::move(packet)) { }
+
+ void Read() override;
+
+ ObjectGuid Npc;
+ Array<TransmogrifyItem, MAX_TRANSMOGRIFY_ITEMS> Items;
+ bool CurrentSpecOnly = false;
+ };
+
+ class TransmogAppearanceSetFavorite final : public ClientPacket
+ {
+ public:
+ TransmogAppearanceSetFavorite(WorldPacket&& packet) : ClientPacket(CMSG_TRANSMOG_APPEARANCE_SET_FAVORITE, std::move(packet)) { }
+
+ void Read() override;
+
+ uint32 ItemModifiedAppearanceID = 0;
+ bool IsFavorite = false;
+ };
+
+ class TransmogCollectionUpdate final : public ServerPacket
+ {
+ public:
+ TransmogCollectionUpdate() : ServerPacket(SMSG_TRANSMOG_COLLECTION_UPDATE) { }
+
+ WorldPacket const* Write() override;
+
+ bool IsFullUpdate = false;
+ bool IsSetFavorite = false;
+ std::vector<uint32> FavoriteAppearances;
+ };
+ }
+}
+
+#endif // TransmogrificationPackets_h__
diff --git a/src/server/game/Server/Protocol/Opcodes.cpp b/src/server/game/Server/Protocol/Opcodes.cpp
index ac47130dcee..777f69c1c17 100644
--- a/src/server/game/Server/Protocol/Opcodes.cpp
+++ b/src/server/game/Server/Protocol/Opcodes.cpp
@@ -774,8 +774,8 @@ void OpcodeTable::Initialize()
DEFINE_HANDLER(CMSG_TRADE_SKILL_SET_FAVORITE, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, WorldPackets::Null, &WorldSession::Handle_NULL);
DEFINE_HANDLER(CMSG_TRAINER_BUY_SPELL, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::NPC::TrainerBuySpell, &WorldSession::HandleTrainerBuySpellOpcode);
DEFINE_HANDLER(CMSG_TRAINER_LIST, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::NPC::Hello, &WorldSession::HandleTrainerListOpcode);
- DEFINE_HANDLER(CMSG_TRANSMOGRIFY_ITEMS, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, WorldPackets::Item::TransmogrifyItems, &WorldSession::HandleTransmogrifyItems);
- DEFINE_HANDLER(CMSG_TRANSMOG_APPEARANCE_SET_FAVORITE, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, WorldPackets::Null, &WorldSession::Handle_NULL);
+ DEFINE_HANDLER(CMSG_TRANSMOGRIFY_ITEMS, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Transmogrification::TransmogrifyItems, &WorldSession::HandleTransmogrifyItems);
+ DEFINE_HANDLER(CMSG_TRANSMOG_APPEARANCE_SET_FAVORITE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Transmogrification::TransmogAppearanceSetFavorite, &WorldSession::HandleTransmogAppearanceSetFavorite);
DEFINE_HANDLER(CMSG_TURN_IN_PETITION, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Petition::TurnInPetition, &WorldSession::HandleTurnInPetition);
DEFINE_HANDLER(CMSG_TUTORIAL, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Misc::TutorialSetFlag, &WorldSession::HandleTutorialFlag);
DEFINE_HANDLER(CMSG_TWITTER_CHECK_STATUS, STATUS_UNHANDLED, PROCESS_INPLACE, WorldPackets::Null, &WorldSession::Handle_NULL);
@@ -1710,7 +1710,7 @@ void OpcodeTable::Initialize()
DEFINE_SERVER_OPCODE_HANDLER(SMSG_TRAINER_LIST, STATUS_NEVER, CONNECTION_TYPE_INSTANCE);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_TRANSFER_ABORTED, STATUS_NEVER, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_TRANSFER_PENDING, STATUS_NEVER, CONNECTION_TYPE_REALM);
- DEFINE_SERVER_OPCODE_HANDLER(SMSG_TRANSMOG_COLLECTION_UPDATE, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
+ DEFINE_SERVER_OPCODE_HANDLER(SMSG_TRANSMOG_COLLECTION_UPDATE, STATUS_NEVER, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_TRIGGER_CINEMATIC, STATUS_NEVER, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_TRIGGER_MOVIE, STATUS_NEVER, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_TURN_IN_PETITION_RESULT, STATUS_NEVER, CONNECTION_TYPE_REALM);
diff --git a/src/server/game/Server/WorldSession.cpp b/src/server/game/Server/WorldSession.cpp
index 937961ad571..a013d8bd662 100644
--- a/src/server/game/Server/WorldSession.cpp
+++ b/src/server/game/Server/WorldSession.cpp
@@ -1079,6 +1079,8 @@ public:
BATTLE_PET_SLOTS,
GLOBAL_ACCOUNT_HEIRLOOMS,
GLOBAL_REALM_CHARACTER_COUNTS,
+ ITEM_APPEARANCES,
+ ITEM_FAVORITE_APPEARANCES,
MAX_QUERIES
};
@@ -1109,6 +1111,14 @@ public:
stmt->setUInt32(0, accountId);
ok = SetPreparedQuery(GLOBAL_REALM_CHARACTER_COUNTS, stmt) && ok;
+ stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_BNET_ITEM_APPEARANCES);
+ stmt->setUInt32(0, battlenetAccountId);
+ ok = SetPreparedQuery(ITEM_APPEARANCES, stmt) && ok;
+
+ stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_BNET_ITEM_FAVORITE_APPEARANCES);
+ stmt->setUInt32(0, battlenetAccountId);
+ ok = SetPreparedQuery(ITEM_FAVORITE_APPEARANCES, stmt) && ok;
+
return ok;
}
};
@@ -1142,6 +1152,7 @@ void WorldSession::InitializeSessionCallback(SQLQueryHolder* realmHolder, SQLQue
LoadTutorialsData(realmHolder->GetPreparedResult(AccountInfoQueryHolderPerRealm::TUTORIALS));
_collectionMgr->LoadAccountToys(holder->GetPreparedResult(AccountInfoQueryHolder::GLOBAL_ACCOUNT_TOYS));
_collectionMgr->LoadAccountHeirlooms(holder->GetPreparedResult(AccountInfoQueryHolder::GLOBAL_ACCOUNT_HEIRLOOMS));
+ _collectionMgr->LoadAccountItemAppearances(holder->GetPreparedResult(AccountInfoQueryHolder::ITEM_APPEARANCES), holder->GetPreparedResult(AccountInfoQueryHolder::ITEM_FAVORITE_APPEARANCES));
if (!m_inQueue)
SendAuthResponse(ERROR_OK, false);
diff --git a/src/server/game/Server/WorldSession.h b/src/server/game/Server/WorldSession.h
index 5f155420ff4..02ba3d182ec 100644
--- a/src/server/game/Server/WorldSession.h
+++ b/src/server/game/Server/WorldSession.h
@@ -366,7 +366,6 @@ namespace WorldPackets
class SwapItem;
class WrapItem;
class CancelTempEnchantment;
- class TransmogrifyItems;
class UseCritterItem;
class SocketGems;
struct ItemInstance;
@@ -666,6 +665,12 @@ namespace WorldPackets
class TradeStatus;
}
+ namespace Transmogrification
+ {
+ class TransmogrifyItems;
+ class TransmogAppearanceSetFavorite;
+ }
+
namespace Vehicle
{
class MoveDismissVehicle;
@@ -1642,7 +1647,8 @@ class TC_GAME_API WorldSession
void SendVoidStorageTransferResult(VoidTransferError result);
// Transmogrification
- void HandleTransmogrifyItems(WorldPackets::Item::TransmogrifyItems& transmogrifyItems);
+ void HandleTransmogrifyItems(WorldPackets::Transmogrification::TransmogrifyItems& transmogrifyItems);
+ void HandleTransmogAppearanceSetFavorite(WorldPackets::Transmogrification::TransmogAppearanceSetFavorite& transmogAppearanceSetFavorite);
// Miscellaneous
void HandleSpellClick(WorldPackets::Spells::SpellClick& spellClick);
diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp
index b7239b21a7b..c69c2ebea5f 100644
--- a/src/server/game/Spells/Spell.cpp
+++ b/src/server/game/Spells/Spell.cpp
@@ -4137,7 +4137,7 @@ void Spell::UpdateSpellCastDataAmmo(WorldPackets::Spells::SpellAmmo& ammo)
{
ammoInventoryType = pItem->GetTemplate()->GetInventoryType();
if (ammoInventoryType == INVTYPE_THROWN)
- ammoDisplayID = pItem->GetDisplayId();
+ ammoDisplayID = pItem->GetDisplayId(m_caster->ToPlayer());
else if (m_caster->HasAura(46699)) // Requires No Ammo
{
ammoDisplayID = 5996; // normal arrow
diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp
index 0cca7a27d2c..3720c2addc8 100644
--- a/src/server/game/Spells/SpellEffects.cpp
+++ b/src/server/game/Spells/SpellEffects.cpp
@@ -5803,9 +5803,9 @@ void Spell::EffectApplyEnchantIllusion(SpellEffIndex /*effIndex*/)
return;
itemTarget->SetState(ITEM_CHANGED, player);
- itemTarget->SetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION, effectInfo->MiscValue);
+ itemTarget->SetModifier(ITEM_MODIFIER_ENCHANT_ILLUSION_ALL_SPECS, effectInfo->MiscValue);
if (itemTarget->IsEquipped())
- player->SetUInt16Value(PLAYER_VISIBLE_ITEM + VISIBLE_ITEM_ENCHANTMENT_OFFSET + (itemTarget->GetSlot() * 2), 1, itemTarget->GetVisibleItemVisual());
+ player->SetUInt16Value(PLAYER_VISIBLE_ITEM + VISIBLE_ITEM_ENCHANTMENT_OFFSET + (itemTarget->GetSlot() * 2), 1, itemTarget->GetVisibleItemVisual(player));
player->RemoveTradeableItem(itemTarget);
itemTarget->ClearSoulboundTradeable(player);