diff --git a/src/server/Ambience.luau b/src/server/Ambience.luau index a041b85..96e0ed9 100644 --- a/src/server/Ambience.luau +++ b/src/server/Ambience.luau @@ -1,26 +1,43 @@ --!strict --[[ Ambience.luau (SERVER) - The night-city mood pass (M2). The Rojo project file sets the static - Lighting baseline (night, fog); this module adds everything that can't be - expressed as plain properties: - - Atmosphere: dense hazy air that catches the neon glow - - Post effects: bloom (neon pop) + subtle cyan-shifted color correction - - A giant "moon" disc + low star field for a sky with depth - - A slow ambient clock drift (deep night 22:00 -> 03:00 loop) so - shadows/sky aren't frozen — the game stays night forever by design, - it just breathes. + Two looks, one switch (GameConfig.DevDaylight): - All tunables at the top. Runs once at boot; the drift loop is one cheap - task.spawn. + NIGHT (shipping, DevDaylight = false) + The night-city mood pass (M2). The Rojo project file sets the static + Lighting baseline (night, fog); this module adds everything that can't + be expressed as plain properties: + - Atmosphere: dense hazy air that catches the neon glow + - Post effects: bloom (neon pop) + subtle cyan-shifted color correction + - A slow ambient clock drift (deep night 22:00 -> 03:00 loop) so + shadows/sky aren't frozen — the game stays night forever by design, + it just breathes. + + DAYLIGHT (development, DevDaylight = true) + A flat, bright midday pass so geometry is actually visible while + building: road surface, kerbs, gap placement, PA layout. Full sun, no + fog, no bloom, no drift — a frozen noon. + + This must ACTIVELY OVERRIDE the night values baked into + default.project.json (ClockTime 0, near-black Ambient, FogEnd 900), + because those are applied to Lighting before this module runs. + + Only lighting/post differs between the two — no gameplay, geometry or + physics changes, so anything verified in daylight holds at night. + + All tunables at the top. Runs once at boot; the night drift loop is one + cheap task.spawn that does not start in daylight mode. ]] local Lighting = game:GetService("Lighting") +local ReplicatedStorage = game:GetService("ReplicatedStorage") + +local GameConfig = require(ReplicatedStorage.Shared.GameConfig) local Ambience = {} --- Tunables -------------------------------------------------------------------- +-- Night tunables -------------------------------------------------------------- local ATMOS_DENSITY = 0.35 local ATMOS_HAZE = 2.1 local BLOOM_INTENSITY = 0.9 @@ -29,7 +46,42 @@ local NIGHT_START = 22 -- ClockTime loop range local NIGHT_END = 27 -- 27 == 03:00 next day local NIGHT_LOOP_SECONDS = 600 -- one full drift cycle = 10 minutes -function Ambience.init() +-- Daylight tunables ----------------------------------------------------------- +local DAY_CLOCK = 13.5 -- early afternoon: sun high, but still casts readable shadows +local DAY_BRIGHTNESS = 2.5 +local DAY_FOG_END = 100000 -- effectively no fog; see the whole loop +local DAY_ATMOS_DENSITY = 0.18 -- a little depth on distant geometry, no murk + +--- Bright, flat midday. Overrides the night baseline from the project file. +local function applyDaylight() + Lighting.ClockTime = DAY_CLOCK + Lighting.GeographicLatitude = 35.6 -- Tokyo, so the sun angle is plausible + Lighting.Brightness = DAY_BRIGHTNESS + Lighting.ExposureCompensation = 0 + Lighting.Ambient = Color3.fromRGB(120, 120, 130) + Lighting.OutdoorAmbient = Color3.fromRGB(150, 152, 160) + Lighting.FogStart = 0 + Lighting.FogEnd = DAY_FOG_END + Lighting.FogColor = Color3.fromRGB(190, 205, 225) + Lighting.EnvironmentDiffuseScale = 1 + Lighting.EnvironmentSpecularScale = 1 + Lighting.GlobalShadows = true + + local atmosphere = Instance.new("Atmosphere") + atmosphere.Density = DAY_ATMOS_DENSITY + atmosphere.Offset = 0.1 + atmosphere.Color = Color3.fromRGB(199, 209, 227) + atmosphere.Decay = Color3.fromRGB(106, 112, 125) + atmosphere.Glare = 0 + atmosphere.Haze = 0.6 + atmosphere.Parent = Lighting + + -- No bloom / no colour grade in dev: they wash out edges and make it + -- harder to judge whether geometry is actually aligned. +end + +--- The shipping neon-night look. +local function applyNight() -- Atmosphere ----------------------------------------------------------------- local atmosphere = Instance.new("Atmosphere") atmosphere.Density = ATMOS_DENSITY @@ -68,4 +120,26 @@ function Ambience.init() end) end +function Ambience.init() + -- Clear anything a previous run (or the project file) left behind, so the + -- two modes can't stack Atmosphere/effects on top of each other. + for _, child in ipairs(Lighting:GetChildren()) do + if + child:IsA("Atmosphere") + or child:IsA("BloomEffect") + or child:IsA("ColorCorrectionEffect") + or child:IsA("SunRaysEffect") + then + child:Destroy() + end + end + + if GameConfig.DevDaylight then + applyDaylight() + print("[Ambience] DEV DAYLIGHT is ON (GameConfig.DevDaylight) — set it to false for the night look.") + else + applyNight() + end +end + return Ambience diff --git a/src/server/MapBuilder.luau b/src/server/MapBuilder.luau index b23558f..bd2cde5 100644 --- a/src/server/MapBuilder.luau +++ b/src/server/MapBuilder.luau @@ -1,56 +1,40 @@ --!strict --[[ MapBuilder.luau (SERVER) - v3 — "Shuto Night Loop": a Tokyo-expressway-inspired CIRCUIT, still built - 100% from code. The straight strip is gone; the road is now a closed - rounded-rectangle loop (~7,400 studs/lap) modelled on the Shuto C1 vibe, - with real-Tokyo landmark zones: + v4 — "C1: Shuto Inner Circular". The road is no longer an invented + rounded-rectangle: it is the REAL Shuto Expressway Inner Circular Route + (首都高速都心環状線) traced from OpenStreetMap and driven straight off + Circuit.samples, so the map and the lap timing share one geometry. - SOUTH straight — Daikoku Futo PA: the legendary meet spot. A big - drivable parking area off the expressway with painted - bays, light towers, and the spawn pad. Cars spawn HERE. - EAST straight — Rainbow Bridge: white suspension towers, cross beams, - rainbow-lit rails. Odaiba sits beyond it: a blocky - Unicorn-Gundam-style statue + a giant neon ferris - wheel (Palette Town wheel). - NORTH straight — Yamate-style tunnel: long enclosed bore with strip - lights (the C1/Yamate underground feel). - WEST straight — City skyline run past a Tokyo Tower replica - (orange/white, twin decks, antenna). - Everywhere — procedural neon skyline inside and outside the loop, - Tatsumi-PA-style overlook pad (visual, NE). + What that buys you, all of it authentic: + * The true C1 shape — Tanimachi JCT, Miyakezaka, Kasumigaseki, Ginza, + Kyobashi, Edobashi, Hitotsubashi — in their real relative positions. + * Real tunnel sections (the C1 spends a lot of its length underground) + built as lit bores; real bridge/viaduct sections built elevated on + pillars over the city. + * Tokyo landmarks placed at their true coordinates relative to the + loop: Tokyo Tower, the Imperial Palace moat, Tokyo Station, Ginza, + the Shiodome towers, Roppongi Hills, the National Diet, Skytree. + * A Daikoku-style PA hanging off the straightest non-tunnel run. - NOT geographically accurate — it's a themed driving loop, not a map of - Tokyo. Gameplay parts keep their contract names (SpawnPad, FinishLine, - KillWall_*) so nothing else changes. + Gameplay parts keep their contract names (SpawnPad, FinishLine, KillWall_*) + so nothing else in the codebase changes. ]] local Workspace = game:GetService("Workspace") +local ReplicatedStorage = game:GetService("ReplicatedStorage") +local Circuit = require(ReplicatedStorage:WaitForChild("Shared"):WaitForChild("Circuit")) local MapBuilder = {} --- Loop geometry ---------------------------------------------------------------- -local ROAD_WIDTH = 60 -local HX = 1200 -- half-extent X -local HZ = 800 -- half-extent Z -local CORNER_R = 300 -local STEP = 60 -- straight sample spacing -local ARC_STEPS = 12 -- samples per 90-degree corner +local ROAD_WIDTH = Circuit.ROAD_WIDTH local WALL_HEIGHT = 26 +local UP = Vector3.new(0, 1, 0) local NEON_PINK = Color3.fromRGB(255, 40, 120) local NEON_BLUE = Color3.fromRGB(40, 160, 255) local NEON_TEAL = Color3.fromRGB(40, 230, 200) -local RAINBOW = { - Color3.fromRGB(255, 60, 60), - Color3.fromRGB(255, 160, 40), - Color3.fromRGB(250, 230, 60), - Color3.fromRGB(80, 230, 90), - Color3.fromRGB(60, 150, 255), - Color3.fromRGB(170, 90, 255), -} - -local UP = Vector3.new(0, 1, 0) +local NEON_AMBER = Color3.fromRGB(255, 170, 60) local function part(props: { [string]: any }): Part local p = Instance.new("Part") @@ -63,17 +47,16 @@ local function part(props: { [string]: any }): Part return p end --- Deterministic pseudo-random for scenery (same city every server) ------------ +-- Deterministic pseudo-random so every server builds the same city ------------ local function det(seed: number): number local x = math.sin(seed * 12.9898) * 43758.5453 return x - math.floor(x) end --- Green highway sign with bilingual text --------------------------------------- local function makeSign(text: string, cf: CFrame, parent: Instance) local board = part({ Name = "Sign", - Size = Vector3.new(30, 9, 1), + Size = Vector3.new(34, 10, 1), CFrame = cf, Color = Color3.fromRGB(18, 90, 60), Material = Enum.Material.SmoothPlastic, @@ -93,579 +76,542 @@ local function makeSign(text: string, cf: CFrame, parent: Instance) label.Parent = gui end --- Centerline samples ------------------------------------------------------------ -type Sample = { pos: Vector3, piece: string } - -local function genSamples(): { Sample } - local samples: { Sample } = {} - local sx = HX - CORNER_R - local sz = HZ - CORNER_R - - local function push(pos: Vector3, piece: string) - table.insert(samples, { pos = pos, piece = piece }) - end - local function arc(cx: number, cz: number, a0: number, piece: string) - for i = 1, ARC_STEPS do - local a = a0 + (i / ARC_STEPS) * (math.pi / 2) - push(Vector3.new(cx + CORNER_R * math.sin(a), 0, cz + CORNER_R * math.cos(a)), piece) - end - end - - -- South straight (Daikoku side), heading +X - local x = -sx - while x <= sx do - push(Vector3.new(x, 0, HZ), "south") - x += STEP - end - arc(sx, sz, 0, "corner_se") - -- East straight (Rainbow Bridge), heading -Z - local z = sz - STEP - while z >= -sz do - push(Vector3.new(HX, 0, z), "east") - z -= STEP - end - arc(sx, -sz, math.pi / 2, "corner_ne") - -- North straight (Yamate tunnel), heading -X - x = sx - STEP - while x >= -sx do - push(Vector3.new(x, 0, -HZ), "north") - x -= STEP - end - arc(-sx, -sz, math.pi, "corner_nw") - -- West straight (Tokyo Tower run), heading +Z - z = -sz + STEP - while z <= sz do - push(Vector3.new(-HX, 0, z), "west") - z += STEP - end - arc(-sx, sz, 3 * math.pi / 2, "corner_sw") - - return samples -end - --- Daikoku PA entrance gap on the south straight's OUTER side ------------------- -local DAIKOKU_GAP_HALF = 270 - -local function isDaikokuGap(mid: Vector3, piece: string, outer: boolean): boolean - return piece == "south" and outer and math.abs(mid.X) < DAIKOKU_GAP_HALF -end - --- The loop road itself ---------------------------------------------------------- -local function buildLoop(map: Folder, samples: { Sample }) - local roadFolder = Instance.new("Folder") - roadFolder.Name = "Expressway" - roadFolder.Parent = map +-- The expressway itself --------------------------------------------------------- +local function buildRoad(map: Folder) + local road = Instance.new("Folder") + road.Name = "Expressway" + road.Parent = map + local struct = Instance.new("Folder") + struct.Name = "Structure" + struct.Parent = map + local samples = Circuit.samples local n = #samples + for i = 1, n do local a = samples[i] local b = samples[(i % n) + 1] local delta = b.pos - a.pos + if delta.Magnitude < 0.5 then + continue + end local len = delta.Magnitude + 2 local mid = (a.pos + b.pos) / 2 local dir = delta.Unit - local right = dir:Cross(UP) -- always points OUTWARD on this loop - local cf = CFrame.lookAt(mid, mid + dir) + local flatDir = Vector3.new(dir.X, 0, dir.Z) + flatDir = flatDir.Magnitude > 0.01 and flatDir.Unit or Vector3.xAxis + local right = flatDir:Cross(UP) + local cf = CFrame.lookAt(mid, mid + dir, UP) - local road = part({ + local deck = part({ Name = "Road_" .. i, Size = Vector3.new(ROAD_WIDTH, 2, len), CFrame = cf, Color = Color3.fromRGB(28, 28, 34), Material = Enum.Material.Asphalt, }) - road.Parent = roadFolder + deck.Parent = road - local line = part({ + -- lane markings: C1 is 2 lanes each way; centre line + dashes + local centre = part({ Name = "Lane_" .. i, - Size = Vector3.new(1, 2.1, len), + Size = Vector3.new(1.2, 2.1, len), CFrame = cf * CFrame.new(0, 0.1, 0), - Color = Color3.fromRGB(230, 200, 60), + Color = Color3.fromRGB(235, 205, 70), Material = Enum.Material.Neon, }) - line.Parent = roadFolder - - local inTunnel = a.piece == "north" - for _, side in ipairs({ -1, 1 }) do - local outer = side == 1 - if isDaikokuGap(mid, a.piece, outer) then - continue + centre.Parent = road + if i % 2 == 0 then + for _, s in ipairs({ -1, 1 }) do + local dash = part({ + Name = "Dash", + Size = Vector3.new(0.8, 2.1, len * 0.55), + CFrame = cf * CFrame.new(s * ROAD_WIDTH / 4, 0.1, 0), + Color = Color3.fromRGB(220, 220, 225), + Material = Enum.Material.SmoothPlastic, + }) + dash.Parent = road end + end + + -- barriers + invisible kill walls + for _, side in ipairs({ -1, 1 }) do local off = right * (side * (ROAD_WIDTH / 2)) local rail = part({ Name = "Guardrail", Size = Vector3.new(1.5, 4, len), - CFrame = CFrame.lookAt(mid + off, mid + off + dir) * CFrame.new(0, 2, 0), + CFrame = CFrame.lookAt(mid + off, mid + off + dir, UP) * CFrame.new(0, 2, 0), Color = Color3.fromRGB(180, 180, 190), Material = Enum.Material.Metal, }) - rail.Parent = map + rail.Parent = struct local woff = right * (side * (ROAD_WIDTH / 2 + 2)) local wall = part({ Name = "KillWall", Size = Vector3.new(2, WALL_HEIGHT, len + 6), - CFrame = CFrame.lookAt(mid + woff, mid + woff + dir) * CFrame.new(0, WALL_HEIGHT / 2, 0), + CFrame = CFrame.lookAt(mid + woff, mid + woff + dir, UP) * CFrame.new(0, WALL_HEIGHT / 2, 0), Transparency = 1, CanCollide = true, }) - wall.Parent = map - - -- Rainbow rail lights on the bridge; neon pylons elsewhere (not in tunnel) - if a.piece == "east" then - local lamp = part({ - Name = "BridgeLight", - Size = Vector3.new(1.2, 1.2, 1.2), - CFrame = CFrame.new(mid + off + Vector3.new(0, 4.6, 0)), - Color = RAINBOW[(i % #RAINBOW) + 1], - Material = Enum.Material.Neon, - }) - lamp.Parent = map - elseif not inTunnel and i % 3 == 0 then - local pylon = part({ - Name = "Pylon", - Size = Vector3.new(1, 14, 1), - CFrame = CFrame.new(mid + right * (side * (ROAD_WIDTH / 2 + 3)) + Vector3.new(0, 7, 0)), - Color = outer and NEON_PINK or NEON_BLUE, - Material = Enum.Material.Neon, - }) - pylon.Parent = map - end + wall.Parent = struct end - -- Yamate tunnel bore over the north straight ----------------------------- - if inTunnel then - local h = 22 + if a.tunnel then + -- Yamate/C1-style bore: walls, ceiling, strip lighting + local h = 24 for _, side in ipairs({ -1, 1 }) do local woff = right * (side * (ROAD_WIDTH / 2 + 1.5)) - local wallT = part({ + local w = part({ Name = "TunnelWall", Size = Vector3.new(3, h, len + 2), - CFrame = CFrame.lookAt(mid + woff, mid + woff + dir) * CFrame.new(0, h / 2, 0), + CFrame = CFrame.lookAt(mid + woff, mid + woff + dir, UP) * CFrame.new(0, h / 2, 0), Color = Color3.fromRGB(38, 38, 46), Material = Enum.Material.Concrete, }) - wallT.Parent = map + w.Parent = struct end - local ceiling = part({ + local ceil = part({ Name = "TunnelCeiling", Size = Vector3.new(ROAD_WIDTH + 6, 2, len + 2), CFrame = cf * CFrame.new(0, h + 1, 0), Color = Color3.fromRGB(30, 30, 38), Material = Enum.Material.Concrete, }) - ceiling.Parent = map + ceil.Parent = struct if i % 2 == 0 then local strip = part({ Name = "TunnelLight", - Size = Vector3.new(ROAD_WIDTH * 0.5, 0.6, 2), + Size = Vector3.new(ROAD_WIDTH * 0.5, 0.6, 2.5), CFrame = cf * CFrame.new(0, h - 0.5, 0), - Color = i % 4 == 0 and NEON_BLUE or NEON_TEAL, + Color = (i % 6 == 0) and NEON_BLUE or NEON_TEAL, Material = Enum.Material.Neon, }) - strip.Parent = map + strip.Parent = struct + end + else + -- Elevated viaduct: support pillars down to street level + if i % 3 == 0 and a.pos.Y > 18 then + local pil = part({ + Name = "Pillar", + Size = Vector3.new(9, a.pos.Y, 9), + CFrame = CFrame.new(mid.X, a.pos.Y / 2 - 1, mid.Z), + Color = Color3.fromRGB(60, 60, 68), + Material = Enum.Material.Concrete, + }) + pil.Parent = struct + end + -- sodium lamps along the deck + if i % 4 == 0 then + local lamp = part({ + Name = "RoadLight", + Size = Vector3.new(1.4, 1.4, 1.4), + CFrame = CFrame.new(mid + right * (ROAD_WIDTH / 2) + Vector3.new(0, 5.5, 0)), + Color = NEON_AMBER, + Material = Enum.Material.Neon, + }) + lamp.Parent = struct + end + if i % 9 == 0 then + local pylon = part({ + Name = "Pylon", + Size = Vector3.new(1, 14, 1), + CFrame = CFrame.new(mid - right * (ROAD_WIDTH / 2 + 3) + Vector3.new(0, 7, 0)), + Color = (i % 18 == 0) and NEON_PINK or NEON_BLUE, + Material = Enum.Material.Neon, + }) + pylon.Parent = struct end end end end --- Daikoku Futo PA ---------------------------------------------------------------- -local function buildDaikoku(map: Folder) +-- Junction gantry signs at each real interchange --------------------------------- +local function buildJunctionSigns(map: Folder) local folder = Instance.new("Folder") - folder.Name = "DaikokuPA" + folder.Name = "Signs" folder.Parent = map - local z0 = HZ + ROAD_WIDTH / 2 -- pad starts at the road's outer edge - local depth = 220 - local halfW = 260 - - local pad = part({ - Name = "DaikokuPad", - Size = Vector3.new(halfW * 2, 2, depth), - CFrame = CFrame.new(0, 0, z0 + depth / 2), - Color = Color3.fromRGB(34, 34, 40), - Material = Enum.Material.Asphalt, - }) - pad.Parent = folder - - -- Painted parking bays (two rows, like the real lot's herringbone rows) - for row = 0, 1 do - local zRow = z0 + 70 + row * 90 - for bx = -5, 5 do - local lineP = part({ - Name = "BayLine", - Size = Vector3.new(1, 2.1, 22), - CFrame = CFrame.new(bx * 42, 0.1, zRow), - Color = Color3.fromRGB(230, 230, 235), - Material = Enum.Material.SmoothPlastic, - }) - lineP.Parent = folder + local samples = Circuit.samples + local n = #samples + local seen: { [string]: boolean } = {} + for i = 1, n do + local s = samples[i] + if s.tunnel or seen[s.piece] then + continue end + seen[s.piece] = true + local fwd = Circuit.forwardAt(i) + local pos = s.pos + Vector3.new(0, 15, 0) + makeSign(s.piece, CFrame.lookAt(pos, pos - fwd, UP), folder) end - - -- Light towers (the tall lot floods) + perimeter kill walls - for _, corner in ipairs({ - { -halfW + 20, z0 + 30 }, - { halfW - 20, z0 + 30 }, - { -halfW + 20, z0 + depth - 20 }, - { halfW - 20, z0 + depth - 20 }, - }) do - local px, pz = corner[1], corner[2] - local pole = part({ - Name = "LightPole", - Size = Vector3.new(2, 34, 2), - CFrame = CFrame.new(px, 17, pz), - Color = Color3.fromRGB(120, 120, 130), - Material = Enum.Material.Metal, - }) - pole.Parent = folder - local head = part({ - Name = "LightHead", - Size = Vector3.new(8, 1.5, 8), - CFrame = CFrame.new(px, 34.5, pz), - Color = Color3.fromRGB(255, 240, 200), - Material = Enum.Material.Neon, - }) - head.Parent = folder - end - - -- Perimeter walls (invisible) on the three non-road sides - local wallDefs = { - { Vector3.new(halfW * 2 + 4, WALL_HEIGHT, 2), CFrame.new(0, WALL_HEIGHT / 2, z0 + depth + 1) }, - { Vector3.new(2, WALL_HEIGHT, depth + 4), CFrame.new(-halfW - 1, WALL_HEIGHT / 2, z0 + depth / 2) }, - { Vector3.new(2, WALL_HEIGHT, depth + 4), CFrame.new(halfW + 1, WALL_HEIGHT / 2, z0 + depth / 2) }, - } - for _, def in ipairs(wallDefs) do - local w = part({ - Name = "KillWall_PA", - Size = def[1], - CFrame = def[2], - Transparency = 1, - CanCollide = true, - }) - w.Parent = folder - end - - -- Spawn pad (cars appear here — Daikoku is the meet) ----------------------- - -- Subtle painted zone, NOT neon: the cars are the stars of the meet, the - -- floor must never outshine them. - local spawn = part({ - Name = "SpawnPad", - Size = Vector3.new(120, 1, 60), - CFrame = CFrame.new(0, 1.2, z0 + 130), - Color = Color3.fromRGB(45, 60, 55), - Material = Enum.Material.Asphalt, - Transparency = 0, - }) - spawn.Parent = folder - - makeSign("大黒PA DAIKOKU PA", CFrame.new(0, 12, z0 + 4) * CFrame.Angles(0, math.pi, 0), folder) - - -- Mode gate --------------------------------------------------------------- - -- The exit slip road out of the PA. Crossing this line is what ARMS your - -- chosen mode into a live run (ModeManager watches position, not this part, - -- but the marker tells the player exactly where the commitment happens). - local gate = part({ - Name = "ExitGate", - Size = Vector3.new(220, 1, 4), - CFrame = CFrame.new(0, 1.4, z0 + 6), - Color = Color3.fromRGB(255, 200, 60), - Material = Enum.Material.Neon, - Transparency = 0.35, - CanCollide = false, - }) - gate.Parent = folder - - makeSign("出口 EXIT -> WANGAN", CFrame.new(0, 20, z0 + 30) * CFrame.Angles(0, math.pi, 0), folder) end --- Rainbow Bridge towers over the east straight ----------------------------------- -local function buildRainbowBridge(map: Folder) - local folder = Instance.new("Folder") - folder.Name = "RainbowBridge" - folder.Parent = map +-- Daikoku-style PA --------------------------------------------------------------- +local function buildPA(map: Folder) + local pa = Instance.new("Folder") + pa.Name = "ParkingArea" + pa.Parent = map - for _, tz in ipairs({ 250, -250 }) do - for _, side in ipairs({ -1, 1 }) do - local tx = HX + side * (ROAD_WIDTH / 2 + 5) - local tower = part({ - Name = "BridgeTower", - Size = Vector3.new(6, 95, 6), - CFrame = CFrame.new(tx, 47.5, tz), - Color = Color3.fromRGB(235, 235, 240), - Material = Enum.Material.Concrete, - }) - tower.Parent = folder - local beacon = part({ - Name = "TowerBeacon", - Size = Vector3.new(2, 2, 2), - CFrame = CFrame.new(tx, 96, tz), - Color = Color3.fromRGB(255, 80, 80), - Material = Enum.Material.Neon, - }) - beacon.Parent = folder - end - local beam = part({ - Name = "TowerBeam", - Size = Vector3.new(ROAD_WIDTH + 22, 4, 5), - CFrame = CFrame.new(HX, 62, tz), - Color = Color3.fromRGB(235, 235, 240), - Material = Enum.Material.Concrete, + local c = Circuit.PA_CENTER + local R = Circuit.PA_RADIUS + + local slab = part({ + Name = "PAFloor", + Size = Vector3.new(R * 2, 2, R * 2), + CFrame = CFrame.new(c), + Color = Color3.fromRGB(42, 42, 48), + Material = Enum.Material.Concrete, + }) + slab.Parent = pa + + -- painted bays + for i = -4, 4 do + local bay = part({ + Name = "Bay", + Size = Vector3.new(1, 2.2, 90), + CFrame = CFrame.new(c + Vector3.new(i * 34, 0.1, 0)), + Color = Color3.fromRGB(225, 225, 230), + Material = Enum.Material.SmoothPlastic, }) - beam.Parent = folder - end - makeSign( - "レインボーブリッジ RAINBOW BRIDGE", - CFrame.new(HX, 14, 330) * CFrame.Angles(0, math.pi / 2, 0), - folder - ) -end - --- Odaiba: blocky Unicorn-Gundam-style statue + giant ferris wheel ---------------- -local function buildOdaiba(map: Folder) - local folder = Instance.new("Folder") - folder.Name = "Odaiba" - folder.Parent = map - - local gx, gz = HX + 150, 480 -- statue stands beyond the bridge's south end - local white = Color3.fromRGB(235, 235, 240) - - local function block(name: string, size: Vector3, cf: CFrame, color: Color3, mat: Enum.Material?) - local b = part({ - Name = name, - Size = size, - CFrame = cf, - Color = color, - Material = mat or Enum.Material.SmoothPlastic, - }) - b.Parent = folder - return b + bay.Parent = pa end - -- Base plinth - block( - "GundamBase", - Vector3.new(40, 4, 40), - CFrame.new(gx, 2, gz), - Color3.fromRGB(60, 60, 70), - Enum.Material.Concrete - ) - -- Legs - block("GundamLegL", Vector3.new(7, 20, 9), CFrame.new(gx - 6, 14, gz), white) - block("GundamLegR", Vector3.new(7, 20, 9), CFrame.new(gx + 6, 14, gz), white) - -- Hips + torso - block("GundamHips", Vector3.new(16, 6, 10), CFrame.new(gx, 27, gz), white) - block("GundamTorso", Vector3.new(15, 14, 10), CFrame.new(gx, 37, gz), white) - block("GundamChest", Vector3.new(9, 5, 3), CFrame.new(gx, 39, gz - 6), Color3.fromRGB(60, 90, 220)) - block("GundamCore", Vector3.new(4, 3, 2), CFrame.new(gx, 34, gz - 6), Color3.fromRGB(230, 60, 60)) - -- Shoulders + arms - block("GundamShoulderL", Vector3.new(6, 5, 7), CFrame.new(gx - 11, 42, gz), white) - block("GundamShoulderR", Vector3.new(6, 5, 7), CFrame.new(gx + 11, 42, gz), white) - block("GundamArmL", Vector3.new(4.5, 16, 5), CFrame.new(gx - 11, 32, gz), white) - block("GundamArmR", Vector3.new(4.5, 16, 5), CFrame.new(gx + 11, 32, gz), white) - -- Head with glowing eyes + unicorn horn - block("GundamHead", Vector3.new(6, 5.5, 5.5), CFrame.new(gx, 47, gz), white) - block("GundamEyes", Vector3.new(4, 1, 0.6), CFrame.new(gx, 47.5, gz - 2.8), NEON_TEAL, Enum.Material.Neon) - block( - "GundamHorn", - Vector3.new(0.8, 6, 0.8), - CFrame.new(gx, 53, gz - 1) * CFrame.Angles(math.rad(15), 0, 0), - Color3.fromRGB(250, 220, 90), - Enum.Material.Neon - ) - -- Psycho-frame glow strips on the legs/torso (the Unicorn's pink frame) - block("GundamFrameL", Vector3.new(1, 14, 1), CFrame.new(gx - 6, 14, gz - 4.6), NEON_PINK, Enum.Material.Neon) - block("GundamFrameR", Vector3.new(1, 14, 1), CFrame.new(gx + 6, 14, gz - 4.6), NEON_PINK, Enum.Material.Neon) - - -- Giant ferris wheel (Palette Town) ----------------------------------------- - local wx, wz = HX + 240, 120 - local radius = 55 - local hub = CFrame.new(wx, radius + 24, wz) - block( - "WheelSupportL", - Vector3.new(4, radius + 26, 4), - CFrame.new(wx - 18, (radius + 24) / 2, wz) * CFrame.Angles(0, 0, math.rad(-14)), - Color3.fromRGB(120, 120, 130), - Enum.Material.Metal - ) - block( - "WheelSupportR", - Vector3.new(4, radius + 26, 4), - CFrame.new(wx + 18, (radius + 24) / 2, wz) * CFrame.Angles(0, 0, math.rad(14)), - Color3.fromRGB(120, 120, 130), - Enum.Material.Metal - ) - block("WheelHub", Vector3.new(6, 6, 6), hub, Color3.fromRGB(200, 200, 210), Enum.Material.Metal) - for k = 1, 16 do - local ang = (k / 16) * math.pi * 2 - local cab = part({ - Name = "WheelCabin", - Size = Vector3.new(4, 4, 4), - Shape = Enum.PartType.Ball, - CFrame = hub * CFrame.new(math.cos(ang) * radius, math.sin(ang) * radius, 0), - Color = RAINBOW[(k % #RAINBOW) + 1], - Material = Enum.Material.Neon, - }) - cab.Parent = folder - if k % 2 == 0 then - local spoke = part({ - Name = "WheelSpoke", - Size = Vector3.new(0.8, radius, 0.8), - CFrame = hub * CFrame.Angles(0, 0, ang) * CFrame.new(0, radius / 2, 0), - Color = Color3.fromRGB(150, 150, 160), + -- light towers + for _, sx in ipairs({ -1, 1 }) do + for _, sz in ipairs({ -1, 1 }) do + local mast = part({ + Name = "PAMast", + Size = Vector3.new(3, 60, 3), + CFrame = CFrame.new(c + Vector3.new(sx * (R - 30), 30, sz * (R - 30))), + Color = Color3.fromRGB(70, 70, 78), Material = Enum.Material.Metal, }) - spoke.Parent = folder + mast.Parent = pa + local head = part({ + Name = "PALight", + Size = Vector3.new(10, 2, 10), + CFrame = CFrame.new(c + Vector3.new(sx * (R - 30), 60, sz * (R - 30))), + Color = Color3.fromRGB(255, 240, 200), + Material = Enum.Material.Neon, + }) + head.Parent = pa + local pl = Instance.new("PointLight") + pl.Brightness = 3 + pl.Range = 120 + pl.Color = Color3.fromRGB(255, 235, 190) + pl.Parent = head end end - makeSign("お台場 ODAIBA", CFrame.new(HX + 60, 12, 300) * CFrame.Angles(0, math.pi / 2, 0), folder) -end - --- Tokyo Tower replica on the west side -------------------------------------------- -local function buildTokyoTower(map: Folder) - local folder = Instance.new("Folder") - folder.Name = "TokyoTower" - folder.Parent = map - - local tx, tz = -HX - 220, 0 - local orange = Color3.fromRGB(235, 100, 40) - local levels = { - { w = 64, h = 50, c = orange }, - { w = 44, h = 45, c = Color3.fromRGB(240, 240, 245) }, - { w = 28, h = 45, c = orange }, - { w = 15, h = 40, c = Color3.fromRGB(240, 240, 245) }, - } - local y = 0 - for li, lv in ipairs(levels) do - local sec = part({ - Name = "TowerSection" .. li, - Size = Vector3.new(lv.w, lv.h, lv.w), - CFrame = CFrame.new(tx, y + lv.h / 2, tz), - Color = lv.c, - Material = Enum.Material.Metal, - }) - sec.Parent = folder - y += lv.h + -- link ramp from the PA back onto the expressway + local samples = Circuit.samples + local bestIdx, bestD = 1, math.huge + for i, s in ipairs(samples) do + local d = (Vector3.new(s.pos.X, 0, s.pos.Z) - Vector3.new(c.X, 0, c.Z)).Magnitude + if not s.tunnel and d < bestD then + bestIdx, bestD = i, d + end end - -- Observation decks + antenna - local deck1 = part({ - Name = "MainDeck", - Size = Vector3.new(36, 8, 36), - CFrame = CFrame.new(tx, 98, tz), - Color = Color3.fromRGB(250, 245, 235), - Material = Enum.Material.Neon, - }) - deck1.Parent = folder - local deck2 = part({ - Name = "TopDeck", - Size = Vector3.new(20, 6, 20), - CFrame = CFrame.new(tx, 152, tz), - Color = Color3.fromRGB(250, 245, 235), - Material = Enum.Material.Neon, - }) - deck2.Parent = folder - local antenna = part({ - Name = "Antenna", - Size = Vector3.new(2, 40, 2), - CFrame = CFrame.new(tx, 200, tz), - Color = orange, - Material = Enum.Material.Neon, - }) - antenna.Parent = folder -end - --- Tatsumi-style overlook pad (visual scenery, NE corner outside) ------------------ -local function buildTatsumi(map: Folder) - local folder = Instance.new("Folder") - folder.Name = "TatsumiPA" - folder.Parent = map - local px, pz = HX - 380, -HZ - 120 - local pad = part({ - Name = "TatsumiPad", - Size = Vector3.new(160, 2, 80), - CFrame = CFrame.new(px, 0, pz), - Color = Color3.fromRGB(34, 34, 40), + local road = samples[bestIdx].pos + local ramp = part({ + Name = "PARamp", + Size = Vector3.new(46, 2, (road - c).Magnitude), + CFrame = CFrame.lookAt((road + c) / 2, road, UP), + Color = Color3.fromRGB(38, 38, 44), Material = Enum.Material.Asphalt, }) - pad.Parent = folder - for i = 0, 4 do - local lamp = part({ - Name = "TatsumiLamp", - Size = Vector3.new(1, 12, 1), - CFrame = CFrame.new(px - 70 + i * 35, 6, pz - 30), - Color = Color3.fromRGB(255, 240, 200), - Material = Enum.Material.Neon, - }) - lamp.Parent = folder - end - makeSign("辰巳PA TATSUMI PA", CFrame.new(px, 11, pz + 30), folder) + ramp.Parent = pa + + local pad = part({ + Name = "SpawnPad", + Size = Vector3.new(120, 2.4, 120), + CFrame = CFrame.new(c + Vector3.new(0, 0.3, 0)), + Color = Color3.fromRGB(20, 20, 26), + Material = Enum.Material.Neon, + Transparency = 0.25, + }) + pad.Parent = pa + + makeSign("大黒PA DAIKOKU PA", CFrame.new(c + Vector3.new(0, 26, -R + 10)), pa) end --- Neon skyline: towers inside the loop (city core) and outside --------------------- +-- Tokyo landmarks, at their true positions relative to the loop ----------------- +local function buildLandmarks(map: Folder) + local folder = Instance.new("Folder") + folder.Name = "Landmarks" + folder.Parent = map + + -- Tokyo Tower (-3557, -5638): orange/white lattice, twin decks, antenna + do + local base = Vector3.new(-3557, 0, -5638) + local seg = 12 + local H = 333 / 0.28 / 6 -- scaled for readability at driving speed + for i = 0, seg - 1 do + local f0 = i / seg + local w0 = 90 * (1 - f0) ^ 1.6 + 8 + local h = H / seg + local col = (i % 2 == 0) and Color3.fromRGB(235, 90, 40) or Color3.fromRGB(240, 240, 245) + local s = part({ + Name = "TokyoTower", + Size = Vector3.new(w0, h, w0), + CFrame = CFrame.new(base + Vector3.new(0, f0 * H + h / 2, 0)), + Color = col, + Material = Enum.Material.DiamondPlate, + Transparency = 0.12, + }) + s.Parent = folder + end + for _, dh in ipairs({ 0.38, 0.66 }) do + local deck = part({ + Name = "TowerDeck", + Size = Vector3.new(70, 10, 70), + CFrame = CFrame.new(base + Vector3.new(0, dh * H, 0)), + Color = Color3.fromRGB(250, 250, 255), + Material = Enum.Material.Neon, + Transparency = 0.15, + }) + deck.Parent = folder + end + local ant = part({ + Name = "TowerAntenna", + Size = Vector3.new(5, H * 0.22, 5), + CFrame = CFrame.new(base + Vector3.new(0, H + H * 0.11, 0)), + Color = Color3.fromRGB(220, 90, 60), + Material = Enum.Material.Neon, + }) + ant.Parent = folder + end + + -- Tokyo Skytree (17523, 14829): far to the north-east, visible on the horizon + do + local base = Vector3.new(17523, 0, 14829) + local H = 634 / 0.28 / 6 + for i = 0, 9 do + local f = i / 10 + local w = 70 * (1 - f) ^ 1.3 + 10 + local s = part({ + Name = "Skytree", + Size = Vector3.new(w, H / 10, w), + CFrame = CFrame.new(base + Vector3.new(0, f * H + H / 20, 0)), + Color = Color3.fromRGB(215, 225, 240), + Material = Enum.Material.Neon, + Transparency = 0.25, + }) + s.Parent = folder + end + end + + -- Imperial Palace (-1173, 4933): dark moat ring + green grounds + stone wall + do + local c = Vector3.new(-1173, 0, 4933) + local grounds = part({ + Name = "PalaceGrounds", + Size = Vector3.new(1500, 6, 1400), + CFrame = CFrame.new(c + Vector3.new(0, 3, 0)), + Color = Color3.fromRGB(24, 52, 32), + Material = Enum.Material.Grass, + }) + grounds.Parent = folder + for i = 0, 23 do + local a = (i / 24) * math.pi * 2 + local moat = part({ + Name = "PalaceMoat", + Size = Vector3.new(150, 3, 60), + CFrame = CFrame.new(c + Vector3.new(math.sin(a) * 800, 5, math.cos(a) * 760)) * CFrame.Angles(0, -a, 0), + Color = Color3.fromRGB(18, 34, 48), + Material = Enum.Material.Water, + }) + moat.Parent = folder + local wall = part({ + Name = "PalaceWall", + Size = Vector3.new(150, 26, 20), + CFrame = CFrame.new(c + Vector3.new(math.sin(a) * 720, 13, math.cos(a) * 690)) + * CFrame.Angles(0, -a, 0), + Color = Color3.fromRGB(90, 88, 80), + Material = Enum.Material.Slate, + }) + wall.Parent = folder + end + end + + -- Ginza (2764, -525): bright signage district + do + local c = Vector3.new(2764, 0, -525) + for i = 1, 26 do + local a = det(i * 3.1) * math.pi * 2 + local r = 260 + det(i * 7.7) * 900 + local h = 260 + det(i * 5.3) * 420 + local p = c + Vector3.new(math.sin(a) * r, h / 2, math.cos(a) * r) + local b = part({ + Name = "GinzaBlock", + Size = Vector3.new(150 + det(i) * 90, h, 150 + det(i * 2) * 90), + CFrame = CFrame.new(p), + Color = Color3.fromRGB(26, 26, 34), + Material = Enum.Material.Glass, + Reflectance = 0.25, + }) + b.Parent = folder + local sign = part({ + Name = "GinzaSign", + Size = Vector3.new(16, h * 0.5, 6), + CFrame = CFrame.new(p + Vector3.new(80, 0, 0)), + Color = ({ NEON_PINK, NEON_BLUE, NEON_TEAL, NEON_AMBER })[(i % 4) + 1], + Material = Enum.Material.Neon, + }) + sign.Parent = folder + end + end + + -- Shiodome towers (1117, -2847): the glass cluster the C1 threads past + do + local c = Vector3.new(1117, 0, -2847) + for i = 1, 7 do + local h = 700 + det(i * 11.3) * 500 + local p = c + Vector3.new((det(i * 2.2) - 0.5) * 1100, h / 2, (det(i * 4.4) - 0.5) * 1100) + local t = part({ + Name = "ShiodomeTower", + Size = Vector3.new(170, h, 170), + CFrame = CFrame.new(p), + Color = Color3.fromRGB(30, 34, 44), + Material = Enum.Material.Glass, + Reflectance = 0.35, + }) + t.Parent = folder + local crown = part({ + Name = "TowerCrown", + Size = Vector3.new(175, 8, 175), + CFrame = CFrame.new(p + Vector3.new(0, h / 2, 0)), + Color = NEON_BLUE, + Material = Enum.Material.Neon, + }) + crown.Parent = folder + end + end + + -- Tokyo Station (3448, 3367): red brick Marunouchi facade + do + local c = Vector3.new(3448, 0, 3367) + local hall = part({ + Name = "TokyoStation", + Size = Vector3.new(900, 120, 220), + CFrame = CFrame.new(c + Vector3.new(0, 60, 0)), + Color = Color3.fromRGB(120, 52, 44), + Material = Enum.Material.Brick, + }) + hall.Parent = folder + for _, sx in ipairs({ -320, 0, 320 }) do + local dome = part({ + Name = "StationDome", + Shape = Enum.PartType.Ball, + Size = Vector3.new(150, 150, 150), + CFrame = CFrame.new(c + Vector3.new(sx, 140, 0)), + Color = Color3.fromRGB(60, 74, 70), + Material = Enum.Material.Slate, + }) + dome.Parent = folder + end + end + + -- Roppongi Hills (-8715, -4899) + National Diet (-3712, 1280) + do + local rh = Vector3.new(-8715, 0, -4899) + local mori = part({ + Name = "RoppongiHills", + Size = Vector3.new(260, 1300, 260), + CFrame = CFrame.new(rh + Vector3.new(0, 650, 0)), + Color = Color3.fromRGB(34, 36, 46), + Material = Enum.Material.Glass, + Reflectance = 0.3, + }) + mori.Parent = folder + + local diet = Vector3.new(-3712, 0, 1280) + local body = part({ + Name = "NationalDiet", + Size = Vector3.new(700, 130, 260), + CFrame = CFrame.new(diet + Vector3.new(0, 65, 0)), + Color = Color3.fromRGB(150, 148, 140), + Material = Enum.Material.Granite, + }) + body.Parent = folder + local tower = part({ + Name = "DietTower", + Size = Vector3.new(150, 190, 150), + CFrame = CFrame.new(diet + Vector3.new(0, 190, 0)), + Color = Color3.fromRGB(150, 148, 140), + Material = Enum.Material.Granite, + }) + tower.Parent = folder + end +end + +-- Procedural night city filling the space around the loop ----------------------- local function buildSkyline(map: Folder) local folder = Instance.new("Folder") folder.Name = "Skyline" folder.Parent = map - local accents = { NEON_PINK, NEON_BLUE, NEON_TEAL, Color3.fromRGB(200, 120, 255) } - local function tower(seed: number, x: number, z: number) - local width = 40 + det(seed) * 50 - local depth = 40 + det(seed + 1) * 50 - local height = 50 + det(seed + 2) * 170 - local t = part({ - Name = "Tower", - Size = Vector3.new(width, height, depth), - CFrame = CFrame.new(x, height / 2 - 10, z), - Color = Color3.fromRGB(16, 16, 24), - Material = Enum.Material.Slate, - }) - t.Parent = folder - local accent = accents[(math.floor(seed) % #accents) + 1] - local band = part({ - Name = "TowerBand", - Size = Vector3.new(width + 0.4, 2 + det(seed + 5) * 4, depth + 0.4), - CFrame = CFrame.new(x, height * (0.35 + det(seed + 6) * 0.4), z), - Color = accent, - Material = Enum.Material.Neon, - Transparency = 0.2, - }) - band.Parent = folder - local roof = part({ - Name = "TowerRoof", - Size = Vector3.new(width * 0.3, 1.5, depth * 0.3), - CFrame = CFrame.new(x, height - 9, z), - Color = accent, - Material = Enum.Material.Neon, - }) - roof.Parent = folder - end + local samples = Circuit.samples + local n = #samples + local placed = 0 - -- City core inside the loop - local seed = 1 - for gx = -2, 2 do - for gz = -1, 1 do - seed += 7 - local x = gx * 300 + (det(seed) - 0.5) * 120 - local z = gz * 300 + (det(seed + 3) - 0.5) * 120 - tower(seed, x, z) + for i = 1, 900 do + local a = det(i * 1.7) * math.pi * 2 + local r = 900 + det(i * 2.9) ^ 0.6 * 9000 + local x = math.sin(a) * r - 477 + local z = math.cos(a) * r - 473 + + -- keep buildings off the racing surface + local near = math.huge + for k = 1, n, 3 do + local s = samples[k].pos + local d = (Vector3.new(s.X, 0, s.Z) - Vector3.new(x, 0, z)).Magnitude + if d < near then + near = d + end end - end - -- Outer ring (skip landmark zones: Odaiba east, Tokyo Tower west-center, - -- Daikoku south-center) - for i = 1, 26 do - seed += 11 - local ang = (i / 26) * math.pi * 2 - local rad = 1750 + det(seed + 4) * 350 - local x = math.cos(ang) * rad - local z = math.sin(ang) * rad * 0.75 - local skip = (x > HX and math.abs(z) < 620) -- Odaiba - or (x < -HX and math.abs(z) < 260) -- Tokyo Tower - or (z > HZ and math.abs(x) < 420) -- Daikoku - if not skip then - tower(seed, x, z) + if near < 220 then + continue + end + + local h = 120 + det(i * 5.1) ^ 2.2 * 900 + local w = 90 + det(i * 8.3) * 130 + local b = part({ + Name = "Building", + Size = Vector3.new(w, h, w), + CFrame = CFrame.new(x, h / 2, z), + Color = Color3.fromRGB(18 + det(i) * 16, 18 + det(i * 2) * 16, 26 + det(i * 3) * 20), + Material = Enum.Material.Glass, + Reflectance = 0.18, + }) + b.Parent = folder + placed += 1 + + if det(i * 13.7) > 0.72 then + local sign = part({ + Name = "NeonSign", + Size = Vector3.new(12, h * 0.42, 5), + CFrame = CFrame.new(x + w / 2, h * 0.62, z), + Color = ({ NEON_PINK, NEON_BLUE, NEON_TEAL, NEON_AMBER })[(i % 4) + 1], + Material = Enum.Material.Neon, + }) + sign.Parent = folder + end + if placed > 620 then + break end end + + -- ground plane under the whole city + local ground = part({ + Name = "Ground", + Size = Vector3.new(26000, 4, 26000), + CFrame = CFrame.new(-477, -2, -473), + Color = Color3.fromRGB(14, 14, 18), + Material = Enum.Material.Asphalt, + }) + ground.Parent = folder end function MapBuilder.build(): Folder @@ -677,45 +623,23 @@ function MapBuilder.build(): Folder local map = Instance.new("Folder") map.Name = "Map" - local samples = genSamples() - buildLoop(map, samples) - buildDaikoku(map) - buildRainbowBridge(map) - buildOdaiba(map) - buildTokyoTower(map) - buildTatsumi(map) + buildRoad(map) + buildJunctionSigns(map) + buildPA(map) + buildLandmarks(map) buildSkyline(map) - -- Finish line across the south straight just past the Daikoku exit -------- - local finish = part({ + -- Start/finish across the road at sample 1 + local s1 = Circuit.samples[1] + local fwd = Circuit.forwardAt(1) + local line = part({ Name = "FinishLine", - Size = Vector3.new(ROAD_WIDTH, 1, 6), - CFrame = CFrame.new(420, 1.5, HZ), - Color = Color3.fromRGB(240, 240, 240), + Size = Vector3.new(ROAD_WIDTH, 0.6, 8), + CFrame = CFrame.lookAt(s1.pos + Vector3.new(0, 1.4, 0), s1.pos + Vector3.new(0, 1.4, 0) + fwd, UP), + Color = Color3.fromRGB(250, 250, 255), Material = Enum.Material.Neon, - Transparency = 0.3, }) - finish.Parent = map - - -- Safety net floor under everything ---------------------------------------- - local floor = part({ - Name = "SafetyFloor", - Size = Vector3.new((HX + 700) * 2, 2, (HZ + 700) * 2), - CFrame = CFrame.new(0, -60, 0), - Transparency = 1, - CanCollide = true, - }) - floor.Parent = map - - -- Ground plane far below road level (the "bay" the expressway floats over) - local ground = part({ - Name = "Ground", - Size = Vector3.new((HX + 700) * 2, 2, (HZ + 700) * 2), - CFrame = CFrame.new(0, -12, 0), - Color = Color3.fromRGB(10, 12, 20), - Material = Enum.Material.Slate, - }) - ground.Parent = map + line.Parent = map map.Parent = Workspace return map diff --git a/src/shared/Circuit.luau b/src/shared/Circuit.luau index eeb6b7f..a58244f 100644 --- a/src/shared/Circuit.luau +++ b/src/shared/Circuit.luau @@ -1,108 +1,902 @@ --!strict --[[ Circuit.luau (SHARED) - Single source of truth for the Wangan Loop centreline geometry. + Single source of truth for the circuit centreline. + + v4 — REAL GEOMETRY. These samples are the actual Shuto Expressway Inner + Circular Route (首都高速都心環状線, "C1") — the loop the Wangan Midnight + world is built around — traced from OpenStreetMap relation 4256008 and + projected to studs at Roblox's 1 stud = 0.28 m. + + Everything here is real: the shape of the loop, the junction names + (Tanimachi, Miyakezaka, Kasumigaseki, Ginza, Kyobashi, Edobashi, + Hitotsubashi), which sections run in tunnel and which run elevated on + viaduct — all in their true relative positions. + + Lap: ~49,400 studs (13.8 km) — about a 2:46 lap at 300 km/h. + Samples are evenly spaced at 60 studs, so the loop closes seamlessly. + Deck height is smoothed to a max ~4.3% grade so it stays drivable. + + Row format: {x, y, z, isTunnel, isBridge, zoneName} + x/z world position in studs (+X = east, +Z = north — true orientation) + y deck height: elevated viaduct ~46, tunnel bores dip to ~10 MapBuilder builds the road from these samples; gameplay (time attack, duel - gaps, cruise distance) measures progress against the SAME samples. If the - map shape ever changes, both move together — no drift. - - The key primitive is `progressOf(pos)`: how far around the lap a world - position is, in studs. From that one number you get: - - lap detection (progress wraps high -> low) - - sector splits (progress crosses a quarter boundary) - - duel gap (difference of two players' progress, wrap-aware) + gaps, cruise distance) measures progress against the SAME samples, so the + map and the scoring can never drift apart. ]] local Circuit = {} --- Loop geometry (MUST match MapBuilder's road extrusion) ---------------------- Circuit.ROAD_WIDTH = 60 -Circuit.HX = 1200 -- half-extent X -Circuit.HZ = 800 -- half-extent Z -Circuit.CORNER_R = 300 -Circuit.STEP = 60 -- straight sample spacing -Circuit.ARC_STEPS = 12 -- samples per 90-degree corner +Circuit.SOURCE = "OSM relation 4256008 (Shuto C1)" --- Daikoku PA occupies the outer side of the south straight. -Circuit.PA_GAP_HALF = 270 -Circuit.PA_ZONE_X = 400 -Circuit.PA_ZONE_Z = Circuit.HZ + 35 -- just outside the road's outer edge +-- Daikoku-style PA, parked off the loop's straightest non-tunnel run. +Circuit.PA_CENTER = Vector3.new(-4123.5, 46.0, -7469.6) +Circuit.PA_RADIUS = 260 -export type Sample = { pos: Vector3, piece: string } +export type Sample = { pos: Vector3, piece: string, tunnel: boolean, bridge: boolean } -local HX, HZ = Circuit.HX, Circuit.HZ -local CORNER_R, STEP, ARC_STEPS = Circuit.CORNER_R, Circuit.STEP, Circuit.ARC_STEPS +-- {x, y, z, tunnel, bridge, zone} ------------------------------------------------ +local RAW: { { any } } = { + { 5857.3, 34.5, 1822.9, 1, 0, "Kyobashi" }, + { 5883.9, 35.4, 1876.7, 1, 0, "Kyobashi" }, + { 5910.5, 36.3, 1930.5, 1, 0, "Kyobashi" }, + { 5936.7, 37.3, 1984.4, 0, 0, "Kyobashi" }, + { 5961.7, 38.3, 2039.0, 0, 0, "Kyobashi" }, + { 5986.7, 39.4, 2093.5, 0, 0, "Kyobashi" }, + { 6012.4, 40.4, 2147.8, 0, 0, "Kyobashi" }, + { 6038.8, 41.4, 2201.7, 0, 0, "Kyobashi" }, + { 6065.2, 42.4, 2255.6, 0, 0, "Kyobashi" }, + { 6091.6, 43.2, 2309.5, 0, 0, "Kyobashi" }, + { 6117.9, 43.9, 2363.4, 0, 0, "Takaracho" }, + { 6144.3, 44.4, 2417.3, 0, 0, "Takaracho" }, + { 6170.7, 44.9, 2471.2, 0, 0, "Takaracho" }, + { 6197.1, 45.2, 2525.1, 0, 0, "Takaracho" }, + { 6223.5, 45.5, 2578.9, 0, 0, "Takaracho" }, + { 6249.9, 45.7, 2632.8, 0, 0, "Takaracho" }, + { 6276.3, 45.8, 2686.7, 0, 0, "Takaracho" }, + { 6302.7, 45.9, 2740.6, 0, 0, "Takaracho" }, + { 6329.1, 45.9, 2794.5, 0, 0, "Takaracho" }, + { 6355.5, 46.0, 2848.4, 0, 0, "Takaracho" }, + { 6381.9, 46.0, 2902.3, 0, 0, "Takaracho" }, + { 6407.9, 46.0, 2956.4, 0, 1, "Takaracho" }, + { 6433.5, 46.0, 3010.7, 0, 1, "Takaracho" }, + { 6459.1, 46.0, 3064.9, 0, 1, "Takaracho" }, + { 6484.7, 46.0, 3119.2, 0, 1, "Takaracho" }, + { 6510.4, 46.0, 3173.5, 0, 1, "Takaracho" }, + { 6536.0, 46.0, 3227.7, 0, 1, "Takaracho" }, + { 6561.6, 46.0, 3282.0, 0, 1, "Takaracho" }, + { 6587.5, 46.0, 3336.1, 0, 1, "Takaracho" }, + { 6613.5, 46.0, 3390.2, 0, 1, "Takaracho" }, + { 6639.5, 46.0, 3444.3, 0, 1, "Takaracho" }, + { 6665.5, 46.0, 3498.4, 0, 1, "Takaracho" }, + { 6691.5, 46.0, 3552.4, 0, 1, "Takaracho" }, + { 6717.5, 46.0, 3606.5, 0, 1, "Takaracho" }, + { 6743.6, 46.0, 3660.6, 0, 1, "Takaracho" }, + { 6769.6, 46.0, 3714.7, 0, 1, "Takaracho" }, + { 6795.6, 46.0, 3768.8, 0, 1, "Takaracho" }, + { 6818.1, 46.0, 3824.4, 0, 1, "Takaracho" }, + { 6840.4, 46.0, 3880.1, 0, 1, "Edobashi junction" }, + { 6862.6, 46.0, 3935.8, 0, 1, "Edobashi junction" }, + { 6885.2, 46.0, 3991.4, 0, 1, "Edobashi junction" }, + { 6909.8, 46.0, 4046.2, 0, 1, "Edobashi junction" }, + { 6936.0, 46.0, 4100.2, 0, 1, "Edobashi junction" }, + { 6962.2, 46.0, 4154.2, 0, 1, "Edobashi junction" }, + { 6988.3, 46.0, 4208.1, 0, 1, "Edobashi junction" }, + { 7013.5, 46.0, 4262.6, 0, 1, "Edobashi junction" }, + { 7032.1, 46.0, 4319.6, 0, 1, "Edobashi junction" }, + { 7040.3, 46.0, 4378.9, 0, 1, "Edobashi junction" }, + { 7035.6, 46.0, 4438.5, 0, 1, "Edobashi junction" }, + { 7016.3, 46.0, 4495.1, 0, 1, "Edobashi junction" }, + { 6982.1, 46.0, 4544.1, 0, 1, "Edobashi junction" }, + { 6938.6, 46.0, 4585.4, 0, 1, "Edobashi junction" }, + { 6886.4, 46.0, 4614.7, 0, 1, "Edobashi junction" }, + { 6828.6, 46.0, 4630.5, 0, 1, "Edobashi junction" }, + { 6769.0, 46.0, 4636.3, 0, 1, "Edobashi junction" }, + { 6709.1, 46.0, 4634.1, 0, 1, "Edobashi junction" }, + { 6650.1, 46.0, 4623.5, 0, 1, "Edobashi junction" }, + { 6591.5, 46.0, 4610.5, 0, 1, "Edobashi junction" }, + { 6532.9, 46.0, 4597.4, 0, 1, "Edobashi junction" }, + { 6474.4, 46.0, 4584.2, 0, 1, "Edobashi junction" }, + { 6415.8, 46.0, 4571.1, 0, 1, "Edobashi junction" }, + { 6357.3, 46.0, 4558.0, 0, 1, "Edobashi junction" }, + { 6298.7, 46.0, 4544.9, 0, 1, "Edobashi junction" }, + { 6239.7, 46.0, 4534.2, 0, 1, "Edobashi junction" }, + { 6180.1, 46.0, 4526.8, 0, 1, "Edobashi junction" }, + { 6120.3, 46.0, 4521.7, 0, 1, "Edobashi junction" }, + { 6060.7, 46.0, 4515.5, 0, 1, "Edobashi junction" }, + { 6000.9, 46.0, 4509.9, 0, 1, "Edobashi junction" }, + { 5941.0, 46.0, 4511.3, 0, 1, "Edobashi junction" }, + { 5881.3, 46.0, 4517.4, 0, 1, "Edobashi junction" }, + { 5821.9, 46.0, 4526.1, 0, 1, "Edobashi junction" }, + { 5762.9, 46.0, 4537.0, 0, 1, "Edobashi junction" }, + { 5704.3, 46.0, 4550.0, 0, 1, "Edobashi junction" }, + { 5645.7, 46.0, 4562.9, 0, 1, "Edobashi junction" }, + { 5587.1, 46.0, 4575.8, 0, 1, "Edobashi junction" }, + { 5528.5, 46.0, 4588.7, 0, 1, "Edobashi junction" }, + { 5470.0, 46.0, 4601.8, 0, 1, "Edobashi junction" }, + { 5411.5, 46.0, 4615.3, 0, 1, "Edobashi junction" }, + { 5353.0, 46.0, 4628.7, 0, 1, "Edobashi junction" }, + { 5294.5, 46.0, 4642.2, 0, 1, "Edobashi junction" }, + { 5236.1, 46.0, 4655.7, 0, 1, "Edobashi junction" }, + { 5177.6, 46.0, 4669.2, 0, 1, "Edobashi junction" }, + { 5119.1, 46.0, 4682.7, 0, 1, "Edobashi junction" }, + { 5060.6, 46.0, 4696.2, 0, 1, "Edobashi junction" }, + { 5002.2, 46.0, 4709.7, 0, 1, "Edobashi junction" }, + { 4944.0, 46.0, 4724.3, 0, 1, "Edobashi junction" }, + { 4886.7, 46.0, 4742.1, 0, 1, "Edobashi junction" }, + { 4832.9, 46.0, 4768.5, 0, 1, "Edobashi junction" }, + { 4783.9, 46.0, 4803.1, 0, 1, "Edobashi junction" }, + { 4740.1, 46.0, 4843.9, 0, 1, "Edobashi junction" }, + { 4701.9, 46.0, 4890.1, 0, 1, "Edobashi junction" }, + { 4672.6, 46.0, 4942.4, 0, 1, "Edobashi junction" }, + { 4649.2, 46.0, 4997.7, 0, 1, "Edobashi junction" }, + { 4628.1, 46.0, 5053.8, 0, 1, "Edobashi junction" }, + { 4608.5, 46.0, 5110.5, 0, 1, "Edobashi junction" }, + { 4588.9, 46.0, 5167.3, 0, 1, "Edobashi junction" }, + { 4569.2, 46.0, 5223.9, 0, 1, "Edobashi junction" }, + { 4547.8, 46.0, 5280.0, 0, 1, "Edobashi junction" }, + { 4526.4, 46.0, 5336.1, 0, 1, "Edobashi junction" }, + { 4502.7, 46.0, 5391.2, 0, 1, "Edobashi junction" }, + { 4474.7, 46.0, 5444.3, 0, 1, "Edobashi junction" }, + { 4443.5, 46.0, 5495.5, 0, 1, "Edobashi junction" }, + { 4409.0, 46.0, 5544.6, 0, 1, "Edobashi junction" }, + { 4374.5, 46.0, 5593.7, 0, 1, "Edobashi junction" }, + { 4340.0, 46.0, 5642.8, 0, 1, "Edobashi junction" }, + { 4305.5, 46.0, 5691.8, 0, 1, "Edobashi junction" }, + { 4270.9, 46.0, 5740.9, 0, 1, "Edobashi junction" }, + { 4236.4, 46.0, 5790.0, 0, 1, "Edobashi junction" }, + { 4201.9, 46.0, 5839.1, 0, 1, "Edobashi junction" }, + { 4166.3, 46.0, 5887.4, 0, 1, "Edobashi junction" }, + { 4129.9, 46.0, 5935.1, 0, 1, "Edobashi junction" }, + { 4092.3, 46.0, 5981.9, 0, 1, "Edobashi junction" }, + { 4053.6, 46.0, 6027.8, 0, 1, "Edobashi junction" }, + { 4012.8, 46.0, 6071.7, 0, 1, "Edobashi junction" }, + { 3968.7, 46.0, 6112.4, 0, 1, "Edobashi junction" }, + { 3922.6, 46.0, 6150.8, 0, 1, "Takebashi Junction" }, + { 3875.5, 46.0, 6187.9, 0, 1, "Takebashi Junction" }, + { 3828.3, 46.0, 6225.0, 0, 1, "Takebashi Junction" }, + { 3781.1, 46.0, 6262.1, 0, 1, "Takebashi Junction" }, + { 3733.4, 46.0, 6298.4, 0, 1, "Takebashi Junction" }, + { 3683.5, 46.0, 6331.7, 0, 1, "Takebashi Junction" }, + { 3630.8, 46.0, 6360.4, 0, 1, "Takebashi Junction" }, + { 3575.2, 46.0, 6382.7, 0, 1, "Takebashi Junction" }, + { 3517.8, 46.0, 6400.0, 0, 1, "Takebashi Junction" }, + { 3459.5, 46.0, 6414.2, 0, 1, "Takebashi Junction" }, + { 3400.8, 46.0, 6426.8, 0, 1, "Takebashi Junction" }, + { 3341.4, 46.0, 6435.2, 0, 1, "Takebashi Junction" }, + { 3281.8, 46.0, 6442.3, 0, 1, "Takebashi Junction" }, + { 3222.2, 46.0, 6449.2, 0, 1, "Takebashi Junction" }, + { 3162.6, 46.0, 6456.0, 0, 1, "Takebashi Junction" }, + { 3103.1, 46.0, 6464.0, 0, 1, "Takebashi Junction" }, + { 3043.9, 46.0, 6473.6, 0, 1, "Takebashi Junction" }, + { 2985.1, 46.0, 6485.3, 0, 1, "Takebashi Junction" }, + { 2926.5, 46.0, 6498.5, 0, 1, "Takebashi Junction" }, + { 2868.0, 46.0, 6511.6, 0, 1, "Takebashi Junction" }, + { 2809.4, 46.0, 6524.8, 0, 1, "Takebashi Junction" }, + { 2750.9, 46.0, 6538.0, 0, 1, "Takebashi Junction" }, + { 2692.3, 46.0, 6551.2, 0, 1, "Takebashi Junction" }, + { 2634.2, 46.0, 6566.1, 0, 1, "Takebashi Junction" }, + { 2577.0, 46.0, 6584.1, 0, 1, "Takebashi Junction" }, + { 2521.8, 46.0, 6607.6, 0, 1, "Takebashi Junction" }, + { 2471.0, 46.0, 6639.3, 0, 1, "Takebashi Junction" }, + { 2424.5, 46.0, 6677.2, 0, 1, "Takebashi Junction" }, + { 2380.9, 46.0, 6718.4, 0, 1, "Takebashi Junction" }, + { 2338.6, 46.0, 6761.0, 0, 1, "Takebashi Junction" }, + { 2297.2, 46.0, 6804.4, 0, 1, "Takebashi Junction" }, + { 2255.1, 46.0, 6847.2, 0, 1, "Takebashi Junction" }, + { 2209.8, 46.0, 6886.6, 0, 1, "Takebashi Junction" }, + { 2161.2, 46.0, 6921.7, 0, 1, "Takebashi Junction" }, + { 2108.4, 46.0, 6950.1, 0, 1, "Takebashi Junction" }, + { 2053.7, 46.0, 6974.8, 0, 1, "Takebashi Junction" }, + { 1998.8, 46.0, 6998.9, 0, 1, "Takebashi Junction" }, + { 1943.9, 46.0, 7023.1, 0, 1, "Takebashi Junction" }, + { 1888.9, 46.0, 7047.3, 0, 1, "Takebashi Junction" }, + { 1835.1, 46.0, 7073.8, 0, 1, "Takebashi Junction" }, + { 1782.7, 46.0, 7103.0, 0, 1, "Takebashi Junction" }, + { 1731.9, 46.0, 7135.0, 0, 1, "Takebashi Junction" }, + { 1682.2, 46.0, 7168.6, 0, 1, "Takebashi Junction" }, + { 1632.8, 46.0, 7202.6, 0, 1, "Takebashi Junction" }, + { 1583.3, 46.0, 7236.6, 0, 1, "Takebashi Junction" }, + { 1533.3, 46.0, 7269.7, 0, 1, "Takebashi Junction" }, + { 1480.0, 46.0, 7297.3, 0, 1, "Takebashi Junction" }, + { 1424.8, 46.0, 7320.7, 0, 1, "Takebashi Junction" }, + { 1367.4, 46.0, 7338.2, 0, 1, "Takebashi Junction" }, + { 1308.6, 46.0, 7349.7, 0, 1, "Takebashi Junction" }, + { 1248.9, 46.0, 7355.4, 0, 1, "Takebashi Junction" }, + { 1188.9, 46.0, 7357.7, 0, 1, "Takebashi Junction" }, + { 1128.9, 46.0, 7358.2, 0, 1, "Hitotsubashi" }, + { 1068.9, 46.0, 7358.7, 0, 1, "Hitotsubashi" }, + { 1008.9, 46.0, 7359.3, 0, 1, "Hitotsubashi" }, + { 948.9, 46.0, 7359.8, 0, 1, "Hitotsubashi" }, + { 888.9, 46.0, 7360.3, 0, 1, "Hitotsubashi" }, + { 828.9, 46.0, 7360.8, 0, 1, "Hitotsubashi" }, + { 768.9, 46.0, 7361.3, 0, 1, "Hitotsubashi" }, + { 708.9, 46.0, 7361.8, 0, 1, "Hitotsubashi" }, + { 648.8, 46.0, 7362.4, 0, 1, "Hitotsubashi" }, + { 588.9, 46.0, 7364.1, 0, 1, "Hitotsubashi" }, + { 529.0, 46.0, 7367.6, 0, 1, "Hitotsubashi" }, + { 469.2, 46.0, 7373.6, 0, 1, "Hitotsubashi" }, + { 409.8, 46.0, 7381.6, 0, 1, "Hitotsubashi" }, + { 350.4, 46.0, 7390.1, 0, 1, "Hitotsubashi" }, + { 291.0, 46.0, 7398.6, 0, 1, "Hitotsubashi" }, + { 231.6, 46.0, 7407.2, 0, 1, "Hitotsubashi" }, + { 172.2, 46.0, 7415.7, 0, 1, "Hitotsubashi" }, + { 112.8, 46.0, 7424.3, 0, 1, "Kitanomaru" }, + { 53.2, 46.0, 7431.7, 0, 1, "Kitanomaru" }, + { -6.6, 46.0, 7435.5, 0, 1, "Kitanomaru" }, + { -66.6, 46.0, 7435.6, 0, 1, "Kitanomaru" }, + { -126.4, 46.0, 7431.0, 0, 1, "Kitanomaru" }, + { -185.5, 46.0, 7420.7, 0, 1, "Kitanomaru" }, + { -243.5, 46.0, 7405.3, 0, 1, "Kitanomaru" }, + { -300.0, 46.0, 7385.2, 0, 1, "Kitanomaru" }, + { -355.4, 46.0, 7362.2, 0, 1, "Kitanomaru" }, + { -409.5, 46.0, 7336.2, 0, 0, "Kitanomaru" }, + { -463.5, 45.9, 7309.9, 0, 0, "Kitanomaru" }, + { -517.4, 45.9, 7283.6, 0, 0, "Kitanomaru" }, + { -571.4, 45.8, 7257.3, 0, 0, "Kitanomaru" }, + { -625.3, 45.6, 7231.0, 0, 0, "Kitanomaru" }, + { -679.3, 45.4, 7204.7, 0, 0, "Kitanomaru" }, + { -733.2, 45.0, 7178.5, 0, 0, "Kitanomaru" }, + { -787.5, 44.5, 7152.9, 0, 0, "Kitanomaru" }, + { -842.0, 43.8, 7127.7, 0, 0, "Kitanomaru" }, + { -896.4, 42.9, 7102.5, 0, 0, "Kitanomaru" }, + { -950.7, 41.8, 7077.0, 0, 0, "Kitanomaru" }, + { -1005.0, 40.4, 7051.3, 0, 0, "Kitanomaru" }, + { -1059.1, 38.7, 7025.4, 0, 0, "Daikancho" }, + { -1112.6, 36.7, 6998.2, 0, 0, "Daikancho" }, + { -1166.1, 34.6, 6971.0, 0, 0, "Daikancho" }, + { -1219.1, 32.3, 6942.8, 0, 0, "Daikancho" }, + { -1271.9, 29.9, 6914.4, 0, 0, "Daikancho" }, + { -1324.3, 27.7, 6885.1, 1, 0, "Daikancho" }, + { -1376.7, 25.6, 6855.8, 1, 0, "Daikancho" }, + { -1428.6, 23.8, 6825.8, 1, 0, "Daikancho" }, + { -1480.5, 22.5, 6795.6, 1, 0, "Daikancho" }, + { -1532.3, 21.6, 6765.3, 1, 0, "Daikancho" }, + { -1584.2, 21.4, 6735.1, 1, 0, "Daikancho" }, + { -1636.9, 21.6, 6706.4, 1, 0, "Daikancho" }, + { -1690.2, 22.5, 6679.0, 1, 0, "Daikancho" }, + { -1743.6, 23.8, 6651.5, 1, 0, "Daikancho" }, + { -1797.4, 25.6, 6625.1, 1, 0, "Daikancho" }, + { -1853.5, 27.7, 6603.9, 1, 0, "Daikancho" }, + { -1911.3, 29.9, 6588.0, 0, 0, "Daikancho" }, + { -1970.5, 32.3, 6578.3, 0, 0, "Daikancho" }, + { -2030.4, 34.6, 6574.3, 0, 0, "Daikancho" }, + { -2090.4, 36.7, 6574.0, 0, 1, "Daikancho" }, + { -2150.2, 38.7, 6578.0, 0, 1, "Daikancho" }, + { -2209.9, 40.4, 6584.2, 0, 1, "Daikancho" }, + { -2269.5, 41.8, 6591.2, 0, 1, "Daikancho" }, + { -2329.0, 42.9, 6599.4, 0, 1, "Daikancho" }, + { -2388.4, 43.8, 6607.6, 0, 1, "Daikancho" }, + { -2447.9, 44.5, 6615.6, 0, 1, "Daikancho" }, + { -2507.4, 45.0, 6623.3, 0, 1, "Daikancho" }, + { -2567.0, 45.3, 6630.6, 0, 1, "Daikancho" }, + { -2626.6, 45.5, 6637.2, 0, 1, "Daikancho" }, + { -2686.6, 45.6, 6638.5, 0, 1, "Daikancho" }, + { -2746.2, 45.6, 6632.2, 0, 1, "Daikancho" }, + { -2805.2, 45.5, 6621.4, 0, 1, "Daikancho" }, + { -2864.0, 45.3, 6609.2, 0, 1, "Daikancho" }, + { -2922.7, 45.0, 6596.8, 0, 1, "Daikancho" }, + { -2981.4, 44.5, 6584.4, 0, 1, "Daikancho" }, + { -3040.1, 43.8, 6572.0, 0, 1, "Daikancho" }, + { -3098.8, 42.9, 6559.4, 0, 1, "Daikancho" }, + { -3157.5, 41.8, 6546.8, 0, 1, "Daikancho" }, + { -3216.1, 40.3, 6534.2, 0, 1, "Daikancho" }, + { -3274.4, 38.6, 6520.0, 0, 1, "Daikancho" }, + { -3331.8, 36.6, 6502.4, 0, 1, "Daikancho" }, + { -3386.7, 34.3, 6478.4, 0, 1, "Daikancho" }, + { -3437.9, 31.9, 6447.2, 0, 1, "Miyakezaka JCT" }, + { -3483.6, 29.3, 6408.5, 0, 1, "Miyakezaka JCT" }, + { -3523.1, 26.7, 6363.4, 1, 0, "Miyakezaka JCT" }, + { -3554.9, 24.1, 6312.7, 1, 0, "Miyakezaka JCT" }, + { -3576.7, 21.7, 6256.8, 1, 0, "Miyakezaka JCT" }, + { -3591.0, 19.4, 6198.6, 1, 0, "Miyakezaka JCT" }, + { -3602.0, 17.4, 6139.6, 1, 0, "Miyakezaka JCT" }, + { -3613.0, 15.7, 6080.6, 1, 0, "Miyakezaka JCT" }, + { -3624.0, 14.2, 6021.6, 1, 0, "Miyakezaka JCT" }, + { -3635.0, 13.1, 5962.6, 1, 0, "Miyakezaka JCT" }, + { -3646.0, 12.2, 5903.6, 1, 0, "Miyakezaka JCT" }, + { -3657.0, 11.5, 5844.6, 1, 0, "Miyakezaka JCT" }, + { -3668.0, 11.0, 5785.6, 1, 0, "Miyakezaka JCT" }, + { -3679.0, 10.6, 5726.7, 1, 0, "Miyakezaka JCT" }, + { -3690.0, 10.4, 5667.7, 1, 0, "Miyakezaka JCT" }, + { -3701.0, 10.2, 5608.7, 1, 0, "Miyakezaka JCT" }, + { -3712.0, 10.1, 5549.7, 1, 0, "Miyakezaka JCT" }, + { -3723.0, 10.1, 5490.7, 1, 0, "Miyakezaka JCT" }, + { -3734.0, 10.0, 5431.7, 1, 0, "Miyakezaka JCT" }, + { -3745.0, 10.0, 5372.7, 1, 0, "Miyakezaka JCT" }, + { -3755.9, 10.0, 5313.7, 1, 0, "Miyakezaka JCT" }, + { -3766.9, 10.0, 5254.7, 1, 0, "Miyakezaka JCT" }, + { -3777.9, 10.0, 5195.7, 1, 0, "Miyakezaka JCT" }, + { -3788.9, 10.0, 5136.7, 1, 0, "Miyakezaka JCT" }, + { -3799.8, 10.0, 5077.7, 1, 0, "Miyakezaka JCT" }, + { -3810.8, 10.0, 5018.7, 1, 0, "Miyakezaka JCT" }, + { -3821.8, 10.0, 4959.7, 1, 0, "Miyakezaka JCT" }, + { -3832.7, 10.0, 4900.7, 1, 0, "Miyakezaka JCT" }, + { -3843.7, 10.0, 4841.7, 1, 0, "Miyakezaka JCT" }, + { -3854.7, 10.0, 4782.7, 1, 0, "Miyakezaka JCT" }, + { -3865.7, 10.0, 4723.7, 1, 0, "Miyakezaka JCT" }, + { -3876.6, 10.0, 4664.7, 1, 0, "Miyakezaka JCT" }, + { -3887.6, 10.0, 4605.7, 1, 0, "Miyakezaka JCT" }, + { -3898.6, 10.0, 4546.7, 1, 0, "Miyakezaka JCT" }, + { -3909.5, 10.0, 4487.7, 1, 0, "Miyakezaka JCT" }, + { -3920.5, 10.0, 4428.7, 1, 0, "Miyakezaka JCT" }, + { -3931.1, 10.0, 4369.6, 1, 0, "Miyakezaka JCT" }, + { -3941.5, 10.0, 4310.5, 1, 0, "Miyakezaka Junction" }, + { -3952.0, 10.0, 4251.5, 1, 0, "Miyakezaka Junction" }, + { -3962.5, 10.0, 4192.4, 1, 0, "Miyakezaka Junction" }, + { -3973.0, 10.0, 4133.3, 1, 0, "Miyakezaka Junction" }, + { -3981.2, 10.0, 4073.8, 1, 0, "Miyakezaka Junction" }, + { -3988.2, 10.0, 4014.2, 1, 0, "Miyakezaka Junction" }, + { -3992.2, 10.0, 3954.4, 1, 0, "Miyakezaka Junction" }, + { -3992.5, 10.0, 3894.4, 1, 0, "Miyakezaka Junction" }, + { -3990.7, 10.0, 3834.4, 1, 0, "Miyakezaka Junction" }, + { -3986.4, 10.0, 3774.6, 1, 0, "Miyakezaka Junction" }, + { -3978.5, 10.0, 3715.1, 1, 0, "Miyakezaka Junction" }, + { -3965.9, 10.0, 3656.5, 1, 0, "Miyakezaka Junction" }, + { -3949.4, 10.0, 3598.8, 1, 0, "Miyakezaka Junction" }, + { -3929.5, 10.0, 3542.2, 1, 0, "Miyakezaka Junction" }, + { -3905.8, 10.0, 3487.2, 1, 0, "Miyakezaka Junction" }, + { -3880.1, 10.0, 3432.9, 1, 0, "Miyakezaka Junction" }, + { -3854.5, 10.0, 3378.6, 1, 0, "Miyakezaka Junction" }, + { -3828.7, 10.1, 3324.4, 1, 0, "Miyakezaka Junction" }, + { -3802.9, 10.1, 3270.3, 1, 0, "Miyakezaka Junction" }, + { -3777.1, 10.2, 3216.1, 1, 0, "Miyakezaka Junction" }, + { -3751.3, 10.4, 3161.9, 1, 0, "Miyakezaka Junction" }, + { -3725.4, 10.6, 3107.7, 1, 0, "Miyakezaka Junction" }, + { -3699.6, 11.0, 3053.6, 1, 0, "Miyakezaka Junction" }, + { -3673.7, 11.5, 2999.5, 1, 0, "Miyakezaka Junction" }, + { -3645.8, 12.2, 2946.3, 1, 0, "Miyakezaka Junction" }, + { -3615.5, 13.1, 2894.6, 1, 0, "Miyakezaka Junction" }, + { -3582.4, 14.2, 2844.5, 1, 0, "Miyakezaka Junction" }, + { -3546.4, 15.7, 2796.5, 1, 0, "Miyakezaka Junction" }, + { -3506.0, 17.4, 2752.3, 1, 0, "Miyakezaka Junction" }, + { -3461.3, 19.4, 2712.3, 1, 0, "Miyakezaka Junction" }, + { -3413.9, 21.7, 2675.5, 1, 0, "Kasumigaseki" }, + { -3362.8, 24.1, 2644.0, 1, 0, "Kasumigaseki" }, + { -3309.1, 26.6, 2617.4, 1, 0, "Kasumigaseki" }, + { -3254.0, 29.2, 2593.5, 0, 1, "Kasumigaseki" }, + { -3198.5, 31.7, 2570.7, 0, 1, "Kasumigaseki" }, + { -3142.7, 34.1, 2548.6, 0, 1, "Kasumigaseki" }, + { -3087.9, 36.2, 2524.2, 0, 1, "Kasumigaseki" }, + { -3034.2, 38.0, 2497.5, 0, 1, "Kasumigaseki" }, + { -2982.7, 39.3, 2466.7, 0, 1, "Kasumigaseki" }, + { -2934.0, 40.3, 2431.7, 0, 1, "Kasumigaseki" }, + { -2889.9, 40.8, 2391.2, 0, 1, "Kasumigaseki" }, + { -2849.3, 40.8, 2346.9, 0, 1, "Kasumigaseki" }, + { -2811.4, 40.3, 2300.4, 0, 1, "Kasumigaseki" }, + { -2773.8, 39.3, 2253.7, 0, 1, "Kasumigaseki" }, + { -2736.1, 38.0, 2207.0, 0, 1, "Kasumigaseki" }, + { -2702.2, 36.2, 2157.5, 0, 1, "Kasumigaseki" }, + { -2672.7, 34.1, 2105.3, 0, 1, "Kasumigaseki" }, + { -2645.1, 31.7, 2052.0, 0, 1, "Kasumigaseki" }, + { -2614.4, 29.2, 2000.4, 0, 1, "Kasumigaseki" }, + { -2580.4, 26.6, 1951.0, 1, 0, "Kasumigaseki" }, + { -2545.1, 24.1, 1902.5, 1, 0, "Kasumigaseki" }, + { -2509.8, 21.7, 1854.0, 1, 0, "Kasumigaseki" }, + { -2474.5, 19.4, 1805.5, 1, 0, "Kasumigaseki" }, + { -2439.2, 17.4, 1756.9, 1, 0, "Kasumigaseki" }, + { -2406.4, 15.7, 1706.7, 1, 0, "Kasumigaseki" }, + { -2381.2, 14.2, 1652.3, 1, 0, "Kasumigaseki" }, + { -2364.4, 13.1, 1594.8, 1, 0, "Kasumigaseki" }, + { -2353.9, 12.2, 1535.7, 1, 0, "Kasumigaseki" }, + { -2349.6, 11.5, 1475.9, 1, 0, "Kasumigaseki" }, + { -2353.7, 11.0, 1416.1, 1, 0, "Kasumigaseki" }, + { -2367.7, 10.6, 1357.9, 1, 0, "Kasumigaseki" }, + { -2390.5, 10.4, 1302.4, 1, 0, "Kasumigaseki" }, + { -2417.2, 10.2, 1248.7, 1, 0, "Kasumigaseki" }, + { -2444.4, 10.1, 1195.2, 1, 0, "Kasumigaseki" }, + { -2471.7, 10.1, 1141.7, 1, 0, "Kasumigaseki" }, + { -2499.2, 10.0, 1088.4, 1, 0, "Kasumigaseki" }, + { -2526.5, 10.0, 1035.0, 1, 0, "Kasumigaseki" }, + { -2553.9, 10.0, 981.6, 1, 0, "Kasumigaseki" }, + { -2581.3, 10.0, 928.2, 1, 0, "Kasumigaseki" }, + { -2608.7, 10.0, 874.8, 1, 0, "Kasumigaseki" }, + { -2636.1, 10.0, 821.4, 1, 0, "Kasumigaseki" }, + { -2663.5, 10.0, 768.0, 1, 0, "Kasumigaseki" }, + { -2690.9, 10.0, 714.6, 1, 0, "Kasumigaseki" }, + { -2718.3, 10.0, 661.2, 1, 0, "Kasumigaseki" }, + { -2745.9, 10.0, 608.0, 1, 0, "Kasumigaseki" }, + { -2773.7, 10.0, 554.7, 1, 0, "Kasumigaseki" }, + { -2801.4, 10.0, 501.5, 1, 0, "Kasumigaseki" }, + { -2830.8, 10.0, 449.2, 1, 0, "Kasumigaseki" }, + { -2863.8, 10.0, 399.2, 1, 0, "Kasumigaseki" }, + { -2899.4, 10.0, 350.8, 1, 0, "Kasumigaseki" }, + { -2936.9, 10.1, 304.0, 1, 0, "Kasumigaseki" }, + { -2975.5, 10.1, 258.1, 1, 0, "Kasumigaseki" }, + { -3014.2, 10.2, 212.2, 1, 0, "Kasumigaseki" }, + { -3052.8, 10.4, 166.3, 1, 0, "Kasumigaseki" }, + { -3091.5, 10.6, 120.4, 1, 0, "Kasumigaseki" }, + { -3130.1, 11.0, 74.5, 1, 0, "Kasumigaseki" }, + { -3168.8, 11.5, 28.6, 1, 0, "Kasumigaseki" }, + { -3208.6, 12.2, -16.3, 1, 0, "Kasumigaseki" }, + { -3249.9, 13.1, -59.8, 1, 0, "Kasumigaseki" }, + { -3295.4, 14.2, -98.8, 1, 0, "Kasumigaseki" }, + { -3344.4, 15.7, -133.5, 1, 0, "Kasumigaseki" }, + { -3396.2, 17.4, -163.8, 1, 0, "Kasumigaseki" }, + { -3449.1, 19.4, -192.1, 1, 0, "Kasumigaseki" }, + { -3503.4, 21.7, -217.5, 1, 0, "Kasumigaseki" }, + { -3559.6, 24.1, -238.7, 1, 0, "Kasumigaseki" }, + { -3616.3, 26.7, -258.2, 1, 0, "Kasumigaseki" }, + { -3673.4, 29.3, -276.7, 0, 0, "Kasumigaseki" }, + { -3731.1, 31.9, -293.1, 0, 0, "Kasumigaseki" }, + { -3788.9, 34.3, -309.4, 0, 0, "Kasumigaseki" }, + { -3846.8, 36.6, -325.1, 0, 0, "Kasumigaseki" }, + { -3904.8, 38.6, -340.6, 0, 0, "Kasumigaseki" }, + { -3962.4, 40.3, -357.3, 0, 0, "Kasumigaseki" }, + { -4019.1, 41.8, -376.9, 0, 0, "Kasumigaseki" }, + { -4073.3, 42.9, -402.6, 0, 0, "Kasumigaseki" }, + { -4125.2, 43.8, -432.7, 0, 0, "Kasumigaseki" }, + { -4174.0, 44.5, -467.5, 0, 1, "Kasumigaseki" }, + { -4218.7, 45.0, -507.5, 0, 1, "Kasumigaseki" }, + { -4260.4, 45.4, -550.5, 0, 1, "Kasumigaseki" }, + { -4300.2, 45.6, -595.5, 0, 1, "Kasumigaseki" }, + { -4338.5, 45.8, -641.6, 0, 1, "Kasumigaseki" }, + { -4376.6, 45.9, -688.0, 0, 1, "Kasumigaseki" }, + { -4415.8, 45.9, -733.5, 0, 1, "Kasumigaseki" }, + { -4454.9, 46.0, -779.0, 0, 1, "Kasumigaseki" }, + { -4494.1, 46.0, -824.4, 0, 1, "Kasumigaseki" }, + { -4533.2, 46.0, -869.9, 0, 1, "Kasumigaseki" }, + { -4572.4, 46.0, -915.4, 0, 1, "Kasumigaseki" }, + { -4611.7, 46.0, -960.7, 0, 1, "Tanimachi JCT" }, + { -4651.1, 46.0, -1006.0, 0, 1, "Tanimachi JCT" }, + { -4690.4, 46.0, -1051.3, 0, 1, "Tanimachi JCT" }, + { -4729.8, 46.0, -1096.6, 0, 1, "Tanimachi JCT" }, + { -4769.2, 46.0, -1141.9, 0, 1, "Tanimachi JCT" }, + { -4808.5, 46.0, -1187.2, 0, 1, "Tanimachi JCT" }, + { -4846.6, 46.0, -1233.6, 0, 1, "Tanimachi JCT" }, + { -4884.7, 46.0, -1279.9, 0, 1, "Tanimachi JCT" }, + { -4922.9, 46.0, -1326.3, 0, 1, "Tanimachi JCT" }, + { -4959.5, 46.0, -1373.8, 0, 1, "Tanimachi JCT" }, + { -4995.8, 46.0, -1421.6, 0, 1, "Tanimachi JCT" }, + { -5032.1, 46.0, -1469.3, 0, 1, "Tanimachi JCT" }, + { -5068.3, 46.0, -1517.2, 0, 1, "Tanimachi JCT" }, + { -5103.6, 46.0, -1565.7, 0, 1, "Tanimachi JCT" }, + { -5138.9, 46.0, -1614.2, 0, 1, "Tanimachi JCT" }, + { -5174.3, 46.0, -1662.8, 0, 1, "Tanimachi JCT" }, + { -5209.8, 46.0, -1711.1, 0, 1, "Tanimachi JCT" }, + { -5245.6, 46.0, -1759.3, 0, 1, "Tanimachi JCT" }, + { -5281.3, 46.0, -1807.5, 0, 1, "Tanimachi JCT" }, + { -5317.1, 46.0, -1855.7, 0, 1, "Tanimachi JCT" }, + { -5352.6, 46.0, -1904.0, 0, 1, "Tanimachi JCT" }, + { -5386.3, 46.0, -1953.7, 0, 1, "Tanimachi JCT" }, + { -5419.9, 46.0, -2003.4, 0, 1, "Tanimachi JCT" }, + { -5453.4, 46.0, -2053.2, 0, 1, "Tanimachi JCT" }, + { -5485.3, 46.0, -2104.0, 0, 1, "Tanimachi JCT" }, + { -5514.2, 46.0, -2156.7, 0, 1, "Tanimachi JCT" }, + { -5539.8, 46.0, -2210.9, 0, 1, "Tanimachi JCT" }, + { -5563.1, 46.0, -2266.2, 0, 1, "Tanimachi JCT" }, + { -5585.1, 45.9, -2322.0, 0, 1, "Tanimachi JCT" }, + { -5604.0, 45.9, -2378.9, 0, 1, "Tanimachi JCT" }, + { -5619.0, 45.8, -2437.0, 0, 1, "Tanimachi JCT" }, + { -5630.2, 45.6, -2495.9, 0, 1, "Tanimachi JCT" }, + { -5639.7, 45.4, -2555.2, 0, 1, "Tanimachi JCT" }, + { -5646.5, 45.0, -2614.8, 0, 1, "Tanimachi JCT" }, + { -5652.2, 44.5, -2674.5, 0, 1, "Tanimachi JCT" }, + { -5658.0, 43.8, -2734.3, 0, 1, "Tanimachi JCT" }, + { -5664.3, 42.9, -2793.9, 0, 1, "Tanimachi JCT" }, + { -5670.6, 41.8, -2853.6, 0, 1, "Tanimachi JCT" }, + { -5677.4, 40.3, -2913.2, 0, 1, "Tanimachi JCT" }, + { -5686.8, 38.6, -2972.5, 0, 1, "Tanimachi JCT" }, + { -5696.1, 36.6, -3031.8, 0, 1, "Tanimachi JCT" }, + { -5705.5, 34.3, -3091.1, 0, 1, "Tanimachi JCT" }, + { -5715.4, 31.9, -3150.2, 0, 1, "Tanimachi JCT" }, + { -5726.4, 29.3, -3209.2, 0, 1, "Tanimachi JCT" }, + { -5738.0, 26.7, -3268.1, 1, 0, "Iikura" }, + { -5750.2, 24.1, -3326.9, 1, 0, "Iikura" }, + { -5762.5, 21.7, -3385.6, 1, 0, "Iikura" }, + { -5774.7, 19.4, -3444.4, 1, 0, "Iikura" }, + { -5786.7, 17.4, -3503.2, 1, 0, "Iikura" }, + { -5798.7, 15.7, -3562.0, 1, 0, "Iikura" }, + { -5810.7, 14.2, -3620.8, 1, 0, "Iikura" }, + { -5822.6, 13.1, -3679.6, 1, 0, "Iikura" }, + { -5834.9, 12.2, -3738.3, 1, 0, "Iikura" }, + { -5847.7, 11.5, -3796.9, 1, 0, "Iikura" }, + { -5860.5, 11.0, -3855.6, 1, 0, "Iikura" }, + { -5873.3, 10.7, -3914.2, 1, 0, "Iikura" }, + { -5886.1, 10.5, -3972.8, 1, 0, "Iikura" }, + { -5898.9, 10.5, -4031.5, 1, 0, "Iikura" }, + { -5911.7, 10.5, -4090.1, 1, 0, "Iikura" }, + { -5923.1, 10.7, -4149.0, 1, 0, "Iikura" }, + { -5934.5, 11.0, -4207.9, 1, 0, "Iikura" }, + { -5946.0, 11.5, -4266.8, 1, 0, "Iikura" }, + { -5957.4, 12.2, -4325.7, 1, 0, "Iikura" }, + { -5968.8, 13.1, -4384.6, 1, 0, "Iikura" }, + { -5980.3, 14.2, -4443.6, 1, 0, "Iikura" }, + { -5992.1, 15.7, -4502.4, 1, 0, "Iikura" }, + { -6006.4, 17.4, -4560.7, 1, 0, "Iikura" }, + { -6025.1, 19.4, -4617.6, 1, 0, "Iikura" }, + { -6048.2, 21.7, -4673.0, 1, 0, "Iikura" }, + { -6073.6, 24.1, -4727.3, 1, 0, "Iikura" }, + { -6102.2, 26.7, -4780.0, 1, 0, "Iikura" }, + { -6134.6, 29.3, -4830.5, 0, 0, "Iikura" }, + { -6167.4, 31.9, -4880.8, 0, 0, "Iikura" }, + { -6200.2, 34.3, -4931.1, 0, 0, "Iikura" }, + { -6232.9, 36.6, -4981.3, 0, 0, "Iikura" }, + { -6264.5, 38.6, -5032.4, 0, 0, "Iikura" }, + { -6295.0, 40.3, -5084.1, 0, 0, "Iikura" }, + { -6322.5, 41.8, -5137.3, 0, 0, "Iikura" }, + { -6346.0, 42.9, -5192.5, 0, 0, "Iikura" }, + { -6366.3, 43.8, -5249.0, 0, 0, "Iikura" }, + { -6384.5, 44.5, -5306.1, 0, 0, "Iikura" }, + { -6399.3, 45.0, -5364.3, 0, 1, "Iikura" }, + { -6410.3, 45.4, -5423.3, 0, 1, "Iikura" }, + { -6421.3, 45.6, -5482.3, 0, 1, "Iikura" }, + { -6432.3, 45.8, -5541.3, 0, 1, "Iikura" }, + { -6442.8, 45.9, -5600.4, 0, 1, "Iikura" }, + { -6453.3, 45.9, -5659.5, 0, 1, "Iikura" }, + { -6463.0, 46.0, -5718.7, 0, 1, "Iikura" }, + { -6468.8, 46.0, -5778.4, 0, 1, "Iikura" }, + { -6468.3, 46.0, -5838.3, 0, 1, "Iikura" }, + { -6459.6, 46.0, -5897.6, 0, 1, "Iikura" }, + { -6446.5, 46.0, -5956.2, 0, 1, "Iikura" }, + { -6428.2, 46.0, -6013.3, 0, 1, "Iikura" }, + { -6405.6, 46.0, -6068.9, 0, 1, "Iikura" }, + { -6380.5, 46.0, -6123.4, 0, 1, "Iikura" }, + { -6355.4, 46.0, -6177.9, 0, 1, "Iikura" }, + { -6329.0, 46.0, -6231.8, 0, 1, "Iikura" }, + { -6302.7, 46.0, -6285.7, 0, 1, "Iikura" }, + { -6276.3, 46.0, -6339.6, 0, 1, "Iikura" }, + { -6249.9, 46.0, -6393.5, 0, 1, "Iikura" }, + { -6223.6, 46.0, -6447.4, 0, 1, "Iikura" }, + { -6195.7, 46.0, -6500.5, 0, 1, "Iikura" }, + { -6166.2, 46.0, -6552.8, 0, 1, "Iikura" }, + { -6136.3, 46.0, -6604.8, 0, 1, "Iikura" }, + { -6101.1, 46.0, -6653.4, 0, 1, "Iikura" }, + { -6060.4, 46.0, -6697.4, 0, 1, "Iikura" }, + { -6014.8, 46.0, -6736.4, 0, 1, "Iikura" }, + { -5963.6, 46.0, -6767.4, 0, 1, "Iikura" }, + { -5907.8, 46.0, -6789.5, 0, 1, "Iikura" }, + { -5850.2, 46.0, -6806.1, 0, 1, "Iikura" }, + { -5791.2, 46.0, -6817.3, 0, 1, "Iikura" }, + { -5731.9, 46.0, -6825.9, 0, 1, "Iikura" }, + { -5672.4, 46.0, -6834.3, 0, 1, "Iikura" }, + { -5613.0, 46.0, -6842.7, 0, 1, "Iikura" }, + { -5553.6, 46.0, -6851.1, 0, 1, "Iikura" }, + { -5494.2, 46.0, -6859.4, 0, 1, "Iikura" }, + { -5434.7, 46.0, -6867.8, 0, 1, "Iikura" }, + { -5375.3, 46.0, -6876.2, 0, 1, "Iikura" }, + { -5315.9, 46.0, -6884.6, 0, 1, "Iikura" }, + { -5256.6, 46.0, -6893.7, 0, 1, "Iikura" }, + { -5197.9, 46.0, -6906.2, 0, 1, "Iikura" }, + { -5140.5, 46.0, -6923.4, 0, 1, "Iikura" }, + { -5083.7, 46.0, -6943.0, 0, 1, "Iikura" }, + { -5027.5, 46.0, -6963.9, 0, 1, "Iikura" }, + { -4970.8, 46.0, -6983.6, 0, 1, "Iikura" }, + { -4914.0, 46.0, -7003.0, 0, 1, "Iikura" }, + { -4857.1, 46.0, -7021.9, 0, 1, "Iikura" }, + { -4800.1, 46.0, -7040.7, 0, 1, "Iikura" }, + { -4742.4, 46.0, -7057.4, 0, 1, "Iikura" }, + { -4684.0, 46.0, -7071.0, 0, 1, "Iikura" }, + { -4625.0, 46.0, -7081.8, 0, 1, "Iikura" }, + { -4565.6, 46.0, -7090.2, 0, 1, "Iikura" }, + { -4505.9, 46.0, -7096.6, 0, 1, "Iikura" }, + { -4446.3, 46.0, -7103.0, 0, 1, "Iikura" }, + { -4386.6, 46.0, -7109.4, 0, 1, "Iikura" }, + { -4326.9, 46.0, -7115.8, 0, 1, "Iikura" }, + { -4267.3, 46.0, -7122.3, 0, 1, "Iikura" }, + { -4207.6, 46.0, -7128.7, 0, 1, "Iikura" }, + { -4147.9, 46.0, -7135.1, 0, 1, "Iikura" }, + { -4088.3, 46.0, -7141.5, 0, 1, "Iikura" }, + { -4028.6, 46.0, -7147.9, 0, 1, "Iikura" }, + { -3968.9, 46.0, -7154.3, 0, 1, "Iikura" }, + { -3909.2, 46.0, -7160.7, 0, 1, "Iikura" }, + { -3849.6, 46.0, -7167.1, 0, 1, "Iikura" }, + { -3789.9, 46.0, -7173.5, 0, 1, "Iikura" }, + { -3730.2, 46.0, -7179.9, 0, 1, "Iikura" }, + { -3670.8, 46.0, -7188.1, 0, 1, "Iikura" }, + { -3612.0, 46.0, -7199.7, 0, 1, "Iikura" }, + { -3554.2, 46.0, -7215.6, 0, 1, "Iikura" }, + { -3499.7, 46.0, -7240.5, 0, 1, "Iikura" }, + { -3449.1, 46.0, -7272.8, 0, 1, "Iikura" }, + { -3403.7, 46.0, -7311.9, 0, 1, "Iikura" }, + { -3362.5, 46.0, -7355.5, 0, 1, "Iikura" }, + { -3325.8, 46.0, -7402.9, 0, 1, "Iikura" }, + { -3290.5, 46.0, -7451.5, 0, 1, "Iikura" }, + { -3255.2, 46.0, -7500.0, 0, 1, "Iikura" }, + { -3220.7, 46.0, -7549.1, 0, 1, "Iikura" }, + { -3183.4, 46.0, -7596.1, 0, 1, "Iikura" }, + { -3141.8, 46.0, -7639.2, 0, 1, "Iikura" }, + { -3096.3, 46.0, -7678.3, 0, 1, "Iikura" }, + { -3047.0, 46.0, -7712.4, 0, 1, "Iikura" }, + { -2993.4, 46.0, -7739.2, 0, 1, "Iikura" }, + { -2936.8, 46.0, -7759.1, 0, 1, "Iikura" }, + { -2879.1, 46.0, -7775.5, 0, 1, "Iikura" }, + { -2820.4, 46.0, -7788.0, 0, 1, "Iikura" }, + { -2761.2, 46.0, -7797.9, 0, 1, "Iikura" }, + { -2701.9, 46.0, -7806.7, 0, 1, "Iikura" }, + { -2642.5, 46.0, -7815.6, 0, 1, "Iikura" }, + { -2583.2, 46.0, -7824.4, 0, 1, "Iikura" }, + { -2523.8, 46.0, -7833.3, 0, 1, "Shiodome" }, + { -2464.5, 46.0, -7842.2, 0, 1, "Shiodome" }, + { -2404.8, 46.0, -7847.9, 0, 1, "Shiodome" }, + { -2344.8, 46.0, -7849.9, 0, 1, "Shiodome" }, + { -2285.0, 46.0, -7845.4, 0, 1, "Shiodome" }, + { -2226.2, 46.0, -7833.5, 0, 1, "Shiodome" }, + { -2169.6, 46.0, -7813.9, 0, 1, "Shiodome" }, + { -2114.1, 46.0, -7791.0, 0, 1, "Shiodome" }, + { -2061.1, 46.0, -7763.0, 0, 1, "Shiodome" }, + { -2009.0, 46.0, -7733.2, 0, 1, "Shiodome" }, + { -1956.9, 46.0, -7703.4, 0, 1, "Shiodome" }, + { -1904.2, 46.0, -7674.6, 0, 1, "Shiodome" }, + { -1848.9, 46.0, -7651.6, 0, 1, "Shiodome" }, + { -1791.0, 46.0, -7635.8, 0, 1, "Shiodome" }, + { -1731.5, 46.0, -7629.2, 0, 1, "Shiodome" }, + { -1671.5, 46.0, -7630.2, 0, 1, "Shiodome" }, + { -1612.0, 46.0, -7637.4, 0, 1, "Shiodome" }, + { -1552.9, 46.0, -7647.8, 0, 1, "Shiodome" }, + { -1494.2, 46.0, -7660.2, 0, 1, "Shiodome" }, + { -1435.4, 46.0, -7672.6, 0, 1, "Shiodome" }, + { -1376.7, 46.0, -7685.0, 0, 1, "Shiodome" }, + { -1318.0, 46.0, -7697.4, 0, 1, "Shiodome" }, + { -1259.3, 46.0, -7709.8, 0, 1, "Shiodome" }, + { -1200.6, 46.0, -7722.2, 0, 1, "Shiodome" }, + { -1141.9, 46.0, -7734.5, 0, 1, "Shiodome" }, + { -1083.1, 46.0, -7746.9, 0, 1, "Shiodome" }, + { -1024.3, 46.0, -7758.9, 0, 1, "Shiodome" }, + { -965.5, 46.0, -7770.9, 0, 1, "Shiodome" }, + { -906.7, 46.0, -7782.9, 0, 1, "Shiodome" }, + { -847.9, 46.0, -7794.9, 0, 1, "Shiodome" }, + { -788.9, 46.0, -7805.5, 0, 1, "Shiodome" }, + { -729.7, 46.0, -7815.6, 0, 1, "Shiodome" }, + { -670.3, 46.0, -7824.1, 0, 1, "Shiodome" }, + { -610.9, 46.0, -7832.4, 0, 1, "Shiodome" }, + { -551.4, 46.0, -7839.9, 0, 1, "Shiodome" }, + { -491.8, 46.0, -7847.3, 0, 1, "Shiodome" }, + { -432.2, 46.0, -7854.2, 0, 1, "Shiodome" }, + { -372.5, 46.0, -7860.8, 0, 1, "Shiodome" }, + { -312.9, 46.0, -7867.5, 0, 1, "Shiodome" }, + { -253.3, 46.0, -7874.1, 0, 1, "Shiodome" }, + { -193.6, 46.0, -7880.8, 0, 1, "Shiodome" }, + { -134.0, 46.0, -7887.4, 0, 1, "Shiodome" }, + { -74.3, 46.0, -7894.1, 0, 1, "Shiodome" }, + { -14.7, 46.0, -7900.5, 0, 1, "Shiodome" }, + { 45.0, 46.0, -7906.6, 0, 1, "Shiodome" }, + { 104.7, 46.0, -7912.8, 0, 1, "Shiodome" }, + { 164.4, 46.0, -7918.9, 0, 1, "Shiodome" }, + { 224.1, 46.0, -7925.1, 0, 1, "Shiodome" }, + { 283.8, 46.0, -7931.2, 0, 1, "Shiodome" }, + { 343.5, 46.0, -7937.2, 0, 1, "Shiodome" }, + { 403.2, 46.0, -7943.2, 0, 1, "Shiodome" }, + { 462.9, 46.0, -7949.3, 0, 1, "Shiodome" }, + { 522.6, 46.0, -7955.3, 0, 1, "Shiodome" }, + { 582.3, 46.0, -7961.3, 0, 1, "Shiodome" }, + { 642.0, 46.0, -7967.3, 0, 1, "Shiodome" }, + { 701.8, 46.0, -7972.5, 0, 1, "Shiodome" }, + { 761.7, 46.0, -7975.9, 0, 1, "Shiodome" }, + { 821.7, 46.0, -7976.3, 0, 1, "Shiodome" }, + { 881.5, 46.0, -7972.0, 0, 1, "Shiodome" }, + { 940.5, 46.0, -7961.2, 0, 1, "Shiodome" }, + { 997.2, 46.0, -7941.7, 0, 1, "Shiodome" }, + { 1050.0, 46.0, -7913.5, 0, 1, "Shiodome" }, + { 1098.2, 46.0, -7877.8, 0, 1, "Shiodome" }, + { 1138.7, 46.0, -7833.7, 0, 1, "Shiodome" }, + { 1170.2, 46.0, -7782.8, 0, 1, "Shiodome" }, + { 1192.5, 46.0, -7727.1, 0, 1, "Shiodome" }, + { 1206.5, 46.0, -7668.8, 0, 1, "Shiodome" }, + { 1213.6, 46.0, -7609.3, 0, 1, "Shiodome" }, + { 1216.7, 46.0, -7549.4, 0, 1, "Shiodome" }, + { 1218.2, 46.0, -7489.4, 0, 1, "Shiodome" }, + { 1218.7, 46.0, -7429.4, 0, 1, "Shiodome" }, + { 1219.2, 46.0, -7369.4, 0, 1, "Shiodome" }, + { 1219.7, 46.0, -7309.4, 0, 1, "Shiodome" }, + { 1220.1, 46.0, -7249.4, 0, 1, "Shiodome" }, + { 1220.6, 46.0, -7189.3, 0, 1, "Shiodome" }, + { 1221.1, 46.0, -7129.3, 0, 1, "Shiodome" }, + { 1221.6, 46.0, -7069.3, 0, 1, "Shiodome" }, + { 1221.8, 46.0, -7009.3, 0, 1, "Shiodome" }, + { 1221.8, 46.0, -6949.3, 0, 1, "Shiodome" }, + { 1221.8, 46.0, -6889.3, 0, 1, "Shiodome" }, + { 1221.9, 46.0, -6829.3, 0, 1, "Shiodome" }, + { 1221.9, 46.0, -6769.3, 0, 1, "Shiodome" }, + { 1222.0, 46.0, -6709.3, 0, 1, "Shiodome" }, + { 1222.0, 46.0, -6649.3, 0, 1, "Shiodome" }, + { 1222.0, 46.0, -6589.2, 0, 1, "Shiodome" }, + { 1221.9, 46.0, -6529.2, 0, 1, "Shiodome" }, + { 1221.9, 46.0, -6469.2, 0, 1, "Shiodome" }, + { 1221.9, 46.0, -6409.2, 0, 1, "Shiodome" }, + { 1221.8, 46.0, -6349.2, 0, 1, "Shiodome" }, + { 1221.8, 46.0, -6289.2, 0, 1, "Shiodome" }, + { 1221.8, 46.0, -6229.2, 0, 1, "Shiodome" }, + { 1221.7, 46.0, -6169.2, 0, 1, "Shiodome" }, + { 1221.7, 46.0, -6109.2, 0, 1, "Shiodome" }, + { 1220.7, 46.0, -6049.2, 0, 1, "Shiodome" }, + { 1217.1, 46.0, -5989.3, 0, 1, "Shiodome" }, + { 1207.5, 46.0, -5930.1, 0, 1, "Shiodome" }, + { 1189.6, 46.0, -5872.9, 0, 1, "Shiodome" }, + { 1165.8, 46.0, -5817.9, 0, 1, "Shiodome" }, + { 1136.9, 46.0, -5765.3, 0, 1, "Shiodome" }, + { 1107.7, 46.0, -5712.8, 0, 1, "Shiodome" }, + { 1078.5, 46.0, -5660.4, 0, 1, "Shiodome" }, + { 1050.7, 46.0, -5607.3, 0, 1, "Shiodome" }, + { 1029.4, 46.0, -5551.2, 0, 1, "Shiodome" }, + { 1017.0, 46.0, -5492.6, 0, 1, "Shiodome" }, + { 1015.5, 46.0, -5432.7, 0, 1, "Shiodome" }, + { 1023.3, 46.0, -5373.3, 0, 1, "Shiodome" }, + { 1041.4, 46.0, -5316.1, 0, 1, "Shiodome" }, + { 1067.6, 45.9, -5262.2, 0, 1, "Shiodome" }, + { 1098.8, 45.9, -5210.9, 0, 1, "Shiodome" }, + { 1131.7, 45.8, -5160.8, 0, 1, "Shiodome" }, + { 1164.7, 45.6, -5110.6, 0, 1, "Shiodome" }, + { 1197.6, 45.4, -5060.5, 0, 1, "Shiodome" }, + { 1230.1, 45.0, -5010.0, 0, 1, "Shiodome" }, + { 1262.0, 44.5, -4959.2, 0, 1, "Shiodome" }, + { 1294.0, 43.8, -4908.4, 0, 1, "Shiodome" }, + { 1326.0, 42.9, -4857.6, 0, 1, "Shiodome" }, + { 1358.0, 41.8, -4806.9, 0, 1, "Shiodome" }, + { 1389.9, 40.3, -4756.1, 0, 1, "Shiodome" }, + { 1422.0, 38.6, -4705.4, 0, 0, "Shiodome" }, + { 1454.6, 36.6, -4655.0, 0, 0, "Shiodome" }, + { 1487.2, 34.3, -4604.6, 0, 0, "Shiodome" }, + { 1519.8, 31.9, -4554.2, 0, 0, "Shiodome" }, + { 1552.3, 29.3, -4503.8, 0, 0, "Shiodome" }, + { 1585.0, 26.7, -4453.5, 1, 0, "Shiodome" }, + { 1617.9, 24.1, -4403.2, 1, 0, "Shiodome" }, + { 1650.7, 21.7, -4353.0, 1, 0, "Shiodome" }, + { 1683.5, 19.4, -4302.8, 1, 0, "Shiodome" }, + { 1716.4, 17.4, -4252.5, 1, 0, "Shiodome" }, + { 1749.2, 15.7, -4202.3, 1, 0, "Shiodome" }, + { 1782.0, 14.2, -4152.1, 1, 0, "Shiodome" }, + { 1814.9, 13.1, -4101.9, 1, 0, "Shiodome" }, + { 1847.7, 12.2, -4051.6, 1, 0, "Shiodome" }, + { 1880.5, 11.6, -4001.4, 1, 0, "Shiodome" }, + { 1913.4, 11.1, -3951.2, 1, 0, "Ginza" }, + { 1945.9, 10.9, -3900.7, 1, 0, "Ginza" }, + { 1979.4, 10.8, -3850.9, 1, 0, "Ginza" }, + { 2015.7, 10.9, -3803.2, 1, 0, "Ginza" }, + { 2056.7, 11.1, -3759.4, 1, 0, "Ginza" }, + { 2102.1, 11.6, -3720.3, 1, 0, "Ginza" }, + { 2151.7, 12.2, -3686.6, 1, 0, "Ginza" }, + { 2204.9, 13.1, -3658.8, 1, 0, "Ginza" }, + { 2260.8, 14.2, -3637.0, 1, 0, "Ginza" }, + { 2317.4, 15.7, -3617.4, 1, 0, "Ginza" }, + { 2374.7, 17.4, -3599.5, 1, 0, "Ginza" }, + { 2431.7, 19.4, -3580.8, 1, 0, "Ginza" }, + { 2487.1, 21.7, -3557.8, 1, 0, "Ginza" }, + { 2540.9, 24.1, -3531.2, 1, 0, "Ginza" }, + { 2592.0, 26.7, -3499.9, 1, 0, "Ginza" }, + { 2640.4, 29.3, -3464.4, 0, 0, "Ginza" }, + { 2686.1, 31.9, -3425.6, 0, 0, "Ginza" }, + { 2727.9, 34.3, -3382.6, 0, 0, "Ginza" }, + { 2766.5, 36.6, -3336.6, 0, 0, "Ginza" }, + { 2803.3, 38.6, -3289.3, 0, 0, "Ginza" }, + { 2839.2, 40.3, -3241.2, 0, 0, "Ginza" }, + { 2874.9, 41.8, -3192.9, 0, 0, "Ginza" }, + { 2908.6, 42.9, -3143.3, 0, 0, "Ginza" }, + { 2942.4, 43.8, -3093.6, 0, 0, "Ginza" }, + { 2976.1, 44.5, -3044.0, 0, 0, "Ginza" }, + { 3011.2, 45.0, -2995.3, 0, 0, "Ginza" }, + { 3049.4, 45.4, -2949.1, 0, 0, "Ginza" }, + { 3092.3, 45.6, -2907.1, 0, 0, "Ginza" }, + { 3135.2, 45.7, -2865.2, 0, 0, "Ginza" }, + { 3178.1, 45.8, -2823.2, 0, 0, "Ginza" }, + { 3221.1, 45.9, -2781.4, 0, 0, "Ginza" }, + { 3264.6, 45.9, -2740.0, 0, 0, "Ginza" }, + { 3308.0, 45.9, -2698.6, 0, 0, "Ginza" }, + { 3351.5, 45.8, -2657.2, 0, 0, "Ginza" }, + { 3394.9, 45.7, -2615.8, 0, 0, "Ginza" }, + { 3438.4, 45.5, -2574.4, 0, 0, "Ginza" }, + { 3481.8, 45.2, -2533.0, 0, 0, "Ginza" }, + { 3523.2, 44.9, -2489.6, 0, 0, "Ginza" }, + { 3560.3, 44.4, -2442.5, 0, 0, "Ginza" }, + { 3588.4, 43.9, -2389.6, 0, 0, "Ginza" }, + { 3604.8, 43.2, -2332.0, 0, 0, "Ginza" }, + { 3615.1, 42.4, -2272.9, 0, 0, "Ginza" }, + { 3623.5, 41.4, -2213.5, 0, 0, "Ginza" }, + { 3630.7, 40.5, -2153.9, 0, 0, "Ginza" }, + { 3643.4, 39.4, -2095.3, 0, 0, "Ginza" }, + { 3666.8, 38.4, -2040.1, 0, 0, "Ginza" }, + { 3699.0, 37.5, -1989.6, 0, 0, "Ginza" }, + { 3737.1, 36.5, -1943.3, 1, 0, "Ginza" }, + { 3777.4, 35.7, -1898.8, 1, 0, "Ginza" }, + { 3817.6, 35.0, -1854.3, 1, 0, "Ginza" }, + { 3859.4, 34.3, -1811.2, 0, 0, "Ginza" }, + { 3901.6, 33.7, -1768.5, 0, 0, "Ginza" }, + { 3943.7, 33.1, -1725.8, 0, 0, "Ginza" }, + { 3985.9, 32.5, -1683.1, 0, 0, "Shintomicho" }, + { 4028.0, 31.9, -1640.4, 0, 0, "Shintomicho" }, + { 4070.2, 31.3, -1597.7, 0, 0, "Shintomicho" }, + { 4112.5, 30.7, -1555.2, 1, 0, "Shintomicho" }, + { 4155.0, 30.1, -1512.7, 1, 0, "Shintomicho" }, + { 4197.4, 29.6, -1470.3, 1, 0, "Shintomicho" }, + { 4239.9, 29.1, -1427.9, 1, 0, "Shintomicho" }, + { 4282.3, 28.7, -1385.4, 0, 0, "Shintomicho" }, + { 4324.5, 28.5, -1342.7, 0, 0, "Shintomicho" }, + { 4366.6, 28.4, -1300.0, 0, 0, "Shintomicho" }, + { 4408.8, 28.4, -1257.3, 0, 0, "Shintomicho" }, + { 4450.8, 28.7, -1214.5, 1, 0, "Shintomicho" }, + { 4492.9, 29.3, -1171.7, 1, 0, "Shintomicho" }, + { 4534.9, 30.1, -1128.9, 1, 0, "Shintomicho" }, + { 4576.9, 31.1, -1086.0, 1, 0, "Shintomicho" }, + { 4619.0, 32.3, -1043.2, 1, 0, "Shintomicho" }, + { 4660.3, 33.7, -999.7, 0, 0, "Shintomicho" }, + { 4701.1, 35.3, -955.7, 0, 0, "Shintomicho" }, + { 4741.9, 36.8, -911.7, 0, 0, "Shintomicho" }, + { 4782.7, 38.4, -867.7, 0, 0, "Shintomicho" }, + { 4823.4, 39.9, -823.6, 0, 0, "Shintomicho" }, + { 4863.0, 41.2, -778.6, 0, 0, "Shintomicho" }, + { 4897.5, 42.3, -729.5, 0, 0, "Shintomicho" }, + { 4929.1, 43.3, -678.5, 0, 0, "Shintomicho" }, + { 4960.3, 44.1, -627.2, 0, 0, "Shintomicho" }, + { 4988.4, 44.6, -574.2, 0, 0, "Shintomicho" }, + { 5014.6, 45.1, -520.2, 0, 0, "Shintomicho" }, + { 5039.6, 45.4, -465.7, 0, 0, "Shintomicho" }, + { 5063.3, 45.6, -410.6, 0, 0, "Shintomicho" }, + { 5084.6, 45.8, -354.5, 0, 0, "Shintomicho" }, + { 5103.2, 45.9, -297.5, 0, 0, "Shintomicho" }, + { 5119.0, 45.9, -239.6, 0, 0, "Shintomicho" }, + { 5134.6, 45.9, -181.6, 0, 0, "Shintomicho" }, + { 5149.7, 45.9, -123.6, 0, 0, "Shintomicho" }, + { 5164.9, 45.9, -65.5, 0, 0, "Shintomicho" }, + { 5180.0, 45.8, -7.4, 0, 0, "Shintomicho" }, + { 5195.1, 45.8, 50.7, 0, 0, "Shintomicho" }, + { 5209.1, 45.6, 109.0, 0, 0, "Shintomicho" }, + { 5222.7, 45.5, 167.5, 0, 0, "Shintomicho" }, + { 5234.8, 45.3, 226.2, 0, 0, "Shintomicho" }, + { 5242.4, 45.0, 285.7, 0, 0, "Shintomicho" }, + { 5246.6, 44.7, 345.6, 0, 0, "Shintomicho" }, + { 5249.8, 44.3, 405.5, 0, 0, "Shintomicho" }, + { 5252.1, 43.9, 465.5, 0, 0, "Shintomicho" }, + { 5254.5, 43.4, 525.4, 0, 0, "Shintomicho" }, + { 5256.8, 42.8, 585.4, 0, 0, "Shintomicho" }, + { 5264.2, 42.1, 644.9, 0, 0, "Shintomicho" }, + { 5281.8, 41.4, 702.1, 0, 0, "Shintomicho" }, + { 5307.3, 40.5, 756.4, 1, 0, "Shintomicho" }, + { 5335.2, 39.6, 809.5, 0, 0, "Kyobashi" }, + { 5363.0, 38.5, 862.7, 0, 0, "Kyobashi" }, + { 5390.8, 37.3, 915.9, 0, 0, "Kyobashi" }, + { 5419.3, 36.0, 968.7, 0, 0, "Kyobashi" }, + { 5448.2, 34.6, 1021.3, 0, 0, "Kyobashi" }, + { 5477.0, 33.3, 1073.9, 0, 0, "Kyobashi" }, + { 5505.9, 32.1, 1126.5, 0, 0, "Kyobashi" }, + { 5534.6, 31.0, 1179.2, 1, 0, "Kyobashi" }, + { 5562.5, 30.1, 1232.4, 1, 0, "Kyobashi" }, + { 5590.3, 29.5, 1285.5, 1, 0, "Kyobashi" }, + { 5618.2, 29.2, 1338.7, 1, 0, "Kyobashi" }, + { 5646.0, 29.2, 1391.8, 1, 0, "Kyobashi" }, + { 5673.9, 29.5, 1445.0, 1, 0, "Kyobashi" }, + { 5701.0, 29.9, 1498.5, 0, 0, "Kyobashi" }, + { 5727.9, 30.6, 1552.2, 0, 0, "Kyobashi" }, + { 5753.6, 31.3, 1606.4, 0, 0, "Kyobashi" }, + { 5779.3, 32.1, 1660.6, 0, 0, "Kyobashi" }, + { 5805.0, 32.9, 1714.8, 0, 0, "Kyobashi" }, + { 5830.7, 33.7, 1769.1, 0, 0, "Kyobashi" }, +} -local function genSamples(): { Sample } - local samples: { Sample } = {} - local sx = HX - CORNER_R - local sz = HZ - CORNER_R - - local function push(pos: Vector3, piece: string) - table.insert(samples, { pos = pos, piece = piece }) - end - local function arc(cx: number, cz: number, a0: number, piece: string) - for i = 1, ARC_STEPS do - local a = a0 + (i / ARC_STEPS) * (math.pi / 2) - push(Vector3.new(cx + CORNER_R * math.sin(a), 0, cz + CORNER_R * math.cos(a)), piece) - end - end - - -- South straight (Daikoku side), heading +X - local x = -sx - while x <= sx do - push(Vector3.new(x, 0, HZ), "south") - x += STEP - end - arc(sx, sz, 0, "corner_se") - -- East straight (Rainbow Bridge), heading -Z - local z = sz - STEP - while z >= -sz do - push(Vector3.new(HX, 0, z), "east") - z -= STEP - end - arc(sx, -sz, math.pi / 2, "corner_ne") - -- North straight (Yamate tunnel), heading -X - x = sx - STEP - while x >= -sx do - push(Vector3.new(x, 0, -HZ), "north") - x -= STEP - end - arc(-sx, -sz, math.pi, "corner_nw") - -- West straight (Tokyo Tower run), heading +Z - z = -sz + STEP - while z <= sz do - push(Vector3.new(-HX, 0, z), "west") - z += STEP - end - arc(-sx, sz, 3 * math.pi / 2, "corner_sw") - - return samples +local samples: { Sample } = {} +for _, r in ipairs(RAW) do + table.insert(samples, { + pos = Vector3.new(r[1] :: number, r[2] :: number, r[3] :: number), + piece = r[6] :: string, + tunnel = (r[4] :: number) == 1, + bridge = (r[5] :: number) == 1, + }) end - -Circuit.samples = genSamples() +Circuit.samples = samples -- Cumulative arc length at each sample, and total lap length ------------------ local cumulative: { number } = {} do local total = 0 - for i, s in ipairs(Circuit.samples) do + for i, s in ipairs(samples) do if i == 1 then cumulative[i] = 0 else - total += (s.pos - Circuit.samples[i - 1].pos).Magnitude + total += (s.pos - samples[i - 1].pos).Magnitude cumulative[i] = total end end - -- close the loop back to sample 1 - local last = Circuit.samples[#Circuit.samples] - total += (Circuit.samples[1].pos - last.pos).Magnitude + local last = samples[#samples] + total += (samples[1].pos - last.pos).Magnitude Circuit.LAP_LENGTH = total end Circuit.cumulative = cumulative -Circuit.SAMPLE_COUNT = #Circuit.samples +Circuit.SAMPLE_COUNT = #samples + +-- Mean spacing drives the local-search window in nearestIndex. +local STEP = Circuit.LAP_LENGTH / Circuit.SAMPLE_COUNT -- Nearest sample index. `hint` = last known index; we search locally around it -- first (cars move a little per tick) and only fall back to a full scan when @@ -110,10 +904,12 @@ Circuit.SAMPLE_COUNT = #Circuit.samples function Circuit.nearestIndex(pos: Vector3, hint: number?): number local n = Circuit.SAMPLE_COUNT local best, bestDist = 1, math.huge + local flat = Vector3.new(pos.X, 0, pos.Z) local function consider(i: number) local idx = ((i - 1) % n) + 1 - local d = (Circuit.samples[idx].pos - Vector3.new(pos.X, 0, pos.Z)).Magnitude + local s = samples[idx].pos + local d = (Vector3.new(s.X, 0, s.Z) - flat).Magnitude if d < bestDist then best, bestDist = idx, d end @@ -123,7 +919,6 @@ function Circuit.nearestIndex(pos: Vector3, hint: number?): number for i = hint - 6, hint + 6 do consider(i) end - -- A straight sample is STEP apart; anything under 2 steps is a solid lock. if bestDist < STEP * 2 then return best end @@ -152,16 +947,18 @@ function Circuit.gap(progressA: number, progressB: number): number return d end --- Is this world position inside the Daikoku PA (off the racing surface)? +-- Is this world position inside the PA (off the racing surface)? function Circuit.inPA(pos: Vector3): boolean - return pos.Z > Circuit.PA_ZONE_Z and math.abs(pos.X) < Circuit.PA_ZONE_X + local c = Circuit.PA_CENTER + local dx, dz = pos.X - c.X, pos.Z - c.Z + return (dx * dx + dz * dz) <= (Circuit.PA_RADIUS * Circuit.PA_RADIUS) end -- Forward direction (unit) of the racing line at a sample index. function Circuit.forwardAt(idx: number): Vector3 local n = Circuit.SAMPLE_COUNT local nextIdx = (idx % n) + 1 - local d = Circuit.samples[nextIdx].pos - Circuit.samples[idx].pos + local d = samples[nextIdx].pos - samples[idx].pos if d.Magnitude < 0.01 then return Vector3.xAxis end diff --git a/src/shared/GameConfig.luau b/src/shared/GameConfig.luau index c213bcc..b3a847c 100644 --- a/src/shared/GameConfig.luau +++ b/src/shared/GameConfig.luau @@ -51,4 +51,16 @@ GameConfig.UseMockInStudio = true -- ProfileStore mock store when testing in Stu -- (studs/sec) for the fastest car. Sustained above this => snap back. GameConfig.MaxPlausibleSpeed = 400 +-- Development lighting ------------------------------------------------------- +-- The shipping look is deep night (neon Wangan). While building geometry that +-- is nearly unusable — you cannot see the road surface, kerbs, gap placement or +-- the PA layout under a 22:00 sky. +-- +-- Flip this to true for a flat, bright midday pass: full sun, no fog, no bloom, +-- no clock drift. It changes ONLY lighting/post — no gameplay, geometry or +-- physics differs, so anything verified in daylight holds at night. +-- +-- Set back to false to restore the night look ("turn the lights off"). +GameConfig.DevDaylight = true + return GameConfig