diff --git a/S1API/Entities/NPC.cs b/S1API/Entities/NPC.cs index 7c5d2cf3..2e6ba067 100644 --- a/S1API/Entities/NPC.cs +++ b/S1API/Entities/NPC.cs @@ -28,6 +28,7 @@ using S1Registry = Il2CppScheduleOne.Registry; using S1Money = Il2CppScheduleOne.Money; using ConversationCategoryList = Il2CppSystem.Collections.Generic.List; +using S1MessageConversationData = Il2CppScheduleOne.Persistence.Datas.MSGConversationData; #elif MONOMELON using NativeVehicleLifecycleAction = System.Action; using S1DevUtilities = ScheduleOne.DevUtilities; @@ -58,6 +59,7 @@ using S1Registry = ScheduleOne.Registry; using S1Money = ScheduleOne.Money; using ConversationCategoryList = System.Collections.Generic.List; +using S1MessageConversationData = ScheduleOne.Persistence.Datas.MSGConversationData; #endif #if IL2CPPMELON @@ -4308,6 +4310,7 @@ internal static bool TrySetRequiresRegionUnlocked(object npc, bool value) => private NPCSupplier? _supplier; private NPCRelationship? _relationship; private NPCMessaging? _messaging; + private S1MessageConversationData? _conversationBeforeNetworkSpawn; private ManagedEventRegistrationTracker>? _noticedDrugDealingRegistrations; private ManagedEventRegistrationTracker>? @@ -4360,6 +4363,7 @@ private void TrySpawnNetworkInstance() internal bool PrepareForNetworkSpawn() { + _conversationBeforeNetworkSpawn = null; try { if (!TryValidateNativeAwakeReferences(out string diagnostic)) @@ -4369,6 +4373,8 @@ internal bool PrepareForNetworkSpawn() return false; } + PreserveConversationBeforeNativeAwake(); + NPCDataAccess.PrepareForRuntime(S1NPC); RestoreLoadedRelationship(); @@ -4397,6 +4403,22 @@ internal bool PrepareForNetworkSpawn() } } + internal void PreserveConversationBeforeNativeAwake() + { + var conversation = S1NPC.MSGConversation; + if (_conversationBeforeNetworkSpawn != null || conversation == null) + return; + + try + { + _conversationBeforeNetworkSpawn = conversation.GetSaveData(); + } + catch (Exception ex) + { + Logger.Warning($"[NPC] Could not preserve conversation before spawning '{GetSafeNpcId()}': {ex.Message}"); + } + } + private bool TryValidateNativeAwakeReferences(out string diagnostic) { var missing = new System.Collections.Generic.List(); @@ -4463,6 +4485,19 @@ internal void FinalizeNetworkSpawn() S1NPC.SetVisible(ShouldBeVisibleAfterSpawn(), networked: false); EnsureMessageConversationReady(resetDefaults: false); + var savedConversation = _conversationBeforeNetworkSpawn; + _conversationBeforeNetworkSpawn = null; + if (savedConversation != null && S1NPC.MSGConversation != null && S1NPC.MSGConversation.MessageHistoryCount == 0) + { + try + { + S1NPC.MSGConversation.Load(savedConversation); + } + catch (Exception ex) + { + Logger.Warning($"[NPC] Could not restore conversation after spawning '{GetSafeNpcId()}': {ex.Message}"); + } + } // If we're the server, also broadcast to clients via RPC after a delay // This ensures the NPC is fully spawned before the RPC is sent diff --git a/S1API/Internal/Patches/NPCPatches.cs b/S1API/Internal/Patches/NPCPatches.cs index 67924667..1fbf9cd8 100644 --- a/S1API/Internal/Patches/NPCPatches.cs +++ b/S1API/Internal/Patches/NPCPatches.cs @@ -1160,7 +1160,10 @@ private static bool NPC_Awake_Prefix(S1NPCs.NPC __instance) } if (isCustomNpc) + { + FindWrapperForS1Npc(__instance)?.PreserveConversationBeforeNativeAwake(); NPCDataAccess.PrepareForNativeAwake(__instance!); + } } catch (Exception ex) {