
Key Required VerifiedUniversal

--[[
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 <font size=\"13\" weight=\"medium\" color=\"#5A5A5E\">by Obsidian Works</font>",
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")


![[QUAIL] Best Universal Aimbot ESP 90+ Features](https://scriptblox.com/images/script/0-1774248169070.jpg)
Is okay (no key)