I want the script to work as smoothly as it can
Over time my NPC attacker script becomes slower and starts walking closer and then away from me over and over.
I’ve tried check if it was a memory leak and stuff but it didn’t fix it.
I’ll also take any suggestions to impove my code since I know it’s a little messy.
--//CODED BY TRUNGNHAN11!
local BasicEnemyAI = {}
BasicEnemyAI.__index = BasicEnemyAI
local MovementAnimations = game.ReplicatedStorage.ServerAnimations.SprintWalkAnimations
local UIS = game:GetService("UserInputService")
local RS = game:GetService("RunService")
local tweenservice = game:GetService("TweenService")
local playerlist = game.Players:GetPlayers()
local ReplicatedStorage = game.ReplicatedStorage
local MovementAnimations = game.ReplicatedStorage.ServerAnimations.SprintWalkAnimations
local RagdollService = require(game:GetService("ServerScriptService").RagdollStuff.RagdollService)
local Remotes: Folder = ReplicatedStorage:WaitForChild("Remotes")
local Server_Motors2 = Remotes:WaitForChild("ServertoServer_Motors")
local characterfolder = workspace:WaitForChild("Characters")
local BlockingNpcEvent = game:GetService("ServerStorage").NPCAttackRelated.BlockEvent
function BasicEnemyAI.new(character)
local self = setmetatable({}, BasicEnemyAI)
self.character = character
self.Weapon = character:FindFirstChildOfClass("Tool")
self.Weapontype = self.Weapon.Name
self.equipped = false
self.isBlocking = self.character:GetAttribute("IsBlocking")
self.IsDashing = false
self.Humanoid = character:WaitForChild("Humanoid")
if self.Humanoid.Health == 100 then
self.Humanoid.Health = math.random(100,150)
end
self.Humanoid.MaxHealth = self.Humanoid.Health
self.HumanoidRootPart = character:WaitForChild("HumanoidRootPart")
self.Alarmed = false
self.AlarmedFlag = false
self.Walkdistance = 20
self.Rundistance = 60
self.stopdistance = nil
if self.Weapontype == "Fist" then
self.stopdistance = 6
elseif self.Weapontype == "WoodSword" then
self.stopdistance = 6.5
end
self.noticedistance = 120
self.UniqueID = character:GetAttribute("UniqueID")
self.CurrentAnimations = nil
self.BlockAniTrack = nil
self.DashCooldown = 4.5
self.CJ = nil
self.Hflag = true
self.flag = true
self.flag1 = true
self.flag2 = false
self.BlockFlag = false
self.ChaseRagdoll = false
self.speed = nil
self.attackflag = false
self.Humanoid:UnequipTools(self.Weapon)
self:loadAnimations()
self:connectEvents()
self.alignorientation = Instance.new("AlignOrientation")
self.alignorientation.Parent = self.HumanoidRootPart
self.alignorientation.Mode = Enum.OrientationAlignmentMode.OneAttachment
self.alignorientation.Attachment0 = self.HumanoidRootPart.RootAttachment
self.alignorientation.PrimaryAxisOnly = true
self.alignorientation.Responsiveness = 75
self.alignorientation.MaxTorque = 1000000
self.Humanoid.StateChanged:Connect(function(OldState, NewState)
if NewState == Enum.HumanoidStateType.Running then
self.CJ = true
else
self.CJ = false
end
end)
return self
end
function BasicEnemyAI:isIdleAnimationPlaying(humanoid)
local animator = humanoid:FindFirstChildOfClass("Animator")
if not animator then
warn("Animator not found in humanoid.")
return false
end
local tracks = animator:GetPlayingAnimationTracks()
for _, track in ipairs(tracks) do
if track.Name == "Idle" then
return true
end
end
return false
end
function BasicEnemyAI:attack(Atype)
local NPCATTACK = game:GetService("ServerStorage").NPCAttackRelated.AttackingEvent
if self.attackflag == false then
BlockingNpcEvent:Fire(self.character, false)
self.attackflag = true
if self.Weapontype == "Fist" then
if Atype == 1 then
NPCATTACK:Fire(self.character, 1, self.Weapontype)
elseif Atype == 2 then
NPCATTACK:Fire(self.character, 2, self.Weapontype)
end
elseif self.Weapontype == "WoodSword" then
if Atype == 1 then
NPCATTACK:Fire(self.character, 1, self.Weapontype)
elseif Atype == 2 then
NPCATTACK:Fire(self.character, 2, self.Weapontype)
end
end
self.attackflag = false
end
end
function BasicEnemyAI:StunCheck()
for _, accessory in ipairs(self.character:GetChildren()) do
if accessory:IsA("Accessory") and accessory.Name == "Stun" then
return true
end
end
return false
end
function BasicEnemyAI:ParryCheck()
for _, accessory in ipairs(self.character:GetChildren()) do
if accessory:IsA("Accessory") and accessory.Name == "ParriedStun" then
return true
end
end
return false
end
function BasicEnemyAI:M1Check()
for _, accessory in ipairs(self.character:GetChildren()) do
if accessory:IsA("Accessory") and accessory.Name == "LightAttack" then
return true
end
end
return false
end
function BasicEnemyAI:HeavyCheck()
for _, accessory in ipairs(self.character:GetChildren()) do
if accessory:IsA("Accessory") and accessory.Name == "HeavyAttack" then
return true
end
end
return false
end
function BasicEnemyAI:EnemyAttackCheck(target)
for _, Highlight in ipairs(target:GetChildren()) do
if Highlight:IsA("Highlight") then
return true
end
end
return false
end
function BasicEnemyAI:Dash(Direction)
local RollingNpcEvent = game:GetService("ServerStorage").NPCAttackRelated.RollingEvent
if self.CJ == true then
if self.IsDashing == false and not self:M1Check() and not self:StunCheck() and not self:ParryCheck() and not self:HeavyCheck() then
self.IsDashing = true
RollingNpcEvent:Fire(self.character, self.IsDashing)
local linearVelocity = Instance.new("LinearVelocity")
linearVelocity.Attachment0 = self.HumanoidRootPart.RootAttachment
linearVelocity.MaxForce = 25000
linearVelocity.RelativeTo = Enum.ActuatorRelativeTo.Attachment0
linearVelocity.Parent = self.HumanoidRootPart
if Direction == "Forward" then
self.AniDashForward:Play()
linearVelocity.VectorVelocity = Vector3.new(0, 1, -45)
elseif Direction == "Backward" then
self.AniDashBackward:Play()
linearVelocity.VectorVelocity = Vector3.new(0, 1, 45)
elseif Direction == "Right" then
linearVelocity.VectorVelocity = Vector3.new(30, 0, 0)
elseif Direction == "Left" then
linearVelocity.VectorVelocity = Vector3.new(-30, 0, 0)
end
task.wait(0.35)
linearVelocity:Destroy()
task.wait(self.DashCooldown)
self.IsDashing = false
end
end
end
function BasicEnemyAI:loadAnimations()
local MovementAnimations = ReplicatedStorage.ServerAnimations.SprintWalkAnimations
local DashAnimations = ReplicatedStorage.ServerAnimations.DashingAnimations
self.AniDashForward = self.Humanoid:LoadAnimation(DashAnimations.RollAnimation)
self.AniDashBackward = self.Humanoid:LoadAnimation(DashAnimations.RollAnimation)
self.AniWalk = self.Humanoid:LoadAnimation(MovementAnimations.Walk)
self.AniSprint = self.Humanoid:LoadAnimation(MovementAnimations.Sprint)
if self.Weapontype == "Fist" then
self.CurrentAnimations = ReplicatedStorage.ServerAnimations.FistAnimations
elseif self.Weapontype == "WoodSword" then
self.CurrentAnimations = ReplicatedStorage.ServerAnimations.WoodSwordAnimations
end
self.Idle = self.Humanoid:LoadAnimation(self.CurrentAnimations.Idle)
self.BlockAniTrack = self.Humanoid:LoadAnimation(self.CurrentAnimations.Blocking)
end
function BasicEnemyAI:BlockAttack()
local IsCurrentlyblocking = self.character:GetAttribute("IsBlocking")
if self.equipped then
if IsCurrentlyblocking then
self.BlockAniTrack:play()
else
self.BlockAniTrack:stop()
end
end
end
function BasicEnemyAI:FindNearestCharacter()
local nearestcharacter = nil
local distance = nil
local direction = nil
for I, character in pairs(characterfolder:GetChildren()) do
local rootPart = character:FindFirstChild("HumanoidRootPart")
local character = character
if character and character ~= self.character and character:GetAttribute("IsEnemy") ~= self.character:GetAttribute("IsEnemy") and character.Humanoid.Health > 0 and character:GetAttribute("testing") == false then
if rootPart ~= nil then
local distanceVector = (character.HumanoidRootPart.Position - self.HumanoidRootPart.Position)
if not nearestcharacter then
nearestcharacter = character
distance = distanceVector.Magnitude
direction = distanceVector.Unit
elseif distanceVector.Magnitude < distance then
nearestcharacter = character
distance = distanceVector.Magnitude
direction = distanceVector.Unit
end
else
end
end
end
return nearestcharacter, distance, direction
end
function BasicEnemyAI:NoticedEvent()
local nearestcharacter, distance, direction = self:FindNearestCharacter()
if nearestcharacter:GetAttribute("IsEnemy") == self.character:GetAttribute("IsEnemy") or self.Humanoid:GetState() == Enum.HumanoidStateType.Jumping or self.Humanoid:GetState() == Enum.HumanoidStateType.Freefall or self.Humanoid:GetState() == Enum.HumanoidStateType.PlatformStanding then return end
local Hrp = nearestcharacter.HumanoidRootPart
local targetPosition = Hrp.Position
local direction = (targetPosition - self.HumanoidRootPart.Position).unit
local lookAtCFrame = CFrame.new(self.HumanoidRootPart.Position, targetPosition)
local tweenInfo = TweenInfo.new(1)
local tween = tweenservice:Create(self.HumanoidRootPart, tweenInfo, { CFrame = lookAtCFrame })
tween:Play()
tween.Completed:Wait()
end
function BasicEnemyAI:FINDANDCHASECharacter()
local nearestcharacter, distance, direction = self:FindNearestCharacter()
local head = self.character:FindFirstChild("Head")
if head ~= nil then
local vector1 = (nearestcharacter.Head.Position - head.Position).Unit
local elook = self.character.HumanoidRootPart.CFrame.LookVector
local dot = elook:Dot(vector1)
if nearestcharacter then
local Hrp = nearestcharacter.HumanoidRootPart
if self.AlarmedFlag == false then
if self.Humanoid.Health ~= self.Humanoid.MaxHealth or dot > 0.3 then
if distance <= self.Rundistance then
self.Humanoid:EquipTool(self.Weapon)
self.Alarmed = true
self.AlarmedFlag = true
elseif distance <= self.noticedistance then
self:NoticedEvent()
self.Humanoid:EquipTool(self.Weapon)
self.Humanoid:MoveTo(Hrp.Position)
self.Alarmed = true
self.AlarmedFlag = true
end
end
end
local CharacterIsCurrentlyRagdoll = self.character:GetAttribute("Ragdolled")
if self.Humanoid.Health > 0 and nearestcharacter.Humanoid.Health > 0 and CharacterIsCurrentlyRagdoll == false then
local TargetIsCurrentlyRagdoll = nearestcharacter:GetAttribute("Ragdolled")
local TargetIsCurrentlyStaggered = nearestcharacter:GetAttribute("Staggered")
local IsStaggered = self.character:GetAttribute("Staggered")
if not self:StunCheck() and not IsStaggered and not self:ParryCheck() and not self:M1Check() and not self:HeavyCheck() and self.Alarmed and self.equipped == true and ((self.ChaseRagdoll == true) or (TargetIsCurrentlyRagdoll == false and self.ChaseRagdoll == false)) then
self:BlockAttack()
if self:isIdleAnimationPlaying(self.Humanoid) == false then
self.Idle:play()
end
if distance <= self.Rundistance and not self:M1Check() and not IsStaggered and not self:StunCheck() and not self:ParryCheck() and not self:HeavyCheck() and not self.character:GetAttribute("Staggered") and not self.character:GetAttribute("IsBlocking") then
local diff = nearestcharacter.HumanoidRootPart.Position - self.HumanoidRootPart.Position
local angle = math.atan2(-diff.X, -diff.Z)
self.alignorientation.CFrame = CFrame.Angles(0, angle, 0)
end
if distance <= (self.stopdistance - (self.stopdistance / 4)) and not IsStaggered then
self.Humanoid:Move(-direction)
elseif distance < self.stopdistance and not IsStaggered and dot > 0.8 and TargetIsCurrentlyStaggered == true and not self:HeavyCheck() and not self:M1Check() then
if self.Weapontype == "Fist" then
if self.Hflag == true then
self.Hflag = false
task.wait(0.5)
self:attack(2)
task.wait(0.5)
self.Hflag = true
end
elseif self.Weapontype == "WoodSword" then
if self.Hflag == true then
self.Hflag = false
task.wait(0.5)
self:attack(2)
self.ChaseRagdoll = true
task.wait(1.5)
self:attack(2)
task.wait(0.5)
self.ChaseRagdoll = false
self.Hflag = true
end
end
elseif distance < 50 and dot > 0.8 and self:EnemyAttackCheck(nearestcharacter) and not IsStaggered then
self.Humanoid:Move(Vector3.new(0,0,0))
if self.BlockFlag == false then
self.BlockFlag = true
if self.IsDashing == false and not self:M1Check() and not IsStaggered and not self:StunCheck() and not self:ParryCheck() and not self:HeavyCheck() and not self.character:GetAttribute("Staggered") and not self.character:GetAttribute("IsBlocking") and not nearestcharacter:GetAttribute("Ragdolled") and 2 == 2 then
self.Humanoid:Move(Vector3.new(0, 0, 0))
if math.random(1,2) == 2 then
BlockingNpcEvent:Fire(self.character , true)
else
task.wait(0.1)
BlockingNpcEvent:Fire(self.character , true)
end
else
self.Humanoid:Move(Vector3.new(0, 0, 0))
task.wait(0.4)
BlockingNpcEvent:Fire(self.character , false)
end
self.BlockFlag = false
end
elseif distance < self.stopdistance and dot > 0.8 and self.flag1 and not self:HeavyCheck() and not IsStaggered and not self:M1Check() and not self.IsBlocking and not self.BlockFlag and not self.character:GetAttribute("CantBlock") and self.character:GetAttribute("CanAttack") then
self.Humanoid:Move(Vector3.new(0, 0, 0))
if self.flag == true then
self.flag = false
if not self.IsBlocking then
self:attack(1)
end
if math.random(1, 20) == 5 then
self.Humanoid:Move(Vector3.new(0, 0, 0))
self.flag1 = false
self.flag2 = true
self.flag = false
end
self.flag = true
end
elseif distance < self.stopdistance and dot > 0.8 and self.flag2 and not self:M1Check() and not IsStaggered and not self:HeavyCheck() and not self.IsBlocking and not self.BlockFlag and not self.character:GetAttribute("CantBlock") and self.character:GetAttribute("CanAttack") then
self.Humanoid:Move(Vector3.new(0, 0, 0))
if self.flag == true then
self.Humanoid:Move(Vector3.new(0, 0, 0))
self.flag = false
if not self.IsBlocking then
--self:attack(2)
end
self.flag2 = false
self.flag1 = true
self.flag = false
task.wait(0.1)
self.flag = true
end
elseif distance <= self.Walkdistance and not self.IsBlocking and not self:M1Check() and not IsStaggered and not self:HeavyCheck() and not self.character:GetAttribute("CantBlock") and self.character:GetAttribute("CanAttack") then
if self.BlockFlag == false then
self.BlockFlag = true
task.wait(0.3)
BlockingNpcEvent:Fire(self.character , false)
self.BlockFlag = false
end
self.Humanoid.WalkSpeed = 15
self.Humanoid:Move(direction)
elseif distance <= self.Rundistance and not self.IsBlocking and not self:M1Check() and not IsStaggered and not self:HeavyCheck() and not self.character:GetAttribute("CantBlock") and self.character:GetAttribute("CanAttack") then
if self.BlockFlag == false then
self.BlockFlag = true
task.wait(0.3)
BlockingNpcEvent:Fire(self.character, false)
self.BlockFlag = false
end
if math.random(1,5) == 5 then
self:Dash("Forward")
end
self.Humanoid.WalkSpeed = 20
self.Humanoid:Move(direction)
else
self.Humanoid.WalkSpeed = 15
self.Humanoid:Move(Vector3.new(0,0,0))
end
else
self.Humanoid.WalkSpeed = 15
self.Humanoid:Move(Vector3.new(0,0,0))
end
end
end
else
end
end
function BasicEnemyAI:connectEvents()
self.Humanoid.Running:Connect(function(speed)
self.speed = speed
if speed >= 17.5 then
self.AniWalk:Stop()
self.AniSprint:Play()
elseif speed > 5 then
self.AniSprint:Stop()
self.AniWalk:Play()
else
self.AniWalk:Stop()
self.AniSprint:Stop()
end
end)
self.Weapon.Equipped:Connect(function()
self:motor6d()
self.equipped = true
self.isBlocking = false
self.Idle:play()
end)
self.Weapon.Unequipped:Connect(function()
self.equipped = false
self.isBlocking = false
self.Idle:stop()
end)
RS.Heartbeat:Connect(function()
self:FINDANDCHASECharacter()
end)
end
function BasicEnemyAI:motor6d()
if self.Weapon:FindFirstChildOfClass("Configuration") and self.Weapon:FindFirstChildOfClass("Configuration").Name == "6D_Config" then
local Configuration: Configuration = self.Weapon:WaitForChild("6D_Config")
for _, Motor_Config: Configuration in Configuration:GetChildren() do
if Motor_Config:IsA("Configuration") then
Server_Motors2:Fire(nil ,self.character ,self.Weapon)
end
end
end
end
function BasicEnemyAI:Clear_Traces(Tool)
if Tool:FindFirstChildOfClass("Configuration") and Tool:FindFirstChildOfClass("Configuration").Name == "6D_Config" then
local Configuration: Configuration = Tool:WaitForChild("6D_Config")
for _, Motor_Config: Configuration in Configuration:GetChildren() do
if Motor_Config:IsA("Configuration") then
for _, Old_Motor: Motor6D in Motor_Config:GetChildren() do
Old_Motor:Destroy()
end
end
end
end
end
return BasicEnemyAI
^ What I want it to be all the time
^ What happens after like 3 minutes