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

local Player = Players.LocalPlayer

-- Variables
local IsParried = false
local Connection = nil
local previousVelocity = nil
local lastClashTime = 0
local clashCooldown = 0.1 -- Cooldown between clash detections

-- Add these variables for ping and CPS tracking
local pingLabel = nil
local cpsLabel = nil
local clickCounter = 0
local lastClickTime = 0
local cpsUpdateTime = 0
local cpsUpdateInterval = 1 -- Update CPS every second

-- 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")

-- Create a frame to hold all indicators
local indicatorFrame = Instance.new("Frame")
indicatorFrame.Name = "IndicatorFrame"
indicatorFrame.Size = UDim2.new(0, 300, 0, 50)
indicatorFrame.Position = UDim2.new(0.5, -150, 0, 10) -- Centered at top
indicatorFrame.BackgroundTransparency = 1
indicatorFrame.Parent = speedIndicator

-- CPS Label (left side)
cpsLabel = Instance.new("TextLabel")
cpsLabel.Name = "CPSLabel"
cpsLabel.Size = UDim2.new(0, 80, 0, 50)
cpsLabel.Position = UDim2.new(0, 0, 0, 0) -- Left side
cpsLabel.BackgroundColor3 = Color3.fromRGB(0, 0, 0)
cpsLabel.BackgroundTransparency = 0.5
cpsLabel.TextColor3 = Color3.fromRGB(255, 255, 255)
cpsLabel.TextSize = 18
cpsLabel.Font = Enum.Font.SourceSansBold
cpsLabel.Text = "0 CPS"
cpsLabel.TextXAlignment = Enum.TextXAlignment.Center
cpsLabel.Parent = indicatorFrame

-- Speed Label (center)
local speedFrame = Instance.new("Frame")
speedFrame.Name = "SpeedFrame"
speedFrame.Size = UDim2.new(0, 150, 0, 50)
speedFrame.Position = UDim2.new(0.5, -75, 0, 0) -- Center
speedFrame.BackgroundColor3 = Color3.fromRGB(0, 0, 0)
speedFrame.BackgroundTransparency = 0.5
speedFrame.Parent = indicatorFrame

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

-- Ping Label (right side)
pingLabel = Instance.new("TextLabel")
pingLabel.Name = "PingLabel"
pingLabel.Size = UDim2.new(0, 80, 0, 50)
pingLabel.Position = UDim2.new(1, -80, 0, 0) -- Right side
pingLabel.BackgroundColor3 = Color3.fromRGB(0, 0, 0)
pingLabel.BackgroundTransparency = 0.5
pingLabel.TextColor3 = Color3.fromRGB(255, 255, 255)
pingLabel.TextSize = 18
pingLabel.Font = Enum.Font.SourceSansBold
pingLabel.Text = "0 ms"
pingLabel.TextXAlignment = Enum.TextXAlignment.Center
pingLabel.Parent = indicatorFrame

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

-- 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

-- IMPROVED PARRY SYSTEM
local ParrySystem = {}

-- Configure parry window based on ball speed with improved timing
function ParrySystem:GetParryWindow(ballSpeed)
    -- More dynamic and responsive parry windows based on ball speed
    if ballSpeed <= 100 then
        return 0.5  -- Very slow balls get a generous window
    elseif ballSpeed <= 200 then
        return 0.45
    elseif ballSpeed <= 300 then
        return 0.4
    elseif ballSpeed <= 400 then
        return 0.35
    elseif ballSpeed <= 500 then
        return 0.3
    elseif ballSpeed <= 600 then
        return 0.25
    else
        return 0.2  -- Very fast balls need precise timing
    end
end

-- Improved parry timing with adaptive distance calculation
function ParrySystem:ShouldParry(ballSpeed, distanceToPlayer)
    local parryWindow = self:GetParryWindow(ballSpeed)
    local estimatedTimeToReach = distanceToPlayer / ballSpeed
    
    -- Adaptive timing based on ball speed
    local parryThreshold = parryWindow
    
    -- For very fast balls, parry slightly earlier
    if ballSpeed > 500 then
        parryThreshold = parryThreshold * 0.8
    -- For medium speed balls, parry at optimal time
    elseif ballSpeed > 300 then
        parryThreshold = parryThreshold * 0.9
    -- For slower balls, wait longer
    else
        parryThreshold = parryThreshold * 1.0
    end
    
    -- Add a minimum distance check to prevent parrying too early
    local minDistanceThreshold = 5 + (ballSpeed / 100)
    
    return estimatedTimeToReach <= parryThreshold and distanceToPlayer <= minDistanceThreshold
end

-- Special function for ultra-fast 1v1 scenarios with improved timing
function ParrySystem:Is1v1FastBall(ballSpeed, targetName, distance)
    if ballSpeed > 500 and targetName == Player.Name then
        -- Calculate optimal parry distance based on speed
        local optimalDistance = math.min(15, 5 + (ballSpeed / 100))
        return distance <= optimalDistance
    end
    return false
end

-- Detect ball clashes to reset parry state
function ParrySystem:DetectClash(currentVelocity, previousVelocity)
    if not previousVelocity then return false end
    
    -- Check for significant velocity changes indicating a clash
    local velocityChange = (currentVelocity - previousVelocity).Magnitude
    local directionChange = currentVelocity.Unit:Dot(previousVelocity.Unit)
    
    -- A clash typically causes a large velocity change or direction reversal
    return velocityChange > 50 or directionChange < 0
end

