--|| SERVICES ||--
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Debris = game:GetService("Debris")
local ServerScriptService = game:GetService("ServerScriptService")
local ServerStorage = game:GetService("ServerStorage")
local TweenService = game:GetService("TweenService")
local PathfindingService = game:GetService("PathfindingService")
--|| LOCALISE ||--
local Pairs = pairs
local Require = require
local Script = script
--|| MODULES ||--
local NPCData = Require(ReplicatedStorage.Database.NPCS)
local GlobalFunctions = Require(ReplicatedStorage.GlobalFunctions)
local Damage = Require(ServerScriptService.Server.Damage)
--|| REMOTE EVENTS ||--
local clientSFX = ReplicatedStorage['Remote Events'].clientSFX
--|| VARIABLES ||--
local RandomSeed = Random.new()
local DeathConnections = {}
local NPCS = {}
local CivillianNPCS = {
[game.Workspace.World.Map.NPCS.Zeppeli] = {
["Last Update"] = tick(),
}
}
local module = {
logNPC = function(NPC)
print("[NPC MANAGER]: "..NPC.Name.." has been logged (Animations also preloaded).")
NPCS[NPC] = {
["CFrame"] = NPC.PrimaryPart.CFrame,
["Last Update"] = tick(),
["Last Attack"] = tick(),
['Animations'] = {},
["Attack Animation"] = 1,
["Whitelist"] = {},
["Owner"] = {},
}
-- Loading in NPC stats
local Humanoid = NPC.Humanoid
Humanoid.MaxHealth = GlobalFunctions.calculateHealth(NPCData[NPC.Name].Defence)
Humanoid.Health = Humanoid.MaxHealth
Humanoid.WalkSpeed = NPCData[NPC.Name].WalkSpd
-- Preloading them animations
for _, Animation in Pairs(ReplicatedStorage.Animations.NPCS[NPC.Name]:GetChildren()) do
NPCS[NPC].Animations[Animation.Name] = Humanoid:LoadAnimation(Animation)
end
NPCS[NPC].Animations["Idle"]:Play()
end,
bindDeath = function(NPC)
print("[NPC MANAGER]: "..NPC.Name.." has been connected to it's death event.")
DeathConnections[NPC] = NPC.Humanoid.Died:Connect(function()
DeathConnections[NPC]:Disconnect()
DeathConnections[NPC] = nil
local respawnPoint = NPCS[NPC].CFrame
NPCS[NPC] = nil
delay(NPCData[NPC.Name].RespawnTime, function()
local respawnModel = ServerStorage.NPCS[NPC.Name]:Clone()
respawnModel:SetPrimaryPartCFrame(respawnPoint)
respawnModel.Parent = game.Workspace.World.Enemies
end)
NPC:Destroy()
end)
end,
getClosestEnemy = function(NPC, Whitelist)
local closest = nil
for _, target in Pairs(game.Workspace:GetDescendants()) do
if target:IsA("Humanoid")
and target.Health > 0
and target.Parent:IsA("Model")
and not Whitelist[target.Parent]
and target.Parent ~= NPC
and target.Parent.Parent ~= game.Workspace.World.Map.NPCS
and target.Parent.Parent ~= game.Workspace.World.Enemies then
local Distance = (NPC.PrimaryPart.Position - target.Parent.PrimaryPart.Position).Magnitude
if Distance <= NPCData[NPC.Name].Range then
if closest == nil then
closest = target.Parent
else
local distanceToClosest = (NPC.PrimaryPart.Position - closest.PrimaryPart.Position).Magnitude
local distanceToTarget = (NPC.PrimaryPart.Position - target.Parent.PrimaryPart.Position).Magnitude
if distanceToTarget < distanceToClosest then
closest = target.Parent
end
end
end
end
end
return closest
end,
moveTo = function(NPC, Pos)
NPC.Humanoid:MoveTo(Pos)
end,
setState = function(NPC, State)
NPC.Data.State.Value = State
end,
attack = function(NPC, Target)
local Inflicted = Damage.inflictDamage(NPC, Target.Humanoid, NPCData[NPC.Name].Strength)
if Inflicted then
local kb = GlobalFunctions.createInstance({
instance = "BodyVelocity",
properties = {
Name = "Knockback",
Velocity = (Target.PrimaryPart.Position - NPC.PrimaryPart.Position).Unit*20,
MaxForce = Vector3.new(1,1,1)*500000,
Parent = Target.PrimaryPart
}
})
Debris:AddItem(kb, 0.1)
local Sound = GlobalFunctions.createInstance({
instance = "Sound",
properties = {
Name = NPC.Name.." Swoosh",
SoundId = "rbxassetid://12222200",
Parent = NPC.PrimaryPart
},
})
Sound:Play()
Debris:AddItem(Sound, 3)
clientSFX:FireAllClients({
Module = "Chicken",
Function = "Hit",
Data = {["Target"] = Target.Humanoid, ["Character"] = NPC}
})
end
end,
}
spawn(function()
while true do
wait()
for NPC, Data in Pairs(NPCS) do
if not DeathConnections[NPC] then module.bindDeath(NPC) end
if NPC and NPC.PrimaryPart and NPC.Humanoid and NPC.Humanoid:IsDescendantOf(game.Workspace) then
local RandomUpdateTime = 0.1 + 0.1 * math.random() -- Generate a random update time
if tick() - Data["Last Update"] >= RandomUpdateTime then -- If the last update was larger then the random time
Data["Last Update"] = tick() -- Set
if NPC.Humanoid.Health ~= NPC.Humanoid.MaxHealth then
local closestEnemy = module.getClosestEnemy(NPC, Data.Whitelist)
if closestEnemy ~= nil then
local distance = (closestEnemy.PrimaryPart.Position - NPC.PrimaryPart.Position).Magnitude
if tick() - Data["Last Attack"] >= NPCData[NPC.Name].AtkSpd and distance <= NPCData[NPC.Name].AtkDistance then
Data["Last Attack"] = tick()
NPCS[NPC]["Attack Animation"] = NPCS[NPC]["Attack Animation"] + 1
if Data["Attack Animation"] > 2 then
NPCS[NPC]["Attack Animation"] = 1
end
local FaceTarget = TweenService:Create(NPC.PrimaryPart, TweenInfo.new(0.1, Enum.EasingStyle.Sine), {["CFrame"] = CFrame.new(NPC.PrimaryPart.Position, closestEnemy.PrimaryPart.Position)})
FaceTarget:Play()
Debris:AddItem(FaceTarget, 0.1)
NPCS[NPC].Animations["Attack"..Data["Attack Animation"]]:Play()
module.attack(NPC, closestEnemy)
else
if not NPCS[NPC].Animations.Moving.IsPlaying then
NPCS[NPC].Animations["Moving"]:Play()
end
local startPos = NPC.PrimaryPart.Position
local endPos = closestEnemy.PrimaryPart.Position
local distanceAway = (NPCData[NPC.Name].AtkDistance - 1)
local FinalisedPosition = endPos + ((startPos - endPos).Unit * distanceAway)
module.moveTo(NPC, FinalisedPosition)
end
else
NPCS[NPC].Animations["Moving"]:Stop()
end
end
end
end
end
end
-- for NPC, Data in Pairs(CivillianNPCS) do
-- local updateTime = 20 + 20 * math.random()
-- if tick() - Data["Last Update"] >= updateTime then
--
-- end
-- end
end)
for _, Enemy in Pairs(game.Workspace.World.Enemies:GetChildren()) do
module.logNPC(Enemy)
end
game.Workspace.World.Enemies.ChildAdded:Connect(function(Enemy)
module.logNPC(Enemy)
end)
return module
This is my NPC manager code, what I wish to do is implement pathfinding service but what happens is the NPC’s start stopping at each node and it looks odd. I’d like to make a good performing NPC manager code. I 've applied everything I know but it seems like it’s not enough to handle up to 200 NPCs.
I was wondering what I could do to improve, I’ve read several topics regarding NPC control and how some remove the humanoid and etc but in my case, the NPC’s just look really off and can start floating.