-- Measure the redesigned ramps: extent, grade, side (E/W), and collisions -- with STRUCTURAL parts (piers, masts, girders). Read-only. local ws = game:GetService("Workspace") local map = ws:FindFirstChild("Map") if not map then return "no Map" end local PA = Vector3.new(-4124, 46, -7470) -- Gather deck parts per run. local runs = {} for _, d in ipairs(map:GetDescendants()) do if d:IsA("BasePart") then local nm = d.Name for _, base in ipairs({ "PAEntryRampTrunk", "PAEntryRampNear", "PAExitRampTrunk", "PAExitRampNear" }) do if nm:sub(1, #base) == base then runs[base] = runs[base] or {} table.insert(runs[base], d) end end end end local out = {} local order = { "PAEntryRampTrunk", "PAEntryRampNear", "PAExitRampTrunk", "PAExitRampNear" } for _, base in ipairs(order) do local parts = runs[base] if not parts then table.insert(out, base .. ": MISSING") else local minY, maxY = math.huge, -math.huge local minX, maxX = math.huge, -math.huge local minZ, maxZ = math.huge, -math.huge local maxGrade = 0 table.sort(parts, function(a, b) return a.Name < b.Name end) for _, p in ipairs(parts) do local q = p.Position minY, maxY = math.min(minY, q.Y), math.max(maxY, q.Y) minX, maxX = math.min(minX, q.X), math.max(maxX, q.X) minZ, maxZ = math.min(minZ, q.Z), math.max(maxZ, q.Z) end for i = 2, #parts do local a, b = parts[i - 1].Position, parts[i].Position local horiz = (Vector3.new(a.X, 0, a.Z) - Vector3.new(b.X, 0, b.Z)).Magnitude if horiz > 0.5 then maxGrade = math.max(maxGrade, math.abs(b.Y - a.Y) / horiz * 100) end end -- mean X relative to PA centre tells us east (+) or west (-) local sx = 0 for _, p in ipairs(parts) do sx += p.Position.X end local side = (sx / #parts) > PA.X and "EAST" or "WEST" table.insert( out, ("%s: n=%d Y=%.0f..%.0f grade<=%.2f%% side=%s X=%.0f..%.0f Z=%.0f..%.0f"):format( base, #parts, minY, maxY, maxGrade, side, minX, maxX, minZ, maxZ ) ) end end -- Structural collision test: does any ramp deck part overlap a pier/mast? local structural = {} for _, d in ipairs(map:GetDescendants()) do if d:IsA("BasePart") then local nm = d.Name if nm:find("PierColumn") or nm:find("PAMast") or nm:find("GirderRib") then table.insert(structural, d) end end end table.insert(out, "structuralParts=" .. #structural) local hits = 0 local detail = {} for _, base in ipairs(order) do local parts = runs[base] or {} local h = 0 for _, p in ipairs(parts) do for _, s in ipairs(structural) do local dx = math.abs(p.Position.X - s.Position.X) local dz = math.abs(p.Position.Z - s.Position.Z) local dy = math.abs(p.Position.Y - s.Position.Y) local rx = (p.Size.X + s.Size.X) / 2 local rz = (p.Size.Z + s.Size.Z) / 2 local ry = (p.Size.Y + s.Size.Y) / 2 if dx < rx and dz < rz and dy < ry then h += 1 end end end hits += h table.insert(detail, base .. "=" .. h) end table.insert(out, "STRUCTURAL_HITS total=" .. hits .. " (" .. table.concat(detail, " ") .. ")") return table.concat(out, "\n")