-- Dominator Garage (client-sided, self-building) -- Tap "CARS" on the left, use the < > arrows to pick a car, press OK to spawn. Then walk up and press Drive. local VEHICLES = { { name = "Dominator 1", id = 97188597374642, spikes = false, snatch = 110 }, { name = "Dominator 2", id = 18705472828, spikes = true, snatch = 120 }, { name = "Dominator 3", id = 81713342766877, spikes = true, snatch = 135 }, { name = "TIV 1", id = 132451995770483, spikes = false, snatch = 150 }, { name = "Tornado Shocker", id = 135174872889473, spikes = true, snatch = 220, drop = 1.6, ride = -2.8 }, -- ride: more negative = sits lower { name = "TIV 2", id = 76361366591159, spikes = false, snatch = 150, drop = 1.6, yaw = 90, seat = Vector3.new(-1.5, -1.6, -2) }, -- seat Y lower = sit lower -- if it faces the wrong way use yaw = -90 -- per-vehicle extras you can add: yaw = 90 (if it drives sideways), seat = Vector3.new(x, y, z) -- drop = how far the body lowers to the ground on deploy (for cars with no rig) } local MAX_SPEED = 95 local REVERSE_SPEED = 30 local ACCEL = 38 local BRAKE = 70 local DRAG = 14 local TURN_RATE = 1.5 local GRAVITY = 196 local RIDE_OFFSET = 0 -- negative lowers the car, positive raises it local DEPLOY_DROP = 0.8 -- how far the body drops on deploy local SPIKE_TRAVEL = 2.5 -- how far spike parts push down into the ground on deploy local DEFAULT_SEAT = Vector3.new(-1.5, -0.4, -2) -- lower number = sit lower local ENGINE_ID = 136646841190295 local ACCEL_ID = 71254224253857 local DEPLOY_ID = 119289479120380 local UNDEPLOY_ID = 122898196250302 local SPIKE_DEPLOY_ID = 93178620992029 -- deploy sound for cars with spikes -- tornado snatch (needs the tornado_api_addon.lua block pasted at the bottom of the tornado script) local SNATCH = { Enabled = true, DefaultMph = 120, -- wind speed AT THE CAR that snatches it (per car: snatch = N in the list above) DeployedMul = 1.5, -- deployed cars are held down: threshold x this Hold = { 8, 13 }, -- seconds carried in the funnel before being thrown out MaxHeight = 0.55, -- fraction of the funnel height it gets lifted to Lift = 60, -- extra upward pull while below max height Grip = 1.6, -- how hard the wind grabs the car ThrowSpeed = 240, ThrowUp = 140, Cooldown = 25, -- seconds before it can be snatched again after landing MinFieldMph = 90, } local function getStorm() local g = getgenv and getgenv() local f = g and g.TornadoAPI if type(f) ~= "function" then return nil end local ok, snap = pcall(f) return ok and snap or nil end local function stormCenterAt(snap, p) local h01 = math.clamp((p.Y - snap.pos.Y) / math.max(snap.height, 1), 0, 1) local bx, bz = snap.bend(h01) return Vector3.new(snap.pos.X + bx, snap.pos.Y, snap.pos.Z + bz) end -- wind speed (mph) at a world position, same falloff the tornado probes use local function localWind(snap, p) local c = stormCenterAt(snap, p) local d = math.max(math.sqrt((p.X - c.X) ^ 2 + (p.Z - c.Z) ^ 2), 1) local Rc = math.max(snap.colR * 0.5, 60) return (d < Rc) and snap.peak * (d / Rc) or snap.peak * (Rc / d) ^ 1.1 end -- wind velocity (studs/s): spin + inflow + updraft local function windField(snap, p) local c = stormCenterAt(snap, p) local rx, rz = p.X - c.X, p.Z - c.Z local dist = math.max(math.sqrt(rx * rx + rz * rz), 1) local rdx, rdz = rx / dist, rz / dist local core = math.max(snap.colR * 0.8, 60) local vt = (dist < core) and (dist / core) or (core / dist) ^ 0.75 local h01 = math.clamp((p.Y - c.Y) / math.max(snap.height, 1), 0, 1) local upK = math.clamp(1 - dist / (core * 1.7), 0, 1) ^ 0.7 local peak = math.max(snap.peak, SNATCH.MinFieldMph) local v = Vector3.new(-rdz, 0, rdx) * (vt * peak) + Vector3.new(-rdx, 0, -rdz) * (vt * peak * 0.45) + Vector3.new(0, upK * peak * 0.7 * (1 - 0.5 * h01), 0) return v, c, dist, core end local Players = game:GetService("Players") local RunService = game:GetService("RunService") local UIS = game:GetService("UserInputService") local lp = Players.LocalPlayer if getgenv then local g = getgenv() if g.__Dominator1 then pcall(g.__Dominator1) end if g.__DominatorGarage then pcall(g.__DominatorGarage) end end local conns, cleanupList = {}, {} local current, spawning = nil, false local function cleanup() if current then pcall(current.destroy); current = nil end for _, c in ipairs(conns) do pcall(function() c:Disconnect() end) end for _, o in ipairs(cleanupList) do pcall(function() o:Destroy() end) end pcall(function() lp.Character:SetAttribute("InVehicle", nil) end) end if getgenv then getgenv().__DominatorGarage = cleanup end -- gui parent local guiParent pcall(function() guiParent = gethui and gethui() end) guiParent = guiParent or lp:WaitForChild("PlayerGui") -- driving HUD local hud = Instance.new("ScreenGui") hud.Name = "DominatorHUD" hud.ResetOnSpawn = false hud.Enabled = false hud.Parent = guiParent table.insert(cleanupList, hud) local function mkButton(parent, text, size, pos, color) local b = Instance.new("TextButton") b.Size = size b.Position = pos b.BackgroundColor3 = color b.TextColor3 = Color3.new(1, 1, 1) b.Font = Enum.Font.GothamBold b.TextSize = 16 b.Text = text b.Parent = parent Instance.new("UICorner", b) return b end local speedLabel = Instance.new("TextLabel") speedLabel.Size = UDim2.new(0, 140, 0, 40) speedLabel.Position = UDim2.new(0.5, -70, 1, -70) speedLabel.BackgroundColor3 = Color3.fromRGB(15, 15, 15) speedLabel.BackgroundTransparency = 0.3 speedLabel.TextColor3 = Color3.new(1, 1, 1) speedLabel.Font = Enum.Font.GothamBold speedLabel.TextSize = 20 speedLabel.Text = "0 MPH" speedLabel.Parent = hud Instance.new("UICorner", speedLabel) local windLabel = Instance.new("TextLabel") windLabel.Size = UDim2.new(0, 190, 0, 28) windLabel.Position = UDim2.new(0.5, -95, 0, 8) windLabel.BackgroundColor3 = Color3.fromRGB(15, 15, 15) windLabel.BackgroundTransparency = 0.3 windLabel.TextColor3 = Color3.fromRGB(120, 255, 150) windLabel.Font = Enum.Font.GothamBold windLabel.TextSize = 15 windLabel.Text = "" windLabel.Visible = false windLabel.Parent = hud Instance.new("UICorner", windLabel) local exitBtn = mkButton(hud, "EXIT", UDim2.new(0, 100, 0, 40), UDim2.new(1, -120, 0, 80), Color3.fromRGB(170, 40, 40)) local deployBtn = mkButton(hud, "DEPLOY", UDim2.new(0, 100, 0, 40), UDim2.new(1, -120, 0, 130), Color3.fromRGB(220, 130, 20)) local lightsBtn = mkButton(hud, "LIGHTS: OFF", UDim2.new(0, 100, 0, 40), UDim2.new(1, -120, 0, 180), Color3.fromRGB(70, 70, 80)) lightsBtn.TextSize = 13 table.insert(conns, exitBtn.Activated:Connect(function() if current then current.exit() end end)) table.insert(conns, deployBtn.Activated:Connect(function() if current then current.toggleDeploy() end end)) table.insert(conns, lightsBtn.Activated:Connect(function() if current then current.toggleLights() end end)) table.insert(conns, UIS.JumpRequest:Connect(function() if current and current.isDriving() then current.exit() end end)) table.insert(conns, UIS.InputBegan:Connect(function(input, gp) if gp or not current then return end if input.KeyCode == Enum.KeyCode.F then current.toggleDeploy() end if input.KeyCode == Enum.KeyCode.L then current.toggleLights() end end)) -- controls local controls pcall(function() controls = require(lp.PlayerScripts:WaitForChild("PlayerModule", 3)):GetControls() end) local function getInput() local mv = Vector3.zero if controls then local o, v = pcall(function() return controls:GetMoveVector() end) if o and v then mv = v end end if mv.Magnitude < 0.05 then local x, z = 0, 0 if UIS:IsKeyDown(Enum.KeyCode.W) then z -= 1 end if UIS:IsKeyDown(Enum.KeyCode.S) then z += 1 end if UIS:IsKeyDown(Enum.KeyCode.D) then x += 1 end if UIS:IsKeyDown(Enum.KeyCode.A) then x -= 1 end mv = Vector3.new(x, 0, z) end return -mv.Z, mv.X end --------------------------------------------------------------------- -- vehicle builder --------------------------------------------------------------------- local function buildVehicle(def) local char = lp.Character local hrp = char and char:FindFirstChild("HumanoidRootPart") if not hrp then error("no character") end local result = game:GetObjects("rbxassetid://" .. def.id) local model = result and result[1] if not model then error("failed to load " .. def.name) end if not model:IsA("Model") then local wrap = Instance.new("Model") model.Parent = wrap model = wrap end model.Name = def.name model.PrimaryPart = nil -- WorldPivot is ignored if a PrimaryPart is set local yaw = def.yaw or 0 local seatOffset = def.seat or DEFAULT_SEAT local vconns = {} -- spike detection (parts named spike / anchor / stake) local spikeSet, spikes = {}, {} local function spikeNamed(d) local x = d while x and x ~= model do local n = x.Name:lower() if n:find("spike") or n:find("anchor") or n:find("stake") then return true end x = x.Parent end return false end for _, d in ipairs(model:GetDescendants()) do if d:IsA("BaseScript") then d:Destroy() elseif d:IsA("BasePart") then d.Anchored = true d.Massless = true if tonumber(d.Name) then d.Name = "V" .. d.Name end if def.spikes and spikeNamed(d) then spikeSet[d] = true end elseif d:IsA("Seat") or d:IsA("VehicleSeat") then d.Disabled = true end end local names = {} for _, c in ipairs(model:GetChildren()) do table.insert(names, c.Name) end local spikeCount = 0 for _ in pairs(spikeSet) do spikeCount += 1 end print(def.name .. " parts: " .. table.concat(names, ", ") .. " | spike parts found: " .. spikeCount) -- measure visible parts (spike parts excluded) in a given frame local function bounds(frame) local mn = Vector3.new(math.huge, math.huge, math.huge) local mx = Vector3.new(-math.huge, -math.huge, -math.huge) local n = 0 for _, d in ipairs(model:GetDescendants()) do if d:IsA("BasePart") and d.Transparency < 0.9 and not spikeSet[d] then local s = d.Size / 2 for ix = -1, 1, 2 do for iy = -1, 1, 2 do for iz = -1, 1, 2 do local pt = frame:PointToObjectSpace((d.CFrame * CFrame.new(s.X * ix, s.Y * iy, s.Z * iz)).Position) mn = Vector3.new(math.min(mn.X, pt.X), math.min(mn.Y, pt.Y), math.min(mn.Z, pt.Z)) mx = Vector3.new(math.max(mx.X, pt.X), math.max(mx.Y, pt.Y), math.max(mx.Z, pt.Z)) end end end n += 1 end end return mn, mx, n end local ref = model:GetPivot() local mn, mx, n = bounds(ref) if n == 0 then spikeSet = {} mn, mx, n = bounds(ref) end if n == 0 then local bcf, bsz = model:GetBoundingBox() local c = ref:PointToObjectSpace(bcf.Position) mn, mx = c - bsz / 2, c + bsz / 2 end local bbSize = mx - mn model.WorldPivot = ref * CFrame.new((mn + mx) / 2) local halfH = bbSize.Y / 2 local swap = math.abs(yaw) % 180 == 90 local width = swap and bbSize.Z or bbSize.X local length = swap and bbSize.X or bbSize.Z -- remember where each spike part sits relative to the pivot local pivot0 = model:GetPivot() for part in pairs(spikeSet) do table.insert(spikes, { part = part, rel = pivot0:ToObjectSpace(part.CFrame) }) end local rayParams = RaycastParams.new() rayParams.FilterType = Enum.RaycastFilterType.Exclude local function updateFilter() rayParams.FilterDescendantsInstances = { model, lp.Character } end updateFilter() -- spawn in front of the player local spawnPos = hrp.Position + hrp.CFrame.LookVector * (length / 2 + 12) local hit = workspace:Raycast(spawnPos + Vector3.new(0, 100, 0), Vector3.new(0, -300, 0), rayParams) local pos = (hit and hit.Position or spawnPos) + Vector3.new(0, halfH, 0) local look0 = hrp.CFrame.LookVector local heading = math.atan2(-look0.X, -look0.Z) local up = Vector3.yAxis local speed, vy = 0, 0 local driving, deployed, deployAlpha = false, false, 0 local mode = "drive" -- drive / snatched / thrown local vel, angVel, airRot = Vector3.zero, Vector3.zero, nil local snatchT, snatchDur, snatchCooldown, spinDir = 0, 0, 0, 1 local snatchMph = def.snatch or SNATCH.DefaultMph local deployDrop = def.drop or DEPLOY_DROP local function buildCF() if airRot then return CFrame.new(pos) * airRot end local fwd = CFrame.Angles(0, heading, 0).LookVector local look = (fwd - up * fwd:Dot(up)).Unit local right = look:Cross(up).Unit return CFrame.fromMatrix(pos, right, up, -look) end local promptPart = Instance.new("Part") promptPart.Size = Vector3.new(1, 1, 1) promptPart.Transparency = 1 promptPart.CanCollide = false promptPart.CanQuery = false promptPart.Anchored = true promptPart.Parent = model -- headlights (front of the car) local lights, lightsOn = {}, false for _, sx in ipairs({ -1, 1 }) do local p = Instance.new("Part") p.Size = Vector3.new(0.9, 0.6, 0.3) p.Material = Enum.Material.Neon p.Color = Color3.fromRGB(70, 70, 70) p.Anchored = true p.CanCollide = false p.CanQuery = false p.CanTouch = false p.Parent = model local sl = Instance.new("SpotLight") sl.Face = Enum.NormalId.Front sl.Range = 110 sl.Angle = 55 sl.Brightness = 5 sl.Color = Color3.fromRGB(255, 244, 214) sl.Enabled = false sl.Parent = p table.insert(lights, { part = p, light = sl, x = sx * width * 0.3 }) end local function setLights(on) lightsOn = on for _, l in ipairs(lights) do l.light.Enabled = on l.part.Color = on and Color3.fromRGB(255, 244, 214) or Color3.fromRGB(70, 70, 70) end lightsBtn.Text = on and "LIGHTS: ON" or "LIGHTS: OFF" lightsBtn.BackgroundColor3 = on and Color3.fromRGB(200, 170, 40) or Color3.fromRGB(70, 70, 80) end -- sounds local engineSound = Instance.new("Sound") engineSound.SoundId = "rbxassetid://" .. ENGINE_ID engineSound.Looped = true engineSound.Volume = 0.4 engineSound.RollOffMaxDistance = 200 engineSound.Parent = promptPart local accelSound = Instance.new("Sound") accelSound.SoundId = "rbxassetid://" .. ACCEL_ID accelSound.Volume = 0.8 accelSound.RollOffMaxDistance = 200 accelSound.Parent = promptPart local function mkSound(id, vol) local snd = Instance.new("Sound") snd.SoundId = "rbxassetid://" .. id snd.Volume = vol snd.RollOffMaxDistance = 200 snd.Parent = promptPart return snd end local deploySound = mkSound(DEPLOY_ID, 1) local undeploySound = mkSound(UNDEPLOY_ID, 1) local spikeDeploySound = mkSound(SPIKE_DEPLOY_ID, 1) local engineOn, wasPressing, lastAccel = false, false, 0 local function place() local cf = buildCF() local e = deployAlpha * deployAlpha * (3 - 2 * deployAlpha) local P = cf * CFrame.new(0, -deployDrop * e, 0) * CFrame.Angles(0, math.rad(yaw), 0) model:PivotTo(P) for _, sp in ipairs(spikes) do sp.part.CFrame = P * sp.rel - cf.UpVector * (SPIKE_TRAVEL * e) end promptPart.CFrame = cf local lcf = cf * CFrame.new(0, -deployDrop * e, 0) for _, l in ipairs(lights) do l.part.CFrame = lcf * CFrame.new(l.x, -halfH * 0.3, -length / 2 - 0.1) end return cf end local cf0 = place() do -- calibrate: put the real lowest visible point exactly on the ground local cmn, _, cn = bounds(cf0) if cn > 0 then halfH = -cmn.Y pos = (hit and hit.Position or spawnPos) + Vector3.new(0, halfH, 0) place() end end print(string.format("%s size: %.1f x %.1f x %.1f | halfH %.2f", def.name, bbSize.X, bbSize.Y, bbSize.Z, halfH)) local prompt = Instance.new("ProximityPrompt") prompt.ActionText = "Drive" prompt.ObjectText = def.name prompt.HoldDuration = 0 prompt.RequiresLineOfSight = false prompt.MaxActivationDistance = math.max(length, width) / 2 + 10 prompt.KeyboardKeyCode = Enum.KeyCode.E prompt.Parent = promptPart model.Parent = workspace -- enter / exit / deploy local function enter() if driving then return end char = lp.Character local hum = char and char:FindFirstChildOfClass("Humanoid") hrp = char and char:FindFirstChild("HumanoidRootPart") if not hum or not hrp then return end driving = true char:SetAttribute("InVehicle", true) updateFilter() for _, p in ipairs(char:GetDescendants()) do if p:IsA("BasePart") then p.CanCollide = false end end hum.Sit = true hrp.Anchored = true prompt.Enabled = false deployBtn.Text = deployed and "RETRACT" or "DEPLOY" setLights(lightsOn) hud.Enabled = true end local function exit() if not driving or mode ~= "drive" then return end driving = false if char then char:SetAttribute("InVehicle", nil) end speed = 0 local hum = char and char:FindFirstChildOfClass("Humanoid") if hrp and hrp.Parent then hrp.Anchored = false hrp.CFrame = buildCF() * CFrame.new(-(width / 2 + 4), 1, seatOffset.Z) end if hum then hum.Sit = false end if char then for _, p in ipairs(char:GetDescendants()) do if p:IsA("BasePart") and p.Name ~= "HumanoidRootPart" then p.CanCollide = true end end end prompt.Enabled = true hud.Enabled = false end local function toggleDeploy() if not driving or mode ~= "drive" then return end if not deployed and math.abs(speed) > 6 then deployBtn.Text = "STOP FIRST" task.delay(1, function() deployBtn.Text = deployed and "RETRACT" or "DEPLOY" end) return end deployed = not deployed deployBtn.Text = deployed and "RETRACT" or "DEPLOY" if deployed then (def.spikes and spikeDeploySound or deploySound):Play() else undeploySound:Play() end end table.insert(vconns, prompt.Triggered:Connect(function(plr) if plr == lp then enter() end end)) table.insert(vconns, lp.CharacterAdded:Connect(function() driving = false prompt.Enabled = true hud.Enabled = false end)) -- tornado snatch local function startSnatch() local cf = buildCF() mode = "snatched" snatchT = 0 snatchDur = SNATCH.Hold[1] + math.random() * (SNATCH.Hold[2] - SNATCH.Hold[1]) vel = cf.LookVector * speed airRot = cf.Rotation angVel = Vector3.new(math.random() - 0.5, math.random() - 0.5, math.random() - 0.5) * 1.2 spinDir = (math.random() < 0.5) and 1 or -1 speed, vy = 0, 0 deployed = false deployBtn.Text = "DEPLOY" end local function land(g) local fwd = airRot.LookVector local flat = Vector3.new(fwd.X, 0, fwd.Z) if flat.Magnitude < 0.1 then flat = Vector3.new(0, 0, -1) end flat = flat.Unit heading = math.atan2(-flat.X, -flat.Z) airRot = nil up = Vector3.yAxis pos = g.Position + Vector3.new(0, halfH, 0) vel, speed, vy = Vector3.zero, 0, 0 mode = "drive" snatchCooldown = os.clock() + SNATCH.Cooldown end local function stepAir(dt, snap) deployAlpha = math.max(deployAlpha - dt / 0.6, 0) if mode == "snatched" then snatchT += dt if not snap or snap.contact < 0.15 then mode = "thrown" else local field, c = windField(snap, pos) local h = pos.Y - snap.pos.Y local lift = (h < snap.height * SNATCH.MaxHeight) and SNATCH.Lift or 0 vel += ((field + Vector3.new(0, lift, 0)) - vel) * math.min(1, SNATCH.Grip * dt) local t = snatchT local target = Vector3.new(math.sin(t * 1.3 + spinDir) * 1.6, 1.8 * spinDir, math.cos(t * 0.9) * 1.4) angVel = angVel:Lerp(target, math.min(1, 1.5 * dt)) if snatchT >= snatchDur then local rx, rz = pos.X - c.X, pos.Z - c.Z local d = math.max(math.sqrt(rx * rx + rz * rz), 1) local rad = Vector3.new(rx / d, 0, rz / d) local tan = Vector3.new(-rad.Z, 0, rad.X) vel = rad * SNATCH.ThrowSpeed + tan * (SNATCH.ThrowSpeed * 0.8) + Vector3.new(0, SNATCH.ThrowUp, 0) mode = "thrown" end end else vel += Vector3.new(0, -GRAVITY, 0) * dt end pos += vel * dt local w = angVel.Magnitude if w > 0.0001 then airRot = CFrame.fromAxisAngle(angVel.Unit, w * dt) * airRot end if mode == "thrown" and vel.Y <= 0 then local reach = halfH + 3 + (-vel.Y) * dt local g = workspace:Raycast(pos + Vector3.new(0, 2, 0), Vector3.new(0, -reach, 0), rayParams) if g then land(g) end end end -- main loop table.insert(vconns, RunService.Heartbeat:Connect(function(dt) dt = math.min(dt, 0.05) local snap = SNATCH.Enabled and getStorm() or nil local need = snatchMph * (deployed and SNATCH.DeployedMul or 1) if snap and snap.contact > 0.05 then local mph = localWind(snap, pos) if driving then windLabel.Visible = true windLabel.Text = string.format("WIND %d / %d MPH", math.floor(mph), math.floor(need)) windLabel.TextColor3 = (mph >= need * 0.8) and Color3.fromRGB(255, 80, 80) or Color3.fromRGB(120, 255, 150) end if mode == "drive" and snap.contact > 0.4 and os.clock() >= snatchCooldown and mph >= need then startSnatch() end elseif driving then windLabel.Visible = false end if mode ~= "drive" then stepAir(dt, snap) local final = place() if driving and hrp and hrp.Parent then hrp.CFrame = final * CFrame.new(seatOffset) end if engineOn then engineSound.PlaybackSpeed = 1.7 engineSound.Volume = 0.6 end if driving then speedLabel.Text = string.format("%d MPH", math.floor(vel.Magnitude * 0.68)) end return end deployAlpha = math.clamp(deployAlpha + (deployed and 1 or -1) * dt / 1.2, 0, 1) if deployed then speed = 0 end local throttle, steer = 0, 0 if driving and not deployed then throttle, steer = getInput() end if throttle > 0.05 then if speed < 0 then speed += BRAKE * dt else speed += ACCEL * throttle * dt end speed = math.min(speed, MAX_SPEED * throttle) elseif throttle < -0.05 then if speed > 0 then speed -= BRAKE * dt else speed += ACCEL * throttle * dt end speed = math.max(speed, -REVERSE_SPEED) else local d = DRAG * dt if math.abs(speed) <= d then speed = 0 else speed -= math.sign(speed) * d end end -- engine + accelerate sounds local pressing = throttle > 0.05 if pressing and not wasPressing and driving and os.clock() - lastAccel > 1.5 then lastAccel = os.clock() accelSound:Play() end wasPressing = pressing if driving and not engineOn then engineOn = true engineSound:Play() elseif not driving and engineOn then engineOn = false engineSound:Stop() end if engineOn then engineSound.PlaybackSpeed = 0.7 + math.clamp(math.abs(speed) / MAX_SPEED, 0, 1) * 1.1 engineSound.Volume = pressing and 0.6 or 0.35 end local sf = math.clamp(speed / 25, -1, 1) heading -= steer * TURN_RATE * sf * dt local cf = buildCF() local fwd = cf.LookVector local flat = Vector3.new(fwd.X, 0, fwd.Z) flat = flat.Magnitude > 0 and flat.Unit or fwd local dist = speed * dt if math.abs(dist) > 0.001 then local dir = flat * math.sign(dist) local boxCF = cf * CFrame.new(0, halfH * 0.25, 0) local bs = Vector3.new(math.max(width * 0.85, 1), math.max(bbSize.Y * 0.4, 1), length * 0.9) local ok2, res = pcall(function() return workspace:Blockcast(boxCF, bs, dir * (math.abs(dist) + 0.5), rayParams) end) if ok2 and res and res.Normal.Y < 0.6 then speed = -speed * 0.25 else pos += flat * dist end end local g = workspace:Raycast(pos + up * (halfH + 6), -up * (halfH + 60), rayParams) if g then up = up:Lerp(g.Normal, math.min(1, 10 * dt)).Unit local targetPos = g.Position + up * (halfH + RIDE_OFFSET + (def.ride or 0)) if pos.Y - targetPos.Y > 1.5 then vy -= GRAVITY * dt pos += Vector3.new(0, vy * dt, 0) if pos.Y < targetPos.Y then pos = Vector3.new(pos.X, targetPos.Y, pos.Z); vy = 0 end else vy = 0 pos = pos:Lerp(targetPos, math.min(1, 18 * dt)) end else up = up:Lerp(Vector3.yAxis, math.min(1, 3 * dt)).Unit vy -= GRAVITY * dt pos += Vector3.new(0, vy * dt, 0) end local final = place() if driving then if hrp and hrp.Parent then local e = deployAlpha * deployAlpha * (3 - 2 * deployAlpha) hrp.CFrame = final * CFrame.new(seatOffset - Vector3.new(0, deployDrop * e, 0)) end speedLabel.Text = string.format("%d MPH", math.floor(math.abs(speed) * 0.68)) end end)) current = { isDriving = function() return driving end, exit = exit, toggleDeploy = toggleDeploy, toggleLights = function() if driving then setLights(not lightsOn) end end, destroy = function() if driving then mode = "drive" exit() end for _, c in ipairs(vconns) do pcall(function() c:Disconnect() end) end model:Destroy() hud.Enabled = false end, } end local function spawnVehicle(def, statusBtn) if spawning then return end spawning = true if current then current.destroy(); current = nil end local old = statusBtn and statusBtn.Text if statusBtn then statusBtn.Text = "Loading..." end local ok, err = pcall(buildVehicle, def) if not ok then warn("Garage: " .. tostring(err)) if statusBtn then statusBtn.Text = "Failed" ; task.wait(1) end end if statusBtn then statusBtn.Text = old end spawning = false end --------------------------------------------------------------------- -- spawn menu: < car name > + OK --------------------------------------------------------------------- local menu = Instance.new("ScreenGui") menu.Name = "DominatorGarage" menu.ResetOnSpawn = false menu.Parent = guiParent table.insert(cleanupList, menu) local toggle = mkButton(menu, "CARS", UDim2.new(0, 70, 0, 36), UDim2.new(0, 10, 0.35, 0), Color3.fromRGB(40, 40, 45)) local panel = Instance.new("Frame") panel.Size = UDim2.new(0, 220, 0, 150) panel.Position = UDim2.new(0, 10, 0.35, 44) panel.BackgroundColor3 = Color3.fromRGB(20, 20, 24) panel.BackgroundTransparency = 0.15 panel.Visible = false panel.Parent = menu Instance.new("UICorner", panel) local index = 1 local leftBtn = mkButton(panel, "<", UDim2.new(0, 40, 0, 40), UDim2.new(0, 8, 0, 10), Color3.fromRGB(60, 90, 160)) local rightBtn = mkButton(panel, ">", UDim2.new(0, 40, 0, 40), UDim2.new(1, -48, 0, 10), Color3.fromRGB(60, 90, 160)) leftBtn.TextSize = 22 rightBtn.TextSize = 22 local nameLabel = Instance.new("TextLabel") nameLabel.Size = UDim2.new(1, -112, 0, 40) nameLabel.Position = UDim2.new(0, 56, 0, 10) nameLabel.BackgroundColor3 = Color3.fromRGB(35, 35, 42) nameLabel.TextColor3 = Color3.new(1, 1, 1) nameLabel.Font = Enum.Font.GothamBold nameLabel.TextSize = 14 nameLabel.TextScaled = false nameLabel.TextWrapped = true nameLabel.Parent = panel Instance.new("UICorner", nameLabel) local countLabel = Instance.new("TextLabel") countLabel.Size = UDim2.new(1, 0, 0, 18) countLabel.Position = UDim2.new(0, 0, 0, 54) countLabel.BackgroundTransparency = 1 countLabel.TextColor3 = Color3.fromRGB(170, 170, 180) countLabel.Font = Enum.Font.Gotham countLabel.TextSize = 12 countLabel.Parent = panel local okBtn = mkButton(panel, "OK", UDim2.new(1, -16, 0, 36), UDim2.new(0, 8, 0, 76), Color3.fromRGB(40, 150, 70)) local removeBtn = mkButton(panel, "Remove car", UDim2.new(1, -16, 0, 30), UDim2.new(0, 8, 0, 116), Color3.fromRGB(150, 50, 50)) removeBtn.TextSize = 14 local function refresh() nameLabel.Text = VEHICLES[index].name countLabel.Text = index .. " / " .. #VEHICLES end refresh() table.insert(conns, leftBtn.Activated:Connect(function() index = (index - 2) % #VEHICLES + 1 refresh() end)) table.insert(conns, rightBtn.Activated:Connect(function() index = index % #VEHICLES + 1 refresh() end)) table.insert(conns, okBtn.Activated:Connect(function() spawnVehicle(VEHICLES[index], okBtn) end)) table.insert(conns, removeBtn.Activated:Connect(function() if current then current.destroy(); current = nil end end)) table.insert(conns, toggle.Activated:Connect(function() panel.Visible = not panel.Visible end)) print("Dominator Garage ready - tap CARS on the left.")