File size: 4,797 Bytes
0712d5f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
-- src/ServerScriptService/NPCManager.server.lua

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local CollectionService = game:GetService("CollectionService")

local NPCConfig = require(ReplicatedStorage:WaitForChild("Shared"):WaitForChild("NPCConfig"))
local DialogueEvent = ReplicatedStorage:WaitForChild("Events"):WaitForChild("DialogueEvent")
local ShopEvent = ReplicatedStorage:WaitForChild("Events"):WaitForChild("ShopEvent")

local npcFolder = Instance.new("Folder")
npcFolder.Name = "NPCs"
npcFolder.Parent = workspace

local function createNPCModel(npcId, npcData)
	local model = Instance.new("Model")
	model.Name = npcId

	-- Create humanoid R6-style body parts
	local torso = Instance.new("Part")
	torso.Name = "HumanoidRootPart"
	torso.Size = Vector3.new(2, 2, 1)
	torso.Position = npcData.Position
	torso.Anchored = true
	torso.Color = npcData.BodyColors.TorsoColor
	torso.Material = Enum.Material.SmoothPlastic
	torso.Parent = model

	local head = Instance.new("Part")
	head.Name = "Head"
	head.Shape = Enum.PartType.Ball
	head.Size = Vector3.new(1.6, 1.6, 1.6)
	head.Position = npcData.Position + Vector3.new(0, 1.8, 0)
	head.Anchored = true
	head.Color = npcData.BodyColors.HeadColor
	head.Material = Enum.Material.SmoothPlastic
	head.Parent = model

	-- Face decal
	local face = Instance.new("Decal")
	face.Face = Enum.NormalId.Front
	face.Texture = "rbxasset://textures/face.png"
	face.Parent = head

	-- Left Arm
	local leftArm = Instance.new("Part")
	leftArm.Name = "LeftArm"
	leftArm.Size = Vector3.new(1, 2, 1)
	leftArm.Position = npcData.Position + Vector3.new(-1.5, 0, 0)
	leftArm.Anchored = true
	leftArm.Color = npcData.BodyColors.LeftArmColor
	leftArm.Material = Enum.Material.SmoothPlastic
	leftArm.Parent = model

	-- Right Arm
	local rightArm = Instance.new("Part")
	rightArm.Name = "RightArm"
	rightArm.Size = Vector3.new(1, 2, 1)
	rightArm.Position = npcData.Position + Vector3.new(1.5, 0, 0)
	rightArm.Anchored = true
	rightArm.Color = npcData.BodyColors.RightArmColor
	rightArm.Material = Enum.Material.SmoothPlastic
	rightArm.Parent = model

	-- Left Leg
	local leftLeg = Instance.new("Part")
	leftLeg.Name = "LeftLeg"
	leftLeg.Size = Vector3.new(1, 2, 1)
	leftLeg.Position = npcData.Position + Vector3.new(-0.5, -2, 0)
	leftLeg.Anchored = true
	leftLeg.Color = npcData.BodyColors.LeftLegColor
	leftLeg.Material = Enum.Material.SmoothPlastic
	leftLeg.Parent = model

	-- Right Leg
	local rightLeg = Instance.new("Part")
	rightLeg.Name = "RightLeg"
	rightLeg.Size = Vector3.new(1, 2, 1)
	rightLeg.Position = npcData.Position + Vector3.new(0.5, -2, 0)
	rightLeg.Anchored = true
	rightLeg.Color = npcData.BodyColors.RightLegColor
	rightLeg.Material = Enum.Material.SmoothPlastic
	rightLeg.Parent = model

	model.PrimaryPart = torso

	-- Nameplate
	local billboard = Instance.new("BillboardGui")
	billboard.Size = UDim2.new(0, 200, 0, 50)
	billboard.StudsOffset = Vector3.new(0, 3.5, 0)
	billboard.AlwaysOnTop = true
	billboard.Parent = head

	local nameLabel = Instance.new("TextLabel")
	nameLabel.Size = UDim2.new(1, 0, 1, 0)
	nameLabel.BackgroundTransparency = 1
	nameLabel.Text = npcData.DisplayName
	nameLabel.TextColor3 = Color3.new(1, 1, 0.7)
	nameLabel.TextScaled = true
	nameLabel.Font = Enum.Font.GothamBold
	nameLabel.TextStrokeTransparency = 0.5
	nameLabel.Parent = billboard

	-- ProximityPrompt for interaction
	local prompt = Instance.new("ProximityPrompt")
	prompt.ActionText = "Talk"
	prompt.ObjectText = npcData.DisplayName
	prompt.MaxActivationDistance = 10
	prompt.HoldDuration = 0
	prompt.Parent = torso

	-- Track dialogue state per player
	local dialogueIndex = {} -- [userId] = current line index

	prompt.Triggered:Connect(function(player)
		if not dialogueIndex[player.UserId] then
			dialogueIndex[player.UserId] = 1
		end

		local lineIndex = dialogueIndex[player.UserId]
		local dialogue = npcData.Dialogue

		if lineIndex <= #dialogue then
			DialogueEvent:FireClient(player, npcData.DisplayName, dialogue[lineIndex], lineIndex < #dialogue)
			dialogueIndex[player.UserId] = lineIndex + 1
		else
			-- Reset dialogue
			dialogueIndex[player.UserId] = 1
			DialogueEvent:FireClient(player, npcData.DisplayName, dialogue[1], #dialogue > 1)

			-- If shopkeeper, open shop UI
			if npcData.Role == "Shopkeeper" then
				ShopEvent:FireClient(player, "OpenShop")
			end
		end
	end)

	CollectionService:AddTag(model, "NPC")
	model.Parent = npcFolder

	return model
end

-- Spawn all NPCs
task.spawn(function()
	task.wait(2) -- Wait for world structures

	for npcId, npcData in pairs(NPCConfig.NPCs) do
		createNPCModel(npcId, npcData)
	end

	print("All NPCs spawned")
end)