## Система событий

В метке (`label`) `rpg_loop` движок обрабатывает события и вызывает пользовательские метки,
благодаря которым игра может реагировать на вход в локацию, подбор предметов и т. д.  
Поведение по умолчанию:
1. Переменная `rpg_event` принимает значение (строку) из следующего списка возможных значений:
	* `"enter"` - вход в место (не в локацию, ВАЖНО!),
	* `"action"` - нажатие игроком "кнопки действия",
	* `"sit_down"` - игрок сел,
	* `"stand_up"` - встал,
	* `"take"` - подобрал предмет в инвентарь,
	* `"use"` - использовал подобранный предмет,
	* `"remove"` - выбросил/выложил предмет из инвентаря,
	* `"no_exit"` - попытка выйти через заблокированный выход.
2. Переменная `rpg_event_object` - имя объекта, если обрабатывается событие инвентаря (`take`, `use`, `remove`).
3. Берётся список меток вида `location_name__place_name`, для текущего места в текущей локации, причём
вместо части имени допускаются символы `?` (`1` любой символ) и `*` (`0` и более любых символов).  
Если персонаж не находится внутри какого-либо зарегистрированного места, метка будет иметь вид `location_name__unknown`.
Перед этим для добавления в список ищутся метки вроде `quest__location_name__place_name` для каждого активного
в данный момент квеста.
4. Из него удаляются несуществующие метки и повторы (остаются только последние среди повторов).
5. Проходимся по каждому элементу (начиная с начала) и вызываем метку с таким названием.
6. Если метка просто завершает своё выполнение, то считаем обработку события завершённым.
7. Если же переменная `rpg_event_stop` была установлена в `False` - переходим к следующей метке из того списка.

Также при входе в локацию с именем `some_location` выполняется метка `on__some_location` (если есть).  
Символы `?` и `*` в этом случае не являются заменителями других символов.

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

***

## Цикл обработки событий

Всё вышеописанное выполняется в метке с циклом событий `rpg_loop`,
она сама себя не вызовет, это нужно делать пользователю.  
Обычно это происходит в стартовой метке, сразу после пролога, который рассказывает некоторую предысторию,
а затем отдаёт игроку контроль над персонажем:
```renpy
label start:
	call my_prologue
	call rpg_loop
```

Этот цикл в `rpg_loop` бесконечен (`while True`), поэтому его вызов должен быть последним.  
Слово "цикл" употребляется здесь не просто так.
Именно из него вызываются метки для реакции на события, и именно к нему возвращается управление после их завершения.

Соответственно, вызов `jump` (вместо `call`) внутри любой метки не приведёт к возврату управления,
из-за чего игра будет считаться завершённой, и откроется главное меню.  
Не используйте `jump` в RPG (повтор для тех, кто невнимательно читал [RPG-Содержание](./RPG-Содержание)).

***

## Почему всё так сложно?  

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

Для начала, она позволяет реализовывать простые вещи простыми средствами (см. пример-1).

Далее. Было бы очень неудобно, к примеру, для каждого места с неоткрываемой дверью делать метки вида
`my_location__closed-1`, `my_location__closed-2` и т. д.,
особенно с учётом того, что по всем локациям таких мест может быть не один десяток.  
Вариант с названием `*__closed*` легко и непринуждённо решает эту проблему (см. пример-2).

Кроме того, эта система позволяет "уточнять" поведение.
Т. е. для примера из прошлого абзаца всё ещё есть возможность сделать метку `my_location__closed-2`, и именно ей
будет передано управление в начале (для места `closed-2`).
Она может, например, проверить какое-то условие, и если оно выполняется,
то запустить какую-то специфическую часть сценария, а иначе - вернуть управление более "общей" метке (см. пример-3).

Тут следует отметить, что чем больше символов нужно поставить на место символов-заменителей,
тем позже будет идти название этой метки в списке меток.
Благодаря этому в прошлом абзаце и происходит сначала вызов `my_location__closed-2`, и лишь затем `*__closed*`,
а не наоборот.

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

