Пользовательские функции и процедуры
Общая информация о GOSUB и FUNC
Сам принцип написания игр на QSP предполагает, что игра будет состоять из отдельных блоков кода — локаций. Эти локации могут быть связаны между собой переходами с помощью операторов GOTO и XGOTO. Таким образом в QSP реализуется перемещение по игре, например, перемещение с места на место, или по главам интерактивной истории.
Однако иногда нам необходимо использовать локации не как локации мест, не как главы или страницы, а для того, чтобы иметь возможность выполнять повторяющиеся фрагменты кода. Например, мы пишем на нескольких разных локациях действие подъёма яблок. И нам нужно, чтобы это действие увеличивало число яблок у нас в рюкзаке, уменьшало на соответствующей локации и добавляло предмет "Яблоки", если мы не поднимали ещё ни одного яблока.
В принципе мы это можем сделать, просто копируя на каждую локацию вот такое действие:
act "Взять яблоко":
if яблоки['рюкзак']=0:
addobj "Яблоки"
end
яблоки['рюкзак'] += 1
яблоки[$curloc] -= 1
end
Но посмотрите, сколько строчек кода это действие занимает. Семь. Если мы скопируем такое действие два или три раза, это ещё ничего, но что, если нам придётся копировать действие сто раз? Это семь сотен строчек кода. Это попросту увеличивает объём нашей игры в десятки раз. А ведь у нас может быть не одно такое действие.
Чтобы нам не приходилось писать одинаковый код множество и множество раз, в QSP предусмотрены специальный оператор GOSUB и специальная функция FUNC, которые позволяют нам превращать любые локации в собственные процедуры и функции. В данном случае мы можем вынести код действия в отдельную локацию, назвать эту локацию "поднять_яблоко", а затем вызывать эту локацию из действия с помощью GOSUB.
Локация "поднять_яблоко":
!# поднять_яблоко
if яблоки['рюкзак']=0:
addobj "Яблоки"
end
яблоки['рюкзак']+=1
яблоки[$curloc]-=1
Действие, которое мы пишем на любой другой локации:
act "Взять яблоко":
gosub "поднять_яблоко"
end
Как видите, всё наше действие сократилось до трёх строчек кода, а сам код подъёма яблока написан нами всего один раз!
(Подробнее о том, как работает оператор GOSUB вы можете прочитать в разделе ниже).
Ну а если, предположим, нам нужно написать сразу три действия по подъёму яблок? И все три эти действия отличаются только числом яблок, которые мы поднимаем:
act "Взять одно яблоко":
if яблоки['рюкзак']=0:
addobj "Яблоки"
end
яблоки['рюкзак']+=1
яблоки[$curloc]-=1
end
act "Взять два яблока":
if яблоки['рюкзак']=0:
addobj "Яблоки"
end
яблоки['рюкзак']+=2
яблоки[$curloc]-=2
end
act "Взять пять яблок":
if яблоки['рюкзак']=0:
addobj "Яблоки"
end
яблоки['рюкзак']+=5
яблоки[$curloc]-=5
end
Вроде бы код здесь тоже повторяется, но не совсем. Что же делать?
И для таких ситуаций в QSP уже предусмотрен специальный механизм. Дело в том, что оператор GOSUB позволяет передавать на локацию, которую мы с его помощью вызываем, различные значения. В данном случае нам нужно передавать на локацию "поднять_яблоко", которую мы писали в предыдущем примере, число яблок. В действиях это будет выглядеть вот так:
act "Взять одно яблоко":
gosub "поднять_яблоко",1
end
act "Взять два яблока":
gosub "поднять_яблоко",2
end
act "Взять пять яблок":
gosub "поднять_яблоко",5
end
Через запятую после названия локации мы можем перечислять различные значения (строковые, числовые, кортежи), и эти значения будут переданы на указанную локацию. Однако где на локации "поднять_яблоко" нам искать эти переданные значения?
Всё просто. Значения, которые мы передаём на локацию таким образом, автоматически помещаются в массив ARGS, в ячейки, начиная с нулевой. И получается, что нам надо искать наше значение в ARGS[0]:
!# поднять_яблоко
if яблоки['рюкзак'] = 0:
addobj "Яблоки"
end
яблоки['рюкзак'] += args[0]
яблоки[$curloc] -= args[0]
Можно передавать до девятнадцати любых значений на локацию и искать их на этой локации в массиве ARGS соответственно в ячейках с нулевой по восемнадцатую. Вот ещё пример передачи аргументов:
Локация "Приготовить":
!# приготовить
*pl "Я должен приготовить <<$args[0]>>, но не просто <<$args[0]>>, а <<$args[0]>> <<$args[1]>>, чтобы съесть <<$args[2]>>."
А вот пример вызова этой локации из действий:
act "Завтрак":
gosub "Приготовить","яичницу","с луком","на завтрак"
end
act "Обед":
gosub "Приготовить","гуляш","с овощами","в обед"
end
act "Ужин":
gosub "Приготовить","салат","с креветками","на ужин"
end
Иногда нам нужно не только передать данные на локацию, но и получить с неё какие-то данные назад. Для этого случая нужно воспользоваться специальной функцией FUNC.
Как и GOSUB, эта функция вызывает локацию, чтобы выполнить на ней какой-то код, однако, в отличие от GOSUB, FUNC может вернуть значение из локации.
Чтобы локация, вызванная с помощью FUNC, вернула какое-то значение, мы должны на этой локации присвоить это значение переменной RESULT. Например, мы хотим написать локацию, на которую будем передавать число, и чтобы эта локация возвращала нам квадрат переданного числа (то есть число умноженное на само себя). Назовём эту локацию "sqr":
!# sqr
result = args[0]*args[0]
Как видите, на локации "sqr" мы умножаем число из ARGS[0] само на себя, а затем результат присваиваем переменной RESULT. Именно значение из переменной RESULT локация "sqr" вернёт с помощью функции FUNC на любой другой локации:
*pl func('sqr',2) & ! на экране будет 4
*pl func('sqr',3) & ! на экране будет 9
*pl func('sqr',4) & ! на экране будет 16
Во всех приведённых примерах функцией является FUNC, а оператором — GOSUB, однако для удобства допустимо называть функциями именно локации, которые мы вызываем с помощью GOSUB или FUNC. Например: «Я написал функцию "поднять_яблоко"».
Описание GOSUB
GOSUB — выполнение кода указанной локации без непосредственного перехода на неё.
Общая запись:
GOSUB [$локация],[аргумент 0],[аргумент 1], ... ,[аргумент 18]
, где [$локация] — это название локации, код которой мы хотим выполнить без непосредственного п ерехода на неё. Значения [аргумент 0], [аргумент 1] и т.д. могут использоваться на этой локации, их значения автоматически помещаются в ячейки массива ARGS[0], ARGS[1], и т.д. соответственно. После обработки локации предыдущие значения ARGS восстанавливаются. Использование аргументов не обязательно.
Оператор имеет краткую форму GS:
GS [$локация],[аргумент 0],[аргумент 1], ... ,[аргумент 18]
Допустимо помещать название локации и аргументы в скобки:
GOSUB([$локация],[аргумент 0],[аргумент 1], ... ,[аргумент 18])
При вызове указанной локации с помощью GOSUB происходит следующее:
- Плеер прерывает выполнение текущего кода (например, кода текущей локации), и обращается к указанной локации
- Базовое описание и список действий указанной локации добавляются к описанию и действиям текущей локации.
- Выполненяется код из поля Выполнить при посещении
- Затем плеер возвращается к выполнению кода, который прервал, к команде сразу после оператора
GOSUB.
На каждой локации автоматически создаётся собственный уникальный массив ARGS, поэтому значения в этом массиве для каждой локации будут свои собственные. После выполнения кода локации, вызванной по GOSUB, массив ARGS этой локации уничтожается.
Обратите внимание! Значения из массива ARGS с локации, вызванной через GOSUB, не транслируются в блоки ACT. В блоки ACT транслируются значения ARGS из текущей локации, т.е. локации, на которую был осуществлён переход с помощью операторов GOTO/XGOTO.
#начало
$args[0] = 'локация начало'
gosub 'переход', 'локация переход'
-
#переход
*pl $args[0] &! На экран выведется 'локация переход'
act 'Перейти':
goto $args[0] &! если нажмётё действие, увидите текст 'локация начало'
end
-
Другие примеры:
! обработка локации "ход"
! На локацию не переадются аргументы
! массив ARGS пуст
GS 'ход'
!обработка локации с названием из переменной $location
!Передаётся один параметр - args[0] равен 1.
GS $location,1
!обработка локации "ход" с передачей 3-х параметров.
! $args[0] = $var (значению), args[1] = 2,
! $args[2] = "данные". Обратите внимание на символы '$'.
GS 'ход',$var,2,'данные'