TrinityCore
CollectionMgr.cpp
Go to the documentation of this file.
1/*
2 * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the
6 * Free Software Foundation; either version 2 of the License, or (at your
7 * option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#include "CollectionMgr.h"
19#include "DatabaseEnv.h"
20#include "DB2Stores.h"
21#include "Item.h"
22#include "Log.h"
23#include "MiscPackets.h"
24#include "ObjectMgr.h"
25#include "Player.h"
26#include "Timer.h"
28#include "WorldSession.h"
29#include <boost/dynamic_bitset.hpp>
30
31namespace
32{
33 MountDefinitionMap FactionSpecificMounts;
34}
35
37{
38 uint32 oldMSTime = getMSTime();
39
40 QueryResult result = WorldDatabase.Query("SELECT spellId, otherFactionSpellId FROM mount_definitions");
41
42 if (!result)
43 {
44 TC_LOG_INFO("server.loading", ">> Loaded 0 mount definitions. DB table `mount_definitions` is empty.");
45 return;
46 }
47
48 do
49 {
50 Field* fields = result->Fetch();
51
52 uint32 spellId = fields[0].GetUInt32();
53 uint32 otherFactionSpellId = fields[1].GetUInt32();
54
55 if (!sDB2Manager.GetMount(spellId))
56 {
57 TC_LOG_ERROR("sql.sql", "Mount spell {} defined in `mount_definitions` does not exist in Mount.db2, skipped", spellId);
58 continue;
59 }
60
61 if (otherFactionSpellId && !sDB2Manager.GetMount(otherFactionSpellId))
62 {
63 TC_LOG_ERROR("sql.sql", "otherFactionSpellId {} defined in `mount_definitions` for spell {} does not exist in Mount.db2, skipped", otherFactionSpellId, spellId);
64 continue;
65 }
66
67 FactionSpecificMounts[spellId] = otherFactionSpellId;
68 } while (result->NextRow());
69
70 TC_LOG_INFO("server.loading", ">> Loaded {} mount definitions in {} ms", FactionSpecificMounts.size(), GetMSTimeDiffToNow(oldMSTime));
71}
72
73namespace
74{
75 EnumFlag<ToyFlags> GetToyFlags(bool isFavourite, bool hasFanfare)
76 {
78 if (isFavourite)
80
81 if (hasFanfare)
83
84 return flags;
85 }
86}
87
88CollectionMgr::CollectionMgr(WorldSession* owner) : _owner(owner), _appearances(std::make_unique<boost::dynamic_bitset<uint32>>()), _transmogIllusions(std::make_unique<boost::dynamic_bitset<uint32>>())
89{
90}
91
93{
94}
95
97{
98 for (auto const& t : _toys)
99 _owner->GetPlayer()->AddToy(t.first, t.second.AsUnderlyingType());
100}
101
102bool CollectionMgr::AddToy(uint32 itemId, bool isFavourite, bool hasFanfare)
103{
104 if (UpdateAccountToys(itemId, isFavourite, hasFanfare))
105 {
106 _owner->GetPlayer()->AddToy(itemId, GetToyFlags(isFavourite, hasFanfare).AsUnderlyingType());
107 return true;
108 }
109
110 return false;
111}
112
114{
115 if (!result)
116 return;
117
118 do
119 {
120 Field* fields = result->Fetch();
121 uint32 itemId = fields[0].GetUInt32();
122 _toys.emplace(itemId, GetToyFlags(fields[1].GetBool(), fields[2].GetBool()));
123 } while (result->NextRow());
124}
125
127{
128 LoginDatabasePreparedStatement* stmt = nullptr;
129 for (auto const& toy : _toys)
130 {
131 stmt = LoginDatabase.GetPreparedStatement(LOGIN_REP_ACCOUNT_TOYS);
133 stmt->setUInt32(1, toy.first);
134 stmt->setBool(2, toy.second.HasFlag(ToyFlags::Favorite));
135 stmt->setBool(3, toy.second.HasFlag(ToyFlags::HasFanfare));
136 trans->Append(stmt);
137 }
138}
139
140bool CollectionMgr::UpdateAccountToys(uint32 itemId, bool isFavourite, bool hasFanfare)
141{
142 return _toys.insert(ToyBoxContainer::value_type(itemId, GetToyFlags(isFavourite, hasFanfare))).second;
143}
144
145void CollectionMgr::ToySetFavorite(uint32 itemId, bool favorite)
146{
147 ToyBoxContainer::iterator itr = _toys.find(itemId);
148 if (itr == _toys.end())
149 return;
150
151 if (favorite)
152 itr->second |= ToyFlags::Favorite;
153 else
154 itr->second &= ~ToyFlags::Favorite;
155}
156
158{
159 auto itr = _toys.find(itemId);
160 if (itr == _toys.end())
161 return;
162
163 itr->second &= ~ ToyFlags::HasFanfare;
164}
165
167{
168 if (sDB2Manager.GetHeirloomByItemId(item->GetEntry()))
169 AddHeirloom(item->GetEntry(), 0);
170
171 AddItemAppearance(item);
172}
173
175{
176 if (!result)
177 return;
178
179 do
180 {
181 Field* fields = result->Fetch();
182 uint32 itemId = fields[0].GetUInt32();
183 uint32 flags = fields[1].GetUInt32();
184
185 HeirloomEntry const* heirloom = sDB2Manager.GetHeirloomByItemId(itemId);
186 if (!heirloom)
187 continue;
188
189 uint32 bonusId = 0;
190
191 for (int32 upgradeLevel = std::size(heirloom->UpgradeItemID) - 1; upgradeLevel >= 0; --upgradeLevel)
192 {
193 if (flags & (1 << upgradeLevel))
194 {
195 bonusId = heirloom->UpgradeItemBonusListID[upgradeLevel];
196 break;
197 }
198 }
199
200 _heirlooms[itemId] = HeirloomData(flags, bonusId);
201 } while (result->NextRow());
202}
203
205{
206 LoginDatabasePreparedStatement* stmt = nullptr;
207 for (auto const& heirloom : _heirlooms)
208 {
209 stmt = LoginDatabase.GetPreparedStatement(LOGIN_REP_ACCOUNT_HEIRLOOMS);
211 stmt->setUInt32(1, heirloom.first);
212 stmt->setUInt32(2, heirloom.second.flags);
213 trans->Append(stmt);
214 }
215}
216
218{
219 return _heirlooms.insert(HeirloomContainer::value_type(itemId, HeirloomData(flags, 0))).second;
220}
221
223{
224 HeirloomContainer::const_iterator itr = _heirlooms.find(itemId);
225 if (itr != _heirlooms.end())
226 return itr->second.bonusId;
227
228 return 0;
229}
230
232{
233 for (auto const& item : _heirlooms)
234 _owner->GetPlayer()->AddHeirloom(item.first, item.second.flags);
235}
236
238{
239 if (UpdateAccountHeirlooms(itemId, flags))
240 _owner->GetPlayer()->AddHeirloom(itemId, flags);
241}
242
244{
245 Player* player = _owner->GetPlayer();
246 if (!player)
247 return;
248
249 HeirloomEntry const* heirloom = sDB2Manager.GetHeirloomByItemId(itemId);
250 if (!heirloom)
251 return;
252
253 HeirloomContainer::iterator itr = _heirlooms.find(itemId);
254 if (itr == _heirlooms.end())
255 return;
256
257 uint32 flags = itr->second.flags;
258 uint32 bonusId = 0;
259
260 for (size_t upgradeLevel = 0; upgradeLevel < std::size(heirloom->UpgradeItemID); ++upgradeLevel)
261 {
262 if (heirloom->UpgradeItemID[upgradeLevel] == castItem)
263 {
264 flags |= 1 << upgradeLevel;
265 bonusId = heirloom->UpgradeItemBonusListID[upgradeLevel];
266 }
267 }
268
269 for (Item* item : player->GetItemListByEntry(itemId, true))
270 item->AddBonuses(bonusId);
271
272 // Get heirloom offset to update only one part of dynamic field
273 auto const& heirlooms = player->m_activePlayerData->Heirlooms;
274 uint32 offset = uint32(std::distance(heirlooms.begin(), std::find(heirlooms.begin(), heirlooms.end(), int32(itemId))));
275
276 player->SetHeirloomFlags(offset, flags);
277 itr->second.flags = flags;
278 itr->second.bonusId = bonusId;
279}
280
282{
283 Player* player = _owner->GetPlayer();
284 if (!player)
285 return;
286
287 // Check already owned heirloom for upgrade kits
288 if (HeirloomEntry const* heirloom = sDB2Manager.GetHeirloomByItemId(item->GetEntry()))
289 {
290 HeirloomContainer::iterator itr = _heirlooms.find(item->GetEntry());
291 if (itr == _heirlooms.end())
292 return;
293
294 // Check for heirloom pairs (normal - heroic, heroic - mythic)
295 uint32 heirloomItemId = heirloom->StaticUpgradedItemID;
296 uint32 newItemId = 0;
297 while (HeirloomEntry const* heirloomDiff = sDB2Manager.GetHeirloomByItemId(heirloomItemId))
298 {
299 if (player->GetItemByEntry(heirloomDiff->ItemID))
300 newItemId = heirloomDiff->ItemID;
301
302 if (HeirloomEntry const* heirloomSub = sDB2Manager.GetHeirloomByItemId(heirloomDiff->StaticUpgradedItemID))
303 {
304 heirloomItemId = heirloomSub->ItemID;
305 continue;
306 }
307
308 break;
309 }
310
311 if (newItemId)
312 {
313 auto const& heirlooms = player->m_activePlayerData->Heirlooms;
314 uint32 offset = uint32(std::distance(heirlooms.begin(), std::find(heirlooms.begin(), heirlooms.end(), int32(itr->first))));
315
316 player->SetHeirloom(offset, newItemId);
317 player->SetHeirloomFlags(offset, 0);
318
319 _heirlooms.erase(itr);
320 _heirlooms[newItemId] = 0;
321
322 return;
323 }
324
325 std::vector<int32> const& bonusListIDs = item->GetBonusListIDs();
326
327 for (uint32 bonusId : bonusListIDs)
328 {
329 if (bonusId != itr->second.bonusId)
330 {
331 item->ClearBonuses();
332 break;
333 }
334 }
335
336 if (std::find(bonusListIDs.begin(), bonusListIDs.end(), int32(itr->second.bonusId)) == bonusListIDs.end())
337 item->AddBonuses(itr->second.bonusId);
338 }
339}
340
342{
343 for (auto const& m : _mounts)
344 AddMount(m.first, m.second, false, false);
345}
346
348{
349 if (!result)
350 return;
351
352 do
353 {
354 Field* fields = result->Fetch();
355 uint32 mountSpellId = fields[0].GetUInt32();
356 MountStatusFlags flags = MountStatusFlags(fields[1].GetUInt8());
357
358 if (!sDB2Manager.GetMount(mountSpellId))
359 continue;
360
361 _mounts[mountSpellId] = flags;
362 } while (result->NextRow());
363}
364
366{
367 for (auto const& mount : _mounts)
368 {
371 stmt->setUInt32(1, mount.first);
372 stmt->setUInt8(2, mount.second);
373 trans->Append(stmt);
374 }
375}
376
377bool CollectionMgr::AddMount(uint32 spellId, MountStatusFlags flags, bool factionMount /*= false*/, bool learned /*= false*/)
378{
379 Player* player = _owner->GetPlayer();
380 if (!player)
381 return false;
382
383 MountEntry const* mount = sDB2Manager.GetMount(spellId);
384 if (!mount)
385 return false;
386
387 MountDefinitionMap::const_iterator itr = FactionSpecificMounts.find(spellId);
388 if (itr != FactionSpecificMounts.end() && !factionMount)
389 AddMount(itr->second, flags, true, learned);
390
391 _mounts.insert(MountContainer::value_type(spellId, flags));
392
393 // Mount condition only applies to using it, should still learn it.
394 if (mount->PlayerConditionID)
395 {
396 PlayerConditionEntry const* playerCondition = sPlayerConditionStore.LookupEntry(mount->PlayerConditionID);
397 if (playerCondition && !ConditionMgr::IsPlayerMeetingCondition(player, playerCondition))
398 return false;
399 }
400
401 if (!learned)
402 {
403 if (!factionMount)
404 SendSingleMountUpdate(std::make_pair(spellId, flags));
405 if (!player->HasSpell(spellId))
406 player->LearnSpell(spellId, true);
407 }
408
409 return true;
410}
411
412void CollectionMgr::MountSetFavorite(uint32 spellId, bool favorite)
413{
414 auto itr = _mounts.find(spellId);
415 if (itr == _mounts.end())
416 return;
417
418 if (favorite)
419 itr->second = MountStatusFlags(itr->second | MOUNT_IS_FAVORITE);
420 else
421 itr->second = MountStatusFlags(itr->second & ~MOUNT_IS_FAVORITE);
422
424}
425
426void CollectionMgr::SendSingleMountUpdate(std::pair<uint32, MountStatusFlags> mount)
427{
428 Player* player = _owner->GetPlayer();
429 if (!player)
430 return;
431
432 // Temporary container, just need to store only selected mount
433 MountContainer tempMounts;
434 tempMounts.insert(mount);
435
437 mountUpdate.IsFullUpdate = false;
438 mountUpdate.Mounts = &tempMounts;
439 player->SendDirectMessage(mountUpdate.Write());
440}
441
443{
444 using iterator_category = std::output_iterator_tag;
445 using value_type = void;
446 using difference_type = void;
447 using pointer = void;
448 using reference = void;
449
450 explicit DynamicBitsetBlockOutputIterator(std::function<void(uint32)>&& action) : _action(std::forward<std::function<void(uint32)>>(action)) { }
451
453 {
454 _action(value);
455 return *this;
456 }
457
461
462private:
463 std::function<void(uint32)> _action;
464};
465
467{
468 Player* owner = _owner->GetPlayer();
469 boost::to_block_range(*_appearances, DynamicBitsetBlockOutputIterator([owner](uint32 blockValue)
470 {
471 owner->AddTransmogBlock(blockValue);
472 }));
473
474 for (auto itr = _temporaryAppearances.begin(); itr != _temporaryAppearances.end(); ++itr)
475 owner->AddConditionalTransmog(itr->first);
476}
477
479{
480 if (knownAppearances)
481 {
482 std::vector<uint32> blocks;
483 do
484 {
485 Field* fields = knownAppearances->Fetch();
486 uint16 blobIndex = fields[0].GetUInt16();
487 if (blobIndex >= blocks.size())
488 blocks.resize(blobIndex + 1);
489
490 blocks[blobIndex] = fields[1].GetUInt32();
491
492 } while (knownAppearances->NextRow());
493
494 _appearances->init_from_block_range(blocks.begin(), blocks.end());
495 }
496
497 if (favoriteAppearances)
498 {
499 do
500 {
501 _favoriteAppearances[favoriteAppearances->Fetch()[0].GetUInt32()] = FavoriteAppearanceState::Unchanged;
502 } while (favoriteAppearances->NextRow());
503 }
504
505 // Static item appearances known by every player
506 static uint32 constexpr hiddenAppearanceItems[] =
507 {
508 134110, // Hidden Helm
509 134111, // Hidden Cloak
510 134112, // Hidden Shoulder
511 168659, // Hidden Chestpiece
512 142503, // Hidden Shirt
513 142504, // Hidden Tabard
514 168665, // Hidden Bracers
515 158329, // Hidden Gloves
516 143539, // Hidden Belt
517 168664 // Hidden Boots
518 };
519
520 for (uint32 hiddenItem : hiddenAppearanceItems)
521 {
522 ItemModifiedAppearanceEntry const* hiddenAppearance = sDB2Manager.GetItemModifiedAppearance(hiddenItem, 0);
523 ASSERT(hiddenAppearance);
524 if (_appearances->size() <= hiddenAppearance->ID)
525 _appearances->resize(hiddenAppearance->ID + 1);
526
527 _appearances->set(hiddenAppearance->ID);
528 }
529}
530
532{
533 uint16 blockIndex = 0;
534 boost::to_block_range(*_appearances, DynamicBitsetBlockOutputIterator([this, &blockIndex, trans](uint32 blockValue)
535 {
536 if (blockValue) // this table is only appended/bits are set (never cleared) so don't save empty blocks
537 {
540 stmt->setUInt16(1, blockIndex);
541 stmt->setUInt32(2, blockValue);
542 trans->Append(stmt);
543 }
544
545 ++blockIndex;
546 }));
547
549 for (auto itr = _favoriteAppearances.begin(); itr != _favoriteAppearances.end();)
550 {
551 switch (itr->second)
552 {
554 stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_BNET_ITEM_FAVORITE_APPEARANCE);
556 stmt->setUInt32(1, itr->first);
557 trans->Append(stmt);
559 ++itr;
560 break;
562 stmt = LoginDatabase.GetPreparedStatement(LOGIN_DEL_BNET_ITEM_FAVORITE_APPEARANCE);
564 stmt->setUInt32(1, itr->first);
565 trans->Append(stmt);
566 itr = _favoriteAppearances.erase(itr);
567 break;
569 ++itr;
570 break;
571 }
572 }
573}
574
576{
577 CLASSMASK_ALL_PLAYABLE, //ITEM_SUBCLASS_ARMOR_MISCELLANEOUS
578 (1 << (CLASS_PRIEST - 1)) | (1 << (CLASS_MAGE - 1)) | (1 << (CLASS_WARLOCK - 1)), //ITEM_SUBCLASS_ARMOR_CLOTH
579 (1 << (CLASS_ROGUE - 1)) | (1 << (CLASS_MONK - 1)) | (1 << (CLASS_DRUID - 1)) | (1 << (CLASS_DEMON_HUNTER - 1)), //ITEM_SUBCLASS_ARMOR_LEATHER
580 (1 << (CLASS_HUNTER - 1)) | (1 << (CLASS_SHAMAN - 1)), //ITEM_SUBCLASS_ARMOR_MAIL
581 (1 << (CLASS_WARRIOR - 1)) | (1 << (CLASS_PALADIN - 1)) | (1 << (CLASS_DEATH_KNIGHT - 1)), //ITEM_SUBCLASS_ARMOR_PLATE
582 CLASSMASK_ALL_PLAYABLE, //ITEM_SUBCLASS_ARMOR_BUCKLER
583 (1 << (CLASS_WARRIOR - 1)) | (1 << (CLASS_PALADIN - 1)) | (1 << (CLASS_SHAMAN - 1)), //ITEM_SUBCLASS_ARMOR_SHIELD
584 1 << (CLASS_PALADIN - 1), //ITEM_SUBCLASS_ARMOR_LIBRAM
585 1 << (CLASS_DRUID - 1), //ITEM_SUBCLASS_ARMOR_IDOL
586 1 << (CLASS_SHAMAN - 1), //ITEM_SUBCLASS_ARMOR_TOTEM
587 1 << (CLASS_DEATH_KNIGHT - 1), //ITEM_SUBCLASS_ARMOR_SIGIL
588 (1 << (CLASS_PALADIN - 1)) | (1 << (CLASS_DEATH_KNIGHT - 1)) | (1 << (CLASS_SHAMAN - 1)) | (1 << (CLASS_DRUID - 1)), //ITEM_SUBCLASS_ARMOR_RELIC
589};
590
592{
593 if (!item->IsSoulBound())
594 return;
595
596 ItemModifiedAppearanceEntry const* itemModifiedAppearance = item->GetItemModifiedAppearance();
597 if (!CanAddAppearance(itemModifiedAppearance))
598 return;
599
600 if (item->IsBOPTradeable() || item->IsRefundable())
601 {
602 AddTemporaryAppearance(item->GetGUID(), itemModifiedAppearance);
603 return;
604 }
605
606 AddItemAppearance(itemModifiedAppearance);
607}
608
609void CollectionMgr::AddItemAppearance(uint32 itemId, uint32 appearanceModId /*= 0*/)
610{
611 ItemModifiedAppearanceEntry const* itemModifiedAppearance = sDB2Manager.GetItemModifiedAppearance(itemId, appearanceModId);
612 if (!CanAddAppearance(itemModifiedAppearance))
613 return;
614
615 AddItemAppearance(itemModifiedAppearance);
616}
617
619{
620 std::vector<TransmogSetItemEntry const*> const* items = sDB2Manager.GetTransmogSetItems(transmogSetId);
621 if (!items)
622 return;
623
624 for (TransmogSetItemEntry const* item : *items)
625 {
626 ItemModifiedAppearanceEntry const* itemModifiedAppearance = sItemModifiedAppearanceStore.LookupEntry(item->ItemModifiedAppearanceID);
627 if (!itemModifiedAppearance)
628 continue;
629
630 AddItemAppearance(itemModifiedAppearance);
631 }
632}
633
634bool CollectionMgr::IsSetCompleted(uint32 transmogSetId) const
635{
636 std::vector<TransmogSetItemEntry const*> const* transmogSetItems = sDB2Manager.GetTransmogSetItems(transmogSetId);
637 if (!transmogSetItems)
638 return false;
639
640 std::array<int8, EQUIPMENT_SLOT_END> knownPieces;
641 knownPieces.fill(-1);
642 for (TransmogSetItemEntry const* transmogSetItem : *transmogSetItems)
643 {
644 ItemModifiedAppearanceEntry const* itemModifiedAppearance = sItemModifiedAppearanceStore.LookupEntry(transmogSetItem->ItemModifiedAppearanceID);
645 if (!itemModifiedAppearance)
646 continue;
647
648 ItemEntry const* item = sItemStore.LookupEntry(itemModifiedAppearance->ItemID);
649 if (!item)
650 continue;
651
652 int32 transmogSlot = ItemTransmogrificationSlots[item->InventoryType];
653 if (transmogSlot < 0 || knownPieces[transmogSlot] == 1)
654 continue;
655
656 auto [hasAppearance, isTemporary] = HasItemAppearance(transmogSetItem->ItemModifiedAppearanceID);
657
658 knownPieces[transmogSlot] = (hasAppearance && !isTemporary) ? 1 : 0;
659 }
660
661 return std::find(knownPieces.begin(), knownPieces.end(), 0) == knownPieces.end();
662}
663
664bool CollectionMgr::CanAddAppearance(ItemModifiedAppearanceEntry const* itemModifiedAppearance) const
665{
666 if (!itemModifiedAppearance)
667 return false;
668
669 if (itemModifiedAppearance->TransmogSourceTypeEnum == 6 || itemModifiedAppearance->TransmogSourceTypeEnum == 9)
670 return false;
671
672 if (!sItemSearchNameStore.LookupEntry(itemModifiedAppearance->ItemID))
673 return false;
674
675 ItemTemplate const* itemTemplate = sObjectMgr->GetItemTemplate(itemModifiedAppearance->ItemID);
676 if (!itemTemplate)
677 return false;
678
679 if (!_owner->GetPlayer())
680 return false;
681
682 if (_owner->GetPlayer()->CanUseItem(itemTemplate) != EQUIP_ERR_OK)
683 return false;
684
685 if (itemTemplate->HasFlag(ITEM_FLAG2_NO_SOURCE_FOR_ITEM_VISUAL) || itemTemplate->GetQuality() == ITEM_QUALITY_ARTIFACT)
686 return false;
687
688 switch (itemTemplate->GetClass())
689 {
691 {
692 if (!(_owner->GetPlayer()->GetWeaponProficiency() & (1 << itemTemplate->GetSubClass())))
693 return false;
694 if (itemTemplate->GetSubClass() == ITEM_SUBCLASS_WEAPON_EXOTIC ||
695 itemTemplate->GetSubClass() == ITEM_SUBCLASS_WEAPON_EXOTIC2 ||
697 itemTemplate->GetSubClass() == ITEM_SUBCLASS_WEAPON_THROWN ||
698 itemTemplate->GetSubClass() == ITEM_SUBCLASS_WEAPON_SPEAR ||
700 return false;
701 break;
702 }
703 case ITEM_CLASS_ARMOR:
704 {
705 switch (itemTemplate->GetInventoryType())
706 {
707 case INVTYPE_BODY:
708 case INVTYPE_SHIELD:
709 case INVTYPE_CLOAK:
710 case INVTYPE_TABARD:
711 case INVTYPE_HOLDABLE:
712 break;
713 case INVTYPE_HEAD:
715 case INVTYPE_CHEST:
716 case INVTYPE_WAIST:
717 case INVTYPE_LEGS:
718 case INVTYPE_FEET:
719 case INVTYPE_WRISTS:
720 case INVTYPE_HANDS:
721 case INVTYPE_ROBE:
722 if (itemTemplate->GetSubClass() == ITEM_SUBCLASS_ARMOR_MISCELLANEOUS)
723 return false;
724 break;
725 default:
726 return false;
727 }
728 if (itemTemplate->GetInventoryType() != INVTYPE_CLOAK)
730 return false;
731 break;
732 }
733 default:
734 return false;
735 }
736
737 if (itemModifiedAppearance->ID < _appearances->size() && _appearances->test(itemModifiedAppearance->ID))
738 return false;
739
740 return true;
741}
742
744{
745 Player* owner = _owner->GetPlayer();
746 if (_appearances->size() <= itemModifiedAppearance->ID)
747 {
748 std::size_t numBlocks = _appearances->num_blocks();
749 _appearances->resize(itemModifiedAppearance->ID + 1);
750 numBlocks = _appearances->num_blocks() - numBlocks;
751 while (numBlocks--)
752 owner->AddTransmogBlock(0);
753 }
754
755 _appearances->set(itemModifiedAppearance->ID);
756 uint32 blockIndex = itemModifiedAppearance->ID / 32;
757 uint32 bitIndex = itemModifiedAppearance->ID % 32;
758 owner->AddTransmogFlag(blockIndex, 1 << bitIndex);
759 auto temporaryAppearance = _temporaryAppearances.find(itemModifiedAppearance->ID);
760 if (temporaryAppearance != _temporaryAppearances.end())
761 {
762 owner->RemoveConditionalTransmog(itemModifiedAppearance->ID);
763 _temporaryAppearances.erase(temporaryAppearance);
764 }
765
766 if (ItemEntry const* item = sItemStore.LookupEntry(itemModifiedAppearance->ItemID))
767 {
768 int32 transmogSlot = ItemTransmogrificationSlots[item->InventoryType];
769 if (transmogSlot >= 0)
770 _owner->GetPlayer()->UpdateCriteria(CriteriaType::LearnAnyTransmogInSlot, transmogSlot, itemModifiedAppearance->ID);
771 }
772
773 if (std::vector<TransmogSetEntry const*> const* sets = sDB2Manager.GetTransmogSetsForItemModifiedAppearance(itemModifiedAppearance->ID))
774 for (TransmogSetEntry const* set : *sets)
775 if (IsSetCompleted(set->ID))
777}
778
779void CollectionMgr::AddTemporaryAppearance(ObjectGuid const& itemGuid, ItemModifiedAppearanceEntry const* itemModifiedAppearance)
780{
781 std::unordered_set<ObjectGuid>& itemsWithAppearance = _temporaryAppearances[itemModifiedAppearance->ID];
782 if (itemsWithAppearance.empty())
783 _owner->GetPlayer()->AddConditionalTransmog(itemModifiedAppearance->ID);
784
785 itemsWithAppearance.insert(itemGuid);
786}
787
789{
790 ItemModifiedAppearanceEntry const* itemModifiedAppearance = item->GetItemModifiedAppearance();
791 if (!itemModifiedAppearance)
792 return;
793
794 auto itr = _temporaryAppearances.find(itemModifiedAppearance->ID);
795 if (itr == _temporaryAppearances.end())
796 return;
797
798 itr->second.erase(item->GetGUID());
799 if (itr->second.empty())
800 {
801 _owner->GetPlayer()->RemoveConditionalTransmog(itemModifiedAppearance->ID);
802 _temporaryAppearances.erase(itr);
803 }
804}
805
806std::pair<bool, bool> CollectionMgr::HasItemAppearance(uint32 itemModifiedAppearanceId) const
807{
808 if (itemModifiedAppearanceId < _appearances->size() && _appearances->test(itemModifiedAppearanceId))
809 return{ true, false };
810
811 if (_temporaryAppearances.find(itemModifiedAppearanceId) != _temporaryAppearances.end())
812 return{ true,true };
813
814 return{ false,false };
815}
816
817std::unordered_set<ObjectGuid> CollectionMgr::GetItemsProvidingTemporaryAppearance(uint32 itemModifiedAppearanceId) const
818{
819 auto temporaryAppearance = _temporaryAppearances.find(itemModifiedAppearanceId);
820 if (temporaryAppearance != _temporaryAppearances.end())
821 return temporaryAppearance->second;
822
823 return std::unordered_set<ObjectGuid>();
824}
825
826std::unordered_set<uint32> CollectionMgr::GetAppearanceIds() const
827{
828 std::unordered_set<uint32> appearances;
829 std::size_t id = _appearances->find_first();
830 while (id != boost::dynamic_bitset<uint32>::npos)
831 {
832 appearances.insert(sItemModifiedAppearanceStore.AssertEntry(id)->ItemAppearanceID);
833 id = _appearances->find_next(id);
834 }
835
836 return appearances;
837}
838
839void CollectionMgr::SetAppearanceIsFavorite(uint32 itemModifiedAppearanceId, bool apply)
840{
841 auto itr = _favoriteAppearances.find(itemModifiedAppearanceId);
842 if (apply)
843 {
844 if (itr == _favoriteAppearances.end())
845 _favoriteAppearances[itemModifiedAppearanceId] = FavoriteAppearanceState::New;
846 else if (itr->second == FavoriteAppearanceState::Removed)
848 else
849 return;
850 }
851 else if (itr != _favoriteAppearances.end())
852 {
853 if (itr->second == FavoriteAppearanceState::New)
854 _favoriteAppearances.erase(itemModifiedAppearanceId);
855 else
857 }
858 else
859 return;
860
862 accountTransmogUpdate.IsFullUpdate = false;
863 accountTransmogUpdate.IsSetFavorite = apply;
864 accountTransmogUpdate.FavoriteAppearances.push_back(itemModifiedAppearanceId);
865
866 _owner->SendPacket(accountTransmogUpdate.Write());
867}
868
870{
872 accountTransmogUpdate.IsFullUpdate = true;
873 accountTransmogUpdate.FavoriteAppearances.reserve(_favoriteAppearances.size());
874 for (auto itr = _favoriteAppearances.begin(); itr != _favoriteAppearances.end(); ++itr)
875 if (itr->second != FavoriteAppearanceState::Removed)
876 accountTransmogUpdate.FavoriteAppearances.push_back(itr->first);
877
878 _owner->SendPacket(accountTransmogUpdate.Write());
879}
880
882{
883 Player* owner = _owner->GetPlayer();
884 boost::to_block_range(*_transmogIllusions, DynamicBitsetBlockOutputIterator([owner](uint32 blockValue)
885 {
886 owner->AddIllusionBlock(blockValue);
887 }));
888}
889
891{
892 if (knownTransmogIllusions)
893 {
894 std::vector<uint32> blocks;
895 do
896 {
897 Field* fields = knownTransmogIllusions->Fetch();
898 uint16 blobIndex = fields[0].GetUInt16();
899 if (blobIndex >= blocks.size())
900 blocks.resize(blobIndex + 1);
901
902 blocks[blobIndex] = fields[1].GetUInt32();
903
904 } while (knownTransmogIllusions->NextRow());
905
906 _transmogIllusions->init_from_block_range(blocks.begin(), blocks.end());
907 }
908
909 // Static illusions known by every player
910 static uint16 constexpr defaultIllusions[] =
911 {
912 3, // Lifestealing
913 13, // Crusader
914 22, // Striking
915 23, // Agility
916 34, // Hide Weapon Enchant
917 43, // Beastslayer
918 44, // Titanguard
919 };
920
921 for (uint16 illusionId : defaultIllusions)
922 {
923 if (_transmogIllusions->size() <= illusionId)
924 _transmogIllusions->resize(illusionId + 1);
925
926 _transmogIllusions->set(illusionId);
927 }
928}
929
931{
932 uint16 blockIndex = 0;
933
934 boost::to_block_range(*_transmogIllusions, DynamicBitsetBlockOutputIterator([this, &blockIndex, trans](uint32 blockValue)
935 {
936 if (blockValue) // this table is only appended/bits are set (never cleared) so don't save empty blocks
937 {
940 stmt->setUInt16(1, blockIndex);
941 stmt->setUInt32(2, blockValue);
942 trans->Append(stmt);
943 }
944 ++blockIndex;
945 }));
946}
947
949{
950 Player* owner = _owner->GetPlayer();
951 if (_transmogIllusions->size() <= transmogIllusionId)
952 {
953 std::size_t numBlocks = _transmogIllusions->num_blocks();
954 _transmogIllusions->resize(transmogIllusionId + 1);
955 numBlocks = _transmogIllusions->num_blocks() - numBlocks;
956 while (numBlocks--)
957 owner->AddIllusionBlock(0);
958 }
959
960 _transmogIllusions->set(transmogIllusionId);
961 uint32 blockIndex = transmogIllusionId / 32;
962 uint32 bitIndex = transmogIllusionId % 32;
963
964 owner->AddIllusionFlag(blockIndex, 1 << bitIndex);
965}
966
967bool CollectionMgr::HasTransmogIllusion(uint32 transmogIllusionId) const
968{
969 return transmogIllusionId < _transmogIllusions->size() && _transmogIllusions->test(transmogIllusionId);
970}
uint32 const PlayerClassByArmorSubclass[MAX_ITEM_SUBCLASS_ARMOR]
ToyFlags
Definition: CollectionMgr.h:61
MountStatusFlags
Definition: CollectionMgr.h:73
@ MOUNT_IS_FAVORITE
Definition: CollectionMgr.h:76
std::map< uint32, MountStatusFlags > MountContainer
Definition: CollectionMgr.h:79
std::unordered_map< uint32, uint32 > MountDefinitionMap
Definition: CollectionMgr.h:80
DB2Storage< ItemEntry > sItemStore("Item.db2", &ItemLoadInfo::Instance)
DB2Storage< ItemSearchNameEntry > sItemSearchNameStore("ItemSearchName.db2", &ItemSearchNameLoadInfo::Instance)
DB2Storage< ItemModifiedAppearanceEntry > sItemModifiedAppearanceStore("ItemModifiedAppearance.db2", &ItemModifiedAppearanceLoadInfo::Instance)
DB2Storage< PlayerConditionEntry > sPlayerConditionStore("PlayerCondition.db2", &PlayerConditionLoadInfo::Instance)
#define sDB2Manager
Definition: DB2Stores.h:538
@ LearnAnyTransmogInSlot
@ CollectTransmogSetFromGroup
SQLTransaction< LoginDatabaseConnection > LoginDatabaseTransaction
std::shared_ptr< ResultSet > QueryResult
std::shared_ptr< PreparedResultSet > PreparedQueryResult
DatabaseWorkerPool< LoginDatabaseConnection > LoginDatabase
Accessor to the realm/login database.
Definition: DatabaseEnv.cpp:22
DatabaseWorkerPool< WorldDatabaseConnection > WorldDatabase
Accessor to the world database.
Definition: DatabaseEnv.cpp:20
int32_t int32
Definition: Define.h:138
uint16_t uint16
Definition: Define.h:143
uint32_t uint32
Definition: Define.h:142
uint16 flags
Definition: DisableMgr.cpp:49
#define ASSERT
Definition: Errors.h:68
@ EQUIP_ERR_OK
Definition: ItemDefines.h:26
@ ITEM_CLASS_ARMOR
Definition: ItemTemplate.h:424
@ ITEM_CLASS_WEAPON
Definition: ItemTemplate.h:422
@ ITEM_FLAG2_NO_SOURCE_FOR_ITEM_VISUAL
Definition: ItemTemplate.h:234
@ ITEM_SUBCLASS_WEAPON_MISCELLANEOUS
Definition: ItemTemplate.h:494
@ ITEM_SUBCLASS_WEAPON_SPEAR
Definition: ItemTemplate.h:497
@ ITEM_SUBCLASS_WEAPON_EXOTIC2
Definition: ItemTemplate.h:492
@ ITEM_SUBCLASS_WEAPON_FISHING_POLE
Definition: ItemTemplate.h:500
@ ITEM_SUBCLASS_WEAPON_EXOTIC
Definition: ItemTemplate.h:491
@ ITEM_SUBCLASS_WEAPON_THROWN
Definition: ItemTemplate.h:496
@ ITEM_SUBCLASS_ARMOR_MISCELLANEOUS
Definition: ItemTemplate.h:529
#define MAX_ITEM_SUBCLASS_ARMOR
Definition: ItemTemplate.h:543
@ INVTYPE_BODY
Definition: ItemTemplate.h:383
@ INVTYPE_HEAD
Definition: ItemTemplate.h:380
@ INVTYPE_CLOAK
Definition: ItemTemplate.h:395
@ INVTYPE_ROBE
Definition: ItemTemplate.h:399
@ INVTYPE_HOLDABLE
Definition: ItemTemplate.h:402
@ INVTYPE_WAIST
Definition: ItemTemplate.h:385
@ INVTYPE_WRISTS
Definition: ItemTemplate.h:388
@ INVTYPE_SHOULDERS
Definition: ItemTemplate.h:382
@ INVTYPE_FEET
Definition: ItemTemplate.h:387
@ INVTYPE_SHIELD
Definition: ItemTemplate.h:393
@ INVTYPE_TABARD
Definition: ItemTemplate.h:398
@ INVTYPE_LEGS
Definition: ItemTemplate.h:386
@ INVTYPE_CHEST
Definition: ItemTemplate.h:384
@ INVTYPE_HANDS
Definition: ItemTemplate.h:389
int32 const ItemTransmogrificationSlots[MAX_INVTYPE]
Definition: Item.cpp:2028
#define TC_LOG_ERROR(filterType__,...)
Definition: Log.h:165
#define TC_LOG_INFO(filterType__,...)
Definition: Log.h:159
@ LOGIN_INS_BNET_TRANSMOG_ILLUSIONS
@ LOGIN_REP_ACCOUNT_MOUNTS
@ LOGIN_REP_ACCOUNT_HEIRLOOMS
@ LOGIN_INS_BNET_ITEM_FAVORITE_APPEARANCE
@ LOGIN_INS_BNET_ITEM_APPEARANCES
@ LOGIN_REP_ACCOUNT_TOYS
@ LOGIN_DEL_BNET_ITEM_FAVORITE_APPEARANCE
#define sObjectMgr
Definition: ObjectMgr.h:1946
@ CLASS_HUNTER
@ CLASS_DRUID
@ CLASS_SHAMAN
@ CLASS_MONK
@ CLASS_PRIEST
@ CLASS_WARRIOR
@ CLASS_WARLOCK
@ CLASS_MAGE
@ CLASS_DEATH_KNIGHT
@ CLASS_DEMON_HUNTER
@ CLASS_PALADIN
@ CLASS_ROGUE
@ ITEM_QUALITY_ARTIFACT
#define CLASSMASK_ALL_PLAYABLE
uint32 GetMSTimeDiffToNow(uint32 oldMSTime)
Definition: Timer.h:57
uint32 getMSTime()
Definition: Timer.h:33
constexpr std::underlying_type< E >::type AsUnderlyingType(E enumValue)
Definition: Util.h:491
void AddTemporaryAppearance(ObjectGuid const &itemGuid, ItemModifiedAppearanceEntry const *itemModifiedAppearance)
bool UpdateAccountToys(uint32 itemId, bool isFavourite, bool hasFanfare)
void RemoveTemporaryAppearance(Item *item)
void AddItemAppearance(Item *item)
void LoadAccountMounts(PreparedQueryResult result)
void MountSetFavorite(uint32 spellId, bool favorite)
std::unordered_map< uint32, FavoriteAppearanceState > _favoriteAppearances
bool IsSetCompleted(uint32 transmogSetId) const
void SaveAccountToys(LoginDatabaseTransaction trans)
bool UpdateAccountHeirlooms(uint32 itemId, uint32 flags)
void ToyClearFanfare(uint32 itemId)
void AddTransmogSet(uint32 transmogSetId)
WorldSession * _owner
void OnItemAdded(Item *item)
HeirloomContainer _heirlooms
void SendSingleMountUpdate(std::pair< uint32, MountStatusFlags > mount)
void SaveAccountTransmogIllusions(LoginDatabaseTransaction trans)
std::unordered_map< uint32, std::unordered_set< ObjectGuid > > _temporaryAppearances
uint32 GetHeirloomBonus(uint32 itemId) const
MountContainer _mounts
void SaveAccountMounts(LoginDatabaseTransaction trans)
void ToySetFavorite(uint32 itemId, bool favorite)
bool HasTransmogIllusion(uint32 transmogIllusionId) const
void SaveAccountHeirlooms(LoginDatabaseTransaction trans)
bool AddMount(uint32 spellId, MountStatusFlags flags, bool factionMount=false, bool learned=false)
std::unique_ptr< boost::dynamic_bitset< uint32 > > _appearances
void LoadTransmogIllusions()
std::unordered_set< ObjectGuid > GetItemsProvidingTemporaryAppearance(uint32 itemModifiedAppearanceId) const
void UpgradeHeirloom(uint32 itemId, int32 castItem)
void LoadAccountHeirlooms(PreparedQueryResult result)
std::unordered_set< uint32 > GetAppearanceIds() const
std::unique_ptr< boost::dynamic_bitset< uint32 > > _transmogIllusions
void LoadAccountTransmogIllusions(PreparedQueryResult knownTransmogIllusions)
CollectionMgr(WorldSession *owner)
bool AddToy(uint32 itemId, bool isFavourite, bool hasFanfare)
void AddHeirloom(uint32 itemId, uint32 flags)
void CheckHeirloomUpgrades(Item *item)
void LoadAccountItemAppearances(PreparedQueryResult knownAppearances, PreparedQueryResult favoriteAppearances)
std::pair< bool, bool > HasItemAppearance(uint32 itemModifiedAppearanceId) const
bool CanAddAppearance(ItemModifiedAppearanceEntry const *itemModifiedAppearance) const
void SetAppearanceIsFavorite(uint32 itemModifiedAppearanceId, bool apply)
static void LoadMountDefinitions()
void SendFavoriteAppearances() const
void SaveAccountItemAppearances(LoginDatabaseTransaction trans)
void AddTransmogIllusion(uint32 transmogIllusionId)
void LoadAccountToys(PreparedQueryResult result)
void LoadItemAppearances()
ToyBoxContainer _toys
static bool IsPlayerMeetingCondition(Player const *player, PlayerConditionEntry const *condition)
Class used to access individual fields of database query result.
Definition: Field.h:90
uint16 GetUInt16() const
Definition: Field.cpp:46
uint32 GetUInt32() const
Definition: Field.cpp:62
Definition: Item.h:170
void AddBonuses(uint32 bonusListID)
Definition: Item.cpp:2534
void ClearBonuses()
Definition: Item.cpp:2562
std::vector< int32 > const & GetBonusListIDs() const
Definition: Item.h:229
bool IsBOPTradeable() const
Definition: Item.h:249
bool IsSoulBound() const
Definition: Item.h:218
bool IsRefundable() const
Definition: Item.h:248
ItemModifiedAppearanceEntry const * GetItemModifiedAppearance() const
Definition: Item.cpp:2418
uint32 GetEntry() const
Definition: Object.h:161
static ObjectGuid GetGUID(Object const *o)
Definition: Object.h:159
void LearnSpell(uint32 spell_id, bool dependent, int32 fromSkill=0, bool suppressMessaging=false, Optional< int32 > traitDefinitionId={})
Definition: Player.cpp:3216
void SendDirectMessage(WorldPacket const *data) const
Definition: Player.cpp:6324
Item * GetItemByEntry(uint32 entry, ItemSearchLocation where=ItemSearchLocation::Default) const
Definition: Player.cpp:12589
void AddTransmogFlag(uint32 slot, uint32 flag)
Definition: Player.h:2798
void UpdateCriteria(CriteriaType type, uint64 miscValue1=0, uint64 miscValue2=0, uint64 miscValue3=0, WorldObject *ref=nullptr)
Definition: Player.cpp:26767
void AddConditionalTransmog(uint32 itemModifiedAppearanceId)
Definition: Player.h:2800
uint32 GetWeaponProficiency() const
Definition: Player.h:1484
void AddIllusionBlock(uint32 blockValue)
Definition: Player.h:2808
void RemoveConditionalTransmog(uint32 itemModifiedAppearanceId)
Definition: Player.h:2801
void AddIllusionFlag(uint32 slot, uint32 flag)
Definition: Player.h:2809
void SetHeirloom(uint32 slot, int32 itemId)
Definition: Player.h:2788
UF::UpdateField< UF::ActivePlayerData, 0, TYPEID_ACTIVE_PLAYER > m_activePlayerData
Definition: Player.h:2864
bool HasSpell(uint32 spell) const override
Definition: Player.cpp:3792
void AddToy(int32 itemId, uint32 flags)
Definition: Player.h:2791
std::vector< Item * > GetItemListByEntry(uint32 entry, bool inBankAlso=false) const
Definition: Player.cpp:12605
void AddHeirloom(int32 itemId, uint32 flags)
Definition: Player.h:2783
void AddTransmogBlock(uint32 blockValue)
Definition: Player.h:2797
InventoryResult CanUseItem(Item *pItem, bool not_loading=true) const
Definition: Player.cpp:11381
void SetHeirloomFlags(uint32 slot, uint32 flags)
Definition: Player.h:2789
void setUInt8(const uint8 index, const uint8 value)
void setBool(const uint8 index, const bool value)
void setUInt32(const uint8 index, const uint32 value)
void setUInt16(const uint8 index, const uint16 value)
uint32 GetClassMask() const
Definition: Unit.h:754
WorldPacket const * Write() override
Player session in the World.
Definition: WorldSession.h:963
Player * GetPlayer() const
void SendPacket(WorldPacket const *packet, bool forced=false)
Send a packet to the client.
uint32 GetBattlenetAccountId() const
void apply(T *val)
Definition: ByteConverter.h:41
constexpr std::size_t size()
Definition: UpdateField.h:796
STL namespace.
DynamicBitsetBlockOutputIterator & operator*()
std::function< void(uint32)> _action
std::output_iterator_tag iterator_category
DynamicBitsetBlockOutputIterator operator++(int)
DynamicBitsetBlockOutputIterator(std::function< void(uint32)> &&action)
DynamicBitsetBlockOutputIterator & operator++()
DynamicBitsetBlockOutputIterator & operator=(uint32 value)
std::array< uint16, 6 > UpgradeItemBonusListID
std::array< int32, 6 > UpgradeItemID
int8 InventoryType
uint32 GetQuality() const
Definition: ItemTemplate.h:779
InventoryType GetInventoryType() const
Definition: ItemTemplate.h:786
bool HasFlag(ItemFlags flag) const
Definition: ItemTemplate.h:871
uint32 GetSubClass() const
Definition: ItemTemplate.h:778
uint32 GetClass() const
Definition: ItemTemplate.h:777
uint32 PlayerConditionID