Перейти к основному содержимому

Назад: Предметы

Порядок работы интерпретатора

Несмотря на то, что эта статья обросла некоторыми подробностями, она всё ещё требует серьёзной редактуры, упрощения и расширения. Так что пока что это всё ещё черновой вариант.

Эта статья подробно рассказывает о порядке работы интерпретатора (плеера) 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
    Вызвать локацию можно разными способами:
  • Блок кода — это выделенный в отдельное целое фрагмент кода игры. Отдельными блоками кода в QSP являются:
    • Локации сами со себе.
    • Код, передаваемый оператору DYNAMIC или функции DYNEVAL в виде текста.
    • Код, выполняемый при нажатии на гиперссылку.
    • Код каждого отдельного Действия (ACT).
    • Код каждого отдельного Цикла (LOOP)

Запуск игры

Каждая игра на QSP структурно представляет собой набор локаций, последовательно записанных в файл.

Когда мы открываем игру в плеере (интерпретаторе), автоматически запускается чтение самой первой локации в файле (далее Стартовая локация), как если бы на неё был совершён переход с помощью оператора GOTO. То есть:

  • В Окно основного описания добавляется текст из поля "Базовое описание" локации (поле "Описание" в Quest Generator).
  • В Окно действий добавляются действия из поля "Базовые действия" локации (поле "Базовые действия" в Quest Generator).
  • Выполняется код из поля "Выполнить при посещении" локации.
  • Если на Стартовой локации в переменную $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 очищается только Окно действий. Окно основного описания не очищается.

В остальном работа этих операторов схожа:

  • В Окно основного описания добавляется текст из поля "Базовое описание" локации (поле "Описание" в Quest Generator).
  • В Окно действий добавляются действия из поля "Базовые действия" локации (поле "Базовые действия" в Quest Generator).
  • Выполняется код из поля "Выполнить при посещении" локации.
  • Если в переменную $ONNEWLOC было помещено название локации-обработчика события "Переход на новую локацию", произойдёт автоматический вызов этой самой локации-обработчика события "Переход на новую локацию" (см. "Служебные локации").
  • После этого плеер "останавливается" и ожидает действий от игрока. При этом локация остаётся "активной", т.е. функция $CURLOC в любой момент может вернуть её название, а в массиве ARGS[] данной локации сохраняются значения, которые на ней были выставлены, и эти значения могут использоваться, например, в действиях, выведенных в Окно действий.

Если в переменную $COUNTER было помещено название локации-счётчика, примерно через равные промежутки времени (по умолчанию раз в пол секунды) плеер будет вызывать локацию-счётчик (см. "Служебные локации").

P.S.: "Переход на новую локацию" — это устоявшееся название события. Технически более правильно называть такие переходы просто "Переход на локацию", поскольку мы можем переходить не только на новые, но и на текущую локацию:

"Счёт: <<count>>"
act "Обновить":
count+=1
goto $curloc & ! перезаходим на текущую локацию
end

Поэтому, столкнувшись с выражением "Переход на новую локацию" помните, что оно может значить в том числе и переход на текущую локацию.

Вызов локации

Вызов локации может быть выполнен напрямую через оператор GOSUB или функцию FUNC или в привязке к какому-либо событию.

При вызове:

  • В Окно основного описания добавляется текст из поля "Базовое описание" локации (поле "Описание" в Quest Generator).
  • В Окно действий добавляются действия из поля "Базовые действия" локации (поле "Базовые действия" в Quest Generator).
  • Выполняется код из поля "Выполнить при посещении" локации.

Дополнительно, при вызове с помощью оператора 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 раза в секунду) происходит полное обновление интерфейса: шрифт и цвета, заданные с помощью системных переменных.

Подробнее о локации-счётчике и создании игровых событий в реальном времени читайте в статье "Реальное время".

Вперёд: Переменные