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


import pygame
import pygame.gfxdraw
import random
import math
import sys

pygame.init()

# --- Размеры экрана и сетки ---
WIDTH, HEIGHT = 1100, 650
GRID_COLS, GRID_ROWS = 9, 5
CELL_W, CELL_H = 88, 92
GRID_X, GRID_Y = 250, 140

FPS = 60
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Plants vs. Zombies - Master Edition")
clock = pygame.time.Clock()

# Шрифты
f_tiny = pygame.font.SysFont("Arial", 12, bold=True)
f_small = pygame.font.SysFont("Arial", 16, bold=True)
f_med = pygame.font.SysFont("Arial", 22, bold=True)
f_big = pygame.font.SysFont("Arial", 44, bold=True)

# База данных всех доступных растений
PLANT_DATA = {
    "peashooter": {"cost": 100, "hp": 150, "cd": 120, "name": "Peashooter", "desc": "Shoots peas at zombies"},
    "sunflower":  {"cost": 50,  "hp": 120, "cd": 120, "name": "Sunflower",  "desc": "Generates extra sun"},
    "cherrybomb": {"cost": 150, "hp": 1000,"cd": 600, "name": "Cherry Bomb","desc": "Explodes in 3x3 area"},
    "wallnut":    {"cost": 50,  "hp": 500, "cd": 400, "name": "Wall-nut",   "desc": "Blocks incoming zombies"},
    "snowpea":    {"cost": 175, "hp": 150, "cd": 150, "name": "Snow Pea",   "desc": "Shoots chilling peas"}
}

# Конфигурация уровней (волны зомби)
LEVELS = [
    {
        "name": "Level 1: Back to the Lawn",
        "sun": 150,
        "waves": [
            ["regular", "regular"],
            ["regular", "conehead"],
            ["regular", "conehead", "regular"]
        ]
    },
    {
        "name": "Level 2: Armored Threat",
        "sun": 200,
        "waves": [
            ["regular", "conehead"],
            ["conehead", "buckethead"],
            ["buckethead", "conehead", "regular", "buckethead"]
        ]
    },
    {
        "name": "Level 3: Gargantuar's Wrath (BOSS)",
        "sun": 300,
        "waves": [
            ["buckethead", "conehead", "buckethead"],
            ["gargantuar"],
            ["gargantuar", "buckethead", "conehead"]
        ]
    }
]

# --- Функции качественной отрисовки ---
def draw_smooth_circle(surf, x, y, r, color):
    pygame.gfxdraw.aacircle(surf, int(x), int(y), int(r), color)
    pygame.gfxdraw.filled_circle(surf, int(x), int(y), int(r), color)

def draw_smooth_ellipse(surf, rect, color):
    pygame.draw.ellipse(surf, color, rect)

# --- Игровые сущности ---
class Particle:
    def __init__(self, x, y, color, size, vx, vy, life):
        self.x, self.y = x, y
        self.color = color
        self.size = size
        self.vx, self.vy = vx, vy
        self.life = life
        self.max_life = life

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

    def draw(self, surf):
        if self.life > 0:
            alpha = int((self.life / self.max_life) * 255)
            s = pygame.Surface((self.size * 2, self.size * 2), pygame.SRCALPHA)
            pygame.draw.circle(s, (*self.color, alpha), (self.size, self.size), self.size)
            surf.blit(s, (self.x - self.size, self.y - self.size))

class Lawnmower:
    def __init__(self, row):
        self.row = row
        self.x = GRID_X - 60
        self.y = GRID_Y + row * CELL_H + CELL_H // 2
        self.active = False
        self.used = False

    def update(self, zombies):
        if self.active:
            self.x += 10
            for z in zombies:
                if z.row == self.row and abs(z.x - self.x) < 40:
                    z.take_damage(9999)
            if self.x > WIDTH + 50:
                self.used = True
        else:
            for z in zombies:
                if z.row == self.row and z.x <= self.x + 30:
                    self.active = True
                    break

    def draw(self, surf):
        if self.used: return
        x, y = int(self.x), int(self.y)
        pygame.draw.rect(surf, (180, 20, 20), (x - 20, y - 15, 35, 25), border_radius=4)
        pygame.draw.circle(surf, (40, 40, 40), (x - 12, y + 12), 8)
        pygame.draw.circle(surf, (40, 40, 40), (x + 10, y + 12), 8)
        pygame.draw.line(surf, (200, 200, 200), (x - 15, y - 10), (x - 25, y - 30), 4)

