local Players = game:GetService("Players")
local VirtualInputManager = game:GetService("VirtualInputManager")
local Player = Players.LocalPlayer

local IsParried = false
local Connection = nil

-- Visual elements
local highlightFolder = Instance.new("Folder")
highlightFolder.Name = "BallHighlights"
highlightFolder.Parent = workspace

-- Create speed indicator UI
local speedIndicator = Instance.new("ScreenGui")
speedIndicator.Name = "SpeedIndicator"
speedIndicator.ResetOnSpawn = false
speedIndicator.Parent = Player:WaitForChild("PlayerGui")

local speedFrame = Instance.new("Frame")
speedFrame.Name = "SpeedFrame"
speedFrame.Size = UDim2.new(0, 150, 0, 50)
speedFrame.Position = UDim2.new(0.85, 0, 0.1, 0) -- Top right corner
speedFrame.BackgroundColor3 = Color3.fromRGB(0, 0, 0)
speedFrame.BackgroundTransparency = 0.5
speedFrame.BorderSizePixel = 2
speedFrame.BorderColor3 = Color3.fromRGB(255, 0, 0)
speedFrame.Parent = speedIndicator

local speedLabel = Instance.new("TextLabel")
speedLabel.Name = "SpeedLabel"
speedLabel.Size = UDim2.new(1, 0, 1, 0)
speedLabel.BackgroundTransparency = 1
speedLabel.Text = "0 Sp/s"
speedLabel.TextColor3 = Color3.fromRGB(255, 255, 255)
speedLabel.TextStrokeTransparency = 0
speedLabel.TextStrokeColor3 = Color3.fromRGB(0, 0, 0)
speedLabel.Font = Enum.Font.SourceSansBold
speedLabel.TextScaled = true
speedLabel.Parent = speedFrame

-- NEW: Create trajectory prediction UI
local trajectoryFolder = Instance.new("Folder")
trajectoryFolder.Name = "TrajectoryVisuals"
trajectoryFolder.Parent = workspace

-- NEW: Create player analysis UI
local analysisFrame = Instance.new("Frame")
analysisFrame.Name = "AnalysisFrame"
analysisFrame.Size = UDim2.new(0, 200, 0, 150)
analysisFrame.Position = UDim2.new(0.01, 0, 0.8, 0) -- Left side of screen
analysisFrame.BackgroundColor3 = Color3.fromRGB(0, 0, 0)
analysisFrame.BackgroundTransparency = 0.5
analysisFrame.BorderSizePixel = 2
analysisFrame.BorderColor3 = Color3.fromRGB(0, 255, 255)
analysisFrame.Visible = true
analysisFrame.Parent = speedIndicator

local analysisTitle = Instance.new("TextLabel")
analysisTitle.Name = "AnalysisTitle"
analysisTitle.Size = UDim2.new(1, 0, 0.2, 0)
analysisTitle.Position = UDim2.new(0, 0, 0, 0)
analysisTitle.BackgroundTransparency = 0.3
analysisTitle.BackgroundColor3 = Color3.fromRGB(0, 0, 50)
analysisTitle.Text = "PLAYER ANALYSIS"
analysisTitle.TextColor3 = Color3.fromRGB(255, 255, 255)
analysisTitle.TextStrokeTransparency = 0
analysisTitle.TextStrokeColor3 = Color3.fromRGB(0, 0, 0)
analysisTitle.Font = Enum.Font.SourceSansBold
analysisTitle.TextScaled = true
analysisTitle.Parent = analysisFrame

local analysisContent = Instance.new("TextLabel")
analysisContent.Name = "AnalysisContent"
analysisContent.Size = UDim2.new(1, 0, 0.8, 0)
analysisContent.Position = UDim2.new(0, 0, 0.2, 0)
analysisContent.BackgroundTransparency = 1
analysisContent.Text = "Collecting data..."
analysisContent.TextColor3 = Color3.fromRGB(255, 255, 255)
analysisContent.TextStrokeTransparency = 0
analysisContent.TextStrokeColor3 = Color3.fromRGB(0, 0, 0)
analysisContent.Font = Enum.Font.SourceSans
analysisContent.TextSize = 14
analysisContent.TextWrapped = true
analysisContent.TextXAlignment = Enum.TextXAlignment.Left
analysisContent.TextYAlignment = Enum.TextYAlignment.Top
analysisContent.Parent = analysisFrame

-- ParrySystem module integrated directly
local ParrySystem = {}

