Программа не возвращается в меню по условию

При смерти или победе игрока вместо выхода в меню закрывается окно программы

import pygame
import sys
import random
import json
from PIL import Image, ImageSequence

# Инициализация Pygame
pygame.init()

# Настройки экрана
WIDTH, HEIGHT = 800, 600
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Mini Mario")

# Цвета
WHITE = (255, 255, 255)
BLUE = (0, 0, 255)
GREEN = (0, 255, 0)
RED = (255, 0, 0)
BLACK = (0, 0, 0)
YELLOW = (255, 255, 0)

# Загрузка пользовательского шрифта
custom_font = pygame.font.Font('assets/fonts/EnterCommand.ttf', 36)

# Часы для контроля FPS
clock = pygame.time.Clock()
FPS = 60

# Загрузка звуков
jump_sound = pygame.mixer.Sound('assets/sounds/jump.wav')
hit_sound = pygame.mixer.Sound('assets/sounds/hit.wav')

# Группы спрайтов
all_sprites = pygame.sprite.Group()
platforms = pygame.sprite.Group()
enemies = pygame.sprite.Group()
coins = pygame.sprite.Group()
flag_group = pygame.sprite.Group()

# Функция загрузки GIF
def load_gif(filename, size=None):
    """Загружает все кадры из GIF и преобразует их в формат Pygame."""
    try:
        pil_image = Image.open(filename)
    except FileNotFoundError:
        print(f"Файл не найден: {filename}")
        return []

    frames = []
    for frame in ImageSequence.Iterator(pil_image):
        frame = frame.convert("RGBA")
        if size:
            frame = frame.resize(size, Image.Resampling.LANCZOS)
        pygame_image = pygame.image.fromstring(frame.tobytes(), frame.size, frame.mode)
        frames.append(pygame_image)
    return frames

