import pygame
import sys
import random
import math

# ========== 初始化 ==========
pygame.init()
WINDOW_WIDTH, WINDOW_HEIGHT = 800, 600
screen = pygame.display.set_mode((WINDOW_WIDTH, WINDOW_HEIGHT))
pygame.display.set_caption("🌀 旋涡吞噬 – 星云幻境")
clock = pygame.time.Clock()

# 世界尺寸
WORLD_WIDTH = 2000
WORLD_HEIGHT = 1500

# ========== 视觉色彩 ==========
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
HOLE_CORE = (10, 10, 20)
GLOW_COLORS = [
    (255, 100, 100), (255, 200, 50), (50, 200, 255),
    (200, 50, 255), (50, 255, 150), (255, 150, 50)
]
SPHERE_COLORS = [
    (255, 80, 80), (80, 180, 255), (255, 210, 50),
    (160, 90, 255), (80, 220, 120), (255, 140, 60)
]
NEBULA_COLORS = [
    (30, 10, 50), (10, 20, 60), (40, 10, 40)
]

# ========== 字体 ==========
font_small = pygame.font.Font(None, 28)
font_med = pygame.font.Font(None, 36)
font_large = pygame.font.Font(None, 48)

# ========== 背景星云 ==========
class Nebula:
    def __init__(self, x, y, radius, color):
        self.x = x
        self.y = y
        self.radius = radius
        self.color = color
        self.particles = []
        for _ in range(int(radius)):
            angle = random.uniform(0, 2 * math.pi)
            dist = random.uniform(0, radius)
            self.particles.append([angle, dist, random.uniform(0.5, 1.5)])

    def draw(self, surface, cam_x, cam_y):
        sx = self.x - cam_x
        sy = self.y - cam_y
        if sx < -self.radius or sx > WINDOW_WIDTH + self.radius or sy < -self.radius or sy > WINDOW_HEIGHT + self.radius:
            return
        # 星云主体（半透明圆）
        nebula_surf = pygame.Surface((self.radius*2, self.radius*2), pygame.SRCALPHA)
        pygame.draw.circle(nebula_surf, (*self.color, 15), (self.radius, self.radius), self.radius)
        surface.blit(nebula_surf, (sx - self.radius, sy - self.radius))
        # 星云粒子
        for p in self.particles:
            angle = p[0] + pygame.time.get_ticks() * 0.0001 * p[2]
            dist = p[1]
            px = sx + math.cos(angle) * dist
            py = sy + math.sin(angle) * dist
            if 0 <= px <= WINDOW_WIDTH and 0 <= py <= WINDOW_HEIGHT:
                alpha = random.randint(30, 80)
                pygame.draw.circle(surface, (*self.color, alpha), (int(px), int(py)), 2)

# ========== 黑洞类 ==========
class BlackHole:
    def __init__(self, x, y, radius, is_player=False):
        self.x = x
        self.y = y
        self.radius = radius
        self.is_player = is_player
        self.vx = random.uniform(-1.5, 1.5)
        self.vy = random.uniform(-1.5, 1.5)
        self.alive = True
        self.glow_color = random.choice(GLOW_COLORS)
        self.trail = []
        # 吸积盘粒子
        self.disk_particles = []
        for _ in range(20):
            self.disk_particles.append({
                'angle': random.uniform(0, 2*math.pi),
                'dist': random.uniform(0.3, 1.0) * radius,
                'speed': random.uniform(0.02, 0.06),
                'size': random.randint(2, 4)
            })

    def update(self, dt):
        if not self.is_player:
            self.vx += random.uniform(-0.1, 0.1) * dt
            self.vy += random.uniform(-0.1, 0.1) * dt
            speed = math.hypot(self.vx, self.vy)
            if speed > 2.0:
                self.vx = self.vx / speed * 2.0
                self.vy = self.vy / speed * 2.0
            self.x += self.vx * dt * 60
            self.y += self.vy * dt * 60
            if self.x < self.radius or self.x > WORLD_WIDTH - self.radius:
                self.vx *= -1
            if self.y < self.radius or self.y > WORLD_HEIGHT - self.radius:
                self.vy *= -1
            self.x = max(self.radius, min(WORLD_WIDTH - self.radius, self.x))
            self.y = max(self.radius, min(WORLD_HEIGHT - self.radius, self.y))

        # 轨迹
        self.trail.append([self.x, self.y, 255])
        for t in self.trail:
            t[2] -= 10
        self.trail = [t for t in self.trail if t[2] > 0]

        # 更新吸积盘粒子
        for p in self.disk_particles:
            p['angle'] += p['speed']
            p['dist'] = self.radius * (0.3 + random.uniform(-0.05, 0.05))

    def draw(self, surface, cam_x, cam_y):
        if not self.alive: return
        sx = self.x - cam_x
        sy = self.y - cam_y
        if sx < -self.radius or sx > WINDOW_WIDTH + self.radius or sy < -self.radius or sy > WINDOW_HEIGHT + self.radius:
            return

        # 轨迹拖尾
        for t in self.trail:
            alpha = t[2]
            tx = t[0] - cam_x
            ty = t[1] - cam_y
            pygame.draw.circle(surface, (*self.glow_color, alpha),
                               (int(tx), int(ty)), int(self.radius * 0.4))

        # 光环
        for i in range(3):
            r = self.radius - i * 5
            if r > 0:
                pygame.draw.circle(surface, self.glow_color, (int(sx), int(sy)), int(r), 2)

        # 吸积盘粒子
        for p in self.disk_particles:
            px = sx + math.cos(p['angle']) * p['dist']
            py = sy + math.sin(p['angle']) * p['dist']
            pygame.draw.circle(surface, self.glow_color, (int(px), int(py)), p['size'])

        # 核心
        pygame.draw.circle(surface, HOLE_CORE, (int(sx), int(sy)), int(self.radius * 0.8))
        pygame.draw.circle(surface, self.glow_color, (int(sx), int(sy)), int(self.radius * 0.4), 2)
        pygame.draw.circle(surface, WHITE, (int(sx), int(sy)), int(self.radius * 0.2))

    def grow(self, amount):
        self.radius += amount
        # 更新吸积盘粒子范围
        for p in self.disk_particles:
            p['dist'] = self.radius * random.uniform(0.3, 1.0)