-- Configure parry window based on ball speed
function ParrySystem:GetParryWindow(ballSpeed)
    local parryWindow = 0.4
    
    if ballSpeed > 200 and ballSpeed <= 300 then
        parryWindow = 0.5
    elseif ballSpeed > 300 and ballSpeed <= 400 then
        parryWindow = 1.3
    elseif ballSpeed > 400 and ballSpeed <= 600 then
        parryWindow = 0.3
    elseif ballSpeed > 600 then
        parryWindow = 0.3
    end
    
    return parryWindow
end

-- Less aggressive parry timing
function ParrySystem:ShouldParry(ballSpeed, distanceToPlayer)
    local parryWindow = self:GetParryWindow(ballSpeed)
    local estimatedTimeToReach = distanceToPlayer / ballSpeed
    
    if ballSpeed > 400 then
        return estimatedTimeToReach <= (parryWindow * 1.0)
    else
        return estimatedTimeToReach <= (parryWindow * 1.0)
    end
end

-- Special function for ultra-fast 1v1 scenarios
function ParrySystem:Is1v1FastBall(ballSpeed, targetName)
    return ballSpeed > 500 and targetName == Player.Name
end

local TrajectorySystem = {}
local trajectoryPoints = {}
local maxTrajectoryPoints = 20
local trajectoryLifetime = 3

-- Calculate bounce reflection
function TrajectorySystem:CalculateReflection(velocity, normal)
    local dot = velocity.X * normal.X + velocity.Y * normal.Y + velocity.Z * normal.Z
    return Vector3.new(
        velocity.X - 2 * dot * normal.X,
        velocity.Y - 2 * dot * normal.Y,
        velocity.Z - 2 * dot * normal.Z
    )
end

-- Predict ball trajectory
function TrajectorySystem:PredictTrajectory(ball, initialVelocity, steps)
    -- Clear old trajectory points
    for _, point in pairs(trajectoryPoints) do
        if point.Part then
            point.Part:Destroy()
        end
    end
    trajectoryPoints = {}
    
    local position = ball.Position
    local velocity = initialVelocity
    local timeStep = 0.1 -- Simulation time step
    
    -- Create trajectory points
    for i = 1, steps do
        -- Simple physics simulation
        local nextPosition = position + (velocity * timeStep)
        
        -- Check for collisions with walls and floor
        local rayParams = RaycastParams.new()
        rayParams.FilterType = Enum.RaycastFilterType.Exclude
        rayParams.FilterDescendantsInstances = {ball, trajectoryFolder}
        
        local rayDirection = (nextPosition - position).Unit * (nextPosition - position).Magnitude
        local rayResult = workspace:Raycast(position, rayDirection, rayParams)
        
        if rayResult then
            -- Collision detected, calculate bounce
            position = rayResult.Position
            velocity = self:CalculateReflection(velocity, rayResult.Normal)
            
            -- Create bounce point (larger and different color)
            self:CreateTrajectoryPoint(position, true)
        else
            -- No collision, continue on path
            position = nextPosition
            
            -- Create regular trajectory point
            self:CreateTrajectoryPoint(position, false)
        end
    end
end

-- Create visual trajectory point
function TrajectorySystem:CreateTrajectoryPoint(position, isBounce)
    local part = Instance.new("Part")
    part.Shape = Enum.PartType.Ball
    part.Size = isBounce and Vector3.new(1, 1, 1) or Vector3.new(0.5, 0.5, 0.5)
    part.Position = position
    part.Anchored = true
    part.CanCollide = false
    part.Material = Enum.Material.Neon
    part.Color = isBounce and Color3.fromRGB(255, 255, 0) or Color3.fromRGB(0, 255, 255)
    part.Transparency = 0.3
    part.Parent = trajectoryFolder
    
    -- Add to trajectory points list with creation time
    table.insert(trajectoryPoints, {
        Part = part,
        CreationTime = os.time()
    })
    
    -- Limit number of trajectory points
    if #trajectoryPoints > maxTrajectoryPoints then
        if trajectoryPoints[1].Part then
            trajectoryPoints[1].Part:Destroy()
        end
        table.remove(trajectoryPoints, 1)
    end
end

-- Clean up old trajectory points
function TrajectorySystem:CleanupOldPoints()
    local currentTime = os.time()
    local i = 1
    
    while i <= #trajectoryPoints do
        if currentTime - trajectoryPoints[i].CreationTime > trajectoryLifetime then
            if trajectoryPoints[i].Part then
                trajectoryPoints[i].Part:Destroy()
            end
            table.remove(trajectoryPoints, i)
        else
            i = i + 1
        end
    end
