/*
CONTAINS:
T-RAY
DETECTIVE SCANNER
HEALTH ANALYZER
GAS ANALYZER
PLANT ANALYZER
REAGENT SCANNER
*/
/obj/item/t_scanner
name = "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 = 0
slot_flags = SLOT_BELT
w_class = 2
w_class = WEIGHT_CLASS_SMALL
item_state = "electronic"
materials = list(MAT_METAL=150)
origin_tech = "magnets=1;engineering=1"
var/scan_range = 1
var/pulse_duration = 10
/obj/item/t_scanner/longer_pulse
pulse_duration = 50
/obj/item/t_scanner/extended_range
scan_range = 3
/obj/item/t_scanner/extended_range/longer_pulse
scan_range = 3
pulse_duration = 50
/obj/item/t_scanner/Destroy()
if(on)
STOP_PROCESSING(SSobj, src)
return ..()
/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()
for(var/turf/T in range(scan_range, src.loc) )
if(!T.intact)
continue
for(var/obj/O in T.contents)
if(O.level != 1)
continue
if(O.invisibility == 101)
O.invisibility = 0
O.alpha = 128
spawn(pulse_duration)
if(O)
var/turf/U = O.loc
if(U && U.intact)
O.invisibility = 101
O.alpha = 255
for(var/mob/living/M in T.contents)
var/oldalpha = M.alpha
if(M.alpha < 255 && istype(M))
M.alpha = 255
spawn(10)
if(M)
M.alpha = oldalpha
var/mob/living/M = locate() in T
if(M && M.invisibility == 2)
M.invisibility = 0
spawn(2)
if(M)
M.invisibility = INVISIBILITY_LEVEL_TWO
/proc/chemscan(var/mob/living/user, var/mob/living/M)
if(ishuman(M))
var/mob/living/carbon/human/H = M
if(H.reagents)
if(H.reagents.reagent_list.len)
user.show_message("Subject contains the following reagents:")
for(var/datum/reagent/R in H.reagents.reagent_list)
user.show_message("[R.volume]u of [R.name][R.overdosed ? " - OVERDOSING" : "."]")
else
user.show_message("Subject contains no reagents.")
if(H.reagents.addiction_list.len)
user.show_message("Subject is addicted to the following reagents:")
for(var/datum/reagent/R in H.reagents.addiction_list)
user.show_message("[R.name] Stage: [R.addiction_stage]/5")
else
user.show_message("Subject is not addicted to any reagents.")
/obj/item/healthanalyzer
name = "Health Analyzer"
icon = 'icons/obj/device.dmi'
icon_state = "health"
item_state = "healthanalyzer"
desc = "A hand-held body scanner able to distinguish vital signs of the subject."
flags = CONDUCT
slot_flags = SLOT_BELT
throwforce = 3
w_class = WEIGHT_CLASS_TINY
throw_speed = 5
throw_range = 10
materials = list(MAT_METAL=200)
origin_tech = "magnets=1;biotech=1"
var/upgraded = 0
var/mode = 1;
/obj/item/healthanalyzer/attack(mob/living/M, mob/living/user)
if(((CLUMSY in user.mutations) || user.getBrainLoss() >= 60) && prob(50))
to_chat(user, text("You try to analyze the floor's vitals!"))
for(var/mob/O in viewers(M, null))
O.show_message(text("[user] has analyzed the floor's vitals!"), 1)
user.show_message(text("Analyzing Results for The floor:\n\t Overall Status: Healthy"), 1)
user.show_message(text("\t Damage Specifics: [0]-[0]-[0]-[0]"), 1)
user.show_message("Key: Suffocation/Toxin/Burns/Brute", 1)
user.show_message("Body Temperature: ???", 1)
return
user.visible_message("[user] has analyzed [M]'s vitals."," You have analyzed [M]'s vitals.")
if(!ishuman(M) || M.isSynthetic())
//these sensors are designed for organic life
user.show_message("Analyzing Results for ERROR:\n\t Overall Status: ERROR")
user.show_message("\t Key: Suffocation/Toxin/Burns/Brute", 1)
user.show_message("\t Damage Specifics: ? - ? - ? - ?")
user.show_message("Body Temperature: [M.bodytemperature-T0C]°C ([M.bodytemperature*1.8-459.67]°F)", 1)
user.show_message("Warning: Blood Level ERROR: --% --cl.Type: ERROR")
user.show_message("Subject's pulse: -- bpm.")
return
var/mob/living/carbon/human/H = M
var/fake_oxy = max(rand(1,40), H.getOxyLoss(), (300 - (H.getToxLoss() + H.getFireLoss() + H.getBruteLoss())))
var/OX = H.getOxyLoss() > 50 ? "[H.getOxyLoss()]" : H.getOxyLoss()
var/TX = H.getToxLoss() > 50 ? "[H.getToxLoss()]" : H.getToxLoss()
var/BU = H.getFireLoss() > 50 ? "[H.getFireLoss()]" : H.getFireLoss()
var/BR = H.getBruteLoss() > 50 ? "[H.getBruteLoss()]" : H.getBruteLoss()
if(H.status_flags & FAKEDEATH)
OX = fake_oxy > 50 ? "[fake_oxy]" : fake_oxy
user.show_message("Analyzing Results for [H]:\n\t Overall Status: dead")
else
user.show_message("Analyzing Results for [H]:\n\t Overall Status: [H.stat > 1 ? "dead" : "[H.health]% healthy"]")
user.show_message("\t Key: Suffocation/Toxin/Burns/Brute", 1)
user.show_message("\t Damage Specifics: [OX] - [TX] - [BU] - [BR]")
user.show_message("Body Temperature: [H.bodytemperature-T0C]°C ([H.bodytemperature*1.8-459.67]°F)", 1)
if(H.timeofdeath && (H.stat == DEAD || (H.status_flags & FAKEDEATH)))
user.show_message("Time of Death: [station_time_timestamp("hh:mm:ss", H.timeofdeath)]")
var/tdelta = round(world.time - H.timeofdeath)
if(tdelta < (DEFIB_TIME_LIMIT * 10))
user.show_message("Subject died [DisplayTimeText(tdelta)] ago, defibrillation may be possible!")
if(mode == 1)
var/list/damaged = H.get_damaged_organs(1,1)
user.show_message("Localized Damage, Brute/Burn:",1)
if(length(damaged) > 0)
for(var/obj/item/organ/external/org in damaged)
user.show_message("\t\t[capitalize(org.name)]: [(org.brute_dam > 0) ? "[org.brute_dam]" : "0"]-[(org.burn_dam > 0) ? "[org.burn_dam]" : "0"]")
OX = H.getOxyLoss() > 50 ? "Severe oxygen deprivation detected" : "Subject bloodstream oxygen level normal"
TX = H.getToxLoss() > 50 ? "Dangerous amount of toxins detected" : "Subject bloodstream toxin level minimal"
BU = H.getFireLoss() > 50 ? "Severe burn damage detected" : "Subject burn injury status O.K"
BR = H.getBruteLoss() > 50 ? "Severe anatomical damage detected" : "Subject brute-force injury status O.K"
if(H.status_flags & FAKEDEATH)
OX = fake_oxy > 50 ? "Severe oxygen deprivation detected" : "Subject bloodstream oxygen level normal"
user.show_message("[OX] | [TX] | [BU] | [BR]")
if(upgraded)
chemscan(user, H)
for(var/thing in H.viruses)
var/datum/disease/D = thing
if(!(D.visibility_flags & HIDDEN_SCANNER))
user.show_message("Warning: [D.form] detected\nName: [D.name].\nType: [D.spread_text].\nStage: [D.stage]/[D.max_stages].\nPossible Cure: [D.cure_text]")
if(H.undergoing_cardiac_arrest())
var/obj/item/organ/internal/heart/heart = H.get_int_organ(/obj/item/organ/internal/heart)
if(heart && !(heart.status & ORGAN_DEAD))
user.show_message("Warning: Medical Emergency detected\nName: Cardiac Arrest.\nType: The patient's heart has stopped.\nStage: 1/1.\nPossible Cure: Electric Shock")
else if(heart && (heart.status & ORGAN_DEAD))
user.show_message("Subject's heart is necrotic.")
else if(!heart)
user.show_message("Subject has no heart.")
if(H.getStaminaLoss())
user.show_message("Subject appears to be suffering from fatigue.")
if(H.getCloneLoss())
user.show_message("Subject appears to have [H.getCloneLoss() > 30 ? "severe" : "minor"] cellular damage.")
if(H.has_brain_worms())
user.show_message("Subject suffering from aberrant brain activity. Recommend further scanning.")
if(H.get_int_organ(/obj/item/organ/internal/brain))
if(H.getBrainLoss() >= 100)
user.show_message("Subject is brain dead.")
else if(H.getBrainLoss() >= 60)
user.show_message("Severe brain damage detected. Subject likely to have mental retardation.")
else if(H.getBrainLoss() >= 10)
user.show_message("Significant brain damage detected. Subject may have had a concussion.")
else
user.show_message("Subject has no brain.")
for(var/name in H.bodyparts_by_name)
var/obj/item/organ/external/e = H.bodyparts_by_name[name]
if(!e)
continue
var/limb = e.name
if(e.status & ORGAN_BROKEN)
if((e.limb_name in list("l_arm", "r_arm", "l_hand", "r_hand", "l_leg", "r_leg", "l_foot", "r_foot")) && !(e.status & ORGAN_SPLINTED))
user.show_message("Unsecured fracture in subject [limb]. Splinting recommended for transport.")
if(e.has_infected_wound())
user.show_message("Infected wound detected in subject [limb]. Disinfection recommended.")
for(var/name in H.bodyparts_by_name)
var/obj/item/organ/external/e = H.bodyparts_by_name[name]
if(!e)
continue
if(e.status & ORGAN_BROKEN)
user.show_message(text("Bone fractures detected. Advanced scanner required for location."), 1)
break
for(var/obj/item/organ/external/e in H.bodyparts)
if(e.internal_bleeding)
user.show_message(text("Internal bleeding detected. Advanced scanner required for location."), 1)
break
var/blood_id = H.get_blood_id()
if(blood_id)
if(H.bleed_rate)
user.show_message("Subject is bleeding!")
var/blood_percent = round((H.blood_volume / BLOOD_VOLUME_NORMAL)*100)
var/blood_type = H.b_type
if(blood_id != "blood")//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(H.blood_volume <= BLOOD_VOLUME_SAFE && H.blood_volume > BLOOD_VOLUME_OKAY)
user.show_message("LOW blood level [blood_percent] %, [H.blood_volume] cl, type: [blood_type]")
else if(H.blood_volume <= BLOOD_VOLUME_OKAY)
user.show_message("CRITICAL blood level [blood_percent] %, [H.blood_volume] cl, type: [blood_type]")
else
user.show_message("Blood level [blood_percent] %, [H.blood_volume] cl, type: [blood_type]")
user.show_message("Subject's pulse: [H.get_pulse(GETPULSE_TOOL)] bpm.")
var/implant_detect
for(var/obj/item/organ/internal/cyberimp/CI in H.internal_organs)
if(CI.is_robotic())
implant_detect += "[H.name] is modified with a [CI.name].
"
if(implant_detect)
user.show_message("Detected cybernetic modifications:")
user.show_message("[implant_detect]")
if(H.gene_stability < 40)
user.show_message("Subject's genes are quickly breaking down!")
else if(H.gene_stability < 70)
user.show_message("Subject's genes are showing signs of spontaneous breakdown.")
else if(H.gene_stability < 85)
user.show_message("Subject's genes are showing minor signs of instability.")
else
user.show_message("Subject's genes are stable.")
add_fingerprint(user)
/obj/item/healthanalyzer/verb/toggle_mode()
set name = "Switch Verbosity"
set category = "Object"
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/attackby(obj/item/I, mob/user, params)
if(istype(I, /obj/item/healthupgrade))
if(upgraded)
to_chat(user, "You have already installed an upgraded in the [src].")
else
to_chat(user, "You install the upgrade in the [src].")
overlays += "advanced"
playsound(loc, I.usesound, 50, 1)
upgraded = TRUE
qdel(I)
return
return ..()
/obj/item/healthanalyzer/advanced
upgraded = TRUE
/obj/item/healthanalyzer/advanced/New()
overlays += "advanced"
/obj/item/healthupgrade
name = "Health Analyzer Upgrade"
icon = 'icons/obj/device.dmi'
icon_state = "healthupgrade"
desc = "An upgrade unit that can be installed on a health analyzer for expanded functionality."
w_class = WEIGHT_CLASS_TINY
origin_tech = "magnets=2;biotech=2"
usesound = 'sound/items/deconstruct.ogg'
/obj/item/analyzer
desc = "A hand-held environmental scanner which reports current gas levels."
name = "analyzer"
icon = 'icons/obj/device.dmi'
icon_state = "atmos"
item_state = "analyzer"
w_class = WEIGHT_CLASS_SMALL
flags = CONDUCT
slot_flags = SLOT_BELT
throwforce = 0
throw_speed = 3
throw_range = 7
materials = list(MAT_METAL=30, MAT_GLASS=20)
origin_tech = "magnets=1;engineering=1"
var/cooldown = FALSE
var/cooldown_time = 250
var/accuracy // 0 is the best accuracy.
/obj/item/analyzer/attack_self(mob/user as mob)
if(user.stat)
return
var/turf/location = user.loc
if(!( istype(location, /turf) ))
return
var/datum/gas_mixture/environment = location.return_air()
var/pressure = environment.return_pressure()
var/total_moles = environment.total_moles()
user.show_message("Results:", 1)
if(abs(pressure - ONE_ATMOSPHERE) < 10)
user.show_message("Pressure: [round(pressure,0.1)] kPa", 1)
else
user.show_message("Pressure: [round(pressure,0.1)] kPa", 1)
if(total_moles)
var/o2_concentration = environment.oxygen/total_moles
var/n2_concentration = environment.nitrogen/total_moles
var/co2_concentration = environment.carbon_dioxide/total_moles
var/plasma_concentration = environment.toxins/total_moles
var/unknown_concentration = 1-(o2_concentration+n2_concentration+co2_concentration+plasma_concentration)
if(abs(n2_concentration - N2STANDARD) < 20)
user.show_message("Nitrogen: [round(n2_concentration*100)] %", 1)
else
user.show_message("Nitrogen: [round(n2_concentration*100)] %", 1)
if(abs(o2_concentration - O2STANDARD) < 2)
user.show_message("Oxygen: [round(o2_concentration*100)] %", 1)
else
user.show_message("Oxygen: [round(o2_concentration*100)] %", 1)
if(co2_concentration > 0.01)
user.show_message("CO2: [round(co2_concentration*100)] %", 1)
else
user.show_message("CO2: [round(co2_concentration*100)] %", 1)
if(plasma_concentration > 0.01)
user.show_message("Plasma: [round(plasma_concentration*100)] %", 1)
if(unknown_concentration > 0.01)
user.show_message("Unknown: [round(unknown_concentration*100)] %", 1)
user.show_message("Temperature: [round(environment.temperature-T0C)] °C", 1)
src.add_fingerprint(user)
return
/obj/item/analyzer/AltClick(mob/user) //Barometer output for measuring when the next storm happens
..()
if(!user.incapacitated() && Adjacent(user))
if(cooldown)
to_chat(user, "[src]'s barometer function is prepraring 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))
/obj/item/reagent_scanner
name = "reagent scanner"
desc = "A hand-held reagent scanner which identifies chemical agents and blood types."
icon = 'icons/obj/device.dmi'
icon_state = "spectrometer"
item_state = "analyzer"
w_class = WEIGHT_CLASS_SMALL
flags = CONDUCT
slot_flags = SLOT_BELT
throwforce = 5
throw_speed = 4
throw_range = 20
materials = list(MAT_METAL=30, MAT_GLASS=20)
origin_tech = "magnets=2;biotech=1;plasmatech=2"
var/details = FALSE
var/datatoprint = ""
var/scanning = TRUE
actions_types = list(/datum/action/item_action/print_report)
/obj/item/reagent_scanner/afterattack(obj/O, mob/user as mob)
if(user.stat)
return
if(!user.IsAdvancedToolUser())
to_chat(user, "You don't have the dexterity to do this!")
return
if(!istype(O))
return
if(!isnull(O.reagents))
var/dat = ""
var/blood_type = ""
if(O.reagents.reagent_list.len > 0)
var/one_percent = O.reagents.total_volume / 100
for(var/datum/reagent/R in O.reagents.reagent_list)
if(R.id != "blood")
dat += "
[TAB][R][details ? ": [R.volume / one_percent]%" : ""]"
else
blood_type = R.data["blood_type"]
dat += "
[TAB][R][blood_type ? " [blood_type]" : ""][details ? ": [R.volume / one_percent]%" : ""]"
if(dat)
to_chat(user, "Chemicals found: [dat]")
datatoprint = dat
scanning = FALSE
else
to_chat(user, "No active chemical agents found in [O].")
else
to_chat(user, "No significant chemical agents found in [O].")
return
/obj/item/reagent_scanner/adv
name = "advanced reagent scanner"
icon_state = "adv_spectrometer"
details = TRUE
origin_tech = "magnets=4;biotech=3;plasmatech=3"
/obj/item/reagent_scanner/proc/print_report()
if(!scanning)
usr.visible_message("[src] rattles and prints out a sheet of paper.")
playsound(loc, 'sound/goonstation/machines/printer_thermal.ogg', 50, 1)
sleep(50)
var/obj/item/paper/P = new(get_turf(src))
P.name = "Reagent Scanner Report: [station_time_timestamp()]"
P.info = "
| Organ | " dat += "Burn Damage | " dat += "Brute Damage | " dat += "Other Wounds | " dat += "|
|---|---|---|---|---|
| [e.name] | [e.burn_dam] | [e.brute_dam] | [robot][bled][AN][splint][open][infected][imp][internal_bleeding][lung_ruptured] | " dat += "|
| [i.name] | N/A | [i.damage] | [infection]:[mech] | " dat += " |