import pygame
import random
import sys
import math

# ── 初始化 ──────────────────────────────────────
pygame.init()
pygame.mixer.init()
WIDTH, HEIGHT = 880, 460
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("012.跑酷")
clock = pygame.time.Clock()

# ── 颜色 ─────────────────────────────────────────
WHITE   = (255, 255, 255)
BLACK   = (0, 0, 0)
SKY_BLUE= (135, 205, 242)
GROUND_C= (88, 53, 21)
BODY_C  = (45, 198, 98)
HEAD_C  = (252, 228, 138)
OBS_C   = (172, 43, 37)
SPIKE_C = (128, 26, 19)

# ── 常量 ─────────────────────────────────────────
GRAVITY       = 0.86
JUMP_FORCE    = -15.5
GROUND_Y      = HEIGHT - 65
INIT_SPEED    = 5.0
MAX_SPEED     = 12.0
SPEED_STEP    = 0.025    # 每帧加速幅度
BG_SCROLL_SPD = 2.0

# ── 音效生成 ──────────────────────────────────────
def make_sound(freq, dur, vol=0.35, wtype='sine'):
    sr = 22050
    n = int(sr * dur / 1000)
    buf = bytearray(n * 2)
    for i in range(n):
        t = i / sr
        env = max(0, 1 - t / (dur / 1000))
        if wtype == 'sine':
            v = int(vol * 32000 * math.sin(2 * math.pi * freq * t) * env)
        elif wtype == 'noise':
            v = int(vol * 28000 * (random.random() * 2 - 1) * env)
        else:
            v = 0
        buf[i*2:i*2+2] = max(-32767, min(32767, v)).to_bytes(2, 'little', signed=True)
    s = pygame.mixer.Sound(buffer=buf)
    s.set_volume(0.7)
    return s

jump_snd  = make_sound(490, 145, wtype='sine')
crash_snd = make_sound(120, 350, wtype='noise')

# ── 数字图片生成（替代字体）───────────────────────
def make_digit_img(num, size=30, color=(255,255,255)):
    """用像素点阵画出简单数字，避免字体问题"""
    # 每个数字用 5x7 点阵表示
    digits = {
        '0': [0b01110, 0b10001, 0b10011, 0b10101, 0b11001, 0b10001, 0b01110],
        '1': [0b00100, 0b01100, 0b00100, 0b00100, 0b00100, 0b00100, 0b01110],
        '2': [0b01110, 0b10001, 0b00001, 0b00010, 0b00100, 0b01000, 0b11111],
        '3': [0b01110, 0b10001, 0b00001, 0b00110, 0b00001, 0b10001, 0b01110],
        '4': [0b00010, 0b00110, 0b01010, 0b10010, 0b11111, 0b00010, 0b00010],
        '5': [0b11111, 0b10000, 0b11110, 0b00001, 0b00001, 0b10001, 0b01110],
        '6': [0b00110, 0b01000, 0b10000, 0b11110, 0b10001, 0b10001, 0b01110],
        '7': [0b11111, 0b00001, 0b00010, 0b00100, 0b01000, 0b01000, 0b01000],
        '8': [0b01110, 0b10001, 0b10001, 0b01110, 0b10001, 0b10001, 0b01110],
        '9': [0b01110, 0b10001, 0b10001, 0b01111, 0b00001, 0b00010, 0b01100],
        '-': [0b00000, 0b00000, 0b00000, 0b11111, 0b00000, 0b00000, 0b00000],
    }
    scale = size // 8
    w = 5 * scale
    h = 7 * scale
    img = pygame.Surface((w, h), pygame.SRCALPHA)
    num_str = str(num)
    total_w = len(num_str) * (w + 2)
    total_img = pygame.Surface((total_w, h), pygame.SRCALPHA)
    for idx, ch in enumerate(num_str):
        pattern = digits.get(ch, digits['0'])
        for row in range(7):
            bits = pattern[row]
            for col in range(5):
                if bits & (1 << (4 - col)):
                    px = idx * (w + 2) + col * scale
                    py = row * scale
                    for dx in range(scale):
                        for dy in range(scale):
                            total_img.set_at((px+dx, py+dy), color)
    return total_img

def blit_num(surf, num, x, y, size=30, color=(255,255,255)):
    img = make_digit_img(num, size, color)
    surf.blit(img, (x, y))

