ТОМ - платформа для текстовых игр

Объявление

Платформа ТОМ предназначена для создания текстовых игр на русском языке и имеет развитый парсер, позволяющий взаимодействовать с играми с помощью команд на близком к естественному языке. В данный момент активно разрабатывается версия ТОМ 2.
Последнюю версию платформы можно скачать здесь.

Информация о пользователе

Привет, Гость! Войдите или зарегистрируйтесь.



ТОМ2: Минимальный проект

Сообщений 1 страница 10 из 10

1

Что же необходимо написать в проекте игры на ТОМ2, чтобы она нормально запустилась? Какие разделы, объекты или секции необходимы для нормального запуска игры? Считается ли пустой текстовый файл корректным интерактивным квестом для интерпретатора ТОМ2?
На данный момент мы не имеем никакой документации. Да оно и понятно, ведь платформа на этапе альфа-разработки. На руках у нас только маленький демо-пример квеста, закрытый словарь на over 3000 слов (предположительно) и, собственно, сам интерпретатор ТОМ2 (вернее его альфа-версия 2.a.3.8). Но не отчаиваемся, включаем думалку и вперёд!

Будем проводить полевые испытания (демо игра как-раз про поляну в центре леса  8-) )
Первый вопрос, который мы проверим - считается ли пустой файл корректным для интерпретатора? Всё предельно просто: создаём пустой текстовый файл и перетаскиваем его на tom2.exe. В консоли видим:

выполняем файл - выполнен.
Ок

Наверняка это говорит о том, что для создания минимального проекта на языке ТОМ2 нужно написать ровно НИЧЕГО.

На этом можно было бы закрыть тему, но мы ведь хотели увидеть хоть что-то в игре. Ладно, попробуем сделать минимальную игру, которая будет хоть что-то показывать помимо стандартных сообщений интерпретатора.
Для начала давайте выведем до боли знакомую банальную строчку "Привет, мир!". Эмм... ладно, допишем ещё что-нибудь, чтобы было интереснее :) Для этого нужно в текстовом файле написать то, что мы хотим вывести в консоль, но начиная с символа % (процент):

Код:
%_Привет, мир!
%_Алиса находилась в центре необычной комнаты: стены её были составлены из квадратных плит, шириной примерно пол метра. По центру комнаты стоял круглый стеклянный стол с несколькими предметами на нём. Стекло было на столько чистым и прозрачным, а освещение в комнате было таким тусклым, что сначала Алисе показалось, что предметы висят в воздухе.

Да, если мы хотим вывести многострочный текст, начинаем каждую строку с символа %.
Знак "_" (земля) в строках ТОМ2 является аналогом пробела, но в отличии от обычного пробела, он не триммируется.

Технические подробности

Триммирование - это процесс приведения строки в порядок, перед выводом. Интерпретатор ТОМ2 перед выводом строки "причёсывает" её, выполняя вложенные куски кода, расставляя заглавные буквы, точки и убирая лишние пробелы, в том числе с конца и начала строки. Вот когда мы не хотим, чтобы пробелы были убраны, то ставим вместо них землю.

Запускаем, и что мы видим!
http://s3.uploads.ru/t/P7Ffw.png

"Замечательно!" - скажете вы
"Ужасно!" - скажу я
Да-да, мы выполнили задачу вывести наш текст на экран, но посмотрите: он ведь абсолютно статичен, мы по прежнему не можем ничем управлять, да и вывелся он куда-то в общий лог, а не как основной текст, описывающий нашу локацию.
А вот что-бы чем-то удобно управлять, нужно ООП (объектно-ориентированное программирование).

Что-ж, время ещё есть, поставим третью цель на сегодя: чтобы ТОМ2 отреагировал на нашу команду так как мы хотим.
Тут уже наступает время ООП. Берёмся крепче за стул, и пытаемся вникнуть во всё это.

Ремарка

На самом деле, я сам совершенно не разбираюсь в новом синтаксисе ТОМ2, поэтому всё нижеописанное - результат личных экспериментов прямо в процессе написания этой статьи

Какой же главный объект в интерактивной текстовой литературе?
"Локация?" - спросите вы
"Персонаж" - скажу я
Персонаж, или протагонист - центральный объект в интерактивной литературе. Тот, кто описывает всё происходящее. Ведь он может просто так, висеть в воздухе и описывать пустоту, но не имея протагониста мы не имеем возможности что-либо обозревать или управлять чем либо.
Ладно, попробуем сделать описать нашего главного персонажа:

