From bf85e572a27acbc35955ef9a05e6c566eda89204 Mon Sep 17 00:00:00 2001 From: oreolek Date: Sat, 7 Nov 2009 07:07:19 +0000 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=20=D1=81=D0=BF=D1=80=D0=B0=D0=B2=D0=BE=D1=87=D0=BD=D0=B8?= =?UTF-8?q?=D0=BA=20INSTEAD.=20=D0=A3=D0=B4=D0=B0=D1=87=D0=B8=20=D0=B2?= =?UTF-8?q?=D1=81=D0=B5=D0=BC=20=D1=80=D0=B0=D0=B7=D1=80=D0=B0=D0=B1=D0=BE?= =?UTF-8?q?=D1=82=D1=87=D0=B8=D0=BA=D0=B0=D0=BC=20=D0=B8=D0=B3=D1=80!=20PD?= =?UTF-8?q?F=20=D0=B2=D0=B5=D1=80=D1=81=D0=B8=D1=8F=20=D1=81=D0=BF=D1=80?= =?UTF-8?q?=D0=B0=D0=B2=D0=BE=D1=87=D0=BD=D0=B8=D0=BA=D0=B0=20=D0=B1=D1=83?= =?UTF-8?q?=D0=B4=D0=B5=D1=82=20=D0=B7=D0=B0=D0=B3=D1=80=D1=83=D0=B6=D0=B5?= =?UTF-8?q?=D0=BD=D0=B0=20=D0=B2=20=D1=80=D0=B0=D0=B7=D0=B4=D0=B5=D0=BB=20?= =?UTF-8?q?Downloads=20=D0=B3=D0=BB=D0=B0=D0=B2=D0=BD=D0=BE=D0=B9=20=D1=81?= =?UTF-8?q?=D1=82=D1=80=D0=B0=D0=BD=D0=B8=D1=86=D1=8B=20=D0=BF=D1=80=D0=BE?= =?UTF-8?q?=D0=B5=D0=BA=D1=82=D0=B0,=20=D1=8F=20=D0=B4=D1=83=D0=BC=D0=B0?= =?UTF-8?q?=D1=8E.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- doc/Makefile | 5 +- doc/dev_manual/manual.tex | 913 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 917 insertions(+), 1 deletion(-) create mode 100755 doc/dev_manual/manual.tex diff --git a/doc/Makefile b/doc/Makefile index 8da90ec..28281f6 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -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) diff --git a/doc/dev_manual/manual.tex b/doc/dev_manual/manual.tex new file mode 100755 index 0000000..ba9dc08 --- /dev/null +++ b/doc/dev_manual/manual.tex @@ -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}