mega football fusion 2 script
Key RequiredUniversal

mega football fusion 2 script

Universal Scriptkruzkruz 4.1K views 146 executesUpdated Oct 20, 2025
0.0/ 5(1 votes)
010% Positive
Lua Script•904 lines (48355 chars)
-- FF2 Ultimate Mega Hub + Training Module (Safe Local-Only)
-- Place as a LocalScript inside StarterPlayerScripts
-- IMPORTANT: This script is intentionally local-only. It does not call RemoteEvents or execute external code.

-- Services
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local UserInput = game:GetService("UserInputService")
local Workspace = game:GetService("Workspace")
local Debris = game:GetService("Debris")

local LocalPlayer = Players.LocalPlayer
local PlayerGui = LocalPlayer:WaitForChild("PlayerGui")

-- Simple UI builder
local function make(parent, class, props)
    local obj = Instance.new(class)
    if props then for k,v in pairs(props) do obj[k] = v end end
    obj.Parent = parent
    return obj
end

-- ======= Loader UI (safe) =======
local loaderGui = make(PlayerGui, "ScreenGui", {Name = "SafeLoaderGUI", ResetOnSpawn = false})
local loaderMain = make(loaderGui, "Frame", {
    Size = UDim2.new(0,560,0,240),
    Position = UDim2.new(0.5,-280,0.5,-120),
    BackgroundColor3 = Color3.fromRGB(25,25,25),
})
make(loaderMain, "UICorner", {CornerRadius = UDim.new(0,10)})
make(loaderMain, "TextLabel", {
    Size = UDim2.new(1,-24,0,40), Position = UDim2.new(0,12,0,12),
    BackgroundTransparency = 1, Text = "FF2 Ultimate Mega Hub (Local)",
    Font = Enum.Font.GothamBold, TextSize = 20, TextColor3 = Color3.fromRGB(245,245,245), TextXAlignment = Enum.TextXAlignment.Left
})
make(loaderMain, "TextLabel", {
    Size = UDim2.new(1,-24,0,20), Position = UDim2.new(0,12,0,44),
    BackgroundTransparency = 1, Text = "Loader • Safe • No external code executed", Font = Enum.Font.Gotham, TextSize = 12, TextColor3 = Color3.fromRGB(170,170,170), TextXAlignment = Enum.TextXAlignment.Left
})
local loaderLog = make(loaderMain, "TextBox", {
    Size = UDim2.new(1,-24,0,110), Position = UDim2.new(0,12,0,72), BackgroundTransparency = 1,
    TextWrapped = true, ClearTextOnFocus = false, TextEditable = false, Font = Enum.Font.Code, TextSize = 13, TextColor3 = Color3.fromRGB(200,200,200),
    Text = "Status: Idle\nPress [Load Hub] to open the Mega Hub."
})
local loadBtn = make(loaderMain, "TextButton", {
    Size = UDim2.new(0,160,0,40), Position = UDim2.new(1,-176,0,22),
    Text = "Load Hub", Font = Enum.Font.GothamSemibold, TextSize = 16, BackgroundColor3 = Color3.fromRGB(60,140,220), TextColor3 = Color3.fromRGB(255,255,255)
})
make(loadBtn, "UICorner", {CornerRadius = UDim.new(0,6)})

local banner = make(loaderMain, "Frame", {Size = UDim2.new(1,-24,0,28), Position = UDim2.new(0,12,1,-52), BackgroundColor3 = Color3.fromRGB(18,18,18), BackgroundTransparency = 0.12})
make(banner, "UICorner", {CornerRadius = UDim.new(0,6)})
make(banner, "TextLabel", {
    Size = UDim2.new(1,-96,1,0), Position = UDim2.new(0,8,0,0),
    BackgroundTransparency = 1, Text = "Only you can see this •", Font = Enum.Font.Gotham, TextSize = 13, TextColor3 = Color3.fromRGB(180,180,180), TextXAlignment = Enum.TextXAlignment.Left
})
local dismissBtn = make(banner, "TextButton", {Size = UDim2.new(0,72,0,20), Position = UDim2.new(1,-88,0,4), Text = "Dismiss", Font = Enum.Font.Gotham, TextSize = 12, BackgroundColor3 = Color3.fromRGB(80,80,80), TextColor3 = Color3.fromRGB(240,240,240)})
make(dismissBtn, "UICorner", {CornerRadius = UDim.new(0,6)})
dismissBtn.MouseButton1Click:Connect(function() banner:Destroy() end)

-- Hotkey to toggle loader visible
local loaderVisible = true
UserInput.InputBegan:Connect(function(input, gp)
    if gp then return end
    if input.KeyCode == Enum.KeyCode.K then
        loaderVisible = not loaderVisible
        loaderGui.Enabled = loaderVisible
    end
end)