end

-- NEW: Player Analysis System
local PlayerAnalysisSystem = {}
local playerData = {}
local lastBallTarget = nil
local lastBallSpeed = 0
local analysisUpdateInterval = 1 -- Update analysis display every second
local lastAnalysisUpdate = 0

-- Initialize player data
function PlayerAnalysisSystem:InitializePlayerData(playerName)
    if not playerData[playerName] then
        playerData[playerName] = {
            parryCount = 0,
            missCount = 0,
            lastParryTime = 0,
            averageReactionTime = 0,
            reactionTimes = {},
            ballsReceived = 0,
            ballsSent = 0,
            lastTargetTime = 0,
            skill = "Unknown",
            threat = "Low"
        }
    end
end

-- Track when a player is targeted by the ball
function PlayerAnalysisSystem:TrackBallTarget(targetName, ballSpeed)
    if targetName and targetName ~= "" and targetName ~= lastBallTarget then
        -- Initialize data for this player if needed
        self:InitializePlayerData(targetName)
        
        -- Record that this player is now targeted
        playerData[targetName].lastTargetTime = os.time()
        playerData[targetName].ballsReceived = playerData[targetName].ballsReceived + 1
        
        -- If there was a previous target, record that they sent the ball
        if lastBallTarget and lastBallTarget ~= "" then
            self:InitializePlayerData(lastBallTarget)
            playerData[lastBallTarget].ballsSent = playerData[lastBallTarget].ballsSent + 1

            -- Calculate and record reaction time
            local reactionTime = os.time() - playerData[lastBallTarget].lastTargetTime
            
            -- Only count reasonable reaction times (0.1 to 2 seconds)
            if reactionTime >= 0.1 and reactionTime <= 2 then
                table.insert(playerData[lastBallTarget].reactionTimes, reactionTime)
                
                -- Calculate average reaction time
                local sum = 0
                for _, time in ipairs(playerData[lastBallTarget].reactionTimes) do
                    sum = sum + time
                end
                
                playerData[lastBallTarget].averageReactionTime = sum / #playerData[lastBallTarget].reactionTimes
                
                -- Count as successful parry
                playerData[lastBallTarget].parryCount = playerData[lastBallTarget].parryCount + 1
                playerData[lastBallTarget].lastParryTime = os.time()
            else
                -- Count as miss if reaction time is outside reasonable range
                playerData[lastBallTarget].missCount = playerData[lastBallTarget].missCount + 1
            end
        end
        
        -- Update last ball target
        lastBallTarget = targetName
        lastBallSpeed = ballSpeed
    end
end

-- Calculate player skill level based on stats
function PlayerAnalysisSystem:CalculatePlayerSkill(playerName)
    local data = playerData[playerName]
    if not data then return "Unknown" end
    
    local totalAttempts = data.parryCount + data.missCount
    if totalAttempts < 3 then return "Analyzing..." end
    
    local successRate = data.parryCount / totalAttempts
    local avgReactionTime = data.averageReactionTime
    
    -- Determine skill level
    if successRate > 0.9 and avgReactionTime < 0.5 then
        return "Expert"
    elseif successRate > 0.75 and avgReactionTime < 0.8 then
        return "Advanced"
    elseif successRate > 0.6 and avgReactionTime < 1.2 then
        return "Intermediate"
    elseif successRate > 0.4 then
        return "Beginner"
    else
        return "Novice"
    end
end

-- Calculate threat level of a player
function PlayerAnalysisSystem:CalculateThreatLevel(playerName)
    local data = playerData[playerName]
    if not data then return "Unknown" end
    
    local totalAttempts = data.parryCount + data.missCount
    if totalAttempts < 3 then return "Analyzing..." end
    
    local successRate = data.parryCount / totalAttempts
    local avgReactionTime = data.averageReactionTime
    
    -- Determine threat level
    if successRate > 0.85 and avgReactionTime < 0.6 then
        return "Very High"
    elseif successRate > 0.7 and avgReactionTime < 0.9 then
        return "High"
    elseif successRate > 0.5 and avgReactionTime < 1.3 then
        return "Medium"
    else
        return "Low"
    end
end

