Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions S1API.Tests/NPCs/NPCPrefabDataTests.cs
Original file line number Diff line number Diff line change
@@ -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));
}
}
22 changes: 16 additions & 6 deletions S1API/Entities/NPC.cs
Original file line number Diff line number Diff line change
Expand Up @@ -358,6 +358,13 @@ private static bool IsGeneratedS1ApiPrefab(GameObject prefabRoot)
?? FindSpawnablePrefabWithComponent<S1NPCs.NPC>(spawnablePrefabs, count);
}

/// <summary>
/// The kind of data an NPC component without any gets before <c>ConfigurePrefab</c>: its own. A donor that already
/// is a dealer keeps that component, so plain data on it would never be replaced.
/// </summary>
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)
Expand Down Expand Up @@ -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
Expand Down
Loading