# Обобщение

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

## Константы и переменные

Константа это такое имя, значение которого строго задаётся и не может изменяться. Ранее мы встретили строковую константу:

    Constant Story "Хейди";

и числовую константу:

    Constant MAX_CARRIED 1;

Это два наиболее частых способа использования констант в Информе.

Значение переменной может изменяться в ходе игры. Мы ещё не задавали собственных переменных, но уже пользовались двумя такими из библиотеки, где они заданы следующим образом:

    Global location;
    Global deadflag;

Значение глобальной переменной по умолчанию равно 0, но его можно изменить в любой момент, например мы ввели:

    location = before_cottage;

для изменения `location` на `before_cottage`, а также

    if (nest in branch) deadflag = 2;

для изменения `deadflag` на 2.

В других главах будет освещено использование локальных переменных и свойств как переменных.

## Описание объектов

Как можно было заметить из предыдущей главы, вся игра описывается в виде объектов. Каждая комната, предмет и даже сам игрок является объектом (объект игрока задаётся библиотекой).

Общий формат задания объекта следующий:

    Object идентификатор "игровое_имя" родительский_объект
    with    свойство значение,
        свойство значение,
        ...
        свойство значение,
    has атрибут атрибут ... атрибут
    ;

Описание начинается с ключевого слова `Object` и заканчивается точкой с запятой, а между ними идёт три основных блока:

- Сразу за словом `Object` идёт заголовочная часть;

- со слова `with` начинается перечисление свойств;

- со слова `has` начинается перечисление атрибутов.

### Заголовок объекта

Заголовок состоит из трёх частей, каждая из которых не обязательна:

- Внутренний идентификатор, по которому другие объекты обращаются к данному объекту. Это должно быть одно слово (можно с цифрами и знаком подчёркивания), до 32 латинских символов, и оно должно быть уникальным в игре. Идентификатор можно опустить, если к объекту не обращаются другие объекты.

  Примеры: `bird`, `tree`, `top_of_tree`.

- Игровое имя в двойных кавычках. Оно может состоять из нескольких слов и не обязательно быть уникальным (например, можно иметь несколько комнат с именем `"Где-то в пустыне"`). Не обязательно, но крайне рекомендуется дать каждому объекту игровое имя.

  Примеры: `"птенчик/"`, `"высок/ий платан/"`, `"На верхушке дерева"`.

- Внутренний идентификатор другого объекта, в котором будет находиться данный в начале игры (такой объект называется «родительским»). Это значение не указывается, если у объекта не будет родительского объекта, а также никогда не указывается для комнат.

  Например, птенчик описывается как:

      Object bird "птенчик/" forest

  что означает что в начале игры он будет находиться в лесной чаще (игрок затем подберёт его с собой). Платан описан следующим образом:

      Object tree "высок/ий платан/" clearing

  Он будет находиться на полянке, но так как он является `scenery`, то игрок не сможет его переместить.

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

      Object -> bird "птенчик/"
      ...

  Мы не будем использовать его в примерах, но тем не менее это довольно удобный и наглядный способ.

### Свойства объектов

Свойства начинаются с ключевого слова `with`. У объекта может быть любое количество свойств и заданы они могут быть в любом порядке. Сначала идёт имя свойства, затем через пробел его значение, после значения — символ запятой.

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

    description "Гнездо сплетено из прутиков и аккуратно устлано мхом.",
    e_to forest,
    name 'детеныш' 'птиц' 'птичк' 'птенчик' 'птенц' 'маленьк',
    each_turn [; if (nest in branch) deadflag = 2; ],

В перечисленных примерах встречаются различные варианты значений свойств: `description` это строка, со свойством `e_to` ассоциирован объект, в свойстве `name` идёт список словарных слов, а `each_turn` содержит в себе локальную функцию. Также значение может быть числовым, например:

    capacity 10,

Существует около 50 стандартных свойств наподобие `name` или `each_turn`. Позже будут рассмотрены самые важные из них, а также то, как задать собственное свойство.

### Атрибуты объектов

