в каком формате сохранения игр
В каком формате сохранять графику и анимацию для 2d игры под Android?
Добрый день. Сама я художник. Но решила попробовать себя в создании игр. Т.е. идея и графика моя, а программиста буду искать. Подошла к концу разработки графики и возникли несколько вопросов:
1) В каком формате сохранять графику для программиста? (Игра 2d под мобильные платформы, рисовалась в векторе)
2) В каком формате сохранять анимацию для игры? (Анимация делалась во флеше)
3) Графика создавалась под разные размеры экранов, самый большой под Galaxy7. Читала, что нужно сохранять по папкам все варианты разрешений, а потом программно игра будет выбирать нужный размер. Но тогда получается огромный вес игры из-за такого количества графики. Или нет? Объясните, пожалуйста, как это происходит.
Буду благодарна за ответы. Потому что хочется понять, как это работает и не хочется облажаться перед программистом)
Сначала найдите программиста, потом он найдет или напишет движок, на котором будет делать вашу игру.
После этого программист вам и скажет в каком формате ему будут нужны графика и анимация.
Вон оно как. Я почему-то была уверена, что, наоборот, сначала нужно всё сделать, а потом все готовые ресурсы отдавать программисту. А про вес игры не поясните? Потому что при экспорте одного слайда игры(например, меню) в png в самом большом разрешении (2560х1440 px) его размер равен примерно 1,5мб. Если посчитать количество графики, умноженное на количество вариантов разрешений (я делала для четырех) + анимация и звук, то получается просто огромный вес игры. Поэтому я и запуталась, в какой формат экспортировать. Может, всё намного проще, а, может, я сделала слишком тяжелую графику, которую придется переделывать О_о
Некоторые программисты сами могут из флеша экспортировать в нужном формате.
Анимации экспортируют в атласы, иногда разбивая на части, если делать на Air, то можно попробовать оставить в векторе, но все зависит от игры и анимаций.
Png можно оптимизировать, или сжать в jpg, можно разбить на составные и отдельно их компоновать.
Обычно используют 2-3 набора разрешений, можно делать ресурсы подгружаемыми отдельно, либо сдлать разные apk файлы для разных устройств с разным разрешением.
Спасибо большое за ответы. Всё стало более-менее понятно.
На самом деле Reviri сказал дело. Для начала надо сделать скелет приложения (т.е. движок), а потом дизайнер дорабатывает его.
Непричёсанные мысли по поводу формата сохранения: теория
Начнём с вводных. Мы разрабатываем программу, которая будет сохранять свои данные в файл, и при этом…
Таких программ на самом деле немало. Это AutoCAD, Photoshop, Microsoft Office (будем честными: даже пытаясь протащить его через ISO, «мелкомягкие» рассчитывали, что он будет совместим в первую очередь с самим собой).
И для простоты добавим ещё одно требование, которое отбросит все три этих программы, но довольно реалистичное (ему отвечают Windows 10 и куча программ помельче).
Я долго искал программу, которая отвечает всем этим требованиям, и наконец нашёл: Inkscape. Несмотря на то, что её файлы основаны на стандарте SVG, половина всей информации лежит в расширениях SVG — пространствах имён «sodipodi» и «inkscape».
В первой части постараюсь обойтись без программирования, зато объясню парочку важных концепций.
Собственно, у нас есть такие возможные форматы для хранения информации.
Формат первый. XML
Из форматов, доступных из коробки, самый распиаренный — XML. Если правильно писать код разбора, часть задач по обратной совместимости решаются автоматически: старая программа пропустит все лишние тэги. Остаётся сделать, чтобы новая читала документы, созданные старой — и дело в шляпе.
[+] Много стандартного инструментария.
[+] Оптимальный выбор, если мы храним размеченный текст.
[±] Можно (хотя и тяжело) редактировать файл вручную.
[−] Разбор может занимать много времени — от 20 до 95% всего времени загрузки. Эту неоптимальность даже обозвали «налогом на угловые скобки». По опыту загрузки (не полной) XLSX собственными силами: раззиповка — 0,3 с, разбор XML — 1,1 с, остальное — 0,6 с.
[−] Программировать своими силами очень опасно, возможны незаметные ошибки.
[−] Зачастую избыточен.
[−] Если в программе есть огромные массивы из малюсеньких объектов (звуковые волны, картинки, матрицы чисел), их приходится сохранять не-XML’ными мерами. Посмотрите, например, как хранятся кривые в SVG:
Будьте готовы к тому, что XML будет громадный, подчас больше, чем ваш документ занимает в памяти. Хотя не стоит так бояться больших файлов: обещаю, мы не будем грузить такую громадину в память.
Каким образом? Я знаю четыре подхода к чтению XML: на callback’ах (SAX), на потоках (StAX), на сопрограммах и DOM. А также два подхода к записи XML: прямая запись тэг за тэгом и DOM.
Чтение на callback’ах даёт сложный и путаный код, напоминающий конечный автомат. DOM расходует памяти в разы больше, чем структура предметной области. Сопрограммы — сложное дело и есть не везде. Так что наш выбор — чтение на потоках, запись тэг за тэгом. В обоих случаях расходуется пренебрежимо мало памяти, так что автоматически выполняется моё обещание не грузить громадину.
На языках, где мы управляем памятью вручную (Си), у потокового парсера есть ещё одна полезная черта. Управление памятью действует как унитазный бачок — потихоньку заполняется, мгновенно сливается — потому в самых быстрых из них действует собственное управление памятью. К сожалению, не получается добиться столь впечатляющей скорости, как в DOM (прежде чем выйти из функции getThing, надо «подобрать концы»). Так что у меня лично получилось (на Си++) сделать потоковый парсер в 2,5 раза медленнее, чем у рекордсмена PugiXML. Но это уже замечательный результат: научился грузить огромный XLSX втрое быстрее, чем Excel.
Формат второй. XML-лайты
«XML-лайтами» я называю языки наподобие XML, в которых единицы смысла — это строки текстового файла (иногда также агрегаты из нескольких строк). Наиболее известный — YAML.
Много лет назад, не накопав хорошего XML-инструментария для Delphi, за полдня я придумал XML-лайт с тупым названием Multilevel. Правда, тогда по неопытности работал только с DOM-разбором.
[+] Достаточно быстры.
[+] Редактируются вручную.
[±] Думаю, можно подобрать подходящий формат для огромных массивов из малюсеньких объектов.
[±] Легко запрограммировать своими силами.
[−] Даже если формат известный, вроде YAML, велика вероятность, что найдутся только DOM-подобные механизмы.
[−] Малопригодны для размеченного текста (и то придётся специально продумывать подходящий формат).
Формат третий. Двоичные XML-подобные коды
XML — формат текстовый. Но ничего не стоит перевести его в двоичный вид. Вот наобум придуманный XML-подобный двоичный формат (порядок байтов для наглядности взят Motorola):
На XML бы это выглядело так.
Я не открываю Америку, формат BIFF из Microsoft Office существует лет тридцать. Компания облажалась в двух местах: 1) Формат не иерархический, что сильно мешает дробить блоки; 2) блок ограничен 64 килобайтами, что решается вторым, третьим и т.д. блоком CONTINUE.
Недавно для целей медиаконтейнера Matroška сделали язык EBML — тот же XML, но двоичный. Конкретно с ним не знаком.
[+] Крайне быстры.
[+] Тэги, скорее всего, будут достаточно коротки, чтобы размеченный текст отнял даже меньше места в файле, чем в памяти.
[±] Легко запрограммировать своими силами.
[±] Огромные массивы из малюсеньких объектов можно сохранять как есть, в двоичном виде.
[−] Ручному редактированию не подлежат, для отладки потребуется программа-дампер.
Прочие форматы (и их недостатки)
Эти форматы лучше даже не рассматривать.
Текстовый или двоичный формат без расширяемой иерархической структуры нечего и придумывать. Раз мы хотим расширяемость — программа скоро вылезет из него, как из детской кроватки.
Любые сериализации через рефлексию (встроенные в Java, Delphi, C#). Годятся только для задач, где большая совместимость не нужна (уровни и сохранения игр, файлы настроек). Если вам удастся задействовать рефлексию и сэкономить строки — вы молодец, но программа не должна полагаться только на неё.
UPD. Чем плоха рефлексия? Костылями при серьёзных изменениях структуры файла.
SQLite продвигает свой формат БД как формат документа. По-моему, из-за сложностей программирования с ним имеет смысл работать именно как с СУБД.
JSON несколько проще в программировании, чем XML, но всё равно сложен. Плюс затруднено потоковое считывание: JSON различает объекты и массивы, и синтаксис JS говорит, что от перестановки атрибутов результат не меняется.
Хотя JSON подсказывает нам одну важную идею. А именно…
Идея 1. Или последовательность, или коллекция
Нашу жизнь очень сильно упростят два важных требования.
Любой тэг должен быть или последовательностью тэгов/блоков, или коллекцией тэгов/блоков. Объясню, что это такое.
Примечание. Я понимаю, что в HTML оба этих тэга, td и th, можно использовать как непарные, браузер разберётся. Но давайте будем считать, что имеем дело с XHTML и эти тэги парные.
Как бы мы переписали нашу несчастную таблицу под машинное сохранение?
Но при этом мы чётко понимаем, что внутри коллекции tbody находится коллекция tr. Но таблица — она двухмерная, и крайне мала вероятность, что внутри tr вставят что-то не являющееся клеткой таблицы — так что закрываем на это глаза.
А вот в нашем простом иерархически-двоичном формате так нельзя. Причина в том, что у тэга XML есть атрибуты, куда, если что, можно закинуть немножко информации. А у каталога двоичного формата нет ничего — потому в этот самый tr не впишешь никаких новых данных: ни стиля, ни соответствия электронного документа и бумажного источника… Потому приходится полностью исключать коллекции в коллекциях.
Выносите отдельные сущности, особенно необязательные, в тэг-контейнер. Заголовок к таблице можно описать двумя способами:
Второй лучше — больше места для расширения. Может, вы когда-нибудь станете оформлять части заголовка жирным и курсивом. А может, будут два заголовка (например, в начале таблицы и в продолжении, если её разорвало на две страницы). Кто знает?
Впрочем,
Идея 2. Extend, upgrade, break. Или расширить, модернизировать, порвать
Какую бы мы ни придумали конструкцию — когда-нибудь мы из неё вырастем. Пусть у нас редактор простейших уровней, например, для Super Mario. Изначально там был один «мир» (скажем, лес). Делаем так, чтобы были два, три и больше миров. Как это будет выглядеть в XML?
Новую программу можно научить открывать старый формат. Но старая программа не откроет нового. И тогда я предлагаю трёхстадийный «мягкий переход». Сколько длится каждая из стадий — зависит от того, насколько сложен формат, как вы заставляете пользователей обновлять программу и как вы готовы тестировать её на проектах старых версий.
(название намеренно сделано похожим на Microsoft’овское Embrace/Extend/Extinguish, да и суть та же).
Расширить: программа пишет старый формат, если в игре один мир, и в новый, если много.
Примерно через полгода-год происходит фаза Модернизировать: программа начинает писать только новый формат, всё ещё читая оба.
И наконец — может, через год-полтора, может, когда руки программиста снова дойдут до этого места и потребуется очередное расширить — даём приказ порвать: перестаём даже читать старый формат.
Идея 3. Работая с текстовыми файлами, используйте нейтральную локаль
«Странно», — подумал я, но девочка из ПФР оставила ман в сто страниц А4. Решив начать по-хорошему, я сел и стал его вдумчиво курить. Где-то на 40-й странице я наткнулся на указание сменить стандартный разделитель целой и дробной частей в винде на запятую, иначе программа не будет работать. Открыл, смотрю — ага, всё верно. Сменил на точку — программа ФОМС заработала, но перестала работать программа ПФР, выдавая всю ту же ошибку сохранения данных. Теперь пожилая женщина-кадровик обучена переключать разделитель, а я проникся небывалой нежностью к отечественным программистам.
Идея 4. Как синхронизировать версии программы у сотрудников, работающих над одним документом
Обычно это важно для внутренних утилит. Часто бывает, что несколько человек работают над одним документом разными версиями программы, и не все вовремя обновляют, отсюда мелкие потери данных. Предлагаю такой тэг.
Цифру partlyCompatibleWith поднимаем до текущей, когда делаем extend или break в чём-то важном. Можно её устанавливать умно в зависимости от того, сохраняли мы в старом формате или нет. Цифру fullyCompatibleWith поднимаем, когда добавляем любой тэг.
Если файл загружен программой версии меньше 25, вывести: «Настоятельно рекомендуем обновить программу. Есть вероятность, что важные данные потеряны».
Если файл загружен программой версии меньше 40, вывести: «Рекомендуем обновить программу. Есть вероятность, что какие-то данные потеряны.»
Если файл загружен какой-то очень новой программой, чей partlyCompatibleWith больше 42, тоже можно что-то вывести.
Если библиотека разбора под вашим контролем, можно поступить и так: если хоть один тэг был прочитан кодом загрузки не полностью, вывести предупреждение. Но делается это, повторяю, на уровне библиотеки разбора.
Также в новых версиях программы можно хранить чёрный список версий, которые часто падают или портят файлы — если замечено, что savedWith в чёрном списке, тоже что-то вывести.
В каком формате сохранения игр
Все сохранения записываются в директорию [ИМЯ ПОЛЬЗОВАТЕЛЯ]\AppData\LocalLow\Weather Factory\Cultist Simulator, в неё можно попасть как с помощью Проводника Windows, так и с помощью кнопки «открыть файл сохранения» с меню настроек в самой игре. Начав игру, видим приветствие и начальную ситуацию на столе:
Затем тут же сохранившись и выйдя в главное меню, открываем директорию для сохранений. Видим следующее:
Папка «mods» пустая, папка «Unity» содержит файлы движка игры (они нам не интересны), файлы «config.ini» и «Player.log» тоже, а вот последний файл, «save.txt» и является объектом нашего интереса. Открываем его.
Примечание №1: кому как, а я предпочитаю для просмотра файлов конфигурации использовать Notepad++ из-за удобства отображения структуры текста. В дальнейшем скриншоты будут именно из этой программы.
Мой файл занимает объём 62 килобайта и содержит много строк. Но не будем пугаться, а попытаемся вникнуть, что это за текст.
Кажется, что всё страшно и запутанно… Но давайте вглядимся внимательнее. Текст состоит из строк, сгруппированных под общими заголовками. Это код javascript, содержащий списки элементов, задействованных в игре в данный момент. Да, так сохранение и устроено. Когда мы жмём в игре кнопку «сохранить и выйти», она запоминает текущее состояние (в том числе и для отдельных карт) и записывает всё это добро в такой вот файл в виде этих групп строк, каждая из этих групп отвечает за отдельный записанный элемент (карту), а заголовок объединяет элементы одного вида. Насколько я понял, заголовки имеют такие роли:
«elementStacks»: имеющиеся на столе карты;
«decks»: карты, ещё не вышедшие на стол;
«metainfo»: версия игры;
«characterDetails»: глобальные сведения о сохранении (имя героя, доступное «наследие», сведения о прошлом «пути»);
«situations»: имеющиеся на столе действия и состояния.
Давайте проверим нашу гипотезу. Поиграем совсем чуть-чуть, чтобы ситуация на столе немного изменилась. Снова сохраняемся и выходим, вновь открываем файл сохранения…
Бинго! Вот ситуация в самой игре:
А вот изменившееся содержание файла сохранения:
Отлично! Выходит, что мы можем отслеживать в этом файле всё, что происходит в игре! А можем ли мы изменять этот файл? Конечно же, это же просто текст! Но делать надо это аккуратно, чтобы ничего не сломать. Итак, кажется, я начинаю догадываться, за что отвечает каждая строка под каждым заголовком. Вот, например, свойства карт заголовка «elementStacks»:
Длинный набор букв и цифр: видимо, это уникальный идентификатор карты, трогать его не будем;
«lifetimeRemaining»: скорее всего, это таймер до исчезновения карты, значение 0 показывает, что карта всегда присутствует на столе;
«lastTablePosX»: расположение карты (координата по оси Х);
«markedForConsumption»: как я понял, это параметр для поглощения карты ячейкой со значком свечки. True – исчезнет, False – останется. По умолчанию стоит «False»;
«elementId»: название карты (какое здесь стоит, та карта и будет на столе);
«lastTablePosY»: расположение карты (координата по оси Y);
«quantity»: количество экземпляров карты. Всё точно соответствует ситуации: по одной карте здоровья и змоций, две карты средств.
Под заголовком «decks» содержатся списки ещё не вышедших карт, отсортированных по разным критериям (охотники, награды за экспедиции и т. д.). Эта часть файла самая большая, нам она пока не нужна. Идём дальше.
«metainfo»: сведения о версии игры. Пропускаем, трогать не будем, а то вдруг сохранение поломается, и игра в нынешней версии не сможет его прочесть.
«characterDetails»: сведения о сохранении. Пожалуй, это самое главное. Разберём.
«executions»: по-моему, это информация о выполненных начальных условиях – запись о исполненной «чёрной работе», полученном действии сна, полученном «наследстве»;
«name»: имя «героя» игрока;
«profession»: название «наследия» на языке локализации;
«pastLevers»: играя сам, я уже прошёл три вознесения (Лампады, Кузни и Чаши) и догадался, что обозначает эта группа. Это карты, унаследованные из прошлого прохождения:
«LastPersonKilled»: последний убитый персонаж;
«LastHeadquarters»: последняя имевшаяся штаб-квартира культа;
«LastCult»: последний культ;
«LastTool»: последний полученный инструмент;
«LastFollower»: последний полученный последователь;
«LastBook»: последняя полученная книга;
«LastDesire»: последнее желание;
«LastSignificantPainting»: последняя особая картина;
«LastCharacterName»: имя «героя» игрока из последней игры.
«futureLevers»: то же самое, но будет записано по итогам текущей игры. Изначально этим параметрам соответствуют некоторые значения, но потом они могут быть перезаписаны;
Примечание №2: как можно заметить из содержания файла сохранения, значения многих параметров являются техническими обозначениями чего-либо, чаще всего карт. Какое обозначение чему соответствует, можно посмотреть на сайте https://www.frangiclave.net, введя интересующее обозначение в строку поиска. Вообще это очень полезный ресурс, позволяющий разобраться во многих деталях игры, по умолчанию скрытых от игрока.
«activeLegacy»: «наследие», по которому мы играем в этом сохранении.
Перейдём к группе элементов «situations». Это действия и состояния. В целом там всё похоже на описание карт, но есть и другие важные параметры:
«recipeId»: использующийся «рецепт», т. е., занята ли ячейка действия или состояния чем-либо. Значение «NULL» обозначает, что действие или состояние свободны, и в него можно класть карты;
«state»: обозначает, запущен ли какой-то процесс или нет. Значение «Unstarted» соответствует неактивному состоянию (его можно начать), а значение «Ongoing» соответствует уже идущему процессу;
«situationWindowOpen»: обозначает, открыто ли окно действия или состояния. Значение по умолчанию – «False» (закрыто);
«situationWindowX» и «situationWindowZ»: по аналогии с картами показывают положение области действия или состояния на столе;
«situationOutputNotes»: текст описания в окне действия или состояния (цифры обозначают части текста, которые можно перематывать стрелками);
«title» (под цифрой 1): описание состояния на языке локализации;
«title» (отдельно): заголовок состояния на языке локализации;
«verbId»: техническое обозначение состояния;
«situationWindowY»: координаты окна состояния;
«completioncount»: параметр завершения какого-либо процесса. 1 – есть завершённый процесс, ожидающий забора карт, 0 – никакой процесс ещё не завершён.
Параметр «timeRemaining» здесь отвечает не за срок существования карты, а за длительность того или иного процесса. Как можно видеть на скриншотах игры и файла сохранения, значение 29.7 соответствует времени, которое осталось до завершения процесса получения карты «наследство» из действия «изучение». В данном случае значение 0 в других действиях показывает, что никакие процессы с таймером не запущены.
Так, что обозначает тот или иной параметр, мы разобрались. А теперь давайте попробуем что-нибудь поменять.
Примечание №3: настоятельно рекомендую перед экспериментами сделать отдельную резервную копию файла сохранения. Автосохранение всего лишь перезаписывает один и тот же файл под разными названиями, оно не гарантирует корректность чтения этого файла после ручных изменений.
Примечание №4: нарушать структуру файла крайне нежелательно, иначе он точно поломается. Уникальный номер элемента (из цифр и букв) трогать тоже лучше не надо.
Начнём с простого. Попробуем изменить количество карт эмоций, например, сделать 7 штук вместо одной:
Нет. Ничего не получается. Впрочем, это не так уж и важно. Теперь попробуем что-нибудь удалить, например, действие работы. Вырезаем кусок файла, описывающий это действие (не забываем о резервной копии!). И…
Хм. сработало! Конечно, на работу завязано множество важных действий, без неё нормально играть не получится. Но это всего лишь эксперимент! Возвращаем удалённое действие на место, убеждаемся, что в итоге ничего не поломалось.
А теперь приступим к самому интересному – добавлению карт и действий/состояний. Иначе для чего мы это всё затевали? Залезаем на уже упомянутый выше сайт и пробуем вписать в файл сохранения… Франжиклав! Почему бы ради эксперимента и не понаглеть немного?
И… файл сломался, так как нет того самого длинного кода. Видимо, он определяет и наличие карты, и её расположение. Да уж, разработчики предусмотрели это, не дали нам запустить свои шаловливые ручки куда не следует. Неужели ничего нельзя делать, кроме изменения количества элементов и их удаления? Хм… А если внаглую скопировать один из кодов и вставить для новой карты? О, а в его начале и в значениях координат одни и те же цифры! А если написать координаты и в коде тоже? Ну-ка…
Получилось. Кстати, параллельно мы отследили, как выглядит уже завершившийся рецепт. Приятное с полезным:
А состояние так получится разместить? Давайте сделаем «слежку», карты дурной славы и «суд» (мне всё-таки хочется попробовать тот мод поскорее):
Отлично! Я специально разложил карты по столу, чтобы показать, что они появились в нужном мне количестве. При этом я заменил Франжиклав на дурную славу в нужном количестве штук, просто поменяв значение параметра «elementId» и изменив число для параметра «quantity». Стоп… Ещё один нюанс…
А можно ли изменить ход идущего процесса? А что, момент подходящий:
Делаем резервную копию сохранения ещё разок, открываем окно работы в «Гловер и Гловер». Затем открываем файл сохранения и обращаем внимание на то, что появились ещё две подгруппы:
«situationStoredElements»: карты, которые появятся при окончании процесса;
«ongoingSlotElements»: карта, занятая процессом;
Код полностью соответствует тому, что мы видим в игре: в конторе мы используем разум (подгруппа «ongoingSlotElements» со значением «reason» в параметре «elementId») и получим две карты средств и карту работы обратно ((подгруппа «situationStoredElements» со значениями «funds» и «gloverandglover_difficultbossa_job» в параметре «elementId» для каждой такой карты). Теперь дерзнём поменять обычные средства на бронзовые спинтрии, например, пусть нам платят получше!
Всё прошло как по маслу! Выходит, что мы можем менять что угодно и как угодно? Определённо да, главное – понимать, что и как делать, чтобы файл сохранения не сломался.
Ладно, продолжим то, что хотели, откатив сохранение. И разумеется, меня после всех этих мероприятий по поиску улик благополучно отправили на нары:
Файл сохранения при этом радикально изменился:
Мы видим, что появились новые параметры:
«availablelegacies»: доступные варианты игры;
«currentending»: полученный финал игры;
«defunctCharacterDetails»: изменённые данные о сохранении.
Также изменился подраздел «futureLevers», вот так:
«LastCult»: «cultsecrethistories_1» (культ Тайных Историй, я выбрал его перед самым арестом);
«LastTool»: «toolknockf» (начитеренный Франжиклав);
«LastFollower»: «neville_b»: последователь Невилл, пришедший ко мне при создании культа.
Примечание №5: видимо, последняя карта для наследования между вариантами игры отслеживается автоматически и заменяется лишь следующей картой того же типа. Франжиклав остался записанным в инструменты несмотря на то, что я принудительно заменил его другой картой.
Та-ак… Если доступные варианты игры записываются отдельно, то можно ли прописать их самому? Конечно можно, главное – файл не сломать. По-видимому, первое из одинаковых слов обозначает сам вариант, а второе – картинку для него. Порывшись на сайте https://www.frangiclave.net, я нашёл все варианты игры, которые можно записать в файл сохранения:
«aspirant»: «aspirant» – искатель;
«physician»: «physician» – медик;
«detective»: «detective» – детектив;
«brightyoungthing»: «brightyoungthing» – юное дарование;
«apostlelantern»: «apostlelantern» – продолжение пути Лампады;
«apostleforge»: «apostleforge» – продолжение пути Кузни;
«apostlegrail»: «apostlegrail» – продолжение пути Чаши;
«dancer»: «dancer» – танцовщица;
«ghoul»: «ghoul» – медиум;
«priest»: «priest» – жрец;
«exile:» «exile» – изгнанник.
Примечание №6: возможные варианты игры надо записывать по три и через запятую в конце строки, кроме последней.
Допустим, я хочу поменять предложенные варианты на другие, и пишу вместо них понравившиеся:
Да! Получилось! И… кажется, я всё-таки что-то сломал, несмотря на все меры предосторожности… Хотя ладно, таковы реалии сурового русского моддинга… Смиритесь…
P. S. в тот мод я так и не поиграл, хотя вроде выполнил условия его разблокировки. Видимо, он так и не подключился, загрузка из мастерской не работает как надо. Ну да ладно. Зато разобрался, как устроены сохранения.
Надеюсь, вам была полезна эта информация, пусть и перегруженная выкладками из кода. Напомню только, что всё надо делать аккуратно и не забывать делать резервные копии редактируемых файлов. И да, я догадался, как включить тот мод на новый вариант игры. Скорее всего, придётся установить этот мод вручную, подсмотреть в его файлах, как называется вариант игры, и подставить это название в файл сохранения на момент окончания игры. С другими модами на варианты игры придётся поступать аналогичным образом.
Спасибо за внимание. Буду рад дополнениям, замечаниям и предложениям в комментариях.