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,139 @@
|
||||
-- FLOATING SCENERY PROBE.
|
||||
--
|
||||
-- The wide captures show grey slabs/towers hanging in the sky with nothing
|
||||
-- under them. No existing probe catches this: the clip probe only asks
|
||||
-- "does road intersect building", which is 0 and correct. Floating geometry
|
||||
-- is the opposite failure -- parts that touch NOTHING at all.
|
||||
--
|
||||
-- Metric: for each candidate scenery part, raycast straight DOWN from its
|
||||
-- underside and report the distance to the first thing hit. A part sitting
|
||||
-- on ground/deck returns ~0. A floating part returns a large number or nil
|
||||
-- (nothing beneath it at all).
|
||||
--
|
||||
-- CONTROL: the same ray cast from a part known to be seated (a Road part)
|
||||
-- must return a small distance. If the control comes back nil/huge the
|
||||
-- raycast itself is broken and every "floating" result is meaningless --
|
||||
-- the silent-zero trap that produced two phantom defects earlier.
|
||||
|
||||
local ws = game:GetService("Workspace")
|
||||
local map = ws:FindFirstChild("Map")
|
||||
if not map then
|
||||
return "NO MAP"
|
||||
end
|
||||
|
||||
local candidates, roads = {}, {}
|
||||
for _, d in ipairs(map:GetDescendants()) do
|
||||
if d:IsA("BasePart") then
|
||||
local nm = d.Name
|
||||
if nm:sub(1, 5) == "Road_" then
|
||||
table.insert(roads, d)
|
||||
elseif
|
||||
nm:find("Building")
|
||||
or nm:find("Tower")
|
||||
or nm:find("Skyline")
|
||||
or nm:find("Block")
|
||||
or nm:find("Slab")
|
||||
or nm:find("Distant")
|
||||
then
|
||||
table.insert(candidates, d)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local params = RaycastParams.new()
|
||||
params.FilterType = Enum.RaycastFilterType.Exclude
|
||||
|
||||
-- distance from a part's underside straight down to the first hit
|
||||
local function dropDist(p)
|
||||
params.FilterDescendantsInstances = { p }
|
||||
local origin = p.Position - Vector3.new(0, p.Size.Y / 2 + 1, 0)
|
||||
local res = ws:Raycast(origin, Vector3.new(0, -6000, 0), params)
|
||||
if not res then
|
||||
return nil
|
||||
end
|
||||
return (origin - res.Position).Magnitude
|
||||
end
|
||||
|
||||
local out = {}
|
||||
|
||||
-- CONTROL: a road part must find something close beneath it (deck/pier/ground).
|
||||
local ctl, ctlN = nil, "none"
|
||||
for _, rd in ipairs(roads) do
|
||||
local d = dropDist(rd)
|
||||
if d then
|
||||
ctl, ctlN = d, rd.Name
|
||||
break
|
||||
end
|
||||
end
|
||||
if ctl == nil then
|
||||
table.insert(out, "CONTROL=FAIL raycast found nothing under ANY road -- probe is broken, ignore results below")
|
||||
else
|
||||
table.insert(out, ("CONTROL=PASS %.1f studs under %s (raycast works)"):format(ctl, ctlN))
|
||||
end
|
||||
|
||||
-- Classify candidates.
|
||||
local FLOAT_MIN = 60 -- studs of empty air below = floating
|
||||
local floating, seated, voidBelow = 0, 0, 0
|
||||
local worst, worstNm, worstPos = -1, "none", nil
|
||||
local samples = {}
|
||||
for _, p in ipairs(candidates) do
|
||||
local d = dropDist(p)
|
||||
if d == nil then
|
||||
voidBelow += 1
|
||||
if #samples < 8 then
|
||||
table.insert(
|
||||
samples,
|
||||
("%s@%.0f,%.0f,%.0f NOTHING-BELOW"):format(p.Name, p.Position.X, p.Position.Y, p.Position.Z)
|
||||
)
|
||||
end
|
||||
elseif d > FLOAT_MIN then
|
||||
floating += 1
|
||||
if d > worst then
|
||||
worst, worstNm, worstPos = d, p.Name, p.Position
|
||||
end
|
||||
if #samples < 8 then
|
||||
table.insert(
|
||||
samples,
|
||||
("%s@%.0f,%.0f,%.0f drop=%.0f"):format(p.Name, p.Position.X, p.Position.Y, p.Position.Z, d)
|
||||
)
|
||||
end
|
||||
else
|
||||
seated += 1
|
||||
end
|
||||
end
|
||||
|
||||
table.insert(
|
||||
out,
|
||||
("candidates=%d roads=%d | seated=%d floating(>%d)=%d nothingBelow=%d"):format(
|
||||
#candidates,
|
||||
#roads,
|
||||
seated,
|
||||
FLOAT_MIN,
|
||||
floating,
|
||||
voidBelow
|
||||
)
|
||||
)
|
||||
if worstPos then
|
||||
table.insert(
|
||||
out,
|
||||
("WORST %s drop=%.0f at %.0f,%.0f,%.0f"):format(worstNm, worst, worstPos.X, worstPos.Y, worstPos.Z)
|
||||
)
|
||||
end
|
||||
for _, s in ipairs(samples) do
|
||||
table.insert(out, " " .. s)
|
||||
end
|
||||
|
||||
-- Name histogram so we can see WHICH generator made them.
|
||||
local hist = {}
|
||||
for _, p in ipairs(candidates) do
|
||||
local base = p.Name:gsub("_%d+$", "")
|
||||
hist[base] = (hist[base] or 0) + 1
|
||||
end
|
||||
local hl = {}
|
||||
for k, v in pairs(hist) do
|
||||
table.insert(hl, ("%s=%d"):format(k, v))
|
||||
end
|
||||
table.sort(hl)
|
||||
table.insert(out, "byName: " .. table.concat(hl, " "))
|
||||
|
||||
return table.concat(out, "\n")
|
||||
Reference in New Issue
Block a user