Статьи по разработке игр на QSP
Что нового в QSP
Пока готовится выход нового плеера версии 5.8.0 (или выше), мы решили рассказать о грядущих нововведениях. Всё (или почти всё), что будет представлено в этой статье, вы уже можете пощупать в плеере "qSpider" от Werewolf`а.
Изменение в работе массивов
Это, пожалуй, самое основное из грядущих изменений, и о нём мы подробно писали в статье "Массивы уже не те". Здесь же изложим кратко.
Теперь в массиве будет нельзя под одним индексом хранить и текстовое и числовое значение. Если вы запишете в ячейку числовое значение, а потом запишете в ту же ячейку текстовое значение, текстовое значение затрёт числовое.
При этом, если вы попытаетесь получить из ячейки со строковым значением числовое значение, плеер вернёт значение по-умолчанию, то есть 0
. И наоборот: если попытаться из ячейки с числовым значением получить строковое, плеер вернёт пустую строку.
Примеры:
mass[1]=123
$mass[1]='string'
*pl mass[1] & ! выведет число 123
*pl $mass[1] & ! выведет строку 'string'
mass[1]=123
$mass[1]='string' & ! затирает числовое значение
*pl mass[1] & ! попытка доступа к числовому значению вернёт 0
*pl $mass[1] & ! выведет строку 'string'
Многомерные массивы
Чтобы организовать многомерный массив, в плеерах версии 5.7.0 (и более ранних) приходилось использовать текстовые индексы. Например:
$unit_coords["3,1"]="Пехотинец"
$unit_coords["2,7"]="Артилерист"
$unit_coords["10,0"]="Танк"
Но в новых версиях плеера (начиная с 5.8.0 и выше) можно не использовать текстовые индексы, а указывать несколько нужных значений через запятую:
$unit_coords[3,1]="Пехотинец"
$unit_coords[2,7]="Артилерист"
$unit_coords[10,0]="Танк"
Это намного упрощает работу с многомерными массивами.
Изменения в работе логических операторов и функций
Все мы знаем, что QSP не поддерживает булевые (логические) типы данных, а вместо них в плеерах версии 5.7.0. использовались числа 0
и -1
.
Здесь 0
означало Ложь (False), а -1
означало Правду (True
). Соответственно и все логические операции возвращали нам эти значения.
Например:
*pl (3>2 and 4>3) & ! AND вернёт -1
*pl (3>2 or 4>3) & ! OR вернёт -1
*pl no 0 & ! NO вернёт -1
*pl (3<2 and 4>3) & ! AND вернёт 0
*pl (3<2 or 4<3) & ! OR вернёт 0
*pl no -1 & ! NO вернёт 0
В новых версиях плеера все логические операции будут возвращать 1
в случае Правды (True
), и 0
в случае Лжи (False
):
*pl (3>2 and 4>3) & ! AND вернёт 1
*pl (3>2 or 4>3) & ! OR вернёт 1
*pl no 0 & ! NO вернёт 1
*pl (3<2 and 4>3) & ! AND вернёт 0
*pl (3<2 or 4<3) & ! OR вернёт 0
*pl no -1 & ! NO вернёт 0
Соответственно и различные функции, возвращавшие "логические" значения, будут возвращать либо 1
(Правда, True
), либо 0
(Ложь, False
):
addobj "Отвёртка"
*pl obj("Отвёртка") & ! OBJ вернёт -1
*pl obj("Оттка") & ! OBJ вернёт 0
*pl isnum("123") & ! ISNUM вернёт -1
*pl isnum("12d") & ! ISNUM вернёт 0
addobj "Отвёртка"
*pl obj("Отвёртка") & ! OBJ вернёт 1
*pl obj("Оттка") & ! OBJ вернёт 0
*pl isnum("123") & ! ISNUM вернёт 1
*pl isnum("12d") & ! ISNUM вернёт 0
Ещё одно изменение в работе логических операций заключается в том, что теперь они на самом деле будут логическими. Да, оказывается в плеерах версий 5.7.0 и старше операции AND
, OR
, NO
не были логическими — они были побитовыми. Это означает, что данные операции могли выполнять сравнение чисел по отдельным битам и возвращать результат этого сравнения.
Например:
*pl (3 and 2) & ! AND вернёт 2
*pl (4 or 6) & ! OR вернёт 6
*pl no 7 & ! NO вернёт -8
Теперь же, не имеет значения, какое число мы передаём логическому оператору. Если это число отлично от нуля, логический оператор будет воспринимать его как 1
, то есть Правду (True
).
*pl (3 and 2) & ! AND вернёт 1
*pl (4 or 6) & ! OR вернёт 1
*pl no 7 & ! NO вернёт 0
И это логично, ведь именно по такому принципу и работали операторы проверки условия if
и elseif
. Если этим операторам передавалось значение отличное от 0
, то это означало, что условие верно (Правда, True
).
if 0:
*pl "условие выполнено"
else
*pl "условие не выполнено"
end
if 3:
*pl "условие выполнено"
else
*pl "условие не выполнено"
end
Аргументы, передаваемые с операторами GOTO и XGOTO
Теперь аргументы (параметры, данные), передаваемые с операторами GOTO
и XGOTO
, и которые мы можем получить из массива ARGS
на локации, однозначно можно будет использовать в действиях, созданных на этой локации.
Раньше это тоже можно было делать, однако в плеере присутствовал досадный баг, который периодически ломал всё это дело. Этот баг исправили, и теперь всё прекрасно работает:
goto "локация_2","текст"
--локация_1
# локация_2
act "Действие":
*pl $args[0]
end
--локация_2
Обратите внимание, ARGS
ведёт себя на текущей локации, как обычный глобальный массив. Т.е., если вы изменяете значения ARGS
в одном из действий, эти же значения будут использоваться в других действиях.
goto "локация_2","из аргументов"
--локация_1
# локация_2
act "Действие - 1":
*pl $args[0]
$args[0]="Из действия 1"
end
act "Действие - 2":
*pl $args[0]
$args[0]="Из действия 2"
end
--локация_2
DISABLESUBEX больше нет
Системная переменная DISABLESUBEX
была предназначена для того, чтобы отключать обработку вложенных выражений. Например, если вы хотели вывести строку, в которой присутствуют вложенные выражения, без изменений, вы могли воспользоваться данной переменной:
health=100
*pl "Здоровье <<health>>" & ! выведет строку 'Здоровье 100'
disablesubex=1
*pl "Здоровье <<health>>" & ! выведет строку 'Здоровье <<health>>'
disablesubex=0
*pl "Здоровье <<health>>" & ! выведет строку 'Здоровье 100'
В плеерах более новых версий это не сработает:
health=100
*pl "Здоровье <<health>>" & ! выведет строку 'Здоровье 100'
disablesubex=1
*pl "Здоровье <<health>>" & ! выведет строку 'Здоровье 100'
disablesubex=0
*pl "Здоровье <<health>>" & ! выведет строку 'Здоровье 100'
Чтобы вывести строку с подвыражениями без обработки таких вложенных выражений в новых версиях плеера, можно использовать фигурные скобки:
health=100
*pl "Здоровье <<health>>" & ! выведет строку 'Здоровье 100'
*pl {Здоровье <<health>>} & ! выведет строку 'Здоровье <<health>>'
*pl "Здоровье <<health>>" & ! выведет строку 'Здоровье 100'
Изменения в работе неявного оператора
Неявный оператор — это оператор, который мы не указываем. В 5.7.0 он делал примерно то же, что делал и оператор *pl
. То есть выводил на экран значение, добавляя после этого значения перевод строки:
*pl 456
*pl "text"
! эквивалентно:
456 & ! здесь для вывода используется неявный оператор
"text" & ! и здесь для вывода используется неявный оператор
Если мы вызывали какую-то функцию, но она не возвращала никакого результата, неявный оператор, как и оператор *pl
, выводил на экран пустую строку и добавлял к ней перевод строки:
"Строка текста"
func('foo')
"Строка текста"
--loc1
# foo
N=R*L
--foo
Вот что мы увидим на экране в плеерах версии 5.7.0:
Строка текста
В новых версиях плеера, если неявный оператор не получит никакого значения от функции, он просто ничего не будет делать. Вот что мы увидим на экране в плеерах версии 5.8.0 и выше:
Строка текста
Неявный вызов пользовательских функций
В плеерах версии 5.7.0 и более ранних вы могли написать собственную функцию и затем вызвать её без возвращения результата, используя оператор GOSUB
, или с возвращением результата, используя функцию FUNC
. В плеерах версии 5.8.0 и выше эта возможность конечно же сохраняется.
Пример:
# start
gosub 'proced' & ! вызов без возвращения результата
*pl func('foo',23,45) & ! вызов с возвращением результата
--start
# proced
act "Action":
*pl 'text'
end
--proced
# foo
result=args[0]+args[1]
--foo
Однако в плеерах версии 5.8.0 и выше вы можете использовать сокращённую запись вызова таких функций:
# start
@proced & ! вызов без возвращения результата
*pl @foo(23,45) & ! вызов с возвращением результата
--start
# proced
act "Action":
*pl 'text'
end
--proced
# foo
result=args[0]+args[1]
--foo
То есть вместо того, чтобы использовать явное указание оператора gosub или функции func
, можно писать символ @
, а после него без пробелов записывать название локации. Само название вызываемой таким образом локации/функции так же не должно содержать пробелов.
Если такой функции нужно передать значения, после названия локации ставим скобки и перечисляем нужные аргументы.
Новая функция ARRITEM
Функция ARRITEM
возвращает значение указанной ячейки массива. Это новая функция, и введена она по большей части для того, чтобы упразднять подвыражения при использовании DYNAMIC
/DYNEVAL
.
Примеры:
! работает в любой версии плеера:
xvar = massive[123]
! работает только в 5.8.0 и выше:
xvar = arritem('massive',123)
Обе команды присваивают переменной xvar
значение из ячейки 123
массива 'massive
', однако во втором случае мы указываем название в виде строкового значения. Таким образом мы, не прибегая к помощи DYNAMIC
/DYNEVAL
можем динамически формировать названия массива при получении значения.
$mass[0]='string'
$mass[1]='array'
*pl $arritem('$'+$mass[0]+$mass[1],345)
Так же ARRITEM
позволяет получить значение из ячейки с текстовым индексом:
*pl $arritem('$mass','text index')
KILLVAR удаляет по текстовому индексу
В плеерах версии 5.7.0 и ниже оператор KILLVAR
умел удалять элементы массивов только по числовому индексу:
$mass[0]='строка 0'
$mass[1]='строка 1'
$mass[2]='строка 2'
$mass["text index"]='строка 3'
$mass[4]='строка 4'
! удаление по числовому индексу
killvar '$mass',2
*pl $mass[2] & ! выведет на экран строку 'строка 3'
В плеерах версии 5.8.0 и выше KILLVAR
умеет удалять элементы массивов не только по числовому, но и по текстовому индексу:
$mass[0]='строка 0'
$mass[1]='строка 1'
$mass[2]='строка 2'
$mass["text index"]='строка 3'
$mass[4]='строка 4'
! удаление по числовому индексу
killvar '$mass','text index'
*pl $mass[3] & ! выведет на экран строку 'строка 4'
Операторы ADDQST и KILLQST переименованы
В плеерах версии 5.7.0 (за исключением Quest Navigator) используются операторы ADDQST
и KILLQST
. В плеерах более новых версий (5.8.0 и выше) данные операторы заменены на INCLIB
и FREELIB
соответственно.
INCLIB
добавляет локации из подключаемого модуля QSP к основной игре.
FREELIB
удаляет все локации ранее подключённых модулей QSP из основной игры.
inclib 'drive.qsp'
inclib 'base.qsp'
freelib
Изменение в работе оператора SET
Для явного объявления переменных в QSP используется оператор SET
. В плеерах версии 5.7.0 и ниже с помощью этого оператора вы могли объявить лишь одну переменную:
set mass=45
Если требовалось объявить несколько переменных, приходилось писать несколько команд SET
:
set mass=45 & set daz=65 & set zaz=79
! эквивалентно:
mass=45 & daz=65 & zaz=79
В плеерах версии 5.8.0. и выше вы можете объявить несколько переменных одной командой:
set mass, daz, zaz = 45, 65, 79
Обратите внимание на форму записи. Здесь лишь один знак =
. Переменные и их значения перечислены через запятую соответственно слева и справа от знака =
.
Более того. С помощью оператора SET
вы можете не только назначать переменным определённые значения, но и присваивать значения других переменных:
set a, b, c = x, y, z
В том числе и менять значения переменных местами не прибегая к помощи третьей переменной:
set j,y=y,j
Само собой, как и в плеерах версии 5.7.0, оператор SET
указывать не обязательно:
mass, daz, zaz = 45, 65, 79
a, b, c = x, y, z
j,y=y,j
Новый оператор LOCAL
В плеерах версии 5.8.0 и выше, а так же в Quest Navigator появился новый оператор, который позволяет объявить указанные переменные локальными для отдельного блока кода (локации, действия, код в DYNAMIC
/DYNEVAL
). После того, как блок кода выполнен, значения переменных восстанавливаются к предыдущим:
i=99
*pl i
gosub 'foo'
*pl i
--start
# foo
local i
i=45
*pl i
--foo
Можно объявить локальную переменную и сразу присвоить ей значение:
Можно объявить сразу несколько локальных переменных:
local i, j, k
! объявляем локальные переменные и присваиваем им значения
local f, d, $g = 123, 45, 'string'
Обратите внимание на последнюю форму записи. Сначала мы перечисляем объявляемые переменные через запятую, затем ставим один знак =
, а после него перечисляем через запятую значения, которые хотим присвоить переменным. Неправильно делать такую запись:
local f=123, d=45, $g='string'
Изменения в работе функций INSTR, ARRCOMP, ARRPOS
Необязательные аргументы функций INSTR
, ARRCOMP
и ARRPOS
в плеерах 5.8.0 и выше, а так же в Quest Navigator переставлены в конец. В плеерах версии 5.7.0. и ниже эти аргументы шли в начале.
Примеры для версий плеера 5.7.0 и ниже:
instr(7,"В корзине 23 красных и 47 синих яблок.", "красн")
! поиск среди элементов массива элемента, который содержит число 23, начиная с 13 элемента
arrpos(13,'mass',23)
! поиск среди элементов массива элемента, который соответствует регулярному выражению, начиная с пятого элемента
arrcomp(5,'$objectbox','\S{2}\s\S{6}')
Те же самые примеры для плееров версии 5.8.0 и выше и Quest Navigator`а:
instr("В корзине 23 красных и 47 синих яблок.", "красн",7)
! поиск среди элементов массива элемента, который содержит число 23, начиная с 13 элемента
arrpos('mass',23,13)
! поиск среди элементов массива элемента, который соответствует регулярному выражению, начиная с пятого элемента
arrcomp('$objectbox','\S{2}\s\S{6}',5)
Новый оператор цикла LOOP
В плеерах версий 5.7.0 и старше, для организации циклов приходилось использовать метки. Начиная с версии 5.8.0. у нас появляется отдельный оператор циклов LOOP
. Вот как он записывается в общем виде:
LOOP [команды 1] WHILE [условие] STEP [команды 2]:
[команды 3]
END
! однострочная форма:
LOOP [команды 1] WHILE [условие] STEP [команды 2]: [команды 3]
Здесь, как вы видите, есть целых три ключевых слова:
LOOP
— это ключевое слово объявляет, что начинается цикл, оно обязательно. ПослеLOOP
могут идти некоторые однострочные операторы. Например, здесь мы можем объявить локальные переменные, которые будут считаться локальными только для данного цикла.WHILE
— после этого ключевого слова должно стоять условие, и пока выполняется это условие, цикл тоже будет выполняться.STEP
— это ключевое слово не является обязательным, однако оно удобно, чтобы перечислить однострочные операторы, не относящиеся напрямую к телу цикла. Например, здесь можно указать изменение счётчика.
Непосредственно тело цикла, то есть его основные команды пишутся после двоеточия. Для однострочной формы — в той же строке, что и loop
, а для многострочной формы — в последующих строках сразу после двоеточия. Многострочную форму необходимо завершать ключевым словом END
.
Несколько примеров:
loop local i=1 while i<11 step i+=1:
*pl "12 * <<i>> = <<12*i>>"
end
loop local i,pos,true=0,-1,1 while true:
pos=arrpos('mass',3,pos+1)
if (pos<>-1 and i<>pos) or pos=0:
i=pos
*pl "mass[<<pos>>] = 3"
else
true=0
end
end
$string='long long long string'
loop while len($string)>0:
$a=$mid($string,1,1)
if arrpos('$letters',$a)=-1:
$letters[]=$a
end
if len($string)>1:
$string=$mid($string,2)
else
$string=''
end
end
*pl
! выводим на экран
loop local i,s=0,arrsize('$letters') while i<s step i+=1:
*p $letters[i]+", "
end
Изменения в чтении длинных строк, разбитых на несколько
Для того, чтобы разбивать длинные строки на несколько (для удобства чтения) в QSP используется сочетание символов " _"
(пробел и символ нижнего подчёркивания). В плеерах версии 5.7.0 и ниже (кроме Quest Navigator 0.0.28) при разборе данной конструкции движок оставлял строки, как есть. Для примера возьмём такую конструкцию:
or _
t:
В плеерах версии 5.7.0 символы преформатирования будут исключены, при интерпретации, а строка, разбитая с помощью " _"
будет объединена, как есть, то есть будет равнозначна строке:
В плеерах же версии 5.8.0 и выше данная строка будет объединена с добавлением пробела вместо каждого сочетания " _"
, то есть будет равнозначна строке:
Данное отличие в прочтении строк используется, как костыль, чтобы отличить классический плеер версии 5.7.0 от Quest Navigator версии 0.0.28:
tort=0
if t _
or _
t:
"Игра запущена на Quest Navigator"
else
"Игра запущена на Классике"
end
Изменения в работе функции RAND
В плеерах версии 5.7.0 и ниже второй параметр функции RAND
по умолчанию был 0
. Например, если вы указывали число 100
в качестве аргумента функции RAND
, то эта функция возвращала случайное число от 0
до 100
. В плеерах версии 5.8.0 и выше, а так же в Quest Navigator, второй параметр по умолчанию равен 1
. То есть если вы укажете лишь одно число, например 100
, функция RAND
вернёт случайное значение от 1
до 100
.
RAND(100) & ! в 5.8.0 вернёт значение от 1 до 100
RAND(10) & ! в 5.7.0 вернёт значение от 0 до 10
RAND(10) & ! в 5.8.0 вернёт значение от 1 до 10
RAND(5) & ! в 5.7.0 вернёт значение от 0 до 5
RAND(5) & ! в 5.8.0 вернёт значение от 1 до 5
RAND(0) & ! в 5.7.0 вернёт значение 0
RAND(0) & ! в 5.8.0 вернёт значение от 1 до 0
RAND(1) & ! в 5.7.0 вернёт значение от 0 до 1
RAND(1) & ! в 5.8.0 вернёт значение 1
Повышение приоритета функций LOC и OBJ
В плеерах версии 5.7.0 (и ниже) у функций LOC
и OBJ
приоритет был ниже, чем у операций сравнения. Это могло быть неочевидным для выражений такого рода:
Кажется, что данное выражение должно выполняться так: проверяется наличие предмета "Отвёртка", проверяется наличие предмета "Верёвка", и лишь потом значения сравниваются. Однако в 5.7.0 у операции сравнения приоритет выше, чем у OBJ
. Поэтому сначала выполняется операция сранения, и лишь потом функция OBJ
. Таким образом в плеерах версии 5.7.0 данное выражение всегда возвращает 0
.
В плеерах версии 5.8.0 (и выше) приоритет у функций OBJ
и LOC
выше, чем у операций сравнения, поэтому данное выражение будет вычисляться именно так, как мы предположили: сначала проверяется наличие обоих предметов и лишь потом сравниваются полученные значения. В плеерах 5.8.0 (и выше) данное выражение будет возвращать 1
, если оба предмета или отсутствуют, или присутствуют, и 0
, если одни предмет присутствует, а другой отсутствует.
Больше аргументов для функций
В плеерах версии 5.7.0 (и ниже) максимальное число аргументов, которое вы могли передавать операторам и функциям, было 10
. Таким образом, например функции MAX
и MIN
могли производить поиск значений лишь среди десяти значений. В плеерах версии 5.8.0 и выше максимальное число аргументов, передаваемых функциям и операторам, — 20
.
max('a','b','c','d','e','f','g','h','i','j')
! работает лишь в плеерах версии 5.8.0 и выше
max('a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t')
Старые новости, о которых вы могли не знать
Здесь мы осветим пару изменений, которые появились в плеере версии 5.7.0 (или раньше), но которые прошли не очень заметно.
Игнорирование отрицательных индексов
В плеерах версий ниже 5.6.5 отрицательные индексы приводились к нулю, а в плеерах более новых версий, они просто напросто игнорируются:
mass[0]=123
*pl mass[-1] & ! на экране увидим 123
mass[-1]=456
*pl mass[0] & ! на экране увидим 456
mass[0]=123
*pl mass[-1] & ! на экране увидим 0
mass[-1]=456
*pl mass[0] & ! на экране увидим 123
Добавление предмета в определённую позицию
Начиная с версии 5.6.5 вы можете добавлять предмет в указанную позицию в окне предметов. При этом, если на текущей позиции уже находится предмет, он сдвигается вниз (вправо) по списку, и все стоящие за ним предметы так же сдвигаются на одну позицию вниз (вправо).
addobj 'Отвёртка','путь к файлу картинки/картинка.png',4
Индексация предметов в списке начинается с единицы. Можно указать индекс численно больший на один, чем число предметов, тогда предмет добавится в конец списка. Однако, если указать индекс числом больше числа предметов плюс один, или индекс меньше единицы, команда addobj
будет проигнорирована:
addobj 'Первый предмет'
addobj 'Второй предмет'
addobj 'Третий предмет'
addobj 'Четвёртый предмет'
addobj 'Пятый предмет','',5 & ! предмет будет добавлен в конец списка
addobj 'Седьмой предмет','',7 & ! предмет не будет добавлен
addobj 'Нулевой предмет','',0 & ! предмет не будет добавлен
Копирование части массива в COPYARR
Начиная с версии 5.7.0 вы можете копировать часть массива в другой массив, используя не обязательные параметры: начальный индекс и количество. В следующем примере в массив 'mass
' будут скопированы шесть элементов массива 'array
', начиная с третьего:
copyarr 'mass','array',3,6
Передача аргументов в ONNEWLOC
Начиная с версии 5.7.0 аргументы, которые передаются на локацию при переходе на эту локацию с помощью операторов GOTO
или XGOTO
, передаются так же и на локацию-обработчик перехода на новую локацию (прописанную в переменной $ONNEWLOC
). При этом, даже если значения в массиве args
будут изменены на локации, на которую был осуществлён переход, на локацию, прописанную в переменной $ONNEWLOC
, будут переданы исходные значения, переданные с GOTO
/XGOTO
.
$onnewloc='onNewLoc'
goto 'next','old text',345
--start
# next
''
'next:'
! значения, переданные с GOTO
*pl $args[0]
*pl args[1]
! меняем значения на текущей локации
args[1]=678
$args[0]='new text'
! изменённые значения
*pl $args[0]
*pl args[1]
--next
# onNewLoc
''
'onNewLoc:'
! значения, переданные с GOTO
*pl $args[0]
*pl args[1]
--onNewLoc
Что ещё?
Плеер продолжает развиваться и совершенствоваться, а значит мы можем ждать ещё много изменений в работе, которые непременно будут отражены в новых статьях, а так же в онлайн-справке wiki.qsp.org.
P.S.:
Данную статью вы можете найти так же и на других ресурсах: