Enemies no longer check attack distance before doing damage

So after I coded these 319 lines of code that controls enemies behavior it was working fantastic! However, give it a couple of weeks and it’s broken again.

Problem(s):

  • After beginning the attack animation the enemy will deal damage despite the player being outside/leaving their attack range.
  • Upon being parried the enemy no longer plays the parry animation and the “SuccessfulParry” sound no longer plays. However, the damage is still nullified (sometimes).

I know for a fact some of ya’ll do NOT want to read 319 lines of code so I included the main problem first, After that follows the entire code. I apologize in advance for violating your existence with this Yandere Simulator type coding.

					local function CheckForSword()
						for _, child in pairs(target:GetChildren()) do
							if child.ClassName == "Model" then


								local Blade = child:FindFirstChild("Blade")

								if not Blade then
									return
								end


								local Flash = Blade:FindFirstChild("Flash")

								if not Flash then
									return
								end

								local SuccessfulParry = Blade:FindFirstChild("SuccessfulParry")

								if SuccessfulParry then
									SuccessfulParry:Play()
								end

								Flash.Enabled = true

								wait(1)

								Flash.Enabled = false

							end
						end
					end

					wait()

					if BlockedAttack == false then
						distance = checkDistance()
						if distance > MaxAttackrange.Value then
							return
						end

						while AttackTrack and AttackTrack.IsPlaying do
							wait()
						end

						target.Humanoid:TakeDamage(DamageAmount) -- Inflict damage

					elseif BlockedAttack == true then
						distance = checkDistance()
						if distance > MaxAttackrange.Value then
							return
						end
						coroutine.wrap(CheckForSword)()
						PlayBlockedAnimation()
					end

And here’s the entire thing.

local CollectionService = game:GetService("CollectionService")

