Переход с QSP 5.7.0 на 5.9.x
Многие авторы, начинавшие разработку игр для плееров версии 5.7.0, боятся переводить проекты на более свежие версии из-за обилия изменений. Байт активно обновляет основную библиотеку QSP уже пять лет, и количество введённых изменений действительно может пугать.
Но на самом деле всё не так страшно.
Список по-настоящему критических изменений весьма скромен, и эта статья поможет безболезненно перевести проект на более новый и быстрый плеер.
Мы не будем подробно описывать все нововведения — они уже описаны в нескольких статьях. Сосредоточимся на практических шагах, попутно раскрывая наиболее значимые изменения в работе плеера по сравнению с версией 5.7.0.
Все нововведения уже отражены в онлайн-справке: wiki.qsp.org
Вот список статей, которые помогут более детально познакомиться со всеми изменениями:
- https://vk.com/@qsplayer-novovvedeniya-v-qsp-580,
- https://vk.com/@qsplayer-novovvedeniya-v-qsp-590,
- https://vk.com/@qsplayer-chto-novogo-v-qsp-591,
- https://vk.com/@qsplayer-novovvedeniya-v-qsp-592-594.
Инструменты
Для правки игры вам потребуются следующие инструменты:
-
Утилита TXT2GAM — конвертирует игру в формат текстового файла и обратно.
- Скачать можно с официального сайта или со страницы релизов на GitHub.
- Если не знаете, как пользоваться утилитой, прочитайте краткое руководство.
-
Анализатор QSP-кода — быстро сканирует игру в формате текстового файла и предоставляет список возможных ошибок, имена переменных, локаций, действий, предметов и прочее.
- Скачать можно по ссылке из темы на форуме, посвящённой Анализатору.
- Или из архива на меге "QSP/Программы". ← Смотрите папку "QSP-Analyser". Для первого раза лучше выбрать последнюю версию без модификаций.
- Краткая информация доступна на форуме и в архиве статей.
-
Для просмотра игры в формате текстовых файлов подойдёт любой текстовый редактор. Рекомендуем Sublime Text или VS Code — они поддерживают подсветку синтаксиса QSP.
- Для Sublime Text разработан пакет, включающий плагин для работы с qsps-файлами и подсветку синтаксиса QSP.
- Для VS Code существует специальное расширение.
Если вы используете системы контроля версий (например, Git), рекомендуем фиксировать каждое масштабное изменение в игре.
Первый этап переноса. Критичные изменения
Версия библиотеки QSP 5.8.0 получила несколько критически значимых изменений, которые сломали обратную совместимость. Игра, написанная для плеера 5.7.0, иногда будет некорректно работать на плеерах версии 5.8.0 и выше.
Поправить такую игру достаточно легко.
Сделайте полную копию вашего проекта на тот случай, если что-то пойдёт не так.
Анализ кода игры для QSP 5.7.0
- Сначала вам нужно сконвертировать игру в формат "qsps". Это делается с помощью утилиты txt2gam, или, если утилита подключена к Quest Generator, с помощью пункта меню "Экспорт → Текстовый файл формата TXT2GAM" в QGen.
Формат "qsps" и "TXT2GAM" — это одно и то же. - Далее нужно запустить Анализатор и "скормить ему игру":
- Запускаете утилиту и нажимаете кнопку с тремя точками в правом верхнем углу.
- Находите и выбираете текстовый файл с вашей игрой.
- Утилита автоматически запустит чтение файла и на вкладке "Анализ" в самом нижнем поле вы увидите список ошибок и предупреждений.
- В той же вкладке нажмите кноп ку "Переменные". В случае успешного чтения файла в трёх полях с полосами прокрутки отобразятся списки используемых в игре переменных.
Это всё, что понадобится нам от утилиты "Анализатор". Не закрывайте окно, вам понадобится информация из него.

