
Key RequiredUniversal

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



yayyyy ai slop, my favorite
@NoSkillz are you being fr or sarcastic?
@kruz ai slop already means its trash
@NoSkillz ik
this script so ahh got instantly banned and im using a good executor
How to get it
It won’t pop up
i got banned