-- TAG ENEMIES ONLY (Model)
local function enemyBehavior(Enemy)
	local humanoid = Enemy:FindFirstChildOfClass("Humanoid")
	local PathfindingService = game:GetService("PathfindingService")
	Enemy.PrimaryPart:SetNetworkOwner(nil)

	-- Config Settings
	local Attackrange = Enemy.Configuration.AttackRange.Value -- Measurement in studs
	local MaxAttackrange = Enemy.Configuration:WaitForChild("MaxAttackDistance")

	local DamageAmount = Enemy.Configuration.DamageAmount.Value 
	local AttackCooldown = Enemy.Configuration.AttackCooldown.Value -- Seconds
	local maxDistanceValue = Enemy.Configuration.MaxDistance.Value
	
	local AnimationLength = Enemy.Configuration.AnimationLength.Value

	local CanJump = Enemy.Configuration:WaitForChild("AgentCanJump")
	local Height = Enemy.Configuration:WaitForChild("AgentHeight")
	local Width = Enemy.Configuration:WaitForChild("AgentRadius")

	-- Get Animation
	local AttackAnimation = Enemy:WaitForChild("AttackAnimation") -- Reference to the Animation object
	local AttackTrack
	local BlockAnimation = Enemy:WaitForChild("BlockedAnimation")
	local BlockTrack

	local function PlayAttackAnimation()
		local char = Enemy
		local humanoid = char:FindFirstChildOfClass("Humanoid")

		if humanoid then
			local animator = humanoid:FindFirstChild("Animator")
			if not animator then
				-- Attempt to wait for the Animator with a timeout
				animator = humanoid:WaitForChild("Animator", 5) -- Timeout after 5 seconds
				if not animator then
					warn("Animator not found on", char.Name)
					return -- Exit the function if Animator is not found
				end
			end

			local AttackAnim = Instance.new("Animation")
			AttackAnim.AnimationId = AttackAnimation.AnimationId -- Set the AnimationId from the referenced Animation object
			AttackTrack = animator:LoadAnimation(AttackAnim)
			AttackTrack:Play()
		end
	end
	
	local function PlayBlockedAnimation()
		local humanoid = Enemy:FindFirstChildOfClass("Humanoid")
		if humanoid then
			local animator = humanoid:FindFirstChild("Animator")
			if not animator then
				animator = humanoid:WaitForChild("Animator", 5) -- Timeout after 5 seconds
				if not animator then
					warn("Animator not found on", Enemy.Name)
					return -- Exit if Animator is not found
				end
			end

			local NewAnim = Instance.new("Animation")
			NewAnim.AnimationId = BlockAnimation.AnimationId -- Set AnimationId from reference
			BlockTrack = animator:LoadAnimation(NewAnim)
			BlockTrack:Play()
		end
	end

	local function CanSeeTarget(target) -- The enemy will chase and attack if it can see the target
		local enemyRootPart = Enemy:FindFirstChild("HumanoidRootPart")
		local targetRootPart = target:FindFirstChild("HumanoidRootPart")

		if not enemyRootPart or not targetRootPart then
			return false
		end

		local origin = enemyRootPart.Position
		local direction = (targetRootPart.Position - enemyRootPart.Position).unit * 40
		local ray = Ray.new(origin, direction)

		local hit, pos = workspace:FindPartOnRay(ray, Enemy)

		if hit then
			if hit:IsDescendantOf(target) then
				return true
			end
		else
			return false
		end
	end

	local function findTarget() -- Finds the target
		local players = game.Players:GetPlayers()
		local nearestTarget
		local minDistance = maxDistanceValue

		for _, player in pairs(players) do
			if player.Character and player.Character:FindFirstChild("HumanoidRootPart") then
				local target = player.Character
				local distance = (Enemy:FindFirstChild("HumanoidRootPart").Position - target.HumanoidRootPart.Position).Magnitude

				if distance < minDistance and CanSeeTarget(target) then
					nearestTarget = target
					minDistance = distance
				end
			end
		end

		return nearestTarget
	end

	local function getPath(destination)
		local pathParams = {
			AgentHeight = Height.Value,
			AgentRadius = Width.Value,
			AgentCanJump = CanJump.Value
		}

		local path = PathfindingService:CreatePath(pathParams)
		path:ComputeAsync(Enemy:FindFirstChild("HumanoidRootPart").Position, destination.Position)

		return path
	end

	local function rotateToFace(target)
		local enemyRootPart = Enemy:FindFirstChild("HumanoidRootPart")
		local targetPosition = target:FindFirstChild("HumanoidRootPart").Position

		local enemyHumanoid = Enemy:FindFirstChild("Humanoid")

		if not enemyHumanoid then
			return
		end

		if enemyHumanoid.Health == 0 then
			return
		end

		if enemyRootPart then
			local lookAtCFrame = CFrame.lookAt(enemyRootPart.Position, Vector3.new(targetPosition.X, enemyRootPart.Position.Y, targetPosition.Z))
			enemyRootPart.CFrame = lookAtCFrame
		end
	end

	-- Enemy Attack Logic
	local function attack(target)
		local function checkDistance()
			local enemyRootPart = Enemy:FindFirstChild("HumanoidRootPart")
			local targetRootPart = target:FindFirstChild("HumanoidRootPart")

			if not enemyRootPart or not targetRootPart then
				return math.huge -- Return large distance if part is missing
			end

			return (enemyRootPart.Position - targetRootPart.Position).Magnitude
		end

		local distance = checkDistance()

		if distance > Attackrange then -- Move towards target if out of range
			local targetRootPart = target:FindFirstChild("HumanoidRootPart")
			if targetRootPart then
				humanoid:MoveTo(targetRootPart.Position)
			end
		elseif distance <= Attackrange then -- Attack when in range
			wait(0.3)
			if target.Humanoid and target.Humanoid.Health > 0 then	
				coroutine.wrap(PlayAttackAnimation)()
			end

			local function AttackWait()
				while AttackTrack and AttackTrack.IsPlaying do
					rotateToFace(target) -- Continue facing target
					wait()
				end
			end

			coroutine.wrap(AttackWait)()

			distance = checkDistance() -- Re-check distance
			if distance <= MaxAttackrange.Value then
				if target.Humanoid and target.Humanoid.Health > 0 then

					local BlockedAttack = false



					local function CheckIfParry()
						for _, child in pairs(target:GetChildren()) do
							if child:IsA("Tool") then


								local Parry = child:FindFirstChild("Parry")

								if not Parry then
									return
								end



								wait(AnimationLength)

								if Parry.Value == false then
									return
								end

								BlockedAttack = true



							end
						end
					end

					CheckIfParry()

					local function CheckForSword()
						for _, child in pairs(target:GetChildren()) do
							if child.ClassName == "Model" then


								local Blade = child:FindFirstChild("Blade")

								if not Blade then
									return
								end


								local Flash = Blade:FindFirstChild("Flash")

								if not Flash then
									return
								end

								local SuccessfulParry = Blade:FindFirstChild("SuccessfulParry")

								if SuccessfulParry then
									SuccessfulParry:Play()
								end

								Flash.Enabled = true

								wait(1)

								Flash.Enabled = false

							end
						end
					end

					wait()

					if BlockedAttack == false then
						distance = checkDistance()
						if distance > MaxAttackrange.Value then
							return
						end

						while AttackTrack and AttackTrack.IsPlaying do
							wait()
						end

						target.Humanoid:TakeDamage(DamageAmount) -- Inflict damage

					elseif BlockedAttack == true then
						distance = checkDistance()
						if distance > MaxAttackrange.Value then
							return
						end
						coroutine.wrap(CheckForSword)()
						PlayBlockedAnimation()
					end


				end
				wait(AttackCooldown) -- Wait for cooldown
			end
		end 
	end

	local function walkTo(destination)
		local path = getPath(destination)

		if path.Status == Enum.PathStatus.Success then
			for index, waypoint in pairs(path:GetWaypoints()) do
				local target = findTarget()
				if target and target.Humanoid and target.Humanoid.Health > 0 then
					-- print("TARGET FOUND", target.Name)
					attack(target)
				else
					humanoid:MoveTo(waypoint.Position)
					humanoid.MoveToFinished:Wait()
				end
			end
		else
			humanoid:MoveTo(destination.Position - (Enemy:FindFirstChild("HumanoidRootPart").CFrame.LookVector * 10))
		end
	end

	local function patrol()
		local waypoints = Enemy.Parent:FindFirstChild("waypoints")
		if waypoints then
			local waypointChildren = waypoints:GetChildren()
			local randomNum = math.random(1, #waypointChildren)
			walkTo(waypointChildren[randomNum])
		else
			-- Code not move unless the player is within range. Which then the enemy will attack.
		end
	end

	while wait() do
		patrol()
	end
end

for _, Enemy in pairs(CollectionService:GetTagged("EnemiesTag")) do
	coroutine.wrap(enemyBehavior)(Enemy)
end

Might need to do another checkDistance in that loop and stop the track if needed.

The execution path for the parry stuff is a little hard to follow. Maybe add step-by-step comments to it for clarification. In the process of doing that you may find the error in the logic XD. Seems likely it’s a logic issue rather than an error in the code. Could be a simple as some flag not being reset.