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