replace the NPC scaffold with data-driven prefabs, and retire the builders

NPCStandIn was written to be dropped on a sphere and carried its identity in
per-instance public fields. The cost of that showed up in the scene itself: of
the two spheres, one was the Bartender and the other was named "Bartender (1)"
in the hierarchy while being configured as the Chair — data changed, name never
was. Nothing outside the scene could see what existed, and two copies could
disagree.

Identity now lives in an InteractableDefinition asset and the scene only places
a prefab. The migration matched the old objects by yarn node rather than by
name, which is the only reason the chair ended up as the chair.

InteractableDefinition rather than the planned "character definition": it covers
a character and a talkable object equally, so Bartender.prefab sits under
Prefabs/Characters and Chair.prefab under Prefabs/Interactables.

Three components collapse into one. DialogueInteractable was in no scene at all
— a dead trigger-zone variant of the same idea. ClickInteractableBridge existed
only to adapt NPCStandIn without modifying it, a constraint that died with
NPCStandIn; DialogueInteractor implements IClickInteractable itself. The parts
worth keeping were lifted out rather than discarded: WorldSpaceLabel and
InteractionPrompt are now normal components on prefab children, so a label can
be seen and positioned in the Editor instead of existing only at runtime.
InputDeviceTracker moved into NightclubArcadia.Core and gained a namespace,
leaving no global-namespace runtime types.

Characters get the same pipeline as Skills and Candidates:
Assets/Characters/characters.json is the source of truth, CharacterSetup
regenerates the definition assets on script reload and removes orphans. Adding
an NPC is a JSON entry plus a prefab — no code, and no Unity for the data half.

Phase 5.4 deletes the one-shot builders rather than disabling them. UILayerSetup,
PlayerControlSetup, UILayerSetupAutoRun and YarnDemoSetup assumed the
single-scene layout, had already destroyed a level scene and turned Systems
binary once each, and after NPCStandIn went they no longer compiled. The prefabs
they used to generate are authored and committed; the wiring knowledge is in
those prefabs and in git history. The asset generators — Skill, Candidate,
Character — are untouched and still work.

