
Движок для запуска квестов
Когда-то увлёкся написанием ботов для телеграмм. В частности — созданием квестов с отображением в чате картинок и кнопок выбора дальнейшего развития событий. Подумал: «Я ж программист. Сейчас как напрограммирую квестов!»
Но не тут-то было. Несмотря на мой опыт работы с различными языками, создание квеста с помощью классического императивного подхода не позволяло сосредоточиться на сюжете — я лишь погрязал в асинхронных вызовах API. Пришёл к выводу, что весь сюжет квеста должен быть представлен не в виде низкоуровневого кода на Python или C++, который я обычно использую, а в виде графа развития событий.
Этот граф нужно было как-то сохранять, редактировать и считывать для запуска. Так как на Си++ имеется отличный парсер nlohman/json, то на JSON я и остановился. Квест хранился в виде JSON, а для выполнения квеста пришлось написать свой движок. На Си++ я создал движок, который бы позволял выполнять шаг за шагом инструкции квеста, оформленные в виде JSON программы. Тогда еще писал весь код сам своими ручками дедовским методом.
Движок отвечает за одновременное выполнение кода всех существующих в проекте квестов и обработку событий от пользователей. Таким образом пользователям в чат с ботом отправляются сообщения и ожидается реакция пользователя. Если пользователь нажимает одну из нескольких возможных кнопок определяющих развитие событий в квесте, то движок ведёт этого пользователя по своей линии событий, которая не касается всех остальных.
Сначала казалось, что вообще не имеет значения прожорливость движка ведь пользователь меняет состояние очень редко, в основном читая текст и просматривая картинки. Но потом оказалось что для нормальной игры нужно отслеживать отправленные в чат сообщения, стирать их и редактировать. Движок должен был поддерживать одновременно огромное количество пользователей и огромное количество квестов. Они не должны были блокировать друг друга ни при каких обстоятельствах. Каждый пользователь, проходящий квест имеет своё состояние и в теории движок должен выдерживать десятки тысяч пользователей онлайн.
Задача усложнялась с каждым нововведением и улучшением. Сначала в квесте поддерживались только блоки паузы, вывода изображения, оператор безусловного перехода наподобие GOTO, вывода текста и вопроса с ветвлением в зависимости от ответа. По сути мной был реализован свой язык программирования и виртуальная машина для выполнения программ на этом языке.
Фантазию несло и я понимал, что нужны новые операторы, чтобы можно было сделать полноценный квест. Не хватало возможности для того, чтобы в сюжете после прохождения сюжетной ветки при определенных условиях можно было отметить в переменной например, что определенный предмет найден или произошло ещё какое-то событие. Для этого пришлось добавить поддержку переменных boolean ( флажки, которые могут принимать значение только правда или ложь) . А соответственно чтобы воспользоваться таким флажком понадобился еще блок условного перехода: выполнение квеста проходит по одной или другой из веток, в зависимости от состояния флажка. Это дало возможность сохранить состояние "меч найден", "дверь открыта", "охранник подкуплен", "умение получено" и строить нелинейный сюжет, где одна длинная ветка квеста будет вести себя по разному в зависимости от состояния переменной, установленной совсем в другом месте квеста.
Вместо обычных текстовых сообщений используется HTML форматированный текст. Причем, в Telegram не поддерживается классический HTML, а доступно очень ограниченное количество тэгов. Кстати, именно такие моменты мешали без костылей использовать существующие инструменты для создания квестов такие как Twine, Ink, RenPy - они другие и нужно слишком много усилий чтобы перепилить под Telegram.
Пример кода квеста на JSON, который выполняется на движке квестов:
Скрытый текст
"columns": [ { "id": "1a322404-268f-474a-8136-46b8f182793c", "name": "Главная", "blocks": [ { "id": "bb42c18d-5013-4427-b70f-ebbe5fcc27e9", "type": "Text", "value": "В школьном канале появляется компромат на Максима: опубликован скриншот его переписки с Лизой с интимными подробностями. Затем его аккаунт удаляется.\n\nВ школе начинается паника. Все уверены, что виноват кто-то из одноклассников Макса.\n\nИгрок — школьный журналист, который должен выяснить пра вду и опубликовать в сообществе @questogramworld." }, { "id": "1059f7af-941f-4ab8-9a58-d16fdf992076", "type": "Image", "value": "/images/df30b172-e38f-4803-91c7-a0c6bf3fbd38/ebf4c658b946489da1126d92cbd0751d.png" }, { "id": "caf9cfd0-34d3-44f7-ad64-fc291c00852a", "type": "Pause", "value": 3 }, { "id": "620550df-574c-4b3f-aebc-769a2d7f47da", "type": "Question", "buttons": [ { "id": "bbdbc731-a038-44d3-bcac-2701bc9c481c", "button": "Поговорить с Максом" }, { "id": "bd1a7b59-2035-4f33-8aa1-0cbaadf2ebe4", "button": "Найти кто первым распространил скриншот" } ], "question": "Что сделать сначала" }, { "id": "bd1a7b59-2035-4f33-8aa1-0cbaadf2ebe4", "type": "Text", "value": "Поговорив с учениками, ты приходишь к выводу, что первоначальное сообщение было удалено, а копии скриншота передавались между учениками весь вчерашний вечер примерно в одно и то, же время.\n\nМногие ученики, после обсуждения стерли это сообщение, посчитав хранение таких интимных подробностей сли шком вызывающим.\n\nНайти способа в какое время была отправлена первая копия не представилось никакого шанса." }, { "id": "b3caa50e-860f-470d-bfba-6b22c7fa3ea5", "type": "Image", "value": "/images/df30b172-e38f-4803-91c7-a0c6bf3fbd38/c3d24872acbc432ea4497de8d61e6e7a.png" }, { "id": "33bce4ee-5552-493e-ba52-b1e1d7f05619", "type": "GoTo", "value": "620550df-574c-4b3f-aebc-769a2d7f47da" } ] },
Писать JSON с сюжетом оказалось чуть проще, чем кодить на Си++, но всё равно затратно по времени и крайне сложно при объемных квестах. Написать хоть сколько нибудь рабочий квест даже с таким подходом было не реально. Хотелось бы иметь редактор-конструктор вроде Scratch или даже подобный интерфейсу ComfyUI, который позволил бы любому ребенку придумать свой шедевр и реализовать придуманный сюжет. Хотелось, чтобы эти квесты мог создавать кто угодно без высшего образования и не ждать джва года когда кто-то сделает квест где можно грабить корованы и где пользователь может играть лесными эльфами, охраной дворца и злодеем.

Когда виртуальная машина заработала и пришло время сделать визуальный редактор для создания сюжета квеста, то выяснилось что мой скилл во фронте и в частности в ReactJS мягко говоря низок. В 2024 году даже топовые LLM не могли помочь мне собрать рабочий ReactJS-интерфейс. Через год ситуация кардинально изменилась: заручившись поддержкой Claude Opus 4.8 удалось реализовать полноценный редактор, хотя опыт фронтенд-разработки у меня был не фонтан.
Увы, пока я реализовывал свою идею с квестами, в РФ Телеграмм начал терять свою актуальность. Пришедшая на замену альтернатива совсем не подходит для этой цели потому что создать своего бота можно только сдав анализы через госуслуги на юрлицо. Но раз уж гигантская часть пути уже пройдена, я всё равно решил запустить проект и уверен, что найдутся ценители, кому такой конструктор придется по душе
Квесты
Сейчас в проекте есть несколько готовых квестов, которые можно выбрать в списке и пройти без регистрации и СМС. Находим на сайте проекта список квестов, кликаем, заходим в телеграм чат с ботом и проходим квест.

Конструктор квестов
А вот чтобы создать квест, придётся немного разобраться с конструктором. Залогиниться можно как с компьютера, так и с любого гаджета. Но мелкий экран телефона вообще не способствует размещению элементов квеста на экране. Поэтому пока только компьютер или планшет.
Сейчас из доступных операторов в моём движке для квестов поддерживаются следующие блоки: текст, изображение, пауза, вопрос, задание значения переменной, условный переход в зависимости от состояния переменной, безусловный переход и завершение ветки.

Генератор изображений на основе модели Z-Image Turbo
Квесты без визуализации были актуальны полвека назад, а сейчас хочется картинок как минимум. В идеале, было бы неплохо еще добавить видеовставки. Телеграмм их поддерживает. Пока с видео не заморачивался. Сейчас можно загрузить свои изображения.
Понимая, что практически никто не будет рисовать изображения ручками, для ускорения работы прикрутил к интерфейсу генератор картинок на основе модели Z-Image Turbo чтобы каждый мог снабдить свой квест графикой на любой вкус. Генерация изображений - не самое дешёвое удовольствие, если использовать топовые модели. Поэтому как компромис к сайту была прикручена возможность генерирования с помощью локальной модели на GPU. Так, сейчас генерирование одной картинки занимает 2-4 секунды.

Заключение
Этот проект - лишь результат эксперимента и хобби. Я никогда не фанател по квестам (за исключением квестов 80х-90х, таким как Space Quest или Full Throttle), не состоял ни в каких группах по этой тематике и вообще не в курсе что сейчас модно, какие тренды, что там в мейнстриме. Поэтому есть определенная доля скепсиса, что сегодня такой нишевый проект в условиях массовых потоков дешевого дофамина вообще никому не будет интересен, но попробовать обязательно стоит. Всех, кому интересна эта тема, жду в сообществе для обсуждения как можно развивать этот проект: https://t.me/questogramworld
