Lotus IF v2.01 Utopia

IF Платформа.

 2002 Сергей Протасов

president@userline.ru

ДАННОЕ ОПИСАНИЕ НЕ ЯВЛЯЕТСЯ ОФИЦИАЛЬНЫМ

Подробнее о реализации

Рекомендации по разработке

Для подробного ознакомления с командами смотрите Указатель.

Платформа Lotus IF это универсальное средство для разработки игр жанра IF.

Новое в этой версии

-Команда Emposible позволяет менять текст, появляющийся в ответ на неправильно введенную фразу.

-Появился логический тип переменных, принимающий значения TRUE/FALSE (ДА/НЕТ)

-Добавлены команды CALL имя локации и RET , для вызова подпрограммы.

-Добавлены команды PUSHLOC и POPLOC, которые заносят и восстанавливают из контейнера адрес локации.

-Добавлены команды PUSHINDEX и POPINDEX, которые заносят и восстанавливают из контейнера адрес строки+1.

-Теперь вложенные условия обрабатываются правильно, так же исправлена ошибка в обработке многократно вложенных структур Talk …Redo и Do…Redo.

Каждый IF должен заканчиваться своим отдельным ENDIF, также и каждый TALK  своим REDO.

-Вместо ENDIF в конце условия можно ставить ;

-Обработку Talk можно заключать в ( ) вместо REDO.

-Ликвидированы кнопки за ненадобностью. Команды, для работы с ними (DO,BTN) сохранены и теперь отражают выбор в виде проиндексированного списка непосредственно в окне текстового вывода.

-Введен шаблон для инициализации NPC и диалогов с ним.

-Введен шаблон, облегчающий размещение предметов по локациям и описание стандартных действий над ними.

-Альтернативные русские названия для некоторых команд:

DROP=БРОСИТЬ

TAKE=ВЗЯТЬ

IF=ЕСЛИ

ENDIF=КОНЕЦ

TALK=ПАРС

REDO=ВВЕРХ

GOTO=ИДТИ

READ=ОЖИДАТЬ

-Введена возможность организации текстовой навигации в редакторе карты.

-Переработана команда DROP, теперь можно писать все через пробел, необходимость в инициализации предмета перед применением drop отпала, если предмет ранее не был объявлен или =0, ему присваивается количество =1 и выбрасывается на локацию. В объявлении состояния название предмета автоматически не дописывается.

-Изменен дескриптор команды TAKE см.Указатель.

-В данной версии отменена поддержка графики.

-Команда @ теперь работает правильно. Повторный вывод текста за @ произойдет только после перехода с другой локации.

-Теперь вызов :common происходит перед остановкой по команде READ, а не в момент перехода на локацию. В результате стал возможным неоднократный вызов common на одной локации.

-Исправлена ошибка, когда интерпретатор не находил ENDIF, написанный в верхнем регистре.

Об интерфейсе

На данный момент гейм-плеер, редактор кода и карты интегрированы в один общий модуль.  Для загрузки сценария (*.scn) Нужно зайти в File->Open file. При этом сразу произойдет запуск сценария на исполнение.

В меню GAME можно сбросить исполнения сценария (RESET) на начало и снова запустить (BEGIN). Save/Load game – сохраняет/грузит текущее состояние игры. Сохраненный файл (*.sav) при последующей загрузке больше не требует загрузки сценария игры.

В меню EDIT для перехода в режим редактирования сценария выбрать Edit Scenary. Для перехода в режим конструктора карты выбрать Map editor.

В MapEditor’е выбрать FILE->Export map для экспорта созданной карты в редактор сценария.


Подробнее о реализации

Lotus If 2.01 - псевдо-объектноорентированный язык, всего с одним классом объектов,

в который можно динамически добавлять новые свойства, свойства автоматически

распространяются на все созданные объекты класса.

Начинать работу лучше с описания локаций. Описание начинается с имени локации после

двоеточия (:). Каждая локация должна завершаться командой Read или ЖДАТЬ.

После достижения команды read интерпретатор переходит в режим ожидания ввода

текста.

Существующие предопределенные переменные

