import pygame
import math
import random

# --- 初始化 ---
pygame.init()
WIDTH, HEIGHT = 800, 600
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("纯Pygame 3D枪战 - Raycasting FPS")
clock = pygame.time.Clock()

# 颜色
SKY_COLOR = (50, 50, 80)
FLOOR_COLOR = (40, 40, 40)
WALL_COLOR_DARK = (100, 100, 100)
WALL_COLOR_LIGHT = (150, 150, 150)
CROSSHAIR_COLOR = (255, 255, 255)
UI_COLOR = (255, 255, 255)

FONT = pygame.font.SysFont("consolas", 30)
BIG_FONT = pygame.font.SysFont("consolas", 60)

# 地图 (1是墙, 0是空地)
MAP = [
    [1,1,1,1,1,1,1,1,1,1],
    [1,0,0,0,0,0,0,0,0,1],
    [1,0,1,1,0,1,0,1,0,1],
    [1,0,1,0,0,0,0,1,0,1],
    [1,0,0,0,1,0,1,0,0,1],
    [1,0,1,0,1,0,1,0,1,1],
    [1,0,1,0,0,0,0,0,0,1],
    [1,0,0,0,1,1,0,1,0,1],
    [1,0,0,0,0,0,0,0,0,1],
    [1,1,1,1,1,1,1,1,1,1],
]
MAP_W, MAP_H = len(MAP[0]), len(MAP)

# --- 3D 核心类 ---
class Player:
    def __init__(self):
        self.x = 1.5
        self.y = 1.5
        self.angle = 0.0 # 弧度
        self.speed = 0.08
        self.rot_speed = 0.05
        self.hp = 100
        self.cooldown = 0

    def move(self):
        keys = pygame.key.get_pressed()
        dx, dy = 0, 0
        if keys[pygame.K_w]:
            dx += math.cos(self.angle) * self.speed
            dy += math.sin(self.angle) * self.speed
        if keys[pygame.K_s]:
            dx -= math.cos(self.angle) * self.speed
            dy -= math.sin(self.angle) * self.speed
        if keys[pygame.K_a]:
            dx += math.cos(self.angle - math.pi/2) * self.speed
            dy += math.sin(self.angle - math.pi/2) * self.speed
        if keys[pygame.K_d]:
            dx += math.cos(self.angle + math.pi/2) * self.speed
            dy += math.sin(self.angle + math.pi/2) * self.speed

        # 简单的墙壁碰撞
        if MAP[int(self.y)][int(self.x + dx)] == 0: self.x += dx
        if MAP[int(self.y + dy)][int(self.x)] == 0: self.y += dy

        # 旋转
        if keys[pygame.K_LEFT]: self.angle -= self.rot_speed
        if keys[pygame.K_RIGHT]: self.angle += self.rot_speed

        if self.cooldown > 0: self.cooldown -= 1

class Enemy:
    def __init__(self, x, y):
        self.x = x
        self.y = y
        self.alive = True
        self.speed = 0.03

    def update(self, player_x, player_y):
        if not self.alive: return
        dx = player_x - self.x
        dy = player_y - self.y
        dist = math.hypot(dx, dy)
        if dist > 1.0:
            nx = self.x + (dx / dist) * self.speed
            ny = self.y + (dy / dist) * self.speed
            if MAP[int(ny)][int(nx)] == 0:
                self.x = nx
                self.y = ny
        return dist

