Further enhance this script, and add a more natural, aesthetic, and refined GUI.

```
--[[
    Auto Block V.3 for Saber Showdown
    - Enhanced specifically for handling Slap attacks
    - Optimized reaction timing and prediction for Slaps
    - Improved detection accuracy and cooldown management
    - Dedicated UI indicators for Slap attacks
    - Comprehensive logging for better monitoring
    - Performance optimizations to ensure smooth gameplay
--]]

-- === 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 TweenService = game:GetService("TweenService")
local SoundService = game:GetService("SoundService")
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
}

local LOG_LEVELS = {
    INFO = 1,
    WARNING = 2,
    ERROR = 3
}

-- === 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 = "rbxassetid://12625891544",
        SlappedLegs = "rbxassetid://12671715182",
        SlappedArms = "rbxassetid://12625896358",
        Punch = "rbxassetid://12625897000",         -- Example attack ID
        Kick = "rbxassetid://12625897500",          -- Example attack ID
        KickUp = "rbxassetid://12625897944",        -- Added KickUp
        KickBack = "rbxassetid://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)
    defaultCounterKey = Enum.KeyCode.E, -- Default key to use for countering
    
    -- Reaction Settings
    reactionTimeThreshold = 0.15,    -- Threshold for reaction time (in seconds)
    predictionFactor = 0.6,          -- Factor for predicting enemy movements (0-1)
    
    -- Slap Lag Handling
    slapLagCompensation = 0.1,       -- 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
    
    -- UI Settings
    ui = {
        ScreenGuiName = "AutoBlockUI",
        BlackBoxSize = UDim2.new(0.2, 0, 0.1, 0),
        BlackBoxPosition = UDim2.new(0.8, 0, 0.9, 0),
        BlackBoxColor = Color3.fromRGB(0, 0, 0),
        BlackBoxTransparency = 0.5,
        LoadingBarSize = UDim2.new(1, 0, 0.2, 0),
        LoadingBarPosition = UDim2.new(0, 0, -0.2, 0),
        LoadingBarColorActive = Color3.fromRGB(0, 255, 0),
        LoadingBarColorInactive = Color3.fromRGB(255, 0, 0),
        LoadingBarTransparency = 0.3,
        MessageBoxTextActive = "Blocking Active",
        MessageBoxTextInactive = "Press 'Q' to toggle Auto Block",
        MessageBoxColor = Color3.fromRGB(255, 255, 255),
        MessageBoxTransparency = 1,
        DirectionIndicator = {         -- Direction Indicator UI
            Size = UDim2.new(0, 50, 0, 50),
            Position = UDim2.new(0.5, -25, 0.5, -25),
            Color = Color3.fromRGB(255, 255, 0),
            Transparency = 0.7,
            ImageId = "rbxassetid://6023426910" -- Example arrow image
        },
        ConfigButton = {               -- Configuration Button UI
            Size = UDim2.new(0, 100, 0, 50),
            Position = UDim2.new(0, 10, 0, 10),
            Color = Color3.fromRGB(0, 0, 255),
            Transparency = 0.5,
            Text = "Settings",
            TextColor = Color3.fromRGB(255, 255, 255)
        },
        ConfigPanel = {                 -- Configuration Panel UI
            Size = UDim2.new(0.3, 0, 0.6, 0),
            Position = UDim2.new(0.35, 0, 0.2, 0),
            Color = Color3.fromRGB(50, 50, 50),
            Transparency = 0.8,
            Title = "Auto Block Settings",
            Elements = {}                    -- Will hold dynamic UI elements
        },
        LogLevelIndicator = {           -- Log Level Indicator UI
            Size = UDim2.new(0, 150, 0, 30),
            Position = UDim2.new(0.5, -75, 0, 10),
            Color = Color3.fromRGB(255, 255, 255),
            Transparency = 0.5,
            Text = "Log Level: Info",
            TextColor = Color3.fromRGB(0, 0, 0)
        },
        StatisticsPanel = {             -- Statistics Panel UI
            Size = UDim2.new(0.2, 0, 0.15, 0),
            Position = UDim2.new(0.75, 0, 0.75, 0),
            Color = Color3.fromRGB(30, 30, 30),
            Transparency = 0.7,
            Title = "Stats",
            Elements = {}                    -- Will hold dynamic UI elements
        },
        SoundToggle = {                 -- Sound Effects Toggle
            Size = UDim2.new(0.8, 0, 0, 30),
            Position = UDim2.new(0.1, 0, 0.6, 0),
            Color = Color3.fromRGB(70, 70, 70),
            BackgroundTransparency = 0.2,
            Text = "Sound Effects: On",
            TextColor = Color3.fromRGB(255, 255, 255)
        }
    },
    
    -- Debris Settings
    debrisLifetime = 300,            -- Time in seconds before debris is removed (e.g., hitboxes)
    
    -- Logging Settings
    logLevel = LOG_LEVELS.INFO,      -- Current logging level
    
    -- Statistics Tracking
    statistics = {
        blocks = 0,
        counters = 0,
        blocksPerAttack = {},          -- [attackType] = count
        countersPerAttack = {}         -- [attackType] = count
    },
    
    -- Sound Settings
    sounds = {
        blockStart = "rbxassetid://YourBlockStartSoundID",
        blockStop = "rbxassetid://YourBlockStopSoundID",
        counter = "rbxassetid://YourCounterSoundID"
    }
}

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

-- === State Tracking ===
local State = {
    isBlocking = false,
    lastBlockTime = {},         -- [player.UserId] = timestamp
    lastCounterTime = {},       -- [player.UserId] = timestamp
    currentBlockDirection = BLOCK_DIRECTIONS.Forward,
    isConfigOpen = false,
    isSoundEnabled = true,
    counterKey = Settings.defaultCounterKey
}

-- === UI Elements ===
local UI = {
    ScreenGui = nil,
    BlackBox = nil,
    LoadingBar = nil,
    MessageBox = nil,
    DirectionIndicator = nil,
    ConfigButton = nil,
    ConfigPanel = nil,
    LogLevelIndicator = nil,
    StatisticsPanel = nil,
    SoundToggle = nil,
    LogDisplay = nil,
    ExportLogButton = nil,
    blocksLabel = nil,
    countersLabel = nil
}

-- === Helper Functions ===

-- Utility: Log with timestamp and level
local function log(message, level)
    level = level or LOG_LEVELS.INFO
    if level < Settings.logLevel then return end
    local prefix
    if level == LOG_LEVELS.INFO then
        prefix = "[INFO]"
    elseif level == LOG_LEVELS.WARNING then
        prefix = "[WARNING]"
    elseif level == LOG_LEVELS.ERROR then
        prefix = "[ERROR]"
    else
        prefix = "[LOG]"
    end
    local timestamp = os.date("%H:%M:%S")
    local logMessage = string.format("[%s] [%s] %s", timestamp, prefix, message)
    print(logMessage)
    
    -- Append to in-game log display
    if UI.LogDisplay then
        UI.LogDisplay.Text = UI.LogDisplay.Text .. "\n" .. logMessage
    end
end

-- UI: Create a new TextLabel
local function createTextLabel(parent, size, position, color, transparency, text, textColor, textScaled, font, textSize)
    local label = Instance.new("TextLabel")
    label.Size = size
    label.Position = position
    label.BackgroundColor3 = color
    label.BackgroundTransparency = transparency
    label.Text = text
    label.TextColor3 = textColor
    label.TextScaled = textScaled
    label.Font = font or Enum.Font.SourceSans
    label.TextSize = textSize or 14
    label.BorderSizePixel = 0
    label.Parent = parent
    return label
end

-- UI: Create a new TextButton
local function createButton(parent, size, position, color, transparency, text, textColor, textScaled)
    local button = Instance.new("TextButton")
    button.Size = size
    button.Position = position
    button.BackgroundColor3 = color
    button.BackgroundTransparency = transparency
    button.Text = text
    button.TextColor3 = textColor
    button.TextScaled = textScaled
    button.BorderSizePixel = 0
    button.Parent = parent
    return button
end

-- UI: Create a new Frame
local function createFrame(parent, size, position, color, transparency)
    local frame = Instance.new("Frame")
    frame.Size = size
    frame.Position = position
    frame.BackgroundColor3 = color
    frame.BackgroundTransparency = transparency
    frame.BorderSizePixel = 0
    frame.Parent = parent
    return frame
end

-- UI: Setup the User Interface
local function setupUI()
    -- Create ScreenGui
    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 = createFrame(
        screenGui, 
        Settings.ui.BlackBoxSize, 
        Settings.ui.BlackBoxPosition, 
        Settings.ui.BlackBoxColor, 
        Settings.ui.BlackBoxTransparency
    )
    
    -- Loading Bar
    local loadingBar = Instance.new("Frame")
    loadingBar.Size = Settings.ui.LoadingBarSize
    loadingBar.Position = Settings.ui.LoadingBarPosition
    loadingBar.BackgroundColor3 = Settings.ui.LoadingBarColorInactive
    loadingBar.BackgroundTransparency = Settings.ui.LoadingBarTransparency
    loadingBar.BorderSizePixel = 0
    loadingBar.Parent = blackBox

    -- Message Box
    local messageBox = createTextLabel(
        blackBox, 
        UDim2.new(1, 0, 1, 0), 
        UDim2.new(0, 0, 0, 0), 
        Color3.new(1,1,1), 
        Settings.ui.MessageBoxTransparency, 
        Settings.ui.MessageBoxTextInactive, 
        Settings.ui.MessageBoxColor, 
        true,
        Enum.Font.SourceSans,
        14
    )

    -- Direction Indicator
    local directionIndicator = Instance.new("ImageLabel")
    directionIndicator.Size = Settings.ui.DirectionIndicator.Size
    directionIndicator.Position = Settings.ui.DirectionIndicator.Position
    directionIndicator.BackgroundTransparency = 1
    directionIndicator.Image = Settings.ui.DirectionIndicator.ImageId
    directionIndicator.ImageColor3 = Settings.ui.DirectionIndicator.Color
    directionIndicator.ImageTransparency = Settings.ui.DirectionIndicator.Transparency
    directionIndicator.AnchorPoint = Vector2.new(0.5, 0.5)
    directionIndicator.Parent = screenGui
    directionIndicator.Visible = false -- Initially hidden

    -- Configuration Button
    local configButton = createButton(
        screenGui, 
        Settings.ui.ConfigButton.Size, 
        Settings.ui.ConfigButton.Position, 
        Settings.ui.ConfigButton.Color, 
        Settings.ui.ConfigButton.Transparency, 
        Settings.ui.ConfigButton.Text, 
        Settings.ui.ConfigButton.TextColor, 
        true
    )

    -- Configuration Panel
    local configPanel = createFrame(
        screenGui, 
        Settings.ui.ConfigPanel.Size, 
        Settings.ui.ConfigPanel.Position, 
        Settings.ui.ConfigPanel.Color, 
        Settings.ui.ConfigPanel.Transparency
    )
    configPanel.Visible = false -- Initially hidden

    -- Configuration Panel Title
    local configTitle = createTextLabel(
        configPanel, 
        UDim2.new(1, 0, 0, 50), 
        UDim2.new(0, 0, 0, 0), 
        Color3.new(1,1,1), 
        1, 
        Settings.ui.ConfigPanel.Title, 
        Color3.fromRGB(0,0,0), 
        true,
        Enum.Font.SourceSansBold,
        24
    )

    -- Log Level Indicator
    local logLevelIndicator = createTextLabel(
        screenGui, 
        Settings.ui.LogLevelIndicator.Size, 
        Settings.ui.LogLevelIndicator.Position, 
        Settings.ui.LogLevelIndicator.Color, 
        Settings.ui.LogLevelIndicator.Transparency, 
        "Log Level: Info", 
        Settings.ui.LogLevelIndicator.TextColor, 
        true
    )

    -- Statistics Panel
    local statisticsPanel = createFrame(
        screenGui, 
        Settings.ui.StatisticsPanel.Size, 
        Settings.ui.StatisticsPanel.Position, 
        Settings.ui.StatisticsPanel.Color, 
        Settings.ui.StatisticsPanel.Transparency
    )
    statisticsPanel.Visible = true -- Always visible

    -- Statistics Panel Title
    local statsTitle = createTextLabel(
        statisticsPanel, 
        UDim2.new(1, 0, 0, 30), 
        UDim2.new(0, 0, 0, 0), 
        Color3.new(1,1,1), 
        1, 
        Settings.ui.StatisticsPanel.Title, 
        Color3.fromRGB(0,0,0), 
        true,
        Enum.Font.SourceSansBold,
        18
    )

    -- Statistics Labels
    local blocksLabel = createTextLabel(
        statisticsPanel, 
        UDim2.new(0.9, 0, 0, 20), 
        UDim2.new(0.05, 0, 0.2, 0), 
        Color3.new(1,1,1), 
        1, 
        "Blocks: 0", 
        Color3.fromRGB(255,255,255), 
        true,
        Enum.Font.SourceSans,
        14
    )

    local countersLabel = createTextLabel(
        statisticsPanel, 
        UDim2.new(0.9, 0, 0, 20), 
        UDim2.new(0.05, 0, 0.4, 0), 
        Color3.new(1,1,1), 
        1, 
        "Counters: 0", 
        Color3.fromRGB(255,255,255), 
        true,
        Enum.Font.SourceSans,
        14
    )

    -- Sound Effects Toggle
    local soundToggle = createButton(
        configPanel, 
        Settings.ui.SoundToggle.Size, 
        Settings.ui.SoundToggle.Position, 
        Settings.ui.SoundToggle.Color, 
        Settings.ui.SoundToggle.BackgroundTransparency, 
        Settings.ui.SoundToggle.Text, 
        Settings.ui.SoundToggle.TextColor, 
        true
    )

    -- Log Display (Scrollable TextBox)
    local logDisplay = Instance.new("ScrollingFrame")
    logDisplay.Size = UDim2.new(1, -20, 0.4, -50)
    logDisplay.Position = UDim2.new(0, 10, 0.3, 0)
    logDisplay.BackgroundTransparency = 1
    logDisplay.BorderSizePixel = 0
    logDisplay.ScrollBarThickness = 5
    logDisplay.Parent = configPanel

    local logText = Instance.new("TextLabel")
    logText.Size = UDim2.new(1, 0, 1, 0)
    logText.Position = UDim2.new(0, 0, 0, 0)
    logText.BackgroundTransparency = 1
    logText.Text = ""
    logText.TextColor3 = Color3.fromRGB(255, 255, 255)
    logText.TextSize = 14
    logText.TextWrapped = true
    logText.Parent = logDisplay

    -- Export Log Button
    local exportLogButton = createButton(
        configPanel, 
        UDim2.new(0.5, 0, 0, 30), 
        UDim2.new(0.25, 0, 0.8, 0), 
        Color3.fromRGB(70, 70, 70), 
        0.2, 
        "Export Logs", 
        Color3.fromRGB(255, 255, 255), 
        true
    )

    -- Assign to UI table
    UI.ScreenGui = screenGui
    UI.BlackBox = blackBox
    UI.LoadingBar = loadingBar
    UI.MessageBox = messageBox
    UI.DirectionIndicator = directionIndicator
    UI.ConfigButton = configButton
    UI.ConfigPanel = configPanel
    UI.LogLevelIndicator = logLevelIndicator
    UI.StatisticsPanel = statisticsPanel
    UI.SoundToggle = soundToggle
    UI.LogDisplay = logText
    UI.ExportLogButton = exportLogButton
    UI.blocksLabel = blocksLabel
    UI.countersLabel = countersLabel

    -- Setup Configuration Panel Elements (Dynamic)

    -- Auto Block Toggle
    local autoBlockToggle = createButton(
        configPanel, 
        UDim2.new(0.8, 0, 0, 30), 
        UDim2.new(0.1, 0, 0.1, 0), 
        Color3.fromRGB(70, 70, 70), 
        0.2, 
        "Auto Block: On", 
        Color3.fromRGB(255, 255, 255), 
        true
    )

    autoBlockToggle.MouseButton1Click:Connect(function()
        Settings.autoBlockEnabled = not Settings.autoBlockEnabled
        autoBlockToggle.Text = "Auto Block: " .. (Settings.autoBlockEnabled and "On" or "Off")
        log("Auto Block " .. (Settings.autoBlockEnabled and "Enabled" or "Disabled"), LOG_LEVELS.INFO)
    end)

    -- Customizable Counter Key
    local counterKeyButton = createButton(
        configPanel, 
        UDim2.new(0.8, 0, 0, 30), 
        UDim2.new(0.1, 0, 0.3, 0), 
        Color3.fromRGB(70, 70, 70), 
        0.2, 
        "Counter Key: " .. State.counterKey.Name, 
        Color3.fromRGB(255, 255, 255), 
        true
    )

    counterKeyButton.MouseButton1Click:Connect(function()
        log("Press a new key for Countering...", LOG_LEVELS.INFO)
        local connection
        connection = UserInputService.InputBegan:Connect(function(input, gameProcessed)
            if gameProcessed then return end
            if input.UserInputType == Enum.UserInputType.Keyboard then
                State.counterKey = input.KeyCode
                counterKeyButton.Text = "Counter Key: " .. State.counterKey.Name
                log("Counter Key changed to " .. State.counterKey.Name, LOG_LEVELS.INFO)
                connection:Disconnect()
            end
        end)
    end)

    -- Statistics Panel Setup
    UI.blocksLabel.Text = "Blocks: 0"
    UI.countersLabel.Text = "Counters: 0"

    -- Sound Effects Toggle
    soundToggle.MouseButton1Click:Connect(function()
        State.isSoundEnabled = not State.isSoundEnabled
        soundToggle.Text = "Sound Effects: " .. (State.isSoundEnabled and "On" or "Off")
        log("Sound Effects " .. (State.isSoundEnabled and "Enabled" or "Disabled"), LOG_LEVELS.INFO)
    end)

    -- Export Log Button
    exportLogButton.MouseButton1Click:Connect(function()
        local logs = UI.LogDisplay.Text
        if #logs == 0 then
            log("No logs to export.", LOG_LEVELS.WARNING)
            return
        end
        -- Encode logs to JSON
        local encodedLogs = game:GetService("HttpService"):JSONEncode({logs = logs})
        -- Implement actual export logic here (e.g., send to server or save to file)
        -- For demonstration, we'll print the encoded logs.
        print("Exporting Logs:", encodedLogs)
        log("Logs have been exported.", LOG_LEVELS.INFO)
    end)
end

-- Blocking: Start Blocking
local function startBlocking()
    if not State.isBlocking then
        State.isBlocking = true
        BlockRemote:FireServer()
        UI.MessageBox.Text = Settings.ui.MessageBoxTextActive
        UI.LoadingBar.BackgroundColor3 = Settings.ui.LoadingBarColorActive
        UI.LoadingBar:TweenSizeAndPosition(
            Settings.ui.LoadingBarSize,
            Settings.ui.LoadingBarPosition,
            "Out",
            "Quad",
            0.2,
            true
        )
        -- Play block start sound
        if State.isSoundEnabled and Settings.sounds.blockStart then
            local sound = Instance.new("Sound")
            sound.SoundId = Settings.sounds.blockStart
            sound.Volume = 0.5
            sound.Parent = SoundService
            sound:Play()
            Debris:AddItem(sound, sound.TimeLength)
        end
        log("Blocking started", LOG_LEVELS.INFO)
        -- Update Statistics
        Settings.statistics.blocks = Settings.statistics.blocks + 1
        UI.blocksLabel.Text = "Blocks: " .. Settings.statistics.blocks
    end
end

-- Blocking: Stop Blocking
local function stopBlocking()
    if State.isBlocking then
        State.isBlocking = false
        UnblockRemote:FireServer()
        UI.MessageBox.Text = Settings.ui.MessageBoxTextInactive
        UI.LoadingBar.BackgroundColor3 = Settings.ui.LoadingBarColorInactive
        UI.LoadingBar:TweenSizeAndPosition(
            Settings.ui.LoadingBarSize,
            Settings.ui.LoadingBarPosition,
            "Out",
            "Quad",
            0.2,
            true
        )
        -- Play block stop sound
        if State.isSoundEnabled and Settings.sounds.blockStop then
            local sound = Instance.new("Sound")
            sound.SoundId = Settings.sounds.blockStop
            sound.Volume = 0.5
            sound.Parent = SoundService
            sound:Play()
            Debris:AddItem(sound, sound.TimeLength)
        end
        log("Blocking stopped", LOG_LEVELS.INFO)
    end
end

-- Blocking: Toggle Blocking State with Debounce
local debounceToggle = false
local function toggleBlocking()
    if debounceToggle then return end
    debounceToggle = true
    if State.isBlocking then
        stopBlocking()
    else
        startBlocking()
    end
    task.delay(0.2, function()
        debounceToggle = false
    end)
end

-- Prediction: Predict Enemy Movement with Enhanced Algorithm
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
    -- Future Enhancement: Incorporate acceleration if data is available
    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.X, direction.Z)) % 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 with Smooth Transitions
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 camLookVector = CAMERA.CFrame.LookVector
    local camRightVector = CAMERA.CFrame.RightVector

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

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

        -- Update Direction Indicator
        UI.DirectionIndicator.Visible = true
        local rotation
        if desiredDirection == BLOCK_DIRECTIONS.Forward then
            rotation = 0
        elseif desiredDirection == BLOCK_DIRECTIONS.Right then
            rotation = 90
        elseif desiredDirection == BLOCK_DIRECTIONS.Backward then
            rotation = 180
        elseif desiredDirection == BLOCK_DIRECTIONS.Left then
            rotation = -90
        end
        UI.DirectionIndicator.Rotation = rotation

        -- Hide after a short duration
        task.delay(1, function()
            UI.DirectionIndicator.Visible = false
        end)
    end

    return desiredDirection
end

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

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

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

    State.lastCounterTime[attackerId] = currentTime
    log("Auto counter performed against " .. attacker.Name, LOG_LEVELS.INFO)
    -- Play counter sound
    if State.isSoundEnabled and Settings.sounds.counter then
        local sound = Instance.new("Sound")
        sound.SoundId = Settings.sounds.counter
        sound.Volume = 0.5
        sound.Parent = SoundService
        sound:Play()
        Debris:AddItem(sound, sound.TimeLength)
    end
    -- Update Statistics
    Settings.statistics.counters = Settings.statistics.counters + 1
    UI.countersLabel.Text = "Counters: " .. Settings.statistics.counters
    -- Update counters per attack type
    Settings.statistics.countersPerAttack[attackType] = (Settings.statistics.countersPerAttack[attackType] or 0) + 1
end

-- Blocking: Handle Incoming Hit with Enhanced Proximity and Slap-Specific Handling
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 cooldown
    if State.lastBlockTime[attackerId] and (currentTime - State.lastBlockTime[attackerId]) < cooldown then
        log("Block on cooldown for " .. attacker.Name, LOG_LEVELS.WARNING)
        return
    end

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

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

    local distance = (playerHRP.Position - attackerPosition).Magnitude
    if distance > maxDistance then
        log("Attacker " .. attacker.Name .. " is too far: " .. tostring(distance), LOG_LEVELS.INFO)
        return
    end

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

    -- Determine if the attack is a slap for specific handling
    local isSlap = (attackType == "Slap")
    
    -- Adjust reaction delay based on attack type
    local reactionDelay = isSlap and Settings.slapLagCompensation or math.min(math.random() * Settings.reactionTimeThreshold, Settings.reactionTimeThreshold)

    task.delay(reactionDelay, function()
        updateBlockDirection(attackerPosition)
        startBlocking()

        -- Schedule unblocking with different delays based on attack type
        local unblockDelay = isSlap and Settings.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
        performAutoCounter(attacker, attackType)
    end

    -- Update Statistics
    Settings.statistics.blocks = Settings.statistics.blocks + 1
    UI.blocksLabel.Text = "Blocks: " .. Settings.statistics.blocks
    Settings.statistics.blocksPerAttack[attackType] = (Settings.statistics.blocksPerAttack[attackType] or 0) + 1

    -- Additional Slap-Specific Handling
    if isSlap then
        log("Slap attack handled from " .. attacker.Name, LOG_LEVELS.INFO)
        -- Future Enhancement: Add visual effects or specific reactions for slaps
    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

    local attackType = Settings.attackTypesInverse and Settings.attackTypesInverse[numericAnimId]
    if not attackType then return end

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

    local attackerHRP = opponentPlayer.Character and opponentPlayer.Character:FindFirstChild("HumanoidRootPart")
    if attackerHRP then
        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 = 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
        log("Hitbox detected attack from " .. attackerPlayer.Name, LOG_LEVELS.INFO)
        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 = hitbox.Touched:Connect(function(hitPart)
                onHitboxTouched(hitPart, attackerPlayer)
            end)

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

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

-- Statistics Update: Refresh Statistics Panel
local function updateStatisticsPanel()
    if not UI.StatisticsPanel then return end
    -- Update blocks and counters
    UI.blocksLabel.Text = "Blocks: " .. Settings.statistics.blocks
    UI.countersLabel.Text = "Counters: " .. Settings.statistics.counters
    -- Future enhancements: Display per attack type stats
end

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

    local lastHeartbeat = 0
    local heartbeatThrottle = 0.05  -- Throttle to run logic every 0.05 seconds (20 times per second)

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

        if lastHeartbeat < heartbeatThrottle then
            return
        end

        lastHeartbeat = 0

        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 camLookVector = CAMERA.CFrame.LookVector
        local camRightVector = CAMERA.CFrame.RightVector

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

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

            local distance = (playerPosition - enemyHRP.Position).Magnitude
            if distance > (Settings.blockDistances.Default) then continue end

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

            -- Early continue if no Humanoid found
            local enemyHumanoid = plr.Character: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

                local attackType = Settings.attackTypesInverse and Settings.attackTypesInverse[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

                handleIncomingHit(plr, enemyHRP.Position, attackType)
            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)
    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.BackgroundColor3 = isBlocking and Settings.ui.LoadingBarColorActive or Settings.ui.LoadingBarColorInactive
    UI.MessageBox.Text = isBlocking and Settings.ui.MessageBoxTextActive or Settings.ui.MessageBoxTextInactive

    -- Optional: Update animations or transitions for UI elements here
end

-- Input Handling: Manual Blocking Toggle and Configurations
local function setupInputHandling()
    -- Manual Blocking Toggle
    UserInputService.InputBegan:Connect(function(input, gameProcessed)
        if gameProcessed then return end
        if input.KeyCode == Enum.KeyCode.Q then
            toggleBlocking()
        end
    end)

    -- Close configuration panel with Escape key
    UserInputService.InputBegan:Connect(function(input, gameProcessed)
        if gameProcessed then return end
        if input.KeyCode == Enum.KeyCode.Escape and State.isConfigOpen then
            State.isConfigOpen = false
            UI.ConfigPanel.Visible = false
            UI.ConfigButton.Text = "Settings"
            log("Configuration panel closed.", LOG_LEVELS.INFO)
        end
    end)

    -- Configuration Button Event
    UI.ConfigButton.MouseButton1Click:Connect(function()
        State.isConfigOpen = not State.isConfigOpen
        UI.ConfigPanel.Visible = State.isConfigOpen
        UI.ConfigButton.Text = State.isConfigOpen and "Close" or "Settings"
        log("Configuration panel " .. (State.isConfigOpen and "opened" or "closed") .. ".", LOG_LEVELS.INFO)
    end)
end

-- Logging: Update Log Level Indicator
local function updateLogLevelIndicator()
    local levelText
    if Settings.logLevel == LOG_LEVELS.INFO then
        levelText = "Info"
    elseif Settings.logLevel == LOG_LEVELS.WARNING then
        levelText = "Warning"
    elseif Settings.logLevel == LOG_LEVELS.ERROR then
        levelText = "Error"
    else
        levelText = "Unknown"
    end
    UI.LogLevelIndicator.Text = "Log Level: " .. levelText
end

-- UI: Setup Logging Controls in Configuration Panel
local function setupLoggingControls()
    -- Dropdown for Log Levels
    local logLevelDropdown = createButton(
        UI.ConfigPanel, 
        UDim2.new(0.8, 0, 0, 30), 
        UDim2.new(0.1, 0, 0.5, 0), 
        Color3.fromRGB(70, 70, 70), 
        0.2, 
        "Log Level: Info", 
        Color3.fromRGB(255, 255, 255), 
        true
    )

    logLevelDropdown.MouseButton1Click:Connect(function()
        if Settings.logLevel == LOG_LEVELS.INFO then
            Settings.logLevel = LOG_LEVELS.WARNING
            logLevelDropdown.Text = "Log Level: Warning"
        elseif Settings.logLevel == LOG_LEVELS.WARNING then
            Settings.logLevel = LOG_LEVELS.ERROR
            logLevelDropdown.Text = "Log Level: Error"
        else
            Settings.logLevel = LOG_LEVELS.INFO
            logLevelDropdown.Text = "Log Level: Info"
        end
        updateLogLevelIndicator()
        log("Log level changed to " .. logLevelDropdown.Text:match("Log Level: (.+)"), LOG_LEVELS.INFO)
    end)
end

-- UI: Setup Configuration Controls
local function setupConfigurationControls()
    -- Setup Logging Controls
    setupLoggingControls()
end

-- UI: Setup Statistics Controls (Future Enhancements Placeholder)
local function setupStatisticsControls()
    -- Future enhancements: Add per-attack-type statistics display
end

-- UI: Setup Sound Controls
local function setupSoundControls()
    -- Sound Effects Toggle already set up in setupUI()
    -- Additional sound settings can be added here
end

-- UI: Setup All Configuration Controls
local function setupAllConfigControls()
    setupConfigurationControls()
    setupStatisticsControls()
    setupSoundControls()
end

-- Export Logs Functionality (Placeholder)
local function exportLogs()
    local logs = UI.LogDisplay.Text
    if #logs == 0 then
        log("No logs to export.", LOG_LEVELS.WARNING)
        return
    end
    -- Implement actual export logic here (e.g., send to server or save to file)
    -- For demonstration, we'll print them
    print("Exporting Logs:", logs)
    log("Logs have been exported.", LOG_LEVELS.INFO)
end

-- Character Cleanup: Handle Character Death or Removal
local function onCharacterRemoved()
    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 All Configuration Controls
setupAllConfigControls()

-- 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
log("Auto Block V.3 is now running with enhanced Slap functions.", LOG_LEVELS.INFO)

-- === End of Script ===