import os
import sys
import random
import math
import pygame

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

GROUND_Y = HEIGHT - 100
GRAVITY = 2000.0  # 像素/秒^2
JUMP_V = -760.0   # 初始跳跃速度（向上为负）
PLAYER_W, PLAYER_H = 56, 64

SCROLL_SPEED_BASE = 320.0   # 基础滚动速度（像素/秒）
SCROLL_ACCEL = 6.0          # 随时间难度增加速度
OBSTACLE_SPAWN_BASE = 1.0   # 初始生成间隔（秒）
OBSTACLE_MIN_GAP = 220      # 障碍间最小间距（像素）

FONT_SIZE = 22
HIGHSCORE_FILE = "highscore.txt"

pygame.init()
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("简易跑酷（Endless Runner）")
clock = pygame.time.Clock()
font = pygame.font.SysFont(None, FONT_SIZE)

# ===== 辅助：高分读写 =====
def load_highscore():
    try:
        with open(HIGHSCORE_FILE, "r") as f:
            return int(f.read().strip() or 0)
    except Exception:
        return 0

def save_highscore(v):
    try:
        with open(HIGHSCORE_FILE, "w") as f:
            f.write(str(int(v)))
    except Exception:
        pass

highscore = load_highscore()

# ===== 玩家类 =====
class Player:
    def __init__(self, x, y):
        self.pos = pygame.Vector2(x, y)
        self.vel = pygame.Vector2(0, 0)
        self.w = PLAYER_W
        self.h = PLAYER_H
        self.on_ground = False
        self.ducking = False
        self.alive = True
        # 跑步动画帧（用简单矩形替代图片）
        self.anim_timer = 0.0
        self.anim_frame = 0
        # 碰撞盒（和 sprite 高度分开，duck 时高度变小）
        self.stand_h = PLAYER_H
        self.duck_h = PLAYER_H * 0.6

    def rect(self):
        h = self.duck_h if self.ducking and self.on_ground else self.stand_h
        r = pygame.Rect(0, 0, self.w, h)
        r.midbottom = (int(self.pos.x), int(self.pos.y))
        return r

    def update(self, dt, keys):
        # 自动向右的世界通过相机移动实现，玩家横向固定
        # 垂直运动受重力
        if not self.alive:
            return

        # 下蹲（仅在地面）
        if (keys[pygame.K_s] or keys[pygame.K_DOWN]) and self.on_ground:
            self.ducking = True
        else:
            # 如果头顶有东西，继续ducking（简单不检测）
            self.ducking = False

        # 跳跃
        if (keys[pygame.K_SPACE] or keys[pygame.K_w] or keys[pygame.K_UP]) and self.on_ground:
            self.vel.y = JUMP_V
            self.on_ground = False
            # 跳跃粒子（可扩展）
            particles.append(Particle((self.pos.x, self.pos.y + 6)))

        # 重力
        self.vel.y += GRAVITY * dt
        self.pos.y += self.vel.y * dt

        # 落地
        ground_y = GROUND_Y
        if self.pos.y > ground_y:
            self.pos.y = ground_y
            self.vel.y = 0
            if not self.on_ground:
                # 着地粒子
                for _ in range(10):
                    particles.append(Particle((self.pos.x, self.pos.y)))
            self.on_ground = True

        # 动画
        if self.on_ground and not self.ducking:
            self.anim_timer += dt * 14.0
            if self.anim_timer >= 1.0:
                self.anim_timer -= 1.0
                self.anim_frame = (self.anim_frame + 1) % 4
        else:
            self.anim_frame = 0

    def draw(self, surf):
        r = self.rect()
        # 身体
        body_col = (40, 160, 240) if self.alive else (140, 140, 140)
        pygame.draw.rect(surf, body_col, r)
        # 头（稍微突出）
        head = pygame.Rect(0,0, r.w * 0.6, r.h * 0.28)
        head.midbottom = (r.centerx, r.top + int(r.h * 0.28))
        pygame.draw.rect(surf, (240, 220, 180), head)
        # 简易脚动画（通过两条线模拟）
        foot_y = r.bottom - 6
        stride = 8 if self.anim_frame % 2 == 0 else -8
        pygame.draw.line(surf, (30,30,30), (r.centerx - 10, foot_y), (r.centerx - 10 + stride, foot_y+8), 4)
        pygame.draw.line(surf, (30,30,30), (r.centerx + 10, foot_y), (r.centerx + 10 - stride, foot_y+8), 4)

