Тобой написан код: import pygame
import sys
import random
import operator
import time

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

# Получение размеров экрана
SCREEN_INFO = pygame.display.Info()
WIDTH, HEIGHT = SCREEN_INFO.current_w, SCREEN_INFO.current_h

# 1) Увеличение ширины карты: устанавливаем GAME_WIDTH на 70% экрана, MAP_WIDTH на 30%
GAME_WIDTH = int(WIDTH * 0.7)  # 70% ширины для игры
MAP_WIDTH = WIDTH - GAME_WIDTH  # 30% для карты и инвентаря
GAME_HEIGHT = HEIGHT - 100  # Оставляем место для консоли

# Устанавливаем оконный режим для отображения кнопок управления окном
WIN = pygame.display.set_mode((WIDTH, HEIGHT))  # Оконный режим
pygame.display.set_caption("Математическая Игра с NPC")

# Цвета
BLUE = (0, 0, 255)
GREEN = (0, 200, 0)
RED = (255, 0, 0)
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
YELLOW = (255, 255, 0)
LIGHT_BLUE = (173, 216, 230)
GRAY = (200, 200, 200)
DARK_GRAY = (50, 50, 50)
LIGHT_GRAY = (230, 230, 230)
ORANGE = (255, 165, 0)  # Цвет для 'обиженных' NPC
DUNGEON_COLOR = (128, 0, 128)  # Фиолетовый цвет для подземелий

# FPS
FPS = 60
clock = pygame.time.Clock()

# Шрифты с уменьшенным размером для инвентаря
font = pygame.font.SysFont(None, 20)
large_font = pygame.font.SysFont(None, 28)
npc_font = pygame.font.SysFont(None, 18)
console_font = pygame.font.SysFont(None, 20)
map_font = pygame.font.SysFont(None, 14)

# Фоновые цвета для территорий
terrain_backgrounds = {
    "Засушливые земли": LIGHT_GRAY,
    "Великие равнины": (144, 238, 144),  # Light green
    "Горные пастбища": (169, 169, 169),  # Dark Gray
    "Туманная долина": (176, 224, 230),  # Powder blue
}

# Названия территорий распределенные по регионам
terrain_map = {}  # Ключ: (x, y), Значение: название местности

