Aux-lane suppression + PA geometry fixes; probe/verify tooling
CI / quality (push) Failing after 1m9s
CI / quality (push) Failing after 1m9s
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
This commit is contained in:
@@ -0,0 +1,125 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Capture the PA slip ramps from the user's own viewpoint, LIVE-ANCHORED.
|
||||
|
||||
The user's screenshot looks along the mainline from above the deck with the
|
||||
ramp descending on the left. Reproduce that framing from the actual ramp
|
||||
geometry so the shot cannot drift onto empty air after an edit.
|
||||
"""
|
||||
import re
|
||||
import shutil
|
||||
import sys
|
||||
import time
|
||||
|
||||
sys.path.insert(0, "/tmp")
|
||||
import rbx # noqa: E402
|
||||
|
||||
OUT = "/home/jesse/roblox/tokyo-wangan-racing/tools/shots"
|
||||
|
||||
# Anchors: for each slip -> mouth (first deck seg), mid-span, landing (last seg).
|
||||
ANCHOR_CODE = """
|
||||
local map = workspace:FindFirstChild("Map")
|
||||
if not map then return "NOMAP" end
|
||||
local segs = {}
|
||||
for _, d in ipairs(map:GetDescendants()) do
|
||||
if d:IsA("BasePart") then
|
||||
for _, s in ipairs({"PAWestSlip", "PAEastSlip"}) do
|
||||
local k = d.Name:match("^" .. s .. "_Deck_(%d+)$")
|
||||
if k then
|
||||
segs[s] = segs[s] or {}
|
||||
table.insert(segs[s], {i = tonumber(k), p = d.Position})
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
local out = {}
|
||||
for _, s in ipairs({"PAWestSlip", "PAEastSlip"}) do
|
||||
local g = segs[s]
|
||||
if g and #g > 2 then
|
||||
table.sort(g, function(a, b) return a.i < b.i end)
|
||||
local mouth = g[1].p
|
||||
local mid = g[math.floor(#g / 2)].p
|
||||
local land = g[#g].p
|
||||
table.insert(out, ("%.1f %.1f %.1f"):format(mouth.X, mouth.Y, mouth.Z))
|
||||
table.insert(out, ("%.1f %.1f %.1f"):format(mid.X, mid.Y, mid.Z))
|
||||
table.insert(out, ("%.1f %.1f %.1f"):format(land.X, land.Y, land.Z))
|
||||
end
|
||||
end
|
||||
if #out == 0 then return "NOSLIP" end
|
||||
return table.concat(out, " | ")
|
||||
"""
|
||||
|
||||
CAM_TPL = """
|
||||
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 "cam ok"
|
||||
"""
|
||||
|
||||
|
||||
def grab(name, px, py, pz, tx, ty, tz, fov=55):
|
||||
code = CAM_TPL.format(fov=fov, px=px, py=py, pz=pz, tx=tx, ty=ty, tz=tz)
|
||||
r = rbx.call("execute_luau", {"datamodel_type": "Edit", "code": code})
|
||||
if isinstance(r, dict) and r.get("isError"):
|
||||
print(f"{name}: CAM FAIL {r}")
|
||||
return None
|
||||
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)[:160]}")
|
||||
return None
|
||||
dest = f"{OUT}/{name}.jpg"
|
||||
shutil.copy(imgs[0], dest)
|
||||
print(f"{name}: OK -> {dest}")
|
||||
return dest
|
||||
|
||||
|
||||
def main():
|
||||
a = rbx.call("execute_luau", {"datamodel_type": "Edit", "code": ANCHOR_CODE},
|
||||
wait=40, timeout=300)
|
||||
txt = a if isinstance(a, str) else str(a)
|
||||
if "NOMAP" in txt or "NOSLIP" in txt:
|
||||
print("ANCHOR FAIL: " + txt[:200])
|
||||
return
|
||||
trip = re.findall(r"(-?\d+\.\d+) (-?\d+\.\d+) (-?\d+\.\d+)", txt)
|
||||
if len(trip) < 6:
|
||||
print("ANCHOR PARSE FAIL: " + txt[:300])
|
||||
return
|
||||
pts = [tuple(float(v) for v in t) for t in trip]
|
||||
print("anchors: " + str(pts))
|
||||
|
||||
made = []
|
||||
for n, base in enumerate((0, 3), start=1):
|
||||
mouth, mid, land = pts[base], pts[base + 1], pts[base + 2]
|
||||
tag = ["west", "east"][n - 1]
|
||||
|
||||
# 1. The user's view: high, behind the mouth, looking down the ramp.
|
||||
made.append(grab(
|
||||
f"ramp_{tag}_userview",
|
||||
mouth[0] - 300, mouth[1] + 120, mouth[2] - 300,
|
||||
land[0], land[1], land[2], fov=60))
|
||||
|
||||
# 2. Side elevation: shows the descent profile against the deck.
|
||||
made.append(grab(
|
||||
f"ramp_{tag}_side",
|
||||
mid[0] + 420, mid[1] + 90, mid[2],
|
||||
mid[0], mid[1], mid[2], fov=48))
|
||||
|
||||
# 3. Top-down over mid-span: is it parallel to the deck or cutting away?
|
||||
made.append(grab(
|
||||
f"ramp_{tag}_topdown",
|
||||
mid[0], mid[1] + 560, mid[2],
|
||||
mid[0], mid[1], mid[2], fov=55))
|
||||
|
||||
made = [m for m in made if m]
|
||||
print("\nMADE " + str(len(made)) + " shots")
|
||||
for m in made:
|
||||
print(m)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user