From 349ad1097fbfa2b22dfec3784aadc1f357a54f35 Mon Sep 17 00:00:00 2001 From: ifBars Date: Wed, 7 Oct 2026 22:39:30 -0700 Subject: [PATCH] fix(npc): prepare customer data before client spawn --- S1API/Entities/NPC.cs | 2 ++ S1API/Entities/NPCCustomer.cs | 46 ++++++++++++++++-------------- S1API/Entities/NPCPrefabBuilder.cs | 13 +++++++++ 3 files changed, 39 insertions(+), 22 deletions(-) diff --git a/S1API/Entities/NPC.cs b/S1API/Entities/NPC.cs index 02fb1783..f001d6ef 100644 --- a/S1API/Entities/NPC.cs +++ b/S1API/Entities/NPC.cs @@ -692,6 +692,8 @@ internal static int IncludeNavMeshArea(int areaMask, int areaIndex) => RewireChildNpcReferences(prefabRoot, dealer); RepairDealerPrefabReferences(prefabRoot, dealer); RepairNpcPrefabReferences(prefabRoot, dealer); + if (sourceNpc != dealer) + RemoveComponentImmediate(sourceNpc); return dealer; } diff --git a/S1API/Entities/NPCCustomer.cs b/S1API/Entities/NPCCustomer.cs index 182cd53f..803cb1d4 100644 --- a/S1API/Entities/NPCCustomer.cs +++ b/S1API/Entities/NPCCustomer.cs @@ -300,6 +300,29 @@ private void WireCoreReferences(S1Economy.Customer customer) } } + internal static S1Economy.CustomerData CreateDefaultCustomerData() + { + var data = ScriptableObject.CreateInstance(); + data.DefaultAffinityData = new S1Economy.CustomerAffinityData(); + foreach (var drugType in Enum.GetValues(typeof(S1Product.EDrugType))) + { + data.DefaultAffinityData.ProductAffinities.Add(new S1Economy.ProductTypeAffinity + { + DrugType = (S1Product.EDrugType)drugType, + Affinity = 0f + }); + } + data.MinWeeklySpend = 100f; + data.MaxWeeklySpend = 400f; + data.MinOrdersPerWeek = 1; + data.MaxOrdersPerWeek = 3; + data.OrderTime = 1200; + data.CanBeDirectlyApproached = true; + data.DependenceMultiplier = 1f; + data.BaseAddiction = 0f; + return data; + } + private void EnsureCustomerData(S1Economy.Customer customer) { try @@ -329,28 +352,7 @@ private void EnsureCustomerData(S1Economy.Customer customer) if (data == null) { - // Create a minimal, safe CustomerData so base game logic has sane defaults - data = ScriptableObject.CreateInstance(); - data.DefaultAffinityData = new S1Economy.CustomerAffinityData(); - // Seed affinities for all product types with neutral preference - Array drugTypes = Enum.GetValues(typeof(S1Product.EDrugType)); - foreach (var dt in drugTypes) - { - data.DefaultAffinityData.ProductAffinities.Add(new S1Economy.ProductTypeAffinity - { - DrugType = (S1Product.EDrugType)dt, - Affinity = 0f - }); - } - // Reasonable defaults - data.MinWeeklySpend = 100f; - data.MaxWeeklySpend = 400f; - data.MinOrdersPerWeek = 1; - data.MaxOrdersPerWeek = 3; - data.OrderTime = 1200; - data.CanBeDirectlyApproached = true; - data.DependenceMultiplier = 1f; - data.BaseAddiction = 0f; + data = CreateDefaultCustomerData(); #if MONOMELON diff --git a/S1API/Entities/NPCPrefabBuilder.cs b/S1API/Entities/NPCPrefabBuilder.cs index 3a9764aa..b8c166b6 100644 --- a/S1API/Entities/NPCPrefabBuilder.cs +++ b/S1API/Entities/NPCPrefabBuilder.cs @@ -111,6 +111,19 @@ internal NPCPrefabBuilder EnsureCustomerInfrastructure() customer = prefabRoot.AddComponent(); customer.enabled = true; } + // A joining client's native Awake runs before its managed NPC wrapper can + // create fallback data. Configure the inactive prefab before network spawn. + if (customer.CustomerData == null) + { + var data = NPCCustomer.CreateDefaultCustomerData(); +#if MONOMELON + var field = typeof(S1Economy.Customer).GetField("customerData", + BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); + field?.SetValue(customer, data); +#else + customer.customerData = data; +#endif + } return this; }