Ты написал код:
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  # Размер клетки карты

# Инициализация переменных для карты и инвентаря
map_area_height = 280  # Высота области карты
inventory_scroll_offset = 0
SCROLL_STEP = 20

# Уровни сложности задач и соответствующие территории
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 = ""

# Переменные для позиции игрока в надземном мире и подземельях
overworld_player_pos = player_pos.copy()
dungeon_player_pos = [0, 0]

# Функция для пересчёта параметров игрока
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} знаний."

# Функция для открытия окна инвентаря (не используется, т.к. инвентарь всегда отображается)
def open_inventory():
    pass

# 1) Изменение функции отрисовки игровой области и инвентаря
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, map_area_height))
    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]
                if region.dungeon_entrances:
                    pygame.draw.rect(WIN, DUNGEON_COLOR, (GAME_WIDTH + 10 + x * MAP_TILE_SIZE, 40 + y * MAP_TILE_SIZE, MAP_TILE_SIZE, MAP_TILE_SIZE))
                else:
                    pygame.draw.rect(WIN, color, (GAME_WIDTH + 10 + x * MAP_TILE_SIZE, 40 + y * MAP_TILE_SIZE, MAP_TILE_SIZE, MAP_TILE_SIZE))
            else:
                # Незаполненные регионы отображаются чёрным
                pygame.draw.rect(WIN, BLACK, (GAME_WIDTH + 10 + x * MAP_TILE_SIZE, 40 + y * MAP_TILE_SIZE, MAP_TILE_SIZE, MAP_TILE_SIZE))

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

# Функция для рисования инвентаря
def draw_inventory():
    # Отрисовка инвентаря ниже карты
    inventory_area_rect = pygame.Rect(GAME_WIDTH, map_area_height, MAP_WIDTH, HEIGHT - map_area_height)
    pygame.draw.rect(WIN, LIGHT_GRAY, inventory_area_rect)
    # Заголовок инвентаря
    inventory_title = large_font.render("Инвентарь", True, BLACK)
    WIN.blit(inventory_title, (GAME_WIDTH + MAP_WIDTH // 2 - inventory_title.get_width() // 2, map_area_height + 10))

    # Отображение предметов в инвентаре с прокруткой
    item_start_y = map_area_height + 50 - inventory_scroll_offset
    for idx, item in enumerate(inventory):
        # Рисуем кнопку "+"
        button_rect = pygame.Rect(GAME_WIDTH + 10, item_start_y + 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, item_start_y + idx * 30))

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

    # Отображение активного снаряжения в инвентаре
    equipped_text = font.render("Активное снаряжение:", True, BLACK)
    WIN.blit(equipped_text, (GAME_WIDTH + 10, map_area_height + 200))
    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, map_area_height + 220))
    WIN.blit(shield_text, (GAME_WIDTH + 30, map_area_height + 240))
    WIN.blit(armor_text, (GAME_WIDTH + 30, map_area_height + 260))

    # Полоса прокрутки
    scrollbar_rect = pygame.Rect(GAME_WIDTH + MAP_WIDTH - 20, map_area_height + 50, 10, HEIGHT - map_area_height - 60)
    pygame.draw.rect(WIN, GRAY, scrollbar_rect)
    # Ползунок
    content_height = len(inventory) * 30
    if content_height > scrollbar_rect.height:
        slider_height = max(int(scrollbar_rect.height * scrollbar_rect.height / content_height), 20)
        slider_pos = int(scrollbar_rect.y + inventory_scroll_offset * scrollbar_rect.height / content_height)
        slider_rect = pygame.Rect(scrollbar_rect.x, slider_pos, scrollbar_rect.width, slider_height)
        pygame.draw.rect(WIN, DARK_GRAY, slider_rect)

# Функция для рисования основного окна (игровой области и карты)
def draw_window():
    if state == 'move':
        draw_game_area()
    elif state == 'dungeon':
        draw_dungeon_area()

    draw_map()
    draw_inventory()

    # Информационная консоль внизу
    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()

# 2) Функция для отображения подземелья
def draw_dungeon_area():
    # Отрисовка подземелья
    pygame.draw.rect(WIN, DARK_GRAY, (0, 0, GAME_WIDTH, GAME_HEIGHT))
    # Рисуем внешние стены
    pygame.draw.rect(WIN, BLACK, (0, 0, GAME_WIDTH, GAME_HEIGHT), 10)  # Стены толщиной 10 пикселей
    # Рисуем игрока
    pygame.draw.rect(WIN, player_color, (player_pos[0], player_pos[1], player_size, player_size))
    # Кнопка выхода из подземелья
    exit_button = pygame.Rect(GAME_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()

# Хранение активных подземелий
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)