Атрибуты начинаются с ключевого слова `has`. Их может быть любое количество и в любом порядке; друг от друга они отделяются пробелом.

Атрибуты проще свойств — у них нет значения, они могут либо присутствовать, либо отсутствовать (быть включены/выключены). Атрибут можно назвать флагом. Изначально если атрибут указан, то он включён (присутствует), если не указан — то выключен (отсутствует).

Ранее мы встретились со следующими атрибутами:

    container light open scenery static supporter

Каждый из них отвечает на вопрос, например, «Является ли объект контейнером?», «Является ли он источником света?» и так далее. Если атрибут указан, то ответом будет «да», если не указан — «нет».

Существует около 30 стандартных атрибутов. Можно также создавать и свои собственные.

## Связи между объектами и дерево объектов

Во время игры Информ следит за связями между объектами — то есть помнит, где находится конкретный объект относительно других объектов. При рассмотрении связей по отношению к объектам используются термины «родительский» и «дочерний».

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

- объект лесной чащи является родительским для объекта игрока, или что

- объект игрока является дочерним для объекта лесной чащи.

Также если игрок держит в руках объект, например гнездо, то

- объект игрока — родительский для объекта гнезда, или

- объект гнезда — дочерний для объекта игрока.

У объекта может быть только один «родитель» (или не быть родительских объектов вообще), но может быть сколько угодно «детей» (в том их может и не быть).

Например, рассмотрим следующие объекты:

    Object nest "птичь/е гнезд/о" clearing
    ...
    Object tree "высок/ий платан/" clearing

Здесь для гнезда родителем является объект `clearing`, и также для платана тоже родителем является `clearing`. То есть и гнездо, и платан являются детьми локации Полянка.

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

Птенчика в лесной чаще мы описали следующим образом:

    Object bird "птенчик/" forest
    ...

В лесной чаще больше ничего нет, поэтому чаща является родителем объекта птенчик, и у чащи есть единственный дочерний объект, птенчик. Когда игрок, который изначально находится в `before_cottage`, переходит на ВОСТОК в чащу, то происходит следующее: родителем игрока становится `forest`, а у `forest` становится два дочерних объекта — птенчик и игрок. В такой манере Информ следит за перемещением объектов и изменением связей.

Далее, пусть игрок подбирает птенца. Происходит изменение связей: птенец теперь — дочерний объект для игрока (уже не для леса), а игрок становится и родительским (для птенца), и дочерним (для леса) объектом.

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

1. В начале игры: |
2. Игрок вводит ИДТИ НА ВОСТОК |
3. Игрок вводит ВЗЯТЬ ПТЕНЦА |
4. Игрок вводит ИДТИ НА СЕВЕРОВОСТОК |
5. Игрок вводит ПОЛОЖИТЬ ПТЕНЦА В ГНЕЗДО |
6. Игрок вводит ВЗЯТЬ ГНЕЗДО |
7. Игрок вводит ИДТИ ВВЕРХ |
8. Игрок вводит ПОЛОЖИТЬ ГНЕЗДО НА СУК |

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

Позже будут рассмотрены команды `parent`, `child` и `children`, при помощи которых можно получить для конкретного объекта его родителя, дочерние объекты и их количество.

## Двойные и одинарные кавычки

### Двойные кавычки

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

Некоторые примеры специальных символов:

- Для записи двойных кавычек в строке используется тильда: `~`

- Для переноса строки используется символ `^`

Длинные строки можно разбить на несколько строк с переносами, Информ просто склеит их, отбросив лишние пробелы (пробелы между словами остаются нетронутыми). Следующие две строки одинаковы для Информа:

    "Это строка из     разных символов."

    "Это
      строка
        из     разных
                    символов."

При выводе длинного пассажа текста интерпретатор делает автоматический перенос с края экрана. Для собственного переноса используется символ `^`.

В игре мы использовали строковую константу:

    Constant Headline
        "^Пример простой игры на Inform.
         ^Авторы: Роджер Фирт (Roger Firth) и Соня Кессерих (Sonja Kesserich).
         ^Перевод Юрия Салтыкова a.k.a. G.A. Garinson^";