-- Trajectory prediction system
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

-- 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

-- Function to update the ping display
local function UpdatePingDisplay()
    local ping = game:GetService("Stats").Network.ServerStatsItem["Data Ping"]:GetValue()
    pingLabel.Text = math.floor(ping) .. " ms"
    
    -- Color code based on ping
    if ping < 50 then
        pingLabel.TextColor3 = Color3.fromRGB(0, 255, 0) -- Green for good ping
    elseif ping < 100 then
        pingLabel.TextColor3 = Color3.fromRGB(255, 255, 0) -- Yellow for medium ping
    elseif ping < 200 then
        pingLabel.TextColor3 = Color3.fromRGB(255, 165, 0) -- Orange for high ping
    else
        pingLabel.TextColor3 = Color3.fromRGB(255, 0, 0) -- Red for very high ping
    end
end

-- Function to track clicks and update CPS
local function UpdateCPS()
    local currentTime = tick()
    
    -- Reset counter every second
    if currentTime - cpsUpdateTime >= cpsUpdateInterval then
        cpsUpdateTime = currentTime
        clickCounter = 0
    end
end

-- Track mouse clicks for CPS
UserInputService.InputBegan:Connect(function(input, gameProcessed)
    if input.UserInputType == Enum.UserInputType.MouseButton1 or 
       input.UserInputType == Enum.UserInputType.MouseButton2 then
        clickCounter = clickCounter + 1
        lastClickTime = tick()
        
        -- Update CPS display
        cpsLabel.Text = clickCounter .. " CPS"
    end
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 ping display
    UpdatePingDisplay()
    
    -- Update CPS tracking
    UpdateCPS()
    
    -- Update visuals
    CreateOrUpdateVisuals(Ball, Distance)
    
    -- Track ball target for player analysis
    PlayerAnalysisSystem:TrackBallTarget(TargetName, Speed)
    
    -- Update player analysis display
    PlayerAnalysisSystem:UpdateAnalysisDisplay()
    
    -- 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)
    
    -- Check for clashes to reset parry state
    local currentVelocity = Ball.zoomies.VectorVelocity
    local currentTime = tick()
    
    if previousVelocity and currentTime - lastClashTime > clashCooldown then
        if ParrySystem:DetectClash(currentVelocity, previousVelocity) then
            IsParried = false
            lastClashTime = currentTime
            print("Clash detected - parry state reset")
        end
    end
    previousVelocity = currentVelocity

    -- IMPROVED PARRY LOGIC
    -- Special case for 1v1 ultra-fast balls - improved timing
    if ParrySystem:Is1v1FastBall(Speed, TargetName, Distance) and not IsParried then
        -- Calculate optimal timing based on speed and distance
        local timeToImpact = Distance / Speed
        
        -- Adaptive timing for ultra-fast balls
        if timeToImpact <= 0.2 then
            VirtualInputManager:SendMouseButtonEvent(0, 0, 0, true, game, 0)
            VirtualInputManager:SendMouseButtonEvent(0, 0, 0, false, game, 0)
            IsParried = true
            
            -- Debug info
            print("Ultra-fast parry at distance: " .. Distance .. ", Speed: " .. Speed)
        end
    -- Normal parry logic with improved timing
    elseif TargetName == Player.Name and not IsParried and ParrySystem:ShouldParry(Speed, Distance) then
        -- Add a small random delay to make parries look more natural (0-30ms)
        local humanDelay = math.random() * 0.03
        task.wait(humanDelay)
        
        VirtualInputManager:SendMouseButtonEvent(0, 0, 0, true, game, 0)
        VirtualInputManager:SendMouseButtonEvent(0, 0, 0, false, game, 0)
        IsParried = true
        
        -- Debug info
        print("Normal parry at distance: " .. Distance .. ", Speed: " .. Speed)
    end
end)

-- Cleanup function for when the script is stopped
local function CleanupScript()
    -- Remove all visual elements
    if highlightFolder then
        highlightFolder:Destroy()
    end
    
    if speedIndicator then
        speedIndicator:Destroy()
    end
    
    if trajectoryFolder then
        trajectoryFolder:Destroy()
    end
    
    -- Disconnect all connections
    if Connection then
        Connection:Disconnect()
    end
    
    -- Reset variables
    IsParried = false
    previousVelocity = nil
    
    print("Script cleanup completed")
end

-- Set up cleanup on script termination
game:GetService("Players").PlayerRemoving:Connect(function(plr)
    if plr == Player then
        CleanupScript()
    end
end)

-- Keybinds for manual control
UserInputService.InputBegan:Connect(function(input, gameProcessed)
    if gameProcessed then return end
    
    -- Toggle analysis panel with F1
    if input.KeyCode == Enum.KeyCode.F1 then
        analysisFrame.Visible = not analysisFrame.Visible
    end
    
    -- Toggle trajectory visualization with F2
    if input.KeyCode == Enum.KeyCode.F2 then
        trajectoryFolder.Visible = not trajectoryFolder.Visible
    end
    
    -- Manual parry with F
    if input.KeyCode == Enum.KeyCode.F then
        VirtualInputManager:SendMouseButtonEvent(0, 0, 0, true, game, 0)
        VirtualInputManager:SendMouseButtonEvent(0, 0, 0, false, game, 0)
    end
end)

-- Initialization message
print("Blade Ball Script loaded successfully!")
print("Press F1 to toggle player analysis")
print("Press F2 to toggle trajectory visualization")
print("Press F for manual parry")

Embed on website

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