class Sun:
    def __init__(self, x, y, target_y=None):
        self.x, self.y = x, y
        self.target_y = target_y if target_y else y
        self.radius = 22
        self.alive = True
        self.timer = 0
        self.pulse = 0.0

    def update(self):
        if self.y < self.target_y:
            self.y += 2
        self.timer += 1
        self.pulse += 0.08
        if self.timer > 650:
            self.alive = False

    def draw(self, surf):
        r = self.radius + math.sin(self.pulse) * 3
        cx, cy = int(self.x), int(self.y)
        # Лучи
        for i in range(8):
            ang = self.pulse + i * (math.pi / 4)
            px = cx + math.cos(ang) * (r + 8)
            py = cy + math.sin(ang) * (r + 8)
            pygame.draw.circle(surf, (255, 180, 0), (int(px), int(py)), 6)
        draw_smooth_circle(surf, cx, cy, r + 2, (255, 140, 0))
        draw_smooth_circle(surf, cx, cy, r, (255, 225, 30))
        draw_smooth_circle(surf, cx - 4, cy - 4, 3, (255, 255, 255))

class Projectile:
    def __init__(self, x, y, row, is_ice=False):
        self.x, self.y = x, y
        self.row = row
        self.is_ice = is_ice
        self.speed = 7
        self.damage = 25
        self.alive = True

    def update(self):
        self.x += self.speed
        if self.x > WIDTH:
            self.alive = False

    def draw(self, surf):
        color = (60, 200, 255) if self.is_ice else (50, 205, 50)
        draw_smooth_circle(surf, self.x, self.y, 8, color)
        draw_smooth_circle(surf, self.x - 2, self.y - 2, 3, (255, 255, 255))