-- ======= Core: Mega Hub + Training Module =======
local function createMegaHub()
    if PlayerGui:FindFirstChild("FF2MegaHub") then return end

    -- State
    local state = {
        Mag = 100,
        JumpPower = 100,
        Speed = 35,
        QBPower = 95,
        catches = 0,
        completions = 0,
        misses = 0,
        catchTimer = 0,
        AutoTPRange = 12,
        AutoTPInterval = 0.6,
        -- Training module defaults
        TrainAngle = 30,      -- degrees
        TrainPower = 80,      -- arbitrary units
        TrainExtend = 3,      -- extend multiplier (1-10)
        TrainSpawnVelocityFactor = 1.0,
        -- Auto-perfect kick cooldown
        PerfectKickCooldown = 0.6,
    }

    local toggles = {
        Mag = true,
        QBAimbot = true,
        BallESP = true,
        BallHitbox = false,
        BallPrediction = true,
        PlayerESP = true,
        AutoCatch = false,
        AutoThrow = false,
        Jump = true,
        Speed = true,
        TargetLine = true,
        ThrowArc = false,
        PlayerDistance = true,
        TeamColors = true,
        AutoTP = false,
        -- training visuals
        TrajectoryVisual = true,
        TrajectoryBeam = true,
        -- NEW toggles
        AutoPerfectKick = false, -- new: local-only perfect kick simulation
        NoVisual = false,        -- new: disable all visuals while keeping local logic
    }

    -- UI root
    local screenGui = make(PlayerGui, "ScreenGui", {Name = "FF2MegaHub", ResetOnSpawn = false})
    local mainFrame = make(screenGui, "Frame", {
        Size = UDim2.new(0,760,0,820),
        Position = UDim2.new(0,20,0,20),
        BackgroundColor3 = Color3.fromRGB(30,30,30),
        BorderSizePixel = 0
    })
    make(mainFrame, "UICorner", {CornerRadius = UDim.new(0,10)})

    -- Title, drag, close
    local titleBar = make(mainFrame, "Frame", {Size = UDim2.new(1,0,0,40), BackgroundTransparency = 0.08, BackgroundColor3 = Color3.fromRGB(18,18,18)})
    make(titleBar, "UICorner", {CornerRadius = UDim.new(0,8)})
    local titleLabel = make(titleBar, "TextLabel", {Text = "FF2 Ultimate Mega Hub + Training", Size = UDim2.new(1,-120,1,0), Position = UDim2.new(0,12,0,0), BackgroundTransparency = 1, TextColor3 = Color3.fromRGB(230,230,230), Font = Enum.Font.GothamBold, TextSize = 16, TextXAlignment = Enum.TextXAlignment.Left})
    local closeBtn = make(titleBar, "TextButton", {Text = "X", Size = UDim2.new(0,92,0,30), Position = UDim2.new(1,-100,0,4), BackgroundColor3 = Color3.fromRGB(170,50,50), TextColor3 = Color3.fromRGB(255,255,255), Font = Enum.Font.GothamBold, TextSize = 16})
    make(closeBtn, "UICorner", {CornerRadius = UDim.new(0,6)})
    closeBtn.MouseButton1Click:Connect(function() mainFrame.Visible = false end)

    -- Dragging
    do
        local dragging, dragStart, startPos = false, nil, nil
        titleBar.InputBegan:Connect(function(input)
            if input.UserInputType == Enum.UserInputType.MouseButton1 then
                dragging = true; dragStart = input.Position; startPos = mainFrame.Position
                input.Changed:Connect(function() if input.UserInputState == Enum.UserInputState.End then dragging = false end end)
            end
        end)
        titleBar.InputChanged:Connect(function(input)
            if input.UserInputType == Enum.UserInputType.MouseMovement and dragging then
                local delta = input.Position - dragStart
                mainFrame.Position = UDim2.new(startPos.X.Scale, startPos.X.Offset + delta.X, startPos.Y.Scale, startPos.Y.Offset + delta.Y)
            end
        end)
    end

    -- Scroll area
    local contentFrame = make(mainFrame, "Frame", {Size = UDim2.new(1,-24,1,-160), Position = UDim2.new(0,12,0,48), BackgroundTransparency = 1})
    local scroll = make(contentFrame, "ScrollingFrame", {Size = UDim2.new(1,0,1,0), CanvasSize = UDim2.new(0,0,0,5200), ScrollBarThickness = 10, BackgroundTransparency = 1})
    local layout = make(scroll, "UIListLayout", {Padding = UDim.new(0,8)})
    layout.HorizontalAlignment = Enum.HorizontalAlignment.Center
    layout.SortOrder = Enum.SortOrder.LayoutOrder

    local statusBar = make(mainFrame, "TextLabel", {Size = UDim2.new(1,-24,0,36), Position = UDim2.new(0,12,1,-100), BackgroundTransparency = 1, TextColor3 = Color3.fromRGB(220,220,220), Font = Enum.Font.Gotham, TextSize = 14, Text = ""})

    -- Toggle creator
    local function addToggle(name, desc, initial)
        local box = make(scroll, "Frame", {Size = UDim2.new(1,-24,0,72), BackgroundTransparency = 1})
        make(box, "TextLabel", {Text = name, Size = UDim2.new(1,-140,0,24), Position = UDim2.new(0,12,0,6), BackgroundTransparency = 1, TextColor3 = Color3.fromRGB(255,230,100), Font = Enum.Font.GothamBold, TextSize = 15, TextXAlignment = Enum.TextXAlignment.Left})
        make(box, "TextLabel", {Text = desc, Size = UDim2.new(1,-140,0,28), Position = UDim2.new(0,12,0,30), BackgroundTransparency = 1, TextColor3 = Color3.fromRGB(200,200,200), Font = Enum.Font.Gotham, TextSize = 12, TextXAlignment = Enum.TextXAlignment.Left})
        local btn = make(box, "TextButton", {Text = initial and "ON" or "OFF", Size = UDim2.new(0,104,0,36), Position = UDim2.new(1,-116,0,18), BackgroundColor3 = initial and Color3.fromRGB(60,160,60) or Color3.fromRGB(80,80,80), TextColor3 = Color3.fromRGB(255,255,255), Font = Enum.Font.GothamBold, TextSize = 14})
        make(btn, "UICorner", {CornerRadius = UDim.new(0,6)})
        btn.MouseButton1Click:Connect(function()
            toggles[name] = not toggles[name]
            btn.Text = toggles[name] and "ON" or "OFF"
            btn.BackgroundColor3 = toggles[name] and Color3.fromRGB(60,160,60) or Color3.fromRGB(80,80,80)
            if name == "NoVisual" then
                -- If NoVisual toggled on, kill visuals; if off, allow visuals per toggles
                if toggles.NoVisual then
                    -- disable many visual toggles but remember they can be toggled manually later
                    toggles.BallESP = false; toggles.TrajectoryVisual = false; toggles.TrajectoryBeam = false; toggles.Mag = false; toggles.PlayerESP = false; toggles.TargetLine = false; toggles.ThrowArc = false; toggles.BallHitbox = false
                end
            end
        end)
        return box
    end

    -- Slider creator (continuous)
    local function addSlider(labelText, minVal, maxVal, initial, onChange)
        local frame = make(scroll, "Frame", {Size = UDim2.new(1,-24,0,76), BackgroundTransparency = 1})
        local label = make(frame, "TextLabel", {Text = labelText .. ": " .. tostring(initial), Size = UDim2.new(1,-140,0,22), Position = UDim2.new(0,12,0,6), BackgroundTransparency = 1, TextColor3 = Color3.fromRGB(160,255,160), Font = Enum.Font.Gotham, TextSize = 14, TextXAlignment = Enum.TextXAlignment.Left})
        local track = make(frame, "Frame", {Size = UDim2.new(1,-140,0,18), Position = UDim2.new(0,12,0,44), BackgroundColor3 = Color3.fromRGB(55,55,55)})
        make(track, "UICorner", {CornerRadius = UDim.new(0,6)})
        local knob = make(track, "Frame", {Size = UDim2.new((initial-minVal)/(maxVal-minVal),0,1,0), BackgroundColor3 = Color3.fromRGB(200,200,200)})
        make(knob, "UICorner", {CornerRadius = UDim.new(0,8)})
        local dragging = false
        knob.InputBegan:Connect(function(input)
            if input.UserInputType == Enum.UserInputType.MouseButton1 then
                dragging = true
                input.Changed:Connect(function() if input.UserInputState == Enum.UserInputState.End then dragging = false end end)
            end
        end)
        UserInput.InputChanged:Connect(function(input)
            if dragging and input.UserInputType == Enum.UserInputType.MouseMovement then
                local absX = math.clamp(input.Position.X - track.AbsolutePosition.X, 0, track.AbsoluteSize.X)
                local frac = absX / track.AbsoluteSize.X
                knob.Size = UDim2.new(frac,0,1,0)
                local value = math.floor(minVal + frac*(maxVal-minVal) + 0.5)
                label.Text = labelText .. ": " .. tostring(value)
                if onChange then pcall(onChange, value) end
            end
        end)
        track.InputBegan:Connect(function(input)
            if input.UserInputType == Enum.UserInputType.MouseButton1 then
                local absX = math.clamp(input.Position.X - track.AbsolutePosition.X, 0, track.AbsoluteSize.X)
                local frac = absX / track.AbsoluteSize.X
                knob.Size = UDim2.new(frac,0,1,0)
                local value = math.floor(minVal + frac*(maxVal-minVal) + 0.5)
                label.Text = labelText .. ": " .. tostring(value)
                if onChange then pcall(onChange, value) end
            end
        end)
        return frame
    end

    -- Add toggles + sliders (core features)
    addToggle("Mag", "Visual mag square showing pickup range", toggles.Mag)
    addToggle("QBAimbot", "Smooth camera aim to nearest player (visual only)", toggles.QBAimbot)
    addToggle("BallESP", "Show ball markers", toggles.BallESP)
    addToggle("BallHitbox", "Show enlarged ball hitbox (visual)", toggles.BallHitbox)
    addToggle("BallPrediction", "Show predicted landing point", toggles.BallPrediction)
    addToggle("PlayerESP", "Show player name tags", toggles.PlayerESP)
    addToggle("AutoCatch", "Local-only auto-catch simulation", toggles.AutoCatch)
    addToggle("AutoThrow", "Local-only auto-throw simulation", toggles.AutoThrow)
    addToggle("Jump", "Apply Jump Power locally", toggles.Jump)
    addToggle("Speed", "Apply Walk Speed locally", toggles.Speed)
    addToggle("TargetLine", "Draw a screen line to aimbot target", toggles.TargetLine)
    addToggle("ThrowArc", "Show rough throw arc preview (visual)", toggles.ThrowArc)
    addToggle("PlayerDistance", "Show player distance in ESP tags", toggles.PlayerDistance)
    addToggle("TeamColors", "Color ESP by team", toggles.TeamColors)
    addToggle("AutoTP", "Auto-TP to closest ball (local only)", toggles.AutoTP)
    addToggle("TrajectoryVisual", "Show trajectory preview for training throws", toggles.TrajectoryVisual)
    addToggle("TrajectoryBeam", "Show continuous beam (dotted) along trajectory", toggles.TrajectoryBeam)
    -- New toggles
    addToggle("AutoPerfectKick", "Auto perfect kick (local simulation only)", toggles.AutoPerfectKick)
    addToggle("NoVisual", "Disable all visuals but keep local logic active", toggles.NoVisual)

    -- Core sliders
    addSlider("Mag (studs)", 1, 300, state.Mag, function(v) state.Mag = v end)
    addSlider("Jump Power", 10, 250, state.JumpPower, function(v) state.JumpPower = v end)
    addSlider("Walk Speed", 8, 100, state.Speed, function(v) state.Speed = v end)
    addSlider("QB Power", 10, 100, state.QBPower, function(v) state.QBPower = v end)
    addSlider("AutoTP Range (1-30)", 1, 30, state.AutoTPRange, function(v) state.AutoTPRange = v end)
    addSlider("AutoTP Interval (0.1-2s)", 0.1, 2, state.AutoTPInterval, function(v) state.AutoTPInterval = v end)

    -- Training module sliders
    addSlider("Throw Angle (deg)", 0, 90, state.TrainAngle, function(v) state.TrainAngle = v end)
    addSlider("Throw Power", 10, 250, state.TrainPower, function(v) state.TrainPower = v end)
    addSlider("Extend (1-10)", 1, 10, state.TrainExtend, function(v) state.TrainExtend = v end)
    addSlider("Spawn Velocity Factor (0.2-2.0)", 0.2, 2, state.TrainSpawnVelocityFactor, function(v) state.TrainSpawnVelocityFactor = v end)

    -- Training controls: spawn dummy, preview, clear
    local trainingBox = make(scroll, "Frame", {Size = UDim2.new(1,-24,0,120), BackgroundTransparency = 1})
    make(trainingBox, "TextLabel", {Text = "Training Module", Size = UDim2.new(1,-140,0,22), Position = UDim2.new(0,12,0,8), BackgroundTransparency = 1, TextColor3 = Color3.fromRGB(255,200,200), Font = Enum.Font.GothamBold, TextSize = 16, TextXAlignment = Enum.TextXAlignment.Left})
    make(trainingBox, "TextLabel", {Text = "Spawn a local test ball and preview its trajectory. Visual-only.", Size = UDim2.new(1,-140,0,40), Position = UDim2.new(0,12,0,28), BackgroundTransparency = 1, TextColor3 = Color3.fromRGB(200,200,200), Font = Enum.Font.Gotham, TextSize = 12, TextXAlignment = Enum.TextXAlignment.Left})

    local buttonsFrame = make(trainingBox, "Frame", {Size = UDim2.new(1,-24,0,44), Position = UDim2.new(0,12,0,68), BackgroundTransparency = 1})
    local spawnBtn = make(buttonsFrame, "TextButton", {Text = "Spawn Dummy Ball", Size = UDim2.new(0,220,0,36), Position = UDim2.new(0,0,0,0), BackgroundColor3 = Color3.fromRGB(80,160,80), Font = Enum.Font.GothamBold, TextColor3 = Color3.fromRGB(255,255,255)})
    make(spawnBtn, "UICorner", {CornerRadius = UDim.new(0,6)})
    local previewBtn = make(buttonsFrame, "TextButton", {Text = "Show Trajectory", Size = UDim2.new(0,180,0,36), Position = UDim2.new(0,230,0,0), BackgroundColor3 = Color3.fromRGB(60,140,220), Font = Enum.Font.GothamBold, TextColor3 = Color3.fromRGB(255,255,255)})
    make(previewBtn, "UICorner", {CornerRadius = UDim.new(0,6)})
    local clearBtn = make(buttonsFrame, "TextButton", {Text = "Clear Training", Size = UDim2.new(0,160,0,36), Position = UDim2.new(0,420,0,0), BackgroundColor3 = Color3.fromRGB(170,60,60), Font = Enum.Font.GothamBold, TextColor3 = Color3.fromRGB(255,255,255)})
    make(clearBtn, "UICorner", {CornerRadius = UDim.new(0,6)})

    -- Info note
    make(scroll, "TextLabel", {Text = "Note: Training elements are local-only and safe. Use Extend to lengthen visualization (1 = normal, up to 10x).", Size = UDim2.new(1,-48,0,40), BackgroundTransparency = 1, TextColor3 = Color3.fromRGB(190,190,190), Font = Enum.Font.Gotham, TextSize = 12, TextWrapped = true, TextXAlignment = Enum.TextXAlignment.Left})

    -- ===== Visual structures =====
    local ballESP = {}
    local playerESP = {}
    local magOutlineParts = {}
    local trajectoryParts = {} -- small spheres for trajectory preview
    local spawnedBall -- reference to spawned dummy ball (local-only)
    local trailDebrisTime = 6

    -- Create mag outline parts (four thin parts forming an outlined square)
    local function createMagOutline()
        -- clean old
        for _,p in pairs(magOutlineParts) do p:Destroy() end
        magOutlineParts = {}
        for i=1,4 do
            local part = Instance.new("Part")
            part.Anchored = true
            part.CanCollide = false
            part.Size = Vector3.new(1,0.4,1)
            part.Material = Enum.Material.Neon
            part.Color = Color3.fromRGB(255,120,120)
            part.Transparency = 0.6
            part.CastShadow = false
            part.Parent = Workspace
            table.insert(magOutlineParts, part)
        end
    end
    createMagOutline()

    -- Billboard helper
    local function createBillboard(part, text, color)
        local bb = Instance.new("BillboardGui")
        bb.Size = UDim2.new(0,140,0,40)
        bb.Adornee = part
        bb.AlwaysOnTop = true
        bb.StudsOffset = Vector3.new(0,2.2,0)
        local label = Instance.new("TextLabel", bb)
        label.Size = UDim2.new(1,0,1,0)
        label.BackgroundTransparency = 1
        label.Text = text
        label.TextColor3 = color or Color3.fromRGB(255,255,255)
        label.Font = Enum.Font.GothamBold
        label.TextSize = 14
        label.TextStrokeTransparency = 0.6
        bb.Parent = part
        return bb, label
    end

    -- Update mag outline around player (square)
    local function updateMagOutline()
        if toggles.NoVisual then
            for _,p in pairs(magOutlineParts) do p.Transparency = 1 end
            return
        end
        if not LocalPlayer.Character or not LocalPlayer.Character:FindFirstChild("HumanoidRootPart") then
            for _,p in pairs(magOutlineParts) do p.Transparency = 1 end
            return
        end
        local center = LocalPlayer.Character.HumanoidRootPart.Position
        local half = (state.Mag or 12)
        local thickness = 0.3
        -- top
        magOutlineParts[1].Size = Vector3.new(half*2 + thickness, 0.3, thickness)
        magOutlineParts[1].CFrame = CFrame.new(center + Vector3.new(0, 0.5, -half))
        -- bottom
        magOutlineParts[2].Size = Vector3.new(half*2 + thickness, 0.3, thickness)
        magOutlineParts[2].CFrame = CFrame.new(center + Vector3.new(0, 0.5, half))
        -- left
        magOutlineParts[3].Size = Vector3.new(thickness, 0.3, half*2 + thickness)
        magOutlineParts[3].CFrame = CFrame.new(center + Vector3.new(-half, 0.5, 0))
        -- right
        magOutlineParts[4].Size = Vector3.new(thickness, 0.3, half*2 + thickness)
        magOutlineParts[4].CFrame = CFrame.new(center + Vector3.new(half, 0.5, 0))
        for _,p in pairs(magOutlineParts) do p.Transparency = toggles.Mag and 0.6 or 1 end
    end

    -- Spawn a local-only dummy ball (appears only for you)
    local function spawnDummyBall(initialPos, initialVel)
        -- Remove old
        if spawnedBall and spawnedBall.Parent then spawnedBall:Destroy(); spawnedBall = nil end

        local ball = Instance.new("Part")
        ball.Name = "LocalDummyBall"
        ball.Shape = Enum.PartType.Ball
        ball.Size = Vector3.new(1,1,1)
        ball.Material = Enum.Material.SmoothPlastic
        ball.Color = Color3.fromRGB(255, 200, 60)
        ball.Anchored = false
        ball.CanCollide = false
        ball.Position = initialPos
        ball.Parent = Workspace -- client-created parts are local to your client
        -- small local visual trail
        local trail = Instance.new("Trail", ball)
        trail.Attachment0 = Instance.new("Attachment", ball)
        trail.Attachment1 = Instance.new("Attachment", ball)
        trail.Enabled = true
        trail.Lifetime = 0.25
        trail.Transparency = NumberSequence.new(0.3,1)
        -- set velocity (local physics)
        if ball and ball:IsA("BasePart") then
            pcall(function() ball.Velocity = initialVel * (state.TrainSpawnVelocityFactor or 1) end)
        end
        spawnedBall = ball
        Debris:AddItem(ball, 30) -- cleanup after 30s
        return ball
    end

    -- Compute projectile trajectory points (discrete samples)
    local function computeTrajectory(origin, velocity, steps, extendMultiplier)
        steps = steps or 40
        extendMultiplier = extendMultiplier or 1
        local points = {}
        local g = Workspace.Gravity or 196.2
        for i = 0, steps * extendMultiplier do
            local t = i * (1/30) -- sample every 1/30s; you can adjust
            local pos = origin + velocity * t + Vector3.new(0, -0.5 * g * t * t, 0)
            table.insert(points, pos)
        end
        return points
    end

    -- Visualize trajectory by spawning small spheres along points (client-only)
    local function showTrajectory(origin, velocity, extend)
        -- clear old
        for _,p in pairs(trajectoryParts) do if p and p.Parent then p:Destroy() end end
        trajectoryParts = {}
        if toggles.NoVisual then return end
        local steps = 40
        local pts = computeTrajectory(origin, velocity, steps, extend)
        for i, pos in ipairs(pts) do
            local s = Instance.new("Part")
            s.Size = Vector3.new(0.18,0.18,0.18)
            s.Shape = Enum.PartType.Ball
            s.Anchored = true
            s.CanCollide = false
            s.Material = Enum.Material.Neon
            s.Color = Color3.fromRGB(120,200,255)
            s.CFrame = CFrame.new(pos)
            s.Parent = Workspace
            Debris:AddItem(s, trailDebrisTime)
            table.insert(trajectoryParts, s)
        end
    end

    -- Clear training visuals
    local function clearTraining()
        if spawnedBall and spawnedBall.Parent then spawnedBall:Destroy(); spawnedBall = nil end
        for _,p in pairs(trajectoryParts) do if p and p.Parent then p:Destroy() end end
        trajectoryParts = {}
    end

    -- Create ball from player's hand (approx using HumanoidRootPart + lookVector)
    local function spawnBallFromPlayer()
        if not LocalPlayer.Character or not LocalPlayer.Character:FindFirstChild("HumanoidRootPart") or not Workspace.CurrentCamera then return end
        local hrp = LocalPlayer.Character.HumanoidRootPart
        local cam = Workspace.CurrentCamera
        local origin = hrp.Position + Vector3.new(0,2,0) -- slightly above
        local angleDeg = state.TrainAngle or 30
        local power = state.TrainPower or 80
        -- compute direction: use camera lookVector rotated upward by angle
        local forward = cam.CFrame.LookVector
        -- rotate forward vector upward by angleDeg around cross axis
        local right = cam.CFrame.RightVector
        local angleRad = math.rad(angleDeg)
        local dir = (CFrame.fromMatrix(Vector3.new(), forward, right, cam.CFrame.UpVector) * CFrame.Angles(-angleRad,0,0)).LookVector
        local velocity = dir * power
        return spawnDummyBall(origin + dir * 1.5, velocity)
    end

    -- ===== Other visuals (ESP, PlayerESP, BallESP, Aimbot target line) =====
    local function createBill(part, txt, color)
        local bb = Instance.new("BillboardGui")
        bb.Size = UDim2.new(0,130,0,36)
        bb.Adornee = part
        bb.AlwaysOnTop = true
        bb.StudsOffset = Vector3.new(0,1.8,0)
        local label = Instance.new("TextLabel", bb)
        label.Size = UDim2.new(1,0,1,0)
        label.BackgroundTransparency = 1
        label.Text = txt
        label.Font = Enum.Font.GothamBold
        label.TextSize = 14
        label.TextColor3 = color or Color3.fromRGB(255,255,255)
        label.TextStrokeTransparency = 0.6
        bb.Parent = part
        return bb, label
    end

    -- (For brevity, reuse earlier logic for ESP update)
    local function updateBallESP()
        if toggles.NoVisual then
            for k,v in pairs(ballESP) do
                if v.bb and v.bb.Parent then v.bb:Destroy() end
                if v.pred and v.pred.Parent then v.pred:Destroy() end
                if v.hit and v.hit.Parent then v.hit:Destroy() end
            end
            ballESP = {}
            return
        end

        if not toggles.BallESP then
            for k,v in pairs(ballESP) do
                if v.bb and v.bb.Parent then v.bb:Destroy() end
                if v.pred and v.pred.Parent then v.pred:Destroy() end
                if v.hit and v.hit.Parent then v.hit:Destroy() end
            end
            ballESP = {}
            return
        end

        for _, obj in pairs(Workspace:GetDescendants()) do
            if obj:IsA("BasePart") and obj.Name:lower():match("ball") then
                if not ballESP[obj] then
                    local bb, label = createBill(obj, "Ball", Color3.fromRGB(255,200,60))
                    ballESP[obj] = {bb = bb, label = label, hit = nil, pred = nil}
                end
                local entry = ballESP[obj]
                if entry and entry.label then entry.label.Text = "Ball | Mag:" .. tostring(state.Mag) end
                if toggles.BallHitbox then
                    if not entry.hit then
                        local h = Instance.new("Part")
                        h.Anchored = true; h.CanCollide = false; h.Size = obj.Size * 1.6; h.Transparency = 0.6; h.Material = Enum.Material.Neon; h.Parent = Workspace
                        entry.hit = h
                    end
                    if entry.hit then entry.hit.CFrame = obj.CFrame end
                else
                    if entry.hit then entry.hit:Destroy(); entry.hit = nil end
                end
            end
        end
    end

    -- Player ESP (brief)
    local function updatePlayerESP()
        if toggles.NoVisual then
            for pl, entry in pairs(playerESP) do
                if entry.bb and entry.bb.Parent then entry.bb:Destroy() end
                playerESP[pl] = nil
            end
            return
        end

        for pl, entry in pairs(playerESP) do
            if not pl.Parent or not toggles.PlayerESP then
                if entry.bb and entry.bb.Parent then entry.bb:Destroy() end
                playerESP[pl] = nil
            end
        end
        if not toggles.PlayerESP then return end
        for _, pl in pairs(Players:GetPlayers()) do
            if pl ~= LocalPlayer and pl.Character and pl.Character:FindFirstChild("HumanoidRootPart") then
                local hrp = pl.Character.HumanoidRootPart
                if not playerESP[pl] then
                    local color = (toggles.TeamColors and (pl.Team and pl.Team.TeamColor.Color or Color3.fromRGB(200,200,200))) or Color3.fromRGB(200,200,200)
                    local bb, label = createBill(hrp, pl.Name, color)
                    playerESP[pl] = {bb = bb, label = label}
                end
                local entry = playerESP[pl]
                if entry and entry.label then
                    local info = pl.Name
                    if toggles.PlayerDistance and LocalPlayer.Character and LocalPlayer.Character:FindFirstChild("HumanoidRootPart") then
                        local dist = (LocalPlayer.Character.HumanoidRootPart.Position - hrp.Position).Magnitude
                        info = string.format("%s | %.1f studs", pl.Name, dist)
                    end
                    entry.label.Text = info
                end
            end
        end
    end

    -- Aimbot target (visual) & target line
    local function getAimbotTarget()
        local cam = Workspace.CurrentCamera
        if not cam then return nil end
        local best, bestDist = nil, math.huge
        for _, pl in pairs(Players:GetPlayers()) do
            if pl ~= LocalPlayer and pl.Character and pl.Character:FindFirstChild("HumanoidRootPart") and pl.Character:FindFirstChild("Humanoid") and pl.Character.Humanoid.Health > 0 then
                local pos = pl.Character.HumanoidRootPart.Position
                local screenPos, onScreen = cam:WorldToViewportPoint(pos)
                if onScreen then
                    local center = Vector2.new(cam.ViewportSize.X/2, cam.ViewportSize.Y/2)
                    local d = (Vector2.new(screenPos.X,screenPos.Y) - center).Magnitude
                    if d < bestDist then bestDist = d; best = pl end
                end
            end
        end
        return best
    end

    local targetLineFrame = make(screenGui, "Frame", {
        BackgroundColor3 = Color3.fromRGB(255,80,80),
        Size = UDim2.new(0,4,0,200),
        AnchorPoint = Vector2.new(0.5,0),
        Position = UDim2.new(0.5,0,0.5,0),
        BackgroundTransparency = 0,
        Visible = false
    })
    make(targetLineFrame, "UICorner", {CornerRadius = UDim.new(0,2)})
    local cameraSmoothing = 12

    -- AutoCatch and AutoThrow (visual)
    local function tryAutoCatch()
        if not toggles.AutoCatch then return end
        if not LocalPlayer.Character or not LocalPlayer.Character:FindFirstChild("HumanoidRootPart") then return end
        local myPos = LocalPlayer.Character.HumanoidRootPart.Position
        for _, obj in pairs(Workspace:GetDescendants()) do
            if obj:IsA("BasePart") and obj.Name:lower():match("ball") then
                local d = (obj.Position - myPos).Magnitude
                if d < (state.Mag or 12) then
                    state.catches = state.catches + 1
                    -- small flash
                    local fx = Instance.new("Part")
                    fx.Size = Vector3.new(0.6,0.6,0.6); fx.Transparency = 1; fx.Anchored = true; fx.CanCollide = false; fx.CFrame = CFrame.new(myPos + Vector3.new(0,3,0)); fx.Parent = Workspace
                    Debris:AddItem(fx, 0.6)
                end
            end
        end
    end

    local autoThrowTimer = 0
    local function tryAutoThrow(dt)
        if not toggles.AutoThrow then return end
        autoThrowTimer = autoThrowTimer + dt
        if autoThrowTimer > 1.2 then
            autoThrowTimer = 0
            state.completions = state.completions + 1
        end
    end

    -- Apply local movement tweaks
    local function applyLocals()
        if LocalPlayer.Character and LocalPlayer.Character:FindFirstChild("Humanoid") then
            local hum = LocalPlayer.Character.Humanoid
            if toggles.Jump then pcall(function() hum.JumpPower = state.JumpPower end) end
            if toggles.Speed then pcall(function() hum.WalkSpeed = state.Speed end) end
        end
    end

    -- Auto-TP to closest ball (local-only)
    local lastAutoTP = 0
    local function autoTPToClosestBall(dt)
        if not toggles.AutoTP then return end
        lastAutoTP = lastAutoTP + dt
        if lastAutoTP < (state.AutoTPInterval or 0.6) then return end
        lastAutoTP = 0
        if not LocalPlayer.Character or not LocalPlayer.Character:FindFirstChild("HumanoidRootPart") then return end
        local hrp = LocalPlayer.Character.HumanoidRootPart
        local bestPart, bestDist = nil, math.huge
        for _, obj in pairs(Workspace:GetDescendants()) do
            if obj:IsA("BasePart") and obj.Name:lower():match("ball") then
                local d = (obj.Position - hrp.Position).Magnitude
                if d < bestDist and d <= (state.AutoTPRange or 12) then
                    bestDist = d; bestPart = obj
                end
            end
        end
        if bestPart then
            pcall(function() hrp.CFrame = bestPart.CFrame + Vector3.new(0, 3.5, 0) end)
        end
    end

    -- ===== AUTO PERFECT KICK (local-only simulation) =====
    local perfectKickTimer = 0
    local function tryAutoPerfectKick(dt)
        if not toggles.AutoPerfectKick then return end
        perfectKickTimer = perfectKickTimer + dt
        if perfectKickTimer < (state.PerfectKickCooldown or 0.6) then return end
        perfectKickTimer = 0
        if not LocalPlayer.Character or not LocalPlayer.Character:FindFirstChild("HumanoidRootPart") then return end
        local hrp = LocalPlayer.Character.HumanoidRootPart
        local closest, bestDist = nil, math.huge
        for _, obj in pairs(Workspace:GetDescendants()) do
            if obj:IsA("BasePart") and obj.Name:lower():match("ball") then
                local d = (obj.Position - hrp.Position).Magnitude
                if d < bestDist and d <= (state.Mag or 12) then
                    bestDist = d; closest = obj
                end
            end
        end
        if closest and bestDist and bestDist <= (state.Mag or 12) then
            -- Simulate "perfect kick" locally:
            state.completions = state.completions + 1
            -- small local flash effect to indicate perfect kick happened (client-only)
            local fx = Instance.new("Part")
            fx.Name = "LocalPerfectKickFX"
            fx.Size = Vector3.new(0.6,0.6,0.6)
            fx.Transparency = 0
            fx.Anchored = true
            fx.CanCollide = false
            fx.CFrame = CFrame.new(hrp.Position + Vector3.new(0, 3, 0))
            fx.Material = Enum.Material.Neon
            fx.Color = Color3.fromRGB(255,255,80)
            fx.Parent = Workspace
            Debris:AddItem(fx, 0.35)
            -- small pop movement for the ball (client-only)
            if closest and closest:IsA("BasePart") then
                pcall(function()
                    closest.Velocity = (closest.CFrame.LookVector * 60) + Vector3.new(0, 30, 0)
                end)
            end
        end
    end

    -- ===== Training button handlers =====
    spawnBtn.MouseButton1Click:Connect(function()
        -- spawn from player if possible
        if not LocalPlayer.Character or not LocalPlayer.Character:FindFirstChild("HumanoidRootPart") or not Workspace.CurrentCamera then
            loaderLog.Text = "Can't spawn: no character or camera."
            return
        end
        local hrp = LocalPlayer.Character.HumanoidRootPart
        local cam = Workspace.CurrentCamera
        local origin = hrp.Position + Vector3.new(0,2,0)
        local angleDeg = state.TrainAngle or 30
        local power = state.TrainPower or 80
        -- direction derived from camera lookvector rotated by angle up
        local forward = cam.CFrame.LookVector
        local right = cam.CFrame.RightVector
        local angleRad = math.rad(angleDeg)
        local dir = (CFrame.new(Vector3.new(), forward, right, cam.CFrame.UpVector) * CFrame.Angles(-angleRad,0,0)).LookVector
        local velocity = dir * power
        spawnDummyBall(origin + dir*1.5, velocity)
        loaderLog.Text = "Spawned local dummy ball (visual-only)."
    end)

    previewBtn.MouseButton1Click:Connect(function()
        if not LocalPlayer.Character or not LocalPlayer.Character:FindFirstChild("HumanoidRootPart") or not Workspace.CurrentCamera then
            loaderLog.Text = "Can't preview: no character or camera."
            return
        end
        local hrp = LocalPlayer.Character.HumanoidRootPart
        local cam = Workspace.CurrentCamera
        local origin = hrp.Position + Vector3.new(0,2,0)
        local angleDeg = state.TrainAngle or 30
        local power = state.TrainPower or 80
        local forward = cam.CFrame.LookVector
        local right = cam.CFrame.RightVector
        local angleRad = math.rad(angleDeg)
        local dir = (CFrame.new(Vector3.new(), forward, right, cam.CFrame.UpVector) * CFrame.Angles(-angleRad,0,0)).LookVector
        local velocity = dir * power
        showTrajectory(origin + dir*1.5, velocity, state.TrainExtend or 1)
        loaderLog.Text = "Showing trajectory preview (local-only). Extend=" .. tostring(state.TrainExtend)
    end)

    clearBtn.MouseButton1Click:Connect(function()
        clearTraining()
        loaderLog.Text = "Cleared training visuals."
    end)

    -- ===== BOTTOM DOCK: Panels with more than 3 features each =====
    local dock = make(mainFrame, "Frame", {Size = UDim2.new(1,-24,0,56), Position = UDim2.new(0,12,1,-160), BackgroundTransparency = 1})
    local dockInner = make(dock, "Frame", {Size = UDim2.new(1,0,1,0), BackgroundTransparency = 1})
    -- Category buttons
    local function makeDockButton(text, x)
        local b = make(dockInner, "TextButton", {Text = text, Size = UDim2.new(0,160,0,40), Position = UDim2.new(0,x,0,8), BackgroundColor3 = Color3.fromRGB(60,60,60), TextColor3 = Color3.fromRGB(240,240,240), Font = Enum.Font.GothamBold, TextSize = 14})
        make(b, "UICorner", {CornerRadius = UDim.new(0,6)})
        return b
    end

    local panels = {}
    local function createPanel(name, x)
        local panel = make(mainFrame, "Frame", {Size = UDim2.new(0,360,0,160), Position = UDim2.new(0,x,1,-320), BackgroundColor3 = Color3.fromRGB(24,24,24)})
        make(panel, "UICorner", {CornerRadius = UDim.new(0,8)})
        panel.Visible = false
        local title = make(panel, "TextLabel", {Text = name, Size = UDim2.new(1,0,0,28), Position = UDim2.new(0,0,0,4), BackgroundTransparency = 1, TextColor3 = Color3.fromRGB(220,220,220), Font = Enum.Font.GothamBold, TextSize = 14})
        local inner = make(panel, "Frame", {Size = UDim2.new(1,-24,1,-36), Position = UDim2.new(0,12,0,36), BackgroundTransparency = 1})
        local y = 0
        return panel, inner, function(add)
            add(inner, 0, 0) -- filler so closure compiles
        end
    end

    -- Movement panel (more than 3 features)
    local movementPanel = make(mainFrame, "Frame", {Size = UDim2.new(0,360,0,160), Position = UDim2.new(0,12,1,-320), BackgroundColor3 = Color3.fromRGB(24,24,24)})
    make(movementPanel, "UICorner", {CornerRadius = UDim.new(0,8)})
    movementPanel.Visible = false
    make(movementPanel, "TextLabel", {Text = "Movement", Size = UDim2.new(1,0,0,28), Position = UDim2.new(0,0,0,4), BackgroundTransparency = 1, TextColor3 = Color3.fromRGB(220,220,220), Font = Enum.Font.GothamBold, TextSize = 14})
    local mvInner = make(movementPanel, "Frame", {Size = UDim2.new(1,-24,1,-36), Position = UDim2.new(0,12,0,36), BackgroundTransparency = 1})
    -- Movement features ( >3 )
    local mvY = 0
    local function addMVFeature(text, callback)
        local b = make(mvInner, "TextButton", {Text = text, Size = UDim2.new(1,0,0,28), Position = UDim2.new(0,0,0,mvY), BackgroundColor3 = Color3.fromRGB(45,45,45), Font = Enum.Font.Gotham, TextColor3 = Color3.fromRGB(230,230,230), TextSize = 14})
        make(b, "UICorner", {CornerRadius = UDim.new(0,6)})
        b.MouseButton1Click:Connect(callback)
        mvY = mvY + 34
    end
    addMVFeature("Toggle WalkSpeed (Speed)", function() toggles.Speed = not toggles.Speed end)
    addMVFeature("Increase Speed +5", function() state.Speed = math.min(300, state.Speed + 5) end)
    addMVFeature("Decrease Speed -5", function() state.Speed = math.max(8, state.Speed - 5) end)
    addMVFeature("Toggle Jump (JumpPower)", function() toggles.Jump = not toggles.Jump end)
    addMVFeature("Reset Movement", function() state.Speed = 35; state.JumpPower = 100 end)

    -- Training panel (more than 3 features)
    local trainingPanel = make(mainFrame, "Frame", {Size = UDim2.new(0,360,0,160), Position = UDim2.new(0,388,1,-320), BackgroundColor3 = Color3.fromRGB(24,24,24)})
    make(trainingPanel, "UICorner", {CornerRadius = UDim.new(0,8)})
    trainingPanel.Visible = false
    make(trainingPanel, "TextLabel", {Text = "Training", Size = UDim2.new(1,0,0,28), Position = UDim2.new(0,0,0,4), BackgroundTransparency = 1, TextColor3 = Color3.fromRGB(220,220,220), Font = Enum.Font.GothamBold, TextSize = 14})
    local trInner = make(trainingPanel, "Frame", {Size = UDim2.new(1,-24,1,-36), Position = UDim2.new(0,12,0,36), BackgroundTransparency = 1})
    local trY = 0
    local function addTRFeature(text, callback)
        local b = make(trInner, "TextButton", {Text = text, Size = UDim2.new(1,0,0,28), Position = UDim2.new(0,0,0,trY), BackgroundColor3 = Color3.fromRGB(45,45,45), Font = Enum.Font.Gotham, TextColor3 = Color3.fromRGB(230,230,230), TextSize = 14})
        make(b, "UICorner", {CornerRadius = UDim.new(0,6)})
        b.MouseButton1Click:Connect(callback)
        trY = trY + 34
    end
    addTRFeature("Spawn Dummy Ball (from player)", function() spawnBallFromPlayer() end)
    addTRFeature("Show Trajectory (preview)", function() 
        if LocalPlayer.Character and LocalPlayer.Character:FindFirstChild("HumanoidRootPart") and Workspace.CurrentCamera then
            local hrp = LocalPlayer.Character.HumanoidRootPart
            local cam = Workspace.CurrentCamera
            local origin = hrp.Position + Vector3.new(0,2,0)
            local angleRad = math.rad(state.TrainAngle or 30)
            local forward = cam.CFrame.LookVector
            local right = cam.CFrame.RightVector
            local dir = (CFrame.new(Vector3.new(), forward, right, cam.CFrame.UpVector) * CFrame.Angles(-angleRad,0,0)).LookVector
            local velocity = dir * state.TrainPower
            showTrajectory(origin + dir*1.5, velocity, state.TrainExtend or 1)
        end
    end)
    addTRFeature("Clear Training Visuals", function() clearTraining() end)
    addTRFeature("Increase Power +10", function() state.TrainPower = state.TrainPower + 10 end)

    -- Misc panel (more than 3 features)
    local miscPanel = make(mainFrame, "Frame", {Size = UDim2.new(0,360,0,160), Position = UDim2.new(0,12,1,-492), BackgroundColor3 = Color3.fromRGB(24,24,24)})
    make(miscPanel, "UICorner", {CornerRadius = UDim.new(0,8)})
    miscPanel.Visible = false
    make(miscPanel, "TextLabel", {Text = "Misc", Size = UDim2.new(1,0,0,28), Position = UDim2.new(0,0,0,4), BackgroundTransparency = 1, TextColor3 = Color3.fromRGB(220,220,220), Font = Enum.Font.GothamBold, TextSize = 14})
    local mcInner = make(miscPanel, "Frame", {Size = UDim2.new(1,-24,1,-36), Position = UDim2.new(0,12,0,36), BackgroundTransparency = 1})
    local mcY = 0
    local function addMCFeature(text, callback)
        local b = make(mcInner, "TextButton", {Text = text, Size = UDim2.new(1,0,0,28), Position = UDim2.new(0,0,0,mcY), BackgroundColor3 = Color3.fromRGB(45,45,45), Font = Enum.Font.Gotham, TextColor3 = Color3.fromRGB(230,230,230), TextSize = 14})
        make(b, "UICorner", {CornerRadius = UDim.new(0,6)})
        b.MouseButton1Click:Connect(callback)
        mcY = mcY + 34
    end
    addMCFeature("Toggle BallESP", function() toggles.BallESP = not toggles.BallESP end)
    addMCFeature("Toggle PlayerESP", function() toggles.PlayerESP = not toggles.PlayerESP end)
    addMCFeature("Toggle AutoPerfectKick", function() toggles.AutoPerfectKick = not toggles.AutoPerfectKick end)
    addMCFeature("Toggle NoVisual", function() toggles.NoVisual = not toggles.NoVisual end)
    addMCFeature("Reset Counters", function() state.catches = 0; state.completions = 0; state.misses = 0 end)

    -- Dock buttons to open/close panels
    local btnMovement = makeDockButton("Movement", 8)
    local btnTraining = makeDockButton("Training", 180)
    local btnMisc = makeDockButton("Misc", 352)

    btnMovement.MouseButton1Click:Connect(function() movementPanel.Visible = not movementPanel.Visible end)
    btnTraining.MouseButton1Click:Connect(function() trainingPanel.Visible = not trainingPanel.Visible end)
    btnMisc.MouseButton1Click:Connect(function() miscPanel.Visible = not miscPanel.Visible end)

    -- ===== Main loop =====
    RunService.RenderStepped:Connect(function(dt)
        state.catchTimer = state.catchTimer + dt
        statusBar.Text = string.format("Catches:%d | Completions:%d | Misses:%d | Mag:%d | Jump:%d | Speed:%d | QB:%d | Timer:%.1f",
            state.catches, state.completions, state.misses, state.Mag, state.JumpPower, state.Speed, state.QBPower, state.catchTimer)

        -- visuals & locals
        pcall(updateBallESP)
        pcall(updatePlayerESP)
        pcall(applyLocals)
        pcall(tryAutoCatch)
        pcall(tryAutoThrow, dt)
        pcall(autoTPToClosestBall, dt)

        -- auto-perfect kick (local)
        pcall(tryAutoPerfectKick, dt)

        -- aimbot camera (visual-only)
        if toggles.QBAimbot then
            local target = getAimbotTarget()
            if target and Workspace.CurrentCamera then
                local cam = Workspace.CurrentCamera
                local targetPos = target.Character.HumanoidRootPart.Position
                local desired = CFrame.new(cam.CFrame.Position, targetPos)
                cam.CFrame = cam.CFrame:Lerp(desired, math.clamp(dt * cameraSmoothing * (state.QBPower/100), 0, 1))
                if toggles.TargetLine and not toggles.NoVisual then
                    local screenPos, onScreen = cam:WorldToViewportPoint(targetPos)
                    local center = Vector2.new(cam.ViewportSize.X/2, cam.ViewportSize.Y/2)
                    if onScreen then
                        local dx = screenPos.X - center.X
                        local dy = screenPos.Y - center.Y
                        local len = Vector2.new(dx, dy).Magnitude
                        targetLineFrame.Size = UDim2.new(0,4,0,len)
                        targetLineFrame.Rotation = math.deg(math.atan2(dy, dx)) + 90
                        local x = center.X + dx/2
                        local y = center.Y + dy/2
                        targetLineFrame.Position = UDim2.new(0, x, 0, y)
                        targetLineFrame.Visible = true
                    else
                        targetLineFrame.Visible = false
                    end
                else
                    if targetLineFrame then targetLineFrame.Visible = false end
                end
            else
                if targetLineFrame then targetLineFrame.Visible = false end
            end
        end

        -- mag outline
        pcall(updateMagOutline)
    end)

    -- Keep ESP updated when players join/leave
    Players.PlayerAdded:Connect(function() pcall(updatePlayerESP) end)
    Players.PlayerRemoving:Connect(function(pl) if playerESP[pl] and playerESP[pl].bb then playerESP[pl].bb:Destroy() end playerESP[pl] = nil end)

    -- Periodic ball scan (for ballESP)
    spawn(function()
        while screenGui and screenGui.Parent do
            pcall(updateBallESP)
            wait(0.8)
        end
    end)