-- Update player analysis display
function PlayerAnalysisSystem:UpdateAnalysisDisplay()
    local currentTime = os.time()
    
    -- Only update at certain intervals to avoid performance impact
    if currentTime - lastAnalysisUpdate < analysisUpdateInterval then
        return
    end
    
    lastAnalysisUpdate = currentTime
    
    -- Find the most threatening players to display
    local threatList = {}
    for playerName, data in pairs(playerData) do
        -- Skip players who haven't interacted with the ball recently
        if currentTime - data.lastTargetTime < 60 then -- Within the last minute
            local skill = self:CalculatePlayerSkill(playerName)
            local threat = self:CalculateThreatLevel(playerName)
            
            table.insert(threatList, {
                name = playerName,
                skill = skill,
                threat = threat,
                successRate = data.parryCount / math.max(1, (data.parryCount + data.missCount)),
                reactionTime = data.averageReactionTime
            })
        end
    end
    
    -- Sort by threat level (highest first)
    table.sort(threatList, function(a, b)
        local threatOrder = {["Very High"] = 4, ["High"] = 3, ["Medium"] = 2, ["Low"] = 1, ["Analyzing..."] = 0, ["Unknown"] = -1}
        return threatOrder[a.threat] > threatOrder[b.threat]
    end)
    
    -- Build display text
    local displayText = ""
    local displayCount = 0
    
    for _, player in ipairs(threatList) do
        if displayCount < 5 then -- Show top 5 threats
            local successPercent = math.floor(player.successRate * 100)
            local reactionTime = player.reactionTime > 0 and string.format("%.2f", player.reactionTime) or "N/A"
            
            displayText = displayText .. player.name .. " [" .. player.threat .. "]\n"
            displayText = displayText .. "  Skill: " .. player.skill .. "\n"
            displayText = displayText .. "  Parry: " .. successPercent .. "% | React: " .. reactionTime .. "s\n"
            
            displayCount = displayCount + 1
        end
    end
    
    if displayText == "" then
        displayText = "Collecting player data...\nParry some balls to analyze opponents."
    end
    
    -- Update the display
    analysisContent.Text = displayText
end

local function GetBall()
    for _, Ball in ipairs(workspace.Balls:GetChildren()) do
        if Ball:GetAttribute("realBall") and Ball:FindFirstChild("zoomies") then
            return Ball
        end
    end
end

local function ResetConnection()
    if Connection then
        Connection:Disconnect()
        Connection = nil
        IsParried = false
    end
end

local function CreateOrUpdateVisuals(ball, distance)
    -- Remove old visuals if they exist
    if ball:FindFirstChild("DistanceLabel") then
        ball.DistanceLabel:Destroy()
    end
    
    -- Calculate text size based on distance
    -- Closer = smaller text, Farther = larger text
    local minDistance = 1   -- Distance at which text is smallest
    local maxDistance = 500  -- Distance at which text is largest
    local minSize = 0.7     -- Minimum text size multiplier
    local maxSize = 1.0     -- Maximum text size multiplier
    
    -- Calculate size factor (inverse relationship with distance)
    local sizeFactor = minSize + (maxSize - minSize) * 
                      math.clamp((distance - minDistance) / (maxDistance - minDistance), 0, 1)
    
    -- Create distance text
    local billboardGui = Instance.new("BillboardGui")
    billboardGui.Name = "DistanceLabel"
    billboardGui.Size = UDim2.new(0, 200 * sizeFactor, 0, 50 * sizeFactor)
    billboardGui.StudsOffset = Vector3.new(0, 0, 0)
    billboardGui.AlwaysOnTop = true
    billboardGui.Parent = ball
    
    local textLabel = Instance.new("TextLabel")
    textLabel.Size = UDim2.new(1, 0, 1, 0)
    textLabel.BackgroundTransparency = 1
    textLabel.Text = string.format("%.1f", distance)
    textLabel.TextColor3 = Color3.new(1, 1, 1)
    textLabel.TextStrokeTransparency = 0
    textLabel.TextStrokeColor3 = Color3.new(0, 0, 0)
    textLabel.Font = Enum.Font.SourceSansBold
    textLabel.TextScaled = true
    textLabel.Parent = billboardGui
    
    -- Update ball color based on distance
    -- Dark red (far) to bright red (close)
    local colorIntensity = math.clamp(1 - (distance / 50), 0, 1) -- Adjust 50 to your preferred max distance
    local highlight = ball:FindFirstChild("Highlight")
    
    if not highlight then
        highlight = Instance.new("Highlight")
        highlight.Name = "Highlight"
        highlight.Parent = ball
    end
    
    highlight.FillColor = Color3.new(1, colorIntensity * 0.5, colorIntensity * 0.5)
    highlight.OutlineColor = Color3.new(1, 0, 0)
    highlight.FillTransparency = 0.3
    highlight.OutlineTransparency = 0.2
    highlight.Enabled = true
