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.
96 lines
2.7 KiB
Luau
96 lines
2.7 KiB
Luau
--[[
|
|
diag_shellyaw2.luau -- corrected. The first attempt looked in the wrong
|
|
place (CarShells/Shells/Cars) and then measured the CHASSIS wheels, so it
|
|
reported an identical -11.80 wheelbase for every car and proved nothing.
|
|
|
|
Shells actually live in ReplicatedStorage.CarModels[car.Id] (CarManager
|
|
line 347). This inspects those templates directly and reports the exact
|
|
cue the auto-aligner uses, plus the resulting yaw.
|
|
]]
|
|
|
|
local ReplicatedStorage = 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 models = ReplicatedStorage:FindFirstChild("CarModels")
|
|
if not models then
|
|
log("ReplicatedStorage.CarModels: MISSING -> every car uses the procedural")
|
|
log("CarBody shell, which is authored nose-toward -Z and cannot be backwards.")
|
|
return table.concat(out, "\n")
|
|
end
|
|
|
|
log("ReplicatedStorage.CarModels children:")
|
|
for _, t in ipairs(models:GetChildren()) do
|
|
log(string.format(" %-14s (%s)", t.Name, t.ClassName))
|
|
end
|
|
log("")
|
|
|
|
for _, template in ipairs(models:GetChildren()) do
|
|
if template:IsA("Model") then
|
|
local wf = template:FindFirstChild("Wheels")
|
|
if not wf then
|
|
log(
|
|
string.format(
|
|
"%-14s Wheels folder MISSING -> shellYaw = 0 (fails soft, keeps authored yaw)",
|
|
template.Name
|
|
)
|
|
)
|
|
else
|
|
local pivotInv = template:GetPivot():Inverse()
|
|
local fSum, fN, rSum, rN = Vector3.zero, 0, Vector3.zero, 0
|
|
local names = {}
|
|
for _, w in ipairs(wf:GetChildren()) do
|
|
table.insert(names, w.Name)
|
|
local part = w:IsA("BasePart") and w or w:FindFirstChildWhichIsA("BasePart", true)
|
|
if part then
|
|
local h = w.Name:sub(1, 1):upper()
|
|
local lp = pivotInv * part.Position
|
|
if h == "F" then
|
|
fSum, fN = fSum + lp, fN + 1
|
|
elseif h == "R" then
|
|
rSum, rN = rSum + lp, rN + 1
|
|
end
|
|
end
|
|
end
|
|
if fN > 0 and rN > 0 then
|
|
local nose = (fSum / fN) - (rSum / rN)
|
|
local usable = (math.abs(nose.X) + math.abs(nose.Z)) > 0.05
|
|
local yaw = usable and -math.atan2(nose.X, -nose.Z) or 0
|
|
log(
|
|
string.format(
|
|
"%-14s [%s] F=%d R=%d nose=(%.2f,%.2f,%.2f) -> yaw %+.1f deg %s",
|
|
template.Name,
|
|
table.concat(names, ","),
|
|
fN,
|
|
rN,
|
|
nose.X,
|
|
nose.Y,
|
|
nose.Z,
|
|
math.deg(yaw),
|
|
usable and "" or "*** DEGENERATE -> yaw 0 ***"
|
|
)
|
|
)
|
|
else
|
|
log(
|
|
string.format(
|
|
"%-14s [%s] F=%d R=%d -> *** NO F/R CUE, shellYaw = 0, stays as authored ***",
|
|
template.Name,
|
|
table.concat(names, ","),
|
|
fN,
|
|
rN
|
|
)
|
|
)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
return table.concat(out, "\n")
|