Files
Batrachophreno 8111a8489b Restore many missing inhand sprites for items, add/port others (#21831)
Many, many, many items have inhand sprites in their .dmis but for
whatever reasons do not display them in-game. This PR:

1. Updates many item definitions to point to their already-existing
inhands correctly. This consists largely of held tools, but also gas
tanks and jetpacks mounted in the suit storage slot.

2. Adds a few codersprites made by me for objects with either missing
inhands or poorly matching mishands (IE, the tape recorder, which has a
black case, reused the white inhand sprites of the health analyzer). The
new sprites are modified or recolored variations of other inhand sprites
from our repo, except for circuitboards which are new.
<img width="444" height="400" alt="image"
src="https://github.com/user-attachments/assets/7f107b9a-fe24-4e31-8f16-4d34768ee117"
/>


3. Adds inhand sprites for Inflatables and Inflatable Boxes made by
Tomixcomics.
<img width="424" height="101" alt="image"
src="https://github.com/user-attachments/assets/434107c4-8577-49a2-a58e-d6b014c03933"
/>

4. Ports inhand sprites for the Hydraulic Rescue Tool from tg's Jaws of
Life.
<img width="224" height="94" alt="Screenshot 2026-02-07 172931"
src="https://github.com/user-attachments/assets/070c7956-f6a8-4fb5-870f-10c64afcc8b3"
/>

5. Some additional cleanup while in the area. The 'analyzer' has been
renamed the 'gas analyzer' to be consistent with the other analyzer
objects, standardized icon_state naming conventions where I saw
oddballs, updated code docs to use DMDocs when in the area, etc.

### Asset Licenses
The following assets that **have not** been created by myself are
included in this PR:

| Path | Original Author | License |
| --- | --- | --- |
| icons/obj/item/hydraulic_rescue_tool.dmi | [SomeAngryMiner (bee
station)](https://github.com/BeeStation/BeeStation-Hornet/pull/2487),
[maxymax
(/tg/station)](https://github.com/tgstation/tgstation/pull/58616) |
CC-BY-SA |
| icons/obj/item/inflatables.dmi |
[Tomixcomics](https://github.com/tomixcomics) | CC-BY-SA |
2026-02-17 17:51:22 +00:00

841 lines
31 KiB
Plaintext

/*
CONTAINS:
HEALTH ANALYZER
GAS ANALYZER
MASS SPECTROMETER
REAGENT SCANNER
BREATH ANALYZER
*/
/obj/item/healthanalyzer
name = "health analyzer"
desc = "A hand-held body scanner able to distinguish vital signs of the subject."
icon = 'icons/obj/item/scanner.dmi'
icon_state = "healthanalyzer"
item_state = "healthanalyzer"
contained_sprite = TRUE
obj_flags = OBJ_FLAG_CONDUCTABLE
slot_flags = SLOT_BELT
throwforce = 3
w_class = WEIGHT_CLASS_SMALL
throw_speed = 5
throw_range = 10
matter = list(MATERIAL_ALUMINIUM = 200)
origin_tech = list(TECH_MAGNET = 1, TECH_BIO = 1)
var/last_scan = 0
var/mode = 1
var/sound_scan = FALSE
/obj/item/healthanalyzer/attack(mob/living/target_mob, mob/living/user, target_zone)
sound_scan = FALSE
if(last_scan <= world.time - 20) //Spam limiter.
last_scan = world.time
sound_scan = TRUE
user.setClickCooldown(DEFAULT_QUICK_COOLDOWN)
flick("[icon_state]-scan", src) //makes it so that it plays the scan animation on a successful scan
health_scan_mob(target_mob, user, mode, sound_scan = sound_scan)
add_fingerprint(user)
/obj/item/healthanalyzer/attack_self(mob/user)
sound_scan = FALSE
if(last_scan <= world.time - 20) //Spam limiter.
last_scan = world.time
sound_scan = TRUE
user.setClickCooldown(DEFAULT_QUICK_COOLDOWN)
flick("[icon_state]-scan", src) //makes it so that it plays the scan animation on a successful scan
health_scan_mob(user, user, mode, sound_scan = sound_scan)
add_fingerprint(user)
/// Calculates severity based on the ratios defined external limbs.
/proc/get_wound_severity(damage_ratio, can_heal_overkill, uppercase = FALSE)
var/degree = "none"
switch(damage_ratio)
if (0 to 10)
degree = "minor"
if (11 to 25)
degree = "moderate"
if (26 to 50)
degree = "significant"
if (51 to 75)
degree = "severe"
if (76 to 99)
degree = "extreme"
if (100 to INFINITY)
degree = can_heal_overkill ? "critical" : "irreparable"
if(uppercase)
degree = capitalize(degree)
return degree
/proc/get_severity(amount, can_heal_overkill, uppercase = FALSE)
var/output = "none"
switch(amount)
if (0)
output = "none"
if (1 to 10)
output = "minor"
if (11 to 25)
output = "moderate"
if (26 to 50)
output = "significant"
if (51 to 75)
output = "severe"
if (76 to 99)
output = "extreme"
if (100 to INFINITY)
output = can_heal_overkill ? "critical" : "irreparable"
if(uppercase)
output = capitalize(output)
return output
/proc/health_scan_mob(var/mob/M, var/mob/living/user, var/show_limb_damage = TRUE, var/just_scan = FALSE, var/sound_scan)
if(!just_scan)
if (((user.is_clumsy()) || (user.mutations & DUMB)) && prob(50))
user.visible_message("<b>[user]</b> runs the scanner over the floor.",
SPAN_NOTICE("You run the scanner over the floor."),
SPAN_NOTICE("You hear metal repeatedly clunking against the floor."))
to_chat(user, SPAN_NOTICE("<b>Scan results for the ERROR:</b>"))
if(sound_scan)
playsound(user.loc, 'sound/items/healthscanner/healthscanner_used.ogg', 25, extrarange = SILENCED_SOUND_EXTRARANGE)
return
if(!user.IsAdvancedToolUser())
to_chat(user, SPAN_WARNING("You don't have the dexterity to do this!"))
return
user.visible_message("<b>[user]</b> runs a scanner over [M].",SPAN_NOTICE("You run the scanner over [M]."))
if(!istype(M, /mob/living/carbon/human))
to_chat(user, SPAN_WARNING("This scanner is designed for humanoid patients only."))
if(sound_scan)
playsound(user.loc, 'sound/items/healthscanner/healthscanner_used.ogg', 25, extrarange = SILENCED_SOUND_EXTRARANGE)
return
var/mob/living/carbon/human/H = M
if(H.isSynthetic() && !H.isFBP())
to_chat(user, SPAN_WARNING("This scanner is designed for organic humanoid patients only."))
if(sound_scan)
playsound(user.loc, 'sound/items/healthscanner/healthscanner_used.ogg', 25, extrarange = SILENCED_SOUND_EXTRARANGE)
return
. = list()
var/header = list()
var/b
var/endb
var/dat = list()
header += "<style> .scan_notice{color: #5f94af;}</style>"
header += "<style> .scan_warning{color: #ff0000; font-style: italic;}</style>"
header += "<style> .scan_danger{color: #ff0000; font-weight: bold;}</style>"
header += "<style> .scan_red{color:red}</style>"
header += "<style> .scan_green{color:green}</style>"
header += "<style> .scan_blue{color: #5f94af}</style>"
header += "<style> .scan_orange{color:#ffa500}</style>"
b = "<b>"
endb = "</b>"
. += "[b]Scan results for \the [H]:[endb]"
if(H.stat == DEAD || H.status_flags & FAKEDEATH)
dat += "<span class='scan_warning'>[b]Time of Death:[endb] [worldtime2text(H.timeofdeath)]</span>"
// Brain activity.
var/brain_status = H.get_brain_status()
dat += "Brain activity: [brain_status]"
var/brain_result = H.get_brain_result()
if(sound_scan)
switch(brain_result)
if(0)
playsound(user.loc, 'sound/items/healthscanner/healthscanner_dead.ogg', 25, extrarange = SILENCED_SOUND_EXTRARANGE)
if(-1)
playsound(user.loc, 'sound/items/healthscanner/healthscanner_used.ogg', 25, extrarange = SILENCED_SOUND_EXTRARANGE)
else
if(brain_result <= 25)
playsound(user.loc, 'sound/items/healthscanner/healthscanner_critical.ogg', 25, extrarange = SHORT_RANGE_SOUND_EXTRARANGE)
else if(brain_result <= 50)
playsound(user.loc, 'sound/items/healthscanner/healthscanner_danger.ogg', 25, extrarange = SHORT_RANGE_SOUND_EXTRARANGE)
else if(brain_result <= 90)
playsound(user.loc, 'sound/items/healthscanner/healthscanner_used.ogg', 25, extrarange = SILENCED_SOUND_EXTRARANGE)
else
playsound(user.loc, 'sound/items/healthscanner/healthscanner_stable.ogg', 25, extrarange = SILENCED_SOUND_EXTRARANGE)
// Pulse rate.
var/pulse_result = "normal"
if(H.should_have_organ(BP_HEART))
if(H.status_flags & FAKEDEATH)
pulse_result = SPAN_DANGER("0")
else
pulse_result = H.get_pulse(GETPULSE_TOOL)
if(H.pulse() == PULSE_NONE)
pulse_result = "<span class='scan_danger'>[pulse_result] BPM</span>"
else if(H.pulse() < PULSE_NORM)
pulse_result = "<span class='scan_notice'>[pulse_result] BPM</span>"
else if(H.pulse() > PULSE_NORM)
pulse_result = "<span class='scan_warning'>[pulse_result] BPM</span>"
else
pulse_result = "<span class='scan_green'>[pulse_result] BPM</span>"
else
pulse_result = "<span class='scan_danger'>0</span>"
dat += "Pulse rate: [pulse_result]"
// Body temperature. Rounds to one digit after decimal.
var/temperature_string
if(H.bodytemperature < H.species.cold_level_1 || H.bodytemperature > H.species.heat_level_1)
temperature_string = "Body temperature: <span class='scan_warning'>[round(H.bodytemperature-T0C, 0.1)]&deg;C ([round(H.bodytemperature*1.8-459.67, 0.1)]&deg;F)</span>"
else
temperature_string = "Body temperature: <span class='scan_green'>[round(H.bodytemperature-T0C, 0.1)]&deg;C ([round(H.bodytemperature*1.8-459.67, 0.1)]&deg;F)</span>"
dat += temperature_string
// Blood pressure and blood type. Based on the idea of a normal blood pressure being 120 over 80.
if(H.should_have_organ(BP_HEART))
var/blood_pressure_string
switch(H.get_blood_pressure_alert())
if(1)
blood_pressure_string = "<span class='scan_danger'>[H.get_blood_pressure()]</span>"
if(2)
blood_pressure_string = "<span class='scan_green'>[H.get_blood_pressure()]</span>"
if(3)
blood_pressure_string = "<span class='scan_warning'>[H.get_blood_pressure()]</span>"
if(4)
blood_pressure_string = "<span class='scan_danger'>[H.get_blood_pressure()]</span>"
var/blood_volume_string = "<span class='scan_green'>\>[BLOOD_VOLUME_SAFE]%</span>"
switch(H.get_blood_volume())
if(BLOOD_VOLUME_OKAY to BLOOD_VOLUME_SAFE)
blood_volume_string = "<span class='scan_notice'>\<[BLOOD_VOLUME_SAFE]%</span>"
if(BLOOD_VOLUME_SURVIVE to BLOOD_VOLUME_OKAY)
blood_volume_string = "<span class='scan_warning'>\<[BLOOD_VOLUME_OKAY]%</span>"
if(-(INFINITY) to BLOOD_VOLUME_SURVIVE)
blood_volume_string = "<span class='scan_danger'>\<[BLOOD_VOLUME_SURVIVE]%</span>"
var/oxygenation = H.get_blood_oxygenation()
var/oxygenation_string = "<span class='scan_green'>[oxygenation]%</span>"
switch(oxygenation)
if(BLOOD_VOLUME_OKAY to BLOOD_VOLUME_SAFE)
oxygenation_string = "<span class='scan_notice'>[oxygenation]%</span>"
if(BLOOD_VOLUME_SURVIVE to BLOOD_VOLUME_OKAY)
oxygenation_string = "<span class='scan_warning'>[oxygenation]%</span>"
if(-(INFINITY) to BLOOD_VOLUME_SURVIVE)
oxygenation_string = "<span class='scan_danger'>[oxygenation]%</span>"
if(H.status_flags & FAKEDEATH)
oxygenation_string = "<span class='scan_danger'>[rand(0,10)]%</span>"
dat += "Blood pressure: [blood_pressure_string]"
dat += "Blood oxygenation: [oxygenation_string]"
dat += "Blood volume: [blood_volume_string]"
dat += "Blood type: <span class ='scan_green'>[H.dna.b_type]</span>"
else
dat += "Blood pressure: N/A"
// Traumatic shock.
if(H.is_asystole() || (H.status_flags & FAKEDEATH))
dat += "<span class='scan_danger'>Cardiovascular shock detected. Administer CPR immediately.</span>"
else if(H.shock_stage > 80)
dat += "<span class='scan_warning'>Imminent cardiovascular shock. Pain relief recommended.</span>"
if(H.getOxyLoss() > 50)
dat += "<span class='scan_blue'>[b]Severe oxygen deprivation detected.[endb]</span>"
if(H.getToxLoss() > 50)
dat += "<span class='scan_orange'>[b]Major systemic organ failure detected.[endb]</span>"
if(H.getFireLoss() > 50)
dat += "<span class='scan_orange'>[b]Severe burn damage detected.[endb]</span>"
if(H.getBruteLoss() > 50)
dat += "<span class='scan_red'>[b]Severe anatomical damage detected.[endb]</span>"
if(show_limb_damage)
var/list/damaged = H.get_damaged_organs(1,1)
if(damaged.len)
for(var/obj/item/organ/external/org in damaged)
var/limb_result = "[capitalize(org.name)][BP_IS_ROBOTIC(org) ? " (Cybernetic)" : ""]:"
if(org.brute_dam > 0)
limb_result = "[limb_result] \[<font color = 'red'><b>[get_severity(org.brute_dam, TRUE)] physical trauma</b></font>\]"
if(org.burn_dam > 0)
limb_result = "[limb_result] \[<font color = '#ffa500'><b>[get_severity(org.burn_dam, TRUE)] burns</b></font>\]"
if(org.status & ORGAN_BLEEDING)
limb_result = "[limb_result] \[<span class='scan_danger'>bleeding</span>\]"
var/is_bandaged = org.is_bandaged()
var/is_salved = org.is_salved()
if(is_bandaged && is_salved)
var/icon/B = icon('icons/obj/item/stacks/medical.dmi', "bandaged")
var/icon/S = icon('icons/obj/item/stacks/medical.dmi', "salved")
limb_result = "[limb_result] \[[icon2html(B, user)] | [icon2html(S, user)]\]"
else if(is_bandaged)
var/icon/B = icon('icons/obj/item/stacks/medical.dmi', "bandaged")
limb_result = "[limb_result] \[[icon2html(B, user)]\]"
else if(is_salved)
var/icon/S = icon('icons/obj/item/stacks/medical.dmi', "salved")
limb_result = "[limb_result] \[[icon2html(S, user)]\]"
dat += limb_result
else
dat += "No detectable limb injuries."
for(var/name in H.organs_by_name)
var/obj/item/organ/external/e = H.organs_by_name[name]
if(!e)
continue
var/limb = e.name
if(e.status & ORGAN_BROKEN)
if(((e.name == BP_L_ARM) || (e.name == BP_R_ARM) || (e.name == BP_L_LEG) || (e.name == BP_R_LEG)) && !(e.status & ORGAN_SPLINTED))
dat += "<span class='scan_warning'>Unsecured fracture in subject [limb]. Splinting recommended for transport.</span>"
for(var/name in H.organs_by_name)
var/obj/item/organ/external/e = H.organs_by_name[name]
if(e && e.status & ORGAN_BROKEN)
dat += "<span class='scan_warning'>Bone fractures detected. Advanced scanner required for location.</span>"
break
var/found_bleed
var/found_tendon
var/found_disloc
for(var/obj/item/organ/external/e in H.organs)
if(e)
if(!found_disloc && e.dislocated == 2)
dat += "<span class='scan_warning'>Dislocation detected. Advanced scanner required for location.</span>"
found_disloc = TRUE
if(!found_bleed && (e.status & ORGAN_ARTERY_CUT))
dat += "<span class='scan_warning'>Arterial bleeding detected. Advanced scanner required for location.</span>"
found_bleed = TRUE
if(!found_tendon && (e.tendon_status() & TENDON_CUT))
dat += "<span class='scan_warning'>Tendon or ligament damage detected. Advanced scanner required for location.</span>"
found_tendon = TRUE
if(found_disloc && found_bleed && found_tendon)
break
. += dat
dat = list()
// Reagent data.
. += "[b]Reagent scan:[endb]"
var/print_reagent_default_message = TRUE
if(H.reagents.total_volume)
var/unknown = 0
var/reagentdata[0]
for(var/_R in H.reagents.reagent_volumes)
var/singleton/reagent/R = GET_SINGLETON(_R)
if(R.scannable)
print_reagent_default_message = FALSE
reagentdata["[_R]"] = SPAN_NOTICE(" [round(REAGENT_VOLUME(H.reagents, _R), 1)]u [R.name]")
else
unknown++
if(reagentdata.len)
print_reagent_default_message = FALSE
dat += SPAN_NOTICE("Beneficial reagents detected in subject's blood:")
for(var/d in reagentdata)
dat += reagentdata[d]
if(unknown)
print_reagent_default_message = FALSE
dat += SPAN_WARNING("Warning: Unknown substance[(unknown>1)?"s":""] detected in subject's blood.")
var/datum/reagents/ingested = H.get_ingested_reagents()
if(ingested && ingested.total_volume)
var/unknown = 0
for(var/_R in ingested.reagent_volumes)
var/singleton/reagent/R = GET_SINGLETON(_R)
if(R.scannable)
print_reagent_default_message = FALSE
dat += SPAN_NOTICE("[R.name] found in subject's stomach.")
else
++unknown
if(unknown)
print_reagent_default_message = FALSE
dat += SPAN_WARNING("Non-medical reagent[(unknown > 1)?"s":""] found in subject's stomach.")
if(print_reagent_default_message)
dat += "No results."
. += dat
header = jointext(header, null)
. = jointext(.,"<br>")
. = jointext(list(header,.),null)
if(user)
to_chat(user, "<hr>")
to_chat(user, .)
to_chat(user, "<hr>")
/obj/item/healthanalyzer/verb/toggle_mode()
set name = "Switch Verbosity"
set category = "Object.Held"
set src in usr
mode = !mode
if(mode)
to_chat(usr, "The scanner now shows specific limb damage.")
else
to_chat(usr, "The scanner no longer shows limb damage.")
/obj/item/analyzer
name = "gas analyzer"
desc = "A hand-held environmental scanner which reports current gas levels."
icon = 'icons/obj/item/scanner.dmi'
icon_state = "airanalyzer"
item_state = "airanalyzer"
contained_sprite = TRUE
w_class = WEIGHT_CLASS_SMALL
obj_flags = OBJ_FLAG_CONDUCTABLE
slot_flags = SLOT_BELT
throwforce = 5
throw_speed = 4
throw_range = 20
matter = list(MATERIAL_ALUMINIUM = 30, MATERIAL_GLASS = 20)
origin_tech = list(TECH_MAGNET = 1, TECH_ENGINEERING = 1)
/obj/item/analyzer/atmosanalyze(var/mob/user)
if(!user) return
var/air = user.return_air()
if (!air) return
flick("[icon_state]-scan", src)
return atmosanalyzer_scan(src, air, user)
/obj/item/analyzer/attack_self(mob/user as mob)
if (user.stat)
return
if (!usr.IsAdvancedToolUser())
to_chat(usr, SPAN_WARNING("You don't have the dexterity to do this!"))
return
analyze_gases(src, user)
return
/obj/item/mass_spectrometer
name = "mass spectrometer"
desc = "A hand-held mass spectrometer which identifies trace chemicals in a blood sample."
icon = 'icons/obj/item/scanner.dmi'
icon_state = "spectrometer"
// Reuses the basic health analyzer inhands.
item_state = "healthanalyzer"
contained_sprite = TRUE
w_class = WEIGHT_CLASS_SMALL
atom_flags = ATOM_FLAG_OPEN_CONTAINER
obj_flags = OBJ_FLAG_CONDUCTABLE
slot_flags = SLOT_BELT
throwforce = 5
throw_speed = 4
throw_range = 20
matter = list(MATERIAL_ALUMINIUM = 30, MATERIAL_GLASS = 20)
origin_tech = list(TECH_MAGNET = 2, TECH_BIO = 2)
var/details = FALSE
/obj/item/mass_spectrometer/Initialize()
. = ..()
create_reagents(5)
/obj/item/mass_spectrometer/on_reagent_change()
clear_blood_overlay()
if(reagents.total_volume)
icon_state = initial(icon_state) + "_s"
var/image/I = image(icon, null, "[initial(icon_state)]-reagent")
I.color = reagents.get_color()
add_blood_overlay(I)
else
icon_state = initial(icon_state)
/obj/item/mass_spectrometer/proc/clear_blood_overlay()
underlays = null
/obj/item/mass_spectrometer/proc/add_blood_overlay(var/image/I)
underlays += I
/obj/item/mass_spectrometer/attack_self(mob/user)
if(use_check_and_message(user))
return
if(reagents.total_volume)
if(LAZYLEN(reagents.reagent_volumes) > 1)
to_chat(user, SPAN_WARNING("There isn't enough blood in the sample!"))
return
if(!REAGENT_DATA(reagents, /singleton/reagent/blood))
to_chat(user, SPAN_WARNING("The sample was contaminated with non-blood reagents!"))
return
var/list/blood_traces = reagents.reagent_data[/singleton/reagent/blood]["trace_chem"]
var/list/output_text = list("Trace Chemicals Found:")
for(var/_C in blood_traces)
var/singleton/reagent/C = GET_SINGLETON(_C)
if(C.spectro_hidden && !details)
continue
if(details)
output_text += "- [C] ([max(round(blood_traces[_C], 0.1), 0.1)] units)"
else
output_text += "- [C]"
if(length(output_text) == 1)
output_text[1] = SPAN_NOTICE("No trace chemicals found.")
to_chat(user, jointext(output_text, "\n"))
/obj/item/mass_spectrometer/adv
name = "advanced mass spectrometer"
icon_state = "spectrometer_adv"
details = TRUE
origin_tech = list(TECH_MAGNET = 4, TECH_BIO = 2)
/obj/item/mass_spectrometer/adv/clear_blood_overlay()
ClearOverlays()
/obj/item/mass_spectrometer/adv/add_blood_overlay(var/image/I)
AddOverlays(I)
/obj/item/reagent_scanner
name = "reagent scanner"
desc = "A hand-held reagent scanner which identifies chemical agents."
icon = 'icons/obj/item/scanner.dmi'
icon_state = "reagent_scanner"
// Reuses the basic health analyzer inhands.
item_state = "healthanalyzer"
contained_sprite = TRUE
w_class = WEIGHT_CLASS_SMALL
obj_flags = OBJ_FLAG_CONDUCTABLE
slot_flags = SLOT_BELT
throwforce = 5
throw_speed = 4
throw_range = 20
matter = list(MATERIAL_ALUMINIUM = 30, MATERIAL_GLASS = 20)
origin_tech = list(TECH_MAGNET = 2, TECH_BIO = 2)
var/details = 0
var/recent_fail = 0
/obj/item/reagent_scanner/afterattack(obj/O, mob/user, proximity)
if(!proximity)
return
if(use_check_and_message(user))
return
if(!istype(O))
return
if(isemptylist(O.reagents?.reagent_volumes))
to_chat(user, SPAN_WARNING("No active chemical agents found in [O]."))
return
var/dat = ""
var/one_percent = O.reagents.total_volume / 100
for (var/_R in O.reagents.reagent_volumes)
var/singleton/reagent/R = GET_SINGLETON(_R)
dat += "\n \t [R][details ? ": [O.reagents.reagent_volumes[_R] / one_percent]%" : ""]"
to_chat(user, SPAN_NOTICE("Chemicals found: [dat]"))
/obj/item/reagent_scanner/adv
name = "advanced reagent scanner"
icon_state = "reagent_scanner_adv"
// Reuses the advanced health analyzer inhands.
item_state = "healthanalyzer_adv"
details = 1
origin_tech = list(TECH_MAGNET = 4, TECH_BIO = 2)
/obj/item/slime_scanner
name = "slime scanner"
icon = 'icons/obj/item/scanner.dmi'
icon_state = "slime_scanner"
item_state = "slime_scanner"
contained_sprite = TRUE
origin_tech = list(TECH_BIO = 1)
w_class = WEIGHT_CLASS_SMALL
obj_flags = OBJ_FLAG_CONDUCTABLE
throwforce = 0
throw_speed = 3
throw_range = 7
matter = list(MATERIAL_ALUMINIUM = 30, MATERIAL_GLASS = 20)
/obj/item/slime_scanner/attack(mob/living/target_mob, mob/living/user, target_zone)
if(!isslime(target_mob))
to_chat(user, SPAN_WARNING("This device can only scan slimes!"))
return
var/mob/living/carbon/slime/T = target_mob
to_chat(user, SPAN_NOTICE("**************************"))
to_chat(user, SPAN_NOTICE("Slime scan results:"))
to_chat(user, SPAN_NOTICE(capitalize_first_letters("[T.colour] [T.is_adult ? "adult" : "baby"] slime")))
to_chat(user, SPAN_NOTICE("Health: [T.health]"))
to_chat(user, SPAN_NOTICE("Nutrition: [T.nutrition]/[T.get_max_nutrition()]"))
if(T.nutrition < T.get_starve_nutrition())
to_chat(user, SPAN_ALERT("Warning: slime is starving!"))
else if (T.nutrition < T.get_hunger_nutrition())
to_chat(user, SPAN_WARNING("Warning: slime is hungry!"))
to_chat(user, SPAN_NOTICE("Electric charge strength: [T.powerlevel]"))
to_chat(user, SPAN_NOTICE("Growth progress: [T.amount_grown]/10"))
if(T.cores > 1)
to_chat(user, SPAN_WARNING("Anomalous number of slime cores detected."))
else if(!T.cores)
to_chat(user, SPAN_WARNING("No slime cores detected."))
to_chat(user, SPAN_NOTICE("Genetic Information:"))
if(T.slime_mutation[4] == T.colour)
to_chat(user, SPAN_WARNING("This slime cannot evolve any further."))
else
var/list/poss_mutations = uniquelist(T.slime_mutation)
var/mutation_message = capitalize(english_list(poss_mutations))
to_chat(user, SPAN_NOTICE(mutation_message))
var/mut_chance = T.mutation_chance / (poss_mutations.len > 2 ? 1 : 2)
to_chat(user, SPAN_NOTICE("Instability: [mut_chance]% chance of mutation upon reproduction."))
to_chat(user, SPAN_NOTICE("**************************"))
/obj/item/price_scanner
name = "price scanner"
desc = "Using an up-to-date database of various costs and prices, this device estimates the market price of an item up to 0.001% accuracy."
icon = 'icons/obj/item/scanner.dmi'
icon_state = "price_scanner"
item_state = "price_scanner"
contained_sprite = TRUE
item_flags = ITEM_FLAG_NO_BLUDGEON
slot_flags = SLOT_BELT
w_class = WEIGHT_CLASS_SMALL
throwforce = 0
throw_speed = 3
throw_range = 3
matter = list(DEFAULT_WALL_MATERIAL = 25, MATERIAL_GLASS = 25)
/obj/item/price_scanner/afterattack(atom/movable/target, mob/user as mob, proximity)
if(!proximity)
return
var/value = get_value(target)
user.visible_message(SPAN_NOTICE("\The [user] scans \the [target] with \the [src]."))
to_chat(user, SPAN_NOTICE("\The [src] estimates the price of \the [target] at <b>[value ? value : "N/A"]电</b>."))
/obj/item/breath_analyzer
name = "breath analyzer"
desc = "A hand-held breath analyzer that provides a robust amount of information about the subject's respiratory system."
icon = 'icons/obj/item/scanner.dmi'
icon_state = "breath_analyzer"
// Reuses the basic health analyzer inhands.
item_state = "healthanalyzer"
contained_sprite = TRUE
w_class = WEIGHT_CLASS_SMALL
obj_flags = OBJ_FLAG_CONDUCTABLE
slot_flags = SLOT_BELT
throwforce = 0
throw_speed = 3
throw_range = 3
matter = list(MATERIAL_ALUMINIUM = 30, MATERIAL_GLASS = 20)
origin_tech = list(TECH_MAGNET = 2, TECH_BIO = 2)
/obj/item/breath_analyzer/attack(mob/living/target_mob, mob/living/user, target_zone)
var/mob/living/carbon/human/H = target_mob
if (!istype(H))
to_chat(user,SPAN_WARNING("You can't find a way to use \the [src] on [H]!"))
return
if ( ((user.is_clumsy()) || (user.mutations & DUMB)) && prob(20))
to_chat(user,SPAN_DANGER("Your hand slips from clumsiness!"))
if(!H.eyes_protected(src, FALSE))
eyestab(H,user)
to_chat(user,SPAN_DANGER("Alert: No breathing detected."))
return
if (!user.IsAdvancedToolUser())
to_chat(user,SPAN_WARNING("You don't have the dexterity to do this!"))
return
if(user == H && !H.can_eat(src))
return
else if(!H.can_force_feed(user, src))
return
user.setClickCooldown(DEFAULT_QUICK_COOLDOWN)
user.visible_message(SPAN_NOTICE("[user] is trying to take a breath sample from [H]."),
SPAN_NOTICE("You gently insert \the [src] into [H]'s mouth."))
if (!LAZYLEN(src.other_DNA))
LAZYADD(src.other_DNA, H.dna.unique_enzymes)
src.other_DNA_type = "saliva"
if (!do_after(user, 2 SECONDS, H, DO_UNIQUE & ~DO_BOTH_CAN_TURN))
return
user.visible_message(SPAN_NOTICE("[user] takes a breath sample from [H]."),
SPAN_NOTICE("\The [src] clicks as it finishes reading [H]'s breath sample."))
to_chat(user,"<b>Breath Sample Results:</b>")
if(H.stat == DEAD || H.losebreath || !H.breathing)
to_chat(user,SPAN_DANGER("Alert: No breathing detected."))
playsound(user.loc, 'sound/items/healthscanner/healthscanner_dead.ogg', 25, extrarange = SILENCED_SOUND_EXTRARANGE)
return
switch(H.getOxyLoss())
if(0 to 25)
to_chat(user,"Subject oxygen levels nominal.")
playsound(user.loc, 'sound/items/healthscanner/healthscanner_stable.ogg', 25, extrarange = SILENCED_SOUND_EXTRARANGE)
if(25 to 50)
to_chat(user,SPAN_NOTICE("Subject oxygen levels abnormal."))
playsound(user.loc, 'sound/items/healthscanner/healthscanner_danger.ogg', 25, extrarange = SHORT_RANGE_SOUND_EXTRARANGE)
if(50 to INFINITY)
to_chat(user,SPAN_NOTICE("<b>Severe oxygen deprivation detected.</b>"))
playsound(user.loc, 'sound/items/healthscanner/healthscanner_critical.ogg', 25, extrarange = SHORT_RANGE_SOUND_EXTRARANGE)
var/obj/item/organ/internal/L = H.internal_organs_by_name[BP_LUNGS]
if(istype(L))
if(L.is_bruised())
to_chat(user,SPAN_WARNING("<b>Ruptured lung detected.</b>"))
else if(L.is_damaged())
to_chat(user,"<b>Damaged lung detected.</b>")
else
to_chat(user,"Subject lung health nominal.")
else
to_chat(user,SPAN_WARNING("Subject lung health unknown."))
var/additional_string = "<font color='green'>\[NORMAL\]</font>"
var/bac = H.get_blood_alcohol()
switch(bac)
if(INTOX_JUDGEIMP to INTOX_MUSCLEIMP)
additional_string = "\[LIGHTLY INTOXICATED\]"
if(INTOX_MUSCLEIMP to INTOX_BALANCE)
additional_string = "\[MODERATELY INTOXICATED\]"
if(INTOX_BALANCE to INTOX_BLACKOUT)
additional_string = "<span class='warning'>\[HEAVILY INTOXICATED\]</span>"
if(INTOX_BLACKOUT to INTOX_DEATH)
additional_string = "<span class='warning'>\[ALCOHOL POISONING LIKELY\]</span>"
if(INTOX_DEATH to INFINITY)
additional_string = SPAN_WARNING("\[DEATH IMMINENT\]")
to_chat(user,"<span class='normal'>Blood Alcohol Content: [round(bac,0.01)] <b>[additional_string]</b></span>")
if(H.breathing && H.breathing.total_volume)
var/unknown = 0
for(var/_R in H.breathing.reagent_volumes)
var/singleton/reagent/R = GET_SINGLETON(_R)
if(R.scannable)
to_chat(user,SPAN_NOTICE("[R.name] found in subject's respiratory system."))
else
++unknown
if(unknown)
to_chat(user,SPAN_WARNING("Non-medical reagent[(unknown > 1)?"s":""] found in subject's respiratory system."))
/obj/item/advanced_healthanalyzer
name = "advanced health analyzer"
desc = "An expensive and varied-use health analyzer that prints full-body scans after a short scanning delay."
icon = 'icons/obj/item/scanner.dmi'
icon_state = "healthanalyzer_adv"
item_state = "healthanalyzer_adv"
contained_sprite = TRUE
slot_flags = SLOT_BELT
w_class = WEIGHT_CLASS_NORMAL
origin_tech = list(TECH_MAGNET = 2, TECH_BIO = 3)
var/obj/machinery/body_scanconsole/connected = null //this is used to print the date and to deal with extra
/obj/item/advanced_healthanalyzer/Initialize()
. = ..()
if(!connected)
var/obj/machinery/body_scanconsole/S = new (src)
S.forceMove(src)
S.update_use_power(POWER_USE_OFF)
connected = S
/obj/item/advanced_healthanalyzer/Destroy()
if(connected)
QDEL_NULL(connected)
return ..()
/obj/item/advanced_healthanalyzer/attack(mob/living/target_mob, mob/living/user, target_zone)
if(!connected)
return
user.setClickCooldown(DEFAULT_QUICK_COOLDOWN)
user.visible_message("<b>[user]</b> starts scanning \the [target_mob] with \the [src].", SPAN_NOTICE("You start scanning \the [target_mob] with \the [src]."))
flick("[icon_state]-scan", src)
if(do_after(user, 7 SECONDS, target_mob, DO_UNIQUE))
print_scan(target_mob, user)
add_fingerprint(user)
/obj/item/advanced_healthanalyzer/proc/print_scan(var/mob/M, var/mob/living/user)
var/obj/item/paper/medscan/R = new /obj/item/paper/medscan(src, connected.format_occupant_data(get_occupant_data(M)), "Scan ([M.name]) ([worldtime2text()])", M)
connected.print(R, message = "\The [src] beeps, printing \the [R] after a moment.", user = user)
/// Variant of print_scan(), main difference is different method to print the paper
/obj/item/advanced_healthanalyzer/cyborg/print_scan(var/mob/M, var/mob/living/user)
var/obj/item/paper/medscan/R = new /obj/item/paper/medscan(src, connected.format_occupant_data(get_occupant_data(M)), "Scan ([M.name]) ([worldtime2text()])", M)
user.visible_message(SPAN_NOTICE("\The [src] beeps, printing \the [R] after a moment."))
playsound(user.loc, SFX_PRINT, 50, 1)
R.forceMove(user.loc)
/obj/item/advanced_healthanalyzer/proc/get_occupant_data(var/mob/living/carbon/human/H)
if (!ishuman(H))
return
var/displayed_stat = H.stat
var/blood_oxygenation = H.get_blood_oxygenation()
if(H.status_flags & FAKEDEATH)
displayed_stat = DEAD
blood_oxygenation = min(blood_oxygenation, BLOOD_VOLUME_SURVIVE)
switch(displayed_stat)
if(CONSCIOUS)
displayed_stat = "Conscious"
if(UNCONSCIOUS)
displayed_stat = "Unconscious"
if(DEAD)
displayed_stat = "DEAD"
var/pulse_result
if(H.should_have_organ(BP_HEART))
var/obj/item/organ/internal/heart/heart = H.internal_organs_by_name[BP_HEART]
if(!heart)
pulse_result = 0
else if(BP_IS_ROBOTIC(heart))
pulse_result = -2
else if(H.status_flags & FAKEDEATH)
pulse_result = 0
else
pulse_result = H.get_pulse(GETPULSE_TOOL)
else
pulse_result = -1
if(pulse_result == ">250")
pulse_result = -3
var/datum/reagents/R = H.bloodstr
connected.has_internal_injuries = FALSE
connected.has_external_injuries = FALSE
var/list/bodyparts = connected.get_external_wound_data(H)
var/list/organs = connected.get_internal_wound_data(H)
var/list/occupant_data = list(
"stationtime" = worldtime2text(),
"stat" = displayed_stat,
"name" = H.name,
"species" = H.get_species(),
"brain_activity" = H.get_brain_status(),
"pulse" = text2num(pulse_result),
"blood_volume" = H.get_blood_volume(),
"blood_oxygenation" = H.get_blood_oxygenation(),
"blood_pressure" = H.get_blood_pressure(),
"blood_type" = H.dna.b_type,
"bruteloss" = get_severity(H.getBruteLoss(), TRUE),
"fireloss" = get_severity(H.getFireLoss(), TRUE),
"oxyloss" = get_severity(H.getOxyLoss(), TRUE),
"toxloss" = get_severity(H.getToxLoss(), TRUE),
"cloneloss" = get_severity(H.getCloneLoss(), TRUE),
"rads" = H.total_radiation,
"paralysis" = H.paralysis,
"bodytemp" = H.bodytemperature,
"borer_present" = H.has_brain_worms(),
"inaprovaline_amount" = REAGENT_VOLUME(R, /singleton/reagent/inaprovaline),
"dexalin_amount" = REAGENT_VOLUME(R, /singleton/reagent/dexalin),
"soporific_amount" = REAGENT_VOLUME(R, /singleton/reagent/soporific),
"bicaridine_amount" = REAGENT_VOLUME(R, /singleton/reagent/bicaridine),
"dermaline_amount" = REAGENT_VOLUME(R, /singleton/reagent/dermaline),
"thetamycin_amount" = REAGENT_VOLUME(R, /singleton/reagent/thetamycin),
"other_amount" = R.total_volume - (REAGENT_VOLUME(R, /singleton/reagent/inaprovaline) + REAGENT_VOLUME(R, /singleton/reagent/soporific) + REAGENT_VOLUME(R, /singleton/reagent/bicaridine) + REAGENT_VOLUME(R, /singleton/reagent/dexalin) + REAGENT_VOLUME(R, /singleton/reagent/dermaline) + REAGENT_VOLUME(R, /singleton/reagent/thetamycin)),
"bodyparts" = bodyparts,
"organs" = organs,
"has_internal_injuries" = connected.has_internal_injuries,
"has_external_injuries" = connected.has_external_injuries,
"missing_limbs" = connected.get_missing_limbs(H),
"missing_organs" = connected.get_missing_organs(H)
)
return occupant_data