-- Verification v3: seam lip measured against the KNOWN shared sample point. -- -- v2 still had a sign bug: Roblox CFrame.LookVector is the -Z axis, so a deck -- built with CFrame.lookAt(mid, mid+dir) has its FORWARD end at local -- z = -(Size.Z-2)/2 and its REAR end at +(Size.Z-2)/2. v2 had these swapped, -- comparing deck i's rear to deck i+1's rear -- a systematic error that -- reported a near-constant 5.2 on every sloped segment regardless of zone. -- -- This version avoids guessing signs: for each deck it takes BOTH end faces, -- picks whichever is nearer the shared sample point b.pos in XZ, and reports -- the surface-height difference there. Sanity-checked against the flat-ground -- expectation and cross-checked with a downward raycast. local sss = game:GetService("ServerScriptService") local orig = sss:FindFirstChild("MapBuilder", true) local clone = orig:Clone() clone.Name = "MapBuilder_Verify3" clone.Parent = orig.Parent local okM, MB = pcall(require, clone) if not okM then clone:Destroy() return "FAIL require: " .. tostring(MB) end local okB, map = pcall(MB.build) if not okB then clone:Destroy() return "FAIL build: " .. tostring(map) end local Circuit = require(game:GetService("ReplicatedStorage"):FindFirstChild("Circuit", true)) local samples = Circuit.samples local n = #samples local decks = {} for _, d in ipairs(map:GetDescendants()) do if d:IsA("BasePart") then local idx = d.Name:match("^Road_(%d+)$") if idx then decks[tonumber(idx)] = d end end end local out = {} local function line(s) out[#out + 1] = s end -- Top-surface height of deck `d` at the end nearest world point `p` (XZ). local function topAtNearestEnd(d, p) local half = (d.Size.Z - 2) / 2 local best, bestD = nil, math.huge for _, z in ipairs({ -half, half }) do local w = d.CFrame * CFrame.new(0, d.Size.Y / 2, z) local dd = (Vector3.new(w.X, 0, w.Z) - Vector3.new(p.X, 0, p.Z)).Magnitude if dd < bestD then best, bestD = w.Y, dd end end return best, bestD end local worst, worstAt, worstGap = 0, -1, 0 local lips = {} for i = 1, n do local j = (i % n) + 1 local a, b = decks[i], decks[j] if a and b then local shared = samples[j].pos -- the sample both decks meet at local ya, da = topAtNearestEnd(a, shared) local yb, db = topAtNearestEnd(b, shared) local lip = math.abs(ya - yb) lips[#lips + 1] = { lip, i, math.max(da, db) } if lip > worst then worst, worstAt, worstGap = lip, i, math.max(da, db) end end end table.sort(lips, function(p, q) return p[1] > q[1] end) local top = {} for k = 1, math.min(5, #lips) do top[#top + 1] = ("i=%d:%.4f(%s)"):format(lips[k][2], lips[k][1], samples[lips[k][2]].piece) end line( ("SEAM max lip=%.4f at i=%d (endpoint XZ err %.3f) -> %s"):format( worst, worstAt, worstGap, worst <= 0.30 and "PASS" or "FAIL" ) ) line("SEAM top5: " .. table.concat(top, " ")) -- Independent cross-check: raycast down onto the driving surface just before -- and just after each seam. A real step would show up here too. -- MUST filter to the Expressway folder only. Filtering on the whole map lets -- the ray hit TunnelCeiling / MedianFence / overpass decks on the way down and -- reports a bogus ~19-stud "step" that has nothing to do with the road surface. local rp = RaycastParams.new() rp.FilterType = Enum.RaycastFilterType.Include rp.FilterDescendantsInstances = { map:FindFirstChild("Expressway") } local rWorst, rAt = 0, -1 for i = 1, n do local j = (i % n) + 1 local p = samples[j].pos local fwd = (samples[(j % n) + 1].pos - samples[i].pos) fwd = Vector3.new(fwd.X, 0, fwd.Z) if fwd.Magnitude > 0.01 then fwd = fwd.Unit local h1 = workspace:Raycast(p - fwd * 3 + Vector3.new(0, 40, 0), Vector3.new(0, -120, 0), rp) local h2 = workspace:Raycast(p + fwd * 3 + Vector3.new(0, 40, 0), Vector3.new(0, -120, 0), rp) if h1 and h2 then local d = math.abs(h1.Position.Y - h2.Position.Y) if d > rWorst then rWorst, rAt = d, i end end end end line( ("RAYCAST surface step across seams (+-3 studs): max=%.4f at i=%d -> %s"):format( rWorst, rAt, rWorst <= 0.60 and "PASS" or "FAIL" ) ) map:Destroy() clone:Destroy() return table.concat(out, "\n")