end

-- Update speed indicator with color coding
local function UpdateSpeedIndicator(speed)
    -- Round speed to nearest integer for display
    local roundedSpeed = math.floor(speed + 0.5)
    speedLabel.Text = roundedSpeed .. " Sp/s"
    
    -- Color code based on speed
    if speed <= 200 then
        speedLabel.TextColor3 = Color3.fromRGB(0, 255, 0) -- Green for slow
    elseif speed <= 300 then
        speedLabel.TextColor3 = Color3.fromRGB(255, 255, 0) -- Yellow for medium
    elseif speed <= 400 then
        speedLabel.TextColor3 = Color3.fromRGB(255, 165, 0) -- Orange for fast
    elseif speed <= 600 then
        speedLabel.TextColor3 = Color3.fromRGB(255, 0, 0) -- Red for very fast
    else
        -- Flashing effect for extreme speeds
        local flash = (os.clock() % 0.5 < 0.25)
        speedLabel.TextColor3 = flash and Color3.fromRGB(255, 0, 0) or Color3.fromRGB(255, 255, 255)
    end
    
    -- Adjust size based on speed (bigger = faster)
    local sizeMultiplier = math.clamp(1 + (speed / 800), 1, 1.8)
    speedFrame.Size = UDim2.new(0, 150 * sizeMultiplier, 0, 50 * sizeMultiplier)
end

workspace.Balls.ChildAdded:Connect(function()
    local Ball = GetBall()
    if not Ball then return end
    
    ResetConnection()
    Connection = Ball:GetAttributeChangedSignal("target"):Connect(function()
        IsParried = false
    end)
end)

RunService.PreSimulation:Connect(function()
    local Ball = GetBall()
    local Character = Player.Character
    if not Ball or not Character or not Character:FindFirstChild("HumanoidRootPart") then
        -- If no ball is found, show 0 speed
        UpdateSpeedIndicator(0)
        return
    end
    
    local HRP = Character.HumanoidRootPart
    local Speed = Ball.zoomies.VectorVelocity.Magnitude
    local Distance = (HRP.Position - Ball.Position).Magnitude
    local TargetName = Ball:GetAttribute("target")
    
    -- Update speed indicator
    UpdateSpeedIndicator(Speed)
    
    -- Update visuals
    CreateOrUpdateVisuals(Ball, Distance)
    
    -- NEW: Track ball target for player analysis
    PlayerAnalysisSystem:TrackBallTarget(TargetName, Speed)
    
    -- NEW: Update player analysis display
    PlayerAnalysisSystem:UpdateAnalysisDisplay()
    
    -- NEW: Predict and visualize ball trajectory
    pcall(function()
    if Ball and Ball:FindFirstChild("zoomies") and Ball.zoomies:FindFirstChild("VectorVelocity") then
        TrajectorySystem:CleanupOldPoints()
        TrajectorySystem:PredictTrajectory(Ball, Ball.zoomies.VectorVelocity, 5) -- Reduced steps from 10 to 5
    end
end)
    
    -- Special case for 1v1 ultra-fast balls - adjusted to be less early
    if ParrySystem:Is1v1FastBall(Speed, TargetName) and not IsParried then
        -- For extremely fast 1v1 balls, wait a bit longer before parrying
        -- Added a distance check to prevent too-early parrying
        if Distance / Speed <= 0.15 then -- Only parry when very close
            VirtualInputManager:SendMouseButtonEvent(0, 0, 0, true, game, 0)
            VirtualInputManager:SendMouseButtonEvent(0, 0, 0, false, game, 0)
            IsParried = true
        end
    -- Normal parry logic with improved timing
    elseif TargetName == Player.Name 
    and not IsParried 
    and ParrySystem:ShouldParry(Speed, Distance) then
         VirtualInputManager:SendMouseButtonEvent(0, 0, 0, true, game, 0)
         VirtualInputManager:SendMouseButtonEvent(0, 0, 0, false, game, 0)
        IsParried = true
    end
end)

-- Clean up highlights and UI when script stops
game.Players.PlayerRemoving:Connect(function(plr)
    if plr == Player then
        highlightFolder:Destroy()
        speedIndicator:Destroy()
        trajectoryFolder:Destroy()
        
        local Ball = GetBall()
        if Ball and Ball:FindFirstChild("DistanceLabel") then
            Ball.DistanceLabel:Destroy()
        end
        if Ball and Ball:FindFirstChild("Highlight") then
            Ball.Highlight:Destroy()
        end
        
        -- Clean up trajectory points
        for _, point in pairs(trajectoryPoints) do
            if point.Part then
                point.Part:Destroy()
            end
        end
    end
end)