Код:
человек Алиса
{ внешнее_описание = "Маленькая девочка Алиса, которая согласилась поучавствовать в этом странном эксперименте за шоколадную конфету"
}
pers = Алиса

pers - это ключевое слово, в которое нужно "помещать" того персонажа, который является главным в данный момент. По сути pers - это тот объект, которым мы сейчас управляем :)
Итак, протагониста создали, дали ему управление. Запускаем, вбиваем элементарную команду "осмотри себя"... не работает! Что-же мы забыли? Да на самом деле много чего.
Во-первых, забыли описать саму команду "осмотри". Интерпретатор просто не понимает, что делать со словом "осмотри".
Во-вторых, слово "себя" нет в "стандартной библиотеке".
В-третьих, слово "Алиса" тоже отсутствует в стандартном словаре ТОМ2, поэтому объект то мы создали, но на него не ссылается ни одно слово, и как-либо обратиться к нему мы не сможем.

Стандартный словарь

Для поиска слов в словаре используем программу dic_viewer.exe, которая прилагается к платформе ТОМ2.
Заметка автору этой программы (ASBer-у): Сделай, чтобы вместо нажатия на кнопку "Посмотреть", можно было жать Enter в поле ввода слова.

Давайте же исправим всё по пунктам!

Действия описываются ключевым словом action. Внутри него есть несколько секций (parsing, check, execute). Всё это уносит нас далеко в дебри особенностей единственного и неповторимого языка ТОМ2. Поэтому опустим сейчас подробное описание всех секций, а просто спи... своруем описание действия "осмотри видимое" из демо-примера. Вот оно:

Код:
//========================================================================================
action(осмотри act*Пф, всё:видимое Это*Вп) //это действие для видимых объектов
{ == parsing ==
  fail, "fail" if :это.cls = пусто //должен быть класс
  fail, "это не по-русски" if :это.word.name = "ты" //"осмотри тебя" - это не по-русски
  Ok
  == check ==
  //объект должен быть виден
  :это.видно

  == execute ==
  if :Это is string or number then
  { var Str = :Это
    %{:Str}
  }
  else 
  { if :Это.внешнее_описание = пусто then %<font collor=red>{:Это*Ип} не имеет внешнего описания!<font>
    else %{:Это.внешнее_описание}. 
  }
}

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

Слово "себя" на самом деле есть в стандартном словаре, но оно ни с чем не связано. Кто же этот "себя"? Да это же тот персонаж, которым мы сейчас управляем! Так и напишем:

Код:
var себя = pers

Почему var? Почему переменная? Я не могу дать ответ на этот вопрос. ТОМ2 во многом своеобразен.

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

Код:
слово Алиса
{
	key = ЖрОд
	defkey = ИпЕч
	form ИпЕч = "Алиса"
	form ИпМч = "Алисы"
	form РпЕч = "Алисы"
	form РпМч = "Алис"
	form ДпЕч = "Алисе"
	form ДпМч = "Алисам"
	form ВпЕч = "Алису"
	form ВпМч = "Алис"
	form ТпЕч = "Алисой"
	form ТпЕчПс = "Алисою"
	form ТпМч = "Алисами"
	form ПпЕч = "Алисе"
	form ПпМч = "Алисах"
}

Нужно всего-лишь описать все формы слова и добавить 2 параметра:
key - неизменяемые параметры слова (Жр - женский род, Од - одушевлённое)
defkey - параметры по умолчанию (Ип - именительный падеж, Еч - единственное число). Используются, когда к слову обращаются, не указывая параметров.

Запускаем, проверяем:
http://s3.uploads.ru/t/CrcVE.png
Работает!

Да, персонаж работает в пустоте, совсем без локаций. Ладно, на сегодня можно и прекратить, слишком много времени уже потрачено.
"А как же конфета!?" - скажете вы - "Алиса ведь не согласится больше участвовать в наших экспериментах"
Ладно, давайте дадим ей эту конфету!

Код:
объект конфета
{is предмет
  внешнее_описание = "Шоколадная конфета в блестящей упаковке. Наверное, очень вкусная."
}

