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