--[[ Brookhaven Studio v6 Car, stunt, RGB, player and house suite for Brookhaven RP, in the Snitch interface (black and white). Every server action goes through Brookhaven's own requests, so it only does what your gamepasses already unlock. Handling, braking and stunts are physics on the car you are driving (your client simulates it); top speed stays at the server's cap for your passes. RightShift: open / close the menu. X: stunt. C: car hop. Hold Q: drift. Any jump and the car does a trick by itself (Stunts -> Air Stunts / Every Jump). ]] local ENV = getgenv() -- Re-running keeps the window hidden if you had hidden it. local startShown = not (ENV.BrookhavenStudio and ENV.BrookhavenStudio.IsShown and not ENV.BrookhavenStudio.IsShown()) if ENV.BrookhavenStudio and ENV.BrookhavenStudio.Unload then local ok, err = pcall(ENV.BrookhavenStudio.Unload) if not ok then warn("[Brookhaven Studio] previous instance failed to unload: " .. tostring(err)) end end local Players = game:GetService("Players") local ReplicatedStorage = game:GetService("ReplicatedStorage") local TweenService = game:GetService("TweenService") local UserInputService = game:GetService("UserInputService") local RunService = game:GetService("RunService") local MarketplaceService = game:GetService("MarketplaceService") local HttpService = game:GetService("HttpService") local Lighting = game:GetService("Lighting") local VirtualUser = game:GetService("VirtualUser") local LocalPlayer = Players.LocalPlayer local Remotes = ReplicatedStorage:WaitForChild("Remotes") local LegacyRemotes = ReplicatedStorage:WaitForChild("RE") local Gamepasses = require(ReplicatedStorage.Modules.Shared.PlayerData.Gamepasses) --------------------------------------------------------------------------------------------- -- Game bridge --------------------------------------------------------------------------------------------- -- Brookhaven pads its legacy remote names with "1"s (e.g. "1Ca1r"), so match on the cleaned name. local function legacyRemote(cleanName) for _, child in ipairs(LegacyRemotes:GetChildren()) do if (child.Name:gsub("1", "")) == cleanName then return child end end error("[Brookhaven Studio] RE." .. cleanName .. " not found - the game may have updated") end local function remote(name) local r = Remotes:FindFirstChild(name) if not r then error("[Brookhaven Studio] Remotes." .. name .. " not found - the game may have updated") end return r end local CarEvent = legacyRemote("Car") local HouseEvent = legacyRemote("PlayersHouse") local NameColorEvent = legacyRemote("RPNameColor") local NameTextEvent = legacyRemote("RPNameText") local PropertySetColor = remote("Property:SetColor") local HouseNumber = LocalPlayer:WaitForChild("PlayersBag"):WaitForChild("HouseNumber") -- pcall-style: called (did the request itself run), then the server's own return values. local function invoke(name, ...) local args = table.pack(...) return pcall(function() return remote(name):InvokeServer(table.unpack(args, 1, args.n)) end) end local ownedCache = {} local function owns(key) if ownedCache[key] ~= nil then return ownedCache[key] end local pass = Gamepasses[key] if pass == nil then error("[Brookhaven Studio] unknown gamepass " .. key) end local ok, result = pcall(function() return MarketplaceService:UserOwnsGamePassAsync(LocalPlayer.UserId, Gamepasses.GetId(pass)) end) if not ok then warn("[Brookhaven Studio] couldn't check " .. key .. ": " .. tostring(result)) return false end ownedCache[key] = result == true return ownedCache[key] end -- Mirrors Brookhaven's VehicleSpeedUtil.GetMaxSpeedForPlayer. local function topSpeed() if owns("VEHICLE_SPEED_UNLOCKED") then return 200 end if owns("VEHICLE_UPGRADE") then return 100 end if owns("PREMIUM") then return 75 end return 55 end -- Best-feeling cars first. pass = the gamepass Brookhaven's catalog requires for it. local CARS = { { id = "Bugatti", label = "Bugatti", pass = "PREMIUM" }, { id = "Hypercar", label = "Hypercar", pass = "PREMIUM" }, { id = "Ferrari", label = "Ferrari", pass = "PREMIUM" }, { id = "PremiumRacer", label = "Premium Racer", pass = "PREMIUM" }, { id = "Formula1", label = "Formula 1", pass = "VEHICLE_PACK" }, { id = "SuperCar", label = "Super Car", pass = "VEHICLE_PACK" }, { id = "Lamboego", label = "Lambo", pass = "VEHICLE_PACK" }, { id = "Skyline", label = "Skyline", pass = "VEHICLE_PACK" }, { id = "CyberTruck", label = "Cybertruck", pass = "VEHICLE_PACK" }, { id = "BatMobile", label = "Batmobile", pass = "VIP" }, { id = "FordGT", label = "Ford GT" }, { id = "350Z", label = "350Z" }, { id = "Challenger", label = "Challenger" }, } local UNDERGLOWS = { "Scrolling Beams", "Scrolling 3 Colors", "Scrolling Split", "One Color", "Front Back", "XGlow", "ShockWave", "Futuristic" } local RGB_MODES = { "Rainbow", "Pastel", "Neon pulse", "Ocean", "Sunset", "Galaxy", "Fire", "Police" } -- Gradient modes: the color glides through these stops and loops back to the first. local RGB_PALETTES = { Ocean = { Color3.fromRGB(0, 90, 255), Color3.fromRGB(0, 200, 255), Color3.fromRGB(0, 255, 200), Color3.fromRGB(0, 140, 255) }, Sunset = { Color3.fromRGB(255, 94, 58), Color3.fromRGB(255, 149, 0), Color3.fromRGB(255, 59, 130), Color3.fromRGB(140, 60, 255) }, Galaxy = { Color3.fromRGB(120, 40, 255), Color3.fromRGB(255, 60, 200), Color3.fromRGB(40, 80, 255), Color3.fromRGB(180, 90, 255) }, Fire = { Color3.fromRGB(255, 30, 0), Color3.fromRGB(255, 120, 0), Color3.fromRGB(255, 200, 0), Color3.fromRGB(255, 80, 0) }, } local SWATCHES = { Color3.fromRGB(245, 245, 247), Color3.fromRGB(24, 24, 28), Color3.fromRGB(77, 201, 240), Color3.fromRGB(155, 120, 255), Color3.fromRGB(255, 120, 170), Color3.fromRGB(255, 196, 92), Color3.fromRGB(96, 214, 150), } -- Stunts rotate the car around one of its own axes (X = pitch, Y = yaw, Z = roll). -- Positive X turns = nose up and over the back; positive Z turns = rolling to the left. local STUNTS = { { name = "Backflip", axis = "X", turns = 1 }, { name = "Frontflip", axis = "X", turns = -1 }, { name = "Barrel roll left", axis = "Z", turns = 1 }, { name = "Barrel roll right", axis = "Z", turns = -1 }, { name = "360 spin", axis = "Y", turns = 1 }, { name = "Double backflip", axis = "X", turns = 2 }, { name = "Corkscrew", axis = "YZ", turns = 1 }, } --------------------------------------------------------------------------------------------- -- Settings (persisted in Solara's workspace folder) --------------------------------------------------------------------------------------------- local SETTINGS_FILE = "BrookhavenStudio.json" local Settings = { car = "Bugatti", autoTune = true, performance = true, accel = 2.0, steer = 1.4, handling = true, grip = true, tireGrip = 2.0, traction = 70, antiRoll = true, brakeAssist = true, brakeStrength = 1.2, autoFlip = false, rideHeight = 0, underglow = "Scrolling Beams", glowLength = 1.0, glowWidth = 1.3, rainbowGlow = false, carRGB = false, rgbMode = "Rainbow", rgbCycle = 6, stunt = "Backflip", stuntAirtime = 1.2, hopAirtime = 0.6, rpName = "", rpBio = "", rainbowName = false, antiAfk = true, speedo = true, lockTime = false, hour = 14, fullbright = false, fovOn = false, fov = 85, unlockZoom = false, houseRainbow = true, houseCycle = 20, loadBuild = false, buildSlot = "", airStunts = true, airTrick = "Backflip", airAlways = true, airMin = 0.25, landAssist = true, stuntTrail = true, drift = true, driftGrip = 0.35, autoLights = false, arraylist = true, toggleNotify = true, blur = true, binds = { Stunt = "X", ["Car Hop"] = "C", Drift = "Q" }, schema = 0, } local SETTINGS_SCHEMA = 4 if isfile and readfile and isfile(SETTINGS_FILE) then local ok, data = pcall(function() return HttpService:JSONDecode(readfile(SETTINGS_FILE)) end) if ok and type(data) == "table" then for key, value in pairs(data) do if Settings[key] ~= nil and type(value) == type(Settings[key]) then Settings[key] = value end end else warn("[Brookhaven Studio] settings file unreadable, using defaults") end end -- Older saves forced ride height level 1 on every car (it made cars scrape and flip), and -- predate some keybinds; move them over once. if Settings.schema < 3 then Settings.rideHeight = 0 end if Settings.schema < SETTINGS_SCHEMA then for name, key in pairs({ Stunt = "X", ["Car Hop"] = "C", Drift = "Q" }) do if Settings.binds[name] == nil then Settings.binds[name] = key end end Settings.schema = SETTINGS_SCHEMA end local alive = true local savePending = false local function saveSoon() if savePending or not writefile then return end savePending = true task.delay(0.8, function() savePending = false if alive then writefile(SETTINGS_FILE, HttpService:JSONEncode(Settings)) end end) end --------------------------------------------------------------------------------------------- -- Interface toolkit: the Snitch look (flat black box, white type, grey hierarchy, no color) --------------------------------------------------------------------------------------------- local T = { bg = Color3.fromRGB(11, 11, 12), side = Color3.fromRGB(14, 14, 16), raised = Color3.fromRGB(20, 20, 22), line = Color3.fromRGB(30, 30, 33), off = Color3.fromRGB(44, 44, 48), white = Color3.fromRGB(245, 245, 245), text = Color3.fromRGB(205, 205, 208), sub = Color3.fromRGB(120, 120, 124), dim = Color3.fromRGB(72, 72, 76), black = Color3.fromRGB(0, 0, 0), } local FONT = Enum.Font.BuilderSansMedium local FONT_BOLD = Enum.Font.BuilderSansBold local FONT_BLACK = Enum.Font.BuilderSansExtraBold local EASE = TweenInfo.new(0.16, Enum.EasingStyle.Quint, Enum.EasingDirection.Out) local connections = {} local function bind(signal, fn) local c = signal:Connect(fn) table.insert(connections, c) return c end local order = 0 local function nextOrder() order += 1 return order end local function make(className, props, children) local inst = Instance.new(className) for key, value in pairs(props or {}) do if key ~= "Parent" then inst[key] = value end end for _, child in ipairs(children or {}) do child.Parent = inst end inst.Parent = props and props.Parent return inst end local function tween(inst, props, info) local tw = TweenService:Create(inst, info or EASE, props) tw:Play() return tw end local function label(props) props.BackgroundTransparency = 1 props.Font = props.Font or FONT props.TextColor3 = props.TextColor3 or T.text props.TextXAlignment = props.TextXAlignment or Enum.TextXAlignment.Left return make("TextLabel", props) end local function corner(radius) return make("UICorner", { CornerRadius = radius and UDim.new(0, radius) or UDim.new(1, 0) }) end local function pad(left, right, top, bottom) return make("UIPadding", { PaddingLeft = UDim.new(0, left or 0), PaddingRight = UDim.new(0, right or 0), PaddingTop = UDim.new(0, top or 0), PaddingBottom = UDim.new(0, bottom or 0), }) end do local oldBlur = Lighting:FindFirstChild("BrookhavenStudioBlur") if oldBlur then oldBlur:Destroy() end end local Gui = make("ScreenGui", { Name = "BrookhavenStudio", ResetOnSpawn = false, IgnoreGuiInset = true, ZIndexBehavior = Enum.ZIndexBehavior.Sibling, DisplayOrder = 60, Parent = (gethui and gethui()) or game:GetService("CoreGui"), }) -- Everything is designed at 1100x720 and shrinks on smaller screens. local function computeScale() local viewport = workspace.CurrentCamera.ViewportSize return math.clamp(math.min(viewport.X / 1100, viewport.Y / 720), 0.6, 1) end local guiScale = computeScale() local scaleObjects = {} local function scaled(inst) table.insert(scaleObjects, make("UIScale", { Scale = guiScale, Parent = inst })) return inst end -- Notifications (bottom right). Fixed-width cards: title, detail line, draining timer line. -- Each sits in a slot whose height collapses on exit, so the stack reflows smoothly. -- Mark: solid white dot = done, hollow ring = refused or a problem, none = info. local TOAST_W, TOAST_H, TOAST_TIME = 316, 66, 4 local toastHolder = scaled(make("Frame", { AnchorPoint = Vector2.new(1, 1), Position = UDim2.new(1, -20, 1, -20), Size = UDim2.fromOffset(TOAST_W, 420), BackgroundTransparency = 1, Parent = Gui, }, { make("UIListLayout", { VerticalAlignment = Enum.VerticalAlignment.Bottom, SortOrder = Enum.SortOrder.LayoutOrder }), })) local toastCount = 0 local function notify(title, message, kind) print("[Brookhaven Studio] " .. title .. ": " .. message) if not alive then return end toastCount += 1 local slot = make("Frame", { LayoutOrder = toastCount, Size = UDim2.fromOffset(TOAST_W, 0), BackgroundTransparency = 1, Parent = toastHolder, }) local card = make("CanvasGroup", { AnchorPoint = Vector2.new(0, 1), Position = UDim2.new(0, 48, 1, 0), Size = UDim2.fromOffset(TOAST_W, TOAST_H), BackgroundColor3 = T.bg, BackgroundTransparency = 0.04, BorderSizePixel = 0, GroupTransparency = 1, Parent = slot, }, { corner(10), make("UIStroke", { Color = T.line, ApplyStrokeMode = Enum.ApplyStrokeMode.Border }), }) local textX = 18 if kind == "good" or kind == "bad" then local dot = make("Frame", { AnchorPoint = Vector2.new(0, 0.5), Position = UDim2.new(0, 18, 0.5, 0), Size = UDim2.fromOffset(8, 8), BackgroundColor3 = kind == "good" and T.white or T.bg, BorderSizePixel = 0, Parent = card, }, { corner() }) if kind == "bad" then make("UIStroke", { Color = T.white, Thickness = 1.5, Parent = dot }) end textX = 38 end label({ Text = title, Font = FONT_BOLD, TextSize = 16, TextColor3 = T.white, Position = UDim2.new(0, textX, 0, 14), Size = UDim2.new(1, -textX - 16, 0, 20), TextTruncate = Enum.TextTruncate.AtEnd, Parent = card, }) label({ Text = message, TextSize = 14, TextColor3 = T.sub, Position = UDim2.new(0, textX, 0, 34), Size = UDim2.new(1, -textX - 16, 0, 18), TextTruncate = Enum.TextTruncate.AtEnd, Parent = card, }) local timer = make("Frame", { AnchorPoint = Vector2.new(0, 1), Position = UDim2.new(0, 0, 1, 0), Size = UDim2.new(1, 0, 0, 2), BackgroundColor3 = T.white, BackgroundTransparency = 0.35, BorderSizePixel = 0, Parent = card, }) tween(slot, { Size = UDim2.fromOffset(TOAST_W, TOAST_H + 10) }, TweenInfo.new(0.3, Enum.EasingStyle.Quint, Enum.EasingDirection.Out)) tween(card, { Position = UDim2.new(0, 0, 1, 0), GroupTransparency = 0 }, TweenInfo.new(0.38, Enum.EasingStyle.Quint, Enum.EasingDirection.Out)) tween(timer, { Size = UDim2.new(0, 0, 0, 2) }, TweenInfo.new(TOAST_TIME, Enum.EasingStyle.Linear)) task.delay(TOAST_TIME, function() if not card.Parent then return end tween(card, { Position = UDim2.new(0, 48, 1, 0), GroupTransparency = 1 }, TweenInfo.new(0.3, Enum.EasingStyle.Quint, Enum.EasingDirection.In)) task.wait(0.28) if not slot.Parent then return end tween(slot, { Size = UDim2.fromOffset(TOAST_W, 0) }, TweenInfo.new(0.28, Enum.EasingStyle.Quint, Enum.EasingDirection.Out)) task.wait(0.3) slot:Destroy() end) end --------------------------------------------------------------------------------------------- -- Cars --------------------------------------------------------------------------------------------- local function vehiclesFolder() return workspace:FindFirstChild("Vehicles") end local function isMine(car) local owner = car and car:FindFirstChild("Owner") return owner ~= nil and owner:IsA("StringValue") and owner.Value == LocalPlayer.Name end local function myCar() local folder = vehiclesFolder() if not folder then return nil end for _, model in ipairs(folder:GetChildren()) do if isMine(model) then return model end end return nil end local function carFromPart(part) local folder = vehiclesFolder() if not folder then return nil end local node = part while node and node.Parent ~= folder do node = node.Parent end return node end -- Brookhaven paints the parts inside Body.Color; fall back to the car's configured default. local function currentPaint(car) local body = car:FindFirstChild("Body") local colorModel = body and body:FindFirstChild("Color") if colorModel then for _, part in ipairs(colorModel:GetDescendants()) do if part:IsA("BasePart") then return part.Color end end end local config = car:FindFirstChild("Config") local default = config and config:FindFirstChild("DefaultColor") return default and default.Value or nil end local Session = nil -- Your car's own paint, remembered per car so Car RGB can put it back after the car has been -- recolored for a while (driving or parked). local originalPaints = setmetatable({}, { __mode = "k" }) -- Your car, driven or parked. Parked cars are looked up at most once a second. local cachedCar, nextCarScan = nil, 0 local function ownCar() if Session then return Session.car end if cachedCar and cachedCar.Parent and isMine(cachedCar) then return cachedCar end cachedCar = nil if os.clock() >= nextCarScan then nextCarScan = os.clock() + 1 cachedCar = myCar() if cachedCar and originalPaints[cachedCar] == nil then originalPaints[cachedCar] = currentPaint(cachedCar) end end return cachedCar end -- Paint requests carry the car's id, so they work from inside or outside the car. local function paintCar(car, color) local called, ok, msg = invoke("SetColor", car:GetAttribute("VehicleUuid"), color) if not called then return false, tostring(ok) end return ok == true, msg end local tunedCars = setmetatable({}, { __mode = "k" }) local refreshModule -- set once the module system exists local setVehicleStatus -- Handling tweaks only go on regular 4-wheel cars (two steering wheels at the front, two -- driven wheels at the back). Bikes, boats and odd vehicles keep Brookhaven's stock physics, -- because car tuning on them causes wheelies and flips. local function accelFor(s) return (s.geom.valid and Settings.performance) and Settings.accel or 1 end local function steerFor() return Settings.performance and Settings.steer or 1 end local function applyPhysics() local s = Session if not s then return end local accel = accelFor(s) s.seat.Torque = s.baseTorque * accel s.seat.TurnSpeed = s.baseTurn * steerFor() local baseTurbo = s.turbo and tonumber(s.baseTurbo) if baseTurbo then s.turboExpected = string.format("%.3f", baseTurbo * accel) s.turbo.Value = s.turboExpected end end -- Tires: Brookhaven's "drift strength" is literally wheel friction (0 = ice, 1.5 = its max). -- While you drive, your client simulates the car, so we raise friction and its weight -- locally: the tire, not the road surface, then decides how much grip there is. Grip is -- capped per car (see analyzeCar) so tall vehicles can't out-grip their own balance and flip. local TIRE_FRICTION_WEIGHT = 10 local STOCK_FRICTION = 1 local function tireFriction(s) return math.min(Settings.tireGrip, math.max(s.geom.gripLimit, STOCK_FRICTION)) end local function tuningTires(s) return s.geom.valid and Settings.handling end -- The friction a wheel should have right now, or nil for the car's own tire. Drift Mode drops -- the rear wheels' grip while the drift key is held. local function wheelFriction(s, wheel) if s.drifting and s.rearWheels[wheel] then return Settings.driftGrip end return tuningTires(s) and tireFriction(s) or nil end local function applyTires() local s = Session if not s then return end for wheel, original in pairs(s.wheelProps) do if wheel.Parent then local friction = wheelFriction(s, wheel) if friction then wheel.CustomPhysicalProperties = PhysicalProperties.new( original.Density, friction, original.Elasticity, TIRE_FRICTION_WEIGHT, original.ElasticityWeight ) else wheel.CustomPhysicalProperties = original end end end end local groundParams = RaycastParams.new() groundParams.FilterType = Enum.RaycastFilterType.Exclude -- Brookhaven's own reference frame for a car is Chassis.Platform (its drive code measures -- speed along Platform.LookVector). Seats can be tilted or turned, so never use them for axes. local function carFrame(s) local platform = s.geom.platform if platform and platform.Parent then return platform.CFrame end return s.seat.CFrame end -- Measures the car: wheel layout, centre-of-mass height, and how much tire grip it can take -- before braking, accelerating or cornering would tip it over. local function analyzeCar(car, seat, root) local chassis = car:FindFirstChild("Chassis") local platform = chassis and chassis:FindFirstChild("Platform") if platform and not platform:IsA("BasePart") then platform = nil end local frame = platform and platform.CFrame or seat.CFrame local front, rear, all = {}, {}, {} for _, d in ipairs(car:GetDescendants()) do local steering = d:HasTag("WheelSteering") if steering or d:HasTag("Wheel") then local holder = d:FindFirstChild("AttachmentHolder") or d:FindFirstChild("PhysicalWheel") local physical = d:FindFirstChild("PhysicalWheel") if holder and holder:IsA("BasePart") then local wheel = { frameLocal = frame:PointToObjectSpace(holder.Position), radius = physical and physical:IsA("BasePart") and physical.Size.Y / 2 or 1, -- Bottom of the real tire (AttachmentHolder sits up at the suspension mount). contact = physical and physical:IsA("BasePart") and frame:PointToObjectSpace(physical.Position).Y - physical.Size.Y / 2 or nil, } table.insert(all, wheel) table.insert(steering and front or rear, wheel) end end end local extents = car:GetExtentsSize() local geom = { valid = false, platform = platform, wheels = #all, gripLimit = STOCK_FRICTION, probe = extents.Y + 3 } if #all < 4 or #front < 2 or #rear < 2 then return geom end local function averageOf(list) local sum = Vector3.zero for _, w in ipairs(list) do sum += w.frameLocal end return sum / #list end local frontLocal, rearLocal = averageOf(front), averageOf(rear) local minX, maxX, contactY, radius = math.huge, -math.huge, 0, 0 for _, w in ipairs(all) do minX = math.min(minX, w.frameLocal.X) maxX = math.max(maxX, w.frameLocal.X) contactY += w.frameLocal.Y - w.radius radius += w.radius end contactY /= #all radius /= #all local com = frame:PointToObjectSpace(root.AssemblyCenterOfMass) local cgHeight = math.max(com.Y - contactY, 0.25) local halfTrack = (maxX - minX) / 2 local frontDist = math.abs(com.Z - frontLocal.Z) local rearDist = math.abs(rearLocal.Z - com.Z) -- Static stability: once tire friction exceeds min(half track, axle distance) / CG height, -- the tires out-grip the car's balance and it flips. Anti-flip covers the margin above it. local stability = math.min(halfTrack, frontDist, rearDist) / cgHeight geom.valid = true geom.stability = stability geom.gripLimit = stability geom.halfTrack = halfTrack geom.frontDist = frontDist geom.rearDist = rearDist geom.cgHeight = cgHeight geom.wheelbase = frontDist + rearDist geom.radius = radius geom.probe = radius * 2 + 3 -- How high the root part rides above the tire contact patches. local contactSum, contactCount = 0, 0 for _, w in ipairs(all) do if w.contact then contactSum += w.contact contactCount += 1 end end if contactCount > 0 then geom.rootHeight = frame:PointToObjectSpace(root.Position).Y - contactSum / contactCount end -- Axle centres, relative to the root part so forces can be attached there. geom.frontRoot = root.CFrame:PointToObjectSpace(frame:PointToWorldSpace(frontLocal)) geom.rearRoot = root.CFrame:PointToObjectSpace(frame:PointToWorldSpace(rearLocal)) return geom end local function ensureHandlingParts(s, root) if s.root == root and s.force and s.force.Parent == root then return end for _, inst in ipairs({ s.force, s.attach, s.align, s.alignAttach, s.rearForce, s.rearAttach }) do inst:Destroy() end s.root = root local frame = carFrame(s) s.attach = make("Attachment", { Name = "BHS_Handling", Parent = root }) s.force = make("VectorForce", { Name = "BHS_Handling", Attachment0 = s.attach, RelativeTo = Enum.ActuatorRelativeTo.World, ApplyAtCenterOfMass = true, Force = Vector3.zero, Parent = root, }) -- Stability control: a sideways force applied at the rear axle. Cancelling the rear tires' -- slide there also creates the yaw torque that stops the tail swinging out (no spin-outs), -- without fighting the steering the way a force at the car's centre does. s.rearAttach = make("Attachment", { Name = "BHS_RearAxle", Position = s.geom.rearRoot, Parent = root }) s.rearForce = make("VectorForce", { Name = "BHS_RearAxle", Attachment0 = s.rearAttach, RelativeTo = Enum.ActuatorRelativeTo.World, ApplyAtCenterOfMass = false, Force = Vector3.zero, Parent = root, }) -- Anti-flip: aligns the car's up axis with the road; yaw (steering) stays free. Its torque -- is zero until the car tilts past normal body roll/pitch (see updateHandling). s.alignAttach = make("Attachment", { Name = "BHS_Stability", Parent = root }) s.alignAttach.WorldAxis = frame.UpVector s.alignAttach.WorldSecondaryAxis = frame.LookVector s.align = make("AlignOrientation", { Name = "BHS_Stability", Mode = Enum.OrientationAlignmentMode.OneAttachment, Attachment0 = s.alignAttach, AlignType = Enum.AlignType.PrimaryAxisParallel, PrimaryAxis = Vector3.yAxis, RigidityEnabled = false, Responsiveness = 15, MaxTorque = 0, MaxAngularVelocity = 6, Enabled = false, Parent = root, }) end local ANTI_FLIP_DEADZONE = 5 -- degrees of tilt allowed before it starts helping local ANTI_FLIP_RAMP = 10 -- degrees over which it ramps to full strength local function updateHandling(dt) local s = Session if not s or not s.geom.valid then return end local root = s.seat.AssemblyRootPart if not root then return end ensureHandlingParts(s, root) if s.stunting then -- The stunt controller owns the car's motion until it lands. s.force.Force = Vector3.zero s.rearForce.Force = Vector3.zero s.align.Enabled = false return end local frame = carFrame(s) local up, right, look = frame.UpVector, frame.RightVector, frame.LookVector local gravity = workspace.Gravity groundParams.FilterDescendantsInstances = { s.car, LocalPlayer.Character } local frontPoint = root.CFrame:PointToWorldSpace(s.geom.frontRoot) local rearPoint = root.CFrame:PointToWorldSpace(s.geom.rearRoot) local frontHit = workspace:Raycast(frontPoint, -up * s.geom.probe, groundParams) local rearHit = workspace:Raycast(rearPoint, -up * s.geom.probe, groundParams) local grounded = frontHit ~= nil or rearHit ~= nil local v = root.AssemblyLinearVelocity local force = Vector3.zero if Settings.handling and Settings.grip and grounded then -- Downforce that builds with speed, only while the wheels are on the road. local flatSpeed = Vector3.new(v.X, 0, v.Z).Magnitude force += Vector3.new(0, -root.AssemblyMass * gravity * math.clamp(flatSpeed / 120, 0, 1) * 0.5, 0) end if Settings.brakeAssist and grounded then -- Brake assist: when you press against the direction you're moving, add braking force, -- capped by how far this car's weight can shift before the rear lifts (no stoppies). local forward = v:Dot(look) local throttle = s.seat.ThrottleFloat if (forward > 4 and throttle < -0.1) or (forward < -4 and throttle > 0.1) then local balance = math.min(s.geom.frontDist, s.geom.rearDist) / s.geom.cgHeight local decel = gravity * math.min(Settings.brakeStrength, balance * 0.9) * math.abs(throttle) -- Never brake harder than it takes to stop this frame, so it can't push backwards. decel = math.min(decel, math.abs(forward) / math.max(dt, 1 / 240)) force += look * (forward > 0 and -1 or 1) * s.mass * decel end end s.force.Force = force if Settings.handling and rearHit and Settings.traction > 0 and not s.drifting then local lateral = root:GetVelocityAtPosition(rearPoint):Dot(right) -- Never push harder sideways than this car's balance allows (tall vehicles tip). local cap = s.mass * gravity * math.min(1.2, s.geom.stability) -- Gain up to 5/s keeps the correction stable even at 30 fps. local push = math.clamp(-lateral * s.mass * (Settings.traction / 100) * 5, -cap, cap) s.rearForce.Force = right * push else s.rearForce.Force = Vector3.zero end -- Target "up": no roll against world up, pitch following the road slope measured under the -- front and rear axles (not the normal of whatever face a single ray hits). local strength = 0 if Settings.handling and Settings.antiRoll and grounded then local roadForward = frontHit and rearHit and (frontHit.Position - rearHit.Position) or nil if not roadForward or roadForward.Magnitude < 0.5 or math.abs(roadForward.Unit.Y) > 0.6 then roadForward = Vector3.new(look.X, 0, look.Z) end if roadForward.Magnitude > 1e-3 then local sideways = roadForward.Unit:Cross(Vector3.yAxis) if sideways.Magnitude > 1e-3 then local targetUp = sideways.Unit:Cross(roadForward.Unit) local tilt = math.deg(math.acos(math.clamp(up:Dot(targetUp), -1, 1))) strength = math.clamp((tilt - ANTI_FLIP_DEADZONE) / ANTI_FLIP_RAMP, 0, 1) s.align.PrimaryAxis = targetUp end end end s.align.MaxTorque = s.flipTorque * strength s.align.Enabled = strength > 0 end -- Brookhaven marks one SuspensionHeights entry "isDefault"; its level is its sorted position. local function defaultRideLevel(car) local config = car:FindFirstChild("Config") local heights = config and config:FindFirstChild("SuspensionHeights") if not heights then return nil end local values, default = {}, nil for _, v in ipairs(heights:GetChildren()) do if v:IsA("NumberValue") then table.insert(values, v.Value) if v:GetAttribute("isDefault") == true then default = v.Value end end end table.sort(values) for i, value in ipairs(values) do if default and math.abs(value - default) < 0.001 then return i end end return nil end -- Requests that need you in the driver's seat. Returns success plus the server's message. local function carRequest(name, ...) if not Session then return false, "Get in your car first" end local called, ok, msg = invoke(name, ...) if not called then return false, tostring(ok) end return ok == true, msg end local function autoTune(s) task.wait(0.6) if Session ~= s then return end local parts = {} local speed = topSpeed() local called, ok, _, value = invoke("SetMaxSpeed", speed) if called and ok then table.insert(parts, "top speed " .. tostring(value or speed)) else table.insert(parts, "top speed refused") end if carRequest("SetTurboAppearance", 3) then table.insert(parts, "turbo 3") end if tuningTires(s) then -- Server-side grip too (Brookhaven caps it at 1.5), within this car's balance limit. local grip = tireFriction(s) if carRequest("SetDriftStrength", math.min(grip, 1.5)) then table.insert(parts, grip < Settings.tireGrip and string.format("grip %.1f (capped for balance)", grip) or string.format("grip %.1f", grip)) end elseif not s.geom.valid then table.insert(parts, "stock handling (not a 4-wheel car)") end -- 0 = leave the car's own ride height alone (forcing the lowest level made cars scrape). if Settings.rideHeight > 0 and carRequest("SetSuspensionLevel", Settings.rideHeight) then table.insert(parts, "ride " .. Settings.rideHeight) end if owns("VEHICLE_CUSTOMIZATION") and carRequest("SetUnderglow", Settings.underglow) then table.insert(parts, string.lower(Settings.underglow)) carRequest("SetUnderglowSize", Settings.glowLength, Settings.glowWidth) end if Session == s then s.ready = true end notify("Auto-tune", table.concat(parts, " · "), "good") end local function syncEffectModules() if refreshModule then refreshModule("Fire") refreshModule("Engine Smoke") end end local function refreshVehicleStatus() if not setVehicleStatus then return end if Session then local name = Session.car:GetAttribute("vehicleName") or Session.car.Name setVehicleStatus(name, Session.geom.valid and "" or "Stock handling", true) else local parked = myCar() if parked then setVehicleStatus(parked:GetAttribute("vehicleName") or parked.Name, "Parked", false) else setVehicleStatus("On foot", "No car out", false) end end end local function endSession() local s = Session if not s then return end Session = nil for _, c in ipairs(s.conns) do c:Disconnect() end if s.seat.Parent then s.seat.Torque = s.baseTorque s.seat.TurnSpeed = s.baseTurn end if s.turbo and s.turbo.Parent and s.baseTurbo then s.turbo.Value = s.baseTurbo end for wheel, original in pairs(s.wheelProps) do if wheel.Parent then wheel.CustomPhysicalProperties = original end end for _, inst in ipairs({ s.force, s.attach, s.align, s.alignAttach, s.rearForce, s.rearAttach }) do inst:Destroy() end syncEffectModules() end local function startSession(seat, car) local turbo = seat:FindFirstChild("Turbo") local s = { seat = seat, car = car, uuid = car:GetAttribute("VehicleUuid"), baseTorque = seat.Torque, baseTurn = seat.TurnSpeed, turbo = turbo and turbo:IsA("StringValue") and turbo or nil, baseTurbo = turbo and turbo:IsA("StringValue") and turbo.Value or nil, ready = false, stunting = false, drifting = false, airArmed = false, groundTime = 0, conns = {}, wheelProps = {}, rearWheels = {}, mass = 0, } for _, part in ipairs(car:GetDescendants()) do if part:IsA("BasePart") then if not part.Massless then s.mass += part:GetMass() end if part.Name == "PhysicalWheel" then s.wheelProps[part] = part.CustomPhysicalProperties or PhysicalProperties.new(part.Material) -- Brookhaven tags the driven (rear) wheel models "Wheel" and the front ones "WheelSteering". s.rearWheels[part] = part.Parent ~= nil and part.Parent:HasTag("Wheel") -- If the server re-sets friction (e.g. a grip change), keep its value as the restore -- point and put our tire setting back on top. table.insert(s.conns, part:GetPropertyChangedSignal("CustomPhysicalProperties"):Connect(function() local p = part.CustomPhysicalProperties local want = Session == s and wheelFriction(s, part) if want and p and (math.abs(p.Friction - want) > 0.01 or math.abs(p.FrictionWeight - TIRE_FRICTION_WEIGHT) > 0.01) then s.wheelProps[part] = p applyTires() end end)) end end end if originalPaints[car] == nil then originalPaints[car] = currentPaint(car) end s.geom = analyzeCar(car, seat, seat.AssemblyRootPart) -- How high the root part rides above the road, for telling when the car is in the air. do local root = seat.AssemblyRootPart groundParams.FilterDescendantsInstances = { car, LocalPlayer.Character } local below = workspace:Raycast(root.Position, Vector3.new(0, -30, 0), groundParams) s.clearance = math.clamp(s.geom.rootHeight or (below and root.Position.Y - below.Position.Y) or 2, 0.3, 15) end if s.geom.valid then s.flipTorque = s.mass * workspace.Gravity * math.max(s.geom.halfTrack, s.geom.wheelbase / 2) * 2 end Session = s -- The server can change these (e.g. a turbo upgrade); adopt its value as the new base. table.insert(s.conns, seat:GetPropertyChangedSignal("Torque"):Connect(function() if Session == s and math.abs(seat.Torque - s.baseTorque * accelFor(s)) > 0.5 then s.baseTorque = seat.Torque applyPhysics() end end)) table.insert(s.conns, seat:GetPropertyChangedSignal("TurnSpeed"):Connect(function() if Session == s and math.abs(seat.TurnSpeed - s.baseTurn * steerFor()) > 0.01 then s.baseTurn = seat.TurnSpeed applyPhysics() end end)) if s.turbo then table.insert(s.conns, s.turbo.Changed:Connect(function(newValue) if Session == s and newValue ~= s.turboExpected then s.baseTurbo = newValue applyPhysics() end end)) end table.insert(s.conns, car:GetAttributeChangedSignal("OnFire"):Connect(syncEffectModules)) table.insert(s.conns, car:GetAttributeChangedSignal("Smoking"):Connect(syncEffectModules)) table.insert(s.conns, RunService.Heartbeat:Connect(updateHandling)) applyPhysics() applyTires() syncEffectModules() if Settings.autoTune and not tunedCars[car] then tunedCars[car] = true task.spawn(autoTune, s) else s.ready = true end end local function onSeatChanged(humanoid) local seat = humanoid.SeatPart if Session and Session.seat ~= seat then endSession() end if seat and seat:IsA("VehicleSeat") and not Session then local car = carFromPart(seat) if car and isMine(car) then startSession(seat, car) end end refreshVehicleStatus() end local function hookCharacter(character) local humanoid = character:WaitForChild("Humanoid", 10) if not humanoid or not alive then return end bind(humanoid:GetPropertyChangedSignal("SeatPart"), function() onSeatChanged(humanoid) end) onSeatChanged(humanoid) end local spawning = false local function spawnCar() if spawning then return end local entry for _, car in ipairs(CARS) do if car.id == Settings.car then entry = car end end if not entry then notify("Spawn", "Pick a car first", "bad") return end local character = LocalPlayer.Character local humanoid = character and character:FindFirstChildOfClass("Humanoid") if not humanoid or humanoid.Health <= 0 then notify("Spawn", "Respawn first", "bad") return end if humanoid.SeatPart or humanoid.Sit then notify("Spawn", "Get out of your seat or vehicle first", "bad") return end spawning = true local before = myCar() CarEvent:FireServer("PickingCar", entry.id) local spawned local started = os.clock() repeat task.wait(0.2) local current = myCar() if current and current ~= before then spawned = current end until spawned or os.clock() - started > 8 or not alive spawning = false if spawned then notify("Spawn", entry.label .. " is out · hop in to apply your tune", "good") else notify("Spawn", "Didn't go through · wait for the cooldown or move off roads/water", "bad") end refreshVehicleStatus() end -- Fire and smoke are server toggles; compare with the car's real state so a dropped -- request can never leave them inverted. local function setCarEffect(attribute, remoteName, wanted) local s = Session if not s then notify(attribute == "OnFire" and "Fire" or "Engine Smoke", "Get in your car first", "bad") return end if (s.car:GetAttribute(attribute) == true) ~= wanted then local ok, msg = carRequest(remoteName) if not ok then notify("Effects", "Refused: " .. tostring(msg), "bad") end end task.delay(0.5, syncEffectModules) end --------------------------------------------------------------------------------------------- -- Stunts. The car's orientation follows a scripted path through a rigid AlignOrientation: it -- settles level, turns exactly the chosen number of times, then is held level until it touches -- down, so it always lands on its wheels facing the way it was going. -- Key stunts launch the car first; Air Stunts use the airtime of a jump you're already in. --------------------------------------------------------------------------------------------- local stuntBusy = false local stuntCount = 0 local HOP = { name = "Car Hop", axis = "X", turns = 0 } local LANDING = { name = "Landing assist", axis = "X", turns = 0 } local function stuntRotation(def, angle) if def.axis == "X" then return CFrame.fromAxisAngle(Vector3.xAxis, angle) elseif def.axis == "Y" then return CFrame.fromAxisAngle(Vector3.yAxis, angle) elseif def.axis == "Z" then return CFrame.fromAxisAngle(Vector3.zAxis, angle) end return CFrame.fromAxisAngle(Vector3.yAxis, angle) * CFrame.fromAxisAngle(Vector3.zAxis, angle) end local function findStunt(name) for _, def in ipairs(STUNTS) do if def.name == name then return def end end return STUNTS[1] end local function smoothstep(x) return x * x * (3 - 2 * x) end -- Wheels down = the root part is within its normal ride height of the ground. local function isGrounded(s, root) groundParams.FilterDescendantsInstances = { s.car, LocalPlayer.Character } return workspace:Raycast(root.Position, Vector3.new(0, -(s.clearance + 1.2), 0), groundParams) ~= nil end -- Seconds until the car comes down, from its height and vertical speed. The ground is checked -- straight below and where the car is heading, since a roof or a hill ahead lands it sooner. local function airtimeLeft(s, root) local gravity = workspace.Gravity local pos, v = root.Position, root.AssemblyLinearVelocity groundParams.FilterDescendantsInstances = { s.car, LocalPlayer.Character } local below = workspace:Raycast(pos, Vector3.new(0, -2000, 0), groundParams) if not below then return nil end local groundY = below.Position.Y local function timeToGround() local height = pos.Y - s.clearance - groundY if height <= 0 and v.Y <= 0 then return 0 end return (v.Y + math.sqrt(math.max(v.Y * v.Y + 2 * gravity * height, 0))) / gravity end local t = timeToGround() for _ = 1, 2 do local ahead = pos + Vector3.new(v.X, 0, v.Z) * t local hit = workspace:Raycast(Vector3.new(ahead.X, pos.Y + 4, ahead.Z), Vector3.new(0, -2000, 0), groundParams) if hit and hit.Position.Y > groundY then groundY = hit.Position.Y t = timeToGround() end end return t end -- Air needed to finish the trick and still be level before touchdown. local function airNeeded(def) local turns = math.abs(def.turns) return turns == 0 and 0.4 or 0.35 + 0.45 * turns end local function airRotateTime(def, left) local turns = math.abs(def.turns) return turns == 0 and 0 or math.min(left - 0.3, 0.85 * turns + 0.15) end -- Two white ribbons off the rear corners while the car is in the air. Created by your client, -- so only you see them. local function addTrails(s, root) local frame = carFrame(s) local right = root.CFrame:VectorToObjectSpace(frame.RightVector) local up = root.CFrame:VectorToObjectSpace(frame.UpVector) local rear = s.geom.valid and s.geom.rearRoot or root.CFrame:PointToObjectSpace(root.Position - frame.LookVector * 4) local half = s.geom.valid and s.geom.halfTrack or 2.5 local made = {} for _, side in ipairs({ -1, 1 }) do local base = rear + right * half * side local a0 = make("Attachment", { Name = "BHS_Trail", Position = base, Parent = root }) local a1 = make("Attachment", { Name = "BHS_Trail", Position = base + up * 0.7, Parent = root }) local trail = make("Trail", { Name = "BHS_Trail", Attachment0 = a0, Attachment1 = a1, Lifetime = 0.45, MinLength = 0.05, FaceCamera = true, LightEmission = 0.5, Color = ColorSequence.new(Color3.new(1, 1, 1)), Transparency = NumberSequence.new({ NumberSequenceKeypoint.new(0, 0.2), NumberSequenceKeypoint.new(1, 1) }), WidthScale = NumberSequence.new({ NumberSequenceKeypoint.new(0, 1), NumberSequenceKeypoint.new(1, 0.1) }), Parent = root, }) table.insert(made, trail) table.insert(made, a0) table.insert(made, a1) end return made end -- flight: expected seconds in the air; rotateTime: seconds the turns take; launch: upward -- speed to add first (0 when the car is already in the air). local function runStunt(s, def, flight, rotateTime, launch) local root = s.seat.AssemblyRootPart if stuntBusy or not root then return false end stuntBusy = true s.stunting = true local frame = carFrame(s) local flatLook = Vector3.new(frame.LookVector.X, 0, frame.LookVector.Z) if flatLook.Magnitude < 0.1 then -- Nose straight up or down: use the direction of travel instead. local v = root.AssemblyLinearVelocity flatLook = Vector3.new(v.X, 0, v.Z) if flatLook.Magnitude < 0.1 then flatLook = Vector3.new(0, 0, -1) end end local level = CFrame.lookAt(Vector3.zero, flatLook.Unit) local startRot = frame.Rotation local attach = make("Attachment", { Name = "BHS_Stunt", Parent = root }) attach.WorldCFrame = CFrame.new(root.Position) * frame.Rotation local align = make("AlignOrientation", { Name = "BHS_Stunt", Mode = Enum.OrientationAlignmentMode.OneAttachment, Attachment0 = attach, RigidityEnabled = true, CFrame = startRot, Parent = root, }) if launch > 0 then -- Lift every part of the car (wheels are separate physics assemblies) by the same amount. local lifted = {} for _, part in ipairs(s.car:GetDescendants()) do if part:IsA("BasePart") and not part.Anchored then local assembly = part.AssemblyRootPart if assembly and not lifted[assembly] then lifted[assembly] = true assembly.AssemblyLinearVelocity += Vector3.new(0, launch, 0) end end end end local trails = Settings.stuntTrail and addTrails(s, root) or {} local started = os.clock() local landedAt local conn local function finish() if conn then conn:Disconnect() end if root.Parent then local tilt = math.deg(math.acos(math.clamp(carFrame(s).UpVector.Y, -1, 1))) print(string.format("[Brookhaven Studio] %s landed: %.1f° tilt after %.2fs", def.name, tilt, os.clock() - started)) end if def.turns ~= 0 then stuntCount += 1 end align:Destroy() attach:Destroy() for _, inst in ipairs(trails) do if inst:IsA("Trail") then inst.Enabled = false end end task.delay(0.5, function() for _, inst in ipairs(trails) do inst:Destroy() end end) s.stunting = false stuntBusy = false end conn = RunService.Heartbeat:Connect(function() if Session ~= s or not root.Parent or not alive then finish() return end local t = os.clock() - started local base = startRot:Lerp(level, smoothstep(math.clamp(t / 0.2, 0, 1))) local progress = rotateTime > 0 and math.clamp(t / rotateTime, 0, 1) or 1 align.CFrame = base * stuntRotation(def, def.turns * 2 * math.pi * smoothstep(progress)) if progress >= 1 and t > flight * 0.5 then if not landedAt and isGrounded(s, root) then landedAt = t end -- Hold level briefly after touchdown so the suspension settles, then hand back. if (landedAt and t - landedAt > 0.2) or t > flight + 2 then finish() end end end) return true end -- Do the trick from mid-air. If the jump is too short for it, add just enough lift that the -- rest of the flight lasts as long as the trick needs. local function stuntFromAir(s, root, def, left) local need = airNeeded(def) if left >= need then return runStunt(s, def, left, airRotateTime(def, left), 0) end local gravity = workspace.Gravity local vy = root.AssemblyLinearVelocity.Y -- Height above the landing spot right now (from 0 = h + vy*t - g*t^2/2 at t = left). local height = gravity * left * left / 2 - vy * left local boost = (gravity * need * need / 2 - height) / need - vy return runStunt(s, def, need, airRotateTime(def, need), math.max(boost, 0)) end local function performStunt(def, airtime, title) local s = Session if not s then notify(title, "Get in your car first", "bad") return end if stuntBusy then return end local root = s.seat.AssemblyRootPart if not root then return end local turns = math.abs(def.turns) if isGrounded(s, root) then -- Enough airtime to finish the rotation comfortably before touchdown. airtime = math.max(airtime, 0.45 + 0.55 * turns) runStunt(s, def, airtime, turns > 0 and airtime * 0.8 or 0, workspace.Gravity * airtime / 2) return end -- Already in the air: use the airtime that's left, topped up if it's too short. local left = airtimeLeft(s, root) if left then stuntFromAir(s, root, def, left) else notify(title, "No ground below to land on", "bad") end end -- Air Stunts and Landing Assist: one decision per jump, made the moment the wheels leave the -- ground, from the predicted airtime. With Every Jump on, any jump longer than the minimum gets -- the trick (topped up with lift); shorter bumps and curbs never trigger anything. local function airMonitor(dt) local s = Session if not s or s.stunting then return end local root = s.seat.AssemblyRootPart if not root then return end if isGrounded(s, root) then s.groundTime += dt if s.groundTime > 0.25 then s.airArmed = true end return end s.groundTime = 0 if not s.airArmed then return end s.airArmed = false if not (Settings.airStunts or Settings.landAssist) then return end local left = airtimeLeft(s, root) if not left then return end if Settings.airStunts then local def = Settings.airTrick == "Random" and STUNTS[math.random(#STUNTS)] or findStunt(Settings.airTrick) if left >= airNeeded(def) or (Settings.airAlways and left >= Settings.airMin) then stuntFromAir(s, root, def, left) return end end if Settings.landAssist and left >= airNeeded(LANDING) then runStunt(s, LANDING, left, 0, 0) end end --------------------------------------------------------------------------------------------- -- RGB --------------------------------------------------------------------------------------------- -- Every mode is a continuous function of time, so the color glides instead of jumping. local function paletteColor(stops, phase) local scaledPhase = phase * #stops local index = math.floor(scaledPhase) local frac = scaledPhase - index local eased = frac * frac * (3 - 2 * frac) local from = stops[index % #stops + 1] local to = stops[(index + 1) % #stops + 1] return from:Lerp(to, eased) end local function rgbColor(mode, t, cycle) local phase = (t % cycle) / cycle if mode == "Pastel" then return Color3.fromHSV(phase, 0.42, 1) elseif mode == "Neon pulse" then local beat = 0.5 + 0.5 * math.sin(t * math.pi * 2 / math.max(cycle / 4, 0.5)) return Color3.fromHSV(phase, 1, 0.35 + 0.65 * beat) elseif mode == "Police" then local sweep = 0.5 + 0.5 * math.sin(t * math.pi * 2 / 0.9) return Color3.fromRGB(255, 40, 40):Lerp(Color3.fromRGB(40, 90, 255), sweep) elseif RGB_PALETTES[mode] then return paletteColor(RGB_PALETTES[mode], phase) end return Color3.fromHSV(phase, 1, 1) end -- What the underglow shows: the paint color when Car RGB is on, otherwise its own rainbow. local function glowColor(t) if Settings.carRGB then return rgbColor(Settings.rgbMode, t, Settings.rgbCycle) end return rgbColor("Rainbow", t, Settings.rgbCycle) end local function paintParts(car) local body = car:FindFirstChild("Body") local colorModel = body and body:FindFirstChild("Color") local parts = {} if colorModel then for _, d in ipairs(colorModel:GetDescendants()) do if d:IsA("BasePart") then table.insert(parts, d) end end end return parts end local function glowObjects(car) local body = car:FindFirstChild("Body") local collide = body and body:FindFirstChild("VehicleCollide") local objects = {} if collide then for _, d in ipairs(collide:GetDescendants()) do if d.Name == "ColorPrimary" and (d:IsA("Beam") or d:IsA("Light")) then table.insert(objects, d) end end end return objects end local function restorePaint() local car = ownCar() local paint = car and originalPaints[car] if paint then task.wait(0.4) -- let any in-flight RGB step land first if not Settings.carRGB and car.Parent then paintCar(car, paint) end end end -- Single entry point for Car RGB so the module, swatches and console stay in sync. local function setCarRGB(v) Settings.carRGB = v saveSoon() if refreshModule then refreshModule("Car RGB") end if not v then task.spawn(restorePaint) end end --------------------------------------------------------------------------------------------- -- House --------------------------------------------------------------------------------------------- local function setHouseColor(color) PropertySetColor:FireServer(color) HouseEvent:FireServer("ColorPickHouse", color) end local function loadBuild(slot) if HouseNumber.Value <= 0 then notify("Saved Props", "Claim a house first, then load props", "bad") return end if slot == "" then notify("Saved Props", "Pick a saved build first", "bad") return end local called, result, message = invoke("HS_PropBuildLoad", slot, "MainButton") print("[Brookhaven Studio] HS_PropBuildLoad ->", called, result, message) if not called then notify("Saved Props", "Didn't load: " .. tostring(result), "bad") elseif result == false then notify("Saved Props", "Server refused the build" .. (message and (": " .. tostring(message)) or ""), "bad") else notify("Saved Props", "Props requested from save " .. slot, "good") end end local function fetchSaves() local called, saves = invoke("HS_HouseSaves") if not called or type(saves) ~= "table" then return nil, tostring(saves) end local options = {} for slot, data in pairs(saves) do table.insert(options, { value = slot, label = string.format("%s · %d props", tostring(data.Name or slot), tonumber(data.PropCount) or 0), }) end table.sort(options, function(a, b) return a.value < b.value end) return options end local function houseColorAt(t) return Color3.fromHSV((t % Settings.houseCycle) / Settings.houseCycle, 0.72, 1) end local function colorClose(a, b) return math.abs(a.R - b.R) + math.abs(a.G - b.G) + math.abs(a.B - b.B) < 0.04 end local housePaint = { parts = {}, recent = {}, refreshAt = 0 } -- The painted parts are the ones in your lot currently showing a color we sent (or the color -- we're drawing locally), so this matches exactly what the server recolors. local function collectHouseParts() local lots = workspace:FindFirstChild("001_Lots") local lot = lots and lots:FindFirstChild(LocalPlayer.Name .. "House") local found = {} if not lot then return found end local candidates = table.clone(housePaint.recent) table.insert(candidates, houseColorAt(os.clock())) for _, d in ipairs(lot:GetDescendants()) do if d:IsA("BasePart") then for _, color in ipairs(candidates) do if colorClose(d.Color, color) then table.insert(found, d) break end end end end return found end --------------------------------------------------------------------------------------------- -- Player and world (lighting, FOV and zoom are local to your screen) --------------------------------------------------------------------------------------------- local LIGHTING_KEYS = { "ClockTime", "Brightness", "Ambient", "OutdoorAmbient", "GlobalShadows", "FogEnd" } local savedLighting = nil local function syncLightingSnapshot() local active = Settings.lockTime or Settings.fullbright if active and not savedLighting then savedLighting = {} for _, key in ipairs(LIGHTING_KEYS) do savedLighting[key] = Lighting[key] end elseif not active and savedLighting then for key, value in pairs(savedLighting) do Lighting[key] = value end savedLighting = nil end end local savedFov local function syncFov() local camera = workspace.CurrentCamera if Settings.fovOn and not savedFov then savedFov = camera.FieldOfView elseif not Settings.fovOn and savedFov then camera.FieldOfView = savedFov savedFov = nil end end local savedZoom local function syncZoom() if Settings.unlockZoom and not savedZoom then savedZoom = LocalPlayer.CameraMaxZoomDistance LocalPlayer.CameraMaxZoomDistance = 1000 elseif not Settings.unlockZoom and savedZoom then LocalPlayer.CameraMaxZoomDistance = savedZoom savedZoom = nil end end RunService:BindToRenderStep("BrookhavenStudioView", Enum.RenderPriority.Last.Value, function() if Settings.fovOn then workspace.CurrentCamera.FieldOfView = Settings.fov end if not savedLighting then return end if Settings.lockTime then Lighting.ClockTime = Settings.hour elseif Lighting.ClockTime ~= savedLighting.ClockTime then savedLighting.ClockTime = Lighting.ClockTime end if Settings.fullbright then Lighting.Brightness = 2 Lighting.Ambient = Color3.fromRGB(178, 178, 178) Lighting.OutdoorAmbient = Color3.fromRGB(178, 178, 178) Lighting.GlobalShadows = false Lighting.FogEnd = 1e6 end end) bind(LocalPlayer.Idled, function() if Settings.antiAfk then VirtualUser:CaptureController() VirtualUser:ClickButton2(Vector2.new()) end end) --------------------------------------------------------------------------------------------- -- Menu: one flat box (RightShift). Sidebar tabs on the left, the page on the right. --------------------------------------------------------------------------------------------- local BOX_W, BOX_H = 780, 540 local SIDEBAR_W = 200 local HEADER_H = 58 local TAB_H, TAB_GAP, TAB_TOP = 44, 4, 12 local PAD = 22 local GLIDE = TweenInfo.new(0.32, Enum.EasingStyle.Quint, Enum.EasingDirection.Out) local menuOpen = false -- Frees the mouse from first person / shift lock while the menu is open. local modal = make("TextButton", { Text = "", Size = UDim2.fromOffset(0, 0), BackgroundTransparency = 1, Modal = false, Visible = false, Parent = Gui, }) local box = make("CanvasGroup", { AnchorPoint = Vector2.new(0.5, 0.5), Position = UDim2.fromScale(0.5, 0.5), Size = UDim2.fromOffset(BOX_W, BOX_H), BackgroundColor3 = T.bg, BorderSizePixel = 0, GroupTransparency = 1, Visible = false, Parent = Gui, }) local basePos = box.Position local boxScale = make("UIScale", { Scale = guiScale * 0.94, Parent = box }) local menuBlur = make("BlurEffect", { Name = "BrookhavenStudioBlur", Size = 0, Parent = Lighting }) local function hline(parent, y) return make("Frame", { Position = UDim2.new(0, 0, 0, y), Size = UDim2.new(1, 0, 0, 1), BackgroundColor3 = T.line, BorderSizePixel = 0, Parent = parent, }) end -- Header (drag handle), full width. local header = make("Frame", { Size = UDim2.new(1, 0, 0, HEADER_H), BackgroundTransparency = 1, Parent = box, }, { pad(24, 24) }) label({ Text = "BROOKHAVEN STUDIO by Obsidian Works", RichText = true, Font = FONT_BLACK, TextSize = 20, TextColor3 = T.white, Size = UDim2.fromScale(0.75, 1), Parent = header, }) label({ Text = "RShift to close", TextSize = 13, TextColor3 = T.dim, AnchorPoint = Vector2.new(1, 0), Position = UDim2.fromScale(1, 0), Size = UDim2.fromScale(0.25, 1), TextXAlignment = Enum.TextXAlignment.Right, Parent = header, }) hline(box, HEADER_H) -- Sidebar: tabs on top, your vehicle card and Unload at the bottom. local sidebar = make("Frame", { Position = UDim2.fromOffset(0, HEADER_H + 1), Size = UDim2.new(0, SIDEBAR_W, 1, -(HEADER_H + 1)), BackgroundColor3 = T.side, BorderSizePixel = 0, Parent = box, }) make("Frame", { AnchorPoint = Vector2.new(1, 0), Position = UDim2.fromScale(1, 0), Size = UDim2.new(0, 1, 1, 0), BackgroundColor3 = T.line, BorderSizePixel = 0, Parent = sidebar, }) -- One highlight and indicator that glide to the active tab. local tabHighlight = make("Frame", { Position = UDim2.fromOffset(10, TAB_TOP), Size = UDim2.new(1, -21, 0, TAB_H), BackgroundColor3 = T.raised, BorderSizePixel = 0, Parent = sidebar, }, { corner(6) }) local tabIndicator = make("Frame", { Position = UDim2.fromOffset(10, TAB_TOP + (TAB_H - 18) / 2), Size = UDim2.fromOffset(3, 18), BackgroundColor3 = T.white, BorderSizePixel = 0, ZIndex = 2, Parent = sidebar, }, { corner() }) local tabList = make("Frame", { Size = UDim2.new(1, -1, 0, 0), AutomaticSize = Enum.AutomaticSize.Y, BackgroundTransparency = 1, ZIndex = 3, Parent = sidebar, }, { make("UIListLayout", { SortOrder = Enum.SortOrder.LayoutOrder, Padding = UDim.new(0, TAB_GAP) }), pad(10, 10, TAB_TOP, 0), }) local cardName, cardLine, cardStroke local unloadHolder do local sideBottom = make("Frame", { AnchorPoint = Vector2.new(0, 1), Position = UDim2.new(0, 0, 1, 0), Size = UDim2.new(1, -1, 0, 156), BackgroundTransparency = 1, Parent = sidebar, }, { pad(18, 18, 0, 16) }) make("Frame", { Position = UDim2.fromOffset(-18, 0), Size = UDim2.new(1, 36, 0, 1), BackgroundColor3 = T.line, BorderSizePixel = 0, Parent = sideBottom, }) label({ Text = "VEHICLE", Font = FONT_BOLD, TextSize = 12, TextColor3 = T.sub, Position = UDim2.fromOffset(0, 16), Size = UDim2.new(1, 0, 0, 14), Parent = sideBottom, }) local ring = make("Frame", { Position = UDim2.fromOffset(0, 36), Size = UDim2.fromOffset(42, 42), BackgroundColor3 = T.raised, BorderSizePixel = 0, Parent = sideBottom, }, { corner() }) cardStroke = make("UIStroke", { Color = T.line, Thickness = 1.5, Parent = ring }) make("ImageLabel", { Size = UDim2.fromScale(1, 1), BackgroundTransparency = 1, Image = "rbxthumb://type=AvatarHeadShot&id=" .. LocalPlayer.UserId .. "&w=150&h=150", Parent = ring, }, { corner() }) cardName = label({ Text = "On foot", Font = FONT_BLACK, TextSize = 21, TextColor3 = T.dim, Position = UDim2.fromOffset(52, 34), Size = UDim2.new(1, -52, 0, 24), TextTruncate = Enum.TextTruncate.AtEnd, Parent = sideBottom, }) cardLine = label({ Text = "", TextSize = 14, TextColor3 = T.sub, Position = UDim2.fromOffset(52, 60), Size = UDim2.new(1, -52, 0, 18), TextTruncate = Enum.TextTruncate.AtEnd, Parent = sideBottom, }) unloadHolder = make("Frame", { AnchorPoint = Vector2.new(0, 1), Position = UDim2.new(0, 0, 1, 0), Size = UDim2.new(1, 0, 0, 40), BackgroundTransparency = 1, Parent = sideBottom, }) end -- While you drive, the card's second line shows live speed and your stunt count. local cardNote, cardActive = "", false setVehicleStatus = function(name, note, active) cardName.Text = name cardNote = note cardActive = active cardName.TextColor3 = active and T.white or T.dim cardStroke.Color = active and T.white or T.line if not active then cardLine.Text = note end end -- Content: page title, subtitle and the scrolling page. local CONTENT_POS = UDim2.fromOffset(SIDEBAR_W, HEADER_H + 1) local content = make("CanvasGroup", { Position = CONTENT_POS, Size = UDim2.new(1, -SIDEBAR_W, 1, -(HEADER_H + 1)), BackgroundTransparency = 1, Parent = box, }) local pageTitle = label({ Text = "", Font = FONT_BLACK, TextSize = 30, TextColor3 = T.white, Position = UDim2.fromOffset(PAD, 20), Size = UDim2.new(1, -PAD * 2, 0, 34), Parent = content, }) local pageSub = label({ Text = "", TextSize = 15, TextColor3 = T.sub, Position = UDim2.fromOffset(PAD, 56), Size = UDim2.new(1, -PAD * 2, 0, 18), Parent = content, }) local scroller = make("ScrollingFrame", { Position = UDim2.fromOffset(0, 88), Size = UDim2.new(1, 0, 1, -88), BackgroundTransparency = 1, BorderSizePixel = 0, CanvasSize = UDim2.new(), AutomaticCanvasSize = Enum.AutomaticSize.Y, ScrollBarThickness = 2, ScrollBarImageColor3 = T.dim, ScrollingDirection = Enum.ScrollingDirection.Y, Parent = content, }, { pad(0, 0, 0, 12) }) local TABS = { "Vehicle", "Stunts", "Visuals", "Player", "House", "Settings" } local TAB_SUB = { Vehicle = "Spawn, tune and drive", Stunts = "Tricks on a key or in mid-air", Visuals = "Paint, glow and camera", Player = "Your roleplay name and session", House = "Color and saved props", Settings = "Interface and keybinds", } local pages, tabButtons = {}, {} local activeTab local function selectTab(name, instant) if name == activeTab then return end activeTab = name pageTitle.Text = name pageSub.Text = TAB_SUB[name] scroller.CanvasPosition = Vector2.zero for i, tabName in ipairs(TABS) do local on = tabName == name local tab = tabButtons[tabName] pages[tabName].Visible = on tab.label.Font = on and FONT_BOLD or FONT tween(tab.label, { TextColor3 = on and T.white or T.sub, Position = UDim2.fromOffset(on and 20 or 16, 0) }) if on then local y = TAB_TOP + (i - 1) * (TAB_H + TAB_GAP) local info = instant and TweenInfo.new(0) or GLIDE tween(tabHighlight, { Position = UDim2.fromOffset(10, y) }, info) tween(tabIndicator, { Position = UDim2.fromOffset(10, y + (TAB_H - 18) / 2) }, info) end end if not instant then content.GroupTransparency = 1 content.Position = CONTENT_POS + UDim2.fromOffset(16, 0) tween(content, { GroupTransparency = 0, Position = CONTENT_POS }, GLIDE) end end for i, tabName in ipairs(TABS) do pages[tabName] = make("Frame", { Size = UDim2.new(1, 0, 0, 0), AutomaticSize = Enum.AutomaticSize.Y, BackgroundTransparency = 1, Visible = false, Parent = scroller, }, { make("UIListLayout", { SortOrder = Enum.SortOrder.LayoutOrder }) }) local btn = make("TextButton", { LayoutOrder = i, Text = "", AutoButtonColor = false, Size = UDim2.new(1, 0, 0, TAB_H), BackgroundTransparency = 1, BorderSizePixel = 0, ZIndex = 3, Parent = tabList, }) local text = label({ Text = tabName, TextSize = 16, TextColor3 = T.sub, Position = UDim2.fromOffset(16, 0), Size = UDim2.new(1, -20, 1, 0), ZIndex = 3, Parent = btn, }) bind(btn.MouseEnter, function() if activeTab ~= tabName then tween(text, { TextColor3 = T.text }) end end) bind(btn.MouseLeave, function() if activeTab ~= tabName then tween(text, { TextColor3 = T.sub }) end end) bind(btn.MouseButton1Click, function() selectTab(tabName) end) tabButtons[tabName] = { btn = btn, label = text } end -- Rows --------------------------------------------------------------------------------------- local Modules, ModuleByName = {}, {} local bindingKey -- keybind waiting for a key press local keyRows = {} local function renderKeyRows() for _, render in pairs(keyRows) do render() end end local carPicker, buildPicker, passNote local unloadRef -- The row builders and pages live in their own scope (Luau allows 200 locals per function). do local current -- page rows are added to local function runSafely(title, fn) task.spawn(function() local ok, err = xpcall(fn, debug.traceback) if not ok then warn(err) notify(title, "Something broke: " .. tostring(err):match("^[^\n]*"), "bad") end end) end local function hoverRow(btn) bind(btn.MouseEnter, function() tween(btn, { BackgroundTransparency = 0 }) end) bind(btn.MouseLeave, function() tween(btn, { BackgroundTransparency = 1 }) end) end -- Name on the left; an optional dim hint line under it. local function rowText(parent, text, hint, rightSpace) local nameLabel = label({ Text = text, TextSize = 17, Position = UDim2.fromOffset(0, hint and 10 or 0), Size = UDim2.new(1, -rightSpace, 0, hint and 22 or 46), TextTruncate = Enum.TextTruncate.AtEnd, Parent = parent, }) if hint then label({ Text = hint, TextSize = 13, TextColor3 = T.dim, Position = UDim2.fromOffset(0, 32), Size = UDim2.new(1, -rightSpace, 0, 16), TextTruncate = Enum.TextTruncate.AtEnd, Parent = parent, }) end return nameLabel end local function sectionHeader(text) local row = make("Frame", { LayoutOrder = nextOrder(), Size = UDim2.new(1, 0, 0, 34), BackgroundTransparency = 1, Parent = current, }, { pad(PAD, PAD) }) local title = label({ Text = string.upper(text), Font = FONT_BOLD, TextSize = 12, TextColor3 = T.sub, Size = UDim2.fromScale(1, 1), TextYAlignment = Enum.TextYAlignment.Bottom, Parent = row, }) pad(0, 0, 0, 4).Parent = title end -- def: { name, get, set, hint?, suffix?, array? (false = keep off the HUD list), quiet? } local function toggleRow(def) local btn = make("TextButton", { LayoutOrder = nextOrder(), Text = "", AutoButtonColor = false, Size = UDim2.new(1, 0, 0, def.hint and 60 or 46), BackgroundColor3 = T.raised, BackgroundTransparency = 1, BorderSizePixel = 0, Parent = current, }, { pad(PAD, PAD) }) local nameLabel = rowText(btn, def.name, def.hint, 56) local track = make("Frame", { AnchorPoint = Vector2.new(1, 0.5), Position = UDim2.new(1, 0, 0.5, 0), Size = UDim2.fromOffset(40, 22), BackgroundColor3 = T.off, BorderSizePixel = 0, Parent = btn, }, { corner() }) local knob = make("Frame", { AnchorPoint = Vector2.new(0, 0.5), Position = UDim2.new(0, 3, 0.5, 0), Size = UDim2.fromOffset(16, 16), BackgroundColor3 = T.sub, BorderSizePixel = 0, Parent = track, }, { corner() }) local m = { def = def, name = def.name } function m.render(instant) local on = def.get() == true local info = instant and TweenInfo.new(0) or EASE tween(nameLabel, { TextColor3 = on and T.white or T.text }, info) tween(track, { BackgroundColor3 = on and T.white or T.off }, info) tween(knob, { Position = on and UDim2.new(1, -19, 0.5, 0) or UDim2.new(0, 3, 0.5, 0), BackgroundColor3 = on and T.black or T.sub, }, info) end hoverRow(btn) bind(btn.MouseButton1Click, function() local enabled = def.get() ~= true def.set(enabled) saveSoon() m.render() if Settings.toggleNotify and not def.quiet then notify(def.name, enabled and "Enabled" or "Disabled", enabled and "good" or nil) end end) m.render(true) table.insert(Modules, m) ModuleByName[def.name] = m return m end local function setting(name, key, extra) local def = { name = name, get = function() return Settings[key] end, set = function(v) Settings[key] = v end, } for k, v in pairs(extra or {}) do def[k] = v end return def end local function sliderRow(text, min, max, step, initial, format, onChange, onRelease) local row = make("Frame", { LayoutOrder = nextOrder(), Size = UDim2.new(1, 0, 0, 62), BackgroundTransparency = 1, Parent = current, }, { pad(PAD, PAD) }) label({ Text = text, TextSize = 16, TextColor3 = T.sub, Size = UDim2.new(1, -90, 0, 32), Parent = row }) local valueText = label({ Font = FONT_BOLD, TextSize = 16, TextColor3 = T.white, AnchorPoint = Vector2.new(1, 0), Position = UDim2.fromScale(1, 0), Size = UDim2.fromOffset(90, 32), TextXAlignment = Enum.TextXAlignment.Right, Parent = row, }) -- Tall invisible hit area around the thin track. local hit = make("TextButton", { Text = "", AutoButtonColor = false, Position = UDim2.fromOffset(0, 34), Size = UDim2.new(1, 0, 0, 18), BackgroundTransparency = 1, Parent = row, }) local track = make("Frame", { AnchorPoint = Vector2.new(0, 0.5), Position = UDim2.fromScale(0, 0.5), Size = UDim2.new(1, 0, 0, 4), BackgroundColor3 = T.off, BorderSizePixel = 0, Parent = hit, }, { corner() }) local fill = make("Frame", { Size = UDim2.fromScale(0, 1), BackgroundColor3 = T.white, BorderSizePixel = 0, Parent = track }, { corner() }) local knob = make("Frame", { AnchorPoint = Vector2.new(0.5, 0.5), Position = UDim2.fromScale(0, 0.5), Size = UDim2.fromOffset(16, 16), BackgroundColor3 = T.white, BorderSizePixel = 0, Parent = track, }, { corner() }) local value = math.clamp(initial, min, max) local function render() local a = (value - min) / (max - min) fill.Size = UDim2.fromScale(a, 1) knob.Position = UDim2.fromScale(a, 0.5) valueText.Text = format(value) end local control = { set = function(v) value = math.clamp(v, min, max) render() onChange(value) end, } local held = false local function setFromX(x) local a = math.clamp((x - track.AbsolutePosition.X) / track.AbsoluteSize.X, 0, 1) local v = math.clamp(math.floor((min + a * (max - min)) / step + 0.5) * step, min, max) if math.abs(v - value) > step / 2 then value = v render() onChange(v) end end local function isPress(input) return input.UserInputType == Enum.UserInputType.MouseButton1 or input.UserInputType == Enum.UserInputType.Touch end bind(hit.InputBegan, function(input) if isPress(input) then held = true setFromX(input.Position.X) end end) bind(UserInputService.InputChanged, function(input) if held and (input.UserInputType == Enum.UserInputType.MouseMovement or input.UserInputType == Enum.UserInputType.Touch) then setFromX(input.Position.X) end end) bind(UserInputService.InputEnded, function(input) if held and isPress(input) then held = false saveSoon() if onRelease then runSafely(text, function() onRelease(value) end) end end end) render() return control end -- A value on the right; click to open the options underneath. local function choiceRow(text, initial, onChange) local btn = make("TextButton", { LayoutOrder = nextOrder(), Text = "", AutoButtonColor = false, Size = UDim2.new(1, 0, 0, 46), BackgroundColor3 = T.raised, BackgroundTransparency = 1, BorderSizePixel = 0, Parent = current, }, { pad(PAD, PAD) }) label({ Text = text, TextSize = 17, Size = UDim2.new(0.45, 0, 1, 0), Parent = btn }) local valueText = label({ Font = FONT_BOLD, TextSize = 16, TextColor3 = T.white, AnchorPoint = Vector2.new(1, 0), Position = UDim2.new(1, -20, 0, 0), Size = UDim2.new(0.55, -20, 1, 0), TextXAlignment = Enum.TextXAlignment.Right, TextTruncate = Enum.TextTruncate.AtEnd, Parent = btn, }) local arrow = label({ Text = "›", Font = FONT_BOLD, TextSize = 22, TextColor3 = T.sub, AnchorPoint = Vector2.new(1, 0.5), Position = UDim2.new(1, 0, 0.5, -2), Size = UDim2.fromOffset(12, 22), TextXAlignment = Enum.TextXAlignment.Center, Parent = btn, }) local list = make("Frame", { LayoutOrder = nextOrder(), Size = UDim2.new(1, 0, 0, 0), AutomaticSize = Enum.AutomaticSize.Y, BackgroundColor3 = T.side, BorderSizePixel = 0, Visible = false, Parent = current, }, { make("UIListLayout", { SortOrder = Enum.SortOrder.LayoutOrder }), pad(0, 0, 4, 4), }) hoverRow(btn) local selected = initial local options = {} local function setOpen(open) list.Visible = open tween(arrow, { Rotation = open and 90 or 0 }) end local function render() for _, child in ipairs(list:GetChildren()) do if child:IsA("TextButton") then child:Destroy() end end local selectedLabel for i, option in ipairs(options) do local isSelected = option.value == selected if isSelected then selectedLabel = option.label end local b = make("TextButton", { LayoutOrder = i, Text = "", AutoButtonColor = false, Size = UDim2.new(1, 0, 0, 34), BackgroundColor3 = T.raised, BackgroundTransparency = 1, BorderSizePixel = 0, Parent = list, }, { pad(PAD + 14, PAD) }) label({ Text = option.label, Font = isSelected and FONT_BOLD or FONT, TextSize = 15, TextColor3 = isSelected and T.white or T.sub, Size = UDim2.fromScale(1, 1), Parent = b, }) if isSelected then make("Frame", { AnchorPoint = Vector2.new(0, 0.5), Position = UDim2.new(0, -12, 0.5, 0), Size = UDim2.fromOffset(3, 14), BackgroundColor3 = T.white, BorderSizePixel = 0, Parent = b, }, { corner() }) end hoverRow(b) bind(b.MouseButton1Click, function() selected = option.value setOpen(false) render() onChange(option.value) saveSoon() end) end valueText.Text = selectedLabel or (#options > 0 and "Choose" or "Loading…") end bind(btn.MouseButton1Click, function() if #options > 0 then setOpen(not list.Visible) end end) render() return { setOptions = function(newOptions, newSelected) options = newOptions if newSelected ~= nil then selected = newSelected end render() end, } end local function namesToOptions(names) local options = {} for _, name in ipairs(names) do table.insert(options, { value = name, label = name }) end return options end -- A pill button on the right, styled like Unload: white on hover. local function actionRow(text, buttonText, fn, hint) local row = make("Frame", { LayoutOrder = nextOrder(), Size = UDim2.new(1, 0, 0, hint and 60 or 46), BackgroundTransparency = 1, Parent = current, }, { pad(PAD, PAD) }) rowText(row, text, hint, 130) local button = make("TextButton", { Text = buttonText, Font = FONT_BOLD, TextSize = 14, TextColor3 = T.text, AutoButtonColor = false, AnchorPoint = Vector2.new(1, 0.5), Position = UDim2.new(1, 0, 0.5, 0), Size = UDim2.fromOffset(0, 30), AutomaticSize = Enum.AutomaticSize.X, BackgroundColor3 = T.raised, BorderSizePixel = 0, Parent = row, }, { corner(6), pad(16, 16) }) bind(button.MouseEnter, function() tween(button, { BackgroundColor3 = T.white, TextColor3 = T.black }) end) bind(button.MouseLeave, function() tween(button, { BackgroundColor3 = T.raised, TextColor3 = T.text }) end) bind(button.MouseButton1Click, function() runSafely(text, fn) end) return button end local function inputRow(text, placeholder, initial, onCommit) local row = make("Frame", { LayoutOrder = nextOrder(), Size = UDim2.new(1, 0, 0, 50), BackgroundTransparency = 1, Parent = current, }, { pad(PAD, PAD) }) rowText(row, text, nil, 260) local boxField = make("TextBox", { Text = initial, PlaceholderText = placeholder, PlaceholderColor3 = T.dim, ClearTextOnFocus = false, Font = FONT, TextSize = 15, TextColor3 = T.white, TextXAlignment = Enum.TextXAlignment.Left, TextTruncate = Enum.TextTruncate.AtEnd, AnchorPoint = Vector2.new(1, 0.5), Position = UDim2.new(1, 0, 0.5, 0), Size = UDim2.fromOffset(250, 32), BackgroundColor3 = T.raised, BorderSizePixel = 0, Parent = row, }, { corner(6), pad(12, 12) }) local stroke = make("UIStroke", { Color = T.line, ApplyStrokeMode = Enum.ApplyStrokeMode.Border, Parent = boxField }) local committed = initial bind(boxField.Focused, function() tween(stroke, { Color = T.sub }) end) bind(boxField.FocusLost, function() tween(stroke, { Color = T.line }) if boxField.Text ~= committed then committed = boxField.Text onCommit(boxField.Text) end end) end local function swatchRow(text, colors, onPick) local row = make("Frame", { LayoutOrder = nextOrder(), Size = UDim2.new(1, 0, 0, 46), BackgroundTransparency = 1, Parent = current, }, { pad(PAD, PAD) }) rowText(row, text, nil, 240) local holder = make("Frame", { AnchorPoint = Vector2.new(1, 0.5), Position = UDim2.new(1, 0, 0.5, 0), Size = UDim2.fromOffset(230, 22), BackgroundTransparency = 1, Parent = row, }, { make("UIListLayout", { FillDirection = Enum.FillDirection.Horizontal, HorizontalAlignment = Enum.HorizontalAlignment.Right, VerticalAlignment = Enum.VerticalAlignment.Center, SortOrder = Enum.SortOrder.LayoutOrder, Padding = UDim.new(0, 8), }), }) for i, color in ipairs(colors) do local swatch = make("TextButton", { LayoutOrder = i, Text = "", AutoButtonColor = false, Size = UDim2.fromOffset(22, 22), BackgroundColor3 = color, BorderSizePixel = 0, Parent = holder, }, { corner() }) local stroke = make("UIStroke", { Color = T.off, Thickness = 1.5, Parent = swatch }) bind(swatch.MouseEnter, function() tween(stroke, { Color = T.white }) end) bind(swatch.MouseLeave, function() tween(stroke, { Color = T.off }) end) bind(swatch.MouseButton1Click, function() runSafely(text, function() onPick(color) end) end) end end local function keyRow(text, bindName, hint) local btn = make("TextButton", { LayoutOrder = nextOrder(), Text = "", AutoButtonColor = false, Size = UDim2.new(1, 0, 0, hint and 60 or 46), BackgroundColor3 = T.raised, BackgroundTransparency = 1, BorderSizePixel = 0, Parent = current, }, { pad(PAD, PAD) }) rowText(btn, text, hint, 150) local pill = make("TextLabel", { AnchorPoint = Vector2.new(1, 0.5), Position = UDim2.new(1, 0, 0.5, 0), Size = UDim2.fromOffset(0, 30), AutomaticSize = Enum.AutomaticSize.X, BackgroundColor3 = T.raised, Font = FONT_BOLD, TextSize = 14, TextColor3 = T.white, Text = "", Parent = btn, }, { corner(6), pad(14, 14) }) keyRows[bindName] = function() local waiting = bindingKey == bindName pill.Text = waiting and "Press a key…" or (Settings.binds[bindName] or "None") pill.BackgroundColor3 = waiting and T.white or T.raised pill.TextColor3 = waiting and T.black or T.white end hoverRow(btn) bind(btn.MouseButton1Click, function() bindingKey = bindName renderKeyRows() end) keyRows[bindName]() end local function note(text) return label({ LayoutOrder = nextOrder(), Text = text, TextSize = 13, TextColor3 = T.dim, Size = UDim2.new(1, 0, 0, 36), TextXAlignment = Enum.TextXAlignment.Center, TextWrapped = true, Parent = current, }) end refreshModule = function(name) local m = ModuleByName[name] if m then m.render() end end local function fmtTimes(v) return string.format("%.1fx", v) end --------------------------------------------------------------------------------------------- -- Pages --------------------------------------------------------------------------------------------- -- Vehicle ------------------------------------------------------------------------------------- current = pages.Vehicle passNote = note("Checking your gamepasses…") sectionHeader("Spawn") carPicker = choiceRow("Car", Settings.car, function(value) Settings.car = value end) actionRow("Spawn car", "Spawn", spawnCar, "Get out of any seat first") toggleRow(setting("Auto-tune", "autoTune", { hint = "Top speed, turbo, grip and underglow as soon as you get in", array = false, })) sectionHeader("Handling") toggleRow(setting("Performance", "performance", { hint = "Harder launches and quicker steering; top speed stays at your cap", set = function(v) Settings.performance = v applyPhysics() end, suffix = function() return fmtTimes(Settings.accel) end, })) -- Brookhaven turns the wheels toward the target angle at TurnSpeed per second (about 60 is -- instant), and ramps throttle and power from Turbo and Torque, which Acceleration scales. local accelSlider = sliderRow("Acceleration", 1, 5, 0.1, Settings.accel, fmtTimes, function(v) Settings.accel = v applyPhysics() end) local steerSlider = sliderRow("Steering response", 1, 6, 0.1, Settings.steer, fmtTimes, function(v) Settings.steer = v applyPhysics() end) actionRow("Max response", "Max out", function() Settings.performance = true refreshModule("Performance") accelSlider.set(5) steerSlider.set(6) saveSoon() notify("Max response", "Acceleration 5x · steering 6x", "good") end, "Fastest launches and near-instant steering") toggleRow(setting("Grip", "handling", { hint = "No sliding in sharp turns; capped per car so tall ones can't flip", set = function(v) Settings.handling = v applyTires() end, })) sliderRow("Tire grip", 0.5, 2, 0.05, Settings.tireGrip, function(v) return string.format("%.2f", v) end, function(v) Settings.tireGrip = v applyTires() end, function() local s = Session if s and tuningTires(s) then local grip = tireFriction(s) local ok, msg = carRequest("SetDriftStrength", math.min(grip, 1.5)) if not ok then notify("Grip", "Refused: " .. tostring(msg), "bad") elseif grip < Settings.tireGrip then notify("Grip", string.format("Capped at %.2f so this car can't flip", grip)) end end end) sliderRow("Stability control", 0, 100, 5, Settings.traction, function(v) return string.format("%d%%", v) end, function(v) Settings.traction = v end) toggleRow(setting("Anti-flip", "antiRoll", { array = false })) toggleRow(setting("Downforce", "grip", { array = false })) toggleRow(setting("Brake Assist", "brakeAssist", { hint = "Much harder braking when you press against your direction", suffix = function() return string.format("%.1fg", Settings.brakeStrength) end, })) sliderRow("Brake strength", 0.5, 2, 0.1, Settings.brakeStrength, function(v) return string.format("%.1fg", v) end, function(v) Settings.brakeStrength = v end) toggleRow(setting("Drift Mode", "drift", { hint = "Hold the drift key (Settings → Keybinds) to let the rear slide", set = function(v) Settings.drift = v if not v and Session and Session.drifting then Session.drifting = false applyTires() end end, })) sliderRow("Drift grip", 0.1, 1, 0.05, Settings.driftGrip, function(v) return string.format("%.2f", v) end, function(v) Settings.driftGrip = v if Session and Session.drifting then applyTires() end end) sectionHeader("Car") toggleRow(setting("Auto Flip", "autoFlip", { hint = "Puts the car back on its wheels if it ends up on its roof" })) toggleRow(setting("Auto Headlights", "autoLights", { hint = "Lights on at night and off in the day; V still works" })) sliderRow("Ride height", 0, 4, 1, Settings.rideHeight, function(v) return v == 0 and "Car default" or ("Level " .. v) end, function(v) Settings.rideHeight = v end) actionRow("Apply ride height", "Apply", function() if not Session then notify("Ride height", "Get in your car first", "bad") return end local level = Settings.rideHeight > 0 and Settings.rideHeight or defaultRideLevel(Session.car) if not level then notify("Ride height", "This car has no ride height settings", "bad") return end local ok, msg = carRequest("SetSuspensionLevel", level) notify("Ride height", ok and ("Level " .. level) or ("Refused: " .. tostring(msg)), ok and "good" or "bad") end) actionRow("Flip upright", "Flip", function() local ok, msg = carRequest("RepositionVehicle") notify("Flip upright", ok and "Car repositioned" or ("Refused: " .. tostring(msg)), ok and "good" or "bad") end) actionRow("Headlights", "Toggle", function() local s = Session if not s then notify("Headlights", "Get in your car first", "bad") return end -- The server answers with the new light state, not a success flag. local called, lightsOn = invoke("ToggleHeadLights") if called and type(lightsOn) == "boolean" then s.lightsOn = lightsOn notify("Headlights", lightsOn and "On" or "Off", "good") else notify("Headlights", "Request failed: " .. tostring(lightsOn), "bad") end end) toggleRow({ name = "Fire", quiet = true, array = false, get = function() return Session ~= nil and Session.car:GetAttribute("OnFire") == true end, set = function(v) task.spawn(setCarEffect, "OnFire", "ToggleOnFire", v) end, }) toggleRow({ name = "Engine Smoke", quiet = true, array = false, get = function() return Session ~= nil and Session.car:GetAttribute("Smoking") == true end, set = function(v) task.spawn(setCarEffect, "Smoking", "ToggleEngineSmoke", v) end, }) -- Stunts -------------------------------------------------------------------------------------- current = pages.Stunts local stuntNames = {} for _, def in ipairs(STUNTS) do table.insert(stuntNames, def.name) end sectionHeader("On a key") choiceRow("Trick", Settings.stunt, function(value) Settings.stunt = value end).setOptions(namesToOptions(stuntNames)) sliderRow("Airtime", 0.8, 2.5, 0.1, Settings.stuntAirtime, function(v) return string.format("%.1fs", v) end, function(v) Settings.stuntAirtime = v end) actionRow("Do the trick", "Go", function() performStunt(findStunt(Settings.stunt), Settings.stuntAirtime, "Stunt") end, "Or press your Stunt key while driving, even mid-jump") actionRow("Car hop", "Hop", function() performStunt(HOP, Settings.hopAirtime, "Car Hop") end, "A quick hop that lands flat; or press your Car Hop key") sliderRow("Hop airtime", 0.3, 1.2, 0.05, Settings.hopAirtime, function(v) return string.format("%.2fs", v) end, function(v) Settings.hopAirtime = v end) sectionHeader("In the air") toggleRow(setting("Air Stunts", "airStunts", { hint = "Drive off something high and the car does the trick by itself", suffix = function() return Settings.airTrick end, })) do local options = namesToOptions(stuntNames) table.insert(options, { value = "Random", label = "Random" }) choiceRow("Air trick", Settings.airTrick, function(value) Settings.airTrick = value end).setOptions(options) end toggleRow(setting("Every Jump", "airAlways", { hint = "Any jump gets the trick: short ones get a boost so it always finishes", array = false, })) sliderRow("Minimum jump", 0.1, 0.8, 0.05, Settings.airMin, function(v) return string.format("%.2fs", v) end, function(v) Settings.airMin = v end) note("Jumps shorter than this are ignored, so curbs and bumps never flip you.") toggleRow(setting("Landing Assist", "landAssist", { hint = "Jumps too short for a trick still land flat on four wheels" })) toggleRow(setting("Stunt Trail", "stuntTrail", { hint = "White streaks behind the car in the air (only you see them)" })) -- Visuals ------------------------------------------------------------------------------------- current = pages.Visuals sectionHeader("Paint") toggleRow(setting("Car RGB", "carRGB", { hint = "Smooth color fade, driving or parked; off puts your color back", set = function(v) setCarRGB(v) end, suffix = function() return Settings.rgbMode end, })) choiceRow("Mode", Settings.rgbMode, function(value) Settings.rgbMode = value end).setOptions(namesToOptions(RGB_MODES)) sliderRow("Cycle length", 1, 20, 0.5, Settings.rgbCycle, function(v) return string.format("%.1fs", v) end, function(v) Settings.rgbCycle = v end) swatchRow("Paint once", SWATCHES, function(color) local car = ownCar() if not car then notify("Paint", "Spawn a car first", "bad") return end if Settings.carRGB then Settings.carRGB = false saveSoon() refreshModule("Car RGB") task.wait(0.4) -- let any in-flight RGB step land first end local ok, msg = paintCar(car, color) if ok then originalPaints[car] = color notify("Paint", "Car painted", "good") else notify("Paint", "Refused: " .. tostring(msg), "bad") end end) sectionHeader("Underglow") toggleRow(setting("RGB Underglow", "rainbowGlow", { hint = "Glow follows your paint, or rainbows on its own" })) choiceRow("Style", Settings.underglow, function(value) Settings.underglow = value end).setOptions(namesToOptions(UNDERGLOWS)) sliderRow("Length", 0.1, 1.5, 0.05, Settings.glowLength, function(v) return string.format("%.2f", v) end, function(v) Settings.glowLength = v end) sliderRow("Width", 0.1, 1.5, 0.05, Settings.glowWidth, function(v) return string.format("%.2f", v) end, function(v) Settings.glowWidth = v end) actionRow("Apply underglow", "Apply", function() local ok, msg = carRequest("SetUnderglow", Settings.underglow) if ok then carRequest("SetUnderglowSize", Settings.glowLength, Settings.glowWidth) notify("Underglow", Settings.underglow, "good") else notify("Underglow", "Refused: " .. tostring(msg), "bad") end end) sectionHeader("Camera and world") toggleRow(setting("Speedometer", "speedo", {})) toggleRow(setting("Fullbright", "fullbright", { set = function(v) Settings.fullbright = v syncLightingSnapshot() end, })) local function hourText(v) return string.format("%02d:%s", math.floor(v) % 24, v % 1 >= 0.5 and "30" or "00") end toggleRow(setting("Time Lock", "lockTime", { set = function(v) Settings.lockTime = v syncLightingSnapshot() end, suffix = function() return hourText(Settings.hour) end, })) sliderRow("Hour", 0, 24, 0.5, Settings.hour, hourText, function(v) Settings.hour = v end) toggleRow(setting("FOV", "fovOn", { set = function(v) Settings.fovOn = v syncFov() end, suffix = function() return tostring(Settings.fov) end, })) sliderRow("Field of view", 50, 120, 1, Settings.fov, tostring, function(v) Settings.fov = v end) toggleRow(setting("Unlock Zoom", "unlockZoom", { set = function(v) Settings.unlockZoom = v syncZoom() end, })) -- Player -------------------------------------------------------------------------------------- current = pages.Player sectionHeader("Roleplay") -- Brookhaven ignores blank names and bios (tested), so an empty box only stops re-applying it. inputRow("Name", "Shown above your head", Settings.rpName, function(text) Settings.rpName = text saveSoon() if text:gsub("%s", "") == "" then notify("Roleplay name", "Brookhaven won't blank a name; it won't be re-applied") return end NameTextEvent:FireServer("RolePlayName", text) notify("Roleplay name", "Set to " .. text, "good") end) inputRow("Bio", "Shown under your name", Settings.rpBio, function(text) Settings.rpBio = text saveSoon() if text:gsub("%s", "") == "" then notify("Roleplay bio", "Brookhaven won't blank a bio; it won't be re-applied") return end NameTextEvent:FireServer("RolePlayBio", text) notify("Roleplay bio", "Bio updated", "good") end) toggleRow(setting("Rainbow Name", "rainbowName", { hint = "Cycles your roleplay name's color (needs a name set)", set = function(v) Settings.rainbowName = v if not v then task.delay(1.2, function() if alive and not Settings.rainbowName then NameColorEvent:FireServer("PickingRPNameColor", Color3.new(1, 1, 1)) end end) end end, })) sectionHeader("Session") toggleRow(setting("Anti-AFK", "antiAfk", { hint = "Stops the 20-minute idle kick" })) -- House --------------------------------------------------------------------------------------- current = pages.House sectionHeader("Color") toggleRow(setting("Rainbow House", "houseRainbow", { hint = "Your house fades through the colors; starts when you claim one", suffix = function() return string.format("%ds", Settings.houseCycle) end, })) sliderRow("Cycle length", 6, 60, 1, Settings.houseCycle, function(v) return string.format("%ds", v) end, function(v) Settings.houseCycle = v end) swatchRow("Paint once", SWATCHES, function(color) if HouseNumber.Value <= 0 then notify("House paint", "Claim a house first", "bad") return end Settings.houseRainbow = false saveSoon() refreshModule("Rainbow House") setHouseColor(color) notify("House paint", "House painted", "good") end) sectionHeader("Saved props") buildPicker = choiceRow("Build", Settings.buildSlot, function(value) Settings.buildSlot = value end) actionRow("Load props", "Load", function() loadBuild(Settings.buildSlot) end, "Into the house you own") toggleRow(setting("Load on claim", "loadBuild", { hint = "Loads the build when you claim a house", array = false, })) -- Settings ------------------------------------------------------------------------------------ current = pages.Settings sectionHeader("Interface") toggleRow(setting("ArrayList", "arraylist", { hint = "Enabled modules in the top-right corner", array = false })) toggleRow(setting("Toggle alerts", "toggleNotify", { hint = "A notification when you switch something on or off", array = false, quiet = true })) toggleRow(setting("Background blur", "blur", { array = false, set = function(v) Settings.blur = v tween(menuBlur, { Size = (v and menuOpen) and 14 or 0 }) end, })) actionRow("Recenter menu", "Reset", function() basePos = UDim2.fromScale(0.5, 0.5) tween(box, { Position = basePos }, GLIDE) end) sectionHeader("Keybinds") keyRow("Stunt", "Stunt") keyRow("Car hop", "Car Hop") keyRow("Drift (hold)", "Drift") note("Click a key, then press the new one. Esc clears it. RightShift opens this menu.") -- Unload button (bottom of the sidebar), styled like Snitch's. do local btn = make("TextButton", { Text = "Unload", Font = FONT_BOLD, TextSize = 15, TextColor3 = T.text, AutoButtonColor = false, Size = UDim2.fromScale(1, 1), BackgroundColor3 = T.raised, BorderSizePixel = 0, Parent = unloadHolder, }, { corner(6) }) bind(btn.MouseEnter, function() tween(btn, { BackgroundColor3 = T.white, TextColor3 = T.black }) end) bind(btn.MouseLeave, function() tween(btn, { BackgroundColor3 = T.raised, TextColor3 = T.text }) end) bind(btn.MouseButton1Click, function() if unloadRef then unloadRef() end end) end end --------------------------------------------------------------------------------------------- -- HUD (always on screen): Minecraft-client arraylist, top right, sorted longest first --------------------------------------------------------------------------------------------- local placeArrayList do local HUD_FONT = Enum.Font.Arcade local HUD_SIZE = 15 local HUD_LINE = 23 local SHADOW = Color3.fromRGB(38, 38, 40) local hud = make("Frame", { AnchorPoint = Vector2.new(1, 0), Position = UDim2.new(1, -4, 0, 4), Size = UDim2.fromOffset(400, 560), BackgroundTransparency = 1, Parent = Gui, }, { make("UIListLayout", { SortOrder = Enum.SortOrder.LayoutOrder, HorizontalAlignment = Enum.HorizontalAlignment.Right }), }) -- Text with a 1px Minecraft shadow; returns the white label (the shadow follows its Text). local function shadowText(parent, layoutOrder, size, color, height) local holder = make("Frame", { LayoutOrder = layoutOrder, Size = UDim2.fromOffset(0, height), AutomaticSize = Enum.AutomaticSize.X, BackgroundTransparency = 1, Parent = parent, }) local shadow = label({ Text = "", Font = HUD_FONT, TextSize = size, TextColor3 = SHADOW, Position = UDim2.fromOffset(1, 1), Size = UDim2.fromOffset(0, height), AutomaticSize = Enum.AutomaticSize.X, Parent = holder, }) local main = label({ Text = "", Font = HUD_FONT, TextSize = size, TextColor3 = color, Size = UDim2.fromOffset(0, height), AutomaticSize = Enum.AutomaticSize.X, Parent = holder, }) bind(main:GetPropertyChangedSignal("Text"), function() shadow.Text = main.Text end) return main, holder end local watermark = make("Frame", { LayoutOrder = -1000000, Size = UDim2.fromOffset(0, 30), AutomaticSize = Enum.AutomaticSize.X, BackgroundTransparency = 1, Parent = hud, }, { pad(0, 4) }) local wmText = shadowText(watermark, 1, 22, T.white, 30) wmText.Text = "BH STUDIO" -- Lines: [name][suffix][gap][bar]. The list layout places every child, so the white bar is -- the last item in the row rather than an absolutely positioned frame. local hudEntries = {} for _, m in ipairs(Modules) do if m.def.array ~= false then local frame = make("Frame", { Size = UDim2.fromOffset(0, HUD_LINE), AutomaticSize = Enum.AutomaticSize.X, BackgroundColor3 = T.black, BackgroundTransparency = 0.5, BorderSizePixel = 0, Visible = false, Parent = hud, }, { pad(6, 0), make("UIListLayout", { FillDirection = Enum.FillDirection.Horizontal, VerticalAlignment = Enum.VerticalAlignment.Center, SortOrder = Enum.SortOrder.LayoutOrder, Padding = UDim.new(0, 7), }), }) local main = shadowText(frame, 1, HUD_SIZE, T.white, HUD_LINE) main.Text = m.name local suffix, suffixHolder = shadowText(frame, 2, HUD_SIZE, T.sub, HUD_LINE) local bar = make("Frame", { LayoutOrder = 3, Size = UDim2.fromOffset(2, HUD_LINE), BackgroundColor3 = T.white, BorderSizePixel = 0, Parent = frame, }) table.insert(hudEntries, { m = m, frame = frame, main = main, suffix = suffix, suffixHolder = suffixHolder, bar = bar }) end end -- Brookhaven's HUD owns the top-right corner (clock and ad icons along the top, the menu -- column down the right edge), so the list sits just inside it. Measured from the live HUD -- so it fits any screen size. local function shownOnScreen(inst, screenGui) local node = inst while node and node ~= screenGui do if node:IsA("GuiObject") and not node.Visible then return false end node = node.Parent end return true end placeArrayList = function() local screen = Gui.AbsoluteSize local origin = Gui.AbsolutePosition local column, topRow = {}, {} for _, screenGui in ipairs(LocalPlayer.PlayerGui:GetChildren()) do if screenGui:IsA("ScreenGui") and screenGui.Enabled then for _, d in ipairs(screenGui:GetDescendants()) do if (d:IsA("GuiButton") or d:IsA("ImageLabel") or d:IsA("TextLabel")) and d.Visible and d.AbsoluteSize.X > 20 then local x = d.AbsolutePosition.X - origin.X local y = d.AbsolutePosition.Y - origin.Y local w, h = d.AbsoluteSize.X, d.AbsoluteSize.Y if d:IsA("GuiButton") and x + w > screen.X - 24 and y < screen.Y * 0.7 and shownOnScreen(d, screenGui) then table.insert(column, x) elseif x + w > screen.X - 480 and y < 80 and h < 80 and shownOnScreen(d, screenGui) then table.insert(topRow, { x + w, y + h }) end end end end end local right = 4 for _, x in ipairs(column) do right = math.max(right, screen.X - x + 8) end -- Only top-row items above the list's own span (entries are under ~220 px wide) push it down. local top = 4 for _, item in ipairs(topRow) do if item[1] > screen.X - right - 220 then top = math.max(top, item[2] + 6) end end hud.Position = UDim2.new(1, -right, 0, top) end -- Every line sorted by its rendered width, widest on top; a slow white-to-grey wave rolls -- down the list (the monochrome take on the client rainbow). bind(RunService.RenderStepped, function() hud.Visible = Settings.arraylist if not Settings.arraylist then return end local visible = {} for _, e in ipairs(hudEntries) do local on = e.m.def.get() == true if on and not e.frame.Visible then e.frame.Visible = true e.main.TextTransparency = 1 tween(e.main, { TextTransparency = 0 }) elseif not on and e.frame.Visible then e.frame.Visible = false end if on then local text = e.m.def.suffix and e.m.def.suffix() or "" if e.suffix.Text ~= text then e.suffix.Text = text end e.suffixHolder.Visible = text ~= "" table.insert(visible, e) end end table.sort(visible, function(a, b) local wa, wb = a.frame.AbsoluteSize.X, b.frame.AbsoluteSize.X if wa ~= wb then return wa > wb end return a.m.name < b.m.name end) local t = os.clock() local function wave(i) local k = 0.5 + 0.5 * math.sin(t * 2.2 - i * 0.55) return Color3.fromRGB(245, 245, 245):Lerp(Color3.fromRGB(150, 150, 154), k) end wmText.TextColor3 = wave(0) for i, e in ipairs(visible) do e.frame.LayoutOrder = i local color = wave(i) e.main.TextColor3 = color e.bar.BackgroundColor3 = color end end) end --------------------------------------------------------------------------------------------- -- Speedometer (your screen only). Speed is studs/s converted at 1 stud = 0.28 m. --------------------------------------------------------------------------------------------- do local Speedo = scaled(make("Frame", { Name = "Speedometer", AnchorPoint = Vector2.new(0.5, 1), Position = UDim2.new(0.5, 0, 1, -24), Size = UDim2.fromOffset(230, 64), BackgroundColor3 = T.bg, BackgroundTransparency = 0.04, BorderSizePixel = 0, Visible = false, Parent = Gui, }, { corner(10), make("UIStroke", { Color = T.line, ApplyStrokeMode = Enum.ApplyStrokeMode.Border }), })) local speedNumber = label({ Text = "0", Font = FONT_BLACK, TextSize = 30, TextColor3 = T.white, Position = UDim2.fromOffset(16, 7), Size = UDim2.fromOffset(90, 32), Parent = Speedo, }) label({ Text = "MPH", Font = FONT_BOLD, TextSize = 11, TextColor3 = T.sub, Position = UDim2.fromOffset(17, 40), Size = UDim2.fromOffset(60, 12), Parent = Speedo, }) local speedCar = label({ Text = "", TextSize = 13, TextColor3 = T.sub, Position = UDim2.fromOffset(104, 13), Size = UDim2.new(1, -120, 0, 16), TextXAlignment = Enum.TextXAlignment.Right, TextTruncate = Enum.TextTruncate.AtEnd, Parent = Speedo, }) local speedTrack = make("Frame", { Position = UDim2.fromOffset(104, 39), Size = UDim2.new(1, -120, 0, 4), BackgroundColor3 = T.off, BorderSizePixel = 0, Parent = Speedo, }, { corner() }) local speedFill = make("Frame", { Size = UDim2.new(0, 0, 1, 0), BackgroundColor3 = T.white, BorderSizePixel = 0, Parent = speedTrack }, { corner() }) local shownSpeed = 0 bind(RunService.RenderStepped, function(dt) local s = Session local driving = s ~= nil Speedo.Visible = Settings.speedo and driving if not driving then shownSpeed = 0 return end local root = s.seat.AssemblyRootPart local mph = root and root.AssemblyLinearVelocity.Magnitude * 0.28 * 2.237 or 0 shownSpeed += (mph - shownSpeed) * math.min(dt * 8, 1) local whole = math.floor(shownSpeed + 0.5) if Settings.speedo then speedNumber.Text = tostring(whole) speedFill.Size = UDim2.new(math.clamp(shownSpeed / 160, 0, 1), 0, 1, 0) speedCar.Text = s.car:GetAttribute("vehicleName") or "" end if menuOpen and cardActive then local parts = { whole .. " mph", stuntCount .. (stuntCount == 1 and " stunt" or " stunts") } if cardNote ~= "" then table.insert(parts, 1, cardNote) end cardLine.Text = table.concat(parts, " · ") end end) end --------------------------------------------------------------------------------------------- -- Opening, closing, dragging and keys --------------------------------------------------------------------------------------------- local dragging = false do local dragStart, startPos bind(header.InputBegan, function(input) if menuOpen and (input.UserInputType == Enum.UserInputType.MouseButton1 or input.UserInputType == Enum.UserInputType.Touch) then dragging, dragStart, startPos = true, input.Position, box.Position end end) bind(UserInputService.InputChanged, function(input) if dragging and (input.UserInputType == Enum.UserInputType.MouseMovement or input.UserInputType == Enum.UserInputType.Touch) then local d = input.Position - dragStart box.Position = UDim2.new(startPos.X.Scale, startPos.X.Offset + d.X, startPos.Y.Scale, startPos.Y.Offset + d.Y) basePos = box.Position end end) bind(UserInputService.InputEnded, function(input) if input.UserInputType == Enum.UserInputType.MouseButton1 or input.UserInputType == Enum.UserInputType.Touch then dragging = false end end) end local function setMenu(open) menuOpen = open modal.Modal = open modal.Visible = open dragging = false if open then box.Visible = true box.Position = basePos -- Never open partly off-screen. local pos, size, screen = box.AbsolutePosition, box.AbsoluteSize, Gui.AbsoluteSize if pos.X < 0 or pos.Y < 0 or pos.X + size.X > screen.X or pos.Y + size.Y > screen.Y then basePos = UDim2.fromScale(0.5, 0.5) box.Position = basePos end boxScale.Scale = guiScale * 0.94 local info = TweenInfo.new(0.34, Enum.EasingStyle.Quint, Enum.EasingDirection.Out) tween(box, { GroupTransparency = 0 }, info) tween(boxScale, { Scale = guiScale }, info) tween(menuBlur, { Size = Settings.blur and 14 or 0 }, info) refreshVehicleStatus() else if bindingKey then bindingKey = nil renderKeyRows() end local info = TweenInfo.new(0.18, Enum.EasingStyle.Quad, Enum.EasingDirection.In) tween(box, { GroupTransparency = 1 }, info) tween(boxScale, { Scale = guiScale * 0.96 }, info) tween(menuBlur, { Size = 0 }, info) task.delay(0.2, function() if not menuOpen then box.Visible = false end end) end end local function setDrift(on) local s = Session if not s or not s.geom.valid or s.drifting == on then return end s.drifting = on applyTires() end -- The game binds Shift itself, so RightShift can arrive as "processed": only ignore keys -- while you're typing in a text box (chat etc.). bind(UserInputService.InputBegan, function(input) if input.UserInputType ~= Enum.UserInputType.Keyboard then return end if bindingKey then local name = bindingKey bindingKey = nil if input.KeyCode == Enum.KeyCode.Escape or input.KeyCode == Enum.KeyCode.Backspace then Settings.binds[name] = nil notify("Keybinds", name .. " cleared") elseif input.KeyCode ~= Enum.KeyCode.RightShift then Settings.binds[name] = input.KeyCode.Name notify("Keybinds", name .. " is now " .. input.KeyCode.Name, "good") end saveSoon() renderKeyRows() return end if UserInputService:GetFocusedTextBox() then return end if input.KeyCode == Enum.KeyCode.RightShift then setMenu(not menuOpen) return end local key = input.KeyCode.Name if key == Settings.binds.Stunt then performStunt(findStunt(Settings.stunt), Settings.stuntAirtime, "Stunt") elseif key == Settings.binds["Car Hop"] then performStunt(HOP, Settings.hopAirtime, "Car Hop") end if key == Settings.binds.Drift and Settings.drift then setDrift(true) end end) bind(UserInputService.InputEnded, function(input) if input.UserInputType == Enum.UserInputType.Keyboard and input.KeyCode.Name == Settings.binds.Drift then setDrift(false) end end) local function unload() if not alive then return end alive = false local rgbCar = Settings.carRGB and ownCar() if rgbCar and originalPaints[rgbCar] then task.spawn(paintCar, rgbCar, originalPaints[rgbCar]) end endSession() RunService:UnbindFromRenderStep("BrookhavenStudioView") if savedLighting then for key, value in pairs(savedLighting) do Lighting[key] = value end savedLighting = nil end if savedFov then workspace.CurrentCamera.FieldOfView = savedFov end if savedZoom then LocalPlayer.CameraMaxZoomDistance = savedZoom end for _, c in ipairs(connections) do c:Disconnect() end table.clear(connections) modal.Modal = false menuBlur:Destroy() Gui:Destroy() if ENV.BrookhavenStudio and ENV.BrookhavenStudio.Unload == unload then ENV.BrookhavenStudio = nil end print("[Brookhaven Studio] unloaded") end unloadRef = unload bind(workspace.CurrentCamera:GetPropertyChangedSignal("ViewportSize"), function() guiScale = computeScale() for _, sc in ipairs(scaleObjects) do sc.Scale = guiScale end if menuOpen then boxScale.Scale = guiScale end task.defer(placeArrayList) end) ENV.BrookhavenStudio = { Unload = unload, Settings = Settings, Modules = ModuleByName, SetShown = setMenu, IsShown = function() return menuOpen end, SelectTab = selectTab, SetCarRGB = setCarRGB, SyncLighting = syncLightingSnapshot, ApplyCarSettings = function() applyPhysics() applyTires() end, Stunt = function(name) performStunt(findStunt(name), Settings.stuntAirtime, "Stunt") end, SetDrift = setDrift, Session = function() return Session end, StuntCount = function() return stuntCount end, } --------------------------------------------------------------------------------------------- -- Start --------------------------------------------------------------------------------------------- syncLightingSnapshot() syncFov() syncZoom() selectTab("Vehicle", true) setMenu(startShown) refreshVehicleStatus() -- Gamepass checks hit Roblox's web API, so they run after the menu is up. task.spawn(function() local available = {} for _, car in ipairs(CARS) do if not car.pass or owns(car.pass) then table.insert(available, { value = car.id, label = car.label }) end end if not alive then return end local selected = Settings.car local found = false for _, option in ipairs(available) do if option.value == selected then found = true end end if not found and available[1] then selected = available[1].value Settings.car = selected end carPicker.setOptions(available, selected) local headline = owns("VIP") and "VIP" or owns("PREMIUM") and "Premium" or "Standard" passNote.Text = string.format("%s · %d of %d cars · top speed %d", headline, #available, #CARS, topSpeed()) if LocalPlayer.Character then task.spawn(hookCharacter, LocalPlayer.Character) end end) bind(LocalPlayer.CharacterAdded, function(character) endSession() hookCharacter(character) end) if Settings.rpName:gsub("%s", "") ~= "" then NameTextEvent:FireServer("RolePlayName", Settings.rpName) end if Settings.rpBio:gsub("%s", "") ~= "" then NameTextEvent:FireServer("RolePlayBio", Settings.rpBio) end task.spawn(function() local options, err = fetchSaves() if not alive then return end if not options then notify("Saved props", "Couldn't read your house saves: " .. tostring(err), "bad") buildPicker.setOptions({}) return end local selected = Settings.buildSlot if selected == "" and options[1] then selected = options[1].value Settings.buildSlot = selected end buildPicker.setOptions(options, selected) end) bind(HouseNumber.Changed, function(number) if number > 0 then notify("House", "House #" .. number .. " claimed" .. (Settings.houseRainbow and " · color cycle on" or ""), "good") if Settings.loadBuild and Settings.buildSlot ~= "" then task.delay(3, function() if alive and HouseNumber.Value == number then loadBuild(Settings.buildSlot) end end) end end end) -- House color cycle. Each server update repaints hundreds of parts for everyone, so the server -- only gets a step every 0.6s; your own screen is smoothed below. task.spawn(function() while alive do if Settings.houseRainbow and HouseNumber.Value > 0 then local color = houseColorAt(os.clock() + 0.05) setHouseColor(color) table.insert(housePaint.recent, 1, color) housePaint.recent[4] = nil if os.clock() >= housePaint.refreshAt then housePaint.refreshAt = os.clock() + 8 task.delay(0.8, function() if alive then housePaint.parts = collectHouseParts() end end) end task.wait(0.6) else table.clear(housePaint.parts) housePaint.refreshAt = 0 task.wait(0.3) end end end) do -- Repaint 20 times a second, and also immediately in any frame where a server update just -- landed (the first part no longer shows what we last wrote), so it never flickers. local nextFrame = 0 local lastWritten bind(RunService.RenderStepped, function() local parts = housePaint.parts if not Settings.houseRainbow or HouseNumber.Value <= 0 or #parts == 0 then return end local now = os.clock() local overwritten = lastWritten ~= nil and parts[1].Color ~= lastWritten if now < nextFrame and not overwritten then return end nextFrame = now + 0.05 local color = houseColorAt(now) for _, part in ipairs(parts) do part.Color = color end lastWritten = parts[1].Color end) end -- RGB streaming. The server gets ~10 small steps a second (tested: 28/28 accepted at this -- rate), sent without waiting on each reply, so other players see a smooth fade too. -- Three refusals in a row (e.g. a car that can't be painted) switch the feature off. local RGB_SEND_INTERVAL = 0.1 local RGB_MAX_IN_FLIGHT = 2 -- getTarget returns what to recolor right now, or nil to pause. local function rgbStream(getTarget, send, onFail) task.spawn(function() local inFlight, failures = 0, 0 while alive do local target = getTarget() if target and inFlight < RGB_MAX_IN_FLIGHT then inFlight += 1 task.spawn(function() local ok, msg = send(target, os.clock() + RGB_SEND_INTERVAL) inFlight -= 1 if ok or getTarget() ~= target then failures = 0 else failures += 1 if failures >= 3 then failures = 0 onFail(msg) end end end) end task.wait(target and RGB_SEND_INTERVAL or 0.25) end end) end -- Car RGB keeps going whether you're driving the car or it's parked (the game accepts paint -- for your car from anywhere). While you drive, it waits for auto-tune to finish first. local function rgbCar() if not Settings.carRGB then return nil end local s = Session if s and not s.ready then return nil end return ownCar() end rgbStream(rgbCar, function(car, t) return paintCar(car, rgbColor(Settings.rgbMode, t, Settings.rgbCycle)) end, function(msg) setCarRGB(false) notify("Car RGB", "Stopped: " .. tostring(msg), "bad") end) -- The underglow request has no car id, so it only works for the car you're in. rgbStream(function() local s = Session return Settings.rainbowGlow and s and s.ready and s or nil end, function(_, t) local ok, msg = carRequest("SetUnderglowColor", 1, glowColor(t)) -- Usually the glow is just switched off; turn the chosen style back on and keep going. if not ok and tostring(msg):lower():find("select an underglow") then carRequest("SetUnderglow", Settings.underglow) end return ok, msg end, function(msg) Settings.rainbowGlow = false saveSoon() refreshModule("RGB Underglow") notify("RGB Underglow", "Stopped: " .. tostring(msg), "bad") end) -- Your own screen: recolor every frame so the fade is perfectly smooth between the server's -- steps (replicated values land before RenderStepped, so these always win on screen). bind(RunService.RenderStepped, function() local t = os.clock() local car = rgbCar() if car then local color = rgbColor(Settings.rgbMode, t, Settings.rgbCycle) for _, part in ipairs(paintParts(car)) do part.Color = color end end local s = Session if not s or not s.ready then return end if Settings.rainbowGlow then local color = glowColor(t) for _, obj in ipairs(glowObjects(s.car)) do if obj:IsA("Beam") then obj.Color = ColorSequence.new(color) else obj.Color = color end end end end) -- Rainbow RP name. Brookhaven waits 1s between name color changes, so match that. task.spawn(function() local hue = 0 while alive do if Settings.rainbowName then hue = (hue + 0.08) % 1 NameColorEvent:FireServer("PickingRPNameColor", Color3.fromHSV(hue, 0.85, 1)) task.wait(1.1) else task.wait(0.3) end end end) -- Auto Flip: if the car stays on its roof or side for a moment, ask the game to set it upright. task.spawn(function() local tippedSince local lastFlip = 0 while alive do local s = Session if Settings.autoFlip and s and not s.stunting and s.geom then local up = carFrame(s).UpVector if up.Y < 0.35 then tippedSince = tippedSince or os.clock() if os.clock() - tippedSince > 1.2 and os.clock() - lastFlip > 5 then lastFlip = os.clock() tippedSince = nil local ok = carRequest("RepositionVehicle") notify("Auto Flip", ok and "Back on your wheels" or "Couldn't flip the car", ok and "good" or "bad") end else tippedSince = nil end else tippedSince = nil end task.wait(0.2) end end) -- Air Stunts and Landing Assist watch every physics step. bind(RunService.Heartbeat, airMonitor) -- Auto Headlights. The game only reports the light state when it's toggled, so this switches -- at dusk and dawn (and when you get in) and checks the answer; pressing V yourself still works -- in between. task.spawn(function() while alive do local s = Session if Settings.autoLights and s and s.ready then local clock = savedLighting and savedLighting.ClockTime or Lighting.ClockTime local night = clock < 6.3 or clock >= 17.7 if s.lightsFor ~= night then s.lightsFor = night if s.lightsOn == nil and not night then s.lightsOn = false -- a freshly spawned car has its lights off end for _ = 1, 2 do if s.lightsOn == night or Session ~= s then break end local called, state = invoke("ToggleHeadLights") if not called or type(state) ~= "boolean" then notify("Auto Headlights", "Couldn't switch the lights: " .. tostring(state), "bad") break end s.lightsOn = state end end end task.wait(1) end end) -- Periodic refresh for things that change on their own. task.spawn(function() local tick = 0 while alive do tick += 1 if tick % 10 == 0 then refreshVehicleStatus() for _, name in ipairs({ "Fire", "Engine Smoke" }) do refreshModule(name) end end -- Brookhaven's HUD changes with what you're doing, so re-measure it every few seconds. if tick % 50 == 1 then placeArrayList() end task.wait(0.1) end end) notify("Brookhaven Studio", "v6 loaded · RightShift opens the menu", "good")