diff --git a/code/__DEFINES/dcs/signals.dm b/code/__DEFINES/dcs/signals.dm index 1c2b00f2de0..7d828dbbfca 100644 --- a/code/__DEFINES/dcs/signals.dm +++ b/code/__DEFINES/dcs/signals.dm @@ -657,6 +657,13 @@ //#define COMPONENT_STOP_IMPLANTING (1<<0) //The name makes sense for both #define COMPONENT_DELETE_NEW_IMPLANT (1<<1) #define COMPONENT_DELETE_OLD_IMPLANT (1<<2) + +/// called on implants, after a successful implantation: (mob/living/target, mob/user, silent, force) +#define COMSIG_IMPLANT_IMPLANTED "implant_implanted" + +/// called on implants, after an implant has been removed: (mob/living/source, silent, special) +#define COMSIG_IMPLANT_REMOVED "implant_removed" + ///called on implants being implanted into someone with an uplink implant: (datum/component/uplink) #define COMSIG_IMPLANT_EXISTING_UPLINK "implant_uplink_exists" //This uses all return values of COMSIG_IMPLANT_OTHER diff --git a/code/game/objects/items/implants/implant.dm b/code/game/objects/items/implants/implant.dm index 51c72ad9627..412fe5f8d10 100644 --- a/code/game/objects/items/implants/implant.dm +++ b/code/game/objects/items/implants/implant.dm @@ -83,6 +83,7 @@ if(user) log_combat(user, target, "implanted", "\a [name]") + SEND_SIGNAL(src, COMSIG_IMPLANT_IMPLANTED, target, user, silent, force) return TRUE /obj/item/implant/proc/removed(mob/living/source, silent = FALSE, special = 0) @@ -96,7 +97,8 @@ var/mob/living/carbon/human/H = source H.sec_hud_set_implants() - return 1 + SEND_SIGNAL(src, COMSIG_IMPLANT_REMOVED, source, silent, special) + return TRUE /obj/item/implant/Destroy() if(imp_in) diff --git a/code/game/objects/items/implants/implant_deathrattle.dm b/code/game/objects/items/implants/implant_deathrattle.dm new file mode 100644 index 00000000000..f8788dc31bc --- /dev/null +++ b/code/game/objects/items/implants/implant_deathrattle.dm @@ -0,0 +1,77 @@ +/datum/deathrattle_group + var/name + var/list/implants = list() + +/datum/deathrattle_group/New(name) + if(name) + src.name = name + else + // Give the group a unique name for debugging, and possible future + // use for making custom linked groups. + src.name = "[rand(100,999)] [pick(GLOB.phonetic_alphabet)]" + +/* + * Proc called by new implant being added to the group. Listens for the + * implant being implanted, removed and destroyed. + * + * If implant is already implanted in a person, then trigger the implantation + * code. + */ +/datum/deathrattle_group/proc/register(obj/item/implant/deathrattle/implant) + if(implant in implants) + return + RegisterSignal(implant, COMSIG_IMPLANT_IMPLANTED, .proc/on_implant_implantation) + RegisterSignal(implant, COMSIG_IMPLANT_REMOVED, .proc/on_implant_removal) + RegisterSignal(implant, COMSIG_PARENT_QDELETING, .proc/on_implant_destruction) + + implants += implant + + if(implant.imp_in) + on_implant_implantation(implant.imp_in) + +/datum/deathrattle_group/proc/on_implant_implantation(obj/item/implant/implant, mob/living/target, mob/user, silent = FALSE, force = FALSE) + SIGNAL_HANDLER + + RegisterSignal(target, COMSIG_MOB_STATCHANGE, .proc/on_user_statchange) + +/datum/deathrattle_group/proc/on_implant_removal(obj/item/implant/implant, mob/living/source, silent = FALSE, special = 0) + SIGNAL_HANDLER + + UnregisterSignal(source, COMSIG_MOB_STATCHANGE) + +/datum/deathrattle_group/proc/on_implant_destruction(obj/item/implant/implant) + implants -= implant + +/datum/deathrattle_group/proc/on_user_statchange(mob/living/owner, new_stat) + SIGNAL_HANDLER + + if(new_stat != DEAD) + return + + var/name = owner.mind ? owner.mind.name : owner.real_name + var/area = get_area_name(get_turf(owner)) + + for(var/_implant in implants) + var/obj/item/implant/deathrattle/implant = _implant + + // Skip the unfortunate soul, and any unimplanted implants + if(implant.imp_in == owner || !implant.imp_in) + continue + + // Deliberately the same message framing as nanite message + ghost deathrattle + to_chat(implant.imp_in, "You hear a strange, robotic voice in your head... \"[name] has died at [area].\"") + +/obj/item/implant/deathrattle + name = "deathrattle implant" + desc = "Hope no one else dies, prepare for when they do." + + activated = FALSE + +/obj/item/implant/deathrattle/can_be_implanted_in(mob/living/target) + // Can be implanted in anything that's a mob. Syndicate cyborgs, talking fish, humans... + return TRUE + +/obj/item/implantcase/deathrattle + name = "implant case - 'Deathrattle'" + desc = "A glass case containing a deathrattle implant." + imp_type = /obj/item/implant/deathrattle diff --git a/code/game/objects/items/storage/uplink_kits.dm b/code/game/objects/items/storage/uplink_kits.dm index 2a9c7e2d106..20e0d921e7e 100644 --- a/code/game/objects/items/storage/uplink_kits.dm +++ b/code/game/objects/items/storage/uplink_kits.dm @@ -565,3 +565,21 @@ /obj/item/storage/box/syndie_kit/signaler/PopulateContents() for(var/i in 1 to 6) new /obj/item/assembly/signaler(src) + +/obj/item/storage/box/syndie_kit/imp_deathrattle + name = "deathrattle implant box" + desc = "Contains eight linked deathrattle implants." + +/obj/item/storage/box/syndie_kit/imp_deathrattle/PopulateContents() + new /obj/item/implanter(src) + + var/datum/deathrattle_group/group = new + + var/implants = list() + for(var/j in 1 to 8) + var/obj/item/implantcase/deathrattle/case = new (src) + implants += case.imp + + for(var/i in implants) + group.register(i) + desc += " The implants are registered to the \"[group.name]\" group." diff --git a/code/modules/uplink/uplink_items.dm b/code/modules/uplink/uplink_items.dm index 75b78cad796..df63da22652 100644 --- a/code/modules/uplink/uplink_items.dm +++ b/code/modules/uplink/uplink_items.dm @@ -1701,6 +1701,17 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) surplus = 0 include_modes = list(/datum/game_mode/nuclear) +/datum/uplink_item/implants/deathrattle + name = "Box of Deathrattle Implants" + desc = "A collection of implants (and one reusable implanter) that should be injected into the team. When one of the team \ + dies, all other implant holders recieve a mental message informing them of their teammates' name \ + and the location of their death. Unlike most implants, these are designed to be implanted \ + in any creature, biological or mechanical." + item = /obj/item/storage/box/syndie_kit/imp_deathrattle + cost = 4 + surplus = 0 + include_modes = list(/datum/game_mode/nuclear) + //Race-specific items /datum/uplink_item/race_restricted diff --git a/tgstation.dme b/tgstation.dme index 6049d6381cf..72fefa8e8c4 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -1156,6 +1156,7 @@ #include "code\game\objects\items\implants\implant_abductor.dm" #include "code\game\objects\items\implants\implant_chem.dm" #include "code\game\objects\items\implants\implant_clown.dm" +#include "code\game\objects\items\implants\implant_deathrattle.dm" #include "code\game\objects\items\implants\implant_exile.dm" #include "code\game\objects\items\implants\implant_explosive.dm" #include "code\game\objects\items\implants\implant_freedom.dm"