diff --git a/code/modules/research/designs/medical_designs.dm b/code/modules/research/designs/medical_designs.dm index 4e95e9d4dfd..cbd05593a3c 100644 --- a/code/modules/research/designs/medical_designs.dm +++ b/code/modules/research/designs/medical_designs.dm @@ -811,6 +811,23 @@ ) departmental_flags = DEPARTMENT_BITFLAG_MEDICAL +/datum/design/cybernetic_heart/anomalock + name = "Voltaic combat cyberheart" + desc = "A cutting-edge cyberheart, originally designed for Nanotrasen killsquad usage but later declassified for normal research. Voltaic technology allows the heart to keep the body upright in dire circumstances, alongside redirecting anomalous flux energy to fully shield the user from shocks and electro-magnetic pulses. Does nothing without a flux anomaly core." + id = "cybernetic_heart_anomalock" + construction_time = 5 SECONDS + materials = list( + /datum/material/iron = SMALL_MATERIAL_AMOUNT * 5, + /datum/material/glass = SMALL_MATERIAL_AMOUNT * 5, + /datum/material/titanium = SHEET_MATERIAL_AMOUNT * 5, + /datum/material/diamond = SHEET_MATERIAL_AMOUNT, + ) + build_path = /obj/item/organ/internal/heart/cybernetic/anomalock + category = list( + RND_CATEGORY_EQUIPMENT + RND_SUBCATEGORY_EQUIPMENT_SCIENCE + ) + departmental_flags = DEPARTMENT_BITFLAG_SCIENCE + /datum/design/cybernetic_lungs name = "Basic Cybernetic Lungs" desc = "A basic pair of cybernetic lungs." diff --git a/code/modules/research/techweb/nodes/research_nodes.dm b/code/modules/research/techweb/nodes/research_nodes.dm index a0b9f8ceccd..82a64f8c823 100644 --- a/code/modules/research/techweb/nodes/research_nodes.dm +++ b/code/modules/research/techweb/nodes/research_nodes.dm @@ -87,6 +87,7 @@ prereq_ids = list(TECHWEB_NODE_ANOMALY_RESEARCH) design_ids = list( "bag_holding", + "cybernetic_heart_anomalock", "wormholeprojector", "gravitygun", "polymorph_belt" diff --git a/code/modules/surgery/organs/internal/heart/heart_anomalock.dm b/code/modules/surgery/organs/internal/heart/heart_anomalock.dm new file mode 100644 index 00000000000..3016d52912e --- /dev/null +++ b/code/modules/surgery/organs/internal/heart/heart_anomalock.dm @@ -0,0 +1,200 @@ +/*! + * Contains Voltaic Combat Cyberheart + */ +#define DOAFTER_IMPLANTING_HEART "implanting" + +/obj/item/organ/internal/heart/cybernetic/anomalock + name = "Voltaic Combat Cyberheart" + desc = "A cutting-edge cyberheart, originally designed for Nanotrasen killsquad usage but later declassified for normal research. Voltaic technology allows the heart to keep the body upright in dire circumstances, alongside redirecting anomalous flux energy to fully shield the user from shocks and electro-magnetic pulses. Requires a refined Flux core as a power source." + icon_state = "anomalock_heart" + + COOLDOWN_DECLARE(survival_cooldown) + ///Cooldown for the activation of the organ + var/survival_cooldown_time = 5 MINUTES + ///The lightning effect on our mob when the implant is active + var/mutable_appearance/lightning_overlay + ///how long the lightning lasts + var/lightning_timer + + //---- Anomaly core variables: + ///The core item the organ runs off. + var/obj/item/assembly/signaler/anomaly/core + ///Accepted types of anomaly cores. + var/required_anomaly = /obj/item/assembly/signaler/anomaly/flux + ///If this one starts with a core in. + var/prebuilt = FALSE + ///If the core is removable once socketed. + var/core_removable = TRUE + +/obj/item/organ/internal/heart/cybernetic/anomalock/on_mob_insert(mob/living/carbon/organ_owner, special, movement_flags) + . = ..() + if(!core) + return + add_lightning_overlay(30 SECONDS) + playsound(organ_owner, 'sound/items/eshield_recharge.ogg', 40) + organ_owner.AddElement(/datum/element/empprotection, EMP_PROTECT_SELF|EMP_PROTECT_CONTENTS) + organ_owner.apply_status_effect(/datum/status_effect/stabilized/yellow, src) + RegisterSignal(organ_owner, SIGNAL_ADDTRAIT(TRAIT_CRITICAL_CONDITION), PROC_REF(activate_survival)) + RegisterSignal(organ_owner, COMSIG_ATOM_EMP_ACT, PROC_REF(on_emp_act)) + +/obj/item/organ/internal/heart/cybernetic/anomalock/on_mob_remove(mob/living/carbon/organ_owner, special) + . = ..() + if(!core) + return + UnregisterSignal(organ_owner, SIGNAL_ADDTRAIT(TRAIT_CRITICAL_CONDITION)) + organ_owner.RemoveElement(/datum/element/empprotection, EMP_PROTECT_SELF|EMP_PROTECT_CONTENTS) + organ_owner.remove_status_effect(/datum/status_effect/stabilized/yellow) + tesla_zap(source = organ_owner, zap_range = 20, power = 2.5e5, cutoff = 1e3) + qdel(src) + +/obj/item/organ/internal/heart/cybernetic/anomalock/attack(mob/living/target_mob, mob/living/user, params) + if(target_mob != user || !istype(target_mob) || !core) + return ..() + + if(DOING_INTERACTION(user, DOAFTER_IMPLANTING_HEART)) + return + user.balloon_alert(user, "this will hurt...") + to_chat(user, span_userdanger("Black cyberveins tear your skin apart, pulling the heart into your ribcage. This feels unwise..")) + if(!do_after(user, 5 SECONDS, interaction_key = DOAFTER_IMPLANTING_HEART)) + return ..() + playsound(target_mob, 'sound/weapons/slice.ogg', 100, TRUE) + user.temporarilyRemoveItemFromInventory(src, TRUE) + Insert(user) + user.apply_damage(100, BRUTE, BODY_ZONE_CHEST) + user.emote("scream") + return TRUE + +/obj/item/organ/internal/heart/cybernetic/anomalock/proc/on_emp_act(severity) + SIGNAL_HANDLER + add_lightning_overlay(10 SECONDS) + +/obj/item/organ/internal/heart/cybernetic/anomalock/proc/add_lightning_overlay(time_to_last = 10 SECONDS) + if(lightning_overlay) + lightning_timer = addtimer(CALLBACK(src, PROC_REF(clear_lightning_overlay)), time_to_last, (TIMER_UNIQUE|TIMER_OVERRIDE)) + return + lightning_overlay = mutable_appearance(icon = 'icons/effects/effects.dmi', icon_state = "lightning") + owner.add_overlay(lightning_overlay) + lightning_timer = addtimer(CALLBACK(src, PROC_REF(clear_lightning_overlay)), time_to_last, (TIMER_UNIQUE|TIMER_OVERRIDE)) + +/obj/item/organ/internal/heart/cybernetic/anomalock/proc/clear_lightning_overlay() + owner.cut_overlay(lightning_overlay) + lightning_overlay = null + +/obj/item/organ/internal/heart/cybernetic/anomalock/attack_self(mob/user, modifiers) + . = ..() + if(.) + return + + if(core) + return attack(user, user, modifiers) + +/obj/item/organ/internal/heart/cybernetic/anomalock/on_life(seconds_per_tick, times_fired) + . = ..() + if(owner.blood_volume <= BLOOD_VOLUME_NORMAL) + owner.blood_volume += 5 * seconds_per_tick + if(owner.health <= owner.crit_threshold) + activate_survival(owner) + +///Does a few things to try to help you live whatever you may be going through +/obj/item/organ/internal/heart/cybernetic/anomalock/proc/activate_survival(mob/living/carbon/organ_owner) + if(!COOLDOWN_FINISHED(src, survival_cooldown)) + return + + organ_owner.apply_status_effect(/datum/status_effect/voltaic_overdrive) + add_lightning_overlay(30 SECONDS) + COOLDOWN_START(src, survival_cooldown, survival_cooldown_time) + addtimer(CALLBACK(src, PROC_REF(notify_cooldown), organ_owner), COOLDOWN_TIMELEFT(src, survival_cooldown)) + +///Alerts our owner that the organ is ready to do its thing again +/obj/item/organ/internal/heart/cybernetic/anomalock/proc/notify_cooldown(mob/living/carbon/organ_owner) + balloon_alert(organ_owner, "your heart strenghtens") + playsound(organ_owner, 'sound/items/eshield_recharge.ogg', 40) + +///Returns the mob we are implanted in so that the electricity effect doesn't runtime +/obj/item/organ/internal/heart/cybernetic/anomalock/proc/get_held_mob() + return owner + +/obj/item/organ/internal/heart/cybernetic/anomalock/attackby(obj/item/item, mob/living/user, params) + if(!istype(item.type, required_anomaly)) + return ..() + if(core) + balloon_alert(user, "core already in!") + return + if(!user.transferItemToLoc(item, src)) + return + core = item + balloon_alert(user, "core installed") + playsound(src, 'sound/machines/click.ogg', 30, TRUE) + add_organ_trait(TRAIT_SHOCKIMMUNE) + update_icon_state() + +/obj/item/organ/internal/heart/cybernetic/anomalock/screwdriver_act(mob/living/user, obj/item/tool) + . = ..() + if(!core) + balloon_alert(user, "no core!") + return + if(!core_removable) + balloon_alert(user, "can't remove core!") + return + balloon_alert(user, "removing core...") + if(!do_after(user, 3 SECONDS, target = src)) + balloon_alert(user, "interrupted!") + return + balloon_alert(user, "core removed") + core.forceMove(drop_location()) + if(Adjacent(user) && !issilicon(user)) + user.put_in_hands(core) + core = null + remove_organ_trait(TRAIT_SHOCKIMMUNE) + update_icon_state() + +/obj/item/organ/internal/heart/cybernetic/anomalock/update_icon_state() + . = ..() + icon_state = initial(icon_state) + (core ? "-core" : "") + +/obj/item/organ/internal/heart/cybernetic/anomalock/prebuilt/Initialize(mapload) + . = ..() + core = new /obj/item/assembly/signaler/anomaly/flux(src) + update_icon_state() + +/datum/status_effect/voltaic_overdrive + id = "voltaic_overdrive" + duration = 30 SECONDS + alert_type = /atom/movable/screen/alert/status_effect/anomalock_active + show_duration = TRUE + +/datum/status_effect/voltaic_overdrive/tick(seconds_between_ticks) + . = ..() + + if(owner.health <= owner.crit_threshold) + owner.heal_overall_damage(5, 5) + owner.adjustOxyLoss(-5) + owner.adjustToxLoss(-5) + +/datum/status_effect/voltaic_overdrive/on_apply() + . = ..() + owner.add_movespeed_mod_immunities(type, /datum/movespeed_modifier/damage_slowdown) + REMOVE_TRAIT(src, TRAIT_CRITICAL_CONDITION, STAT_TRAIT) + owner.reagents.add_reagent(/datum/reagent/medicine/coagulant, 5) + owner.add_filter("emp_shield", 2, outline_filter(1, "#639BFF")) + to_chat(owner, span_revendanger("You feel a burst of energy! It's do or die!")) + if(iscarbon(owner)) + var/mob/living/carbon/carbon_owner = owner + carbon_owner.gain_trauma(/datum/brain_trauma/special/tenacity, TRAUMA_RESILIENCE_ABSOLUTE) + +/datum/status_effect/voltaic_overdrive/on_remove() + . = ..() + owner.remove_movespeed_mod_immunities(type, /datum/movespeed_modifier/damage_slowdown) + owner.remove_filter("emp_shield") + owner.balloon_alert(owner, "your heart weakens") + if(iscarbon(owner)) + var/mob/living/carbon/carbon_owner = owner + carbon_owner.cure_trauma_type(/datum/brain_trauma/special/tenacity, TRAUMA_RESILIENCE_ABSOLUTE) + + +/atom/movable/screen/alert/status_effect/anomalock_active + name = "voltaic overdrive" + icon_state = "anomalock_heart" + desc = "Voltaic energy is flooding your muscles, keeping your body upright. You have 30 seconds before it falters!" + +#undef DOAFTER_IMPLANTING_HEART diff --git a/icons/effects/effects.dmi b/icons/effects/effects.dmi index 96c02aa91f9..245cb0a486e 100644 Binary files a/icons/effects/effects.dmi and b/icons/effects/effects.dmi differ diff --git a/icons/hud/screen_alert.dmi b/icons/hud/screen_alert.dmi index e56df056053..dda7d6cc635 100644 Binary files a/icons/hud/screen_alert.dmi and b/icons/hud/screen_alert.dmi differ diff --git a/icons/obj/medical/organs/organs.dmi b/icons/obj/medical/organs/organs.dmi index 0d04f7fae3c..62a45426e18 100644 Binary files a/icons/obj/medical/organs/organs.dmi and b/icons/obj/medical/organs/organs.dmi differ diff --git a/sound/items/eshield_recharge.ogg b/sound/items/eshield_recharge.ogg new file mode 100644 index 00000000000..747ad4563e5 Binary files /dev/null and b/sound/items/eshield_recharge.ogg differ diff --git a/tgstation.dme b/tgstation.dme index c61a30abce7..7a67c91773d 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -5939,6 +5939,7 @@ #include "code\modules\surgery\organs\internal\ears\_ears.dm" #include "code\modules\surgery\organs\internal\eyes\_eyes.dm" #include "code\modules\surgery\organs\internal\heart\_heart.dm" +#include "code\modules\surgery\organs\internal\heart\heart_anomalock.dm" #include "code\modules\surgery\organs\internal\heart\heart_ethereal.dm" #include "code\modules\surgery\organs\internal\liver\_liver.dm" #include "code\modules\surgery\organs\internal\liver\liver_golem.dm"