diff options
Diffstat (limited to 'src/server')
24 files changed, 429 insertions, 51 deletions
diff --git a/src/server/database/Database/Implementation/HotfixDatabase.cpp b/src/server/database/Database/Implementation/HotfixDatabase.cpp index f9adad39d00..d0a47a6ba82 100644 --- a/src/server/database/Database/Implementation/HotfixDatabase.cpp +++ b/src/server/database/Database/Implementation/HotfixDatabase.cpp @@ -2009,6 +2009,14 @@ void HotfixDatabaseConnection::DoPrepareStatements() PREPARE_MAX_ID_STMT(HOTFIX_SEL_VIGNETTE, "SELECT MAX(ID) + 1 FROM vignette", CONNECTION_SYNCH); PREPARE_LOCALE_STMT(HOTFIX_SEL_VIGNETTE, "SELECT ID, Name_lang FROM vignette_locale WHERE (`VerifiedBuild` > 0) = ? AND locale = ?", CONNECTION_SYNCH); + // WarbandScene.db2 + PrepareStatement(HOTFIX_SEL_WARBAND_SCENE, "SELECT Name, Description, Source, PositionX, PositionY, PositionZ, LookAtX, LookAtY, LookAtZ, ID, " + "MapID, Fov, TimeOfDay, Flags, SoundAmbienceID, Quality, TextureKit, DefaultScenePriority, SourceType FROM warband_scene" + " WHERE (`VerifiedBuild` > 0) = ?", CONNECTION_SYNCH); + PREPARE_MAX_ID_STMT(HOTFIX_SEL_WARBAND_SCENE, "SELECT MAX(ID) + 1 FROM warband_scene", CONNECTION_SYNCH); + PREPARE_LOCALE_STMT(HOTFIX_SEL_WARBAND_SCENE, "SELECT ID, Name_lang, Description_lang, Source_lang FROM warband_scene_locale" + " WHERE (`VerifiedBuild` > 0) = ? AND locale = ?", CONNECTION_SYNCH); + // WmoAreaTable.db2 PrepareStatement(HOTFIX_SEL_WMO_AREA_TABLE, "SELECT AreaName, ID, WmoID, NameSetID, WmoGroupID, SoundProviderPref, SoundProviderPrefUnderwater, " "AmbienceID, UwAmbience, ZoneMusic, UwZoneMusic, IntroSound, UwIntroSound, AreaTableID, Flags FROM wmo_area_table" diff --git a/src/server/database/Database/Implementation/HotfixDatabase.h b/src/server/database/Database/Implementation/HotfixDatabase.h index 1a7d158cdd7..f713884f6cb 100644 --- a/src/server/database/Database/Implementation/HotfixDatabase.h +++ b/src/server/database/Database/Implementation/HotfixDatabase.h @@ -1161,6 +1161,10 @@ enum HotfixDatabaseStatements : uint32 HOTFIX_SEL_VIGNETTE_MAX_ID, HOTFIX_SEL_VIGNETTE_LOCALE, + HOTFIX_SEL_WARBAND_SCENE, + HOTFIX_SEL_WARBAND_SCENE_MAX_ID, + HOTFIX_SEL_WARBAND_SCENE_LOCALE, + HOTFIX_SEL_WMO_AREA_TABLE, HOTFIX_SEL_WMO_AREA_TABLE_MAX_ID, HOTFIX_SEL_WMO_AREA_TABLE_LOCALE, diff --git a/src/server/database/Database/Implementation/LoginDatabase.cpp b/src/server/database/Database/Implementation/LoginDatabase.cpp index d67e753041f..f9cb219af61 100644 --- a/src/server/database/Database/Implementation/LoginDatabase.cpp +++ b/src/server/database/Database/Implementation/LoginDatabase.cpp @@ -191,6 +191,11 @@ void LoginDatabaseConnection::DoPrepareStatements() PrepareStatement(LOGIN_SEL_BNET_TRANSMOG_ILLUSIONS, "SELECT blobIndex, illusionMask FROM battlenet_account_transmog_illusions WHERE battlenetAccountId = ? ORDER BY blobIndex DESC", CONNECTION_ASYNC); PrepareStatement(LOGIN_INS_BNET_TRANSMOG_ILLUSIONS, "INSERT INTO battlenet_account_transmog_illusions (battlenetAccountId, blobIndex, illusionMask) VALUES (?, ?, ?) " "ON DUPLICATE KEY UPDATE illusionMask = illusionMask | VALUES(illusionMask)", CONNECTION_ASYNC); + PrepareStatement(LOGIN_SEL_BNET_WARBAND_SCENES, "SELECT warbandSceneId, isFavorite, hasFanfare FROM battlenet_account_warband_scenes WHERE battlenetAccountId = ?", CONNECTION_ASYNC); + PrepareStatement(LOGIN_INS_BNET_WARBAND_SCENE, "INSERT INTO battlenet_account_warband_scenes (battlenetAccountId, warbandSceneId, isFavorite, hasFanfare) VALUES (?, ?, ?, ?) " + "ON DUPLICATE KEY UPDATE isFavorite = VALUES(isFavorite)", CONNECTION_ASYNC); + PrepareStatement(LOGIN_UPD_BNET_WARBAND_SCENE, "UPDATE battlenet_account_warband_scenes SET isFavorite = ?, hasFanfare = ? WHERE battlenetAccountId = ? AND warbandSceneId = ?", CONNECTION_ASYNC); + PrepareStatement(LOGIN_DEL_BNET_WARBAND_SCENE, "DELETE FROM battlenet_account_warband_scenes WHERE battlenetAccountId = ? AND warbandSceneId = ?", CONNECTION_ASYNC); } LoginDatabaseConnection::LoginDatabaseConnection(MySQLConnectionInfo& connInfo, ConnectionFlags connectionFlags) : MySQLConnection(connInfo, connectionFlags) diff --git a/src/server/database/Database/Implementation/LoginDatabase.h b/src/server/database/Database/Implementation/LoginDatabase.h index e03fd277e02..7b70d063cb3 100644 --- a/src/server/database/Database/Implementation/LoginDatabase.h +++ b/src/server/database/Database/Implementation/LoginDatabase.h @@ -176,6 +176,10 @@ enum LoginDatabaseStatements : uint32 LOGIN_DEL_BNET_ITEM_FAVORITE_APPEARANCE, LOGIN_SEL_BNET_TRANSMOG_ILLUSIONS, LOGIN_INS_BNET_TRANSMOG_ILLUSIONS, + LOGIN_SEL_BNET_WARBAND_SCENES, + LOGIN_INS_BNET_WARBAND_SCENE, + LOGIN_UPD_BNET_WARBAND_SCENE, + LOGIN_DEL_BNET_WARBAND_SCENE, MAX_LOGINDATABASE_STATEMENTS }; diff --git a/src/server/game/DataStores/DB2LoadInfo.h b/src/server/game/DataStores/DB2LoadInfo.h index cacc47ea059..f3fc6fa7366 100644 --- a/src/server/game/DataStores/DB2LoadInfo.h +++ b/src/server/game/DataStores/DB2LoadInfo.h @@ -6668,6 +6668,34 @@ struct VignetteLoadInfo static constexpr DB2LoadInfo Instance{ Fields, 13, &VignetteMeta::Instance, HOTFIX_SEL_VIGNETTE }; }; +struct WarbandSceneLoadInfo +{ + static constexpr DB2FieldMeta Fields[19] = + { + { .IsSigned = false, .Type = FT_STRING, .Name = "Name" }, + { .IsSigned = false, .Type = FT_STRING, .Name = "Description" }, + { .IsSigned = false, .Type = FT_STRING, .Name = "Source" }, + { .IsSigned = false, .Type = FT_FLOAT, .Name = "PositionX" }, + { .IsSigned = false, .Type = FT_FLOAT, .Name = "PositionY" }, + { .IsSigned = false, .Type = FT_FLOAT, .Name = "PositionZ" }, + { .IsSigned = false, .Type = FT_FLOAT, .Name = "LookAtX" }, + { .IsSigned = false, .Type = FT_FLOAT, .Name = "LookAtY" }, + { .IsSigned = false, .Type = FT_FLOAT, .Name = "LookAtZ" }, + { .IsSigned = false, .Type = FT_INT, .Name = "ID" }, + { .IsSigned = false, .Type = FT_INT, .Name = "MapID" }, + { .IsSigned = false, .Type = FT_FLOAT, .Name = "Fov" }, + { .IsSigned = true, .Type = FT_INT, .Name = "TimeOfDay" }, + { .IsSigned = true, .Type = FT_INT, .Name = "Flags" }, + { .IsSigned = true, .Type = FT_INT, .Name = "SoundAmbienceID" }, + { .IsSigned = true, .Type = FT_BYTE, .Name = "Quality" }, + { .IsSigned = true, .Type = FT_INT, .Name = "TextureKit" }, + { .IsSigned = true, .Type = FT_INT, .Name = "DefaultScenePriority" }, + { .IsSigned = true, .Type = FT_BYTE, .Name = "SourceType" }, + }; + + static constexpr DB2LoadInfo Instance{ Fields, 19, &WarbandSceneMeta::Instance, HOTFIX_SEL_WARBAND_SCENE }; +}; + struct WmoAreaTableLoadInfo { static constexpr DB2FieldMeta Fields[15] = diff --git a/src/server/game/DataStores/DB2Stores.cpp b/src/server/game/DataStores/DB2Stores.cpp index c32c84e49cf..441123fe052 100644 --- a/src/server/game/DataStores/DB2Stores.cpp +++ b/src/server/game/DataStores/DB2Stores.cpp @@ -383,6 +383,7 @@ DB2Storage<UnitPowerBarEntry> sUnitPowerBarStore("UnitPowerBar DB2Storage<VehicleEntry> sVehicleStore("Vehicle.db2", &VehicleLoadInfo::Instance); DB2Storage<VehicleSeatEntry> sVehicleSeatStore("VehicleSeat.db2", &VehicleSeatLoadInfo::Instance); DB2Storage<VignetteEntry> sVignetteStore("Vignette.db2", &VignetteLoadInfo::Instance); +DB2Storage<WarbandSceneEntry> sWarbandSceneStore("WarbandScene.db2", &WarbandSceneLoadInfo::Instance); DB2Storage<WMOAreaTableEntry> sWMOAreaTableStore("WMOAreaTable.db2", &WmoAreaTableLoadInfo::Instance); DB2Storage<WorldEffectEntry> sWorldEffectStore("WorldEffect.db2", &WorldEffectLoadInfo::Instance); DB2Storage<WorldMapOverlayEntry> sWorldMapOverlayStore("WorldMapOverlay.db2", &WorldMapOverlayLoadInfo::Instance); @@ -1001,6 +1002,7 @@ uint32 DB2Manager::LoadStores(std::string const& dataPath, LocaleConstant defaul LOAD_DB2(sVehicleStore); LOAD_DB2(sVehicleSeatStore); LOAD_DB2(sVignetteStore); + LOAD_DB2(sWarbandSceneStore); LOAD_DB2(sWMOAreaTableStore); LOAD_DB2(sWorldEffectStore); LOAD_DB2(sWorldMapOverlayStore); diff --git a/src/server/game/DataStores/DB2Stores.h b/src/server/game/DataStores/DB2Stores.h index a9b1f73b614..17bdbc93c54 100644 --- a/src/server/game/DataStores/DB2Stores.h +++ b/src/server/game/DataStores/DB2Stores.h @@ -302,6 +302,7 @@ TC_GAME_API extern DB2Storage<UnitPowerBarEntry> sUnitPowerBa TC_GAME_API extern DB2Storage<VehicleEntry> sVehicleStore; TC_GAME_API extern DB2Storage<VehicleSeatEntry> sVehicleSeatStore; TC_GAME_API extern DB2Storage<VignetteEntry> sVignetteStore; +TC_GAME_API extern DB2Storage<WarbandSceneEntry> sWarbandSceneStore; TC_GAME_API extern DB2Storage<WorldEffectEntry> sWorldEffectStore; TC_GAME_API extern DB2Storage<WorldMapOverlayEntry> sWorldMapOverlayStore; TC_GAME_API extern DB2Storage<WorldStateExpressionEntry> sWorldStateExpressionStore; diff --git a/src/server/game/DataStores/DB2Structure.h b/src/server/game/DataStores/DB2Structure.h index 846c095fe71..387795998d9 100644 --- a/src/server/game/DataStores/DB2Structure.h +++ b/src/server/game/DataStores/DB2Structure.h @@ -4770,6 +4770,27 @@ struct VignetteEntry bool IsInfiniteAOI() const { return GetFlags().HasFlag(VignetteFlags::InfiniteAOI | VignetteFlags::ZoneInfiniteAOI); } }; +struct WarbandSceneEntry +{ + LocalizedString Name; + LocalizedString Description; + LocalizedString Source; + DBCPosition3D Position; + DBCPosition3D LookAt; + uint32 ID; + uint32 MapID; + float Fov; + int32 TimeOfDay; + int32 Flags; + int32 SoundAmbienceID; + int8 Quality; + int32 TextureKit; + int32 DefaultScenePriority; + int8 SourceType; + + EnumFlag<WarbandSceneFlags> GetFlags() const { return static_cast<WarbandSceneFlags>(Flags); } +}; + struct WMOAreaTableEntry { LocalizedString AreaName; diff --git a/src/server/game/DataStores/DBCEnums.h b/src/server/game/DataStores/DBCEnums.h index 01919abb61a..8342b9cbe1b 100644 --- a/src/server/game/DataStores/DBCEnums.h +++ b/src/server/game/DataStores/DBCEnums.h @@ -2657,6 +2657,17 @@ enum class VignetteFlags DEFINE_ENUM_FLAG(VignetteFlags); +enum class WarbandSceneFlags : uint8 +{ + DoNotInclude = 0x01, + HiddenUntilCollected = 0x02, + CannotBeSaved = 0x04, + AwardedAutomatically = 0x08, + IsDefault = 0x10 +}; + +DEFINE_ENUM_FLAG(WarbandSceneFlags); + enum WorldMapTransformsFlags { WORLD_MAP_TRANSFORMS_FLAG_DUNGEON = 0x04 diff --git a/src/server/game/Entities/Player/CollectionMgr.cpp b/src/server/game/Entities/Player/CollectionMgr.cpp index f5690ac2d59..5f833f7824e 100644 --- a/src/server/game/Entities/Player/CollectionMgr.cpp +++ b/src/server/game/Entities/Player/CollectionMgr.cpp @@ -16,10 +16,12 @@ */ #include "CollectionMgr.h" +#include "CollectionPackets.h" #include "DatabaseEnv.h" #include "DB2Stores.h" #include "Item.h" #include "Log.h" +#include "MapUtils.h" #include "MiscPackets.h" #include "ObjectMgr.h" #include "Player.h" @@ -31,6 +33,7 @@ namespace { MountDefinitionMap FactionSpecificMounts; + std::vector<uint32> DefaultWarbandScenes; } void CollectionMgr::LoadMountDefinitions() @@ -70,6 +73,13 @@ void CollectionMgr::LoadMountDefinitions() TC_LOG_INFO("server.loading", ">> Loaded {} mount definitions in {} ms", FactionSpecificMounts.size(), GetMSTimeDiffToNow(oldMSTime)); } +void CollectionMgr::LoadWarbandSceneDefinitions() +{ + for (WarbandSceneEntry const* warbandScene : sWarbandSceneStore) + if (warbandScene->GetFlags().HasFlag(WarbandSceneFlags::AwardedAutomatically)) + DefaultWarbandScenes.push_back(warbandScene->ID); +} + namespace { EnumFlag<ToyFlags> GetToyFlags(bool isFavourite, bool hasFanfare) @@ -89,14 +99,12 @@ CollectionMgr::CollectionMgr(WorldSession* owner) : _owner(owner), _appearances( { } -CollectionMgr::~CollectionMgr() -{ -} +CollectionMgr::~CollectionMgr() = default; void CollectionMgr::LoadToys() { - for (auto const& t : _toys) - _owner->GetPlayer()->AddToy(t.first, t.second.AsUnderlyingType()); + for (auto const& [itemId, flags] : _toys) + _owner->GetPlayer()->AddToy(itemId, flags.AsUnderlyingType()); } bool CollectionMgr::AddToy(uint32 itemId, bool isFavourite, bool hasFanfare) @@ -275,7 +283,7 @@ void CollectionMgr::UpgradeHeirloom(uint32 itemId, int32 castItem) // Get heirloom offset to update only one part of dynamic field auto const& heirlooms = player->m_activePlayerData->Heirlooms; - uint32 offset = uint32(std::distance(heirlooms.begin(), std::find(heirlooms.begin(), heirlooms.end(), int32(itemId)))); + uint32 offset = uint32(std::ranges::distance(heirlooms.begin(), std::ranges::find(heirlooms, int32(itemId)))); player->SetHeirloomFlags(offset, flags); itr->second.flags = flags; @@ -315,7 +323,7 @@ void CollectionMgr::CheckHeirloomUpgrades(Item* item) if (newItemId) { auto const& heirlooms = player->m_activePlayerData->Heirlooms; - uint32 offset = uint32(std::distance(heirlooms.begin(), std::find(heirlooms.begin(), heirlooms.end(), int32(itr->first)))); + uint32 offset = uint32(std::ranges::distance(heirlooms.begin(), std::ranges::find(heirlooms, int32(itr->first)))); player->SetHeirloom(offset, newItemId); player->SetHeirloomFlags(offset, 0); @@ -337,7 +345,7 @@ void CollectionMgr::CheckHeirloomUpgrades(Item* item) } } - if (std::find(bonusListIDs.begin(), bonusListIDs.end(), int32(itr->second.bonusId)) == bonusListIDs.end()) + if (!advstd::ranges::contains(bonusListIDs, int32(itr->second.bonusId))) item->AddBonuses(itr->second.bonusId); } } @@ -439,6 +447,7 @@ void CollectionMgr::SendSingleMountUpdate(std::pair<uint32, MountStatusFlags> mo player->SendDirectMessage(mountUpdate.Write()); } +template <std::invocable<uint32> OutputAction> struct DynamicBitsetBlockOutputIterator { using iterator_category = std::output_iterator_tag; @@ -447,11 +456,11 @@ struct DynamicBitsetBlockOutputIterator using pointer = void; using reference = void; - explicit DynamicBitsetBlockOutputIterator(std::function<void(uint32)>&& action) : _action(std::forward<std::function<void(uint32)>>(action)) { } + explicit DynamicBitsetBlockOutputIterator(OutputAction const& action) : _action(&action) { } DynamicBitsetBlockOutputIterator& operator=(uint32 value) { - _action(value); + std::invoke(*_action, value); return *this; } @@ -460,7 +469,7 @@ struct DynamicBitsetBlockOutputIterator DynamicBitsetBlockOutputIterator operator++(int) { return *this; } private: - std::function<void(uint32)> _action; + OutputAction const* _action; }; void CollectionMgr::LoadItemAppearances() @@ -471,8 +480,8 @@ void CollectionMgr::LoadItemAppearances() owner->AddTransmogBlock(blockValue); })); - for (auto itr = _temporaryAppearances.begin(); itr != _temporaryAppearances.end(); ++itr) - owner->AddConditionalTransmog(itr->first); + for (auto const& [itemModifiedAppearanceId, _] : _temporaryAppearances) + owner->AddConditionalTransmog(itemModifiedAppearanceId); } void CollectionMgr::LoadAccountItemAppearances(PreparedQueryResult knownAppearances, PreparedQueryResult favoriteAppearances) @@ -498,7 +507,7 @@ void CollectionMgr::LoadAccountItemAppearances(PreparedQueryResult knownAppearan { do { - _favoriteAppearances[favoriteAppearances->Fetch()[0].GetUInt32()] = FavoriteAppearanceState::Unchanged; + _favoriteAppearances[favoriteAppearances->Fetch()[0].GetUInt32()] = CollectionItemState::Unchanged; } while (favoriteAppearances->NextRow()); } @@ -531,7 +540,7 @@ void CollectionMgr::LoadAccountItemAppearances(PreparedQueryResult knownAppearan void CollectionMgr::SaveAccountItemAppearances(LoginDatabaseTransaction trans) { uint16 blockIndex = 0; - boost::to_block_range(*_appearances, DynamicBitsetBlockOutputIterator([this, &blockIndex, trans](uint32 blockValue) + boost::to_block_range(*_appearances, DynamicBitsetBlockOutputIterator([this, &blockIndex, trans = trans.get()](uint32 blockValue) { if (blockValue) // this table is only appended/bits are set (never cleared) so don't save empty blocks { @@ -550,29 +559,30 @@ void CollectionMgr::SaveAccountItemAppearances(LoginDatabaseTransaction trans) { switch (itr->second) { - case FavoriteAppearanceState::New: + case CollectionItemState::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->second = CollectionItemState::Unchanged; ++itr; break; - case FavoriteAppearanceState::Removed: + case CollectionItemState::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: + case CollectionItemState::Unchanged: + case CollectionItemState::Changed: ++itr; break; } } } -uint32 const PlayerClassByArmorSubclass[MAX_ITEM_SUBCLASS_ARMOR] = +constexpr uint32 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 @@ -658,7 +668,7 @@ bool CollectionMgr::IsSetCompleted(uint32 transmogSetId) const knownPieces[transmogSlot] = (hasAppearance && !isTemporary) ? 1 : 0; } - return std::find(knownPieces.begin(), knownPieces.end(), 0) == knownPieces.end(); + return !advstd::ranges::contains(knownPieces, 0); } bool CollectionMgr::CanAddAppearance(ItemModifiedAppearanceEntry const* itemModifiedAppearance) const @@ -808,12 +818,12 @@ void CollectionMgr::RemoveTemporaryAppearance(Item* item) std::pair<bool, bool> CollectionMgr::HasItemAppearance(uint32 itemModifiedAppearanceId) const { if (itemModifiedAppearanceId < _appearances->size() && _appearances->test(itemModifiedAppearanceId)) - return{ true, false }; + return { true, false }; - if (_temporaryAppearances.find(itemModifiedAppearanceId) != _temporaryAppearances.end()) - return{ true,true }; + if (_temporaryAppearances.contains(itemModifiedAppearanceId)) + return { true, true }; - return{ false,false }; + return { false, false }; } std::unordered_set<ObjectGuid> CollectionMgr::GetItemsProvidingTemporaryAppearance(uint32 itemModifiedAppearanceId) const @@ -844,18 +854,18 @@ void CollectionMgr::SetAppearanceIsFavorite(uint32 itemModifiedAppearanceId, boo if (apply) { if (itr == _favoriteAppearances.end()) - _favoriteAppearances[itemModifiedAppearanceId] = FavoriteAppearanceState::New; - else if (itr->second == FavoriteAppearanceState::Removed) - itr->second = FavoriteAppearanceState::Unchanged; + _favoriteAppearances[itemModifiedAppearanceId] = CollectionItemState::New; + else if (itr->second == CollectionItemState::Removed) + itr->second = CollectionItemState::Unchanged; else return; } else if (itr != _favoriteAppearances.end()) { - if (itr->second == FavoriteAppearanceState::New) + if (itr->second == CollectionItemState::New) _favoriteAppearances.erase(itemModifiedAppearanceId); else - itr->second = FavoriteAppearanceState::Removed; + itr->second = CollectionItemState::Removed; } else return; @@ -873,9 +883,9 @@ void CollectionMgr::SendFavoriteAppearances() const WorldPackets::Transmogrification::AccountTransmogUpdate accountTransmogUpdate; accountTransmogUpdate.IsFullUpdate = true; accountTransmogUpdate.FavoriteAppearances.reserve(_favoriteAppearances.size()); - for (auto itr = _favoriteAppearances.begin(); itr != _favoriteAppearances.end(); ++itr) - if (itr->second != FavoriteAppearanceState::Removed) - accountTransmogUpdate.FavoriteAppearances.push_back(itr->first); + for (auto [itemModifiedAppearanceId, state] : _favoriteAppearances) + if (state != CollectionItemState::Removed) + accountTransmogUpdate.FavoriteAppearances.push_back(itemModifiedAppearanceId); _owner->SendPacket(accountTransmogUpdate.Write()); } @@ -933,7 +943,7 @@ void CollectionMgr::SaveAccountTransmogIllusions(LoginDatabaseTransaction trans) { uint16 blockIndex = 0; - boost::to_block_range(*_transmogIllusions, DynamicBitsetBlockOutputIterator([this, &blockIndex, trans](uint32 blockValue) + boost::to_block_range(*_transmogIllusions, DynamicBitsetBlockOutputIterator([this, &blockIndex, trans = trans.get()](uint32 blockValue) { if (blockValue) // this table is only appended/bits are set (never cleared) so don't save empty blocks { @@ -970,3 +980,133 @@ bool CollectionMgr::HasTransmogIllusion(uint32 transmogIllusionId) const { return transmogIllusionId < _transmogIllusions->size() && _transmogIllusions->test(transmogIllusionId); } + +void CollectionMgr::LoadWarbandScenes() +{ + Player* owner = _owner->GetPlayer(); + for (auto const& [warbandSceneId, _] : _warbandScenes) + owner->AddWarbandScenesFlag(warbandSceneId / 32, 1 << warbandSceneId % 32); +} + +void CollectionMgr::LoadAccountWarbandScenes(PreparedQueryResult knownWarbandScenes) +{ + if (knownWarbandScenes) + { + do + { + Field* fields = knownWarbandScenes->Fetch(); + uint32 warbandSceneId = fields[0].GetUInt32(); + if (!sWarbandSceneStore.HasRecord(warbandSceneId)) + { + _warbandScenes[warbandSceneId].State = CollectionItemState::Removed; + continue; + } + + bool isFavorite = fields[1].GetBool(); + bool hasFanfare = fields[2].GetBool(); + + WarbandSceneCollectionItem& warbandScene = _warbandScenes.try_emplace(warbandSceneId).first->second; + if (isFavorite) + warbandScene.Flags |= WarbandSceneCollectionFlags::Favorite; + + if (hasFanfare) + warbandScene.Flags |= WarbandSceneCollectionFlags::HasFanfare; + + } while (knownWarbandScenes->NextRow()); + } + + for (uint32 warbandSceneId : DefaultWarbandScenes) + if (auto [itr, isNew] = _warbandScenes.try_emplace(warbandSceneId); isNew) + itr->second.State = CollectionItemState::New; +} + +void CollectionMgr::SaveAccountWarbandScenes(LoginDatabaseTransaction trans) +{ + LoginDatabasePreparedStatement* stmt; + for (auto itr = _warbandScenes.begin(); itr != _warbandScenes.end(); ) + { + auto& [warbandSceneId, data] = *itr; + switch (data.State) + { + case CollectionItemState::New: + stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_BNET_WARBAND_SCENE); + stmt->setUInt32(0, _owner->GetBattlenetAccountId()); + stmt->setUInt32(1, warbandSceneId); + stmt->setBool(2, data.Flags.HasFlag(WarbandSceneCollectionFlags::Favorite)); + stmt->setBool(3, data.Flags.HasFlag(WarbandSceneCollectionFlags::HasFanfare)); + trans->Append(stmt); + data.State = CollectionItemState::Unchanged; + break; + case CollectionItemState::Changed: + stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_BNET_WARBAND_SCENE); + stmt->setBool(0, data.Flags.HasFlag(WarbandSceneCollectionFlags::Favorite)); + stmt->setBool(1, data.Flags.HasFlag(WarbandSceneCollectionFlags::HasFanfare)); + stmt->setUInt32(2, _owner->GetBattlenetAccountId()); + stmt->setUInt32(3, warbandSceneId); + trans->Append(stmt); + data.State = CollectionItemState::Unchanged; + break; + case CollectionItemState::Removed: + stmt = LoginDatabase.GetPreparedStatement(LOGIN_DEL_BNET_WARBAND_SCENE); + stmt->setUInt32(0, _owner->GetBattlenetAccountId()); + stmt->setUInt32(1, warbandSceneId); + trans->Append(stmt); + itr = _warbandScenes.erase(itr); + break; + default: + break; + } + } +} + +void CollectionMgr::AddWarbandScene(uint32 warbandSceneId) +{ + if (!sWarbandSceneStore.HasRecord(warbandSceneId)) + return; + + WarbandSceneCollectionItem& warbandScene = _warbandScenes.try_emplace(warbandSceneId).first->second; + warbandScene.State = CollectionItemState::New; + + uint32 blockIndex = warbandSceneId / 32; + uint32 bitIndex = warbandSceneId % 32; + _owner->GetPlayer()->AddWarbandScenesFlag(blockIndex, 1 << bitIndex); +} + +bool CollectionMgr::HasWarbandScene(uint32 warbandSceneId) const +{ + return _warbandScenes.contains(warbandSceneId); +} + +void CollectionMgr::SetWarbandSceneIsFavorite(uint32 warbandSceneId, bool apply) +{ + WarbandSceneCollectionItem* warbandScene = Trinity::Containers::MapGetValuePtr(_warbandScenes, warbandSceneId); + if (!warbandScene) + return; + + if (apply) + warbandScene->Flags |= WarbandSceneCollectionFlags::Favorite; + else + warbandScene->Flags &= ~WarbandSceneCollectionFlags::Favorite; + + if (warbandScene->State == CollectionItemState::Unchanged) + warbandScene->State = CollectionItemState::Changed; +} + +void CollectionMgr::SendWarbandSceneCollectionData() const +{ + WorldPackets::Collections::AccountItemCollectionData accountItemCollection; + accountItemCollection.Type = ItemCollectionType::WarbandScene; + + for (auto const& [warbandSceneId, data] : _warbandScenes) + { + if (data.State == CollectionItemState::Removed) + continue; + + WorldPackets::Collections::ItemCollectionItemData& item = accountItemCollection.Items.emplace_back(); + item.ID = warbandSceneId; + item.Type = ItemCollectionType::WarbandScene; + item.Flags = data.Flags.AsUnderlyingType(); + } + + _owner->SendPacket(accountItemCollection.Write()); +} diff --git a/src/server/game/Entities/Player/CollectionMgr.h b/src/server/game/Entities/Player/CollectionMgr.h index 5e46afd8fd8..9b17eb0c8bf 100644 --- a/src/server/game/Entities/Player/CollectionMgr.h +++ b/src/server/game/Entities/Player/CollectionMgr.h @@ -15,8 +15,8 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef CollectionMgr_h__ -#define CollectionMgr_h__ +#ifndef TRINITYCORE_COLLECTION_MGR_H +#define TRINITYCORE_COLLECTION_MGR_H #include "Define.h" #include "DatabaseEnvFwd.h" @@ -31,6 +31,14 @@ class Item; class WorldSession; struct ItemModifiedAppearanceEntry; +enum class CollectionItemState : uint8 +{ + Unchanged, + New, + Changed, + Removed +}; + enum HeirloomPlayerFlags { HEIRLOOM_FLAG_NONE = 0x00, @@ -79,13 +87,35 @@ enum MountStatusFlags : uint8 typedef std::map<uint32, MountStatusFlags> MountContainer; typedef std::unordered_map<uint32, uint32> MountDefinitionMap; +enum class WarbandSceneCollectionFlags : uint8 +{ + None = 0x00, + Favorite = 0x01, + HasFanfare = 0x02 +}; + +DEFINE_ENUM_FLAG(WarbandSceneCollectionFlags); + +struct WarbandSceneCollectionItem +{ + EnumFlag<WarbandSceneCollectionFlags> Flags = WarbandSceneCollectionFlags::None; + CollectionItemState State = CollectionItemState::Unchanged; +}; + +using WarbandSceneCollectionContainer = std::map<uint32, WarbandSceneCollectionItem>; + class TC_GAME_API CollectionMgr { public: explicit CollectionMgr(WorldSession* owner); + CollectionMgr(CollectionMgr const&) = delete; + CollectionMgr(CollectionMgr&&) = delete; + CollectionMgr& operator=(CollectionMgr const&) = delete; + CollectionMgr& operator=(CollectionMgr&&) = delete; ~CollectionMgr(); static void LoadMountDefinitions(); + static void LoadWarbandSceneDefinitions(); // Account-wide toys void LoadToys(); @@ -96,7 +126,7 @@ public: bool AddToy(uint32 itemId, bool isFavourite, bool hasFanfare); bool UpdateAccountToys(uint32 itemId, bool isFavourite, bool hasFanfare); - bool HasToy(uint32 itemId) const { return _toys.count(itemId) > 0; } + bool HasToy(uint32 itemId) const { return _toys.contains(itemId); } ToyBoxContainer const& GetAccountToys() const { return _toys; } @@ -139,6 +169,9 @@ public: // returns ItemAppearance::ID, not ItemModifiedAppearance::ID std::unordered_set<uint32> GetAppearanceIds() const; + void SetAppearanceIsFavorite(uint32 itemModifiedAppearanceId, bool apply); + void SendFavoriteAppearances() const; + // Illusions void LoadTransmogIllusions(); void LoadAccountTransmogIllusions(PreparedQueryResult knownTransmogIllusions); @@ -146,15 +179,16 @@ public: void AddTransmogIllusion(uint32 transmogIllusionId); bool HasTransmogIllusion(uint32 transmogIllusionId) const; - enum class FavoriteAppearanceState - { - New, - Removed, - Unchanged - }; + // Warband Scenes + void LoadWarbandScenes(); + void LoadAccountWarbandScenes(PreparedQueryResult knownWarbandScenes); + void SaveAccountWarbandScenes(LoginDatabaseTransaction trans); + void AddWarbandScene(uint32 warbandSceneId); + bool HasWarbandScene(uint32 warbandSceneId) const; + void SetWarbandSceneIsFavorite(uint32 warbandSceneId, bool apply); + WarbandSceneCollectionContainer const& GetWarbandScenes() const { return _warbandScenes; } - void SetAppearanceIsFavorite(uint32 itemModifiedAppearanceId, bool apply); - void SendFavoriteAppearances() const; + void SendWarbandSceneCollectionData() const; private: bool CanAddAppearance(ItemModifiedAppearanceEntry const* itemModifiedAppearance) const; @@ -168,8 +202,9 @@ private: MountContainer _mounts; std::unique_ptr<boost::dynamic_bitset<uint32>> _appearances; std::unordered_map<uint32, std::unordered_set<ObjectGuid>> _temporaryAppearances; - std::unordered_map<uint32, FavoriteAppearanceState> _favoriteAppearances; + std::unordered_map<uint32, CollectionItemState> _favoriteAppearances; std::unique_ptr<boost::dynamic_bitset<uint32>> _transmogIllusions; + WarbandSceneCollectionContainer _warbandScenes; }; -#endif // CollectionMgr_h__ +#endif // TRINITYCORE_COLLECTION_MGR_H diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 96dd0fbc8fd..ee60c0324f1 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -18472,6 +18472,7 @@ bool Player::LoadFromDB(ObjectGuid guid, CharacterDatabaseQueryHolder const& hol GetSession()->GetCollectionMgr()->LoadMounts(); GetSession()->GetCollectionMgr()->LoadItemAppearances(); GetSession()->GetCollectionMgr()->LoadTransmogIllusions(); + GetSession()->GetCollectionMgr()->LoadWarbandScenes(); LearnSpecializationSpells(); @@ -20644,6 +20645,7 @@ void Player::SaveToDB(LoginDatabaseTransaction loginTransaction, CharacterDataba GetSession()->GetCollectionMgr()->SaveAccountMounts(loginTransaction); GetSession()->GetCollectionMgr()->SaveAccountItemAppearances(loginTransaction); GetSession()->GetCollectionMgr()->SaveAccountTransmogIllusions(loginTransaction); + GetSession()->GetCollectionMgr()->SaveAccountWarbandScenes(loginTransaction); Battlenet::RealmHandle currentRealmId = sRealmList->GetCurrentRealmId(); @@ -24740,6 +24742,12 @@ void Player::SendInitialPacketsBeforeAddToMap() GetSession()->GetCollectionMgr()->SendFavoriteAppearances(); + // SMSG_ACCOUNT_WARBAND_SCENE_UPDATE + WorldPackets::Misc::AccountWarbandSceneUpdate warbandSceneUpdate; + warbandSceneUpdate.IsFullUpdate = true; + warbandSceneUpdate.WarbandScenes = &GetSession()->GetCollectionMgr()->GetWarbandScenes(); + SendDirectMessage(warbandSceneUpdate.Write()); + WorldPackets::Character::InitialSetup initialSetup; initialSetup.ServerExpansionLevel = sWorld->getIntConfig(CONFIG_EXPANSION); SendDirectMessage(initialSetup.Write()); diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index 4217aa81601..36bf7c764eb 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -2909,6 +2909,9 @@ class TC_GAME_API Player final : public Unit, public GridObject<Player> void AddIllusionBlock(uint32 blockValue) { AddDynamicUpdateFieldValue(m_values.ModifyValue(&Player::m_activePlayerData).ModifyValue(&UF::ActivePlayerData::TransmogIllusions)) = blockValue; } void AddIllusionFlag(uint32 slot, uint32 flag) { SetUpdateFieldFlagValue(m_values.ModifyValue(&Player::m_activePlayerData).ModifyValue(&UF::ActivePlayerData::TransmogIllusions, slot), flag); } + void AddWarbandScenesBlock(uint32 blockValue) { AddDynamicUpdateFieldValue(m_values.ModifyValue(&Player::m_activePlayerData).ModifyValue(&UF::ActivePlayerData::WarbandScenes)) = blockValue; } + void AddWarbandScenesFlag(uint32 slot, uint32 flag) { SetUpdateFieldFlagValue(m_values.ModifyValue(&Player::m_activePlayerData).ModifyValue(&UF::ActivePlayerData::WarbandScenes, slot), flag); } + void AddSelfResSpell(int32 spellId) { AddDynamicUpdateFieldValue(m_values.ModifyValue(&Player::m_activePlayerData).ModifyValue(&UF::ActivePlayerData::SelfResSpells)) = spellId; } void RemoveSelfResSpell(int32 spellId) { diff --git a/src/server/game/Handlers/CharacterHandler.cpp b/src/server/game/Handlers/CharacterHandler.cpp index 72037caa62f..9a5ba64b353 100644 --- a/src/server/game/Handlers/CharacterHandler.cpp +++ b/src/server/game/Handlers/CharacterHandler.cpp @@ -470,6 +470,9 @@ void WorldSession::HandleCharEnum(CharacterDatabaseQueryHolder const& holder) } SendPacket(charEnum.Write()); + + if (!charEnum.IsDeletedCharacters) + _collectionMgr->SendWarbandSceneCollectionData(); } void WorldSession::HandleCharEnumOpcode(WorldPackets::Character::EnumCharacters& /*enumCharacters*/) diff --git a/src/server/game/Handlers/CollectionsHandler.cpp b/src/server/game/Handlers/CollectionsHandler.cpp index 0302ad4b354..ea32d5f26e8 100644 --- a/src/server/game/Handlers/CollectionsHandler.cpp +++ b/src/server/game/Handlers/CollectionsHandler.cpp @@ -36,7 +36,9 @@ void WorldSession::HandleCollectionItemSetFavorite(WorldPackets::Collections::Co break; } case ItemCollectionType::TransmogSetFavorite: + break; case ItemCollectionType::WarbandScene: + GetCollectionMgr()->SetWarbandSceneIsFavorite(collectionItemSetFavorite.ID, collectionItemSetFavorite.IsFavorite); break; default: break; diff --git a/src/server/game/Server/Packets/CollectionPackets.cpp b/src/server/game/Server/Packets/CollectionPackets.cpp index 5669d50a0c6..2f82f083376 100644 --- a/src/server/game/Server/Packets/CollectionPackets.cpp +++ b/src/server/game/Server/Packets/CollectionPackets.cpp @@ -18,9 +18,37 @@ #include "CollectionPackets.h" #include "PacketUtilities.h" -void WorldPackets::Collections::CollectionItemSetFavorite::Read() +namespace WorldPackets::Collections +{ +void CollectionItemSetFavorite::Read() { _worldPacket >> As<uint8>(Type); _worldPacket >> ID; _worldPacket >> Bits<1>(IsFavorite); } + +ByteBuffer& operator<<(ByteBuffer& data, ItemCollectionItemData const& item) +{ + data << int32(item.ID); + data << uint8(item.Type); + data << int64(item.Unknown1110); + data << int32(item.Flags); + + return data; +} + +WorldPacket const* AccountItemCollectionData::Write() +{ + _worldPacket << uint32(Unknown1110_1); + _worldPacket << uint8(Type); + _worldPacket << uint32(Items.size()); + + for (ItemCollectionItemData const& item : Items) + _worldPacket << item; + + _worldPacket << Bits<1>(Unknown1110_2); + _worldPacket.FlushBits(); + + return &_worldPacket; +} +} diff --git a/src/server/game/Server/Packets/CollectionPackets.h b/src/server/game/Server/Packets/CollectionPackets.h index c6a972d5bae..f9656623c27 100644 --- a/src/server/game/Server/Packets/CollectionPackets.h +++ b/src/server/game/Server/Packets/CollectionPackets.h @@ -36,6 +36,27 @@ namespace WorldPackets uint32 ID = 0; bool IsFavorite = false; }; + + struct ItemCollectionItemData + { + int32 ID = 0; + ItemCollectionType Type = ItemCollectionType::None; + int64 Unknown1110 = 0; + int32 Flags = 0; + }; + + class AccountItemCollectionData final : public ServerPacket + { + public: + AccountItemCollectionData() : ServerPacket(SMSG_ACCOUNT_ITEM_COLLECTION_DATA) { } + + WorldPacket const* Write() override; + + uint32 Unknown1110_1 = 0; + ItemCollectionType Type = ItemCollectionType::None; + bool Unknown1110_2 = false; + std::vector<ItemCollectionItemData> Items; + }; } } diff --git a/src/server/game/Server/Packets/MiscPackets.cpp b/src/server/game/Server/Packets/MiscPackets.cpp index 310a118b316..9e75e365f69 100644 --- a/src/server/game/Server/Packets/MiscPackets.cpp +++ b/src/server/game/Server/Packets/MiscPackets.cpp @@ -816,3 +816,24 @@ WorldPacket const* WorldPackets::Misc::DisplayToast::Write() return &_worldPacket; } + +WorldPacket const* WorldPackets::Misc::AccountWarbandSceneUpdate::Write() +{ + _worldPacket << Bits<1>(IsFullUpdate); + _worldPacket << uint32(WarbandScenes->size()); + _worldPacket << uint32(WarbandScenes->size()); + _worldPacket << uint32(WarbandScenes->size()); + + for (auto [warbandSceneId, _] : *WarbandScenes) + _worldPacket << uint32(warbandSceneId); + + for (auto [_, data] : *WarbandScenes) + _worldPacket << Bits<1>(data.Flags.HasFlag(WarbandSceneCollectionFlags::Favorite)); + + for (auto [_, data] : *WarbandScenes) + _worldPacket << Bits<1>(data.Flags.HasFlag(WarbandSceneCollectionFlags::HasFanfare)); + + _worldPacket.FlushBits(); + + return &_worldPacket; +} diff --git a/src/server/game/Server/Packets/MiscPackets.h b/src/server/game/Server/Packets/MiscPackets.h index 4cf942cf89f..5855ceeb233 100644 --- a/src/server/game/Server/Packets/MiscPackets.h +++ b/src/server/game/Server/Packets/MiscPackets.h @@ -1010,6 +1010,17 @@ namespace WorldPackets ::Gender Gender = GENDER_NONE; uint32 CurrencyID = 0; }; + + class AccountWarbandSceneUpdate final : public ServerPacket + { + public: + AccountWarbandSceneUpdate() : ServerPacket(SMSG_ACCOUNT_WARBAND_SCENE_UPDATE) { } + + WorldPacket const* Write() override; + + bool IsFullUpdate = false; + WarbandSceneCollectionContainer const* WarbandScenes = nullptr; + }; } } diff --git a/src/server/game/Server/Protocol/Opcodes.cpp b/src/server/game/Server/Protocol/Opcodes.cpp index 538cb0a0ee8..bf1576592f9 100644 --- a/src/server/game/Server/Protocol/Opcodes.cpp +++ b/src/server/game/Server/Protocol/Opcodes.cpp @@ -1048,7 +1048,7 @@ void OpcodeTable::InitializeServerOpcodes() DEFINE_SERVER_OPCODE_HANDLER(SMSG_ACCOUNT_CRITERIA_UPDATE, STATUS_NEVER, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_ACCOUNT_DATA_TIMES, STATUS_NEVER, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_ACCOUNT_EXPORT_RESPONSE, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); - DEFINE_SERVER_OPCODE_HANDLER(SMSG_ACCOUNT_ITEM_COLLECTION_DATA, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); + DEFINE_SERVER_OPCODE_HANDLER(SMSG_ACCOUNT_ITEM_COLLECTION_DATA, STATUS_NEVER, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_ACCOUNT_HEIRLOOM_UPDATE, STATUS_NEVER, CONNECTION_TYPE_INSTANCE); DEFINE_SERVER_OPCODE_HANDLER(SMSG_ACCOUNT_MOUNT_REMOVED, STATUS_UNHANDLED, CONNECTION_TYPE_INSTANCE); DEFINE_SERVER_OPCODE_HANDLER(SMSG_ACCOUNT_MOUNT_UPDATE, STATUS_NEVER, CONNECTION_TYPE_INSTANCE); @@ -1060,7 +1060,7 @@ void OpcodeTable::InitializeServerOpcodes() DEFINE_SERVER_OPCODE_HANDLER(SMSG_ACCOUNT_TOY_UPDATE, STATUS_NEVER, CONNECTION_TYPE_INSTANCE); DEFINE_SERVER_OPCODE_HANDLER(SMSG_ACCOUNT_TRANSMOG_SET_FAVORITES_UPDATE, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_ACCOUNT_TRANSMOG_UPDATE, STATUS_NEVER, CONNECTION_TYPE_REALM); - DEFINE_SERVER_OPCODE_HANDLER(SMSG_ACCOUNT_WARBAND_SCENE_UPDATE, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); + DEFINE_SERVER_OPCODE_HANDLER(SMSG_ACCOUNT_WARBAND_SCENE_UPDATE, STATUS_NEVER, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_ACHIEVEMENT_DELETED, STATUS_NEVER, CONNECTION_TYPE_INSTANCE); DEFINE_SERVER_OPCODE_HANDLER(SMSG_ACHIEVEMENT_EARNED, STATUS_NEVER, CONNECTION_TYPE_INSTANCE); DEFINE_SERVER_OPCODE_HANDLER(SMSG_ACTIVATE_ESSENCE_FAILED, STATUS_NEVER, CONNECTION_TYPE_REALM); diff --git a/src/server/game/Server/WorldSession.cpp b/src/server/game/Server/WorldSession.cpp index 01591b0b3c8..2d095c59ac4 100644 --- a/src/server/game/Server/WorldSession.cpp +++ b/src/server/game/Server/WorldSession.cpp @@ -1100,6 +1100,7 @@ public: ITEM_APPEARANCES, ITEM_FAVORITE_APPEARANCES, TRANSMOG_ILLUSIONS, + WARBAND_SCENES, MAX_QUERIES }; @@ -1147,6 +1148,10 @@ public: stmt->setUInt32(0, battlenetAccountId); ok = SetPreparedQuery(TRANSMOG_ILLUSIONS, stmt) && ok; + stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_BNET_WARBAND_SCENES); + stmt->setUInt32(0, battlenetAccountId); + ok = SetPreparedQuery(WARBAND_SCENES, stmt) && ok; + return ok; } }; @@ -1199,6 +1204,7 @@ void WorldSession::InitializeSessionCallback(LoginDatabaseQueryHolder const& hol _collectionMgr->LoadAccountMounts(holder.GetPreparedResult(AccountInfoQueryHolder::MOUNTS)); _collectionMgr->LoadAccountItemAppearances(holder.GetPreparedResult(AccountInfoQueryHolder::ITEM_APPEARANCES), holder.GetPreparedResult(AccountInfoQueryHolder::ITEM_FAVORITE_APPEARANCES)); _collectionMgr->LoadAccountTransmogIllusions(holder.GetPreparedResult(AccountInfoQueryHolder::TRANSMOG_ILLUSIONS)); + _collectionMgr->LoadAccountWarbandScenes(holder.GetPreparedResult(AccountInfoQueryHolder::WARBAND_SCENES)); if (!m_inQueue) SendAuthResponse(ERROR_OK, false); diff --git a/src/server/game/Spells/Spell.h b/src/server/game/Spells/Spell.h index db187dd3adb..fcb41cb16dd 100644 --- a/src/server/game/Spells/Spell.h +++ b/src/server/game/Spells/Spell.h @@ -441,6 +441,7 @@ class TC_GAME_API Spell void EffectChangeActiveCombatTraitConfig(); void EffectTeleportGraveyard(); void EffectUpdateInteractions(); + void EffectLearnWarbandScene(); typedef std::unordered_set<Aura*> UsedSpellMods; diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp index 7bcb7d27f65..e01435dc9e8 100644 --- a/src/server/game/Spells/SpellEffects.cpp +++ b/src/server/game/Spells/SpellEffects.cpp @@ -429,7 +429,7 @@ NonDefaultConstructible<SpellEffectHandlerFn> SpellEffectHandlers[TOTAL_SPELL_EF &Spell::EffectNULL, //338 SPELL_EFFECT_338 &Spell::EffectNULL, //339 SPELL_EFFECT_UI_ACTION &Spell::EffectNULL, //340 SPELL_EFFECT_340 - &Spell::EffectNULL, //341 SPELL_EFFECT_LEARN_WARBAND_SCENE + &Spell::EffectLearnWarbandScene, //341 SPELL_EFFECT_LEARN_WARBAND_SCENE }; void Spell::EffectNULL() @@ -6177,3 +6177,15 @@ void Spell::EffectUpdateInteractions() target->UpdateVisibleObjectInteractions(true, false, true, true); } + +void Spell::EffectLearnWarbandScene() +{ + if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET) + return; + + Player* target = Object::ToPlayer(unitTarget); + if (!target) + return; + + target->GetSession()->GetCollectionMgr()->AddWarbandScene(effectInfo->MiscValue); +} diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index 4c5462b46af..920386c1fd5 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -2365,6 +2365,9 @@ bool World::SetInitialWorldSettings() TC_LOG_INFO("server.loading", "Loading mount definitions..."); CollectionMgr::LoadMountDefinitions(); + TC_LOG_INFO("server.loading", "Loading warband scene definitions..."); + CollectionMgr::LoadWarbandSceneDefinitions(); + TC_LOG_INFO("server.loading", "Loading GM bugs..."); sSupportMgr->LoadBugTickets(); |