Различия

Показаны различия между двумя версиями страницы.

Ссылка на это сравнение

Предыдущая версия справа и слеваПредыдущая версия
Следующая версия
Предыдущая версия
scr:events [2024/08/20 12:03] – [Введение] rosarakscr:events [2024/08/29 03:01] (текущий) – [Описание для скриптов] rosarak
Строка 1: Строка 1:
 ====== Event Management ====== ====== Event Management ======
 ===== Введение ===== ===== Введение =====
-Ивенты (events) — основной способ взаимодействия [[scr:Entites|сущностей]]. Как и во многих других языках программирования, это система обратного вызова. Она позволяет зарегистрировать ивент с любым названием, при запуске которого вызываются и выполняются предписанные обработчики события (event handlers). Для дополнительной технической информации ознакомьтесь с [[https://storm-devs.github.io/storm-engine/kb/0003-scripting-functions.html|этой статьёй]].+Ивенты (events) — способ опционально выполнять код из разных методов. Как и во многих других языках программирования, это система обратного вызова. Она позволяет зарегистрировать ивент с любым названием, при запуске которого в порядке очереди (FIFO) вызываются и выполняются предписанные обработчики события (event handlers). За счёт использования ивентов код становится более гибким, децентрализованным, позволяя управлять его структурой из разных мест, оперативно внедряя код одних методов в другие. Для дополнительной технической информации ознакомьтесь с [[https://storm-devs.github.io/storm-engine/kb/0003-scripting-functions.html|официальной документацией]]. 
 + 
  
 ===== Описание для скриптов ==== ===== Описание для скриптов ====
-Вызов ивента в скриптах приводит к вызову всех подключённых к нему функций (event handlers - обработчики ивента). Функции-обработчики не имеют собственных аргументов((<color red>Уточнить</color>)) и получают всё необходимое через ''GetEventData''. Неформально это можно описать так: ивент кричит в пространство своё имя, и если хоть кто-то готов его выслушать, то ивент передаёт каждому слушателю сообщение, состоящее из аргументов.+Вызов ивента в скриптах приводит к вызову всех подключённых к нему функций - обработчиков ивента. Функции-обработчики не имеют собственных аргументов((<color red>Уточнить</color>)) и получают всё необходимое через ''GetEventData''. Неформально это можно описать так: ивент кричит в пространство своё имя, и если хоть кто-то готов его выслушать, то ивент передаёт каждому слушателю сообщение, состоящее из аргументов.
  
 Для мгновенного вызова используется функция ''Event'', для отложенного ''PostEvent'', они могут содержать множественное число аргументов. Для мгновенного вызова используется функция ''Event'', для отложенного ''PostEvent'', они могут содержать множественное число аргументов.
Строка 10: Строка 12:
 PostEvent(string eventName, int delay, string format, arg1, arg2, arg3, ...);</code> PostEvent(string eventName, int delay, string format, arg1, arg2, arg3, ...);</code>
   * ''eventName'' - название ивента   * ''eventName'' - название ивента
-  * ''delay'' - задержка в миллисекундах +  * ''delay'' - задержка в миллисекундах, чувствительна к ''timescale'', отсчёт продолжается и во время загрузочных экранов 
-  * ''format'' - строка из символов, обозначающих порядок и тип передаваемых аргументов:\\ ''l'' - int или bool, ''f'' - float, ''s'' - string, ''a'' или ''i''aref или object, ''e'' - ref+  * ''format'' - строка из символов, обозначающих порядок и тип передаваемых аргументов:\\ ''l'' - int или bool, ''f'' - float, ''s'' - string, ''a'' - aref, ''i'' - object, ''e'' - ref((**EvgAnat:** Мой опыт говорит, что ''a'' - это чётко aref; ''i'' - чётко основной объект aka экземпляр класса, к которому обращаются; ''e'' обязательно ставить, если хочешь возвращать значение; в остальном стабильнее со ссылкой на объект работает aref.)) 
 +e обязательно ставить, если хочешь возвращать значение, в остальном стабильнее со ссылкой на объект работает aref  
   * ''arg1'', ''arg2'', ''arg3'', ... - аргументы; типы соответствуют заданному в ''format'' порядку   * ''arg1'', ''arg2'', ''arg3'', ... - аргументы; типы соответствуют заданному в ''format'' порядку
-Формат и передаваемые аргументы необязательны, можно сделать ивент процедурой, не передавая ничего.\\ +Формат и передаваемые аргументы необязательны, можно сделать ивент процедурой, не передавая ничего.\\ **NB**: Отложенные ивенты активируются и после смены локации, но забываются после перезагрузки, если не успели отработать. Это отличает их от стандартных методов отложенного вызова функций (''DoQuestFunctionDelay'', ''LAi_MethodDelay'', ...), которые деактивируются в обоих случаях.\\ 
 Для постоянной привязки функции к соответствующему ивенту следует использовать: Для постоянной привязки функции к соответствующему ивенту следует использовать:
 <code c>#event_handler(string eventName, string functionName);</code> <code c>#event_handler(string eventName, string functionName);</code>
 В остальных случаях привязка и отключение производятся посредством: В остальных случаях привязка и отключение производятся посредством:
 <code c>SetEventHandler(eventName, functionName, int post); <code c>SetEventHandler(eventName, functionName, int post);
-DelEventHandler(eventName, functionName);</code> +SetEventHandlerConst(eventName, functionName, int post); 
-Аргумент ''post'' в случае единицы не даёт обработчику включиться сразу, только со следующего кадра((<color red>Уточнить</color>)).\\ До отключения через ''DelEventHandler'' обработчик всегда будет реагировать на вызов ивента, в том числе после смены локации и перезагрузки, если в сохранении он уже привязан. Отключить обработчик, указанный в ''<color green>#event_handler</color>'', таким способом нельзя.+DelEventHandler(eventName, functionName); 
 +DelEventHandlerConst(eventName, functionName);</code> 
 +Аргумент ''post'' в случае единицы не даёт обработчику включиться сразу, только со следующего кадра((<color red>Уточнить</color>)).\\ Обработчик, активированный через ''SetEventHandlerConst''будет реагировать на вызов ивента и после перезагрузки, пока не будет отключён через ''DelEventHandlerConst''.\\ При этом обработчик, указанный в ''<color green>#event_handler</color>'', убрать через ''DelEventHandler'' нельзя.
  
 Вместо собственных аргументов обработчики получают их по одному внутри тела с помощью ''GetEventData()'': Вместо собственных аргументов обработчики получают их по одному внутри тела с помощью ''GetEventData()'':
Строка 39: Строка 45:
 Всякий винконд (win condition - триггер) можно оформить в виде ивента и далее вместо того, чтобы плодить огромное количество атрибутов, достаточно в нужных местах включать и отключать (привязывать/отвязывать) соответствующие обработчики. Тем самым, в отличие от проверки через ''QuestsCheck'', проверка из ивента будет проходить только по целевым условиям. Добавление новых триггеров в этом случае осуществляется значительно проще, чем прописывание дополнительных кейсов в ''ProcessCondition'', а их выбор расширяется до огромного количества всевозможных параметров из движка. Если по квесту для вызова функции предполагается несколько условий, то её можно подключить к нескольким ивентам, проверяя выполнение остальных условий уже внутри самой функции. Когда все проверки пройдены, и соответствующие скрипты отработали, можно по надобности отключить обработчик и, возможно, вновь активировать его в другом месте. Всякий винконд (win condition - триггер) можно оформить в виде ивента и далее вместо того, чтобы плодить огромное количество атрибутов, достаточно в нужных местах включать и отключать (привязывать/отвязывать) соответствующие обработчики. Тем самым, в отличие от проверки через ''QuestsCheck'', проверка из ивента будет проходить только по целевым условиям. Добавление новых триггеров в этом случае осуществляется значительно проще, чем прописывание дополнительных кейсов в ''ProcessCondition'', а их выбор расширяется до огромного количества всевозможных параметров из движка. Если по квесту для вызова функции предполагается несколько условий, то её можно подключить к нескольким ивентам, проверяя выполнение остальных условий уже внутри самой функции. Когда все проверки пройдены, и соответствующие скрипты отработали, можно по надобности отключить обработчик и, возможно, вновь активировать его в другом месте.
  
-С учётом того, что все дополнительные иниты, вроде новых предметов, также можно писать внутри скриптов конкретного квеста, то, обзаведясь необходимым количеством ивент-кондов, можно компактно хранить все скрипты квеста в одной папке, то есть без использования совершенно чудовищных мегафайлов, вроде ''reaction_functions'' и ''quests_reaction''. При этом все диалоги могут подгружать актуальные квестовые ноды через ''loadsegment'', пробегая список активных квестов/событий. Загрузка уже существующего кейса (с названием, присутствующим в ''switch'' основного файла), как известно, не приводит к конфликтам. Трудности могут возникнуть, лишь если диалог чрезмерно перегружен конфликтующими проверками из разных квестов, где важна очерёдность, тогда либо в соответствующих операторах ветвления нужно указывать все условия по всем квестами, либо для таких редких ситуаций всё же выписывать условия в требуемом порядке.+С учётом того, что все дополнительные иниты, вроде новых предметов, также можно писать внутри скриптов конкретного квеста, то, обзаведясь необходимым количеством ивент-кондов, можно компактно хранить все скрипты квеста в одной папке, то есть без использования совершенно чудовищных мегафайлов, вроде ''reaction_functions'' и ''quests_reaction''. При этом диалоги могут подгружать актуальные квестовые ноды через ''loadsegment'', пробегая список активных квестов/событий.
  
 Тем самым, разместив скрипты каждого квеста в отдельной папке (полностью или почти полностью), мы приобретаем модульность, позволяющую проще ориентироваться в коде и добавляющую гибкость для модификации игры. Доведя эту систему до ума, можно будет добиться, например, чтобы для активации квеста было достаточно поместить его папку в соответствующий каталог, из которого всё необходимое будет подхватываться скриптами. Отдельные инит-функции могут парсить этот каталог при загрузке игры, а дополнительные правки вне папки квеста можно оформлять в виде инструкций для лончера, который, по сути, станет мод-органайзером. Тем самым, разместив скрипты каждого квеста в отдельной папке (полностью или почти полностью), мы приобретаем модульность, позволяющую проще ориентироваться в коде и добавляющую гибкость для модификации игры. Доведя эту систему до ума, можно будет добиться, например, чтобы для активации квеста было достаточно поместить его папку в соответствующий каталог, из которого всё необходимое будет подхватываться скриптами. Отдельные инит-функции могут парсить этот каталог при загрузке игры, а дополнительные правки вне папки квеста можно оформлять в виде инструкций для лончера, который, по сути, станет мод-органайзером.