from ursina import *
import random

app = Ursina()

# ---------- 基础设置 ----------
window.title = "3D Parkour Runner - Fixed"
window.borderless = False
window.size = (800, 600)
window.color = color.rgb(100, 150, 255)

# 光照
DirectionalLight(parent=camera, rotation=(45, 45, 0))
AmbientLight(color=color.rgba(100, 100, 100, 50))
Sky()

# ---------- 游戏变量 ----------
LANE_POSITIONS = [-2, 0, 2]
GROUND_Y = 0
INITIAL_GAME_SPEED = 10
SPEED_INCREASE_RATE = 0.5
game_speed = INITIAL_GAME_SPEED
game_over = False
score = 0

# ---------- 摄像机固定视角 ----------
camera.position = (0, 5, -8)          # 角色后方高处
camera.rotation_x = 20                # 微微俯视
camera.look_at((0, 1, 10))            # 看向前方远处

# ---------- 场景建筑 ----------
road = Entity(model='cube', scale=(6, 0.1, 200), color=color.hex('#3a3a3a'),
              position=(0, -0.05, 0), texture='white_cube', texture_scale=(6, 50))

# 车道线
for z in range(-90, 100, 6):
    for lane_offset in [-0.7, 0.7]:
        Entity(model='cube', scale=(0.1, 0.01, 2), color=color.white,
               position=(lane_offset, 0.01, z), texture='white_cube')

# 路沿围栏
for side in [-3.2, 3.2]:
    for z in range(-90, 100, 3):
        Entity(model='cube', scale=(0.1, 1, 0.1), color=color.gray, position=(side, 0.5, z))
    Entity(model='cube', scale=(0.1, 0.1, 200), color=color.gray, position=(side, 1.0, 0))

# 路灯
for z in range(-80, 100, 15):
    Entity(model='cylinder', scale=(0.1, 2, 0.1), color=color.dark_gray, position=(0, 1, z))
    Entity(model='sphere', scale=(0.2, 0.2, 0.2), color=color.yellow, position=(0, 2.1, z))

# 树木
for i in range(25):
    side = random.choice([-5, 5])
    z = random.uniform(-80, 80)
    Entity(model='cylinder', scale=(0.3, 2.5, 0.3), color=color.brown, position=(side, 1.25, z))
    for j in range(3):
        Entity(model='sphere', scale=(1.2 - j*0.3, 1.2 - j*0.3, 1.2 - j*0.3),
               color=color.rgb(30, 100 + j*30, 30), position=(side, 2.5 + j*0.8, z))

# 远处建筑剪影
for z in range(-90, 100, 20):
    Entity(model='cube', scale=(2, random.uniform(3, 8), 0.5), color=color.rgb(40, 40, 40),
           position=(random.choice([-8, 8]), random.uniform(1.5, 4), z), rotation_y=random.uniform(0, 15))

# ---------- 玩家 ----------
class Player(Entity):
    def __init__(self):
        super().__init__(position=(0, GROUND_Y + 0.9, 0))
        self.lane_index = 1
        self.target_lane = 1
        self.velocity_y = 0
        self.is_jumping = False
        self.grounded = True
        self.run_cycle = 0

        # 身体
        self.torso = Entity(model='cube', scale=(0.5, 0.6, 0.3), color=color.hex('#3498db'),
                            parent=self, position=(0, 0.3, 0))
        self.head = Entity(model='sphere', scale=(0.4, 0.4, 0.4), color=color.hex('#f1c40f'),
                           parent=self, position=(0, 0.85, 0))
        self.eye_left = Entity(model='sphere', scale=(0.08, 0.08, 0.08), color=color.black,
                               parent=self.head, position=(-0.1, 0.1, 0.2))
        self.eye_right = Entity(model='sphere', scale=(0.08, 0.08, 0.08), color=color.black,
                                parent=self.head, position=(0.1, 0.1, 0.2))
        self.leg_left = Entity(model='cube', scale=(0.15, 0.4, 0.15), color=color.hex('#2c3e50'),
                               parent=self, position=(-0.15, -0.2, 0))
        self.leg_right = Entity(model='cube', scale=(0.15, 0.4, 0.15), color=color.hex('#2c3e50'),
                                parent=self, position=(0.15, -0.2, 0))

    def update(self):
        if game_over:
            return
        target_x = LANE_POSITIONS[self.target_lane]
        self.x = lerp(self.x, target_x, 0.2 * 60 * time.dt)

        # 跑步动画
        if not self.grounded or abs(self.x - target_x) > 0.01:
            self.run_cycle += 15 * time.dt
            self.leg_left.rotation_x = sin(self.run_cycle) * 30
            self.leg_right.rotation_x = -sin(self.run_cycle) * 30
        else:
            self.leg_left.rotation_x = 0
            self.leg_right.rotation_x = 0

        # 跳跃物理
        if not self.grounded:
            self.velocity_y -= 0.8 * 60 * time.dt
            self.y += self.velocity_y * time.dt

        if self.y <= GROUND_Y + 0.9:
            self.y = GROUND_Y + 0.9
            self.velocity_y = 0
            self.grounded = True
            self.is_jumping = False

    def jump(self):
        if self.grounded:
            self.velocity_y = 8
            self.grounded = False
            self.is_jumping = True

    def move_left(self):
        if self.target_lane > 0:
            self.target_lane -= 1

    def move_right(self):
        if self.target_lane < 2:
            self.target_lane += 1

player = Player()

