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.
186 lines
5.9 KiB
Luau
186 lines
5.9 KiB
Luau
--[[
|
|
verify_fixes.luau -- build all five cars with the NEWLY PUSHED code and
|
|
measure whether the two fixes actually work. Runs in the EDIT datamodel,
|
|
where require() is fresh (no Play-session cache pin), so this is the first
|
|
honest test of these fixes.
|
|
|
|
Checks, per car:
|
|
|
|
FIX 1 -- FACING (hatch_junk, hyper_x rendered backwards)
|
|
Compare mean Z of F* wheel groups vs R* groups in the model's own frame.
|
|
Chassis convention: front axle is built at NEGATIVE Z. So frontZ < rearZ
|
|
means the car faces the right way. hatch_junk / hyper_x previously came
|
|
out reversed because their shell wheel LABELS are swapped, which fooled
|
|
the auto-solver into a 180 degree rotation; SHELL_YAW_OVERRIDE pins them
|
|
to yaw 0.
|
|
|
|
FIX 2 -- WHEEL STANCE (rx7_fc wheels protruding from the bodywork)
|
|
For each wheel, measure |X| of the wheel's visual centre against |X| of
|
|
the axle it hangs from. The old code picked an arbitrary BasePart as the
|
|
wheel root (often a rotor/caliper ~0.96 studs inboard of the tyre) and
|
|
made THAT concentric with the axle, shoving the tyre outboard by the
|
|
difference. Now it picks the largest-volume part (the tyre). Residual
|
|
offset should be near zero; anything > 0.35 studs is a visible protrusion.
|
|
|
|
Everything is measured from the built model, not inferred from source.
|
|
Models are destroyed afterwards so Edit is left clean.
|
|
]]
|
|
|
|
local ServerScriptService = game:GetService("ServerScriptService")
|
|
|
|
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 serverFolder = ServerScriptService:FindFirstChild("Server", true)
|
|
local cmMod = serverFolder and serverFolder:FindFirstChild("CarManager")
|
|
if not cmMod then
|
|
return "FATAL: no CarManager module found"
|
|
end
|
|
|
|
log(
|
|
string.format(
|
|
"CarManager source: %d bytes, SHELL_YAW_OVERRIDE=%s",
|
|
#cmMod.Source,
|
|
tostring(cmMod.Source:find("SHELL_YAW_OVERRIDE", 1, true) ~= nil)
|
|
)
|
|
)
|
|
|
|
local okCM, CarManager = pcall(require, cmMod)
|
|
if not okCM then
|
|
return "FATAL: require(CarManager) failed: " .. tostring(CarManager)
|
|
end
|
|
if typeof(CarManager._buildForDiag) ~= "function" then
|
|
return "FATAL: _buildForDiag is "
|
|
.. typeof(CarManager._buildForDiag)
|
|
.. " -- require() served STALE code, results would be meaningless"
|
|
end
|
|
log("_buildForDiag present -> NEW code is genuinely running")
|
|
|
|
-- find Cars + Tuning modules
|
|
local function findModule(name)
|
|
for _, svc in ipairs({ game:GetService("ReplicatedStorage"), ServerScriptService }) do
|
|
local m = svc:FindFirstChild(name, true)
|
|
if m and m:IsA("ModuleScript") then
|
|
return m
|
|
end
|
|
end
|
|
return nil
|
|
end
|
|
|
|
local carsMod = findModule("Cars")
|
|
local chassisMod = findModule("Chassis")
|
|
if not carsMod then
|
|
return "FATAL: Cars module not found"
|
|
end
|
|
if not chassisMod then
|
|
return "FATAL: Chassis module not found (needed for tuningFor)"
|
|
end
|
|
local Cars = require(carsMod)
|
|
local Chassis = require(chassisMod)
|
|
if typeof(Chassis.tuningFor) ~= "function" then
|
|
return "FATAL: Chassis.tuningFor is " .. typeof(Chassis.tuningFor)
|
|
end
|
|
|
|
local FRONT_NEG_Z = true -- chassis builds front axle at -Z
|
|
local PROTRUDE_TOL = 0.35
|
|
|
|
log("")
|
|
log(string.format("%-10s %8s %8s %-12s | %8s %-10s", "car", "frontZ", "rearZ", "FACING", "maxOff", "STANCE"))
|
|
log(string.rep("-", 74))
|
|
|
|
local built = {}
|
|
local anyFail = false
|
|
|
|
local carList = Cars.List or Cars.All
|
|
if typeof(carList) ~= "table" or #carList == 0 then
|
|
return "FATAL: could not find a non-empty car list (Cars.List). " .. "Refusing to report a vacuous pass."
|
|
end
|
|
log(string.format("car list: %d entries", #carList))
|
|
|
|
for _, car in ipairs(carList) do
|
|
if typeof(car) == "table" and car.Id then
|
|
local tuning = Chassis.tuningFor(car)
|
|
local ok, model = pcall(CarManager._buildForDiag, car, tuning)
|
|
if not ok or typeof(model) ~= "Instance" then
|
|
log(string.format("%-10s BUILD FAILED: %s", car.Id, tostring(model)))
|
|
anyFail = true
|
|
else
|
|
model.Parent = workspace
|
|
built[#built + 1] = model
|
|
local pivotInv = model:GetPivot():Inverse()
|
|
|
|
-- ---- FACING: mean Z of front vs rear wheel groups
|
|
local fS, fN, rS, rN = 0, 0, 0, 0
|
|
-- ---- STANCE: wheel centre |X| vs its axle |X|
|
|
local maxOff = 0
|
|
|
|
for _, d in ipairs(model:GetDescendants()) do
|
|
if d:IsA("BasePart") and d.Name:match("^Wheel_") then
|
|
local lbl = d.Name:sub(7, 7):upper()
|
|
local z = (pivotInv * d.CFrame).Z
|
|
if lbl == "F" then
|
|
fS, fN = fS + z, fN + 1
|
|
elseif lbl == "R" then
|
|
rS, rN = rS + z, rN + 1
|
|
end
|
|
|
|
-- visual tyre centre = centroid of the attached shell parts
|
|
local axleX = (pivotInv * d.CFrame).X
|
|
local sum, n = Vector3.zero, 0
|
|
for _, c in ipairs(d:GetDescendants()) do
|
|
if c:IsA("BasePart") then
|
|
sum, n = sum + c.Position, n + 1
|
|
end
|
|
end
|
|
if n > 0 then
|
|
local visX = (pivotInv * CFrame.new(sum / n)).X
|
|
local off = math.abs(math.abs(visX) - math.abs(axleX))
|
|
if off > maxOff then
|
|
maxOff = off
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
local facing, stance = "n/a", "n/a"
|
|
if fN > 0 and rN > 0 then
|
|
local fz, rz = fS / fN, rS / rN
|
|
local good = FRONT_NEG_Z and (fz < rz) or (fz > rz)
|
|
facing = good and "OK" or "BACKWARDS"
|
|
if not good then
|
|
anyFail = true
|
|
end
|
|
stance = (maxOff <= PROTRUDE_TOL) and "OK" or "PROTRUDING"
|
|
if maxOff > PROTRUDE_TOL then
|
|
anyFail = true
|
|
end
|
|
log(string.format("%-10s %8.2f %8.2f %-12s | %8.3f %-10s", car.Id, fz, rz, facing, maxOff, stance))
|
|
else
|
|
log(string.format("%-10s (no Wheel_* parts found)", car.Id))
|
|
anyFail = true
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
for _, m in ipairs(built) do
|
|
m:Destroy()
|
|
end
|
|
|
|
local tested = #built
|
|
log("")
|
|
if tested == 0 then
|
|
log(">>> NOTHING WAS TESTED -- no cars built. This is a FAILURE, not a pass.")
|
|
elseif anyFail then
|
|
log(string.format(">>> SOME CHECKS FAILED (%d cars tested)", tested))
|
|
else
|
|
log(string.format(">>> ALL CHECKS PASSED (%d cars tested)", tested))
|
|
end
|
|
return table.concat(out, "\n")
|