Добавлен справочник INSTEAD. Удачи всем разработчикам игр!

PDF версия справочника будет загружена в раздел Downloads главной страницы проекта, я думаю.
This commit is contained in:
Alexander Yakovlev 2009-11-07 07:07:19 +00:00
parent 308a81e69c
commit bf85e572a2
2 changed files with 917 additions and 1 deletions

View file

@ -1,9 +1,12 @@
include ../Rules.make
clean:
rm -f ./dev_manual/manual.toc ./dev_manual/manual.out ./dev_manual/manual.log ./dev_manual/manual.aux
all:
pdflatex -output-directory ./dev_manual dev_manual/manual.tex
pdflatex -output-directory ./dev_manual dev_manual/manual.tex
install:
$(INSTALLD) $(DOCPATH)
$(INSTALL) *.jpg *.html *.txt $(DOCPATH)
$(INSTALL) *.jpg *.html *.txt dev_manual/*.pdf $(DOCPATH)
$(INSTALLD) $(MANPATH)
$(INSTALL) *.6 $(MANPATH)

913
doc/dev_manual/manual.tex Executable file
View file

@ -0,0 +1,913 @@
\documentclass[a4paper,12pt]{article}
\usepackage[utf8]{inputenc}
\usepackage[russian]{babel}
\usepackage[a4paper]{geometry}
\usepackage[pdftex,colorlinks,linkcolor = blue,urlcolor = blue,unicode]{hyperref}
\usepackage{xcolor}
\geometry{verbose,tmargin=1cm,bmargin=1cm,lmargin=1cm,rmargin=1cm,headheight=1cm,headsep=1cm,footskip=0.7cm}
\begin{document}
\title{Справочное пособие по INSTEAD}
\author{Александр Яковлев\\oreolek@jabber.ru \and \textit{при участии Петра Косых}\\\textit{gl00my@jabber.ru}}
\maketitle
\tableofcontents
\clearpage
\section*{Введение}
Я предполагаю, что вы, читатель, интересуетесь процессом создания игр для движка INSTEAD. Также я думаю, что вы - человек умный и вам не надо повторять дважды.
Игры для движка STEAD пишутся на языке \href{http://www.lua.org}{Lua} версии 5.1 (то есть, последней на данный момент). Знание этого языка будет очень полезным при написании игр, но я постараюсь сделать описание настолько подробным, что даже новичок в Lua смог программировать без проблем. Между прочим, знающим Lua будет небезынтересно посмотреть код движка.
Главное окно игры содержит информацию о статической и динамической части сцены, активные события и картинку сцены с возможными переходами в другие сцены (в графическом интерпретаторе).
Динамическая часть сцены составлена из описаний объектов сцены, она отображается всегда. Она может выглядеть так: ``Стоит стол. Рядом стоит стул.''. Если динамическая часть пуста, то игроку не с чем контактировать в сцене.
Статическая часть сцены описывает саму сцену, её ``декорации''. Она отображается при показе сцены (единожды или каждый раз -- решает автор игры), или при повторении команды look (в графическом интерпретаторе при щелчке на названии сцены).
Игрок имеет собственный инвентарь. В нём лежат объекты, доступные на любой сцене. Чаще всего инвентарь рассматривают как некую ``котомку'', в которой лежат объекты; в этом случае каждый объект считают предметом. Такая трактовка практична, обыденна и интуитивна; но не единственна. Понятие инвентаря является условным, ведь это лишь контейнер. В нём могут находиться такие объекты, как ``открыть'', ``потрогать'', ``лизнуть''. Можно наполнить его объектами ``нога'', ``рука'', ``мозг''. Автор игры свободен в определении этих понятий, но он также должен определить действия игрока над ними.
Действиями игрока могут быть:
\begin{itemize}
\item {осмотр сцены}
\item {действие на объект сцены}
\item {действие на объект инвентаря}
\item {действие объектом инвентаря на объект сцены}
\item {действие объектом инвентаря на объект инвентаря}
\end{itemize}
Осмотр сцены - это чаще всего неявное действие. Игрок входит в комнату, он автоматически осматривает её.
Действие на объект сцены обычно понимается как изучение объекта, или использование его. Например, если в сцене существует объект ``чашка кофе'', то действием на него может быть выпивание кофе, тщательный осмотр чашки, разбивание чашки или перемещение чашки в инвентарь. Это определяется только автором и никем другим.
Действие на объект инвентаря понимается аналогично.Например, если в инвентаре лежит объект ``яблоко'', его можно съесть или осмотреть. С другой стороны, если в инвентаре лежит объект ``осмотреть'', то действие над ним будет трудно описать логически.
Действие объектом инвентаря на объект сцены -- это чаще всего использование или передача объекта. Например, действие объектом ``нож'' на объект ``бармен'' может означать передачу ножа бармену, угрозу ножом бармену, убийство ножом бармена и многое другое.
Действие объектом инвентаря на объект инвентаря понимается так же свободно. Это может быть соединение предметов (``сарделька'' + ``кетчуп'') в одно (``сарделька с кетчупом''), либо использование (``открыть'' + ``ящик'').
Эти примеры подробно показывают первую из идей STEAD - гибкость. Автор свободен в своей фантазии и может трактовать все понятия движка как хочет.
Игра представляет из себя каталог, в котором должен находиться скрипт main.lua. Другие ресурсы игры (скрипты на lua, графика и музыка) должны находиться в рамках этого каталога. Все ссылки на ресурсы делаются относительно текущего каталога -- каталога игры.
Игра начинается именно с main.lua. В начале файла main.lua может быть определен заголовок, состоящий из тегов. Теги должны начинаться с символов комментария ``--''. На данный момент существует один тег: \verb/$Name:/, который должен содержать название игры. Пример использования тега:
\begin{verbatim}
-- $Name: Самая интересная игра!$
\end{verbatim}
Интерпретатор ищет доступные игры в каталогах:\\
Unix версия интерпретатора просматривает игры в каталогах:\\
\verb;/usr/local/share/instead/games; (по умолчанию),\\
\verb,~/.instead/games,.\\
WinXP версия:\\
\verb;Documents and Settings/USER/Local Settings/Application Data/instead/games;\\
WinVista: \verb;Users\USER\AppData\Local\instead\games;\\
Все Windows: \verb;куда-вы-установили-INSTEAD/games;
В дальнейшем я буду отталкиваться от возможностей графической ветки интерпретатора -- \\ INSTEAD-SDL.
\section{Сцена}
Сцена -- это единица игры, в рамках которой игрок может изучать все объекты сцены и взаимодействовать с ними. В игре должна быть хотя бы одна сцена с именем main.
\begin{verbatim}
main = room {
nam = 'главная комната',
dsc = 'Вы в большой комнате.',
};
\end{verbatim}
Отмечу, что пример выше является минимальной игрой для INSTEAD. Это некий ``Hello, World'', который я рекомендую сохранить под именем main.lua и поместить в отдельную папку в каталоге для игр.
Атрибут \verb/nam/ (имя) является необходимым для любого объекта. Для сцены это -- то, что будет заголовком сцены при ее отображении. Имя сцены также используется для ее идентификации при переходах.
Атрибут \verb/dsc/ -- это описание статической части сцены, которое выводится при входе в сцену или выполнении команды look.
\textbf{\textcolor{red}{Внимание!!!}} Если для вашего творческого замысла необходимо, чтобы описание статической части сцены выводилось на каждом ходу (а не только при первом входе в сцену), вы можете определить для своей игры параметр forcedsc (в начале игры).
\begin{verbatim}
game.forcedsc = true;
\end{verbatim}
Или, аналогично, задать атрибут forcedsc для конкретных сцен.
Для длинных описаний удобно использовать запись вида:
\begin{verbatim}
dsc = [[ Очень длинное описание... ]],
\end{verbatim}
При этом переводы строк игнорируются. Если вы хотите, чтобы в выводе описания сцены присутствовали абзацы -- используйте символ \verb/^/.
\begin{verbatim}
dsc = [[ Первый абзац. ^^
Второй Абзац.^^
Третий абзац.^
На новой строке.]],
\end{verbatim}
К текущей сцене можно обратиться через функцию \verb/here()/.
\section{Объект}
\subsection{Нормальные объекты}
Объекты -- это единицы сцены, с которыми взаимодействует игрок.
\begin{verbatim}
table = obj {
nam = 'стол',
dsc = 'В комнате стоит {стол}.',
act = 'Гм... Просто стол...',
};
\end{verbatim}
Имя объекта \verb/nam/ используется для адресации объекта.
\verb/dsc/ -- описатель объекта. Он будет выведен в динамической части сцены. Фигурными скобками отображается фрагмент текста, который будет являться ссылкой в графическом интерпретаторе. Если вы забудете сделать ссылку, то интерпретатор не выдаст ошибки, но игроки не смогут взаимодействовать с объектом.
\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}
Если атрибут или обработчик оформлен как функция, то обычно первый аргумент функции (s) сам объект. В данном примере, при показе сцены будет в динамической части сцены будет текст: ``На столе что-то лежит''. При взаимодействии с ``что-то'', переменная \verb/_seen/ объекта \verb/apple/ будет установлена в \verb/true/, и мы увидим, что это было яблоко.
Запись \verb/s._seen/ означает, что переменная \verb/_seen/ размещена в объекте \verb/s/ (то есть, \verb/apple/).В языке Lua переменные необязательно объявлять заранее, при первом обращении к ней переменная \verb/apple._seen/ появится сама; но хорошим тоном будет заранее \textbf{проинициализировать} переменную со значением \verb/false/.
Подчеркивание в имени переменной означает, что она \textbf{попадет} в файл сохранения игры. Сохраняются все переменные, название которых начинается с большой буквы или с подчёркивания. Вы можете переопределить функцию \verb/isForSave(k)/, если вас это не устраивает.
\textbf{\textcolor{red}{Внимание!!!}} Переменные в любом случае не записываются в файл сохранения, если они не размещены в одном из перечисленных типов объектов: комната, объект, диалог, игра, игрок.
\subsection{Облегченные объекты}
Иногда, сцену нужно наполнить декорациями, которые обладают ограниченной функциональностью, но делают игру разнообразней. Для этого можно использовать облегченный объект. Например:
\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}
Как видим, \verb/vobj/ позволяет сделать легкую версию статического объекта, с которым тем не менее можно взаимодействовать (за счет определения обработчика \verb/act/ в сцене и анализа ключа объекта). \verb/vobj/ также вызывает метод \verb/used/, при этом в качестве третьего параметра передается объект, воздействующий на виртуальный объект.
Синтаксис \verb/vobj/: \verb/vobj(ключ, имя, описатель);/ где ключ -- это цифра, которая будет передана обработчикам \verb.act/used. сцены как второй параметр.
Существует модификация объекта \verb/vobj/ -- \verb/vway/. \verb/vway/ реализует ссылку. Синтаксис и пример:
\begin{verbatim}
vway(имя, описание, сцена назначения);
obj = { vway("дальше", "Нажмите {здесь}.", 'nextroom') }
\end{verbatim}
Вы можете динамически заполнять сцену объектами \verb/vobj/ или \verb/vway/ с помощью методов \verb/add/ и \verb/del/.
В довершение, определена также упрощенная сцена \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{Динамическое создание объектов}
Вы можете использовать аллокаторы \verb/new/ и \verb/delete/ для создания и удаление динамических объектов:
\begin{verbatim}
new("obj { nam = 'a' ..... }")
put(new [[obj {nam = 'test' } ]]);
put(new('myconstructor()');
\end{verbatim}
Созданный объект будет попадать в файл сохранения. \verb/new()/ возвращает реальный объект;чтобы получить его имя,если это нужно, используйте функцию\verb/deref/.
\section{Некоторые манипуляции с объектами}
\subsection{Объект и сцена}
Ссылкой на объект называется текстовая строка, содержащая имя объекта при его создании. Например: \verb/'table'/ -- ссылка на объект \verb/table/.
Для того, чтобы поместить в сцену объекты, нужно определить массив \verb/obj/, состоящий из ссылок на объекты:
\begin{verbatim}
main = room {
nam = 'главная комната',
dsc = 'Вы в большой комнате.',
obj = { 'tabl' },
};
\end{verbatim}
\subsection{Объекты, связанные с объектами}
Объекты тоже могут содержать атрибут \verb/obj/. При этом, список будет последовательно разворачиваться. Например, поместим на стол яблоко:
\begin{verbatim}
apple = obj {
nam = 'яблоко',
dsc = 'На столе лежит {яблоко}.',
};
table = obj {
nam = 'стол',
dsc = 'В комнате стоит {стол}.',
obj = { 'apple' },
};
\end{verbatim}
При этом, в описании сцены мы увидим описание объектов ``стол'' и ``яблоко'', так как \verb/apple/ -- связанный с \verb/table/ объект.
\subsection{Действия объектов друг на друга}
Игрок может действовать объектом инвентаря на другие объекты. При этом вызывается обработчик \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/ в нужной комнате. В этом случае использование объектов сцены будет аналогично использованию объектов инвентаря.
\section{Смена сцен}
Как только главный герой уходит со сцены, декорации меняются. Но чтобы игрок ушёл из нашей сцены, он должен знать, куда идти.
Для перехода между сценами используется атрибут сцены -- список \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/ может быть функцией, и вы можете генерировать имена сцен на лету, например, если вы хотите, чтобы игрок не знал название сцены, пока не попал на нее.
При переходе между сценами движок вызывает обработчик \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}
Как видим, обработчики могут возвращать два значения: строку и статус. В нашем примере функция \verb/exit/ вернет \verb/false/, если игрок попытается уйти из зала в \verb/main/. \verb/false/ блокирует переход. Такая же логика работает и для \verb/enter/. Кроме того, она работает и для обработчика \verb/tak/ (о нём чуть позже).
\textbf{\textcolor{red}{Важное замечание:}} обработчик enter вызывается не при входе в комнату, а чуть раньше. Когда игрок хочет уйти в комнату, движок вызывает её обработчик enter, чтобы убедиться в том,что тот не возвращает false и переход возможен. Поэтому если вы используете \verb/here()/ в \verb/enter/, она будет указывать прежде всего -- на предыдущую комнату. Если вы передаёте enter параметр сцены, то он будет указывать всегда на текущую сцену.
Если требуется перейти на другую сцену автоматически, можно использовать функцию \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{Инвентарь}
Инвентарь проще всего возвращается функцией inv(). Он представлен списком, поэтому для него справедливы все их трюки (см. соответствующий раздел).
Простейший вариант сделать объект, который можно брать -- определить у него обработчик tak.
Если предмет сцены имеет обработчик \verb/tak/ и НЕ имеет обработчика \verb/act/ \footnote{Пользуясь случаем: я считаю,что tak - немного неудобное имя. Именно так. - А.Я.}, то при действии на нём вызывается не \verb/act/, а \verb/tak/; после этого предмет перемещается в инвентарь. Это происходит вот так:
\begin{verbatim}
apple = obj {
nam = 'яблоко',
dsc = 'На столе лежит {яблоко}.',
tak = 'Вы взяли яблоко.',
};
\end{verbatim}
\subsection{Игрок}
Игрок в STEAD представлен объектом \verb/pl/. Тип объекта -- \verb/player/.
Атрибут \verb/obj/ представляет собой инвентарь игрока.
\subsection{Игра}
Игра представлена объектом \verb/game/. Он хранит в себе указатель на текущего игрока (\verb/'pl'/) и некоторые параметры. Например, вы можете указать в начале своей игры кодировку текста следующим образом:
\begin{verbatim}
game.codepage="UTF-8";
\end{verbatim}
Кроме того, объект \verb/game/ может содержать обработчики по умолчанию \verb/act/, \verb/inv/, \verb/use/, которые будут вызваны, если в результате действий пользователя не будут найдены никакие другие обработчики. Например, вы можете написать в начале игры:
\begin{verbatim}
game.act = 'Не получается.';
game.inv = 'Гм.. Странная штука..';
game.use = 'Не сработает...';
\end{verbatim}
На практике полезно что-то вроде:
\begin{verbatim}
game.inv = function()
local a = rnd(7);
local reaction = {
[1] = 'Либо я ошибся карманом, либо мне нужно что-то другое.',
[2] = 'Откуда у меня в кармане ЭТО?!',
[3] = 'Сам не понял,что достал. Положу обратно.',
[4] = 'Это что-то неправильное.',
[5] = 'В моих карманах что только не залёживается...',
[6] = 'Я не представляю, как я могу тащить ЭТО с собою.',
[7] = 'Мне показалось или оно на меня смотрит?',
};
return reaction[a];
end;
\end{verbatim}
\section{Диалоги}
Третьим важным типом в движке являются диалоги. Диалоги -- это особый подвид сцен, содержащий только фразы. Например, диалог может выглядеть следующим образом:
\begin{verbatim}
povardlg = dlg {
nam = 'на кухне',
dsc = 'Передо мной полное лицо повара...',
obj = {
[1] = phr('Мне вот-этих зелененьких... Ага -- и бобов!', 'На здоровье!'),
[2] = phr('Картошку с салом, пожалуйста!', 'Приятного аппетита!'),
[3] = phr('Две порции чесночного супа!!!', 'Прекрасный выбор!'),
[4] = phr('Мне что-нибудь легонькое, у меня язва...', 'Овсянка!'),
},
};
\end{verbatim}
\verb/phr/ -- создание фразы. Фраза содержит вопрос, ответ и реакцию (реакция в данном примере отсутствует). Когда игрок выбирает одну из фраз, фраза отключается. Когда все фразы отключатся диалог заканчивается. Реакция -- это строка кода на lua который выполнится после отключения фразы.
\verb/_phr/ -- создание выключенной фразы. Она не видна изначально, но её можно включить с помощью функции \verb/pon()/ (см. ниже).
Вот как создаётся фраза:
\begin{verbatim}
[1] = phr('Мне вот-этих зелененьких... Ага -- и бобов!', 'На здоровье!', [[pon(1);]]),
\end{verbatim}
В реакции может быть любой lua код (простейшей реакцией является возвращение строки), но в STEAD определены наиболее часто используемые функции:
\begin{description}
\item[pon(n...)] включить фразы диалога с номерами n... (в нашем примере -- чтобы игрок мог повторно взять еду того-же вида).
\item[poff(n...)] выключить фразы диалога с номерами n...
\item[prem(n...)] удалить (заблокировать) фразы диалога с номерами n... (удаление означает невозможность включения фраз. pon(n..) не приведет к включению фраз).
\end{description}
Как ответ,так и реакция могут быть функциями.
Переход в диалог осуществляется как переход на сцену:
\begin{verbatim}
return goto('povardlg');
\end{verbatim}
Вы можете переходить из одного диалога в другой диалог -- организовывая иерархические диалоги.
Также, вы можете прятать некоторые фразы при инициализации диалога и показывать их при некоторых условиях.
Вы можете включать/выключать фразы не только текущего, но и произвольного диалога, с помощью методов объекта диалог \verb,pon/poff,. Например:
\begin{verbatim}
shopman:pon(5);
\end{verbatim}
Если номер фразы не указан,то это означает,что действие относится к текущей фразе:
\begin{verbatim}
phr('a', 'b', [[ pon() ]]);
\end{verbatim}
\section{О списках}
Каждый атрибут-список имеет методы:
\begin{description}
\item[add] Добавление элемента в список.Необязательным вторым параметром является позиция в списке.
\item[del] Удаление элемента из списка.
\item[look] Получение индекса элемента в списке по идентификатору
\item[srch] Проверка на наличие объекта в списке по его nam. Возвращает 2 значения: идентификатор и позицию; если объекта нет, вернёт nil. Например: \begin{verbatim}objs():srch('Ножик')\end{verbatim}
\item[set] Изменение объекта по номеру. Например, этот пример присвоит заменит первый объект списка: \begin{verbatim}objs():set('knife',1);\end{verbatim}
\item[disable] Скрытие объекта в списке; отличается от удаления тем, что может быть возвращен к жизни через метод enable
\item[enable] Показ скрытого объекта
\item[zap] Обнуление списка
\item[cat(b)] Склеивает список со списком b
\item[cat(b,pos)] Добавляет в список список b на позицию pos
\item[disable\_all] Аналогично disable, но массово
\item[enable\_all] Смысл понятен.
\end{description}
Параметрами методов могут быть объекты, их идентификаторы и имена.
\section{Функции}
\begin{description}
\item[inv()] возвращает список инвентаря
\item[objs()] возвращает список объектов указанной сцены; если сцена не указана, то возвращает список объектов текущей сцены.
\item[ways()] возвращает список возможных переходов из указанной сцены; если сцена не указана, то возвращает список возможных переходов из текущей сцены.
\item[me()] возвращает объект pl (объект игрока)
\item[here()] возвращает текущую сцену
\item[where()] возвращает текущую сцену как строку-имя объекта, а не сам объект
\item[from()] возвращает объект прошлой сцены
\item[ref(nam)] возвращает ссылку на указанный объект: \begin{verbatim}ref('home') == home\end{verbatim}
\item[deref(object)] возвращает ссылку строкой для объекта: \begin{verbatim}deref(knife) == 'knife'\end{verbatim}
\item[have(object)] проверяет, есть ли объект в инвентаре по имени объекта или по его nam
\item[move(from, where)] переносит объект из текущей сцены в другую
\item[movef(from, where)] действует так же, но добавляет объект в начало списка
\item[seen(object,scene)] проверяет, есть ли объект в указанной сцене (в текущей, если сцена не указана)
\item[drop(object,scene)] выбрасывает объект из инвентаря в указанную сцену (в текущую, если сцена не указана)
\item[dropf(object,scene)] то же, что drop, но объект появляется в начале списка
\item[put(object,scene)] кладёт предмет в указанную сцену; в текущую, если сцена не указана
\item[remove(object,scene)] удаляет предмет из указанной сцены; из текущей, если сцена не указана
\item[take(object,scene)] перемещает объект из указанной(текущей) сцены в инвентарь
\item[taken(object)] проверяет, взят ли уже объект
\item[rnd(m)] возвращает случайное целое значение от 1 до m
\item[goto(destination)] переносит в сцену w; используется в конструкции вида \verb/return goto('inmycar');/
\item[change\_pl(player)] переключает на другого игрока (со своим инвентарём и позицией), также используется в \verb/return/. Может использоваться для переключения между разными инвентарями.
\item[back()] переносит в предыдущую сцену (аналогично goto)
\item[time()] возвращает текущее время игры в активных действиях.
\item[cat(...)] возвращает строку -- склейку строк-аргументов. Если первый аргумент nil, то функция возвращает nil.\footnote{Функция легко и просто заменяется обычным оператором склейки строк Lua: \texttt{строка1 .. строка2}. Тем не менее, этот оператор выдаст ошибку при склейке nil}
\item[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] Добавляет строку и пробел в буфер
\item[pn] Добавляет строку и перевод строки в буфер
\item[pstart] Начало заполнения буфера (в обработчиках не нужно)
\item[pend] Конец заполнения буфера, возврат строки (в обработчиках не нужно)
\item[pclr] Очистка буфера
\item[pget] Получение содержимое буфера на текущий момент
\end{description}
Другой пример:
\begin{verbatim}
life = function(s)
p 'Ветер дует мне в спину.'
return pend(), true
end
\end{verbatim}
\section{Добавление динамики в игру}
Игра измеряет время в своих единицах - в шагах, или активных действиях. Каждое действие игрока - это его шаг, пусть даже он тратит его на то,чтобы ещё раз осмотреться. Что он увидит нового? Что изменится в мире игры за это время?
Именно для того,чтобы задать динамику мира, существует система с говорящим названием 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}
В этом примере кот по имени Барсик, сидя в инвентаре у игрока, будет на каждом шагу показывать свою активность. Но приведённый код пока что не будет работать. Для того,чтобы объявить объект ``живым'', следует добавить его в соответствующий список с помощью функции \verb/lifeon()/.
\begin{verbatim}
inv():add('mycat');
lifeon('mycat');
\end{verbatim}
Любой объект или сцена могут иметь свой обработчик \verb/life/, который вызывается каждый раз при смене текущего времени игры, если объект или сцена были добавлены в список живых объектов с помощью \verb/lifeon/. Не забывайте удалять живые объекты из списка с помощью \verb/lifeoff/, когда они больше не нужны. Это можно сделать, например, в обработчике \verb/exit/, или любым другим способом.
Вы можете вернуть из обработчика life второй код возврата, важность. (true или false). Если он равен true, то возвращаемое значение будет выведено ДО описания объектов; по умолчанию значение равно false.
Если вы хотите ``очистить экран'', то можно воспользоваться вот каким хаком. Из метода \verb/life/ доступна переменная \verb/ACTION_TEXT/ -- это тот текст, который содержит реакцию на действие игрока. Соответственно, сделав \verb/ACTION_TEXT = nil/,можно ``запретить'' вывод реакции. Например,для перехода на конец игры можно сделать:
\begin{verbatim}
ACTION_TEXT = nil
return goto('theend'), true
\end{verbatim}
\section{Краски и звуки}
В чём очевидное преимущество графического интерпретатора над текстовой веткой -- это то, что он может говорить и показывать. Проще говоря, вы можете добавить в игру графику и музыку.
Графический интерпретатор анализирует атрибут сцены \verb/pic/, и воспринимает его как путь к картинке-иллюстрации для сцены. Если в текущей сцене не определен атрибут \verb/pic/, то берется \verb/game.pic/. Если не определен и он, то картинка не отображается.
Unix-версия движка поставляется в исходных кодах, поэтому поддержка форматов графики и музыки определяется на стадии его компиляции; если при сборке в системе присутствуют нужные библиотеки для SDL, то формат будет поддерживаться. Я рекомендую использовать форматы PNG, JPG и GIF. Новые версии движка поддерживают GIF-анимацию. Те же правила действуют для музыки. Здесь строгих рамок нет, поэтому старайтесь не использовать редких форматов. Проверена поддержка WAV, MP3, OGG, FLAC, MIDI, XM, MOD, IT, S3M.
Вы также можете встраивать графические изображения в текст или в инвентарь с помощью функции \verb/img/. Например:
\begin{verbatim}
knife = obj {
nam = 'Нож'..img('img/knife.png'),
}
\end{verbatim}
Теперь о звуке. Фоновая музыка задаётся с помощью функции:
\begin{verbatim}
set_music(имя музыкального файла, количество проигрываний)
\end{verbatim}
Она проигрывается циклически бесконечно,если количество проигрываний не задано.
\verb/get_music()/ возвращает текущее имя трека.
Функция \verb/get_music_loop/ возвращает, на каком витке повторения находится мелодия. 0 - означает вечный цикл. n -- количество проигрываний. -1 -- проигрывание текущего трека закончено.
Помимо фонового сопровождения, \verb/set_sound()/ позволяет проиграть звуковой файл. \verb/get_sound()/ возвращает имя звукового файла, который будет проигран.
\section{Трюки}
\subsection{Форматирование}
SDL-INSTEAD поддерживает простое форматирование текста с помощью функций:
\begin{description}
\item[txtc(текст)] -- разместить текст по центру
\item[txtr(текст)] -- разместить текст справа
\item[txtl(текст)] -- разместить слева
\item[txtb(текст)] -- полужирное начертание
\item[txtem(текст)] -- начертание курсивом
\item[txtu(текст)] -- подчеркнутый текст
\end{description}
\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}
Вы можете использовать конструктор \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/#rgb/, где r g и b компоненты цвета в шестнадцатеричном виде. Кроме того некоторые основные цвета распознаются по своим именам. Например: \verb/yellowgreen/, или \verb/violet/. Какие именно цвета распознаются - написано в коде движка.
Параметры могут принимать значения:
\begin{description}
\item[scr.w] ширина игрового пространства в пикселях
\item[scr.h] высота игрового пространства в пикселях
\item[scr.col.bg] цвет фона
\item[scr.gfx.bg] путь к картинке фонового изображения
\item[scr.gfx.cursor.x] абсцисса центра курсора
\item[scr.gfx.cursor.y] ордината центра курсора
\item[scr.gfx.cursor.normal] путь к картинке-курсору
\item[scr.gfx.cursor.use] путь к картинке-курсору режима использования
\item[scr.gfx.use] путь к картинке-индикатору режима использования
\item[scr.gfx.pad] размер отступов к скролл-барам и краям меню
\item[scr.gfx.x] абсцисса окна изображений
\item[scr.gfx.y] ордината окна изображений
\item[scr.gfx.w] ширина окна изображений
\item[scr.gfx.h] высота окна изображений
\item[win.gfx.h] синоним scr.gfx.h
\item[scr.gfx.mode] режим расположения
\item[win.x] абсцисса главного окна
\item[win.y] ордината главного окна
\item[win.w] ширина главного окна
\item[win.h] высота главного окна
\item[win.fnt.name] путь к файлу шрифта
\item[win.fnt.size] размер шрифта главного окна
\item[win.gfx.up] путь к файлу изображения скроллера вверх для главного окна
\item[win.gfx.down] путь к файлу изображения скроллера вниз для главного окна
\item[win.col.fg] цвет текста главного окна
\item[win.col.link] цвет ссылок главного окна
\item[win.col.alink] цвет активных ссылок главного окна
\item[inv.x] абсцисса области инвентаря
\item[inv.y] ордината области инвентаря
\item[inv.w] ширина области инвентаря
\item[inv.h] высота области инвентаря
\item[inv.mode] строка режима инвентаря
\item[inv.col.fg] цвет текста инвентаря
\item[inv.col.link] цвет ссылок инвентаря
\item[inv.col.alink] цвет активных ссылок инвентаря
\item[inv.fnt.name] путь к шрифту инвентаря
\item[inv.fnt.size] размер шрифта инвентаря
\item[inv.gfx.up] путь к изображению скроллера вверх для инвентаря
\item[inv.gfx.down] путь к изображению скроллера вниз для инвентаря
\item[menu.col.bg] фон меню
\item[menu.col.fg] цвет текста меню
\item[menu.col.link] цвет ссылок меню
\item[menu.col.alink] цвет активных ссылок меню
\item[menu.col.alpha] прозрачность меню (0-255)
\item[menu.col.border] цвет границы меню
\item[menu.bw] толщина границы меню
\item[menu.fnt.name] путь к шрифту меню
\item[menu.fnt.size] размер шрифта меню
\item[menu.gfx.button] путь к значку меню
\item[menu.button.x] абсцисса кнопки меню
\item[menu.button.y] ордината кнопки меню
\item[snd.click] путь к звуку щелчка
\item[include] имя темы (последний компонент в пути каталога)
\end{description}
Окно изображений -- область, в которой располагается картинка сцены. Интерпретация зависит от режима расположения.
Главное окно -- область, в которой располагается описание сцены.
Режим расположения может быть равен \verb/fixed/, \verb/embedded/ или \verb/float/. В режиме \verb/embedded/ картинка является частью содержимого главного окна, параметры \verb/win.x/, \verb/win.y/, \verb/win.w/ игнорируются.
\verb/float/ -- картинка расположена по указанным координатам (\verb/win.x/, \verb/win.y/) и масштабируется к размеру \verb/win.w/ x \verb/win.h/ если превышает его.
\verb/fixed/ -- картинка является частью сцены как в режиме \verb/embedded/, но не скроллируется вместе с текстом, а расположена непосредственно над ним.
Режим инвентаря может быть равен \verb/horizontal/ или \verb/vertical/. В горизонтальном режиме инвентаря в одной строке могут быть несколько предметов. В вертикальном режиме, в каждой строке инвентаря содержится только один предмет.
Кроме того, заголовок темы может включать в себя комментарии с тегами. На данный момент существует только один тег: \verb/$Name:/, содержащий строку с именем темы. Например:
\begin{verbatim}
; $Name:Новая тема$
; модификация темы book
include = book
scr.gfx.h = 500
\end{verbatim}
Интерпретатор выполняет поиск тем в каталоге \verb.themes..\\
Unix версия кроме этого каталога, просматривает также каталог \verb;~/.instead/themes/;\\
WinXP: \verb.Documents and Settings/USER/Local Settings/Application Data/instead/themes.\\
Игра может задавать собственную тему; для этого в каталоге с игрой должен лежать тот самый \verb/theme.ini/. Его формат никак при этом не меняется, просто он загружается в первую очередь вместе с игрой.
\section{Дополнительные источники документации}
Вот и закончен справочник по INSTEAD. Напомним, что INSTEAD расшифровывается (и переводится) как ``Интерпретатор простых текстовых приключений''. Дополнительную информацию вы можете получить в Интернете:
\begin{itemize}
\item \href{http://instead.googlecode.com/}{Сайт программы}
\item \href{http://instead.pinebrush.com/}{Форум программы}
\end{itemize}
\end{document}