# ---------- 障碍物 ----------
class Obstacle(Entity):
    def __init__(self, lane, obstacle_type='barrier'):
        x = LANE_POSITIONS[lane]
        z = random.uniform(30, 50)
        if obstacle_type == 'barrier':
            super().__init__(model='cone', color=color.hex('#e74c3c'), scale=(0.6, 0.8, 0.6),
                             position=(x, GROUND_Y + 0.4, z), texture='white_cube')
            Entity(model='cube', scale=(0.7, 0.1, 0.7), color=color.white, parent=self, position=(0, 0.2, 0))
        else:
            super().__init__(model='cube', color=color.hex('#f39c12'), scale=(0.8, 1.5, 0.8),
                             position=(x, GROUND_Y + 0.75, z), texture='white_cube', texture_scale=(1, 2))
        self.lane = lane
        self.obstacle_type = obstacle_type

    def update(self):
        if game_over:
            return
        self.z -= game_speed * time.dt
        if self.z < -10:
            destroy(self)

# ---------- 金币 ----------
class Coin(Entity):
    def __init__(self, lane):
        x = LANE_POSITIONS[lane]
        z = random.uniform(30, 50)
        super().__init__(model='circle', color=color.gold, scale=0.4,
                         position=(x, GROUND_Y + 1, z), rotation_x=90, double_sided=True)
        self.glow = Entity(model='circle', color=color.yellow, scale=0.5,
                           position=(0, 0, -0.01), rotation_x=90, parent=self)
        self.lane = lane

    def update(self):
        if game_over:
            return
        self.z -= game_speed * time.dt
        self.rotation_z += 120 * time.dt
        if self.z < -10:
            destroy(self)

# ---------- 粒子特效 ----------
class Particle(Entity):
    def __init__(self, position, color):
        super().__init__(model='quad', color=color, scale=0.1, position=position, billboard=True)
        self.velocity = Vec3(random.uniform(-1, 1), random.uniform(1, 2), random.uniform(-1, 1))
        self.lifetime = 0.5

    def update(self):
        self.position += self.velocity * time.dt
        self.velocity.y -= 5 * time.dt
        self.lifetime -= time.dt
        if self.lifetime <= 0:
            destroy(self)

# ---------- 生成函数 ----------
def spawn_obstacle():
    lane = random.randint(0, 2)
    obs_type = random.choice(['barrier', 'block'])
    Obstacle(lane, obs_type)

def spawn_coin():
    lane = random.randint(0, 2)
    Coin(lane)

spawn_obstacle_timer = 0
spawn_coin_timer = 0
game_over_text = None

# ---------- 键盘输入 ----------
def input(key):
    global game_over, game_speed, score, spawn_obstacle_timer, spawn_coin_timer, game_over_text

    if key == 'r' and game_over:
        # 重置游戏
        game_over = False
        game_speed = INITIAL_GAME_SPEED
        score = 0
        player.lane_index = 1
        player.target_lane = 1
        player.position = (0, GROUND_Y + 0.9, 0)
        player.grounded = True
        player.is_jumping = False
        player.velocity_y = 0
        for e in scene.entities:
            if isinstance(e, (Obstacle, Coin, Particle)):
                destroy(e)
        spawn_obstacle_timer = 0
        spawn_coin_timer = 0
        if game_over_text:
            destroy(game_over_text)
            game_over_text = None
        return

    if game_over:
        return

    if key == 'space':
        player.jump()
    elif key in ('a', 'left arrow'):
        player.move_left()
    elif key in ('d', 'right arrow'):
        player.move_right()

# ---------- 主更新循环 ----------
def update():
    global game_over, game_speed, score, spawn_obstacle_timer, spawn_coin_timer, game_over_text

    if game_over:
        return

    game_speed += SPEED_INCREASE_RATE * time.dt

    spawn_obstacle_timer += time.dt
    if spawn_obstacle_timer > random.uniform(0.8, 1.5 / (game_speed * 0.1)):
        spawn_obstacle()
        spawn_obstacle_timer = 0

    spawn_coin_timer += time.dt
    if spawn_coin_timer > random.uniform(1.5, 3.0 / (game_speed * 0.1)):
        spawn_coin()
        spawn_coin_timer = 0

    for obs in scene.entities:
        if isinstance(obs, Obstacle):
            if abs(obs.z - player.z) < 0.7 and abs(obs.x - player.x) < 0.7:
                if obs.obstacle_type == 'barrier' and player.is_jumping and player.y > GROUND_Y + 1.2:
                    continue
                game_over = True
                if not game_over_text:
                    game_over_text = Text(
                        text="GAME OVER\nPress R to restart",
                        position=(0, 0.1), scale=3, color=color.red, origin=(0, 0),
                        font='VeraMono.ttf'
                    )
                return

        if isinstance(obs, Coin):
            if abs(obs.z - player.z) < 0.7 and abs(obs.x - player.x) < 0.7:
                score += 10
                for _ in range(8):
                    Particle(obs.world_position, color.gold)
                destroy(obs)

    if not hasattr(update, "score_display"):
        update.score_display = Text(
            text=f"Score: {score}\nSpeed: {game_speed:.1f}",
            position=(-0.85, 0.45), scale=1.5, background=True
        )
    else:
        update.score_display.text = f"Score: {score}\nSpeed: {game_speed:.1f}"

# 初始物体
for _ in range(3):
    spawn_obstacle()
for _ in range(5):
    spawn_coin()

app.run()