Files
tokyo-wangan-racing/tools/probe_floating.luau
T
jesse c297b20aed 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).
2026-08-03 21:15:25 -04:00

140 lines
3.7 KiB
Luau

-- 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")