LOCATION - имя текущей локации, доступно для условия проверки соответсвия свойства РАСПОЛОЖЕНИЕ

    к текущей локации, а так же для присвоения свойству РАСПОЛОЖЕНИЕ имени текущей локации.

Работа с указателем на объект

<object> - указатель на объект, введеный в строке ввода. Доступен для применения внутри

     конструкции TALK <object> ... REDO

     Если указатель содержит имя объекта недоступного на текущей локации и не принадлежащий

     инвентарю, команды внутри конструкции выполняться не будут.

Внутри конструкции указатель <object> можно использовать вместо настоящего названия

объекта и получать доступ ко всем его свойствам.

Пример:

<object>_Расположение=комната1

В принципе указатель можно использовать вне конструктора, при этом указатель

будт спозицирован на последнем введенным в конструкторе объекте (только смысла

в этом имхо мало).

Особенности вывода на экран

Существуют определенные правила вывода текста на экран, которые могут пригодиться.

Нужно запомнитЬ, что сначала на экран выводится текст после @, затемем после !.

Текст после @ выводится только один раз при заходе на локацию, обычно это описание

самой локации. Затем, если на локации имеются какие-либо видимые объекты,

выводятся их дескрипторы. Может потребоваться чтобы дескрипторы выводились

после какого-то определенного предложения из описания локации. Для этого в конце

предложения нужно поставить знак %. Тогда к концу этой строки "приклеются"

все дескрипторы.

Пример:

@Ты находишься в спортивном зале. Большое светлое помещение с множеством огромных

@окон.%

@На запад выход в коридор.

Условия связаные с

    значением имени локации

Для проверки значения переменной location из common'a можно записать условие:

IF location=<имя локации>

Для проверки значения свойства РАСПОЛОЖЕНИЕ у объектов , можно записать:

IF <object>_Расположение=location

или:

IF <object>_Расположение=<имя локации>

IF <object>_Расположение=INV

Существует так же специальное условие проверки на доступность

объекта из текущей локации (аналог Расположение=INV ИЛИ Расположение=Location)

IF <object>_Расположение=Enable

Как работать с парсером

Все вводимые команды инициализируются в конструкции TALK ... REDO

После слова TALK (или русский аналог ПАРС) перечисляются маски

тех слов, ввод которых будет ожидаться компьютером от игрока.

Если все маски при вводе совпадают, выполняется тело конструкции

со строки ниже команды TALK до строки REDO, затем происходит переход

на метку текущей локации.

Например чтобы программа поняла фразу: ПРИВЯЗАТЬ ЛОШАДЬ ВЕРЕВКОЙ К ДЕРЕВУ

Можно написать:

TALK привяз веревк лошадь дерев

...

... эти строки будут выполняться если играющий введет фразу

REDO

Через знак / можно перечислять другие варианты масок.

Например:

TALK привяз веревк лошадь дерев/прицепить веревк лошадь дерев

Интерпретатор позваляет указывать, вместо маски слова, указатель на объект

<object>. Если играющий в фразе введет так же имя объекта, и этот объект

будет находится или в инвентаре или на текущей локации или в объекте,который есть

на локации или в инвентаре, тогда выполнится тело конструкции.

Для определения объекта по маске, используются свойства объекта

МАСКА и СИНОНИМ. Таким образом у объекта может быть одновременно две маски.

Доступ к свойствам указателя аналогичен доступу к свойствам любого объекта.

Для примера смотрите типовые манипуляции с предметами в локации common

в шаблоне, при загрузке платформы.

    Как манипулировать предметами

Предметы - это объекты, и они обладают всеми свойствами объектов.

Чтобы поместить объект в инвентарь нужно написать:

<имя объекта>_РАСПОЛОЖЕНИЕ=INV

Бывает полезно, прежде чем брать предмет в инвентарь, проверить его

свойство НЕПЕРЕНОСИМЫЙ на FALSE, иначе легко можно поместить в инвентарь

какого-нибудь NPC (хотя иногда это неплохо).

Вообще типовые действия (взять,бросить,изучить) уже указаны в

lotus.lib, посмотрите, как там организовано изменение и проверка

расположения объекта.

