diff --git a/code/__HELPERS/unsorted.dm b/code/__HELPERS/unsorted.dm index e6917bc8830..7a7213b12bf 100644 --- a/code/__HELPERS/unsorted.dm +++ b/code/__HELPERS/unsorted.dm @@ -534,6 +534,17 @@ Turf and target are seperate in case you want to teleport some distance from a t loc = loc.loc return loc +/* +Returns 1 if the chain up to the area contains the given typepath +0 otherwise +*/ +/atom/proc/is_found_within(var/typepath) + var/atom/A = src + while(A.loc) + if(istype(A.loc, typepath)) + return 1 + A = A.loc + return 0 // the on-close client verb // called when a browser popup window is closed after registering with proc/onclose() diff --git a/code/game/machinery/vending.dm b/code/game/machinery/vending.dm index 01443ade04e..f05ab015c49 100644 --- a/code/game/machinery/vending.dm +++ b/code/game/machinery/vending.dm @@ -889,7 +889,7 @@ product_ads = "Go save some lives!;The best stuff for your medbay.;Only the finest tools.;Natural chemicals!;This stuff saves lives.;Don't you want some?;Ping!" req_access_txt = "5" products = list(/obj/item/weapon/reagent_containers/glass/bottle/charcoal = 4,/obj/item/weapon/reagent_containers/glass/bottle/morphine = 4,/obj/item/weapon/reagent_containers/glass/bottle/ether = 4,/obj/item/weapon/reagent_containers/glass/bottle/epinephrine = 4, - /obj/item/weapon/reagent_containers/glass/bottle/toxin = 4,/obj/item/weapon/reagent_containers/syringe/antiviral = 4,/obj/item/weapon/reagent_containers/syringe/insulin = 4, + /obj/item/weapon/reagent_containers/glass/bottle/toxin = 4,/obj/item/weapon/reagent_containers/syringe/antiviral = 6,/obj/item/weapon/reagent_containers/syringe/insulin = 4, /obj/item/weapon/reagent_containers/syringe = 12,/obj/item/device/healthanalyzer = 5,/obj/item/device/healthupgrade = 5,/obj/item/weapon/reagent_containers/glass/beaker = 4, /obj/item/weapon/reagent_containers/dropper = 2,/obj/item/stack/medical/advanced/bruise_pack = 3, /obj/item/stack/medical/advanced/ointment = 3, /obj/item/stack/medical/bruise_pack = 3,/obj/item/stack/medical/splint = 2, /obj/item/device/sensor_device = 2) diff --git a/code/modules/mob/living/carbon/brain/MMI.dm b/code/modules/mob/living/carbon/brain/MMI.dm index 5e6b142038c..a0168448391 100644 --- a/code/modules/mob/living/carbon/brain/MMI.dm +++ b/code/modules/mob/living/carbon/brain/MMI.dm @@ -12,91 +12,112 @@ var/alien = 0 var/syndiemmi = 0 //Whether or not this is a Syndicate MMI var/mob/living/carbon/brain/brainmob = null//The current occupant. + var/obj/item/organ/brain/held_brain = null // This is so MMI's aren't brainscrubber 9000's var/mob/living/silicon/robot/robot = null//Appears unused. var/obj/mecha/mecha = null//This does not appear to be used outside of reference in mecha.dm. // I'm using this for mechs giving MMIs HUDs now - attackby(var/obj/item/O as obj, var/mob/user as mob, params) - if(istype(O, /obj/item/organ/brain/crystal )) - user << " This brain is too malformed to be able to use with the [src]." +/obj/item/device/mmi/attackby(var/obj/item/O as obj, var/mob/user as mob, params) + if(istype(O, /obj/item/organ/brain/crystal )) + user << " This brain is too malformed to be able to use with the [src]." + return + if(istype(O,/obj/item/organ/brain) && !brainmob) //Time to stick a brain in it --NEO + var/obj/item/organ/brain/B = O + if(!B.brainmob) + user << "You aren't sure where this brain came from, but you're pretty sure it's a useless brain." return - if(istype(O,/obj/item/organ/brain) && !brainmob) //Time to stick a brain in it --NEO - if(!O:brainmob) - user << "\red You aren't sure where this brain came from, but you're pretty sure it's a useless brain." - return - for(var/mob/V in viewers(src, null)) - V.show_message(text("\blue [user] sticks \a [O] into \the [src].")) - brainmob = O:brainmob - O:brainmob = null - brainmob.loc = src - brainmob.container = src - brainmob.stat = 0 - respawnable_list -= brainmob - dead_mob_list -= brainmob//Update dem lists - living_mob_list += brainmob - - user.drop_item() - if(istype(O,/obj/item/organ/brain/xeno)) - name = "Man-Machine Interface: Alien - [brainmob.real_name]" - icon = 'icons/mob/alien.dmi' - icon_state = "AlienMMI" - alien = 1 - else - name = "Man-Machine Interface: [brainmob.real_name]" - icon_state = "mmi_full" - alien = 0 - qdel(O) - - - - feedback_inc("cyborg_mmis_filled",1) - + if(held_brain) + user << "Somehow, this MMI still has a brain in it. Report this to the bug tracker." + log_to_dd("[user] tried to stick a [O] into [src] in [get_area(src)], but the held brain variable wasn't cleared") return - - if(brainmob) - O.attack(brainmob, user)//Oh noooeeeee - // Brainmobs can take damage, but they can't actually die. Maybe should fix. + if(B.status & ORGAN_DEAD) // As it stood, a brain would never become unviable for MMI usage. Perhaps we should keep it that way? + user << "This [B] is dead" return - ..() + for(var/mob/V in viewers(src, null)) + V.show_message("[user] sticks \a [O] into \the [src].") + brainmob = B.brainmob + B.brainmob = null + brainmob.loc = src + brainmob.container = src + brainmob.stat = CONSCIOUS + respawnable_list -= brainmob + dead_mob_list -= brainmob//Update dem lists + living_mob_list += brainmob - - - attack_self(mob/user as mob) - if(!brainmob) - user << "\red You upend the MMI, but there's nothing in it." + user.drop_item() + B.forceMove(src) + held_brain = B + if(istype(O,/obj/item/organ/brain/xeno)) // I'm not sure how well this will work now, since I don't think you can actually get xeno brains + name = "Man-Machine Interface: Alien - [brainmob.real_name]" + icon = 'icons/mob/alien.dmi' + icon_state = "AlienMMI" + alien = 1 else - user << "You unlock and upend the MMI, spilling the brain onto the floor." - if(alien) - var/obj/item/organ/brain/xeno/brain = new(user.loc) - dropbrain(brain,get_turf(user)) - else - var/obj/item/organ/brain/brain = new(user.loc) - dropbrain(brain,get_turf(user)) - icon = 'icons/obj/assemblies.dmi' - icon_state = "mmi_empty" - name = "Man-Machine Interface" - - proc - transfer_identity(var/mob/living/carbon/human/H)//Same deal as the regular brain proc. Used for human-->robot people. - brainmob = new(src) - brainmob.name = H.real_name - brainmob.real_name = H.real_name - brainmob.dna = H.dna.Clone() - brainmob.container = src - name = "Man-Machine Interface: [brainmob.real_name]" icon_state = "mmi_full" - return + alien = 0 + feedback_inc("cyborg_mmis_filled",1) + + return + + if(brainmob) + O.attack(brainmob, user)//Oh noooeeeee + // Brainmobs can take damage, but they can't actually die. Maybe should fix. + return + ..() + + + +/obj/item/device/mmi/attack_self(mob/user as mob) + if(!brainmob) + user << "You upend the MMI, but there's nothing in it." + else + user << "You unlock and upend the MMI, spilling the brain onto the floor." + dropbrain(get_turf(user)) + icon = 'icons/obj/assemblies.dmi' + icon_state = "mmi_empty" + name = "Man-Machine Interface" + +/obj/item/device/mmi/proc/transfer_identity(var/mob/living/carbon/human/H)//Same deal as the regular brain proc. Used for human-->robot people. + brainmob = new(src) + brainmob.name = H.real_name + brainmob.real_name = H.real_name + brainmob.dna = H.dna.Clone() + brainmob.container = src + + if(!istype(H.species) || isnull(H.species.return_organ("brain"))) // Diona/buggy people + held_brain = new(src) + else // We have a species, and it has a brain + var/brain_path = H.species.return_organ("brain") + if(!ispath(brain_path, /obj/item/organ/brain)) + brain_path = /obj/item/organ/brain + held_brain = new brain_path(src) // Slime people will keep their slimy brains this way + held_brain.dna = brainmob.dna.Clone() + held_brain.name = "\the [brainmob.name]'s [initial(held_brain.name)]" + + name = "Man-Machine Interface: [brainmob.real_name]" + icon_state = "mmi_full" + return + //I made this proc as a way to have a brainmob be transferred to any created brain, and to solve the //problem i was having with alien/nonalien brain drops. - dropbrain(var/obj/item/organ/brain/brain, var/turf/dropspot) - brainmob.container = null//Reset brainmob mmi var. - brainmob.loc = brain//Throw mob into brain. - respawnable_list += brainmob - living_mob_list -= brainmob//Get outta here - brain.brainmob = brainmob//Set the brain to use the brainmob - brain.brainmob.cancel_camera() - brainmob = null//Set mmi brainmob var to null +/obj/item/device/mmi/proc/dropbrain(var/turf/dropspot) + if(isnull(held_brain)) + log_to_dd("[src] at [loc] attempted to drop brain without a contained brain in [get_area(src)].") + brainmob << "Your MMI did not contain a brain! We'll make a new one for you, but you'd best report this to the bugtracker!" + held_brain = new(dropspot) // Let's not ruin someone's round because of something dumb -- Crazylemon + held_brain.dna = brainmob.dna.Clone() + held_brain.name = "\the [brainmob.name]'s [initial(held_brain.name)]" + + brainmob.container = null//Reset brainmob mmi var. + brainmob.loc = held_brain//Throw mob into brain. + respawnable_list += brainmob + living_mob_list -= brainmob//Get outta here + held_brain.brainmob = brainmob//Set the brain to use the brainmob + held_brain.brainmob.cancel_camera() + brainmob = null//Set mmi brainmob var to null + held_brain.forceMove(dropspot) + held_brain = null /obj/item/device/mmi/radio_enabled @@ -106,25 +127,23 @@ var/obj/item/device/radio/radio = null//Let's give it a radio. - New() - ..() - radio = new(src)//Spawns a radio inside the MMI. - radio.broadcasting = 1//So it's broadcasting from the start. +/obj/item/device/mmi/radio_enabled/New() + ..() + radio = new(src)//Spawns a radio inside the MMI. + radio.broadcasting = 1//So it's broadcasting from the start. - verb//Allows the brain to toggle the radio functions. +/obj/item/device/mmi/radio_enabled/verb/Toggle_Listening() + set name = "Toggle Listening" + set desc = "Toggle listening channel on or off." + set category = "MMI" + set src = usr.loc + set popup_menu = 0 - Toggle_Listening() - set name = "Toggle Listening" - set desc = "Toggle listening channel on or off." - set category = "MMI" - set src = usr.loc - set popup_menu = 0 + if(brainmob.stat) + brainmob << "Can't do that while incapacitated or dead." - if(brainmob.stat) - brainmob << "Can't do that while incapacitated or dead." - - radio.listening = radio.listening==1 ? 0 : 1 - brainmob << "\blue Radio is [radio.listening==1 ? "now" : "no longer"] receiving broadcast." + radio.listening = radio.listening==1 ? 0 : 1 + brainmob << "Radio is [radio.listening==1 ? "now" : "no longer"] receiving broadcast." /obj/item/device/mmi/emp_act(severity) if(!brainmob) @@ -153,6 +172,9 @@ if(brainmob) qdel(brainmob) brainmob = null + if(held_brain) + qdel(held_brain) + held_brain = null return ..() /obj/item/device/mmi/syndie diff --git a/code/modules/mob/living/carbon/brain/posibrain.dm b/code/modules/mob/living/carbon/brain/posibrain.dm index 3cd7e511369..67caeb2788c 100644 --- a/code/modules/mob/living/carbon/brain/posibrain.dm +++ b/code/modules/mob/living/carbon/brain/posibrain.dm @@ -54,19 +54,24 @@ else if (response == "Never for this round") C.prefs.be_special ^= BE_PAI +// This should not ever happen, but let's be safe +/obj/item/device/mmi/posibrain/dropbrain(var/turf/dropspot) + log_to_dd("[src] at [loc] attempted to drop brain without a contained brain.") + return /obj/item/device/mmi/posibrain/transfer_identity(var/mob/living/carbon/H) name = "positronic brain ([H])" - brainmob.name = H.real_name - brainmob.real_name = H.real_name - brainmob.dna = H.dna + if(isnull(brainmob.dna)) + brainmob.dna = H.dna.Clone() + brainmob.name = brainmob.dna.real_name + brainmob.real_name = brainmob.name brainmob.timeofhostdeath = H.timeofdeath - brainmob.stat = 0 + brainmob.stat = CONSCIOUS if(brainmob.mind) brainmob.mind.assigned_role = "Positronic Brain" if(H.mind) H.mind.transfer_to(brainmob) - brainmob << "\blue You feel slightly disoriented. That's normal when you're just a metal cube." + brainmob << "You feel slightly disoriented. That's normal when you're just a metal cube." icon_state = "posibrain-occupied" return diff --git a/code/modules/mob/living/carbon/human/species/species.dm b/code/modules/mob/living/carbon/human/species/species.dm index e679991ed2e..bf0702f5f9e 100644 --- a/code/modules/mob/living/carbon/human/species/species.dm +++ b/code/modules/mob/living/carbon/human/species/species.dm @@ -118,6 +118,7 @@ "l_foot" = list("path" = /obj/item/organ/external/foot), "r_foot" = list("path" = /obj/item/organ/external/foot/right) ) + var/cyborg_type = "Cyborg" /datum/species/New() //If the species has eyes, they are the default vision organ @@ -593,3 +594,12 @@ H.bodytemp.icon_state = "temp0" return 1 + +/* +Returns the path corresponding to the corresponding organ +It'll return null if the organ doesn't correspond, so include null checks when using this! +*/ +/datum/species/proc/return_organ(var/organ_slot) + if(!(organ_slot in has_organ)) + return null + return has_organ[organ_slot] \ No newline at end of file diff --git a/code/modules/mob/transform_procs.dm b/code/modules/mob/transform_procs.dm index 9c2913bf68e..f31b76639ed 100644 --- a/code/modules/mob/transform_procs.dm +++ b/code/modules/mob/transform_procs.dm @@ -161,7 +161,7 @@ O.job = "Cyborg" O.notify_ai(1) - if(O.mind.assigned_role == "Cyborg") + if(O.mind && O.mind.assigned_role == "Cyborg") if(O.mind.role_alt_title == "Android") O.mmi = new /obj/item/device/mmi/posibrain(O) else if(O.mind.role_alt_title == "Robot") diff --git a/code/modules/organs/organ.dm b/code/modules/organs/organ.dm index 62449b727c1..36fa04a0c5d 100644 --- a/code/modules/organs/organ.dm +++ b/code/modules/organs/organ.dm @@ -23,6 +23,8 @@ var/list/organ_cache = list() germ_level = 0 var/datum/dna/dna var/datum/species/species + var/needs_preservation_update = 1 + var/is_in_freezer = 0 /obj/item/organ/Destroy() if(!owner) @@ -106,8 +108,7 @@ var/list/organ_cache = list() if(status & ORGAN_DEAD) return - // Don't process if we're in a freezer, an MMI or a stasis bag. //TODO: ambient temperature? - if(istype(loc,/obj/item/device/mmi) || istype(loc,/obj/item/bodybag/cryobag) || istype(loc,/obj/structure/closet/crate/freezer)) + if(is_preserved()) return //Process infections @@ -121,8 +122,11 @@ var/list/organ_cache = list() if(B && prob(40)) reagents.remove_reagent("blood",0.1) blood_splatter(src,B,1) - - germ_level += rand(2,6) + // Maybe scale it down a bit, have it REALLY kick in once past the basic infection threshold + // Another mercy for surgeons preparing transplant organs + germ_level++ + if(germ_level >= INFECTION_LEVEL_ONE) + germ_level += rand(2,6) if(germ_level >= INFECTION_LEVEL_TWO) germ_level += rand(2,6) if(germ_level >= INFECTION_LEVEL_THREE) @@ -140,6 +144,32 @@ var/list/organ_cache = list() if(damage >= max_damage) die() +/obj/item/organ/proc/is_preserved() + if(istype(loc,/obj/item/device/mmi)) + germ_level = max(0, germ_level - 1) // So a brain can slowly recover from being left out of an MMI + return 1 + if(is_found_within(/obj/item/bodybag/cryobag)) + return 1 + if(is_found_within(/obj/structure/closet/crate/freezer)) + return 1 + if(istype(loc,/turf)) + if(needs_preservation_update) + // I don't want to loop through everything in the tile constantly, especially since it'll be a pile of organs + // if the virologist releases gibbingtons again or something + // There's probably a much less silly way of doing this, but BYOND native algorithms are stupidly naive + is_in_freezer = 0 + for(var/obj/structure/closet/crate/freezer/F in loc.contents) + if(F.opened) + is_in_freezer = 1 // on the same tile, close enough, should keep organs much fresher on avg + break + needs_preservation_update = 0 + spawn(100) // (100 / 10) once every 10 seconds + needs_preservation_update = 1 + return is_in_freezer // I'd like static varibles, please + + // You can do your cool location temperature organ preserving effects here! + return 0 + /obj/item/organ/examine(mob/user) ..(user) if(status & ORGAN_DEAD) @@ -175,6 +205,7 @@ var/list/organ_cache = list() /obj/item/organ/proc/rejuvenate() damage = 0 + germ_level = 0 /obj/item/organ/proc/is_damaged() return damage > 0 @@ -189,15 +220,16 @@ var/list/organ_cache = list() /obj/item/organ/proc/handle_antibiotics() var/antibiotics = owner.reagents.get_reagent_amount("spaceacillin") - if (!germ_level || antibiotics < 5) + if (!germ_level || antibiotics <= 0.4) return if (germ_level < INFECTION_LEVEL_ONE) germ_level = 0 //cure instantly else if (germ_level < INFECTION_LEVEL_TWO) - germ_level -= 6 //at germ_level == 500, this should cure the infection in a minute + germ_level -= 24 //at germ_level == 500, this should cure the infection in 15 seconds else - germ_level -= 2 //at germ_level == 1000, this will cure the infection in 5 minutes + germ_level -= 8 // at germ_level == 1000, this will cure the infection in 1 minute, 15 seconds + // Let's not drag this on, medbay has only so much antibiotics //Adds autopsy data for used_weapon. /obj/item/organ/proc/add_autopsy_data(var/used_weapon, var/damage) diff --git a/code/modules/organs/organ_external.dm b/code/modules/organs/organ_external.dm index e1ab07b028e..f6c08a20d84 100644 --- a/code/modules/organs/organ_external.dm +++ b/code/modules/organs/organ_external.dm @@ -79,24 +79,28 @@ switch(stage) if(0) if(istype(W,/obj/item/weapon/scalpel)) + spread_germs_to_organ(src,user) user.visible_message("[user] cuts [src] open with [W]!") stage++ return if(1) if(istype(W,/obj/item/weapon/retractor)) + spread_germs_to_organ(src,user) user.visible_message("[user] cracks [src] open like an egg with [W]!") stage++ return if(2) if(istype(W,/obj/item/weapon/hemostat)) + spread_germs_to_organ(src,user) if(contents.len) var/obj/item/removing = pick(contents) removing.loc = get_turf(user.loc) var/obj/item/organ/O = removing if(istype(O)) O.status |= ORGAN_CUT_AWAY + spread_germs_to_organ(O,user) // This wouldn't be any cleaner than the actual surgery O.removed(user) - else if(!(user.l_hand && user.r_hand)) + if(!(user.l_hand && user.r_hand)) user.put_in_hands(removing) user.visible_message("[user] extracts [removing] from [src] with [W]!") else diff --git a/code/modules/organs/subtypes/standard.dm b/code/modules/organs/subtypes/standard.dm index c11b8e028ab..b82df310488 100644 --- a/code/modules/organs/subtypes/standard.dm +++ b/code/modules/organs/subtypes/standard.dm @@ -137,6 +137,8 @@ /obj/item/organ/external/head/removed() if(owner) + if(!istype(dna)) + dna = owner.dna.Clone() name = "[dna.real_name]'s head" owner.unEquip(owner.glasses) owner.unEquip(owner.head) diff --git a/code/modules/surgery/organs_internal.dm b/code/modules/surgery/organs_internal.dm index f3a5abd2ac8..97f639e76d3 100644 --- a/code/modules/surgery/organs_internal.dm +++ b/code/modules/surgery/organs_internal.dm @@ -136,6 +136,7 @@ for(var/obj/item/organ/I in affected.internal_organs) if(I && I.damage > 0) if(I.robotic < 2) + spread_germs_to_organ(I, user) user.visible_message("[user] starts treating damage to [target]'s [I.name] with [tool_name].", \ "You start treating damage to [target]'s [I.name] with [tool_name]." ) @@ -238,6 +239,7 @@ var/obj/item/organ/I = target.op_stage.organ_ref if(I && istype(I)) + spread_germs_to_organ(I, user) I.status |= ORGAN_CUT_AWAY fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) @@ -297,6 +299,7 @@ if(target.op_stage.current_organ) var/obj/item/organ/O = target.op_stage.organ_ref if(O && istype(O)) + spread_germs_to_organ(O, user) O.removed(user) fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) @@ -376,6 +379,7 @@ var/obj/item/organ/O = tool if(istype(O)) O.replaced(target,affected) + spread_germs_to_organ(O, user) fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) user.visible_message("\red [user]'s hand slips, damaging \the [tool]!", \ @@ -433,6 +437,7 @@ var/obj/item/organ/I = target.op_stage.organ_ref if(I && istype(I)) I.status &= ~ORGAN_CUT_AWAY + spread_germs_to_organ(I, user) fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) var/obj/item/organ/external/affected = target.get_organ(target_zone) diff --git a/code/modules/surgery/surgery.dm b/code/modules/surgery/surgery.dm index 6b7d0fe1456..e71df599342 100644 --- a/code/modules/surgery/surgery.dm +++ b/code/modules/surgery/surgery.dm @@ -66,7 +66,7 @@ /datum/surgery_step/proc/fail_step(mob/living/user, mob/living/carbon/human/target, target_zone, obj/item/tool) return null -/proc/spread_germs_to_organ(obj/item/organ/external/E, mob/living/carbon/human/user) +/proc/spread_germs_to_organ(obj/item/organ/E, mob/living/carbon/human/user) if(!istype(user) || !istype(E)) return var/germ_level = user.germ_level