EditMode 42/42, PlayMode 7/7, YarnCheck 9 files / 35 nodes. All three scenes
still text.

Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
2026-08-25 21:53:06 +02:00
co-authored by Claude Opus 5
parent ec4619d9dc
commit 9a0618fd31
54 changed files with 2292 additions and 2195 deletions
@@ -119,6 +119,78 @@ NavMeshSettings:
debug:
m_Flags: 0
m_NavMeshData: {fileID: 0}
--- !u!1001 &16398773
PrefabInstance:
m_ObjectHideFlags: 0
serializedVersion: 2
m_Modification:
serializedVersion: 3
m_TransformParent: {fileID: 91390000}
m_Modifications:
- target: {fileID: 7280501095255275029, guid: 5f94d8e9cc93d47deb267073aebce344, type: 3}
propertyPath: m_Name
value: Chair
objectReference: {fileID: 0}
- target: {fileID: 7280501095255275029, guid: 5f94d8e9cc93d47deb267073aebce344, type: 3}
propertyPath: m_Layer
value: 6
objectReference: {fileID: 0}
- target: {fileID: 8926026372035049776, guid: 5f94d8e9cc93d47deb267073aebce344, type: 3}
propertyPath: m_LocalPosition.x
value: 2.5
objectReference: {fileID: 0}
- target: {fileID: 8926026372035049776, guid: 5f94d8e9cc93d47deb267073aebce344, type: 3}
propertyPath: m_LocalPosition.y
value: 2
objectReference: {fileID: 0}
- target: {fileID: 8926026372035049776, guid: 5f94d8e9cc93d47deb267073aebce344, type: 3}
propertyPath: m_LocalPosition.z
value: 2
objectReference: {fileID: 0}
- target: {fileID: 8926026372035049776, guid: 5f94d8e9cc93d47deb267073aebce344, type: 3}
propertyPath: m_LocalRotation.w
value: 1
objectReference: {fileID: 0}
- target: {fileID: 8926026372035049776, guid: 5f94d8e9cc93d47deb267073aebce344, type: 3}
propertyPath: m_LocalRotation.x
value: -0
objectReference: {fileID: 0}
- target: {fileID: 8926026372035049776, guid: 5f94d8e9cc93d47deb267073aebce344, type: 3}
propertyPath: m_LocalRotation.y
value: -0
objectReference: {fileID: 0}
- target: {fileID: 8926026372035049776, guid: 5f94d8e9cc93d47deb267073aebce344, type: 3}
propertyPath: m_LocalRotation.z
value: -0
objectReference: {fileID: 0}
- target: {fileID: 8926026372035049776, guid: 5f94d8e9cc93d47deb267073aebce344, type: 3}
propertyPath: m_LocalEulerAnglesHint.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 8926026372035049776, guid: 5f94d8e9cc93d47deb267073aebce344, type: 3}
propertyPath: m_LocalEulerAnglesHint.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 8926026372035049776, guid: 5f94d8e9cc93d47deb267073aebce344, type: 3}
propertyPath: m_LocalEulerAnglesHint.z
value: 0
objectReference: {fileID: 0}
m_RemovedComponents: []
m_RemovedGameObjects: []
m_AddedGameObjects: []
m_AddedComponents:
- targetCorrespondingSourceObject: {fileID: 1028094190342798046, guid: 5f94d8e9cc93d47deb267073aebce344, type: 3}
insertIndex: -1
addedObject: {fileID: 0}
- targetCorrespondingSourceObject: {fileID: 777994819892892268, guid: 5f94d8e9cc93d47deb267073aebce344, type: 3}
insertIndex: -1
addedObject: {fileID: 0}
m_SourcePrefab: {fileID: 100100000, guid: 5f94d8e9cc93d47deb267073aebce344, type: 3}
--- !u!4 &16398774 stripped
Transform:
m_CorrespondingSourceObject: {fileID: 8926026372035049776, guid: 5f94d8e9cc93d47deb267073aebce344, type: 3}
m_PrefabInstance: {fileID: 16398773}
m_PrefabAsset: {fileID: 0}
--- !u!1 &91389999
GameObject:
m_ObjectHideFlags: 0
@@ -148,9 +220,9 @@ Transform:
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children:
- {fileID: 848933010}
- {fileID: 1138462137}
- {fileID: 798705542}
- {fileID: 897841317}
- {fileID: 16398774}
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &545208166
@@ -504,342 +576,78 @@ Transform:
- {fileID: 1988869524}
m_Father: {fileID: 91390000}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!1 &848933009
GameObject:
--- !u!1001 &897841316
PrefabInstance:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 848933010}
- component: {fileID: 848933013}
- component: {fileID: 848933012}
- component: {fileID: 848933011}
- component: {fileID: 848933014}
- component: {fileID: 848933016}
- component: {fileID: 848933015}
m_Layer: 6
m_Name: Bartender
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &848933010
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 848933009}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: -5, y: 2, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 91390000}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!135 &848933011
SphereCollider:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 848933009}
m_Material: {fileID: 0}
m_IncludeLayers:
serializedVersion: 2
m_Bits: 0
m_ExcludeLayers:
serializedVersion: 2
m_Bits: 0
m_LayerOverridePriority: 0
m_IsTrigger: 0
m_ProvidesContacts: 0
m_Enabled: 1
serializedVersion: 3
m_Radius: 0.5
m_Center: {x: 0, y: 0, z: 0}
--- !u!23 &848933012
MeshRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 848933009}
m_Enabled: 1
m_CastShadows: 1
m_ReceiveShadows: 1
m_DynamicOccludee: 1
m_StaticShadowCaster: 0
m_MotionVectors: 1
m_LightProbeUsage: 1
m_ReflectionProbeUsage: 1
m_RayTracingMode: 2
m_RayTraceProcedural: 0
m_RayTracingAccelStructBuildFlagsOverride: 0
m_RayTracingAccelStructBuildFlags: 1
m_SmallMeshCulling: 1
m_ForceMeshLod: -1
m_MeshLodSelectionBias: 0
m_RenderingLayerMask: 1
m_RendererPriority: 0
m_Materials:
- {fileID: 2100000, guid: 31321ba15b8f8eb4c954353edc038b1d, type: 2}
m_StaticBatchInfo:
firstSubMesh: 0
subMeshCount: 0
m_StaticBatchRoot: {fileID: 0}
m_ProbeAnchor: {fileID: 0}
m_LightProbeVolumeOverride: {fileID: 0}
m_ScaleInLightmap: 1
m_ReceiveGI: 1
m_PreserveUVs: 0
m_IgnoreNormalsForChartDetection: 0
m_ImportantGI: 0
m_StitchLightmapSeams: 1
m_SelectedEditorRenderState: 3
m_MinimumChartSize: 4
m_AutoUVMaxDistance: 0.5
m_AutoUVMaxAngle: 89
m_LightmapParameters: {fileID: 0}
m_GlobalIlluminationMeshLod: 0
m_SortingLayerID: 0
m_SortingLayer: 0
m_SortingOrder: 0
m_MaskInteraction: 0
m_AdditionalVertexStreams: {fileID: 0}
--- !u!33 &848933013
MeshFilter:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 848933009}
m_Mesh: {fileID: 10207, guid: 0000000000000000e000000000000000, type: 0}
--- !u!114 &848933014
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 848933009}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 23d0ac85377c640a1900cfd13e89c389, type: 3}
m_Name:
m_EditorClassIdentifier: Assembly-CSharp::NPCStandIn
npcName: Bartender
npcColor: {r: 1, g: 0.92156863, b: 0.015686275, a: 1}
dialogueRunner: {fileID: 0}
yarnStartNode: Bartender_Talk
dialogueUI: {fileID: 0}
labelHeight: 1.2
labelFontSize: 4
labelColor: {r: 0.2, g: 0.2, b: 0.2, a: 1}
player: {fileID: 0}
interactionRange: 3
promptHeight: 0.7
promptFontSize: 3
--- !u!114 &848933015
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 848933009}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 7862f80331f384d28b27600075971a77, type: 3}
m_Name:
m_EditorClassIdentifier: Assembly-CSharp::NightclubArcadia.Cinematics.CameraPointOfInterest
weight: 1
triggerRadius: 8
maxBias: 2.5
extraDistance: 1.5
--- !u!114 &848933016
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 848933009}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 5257f07d188de44eb9a04ba4d3a65fec, type: 3}
m_Name:
m_EditorClassIdentifier: Assembly-CSharp::NightclubArcadia.Interaction.ClickInteractableBridge
npc: {fileID: 0}
--- !u!1 &1138462136
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 1138462137}
- component: {fileID: 1138462141}
- component: {fileID: 1138462140}
- component: {fileID: 1138462139}
- component: {fileID: 1138462138}
- component: {fileID: 1138462143}
- component: {fileID: 1138462142}
m_Layer: 6
m_Name: Bartender (1)
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &1138462137
m_Modification:
serializedVersion: 3
m_TransformParent: {fileID: 91390000}
m_Modifications:
- target: {fileID: 172386393200463862, guid: c0db6e63cdcba4608a13198c04bfc610, type: 3}
propertyPath: m_LocalPosition.x
value: -5
objectReference: {fileID: 0}
- target: {fileID: 172386393200463862, guid: c0db6e63cdcba4608a13198c04bfc610, type: 3}
propertyPath: m_LocalPosition.y
value: 2
objectReference: {fileID: 0}
- target: {fileID: 172386393200463862, guid: c0db6e63cdcba4608a13198c04bfc610, type: 3}
propertyPath: m_LocalPosition.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 172386393200463862, guid: c0db6e63cdcba4608a13198c04bfc610, type: 3}
propertyPath: m_LocalRotation.w
value: 1
objectReference: {fileID: 0}
- target: {fileID: 172386393200463862, guid: c0db6e63cdcba4608a13198c04bfc610, type: 3}
propertyPath: m_LocalRotation.x
value: -0
objectReference: {fileID: 0}
- target: {fileID: 172386393200463862, guid: c0db6e63cdcba4608a13198c04bfc610, type: 3}
propertyPath: m_LocalRotation.y
value: -0
objectReference: {fileID: 0}
- target: {fileID: 172386393200463862, guid: c0db6e63cdcba4608a13198c04bfc610, type: 3}
propertyPath: m_LocalRotation.z
value: -0
objectReference: {fileID: 0}
- target: {fileID: 172386393200463862, guid: c0db6e63cdcba4608a13198c04bfc610, type: 3}
propertyPath: m_LocalEulerAnglesHint.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 172386393200463862, guid: c0db6e63cdcba4608a13198c04bfc610, type: 3}
propertyPath: m_LocalEulerAnglesHint.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 172386393200463862, guid: c0db6e63cdcba4608a13198c04bfc610, type: 3}
propertyPath: m_LocalEulerAnglesHint.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 7530910684996007227, guid: c0db6e63cdcba4608a13198c04bfc610, type: 3}
propertyPath: m_Name
value: Bartender
objectReference: {fileID: 0}
- target: {fileID: 7530910684996007227, guid: c0db6e63cdcba4608a13198c04bfc610, type: 3}
propertyPath: m_Layer
value: 6
objectReference: {fileID: 0}
m_RemovedComponents: []
m_RemovedGameObjects: []
m_AddedGameObjects: []
m_AddedComponents:
- targetCorrespondingSourceObject: {fileID: 7322328536378480179, guid: c0db6e63cdcba4608a13198c04bfc610, type: 3}
insertIndex: -1
addedObject: {fileID: 0}
- targetCorrespondingSourceObject: {fileID: 4737069642952972654, guid: c0db6e63cdcba4608a13198c04bfc610, type: 3}
insertIndex: -1
addedObject: {fileID: 0}
m_SourcePrefab: {fileID: 100100000, guid: c0db6e63cdcba4608a13198c04bfc610, type: 3}
--- !u!4 &897841317 stripped
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_CorrespondingSourceObject: {fileID: 172386393200463862, guid: c0db6e63cdcba4608a13198c04bfc610, type: 3}
m_PrefabInstance: {fileID: 897841316}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1138462136}
serializedVersion: 2
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 2.5, y: 2, z: 2}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 91390000}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &1138462138
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1138462136}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 23d0ac85377c640a1900cfd13e89c389, type: 3}
m_Name:
m_EditorClassIdentifier: Assembly-CSharp::NPCStandIn
npcName: Chair
npcColor: {r: 0, g: 0, b: 1, a: 1}
dialogueRunner: {fileID: 0}
yarnStartNode: Chair_Interaction
dialogueUI: {fileID: 0}
labelHeight: 1.2
labelFontSize: 4
labelColor: {r: 0.2, g: 0.2, b: 0.2, a: 1}
player: {fileID: 0}
interactionRange: 3
promptHeight: 0.7
promptFontSize: 3
--- !u!135 &1138462139
SphereCollider:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1138462136}
m_Material: {fileID: 0}
m_IncludeLayers:
serializedVersion: 2
m_Bits: 0
m_ExcludeLayers:
serializedVersion: 2
m_Bits: 0
m_LayerOverridePriority: 0
m_IsTrigger: 0
m_ProvidesContacts: 0
m_Enabled: 1
serializedVersion: 3
m_Radius: 0.5
m_Center: {x: 0, y: 0, z: 0}
--- !u!23 &1138462140
MeshRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1138462136}
m_Enabled: 1
m_CastShadows: 1
m_ReceiveShadows: 1
m_DynamicOccludee: 1
m_StaticShadowCaster: 0
m_MotionVectors: 1
m_LightProbeUsage: 1
m_ReflectionProbeUsage: 1
m_RayTracingMode: 2
m_RayTraceProcedural: 0
m_RayTracingAccelStructBuildFlagsOverride: 0
m_RayTracingAccelStructBuildFlags: 1
m_SmallMeshCulling: 1
m_ForceMeshLod: -1
m_MeshLodSelectionBias: 0
m_RenderingLayerMask: 1
m_RendererPriority: 0
m_Materials:
- {fileID: 2100000, guid: 31321ba15b8f8eb4c954353edc038b1d, type: 2}
m_StaticBatchInfo:
firstSubMesh: 0
subMeshCount: 0
m_StaticBatchRoot: {fileID: 0}
m_ProbeAnchor: {fileID: 0}
m_LightProbeVolumeOverride: {fileID: 0}
m_ScaleInLightmap: 1
m_ReceiveGI: 1
m_PreserveUVs: 0
m_IgnoreNormalsForChartDetection: 0
m_ImportantGI: 0
m_StitchLightmapSeams: 1
m_SelectedEditorRenderState: 3
m_MinimumChartSize: 4
m_AutoUVMaxDistance: 0.5
m_AutoUVMaxAngle: 89
m_LightmapParameters: {fileID: 0}
m_GlobalIlluminationMeshLod: 0
m_SortingLayerID: 0
m_SortingLayer: 0
m_SortingOrder: 0
m_MaskInteraction: 0
m_AdditionalVertexStreams: {fileID: 0}
--- !u!33 &1138462141
MeshFilter:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1138462136}
m_Mesh: {fileID: 10207, guid: 0000000000000000e000000000000000, type: 0}
--- !u!114 &1138462142
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1138462136}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 7862f80331f384d28b27600075971a77, type: 3}
m_Name:
m_EditorClassIdentifier: Assembly-CSharp::NightclubArcadia.Cinematics.CameraPointOfInterest
weight: 1
triggerRadius: 8
maxBias: 2.5
extraDistance: 1.5
--- !u!114 &1138462143
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1138462136}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 5257f07d188de44eb9a04ba4d3a65fec, type: 3}
m_Name:
m_EditorClassIdentifier: Assembly-CSharp::NightclubArcadia.Interaction.ClickInteractableBridge
npc: {fileID: 0}
--- !u!1 &1226552496
GameObject:
m_ObjectHideFlags: 0