--[[ diag_nosecue.luau -- confirm WHICH cue is lying. diag_shellgeom (bodywork asymmetry, wheel-independent) says the built fleet disagrees on facing: rx7_fc +0.550, silv_s15 +0.206, hatch_junk +0.614 all land one way, while hyper_x -0.290 and gtr_r34 -0.362 land the other. Exactly two cars wrong -- matching the user's report. The aligner derives facing from each shell's Wheels folder. If a shell's F*/R* wheel labels are swapped relative to its bodywork, the aligner confidently rotates it backwards. This cross-checks the wheel cue against two INDEPENDENT cues that cannot both be wrong: * named bodywork: Hood/Windshield sit at the nose, Boot/Spoiler at the tail * bodywork asymmetry (as before) If wheels disagree with the bodywork, the wheel labels are the bug. ]] local ReplicatedStorage = game:GetService("ReplicatedStorage") 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 log(string.format("%-14s %10s %10s %10s %10s %s", "car", "wheelZ", "hoodZ", "bootZ", "asym", "wheels-vs-body")) log(string.rep("-", 78)) for _, t in ipairs(ReplicatedStorage.CarModels:GetChildren()) do local pivotInv = t:GetPivot():Inverse() -- cue 1: wheels local wheelDz = nil local wf = t:FindFirstChild("Wheels") if wf then local fS, fN, rS, rN = 0, 0, 0, 0 for _, w in ipairs(wf:GetChildren()) do local p = w:IsA("BasePart") and w or w:FindFirstChildWhichIsA("BasePart", true) if p then local z = (pivotInv * p.Position).Z local h = w.Name:sub(1, 1):upper() if h == "F" then fS, fN = fS + z, fN + 1 elseif h == "R" then rS, rN = rS + z, rN + 1 end end end if fN > 0 and rN > 0 then wheelDz = fS / fN - rS / rN end end -- cue 2: named bodywork local function avgZ(patterns) local s, n = 0, 0 for _, p in ipairs(t:GetDescendants()) do if p:IsA("BasePart") then local nm = p.Name:lower() for _, pat in ipairs(patterns) do if nm == pat then s += (pivotInv * p.Position).Z n += 1 break end end end end return n > 0 and (s / n) or nil end local hoodZ = avgZ({ "hood", "windshield", "bonnet", "grille" }) local bootZ = avgZ({ "boot", "spoiler", "trunk" }) -- cue 3: asymmetry local sum, wsum, lo, hi = 0, 0, math.huge, -math.huge for _, p in ipairs(t:GetDescendants()) do if p:IsA("BasePart") then local nm = p.Name:lower() if not (nm:find("wheel") or nm:find("tyre") or nm:find("tire") or nm:find("rim")) then local z = (pivotInv * p.Position).Z local v = p.Size.X * p.Size.Y * p.Size.Z sum, wsum = sum + z * v, wsum + v lo, hi = math.min(lo, z), math.max(hi, z) end end end local asym = wsum > 0 and (sum / wsum - (lo + hi) / 2) or nil -- Wheels say nose is at sign(wheelDz). Bodywork says nose is where the -- hood is, i.e. sign(hoodZ). They must agree. local verdict = "?" if wheelDz and hoodZ then local wheelNose = wheelDz < 0 and -1 or 1 local hoodNose = hoodZ < 0 and -1 or 1 verdict = (wheelNose == hoodNose) and "agree" or "*** DISAGREE -- wheel labels swapped ***" end log( string.format( "%-14s %10s %10s %10s %10s %s", t.Name, wheelDz and string.format("%+.2f", wheelDz) or "n/a", hoodZ and string.format("%+.2f", hoodZ) or "n/a", bootZ and string.format("%+.2f", bootZ) or "n/a", asym and string.format("%+.2f", asym) or "n/a", verdict ) ) end return table.concat(out, "\n")