From 5df58902519d327ab5ef985fe49693d367d750ee Mon Sep 17 00:00:00 2001
From: Qwertytoforty <52090703+Qwertytoforty@users.noreply.github.com>
Date: Thu, 28 Nov 2024 09:14:40 -0500
Subject: [PATCH] Reviving the revival of revivers reviving. Revival edition.
(#27436)
* reviver
* toxic
* more changes
* final changes
* I lied one more
* woe rename apon thee
* Update code/modules/surgery/organs/augments_internal.dm
Signed-off-by: DGamerL <108773801+DGamerL@users.noreply.github.com>
---------
Signed-off-by: DGamerL <108773801+DGamerL@users.noreply.github.com>
Co-authored-by: DGamerL <108773801+DGamerL@users.noreply.github.com>
---
code/datums/status_effects/buffs.dm | 17 +-
code/modules/mob/living/carbon/carbon_life.dm | 10 ++
.../hostile/mining/elites/pandora.dm | 2 +-
.../research/designs/medical_designs.dm | 2 +-
.../surgery/organs/augments_internal.dm | 169 ++++++++++++++----
code/modules/surgery/organs/body_egg.dm | 12 +-
code/modules/surgery/organs/heart.dm | 16 +-
.../organs/organ_datums/organ_datum.dm | 3 +
code/modules/surgery/organs/organ_internal.dm | 3 +
9 files changed, 184 insertions(+), 50 deletions(-)
diff --git a/code/datums/status_effects/buffs.dm b/code/datums/status_effects/buffs.dm
index 5d61ea6d71e..aa7cb7f23b6 100644
--- a/code/datums/status_effects/buffs.dm
+++ b/code/datums/status_effects/buffs.dm
@@ -673,14 +673,23 @@
/datum/status_effect/hope/tick()
if(owner.stat == DEAD || owner.health <= HEALTH_THRESHOLD_DEAD) // No dead healing, or healing in dead crit
return
+ var/heal_multiplier = 0
+ switch(owner.health)
+ if(50 to INFINITY)
+ heal_multiplier = 1
+ if(0 to 50)
+ heal_multiplier = 2
+ if(-50 to 0)
+ heal_multiplier = 3
+ if(-100 to -50)
+ heal_multiplier = 4
+ owner.adjustBruteLoss(-heal_multiplier)
+ owner.adjustFireLoss(-heal_multiplier)
+ owner.adjustOxyLoss(-heal_multiplier)
if(owner.health > 50)
if(prob(0.5))
hope_message()
return
- var/heal_multiplier = min(3, ((50 - owner.health) / 50 + 1)) // 1 hp at 50 health, 2 at 0, 3 at -50
- owner.adjustBruteLoss(-heal_multiplier * 0.5)
- owner.adjustFireLoss(-heal_multiplier * 0.5)
- owner.adjustOxyLoss(-heal_multiplier)
if(prob(heal_multiplier * 2))
hope_message()
diff --git a/code/modules/mob/living/carbon/carbon_life.dm b/code/modules/mob/living/carbon/carbon_life.dm
index ce2504424bf..642f043c994 100644
--- a/code/modules/mob/living/carbon/carbon_life.dm
+++ b/code/modules/mob/living/carbon/carbon_life.dm
@@ -10,6 +10,8 @@
if(stat != DEAD)
handle_organs()
+ else
+ handle_dead_organs()
//stuff in the stomach
if(LAZYLEN(stomach_contents))
@@ -205,6 +207,14 @@
var/datum/organ/datum_organ_var_name_idk = internal_organ_datums[organ_tag]
datum_organ_var_name_idk.on_life()
+/mob/living/carbon/proc/handle_dead_organs()
+ for(var/thing in internal_organs)
+ var/obj/item/organ/internal/O = thing
+ O.dead_process()
+ for(var/organ_tag in internal_organ_datums)
+ var/datum/organ/datum_organ_var_name_idk = internal_organ_datums[organ_tag]
+ datum_organ_var_name_idk.dead_process()
+
/mob/living/carbon/handle_diseases()
for(var/thing in viruses)
var/datum/disease/D = thing
diff --git a/code/modules/mob/living/simple_animal/hostile/mining/elites/pandora.dm b/code/modules/mob/living/simple_animal/hostile/mining/elites/pandora.dm
index c9450b56f57..5f8c3709735 100644
--- a/code/modules/mob/living/simple_animal/hostile/mining/elites/pandora.dm
+++ b/code/modules/mob/living/simple_animal/hostile/mining/elites/pandora.dm
@@ -198,7 +198,7 @@
friendly_fire_check = TRUE
-//Pandora's loot: Hope //Hope I know what to make it do
+//Pandora's loot: Hope
/obj/item/clothing/accessory/necklace/pandora_hope
name = "Hope"
desc = "Found at the bottom of Pandora. After all the evil was released, this was the only thing left inside."
diff --git a/code/modules/research/designs/medical_designs.dm b/code/modules/research/designs/medical_designs.dm
index 40a4ad403dd..c6b665d21cc 100644
--- a/code/modules/research/designs/medical_designs.dm
+++ b/code/modules/research/designs/medical_designs.dm
@@ -616,7 +616,7 @@
name = "Reviver Implant"
desc = "This implant will attempt to revive you if you lose consciousness. For the faint of heart!"
id = "ci-reviver"
- req_tech = list("materials" = 5, "programming" = 4, "biotech" = 5)
+ req_tech = list("materials" = 5, "programming" = 6, "biotech" = 7)
build_type = PROTOLATHE | MECHFAB
construction_time = 60
materials = list(MAT_METAL = 800, MAT_GLASS = 800, MAT_GOLD = 300, MAT_URANIUM = 500)
diff --git a/code/modules/surgery/organs/augments_internal.dm b/code/modules/surgery/organs/augments_internal.dm
index 914824891c1..90698ed3b91 100644
--- a/code/modules/surgery/organs/augments_internal.dm
+++ b/code/modules/surgery/organs/augments_internal.dm
@@ -663,14 +663,21 @@
/obj/item/organ/internal/cyberimp/chest/reviver
name = "Reviver implant"
- desc = "This implant will attempt to heal you out of critical condition. For the faint of heart!"
+ desc = "This implant will attempt to revive and heal you out of critical condition or death. For the faint of heart!"
icon_state = "reviver_implant"
implant_overlay = null
- origin_tech = "materials=5;programming=4;biotech=4"
+ origin_tech = "materials=5;programming=5;biotech=6"
slot = "heartdrive"
- var/revive_cost = 0
+ /// How long the implant will go on cooldown for once the user has exited crit, in seconds.
+ var/revive_cost = 0 SECONDS
+ /// Are we in the progress of healing the user?
var/reviving = FALSE
- var/cooldown = 0
+ /// Have we defibed someone this heal period? If so, do not heal past crit without an upgraded heart, as it is low on juice.
+ var/has_defibed = FALSE
+ /// How long we are on cooldown for
+ COOLDOWN_DECLARE(reviver_cooldown)
+ /// How long till we can try to defib again
+ COOLDOWN_DECLARE(defib_cooldown)
/obj/item/organ/internal/cyberimp/chest/reviver/hardened
name = "Hardened reviver implant"
@@ -680,52 +687,146 @@
. = ..()
desc += " The implant has been hardened. It is invulnerable to EMPs."
+/obj/item/organ/internal/cyberimp/chest/reviver/dead_process()
+ try_heal() // Allows implant to work even on dead people
+
/obj/item/organ/internal/cyberimp/chest/reviver/on_life()
- if(cooldown > world.time || owner.suiciding) // don't heal while you're in cooldown!
- return
+ try_heal()
+
+/obj/item/organ/internal/cyberimp/chest/reviver/proc/try_heal()
if(reviving)
- reviving = FALSE
- if(owner.health <= HEALTH_THRESHOLD_CRIT + 10) //We do not want to leave them on the end of crit constantly.
- addtimer(CALLBACK(src, PROC_REF(heal)), 30)
- reviving = TRUE
- if(owner.health > HEALTH_THRESHOLD_CRIT && owner.HasDisease(new /datum/disease/critical/shock(0)) && prob(15)) //We do not do an else, as we need them to cure shock inside this magic zone of 10 damage
- for(var/datum/disease/critical/shock/S in owner.viruses)
- S.cure()
- revive_cost += 150
- to_chat(owner, "You feel better.")
- if(!reviving)
- return
- cooldown = revive_cost + world.time
- revive_cost = 0
- reviving = TRUE
+ if(owner.stat != DEAD && reached_heal_threshold()) //Don't stop healing when they are dead.
+ COOLDOWN_START(src, reviver_cooldown, revive_cost)
+ reviving = FALSE
+ to_chat(owner, "Your reviver implant shuts down and starts recharging. It will be ready again in [DisplayTimeText(revive_cost)].")
+ else
+ addtimer(CALLBACK(src, PROC_REF(heal)), 3 SECONDS)
+ return
+ if(!COOLDOWN_FINISHED(src, reviver_cooldown) || owner.suiciding) // don't heal while you're in cooldown!
+ return
+ if(owner.health <= 0 || owner.stat == DEAD)
+ revive_cost = 0
+ reviving = TRUE
+ has_defibed = FALSE
+ to_chat(owner, "You feel a faint buzzing as your reviver implant starts patching your wounds...")
+ COOLDOWN_START(src, defib_cooldown, 8 SECONDS) // 5 seconds after heal proc delay
/obj/item/organ/internal/cyberimp/chest/reviver/proc/heal()
if(QDELETED(owner))
return
- if(prob(90) && owner.getOxyLoss())
+ // This is not on defib check so it doesnt revive IPCs either in a demon.
+ if(HAS_TRAIT(owner, TRAIT_UNREVIVABLE))
+ return
+ if(COOLDOWN_FINISHED(src, defib_cooldown))
+ revive_dead()
+ /// boolean that stands for if PHYSICAL damage being patched
+ var/body_damage_patched = FALSE
+ if(owner.getOxyLoss())
owner.adjustOxyLoss(-3)
- revive_cost += 5
- if(prob(75) && owner.getBruteLoss())
- owner.adjustBruteLoss(-2)
- revive_cost += 15
- if(prob(75) && owner.getFireLoss())
- owner.adjustFireLoss(-2)
- revive_cost += 15
- if(prob(40) && owner.getToxLoss())
+ revive_cost += 0.5 SECONDS
+ if(owner.getBruteLoss())
+ owner.adjustBruteLoss(-2, robotic = TRUE)
+ revive_cost += 4 SECONDS
+ body_damage_patched = TRUE
+ if(owner.getFireLoss())
+ owner.adjustFireLoss(-2, robotic = TRUE)
+ revive_cost += 4 SECONDS
+ body_damage_patched = TRUE
+ if(owner.getToxLoss())
owner.adjustToxLoss(-1)
- revive_cost += 25
+ revive_cost += 4 SECONDS
+ if(body_damage_patched && prob(25)) // healing is called every few seconds, not every tick
+ if(owner.stat != CONSCIOUS)
+ owner.visible_message("[owner]'s body [pick("twitches", "shifts", "shivers", "spasms", "vibrates")] a bit.", \
+ "You feel like something is patching your injured body.")
+ else // No twitching if awake.
+ to_chat(owner, "You feel like something is patching your injured body.")
+
+/obj/item/organ/internal/cyberimp/chest/reviver/proc/revive_dead()
+ if(!COOLDOWN_FINISHED(src, defib_cooldown) || owner.stat != DEAD || !can_defib())
+ return
+ var/mob/dead/observer/ghost = owner.get_ghost()
+ if(ghost)
+ if(!ghost.can_reenter_corpse)
+ return
+ to_chat(ghost, "You are being revived by [src]!")
+ window_flash(ghost.client)
+ SEND_SOUND(ghost, sound('sound/effects/genetics.ogg'))
+ COOLDOWN_START(src, defib_cooldown, 16 SECONDS)
+ playsound(get_turf(owner), 'sound/machines/defib_charge.ogg', 50, FALSE)
+ owner.grab_ghost()
+ owner.visible_message("[owner]'s body spasms violently!")
+ addtimer(CALLBACK(src, PROC_REF(zap_em)), 5 SECONDS)
+
+/obj/item/organ/internal/cyberimp/chest/reviver/proc/zap_em()
+ playsound(owner, 'sound/machines/defib_zap.ogg', 75, TRUE, -1)
+ // Inflict some brain damage scaling with time spent dead
+ var/time_dead = world.time - owner.timeofdeath
+ var/obj/item/organ/internal/brain/sponge = owner.get_int_organ(/obj/item/organ/internal/brain)
+ var/defib_time_brain_damage = min(100 * time_dead / BASE_DEFIB_TIME_LIMIT, 89) // 20 from 1 minute onward, +20 per minute up to 99 (10 organ damage flat added after)
+ if(time_dead > DEFIB_TIME_LOSS && defib_time_brain_damage > sponge.damage)
+ owner.setBrainLoss(defib_time_brain_damage)
+ // Turns out it takes some raw materials to heal you so much and defib you from inside. We took some material from your organs and iron from blood, hope you do not mind!
+ if(ishuman(owner))
+ var/mob/living/carbon/human/H = owner
+ for(var/obj/item/organ/internal/I in H.internal_organs)
+ I.receive_damage(10, TRUE)
+ H.blood_volume *= 0.85
+ revive_cost += 10 MINUTES // Additional 10 minutes cooldown after revival.
+ owner.SetLoseBreath(0) //Reset the heart attack losebreath of hell
+ owner.set_heartattack(FALSE)
+ owner.update_revive()
+ owner.KnockOut()
+ owner.Paralyse(10 SECONDS)
+ owner.emote("gasp")
+ owner.Jitter(20 SECONDS)
+ has_defibed = TRUE
+ SEND_SIGNAL(owner, COMSIG_LIVING_MINOR_SHOCK)
+ add_attack_logs(owner, owner, "Revived with [src]")
+
+/obj/item/organ/internal/cyberimp/chest/reviver/proc/can_defib()
+ if(!owner)
+ return FALSE
+ // They will get up on their own.
+ if(ismachineperson(owner))
+ return FALSE
+ // Slight tweak, revive if brute burn and oxygen loss *combined* are above 210, to avoid spam defibing at like, 200+ brute burn damage or 200 o2 loss
+ if(!owner.is_revivable() || owner.getBruteLoss() + owner.getFireLoss() + owner.getOxyLoss() >= 210 || HAS_TRAIT(owner, TRAIT_HUSK) || owner.blood_volume < BLOOD_VOLUME_SURVIVE)
+ return FALSE
+ // Let us break this on multiple lines
+ // Clone loss is a new addition outside defib code, to avoid endless revive hell.
+ // Avoid defibing with over 125 toxin loss as well. This also means toxin will delay someone from being revived *much* longer.
+ if(!owner.get_organ_slot("brain") || HAS_TRAIT(owner, TRAIT_FAKEDEATH) || HAS_TRAIT(owner, TRAIT_BADDNA) || owner.getCloneLoss() >= 180 || owner.getToxLoss() >= 125)
+ return FALSE
+ return TRUE
+
+/obj/item/organ/internal/cyberimp/chest/reviver/proc/reached_heal_threshold()
+ // By default, 0 health. Won't heal you out of shock alone, will need some medical attention still if you do not have meds!
+ // Will heal you out of crit though with 2 ticks of extra healing, due to callback.
+ if(ishuman(owner))
+ var/mob/living/carbon/human/H = owner
+ var/heal_threshold = 0
+ var/obj/item/organ/internal/heart/cybernetic/upgraded/U = H.get_int_organ(/obj/item/organ/internal/heart/cybernetic/upgraded)
+ if(U) // The heart assists in healing, and will heal you out of shock.
+ heal_threshold = 25
+ if(!has_defibed) // Not low on power, can heal you out of shock.
+ heal_threshold = 25
+ if(H.health > heal_threshold)
+ return TRUE
+ return FALSE
/obj/item/organ/internal/cyberimp/chest/reviver/emp_act(severity)
if(!owner || emp_proof)
return
if(reviving)
- revive_cost += 200
+ revive_cost += 40 SECONDS
+ COOLDOWN_START(src, defib_cooldown, 20 SECONDS)
else
- cooldown += 200
+ COOLDOWN_START(src, reviver_cooldown, 20 SECONDS)
if(ishuman(owner))
var/mob/living/carbon/human/H = owner
- if(H.stat != DEAD && prob(50 / severity))
+ if(H.stat != DEAD && prob(50 / severity) && H.can_heartattack())
H.set_heartattack(TRUE)
addtimer(CALLBACK(src, PROC_REF(undo_heart_attack)), 600 / severity)
@@ -734,6 +835,8 @@
if(!istype(H))
return
H.set_heartattack(FALSE)
+ H.adjustOxyLoss(-100) ///In the unlikely case that you are still alive, this should get you maybe to livable circumstances.
+ H.SetLoseBreath(0)
if(H.stat == CONSCIOUS)
to_chat(H, "You feel your heart beating again!")
diff --git a/code/modules/surgery/organs/body_egg.dm b/code/modules/surgery/organs/body_egg.dm
index 61ba45d63f2..f67c86d5725 100644
--- a/code/modules/surgery/organs/body_egg.dm
+++ b/code/modules/surgery/organs/body_egg.dm
@@ -14,12 +14,10 @@
..()
ADD_TRAIT(owner, TRAIT_XENO_HOST, TRAIT_GENERIC)
ADD_TRAIT(owner, TRAIT_XENO_IMMUNE, "xeno immune")
- START_PROCESSING(SSobj, src)
owner.med_hud_set_status()
INVOKE_ASYNC(src, PROC_REF(AddInfectionImages), owner)
/obj/item/organ/internal/body_egg/remove(mob/living/carbon/M, special = 0)
- STOP_PROCESSING(SSobj, src)
if(owner)
REMOVE_TRAIT(owner, TRAIT_XENO_HOST, TRAIT_GENERIC)
REMOVE_TRAIT(owner, TRAIT_XENO_IMMUNE, "xeno immune")
@@ -27,10 +25,14 @@
INVOKE_ASYNC(src, PROC_REF(RemoveInfectionImages), owner)
. = ..()
-/obj/item/organ/internal/body_egg/process()
- if(!owner)
+/obj/item/organ/internal/body_egg/on_life()
+ if(!(src in owner.internal_organs)) // I can only presume this is here for a reason, so not touching it.
+ remove(owner)
return
- if(!(src in owner.internal_organs))
+ egg_process()
+
+/obj/item/organ/internal/body_egg/dead_process()
+ if(!(src in owner.internal_organs)) // I can only presume this is here for a reason, so not touching it.
remove(owner)
return
egg_process()
diff --git a/code/modules/surgery/organs/heart.dm b/code/modules/surgery/organs/heart.dm
index 0b3e52c5578..c35fca1463d 100644
--- a/code/modules/surgery/organs/heart.dm
+++ b/code/modules/surgery/organs/heart.dm
@@ -237,19 +237,23 @@
if(prob(20) && emagged)
attempted_restart = TRUE
heart_datum.change_beating(TRUE) // Mötley Crüe - Kickstart My Heart
+ owner.adjustOxyLoss(-100)
+ owner.SetLoseBreath(0)
addtimer(CALLBACK(src, PROC_REF(message_to_owner), owner, "Your [name] returns to its normal rhythm!"), 3 SECONDS)
addtimer(CALLBACK(src, PROC_REF(recharge)), 20 SECONDS)
else if(prob(10))
attempted_restart = TRUE
heart_datum.change_beating(TRUE)
+ owner.adjustOxyLoss(-100)
+ owner.SetLoseBreath(0)
addtimer(CALLBACK(src, PROC_REF(message_to_owner), owner, "Your [name] returns to its normal rhythm!"), 3 SECONDS)
addtimer(CALLBACK(src, PROC_REF(recharge)), 30 SECONDS)
else
attempted_restart = TRUE
if(emagged)
- addtimer(CALLBACK(src, PROC_REF(recharge)), 20 SECONDS)
+ addtimer(CALLBACK(src, PROC_REF(recharge)), 10 SECONDS)
else
- addtimer(CALLBACK(src, PROC_REF(recharge)), 30 SECONDS)
+ addtimer(CALLBACK(src, PROC_REF(recharge)), 15 SECONDS)
addtimer(CALLBACK(src, PROC_REF(message_to_owner), owner, "Your [name] fails to return to its normal rhythm!"), 3 SECONDS)
if(owner.HasDisease(/datum/disease/critical/heart_failure))
@@ -259,19 +263,19 @@
for(var/datum/disease/critical/heart_failure/HF in owner.viruses)
HF.cure()
addtimer(CALLBACK(src, PROC_REF(message_to_owner), owner, "Your [name] returns to its normal rhythm!"), 30)
- addtimer(CALLBACK(src, PROC_REF(recharge)), 200)
+ addtimer(CALLBACK(src, PROC_REF(recharge)), 20 SECONDS)
else if(prob(25))
attempted_restart = TRUE
for(var/datum/disease/critical/heart_failure/HF in owner.viruses)
HF.cure()
addtimer(CALLBACK(src, PROC_REF(message_to_owner), owner, "Your [name] returns to its normal rhythm!"), 30)
- addtimer(CALLBACK(src, PROC_REF(recharge)), 200)
+ addtimer(CALLBACK(src, PROC_REF(recharge)), 20 SECONDS)
else
attempted_restart = TRUE
if(emagged)
- addtimer(CALLBACK(src, PROC_REF(recharge)), 200)
+ addtimer(CALLBACK(src, PROC_REF(recharge)), 10 SECONDS)
else
- addtimer(CALLBACK(src, PROC_REF(recharge)), 300)
+ addtimer(CALLBACK(src, PROC_REF(recharge)), 15 SECONDS)
addtimer(CALLBACK(src, PROC_REF(message_to_owner), owner, "Your [name] fails to return to its normal rhythm!"), 30)
diff --git a/code/modules/surgery/organs/organ_datums/organ_datum.dm b/code/modules/surgery/organs/organ_datums/organ_datum.dm
index a48df727a1e..13da88da41b 100644
--- a/code/modules/surgery/organs/organ_datums/organ_datum.dm
+++ b/code/modules/surgery/organs/organ_datums/organ_datum.dm
@@ -25,6 +25,9 @@
/datum/organ/proc/on_life()
return
+/datum/organ/proc/dead_process()
+ return
+
/// Only called when the organ is robotic AND is not emp proof. Return true to override default functions
/datum/organ/proc/on_successful_emp()
return FALSE
diff --git a/code/modules/surgery/organs/organ_internal.dm b/code/modules/surgery/organs/organ_internal.dm
index 4beddc40ac3..594cef18647 100644
--- a/code/modules/surgery/organs/organ_internal.dm
+++ b/code/modules/surgery/organs/organ_internal.dm
@@ -170,6 +170,9 @@
/obj/item/organ/internal/proc/on_life()
return
+/obj/item/organ/internal/proc/dead_process()
+ return
+
//abstract proc called by carbon/death()
/obj/item/organ/internal/proc/on_owner_death()
return