local RunService = game:GetService("RunService") ------------------------------------------------------------------ -- SERVER HALF (runs only when this code is in ServerScriptService) ------------------------------------------------------------------ if RunService:IsServer() then local Players = game:GetService("Players") local ReplicatedStorage = game:GetService("ReplicatedStorage") -- ===== Settings ===== local ALLOWED_USER_IDS = {} -- add your UserId (and testers). The place owner is always allowed. local FUEL_FILL_TO = nil -- e.g. 100; only used when no Max/Capacity value is found local EXTRA_FUEL_NAMES = {} -- exact name(s) of your fuel value, e.g. { "Juice", "BikeGas" } local PAUSE_SCRIPTS_WHILE_FLYING = {} -- exact names of YOUR game scripts that teleport players back (anti-cheat, "return to bike"...). -- They're switched off while someone flies and back on after. e.g. { "BikeReturn", "AntiCheat" } local AUTO_PAUSE_SUSPECTS = false -- while flying, also switch off scripts whose NAME looks like a "teleport me back" script -- (anti-cheat, return, leash, boundary, kick...). They come back on when you stop flying. local SCAN_INTERVAL = 0.5 -- seconds between searches for fuel values local AUTO_DROPS_NEEDED = 3 -- scans in a row a number must fall before auto-detect locks it local PRINT_DEBUG = true -- print what gets locked to the Output window -- ==================== local FUEL_WORDS = { "fuel", "petrol", "gasoline", "tank", "energy", "juice", "battery" } local IGNORE_WORDS = { "rate", "use", "usage", "drain", "consum", "burn", "cost", "price", "efficien", "pedal", "throttle" } local extraSet = {} for _, nm in ipairs(EXTRA_FUEL_NAMES) do extraSet[string.lower(nm)] = true end local remote = Instance.new("RemoteEvent") remote.Name = "InfiniteFuelToggle" remote.Parent = ReplicatedStorage local function isAllowed(player) if RunService:IsStudio() then return true end if game.CreatorType == Enum.CreatorType.User and player.UserId == game.CreatorId then return true end return table.find(ALLOWED_USER_IDS, player.UserId) ~= nil end -- "current" = fuel level, "max" = capacity, nil = not fuel related local function classify(name) local n = string.lower(name) if extraSet[n] then return "current" end local matches = n == "gas" or string.find(n, "^gas") ~= nil or string.find(n, "gas$") ~= nil for _, word in ipairs(FUEL_WORDS) do if string.find(n, word, 1, true) then matches = true break end end if not matches then return nil end if string.find(n, "max", 1, true) or string.find(n, "capacity", 1, true) then return "max" end for _, word in ipairs(IGNORE_WORDS) do if string.find(n, word, 1, true) then return nil end end return "current" end local function readNumber(obj, attr) local v if attr then v = obj:GetAttribute(attr) else v = obj.Value end return type(v) == "number" and v or nil end local function write(obj, attr, value) if attr then obj:SetAttribute(attr, value) else obj.Value = value end end local function enforce(entry) if not entry.obj.Parent then return end local current = readNumber(entry.obj, entry.attr) if not current then return end local target if entry.max and entry.max.obj.Parent then target = readNumber(entry.max.obj, entry.max.attr) end target = target or FUEL_FILL_TO if target then entry.seen = target if current < target then write(entry.obj, entry.attr, target) end elseif current > entry.seen then entry.seen = current -- no max known: a refuel raises the held level elseif current < entry.seen then write(entry.obj, entry.attr, entry.seen) end end -- state[player] = { entries = { [obj] = { [key] = entry } }, list = {}, last = 0, watch = {} } local state = {} local VALUE_KEY = "\0value" local function dropState(s) for _, byObj in pairs(s.entries) do for _, entry in pairs(byObj) do if entry.conn then entry.conn:Disconnect() end end end end local function topModel(part) local model = part:FindFirstAncestorOfClass("Model") while model and model.Parent and model.Parent:IsA("Model") do model = model.Parent end return model end local function pathOf(obj, attr) return obj:GetFullName() .. (attr and ("." .. attr) or "") end local function register(s, item, auto, maxRef) local byObj = s.entries[item.obj] if not byObj then byObj = {} s.entries[item.obj] = byObj end local key = item.attr or VALUE_KEY local entry = byObj[key] if not entry then entry = { obj = item.obj, attr = item.attr, seen = item.value, auto = auto } byObj[key] = entry local signal if item.attr then signal = item.obj:GetAttributeChangedSignal(item.attr) else signal = item.obj:GetPropertyChangedSignal("Value") end entry.conn = signal:Connect(function() enforce(entry) -- put it back the moment something lowers it end) if PRINT_DEBUG then print("[InfiniteFuel/server] locking" .. (auto and " (auto-detected)" or "") .. ":", pathOf(item.obj, item.attr), "=", item.value) end end entry.max = maxRef or entry.max return entry end local function scan(player, s) local currents, maxes, numeric = {}, {}, {} local function consider(inst) if inst:IsA("NumberValue") or inst:IsA("IntValue") then local kind = classify(inst.Name) if kind == "current" then table.insert(currents, { obj = inst, value = inst.Value }) elseif kind == "max" then table.insert(maxes, { obj = inst }) else table.insert(numeric, { obj = inst, value = inst.Value }) end end for name, value in pairs(inst:GetAttributes()) do if type(value) == "number" then local kind = classify(name) if kind == "current" then table.insert(currents, { obj = inst, attr = name, value = value }) elseif kind == "max" then table.insert(maxes, { obj = inst, attr = name }) elseif name ~= "InfiniteFuel" then table.insert(numeric, { obj = inst, attr = name, value = value }) end end end end local function scanTree(root) consider(root) for _, inst in ipairs(root:GetDescendants()) do consider(inst) end end local character = player.Character local humanoid = character and character:FindFirstChildOfClass("Humanoid") local seat = humanoid and humanoid.SeatPart if seat then local bike = topModel(seat) if bike then scanTree(bike) end end if character then scanTree(character) end scanTree(player) local function container(item) return item.attr and item.obj or item.obj.Parent end local maxByContainer = {} for _, m in ipairs(maxes) do local c = container(m) if c and not maxByContainer[c] then maxByContainer[c] = m end end local seenNow = {} for _, item in ipairs(currents) do local entry = register(s, item, false, maxByContainer[container(item)]) seenNow[entry] = true end -- Nothing matched a fuel name: lock any number that keeps falling if #currents == 0 then for _, item in ipairs(numeric) do local byObj = s.watch[item.obj] if not byObj then byObj = {} s.watch[item.obj] = byObj end local key = item.attr or VALUE_KEY local w = byObj[key] if not w then byObj[key] = { last = item.value, drops = 0 } else if item.value < w.last then w.drops += 1 elseif item.value > w.last then w.drops = 0 end w.last = item.value if w.drops >= AUTO_DROPS_NEEDED and item.value > 0 then local entry = register(s, item, true) seenNow[entry] = true end end end end local list = {} for obj, byObj in pairs(s.entries) do for key, entry in pairs(byObj) do if (seenNow[entry] or entry.auto) and entry.obj.Parent then table.insert(list, entry) else if entry.conn then entry.conn:Disconnect() end byObj[key] = nil end end if next(byObj) == nil then s.entries[obj] = nil end end s.list = list for _, entry in ipairs(list) do enforce(entry) end end remote.OnServerEvent:Connect(function(player, on) if not isAllowed(player) then return end if on == true then player:SetAttribute("InfiniteFuel", true) -- hook for your own drain code if not state[player] then state[player] = { entries = {}, list = {}, last = -math.huge, watch = {} } end else player:SetAttribute("InfiniteFuel", nil) local s = state[player] if s then dropState(s) state[player] = nil end end end) Players.PlayerRemoving:Connect(function(player) local s = state[player] if s then dropState(s) state[player] = nil end end) RunService.Heartbeat:Connect(function() local now = os.clock() for player, s in pairs(state) do if now - s.last >= SCAN_INTERVAL then s.last = now scan(player, s) end for _, entry in ipairs(s.list) do enforce(entry) end end end) ------------------------------------------------------------------ -- Fly (server side) -- The bike's seat and welds live on the SERVER. If the server still thinks you're attached to -- the bike, or doesn't accept your movement, it drags you back to the spot where you took off -- (that's the "teleport back"). So while you fly, the server: -- * takes you off the seat, switches that seat off, and keeps un-seating you, -- * cuts every weld/joint tying your character to the bike (on the server, where it counts), -- * gives YOU ownership of your character's physics and checks that it really worked, -- * if your movement still isn't reaching the server, it switches to SERVER-DRIVEN flight: -- your client just sends where you want to go and the server moves you. No disagreement -- between client and server is possible, so there's nothing to snap back to, -- * puts you straight back if any other script teleports you while you fly, -- * sets the attribute "Flying" = true on your Player and character. -- If YOUR OWN game scripts pull players back (anti-cheat, "return to bike", speed checks), add -- this to the top of that code: if player:GetAttribute("Flying") then return end ------------------------------------------------------------------ do local FLY_MAX_SPEED = 700 -- the server never accepts a faster fly speed than this local FLY_LEAN = math.rad(30) local YANK_MIN_STUDS = 60 -- CLIENT mode only: a jump bigger than this (against your direction) counts as a teleport -- Server-driven flight is the most reliable (the server knows exactly where you should be, so ANY teleport, -- big or small, is caught and undone). Leave this true. Set false only if you want the old client-driven flight. local FORCE_SERVER_FLY = true local SERVER_YANK_SLACK = 3 -- studs of wiggle room on top of how far your flying could have moved you -- AFTER you stop flying, a "teleport guard" keeps protecting you: any teleport that walking/falling can't explain is -- undone, and the bike's seat can't pull you back from far away. It ends when you respawn, when you walk up to a -- seat and sit on your own, or after this many seconds (0 = no time limit). NOTE: while it's active, teleports from -- your own game (checkpoints, teleport menus) are undone too, so set a limit if you use those. local GUARD_SECONDS = 600 local GUARD_MIN_STUDS = 20 -- a jump bigger than (walk/fall distance + this) counts as a teleport local GUARD_SEAT_RANGE = 20 -- seats you've flown away from stay switched off until you're this close again local pausedCount, pausedWas = {}, {} -- for PAUSE_SCRIPTS_WHILE_FLYING (shared by everyone who flies) local flyRemote = Instance.new("RemoteFunction") flyRemote.Name = "FlyRequest" flyRemote.Parent = ReplicatedStorage local inputRemote = Instance.new("RemoteEvent") -- client -> server: where I want to go inputRemote.Name = "FlyInput" inputRemote.Parent = ReplicatedStorage local modeRemote = Instance.new("RemoteEvent") -- server -> client: "server" = I'm moving you now modeRemote.Name = "FlyMode" modeRemote.Parent = ReplicatedStorage local flyers = {} -- [player] = state local function getParts(player) local character = player.Character local humanoid = character and character:FindFirstChildOfClass("Humanoid") local hrp = character and character:FindFirstChild("HumanoidRootPart") return character, humanoid, hrp end -- Cut every joint that ties the character to something that isn't the character (the bike) local function detachFromVehicle(character, hrp) local cut = 0 local root = hrp.AssemblyRootPart if not root then return 0 end for _, part in ipairs(root:GetConnectedParts(true)) do for _, joint in ipairs(part:GetJoints()) do local a, b if joint:IsA("JointInstance") or joint:IsA("WeldConstraint") then a, b = joint.Part0, joint.Part1 elseif joint:IsA("Constraint") then a = joint.Attachment0 and joint.Attachment0.Parent b = joint.Attachment1 and joint.Attachment1.Parent end if a and b and (a:IsDescendantOf(character) ~= b:IsDescendantOf(character)) then joint:Destroy() cut += 1 end end end return cut end -- Prints WHERE you got sent and which scripts could have done it (so the culprit can be found) local SUSPECT_WORDS = { "teleport", "anticheat", "anti", "cheat", "distance", "range", "bound", "limit", "return", "respawn", "reset", "zone", "bike", "seat", "kick", "exploit", "fly", "safe", "boundary" } local function reportYank(player, f, dest, dist) warn(string.format("[Fly/server] something teleported %s %.0f studs while flying, undoing it (x%d).", player.Name, dist, f.undos)) pcall(function() local params = OverlapParams.new() params.FilterType = Enum.RaycastFilterType.Exclude params.FilterDescendantsInstances = { player.Character } local near = {} for _, part in ipairs(workspace:GetPartBoundsInRadius(dest, 25, params)) do table.insert(near, { name = part:GetFullName(), d = (part.Position - dest).Magnitude }) end table.sort(near, function(a, b) return a.d < b.d end) if #near > 0 then print("[Fly/server] you were sent to " .. tostring(dest) .. ". Closest things there:") for i = 1, math.min(4, #near) do print(string.format("[Fly/server] %s (%.0f studs)", near[i].name, near[i].d)) end else print("[Fly/server] you were sent to " .. tostring(dest) .. " (nothing nearby)") end end) if not f.listed then f.listed = true local found = 0 print("[Fly/server] scripts in your game that could be doing it (check these; or put their names in PAUSE_SCRIPTS_WHILE_FLYING):") for _, inst in ipairs(game:GetDescendants()) do if (inst:IsA("Script") or inst:IsA("LocalScript") or inst:IsA("ModuleScript")) and inst ~= script then local n = string.lower(inst.Name) for _, word in ipairs(SUSPECT_WORDS) do if string.find(n, word, 1, true) then print("[Fly/server] " .. inst:GetFullName()) found += 1 break end end if found >= 15 then break end end end if found == 0 then print("[Fly/server] (none with a telling name. Look at your anti-cheat / bike / respawn scripts)") end end end -- If you moved somewhere your own flying can't have taken you, put you straight back. -- Runs twice per frame so the undo usually lands BEFORE the teleport is ever sent to your screen. local function checkYank(player, f, root) local now = os.clock() local pos = root.Position local delta = pos - f.lastPos local dist = delta.Magnitude local elapsed = now - (f.lastT or now) f.lastT = now local yanked = false if f.mode == "server" then -- The server is the one moving you, so it knows exactly how far you could have gone. -- Anything beyond that (even a few studs) is something else moving you: undo it. local sp = f.vel and f.vel.Magnitude or 0 local allowed = sp * math.max(elapsed, 1 / 30) * 1.5 + SERVER_YANK_SLACK yanked = dist > allowed elseif dist > YANK_MIN_STUDS then local along = -1 local v = root.AssemblyLinearVelocity if v.Magnitude > 1 then along = delta.Unit:Dot(v.Unit) -- real flying moves you along your velocity end yanked = along < 0.3 end if yanked then f.undos += 1 if f.undos <= 3 then reportYank(player, f, pos, dist) end local keepVel = (f.mode == "server" and f.vel) or root.AssemblyLinearVelocity root.CFrame = f.lastCF root.AssemblyLinearVelocity = keepVel root.AssemblyAngularVelocity = Vector3.zero pos = root.Position modeRemote:FireClient(player, "yank", dist) end f.lastPos = pos f.lastCF = root.CFrame end local AUTO_PAUSE_WORDS = { "anticheat", "anti_cheat", "anti-cheat", "cheat", "exploit", "return", "respawn", "teleport", "leash", "tether", "boundary", "outofbounds", "kick", "distance", "safezone" } local function isAutoSuspect(inst) if not AUTO_PAUSE_SUSPECTS then return false end if inst.Name == script.Name then return false end -- never switch off this script's own copies local n = string.lower(inst.Name) for _, word in ipairs(AUTO_PAUSE_WORDS) do if string.find(n, word, 1, true) then return true end end return false end local function pauseScripts(f) f.paused = {} local names = {} for _, n in ipairs(PAUSE_SCRIPTS_WHILE_FLYING) do names[n] = true end for _, inst in ipairs(game:GetDescendants()) do if inst:IsA("Script") and inst ~= script and (names[inst.Name] or isAutoSuspect(inst)) then pcall(function() if pausedCount[inst] == nil then if not inst.Enabled then return end pausedWas[inst] = true pausedCount[inst] = 0 inst.Enabled = false print("[Fly/server] paused script while flying:", inst:GetFullName()) end pausedCount[inst] += 1 f.paused[inst] = true end) end end end local function resumeScripts(f) for inst in pairs(f.paused or {}) do pcall(function() pausedCount[inst] -= 1 if pausedCount[inst] <= 0 then pausedCount[inst] = nil pausedWas[inst] = nil if inst.Parent then inst.Enabled = true end end end) end f.paused = {} end -- While you fly, your character can't set off Touched events: no seat grabbing you, and no -- kill-brick / out-of-bounds / "return" zones that teleport you by touch. local function shieldCharacter(character, f) f.touch = {} local function shield(part) if part:IsA("BasePart") and f.touch[part] == nil then f.touch[part] = part.CanTouch part.CanTouch = false end end for _, d in ipairs(character:GetDescendants()) do shield(d) end f.touchConn = character.DescendantAdded:Connect(shield) end local function unshieldCharacter(f) if f.touchConn then f.touchConn:Disconnect() end for part, was in pairs(f.touch or {}) do if part.Parent then part.CanTouch = was end end f.touch = {} end local function leaveSeat(player, f) local character, humanoid = getParts(player) local seat = humanoid and humanoid.SeatPart if not seat then return end if f.seats[seat] == nil then f.seats[seat] = seat.Disabled end seat.Disabled = true -- the seat can't grab you again while you fly humanoid.Sit = false local weld = seat:FindFirstChild("SeatWeld") if weld then weld:Destroy() end if PRINT_DEBUG then print("[Fly/server] took", player.Name, "off", seat:GetFullName()) end end -- Make the player the physics owner of their character. Returns true only if it really stuck. local function takeOwnership(hrp, player) local root = hrp.AssemblyRootPart or hrp pcall(function() root:SetNetworkOwner(player) end) local owner = nil pcall(function() owner = root:GetNetworkOwner() end) return owner == player end local function dropMovers(f) if f.lv then f.lv:Destroy() f.lv = nil end if f.ao then f.ao:Destroy() f.ao = nil end if f.att then f.att:Destroy() f.att = nil end end -- Server-driven flight: the server owns the character and moves it itself local function enterServerMode(player, f, reportedPos) local character, humanoid, hrp = getParts(player) if not hrp or not humanoid or f.mode == "server" then return end f.mode = "server" -- carry on from where the player actually is on their screen, so nothing snaps back if reportedPos and (reportedPos - hrp.Position).Magnitude < 3000 then hrp.CFrame = CFrame.new(reportedPos) * (hrp.CFrame - hrp.CFrame.Position) end local look = hrp.CFrame.LookVector local flat = Vector3.new(look.X, 0, look.Z) f.facing = flat.Magnitude > 0.05 and flat.Unit or Vector3.new(0, 0, -1) f.vel = Vector3.zero f.lean = 0 f.autoRotate = humanoid.AutoRotate humanoid.AutoRotate = false local att = Instance.new("Attachment") att.Name = "FlyAttachment" att.Parent = hrp local lv = Instance.new("LinearVelocity") lv.Name = "FlyVelocity" lv.Attachment0 = att lv.RelativeTo = Enum.ActuatorRelativeTo.World lv.VelocityConstraintMode = Enum.VelocityConstraintMode.Vector lv.MaxForce = math.huge lv.VectorVelocity = Vector3.zero lv.Parent = hrp local ao = Instance.new("AlignOrientation") ao.Name = "FlyOrientation" ao.Mode = Enum.OrientationAlignmentMode.OneAttachment ao.Attachment0 = att ao.RigidityEnabled = false ao.MaxTorque = math.huge ao.MaxAngularVelocity = math.huge ao.Responsiveness = 30 ao.CFrame = CFrame.lookAt(Vector3.zero, f.facing) ao.Parent = hrp f.att, f.lv, f.ao = att, lv, ao -- the server (not a client) simulates the character from now on local root = hrp.AssemblyRootPart or hrp pcall(function() root:SetNetworkOwner(nil) end) f.lastPos = hrp.Position f.lastCF = hrp.CFrame modeRemote:FireClient(player, "server") if PRINT_DEBUG then print("[Fly/server] server-driven flight for", player.Name) end end local function stepServerMode(f, humanoid, dt) local inp = f.input local dir = Vector3.zero local speed, maxSpeed = 0, 1 local facing = f.facing if inp and os.clock() - f.inputAt < 0.5 then dir, speed, maxSpeed, facing = inp.dir, inp.speed, inp.maxSpeed, inp.facing end f.facing = facing local rate = (dir.Magnitude > 0.05) and 7 or 10 -- same feel as the client: speed up / brake f.vel = f.vel:Lerp(dir * speed, 1 - math.exp(-rate * dt)) f.lv.VectorVelocity = f.vel local leanTarget = math.clamp(f.vel:Dot(facing) / math.max(maxSpeed, 1), -0.3, 1) * FLY_LEAN f.lean += (leanTarget - f.lean) * (1 - math.exp(-8 * dt)) f.ao.CFrame = CFrame.lookAt(Vector3.zero, facing) * CFrame.Angles(-f.lean, 0, 0) if humanoid:GetState() ~= Enum.HumanoidStateType.Freefall then humanoid:ChangeState(Enum.HumanoidStateType.Freefall) end end -- Anything that PUSHES or HOLDS your character (leftover movers/forces) makes the fall slow or pulls -- you sideways. After you let go of fly these are removed so only normal gravity is left. local STRAY_MOVER_CLASSES = { "BodyMover", "RocketPropulsion", "LinearVelocity", "AngularVelocity", "VectorForce", "LineForce", "Torque", "AlignPosition", "AlignOrientation", "PlaneConstraint" } local function removeStrayMovers(character) if not character then return end for _, inst in ipairs(character:GetDescendants()) do for _, class in ipairs(STRAY_MOVER_CLASSES) do if inst:IsA(class) then print("[Fly] removed leftover mover after flying:", inst:GetFullName()) pcall(function() inst:Destroy() end) break end end end end -- ===== Teleport guard (keeps protecting you AFTER you stop flying) ===== local guards = {} -- [player] = { seats, lastPos, lastCF, lastT, lastVel, undos, t0, ... } local function endGuard(player, restoreSeats) local g = guards[player] if not g then return end guards[player] = nil if g.hb then g.hb:Disconnect() end if g.cf then g.cf:Disconnect() end if g.cc then g.cc:Disconnect() end if restoreSeats then for seat, wasDisabled in pairs(g.seats) do if seat.Parent then seat.Disabled = wasDisabled end end end if PRINT_DEBUG then print("[Fly/server] teleport guard ended for", player.Name) end end local function guardCheck(player, g) local character, humanoid, root = getParts(player) if not root or not humanoid then return end local now = os.clock() if GUARD_SECONDS > 0 and now - g.t0 > GUARD_SECONDS then endGuard(player, true) return end -- seats you flew away from stay off until you walk back up to them for seat, wasDisabled in pairs(g.seats) do if seat.Parent then if (seat.Position - root.Position).Magnitude > GUARD_SEAT_RANGE then seat.Disabled = true else seat.Disabled = wasDisabled end end end local pos = root.Position local seat = humanoid.SeatPart if seat then if (seat.Position - g.lastPos).Magnitude > GUARD_SEAT_RANGE then -- something sat you on a seat you were far from: that's the snap-back, undo it g.undos += 1 if g.undos <= 3 then reportYank(player, g, pos, (pos - g.lastPos).Magnitude) end seat.Disabled = true humanoid.Sit = false local weld = seat:FindFirstChild("SeatWeld") if weld then weld:Destroy() end root.CFrame = g.lastCF pos = root.Position modeRemote:FireClient(player, "yank", 0) else -- you walked up and sat down yourself: done endGuard(player, true) return end else local elapsed = math.max(now - g.lastT, 1 / 60) local dist = (pos - g.lastPos).Magnitude local speed = math.max(g.lastVel.Magnitude, root.AssemblyLinearVelocity.Magnitude) local allowed = (speed + humanoid.WalkSpeed) * elapsed * 2 + GUARD_MIN_STUDS if dist > allowed then g.undos += 1 if g.undos <= 3 then reportYank(player, g, pos, dist) end root.CFrame = g.lastCF root.AssemblyLinearVelocity = g.lastVel pos = root.Position modeRemote:FireClient(player, "yank", dist) end end g.lastPos = pos g.lastCF = root.CFrame g.lastVel = root.AssemblyLinearVelocity g.lastT = now end local function startGuard(player, seats) local _, humanoid, hrp = getParts(player) if not hrp or not humanoid then -- no character (respawning): nothing to protect, just give the seats back for seat, wasDisabled in pairs(seats) do if seat.Parent then seat.Disabled = wasDisabled end end return end local old = guards[player] if old then for seat, was in pairs(old.seats) do if seats[seat] == nil then seats[seat] = was end end endGuard(player, false) end local g = { seats = seats, lastPos = hrp.Position, lastCF = hrp.CFrame, lastT = os.clock(), lastVel = hrp.AssemblyLinearVelocity, undos = 0, t0 = os.clock(), } guards[player] = g g.hb = RunService.Heartbeat:Connect(function() if guards[player] == g then guardCheck(player, g) end end) g.cf = hrp:GetPropertyChangedSignal("CFrame"):Connect(function() if guards[player] == g then guardCheck(player, g) end end) g.cc = player.CharacterRemoving:Connect(function() endGuard(player, true) end) if PRINT_DEBUG then print("[Fly/server] teleport guard on for", player.Name) end end local function stopFly(player) local f = flyers[player] if not f then return end flyers[player] = nil if f.conn then f.conn:Disconnect() end if f.conn2 then f.conn2:Disconnect() end if f.cfConn then f.cfConn:Disconnect() end if f.charConn then f.charConn:Disconnect() end resumeScripts(f) unshieldCharacter(f) local _, humanoid, hrp = getParts(player) if f.mode == "server" and hrp then -- stand you back up where you are and let you drift down gently local look = hrp.CFrame.LookVector local flat = Vector3.new(look.X, 0, look.Z) if flat.Magnitude > 0.05 then hrp.CFrame = CFrame.lookAt(hrp.Position, hrp.Position + flat.Unit) end do local v = f.vel or Vector3.zero; hrp.AssemblyLinearVelocity = Vector3.new(v.X * 0.25, math.min(v.Y * 0.25, 0), v.Z * 0.25) end hrp.AssemblyAngularVelocity = Vector3.zero end dropMovers(f) removeStrayMovers(player.Character) if humanoid and f.autoRotate ~= nil then humanoid.AutoRotate = f.autoRotate end if hrp then local root = hrp.AssemblyRootPart or hrp pcall(function() root:SetNetworkOwnershipAuto() end) -- back to the player end player:SetAttribute("Flying", nil) local character = player.Character if character then character:SetAttribute("Flying", nil) end -- hand the seats and the teleport protection to the guard (seats stay off while you're far from them) startGuard(player, f.seats) end flyRemote.OnServerInvoke = function(player, on) if not isAllowed(player) then warn("[Fly/server] " .. player.Name .. " (" .. player.UserId .. ") isn't allowed. Add the UserId to ALLOWED_USER_IDS at the top of the server half.") return false end if on ~= true then stopFly(player) return true end stopFly(player) local character, humanoid, hrp = getParts(player) if not humanoid or not hrp or humanoid.Health <= 0 then return false end local look = hrp.CFrame.LookVector local flat = Vector3.new(look.X, 0, look.Z) local f = { seats = {}, mode = "client", bad = 0, undos = 0, lastDetach = 0, vel = Vector3.zero, lean = 0, inputAt = 0, facing = flat.Magnitude > 0.05 and flat.Unit or Vector3.new(0, 0, -1), } flyers[player] = f local oldGuard = guards[player] if oldGuard then for seat, was in pairs(oldGuard.seats) do f.seats[seat] = was end endGuard(player, false) end -- get off the seat and cut anything else holding you to the bike shieldCharacter(character, f) leaveSeat(player, f) local cut = detachFromVehicle(character, hrp) if PRINT_DEBUG and cut > 0 then print("[Fly/server] cut", cut, "joint(s) tying", player.Name, "to the bike") end local owned = takeOwnership(hrp, player) player:SetAttribute("Flying", true) character:SetAttribute("Flying", true) f.lastPos = hrp.Position f.lastCF = hrp.CFrame f.lastT = os.clock() -- the moment anything sets your position far away, put it back (before it can be drawn) f.cfConn = hrp:GetPropertyChangedSignal("CFrame"):Connect(function() if flyers[player] == f then checkYank(player, f, hrp) end end) f.conn = RunService.Heartbeat:Connect(function(dt) local char, hum, root = getParts(player) if not char or not hum or not root then return end leaveSeat(player, f) for _, j in ipairs(root:GetJoints()) do local a, b if j:IsA("JointInstance") or j:IsA("WeldConstraint") then a, b = j.Part0, j.Part1 elseif j:IsA("Constraint") then a = j.Attachment0 and j.Attachment0.Parent b = j.Attachment1 and j.Attachment1.Parent end if a and b and (a:IsDescendantOf(char) ~= b:IsDescendantOf(char)) then pcall(function() j:Destroy() end) end end local now = os.clock() if now - f.lastDetach > 0.25 then f.lastDetach = now detachFromVehicle(char, root) end if f.mode == "server" and f.lv then stepServerMode(f, hum, dt) end checkYank(player, f, root) end) f.conn2 = RunService.Stepped:Connect(function() local _, _, root = getParts(player) if root then checkYank(player, f, root) end end) f.charConn = player.CharacterRemoving:Connect(function() stopFly(player) end) pauseScripts(f) if FORCE_SERVER_FLY or not owned then if not owned and not FORCE_SERVER_FLY then warn("[Fly/server] couldn't give " .. player.Name .. " ownership of their character, so the server will drive the flight.") end enterServerMode(player, f, nil) end return f.mode end -- The client tells us where it wants to go (server mode) and where it is (to check it's getting through) inputRemote.OnServerEvent:Connect(function(player, dir, speed, maxSpeed, facing, pos) local f = flyers[player] if not f then return end if typeof(dir) ~= "Vector3" or typeof(facing) ~= "Vector3" or typeof(pos) ~= "Vector3" or type(speed) ~= "number" or type(maxSpeed) ~= "number" then return end if dir ~= dir or facing ~= facing or pos ~= pos or speed ~= speed or maxSpeed ~= maxSpeed then return end -- NaN if dir.Magnitude > 1 then dir = dir.Unit end local flat = Vector3.new(facing.X, 0, facing.Z) flat = flat.Magnitude > 0.05 and flat.Unit or f.facing speed = math.clamp(speed, 0, FLY_MAX_SPEED) maxSpeed = math.clamp(maxSpeed, 1, FLY_MAX_SPEED) f.input = { dir = dir, speed = speed, maxSpeed = maxSpeed, facing = flat } f.inputAt = os.clock() if f.mode == "client" then -- is the server seeing the same movement the player sees? local _, _, hrp = getParts(player) if hrp then if (pos - hrp.Position).Magnitude > 80 + 1.0 * speed then f.bad += 1 if f.bad >= 8 then warn("[Fly/server] " .. player.Name .. "'s movement isn't reaching the server, switching to server-driven flight.") enterServerMode(player, f, pos) end else f.bad = 0 end end end end) Players.PlayerRemoving:Connect(function(player) stopFly(player) endGuard(player, false) end) end return end ------------------------------------------------------------------ -- CLIENT HALF (the GUI) ------------------------------------------------------------------ local Players = game:GetService("Players") local RunService = game:GetService("RunService") local UserInputService = game:GetService("UserInputService") local player = Players.LocalPlayer local playerGui = player:WaitForChild("PlayerGui") -- ===== Settings you can tweak ===== local DEFAULT_KEY = Enum.KeyCode.Q local TARGET_PITCH_DEG = 40 -- starting wheelie angle (the GUI slider changes it live) local MIN_PITCH_DEG = 10 -- lowest angle the slider allows local MAX_PITCH_DEG = 75 -- highest angle the slider allows (past ~80 the bike can flip over backwards) local DEFAULT_SMOOTHNESS = 60 -- starting value of the Smoothness slider (0 = silky slow, 100 = instant) local SLEW_SLOWEST_DEG = 20 -- degrees/sec the wheelie rises at Smoothness 0 local SLEW_FASTEST_DEG = 500 -- degrees/sec the wheelie rises at Smoothness 100 -- Stoppie (rear wheel up). Uses the same controller as the wheelie but pitches the nose DOWN. local DEFAULT_STOPPIE_KEY = Enum.KeyCode.E local STOPPIE_PITCH_DEG = 22 -- starting stoppie angle (the Stoppie tab slider changes it live) local MIN_STOPPIE_DEG = 8 -- lowest angle the stoppie slider allows local MAX_STOPPIE_DEG = 45 -- highest angle the stoppie slider allows (real stoppies rarely go past ~40) local DEFAULT_STOPPIE_SMOOTHNESS = 45 -- stoppies look best a little slower than wheelies (weight transfer) local STOPPIE_NEEDS_BRAKE = true -- true = the rear only lifts while you brake (like a real stoppie) local STOPPIE_MIN_SPEED = 6 -- studs/sec: below this the stoppie has settled flat local STOPPIE_FULL_SPEED = 35 -- studs/sec: at/above this the stoppie reaches its full angle local STOPPIE_SETTLE_TIME = 0.4 -- seconds it keeps easing the bike level after the stoppie ends local GAIN = 6 -- how firmly it pulls toward the angle (hold strength; also rises a bit at the snappy end of Smoothness) local MAX_RATE = 6 -- max rotation speed in radians/sec local RESPONSE = 30 -- how hard it takes over the bike's rotation local ROLL_GAIN = 12 -- how hard it fights the bike falling sideways local STEER_ROLL_FACTOR = 0.25 -- side-balance strength while you're steering (lower = freer turning) local AUTO_DETECT_FRONT = true -- learn the bike's front from the direction it drives local DETECT_SPEED = 12 -- studs/sec needed to learn the front local FRONT_IS_REVERSED = false -- only used when AUTO_DETECT_FRONT is false local REQUIRE_THROTTLE = false -- true = only wheelie while holding forward local MIN_SPEED = 0 -- studs/sec needed before the wheelie starts -- Fly (Extras tab). Moves only your character, never the bike. local DEFAULT_FLY_KEY = Enum.KeyCode.F local FLY_SPEED_DEFAULT = 70 -- starting fly speed in studs/sec (the Extras slider changes it live) local FLY_SPEED_MIN = 10 local FLY_SPEED_MAX = 250 local FLY_BOOST_MULT = 2 -- holding Shift multiplies your speed by this local FLY_ACCEL = 7 -- how quickly you speed up (higher = snappier) local FLY_BRAKE = 10 -- how quickly you slow down when you let go (higher = stops faster) local FLY_LEAN_DEG = 30 -- how far you lean into the direction you fly (0 = stay upright) local POST_FLY_GUARD_SECONDS = 600 -- after flying, undo teleports on your screen for this long (ends when you sit on a seat; 0 = off) local PAUSE_LOCAL_SCRIPTS_WHILE_FLYING = {} -- exact names of LocalScripts that move/teleport you, switched off while you fly local AUTO_PAUSE_LOCAL_SUSPECTS = false -- also switch off LocalScripts whose NAME looks like a "teleport me back" script while you fly local FUEL_SCAN_INTERVAL = 1 -- seconds between searches for fuel values local EXTRA_FUEL_NAMES = {} -- exact name(s) of your fuel value, e.g. { "Juice" } (also set it in the server half above) local FUEL_FILL_TO = nil -- (also set it in the server half above) only used if no Max/Capacity value is found next to your fuel value. -- Set to your tank size (e.g. 100) to fill to that instead of the level you had. -- ================================== local toggleKey = DEFAULT_KEY local stoppieKey = DEFAULT_STOPPIE_KEY local enabled = false -- wheelie on/off local stoppieEnabled = false -- stoppie on/off (never on together with the wheelie) local rebinding = nil -- nil, "wheelie" or "stoppie": which key is waiting for a new key local infFuel = false local fuelFound = 0 local fuelResetRequested = false local targetAngleDeg = math.clamp(TARGET_PITCH_DEG, MIN_PITCH_DEG, MAX_PITCH_DEG) -- current wheelie angle (set by the slider) local smoothness = math.clamp(DEFAULT_SMOOTHNESS, 0, 100) -- current Smoothness slider value local stoppieAngleDeg = math.clamp(STOPPIE_PITCH_DEG, MIN_STOPPIE_DEG, MAX_STOPPIE_DEG) local stoppieSmoothness = math.clamp(DEFAULT_STOPPIE_SMOOTHNESS, 0, 100) local brakeToStoppie = STOPPIE_NEEDS_BRAKE local resetFrontRequested = false -- set by the Reset Front button, handled in the physics step local flyKey = DEFAULT_FLY_KEY local flying = false -- fly on/off (never on together with the wheelie or stoppie) local flySpeed = math.clamp(FLY_SPEED_DEFAULT, FLY_SPEED_MIN, FLY_SPEED_MAX) local setFly -- defined further down in the Fly section -- Optional: the server script (InfiniteFuelServer) creates this so the fuel is -- kept full on the SERVER too, which is what makes it stick if the server owns fuel. local ReplicatedStorage = game:GetService("ReplicatedStorage") local fuelRemote = nil task.spawn(function() fuelRemote = ReplicatedStorage:WaitForChild("InfiniteFuelToggle", 10) if fuelRemote and infFuel then fuelRemote:FireServer(true) end end) ------------------------------------------------------------------ -- GUI ------------------------------------------------------------------ local TweenService = game:GetService("TweenService") local THEME = { panel = Color3.fromRGB(16, 16, 22), header = Color3.fromRGB(23, 23, 31), row = Color3.fromRGB(26, 26, 36), rowHover = Color3.fromRGB(34, 34, 46), chip = Color3.fromRGB(42, 42, 57), trackOff = Color3.fromRGB(60, 60, 78), text = Color3.fromRGB(240, 240, 246), subtext = Color3.fromRGB(135, 135, 155), accent = Color3.fromRGB(255, 106, 43), accent2 = Color3.fromRGB(255, 176, 60), good = Color3.fromRGB(72, 200, 120), warn = Color3.fromRGB(240, 192, 64), idle = Color3.fromRGB(105, 105, 125), dark = Color3.fromRGB(20, 20, 24), } local PANEL_W, HEADER_H = 270, 46 local TAB_H, PAGE_H, STATUS_H = 34, 246, 38 local BODY_H = 20 + TAB_H + PAGE_H + STATUS_H + 16 -- body padding + tab bar + page + status pill + two gaps local TWEEN_INFO = TweenInfo.new(0.18, Enum.EasingStyle.Quad, Enum.EasingDirection.Out) local function tween(inst, props, info) TweenService:Create(inst, info or TWEEN_INFO, props):Play() end local function addCorner(inst, radius) local c = Instance.new("UICorner") c.CornerRadius = UDim.new(0, radius) c.Parent = inst return c end local function addStroke(inst, transparency) local s = Instance.new("UIStroke") s.Color = Color3.new(1, 1, 1) s.Transparency = transparency s.Thickness = 1 s.ApplyStrokeMode = Enum.ApplyStrokeMode.Border s.Parent = inst return s end local function makeLabel(parent, props) local l = Instance.new("TextLabel") l.BackgroundTransparency = 1 l.BorderSizePixel = 0 l.TextXAlignment = Enum.TextXAlignment.Left l.TextColor3 = THEME.text for k, v in pairs(props) do l[k] = v end l.Parent = parent return l end local gui = Instance.new("ScreenGui") gui.Name = "AutoWheelieGui" gui.ResetOnSpawn = false gui.Parent = playerGui local frame = Instance.new("Frame") frame.Name = "Panel" frame.Size = UDim2.fromOffset(PANEL_W, HEADER_H + BODY_H) frame.Position = UDim2.new(1, -(PANEL_W + 20), 1, -(HEADER_H + BODY_H + 20)) -- bottom-right corner frame.BackgroundColor3 = THEME.panel frame.BorderSizePixel = 0 frame.ClipsDescendants = true frame.Parent = gui addCorner(frame, 14) addStroke(frame, 0.88) -- Title bar (also the drag handle) local title = Instance.new("Frame") title.Name = "TitleBar" title.Size = UDim2.new(1, 0, 0, HEADER_H) title.BackgroundColor3 = THEME.header title.BorderSizePixel = 0 title.Active = true -- lets it catch mouse/touch input title.Parent = frame local titleDot = Instance.new("Frame") titleDot.Size = UDim2.fromOffset(10, 10) titleDot.Position = UDim2.new(0, 16, 0.5, -5) titleDot.BackgroundColor3 = THEME.accent titleDot.BorderSizePixel = 0 titleDot.Parent = title addCorner(titleDot, 5) makeLabel(title, { Name = "Title", Size = UDim2.new(1, -90, 1, 0), Position = UDim2.fromOffset(34, 0), Font = Enum.Font.GothamBold, TextSize = 15, Text = "Bike Tricks", }) local minButton = Instance.new("TextButton") minButton.Name = "Minimize" minButton.Size = UDim2.fromOffset(28, 28) minButton.Position = UDim2.new(1, -40, 0.5, -14) minButton.BackgroundColor3 = THEME.chip minButton.BorderSizePixel = 0 minButton.AutoButtonColor = false minButton.Font = Enum.Font.GothamBold minButton.TextSize = 16 minButton.TextColor3 = THEME.text minButton.Text = "–" minButton.Parent = title addCorner(minButton, 8) -- thin accent line under the title bar local accentLine = Instance.new("Frame") accentLine.Size = UDim2.new(1, 0, 0, 2) accentLine.Position = UDim2.new(0, 0, 1, -2) accentLine.BackgroundColor3 = Color3.new(1, 1, 1) accentLine.BorderSizePixel = 0 accentLine.Parent = title local accentGradient = Instance.new("UIGradient") accentGradient.Color = ColorSequence.new(THEME.accent, THEME.accent2) accentGradient.Transparency = NumberSequence.new({ NumberSequenceKeypoint.new(0, 0), NumberSequenceKeypoint.new(1, 0.7), }) accentGradient.Parent = accentLine -- Body: [tab bar] [page] [status pill] local body = Instance.new("Frame") body.Name = "Body" body.Position = UDim2.fromOffset(0, HEADER_H) body.Size = UDim2.new(1, 0, 0, BODY_H) body.BackgroundTransparency = 1 body.BorderSizePixel = 0 body.Parent = frame local padding = Instance.new("UIPadding") padding.PaddingTop = UDim.new(0, 10) padding.PaddingBottom = UDim.new(0, 10) padding.PaddingLeft = UDim.new(0, 10) padding.PaddingRight = UDim.new(0, 10) padding.Parent = body local layout = Instance.new("UIListLayout") layout.Padding = UDim.new(0, 8) layout.SortOrder = Enum.SortOrder.LayoutOrder layout.Parent = body ------------------------------------------------------------------ -- Tabs ------------------------------------------------------------------ local TAB_NAMES = { "Wheelie", "Stoppie", "Extras" } local tabBar = Instance.new("Frame") tabBar.Name = "Tabs" tabBar.LayoutOrder = 1 tabBar.Size = UDim2.new(1, 0, 0, TAB_H) tabBar.BackgroundColor3 = THEME.row tabBar.BorderSizePixel = 0 tabBar.Parent = body addCorner(tabBar, 10) addStroke(tabBar, 0.94) -- the highlighted pill that slides under the active tab local tabIndicator = Instance.new("Frame") tabIndicator.Size = UDim2.new(1 / #TAB_NAMES, -6, 1, -6) tabIndicator.Position = UDim2.new(0, 3, 0, 3) tabIndicator.BackgroundColor3 = Color3.new(1, 1, 1) tabIndicator.BorderSizePixel = 0 tabIndicator.Parent = tabBar addCorner(tabIndicator, 8) local tabGradient = Instance.new("UIGradient") tabGradient.Color = ColorSequence.new(THEME.accent, THEME.accent2) tabGradient.Parent = tabIndicator local pagesHolder = Instance.new("Frame") pagesHolder.Name = "Pages" pagesHolder.LayoutOrder = 2 pagesHolder.Size = UDim2.new(1, 0, 0, PAGE_H) pagesHolder.BackgroundTransparency = 1 pagesHolder.BorderSizePixel = 0 pagesHolder.Parent = body local pages, tabButtons = {}, {} for i, name in ipairs(TAB_NAMES) do local page if name == "Extras" then -- Extras has the most rows, so it scrolls page = Instance.new("ScrollingFrame") page.AutomaticCanvasSize = Enum.AutomaticSize.Y page.CanvasSize = UDim2.new(0, 0, 0, 0) page.ScrollBarThickness = 3 page.ScrollBarImageColor3 = THEME.chip page.ScrollingDirection = Enum.ScrollingDirection.Y page.ElasticBehavior = Enum.ElasticBehavior.Never local scrollPad = Instance.new("UIPadding") scrollPad.PaddingRight = UDim.new(0, 6) scrollPad.Parent = page else page = Instance.new("Frame") end page.Name = name .. "Page" page.Size = UDim2.fromScale(1, 1) page.BackgroundTransparency = 1 page.BorderSizePixel = 0 page.Visible = false page.Parent = pagesHolder local pageLayout = Instance.new("UIListLayout") pageLayout.Padding = UDim.new(0, 8) pageLayout.SortOrder = Enum.SortOrder.LayoutOrder pageLayout.Parent = page pages[i] = page local button = Instance.new("TextButton") button.Name = name .. "Tab" button.Size = UDim2.new(1 / #TAB_NAMES, 0, 1, 0) button.Position = UDim2.new((i - 1) / #TAB_NAMES, 0, 0, 0) button.BackgroundTransparency = 1 button.AutoButtonColor = false button.Font = Enum.Font.GothamBold button.TextSize = 13 button.TextColor3 = THEME.subtext button.Text = name button.ZIndex = 2 button.Parent = tabBar tabButtons[i] = button end local pageWheelie, pageStoppie, pageExtras = pages[1], pages[2], pages[3] local function showTab(index, instant) for i, page in ipairs(pages) do page.Visible = (i == index) end for i, button in ipairs(tabButtons) do tween(button, { TextColor3 = (i == index) and THEME.dark or THEME.subtext }) end local pos = UDim2.new((index - 1) / #TAB_NAMES, 3, 0, 3) if instant then tabIndicator.Position = pos else tween(tabIndicator, { Position = pos }, TweenInfo.new(0.2, Enum.EasingStyle.Quad, Enum.EasingDirection.Out)) end end for i, button in ipairs(tabButtons) do button.Activated:Connect(function() showTab(i) end) end ------------------------------------------------------------------ -- Row builders (all take the page they should be placed on) ------------------------------------------------------------------ local function makeRowFrame(className, order, parent) local row = Instance.new(className) row.LayoutOrder = order row.Size = UDim2.new(1, 0, 0, 50) row.BackgroundColor3 = THEME.row row.BorderSizePixel = 0 if className == "TextButton" then row.AutoButtonColor = false row.Text = "" end row.Parent = parent addCorner(row, 10) addStroke(row, 0.94) return row end local function makeRowText(row, name, sub) local nameLabel = makeLabel(row, { Position = UDim2.fromOffset(14, 9), Size = UDim2.new(1, -120, 0, 17), Font = Enum.Font.GothamBold, TextSize = 14, Text = name, }) local subLabel = makeLabel(row, { Position = UDim2.fromOffset(14, 27), Size = UDim2.new(1, -120, 0, 14), Font = Enum.Font.Gotham, TextSize = 12, TextColor3 = THEME.subtext, TextTruncate = Enum.TextTruncate.AtEnd, Text = sub, }) return nameLabel, subLabel end -- A row with an on/off switch on the right local function makeToggleRow(order, name, sub, parent) local row = makeRowFrame("TextButton", order, parent) local _, subLabel = makeRowText(row, name, sub) local track = Instance.new("Frame") track.Size = UDim2.fromOffset(40, 22) track.Position = UDim2.new(1, -54, 0.5, -11) track.BackgroundColor3 = THEME.trackOff track.BorderSizePixel = 0 track.Parent = row addCorner(track, 11) local knob = Instance.new("Frame") knob.Size = UDim2.fromOffset(16, 16) knob.Position = UDim2.new(0, 3, 0.5, -8) knob.BackgroundColor3 = Color3.new(1, 1, 1) knob.BorderSizePixel = 0 knob.Parent = track addCorner(knob, 8) row.MouseEnter:Connect(function() tween(row, { BackgroundColor3 = THEME.rowHover }) end) row.MouseLeave:Connect(function() tween(row, { BackgroundColor3 = THEME.row }) end) return { row = row, sub = subLabel, track = track, knob = knob } end local function setSwitch(r, on, color) tween(r.track, { BackgroundColor3 = on and color or THEME.trackOff }) tween(r.knob, { Position = on and UDim2.new(0, 21, 0.5, -8) or UDim2.new(0, 3, 0.5, -8) }) end -- A small button on the right side of a row (key chips, Reset) local function makeChip(parent, text) local chip = Instance.new("TextButton") chip.Size = UDim2.fromOffset(96, 30) chip.Position = UDim2.new(1, -108, 0.5, -15) chip.BackgroundColor3 = THEME.chip chip.BorderSizePixel = 0 chip.AutoButtonColor = false chip.Font = Enum.Font.GothamBold chip.TextSize = 12 chip.TextColor3 = THEME.text chip.TextTruncate = Enum.TextTruncate.AtEnd chip.Text = text chip.Parent = parent addCorner(chip, 8) addStroke(chip, 0.9) return chip end -- Keybind row (key chip on the right) local function makeKeyRow(order, name, parent) local row = makeRowFrame("Frame", order, parent) local _, sub = makeRowText(row, name, "Click the key to change it") local chip = makeChip(row, "") return { row = row, sub = sub, chip = chip } end -- Reusable slider row: name + live value on top, a draggable track below. -- formatValue(v) -> text shown on the right onChange(v) -> called whenever the value changes -- leftCaption / rightCaption (optional) -> small labels under the ends of the track local function makeSlider(parent, order, name, minV, maxV, startV, formatValue, onChange, leftCaption, rightCaption) local captioned = leftCaption ~= nil local row = makeRowFrame("Frame", order, parent) row.Size = UDim2.new(1, 0, 0, captioned and 66 or 56) local trackY = captioned and 34 or 36 makeLabel(row, { Position = UDim2.fromOffset(14, 8), Size = UDim2.new(1, -100, 0, 17), Font = Enum.Font.GothamBold, TextSize = 14, Text = name, }) local valueLabel = makeLabel(row, { Position = UDim2.new(1, -94, 0, 8), Size = UDim2.fromOffset(80, 17), Font = Enum.Font.GothamBold, TextSize = 14, TextXAlignment = Enum.TextXAlignment.Right, TextColor3 = THEME.accent, Text = "", }) if captioned then makeLabel(row, { Position = UDim2.fromOffset(14, 48), Size = UDim2.fromOffset(80, 12), Font = Enum.Font.Gotham, TextSize = 11, TextColor3 = THEME.subtext, Text = leftCaption, }) makeLabel(row, { Position = UDim2.new(1, -94, 0, 48), Size = UDim2.fromOffset(80, 12), Font = Enum.Font.Gotham, TextSize = 11, TextColor3 = THEME.subtext, TextXAlignment = Enum.TextXAlignment.Right, Text = rightCaption or "", }) end local track = Instance.new("Frame") track.Position = UDim2.new(0, 14, 0, trackY) track.Size = UDim2.new(1, -28, 0, 6) track.BackgroundColor3 = THEME.trackOff track.BorderSizePixel = 0 track.Parent = row addCorner(track, 3) local fill = Instance.new("Frame") fill.Size = UDim2.new(0, 0, 1, 0) fill.BackgroundColor3 = Color3.new(1, 1, 1) fill.BorderSizePixel = 0 fill.Parent = track addCorner(fill, 3) local fillGradient = Instance.new("UIGradient") fillGradient.Color = ColorSequence.new(THEME.accent, THEME.accent2) fillGradient.Parent = fill local knob = Instance.new("Frame") knob.AnchorPoint = Vector2.new(0.5, 0.5) knob.Position = UDim2.new(0, 0, 0.5, 0) knob.Size = UDim2.fromOffset(16, 16) knob.BackgroundColor3 = Color3.new(1, 1, 1) knob.BorderSizePixel = 0 knob.ZIndex = 2 knob.Parent = track addCorner(knob, 11) -- Invisible, taller hit area so the slider is easy to grab (also on touch) local hit = Instance.new("TextButton") hit.Position = UDim2.new(0, 6, 0, trackY - 12) hit.Size = UDim2.new(1, -12, 0, 30) hit.BackgroundTransparency = 1 hit.AutoButtonColor = false hit.Text = "" hit.Parent = row local function setValue(v) v = math.clamp(math.floor(v + 0.5), minV, maxV) local alpha = (v - minV) / (maxV - minV) fill.Size = UDim2.new(alpha, 0, 1, 0) knob.Position = UDim2.new(alpha, 0, 0.5, 0) valueLabel.Text = formatValue(v) onChange(v) end local function setFromX(x) local width = track.AbsoluteSize.X if width <= 0 then return end local alpha = math.clamp((x - track.AbsolutePosition.X) / width, 0, 1) setValue(minV + alpha * (maxV - minV)) end local dragging = false local dragInput = nil hit.InputBegan:Connect(function(input) if input.UserInputType == Enum.UserInputType.MouseButton1 or input.UserInputType == Enum.UserInputType.Touch then dragging = true dragInput = input setFromX(input.Position.X) tween(knob, { Size = UDim2.fromOffset(21, 21) }) end end) UserInputService.InputChanged:Connect(function(input) if not dragging then return end if input.UserInputType == Enum.UserInputType.MouseMovement or input == dragInput then setFromX(input.Position.X) end end) UserInputService.InputEnded:Connect(function(input) if not dragging then return end if input == dragInput or input.UserInputType == Enum.UserInputType.MouseButton1 then dragging = false dragInput = nil tween(knob, { Size = UDim2.fromOffset(16, 16) }) end end) setValue(startV) return row end local function degreesText(v) return v .. "°" end local function smoothnessWord(v) if v < 20 then return "Silky" end if v < 40 then return "Smooth" end if v < 65 then return "Balanced" end if v < 85 then return "Snappy" end return "Instant" end ------------------------------------------------------------------ -- Page 1: Wheelie ------------------------------------------------------------------ local toggleRow = makeToggleRow(1, "Auto Wheelie", "Lifts and holds the front wheel", pageWheelie) makeSlider(pageWheelie, 2, "Wheelie Angle", MIN_PITCH_DEG, MAX_PITCH_DEG, targetAngleDeg, degreesText, function(v) targetAngleDeg = v end) makeSlider(pageWheelie, 3, "Smoothness", 0, 100, smoothness, smoothnessWord, function(v) smoothness = v end, "Smooth", "Snappy") local wheelieKeyRow = makeKeyRow(4, "Wheelie Key", pageWheelie) ------------------------------------------------------------------ -- Page 2: Stoppie ------------------------------------------------------------------ local stoppieRow = makeToggleRow(1, "Auto Stoppie", "Brake to lift the rear wheel", pageStoppie) makeSlider(pageStoppie, 2, "Stoppie Angle", MIN_STOPPIE_DEG, MAX_STOPPIE_DEG, stoppieAngleDeg, degreesText, function(v) stoppieAngleDeg = v end) makeSlider(pageStoppie, 3, "Smoothness", 0, 100, stoppieSmoothness, smoothnessWord, function(v) stoppieSmoothness = v end, "Smooth", "Snappy") local stoppieKeyRow = makeKeyRow(4, "Stoppie Key", pageStoppie) ------------------------------------------------------------------ -- Page 3: Extras ------------------------------------------------------------------ local flyRow = makeToggleRow(1, "Fly", "You fly, the bike stays", pageExtras) local function flySpeedText(v) return v .. " st/s" end makeSlider(pageExtras, 2, "Fly Speed", FLY_SPEED_MIN, FLY_SPEED_MAX, flySpeed, flySpeedText, function(v) flySpeed = v end, "Slow", "Fast") local flyKeyRow = makeKeyRow(3, "Fly Key", pageExtras) local fuelRow = makeToggleRow(4, "Infinite Fuel", "Keeps the tank full", pageExtras) local brakeRow = makeToggleRow(5, "Brake to Stoppie", "Rear lifts only while braking", pageExtras) local resetRow = makeRowFrame("Frame", 6, pageExtras) makeRowText(resetRow, "Reset Front", "Re-learn which way the bike faces") local resetButton = makeChip(resetRow, "Reset") local tipRow = Instance.new("Frame") tipRow.LayoutOrder = 7 tipRow.Size = UDim2.new(1, 0, 0, 112) tipRow.BackgroundColor3 = THEME.row tipRow.BorderSizePixel = 0 tipRow.Parent = pageExtras addCorner(tipRow, 10) makeLabel(tipRow, { Position = UDim2.fromOffset(14, 8), Size = UDim2.new(1, -28, 1, -16), Font = Enum.Font.Gotham, TextSize = 11, TextWrapped = true, TextYAlignment = Enum.TextYAlignment.Top, TextColor3 = THEME.subtext, Text = "Fly moves only you: it hops you off the bike and leaves it parked. Move toward where the camera looks, Space = up, Ctrl or C = down, Shift = boost, let go to hover.\n\nWheelie, Stoppie and Fly can't run together: turning one on turns the others off. With Brake to Stoppie on, hold your brake (S / Down) to lift the rear.", }) ------------------------------------------------------------------ -- Status pill (always visible under the tabs) ------------------------------------------------------------------ local statusRow = Instance.new("Frame") statusRow.LayoutOrder = 3 statusRow.Size = UDim2.new(1, 0, 0, STATUS_H) statusRow.BackgroundColor3 = THEME.row statusRow.BorderSizePixel = 0 statusRow.Parent = body addCorner(statusRow, 10) local statusDot = Instance.new("Frame") statusDot.Size = UDim2.fromOffset(8, 8) statusDot.Position = UDim2.new(0, 14, 0.5, -4) statusDot.BackgroundColor3 = THEME.idle statusDot.BorderSizePixel = 0 statusDot.Parent = statusRow addCorner(statusDot, 4) local statusLabel = makeLabel(statusRow, { Position = UDim2.fromOffset(30, 0), Size = UDim2.new(1, -40, 1, 0), Font = Enum.Font.Gotham, TextSize = 12, TextWrapped = true, TextTruncate = Enum.TextTruncate.AtEnd, TextColor3 = THEME.subtext, Text = "Off", }) local lastStatus = "" local function setStatus(text) if text ~= lastStatus then lastStatus = text statusLabel.Text = string.upper(string.sub(text, 1, 1)) .. string.sub(text, 2) local color = THEME.warn if text == "off" then color = THEME.idle elseif string.sub(text, 1, 7) == "holding" then color = THEME.good elseif string.sub(text, 1, 5) == "ready" then color = THEME.accent2 end tween(statusDot, { BackgroundColor3 = color }) end end ------------------------------------------------------------------ -- UI state ------------------------------------------------------------------ local function refreshKeyRow(k, modeName, key) if rebinding == modeName then k.chip.Text = "Press a key..." k.sub.Text = "Esc to cancel" tween(k.chip, { BackgroundColor3 = THEME.warn, TextColor3 = THEME.dark }) else k.chip.Text = key.Name k.sub.Text = "Click the key to change it" tween(k.chip, { BackgroundColor3 = THEME.chip, TextColor3 = THEME.text }) end end local function updateUI() setSwitch(toggleRow, enabled, THEME.accent) setSwitch(stoppieRow, stoppieEnabled, THEME.accent) setSwitch(brakeRow, brakeToStoppie, THEME.accent) setSwitch(flyRow, flying, THEME.accent) flyRow.sub.Text = flying and "Flying (Shift = boost)" or "You fly, the bike stays" stoppieRow.sub.Text = brakeToStoppie and "Brake to lift the rear wheel" or "Lifts and holds the rear wheel" brakeRow.sub.Text = brakeToStoppie and "Rear lifts only while braking" or "Rear lifts without braking" if infFuel then if fuelFound > 0 then fuelRow.sub.Text = fuelFound .. (fuelFound == 1 and " fuel value found" or " fuel values found") if fuelRemote then fuelRow.sub.Text = fuelRow.sub.Text .. " + server" end setSwitch(fuelRow, true, THEME.accent) else fuelRow.sub.Text = "No fuel values found" setSwitch(fuelRow, true, THEME.warn) end else fuelRow.sub.Text = "Keeps the tank full" setSwitch(fuelRow, false, THEME.accent) end refreshKeyRow(wheelieKeyRow, "wheelie", toggleKey) refreshKeyRow(stoppieKeyRow, "stoppie", stoppieKey) refreshKeyRow(flyKeyRow, "fly", flyKey) end -- Wheelie and Stoppie share the same physics, so only one can be on at a time local function setEnabled(value) enabled = value if value then stoppieEnabled = false if flying and setFly then setFly(false) end -- the wheelie needs you on the bike end updateUI() end local function setStoppie(value) stoppieEnabled = value if value then enabled = false if flying and setFly then setFly(false) end -- the stoppie needs you on the bike end updateUI() end toggleRow.row.Activated:Connect(function() setEnabled(not enabled) end) stoppieRow.row.Activated:Connect(function() setStoppie(not stoppieEnabled) end) brakeRow.row.Activated:Connect(function() brakeToStoppie = not brakeToStoppie updateUI() end) wheelieKeyRow.chip.Activated:Connect(function() if rebinding == "wheelie" then rebinding = nil -- click again to cancel else rebinding = "wheelie" end -- click again to cancel updateUI() end) stoppieKeyRow.chip.Activated:Connect(function() if rebinding == "stoppie" then rebinding = nil -- click again to cancel else rebinding = "stoppie" end updateUI() end) resetButton.Activated:Connect(function() resetFrontRequested = true resetButton.Text = "Done" task.delay(1, function() if resetButton.Parent then resetButton.Text = "Reset" end end) end) fuelRow.row.Activated:Connect(function() infFuel = not infFuel player:SetAttribute("InfiniteFuel", infFuel or nil) -- hook for your own drain code fuelFound = 0 fuelResetRequested = true -- fuel code clears and rescans on the next frame if fuelRemote then fuelRemote:FireServer(infFuel) end updateUI() end) flyRow.row.Activated:Connect(function() setFly(not flying) end) flyKeyRow.chip.Activated:Connect(function() if rebinding == "fly" then rebinding = nil -- click again to cancel else rebinding = "fly" end updateUI() end) -- Minimize / expand local minimized = false minButton.MouseEnter:Connect(function() tween(minButton, { BackgroundColor3 = THEME.trackOff }) end) minButton.MouseLeave:Connect(function() tween(minButton, { BackgroundColor3 = THEME.chip }) end) minButton.Activated:Connect(function() minimized = not minimized minButton.Text = minimized and "+" or "–" tween(frame, { Size = UDim2.fromOffset(PANEL_W, minimized and HEADER_H or (HEADER_H + BODY_H)), }, TweenInfo.new(0.22, Enum.EasingStyle.Quad, Enum.EasingDirection.Out)) end) showTab(1, true) updateUI() ------------------------------------------------------------------ -- Dragging (click and hold the title bar) ------------------------------------------------------------------ local dragging = false local dragStart = Vector2.zero local startPos = frame.Position title.InputBegan:Connect(function(input) if input.UserInputType == Enum.UserInputType.MouseButton1 or input.UserInputType == Enum.UserInputType.Touch then dragging = true dragStart = Vector2.new(input.Position.X, input.Position.Y) startPos = frame.Position input.Changed:Connect(function() if input.UserInputState == Enum.UserInputState.End then dragging = false end end) end end) UserInputService.InputChanged:Connect(function(input) if not dragging then return end if input.UserInputType == Enum.UserInputType.MouseMovement or input.UserInputType == Enum.UserInputType.Touch then local delta = Vector2.new(input.Position.X, input.Position.Y) - dragStart frame.Position = UDim2.new( startPos.X.Scale, startPos.X.Offset + delta.X, startPos.Y.Scale, startPos.Y.Offset + delta.Y ) end end) ------------------------------------------------------------------ -- Keybind handling ------------------------------------------------------------------ UserInputService.InputBegan:Connect(function(input, gameProcessed) -- While rebinding, the next keyboard key becomes the new key for that trick if rebinding then if input.UserInputType == Enum.UserInputType.Keyboard then if input.KeyCode ~= Enum.KeyCode.Escape then local newKey = input.KeyCode -- if the new key is already used by another action, swap keys so no two actions share one local keys = { wheelie = toggleKey, stoppie = stoppieKey, fly = flyKey } local oldKey = keys[rebinding] for name, k in pairs(keys) do if name ~= rebinding and k == newKey then keys[name] = oldKey end end keys[rebinding] = newKey toggleKey, stoppieKey, flyKey = keys.wheelie, keys.stoppie, keys.fly end rebinding = nil updateUI() end return end if gameProcessed then return end if input.KeyCode == toggleKey then setEnabled(not enabled) elseif input.KeyCode == stoppieKey then setStoppie(not stoppieEnabled) elseif input.KeyCode == flyKey then setFly(not flying) end end) ------------------------------------------------------------------ -- Finding the bike ------------------------------------------------------------------ local vehicleCache = { root = nil, result = false, checkedAt = 0 } -- True if the physics assembly includes parts that aren't part of the character local function carriesVehicle(root, character) local now = os.clock() if vehicleCache.root == root and now - vehicleCache.checkedAt < 1 then return vehicleCache.result end local result = false for _, part in ipairs(root:GetConnectedParts(true)) do if not part:IsDescendantOf(character) then result = true break end end vehicleCache.root = root vehicleCache.result = result vehicleCache.checkedAt = now return result end -- Returns: referencePart (for orientation), root (physics body to rotate) local function getVehicle() local character = player.Character if not character then return nil end local humanoid = character:FindFirstChildOfClass("Humanoid") local hrp = character:FindFirstChild("HumanoidRootPart") if not humanoid or not hrp then return nil end -- Normal case: you're sitting in a Seat or VehicleSeat local seat = humanoid.SeatPart if seat and seat:IsA("BasePart") then return seat, seat.AssemblyRootPart end -- Fallback: the character is welded/attached to the bike (no seat) local root = hrp.AssemblyRootPart if root and not root.Anchored and carriesVehicle(root, character) then return hrp, root end return nil end local function topModel(part) local model = part:FindFirstAncestorOfClass("Model") while model and model.Parent and model.Parent:IsA("Model") do model = model.Parent end return model end ------------------------------------------------------------------ -- Infinite fuel -- Freezes your fuel at whatever level it's at when you turn it on (if you -- refuel higher, it holds that new level). It looks on your bike and your -- player for number values / attributes whose names look like fuel (fuel, -- petrol, gasoline, gas, tank) and locks them: -- * the moment the game lowers one, a change listener puts it right back -- (same frame, before it's drawn), plus a per-frame check -- * the locked level is remembered between scans so it can't creep down -- If nothing matches a fuel name, it watches every number on your bike and -- player, and any value that keeps falling gets locked (auto-detect). -- Everything it finds is printed to the Output window. -- This only sticks if your game's fuel lives on the client or replicates from -- it. If your server owns the fuel, change it in the server script instead. ------------------------------------------------------------------ local fuelLookup = {} -- [instance][attributeName or VALUE_KEY] = entry local fuelList = {} local fuelWatch = setmetatable({}, { __mode = "k" }) -- [instance][key] = { last, drops } local fuelDumped = false local lastFuelScan = -math.huge local VALUE_KEY = "\0value" local AUTO_DROPS_NEEDED = 3 -- scans in a row a value must fall before auto-detect locks it local FUEL_WORDS = { "fuel", "petrol", "gasoline", "tank", "energy", "juice", "battery" } local IGNORE_WORDS = { "rate", "use", "usage", "drain", "consum", "burn", "cost", "price", "efficien", "pedal", "throttle" } local extraSet = {} for _, nm in ipairs(EXTRA_FUEL_NAMES) do extraSet[string.lower(nm)] = true end -- returns "current" for a fuel level, "max" for a capacity, nil for anything else local function classifyFuelName(name) local n = string.lower(name) if extraSet[n] then return "current" end local matches = n == "gas" or string.find(n, "^gas") ~= nil or string.find(n, "gas$") ~= nil for _, word in ipairs(FUEL_WORDS) do if string.find(n, word, 1, true) then matches = true break end end if not matches then return nil end if string.find(n, "max", 1, true) or string.find(n, "capacity", 1, true) then return "max" end for _, word in ipairs(IGNORE_WORDS) do if string.find(n, word, 1, true) then return nil end end return "current" end local function fuelPath(obj, attr) return obj:GetFullName() .. (attr and ("." .. attr) or "") end local function readNumber(obj, attr) local v if attr then v = obj:GetAttribute(attr) else v = obj.Value end return type(v) == "number" and v or nil end local function writeEntry(entry, value) if entry.attr then entry.obj:SetAttribute(entry.attr, value) else entry.obj.Value = value end end -- The level to hold: the tank's Max/Capacity value if we found one, else FUEL_FILL_TO local function fuelTarget(entry) local m = entry.max if m and m.obj.Parent then local cap = readNumber(m.obj, m.attr) if cap then return cap end end return FUEL_FILL_TO end local function enforce(entry) if not infFuel or not entry.obj.Parent then return end local current = readNumber(entry.obj, entry.attr) if not current then return end local target = fuelTarget(entry) if target then -- Keep it at max: anything below the target gets put straight back entry.seen = target if current < target then writeEntry(entry, target) end else -- No max known: hold the highest level seen (a refuel raises it) if current > entry.seen then entry.seen = current elseif current < entry.seen then writeEntry(entry, entry.seen) end end end local function dropEntry(entry) if entry.conn then entry.conn:Disconnect() entry.conn = nil end end local function clearFuel() for _, byObj in pairs(fuelLookup) do for _, entry in pairs(byObj) do dropEntry(entry) end end fuelLookup, fuelList = {}, {} fuelWatch = setmetatable({}, { __mode = "k" }) fuelDumped = false end local function registerTarget(found, obj, attr, value, auto, maxRef) local byObj = fuelLookup[obj] if not byObj then byObj = {} fuelLookup[obj] = byObj end local key = attr or VALUE_KEY local entry = byObj[key] if not entry then entry = { obj = obj, attr = attr, seen = value, auto = auto, max = maxRef } byObj[key] = entry local signal if attr then signal = obj:GetAttributeChangedSignal(attr) else signal = obj:GetPropertyChangedSignal("Value") end entry.conn = signal:Connect(function() enforce(entry) -- put it back the instant something lowers it end) print("[AutoWheelie] locking fuel" .. (auto and " (auto-detected)" or "") .. ":", fuelPath(obj, attr), "=", value, maxRef and ("(max: " .. fuelPath(maxRef.obj, maxRef.attr) .. ")") or "(no max value found)") enforce(entry) -- fill it right now elseif maxRef then entry.max = maxRef end found[entry] = true end local function scanFuel(bikeModel) local found = {} local numeric = {} -- every other number we saw (for the Output dump and auto-detect) local currents, maxes = {}, {} local function consider(inst) if inst:IsA("NumberValue") or inst:IsA("IntValue") then local kind = classifyFuelName(inst.Name) if kind == "current" then table.insert(currents, { obj = inst, value = inst.Value }) elseif kind == "max" then table.insert(maxes, { obj = inst, value = inst.Value }) elseif kind == nil then table.insert(numeric, { obj = inst, value = inst.Value }) end end for name, value in pairs(inst:GetAttributes()) do if type(value) == "number" then local kind = classifyFuelName(name) if kind == "current" then table.insert(currents, { obj = inst, attr = name, value = value }) elseif kind == "max" then table.insert(maxes, { obj = inst, attr = name, value = value }) elseif kind == nil then table.insert(numeric, { obj = inst, attr = name, value = value }) end end end end local function scanTree(rootInst) consider(rootInst) for _, inst in ipairs(rootInst:GetDescendants()) do consider(inst) end end if bikeModel then scanTree(bikeModel) end if player.Character then scanTree(player.Character) end -- the Player itself (stats folders etc.), but not our own GUI consider(player) for _, child in ipairs(player:GetChildren()) do if child ~= playerGui then scanTree(child) end end -- Pair each fuel level with a Max/Capacity value that lives in the same place local function container(item) return item.attr and item.obj or item.obj.Parent end local maxByContainer = {} for _, m in ipairs(maxes) do local c = container(m) if c and not maxByContainer[c] then maxByContainer[c] = m end end for _, item in ipairs(currents) do registerTarget(found, item.obj, item.attr, item.value, false, maxByContainer[container(item)]) end -- Nothing matched a fuel name: dump what we can see, and lock anything that keeps falling if next(found) == nil then if not fuelDumped then fuelDumped = true print(string.format("[AutoWheelie] no fuel-named values found. %d other numbers on your bike/player:", #numeric)) for i, item in ipairs(numeric) do if i > 60 then print(" ...and " .. (#numeric - 60) .. " more") break end print(" " .. fuelPath(item.obj, item.attr), "=", item.value) end end for _, item in ipairs(numeric) do local byObj = fuelWatch[item.obj] if not byObj then byObj = {} fuelWatch[item.obj] = byObj end local key = item.attr or VALUE_KEY local state = byObj[key] if not state then byObj[key] = { last = item.value, drops = 0 } else if item.value < state.last then state.drops += 1 elseif item.value > state.last then state.drops = 0 end state.last = item.value if state.drops >= AUTO_DROPS_NEEDED and item.value > 0 then registerTarget(found, item.obj, item.attr, item.value, true) end end end end -- keep entries we still see (so their locked level survives), drop the rest local list = {} for obj, byObj in pairs(fuelLookup) do for key, entry in pairs(byObj) do if (found[entry] or entry.auto) and entry.obj.Parent then table.insert(list, entry) else dropEntry(entry) byObj[key] = nil end end if next(byObj) == nil then fuelLookup[obj] = nil end end fuelList = list if fuelFound ~= #list then fuelFound = #list updateUI() end end RunService.Heartbeat:Connect(function() if fuelResetRequested then fuelResetRequested = false clearFuel() lastFuelScan = -math.huge end if not infFuel then return end local now = os.clock() if now - lastFuelScan >= FUEL_SCAN_INTERVAL then lastFuelScan = now local _, bikeRoot = getVehicle() scanFuel(bikeRoot and topModel(bikeRoot) or nil) end for _, entry in ipairs(fuelList) do enforce(entry) end end) RunService.RenderStepped:Connect(function() if not infFuel then return end for _, entry in ipairs(fuelList) do enforce(entry) end end) ------------------------------------------------------------------ -- Fly (moves ONLY your character. The bike stays parked on the ground.) -- * Move with your normal keys / thumbstick, toward where the camera looks -- * Space = up, Ctrl or C = down, Shift = boost (gamepad: A up, B down, X boost) -- * Touch screens get Up / Down buttons while flying -- * Let go of everything and you hover in place (smooth speed up and slow down) -- * You lean into the direction you fly, and it always faces the camera -- The server helper (second copy of this script) gets you off the bike for real and, if your -- movement isn't getting through, it drives the flight itself so nothing can snap you back. -- The status pill shows "[server]" when the server is driving. ------------------------------------------------------------------ do local flyData = nil -- live objects while flying (nil = not flying) local flyToken = 0 -- changes on every on/off, so a slow start can never outlive a quick toggle local holdUp, holdDown = false, false local controls = nil -- Roblox's own PlayerModule controls, so keyboard / thumbstick / touch all just work task.spawn(function() local scripts = player:WaitForChild("PlayerScripts", 10) local module = scripts and scripts:WaitForChild("PlayerModule", 10) if module then local ok, result = pcall(function() return require(module):GetControls() end) if ok then controls = result end end end) -- Up / Down buttons (only shown on touch devices while flying) local touchButtons = Instance.new("Frame") touchButtons.Name = "FlyTouchButtons" touchButtons.BackgroundTransparency = 1 touchButtons.Size = UDim2.fromOffset(64, 136) touchButtons.Position = UDim2.new(1, -84, 0.5, -68) touchButtons.Visible = false touchButtons.Parent = gui local function makeHoldButton(text, y, onChange) local b = Instance.new("TextButton") b.Size = UDim2.fromOffset(64, 64) b.Position = UDim2.fromOffset(0, y) b.BackgroundColor3 = THEME.panel b.BackgroundTransparency = 0.15 b.BorderSizePixel = 0 b.AutoButtonColor = false b.Font = Enum.Font.GothamBold b.TextSize = 22 b.TextColor3 = THEME.text b.Text = text b.Parent = touchButtons addCorner(b, 32) addStroke(b, 0.8) b.InputBegan:Connect(function(input) if input.UserInputType == Enum.UserInputType.Touch or input.UserInputType == Enum.UserInputType.MouseButton1 then onChange(true) tween(b, { BackgroundColor3 = THEME.accent }) end end) b.InputEnded:Connect(function(input) if input.UserInputType == Enum.UserInputType.Touch or input.UserInputType == Enum.UserInputType.MouseButton1 then onChange(false) tween(b, { BackgroundColor3 = THEME.panel }) end end) end makeHoldButton("▲", 0, function(v) holdUp = v end) makeHoldButton("▼", 72, function(v) holdDown = v end) -- Humanoid states that would fight the flight (sitting, running on the floor, climbing, ragdoll...) local BLOCKED_STATES = { Enum.HumanoidStateType.Seated, Enum.HumanoidStateType.Landed, Enum.HumanoidStateType.Running, Enum.HumanoidStateType.RunningNoPhysics, Enum.HumanoidStateType.Climbing, Enum.HumanoidStateType.GettingUp, Enum.HumanoidStateType.FallingDown, Enum.HumanoidStateType.Ragdoll, } -- Is the character still physically joined to something that isn't the character (the bike)? local function attachedToVehicle(character, hrp) local root = hrp.AssemblyRootPart if not root then return false end for _, part in ipairs(root:GetConnectedParts(true)) do if not part:IsDescendantOf(character) then return true end end return false end -- Only used when there's no server helper: cut every link between you and the bike on your screen local function detachFromVehicle(character, hrp) pcall(function() local root = hrp.AssemblyRootPart local parts = root and root:GetConnectedParts(true) or {} for _, part in ipairs(parts) do for _, joint in ipairs(part:GetJoints()) do local a, b if joint:IsA("JointInstance") or joint:IsA("WeldConstraint") then a, b = joint.Part0, joint.Part1 elseif joint:IsA("Constraint") then a = joint.Attachment0 and joint.Attachment0.Parent b = joint.Attachment1 and joint.Attachment1.Parent end if a and b and (a:IsDescendantOf(character) ~= b:IsDescendantOf(character)) then joint:Destroy() end end end end) end local function dropMovers(s) if s.lv then s.lv:Destroy() s.lv = nil end if s.ao then s.ao:Destroy() s.ao = nil end if s.att then s.att:Destroy() s.att = nil end end -- The server decided to drive the flight itself (your own movement wasn't getting through) local function switchToServer(s) if s.mode == "server" then return end s.mode = "server" dropMovers(s) warn("[Fly] your movement wasn't reaching the server, so the server is now driving the flight.") end -- Talking to the server helper local remotes = nil local function getRemotes() if remotes then return remotes end local request = ReplicatedStorage:FindFirstChild("FlyRequest") or ReplicatedStorage:WaitForChild("FlyRequest", 3) local input = request and ReplicatedStorage:WaitForChild("FlyInput", 3) local modeEvent = request and ReplicatedStorage:WaitForChild("FlyMode", 3) if not (request and input and modeEvent) then return nil end remotes = { request = request, input = input, mode = modeEvent } modeEvent.OnClientEvent:Connect(function(mode) if flyData and mode == "server" then switchToServer(flyData) elseif flyData and mode == "yank" then flyData.undone += 1 flyData.noticeUntil = os.clock() + 3 end end) return remotes end local serverFlyOn = false local function tellServer(on) local r = getRemotes() if not r then return nil end local ok, result = pcall(function() return r.request:InvokeServer(on) end) if ok then return result end return nil end -- Optional: switch off LocalScripts (by name) that move you around while you fly local pausedLocal = {} local AUTO_PAUSE_WORDS = { "anticheat", "anti_cheat", "anti-cheat", "cheat", "exploit", "return", "respawn", "teleport", "leash", "tether", "boundary", "outofbounds", "kick", "distance", "safezone" } local function isAutoSuspect(inst) if not AUTO_PAUSE_LOCAL_SUSPECTS then return false end if inst.Name == script.Name then return false end local n = string.lower(inst.Name) for _, word in ipairs(AUTO_PAUSE_WORDS) do if string.find(n, word, 1, true) then return true end end return false end local function pauseLocalScripts() local names = {} for _, n in ipairs(PAUSE_LOCAL_SCRIPTS_WHILE_FLYING) do names[n] = true end local function scan(root) for _, inst in ipairs(root:GetDescendants()) do if inst:IsA("LocalScript") and inst ~= script and (names[inst.Name] or isAutoSuspect(inst)) then pcall(function() if inst.Enabled then pausedLocal[inst] = true inst.Enabled = false print("[Fly] paused script while flying:", inst:GetFullName()) end end) end end end scan(player) if player.Character then scan(player.Character) end end local function resumeLocalScripts() for inst in pairs(pausedLocal) do pausedLocal[inst] = nil if inst.Parent then pcall(function() inst.Enabled = true end) end end end -- Anything that PUSHES or HOLDS your character (leftover movers/forces) makes the fall slow or pulls -- you sideways. After you let go of fly these are removed so only normal gravity is left. local STRAY_MOVER_CLASSES = { "BodyMover", "RocketPropulsion", "LinearVelocity", "AngularVelocity", "VectorForce", "LineForce", "Torque", "AlignPosition", "AlignOrientation", "PlaneConstraint" } local function removeStrayMovers(character) if not character then return end for _, inst in ipairs(character:GetDescendants()) do for _, class in ipairs(STRAY_MOVER_CLASSES) do if inst:IsA(class) then print("[Fly] removed leftover mover after flying:", inst:GetFullName()) pcall(function() inst:Destroy() end) break end end end end local function stopFly() resumeLocalScripts() local s = flyData flyData = nil holdUp, holdDown = false, false touchButtons.Visible = false if serverFlyOn then serverFlyOn = false task.spawn(tellServer, false) end if not s then return end if s.conn then s.conn:Disconnect() end if s.cfConn then s.cfConn:Disconnect() end if s.idleTrack then pcall(function() s.idleTrack:Stop(0.1) s.idleTrack:Destroy() end) end if s.animate and s.animate.Parent then s.animate.Enabled = true end dropMovers(s) for part, was in pairs(s.touch or {}) do if part.Parent then part.CanTouch = was end end local humanoid, hrp = s.humanoid, s.hrp if humanoid and humanoid.Parent then for _, state in ipairs(BLOCKED_STATES) do humanoid:SetStateEnabled(state, true) end humanoid.PlatformStand = false humanoid.AutoRotate = s.autoRotate removeStrayMovers(s.character) humanoid:ChangeState(Enum.HumanoidStateType.Freefall) end -- (when the server was driving, it stands you back up itself) if s.mode == "client" and hrp and hrp.Parent then local look = hrp.CFrame.LookVector local flat = Vector3.new(look.X, 0, look.Z) if flat.Magnitude > 0.05 then hrp.CFrame = CFrame.lookAt(hrp.Position, hrp.Position + flat.Unit) end hrp.AssemblyLinearVelocity = Vector3.new(s.vel.X * 0.25, math.min(s.vel.Y * 0.25, 0), s.vel.Z * 0.25) -- plain fall, no slow-fall hrp.AssemblyAngularVelocity = Vector3.zero end -- For a moment after you let go, if something teleports you a long way, put you straight back if humanoid and humanoid.Parent and hrp and hrp.Parent then task.spawn(function() local t0 = os.clock() local lastCF = hrp.CFrame local warned = false local vy0 = math.min(hrp.AssemblyLinearVelocity.Y, 0) local landed = false local nextClean = 0 while os.clock() - t0 < math.max(3, POST_FLY_GUARD_SECONDS) and hrp.Parent and humanoid.Parent and not flyData and not humanoid.SeatPart do RunService.Heartbeat:Wait() local t = os.clock() - t0 if t >= nextClean and t < 3 then nextClean = t + 0.25 removeStrayMovers(s.character) end -- normal gravity: if something is holding you up, fall like you always do until you land if not landed then if humanoid.FloorMaterial ~= Enum.Material.Air then landed = true else local v = hrp.AssemblyLinearVelocity local normal = vy0 - Workspace.Gravity * t if v.Y > normal then hrp.AssemblyLinearVelocity = Vector3.new(v.X, normal, v.Z) end end end local d = (hrp.Position - lastCF.Position).Magnitude if d > math.max(40, hrp.AssemblyLinearVelocity.Magnitude * 0.2 + 25) then if not warned then warned = true warn(string.format("[Fly] something sent you %.0f studs right after you stopped flying, putting you back.", d)) end hrp.CFrame = lastCF else lastCF = hrp.CFrame end end end) end end local function readInput(cam) local typing = UserInputService:GetFocusedTextBox() ~= nil -- sideways / forwards, relative to where the camera looks local move = Vector3.zero if controls then local ok, m = pcall(function() return controls:GetMoveVector() end) if ok and m then move = m end elseif not typing then local x, z = 0, 0 if UserInputService:IsKeyDown(Enum.KeyCode.W) then z -= 1 end if UserInputService:IsKeyDown(Enum.KeyCode.S) then z += 1 end if UserInputService:IsKeyDown(Enum.KeyCode.A) then x -= 1 end if UserInputService:IsKeyDown(Enum.KeyCode.D) then x += 1 end move = Vector3.new(x, 0, z) end local world = cam.CFrame:VectorToWorldSpace(Vector3.new(move.X, 0, move.Z)) -- up / down / boost local up = 0 local boost = false if not typing then if UserInputService:IsKeyDown(Enum.KeyCode.Space) then up += 1 end if UserInputService:IsKeyDown(Enum.KeyCode.LeftControl) or UserInputService:IsKeyDown(Enum.KeyCode.C) then up -= 1 end boost = UserInputService:IsKeyDown(Enum.KeyCode.LeftShift) end if UserInputService:IsGamepadButtonDown(Enum.UserInputType.Gamepad1, Enum.KeyCode.ButtonA) then up += 1 end if UserInputService:IsGamepadButtonDown(Enum.UserInputType.Gamepad1, Enum.KeyCode.ButtonB) then up -= 1 end if UserInputService:IsGamepadButtonDown(Enum.UserInputType.Gamepad1, Enum.KeyCode.ButtonX) then boost = true end if holdUp then up += 1 end if holdDown then up -= 1 end local dir = world + Vector3.new(0, math.clamp(up, -1, 1), 0) if dir.Magnitude > 1 then dir = dir.Unit end return dir, boost end local function flyStep(dt) local s = flyData if not s then return end local humanoid, hrp = s.humanoid, s.hrp if not (s.character.Parent and humanoid.Parent and hrp.Parent) or humanoid.Health <= 0 then setFly(false) return end local cam = Workspace.CurrentCamera if not cam then return end -- If the game grabbed you back onto a seat, hop off again (the bike must stay on the ground) if humanoid.SeatPart then humanoid.Sit = false humanoid:ChangeState(Enum.HumanoidStateType.Jumping) elseif humanoid:GetState() ~= Enum.HumanoidStateType.Freefall then humanoid:ChangeState(Enum.HumanoidStateType.Freefall) end local clientMode = (s.mode == "client") -- anything welded / roped from you to something that isn't you (the bike) is cut every frame for _, j in ipairs(hrp:GetJoints()) do local a, b if j:IsA("JointInstance") or j:IsA("WeldConstraint") then a, b = j.Part0, j.Part1 elseif j:IsA("Constraint") then a = j.Attachment0 and j.Attachment0.Parent b = j.Attachment1 and j.Attachment1.Parent end if a and b and (a:IsDescendantOf(s.character) ~= b:IsDescendantOf(s.character)) then pcall(function() j:Destroy() end) end end -- Detect something teleporting you (a big jump in one frame) and put you straight back if clientMode then local jumped = (hrp.Position - s.lastPos).Magnitude if jumped > math.max(80, s.vel.Magnitude * dt * 4 + 40) then s.snaps += 1 local nowT = os.clock() table.insert(s.snapTimes, nowT) while #s.snapTimes > 0 and nowT - s.snapTimes[1] > 3 do table.remove(s.snapTimes, 1) end warn(string.format("[Fly] something moved you %.0f studs in one frame (x%d). A script in your game is teleporting you.", jumped, s.snaps)) if #s.snapTimes >= 8 then warn("[Fly] your game keeps teleporting you back, so fly was switched off. Add if player:GetAttribute(\"Flying\") then return end to the script that moves you.") setFly(false) return end hrp.CFrame = s.lastCF hrp.AssemblyLinearVelocity = s.vel hrp.AssemblyAngularVelocity = Vector3.zero end end -- keep every animation except the calm idle pose stopped (no running / falling legs in the air) if s.animator then for _, track in ipairs(s.animator:GetPlayingAnimationTracks()) do if track ~= s.idleTrack then track:Stop(0.1) end end end -- Where do we want to go? local dir, boost = readInput(cam) local topSpeed = flySpeed * (boost and FLY_BOOST_MULT or 1) local desired = dir * topSpeed -- Glide toward it: speeds up smoothly, and brakes a little harder than it accelerates local rate = (dir.Magnitude > 0.05) and FLY_ACCEL or FLY_BRAKE s.vel = s.vel:Lerp(desired, 1 - math.exp(-rate * dt)) -- Face where the camera looks local look = cam.CFrame.LookVector local flat = Vector3.new(look.X, 0, look.Z) if flat.Magnitude > 0.05 then s.facing = flat.Unit end if clientMode then -- your own computer moves you (smoothest) s.lv.VectorVelocity = s.vel local forwardSpeed = s.vel:Dot(s.facing) local leanTarget = math.clamp(forwardSpeed / (flySpeed * FLY_BOOST_MULT), -0.3, 1) * math.rad(FLY_LEAN_DEG) s.lean += (leanTarget - s.lean) * (1 - math.exp(-8 * dt)) s.ao.CFrame = CFrame.lookAt(Vector3.zero, s.facing) * CFrame.Angles(-s.lean, 0, 0) end s.lastPos = hrp.Position s.lastCF = hrp.CFrame -- Tell the server: constantly while it's driving, and a few times a second so it can check -- that your movement is getting through local now = os.clock() if remotes and (not clientMode or now - s.lastSend > 0.2) then s.lastSend = now remotes.input:FireServer(dir, topSpeed, flySpeed * FLY_BOOST_MULT, s.facing, hrp.Position) end -- Status pill if now - s.statusAt > 0.15 then s.statusAt = now local text = string.format("holding flight, %d studs/s%s", math.floor(s.vel.Magnitude + 0.5), clientMode and "" or " [server]") if now < s.noticeUntil then text = text .. string.format(" - blocked a teleport (x%d)", s.undone) end setStatus(text) end end local function startFly(token) local character = player.Character local humanoid = character and character:FindFirstChildOfClass("Humanoid") local hrp = character and character:FindFirstChild("HumanoidRootPart") if not humanoid or not hrp or humanoid.Health <= 0 then if token == flyToken then flying = false updateUI() end return end -- 0) The server takes you off the seat, cuts anything tying you to the bike, and takes care of -- the physics ownership. This is what stops the teleport back. local serverResult = tellServer(true) if token ~= flyToken then if serverResult and not flying then task.spawn(tellServer, false) end return end local mode = "client" if serverResult == "client" or serverResult == "server" then serverFlyOn = true mode = serverResult elseif serverResult == false then warn("[Fly] the server refused. Add your UserId to ALLOWED_USER_IDS at the top of the server half, or fly will snap you back.") else warn("[Fly] no server helper found. Put this same script in ServerScriptService too, or fly will snap you back.") end pauseLocalScripts() -- 1) Hop off the bike so ONLY you fly. The bike stays where it is. local hadSeat = humanoid.SeatPart ~= nil if hadSeat then local t0 = os.clock() while humanoid.SeatPart and os.clock() - t0 < 1.5 and token == flyToken do humanoid.Sit = false humanoid.Jump = true humanoid:ChangeState(Enum.HumanoidStateType.Jumping) task.wait(0.1) end if token ~= flyToken then return end if humanoid.SeatPart then -- the game won't let go of you: flying now would just snap you back to the seat warn("[Fly] couldn't get off the seat, so fly was cancelled") if serverFlyOn then serverFlyOn = false task.spawn(tellServer, false) end flying = false updateUI() setStatus("couldn't get off the bike") return end task.wait(0.1) -- let the server finish removing the seat weld if token ~= flyToken then return end elseif not serverFlyOn and attachedToVehicle(character, hrp) then -- no seat and no server helper, but welded to the bike some other way detachFromVehicle(character, hrp) task.wait(0.1) if token ~= flyToken then return end end -- 2) Stop the humanoid from fighting the flight local prior = {} for _, state in ipairs(BLOCKED_STATES) do prior[state] = humanoid:GetStateEnabled(state) humanoid:SetStateEnabled(state, false) end local autoRotate = humanoid.AutoRotate humanoid.AutoRotate = false local look = hrp.CFrame.LookVector local facing = Vector3.new(look.X, 0, look.Z) facing = facing.Magnitude > 0.05 and facing.Unit or Vector3.new(0, 0, -1) -- your character can't set off Touched events on your screen while you fly (no touch-zone teleports) local touch = {} for _, d in ipairs(character:GetDescendants()) do if d:IsA("BasePart") then touch[d] = d.CanTouch d.CanTouch = false end end local data = { touch = touch, character = character, humanoid = humanoid, hrp = hrp, prior = prior, autoRotate = autoRotate, mode = mode, vel = Vector3.zero, facing = facing, lean = 0, statusAt = 0, lastSend = 0, lastPos = hrp.Position, lastCF = hrp.CFrame, snaps = 0, snapTimes = {}, undone = 0, noticeUntil = 0, } -- 3) Client mode: the movers. One holds your velocity (so no gravity drift while hovering), -- one holds your facing. (In server mode the server has its own and this part is skipped.) if mode == "client" then local att = Instance.new("Attachment") att.Name = "FlyAttachment" att.Parent = hrp local startVel = hrp.AssemblyLinearVelocity if startVel.Magnitude > 120 then startVel = startVel.Unit * 120 end startVel = Vector3.new(startVel.X * 0.3, 0, startVel.Z * 0.3) -- keep a little momentum, no jolt data.vel = startVel local lv = Instance.new("LinearVelocity") lv.Name = "FlyVelocity" lv.Attachment0 = att lv.RelativeTo = Enum.ActuatorRelativeTo.World lv.VelocityConstraintMode = Enum.VelocityConstraintMode.Vector lv.MaxForce = math.huge lv.VectorVelocity = startVel lv.Parent = hrp local ao = Instance.new("AlignOrientation") ao.Name = "FlyOrientation" ao.Mode = Enum.OrientationAlignmentMode.OneAttachment ao.Attachment0 = att ao.RigidityEnabled = false ao.MaxTorque = math.huge ao.MaxAngularVelocity = math.huge ao.Responsiveness = 30 ao.CFrame = CFrame.lookAt(Vector3.zero, facing) ao.Parent = hrp data.att, data.lv, data.ao = att, lv, ao end -- Animations: switch off the default Animate script (the one that makes your legs run / arms -- flail in the air) and hold the game's own idle pose instead, so you hover like a normal fly. do local animate = character:FindFirstChild("Animate") if animate and animate:IsA("LocalScript") and animate.Enabled then data.animate = animate animate.Enabled = false end local animator = humanoid:FindFirstChildOfClass("Animator") data.animator = animator local idleFolder = animate and animate:FindFirstChild("idle") local idleAnim = idleFolder and idleFolder:FindFirstChild("Animation1") if animator and idleAnim and idleAnim:IsA("Animation") then pcall(function() local track = animator:LoadAnimation(idleAnim) track.Looped = true track.Priority = Enum.AnimationPriority.Action track:Play(0.2) data.idleTrack = track end) end end flyData = data if mode == "client" then -- if a script teleports you, undo it the same instant (so you never see it) data.cfConn = hrp:GetPropertyChangedSignal("CFrame"):Connect(function() if flyData ~= data then return end local jumped = (hrp.Position - data.lastPos).Magnitude if jumped > math.max(60, data.vel.Magnitude * 0.1 + 40) then data.undone += 1 data.noticeUntil = os.clock() + 3 hrp.CFrame = data.lastCF hrp.AssemblyLinearVelocity = data.vel hrp.AssemblyAngularVelocity = Vector3.zero end end) end data.conn = RunService.RenderStepped:Connect(flyStep) touchButtons.Visible = UserInputService.TouchEnabled end setFly = function(value) value = value and true or false if value == flying then return end flying = value flyToken += 1 if value then -- fly, wheelie and stoppie all want the bike/character, so only one at a time enabled = false stoppieEnabled = false task.spawn(startFly, flyToken) else stopFly() end updateUI() end -- Respawning / leaving the character always switches fly off cleanly player.CharacterRemoving:Connect(function() if flying then setFly(false) end end) end ------------------------------------------------------------------ -- Wheelie / Stoppie logic (runs before each physics step) -- -- Both tricks use the same controller: a "commanded" pitch angle glides toward a target and the -- bike is rotated to follow it (positive = nose up = wheelie, negative = nose down = stoppie). -- * Wheelie: target is the Wheelie Angle slider. -- * Stoppie: target is the Stoppie Angle slider, but it behaves like a real one: -- - the rear only lifts while you BRAKE (turn off with "Brake to Stoppie") -- - the angle scales with your speed, so as you slow to a stop the rear settles back down -- - when you let go of the brake it eases the bike level before handing control back ------------------------------------------------------------------ local fallbackDirection = FRONT_IS_REVERSED and -1 or 1 local localForward = nil -- the bike's front, stored relative to the reference part local calibratedRoot = nil -- which bike localForward belongs to local cmdPitch = nil -- the "commanded" angle that glides toward the target (nil = not holding yet) local lastMode = nil -- "wheelie", "stoppie" or nil: lets us reset cleanly when the mode changes local releaseTimer = 0 -- stoppie: how long the bike has been level since the brake was released -- Smoothness (0-100) -> how fast the angle changes, in degrees/sec. -- Exponential, so each step of the slider feels like an even change. local function slewFromSmoothness(v) return SLEW_SLOWEST_DEG * (SLEW_FASTEST_DEG / SLEW_SLOWEST_DEG) ^ (v / 100) end -- 0 -> 1 with soft ends (so the stoppie fades in/out with speed instead of switching) local function smooth01(x) x = math.clamp(x, 0, 1) return x * x * (3 - 2 * x) end -- Are you braking? Works with a VehicleSeat, S / Down arrow, a gamepad trigger, or pulling back on a thumbstick local function isBraking(ref, forward) if ref:IsA("VehicleSeat") and ref.Throttle < 0 then return true end if not UserInputService:GetFocusedTextBox() then if UserInputService:IsKeyDown(Enum.KeyCode.S) or UserInputService:IsKeyDown(Enum.KeyCode.Down) then return true end end if UserInputService:IsGamepadButtonDown(Enum.UserInputType.Gamepad1, Enum.KeyCode.ButtonL2) then return true end local character = player.Character local humanoid = character and character:FindFirstChildOfClass("Humanoid") if humanoid and humanoid.MoveDirection.Magnitude > 0.1 and humanoid.MoveDirection:Dot(forward) < -0.4 then return true end return false end RunService.Stepped:Connect(function(_, dt) -- "Reset Front" button: forget the learned front so it's found again from the way the bike drives if resetFrontRequested then resetFrontRequested = false localForward = nil cmdPitch = nil end local mode = enabled and "wheelie" or (stoppieEnabled and "stoppie" or nil) if mode ~= lastMode then lastMode = mode cmdPitch = nil -- start the new trick from the bike's real angle releaseTimer = 0 end if not mode then if not flying then setStatus("off") end -- while flying, the Fly section writes the status return end local ref, root = getVehicle() if not ref or not root then localForward = nil cmdPitch = nil calibratedRoot = nil setStatus("can't find your bike. Sit on it (or get on it) first") return end if root.Anchored then cmdPitch = nil setStatus("bike is anchored, so it can't move") return end -- New bike? Forget what we learned about the old one if calibratedRoot ~= root then cmdPitch = nil calibratedRoot = root localForward = nil end local cf = ref.CFrame -- Learn which way is "front" from the way the bike actually drives if AUTO_DETECT_FRONT and not localForward then local vel = root.AssemblyLinearVelocity local flat = Vector3.new(vel.X, 0, vel.Z) local reversing = ref:IsA("VehicleSeat") and ref.Throttle < 0 if flat.Magnitude >= DETECT_SPEED and not reversing then local dirUnit = flat.Unit local best, bestDot = nil, 0.8 local candidates = { cf.LookVector, -cf.LookVector, cf.RightVector, -cf.RightVector } for _, c in ipairs(candidates) do local d = c:Dot(dirUnit) if d > bestDot then best, bestDot = c, d end end if best then localForward = cf:VectorToObjectSpace(best) end end if not localForward then setStatus("drive forward a bit so I can find the bike's front") return end end local forward if AUTO_DETECT_FRONT then forward = cf:VectorToWorldSpace(localForward) else forward = cf.LookVector * fallbackDirection end local forwardSpeed = root.AssemblyLinearVelocity:Dot(forward) -- Axis that tips the nose up (forward x up). Positive rotation = nose up. local axis = forward:Cross(cf.UpVector) local pitch = math.asin(math.clamp(forward.Y, -1, 1)) -- Work out where we want the bike to be, and how smooth to be about it local targetPitch, modeSmooth if mode == "wheelie" then if REQUIRE_THROTTLE and ref:IsA("VehicleSeat") and ref.Throttle <= 0 then cmdPitch = nil setStatus("hold forward to wheelie") return end if math.abs(forwardSpeed) < MIN_SPEED then cmdPitch = nil setStatus("go faster to wheelie") return end targetPitch = math.rad(targetAngleDeg) -- follows the Wheelie Angle slider live modeSmooth = smoothness else modeSmooth = stoppieSmoothness local braking = (not brakeToStoppie) or isBraking(ref, forward) -- Fades in as you speed up past STOPPIE_MIN_SPEED, so the rear settles as you slow to a stop local speedFactor = smooth01((forwardSpeed - STOPPIE_MIN_SPEED) / (STOPPIE_FULL_SPEED - STOPPIE_MIN_SPEED)) if braking and speedFactor > 0 then releaseTimer = 0 targetPitch = -math.rad(stoppieAngleDeg) * speedFactor -- negative = nose down else if cmdPitch == nil then -- not in a stoppie: stay out of the way and let the bike ride normally setStatus(braking and "ready: ride faster to stoppie" or "ready: hold the brake to lift the rear") return end -- stoppie just ended: ease the bike back level, then hand control back targetPitch = 0 if math.abs(cmdPitch) < 0.02 then releaseTimer += dt if releaseTimer >= STOPPIE_SETTLE_TIME then cmdPitch = nil releaseTimer = 0 setStatus("ready: hold the brake to lift the rear") return end end end end -- Smoothness: instead of snapping to the target, a "commanded" angle glides toward it at the -- speed set by the Smoothness slider. The controller below chases that glide path, so how the -- trick FEELS changes, but how firmly it HOLDS (and the side-balance) stays the same. if cmdPitch == nil then cmdPitch = pitch -- start gliding from wherever the bike is right now end local slewRad = math.rad(slewFromSmoothness(modeSmooth)) local maxStep = slewRad * dt cmdPitch += math.clamp(targetPitch - cmdPitch, -maxStep, maxStep) -- Above the neutral setting (60) it also pulls a little harder so "Instant" really is instant local gainNow = GAIN * (1 + math.max(0, modeSmooth - 60) / 40 * 0.8) local maxRate = math.max(MAX_RATE, slewRad * 1.25) local desiredRate = math.clamp((cmdPitch - pitch) * gainNow, -maxRate, maxRate) local angVel = root.AssemblyAngularVelocity local blend = math.clamp(dt * RESPONSE, 0, 1) -- Pitch: hold the bike at the commanded angle local currentRate = angVel:Dot(axis) local newVel = angVel + axis * (desiredRate - currentRate) * blend -- Roll: keep the bike from falling sideways. This is weakened while you steer -- so turning still works. Turning (yaw) is never touched. local levelRight = forward:Cross(Vector3.yAxis) if levelRight.Magnitude > 0.1 then levelRight = levelRight.Unit local steering = false if ref:IsA("VehicleSeat") and math.abs(ref.SteerFloat) > 0.1 then steering = true else local character = player.Character local humanoid = character and character:FindFirstChildOfClass("Humanoid") if humanoid and math.abs(humanoid.MoveDirection:Dot(levelRight)) > 0.3 then steering = true end end local gain = steering and ROLL_GAIN * STEER_ROLL_FACTOR or ROLL_GAIN local rollError = forward:Dot(axis:Cross(levelRight)) local desiredRoll = math.clamp(rollError * gain, -MAX_RATE, MAX_RATE) local currentRoll = angVel:Dot(forward) newVel += forward * (desiredRoll - currentRoll) * blend end root.AssemblyAngularVelocity = newVel local bikeName = (topModel(root) or root).Name if mode == "wheelie" then setStatus(string.format("holding wheelie on %s (%d°)", bikeName, math.floor(math.deg(pitch) + 0.5))) else setStatus(string.format("holding stoppie on %s (%d°)", bikeName, math.floor(math.deg(-pitch) + 0.5))) end end)