# Класс камеры
class Camera:
    def __init__(self, width, height):
        self.width = width
        self.height = height
        self.offset_x = 0
        self.offset_y = 0

    def apply(self, entity):
        """Применяет смещение камеры к объекту."""
        return entity.rect.move(-self.offset_x, -self.offset_y)

    def update(self, target):
        """Обновляет положение камеры относительно цели (игрока)."""
        self.offset_x = max(0, target.rect.x - WIDTH // 2)
        self.offset_x = min(self.offset_x, self.width - WIDTH)

# Класс игрока
class Player(pygame.sprite.Sprite):
    def __init__(self):
        super().__init__()
        self.running_frames = load_gif('assets/images/player/running_mario.gif', size=(60, 60))
        self.current_running_frame = 0
        self.standing_image = pygame.transform.scale(
            pygame.image.load('assets/images/player/standing_mario.png'), (60, 60))
        self.jumping_image = pygame.transform.scale(
            pygame.image.load('assets/images/player/jumping_mario.png'), (60, 60))
        self.image = self.standing_image
        self.rect = self.image.get_rect(center=(100, HEIGHT - 100))
        self.velocity_y = 0
        self.on_ground = False
        self.is_jumping = False
        self.is_moving = False
        self.lives = 3  # Добавляем жизни

    def update(self):
        if self.is_moving and not self.is_jumping:
            self.current_running_frame += 0.2
            if self.current_running_frame >= len(self.running_frames):
                self.current_running_frame = 0
            self.image = self.running_frames[int(self.current_running_frame)]
        elif self.is_jumping:
            self.image = self.jumping_image
        else:
            self.image = self.standing_image

        keys = pygame.key.get_pressed()
        if keys[pygame.K_LEFT]:
            self.rect.x -= 5
            self.is_moving = True
        elif keys[pygame.K_RIGHT]:
            self.rect.x += 5
            self.is_moving = True
        else:
            self.is_moving = False

        self.velocity_y += 1
        if self.velocity_y > 10:
            self.velocity_y = 10
        self.rect.y += self.velocity_y

        self.on_ground = False
        for platform in platforms:
            if self.rect.colliderect(platform.rect) and self.velocity_y > 0: 
                self.rect.bottom = platform.rect.top
                self.velocity_y = 0
                self.on_ground = True
                self.is_jumping = False

    def jump(self):
        if self.on_ground:
            self.velocity_y = -15
            self.is_jumping = True
            jump_sound.play()

# Класс платформы
class Platform(pygame.sprite.Sprite):
    def __init__(self, x, y, width, height):
        super().__init__()
        self.image = pygame.Surface((width, height))
        self.image.fill(GREEN)
        self.rect = self.image.get_rect(topleft=(x, y))

# Класс монеты
class Coin(pygame.sprite.Sprite):
    def __init__(self, x, y):
        super().__init__()
        self.frames = load_gif('assets/images/coins/coin_animation.gif', size=(20, 20))
        self.current_frame = 0
        self.image = self.frames[self.current_frame]
        self.rect = self.image.get_rect(topleft=(x, y))

    def update(self):
        self.current_frame += 0.2
        if self.current_frame >= len(self.frames):
            self.current_frame = 0
        self.image = self.frames[int(self.current_frame)]

# Класс обычного врага
class Enemy(pygame.sprite.Sprite):
    def __init__(self, x, y):
        super().__init__()
        self.frames = load_gif('assets/images/enemies/enemy_animation.gif', size=(40, 40))
        self.current_frame = 0
        self.image = self.frames[self.current_frame]
        self.rect = self.image.get_rect(topleft=(x, y))
        self.speed = random.choice([2, -2])
        self.direction = 1

    def update(self):
        self.current_frame += 0.2
        if self.current_frame >= len(self.frames):
            self.current_frame = 0
        self.image = self.frames[int(self.current_frame)]

        self.rect.x += self.speed * self.direction
        if self.rect.left <= 0 or self.rect.right >= WIDTH * 2:
            self.direction *= -1

# Класс флага
class Flag(pygame.sprite.Sprite):
    def __init__(self, x, y):
        super().__init__()
        try:
            self.image = pygame.image.load('assets/images/flag.png')
            self.image = pygame.transform.scale(self.image, (50, 100))
        except FileNotFoundError:
            print("Файл flag.png не найден! Проверьте путь.")
            self.image = pygame.Surface((50, 100))
            self.image.fill(RED)
        self.rect = self.image.get_rect(topleft=(x, y)) 

# Класс кнопки
class Button(pygame.sprite.Sprite):
    def __init__(self, x, y, image, action=None):
        super().__init__()
        self.image = image
        self.rect = self.image.get_rect()
        self.rect.topleft = (x, y)
        self.action = action

    def check_click(self, mouse_pos):
        if self.rect.collidepoint(mouse_pos):
            return self.action
        return None

# Функция отображения меню
def show_menu():
    global game_state

    menu_background = pygame.image.load('assets/images/menu/menu_background.png')
    start_button_img = pygame.image.load('assets/images/menu/start_button.png')
    quit_button_img = pygame.image.load('assets/images/menu/quit_button.png')

    buttons = pygame.sprite.Group()
    start_button = Button(WIDTH // 2 - 100, HEIGHT // 2 - 50, start_button_img, "start")
    quit_button = Button(WIDTH // 2 - 100, HEIGHT // 2 + 20, quit_button_img, "quit")
    buttons.add(start_button, quit_button)

    game_state = "menu"
    while game_state == "menu":
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                sys.exit()

            if event.type == pygame.MOUSEBUTTONDOWN:
                mouse_pos = pygame.mouse.get_pos()
                for button in buttons:
                    action = button.check_click(mouse_pos)
                    if action == "start":
                        game_state = "game"
                        return
                    elif action == "quit":
                        pygame.quit()
                        sys.exit()

        screen.blit(menu_background, (0, 0))
        buttons.draw(screen)
        pygame.display.flip()
        clock.tick(FPS)

# Функция загрузки уровня
def load_level(level_number):
    platforms.empty()
    enemies.empty()
    coins.empty()
    flag_group.empty()

    platforms.add(Platform(0, HEIGHT - 40, WIDTH * 2, 40))

    if level_number == 1:
        generate_platforms(8, level_length_multiplier=2)
        generate_coins(6)
        enemies.add(Enemy(300, HEIGHT - 80), Enemy(600, HEIGHT - 80))
        flag_group.add(Flag(WIDTH * 2 - 100, HEIGHT - 140))
    else:
        generate_platforms(3 + level_number * 2)
        generate_coins(3 + level_number)
        global spawn_enemies_continuously
        spawn_enemies_continuously = True
        flag_group.add(Flag(WIDTH * 2 - 100, HEIGHT - 140))

# Генерация платформ
def generate_platforms(count, level_length_multiplier=1):
    last_x = 0
    last_y = HEIGHT - 150
    for _ in range(count):
        x = random.randint(last_x + 150, last_x + 350)
        y = random.randint(last_y - 50, last_y + 50)
        if y > HEIGHT - 100:
            y = HEIGHT - 150
        platforms.add(Platform(x, y, 100, 20))
        last_x, last_y = x, y

    if level_length_multiplier > 1:
        for _ in range(count):
            x = random.randint(last_x + 150, last_x + 350)
            y = random.randint(last_y - 50, last_y + 50)
            if y > HEIGHT - 100:
                y = HEIGHT - 150
            platforms.add(Platform(x, y, 100, 20))
            last_x, last_y = x, y

# Генерация монет
def generate_coins(count):
    for _ in range(count):
        x = random.randint(50, WIDTH * 2 - 50)
        y = random.randint(HEIGHT // 2, HEIGHT - 150)
        coins.add(Coin(x, y))

# Сохранение рекорда
def save_high_score(score):
    try:
        with open("high_score.json", "r") as file:
            data = json.load(file)
    except FileNotFoundError:
        data = {"high_score": 0}
    if score > data["high_score"]:
        data["high_score"] = score
        with open("high_score.json", "w") as file:
            json.dump(data, file)

# Загрузка рекорда
def load_high_score():
    try:
        with open("high_score.json", "r") as file:
            return json.load(file)["high_score"]
    except FileNotFoundError:
        return 0

# Основной игровой цикл
def main():
    global game_state, current_level, score, high_score, spawn_enemies_continuously, start_time

    player = Player()
    all_sprites.add(player)

    camera = Camera(WIDTH * 2, HEIGHT)  # Создаем камеру

    current_level = 1
    score = 0
    high_score = load_high_score()
    spawn_enemies_continuously = False
    time_limit = 60
    start_time = pygame.time.get_ticks()

    load_level(current_level)

    game_state = "menu"

    running = True
    while running:
        if game_state == "menu":
            show_menu()

        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False

            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_SPACE:
                    player.jump()

        # Обновление
        all_sprites.update()
        enemies.update()
        coins.update()

        # Проверка столкновений с врагами
        for enemy in enemies:
            if player.rect.colliderect(enemy.rect):
                # Если Марио находится выше врага (прыгает на голову)
                if player.rect.bottom <= enemy.rect.top + 10:
                    hit_sound.play()
                    score += 20
                    player.velocity_y = -15  # Подпрыгивание после удара по врагу
                    player.is_jumping = True
                    enemy.kill()  # Уничтожаем врага
                else:
                    # Если Марио столкнулся с врагом иначе (не на голову)
                    hit_sound.play()
                    player.lives -= 1  # Уменьшаем жизни
                    if player.lives <= 0:  # Если жизни закончились
                        show_explosion(player.rect.x, player.rect.y)
                        save_high_score(score)
                        reset_game()
                    else:
                        # Возвращаем игрока в начало уровня
                        player.rect.x = 50
                        player.rect.y = HEIGHT - 100
                        player.velocity_y = 0

        # Проверка сбора монет
        coin_hits = pygame.sprite.spritecollide(player, coins, True)
        score += len(coin_hits) * 10

        # Проверка взаимодействия с флагом
        if pygame.sprite.spritecollide(player, flag_group, False):
            current_level += 1
            if current_level > 2:
                win_text = custom_font.render("You Win!", True, BLUE)
                screen.fill(WHITE)
                screen.blit(win_text, (WIDTH // 2 - win_text.get_width() // 2, HEIGHT // 2))
                pygame.display.flip()
                pygame.time.delay(3000)

                save_high_score(score)
                reset_game()
            else:
                load_level(current_level)
                player.rect.x = 50
                score += 100

        # Таймер
        current_time = pygame.time.get_ticks()
        elapsed_time = (current_time - start_time) // 1000
        remaining_time = max(0, time_limit - elapsed_time)

        if remaining_time == 0:
            game_over_text = custom_font.render("Game Over", True, RED)
            screen.fill(WHITE)
            screen.blit(game_over_text, (WIDTH // 2 - game_over_text.get_width() // 2, HEIGHT // 2))
            pygame.display.flip()
            pygame.time.delay(3000)

            save_high_score(score)
            reset_game()

        # Спавн врагов на втором уровне
        if current_level == 2 and spawn_enemies_continuously:
            if len(enemies) < 5:  # Ограничение на количество врагов
                if random.randint(1, 100) == 1:
                    enemies.add(Enemy(random.randint(50, WIDTH - 50), HEIGHT - 80))

        # Обновление камеры
        camera.update(player)

        # Отрисовка
        screen.fill(WHITE)

        # Отрисовка платформ
        for platform in platforms:
            screen.blit(platform.image, camera.apply(platform))

        # Отрисовка врагов
        for enemy in enemies:
            screen.blit(enemy.image, camera.apply(enemy))

        # Отрисовка монет
        for coin in coins:
            screen.blit(coin.image, camera.apply(coin))

        # Отрисовка флага
        for flag in flag_group:
            screen.blit(flag.image, camera.apply(flag))

        # Отрисовка игрока
        screen.blit(player.image, camera.apply(player))

        # Отображение счета, времени и жизней
        score_text = custom_font.render(f"Score: {score}", True, BLACK)
        time_text = custom_font.render(f"Time: {remaining_time}", True, BLACK)
        high_score_text = custom_font.render(f"High Score: {high_score}", True, BLACK)
        lives_text = custom_font.render(f"Lives: {player.lives}", True, RED)

        screen.blit(score_text, (10, 10))
        screen.blit(time_text, (WIDTH - 170, 10))
        screen.blit(high_score_text, (10, HEIGHT - 40))
        screen.blit(lives_text, (WIDTH - 200, HEIGHT - 40))  # Отображаем жизни

        pygame.display.flip()
        clock.tick(FPS)

    pygame.quit()

# Сброс игры
def reset_game():
    global current_level, score, start_time, game_state
    score = 0
    current_level = 1 
    load_level(current_level)
    player.rect.x = 50
    player.rect.y = HEIGHT - 100
    player.lives = 3  # Сбрасываем жизни
    start_time = pygame.time.get_ticks()
    game_state = "menu"

# Функция отображения взрыва
def show_explosion(x, y):
    explosion = pygame.image.load('assets/images/explosion.png')
    screen.blit(explosion, (x, y))
    pygame.display.flip()
    pygame.time.delay(500)

if __name__ == "__main__":
    main()

Ответы (0 шт):