diff --git a/S1API.Tests/NPCs/NPCPrefabDataTests.cs b/S1API.Tests/NPCs/NPCPrefabDataTests.cs new file mode 100644 index 00000000..8e4d1b83 --- /dev/null +++ b/S1API.Tests/NPCs/NPCPrefabDataTests.cs @@ -0,0 +1,20 @@ +using S1API.Entities; +using S1API.Internal.Entities; + +namespace S1API.Tests.NPCs; + +public sealed class NPCPrefabDataTests +{ + [Fact] + public void ADonorThatIsAlreadyADealerOrSupplierGetsDataOfItsOwnKind() + { + Assert.Equal(NpcRootRole.Dealer, NPC.DataRoleForComponent(isDealer: true, isSupplier: false)); + Assert.Equal(NpcRootRole.Supplier, NPC.DataRoleForComponent(isDealer: false, isSupplier: true)); + } + + [Fact] + public void APlainDonorGetsPlainDataWhichTheRolesOwnDataReplacesLater() + { + Assert.Equal(NpcRootRole.Plain, NPC.DataRoleForComponent(isDealer: false, isSupplier: false)); + } +} diff --git a/S1API/Entities/NPC.cs b/S1API/Entities/NPC.cs index bb6befdd..02fb1783 100644 --- a/S1API/Entities/NPC.cs +++ b/S1API/Entities/NPC.cs @@ -358,6 +358,13 @@ private static bool IsGeneratedS1ApiPrefab(GameObject prefabRoot) ?? FindSpawnablePrefabWithComponent(spawnablePrefabs, count); } + /// + /// The kind of data an NPC component without any gets before ConfigurePrefab: its own. A donor that already + /// is a dealer keeps that component, so plain data on it would never be replaced. + /// + internal static NpcRootRole DataRoleForComponent(bool isDealer, bool isSupplier) => + isDealer ? NpcRootRole.Dealer : isSupplier ? NpcRootRole.Supplier : NpcRootRole.Plain; + private static S1NPCs.NPC? FindPlainNpcComponent(GameObject prefabRoot) { if (prefabRoot == null) @@ -1018,13 +1025,16 @@ private static GameObject GetOrCreatePerNpcPrefab(System.Type npcType, NPC? owne NormalizeBaseEmployeePrefab(prefabNO.gameObject, sourcePrefabName, rootRole); prefabNO.gameObject.name = prefabName; - // Some native plain NPC prefabs have no framework data object. Attach one - // before ConfigurePrefab applies identity and other saved defaults. - if (rootRole == NpcRootRole.Plain) + // ConfigurePrefab needs data matching the donor component, before role replacement. + S1NPCs.NPC? donorNpc = rootRole == NpcRootRole.Plain + ? FindPlainNpcComponent(prefabNO.gameObject) + : GetPreferredNpcComponent(prefabNO.gameObject); + if (donorNpc != null && NPCDataAccess.GetDataObject(donorNpc) == null) { - S1NPCs.NPC? plainNpc = FindPlainNpcComponent(prefabNO.gameObject); - if (plainNpc != null && NPCDataAccess.GetDataObject(plainNpc) == null) - NPCDataAccess.AssignNewData(plainNpc, rootRole, plainNpc); + NpcRootRole dataRole = DataRoleForComponent( + CrossType.Is(donorNpc, out S1Economy.Dealer _), + CrossType.Is(donorNpc, out S1Economy.Supplier _)); + NPCDataAccess.AssignNewData(donorNpc, dataRole, donorNpc); } // Ensure template prefab does not execute runtime logic or remain in NPC registry