Есть так же ключевые команды:

EXAMINE <object>  - показывает значение свойства ОПИСАНИЕ выбранного

объекта.

SHOWITEMS <object> - показывает вложеные в указаный объект предметы, у которых

значение свойства НЕВИДИМЫЙ=FALSE (НЕТ)

Вместо стандартного "Там же есть:", можно указать свое

значение свойству СОДЕРЖАНИЕ.

  Как поместить объект в объект, а

         потом извлечь его

Вообще для каждого объекта-контейнера положено вручную прописывать

команды типа "положить в объект <object>" и "взять из объекта <object>"

и в предложении указывать этот самый объект-контейнер.

В свойстве РАСПОЛОЖЕНИЕ помещаемого объекта указываем

имя объекта-контейнера, а свойство НЕДОСТУПЕН делаем true.

Когда нужно извлечь объект, восстанавливаем значения измененных свойств.

Пример:

:common

TALK положить <object> в коробку

IF коробка_Расположение=INV

IF <object>_Расположение=INV

<object>_Недоступен=true

<object>_Расположение=коробка

!Ты положил <object> в коробку

ENDIF

ENDIF

REDO

TALK вынуть <object> из коробки

IF коробка_Расположение=INV

IF <object>_Расположение=коробка

<object>_Расположение=INV

!Ты вынул из коробки <object>

<object>_Недоступен=false

ENDIF

ENDIF

REDO

...

RETURN

           Как перемещать NPC

Для начала нужно проинициализировать счетчик шагов. Счетчик будет увеличиваться на 1

каждый раз, когда Вы совершаете шаг и, в зависимости от значения счетчика, прописываем

свойство РАСПОЛОЖЕНИЕ у объекта - NPC. Если требуется чтобы NPC двигался медленно,

нужно пропускать несколько значений счетчика.

Инкремент счетчика производим в локации common, там же меняем значение свойства

РАСПОЛОЖЕНИЕ.

Пример:

:common

chet=chet+1

IF chet=1

npc_Расположение=location1

IF chet=4

npc_Расположение=location2

IF chet=8

npc_Расположение=location3

IF chet=12

npc_Расположение=location4

IF chet=16

npc_Расположение=location5

IF chet=18

chet=0

...

RETURN


Рекомендации по разработке.

Объявление локаций:

Начать лучше всего с описания локаций. По предварительно составленной схеме локаций объявляем их с помощью установки меток :имя локации.

Если нужно, добавляем картинку командой PIC.

Далее вводим общее описание комнаты через символ @. Этот символ обеспечит однократный вывод описания локации при заходе на нее.

После описания локации следуют описания действий на ней, затем команды перехода на другие локации BTN и BTNS .

Все локации (кроме :common)  нужно завершать командой останова read.

Работа с инвентарём :

Предметы можно заполучить по-разному. Можно, к примеру придя в магазин, дать денег и купить пива. А можно просто пройтись по свалке и найти валяющуюся бутылку. В первом случае получение пива строго привязано к условию (ЕСЛИ ЕСТЬ ДЕНЕГ>N ТО ДЕНЕГ=ДЕНЕГ-N,  ПИВА=ПИВА+1) в программе это реализуется  примерно так:

INV деньги	* Здесь объявляем переменные инвентаря

деньги=100	* перед их использованием

…

:магазин	* Объявили локацию

do купить пива	* Обработка нажатия кнопки КУПИТЬ ПИВА

if деньги<25	* Если денег больше нет тогда делаем это.

! У вас нет столько денег

endif	* конец условия

if деньги>=25	* Это условие выполняется если денег хватает.

^INV пиво	* Достаточно один раз объявить что пиво предмет.

деньги=деньги-25

пиво=пиво+1

endif

redo	* Конец обработчика

read	* Остановка и переход в режим ожидания нажатий.

В рассмотренном примере пиво как предмет было объявлено в самом начале программы и когда его количество стало больше 0, оно появилось в списке инвентаря. Это пример получения предмета из условия. Пока условие о наличии денег выполняется, нам будут послушно каждый раз выдавать  новую бутылку пива. В другом случае, если мы находимся на локации свалка, то, лежащее там  пиво строго ограничено одной штукой.

