Игры для движка STEAD пишутся на языке \href{http://www.lua.org}{Lua}. Знание этого языка будет очень полезным при написании игр, но я постараюсь сделать описание настолько подробным, что даже новичок в этом языке смог создавать игры для INSTEAD без проблем. Между прочим, знающим Lua будет небезынтересно посмотреть код движка.
\textbf{Главное окно} игры содержит информацию о статической и динамической части сцены, активные события и картинку сцены с возможными переходами в другие сцены (в графическом интерпретаторе).
\textbf{Динамическая часть} сцены составлена из описаний объектов сцены, она отображается всегда. Она может выглядеть так: <<Стоит стол. Рядом стоит стул>>. Если динамическая часть пуста, то игроку не с чем контактировать в сцене.
\textbf{Статическая часть} сцены описывает саму сцену, её <<декорации>>. Она отображается при показе сцены (единожды или каждый раз -- решает автор игры), или при повторении команды look (в графическом интерпретаторе при щелчке на названии сцены).
Игрок имеет собственный \textbf{инвентарь}. В нём лежат объекты, доступные на любой сцене. Чаще всего инвентарь рассматривают как некую <<котомку>>, в которой лежат объекты; в этом случае каждый объект считают предметом. Такая трактовка практична, обыденна и интуитивна; но не единственна. Понятие инвентаря является условным, ведь это лишь контейнер. В нём могут находиться такие объекты, как <<открыть>>, <<потрогать>>, <<лизнуть>>. Можно наполнить его объектами <<нога>>, <<рука>>, <<мозг>>. Автор игры свободен в определении этих понятий, но он также должен определить действия игрока над ними.
На рисунке \ref{INSTEAD-running} очень чётко видны границы между этими областями. Главное окно имеет бежевый фон, инвентарь -- чёрный. Динамическая часть идёт сразу после ссылок перехода и выделена курсивом; статическая часть отпечатана обычным шрифтом.
Действие на объект сцены обычно понимается как изучение объекта, или использование его. Например, если в сцене существует объект <<чашка кофе>>, то действием на него может быть выпивание кофе, тщательный осмотр чашки, разбивание чашки или перемещение чашки в инвентарь. Это определяется только автором и никем другим.
Действие на объект инвентаря понимается аналогично. Например, если в инвентаре лежит объект <<яблоко>>, его можно съесть или осмотреть. С другой стороны, если в инвентаре лежит объект <<осмотреть>>, то действие над ним будет трудно описать логически.
Действие объектом инвентаря на объект сцены -- это чаще всего использование или передача объекта. Например, действие объектом <<нож>> на объект <<бармен>> может означать передачу ножа бармену, угрозу ножом бармену, убийство ножом бармена и многое другое.
Действие объектом инвентаря на объект инвентаря понимается так же свободно. Это может быть соединение предметов (<<сарделька>> + <<кетчуп>>) в одно (<<сарделька с кетчупом>>), либо использование (<<открыть>> + <<ящик>>).
Игра представляет из себя каталог, в котором должен находиться скрипт main.lua. Другие ресурсы игры (скрипты на lua, графика и музыка) должны находиться в рамках этого каталога. Все ссылки на ресурсы делаются относительно текущего каталога -- каталога игры.
Игра начинается именно с main.lua. В начале файла main.lua может быть определён заголовок, состоящий из тегов. Теги должны начинаться с символов комментария \verb/--/. На данный момент существуют два тега: \verb/$Name:,/ который должен содержать название игры, и \verb/$Version:,/который указывает версию игры. Пример использования тега:
Если это указание отсутствует, то STEAD будет работать в режиме совместимости, используя устаревшее API. Описание устаревшего API находится в предыдущих версиях данного справочника и убрано из данного издания для краткости.
Windows сборка использует каталог \verb/куда-вы-установили-INSTEAD\games/.
Расположение пользовательских игр также зависит от вашей версии Windows. Например, в Windows Vista игры могут лежать в каталоге \verb/AppData\Local\instead\games/.
На данный момент активно развивается только графическая ветка интерпретатора -- INSTEAD-SDL. Поэтому справочник в б\'{о}льшей мере описывает её возможности.
Отмечу, что пример выше является минимальной игрой для INSTEAD. Это некий <<Hello, World>>, который я рекомендую сохранить под именем main.lua и поместить в отдельную папку в каталоге для игр.
Атрибут \verb/nam/ (имя) является необходимым для любого объекта. Для сцены это -- то, что будет заголовком сцены при её отображении. Имя сцены также используется для её идентификации при переходах.
\textbf{\textcolor{red}{Внимание!!!}} Если для вашего творческого замысла необходимо, чтобы описание статической части сцены выводилось на каждом ходу (а не только при первом входе в сцену), вы можете определить для своей игры параметр forcedsc (в начале игры).
\begin{verbatim}
game.forcedsc = true;
\end{verbatim}
Или, аналогично, задать атрибут forcedsc для конкретных сцен.
\verb/dsc/ -- описатель объекта. Он будет выведен в динамической части сцены. Фигурными скобками отображается фрагмент текста, который будет являться ссылкой в графическом интерпретаторе. Если вы забудете сделать ссылку, то интерпретатор не выдаст ошибки, но игроки не смогут взаимодействовать с объектом.
\verb/act/ -- это обработчик, который вызывается при действии пользователя на объект сцены. Если объект находится в инвентаре, то действие с ним будет передаваться другому обработчику -- \verb/inv/.
До сих пор в примерах приводились примитивные обработчики, которые всего лишь возвращают определённую строку. В примере выше обращение к объекту вызовет банальную реакцию: интерпретатор напечатает строку <<Гм... Просто стол...>>. Хуже того: он будет отвечать тем же образом каждый раз при обращении к объекту. Это не совсем гибкий подход, поэтому STEAD позволяет определить любой атрибут объекта как функцию. Так, возможно построить такую конструкцию:
Если атрибут или обработчик оформлен как функция, то обычно первый аргумент функции \verb/(s)/ есть сам объект. В данном примере, при показе сцены будет в динамической части сцены будет текст: <<На столе что-то лежит>>. При взаимодействии со ссылкой <<что-то>>, переменная \verb/_seen/ объекта \verb/apple/ будет установлена в \verb/true/, и мы увидим, что это было яблоко.
\index{Переменные}
Запись \verb/s._seen/ означает, что переменная \verb/_seen/ размещена в объекте \verb/s/ (то есть, \verb/apple/). В языке Lua переменные необязательно объявлять заранее, при первом обращении к ней переменная \verb/apple._seen/ появится сама; но хорошим тоном будет заранее \textbf{проинициализировать} переменную со значением \verb/false/.
Подчёркивание в имени переменной означает, что она \textbf{попадёт} в файл сохранения игры. Сохраняются все переменные, название которых начинается сс подчёркивания.
Вы можете переопределить функцию \verb/isForSave(k)/, если вас это не устраивает, либо (рекомендованный вариант) воспользоваться блоком \verb/var/, в котором сохраняются все переменные. Для краткости примеры этого руководства не следят за сохранением переменных.
\textbf{\textcolor{red}{Внимание!!!}} Переменные в любом случае не записываются в файл сохранения, если они не размещены в одном из перечисленных типов объектов: комната, объект, диалог, игра, игрок, глобальное пространство.
В файл сохранения могут быть записаны строки, числа, логические значения, ссылки на объекты и конструкции \verb/code/ (о них чуть ниже).
Также вы можете определять переменные при помощи блоков \verb/var/ и \verb/global/, о которых будет рассказано позже.
Lua позволяет опускать скобки вокруг параметров, если параметр всего один.
Если необходимо показать, что действие невыполнимо, можно вернуть из обработчика значение \verb/false/ или \verb/nil/. При этом будет отображено описание по умолчанию -- так, для обработчика \verb/act/ это будет \verb`game.act.`
Иногда, сцену нужно наполнить декорациями, которые обладают ограниченной функциональностью, но делают игру разнообразней. Для этого можно использовать облегчённый объект. Например:
Как видим, \verb/vobj/ позволяет сделать лёгкую версию статического объекта, с которым тем не менее можно взаимодействовать (за счёт определения обработчика \verb/act/ в сцене и анализа ключа объекта). \verb/vobj/ также вызывает метод \verb/used/, при этом в качестве третьего параметра передаётся объект, воздействующий на виртуальный объект.
Созданный объект будет попадать в файл сохранения. \verb/new()/ возвращает реальный объект; чтобы получить его имя, если это нужно, используйте функцию \index{Функции!deref}\verb/deref()./
Игрок может действовать объектом инвентаря на другие объекты. При этом вызывается обработчик \verb/use/ у объекта которым действуют и \verb/used/ -- на которого действуют.
\verb/use/ и \verb/used/ могут быть функциями. Тогда первый параметр это сам объект, а второй -- ссылка на объект, на который направлено действие в случае \verb/use/ и объект, которым действие осуществляется в случае \verb/used/.
\verb/use/ может вернуть статус \verb/false/, в этом случае обработчик \verb/used/ не вызывается (если он вообще был). Статус обработчика \verb/used/ -- игнорируется. Это будет выглядеть как:
Возможно также действовать объектами сцены на объекты сцены; для этого нужно установить переменную \verb/game.scene_use = true/ или поставить \verb/scene.use=true/ в нужной комнате. В этом случае использование объектов сцены будет аналогично использованию объектов инвентаря.
Скрытый объект -- это объект, который находится в сцене, но на данный момент словно бы <<выключен>>. Он присутствует для движка, но не существует для игрока. Его описание не выводится и с ним невозможно контактировать. Это можно использовать, например, вместо динамического создания объектов.
Чтобы создать заведомо выключенный объект, необходимо воспользоваться конструкцией вида:
При этом, вы сможете переходить между сценами \verb/main/ и \verb/room2/. Как вы помните, \verb/nam/ может быть функцией, и вы можете генерировать имена сцен на лету, например, если вы хотите, чтобы игрок не знал название сцены, пока не попал на неё.
При переходе между сценами движок вызывает обработчик \verb/exit/ из текущей сцены и \verb/enter/ той сцены, куда идёт игрок. \verb/exit/ и \verb/enter/ могут быть функциями -- тогда первый параметр это сам объект, а второй -- ссылка на комнату куда игрок хочет идти (для \verb/exit/) или из которой уходит (для \verb/enter/).
Как видим, обработчики могут возвращать два значения: строку и статус. В нашем примере функция \verb/exit/ вернёт \verb/false/, если игрок попытается уйти из зала в \verb/main/. \verb/false/ блокирует переход. Такая же логика работает и для \verb/enter/. Кроме того, она работает и для обработчика \verb/tak/ (о нём чуть позже).
Если предмет сцены имеет обработчик \verb/tak/ и НЕ имеет обработчика \verb/act/ \footnote{Пользуясь случаем: я считаю, что tak -- немного неудобное имя. Именно так. -- А.Я.}, то при действии на нём вызывается не \verb/act/, а\verb/tak/; после этого предмет перемещается в инвентарь. Это происходит вот так:
Игра представлена объектом \verb/game/. Он хранит в себе указатель на текущего игрока (\verb/'pl'/) и некоторые параметры. Например, вы можете указать в начале своей игры кодировку текста следующим образом:
Кроме того, объект \verb/game/ может содержать обработчики по умолчанию \verb/act/, \verb/inv/, \verb/use/, которые будут вызваны, если в результате действий пользователя не будут найдены никакие другие обработчики. Например, вы можете написать в начале игры:
\begin{verbatim}
game.act = 'Не получается.';
game.inv = 'Гм.. Странная штука..';
game.use = 'Не сработает...';
\end{verbatim}
На практике полезно что-то вроде:
\begin{verbatim}
game.inv = function()
local reaction = {
[1] = 'Либо я ошибся карманом, либо мне нужно что-то другое.',
Таймер -- это объект \verb/timer/, который служит для отсчёта \textbf{реального} времени (в миллисекундах). В этом его существенное отличие от атрибутов \verb/life/, которые служат для измерения игрового времени (в шагах).
Двоеточия при вызове \verb/set/ и \verb/stop/ важны, их не стоит заменять на точки. По умолчанию таймер выключён, и не имеет заданного обработчика. Если таймер включён, то обработчик вызывается с заданным интервалом.
Ввод с клавиатуры анализируется при помощи объекта \verb/input/. Этот объект принимает все нажатия клавиш. По умолчанию он не имеет запрограммированных обработчиков, поэтому ничего не выполняет.
Создание нового обработчика для клавиши выполняется командой \verb/input.key(s, pressed, key),/ где \verb/pressed/ -- нажатие или отжатие, \verb/key/ -- символьное имя клавиши;
Если обработчик возвращает не \verb/nil/, то клавиша обрабатывается \textbf{им}, а не интерпретатором. Таким образом, можно переопределить обработку любой клавиши.
Третьим важным типом в движке являются диалоги. Диалоги -- это особый подвид сцен, содержащий только фразы. Например, диалог может выглядеть следующим образом:
\verb/phr/ -- создание фразы. Фраза содержит вопрос, ответ и реакцию (реакция в данном примере отсутствует). Когда игрок выбирает одну из фраз, фраза отключается. Когда все фразы отключатся диалог заканчивается. Реакция -- это строка кода на lua который выполнится после отключения фразы.
\verb/_phr/ -- создание выключенной фразы. Она не видна изначально, но её можно включить с помощью функции \verb/pon()/ (см. ниже). По смыслу это эквивалентно \verb/phr(<...>):disable()./
\item[prem(n...)] удалить (заблокировать) фразы диалога с номерами n... Удаление означает невозможность включения фраз. \verb/pon(n..)/ не приведёт к включению фраз.
\item[pseen(n...)] вернет true, если все заданные фразы диалога видимы.
\item[punseen(n...)] вернет true, если все заданные фразы диалога невидимы.
\item[look]\index{Методы!look} Получение индекса элемента в списке по идентификатору
\item[srch]\index{Методы!srch} Проверка на наличие объекта в списке по его nam. Возвращает 2 значения: идентификатор и позицию; если объекта нет, вернёт nil. Например: \begin{verbatim}objs():srch('Ножик')\end{verbatim}
\item[set]\index{Методы!set} Изменение объекта по номеру. Например, этот пример присвоит заменит первый объект списка: \begin{verbatim}objs():set('knife',1);\end{verbatim}
\item[disable]\index{Методы!disable} Скрытие объекта в списке; отличается от удаления тем, что может быть возвращён к жизни через метод enable
\item[enable]\index{Методы!enable} Показ скрытого объекта
\item[zap]\index{Методы!zap} Обнуление списка
\item[cat(b)]\index{Методы!cat} Склеивает список со списком b
Замечание. Если аргумент у функции единственен и текстовый, то скобки вокруг аргумента можно опускать. Большинство описываемых в этом разделе функций с несколькими параметрами могут
использоваться без указания всех параметров. В этом случае опущенные параметры принимают значение по умолчанию (обычно -- текущая сцена).
\item[inv()]\index{Функции!inv} возвращает список инвентаря
\item[objs()]\index{Функции!objs} возвращает список объектов указанной сцены; если сцена не указана, то возвращает список объектов текущей сцены.
\item[ways()]\index{Функции!ways} возвращает список возможных переходов из указанной сцены; если сцена не указана, то возвращает список возможных переходов из текущей сцены.
\item[me()]\index{Функции!me} возвращает объект pl (объект игрока)
\item[ref(nam)]\index{Функции!ref} возвращает указанный объект: \begin{verbatim}ref('home') == home\end{verbatim} nam может быть функцией, строкой или текстовой ссылкой
\item[change\_pl(player)]\index{Функции!change\_pl} переключает на другого игрока (со своим инвентарём и позицией), также используется в \linebreak\verb/return/. Может использоваться для переключения между разными инвентарями.
\item[cat(...)]\index{Функции!cat} возвращает строку -- склейку строк-аргументов. Если первый аргумент nil, то функция возвращает nil.\footnote{Функция легко и просто заменяется обычным оператором склейки строк Lua: \texttt{строка1 .. строка2}. Тем не менее, этот оператор выдаст ошибку при склейке nil}
Если вы хотите перенести объект из произвольной сцены, вам придётся удалить его из старой сцены с помощью метода del. Для создания сложно перемещающихся объектов, вам придётся написать свой метод, который будет сохранять текущую позицию объекта в самом объекте и делать удаление объекта из старой сцены. Вы можете указать исходную позицию (комнату) объекта в качестве третьего параметра move.
Игра измеряет время в своих единицах -- в шагах, или активных действиях. Каждое действие игрока -- это его шаг, пусть даже он тратит его на то, чтобы ещё раз осмотреться. Что он увидит нового? Что изменится в мире игры за это время?
В этом примере кот по имени Барсик, сидя в инвентаре у игрока, будет на каждом шагу показывать свою активность. Но приведённый код пока что не будет работать. Для того, чтобы объявить объект <<живым>>, следует добавить его в соответствующий список с помощью функции \verb/lifeon()/.
Любой объект или сцена могут иметь свой обработчик \verb/life/, который вызывается каждый раз при смене текущего времени игры, если объект или сцена были добавлены в список живых объектов с помощью \verb/lifeon/. Не забывайте удалять живые объекты из списка с помощью \verb/lifeoff/, когда они больше не нужны. Это можно сделать, например, в обработчике \verb/exit/, или любым другим способом.
Вы можете вернуть из обработчика life второй код возврата, важность. (true или false). Если он равен true, то возвращаемое значение будет выведено ДО описания объектов; по умолчанию значение равно false.
Если вы хотите <<очистить экран>>, то можно воспользоваться следующим хаком. Из метода \verb/life/ доступна переменная \verb/ACTION_TEXT/ -- это тот текст, который содержит реакцию на действие игрока. Соответственно, сделав \verb/ACTION_TEXT = nil/, можно <<запретить>> вывод реакции. Например, для перехода на конец игры можно сделать:
В чём очевидное преимущество графического интерпретатора над текстовой веткой -- это то, что он может говорить и показывать. Проще говоря, вы можете добавить в игру графику и музыку.
Графический интерпретатор анализирует атрибут сцены \verb/pic/, и воспринимает его как путь к картинке-ил\-лю\-страции для сцены. Если в текущей сцене не определён атрибут \verb/pic/, то берётся \verb/game.pic/. Если не определён и он, то картинка не отображается.
Unix-версия движка поставляется в исходных кодах, поэтому поддержка форматов графики и музыки в основном определяется возможностями библиотеки SDL, установленной в вашей системе. В большинстве случаев обеспечивается поддержка всех основных форматов. Для графики я рекомендую использовать форматы PNG, JPG и GIF (также поддерживаются анимированные GIF). Новые версии движка поддерживают GIF-анимацию. Те же правила действуют для музыки. Здесь строгих рамок нет, поэтому старайтесь не использовать редких форматов. Проверена поддержка WAV, MP3, OGG, FLAC, XM, MOD, IT, S3M.
При этом изображения можно комбинировать, накладывая друг на друга. Очень удобны в этом отношении PNG и GIF, потому что они позволяют задать прозрачность фона.
В случае анимированных GIF наложение происходит только с первым кадром.
Картинки складываются стопкой снизу вверх. Самая первая картинка является фоном. Она должна быть самой большой; её границы - это и границы всей картинки.
Точка с запятой разделяет изображения; после неё идёт сначала путь к картинке, затем символ \verb/@/ и координаты точки фона, куда будет помещён левый верхний угол накладываемой картинки.
Координаты считаются от левого верхнего угла фонового изображения. Если после символа \verb/@/ идёт буква \verb/c/, то в эту точку будет помещён не левый верхний угол, а\textbf{центр} накладываемой картинки.
Помимо фонового сопровождения, \verb/set_sound()/ позволяет проиграть звуковой файл. \verb/get_sound()/ возвращает имя звукового файла, который будет проигран.
Переменная в vars может содержать участок кода (\verb/code [[...]]/), который также будет сохранён. Таким образом, вы можете создавать сложные сохраняемые обработчики.
Немного по последней функции. Код \verb/txtnb(' ')/ даст вам один неразрывный пробел. Неразрывный пробел - это пробел, который нельзя заменить переходом на новую строку. То есть, если вы напишете \verb/txtnb('a b')/, то это будет гарантировать вам, что \verb/a/ и \verb/b/ находятся на одной строке, а не на разных.
Проверка правописания готовой игры -- это большая головная боль. У вас есть примерно 100 Кб кода, в которых находятся около 80 Кб текста. Любая программа проверки орфографии будет сильно ругаться на синтаксис Lua и мешать. Один из способов проверки -- использовать редактор Emacs.
\item Пробел для того, чтобы пропустить слово; Латинская a, чтобы игнорировать слово вообще; i чтобы добавить слово в словарь пользователя; цифры, чтобы заменить слово на один предложенных вариантов.
Если вы впервые видите этот редактор, я настоятельно НЕ рекомендую нажимать что-нибудь и щёлкать на что-нибудь непонятное. Будет только непонятнее.
\subsection{Меню}
Вы можете делать меню в области инвентаря, определяя объекты с типом menu. При этом, обработчик меню будет вызван после клика мыши. Если обработчик не возвращает текст, то состояние игры не изменяется. Например, реализация кармана:
\textbf{\textcolor{red}{Важное замечание:}} Лучше не использовать компиляцию игр с помощью luac, так как luac создаёт платформозависимый код. Таким образом, вам придётся выдавать сразу две скомпилированных версии: для 32-битных и 64-битных машин\footnote{А в будущем, возможно, SDL-INSTEAD будет поддерживать больше платформ, поэтому использовать предложенный метод будет ещё выгоднее}. Однако, компиляция игр может быть использована для поиска ошибок в коде.
И запустите игру в lua: \verb/lua game.lua/. При этом игра будет работать в примитивном shell окружении. Полезные команды: ls, go, act, use.... Теоретически движок можно таким образом привязать даже к CGI окружению.
Также рекомендуется использовать модуль \verb/dbg/. После подключения этого модуля в инвентаре появится объект "отладка". Нажав на него (либо на F7), вы войдёте в диалог отладки, откуда сможете управлять игрой. Подробное описание модуля смотрите \href{http://instead.pinebrush.com/wiki/ru/gamedev/modules/dbg}{здесь}.
Тема, которая является минимально необходимой -- это тема \verb/default/. Эта тема всегда загружается первой. Все остальные темы наследуются от неё и могут частично или полностью заменять её параметры. Выбор темы осуществляется пользователем через меню настроек, однако конкретная игра может содержать собственную тему и таким образом влиять на свой внешний вид. В этом случае в каталоге с игрой должен находиться свой файл \verb/theme.ini/. Тем не менее, пользователь свободен отключить данный механизм, при этом интерпретатор будет предупреждать о нарушении творческого замысла автора игры.
Цвет задаётся в форме \verb/#rgb/, где \verb/r/, \verb/g/ и \verb/b/ -- компоненты цвета в шестнадцатеричном виде. Кроме того некоторые основные цвета распознаются по своим именам (см. таблицу~\ref{table_colors})
\index{Окно сцены} Окно сцены -- область, в которой располагается сцена. Интерпретация зависит от режима расположения. (см. таблицу~\ref{table_param_screen})
В режиме \verb/embedded/ картинка является частью содержимого главного окна, параметры главного окна (см. ниже) \verb/scr.gfx.x/, \verb/scr.gfx.y/, \verb/scr.gfx.w/ игнорируются.
В режиме \verb/float/ картинка расположена по указанным координатам (\verb/scr.gfx.x/, \verb/scr.gfx.y/) и масштабируется к размеру \verb/scr.gfx.w/ x \verb/scr.gfx.h/ если превышает его.
В режиме \verb/fixed/ -- картинка является частью сцены как в режиме \verb/embedded/, но не скроллируется вместе с текстом, а расположена непосредственно над ним.
В горизонтальном режиме инвентаря в одной строке могут быть несколько предметов. В вертикальном режиме, в каждой строке инвентаря содержится только один предмет.
Кроме того, заголовок темы может включать в себя комментарии с тегами. На данный момент существует только один тег: \verb/$Name:/, содержащий строку с именем темы. Например:
Игра может задавать собственную тему; для этого в каталоге с игрой должен лежать тот самый \verb/theme.ini/. Его формат никак при этом не меняется, просто эта тема загружается сразу после default темы вместе с игрой.
Начиная с версии 1.2.0, INSTEAD также распространяется с некоторым набором полезных модулей. Использование этих модулей очень облегчает разработку игр на INSTEAD.
Модуль подключается при помощи следующей конструкции:
\begin{verbatim}
require "имя модуля"
\end{verbatim}
в начале файла main.lua. При этом важен порядок подключения: некоторые модули могут вызывать ошибки, будучи подключёнными в неправильном порядке.
\subsection{Click}
Модуль \verb/click/ позволяет перехватывать щёлканья мышкой по иллюстрации к сцене.
При клике вызывается обработчик \verb/click/ текущей сцены, либо универсальный обработчик \verb/game.click./
Обработчик получает три параметра: объект (комната или \verb/game/) и координаты клика. Координаты — это абсцисса (\verb/x/) и ордината (\verb/y/) в системе координат оригинального (не масштабированного) изображения. То есть, если оригинальная иллюстрация имеет размер 1024x768 пикселов, но SDL-INSTEAD запущен в полноэкранном режиме 800x600, то координаты будут всё равно вычисляться в системе 1024x768. Иначе код игры пришлось бы переписывать заново для каждого возможного размера картинки.
Координаты считаются от верхнего левого угла.
\begin{verbatim}
game.click = function(scene, x, y)
p ("Click at:",x,",", y);
end
house = room {
nam = 'Дом';
pic = 'house.png';
click = function(scene, x, y)
if x > 100 and x < 120 and y > 50 and y < 90 then
goto 'street'
end
end
}
\end{verbatim}
\subsection{Dbg}
Подключение этого модуля добавляет в инвентарь объект \verb/debug/. При нажатии клавиши F7 или выборе этого объекта вы попадаете в меню, из которого можете:
\begin{itemize}
\item переходить между локациями
\item оперировать инвентарём (подбирать и бросать предметы)
\item выполнять произвольный код Lua
\item следить за состоянием объектов
\end{itemize}
Модуль является сильнейшим средством обмана игры и не рекомендуется для включения на уже выпущенных играх.
\subsection{Format, quotes, para, dash}
Модуль \verb/format/ служит для переформатирования выводимого текста согласно русской книгопечатной традиции.
Замена происходит только при выводе содержимого сцены. Замена не производится в выводе инвентаря и меню.
Модуль имеет всего 4 настройки:
Функция \verb/format.filter/ служит для задания пользовательского фильтра. Она получает текст, который будет выведен игроку.
Если настройка \verb/format.para/ установлена в \verb/true/, то в начало каждого параграфа будет вставлен небольшой отступ. Например, как в этом справочнике. Вы можете подключить модуль \verb/para/ вместо включения этой настройки.
Если настройка \verb/format.dash/ установлена в \verb/true/, то каждая последовательность символов \verb/--/ будет заменена на среднее тире (--). Вы можете подключить модуль \verb/dash/ вместо включения этой настройки.
Наконец, если настройка \verb/format.quotes/ установлена в \verb/true/, то будут производиться следующие замены: см. табл. \ref{table_quotes}
\begin{table}[ht]
\begin{center}
\begin{tabular}{|lc|}
\hline
Последовательность символов & Результат \\
\hline
\verb/<</ & <<\\
\verb/>>/ & >>\\
\verb/,,/ & ,,\\
\verb/''/ (два апострофа) & ''\\
\verb/_"/ & <<\\
\verb/"_/ & >>\\
\verb/"/ & << или >>, угадывается автоматически\\
\hline
\end{tabular}
\end{center}
\caption{Замены кавычек}\label{table_quotes}
\end{table}
В соответствии с русской типографской традицией, вложенные кавычки должны быть другого стиля: <<кавычки „кавычки внутренние” внешние>>. Тем не менее, при использовании \verb/_"/ и \verb/"_/ , результат не предсказуем до конца и может быть таким: \guillemotleft внешние кавычки \guillemotleft кавычки внутренние\guillemotright, что также допустимо.
Вы можете подключить модуль \verb/quotes/ вместо включения этой настройки.
\subsection{Hideinv}
Модуль позволяет временно отключать инвентарь. Например, вы хотите вставить в середину игры сцену-заставку, в которой от игрока не требуется управление инвентарём. Для этого эта сцена должна быть определена с атрибутом \verb/hideinv/ в значении \verb/true/:
\begin{verbatim}
require "hideinv"
happyend = room {
nam = 'Конец';
hideinv = true;
dsc = [[ Вы прошли игру! ]];
}
\end{verbatim}
Если вы хотите использовать \verb/hideinv/ не в обычной комнате, а в \verb/xroom/, то подключите модуль \verb/hideinv/ перед модулем \verb/xact/.
\subsection{Hotkeys}
При подключении этого модуля становится возможным выбирать реплики в диалогах при помощи клавиш 1-9. Так, первая реплика выбирается клавишей 1, вторая -- клавишей 2 и т.д. Настроек модуль не имеет.
\subsection{Kbd}
Модуль \verb/kbd/ служит для перехвата событий с клавиатуры.
Он содержит две функции:
Функция \verb/hook_keys/ ставит событие перехвата на клавиши, которые переданы ей в качестве параметров. Событие возникает, когда нажата \textbf{любая} из указанных клавиш. При возникновении события выполняется блок here().kbd или же, если он отсутствует, блок game.kbd. Выполняющемуся блоку передаются три параметра: self (локация или game), булево значение нажатия (true означает нажато, false -- отжато) и текст клавиши.Например:
\begin{verbatim}
hook_keys('a', 'b', 'c')
\end{verbatim}
Событие here().kbd будет вызвано при нажатии любой из клавиш a, b или c.
Клавиши передаются функции соответственно своим клавиатурным кодам, определённым в SDL. Для удобства некоторые коды клавиш приведены в табл. \ref{table_keyboard}
Чтобы снять перехват с клавиш, следует вызвать функцию \verb/unhook_keys./ Её синтаксис аналогичен предыдущей функции, значение же противоположно.
Примером использования модуля \verb/kbd/ также служит модуль \verb/hotkeys./
От обычного сохранения он отличается только тем, что он управляется только разработчиком игры. Вы можете создавать сколько угодно снапшотов (тогда как сохранений может быть только 5). Пользователь может удалять снапшоты (зная, где они лежат и как выглядят), но не может их редактировать.
Сохранения можно вызывать из игры, но нельзя загружать. Сохранения доступны глобально, снапшоты — локально. Пользователь может сделать резервную копию сохранений, но не снапшотов.
Чтобы создать снапшот, следует вызвать функцию \verb/make_snapshot()./ Необязательным параметром является номер слота.
Следует помнить, что снапшот будет создан не моментально, а после завершения текущего такта игры. Иначе сохранённое состояние может оказаться противоречивым.
Чтобы загрузить сохранённый снапшот, нужно вызвать функцию \verb/restore_snapshot()./ Опять же, ей можно передать номер загружаемого слота.
Чтобы удалить сохранённый снапшот, вызывается функция \verb/delete_snapshot()./ Снапшоты занимают место на диске, и их чистка всегда полезна.
\begin{verbatim}
house = room {
nam = 'У здания',
entered = code [[ make_snapshot() ]],
dsc = 'Вы стоите перед зданием.',
}
\end{verbatim}
\subsection{Timer}
Модуль \verb/timer/ -- это удобная прослойка для управления таймером.
Метод \verb/timer:set(интервал)/ включает таймер с заданным интервалом в миллисекундах. Каждое заданное количество миллисекунд будет вызываться событие \verb/here().timer/ либо \verb/game.timer/, если в данной локации не определён этот атрибут. Чтобы отключить таймер, нужно вызвать метод \verb/timer:stop()./
Главным отличием использования модуля Timer от обычного использования объекта \verb/timer/ является то, что при использовании модуля обработчик работает в контексте stead, а не sdl-instead; то есть, вы защищены от ошибок с неправильным возвратом управления.
\begin{verbatim}
game.timer = function(s)
set_sound('gfx/beep.ogg');
p "Timer:"
p (time())
end
init()
timer:set(1000)
end
\end{verbatim}
\subsection{Theme}
Модуль \verb/theme/ позволяет менять тему в процессе игры, без редактирования файла \verb/main.lua./
Он объявляет следующие функции:
\begin{description}
\item[theme.get('имя параметра')] — чтение текущих параметров текущей темы
\item[win.geom(x, y, w, h)] — смена координат и размеров главного окна
\item[win.color(fg, link, alink)] — смена цветов текста, ссылок и активных ссылок главного окна
\item[win.font(name, size, height)] — смена параметров шрифта главного окна
\item[win.gfx.up(pic, x, y)] — смена параметров верхнего скроллера главного окна
\item[win.gfx.down(pic, x, y)] — смена параметров нижнего скроллера главного окна
\item[inv.geom(x, y, w, h)] — смена координат и размеров инвентаря
Довольно много людей жаловались, что в INSTEAD очень неудобно вставлять описание объектов в описание комнат. Например, автор хочет сделать что-то подобное:
\begin{quote}
Вы входите в большой зал. На стенах висят \href{}{картины}. С потолка свешивается огромная \href{}{люстра}. Здесь очень красиво, хотя странный сильный \href{}{запах} немного портит впечатление.
\end{quote}
Чтобы написать нечто подобное в стандартном окружении INSTEAD, необходимо объявить три объекта: картины, люстра и запах. Описание локации (атрибут dsc) будет пустым, а то, что будет выводиться игроку, будет составляться из описаний предметов. Так, первые две фразы будут храниться в атрибуте dsc объекта <<картины>>, третья фраза будет принадлежать объекту <<люстра>>, а объект <<запах>> будет описывать всё остальное. При этом объекты должны добавляться в сцену в строго определённом порядке.
Чтобы описание сцены действительно хранилось в описании сцены, был написан модуль \verb/xact./ Он также позволяет делать ссылки на объекты из других объектов и реакций.
Обычная ссылка INSTEAD выглядит так:
\begin{verbatim}
dsc = "Вы входите в большой зал. На стенах висят {картины}."
\end{verbatim}
xact - cсылка выглядит так:
\begin{verbatim}
dsc = "Вы входите в большой зал. На стенах висят {pictures|картины}."
\end{verbatim}
Здесь объект pictures отвечает за картины. Ссылка на него может стоять где угодно. Ссылаться можно на сам объект или его\verb/nam./
Общий вид ссылок:
\begin{verbatim}
{объект(параметры)|текст}
\end{verbatim}
До версии 1.2.2 символом разделителя было двоеточие (\verb/:/). После версии 1.2.2 символ разделителя задаётся полем \verb/stead.delim/; по умолчанию он задан вертикальной линией (\verb/|/).
Также модуль \verb/xact/ содержит несколько упрощённых реализаций объектов:
Объект \verb/xact/ описывает простейшую реакцию. Это объект — декорация, он может только выполнять одну функцию, когда к нему обращаются. Объект задаётся так:
\begin{verbatim}
hello = xact('nam',code[[do_something()]])
\end{verbatim}
Первый параметр функции \verb/xact/ – имя объекта, второй – реакция. Реакция может быть строкой, функцией или \verb/code/.
Комната \verb/xroom/ отличается от обычной тем, что имеет атрибут \verb/xdsc/. Если его задать, то он будет выведен после описания сцены, вместо описаний объектов.
Функцию \verb/xdsc('имя атрибута')/ можно вызывать несколько раз, чтобы вывести на экран дополнительные описания.
\begin{verbatim}
main = room {
dsc = [[Я в комнате.]];
xdsc = [[ Я вижу {apple|яблоко} и {knife|нож}. ]];
other = [[ Еще здесь лежат {chain|цепь} и {tool|пила}.]];
Вот и закончен справочник по INSTEAD. Напомню, что INSTEAD расшифровывается (и переводится) как <<Интерпретатор простых текстовых приключений>>. Официальная документация находится в каталоге doc и поставляется с instead. Дополнительную информацию вы можете получить в Интернете:
Кроме того, полезно будет посмотреть Subversion-репозиторий INSTEAD, где хранится исходный код самого движка и код нескольких полезных трюков, которые не пакуются в релиз для Windows\footnote{Так как релиз для Linux выходит в исходных кодах и заведомо меньше по размеру, то дополнительные исходные коды из него не вырезаются.}. Вы также можете найти несколько полезных руководств (в том числе и это) в подкаталоге doc/ программы.