/proc/scan_health(var/mob/M as mob, var/verbose_reagent_info = 0, var/disease_detection = 1)
if (!M)
return "ERROR: NO SUBJECT DETECTED"
if (isghostdrone(M))
return "ERROR: INVALID DATA FROM SUBJECT"
var/death_state = M.stat
if (M.bioHolder && M.bioHolder.HasEffect("dead_scan"))
death_state = 2
var/health_percent = round(100 * M.health / M.max_health)
var/colored_health
if (health_percent >= 51 && health_percent <= 100)
colored_health = "[health_percent]"
else if (health_percent >= 1 && health_percent <= 50)
colored_health = "[health_percent]"
else colored_health = "[health_percent]"
var/optimal_temp = M.base_body_temp
var/body_temp = "[M.bodytemperature - T0C]°C ([M.bodytemperature * 1.8-459.67]°F)"
var/colored_temp = ""
if (M.bodytemperature >= (optimal_temp + 60))
colored_temp = "[body_temp]"
else if (M.bodytemperature >= (optimal_temp + 30))
colored_temp = "[body_temp]"
else if (M.bodytemperature <= (optimal_temp - 60))
colored_temp = "[body_temp]"
else if (M.bodytemperature <= (optimal_temp - 30))
colored_temp = "[body_temp]"
else
colored_temp = "[body_temp]"
var/oxy = M.get_oxygen_deprivation()
var/tox = M.get_toxin_damage()
var/burn = M.get_burn_damage()
var/brute = M.get_brute_damage()
// contained here in order to change them easier
var/oxy_font = ""
var/tox_font = ""
var/burn_font = ""
var/brute_font = ""
var/oxy_data = "[oxy > 50 ? "" : "[oxy_font]"][oxy]"
var/tox_data = "[tox > 50 ? "" : "[tox_font]"][tox]"
var/burn_data = "[burn > 50 ? "" : "[burn_font]"][burn]"
var/brute_data = "[brute > 50 ? "" : "[brute_font]"][brute]"
var/rad_data = null
var/blood_data = null
var/brain_data = null
var/heart_data = null
var/reagent_data = null
var/pathogen_data = null
var/disease_data = null
if (ishuman(M))
var/mob/living/carbon/human/H = M
if (blood_system)
if (verbose_reagent_info)
if (isvampire(H)) // Added a pair of vampire checks here (Convair880).
blood_data = "Blood level: 500 units"
else
blood_data = "Blood level: [H.blood_volume] unit[H.blood_volume == 1 ? "" : "s"]"
if (H.bleeding)
blood_data += " | Blood loss: [H.bleeding] unit[H.bleeding == 1 ? "" : "s"]"
else
if (isvampire(H))
blood_data = "Blood level: NORMAL"
else
switch (H.blood_volume)
if (401 to INFINITY)
blood_data = "Blood level: NORMAL"
if (251 to 400)
blood_data = "Blood level: LOW"
if (151 to 250)
blood_data = "Blood level: VERY LOW"
if (1 to 150)
blood_data = "Blood level: DANGEROUSLY LOW"
if (-INFINITY to 0)
blood_data = "Blood level: NO BLOOD DETECTED"
switch (H.bleeding)
if (1 to 3)
blood_data += " | Minor bleeding wounds detected"
if (4 to 6)
blood_data += " | Bleeding wounds detected"
if (7 to INFINITY)
blood_data += " | Major bleeding wounds detected"
if (H.implant && H.implant.len > 0)
var/bad_stuff = 0
for (var/obj/item/implant/I in H)
if (istype(I, /obj/item/implant/projectile))
bad_stuff ++
if (bad_stuff)
blood_data += " | Foreign object[bad_stuff == 1 ? "" : "s"] detected"
if (H.pathogens.len)
pathogen_data = "Scans indicate the presence of [H.pathogens.len > 1 ? "[H.pathogens.len] " : null]pathogenic bodies."
var/list/therapy = list()
var/remissive = 0
for (var/uid in H.pathogens)
var/datum/pathogen/P = H.pathogens[uid]
if (P.in_remission)
remissive ++
if (!(P.suppressant.therapy in therapy))
therapy += P.suppressant.therapy
var/count_part
if (!remissive)
count_part = "None of them appear"
else if (remissive == 1)
count_part = "One pathogen appears"
else
count_part = "[remissive] of them appear"
pathogen_data += "
[count_part] to be in a remissive state."
pathogen_data += "
Suggested pathogen suppression therapies: [jointext(therapy, ", ")]."
if (H.organHolder)
if (H.organHolder.brain)
if (H.get_brain_damage() >= 100)
brain_data = "Subject is braindead."
else if (H.get_brain_damage() >= 60)
brain_data = "Severe brain damage detected. Subject likely unable to function well."
else if (H.get_brain_damage() >= 10)
brain_data = "Significant brain damage detected. Subject may have had a concussion."
else
brain_data = "Subject has no brain."
else
brain_data = "Subject has no brain."
if (H.organHolder && !H.organHolder.heart)
heart_data = "Subject has no heart."
if (M.radiation)
rad_data = " Radiation: [round(M.radiation / 10, 0.1)] Gy"
for (var/datum/ailment_data/A in M.ailments)
if (disease_detection >= A.detectability)
disease_data += "
[A.scan_info()]"
if (M.reagents)
if (verbose_reagent_info)
reagent_data = scan_reagents(M, 0)
else
var/ephe_amt = M.reagents:get_reagent_amount("ephedrine")
var/epi_amt = M.reagents:get_reagent_amount("epinephrine")
var/atro_amt = M.reagents:get_reagent_amount("atropine")
var/total_amt = ephe_amt + epi_amt + atro_amt
if (total_amt)
reagent_data = "Bloodstream Analysis located [total_amt] units of rejuvenation chemicals."
if (!ishuman(M)) // vOv
if (M.get_brain_damage() >= 100)
brain_data = "Subject is braindead."
else if (M.get_brain_damage() >= 60)
brain_data = "Severe brain damage detected. Subject likely unable to function well."
else if (M.get_brain_damage() >= 10)
brain_data = "Significant brain damage detected. Subject may have had a concussion."
var/data = "--------------------------------
\
Analyzing Results for [M]:
\
Overall Status: [death_state > 1 ? "DEAD" : "[colored_health]% healthy"]
\
Damage Specifics: [oxy_data] - [tox_data] - [burn_data] - [brute_data]
\
Key: [oxy_font]Suffocation/[tox_font]Toxin/[burn_font]Burns/[brute_font]Brute
\
Body Temperature: [colored_temp]\
[rad_data ? "
[rad_data]" : null]\
[blood_data ? "
[blood_data]" : null]\
[brain_data ? "
[brain_data]" : null]\
[heart_data ? "
[heart_data]" : null]\
[reagent_data ? "
[reagent_data]" : null]\
[pathogen_data ? "
[pathogen_data]" : null]\
[disease_data ? "[disease_data]" : null]"
return data
/proc/update_medical_record(var/mob/living/carbon/human/M)
if (!M || !ishuman(M))
return
var/patientname = M.name
if (M:wear_id && M:wear_id:registered)
patientname = M.wear_id:registered
for (var/datum/data/record/E in data_core.general)
if (E.fields["name"] == patientname)
switch (M.stat)
if (0)
if (M.bioHolder && M.bioHolder.HasEffect("fat"))
E.fields["p_stat"] = "Physically Unfit"
else
E.fields["p_stat"] = "Active"
if (1)
E.fields["p_stat"] = "*Unconscious*"
if (2)
E.fields["p_stat"] = "*Deceased*"
for (var/datum/data/record/R in data_core.medical)
if ((R.fields["id"] == E.fields["id"]))
R.fields["bioHolder.bloodType"] = M.bioHolder.bloodType
R.fields["cdi"] = english_list(M.ailments, "No diseases have been diagnosed at the moment.")
if (M.ailments.len)
R.fields["cdi_d"] = "Diseases detected at [time2text(world.realtime,"hh:mm")]."
else
R.fields["cdi_d"] = "No notes."
break
break
return
/proc/scan_reagents(var/atom/A as turf|obj|mob, var/show_temp = 1, var/single_line = 0)
if (!A)
return "ERROR: NO SUBJECT DETECTED"
var/data = null
var/reagent_data = null
if (A.reagents)
if (A.reagents.reagent_list.len > 0)
var/reagents_length = A.reagents.reagent_list.len
data = "[reagents_length] chemical agent[reagents_length > 1 ? "s" : ""] found in [A]."
for (var/current_id in A.reagents.reagent_list)
var/datum/reagent/current_reagent = A.reagents.reagent_list[current_id]
if (single_line)
reagent_data += " [current_reagent] ([current_reagent.volume]),"
else
reagent_data += "
[current_reagent.name] - [current_reagent.volume]"
if (single_line)
data += "[copytext(reagent_data, 1, -1)]"
else
data += "[reagent_data]"
if (show_temp)
data += "
Overall temperature: [A.reagents.total_temperature - T0C]°C ([A.reagents.total_temperature * 1.8-459.67]°F)"
else
data = "No active chemical agents found in [A]."
else
data = "No significant chemical agents found in [A]."
return data
// Should make it easier to maintain the detective's scanner and PDA program (Convair880).
/proc/scan_forensic(var/atom/A as turf|obj|mob)
var/fingerprint_data = null
var/blood_data = null
var/forensic_data = null
var/glove_data = null
var/contraband_data = null
if (!A)
return "ERROR: NO SUBJECT DETECTED"
if (istype(A, /mob/living/carbon/human))
var/mob/living/carbon/human/H = A
if (!isnull(H.gloves))
var/obj/item/clothing/gloves/WG = H.gloves
if (WG.glove_ID)
glove_data += "[WG.glove_ID] ([H]'s worn [WG.name])"
if (!WG.hide_prints)
fingerprint_data += "
[H]'s fingerprints: [md5(H.bioHolder.Uid)]"
else
fingerprint_data += "
Unable to scan [H]'s fingerprints."
else
fingerprint_data += "
[H]'s fingerprints: [md5(H.bioHolder.Uid)]"
if (H.gunshot_residue) // Left by firing a kinetic gun.
forensic_data += "
Gunshot residue found."
if (H.implant && H.implant.len > 0)
var/wounds = null
for (var/obj/item/implant/I in H)
if (istype(I, /obj/item/implant/projectile))
wounds ++
if (wounds)
forensic_data += "
[wounds] gunshot [wounds == 1 ? "wound" : "wounds"] detected."
if (H.fingerprints) // Left by grabbing or pulling people.
var/list/FFP = params2list(H:fingerprints)
for(var/i in FFP)
fingerprint_data += "
Foreign fingerprint on [H]: [i]"
if (H.bioHolder.Uid) // For quick reference. Also, attacking somebody only makes their clothes bloody, not the mob (think naked dudes).
blood_data += "
[H]'s blood DNA: [H.bioHolder.Uid]"
if (H.blood_DNA && isnull(H.gloves)) // Don't magically detect blood through worn gloves.
var/list/BH = params2list(H:blood_DNA)
for(var/i in BH)
blood_data += "
Blood on [H]'s hands: [i]"
var/list/gear_to_check = list(H.head, H.wear_mask, H.w_uniform, H.wear_suit, H.belt, H.gloves, H.back)
for (var/obj/item/check in gear_to_check)
if (check && check.blood_DNA)
var/list/BC = params2list(check.blood_DNA)
for(var/i in BC)
blood_data += "
Blood on worn [check.name]: [i]"
if (H.r_hand && H.r_hand.blood_DNA)
var/list/BIR = params2list(H.r_hand.blood_DNA)
for(var/i in BIR)
blood_data += "
Blood on held [H.r_hand.name]: [i]"
if (H.l_hand && H.l_hand.blood_DNA)
var/list/BIL = params2list(H.l_hand.blood_DNA)
for(var/i in BIL)
blood_data += "
Blood on held [H.l_hand.name]: [i]"
else
if (!A.fingerprints)
fingerprint_data += "
Unable to locate any fingerprints."
else
var/list/FP = params2list(A:fingerprints)
for(var/i in FP)
fingerprint_data += "
[i]"
if(!A.blood_DNA)
blood_data += "
Unable to locate any blood traces."
else
var/list/DNA = params2list(A:blood_DNA)
for(var/i in DNA)
blood_data += "
[i]"
if (istype(A, /obj/item))
var/obj/item/I = A
if(I.contraband)
contraband_data = "(CONTRABAND: LEVEL [I.contraband])"
if (istype(A, /obj/item/clothing/gloves))
var/obj/item/clothing/gloves/G = A
if (G.glove_ID)
glove_data += "[G.glove_ID] [G.material_prints ? "([G.material_prints])" : null]"
if (istype(A, /obj/item/casing/))
var/obj/item/casing/C = A
if(C.forensic_ID)
forensic_data += "
Forensic profile of [C]: [C.forensic_ID]"
if (istype(A, /obj/item/implant/projectile))
var/obj/item/implant/projectile/P = A
if(P.forensic_ID)
forensic_data += "
Forensic profile of [P]: [P.forensic_ID]"
if (istype(A, /obj/item/gun))
var/obj/item/gun/G = A
if(G.forensic_ID)
forensic_data += "
Forensic profile of [G]: [G.forensic_ID]"
if (istype(A, /turf/simulated/wall))
var/turf/simulated/wall/W = A
if (W.forensic_impacts && islist(W.forensic_impacts) && W.forensic_impacts.len)
for(var/i in W.forensic_impacts)
forensic_data += "
Forensic signature found: [i]"
if (!fingerprint_data) // Just in case, we'd always want to have a readout for these.
fingerprint_data = "
Unable to locate any fingerprints."
if (!blood_data)
blood_data = "
Unable to locate any blood traces."
// This was the least enjoyable part of the entire exercise. Formatting is nothing but a chore.
var/data = "--------------------------------
\
Forensic analysis of [A] [contraband_data ? "[contraband_data]" : null]
\
\
Isolated fingerprints:[fingerprint_data]
\
\
Isolated blood samples:[blood_data]
\
[forensic_data ? "
Additional forensic data:[forensic_data]
" : null]\
[glove_data ? "
Material analysis: [glove_data]" : null]\
"
return data
// Made this a global proc instead of 10 or so instances of duplicate code spread across the codebase (Convair880).
/proc/scan_atmospheric(var/atom/A as turf|obj, var/pda_readout = 0, var/simple_output = 0)
if (!A)
if (pda_readout == 1)
return "Unable to obtain a reading."
else if (simple_output == 1)
return "(Error: no source provided)"
else
return "Unable to obtain a reading."
var/datum/gas_mixture/check_me = null
var/pressure = null
var/total_moles = null
if (hasvar(A, "air_contents"))
check_me = A:air_contents // Not pretty, but should be okay here.
if (isturf(A))
check_me = A.return_air()
if (istype(A, /obj/machinery/atmospherics/pipe))
var/obj/machinery/atmospherics/pipe/P = A
check_me = P.parent.air
if (istype(A, /obj/item/assembly/time_bomb))
var/obj/item/assembly/time_bomb/TB = A
if (TB.part3)
check_me = TB.part3.air_contents
if (istype(A, /obj/item/assembly/radio_bomb))
var/obj/item/assembly/radio_bomb/RB = A
if (RB.part3)
check_me = RB.part3.air_contents
if (istype(A, /obj/item/assembly/proximity_bomb))
var/obj/item/assembly/proximity_bomb/PB = A
if (PB.part3)
check_me = PB.part3.air_contents
if (istype(A, /obj/item/flamethrower/))
var/obj/item/flamethrower/FT = A
if (FT.part4)
check_me = FT.part4.air_contents
if (!check_me || !istype(check_me, /datum/gas_mixture/))
if (pda_readout == 1)
return "[A] does not contain any gas."
else if (simple_output == 1)
return "([A] has no gas holder)"
else
return "[A] does not contain any gas."
pressure = check_me.return_pressure()
total_moles = check_me.total_moles()
//DEBUG_MESSAGE("[A] contains: [pressure] kPa, [total_moles] moles.")
var/data = ""
if (total_moles > 0)
var/o2_concentration = check_me.oxygen/total_moles
var/n2_concentration = check_me.nitrogen/total_moles
var/co2_concentration = check_me.carbon_dioxide/total_moles
var/plasma_concentration = check_me.toxins/total_moles
var/unknown_concentration = 1 - (o2_concentration + n2_concentration + co2_concentration + plasma_concentration)
if (pda_readout == 1) // Output goes into PDA interface, not the user's chatbox.
data = "Air Pressure: [round(pressure, 0.1)] kPa
\
Nitrogen: [round(n2_concentration * 100)]%
\
Oxygen: [round(o2_concentration * 100)]%
\
CO2: [round(co2_concentration * 100)]%
\
Plasma: [round(plasma_concentration * 100)]%
\
[unknown_concentration > 0.01 ? "Unknown: [round(unknown_concentration * 100)]%
" : ""]\
Temperature: [round(check_me.temperature - T0C)]°C
"
else if (simple_output == 1) // For the log_atmos() proc.
data = "(Pressure: [round(pressure, 0.1)] kPa, Temp: [round(check_me.temperature - T0C)]°C\
, Contents: [round(n2_concentration * 100)]% N2 / [round(o2_concentration * 100)]% O2 / [round(co2_concentration * 100)]% CO2 / [round(plasma_concentration * 100)]% PL\
[unknown_concentration > 0.01 ? " / [round(unknown_concentration * 100)]% other)" : ")"]"
else
data = "--------------------------------
\
Atmospheric analysis of [A]
\
\
Pressure: [round(pressure, 0.1)] kPa
\
Nitrogen: [round(n2_concentration * 100)]%
\
Oxygen: [round(o2_concentration * 100)]%
\
CO2: [round(co2_concentration * 100)]%
\
Plasma: [round(plasma_concentration * 100)]%
\
[unknown_concentration > 0.01 ? "Unknown: [round(unknown_concentration * 100)]%
" : ""]\
Temperature: [round(check_me.temperature - T0C)]°C
"
else
// Only used for "Atmospheric Scan" accessible through the PDA interface, which targets the turf
// the PDA user is standing on. Everything else (i.e. clicking with the PDA on objects) goes in the chatbox.
if (pda_readout == 1)
data = "This area does not contain any gas."
else if (simple_output == 1)
data = "(Contents: empty)"
else
data = "[A] does not contain any gas."
return data
// Yeah, another scan I made into a global proc (Convair880).
/proc/scan_plant(var/atom/A as turf|obj, var/mob/user as mob)
if (!A || !user || !ismob(user))
return
var/datum/plant/P = null
var/datum/plantgenes/DNA = null
if (istype(A, /obj/machinery/plantpot))
var/obj/machinery/plantpot/PP = A
if (!PP.current || PP.dead)
return "Cannot scan."
P = PP.current
DNA = PP.plantgenes
else if (istype(A, /obj/item/seed/))
var/obj/item/seed/S = A
if (S.isstrange || !S.planttype)
return "This seed has non-standard DNA and thus cannot be scanned."
P = S.planttype
DNA = S.plantgenes
else if (istype(A, /obj/item/reagent_containers/food/snacks/plant/))
var/obj/item/reagent_containers/food/snacks/plant/F = A
P = F.planttype
DNA = F.plantgenes
else
return
if (!P || !istype(P, /datum/plant/) || !DNA || !istype(DNA, /datum/plantgenes/))
return "Cannot scan."
HYPgeneticanalysis(user, A, P, DNA) // Just use the existing proc.
return