diff --git a/Source/Framework/Constants/SharedConst.cs b/Source/Framework/Constants/SharedConst.cs index 03710faa0..645ea9449 100644 --- a/Source/Framework/Constants/SharedConst.cs +++ b/Source/Framework/Constants/SharedConst.cs @@ -3139,6 +3139,15 @@ namespace Framework.Constants NoPaddingAboveUiWidgets = 0x020000 } + public enum WarbandSceneFlags + { + DoNotInclude = 0x01, + HiddenUntilCollected = 0x02, + CannotBeSaved = 0x04, + AwardedAutomatically = 0x08, + IsDefault = 0x10 + } + public enum WorldMapTransformsFlags { Dungeon = 0x04 diff --git a/Source/Framework/Database/Databases/HotfixDatabase.cs b/Source/Framework/Database/Databases/HotfixDatabase.cs index 90c6dd268..8fcd1475e 100644 --- a/Source/Framework/Database/Databases/HotfixDatabase.cs +++ b/Source/Framework/Database/Databases/HotfixDatabase.cs @@ -1629,6 +1629,13 @@ namespace Framework.Database "MinHeight, VignetteType, RewardQuestID, UiWidgetSetID, UiMapPinInfoID, ObjectiveType FROM vignette WHERE (`VerifiedBuild` > 0) = ?"); PrepareStatement(HotfixStatements.SEL_VIGNETTE_LOCALE, "SELECT ID, Name_lang FROM vignette_locale WHERE (`VerifiedBuild` > 0) = ? AND locale = ?"); + // WarbandScene.db2 + PrepareStatement(HotfixStatements.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) = ?"); + PrepareStatement(HotfixStatements.SEL_WARBAND_SCENE_LOCALE, "SELECT ID, Name_lang, Description_lang, Source_lang FROM warband_scene_locale" + + " WHERE (`VerifiedBuild` > 0) = ? AND locale = ?"); + // WmoAreaTable.db2 PrepareStatement(HotfixStatements.SEL_WMO_AREA_TABLE, "SELECT AreaName, ID, WmoID, NameSetID, WmoGroupID, SoundProviderPref, SoundProviderPrefUnderwater, " + "AmbienceID, UwAmbience, ZoneMusic, UwZoneMusic, IntroSound, UwIntroSound, AreaTableID, Flags FROM wmo_area_table" + @@ -2435,6 +2442,9 @@ namespace Framework.Database SEL_VIGNETTE, SEL_VIGNETTE_LOCALE, + SEL_WARBAND_SCENE, + SEL_WARBAND_SCENE_LOCALE, + SEL_WMO_AREA_TABLE, SEL_WMO_AREA_TABLE_LOCALE, diff --git a/Source/Framework/Database/Databases/LoginDatabase.cs b/Source/Framework/Database/Databases/LoginDatabase.cs index e8cae1b1a..79a5f4650 100644 --- a/Source/Framework/Database/Databases/LoginDatabase.cs +++ b/Source/Framework/Database/Databases/LoginDatabase.cs @@ -174,6 +174,11 @@ namespace Framework.Database PrepareStatement(LoginStatements.SEL_BNET_TRANSMOG_ILLUSIONS, "SELECT blobIndex, illusionMask FROM battlenet_account_transmog_illusions WHERE battlenetAccountId = ? ORDER BY blobIndex DESC"); PrepareStatement(LoginStatements.INS_BNET_TRANSMOG_ILLUSIONS, "INSERT INTO battlenet_account_transmog_illusions (battlenetAccountId, blobIndex, illusionMask) VALUES (?, ?, ?) " + "ON DUPLICATE KEY UPDATE illusionMask = illusionMask | VALUES(illusionMask)"); + PrepareStatement(LoginStatements.SEL_BNET_WARBAND_SCENES, "SELECT warbandSceneId, isFavorite, hasFanfare FROM battlenet_account_warband_scenes WHERE battlenetAccountId = ?"); + PrepareStatement(LoginStatements.INS_BNET_WARBAND_SCENE, "INSERT INTO battlenet_account_warband_scenes (battlenetAccountId, warbandSceneId, isFavorite, hasFanfare) VALUES (?, ?, ?, ?) " + + "ON DUPLICATE KEY UPDATE isFavorite = VALUES(isFavorite)"); + PrepareStatement(LoginStatements.UPD_BNET_WARBAND_SCENE, "UPDATE battlenet_account_warband_scenes SET isFavorite = ?, hasFanfare = ? WHERE battlenetAccountId = ? AND warbandSceneId = ?"); + PrepareStatement(LoginStatements.DEL_BNET_WARBAND_SCENE, "DELETE FROM battlenet_account_warband_scenes WHERE battlenetAccountId = ? AND warbandSceneId = ?"); } } @@ -327,6 +332,10 @@ namespace Framework.Database DEL_BNET_ITEM_FAVORITE_APPEARANCE, SEL_BNET_TRANSMOG_ILLUSIONS, INS_BNET_TRANSMOG_ILLUSIONS, + SEL_BNET_WARBAND_SCENES, + INS_BNET_WARBAND_SCENE, + UPD_BNET_WARBAND_SCENE, + DEL_BNET_WARBAND_SCENE, MAX_LOGINDATABASE_STATEMENTS } diff --git a/Source/Game/DataStorage/CliDB.cs b/Source/Game/DataStorage/CliDB.cs index da7f13aec..168bcf531 100644 --- a/Source/Game/DataStorage/CliDB.cs +++ b/Source/Game/DataStorage/CliDB.cs @@ -392,6 +392,7 @@ namespace Game.DataStorage VehicleStorage = ReadDB2("Vehicle.db2", HotfixStatements.SEL_VEHICLE); VehicleSeatStorage = ReadDB2("VehicleSeat.db2", HotfixStatements.SEL_VEHICLE_SEAT); VignetteStorage = ReadDB2("Vignette.db2", HotfixStatements.SEL_VIGNETTE, HotfixStatements.SEL_VIGNETTE_LOCALE); + WarbandSceneStorage = ReadDB2("WarbandScene.db2", HotfixStatements.SEL_WARBAND_SCENE, HotfixStatements.SEL_WARBAND_SCENE_LOCALE); WMOAreaTableStorage = ReadDB2("WMOAreaTable.db2", HotfixStatements.SEL_WMO_AREA_TABLE, HotfixStatements.SEL_WMO_AREA_TABLE_LOCALE); WorldEffectStorage = ReadDB2("WorldEffect.db2", HotfixStatements.SEL_WORLD_EFFECT); WorldMapOverlayStorage = ReadDB2("WorldMapOverlay.db2", HotfixStatements.SEL_WORLD_MAP_OVERLAY); @@ -790,6 +791,7 @@ namespace Game.DataStorage public static DB6Storage VehicleStorage; public static DB6Storage VehicleSeatStorage; public static DB6Storage VignetteStorage; + public static DB6Storage WarbandSceneStorage; public static DB6Storage WMOAreaTableStorage; public static DB6Storage WorldEffectStorage; public static DB6Storage WorldMapOverlayStorage; diff --git a/Source/Game/DataStorage/Structs/W_Records.cs b/Source/Game/DataStorage/Structs/W_Records.cs index c8a83dca6..4129bcce9 100644 --- a/Source/Game/DataStorage/Structs/W_Records.cs +++ b/Source/Game/DataStorage/Structs/W_Records.cs @@ -2,9 +2,31 @@ // Licensed under the GNU GENERAL PUBLIC LICENSE. See LICENSE file in the project root for full license information. using Framework.Constants; +using System.Numerics; namespace Game.DataStorage { + public sealed class WarbandSceneRecord + { + public LocalizedString Name; + public LocalizedString Description; + public LocalizedString Source; + public Vector3 Position; + public Vector3 LookAt; + public uint Id; + public uint MapID; + public float Fov; + public int TimeOfDay; + public int Flags; + public int SoundAmbienceID; + public sbyte Quality; + public int TextureKit; + public int DefaultScenePriority; + public sbyte SourceType; + + public WarbandSceneFlags GetFlags() { return (WarbandSceneFlags)Flags; } + } + public sealed class WMOAreaTableRecord { public string AreaName; diff --git a/Source/Game/Entities/Player/CollectionManager.cs b/Source/Game/Entities/Player/CollectionManager.cs index 868426d50..ce7b1ac6e 100644 --- a/Source/Game/Entities/Player/CollectionManager.cs +++ b/Source/Game/Entities/Player/CollectionManager.cs @@ -15,6 +15,7 @@ namespace Game.Entities public class CollectionMgr { static Dictionary FactionSpecificMounts = new(); + static List DefaultWarbandScenes = new(); WorldSession _owner; Dictionary _toys = new(); @@ -22,8 +23,9 @@ namespace Game.Entities Dictionary _mounts = new(); BitSet _appearances; MultiMap _temporaryAppearances = new(); - Dictionary _favoriteAppearances = new(); + Dictionary _favoriteAppearances = new(); BitSet _transmogIllusions; + Dictionary _warbandScenes = new(); public static void LoadMountDefinitions() { @@ -59,6 +61,13 @@ namespace Game.Entities Log.outInfo(LogFilter.ServerLoading, "Loaded {0} mount definitions in {1} ms", FactionSpecificMounts.Count, Time.GetMSTimeDiffToNow(oldMSTime)); } + public static void LoadWarbandSceneDefinitions() + { + foreach (var (_, warbandScene) in CliDB.WarbandSceneStorage) + if (warbandScene.GetFlags().HasFlag(WarbandSceneFlags.AwardedAutomatically)) + DefaultWarbandScenes.Add(warbandScene.Id); + } + public CollectionMgr(WorldSession owner) { _owner = owner; @@ -68,8 +77,8 @@ namespace Game.Entities public void LoadToys() { - foreach (var pair in _toys) - _owner.GetPlayer().AddToy(pair.Key, (uint)pair.Value); + foreach (var (itemId, flags) in _toys) + _owner.GetPlayer().AddToy(itemId, (uint)flags); } public bool AddToy(uint itemId, bool isFavourite, bool hasFanfare) @@ -435,8 +444,8 @@ namespace Game.Entities foreach (uint blockValue in _appearances.ToBlockRange()) owner.AddTransmogBlock(blockValue); - foreach (var value in _temporaryAppearances.Keys) - owner.AddConditionalTransmog(value); + foreach (var itemModifiedAppearanceId in _temporaryAppearances.Keys) + owner.AddConditionalTransmog(itemModifiedAppearanceId); } public void LoadAccountItemAppearances(SQLResult knownAppearances, SQLResult favoriteAppearances) @@ -461,7 +470,7 @@ namespace Game.Entities { do { - _favoriteAppearances[favoriteAppearances.Read(0)] = FavoriteAppearanceState.Unchanged; + _favoriteAppearances[favoriteAppearances.Read(0)] = CollectionItemState.Unchanged; } while (favoriteAppearances.NextRow()); } @@ -511,24 +520,24 @@ namespace Game.Entities foreach (var key in _favoriteAppearances.Keys) { - var appearanceState = _favoriteAppearances[key]; - switch (appearanceState) + switch (_favoriteAppearances[key]) { - case FavoriteAppearanceState.New: + case CollectionItemState.New: stmt = LoginDatabase.GetPreparedStatement(LoginStatements.INS_BNET_ITEM_FAVORITE_APPEARANCE); stmt.AddValue(0, _owner.GetBattlenetAccountId()); stmt.AddValue(1, key); trans.Append(stmt); - appearanceState = FavoriteAppearanceState.Unchanged; + _favoriteAppearances[key] = CollectionItemState.Unchanged; break; - case FavoriteAppearanceState.Removed: + case CollectionItemState.Removed: stmt = LoginDatabase.GetPreparedStatement(LoginStatements.DEL_BNET_ITEM_FAVORITE_APPEARANCE); stmt.AddValue(0, _owner.GetBattlenetAccountId()); stmt.AddValue(1, key); trans.Append(stmt); _favoriteAppearances.Remove(key); break; - case FavoriteAppearanceState.Unchanged: + case CollectionItemState.Unchanged: + case CollectionItemState.Changed: break; } } @@ -753,18 +762,18 @@ namespace Game.Entities if (apply) { if (!_favoriteAppearances.ContainsKey(itemModifiedAppearanceId)) - _favoriteAppearances[itemModifiedAppearanceId] = FavoriteAppearanceState.New; - else if (apperanceState == FavoriteAppearanceState.Removed) - apperanceState = FavoriteAppearanceState.Unchanged; + _favoriteAppearances[itemModifiedAppearanceId] = CollectionItemState.New; + else if (apperanceState == CollectionItemState.Removed) + apperanceState = CollectionItemState.Unchanged; else return; } else if (_favoriteAppearances.ContainsKey(itemModifiedAppearanceId)) { - if (apperanceState == FavoriteAppearanceState.New) + if (apperanceState == CollectionItemState.New) _favoriteAppearances.Remove(itemModifiedAppearanceId); else - apperanceState = FavoriteAppearanceState.Removed; + apperanceState = CollectionItemState.Removed; } else return; @@ -783,9 +792,9 @@ namespace Game.Entities { AccountTransmogUpdate accountTransmogUpdate = new(); accountTransmogUpdate.IsFullUpdate = true; - foreach (var pair in _favoriteAppearances) - if (pair.Value != FavoriteAppearanceState.Removed) - accountTransmogUpdate.FavoriteAppearances.Add(pair.Key); + foreach (var (itemModifiedAppearanceId, state) in _favoriteAppearances) + if (state != CollectionItemState.Removed) + accountTransmogUpdate.FavoriteAppearances.Add(itemModifiedAppearanceId); _owner.SendPacket(accountTransmogUpdate); } @@ -922,17 +931,171 @@ namespace Game.Entities return transmogIllusionId < _transmogIllusions.Count && _transmogIllusions.Get((int)transmogIllusionId); } + public void LoadWarbandScenes() + { + Player owner = _owner.GetPlayer(); + foreach (var (warbandSceneId, _) in _warbandScenes) + owner.AddWarbandScenesFlag((int)(warbandSceneId / 32), 1u << (int)warbandSceneId % 32); + } + + public void LoadAccountWarbandScenes(SQLResult result) + { + if (!result.IsEmpty()) + { + do + { + uint warbandSceneId = result.Read(0); + if (!CliDB.WarbandSceneStorage.HasRecord(warbandSceneId)) + { + if (!_warbandScenes.ContainsKey(warbandSceneId)) + _warbandScenes[warbandSceneId] = new(); + + _warbandScenes[warbandSceneId].State = CollectionItemState.Removed; + continue; + } + + bool isFavorite = result.Read(1); + bool hasFanfare = result.Read(2); + + if (!_warbandScenes.ContainsKey(warbandSceneId)) + _warbandScenes[warbandSceneId] = new(); + + if (isFavorite) + _warbandScenes[warbandSceneId].Flags |= WarbandSceneCollectionFlags.Favorite; + + if (hasFanfare) + _warbandScenes[warbandSceneId].Flags |= WarbandSceneCollectionFlags.HasFanfare; + + } while (result.NextRow()); + } + + foreach (uint warbandSceneId in DefaultWarbandScenes) + { + if (_warbandScenes.ContainsKey(warbandSceneId)) + continue; + + _warbandScenes[warbandSceneId] = new(); + _warbandScenes[warbandSceneId].State = CollectionItemState.New; + } + } + + public void SaveAccountWarbandScenes(SQLTransaction trans) + { + PreparedStatement stmt; + foreach (var warbandSceneId in _warbandScenes.Keys.ToList()) + { + var warbanScene = _warbandScenes[warbandSceneId]; + switch (warbanScene.State) + { + case CollectionItemState.New: + stmt = LoginDatabase.GetPreparedStatement(LoginStatements.INS_BNET_WARBAND_SCENE); + stmt.AddValue(0, _owner.GetBattlenetAccountId()); + stmt.AddValue(1, warbandSceneId); + stmt.AddValue(2, warbanScene.Flags.HasFlag(WarbandSceneCollectionFlags.Favorite)); + stmt.AddValue(3, warbanScene.Flags.HasFlag(WarbandSceneCollectionFlags.HasFanfare)); + trans.Append(stmt); + warbanScene.State = CollectionItemState.Unchanged; + break; + case CollectionItemState.Changed: + stmt = LoginDatabase.GetPreparedStatement(LoginStatements.UPD_BNET_WARBAND_SCENE); + stmt.AddValue(0, warbanScene.Flags.HasFlag(WarbandSceneCollectionFlags.Favorite)); + stmt.AddValue(1, warbanScene.Flags.HasFlag(WarbandSceneCollectionFlags.HasFanfare)); + stmt.AddValue(2, _owner.GetBattlenetAccountId()); + stmt.AddValue(3, warbandSceneId); + trans.Append(stmt); + warbanScene.State = CollectionItemState.Unchanged; + break; + case CollectionItemState.Removed: + stmt = LoginDatabase.GetPreparedStatement(LoginStatements.DEL_BNET_WARBAND_SCENE); + stmt.AddValue(0, _owner.GetBattlenetAccountId()); + stmt.AddValue(1, warbandSceneId); + trans.Append(stmt); + _warbandScenes.Remove(warbandSceneId); + break; + default: + break; + } + } + } + + public void AddWarbandScene(uint warbandSceneId) + { + if (!CliDB.WarbandSceneStorage.HasRecord(warbandSceneId)) + return; + + if (!_warbandScenes.ContainsKey(warbandSceneId)) + { + _warbandScenes[warbandSceneId] = new(); + _warbandScenes[warbandSceneId].State = CollectionItemState.New; + } + + int blockIndex = (int)warbandSceneId / 32; + int bitIndex = (int)warbandSceneId % 32; + _owner.GetPlayer().AddWarbandScenesFlag(blockIndex, 1u << bitIndex); + } + + public bool HasWarbandScene(uint warbandSceneId) + { + return _warbandScenes.ContainsKey(warbandSceneId); + } + + public void SetWarbandSceneIsFavorite(uint warbandSceneId, bool apply) + { + WarbandSceneCollectionItem warbandScene = _warbandScenes.LookupByKey(warbandSceneId); + if (warbandScene == null) + return; + + if (apply) + warbandScene.Flags |= WarbandSceneCollectionFlags.Favorite; + else + warbandScene.Flags &= ~WarbandSceneCollectionFlags.Favorite; + + if (warbandScene.State == CollectionItemState.Unchanged) + warbandScene.State = CollectionItemState.Changed; + } + + public void SendWarbandSceneCollectionData() + { + AccountItemCollectionData accountItemCollection = new(); + accountItemCollection.Type = ItemCollectionType.WarbandScene; + + foreach (var (warbandSceneId, data) in _warbandScenes) + { + if (data.State == CollectionItemState.Removed) + continue; + + ItemCollectionItemData item = new(); + item.Id = (int)warbandSceneId; + item.Type = ItemCollectionType.WarbandScene; + item.Flags = (int)data.Flags; + + accountItemCollection.Items.Add(item); + } + + _owner.SendPacket(accountItemCollection); + } + + public Dictionary GetWarbandScenes() { return _warbandScenes; } + public bool HasToy(uint itemId) { return _toys.ContainsKey(itemId); } public Dictionary GetAccountToys() { return _toys; } public Dictionary GetAccountHeirlooms() { return _heirlooms; } public Dictionary GetAccountMounts() { return _mounts; } } - enum FavoriteAppearanceState + public enum CollectionItemState { + Unchanged, New, - Removed, - Unchanged + Changed, + Removed + } + + public enum WarbandSceneCollectionFlags + { + None = 0x00, + Favorite = 0x01, + HasFanfare = 0x02 } public class HeirloomData @@ -946,4 +1109,10 @@ namespace Game.Entities bonusId = _bonusId; } } + + public class WarbandSceneCollectionItem + { + public WarbandSceneCollectionFlags Flags; + public CollectionItemState State; + } } diff --git a/Source/Game/Entities/Player/Player.DB.cs b/Source/Game/Entities/Player/Player.DB.cs index 5c0ef8675..1982cefc9 100644 --- a/Source/Game/Entities/Player/Player.DB.cs +++ b/Source/Game/Entities/Player/Player.DB.cs @@ -3470,6 +3470,7 @@ namespace Game.Entities GetSession().GetCollectionMgr().LoadMounts(); GetSession().GetCollectionMgr().LoadItemAppearances(); GetSession().GetCollectionMgr().LoadTransmogIllusions(); + GetSession().GetCollectionMgr().LoadWarbandScenes(); LearnSpecializationSpells(); @@ -4111,6 +4112,7 @@ namespace Game.Entities GetSession().GetCollectionMgr().SaveAccountMounts(loginTransaction); GetSession().GetCollectionMgr().SaveAccountItemAppearances(loginTransaction); GetSession().GetCollectionMgr().SaveAccountTransmogIllusions(loginTransaction); + GetSession().GetCollectionMgr().SaveAccountWarbandScenes(loginTransaction); var currentRealmId = Global.RealmMgr.GetCurrentRealmId(); diff --git a/Source/Game/Entities/Player/Player.cs b/Source/Game/Entities/Player/Player.cs index 80299fff1..32445c70a 100644 --- a/Source/Game/Entities/Player/Player.cs +++ b/Source/Game/Entities/Player/Player.cs @@ -5735,6 +5735,12 @@ namespace Game.Entities GetSession().GetCollectionMgr().SendFavoriteAppearances(); + // SMSG_ACCOUNT_WARBAND_SCENE_UPDATE + AccountWarbandSceneUpdate warbandSceneUpdate = new(); + warbandSceneUpdate.IsFullUpdate = true; + warbandSceneUpdate.WarbandScenes = GetSession().GetCollectionMgr().GetWarbandScenes(); + SendPacket(warbandSceneUpdate); + InitialSetup initialSetup = new(); initialSetup.ServerExpansionLevel = (byte)WorldConfig.GetIntValue(WorldCfg.Expansion); SendPacket(initialSetup); @@ -7832,6 +7838,9 @@ namespace Game.Entities public void AddIllusionBlock(uint blockValue) { AddDynamicUpdateFieldValue(m_values.ModifyValue(m_activePlayerData).ModifyValue(m_activePlayerData.TransmogIllusions), blockValue); } public void AddIllusionFlag(int slot, uint flag) { SetUpdateFieldFlagValue(m_values.ModifyValue(m_activePlayerData).ModifyValue(m_activePlayerData.TransmogIllusions, slot), flag); } + public void AddWarbandScenesBlock(uint blockValue) { AddDynamicUpdateFieldValue(m_values.ModifyValue(m_activePlayerData).ModifyValue(m_activePlayerData.WarbandScenes), blockValue); } + public void AddWarbandScenesFlag(int slot, uint flag) { SetUpdateFieldFlagValue(m_values.ModifyValue(m_activePlayerData).ModifyValue(m_activePlayerData.WarbandScenes, slot), flag); } + public void AddSelfResSpell(uint spellId) { AddDynamicUpdateFieldValue(m_values.ModifyValue(m_activePlayerData).ModifyValue(m_activePlayerData.SelfResSpells), spellId); } public void RemoveSelfResSpell(uint spellId) { diff --git a/Source/Game/Handlers/CharacterHandler.cs b/Source/Game/Handlers/CharacterHandler.cs index 88aeef13e..6fb98319a 100644 --- a/Source/Game/Handlers/CharacterHandler.cs +++ b/Source/Game/Handlers/CharacterHandler.cs @@ -123,6 +123,9 @@ namespace Game } SendPacket(charResult); + + if (!charResult.IsDeletedCharacters) + _collectionMgr.SendWarbandSceneCollectionData(); } [WorldPacketHandler(ClientOpcodes.EnumCharactersDeletedByClient, Status = SessionStatus.Authed)] diff --git a/Source/Game/Handlers/CollectionsHandler.cs b/Source/Game/Handlers/CollectionsHandler.cs index 3d41c8d3e..284a42250 100644 --- a/Source/Game/Handlers/CollectionsHandler.cs +++ b/Source/Game/Handlers/CollectionsHandler.cs @@ -27,7 +27,9 @@ namespace Game break; } case ItemCollectionType.TransmogSetFavorite: + break; case ItemCollectionType.WarbandScene: + GetCollectionMgr().SetWarbandSceneIsFavorite(collectionItemSetFavorite.Id, collectionItemSetFavorite.IsFavorite); break; default: break; diff --git a/Source/Game/Networking/Packets/CollectionPackets.cs b/Source/Game/Networking/Packets/CollectionPackets.cs index e83c3ff9e..8d5cb2495 100644 --- a/Source/Game/Networking/Packets/CollectionPackets.cs +++ b/Source/Game/Networking/Packets/CollectionPackets.cs @@ -2,6 +2,7 @@ // Licensed under the GNU GENERAL PUBLIC LICENSE. See LICENSE file in the project root for full license information. using Framework.Constants; +using System.Collections.Generic; namespace Game.Networking.Packets { @@ -20,4 +21,43 @@ namespace Game.Networking.Packets public uint Id; public bool IsFavorite; } + + class AccountItemCollectionData : ServerPacket + { + public AccountItemCollectionData() : base(ServerOpcodes.AccountItemCollectionData) { } + + public override void Write() + { + _worldPacket.WriteUInt32(Unknown1110_1); + _worldPacket.WriteUInt8((byte)Type); + _worldPacket.WriteInt32(Items.Count); + + foreach (ItemCollectionItemData item in Items) + item.Write(_worldPacket); + + _worldPacket.WriteBit(Unknown1110_2); + _worldPacket.FlushBits(); + } + + public uint Unknown1110_1; + public ItemCollectionType Type; + public bool Unknown1110_2; + public List Items = new(); + } + + struct ItemCollectionItemData + { + public int Id; + public ItemCollectionType Type; + public long Unknown1110; + public int Flags; + + public void Write(WorldPacket packet) + { + packet.WriteInt32(Id); + packet.WriteUInt8((byte)Type); + packet.WriteInt64(Unknown1110); + packet.WriteInt32(Flags); + } + } } \ No newline at end of file diff --git a/Source/Game/Networking/Packets/MiscPackets.cs b/Source/Game/Networking/Packets/MiscPackets.cs index 2fbf30e28..befc8eb50 100644 --- a/Source/Game/Networking/Packets/MiscPackets.cs +++ b/Source/Game/Networking/Packets/MiscPackets.cs @@ -1484,6 +1484,33 @@ namespace Game.Networking.Packets public ObjectGuid SourceGuid; } + class AccountWarbandSceneUpdate : ServerPacket + { + public bool IsFullUpdate; + public Dictionary WarbandScenes; + + public AccountWarbandSceneUpdate() : base(ServerOpcodes.AccountWarbandSceneUpdate) { } + + public override void Write() + { + _worldPacket.WriteBit(IsFullUpdate); + _worldPacket.WriteInt32(WarbandScenes.Count); + _worldPacket.WriteInt32(WarbandScenes.Count); + _worldPacket.WriteInt32(WarbandScenes.Count); + + foreach (var (warbandSceneId, _) in WarbandScenes) + _worldPacket.WriteUInt32(warbandSceneId); + + foreach (var (_, data) in WarbandScenes) + _worldPacket.WriteBit(data.Flags.HasFlag(WarbandSceneCollectionFlags.Favorite)); + + foreach (var (_, data) in WarbandScenes) + _worldPacket.WriteBit(data.Flags.HasFlag(WarbandSceneCollectionFlags.HasFanfare)); + + _worldPacket.FlushBits(); + } + } + //Structs struct PhaseShiftDataPhase { diff --git a/Source/Game/Server/WorldSession.cs b/Source/Game/Server/WorldSession.cs index 796686368..ebcff6430 100644 --- a/Source/Game/Server/WorldSession.cs +++ b/Source/Game/Server/WorldSession.cs @@ -805,6 +805,7 @@ namespace Game _collectionMgr.LoadAccountMounts(holder.GetResult(AccountInfoQueryLoad.Mounts)); _collectionMgr.LoadAccountItemAppearances(holder.GetResult(AccountInfoQueryLoad.ItemAppearances), holder.GetResult(AccountInfoQueryLoad.ItemFavoriteAppearances)); _collectionMgr.LoadAccountTransmogIllusions(holder.GetResult(AccountInfoQueryLoad.TransmogIllusions)); + _collectionMgr.LoadAccountWarbandScenes(holder.GetResult(AccountInfoQueryLoad.WarbandScenes)); if (!m_inQueue) SendAuthResponse(BattlenetRpcErrorCode.Ok, false); @@ -1173,6 +1174,10 @@ namespace Game stmt = LoginDatabase.GetPreparedStatement(LoginStatements.SEL_BNET_TRANSMOG_ILLUSIONS); stmt.AddValue(0, battlenetAccountId); SetQuery(AccountInfoQueryLoad.TransmogIllusions, stmt); + + stmt = LoginDatabase.GetPreparedStatement(LoginStatements.SEL_BNET_WARBAND_SCENES); + stmt.AddValue(0, battlenetAccountId); + SetQuery(AccountInfoQueryLoad.WarbandScenes, stmt); } } @@ -1189,5 +1194,6 @@ namespace Game GlobalAccountDataIndexPerRealm, TutorialsIndexPerRealm, TransmogIllusions, + WarbandScenes } } diff --git a/Source/Game/Spells/SpellEffects.cs b/Source/Game/Spells/SpellEffects.cs index 5bf393f3d..b85c536b3 100644 --- a/Source/Game/Spells/SpellEffects.cs +++ b/Source/Game/Spells/SpellEffects.cs @@ -5933,6 +5933,19 @@ namespace Game.Spells target.UpdateVisibleObjectInteractions(true, false, true, true); } + + [SpellEffectHandler(SpellEffectName.LearnWarbanScene)] + void EffectLearnWarbandScene() + { + if (effectHandleMode != SpellEffectHandleMode.HitTarget) + return; + + Player target = unitTarget?.ToPlayer(); + if (target == null) + return; + + target.GetSession().GetCollectionMgr().AddWarbandScene((uint)effectInfo.MiscValue); + } } public class DispelableAura diff --git a/Source/Game/World/WorldManager.cs b/Source/Game/World/WorldManager.cs index 70513527f..2b15e6e15 100644 --- a/Source/Game/World/WorldManager.cs +++ b/Source/Game/World/WorldManager.cs @@ -988,6 +988,9 @@ namespace Game Log.outInfo(LogFilter.ServerLoading, "Loading mount definitions..."); CollectionMgr.LoadMountDefinitions(); + Log.outInfo(LogFilter.ServerLoading, "Loading warband scene definitions..."); + CollectionMgr.LoadWarbandSceneDefinitions(); + Log.outInfo(LogFilter.ServerLoading, "Loading GM bugs..."); Global.SupportMgr.LoadBugTickets();