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:
2026-08-03 21:15:25 -04:00
parent 1d23c187ad
commit c297b20aed
34 changed files with 2206 additions and 46 deletions
+72
View File
@@ -0,0 +1,72 @@
#!/usr/bin/env python3
"""Eyes-on shots at the two REPORTED defects (not generic beauty angles).
Targets:
A entry ramp start (-4122, 47, -7860) -- the 390-stud access gap
B exit ramp start (-4258, 47, -7836)
C mainline junction (-3700, 102, -7184) -- the dY -6.0 step
D ramp vs nearest building(-4419, 223, -6751)
"""
import shutil
import sys
import time
sys.path.insert(0, "/tmp")
import rbx # noqa: E402
OUT = "/home/jesse/roblox/tokyo-wangan-racing/tools/shots"
SHOTS = [
# name, camera pos, look-at
("D1_entry_ramp_start", (-4122 + 210, 47 + 120, -7860 + 210), (-4122, 47, -7860)),
("D2_entry_start_low", (-4122 + 70, 47 + 25, -7860 + 70), (-4122, 55, -7860)),
("D3_exit_ramp_start", (-4258 - 200, 47 + 110, -7836 + 200), (-4258, 47, -7836)),
("D4_junction_step", (-3700 + 190, 102 + 90, -7184 + 190), (-3700, 102, -7184)),
("D5_ramp_vs_building", (-4419 + 520, 223 + 180, -6751 + 520), (-4300, 120, -7100)),
("D6_corridor_along", (-4122, 47 + 260, -7860 + 480), (-4300, 110, -7200)),
]
CAM = """
local cam = workspace.CurrentCamera
cam.CameraType = Enum.CameraType.Scriptable
cam.FieldOfView = {fov}
cam.CFrame = CFrame.lookAt(Vector3.new({px},{py},{pz}), Vector3.new({tx},{ty},{tz}))
return "ok"
"""
def main():
made = []
for name, (px, py, pz), (tx, ty, tz) in SHOTS:
fov = 70 if "low" in name or "along" in name else 55
r = rbx.call(
"execute_luau",
{
"datamodel_type": "Edit",
"code": CAM.format(
fov=fov, px=px, py=py, pz=pz, tx=tx, ty=ty, tz=tz
),
},
wait=12,
timeout=300,
)
if isinstance(r, dict) and r.get("isError"):
print(f"{name}: CAM FAIL {str(r)[:120]}")
continue
time.sleep(1.5)
cap = rbx.call(
"screen_capture", {"capture_id": "ScreenCapture_1"}, wait=22, timeout=300
)
imgs = cap.get("images") if isinstance(cap, dict) else None
if not imgs:
print(f"{name}: CAPTURE FAIL {str(cap)[:140]}")
continue
dest = f"{OUT}/{name}.jpg"
shutil.copy(imgs[0], dest)
made.append(dest)
print(f"{name}: OK -> {dest}")
print(f"\nMADE {len(made)}")
if __name__ == "__main__":
main()
+132
View File
@@ -0,0 +1,132 @@
#!/usr/bin/env python3
"""Eyes-on shots of the PA flyover ramps.
Camera targets are QUERIED FROM THE LIVE GEOMETRY on every run, never
hardcoded. The previous capture script pinned literal coordinates at the
old defect sites; after the ramps were redesigned those coords pointed at
empty air and the shots silently framed the wrong thing. Same class of bug
as the hardcoded yFork=96 -- a constant that stops matching the model.
Each shot names a part and an offset; the part's real position is looked up
in Studio at capture time. If a named part is missing the shot is SKIPPED
with a loud message rather than falling back to a stale guess.
"""
import shutil
import sys
import time
sys.path.insert(0, "/tmp")
import rbx # noqa: E402
OUT = "/home/jesse/roblox/tokyo-wangan-racing/tools/shots"
# name, anchor part, camera offset from anchor, look-at offset from anchor, fov
SHOTS = [
# The flyover itself, from the side -- the money shot: ramp over deck.
("F1_flyover_side", "PAEntryRampCross_100", (300, 150, 300), (0, -20, 0), 55),
# Underneath the crossing, looking up: proves real clearance.
("F2_flyover_under", "PAEntryRampCross_100", (140, -70, 140), (0, 10, 0), 75),
# The spiral descending into the PA apron.
("F3_spiral_into_pa", "PAEntryRampTrunk_200", (340, 220, 340), (-120, -60, -120), 60),
# Exit ramp climbing out of the PA.
("F4_exit_climb", "PAExitRampTrunk_200", (-340, 200, 300), (60, -40, 40), 60),
# Where the descent lands on the far carriageway.
("F5_landing_merge", "PAEntryRampNear_200", (220, 130, 220), (0, -15, 0), 55),
# Wide establishing shot of the whole interchange.
("F6_wide", "PAEntryRampCross_100", (900, 620, 900), (-250, -120, -250), 60),
]
POS_QUERY = """
local ws=game:GetService("Workspace"); local map=ws:FindFirstChild("Map")
if not map then return "NOMAP" end
local want={}
for _,n in ipairs({%s}) do want[n]=true end
local found={}
for _,d in ipairs(map:GetDescendants()) do
if d:IsA("BasePart") and want[d.Name] then
found[#found+1]=("%%s=%%.1f,%%.1f,%%.1f"):format(d.Name,d.Position.X,d.Position.Y,d.Position.Z)
end
end
return table.concat(found,"|")
"""
CAM = """
local cam = workspace.CurrentCamera
cam.CameraType = Enum.CameraType.Scriptable
cam.FieldOfView = {fov}
cam.CFrame = CFrame.lookAt(Vector3.new({px},{py},{pz}), Vector3.new({tx},{ty},{tz}))
return "ok"
"""
def fetch_anchors(names):
"""Look up each anchor part's real position in the live datamodel."""
lst = ",".join('"%s"' % n for n in sorted(set(names)))
r = rbx.call(
"execute_luau",
{"datamodel_type": "Edit", "code": POS_QUERY % lst},
wait=40,
timeout=400,
)
txt = str(r)
out = {}
for chunk in txt.split("|"):
if "=" not in chunk:
continue
nm, _, vals = chunk.partition("=")
nm = nm.strip().strip("'\"[]{}() ")
parts = vals.strip().strip("'\"[]{}() ").split(",")
if len(parts) != 3:
continue
try:
out[nm] = tuple(float(v) for v in parts)
except ValueError:
continue
return out
def main():
anchors = fetch_anchors([s[1] for s in SHOTS])
print(f"resolved {len(anchors)} anchors: {sorted(anchors)}")
made, skipped = [], []
for name, anchor, cam_off, look_off, fov in SHOTS:
if anchor not in anchors:
print(f"{name}: SKIP -- anchor {anchor} not found in model")
skipped.append(name)
continue
ax, ay, az = anchors[anchor]
px, py, pz = ax + cam_off[0], ay + cam_off[1], az + cam_off[2]
tx, ty, tz = ax + look_off[0], ay + look_off[1], az + look_off[2]
r = rbx.call(
"execute_luau",
{
"datamodel_type": "Edit",
"code": CAM.format(
fov=fov, px=px, py=py, pz=pz, tx=tx, ty=ty, tz=tz
),
},
wait=12,
timeout=300,
)
if isinstance(r, dict) and r.get("isError"):
print(f"{name}: CAM FAIL {str(r)[:120]}")
skipped.append(name)
continue
time.sleep(1.5)
cap = rbx.call(
"screen_capture", {"capture_id": "ScreenCapture_1"}, wait=22, timeout=300
)
imgs = cap.get("images") if isinstance(cap, dict) else None
if not imgs:
print(f"{name}: CAPTURE FAIL {str(cap)[:140]}")
skipped.append(name)
continue
dest = f"{OUT}/{name}.jpg"
shutil.copy(imgs[0], dest)
made.append(dest)
print(f"{name}: OK ({anchor} @ {ax:.0f},{ay:.0f},{az:.0f}) -> {dest}")
print(f"\nMADE {len(made)} SKIPPED {len(skipped)} {skipped if skipped else ''}")
if __name__ == "__main__":
main()
+25 -4
View File
@@ -16,7 +16,14 @@ for _, d in ipairs(map:GetDescendants()) do
if nm:find("PierColumn") or nm:find("PAMast") or nm:find("GirderRib") then
table.insert(structural, d)
end
for _, base in ipairs({ "PAEntryRampTrunk", "PAEntryRampNear", "PAExitRampTrunk", "PAExitRampNear" }) do
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)
@@ -56,7 +63,14 @@ table.insert(out, "structuralParts=" .. #structural)
local function sortKey(p)
return tonumber(p.Name:match("_(%d+)$")) or 0
end
for _, base in ipairs({ "PAEntryRampTrunk", "PAEntryRampNear", "PAExitRampTrunk", "PAExitRampNear" }) do
for _, base in ipairs({
"PAEntryRampTrunk",
"PAEntryRampCross",
"PAEntryRampNear",
"PAExitRampTrunk",
"PAExitRampCross",
"PAExitRampNear",
}) do
local parts = runs[base]
if not parts then
table.insert(out, base .. ": MISSING")
@@ -73,8 +87,15 @@ for _, base in ipairs({ "PAEntryRampTrunk", "PAEntryRampNear", "PAExitRampTrunk"
end
end
-- Trunk -> Near handoff
for _, pair in ipairs({ { "PAEntryRampTrunk", "PAEntryRampNear" }, { "PAExitRampTrunk", "PAExitRampNear" } }) do
-- 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)
+145
View File
@@ -0,0 +1,145 @@
-- Diagnose the two reported defects:
-- (1) ramps not accessible in either direction (connectivity)
-- (2) ramps pass through buildings (clipping)
-- Uses native GetPartBoundsInBox so this stays fast on ~22k parts.
local ws = game:GetService("Workspace")
local map = ws:FindFirstChild("Map")
if not map then
return "NO MAP"
end
local BUILDING = {
Building = true,
TokyoTower = true,
TowerDeck = true,
TowerAntenna = true,
ShiodomeTower = true,
TowerCrown = true,
DietTower = true,
RoppongiHills = true,
TokyoStation = true,
StationDome = true,
Skytree = true,
GinzaBlock = true,
NationalDiet = true,
BridgeTower = true,
TowerCrossbeam = true,
Structure = true,
PalaceWall = true,
}
local ramp, drivable = {}, {}
for _, d in ipairs(map:GetDescendants()) do
if d:IsA("BasePart") then
local n = d.Name
if n:find("PAEntryRamp") or n:find("PAExitRamp") then
table.insert(ramp, d)
elseif n:match("^Road_%d+$") or n == "PAFloor" then
table.insert(drivable, d)
end
end
end
if #ramp == 0 then
return "no ramp parts"
end
-- ---------- (2) BUILDING CLIP CHECK ----------
local op = OverlapParams.new()
op.MaxParts = 40
local clipHits, clipNames, worst = 0, {}, {}
for _, r in ipairs(ramp) do
local hits = ws:GetPartBoundsInBox(r.CFrame, r.Size, op)
for _, h in ipairs(hits) do
if h ~= r and BUILDING[h.Name] then
clipHits += 1
clipNames[h.Name] = (clipNames[h.Name] or 0) + 1
if #worst < 5 then
table.insert(worst, ("%s@%.0f,%.0f,%.0f"):format(r.Name, r.Position.X, r.Position.Y, r.Position.Z))
end
break
end
end
end
-- CONTROL: same probe against mainline Road_ parts (known-good, should be ~0)
local ctrlHits, ctrlN = 0, 0
for i = 1, #drivable, 9 do
local d = drivable[i]
ctrlN += 1
local hits = ws:GetPartBoundsInBox(d.CFrame, d.Size, op)
for _, h in ipairs(hits) do
if h ~= d and BUILDING[h.Name] then
ctrlHits += 1
break
end
end
end
local cn = {}
for k, v in pairs(clipNames) do
table.insert(cn, k .. "=" .. v)
end
-- ---------- (1) CONNECTIVITY ----------
-- Ramp basename groups, then endpoints of each group by trailing index.
local groups = {}
for _, r in ipairs(ramp) do
local base, idx = r.Name:match("^(.-)_?(%d+)$")
base = base or r.Name
idx = tonumber(idx) or 0
local g = groups[base]
if not g then
g = { lo = math.huge, hi = -math.huge, loP = nil, hiP = nil, n = 0 }
groups[base] = g
end
g.n += 1
if idx < g.lo then
g.lo, g.loP = idx, r
end
if idx > g.hi then
g.hi, g.hiP = idx, r
end
end
-- For an endpoint, find nearest drivable surface and report gap + height delta.
local function nearestDrivable(p)
local bd, bp = math.huge, nil
for _, d in ipairs(drivable) do
local dd = (d.Position - p).Magnitude
if dd < bd then
bd, bp = dd, d
end
end
return bd, bp
end
local lines = {}
for base, g in pairs(groups) do
if g.loP and g.hiP then
local d1, p1 = nearestDrivable(g.loP.Position)
local d2, p2 = nearestDrivable(g.hiP.Position)
table.insert(
lines,
("%s n=%d | START gap=%.0f dY=%.1f to %s | END gap=%.0f dY=%.1f to %s"):format(
base,
g.n,
d1,
p1 and (g.loP.Position.Y - p1.Position.Y) or -999,
p1 and p1.Name or "nil",
d2,
p2 and (g.hiP.Position.Y - p2.Position.Y) or -999,
p2 and p2.Name or "nil"
)
)
end
end
return ("CLIP ramp=%d/%d parts hit buildings [%s] samples=[%s] || CONTROL road %d/%d hit\n%s"):format(
clipHits,
#ramp,
table.concat(cn, ", "),
table.concat(worst, " ; "),
ctrlHits,
ctrlN,
table.concat(lines, "\n")
)
+91
View File
@@ -0,0 +1,91 @@
-- Why did the building-clip probe return 0 with a 0 control? Diagnose the metric.
local ws = game:GetService("Workspace")
local map = ws:FindFirstChild("Map")
local BUILDING = {
Building = true,
TokyoTower = true,
TowerDeck = true,
TowerAntenna = true,
ShiodomeTower = true,
TowerCrown = true,
DietTower = true,
RoppongiHills = true,
TokyoStation = true,
StationDome = true,
Skytree = true,
GinzaBlock = true,
NationalDiet = true,
BridgeTower = true,
TowerCrossbeam = true,
Structure = true,
PalaceWall = true,
}
local ramp, bld = {}, {}
local anyQueryOff, anyCollideOff = 0, 0
for _, d in ipairs(ws:GetDescendants()) do
if d:IsA("BasePart") then
if d.Name:find("PAEntryRamp") or d.Name:find("PAExitRamp") then
table.insert(ramp, d)
elseif BUILDING[d.Name] then
table.insert(bld, d)
if not d.CanQuery then
anyQueryOff += 1
end
if not d.CanCollide then
anyCollideOff += 1
end
end
end
end
if #bld == 0 then
return "NO building parts found in Workspace at all (name set wrong?)"
end
-- nearest building to the ramp chain (subsample ramps for speed)
local bd, br, bb = math.huge, nil, nil
for i = 1, #ramp, 7 do
local r = ramp[i]
for _, b in ipairs(bld) do
local dd = (r.Position - b.Position).Magnitude
if dd < bd then
bd, br, bb = dd, r, b
end
end
end
-- how many buildings are anywhere near the PA at all?
local cen = Vector3.new(-4128, 81, -7438)
local near2000 = 0
for _, b in ipairs(bld) do
if (b.Position - cen).Magnitude < 2000 then
near2000 += 1
end
end
-- does the raw overlap API return ANYTHING for a ramp part? (metric sanity)
local op = OverlapParams.new()
op.MaxParts = 50
local sample = ramp[math.floor(#ramp / 2)]
local hits = ws:GetPartBoundsInBox(sample.CFrame, sample.Size * 1.2, op)
local hn = {}
for i = 1, math.min(#hits, 8) do
table.insert(hn, hits[i].Name)
end
return ("buildings=%d (CanQuery off=%d, CanCollide off=%d) | nearestBldToRamp=%.0f (%s -> %s @%.0f,%.0f,%.0f) | bldWithin2000ofPA=%d | overlapSanity hits=%d [%s]"):format(
#bld,
anyQueryOff,
anyCollideOff,
bd,
br and br.Name or "nil",
bb and bb.Name or "nil",
bb and bb.Position.X or 0,
bb and bb.Position.Y or 0,
bb and bb.Position.Z or 0,
near2000,
#hits,
table.concat(hn, ",")
)
+108
View File
@@ -0,0 +1,108 @@
-- Building-clip probe, CORRECTLY SCOPED.
--
-- The previous version tested only PAEntryRamp*/PAExitRamp* against buildings
-- and reported 0/1200 -- but its control ALSO returned 0, which makes the
-- result vacuous. Worse, the part visibly speared by a tower is a Road_*
-- segment, i.e. it was sitting in the control group, not the test group.
--
-- This version:
-- * tests EVERY drivable part (Road_*, ramps, PA surfaces) -- 21k parts
-- * proves the metric can detect a hit BEFORE trusting a zero (positive
-- control: a synthetic part deliberately placed inside a building)
-- * reports the worst offenders with coordinates so they can be shot
local ws = game:GetService("Workspace")
local map = ws:FindFirstChild("Map")
if not map then
return "NO MAP"
end
local BUILDING = {
Building = true,
TokyoTower = true,
TowerDeck = true,
TowerAntenna = true,
ShiodomeTower = true,
TowerCrown = true,
DietTower = true,
RoppongiHills = true,
TokyoStation = true,
StationDome = true,
Skytree = true,
GinzaBlock = true,
NationalDiet = true,
BridgeTower = true,
TowerCrossbeam = true,
PalaceWall = true,
}
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
local drive, bld = {}, {}
for _, d in ipairs(map:GetDescendants()) do
if d:IsA("BasePart") then
if BUILDING[d.Name] then
table.insert(bld, d)
elseif isDrivable(d.Name) then
table.insert(drive, d)
end
end
end
if #bld == 0 or #drive == 0 then
return ("SCOPE FAIL drive=%d bld=%d"):format(#drive, #bld)
end
-- Build an OverlapParams limited to building parts only.
local op = OverlapParams.new()
op.FilterType = Enum.RaycastFilterType.Include
op.FilterDescendantsInstances = bld
op.MaxParts = 12
-- POSITIVE CONTROL: put a probe cube dead-centre inside a building and
-- confirm the metric reports it. If this returns 0, the metric is broken and
-- every other number below is meaningless.
local target = bld[math.floor(#bld / 2)]
local ctrlHits = ws:GetPartBoundsInBox(CFrame.new(target.Position), Vector3.new(4, 4, 4), op)
local controlOK = #ctrlHits > 0
local hits, samples = 0, {}
for _, p in ipairs(drive) do
-- shrink slightly so parts merely TOUCHING a facade aren't flagged;
-- we want genuine interpenetration
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
if #samples < 6 then
table.insert(
samples,
("%s@%.0f,%.0f,%.0f in %s"):format(
p.Name,
p.Position.X,
p.Position.Y,
p.Position.Z,
h[1].Name
)
)
end
end
end
end
return ("CONTROL=%s (%d hits on a part inside a building) || drivable=%d bld=%d CLIPPING=%d [%s]"):format(
controlOK and "PASS" or "FAIL-METRIC-BROKEN",
#ctrlHits,
#drive,
#bld,
hits,
table.concat(samples, " ; ")
)
+146
View File
@@ -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")
+125
View File
@@ -0,0 +1,125 @@
-- CLIP PROBE v4 -- FULL-WORKSPACE SCOPE. This is the one that finds the
-- defect the user can see.
--
-- WHY v2 AND v3 BOTH RETURNED A FALSE ZERO:
-- both scoped obstacles to the `Map` folder. The PA buildings are NOT in
-- Map -- they are Creator-Store models parented directly to Workspace as
-- siblings: McDonald's (1666 parts), Gas Station 1 (1463), 7 eleven (627),
-- Restaurant1, Updated Ramen Shop, Fast Food Restaurant, すき家, Parking lot.
-- The probes literally could not see the things being speared. A passing
-- positive control did NOT save me: the control proved the metric works,
-- not that the SCOPE covers the real obstacles.
--
-- v4 collects obstacles from the ENTIRE workspace, excluding only the Map
-- folder's own drivable/infra parts. Reports per-model hit counts so we know
-- exactly which shop each ramp is driving through.
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
-- collect drivable parts (ramps especially) from Map
local drive = {}
for _, d in ipairs(map:GetDescendants()) do
if d:IsA("BasePart") and isDrivable(d.Name) then
table.insert(drive, d)
end
end
-- collect obstacles: every BasePart in workspace that is NOT inside Map.
-- These are the imported building models.
local obst = {}
local modelOf = {}
for _, top in ipairs(ws:GetChildren()) do
if top ~= map and not top:IsA("Terrain") and not top:IsA("Camera") then
if top:IsA("BasePart") then
-- loose parts like stray PAPerimeter/PARampKerb: skip, they're infra
if not isDrivable(top.Name) and not top.Name:find("Perimeter") and not top.Name:find("Kerb") then
table.insert(obst, top)
modelOf[top] = top.Name
end
else
for _, d in ipairs(top:GetDescendants()) do
if d:IsA("BasePart") then
table.insert(obst, d)
modelOf[d] = top.Name
end
end
end
end
end
if #obst == 0 then
return "SCOPE FAIL: no obstacles found outside Map"
end
local op = OverlapParams.new()
op.FilterType = Enum.RaycastFilterType.Include
op.FilterDescendantsInstances = obst
op.MaxParts = 20
-- CONTROL+ : a box at a known obstacle's centre must report a hit
local tgt = obst[math.floor(#obst / 2)]
local cPos = ws:GetPartBoundsInBox(CFrame.new(tgt.Position), Vector3.new(4, 4, 4), op)
-- CONTROL- : empty sky must report zero
local cNeg = ws:GetPartBoundsInBox(CFrame.new(0, 40000, 0), Vector3.new(8, 8, 8), op)
local trustworthy = #cPos > 0 and #cNeg == 0
local hits = 0
local byModel, byRamp = {}, {}
local samples = {}
for _, p in ipairs(drive) do
local probe = p.Size - Vector3.new(1.0, 1.0, 1.0)
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 mn = modelOf[h[1]] or h[1].Name
byModel[mn] = (byModel[mn] 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, mn)
)
end
end
end
end
local out = {}
table.insert(
out,
("CONTROL+=%d CONTROL-=%d -> %s"):format(#cPos, #cNeg, trustworthy and "TRUSTWORTHY" or "BROKEN-IGNORE")
)
table.insert(out, ("drivable=%d obstacles(outside Map)=%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)
table.insert(out, label .. ": " .. table.concat(l, " | "))
end
if hits > 0 then
dump(byModel, "MODELS SPEARED")
dump(byRamp, "offending ramp groups")
for _, s in ipairs(samples) do
table.insert(out, " " .. s)
end
end
return table.concat(out, "\n")
+139
View File
@@ -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")
+104
View File
@@ -0,0 +1,104 @@
-- FLYOVER PROBE. Two things the connectivity check cannot tell you:
--
-- 1. Does the crossing actually pass OVER the mainline deck, with real
-- vertical clearance? A "flyover" that intersects the deck is just a
-- wall. Measured as: for every Cross part, find mainline Road parts
-- directly beneath it in plan, and report the worst (smallest) gap
-- between the ramp underside and the road topside.
--
-- 2. Does the descent land tangent and flush on the FAR carriageway?
-- Reported as the dY at the last descent part vs the road beneath it.
--
-- Every metric carries a CONTROL that must come back non-zero, because a
-- silent 0 from a mis-scoped query is indistinguishable from a genuine pass
-- -- that exact trap produced two phantom "defects" earlier in this build.
local ws = game:GetService("Workspace")
local map = ws:FindFirstChild("Map")
if not map then
return "NO MAP"
end
local cross, descent, roads = {}, {}, {}
for _, d in ipairs(map:GetDescendants()) do
if d:IsA("BasePart") then
local nm = d.Name
if nm:find("RampCross_") then
table.insert(cross, d)
elseif nm:find("RampNear_") then
table.insert(descent, d)
elseif nm:sub(1, 5) == "Road_" then
table.insert(roads, d)
end
end
end
-- plan-view overlap (ignores Y entirely)
local function planOverlap(a, b)
return math.abs(a.Position.X - b.Position.X) < (a.Size.X + b.Size.X) / 2
and math.abs(a.Position.Z - b.Position.Z) < (a.Size.Z + b.Size.Z) / 2
end
local out = {}
-- CONTROL 1: a road part must plan-overlap itself.
local ctlSelf = (#roads > 0 and planOverlap(roads[1], roads[1])) and 1 or 0
table.insert(out, ("CONTROL planOverlap-self=%d (want 1)"):format(ctlSelf))
-- CLEARANCE over the deck.
local worst, worstAt, nOver = 1e9, "none", 0
for _, cp in ipairs(cross) do
for _, rd in ipairs(roads) do
if planOverlap(cp, rd) then
nOver += 1
-- ramp underside minus road topside
local gap = (cp.Position.Y - cp.Size.Y / 2) - (rd.Position.Y + rd.Size.Y / 2)
if gap < worst then
worst, worstAt = gap, cp.Name .. " over " .. rd.Name
end
end
end
end
if nOver == 0 then
table.insert(out, "CROSSING: 0 road parts under any Cross part -- ramp does NOT span the deck")
else
table.insert(out, ("CROSSING: %d overlaps | worst vertical gap=%.1f at %s"):format(nOver, worst, worstAt))
end
-- LANDING: last descent part of each ramp vs the road under it.
local function tailOf(prefix)
local best, bestIdx = nil, -1
for _, p in ipairs(descent) do
if p.Name:sub(1, #prefix) == prefix then
local i = tonumber(p.Name:match("_(%d+)$")) or 0
if i > bestIdx then
best, bestIdx = p, i
end
end
end
return best
end
for _, pre in ipairs({ "PAEntryRampNear", "PAExitRampNear" }) do
local t = tailOf(pre)
if not t then
table.insert(out, pre .. ": no tail part")
else
local bestGap, bestNm = 1e9, "none"
for _, rd in ipairs(roads) do
if planOverlap(t, rd) then
local dy = (t.Position.Y - t.Size.Y / 2) - (rd.Position.Y + rd.Size.Y / 2)
if math.abs(dy) < math.abs(bestGap) then
bestGap, bestNm = dy, rd.Name
end
end
end
if bestNm == "none" then
table.insert(out, ("%s LANDING: tail is over NO road -- lands off the carriageway"):format(pre))
else
table.insert(out, ("%s LANDING: dY=%.1f onto %s"):format(pre, bestGap, bestNm))
end
end
end
table.insert(out, ("counts cross=%d descent=%d roads=%d"):format(#cross, #descent, #roads))
return table.concat(out, "\n")
+119
View File
@@ -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")
+109
View File
@@ -0,0 +1,109 @@
-- What actually sits near the four ramp ENDS? The user reports ramps going
-- through buildings; the clip probe only found pillars vs perimeter wall.
-- So: name every non-drivable part within 140 studs of each ramp end, and
-- separately list PA-area structures with their footprints, so the ramp
-- can be re-aimed at a genuinely clear corridor instead of guessed at.
local ws = game:GetService("Workspace")
local map = ws:FindFirstChild("Map")
if not map then
return "NO MAP"
end
local WANT = {
PAEntryRampNear_200 = true,
PAExitRampNear_200 = true,
PAEntryRampTrunk_1 = true,
PAExitRampTrunk_1 = true,
}
local ends = {}
local all = {}
for _, d in ipairs(map:GetDescendants()) do
if d:IsA("BasePart") then
if WANT[d.Name] then
ends[d.Name] = d.Position
end
table.insert(all, d)
end
end
local function drivable(n)
return n:match("^Road_") or n:find("Ramp") or n == "PAFloor" or n == "Ground" or n:find("Water")
end
local out = {}
for n, p in pairs(ends) do
local near = {}
local closest, closeName = 1e9, "none"
for _, d in ipairs(all) do
if not drivable(d.Name) then
local dist = (d.Position - p).Magnitude
if dist < 140 then
near[d.Name] = (near[d.Name] or 0) + 1
if dist < closest then
closest, closeName = dist, d.Name
end
end
end
end
local l = {}
for k, v in pairs(near) do
table.insert(l, ("%s x%d"):format(k, v))
end
table.sort(l)
local keep = {}
for i = 1, math.min(#l, 10) do
table.insert(keep, l[i])
end
table.insert(
out,
("%s @%.0f,%.0f,%.0f closest=%s(%.0f) near: %s"):format(
n,
p.X,
p.Y,
p.Z,
closeName,
closest,
table.concat(keep, ", ")
)
)
end
-- PA-area structures with footprints so we can aim at a clear corridor.
table.insert(out, "--- PA structures (within 700 of PA centre) ---")
local paf = nil
for _, d in ipairs(all) do
if d.Name == "PAFloor" then
paf = d
end
end
if paf then
local c = paf.Position
table.insert(out, ("PAFloor centre %.0f,%.0f,%.0f size %.0fx%.0f"):format(c.X, c.Y, c.Z, paf.Size.X, paf.Size.Z))
local seen = {}
local rows = {}
for _, d in ipairs(all) do
if not drivable(d.Name) and (d.Position - c).Magnitude < 700 then
local base = d.Name:gsub("_%d+$", "")
if not seen[base] then
seen[base] = { minx = 1e9, maxx = -1e9, minz = 1e9, maxz = -1e9, n = 0 }
end
local s = seen[base]
s.n += 1
s.minx = math.min(s.minx, d.Position.X - d.Size.X / 2)
s.maxx = math.max(s.maxx, d.Position.X + d.Size.X / 2)
s.minz = math.min(s.minz, d.Position.Z - d.Size.Z / 2)
s.maxz = math.max(s.maxz, d.Position.Z + d.Size.Z / 2)
end
end
for k, s in pairs(seen) do
table.insert(rows, ("%s n=%d X[%.0f..%.0f] Z[%.0f..%.0f]"):format(k, s.n, s.minx, s.maxx, s.minz, s.maxz))
end
table.sort(rows)
for _, r in ipairs(rows) do
table.insert(out, " " .. r)
end
end
return table.concat(out, "\n")
+103
View File
@@ -0,0 +1,103 @@
-- Geometry facts needed to re-cut the ramps so they (a) merge at the highway
-- EDGE instead of landing on top of it, (b) run a corridor that misses the PA
-- shops, and (c) get pole/beam supports instead of whatever's there now.
--
-- Outputs:
-- 1. Mainline cross-section at the PA: per-carriageway edge X/Z + width, so
-- a merge can target the OUTER edge rather than the deck centreline.
-- 2. Plan-footprint of every shop model near the PA, as an X/Z box, so a
-- clear corridor can be computed instead of guessed.
-- 3. Current support part sizes/shapes, to see what needs replacing.
local ws = game:GetService("Workspace")
local map = ws:FindFirstChild("Map")
local out = {}
-- 1. mainline cross-section near the PA (sample by Z band)
local band = {}
for _, d in ipairs(map:GetDescendants()) do
if d:IsA("BasePart") and d.Name:match("^Road_") then
local p = d.Position
if p.Z > -7250 and p.Z < -7050 and p.X > -4700 and p.X < -3400 then
table.insert(band, d)
end
end
end
table.sort(band, function(a, b)
return a.Position.Z < b.Position.Z
end)
table.insert(out, ("mainline sample parts in band=%d"):format(#band))
-- cluster by Z to find the two carriageways
local zmin, zmax = 1e9, -1e9
for _, d in ipairs(band) do
zmin = math.min(zmin, d.Position.Z - d.Size.Z / 2)
zmax = math.max(zmax, d.Position.Z + d.Size.Z / 2)
end
table.insert(out, ("mainline Z extent %.1f .. %.1f (total %.1f)"):format(zmin, zmax, zmax - zmin))
for i = 1, math.min(#band, 6) do
local d = band[i]
table.insert(
out,
(" %s pos %.0f,%.1f,%.1f size %.1fx%.1f rotY=%.0f"):format(
d.Name,
d.Position.X,
d.Position.Y,
d.Position.Z,
d.Size.X,
d.Size.Z,
math.deg(select(2, d.CFrame:ToEulerAnglesYXZ()))
)
)
end
-- 2. shop model footprints near the PA
table.insert(out, "--- shop model footprints (plan boxes) ---")
for _, top in ipairs(ws:GetChildren()) do
if top ~= map and not top:IsA("Terrain") and not top:IsA("Camera") and not top:IsA("BasePart") then
local x0, x1, z0, z1, y0, y1, n = 1e9, -1e9, 1e9, -1e9, 1e9, -1e9, 0
for _, d in ipairs(top:GetDescendants()) do
if d:IsA("BasePart") then
n += 1
x0 = math.min(x0, d.Position.X - d.Size.X / 2)
x1 = math.max(x1, d.Position.X + d.Size.X / 2)
z0 = math.min(z0, d.Position.Z - d.Size.Z / 2)
z1 = math.max(z1, d.Position.Z + d.Size.Z / 2)
y0 = math.min(y0, d.Position.Y - d.Size.Y / 2)
y1 = math.max(y1, d.Position.Y + d.Size.Y / 2)
end
end
if n > 0 and x0 > -6000 and x1 < -2500 and z0 > -9000 and z1 < -6000 then
table.insert(
out,
(" %-42s n=%-5d X[%.0f..%.0f] Z[%.0f..%.0f] Y[%.0f..%.0f]"):format(top.Name, n, x0, x1, z0, z1, y0, y1)
)
end
end
end
-- 3. what the current supports look like
table.insert(out, "--- current ramp supports ---")
local sup = {}
for _, d in ipairs(ws:GetDescendants()) do
if d:IsA("BasePart") and (d.Name:find("RampPillar") or d.Name:find("PierColumn") or d.Name:find("PierCap")) then
local k = ("%s shape=%s size=%.1fx%.1fx%.1f"):format(
d.Name,
d.Shape and d.Shape.Name or "n/a",
d.Size.X,
d.Size.Y,
d.Size.Z
)
sup[k] = (sup[k] or 0) + 1
end
end
local sl = {}
for k, v in pairs(sup) do
table.insert(sl, ("%s x%d"):format(k, v))
end
table.sort(sl)
for i = 1, math.min(#sl, 12) do
table.insert(out, " " .. sl[i])
end
return table.concat(out, "\n")