Алиса + конфета

Здесь мы создали конфету, сказали что она является предметом и дали краткое описание. Так-же мы дали конфету Алисе оператором "+". Не смотря на безупречный код, Алиса отказывается осмотреть конфету в своих карманах. Ну что-ж, видимо издержки альфа версии: все предметы "внутри" персонажа являются невидимыми :)
Ну и ладно, главное конфета у Алисы, а значит она будет участвовать в следующих испытаниях платформы ТОМ2! Тогда-то мы и поговорим о локациях.

Пока!

Весь текст программы
Код:
%_Привет, мир!
%_Алиса находилась в центре необычной комнаты: стены её были составлены из квадратных плит, шириной примерно пол метра. По центру комнаты стоял круглый стеклянный стол с несколькими предметами на нём. Стекло было на столько чистым и прозрачным, а освещение в комнате было таким тусклым, что сначала Алисе показалось, что предметы висят в воздухе.

слово Алиса
{
	key = ЖрОд
	defkey = ИпЕч
	form ИпЕч = "Алиса"
	form ИпМч = "Алисы"
	form РпЕч = "Алисы"
	form РпМч = "Алис"
	form ДпЕч = "Алисе"
	form ДпМч = "Алисам"
	form ВпЕч = "Алису"
	form ВпМч = "Алис"
	form ТпЕч = "Алисой"
	form ТпЕчПс = "Алисою"
	form ТпМч = "Алисами"
	form ПпЕч = "Алисе"
	form ПпМч = "Алисах"
}

человек Алиса
{ внешнее_описание = "Маленькая девочка Алиса, которая согласилась поучавствовать в этом странном эксперименте за шоколадную конфету"
}

pers = Алиса
var себя = pers

//========================================================================================
action(осмотри act*Пф, всё:видимое Это*Вп) //это действие для видимых объектов
{ == parsing ==
  fail, "fail" if :это.cls = пусто //должен быть класс
  fail, "это не по-русски" if :это.word.name = "ты" //"осмотри тебя" - это не по-русски
  Ok
  == check ==
  //объект должен быть виден
  :это.видно

  == execute ==
  if :Это is string or number then
  { var Str = :Это
    %{:Str}
  }
  else 
  { if :Это.внешнее_описание = пусто then %<font collor=red>{:Это*Ип} не имеет внешнего описания!<font>
    else %{:Это.внешнее_описание}. 
  }
}

object конфета
{is предмет
 внешнее_описание = "Шоколадная конфета в блестящей упаковке. Наверное, очень вкусная."
}

Алиса + конфета

Отредактировано Alexandr (2013-05-11 19:15:51)

2

Спасибо! отличное исследование!

Главное меню вшито в платформу и мы не способны на него влиять.

Почему же не способны?

Код:
%<location>нестандартное меню:<btn=привет!>привет мир</btn>. </location>

Если в игре нет персонажа (pers) эта строка появится 1 раз, если же персонаж определён - зависнет вверху экрана до вывода следующего тэга <location>.


Во-первых, забыли описать саму команду "осмотри". Интерпретатор просто не понимает, что делать со словом "осмотри".

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


Алиса + конфета

На самом деле в 2м ТОМе этот синтаксис сейчас не работает, но я подумаю чтобы он заработал.
Должно сработать:

Код:
конфета внутри Алисы

("Внутри", это значит в кармане или в руке, а не то что Алиса её съела)

В остальном всё совершенно верно, спасибо за замечательный обзор.

3

Да, действительно "внутри" сработало. Я вообще думал написать "дать Алисе конфету", но это было бы уже действием, которое пришлось бы описывать как действие "осмотреть". Внутри этого действия всё-равно надо было бы написать "внутри" :) Но выглядело бы это однозначно красивее. Сейчас поэкспериментирую над этим.  :crazy:

4

Код:
  объект стол
  {
	внешнее_описание = "Стол находится точно по центру комнаты и представляет из себя круглый лист сантиметрового стекла, держащийся на единственной металлической ножке, намертво вбитой в пол. {pers*Дп} очень захотелось потрогать его руками, на столько он был гладким и неестественным."
	%{items.count} // для теста

	объект ключ
	{
    внешнее_описание = "Бронзовый ключик небольшого размера. Отпалирован до блеска."
	}

	объект бутылочка:зелья
	{
    внешнее_описание = "ту-туру"
	}
  }