Тогда мы помещаем его на локацию командой DROP, как в примере:

:свалка

@ Вы находитесь на городской свалке

^пиво=пиво+1	* Прибавляем к инвентарю бутылку

^drop,пиво,лежит бутылка	* и тут же ее выбрасываем на свалку.

Talk поднять/взять/брать	* если будет введена фраза, где

Take пиво вы взяли	* встретится одна из перечисленных

Redo	* частей слова, вы возьмете бутылку

read

Теперь поясню, для чего нужно было прибавлять бутылку пива к инвентарю. Допустим, если у вас уже есть пиво, а вы  попадаете на локацию свалка, тогда при выполнении команды DROP вы лишитесь одной бутылки.

Чтобы этого не происходило мы ее прибавляем. Нам не нужно, чтобы команда DROP срабатывала каждый раз, как только мы входим на локацию (получится что мы постоянно будем находить бутылку пива), по этому, поставив впереди значок ^, мы тем самым сказали интерпретатору, что DROP выполнится один раз.  Как вы могли заметить, перед использованием команды DROP переменная пиво не была объявлена командой INV как инвентарь, интерпретатор при выполнении DROP сам производит преобразование переменной в инвентарь.

О том как можно манипулировать предметами:

Во первых иногда надо иметь возможность осуществлять в любой локации какое-то определенное действие связаное с предметом инвентаря. Например у вас есть карта и вы должны иметь возможность посмотреть ее на любой локации. Для этого действие cмотреть карту  совсем не нужно привязывать к каждой локации, достаточно это действие описать на локации :common. При каждом заходе на какую-нибудь локацию, сначала выполнится содержимое локации :common. Если к примеру действие назначить на кнопку по команде DO, нужно описать эту команду на локации :common примерно так:

:комната1

@ Вы находитесь в комнате.

…

read

:common	* Содержимое этой локации будет

do осмотреть карту

if карта=1	* выполняться прежде чем произойдет

! Вы посмотрели на карту и все поняли.	* переход на локацию комната1

endif

redo

return

Главное свойство локации :common в том что при переходе на нее, в стеке сохраняется адрес  настоящей локации, и команды redo или return вернут вас обратно в начало настоящей локации. Таким образом выполнится подпрограмма.

Может быть потребуется управлять предметами дистанционно. В Lotus это тоже возможно. Например, вы нажали в подвале на рычаг, и в печи котельной исчез уголь. Для этого существует команда KILL, убивающая брошеный на какой-то локации предмет.

Логические условия.

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

Логическое условие И можно реализовать так:

IF условие1

IF условие2

…	* Выполняем эти команды если оба условия истины

ENDIF	* Достаточно одного ENDIF

Логическое условие ИЛИ немного сложнее :

IF условие1

JUMP m1	* По команде JUMP произойдет вызов подпрограммы m1

ENDIF

IF условие2

JUMP m1

ENDIF

…

READ

* Подпрограмма, которая будет выполнятся если одно из условий истино.

:m1

…

RETURN	* RETURN вернет интерпретатор в начало вызывающей локации.

Применение локации :common

Если вы собираетесь делать серьезную if игру,  без локации :common обойтись будет практически невозможно. Именно в этой локации помещают обработчики таймера, если в вашей игре какое-то действие ограничено временем. Общие действия над инвентарем и менеджер характеристик также располагается на локации :common.

 Нужно хорошо представлять, как ведет себя интерпретатор на этой локации.

По достижении команды REDO произойдет немедленный выход в начало вызывающей локации. Нельзя осуществлять непосредственный переход внутри :common на метки, в этом случае будет потерян адрес вызывающей локации и как следствие возврат из :common произведется на уже новую метку.

В случае необходимости переходов внутри :сommon, используйте команду JUMP вместо GOTO, осуществляющую переход на следующую после метки строку.


:common

return

Локация :common, которая (если есть) выполняется каждый раз перед выполнением READ. При запуске сценария интерпретатор ищет локацию common, и если находит, будет постоянно к ней обращаться прежде, чем выполнять READ. То есть если :common существует, то фактически остановка и переход в режим ожидания ввода осуществляется после выхода из common по команде RETURN.