local UserInputService = game:GetService("UserInputService")
local RunService = game:GetService("RunService")
local Players = game:GetService("Players")
local VirtualInputManager = game:GetService("VirtualInputManager")

-- Player setup
local Player = Players.LocalPlayer

-- Variables
local IsHolding = false
local ClicksPerSecond = 30
local ClickDelay = 1 / ClicksPerSecond
local LastClick = 0
local Connection
local Enabled = true -- Auto-clicker starts enabled

-- Create status indicator
local statusGui = Instance.new("ScreenGui")
statusGui.Name = "AutoFClickerStatus"
statusGui.ResetOnSpawn = false
statusGui.Parent = Player:WaitForChild("PlayerGui")

local statusLabel = Instance.new("TextLabel")
statusLabel.Name = "StatusLabel"
statusLabel.Size = UDim2.new(0, 200, 0, 30)
statusLabel.Position = UDim2.new(0, 10, 0.9, 50)
statusLabel.BackgroundColor3 = Color3.fromRGB(0, 0, 0)
statusLabel.BackgroundTransparency = 0.5
statusLabel.TextColor3 = Color3.fromRGB(0, 255, 0)
statusLabel.TextSize = 14
statusLabel.Font = Enum.Font.SourceSansBold
statusLabel.Text = "F-Key Clicker: ENABLED [Z]"
statusLabel.Parent = statusGui

-- Function to update status display
function UpdateStatus()
    if Enabled then
        statusLabel.Text = "F-Key Clicker: ENABLED [Z]"
        statusLabel.TextColor3 = Color3.fromRGB(0, 255, 0)
    else
        statusLabel.Text = "F-Key Clicker: DISABLED [Z]"
        statusLabel.TextColor3 = Color3.fromRGB(255, 0, 0)
    end
end

-- Function to press F key
function PressF()
    -- Simulate F key press and release
    VirtualInputManager:SendKeyEvent(true, Enum.KeyCode.F, false, game)
    task.wait(0.01) -- Small delay between press and release
    VirtualInputManager:SendKeyEvent(false, Enum.KeyCode.F, false, game)
end

-- Main loop
Connection = RunService.Heartbeat:Connect(function()
    if Enabled and IsHolding then
        local currentTime = tick()
        if currentTime - LastClick >= ClickDelay then
            LastClick = currentTime
            PressF()
        end
    end
end)

-- Input detection for mouse
UserInputService.InputBegan:Connect(function(input, gameProcessed)
    if input.UserInputType == Enum.UserInputType.MouseButton1 then
        IsHolding = true
    end
end)

UserInputService.InputEnded:Connect(function(input, gameProcessed)
    if input.UserInputType == Enum.UserInputType.MouseButton1 then
        IsHolding = false
    end
end)

-- Toggle key (Z)
UserInputService.InputBegan:Connect(function(input, gameProcessed)
    if input.KeyCode == Enum.KeyCode.Z and not gameProcessed then
        Enabled = not Enabled
        UpdateStatus()
        
        -- Notification
        game.StarterGui:SetCore("SendNotification", {
            Title = "F-Key Clicker",
            Text = Enabled and "F-key clicker enabled" or "F-key clicker disabled",
            Duration = 2
        })
    end
end)

-- Initial status update
UpdateStatus()

-- Notification
game.StarterGui:SetCore("SendNotification", {
    Title = "F-Key Clicker Loaded",
    Text = "Hold LMB to auto-press F key. Press Z to toggle on/off.",
    Duration = 5
})

-- Cleanup
Players.PlayerRemoving:Connect(function(plr)
    if plr == Player then
        if Connection then
            Connection:Disconnect()
        end
        if statusGui then
            statusGui:Destroy()
        end
    end
end)

local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local VirtualInputManager = game:GetService("VirtualInputManager")

local Player = Players.LocalPlayer
local Character = Player.Character or Player.CharacterAdded:Wait()
local HRP = Character:WaitForChild("HumanoidRootPart")

-- Configuration
local EMERGENCY_REACH_DISTANCE = 12 -- Maximum distance to consider "in reach"
local REACTION_THRESHOLD = 0.15 -- Time in seconds before ball hits to trigger emergency parry
local RESET_DISTANCE = 30 -- Distance at which to reset ball tracking
local CLASH_DETECTION_SENSITIVITY = 30 -- Lower = more sensitive to clash detection
local POST_CLASH_PARRY_DELAY = 0.05 -- Time to wait after clash before allowing another parry