Можно ли как-то достучаться до объектов внутри этого объекта, что-бы перечислить их, как лежащие на столе?

5

Alexandr написал(а):

Можно ли как-то достучаться до объектов внутри этого объекта, что-бы перечислить их, как лежащие на столе?

Пока никак. Могу предложить временный обходной путь:

Код:
var _предметы = "на столе лежат "
if ключ.pos = стол then _предметы = _предметы + "ключ "
if бутылочка:зелья.pos = стол then _предметы = _предметы + "бутылочка зелья "
%{_предметы}

6

Обнаружил какой-то странный глюк. Наверное час искал, почему внешнее_описание у комнаты вдруг обнуляется.
Код для воспроизведения:

Код:
локация комната:1
{
  это комната
  var title = "странная комната"
  внешнее_описание = "хэх"
  объект стол
  {
    внешнее_описание = "Описание стола"

	объект бутылочка:зелья
	{
    внешнее_описание = "описание бутылочки"
	}
	объект ключ
	{
    внешнее_описание = "Бронзовый ключик небольшого размера. Отпалирован до блеска."
	}
  }
}

В консоли набираем:

> вне игры
Вы вне игры.
> комната:1.внешнее_описание
Описание бутылочки

Почему вдруг комната берёт себе описание бутылочки? Менял местами создание ключа и бутылочки, всегда берёт именно от бутылочки. Странно  %-)

7

Спасибо, поразбираюсь. Я подозревал что там что-то не так работает при переопределении классовых свойств.
Как вариант, можешь везде писать var внешнее_описание = "..." - должно правильно работать.

8

Alexandr написал(а):

Почему вдруг комната берёт себе описание бутылочки? Менял местами создание ключа и бутылочки, всегда берёт именно от бутылочки. Странно

Как это ни странно выглядит, но так и должно работать =) сейчас объясню.
Первое на что нужно обратить внимание - классы ключа и бутылочки зелья:
>ключ.класс
>бутылочка:зелья.класс
У ключа есть библиотечный класс, а у бутылочки зелья его нет.

Далее с ключом всё ясно и понятно, строка внешнее_описание = "Бронзовый ключик небольшого размера. Отпалирован до блеска." выполняется следующим образом:
1. находится классовое значение свойства "внешнее_описание", оно есть у класса "всё:видимое" и доступно по цепочке классовых наследований.
2. классовое свойство во всем эмитирует поведение собственного свойства объекта, т.е. если мы хотим его изменить, изменяется не свойство класса, а создаётся одноимённое свойство объекта, которому и присваивается новое значение.
Что собственно и происходит.

С бутылочкой чуть сложнее. Строка внешнее_описание = "описание бутылочки" выполняется так:
1. ищется значение свойства "внешнее_описание" для объекта "бутылочка:зелья".
2. свойство "внешнее_описание" отсутствует у самой бутылочки и не находится у её классов, так как классов нет.
3. следующим этапом "внешнее_описание" ищется в окружении бутылочки и находится на 1 уровень ниже в контексте стола.
4. т.к. найденное значение принадлежит столу, а не бутылочке, при присвоении происходит замена значения свойства стола.

Какие можно сделать выводы:
1. классы надо назначать в самом начале конструктора, прежде чем  мы начали делать что-то другое.
2. при создании свойств объектов лучше использовать синтаксис var X = value
3. функции и конструкторы ТОМа не инкапсулируются как в других языках. Для функции доступны помимо собственных аргументов и глобальных переменных также и переменные контекста, из которого функция вызвана. К этому надо привыкнуть и писать немного аккуратнее =)

9

Ужас, как всё сложно! Я конечно понял, но пойди объясни это новичкам. Вот так вроде делаешь для людей, все сложности выносишь в стандартную библиотеку. Остаётся только сидеть да писать на ЕЯ квестики, а выходит вон что. Так или иначе надо разбирать внутреннее устройство парсера, либо читать огромный список строгих правил, следуя которым таких плавающих ошибок почти не будет. Шаг влево, шаг в право - расстрел лезешь на форум и всей толпой разбираешься "почему парсер делает не то, что написано?".
Ладно, поживём увидим, может правил будет не так уж и много.

10

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