# ===== 障碍与平台 =====
class Obstacle:
    def __init__(self, x, y, w, h, typ="block"):
        self.pos = pygame.Vector2(x, y)  # pos 表示 midbottom
        self.w = w
        self.h = h
        self.type = typ
        self.dead = False

    def rect(self):
        r = pygame.Rect(0, 0, self.w, self.h)
        r.midbottom = (int(self.pos.x), int(self.pos.y))
        return r

    def update(self, dt, speed):
        self.pos.x -= speed * dt
        if self.pos.x < -200:
            self.dead = True

    def draw(self, surf):
        r = self.rect()
        if self.type == "block":
            pygame.draw.rect(surf, (180, 60, 60), r)
        elif self.type == "spike":
            # 绘制简单三角尖刺
            points = [
                (r.left, r.bottom),
                (r.left + r.w * 0.5, r.top),
                (r.right, r.bottom)
            ]
            pygame.draw.polygon(surf, (40,40,40), points)
        elif self.type == "platform":
            pygame.draw.rect(surf, (100, 100, 220), r)
            pygame.draw.rect(surf, (80, 80, 200), (r.left, r.top, r.w, 6))

# 粒子（简单）
class Particle:
    def __init__(self, pos):
        self.pos = pygame.Vector2(pos)
        angle = random.uniform(-math.pi/2 - 0.6, -math.pi/2 + 0.6)
        speed = random.uniform(160, 420)
        self.vel = pygame.Vector2(math.cos(angle) * speed, math.sin(angle) * speed)
        self.life = random.uniform(0.4, 0.9)
        self.size = random.uniform(2, 5)
        self.col = (200 + random.randint(0,55), 150 + random.randint(0,80), 60)

    def update(self, dt):
        self.life -= dt
        self.pos += self.vel * dt
        self.vel.y += GRAVITY * 0.3 * dt
        # 阻尼
        self.vel *= (1 - dt * 3)

    def draw(self, surf):
        if self.life <= 0:
            return
        alpha = max(0, min(255, int(255 * (self.life / 1.0))))
        pygame.draw.circle(surf, self.col, (int(self.pos.x), int(self.pos.y)), int(self.size))

# ===== 游戏状态与工具 =====
def reset_game():
    p = Player(160, GROUND_Y)
    return {
        "player": p,
        "obstacles": [],
        "particles": [],
        "scroll": SCROLL_SPEED_BASE,
        "spawn_timer": 0.0,
        "score": 0.0,
        "distance": 0.0,
        "running": True,
        "game_over": False,
        "time": 0.0
    }

state = reset_game()
particles = state["particles"]

def spawn_obstacle(state):
    # 随机生成普通障碍、尖刺或可站立平台
    typ = random.choices(["block", "spike", "platform"], weights=[60, 25, 15])[0]
    x = WIDTH + random.uniform(80, 160)
    if typ == "block":
        h = random.randint(40, 120)
        w = random.randint(36, 64)
        y = GROUND_Y
        return Obstacle(x, y, w, h, "block")
    elif typ == "spike":
        w = random.randint(40, 80)
        h = random.randint(28, 48)
        y = GROUND_Y
        return Obstacle(x, y, w, h, "spike")
    else:
        # 平台：高度高于地面，可跳上去
        w = random.randint(140, 260)
        h = 18
        y = GROUND_Y - random.choice([120, 180, 240])
        return Obstacle(x, y, w, h, "platform")

def rects_collide(r1, r2):
    return r1.colliderect(r2)

