-- Verify the Daikoku Futo PA rebuild against SOURCED real-world figures. -- -- Sources: -- * ja.wikipedia 大黒パーキングエリア -- 小型 341 / 大型 59 / 身障者用 4 = 404 -- * OpenStreetMap way 59608205 -- apron 181 m x 174 m -- -- Runs on a CLONE of MapBuilder so it never disturbs the live map, matching -- the pattern used by verify_c1_fixes3. local ServerScriptService = game:GetService("ServerScriptService") local out = {} local function log(...) table.insert(out, table.concat({ ... }, " ")) end local pass, fail = 0, 0 local function check(name, ok, detail) if ok then pass += 1 log("PASS", name, detail or "") else fail += 1 log("FAIL", name, detail or "") end end local src = ServerScriptService:FindFirstChild("Server") src = src and src:FindFirstChild("MapBuilder") or ServerScriptService:FindFirstChild("MapBuilder", true) if not src then return "FATAL: MapBuilder module not found" end local MapBuilder = require(src:Clone()) local map = MapBuilder.build and MapBuilder.build() or MapBuilder.Build() local pa = map:FindFirstChild("ParkingArea", true) if not pa then return "FATAL: ParkingArea folder not built" end -- 1. Capacity must be EXACTLY the published numbers ------------------------- local cap = pa:FindFirstChild("PACapacity") check("capacity value present", cap ~= nil, cap and cap.Value or "missing") if cap then check("capacity == 341/59/4/404", cap.Value == "small=341,large=59,accessible=4,total=404", cap.Value) end -- 2. Stall line counts must agree with those numbers ------------------------ -- markRow paints n+1 divider lines per row of n stalls. local counts = {} for _, d in ipairs(pa:GetDescendants()) do counts[d.Name] = (counts[d.Name] or 0) + 1 end -- 小型: 11 rows x (31+1) = 352 dividers, 11 head lines check("small stall dividers = 352", counts.StallLine == 352, tostring(counts.StallLine)) check("small stall head lines = 11", counts.StallHead == 11, tostring(counts.StallHead)) -- 大型: rows of 20,20,19 -> 21+21+20 = 62 dividers, 3 heads check("large stall dividers = 62", counts.BigStallLine == 62, tostring(counts.BigStallLine)) check("large stall head lines = 3", counts.BigStallHead == 3, tostring(counts.BigStallHead)) -- 身障者用: 1 row of 4 -> 5 dividers check("accessible dividers = 5", counts.AccStallLine == 5, tostring(counts.AccStallLine)) -- 3. Parking geometry must sit ON the apron slab --------------------------- -- NOTE: scoped to parking furniture only. The first version of this test swept -- every descendant and flagged PARingWall/PARingRail -- the ELEVATED ring road, -- which is supposed to sit at/over the perimeter. That was a bad test, not a -- bad build. local floor = pa:FindFirstChild("PAFloor") check("PAFloor exists", floor ~= nil) local function isParkingFurniture(d) return d.Name:find("Stall") or d.Name:find("PAHonkan") or d.Name:find("PANibankan") or d.Name == "PAEVCharger" or d.Name == "PALowCarBump" or d.Name == "PAMast" end local offSlab, worst = 0, 0 if floor then local hx, hz = floor.Size.X / 2, floor.Size.Z / 2 local fc = floor.Position for _, d in ipairs(pa:GetDescendants()) do if d:IsA("BasePart") and d ~= floor and isParkingFurniture(d) then local dx = math.abs(d.Position.X - fc.X) + d.Size.X / 2 local dz = math.abs(d.Position.Z - fc.Z) + d.Size.Z / 2 local over = math.max(dx - hx, dz - hz) if over > 0.5 then offSlab += 1 worst = math.max(worst, over) if offSlab <= 5 then log(" off-slab:", d.Name, string.format("by %.1f studs", over)) end end end end end check( "no parking geometry overhangs the apron", offSlab == 0, string.format("%d parts, worst %.1f studs", offSlab, worst) ) -- 4. The 小型 block must not collide with the 大型 block -------------------- -- The original test compared consecutive head-line gaps, which is always 0 for -- back-to-back rows (they share a nose line) -- meaningless. It also hid a REAL -- 42-stud overlap between the small rows and the truck bays. Compare Z spans. local function zSpan(prefix) local lo, hi = math.huge, -math.huge for _, d in ipairs(pa:GetDescendants()) do if d:IsA("BasePart") and d.Name:sub(1, #prefix) == prefix then lo = math.min(lo, d.Position.Z - d.Size.Z / 2) hi = math.max(hi, d.Position.Z + d.Size.Z / 2) end end return lo, hi end local _, sHi = zSpan("Stall") local bLo, bHi = zSpan("BigStall") check( "小型 block clears 大型 block", sHi < bLo, string.format("small ends %.1f, large starts %.1f (gap %.1f)", sHi, bLo, bLo - sHi) ) check( "大型 block within apron", floor and bHi < floor.Position.Z + floor.Size.Z / 2, string.format("large ends %.1f", bHi) ) -- 5. Buildings on the correct sides ----------------------------------------- local honkan = pa:FindFirstChild("PAHonkan") local niban = pa:FindFirstChild("PANibankan") check("本館 exists", honkan ~= nil) check("二番館 exists", niban ~= nil) if honkan and floor then check( "本館 on the WEST side", honkan.Position.X < floor.Position.X, string.format("dx %.0f", honkan.Position.X - floor.Position.X) ) end if niban and floor then check( "二番館 on the NORTH side", niban.Position.Z < floor.Position.Z, string.format("dz %.0f", niban.Position.Z - floor.Position.Z) ) end -- 6. Nothing may intersect the buildings ------------------------------------ local clash = 0 for _, b in ipairs({ honkan, niban }) do if b then local hx, hz = b.Size.X / 2, b.Size.Z / 2 for _, d in ipairs(pa:GetDescendants()) do if d:IsA("BasePart") and d ~= b and d.Name:find("Stall") then local dx = math.abs(d.Position.X - b.Position.X) - (hx + d.Size.X / 2) local dz = math.abs(d.Position.Z - b.Position.Z) - (hz + d.Size.Z / 2) if dx < 0 and dz < 0 then clash += 1 if clash <= 5 then log(" clash:", d.Name, b.Name) end end end end end end check("no stalls inside a building", clash == 0, tostring(clash) .. " overlaps") -- 7. Documented amenities --------------------------------------------------- check("EV chargers present (2008 first-in-Japan)", (counts.PAEVCharger or 0) >= 1, tostring(counts.PAEVCharger)) check("low-car bumps present (段差)", (counts.PALowCarBump or 0) >= 1, tostring(counts.PALowCarBump)) check("light masts = 4", counts.PAMast == 4, tostring(counts.PAMast)) check("PASpawn survived", pa:FindFirstChildOfClass("SpawnLocation") ~= nil) map:Destroy() log("") log(string.format("RESULT %d passed, %d failed", pass, fail)) return table.concat(out, "\n")