From 0c8ed8999239654274166985dc85a69897d0d58a Mon Sep 17 00:00:00 2001 From: Ghom <42542238+Ghommie@users.noreply.github.com> Date: Sun, 2 Feb 2020 19:08:57 +0100 Subject: [PATCH 1/2] Merge pull request #10705 from kevinz000/revert_poojawa_blood_1 Cleanup/Partial revert of RGB blood ""rework"" --- code/_globalvars/lists/mobs.dm | 11 +- .../effects/decals/cleanable/aliens.dm | 148 +- .../objects/effects/decals/cleanable/gibs.dm | 18 +- code/game/objects/items/devices/scanners.dm | 1684 ++++++++--------- code/game/objects/items/stacks/stack.dm | 1 - code/game/objects/items/twohanded.dm | 1 - code/modules/detectivework/detective_work.dm | 1 - code/modules/surgery/bodyparts/bodyparts.dm | 2 +- code/modules/surgery/bodyparts/helpers.dm | 4 - code/modules/surgery/organs/heart.dm | 5 - .../modules/reagents/reagents/cit_reagents.dm | 2 +- 11 files changed, 934 insertions(+), 943 deletions(-) diff --git a/code/_globalvars/lists/mobs.dm b/code/_globalvars/lists/mobs.dm index 0a28cdf3..b08d7ad2 100644 --- a/code/_globalvars/lists/mobs.dm +++ b/code/_globalvars/lists/mobs.dm @@ -109,12 +109,7 @@ GLOBAL_LIST_INIT(all_types_bloods,list( "BUG" )) -GLOBAL_LIST_INIT(blood_types, list( - "blood", - "jellyblood" - )) - -GLOBAL_LIST_INIT(blood_id_types, list( - "blood" = /datum/reagent/blood, - "jellyblood" = /datum/reagent/blood/jellyblood +GLOBAL_LIST_INIT(blood_reagent_types, list( + /datum/reagent/blood, + /datum/reagent/blood/jellyblood )) diff --git a/code/game/objects/effects/decals/cleanable/aliens.dm b/code/game/objects/effects/decals/cleanable/aliens.dm index 676581a2..04c30894 100644 --- a/code/game/objects/effects/decals/cleanable/aliens.dm +++ b/code/game/objects/effects/decals/cleanable/aliens.dm @@ -1,71 +1,77 @@ -// Note: BYOND is object oriented. There is no reason for this to be copy/pasted blood code. - -/obj/effect/decal/cleanable/blood/xeno - name = "xeno blood" - desc = "It's green and acidic. It looks like... blood?" - color = BLOOD_COLOR_XENO - -/obj/effect/decal/cleanable/blood/splatter/xeno - color = BLOOD_COLOR_XENO - -/obj/effect/decal/cleanable/blood/gibs/xeno - color = BLOOD_COLOR_XENO - gibs_reagent_id = /datum/reagent/liquidgibs/xeno - gibs_bloodtype = "X*" - -/obj/effect/decal/cleanable/blood/gibs/xeno/Initialize(mapload, list/datum/disease/diseases) - . = ..() - update_icon() - -/obj/effect/decal/cleanable/blood/gibs/xeno/update_icon() - add_atom_colour(blood_DNA_to_color(), FIXED_COLOUR_PRIORITY) - cut_overlays() - var/mutable_appearance/flesh = mutable_appearance(icon, "[icon_state]x_flesh") - flesh.appearance_flags = RESET_COLOR - flesh.color = body_colors - add_overlay(flesh) - -/obj/effect/decal/cleanable/blood/gibs/xeno/streak(list/directions) - set waitfor = FALSE - var/list/diseases = list() - SEND_SIGNAL(src, COMSIG_GIBS_STREAK, directions, diseases) - var/direction = pick(directions) - for(var/i in 0 to pick(0, 200; 1, 150; 2, 50)) - sleep(2) - if(i > 0) - var/obj/effect/decal/cleanable/blood/splatter/xeno/splat = new /obj/effect/decal/cleanable/blood/splatter/xeno(loc, diseases) - splat.transfer_blood_dna(blood_DNA, diseases) - if(!step_to(src, get_step(src, direction), 0)) - break - -/obj/effect/decal/cleanable/blood/gibs/xeno/up - random_icon_states = list("gib1", "gib2", "gib3", "gib4", "gib5", "gib6","gibup1","gibup1","gibup1") - -/obj/effect/decal/cleanable/blood/gibs/xeno/down - random_icon_states = list("gib1", "gib2", "gib3", "gib4", "gib5", "gib6","gibdown1","gibdown1","gibdown1") - -/obj/effect/decal/cleanable/blood/gibs/xeno/body - random_icon_states = list("gibhead", "gibtorso") - -/obj/effect/decal/cleanable/blood/gibs/xeno/torso - random_icon_states = list("gibtorso") - -/obj/effect/decal/cleanable/blood/gibs/xeno/limb - random_icon_states = list("gibleg", "gibarm") - -/obj/effect/decal/cleanable/blood/gibs/xeno/core - random_icon_states = list("gibmid1", "gibmid2", "gibmid3") - -/obj/effect/decal/cleanable/blood/gibs/xeno/larva - random_icon_states = list("xgiblarva1", "xgiblarva2") - -/obj/effect/decal/cleanable/blood/gibs/xeno/larva/body - random_icon_states = list("xgiblarvahead", "xgiblarvatorso") - -/obj/effect/decal/cleanable/blood/xtracks - icon_state = "tracks" - random_icon_states = null - -/obj/effect/decal/cleanable/blood/xtracks/Initialize() - add_blood_DNA(list("UNKNOWN DNA" = "X*")) - . = ..() +// Note: BYOND is object oriented. There is no reason for this to be copy/pasted blood code. + +/obj/effect/decal/cleanable/blood/xeno + name = "xeno blood" + desc = "It's green and acidic. It looks like... blood?" + color = BLOOD_COLOR_XENO + +/obj/effect/decal/cleanable/blood/splatter/xeno + color = BLOOD_COLOR_XENO + +/obj/effect/decal/cleanable/blood/gibs/xeno + color = BLOOD_COLOR_XENO + gibs_reagent_id = /datum/reagent/liquidgibs/xeno + gibs_bloodtype = "X*" + +/obj/effect/decal/cleanable/blood/gibs/xeno/Initialize(mapload, list/datum/disease/diseases) + . = ..() + update_icon() + +/obj/effect/decal/cleanable/blood/gibs/xeno/update_icon() + add_atom_colour(blood_DNA_to_color(), FIXED_COLOUR_PRIORITY) + cut_overlays() + var/mutable_appearance/flesh = mutable_appearance(icon, "[icon_state]x_flesh") + flesh.appearance_flags = RESET_COLOR + flesh.color = body_colors + add_overlay(flesh) + +/obj/effect/decal/cleanable/blood/gibs/xeno/streak(list/directions) + set waitfor = FALSE + var/list/diseases = list() + SEND_SIGNAL(src, COMSIG_GIBS_STREAK, directions, diseases) + var/direction = pick(directions) + var/dist = 0 + if(prob(50)) //yes this and the one below are different for a reason. + if(prob(25)) + dist = 2 + else + dist = 1 + if(dist) + for(var/i in 1 to dist) + sleep(2) + var/obj/effect/decal/cleanable/blood/splatter/xeno/splat = new /obj/effect/decal/cleanable/blood/splatter/xeno(loc, diseases) + splat.transfer_blood_dna(blood_DNA, diseases) + if(!step_to(src, get_step(src, direction), 0)) + break + +/obj/effect/decal/cleanable/blood/gibs/xeno/up + random_icon_states = list("gib1", "gib2", "gib3", "gib4", "gib5", "gib6","gibup1","gibup1","gibup1") + +/obj/effect/decal/cleanable/blood/gibs/xeno/down + random_icon_states = list("gib1", "gib2", "gib3", "gib4", "gib5", "gib6","gibdown1","gibdown1","gibdown1") + +/obj/effect/decal/cleanable/blood/gibs/xeno/body + random_icon_states = list("gibhead", "gibtorso") + +/obj/effect/decal/cleanable/blood/gibs/xeno/torso + random_icon_states = list("gibtorso") + +/obj/effect/decal/cleanable/blood/gibs/xeno/limb + random_icon_states = list("gibleg", "gibarm") + +/obj/effect/decal/cleanable/blood/gibs/xeno/core + random_icon_states = list("gibmid1", "gibmid2", "gibmid3") + +/obj/effect/decal/cleanable/blood/gibs/xeno/larva + random_icon_states = list("xgiblarva1", "xgiblarva2") + +/obj/effect/decal/cleanable/blood/gibs/xeno/larva/body + random_icon_states = list("xgiblarvahead", "xgiblarvatorso") + +/obj/effect/decal/cleanable/blood/xtracks + icon_state = "tracks" + random_icon_states = null + +/obj/effect/decal/cleanable/blood/xtracks/Initialize() + add_blood_DNA(list("UNKNOWN DNA" = "X*")) + . = ..() diff --git a/code/game/objects/effects/decals/cleanable/gibs.dm b/code/game/objects/effects/decals/cleanable/gibs.dm index 1a235e4d..7df0153d 100644 --- a/code/game/objects/effects/decals/cleanable/gibs.dm +++ b/code/game/objects/effects/decals/cleanable/gibs.dm @@ -5,7 +5,7 @@ layer = LOW_OBJ_LAYER random_icon_states = list("gib1", "gib2", "gib3", "gib4", "gib5", "gib6") mergeable_decal = FALSE - bloodiness = 0 + bloodiness = 0 //This isn't supposed to be bloody. var/body_colors = "#e3ba84" //a default color just in case. var/gibs_reagent_id = /datum/reagent/liquidgibs var/gibs_bloodtype = "A+" @@ -20,7 +20,6 @@ add_blood_DNA(list("Non-human DNA" = gibs_bloodtype), diseases) update_icon() - /obj/effect/decal/cleanable/blood/gibs/update_icon() add_atom_colour(blood_DNA_to_color(), FIXED_COLOUR_PRIORITY) cut_overlays() @@ -45,13 +44,18 @@ var/list/diseases = list() SEND_SIGNAL(src, COMSIG_GIBS_STREAK, directions, diseases) var/direction = pick(directions) - for(var/i in 0 to pick(0, 200; 1, 150; 2, 50)) - sleep(2) - if(i > 0) + var/dist = 0 + if(prob(50)) //yes this and the one below are different for a reason. + if(prob(25)) + dist = 2 + else + dist = 1 + if(dist) + for(var/i in 1 to dist) var/obj/effect/decal/cleanable/blood/splatter/splat = new /obj/effect/decal/cleanable/blood/splatter(loc, diseases) splat.transfer_blood_dna(blood_DNA, diseases) - if(!step_to(src, get_step(src, direction), 0)) - break + if(!step_to(src, get_step(src, direction), 0)) + break /obj/effect/decal/cleanable/blood/gibs/up random_icon_states = list("gib1", "gib2", "gib3", "gib4", "gib5", "gib6","gibup1","gibup1","gibup1") diff --git a/code/game/objects/items/devices/scanners.dm b/code/game/objects/items/devices/scanners.dm index c54f9aba..4a8a8018 100644 --- a/code/game/objects/items/devices/scanners.dm +++ b/code/game/objects/items/devices/scanners.dm @@ -1,843 +1,841 @@ - -/* - -CONTAINS: -T-RAY -HEALTH ANALYZER -GAS ANALYZER -SLIME SCANNER -NANITE SCANNER -GENE SCANNER - -*/ -/obj/item/t_scanner - name = "\improper T-ray scanner" - desc = "A terahertz-ray emitter and scanner used to detect underfloor objects such as cables and pipes." - icon = 'icons/obj/device.dmi' - icon_state = "t-ray0" - var/on = FALSE - slot_flags = ITEM_SLOT_BELT - w_class = WEIGHT_CLASS_SMALL - item_state = "electronic" - lefthand_file = 'icons/mob/inhands/misc/devices_lefthand.dmi' - righthand_file = 'icons/mob/inhands/misc/devices_righthand.dmi' - materials = list(MAT_METAL=150) - -/obj/item/t_scanner/suicide_act(mob/living/carbon/user) - user.visible_message("[user] begins to emit terahertz-rays into [user.p_their()] brain with [src]! It looks like [user.p_theyre()] trying to commit suicide!") - return TOXLOSS - -/obj/item/t_scanner/attack_self(mob/user) - - on = !on - icon_state = copytext(icon_state, 1, length(icon_state))+"[on]" - - if(on) - START_PROCESSING(SSobj, src) - -/obj/item/t_scanner/process() - if(!on) - STOP_PROCESSING(SSobj, src) - return null - scan() - -/obj/item/t_scanner/proc/scan() - t_ray_scan(loc) - -/proc/t_ray_scan(mob/viewer, flick_time = 8, distance = 3) - if(!ismob(viewer) || !viewer.client) - return - var/list/t_ray_images = list() - for(var/obj/O in orange(distance, viewer) ) - if(O.level != 1) - continue - - if(O.invisibility == INVISIBILITY_MAXIMUM) - var/image/I = new(loc = get_turf(O)) - var/mutable_appearance/MA = new(O) - MA.alpha = 128 - MA.dir = O.dir - I.appearance = MA - t_ray_images += I - if(t_ray_images.len) - flick_overlay(t_ray_images, list(viewer.client), flick_time) - -/obj/item/healthanalyzer - name = "health analyzer" - icon = 'icons/obj/device.dmi' - icon_state = "health" - item_state = "healthanalyzer" - lefthand_file = 'icons/mob/inhands/equipment/medical_lefthand.dmi' - righthand_file = 'icons/mob/inhands/equipment/medical_righthand.dmi' - desc = "A hand-held body scanner able to distinguish vital signs of the subject." - flags_1 = CONDUCT_1 - item_flags = NOBLUDGEON - slot_flags = ITEM_SLOT_BELT - throwforce = 3 - w_class = WEIGHT_CLASS_TINY - throw_speed = 3 - throw_range = 7 - materials = list(MAT_METAL=200) - var/mode = 1 - var/scanmode = 0 - var/advanced = FALSE - -/obj/item/healthanalyzer/suicide_act(mob/living/carbon/user) - user.visible_message("[user] begins to analyze [user.p_them()]self with [src]! The display shows that [user.p_theyre()] dead!") - return BRUTELOSS - -/obj/item/healthanalyzer/attack_self(mob/user) - if(!scanmode) - to_chat(user, "You switch the health analyzer to scan chemical contents.") - scanmode = 1 - else - to_chat(user, "You switch the health analyzer to check physical health.") - scanmode = 0 - -/obj/item/healthanalyzer/attack(mob/living/M, mob/living/carbon/human/user) - - // Clumsiness/brain damage check - if ((HAS_TRAIT(user, TRAIT_CLUMSY) || HAS_TRAIT(user, TRAIT_DUMB)) && prob(50)) - to_chat(user, "You stupidly try to analyze the floor's vitals!") - user.visible_message("[user] has analyzed the floor's vitals!") - var/msg = "*---------*\nAnalyzing results for The floor:\n\tOverall status: Healthy\n" - msg += "Key: Suffocation/Toxin/Burn/Brute\n" - msg += "\tDamage specifics: 0-0-0-0\n" - msg += "Body temperature: ???\n" - msg += "*---------*" - to_chat(user, msg) - return - - user.visible_message("[user] has analyzed [M]'s vitals.") - - if(scanmode == 0) - healthscan(user, M, mode, advanced) - else if(scanmode == 1) - chemscan(user, M) - - add_fingerprint(user) - - -// Used by the PDA medical scanner too -/proc/healthscan(mob/user, mob/living/M, mode = 1, advanced = FALSE) - if(isliving(user) && (user.incapacitated() || user.eye_blind)) - return - //Damage specifics - var/oxy_loss = M.getOxyLoss() - var/tox_loss = M.getToxLoss() - var/fire_loss = M.getFireLoss() - var/brute_loss = M.getBruteLoss() - var/mob_status = (M.stat == DEAD ? "Deceased" : "[round(M.health/M.maxHealth,0.01)*100] % healthy") - - if(HAS_TRAIT(M, TRAIT_FAKEDEATH) && !advanced) - mob_status = "Deceased" - oxy_loss = max(rand(1, 40), oxy_loss, (300 - (tox_loss + fire_loss + brute_loss))) // Random oxygen loss - - if(ishuman(M)) - var/mob/living/carbon/human/H = M - if(H.undergoing_cardiac_arrest() && H.stat != DEAD) - to_chat(user, "Subject suffering from heart attack: Apply defibrillation or other electric shock immediately!") - if(H.undergoing_liver_failure() && H.stat != DEAD) //might be depreciated BUG_PROBABLE_CAUSE - to_chat(user, "Subject is suffering from liver failure: Apply Corazone and begin a liver transplant immediately!") - - var/msg = "*---------*\nAnalyzing results for [M]:\n\tOverall status: [mob_status]\n" - - // Damage descriptions - if(brute_loss > 10) - msg += "\t[brute_loss > 50 ? "Severe" : "Minor"] tissue damage detected.\n" - if(fire_loss > 10) - msg += "\t[fire_loss > 50 ? "Severe" : "Minor"] burn damage detected.\n" - if(oxy_loss > 10) - msg += "\t[oxy_loss > 50 ? "Severe" : "Minor"] oxygen deprivation detected.\n" - if(tox_loss > 10) - msg += "\t[tox_loss > 50 ? "Severe" : "Minor"] amount of toxin damage detected.\n" - if(M.getStaminaLoss()) - msg += "\tSubject appears to be suffering from fatigue.\n" - if(advanced) - msg += "\tFatigue Level: [M.getStaminaLoss()]%.\n" - if (M.getCloneLoss()) - msg += "\tSubject appears to have [M.getCloneLoss() > 30 ? "Severe" : "Minor"] cellular damage.\n" - if(advanced) - msg += "\tCellular Damage Level: [M.getCloneLoss()].\n" - if (!M.getorgan(/obj/item/organ/brain)) - to_chat(user, "\tSubject lacks a brain.") //Unsure how this won't proc for 50% of the cit playerbase (This is a joke everyone on cit a cute.) - if(ishuman(M) && advanced) // Should I make this not advanced? - var/mob/living/carbon/human/H = M - var/obj/item/organ/liver/L = H.getorganslot("liver") - if(L) - if(L.swelling > 20) - msg += "\tSubject is suffering from an enlarged liver.\n" //i.e. shrink their liver or give them a transplant. - else - msg += "\tSubject's liver is missing.\n" - var/obj/item/organ/tongue/T = H.getorganslot("tongue") - if(T) - if(T.damage > 40) - msg += "\tSubject is suffering from severe burn tissue on their tongue.\n" //i.e. their tongue is shot - if(T.name == "fluffy tongue") - msg += "\tSubject is suffering from a fluffified tongue. Suggested cure: Yamerol or a tongue transplant.\n" - else - msg += "\tSubject's tongue is missing.\n" - var/obj/item/organ/lungs/Lung = H.getorganslot("lungs") - if(Lung) - if(Lung.damage > 150) - msg += "\tSubject is suffering from acute emphysema leading to trouble breathing.\n" //i.e. Their lungs are shot - else - msg += "\tSubject's lungs have collapsed from trauma!\n" - var/obj/item/organ/genital/penis/P = H.getorganslot("penis") - if(P) - if(P.length>20) - msg += "\tSubject has a sizeable gentleman's organ at [P.length] inches.\n" - var/obj/item/organ/genital/breasts/Br = H.getorganslot("breasts") - if(Br) - if(Br.cached_size>5) - msg += "\tSubject has a sizeable bosom with a [Br.size] cup.\n" - if (M.getOrganLoss(ORGAN_SLOT_BRAIN) >= 200 || !M.getorgan(/obj/item/organ/brain)) - msg += "\tSubject's brain function is non-existent.\n" - else if (M.getOrganLoss(ORGAN_SLOT_BRAIN) >= 120) - msg += "\tSevere brain damage detected. Subject likely to have mental traumas.\n" - else if (M.getOrganLoss(ORGAN_SLOT_BRAIN) >= 45) - msg += "\tBrain damage detected.\n" - - if(iscarbon(M)) - - var/mob/living/carbon/C = M - if(LAZYLEN(C.get_traumas())) - var/list/trauma_text = list() - for(var/datum/brain_trauma/B in C.get_traumas()) - var/trauma_desc = "" - switch(B.resilience) - if(TRAUMA_RESILIENCE_SURGERY) - trauma_desc += "severe " - if(TRAUMA_RESILIENCE_LOBOTOMY) - trauma_desc += "deep-rooted " - if(TRAUMA_RESILIENCE_MAGIC, TRAUMA_RESILIENCE_ABSOLUTE) - trauma_desc += "permanent " - trauma_desc += B.scan_desc - trauma_text += trauma_desc - msg += "\tCerebral traumas detected: subject appears to be suffering from [english_list(trauma_text)].\n" - if(C.roundstart_quirks.len) - msg += "\tSubject has the following physiological traits: [C.get_trait_string()].\n" - if(advanced) - msg += "\tBrain Activity Level: [(200 - M.getOrganLoss(ORGAN_SLOT_BRAIN))/2]%.\n" - if(M.radiation) - msg += "\tSubject is irradiated.\n" - if(advanced) - msg += "\tRadiation Level: [M.radiation]%.\n" - - if(advanced && M.hallucinating()) - msg += "\tSubject is hallucinating.\n" - - //MKUltra - if(advanced && M.has_status_effect(/datum/status_effect/chem/enthrall)) - msg += "\tSubject has abnormal brain fuctions.\n" - - //Astrogen shenanigans - if(advanced && M.reagents.has_reagent("astral")) - if(M.mind) - msg += "\tWarning: subject may be possesed.\n" - else - msg += "\tSubject appears to be astrally projecting.\n" - - //Eyes and ears - if(advanced) - if(iscarbon(M)) - var/mob/living/carbon/C = M - var/obj/item/organ/ears/ears = C.getorganslot(ORGAN_SLOT_EARS) - msg += "\t==EAR STATUS==\n" - if(istype(ears)) - var/healthy = TRUE - if(HAS_TRAIT_FROM(C, TRAIT_DEAF, GENETIC_MUTATION)) - healthy = FALSE - msg += "\tSubject is genetically deaf.\n" - else if(HAS_TRAIT(C, TRAIT_DEAF)) - healthy = FALSE - msg += "\tSubject is deaf.\n" - else - if(ears.damage) - to_chat(user, "\tSubject has [ears.damage > ears.maxHealth ? "permanent ": "temporary "]hearing damage.") - healthy = FALSE - if(ears.deaf) - to_chat(user, "\tSubject is [ears.damage > ears.maxHealth ? "permanently ": "temporarily "] deaf.") - healthy = FALSE - if(healthy) - msg += "\tHealthy.\n" - else - msg += "\tSubject does not have ears.\n" - var/obj/item/organ/eyes/eyes = C.getorganslot(ORGAN_SLOT_EYES) - msg += "\t==EYE STATUS==\n" - if(istype(eyes)) - var/healthy = TRUE - if(HAS_TRAIT(C, TRAIT_BLIND)) - msg += "\tSubject is blind.\n" - healthy = FALSE - if(HAS_TRAIT(C, TRAIT_NEARSIGHT)) - msg += "\tSubject is nearsighted.\n" - healthy = FALSE - if(eyes.damage > 30) - msg += "\tSubject has severe eye damage.\n" - healthy = FALSE - else if(eyes.damage > 20) - msg += "\tSubject has significant eye damage.\n" - healthy = FALSE - else if(eyes.damage) - msg += "\tSubject has minor eye damage.\n" - healthy = FALSE - if(healthy) - msg += "\tHealthy.\n" - else - msg += "\tSubject does not have eyes.\n" - - - if(ishuman(M)) - var/mob/living/carbon/human/H = M - var/ldamage = H.return_liver_damage() - if(ldamage > 10) - msg += "\t[ldamage > 45 ? "Severe" : "Minor"] liver damage detected.\n" - if(advanced && H.has_dna()) - to_chat(user, "\tGenetic Stability: [H.dna.stability]%.") - // Body part damage report - if(iscarbon(M) && mode == 1) - var/mob/living/carbon/C = M - var/list/damaged = C.get_damaged_bodyparts(1,1) - if(length(damaged)>0 || oxy_loss>0 || tox_loss>0 || fire_loss>0) - msg += "\tDamage: Brute-Burn-Toxin-Suffocation\n\t\tSpecifics: [brute_loss]-[fire_loss]-[tox_loss]-[oxy_loss]\n" - for(var/obj/item/bodypart/org in damaged) - msg += "\t\t[capitalize(org.name)]: [(org.brute_dam > 0) ? "[org.brute_dam]" : "0"]-[(org.burn_dam > 0) ? "[org.burn_dam]" : "0"]\n" - - //Bones broken report! Hyperstation 13 - if(iscarbon(M) && mode == 1) - var/mob/living/carbon/C = M - for(var/X in C.bodyparts) - var/obj/item/bodypart/LB = X - var/broken = LB.broken - if(broken == 1) - msg += "\tSubjects [LB.name] is fractured!\n" - -//Organ damages report - if(ishuman(M)) - var/mob/living/carbon/human/H = M - var/minor_damage - var/major_damage - var/max_damage - var/report_organs = FALSE - - //Piece together the lists to be reported - for(var/O in H.internal_organs) - var/obj/item/organ/organ = O - if(organ.organ_flags & ORGAN_FAILING) - report_organs = TRUE //if we report one organ, we report all organs, even if the lists are empty, just for consistency - if(max_damage) - max_damage += ", " //prelude the organ if we've already reported an organ - max_damage += organ.name //this just slaps the organ name into the string of text - else - max_damage = "\tNon-Functional Organs: " //our initial statement - max_damage += organ.name - else if(organ.damage > organ.high_threshold) - report_organs = TRUE - if(major_damage) - major_damage += ", " - major_damage += organ.name - else - major_damage = "\tSeverely Damaged Organs: " - major_damage += organ.name - else if(organ.damage > organ.low_threshold) - report_organs = TRUE - if(minor_damage) - minor_damage += ", " - minor_damage += organ.name - else - minor_damage = "\tMildly Damaged Organs: " - minor_damage += organ.name - - if(report_organs) //we either finish the list, or set it to be empty if no organs were reported in that category - if(!max_damage) - max_damage = "\tNon-Functional Organs: " - else - max_damage += "" - if(!major_damage) - major_damage = "\tSeverely Damaged Organs: " - else - major_damage += "" - if(!minor_damage) - minor_damage = "\tMildly Damaged Organs: " - else - minor_damage += "" - msg += "[minor_damage]" - msg += "[major_damage]" - msg += "[max_damage]" - - // Species and body temperature - if(ishuman(M)) - var/mob/living/carbon/human/H = M - var/datum/species/S = H.dna.species - var/mutant = FALSE - if (H.dna.check_mutation(HULK)) - mutant = TRUE - else if (S.mutantlungs != initial(S.mutantlungs)) - mutant = TRUE - else if (S.mutant_brain != initial(S.mutant_brain)) - mutant = TRUE - else if (S.mutant_heart != initial(S.mutant_heart)) - mutant = TRUE - else if (S.mutanteyes != initial(S.mutanteyes)) - mutant = TRUE - else if (S.mutantears != initial(S.mutantears)) - mutant = TRUE - else if (S.mutanthands != initial(S.mutanthands)) - mutant = TRUE - else if (S.mutanttongue != initial(S.mutanttongue)) - mutant = TRUE - else if (S.mutanttail != initial(S.mutanttail)) - mutant = TRUE - else if (S.mutantliver != initial(S.mutantliver)) - mutant = TRUE - else if (S.mutantstomach != initial(S.mutantstomach)) - mutant = TRUE - - msg += "Species: [H.dna.custom_species ? H.dna.custom_species : S.name] Base: [S.name]\n" - if(mutant) - msg += "Subject has mutations present." - msg += "Body temperature: [round(M.bodytemperature-T0C,0.1)] °C ([round(M.bodytemperature*1.8-459.67,0.1)] °F)\n" - - // Time of death - if(M.tod && (M.stat == DEAD || ((HAS_TRAIT(M, TRAIT_FAKEDEATH)) && !advanced))) - msg += "Time of Death: [M.tod]\n" - var/tdelta = round(world.time - M.timeofdeath) - if(tdelta < (DEFIB_TIME_LIMIT * 10)) - msg += "Subject died [DisplayTimeText(tdelta)] ago, defibrillation may be possible!\n" - - for(var/thing in M.diseases) - var/datum/disease/D = thing - if(!(D.visibility_flags & HIDDEN_SCANNER)) - msg += "Warning: [D.form] detected\nName: [D.name].\nType: [D.spread_text].\nStage: [D.stage]/[D.max_stages].\nPossible Cure: [D.cure_text]\n" - - // Blood Level - if(M.has_dna()) - var/mob/living/carbon/C = M - var/blood_id = C.get_blood_id() - if(blood_id) - if(ishuman(C)) - var/mob/living/carbon/human/H = C - if(H.bleed_rate) - msg += "Subject is bleeding!\n" - var/blood_percent = round((C.scan_blood_volume() / (BLOOD_VOLUME_NORMAL * C.blood_ratio))*100) - var/blood_type = C.dna.blood_type - if(blood_id != ("blood" || "jellyblood"))//special blood substance - var/datum/reagent/R = GLOB.chemical_reagents_list[blood_id] - if(R) - blood_type = R.name - else - blood_type = blood_id - if(C.scan_blood_volume() <= (BLOOD_VOLUME_SAFE*C.blood_ratio) && C.scan_blood_volume() > (BLOOD_VOLUME_OKAY*C.blood_ratio)) - msg += "LOW blood level [blood_percent] %, [C.scan_blood_volume()] cl, type: [blood_type]\n" - else if(C.scan_blood_volume() <= (BLOOD_VOLUME_OKAY*C.blood_ratio)) - msg += "CRITICAL blood level [blood_percent] %, [C.scan_blood_volume()] cl, type: [blood_type]\n" - else - msg += "Blood level [blood_percent] %, [C.scan_blood_volume()] cl, type: [blood_type]\n" - - var/cyberimp_detect - for(var/obj/item/organ/cyberimp/CI in C.internal_organs) - if(CI.status == ORGAN_ROBOTIC && !CI.syndicate_implant) - cyberimp_detect += "[C.name] is modified with a [CI.name].
" - if(cyberimp_detect) - msg += "Detected cybernetic modifications:\n" - msg += "[cyberimp_detect]\n" - msg += "*---------*
" - to_chat(user, msg) - SEND_SIGNAL(M, COMSIG_NANITE_SCAN, user, FALSE) - -/proc/chemscan(mob/living/user, mob/living/M) - if(istype(M)) - if(M.reagents) - var/msg = "*---------*\n" - if(M.reagents.reagent_list.len) - var/list/datum/reagent/reagents = list() - for(var/datum/reagent/R in M.reagents.reagent_list) - if(R.chemical_flags & REAGENT_INVISIBLE) - continue - reagents += R - - if(length(reagents)) - msg += "Subject contains the following reagents:\n" - for(var/datum/reagent/R in reagents) - msg += "[R.volume] units of [R.name][R.overdosed == 1 ? " - OVERDOSING" : "."]\n" - else - msg += "Subject contains no reagents.\n" - - else - msg += "Subject contains no reagents.\n" - if(M.reagents.addiction_list.len) - msg += "Subject is addicted to the following reagents:\n" - for(var/datum/reagent/R in M.reagents.addiction_list) - msg += "[R.name]\n" - else - msg += "Subject is not addicted to any reagents.\n" - msg += "*---------*
" - to_chat(user, msg) - - var/datum/reagent/impure/fermiTox/F = M.reagents.has_reagent(/datum/reagent/impure/fermiTox) - switch(F?.volume) - if(5 to 10) - msg += "Subject contains a low amount of toxic isomers.\n" - if(10 to 25) - msg += "Subject contains toxic isomers.\n" - if(25 to 50) - msg += "Subject contains a substantial amount of toxic isomers.\n" - if(50 to 95) - msg += "Subject contains a high amount of toxic isomers.\n" - if(95 to INFINITY) - msg += "Subject contains a extremely dangerous amount of toxic isomers.\n" - -/obj/item/healthanalyzer/verb/toggle_mode() - set name = "Switch Verbosity" - set category = "Object" - - if(usr.stat || !usr.canmove || usr.restrained()) - return - - mode = !mode - switch (mode) - if(1) - to_chat(usr, "The scanner now shows specific limb damage.") - if(0) - to_chat(usr, "The scanner no longer shows limb damage.") - -/obj/item/healthanalyzer/advanced - name = "advanced health analyzer" - icon_state = "health_adv" - desc = "A hand-held body scanner able to distinguish vital signs of the subject with high accuracy." - advanced = TRUE - -/obj/item/analyzer - desc = "A hand-held environmental scanner which reports current gas levels. Alt-Click to use the built in barometer function." - name = "analyzer" - icon = 'icons/obj/device.dmi' - icon_state = "analyzer" - item_state = "analyzer" - lefthand_file = 'icons/mob/inhands/equipment/tools_lefthand.dmi' - righthand_file = 'icons/mob/inhands/equipment/tools_righthand.dmi' - w_class = WEIGHT_CLASS_SMALL - flags_1 = CONDUCT_1 - item_flags = NOBLUDGEON - slot_flags = ITEM_SLOT_BELT - throwforce = 0 - throw_speed = 3 - throw_range = 7 - tool_behaviour = TOOL_ANALYZER - materials = list(MAT_METAL=30, MAT_GLASS=20) - grind_results = list(/datum/reagent/mercury = 5, /datum/reagent/iron = 5, /datum/reagent/silicon = 5) - var/cooldown = FALSE - var/cooldown_time = 250 - var/accuracy // 0 is the best accuracy. - -/obj/item/analyzer/examine(mob/user) - . = ..() - . += "Alt-click [src] to activate the barometer function." - -/obj/item/analyzer/suicide_act(mob/living/carbon/user) - user.visible_message("[user] begins to analyze [user.p_them()]self with [src]! The display shows that [user.p_theyre()] dead!") - return BRUTELOSS - -/obj/item/analyzer/attack_self(mob/user) - add_fingerprint(user) - - if (user.stat || user.eye_blind) - return - - var/turf/location = user.loc - if(!istype(location)) - return - - var/datum/gas_mixture/environment = location.return_air() - - var/pressure = environment.return_pressure() - var/total_moles = environment.total_moles() - - to_chat(user, "Results:") - if(abs(pressure - ONE_ATMOSPHERE) < 10) - to_chat(user, "Pressure: [round(pressure, 0.01)] kPa") - else - to_chat(user, "Pressure: [round(pressure, 0.01)] kPa") - if(total_moles) - var/list/env_gases = environment.gases - - var/o2_concentration = env_gases[/datum/gas/oxygen]/total_moles - var/n2_concentration = env_gases[/datum/gas/nitrogen]/total_moles - var/co2_concentration = env_gases[/datum/gas/carbon_dioxide]/total_moles - var/plasma_concentration = env_gases[/datum/gas/plasma]/total_moles - - if(abs(n2_concentration - N2STANDARD) < 20) - to_chat(user, "Nitrogen: [round(n2_concentration*100, 0.01)] % ([round(env_gases[/datum/gas/nitrogen], 0.01)] mol)") - else - to_chat(user, "Nitrogen: [round(n2_concentration*100, 0.01)] % ([round(env_gases[/datum/gas/nitrogen], 0.01)] mol)") - - if(abs(o2_concentration - O2STANDARD) < 2) - to_chat(user, "Oxygen: [round(o2_concentration*100, 0.01)] % ([round(env_gases[/datum/gas/oxygen], 0.01)] mol)") - else - to_chat(user, "Oxygen: [round(o2_concentration*100, 0.01)] % ([round(env_gases[/datum/gas/oxygen], 0.01)] mol)") - - if(co2_concentration > 0.01) - to_chat(user, "CO2: [round(co2_concentration*100, 0.01)] % ([round(env_gases[/datum/gas/carbon_dioxide], 0.01)] mol)") - else - to_chat(user, "CO2: [round(co2_concentration*100, 0.01)] % ([round(env_gases[/datum/gas/carbon_dioxide], 0.01)] mol)") - - if(plasma_concentration > 0.005) - to_chat(user, "Plasma: [round(plasma_concentration*100, 0.01)] % ([round(env_gases[/datum/gas/plasma], 0.01)] mol)") - else - to_chat(user, "Plasma: [round(plasma_concentration*100, 0.01)] % ([round(env_gases[/datum/gas/plasma], 0.01)] mol)") - - GAS_GARBAGE_COLLECT(environment.gases) - - for(var/id in env_gases) - if(id in GLOB.hardcoded_gases) - continue - var/gas_concentration = env_gases[id]/total_moles - to_chat(user, "[GLOB.meta_gas_names[id]]: [round(gas_concentration*100, 0.01)] % ([round(env_gases[id], 0.01)] mol)") - to_chat(user, "Temperature: [round(environment.temperature-T0C, 0.01)] °C ([round(environment.temperature, 0.01)] K)") - -/obj/item/analyzer/AltClick(mob/user) //Barometer output for measuring when the next storm happens - ..() - - if(user.canUseTopic(src)) - - if(cooldown) - to_chat(user, "[src]'s barometer function is preparing itself.") - return - - var/turf/T = get_turf(user) - if(!T) - return - - playsound(src, 'sound/effects/pop.ogg', 100) - var/area/user_area = T.loc - var/datum/weather/ongoing_weather = null - - if(!user_area.outdoors) - to_chat(user, "[src]'s barometer function won't work indoors!") - return - - for(var/V in SSweather.processing) - var/datum/weather/W = V - if(W.barometer_predictable && (T.z in W.impacted_z_levels) && W.area_type == user_area.type && !(W.stage == END_STAGE)) - ongoing_weather = W - break - - if(ongoing_weather) - if((ongoing_weather.stage == MAIN_STAGE) || (ongoing_weather.stage == WIND_DOWN_STAGE)) - to_chat(user, "[src]'s barometer function can't trace anything while the storm is [ongoing_weather.stage == MAIN_STAGE ? "already here!" : "winding down."]") - return - - to_chat(user, "The next [ongoing_weather] will hit in [butchertime(ongoing_weather.next_hit_time - world.time)].") - if(ongoing_weather.aesthetic) - to_chat(user, "[src]'s barometer function says that the next storm will breeze on by.") - else - var/next_hit = SSweather.next_hit_by_zlevel["[T.z]"] - var/fixed = next_hit ? next_hit - world.time : -1 - if(fixed < 0) - to_chat(user, "[src]'s barometer function was unable to trace any weather patterns.") - else - to_chat(user, "[src]'s barometer function says a storm will land in approximately [butchertime(fixed)].") - cooldown = TRUE - addtimer(CALLBACK(src,/obj/item/analyzer/proc/ping), cooldown_time) - -/obj/item/analyzer/proc/ping() - if(isliving(loc)) - var/mob/living/L = loc - to_chat(L, "[src]'s barometer function is ready!") - playsound(src, 'sound/machines/click.ogg', 100) - cooldown = FALSE - -/obj/item/analyzer/proc/butchertime(amount) - if(!amount) - return - if(accuracy) - var/inaccurate = round(accuracy*(1/3)) - if(prob(50)) - amount -= inaccurate - if(prob(50)) - amount += inaccurate - return DisplayTimeText(max(1,amount)) - -/proc/atmosanalyzer_scan(mixture, mob/living/user, atom/target = src) - var/icon = target - user.visible_message("[user] has used the analyzer on [icon2html(icon, viewers(src))] [target].", "You use the analyzer on [icon2html(icon, user)] [target].") - to_chat(user, "Results of analysis of [icon2html(icon, user)] [target].") - - var/list/airs = islist(mixture) ? mixture : list(mixture) - for(var/g in airs) - if(airs.len > 1) //not a unary gas mixture - to_chat(user, "Node [airs.Find(g)]") - var/datum/gas_mixture/air_contents = g - - var/total_moles = air_contents.total_moles() - var/pressure = air_contents.return_pressure() - var/volume = air_contents.return_volume() //could just do mixture.volume... but safety, I guess? - var/temperature = air_contents.temperature - var/cached_scan_results = air_contents.analyzer_results - - if(total_moles > 0) - to_chat(user, "Moles: [round(total_moles, 0.01)] mol") - to_chat(user, "Volume: [volume] L") - to_chat(user, "Pressure: [round(pressure,0.01)] kPa") - - var/list/cached_gases = air_contents.gases - for(var/id in cached_gases) - var/gas_concentration = cached_gases[id]/total_moles - to_chat(user, "[GLOB.meta_gas_names[id]]: [round(gas_concentration*100, 0.01)] % ([round(cached_gases[id], 0.01)] mol)") - to_chat(user, "Temperature: [round(temperature - T0C,0.01)] °C ([round(temperature, 0.01)] K)") - - else - if(airs.len > 1) - to_chat(user, "This node is empty!") - else - to_chat(user, "[target] is empty!") - - if(cached_scan_results && cached_scan_results["fusion"]) //notify the user if a fusion reaction was detected - var/instability = round(cached_scan_results["fusion"], 0.01) - to_chat(user, "Large amounts of free neutrons detected in the air indicate that a fusion reaction took place.") - to_chat(user, "Instability of the last fusion reaction: [instability].") - return - -//slime scanner - -/obj/item/slime_scanner - name = "slime scanner" - desc = "A device that analyzes a slime's internal composition and measures its stats." - icon = 'icons/obj/device.dmi' - icon_state = "adv_spectrometer" - item_state = "analyzer" - lefthand_file = 'icons/mob/inhands/equipment/tools_lefthand.dmi' - righthand_file = 'icons/mob/inhands/equipment/tools_righthand.dmi' - w_class = WEIGHT_CLASS_SMALL - flags_1 = CONDUCT_1 - throwforce = 0 - throw_speed = 3 - throw_range = 7 - materials = list(MAT_METAL=30, MAT_GLASS=20) - -/obj/item/slime_scanner/attack(mob/living/M, mob/living/user) - if(user.stat || user.eye_blind) - return - if (!isslime(M)) - to_chat(user, "This device can only scan slimes!") - return - var/mob/living/simple_animal/slime/T = M - slime_scan(T, user) - -/proc/slime_scan(mob/living/simple_animal/slime/T, mob/living/user) - to_chat(user, "========================") - to_chat(user, "Slime scan results:") - to_chat(user, "[T.colour] [T.is_adult ? "adult" : "baby"] slime") - to_chat(user, "Nutrition: [T.nutrition]/[T.get_max_nutrition()]") - if (T.nutrition < T.get_starve_nutrition()) - to_chat(user, "Warning: slime is starving!") - else if (T.nutrition < T.get_hunger_nutrition()) - to_chat(user, "Warning: slime is hungry") - to_chat(user, "Electric change strength: [T.powerlevel]") - to_chat(user, "Health: [round(T.health/T.maxHealth,0.01)*100]%") - if (T.slime_mutation[4] == T.colour) - to_chat(user, "This slime does not evolve any further.") - else - if (T.slime_mutation[3] == T.slime_mutation[4]) - if (T.slime_mutation[2] == T.slime_mutation[1]) - to_chat(user, "Possible mutation: [T.slime_mutation[3]]") - to_chat(user, "Genetic destability: [T.mutation_chance/2] % chance of mutation on splitting") - else - to_chat(user, "Possible mutations: [T.slime_mutation[1]], [T.slime_mutation[2]], [T.slime_mutation[3]] (x2)") - to_chat(user, "Genetic destability: [T.mutation_chance] % chance of mutation on splitting") - else - to_chat(user, "Possible mutations: [T.slime_mutation[1]], [T.slime_mutation[2]], [T.slime_mutation[3]], [T.slime_mutation[4]]") - to_chat(user, "Genetic destability: [T.mutation_chance] % chance of mutation on splitting") - if (T.cores > 1) - to_chat(user, "Multiple cores detected") - to_chat(user, "Growth progress: [T.amount_grown]/[SLIME_EVOLUTION_THRESHOLD]") - if(T.effectmod) - to_chat(user, "Core mutation in progress: [T.effectmod]") - to_chat(user, "Progress in core mutation: [T.applied] / [SLIME_EXTRACT_CROSSING_REQUIRED]") - to_chat(user, "========================") - - -/obj/item/nanite_scanner - name = "nanite scanner" - icon = 'icons/obj/device.dmi' - icon_state = "nanite_scanner" - item_state = "nanite_remote" - lefthand_file = 'icons/mob/inhands/equipment/medical_lefthand.dmi' - righthand_file = 'icons/mob/inhands/equipment/medical_righthand.dmi' - desc = "A hand-held body scanner able to detect nanites and their programming." - flags_1 = CONDUCT_1 - item_flags = NOBLUDGEON - slot_flags = ITEM_SLOT_BELT - throwforce = 3 - w_class = WEIGHT_CLASS_TINY - throw_speed = 3 - throw_range = 7 - materials = list(MAT_METAL=200) - -/obj/item/nanite_scanner/attack(mob/living/M, mob/living/carbon/human/user) - user.visible_message("[user] has analyzed [M]'s nanites.") - - add_fingerprint(user) - - var/response = SEND_SIGNAL(M, COMSIG_NANITE_SCAN, user, TRUE) - if(!response) - to_chat(user, "No nanites detected in the subject.") - -/obj/item/sequence_scanner - name = "genetic sequence scanner" - icon = 'icons/obj/device.dmi' - icon_state = "gene" - item_state = "healthanalyzer" - lefthand_file = 'icons/mob/inhands/equipment/medical_lefthand.dmi' - righthand_file = 'icons/mob/inhands/equipment/medical_righthand.dmi' - desc = "A hand-held scanner able to swiftly scan someone for potential mutations. Hold near a DNA console to update from their database." - flags_1 = CONDUCT_1 - item_flags = NOBLUDGEON - slot_flags = ITEM_SLOT_BELT - throwforce = 3 - w_class = WEIGHT_CLASS_TINY - throw_speed = 3 - throw_range = 7 - materials = list(MAT_METAL=200) - var/list/discovered = list() //hit a dna console to update the scanners database - -/obj/item/sequence_scanner/attack(mob/living/M, mob/living/carbon/human/user) - user.visible_message("[user] has analyzed [M]'s genetic sequence.") - - add_fingerprint(user) - - gene_scan(M, user, src) - -/obj/item/sequence_scanner/afterattack(obj/O, mob/user, proximity) - . = ..() - if(!istype(O) || !proximity) - return - - if(istype(O, /obj/machinery/computer/scan_consolenew)) - var/obj/machinery/computer/scan_consolenew/C = O - if(C.stored_research) - to_chat(user, "[name] database updated.") - discovered = C.stored_research.discovered_mutations - else - to_chat(user,"No database to update from.") - -/proc/gene_scan(mob/living/carbon/C, mob/living/user, obj/item/sequence_scanner/G) - if(!iscarbon(C) || !C.has_dna()) - return - to_chat(user, "[C.name]'s potential mutations.") - for(var/A in C.dna.mutation_index) - var/datum/mutation/human/HM = GET_INITIALIZED_MUTATION(A) - var/mut_name - if(G && (A in G.discovered)) - mut_name = "[HM.name] ([HM.alias])" - else - mut_name = HM.alias - var/temp = GET_GENE_STRING(HM.type, C.dna) - var/display - for(var/i in 0 to length(temp) / DNA_MUTATION_BLOCKS-1) - if(i) - display += "-" - display += copytext(temp, 1 + i*DNA_MUTATION_BLOCKS, DNA_MUTATION_BLOCKS*(1+i) + 1) - - - to_chat(user, "- [mut_name] > [display]") + +/* + +CONTAINS: +T-RAY +HEALTH ANALYZER +GAS ANALYZER +SLIME SCANNER +NANITE SCANNER +GENE SCANNER + +*/ +/obj/item/t_scanner + name = "\improper T-ray scanner" + desc = "A terahertz-ray emitter and scanner used to detect underfloor objects such as cables and pipes." + icon = 'icons/obj/device.dmi' + icon_state = "t-ray0" + var/on = FALSE + slot_flags = ITEM_SLOT_BELT + w_class = WEIGHT_CLASS_SMALL + item_state = "electronic" + lefthand_file = 'icons/mob/inhands/misc/devices_lefthand.dmi' + righthand_file = 'icons/mob/inhands/misc/devices_righthand.dmi' + materials = list(MAT_METAL=150) + +/obj/item/t_scanner/suicide_act(mob/living/carbon/user) + user.visible_message("[user] begins to emit terahertz-rays into [user.p_their()] brain with [src]! It looks like [user.p_theyre()] trying to commit suicide!") + return TOXLOSS + +/obj/item/t_scanner/attack_self(mob/user) + + on = !on + icon_state = copytext(icon_state, 1, length(icon_state))+"[on]" + + if(on) + START_PROCESSING(SSobj, src) + +/obj/item/t_scanner/process() + if(!on) + STOP_PROCESSING(SSobj, src) + return null + scan() + +/obj/item/t_scanner/proc/scan() + t_ray_scan(loc) + +/proc/t_ray_scan(mob/viewer, flick_time = 8, distance = 3) + if(!ismob(viewer) || !viewer.client) + return + var/list/t_ray_images = list() + for(var/obj/O in orange(distance, viewer) ) + if(O.level != 1) + continue + + if(O.invisibility == INVISIBILITY_MAXIMUM) + var/image/I = new(loc = get_turf(O)) + var/mutable_appearance/MA = new(O) + MA.alpha = 128 + MA.dir = O.dir + I.appearance = MA + t_ray_images += I + if(t_ray_images.len) + flick_overlay(t_ray_images, list(viewer.client), flick_time) + +/obj/item/healthanalyzer + name = "health analyzer" + icon = 'icons/obj/device.dmi' + icon_state = "health" + item_state = "healthanalyzer" + lefthand_file = 'icons/mob/inhands/equipment/medical_lefthand.dmi' + righthand_file = 'icons/mob/inhands/equipment/medical_righthand.dmi' + desc = "A hand-held body scanner able to distinguish vital signs of the subject." + flags_1 = CONDUCT_1 + item_flags = NOBLUDGEON + slot_flags = ITEM_SLOT_BELT + throwforce = 3 + w_class = WEIGHT_CLASS_TINY + throw_speed = 3 + throw_range = 7 + materials = list(MAT_METAL=200) + var/mode = 1 + var/scanmode = 0 + var/advanced = FALSE + +/obj/item/healthanalyzer/suicide_act(mob/living/carbon/user) + user.visible_message("[user] begins to analyze [user.p_them()]self with [src]! The display shows that [user.p_theyre()] dead!") + return BRUTELOSS + +/obj/item/healthanalyzer/attack_self(mob/user) + if(!scanmode) + to_chat(user, "You switch the health analyzer to scan chemical contents.") + scanmode = 1 + else + to_chat(user, "You switch the health analyzer to check physical health.") + scanmode = 0 + +/obj/item/healthanalyzer/attack(mob/living/M, mob/living/carbon/human/user) + + // Clumsiness/brain damage check + if ((HAS_TRAIT(user, TRAIT_CLUMSY) || HAS_TRAIT(user, TRAIT_DUMB)) && prob(50)) + to_chat(user, "You stupidly try to analyze the floor's vitals!") + user.visible_message("[user] has analyzed the floor's vitals!") + var/msg = "*---------*\nAnalyzing results for The floor:\n\tOverall status: Healthy\n" + msg += "Key: Suffocation/Toxin/Burn/Brute\n" + msg += "\tDamage specifics: 0-0-0-0\n" + msg += "Body temperature: ???\n" + msg += "*---------*" + to_chat(user, msg) + return + + user.visible_message("[user] has analyzed [M]'s vitals.") + + if(scanmode == 0) + healthscan(user, M, mode, advanced) + else if(scanmode == 1) + chemscan(user, M) + + add_fingerprint(user) + + +// Used by the PDA medical scanner too +/proc/healthscan(mob/user, mob/living/M, mode = 1, advanced = FALSE) + if(isliving(user) && (user.incapacitated() || user.eye_blind)) + return + //Damage specifics + var/oxy_loss = M.getOxyLoss() + var/tox_loss = M.getToxLoss() + var/fire_loss = M.getFireLoss() + var/brute_loss = M.getBruteLoss() + var/mob_status = (M.stat == DEAD ? "Deceased" : "[round(M.health/M.maxHealth,0.01)*100] % healthy") + + if(HAS_TRAIT(M, TRAIT_FAKEDEATH) && !advanced) + mob_status = "Deceased" + oxy_loss = max(rand(1, 40), oxy_loss, (300 - (tox_loss + fire_loss + brute_loss))) // Random oxygen loss + + if(ishuman(M)) + var/mob/living/carbon/human/H = M + if(H.undergoing_cardiac_arrest() && H.stat != DEAD) + to_chat(user, "Subject suffering from heart attack: Apply defibrillation or other electric shock immediately!") + if(H.undergoing_liver_failure() && H.stat != DEAD) //might be depreciated BUG_PROBABLE_CAUSE + to_chat(user, "Subject is suffering from liver failure: Apply Corazone and begin a liver transplant immediately!") + + var/msg = "*---------*\nAnalyzing results for [M]:\n\tOverall status: [mob_status]\n" + + // Damage descriptions + if(brute_loss > 10) + msg += "\t[brute_loss > 50 ? "Severe" : "Minor"] tissue damage detected.\n" + if(fire_loss > 10) + msg += "\t[fire_loss > 50 ? "Severe" : "Minor"] burn damage detected.\n" + if(oxy_loss > 10) + msg += "\t[oxy_loss > 50 ? "Severe" : "Minor"] oxygen deprivation detected.\n" + if(tox_loss > 10) + msg += "\t[tox_loss > 50 ? "Severe" : "Minor"] amount of toxin damage detected.\n" + if(M.getStaminaLoss()) + msg += "\tSubject appears to be suffering from fatigue.\n" + if(advanced) + msg += "\tFatigue Level: [M.getStaminaLoss()]%.\n" + if (M.getCloneLoss()) + msg += "\tSubject appears to have [M.getCloneLoss() > 30 ? "Severe" : "Minor"] cellular damage.\n" + if(advanced) + msg += "\tCellular Damage Level: [M.getCloneLoss()].\n" + if (!M.getorgan(/obj/item/organ/brain)) + to_chat(user, "\tSubject lacks a brain.") //Unsure how this won't proc for 50% of the cit playerbase (This is a joke everyone on cit a cute.) + if(ishuman(M) && advanced) // Should I make this not advanced? + var/mob/living/carbon/human/H = M + var/obj/item/organ/liver/L = H.getorganslot("liver") + if(L) + if(L.swelling > 20) + msg += "\tSubject is suffering from an enlarged liver.\n" //i.e. shrink their liver or give them a transplant. + else + msg += "\tSubject's liver is missing.\n" + var/obj/item/organ/tongue/T = H.getorganslot("tongue") + if(T) + if(T.damage > 40) + msg += "\tSubject is suffering from severe burn tissue on their tongue.\n" //i.e. their tongue is shot + if(T.name == "fluffy tongue") + msg += "\tSubject is suffering from a fluffified tongue. Suggested cure: Yamerol or a tongue transplant.\n" + else + msg += "\tSubject's tongue is missing.\n" + var/obj/item/organ/lungs/Lung = H.getorganslot("lungs") + if(Lung) + if(Lung.damage > 150) + msg += "\tSubject is suffering from acute emphysema leading to trouble breathing.\n" //i.e. Their lungs are shot + else + msg += "\tSubject's lungs have collapsed from trauma!\n" + var/obj/item/organ/genital/penis/P = H.getorganslot("penis") + if(P) + if(P.length>20) + msg += "\tSubject has a sizeable gentleman's organ at [P.length] inches.\n" + var/obj/item/organ/genital/breasts/Br = H.getorganslot("breasts") + if(Br) + if(Br.cached_size>5) + msg += "\tSubject has a sizeable bosom with a [Br.size] cup.\n" + if (M.getOrganLoss(ORGAN_SLOT_BRAIN) >= 200 || !M.getorgan(/obj/item/organ/brain)) + msg += "\tSubject's brain function is non-existent.\n" + else if (M.getOrganLoss(ORGAN_SLOT_BRAIN) >= 120) + msg += "\tSevere brain damage detected. Subject likely to have mental traumas.\n" + else if (M.getOrganLoss(ORGAN_SLOT_BRAIN) >= 45) + msg += "\tBrain damage detected.\n" + + if(iscarbon(M)) + + var/mob/living/carbon/C = M + if(LAZYLEN(C.get_traumas())) + var/list/trauma_text = list() + for(var/datum/brain_trauma/B in C.get_traumas()) + var/trauma_desc = "" + switch(B.resilience) + if(TRAUMA_RESILIENCE_SURGERY) + trauma_desc += "severe " + if(TRAUMA_RESILIENCE_LOBOTOMY) + trauma_desc += "deep-rooted " + if(TRAUMA_RESILIENCE_MAGIC, TRAUMA_RESILIENCE_ABSOLUTE) + trauma_desc += "permanent " + trauma_desc += B.scan_desc + trauma_text += trauma_desc + msg += "\tCerebral traumas detected: subject appears to be suffering from [english_list(trauma_text)].\n" + if(C.roundstart_quirks.len) + msg += "\tSubject has the following physiological traits: [C.get_trait_string()].\n" + if(advanced) + msg += "\tBrain Activity Level: [(200 - M.getOrganLoss(ORGAN_SLOT_BRAIN))/2]%.\n" + if(M.radiation) + msg += "\tSubject is irradiated.\n" + if(advanced) + msg += "\tRadiation Level: [M.radiation]%.\n" + + if(advanced && M.hallucinating()) + msg += "\tSubject is hallucinating.\n" + + //MKUltra + if(advanced && M.has_status_effect(/datum/status_effect/chem/enthrall)) + msg += "\tSubject has abnormal brain fuctions.\n" + + //Astrogen shenanigans + if(advanced && M.reagents.has_reagent("astral")) + if(M.mind) + msg += "\tWarning: subject may be possesed.\n" + else + msg += "\tSubject appears to be astrally projecting.\n" + + //Eyes and ears + if(advanced) + if(iscarbon(M)) + var/mob/living/carbon/C = M + var/obj/item/organ/ears/ears = C.getorganslot(ORGAN_SLOT_EARS) + msg += "\t==EAR STATUS==\n" + if(istype(ears)) + var/healthy = TRUE + if(HAS_TRAIT_FROM(C, TRAIT_DEAF, GENETIC_MUTATION)) + healthy = FALSE + msg += "\tSubject is genetically deaf.\n" + else if(HAS_TRAIT(C, TRAIT_DEAF)) + healthy = FALSE + msg += "\tSubject is deaf.\n" + else + if(ears.damage) + to_chat(user, "\tSubject has [ears.damage > ears.maxHealth ? "permanent ": "temporary "]hearing damage.") + healthy = FALSE + if(ears.deaf) + to_chat(user, "\tSubject is [ears.damage > ears.maxHealth ? "permanently ": "temporarily "] deaf.") + healthy = FALSE + if(healthy) + msg += "\tHealthy.\n" + else + msg += "\tSubject does not have ears.\n" + var/obj/item/organ/eyes/eyes = C.getorganslot(ORGAN_SLOT_EYES) + msg += "\t==EYE STATUS==\n" + if(istype(eyes)) + var/healthy = TRUE + if(HAS_TRAIT(C, TRAIT_BLIND)) + msg += "\tSubject is blind.\n" + healthy = FALSE + if(HAS_TRAIT(C, TRAIT_NEARSIGHT)) + msg += "\tSubject is nearsighted.\n" + healthy = FALSE + if(eyes.damage > 30) + msg += "\tSubject has severe eye damage.\n" + healthy = FALSE + else if(eyes.damage > 20) + msg += "\tSubject has significant eye damage.\n" + healthy = FALSE + else if(eyes.damage) + msg += "\tSubject has minor eye damage.\n" + healthy = FALSE + if(healthy) + msg += "\tHealthy.\n" + else + msg += "\tSubject does not have eyes.\n" + + + if(ishuman(M)) + var/mob/living/carbon/human/H = M + var/ldamage = H.return_liver_damage() + if(ldamage > 10) + msg += "\t[ldamage > 45 ? "Severe" : "Minor"] liver damage detected.\n" + if(advanced && H.has_dna()) + to_chat(user, "\tGenetic Stability: [H.dna.stability]%.") + // Body part damage report + if(iscarbon(M) && mode == 1) + var/mob/living/carbon/C = M + var/list/damaged = C.get_damaged_bodyparts(1,1) + if(length(damaged)>0 || oxy_loss>0 || tox_loss>0 || fire_loss>0) + msg += "\tDamage: Brute-Burn-Toxin-Suffocation\n\t\tSpecifics: [brute_loss]-[fire_loss]-[tox_loss]-[oxy_loss]\n" + for(var/obj/item/bodypart/org in damaged) + msg += "\t\t[capitalize(org.name)]: [(org.brute_dam > 0) ? "[org.brute_dam]" : "0"]-[(org.burn_dam > 0) ? "[org.burn_dam]" : "0"]\n" + + //Bones broken report! Hyperstation 13 + if(iscarbon(M) && mode == 1) + var/mob/living/carbon/C = M + for(var/X in C.bodyparts) + var/obj/item/bodypart/LB = X + var/broken = LB.broken + if(broken == 1) + msg += "\tSubjects [LB.name] is fractured!\n" + +//Organ damages report + if(ishuman(M)) + var/mob/living/carbon/human/H = M + var/minor_damage + var/major_damage + var/max_damage + var/report_organs = FALSE + + //Piece together the lists to be reported + for(var/O in H.internal_organs) + var/obj/item/organ/organ = O + if(organ.organ_flags & ORGAN_FAILING) + report_organs = TRUE //if we report one organ, we report all organs, even if the lists are empty, just for consistency + if(max_damage) + max_damage += ", " //prelude the organ if we've already reported an organ + max_damage += organ.name //this just slaps the organ name into the string of text + else + max_damage = "\tNon-Functional Organs: " //our initial statement + max_damage += organ.name + else if(organ.damage > organ.high_threshold) + report_organs = TRUE + if(major_damage) + major_damage += ", " + major_damage += organ.name + else + major_damage = "\tSeverely Damaged Organs: " + major_damage += organ.name + else if(organ.damage > organ.low_threshold) + report_organs = TRUE + if(minor_damage) + minor_damage += ", " + minor_damage += organ.name + else + minor_damage = "\tMildly Damaged Organs: " + minor_damage += organ.name + + if(report_organs) //we either finish the list, or set it to be empty if no organs were reported in that category + if(!max_damage) + max_damage = "\tNon-Functional Organs: " + else + max_damage += "" + if(!major_damage) + major_damage = "\tSeverely Damaged Organs: " + else + major_damage += "" + if(!minor_damage) + minor_damage = "\tMildly Damaged Organs: " + else + minor_damage += "" + msg += "[minor_damage]" + msg += "[major_damage]" + msg += "[max_damage]" + + // Species and body temperature + if(ishuman(M)) + var/mob/living/carbon/human/H = M + var/datum/species/S = H.dna.species + var/mutant = FALSE + if (H.dna.check_mutation(HULK)) + mutant = TRUE + else if (S.mutantlungs != initial(S.mutantlungs)) + mutant = TRUE + else if (S.mutant_brain != initial(S.mutant_brain)) + mutant = TRUE + else if (S.mutant_heart != initial(S.mutant_heart)) + mutant = TRUE + else if (S.mutanteyes != initial(S.mutanteyes)) + mutant = TRUE + else if (S.mutantears != initial(S.mutantears)) + mutant = TRUE + else if (S.mutanthands != initial(S.mutanthands)) + mutant = TRUE + else if (S.mutanttongue != initial(S.mutanttongue)) + mutant = TRUE + else if (S.mutanttail != initial(S.mutanttail)) + mutant = TRUE + else if (S.mutantliver != initial(S.mutantliver)) + mutant = TRUE + else if (S.mutantstomach != initial(S.mutantstomach)) + mutant = TRUE + + msg += "Species: [H.dna.custom_species ? H.dna.custom_species : S.name] Base: [S.name]\n" + if(mutant) + msg += "Subject has mutations present." + msg += "Body temperature: [round(M.bodytemperature-T0C,0.1)] °C ([round(M.bodytemperature*1.8-459.67,0.1)] °F)\n" + + // Time of death + if(M.tod && (M.stat == DEAD || ((HAS_TRAIT(M, TRAIT_FAKEDEATH)) && !advanced))) + msg += "Time of Death: [M.tod]\n" + var/tdelta = round(world.time - M.timeofdeath) + if(tdelta < (DEFIB_TIME_LIMIT * 10)) + msg += "Subject died [DisplayTimeText(tdelta)] ago, defibrillation may be possible!\n" + + for(var/thing in M.diseases) + var/datum/disease/D = thing + if(!(D.visibility_flags & HIDDEN_SCANNER)) + msg += "Warning: [D.form] detected\nName: [D.name].\nType: [D.spread_text].\nStage: [D.stage]/[D.max_stages].\nPossible Cure: [D.cure_text]\n" + + // Blood Level + if(M.has_dna()) + var/mob/living/carbon/C = M + var/blood_typepath = C.get_blood_id() + if(blood_typepath) + if(ishuman(C)) + var/mob/living/carbon/human/H = C + if(H.bleed_rate) + msg += "Subject is bleeding!\n" + var/blood_percent = round((C.scan_blood_volume() / (BLOOD_VOLUME_NORMAL * C.blood_ratio))*100) + var/blood_type = C.dna.blood_type + if(!(blood_typepath in GLOB.blood_reagent_types)) + var/datum/reagent/R = GLOB.chemical_reagents_list[blood_typepath] + if(R) + blood_type = R.name + if(C.scan_blood_volume() <= (BLOOD_VOLUME_SAFE*C.blood_ratio) && C.scan_blood_volume() > (BLOOD_VOLUME_OKAY*C.blood_ratio)) + msg += "LOW blood level [blood_percent] %, [C.scan_blood_volume()] cl, type: [blood_type]\n" + else if(C.scan_blood_volume() <= (BLOOD_VOLUME_OKAY*C.blood_ratio)) + msg += "CRITICAL blood level [blood_percent] %, [C.scan_blood_volume()] cl, type: [blood_type]\n" + else + msg += "Blood level [blood_percent] %, [C.scan_blood_volume()] cl, type: [blood_type]\n" + + var/cyberimp_detect + for(var/obj/item/organ/cyberimp/CI in C.internal_organs) + if(CI.status == ORGAN_ROBOTIC && !CI.syndicate_implant) + cyberimp_detect += "[C.name] is modified with a [CI.name].
" + if(cyberimp_detect) + msg += "Detected cybernetic modifications:\n" + msg += "[cyberimp_detect]\n" + msg += "*---------*
" + to_chat(user, msg) + SEND_SIGNAL(M, COMSIG_NANITE_SCAN, user, FALSE) + +/proc/chemscan(mob/living/user, mob/living/M) + if(istype(M)) + if(M.reagents) + var/msg = "*---------*\n" + if(M.reagents.reagent_list.len) + var/list/datum/reagent/reagents = list() + for(var/datum/reagent/R in M.reagents.reagent_list) + if(R.chemical_flags & REAGENT_INVISIBLE) + continue + reagents += R + + if(length(reagents)) + msg += "Subject contains the following reagents:\n" + for(var/datum/reagent/R in reagents) + msg += "[R.volume] units of [R.name][R.overdosed == 1 ? " - OVERDOSING" : "."]\n" + else + msg += "Subject contains no reagents.\n" + + else + msg += "Subject contains no reagents.\n" + if(M.reagents.addiction_list.len) + msg += "Subject is addicted to the following reagents:\n" + for(var/datum/reagent/R in M.reagents.addiction_list) + msg += "[R.name]\n" + else + msg += "Subject is not addicted to any reagents.\n" + msg += "*---------*
" + to_chat(user, msg) + + var/datum/reagent/impure/fermiTox/F = M.reagents.has_reagent(/datum/reagent/impure/fermiTox) + switch(F?.volume) + if(5 to 10) + msg += "Subject contains a low amount of toxic isomers.\n" + if(10 to 25) + msg += "Subject contains toxic isomers.\n" + if(25 to 50) + msg += "Subject contains a substantial amount of toxic isomers.\n" + if(50 to 95) + msg += "Subject contains a high amount of toxic isomers.\n" + if(95 to INFINITY) + msg += "Subject contains a extremely dangerous amount of toxic isomers.\n" + +/obj/item/healthanalyzer/verb/toggle_mode() + set name = "Switch Verbosity" + set category = "Object" + + if(usr.stat || !usr.canmove || usr.restrained()) + return + + mode = !mode + switch (mode) + if(1) + to_chat(usr, "The scanner now shows specific limb damage.") + if(0) + to_chat(usr, "The scanner no longer shows limb damage.") + +/obj/item/healthanalyzer/advanced + name = "advanced health analyzer" + icon_state = "health_adv" + desc = "A hand-held body scanner able to distinguish vital signs of the subject with high accuracy." + advanced = TRUE + +/obj/item/analyzer + desc = "A hand-held environmental scanner which reports current gas levels. Alt-Click to use the built in barometer function." + name = "analyzer" + icon = 'icons/obj/device.dmi' + icon_state = "analyzer" + item_state = "analyzer" + lefthand_file = 'icons/mob/inhands/equipment/tools_lefthand.dmi' + righthand_file = 'icons/mob/inhands/equipment/tools_righthand.dmi' + w_class = WEIGHT_CLASS_SMALL + flags_1 = CONDUCT_1 + item_flags = NOBLUDGEON + slot_flags = ITEM_SLOT_BELT + throwforce = 0 + throw_speed = 3 + throw_range = 7 + tool_behaviour = TOOL_ANALYZER + materials = list(MAT_METAL=30, MAT_GLASS=20) + grind_results = list(/datum/reagent/mercury = 5, /datum/reagent/iron = 5, /datum/reagent/silicon = 5) + var/cooldown = FALSE + var/cooldown_time = 250 + var/accuracy // 0 is the best accuracy. + +/obj/item/analyzer/examine(mob/user) + . = ..() + . += "Alt-click [src] to activate the barometer function." + +/obj/item/analyzer/suicide_act(mob/living/carbon/user) + user.visible_message("[user] begins to analyze [user.p_them()]self with [src]! The display shows that [user.p_theyre()] dead!") + return BRUTELOSS + +/obj/item/analyzer/attack_self(mob/user) + add_fingerprint(user) + + if (user.stat || user.eye_blind) + return + + var/turf/location = user.loc + if(!istype(location)) + return + + var/datum/gas_mixture/environment = location.return_air() + + var/pressure = environment.return_pressure() + var/total_moles = environment.total_moles() + + to_chat(user, "Results:") + if(abs(pressure - ONE_ATMOSPHERE) < 10) + to_chat(user, "Pressure: [round(pressure, 0.01)] kPa") + else + to_chat(user, "Pressure: [round(pressure, 0.01)] kPa") + if(total_moles) + var/list/env_gases = environment.gases + + var/o2_concentration = env_gases[/datum/gas/oxygen]/total_moles + var/n2_concentration = env_gases[/datum/gas/nitrogen]/total_moles + var/co2_concentration = env_gases[/datum/gas/carbon_dioxide]/total_moles + var/plasma_concentration = env_gases[/datum/gas/plasma]/total_moles + + if(abs(n2_concentration - N2STANDARD) < 20) + to_chat(user, "Nitrogen: [round(n2_concentration*100, 0.01)] % ([round(env_gases[/datum/gas/nitrogen], 0.01)] mol)") + else + to_chat(user, "Nitrogen: [round(n2_concentration*100, 0.01)] % ([round(env_gases[/datum/gas/nitrogen], 0.01)] mol)") + + if(abs(o2_concentration - O2STANDARD) < 2) + to_chat(user, "Oxygen: [round(o2_concentration*100, 0.01)] % ([round(env_gases[/datum/gas/oxygen], 0.01)] mol)") + else + to_chat(user, "Oxygen: [round(o2_concentration*100, 0.01)] % ([round(env_gases[/datum/gas/oxygen], 0.01)] mol)") + + if(co2_concentration > 0.01) + to_chat(user, "CO2: [round(co2_concentration*100, 0.01)] % ([round(env_gases[/datum/gas/carbon_dioxide], 0.01)] mol)") + else + to_chat(user, "CO2: [round(co2_concentration*100, 0.01)] % ([round(env_gases[/datum/gas/carbon_dioxide], 0.01)] mol)") + + if(plasma_concentration > 0.005) + to_chat(user, "Plasma: [round(plasma_concentration*100, 0.01)] % ([round(env_gases[/datum/gas/plasma], 0.01)] mol)") + else + to_chat(user, "Plasma: [round(plasma_concentration*100, 0.01)] % ([round(env_gases[/datum/gas/plasma], 0.01)] mol)") + + GAS_GARBAGE_COLLECT(environment.gases) + + for(var/id in env_gases) + if(id in GLOB.hardcoded_gases) + continue + var/gas_concentration = env_gases[id]/total_moles + to_chat(user, "[GLOB.meta_gas_names[id]]: [round(gas_concentration*100, 0.01)] % ([round(env_gases[id], 0.01)] mol)") + to_chat(user, "Temperature: [round(environment.temperature-T0C, 0.01)] °C ([round(environment.temperature, 0.01)] K)") + +/obj/item/analyzer/AltClick(mob/user) //Barometer output for measuring when the next storm happens + ..() + + if(user.canUseTopic(src)) + + if(cooldown) + to_chat(user, "[src]'s barometer function is preparing itself.") + return + + var/turf/T = get_turf(user) + if(!T) + return + + playsound(src, 'sound/effects/pop.ogg', 100) + var/area/user_area = T.loc + var/datum/weather/ongoing_weather = null + + if(!user_area.outdoors) + to_chat(user, "[src]'s barometer function won't work indoors!") + return + + for(var/V in SSweather.processing) + var/datum/weather/W = V + if(W.barometer_predictable && (T.z in W.impacted_z_levels) && W.area_type == user_area.type && !(W.stage == END_STAGE)) + ongoing_weather = W + break + + if(ongoing_weather) + if((ongoing_weather.stage == MAIN_STAGE) || (ongoing_weather.stage == WIND_DOWN_STAGE)) + to_chat(user, "[src]'s barometer function can't trace anything while the storm is [ongoing_weather.stage == MAIN_STAGE ? "already here!" : "winding down."]") + return + + to_chat(user, "The next [ongoing_weather] will hit in [butchertime(ongoing_weather.next_hit_time - world.time)].") + if(ongoing_weather.aesthetic) + to_chat(user, "[src]'s barometer function says that the next storm will breeze on by.") + else + var/next_hit = SSweather.next_hit_by_zlevel["[T.z]"] + var/fixed = next_hit ? next_hit - world.time : -1 + if(fixed < 0) + to_chat(user, "[src]'s barometer function was unable to trace any weather patterns.") + else + to_chat(user, "[src]'s barometer function says a storm will land in approximately [butchertime(fixed)].") + cooldown = TRUE + addtimer(CALLBACK(src,/obj/item/analyzer/proc/ping), cooldown_time) + +/obj/item/analyzer/proc/ping() + if(isliving(loc)) + var/mob/living/L = loc + to_chat(L, "[src]'s barometer function is ready!") + playsound(src, 'sound/machines/click.ogg', 100) + cooldown = FALSE + +/obj/item/analyzer/proc/butchertime(amount) + if(!amount) + return + if(accuracy) + var/inaccurate = round(accuracy*(1/3)) + if(prob(50)) + amount -= inaccurate + if(prob(50)) + amount += inaccurate + return DisplayTimeText(max(1,amount)) + +/proc/atmosanalyzer_scan(mixture, mob/living/user, atom/target = src) + var/icon = target + user.visible_message("[user] has used the analyzer on [icon2html(icon, viewers(src))] [target].", "You use the analyzer on [icon2html(icon, user)] [target].") + to_chat(user, "Results of analysis of [icon2html(icon, user)] [target].") + + var/list/airs = islist(mixture) ? mixture : list(mixture) + for(var/g in airs) + if(airs.len > 1) //not a unary gas mixture + to_chat(user, "Node [airs.Find(g)]") + var/datum/gas_mixture/air_contents = g + + var/total_moles = air_contents.total_moles() + var/pressure = air_contents.return_pressure() + var/volume = air_contents.return_volume() //could just do mixture.volume... but safety, I guess? + var/temperature = air_contents.temperature + var/cached_scan_results = air_contents.analyzer_results + + if(total_moles > 0) + to_chat(user, "Moles: [round(total_moles, 0.01)] mol") + to_chat(user, "Volume: [volume] L") + to_chat(user, "Pressure: [round(pressure,0.01)] kPa") + + var/list/cached_gases = air_contents.gases + for(var/id in cached_gases) + var/gas_concentration = cached_gases[id]/total_moles + to_chat(user, "[GLOB.meta_gas_names[id]]: [round(gas_concentration*100, 0.01)] % ([round(cached_gases[id], 0.01)] mol)") + to_chat(user, "Temperature: [round(temperature - T0C,0.01)] °C ([round(temperature, 0.01)] K)") + + else + if(airs.len > 1) + to_chat(user, "This node is empty!") + else + to_chat(user, "[target] is empty!") + + if(cached_scan_results && cached_scan_results["fusion"]) //notify the user if a fusion reaction was detected + var/instability = round(cached_scan_results["fusion"], 0.01) + to_chat(user, "Large amounts of free neutrons detected in the air indicate that a fusion reaction took place.") + to_chat(user, "Instability of the last fusion reaction: [instability].") + return + +//slime scanner + +/obj/item/slime_scanner + name = "slime scanner" + desc = "A device that analyzes a slime's internal composition and measures its stats." + icon = 'icons/obj/device.dmi' + icon_state = "adv_spectrometer" + item_state = "analyzer" + lefthand_file = 'icons/mob/inhands/equipment/tools_lefthand.dmi' + righthand_file = 'icons/mob/inhands/equipment/tools_righthand.dmi' + w_class = WEIGHT_CLASS_SMALL + flags_1 = CONDUCT_1 + throwforce = 0 + throw_speed = 3 + throw_range = 7 + materials = list(MAT_METAL=30, MAT_GLASS=20) + +/obj/item/slime_scanner/attack(mob/living/M, mob/living/user) + if(user.stat || user.eye_blind) + return + if (!isslime(M)) + to_chat(user, "This device can only scan slimes!") + return + var/mob/living/simple_animal/slime/T = M + slime_scan(T, user) + +/proc/slime_scan(mob/living/simple_animal/slime/T, mob/living/user) + to_chat(user, "========================") + to_chat(user, "Slime scan results:") + to_chat(user, "[T.colour] [T.is_adult ? "adult" : "baby"] slime") + to_chat(user, "Nutrition: [T.nutrition]/[T.get_max_nutrition()]") + if (T.nutrition < T.get_starve_nutrition()) + to_chat(user, "Warning: slime is starving!") + else if (T.nutrition < T.get_hunger_nutrition()) + to_chat(user, "Warning: slime is hungry") + to_chat(user, "Electric change strength: [T.powerlevel]") + to_chat(user, "Health: [round(T.health/T.maxHealth,0.01)*100]%") + if (T.slime_mutation[4] == T.colour) + to_chat(user, "This slime does not evolve any further.") + else + if (T.slime_mutation[3] == T.slime_mutation[4]) + if (T.slime_mutation[2] == T.slime_mutation[1]) + to_chat(user, "Possible mutation: [T.slime_mutation[3]]") + to_chat(user, "Genetic destability: [T.mutation_chance/2] % chance of mutation on splitting") + else + to_chat(user, "Possible mutations: [T.slime_mutation[1]], [T.slime_mutation[2]], [T.slime_mutation[3]] (x2)") + to_chat(user, "Genetic destability: [T.mutation_chance] % chance of mutation on splitting") + else + to_chat(user, "Possible mutations: [T.slime_mutation[1]], [T.slime_mutation[2]], [T.slime_mutation[3]], [T.slime_mutation[4]]") + to_chat(user, "Genetic destability: [T.mutation_chance] % chance of mutation on splitting") + if (T.cores > 1) + to_chat(user, "Multiple cores detected") + to_chat(user, "Growth progress: [T.amount_grown]/[SLIME_EVOLUTION_THRESHOLD]") + if(T.effectmod) + to_chat(user, "Core mutation in progress: [T.effectmod]") + to_chat(user, "Progress in core mutation: [T.applied] / [SLIME_EXTRACT_CROSSING_REQUIRED]") + to_chat(user, "========================") + + +/obj/item/nanite_scanner + name = "nanite scanner" + icon = 'icons/obj/device.dmi' + icon_state = "nanite_scanner" + item_state = "nanite_remote" + lefthand_file = 'icons/mob/inhands/equipment/medical_lefthand.dmi' + righthand_file = 'icons/mob/inhands/equipment/medical_righthand.dmi' + desc = "A hand-held body scanner able to detect nanites and their programming." + flags_1 = CONDUCT_1 + item_flags = NOBLUDGEON + slot_flags = ITEM_SLOT_BELT + throwforce = 3 + w_class = WEIGHT_CLASS_TINY + throw_speed = 3 + throw_range = 7 + materials = list(MAT_METAL=200) + +/obj/item/nanite_scanner/attack(mob/living/M, mob/living/carbon/human/user) + user.visible_message("[user] has analyzed [M]'s nanites.") + + add_fingerprint(user) + + var/response = SEND_SIGNAL(M, COMSIG_NANITE_SCAN, user, TRUE) + if(!response) + to_chat(user, "No nanites detected in the subject.") + +/obj/item/sequence_scanner + name = "genetic sequence scanner" + icon = 'icons/obj/device.dmi' + icon_state = "gene" + item_state = "healthanalyzer" + lefthand_file = 'icons/mob/inhands/equipment/medical_lefthand.dmi' + righthand_file = 'icons/mob/inhands/equipment/medical_righthand.dmi' + desc = "A hand-held scanner able to swiftly scan someone for potential mutations. Hold near a DNA console to update from their database." + flags_1 = CONDUCT_1 + item_flags = NOBLUDGEON + slot_flags = ITEM_SLOT_BELT + throwforce = 3 + w_class = WEIGHT_CLASS_TINY + throw_speed = 3 + throw_range = 7 + materials = list(MAT_METAL=200) + var/list/discovered = list() //hit a dna console to update the scanners database + +/obj/item/sequence_scanner/attack(mob/living/M, mob/living/carbon/human/user) + user.visible_message("[user] has analyzed [M]'s genetic sequence.") + + add_fingerprint(user) + + gene_scan(M, user, src) + +/obj/item/sequence_scanner/afterattack(obj/O, mob/user, proximity) + . = ..() + if(!istype(O) || !proximity) + return + + if(istype(O, /obj/machinery/computer/scan_consolenew)) + var/obj/machinery/computer/scan_consolenew/C = O + if(C.stored_research) + to_chat(user, "[name] database updated.") + discovered = C.stored_research.discovered_mutations + else + to_chat(user,"No database to update from.") + +/proc/gene_scan(mob/living/carbon/C, mob/living/user, obj/item/sequence_scanner/G) + if(!iscarbon(C) || !C.has_dna()) + return + to_chat(user, "[C.name]'s potential mutations.") + for(var/A in C.dna.mutation_index) + var/datum/mutation/human/HM = GET_INITIALIZED_MUTATION(A) + var/mut_name + if(G && (A in G.discovered)) + mut_name = "[HM.name] ([HM.alias])" + else + mut_name = HM.alias + var/temp = GET_GENE_STRING(HM.type, C.dna) + var/display + for(var/i in 0 to length(temp) / DNA_MUTATION_BLOCKS-1) + if(i) + display += "-" + display += copytext(temp, 1 + i*DNA_MUTATION_BLOCKS, DNA_MUTATION_BLOCKS*(1+i) + 1) + + + to_chat(user, "- [mut_name] > [display]") diff --git a/code/game/objects/items/stacks/stack.dm b/code/game/objects/items/stacks/stack.dm index 873cb7ba..6173ac52 100644 --- a/code/game/objects/items/stacks/stack.dm +++ b/code/game/objects/items/stacks/stack.dm @@ -398,7 +398,6 @@ fingerprintshidden = from.fingerprintshidden.Copy() if(from.fingerprintslast) fingerprintslast = from.fingerprintslast - //TODO bloody overlay /obj/item/stack/microwave_act(obj/machinery/microwave/M) if(istype(M) && M.dirty < 100) diff --git a/code/game/objects/items/twohanded.dm b/code/game/objects/items/twohanded.dm index af08ac44..ce99eb7d 100644 --- a/code/game/objects/items/twohanded.dm +++ b/code/game/objects/items/twohanded.dm @@ -346,7 +346,6 @@ icon_state = "dualsaber[item_color][wielded]" else icon_state = "dualsaber0" - clean_blood() /obj/item/twohanded/dualsaber/attack(mob/target, mob/living/carbon/human/user) diff --git a/code/modules/detectivework/detective_work.dm b/code/modules/detectivework/detective_work.dm index 7ab5b2db..3b1b00fc 100644 --- a/code/modules/detectivework/detective_work.dm +++ b/code/modules/detectivework/detective_work.dm @@ -65,7 +65,6 @@ fingerprintslast = M.ckey - //Set ignoregloves to add prints irrespective of the mob having gloves on. /atom/proc/add_fingerprint(mob/living/M, ignoregloves = FALSE) if(!M || !M.key) diff --git a/code/modules/surgery/bodyparts/bodyparts.dm b/code/modules/surgery/bodyparts/bodyparts.dm index 95bcab31..85659f57 100644 --- a/code/modules/surgery/bodyparts/bodyparts.dm +++ b/code/modules/surgery/bodyparts/bodyparts.dm @@ -836,7 +836,7 @@ px_x = 2 px_y = 12 max_stamina_damage = 50 - stam_heal_tick = 2 + stam_heal_tick = 4 /obj/item/bodypart/r_leg/is_disabled() if(HAS_TRAIT(owner, TRAIT_PARALYSIS_R_LEG)) diff --git a/code/modules/surgery/bodyparts/helpers.dm b/code/modules/surgery/bodyparts/helpers.dm index 35043c8a..0cb3a1cb 100644 --- a/code/modules/surgery/bodyparts/helpers.dm +++ b/code/modules/surgery/bodyparts/helpers.dm @@ -17,7 +17,6 @@ return L return FALSE - /mob/proc/has_left_hand(check_disabled = TRUE) return TRUE @@ -45,8 +44,6 @@ /mob/living/carbon/alien/larva/has_right_hand() return TRUE - - /mob/proc/has_left_leg() return TRUE @@ -67,7 +64,6 @@ else return FALSE - //Limb numbers /mob/proc/get_num_arms(check_disabled = TRUE) return 2 diff --git a/code/modules/surgery/organs/heart.dm b/code/modules/surgery/organs/heart.dm index ca42e76d..71863be9 100644 --- a/code/modules/surgery/organs/heart.dm +++ b/code/modules/surgery/organs/heart.dm @@ -221,11 +221,6 @@ obj/item/organ/heart/cybernetic/upgraded/on_life() addtimer(VARSET_CALLBACK(src, dose_available, TRUE), 5 MINUTES) ramount = 0 -/obj/item/organ/heart/ipc - name = "IPC heart" - desc = "An electronic pump that regulates hydraulic functions, the electronics have EMP shielding." - icon_state = "heart-c" - /obj/item/organ/heart/ipc name = "IPC heart" desc = "An electronic pump that regulates hydraulic functions, they have an auto-restart after EMPs." diff --git a/modular_citadel/code/modules/reagents/reagents/cit_reagents.dm b/modular_citadel/code/modules/reagents/reagents/cit_reagents.dm index c55f43b0..5bce8969 100644 --- a/modular_citadel/code/modules/reagents/reagents/cit_reagents.dm +++ b/modular_citadel/code/modules/reagents/reagents/cit_reagents.dm @@ -1,7 +1,7 @@ //body bluids /datum/reagent/consumable/semen name = "Semen" - description = "Sperm from some animal. I bet you'll drink this out of a bucket someday." + description = "Sperm from some animal. Useless for anything but insemination, really." taste_description = "something salty" taste_mult = 2 //Not very overpowering flavor data = list("donor"=null,"viruses"=null,"donor_DNA"=null,"blood_type"=null,"resistances"=null,"trace_chem"=null,"mind"=null,"ckey"=null,"gender"=null,"real_name"=null) From e23546c3a493332774e8388269f232358491f6a6 Mon Sep 17 00:00:00 2001 From: Archie Date: Fri, 25 Dec 2020 17:32:54 -0300 Subject: [PATCH 2/2] sleeper --- code/game/machinery/Sleeper.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/game/machinery/Sleeper.dm b/code/game/machinery/Sleeper.dm index 09d31b9a..18306ff6 100644 --- a/code/game/machinery/Sleeper.dm +++ b/code/game/machinery/Sleeper.dm @@ -262,7 +262,7 @@ if(blood_id) data["occupant"]["blood"] = list() // We can start populating this list. var/blood_type = C.dna.blood_type - if(blood_id != "blood") // special blood substance + if(!(blood_id in GLOB.blood_reagent_types)) // special blood substance var/datum/reagent/R = GLOB.chemical_reagents_list[blood_id] if(R) blood_type = R.name