Эта локация окажется полезной например для того, чтобы отслеживать состояние характеристик и инвентаря персонажа.

Необходимо строгое соблюдение синтаксиса написания этой локации и указания в конце слова return. Выход из common может быть осуществлен и в результате исполнения команды redo, например в конструкции talk…redo. При переходе в локацию common ее адрес не заносится в стек, и таким образом сохраняется адрес прежней локации, на которую происходит возврат.


^

При установке этого символа в самом начале строки, строка будет выполняться только один раз. Полезно использовать, например,  для предварительного разброса предметов по локациям.

В этой версии  отключена.


Переменные

На платформе реализован целочисленный тип переменной. Можно присвоить ей любое имя, состоящее из одного слова. Интерпретатор приводит ее к нижнему регистру и в таком виде хранит в памяти. У переменных есть  скрытые свойства , отвечающие за принадлежность переменной к инвентарю или характеру (см. INV, VIP в Указателе).

Существует специальная переменная RND, в которой постоянно генерируется псевдо-случайное число от 0 до 99.

Есть так же переменная-таймер TIME, увеличивающая свое значение на 1 каждую секунду, которую можно сбросить на 0 в любой момент и начать новый отсчет.

Новая переменная STEP  увеличивается на 1 при каждом переходе на  локацию (шаг).


RND

RND – это обычная переменная, в которой постоянно генерируется какое-то случайное число от 0 до 99.


TIME

Переменная TIME отслеживает секунды, каждую секунду ее значение увеличивается на 1 до 86400 (24 часа) после этого значение обнуляется и начинается новый отсчет. Можно сбить счетчик, присвоив нужное вам значение, и начать с него отсчет. Переменную нельзя сбить коммандой CLEAR без параметров.


Команада CLS очищает только экран.


Устаревшая команда. В этой версии не поддерживается.


Устаревшая команда. В этой версии не поддерживается.


Используйте: VIP имя

чтобы объявить характеристику персонажа. При этом создается переменная с указанным именем, классифицируемая как характеристика  равная 0.

В списке характеристик эта переменная появится, если ее значение будет >0.

Для характеристик доступны все действия что и для простых переменных.

Пример:

VIP жизни

жизни=100

…

read


IF(ЕСЛИ) условие

…

ENDIF(КОНЕЦ)

Интерпретатор позволяет строить простые условия сравнения двух переменных/чисел без использования логических операторов типа OR,AND  и математических выражений.

При необходимости математические выражения выносятся перед условием и результат присваивается какой-либо переменной, затем в условии производится анализ этой переменной.

Логическое выражение И можно построить с использованием вложенных условий, для каждого IF нужно указывать свой ENDIF.

Если условие не истинно, интерпретатор ищет строку ENDIF и продолжает с неё работу. Если условие истинно интерпретатор переходит на следующую после IF строку.

Пример:

IF магия>=100

IF сила>40

…	- эти операторы выполняются при выполнении условий

ENDIF

ENDIF

…	- эти операторы выполняются в любом случае

В новой версии реализованы так же условия:

IF <object>_Расположение=Location …           ENDIF	Выполняется, если объект <object> расположен на текущей локации.
IF <object>_Расположение=INV …           ENDIF	Выполняется, если объект <object> расположен в инвентаре.
IF <object>_Расположение=Enable … ENDIF	Выполняется если объект <object>  существует или на текущей локации или в инвентаре.
IF <object>_Расположение=<локация> …           ENDIF	Выполняется, если объект <object> расположен на указаной локации  <локация>.
IF Location=<локация> … ENDIF	Выполняется если текущая локация cоответсвует указаной <локация>

IF enable предмет

…

ENDIF

Устаревшая команда. В этой версии не поддерживается.


TALK(ПАРС) фраза1/фраза2/…

…

REDO(ВВЕРХ)

Инициализация текстового ввода. После перечисления фраз через «/» и достижения READ, интерпретатор готов переваривать текстовую  строку . Если одна из перечисленных фраз в комманде TALK, полностью входит в вводимый текст, исполняется код между TALK и REDO, а далее интерпретатор поднимается к метке.

