someone please tidy this Script make it so it looks more clearer to comprehend too and understand in a greater span.
Ai Script:
local AI = (script.Parent)
local root = AI:WaitForChild("HumanoidRootPart")
pcall(function() root:SetNetworkOwner(nil) end)
local hum = AI:WaitForChild("Monster")
-- Folders
local States = script:WaitForChild("States")
local ModulesFolder = script:WaitForChild("Modules")
local SettingsFolder = script:WaitForChild("Settings")
local AnimFolder = SettingsFolder:WaitForChild("AnimationSettings")
local ChasingSettings = SettingsFolder:WaitForChild("ChasingSettings")
local TPSettings = SettingsFolder:WaitForChild("TPSettings")
local PatrolSettings = SettingsFolder:WaitForChild("PatrolSettings")
local JumpscareSettings = SettingsFolder:WaitForChild("JumpscareSettings")
local ShakeSettings = SettingsFolder:WaitForChild("ShakeSettings")
local HearingSettings = SettingsFolder:WaitForChild("HearingSettings")
local Sounds = SettingsFolder:WaitForChild("Sounds")
local Remotes = game:GetService("ReplicatedStorage"):WaitForChild("AIRemotes")
local Events = script:WaitForChild("Events")
hum.WalkSpeed = ChasingSettings.PatrolSpeed.Value
Sounds.ChaseSound.Parent = root
Sounds.Footstep.DidLoop:Connect(function()
Remotes.ShakeScreen:FireAllClients(root)
end)
Sounds.Footstep.Parent = root
Sounds.TPSound.Parent = root
if TPSettings.ParticleEnabled.Value then
TPSettings.Particle.Value.Parent = root
end
-- Modules
local RaycastModule = require(ModulesFolder:WaitForChild("RaycastModule")) -- Gets the functions inside this Module.
local PatrolMovementModule = require(ModulesFolder:WaitForChild("PatrolMovement")) -- Gets the functions inside this Module.
local ChaseMovementModule = require(ModulesFolder:WaitForChild("ChasingMovement")) -- Gets the functions inside this Module.
local HearingMovementModule = require(ModulesFolder:WaitForChild("HearingMovement")) -- Gets the functions inside this Module.
-- Values
local IsChasing = States:WaitForChild("IsChasing") -- A value that if is true that means the AI is chasing.
local IsJumpscareing = States:WaitForChild("IsJumpscareing") -- A value that if is true that means the AI is jumpscareing a player
local IsBeeingTeleported = States:WaitForChild("IsBeeingTeleported") -- A value that if is true that means the AI is beeing teleported
-- Animations
if AnimFolder.IdleAnimEnabled.Value then
IdleAnim = hum:LoadAnimation(AnimFolder.Idle) -- If the idle anim is enabled loads the animation
end
local WalkAnim = hum:LoadAnimation(AnimFolder.Walk)-- Loads the walk animation
if AnimFolder.RunAnimEnabled.Value then
RunAnim = hum:LoadAnimation(AnimFolder.Run)-- If the run anim is enabled loads the animation
end
local JumpscareAnim = hum:LoadAnimation(AnimFolder.Jumpscare)-- Loads the jumpscare animation
-- Patrol Movement
local PathParams = {
["AgentRadius"] = root.Size.X;
["AgentHeight"] = hum.HipHeight;
["AgentCanJump"] = false;
["WaypointSpacing"] = 5;
}
local MaxWaypoints = #PatrolSettings.LoopPointsFolder.Value:GetChildren()
local CurrentWayPointIndex = #PatrolSettings.LoopPointsFolder.Value:GetChildren()
local Util = {}
do
function Util:GetVelocity()
return (root.Velocity * Vector3.new(1,0,1)).Magnitude-- Gets the velocity of the AI Whit 0 on Y axis
end
function Util:GetFloorPos()
return (root.Position - Vector3.new(0,hum.HipHeight,0))
end
function Util:GetDistanceFromVector(Start,End)
return (((typeof(Start) == "Vector3" and Start) or Start.Position) - ((typeof(End) == "Vector3" and End) or End.Position)).Magnitude -- Gets the distance of Start and End parameter
end
function Util:GetLoopPoint()
if PatrolSettings.RandomWayPoints.Value then
return PatrolSettings.LoopPointsFolder.Value:GetChildren()[math.random(1,MaxWaypoints)]
end
if PatrolSettings.FixedPath.Value then
CurrentWayPointIndex += 1
if CurrentWayPointIndex > MaxWaypoints then
CurrentWayPointIndex = 1
end
return PatrolSettings.LoopPointsFolder.Value:WaitForChild(PatrolSettings.WaypointName.Value..CurrentWayPointIndex)
end
return PatrolSettings.LoopPointsFolder.Value:GetChildren()[math.random(1,MaxWaypoints)]
end
function Util:GetRoot(p1 : Player | Model)
if p1:IsA("Player") then
if not p1.Character then
return nil
--repeat wait() until p1.Character
end
if not p1.Character or not p1.Character.HumanoidRootPart or not p1.Character.PrimaryPart then
return nil
--repeat wait() until p1.Character.HumanoidRootPart or p1.Character.PrimaryPart
end
return p1.Character.HumanoidRootPart or p1.Character.PrimaryPart
elseif p1:IsA("Model") then
if not p1.HumanoidRootPart or not p1.PrimaryPart then
return nil
--repeat wait() until p1.HumanoidRootPart or p1.PrimaryPart
end
return p1.HumanoidRootPart or p1.PrimaryPart
end
return nil
end
function Util:ChasePlayer(Player,Path)
if Player.AIFolderValues.IsSafe.Value == false then
Path:Run(Util:GetRoot(Player).Position + (Util:GetRoot(Player).AssemblyLinearVelocity/3))
end
end
function Util:CanChasePlr(p2)
if p2 and p2.Character and p2.AIFolderValues.IsSafe.Value == false and p2.Character.Humanoid and p2.Character.Humanoid.Health > 1
and p2.Character.HumanoidRootPart and Util:GetDistanceFromVector(p2.Character.HumanoidRootPart.Position,root.Position) < ChasingSettings.ChaseMaxDistance.Value then
return true
end
return false
end
function Util:StopAllAnims()
if IdleAnim and IdleAnim.IsPlaying then
IdleAnim:Stop(AnimFolder.FadeTime.Value)
end
if RunAnim and RunAnim.IsPlaying then
RunAnim:Stop(AnimFolder.FadeTime.Value)
end
if WalkAnim and WalkAnim.IsPlaying then
WalkAnim:Stop(AnimFolder.FadeTime.Value)
end
if JumpscareAnim and JumpscareAnim.IsPlaying then
JumpscareAnim:Stop(AnimFolder.FadeTime.Value)
end
end
end
spawn(function()
local OrigenPos = root.CFrame
local PossibleStuck = false
local OldRootPos = root.Position
local StuckAmount = 0
while true do
if Util:GetDistanceFromVector(OldRootPos,root.Position) < 1 and States.IsJumpscareing.Value == false then
StuckAmount += 1
PossibleStuck = true
if StuckAmount >= TPSettings.StuckTimesToTP.Value then
States.IsChasing.Value = false
States.IsBeeingTeleported.Value = true
States.CanChase.Value = false
States.CanJumpscare.Value = false
States.IsHearing.Value = false
States.CanHear.Value = false
spawn(function()
if #game:GetService("Players"):GetPlayers() ~= 0 then
for v1,v2 in pairs(game:GetService("Players"):GetPlayers()) do
if v2.AIFolderValues then
if v2.AIFolderValues.GettingChased then
v2.AIFolderValues.GettingChased.Value = false
Remotes.Chase:FireClient(v2,false,ChasingSettings.ChaseFov.Value,Sounds.ChaseTheme,AI)
end
end
end
end
end)
if TPSettings.ParticleEnabled.Value then
TPSettings.Particle.Value.Enabled = true
spawn(function()
wait(TPSettings.ParticleLifeTime.Value)
TPSettings.Particle.Value.Enabled = false
end)
end
if TPSettings.TPFolder.Value == nil then
root.CFrame = OrigenPos
else
local random = TPSettings.TPFolder.Value:GetChildren()[math.random(1,#TPSettings.TPFolder.Value:GetChildren())]
root.CFrame = random.CFrame + Vector3.new(0,hum.HipHeight + 2,0)
end
root.TPSound:Play()
spawn(function()
wait(0.5)
States.IsBeeingTeleported.Value = false
States.CanChase.Value = true
States.CanJumpscare.Value = true
States.CanHear.Value = true
end)
end
else
StuckAmount = 0
PossibleStuck = false
end
if not PossibleStuck then
PossibleStuck = false
OldRootPos = root.Position
end
wait(1)
end
end)
spawn(function()
while true do
local CurrentAnim = States.CurrentAnim
if CurrentAnim.Value ~= AnimFolder.Walk and Util:GetVelocity() > 1 and States.IsChasing.Value == false and States.IsJumpscareing.Value == false then
Util:StopAllAnims()
CurrentAnim.Value = AnimFolder.Walk
WalkAnim:Play(AnimFolder.FadeTime.Value)
end
if WalkAnim.IsPlaying and States.IsChasing.Value == false then
WalkAnim:AdjustSpeed(math.clamp(Util:GetVelocity() / 5.3, 0.1, AnimFolder.WalkAnimSpeed.Value))
elseif not AnimFolder.RunAnimEnabled.Value and WalkAnim.IsPlaying and States.IsChasing.Value == true then
WalkAnim:AdjustSpeed(math.clamp(Util:GetVelocity() / 5.3 , 0.1 , AnimFolder.WalkAnimSpeed.Value + 0.5))
end
if AnimFolder.RunAnimEnabled.Value and CurrentAnim.Value ~= AnimFolder.Run and Util:GetVelocity() > 1 and States.IsChasing.Value == true and States.IsJumpscareing.Value == false then
Util:StopAllAnims()
CurrentAnim.Value = AnimFolder.Run
RunAnim:Play(AnimFolder.FadeTime.Value)
elseif not AnimFolder.RunAnimEnabled.Value and CurrentAnim.Value ~= AnimFolder.Walk and Util:GetVelocity() > 1 and States.IsChasing.Value == true and States.IsJumpscareing.Value == false then
Util:StopAllAnims()
CurrentAnim.Value = AnimFolder.Walk
WalkAnim:Play(AnimFolder.FadeTime.Value)
end
if RunAnim.IsPlaying then
RunAnim:AdjustSpeed(math.clamp(Util:GetVelocity()/5.3,0.1,AnimFolder.RunAnimSpeed.Value))
end
if AnimFolder.IdleAnimEnabled.Value and CurrentAnim.Value ~= AnimFolder.Idle and Util:GetVelocity() < 1 and States.IsJumpscareing.Value == false then
Util:StopAllAnims()
CurrentAnim.Value = AnimFolder.Idle
IdleAnim:Play(AnimFolder.FadeTime.Value)
end
if AnimFolder.IdleAnimEnabled.Value and IdleAnim.IsPlaying then
IdleAnim:AdjustSpeed(AnimFolder.IdleAnimSpeed.Value)
elseif not AnimFolder.IdleAnimEnabled.Value and WalkAnim.IsPlaying and Util:GetVelocity() < 1 then
WalkAnim:AdjustSpeed(0.1)
end
wait()
end
end)
local SeeingAPlayer = false -- a value that if is true it means that the AI is Seeing a player and that player can be chased by the AI
local CurrentSeeingPlayer = nil
local Stuck = false
local Reached = false
spawn(function()
while true do
if #game:GetService("Players"):GetPlayers() == 0 then
wait()
continue
end
SeeingAPlayer = false
local mag, target = math.huge, nil
for i,plr in pairs(game:GetService("Players"):GetPlayers()) do
local CanChasePlr = Util:CanChasePlr(plr)
if plr.Character
and plr.Character:FindFirstChildWhichIsA("Humanoid")
and plr.Character.Humanoid.Health > 2
and CanChasePlr == true
and RaycastModule:RayCastAI(AI, plr.Character,ChasingSettings.ChaseMaxDistance.Value) and Util:GetDistanceFromVector(plr.Character.HumanoidRootPart.Position, root.Position) < mag then
target = plr
mag = Util:GetDistanceFromVector(plr.Character.HumanoidRootPart.Position, root.Position)
end
end
if target then
CurrentSeeingPlayer = target
SeeingAPlayer = true
end
wait()
end
end)
local function Kill(part : BasePart)
if States.IsJumpscareing.Value or States.CanJumpscare.Value == false then return end
if game:GetService("Players"):GetPlayerFromCharacter(part.Parent) then
local PlrRoot = Util:GetRoot(part.Parent) :: BasePart | nil
if PlrRoot then
if Util:GetDistanceFromVector(PlrRoot.Position,root.Position) < JumpscareSettings.KillDistance.Value then
if part.Parent.Humanoid then
if part.Parent.Humanoid.Health > 2 then
States.IsJumpscareing.Value = true
States.CanJumpscare.Value = false
repeat wait() part.Parent.Humanoid.Health = 0 until part.Parent.Humanoid.Health == 0
Remotes.Jumpscare:FireClient(game:GetService("Players"):GetPlayerFromCharacter(part.Parent),JumpscareSettings,Sounds.JumpscareSound)
States.CurrentAnim.Value = AnimFolder.Jumpscare
root.Anchored = true
Util:StopAllAnims()
JumpscareAnim:Play(AnimFolder.FadeTime.Value)
task.wait(JumpscareSettings.JumpscareTime.Value)
JumpscareAnim:Stop(AnimFolder.FadeTime.Value)
States.IsJumpscareing.Value = false
root.Anchored = false
States.CanJumpscare.Value = true
States.CurrentAnim.Value = nil
end
end
end
end
end
end
for v1,v2 in pairs(AI:GetChildren()) do
if v2:IsA("BasePart") or v2:IsA("MeshPart") or v2:IsA("UnionOperation") then
v2.Touched:Connect(Kill)
end
end
Remotes.Hearing.OnServerEvent:Connect(function(plr, AIModel,PlrRoot : BasePart)
if States.CanHear.Value and AIModel == AI and States.IsJumpscareing.Value == false
and States.IsBeeingTeleported.Value == false
and States.IsChasing.Value == false
and States.IsHearing.Value == false then
local testPath : Path = game:GetService("PathfindingService"):CreatePath(PathParams)
local posToRun = Util:GetRoot(plr).Position
local computed, e = pcall(function()
testPath:ComputeAsync(root.Position,posToRun)
end)
if not computed or testPath.Status == Enum.PathStatus.NoPath or #testPath:GetWaypoints() < 2 then
return
end
local ReachedHear = false
local UnStuckHear = false
States.IsHearing.Value = true
States.CanHear.Value = false
local HearingPath = HearingMovementModule.new(AI,PathParams,Events.StuckHear,Events.ReachedHearPath)
local finished = Events.ReachedHearPath.Event:Connect(function()
ReachedHear = true
end)
local StuckFunction = Events.StuckHear.Event:Connect(function()
UnStuckHear = true
end)
if States.IsHearing.Value then
HearingPath:Run(posToRun)
end
repeat wait() until UnStuckHear or ReachedHear or States.IsChasing.Value or States.IsBeeingTeleported.Value or States.IsJumpscareing.Value or SeeingAPlayer == true --(GetNearestChar() and table.find(FindTargets(),GetNearestChar()) and RaycastModule:RayCastAI(AI, GetNearestChar().Character))
local stopped = false
repeat wait()
pcall(function()
HearingPath:Stop(UnStuckHear)
stopped = true
end)
until stopped == true
HearingPath:Destroy()
if finished then
finished:Disconnect()
end
if StuckFunction then
StuckFunction:Disconnect()
end
ReachedHear = false
UnStuckHear = false
States.IsHearing.Value = false
spawn(function()
task.wait(HearingSettings.WaitTimeToEnableHearAgain.Value)
if not States.IsBeeingTeleported.Value then
States.CanHear.Value = true
end
end)
end
end)
while true do
if SeeingAPlayer and States.CanChase.Value and not States.IsJumpscareing.Value then
local ReachedChase = false
local MainTarget = CurrentSeeingPlayer
MainTarget.AIFolderValues.GettingChased.Value = true
States.IsChasing.Value = true
States.CanChase.Value = false
local ChasePath = ChaseMovementModule.new(AI,PathParams)
local Connection = ChasePath.Reached:Connect(function()
ReachedChase = true
end)
Remotes.Chase:FireClient(CurrentSeeingPlayer,true,ChasingSettings.ChaseFov.Value,Sounds.ChaseTheme,AI)
if ChasingSettings.ChaseSound.Value and root.ChaseSound then
root.ChaseSound:Play()
end
hum.WalkSpeed = ChasingSettings.ChaseSpeed.Value
repeat
if CurrentSeeingPlayer then
if CurrentSeeingPlayer.AIFolderValues.GettingChased.Value == false then
Remotes.Chase:FireClient(CurrentSeeingPlayer,true,ChasingSettings.ChaseFov.Value,Sounds.ChaseTheme,AI)
CurrentSeeingPlayer.AIFolderValues.GettingChased.Value = true
end
Util:ChasePlayer(CurrentSeeingPlayer,ChasePath)
end
wait()
until ReachedChase or CurrentSeeingPlayer == nil or CurrentSeeingPlayer.Character == nil or CurrentSeeingPlayer.Character.PrimaryPart == nil or (CurrentSeeingPlayer.Character.Humanoid and CurrentSeeingPlayer.Character.Humanoid.Health < 1) or Util:GetDistanceFromVector(Util:GetRoot(MainTarget).Position,root.Position) > ChasingSettings.ChaseMaxDistance.Value or States.IsJumpscareing.Value or States.IsBeeingTeleported.Value
hum.WalkSpeed = ChasingSettings.PatrolSpeed.Value
pcall(function()
ChasePath:Stop()
end)
if ChasingSettings.ChaseSound.Value and root.ChaseSound then
root.ChaseSound:Stop()
end
if Connection then
Connection:Disconnect()
Connection = nil
end
if MainTarget then
MainTarget.AIFolderValues.GettingChased.Value = false
end
States.IsChasing.Value = false
for i,Player in pairs(game:GetService("Players"):GetPlayers()) do
if Player.AIFolderValues then
if Player.AIFolderValues.GettingChased then
Player.AIFolderValues.GettingChased.Value = false
Remotes.Chase:FireClient(Player,false,ChasingSettings.ChaseFov.Value,Sounds.ChaseTheme,AI)
end
end
end
spawn(function()
wait(0.5)
States.CanChase.Value = true
end)
else
if IsChasing.Value or IsJumpscareing.Value or IsBeeingTeleported.Value or States.IsHearing.Value then game:GetService("RunService").Heartbeat:Wait() continue end-- checks if the AI is chasing or jumpscareing
Stuck = false
Reached = false
local WayPoint = Util:GetLoopPoint()
if not WayPoint then
repeat
WayPoint = Util:GetLoopPoint()
wait()
until WayPoint
end
local PatrolPath = PatrolMovementModule.new(AI,PathParams,Events.Stuck,Events.Reached)
local StuckConnection = Events.Stuck.Event:Connect(function()
Stuck = true
end)
local ReachedConnection = Events.Reached.Event:Connect(function()
Reached = true
end)
PatrolPath:Run(WayPoint.Position)
repeat wait() until IsJumpscareing.Value or IsBeeingTeleported.Value or SeeingAPlayer or Reached or Stuck or States.IsHearing.Value
if PatrolPath and PatrolPath._MoveConnection then
PatrolPath:Stop()
end
WayPoint = nil
Reached = false
Stuck = false
ReachedConnection:Disconnect()
StuckConnection:Disconnect()
ReachedConnection = nil
StuckConnection = nil
end
game:GetService("RunService").Heartbeat:Wait()
end