-- Variables
local ballsBeingTracked = {}

-- Function to get all balls (works for both regular and class PvP)
local function GetAllBalls()
    local balls = {}
    
    -- Check regular balls folder
    if workspace:FindFirstChild("Balls") then
        for _, ball in ipairs(workspace.Balls:GetChildren()) do
            if ball:IsA("BasePart") and (ball:GetAttribute("realBall") or ball.Name == "Ball") then
                table.insert(balls, ball)
            end
        end
    end
    
    -- Check for class PvP balls (usually in ReplicatedStorage or workspace)
    for _, ball in ipairs(workspace:GetChildren()) do
        if ball:IsA("BasePart") and (ball.Name == "Ball" or ball.Name:find("Ball")) then
            if ball:FindFirstChild("zoomies") or ball:FindFirstChild("BodyVelocity") then
                table.insert(balls, ball)
            end
        end
    end
    
    return balls
end

-- Function to get ball velocity
local function GetBallVelocity(ball)
    if ball:FindFirstChild("zoomies") and ball.zoomies:FindFirstChild("VectorVelocity") then
        return ball.zoomies.VectorVelocity
    elseif ball:FindFirstChild("BodyVelocity") then
        return ball.BodyVelocity.Velocity
    else
        -- Estimate velocity by position change if no velocity component found
        if ballsBeingTracked[ball] and ballsBeingTracked[ball].lastPosition then
            local currentPosition = ball.Position
            local lastPosition = ballsBeingTracked[ball].lastPosition
            local deltaTime = 0.03 -- Approximate time between frames
            return (currentPosition - lastPosition) / deltaTime
        end
    end
    
    return Vector3.new(0, 0, 0)
end

-- Function to check if ball is targeting the player
local function IsBallTargetingPlayer(ball)
    -- Check for target attribute (regular gameplay)
    if ball:GetAttribute("target") == Player.Name then
        return true
    end
    
    -- For class PvP, check if ball is moving toward player
    local velocity = GetBallVelocity(ball)
    if velocity.Magnitude > 10 then
        local directionToPlayer = (HRP.Position - ball.Position).Unit
        local ballDirection = velocity.Unit
        local dotProduct = directionToPlayer:Dot(ballDirection)
        
        -- If dot product is high, ball is moving toward player
        if dotProduct > 0.7 then
            return true
        end
    end
    
    return false
end

-- Enhanced clash detection function
local function DetectClash(ball)
    if not ballsBeingTracked[ball] then return false end
    
    local currentVelocity = GetBallVelocity(ball)
    local previousVelocity = ballsBeingTracked[ball].lastVelocity
    
    if not previousVelocity then return false end
    
    -- Multiple clash detection methods
    
    -- 1. Velocity magnitude change
    local velocityChangeMagnitude = (currentVelocity - previousVelocity).Magnitude
    
    -- 2. Direction change
    local currentDirection = currentVelocity.Unit
    local previousDirection = previousVelocity.Unit
    local directionChange = currentDirection:Dot(previousDirection)
    
    -- 3. Speed change
    local currentSpeed = currentVelocity.Magnitude
    local previousSpeed = previousVelocity.Magnitude
    local speedChange = math.abs(currentSpeed - previousSpeed)
    
    -- Detect clash using multiple criteria
    if velocityChangeMagnitude > CLASH_DETECTION_SENSITIVITY or 
       directionChange < 0 or 
       speedChange > CLASH_DETECTION_SENSITIVITY * 2 then
        return true
    end
    
    return false
end

-- Function to perform emergency parry
local function PerformEmergencyParry(ball)
    -- Send parry input
    VirtualInputManager:SendMouseButtonEvent(0, 0, 0, true, game, 0)
    task.wait(0.01)
    VirtualInputManager:SendMouseButtonEvent(0, 0, 0, false, game, 0)
    
    -- Mark this ball as having had an emergency parry attempt
    if ballsBeingTracked[ball] then
        ballsBeingTracked[ball].emergencyParryAttempted = true
        ballsBeingTracked[ball].lastParryTime = tick()
    end
end

-- Update character reference when character changes
Player.CharacterAdded:Connect(function(newCharacter)
    Character = newCharacter
    HRP = Character:WaitForChild("HumanoidRootPart")
end)