# Игровые параметры
player_size = 50
player_pos = [GAME_WIDTH // 2, GAME_HEIGHT // 2]
player_color = BLUE
player_speed = 5

# Карта мира
WORLD_SIZE = 30  # 30x30 регионов
MAP_TILE_SIZE = 8  # Увеличен размер клетки до 8

# Уровни сложности задач и соответствующие территории
def calculate_level(x, y):
    center = WORLD_SIZE // 2
    distance = max(abs(x - center), abs(y - center))
    # Уровень увеличивается по мере удаления от центра
    return min(1 + distance // 3, 10)  # Ограничение до 10

npc_levels = {}
for x in range(WORLD_SIZE):
    for y in range(WORLD_SIZE):
        level = calculate_level(x, y)
        if level <= 3:
            math_range = (1, 10)
            territory = "Засушливые земли"
        elif level <= 6:
            math_range = (10, 50)
            territory = "Великие равнины"
        elif level <= 8:
            math_range = (50, 100)
            territory = "Горные пастбища"
        else:
            math_range = (100, 1000)
            territory = "Туманная долина"
        npc_levels[(x, y)] = {"math_range": math_range, "territory": territory}

# Уникальные названия предметов
sword_names = [
    "Рубиголов", "Остроклюв", "Буревестник", "Темнокаменный клинок",
    "Звёздочёрный меч", "Лунный колун", "Солнечный разрубатель",
    "Грозовой крюк", "Теневой блеск", "Тиранический кромкогрив",
    "Изумрудный резак", "Пламеглот", "Кристальный рубанок",
    "Багровый пырсок", "Сапфировый прорезинитель"
]

shield_names = [
    "Чешуя Дракона", "Черепаший панцирь", "Обелисковый бастион",
    "Гранитная кулачность", "Ледяной заслон", "Мифриловый щит",
    "Эльфийская структура", "Баншейская маржа", "Древний барьер",
    "Стальной фундамент", "Титановый блеск", "Железный усадок",
    "Кожаный покров", "Магический заслон", "Адамантиновый щит"
]

armor_names = [
    "Непроницаемая кожа", "Стальной заслон", "Броня носорога",
    "Серебряный панцирь", "Драконья кожа", "Мифриловая броня",
    "Эльфийское обмундирование", "Гномская кольчуга",
    "Титановая латная рубаха", "Магическая мантия",
    "Закалённый факел", "Железный каркас", "Кожаный покров",
    "Духовная броня", "Кристальная защита"
]

# Типы предметов
items_types = [
    {
        'type': 'Меч',
        'category_names': sword_names,
        'stat': 'Атака',
        'stat_value_range': (5, 15)
    },
    {
        'type': 'Щит',
        'category_names': shield_names,
        'stat': 'Защита',
        'stat_value_range': (3, 10)
    },
    {
        'type': 'Доспех',
        'category_names': armor_names,
        'stat': 'Защита',
        'stat_value_range': (4, 12)
    }
]

# NPC профессии и имена
npc_professions = [
    {'profession': 'Дровосек', 'names': ['Иван', 'Пётр', 'Алексей', 'Николай', 'Ли']},
    {'profession': 'Кузнец', 'names': ['Софья', 'Мария', 'Елена', 'Анна', 'Хуа']},
    {'profession': 'Травник', 'names': ['Дмитрий', 'Максим', 'Виктор', 'Андрей', 'Тихон']},
    {'profession': 'Рыбак', 'names': ['Константин', 'Георгий', 'Василий', 'Михаил', 'Лун']},
    {'profession': 'Алхимик', 'names': ['Лора', 'Майкл', 'Сара', 'Джек', 'Мию']},
    {'profession': 'Мастер', 'names': ['Боб', 'Том', 'Лиза', 'Кэт', 'Нана']}
]

# Структура региона с добавленным cooldown для NPC и возможными подземельями
class Region:
    def __init__(self, x, y):
        self.coord = (x, y)
        self.npcs = []  # Список NPC в регионе
        self.dungeon_entrances = []  # Список позиций входов в подземелье
        self.dungeons = {}  # Хранение подземелий
        self.generate_npcs()
        self.generate_dungeon_entrances()

    def generate_npcs(self):
        num_npcs = random.randint(1, 5)  # Ограничение от 1 до 5 NPC
        for _ in range(num_npcs):
            profession_entry = random.choice(npc_professions)
            profession = profession_entry['profession']
            name = random.choice(profession_entry['names'])
            # Генерация уровня NPC на основе региона
            level = calculate_level(*self.coord)
            # Генерация позиции NPC с учётом предотвращения наложения и отступа от границ
            pos = generate_npc_position(existing_npcs=self.npcs)
            # Название местности
            territory = npc_levels[self.coord]['territory']
            self.npcs.append({
                'pos': pos,
                'has_interacted': False,
                'profession': profession,
                'name': name,
                'level': level,
                'task_solved': False,
                'cooldown': 0  # Время, до которого NPC не может взаимодействовать после отказа
            })

    def generate_dungeon_entrances(self):
        # С вероятностью 20% регион может содержать подземелье
        if random.random() < 0.2:
            # Добавляем 1 или 2 входа в подземелье
            num_entrances = random.randint(1, 2)
            for _ in range(num_entrances):
                pos = generate_dungeon_entrance_position(existing_entrances=self.dungeon_entrances)
                self.dungeon_entrances.append(pos)

def generate_npc_position(existing_npcs=None):
    if existing_npcs is None:
        existing_npcs = []
    attempts = 0
    margin = 50  # 50 пикселей от каждой стороны
    while attempts < 100:
        x = random.randint(margin, GAME_WIDTH - 200 - 50)  # 50 - размер NPC
        y = random.randint(margin, GAME_HEIGHT - 150 - 50)
        new_rect = pygame.Rect(x, y, 50, 50)
        overlap = False
        for npc in existing_npcs:
            existing_rect = pygame.Rect(npc['pos'][0], npc['pos'][1], 50, 50)
            if new_rect.colliderect(existing_rect.inflate(50, 50)):
                overlap = True
                break
        if not overlap:
            return [x, y]
        attempts += 1
    return [random.randint(margin, GAME_WIDTH - 200 - 50), random.randint(margin, GAME_HEIGHT - 150 - 50)]

def generate_dungeon_entrance_position(existing_entrances=None):
    if existing_entrances is None:
        existing_entrances = []
    attempts = 0
    margin = 60  # Отступ для входов
    while attempts < 100:
        x = random.randint(margin, GAME_WIDTH - 60)  # Размер входа = 40, добавляем паддинг
        y = random.randint(margin, GAME_HEIGHT - 60)
        new_rect = pygame.Rect(x, y, 40, 40)
        overlap = False
        for entrance in existing_entrances:
            existing_rect = pygame.Rect(entrance[0], entrance[1], 40, 40)
            if new_rect.colliderect(existing_rect.inflate(50, 50)):
                overlap = True
                break
        if not overlap:
            return [x, y]
        attempts +=1
    return [random.randint(margin, GAME_WIDTH - 60), random.randint(margin, GAME_HEIGHT - 60)]

# Генерация и хранение регионов
world_regions = {}

def get_region(x, y):
    if (x, y) not in world_regions and 0 <= x < WORLD_SIZE and 0 <= y < WORLD_SIZE:
        world_regions[(x, y)] = Region(x, y)
    return world_regions.get((x, y), None)

# Текущий регион игрока
current_region_coord = (WORLD_SIZE // 2, WORLD_SIZE // 2)  # Начинаем в центре
current_region = get_region(*current_region_coord)

# Инициализация имен местностей
def assign_territories(x, y):
    if (x, y) not in terrain_map:
        region_level = npc_levels[(x, y)]['territory']
        terrain_map[(x, y)] = region_level

# Вызов функции для начальной территории
assign_territories(*current_region_coord)

# Инвентарь
inventory = []

# Активное снаряжение
equipped = {
    'Меч': [],
    'Щит': None,
    'Доспех': None
}

# Игрокские параметры
attack = 0
defense = 0
knowledge_level = 1
tasks_solved = 0
tasks_total = 0  # Общее количество решенных задач
tasks_needed = 10  # Необходимое количество для повышения уровня

# Процент верных решений
def calculate_percentage():
    if tasks_total == 0:
        return 0
    return int((tasks_solved / tasks_total) * 100)

# Состояния игры
state = 'move'  # Возможные состояния: 'move', 'dialog', 'inventory', 'dungeon'

# Диалоговые окна
current_npc = None
current_question = None
correct_answer = None
player_answer = ''

# Информационная консоль
console_message = ""

# Функция для пересчёта параметров игрока
def recalculate_stats():
    global attack, defense
    attack = sum([item['value'] for item in equipped['Меч']])
    shield_def = equipped['Щит']['value'] if equipped['Щит'] else 0
    armor_def = equipped['Доспех']['value'] if equipped['Доспех'] else 0
    defense = shield_def + armor_def

# Функция для обновления уровня знаний
def update_level():
    global knowledge_level, tasks_needed, console_message
    levels_requirements = {1:10, 2:20, 3:30, 4:40, 5:50, 6:60, 7:70, 8:80, 9:90, 10:100}
    if knowledge_level in levels_requirements:
        if tasks_solved >= levels_requirements[knowledge_level]:
            knowledge_level +=1
            tasks_needed = levels_requirements.get(knowledge_level, tasks_needed)
            console_message += f" Поздравляем! Вы достигли уровня {knowledge_level} знаний."

# 2) Функция для открытия окна инвентаря ниже карты с уменьшенным шрифтом
def open_inventory():
    global state
    state = 'inventory'
    inventory_running = True
    while inventory_running:
        clock.tick(FPS)
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                sys.exit()
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_ESCAPE:
                    inventory_running = False
                    state = 'move'
            if event.type == pygame.MOUSEBUTTONDOWN:
                mouse_pos = pygame.mouse.get_pos()
                # Проверяем нажатие на кнопки "+"
                for idx, item in enumerate(inventory.copy()):
                    button_rect = pygame.Rect(GAME_WIDTH + 10, GAME_HEIGHT + 10 + idx * 30, 20, 20)  # Малая кнопка слева
                    if button_rect.collidepoint(mouse_pos):
                        equip_item(idx)
                        inventory_running = False
                        break  # Одновременно только одна кнопка

        # Отрисовка игрового окна под инвентарём
        draw_window()

        # Отрисовка инвентаря ниже карты
        # Разделим правую часть на карту (верх) и инвентарь (низ)
        # Полупрозрачный фон для инвентаря
        inventory_surface = pygame.Surface((MAP_WIDTH, HEIGHT - GAME_HEIGHT))
        inventory_surface.set_alpha(240)  # Прозрачность
        inventory_surface.fill(WHITE)
        WIN.blit(inventory_surface, (GAME_WIDTH, GAME_HEIGHT))

        # Заголовок инвентаря
        inventory_title = large_font.render("Инвентарь", True, BLACK)
        WIN.blit(inventory_title, (GAME_WIDTH + MAP_WIDTH//2 - inventory_title.get_width()//2, GAME_HEIGHT + 10))

        # Отображение предметов в инвентаре с кнопками "+"
        for idx, item in enumerate(inventory):
            # Рисуем кнопку "+"
            button_rect = pygame.Rect(GAME_WIDTH + 10, GAME_HEIGHT + 40 + idx * 30, 20, 20)  # Малая кнопка слева
            pygame.draw.rect(WIN, GRAY, button_rect)
            plus_text = font.render("+", True, BLACK)
            WIN.blit(plus_text, (button_rect.x + 5, button_rect.y + 2))

            # Отображаем текст предмета
            item_text = font.render(f"{idx+1}. {item['name']} ({item['type']}: {item['stat']} +{item['value']})", True, BLACK)
            WIN.blit(item_text, (GAME_WIDTH + 35, GAME_HEIGHT + 40 + idx * 30))

            # Изменение цвета кнопки при наведении
            mouse_pos = pygame.mouse.get_pos()
            if button_rect.collidepoint(mouse_pos):
                pygame.draw.rect(WIN, YELLOW, button_rect, 2)

        # Отображение активного снаряжения в инвентаре
        equipped_text = font.render("Активное снаряжение:", True, BLACK)
        WIN.blit(equipped_text, (GAME_WIDTH + 10, GAME_HEIGHT + MAP_WIDTH//2))
        swords = ', '.join([item['name'] for item in equipped['Меч']]) if equipped['Меч'] else "Нет"
        shield = equipped['Щит']['name'] if equipped['Щит'] else "Нет"
        armor = equipped['Доспех']['name'] if equipped['Доспех'] else "Нет"
        swords_text = font.render(f"Мечи: {swords}", True, BLACK)
        shield_text = font.render(f"Щит: {shield}", True, BLACK)
        armor_text = font.render(f"Доспех: {armor}", True, BLACK)
        WIN.blit(swords_text, (GAME_WIDTH + 30, GAME_HEIGHT + MAP_WIDTH//2 + 20))
        WIN.blit(shield_text, (GAME_WIDTH + 30, GAME_HEIGHT + MAP_WIDTH//2 + 40))
        WIN.blit(armor_text, (GAME_WIDTH + 30, GAME_HEIGHT + MAP_WIDTH//2 + 60))

        pygame.display.update()

# 3) Добавление черных квадратов как входов в подземелья в основном игровом пространстве
def draw_game_area():
    # Отрисовка текущей территории фоном
    territory = terrain_map.get(current_region_coord, "Засушливые земли")
    bg_color = terrain_backgrounds.get(territory, WHITE)
    pygame.draw.rect(WIN, bg_color, (0, 0, GAME_WIDTH, GAME_HEIGHT))

    # Рисуем территорию сверху
    territory_title = large_font.render(territory, True, BLACK)
    WIN.blit(territory_title, (GAME_WIDTH//2 - territory_title.get_width()//2, 10))

    # Рисуем NPC
    for npc in current_region.npcs:
        # Цвет NPC зависит от состояния
        current_time = time.time()
        if current_time < npc['cooldown']:
            color = ORANGE
        else:
            color = YELLOW if npc['task_solved'] else GREEN
        pygame.draw.rect(WIN, color, (npc['pos'][0], npc['pos'][1], 50, 50))
        # Отображаем имя, уровень и профессию
        npc_text = npc_font.render(f"{npc['profession']} (ур.{npc['level']}): {npc['name']}", True, BLACK)
        WIN.blit(npc_text, (npc['pos'][0], npc['pos'][1] - 20))

        # Если игрок рядом и можно получить предмет, показываем информацию
        if state == 'move' and not npc['task_solved'] and current_time >= npc['cooldown']:
            player_rect = pygame.Rect(player_pos[0], player_pos[1], player_size, player_size)
            npc_rect = pygame.Rect(npc['pos'][0], npc['pos'][1], 50, 50)
            # Взаимодействие только при близком расстоянии
            if player_rect.colliderect(npc_rect.inflate(6, 6)):  # Расстояние примерно 3 пикселя
                # Каждый NPC может дать один случайный предмет
                possible_item = generate_item(npc['level'])
                item_type = possible_item['type']
                item_name = possible_item['name']
                item_stat = possible_item['stat']
                stat_value = possible_item['value']
                info_text = f"{item_type} {item_name} ({item_stat} +{stat_value})"
                info_render = font.render(info_text, True, YELLOW)
                WIN.blit(info_render, (npc['pos'][0], npc['pos'][1] + 60))

    # Рисуем входы в подземелья как черные квадраты
    for entrance in current_region.dungeon_entrances:
        pygame.draw.rect(WIN, BLACK, (entrance[0], entrance[1], 40, 40))
        # Опционально: можно добавить иконку или символ для входа
        entrance_text = font.render("Подземелье", True, WHITE)
        WIN.blit(entrance_text, (entrance[0]+5, entrance[1]+10))

    # Рисуем игрока
    pygame.draw.rect(WIN, player_color, (player_pos[0], player_pos[1], player_size, player_size))

    # Отображаем параметры игрока
    attack_text = font.render(f"Атака: {attack}", True, BLACK)
    defense_text = font.render(f"Защита: {defense}", True, BLACK)
    percentage_text = font.render(f"Процент верных: {calculate_percentage()}%", True, BLACK)
    knowledge_text = font.render(f"Уровень Знаний: {knowledge_level} ({tasks_solved}/{tasks_needed})", True, BLACK)
    WIN.blit(attack_text, (GAME_WIDTH - 200, 20))
    WIN.blit(defense_text, (GAME_WIDTH - 200, 50))
    WIN.blit(percentage_text, (GAME_WIDTH - 200, 80))
    WIN.blit(knowledge_text, (GAME_WIDTH - 200, 110))

    # Рисуем кнопку инвентаря под параметрами
    inventory_button = pygame.Rect(GAME_WIDTH - 120, 140, 100, 30)
    pygame.draw.rect(WIN, GRAY, inventory_button)
    inventory_text = font.render("Инвентарь", True, BLACK)
    WIN.blit(inventory_text, (inventory_button.x + 15, inventory_button.y + 8))
    # Проверка наведения на кнопку
    mouse_pos = pygame.mouse.get_pos()
    mouse_pressed = pygame.mouse.get_pressed()
    if inventory_button.collidepoint(mouse_pos):
        pygame.draw.rect(WIN, YELLOW, inventory_button, 2)
        if mouse_pressed[0]:
            open_inventory()

# Функция для рисования карты
def draw_map():
    # Отрисовка карты на верхней части правого блока
    pygame.draw.rect(WIN, DARK_GRAY, (GAME_WIDTH, 0, MAP_WIDTH, GAME_HEIGHT//2))
    map_title = large_font.render("Карта", True, WHITE)
    WIN.blit(map_title, (GAME_WIDTH + MAP_WIDTH//2 - map_title.get_width()//2, 10))

    # Отрисовка квадратов регионов
    for y in range(WORLD_SIZE):
        for x in range(WORLD_SIZE):
            region_coord = (x, y)
            if region_coord in terrain_map:
                # Отображаем только открытые регионы
                if region_coord == current_region_coord:
                    color = YELLOW  # Текущий регион
                else:
                    color = WHITE  # Посещённые регионы
                # Проверяем, есть ли подземелье
                region = world_regions[region_coord]
                for entrance in region.dungeon_entrances:
                    # Для упрощения, не отображаем их на карте
                    pass
                if region.dungeon_entrances:
                    pygame.draw.rect(WIN, DUNGEON_COLOR, (GAME_WIDTH + 10 + x * MAP_TILE_SIZE*2, 40 + y * MAP_TILE_SIZE*2, MAP_TILE_SIZE*2, MAP_TILE_SIZE*2))
                else:
                    pygame.draw.rect(WIN, color, (GAME_WIDTH + 10 + x * MAP_TILE_SIZE*2, 40 + y * MAP_TILE_SIZE*2, MAP_TILE_SIZE*2, MAP_TILE_SIZE*2))
            else:
                # Незаполненные регионы отображаются чёрным
                pygame.draw.rect(WIN, BLACK, (GAME_WIDTH + 10 + x * MAP_TILE_SIZE*2, 40 + y * MAP_TILE_SIZE*2, MAP_TILE_SIZE*2, MAP_TILE_SIZE*2))

    # Отображение координат текущего региона
    coord_text = map_font.render(f"Координаты: {current_region_coord}", True, WHITE)
    WIN.blit(coord_text, (GAME_WIDTH + 10, GAME_HEIGHT//2 - 10))

# Функция для рисования инвентаря
def draw_inventory():
    # Инвентарь уже рисуется в функции open_inventory
    pass  # Реализация внутри open_inventory

# Функция для рисования основного окна (игровой области и карты)
def draw_window():
    # Отрисовка игрового пространства и карты
    draw_game_area()
    draw_map()

    # Информационная консоль внизу
    pygame.draw.rect(WIN, LIGHT_BLUE, (0, GAME_HEIGHT, WIDTH, HEIGHT - GAME_HEIGHT))
    console_text = console_font.render(console_message, True, BLACK)
    WIN.blit(console_text, (10, GAME_HEIGHT + 10))

    pygame.display.update()

# 4) Генерация и отображение подземелий и управление состоянием подземелий

# Хранение активных подземелий
active_dungeons = {}

# Функция для генерации математического вопроса
def generate_math_question(npc_level):
    operations = ['+', '-', '*', '/']  # Убрали '**'
    if 1 <= npc_level <= 3:
        num_range = (1, 10)
        num_operations = 1
    elif 4 <= npc_level <=6:
        num_range = (10, 50)
        num_operations = 2
    elif 7 <= npc_level <=9:
        num_range = (50, 100)
        num_operations = 3
    else:
        num_range = (100, 1000)
        num_operations = 4

    question_parts = []
    operations_sequence = []
    num1 = random.randint(*num_range)
    question_parts.append(str(num1))
    for _ in range(num_operations):
        operation = random.choice(operations)
        # Если операция деления, гарантируем целочисленный результат
        if operation == '/':
            num = random.randint(*num_range)
            # Чтобы деление было целочисленным
            num1 = num1 * num
            question_parts = [str(num1)]  # Пересчитываем первый операнд
            question_parts.append('/')
            question_parts.append(str(num))
            break  # Ограничиваем до одного деления для безопасности
        else:
            num = random.randint(*num_range)
            question_parts.append(operation)
            question_parts.append(str(num))
            num1 = eval(f"{num1} {operation} {num}")
    question = ' '.join(question_parts)
    try:
        # Безопасное вычисление, ограничим операции
        allowed_ops = {'+': operator.add, '-': operator.sub, '*': operator.mul, '/': operator.truediv}
        expr = question
        # Рассчитываем ответ
        answer = eval(expr, {"__builtins__": None}, allowed_ops)
        if isinstance(answer, float):
            if answer.is_integer():
                answer = int(answer)
            else:
                # Если результат не целый, повторяем генерацию
                return generate_math_question(npc_level)
    except:
        # В случае ошибки повторяем генерацию
        return generate_math_question(npc_level)
    return question, answer

# Функция для генерации предмета на основе уровня NPC
def generate_item(npc_level):
    # Выбираем случайный тип предмета
    item_type_entry = random.choice(items_types)
    item_type = item_type_entry['type']
    item_name = random.choice(item_type_entry['category_names'])
    stat = item_type_entry['stat']
    # Увеличиваем значения параметров на основе уровня NPC
    stat_value = random.randint(
        item_type_entry['stat_value_range'][0] + npc_level,
        item_type_entry['stat_value_range'][1] + npc_level
    )
    return {
        'type': item_type,
        'name': item_name,
        'stat': stat,
        'value': stat_value
    }

# Функция обработки диалога с NPC
def handle_dialog(npc):
    global state, current_question, correct_answer, player_answer, inventory, console_message, tasks_solved, tasks_total
    # Генерируем вопрос
    current_question, correct_answer = generate_math_question(npc['level'])
    state = 'dialog'
    player_answer = ''
    # Определяем предмет, который даст NPC в зависимости от уровня знания
    received_item = generate_item(npc['level'])
    console_message = f"{npc['name']} предлагает вам {received_item['type']} {received_item['name']} ({received_item['stat']} +{received_item['value']}). Решите задачу, чтобы получить его."

    # Главный цикл диалога
    while state == 'dialog':
        clock.tick(FPS)
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                sys.exit()
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_ESCAPE:
                    console_message = "Диалог завершён без решения задачи."
                    state = 'move'
                    npc['cooldown'] = time.time() + 60  # Устанавливаем 1 минуту cooldown
                elif event.key == pygame.K_RETURN:
                    if player_answer:
                        try:
                            user_answer = int(player_answer)
                            if user_answer == correct_answer:
                                inventory.append(received_item)
                                console_message = f"Вы заслужили этот предмет: {received_item['name']}."
                                tasks_solved +=1
                                tasks_total +=1
                                # Обновление процента верных
                                update_level()
                                # Обновляем статус NPC
                                npc['task_solved'] = True
                                state = 'move'
                            else:
                                console_message = f"Неправильный ответ! Правильный ответ: {correct_answer}."
                                tasks_total +=1
                                state = 'move'
                                npc['cooldown'] = time.time() + 60  # Устанавливаем 1 минуту cooldown
                        except:
                            console_message = "Пожалуйста, введите корректный ответ."
                            player_answer = ''
                    else:
                        # Если нет ответа, выходим из диалога
                        console_message = "Диалог завершён без решения задачи."
                        state = 'move'
                        npc['cooldown'] = time.time() + 60  # Устанавливаем 1 минуту cooldown
                elif event.key == pygame.K_BACKSPACE:
                    player_answer = player_answer[:-1]
                else:
                    if event.unicode.isdigit() or (event.unicode == '-' and len(player_answer)==0):
                        player_answer += event.unicode

        # Рисуем основной экран
        draw_window()
        # Отображаем информацию диалога в консоли
        pygame.draw.rect(WIN, LIGHT_BLUE, (0, GAME_HEIGHT, WIDTH, HEIGHT - GAME_HEIGHT))
        # Первая строка: предложение предмета
        dialog_line1 = console_font.render(f"{npc['name']} предлагает вам {received_item['type']} {received_item['name']} ({received_item['stat']} +{received_item['value']}). Решите задачу, чтобы получить его.", True, BLACK)
        WIN.blit(dialog_line1, (10, GAME_HEIGHT + 10))
        # Вторая строка: сама задача
        if current_question and correct_answer is not None:
            dialog_line2 = console_font.render(f"Задача: {current_question}", True, BLACK)
            WIN.blit(dialog_line2, (10, GAME_HEIGHT + 40))
        # Третья строка: поле ввода ответа
        input_prompt = console_font.render("Ответ:", True, BLACK)
        input_field = console_font.render(player_answer, True, BLACK)
        WIN.blit(input_prompt, (10, GAME_HEIGHT + 70))
        WIN.blit(input_field, (70, GAME_HEIGHT + 70))

        pygame.display.update()

# Функция для взаимодействия с NPC
def interact_with_npc(npc):
    global state, current_npc
    current_npc = npc
    handle_dialog(npc)

# Хранение активных подземелий
active_dungeons = {}

# Функция для генерации карты подземелья (25x25 клеток)
def generate_dungeon_map():
    DUNGEON_SIZE = 25  # 25x25 клеток
    dungeon_map = [[0 for _ in range(DUNGEON_SIZE)] for _ in range(DUNGEON_SIZE)]
    # Простая генерация подземелья: можно усложнить
    for y in range(DUNGEON_SIZE):
        for x in range(DUNGEON_SIZE):
            dungeon_map[y][x] = random.choice([0, 1])  # 0 - стенка, 1 - проходимо
    # Гарантируем выход
    dungeon_map[0][0] = 1
    dungeon_map[DUNGEON_SIZE-1][DUNGEON_SIZE-1] = 1
    return dungeon_map

# Функция для входа в подземелье
def enter_dungeon():
    global state, current_dungeon, player_pos, current_dungeon_coord
    state = 'dungeon'
    current_dungeon_coord = current_region_coord
    if current_dungeon_coord not in active_dungeons:
        active_dungeons[current_dungeon_coord] = generate_dungeon_map()
    current_dungeon = active_dungeons[current_dungeon_coord]
    # Сброс позиции игрока в подземелье
    player_pos = [GAME_WIDTH // 2, GAME_HEIGHT // 2]

# Функция для выхода из подземелья
def exit_dungeon():
    global state, player_pos
    state = 'move'
    # Возвращаем игрока на поверхность в центр региона
    player_pos = [GAME_WIDTH // 2, GAME_HEIGHT // 2]

# Функция для входа в подземелье при приближении к входу
def check_dungeon_entry():
    global state
    for entrance in current_region.dungeon_entrances:
        entrance_rect = pygame.Rect(entrance[0], entrance[1], 40, 40)
        player_rect = pygame.Rect(player_pos[0], player_pos[1], player_size, player_size)
        if player_rect.colliderect(entrance_rect.inflate(10, 10)):  # Приближение
            EnterDungeonPrompt()

# Функция для отображения подземелья
def draw_dungeon():
    DUNGEON_SIZE = 25
    TILE_SIZE = 20  # Размер клетки подземелья
    dungeon_surface = pygame.Surface((DUNGEON_SIZE * TILE_SIZE, DUNGEON_SIZE * TILE_SIZE))
    dungeon_surface.fill(DARK_GRAY)

    for y in range(DUNGEON_SIZE):
        for x in range(DUNGEON_SIZE):
            if current_dungeon[y][x] == 1:
                pygame.draw.rect(dungeon_surface, WHITE, (x*TILE_SIZE, y*TILE_SIZE, TILE_SIZE, TILE_SIZE))
            else:
                pygame.draw.rect(dungeon_surface, BLACK, (x*TILE_SIZE, y*TILE_SIZE, TILE_SIZE, TILE_SIZE))
            pygame.draw.rect(dungeon_surface, GRAY, (x*TILE_SIZE, y*TILE_SIZE, TILE_SIZE, TILE_SIZE), 1)

    # Отображение подземелья на экране
    WIN.blit(dungeon_surface, (GAME_WIDTH//2 - DUNGEON_SIZE*TILE_SIZE//2, GAME_HEIGHT//2 - DUNGEON_SIZE*TILE_SIZE//2))

    # Кнопка выхода из подземелья
    exit_button = pygame.Rect(WIDTH - 110, 10, 100, 30)
    pygame.draw.rect(WIN, GRAY, exit_button)
    exit_text = font.render("Выйти", True, BLACK)
    WIN.blit(exit_text, (exit_button.x + 25, exit_button.y + 5))
    # Проверка наведения на кнопку
    mouse_pos = pygame.mouse.get_pos()
    mouse_pressed = pygame.mouse.get_pressed()
    if exit_button.collidepoint(mouse_pos):
        pygame.draw.rect(WIN, YELLOW, exit_button, 2)
        if mouse_pressed[0]:
            exit_dungeon()

    pygame.display.update()

# Функция для входа в подземелье при приближении
def EnterDungeonPrompt():
    global state
    prompt_running = True
    while prompt_running and state == 'move':
        clock.tick(FPS)
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                sys.exit()
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_y:
                    # Войти в подземелье
                    enter_dungeon()
                    prompt_running = False
                elif event.key == pygame.K_n or event.key == pygame.K_ESCAPE:
                    # Отказаться
                    console_message = "Вы отказались войти в подземелье."
                    prompt_running = False

        # Обновление экрана
        draw_window()
        # Отрисовка запроса
        prompt_text = font.render("Войти в подземелье? (Y/N)", True, BLACK)
        WIN.blit(prompt_text, (10, GAME_HEIGHT + 40))
        pygame.display.update()

# Функция для экипировки предмета
def equip_item(idx):
    global equipped, inventory, console_message, attack, defense
    item = inventory.pop(idx)
    if item['type'] == 'Меч':
        if len(equipped['Меч']) < 2:
            equipped['Меч'].append(item)
            console_message = f"Вы экипировали меч: {item['name']} (Атака +{item['value']})"
        else:
            console_message = "Максимум два меча уже экипированы."
            inventory.insert(idx, item)  # Возвращаем предмет обратно
    elif item['type'] == 'Щит':
        if equipped['Щит']:
            console_message = f"Сейчас экипирован щит: {equipped['Щит']['name']}. Смените его перед экипировкой нового щита."
            inventory.insert(idx, item)  # Возвращаем предмет обратно
        else:
            equipped['Щит'] = item
            console_message = f"Вы экипировали щит: {item['name']} (Защита +{item['value']})"
    elif item['type'] == 'Доспех':
        if equipped['Доспех']:
            console_message = f"Сейчас экипированы доспехи: {equipped['Доспех']['name']}. Смените их перед экипировкой новых доспехов."
            inventory.insert(idx, item)  # Возвращаем предмет обратно
        else:
            equipped['Доспех'] = item
            console_message = f"Вы экипировали доспехи: {item['name']} (Защита +{item['value']})"
    recalculate_stats()

# Функция для перехода между регионами
def move_to_region(new_x, new_y, direction):
    global current_region_coord, current_region, player_pos, console_message, state
    # Проверяем границы мира
    if not (0 <= new_x < WORLD_SIZE and 0 <= new_y < WORLD_SIZE):
        console_message = "Вы не можете выйти за пределы мира."
        return

    # Устанавливаем новую позицию игрока в зависимости от направления
    if direction == 'right':
        player_pos[0] = 0
    elif direction == 'left':
        player_pos[0] = GAME_WIDTH - player_size
    elif direction == 'down':
        player_pos[1] = 0
    elif direction == 'up':
        player_pos[1] = GAME_HEIGHT - player_size

    current_region_coord = (new_x, new_y)
    current_region = get_region(*current_region_coord)
    assign_territories(new_x, new_y)
    console_message = "Вы перешли в новый регион."

# Основной игровой цикл
def main():
    global state, player_pos, current_dungeon
    running = True
    while running:
        clock.tick(FPS)
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False

        keys = pygame.key.get_pressed()
        if state == 'move':
            movement = False
            direction = None
            new_x, new_y = current_region_coord
            # Перемещение игрока
            if keys[pygame.K_LEFT]:
                if player_pos[0] - player_speed > 0:
                    player_pos[0] -= player_speed
                else:
                    # Переход в левый регион
                    new_x = current_region_coord[0] -1
                    new_y = current_region_coord[1]
                    direction = 'left'
                    movement = True
            if keys[pygame.K_RIGHT]:
                if player_pos[0] + player_speed + player_size < GAME_WIDTH:
                    player_pos[0] += player_speed
                else:
                    # Переход в правый регион
                    new_x = current_region_coord[0] +1
                    new_y = current_region_coord[1]
                    direction = 'right'
                    movement = True
            if keys[pygame.K_UP]:
                if player_pos[1] - player_speed > 0:
                    player_pos[1] -= player_speed
                else:
                    # Переход в верхний регион
                    new_x = current_region_coord[0]
                    new_y = current_region_coord[1] -1
                    direction = 'up'
                    movement = True
            if keys[pygame.K_DOWN]:
                if player_pos[1] + player_speed + player_size < GAME_HEIGHT:
                    player_pos[1] += player_speed
                else:
                    # Переход в нижний регион
                    new_x = current_region_coord[0]
                    new_y = current_region_coord[1] +1
                    direction = 'down'
                    movement = True

            if movement:
                move_to_region(new_x, new_y, direction)

            # Проверка на вход в подземелье
            check_dungeon_entry()

            # Проверяем близость к NPC
            for npc in current_region.npcs:
                player_rect = pygame.Rect(player_pos[0], player_pos[1], player_size, player_size)
                npc_rect = pygame.Rect(npc['pos'][0], npc['pos'][1], 50, 50)
                # Взаимодействие только при близком расстоянии
                if player_rect.colliderect(npc_rect.inflate(6, 6)) and not npc['task_solved'] and time.time() >= npc['cooldown']:
                    interact_with_npc(npc)
                    break  # Один NPC за раз

        elif state == 'dungeon':
            # Управление внутри подземелья
            if keys[pygame.K_LEFT]:
                player_pos[0] -= player_speed
            if keys[pygame.K_RIGHT]:
                player_pos[0] += player_speed
            if keys[pygame.K_UP]:
                player_pos[1] -= player_speed
            if keys[pygame.K_DOWN]:
                player_pos[1] += player_speed

            # Ограничение движения внутри подземелья
            DUNGEON_SIZE = 25
            TILE_SIZE = 20
            dungeon_width = DUNGEON_SIZE * TILE_SIZE
            dungeon_height = DUNGEON_SIZE * TILE_SIZE
            if player_pos[0] < 0:
                player_pos[0] = 0
            if player_pos[0] + player_size > dungeon_width:
                player_pos[0] = dungeon_width - player_size
            if player_pos[1] < 0:
                player_pos[1] = 0
            if player_pos[1] + player_size > dungeon_height:
                player_pos[1] = dungeon_height - player_size

        draw_window()

        if state == 'dungeon':
            draw_dungeon()

    pygame.quit()
    sys.exit()

# Функция для взаимодействия с входом в подземелье
def check_dungeon_entry():
    global state
    for entrance in current_region.dungeon_entrances:
        entrance_rect = pygame.Rect(entrance[0], entrance[1], 40, 40)
        player_rect = pygame.Rect(player_pos[0], player_pos[1], player_size, player_size)
        if player_rect.colliderect(entrance_rect.inflate(10, 10)):  # Приближение к входу
            EnterDungeonPrompt()
            break  # Только один вход за раз

# Функция для пересчёта параметров игрока
def recalculate_stats():
    global attack, defense
    attack = sum([item['value'] for item in equipped['Меч']])
    shield_def = equipped['Щит']['value'] if equipped['Щит'] else 0
    armor_def = equipped['Доспех']['value'] if equipped['Доспех'] else 0
    defense = shield_def + armor_def

# Функция для отображения приглашения войти в подземелье
def EnterDungeonPrompt():
    global state, current_dungeon_coord
    prompt_running = True
    while prompt_running and state == 'move':
        clock.tick(FPS)
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                sys.exit()
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_y:
                    # Войти в подземелье
                    enter_dungeon()
                    prompt_running = False
                elif event.key == pygame.K_n or event.key == pygame.K_ESCAPE:
                    # Отказаться
                    console_message = "Вы отказались войти в подземелье."
                    prompt_running = False

        # Обновление экрана
        draw_window()
        # Отрисовка запроса
        prompt_text = font.render("Войти в подземелье? (Y/N)", True, BLACK)
        WIN.blit(prompt_text, (10, GAME_HEIGHT + 40))
        pygame.display.update()

# Запуск игры
if __name__ == "__main__":
    main()


Надо изменить следующее: 1)квадраты карты справа сделать примерно в полтора раза меньше. За этот счет область, которую занимает карта, тоже уменьшить. Область карты должна быть ограничена областью 30 на 30 квадратов. Ниже этой области начинается серая область инвентаря. Ее можно сделать открытой всегда, а не пытаться отрисовывать поверх остальных объекто. Чтобы заработанные предметы помещались, можно их располагать в списке с вертикальной полосой прокрутки. 2)Когда пользователь попадает в подземелье, оно отображается таким же экраном как и отдельные экраны надземного мира. Масштаб и размеры ячеек сохраняются как  в надземном мире. По подземельям так же можно перемещаться. При этом место входа/выхода из подземелья совпадает с его координатами на экране надземного мира. 3)Внутренний мир пещер (промежуточные стены, лабиринты) рисовать не нужно. Можно нарисовать внешнюю черную стену по периметру пещеры (как рамка). Важно: пещера отображается не поверх окна надземного мира (так рябит), а вместо него, так же как менялись экраны при переходе по карте влево и вправо.