fix(map): clear PA_LIFT over Honkan; complete ramp kerbs; open the merge
PA_LIFT 38 -> 56. The old value was sized for a car (~7 studs) but the apron carries PAHonkan at 46.4 studs, so the mainline deck cut through the building's top floor (Road_536..539). Clearance must exceed the tallest structure beneath, not just a vehicle. LIFT_RAMP 18 -> 26 so the longer climb keeps the same grade. Light masts 60 -> 40 so they don't spear the newly raised deck (they would have topped out at 107 vs underside 101). Ramp kerbs: the guard was 'h > 8', a height test on a ramp that starts at apron level, so the first 2-3 segments got no kerb -- the barrier visibly stopped part-way down. Now 'h > 2'. Conversely the kerb ran all the way to the mainline end, leaving a 2.4-stud kerb standing in the merge lane (measured Road_526 -> PARampKerb); the final segment is now open. Verified in a live Studio datamodel: Honkan roof 93.4 vs deck underside 101.0 (7.6 studs clear); lane-surface raycast at the PA approach drops from worstBump 4.751 to 0.254 studs.
This commit is contained in:
+251
-42
@@ -977,8 +977,19 @@ local function buildPA(map: Folder)
|
|||||||
-- 6 m aisles) are conventional Japanese parking-design values -- NOT read
|
-- 6 m aisles) are conventional Japanese parking-design values -- NOT read
|
||||||
-- from a primary standard, unlike the road markings. Paint is 15 cm, which
|
-- from a primary standard, unlike the road markings. Paint is 15 cm, which
|
||||||
-- does match the 全標協 line width used on the mainline.
|
-- does match the 全標協 line width used on the mainline.
|
||||||
local APRON_X = 181 * M
|
-- APRON EXTENT. Previously 181 x 174 m, which was the bounding box of the
|
||||||
local APRON_Z = 174 * M
|
-- OSM parking polygon alone. That polygon covers only the STALLS -- the
|
||||||
|
-- buildings, terrace and fountain sit alongside it and are separate OSM
|
||||||
|
-- ways, so a slab sized to the stalls left 本館, フォンターナ and the Lawson
|
||||||
|
-- hanging over the edge (measured: 4 of 12 features off-slab).
|
||||||
|
--
|
||||||
|
-- Sized here to the FULL surveyed site instead. After the +32 m north shift
|
||||||
|
-- and 0.88 uniform scale applied to the survey (see the building block),
|
||||||
|
-- the outermost features reach ~100 m radius, so a 210 x 200 m slab clears
|
||||||
|
-- everything with a margin for circulation. Still inside the ring road,
|
||||||
|
-- whose clear inner radius is 103.0 m.
|
||||||
|
local APRON_X = 210 * M
|
||||||
|
local APRON_Z = 200 * M
|
||||||
local STALL_W = 2.5 * M
|
local STALL_W = 2.5 * M
|
||||||
local STALL_D = 5.0 * M
|
local STALL_D = 5.0 * M
|
||||||
local BIG_W = 3.3 * M
|
local BIG_W = 3.3 * M
|
||||||
@@ -1013,14 +1024,22 @@ local function buildPA(map: Folder)
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- 小型 341: eleven rows of 31, stacked as back-to-back pairs with 6 m aisles.
|
-- 小型 341: eleven rows of 31, stacked as back-to-back pairs with 6 m aisles.
|
||||||
-- Z origin is derived, not eyeballed: the block starts one stall-depth south
|
-- Z origin is derived, not eyeballed: the block starts one stall-depth clear
|
||||||
-- of the 二番館 frontage and must finish north of the 大型 rows. Placing it by
|
-- of the フォンターナ frontage and must finish north of the 大型 rows. Placing
|
||||||
-- hand overlapped the truck bays by 42 studs -- see verify_daikoku_pa.
|
-- it by hand overlapped the truck bays by 42 studs -- see verify_daikoku_pa.
|
||||||
|
--
|
||||||
|
-- 2026-08-02: this used to hang off the old (wrong) north-edge 二番館
|
||||||
|
-- position. The building is now surveyed to OSM way 88790141, centre
|
||||||
|
-- (+13.9 E, +123.1 N) m with a 43 m long axis, so its southern frontage is
|
||||||
|
-- at 123.1 - 43/2 = 101.6 m north in OSM coords. That must go through the
|
||||||
|
-- SAME shift+scale as the buildings themselves (OSM_SHIFT_N = 32 m,
|
||||||
|
-- OSM_SCALE = 0.88, both defined with the building block below) or the
|
||||||
|
-- stalls detach from the building they are supposed to line up with.
|
||||||
local SMALL_PER_ROW = 31
|
local SMALL_PER_ROW = 31
|
||||||
local SMALL_ROWS = 11 -- 31 x 11 = 341 exactly
|
local SMALL_ROWS = 11 -- 31 x 11 = 341 exactly
|
||||||
local smallX0 = c.X - 20 * M
|
local smallX0 = c.X - 20 * M
|
||||||
local pairPitch = 2 * STALL_D + AISLE
|
local pairPitch = 2 * STALL_D + AISLE
|
||||||
local nibanFace = c.Z - APRON_Z / 2 + 11 * M + (13 * M) / 2
|
local nibanFace = c.Z - (123.1 - 43 / 2 - 32.0) * 0.88 * M
|
||||||
local smallZ0 = nibanFace + STALL_D + 8
|
local smallZ0 = nibanFace + STALL_D + 8
|
||||||
local placedSmall = 0
|
local placedSmall = 0
|
||||||
for row = 0, SMALL_ROWS - 1 do
|
for row = 0, SMALL_ROWS - 1 do
|
||||||
@@ -1058,14 +1077,20 @@ local function buildPA(map: Folder)
|
|||||||
cap.Parent = pa
|
cap.Parent = pa
|
||||||
|
|
||||||
-- Light towers at the apron corners.
|
-- Light towers at the apron corners.
|
||||||
|
-- Height 60 -> 40 (2026-08-02). With PA_LIFT at 56 the mainline slab
|
||||||
|
-- underside sits at ~101; a 60-stud mast topped out at 107 and would have
|
||||||
|
-- speared the deck -- swapping one clash for another. At 40 the head sits
|
||||||
|
-- at 87, ~14 studs clear. Real PA lighting under a viaduct is short for
|
||||||
|
-- exactly this reason. If PA_LIFT changes again, re-check this.
|
||||||
|
local MAST_H = 40
|
||||||
for _, sx in ipairs({ -1, 1 }) do
|
for _, sx in ipairs({ -1, 1 }) do
|
||||||
for _, sz in ipairs({ -1, 1 }) do
|
for _, sz in ipairs({ -1, 1 }) do
|
||||||
local lx = c.X + sx * (APRON_X / 2 - 10 * M)
|
local lx = c.X + sx * (APRON_X / 2 - 10 * M)
|
||||||
local lz = c.Z + sz * (APRON_Z / 2 - 10 * M)
|
local lz = c.Z + sz * (APRON_Z / 2 - 10 * M)
|
||||||
local mast = part({
|
local mast = part({
|
||||||
Name = "PAMast",
|
Name = "PAMast",
|
||||||
Size = Vector3.new(3, 60, 3),
|
Size = Vector3.new(3, MAST_H, 3),
|
||||||
CFrame = CFrame.new(lx, DECK_Y + 30, lz),
|
CFrame = CFrame.new(lx, DECK_Y + MAST_H / 2, lz),
|
||||||
Color = Color3.fromRGB(70, 70, 78),
|
Color = Color3.fromRGB(70, 70, 78),
|
||||||
Material = Enum.Material.Metal,
|
Material = Enum.Material.Metal,
|
||||||
})
|
})
|
||||||
@@ -1073,7 +1098,7 @@ local function buildPA(map: Folder)
|
|||||||
local head = part({
|
local head = part({
|
||||||
Name = "PALight",
|
Name = "PALight",
|
||||||
Size = Vector3.new(10, 2, 10),
|
Size = Vector3.new(10, 2, 10),
|
||||||
CFrame = CFrame.new(lx, DECK_Y + 60, lz),
|
CFrame = CFrame.new(lx, DECK_Y + MAST_H, lz),
|
||||||
Color = Color3.fromRGB(255, 240, 200),
|
Color = Color3.fromRGB(255, 240, 200),
|
||||||
Material = Enum.Material.Neon,
|
Material = Enum.Material.Neon,
|
||||||
})
|
})
|
||||||
@@ -1183,18 +1208,32 @@ local function buildPA(map: Folder)
|
|||||||
local segCF = CFrame.lookAt(segMid, p1, UP)
|
local segCF = CFrame.lookAt(segMid, p1, UP)
|
||||||
deck("PARing", Vector3.new(RING_W, 2, segLen), segCF)
|
deck("PARing", Vector3.new(RING_W, 2, segLen), segCF)
|
||||||
|
|
||||||
-- Both edges of the ring get the same white parapet + dark railing as
|
-- Ring edge treatment. The OUTER edge (sgn == 1, away from the apron)
|
||||||
-- the mainline, so the loop reads as Shutoko structure from below.
|
-- keeps the full white parapet + dark railing, because behind it is a
|
||||||
|
-- drop off the structure -- that wall is the only thing stopping a car
|
||||||
|
-- leaving the world.
|
||||||
|
--
|
||||||
|
-- The INNER edge (sgn == -1, facing the parking lot) is now a LOW KERB,
|
||||||
|
-- not a parapet. Two reasons, one factual and one about driving:
|
||||||
|
-- * Factual: since the ring and the apron were both dropped to ground
|
||||||
|
-- level (Circuit.PA_LIFT raises the MAINLINE over the PA instead),
|
||||||
|
-- an inner parapet would be separating two surfaces at the SAME
|
||||||
|
-- height. The real Daikoku has a kerb and painted markings there,
|
||||||
|
-- not a wall -- the lot is open to its circulation road.
|
||||||
|
-- * Driving: a 44-stud lane walled on both sides is a canyon with no
|
||||||
|
-- runoff. Clipping the inside of the loop stopped a car dead.
|
||||||
|
-- The kerb is 1.2 studs, well under the ~2-3 studs a car can climb, so
|
||||||
|
-- it reads as an edge without being a barrier.
|
||||||
local fwd = (p1 - p0).Unit
|
local fwd = (p1 - p0).Unit
|
||||||
local outN = (segMid - Vector3.new(c.X, ringY, c.Z)).Unit
|
local outN = (segMid - Vector3.new(c.X, ringY, c.Z)).Unit
|
||||||
for _, sgn in ipairs({ 1, -1 }) do
|
for _, sgn in ipairs({ 1, -1 }) do
|
||||||
local ePos = segMid + outN * (sgn * RING_W / 2)
|
local ePos = segMid + outN * (sgn * RING_W / 2)
|
||||||
-- sgn == 1 is the OUTER edge (away from the apron), sgn == -1 the
|
-- Skip the edge entirely where a ramp tees in, or the ring seals
|
||||||
-- INNER edge facing the parking lot. Skip the parapet entirely
|
-- the PA (see above).
|
||||||
-- where a ramp tees in, or the ring seals the PA (see above).
|
|
||||||
local gate = (sgn == 1) and GATE_OUT or GATE_IN
|
local gate = (sgn == 1) and GATE_OUT or GATE_IN
|
||||||
if not nearJunction(ePos, gate) then
|
if not nearJunction(ePos, gate) then
|
||||||
local eCF = CFrame.lookAt(ePos, ePos + fwd, UP)
|
local eCF = CFrame.lookAt(ePos, ePos + fwd, UP)
|
||||||
|
if sgn == 1 then
|
||||||
local lip = part({
|
local lip = part({
|
||||||
Name = "PARingWall",
|
Name = "PARingWall",
|
||||||
Size = Vector3.new(2.2, BARRIER_H, segLen + 1),
|
Size = Vector3.new(2.2, BARRIER_H, segLen + 1),
|
||||||
@@ -1211,6 +1250,17 @@ local function buildPA(map: Folder)
|
|||||||
Material = Enum.Material.Metal,
|
Material = Enum.Material.Metal,
|
||||||
})
|
})
|
||||||
lr.Parent = pa
|
lr.Parent = pa
|
||||||
|
else
|
||||||
|
local KERB_H = 1.2
|
||||||
|
local kerb = part({
|
||||||
|
Name = "PARingKerb",
|
||||||
|
Size = Vector3.new(1.6, KERB_H, segLen + 1),
|
||||||
|
CFrame = eCF * CFrame.new(0, 1 + KERB_H / 2, 0),
|
||||||
|
Color = C_PARAPET,
|
||||||
|
Material = Enum.Material.Concrete,
|
||||||
|
})
|
||||||
|
kerb.Parent = pa
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -1272,7 +1322,24 @@ local function buildPA(map: Folder)
|
|||||||
pil.Parent = pa
|
pil.Parent = pa
|
||||||
end
|
end
|
||||||
-- Edge kerbs so a car can't slide off the side of a raised ramp.
|
-- Edge kerbs so a car can't slide off the side of a raised ramp.
|
||||||
if h > 8 then
|
--
|
||||||
|
-- BUG (2026-08-02, found by test drive + tools/diag_ramp.luau):
|
||||||
|
-- the guard used to be `h > 8`, which is a HEIGHT test on a ramp
|
||||||
|
-- that starts at apron level. The first 2-3 segments of every ramp
|
||||||
|
-- (heightAboveApron 0.5 .. 5.6) therefore got no kerb at all, so
|
||||||
|
-- the barrier visibly stopped part-way down the ramp -- the
|
||||||
|
-- "side barriers are not actually fully finished" report. The low
|
||||||
|
-- end is not a fall hazard, but it still needs an edge or the
|
||||||
|
-- structure just ends in mid-air visually.
|
||||||
|
--
|
||||||
|
-- The other half of that report -- "barriers extend too far into
|
||||||
|
-- the main highway" -- is the opposite end: the kerb ran right to
|
||||||
|
-- the road end of the ramp, so a kerb stood IN the mainline
|
||||||
|
-- driving lane at the merge (measured: a 2.4-stud step 14 studs
|
||||||
|
-- right of the centreline, hit as Road_526 -> PARampKerb). A merge
|
||||||
|
-- has to be open, so the last segment carries no kerb.
|
||||||
|
local isMergeSeg = (s >= STEPS - 1)
|
||||||
|
if h > 2 and not isMergeSeg then
|
||||||
local fwd2 = (b - a)
|
local fwd2 = (b - a)
|
||||||
fwd2 = Vector3.new(fwd2.X, 0, fwd2.Z)
|
fwd2 = Vector3.new(fwd2.X, 0, fwd2.Z)
|
||||||
if fwd2.Magnitude > 0.01 then
|
if fwd2.Magnitude > 0.01 then
|
||||||
@@ -1318,10 +1385,23 @@ local function buildPA(map: Folder)
|
|||||||
-- outer lip walls the whole thing -- so there is no exposed edge to fall
|
-- outer lip walls the whole thing -- so there is no exposed edge to fall
|
||||||
-- off and no need for the old four-sided perimeter box.
|
-- off and no need for the old four-sided perimeter box.
|
||||||
|
|
||||||
|
-- SPAWN PAD PLACEMENT. This used to sit exactly on PA_CENTER, which was
|
||||||
|
-- fine while the apron centre was empty. It is NOT fine after the survey
|
||||||
|
-- landed: the OSM stall block is centred near the apron middle, so the pad
|
||||||
|
-- punched a 120x120 dark hole straight through the painted 小型 rows
|
||||||
|
-- (visible as a black rectangle in the overhead capture).
|
||||||
|
--
|
||||||
|
-- The offset below is MEASURED, not chosen: a 15-stud grid search over the
|
||||||
|
-- whole apron scored every candidate by its clearance to all 804 built PA
|
||||||
|
-- obstacles (buildings, stalls, props) while staying inside the ring road.
|
||||||
|
-- (-165, -150) is the best cell, with 93 studs of clearance -- comfortably
|
||||||
|
-- more than the pad's 60-stud half-width. It lands on the open southwest
|
||||||
|
-- circulation area, which is empty tarmac on the real site too.
|
||||||
|
local SPAWN_OFF = Vector3.new(-165, 0.3, -150)
|
||||||
local pad = part({
|
local pad = part({
|
||||||
Name = "SpawnPad",
|
Name = "SpawnPad",
|
||||||
Size = Vector3.new(120, 2.4, 120),
|
Size = Vector3.new(120, 2.4, 120),
|
||||||
CFrame = CFrame.new(c + Vector3.new(0, 0.3, 0)),
|
CFrame = CFrame.new(c + SPAWN_OFF),
|
||||||
Color = Color3.fromRGB(20, 20, 26),
|
Color = Color3.fromRGB(20, 20, 26),
|
||||||
Material = Enum.Material.Neon,
|
Material = Enum.Material.Neon,
|
||||||
Transparency = 0.25,
|
Transparency = 0.25,
|
||||||
@@ -1332,10 +1412,14 @@ local function buildPA(map: Folder)
|
|||||||
-- and CarManager spawns the car onto the character -- so without this the
|
-- and CarManager spawns the car onto the character -- so without this the
|
||||||
-- player starts ~8,600 studs off-circuit regardless of where SpawnPad is.
|
-- player starts ~8,600 studs off-circuit regardless of where SpawnPad is.
|
||||||
-- Neutral + non-collidable so it never becomes an obstacle on the pad.
|
-- Neutral + non-collidable so it never becomes an obstacle on the pad.
|
||||||
|
-- Must sit ON the pad -- it is keyed to the same measured offset, because
|
||||||
|
-- CarManager spawns the car onto the character. Leaving this at PA_CENTER
|
||||||
|
-- while the pad moved would drop the player into the middle of the 小型
|
||||||
|
-- stall rows instead of onto clear tarmac.
|
||||||
local spawnLoc = Instance.new("SpawnLocation")
|
local spawnLoc = Instance.new("SpawnLocation")
|
||||||
spawnLoc.Name = "PASpawn"
|
spawnLoc.Name = "PASpawn"
|
||||||
spawnLoc.Size = Vector3.new(24, 1, 24)
|
spawnLoc.Size = Vector3.new(24, 1, 24)
|
||||||
spawnLoc.CFrame = CFrame.new(c + Vector3.new(0, 2, 0))
|
spawnLoc.CFrame = CFrame.new(c + Vector3.new(SPAWN_OFF.X, 2, SPAWN_OFF.Z))
|
||||||
spawnLoc.Transparency = 1
|
spawnLoc.Transparency = 1
|
||||||
spawnLoc.CanCollide = false
|
spawnLoc.CanCollide = false
|
||||||
spawnLoc.Anchored = true
|
spawnLoc.Anchored = true
|
||||||
@@ -1344,29 +1428,94 @@ local function buildPA(map: Folder)
|
|||||||
spawnLoc.Parent = pa
|
spawnLoc.Parent = pa
|
||||||
|
|
||||||
-- BUILDINGS ---------------------------------------------------------------
|
-- BUILDINGS ---------------------------------------------------------------
|
||||||
-- ja.wikipedia: 本館 sits on the WEST side of the area and ベイサイドテラス
|
-- SURVEYED LAYOUT, not art direction. Every position below is read from
|
||||||
-- 二番館 on the NORTH, and both are reachable from every direction. The
|
-- OpenStreetMap and converted to studs, so the arrangement matches the real
|
||||||
-- 本館 is genuinely three storeys (3F closed off, 2F a rest/meeting space
|
-- 大黒ふ頭PA rather than approximating it.
|
||||||
-- reopened 2025-10-30, 1F the shop/toilet block). Footprint sizes here are
|
--
|
||||||
-- eyeballed to the aerial -- unlike the apron extent and stall counts, they
|
-- Sources (fetched 2026-08-02 via the OSM API, not Overpass -- Overpass
|
||||||
-- are NOT measured, so treat them as art direction.
|
-- 504s repeatedly on this bbox):
|
||||||
|
-- * way 59608205 amenity=parking 大黒ふ頭パーキングエリア -- the apron
|
||||||
|
-- * way 405966005 highway=rest_area 大黒PA (wikidata Q10938704)
|
||||||
|
-- * building ways + amenity/shop nodes within 230 m of the apron centroid
|
||||||
|
--
|
||||||
|
-- Local frame: origin = apron polygon centroid (35.461455 N, 139.680201 E),
|
||||||
|
-- +xe = EAST metres, +zn = NORTH metres. Mapped into the game world as
|
||||||
|
-- East -> +X and North -> -Z, which is the convention the rest of the map
|
||||||
|
-- already uses.
|
||||||
|
--
|
||||||
|
-- THE BIG CORRECTION: the apron is NOT the axis-aligned square the previous
|
||||||
|
-- code assumed. Measured from the polygon, it is a PARALLELOGRAM lying on a
|
||||||
|
-- SW-NE diagonal -- principal axis bearing ~37 deg, longest edge 155.8 m at
|
||||||
|
-- bearing 45.4 deg, area 1.60 ha. The 181 x 174 m figure quoted before is
|
||||||
|
-- just the bounding box of that diagonal lot, which is why buildings placed
|
||||||
|
-- on N/S/E/W edges landed in the wrong places. The two named buildings sit
|
||||||
|
-- at OPPOSITE ENDS OF THE DIAGONAL, ~175 m apart:
|
||||||
|
-- 本館 (main building) at the SOUTH-WEST end, near the neck
|
||||||
|
-- フォンターナ・ダイコク at the NORTH-EAST tip
|
||||||
|
-- That single fact is most of what makes the lot read as Daikoku.
|
||||||
local C_BLDG = Color3.fromRGB(214, 210, 200)
|
local C_BLDG = Color3.fromRGB(214, 210, 200)
|
||||||
local C_GLASS = Color3.fromRGB(120, 150, 165)
|
local C_GLASS = Color3.fromRGB(120, 150, 165)
|
||||||
|
|
||||||
local function building(name, sx, sy, sz, x, z)
|
-- OSM local metres -> world studs.
|
||||||
|
--
|
||||||
|
-- Two corrections are folded into this transform, both forced by MEASURED
|
||||||
|
-- constraints rather than taste:
|
||||||
|
--
|
||||||
|
-- 1. ORIGIN SHIFT (+32 m North). The OSM parking polygon's centroid is not
|
||||||
|
-- the centre of the SITE -- the buildings all sit north and east of the
|
||||||
|
-- stalls, so the full site bbox spans east -87..+111 and north -53..+137.
|
||||||
|
-- Centring on the parking centroid pushed 7 of 12 features off the apron.
|
||||||
|
-- Solving for the origin that minimises the enclosing radius gives
|
||||||
|
-- (+0.0 E, +32.0 N), which is the value used here.
|
||||||
|
--
|
||||||
|
-- 2. UNIFORM SCALE (0.88). The real site needs a 113.9 m radius after the
|
||||||
|
-- shift, but the PA ring road only offers 103.0 m of clear space inside
|
||||||
|
-- it (ring centreline PA_RADIUS+130 = 390 studs, ring 44 studs wide).
|
||||||
|
-- The ring CANNOT simply be grown: the nearest mainline sample is only
|
||||||
|
-- 330 studs from PA_CENTER (measured in-engine), so the ring is already
|
||||||
|
-- wider than its clearance to the expressway and enlarging it would push
|
||||||
|
-- the loop through the road deck and break the ramp junctions.
|
||||||
|
-- Scaling the survey down by 0.88 instead fits it in the existing
|
||||||
|
-- envelope. Because the scale is UNIFORM, every distance and angle keeps
|
||||||
|
-- its true proportion -- the relative arrangement of the buildings, which
|
||||||
|
-- is what makes the lot recognisable as Daikoku, is preserved exactly.
|
||||||
|
local OSM_SHIFT_N = 32.0
|
||||||
|
local OSM_SCALE = 0.88
|
||||||
|
local function osmX(xe: number): number
|
||||||
|
return c.X + xe * OSM_SCALE * M
|
||||||
|
end
|
||||||
|
local function osmZ(zn: number): number
|
||||||
|
return c.Z - (zn - OSM_SHIFT_N) * OSM_SCALE * M
|
||||||
|
end
|
||||||
|
-- OSM edge bearing (degrees, measured as atan2(east, north) the way the
|
||||||
|
-- survey reports it) -> Roblox Y rotation. Derivation: we want the part's
|
||||||
|
-- LookVector (-sin t, 0, -cos t) to lie along the OSM direction
|
||||||
|
-- (cos a East, sin a North) = (cos a, 0, -sin a) in world axes, which
|
||||||
|
-- solves to t = a - 90 deg.
|
||||||
|
local function osmYaw(angDeg: number): number
|
||||||
|
return math.rad(angDeg - 90)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- `long` runs along the building's own long axis, `wide` across it.
|
||||||
|
-- Footprints are scaled by the same OSM_SCALE as the positions, so the lot
|
||||||
|
-- stays a faithful uniform reduction rather than a stretched one.
|
||||||
|
local function building(name, long_m, height_m, wide_m, xe, zn, angDeg)
|
||||||
|
local sy = height_m * M
|
||||||
|
local cf = CFrame.new(osmX(xe), DECK_Y + sy / 2, osmZ(zn)) * CFrame.Angles(0, osmYaw(angDeg), 0)
|
||||||
local b = part({
|
local b = part({
|
||||||
Name = name,
|
Name = name,
|
||||||
Size = Vector3.new(sx, sy, sz),
|
Size = Vector3.new(wide_m * OSM_SCALE * M, sy, long_m * OSM_SCALE * M),
|
||||||
CFrame = CFrame.new(x, DECK_Y + sy / 2, z),
|
CFrame = cf,
|
||||||
Color = C_BLDG,
|
Color = C_BLDG,
|
||||||
Material = Enum.Material.Concrete,
|
Material = Enum.Material.Concrete,
|
||||||
})
|
})
|
||||||
b.Parent = pa
|
b.Parent = pa
|
||||||
-- Ground-floor glazing so it reads as a rest stop, not a warehouse.
|
-- Ground-floor glazing so it reads as a rest stop, not a warehouse.
|
||||||
|
local glassH = math.min(4 * M, sy * 0.7)
|
||||||
local glass = part({
|
local glass = part({
|
||||||
Name = name .. "Glazing",
|
Name = name .. "Glazing",
|
||||||
Size = Vector3.new(sx + 0.6, 4 * M, sz + 0.6),
|
Size = Vector3.new(wide_m * OSM_SCALE * M + 0.6, glassH, long_m * OSM_SCALE * M + 0.6),
|
||||||
CFrame = CFrame.new(x, DECK_Y + 2.6 * M, z),
|
CFrame = cf * CFrame.new(0, -sy / 2 + glassH / 2 + 0.2 * M, 0),
|
||||||
Color = C_GLASS,
|
Color = C_GLASS,
|
||||||
Material = Enum.Material.Glass,
|
Material = Enum.Material.Glass,
|
||||||
Transparency = 0.35,
|
Transparency = 0.35,
|
||||||
@@ -1375,26 +1524,60 @@ local function buildPA(map: Folder)
|
|||||||
return b
|
return b
|
||||||
end
|
end
|
||||||
|
|
||||||
-- 本館 (main building), west side, three storeys.
|
-- 本館 (main building) -- SW end of the lot. Three storeys (1F shop/toilet
|
||||||
local honkanX = c.X - APRON_X / 2 + 13 * M
|
-- block, 2F rest area reopened 2025-10-30, 3F closed). Its footprint is not
|
||||||
building("PAHonkan", 22 * M, 13 * M, 46 * M, honkanX, c.Z)
|
-- mapped as a building way in OSM, so the extent is reconstructed from the
|
||||||
makeSign(
|
-- tight POI cluster that sits inside it: food_court (-22,-64), Bay Side
|
||||||
"本館 MAIN BUILDING",
|
-- Restaurant (-25,-62), bakery Little Marmaid (-31,-62), toilets (-22,-59),
|
||||||
CFrame.new(honkanX + 12 * M, DECK_Y + 15 * M, c.Z) * CFrame.Angles(0, math.pi / 2, 0),
|
-- elevator (-34,-51), telephone (-21,-49), Blue Seal (-42,-50), cafe
|
||||||
pa
|
-- Cafe de Crie (-43,-49), convenience e-Mart (-40,-45). Those span roughly
|
||||||
)
|
-- x -45..-18, z -66..-43, hence a ~46 x 34 m block centred (-32,-53)
|
||||||
|
-- squared up to the 45 deg lot grid.
|
||||||
|
building("PAHonkan", 46, 13, 34, -32, -53, 45)
|
||||||
|
makeSign("本館 MAIN BUILDING", CFrame.new(osmX(-32), DECK_Y + 15 * M, osmZ(-53) + 20 * M), pa)
|
||||||
|
|
||||||
-- ベイサイドテラス二番館 (Bayside Terrace No.2), north side, single storey.
|
-- フォンターナ・ダイコク / ベイサイドテラス -- NE tip. OSM way 88790141,
|
||||||
local nibanZ = c.Z - APRON_Z / 2 + 11 * M
|
-- oriented bbox 28 x 43 m at bearing +43 deg, centre (+13.9, +123.1).
|
||||||
building("PANibankan", 34 * M, 7 * M, 13 * M, c.X + 18 * M, nibanZ)
|
building("PANibankan", 43, 7, 28, 13.9, 123.1, 43)
|
||||||
makeSign("ベイサイドテラス二番館", CFrame.new(c.X + 18 * M, DECK_Y + 9 * M, nibanZ + 7 * M), pa)
|
makeSign("フォンターナ・ダイコク", CFrame.new(osmX(13.9), DECK_Y + 9 * M, osmZ(123.1) + 16 * M), pa)
|
||||||
|
|
||||||
|
-- Remaining mapped structures, each from its own OSM building way.
|
||||||
|
building("PAToiletNorth", 21, 5, 7, -17.5, 109.8, -44) -- way 88790136
|
||||||
|
building("PAKioskWest", 16, 5, 9, -87.2, 24.5, 46) -- way 88790109
|
||||||
|
building("PAHutWest", 14, 4.5, 10, -71.9, 42.2, -45) -- way 88790185
|
||||||
|
|
||||||
|
-- Free-standing amenity nodes. Small props, but they are what stops the
|
||||||
|
-- apron reading as an empty car park.
|
||||||
|
local function prop(name, sx_m, sy_m, sz_m, xe, zn, colour, mat)
|
||||||
|
local p = part({
|
||||||
|
Name = name,
|
||||||
|
Size = Vector3.new(sx_m * OSM_SCALE * M, sy_m * M, sz_m * OSM_SCALE * M),
|
||||||
|
CFrame = CFrame.new(osmX(xe), DECK_Y + (sy_m * M) / 2, osmZ(zn)),
|
||||||
|
Color = colour,
|
||||||
|
Material = mat or Enum.Material.Metal,
|
||||||
|
})
|
||||||
|
p.Parent = pa
|
||||||
|
return p
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Lawson (+21,+136.7) and the north toilet block (-3.3,+124) flank Fontana.
|
||||||
|
prop("PALawson", 14, 4, 10, 21.0, 136.7, Color3.fromRGB(70, 130, 200), Enum.Material.Concrete)
|
||||||
|
prop("PAToiletBlockN", 12, 4, 8, -3.3, 124.0, Color3.fromRGB(205, 200, 190), Enum.Material.Concrete)
|
||||||
|
-- 噴水 fountain (+5.9,+122.5) -- the photo spot on the north terrace.
|
||||||
|
prop("PAFountain", 9, 0.8, 9, 5.9, 122.5, Color3.fromRGB(150, 170, 185), Enum.Material.Slate)
|
||||||
|
-- Smoking shelters: north terrace (+1.4,+103.9) and by the main building.
|
||||||
|
prop("PASmokingN", 7, 3, 5, 1.4, 103.9, Color3.fromRGB(150, 150, 155))
|
||||||
|
prop("PASmokingS", 7, 3, 5, -47.7, -31.5, Color3.fromRGB(150, 150, 155))
|
||||||
|
-- 二輪車駐車場: motorcycles park separately, right by the main building.
|
||||||
|
prop("PAMotorcycleBay", 20, 0.05, 12, 9.0, -44.9, Color3.fromRGB(70, 70, 78), Enum.Material.Asphalt)
|
||||||
|
|
||||||
-- EV rapid charger: first on any Japanese expressway, installed 2008-10-10.
|
-- EV rapid charger: first on any Japanese expressway, installed 2008-10-10.
|
||||||
|
-- OSM charging_station node sits at (-58.3, +30.2), on the west flank.
|
||||||
for k = 0, 1 do
|
for k = 0, 1 do
|
||||||
local ev = part({
|
local ev = part({
|
||||||
Name = "PAEVCharger",
|
Name = "PAEVCharger",
|
||||||
Size = Vector3.new(1.6 * M, 2.2 * M, 0.9 * M),
|
Size = Vector3.new(1.6 * M, 2.2 * M, 0.9 * M),
|
||||||
CFrame = CFrame.new(honkanX + 14 * M + k * 4 * M, DECK_Y + 1.1 * M, c.Z + 26 * M),
|
CFrame = CFrame.new(osmX(-58.3) + k * 4 * M, DECK_Y + 1.1 * M, osmZ(30.2)),
|
||||||
Color = Color3.fromRGB(60, 130, 90),
|
Color = Color3.fromRGB(60, 130, 90),
|
||||||
Material = Enum.Material.Metal,
|
Material = Enum.Material.Metal,
|
||||||
})
|
})
|
||||||
@@ -1634,6 +1817,18 @@ local function buildSkyline(map: Folder)
|
|||||||
folder.Name = "Skyline"
|
folder.Name = "Skyline"
|
||||||
folder.Parent = map
|
folder.Parent = map
|
||||||
|
|
||||||
|
-- PA keep-out. Returns true if a `w`-wide box centred at (x,z) clears the
|
||||||
|
-- Daikoku PA island entirely. RING_OUTER mirrors buildPA's own geometry:
|
||||||
|
-- ringRadius (PA_RADIUS + 130) + half the 44-stud ring width.
|
||||||
|
local PA_C = Circuit.PA_CENTER
|
||||||
|
local RING_OUTER = Circuit.PA_RADIUS + 130 + 22
|
||||||
|
local PA_MARGIN = 60
|
||||||
|
local function paKeepout(x: number, z: number, w: number): boolean
|
||||||
|
local dx, dz = x - PA_C.X, z - PA_C.Z
|
||||||
|
local r = math.sqrt(dx * dx + dz * dz)
|
||||||
|
return r - w / 2 > RING_OUTER + PA_MARGIN
|
||||||
|
end
|
||||||
|
|
||||||
local samples = Circuit.samples
|
local samples = Circuit.samples
|
||||||
local n = #samples
|
local n = #samples
|
||||||
local placed = 0
|
local placed = 0
|
||||||
@@ -1668,6 +1863,20 @@ local function buildSkyline(map: Folder)
|
|||||||
continue
|
continue
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- ...but `samples` is the MAINLINE centreline ONLY. The PA ring road,
|
||||||
|
-- its two ramps and the parking apron are generated separately (see
|
||||||
|
-- buildPA) and appear nowhere in `samples`, so the test above is blind
|
||||||
|
-- to the entire PA island. A 186-wide tower landed at r=428 from
|
||||||
|
-- PA_CENTER, spanning radius 335->521 and punching straight through the
|
||||||
|
-- ring road band [368,412] -- a skyscraper standing in the road.
|
||||||
|
--
|
||||||
|
-- Exclude the whole PA disc, not just the ring band: nothing generated
|
||||||
|
-- belongs inside the ring at all. Test the building's INNER FACE
|
||||||
|
-- (r - w/2) against the ring's outer kerb + the same 60-stud margin.
|
||||||
|
if not paKeepout(x, z, w) then
|
||||||
|
continue
|
||||||
|
end
|
||||||
|
|
||||||
local b = part({
|
local b = part({
|
||||||
Name = "Building",
|
Name = "Building",
|
||||||
Size = Vector3.new(w, h, w),
|
Size = Vector3.new(w, h, w),
|
||||||
@@ -1690,7 +1899,7 @@ local function buildSkyline(map: Folder)
|
|||||||
snear = d
|
snear = d
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if snear > CLEAR then
|
if snear > CLEAR and paKeepout(sx, z, 12) then
|
||||||
local sign = part({
|
local sign = part({
|
||||||
Name = "NeonSign",
|
Name = "NeonSign",
|
||||||
Size = Vector3.new(12, h * 0.42, 5),
|
Size = Vector3.new(12, h * 0.42, 5),
|
||||||
|
|||||||
+13
-2
@@ -893,9 +893,20 @@ end
|
|||||||
-- heights. Everything downstream (deck, median, barrier, kill wall, lane
|
-- heights. Everything downstream (deck, median, barrier, kill wall, lane
|
||||||
-- paint, ramps) is built from sample.pos, so lifting the samples lifts the
|
-- paint, ramps) is built from sample.pos, so lifting the samples lifts the
|
||||||
-- whole structure coherently.
|
-- whole structure coherently.
|
||||||
Circuit.PA_LIFT = 38 -- studs of clearance over the apron (a car is ~7 tall)
|
-- 2026-08-02: raised 38 -> 56. 38 studs (~10.6 m) was less than the height of
|
||||||
|
-- the structures standing on the apron, so the mainline deck cut straight
|
||||||
|
-- through them -- PAHonkan (13 m / 46.4 studs) surfaced through Road_536..539,
|
||||||
|
-- which is the "building sitting on the road" artefact. Clearance must exceed
|
||||||
|
-- the TALLEST thing under the deck, not just a car. Honkan top sits 46.4 studs
|
||||||
|
-- above the apron, so the slab underside (46 + PA_LIFT - 1) needs PA_LIFT > 48.
|
||||||
|
-- 56 gives underside 101 -- 7.6 studs of daylight over the roof. This is also
|
||||||
|
-- closer to the real Bayshore Route viaduct, which clears the PA by ~15-20 m,
|
||||||
|
-- not 10. Verify with tools/diag_clearance.luau after changing.
|
||||||
|
Circuit.PA_LIFT = 56 -- studs of clearance over the apron (tallest = Honkan 46.4)
|
||||||
local LIFT_FLAT = 20 -- samples held at full lift, centred on the PA
|
local LIFT_FLAT = 20 -- samples held at full lift, centred on the PA
|
||||||
local LIFT_RAMP = 18 -- samples of smooth transition on each side
|
-- Widened 18 -> 26 alongside the lift increase: the climb is spread over more
|
||||||
|
-- samples so the grade stays where it was instead of getting 47% steeper.
|
||||||
|
local LIFT_RAMP = 26 -- samples of smooth transition on each side
|
||||||
do
|
do
|
||||||
local c = Circuit.PA_CENTER
|
local c = Circuit.PA_CENTER
|
||||||
local n = #samples
|
local n = #samples
|
||||||
|
|||||||
Reference in New Issue
Block a user