# -*- coding: utf-8 -*-
import pygame
import math
import random
import sys

# ============================================================
#  配置
# ============================================================
WIDTH, HEIGHT = 1000, 640
FPS = 60

SEG_LEN   = 8.0
ROAD_W    = 14.0
HALF_W    = ROAD_W * 0.5
SHOULDER  = 1.0
N_SEG     = 1200
VIEW_SEGS = 110

FOV      = math.radians(76)
FOCAL    = (WIDTH * 0.5) / math.tan(FOV * 0.5)
NEAR     = 0.9
CAM_H    = 1.15
CAM_PITCH = 0.05

MAX_SPEED = 62.0
ACCEL     = 10.5
BRAKE     = 26.0
WHEELBASE = 2.6
MAX_STEER = 0.55
MAX_LAT   = 11.0

SKY_TOP  = (52, 104, 186)
SKY_BOT  = (172, 206, 236)
FOG_COL  = (172, 206, 236)
GRASS_A  = (36, 100, 48)

GEAR_TOPS_KMH  = [45, 80, 120, 160, 200, 230]
GEAR_ACCEL_MUL = [2.0, 1.6, 1.3, 1.05, 0.85, 0.7]
GEAR_MIN_KMH   = [0,   20,  50,  85, 120, 155]
GEAR_MAX_KMH   = [60,  95, 140, 185, 220, 260]

CLUTCH_ENGAGE_RATE = 6.0
CLUTCH_PRESS_RATE  = 8.0
SHIFT_LOCK_TIME    = 0.35
SHIFT_DAMAGE       = 3.0

COUNTDOWN_TOTAL = 3.0
COUNTDOWN_GO_TIME = 0.8

STEER_MIN_SPEED    = 1.5
CAM_H_SMOOTH_RATE  = 6.0
LAUNCH_PROTECT_TIME = 0.6

WALL_MARGIN = 0.4
WALL_BOUNCE = 0.5
WALL_IMPACT_DAMAGE = 2.0

AUTO_LAUNCH_TIME = 2.0
AUTO_LAUNCH_ACCEL_MUL = 2.2

# AI 车速参数（已整体降低）
AI_BASE_SPEED   = 22.0    # 第一辆 AI 基础速度 m/s
AI_BASE_STEP    = 2.0     # 每辆 AI 基础速度递增
AI_FOLLOW_RATIO = 10.0    # 玩家速度占比对 AI 目标速度的贡献
AI_ACCEL_LOW    = 0.35    # AI 低速加速平滑率
AI_ACCEL_HIGH   = 0.5     # AI 高速加速平滑率
AI_MAX_SPEED    = 30.0    # AI 硬上限 m/s（≈108 km/h）


# ============================================================
#  中文字体
# ============================================================
def get_font(size, bold=False):
    candidates = [
        "microsoftyahei", "msyh", "simhei",
        "pingfangsc", "pingfang", "hiraginosansgb",
        "notosanscjk", "notosanscjksc",
        "wenquanyimicrohei", "wenquanyizenhei",
        "arialunicode",
    ]
    for name in candidates:
        path = pygame.font.match_font(name, bold=bold)
        if path:
            return pygame.font.Font(path, size)
    return pygame.font.SysFont(None, size, bold=bold)


# ============================================================
#  工具
# ============================================================
def blend(c1, c2, t):
    if t <= 0.0: return c1
    if t >= 1.0: return c2
    return (int(c1[0] + (c2[0] - c1[0]) * t),
            int(c1[1] + (c2[1] - c1[1]) * t),
            int(c1[2] + (c2[2] - c1[2]) * t))


def clip_near(pts, near=NEAR):
    out = []
    n = len(pts)
    for i in range(n):
        a = pts[i]; b = pts[(i + 1) % n]
        ina = a[2] >= near; inb = b[2] >= near
        if ina: out.append(a)
        if ina != inb:
            t = (near - a[2]) / (b[2] - a[2])
            out.append((a[0] + (b[0] - a[0]) * t,
                        a[1] + (b[1] - a[1]) * t, near))
    return out


def ring_poly(cx, cy, r_out, r_in, a0, a1, steps=40):
    pts = []
    for k in range(steps + 1):
        a = a0 + (a1 - a0) * k / steps
        pts.append((cx + math.cos(a) * r_out, cy + math.sin(a) * r_out))
    for k in range(steps, -1, -1):
        a = a0 + (a1 - a0) * k / steps
        pts.append((cx + math.cos(a) * r_in, cy + math.sin(a) * r_in))
    return pts


def ang_lerp(a, b, t):
    d = (b - a + math.pi) % (2 * math.pi) - math.pi
    return a + d * t