# ===== 主循环 =====
running = True
while running:
    dt = clock.tick(FPS) / 1000.0
    state["time"] += dt

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
        elif event.type == pygame.KEYDOWN:
            if event.key == pygame.K_r:
                # 重置
                if state["game_over"]:
                    if state["score"] > highscore:
                        highscore = int(state["score"])
                        save_highscore(highscore)
                state = reset_game()
                particles = state["particles"]
            elif event.key == pygame.K_ESCAPE:
                running = False

    keys = pygame.key.get_pressed()

    if not state["game_over"]:
        # 增加滚动速度随时间提高难度
        state["scroll"] += SCROLL_ACCEL * dt
        speed = state["scroll"]

        # 产生障碍（基于距离保证最小间隔）
        state["spawn_timer"] -= dt
        spawn_interval = max(0.45, OBSTACLE_SPAWN_BASE - min(0.9, state["time"] * 0.02))
        if state["spawn_timer"] <= 0:
            # 通过检查最近障碍位置保证最小像素间隔
            can_spawn = True
            if state["obstacles"]:
                last_x = max(o.pos.x for o in state["obstacles"])
                if last_x > WIDTH and last_x - WIDTH < OBSTACLE_MIN_GAP:
                    can_spawn = False
            if can_spawn:
                state["obstacles"].append(spawn_obstacle(state))
                state["spawn_timer"] = spawn_interval

        # 更新 player
        state["player"].update(dt, keys)

        # 更新障碍和检测碰撞
        for o in state["obstacles"]:
            o.update(dt, speed)
        # 平台特性：若玩家在平台上要随平台位移（这里平台不移动，只垂直在空中）
        # 检测碰撞：玩家脚部与障碍顶部接触 => 支持踩在平台上
        player_rect = state["player"].rect()
        player_bottom_prev = player_rect.bottom

        # 先处理平台着陆：如果玩家正在下落，检查是否能站到平台
        if not state["player"].on_ground:
            for o in state["obstacles"]:
                if o.type == "platform":
                    plat_rect = o.rect()
                    # 判断玩家在平台横向范围内，且从上方向下碰到平台顶部
                    if player_rect.centerx > plat_rect.left + 8 and player_rect.centerx < plat_rect.right - 8:
                        # 预计下落一小步后的 y
                        next_y = state["player"].pos.y + state["player"].vel.y * dt
                        # 如果玩家脚会低于平台顶并目前高于平台顶 -> 着陆
                        if next_y >= plat_rect.top and state["player"].pos.y <= plat_rect.top:
                            state["player"].pos.y = plat_rect.top
                            state["player"].vel.y = 0
                            state["player"].on_ground = True

        # 一般地面与阻挡物碰撞（死亡情况）
        for o in state["obstacles"]:
            if o.type in ("block", "spike"):
                if rects_collide(player_rect, o.rect()):
                    # 简单判定：若是 spike 或撞到 block 则死亡
                    state["player"].alive = False
                    state["game_over"] = True
                    # 生成大量粒子
                    for _ in range(30):
                        particles.append(Particle(state["player"].pos))
                    break

        # 清理离开屏幕的障碍
        state["obstacles"] = [o for o in state["obstacles"] if not o.dead]

        # 更新粒子
        for p in particles:
            p.update(dt)
        particles = [p for p in particles if p.life > 0]
        state["particles"] = particles

        # 增加分数（基于时间或滚动距离）
        state["distance"] += speed * dt
        state["score"] = state["distance"] * 0.1  # 调整系数显示友好数字

    else:
        # 游戏结束时仍可更新部分粒子动画
        for p in particles:
            p.update(dt)
        particles = [p for p in particles if p.life > 0]
        state["particles"] = particles

    # ===== 绘制 =====
    screen.fill((135, 206, 235))  # 天空蓝

    # 画远景山丘（简单）
    hills_off = int(state["time"] * 20) % (WIDTH*2)
    for i in range(-1, 3):
        hx = (i * 600 - hills_off) + 50
        pygame.draw.ellipse(screen, (100, 180, 120), (hx, GROUND_Y - 220, 700, 260))

    # 画地面（带纹理线）
    pygame.draw.rect(screen, (80, 200, 90), (0, GROUND_Y, WIDTH, HEIGHT - GROUND_Y))
    # 地面表面线条
    for i in range(0, WIDTH, 40):
        pygame.draw.line(screen, (60,160,60), (i - (int(state["time"]*80) % 40), GROUND_Y + 6), (i - (int(state["time"]*80) % 40), GROUND_Y + 10), 2)

    # 绘制平台和障碍
    for o in state["obstacles"]:
        o.draw(screen)

    # 绘制玩家
    state["player"].draw(screen)

    # 粒子
    for p in particles:
        p.draw(screen)

    # UI
    score_surf = font.render(f"分数: {int(state['score'])}", True, (20,20,20))
    screen.blit(score_surf, (12, 12))
    hs_surf = font.render(f"最高分: {highscore}", True, (20,20,20))
    screen.blit(hs_surf, (12, 40))
    info = font.render("空格/W/↑ 跳跃   S/↓ 下蹲   R 重新开始   Esc 退出", True, (30,30,30))
    screen.blit(info, (12, HEIGHT - 32))

    if state["game_over"]:
        big = pygame.font.SysFont(None, 64).render("游戏结束", True, (200, 30, 30))
        screen.blit(big, (WIDTH // 2 - big.get_width() // 2, HEIGHT // 2 - 40))
        hint = font.render("按 R 重试   Esc 退出", True, (30,30,30))
        screen.blit(hint, (WIDTH // 2 - hint.get_width() // 2, HEIGHT // 2 + 30))
        # 保存高分
        if state["score"] > highscore:
            highscore = int(state["score"])
            save_highscore(highscore)

    pygame.display.flip()

pygame.quit()
sys.exit()