end

-- Hook up loader button
loadBtn.MouseButton1Click:Connect(function()
    loadBtn.Text = "Loading..."
    loadBtn.Active = false
    loaderLog.Text = "Status: Initializing local Mega Hub with Training Module..."
    for i=1,3 do wait(0.18); loaderLog.Text = loaderLog.Text .. "\nStep " .. i .. " complete." end
    createMegaHub()
    wait(0.12)
    loaderLog.Text = loaderLog.Text .. "\nMega Hub + Training loaded locally. Use K to toggle loader. Use Hub to spawn/preview training balls."
    loadBtn.Text = "Load Hub"
    loadBtn.Active = true
end)

print("[FF2 Mega Hub + Training] Ready (local-only).")

Features & Information

op fetures mags might not work dempending fixing it still
#READ BEFORE EXCUTE IT IS STILL IN DEVLOPEMENT PLS

Community Comments (8)

NoSkillzNoSkillzOct 20, 2025

yayyyy ai slop, my favorite

Replying to @NoSkillz
kruzkruzOct 20, 2025

@NoSkillz are you being fr or sarcastic?

Replying to @kruz
NoSkillzNoSkillzOct 20, 2025(edited)

@kruz ai slop already means its trash

Replying to @NoSkillz
kruzkruzOct 20, 2025

@NoSkillz ik

deedeenkydeedeenkyOct 24, 2025

this script so ahh got instantly banned and im using a good executor

demarius050213demarius050213Oct 26, 2025

How to get it

demarius050213demarius050213Oct 26, 2025

It won’t pop up

meekyman2meekyman2Nov 2, 2025

i got banned

More Scripts for Universal Scripts