# ============================================================
#  赛道
# ============================================================
def build_track():
    segs = []
    x = z = h = 0.0
    for i in range(N_SEG):
        curv = (0.0040 * math.sin(i * 0.0165)
              + 0.0030 * math.sin(i * 0.0059 + 1.9)
              + 0.0015 * math.sin(i * 0.0290 + 4.2))
        segs.append((x, z, h, curv))
        h += curv * SEG_LEN
        x += math.sin(h) * SEG_LEN
        z += math.cos(h) * SEG_LEN
    return segs


def find_segment(segs, x, z, hint):
    n = len(segs)
    best = hint; bestd = 1e18
    for k in range(-8, 50):
        i = (hint + k) % n
        sx, sz, _, _ = segs[i]
        d = (sx - x) ** 2 + (sz - z) ** 2
        if d < bestd:
            bestd = d; best = i
    return best


def seg_coord(segs, idx):
    sx, sz, sh, _ = segs[idx % N_SEG]
    return sx, sz, sh, math.cos(sh), -math.sin(sh)


# ============================================================
#  天空
# ============================================================
def draw_sky(surf, pitch):
    hy = int(HEIGHT * 0.5 - math.tan(pitch) * FOCAL)
    hy = max(0, min(HEIGHT, hy))
    if hy > 0:
        band = 6
        for y in range(0, hy, band):
            t = y / max(1, hy)
            pygame.draw.rect(surf, blend(SKY_TOP, SKY_BOT, t), (0, y, WIDTH, band))
    if hy < HEIGHT:
        pygame.draw.rect(surf, GRASS_A, (0, hy, WIDTH, HEIGHT - hy))
    return hy


# ============================================================
#  投影
# ============================================================
car_seg_ref = [0]


def make_projector(cam_x, cam_z, cam_h, cam_pitch):
    cosh = math.cos(cam_h); sinh = math.sin(cam_h)
    cosp = math.cos(cam_pitch); sinp = math.sin(cam_pitch)
    hw = WIDTH * 0.5; hh = HEIGHT * 0.5

    def w2c(wx, wz, wy=0.0):
        dx = wx - cam_x; dz = wz - cam_z
        X = dx * cosh - dz * sinh
        Z = dx * sinh + dz * cosh
        Y = wy - CAM_H
        Yc = Y * cosp + Z * sinp
        Zc = -Y * sinp + Z * cosp
        return (X, Yc, Zc)

    def proj(p):
        inv = FOCAL / p[2]
        return (hw + p[0] * inv, hh - p[1] * inv)

    return w2c, proj


def draw_road(surf, segs, cam_x, cam_z, cam_h, cam_pitch, w2c, proj):
    draw_sky(surf, cam_pitch)
    start = (car_seg_ref[0] - 2) % N_SEG

    for k in range(VIEW_SEGS, -1, -1):
        i = (start + k) % N_SEG
        j = (i + 1) % N_SEG
        sx, sz, sh, _ = segs[i]
        nx, nz, _, _ = segs[j]
        rx = math.cos(sh); rz = -math.sin(sh)

        dist = math.hypot(sx - cam_x, sz - cam_z)
        fog = max(0.0, min(0.88, (dist - 70.0) / 460.0))

        w = HALF_W + SHOULDER
        q = [w2c(sx - rx * w, sz - rz * w),
             w2c(sx + rx * w, sz + rz * w),
             w2c(nx + rx * w, nz + rz * w),
             w2c(nx - rx * w, nz - rz * w)]
        if not all(p[2] < NEAR for p in q):
            qq = clip_near(q) if min(p[2] for p in q) < NEAR else q
            if len(qq) >= 3:
                col = blend((210, 210, 210) if (i & 1) else (190, 190, 190), FOG_COL, fog)
                pygame.draw.polygon(surf, col, [proj(p) for p in qq])

        q = [w2c(sx - rx * HALF_W, sz - rz * HALF_W),
             w2c(sx + rx * HALF_W, sz + rz * HALF_W),
             w2c(nx + rx * HALF_W, nz + rz * HALF_W),
             w2c(nx - rx * HALF_W, nz - rz * HALF_W)]
        if not all(p[2] < NEAR for p in q):
            qq = clip_near(q) if min(p[2] for p in q) < NEAR else q
            if len(qq) >= 3:
                base = (58, 58, 63) if (i & 1) else (51, 51, 56)
                pygame.draw.polygon(surf, blend(base, FOG_COL, fog), [proj(p) for p in qq])


