--[[ diag_steer4.luau -- isolate the real constraint on steering. Prior runs had a CONFOUND: every hoisted/static test commanded exactly -MaxSteerAngle, which is exactly the servo's LowerAngle limit. Sitting a servo precisely on its own limit can deadlock it. The isolated 2-part servo (target -30, limits +/-35) worked perfectly, so the API is fine. This matrix commands -20 deg (well INSIDE the +/-35.08 limits) and varies one thing at a time. ]] local ServerScriptService = game:GetService("ServerScriptService") local ReplicatedStorage = game:GetService("ReplicatedStorage") local RunService = game:GetService("RunService") local out = {} local function log(...) local parts = {} for i = 1, select("#", ...) do parts[#parts + 1] = tostring((select(i, ...))) end table.insert(out, table.concat(parts, " ")) end local Shared = ReplicatedStorage:FindFirstChild("Shared") local Cars = require(Shared.Cars) local Chassis = require(Shared.Chassis) local Server = ServerScriptService:FindFirstChild("Server", true) local CarManager = require(Server.CarManager) local pad = Instance.new("Part") pad.Size = Vector3.new(3000, 8, 3000) pad.Position = Vector3.new(0, 5000, 0) pad.Anchored = true pad.Material = Enum.Material.Concrete pad.Parent = workspace local padTop = pad.Position.Y + 4 local car = Cars.Get("gtr_r34") local tuning = Chassis.tuningFor(car) local LIMIT = tuning.MaxSteerAngle local INSIDE = -20 -- comfortably inside +/-35.08 local function build(y) local m = CarManager._buildForDiag(car, tuning) m.Parent = workspace m:PivotTo(CFrame.new(0, y, 0)) return m end local function servosOf(model) local servos, motors = {}, {} for _, d in ipairs(model:GetDescendants()) do if d:IsA("HingeConstraint") then if d.Name == Chassis.STEER_SERVO_NAME then table.insert(servos, d) elseif d.Name == Chassis.WHEEL_MOTOR_NAME then table.insert(motors, d) end end end return servos, motors end -- Run one case: returns the CurrentAngle trace. local function runCase(label, opts) local m = build(opts.hoist and (padTop + 40) or (padTop + 3)) local body = m.PrimaryPart local servos, motors = servosOf(m) if opts.hoist then body.Anchored = true end if opts.noWheel then for _, d in ipairs(m:GetDescendants()) do if d:IsA("BasePart") and d.Name:sub(1, 6) == "Wheel_" then d:Destroy() end end end if opts.motorOff then for _, mo in ipairs(motors) do mo.Enabled = false end end if opts.noLimits then for _, s in ipairs(servos) do s.LimitsEnabled = false end end for _ = 1, 60 do RunService.Heartbeat:Wait() end if opts.speed then body.AssemblyLinearVelocity = body.CFrame.LookVector * opts.speed for _, mo in ipairs(motors) do mo.MotorMaxTorque = 0 end end for _, s in ipairs(servos) do s.TargetAngle = opts.target end local trace = {} for step = 1, 120 do if opts.speed then for _, s in ipairs(servos) do s.TargetAngle = opts.target end end if step % 24 == 0 then trace[#trace + 1] = string.format("%.1f", servos[1].CurrentAngle) end RunService.Heartbeat:Wait() end local final = servos[1].CurrentAngle local ok = math.abs(final - opts.target) < 5 log( string.format( "%-42s target %6.1f -> [%s] final %6.2f %s", label, opts.target, table.concat(trace, " "), final, ok and "*** TURNS ***" or "stuck" ) ) m:Destroy() end log( string.format("limits +/-%.2f deg | INSIDE target %.1f | AngularSpeed %.1f rad/s", LIMIT, INSIDE, tuning.SteerSpeed) ) log("") runCase("A1 hoisted, target INSIDE limits", { hoist = true, target = INSIDE }) runCase("A2 hoisted, target AT limit", { hoist = true, target = -LIMIT }) runCase("A3 hoisted, LimitsEnabled=false", { hoist = true, target = INSIDE, noLimits = true }) runCase("A4 hoisted, wheels removed", { hoist = true, target = INSIDE, noWheel = true }) runCase("A5 hoisted, wheel motors disabled", { hoist = true, target = INSIDE, motorOff = true }) log("") runCase("B1 ground static, target INSIDE", { target = INSIDE }) runCase("B2 ground static, motors disabled", { target = INSIDE, motorOff = true }) log("") runCase("C1 ground coasting @60, INSIDE", { target = INSIDE, speed = 60 }) runCase("C2 ground coasting @120, INSIDE", { target = INSIDE, speed = 120 }) pad:Destroy() return table.concat(out, "\n")