--[[ diag_solveryaw.luau -- does the shell yaw solver actually fire for hatch_junk / hyper_x, and what does it produce? Hypothesis under test: the solver derives "nose" from the F/R wheel labels, so for a shell whose F wheels sit at +Z it should compute a 180-degree yaw and rotate the shell to face -Z. If that is happening, those two cars would look CORRECT -- but they look backwards. So either the solver is not being reached, or it computes something other than 180. CACHE GUARD FIRST. require() caches for the whole Studio session, so a build test can silently run OLD code. Before trusting anything below, the script re-reads Chassis.DRIVE_SIGN via require() and compares it to the .Source text. If they disagree, the cache is stale and the script ABORTS rather than reporting a number that means nothing. ]] local RS = game:GetService("ReplicatedStorage") local SSS = game:GetService("ServerScriptService") local out = {} local function log(s) table.insert(out, s) end local function find(name) for _, root in ipairs({ SSS, RS, game:GetService("ServerStorage"), workspace }) do local m = root:FindFirstChild(name, true) if m and m:IsA("ModuleScript") then return m end end return nil end -- ---- cache guard ------------------------------------------------------- local chMod = find("Chassis") if not chMod then return "FATAL: Chassis not found" end local srcHasNeg1 = chMod.Source:find("DRIVE_SIGN = -1", 1, true) ~= nil local okReq, Chassis = pcall(require, chMod) if not okReq then return "FATAL: require(Chassis) failed: " .. tostring(Chassis) end local liveNeg1 = (Chassis.DRIVE_SIGN == -1) log( string.format( "cache guard: source DRIVE_SIGN=-1? %s require() DRIVE_SIGN=%s", tostring(srcHasNeg1), tostring(Chassis.DRIVE_SIGN) ) ) if srcHasNeg1 ~= liveNeg1 then log("") log(">>> ABORT: require() cache disagrees with source. Any build measured now") log(" would use STALE code. Restart Studio (or reopen the place) and re-run.") return table.concat(out, "\n") end log("cache guard: PASS (require matches source)") log("") -- ---- what does the solver compute? ------------------------------------- -- Recompute the solver's own input the same way the source does: nose is the -- F-centroid minus R-centroid, in shell-local space; yaw = -atan2(nose.X, -nose.Z). local TARGETS = { "rx7_fc", "silv_s15", "gtr_r34", "hatch_junk", "hyper_x" } local shells = {} for _, root in ipairs({ RS, SSS, game:GetService("ServerStorage"), workspace }) do for _, d in ipairs(root:GetDescendants()) do if d:IsA("Model") and d:FindFirstChild("Wheels") and not shells[d.Name] then for _, t in ipairs(TARGETS) do if d.Name == t then shells[d.Name] = d end end end end end log(string.format("%-11s %-12s %-14s %s", "shell", "nose.Z", "solver yaw", "meaning")) log(string.rep("-", 74)) for _, name in ipairs(TARGETS) do local shell = shells[name] if not shell then log(string.format("%-11s NOT FOUND", name)) continue end local pivotInv = shell:GetPivot():Inverse() local wheels = shell:FindFirstChild("Wheels") local fSum, fN, rSum, rN = Vector3.zero, 0, Vector3.zero, 0 for _, w in ipairs(wheels:GetChildren()) do local sum, n = Vector3.zero, 0 if w:IsA("BasePart") then sum, n = w.Position, 1 end for _, d in ipairs(w:GetDescendants()) do if d:IsA("BasePart") then sum, n = sum + d.Position, n + 1 end end if n > 0 then local lp = pivotInv * (sum / n) local head = w.Name:sub(1, 1):upper() if head == "F" then fSum, fN = fSum + lp, fN + 1 elseif head == "R" then rSum, rN = rSum + lp, rN + 1 end end end if fN == 0 or rN == 0 then log(string.format("%-11s no F/R labels", name)) continue end local nose = (fSum / fN) - (rSum / rN) local yaw = -math.atan2(nose.X, -nose.Z) local deg = math.deg(yaw) local meaning if math.abs(deg) < 15 then meaning = "no rotation applied" elseif math.abs(math.abs(deg) - 180) < 15 then meaning = "180 flip -> solver DOES correct this shell" else meaning = "partial rotation" end log( string.format( "%-11s %-12s %-14s %s", name, string.format("%+.1f", nose.Z), string.format("%+.1f deg", deg), meaning ) ) end log("") log("If the two reported-backwards shells show a 180 flip here, the solver is") log("already correcting them in theory -- so the fault is that this code path") log("is not reached at build time, NOT the maths. That distinction decides the fix.") return table.concat(out, "\n")