# ========== 可吞噬水晶球 ==========
class Crystal:
    def __init__(self, x, y, radius, color):
        self.x = x
        self.y = y
        self.radius = radius
        self.color = color
        self.alive = True
        self.pulse = random.uniform(0, 2 * math.pi)

    def draw(self, surface, cam_x, cam_y):
        if not self.alive: return
        sx = self.x - cam_x
        sy = self.y - cam_y
        if sx < -self.radius or sx > WINDOW_WIDTH + self.radius or sy < -self.radius or sy > WINDOW_HEIGHT + self.radius:
            return
        # 脉动效果
        pulse_radius = self.radius + int(math.sin(self.pulse + pygame.time.get_ticks() * 0.005) * 2)
        # 光晕
        for r in range(pulse_radius + 4, pulse_radius, -2):
            alpha = 30
            pygame.draw.circle(surface, (*self.color, alpha), (int(sx), int(sy)), r)
        # 主体
        pygame.draw.circle(surface, self.color, (int(sx), int(sy)), pulse_radius)
        # 高光
        pygame.draw.circle(surface, WHITE, (int(sx - pulse_radius*0.3), int(sy - pulse_radius*0.3)), int(pulse_radius*0.3))
        # 内部闪光
        inner_alpha = int(100 + 50 * math.sin(pygame.time.get_ticks() * 0.01))
        pygame.draw.circle(surface, (255, 255, 255, inner_alpha), (int(sx), int(sy)), int(pulse_radius*0.4))

# ========== 游戏状态 ==========
class Game:
    def __init__(self):
        # 星云背景
        self.nebulae = [Nebula(random.randint(0, WORLD_WIDTH), random.randint(0, WORLD_HEIGHT), random.randint(100, 300), random.choice(NEBULA_COLORS)) for _ in range(8)]
        self.reset()

    def reset(self):
        self.player = BlackHole(WORLD_WIDTH//2, WORLD_HEIGHT//2, 18, is_player=True)
        self.player.glow_color = (255, 200, 50)
        self.ai_holes = [BlackHole(random.randint(100, WORLD_WIDTH-100), random.randint(100, WORLD_HEIGHT-100), random.randint(15, 35)) for _ in range(10)]
        self.crystals = []
        for _ in range(70):
            self.spawn_crystal()
        self.score = 0
        self.game_over = False
        # 技能：冲刺
        self.boost_cooldown = 0
        self.boost_active = False
        self.boost_duration = 0

    def spawn_crystal(self):
        x = random.randint(50, WORLD_WIDTH-50)
        y = random.randint(50, WORLD_HEIGHT-50)
        radius = random.randint(8, 22)
        color = random.choice(SPHERE_COLORS)
        self.crystals.append(Crystal(x, y, radius, color))

    def update(self, dt):
        if self.game_over:
            return

        # 玩家控制：鼠标移动 + 冲刺技能
        mouse_x, mouse_y = pygame.mouse.get_pos()
        cam_x = self.player.x - WINDOW_WIDTH / 2
        cam_y = self.player.y - WINDOW_HEIGHT / 2
        cam_x = max(0, min(WORLD_WIDTH - WINDOW_WIDTH, cam_x))
        cam_y = max(0, min(WORLD_HEIGHT - WINDOW_HEIGHT, cam_y))
        target_x = mouse_x + cam_x
        target_y = mouse_y + cam_y
        dx = target_x - self.player.x
        dy = target_y - self.player.y
        dist = math.hypot(dx, dy)
        move_speed = 5 * dt * 60
        if self.boost_active:
            move_speed *= 2.5
            self.boost_duration -= 1
            if self.boost_duration <= 0:
                self.boost_active = False
        if dist > 2:
            self.player.x += (dx / dist) * move_speed
            self.player.y += (dy / dist) * move_speed
        self.player.x = max(self.player.radius, min(WORLD_WIDTH - self.player.radius, self.player.x))
        self.player.y = max(self.player.radius, min(WORLD_HEIGHT - self.player.radius, self.player.y))

        # 冲刺技能冷却
        if self.boost_cooldown > 0:
            self.boost_cooldown -= 1
        keys = pygame.key.get_pressed()
        if keys[pygame.K_SPACE] and self.boost_cooldown <= 0 and not self.boost_active:
            self.boost_active = True
            self.boost_duration = 30
            self.boost_cooldown = 120
            # 消耗一定体积
            self.player.grow(-3)
            if self.player.radius < 5:
                self.game_over = True
                return

        # 更新AI
        for hole in self.ai_holes:
            hole.update(dt)
        # 更新玩家自身
        self.player.update(dt)

        # 吞噬水晶
        for crystal in self.crystals[:]:
            if not crystal.alive: continue
            dist = math.hypot(self.player.x - crystal.x, self.player.y - crystal.y)
            if dist < self.player.radius:
                crystal.alive = False
                self.crystals.remove(crystal)
                self.player.grow(0.9)
                self.score += 5
                self.spawn_crystal()
        for hole in self.ai_holes:
            if not hole.alive: continue
            for crystal in self.crystals[:]:
                if not crystal.alive: continue
                dist = math.hypot(hole.x - crystal.x, hole.y - crystal.y)
                if dist < hole.radius:
                    crystal.alive = False
                    self.crystals.remove(crystal)
                    hole.grow(0.6)
                    self.spawn_crystal()

        # 黑洞互吞
        all_holes = [self.player] + self.ai_holes
        for i, h1 in enumerate(all_holes):
            if not h1.alive: continue
            for j, h2 in enumerate(all_holes):
                if i >= j or not h2.alive: continue
                dist = math.hypot(h1.x - h2.x, h1.y - h2.y)
                if dist < max(h1.radius, h2.radius) * 0.8:
                    if h1.radius > h2.radius:
                        h2.alive = False
                        h1.grow(h2.radius * 0.5)
                        if h2.is_player:
                            self.game_over = True
                            return
                    else:
                        h1.alive = False
                        h2.grow(h1.radius * 0.5)
                        if h1.is_player:
                            self.game_over = True
                            return

        self.ai_holes = [h for h in self.ai_holes if h.alive]
        while len(self.ai_holes) < 10:
            self.ai_holes.append(BlackHole(random.randint(100, WORLD_WIDTH-100), random.randint(100, WORLD_HEIGHT-100), random.randint(15, 30)))
        while len(self.crystals) < 70:
            self.spawn_crystal()

    def draw(self):
        cam_x = self.player.x - WINDOW_WIDTH / 2
        cam_y = self.player.y - WINDOW_HEIGHT / 2
        cam_x = max(0, min(WORLD_WIDTH - WINDOW_WIDTH, cam_x))
        cam_y = max(0, min(WORLD_HEIGHT - WINDOW_HEIGHT, cam_y))

        screen.fill(BLACK)
        # 绘制星云
        for nebula in self.nebulae:
            nebula.draw(screen, cam_x, cam_y)

        # 绘制水晶
        for crystal in self.crystals:
            crystal.draw(screen, cam_x, cam_y)
        # 绘制AI黑洞
        for hole in self.ai_holes:
            hole.draw(screen, cam_x, cam_y)
        # 绘制玩家黑洞
        self.player.draw(screen, cam_x, cam_y)

        # UI
        score_text = font_med.render(f"Score: {self.score}", True, WHITE)
        screen.blit(score_text, (20, 20))
        size_text = font_small.render(f"Size: {self.player.radius:.0f}", True, self.player.glow_color)
        screen.blit(size_text, (20, 60))
        # 冲刺技能图标
        if self.boost_cooldown > 0:
            cd_text = font_small.render(f"Boost: {self.boost_cooldown//60}s", True, (150, 150, 150))
        else:
            cd_text = font_small.render("Boost: Ready (SPACE)", True, (0, 255, 0))
        screen.blit(cd_text, (20, 90))

        if self.game_over:
            overlay = pygame.Surface((WINDOW_WIDTH, WINDOW_HEIGHT), pygame.SRCALPHA)
            overlay.fill((0,0,0, 200))
            screen.blit(overlay, (0,0))
            over_text = font_large.render("YOU WERE SWALLOWED", True, (255, 50, 50))
            screen.blit(over_text, (WINDOW_WIDTH//2 - over_text.get_width()//2, WINDOW_HEIGHT//2 - 30))
            restart_text = font_med.render("Press R to restart", True, WHITE)
            screen.blit(restart_text, (WINDOW_WIDTH//2 - restart_text.get_width()//2, WINDOW_HEIGHT//2 + 30))

# ========== 主循环 ==========
def main():
    game = Game()
    running = True
    while running:
        dt = clock.tick(60) / 1000.0
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_r and game.game_over:
                    game.reset()
        game.update(dt)
        game.draw()
        pygame.display.flip()
    pygame.quit()
    sys.exit()

if __name__ == "__main__":
    main()