# ============================================================
#  AI 车渲染
# ============================================================
def draw_ai_car(surf, ai, segs, cam_x, cam_z, w2c, proj, font_xs):
    idx = ai['seg']
    sx, sz, sh, rx, rz = seg_coord(segs, idx)
    cx = sx + rx * ai['lat']
    cz = sz + rz * ai['lat']

    dist = math.hypot(cx - cam_x, cz - cam_z)
    if dist < 2.0 or dist > 500:
        return
    fog = max(0.0, min(0.80, (dist - 60.0) / 400.0))

    L = 3.6; Wd = 1.4; Hh = 1.4
    fx = math.sin(sh); fz = math.cos(sh)
    lx = -rx; lz = -rz

    def corner(u, v, w):
        px = cx + fx * (u - 0.5) * L + lx * v * (Wd * 0.5)
        pz = cz + fz * (u - 0.5) * L + lz * v * (Wd * 0.5)
        return w2c(px, pz, w * Hh)

    base_pts = [corner(0, -1, 0), corner(1, -1, 0),
                corner(1, 1, 0), corner(0, 1, 0)]
    top_pts  = [corner(0, -1, 1), corner(1, -1, 1),
                corner(1, 1, 1), corner(0, 1, 1)]

    body_col = ai['color']
    bright = blend(body_col, (255, 255, 255), 0.3)

    side_defs = [(0, 1, 0.90), (1, 2, 0.75), (2, 3, 0.90), (3, 0, 0.75)]
    for a, b, shade in side_defs:
        quad = [base_pts[a], base_pts[b], top_pts[b], top_pts[a]]
        if all(p[2] < NEAR for p in quad):
            continue
        qq = clip_near(quad) if min(p[2] for p in quad) < NEAR else quad
        if len(qq) >= 3:
            c = blend(bright, (20, 20, 24), 1.0 - shade)
            c = blend(c, FOG_COL, fog)
            pygame.draw.polygon(surf, c, [proj(p) for p in qq])
            if dist < 120:
                pygame.draw.polygon(surf, blend((240, 240, 250), FOG_COL, fog),
                                    [proj(p) for p in qq], 1)

    quad = top_pts
    if not all(p[2] < NEAR for p in quad):
        qq = clip_near(quad) if min(p[2] for p in quad) < NEAR else quad
        if len(qq) >= 3:
            pygame.draw.polygon(surf, blend(blend(body_col, (255, 255, 255), 0.5), FOG_COL, fog),
                                [proj(p) for p in qq])
            if dist < 120:
                pygame.draw.polygon(surf, blend((240, 240, 250), FOG_COL, fog),
                                    [proj(p) for p in qq], 1)

    tail_c = w2c(cx - fx * L * 0.5, cz - fz * L * 0.5, Hh * 0.5)
    if tail_c[2] > NEAR:
        tp = proj(tail_c)
        vx = cam_x - cx; vz = cam_z - cz
        facing = fx * vx + fz * vz
        if facing > 0:
            pygame.draw.circle(surf, blend((255, 70, 70), FOG_COL, fog),
                               (int(tp[0]), int(tp[1])), max(2, int(6 - dist * 0.01)))

    if dist < 90:
        top_c = w2c(cx, cz, Hh * 2.0)
        if top_c[2] > NEAR:
            sp = proj(top_c)
            label = font_xs.render(ai['name'], True, blend((255, 255, 255), FOG_COL, fog))
            surf.blit(label, (sp[0] - label.get_width() // 2, sp[1] - 18))


# ============================================================
#  倒计时 / 自动加速提示
# ============================================================
def draw_countdown(surf, fonts, countdown_timer, go_timer):
    font_huge = fonts[4]
    W, H = WIDTH, HEIGHT

    if countdown_timer > 0 or go_timer > 0:
        overlay = pygame.Surface((W, H), pygame.SRCALPHA)
        a = 90 if countdown_timer > 0 else int(90 * (go_timer / COUNTDOWN_GO_TIME))
        overlay.fill((0, 0, 0, a))
        surf.blit(overlay, (0, 0))

    if countdown_timer > 0:
        num = int(math.ceil(countdown_timer))
        frac = countdown_timer - (num - 1)
        scale = 1.0 + 0.4 * (1.0 - frac)
        colors = {3: (230, 70, 70), 2: (240, 160, 60), 1: (240, 220, 70)}
        col = colors.get(num, (240, 240, 240))
        txt = str(num)
        base = font_huge.render(txt, True, col)
        w = int(base.get_width() * scale)
        h = int(base.get_height() * scale)
        scaled = pygame.transform.smoothscale(base, (max(1, w), max(1, h)))
        surf.blit(scaled, (W // 2 - w // 2, H // 2 - h // 2 - 40))
        r = int(140 * scale)
        pygame.draw.circle(surf, col, (W // 2, H // 2 - 40 + h // 2), r, 4)

    elif go_timer > 0:
        t = go_timer / COUNTDOWN_GO_TIME
        alpha_scale = t
        txt = "GO!"
        base = font_huge.render(txt, True, (90, 230, 120))
        scale = 1.0 + 0.6 * (1.0 - t)
        w = int(base.get_width() * scale)
        h = int(base.get_height() * scale)
        scaled = pygame.transform.smoothscale(base, (max(1, w), max(1, h)))
        scaled.set_alpha(int(255 * alpha_scale))
        surf.blit(scaled, (W // 2 - w // 2, H // 2 - h // 2 - 40))


def draw_auto_launch_hint(surf, fonts, launch_remaining, launch_total):
    font_m = fonts[1]
    font_xs = fonts[3]
    W, H = WIDTH, HEIGHT
    t = max(0.0, min(1.0, launch_remaining / max(0.001, launch_total)))

    txt = "自动加速中..."
    label = font_m.render(txt, True, (255, 220, 90))
    surf.blit(label, (W // 2 - label.get_width() // 2, 78))

    bw, bh = 260, 8
    bx = W // 2 - bw // 2
    by = 108
    pygame.draw.rect(surf, (18, 18, 24), (bx - 2, by - 2, bw + 4, bh + 4), border_radius=5)
    pygame.draw.rect(surf, (255, 200, 80), (bx, by, int(bw * t), bh), border_radius=3)

    hint = font_xs.render("加速结束后恢复操控", True, (180, 180, 190))
    surf.blit(hint, (W // 2 - hint.get_width() // 2, by + 14))


# ============================================================
#  HUD
# ============================================================
def draw_cockpit(surf, steer, speed_kmh, rpm_ratio, gear, fonts,
                 dmg, rank_txt,
                 manual_mode, shift_flash, clutch, shift_lock, wall_flash,
                 auto_launch_active):
    font_b, font_m, font_s, font_xs = fonts[0], fonts[1], fonts[2], fonts[3]
    W, H = WIDTH, HEIGHT
    dash_y = 468

    pygame.draw.polygon(surf, (22, 22, 26),
                        [(0, 0), (135, 0), (58, dash_y), (0, dash_y)])
    pygame.draw.polygon(surf, (22, 22, 26),
                        [(W, 0), (W - 135, 0), (W - 58, dash_y), (W, dash_y)])
    pygame.draw.rect(surf, (20, 20, 24), (0, 0, W, 30))
    pygame.draw.line(surf, (48, 48, 56), (0, 30), (W, 30), 2)

    pygame.draw.polygon(surf, (30, 30, 36), [
        (0, dash_y + 22), (W * 0.16, dash_y - 4),
        (W * 0.5, dash_y - 12), (W * 0.84, dash_y - 4),
        (W, dash_y + 22), (W, H), (0, H)
    ])
    pygame.draw.polygon(surf, (44, 44, 52), [
        (0, dash_y + 22), (W * 0.16, dash_y - 4),
        (W * 0.5, dash_y - 12), (W * 0.84, dash_y - 4),
        (W, dash_y + 22), (W, dash_y + 30), (0, dash_y + 30)
    ])
    for cx in (150, 850):
        pygame.draw.rect(surf, (18, 18, 22), (cx - 40, dash_y + 42, 80, 24), border_radius=6)
        pygame.draw.rect(surf, (52, 52, 62), (cx - 36, dash_y + 46, 72, 16), border_radius=4)

    cx, cy = 330, 668
    theta = -steer * 0.55
    rim = ring_poly(cx, cy, 162, 134, math.pi, 2 * math.pi, 44)
    pygame.draw.polygon(surf, (16, 16, 20), rim)
    inner = ring_poly(cx, cy, 158, 138, math.pi, 2 * math.pi, 44)
    pygame.draw.polygon(surf, (34, 34, 40), inner)
    for base in (math.radians(212), math.radians(328)):
        a = base + theta
        p1 = (cx + math.cos(a) * 42, cy + math.sin(a) * 42)
        p2 = (cx + math.cos(a) * 150, cy + math.sin(a) * 150)
        pygame.draw.line(surf, (24, 24, 30), p1, p2, 20)
        pygame.draw.line(surf, (46, 46, 54), p1, p2, 8)
    pygame.draw.circle(surf, (28, 28, 34), (cx, cy), 44)
    pygame.draw.circle(surf, (56, 56, 66), (cx, cy), 44, 3)

    surf.blit(font_b.render(f"{int(speed_kmh)}", True, (240, 240, 250)), (W - 240, H - 128))
    surf.blit(font_m.render("km/h", True, (150, 160, 180)), (W - 108, H - 100))

    gcol = (120, 230, 140) if gear > 0 else (200, 200, 200)
    if manual_mode:
        gcol = (255, 200, 80) if shift_flash > 0 else (120, 230, 140)
    if shift_lock > 0:
        gcol = (230, 70, 70)
    surf.blit(font_b.render(f"{gear}", True, gcol), (W - 235, H - 74))
    mode_txt = "手动" if manual_mode else "自动"
    mode_col = (255, 200, 80) if manual_mode else (130, 140, 160)
    surf.blit(font_s.render(mode_txt, True, mode_col), (W - 178, H - 46))

    bx, by, bw, bh = W - 245, H - 165, 210, 14
    pygame.draw.rect(surf, (18, 18, 24), (bx - 2, by - 2, bw + 4, bh + 4), border_radius=5)
    segs_n = 22
    lit = int(rpm_ratio * segs_n)
    for s in range(segs_n):
        c = (70, 200, 110) if s < segs_n * 0.6 else \
            (230, 200, 70) if s < segs_n * 0.85 else (230, 70, 70)
        if s >= lit: c = (40, 40, 48)
        x0 = bx + s * (bw / segs_n)
        pygame.draw.rect(surf, c, (x0 + 1, by, bw / segs_n - 2, bh), border_radius=3)

    if manual_mode:
        cbx, cby, cbw, cbh = W - 245, H - 185, 210, 10
        surf.blit(font_xs.render("离合", True, (200, 200, 210)), (cbx, cby - 16))
        pygame.draw.rect(surf, (18, 18, 24), (cbx - 2, cby - 2, cbw + 4, cbh + 4), border_radius=4)
        fill = int(cbw * clutch)
        ccol = (230, 70, 70) if clutch > 0.8 else (230, 200, 70) if clutch > 0.2 else (100, 220, 120)
        pygame.draw.rect(surf, ccol, (cbx, cby, fill, cbh), border_radius=3)

    dbx, dby, dbw, dbh = 140, 82, 200, 16
    surf.blit(font_xs.render("车损", True, (200, 200, 210)), (dbx, dby - 22))
    pygame.draw.rect(surf, (18, 18, 24), (dbx - 2, dby - 2, dbw + 4, dbh + 4), border_radius=5)
    fill = int(dbw * min(1.0, dmg / 100.0))
    dcol = (100, 220, 120) if dmg < 40 else (230, 200, 70) if dmg < 70 else (230, 70, 70)
    pygame.draw.rect(surf, dcol, (dbx, dby, fill, dbh), border_radius=4)

    if wall_flash > 0:
        warn = font_m.render("撞墙！", True, (255, 90, 90))
        surf.blit(warn, (W // 2 - warn.get_width() // 2, 80))
        pygame.draw.rect(surf, (255, 90, 90), (0, 0, W, HEIGHT), 3)

    if auto_launch_active:
        badge = font_s.render("自动加速", True, (255, 220, 90))
        surf.blit(badge, (24, 40))

    surf.blit(font_m.render(rank_txt, True, (240, 240, 250)), (W // 2 - 40, 42))

    hint = font_xs.render("W/S 油门刹车  A/D 转向  空格 手刹  M 自动/手动  R 重置",
                          True, (110, 120, 140))
    surf.blit(hint, (24, H - 26))
    if manual_mode:
        hint2 = font_xs.render("Shift 升挡  Ctrl 降挡  按住 C 踩离合", True, (200, 180, 120))
        surf.blit(hint2, (24, H - 46))


# ============================================================
#  初始化
# ============================================================
def spawn_ais(player_seg):
    ais = []
    colors = [(255, 80, 80), (255, 220, 80), (90, 230, 140)]
    lats = [-3.5, 0.0, 3.5]
    for k in range(3):
        ais.append({
            'seg': player_seg,
            'lat': lats[k],
            'lat_target': lats[k],
            'speed': 0.0,
            'color': colors[k],
            'name': f"AI-{k+1}",
            'rel_prog': 0.0,
            'total_dist': 0.0,
        })
    return ais


# ============================================================
#  主程序
# ============================================================
def main():
    pygame.init()
    pygame.display.set_caption("3D 赛车模拟器 — 车内视角 · 自动起步")
    screen = pygame.display.set_mode((WIDTH, HEIGHT))
    clock = pygame.time.Clock()

    fonts = (
        get_font(54, bold=True),
        get_font(22, bold=True),
        get_font(16, bold=True),
        get_font(13, bold=True),
        get_font(140, bold=True),
    )

    segs = build_track()
    car = {
        'x': segs[0][0], 'z': segs[0][1], 'h': segs[0][2],
        'speed': 0.0, 'steer': 0.0, 'gear': 1,
        'dmg': 0.0, 'spin_timer': 0.0,
        'total_dist': 0.0,
    }
    car_seg = 0
    car_seg_ref[0] = 0
    lateral = 0.0
    cam_h_smooth = car['h']

    spawn_x = car['x']
    spawn_z = car['z']
    spawn_h = car['h']

    ais = spawn_ais(0)

    manual_mode = False
    clutch = 0.0
    shift_lock = 0.0
    shift_flash = 0.0
    wall_flash = 0.0

    race_state = 'countdown'
    countdown_timer = COUNTDOWN_TOTAL
    go_timer = 0.0
    auto_launch_timer = 0.0

    running = True
    while running:
        dt = clock.tick(FPS) / 1000.0
        dt = min(dt, 0.05)

        # ---------------- 状态计时 ----------------
        if race_state == 'countdown':
            countdown_timer -= dt
            if countdown_timer <= 0:
                countdown_timer = 0.0
                race_state = 'auto_launch'
                auto_launch_timer = AUTO_LAUNCH_TIME
                go_timer = COUNTDOWN_GO_TIME
                car['h'] = spawn_h
                cam_h_smooth = spawn_h
                car['steer'] = 0.0
        elif race_state == 'auto_launch':
            auto_launch_timer -= dt
            if auto_launch_timer <= 0:
                auto_launch_timer = 0.0
                race_state = 'racing'

        if go_timer > 0:
            go_timer -= dt
            if go_timer < 0: go_timer = 0.0

        # ---------------- 事件 ----------------
        for e in pygame.event.get():
            if e.type == pygame.QUIT:
                running = False
            elif e.type == pygame.KEYDOWN:
                if e.key == pygame.K_ESCAPE:
                    running = False
                elif e.key == pygame.K_r:
                    car['x'] = segs[car_seg][0]
                    car['z'] = segs[car_seg][1]
                    car['h'] = segs[car_seg][2]
                    car['speed'] = 0.0
                    car['steer'] = 0.0
                    car['dmg'] = 0.0
                    car['spin_timer'] = 0.0
                    car['total_dist'] = 0.0
                    car['gear'] = 1

                    spawn_x = car['x']
                    spawn_z = car['z']
                    spawn_h = car['h']

                    clutch = 0.0
                    shift_lock = 0.0
                    shift_flash = 0.0
                    wall_flash = 0.0
                    cam_h_smooth = car['h']
                    ais = spawn_ais(car_seg)
                    race_state = 'countdown'
                    countdown_timer = COUNTDOWN_TOTAL
                    go_timer = 0.0
                    auto_launch_timer = 0.0
                elif e.key == pygame.K_m:
                    manual_mode = not manual_mode
                    if not manual_mode:
                        clutch = 0.0
                elif manual_mode and race_state == 'racing' and e.key in (pygame.K_LSHIFT, pygame.K_RSHIFT):
                    if clutch > 0.5:
                        if car['gear'] < 6:
                            car['gear'] += 1
                            shift_flash = 0.3
                    else:
                        shift_lock = SHIFT_LOCK_TIME
                        car['dmg'] = min(100.0, car['dmg'] + SHIFT_DAMAGE)
                elif manual_mode and race_state == 'racing' and e.key in (pygame.K_LCTRL, pygame.K_RCTRL):
                    if clutch > 0.5:
                        if car['gear'] > 1:
                            car['gear'] -= 1
                            shift_flash = 0.3
                    else:
                        shift_lock = SHIFT_LOCK_TIME
                        car['dmg'] = min(100.0, car['dmg'] + SHIFT_DAMAGE)

        # ---------------- 输入 ----------------
        keys = pygame.key.get_pressed()
        throttle  = keys[pygame.K_UP]   or keys[pygame.K_w]
        braking   = keys[pygame.K_DOWN] or keys[pygame.K_s]
        left      = keys[pygame.K_LEFT] or keys[pygame.K_a]
        right     = keys[pygame.K_RIGHT] or keys[pygame.K_d]
        handbrake = keys[pygame.K_SPACE]

        clutch_pressed = keys[pygame.K_c]

        if manual_mode:
            if clutch_pressed:
                clutch += CLUTCH_PRESS_RATE * dt
            else:
                clutch -= CLUTCH_ENGAGE_RATE * dt
            clutch = max(0.0, min(1.0, clutch))
        else:
            clutch = 0.0

        # ---------------- 转向与锁定 ----------------
        auto_launch_active = (race_state == 'auto_launch')
        if race_state == 'countdown':
            car['x'] = spawn_x
            car['z'] = spawn_z
            car['h'] = spawn_h
            car['speed'] = 0.0
            car['steer'] = 0.0
            car['spin_timer'] = 0.0
            cam_h_smooth = spawn_h
            steer_in = 0
        elif auto_launch_active:
            car['h'] = spawn_h
            car['steer'] = 0.0
            cam_h_smooth = spawn_h
            steer_in = 0
        elif car['spin_timer'] > 0:
            steer_in = 0
        else:
            steer_in = (1 if right else 0) - (1 if left else 0)

        target = steer_in * MAX_STEER
        car['steer'] += (target - car['steer']) * min(1.0, dt * 9.0)

        sp = car['speed']

        if manual_mode:
            gear_top_kmh = GEAR_TOPS_KMH[car['gear'] - 1]
            gear_top_mps = gear_top_kmh / 3.6
            gear_accel_mul = GEAR_ACCEL_MUL[car['gear'] - 1]
        else:
            gear_top_mps = MAX_SPEED
            gear_accel_mul = 1.0

        eff_max = min(gear_top_mps, MAX_SPEED)

        power_mul = (1.0 - clutch)
        eff_acc = ACCEL * gear_accel_mul * power_mul

        if race_state == 'countdown':
            sp = 0.0
        elif auto_launch_active:
            sp += eff_acc * AUTO_LAUNCH_ACCEL_MUL * \
                  (1.0 - (sp / max(1.0, eff_max)) ** 1.5) * dt
            sp = max(0.0, min(sp, eff_max))
        else:
            if throttle and shift_lock <= 0:
                sp += eff_acc * (1.0 - (sp / max(1.0, eff_max)) ** 1.5) * dt
            elif not braking and shift_lock <= 0:
                sp -= 0.9 * dt

            if braking:
                sp -= BRAKE * dt
            if handbrake:
                sp -= 34.0 * dt
            if clutch > 0.5:
                sp -= 2.5 * dt
            if manual_mode and sp * 3.6 < GEAR_MIN_KMH[car['gear'] - 1] and car['gear'] > 1:
                sp -= 8.0 * dt

            sp = max(0.0, min(sp, eff_max))
        car['speed'] = sp

        if not manual_mode and race_state in ('racing', 'auto_launch'):
            kmh_now = sp * 3.6
            if   kmh_now < 28:  car['gear'] = 1
            elif kmh_now < 60:  car['gear'] = 2
            elif kmh_now < 100: car['gear'] = 3
            elif kmh_now < 145: car['gear'] = 4
            elif kmh_now < 190: car['gear'] = 5
            else:               car['gear'] = 6

        # ---------------- 航向更新 ----------------
        if sp > STEER_MIN_SPEED and car['spin_timer'] <= 0 and race_state == 'racing':
            tan_max = MAX_LAT * WHEELBASE / (sp * sp)
            tan_a = math.tan(car['steer'])
            tan_a = max(-tan_max, min(tan_max, tan_a))
            omega = sp * tan_a / WHEELBASE
            car['h'] += omega * dt
        elif car['spin_timer'] > 0:
            car['h'] += 2.0 * dt

        if race_state in ('racing', 'auto_launch'):
            car['x'] += math.sin(car['h']) * sp * dt
            car['z'] += math.cos(car['h']) * sp * dt
            car['total_dist'] += sp * dt

        car_seg = find_segment(segs, car['x'], car['z'], car_seg)
        car_seg_ref[0] = car_seg

        sx, sz, sh, _ = segs[car_seg]
        rx, rz = math.cos(sh), -math.sin(sh)
        lateral = (car['x'] - sx) * rx + (car['z'] - sz) * rz

        # ---------------- 空气墙 ----------------
        wall_limit = HALF_W - WALL_MARGIN
        if race_state in ('racing', 'auto_launch') and abs(lateral) > wall_limit:
            clamped_lat = max(-wall_limit, min(wall_limit, lateral))
            car['x'] = sx + rx * clamped_lat
            car['z'] = sz + rz * clamped_lat
            lateral = clamped_lat

            car['speed'] *= WALL_BOUNCE
            car['dmg'] = min(100.0, car['dmg'] + WALL_IMPACT_DAMAGE)
            wall_flash = 0.3

            if clamped_lat != 0:
                center_dir = math.atan2(-rx * clamped_lat, -rz * clamped_lat)
                car['h'] = ang_lerp(car['h'], center_dir, 0.15)

        # ---------------- 摄像机 ----------------
        if race_state not in ('countdown', 'auto_launch'):
            cam_h_smooth = ang_lerp(cam_h_smooth, car['h'],
                                    min(1.0, dt * CAM_H_SMOOTH_RATE))

        if wall_flash > 0:
            wall_flash -= dt

        # ---------------- AI 更新（已降速） ----------------
        if race_state == 'racing':
            player_speed_ratio = sp / MAX_SPEED
            for k, ai in enumerate(ais):
                base = AI_BASE_SPEED + k * AI_BASE_STEP
                desired = base + player_speed_ratio * AI_FOLLOW_RATIO
                accel_rate = AI_ACCEL_LOW if ai['speed'] < 5 else AI_ACCEL_HIGH
                ai['speed'] += (desired - ai['speed']) * min(1.0, dt * accel_rate)
                ai['speed'] = min(ai['speed'], AI_MAX_SPEED)

                ai['rel_prog'] += (ai['speed'] - sp) * dt / SEG_LEN
                ai['rel_prog'] = max(-8.0, min(40.0, ai['rel_prog']))

                ai['seg'] = int((car_seg + ai['rel_prog']) % N_SEG)
                ai['total_dist'] += ai['speed'] * dt

                if abs(ai['rel_prog']) < 3.0 and abs(ai['lat'] - lateral) < 2.5:
                    ai['lat_target'] = lateral + (3.5 if ai['lat'] > lateral else -3.5)
                ai['lat'] += (ai['lat_target'] - ai['lat']) * min(1.0, dt * 1.5)
                ai['lat'] = max(-wall_limit + 1.0, min(wall_limit - 1.0, ai['lat']))

        if shift_lock > 0: shift_lock -= dt
        if shift_flash > 0: shift_flash -= dt
        if car['spin_timer'] > 0: car['spin_timer'] -= dt

        # ---------------- 转速 ----------------
        kmh = sp * 3.6
        if manual_mode:
            top = GEAR_TOPS_KMH[car['gear'] - 1]
            rpm_ratio = 0.15 + 0.85 * min(1.0, kmh / max(1.0, top))
            if clutch > 0.3:
                rpm_ratio = min(1.0, rpm_ratio + 0.3 * clutch)
        else:
            rpm_ratio = 0.15 + 0.85 * ((sp / max(1.0, eff_max) * 6.0) % 1.0 if sp > 1 else 0.05)
        rpm_ratio = min(1.0, rpm_ratio)

        # ---------------- 名次 ----------------
        all_dist = [(car['total_dist'], 'player')]
        for ai in ais:
            all_dist.append((ai['total_dist'], ai['name']))
        all_dist.sort(key=lambda x: -x[0])
        player_rank = next(i for i, (d, n) in enumerate(all_dist) if n == 'player') + 1
        rank_txt = f"第 {player_rank} / {len(all_dist)} 名"

        # ---------------- 渲染 ----------------
        w2c, proj = make_projector(car['x'], car['z'], cam_h_smooth, CAM_PITCH)
        draw_road(screen, segs, car['x'], car['z'], cam_h_smooth, CAM_PITCH, w2c, proj)

        vis_ais = []
        for ai in ais:
            sx2, sz2, _, arx, arz = seg_coord(segs, ai['seg'])
            ax = sx2 + arx * ai['lat']
            az = sz2 + arz * ai['lat']
            d = math.hypot(ax - car['x'], az - car['z'])
            vis_ais.append((d, ai))
        vis_ais.sort(key=lambda x: -x[0])
        for d, ai in vis_ais:
            draw_ai_car(screen, ai, segs, car['x'], car['z'], w2c, proj, fonts[3])

        draw_cockpit(screen, car['steer'], kmh, rpm_ratio, car['gear'], fonts,
                     car['dmg'], rank_txt,
                     manual_mode, shift_flash, clutch, shift_lock, wall_flash,
                     auto_launch_active)

        if race_state == 'countdown' or go_timer > 0:
            draw_countdown(screen, fonts, countdown_timer, go_timer)

        if auto_launch_active:
            draw_auto_launch_hint(screen, fonts, auto_launch_timer, AUTO_LAUNCH_TIME)

        if sp > 25:
            amp = (sp - 25) / MAX_SPEED
            off = int(math.sin(pygame.time.get_ticks() * 0.06) * amp * 3)
            screen.scroll(dx=off)

        pygame.display.flip()

    pygame.quit()
    sys.exit()


if __name__ == "__main__":
    main()