Disable PA on/off ramps; restore mainline to pre-ramp state
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).
This commit is contained in:
@@ -0,0 +1,146 @@
|
||||
-- CLIP PROBE v3 -- INVERTED SCOPE.
|
||||
--
|
||||
-- v2 tested drivable parts against a HARDCODED WHITELIST of 16 building
|
||||
-- names (Building, TokyoTower, GinzaBlock, ...). It reported CLIPPING=0 with
|
||||
-- a passing control, and I reported that as fact. The user can see ramps
|
||||
-- going through buildings, so the zero was false.
|
||||
--
|
||||
-- The bug is the whitelist: any obstacle whose name isn't one of those 16 is
|
||||
-- invisible to the test. The PA structures (shops, kiosks, canopies) are the
|
||||
-- prime suspect -- they sit exactly where the ramp ends land.
|
||||
--
|
||||
-- v3 inverts the scope: an obstacle is ANY part that is not drivable and not
|
||||
-- infrastructure (pier/rail/sign/ground). No name list to go stale.
|
||||
-- It also reports offenders GROUPED BY OBSTACLE NAME, so the answer names
|
||||
-- the thing being speared instead of just counting.
|
||||
|
||||
local ws = game:GetService("Workspace")
|
||||
local map = ws:FindFirstChild("Map")
|
||||
if not map then
|
||||
return "NO MAP"
|
||||
end
|
||||
|
||||
local function isDrivable(n)
|
||||
return n:match("^Road_")
|
||||
or n:find("Ramp")
|
||||
or n == "PAFloor"
|
||||
or n:match("^Apron")
|
||||
or n:match("^Slip")
|
||||
or n:match("^Merge")
|
||||
end
|
||||
|
||||
-- things that are ALLOWED to intersect a road (structure, decor, markings)
|
||||
local function isInfra(n)
|
||||
return n:find("Pier")
|
||||
or n:find("Pillar")
|
||||
or n:find("Column")
|
||||
or n:find("Support")
|
||||
or n:find("Beam")
|
||||
or n:find("Rail")
|
||||
or n:find("Guard")
|
||||
or n:find("Barrier")
|
||||
or n:find("Sign")
|
||||
or n:find("Light")
|
||||
or n:find("Lamp")
|
||||
or n:find("Line")
|
||||
or n:find("Mark")
|
||||
or n:find("Stripe")
|
||||
or n:find("Deck")
|
||||
or n:find("Water")
|
||||
or n == "Ground"
|
||||
or n:find("Grounds")
|
||||
or n:find("Median")
|
||||
or n:find("Curb")
|
||||
or n:find("Wall")
|
||||
end
|
||||
|
||||
local drive, obst = {}, {}
|
||||
for _, d in ipairs(map:GetDescendants()) do
|
||||
if d:IsA("BasePart") then
|
||||
local n = d.Name
|
||||
if isDrivable(n) then
|
||||
table.insert(drive, d)
|
||||
elseif not isInfra(n) then
|
||||
table.insert(obst, d)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if #obst == 0 or #drive == 0 then
|
||||
return ("SCOPE FAIL drive=%d obst=%d"):format(#drive, #obst)
|
||||
end
|
||||
|
||||
local op = OverlapParams.new()
|
||||
op.FilterType = Enum.RaycastFilterType.Include
|
||||
op.FilterDescendantsInstances = obst
|
||||
op.MaxParts = 12
|
||||
|
||||
-- POSITIVE CONTROL: probe dead-centre of a known obstacle must report a hit.
|
||||
local target = obst[math.floor(#obst / 2)]
|
||||
local ctrl = ws:GetPartBoundsInBox(CFrame.new(target.Position), Vector3.new(4, 4, 4), op)
|
||||
local controlOK = #ctrl > 0
|
||||
|
||||
-- SECOND CONTROL: a box far out in empty sky must report ZERO. Guards the
|
||||
-- opposite failure -- a filter so broad everything "hits".
|
||||
local negCtrl = ws:GetPartBoundsInBox(CFrame.new(0, 40000, 0), Vector3.new(8, 8, 8), op)
|
||||
local negOK = #negCtrl == 0
|
||||
|
||||
local hits = 0
|
||||
local byObst, byRamp = {}, {}
|
||||
local samples = {}
|
||||
for _, p in ipairs(drive) do
|
||||
local probe = p.Size - Vector3.new(1.5, 1.5, 1.5)
|
||||
if probe.X > 0.5 and probe.Y > 0.5 and probe.Z > 0.5 then
|
||||
local h = ws:GetPartBoundsInBox(p.CFrame, probe, op)
|
||||
if #h > 0 then
|
||||
hits += 1
|
||||
local on = h[1].Name
|
||||
byObst[on] = (byObst[on] or 0) + 1
|
||||
local rn = p.Name:gsub("_%d+$", "")
|
||||
byRamp[rn] = (byRamp[rn] or 0) + 1
|
||||
if #samples < 10 then
|
||||
table.insert(
|
||||
samples,
|
||||
("%s@%.0f,%.0f,%.0f IN %s"):format(p.Name, p.Position.X, p.Position.Y, p.Position.Z, on)
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local out = {}
|
||||
table.insert(
|
||||
out,
|
||||
("CONTROL+=%s(%d) CONTROL-=%s(%d) -> probe %s"):format(
|
||||
controlOK and "PASS" or "FAIL",
|
||||
#ctrl,
|
||||
negOK and "PASS" or "FAIL",
|
||||
#negCtrl,
|
||||
(controlOK and negOK) and "TRUSTWORTHY" or "BROKEN-IGNORE-RESULTS"
|
||||
)
|
||||
)
|
||||
table.insert(out, ("drivable=%d obstacles=%d CLIPPING=%d"):format(#drive, #obst, hits))
|
||||
|
||||
local function dump(t, label)
|
||||
local l = {}
|
||||
for k, v in pairs(t) do
|
||||
table.insert(l, ("%s=%d"):format(k, v))
|
||||
end
|
||||
table.sort(l, function(a, b)
|
||||
return a > b
|
||||
end)
|
||||
local keep = {}
|
||||
for i = 1, math.min(#l, 12) do
|
||||
table.insert(keep, l[i])
|
||||
end
|
||||
table.insert(out, label .. ": " .. table.concat(keep, " "))
|
||||
end
|
||||
if hits > 0 then
|
||||
dump(byObst, "speared obstacles")
|
||||
dump(byRamp, "offending road groups")
|
||||
for _, s in ipairs(samples) do
|
||||
table.insert(out, " " .. s)
|
||||
end
|
||||
end
|
||||
|
||||
return table.concat(out, "\n")
|
||||
Reference in New Issue
Block a user