# ── 滚动背景 ──────────────────────────────────────
class Background:
    def __init__(self):
        self.offset = 0
        self.clouds = []
        for _ in range(8):
            self.clouds.append({
                'x': random.randint(0, WIDTH),
                'y': random.randint(20, 140),
                'w': random.randint(60, 120),
                'h': random.randint(20, 35),
                'speed': random.uniform(0.3, 0.8)
            })
        # 远山
        self.mountains = []
        for _ in range(6):
            self.mountains.append({
                'x': random.randint(0, WIDTH * 2),
                'h': random.randint(30, 70),
                'w': random.randint(120, 200)
            })

    def update(self):
        self.offset = (self.offset + BG_SCROLL_SPD) % WIDTH
        for c in self.clouds:
            c['x'] -= c['speed']
            if c['x'] + c['w'] < 0:
                c['x'] = WIDTH + random.randint(0, 100)
                c['y'] = random.randint(20, 140)

    def draw(self, surf):
        # 天空
        surf.fill(SKY_BLUE)
        # 云
        for c in self.clouds:
            pygame.draw.ellipse(surf, (255,255,255, 200),
                                (c['x'], c['y'], c['w'], c['h']))
            pygame.draw.ellipse(surf, (240,240,245),
                                (c['x']+10, c['y']-5, c['w']-20, c['h']+5))
        # 远山
        for m in self.mountains:
            mx = (m['x'] - self.offset * 0.2) % (WIDTH + 200) - 100
            my = GROUND_Y - 10
            pts = [(mx, my), (mx + m['w']//2, my - m['h']), (mx + m['w'], my)]
            pygame.draw.polygon(surf, (105, 142, 168), pts)

# ── 玩家 ─────────────────────────────────────────
class Player:
    def __init__(self):
        self.w, self.h = 46, 63
        self.x = 115
        self.y = GROUND_Y - self.h
        self.vy = 0
        self.grounded = True
        self.run_frame = 0

    def jump(self):
        if self.grounded:
            self.vy = JUMP_FORCE
            self.grounded = False
            jump_snd.play()

    def update(self):
        self.vy += GRAVITY
        self.y += self.vy
        if self.y >= GROUND_Y - self.h:
            self.y = GROUND_Y - self.h
            self.vy = 0
            self.grounded = True
        self.run_frame = (self.run_frame + 0.08) % 2

    def draw(self, surf):
        x, y = self.x, self.y
        w, h = self.w, self.h
        # 腿（交替摆动）
        leg_off = 3 if int(self.run_frame) == 0 else -3
        leg_w, leg_h = 7, h // 5
        leg_y = y + h - leg_h
        pygame.draw.ellipse(surf, (38, 162, 76),
                            (x + w//2 - leg_w - 3 + leg_off, leg_y, leg_w, leg_h))
        pygame.draw.ellipse(surf, (38, 162, 76),
                            (x + w//2 + 2 - leg_off, leg_y, leg_w, leg_h))
        # 身体
        body_r = pygame.Rect(x + w//4, y + h//4, w//2, h//2)
        pygame.draw.rect(surf, BODY_C, body_r, border_radius=6)
        # 手臂
        arm_y = y + h//3
        pygame.draw.line(surf, BODY_C, (x + w//4 - 3, arm_y),
                         (x + w//4 - 12, arm_y + 10), 4)
        pygame.draw.line(surf, BODY_C, (x + 3*w//4 + 3, arm_y),
                         (x + 3*w//4 + 12, arm_y + 10), 4)
        # 头
        cx, cy = x + w//2, y + h//4 - 8
        r = 14
        pygame.draw.circle(surf, HEAD_C, (cx, cy), r)
        # 眼睛
        pygame.draw.circle(surf, BLACK, (cx - 5, cy - 2), 3)
        pygame.draw.circle(surf, BLACK, (cx + 5, cy - 2), 3)
        # 嘴巴
        pygame.draw.arc(surf, BLACK, (cx - 5, cy + 1, 10, 6), 0.2, math.pi - 0.2, 2)

    def rect(self):
        m = 7
        return pygame.Rect(self.x + m, self.y + m, self.w - 2*m, self.h - 2*m)

# ── 障碍物 ───────────────────────────────────────
class Obstacle:
    def __init__(self, spd):
        self.w = random.randint(34, 54)
        self.h = random.randint(40, 78)
        self.x = WIDTH + random.randint(0, 200)
        self.y = GROUND_Y - self.h
        self.spd = spd

    def update(self):
        self.x -= self.spd

    def draw(self, surf):
        x, y, w, h = self.x, self.y, self.w, self.h
        # 主体
        pygame.draw.rect(surf, OBS_C, (x, y, w, h), border_radius=5)
        # 尖刺
        n = max(3, w // 12)
        for i in range(n):
            sx = x + (i + 0.5) * (w / n)
            pygame.draw.polygon(surf, SPIKE_C,
                                [(sx, y - 10), (sx - 7, y), (sx + 7, y)])

    def off_screen(self):
        return self.x + self.w < -50

    def rect(self):
        m = 4
        return pygame.Rect(self.x + m, self.y + m, self.w - 2*m, self.h - 2*m)

# ── 粒子特效 ──────────────────────────────────────
class Particles:
    def __init__(self):
        self.list = []

    def burst(self, x, y, n=10):
        for _ in range(n):
            self.list.append({
                'x': x, 'y': y,
                'vx': random.uniform(-4, 4),
                'vy': random.uniform(-6, -1),
                'life': 25,
                'clr': random.choice([(255,215,0),(255,170,0),(255,255,120)])
            })

    def update(self):
        for p in self.list[:]:
            p['x'] += p['vx']
            p['y'] += p['vy']
            p['vy'] += 0.2
            p['life'] -= 1
            if p['life'] <= 0:
                self.list.remove(p)

    def draw(self, surf):
        for p in self.list:
            a = min(255, p['life'] * 10)
            clr = tuple(c * a // 255 for c in p['clr'])
            pygame.draw.circle(surf, clr, (int(p['x']), int(p['y'])), 3)

# ── 游戏主循环 ───────────────────────────────────
def game_loop():
    player = Player()
    bg = Background()
    particles = Particles()
    obstacles = []
    score = 0
    speed = INIT_SPEED
    last_spawn = pygame.time.get_ticks()

    while True:
        now = pygame.time.get_ticks()

        # 加速
        speed = min(MAX_SPEED, speed + SPEED_STEP * 0.03)

        for e in pygame.event.get():
            if e.type == pygame.QUIT:
                pygame.quit(); sys.exit()
            if e.type == pygame.KEYDOWN:
                if e.key == pygame.K_SPACE:
                    player.jump()
                if e.key == pygame.K_ESCAPE:
                    return None

        # 生成障碍物
        gap = max(550, 1700 - score * 2.5)
        if now - last_spawn > gap:
            obstacles.append(Obstacle(speed))
            last_spawn = now

        # 更新
        player.update()
        bg.update()

        for ob in obstacles[:]:
            ob.update()
            if ob.off_screen():
                obstacles.remove(ob)
                score += 1
                particles.burst(WIDTH//2, GROUND_Y - 30)

        particles.update()

        # 碰撞
        pr = player.rect()
        for ob in obstacles:
            if pr.colliderect(ob.rect()):
                crash_snd.play()
                return score

        # ── 绘制 ──
        bg.draw(screen)

        # 地面
        pygame.draw.rect(screen, GROUND_C, (0, GROUND_Y, WIDTH, HEIGHT - GROUND_Y))
        for gx in range(0, WIDTH, 56):
            off = (now * speed * 0.018) % 56
            pygame.draw.line(screen, (132, 87, 38),
                             (gx - off, GROUND_Y), (gx - off + 28, GROUND_Y), 3)

        player.draw(screen)
        for ob in obstacles:
            ob.draw(screen)
        particles.draw(screen)

        # 显示分数（纯数字点阵，无字体依赖）
        blit_num(screen, score, 18, 16, 32, WHITE)

        # 速度指示
        speed_int = int(speed * 10)
        blit_num(screen, f'SP{speed_int}', 18, 52, 18, (200,200,200))

        pygame.display.flip()
        clock.tick(60)

# ── Game Over ────────────────────────────────────
def show_end(score):
    wait = True
    while wait:
        for e in pygame.event.get():
            if e.type == pygame.QUIT:
                pygame.quit(); sys.exit()
            if e.type == pygame.KEYDOWN:
                if e.key == pygame.K_ESCAPE:
                    return False
                return True

        screen.fill((25, 25, 35))
        # 用点阵画文字
        blit_num(screen, 'GAMEOVER', WIDTH//2 - 120, HEIGHT//2 - 60, 28, (255,70,70))
        blit_num(screen, score, WIDTH//2 - 40, HEIGHT//2 - 10, 36, WHITE)
        blit_num(screen, 'PRESSANYKEY', WIDTH//2 - 100, HEIGHT//2 + 50, 20, (180,180,185))

        pygame.display.flip()
        clock.tick(30)
    return False

# ── 启动 ──────────────────────────────────────────
if __name__ == "__main__":
    while True:
        result = game_loop()
        if result is None:
            break
        if not show_end(result):
            break
    pygame.quit()
    sys.exit()