TimberWoods / src /ServerScriptService /NPCManager.server.lua
algorembrant's picture
Upload 88 files
0712d5f verified
-- 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)