Three iterations of the Daikoku PA ramps never read as a real
interchange. Measured why, rather than tuning further:
* the entry and exit decks INTERSECTED -- 2547 sample pairs within
30 studs horizontally at <12 studs dY, worst case 0.5 studs apart
at dY=0.0. On screen that is the bowtie tangle of white rails.
* both real Daikoku ramps turn the SAME way (clockwise; the guide
video says "right, right"). This build mirrored them into each
other.
* the real arc wraps OUTSIDE the lot and forms its north/east
boundary. This build cut across the apron interior.
Gated behind BUILD_PA_RAMPS=false rather than deleted -- the radius
was already correct (470 studs vs an OSM-measured mean of 469) and
the merge lands tangent at 0.1-0.2 deg divergence. Both worth keeping.
Also removed two leftovers that would have outlived the ramps:
* rampOpen punched holes in the mainline's outer parapet, railing
and kill wall for ramps that no longer exist -- a barrier gap with
a drop behind it. Sealed via RAMP_EDGE_OPENINGS=false. Verified:
Barrier/EdgeRail/KillWall = 1646 each = 823 samples x 2 edges,
0 road samples without a guard within 90 studs.
* gore zebra hatching painted 1024 bars + 64 boundary lines marking
a junction that is not there. Sealed via RAMP_GORE_MARKINGS=false.
Verified after a cache-busting fresh rebuild (Studio's require() cache
otherwise serves the previous map and reports a false pass):
parts 22628 -> 15988
all ramp part classes = 0
Road = 823 (unchanged), spawn pad + PASpawn intact
clip probe TRUSTWORTHY (control +11/-0), CLIPPING=0
Research and rebuild spec kept for when this is revisited:
reference/ramps/BUILD_SPEC.md, INDEX.md (10 CC-licensed Commons
photos), docs/daikoku-reference-spec.md (OSM circle fits).
117 lines
3.2 KiB
Luau
117 lines
3.2 KiB
Luau
-- CONTROL + CONNECTIVITY.
|
|
-- 1. Control: deliberately test a part known to intersect structure, so a
|
|
-- 0-hit result on the ramps means "clear", not "probe is broken".
|
|
-- 2. Connectivity: largest gap between consecutive deck segments within each
|
|
-- run, and the trunk->near handoff distance.
|
|
local ws = game:GetService("Workspace")
|
|
local map = ws:FindFirstChild("Map")
|
|
if not map then
|
|
return "no Map"
|
|
end
|
|
|
|
local structural, runs = {}, {}
|
|
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
|
|
for _, base in ipairs({
|
|
"PAEntryRampTrunk",
|
|
"PAEntryRampCross",
|
|
"PAEntryRampNear",
|
|
"PAExitRampTrunk",
|
|
"PAExitRampCross",
|
|
"PAExitRampNear",
|
|
}) do
|
|
if nm:sub(1, #base + 1) == base .. "_" then
|
|
runs[base] = runs[base] or {}
|
|
table.insert(runs[base], d)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
local function overlaps(p, s)
|
|
local d1, d2 = p.Position, s.Position
|
|
return math.abs(d1.X - d2.X) < (p.Size.X + s.Size.X) / 2
|
|
and math.abs(d1.Z - d2.Z) < (p.Size.Z + s.Size.Z) / 2
|
|
and math.abs(d1.Y - d2.Y) < (p.Size.Y + s.Size.Y) / 2
|
|
end
|
|
|
|
local out = {}
|
|
|
|
-- CONTROL: a probe box placed exactly on the first structural part must hit.
|
|
local ctrl = structural[1]
|
|
local fake = { Position = ctrl.Position, Size = Vector3.new(4, 4, 4) }
|
|
local ctrlHits = 0
|
|
for _, s in ipairs(structural) do
|
|
if overlaps(fake, s) then
|
|
ctrlHits += 1
|
|
end
|
|
end
|
|
table.insert(
|
|
out,
|
|
("CONTROL (box on a known pier): hits=%d -> probe %s"):format(
|
|
ctrlHits,
|
|
ctrlHits > 0 and "WORKS" or "IS BROKEN/VACUOUS"
|
|
)
|
|
)
|
|
table.insert(out, "structuralParts=" .. #structural)
|
|
|
|
-- CONNECTIVITY
|
|
local function sortKey(p)
|
|
return tonumber(p.Name:match("_(%d+)$")) or 0
|
|
end
|
|
for _, base in ipairs({
|
|
"PAEntryRampTrunk",
|
|
"PAEntryRampCross",
|
|
"PAEntryRampNear",
|
|
"PAExitRampTrunk",
|
|
"PAExitRampCross",
|
|
"PAExitRampNear",
|
|
}) do
|
|
local parts = runs[base]
|
|
if not parts then
|
|
table.insert(out, base .. ": MISSING")
|
|
else
|
|
table.sort(parts, function(a, b)
|
|
return sortKey(a) < sortKey(b)
|
|
end)
|
|
local maxGap = 0
|
|
for i = 2, #parts do
|
|
local g = (parts[i].Position - parts[i - 1].Position).Magnitude
|
|
maxGap = math.max(maxGap, g)
|
|
end
|
|
table.insert(out, ("%s: n=%d maxSegGap=%.1f"):format(base, #parts, maxGap))
|
|
end
|
|
end
|
|
|
|
-- Handoffs along the flyover chain: Trunk -> Cross -> Near. The ramps now
|
|
-- cross OVER the mainline, so `Cross` sits between the spiral and the
|
|
-- descent; checking Trunk->Near directly would report a false gap.
|
|
for _, pair in ipairs({
|
|
{ "PAEntryRampTrunk", "PAEntryRampCross" },
|
|
{ "PAEntryRampCross", "PAEntryRampNear" },
|
|
{ "PAExitRampTrunk", "PAExitRampCross" },
|
|
{ "PAExitRampCross", "PAExitRampNear" },
|
|
}) do
|
|
local t, nr = runs[pair[1]], runs[pair[2]]
|
|
if t and nr then
|
|
table.sort(t, function(a, b)
|
|
return sortKey(a) < sortKey(b)
|
|
end)
|
|
table.sort(nr, function(a, b)
|
|
return sortKey(a) < sortKey(b)
|
|
end)
|
|
local best = math.huge
|
|
for _, a in ipairs({ t[1], t[#t] }) do
|
|
for _, b in ipairs({ nr[1], nr[#nr] }) do
|
|
best = math.min(best, (a.Position - b.Position).Magnitude)
|
|
end
|
|
end
|
|
table.insert(out, ("HANDOFF %s->%s closestEnd=%.1f"):format(pair[1], pair[2], best))
|
|
end
|
|
end
|
|
return table.concat(out, "\n")
|