И наконец, она позволяет менять стандартное поведение на то, что нужно вам!  
Нужно, чтобы перед "стандартным" именем `location_name__place_name` добавлялся номер дня вроде `day5`? Легко.  
Добавить и время суток `day/night/some_other`? Запросто.  
Вызвать стандартную версию, если отсутствует специфическая метка? Уже в комплекте.  
Об этом повествует последний абзац, а пока несколько примеров.

***

## Пример-1

Простейший случай.

Игрок находится на локации `square`, в месте `path-1`.  
Игрок подбирает предмет `"key"`.  
Есть метка `square__path-1`, больше никаких похожих меток нет.  
Следовательно, она и будет вызвана (в случае её отсутствия никаких ошибок бы не было).

```renpy
label square__path-1:
	if rpg_event != "take" or rpg_event_object != "key":
		return
	
	"Что это за ключ?"
	window hide
```

***

## Пример-2

Обработка нескольких мест в одной метке.

Следующая метка будет подходить и для обработки места `closed` в локации `house`,
и для обработки места `closed-2` в локации `my_location`.

Это будет происходить из-за того, что и `house__closed`, и `my_location__closed-2` подходят под шаблон `*__closed*`.

```renpy
label *__closed*:
	me "Я нахожусь в локации <" + cur_location_name + ">, в месте <" + cur_place_name + ">."
	window hide
```

***

## Пример-3

Специализация и возврат к обобщенной метке.

```renpy
# специализированная метка для конкретного места в конкретной локации
label my_location__closed-2:
	if day_name == 'sunday':      # выходной день
		$ rpg_event_stop = False    # продолжение обработки события более общей меткой
		return                      # выход из текущей метки
	# и ничего не мешает вообще удалить это условие, если такое поведение не нужно
	
	"Обычная закрытая дверь среди десятка таких же."
	"Внезапно я услышал за ней голоса, и мне захотелось отойти от неё."
	window hide

# общая метка
label *__closed*:
	"Закрыто."
	window hide
```

***

## Изменение стандартного поведения

Для получения списка меток движок использует функцию `get_place_labels`.  
Вы можете заменить её реализацию на свою, чтобы модифицировать её поведение.  

Вот пример того, как можно добавить дни (о которых говорилось в 3 части данной статьи) к названиям меток
для большей их специализации:
```python
def get_place_labels():
	usual_label = cur_location_name + '__' + (cur_place_name or 'unknown')
	
	res = []
	for quest in get_started_quests():
		res.extend(get_glob_labels(quest + '__' + usual_label))
	res.extend(get_glob_labels('day' + str(day_num) + '__' + usual_label))
	res.extend(get_glob_labels(usual_label))
	return res
```

В начале мы просто определяем, как будет выглядеть название места, которое мы ищем.  
Это будет что-то вроде `square__path-1` или `house__unknown`.

Далее мы создаём список, который будет результатом работы этой функции, и добавляем в него:
* метки вида `quest__square__path-1` для каждого активного в данный момент квеста `quest`,
* метки вида `day5__square__path-1` для текущего дня, номер которого берём из переменной `day_num`
(это не стандартная переменная, вы сами должны создать и изменять её, если она вам нужна),
* метки вида `square__path-1`.

И в конце возвращаем этот список как результат работы этой функции.  
Напоминание:
* Названия несуществующих меток будут проигнорированы, это не будет считаться за ошибку.
* В случае повторов будут удалены все повторы, кроме последнего.
Благодаря этому та же метка `*__closed*` будет обработана в последнюю очередь, несмотря на то,
что будет попадать в список при каждом запросе меток вида `quest__square__closed-2` и `day4__square__closed-2`.

Теперь чуть подробнее о функции `get_glob_labels`.  
Это стандартная функция, которая возвращает список меток, под шаблон которых подходит переданный аргумент.  
Допустим, есть 5 меток: `house__closed*`, `house__street`, `square__closed-2`, `square__closed-*`, `*__closed*`.  
Тогда вызов `get_glob_labels('square__closed-2')` вернёт список `['square__closed-2', 'square__closed-*', '*__closed*']`,
причём обязательно в таком порядке, т. к. первый элемент совпадает полностью, у второго нужно заменить 1 символ,
а у последнего - 8.  
Остальные же 2 метки будут проигнорированы, т. к. они совсем не подходят под запрос.

***

[Далее ->](./Персонажи)  
[<- Назад](./Объекты-локаций)