Разнотипные значения в массивах
Одно из самых масштабных изменений в QSP: больше нельзя хранить в одном массиве под одним индексом и числовое, и текстовое значение. Подробно это изменение описано в статье "Массивы уже не те" .
Если вы использовали старое поведение массивов как фичу (например, хранили названия и здоровье юнитов под одним индексом), в плеерах версии 5.8.0 и выше это сломает работу игры.
! $unit - название, unit - здоровье
$unit[0] = "пехотинец" & unit[0] = 300
$unit[1] = "гвардеец" & unit[1] = 670
$unit[2] = "лучник" & unit[2] = 1500
$unit[3] = "артиллерист" & unit[3] = 10
В примере выше числовые значения затрут текстовые, а в плеерах версии 5.7.0 оба значения хранятся одновременно.
Решение простое. Чтобы починить игру, сломавшуюся из-за подобного использования массивов, мы конвертировали QSP-файл в текстовый формат и "скормили" его Анализатору.
Нужно сделать следующее:
-
Скопируйте списки текстовых и числовых переменных из соответствующих окошек Анализатора и сравните.
- Можно, например, вставить эти списки в Excel, отсортировать по алфавиту и сопоставить вручную.
-
Если в обоих списках присутствуют одноимённые массивы (например,
$unitиunit), переименуйте текстовые массивы во всей игре. (Для приведённого примера$unitможно переименовать в$unit_name.) -
Массивы
$args/argsи$result/resultпереименовывать не нужно — это специальные массивы. Однако во всех локациях, где используются$args/args, первой строчкой обязательно проводите инициализацию (например,args[9] = args[9]), если используете текстовые индексы для элементов массива$args/args. -
Будьте аккуратны с переименованием массивов, которые не используют одновременное хранения числовых и текстовых значений под одним индексом, но хранят значения обоих типов под разными индексами.
Когда не останется одноимённых текстовых и числовых массивов (за исключением выше обозначенных случаев), игра в плане хранения данных в массивах станет совместима и с плеерами версии 5.7.0, и с плеерами версий 5.8.0 и выше.
Изменения в работе функций INSTR, ARRCOMP, ARRPOS
Необязательные аргументы функций INSTR, ARRCOMP и ARRPOS в плеерах 5.8.0 и выше пер еставлены в конец. В плеерах версии 5.7.0 и ниже эти аргументы шли в начале.
Например, в плеере 5.7.0 поиск подстроки в строке мог выглядеть так:
instr(7, "В корзине 23 красных и 47 синих яблок.", "красн") & ! 14
Если вы полност ью переходите на новые версии плеера и совместимость с версией 5.7.0 вас не волнует, найдите все вхождения instr, arrcomp и arrpos в игре и переставьте необязательный аргумент в конец:
instr("В корзине 23 красных и 47 синих яблок.", "красн", 7) & ! 14
Если нужно сохранить совместимость с плеерами версии 5.7.0, сделайте следующее:
- Проверьте значения необязательных аргументов. Если для
instrэти значения везде равны1, а дляarrposиarrcomp—0, достаточно просто опустить эти значения, и игра в плане работы этих функций станет совместима и с 5.7.0, и с 5.8.0 и выше. - Если значения необязательных аргументов отличаются от указанных выше, напишите функции-обёртки для
instr,arrcompиarrposи замените все встроенные функции QSP в игре на свои. Пример для функцииinstrниже.
# test
! будет работать во всех плеерах
func("myInstr", "В корзине 23 красных и 47 синих яблок.", "красн", 7) & ! 14
-- test
# myInstr
$args[0] = $args[0] & ! строка, по которой производим поиск
$args[1] = $args[1] & ! подстрока, которую ищем
args[2] = iif(args[2]>0, args[2], 1) & ! с какого элемента начинать поиск
if $QSPVER >= '5.8.0':
result = instr($args[0], $args[1], args[2])
else:
result = instr(args[2], $args[0], $args[1])
end
-- myInstr
Как видно из примера, в нашей функции мы всё равно изменили порядок аргументов, переставив необязательный аргумент в конец. Все строки с оригинальным instr придётся заменить на нашу функцию. Такое решение обеспечит совместимость и с плеерами версии 5.7.0, и с более новыми плеерами.
Поиск и замену этих функций удобнее всего делать в текстовом редакторе типа Sublime Text или VS Code — там можно использовать регулярные выражения для поиска и замены. Это избавит от ручной обработки каждого вхождения, и вы сможете в полуавтоматическом режиме заменить все сомнительные функции на исправленные за считанные минуты.
DISABLESUBEX не работает
Системная переменная, которая раньше отключала распознавание подвыражений, больше не работает.
Если раньше вы писали такой код:
disablesubex = 1
'<<disablesubex>>'
disablesubex = 0
На экран без изменений выводилась строка "<<disablesubex>>". Подвыражение не раскрывалось.
Теперь переменная DISABLESUBEX больше не отключает раскрытие подвыражений, поэтому на экране вы увидите "1".
Чтобы вывести подвыражение на экран необработанным, используйте конкатенацию для разделения угловых скобок:
'<'+'<disablesubex>>'
Эта правка сохранит совместимость с плеерами версии 5.7.0.
ADDQST → INCLIB, KILLQST → FREELIB
В новых версиях плееров операторы ADDQST и KILLQST заменены на INCLIB и FREELIB.
Всё очень просто.
Переименуйте эти операторы, если не требуется сохранять совместимость с 5.7.0.
Если совместимость требуется, проверяйте версию плеера перед вызовом оператора:
if $QSPVER >= '5.8.0':
INCLIB 'my_lib.qsp'
else:
ADDQST 'my_lib.qsp'
end
Работать всё будет одинаково — это те же самые операторы, просто переименованные.
Изменения в работе логических операторов и функций
В QSP нет булевых значений (True и False). В плеерах версии 5.7.0 вместо них использовались числа 0 и -1.
0 означало Ложь (False), а -1 — Правду (True). Соответственно все логические операции возвращали эти значения:
! 5.7.0
*pl (3>2 and 4>3) & ! -1
*pl (3>2 or 4>3) & ! -1
*pl no 0 & ! -1
*pl (3<2 and 4>3) & ! 0
*pl (3<2 or 4<3) & ! 0
*pl no -1 & ! 0
Операторы AND, OR, NO были битовыми — они не просто сверяли истинность или ложность значений, а вычисляли их битовый результат:
! 5.7.0
*pl (3 and 2) & ! 2
*pl (4 or 6) & ! 6
*pl no 7 & ! -8
_В плеерах начиная с версии 5.8.0 операции сравнения, логические операторы и функции можно полноправно назвать логическими.
AND, OR и NO теперь всегда возвращают числовые значения 0 (эквивалент False) или 1 (эквивалент True). То же самое и с операциями сравнения.
! >= 5.8.0
*pl (3>2 and 4>3) & ! 1
*pl (3>2 or 4>3) & ! 1
*pl no 0 & ! 1
*pl (3<2 and 4>3) & ! 0
*pl (3<2 or 4<3) & ! 0
*pl no 1 & ! 0
*pl (3 and 2) & ! 1
*pl (4 or 6) & ! 1
*pl no 7 & ! 0
Обратите внимание: и в старых, и в новых версиях плееров ЛЮБОЕ число, отличное от нуля, воспринимается как True. Только 0 является эквивалентом False. Когда логический оператор получает любое отличное от нуля число в качестве операнда, он работает с ним, как если бы это была единица. Поэтому no 7 возвращает 0, что более правильно, чем в 5.7.0.
Логические функции ISNUM, LOC, ISPLAY и другие раньше возвращали -1 как эквивалент True и 0 как эквивалент False. Начиная с версии 5.8.0 они возвращают 1 и 0 соответственно, как и операции сравнения.
isnum('123') & ! 1
isnum('12d') & ! 0
Операция OBJ, которая в плеерах версии 5.7.0 возвращала -1 при наличии предмета в окне предметов, претерпела изменения.
В плеерах начиная с версии 5.8.0 она, как и прочие логические функции, стала возвращать 1 или 0. Однако начиная с версии 5.9.4 она возвращает ЧИСЛО предметов с указанным названием в инвентаре.
addobj 'Отвёртка'
addobj 'Отвёртка'
addobj 'Отвёртка'
*pl OBJ('Отвёртка') & ! в 5.7.0 ===> -1
& ! в 5.8.0 ===> 1
& ! в 5.9.0 ===> 3
Как видно, в QSP произошло много изменений в логических операторах и функциях, и всё это может повлиять на работоспособность игры.
К сожалению, так же быстро и просто пофиксить всё это, как массивы, не получится, однако ничего принципиально сложного в исправлении не требуется.
Если вы использовали побитовые операции в игре именно для вычислений каких-то значений, а не просто для проверки условий, вам придётся либо остаться на 5.7.0, либо написать собственные локации-функции, имитирующие побитовые операции.
Если вы просто использовали OR, AND, NO для проверки условий, ничего исправлять не нужно — всё продолжит работать так же, как работало раньше.
Чтобы сохранить совместимость с 5.7.0 и более новыми плеерами, уберите явное сравнение с -1 для логических функций.
Пример:
! совместимо только с плеерами версии 5.7.0
:input_age
$age = $input('Сколько вам лет?')
if isnum($age) = -1:
hero_age = val($age)
else:
jump 'input_age'
end
! совместимо с плеерами любых версий
:input_age
$age = $input('Сколько вам лет?')
if isnum($age):
hero_age = val($age)
else:
jump 'input_age'
end
Также следует учесть, что в плеерах версии 5.7.0 (и ниже) у функций LOC и OBJ приоритет был ниже, чем у операций сравнения. Это могло быть неочевидным для выражений такого рода:
(obj 'Отвёртка' = obj 'Верёвка')
Кажется, что это выражение должно выполняться так: проверяется наличие предмета "Отвёртка", проверяется наличие предмета "Верёвка", и лишь потом значения сравниваются. Однако в 5.7.0 у операции сравнения приоритет выше, чем у OBJ. Поэтому сначала выполняется операция сравнения, и лишь потом функция OBJ. Таким образом в плеерах версии 5.7.0 это выражение всегда возвращает 0.
Начиная с версии 5.8.0 приоритет для LOC и OBJ стал т аким же, как и для других функций типа isnum и isplay, из-за чего некоторые сложные условия могут теперь работать неправильно. Чтобы исправить это и сохранить совместимость со всеми версиями плееров, расставьте скобки во всех условиях с такими функциями:
if (obj 'Отвёртка') = (obj 'Верёвка'):
...
Это будет прочитано одинаково и в плеере версии 5.7.0, и в плеерах версий 5.8.0 и выше.
Аргумент по умолчанию для функции RAND
В плеерах версии 5.7.0 и ниже второй параметр функции RAND по умолчанию был 0. Например, если вы указывали число 100 в качестве аргумента функции RAND, она возвращала случайное число от 0 до 100. В плеерах версии 5.8.0 и выше, а также в Quest Navigator, второй параметр по умолчанию равен 1. То есть если вы укажете лишь одно число, например 100, функция RAND вернёт случайное значение от 1 до 100.
Чтобы избежать багов в более новых версиях плееров и сохранить совместимость с плеерами версии 5.7.0, явно укажите второй аргумент:
! вместо:
RAND(100)
! пишем:
RAND(0, 100)
В текстовых редакторах типа VS Code и Sublime Text это легко делается с помощью регулярных выражений.
Изменения в работе неявного оператора
Неявный оператор — это оператор, который мы не указываем. В 5.7.0 он делал примерно то же, что оператор *pl — выводил на экран значение, добавляя после него перевод строки:
! работает в плеерах любых версий
*pl 456
*pl "text"
! эквивалентно:
456 & ! здесь для вывода используется неявный оператор
"text" & ! и здесь для вывода используется неявный оператор
Если мы вызывали какую-то функцию, но она не возвращала результат, неявный оператор, как и оператор *pl, выводил на экран пустую строку и добавлял к ней перевод строки:

Начиная с версии 5.8.0, если функция не возвращает значение, неявный оператор будет просто игнорировать такую функцию.

Чтобы исправить это и сохранить совместимость с плеерами версии 5.7.0, вместо неявного оператора везде напишите явный оператор *pl.
*pl "Строка текста"
*pl func('foo.3')
*pl "Строка текста"
