События - TrueCat17/Ren-Engine GitHub Wiki
В метке (label
) rpg_loop
движок обрабатывает события и вызывает пользовательские метки,
благодаря которым игра может реагировать на вход в локацию, подбор предметов и т. д.
Поведение по умолчанию:
- Переменная
rpg_event
принимает значение (строку) из следующего списка возможных значений:-
"enter"
- вход в место (не в локацию, ВАЖНО!), -
"action"
- нажатие игроком "кнопки действия", -
"sit_down"
- игрок сел, -
"stand_up"
- встал, -
"take"
- подобрал предмет в инвентарь, -
"use"
- использовал подобранный предмет, -
"remove"
- выбросил/выложил предмет из инвентаря, -
"no_exit"
- попытка выйти через заблокированный выход.
-
- Переменная
rpg_event_object
- имя объекта, если обрабатывается событие инвентаря (take
,use
,remove
). - Берётся список меток вида
location_name__place_name
, для текущего места в текущей локации, причём вместо части имени допускаются символы?
(1
любой символ) и*
(0
и более любых символов).
Если персонаж не находится внутри какого-либо зарегистрированного места, метка будет иметь видlocation_name__unknown
. Перед этим для добавления в список ищутся метки вродеquest__location_name__place_name
для каждого активного в данный момент квеста. - Из него удаляются несуществующие метки и повторы (остаются только последние среди повторов).
- Проходимся по каждому элементу (начиная с начала) и вызываем метку с таким названием.
- Если метка просто завершает своё выполнение, то считаем обработку события завершённым.
- Если же переменная
rpg_event_stop
была установлена вFalse
- переходим к следующей метке из того списка.
Также при входе в локацию с именем some_location
выполняется метка on__some_location
(если есть).
Символы ?
и *
в этом случае не являются заменителями других символов.
Примечание: если несколько мест пересекаются (в том числе и одно место внутри другого),
и персонаж во время события стоит на этом пересечении, то
выбор какого-то определённого места не гарантируется и зависит от фазы Луны.
Не полагайтесь на то, что если в данный момент выбирается что-то одно, то оно будет выбираться всегда.
Старайтесь вообще избегать пересечения областей у мест.
Всё вышеописанное выполняется в метке с циклом событий rpg_loop
,
она сама себя не вызовет, это нужно делать пользователю.
Обычно это происходит в стартовой метке, сразу после пролога, который рассказывает некоторую предысторию,
а затем отдаёт игроку контроль над персонажем:
label start:
call my_prologue
call rpg_loop
Этот цикл в rpg_loop
бесконечен (while True
), поэтому его вызов должен быть последним.
Слово "цикл" употребляется здесь не просто так.
Именно из него вызываются метки для реакции на события, и именно к нему возвращается управление после их завершения.
Соответственно, вызов jump
(вместо call
) внутри любой метки не приведёт к возврату управления,
из-за чего игра будет считаться завершённой, и откроется главное меню.
Не используйте jump
в 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
? Запросто.
Вызвать стандартную версию, если отсутствует специфическая метка? Уже в комплекте.
Об этом повествует последний абзац, а пока несколько примеров.
Простейший случай.
Игрок находится на локации square
, в месте path-1
.
Игрок подбирает предмет "key"
.
Есть метка square__path-1
, больше никаких похожих меток нет.
Следовательно, она и будет вызвана (в случае её отсутствия никаких ошибок бы не было).
label square__path-1:
if rpg_event != "take" or rpg_event_object != "key":
return
"Что это за ключ?"
window hide
Обработка нескольких мест в одной метке.
Следующая метка будет подходить и для обработки места closed
в локации house
,
и для обработки места closed-2
в локации my_location
.
Это будет происходить из-за того, что и house__closed
, и my_location__closed-2
подходят под шаблон *__closed*
.
label *__closed*:
me "Я нахожусь в локации <" + cur_location_name + ">, в месте <" + cur_place_name + ">."
window hide
Специализация и возврат к обобщенной метке.
# специализированная метка для конкретного места в конкретной локации
label my_location__closed-2:
if day_name == 'sunday': # выходной день
$ rpg_event_stop = False # продолжение обработки события более общей меткой
return # выход из текущей метки
# и ничего не мешает вообще удалить это условие, если такое поведение не нужно
"Обычная закрытая дверь среди десятка таких же."
"Внезапно я услышал за ней голоса, и мне захотелось отойти от неё."
window hide
# общая метка
label *__closed*:
"Закрыто."
window hide
Для получения списка меток движок использует функцию get_place_labels
.
Вы можете заменить её реализацию на свою, чтобы модифицировать её поведение.
Вот пример того, как можно добавить дни (о которых говорилось в 3 части данной статьи) к названиям меток для большей их специализации:
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 метки будут проигнорированы, т. к. они совсем не подходят под запрос.