steed/doc/manual.tex
2010-06-28 12:57:11 +00:00

1273 lines
88 KiB
TeX
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

\documentclass[a4paper,12pt]{article}
\usepackage[utf8]{inputenc}
\usepackage[russian]{babel}
\usepackage[a4paper]{geometry}
\usepackage[pdftex,colorlinks,linkcolor = blue,urlcolor = blue,unicode]{hyperref}
\usepackage{xcolor}
\usepackage{graphicx}
\usepackage{wrapfig}%обтекание текстом
\geometry{verbose,tmargin=1cm,bmargin=1cm,lmargin=1cm,rmargin=1cm,headheight=1cm,headsep=1cm,footskip=0.7cm}
\usepackage{indentfirst}
\usepackage{makeidx}
\makeindex
\begin{document}
\title{Справочное пособие по INSTEAD (Относится к версиям 1.1.xx, Для версий 1.2.xx используйте официальную документацию)}
\author{Александр Яковлев\\oreolek@jabber.ru \and \textit{при участии Петра Косых}\\\textit{gl00my@jabber.ru}}
\maketitle
\tableofcontents
\clearpage
\section{Введение}
Данный справочник написан в предположении, что читатель знаком с основами объектно - ориентированного программирования.
Игры для движка STEAD пишутся на языке \href{http://www.lua.org}{Lua} версии 5.1 (то есть, последней на данный момент). Знание этого языка будет очень полезным при написании игр, но я постараюсь сделать описание настолько подробным, что даже новичок в этом языке смог создавать игры для INSTEAD без проблем. Между прочим, знающим Lua будет небезынтересно посмотреть код движка.
\begin{wrapfigure}{l}{0.55\linewidth}
\includegraphics[scale=0.7]{1.jpg}
\caption{INSTEAD с запущенной игрой}
\label{INSTEAD-running}
\end{wrapfigure}
\index{Главное окно}
\textbf{Главное окно} игры содержит информацию о статической и динамической части сцены, активные события и картинку сцены с возможными переходами в другие сцены (в графическом интерпретаторе).
\index{Сцена!динамическая часть}
\textbf{Динамическая часть} сцены составлена из описаний объектов сцены, она отображается всегда. Она может выглядеть так: <<Стоит стол. Рядом стоит стул>>. Если динамическая часть пуста, то игроку не с чем контактировать в сцене.
\index{Сцена!статическая часть}
\textbf{Статическая часть} сцены описывает саму сцену, её <<декорации>>. Она отображается при показе сцены (единожды или каждый раз --- решает автор игры), или при повторении команды look (в графическом интерпретаторе при щелчке на названии сцены).
\index{Инвентарь}
Игрок имеет собственный \textbf{инвентарь}. В нём лежат объекты, доступные на любой сцене. Чаще всего инвентарь рассматривают как некую <<котомку>>, в которой лежат объекты; в этом случае каждый объект считают предметом. Такая трактовка практична, обыденна и интуитивна; но не единственна. Понятие инвентаря является условным, ведь это лишь контейнер. В нём могут находиться такие объекты, как <<открыть>>, <<потрогать>>, <<лизнуть>>. Можно наполнить его объектами <<нога>>, <<рука>>, <<мозг>>. Автор игры свободен в определении этих понятий, но он также должен определить действия игрока над ними.
На рисунке \ref{INSTEAD-running} очень чётко видны границы между этими областями. Главное окно имеет бежевый фон\footnote{Поправьте меня, если я неправ; в описании главного окна нет инвентаря}, инвентарь --- чёрный. Динамическая часть идёт сразу после ссылок перехода и выделена курсивом; статическая часть отпечатана обычным шрифтом.
Действиями игрока могут быть:
\begin{itemize}
\item {осмотр сцены}
\item {действие на объект сцены}
\item {действие на объект инвентаря}
\item {действие объектом инвентаря на объект сцены}
\item {действие объектом инвентаря на объект инвентаря}
\end{itemize}
Осмотр сцены --- это чаще всего неявное действие. Игрок входит в комнату, он автоматически осматривает её.
\index{Действие!на объект сцены}
Действие на объект сцены обычно понимается как изучение объекта, или использование его. Например, если в сцене существует объект <<чашка кофе>>, то действием на него может быть выпивание кофе, тщательный осмотр чашки, разбивание чашки или перемещение чашки в инвентарь. Это определяется только автором и никем другим.
\index{Действие!на объект инвентаря}
Действие на объект инвентаря понимается аналогично. Например, если в инвентаре лежит объект <<яблоко>>, его можно съесть или осмотреть. С другой стороны, если в инвентаре лежит объект <<осмотреть>>, то действие над ним будет трудно описать логически.
\index{Действие!объектом на объект}
Действие объектом инвентаря на объект сцены --- это чаще всего использование или передача объекта. Например, действие объектом <<нож>> на объект <<бармен>> может означать передачу ножа бармену, угрозу ножом бармену, убийство ножом бармена и многое другое.
Действие объектом инвентаря на объект инвентаря понимается так же свободно. Это может быть соединение предметов (<<сарделька>> + <<кетчуп>>) в одно (<<сарделька с кетчупом>>), либо использование (<<открыть>> + <<ящик>>).
Эти примеры подробно показывают первую из идей STEAD --- гибкость. Автор свободен в своей фантазии и может трактовать все понятия движка как хочет.
Игра представляет из себя каталог, в котором должен находиться скрипт main.lua. Другие ресурсы игры (скрипты на lua, графика и музыка) должны находиться в рамках этого каталога. Все ссылки на ресурсы делаются относительно текущего каталога --- каталога игры.
Игра начинается именно с main.lua. В начале файла main.lua может быть определён заголовок, состоящий из тегов. Теги должны начинаться с символов комментария \verb/--/. На данный момент существует один тег: \verb/$Name:,/ который должен содержать название игры. Пример использования тега:
\begin{verbatim}
-- $Name: Самая интересная игра!$
\end{verbatim}
Интерпретатор ищет доступные игры в следующих каталогах:
\index{Каталоги!игр}
Unix версия интерпретатора просматривает \verb;/usr/local/share/instead/games; (по умолчанию), а также \verb,~/.instead/games,.
Windows сборка использует каталог \verb/куда-вы-установили-INSTEAD\games/; каталоги пользовательских игр немного меняются в зависимости от вашей версии Windows. В Windows XP это \texttt{Documents and Settings/USER/Local Settings/Application Data/instead/games}, когда как в Windows Vista это \verb;Users\USER\AppData\Local\instead\games;.
На данный момент активно развивается только графическая ветка интерпретатора --- INSTEAD-SDL. Поэтому справочник в б\'{о}льшей мере описывает её возможности.
\section{Сцена}\index{Сцена}
Сцена --- это единица игры, в рамках которой игрок может изучать все объекты сцены и взаимодействовать с ними. В игре должна быть хотя бы одна сцена с именем main.
\begin{verbatim}
main = room {
nam = 'главная комната',
dsc = 'Вы в большой комнате.',
};
\end{verbatim}
Отмечу, что пример выше является минимальной игрой для INSTEAD. Это некий <<Hello, World>>, который я рекомендую сохранить под именем main.lua и поместить в отдельную папку в каталоге для игр.
\index{Атрибуты!nam}
Атрибут \verb/nam/ (имя) является необходимым для любого объекта. Для сцены это --- то, что будет заголовком сцены при её отображении. Имя сцены также используется для её идентификации при переходах.
\index{Атрибуты!dsc}
Атрибут \verb/dsc/ --- это описание статической части сцены, которое выводится при входе в сцену или выполнении команды look.
\index{Атрибуты!forcedsc}
\textbf{\textcolor{red}{Внимание!!!}} Если для вашего творческого замысла необходимо, чтобы описание статической части сцены выводилось на каждом ходу (а не только при первом входе в сцену), вы можете определить для своей игры параметр forcedsc (в начале игры).
\begin{verbatim}
game.forcedsc = true;
\end{verbatim}
Или, аналогично, задать атрибут forcedsc для конкретных сцен.
\index{[[ ]]}
Для длинных описаний удобно использовать запись вида:
\begin{verbatim}
dsc = [[ Очень длинное описание... ]],
\end{verbatim}
При этом переводы строк игнорируются. Если вы хотите, чтобы в выводе описания сцены присутствовали абзацы --- используйте символ \verb/^/.
\begin{verbatim}
dsc = [[ Первый абзац. ^^
Второй Абзац.^^
Третий абзац.^
На новой строке.]],
\end{verbatim}
К текущей сцене можно обратиться через функцию \verb/here()/.
\index{Функции!here}
\section{Объект}
\index{Объекты!нормальные}
\subsection{Нормальные объекты}
Объекты --- это единицы сцены, с которыми взаимодействует игрок.
\begin{verbatim}
table = obj {
nam = 'стол',
dsc = 'В комнате стоит {стол}.',
act = 'Гм... Просто стол...',
};
\end{verbatim}
Имя объекта \verb/nam/ используется для адресации объекта.
\index{Атрибуты!dsc}
\verb/dsc/ --- описатель объекта. Он будет выведен в динамической части сцены. Фигурными скобками отображается фрагмент текста, который будет являться ссылкой в графическом интерпретаторе. Если вы забудете сделать ссылку, то интерпретатор не выдаст ошибки, но игроки не смогут взаимодействовать с объектом.
\index{Атрибуты!act}
\verb/act/ --- это обработчик, который вызывается при действии пользователя на объект сцены. Если объект находится в инвентаре, то действие с ним будет передаваться другому обработчику --- \verb/inv/.
Настало время сделать небольшое отступление. До сих пор в примерах приводились примитивные обработчики, которые всего лишь возвращают определённую строку. В примере выше обращение к объекту вызовет банальную реакцию: интерпретатор напечатает строку <<Гм... Просто стол...>>. Хуже того: он будет отвечать тем же образом каждый раз при обращении к объекту. Это не совсем гибкий подход, поэтому STEAD позволяет определить любой атрибут объекта как функцию. Так, возможно построить такую конструкцию:
\begin{verbatim}
apple = obj {
nam = 'яблоко',
dsc = function(s)
if not s._seen then
return 'На столе {что-то} лежит.';
else
return 'На столе лежит {яблоко}.';
end
end,
act = function(s)
if s._seen then
return 'Это яблоко!';
else
s._seen = true;
return 'Я присматриваюсь и понимаю, что это --- яблоко.!';
end
end,
};
\end{verbatim}
Иногда может понадобиться обработчик, который совершал бы некоторое действие, но не выводил никакого описания. Например:
\begin{verbatim}
button = obj {
nam = "кнопка",
dsc = "На стене комнаты видна большая красная {кнопка}.",
act = function (s)
here().dsc = [[После того как я нажал на кнопку, комната преобразилась. Книжный шкаф куда-то исчез вместе со столом и комодом, а на его месте появился странного вида аппарат.]];
return true;
end,
}
\end{verbatim}
В данном случае обработчик \verb/act/ нужен для того, чтобы поменять описание комнаты, и не нужно, чтобы чтобы он выводил результат действия. Для отключения результата можно вернуть из обработчика значение \verb/true/ -- это будет означать, что действие успешно выполнено, но не требует дополнительного описания.
Если необходимо показать, что действие невыполнимо, можно вернуть из обработчика \verb`act` значение \verb/false/ или \verb/nil/. При этом будет отображено описание по умолчанию из \verb`game.act`.
\index{Функции}
Если атрибут или обработчик оформлен как функция, то обычно первый аргумент функции \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)/, если вас это не устраивает.
\textbf{\textcolor{red}{Внимание!!!}} Переменные в любом случае не записываются в файл сохранения, если они не размещены в одном из перечисленных типов объектов: комната, объект, диалог, игра, игрок.
\subsection{Облегчённые объекты}
\index{Объекты!облегчённые}
Иногда, сцену нужно наполнить декорациями, которые обладают ограниченной функциональностью, но делают игру разнообразней. Для этого можно использовать облегчённый объект. Например:
\begin{verbatim}
sside = room {
nam = 'южная сторона',
dsc = [[Я нахожусь у южной стены здания института. ]],
act = function(s, w)
if w == 1 then
ways():add('stolcorridor');
return "Я подошёл к подъезду. Хм --- зайти внутрь?";
end
end,
obj = {vobj(1, "подъезд", "У восточного угла находится небольшой {подъезд}.")},
};
\end{verbatim}
\index{Объекты!облегчённые!vobj}
Как видим, \verb/vobj/ позволяет сделать лёгкую версию статического объекта, с которым тем не менее можно взаимодействовать (за счёт определения обработчика \verb/act/ в сцене и анализа ключа объекта). \verb/vobj/ также вызывает метод \verb/used/, при этом в качестве третьего параметра передаётся объект, воздействующий на виртуальный объект.
Синтаксис \verb/vobj/ таков: \verb/vobj(ключ, имя, описатель);,/ где ключ --- это цифра, которая будет передана обработчикам \verb.act/used. сцены как второй параметр.
\index{Объекты!облегчённые!vway}
Существует модификация объекта \verb/vobj/ под именем \verb/vway/. \verb/vway/ реализует ссылку. Синтаксис и пример:
\begin{verbatim}
vway(имя, описание, сцена назначения);
obj = { vway("дальше", "Нажмите {здесь}.", 'nextroom') }
\end{verbatim}
Вы можете динамически заполнять сцену объектами \verb/vobj/ или \verb/vway/ с помощью методов \verb/add/ и \verb/del/.
\index{Объекты!облегчённые!vroom}
В довершение, определена также упрощённая сцена \verb/vroom/. Синтаксис:
\begin{verbatim}
vroom(имя перехода, сцена назначения)
\end{verbatim}
Ниже приводится несколько примеров и трюков с подобными объектами:
\begin{verbatim}
home.objs:add(vway("next", "{Дальше}.", 'next_room');
home.objs:del("next");
home.objs:add(vroom("идти на запад", 'mountains');
if not home.obj:srch('Дорога') then
home.obj:add(vway('Дорога', 'Я заметил {дорогу}, ведущую в лес...', 'forest'));
end
obj = {vway('Дорога', 'Я заметил {дорогу}, ведущую в лес...', 'forest'):disable()},
objs()[1]:disable();
objs()[1]:enable();
\end{verbatim}
\subsection{Динамическое создание объектов}
\index{Функции!new}
\index{Функции!delete}
Вы можете использовать аллокаторы \verb/new/ и \verb/delete/ для создания и удаление динамических объектов:
\begin{verbatim}
new("obj { nam = 'a' ..... }")
put(new [[obj {nam = 'test' } ]]);
put(new('myconstructor()');
\end{verbatim}
Созданный объект будет попадать в файл сохранения. \verb/new()/ возвращает реальный объект; чтобы получить его имя, если это нужно, используйте функцию \index{Функции!deref} \verb/deref()./
\section{Некоторые манипуляции с объектами}
\subsection{Объект и сцена}
\index{Ссылки!на объект}
Ссылкой на объект называется текстовая строка, содержащая имя объекта при его создании. Например: \verb/'table'/ --- ссылка на объект \verb/table/.
\index{Атрибуты!obj}
Для того, чтобы поместить в сцену объекты, нужно определить массив \verb/obj/, состоящий из ссылок на объекты:
\begin{verbatim}
main = room {
nam = 'главная комната',
dsc = 'Вы в большой комнате.',
obj = { 'tabl' },
};
\end{verbatim}
\subsection{Объекты, связанные с объектами}
\index{Объекты!связанные}
\index{Атрибуты!obj}
Объекты тоже могут содержать атрибут \verb/obj./ При этом, список будет последовательно разворачиваться. Например, поместим на стол яблоко:
\begin{verbatim}
apple = obj {
nam = 'яблоко',
dsc = 'На столе лежит {яблоко}.',
};
table = obj {
nam = 'стол',
dsc = 'В комнате стоит {стол}.',
obj = { 'apple' },
};
\end{verbatim}
При этом, в описании сцены мы увидим описание объектов <<стол>> и <<яблоко>>, так как \verb/apple/ --- связанный с \verb/table/ объект.
\subsection{Действия объектов друг на друга}
\index{Действие!объектом на объект}
Игрок может действовать объектом инвентаря на другие объекты. При этом вызывается обработчик \verb/use/ у объекта которым действуют и \verb/used/ --- на которого действуют.
Например:
\begin{verbatim}
knife = obj {
nam = 'нож',
dsc = 'На столе лежит {нож}',
inv = 'Острый!',
tak = 'Я взял нож!',
use = 'Вы пытаетесь использовать нож.',
};
tabl = obj {
nam = 'стол',
dsc = 'В комнате стоит {стол}.',
act = 'Гм... Просто стол...',
obj = { 'apple', 'knife' },
used = 'Вы пытаетесь сделать что-то со столом...',
};
\end{verbatim}
Если игрок возьмёт нож и использует его на стол --- то увидит текст обработчиков \verb/knife.use/ и \verb/tabl.used/.
\verb/use/ и \verb/used/ могут быть функциями. Тогда первый параметр это сам объект, а второй --- ссылка на объект, на который направлено действие в случае \verb/use/ и объект, которым действие осуществляется в случае \verb/used/.
\verb/use/ может вернуть статус \verb/false/, в этом случае обработчик \verb/used/ не вызывается (если он вообще был). Статус обработчика \verb/used/ --- игнорируется. Это будет выглядеть как:
\begin{verbatim}
return 'Строка реакции', false;
\end{verbatim}
Возможно также действовать объектами сцены на объекты сцены; для этого нужно установить переменную \verb/game.scene_use = true/ или поставить \verb/scene.use=true/ в нужной комнате. В этом случае использование объектов сцены будет аналогично использованию объектов инвентаря.
\subsection{Скрытие объектов}
\index{Объекты!скрытые}
\index{Методы!enable}
\index{Методы!disable}
При помощи методов \verb/enable/ и \verb/disable/ становится возможным управлять появлением и исчезновением объектов.
Скрытый объект --- это объект, который находится в сцене, но на данный момент словно бы <<выключен>>. Он присутствует для движка, но не существует для игрока. Его описание не выводится и с ним невозможно контактировать. Это можно использовать, например, вместо динамического создания объектов.
Чтобы создать заведомо выключенный объект, необходимо воспользоваться конструкцией вида \verb/knife = {<...>}:disable()./
Объект \verb/knife/ будет создан и тут же выключен.
Методы \verb/enable()/ и \verb/disable()/ возвращают сам объект. Они присутствуют у любого объекта и списка объектов.
\section{Смена сцен}
\index{Сцена!смена сцен}
Как только главный герой уходит со сцены, декорации меняются. Но чтобы игрок ушёл из нашей сцены, он должен знать, куда идти.
\index{Атрибуты!way}
Для перехода между сценами используется атрибут сцены --- список \verb/way/.
\begin{verbatim}
room2 = room {
nam = 'зал',
dsc = 'Вы в огромном зале.',
way = { 'main' },
};
main = room {
nam = 'главная комната',
dsc = 'Вы в большой комнате.',
obj = { 'tabl' },
way = { 'room2' },
};
\end{verbatim}
При этом, вы сможете переходить между сценами \verb/main/ и \verb/room2/. Как вы помните, \verb/nam/ может быть функцией, и вы можете генерировать имена сцен на лету, например, если вы хотите, чтобы игрок не знал название сцены, пока не попал на неё.
\index{Атрибуты!exit}
\index{Атрибуты!enter}
При переходе между сценами движок вызывает обработчик \verb/exit/ из текущей сцены и \verb/enter/ той сцены, куда идёт игрок. \verb/exit/ и \verb/enter/ могут быть функциями --- тогда первый параметр это сам объект, а второй --- ссылка на комнату куда игрок хочет идти (для \verb/exit/) или из которой уходит (для \verb/enter/). Например:
\begin{verbatim}
room2 = room {
enter = function(s, f)
if f == 'main' then
return 'Вы пришли из комнаты.';
end
end,
nam = 'зал',
dsc = 'Вы в огромном зале.',
way = { 'main' },
exit = function(s, t)
if t == 'main' then
return 'Я не хочу назад!', false
end
end,
};
\end{verbatim}
\index{Атрибуты!tak}
Как видим, обработчики могут возвращать два значения: строку и статус. В нашем примере функция \verb/exit/ вернёт \verb/false/, если игрок попытается уйти из зала в \verb/main/. \verb/false/ блокирует переход. Такая же логика работает и для \verb/enter/. Кроме того, она работает и для обработчика \verb/tak/ (о нём чуть позже).
\textbf{\textcolor{red}{Важное замечание:}} обработчик enter вызывается не при входе в комнату, а чуть раньше. Когда игрок хочет уйти в комнату, движок вызывает её обработчик enter, чтобы убедиться в том, что тот не возвращает false и переход возможен. Поэтому если вы используете \verb/here()/ в \verb/enter/, она будет указывать прежде всего --- на предыдущую комнату. Если вы передаёте enter параметр сцены, то он будет указывать всегда на текущую сцену.
\index{Функции!goto}
Если требуется перейти на другую сцену автоматически, можно использовать функцию \verb/goto/ со ссылкой на сцену как параметром:
\begin{verbatim}
return goto('main');
\end{verbatim}
Почему здесь написано \verb/return/? Дело в том, что функция \verb/goto()/ не является безусловным переходом, как можно подумать. Она возвращает описание новой сцены, поэтому почти всегда должна завершать работу обработчика таким нехитрым способом.
\textbf{\textcolor{red}{НО: }}Если вы выполните goto из обработчика exit, то получите переполнение стека, так как goto снова и снова будет вызывать метод exit. Вы можете избавиться от этого, если вставите проверку, разрушающую рекурсию. Например:
\begin{verbatim}
exit = function(s, t)
if t == 'dialog' then return; end
return goto('dialog');
\end{verbatim}
Обычно движок сам пытается разорвать рекурсию. Тем не менее, автор должен понимать, что он делает и не надеяться на автоматику.
\section{Специальные типы объектов}
\subsection{Инвентарь}
\index{Инвентарь}
\index{Функции!inv}
Инвентарь проще всего возвращается функцией inv(). Он представлен списком, поэтому для него справедливы все их трюки (см. соответствующий раздел).
\index{Функции!tak}
Простейший вариант сделать объект, который можно брать --- определить у него обработчик tak.
Если предмет сцены имеет обработчик \verb/tak/ и НЕ имеет обработчика \verb/act/ \footnote{Пользуясь случаем: я считаю, что tak --- немного неудобное имя. Именно так. --- А.Я.}, то при действии на нём вызывается не \verb/act/, а \verb/tak/; после этого предмет перемещается в инвентарь. Это происходит вот так:
\begin{verbatim}
apple = obj {
nam = 'яблоко',
dsc = 'На столе лежит {яблоко}.',
tak = 'Вы взяли яблоко.',
};
\end{verbatim}
\subsection{Игрок}
\index{Объекты!pl}
Игрок в STEAD представлен объектом \verb/pl/. Тип объекта --- \verb/player/.
\index{Атрибуты!obj}
Атрибут \verb/obj/ представляет собой инвентарь игрока.
Объекты игрока можно воспринимать как объекты инвентаря, в этом случае верны следующие конструкции:
\begin{verbatim}
remove('knife', me());
put('knife', me());
take('knife', me());
where('knife');
\end{verbatim}
Необходимо отметить, что это верно не в каждой игре.
\subsection{Игра}
\index{Объекты!game}
Игра представлена объектом \verb/game/. Он хранит в себе указатель на текущего игрока (\verb/'pl'/) и некоторые параметры. Например, вы можете указать в начале своей игры кодировку текста следующим образом:
\begin{verbatim}
game.codepage="UTF-8";
\end{verbatim}
\index{Атрибуты!act}
\index{Атрибуты!inv}
\index{Атрибуты!use}
Кроме того, объект \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] = 'Либо я ошибся карманом, либо мне нужно что-то другое.',
[2] = 'Откуда у меня в кармане ЭТО?!',
[3] = 'Сам не понял, что достал. Положу обратно.',
[4] = 'Это что-то неправильное.',
[5] = 'В моих карманах что только не залёживается...',
[6] = 'Я не представляю, как я могу тащить ЭТО с собою.',
[7] = 'Мне показалось или оно на меня смотрит?',
};
return reaction[rnd(#reaction)];
end;
\end{verbatim}
\subsection{Таймер}
\index{Объекты!timer}
\label{objects_timer}
Таймер --- это объект \verb/timer/, который служит для отсчёта \textbf{реального} времени (в миллисекундах). В этом его существенное отличие от атрибутов \verb/life/, которые служат для измерения игрового времени (в шагах).
Для управления таймером используются функции:
\begin{description}
\item[timer:set(ms)] задать интервал таймера в миллисекундах
\item[timer:stop()] отключить таймер
\item[timer.callback(s)] функция-обработчик таймера, которая вызывается через заданный интервал времени
\end{description}
Двоеточия при вызове \verb/set/ и \verb/stop/ важны, их не стоит заменять на точки. По умолчанию таймер выключён, и не имеет заданного обработчика. Если таймер включён, то обработчик вызывается с заданным интервалом.
Чтобы включить таймер после выполнения \verb/stop/, достаточно переинициализировать его командой \verb/set/ с прежним интервалом.
Пример использования таймера:
\begin{verbatim}
timer.callback = function(s)
main.time = main.time + 1;
return "look";
end
timer:set(100);
main = room {
time = 1,
force_dsc = true,
nam = 'Таймер',
dsc = function(s)
return 'Демонстрация: '..tostring(s._time);
end
};
\end{verbatim}
\subsection{Ввод с клавиатуры}
\index{Объекты!input}
\label{objects_input}
Ввод с клавиатуры анализируется при помощи объекта \verb/input/. Этот объект принимает все нажатия клавиш. По умолчанию он не имеет запрограммированных обработчиков, поэтому ничего не выполняет.
Создание нового обработчика для клавиши выполняется командой \verb/input.key(s, pressed, key),/ где \verb/pressed/ -- нажатие или отжатие, \verb/key/ -- символьное имя клавиши;
Если обработчик возвращает не \verb/nil/, то клавиша обрабатывается \textbf{им}, а не интерпретатором. Таким образом, можно переопределить обработку любой клавиши.
Например:
\begin{verbatim}
input.key = function(s, pr, key)
if not pr or key == "escape" then return
elseif key == 'space' then key = ' '
elseif key == 'return' then key = '^';
end
if key:len() > 1 then return end
main.txt = main.txt:gsub('_$','');
main.txt = main.txt..key..'_';
return "look";
end
main = room {
txt = '_',
force_dsc = true,
nam = 'Клавиатура',
dsc = function(s)
return 'Демонстрация: '..tostring(s._txt);
end
};
\end{verbatim}
\section{Диалоги}
\index{Диалоги}
Третьим важным типом в движке являются диалоги. Диалоги --- это особый подвид сцен, содержащий только фразы. Например, диалог может выглядеть следующим образом:
\begin{verbatim}
povardlg = dlg {
nam = 'на кухне',
dsc = 'Передо мной полное лицо повара...',
obj = {
[1] = phr('Мне вот-этих зелёненьких... Ага --- и бобов!', 'На здоровье!'),
[2] = phr('Картошку с салом, пожалуйста!', 'Приятного аппетита!'),
[3] = phr('Две порции чесночного супа!!!', 'Прекрасный выбор!'),
[4] = phr('Мне что-нибудь лёгонькое, у меня язва...', 'Овсянка!'),
},
};
\end{verbatim}
\index{Функции!phr}
\verb/phr/ --- создание фразы. Фраза содержит вопрос, ответ и реакцию (реакция в данном примере отсутствует). Когда игрок выбирает одну из фраз, фраза отключается. Когда все фразы отключатся диалог заканчивается. Реакция --- это строка кода на lua который выполнится после отключения фразы.
\index{Функции!\_phr}
\verb/_phr/ --- создание выключенной фразы. Она не видна изначально, но её можно включить с помощью функции \verb/pon()/ (см. ниже). По смыслу это эквивалентно \verb/phr(<...>):disable()./
Вот как создаётся фраза:
\begin{verbatim}
[1] = phr('Мне вот-этих зелёненьких... Ага --- и бобов!', 'На здоровье!', [[pon(1);]]),
\end{verbatim}
В реакции может быть любой lua код (простейшей реакцией является возвращение строки), но в STEAD определены наиболее часто используемые функции:
\index{Функции!pon}
\index{Функции!poff}
\index{Функции!prem}
\begin{description}
\item[pon(n...)] включить фразы диалога с номерами n... (в нашем примере --- чтобы игрок мог повторно взять еду того-же вида).
\item[poff(n...)] выключить фразы диалога с номерами n...
\item[prem(n...)] удалить (заблокировать) фразы диалога с номерами n... (удаление означает невозможность включения фраз. pon(n..) не приведёт к включению фраз).
\end{description}
Как ответ, так и реакция могут быть функциями. Вы можете закончить диалог, выполнив в реакции функцию \verb/back()./
Переход в диалог осуществляется как переход на сцену:
\begin{verbatim}
return goto('povardlg');
\end{verbatim}
Вы можете переходить из одного диалога в другой диалог --- организовывая иерархические диалоги.
Также, вы можете прятать некоторые фразы при инициализации диалога и показывать их при некоторых условиях.
\index{Методы!pon}
\index{Методы!poff}
Вы можете включать/выключать фразы не только текущего, но и произвольного диалога, с помощью методов объекта диалог \verb,pon/poff,. Например:
\begin{verbatim}
shopman:pon(5);
\end{verbatim}
Если номер фразы не указан, то это означает, что действие относится к текущей фразе:
\begin{verbatim}
phr('a', 'b', [[ pon() ]]);
\end{verbatim}
\section{О списках}
Каждый атрибут-список имеет методы:
\begin{description}
\item[add] \index{Методы!add} Добавление элемента в список. Необязательным вторым параметром является позиция в списке.
\item[del] \index{Методы!del} Удаление элемента из списка.
\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[cat(b,pos)] Добавляет в список список b на позицию pos
\item[disable\_all] \index{Методы!disable\_all} Аналогично disable, но массово
\item[enable\_all] \index{Методы!enable\_all} Аналогично enable, но массово
\end{description}
Параметрами методов могут быть объекты, их идентификаторы и имена.
\section{Функции}
Замечание. Если аргумент у функции единственен и текстовый, то скобки вокруг аргумента можно опускать. Большинство описываемых в этом разделе функций с несколькими параметрами могут
использоваться без указания всех параметров. В этом случае опущенные параметры принимают значение по умолчанию (обычно --- текущая сцена).
.
\begin{description}
\item[inv()] \index{Функции!inv} возвращает список инвентаря
\item[objs()] \index{Функции!objs} возвращает список объектов указанной сцены; если сцена не указана, то возвращает список объектов текущей сцены.
\item[ways()] \index{Функции!ways} возвращает список возможных переходов из указанной сцены; если сцена не указана, то возвращает список возможных переходов из текущей сцены.
\item[me()] \index{Функции!me} возвращает объект pl (объект игрока)
\item[here()] \index{Функции!here} возвращает текущую сцену
\item[where()] \index{Функции!where} возвращает сцену в которой помещен объект (если он был добавлен с использованием функций put, drop, move)
\item[from()] \index{Функции!from} возвращает прошлую сцену
\item[ref(nam)] \index{Функции!ref} возвращает указанный объект: \begin{verbatim}ref('home') == home\end{verbatim} nam может быть функцией, строкой или текстовой ссылкой
\item[deref(object)] \index{Функции!deref} возвращает ссылку строкой для объекта: \begin{verbatim}deref(knife) == 'knife'\end{verbatim}
\item[have(object)] \index{Функции!have} проверяет, есть ли объект в инвентаре по имени объекта или по его nam
\item[move(from, where, from)] \index{Функции!move} переносит объект из текущей сцены в другую
\item[movef(from, where, from)] \index{Функции!movef} действует так же, но добавляет объект в начало списка
\item[seen(object,scene)] \index{Функции!seen} проверяет, есть ли объект в указанной сцене (в текущей, если сцена не указана)
\item[drop(object,scene)] \index{Функции!drop} выбрасывает объект из инвентаря в указанную сцену (в текущую, если сцена не указана)
\item[dropf(object,scene)] \index{Функции!dropf} то же, что drop, но объект появляется в начале списка
\item[put(object,scene)] \index{Функции!put} кладёт предмет в указанную сцену; в текущую, если сцена не указана
\item[remove(object,scene)] \index{Функции!remove} удаляет предмет из указанной сцены; из текущей, если сцена не указана
\item[take(object,scene)] \index{Функции!take} перемещает объект из указанной(текущей) сцены в инвентарь
\item[taken(object)] \index{Функции!taken} проверяет, взят ли уже объект
\item[rnd(m)] \index{Функции!rnd} возвращает случайное целое значение от 1 до m
\item[goto(destination)] \index{Функции!goto} переносит в сцену w; используется в конструкции вида \verb/return goto('inmycar');/
\item[change\_pl(player)] \index{Функции!change\_pl} переключает на другого игрока (со своим инвентарём и позицией), также используется в \verb/return/. Может использоваться для переключения между разными инвентарями.
\item[back()] \index{Функции!back} переносит в предыдущую сцену (аналогично goto)
\item[time()] \index{Функции!time} возвращает текущее время игры в активных действиях.
\item[cat(...)] \index{Функции!cat} возвращает строку --- склейку строк-аргументов. Если первый аргумент nil, то функция возвращает nil.\footnote{Функция легко и просто заменяется обычным оператором склейки строк Lua: \texttt{строка1 .. строка2}. Тем не менее, этот оператор выдаст ошибку при склейке nil}
\item[par(...)] \index{Функции!par} возвращает строку --- склейку строк-аргументов, разбитых строкой-первым параметром.
\end{description}
Следующие записи эквивалентны:
\begin{verbatim}
ref('home').obj:add('chair');
home.obj:add('chair');
objs('home'):add('chair');
put('chair', 'home');
\end{verbatim}
Если вы хотите перенести объект из произвольной сцены, вам придётся удалить его из старой сцены с помощью метода del. Для создания сложно перемещающихся объектов, вам придётся написать свой метод, который будет сохранять текущую позицию объекта в самом объекте и делать удаление объекта из старой сцены. Вы можете указать исходную позицию (комнату) объекта в качестве третьего параметра move.
\begin{verbatim}
move('mycat','inmycar', 'forest');
\end{verbatim}
Отдельно следует упомянуть функции альтернативного вывода текста. Их назначение можно понять по следующим формам записи:
\begin{verbatim}
act = function(s)
p "Я осмотрел его...';
p "Гммм...."
end
act = function(s)
return "Я осмотрел его... Гммм...."
end
\end{verbatim}
\begin{description}
\item[p] \index{Функции!p} Добавляет строку и пробел в буфер
\item[pn] \index{Функции!pn} Добавляет строку и перевод строки в буфер
\item[pclr] \index{Функции!pclr} Очистка буфера
\item[pget] \index{Функции!pget} Получение содержимое буфера на текущий момент
\end{description}
Другой пример:
\begin{verbatim}
life = function(s)
p 'Ветер дует мне в спину.'
return pget(), true
end
\end{verbatim}
\section{Добавление динамики в игру}
\index{Атрибуты!life}
Игра измеряет время в своих единицах --- в шагах, или активных действиях. Каждое действие игрока --- это его шаг, пусть даже он тратит его на то, чтобы ещё раз осмотреться. Что он увидит нового? Что изменится в мире игры за это время?
Именно для того, чтобы задать динамику мира, существует система с говорящим названием life.
Вы можете определять обработчики, которые выполняются каждый раз, когда время игры увеличивается на 1. Например:
\begin{verbatim}
mycat = obj {
nam = 'Барсик',
lf = {
[1] = 'Барсик шевелится у меня за пазухой.',
[2] = 'Барсик выглядывает из за пазухи.',
[3] = 'Барсик мурлычет у меня за пазухой.',
[4] = 'Барсик дрожит у меня за пазухой.',
[5] = 'Я чувствую тепло Барсика у себя за пазухой.',
[6] = 'Барсик высовывает голову из за пазухи и осматривает местность.',
},
life = function(s)
local r = rnd(6);
if r > 2 then
return;
end
r = rnd(6);
return s.lf[r];
end,
\end{verbatim}
\index{Функции!lifeon}
В этом примере кот по имени Барсик, сидя в инвентаре у игрока, будет на каждом шагу показывать свою активность. Но приведённый код пока что не будет работать. Для того, чтобы объявить объект <<живым>>, следует добавить его в соответствующий список с помощью функции \verb/lifeon()/.
\begin{verbatim}
inv():add('mycat');
lifeon('mycat');
\end{verbatim}
\index{Функции!lifeoff}
Любой объект или сцена могут иметь свой обработчик \verb/life/, который вызывается каждый раз при смене текущего времени игры, если объект или сцена были добавлены в список живых объектов с помощью \verb/lifeon/. Не забывайте удалять живые объекты из списка с помощью \verb/lifeoff/, когда они больше не нужны. Это можно сделать, например, в обработчике \verb/exit/, или любым другим способом.
Вы можете вернуть из обработчика life второй код возврата, важность. (true или false). Если он равен true, то возвращаемое значение будет выведено ДО описания объектов; по умолчанию значение равно false.
\index{Переменные!ACTION\_TEXT}
Если вы хотите <<очистить экран>>, то можно воспользоваться следующим хаком. Из метода \verb/life/ доступна переменная \verb/ACTION_TEXT/ --- это тот текст, который содержит реакцию на действие игрока. Соответственно, сделав \verb/ACTION_TEXT = nil/, можно <<запретить>> вывод реакции. Например, для перехода на конец игры можно сделать:
\begin{verbatim}
ACTION_TEXT = nil
return goto('theend'), true
\end{verbatim}
Также для добавления динамики в игру служат объекты timer и input, см. разделы \ref{objects_timer} и \ref{objects_input}.
\section{Краски и звуки}
В чём очевидное преимущество графического интерпретатора над текстовой веткой --- это то, что он может говорить и показывать. Проще говоря, вы можете добавить в игру графику и музыку.
\index{Атрибуты!pic}
Графический интерпретатор анализирует атрибут сцены \verb/pic/, и воспринимает его как путь к картинке-иллюстрации для сцены. Если в текущей сцене не определён атрибут \verb/pic/, то берётся \verb/game.pic/. Если не определён и он, то картинка не отображается.
Unix-версия движка поставляется в исходных кодах, поэтому поддержка форматов графики и музыки в основном определяется возможностями библиотеки SDL, установленной в вашей системе. В большинстве случаев обеспечивается поддержка всех основных форматов. Для графики я рекомендую использовать форматы PNG, JPG и GIF (также поддерживаются анимированные GIF). Новые версии движка поддерживают GIF-анимацию. Те же правила действуют для музыки. Здесь строгих рамок нет, поэтому старайтесь не использовать редких форматов. Проверена поддержка WAV, MP3, OGG, FLAC, XM, MOD, IT, S3M.
\index{Функции!img}
Вы также можете встраивать графические изображения в текст или в инвентарь с помощью функции \verb/img/. Например:
\begin{verbatim}
knife = obj {
nam = 'Нож'..img('img/knife.png'),
}
\end{verbatim}
При этом изображения можно комбинировать, накладывая друг на друга. Очень удобны в этом отношении PNG и GIF, потому что они позволяют задать прозрачность фона.
В случае анимированных GIF наложение происходит только с первым кадром.
Это выглядит следующим образом:
\begin{verbatim}
pic = "gfx/mycat.png;gfx/milk.png@100,100;gfx/fish.png@c20,20"
\end{verbatim}
Картинки складываются стопкой снизу вверх. Самая первая картинка является фоном. Она должна быть самой большой; её границы - это и границы всей картинки.
Точка с запятой разделяет изображения; после неё идёт сначала путь к картинке, затем символ \verb/@/ и координаты точки фона, куда будет помещён левый верхний угол накладываемой картинки.
Координаты считаются от левого верхнего угла фонового изображения. Если после символа \verb/@/ идёт буква \verb/c/, то в эту точку будет помещён не левый верхний угол, а \textbf{центр} накладываемой картинки.
Теперь о звуке. Фоновая музыка задаётся с помощью функции:\index{Функции!set\_music}
\begin{verbatim}
set_music(имя музыкального файла, количество проигрываний)
\end{verbatim}
Она проигрывается циклически бесконечно, если количество проигрываний не задано.
\index{Функции!get\_music} \verb/get_music()/ возвращает текущее имя трека.
\index{Функции!get\_music\_loop} Функция \verb/get_music_loop/ возвращает текущий счетчик проигрываний мелодии. 0 --- означает вечный цикл. n --- количество оставшихся проигрываний. -1 --- проигрывание текущего трека закончено.
\index{Функции!set\_sound}
\index{Функции!get\_sound}
Помимо фонового сопровождения, \verb/set_sound()/ позволяет проиграть звуковой файл. \verb/get_sound()/ возвращает имя звукового файла, который будет проигран.
\section{Трюки}
\subsection{Форматирование}
SDL-INSTEAD поддерживает простое форматирование текста с помощью функций:
\begin{description}
\item[txtc(текст)] \index{Функции!txtc} --- разместить текст по центру
\item[txtr(текст)] \index{Функции!txtr} --- разместить текст справа
\item[txtl(текст)] \index{Функции!txtl} --- разместить слева
\item[txtb(текст)] \index{Функции!txtb} --- полужирное начертание
\item[txtem(текст)] \index{Функции!txtem} --- начертание курсивом
\item[txtu(текст)] \index{Функции!txtu} --- подчёркнутый текст
\item[txtnb(текст)] \index{Функции!txtnb} --- делает все пробелы в тексте неразрывными.
\end{description}
Немного по последней функции. Код \verb/txtnb(' ')/ даст вам один неразрывный пробел. Неразрывный пробел - это пробел, который нельзя заменить переходом на новую строку. То есть, если вы напишете \verb/txtnb('a b')/, то это будет гарантировать вам, что \verb/a/ и \verb/b/ находятся на одной строке, а не на разных.
\subsection{Проверка правописания}
Проверка правописания готовой игры --- это большая головная боль. У вас есть примерно 100 Кб кода, в которых находятся около 80 Кб текста. Любая программа проверки орфографии будет сильно ругаться на синтаксис Lua и мешать. Один из способов проверки --- использовать редактор Emacs.
Для проверки нужно установить сам Emacs и поддержку Lua к нему (lua-mode); дальнейшие операции с редактором:
\begin{enumerate}
\item Открыть нужный файл
\item Если русские буквы выглядят кракозябрами --- выбираете в меню Options --- Set Font/FontSet... шрифт fixed
\item Tools --- Spell Checking --- Select Russian Dict
\item Tools --- Spell Checking --- Spell-Check Buffer
\item Пробел для того, чтобы пропустить слово; Латинская a, чтобы игнорировать слово вообще; i чтобы добавить слово в словарь пользователя; цифры, чтобы заменить слово на один предложенных вариантов.
\end{enumerate}
Если вы впервые видите этот редактор, я настоятельно НЕ рекомендую нажимать что-нибудь и щёлкать на что-нибудь непонятное. Будет только непонятнее.
\subsection{Меню}
Вы можете делать меню в области инвентаря, определяя объекты с типом menu. При этом, обработчик меню будет вызван после клика мыши. Если обработчик не возвращает текст, то состояние игры не изменяется. Например, реализация кармана:
\begin{verbatim}
pocket = menu {
State = false,
nam = function(s)
if s.State then
return txtu('карман');
end
return 'карман';
end,
gen = function(s)
if s.State then
s:enable_all();
else
s:disable_all();
end
end,
menu = function(s)
if s.State then
s.State = false;
else
s.State = true;
end
s:gen();
end,
};
knife = obj {
nam = 'нож',
inv = 'Это нож',
};
inv():add(pocket);
put(knife, pocket);
pocket:gen();
main = room {
nam = 'test',
};
\end{verbatim}
\subsection{Статус}
Ниже представлена реализация статуса игрока в виде текста, который появляется в инвентаре, но не может быть выбран.
\begin{verbatim}
pl.Life = 10;
pl.Power = 10;
status = obj {
nam = 'Жизнь: '..pl.Life..',Сила: '..pl.Power,
};
inv():add('status');
status.object_type = false
\end{verbatim}
\index{Статус}
Вы можете использовать конструктор \verb/stat/ для создания статуса:
\begin{verbatim}
status = stat {
nam = function(s)
return 'Статус!!!';
end
};
inv():add('status');
\end{verbatim}
\subsection{Кодирование исходного кода}
Если вы не хотите показывать исходный код своих игр, вы можете закодировать его с помощью команды
\begin{verbatim}
sdl-instead -encode <путь к файлу> [выходной путь]
\end{verbatim}
и использовать его с помощью lua функции \verb/doencfile(путь к файлу)/.
\textbf{\textcolor{red}{Важное замечание:}} Лучше не использовать компиляцию игр с помощью luac, так как luac создаёт платформозависимый код. Таким образом, вам придётся выдавать сразу две скомпилированных версии: для 32-битных и 64-битных машин\footnote{А в будущем, возможно, SDL-INSTEAD будет поддерживать больше платформ, поэтому использовать предложенный метод будет ещё выгоднее}. Однако, компиляция игр может быть использована для поиска ошибок в коде.
\subsection{Создание собственного плейлиста}
Вы можете написать для игры свой проигрыватель музыки, создав его на основе живого объекта, например:
\begin{verbatim}
tracks = {"mus/astro2.mod", "mus/aws_chas.xm", "mus/dmageofd.xm", "mus/doomsday.s3m"}
mplayer = obj {
nam = 'плеер',
life = function(s)
local n = get_music();
local v = get_music_loop();
if not n or not v then
set_music(tracks[2], 1);
elseif v == -1 then
local n = get_music();
while get_music() == n do
n = tracks[rnd(4)]
end
set_music(n, 1);
end
end,
};
lifeon('mplayer');
\end{verbatim}
\subsection{Отладка}
Для того, чтобы во время ошибки увидеть стек вызовов функций lua, вы можете запустить
\begin{verbatim}
sdl-instead -debug
\end{verbatim}
Вы можете отлаживать свою игру вообще без instead. Например, вы можете создать следующий файл game.lua:
\begin{verbatim}
dofile("/usr/share/games/stead/stead.lua"); --- путь к stead.lua
dofile("main.lua"); --- ваша игра
game:ini();
iface:shell();
\end{verbatim}
И запустите игру в lua: \verb/lua game.lua/. При этом игра будет работать в примитивном shell окружении. Полезные команды: ls, go, act, use.... Теоретически движок можно таким образом привязать даже к CGI окружению.
\section{Создание тем для SDL-INSTEAD}
Графический интерпретатор поддерживает механизм тем.
Тема --- это инструкция к оформлению игры. Она задаёт внешний вид и положения всех информационных блоков на экране.
Тема представляет из себя каталог, с файлом \verb/theme.ini/ внутри. Файл \verb/theme.ini/ здесь является ключевым.
Тема, которая является минимально необходимой --- это тема \verb/default/. Эта тема всегда загружается первой. Все остальные темы наследуются от неё и могут частично или полностью заменять её параметры. Выбор темы осуществляется пользователем через меню настроек, однако конкретная игра может содержать собственную тему и таким образом влиять на свой внешний вид. В этом случае в каталоге с игрой должен находиться свой файл \verb/theme.ini/. Тем не менее, пользователь свободен отключить данный механизм, при этом интерпретатор будет предупреждать о нарушении творческого замысла автора игры.
Синтаксис \verb/theme.ini/ очень прост.
\begin{verbatim}
<параметр> = <значение>
; комментарий
\end{verbatim}
Кроме того, тема может включать в себя другую тему с помощью выражения вида:
\verb/include = имя темы/.
Значения могут быть следующих типов: строка, цвет, число.
Цвет задаётся в форме \verb/#rgb/, где \verb/r/, \verb/g/ и \verb/b/ --- компоненты цвета в шестнадцатеричном виде. Кроме того некоторые основные цвета распознаются по своим именам:
\newcommand{\tabColor}[2]{
\texttt{#1} & \colorbox[HTML]{#2}{T.}
}
\newcommand{\tabColorq}[8]{
\tabColor{#1}{#2} & \tabColor{#3}{#4} & \tabColor{#5}{#6} & \tabColor{#7}{#8} \\
\hline
}
\newcommand{\tabColort}[6]{
\tabColor{#1}{#2} & \tabColor{#3}{#4} & \tabColor{#5}{#6} & & \\
\hline
}
\begin{table}[ht!]
\begin{tabular}{|l|c|l|c|l|c|l|c|}
\hline
параметр & & параметр & & параметр & & параметр & \\
\hline
\tabColorq{aliceblue}{F0F8FF}{forestgreen}{228B22}{mediumvioletred}{C71585}{violet}{EE82EE}
\tabColorq{antiquewhite}{FAEBD7}{fuchsia}{FF00FF}{midnightblue}{191970}{violetred}{D02090}
\tabColorq{aqua}{00FFFF}{gainsboro}{DCDCDC}{mintcream}{F5FFFA}{wheat}{F5DEB3}
\tabColorq{aquamarine}{7FFFD4}{ghostwhite}{F8F8FF}{mistyrose}{FFE4E1}{white}{FFFFFF}
\tabColorq{azure}{F0FFFF}{gold}{FFD700}{moccasin}{FFE4B5}{whitesmoke}{F5F5F5}
\tabColorq{beige}{F5F5DC}{goldenrod}{DAA520}{navajowhite}{FFDEAD}{yellow}{FFFF00}
\tabColorq{bisque}{FFE4C4}{gray}{808080}{navy}{000080}{yellowgreen}{9ACD32}
\tabColort{black}{000000}{green}{008000}{oldlace}{FDF5E6}
\tabColort{blanchedalmond}{FFEBCD}{greenyellow}{ADFF2F}{olive}{808000}
\tabColort{blue}{0000FF}{honeydew}{F0FFF0}{olivedrab}{6B8E23}
\tabColort{blueviolet}{8A2BE2}{hotpink}{FF69B4}{orange}{FFA500}
\tabColort{brown}{A52A2A}{indianred}{CD5C5C}{orangered}{FF4500}
\tabColort{burlywood}{DEB887}{indigo}{4B0082}{orchid}{DA70D6}
\tabColort{cadetblue}{5F9EA0}{ivory}{FFFFF0}{palegoldenrod}{EEE8AA}
\tabColort{chartreuse}{7FFF00}{lavender}{E6E6FA}{palegreen}{98FB98}
\tabColort{chocolate}{D2691E}{lavenderblush}{FFF0F5}{paleturquoise}{AFEEEE}
\tabColort{coral}{FF7F50}{lawngreen}{7CFC00}{palevioletred}{D87093}
\tabColort{cornflowerblue}{6495ED}{lemonchiffon}{FFFACD}{papayawhip}{FFEFD5}
\tabColort{cornsilk}{FFF8DC}{lightblue}{ADD8E6}{peachpuff}{FFDAB9}
\tabColort{crimson}{DC143C}{lightcoral}{F08080}{peru}{CD853F}
\tabColort{cyan}{00FFFF}{lightcyan}{E0FFFF}{pink}{FFC0CB}
\tabColort{darkblue}{00008B}{lightgoldenrodyellow}{FAFAD2}{plum}{DDA0DD}
\tabColort{darkcyan}{008B8B}{lightgrey}{D3D3D3}{powderblue}{B0E0E6}
\tabColort{darkgoldenrod}{B8860B}{lightgreen}{90EE90}{purple}{800080}
\tabColort{darkgray}{A9A9A9}{lightpink}{FFB6C1}{red}{FF0000}
\tabColort{darkgreen}{006400}{lightsalmon}{FFA07A}{rosybrown}{BC8F8F}
\tabColort{darkkhaki}{BDB76B}{lightseagreen}{20B2AA}{royalblue}{4169E1}
\tabColort{darkmagenta}{8B008B}{lightskyblue}{87CEFA}{saddlebrown}{8B4513}
\tabColort{darkolivegreen}{556B2F}{lightslateblue}{8470FF}{salmon}{FA8072}
\tabColort{darkorange}{FF8C00}{lightslategray}{778899}{sandybrown}{F4A460}
\tabColort{darkorchid}{9932CC}{lightsteelblue}{B0C4DE}{seagreen}{2E8B57}
\tabColort{darkred}{8B0000}{lightyellow}{FFFFE0}{seashell}{FFF5EE}
\tabColort{darksalmon}{E9967A}{lime}{00FF00}{sienna}{A0522D}
\tabColort{darkseagreen}{8FBC8F}{limegreen}{32CD32}{silver}{C0C0C0}
\tabColort{darkslateblue}{483D8B}{linen}{FAF0E6}{skyblue}{87CEEB}
\tabColort{darkslategray}{2F4F4F}{magenta}{FF00FF}{slateblue}{6A5ACD}
\tabColort{darkturquoise}{00CED1}{maroon}{800000}{slategray}{708090}
\tabColort{darkviolet}{9400D3}{mediumaquamarine}{66CDAA}{snow}{FFFAFA}
\tabColort{deeppink}{FF1493}{mediumblue}{0000CD}{springgreen}{00FF7F}
\tabColort{deepskyblue}{00BFFF}{mediumorchid}{BA55D3}{steelblue}{4682B4}
\tabColort{dimgray}{696969}{mediumpurple}{9370D8}{tan}{D2B48C}
\tabColort{dodgerblue}{1E90FF}{mediumseagreen}{3CB371}{teal}{008080}
\tabColort{feldspar}{D19275}{mediumslateblue}{7B68EE}{thistle}{D8BFD8}
\tabColort{firebrick}{B22222}{mediumspringgreen}{00FA9A}{tomato}{FF6347}
\tabColort{floralwhite}{FFFAF0}{mediumturquoise}{48D1CC}{turquoise}{40E0D0}
\end{tabular}
\caption{Цветовые константы INSTEAD}
\end{table}
\subsection{Параметры окна сцены}
\index{Окно сцены} Окно сцены --- область, в которой располагается сцена. Интерпретация зависит от режима расположения. (см. таблицу~\ref{param_screen})
\newcommand{\tabParam}[3]{
\texttt{#1} & #2 & #3 \\
\hline
}
\begin{table}[ht!]
\begin{tabular}{|l|c|l|}
\hline
параметр & тип & описание \\
\hline
\tabParam{scr.w}{число}{ширина игрового пространства, пиксели}
\tabParam{scr.h}{число}{высота игрового пространства, пиксели}
\tabParam{scr.col.bg}{цвет}{цвет фона}
\tabParam{scr.gfx.bg}{строка}{путь к файлу фонового изображения}
\tabParam{scr.gfx.cursor.x}{число}{абсцисса центра курсора, пиксели}
\tabParam{scr.gfx.cursor.y}{число}{ордината центра курсора, пиксели}
\tabParam{scr.gfx.cursor.normal}{строка}{путь к картинке-курсору}
\tabParam{scr.gfx.cursor.use}{строка}{путь к картинке-курсору режима использования}
\tabParam{scr.gfx.use}{строка}{путь к картинке-индикатору режима использования}
\tabParam{scr.gfx.pad}{число}{размер отступов к скролл-барам и краям меню, пиксели}
\tabParam{scr.gfx.x}{число}{абсцисса окна изображений, пиксели}
\tabParam{scr.gfx.y}{число}{ордината окна изображений, пиксели}
\tabParam{scr.gfx.w}{число}{ширина окна изображений, пиксели}
\tabParam{scr.gfx.h}{число}{высота окна изображений, пиксели}
\tabParam{win.gfx.h}{число}{синоним \texttt{scr.gfx.h}}
\tabParam{scr.gfx.mode}{строка}{режим расположения}
\end{tabular}
\caption{Параметры окна изображений}\label{param_screen}
\end{table}
\medskip
Параметр \verb/scr.gfx.mode/ может принимать одно из значений: \verb/fixed/, \verb/embedded/ или \verb/float/.
В режиме \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/, но не скроллируется вместе с текстом, а расположена непосредственно над ним.
\subsection{Параметры главного окна}
\index{Главное окно} Главное окно --- область, в которой располагается описание сцены. (табл.~\ref{param_window})
\medskip
\begin{table}[ht!]
\begin{tabular}{|l|c|l|}
\hline
параметр & тип & описание \\
\hline
\tabParam{win.x}{число}{абсцисса главного окна, пиксели}
\tabParam{win.y}{число}{ордината главного окна, пиксели}
\tabParam{win.w}{число}{ширина главного окна, пиксели}
\tabParam{win.h}{число}{высота главного окна, пиксели}
\tabParam{win.fnt.name}{строка}{путь к файлу шрифта}
\tabParam{win.fnt.size}{число}{размер шрифта главного окна, пункты}
\tabParam{win.gfx.up}{строка}{путь к файлу изображения скроллера вверх для главного окна}
\tabParam{win.gfx.down}{строка}{путь к файлу изображения скроллера вниз для главного окна}
\tabParam{win.gfx.h}{число}{синоним \texttt{scr.gfx.h}}%интересно, почему для scr.gfx.w нету?
\tabParam{win.col.fg}{цвет}{цвет текста главного окна}
\tabParam{win.col.link}{цвет}{цвет ссылок главного окна}
\tabParam{win.col.alink}{цвет}{цвет активных ссылок главного окна}
\end{tabular}
\caption{Параметры главного окна}\label{param_window}
\end{table}
\medskip
\subsection{Параметры области инвентаря}
\index{Инвентарь} См. таблицу \ref{param_inventory}.
\medskip
\begin{table}[ht!]
\begin{tabular}{|l|c|l|}
\hline
параметр & тип & описание \\
\hline
\tabParam{inv.x}{число}{абсцисса области инвентаря,пиксели}
\tabParam{inv.y}{число}{ордината области инвентаря,пиксели}
\tabParam{inv.w}{число}{ширина области инвентаря,пиксели}
\tabParam{inv.h}{число}{высота области инвентаря,пиксели}
\tabParam{inv.col.fg}{цвет}{цвет текста инвентаря}
\tabParam{inv.col.link}{цвет}{цвет ссылок инвентаря}
\tabParam{inv.col.alink}{цвет}{цвет активных ссылок инвентаря}
\tabParam{inv.fnt.name}{строка}{путь к шрифту инвентаря}
\tabParam{inv.fnt.size}{число}{размер шрифта инвентаря,пункты}
\tabParam{inv.gfx.up}{строка}{путь к изображению скроллера вверх для инвентаря}
\tabParam{inv.gfx.down}{строка}{путь к изображению скроллера вниз для инвентаря}
\tabParam{inv.mode}{строка}{режим инвентаря}
\end{tabular}
\caption{Параметры области инвентаря}\label{param_inventory}
\end{table}
\medskip
Параметр \verb/inv.mode/ может принимать значение \verb/horizontal/ или \verb/vertical/.
В горизонтальном режиме инвентаря в одной строке могут быть несколько предметов. В вертикальном режиме, в каждой строке инвентаря содержится только один предмет.
\subsection{Параметры главного меню}
Параметры главного меню INSTEAD-SDL перечислены в таблице \ref{param_menu}
\medskip
\begin{table}[ht!]
\begin{tabular}{|l|c|l|}
\hline
параметр & тип & описание \\
\hline
\tabParam{menu.col.bg}{цвет}{цвет фона меню}
\tabParam{menu.col.fg}{цвет}{цвет текста меню}
\tabParam{menu.col.link}{цвет}{цвет ссылок меню}
\tabParam{menu.col.alink}{цвет}{цвет активных ссылок меню}
\tabParam{menu.col.alpha}{цвет}{прозрачность меню (0---255)}
\tabParam{menu.col.border}{цвет}{цвет границы меню}
\tabParam{menu.bw}{число}{толщина границы меню, пиксели}
\tabParam{menu.fnt.name}{строка}{путь к шрифту меню}
\tabParam{menu.fnt.size}{число}{размер шрифта меню, пункты}
\tabParam{menu.gfx.button}{строка}{путь к значку меню}
\tabParam{menu.button.x}{число}{абсцисса кнопки меню, пиксели}
\tabParam{menu.button.y}{число}{ордината кнопки меню, пиксели}
\end{tabular}
\caption{Параметры главного меню}\label{param_menu}
\end{table}
\medskip
Напоминаю, что имя темы есть имя каталога с нею.
\subsection{Прочее}
Кроме того, заголовок темы может включать в себя комментарии с тегами. На данный момент существует только один тег: \verb/$Name:/, содержащий строку с именем темы. Например:
\begin{verbatim}
; $Name:Новая тема$
; модификация темы book
include = book
scr.gfx.h = 500
\end{verbatim}
Еще один параметр темы: \verb/snd.click = путь к звуку щелчка/.
Интерпретатор ищет доступные темы в следующих каталогах.
\index{Каталоги!тем}
Unix версия интерпретатора просматривает игры в:\\
\verb;/usr/local/share/instead/themes; (по умолчанию),\\
\verb,~/.instead/themes,.\\
WinXP версия:\\
\verb;Documents and Settings/USER/Local Settings/Application Data/instead/themes;\\
WinVista: \verb;Users\USER\AppData\Local\instead\themes;\\
Все Windows: \verb;куда-вы-установили-INSTEAD/themes;
Игра может задавать собственную тему; для этого в каталоге с игрой должен лежать тот самый \verb/theme.ini/. Его формат никак при этом не меняется, просто эта тема загружается сразу после default темы вместе с игрой.
\section{Дополнительные источники документации}
Вот и закончен справочник по INSTEAD. Напомню, что INSTEAD расшифровывается (и переводится) как <<Интерпретатор простых текстовых приключений>>. Официальная документация находится в каталоге doc и поставляется с instead. Дополнительную информацию вы можете получить в Интернете:
\begin{itemize}
\item \href{http://instead.googlecode.com/}{Сайт программы}
\item \href{http://instead.pinebrush.com/}{Форум программы}
\end{itemize}
Кроме того, полезно будет посмотреть Subversion-репозиторий INSTEAD, где хранится исходный код самого движка и код нескольких полезных трюков, которые не пакуются в релиз для Windows\footnote{Так как релиз для Linux выходит в исходных кодах и заведомо меньше по размеру, то дополнительные исходные коды из него не вырезаются.}. Вы также можете найти несколько полезных руководств (в том числе и это) в подкаталоге doc/ программы.
\printindex
\end{document}