From de0b29dea68bdc1b193250667de7de12d2fc95cf Mon Sep 17 00:00:00 2001 From: coiax Date: Mon, 20 Feb 2017 22:13:54 +0000 Subject: [PATCH] Partially grown clones are horribly disfigured + cardiac arrest refactor (#24106) add: Refactors heart attack code, a cardiac arrest will knock someone unconscious and kill them very quickly. add: Adds corazone, an anti-heart attack drug, made by mixing 2 parts Phenol, 1 part Lithium. A person with corazone in their system will not suffer any negative effects from missing a heart. Use it during surgery. add: Abductor glands are now hearts, the abductor operation table now automatically injects corazone to prevent deaths during surgery. The gland will restart if it stops beating. fix: Cloning pods always know the name of the person they are cloning. add: You can swipe a medical ID card to eject someone from the cloning pod early. The cloning pod will announce this over the radio. del: Fresh clones have no organs or limbs, they gain them during the cloning process. Ejecting a clone too early is not recommended. Power loss will also eject a clone as before. del: An ejected clone will take damage from being at critical health very quickly upon ejection, rather than before, where a clone could be stable in critical for up to two minutes. add: Occupants of cloning pods do not interact with the air outside the pod. After @Cyberboss 's multi-pod cloning got merged, I scrapped the multi-pod stuff from my own work, and brought back the maiming. Early ejected clones being partially disfigured is a cool feature. This penalises (but does not completely remove) speed cloning, as there is a window where the clone can be ejected without missing any limbs or organs. Otherwise the clones will probably die due to lack of lungs or heart. Heart attacks are more lethal, there's an easy medicine for treating them, if chemistry have it on hand. Clones not reacting to the air around them is a buff to plasmamen (although they'll still burn to death once they're ejected in an oxygen atmosphere). --- code/__DEFINES/is_helpers.dm | 2 + .../miniantags/abduction/abduction_gear.dm | 54 ++++- .../miniantags/abduction/abduction_surgery.dm | 10 +- .../gamemodes/miniantags/abduction/gland.dm | 74 +++---- .../abduction/machinery/dispenser.dm | 4 +- .../abduction/machinery/experiment.dm | 4 +- code/game/machinery/cloning.dm | 155 +++++++------ .../game/objects/effects/spawners/lootdrop.dm | 17 ++ code/game/objects/items/devices/scanners.dm | 2 +- code/game/objects/items/weapons/defib.dm | 209 ++++++++++-------- code/modules/cargo/exports/organs.dm | 2 +- code/modules/mob/living/blood.dm | 14 +- code/modules/mob/living/carbon/human/death.dm | 1 - code/modules/mob/living/carbon/human/human.dm | 1 + .../mob/living/carbon/human/human_defense.dm | 5 +- .../mob/living/carbon/human/human_defines.dm | 2 - code/modules/mob/living/carbon/human/life.dm | 57 +++-- .../chemistry/reagents/medicine_reagents.dm | 8 + .../chemistry/reagents/toxin_reagents.dm | 6 +- .../reagents/chemistry/recipes/medicine.dm | 8 +- code/modules/surgery/organs/augments_chest.dm | 6 +- code/modules/surgery/organs/organ_internal.dm | 25 +-- 22 files changed, 392 insertions(+), 274 deletions(-) diff --git a/code/__DEFINES/is_helpers.dm b/code/__DEFINES/is_helpers.dm index 3316894b169..d8d80a8b1c5 100644 --- a/code/__DEFINES/is_helpers.dm +++ b/code/__DEFINES/is_helpers.dm @@ -128,6 +128,8 @@ var/list/static/global/pointed_types = typecacheof(list( #define is_pointed(W) (is_type_in_typecache(W, pointed_types)) +#define isbodypart(A) (istype(A, /obj/item/bodypart)) + //Assemblies #define isassembly(O) (istype(O, /obj/item/device/assembly)) diff --git a/code/game/gamemodes/miniantags/abduction/abduction_gear.dm b/code/game/gamemodes/miniantags/abduction/abduction_gear.dm index b7b05526a13..91e4bcda519 100644 --- a/code/game/gamemodes/miniantags/abduction/abduction_gear.dm +++ b/code/game/gamemodes/miniantags/abduction/abduction_gear.dm @@ -435,7 +435,7 @@ Congratulations! You are now trained for xenobiology research!"} species = "[H.dna.species.name]" if(L.mind && L.mind.changeling) species = "Changeling lifeform" - var/obj/item/organ/gland/temp = locate() in H.internal_organs + var/obj/item/organ/heart/gland/temp = locate() in H.internal_organs if(temp) helptext = "Experimental gland detected!" else @@ -531,15 +531,6 @@ Congratulations! You are now trained for xenobiology research!"} // Operating Table / Beds / Lockers -/obj/structure/table/optable/abductor - icon = 'icons/obj/abductor.dmi' - icon_state = "bed" - can_buckle = 1 - buckle_lying = 1 - flags = NODECONSTRUCT - resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF - - /obj/structure/bed/abductor name = "resting contraption" desc = "This looks similar to contraptions from earth. Could aliens be stealing our technology?" @@ -576,6 +567,17 @@ Congratulations! You are now trained for xenobiology research!"} new /obj/structure/table/abductor(src.loc) qdel(src) return + if(istype(I, /obj/item/stack/sheet/mineral/silver)) + var/obj/item/stack/sheet/P = I + if(P.get_amount() < 1) + user << "You need one sheet of silver to do \ + this!" + return + user << "You start adding [P] to [src]..." + if(do_after(user, 50, target = src)) + P.use(1) + new /obj/structure/table/optable/abductor(src.loc) + qdel(src) /obj/structure/table/abductor name = "alien table" @@ -589,6 +591,38 @@ Congratulations! You are now trained for xenobiology research!"} canSmoothWith = null frame = /obj/structure/table_frame/abductor +/obj/structure/table/optable/abductor + name = "alien operating table" + desc = "Used for alien medical procedures. The surface is covered in tiny spines." + frame = /obj/structure/table_frame/abductor + buildstack = /obj/item/stack/sheet/mineral/silver + framestack = /obj/item/stack/sheet/mineral/abductor + buildstackamount = 1 + framestackamount = 1 + icon = 'icons/obj/abductor.dmi' + icon_state = "bed" + can_buckle = 1 + buckle_lying = 1 + + var/static/list/injected_reagents = list("corazone") + +/obj/structure/table/optable/abductor/Crossed(atom/movable/AM) + . = ..() + if(iscarbon(AM)) + START_PROCESSING(SSobj, src) + AM << "You feel a series of tiny pricks!" + +/obj/structure/table/optable/abductor/process() + . = PROCESS_KILL + for(var/mob/living/carbon/C in get_turf(src)) + . = TRUE + for(var/chemical in injected_reagents) + if(C.reagents.get_reagent_amount(chemical) < 1) + C.reagents.add_reagent(chemical, 1) + +/obj/structure/table/optable/abductor/Destroy() + STOP_PROCESSING(SSobj, src) + . = ..() /obj/structure/closet/abductor name = "alien locker" diff --git a/code/game/gamemodes/miniantags/abduction/abduction_surgery.dm b/code/game/gamemodes/miniantags/abduction/abduction_surgery.dm index def0cd77e2f..e888e2884c5 100644 --- a/code/game/gamemodes/miniantags/abduction/abduction_surgery.dm +++ b/code/game/gamemodes/miniantags/abduction/abduction_surgery.dm @@ -33,15 +33,15 @@ if(IC) user.visible_message("[user] pulls [IC] out of [target]'s [target_zone]!", "You pull [IC] out of [target]'s [target_zone].") user.put_in_hands(IC) - IC.Remove(target, special = 1) + IC.Remove(target) return 1 else user << "You don't find anything in [target]'s [target_zone]!" - return 0 + return 1 /datum/surgery_step/gland_insert name = "insert gland" - implements = list(/obj/item/organ/gland = 100) + implements = list(/obj/item/organ/heart/gland = 100) time = 32 /datum/surgery_step/gland_insert/preop(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery) @@ -50,6 +50,6 @@ /datum/surgery_step/gland_insert/success(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery) user.visible_message("[user] inserts [tool] into [target].", "You insert [tool] into [target].") user.drop_item() - var/obj/item/organ/gland/gland = tool + var/obj/item/organ/heart/gland/gland = tool gland.Insert(target, 2) - return 1 \ No newline at end of file + return 1 diff --git a/code/game/gamemodes/miniantags/abduction/gland.dm b/code/game/gamemodes/miniantags/abduction/gland.dm index ead066e8ccd..15560a450ee 100644 --- a/code/game/gamemodes/miniantags/abduction/gland.dm +++ b/code/game/gamemodes/miniantags/abduction/gland.dm @@ -1,12 +1,11 @@ -/obj/item/organ/gland +/obj/item/organ/heart/gland name = "fleshy mass" desc = "A nausea-inducing hunk of twisting flesh and metal." icon = 'icons/obj/abductor.dmi' - zone = "chest" - slot = "gland" icon_state = "gland" status = ORGAN_ROBOTIC origin_tech = "materials=4;biotech=7;abductor=3" + beating = TRUE var/cooldown_low = 300 var/cooldown_high = 300 var/next_activation = 0 @@ -14,30 +13,33 @@ var/human_only = 0 var/active = 0 -/obj/item/organ/gland/proc/ownerCheck() +/obj/item/organ/heart/gland/proc/ownerCheck() if(ishuman(owner)) return 1 if(!human_only && iscarbon(owner)) return 1 return 0 -/obj/item/organ/gland/proc/Start() +/obj/item/organ/heart/gland/proc/Start() active = 1 next_activation = world.time + rand(cooldown_low,cooldown_high) -/obj/item/organ/gland/Remove(var/mob/living/carbon/M, special = 0) +/obj/item/organ/heart/gland/Remove(var/mob/living/carbon/M, special = 0) active = 0 if(initial(uses) == 1) uses = initial(uses) ..() -/obj/item/organ/gland/Insert(var/mob/living/carbon/M, special = 0) +/obj/item/organ/heart/gland/Insert(var/mob/living/carbon/M, special = 0) ..() if(special != 2 && uses) // Special 2 means abductor surgery Start() -/obj/item/organ/gland/on_life() +/obj/item/organ/heart/gland/on_life() + if(!beating) + // alien glands are immune to stopping. + beating = TRUE if(!active) return if(!ownerCheck()) @@ -50,28 +52,28 @@ if(!uses) active = 0 -/obj/item/organ/gland/proc/activate() +/obj/item/organ/heart/gland/proc/activate() return -/obj/item/organ/gland/heals +/obj/item/organ/heart/gland/heals cooldown_low = 200 cooldown_high = 400 uses = -1 icon_state = "health" -/obj/item/organ/gland/heals/activate() +/obj/item/organ/heart/gland/heals/activate() owner << "You feel curiously revitalized." owner.adjustBruteLoss(-20) owner.adjustOxyLoss(-20) owner.adjustFireLoss(-20) -/obj/item/organ/gland/slime +/obj/item/organ/heart/gland/slime cooldown_low = 600 cooldown_high = 1200 uses = -1 icon_state = "slime" -/obj/item/organ/gland/slime/activate() +/obj/item/organ/heart/gland/slime/activate() owner << "You feel nauseous!" owner.vomit(20) @@ -80,14 +82,14 @@ Slime.Friends = list(owner) Slime.Leader = owner -/obj/item/organ/gland/mindshock +/obj/item/organ/heart/gland/mindshock origin_tech = "materials=4;biotech=4;magnets=6;abductor=3" cooldown_low = 300 cooldown_high = 300 uses = -1 icon_state = "mindshock" -/obj/item/organ/gland/mindshock/activate() +/obj/item/organ/heart/gland/mindshock/activate() owner << "You get a headache." var/turf/T = get_turf(owner) @@ -97,38 +99,37 @@ H << "You hear a buzz in your head." H.confused += 20 -/obj/item/organ/gland/pop +/obj/item/organ/heart/gland/pop cooldown_low = 900 cooldown_high = 1800 uses = -1 human_only = 1 icon_state = "species" -/obj/item/organ/gland/pop/activate() +/obj/item/organ/heart/gland/pop/activate() owner << "You feel unlike yourself." var/species = pick(list(/datum/species/lizard,/datum/species/jelly/slime,/datum/species/pod,/datum/species/fly,/datum/species/jelly)) owner.set_species(species) -/obj/item/organ/gland/ventcrawling +/obj/item/organ/heart/gland/ventcrawling origin_tech = "materials=4;biotech=5;bluespace=4;abductor=3" cooldown_low = 1800 cooldown_high = 2400 uses = 1 icon_state = "vent" -/obj/item/organ/gland/ventcrawling/activate() +/obj/item/organ/heart/gland/ventcrawling/activate() owner << "You feel very stretchy." owner.ventcrawler = VENTCRAWLER_ALWAYS - return -/obj/item/organ/gland/viral +/obj/item/organ/heart/gland/viral cooldown_low = 1800 cooldown_high = 2400 uses = 1 icon_state = "viral" -/obj/item/organ/gland/viral/activate() +/obj/item/organ/heart/gland/viral/activate() owner << "You feel sick." var/virus_type = pick(/datum/disease/beesease, /datum/disease/brainrot, /datum/disease/magnitis) var/datum/disease/D = new virus_type() @@ -139,48 +140,47 @@ owner.med_hud_set_status() -/obj/item/organ/gland/emp //TODO : Replace with something more interesting +/obj/item/organ/heart/gland/emp //TODO : Replace with something more interesting origin_tech = "materials=4;biotech=4;magnets=6;abductor=3" cooldown_low = 900 cooldown_high = 1600 uses = 10 icon_state = "emp" -/obj/item/organ/gland/emp/activate() +/obj/item/organ/heart/gland/emp/activate() owner << "You feel a spike of pain in your head." empulse(get_turf(owner), 2, 5, 1) -/obj/item/organ/gland/spiderman +/obj/item/organ/heart/gland/spiderman cooldown_low = 450 cooldown_high = 900 uses = 10 icon_state = "spider" -/obj/item/organ/gland/spiderman/activate() +/obj/item/organ/heart/gland/spiderman/activate() owner << "You feel something crawling in your skin." owner.faction |= "spiders" new /obj/structure/spider/spiderling(owner.loc) -/obj/item/organ/gland/egg +/obj/item/organ/heart/gland/egg cooldown_low = 300 cooldown_high = 400 uses = -1 icon_state = "egg" -/obj/item/organ/gland/egg/activate() +/obj/item/organ/heart/gland/egg/activate() owner << "You lay an egg!" var/obj/item/weapon/reagent_containers/food/snacks/egg/egg = new(owner.loc) egg.reagents.add_reagent("sacid",20) egg.desc += " It smells bad." -/obj/item/organ/gland/bloody +/obj/item/organ/heart/gland/bloody cooldown_low = 200 cooldown_high = 400 uses = -1 -/obj/item/organ/gland/bloody/activate() - owner.adjustBruteLoss(15) - +/obj/item/organ/heart/gland/bloody/activate() + owner.blood_volume -= 20 owner.visible_message("[owner]'s skin erupts with blood!",\ "Blood pours from your skin!") @@ -189,13 +189,13 @@ for(var/mob/living/carbon/human/H in oview(3,owner)) //Blood decals for simple animals would be neat. aka Carp with blood on it. H.add_mob_blood(owner) -/obj/item/organ/gland/bodysnatch +/obj/item/organ/heart/gland/bodysnatch cooldown_low = 600 cooldown_high = 600 human_only = 1 uses = 1 -/obj/item/organ/gland/bodysnatch/activate() +/obj/item/organ/heart/gland/bodysnatch/activate() owner << "You feel something moving around inside you..." //spawn cocoon with clone greytide snpc inside if(ishuman(owner)) @@ -203,7 +203,6 @@ C.Copy(owner) C.Start() owner.gib() - return /obj/structure/spider/cocoon/abductor name = "slimy cocoon" @@ -240,13 +239,13 @@ M.loc = src.loc qdel(src) -/obj/item/organ/gland/plasma +/obj/item/organ/heart/gland/plasma cooldown_low = 1200 cooldown_high = 1800 origin_tech = "materials=4;biotech=4;plasmatech=6;abductor=3" uses = -1 -/obj/item/organ/gland/plasma/activate() +/obj/item/organ/heart/gland/plasma/activate() owner << "You feel bloated." sleep(150) if(!owner) return @@ -258,4 +257,3 @@ if(istype(T)) T.atmos_spawn_air("plasma=50;TEMP=[T20C]") owner.vomit() - return diff --git a/code/game/gamemodes/miniantags/abduction/machinery/dispenser.dm b/code/game/gamemodes/miniantags/abduction/machinery/dispenser.dm index f4c07f98343..5894c4d98b5 100644 --- a/code/game/gamemodes/miniantags/abduction/machinery/dispenser.dm +++ b/code/game/gamemodes/miniantags/abduction/machinery/dispenser.dm @@ -15,7 +15,7 @@ /obj/machinery/abductor/gland_dispenser/New() ..() - gland_types = subtypesof(/obj/item/organ/gland) + gland_types = subtypesof(/obj/item/organ/heart/gland) gland_types = shuffle(gland_types) gland_colors = new/list(gland_types.len) amounts = new/list(gland_types.len) @@ -60,7 +60,7 @@ return /obj/machinery/abductor/gland_dispenser/attackby(obj/item/weapon/W, mob/user, params) - if(istype(W, /obj/item/organ/gland)) + if(istype(W, /obj/item/organ/heart/gland)) if(!user.drop_item()) return W.loc = src diff --git a/code/game/gamemodes/miniantags/abduction/machinery/experiment.dm b/code/game/gamemodes/miniantags/abduction/machinery/experiment.dm index 265838d15a3..c82a72631ec 100644 --- a/code/game/gamemodes/miniantags/abduction/machinery/experiment.dm +++ b/code/game/gamemodes/miniantags/abduction/machinery/experiment.dm @@ -160,7 +160,7 @@ if(H.stat == DEAD) say("Specimen deceased - please provide fresh sample.") return "Specimen deceased." - var/obj/item/organ/gland/GlandTest = locate() in H.internal_organs + var/obj/item/organ/heart/gland/GlandTest = locate() in H.internal_organs if(!GlandTest) say("Experimental dissection not detected!") return "No glands detected!" @@ -185,7 +185,7 @@ H.mind.announce_objectives() ticker.mode.update_abductor_icons_added(H.mind) - for(var/obj/item/organ/gland/G in H.internal_organs) + for(var/obj/item/organ/heart/gland/G in H.internal_organs) G.Start() point_reward++ if(point_reward > 0) diff --git a/code/game/machinery/cloning.dm b/code/game/machinery/cloning.dm index 5441fc9c481..81de98c0aa2 100644 --- a/code/game/machinery/cloning.dm +++ b/code/game/machinery/cloning.dm @@ -4,6 +4,7 @@ //Potential replacement for genetics revives or something I dunno (?) #define CLONE_INITIAL_DAMAGE 190 //Clones in clonepods start with 190 cloneloss damage and 190 brainloss damage, thats just logical +#define MINIMUM_HEAL_LEVEL 40 #define SPEAK(message) radio.talk_into(src, message, radio_channel, get_spans()) @@ -16,12 +17,10 @@ icon_state = "pod_0" req_access = list(access_cloning) //For premature unlocking. verb_say = "states" - var/heal_level = 90 //The clone is released once its health reaches this level. - var/locked = FALSE + var/heal_level //The clone is released once its health reaches this level. var/obj/machinery/computer/cloning/connected = null //So we remember the connected clone machine. var/mess = FALSE //Need to clean out it if it's full of exploded clone. var/attempting = FALSE //One clone attempt at a time thanks - var/eject_wait = FALSE //Don't eject them as soon as they are created fuckkk var/speed_coeff var/efficiency @@ -34,6 +33,13 @@ var/obj/effect/countdown/clonepod/countdown + var/list/unattached_flesh + var/flesh_number = 0 + + // The "brine" is the reagents that are automatically added in small + // amounts to the occupant. + var/static/list/brine_types = list("salbutamol", "bicaridine", "corazone") + /obj/machinery/clonepod/New() ..() var/obj/item/weapon/circuitboard/machine/B = new /obj/item/weapon/circuitboard/machine/clonepod(null) @@ -55,6 +61,10 @@ countdown = null if(connected) connected.DetachCloner(src) + for(var/i in unattached_flesh) + qdel(i) + LAZYCLEARLIST(unattached_flesh) + unattached_flesh = null . = ..() /obj/machinery/clonepod/RefreshParts() @@ -65,6 +75,8 @@ for(var/obj/item/weapon/stock_parts/manipulator/P in component_parts) speed_coeff += P.rating heal_level = (efficiency * 15) + 10 + if(heal_level < MINIMUM_HEAL_LEVEL) + heal_level = MINIMUM_HEAL_LEVEL if(heal_level > 100) heal_level = 100 @@ -137,18 +149,14 @@ return FALSE if(clonemind.damnation_type) //Can't clone the damned. INVOKE_ASYNC(src, .proc/horrifyingsound) - mess = 1 + mess = TRUE icon_state = "pod_g" update_icon() return FALSE attempting = TRUE //One at a time!! - locked = TRUE countdown.start() - eject_wait = TRUE - addtimer(CALLBACK(src, .proc/wait_complete), 30) - var/mob/living/carbon/human/H = new /mob/living/carbon/human(src) H.hardset_dna(ui, se, H.real_name, null, mrace, features) @@ -172,8 +180,8 @@ icon_state = "pod_1" //Get the clone body ready - H.setCloneLoss(CLONE_INITIAL_DAMAGE) //Yeah, clones start with very low health, not with random, because why would they start with random health - H.setBrainLoss(CLONE_INITIAL_DAMAGE) + maim_clone(H) + check_brine() // put in chemicals NOW to stop death via cardiac arrest H.Paralyse(4) if(grab_ghost_when == CLONER_FRESH_CLONE) @@ -196,24 +204,19 @@ attempting = FALSE return TRUE -/obj/machinery/clonepod/proc/wait_complete() - eject_wait = FALSE - //Grow clones to maturity then kick them out. FREELOADERS /obj/machinery/clonepod/process() if(!is_operational()) //Autoeject if power is lost if (occupant) - locked = FALSE go_out() connected_message("Clone Ejected: Loss of power.") else if((occupant) && (occupant.loc == src)) if((occupant.stat == DEAD) || (occupant.suiciding) || occupant.hellbound) //Autoeject corpses and suiciding dudes. connected_message("Clone Rejected: Deceased.") - SPEAK("The cloning of [occupant.real_name] has been \ + SPEAK("The cloning of [occupant.real_name] has been \ aborted due to unrecoverable tissue failure.") - locked = FALSE go_out() else if(occupant.cloneloss > (100 - heal_level)) @@ -221,37 +224,43 @@ //Slowly get that clone healed and finished. occupant.adjustCloneLoss(-((speed_coeff/2) * config.damage_multiplier)) + var/progress = CLONE_INITIAL_DAMAGE - occupant.getCloneLoss() + // To avoid the default cloner making incomplete clones + progress += (100 - MINIMUM_HEAL_LEVEL) + var/milestone = CLONE_INITIAL_DAMAGE / flesh_number + var/installed = flesh_number - unattached_flesh.len + + if((progress / milestone) >= installed) + // attach some flesh + var/obj/item/I = pick_n_take(unattached_flesh) + if(isorgan(I)) + var/obj/item/organ/O = I + O.Insert(occupant) + else if(isbodypart(I)) + var/obj/item/bodypart/BP = I + BP.attach_limb(occupant) //Premature clones may have brain damage. occupant.adjustBrainLoss(-((speed_coeff/2) * config.damage_multiplier)) - //So clones don't die of oxyloss in a running pod. - if (occupant.reagents.get_reagent_amount("salbutamol") < 30) - occupant.reagents.add_reagent("salbutamol", 60) - // NOBREATH species will take brute damage in crit instead - // so heal that as well - if(occupant.reagents.get_reagent_amount("bicaridine") < 5) - occupant.reagents.add_reagent("bicaridine", 10) + check_brine() use_power(7500) //This might need tweaking. - else if((occupant.cloneloss <= (100 - heal_level)) && (!eject_wait)) + else if((occupant.cloneloss <= (100 - heal_level))) connected_message("Cloning Process Complete.") - SPEAK("The cloning cycle of [occupant] is complete.") - locked = FALSE + SPEAK("The cloning cycle of [occupant.real_name] is complete.") go_out() else if ((!occupant) || (occupant.loc != src)) occupant = null - if (locked) - locked = FALSE if (!mess && !panel_open) icon_state = "pod_0" use_power(200) //Let's unlock this early I guess. Might be too early, needs tweaking. /obj/machinery/clonepod/attackby(obj/item/weapon/W, mob/user, params) - if(!(occupant || mess || locked)) + if(!(occupant || mess)) if(default_deconstruction_screwdriver(user, "[icon_state]_maintenance", "[initial(icon_state)]",W)) return @@ -263,7 +272,7 @@ if(istype(W,/obj/item/device/multitool)) var/obj/item/device/multitool/P = W - + if(istype(P.buffer, /obj/machinery/computer/cloning)) if(get_area(P.buffer) != get_area(src)) user << "-% Cannot link machines across power zones. Buffer cleared %-" @@ -278,28 +287,27 @@ P.buffer = src user << "-% Successfully stored \ref[P.buffer] [P.buffer.name] in buffer %-" return - - if (W.GetID()) - if (!check_access(W)) + + if(W.GetID()) + if(!check_access(W)) user << "Access Denied." return - if (!locked || !occupant) - return - if (occupant.health < -20 && occupant.stat != DEAD) - user << "Access Refused. Patient status still unstable." + if(!(occupant || mess)) + user << "Error: Pod has no occupant." return else - locked = FALSE - user << "System unlocked." + connected_message("Authorized Ejection") + SPEAK("An authorized ejection of [occupant.real_name] has occurred.") + user << "You force an emergency ejection. " + go_out() else return ..() /obj/machinery/clonepod/emag_act(mob/user) - if (isnull(occupant)) + if(!occupant) return - user << "You force an emergency ejection." - locked = FALSE - go_out() + user << "You corrupt the genetic compiler." + malfunction() //Put messages in the connected computer's temp var for display. /obj/machinery/clonepod/proc/connected_message(message) @@ -312,31 +320,17 @@ connected.updateUsrDialog() return TRUE -/obj/machinery/clonepod/verb/eject() - set name = "Eject Cloner" - set category = "Object" - set src in oview(1) - - if(!usr) - return - if(usr.stat || !usr.canmove || usr.restrained()) - return - go_out() - add_fingerprint(usr) - /obj/machinery/clonepod/proc/go_out() - if (locked) - return countdown.stop() - if (mess) //Clean that mess and dump those gibs! + if(mess) //Clean that mess and dump those gibs! mess = FALSE new /obj/effect/gibspawner/generic(loc) audible_message("You hear a splat.") icon_state = "pod_0" return - if (!occupant) + if(!occupant) return if(grab_ghost_when == CLONER_MATURE_CLONE) @@ -349,7 +343,6 @@ var/turf/T = get_turf(src) occupant.forceMove(T) icon_state = "pod_0" - eject_wait = FALSE //If it's still set somehow. occupant.domutcheck(1) //Waiting until they're out before possible monkeyizing. The 1 argument forces powers to manifest. occupant = null @@ -383,14 +376,12 @@ /obj/machinery/clonepod/ex_act(severity, target) ..() if(!QDELETED(src)) - locked = FALSE go_out() /obj/machinery/clonepod/handle_atom_del(atom/A) if(A == occupant) occupant = null - locked = FALSE - go_out() + countdown.stop() /obj/machinery/clonepod/proc/horrifyingsound() for(var/i in 1 to 5) @@ -401,10 +392,45 @@ /obj/machinery/clonepod/deconstruct(disassembled = TRUE) if(occupant) - locked = FALSE go_out() ..() +/obj/machinery/clonepod/proc/maim_clone(mob/living/carbon/human/H) + if(!unattached_flesh) + unattached_flesh = list() + else + for(var/fl in unattached_flesh) + qdel(fl) + unattached_flesh.Cut() + + H.setCloneLoss(CLONE_INITIAL_DAMAGE) //Yeah, clones start with very low health, not with random, because why would they start with random health + H.setBrainLoss(CLONE_INITIAL_DAMAGE) + // In addition to being cellularly damaged and having barely any + // brain function, they also have no limbs or internal organs. + var/static/list/zones = list("r_arm", "l_arm", "r_leg", "l_leg") + for(var/zone in zones) + var/obj/item/bodypart/BP = H.get_bodypart(zone) + BP.drop_limb() + BP.forceMove(src) + unattached_flesh += BP + + for(var/o in H.internal_organs) + var/obj/item/organ/organ = o + if(!istype(organ) || organ.vital) + continue + organ.Remove(H, special=TRUE) + organ.forceMove(src) + unattached_flesh += organ + + flesh_number = unattached_flesh.len + +/obj/machinery/clonepod/proc/check_brine() + // Clones are in a pickled bath of mild chemicals, keeping + // them alive, despite their lack of internal organs + for(var/bt in brine_types) + if(occupant.reagents.get_reagent_amount(bt) < 1) + occupant.reagents.add_reagent(bt, 1) + /* * Diskette Box @@ -449,3 +475,4 @@ #undef CLONE_INITIAL_DAMAGE #undef SPEAK +#undef MINIMUM_HEAL_LEVEL diff --git a/code/game/objects/effects/spawners/lootdrop.dm b/code/game/objects/effects/spawners/lootdrop.dm index 02706abf4d4..d8ec9e29218 100644 --- a/code/game/objects/effects/spawners/lootdrop.dm +++ b/code/game/objects/effects/spawners/lootdrop.dm @@ -169,3 +169,20 @@ /obj/structure/closet/crate/secure/loot = 20, "" = 80 ) + +/obj/effect/spawner/lootdrop/organ_spawner + name = "organ spawner" + loot = list( + /obj/item/organ/heart/gland/bloody = 7, + /obj/item/organ/heart/gland/bodysnatch = 4, + /obj/item/organ/heart/gland/egg = 7, + /obj/item/organ/heart/gland/emp = 3, + /obj/item/organ/heart/gland/mindshock = 5, + /obj/item/organ/heart/gland/plasma = 7, + /obj/item/organ/heart/gland/pop = 5, + /obj/item/organ/heart/gland/slime = 4, + /obj/item/organ/heart/gland/spiderman = 5, + /obj/item/organ/heart/gland/ventcrawling = 1, + /obj/item/organ/body_egg/alien_embryo = 1, + /obj/item/organ/hivelord_core = 2) + lootcount = 3 diff --git a/code/game/objects/items/devices/scanners.dm b/code/game/objects/items/devices/scanners.dm index 9da0d31aac7..f5ee27cc0e3 100644 --- a/code/game/objects/items/devices/scanners.dm +++ b/code/game/objects/items/devices/scanners.dm @@ -125,7 +125,7 @@ MASS SPECTROMETER if(ishuman(M)) var/mob/living/carbon/human/H = M - if(H.heart_attack && H.stat != DEAD) + if(H.undergoing_cardiac_arrest() && H.stat != DEAD) user << "Subject suffering from heart attack: Apply defibrillator immediately!" if(iscarbon(M)) diff --git a/code/game/objects/items/weapons/defib.dm b/code/game/objects/items/weapons/defib.dm index 965c9fe1463..47bc2d5835c 100644 --- a/code/game/objects/items/weapons/defib.dm +++ b/code/game/objects/items/weapons/defib.dm @@ -1,4 +1,6 @@ //backpack item +#define HALFWAYCRITDEATH ((HEALTH_THRESHOLD_CRIT + HEALTH_THRESHOLD_DEAD) * 0.5) + /obj/item/weapon/defibrillator name = "defibrillator" desc = "A device that delivers powerful shocks to detachable paddles that resuscitate incapacitated patients." @@ -240,7 +242,6 @@ paddles = make_paddles() bcell = new(src) update_icon() - return /obj/item/weapon/defibrillator/compact/combat name = "combat defibrillator" @@ -253,7 +254,6 @@ paddles = make_paddles() bcell = new /obj/item/weapon/stock_parts/cell/infinite(src) update_icon() - return /obj/item/weapon/defibrillator/compact/combat/loaded/attackby(obj/item/weapon/W, mob/user, params) if(W == paddles) @@ -339,7 +339,6 @@ return 1 /obj/item/weapon/twohanded/shockpaddles/attack(mob/M, mob/user) - var/halfwaycritdeath = (HEALTH_THRESHOLD_CRIT + HEALTH_THRESHOLD_DEAD) / 2 if(busy) return @@ -359,6 +358,11 @@ else user << "[src] are recharging!" return + + if(user.a_intent == INTENT_DISARM) + do_disarm(M, user) + return + if(!ishuman(M)) if(req_defib) user << "The instructions on [defib] don't mention how to revive that..." @@ -367,101 +371,109 @@ return var/mob/living/carbon/human/H = M - if(user.a_intent == INTENT_DISARM) - if(req_defib && defib.safety) - return - if(!req_defib && !combat) - return - busy = 1 - H.visible_message("[user] has touched [H.name] with [src]!", \ - "[user] has touched [H.name] with [src]!") - H.adjustStaminaLoss(50) - H.Weaken(5) - H.updatehealth() //forces health update before next life tick - playsound(get_turf(src), 'sound/machines/defib_zap.ogg', 50, 1, -1) - H.emote("gasp") - add_logs(user, M, "stunned", src) - if(req_defib) - defib.deductcharge(revivecost) - cooldown = 1 - busy = 0 - update_icon() - if(req_defib) - defib.cooldowncheck(user) - else - recharge(60) - return if(user.zone_selected != "chest") user << "You need to target your patient's \ chest with [src]!" return + if(user.a_intent == INTENT_HARM) - if(req_defib && defib.safety) - return - if(!req_defib && !combat) - return - user.visible_message("[user] begins to place [src] on [M.name]'s chest.", - "You overcharge the paddles and begin to place them onto [M]'s chest...") - busy = 1 - update_icon() - if(do_after(user, 30, target = M)) - user.visible_message("[user] places [src] on [M.name]'s chest.", - "You place [src] on [M.name]'s chest and begin to charge them.") - var/turf/T = get_turf(defib) - playsound(get_turf(src), 'sound/machines/defib_charge.ogg', 50, 0) - if(req_defib) - T.audible_message("\The [defib] lets out an urgent beep and lets out a steadily rising hum...") - else - user.audible_message("[src] let out an urgent beep.") - if(do_after(user, 30, target = M)) //Takes longer due to overcharging - if(!M) - busy = 0 - update_icon() - return - if(M && M.stat == DEAD) - user << "[M] is dead." - playsound(get_turf(src), 'sound/machines/defib_failed.ogg', 50, 0) - busy = 0 - update_icon() - return - user.visible_message("[user] shocks [M] with \the [src]!", "You shock [M] with \the [src]!") - playsound(get_turf(src), 'sound/machines/defib_zap.ogg', 100, 1, -1) - playsound(loc, 'sound/weapons/Egloves.ogg', 100, 1, -1) - var/mob/living/carbon/human/HU = M - M.emote("scream") - if(!HU.heart_attack) - HU.heart_attack = 1 - if(!HU.stat) - HU.visible_message("[M] thrashes wildly, clutching at their chest!", - "You feel a horrible agony in your chest!") - HU.apply_damage(50, BURN, "chest") - add_logs(user, M, "overloaded the heart of", defib) - M.Weaken(5) - M.Jitter(100) - if(req_defib) - defib.deductcharge(revivecost) - cooldown = 1 - busy = 0 - update_icon() - if(!req_defib) - recharge(60) - if(req_defib && (defib.cooldowncheck(user))) - return - busy = 0 - update_icon() + do_harm(H, user) return + if((!req_defib && grab_ghost) || (req_defib && defib.grab_ghost)) H.notify_ghost_cloning("Your heart is being defibrillated!") H.grab_ghost() // Shove them back in their body. else if(!H.suiciding && !(H.disabilities & NOCLONE)&& !H.hellbound) H.notify_ghost_cloning("Your heart is being defibrillated. Re-enter your corpse if you want to be revived!", source = src) - user.visible_message("[user] begins to place [src] on [M.name]'s chest.", "You begin to place [src] on [M.name]'s chest...") + do_help(H, user) + + +/obj/item/weapon/twohanded/shockpaddles/proc/do_disarm(mob/living/M, mob/living/user) + if(req_defib && defib.safety) + return + if(!req_defib && !combat) + return + busy = 1 + M.visible_message("[user] has touched [M] with [src]!", \ + "[user] has touched [M] with [src]!") + M.adjustStaminaLoss(50) + M.Weaken(5) + M.updatehealth() //forces health update before next life tick + playsound(get_turf(src), 'sound/machines/defib_zap.ogg', 50, 1, -1) + M.emote("gasp") + add_logs(user, M, "stunned", src) + if(req_defib) + defib.deductcharge(revivecost) + cooldown = 1 + busy = 0 + update_icon() + if(req_defib) + defib.cooldowncheck(user) + else + recharge(60) + +/obj/item/weapon/twohanded/shockpaddles/proc/do_harm(mob/living/carbon/human/H, mob/living/user) + if(req_defib && defib.safety) + return + if(!req_defib && !combat) + return + user.visible_message("[user] begins to place [src] on [H]'s chest.", + "You overcharge the paddles and begin to place them onto [H]'s chest...") busy = 1 update_icon() - if(do_after(user, 30, target = M)) //beginning to place the paddles on patient's chest to allow some time for people to move away to stop the process - user.visible_message("[user] places [src] on [M.name]'s chest.", "You place [src] on [M.name]'s chest.") + if(do_after(user, 30, target = H)) + user.visible_message("[user] places [src] on [H]'s chest.", + "You place [src] on [H]'s chest and begin to charge them.") + var/turf/T = get_turf(defib) + playsound(get_turf(src), 'sound/machines/defib_charge.ogg', 50, 0) + if(req_defib) + T.audible_message("\The [defib] lets out an urgent beep and lets out a steadily rising hum...") + else + user.audible_message("[src] let out an urgent beep.") + if(do_after(user, 30, target = H)) //Takes longer due to overcharging + if(!H) + busy = 0 + update_icon() + return + if(H && H.stat == DEAD) + user << "[H] is dead." + playsound(get_turf(src), 'sound/machines/defib_failed.ogg', 50, 0) + busy = 0 + update_icon() + return + user.visible_message("[user] shocks [H] with \the [src]!", "You shock [H] with \the [src]!") + playsound(get_turf(src), 'sound/machines/defib_zap.ogg', 100, 1, -1) + playsound(loc, 'sound/weapons/Egloves.ogg', 100, 1, -1) + H.emote("scream") + if(H.can_heartattack() && !H.undergoing_cardiac_arrest()) + if(!H.stat) + H.visible_message("[H] thrashes wildly, clutching at their chest!", + "You feel a horrible agony in your chest!") + H.set_heartattack(TRUE) + H.apply_damage(50, BURN, "chest") + add_logs(user, H, "overloaded the heart of", defib) + H.Weaken(5) + H.Jitter(100) + if(req_defib) + defib.deductcharge(revivecost) + cooldown = 1 + busy = 0 + update_icon() + if(!req_defib) + recharge(60) + if(req_defib && (defib.cooldowncheck(user))) + return + busy = 0 + update_icon() + +/obj/item/weapon/twohanded/shockpaddles/proc/do_help(mob/living/carbon/human/H, mob/living/user) + user.visible_message("[user] begins to place [src] on [H]'s chest.", "You begin to place [src] on [H]'s chest...") + busy = 1 + update_icon() + if(do_after(user, 30, target = H)) //beginning to place the paddles on patient's chest to allow some time for people to move away to stop the process + user.visible_message("[user] places [src] on [H]'s chest.", "You place [src] on [H]'s chest.") playsound(get_turf(src), 'sound/machines/defib_charge.ogg', 50, 0) var/tplus = world.time - H.timeofdeath // past this much time the patient is unrecoverable @@ -472,7 +484,7 @@ var/tloss = DEFIB_TIME_LOSS * 10 var/total_burn = 0 var/total_brute = 0 - if(do_after(user, 20, target = M)) //placed on chest and short delay to shock for dramatic effect, revive time is 5sec total + if(do_after(user, 20, target = H)) //placed on chest and short delay to shock for dramatic effect, revive time is 5sec total for(var/obj/item/carried_item in H.contents) if(istype(carried_item, /obj/item/clothing/suit/space)) if((!src.combat && !req_defib) || (req_defib && !defib.combat)) @@ -482,7 +494,7 @@ update_icon() return if(H.stat == DEAD) - M.visible_message("[M]'s body convulses a bit.") + H.visible_message("[H]'s body convulses a bit.") playsound(get_turf(src), "bodyfall", 50, 1) playsound(get_turf(src), 'sound/machines/defib_zap.ogg', 50, 1, -1) total_brute = H.getBruteLoss() @@ -512,23 +524,24 @@ playsound(get_turf(src), 'sound/machines/defib_failed.ogg', 50, 0) else //If the body has been fixed so that they would not be in crit when defibbed, give them oxyloss to put them back into crit - if (H.health > halfwaycritdeath) - H.adjustOxyLoss(H.health - halfwaycritdeath, 0) + if (H.health > HALFWAYCRITDEATH) + H.adjustOxyLoss(H.health - HALFWAYCRITDEATH, 0) else var/overall_damage = total_brute + total_burn + H.getToxLoss() + H.getOxyLoss() var/mobhealth = H.health - H.adjustOxyLoss((mobhealth - halfwaycritdeath) * (H.getOxyLoss() / overall_damage), 0) - H.adjustToxLoss((mobhealth - halfwaycritdeath) * (H.getToxLoss() / overall_damage), 0) - H.adjustFireLoss((mobhealth - halfwaycritdeath) * (total_burn / overall_damage), 0) - H.adjustBruteLoss((mobhealth - halfwaycritdeath) * (total_brute / overall_damage), 0) + H.adjustOxyLoss((mobhealth - HALFWAYCRITDEATH) * (H.getOxyLoss() / overall_damage), 0) + H.adjustToxLoss((mobhealth - HALFWAYCRITDEATH) * (H.getToxLoss() / overall_damage), 0) + H.adjustFireLoss((mobhealth - HALFWAYCRITDEATH) * (total_burn / overall_damage), 0) + H.adjustBruteLoss((mobhealth - HALFWAYCRITDEATH) * (total_brute / overall_damage), 0) H.updatehealth() // Previous "adjust" procs don't update health, so we do it manually. user.visible_message("[req_defib ? "[defib]" : "[src]"] pings: Resuscitation successful.") playsound(get_turf(src), 'sound/machines/defib_success.ogg', 50, 0) + H.set_heartattack(FALSE) H.revive() H.emote("gasp") if(tplus > tloss) H.setBrainLoss( max(0, min(99, ((tlimit - tplus) / tlimit * 100)))) - add_logs(user, M, "revived", defib) + add_logs(user, H, "revived", defib) if(req_defib) defib.deductcharge(revivecost) cooldown = 1 @@ -537,14 +550,14 @@ defib.cooldowncheck(user) else recharge(60) - else if(H.heart_attack) - H.heart_attack = 0 - user.visible_message("[req_defib ? "[defib]" : "[src]"] pings: Patient's heart is now beating again.") - playsound(get_turf(src), 'sound/machines/defib_zap.ogg', 50, 1, -1) - else if (!H.getorgan(/obj/item/organ/heart)) user.visible_message("[req_defib ? "[defib]" : "[src]"] buzzes: Patient's heart is missing. Operation aborted.") playsound(get_turf(src), 'sound/machines/defib_failed.ogg', 50, 0) + else if(H.undergoing_cardiac_arrest()) + H.set_heartattack(FALSE) + user.visible_message("[req_defib ? "[defib]" : "[src]"] pings: Patient's heart is now beating again.") + playsound(get_turf(src), 'sound/machines/defib_zap.ogg', 50, 1, -1) + else user.visible_message("[req_defib ? "[defib]" : "[src]"] buzzes: Patient is not in a valid state. Operation aborted.") @@ -579,3 +592,5 @@ icon_state = "defibpaddles0" item_state = "defibpaddles0" req_defib = FALSE + +#undef HALFWAYCRITDEATH diff --git a/code/modules/cargo/exports/organs.dm b/code/modules/cargo/exports/organs.dm index ebf34e8763c..39c0051a4ee 100644 --- a/code/modules/cargo/exports/organs.dm +++ b/code/modules/cargo/exports/organs.dm @@ -56,7 +56,7 @@ /datum/export/organ/alien/abductor cost = 2500 unit_name = "abductor gland" - export_types = list(/obj/item/organ/gland) + export_types = list(/obj/item/organ/heart/gland) /datum/export/organ/alien/changeling_egg cost = 50000 // Holy. Fuck. diff --git a/code/modules/mob/living/blood.dm b/code/modules/mob/living/blood.dm index 73ae98b59db..4a0e072a06f 100644 --- a/code/modules/mob/living/blood.dm +++ b/code/modules/mob/living/blood.dm @@ -6,11 +6,13 @@ if(bleedsuppress) return else - bleedsuppress = 1 - spawn(amount) - bleedsuppress = 0 - if(stat != DEAD && bleed_rate) - src << "The blood soaks through your bandage." + bleedsuppress = TRUE + addtimer(CALLBACK(src, .proc/resume_bleeding), amount) + +/mob/living/carbon/human/proc/resume_bleeding() + bleedsuppress = 0 + if(stat != DEAD && bleed_rate) + src << "The blood soaks through your bandage." /mob/living/carbon/monkey/handle_blood() @@ -273,4 +275,4 @@ T = get_turf(src) var/obj/effect/decal/cleanable/oil/B = locate() in T.contents if(!B) - B = new(T) \ No newline at end of file + B = new(T) diff --git a/code/modules/mob/living/carbon/human/death.dm b/code/modules/mob/living/carbon/human/death.dm index cd3c9a533ac..6464d1ab70e 100644 --- a/code/modules/mob/living/carbon/human/death.dm +++ b/code/modules/mob/living/carbon/human/death.dm @@ -21,7 +21,6 @@ dizziness = 0 jitteriness = 0 - heart_attack = 0 if(istype(loc, /obj/mecha)) var/obj/mecha/M = loc diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index b0eac013cd8..9ebefb9dc9b 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -820,6 +820,7 @@ regenerate_limbs() regenerate_organs() remove_all_embedded_objects() + set_heartattack(FALSE) drunkenness = 0 for(var/datum/mutation/human/HM in dna.mutations) if(HM.quality != POSITIVE) diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm index 16c2ae46bc4..9fefbb3ab6e 100644 --- a/code/modules/mob/living/carbon/human/human_defense.dm +++ b/code/modules/mob/living/carbon/human/human_defense.dm @@ -461,9 +461,10 @@ var/obj/item/clothing/gloves/G = gloves gloves_siemens_coeff = G.siemens_coefficient siemens_coeff = gloves_siemens_coeff - if(heart_attack && !illusion) + if(undergoing_cardiac_arrest() && !illusion) if(shock_damage * siemens_coeff >= 1 && prob(25)) - heart_attack = 0 + var/obj/item/organ/heart/heart = getorganslot("heart") + heart.beating = TRUE if(stat == CONSCIOUS) src << "You feel your heart beating again!" . = ..(shock_damage,source,siemens_coeff,safety,override,tesla_shock, illusion, stun) diff --git a/code/modules/mob/living/carbon/human/human_defines.dm b/code/modules/mob/living/carbon/human/human_defines.dm index d712ce9b6a9..796897c5bcf 100644 --- a/code/modules/mob/living/carbon/human/human_defines.dm +++ b/code/modules/mob/living/carbon/human/human_defines.dm @@ -46,8 +46,6 @@ var/global/default_martial_art = new/datum/martial_art var/name_override //For temporary visible name changes - var/heart_attack = 0 - var/drunkenness = 0 //Overall drunkenness - check handle_alcohol() in life.dm for effects var/datum/personal_crafting/handcrafting can_buckle = TRUE diff --git a/code/modules/mob/living/carbon/human/life.dm b/code/modules/mob/living/carbon/human/life.dm index 239f657ba03..2651a150cd0 100644 --- a/code/modules/mob/living/carbon/human/life.dm +++ b/code/modules/mob/living/carbon/human/life.dm @@ -325,27 +325,50 @@ if(!has_embedded_objects()) clear_alert("embeddedobject") +/mob/living/carbon/human/proc/can_heartattack() + CHECK_DNA_AND_SPECIES(src) + if(NOBLOOD in dna.species.species_traits) + return FALSE + return TRUE + +/mob/living/carbon/human/proc/undergoing_cardiac_arrest() + if(!can_heartattack()) + return FALSE + var/obj/item/organ/heart/heart = getorganslot("heart") + if(istype(heart) && heart.beating) + return FALSE + return TRUE + +/mob/living/carbon/human/proc/set_heartattack(status) + if(!can_heartattack()) + return FALSE + + var/obj/item/organ/heart/heart = getorganslot("heart") + if(!istype(heart)) + return + + heart.beating = !status + /mob/living/carbon/human/proc/handle_heart() - CHECK_DNA_AND_SPECIES(src) - var/needs_heart = (!(NOBLOOD in dna.species.species_traits)) + if(!can_heartattack()) + return + var/we_breath = (!(NOBREATH in dna.species.species_traits)) - if(heart_attack) - if(!needs_heart) - heart_attack = FALSE - else if(we_breath) - if(losebreath < 3) - losebreath += 2 - adjustOxyLoss(5) - adjustBruteLoss(1) - else - // even though we don't require oxygen, our blood still needs - // circulation, and without it, our tissues die and start - // gaining toxins - adjustBruteLoss(3) - if(src.reagents) - src.reagents.add_reagent("toxin", 2) + + if(!undergoing_cardiac_arrest()) + return + + // Cardiac arrest, unless corazone + if(reagents.get_reagent_amount("corazone")) + return + + if(we_breath) + adjustOxyLoss(8) + Paralyse(4) + // Tissues die without blood circulation + adjustBruteLoss(2) /* Alcohol Poisoning Chart diff --git a/code/modules/reagents/chemistry/reagents/medicine_reagents.dm b/code/modules/reagents/chemistry/reagents/medicine_reagents.dm index 0c6a3e4302b..67fc0608607 100644 --- a/code/modules/reagents/chemistry/reagents/medicine_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/medicine_reagents.dm @@ -1116,3 +1116,11 @@ M.adjustToxLoss(2, 0) . = 1 ..() + +/datum/reagent/medicine/corazone + // Heart attack code will not do as damage if corazone is present + // because it's SPACE MAGIC ASPIRIN + name = "Corazone" + id = "corazone" + description = "A medication used to treat pain, fever, and inflammation, along with heart attacks." + color = "#F5F5F5" diff --git a/code/modules/reagents/chemistry/reagents/toxin_reagents.dm b/code/modules/reagents/chemistry/reagents/toxin_reagents.dm index fc79232e8f5..24ca24a3d3f 100644 --- a/code/modules/reagents/chemistry/reagents/toxin_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/toxin_reagents.dm @@ -522,8 +522,8 @@ if(3) if(ishuman(M)) var/mob/living/carbon/human/H = M - if(!H.heart_attack) - H.heart_attack = 1 // rip in pepperoni + if(!H.undergoing_cardiac_arrest() && H.can_heartattack()) + H.set_heartattack(TRUE) if(H.stat == CONSCIOUS) H.visible_message("[H] clutches at [H.p_their()] chest as if [H.p_their()] heart stopped!") else @@ -833,4 +833,4 @@ if(prob(10)) M.Weaken(1, 0) . = 1 - ..() \ No newline at end of file + ..() diff --git a/code/modules/reagents/chemistry/recipes/medicine.dm b/code/modules/reagents/chemistry/recipes/medicine.dm index e446ab9736b..187b42f035f 100644 --- a/code/modules/reagents/chemistry/recipes/medicine.dm +++ b/code/modules/reagents/chemistry/recipes/medicine.dm @@ -212,4 +212,10 @@ name = "Tricordrazine" id = "tricordrazine" results = list("tricordrazine" = 3) - required_reagents = list("bicaridine" = 1, "kelotane" = 1, "antitoxin" = 1) \ No newline at end of file + required_reagents = list("bicaridine" = 1, "kelotane" = 1, "antitoxin" = 1) + +/datum/chemical_reaction/corazone + name = "Corazone" + id = "corazone" + results = list("corazone" = 3) + required_reagents = list("phenol" = 2, "lithium" = 1) diff --git a/code/modules/surgery/organs/augments_chest.dm b/code/modules/surgery/organs/augments_chest.dm index 850335c717a..e352273a13e 100644 --- a/code/modules/surgery/organs/augments_chest.dm +++ b/code/modules/surgery/organs/augments_chest.dm @@ -100,8 +100,8 @@ if(ishuman(owner)) var/mob/living/carbon/human/H = owner - if(H.stat != DEAD && prob(50 / severity)) - H.heart_attack = TRUE + if(H.stat != DEAD && prob(50 / severity) && H.can_heartattack()) + H.set_heartattack(TRUE) H << "You feel a horrible agony in your chest!" addtimer(CALLBACK(src, .proc/undo_heart_attack), 600 / severity) @@ -109,7 +109,7 @@ var/mob/living/carbon/human/H = owner if(!istype(H)) return - H.heart_attack = FALSE + H.set_heartattack(FALSE) if(H.stat == CONSCIOUS) H << "You feel your heart beating again!" diff --git a/code/modules/surgery/organs/organ_internal.dm b/code/modules/surgery/organs/organ_internal.dm index 086395d0b64..da4ab4725f7 100644 --- a/code/modules/surgery/organs/organ_internal.dm +++ b/code/modules/surgery/organs/organ_internal.dm @@ -104,6 +104,7 @@ zone = "chest" slot = "heart" origin_tech = "biotech=5" + // Heart attack code is in code/modules/mob/living/carbon/human/life.dm var/beating = 1 var/icon_base = "heart" attack_verb = list("beat", "thumped") @@ -116,15 +117,8 @@ /obj/item/organ/heart/Remove(mob/living/carbon/M, special = 0) ..() - if(ishuman(M)) - var/mob/living/carbon/human/H = M - if(H.stat == DEAD || H.heart_attack) - Stop() - return - if(!special) - H.heart_attack = 1 - - addtimer(CALLBACK(src, .proc/stop_if_unowned), 120) + if(!special) + addtimer(CALLBACK(src, .proc/stop_if_unowned), 120) /obj/item/organ/heart/proc/stop_if_unowned() if(!owner) @@ -134,18 +128,11 @@ ..() if(!beating) visible_message("[user] squeezes [src] to \ - make it beat again!") + make it beat again!", "You squeeze \ + [src] to make it beat again!") Restart() addtimer(CALLBACK(src, .proc/stop_if_unowned), 80) -/obj/item/organ/heart/Insert(mob/living/carbon/M, special = 0) - ..() - if(ishuman(M) && beating) - var/mob/living/carbon/human/H = M - if(H.heart_attack) - H.heart_attack = 0 - return - /obj/item/organ/heart/proc/Stop() beating = 0 update_icon() @@ -866,4 +853,4 @@ flash_protect = 2 /obj/item/organ/eyes/robotic/shield/emp_act(severity) - return \ No newline at end of file + return