Несмотря на то, что эта статья обросла некоторыми подробностями, она всё ещё требует серьёзной редактуры, упрощения и расширения. Так что пока что это всё ещё черновой вариант.
Эта статья подробно рассказывает о порядке работы интерпретатора (плеера) QSP. Она может показаться вам довольно сложной, но читать её всю не обязательно. Вернитесь к ней, когда у вас возникнут сложности в понимании того, как ведёт себя плеер. А пока достаточно ознакомиться с общими принципами:
GOTO
или XGOTO
, или вызывать их с помощью GOSUB
, FUNC
, или иным предусмотренным плеером способом.Ниже по тексту будут использоваться следующие определения:
GOTO
или XGOTO
. При этом локация становится "активной", или "текущей". Функция $CURLOC
возвращает название локации, на которую был совершён переход, а массив ARGS[]
этой локации сохраняет свои значения, пока снова не будет осуществлён переход на локацию (другую, или ту же самую). После добавления текста из поля "Базовое описание" в Окно основного описания, действий из поля "Базовые действия" в Окно действий, и выполнения кода из поля "Выполнить при посещении", плеер "останавливается" и ожидает участия игрока, при этом локация, на которую был осуществлён переход, остаётся "активной" ("текущей"), т.е. функция $CURLOC
в любой момент может вернуть название этой локации, а массив ARGS[]
сохраняет значения.GOTO
— осуществляет переход на указанную локацию с автоматической очисткой Окна основного описания и Окна действий.XGOTO
— осуществляет переход на указанную локацию с автоматической очисткой Окна действий. Окно основного описания НЕ очищается.GOSUB
, функции FUNC
, или в связи с выполнением другого события (например, "Выделение предмета", "Загрузка сохранения", "Ввод в поле ввода", "Выбор пункта меню"). В отличие от перехода на локацию, при вызове локация не становится "активной" ("текущей"), т.е. функция $CURLOC
не возвращает название этой локации, а массив ARGS[]
сохраняет свои значения только пока выполняется код локации. После выполнения кода локации, продолжается выполнение того блока кода, который выполнялся до вызова. Например, если локация была вызвана из действия, то после выполнения её кода, продолжится выполнение кода действия, при этом в массив ARGS[]
внутри действия не попадут значения массива ARGS[]
из вызванной локации. При вызове локации в Окно основного описания добавляется текст из поля "Базовое описание" локации, в Окно действий добавляются действия из поля "Базовые действия" локации, и выполняется код из поля "Выполнить при посещении" локации. Очистка окон при вызове локации НЕ ПРОИСХОДИТ. act "Действие с вызовом локации": *pl "Выводим текст до вызова" gosub 'foo' & ! вызываем локацию foo *pl "Продолжаем выполнять код после вызова локации foo" endВызвать локацию можно разными способами:
GOSUB
вызывает локацию без возвращения результата (подробнее в статье "Пользовательские функции и процедуры").Каждая игра на QSP структурно представляет собой набор локаций, последовательно записанных в файл.
Когда мы открываем игру в плеере (интерпретаторе), автоматически запускается чтение самой первой локации в файле (далее Стартовая локация), как если бы на неё был совершён переход с помощью оператора GOTO
. То есть:
$ONNEWLOC
было помещено название локации-обработчика события "Переход на новую локацию", произойдёт автоматический вызов этой самой локации-обработчика события "Переход на новую локацию" (см. "Служебные локации").$CURLOC
в любой момент может вернуть её название, а в массиве ARGS[]
данной локации сохраняются значения, которые на ней были выставлены, и эти значения могут использоваться, например, в действиях, выведенных в Окно действий.
Если на Стартовой локации в переменную $COUNTER
было помещено название локации-счётчика, примерно через равные промежутки времени (по умолчанию раз в пол секунды) плеер будет вызывать локацию-счётчик (см. "Служебные локации").
Код в QSP всегда выполняется последовательно, команда за командой. Чтение команд происходит сверху вниз и справа налево:
*pl "Первая команда" *pl "Вторая команда" *pl "Третья команда" *pl "Четвёртая команда" & *pl "Пятая команда" & *pl "Шестая команда"
QSP не способен выполнить две команды одновременно, или случайно выполнить вторую команду раньше первой. Поэтому в большинстве случаев, если вам кажется, что плеер "забывает" выполнить какую-либо команду, скорее всего эта команда написана в таком месте, где плеер просто не может до неё добраться.
Например, если написано невыполнимое условие, команда никогда не будет выполнена:
if 5>6: *pl "Данная команда никогда не будет выполнена" end
Команды, стоящие после GOTO
или XGOTO
, так же никогда не будут выполнены:
*pl "Текст на локации" & ! этот текст будет виден на локации всегда act "Переход по XGOTO": *pl "Этот текст виден благодаря тому, что вы перешли с помощью XGOTO" xgoto $curloc & ! переходим на текущую локацию *pl "А эта команда никогда не будет выполнена" end act "Переход по GOTO": *pl "Эта команда будет выполнена" ! но при переходе по GOTO Окно основного описания очистится, ! так что эту строчку вы всё равно не увидите. goto $curloc & ! переходим на текущую локацию *pl "А эта команда никогда не будет выполнена" end
Код действий (ACT
) не выполняется сразу, а "прикрепляется" к этим действиям. Он будет выполнен только тогда, когда игрок нажмёт на соответствующее действие.
example=12 & ! присваиваем переменной число 12 ! создаём действие act "Вывести значение переменной example": *pl example end example=37 & ! меняем значение в переменной
То же самое происходит с кодом в гиперссылках. Он не выполняется сразу, когда мы создаём гиперссылку, он выполняется только тогда, когда мы на гиперссылку нажимаем:
usehtml=1 & ! включаем режим распознавание HTML example=12 ! выводим на экран гиперссылку с кодом *pl "<a href='EXEC:*pl example'>Вывести значение переменной example</a>" example=37
При выполнении команды, которая содержит строки с вложенными выражениями, сначала раскрываются вложенные выражения, и только затем происходит работа со строкой, например, передача её оператору для вывода на экран:
яблоки = 23 ! Сначала в строку подставится значение, ! потом строка выведется на экран: *pl "Яблок в кармане: <<яблоки>>."
Переход на новую локацию может осуществляться с помощью двух операторов GOTO
и XGOTO
. Различие в их работе заключается в следующем:
GOTO
очищаются Окно основного описания и Окно действий.XGOTO
очищается только Окно действий. Окно основного описания не очищается.В остальном работа этих операторов схожа:
$ONNEWLOC
было помещено название локации-обработчика события "Переход на новую локацию", произойдёт автоматический вызов этой самой локации-обработчика события "Переход на новую локацию" (см. "Служебные локации").$CURLOC
в любой момент может вернуть её название, а в массиве ARGS[]
данной локации сохраняются значения, которые на ней были выставлены, и эти значения могут использоваться, например, в действиях, выведенных в Окно действий.
Если в переменную $COUNTER
было помещено название локации-счётчика, примерно через равные промежутки времени (по умолчанию раз в пол секунды) плеер будет вызывать локацию-счётчик (см. "Служебные локации").
P.S.: "Переход на новую локацию" — это устоявшееся название события. Технически более правильно называть такие переходы просто "Переход на локацию", поскольку мы можем переходить не только на новые, но и на текущую локацию:
"Счёт: <<count>>" act "Обновить": count+=1 goto $curloc & ! перезаходим на текущую локацию end
Поэтому, столкнувшись с выражением "Переход на новую локацию" помните, что оно может значить в том числе и переход на текущую локацию.
Вызов локации может быть выполнен напрямую через оператор **GOSUB** или функцию **FUNC** или в привязке к какому-либо событию.
При вызове:
Дополнительно, при вызове с помощью оператора GOSUB
, или функции FUNC
, а так же с помощью оператора **MENU**, осуществляется возврат к тому коду, из которого был осуществлён вызов, и продолжается выполнение этого кода. Например, если локация была вызвана из действия, то произойдёт возврат к выполнению кода действия.
act "Действие с вызовом локации": *pl "Выводим текст до вызова" gosub 'foo' & ! вызываем локацию foo *pl "Продолжаем выполнять код после вызова локации foo" end
Более подробно работа оператора GOSUB
и функции FUNC
освещена в разделе "Пользовтальские функции и процедуры".
О работе оператора MENU
более подробно можно почитать в разделе "Меню".
События, если смотреть на это понятие с точки зрения работы плеера, — это некое изменение состояния в написанной нами игре. Например, у нас все предметы были не выделены, и вот игрок щёлкает мышью по одному из предметов, и предмет оказывается выделен. То есть предмет изменил своё состояние с "не выделен", на "выделен". Это и есть событие выделения предмета.
Конечно, мы могли бы и сами отслеживать некоторые события, например, с помощью локации-счётчика постоянно проверять, какое значение возвращает нам функция $SELOBJ
, и в момент, когда функция изменяет своё значение с одного на другое, мы точно знаем, что произошло выделение нового предмета, или иными словами: событие "Выделение предмета". Однако, для нас это, во-первых, лишний код, а, во-вторых, очень неудобный и неточный инструмент по отслеживанию событий, поскольку локация-счётчик имеет ряд ограничений и на скорость выполнения, и на очерёдность. Мы хотим, чтобы миллисекунда в миллисекунду мы знали, что какое-то событие произошло, и чтобы при этом мы могли выполнить какой-либо код.
Именно для этого в QSP введены специальные служебные локации: локации-обработчики событий (и локация-счётчик).
Любая локация, названная как угодно, и написанная каким угодно образом, может быть назначена обработчиком-события или локацией-счётчиком. Для этого название этой локации нужно прописать в специальную системную переменную. Например:
$counter = 'счётчик' & ! назначаем локацию-счётчик $onobjsel = 'onClick' & ! назначаем локацию-обработчик события "Выделение предмета" $usercom = 'debugger' & ! назначаем локацию-обработчик события "Нажатие клавиши ввода в Поле ввода"
Как видите, название локации может быть совершенно любым, главное, чтобы она была прописана в нужную системную переменную. И как только это происходит, как только мы прописываем имя локации в нужную переменную, эта локация становится связана с указанным событием. Это значит, что как только это событие случится, будет выполнен код на указанной локации.
Подробнее о том, какая системная переменная за связку с каким событием отвечает, вы можете прочитать в статье "Служебные локации".
Чтобы отвязать локацию от события, достаточно в нужную системную переменную прописать пустое значение:
$counter = '' & ! отключаем локацию-счётчик $onobjsel = '' & ! отключаем локацию-обработчик события "Выделение предмета"
Если была назначена локация-счётчик, то с приблизительно равной периодичностью будет происходить вызов указанной локации-счётчика. По умолчанию локация-счётчик вызывается 2 раза в секунду, то есть каждые 500 миллисекунд — это значение можно изменять с помощью оператора SETTIMER
.
settimer 100 & ! устанавливаем период обращения к локации-счётчику в 100 мс $counter = 'отсчёт_времени' & ! плеер будет вызывать локацию **отсчёт времени** примерно 10 раз в секунду
Так же с заданной периодичностью (по умолчанию 2 раза в секунду) происходит полное обновление интерфейса: шрифт и цвета, заданные с помощью системных переменных.
Подробнее о локации-счётчике и создании игровых событий в реальном времени читайте в статье "Реальное время".