I’ve created systems that spawn multiple types of NPCs based on certain parameters before. It’s no easy feat but it is certainly possible.
After playing the game, I can deduce that the entirety of their system is client-based (local script). This makes it a lot easier to code.
The basis of our entire code is this:
(This is a LocalScript inside StarterPlayer → StarterPlayerScripts)
local REP = game:GetService("ReplicatedStorage")
local NPC = REP:WaitForChild("NPC")
local plr = game.Players.LocalPlayer
local npcGui = plr.PlayerGui:WaitForChild("NPC")
local button = npcGui.Regen
--// On Button Press //--
button.Activated:Connect(function()
for _, repNPC in pairs(NPC:GetChildren()) do
local npc = repNPC:Clone()
npc.Parent = workspace
npc:SetPrimaryPartCFrame()
end
end)
This short piece of code simply detects when the UI button is activated. When the button is activated it iterates through the NPC folder and spawns them into the workspace.
The only problem now is deciding how to handle different CFrames for spawning.
We have a folder in ReplicatedStorage named NPC that holds the NPCs’ character models.

Option 1:
We could add attributes to each of these models, but assigning an attribute to each NPC while in studio is tedious.
Option 2:
We could create a LUA table inside a ModuleScript that stores information on where we want the NPCs to spawn. But this is also tedious.
Option 3:
We could add parts into the workspace that represent spawn points. Then we can add an ObjectValue to each NPC pointing towards their assigned spawn point.
I prefer Option 3, solely because it requires the least amount of work and code. It works just as well as the other two.
Working with Option 3:
Create an ObjectValue named Spawn inside each NPC. This step requires no code and is done in studio.

Create a Folder in workspace containing all the spawns. The names of your spawns do matter, they could all be named Part and it would still work. What matter is making sure your spawns are non-CanCollide and Anchored. Transparency is up to you.

How do I see LookVector?

Right click on the desired part and click Show Orientation Indicator
The LookVector is the direction in which the blue sphere is jutting out.

I recommend adding a decal pointing towards the LookVector of your parts. This makes it easier to create spawns in studio.

Then simply assign the ObjectValue to the spawn part you want.

After you’ve set all this up in studio, we can read this information with the script.
Local Script Code:
local REP = game:GetService("ReplicatedStorage")
local NPC = REP:WaitForChild("NPC")
local NPCSpawns = workspace.NPCSpawns
local plr = game.Players.LocalPlayer
local npcGui = plr.PlayerGui:WaitForChild("NPC")
local button = npcGui.Regen
--// On Button Press //--
button.Activated:Connect(function()
for _, repNPC in pairs(NPC:GetChildren()) do
local npcSpawn = repNPC:WaitForChild("Spawn").Value
local npcSize = repNPC:GetExtentsSize()
local npc = repNPC:Clone()
npc.Parent = workspace
npc:SetPrimaryPartCFrame(npcSpawn.CFrame + Vector3.new(0, npcSize.Y/2 + npcSpawn.Size.Y/2, 0))
end
end)
With the addition of three lines of code we get the desired result:

We achieved this by setting the NPCs’ PrimaryParts’ (HumanoidRootPart) CFrames to that of the spawn part. We then added a Y offset to the CFrame to ensure the NPC spawns at the top of the spawn part. This keeps them from clipping through floors.
I’m leaving a rbxl file so that you can easily look at how I set my system up. Simply open the file up in your computer’s file explorer. Or go to ‘file’ in the top left of Roblox Studio and click ‘Open from File…’
SpawnNPC.rbxl (56.8 KB)
I hope this helps! 