Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reviving the revival of revivers reviving. Revival edition. #27436

17 changes: 13 additions & 4 deletions code/datums/status_effects/buffs.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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()

Expand Down
10 changes: 10 additions & 0 deletions code/modules/mob/living/carbon/carbon_life.dm
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@

if(stat != DEAD)
handle_organs()
else
handle_dead_organs()

//stuff in the stomach
if(LAZYLEN(stomach_contents))
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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."
Expand Down
2 changes: 1 addition & 1 deletion code/modules/research/designs/medical_designs.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
169 changes: 136 additions & 33 deletions code/modules/surgery/organs/augments_internal.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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, "<span class='notice'>You feel better.</span>")
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, "<span class='notice'>Your reviver implant shuts down and starts recharging. It will be ready again in [DisplayTimeText(revive_cost)].</span>")
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, "<span class='notice'>You feel a faint buzzing as your reviver implant starts patching your wounds...</span>")
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("<span class='warning'>[owner]'s body [pick("twitches", "shifts", "shivers", "spasms", "vibrates")] a bit.</span>", \
"<span class='notice'>You feel like something is patching your injured body.</span>")
else // No twitching if awake.
to_chat(owner, "<span class='notice'>You feel like something is patching your injured body.</span>")

/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, "<span class='ghostalert'>You are being revived by [src]!</span>")
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("<span class='warning'>[owner]'s body spasms violently!</span>")
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)

Expand All @@ -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, "<span class='notice'>You feel your heart beating again!</span>")

Expand Down
12 changes: 7 additions & 5 deletions code/modules/surgery/organs/body_egg.dm
Original file line number Diff line number Diff line change
Expand Up @@ -14,23 +14,25 @@
..()
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")
owner.med_hud_set_status()
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()
Expand Down
16 changes: 10 additions & 6 deletions code/modules/surgery/organs/heart.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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, "<span class='warning'>Your [name] returns to its normal rhythm!</span>"), 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, "<span class='warning'>Your [name] returns to its normal rhythm!</span>"), 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, "<span class='warning'>Your [name] fails to return to its normal rhythm!</span>"), 3 SECONDS)

if(owner.HasDisease(/datum/disease/critical/heart_failure))
Expand All @@ -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, "<span class='warning'>Your [name] returns to its normal rhythm!</span>"), 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, "<span class='warning'>Your [name] returns to its normal rhythm!</span>"), 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, "<span class='warning'>Your [name] fails to return to its normal rhythm!</span>"), 30)


Expand Down
3 changes: 3 additions & 0 deletions code/modules/surgery/organs/organ_datums/organ_datum.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions code/modules/surgery/organs/organ_internal.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down