-- Verify the linear slips against assertions that can FAIL. -- Fixes the earlier harness bug: FindFirstChild must be RECURSIVE (PAFloor is -- nested), otherwise `floor` is nil and every apron test vacuously reports -- "does not reach" regardless of the real geometry. local HttpService = game:GetService("HttpService") local map = workspace:FindFirstChild("Map") if not map then return "no Map -- run the rebuild first" end local ramps = map:FindFirstChild("PARamps", true) if not ramps then return "PARamps folder missing" end local floor = map:FindFirstChild("PAFloor", true) -- RECURSIVE if not floor then return "PAFloor missing" end local fTop = floor.Position.Y + floor.Size.Y / 2 local fx0, fx1 = floor.Position.X - floor.Size.X / 2, floor.Position.X + floor.Size.X / 2 local fz0, fz1 = floor.Position.Z - floor.Size.Z / 2, floor.Position.Z + floor.Size.Z / 2 -- Collect deck segments per slip, ordered by their index suffix so we can find -- the true LOW end rather than guessing from a bounding box. local decks = { PAWestSlip = {}, PAEastSlip = {} } local counts = {} for _, d in ipairs(ramps:GetDescendants()) do if d:IsA("BasePart") then counts[d.Name:gsub("_%d+$", "")] = (counts[d.Name:gsub("_%d+$", "")] or 0) + 1 for slipName, list in pairs(decks) do local idx = d.Name:match("^" .. slipName .. "_Deck_(%d+)$") if idx then table.insert(list, { i = tonumber(idx), p = d.Position, s = d.Size, cf = d.CFrame }) end end end end local verdict, report = {}, {} for slipName, list in pairs(decks) do table.sort(list, function(a, b) return a.i < b.i end) if #list < 50 then table.insert(verdict, slipName .. " FAIL: only " .. #list .. " deck segments") else local first, last = list[1], list[#list] local yTopFirst = first.p.Y + first.s.Y / 2 local yTopLast = last.p.Y + last.s.Y / 2 -- 1. Does the LOW end sit inside the apron footprint? local inX = last.p.X >= fx0 and last.p.X <= fx1 local inZ = last.p.Z >= fz0 and last.p.Z <= fz1 local lands = inX and inZ -- 2. Is the low end FLUSH with the apron surface? A lip catches cars. local lip = yTopLast - fTop -- 3. Is the high end flush with the mainline deck (Y 103)? local mouthErr = yTopFirst - 103 -- 4. Peak grade over any single segment. local maxGrade = 0 for k = 2, #list do local a, b = list[k - 1], list[k] local dy = math.abs((b.p.Y) - (a.p.Y)) local dxz = (Vector3.new(b.p.X, 0, b.p.Z) - Vector3.new(a.p.X, 0, a.p.Z)).Magnitude if dxz > 0.5 then maxGrade = math.max(maxGrade, dy / dxz) end end report[slipName] = { segs = #list, mouth = { x = math.floor(first.p.X), z = math.floor(first.p.Z), yTop = math.floor(yTopFirst * 10) / 10 }, landing = { x = math.floor(last.p.X), z = math.floor(last.p.Z), yTop = math.floor(yTopLast * 10) / 10 }, landsInApron = lands, lipStuds = math.floor(lip * 100) / 100, mouthErrStuds = math.floor(mouthErr * 100) / 100, peakGradePct = math.floor(maxGrade * 1000) / 10, } if not lands then table.insert(verdict, ("%s FAIL: landing (%d,%d) outside apron x[%d..%d] z[%d..%d]") :format(slipName, last.p.X, last.p.Z, fx0, fx1, fz0, fz1)) end if math.abs(lip) > 1.5 then table.insert(verdict, ("%s FAIL: %.2f stud lip at apron"):format(slipName, lip)) end if math.abs(mouthErr) > 1.5 then table.insert(verdict, ("%s FAIL: %.2f stud step at mainline mouth"):format(slipName, mouthErr)) end if maxGrade > 0.16 then table.insert(verdict, ("%s FAIL: peak grade %.1f%%"):format(slipName, maxGrade * 100)) end end end -- 5. Does the mainline parapet actually open at each mouth? Count Barrier parts -- within 160 studs of each mouth on the lot side; a sealed mouth = ramp is -- decorative. local road = map local mouthBlocked = {} for slipName, r in pairs(report) do local mp = Vector3.new(r.mouth.x, 103, r.mouth.z) local n = 0 for _, d in ipairs(road:GetDescendants()) do if d:IsA("BasePart") and (d.Name == "Barrier" or d.Name == "Parapet") then if (Vector3.new(d.Position.X, 103, d.Position.Z) - mp).Magnitude < 150 then n += 1 end end end mouthBlocked[slipName] = n end return HttpService:JSONEncode({ apron = { top = fTop, x = { math.floor(fx0), math.floor(fx1) }, z = { math.floor(fz0), math.floor(fz1) } }, slips = report, partCounts = counts, barriersNearMouth = mouthBlocked, verdict = #verdict == 0 and "ALL PASS" or table.concat(verdict, " | "), })