From 940ecad00a74ca479656cd6e51dc0c5ed2366fa7 Mon Sep 17 00:00:00 2001 From: hondacrx Date: Tue, 31 May 2022 15:25:11 -0400 Subject: [PATCH] Core/Conversations: Conversation actor improvements Port From (https://github.com/TrinityCore/TrinityCore/commit/fda65981c7a4c475c270755c198dee85cd38dfa3) --- Source/Framework/Constants/SharedConst.cs | 4 +- .../DataStorage/ConversationDataStorage.cs | 218 +++++++++++++----- Source/Game/Entities/Conversation.cs | 119 +++++++--- Source/Scripts/World/Conversation.cs | 1 - 4 files changed, 243 insertions(+), 99 deletions(-) diff --git a/Source/Framework/Constants/SharedConst.cs b/Source/Framework/Constants/SharedConst.cs index ae9a50173..2052d43e7 100644 --- a/Source/Framework/Constants/SharedConst.cs +++ b/Source/Framework/Constants/SharedConst.cs @@ -2875,8 +2875,8 @@ namespace Framework.Constants public enum ConversationActorType { - WorldObjectActor = 0, - CreatureActor = 1 + WorldObject = 0, + TalkingHead = 1 } public enum CorpseDynFlags diff --git a/Source/Game/DataStorage/ConversationDataStorage.cs b/Source/Game/DataStorage/ConversationDataStorage.cs index e4741cf65..442ad2826 100644 --- a/Source/Game/DataStorage/ConversationDataStorage.cs +++ b/Source/Game/DataStorage/ConversationDataStorage.cs @@ -32,8 +32,7 @@ namespace Game.DataStorage _conversationLineTemplateStorage.Clear(); _conversationTemplateStorage.Clear(); - Dictionary actorsByConversation = new(); - Dictionary actorGuidsByConversation = new(); + Dictionary> actorsByConversation = new(); SQLResult lineTemplates = DB.World.Query("SELECT Id, UiCameraID, ActorIdx, Flags FROM conversation_line_template"); if (!lineTemplates.IsEmpty()) @@ -67,7 +66,7 @@ namespace Game.DataStorage Log.outInfo(LogFilter.ServerLoading, "Loaded 0 Conversation line templates. DB table `conversation_line_template` is empty."); } - SQLResult actorResult = DB.World.Query("SELECT ConversationId, ConversationActorId, ConversationActorGuid, Idx, CreatureId, CreatureDisplayInfoId FROM conversation_actors"); + SQLResult actorResult = DB.World.Query("SELECT ConversationId, ConversationActorId, ConversationActorGuid, Idx, CreatureId, CreatureDisplayInfoId, NoActorObject, ActivePlayerObject FROM conversation_actors"); if (!actorResult.IsEmpty()) { uint oldMSTime = Time.GetMSTime(); @@ -75,62 +74,37 @@ namespace Game.DataStorage do { - uint conversationId = actorResult.Read(0); - uint actorId = actorResult.Read(1); - ulong actorGuid = actorResult.Read(2); - ushort idx = actorResult.Read(3); - uint creatureId = actorResult.Read(4); - uint creatureDisplayInfoId = actorResult.Read(5); + ConversationActorDbRow data; + ConversationActorTemplate actor = new(); - if (creatureId != 0 && actorGuid != 0) - { - Log.outError(LogFilter.Sql, $"Table `conversation_actors` references both actor (ID: {actorId}) and actorGuid (GUID: {actorGuid}) for Conversation {conversationId}, skipped."); + data.ConversationId = actorResult.Read(0); + data.ConversationId = actorResult.Read(1); + data.SpawnId = actorResult.Read(2); + data.ActorIndex = actor.Index = actorResult.Read(3); + data.CreatureId = actorResult.Read(4); + data.CreatureDisplayInfoId = actorResult.Read(5); + bool noActorObject = actorResult.Read(6) == 1; + bool activePlayerObject = actorResult.Read(7) == 1; + + if (activePlayerObject) + actor.ActivePlayerTemplate = new(); + else if (noActorObject) + actor.NoObjectTemplate = new(); + else if (data.SpawnId != 0) + actor.WorldObjectTemplate = new(); + else + actor.TalkingHeadTemplate = new(); + + bool valid = data.Invoke(actor); + if (!valid) continue; - } - if (creatureId != 0) - { - if (creatureDisplayInfoId != 0) - { - ConversationActor conversationActor = new(); - conversationActor.ActorId = actorId; - conversationActor.CreatureId = creatureId; - conversationActor.CreatureDisplayInfoId = creatureDisplayInfoId; + if (!actorsByConversation.ContainsKey(data.ConversationId)) + actorsByConversation[data.ConversationId] = new(); - if (!actorsByConversation.ContainsKey(conversationId)) - actorsByConversation[conversationId] = new ConversationActor[idx + 1]; - - ConversationActor[] actors = actorsByConversation[conversationId]; - if (actors.Length <= idx) - Array.Resize(ref actors, idx + 1); - - actors[idx] = conversationActor; - ++count; - } - else - Log.outError(LogFilter.Sql, $"Table `conversation_actors` references an actor (CreatureId: {creatureId}) without CreatureDisplayInfoId for Conversation {conversationId}, skipped"); - } - else if (actorGuid != 0) - { - CreatureData creData = Global.ObjectMgr.GetCreatureData(actorGuid); - if (creData != null) - { - if (!actorGuidsByConversation.ContainsKey(conversationId)) - actorGuidsByConversation[conversationId] = new ulong[idx + 1]; - - var guids = actorGuidsByConversation[conversationId]; - if (guids.Length <= idx) - Array.Resize(ref guids, idx + 1); - - guids[idx] = actorGuid; - ++count; - } - else - Log.outError(LogFilter.Sql, $"Table `conversation_actors` references an invalid creature guid (GUID: {actorGuid}) for Conversation {conversationId}, skipped"); - } - - } - while (actorResult.NextRow()); + actorsByConversation[data.ConversationId].Add(actor); + ++count; + } while (actorResult.NextRow()); Log.outInfo(LogFilter.ServerLoading, "Loaded {0} Conversation actors in {1} ms", count, Time.GetMSTimeDiffToNow(oldMSTime)); } @@ -153,7 +127,6 @@ namespace Game.DataStorage conversationTemplate.ScriptId = Global.ObjectMgr.GetScriptId(templateResult.Read(3)); conversationTemplate.Actors = actorsByConversation.TryGetValue(conversationTemplate.Id, out var actors) ? actors.ToList() : null; - conversationTemplate.ActorGuids = actorGuidsByConversation.TryGetValue(conversationTemplate.Id, out var actorGuids) ? actorGuids.ToList() : null; ConversationLineRecord currentConversationLine = CliDB.ConversationLineStorage.LookupByKey(conversationTemplate.FirstLineId); if (currentConversationLine == null) @@ -197,22 +170,146 @@ namespace Game.DataStorage Dictionary _conversationTemplateStorage = new(); Dictionary _conversationLineTemplateStorage = new(); + + struct ConversationActorDbRow + { + public uint ConversationId; + public uint ActorIndex; + + public ulong SpawnId; + public uint CreatureId; + public uint CreatureDisplayInfoId; + + public bool Invoke(ConversationActorTemplate template) + { + if (template.WorldObjectTemplate == null) + return Invoke(template.WorldObjectTemplate); + + if (template.NoObjectTemplate == null) + return Invoke(template.NoObjectTemplate); + + if (template.ActivePlayerTemplate == null) + return Invoke(template.ActivePlayerTemplate); + + if (template.TalkingHeadTemplate == null) + return Invoke(template.TalkingHeadTemplate); + + return false; + } + + public bool Invoke(ConversationActorWorldObjectTemplate worldObject) + { + if (Global.ObjectMgr.GetCreatureData(SpawnId) == null) + { + Log.outError(LogFilter.Sql, $"Table `conversation_actors` references an invalid creature guid (GUID: {SpawnId}) for Conversation {ConversationId} and Idx {ActorIndex}, skipped."); + return false; + } + + if (CreatureId != 0) + Log.outError(LogFilter.Sql, $"Table `conversation_actors` with ConversationActorGuid cannot have CreatureId ({CreatureId}). Conversation {ConversationId} and Idx {ActorIndex}."); + + if (CreatureDisplayInfoId != 0) + Log.outError(LogFilter.Sql, $"Table `conversation_actors` with ConversationActorGuid cannot have CreatureDisplayInfoId ({CreatureDisplayInfoId}). Conversation {ConversationId} and Idx {ActorIndex}."); + + worldObject.SpawnId = SpawnId; + return true; + } + + public bool Invoke(ConversationActorNoObjectTemplate noObject) + { + if (Global.ObjectMgr.GetCreatureTemplate(CreatureId) == null) + { + Log.outError(LogFilter.Sql, $"Table `conversation_actors` references an invalid creature id ({CreatureId}) for Conversation {ConversationId} and Idx {ActorIndex}, skipped."); + return false; + } + if (CreatureDisplayInfoId != 0 && !CliDB.CreatureDisplayInfoStorage.ContainsKey(CreatureDisplayInfoId)) + { + Log.outError(LogFilter.Sql, $"Table `conversation_actors` references an invalid creature display id ({CreatureDisplayInfoId}) for Conversation {ConversationId} and Idx {ActorIndex}, skipped."); + return false; + } + + if (SpawnId != 0) + Log.outError(LogFilter.Sql, $"Table `conversation_actors` with NoActorObject cannot have ConversationActorGuid ({SpawnId}). Conversation {ConversationId} and Idx {ActorIndex}."); + + noObject.CreatureId = CreatureId; + noObject.CreatureDisplayInfoId = CreatureDisplayInfoId; + return true; + } + + public bool Invoke(ConversationActorActivePlayerTemplate activePlayer) + { + if (SpawnId != 0) + Log.outError(LogFilter.Sql, $"Table `conversation_actors` with ActivePlayerObject cannot have ConversationActorGuid ({SpawnId}). Conversation {ConversationId} and Idx {ActorIndex}."); + + if (CreatureId != 0) + Log.outError(LogFilter.Sql, $"Table `conversation_actors` with ActivePlayerObject cannot have CreatureId ({CreatureId}). Conversation {ConversationId} and Idx {ActorIndex}."); + + if (CreatureDisplayInfoId != 0) + Log.outError(LogFilter.Sql, $"Table `conversation_actors` with ActivePlayerObject cannot have CreatureDisplayInfoId ({CreatureDisplayInfoId}). Conversation {ConversationId} and Idx {ActorIndex}."); + + return true; + } + + public bool Invoke(ConversationActorTalkingHeadTemplate talkingHead) + { + if (Global.ObjectMgr.GetCreatureTemplate(CreatureId) == null) + { + Log.outError(LogFilter.Sql, $"Table `conversation_actors` references an invalid creature id ({CreatureId}) for Conversation {ConversationId} and Idx {ActorIndex}, skipped."); + return false; + } + if (CreatureDisplayInfoId != 0 && !CliDB.CreatureDisplayInfoStorage.ContainsKey(CreatureDisplayInfoId)) + { + Log.outError(LogFilter.Sql, $"Table `conversation_actors` references an invalid creature display id ({CreatureDisplayInfoId}) for Conversation {ConversationId} and Idx {ActorIndex}, skipped."); + return false; + } + + if (SpawnId != 0) + Log.outError(LogFilter.Sql, $"Table `conversation_actors` with TalkingHead cannot have ConversationActorGuid ({SpawnId}). Conversation {ConversationId} and Idx {ActorIndex}."); + + talkingHead.CreatureId = CreatureId; + talkingHead.CreatureDisplayInfoId = CreatureDisplayInfoId; + return true; + } + } } - public class ConversationActor + public class ConversationActorWorldObjectTemplate + { + public ulong SpawnId; + } + + public class ConversationActorNoObjectTemplate { - public uint ActorId; public uint CreatureId; public uint CreatureDisplayInfoId; } + public class ConversationActorActivePlayerTemplate + { + } + + public class ConversationActorTalkingHeadTemplate + { + public uint CreatureId; + public uint CreatureDisplayInfoId; + } + + public struct ConversationActorTemplate + { + public int Id; + public uint Index; + public ConversationActorWorldObjectTemplate WorldObjectTemplate; + public ConversationActorNoObjectTemplate NoObjectTemplate; + public ConversationActorActivePlayerTemplate ActivePlayerTemplate; + public ConversationActorTalkingHeadTemplate TalkingHeadTemplate; + } + public class ConversationLineTemplate { public uint Id; // Link to ConversationLine.db2 public uint UiCameraID; // Link to UiCamera.db2 public byte ActorIdx; // Index from conversation_actors public byte Flags; - public ushort Padding; } public class ConversationTemplate @@ -222,8 +319,7 @@ namespace Game.DataStorage public uint TextureKitId; // Background texture public uint ScriptId; - public List Actors = new(); - public List ActorGuids = new(); + public List Actors = new(); public List Lines = new(); } diff --git a/Source/Game/Entities/Conversation.cs b/Source/Game/Entities/Conversation.cs index 078dedd04..a6af0266d 100644 --- a/Source/Game/Entities/Conversation.cs +++ b/Source/Game/Entities/Conversation.cs @@ -124,38 +124,8 @@ namespace Game.Entities _textureKitId = conversationTemplate.TextureKitId; - if (conversationTemplate.Actors != null) - { - foreach (var actor in conversationTemplate.Actors) - { - if (actor != null) - { - ConversationActorField actorField = new(); - actorField.CreatureID = actor.CreatureId; - actorField.CreatureDisplayInfoID = actor.CreatureDisplayInfoId; - actorField.Id = (int)actor.ActorId; - actorField.Type = ConversationActorType.CreatureActor; - - AddDynamicUpdateFieldValue(m_values.ModifyValue(m_conversationData).ModifyValue(m_conversationData.Actors), actorField); - } - } - } - - if (conversationTemplate.ActorGuids != null) - { - for (ushort actorIndex = 0; actorIndex < conversationTemplate.ActorGuids.Count; ++actorIndex) - { - ulong actorGuid = conversationTemplate.ActorGuids[actorIndex]; - if (actorGuid == 0) - continue; - - foreach (var creature in map.GetCreatureBySpawnIdStore().LookupByKey(actorGuid)) - { - // we just need the last one, overriding is legit - AddActor(creature.GetGUID(), actorIndex); - } - } - } + foreach (var actor in conversationTemplate.Actors) + new ConversationActorFillVisitor(this, creator, map, actor).Invoke(actor); Global.ScriptMgr.OnConversationCreate(this, creator); @@ -221,11 +191,26 @@ namespace Game.Entities return true; } - public void AddActor(ObjectGuid actorGuid, ushort actorIdx) + public void AddActor(int actorId, uint actorIdx, ObjectGuid actorGuid) { - ConversationActorField actorField = m_values.ModifyValue(m_conversationData).ModifyValue(m_conversationData.Actors, actorIdx); + ConversationActorField actorField = m_values.ModifyValue(m_conversationData).ModifyValue(m_conversationData.Actors, (int)actorIdx); + SetUpdateFieldValue(ref actorField.CreatureID, 0u); + SetUpdateFieldValue(ref actorField.CreatureDisplayInfoID, 0u); SetUpdateFieldValue(ref actorField.ActorGUID, actorGuid); - SetUpdateFieldValue(ref actorField.Type, ConversationActorType.WorldObjectActor); + SetUpdateFieldValue(ref actorField.Id, actorId); + SetUpdateFieldValue(ref actorField.Type, ConversationActorType.WorldObject); + SetUpdateFieldValue(ref actorField.NoActorObject, 0u); + } + + public void AddActor(int actorId, uint actorIdx, ConversationActorType type, uint creatureId, uint creatureDisplayInfoId) + { + ConversationActorField actorField = m_values.ModifyValue(m_conversationData).ModifyValue(m_conversationData.Actors, (int)actorIdx); + SetUpdateFieldValue(ref actorField.CreatureID, creatureId); + SetUpdateFieldValue(ref actorField.CreatureDisplayInfoID, creatureDisplayInfoId); + SetUpdateFieldValue(ref actorField.ActorGUID, ObjectGuid.Empty); + SetUpdateFieldValue(ref actorField.Id, actorId); + SetUpdateFieldValue(ref actorField.Type, type); + SetUpdateFieldValue(ref actorField.NoActorObject, type == ConversationActorType.WorldObject ? 1 : 0u); } public TimeSpan GetLineStartTime(Locale locale, int lineId) @@ -350,4 +335,68 @@ namespace Game.Entities } } } + + class ConversationActorFillVisitor + { + Conversation _conversation; + Unit _creator; + Map _map; + ConversationActorTemplate _actor; + + public ConversationActorFillVisitor(Conversation conversation, Unit creator, Map map, ConversationActorTemplate actor) + { + _conversation = conversation; + _creator = creator; + _map = map; + _actor = actor; + + } + + public void Invoke(ConversationActorTemplate template) + { + if (template.WorldObjectTemplate == null) + Invoke(template.WorldObjectTemplate); + + if (template.NoObjectTemplate == null) + Invoke(template.NoObjectTemplate); + + if (template.ActivePlayerTemplate == null) + Invoke(template.ActivePlayerTemplate); + + if (template.TalkingHeadTemplate == null) + Invoke(template.TalkingHeadTemplate); + } + + public void Invoke(ConversationActorWorldObjectTemplate worldObject) + { + Creature bestFit = null; + + foreach (var creature in _map.GetCreatureBySpawnIdStore().LookupByKey(worldObject.SpawnId)) + { + bestFit = creature; + + // If creature is in a personal phase then we pick that one specifically + if (creature.GetPhaseShift().GetPersonalGuid() == _creator.GetGUID()) + break; + } + + if (bestFit) + _conversation.AddActor(_actor.Id, _actor.Index, bestFit.GetGUID()); + } + + public void Invoke(ConversationActorNoObjectTemplate noObject) + { + _conversation.AddActor(_actor.Id, _actor.Index, ConversationActorType.WorldObject, noObject.CreatureId, noObject.CreatureDisplayInfoId); + } + + public void Invoke(ConversationActorActivePlayerTemplate activePlayer) + { + _conversation.AddActor(_actor.Id, _actor.Index, ObjectGuid.Create(HighGuid.Player, 0xFFFFFFFFFFFFFFFF)); + } + + public void Invoke(ConversationActorTalkingHeadTemplate talkingHead) + { + _conversation.AddActor(_actor.Id, _actor.Index, ConversationActorType.TalkingHead, talkingHead.CreatureId, talkingHead.CreatureDisplayInfoId); + } + } } diff --git a/Source/Scripts/World/Conversation.cs b/Source/Scripts/World/Conversation.cs index 11134a0ed..d2e2a8173 100644 --- a/Source/Scripts/World/Conversation.cs +++ b/Source/Scripts/World/Conversation.cs @@ -32,7 +32,6 @@ namespace Scripts.World public override void OnConversationCreate(Conversation conversation, Unit creator) { - conversation.AddActor(ObjectGuid.Create(HighGuid.Player, 0xFFFFFFFFFFFFFFFF), 1); Player player = creator.ToPlayer(); if (player != null) player.KilledMonsterCredit(NpcTalkToYourCommanderCredit);