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


import pygame
import random
import sys

# --- Инициализация и настройки ---
pygame.init()
WIDTH, HEIGHT = 1000, 600
GRID_COLS, GRID_ROWS = 9, 5
CELL_W, CELL_H = 90, 90
GRID_X, GRID_Y = 140, 100

FPS = 60
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Plants vs. Zombies - Python Edition")
clock = pygame.time.Clock()
font = pygame.font.SysFont("Arial", 18, bold=True)
big_font = pygame.font.SysFont("Arial", 42, bold=True)

# Палитра
COLOR_BG = (135, 206, 235)
COLOR_LAWN_LIGHT = (118, 186, 27)
COLOR_LAWN_DARK = (104, 169, 21)
COLOR_UI = (80, 50, 20)
COLOR_WHITE = (255, 255, 255)
COLOR_BLACK = (0, 0, 0)
COLOR_SUN = (255, 215, 0)

# --- Игровые сущности ---
class Sun:
    def __init__(self, x, y, target_y=None):
        self.x = x
        self.y = y
        self.target_y = target_y if target_y else y
        self.radius = 20
        self.alive = True
        self.timer = 0

    def update(self):
        if self.y < self.target_y:
            self.y += 2
        self.timer += 1
        if self.timer > 600:  # Исчезает через 10 сек
            self.alive = False

    def draw(self, surface):
        pygame.draw.circle(surface, (255, 165, 0), (int(self.x), int(self.y)), self.radius + 3)
        pygame.draw.circle(surface, COLOR_SUN, (int(self.x), int(self.y)), self.radius)

class Projectile:
    def __init__(self, x, y, row):
        self.x = x
        self.y = y
        self.row = row
        self.speed = 6
        self.damage = 20
        self.alive = True

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

    def draw(self, surface):
        pygame.draw.circle(surface, (50, 205, 50), (int(self.x), int(self.y)), 8)
        pygame.draw.circle(surface, (255, 255, 255), (int(self.x) - 2, int(self.y) - 2), 3)

class Plant:
    def __init__(self, col, row, p_type):
        self.col = col
        self.row = row
        self.type = p_type
        self.x = GRID_X + col * CELL_W + CELL_W // 2
        self.y = GRID_Y + row * CELL_H + CELL_H // 2
        self.hp = 100 if p_type != "wallnut" else 400
        self.max_hp = self.hp
        self.cooldown = 0

    def update(self, game):
        self.cooldown += 1
        if self.type == "sunflower" and self.cooldown >= 420:  # ~7 секунд
            game.suns.append(Sun(self.x, self.y - 10))
            self.cooldown = 0
        elif self.type == "peashooter" and self.cooldown >= 90:  # ~1.5 секунды
            # Стрелять, только если на линии есть зомби
            if any(z.row == self.row and z.x > self.x for z in game.zombies):
                game.projectiles.append(Projectile(self.x + 20, self.y - 8, self.row))
                self.cooldown = 0

    def draw(self, surface):
        if self.type == "peashooter":
            pygame.draw.circle(surface, (34, 139, 34), (self.x, self.y), 24)
            pygame.draw.rect(surface, (0, 100, 0), (self.x + 10, self.y - 12, 18, 14), border_radius=4)
            pygame.draw.circle(surface, (0, 0, 0), (self.x + 6, self.y - 6), 4)
        elif self.type == "sunflower":
            for angle in range(0, 360, 45):
                rad = angle * 3.14159 / 180
                ox = self.x + int(16 * pygame.math.Vector2(1, 0).rotate(angle).x)
                oy = self.y + int(16 * pygame.math.Vector2(1, 0).rotate(angle).y)
                pygame.draw.circle(surface, COLOR_SUN, (ox, oy), 8)
            pygame.draw.circle(surface, (139, 69, 19), (self.x, self.y), 16)
        elif self.type == "wallnut":
            w = int(22 * (self.hp / self.max_hp * 0.4 + 0.6))
            pygame.draw.ellipse(surface, (160, 82, 45), (self.x - w, self.y - 28, w * 2, 56))
            pygame.draw.circle(surface, COLOR_BLACK, (self.x - 6, self.y - 8), 3)
            pygame.draw.circle(surface, COLOR_BLACK, (self.x + 6, self.y - 8), 3)

class Zombie:
    def __init__(self, row):
        self.row = row
        self.x = WIDTH + 30
        self.y = GRID_Y + row * CELL_H + CELL_H // 2
        self.speed = 0.35
        self.hp = 100
        self.eating = False
        self.alive = True

    def update(self, plants):
        self.eating = False
        # Проверка поедания растений
        for plant in plants:
            if plant.row == self.row and abs((plant.x + 15) - self.x) < 25:
                self.eating = True
                plant.hp -= 0.5
                break

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

        if self.hp <= 0:
            self.alive = False

    def draw(self, surface):
        # Тело
        pygame.draw.rect(surface, (47, 79, 79), (self.x - 12, self.y - 10, 24, 35))
        # Голова
        pygame.draw.circle(surface, (143, 188, 143), (int(self.x), int(self.y) - 22), 16)
        # Глаза
        pygame.draw.circle(surface, (255, 0, 0), (int(self.x) - 5, int(self.y) - 24), 3)
        pygame.draw.circle(surface, (255, 0, 0), (int(self.x) + 5, int(self.y) - 24), 3)

