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,119 @@
|
||||
-- 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")
|
||||
Reference in New Issue
Block a user