# Функция для входа в подземелье
def enter_dungeon(entrance_pos):
    global state, current_dungeon, player_pos, current_dungeon_coord, overworld_player_pos, dungeon_player_pos
    state = 'dungeon'
    current_dungeon_coord = current_region_coord
    if current_dungeon_coord not in active_dungeons:
        active_dungeons[current_dungeon_coord] = {}  # Мы не генерируем карту подземелья
    current_dungeon = active_dungeons[current_dungeon_coord]
    # Сохраняем позицию игрока в надземном мире
    overworld_player_pos = player_pos.copy()
    # Устанавливаем позицию игрока в подземелье
    player_pos = entrance_pos.copy()
    dungeon_player_pos = player_pos.copy()

# Функция для выхода из подземелья
def exit_dungeon():
    global state, player_pos, overworld_player_pos
    state = 'move'
    # Восстанавливаем позицию игрока в надземном мире
    player_pos = overworld_player_pos.copy()

# Функция для входа в подземелье при приближении к входу
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(entrance)
            break  # Только один вход за раз

# Функция для отображения приглашения войти в подземелье
def EnterDungeonPrompt(entrance_pos):
    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(entrance_pos)
                    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
    if idx >= len(inventory):
        return
    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, inventory_scroll_offset
    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:
                if event.button == 4:  # Прокрутка вверх
                    inventory_scroll_offset = max(inventory_scroll_offset - SCROLL_STEP, 0)
                elif event.button == 5:  # Прокрутка вниз
                    inventory_scroll_offset += SCROLL_STEP

        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] and player_pos[0] - player_speed > 10:
                player_pos[0] -= player_speed
            if keys[pygame.K_RIGHT] and player_pos[0] + player_speed + player_size < GAME_WIDTH - 10:
                player_pos[0] += player_speed
            if keys[pygame.K_UP] and player_pos[1] - player_speed > 10:
                player_pos[1] -= player_speed
            if keys[pygame.K_DOWN] and player_pos[1] + player_speed + player_size < GAME_HEIGHT - 10:
                player_pos[1] += player_speed

        draw_window()

    pygame.quit()
    sys.exit()

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

Надо изменить следующее: 1)Сделать список профессий гораздо больше: огромный словарь из 1000 вариантов (самые разные, в том числе современные. Например, стоматолог, зоотехник, крановщик, газосварщик и пр.). Но важно чтобы надписи не оказались слишком длинные, поэтому старайся использовать названия длиной в одно слово. 2)Сделать список имен гораздо больше: огромный словарь в 1000 имен (американские, китайские, индийские, африканские, славянские и пр.), но тоже не слишком длинные. 3)Можно писать профессию и имя две строки. 3)В пещере выход находится на тех же координатах экранах, что и черный квадрат на экране надземного мира. Это важно чтобы выйти можно было не в любой момент. Отрисовывается в виде светлого квадрата. 4)Когда выходишь из пещеры, чтобы тут же не попадать в диалоговое окно повторного входа в пещеру, можно смещать персонажа на несколько пикселей в сторону от черного квадрата входа. 5) У тебя не получается сделать список с полосой прокрутки. Я себе представлял это списком с полосой прокрутки не на всю нижнюю область экрана. Иначе список с вещами начинает наползать на перечень надетых вещей. 6)Предлагаю перечень надетых вещей перенести в основное пространство под строчками с атакой, защитой и пр. 7) Добавь в блок параметров персонажа параметр Жизнь с изначальным значением 10. Над синим квадратиком персонажа узкой линией нарисуй линию жизни. 8)Сделай расстояние между строчками меньше в два раза, чтобы они были компактнее. 9)Добавь аккуратную рамку вокруг блока текста с атакой, защитой, жизнью, процентом верных, уровнем и активным снаряжением, 10)Проверь еще раз, чтобы примеры с делением давали целочисленный ответ, 11)В пещере обитают монстры (не более 10 штук в пещере). Они должны рисоваться красными квадратами тоже со значением жизни сверху. Они перемещаются по пещере, но в момент входа персонажа находятся рядом с ее краями.