Further enhance this roblox script, and add a bunch more features:

```
--[[
    Autoblock V.3
    Super Advanced Auto Block & Swing Script for Saber Showdown
    Enhanced with an advanced auto-block framework for improved performance and functionality
    Author: Keperman209
    Version: 3.0
    Date: 09.24.2024
--]]

-- === Services ===
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local UserInputService = game:GetService("UserInputService")
local RunService = game:GetService("RunService")
local VirtualInputManager = game:GetService("VirtualInputManager")
local CollectionService = game:GetService("CollectionService")
local Debris = game:GetService("Debris")
local Workspace = workspace

-- === Constants ===
local LOCAL_PLAYER = Players.LocalPlayer
local CAMERA = Workspace.CurrentCamera
local ATTACK_HITBOX_TAG = "AttackHitbox"

local BLOCK_DIRECTIONS = {
    Forward = 1,
    Right = 2,
    Backward = 3,
    Left = 4
}

-- === Remotes ===
local LightsaberRemotes = ReplicatedStorage:WaitForChild("LightsaberRemotes")
local BlockRemote = LightsaberRemotes:WaitForChild("Block")
local UnblockRemote = LightsaberRemotes:WaitForChild("Unblock")
local UpdateBlockDirectionRemote = LightsaberRemotes:WaitForChild("UpdateBlockDirection")

-- === Settings ===
local Settings = {
    -- Blocking Settings
    blockDistances = {              -- Max distance to attempt a block per attack type
        Slap = 10,                   -- Slaps require closer proximity
        Default = 15                 -- Default distance for other attacks
    },
    autoBlockEnabled = true,         -- Enable/Disable dynamic auto-blocking
    blockCooldown = 0.5,             -- General cooldown between blocks (seconds)
    attackBlockCooldowns = {         -- Cooldown between blocks per attack type (seconds)
        Slap = 0.2,
        Default = 0.5
    },
    attackTypes = {                  -- Attack animation IDs
        Slap = 12625891544,
        SlappedLegs = 12671715182,
        SlappedArms = 12625896358,
        Punch = 12625897000,         -- Example attack ID
        Kick = 12625897500,          -- Example attack ID
        KickUp = 12625897944,        -- Added KickUp
        KickBack = 12625899752        -- Added KickBack
    },

    -- Counter Attacking Settings
    autoCounterEnabled = true,       -- Enable/Disable auto counter attacks
    autoCounterDistance = 10,        -- Distance within which to auto counter
    counterCooldown = 1.5,           -- Cooldown between counters (seconds)
    counterKey = Enum.KeyCode.E,      -- Key to use for countering

    -- Reaction Settings
    reactionTimeThreshold = 0.2,     -- Threshold for reaction time (in seconds)
    predictionFactor = 0.5,          -- Factor for predicting enemy movements (0-1)

    -- Slap Lag Handling
    slapLagCompensation = 0.2,       -- Additional delay to compensate for lag in slap attacks

    -- Aim-Lock Directional Block Settings
    aimLockEnabled = true,           -- Enable/Disable aim-lock directional blocking
    aimLockSmoothness = 0.1,         -- Smoothness factor for direction adjustment

    -- Slap Blocking Specific Settings
    slap = {
        slapCooldown = 1.0,           -- Cooldown between handling slap blocks (seconds)
        autoSlapEnabled = true,       -- Enable/Disable auto countering slaps
        autoSlapDistance = 8,         -- Distance within which to auto counter slaps
        slapTimerESPEnabled = true,   -- Enable/Disable visual cooldown indicator for slaps
    },

    -- UI Settings
    ui = {
        ScreenGuiName = "AutoBlockUI_V3",
        -- Black Box Frame
        BlackBox = {
            Size = UDim2.new(0.25, 0, 0.2, 0),
            Position = UDim2.new(0.375, 0, 0.8, 0),
            BackgroundColor = Color3.fromRGB(30, 30, 30),
            BackgroundTransparency = 0.15,
            CornerRadius = 12,
            BorderColor = Color3.fromRGB(255, 255, 255),
            BorderSizePixel = 2
        },
        -- Loading Bar
        LoadingBar = {
            Size = UDim2.new(1, -20, 0.25, -10),
            Position = UDim2.new(0, 10, 0.1, 10),
            BackgroundColorActive = Color3.fromRGB(0, 255, 0),
            BackgroundColorInactive = Color3.fromRGB(255, 0, 0),
            BackgroundTransparency = 0.3,
            CornerRadius = 8
        },
        -- Message Box
        MessageBox = {
            Size = UDim2.new(1, -20, 0.4, -10),
            Position = UDim2.new(0, 10, 0.35, 10),
            BackgroundTransparency = 1,
            TextColor = Color3.fromRGB(255, 255, 255),
            TextScaled = true,
            Font = Enum.Font.GothamSemibold,
            Text = "Press 'Q' to toggle Auto Block",
            TextStrokeColor = Color3.fromRGB(0, 0, 0),
            TextStrokeTransparency = 0.5
        },
        -- Direction Indicator
        DirectionIndicator = {
            Size = UDim2.new(0.2, -10, 0.5, -10),
            Position = UDim2.new(0.4, 10, 0.5, 10),
            BackgroundColor = Color3.fromRGB(50, 50, 50),
            BackgroundTransparency = 0.3,
            CornerRadius = 8,
            Image = nil -- Placeholder for directional icons
        },
        -- Toggle Button
        ToggleButton = {
            Size = UDim2.new(0.3, -20, 0.25, -10),
            Position = UDim2.new(0.35, 10, 0.7, 10),
            BackgroundColor = Color3.fromRGB(70, 130, 180),
            BackgroundTransparency = 0.3,
            CornerRadius = 8,
            TextColor = Color3.fromRGB(255, 255, 255),
            TextScaled = true,
            Font = Enum.Font.GothamBold,
            Text = "Disable Auto Block",
            TextStrokeColor = Color3.fromRGB(0, 0, 0),
            TextStrokeTransparency = 0.5
        },
        -- Slap Timer (ESP) Visual Indicator
        SlapTimer = {
            Size = UDim2.new(0.3, -20, 0.05, -10),
            Position = UDim2.new(0.35, 10, 0.8, 10),
            BackgroundColor = Color3.fromRGB(255, 0, 0),
            BackgroundTransparency = 0.3,
            CornerRadius = 8,
            Visible = false,
            TextColor = Color3.fromRGB(255, 255, 255),
            TextScaled = true,
            Font = Enum.Font.GothamSemibold,
            Text = "Slap Cooldown: 0s",
            TextStrokeColor = Color3.fromRGB(0, 0, 0),
            TextStrokeTransparency = 0.5
        }
    },

    -- Debris Settings
    debrisLifetime = 300,            -- Time in seconds before debris is removed (e.g., hitboxes)
}

-- === Inverse Mapping for Attack Types ===
local attackTypesInverse = {}
for attack, id in pairs(Settings.attackTypes) do
    attackTypesInverse[tostring(id)] = attack
end

-- === State Tracking ===
local State = {
    isBlocking = false,
    lastBlockTime = {},         -- [player.UserId] = timestamp
    lastCounterTime = {},       -- [player.UserId] = timestamp
    currentBlockDirection = BLOCK_DIRECTIONS.Forward,
    autoBlockEnabled = Settings.autoBlockEnabled,
    lastSlapBlockTime = {},     -- [player.UserId] = timestamp
    canSlap = {},               -- [player.UserId] = boolean indicating if player can slap
}

-- === UI Elements ===
local UI = {
    ScreenGui = nil,
    BlackBox = nil,
    LoadingBar = nil,
    MessageBox = nil,
    DirectionIndicator = nil,
    ToggleButton = nil,
    SlapTimer = nil
}

-- === Helper Functions ===

-- Utility: Print with timestamp
local function debugPrint(message)
    print(string.format("[%s] %s", os.date("%H:%M:%S"), message))
end

-- UI: Create Rounded Corners for Frames
local function addRoundedCorners(frame, radius)
    local corner = Instance.new("UICorner")
    corner.CornerRadius = UDim.new(0, radius)
    corner.Parent = frame
    return corner
end

-- UI: Setup the User Interface
local function setupUI()
    local screenGui = Instance.new("ScreenGui")
    screenGui.Name = Settings.ui.ScreenGuiName
    screenGui.Parent = LOCAL_PLAYER:WaitForChild("PlayerGui")
    screenGui.ResetOnSpawn = false
    screenGui.DisplayOrder = 1000  -- Ensure it's on top

    -- Black Box Frame
    local blackBox = Instance.new("Frame")
    blackBox.Size = Settings.ui.BlackBox.Size
    blackBox.Position = Settings.ui.BlackBox.Position
    blackBox.BackgroundColor3 = Settings.ui.BlackBox.BackgroundColor
    blackBox.BackgroundTransparency = Settings.ui.BlackBox.BackgroundTransparency
    blackBox.BorderSizePixel = Settings.ui.BlackBox.BorderSizePixel
    blackBox.BorderColor3 = Settings.ui.BlackBox.BorderColor
    blackBox.Parent = screenGui
    addRoundedCorners(blackBox, Settings.ui.BlackBox.CornerRadius)

    -- Loading Bar Background
    local loadingBarBackground = Instance.new("Frame")
    loadingBarBackground.Size = Settings.ui.LoadingBar.Size
    loadingBarBackground.Position = Settings.ui.LoadingBar.Position
    loadingBarBackground.BackgroundColor3 = Color3.fromRGB(50, 50, 50)
    loadingBarBackground.BackgroundTransparency = 0.3
    loadingBarBackground.BorderSizePixel = 0
    loadingBarBackground.Parent = blackBox
    addRoundedCorners(loadingBarBackground, Settings.ui.LoadingBar.CornerRadius or 8)

    -- Loading Bar Foreground
    local loadingBar = Instance.new("Frame")
    loadingBar.Size = UDim2.new(0, 0, 1, 0) -- Start empty
    loadingBar.Position = UDim2.new(0, 0, 0, 0)
    loadingBar.BackgroundColor3 = Settings.ui.LoadingBar.BackgroundColorInactive
    loadingBar.BorderSizePixel = 0
    loadingBar.Parent = loadingBarBackground
    addRoundedCorners(loadingBar, Settings.ui.LoadingBar.CornerRadius or 8)

    -- Message Box
    local messageBox = Instance.new("TextLabel")
    messageBox.Size = Settings.ui.MessageBox.Size
    messageBox.Position = Settings.ui.MessageBox.Position
    messageBox.BackgroundTransparency = Settings.ui.MessageBox.BackgroundTransparency
    messageBox.TextColor3 = Settings.ui.MessageBox.TextColor
    messageBox.TextScaled = Settings.ui.MessageBox.TextScaled
    messageBox.Text = Settings.ui.MessageBox.Text
    messageBox.Font = Settings.ui.MessageBox.Font
    messageBox.TextStrokeColor3 = Settings.ui.MessageBox.TextStrokeColor
    messageBox.TextStrokeTransparency = Settings.ui.MessageBox.TextStrokeTransparency
    messageBox.BorderSizePixel = 0
    messageBox.Parent = blackBox

    -- Direction Indicator
    local directionIndicator = Instance.new("Frame")
    directionIndicator.Size = Settings.ui.DirectionIndicator.Size
    directionIndicator.Position = Settings.ui.DirectionIndicator.Position
    directionIndicator.BackgroundColor3 = Settings.ui.DirectionIndicator.BackgroundColor
    directionIndicator.BackgroundTransparency = Settings.ui.DirectionIndicator.BackgroundTransparency
    directionIndicator.BorderSizePixel = 0
    directionIndicator.Parent = blackBox
    addRoundedCorners(directionIndicator, Settings.ui.DirectionIndicator.CornerRadius or 8)

    -- Direction Indicator Icon
    local directionIcon = Instance.new("ImageLabel")
    directionIcon.Size = UDim2.new(0.8, 0, 0.8, 0)
    directionIcon.Position = UDim2.new(0.1, 0, 0.1, 0)
    directionIcon.BackgroundTransparency = 1
    directionIcon.Image = "rbxassetid://4542562196" -- Placeholder: Forward Arrow Icon
    directionIcon.Visible = true
    directionIcon.Parent = directionIndicator

    -- Toggle Button
    local toggleButton = Instance.new("TextButton")
    toggleButton.Size = Settings.ui.ToggleButton.Size
    toggleButton.Position = Settings.ui.ToggleButton.Position
    toggleButton.BackgroundColor3 = Settings.ui.ToggleButton.BackgroundColor
    toggleButton.BackgroundTransparency = Settings.ui.ToggleButton.BackgroundTransparency
    toggleButton.BorderSizePixel = 0
    toggleButton.TextColor3 = Settings.ui.ToggleButton.TextColor
    toggleButton.TextScaled = Settings.ui.ToggleButton.TextScaled
    toggleButton.Font = Settings.ui.ToggleButton.Font
    toggleButton.Text = Settings.ui.ToggleButton.Text
    toggleButton.TextStrokeColor3 = Settings.ui.ToggleButton.TextStrokeColor
    toggleButton.TextStrokeTransparency = Settings.ui.ToggleButton.TextStrokeTransparency
    toggleButton.Parent = blackBox
    addRoundedCorners(toggleButton, Settings.ui.ToggleButton.CornerRadius or 8)

    -- Toggle Button Hover Effect
    toggleButton.MouseEnter:Connect(function()
        toggleButton.BackgroundColor3 = Color3.fromRGB(100, 149, 237)
    end)
    toggleButton.MouseLeave:Connect(function()
        toggleButton.BackgroundColor3 = State.autoBlockEnabled and Settings.ui.ToggleButton.BackgroundColor or Color3.fromRGB(169, 169, 169)
    end)

    -- Toggle Button Functionality
    toggleButton.MouseButton1Click:Connect(function()
        State.autoBlockEnabled = not State.autoBlockEnabled
        toggleButton.Text = State.autoBlockEnabled and "Disable Auto Block" or "Enable Auto Block"
        toggleButton.BackgroundColor3 = State.autoBlockEnabled and Settings.ui.ToggleButton.BackgroundColor or Color3.fromRGB(169, 169, 169)
        UI.MessageBox.Text = State.autoBlockEnabled and "Auto Block Enabled" or "Auto Block Disabled"
        debugPrint("Auto Block " .. (State.autoBlockEnabled and "Enabled" or "Disabled"))
    end)

    -- Slap Timer (ESP) Visual Indicator
    local slapTimer = Instance.new("TextLabel")
    slapTimer.Size = Settings.ui.SlapTimer.Size
    slapTimer.Position = Settings.ui.SlapTimer.Position
    slapTimer.BackgroundColor3 = Settings.ui.SlapTimer.BackgroundColor
    slapTimer.BackgroundTransparency = Settings.ui.SlapTimer.BackgroundTransparency
    slapTimer.BorderSizePixel = 0
    slapTimer.TextColor3 = Settings.ui.SlapTimer.TextColor
    slapTimer.TextScaled = Settings.ui.SlapTimer.TextScaled
    slapTimer.Font = Settings.ui.SlapTimer.Font
    slapTimer.Text = Settings.ui.SlapTimer.Text
    slapTimer.TextStrokeColor3 = Settings.ui.SlapTimer.TextStrokeColor
    slapTimer.TextStrokeTransparency = Settings.ui.SlapTimer.TextStrokeTransparency
    slapTimer.Visible = Settings.ui.SlapTimer.Visible
    slapTimer.Parent = blackBox
    addRoundedCorners(slapTimer, Settings.ui.SlapTimer.CornerRadius or 8)

    -- Assign UI Elements to the UI Table
    UI.ScreenGui = screenGui
    UI.BlackBox = blackBox
    UI.LoadingBar = loadingBar
    UI.MessageBox = messageBox
    UI.DirectionIndicator = directionIndicator
    UI.ToggleButton = toggleButton
    UI.SlapTimer = slapTimer
end

-- Blocking: Start Blocking
local function startBlocking()
    if not State.isBlocking then
        State.isBlocking = true
        BlockRemote:FireServer()
        UI.MessageBox.Text = "Blocking Active"
        UI.LoadingBar.BackgroundColor3 = Settings.ui.LoadingBar.BackgroundColorActive
        UI.LoadingBar:TweenSize(UDim2.new(1, 0, 1, 0), "Out", "Quad", 0.2, true)
        debugPrint("Blocking started")
    end
end

-- Blocking: Stop Blocking
local function stopBlocking()
    if State.isBlocking then
        State.isBlocking = false
        UnblockRemote:FireServer()
        UI.MessageBox.Text = Settings.autoBlockEnabled and "Auto Block Enabled" or "Auto Block Disabled"
        UI.LoadingBar.BackgroundColor3 = Settings.ui.LoadingBar.BackgroundColorInactive
        UI.LoadingBar:TweenSize(UDim2.new(0, 0, 1, 0), "Out", "Quad", 0.2, true)
        debugPrint("Blocking stopped")
    end
end

-- Blocking: Toggle Blocking State
local function toggleBlocking()
    if State.isBlocking then
        stopBlocking()
    else
        startBlocking()
    end
end

-- Prediction: Predict Enemy Movement
local function predictEnemyMovement(enemy)
    local enemyCharacter = enemy.Character
    if not enemyCharacter then return nil end

    local enemyHRP = enemyCharacter:FindFirstChild("HumanoidRootPart")
    if not enemyHRP then return nil end

    local velocity = enemyHRP.Velocity
    return enemyHRP.Position + (velocity * Settings.predictionFactor)
end

-- Direction: Get Hit Direction Based on Attacker Position
local function getHitDirection(attackerPosition, playerPosition, camLookVector, camRightVector)
    local direction = (attackerPosition - playerPosition).Unit
    local dotProduct = direction:Dot(camLookVector)

    if Settings.aimLockEnabled then
        if dotProduct > math.cos(math.rad(45)) then
            return BLOCK_DIRECTIONS.Forward
        elseif dotProduct < -math.cos(math.rad(45)) then
            return BLOCK_DIRECTIONS.Backward
        else
            return direction:Dot(camRightVector) > 0 and BLOCK_DIRECTIONS.Right or BLOCK_DIRECTIONS.Left
        end
    else
        local angle = math.deg(math.atan2(direction.Z, direction.X)) % 360

        if angle < 45 or angle >= 315 then
            return BLOCK_DIRECTIONS.Forward
        elseif angle < 135 then
            return BLOCK_DIRECTIONS.Right
        elseif angle < 225 then
            return BLOCK_DIRECTIONS.Backward
        else
            return BLOCK_DIRECTIONS.Left
        end
    end
end

-- Direction: Update Block Direction
local function updateBlockDirection(attackerPosition)
    local playerCharacter = LOCAL_PLAYER.Character
    if not playerCharacter then return BLOCK_DIRECTIONS.Forward end

    local playerHRP = playerCharacter:FindFirstChild("HumanoidRootPart")
    if not playerHRP then return BLOCK_DIRECTIONS.Forward end

    local playerPosition = playerHRP.Position
    local camCFrame = CAMERA.CFrame
    local camLookVector = camCFrame.LookVector
    local camRightVector = camCFrame.RightVector

    local desiredDirection = getHitDirection(attackerPosition, playerPosition, camLookVector, camRightVector)

    if desiredDirection ~= State.currentBlockDirection then
        State.currentBlockDirection = desiredDirection
        UpdateBlockDirectionRemote:FireServer(desiredDirection)
        debugPrint(string.format("Block direction updated to %s", desiredDirection))

        -- Update Direction Indicator
        local directionIcons = {
            [BLOCK_DIRECTIONS.Forward] = "rbxassetid://4542562196",  -- Forward Arrow Icon
            [BLOCK_DIRECTIONS.Right] = "rbxassetid://4542562200",    -- Right Arrow Icon (Replace with actual icon ID)
            [BLOCK_DIRECTIONS.Backward] = "rbxassetid://4542562204", -- Backward Arrow Icon (Replace with actual icon ID)
            [BLOCK_DIRECTIONS.Left] = "rbxassetid://4542562208"      -- Left Arrow Icon (Replace with actual icon ID)
        }
        UI.DirectionIndicator:FindFirstChildOfClass("ImageLabel").Image = directionIcons[desiredDirection] or directionIcons[BLOCK_DIRECTIONS.Forward]
    end

    return desiredDirection
end

-- Counter Attacking: Perform Auto Counter
local function performAutoCounter(attacker)
    local attackerId = attacker.UserId
    local currentTime = tick()

    if State.lastCounterTime[attackerId] and (currentTime - State.lastCounterTime[attackerId]) < Settings.counterCooldown then
        return
    end

    -- Perform counter attack
    VirtualInputManager:SendKeyEvent(true, Settings.counterKey, false, game)
    task.delay(0.05, function()
        VirtualInputManager:SendKeyEvent(false, Settings.counterKey, false, game)
    end)

    State.lastCounterTime[attackerId] = currentTime
    debugPrint("Auto counter performed against " .. attacker.Name)
end

-- Counter Attacking: Perform Auto Counter for Slaps
local function counterSlap(attacker)
    local attackerId = attacker.UserId
    local currentTime = tick()

    if State.lastCounterTime[attackerId] and (currentTime - State.lastCounterTime[attackerId]) < Settings.counterCooldown then
        return
    end

    -- Perform counter slap attack
    VirtualInputManager:SendKeyEvent(true, Settings.counterKey, false, game)
    task.delay(0.05, function()
        VirtualInputManager:SendKeyEvent(false, Settings.counterKey, false, game)
    end)

    State.lastCounterTime[attackerId] = currentTime
    debugPrint("Auto counter (Slap) performed against " .. attacker.Name)
end

-- Blocking: Handle Incoming Hit with Enhanced Proximity and Throttling
local function handleIncomingHit(attacker, attackerPosition, attackType)
    local attackerId = attacker.UserId
    local currentTime = tick()

    -- Determine appropriate cooldown and distance based on attack type
    local cooldown = Settings.attackBlockCooldowns[attackType] or Settings.blockCooldown
    local maxDistance = Settings.blockDistances[attackType] or Settings.blockDistances.Default

    -- Check for general cooldown
    if State.lastBlockTime[attackerId] and (currentTime - State.lastBlockTime[attackerId]) < cooldown then
        return
    end

    -- Check distance
    local playerCharacter = LOCAL_PLAYER.Character
    if not playerCharacter then return end

    local playerHRP = playerCharacter:FindFirstChild("HumanoidRootPart")  -- Cached access
    if not playerHRP then return end

    local distance = (playerHRP.Position - attackerPosition).Magnitude
    if distance > maxDistance then
        return
    end

    -- Determine if the attack is a slap for specific handling
    local isSlap = (attackType == "Slap")

    if isSlap then
        -- Handle Slap Blocking with Per-Attacker Cooldown
        local slapCooldown = Settings.slap.slapCooldown

        if not State.canSlap[attackerId] then
            debugPrint("Slap blocked recently from " .. attacker.Name .. ". Ignoring to prevent spam.")
            return
        end

        -- Update slap block state
        State.canSlap[attackerId] = false
        State.lastSlapBlockTime[attackerId] = currentTime

        -- Display Slap Cooldown Timer if ESP is enabled
        if Settings.slap.slapTimerESPEnabled and UI.SlapTimer then
            UI.SlapTimer.Visible = true
            UI.SlapTimer.Text = string.format("Slap Cooldown: %.1fs", slapCooldown)
            -- Update the timer visually
            task.spawn(function()
                local startTime = tick()
                while tick() - startTime < slapCooldown and Settings.slap.slapTimerESPEnabled and UI.SlapTimer.Visible do
                    local timeLeft = math.max(0, slapCooldown - (tick() - startTime))
                    UI.SlapTimer.Text = string.format("Slap Cooldown: %.1fs", timeLeft)
                    task.wait(0.1)
                end
                UI.SlapTimer.Visible = false
            end)
        end
    end

    -- Update last block time for throttling
    State.lastBlockTime[attackerId] = currentTime

    -- Determine reaction delay based on attack type
    local reactionDelay = isSlap and Settings.slap.slapLagCompensation or Settings.reactionTimeThreshold

    task.delay(reactionDelay, function()
        local desiredDirection = updateBlockDirection(attackerPosition)
        startBlocking()

        -- Schedule unblocking with different delays based on attack type
        local unblockDelay = isSlap and Settings.slap.slapLagCompensation or 0.5
        task.delay(unblockDelay, stopBlocking)
    end)

    -- Handle Auto Countering if enabled and within counter distance
    if Settings.autoCounterEnabled and distance <= Settings.autoCounterDistance then
        if isSlap and Settings.slap.autoSlapEnabled and distance <= Settings.slap.autoSlapDistance then
            counterSlap(attacker)
        elseif not isSlap then
            performAutoCounter(attacker)
        end
    end

    -- Reset canSlap after cooldown if it's a slap
    if isSlap then
        task.spawn(function()
            task.wait(Settings.slap.slapCooldown)
            State.canSlap[attackerId] = true
            debugPrint("Slap cooldown ended for " .. attacker.Name)
        end)
    end
end

-- Slap Blocking: Handle Slap Animation
local function handleSlapAnimation(player, animTrack)
    if not Settings.slap.autoSlapEnabled then return end

    local localCharacter = LOCAL_PLAYER.Character
    local enemyCharacter = player.Character

    if not localCharacter or not enemyCharacter then return end

    local localHRP = localCharacter:FindFirstChild("HumanoidRootPart")
    local enemyHRP = enemyCharacter:FindFirstChild("HumanoidRootPart")

    if not localHRP or not enemyHRP then return end

    local distance = (localHRP.Position - enemyHRP.Position).Magnitude

    if distance <= Settings.blockDistances.Slap then 
        handleIncomingHit(player, enemyHRP.Position, "Slap")
    end
end

-- Attack Detection: Handle Animation Played
local function onAnimationPlayed(animTrack, opponentPlayer)
    local anim = animTrack.Animation
    if not anim then return end

    local animId = anim.AnimationId
    if not animId then return end

    -- Extract numeric ID from AnimationId
    local numericAnimId = animId:match("rbxassetid://(%d+)")
    if not numericAnimId then return end
    numericAnimId = tonumber(numericAnimId)

    if not numericAnimId then return end

    local attackType = attackTypesInverse[tostring(numericAnimId)]
    if not attackType then return end

    debugPrint(string.format("Detected attack '%s' from %s", attackType, opponentPlayer.Name))

    -- Handle Slap Animation Separately
    if attackType == "Slap" then
        handleSlapAnimation(opponentPlayer, animTrack)
    else
        local attackerCharacter = opponentPlayer.Character
        if not attackerCharacter then return end

        local attackerHRP = attackerCharacter:FindFirstChild("HumanoidRootPart")
        if not attackerHRP then return end

        handleIncomingHit(opponentPlayer, attackerHRP.Position, attackType)
    end
end

-- Animation Monitoring: Connect Animation Events
local function monitorAnimations(character, opponentPlayer)
    local humanoid = character:FindFirstChildOfClass("Humanoid")
    if not humanoid then return end

    local connection
    connection = humanoid.AnimationPlayed:Connect(function(animTrack)
        onAnimationPlayed(animTrack, opponentPlayer)
    end)

    -- Cleanup when character is removed
    character.AncestryChanged:Connect(function(_, parent)
        if not parent and connection then
            connection:Disconnect()
        end
    end)
end

-- Hitbox Detection: Handle Hitbox Touched
local function onHitboxTouched(part, attackerPlayer)
    if not part or attackerPlayer == LOCAL_PLAYER then return end

    local attackerCharacter = part.Parent
    if not attackerCharacter then return end

    local attackerHRP = attackerCharacter:FindFirstChild("HumanoidRootPart")
    if attackerHRP then
        debugPrint("Hitbox detected attack from " .. attackerPlayer.Name)
        handleIncomingHit(attackerPlayer, attackerHRP.Position, "Default") -- Assuming 'Default' for non-slap attacks
    end
end

-- Hitbox Monitoring: Connect Hitbox Events
local function monitorHitboxes(character, attackerPlayer)
    local hitboxes = CollectionService:GetTagged(ATTACK_HITBOX_TAG)
    for _, hitbox in ipairs(hitboxes) do
        if hitbox:IsDescendantOf(character) then
            local connection
            connection = hitbox.Touched:Connect(function(hitPart)
                onHitboxTouched(hitPart, attackerPlayer)
            end)

            -- Cleanup when hitbox is removed
            hitbox.AncestryChanged:Connect(function(_, parent)
                if not parent and connection then
                    connection:Disconnect()
                end
            end)

            -- Remove hitbox after specified time to prevent memory leaks
            Debris:AddItem(hitbox, Settings.debrisLifetime)
        end
    end
end

-- Advanced Reaction System: Continuous Monitoring with Throttling
local function advancedReactionAutoBlock()
    if not Settings.autoBlockEnabled then return end

    local heartbeatThrottle = 0.05  -- Run logic every 0.05 seconds (20 times per second)
    local accumulatedTime = 0

    RunService.Heartbeat:Connect(function(deltaTime)
        accumulatedTime += deltaTime

        if accumulatedTime < heartbeatThrottle then
            return
        end

        accumulatedTime = accumulatedTime - heartbeatThrottle

        if not Settings.autoBlockEnabled then return end

        local playerCharacter = LOCAL_PLAYER.Character
        if not playerCharacter then return end

        local playerHRP = playerCharacter:FindFirstChild("HumanoidRootPart")
        if not playerHRP then return end

        local playerPosition = playerHRP.Position
        local camCFrame = CAMERA.CFrame
        local camLookVector = camCFrame.LookVector
        local camRightVector = camCFrame.RightVector

        for _, plr in ipairs(Players:GetPlayers()) do
            if plr == LOCAL_PLAYER or not plr.Character then continue end

            local enemyCharacter = plr.Character
            local enemyHRP = enemyCharacter:FindFirstChild("HumanoidRootPart")
            if not enemyHRP then continue end

            local distance = (playerPosition - enemyHRP.Position).Magnitude

            -- Determine maximum relevant distance for this enemy based on attack types
            local maxRelevantDistance = Settings.blockDistances.Default
            for _, attackType in pairs(Settings.attackTypes) do
                local attackDistance = Settings.blockDistances[attackType] or Settings.blockDistances.Default
                if attackDistance > maxRelevantDistance then
                    maxRelevantDistance = attackDistance
                end
            end

            if distance > maxRelevantDistance then continue end

            -- Prediction and Direction Update
            local predictedPosition = predictEnemyMovement(plr) or enemyHRP.Position
            updateBlockDirection(predictedPosition)

            local enemyHumanoid = enemyCharacter:FindFirstChildOfClass("Humanoid")
            if not enemyHumanoid then continue end

            -- Check for active attack animations
            for _, animTrack in ipairs(enemyHumanoid:GetPlayingAnimationTracks()) do
                local anim = animTrack.Animation
                if not anim then continue end

                local animId = anim.AnimationId
                if not animId then continue end

                local numericAnimId = animId:match("rbxassetid://(%d+)")
                if not numericAnimId then continue end
                numericAnimId = tonumber(numericAnimId)

                if not numericAnimId then continue end

                local attackType = attackTypesInverse[tostring(numericAnimId)]
                if not attackType then continue end

                -- Determine if the attack should be considered based on distance
                local requiredDistance = Settings.blockDistances[attackType] or Settings.blockDistances.Default
                if distance > requiredDistance then continue end

                -- Handle Slap Animation Separately
                if attackType == "Slap" then
                    handleSlapAnimation(plr, animTrack)
                else
                    handleIncomingHit(plr, enemyHRP.Position, attackType)
                end
            end
        end
    end)
end

-- Player Monitoring: Handle New Players
local function onPlayerAdded(plr)
    if plr == LOCAL_PLAYER then return end

    local function onCharacterAdded(character)
        monitorAnimations(character, plr)
        monitorHitboxes(character, plr)
        connectCharacterEvents(character)
    end

    plr.CharacterAdded:Connect(onCharacterAdded)

    -- Handle existing character
    if plr.Character then
        onCharacterAdded(plr.Character)
    end
end

-- Initialize Player Monitoring
local function initializePlayerMonitoring()
    for _, plr in ipairs(Players:GetPlayers()) do
        onPlayerAdded(plr)
    end
    Players.PlayerAdded:Connect(onPlayerAdded)
end

-- UI Feedback & Directional Block Update
local function updateUIAndDirection()
    -- Update UI based on blocking state
    local isBlocking = State.isBlocking
    UI.LoadingBar.Size = isBlocking and UDim2.new(1, 0, 1, 0) or UDim2.new(0, 0, 1, 0)
    UI.LoadingBar.BackgroundColor3 = isBlocking and Settings.ui.LoadingBar.BackgroundColorActive or Settings.ui.LoadingBar.BackgroundColorInactive
    UI.MessageBox.Text = isBlocking and "Blocking Active" or (Settings.autoBlockEnabled and "Auto Block Enabled" or "Auto Block Disabled")
end

-- === Swing-Blocking Feature ===

-- Swing-Blocking: Perform Swing and Block
local function performSwingBlock()
    if State.isBlocking then
        debugPrint("Already blocking. Swing-block aborted.")
        return
    end

    -- Start Blocking
    startBlocking()

    -- Simulate Mouse1 Click for Swing
    VirtualInputManager:SendMouseButtonEvent(0, 0, Enum.UserInputType.MouseButton1, true, game, 1) -- MouseButton1 Down
    task.delay(0.05, function()
        VirtualInputManager:SendMouseButtonEvent(0, 0, Enum.UserInputType.MouseButton1, false, game, 1) -- MouseButton1 Up
    end)
    debugPrint("Swing action simulated.")

    -- Maintain Blocking State for 0.7 seconds
    task.delay(0.7, function()
        stopBlocking()
    end)
end

-- Input Handling: Manual Blocking and Swing
local function setupInputHandling()
    UserInputService.InputBegan:Connect(function(input, gameProcessed)
        if gameProcessed then return end

        if input.KeyCode == Enum.KeyCode.Q then
            toggleBlocking()
        elseif input.KeyCode == Enum.KeyCode.R then
            performSwingBlock()
        end
    end)
end

-- Character Cleanup: Handle Character Death or Removal
local function onCharacterRemoved(character)
    stopBlocking()
end

local function connectCharacterEvents(character)
    local humanoid = character:FindFirstChildOfClass("Humanoid")
    if humanoid then
        humanoid.Died:Connect(onCharacterRemoved)
    end

    character.AncestryChanged:Connect(function(_, parent)
        if not parent then
            stopBlocking()
        end
    end)
end

local function setupLocalPlayerCharacterMonitoring()
    local function onLocalCharacterAdded(character)
        connectCharacterEvents(character)
    end

    if LOCAL_PLAYER.Character then
        connectCharacterEvents(LOCAL_PLAYER.Character)
    end

    LOCAL_PLAYER.CharacterAdded:Connect(onLocalCharacterAdded)
end

-- === Initialization ===

-- Setup UI
setupUI()

-- Setup Input Handling
setupInputHandling()

-- Initialize Player Monitoring
initializePlayerMonitoring()

-- Setup Local Player Character Monitoring
setupLocalPlayerCharacterMonitoring()

-- Setup Combined UI Feedback and Directional Block Update
RunService.RenderStepped:Connect(updateUIAndDirection)

-- Initialize Advanced Reaction System
advancedReactionAutoBlock()

-- Notify that the script is running
debugPrint("Autoblock V.3 is now running.")
``