Daikoku PA: lift mainline over the apron, rebuild both ramps
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.
This commit is contained in:
@@ -0,0 +1,184 @@
|
||||
--[[
|
||||
diag_wheelroot2.luau -- same investigation as diag_wheelroot, but wrapped
|
||||
so a failure reports WHERE. The first attempt died with a bare "attempt to
|
||||
call a nil value" and no line, which tells us nothing; both _buildForDiag
|
||||
(CarManager:988) and Chassis.tuningFor (Chassis:158) are confirmed to
|
||||
exist, so the nil is something else in the script.
|
||||
|
||||
Runs each stage under pcall + traceback and prints progress markers, so
|
||||
the last marker printed localises the fault even if the traceback is
|
||||
swallowed by the executor.
|
||||
]]
|
||||
|
||||
local ServerScriptService = game:GetService("ServerScriptService")
|
||||
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
||||
local RunService = game:GetService("RunService")
|
||||
|
||||
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 stage(name, fn)
|
||||
local ok, err = xpcall(fn, function(e)
|
||||
return tostring(e) .. "\n" .. debug.traceback("", 2)
|
||||
end)
|
||||
if not ok then
|
||||
log("!! FAILED at stage: " .. name)
|
||||
log(err)
|
||||
return false
|
||||
end
|
||||
log(" ok: " .. name)
|
||||
return true
|
||||
end
|
||||
|
||||
local Shared, Cars, Chassis, CarManager, cm
|
||||
|
||||
stage("require shared", function()
|
||||
Shared = ReplicatedStorage:FindFirstChild("Shared")
|
||||
Cars = require(Shared.Cars)
|
||||
Chassis = require(Shared.Chassis)
|
||||
end)
|
||||
|
||||
stage("clone+require CarManager", function()
|
||||
local orig = ServerScriptService:FindFirstChild("Server", true):FindFirstChild("CarManager")
|
||||
cm = orig:Clone()
|
||||
cm.Parent = orig.Parent
|
||||
CarManager = require(cm)
|
||||
assert(typeof(CarManager._buildForDiag) == "function", "_buildForDiag is " .. typeof(CarManager._buildForDiag))
|
||||
assert(typeof(Chassis.tuningFor) == "function", "tuningFor is " .. typeof(Chassis.tuningFor))
|
||||
assert(typeof(Cars.Get) == "function", "Cars.Get is " .. typeof(Cars.Get))
|
||||
end)
|
||||
|
||||
-- ------------------------------------------------- stage 1: template roots
|
||||
stage("template root pick", function()
|
||||
log("")
|
||||
log("=== TEMPLATE: what does line 589 pick as root? ===")
|
||||
log(string.format("%-12s %-5s %-18s %8s %7s", "car", "grp", "root picked", "offX", "nParts"))
|
||||
for _, t in ipairs(ReplicatedStorage.CarModels:GetChildren()) do
|
||||
local wg = t:FindFirstChild("Wheels")
|
||||
if wg then
|
||||
for _, w in ipairs(wg:GetChildren()) do
|
||||
local root = w:IsA("BasePart") and w or w:FindFirstChildWhichIsA("BasePart", true)
|
||||
local parts = {}
|
||||
if w:IsA("BasePart") then
|
||||
parts[1] = w
|
||||
end
|
||||
for _, d in ipairs(w:GetDescendants()) do
|
||||
if d:IsA("BasePart") then
|
||||
parts[#parts + 1] = d
|
||||
end
|
||||
end
|
||||
if root and #parts > 0 then
|
||||
local lo, hi = parts[1].Position, parts[1].Position
|
||||
for _, p in ipairs(parts) do
|
||||
lo = Vector3.new(
|
||||
math.min(lo.X, p.Position.X),
|
||||
math.min(lo.Y, p.Position.Y),
|
||||
math.min(lo.Z, p.Position.Z)
|
||||
)
|
||||
hi = Vector3.new(
|
||||
math.max(hi.X, p.Position.X),
|
||||
math.max(hi.Y, p.Position.Y),
|
||||
math.max(hi.Z, p.Position.Z)
|
||||
)
|
||||
end
|
||||
local centre = (lo + hi) / 2
|
||||
log(
|
||||
string.format(
|
||||
"%-12s %-5s %-18s %8.3f %7d",
|
||||
t.Name,
|
||||
w.Name,
|
||||
root.Name,
|
||||
math.abs(root.Position.X - centre.X),
|
||||
#parts
|
||||
)
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
-- ------------------------------------------------- stage 2: built cars
|
||||
stage("built car check", function()
|
||||
log("")
|
||||
log("=== BUILT: tyre offset from its physics cylinder + stray welds ===")
|
||||
log(string.format("%-12s %-5s %9s %s", "car", "grp", "tyreErrX", "welds to OUTSIDE the group"))
|
||||
for _, t in ipairs(ReplicatedStorage.CarModels:GetChildren()) do
|
||||
local car = Cars.Get(t.Name)
|
||||
if car then
|
||||
local m = CarManager._buildForDiag(car, Chassis.tuningFor(car))
|
||||
m.Parent = workspace
|
||||
m:PivotTo(CFrame.new(0, 5010, 0))
|
||||
for _ = 1, 4 do
|
||||
RunService.Heartbeat:Wait()
|
||||
end
|
||||
local body = m.PrimaryPart
|
||||
local shell = m:FindFirstChild("Shell")
|
||||
local wg = shell and shell:FindFirstChild("Wheels")
|
||||
if wg and body then
|
||||
local inv = body.CFrame:Inverse()
|
||||
for _, w in ipairs(wg:GetChildren()) do
|
||||
local physName = "Wheel_" .. w.Name:upper()
|
||||
local phys = m:FindFirstChild(physName)
|
||||
local group, inGroup = {}, {}
|
||||
if w:IsA("BasePart") then
|
||||
group[1], inGroup[w] = w, true
|
||||
end
|
||||
for _, d in ipairs(w:GetDescendants()) do
|
||||
if d:IsA("BasePart") then
|
||||
group[#group + 1] = d
|
||||
inGroup[d] = true
|
||||
end
|
||||
end
|
||||
if phys and #group > 0 then
|
||||
local physX = (inv * phys.Position).X
|
||||
local best, bestV = group[1], -1
|
||||
for _, p in ipairs(group) do
|
||||
local v = p.Size.X * p.Size.Y * p.Size.Z
|
||||
if v > bestV then
|
||||
best, bestV = p, v
|
||||
end
|
||||
end
|
||||
local errX = (inv * best.Position).X - physX
|
||||
local bad = {}
|
||||
for _, c in ipairs(m:GetDescendants()) do
|
||||
if c:IsA("WeldConstraint") or c:IsA("Weld") then
|
||||
local a, b = c.Part0, c.Part1
|
||||
local aIn = (a ~= nil) and inGroup[a] or false
|
||||
local bIn = (b ~= nil) and inGroup[b] or false
|
||||
if (aIn or bIn) and not (aIn and bIn) then
|
||||
local other = aIn and b or a
|
||||
local oname = other and other.Name or "nil"
|
||||
if oname ~= physName then
|
||||
bad[#bad + 1] = oname
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
log(
|
||||
string.format(
|
||||
"%-12s %-5s %9.3f %s",
|
||||
t.Name,
|
||||
w.Name,
|
||||
errX,
|
||||
#bad > 0 and table.concat(bad, ", ") or "-none-"
|
||||
)
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
m:Destroy()
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
if cm then
|
||||
cm:Destroy()
|
||||
end
|
||||
return table.concat(out, "\n")
|
||||
Reference in New Issue
Block a user