Сигналы - концепт, позволяющий реализовать парадигму реактивного программирования. Они позволяют реагировать на разные события из других частей кода, при этом объект вызывающий событие может ничего не знать про объектов следящих за его событиями.
В билде сигналы почти точная копия сигналов с /TG/station.
Основным API для работы с сигналами являются:
- Макросы
SEND_SIGNAL,SEND_GLOBAL_SIGNAL:
SEND_SIGNAL(target, sigtype, arguments...)
SEND_GLOBAL_SIGNAL(sigtype, arguments...)Они используются для отправки сигналов.
target - куда отправить сигнал, все слушатели target получат этот сигнал. Если сигнал происходит с текущим объектом - вместо target соответственно указываем src.
sigtype - некая строка, название сигнала.
arguments... - аргументы которые передаёт источник сигнала.
SEND_GLOBAL_SIGNAL идентична макросу выше, за исключением того что нельзя указать получателя - он уже указан и в качестве него испольуется подсистема SSelements.
- Функции
register_signal,register_global_signal:
/datum/proc/register_signal(datum/target, sig_type_or_types, proctype, override = FALSE)
/datum/proc/register_global_signal(sig_type_or_types, proctype, override = FALSE)Эти функции используются для подписывания на сигналы.
target - откуда ждать сигнала.
sig_type_or_types - название сигнала или лист названии.
proctype - путь функции которая будет вызвана при получении сигнала. Например: /datum/click_handler/proc/OnMobLogout
register_global_signal также как и макрос отличается от соседней функции только уже указанным получателем.
override - если FALSE (по-умолчанию) - сообщает об ошибке при повторной регистрации одного и того же сигнала.
- Функции
unregister_signal,unregister_global_signal:
/datum/proc/unregister_signal(datum/target, sig_type_or_types)
/datum/proc/unregister_global_signal(sig_type_or_types)Эти функции отписывают все функции которые должны быть вызваны при получении определённого сигнала.
target - источник сигнала, от которого необходимо отписать некоторые сигналы.
sig_type_or_types - типы сигнала или сигналов, которые необходимо прекратить слушать.
У нас есть некоторая консоль /obj/status_console которая следит за показателем здоровья каждого члена экипажа и при падении здоровья ниже 20 - должна проигрывать звук.
Самым наивным способом можно добавить проверку на количество здоровья в функцию Life (если она у нас ещё есть) или при получении урона, найти список всех консолей и проиграть им звук:
/mob/on_low_health()
for(var/obj/status_console/C in world)
playsound(C, ...)Есть второй способ с использованием подсистем (если они у нас есть) - каждое N количество тиков проверять здоровье всех членов экипажа и также проигрывать всем консолям звук:
for(var/mob/M in mob_list)
if(M.health < 20)
for(var/obj/status_console/C in world)
playsound(C, ...)При таком способе страдает производительность - приходится постоянно проверять всех мобов и существует неточность - между теми тиками когда мы проверяем всех мобов их здоровье может упасть ниже установленной нами границой и затем даже подняться. Это приводит к тому что иногда мы может не успеть обнаружить падение здоровья или найти с опозданием.
Есть третий способ с сигналами. Для начала нужно создать строку которая будет использована в качестве идентификатора нашего сигнала:
#define SIGNAL_LOW_HEALTH "low_health"Затем в функцию Life, или иную другую, мобов добавить проверку на здоровье и вызов сигнала:
if(health < 20)
SEND_GLOBAL_SIGNAL(SIGNAL_LOW_HEALTH, src)
SEND_SIGNAL(src, SIGNAL_LOW_HEALTH, src)Здесь мы послали в себя и глобально сигнал SIGNAL_LOW_HEALTH и аргументом также передаём себя.
В нашем случае глобальный сигнал будет оправданым - нашей консоли нужно знать обо всех раненых мобов (можно добавить проверку на его наличие в списке экипажа).
Если вы отправляете глобальный сигнал - также важно добавить и его локальную версию (target = src), иногда может будет удобнее слушать сигнал только конкретного объекта, а не всех.
Например: у нас есть сигнал "!death", который срабатывает при смерти моба. Он передаётся как глобально так и локально. Второй случай нам нужен для меча культистов - после удара по мобу, если он умрёт в течении нескольких секунд - необходимо выполнить некоторые действие. В этом случае при ударе по мобу мы должны слушать сигнал о смерти конкретного моба а не всех!
Мы добавили SEND_SIGNAL нашим мобам когда у них меньше 20 здоровья - теперь нужно добавить реакцию на этот сигнал.
Это можно сделать по-разному, в нашем случае можно начать слушать сигнал с самого создания консоли:
/obj/status_console/Initialize()
..()
register_global_signal(SIGNAL_LOW_HEALTH, /obj/status_console/proc/on_low_health)
/obj/status_console/Destroy()
unregister_global_signal(SIGNAL_LOW_HEALTH)
..()
/obj/status_console/proc/on_low_health(mob/M)
playsound(src, ...)Здесь мы создали функцию on_low_health(mob/M) (внимание на аргументы - mob/M - это тот объект который передаётся в SEND_SIGNAL после названия сигнала, в нашем случае здесь только один аргумент). Также мы подписались на глобальное событие SIGNAL_LOW_HEALTH и сообщили что обрабатывать этот сигнал будет функция /obj/status_console/proc/on_low_health.
Теперь в любой момент когда у любого моба здоровье будет меньше 20 - будет вызвана функция /obj/status_console/proc/on_low_health.
И не забывайте производить отписку от всех подписанных сигналов! В данном случае это можно сделать в Destroy.