которую можно было бы с тем же успехом записать как

    Constant Headline
        "^Пример простой игры на Inform.^Авторы: Роджер Фирт (Roger Firth) и Соня Кессерих (Sonja Kesserich).^Перевод Юрия Салтыкова a.k.a. G.A. Garinson^";

Строки используются, например, в свойстве `description`:

    with description "Слишком мал, чтобы летать, птенец беспомощно попискивает.",

Также строки применяются в командах `print`, что можно будет увидеть позже.

### Одинарные кавычки

В одинарные кавычки заключаются словарные слова. Это должно быть единственное слово, без пробелов (можно с цифрами и дефисом). Регистр символов не учитывается. Кроме того, значащими являются только первые девять символов.

Когда игрок вводит команду, интерпретатор разбивает ввод на отдельные слова и затем ищет их в словаре. Если эти слова образуют некую верную команду, то он пытается её выполнить.

Пример из нашей игры:

    name 'птичь' 'гнезд' 'гнездышк' 'пруть' 'прутик' 'мох',

## Функции и инструкции

Функция представляет из себя набор инструкций, которые выполняются интерпретатором. Есть два вида функций и более 20 видов инструкций.

### Инструкции

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

    location = before_cottage;

что называется присваиванием. Присваивание задаёт новое значение для переменной, в данном случае глобальной библиотечной переменной `location`. Далее,

    if (nest in branch) deadflag = 2;

содержит сразу две инструкции, присваивание, перед которым идёт инструкция `if`:

    if (nest in branch) ...

Инструкция `if` проверяет выполнение какого-либо условия. Если условие истинно, то интерпретатор выполняет инструкцию, которая следует далее. Если условие ложно, то следующая инструкция пропускается. В данном случае проверяется условие, находится ли `nest` на или в объекте `branch` (то есть является ли дочерним). Практически всегда во время игры это будет ложно, поэтому следующая инструкция игнорируется. Когда же условие выполнится, то интерпретатор выполнит присваивание:

    deadflag = 2;

что изменит `deadflag` на 2. Обычно подчинённые инструкции записываются под `if`, с отступом, потому что так их проще читать:

    if (nest in branch)
        deadflag = 2;

### Глобальные функции

Глобальная функция представляет собой серию инструкций, у которой есть своё имя. При вызове функции выполняются эти инструкции. Вот одна из глобальных функций:

    [ Initialise; location = before_cottage; ];

Поскольку размер нашей функции мал, то мы записали её в одну строчку. Её можно отформатировать иначе:

    [ Initialise;
        location = before_cottage;
    ];

Часть `[ Initialise;` обозначает начало функции и включает её имя, по которому её можно вызвать. `];` — это конец функции. Между ними идёт тело функции, в котором содержатся инструкции. Вызвать функцию очень просто:

    Initialise();

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

> Заметьте, что мы описали функцию `Initialise`, но в игре её вызывали. На самом деле, функция вызывается при старте игры, самой библиотекой Информа.

### Локальные функции

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

    [; if (nest in branch) deadflag = 2; ]

Точнее, мы записали эту функцию как значение свойства:

    each_turn [; if (nest in branch) deadflag = 2; ],

Его можно переписать следующим образом:

    each_turn [;
        if (nest in branch)
        deadflag = 2;
    ],

Любые локальные функции задаются таким образом — как значение свойства объекта. Они привязаны к объекту и находятся в нём.

Локальные функции не вызываются по имени как глобальные функции. Имени у них нет, эти функции вызываются библиотекой автоматически в нужный момент игры. Момент определяется ролью, которую играет свойство. В нашем случае этим моментом будет конец каждого хода, когда игрок находится в той же комнате, где сук. Позже будет показаны другие примеры локальных функций а также способ вручную вызвать локальную функцию, при помощи синтаксиса `идентификатор.свойство()`, в нашем случае это было бы `branch.each_turn()`.

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