Пример:

…

Talk взять камень/поднять камень

! Вы подняли камень

redo

…

read

Код внутри конструкции выполнится, если например ввести : приподнять камень.


TAKE(ВЗЯТЬ) предмет описание действия

Устаревшая команда. В этой версии не поддерживается.


DROP(БРОСИТЬ),предмет,описание состояния

Устаревшая команда. В этой версии не поддерживается.


DO имя

…

REDO(ВВЕРХ)

В этой версии заменена на диалоговый выбор.

Создается кнопка с указанным именем. После этого интерпретатор переходит на строку следующую за REDO и продолжает выполнение кода до остановки по команде READ.

При нажатии на кнопку с указанным именем выполняется код записанный после команды DO до REDO, затем интерпретатор переходит вверх на метку той локации в которой он находится. Нужно помнить, что использование конструкции DO…Redo должно начинаться с указания метки на той локации, где она используется.

Пример:

:loc

Inv камень

DO взять камень

камень=камень+1

! Вы взяли камень

REDO

…

read

Рассмотрим эту конструкцию подробнее. Команда DO выполняет две задачи: задачу установки кнопки на исполнение инструкций следующих за DO и перевод интерпретатора на исполнение следующей за REDO команды.

То есть сразу инструкции в конструкции DO…REDO исполняться не будут, а смогут быть выполнены только после нажатия на назначенную  кнопку. После исполнения команд внутри конструкции интерпретатор встретит  команду REDO и осуществит прыжок  на последнюю пройденную метку (то есть на адрес из стека).

Команда GOTO заключенная внутри конструктора после исполнения убивает возвратный адрес в стеке. Используйте ее если хотите намеренно покинуть конструктор. В другом случае команда JUMP переведёт Вас на другую локацию и там по команде  REDO вы сможете вернуться обратно на прежнюю локацию.


JUMP метка

(JUMPS)

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


BTN [название кнопки] имя локации

(BTNS)

В этой версии заменена на диалоговый выбор.

Данная комманда используется аналогично BTN на if-платформе URQ. Создается кнопка с указанным названием. При нажатии на кнопку происходит переход на метку, указанную как имя локации. После этого кнопка исчезает.

Необходимо помнить, что по команде BTN только создается кнопка, а

выполнение программы продолжается до остановки по команде READ.

BTNS – работает аналогично BTN, с той разницей, что дополнительно после перехода происходит очистка экрана.

Пример:

! Вы можете пойти на юг.

BTN [Пойти на ЮГ] юг

Read

:юг

…


READ(ЖДАТЬ)

По команде READ происходит остановка интерпретатора и ожидается

ввод.

Пример:

Inv камень

DO взять камень

камень=камень+1

! Вы взяли камень

REDO

…

READ


CLEAR

CLEAR имя переменной

Командой CLEAR без параметров уничтожаются все переменные, кроме time. Для удаления только конкретной переменной (в том числе обнуления time) нужно указать её имя.


KILL предмет локация

Устаревшая команда. Этой версией не поддерживается.


!

@  текст [#переменная]

Для вывода текста на экран используйте команды ! или @. Вместе с текстом можно выводить значения переменных (в том числе инвентарь/характеристики), используя макрос [#переменная] или _#переменная_ .

Комманда @ выведет текст один раз и пока не произойдет перехода на другую локацию, выводиться больше не будет.

Пример:

! Вы стоите в большой комнате. На столе лежит [#many] монет.

Read


*

Строка, начинающаяся со значка * далее интерпретатором не рассматривается, используйте ее для коментариев.

Пример:

* Это коментарий.


GOTO(ИДТИ) метка

(GOTOS)

Комманда безусловного перехода по метке. Далее выполняется код со строки следующей после метки.

GOTOS – полный аналог GOTO, с той разницей, что при переходе происходит очистка экрана.

Пример:

:m1

….

GOTO m1


PAUSE надпись

В этой версии не реализована.


PIC имя картинки.jpg

DELPIC

Эти команды больше не поддерживаются.