diff --git a/code/_onclick/observer_onclick.dm b/code/_onclick/observer_onclick.dm
index 1b3955b5de9..71cf3b5037a 100644
--- a/code/_onclick/observer_onclick.dm
+++ b/code/_onclick/observer_onclick.dm
@@ -83,7 +83,7 @@
if(!istype(user)) // Make sure user is actually an observer. Revenents also use attack_ghost, but do not have the health_scan var.
return FALSE
if(user.client)
- if(user.gas_scan && atmos_scan(user=user, target=src, silent=TRUE))
+ if(user.gas_scan && src.return_analyzable_air() && atmos_scan(user=user, target=src, silent=TRUE))
return TRUE
// health + machine analyzer for ghosts
diff --git a/code/game/objects/items/devices/scanners.dm b/code/game/objects/items/devices/scanners.dm
index 395cca3a964..37372543ccb 100644
--- a/code/game/objects/items/devices/scanners.dm
+++ b/code/game/objects/items/devices/scanners.dm
@@ -606,10 +606,13 @@ SLIME SCANNER
var/cooldown = FALSE
var/cooldown_time = 250
var/accuracy // 0 is the best accuracy.
+ /// FALSE: Sum gas mixes then present. TRUE: Present each mix individually
+ var/show_detailed = FALSE
/obj/item/analyzer/examine(mob/user)
. = ..()
. += "Alt-click [src] to activate the barometer function."
+ . += "Alt-Shift-click [src] to toggle detailed reporting on or off."
/obj/item/analyzer/attack_self__legacy__attackchain(mob/user as mob)
@@ -620,9 +623,13 @@ SLIME SCANNER
if(!isturf(location))
return
- atmos_scan(user, location)
+ atmos_scan(user = user, target = location, detailed = show_detailed)
add_fingerprint(user)
+/obj/item/analyzer/AltShiftClick(mob/user)
+ show_detailed = !show_detailed
+ to_chat(user, "You toggle detailed reporting [show_detailed ? "on" : "off"]")
+
/obj/item/analyzer/AltClick(mob/user) //Barometer output for measuring when the next storm happens
..()
@@ -691,25 +698,30 @@ SLIME SCANNER
if(!can_see(user, target, 1))
return
if(target.return_analyzable_air())
- atmos_scan(user, target)
+ atmos_scan(user, target, detailed = show_detailed)
else
- atmos_scan(user, get_turf(target))
+ atmos_scan(user, get_turf(target), detailed = show_detailed)
/**
* Outputs a message to the user describing the target's gasmixes.
* Used in chat-based gas scans.
*/
-/proc/atmos_scan(mob/user, atom/target, silent = FALSE, print = TRUE, milla_turf_details = FALSE)
- var/datum/gas_mixture/air
+/proc/atmos_scan(mob/user, atom/target, silent = FALSE, print = TRUE, milla_turf_details = FALSE, detailed = FALSE)
+ var/datum/gas_mixture/gasmix
+ var/list/airs
var/list/milla = null
if(milla_turf_details && istype(target, /turf))
milla = new/list(MILLA_TILE_SIZE)
get_tile_atmos(target, milla)
- air = new()
- air.copy_from_milla(milla)
+ gasmix = new()
+ gasmix.copy_from_milla(milla)
+ airs += gasmix
else
- air = target.return_analyzable_air()
- if(!air)
+ gasmix = target.return_analyzable_air()
+ if(!istype(gasmix, /list))
+ gasmix = list(gasmix)
+ airs += gasmix
+ if(!gasmix)
return FALSE
var/list/message = list()
@@ -719,35 +731,87 @@ SLIME SCANNER
if(!print)
return TRUE
+ var/total_moles = 0
+ var/pressure = 0
+ var/volume = 0
+ var/heat_capacity = 0
+ var/thermal_energy = 0
+ var/oxygen = 0
+ var/nitrogen = 0
+ var/toxins
+ var/carbon_dioxide = 0
+ var/sleeping_agent = 0
+ var/agent_b = 0
- var/total_moles = air.total_moles()
- var/pressure = air.return_pressure()
- var/volume = air.return_volume() //could just do mixture.volume... but safety, I guess?
- var/heat_capacity = air.heat_capacity()
- var/thermal_energy = air.thermal_energy()
+ if(detailed)// Present all mixtures one by one
+ for(var/datum/gas_mixture/air as anything in airs)
+ total_moles = air.total_moles()
+ pressure = air.return_pressure()
+ volume = air.return_volume() //could just do mixture.volume... but safety, I guess?
+ heat_capacity = air.heat_capacity()
+ thermal_energy = air.thermal_energy()
- if(total_moles)
- message += "Total: [round(total_moles, 0.01)] moles"
- if(air.oxygen() && (milla_turf_details || air.oxygen() / total_moles > 0.01))
- message += " Oxygen: [round(air.oxygen(), 0.01)] moles ([round(air.oxygen() / total_moles * 100, 0.01)] %)"
- if(air.nitrogen() && (milla_turf_details || air.nitrogen() / total_moles > 0.01))
- message += " Nitrogen: [round(air.nitrogen(), 0.01)] moles ([round(air.nitrogen() / total_moles * 100, 0.01)] %)"
- if(air.carbon_dioxide() && (milla_turf_details || air.carbon_dioxide() / total_moles > 0.01))
- message += " Carbon Dioxide: [round(air.carbon_dioxide(), 0.01)] moles ([round(air.carbon_dioxide() / total_moles * 100, 0.01)] %)"
- if(air.toxins() && (milla_turf_details || air.toxins() / total_moles > 0.01))
- message += " Plasma: [round(air.toxins(), 0.01)] moles ([round(air.toxins() / total_moles * 100, 0.01)] %)"
- if(air.sleeping_agent() && (milla_turf_details || air.sleeping_agent() / total_moles > 0.01))
- message += " Nitrous Oxide: [round(air.sleeping_agent(), 0.01)] moles ([round(air.sleeping_agent() / total_moles * 100, 0.01)] %)"
- if(air.agent_b() && (milla_turf_details || air.agent_b() / total_moles > 0.01))
- message += " Agent B: [round(air.agent_b(), 0.01)] moles ([round(air.agent_b() / total_moles * 100, 0.01)] %)"
- message += "Temperature: [round(air.temperature()-T0C)] °C ([round(air.temperature())] K)"
- message += "Volume: [round(volume)] Liters"
- message += "Pressure: [round(pressure, 0.1)] kPa"
- message += "Heat Capacity: [DisplayJoules(heat_capacity)] / K"
- message += "Thermal Energy: [DisplayJoules(thermal_energy)]"
- else
- message += "[target] is empty!"
- message += "Volume: [round(volume)] Liters" // don't want to change the order volume appears in, suck it
+ if(total_moles)
+ message += "Total: [round(total_moles, 0.01)] moles"
+ if(air.oxygen() && (milla_turf_details || air.oxygen() / total_moles > 0.01))
+ message += " Oxygen: [round(air.oxygen(), 0.01)] moles ([round(air.oxygen() / total_moles * 100, 0.01)] %)"
+ if(air.nitrogen() && (milla_turf_details || air.nitrogen() / total_moles > 0.01))
+ message += " Nitrogen: [round(air.nitrogen(), 0.01)] moles ([round(air.nitrogen() / total_moles * 100, 0.01)] %)"
+ if(air.carbon_dioxide() && (milla_turf_details || air.carbon_dioxide() / total_moles > 0.01))
+ message += " Carbon Dioxide: [round(air.carbon_dioxide(), 0.01)] moles ([round(air.carbon_dioxide() / total_moles * 100, 0.01)] %)"
+ if(air.toxins() && (milla_turf_details || air.toxins() / total_moles > 0.01))
+ message += " Plasma: [round(air.toxins(), 0.01)] moles ([round(air.toxins() / total_moles * 100, 0.01)] %)"
+ if(air.sleeping_agent() && (milla_turf_details || air.sleeping_agent() / total_moles > 0.01))
+ message += " Nitrous Oxide: [round(air.sleeping_agent(), 0.01)] moles ([round(air.sleeping_agent() / total_moles * 100, 0.01)] %)"
+ if(air.agent_b() && (milla_turf_details || air.agent_b() / total_moles > 0.01))
+ message += " Agent B: [round(air.agent_b(), 0.01)] moles ([round(air.agent_b() / total_moles * 100, 0.01)] %)"
+ message += "Temperature: [round(air.temperature()-T0C)] °C ([round(air.temperature())] K)"
+ message += "Volume: [round(volume)] Liters"
+ message += "Pressure: [round(pressure, 0.1)] kPa"
+ message += "Heat Capacity: [DisplayJoules(heat_capacity)] / K"
+ message += "Thermal Energy: [DisplayJoules(thermal_energy)]"
+ else
+ message += "[target] is empty!"
+ message += "Volume: [round(volume)] Liters" // don't want to change the order volume appears in, suck it
+
+ else// Sum mixtures then present
+ for(var/datum/gas_mixture/air as anything in airs)
+ total_moles += air.total_moles()
+ pressure += air.return_pressure()
+ volume += air.return_volume()
+ heat_capacity += air.heat_capacity()
+ thermal_energy += air.thermal_energy()
+ oxygen += air.oxygen()
+ nitrogen += air.nitrogen()
+ toxins += air.toxins()
+ carbon_dioxide += air.carbon_dioxide()
+ sleeping_agent += air.sleeping_agent()
+ agent_b += air.agent_b()
+
+ var/temperature = heat_capacity ? thermal_energy / heat_capacity : 0
+
+ if(total_moles)
+ message += "Total: [round(total_moles, 0.01)] moles"
+ if(oxygen && (milla_turf_details || oxygen / total_moles > 0.01))
+ message += " Oxygen: [round(oxygen, 0.01)] moles ([round(oxygen / total_moles * 100, 0.01)] %)"
+ if(nitrogen && (milla_turf_details || nitrogen / total_moles > 0.01))
+ message += " Nitrogen: [round(nitrogen, 0.01)] moles ([round(nitrogen / total_moles * 100, 0.01)] %)"
+ if(carbon_dioxide && (milla_turf_details || carbon_dioxide / total_moles > 0.01))
+ message += " Carbon Dioxide: [round(carbon_dioxide, 0.01)] moles ([round(carbon_dioxide / total_moles * 100, 0.01)] %)"
+ if(toxins && (milla_turf_details || toxins / total_moles > 0.01))
+ message += " Plasma: [round(toxins, 0.01)] moles ([round(toxins / total_moles * 100, 0.01)] %)"
+ if(sleeping_agent && (milla_turf_details || sleeping_agent / total_moles > 0.01))
+ message += " Nitrous Oxide: [round(sleeping_agent, 0.01)] moles ([round(sleeping_agent / total_moles * 100, 0.01)] %)"
+ if(agent_b && (milla_turf_details || agent_b / total_moles > 0.01))
+ message += " Agent B: [round(agent_b, 0.01)] moles ([round(agent_b / total_moles * 100, 0.01)] %)"
+ message += "Temperature: [round(temperature-T0C)] °C ([round(temperature)] K)"
+ message += "Volume: [round(volume)] Liters"
+ message += "Pressure: [round(pressure, 0.1)] kPa"
+ message += "Heat Capacity: [DisplayJoules(heat_capacity)] / K"
+ message += "Thermal Energy: [DisplayJoules(thermal_energy)]"
+ else
+ message += "[target] is empty!"
+ message += "Volume: [round(volume)] Liters" // don't want to change the order volume appears in, suck it
if(milla)
// Values from milla/src/lib.rs, +1 due to array indexing difference.
diff --git a/code/modules/atmospherics/machinery/pipes/pipe.dm b/code/modules/atmospherics/machinery/pipes/pipe.dm
index 9fcf304fa17..ef39962d7fc 100644
--- a/code/modules/atmospherics/machinery/pipes/pipe.dm
+++ b/code/modules/atmospherics/machinery/pipes/pipe.dm
@@ -64,7 +64,7 @@
/obj/machinery/atmospherics/pipe/return_analyzable_air()
if(!parent)
return null
- return parent.air
+ return list(parent.air) + parent.other_airs
/obj/machinery/atmospherics/pipe/build_network(remove_deferral = FALSE)
if(!parent)