-- Main update loop
RunService.Heartbeat:Connect(function()
    -- Update character reference if needed
    if not Character or not Character:FindFirstChild("HumanoidRootPart") then
        Character = Player.Character
        if not Character then return end
        HRP = Character:FindFirstChild("HumanoidRootPart")
        if not HRP then return end
    end
    
    -- Get all balls
    local balls = GetAllBalls()
    
    -- Track each ball
    for _, ball in ipairs(balls) do
        -- Initialize tracking for new balls
        if not ballsBeingTracked[ball] then
            ballsBeingTracked[ball] = {
                lastPosition = ball.Position,
                lastVelocity = GetBallVelocity(ball),
                emergencyParryAttempted = false,
                approachId = 0,
                lastTargetChange = 0,
                lastTarget = ball:GetAttribute("target"),
                lastClashTime = 0,
                lastParryTime = 0,
                consecutiveClashes = 0
            }
        end
        
        -- Calculate distance to player
        local distance = (HRP.Position - ball.Position).Magnitude
        
        -- Check for clash with enhanced detection
        local clashDetected = DetectClash(ball)
        local currentTime = tick()
        
        -- Handle clash detection
        if clashDetected then
            -- Record clash time
            ballsBeingTracked[ball].lastClashTime = currentTime
            ballsBeingTracked[ball].consecutiveClashes = (ballsBeingTracked[ball].consecutiveClashes or 0) + 1
            
            -- Immediately reset emergency parry flag to allow for follow-up parries
            ballsBeingTracked[ball].emergencyParryAttempted = false
            ballsBeingTracked[ball].approachId = ballsBeingTracked[ball].approachId + 1
        end
        
        -- Check if target changed
        local currentTarget = ball:GetAttribute("target")
        if currentTarget ~= ballsBeingTracked[ball].lastTarget then
            ballsBeingTracked[ball].lastTarget = currentTarget
            ballsBeingTracked[ball].lastTargetChange = currentTime
            ballsBeingTracked[ball].approachId = ballsBeingTracked[ball].approachId + 1
            ballsBeingTracked[ball].emergencyParryAttempted = false
            ballsBeingTracked[ball].consecutiveClashes = 0
        end
        
        -- Check if ball is targeting player and within reach
        if distance <= EMERGENCY_REACH_DISTANCE and IsBallTargetingPlayer(ball) then
            -- Get ball velocity and calculate time to impact
            local velocity = GetBallVelocity(ball)
            local speed = velocity.Magnitude
            
            if speed > 0 then
                local timeToImpact = distance / speed
                
                -- Check if enough time has passed since last clash
                local timeSinceLastClash = currentTime - (ballsBeingTracked[ball].lastClashTime or 0)
                local timeSinceLastParry = currentTime - (ballsBeingTracked[ball].lastParryTime or 0)
                
                -- If ball is about to hit and we can parry again
                local canParry = not ballsBeingTracked[ball].emergencyParryAttempted or 
                                (timeSinceLastClash > POST_CLASH_PARRY_DELAY and 
                                 timeSinceLastParry > POST_CLASH_PARRY_DELAY)
                
                if timeToImpact <= REACTION_THRESHOLD and canParry then
                    -- For consecutive clashes, be more aggressive with parrying
                    if ballsBeingTracked[ball].consecutiveClashes >= 2 or timeToImpact < REACTION_THRESHOLD * 0.7 then
                        PerformEmergencyParry(ball)
                    end
                end
            end
        elseif distance > RESET_DISTANCE then
            -- Reset tracking when ball is far away
            ballsBeingTracked[ball].emergencyParryAttempted = false
            ballsBeingTracked[ball].approachId = ballsBeingTracked[ball].approachId + 1
            ballsBeingTracked[ball].consecutiveClashes = 0
        end
        
        -- Update position and velocity tracking
        ballsBeingTracked[ball].lastPosition = ball.Position
        ballsBeingTracked[ball].lastVelocity = GetBallVelocity(ball)
    end
    
    -- Clean up tracking for balls that no longer exist
    for ball, _ in pairs(ballsBeingTracked) do
        if not ball or not ball.Parent then
            ballsBeingTracked[ball] = nil
        end
    end
end)

-- Final status message in console
print("=== Blade Ball Parry System ===")
print("✓ Main Parry System: Active")
print("✓ F-Key Clicker: Active (Toggle with Z)")
print("✓ Player Analysis System: Active")
print("✓ Trajectory Prediction: Active")
print("=====================================")

Embed on website

To embed this project on your website, copy the following code and paste it into your website's HTML: