Загрузка данных


import pygame
import random
import math

pygame.init()

WIDTH = 800
HEIGHT = 650
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("CYBER VOID // NEON OVERDRIVE")
clock = pygame.time.Clock()
FPS = 60

# Неоновая палитра
BG_DARK = (8, 9, 18)
NEON_CYAN = (0, 245, 255)
NEON_PINK = (255, 20, 147)
NEON_YELLOW = (255, 230, 60)
NEON_ORANGE = (255, 120, 30)
NEON_PURPLE = (180, 50, 255)
NEON_GREEN = (50, 255, 140)
WHITE = (255, 255, 255)

font_sm = pygame.font.SysFont("consolas", 18, bold=True)
font_md = pygame.font.SysFont("consolas", 26, bold=True)
font_lg = pygame.font.SysFont("consolas", 52, bold=True)

def draw_glow_circle(surf, color, center, radius, glow_factor=3):
    glow_surf = pygame.Surface((radius * glow_factor * 2, radius * glow_factor * 2), pygame.SRCALPHA)
    pygame.draw.circle(glow_surf, (*color, 45), (radius * glow_factor, radius * glow_factor), radius * glow_factor)
    pygame.draw.circle(glow_surf, (*color, 120), (radius * glow_factor, radius * glow_factor), int(radius * 1.5))
    pygame.draw.circle(glow_surf, color, (radius * glow_factor, radius * glow_factor), radius)
    surf.blit(glow_surf, (center[0] - radius * glow_factor, center[1] - radius * glow_factor), special_flags=pygame.BLEND_ADD)

class DamageText:
    def __init__(self, x, y, text, color=WHITE):
        self.x = x + random.randint(-12, 12)
        self.y = y
        self.text = str(text)
        self.color = color
        self.life = 35

    def update(self):
        self.y -= 1.2
        self.life -= 1

    def draw(self, surf):
        alpha = min(255, self.life * 8)
        rendered = font_sm.render(self.text, True, self.color)
        rendered.set_alpha(alpha)
        surf.blit(rendered, (self.x, self.y))

class Player:
    def __init__(self):
        self.x = WIDTH // 2
        self.y = HEIGHT - 90
        self.speed = 7
        self.cooldown = 0
        self.shield = 0
        self.triple_timer = 0
        self.bombs = 2
        self.trail = []

    def update(self, keys):
        moved = False
        if (keys[pygame.K_LEFT] or keys[pygame.K_a]) and self.x > 30:
            self.x -= self.speed
            moved = True
        if (keys[pygame.K_RIGHT] or keys[pygame.K_d]) and self.x < WIDTH - 30:
            self.x += self.speed
            moved = True
        if (keys[pygame.K_UP] or keys[pygame.K_w]) and self.y > 40:
            self.y -= self.speed
            moved = True
        if (keys[pygame.K_DOWN] or keys[pygame.K_s]) and self.y < HEIGHT - 40:
            self.y += self.speed
            moved = True

        # След реактивного сопла
        self.trail.append((self.x, self.y + 18, 14))
        for i in range(len(self.trail) - 1, -1, -1):
            x, y, r = self.trail[i]
            if r <= 1:
                self.trail.pop(i)
            else:
                self.trail[i] = (x + random.uniform(-1, 1), y + 3, r - 1.2)

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

    def draw(self, surf):
        # Двигатели
        for x, y, r in self.trail:
            pygame.draw.circle(surf, NEON_ORANGE, (int(x), int(y)), int(r))

        # Корпус истребителя
        pts = [(self.x, self.y - 24), (self.x - 22, self.y + 18), (self.x, self.y + 8), (self.x + 22, self.y + 18)]
        glow_pts = [(self.x, self.y - 27), (self.x - 25, self.y + 21), (self.x, self.y + 10), (self.x + 25, self.y + 21)]
        pygame.draw.polygon(surf, (*NEON_CYAN, 70), glow_pts)
        pygame.draw.polygon(surf, NEON_CYAN, pts, 3)
        pygame.draw.line(surf, WHITE, (self.x, self.y - 18), (self.x, self.y + 6), 2)

        # Силовой щит
        if self.shield > 0:
            draw_glow_circle(surf, NEON_PURPLE, (int(self.x), int(self.y)), 32, glow_factor=2)

class Bullet:
    def __init__(self, x, y, dx=0, dy=-14, color=NEON_YELLOW, is_enemy=False):
        self.x = x
        self.y = y
        self.dx = dx
        self.dy = dy
        self.color = color
        self.is_enemy = is_enemy
        self.radius = 5 if is_enemy else 4

    def update(self):
        self.x += self.dx
        self.y += self.dy

    def draw(self, surf):
        pygame.draw.circle(surf, self.color, (int(self.x), int(self.y)), self.radius)
        trail_start = (int(self.x - self.dx * 1.5), int(self.y - self.dy * 1.5))
        pygame.draw.line(surf, self.color, (int(self.x), int(self.y)), trail_start, 3)

class PowerUp:
    def __init__(self, x, y):
        self.x = x
        self.y = y
        self.type = random.choice(["triple", "shield", "bomb"])
        self.color = NEON_GREEN if self.type == "triple" else (NEON_PURPLE if self.type == "shield" else NEON_ORANGE)
        self.radius = 12
        self.pulse = 0

    def update(self):
        self.y += 2
        self.pulse += 0.1

    def draw(self, surf):
        r = int(self.radius + math.sin(self.pulse) * 3)
        draw_glow_circle(surf, self.color, (int(self.x), int(self.y)), r, glow_factor=2)
        letter = "3" if self.type == "triple" else ("S" if self.type == "shield" else "B")
        txt = font_sm.render(letter, True, WHITE)
        surf.blit(txt, (self.x - txt.get_width() // 2, self.y - txt.get_height() // 2))

class DroneEnemy:
    def __init__(self, speed_mult):
        self.x = random.randint(40, WIDTH - 40)
        self.y = -30
        self.speed = random.uniform(2.5, 4.2) * speed_mult
        self.radius = 18
        self.hp = 2
        self.rot = 0

    def update(self):
        self.y += self.speed
        self.rot += 0.08

    def draw(self, surf):
        pts = [
            (self.x + math.cos(self.rot + i * math.pi / 2) * self.radius,
             self.y + math.sin(self.rot + i * math.pi / 2) * self.radius)
            for i in range(4)
        ]
        pygame.draw.polygon(surf, NEON_PINK, pts, 2)
        pygame.draw.circle(surf, WHITE, (int(self.x), int(self.y)), 3)

# БОСС 1: Тяжёлый разрушитель (Стреляет веерными залпами)
class DestroyerBoss:
    def __init__(self):
        self.x = WIDTH // 2
        self.y = -100
        self.target_y = 110
        self.max_hp = 80
        self.hp = self.max_hp
        self.radius = 50
        self.timer = 0
        self.dir = 2.5
        self.name = "CRUISER PROTO-9"

    def update(self, bullets):
        if self.y < self.target_y:
            self.y += 2
        else:
            self.x += self.dir
            if self.x < 100 or self.x > WIDTH - 100:
                self.dir = -self.dir
            self.timer += 1
            if self.timer % 40 == 0:
                for a in [-0.5, -0.25, 0, 0.25, 0.5]:
                    bullets.append(Bullet(self.x, self.y + 40, math.sin(a) * 6, math.cos(a) * 6, NEON_PINK, True))

    def draw(self, surf):
        pts = [(self.x, self.y + 45), (self.x - 55, self.y - 30), (self.x, self.y - 15), (self.x + 55, self.y - 30)]
        pygame.draw.polygon(surf, NEON_PINK, pts, 3)
        draw_glow_circle(surf, NEON_ORANGE, (int(self.x), int(self.y)), 16, glow_factor=2)

# БОСС 2: Снайпер-винг (Быстрый, круговая спиральная атака)
class DreadnoughtBoss:
    def __init__(self):
        self.x = WIDTH // 2
        self.y = -120
        self.target_y = 130
        self.max_hp = 130
        self.hp = self.max_hp
        self.radius = 60
        self.timer = 0
        self.spiral_angle = 0
        self.name = "CYBER DREADNOUGHT"

    def update(self, bullets):
        if self.y < self.target_y:
            self.y += 2
        else:
            self.timer += 1
            self.x = WIDTH // 2 + math.sin(self.timer * 0.03) * 220
            # Спиральные залпы
            if self.timer % 7 == 0:
                self.spiral_angle += 0.4
                vx = math.cos(self.spiral_angle) * 5
                vy = math.sin(self.spiral_angle) * 5
                bullets.append(Bullet(self.x, self.y, vx, vy, NEON_PURPLE, True))

    def draw(self, surf):
        # Двойное неоновое кольцо с шипами
        pygame.draw.circle(surf, NEON_PURPLE, (int(self.x), int(self.y)), 52, 3)
        pygame.draw.circle(surf, NEON_CYAN, (int(self.x), int(self.y)), 28, 2)
        draw_glow_circle(surf, WHITE, (int(self.x), int(self.y)), 12, glow_factor=2)

class Particle:
    def __init__(self, x, y, color):
        self.x = x
        self.y = y
        self.color = color
        angle = random.uniform(0, math.pi * 2)
        speed = random.uniform(2, 7)
        self.vx = math.cos(angle) * speed
        self.vy = math.sin(angle) * speed
        self.life = random.randint(18, 30)

    def update(self):
        self.x += self.vx
        self.y += self.vy
        self.life -= 1

    def draw(self, surf):
        if self.life > 0:
            pygame.draw.circle(surf, self.color, (int(self.x), int(self.y)), max(1, self.life // 6))

def main():
    player = Player()
    bullets = []
    enemies = []
    particles = []
    powerups = []
    dmg_texts = []
    boss = None

    boss_sequence = 1
    next_boss_score = 150
    score = 0
    shake = 0
    game_over = False

    # 3-слойный звёздный параллакс
    stars = [
        [random.randint(0, WIDTH), random.randint(0, HEIGHT), random.choice([1, 2, 3])]
        for _ in range(90)
    ]

    running = True
    while running:
        clock.tick(FPS)
        keys = pygame.key.get_pressed()

        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False
            if event.type == pygame.KEYDOWN:
                if game_over and event.key == pygame.K_r:
                    main()
                    return
                # Ульта / EMP Бомба
                if not game_over and event.key == pygame.K_e and player.bombs > 0:
                    player.bombs -= 1
                    shake = 28
                    bullets = [b for b in bullets if not b.is_enemy]
                    for e in enemies:
                        score += 15
                        for _ in range(12):
                            particles.append(Particle(e.x, e.y, NEON_ORANGE))
                    enemies.clear()
                    if boss:
                        boss.hp -= 25
                        dmg_texts.append(DamageText(boss.x, boss.y, 25, NEON_YELLOW))

        if not game_over:
            player.update(keys)

            # Стрельба
            if (keys[pygame.K_SPACE] or keys[pygame.K_j]) and player.cooldown == 0:
                if player.triple_timer > 0:
                    bullets.append(Bullet(player.x, player.y - 20, 0, -15, NEON_YELLOW))
                    bullets.append(Bullet(player.x - 12, player.y - 12, -3.5, -14, NEON_YELLOW))
                    bullets.append(Bullet(player.x + 12, player.y - 12, 3.5, -14, NEON_YELLOW))
                else:
                    bullets.append(Bullet(player.x, player.y - 20, 0, -15, NEON_YELLOW))
                player.cooldown = 10

            # Спавн боссов
            if score >= next_boss_score and boss is None:
                boss = DestroyerBoss() if boss_sequence % 2 != 0 else DreadnoughtBoss()
                next_boss_score += 250
                boss_sequence += 1

            # Спавн обычных мобов
            if boss is None and random.randint(1, 40) == 1:
                enemies.append(DroneEnemy(1.0 + score / 600))

            # Логика пуль
            for b in bullets[:]:
                b.update()
                if b.y < -20 or b.y > HEIGHT + 20 or b.x < -20 or b.x > WIDTH + 20:
                    bullets.remove(b)
                    continue

                if b.is_enemy:
                    if math.hypot(b.x - player.x, b.y - player.y) < 22:
                        bullets.remove(b)
                        if player.shield > 0:
                            player.shield -= 1
                            shake = 8
                        else:
                            game_over = True
                            shake = 30
                else:
                    # Попадание по мобам
                    for e in enemies[:]:
                        if math.hypot(b.x - e.x, b.y - e.y) < e.radius + 6:
                            e.hp -= 1
                            dmg_texts.append(DamageText(e.x, e.y, 1))
                            if b in bullets:
                                bullets.remove(b)
                            if e.hp <= 0:
                                for _ in range(15):
                                    particles.append(Particle(e.x, e.y, NEON_PINK))
                                if random.random() < 0.28:
                                    powerups.append(PowerUp(e.x, e.y))
                                enemies.remove(e)
                                score += 15
                                shake = 4
                            break

                    # Попадание по боссу
                    if boss and b in bullets:
                        if math.hypot(b.x - boss.x, b.y - boss.y) < boss.radius:
                            boss.hp -= 1
                            dmg_texts.append(DamageText(boss.x, boss.y, 1))
                            bullets.remove(b)
                            if boss.hp <= 0:
                                for _ in range(60):
                                    particles.append(Particle(boss.x, boss.y, NEON_ORANGE))
                                score += 200
                                shake = 35
                                boss = None
                            break

            # Обновление босса
            if boss:
                boss.update(bullets)

            # Обновление обычных мобов
            for e in enemies[:]:
                e.update()
                if math.hypot(e.x - player.x, e.y - player.y) < e.radius + 18:
                    if player.shield > 0:
                        player.shield -= 1
                        enemies.remove(e)
                        shake = 10
                    else:
                        game_over = True
                        shake = 30
                elif e.y > HEIGHT + 40:
                    enemies.remove(e)

            # Подбор бонусов
            for p in powerups[:]:
                p.update()
                if math.hypot(p.x - player.x, p.y - player.y) < 32:
                    if p.type == "triple":
                        player.triple_timer = 400
                    elif p.type == "shield":
                        player.shield = 1
                    elif p.type == "bomb":
                        player.bombs = min(3, player.bombs + 1)
                    powerups.remove(p)
                elif p.y > HEIGHT:
                    powerups.remove(p)

        # Тексты урона и частицы
        for t in dmg_texts[:]:
            t.update()
            if t.life <= 0:
                dmg_texts.remove(t)

        for pt in particles[:]:
            pt.update()
            if pt.life <= 0:
                particles.remove(pt)

        # Движение фона
        for s in stars:
            s[1] += s[2] * 0.9
            if s[1] > HEIGHT:
                s[1] = 0
                s[0] = random.randint(0, WIDTH)

        # Виртуальный холст для отрисовки шейка
        canvas = pygame.Surface((WIDTH, HEIGHT))
        canvas.fill(BG_DARK)

        for s in stars:
            col = (100, 110, 150) if s[2] == 1 else (200, 220, 255)
            pygame.draw.circle(canvas, col, (int(s[0]), int(s[1])), s[2] // 2 + 1)

        for pt in particles:
            pt.draw(canvas)
        for p in powerups:
            p.draw(canvas)
        for b in bullets:
            b.draw(canvas)
        for e in enemies:
            e.draw(canvas)
        if boss:
            boss.draw(canvas)
        if not game_over:
            player.draw(canvas)

        for t in dmg_texts:
            t.draw(canvas)

        # Интерфейс
        canvas.blit(font_md.render(f"SCORE: {score}", True, NEON_CYAN), (25, 20))
        canvas.blit(font_sm.render(f"EMP BOMBS [E]: {'■ ' * player.bombs}", True, NEON_ORANGE), (25, 55))
        if player.triple_timer > 0:
            canvas.blit(font_sm.render("TRIPLE SHOT ACTIVE", True, NEON_GREEN), (25, 80))

        # Полоса HP босса сверху
        if boss:
            bar_w = 400
            bar_h = 12
            bx = WIDTH // 2 - bar_w // 2
            by = 25
            ratio = max(0, boss.hp / boss.max_hp)
            pygame.draw.rect(canvas, (40, 20, 40), (bx, by, bar_w, bar_h))
            pygame.draw.rect(canvas, NEON_PINK, (bx, by, int(bar_w * ratio), bar_h))
            pygame.draw.rect(canvas, WHITE, (bx, by, bar_w, bar_h), 1)
            b_txt = font_sm.render(boss.name, True, WHITE)
            canvas.blit(b_txt, (WIDTH // 2 - b_txt.get_width() // 2, by + 16))

        if game_over:
            t1 = font_lg.render("SYSTEM CRITICAL", True, NEON_PINK)
            t2 = font_md.render("PRESS 'R' TO REBOOT", True, WHITE)
            canvas.blit(t1, (WIDTH // 2 - t1.get_width() // 2, HEIGHT // 2 - 40))
            canvas.blit(t2, (WIDTH // 2 - t2.get_width() // 2, HEIGHT // 2 + 30))

        offset = [0, 0]
        if shake > 0:
            offset = [random.randint(-shake, shake), random.randint(-shake, shake)]
            shake = max(0, shake - 2)

        screen.blit(canvas, offset)
        pygame.display.flip()

    pygame.quit()

if __name__ == "__main__":
    main()