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).
120 lines
3.3 KiB
Luau
120 lines
3.3 KiB
Luau
-- GROUND-TRUTH PROBE for the floating-scenery finding.
|
|
--
|
|
-- probe_floating reported 664 parts with "nothing below". That number alone
|
|
-- is ambiguous: it reads identically whether (a) the buildings genuinely
|
|
-- hang in the air, or (b) the map has no ground/terrain plane at all, so
|
|
-- NOTHING can ever return a hit. Distinguish before calling it a defect.
|
|
--
|
|
-- Measure:
|
|
-- 1. Does the map contain terrain / a baseplate / a water or ground slab?
|
|
-- 2. Bottom-face Y of each scenery part vs the road deck Y and vs 0.
|
|
-- A building whose bottom is at/below ~0 is "planted" even with no
|
|
-- ground mesh; one whose bottom is hundreds of studs up is FLOATING.
|
|
|
|
local ws = game:GetService("Workspace")
|
|
local map = ws:FindFirstChild("Map")
|
|
if not map then
|
|
return "NO MAP"
|
|
end
|
|
|
|
local out = {}
|
|
|
|
-- 1. terrain / ground candidates
|
|
local terr = ws:FindFirstChildOfClass("Terrain")
|
|
local terrHas = false
|
|
if terr then
|
|
local ok, reg = pcall(function()
|
|
return terr.MaxExtents
|
|
end)
|
|
-- MaxExtents is a Region3int16; its Min/Max are Vector3int16 which have
|
|
-- no .Magnitude. Compare components instead.
|
|
if ok and reg then
|
|
local mn, mx = reg.Min, reg.Max
|
|
terrHas = (mx.X - mn.X) > 1 or (mx.Y - mn.Y) > 1 or (mx.Z - mn.Z) > 1
|
|
end
|
|
end
|
|
table.insert(out, ("Terrain present=%s hasVoxels=%s"):format(tostring(terr ~= nil), tostring(terrHas)))
|
|
|
|
local groundish = {}
|
|
for _, d in ipairs(ws:GetDescendants()) do
|
|
if d:IsA("BasePart") then
|
|
local n = d.Name:lower()
|
|
if
|
|
n:find("ground")
|
|
or n:find("base")
|
|
or n:find("water")
|
|
or n:find("sea")
|
|
or n:find("bay")
|
|
or n:find("floor")
|
|
then
|
|
local sz = d.Size
|
|
if sz.X > 200 or sz.Z > 200 then
|
|
table.insert(
|
|
groundish,
|
|
("%s size=%.0fx%.0f topY=%.1f"):format(d.Name, sz.X, sz.Z, d.Position.Y + sz.Y / 2)
|
|
)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
table.insert(out, ("large ground-like slabs=%d"):format(#groundish))
|
|
for i = 1, math.min(#groundish, 6) do
|
|
table.insert(out, " " .. groundish[i])
|
|
end
|
|
|
|
-- 2. bottom-face Y of scenery vs deck
|
|
local roadY, roadN = {}, 0
|
|
local scen = {}
|
|
for _, d in ipairs(map:GetDescendants()) do
|
|
if d:IsA("BasePart") then
|
|
local nm = d.Name
|
|
if nm:sub(1, 5) == "Road_" then
|
|
roadN += 1
|
|
table.insert(roadY, d.Position.Y)
|
|
elseif
|
|
nm:find("Building")
|
|
or nm:find("Tower")
|
|
or nm:find("Ginza")
|
|
or nm:find("Shiodome")
|
|
or nm:find("Diet")
|
|
then
|
|
table.insert(scen, d)
|
|
end
|
|
end
|
|
end
|
|
table.sort(roadY)
|
|
local medRoad = roadY[math.max(1, math.floor(#roadY / 2))] or 0
|
|
table.insert(out, ("median road Y=%.1f (n=%d)"):format(medRoad, roadN))
|
|
|
|
local planted, floatHigh = 0, 0
|
|
local hist = {}
|
|
local worst, worstNm, worstP = -1e9, "none", nil
|
|
for _, p in ipairs(scen) do
|
|
local btm = p.Position.Y - p.Size.Y / 2
|
|
if btm <= 5 then
|
|
planted += 1
|
|
else
|
|
floatHigh += 1
|
|
local base = p.Name:gsub("_%d+$", "")
|
|
hist[base] = (hist[base] or 0) + 1
|
|
if btm > worst then
|
|
worst, worstNm, worstP = btm, p.Name, p.Position
|
|
end
|
|
end
|
|
end
|
|
table.insert(out, ("scenery=%d | bottom<=5 (planted)=%d | bottom>5 (airborne)=%d"):format(#scen, planted, floatHigh))
|
|
if worstP then
|
|
table.insert(
|
|
out,
|
|
("HIGHEST bottom: %s btmY=%.0f at %.0f,%.0f,%.0f"):format(worstNm, worst, worstP.X, worstP.Y, worstP.Z)
|
|
)
|
|
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, "airborne byName: " .. (#hl > 0 and table.concat(hl, " ") or "(none)"))
|
|
|
|
return table.concat(out, "\n")
|