Yes, here,
--[[IF YOU WANT TO CONFIGURE SOMETHING, GO TO THE CONFIGURATION IN NPC AND EDIT IT
]]
-------------------------------------------------------------
local Players = game:GetService("Players")
local Pathfinding = require(script.SimplePath)
workspace.DescendantAdded:Connect(function(instance)
if instance:IsA("Model") and Players:GetPlayerFromCharacter(instance) then
local String = Instance.new("StringValue")
String.Parent = instance
String.Name = "State"
end
end)
for i, NPC in pairs(workspace.NPCs:GetChildren()) do
local LookAtPlayerRange = NPC.Configuration.DistanceOfDetection.Value -- Studs between player and dummy that dummy can look.
-- [Head, Torso, HumanoidRootPart], "Torso" and "UpperTorso" works with both R6 and R15.
-- Also make sure to not misspell it.
local PartToLookAt = "Head" -- Where should the npc look at.
local LookBackOnNil = true -- Should the npc look at where they should when player is out of range.
local CanShoot = true
--[[
[Horizontal and Vertical limits for head and body tracking.]
Setting to 0 negates tracking, setting to 1 is normal tracking, and setting to anything higher than 1 goes past real life head/body rotation capabilities.
--]]
local HeadHorFactor = 0.8
local HeadVertFactor = 0.5
local BodyHorFactor = 0.4
local BodyVertFactor = 0.4
-- Don't set this above 1, it will cause glitchy behaviour.
local UpdateSpeed = 0.3 -- How fast the body will rotates.
local UpdateDelay = 0.05 -- How fast the heartbeat will update.
-------------------------------------------------------
--
local Ang = CFrame.Angles
local aTan = math.atan
--
--------------------------------------------
local Body = NPC
local Head = Body:WaitForChild("Head")
local Hum = Body:WaitForChild("Humanoid")
local Core = Body:WaitForChild("HumanoidRootPart")
local IsR6 = (Hum.RigType.Value==0)
local Trso = (IsR6 and Body:WaitForChild("Torso")) or Body:WaitForChild("UpperTorso")
local Neck = (IsR6 and Trso:WaitForChild("Neck")) or Head:WaitForChild("Neck")
local Waist = (not IsR6 and Trso:WaitForChild("Waist"))
local Path = Pathfinding.new(Body)
local NeckOrgnC0 = Neck.C0
local WaistOrgnC0 = (not IsR6 and Waist.C0)
--------------------------------------------
-- Necessery Functions
local function getClosestPlayer() -- Get the closest player in the range.
local closest_player, closest_distance = nil, LookAtPlayerRange
for i, player in pairs(workspace:GetDescendants()) do
if player:FindFirstChild("Humanoid") and player ~= Body and Players:GetPlayerFromCharacter(player) or player.Parent == workspace.LookAtAbleObjects then
local distance = (Core.Position - player.PrimaryPart.Position).Magnitude
if distance < closest_distance then
closest_player = player
closest_distance = distance
end
end
end
return closest_player
end
local function rWait(n) -- Fix many heartbeat lag issue
n = n or 0.05
local startTime = os.clock()
while os.clock() - startTime < n do
game:GetService("RunService").Heartbeat:Wait()
end
end
local ErrorPart = nil
local function GetValidPartToLookAt(Char, bodypart)
local pHum = Char:FindFirstChild("Humanoid")
if not Char and pHum then return nil end
local pIsR6 = (pHum.RigType.Value==0)
local ValidPart
if bodypart == "Torso" or bodypart == "UpperTorso" then
if pIsR6 then ValidPart = Char:FindFirstChild("Torso") else ValidPart = Char:FindFirstChild("UpperTorso") end
if ValidPart ~= Char:FindFirstChild(bodypart) then
ValidPart = Char.PrimaryPart
end
else ValidPart = Char:FindFirstChild(bodypart) end
if ValidPart then return ValidPart else
if ErrorPart ~= bodypart then
warn(Body.Name.." can't find part to look: "..tostring(bodypart))
ErrorPart = bodypart
end
return nil end
end
local function LookAt(NeckC0, WaistC0)
if not IsR6 then
Neck.C0 = Neck.C0:lerp(NeckC0, UpdateSpeed/2)
Waist.C0 = Waist.C0:lerp(WaistC0, UpdateSpeed/2)
else
Neck.C0 = Neck.C0:lerp(NeckC0, UpdateSpeed/2)
end
end
--------------------------------------------
game:GetService("RunService").Heartbeat:Connect(function()
rWait(UpdateDelay)
local TrsoLV = Trso.CFrame.lookVector
local HdPos = Head.CFrame.p
local player = getClosestPlayer()
local LookAtPart
if IsR6 and Neck or Neck and Waist then
if player and NPC.Humanoid.Health>0 then
if NPC:FindFirstChild("Humanoid") then
LookAtPart = GetValidPartToLookAt(player, PartToLookAt)
if LookAtPart and NPC.Humanoid.Health>0 then
local Dist = nil;
local Diff = nil;
local is_in_front = Core.CFrame:ToObjectSpace(LookAtPart.CFrame).Z < 0
if is_in_front then
Dist = (Head.CFrame.p-LookAtPart.CFrame.p).magnitude
Diff = Head.CFrame.Y-LookAtPart.CFrame.Y
if not IsR6 then
if Body.Configuration.CurrentDetection.Value <= 10 then
if player.State.Value == "" then
Body.Configuration.CurrentDetection.Value += Body.Configuration.DetectionRate.Value
elseif Body.Configuration.StateRate:FindFirstChild(player.State.Value) then
Body.Configuration.CurrentDetection.Value += Body.Configuration.StateRate:FindFirstChild(player.State.Value).Value
end
if Players:GetPlayerFromCharacter(player) then
game.ReplicatedStorage.NPCRemotes.Detection:FireClient(game.Players:GetPlayerFromCharacter(player), Body.Configuration.CurrentDetection.Value)
end
end
if Body.Configuration.CurrentDetection.Value >= 10 then
if Body.Configuration.Surrendered.Value == false and Body.Configuration.CanShoot.Value == false then
Path.Visualize = true
if Body.Configuration.Running.Value == false then
Body.Configuration.Running.Value = true
--Dummy knows to compute path again if something blocks the path
Path.Blocked:Connect(function()
Path:Run(Body.Configuration.WayPoints.Escape:GetChildren()[math.random(1,#Body.Configuration.WayPoints.Escape:GetChildren())].Value)
end)
--If the position of Goal changes at the next waypoint, compute path again
Path.WaypointReached:Connect(function()
Path:Run(Body.Configuration.WayPoints.Escape:GetChildren()[math.random(1,#Body.Configuration.WayPoints.Escape:GetChildren())].Value)
end)
--Dummmy knows to compute path again if an error occurs
Path.Error:Connect(function(errorType)
Path:Run(Body.Configuration.WayPoints.Escape:GetChildren()[math.random(1,#Body.Configuration.WayPoints.Escape:GetChildren())].Value)
end)
Path.Reached:Connect(function()
Body:Destroy()
game.ReplicatedStorage.NPCRemotes.Loud:Fire()
workspace.Loud:Play()
game:GetService("TweenService"):Create(workspace.Loud, TweenInfo.new(1), {Volume = .5}):Play()
game:GetService("TweenService"):Create(workspace.Stealth, TweenInfo.new(1), {Volume = 0}):Play()
wait(1)
workspace.Stealth:Stop()
end)
Body.Humanoid.WalkSpeed = Body.Configuration.RunSpeed.Value
Path:Run(Body.Configuration.WayPoints.Escape:GetChildren()[math.random(1,#Body.Configuration.WayPoints.Escape:GetChildren())].Value)
wait(5)
if Body.Humanoid.Health <1 then
game.ReplicatedStorage.NPCRemotes.Loud:Fire()
workspace.Loud:Play()
game:GetService("TweenService"):Create(workspace.Loud, TweenInfo.new(1), {Volume = .5}):Play()
game:GetService("TweenService"):Create(workspace.Stealth, TweenInfo.new(1), {Volume = 0}):Play()
wait(1)
workspace.Stealth:Stop()
end
end
else
if Body.Configuration.CanShoot.Value == true then
for i, x in pairs(Body.Weapon:GetDescendants()) do
if x:IsA("BasePart") then
x.Transparency = 0
end
end
coroutine.wrap(function()
while true do
task.wait()
if player.Humanoid.Health>0 and Players:GetPlayerFromCharacter(player) then
local Main = Body.HumanoidRootPart.Position
local Target = Vector3.new(player.HumanoidRootPart.Position.X, Body.HumanoidRootPart.Position.Y, player.HumanoidRootPart.Position.Z)
Body.HumanoidRootPart.CFrame = CFrame.lookAt(Main, Target)
end
end
end)()
coroutine.wrap(function()
local Firerate = Body.Configuration.CanShoot.WeaponProperties.Firerate.Value
while task.wait(Firerate) do
if player.Humanoid.Health>0 and Players:GetPlayerFromCharacter(player) then
CanShoot = true
if CanShoot == true then
print("hurt")
CanShoot = false
local rayLength = 15 --//How long the ray should be
local raycastParams = RaycastParams.new()
raycastParams.FilterDescendantsInstances = {Body}
raycastParams.FilterType = Enum.RaycastFilterType.Blacklist
local rayOrigin = Body.Weapon.FirePos.WorldPosition
local thing = Vector3.new(math.random(0, Body.Configuration.CanShoot.WeaponProperties.Inaccuracy.Value),math.random(0, Body.Configuration.CanShoot.WeaponProperties.Inaccuracy.Value),math.random(0, Body.Configuration.CanShoot.WeaponProperties.Inaccuracy.Value))
local rayDirection = (player.Head.Position+thing - Body.Weapon.FirePos.WorldPosition).Unit * rayLength
local rayResult = workspace:Raycast(rayOrigin, rayDirection, raycastParams)
if rayResult then
if rayResult.Instance:FindFirstAncestorOfClass("Model") and rayResult.Instance:FindFirstAncestorOfClass("Model"):FindFirstChild("Humanoid") and rayResult.Instance:FindFirstAncestorOfClass("Model") ~=Body then
local Damage = math.random(Body.Configuration.CanShoot.WeaponProperties.DamageMin.Value, Body.Configuration.CanShoot.WeaponProperties.DamageMax.Value)
rayResult.Instance:FindFirstAncestorOfClass("Model").Humanoid:TakeDamage(Damage)
print(Damage)
end
end
end
end
end
end)()
wait(5)
if Body.Humanoid.Health <1 then
game.ReplicatedStorage.NPCRemotes.Loud:Fire()
workspace.Loud:Play()
game:GetService("TweenService"):Create(workspace.Loud, TweenInfo.new(1), {Volume = .5}):Play()
game:GetService("TweenService"):Create(workspace.Stealth, TweenInfo.new(1), {Volume = 0}):Play()
wait(1)
workspace.Stealth:Stop()
end
end
end
end
LookAt(NeckOrgnC0*Ang(-(aTan(Diff/Dist)*HeadVertFactor), (((HdPos-LookAtPart.CFrame.p).Unit):Cross(TrsoLV)).Y*HeadHorFactor, 0),
WaistOrgnC0*Ang(-(aTan(Diff/Dist)*BodyVertFactor), (((HdPos-LookAtPart.CFrame.p).Unit):Cross(TrsoLV)).Y*BodyHorFactor, 0))
else
LookAt(NeckOrgnC0*Ang((aTan(Diff/Dist)*HeadVertFactor), 0, (((HdPos-LookAtPart.CFrame.p).Unit):Cross(TrsoLV)).Y*HeadHorFactor))
end
elseif LookBackOnNil and NPC.Humanoid.Health>0 then
if Body then
if Players:GetPlayerFromCharacter(player) then
game.ReplicatedStorage.NPCRemotes.Detection:FireClient(game.Players:GetPlayerFromCharacter(player), Body.Configuration.CurrentDetection.Value)
end
if Body.Configuration.CurrentDetection.Value >= 0 then
Body.Configuration.CurrentDetection.Value -= Body.Configuration.DetectionRate.Value
end
LookAt(NeckOrgnC0, WaistOrgnC0)
end
end
end
end
end
end
end)
Body.Humanoid.Died:Connect(function()
if Body.Configuration.Dead.Value == false then
Body.HumanoidRootPart.GetDisguise.Enabled = true
Body.Parent = workspace.LookAtAbleObjects
local String = Instance.new("StringValue")
String.Parent = Body
String.Name = "State"
String.Value = "Dead"
end
end)
Body.HumanoidRootPart.GetDisguise.Triggered:Connect(function(player)
if player.Character:FindFirstChildOfClass("Pants") then
player.Character:FindFirstChildOfClass("Pants"):Destroy()
end
if player.Character:FindFirstChildOfClass("Shirt") then
player.Character:FindFirstChildOfClass("Shirt"):Destroy()
end
if Body:FindFirstChildOfClass("Shirt") then
Body:FindFirstChildOfClass("Shirt"):Clone().Parent = player.Character
end
if Body:FindFirstChildOfClass("Pants") then
Body:FindFirstChildOfClass("Pants"):Clone().Parent = player.Character
end
player.Character.State.Value = Body.Configuration.IfDisguise.Value
end)
game.ReplicatedStorage.NPCRemotes.Loud.Event:Connect(function()
if Body.Configuration.Running.Value == false and Body.Configuration.CanShoot.Value == false and Body.Configuration.Surrendered.Value == false then
Body.Configuration.Running.Value = true
--Dummy knows to compute path again if something blocks the path
Path.Blocked:Connect(function()
Path:Run(Body.Configuration.WayPoints.Escape:GetChildren()[math.random(1,#Body.Configuration.WayPoints.Escape:GetChildren())].Value)
end)
--If the position of Goal changes at the next waypoint, compute path again
Path.WaypointReached:Connect(function()
Path:Run(Body.Configuration.WayPoints.Escape:GetChildren()[math.random(1,#Body.Configuration.WayPoints.Escape:GetChildren())].Value)
end)
--Dummmy knows to compute path again if an error occurs
Path.Error:Connect(function(errorType)
Path:Run(Body.Configuration.WayPoints.Escape:GetChildren()[math.random(1,#Body.Configuration.WayPoints.Escape:GetChildren())].Value)
end)
Path.Reached:Connect(function()
Body:Destroy()
end)
Body.Humanoid.WalkSpeed = Body.Configuration.RunSpeed.Value
Path:Run(Body.Configuration.WayPoints.Escape:GetChildren()[math.random(1,#Body.Configuration.WayPoints.Escape:GetChildren())].Value)
end
end)
local PrevStand
local Stand
coroutine.wrap(function()
while true do
wait(math.random(1,5))
Path.Reached:Connect(function()
local PrevStand = Stand
wait(math.random(1,5))
end)
if PrevStand then
PrevStand.Occupied.Value = false
end
Stand = Body.Configuration.WayPoints.Casual:GetChildren()[math.random(1,#Body.Configuration.WayPoints.Casual:GetChildren())].Value
if Stand.Occupied.Value == false then
Stand.Occupied.Value = true
Path:Run(Stand)
end
end
end)()
end