class Plant:
    def __init__(self, col, row, p_type):
        self.col, self.row = col, row
        self.type = p_type
        self.data = PLANT_DATA[p_type]
        self.hp = self.data["hp"]
        self.max_hp = self.hp
        self.x = GRID_X + col * CELL_W + CELL_W // 2
        self.y = GRID_Y + row * CELL_H + CELL_H // 2
        self.action_timer = 0
        self.anim_phase = random.random() * 6.0
        self.alive = True

    def update(self, game):
        self.anim_phase += 0.05
        self.action_timer += 1

        if self.type == "sunflower" and self.action_timer >= 450:
            game.suns.append(Sun(self.x + random.randint(-15, 15), self.y - 15))
            self.action_timer = 0

        elif self.type in ["peashooter", "snowpea"]:
            # Проверяем наличие зомби впереди на той же линии
            has_targets = any(z.row == self.row and z.x > self.x for z in game.zombies)
            if has_targets and self.action_timer >= 90:
                is_ice = (self.type == "snowpea")
                game.projectiles.append(Projectile(self.x + 24, self.y - 8, self.row, is_ice))
                self.action_timer = 0

        elif self.type == "cherrybomb":
            if self.action_timer >= 120:  # Взрыв через 2 секунды
                self.alive = False
                game.spawn_explosion(self.x, self.y, self.col, self.row)

    def draw(self, surf):
        x, y = self.x, self.y + int(math.sin(self.anim_phase) * 2)

        # Стебель и базовые листья у земли
        pygame.draw.ellipse(surf, (34, 120, 34), (x - 20, y + 18, 18, 10))
        pygame.draw.ellipse(surf, (34, 120, 34), (x + 2, y + 18, 18, 10))
        pygame.draw.line(surf, (34, 139, 34), (x, y + 20), (x, y), 6)

        if self.type in ["peashooter", "snowpea"]:
            head_col = (100, 200, 255) if self.type == "snowpea" else (50, 180, 50)
            # Голова
            draw_smooth_circle(surf, x, y - 8, 18, head_col)
            # Ствол (раструб)
            pygame.draw.rect(surf, head_col, (x + 6, y - 15, 18, 14), border_radius=4)
            draw_smooth_circle(surf, x + 24, y - 8, 7, (20, 80, 20))
            # Глаза
            draw_smooth_circle(surf, x + 4, y - 14, 4, (255, 255, 255))
            draw_smooth_circle(surf, x + 6, y - 14, 2, (0, 0, 0))
            if self.type == "snowpea":
                # Ледяные кристаллы сзади
                for angle in [-20, 0, 20]:
                    draw_smooth_circle(surf, x - 14 + angle//2, y - 14 + angle, 5, (180, 235, 255))

        elif self.type == "sunflower":
            # Лепестки
            for i in range(12):
                a = self.anim_phase * 0.5 + i * (math.pi / 6)
                lx = x + math.cos(a) * 20
                ly = (y - 8) + math.sin(a) * 20
                draw_smooth_circle(surf, lx, ly, 7, (255, 200, 0))
            # Сердцевина
            draw_smooth_circle(surf, x, y - 8, 15, (139, 69, 19))
            # Улыбающееся лицо
            draw_smooth_circle(surf, x - 4, y - 12, 3, (0, 0, 0))
            draw_smooth_circle(surf, x + 4, y - 12, 3, (0, 0, 0))
            pygame.draw.arc(surf, (0, 0, 0), (x - 6, y - 10, 12, 8), math.pi, 2 * math.pi, 2)

        elif self.type == "wallnut":
            # Стенорех
            pygame.draw.ellipse(surf, (160, 82, 45), (x - 18, y - 26, 36, 52))
            draw_smooth_circle(surf, x - 6, y - 12, 5, (255, 255, 255))
            draw_smooth_circle(surf, x + 6, y - 12, 5, (255, 255, 255))
            draw_smooth_circle(surf, x - 4, y - 12, 2, (0, 0, 0))
            draw_smooth_circle(surf, x + 4, y - 12, 2, (0, 0, 0))
            # Трещины при получении урона
            if self.hp < self.max_hp * 0.6:
                pygame.draw.line(surf, (80, 40, 20), (x - 6, y), (x + 2, y + 8), 2)
            if self.hp < self.max_hp * 0.3:
                pygame.draw.line(surf, (80, 40, 20), (x + 4, y - 4), (x + 10, y + 6), 2)

        elif self.type == "cherrybomb":
            scale = 1.0 + (self.action_timer / 120) * 0.3
            r = int(14 * scale)
            c1_x, c1_y = x - 10, y
            c2_x, c2_y = x + 10, y - 4
            # Стебель
            pygame.draw.line(surf, (0, 100, 0), (c1_x, c1_y), (x, y - 24), 3)
            pygame.draw.line(surf, (0, 100, 0), (c2_x, c2_y), (x, y - 24), 3)
            # Вишни
            draw_smooth_circle(surf, c1_x, c1_y, r, (200, 20, 20))
            draw_smooth_circle(surf, c2_x, c2_y, r, (220, 20, 20))

class Zombie:
    def __init__(self, row, z_type="regular"):
        self.row = row
        self.type = z_type
        self.x = WIDTH + random.randint(20, 80)
        self.y = GRID_Y + row * CELL_H + CELL_H // 2
        self.eating = False
        self.alive = True
        self.frozen_timer = 0
        self.walk_cycle = 0.0

        if z_type == "regular":
            self.hp = 120
            self.speed = 0.35
            self.damage = 0.6
        elif z_type == "conehead":
            self.hp = 280
            self.speed = 0.35
            self.damage = 0.6
        elif z_type == "buckethead":
            self.hp = 550
            self.speed = 0.35
            self.damage = 0.6
        elif z_type == "gargantuar":
            self.hp = 1600
            self.speed = 0.22
            self.damage = 999  # Сминает растение сразу

    def take_damage(self, dmg, freeze=False):
        self.hp -= dmg
        if freeze:
            self.frozen_timer = 180
        if self.hp <= 0:
            self.alive = False

    def update(self, plants):
        if self.frozen_timer > 0:
            self.frozen_timer -= 1
            spd = self.speed * 0.5
        else:
            spd = self.speed

        self.eating = False
        self.walk_cycle += 0.06

        # Поиск растений для атаки
        for plant in plants:
            if plant.row == self.row and abs((plant.x + 10) - self.x) < 25:
                self.eating = True
                plant.hp -= self.damage
                break

        if not self.eating:
            self.x -= spd

    def draw(self, surf):
        x = int(self.x)
        y = int(self.y)
        wobble = math.sin(self.walk_cycle) * 3

        tint = (130, 180, 255) if self.frozen_timer > 0 else (120, 150, 120)

        if self.type == "gargantuar":
            # Гигант Гаргантюа
            gy = y - 20
            pygame.draw.rect(surf, (60, 50, 40), (x - 25, gy - 20, 50, 65), border_radius=6) # Тело
            draw_smooth_circle(surf, x, gy - 40, 26, tint) # Огромная голова
            draw_smooth_circle(surf, x - 8, gy - 44, 4, (255, 0, 0))
            # Телеграфный столб/дубина в руках
            pygame.draw.rect(surf, (80, 40, 15), (x - 45, gy - 60, 16, 90), border_radius=3)
        else:
            # Обычные зомби и в броне
            # Ноги
            pygame.draw.line(surf, (40, 40, 80), (x - 5, y + 20), (x - 10 + int(wobble), y + 38), 6)
            pygame.draw.line(surf, (40, 40, 80), (x + 5, y + 20), (x + 8 - int(wobble), y + 38), 6)
            # Рубашка/пиджак
            pygame.draw.rect(surf, (70, 60, 50), (x - 12, y - 10, 24, 32), border_radius=4)
            # Руки вытянуты вперед
            pygame.draw.line(surf, tint, (x - 5, y - 4), (x - 22, y - 4 + int(wobble)), 5)
            # Голова
            draw_smooth_circle(surf, x, y - 24, 15, tint)
            # Красные глаза
            draw_smooth_circle(surf, x - 5, y - 26, 3, (255, 0, 0))
            draw_smooth_circle(surf, x + 3, y - 26, 3, (255, 0, 0))

            # Головные уборы
            if self.type == "conehead":
                points = [(x - 12, y - 32), (x + 12, y - 32), (x, y - 62)]
                pygame.draw.polygon(surf, (255, 140, 0), points)
            elif self.type == "buckethead":
                pygame.draw.rect(surf, (160, 160, 170), (x - 12, y - 46, 24, 22), border_radius=2)

# --- Главный Контроллер Игры ---
class GameManager:
    def __init__(self):
        self.state = "MENU"  # MENU, CHOOSER, PLAY, PAUSE, WIN, GAMEOVER
        self.current_level_idx = 0
        self.sun = 150
        self.selected_seed = None
        self.all_seeds = list(PLANT_DATA.keys())
        self.deck = ["peashooter", "sunflower", "cherrybomb", "wallnut", "snowpea"]
        self.cooldowns = {k: 0 for k in self.all_seeds}

        self.grid = [[None for _ in range(GRID_COLS)] for _ in range(GRID_ROWS)]
        self.plants = []
        self.zombies = []
        self.projectiles = []
        self.suns = []
        self.particles = []
        self.mowers = []

        self.waves_data = []
        self.wave_index = 0
        self.wave_timer = 0
        self.sky_sun_timer = 0

    def start_level(self, idx):
        self.current_level_idx = idx
        lvl = LEVELS[idx]
        self.sun = lvl["sun"]
        self.grid = [[None for _ in range(GRID_COLS)] for _ in range(GRID_ROWS)]
        self.plants.clear()
        self.zombies.clear()
        self.projectiles.clear()
        self.suns.clear()
        self.particles.clear()
        self.mowers = [Lawnmower(r) for r in range(GRID_ROWS)]
        self.waves_data = lvl["waves"]
        self.wave_index = 0
        self.wave_timer = 300 # Пауза перед первой волной
        self.cooldowns = {k: 0 for k in self.all_seeds}
        self.selected_seed = None
        self.state = "PLAY"

    def spawn_explosion(self, cx, cy, c_col, c_row):
        # Взрыв в радиусе 3x3 клетки
        for r in range(max(0, c_row - 1), min(GRID_ROWS, c_row + 2)):
            for z in self.zombies:
                if z.row == r and abs(z.x - cx) < CELL_W * 1.5:
                    z.take_damage(1800)

        # Частицы эффекта
        for _ in range(40):
            ang = random.uniform(0, math.pi * 2)
            spd = random.uniform(2, 8)
            col = random.choice([(255, 50, 0), (255, 180, 0), (80, 80, 80)])
            self.particles.append(Particle(cx, cy, col, random.randint(4, 8), math.cos(ang) * spd, math.sin(ang) * spd, 35))

    def handle_click(self, pos):
        # 1. Сбор солнца
        for s in self.suns:
            if math.hypot(pos[0] - s.x, pos[1] - s.y) <= s.radius + 6:
                self.sun += 25
                s.alive = False
                return

        # 2. Меню Магазина / Колоды
        if self.state == "CHOOSER":
            # Кнопка 'Let's Rock'
            if 480 <= pos[0] <= 620 and 560 <= pos[1] <= 610:
                if len(self.deck) > 0:
                    self.start_level(self.current_level_idx)
                return

            # Выбор карт в колоду
            for idx, p_type in enumerate(self.all_seeds):
                card_r = pygame.Rect(180 + (idx % 4) * 110, 200 + (idx // 4) * 130, 95, 115)
                if card_r.collidepoint(pos):
                    if p_type in self.deck:
                        self.deck.remove(p_type)
                    elif len(self.deck) < 5:
                        self.deck.append(p_type)
            return

        # 3. Карточки верхней панели
        if self.state == "PLAY":
            for idx, p_type in enumerate(self.deck):
                card_r = pygame.Rect(100 + idx * 75, 15, 68, 88)
                if card_r.collidepoint(pos):
                    cost = PLANT_DATA[p_type]["cost"]
                    if self.sun >= cost and self.cooldowns[p_type] == 0:
                        self.selected_seed = p_type
                    return

            # Высадка на поле
            if GRID_X <= pos[0] < GRID_X + GRID_COLS * CELL_W and GRID_Y <= pos[1] < GRID_Y + GRID_ROWS * CELL_H:
                col = (pos[0] - GRID_X) // CELL_W
                row = (pos[1] - GRID_Y) // CELL_H

                if self.selected_seed and self.grid[row][col] is None:
                    cost = PLANT_DATA[self.selected_seed]["cost"]
                    p = Plant(col, row, self.selected_seed)
                    self.plants.append(p)
                    self.grid[row][col] = p
                    self.sun -= cost
                    self.cooldowns[self.selected_seed] = PLANT_DATA[self.selected_seed]["cd"]
                    self.selected_seed = None

    def update(self):
        if self.state != "PLAY":
            return

        # Перезарядка карточек
        for k in self.cooldowns:
            if self.cooldowns[k] > 0:
                self.cooldowns[k] -= 1

        # Естественный спавн солнца
        self.sky_sun_timer += 1
        if self.sky_sun_timer >= 320:
            tx = random.randint(GRID_X, GRID_X + GRID_COLS * CELL_W - 40)
            ty = random.randint(GRID_Y + 50, HEIGHT - 80)
            self.suns.append(Sun(tx, 0, ty))
            self.sky_sun_timer = 0

        # Управление волнами зомби
        self.wave_timer += 1
        if self.wave_timer >= 500:
            if self.wave_index < len(self.waves_data):
                wave = self.waves_data[self.wave_index]
                for z_type in wave:
                    r = random.randint(0, GRID_ROWS - 1)
                    self.zombies.append(Zombie(r, z_type))
                self.wave_index += 1
                self.wave_timer = 0
            elif len(self.zombies) == 0:
                # Все волны побеждены
                if self.current_level_idx + 1 < len(LEVELS):
                    self.current_level_idx += 1
                    self.state = "CHOOSER"
                else:
                    self.state = "WIN"

        # Обновление сущностей
        for p in self.plants: p.update(self)
        for proj in self.projectiles: proj.update()
        for z in self.zombies: z.update(self.plants)
        for s in self.suns: s.update()
        for m in self.mowers: m.update(self.zombies)
        for part in self.particles: part.update()

        # Столкновения: снаряды и зомби
        for proj in self.projectiles:
            for z in self.zombies:
                if proj.row == z.row and abs(proj.x - z.x) < 25:
                    z.take_damage(proj.damage, freeze=proj.is_ice)
                    proj.alive = False
                    for _ in range(4):
                        col = (100, 220, 255) if proj.is_ice else (80, 220, 80)
                        self.particles.append(Particle(proj.x, proj.y, col, 3, random.uniform(-2, 2), random.uniform(-2, 2), 15))
                    break

        # Прорыв обороны зомби
        for z in self.zombies:
            if z.x < GRID_X - 70:
                self.state = "GAMEOVER"

        # Очистка мертвых сущностей
        for p in self.plants:
            if p.hp <= 0 or not p.alive:
                self.grid[p.row][p.col] = None
        self.plants = [p for p in self.plants if p.hp > 0 and p.alive]
        self.projectiles = [p for p in self.projectiles if p.alive]
        self.zombies = [z for z in self.zombies if z.alive]
        self.suns = [s for s in self.suns if s.alive]
        self.particles = [p for p in self.particles if p.life > 0]

    def draw(self, surf):
        # 1. Отрисовка газона
        surf.fill((85, 145, 60))
        for r in range(GRID_ROWS):
            for c in range(GRID_COLS):
                rect = pygame.Rect(GRID_X + c * CELL_W, GRID_Y + r * CELL_H, CELL_W, CELL_H)
                col = (112, 185, 30) if (r + c) % 2 == 0 else (98, 168, 24)
                pygame.draw.rect(surf, col, rect)

        # Разделительные линии
        for r in range(GRID_ROWS + 1):
            pygame.draw.line(surf, (70, 130, 20), (GRID_X, GRID_Y + r * CELL_H), (GRID_X + GRID_COLS * CELL_W, GRID_Y + r * CELL_H), 2)

        # 2. Объекты на поле
        for m in self.mowers: m.draw(surf)
        for p in self.plants: p.draw(surf)
        for z in self.zombies: z.draw(surf)
        for proj in self.projectiles: proj.draw(surf)
        for s in self.suns: s.draw(surf)
        for part in self.particles: part.draw(surf)

        # 3. Верхняя панель (HUD)
        pygame.draw.rect(surf, (110, 75, 45), (0, 0, WIDTH, 110))
        pygame.draw.rect(surf, (60, 40, 20), (0, 105, WIDTH, 5))

        # Счетчик солнца
        pygame.draw.rect(surf, (220, 200, 140), (15, 15, 75, 80), border_radius=6)
        pygame.draw.circle(surf, (255, 190, 0), (52, 40), 16)
        sun_lbl = f_med.render(str(self.sun), True, (50, 30, 0))
        surf.blit(sun_lbl, (52 - sun_lbl.get_width() // 2, 62))

        # Карточки семян
        for idx, p_type in enumerate(self.deck):
            x = 100 + idx * 75
            card_r = pygame.Rect(x, 15, 68, 88)
            cost = PLANT_DATA[p_type]["cost"]
            cd = self.cooldowns[p_type]
            max_cd = PLANT_DATA[p_type]["cd"]

            can_buy = (self.sun >= cost and cd == 0)
            base_col = (205, 185, 135) if can_buy else (130, 120, 105)
            if self.selected_seed == p_type:
                base_col = (255, 255, 120)

            pygame.draw.rect(surf, base_col, card_r, border_radius=5)
            pygame.draw.rect(surf, (50, 30, 10), card_r, 2, border_radius=5)

            name_t = f_tiny.render(p_type[:6].upper(), True, (0, 0, 0))
            cost_t = f_small.render(str(cost), True, (150, 20, 20))
            surf.blit(name_t, (x + 34 - name_t.get_width() // 2, 22))
            surf.blit(cost_t, (x + 34 - cost_t.get_width() // 2, 75))

            # Затемнение отката (cooldown)
            if cd > 0:
                pct = cd / max_cd
                h = int(88 * pct)
                s = pygame.Surface((68, h), pygame.SRCALPHA)
                s.fill((0, 0, 0, 150))
                surf.blit(s, (x, 15 + (88 - h)))

        # Прогресс уровня
        lvl_info = LEVELS[self.current_level_idx]
        title_t = f_small.render(lvl_info["name"], True, (255, 255, 255))
        surf.blit(title_t, (WIDTH - title_t.get_width() - 25, 20))

        # Индикатор волны
        total_w = len(self.waves_data)
        bar_w = 180
        pygame.draw.rect(surf, (40, 40, 40), (WIDTH - bar_w - 25, 50, bar_w, 18), border_radius=4)
        progress = min(1.0, self.wave_index / max(1, total_w))
        pygame.draw.rect(surf, (50, 200, 50), (WIDTH - bar_w - 25, 50, int(bar_w * progress), 18), border_radius=4)
        w_t = f_tiny.render("WAVE PROGRESS", True, (255, 255, 255))
        surf.blit(w_t, (WIDTH - bar_w // 2 - 25 - w_t.get_width() // 2, 53))

        # 4. Экраны состояний
        if self.state == "CHOOSER":
            overlay = pygame.Surface((WIDTH, HEIGHT), pygame.SRCALPHA)
            overlay.fill((0, 0, 0, 190))
            surf.blit(overlay, (0, 0))

            t = f_big.render("CHOOSE YOUR SEEDS", True, (255, 220, 0))
            surf.blit(t, (WIDTH // 2 - t.get_width() // 2, 70))

            for idx, p_type in enumerate(self.all_seeds):
                rx = 180 + (idx % 4) * 110
                ry = 200 + (idx // 4) * 130
                r = pygame.Rect(rx, ry, 95, 115)
                chosen = p_type in self.deck
                col = (180, 240, 180) if chosen else (180, 160, 140)
                pygame.draw.rect(surf, col, r, border_radius=6)
                pygame.draw.rect(surf, (0, 0, 0), r, 2, border_radius=6)

                n = f_small.render(p_type.capitalize(), True, (0, 0, 0))
                c = f_small.render(f"${PLANT_DATA[p_type]['cost']}", True, (150, 0, 0))
                surf.blit(n, (rx + 47 - n.get_width() // 2, ry + 15))
                surf.blit(c, (rx + 47 - c.get_width() // 2, ry + 80))

            # Кнопка 'Let's Rock'
            btn_r = pygame.Rect(480, 560, 140, 50)
            pygame.draw.rect(surf, (40, 160, 40), btn_r, border_radius=8)
            btn_t = f_med.render("LET'S ROCK", True, (255, 255, 255))
            surf.blit(btn_t, (550 - btn_t.get_width() // 2, 572))

        elif self.state == "PAUSE":
            overlay = pygame.Surface((WIDTH, HEIGHT), pygame.SRCALPHA)
            overlay.fill((0, 0, 0, 160))
            surf.blit(overlay, (0, 0))
            t = f_big.render("PAUSED (Press P to resume)", True, (255, 255, 255))
            surf.blit(t, (WIDTH // 2 - t.get_width() // 2, HEIGHT // 2 - 30))

        elif self.state == "GAMEOVER":
            overlay = pygame.Surface((WIDTH, HEIGHT), pygame.SRCALPHA)
            overlay.fill((80, 0, 0, 200))
            surf.blit(overlay, (0, 0))
            t = f_big.render("THE ZOMBIES ATE YOUR BRAINS!", True, (255, 60, 60))
            st = f_med.render("Press R to Retry", True, (255, 255, 255))
            surf.blit(t, (WIDTH // 2 - t.get_width() // 2, HEIGHT // 2 - 40))
            surf.blit(st, (WIDTH // 2 - st.get_width() // 2, HEIGHT // 2 + 25))

        elif self.state == "WIN":
            overlay = pygame.Surface((WIDTH, HEIGHT), pygame.SRCALPHA)
            overlay.fill((20, 80, 20, 200))
            surf.blit(overlay, (0, 0))
            t = f_big.render("YOU DEFENDED THE GARDEN!", True, (255, 220, 0))
            surf.blit(t, (WIDTH // 2 - t.get_width() // 2, HEIGHT // 2 - 30))

# --- Главный цикл ---
def main():
    gm = GameManager()
    gm.state = "CHOOSER"
    running = True

    while running:
        clock.tick(FPS)

        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False
            elif event.type == pygame.KEYDOWN:
                if event.key in [pygame.K_p, pygame.K_ESCAPE]:
                    if gm.state == "PLAY": gm.state = "PAUSE"
                    elif gm.state == "PAUSE": gm.state = "PLAY"
                elif event.key == pygame.K_r and gm.state in ["GAMEOVER", "WIN"]:
                    gm.start_level(gm.current_level_idx)
            elif event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
                gm.handle_click(pygame.mouse.get_pos())

        gm.update()
        gm.draw(screen)
        pygame.display.flip()

    pygame.quit()
    sys.exit()

if __name__ == "__main__":
    main()