# --- 渲染与主逻辑 ---
def main():
    player = Player()
    enemies = [Enemy(3.5, 3.5), Enemy(7.5, 7.5), Enemy(2.5, 8.5)]
    game_over = False
    score = 0

    running = True
    while running:
        clock.tick(60)
        for event in pygame.event.get():
            if event.type == pygame.QUIT: running = False
            if event.type == pygame.KEYDOWN and game_over:
                if event.key == pygame.K_r: main(); return

        if not game_over:
            player.move()

            # 射击检测 (鼠标左键)
            if pygame.mouse.get_pressed()[0] and player.cooldown <= 0:
                player.cooldown = 15
                # 向前发射一条射线
                hit_enemy = False
                for e in enemies:
                    if not e.alive: continue
                    dx = e.x - player.x
                    dy = e.y - player.y
                    dist = math.hypot(dx, dy)
                    angle_to_enemy = math.atan2(dy, dx)
                    diff = (angle_to_enemy - player.angle + math.pi) % (2*math.pi) - math.pi
                    # 如果角度差很小，且距离不远，判定击中
                    if abs(diff) < 0.2 and dist < 8:
                        e.alive = False
                        score += 100
                        hit_enemy = True
                        break

            # 敌人攻击
            for e in enemies:
                dist = e.update(player.x, player.y)
                if e.alive and dist < 1.2:
                    player.hp -= 1
                    if player.hp <= 0: game_over = True

        # --- 3D 渲染 (Raycasting) ---
        # 1. 画天空和地板
        screen.fill(SKY_COLOR, (0, 0, WIDTH, HEIGHT // 2))
        screen.fill(FLOOR_COLOR, (0, HEIGHT // 2, WIDTH, HEIGHT // 2))

        # 2. 画墙
        FOV = math.pi / 3 # 60度视野
        num_rays = WIDTH
        z_buffer = [] # 记录每列的深度，用于遮挡敌人

        for i in range(num_rays):
            ray_angle = player.angle - FOV / 2 + (i / num_rays) * FOV
            sin_a = math.sin(ray_angle)
            cos_a = math.cos(ray_angle)

            dist = 0
            hit_wall = False
            while not hit_wall and dist < 20:
                dist += 0.05
                test_x = int(player.x + cos_a * dist)
                test_y = int(player.y + sin_a * dist)
                if test_x < 0 or test_x >= MAP_W or test_y < 0 or test_y >= MAP_H:
                    hit_wall = True
                    dist = 20
                elif MAP[test_y][test_x] == 1:
                    hit_wall = True
            
            # 修复鱼眼效应
            corr_dist = dist * math.cos(ray_angle - player.angle)
            z_buffer.append(corr_dist)

            # 计算墙的高度和颜色
            wall_height = int(HEIGHT / corr_dist)
            shade = max(50, min(255, int(255 / corr_dist)))
            color = (shade, shade, shade)
            
            top = (HEIGHT - wall_height) // 2
            bottom = top + wall_height
            pygame.draw.line(screen, color, (i, top), (i, bottom))

        # 3. 画敌人 (从远到近排序)
        visible_enemies = []
        for e in enemies:
            if not e.alive: continue
            dx = e.x - player.x
            dy = e.y - player.y
            dist = math.hypot(dx, dy)
            angle_to_enemy = math.atan2(dy, dx)
            diff = (angle_to_enemy - player.angle + math.pi) % (2*math.pi) - math.pi
            
            if abs(diff) < FOV / 2:
                screen_x = int((0.5 + diff / FOV) * WIDTH)
                size = int(HEIGHT / dist)
                visible_enemies.append((dist, screen_x, size, e))

        visible_enemies.sort(reverse=True) # 远处的先画

        for dist, screen_x, size, e in visible_enemies:
            half_size = size // 2
            top = (HEIGHT - size) // 2
            bottom = top + size
            left = screen_x - half_size
            right = screen_x + half_size

            # 简单的深度遮挡
            if 0 <= screen_x < WIDTH and z_buffer[screen_x] > dist:
                pygame.draw.rect(screen, (200, 50, 50), (left, top, size, size))
                # 画个眼睛
                eye_size = max(2, size // 5)
                pygame.draw.circle(screen, (255,255,255), (screen_x - eye_size, top + size//3), eye_size)
                pygame.draw.circle(screen, (255,255,255), (screen_x + eye_size, top + size//3), eye_size)

        # UI
        cross_len = 10
        pygame.draw.line(screen, CROSSHAIR_COLOR, (WIDTH//2 - cross_len, HEIGHT//2), (WIDTH//2 + cross_len, HEIGHT//2), 2)
        pygame.draw.line(screen, CROSSHAIR_COLOR, (WIDTH//2, HEIGHT//2 - cross_len), (WIDTH//2, HEIGHT//2 + cross_len), 2)

        hp_txt = FONT.render(f"HP: {max(0, player.hp)}", True, UI_COLOR)
        score_txt = FONT.render(f"SCORE: {score}", True, UI_COLOR)
        screen.blit(hp_txt, (10, 10))
        screen.blit(score_txt, (WIDTH - 180, 10))

        if game_over:
            over_txt = BIG_FONT.render("YOU DIED", True, (255, 50, 50))
            rect = over_txt.get_rect(center=(WIDTH//2, HEIGHT//2 - 30))
            screen.blit(over_txt, rect)
            restart_txt = FONT.render("Press R to Restart", True, UI_COLOR)
            r_rect = restart_txt.get_rect(center=(WIDTH//2, HEIGHT//2 + 30))
            screen.blit(restart_txt, r_rect)

        pygame.display.flip()

    pygame.quit()

if __name__ == "__main__":
    main()