# --- Игровой Контроллер ---
class Game:
    def __init__(self):
        self.sun_score = 150
        self.grid = [[None for _ in range(GRID_COLS)] for _ in range(GRID_ROWS)]
        self.plants = []
        self.zombies = []
        self.projectiles = []
        self.suns = []
        self.selected_tool = None
        self.sky_sun_timer = 0
        self.zombie_timer = 0
        self.game_over = False

        self.cards = [
            {"type": "sunflower", "cost": 50, "rect": pygame.Rect(20, 110, 100, 60)},
            {"type": "peashooter", "cost": 100, "rect": pygame.Rect(20, 180, 100, 60)},
            {"type": "wallnut", "cost": 50, "rect": pygame.Rect(20, 250, 100, 60)},
        ]

    def handle_click(self, pos):
        # 1. Клик по падающим солнышкам
        for sun in self.suns:
            if pygame.math.Vector2(pos[0] - sun.x, pos[1] - sun.y).length() <= sun.radius:
                self.sun_score += 25
                sun.alive = False
                return

        # 2. Выбор растения в UI
        for card in self.cards:
            if card["rect"].collidepoint(pos):
                if self.sun_score >= card["cost"]:
                    self.selected_tool = card["type"]
                return

        # 3. Высадка на поле
        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_tool and self.grid[row][col] is None:
                cost = next(c["cost"] for c in self.cards if c["type"] == self.selected_tool)
                if self.sun_score >= cost:
                    p = Plant(col, row, self.selected_tool)
                    self.plants.append(p)
                    self.grid[row][col] = p
                    self.sun_score -= cost
                    self.selected_tool = None

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

        # Солнечный свет с неба
        self.sky_sun_timer += 1
        if self.sky_sun_timer >= 350:
            target_y = random.randint(GRID_Y, HEIGHT - 80)
            self.suns.append(Sun(random.randint(GRID_X, WIDTH - 60), 0, target_y))
            self.sky_sun_timer = 0

        # Спавн зомби
        self.zombie_timer += 1
        if self.zombie_timer >= 280:
            self.zombies.append(Zombie(random.randint(0, GRID_ROWS - 1)))
            self.zombie_timer = 0

        # Обновление сущностей
        for plant in self.plants:
            plant.update(self)
        for proj in self.projectiles:
            proj.update()
        for zombie in self.zombies:
            zombie.update(self.plants)
            if zombie.x < GRID_X - 10:
                self.game_over = True
        for sun in self.suns:
            sun.update()

        # Коллизии: Снаряды -> Зомби
        for proj in self.projectiles:
            for zombie in self.zombies:
                if proj.row == zombie.row and abs(proj.x - zombie.x) < 20:
                    zombie.hp -= proj.damage
                    proj.alive = False
                    break

        # Очистка мертвых объектов
        for p in self.plants:
            if p.hp <= 0:
                self.grid[p.row][p.col] = None
        self.plants = [p for p in self.plants if p.hp > 0]
        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]

    def draw(self, surface):
        surface.fill(COLOR_BG)

        # Лужайка
        for r in range(GRID_ROWS):
            for c in range(GRID_COLS):
                color = COLOR_LAWN_LIGHT if (r + c) % 2 == 0 else COLOR_LAWN_DARK
                rect = pygame.Rect(GRID_X + c * CELL_W, GRID_Y + r * CELL_H, CELL_W, CELL_H)
                pygame.draw.rect(surface, color, rect)
                pygame.draw.rect(surface, (60, 110, 20), rect, 1)

        # UI Панель
        pygame.draw.rect(surface, COLOR_UI, (0, 0, WIDTH, 80))
        sun_text = font.render(f"Sun: {self.sun_score}", True, COLOR_SUN)
        surface.blit(sun_text, (25, 30))

        # Карточки покупки
        for card in self.cards:
            is_active = self.selected_tool == card["type"]
            can_afford = self.sun_score >= card["cost"]
            c_color = (220, 200, 160) if can_afford else (120, 110, 100)
            if is_active:
                c_color = (255, 255, 100)

            pygame.draw.rect(surface, c_color, card["rect"], border_radius=6)
            pygame.draw.rect(surface, COLOR_BLACK, card["rect"], 2, border_radius=6)

            txt = font.render(f"{card['type'][:4].upper()}", True, COLOR_BLACK)
            cost_txt = font.render(f"{card['cost']}", True, (139, 0, 0))
            surface.blit(txt, (card["rect"].x + 10, card["rect"].y + 10))
            surface.blit(cost_txt, (card["rect"].x + 10, card["rect"].y + 35))

        # Отрисовка объектов
        for plant in self.plants:
            plant.draw(surface)
        for zombie in self.zombies:
            zombie.draw(surface)
        for proj in self.projectiles:
            proj.draw(surface)
        for sun in self.suns:
            sun.draw(surface)

        # Экран поражения
        if self.game_over:
            overlay = pygame.Surface((WIDTH, HEIGHT), pygame.SRCALPHA)
            overlay.fill((0, 0, 0, 180))
            surface.blit(overlay, (0, 0))
            t = big_font.render("THE ZOMBIES ATE YOUR BRAINS!", True, (220, 20, 60))
            surface.blit(t, (WIDTH // 2 - t.get_width() // 2, HEIGHT // 2 - 20))

# --- Главный цикл ---
def main():
    game = Game()
    running = True

    while running:
        clock.tick(FPS)
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False
            elif event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
                game.handle_click(pygame.mouse.get_pos())

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

    pygame.quit()
    sys.exit()

if __name__ == "__main__":
    main()