Files
tokyo-wangan-racing/tools/probe_slip_envelope.luau
jesse 6e50f9718d
CI / quality (push) Failing after 1m9s
Aux-lane suppression + PA geometry fixes; probe/verify tooling
MapBuilder.luau
  - Suppress the aux lane across samples 525-550 (AUX_SUPPRESS_FROM/TO),
    honoring the user's hand deletion in Studio so a rebuild no longer
    regenerates it.
  - Smoothstep the width ramp over AUX_BLEND=5 samples on each side. A hard
    cut produced a 26-stud lateral fence jog; the blend removes it.
  - DO NOT remove this suppression. Re-enabling it resurrects geometry the
    user deleted by hand.

rokit.toml
  - rojo 7.5.1 -> 7.7.0 to match the auto-updating Studio plugin.
    Server older than plugin fails the handshake with
    "attempt to index number with 'protocolVersion'".

tools/
  - 34 read-only probe/verify/capture scripts used to measure the datamodel
    instead of reasoning from source.

Verified-good baseline at this commit:
  total parts       16788
  PAWestSlip        60 segs, 34.4 deg total bend, worst 1.4, 0 kinks >5
  PAEastSlip        60 segs, 30.7 deg total bend, worst 1.3, 0 kinks >5
  AuxLane parts     30 (samples 525-550 deliberately absent)
  barrier coverage  823 samples, 0 unguarded
  fence jogs >10st  0
2026-08-06 08:27:47 -04:00

110 lines
4.1 KiB
Luau

-- Measure the envelope a SIMPLE straight slip-ramp must live in.
-- User's drawing: short stubs peeling off the mainline at a shallow angle,
-- running roughly PARALLEL to the highway in the strip between the mainline
-- edge and the PA lot's north edge, descending an S onto the apron.
--
-- We need to know, in real numbers:
-- 1. Where the mainline actually runs across the PA frontage (samples,
-- heading, deck Y) -- is it straight enough for a parallel slip?
-- 2. Where the lot's north edge is, and how wide the clear strip is
-- between the mainline's south edge and that lot edge.
-- 3. Deck Y vs apron Y (the height the S-curve must lose).
-- 4. What is already sitting in that strip that a ramp would hit.
local RS = game:GetService("ReplicatedStorage")
local HttpService = game:GetService("HttpService")
local Circuit = require(RS:WaitForChild("Shared"):WaitForChild("Circuit"))
local samples = Circuit.samples
local n = #samples
local c = Circuit.PA_CENTER
-- Nearest mainline sample to the PA centre.
local bestIdx, bestD = 1, math.huge
for i = 1, n do
local p = samples[i].pos
local d = (Vector3.new(p.X, 0, p.Z) - Vector3.new(c.X, 0, c.Z)).Magnitude
if d < bestD then bestD, bestIdx = d, i end
end
-- Walk a band around the PA frontage and report heading + deck Y, so we can
-- see how straight the mainline is and where a parallel slip could sit.
local band = {}
for k = -40, 40, 5 do
local i = ((bestIdx - 1 + k) % n) + 1
local s = samples[i].pos
local nx = samples[(i % n) + 1].pos
local hdg = math.deg(math.atan2(nx.Z - s.Z, nx.X - s.X))
local d = (Vector3.new(s.X, 0, s.Z) - Vector3.new(c.X, 0, c.Z)).Magnitude
table.insert(band, {
k = k, idx = i,
x = math.floor(s.X), y = math.floor(s.Y + 0.5), z = math.floor(s.Z),
hdg = math.floor(hdg + 0.5),
distPA = math.floor(d),
})
end
-- The apron / lot floor: bounds and surface height.
local map = workspace:FindFirstChild("Map")
local floor = map and map:FindFirstChild("PAFloor", true)
local lot = nil
if floor then
lot = {
name = floor.Name,
surfaceY = floor.Position.Y + floor.Size.Y / 2,
x_min = math.floor(floor.Position.X - floor.Size.X / 2),
x_max = math.floor(floor.Position.X + floor.Size.X / 2),
z_min = math.floor(floor.Position.Z - floor.Size.Z / 2),
z_max = math.floor(floor.Position.Z + floor.Size.Z / 2),
}
end
-- What already occupies the strip between the mainline and the lot's north
-- edge? Anything here is a collision candidate for a parallel slip ramp.
-- Strip = north third of the lot bbox, i.e. Z above (z_max - 220).
local obstacles = {}
if lot then
local stripZ = lot.z_max - 220
for _, d in ipairs(workspace:GetDescendants()) do
if d:IsA("BasePart") then
local p = d.Position
if p.Z >= stripZ and p.Z <= lot.z_max + 160
and p.X >= lot.x_min - 60 and p.X <= lot.x_max + 60 then
local key = d.Name
local e = obstacles[key]
if not e then
e = { n = 0, x_min = 1e9, x_max = -1e9, z_min = 1e9,
z_max = -1e9, y_max = -1e9 }
obstacles[key] = e
end
e.n += 1
e.x_min = math.min(e.x_min, p.X); e.x_max = math.max(e.x_max, p.X)
e.z_min = math.min(e.z_min, p.Z); e.z_max = math.max(e.z_max, p.Z)
e.y_max = math.max(e.y_max, p.Y + d.Size.Y / 2)
end
end
end
end
local obsOut = {}
for k, e in pairs(obstacles) do
table.insert(obsOut, {
name = k, n = e.n,
x = math.floor(e.x_min) .. ".." .. math.floor(e.x_max),
z = math.floor(e.z_min) .. ".." .. math.floor(e.z_max),
ytop = math.floor(e.y_max),
})
end
table.sort(obsOut, function(a, b) return a.n > b.n end)
while #obsOut > 25 do table.remove(obsOut) end
return HttpService:JSONEncode({
bestIdx = bestIdx,
nearestDist = math.floor(bestD),
pa_center = { math.floor(c.X), math.floor(c.Y), math.floor(c.Z) },
pa_radius = Circuit.PA_RADIUS,
band = band,
lot = lot,
strip_obstacles = obsOut,
})