Skip to content

Commit

Permalink
feat: Gas Analyzer TGUI (TG port) (#2333)
Browse files Browse the repository at this point in the history
  • Loading branch information
larentoun authored Mar 22, 2023
1 parent a8b7c60 commit cd167b3
Show file tree
Hide file tree
Showing 21 changed files with 558 additions and 267 deletions.
8 changes: 2 additions & 6 deletions code/ATMOSPHERICS/components/unary_devices/tank.dm
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,8 @@
return
add_underlay(T, node, dir)

/obj/machinery/atmospherics/unary/tank/attackby(obj/item/W, mob/user, params)
if(istype(W, /obj/item/analyzer))
atmosanalyzer_scan(air_contents, user)
return

return ..()
/obj/machinery/atmospherics/unary/tank/return_analyzable_air()
return air_contents

/obj/machinery/atmospherics/unary/tank/air
name = "Pressure Tank (Air)"
Expand Down
11 changes: 5 additions & 6 deletions code/ATMOSPHERICS/pipes/pipe.dm
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,6 @@
/obj/machinery/atmospherics/pipe/returnPipenet(obj/machinery/atmospherics/A)
return parent

/obj/machinery/atmospherics/pipe/attackby(obj/item/W, mob/user, params)
if(istype(W, /obj/item/analyzer))
atmosanalyzer_scan(parent.air, user)
return
return ..()

/obj/machinery/atmospherics/proc/pipeline_expansion()
return null

Expand All @@ -66,6 +60,11 @@
return 0
return parent.air

/obj/machinery/atmospherics/pipe/return_analyzable_air()
if(!parent)
return 0
return parent.air

/obj/machinery/atmospherics/pipe/build_network(remove_deferral = FALSE)
if(!parent)
parent = new /datum/pipeline()
Expand Down
3 changes: 3 additions & 0 deletions code/LINDA/LINDA_turf_tile.dm
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@

return GM

/turf/return_analyzable_air()
return return_air()

/turf/remove_air(amount)
var/datum/gas_mixture/GM = new

Expand Down
18 changes: 12 additions & 6 deletions code/__DEFINES/atmospherics.dm
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,18 @@

//ATMOS
//stuff you should probably leave well alone!
#define R_IDEAL_GAS_EQUATION 8.31 //kPa*L/(K*mol)
#define ONE_ATMOSPHERE 101.325 //kPa
#define TCMB 2.7 // -270.3degC
#define TCRYO 265 // -48.15degC
#define T0C 273.15 // 0degC
#define T20C 293.15 // 20degC
/// kPa*L/(K*mol)
#define R_IDEAL_GAS_EQUATION 8.31
/// kPa
#define ONE_ATMOSPHERE 101.325
/// -270.3degC
#define TCMB 2.7
/// -48.15degC
#define TCRYO 265
/// 0degC
#define T0C 273.15
/// 20degC
#define T20C 293.15

#define MOLES_CELLSTANDARD (ONE_ATMOSPHERE*CELL_VOLUME/(T20C*R_IDEAL_GAS_EQUATION)) //moles in a 2.5 m^3 cell at 101.325 Pa and 20 degC
#define M_CELL_WITH_RATIO (MOLES_CELLSTANDARD * 0.005) //compared against for superconductivity
Expand Down
51 changes: 51 additions & 0 deletions code/__HELPERS/atmospherics.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/** A simple rudimentary gasmix to information list converter. Can be used for UIs.
* Args:
* * gasmix: [/datum/gas_mixture]
* * name: String used to name the list, optional.
* Returns: A list parsed_gasmixes with the following structure:
* - parsed_gasmixes Value: Assoc List Desc: The thing we return
* -- Key: name Value: String Desc: Gasmix Name
* -- Key: temperature Value: Number Desc: Temperature in kelvins
* -- Key: volume Value: Number Desc: Volume in liters
* -- Key: pressure Value: Number Desc: Pressure in kPa
* -- Key: oxygen Value: Number Desc: Amount of mols of O2
* -- Key: carbon_dioxide Value: Number Desc: Amount of mols of CO2
* -- Key: nitrogen Value: Number Desc: Amount of mols of N2
* -- Key: toxins Value: Number Desc: Amount of mols of plasma
* -- Key: sleeping_agent Value: Number Desc: Amount of mols of N2O
* -- Key: agent_b Value: Number Desc: Amount of mols of agent B
* -- Key: total_moles Value: Number Desc: Total amount of mols in the mixture
* Returned list should always be filled with keys even if value are nulls.
*/

//TODO: Port gas_mixture_parser from TG
/proc/gas_mixture_parser(datum/gas_mixture/gasmix, name)
. = list(
"oxygen" = null,
"carbon_dioxide" = null,
"nitrogen" = null,
"toxins" = null,
"sleeping_agent" = null,
"agent_b" = null,
"name" = format_text(name),
"total_moles" = null,
"temperature" = null,
"volume"= null,
"pressure"= null,
"heat_capacity" = null,
"thermal_energy" = null,
)
if(!gasmix)
return
.["oxygen"] = gasmix.oxygen
.["carbon_dioxide"] = gasmix.carbon_dioxide
.["nitrogen"] = gasmix.nitrogen
.["toxins"] = gasmix.toxins
.["sleeping_agent"] = gasmix.sleeping_agent
.["agent_b"] = gasmix.agent_b
.["total_moles"] = gasmix.total_moles()
.["temperature"] = gasmix.temperature
.["volume"] = gasmix.volume
.["pressure"] = gasmix.return_pressure()
.["heat_capacity"] = display_joules(gasmix.heat_capacity())
.["thermal_energy"] = display_joules(gasmix.thermal_energy())
28 changes: 0 additions & 28 deletions code/__HELPERS/unsorted.dm
Original file line number Diff line number Diff line change
Expand Up @@ -254,34 +254,6 @@ Turf and target are seperate in case you want to teleport some distance from a t
/proc/format_frequency(var/f)
return "[round(f / 10)].[f % 10]"

/obj/proc/atmosanalyzer_scan(var/datum/gas_mixture/air_contents, mob/user, var/obj/target = src)
var/obj/icon = target
user.visible_message("[user] has used the analyzer on [target].", "<span class='notice'>You use the analyzer on [target].</span>")
var/pressure = air_contents.return_pressure()
var/total_moles = air_contents.total_moles()

user.show_message("<span class='notice'>Results of analysis of [bicon(icon)] [target].</span>", 1)
if(total_moles>0)
var/o2_concentration = air_contents.oxygen/total_moles
var/n2_concentration = air_contents.nitrogen/total_moles
var/co2_concentration = air_contents.carbon_dioxide/total_moles
var/plasma_concentration = air_contents.toxins/total_moles

var/unknown_concentration = 1-(o2_concentration+n2_concentration+co2_concentration+plasma_concentration)

user.show_message("<span class='notice'>Pressure: [round(pressure,0.1)] kPa</span>", 1)
user.show_message("<span class='notice'>Nitrogen: [round(n2_concentration*100)] % ([round(air_contents.nitrogen,0.01)] moles)</span>", 1)
user.show_message("<span class='notice'>Oxygen: [round(o2_concentration*100)] % ([round(air_contents.oxygen,0.01)] moles)</span>", 1)
user.show_message("<span class='notice'>CO2: [round(co2_concentration*100)] % ([round(air_contents.carbon_dioxide,0.01)] moles)</span>", 1)
user.show_message("<span class='notice'>Plasma: [round(plasma_concentration*100)] % ([round(air_contents.toxins,0.01)] moles)</span>", 1)
if(unknown_concentration>0.01)
user.show_message("<span class='danger'>Unknown: [round(unknown_concentration*100)] % ([round(unknown_concentration*total_moles,0.01)] moles)</span>", 1)
user.show_message("<span class='notice'>Total: [round(total_moles,0.01)] moles</span>", 1)
user.show_message("<span class='notice'>Temperature: [round(air_contents.temperature-T0C)] &deg;C</span>", 1)
else
user.show_message("<span class='notice'>[target] is empty!</span>", 1)
return

//Picks a string of symbols to display as the law number for hacked or ion laws
/proc/ionnum()
return "[pick("!","@","#","$","%","^","&","*")][pick("!","@","#","$","%","^","&","*")][pick("!","@","#","$","%","^","&","*")][pick("!","@","#","$","%","^","&","*")]"
Expand Down
4 changes: 4 additions & 0 deletions code/game/atoms.dm
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,10 @@
else
return null

///Return the air if we can analyze it
/atom/proc/return_analyzable_air()
return null

/atom/proc/check_eye(mob/user)
return

Expand Down
6 changes: 3 additions & 3 deletions code/game/machinery/atmoalter/portable_atmospherics.dm
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,9 @@
if(holding)
. += "<span class='notice'>\The [src] contains [holding]. Alt-click [src] to remove it.</span>"

/obj/machinery/portable_atmospherics/return_analyzable_air()
return air_contents

/obj/machinery/portable_atmospherics/proc/replace_tank(mob/living/user, close_valve, obj/item/tank/new_tank)
if(holding)
holding.forceMove(drop_location())
Expand All @@ -126,9 +129,6 @@
src.holding = T
update_icon()
return
if((istype(W, /obj/item/analyzer)) && get_dist(user, src) <= 1)
atmosanalyzer_scan(air_contents, user)
return
return ..()

/obj/machinery/portable_atmospherics/wrench_act(mob/user, obj/item/I)
Expand Down
9 changes: 3 additions & 6 deletions code/game/machinery/atmoalter/scrubber.dm
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,9 @@
/obj/machinery/portable_atmospherics/scrubber/return_air()
return air_contents

/obj/machinery/portable_atmospherics/scrubber/return_analyzable_air()
return air_contents

/obj/machinery/portable_atmospherics/scrubber/attack_ai(mob/user)
add_hiddenprint(user)
return attack_hand(user)
Expand Down Expand Up @@ -185,12 +188,6 @@
else
icon_state = "scrubber:0"

/obj/machinery/portable_atmospherics/scrubber/huge/attackby(obj/item/W, mob/user, params)
if((istype(W, /obj/item/analyzer)) && get_dist(user, src) <= 1)
atmosanalyzer_scan(air_contents, user)
return
return ..()

/obj/machinery/portable_atmospherics/scrubber/huge/wrench_act(mob/user, obj/item/I)
. = TRUE
if(stationary)
Expand Down
5 changes: 5 additions & 0 deletions code/game/mecha/mecha.dm
Original file line number Diff line number Diff line change
Expand Up @@ -1009,6 +1009,11 @@
return cabin_air
return get_turf_air()

/obj/mecha/return_analyzable_air()
if(use_internal_tank)
return cabin_air
return null

/obj/mecha/proc/return_pressure()
var/datum/gas_mixture/t_air = return_air()
if(t_air)
Expand Down
132 changes: 0 additions & 132 deletions code/game/objects/items/devices/scanners.dm
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ CONTAINS:
T-RAY
DETECTIVE SCANNER
HEALTH ANALYZER
GAS ANALYZER
PLANT ANALYZER
REAGENT SCANNER
*/
Expand Down Expand Up @@ -585,137 +584,6 @@ REAGENT SCANNER
origin_tech = "magnets=2;biotech=2"
usesound = 'sound/items/deconstruct.ogg'

/obj/item/analyzer
desc = "A hand-held environmental scanner which reports current gas levels."
name = "analyzer"
icon = 'icons/obj/device.dmi'
icon_state = "atmos"
item_state = "analyzer"
w_class = WEIGHT_CLASS_SMALL
flags = CONDUCT
slot_flags = SLOT_BELT
throwforce = 0
throw_speed = 3
throw_range = 7
materials = list(MAT_METAL=30, MAT_GLASS=20)
origin_tech = "magnets=1;engineering=1"
var/cooldown = FALSE
var/cooldown_time = 250
var/accuracy // 0 is the best accuracy.

/obj/item/analyzer/examine(mob/user)
. = ..()
. += "<span class='info'>Alt-click [src] to activate the barometer function.</span>"

/obj/item/analyzer/attack_self(mob/user as mob)

if(user.stat)
return

var/turf/location = user.loc
if(!( istype(location, /turf) ))
return

var/datum/gas_mixture/environment = location.return_air()

var/pressure = environment.return_pressure()
var/total_moles = environment.total_moles()

to_chat(user, "<span class='info'><B>Results:</B></span>")
if(abs(pressure - ONE_ATMOSPHERE) < 10)
to_chat(user, "<span class='info'>Pressure: [round(pressure,0.1)] kPa</span>")
else
to_chat(user, "<span class='alert'>Pressure: [round(pressure,0.1)] kPa</span>")
if(total_moles)
var/o2_concentration = environment.oxygen/total_moles
var/n2_concentration = environment.nitrogen/total_moles
var/co2_concentration = environment.carbon_dioxide/total_moles
var/plasma_concentration = environment.toxins/total_moles

var/unknown_concentration = 1-(o2_concentration+n2_concentration+co2_concentration+plasma_concentration)
if(abs(n2_concentration - N2STANDARD) < 20)
to_chat(user, "<span class='info'>Nitrogen: [round(n2_concentration*100)] %</span>")
else
to_chat(user, "<span class='alert'>Nitrogen: [round(n2_concentration*100)] %</span>")

if(abs(o2_concentration - O2STANDARD) < 2)
to_chat(user, "<span class='info'>Oxygen: [round(o2_concentration*100)] %</span>")
else
to_chat(user, "<span class='alert'>Oxygen: [round(o2_concentration*100)] %</span>")

if(co2_concentration > 0.01)
to_chat(user, "<span class='alert'>CO2: [round(co2_concentration*100)] %</span>")
else
to_chat(user, "<span class='info'>CO2: [round(co2_concentration*100)] %</span>")

if(plasma_concentration > 0.01)
to_chat(user, "<span class='info'>Plasma: [round(plasma_concentration*100)] %</span>")

if(unknown_concentration > 0.01)
to_chat(user, "<span class='alert'>Unknown: [round(unknown_concentration*100)] %</span>")

to_chat(user, "<span class='info'>Temperature: [round(environment.temperature-T0C)] &deg;C</span>")

add_fingerprint(user)

/obj/item/analyzer/AltClick(mob/living/user) //Barometer output for measuring when the next storm happens
if(!istype(user) || user.incapacitated())
to_chat(user, "<span class='warning'>You can't do that right now!</span>")
return
if(!Adjacent(user))
return
if(cooldown)
to_chat(user, "<span class='warning'>[src]'s barometer function is prepraring itself.</span>")
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, "<span class='warning'>[src]'s barometer function won't work indoors!</span>")
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, "<span class='warning'>[src]'s barometer function can't trace anything while the storm is [ongoing_weather.stage == MAIN_STAGE ? "already here!" : "winding down."]</span>")
return
to_chat(user, "<span class='notice'>The next [ongoing_weather] will hit in [butchertime(ongoing_weather.next_hit_time - world.time)].</span>")
if(ongoing_weather.aesthetic)
to_chat(user, "<span class='warning'>[src]'s barometer function says that the next storm will breeze on by.</span>")
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, "<span class='warning'>[src]'s barometer function was unable to trace any weather patterns.</span>")
else
to_chat(user, "<span class='warning'>[src]'s barometer function says a storm will land in approximately [butchertime(fixed)].</span>")
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, "<span class='notice'>[src]'s barometer function is ready!</span>")
playsound(src, 'sound/machines/click.ogg', 100)
cooldown = FALSE

/obj/item/analyzer/proc/butchertime(amount)
if(!amount)
return
if(accuracy)
var/inaccurate = round(accuracy * (1 / 3))
if(prob(50))
amount -= inaccurate
if(prob(50))
amount += inaccurate
return DisplayTimeText(max(1, amount))

/obj/item/reagent_scanner
name = "reagent scanner"
desc = "A hand-held reagent scanner which identifies chemical agents and blood types."
Expand Down
Loading

0 comments on commit cd167b3

Please sign in to comment.