From 6c21bc44662791fca255a51b5b92930538f0b322 Mon Sep 17 00:00:00 2001
From: Tename <37943518+Tename@users.noreply.github.com>
Date: Mon, 4 Jan 2021 12:09:48 -0800
Subject: [PATCH 1/6] bazinga
---
code/__HELPERS/roundend.dm | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/code/__HELPERS/roundend.dm b/code/__HELPERS/roundend.dm
index ab4d42c5..56e5daa6 100644
--- a/code/__HELPERS/roundend.dm
+++ b/code/__HELPERS/roundend.dm
@@ -383,7 +383,7 @@
for (var/i in GLOB.ai_list)
var/mob/living/silicon/ai/aiPlayer = i
if(aiPlayer.mind)
- parts += "[aiPlayer.name] (Played by: [aiPlayer.mind.key])'s laws [aiPlayer.stat != DEAD ? "at the end of the round" : "when it was deactivated"] were:"
+ parts += "[aiPlayer.name]'s laws [aiPlayer.stat != DEAD ? "at the end of the round" : "when it was deactivated"] were:"
parts += aiPlayer.laws.get_law_list(include_zeroth=TRUE)
parts += "Total law changes: [aiPlayer.law_change_counter]"
@@ -394,14 +394,14 @@
for(var/mob/living/silicon/robot/robo in aiPlayer.connected_robots)
borg_num--
if(robo.mind)
- robolist += "[robo.name] (Played by: [robo.mind.key])[robo.stat == DEAD ? " (Deactivated)" : ""][borg_num ?", ":""]
"
+ robolist += "[robo.name] [robo.stat == DEAD ? " (Deactivated)" : ""][borg_num ?", ":""]
"
parts += "[robolist]"
if(!borg_spacer)
borg_spacer = TRUE
for (var/mob/living/silicon/robot/robo in GLOB.silicon_mobs)
if (!robo.connected_ai && robo.mind)
- parts += "[borg_spacer?"
":""][robo.name] (Played by: [robo.mind.key]) [(robo.stat != DEAD)? "survived as an AI-less borg!" : "was unable to survive the rigors of being a cyborg without an AI."] Its laws were:"
+ parts += "[borg_spacer?"
":""][robo.name] [(robo.stat != DEAD)? "survived as an AI-less borg!" : "was unable to survive the rigors of being a cyborg without an AI."] Its laws were:"
if(robo) //How the hell do we lose robo between here and the world messages directly above this?
parts += robo.laws.get_law_list(include_zeroth=TRUE)
From 670fe6e600bae74d93bac2920edd65d11f23fe1a Mon Sep 17 00:00:00 2001
From: Tename <37943518+Tename@users.noreply.github.com>
Date: Mon, 4 Jan 2021 12:19:27 -0800
Subject: [PATCH 2/6] Oops
---
code/__HELPERS/roundend.dm | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/code/__HELPERS/roundend.dm b/code/__HELPERS/roundend.dm
index 56e5daa6..541b3d56 100644
--- a/code/__HELPERS/roundend.dm
+++ b/code/__HELPERS/roundend.dm
@@ -383,7 +383,7 @@
for (var/i in GLOB.ai_list)
var/mob/living/silicon/ai/aiPlayer = i
if(aiPlayer.mind)
- parts += "[aiPlayer.name]'s laws [aiPlayer.stat != DEAD ? "at the end of the round" : "when it was deactivated"] were:"
+ parts += "[aiPlayer.name]'s laws [aiPlayer.stat != DEAD ? "at the end of the round" : "when it was deactivated"] were:"
parts += aiPlayer.laws.get_law_list(include_zeroth=TRUE)
parts += "Total law changes: [aiPlayer.law_change_counter]"
From 7f2de64be7863d1f9eb14e1b5706107f34cfcc20 Mon Sep 17 00:00:00 2001
From: kevinz000 <2003111+kevinz000@users.noreply.github.com>
Date: Sun, 12 Jan 2020 00:45:21 -0800
Subject: [PATCH 3/6] RCD Update Sync
---
code/game/machinery/firealarm.dm | 14 ++++++++++++++
.../effects/temporary_visuals/miscellaneous.dm | 9 ++++++++-
code/game/objects/items/RCD.dm | 4 ++--
code/modules/research/designs/misc_designs.dm | 6 +++---
code/modules/research/techweb/all_nodes.dm | 2 +-
5 files changed, 28 insertions(+), 7 deletions(-)
diff --git a/code/game/machinery/firealarm.dm b/code/game/machinery/firealarm.dm
index 9f3a5904..1ef3fc0a 100644
--- a/code/game/machinery/firealarm.dm
+++ b/code/game/machinery/firealarm.dm
@@ -256,6 +256,20 @@
return
return ..()
+/obj/machinery/firealarm/rcd_vals(mob/user, obj/item/construction/rcd/the_rcd)
+ if((buildstage == 0) && (the_rcd.upgrade & RCD_UPGRADE_SIMPLE_CIRCUITS))
+ return list("mode" = RCD_UPGRADE_SIMPLE_CIRCUITS, "delay" = 20, "cost" = 1)
+ return FALSE
+
+/obj/machinery/firealarm/rcd_act(mob/user, obj/item/construction/rcd/the_rcd, passed_mode)
+ switch(passed_mode)
+ if(RCD_UPGRADE_SIMPLE_CIRCUITS)
+ user.visible_message("[user] fabricates a circuit and places it into [src].", \
+ "You adapt a fire alarm circuit and slot it into the assembly.")
+ buildstage = 1
+ update_icon()
+ return TRUE
+ return FALSE
/obj/machinery/firealarm/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1, attack_dir)
. = ..()
diff --git a/code/game/objects/effects/temporary_visuals/miscellaneous.dm b/code/game/objects/effects/temporary_visuals/miscellaneous.dm
index 296112a4..3be5e846 100644
--- a/code/game/objects/effects/temporary_visuals/miscellaneous.dm
+++ b/code/game/objects/effects/temporary_visuals/miscellaneous.dm
@@ -438,6 +438,13 @@
else
return INITIALIZE_HINT_QDEL
+/obj/effect/temp_visual/slugboom
+ icon = 'icons/effects/96x96.dmi'
+ icon_state = "slugboom"
+ randomdir = FALSE
+ duration = 30
+ pixel_x = -24
+
/obj/effect/constructing_effect
icon = 'icons/effects/effects_rcd.dmi'
icon_state = ""
@@ -477,4 +484,4 @@
addtimer(CALLBACK(src, .proc/end), 15)
/obj/effect/constructing_effect/proc/end()
- qdel(src)
\ No newline at end of file
+ qdel(src)
diff --git a/code/game/objects/items/RCD.dm b/code/game/objects/items/RCD.dm
index e3c1953e..3ec976cb 100644
--- a/code/game/objects/items/RCD.dm
+++ b/code/game/objects/items/RCD.dm
@@ -47,9 +47,9 @@ RLD
/obj/item/construction/examine(mob/user)
. = ..()
- . += "\A [src]. It currently holds [matter]/[max_matter] matter-units."
+ . += "It currently holds [matter]/[max_matter] matter-units."
if(upgrade & RCD_UPGRADE_FRAMES)
- . += "It contains the design for machine frames, computer frames and deconstruction."
+ . += "It contains the design for machine frames, computer frames and deconstruction."
if(upgrade & RCD_UPGRADE_SIMPLE_CIRCUITS)
. += "It contains the design for firelock, air alarm, fire alarm, apc circuits and crap power cells."
diff --git a/code/modules/research/designs/misc_designs.dm b/code/modules/research/designs/misc_designs.dm
index 706d209d..959d0ed5 100644
--- a/code/modules/research/designs/misc_designs.dm
+++ b/code/modules/research/designs/misc_designs.dm
@@ -488,15 +488,15 @@
desc = "A tool that can construct and deconstruct walls, airlocks and floors on the fly."
id = "rcd_loaded"
build_type = PROTOLATHE
- materials = list(MAT_METAL=48000, MAT_GLASS=32000) // costs more than what it did in the autolathe, this one comes loaded.
+ materials = list(MAT_METAL = MINERAL_MATERIAL_AMOUNT, MAT_GLASS = MINERAL_MATERIAL_AMOUNT) // costs more than what it did in the autolathe, this one comes loaded.
build_path = /obj/item/construction/rcd/loaded
category = list("Equipment")
departmental_flags = DEPARTMENTAL_FLAG_ENGINEERING
-/datum/design/pipe_dispenser
+/datum/design/rpd
name = "Rapid Pipe Dispenser (RPD)"
desc = "A tool that can construct and deconstruct pipes on the fly."
- id = "pipe_dispenser"
+ id = "rpd"
build_type = PROTOLATHE
materials = list(MAT_METAL = 75000, MAT_GLASS = 37500)
build_path = /obj/item/pipe_dispenser
diff --git a/code/modules/research/techweb/all_nodes.dm b/code/modules/research/techweb/all_nodes.dm
index 17b30565..a13739e3 100644
--- a/code/modules/research/techweb/all_nodes.dm
+++ b/code/modules/research/techweb/all_nodes.dm
@@ -201,7 +201,7 @@
display_name = "Advanced Engineering"
description = "Pushing the boundaries of physics, one chainsaw-fist at a time."
prereq_ids = list("engineering", "emp_basic")
- design_ids = list("engine_goggles", "magboots", "forcefield_projector", "weldingmask", "tray_goggles_prescription", "engine_goggles_prescription", "mesons_prescription", "double_emergency_oxygen","rcd_loaded", "pipe_dispenser","rcd_upgrade_frames", "rcd_upgrade_simple_circuits")
+ design_ids = list("engine_goggles", "magboots", "forcefield_projector", "weldingmask", "rcd_loaded", "rpd", "tray_goggles_prescription", "engine_goggles_prescription", "mesons_prescription","rcd_upgrade_frames", "rcd_upgrade_simple_circuits", "double_emergency_oxygen")
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 4000)
export_price = 5000
From 0fba3d15e11fef90cce21e38d84c7ad889e455ba Mon Sep 17 00:00:00 2001
From: Archie
Date: Mon, 4 Jan 2021 21:22:21 -0300
Subject: [PATCH 4/6] scannerfix
---
code/game/objects/items/devices/scanners.dm | 23 ++++++++++---------
.../chemistry/machinery/chem_dispenser.dm | 11 +++++++--
2 files changed, 21 insertions(+), 13 deletions(-)
diff --git a/code/game/objects/items/devices/scanners.dm b/code/game/objects/items/devices/scanners.dm
index 4fdcca94..9f5635ac 100644
--- a/code/game/objects/items/devices/scanners.dm
+++ b/code/game/objects/items/devices/scanners.dm
@@ -472,17 +472,18 @@ GENE SCANNER
msg += "Subject is not addicted to any reagents.\n"
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"
+ if(istype(F))
+ 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"
msg += "*---------*"
to_chat(user, msg)
diff --git a/code/modules/reagents/chemistry/machinery/chem_dispenser.dm b/code/modules/reagents/chemistry/machinery/chem_dispenser.dm
index ba791f83..3bd9fc5e 100644
--- a/code/modules/reagents/chemistry/machinery/chem_dispenser.dm
+++ b/code/modules/reagents/chemistry/machinery/chem_dispenser.dm
@@ -92,8 +92,15 @@
/obj/machinery/chem_dispenser/Initialize()
. = ..()
- for(var/list/L in list(dispensable_reagents, emagged_reagents, upgrade_reagents, upgrade_reagents2, upgrade_reagents3))
- L = sortList(L, /proc/cmp_reagents_asc)
+ if(emagged_reagents)
+ emagged_reagents = sortList(emagged_reagents, /proc/cmp_reagents_asc)
+ if(upgrade_reagents)
+ upgrade_reagents = sortList(upgrade_reagents, /proc/cmp_reagents_asc)
+ if(upgrade_reagents2)
+ upgrade_reagents2 = sortList(upgrade_reagents2, /proc/cmp_reagents_asc)
+ if(upgrade_reagents3)
+ upgrade_reagents3 = sortList(upgrade_reagents3, /proc/cmp_reagents_asc)
+ dispensable_reagents = sortList(dispensable_reagents, /proc/cmp_reagents_asc)
update_icon()
/obj/machinery/chem_dispenser/Destroy()
From 0b39a4986cddf40208bec99dfaaed9c6caaafc88 Mon Sep 17 00:00:00 2001
From: Putnam3145
Date: Sun, 12 Jan 2020 00:41:00 -0800
Subject: [PATCH 5/6] Merge pull request #10532 from Ghommie/Ghommie-cit503
A few reagents related fixes.
---
code/modules/reagents/reagent_containers/borghydro.dm | 9 ++++++---
code/modules/reagents/reagent_dispenser.dm | 2 +-
2 files changed, 7 insertions(+), 4 deletions(-)
diff --git a/code/modules/reagents/reagent_containers/borghydro.dm b/code/modules/reagents/reagent_containers/borghydro.dm
index 51937448..0178bf49 100644
--- a/code/modules/reagents/reagent_containers/borghydro.dm
+++ b/code/modules/reagents/reagent_containers/borghydro.dm
@@ -31,6 +31,7 @@ Borg Hypospray
var/accepts_reagent_upgrades = TRUE //If upgrades can increase number of reagents dispensed.
var/list/modes = list() //Basically the inverse of reagent_ids. Instead of having numbers as "keys" and strings as values it has strings as keys and numbers as values.
//Used as list for input() in shakers.
+ var/list/reagent_names = list()
/obj/item/reagent_containers/borghypo/Initialize()
@@ -57,7 +58,7 @@ Borg Hypospray
return 1
// Use this to add more chemicals for the borghypo to produce.
-/obj/item/reagent_containers/borghypo/proc/add_reagent(reagent)
+/obj/item/reagent_containers/borghypo/proc/add_reagent(datum/reagent/reagent)
reagent_ids |= reagent
var/datum/reagents/RG = new(30)
RG.my_atom = src
@@ -67,9 +68,11 @@ Borg Hypospray
R.add_reagent(reagent, 30)
modes[reagent] = modes.len + 1
+ reagent_names[initial(reagent.name)] = reagent
-/obj/item/reagent_containers/borghypo/proc/del_reagent(reagent)
+/obj/item/reagent_containers/borghypo/proc/del_reagent(datum/reagent/reagent)
reagent_ids -= reagent
+ reagent_names -= initial(reagent.name)
var/datum/reagents/RG
var/datum/reagents/TRG
for(var/i in 1 to reagent_ids.len)
@@ -115,7 +118,7 @@ Borg Hypospray
log_combat(user, M, "injected", src, "(CHEMICALS: [english_list(injected)])")
/obj/item/reagent_containers/borghypo/attack_self(mob/user)
- var/chosen_reagent = modes[input(user, "What reagent do you want to dispense?") as null|anything in reagent_ids]
+ var/chosen_reagent = modes[reagent_names[input(user, "What reagent do you want to dispense?") as null|anything in reagent_names]]
if(!chosen_reagent)
return
mode = chosen_reagent
diff --git a/code/modules/reagents/reagent_dispenser.dm b/code/modules/reagents/reagent_dispenser.dm
index 8ac4b354..2e7269c7 100644
--- a/code/modules/reagents/reagent_dispenser.dm
+++ b/code/modules/reagents/reagent_dispenser.dm
@@ -184,7 +184,7 @@
icon_state = "virus_food"
anchored = TRUE
density = FALSE
- reagent_id = /datum/reagent/toxin/mutagen/mutagenvirusfood
+ reagent_id = /datum/reagent/consumable/virus_food
/obj/structure/reagent_dispensers/cooking_oil
name = "vat of cooking oil"
From dc7c1aca912c0b10da954664c67cf7be592f0daf Mon Sep 17 00:00:00 2001
From: Ghom <42542238+Ghommie@users.noreply.github.com>
Date: Tue, 14 Jan 2020 13:52:10 +0100
Subject: [PATCH 6/6] Merge pull request #10538 from Putnam3145/chemistry-aaa
Fixes to chemistry
---
code/game/objects/items/devices/scanners.dm | 1686 ++++++++---------
.../chemistry/reagents/other_reagents.dm | 2 +-
code/modules/reagents/reagent_containers.dm | 442 ++---
3 files changed, 1065 insertions(+), 1065 deletions(-)
diff --git a/code/game/objects/items/devices/scanners.dm b/code/game/objects/items/devices/scanners.dm
index 9f5635ac..5c60a9ac 100644
--- a/code/game/objects/items/devices/scanners.dm
+++ b/code/game/objects/items/devices/scanners.dm
@@ -1,843 +1,843 @@
-
-/*
-
-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(M.reagents.has_reagent(/datum/reagent/fermi/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"
-
- var/datum/reagent/impure/fermiTox/F = M.reagents.has_reagent(/datum/reagent/impure/fermiTox)
- if(istype(F))
- 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"
-
- msg += "*---------*"
- to_chat(user, msg)
-
-/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(M.reagents.has_reagent(/datum/reagent/fermi/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"
+
+ var/datum/reagent/impure/fermiTox/F = M.reagents.has_reagent(/datum/reagent/impure/fermiTox)
+ if(istype(F,/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"
+
+ msg += "*---------*"
+ to_chat(user, msg)
+
+/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/modules/reagents/chemistry/reagents/other_reagents.dm b/code/modules/reagents/chemistry/reagents/other_reagents.dm
index 1262d4e7..33cf85c4 100644
--- a/code/modules/reagents/chemistry/reagents/other_reagents.dm
+++ b/code/modules/reagents/chemistry/reagents/other_reagents.dm
@@ -60,7 +60,7 @@
B = new(T)
if(data["blood_DNA"])
B.blood_DNA[data["blood_DNA"]] = data["blood_type"]
- if(!B.reagents)
+ if(B.reagents)
B.reagents.add_reagent(type, reac_volume)
B.update_icon()
diff --git a/code/modules/reagents/reagent_containers.dm b/code/modules/reagents/reagent_containers.dm
index 31c80248..6d93a5e1 100644
--- a/code/modules/reagents/reagent_containers.dm
+++ b/code/modules/reagents/reagent_containers.dm
@@ -1,221 +1,221 @@
-#define PH_WEAK (1 << 0)
-#define TEMP_WEAK (1 << 1)
-
-/obj/item/reagent_containers
- name = "Container"
- desc = "..."
- icon = 'icons/obj/chemical.dmi'
- icon_state = null
- w_class = WEIGHT_CLASS_TINY
- var/amount_per_transfer_from_this = 5
- var/list/possible_transfer_amounts = list(5,10,15,20,25,30)
- var/volume = 30
- var/reagent_flags
- var/list/list_reagents = null
- var/spawned_disease = null
- var/disease_amount = 20
- var/spillable = FALSE
- var/beaker_weakness_bitflag = NONE//Bitflag!
- var/container_HP = 2
- var/cached_icon
- var/splashable = FALSE
-
-/obj/item/reagent_containers/Initialize(mapload, vol)
- . = ..()
- if(isnum(vol) && vol > 0)
- volume = vol
- create_reagents(volume, reagent_flags)
- if(spawned_disease)
- var/datum/disease/F = new spawned_disease()
- var/list/data = list("blood_DNA" = "UNKNOWN DNA", "blood_type" = "SY","viruses"= list(F))
- reagents.add_reagent(/datum/reagent/blood, disease_amount, data)
- add_initial_reagents()
-
-/obj/item/reagent_containers/proc/add_initial_reagents()
- if(list_reagents)
- reagents.add_reagent_list(list_reagents)
-
-/obj/item/reagent_containers/attack_self(mob/user)
- if(possible_transfer_amounts.len)
- var/i=0
- for(var/A in possible_transfer_amounts)
- i++
- if(A == amount_per_transfer_from_this)
- if(i[src]'s transfer amount is now [amount_per_transfer_from_this] units.")
- return
-
-/obj/item/reagent_containers/attack(mob/M, mob/user, def_zone)
- if(user.a_intent == INTENT_HARM)
- return ..()
-
-/obj/item/reagent_containers/proc/canconsume(mob/eater, mob/user)
- if(!iscarbon(eater))
- return 0
- var/mob/living/carbon/C = eater
- var/covered = ""
- if(C.is_mouth_covered(head_only = 1))
- covered = "headgear"
- else if(C.is_mouth_covered(mask_only = 1))
- covered = "mask"
- if(covered)
- var/who = (isnull(user) || eater == user) ? "your" : "[eater.p_their()]"
- to_chat(user, "You have to remove [who] [covered] first!")
- return 0
- return 1
-
-/obj/item/reagent_containers/ex_act()
- if(reagents)
- for(var/datum/reagent/R in reagents.reagent_list)
- R.on_ex_act()
- if(!QDELETED(src))
- ..()
-
-/obj/item/reagent_containers/fire_act(exposed_temperature, exposed_volume)
- reagents.expose_temperature(exposed_temperature)
- ..()
-
-/obj/item/reagent_containers/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
- . = ..()
- SplashReagents(hit_atom, TRUE)
-
-/obj/item/reagent_containers/proc/bartender_check(atom/target)
- . = FALSE
- var/turf/T = get_turf(src)
- if(!T || !thrownby || !thrownby.actions)
- return
- for(var/datum/action/innate/D in thrownby.actions)
- if(D.active && istype(D, /datum/action/innate/drink_fling))
- return TRUE
-
-
-/obj/item/reagent_containers/proc/ForceResetRotation()
- transform = initial(transform)
-
-/obj/item/reagent_containers/proc/SplashReagents(atom/target, thrown = FALSE)
- if(!reagents || !reagents.total_volume || !(splashable ? splashable : spillable))
- return
-
- if(ismob(target) && target.reagents)
- if(thrown)
- reagents.total_volume *= rand(5,10) * 0.1 //Not all of it makes contact with the target
- var/mob/M = target
- var/R
- target.visible_message("[M] has been splashed with something!", \
- "[M] has been splashed with something!")
- for(var/datum/reagent/A in reagents.reagent_list)
- R += "[A.type] ([num2text(A.volume)]), "
-
- if(thrownby)
- log_combat(thrownby, M, "splashed", R)
- reagents.reaction(target, TOUCH)
-
- else if(bartender_check(target) && thrown)
- if(!istype(src, /obj/item/reagent_containers/food/drinks)) //drinks smash against solid objects
- visible_message("[src] lands upright without spilling a single drop!")
- transform = initial(transform)
- return
-
- else
- if(isturf(target) && reagents.reagent_list.len && thrownby)
- log_combat(thrownby, target, "splashed (thrown) [english_list(reagents.reagent_list)]", "in [AREACOORD(target)]")
- log_game("[key_name(thrownby)] splashed (thrown) [english_list(reagents.reagent_list)] on [target] in [AREACOORD(target)].")
- message_admins("[ADMIN_LOOKUPFLW(thrownby)] splashed (thrown) [english_list(reagents.reagent_list)] on [target] in [ADMIN_VERBOSEJMP(target)].")
- visible_message("[src] spills its contents all over [target].")
- reagents.reaction(target, TOUCH)
- if(QDELETED(src))
- return
-
- reagents.clear_reagents()
-
-//melts plastic beakers
-/obj/item/reagent_containers/microwave_act(obj/machinery/microwave/M)
- reagents.expose_temperature(1000)
- if(beaker_weakness_bitflag & TEMP_WEAK)
- var/list/seen = viewers(5, get_turf(src))
- var/iconhtml = icon2html(src, seen)
- for(var/mob/H in seen)
- to_chat(H, "[iconhtml] \The [src]'s melts from the temperature!")
- playsound(get_turf(src), 'sound/FermiChem/heatmelt.ogg', 80, 1)
- qdel(src)
- ..()
-
-//melts plastic beakers
-/obj/item/reagent_containers/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume)
- reagents.expose_temperature(exposed_temperature)
- temp_check()
-
-/obj/item/reagent_containers/proc/temp_check()
- if(beaker_weakness_bitflag & TEMP_WEAK)
- if(reagents.chem_temp >= 444)//assuming polypropylene
- START_PROCESSING(SSobj, src)
-
-//melts glass beakers
-/obj/item/reagent_containers/proc/pH_check()
- if(beaker_weakness_bitflag & PH_WEAK)
- if((reagents.pH < 1.5) || (reagents.pH > 12.5))
- START_PROCESSING(SSobj, src)
-
-
-/obj/item/reagent_containers/process()
- if(!cached_icon)
- cached_icon = icon_state
- var/damage
- var/cause
- if(beaker_weakness_bitflag & PH_WEAK)
- if(reagents.pH < 2)
- damage = (2 - reagents.pH)/20
- cause = "from the extreme pH"
- playsound(get_turf(src), 'sound/FermiChem/bufferadd.ogg', 50, 1)
-
- if(reagents.pH > 12)
- damage = (reagents.pH - 12)/20
- cause = "from the extreme pH"
- playsound(get_turf(src), 'sound/FermiChem/bufferadd.ogg', 50, 1)
-
- if(beaker_weakness_bitflag & TEMP_WEAK)
- if(reagents.chem_temp >= 444)
- if(damage)
- damage += (reagents.chem_temp/444)/5
- else
- damage = (reagents.chem_temp/444)/5
- if(cause)
- cause += " and "
- cause += "from the high temperature"
- playsound(get_turf(src), 'sound/FermiChem/heatdam.ogg', 50, 1)
-
- if(!damage || damage <= 0)
- STOP_PROCESSING(SSobj, src)
-
- container_HP -= damage
-
- var/list/seen = viewers(5, get_turf(src))
- var/iconhtml = icon2html(src, seen)
-
- var/damage_percent = ((container_HP / initial(container_HP)*100))
- switch(damage_percent)
- if(-INFINITY to 0)
- for(var/mob/M in seen)
- to_chat(M, "[iconhtml] \The [src]'s melts [cause]!")
- playsound(get_turf(src), 'sound/FermiChem/acidmelt.ogg', 80, 1)
- SSblackbox.record_feedback("tally", "fermi_chem", 1, "Times beakers have melted")
- STOP_PROCESSING(SSobj, src)
- qdel(src)
- return
- if(0 to 35)
- icon_state = "[cached_icon]_m3"
- desc = "[initial(desc)] It is severely deformed."
- if(35 to 70)
- icon_state = "[cached_icon]_m2"
- desc = "[initial(desc)] It is deformed."
- if(70 to 85)
- desc = "[initial(desc)] It is mildly deformed."
- icon_state = "[cached_icon]_m1"
-
- update_icon()
- if(prob(25))
- for(var/mob/M in seen)
- to_chat(M, "[iconhtml] \The [src]'s is damaged by [cause] and begins to deform!")
+#define PH_WEAK (1 << 0)
+#define TEMP_WEAK (1 << 1)
+
+/obj/item/reagent_containers
+ name = "Container"
+ desc = "..."
+ icon = 'icons/obj/chemical.dmi'
+ icon_state = null
+ w_class = WEIGHT_CLASS_TINY
+ var/amount_per_transfer_from_this = 5
+ var/list/possible_transfer_amounts = list(5,10,15,20,25,30)
+ var/volume = 30
+ var/reagent_flags
+ var/list/list_reagents = null
+ var/spawned_disease = null
+ var/disease_amount = 20
+ var/spillable = FALSE
+ var/beaker_weakness_bitflag = NONE//Bitflag!
+ var/container_HP = 2
+ var/cached_icon
+ var/splashable = FALSE
+
+/obj/item/reagent_containers/Initialize(mapload, vol)
+ . = ..()
+ if(isnum(vol) && vol > 0)
+ volume = vol
+ create_reagents(volume, reagent_flags)
+ if(spawned_disease)
+ var/datum/disease/F = new spawned_disease()
+ var/list/data = list("blood_DNA" = "UNKNOWN DNA", "blood_type" = "SY","viruses"= list(F))
+ reagents.add_reagent(/datum/reagent/blood, disease_amount, data)
+ add_initial_reagents()
+
+/obj/item/reagent_containers/proc/add_initial_reagents()
+ if(list_reagents)
+ reagents.add_reagent_list(list_reagents)
+
+/obj/item/reagent_containers/attack_self(mob/user)
+ if(possible_transfer_amounts.len)
+ var/i=0
+ for(var/A in possible_transfer_amounts)
+ i++
+ if(A == amount_per_transfer_from_this)
+ if(i[src]'s transfer amount is now [amount_per_transfer_from_this] units.")
+ return
+
+/obj/item/reagent_containers/attack(mob/M, mob/user, def_zone)
+ if(user.a_intent == INTENT_HARM)
+ return ..()
+
+/obj/item/reagent_containers/proc/canconsume(mob/eater, mob/user)
+ if(!iscarbon(eater))
+ return 0
+ var/mob/living/carbon/C = eater
+ var/covered = ""
+ if(C.is_mouth_covered(head_only = 1))
+ covered = "headgear"
+ else if(C.is_mouth_covered(mask_only = 1))
+ covered = "mask"
+ if(covered)
+ var/who = (isnull(user) || eater == user) ? "your" : "[eater.p_their()]"
+ to_chat(user, "You have to remove [who] [covered] first!")
+ return 0
+ return 1
+
+/obj/item/reagent_containers/ex_act()
+ if(reagents)
+ for(var/datum/reagent/R in reagents.reagent_list)
+ R.on_ex_act()
+ if(!QDELETED(src))
+ ..()
+
+/obj/item/reagent_containers/fire_act(exposed_temperature, exposed_volume)
+ reagents.expose_temperature(exposed_temperature)
+ ..()
+
+/obj/item/reagent_containers/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
+ . = ..()
+ SplashReagents(hit_atom, TRUE)
+
+/obj/item/reagent_containers/proc/bartender_check(atom/target)
+ . = FALSE
+ var/turf/T = get_turf(src)
+ if(!T || !thrownby || !thrownby.actions)
+ return
+ for(var/datum/action/innate/D in thrownby.actions)
+ if(D.active && istype(D, /datum/action/innate/drink_fling))
+ return TRUE
+
+
+/obj/item/reagent_containers/proc/ForceResetRotation()
+ transform = initial(transform)
+
+/obj/item/reagent_containers/proc/SplashReagents(atom/target, thrown = FALSE)
+ if(!reagents || !reagents.total_volume || !(splashable ? splashable : spillable))
+ return
+
+ if(ismob(target) && target.reagents)
+ if(thrown)
+ reagents.total_volume *= rand(5,10) * 0.1 //Not all of it makes contact with the target
+ var/mob/M = target
+ var/R
+ target.visible_message("[M] has been splashed with something!", \
+ "[M] has been splashed with something!")
+ for(var/datum/reagent/A in reagents.reagent_list)
+ R += "[A.type] ([A.volume]),"
+
+ if(thrownby)
+ log_combat(thrownby, M, "splashed", R)
+ reagents.reaction(target, TOUCH)
+
+ else if(bartender_check(target) && thrown)
+ if(!istype(src, /obj/item/reagent_containers/food/drinks)) //drinks smash against solid objects
+ visible_message("[src] lands upright without spilling a single drop!")
+ transform = initial(transform)
+ return
+
+ else
+ if(isturf(target) && reagents.reagent_list.len && thrownby)
+ log_combat(thrownby, target, "splashed (thrown) [english_list(reagents.reagent_list)]", "in [AREACOORD(target)]")
+ log_game("[key_name(thrownby)] splashed (thrown) [english_list(reagents.reagent_list)] on [target] in [AREACOORD(target)].")
+ message_admins("[ADMIN_LOOKUPFLW(thrownby)] splashed (thrown) [english_list(reagents.reagent_list)] on [target] in [ADMIN_VERBOSEJMP(target)].")
+ visible_message("[src] spills its contents all over [target].")
+ reagents.reaction(target, TOUCH)
+ if(QDELETED(src))
+ return
+
+ reagents.clear_reagents()
+
+//melts plastic beakers
+/obj/item/reagent_containers/microwave_act(obj/machinery/microwave/M)
+ reagents.expose_temperature(1000)
+ if(beaker_weakness_bitflag & TEMP_WEAK)
+ var/list/seen = viewers(5, get_turf(src))
+ var/iconhtml = icon2html(src, seen)
+ for(var/mob/H in seen)
+ to_chat(H, "[iconhtml] \The [src]'s melts from the temperature!")
+ playsound(get_turf(src), 'sound/FermiChem/heatmelt.ogg', 80, 1)
+ qdel(src)
+ ..()
+
+//melts plastic beakers
+/obj/item/reagent_containers/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume)
+ reagents.expose_temperature(exposed_temperature)
+ temp_check()
+
+/obj/item/reagent_containers/proc/temp_check()
+ if(beaker_weakness_bitflag & TEMP_WEAK)
+ if(reagents.chem_temp >= 444)//assuming polypropylene
+ START_PROCESSING(SSobj, src)
+
+//melts glass beakers
+/obj/item/reagent_containers/proc/pH_check()
+ if(beaker_weakness_bitflag & PH_WEAK)
+ if((reagents.pH < 1.5) || (reagents.pH > 12.5))
+ START_PROCESSING(SSobj, src)
+
+
+/obj/item/reagent_containers/process()
+ if(!cached_icon)
+ cached_icon = icon_state
+ var/damage
+ var/cause
+ if(beaker_weakness_bitflag & PH_WEAK)
+ if(reagents.pH < 2)
+ damage = (2 - reagents.pH)/20
+ cause = "from the extreme pH"
+ playsound(get_turf(src), 'sound/FermiChem/bufferadd.ogg', 50, 1)
+
+ if(reagents.pH > 12)
+ damage = (reagents.pH - 12)/20
+ cause = "from the extreme pH"
+ playsound(get_turf(src), 'sound/FermiChem/bufferadd.ogg', 50, 1)
+
+ if(beaker_weakness_bitflag & TEMP_WEAK)
+ if(reagents.chem_temp >= 444)
+ if(damage)
+ damage += (reagents.chem_temp/444)/5
+ else
+ damage = (reagents.chem_temp/444)/5
+ if(cause)
+ cause += " and "
+ cause += "from the high temperature"
+ playsound(get_turf(src), 'sound/FermiChem/heatdam.ogg', 50, 1)
+
+ if(!damage || damage <= 0)
+ STOP_PROCESSING(SSobj, src)
+
+ container_HP -= damage
+
+ var/list/seen = viewers(5, get_turf(src))
+ var/iconhtml = icon2html(src, seen)
+
+ var/damage_percent = ((container_HP / initial(container_HP)*100))
+ switch(damage_percent)
+ if(-INFINITY to 0)
+ for(var/mob/M in seen)
+ to_chat(M, "[iconhtml] \The [src]'s melts [cause]!")
+ playsound(get_turf(src), 'sound/FermiChem/acidmelt.ogg', 80, 1)
+ SSblackbox.record_feedback("tally", "fermi_chem", 1, "Times beakers have melted")
+ STOP_PROCESSING(SSobj, src)
+ qdel(src)
+ return
+ if(0 to 35)
+ icon_state = "[cached_icon]_m3"
+ desc = "[initial(desc)] It is severely deformed."
+ if(35 to 70)
+ icon_state = "[cached_icon]_m2"
+ desc = "[initial(desc)] It is deformed."
+ if(70 to 85)
+ desc = "[initial(desc)] It is mildly deformed."
+ icon_state = "[cached_icon]_m1"
+
+ update_icon()
+ if(prob(25))
+ for(var/mob/M in seen)
+ to_chat(M, "[iconhtml] \The [src]'s is damaged by [cause] and begins to deform!")