Smarter and better enemy AI?

I’ve been trying my hand at enemy AI recently with FSMs and I have a decent result. It gets the job done but I have some issues and things I would like to improve that my pea brain can’t figure out. Here is a demo video.

As you can see, there are some things I can improve. The movement is choppy, I handle cooldowns really poorly in my spaghetti code and the fight just feels too unpredictable.

Please give me advice, coding techniques, resources, anything that you use to perfect your enemy AI. Any help is appreciated since I’m still not a great programmer haha
Here is the code.

local RunService = game:GetService("RunService")
local RS = game:GetService("ReplicatedStorage")
local Players = game:GetService("Players")

local damageEvent = RS:WaitForChild("Events"):FindFirstChild("DamagePlayer")
local cannonProj = RS:WaitForChild("Projectiles"):FindFirstChild("CannonArmPart")

local humanoid = script.Parent
local root = humanoid.Parent.PrimaryPart

local targetDistance = script:GetAttribute("TargetDistance")
local stopDistance = script:GetAttribute("StopDistance")
local damage = script:GetAttribute("Damage")
local attackDistance = script:GetAttribute("AttackDistance")
local attackDistance2 = script:GetAttribute("AttackDistance2")
local attackWait = script:GetAttribute("AttackWait")
local attackCooldown = script:GetAttribute("AttackCooldown")
local lastAttack = tick()
local originalSpeed = humanoid.WalkSpeed

local tickSpeed = 0.1

local state = {}
local currentState = nil
local attacking = false

blade = humanoid.Parent.Cutlass.Blade
local attackSound = blade.SwingSound

local t = {}

blade.Touched:Connect(function(hit)
	if attacking then
		local player = game.Players:GetPlayerFromCharacter(hit.Parent)
		if player and player.Character then
			if hit == player.Character.HumanoidRootPart and table.find(t, hit.Parent) == nil then
				damageEvent:Fire(player.Character.Humanoid, damage)
				table.insert(t, hit.Parent)
			end	
		end
	end
end)

function performSwordAttack()
	attacking = true
	local anim = script.Parent.Parent.Swing
	humanoid = script.Parent
	local track = humanoid:LoadAnimation(anim)
	attackSound = blade.SwingSound
	attackSound:Play()
	track:Play()
	track.Stopped:Wait()
	attacking = false
	humanoid:Move(Vector3.new())
end

function performCannonAttack()
	
	humanoid.WalkSpeed = 0
	local anim = script.Parent.Parent.Cannon
	humanoid = script.Parent
	local track = humanoid:LoadAnimation(anim)
	track:Play()
	delay (.2, function()
		attackSound = blade.CannonSound
		attackSound:Play()
		
		local projectile = cannonProj:Clone()
		local spawnPart = humanoid.Parent["Left Arm"].SpawnPart
		
		local nearestPlayer, distance, direction = findNearestPlayer()
		projectile.Parent = game.Workspace
		projectile.Position = spawnPart.CFrame.Position + spawnPart.CFrame.LookVector * 1
		print(nearestPlayer)
		projectile.CFrame = CFrame.lookAt(projectile.Position, nearestPlayer.Character:FindFirstChild("HumanoidRootPart").Position)
		
		RunService.Heartbeat:Connect(function()
			projectile.Position = projectile.Position + projectile.CFrame.LookVector * 1
		end)
	end)
end


function state.Chasing()
	
	local nearestPlayer, distance, direction = findNearestPlayer()
	if nearestPlayer then
		if distance <= targetDistance and distance >= attackDistance then
			humanoid:Move(direction)
		elseif distance >= targetDistance then
			currentState = state.Wandering
			humanoid:Move(Vector3.new())
		elseif distance <= attackDistance and tick() - lastAttack >= attackCooldown then
			currentState = state.Attacking
		elseif distance <= attackDistance and not ((tick() - lastAttack) >= attackCooldown) then
			humanoid:Move(direction)
		end
end

function state.Attacking()
		local chosenAttack = nil
		local nearestPlayer, distance, direction = findNearestPlayer()
		if distance <= attackDistance then
			if distance <= attackDistance2 then
				chosenAttack = math.random(2,10)
			else
				chosenAttack = math.random(1,4)
			end
			lastAttack = tick()
			local humRootPart = script.Parent.Parent.HumanoidRootPart
			local charPos = nearestPlayer.Character.HumanoidRootPart.Position
			local pos = CFrame.lookAt(humRootPart.Position, Vector3.new(charPos.X, humRootPart.Position.Y, charPos.Z))
			humRootPart.CFrame = pos
			
			if chosenAttack <= 4 and chosenAttack > 1 then
				performCannonAttack()
			elseif chosenAttack == 1 then
				currentState = state.Chasing
				return
			else
				performSwordAttack()
			end
			wait(attackWait)
			table.clear(t)
			humanoid.WalkSpeed = originalSpeed
			
		else
			currentState = state.Chasing
		end
	end
end

function state.Wandering()
	local nearestPlayer, distance, direction = findNearestPlayer()
	if nearestPlayer then
		if distance <= targetDistance then
			currentState = state.Chasing
		end
	end
end

function findNearestPlayer()
	local playerList = Players:GetPlayers()

	local nearestPlayer = nil
	local distance = nil
	local direction = nil

	for _, player in pairs(playerList) do
		local character = player.Character
		if character then
			local distanceVector = (player.Character.HumanoidRootPart.Position - root.Position)
			if not nearestPlayer then
				nearestPlayer = player
				distance = distanceVector.Magnitude
				direction = distanceVector.Unit
			elseif distanceVector.Magnitude < distance then
				nearestPlayer = player
				distance = distanceVector.Magnitude
				direction = distanceVector.Unit
			end	
		end
	end

	return nearestPlayer, distance, direction
end

currentState = state.Wandering

while true do
	currentState()
	task.wait(tickSpeed)
end

Sorry for bump, but I still need help a day later