-- 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)