The PA ring road and the expressway mainline were both at ground level, so the ring parapet stood INSIDE the carriageway (measured -10.0 studs of vertical overlap: a wall in the middle of the road). Reference photos of the real Daikoku Futo PA show the expressway flying OVER the parking apron on pillars, with the PA at ground level and ramps climbing up to meet it. Rebuilt to match: - mainline lifted 38 studs through the junction, eased in/out (max 5.26% grade, within real expressway limits) - PA ring + apron stay at ground level; legacy PARingPillar props removed - both on/off ramps regraded over the full rise with smoothstep easing, segments overlapped 3 studs so wheels can't catch a seam, plus support pillars and edge kerbs where the deck is high Verified in-engine against built parts (tools/diag_daikoku.luau), not just source: clearance now +28.0 studs, both ramps continuous (max step 5.62, no gaps). tools/plot_daikoku.py renders the plan/section for comparison.
162 lines
4.7 KiB
Luau
162 lines
4.7 KiB
Luau
--[[
|
|
verify_convention.luau -- prove the forward convention is now consistent.
|
|
|
|
Deliberately NOT measuring anything derived from a constant. The previous
|
|
harness read Wheel_* positions, which come straight from
|
|
Chassis.WHEELBASE_HALF (5.9) -- identical for every car whether or not any
|
|
fix applied, so it passed vacuously. Here every assertion is a RELATIONSHIP
|
|
that genuinely differs between the broken and fixed builds:
|
|
|
|
1. The steering servo must sit on the axle that LEADS under throttle.
|
|
Broken build: servos on the trailing axle (shopping-cart caster).
|
|
2. The shell nose must point the same way the seat drives.
|
|
Broken build: 180 degrees opposed.
|
|
3. DRIVE_SIGN must spin the wheels toward the direction of travel.
|
|
|
|
Builds each car via the real path (Chassis.tuningFor + _buildForDiag) in
|
|
the Edit datamodel, where require() is fresh.
|
|
]]
|
|
|
|
local SSS = game:GetService("ServerScriptService")
|
|
local RS = game:GetService("ReplicatedStorage")
|
|
|
|
local out = {}
|
|
local function log(...)
|
|
local parts = {}
|
|
for i = 1, select("#", ...) do
|
|
parts[#parts + 1] = tostring((select(i, ...)))
|
|
end
|
|
table.insert(out, table.concat(parts, " "))
|
|
end
|
|
|
|
local function findModule(name: string): ModuleScript?
|
|
for _, root in ipairs({ SSS, RS }) do
|
|
local hit = root:FindFirstChild(name, true)
|
|
if hit and hit:IsA("ModuleScript") then
|
|
return hit
|
|
end
|
|
end
|
|
return nil
|
|
end
|
|
|
|
local cmMod = findModule("CarManager")
|
|
local carsMod = findModule("Cars")
|
|
local chassisMod = findModule("Chassis")
|
|
if not (cmMod and carsMod and chassisMod) then
|
|
return "FATAL: missing module(s)"
|
|
end
|
|
|
|
local CarManager = require(cmMod)
|
|
local Cars = require(carsMod)
|
|
local Chassis = require(chassisMod)
|
|
|
|
if typeof(CarManager._buildForDiag) ~= "function" then
|
|
return "FATAL: _buildForDiag absent -- stale CarManager, refusing to report"
|
|
end
|
|
|
|
log(string.format("Chassis.DRIVE_SIGN = %s", tostring(Chassis.DRIVE_SIGN)))
|
|
log("")
|
|
|
|
local carList = Cars.List or Cars.All
|
|
if typeof(carList) ~= "table" or #carList == 0 then
|
|
return "FATAL: empty car list"
|
|
end
|
|
|
|
log(string.format("%-11s %-22s %-24s %s", "car", "steered axle", "shell nose vs drive", "verdict"))
|
|
log(string.rep("-", 78))
|
|
|
|
local tested, failed = 0, 0
|
|
|
|
for _, car in ipairs(carList) do
|
|
if typeof(car) ~= "table" or not car.Id then
|
|
continue
|
|
end
|
|
|
|
local tuning = Chassis.tuningFor(car)
|
|
local ok, model = pcall(CarManager._buildForDiag, car, tuning)
|
|
if not ok or typeof(model) ~= "Instance" then
|
|
log(string.format("%-11s BUILD FAILED: %s", car.Id, tostring(model)))
|
|
failed += 1
|
|
continue
|
|
end
|
|
|
|
local body = model:FindFirstChild("Body")
|
|
local seat
|
|
for _, d in ipairs(model:GetDescendants()) do
|
|
if d:IsA("VehicleSeat") then
|
|
seat = d
|
|
break
|
|
end
|
|
end
|
|
|
|
if not (body and body:IsA("BasePart") and seat) then
|
|
log(string.format("%-11s NO BODY/SEAT", car.Id))
|
|
failed += 1
|
|
model:Destroy()
|
|
continue
|
|
end
|
|
|
|
tested += 1
|
|
local inv = body.CFrame:Inverse()
|
|
-- Direction of travel under positive throttle, in body-local space.
|
|
local driveZ = inv:VectorToObjectSpace(seat.CFrame.LookVector).Z
|
|
local drivePos = driveZ > 0
|
|
|
|
-- (1) Which axle carries the steering servos?
|
|
local servoZ, servoN = 0, 0
|
|
for _, d in ipairs(model:GetDescendants()) do
|
|
if d:IsA("HingeConstraint") and d.ActuatorType == Enum.ActuatorType.Servo then
|
|
local host = d.Parent
|
|
if host and host:IsA("BasePart") then
|
|
servoZ += (inv * host.CFrame).Z
|
|
servoN += 1
|
|
end
|
|
end
|
|
end
|
|
local steerLeads, steerTxt = false, "no servos"
|
|
if servoN > 0 then
|
|
local sm = servoZ / servoN
|
|
steerLeads = (drivePos and sm > 0) or (not drivePos and sm < 0)
|
|
steerTxt = string.format("z=%+.2f %s", sm, steerLeads and "LEADS" or "TRAILS")
|
|
end
|
|
|
|
-- (2) Shell nose vs direction of travel.
|
|
local shell
|
|
for _, c in ipairs(model:GetChildren()) do
|
|
if c:IsA("Model") then
|
|
shell = c
|
|
break
|
|
end
|
|
end
|
|
local shellOk, shellTxt = true, "no shell"
|
|
if shell then
|
|
local shellLook = shell:GetPivot().LookVector
|
|
local seatLook = seat.CFrame.LookVector
|
|
local a = Vector3.new(shellLook.X, 0, shellLook.Z)
|
|
local b = Vector3.new(seatLook.X, 0, seatLook.Z)
|
|
if a.Magnitude > 1e-4 and b.Magnitude > 1e-4 then
|
|
local deg = math.deg(math.acos(math.clamp(a.Unit:Dot(b.Unit), -1, 1)))
|
|
shellOk = deg < 30
|
|
shellTxt = string.format("%.1f deg %s", deg, shellOk and "OK" or "FLIPPED")
|
|
end
|
|
end
|
|
|
|
local pass = (servoN == 0 or steerLeads) and shellOk
|
|
if not pass then
|
|
failed += 1
|
|
end
|
|
log(string.format("%-11s %-22s %-24s %s", car.Id, steerTxt, shellTxt, pass and "PASS" or "FAIL"))
|
|
model:Destroy()
|
|
end
|
|
|
|
log("")
|
|
if tested == 0 then
|
|
log(">>> NOTHING TESTED -- this is a FAILURE, not a pass.")
|
|
elseif failed > 0 then
|
|
log(string.format(">>> %d/%d FAILED", failed, tested))
|
|
else
|
|
log(string.format(">>> ALL %d CARS PASS: steered axle leads, shell faces travel", tested))
|
|
end
|
|
|
|
return table.concat(out, "\n")
|