code:
//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31
// A datum for dealing with threshold limit values
// used in /obj/machinery/alarm
/datum/tlv
	var/min2
	var/min1
	var/max1
	var/max2

/datum/tlv/New(_min2 as num, _min1 as num, _max1 as num, _max2 as num)
	min2 = _min2
	min1 = _min1
	max1 = _max1
	max2 = _max2

/datum/tlv/proc/get_danger_level(curval as num)
	if (max2 >=0 && curval>=max2)
		return 2
	if (min2 >=0 && curval<=min2)
		return 2
	if (max1 >=0 && curval>=max1)
		return 1
	if (min1 >=0 && curval<=min1)
		return 1
	return 0

/datum/tlv/proc/CopyFrom(datum/tlv/other)
	min2 = other.min2
	min1 = other.min1
	max1 = other.max1
	max2 = other.max2

#define AALARM_MODE_SCRUBBING 1
#define AALARM_MODE_VENTING 2 //makes draught
#define AALARM_MODE_PANIC 3 //constantly sucks all air
#define AALARM_MODE_REPLACEMENT 4 //sucks off all air, then refill and swithes to scrubbing
#define AALARM_MODE_OFF 5
#define AALARM_MODE_FLOOD 6 //Emagged mode; turns off scrubbers and pressure checks on vents

#define AALARM_SCREEN_MAIN    1
#define AALARM_SCREEN_VENT    2
#define AALARM_SCREEN_SCRUB   3
#define AALARM_SCREEN_MODE    4
#define AALARM_SCREEN_SENSORS 5

#define AALARM_REPORT_TIMEOUT 100

/obj/machinery/alarm
	name = "alarm"
	desc = "A machine that monitors atmosphere levels. Goes off if the area is dangerous."
	icon = 'icons/obj/monitors.dmi'
	icon_state = "alarm0"
	anchored = 1
	use_power = 1
	idle_power_usage = 4
	active_power_usage = 8
	power_channel = ENVIRON
	req_access = list(access_atmospherics)
	var/frequency = 1439
	//var/skipprocess = 0 //Experimenting
	var/alarm_frequency = 1437

	var/datum/radio_frequency/radio_connection
	var/locked = 1
	var/datum/wires/alarm/wires = null
	var/aidisabled = 0
	var/shorted = 0
	var/buildstage = 2 // 2 = complete, 1 = no wires,  0 = circuit gone

	var/mode = AALARM_MODE_SCRUBBING

	var/screen = AALARM_SCREEN_MAIN
	var/area_uid
	var/area/alarm_area
	var/danger_level = 0

	// breathable air according to human/Life()
	var/list/TLV = list(
		"oxygen"         = new/datum/tlv(  16,   19, 135, 140), // Partial pressure, kpa
		"carbon dioxide" = new/datum/tlv(-1.0, -1.0,   5,  10), // Partial pressure, kpa
		"plasma"         = new/datum/tlv(-1.0, -1.0, 0.2, 0.5), // Partial pressure, kpa
		"other"          = new/datum/tlv(-1.0, -1.0, 0.5, 1.0), // Partial pressure, kpa
		"pressure"       = new/datum/tlv(ONE_ATMOSPHERE*0.80,ONE_ATMOSPHERE*0.90,ONE_ATMOSPHERE*1.10,ONE_ATMOSPHERE*1.20), /* kpa */
		"temperature"    = new/datum/tlv(T0C, T0C+10, T0C+40, T0C+66), // K
	)

/*
	// breathable air according to wikipedia
		"oxygen"         = new/datum/tlv(   9,  12, 158, 296), // Partial pressure, kpa
		"carbon dioxide" = new/datum/tlv(-1.0,-1.0, 0.5,   1), // Partial pressure, kpa
*/
/obj/machinery/alarm/server
	//req_access = list(access_rd) //no, let departaments to work together
	TLV = list(
		"oxygen"         = new/datum/tlv(-1.0, -1.0,-1.0,-1.0), // Partial pressure, kpa
		"carbon dioxide" = new/datum/tlv(-1.0, -1.0,-1.0,-1.0), // Partial pressure, kpa
		"plasma"         = new/datum/tlv(-1.0, -1.0,-1.0,-1.0), // Partial pressure, kpa
		"other"          = new/datum/tlv(-1.0, -1.0,-1.0,-1.0), // Partial pressure, kpa
		"pressure"       = new/datum/tlv(-1.0, -1.0,-1.0,-1.0), /* kpa */
		"temperature"    = new/datum/tlv(-1.0, -1.0,-1.0,-1.0), // K
	)

/obj/machinery/alarm/kitchen_cold_room
	TLV = list(
		"oxygen"         = new/datum/tlv(  16,   19, 135, 140), // Partial pressure, kpa
		"carbon dioxide" = new/datum/tlv(-1.0, -1.0,   5,  10), // Partial pressure, kpa
		"plasma"         = new/datum/tlv(-1.0, -1.0, 0.2, 0.5), // Partial pressure, kpa
		"other"          = new/datum/tlv(-1.0, -1.0, 0.5, 1.0), // Partial pressure, kpa
		"pressure"       = new/datum/tlv(ONE_ATMOSPHERE*0.80,ONE_ATMOSPHERE*0.90,ONE_ATMOSPHERE*1.50,ONE_ATMOSPHERE*1.60), /* kpa */
		"temperature"    = new/datum/tlv(200, 210, 273.15, 283.15), // K
	)

//all air alarms in area are connected via magic
/area
	var/obj/machinery/alarm/master_air_alarm
	var/list/air_vent_names = list()
	var/list/air_scrub_names = list()
	var/list/air_vent_info = list()
	var/list/air_scrub_info = list()

/obj/machinery/alarm/New(nloc, ndir, nbuild)
	..()
	wires = new(src)
	if(nloc)
		loc = nloc

	if(ndir)
		dir = ndir

	if(nbuild)
		buildstage = 0
		panel_open = 1
		pixel_x = (dir & 3)? 0 : (dir == 4 ? -24 : 24)
		pixel_y = (dir & 3)? (dir ==1 ? -24 : 24) : 0

	alarm_area = get_area(loc)
	area_uid = alarm_area.uid
	if (name == "alarm")
		name = "[alarm_area.name] Air Alarm"

	update_icon()
	if(ticker && ticker.current_state == 3)//if the game is running
		src.initialize()

/obj/machinery/alarm/Destroy()
	if(radio_controller)
		radio_controller.remove_object(src, frequency)
	..()

/obj/machinery/alarm/initialize()
	set_frequency(frequency)
	if (!master_is_operating())
		elect_master()

/obj/machinery/alarm/proc/master_is_operating()
	return alarm_area.master_air_alarm && !(alarm_area.master_air_alarm.stat & (NOPOWER|BROKEN))

/obj/machinery/alarm/proc/elect_master()
	var/area/A = alarm_area
	for (var/obj/machinery/alarm/AA in A)
		if (!(AA.stat & (NOPOWER|BROKEN)))
			alarm_area.master_air_alarm = AA
			return 1
	return 0

/obj/machinery/alarm/attack_hand(mob/user)
	if (..())
		return
	user.set_machine(src)

	if ( (get_dist(src, user) > 1 ))
		if (!istype(user, /mob/living/silicon))
			user.unset_machine()
			user << browse(null, "window=air_alarm")
			user << browse(null, "window=AAlarmwires")
			return

		else if (user.has_unlimited_silicon_privilege && src.aidisabled)
			user << "AI control for this Air Alarm interface has been disabled."
			user << browse(null, "window=air_alarm")
			return

	if(!shorted)
		//user << browse(return_text(),"window=air_alarm")
		//onclose(user, "air_alarm")
	/*	var/datum/browser/popup = new(user, "air_alarm", "[alarm_area.name] Air Alarm", 500, 400)
		popup.set_content(return_text())
		popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state))
		popup.open()
		refresh_all()*/
		ui_interact(user)

	if(panel_open && (!istype(user, /mob/living/silicon/ai)))
		wires.Interact(user)

	return

/obj/machinery/alarm/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null)
	if(stat & (BROKEN|NOPOWER))
		return
	ui = SSnano.push_open_or_new_ui(user, src, ui_key, ui, "air_alarm.tmpl", "Air Alarm", 350, 500, 1)

/obj/machinery/alarm/get_ui_data(mob/user)
	var/data = list()

	data["locked"] = locked
	data["siliconUser"] = user.has_unlimited_silicon_privilege
	data["screen"] = screen
	data["dangerous"] = emagged

	populate_status(data)
	if (!locked || user.has_unlimited_silicon_privilege)
		populate_controls(data)

	return data

/obj/machinery/alarm/proc/shock(mob/user, prb)
	if((stat & (NOPOWER)))		// unpowered, no shock
		return 0
	if(!prob(prb))
		return 0 //you lucked out, no shock for you
	var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread
	s.set_up(5, 1, src)
	s.start() //sparks always.
	if (electrocute_mob(user, get_area(src), src))
		return 1
	else
		return 0

/obj/machinery/alarm/proc/refresh_all()
	for(var/id_tag in alarm_area.air_vent_names)
		var/list/I = alarm_area.air_vent_info[id_tag]
		if (I && I["timestamp"]+AALARM_REPORT_TIMEOUT/2 > world.time)
			continue
		send_signal(id_tag, list("status") )
	for(var/id_tag in alarm_area.air_scrub_names)
		var/list/I = alarm_area.air_scrub_info[id_tag]
		if (I && I["timestamp"]+AALARM_REPORT_TIMEOUT/2 > world.time)
			continue
		send_signal(id_tag, list("status") )

/obj/machinery/alarm/proc/set_frequency(new_frequency)
	radio_controller.remove_object(src, frequency)
	frequency = new_frequency
	radio_connection = radio_controller.add_object(src, frequency, RADIO_TO_AIRALARM)

/obj/machinery/alarm/proc/send_signal(var/target, var/list/command)//sends signal 'command' to 'target'. Returns 0 if no radio connection, 1 otherwise
	if(!radio_connection)
		return 0

	var/datum/signal/signal = new
	signal.transmission_method = 1 //radio signal
	signal.source = src

	signal.data = command
	signal.data["tag"] = target
	signal.data["sigtype"] = "command"

	radio_connection.post_signal(src, signal, RADIO_FROM_AIRALARM)
//			world << text("Signal [] Broadcasted to []", command, target)

	return 1

/obj/machinery/alarm/proc/populate_status(var/list/data)
	var/turf/location = get_turf(src)
	var/datum/gas_mixture/environment = location.return_air()
	var/total = environment.oxygen + environment.carbon_dioxide + environment.toxins + environment.nitrogen

	var/list/environment_data = list()
	data["atmos_alarm"] = alarm_area.atmosalm
	data["fire_alarm"] = (alarm_area.fire != null && alarm_area.fire)
	data["danger_level"] = danger_level
	if(total)
		var/datum/tlv/cur_tlv
		var/partial_pressure = R_IDEAL_GAS_EQUATION*environment.temperature/environment.volume

		cur_tlv = TLV["pressure"]
		var/pressure = environment.return_pressure()
		var/pressure_danger = cur_tlv.get_danger_level(pressure)
		environment_data += list(list("name" = "Pressure", "value" = pressure, "unit" = "kPa", "danger_level" = pressure_danger))

		cur_tlv = TLV["oxygen"]
		var/oxygen_danger = cur_tlv.get_danger_level(environment.oxygen*partial_pressure)
		environment_data += list(list("name" = "Oxygen", "value" = environment.oxygen / total * 100, "unit" = "%", "danger_level" = oxygen_danger))

		cur_tlv = TLV["carbon dioxide"]
		var/carbon_dioxide_danger = cur_tlv.get_danger_level(environment.carbon_dioxide*partial_pressure)
		environment_data += list(list("name" = "Carbon dioxide", "value" = environment.carbon_dioxide / total * 100, "unit" = "%", "danger_level" = carbon_dioxide_danger))

		cur_tlv = TLV["plasma"]
		var/plasma_danger = cur_tlv.get_danger_level(environment.toxins*partial_pressure)
		environment_data += list(list("name" = "Toxins", "value" = environment.toxins / total * 100, "unit" = "%", "danger_level" = plasma_danger))

		cur_tlv = TLV["other"]
		var/other_moles = 0.0
		for(var/datum/gas/G in environment.trace_gases)
			other_moles+=G.moles
		var/other_danger = cur_tlv.get_danger_level(other_moles*partial_pressure)
		environment_data += list(list("name" = "Other", "value" = other_moles / total * 100, "unit" = "%", "danger_level" = other_danger))

		cur_tlv = TLV["temperature"]
		var/temperature_danger = cur_tlv.get_danger_level(environment.temperature)
		environment_data += list(list("name" = "Temperature", "value" = environment.temperature, "unit" = "K ([round(environment.temperature - T0C, 0.1)]C)", "danger_level" = temperature_danger))
		data["environment_data"] = environment_data

/obj/machinery/alarm/proc/populate_controls(var/list/data)
	switch(screen)
		if(AALARM_SCREEN_MAIN)
			data["mode"] = mode
		if(AALARM_SCREEN_VENT)
			data["vents"] = list()
			for(var/id_tag in alarm_area.air_vent_names)
				var/long_name = alarm_area.air_vent_names[id_tag]
				var/list/info = alarm_area.air_vent_info[id_tag]
				if(!info)
					continue
				data["vents"] += list(list(
						"id_tag"	= id_tag,
						"long_name" = sanitize(long_name),
						"power"		= info["power"],
						"checks"	= info["checks"],
						"excheck"	= info["checks"]&1,
						"incheck"	= info["checks"]&2,
						"direction"	= info["direction"],
						"external"	= info["external"]
					))
		if(AALARM_SCREEN_SCRUB)
			data["scrubbers"] = list()
			for(var/id_tag in alarm_area.air_scrub_names)
				var/long_name = alarm_area.air_scrub_names[id_tag]
				var/list/info = alarm_area.air_scrub_info[id_tag]
				if(!info)
					continue
				data["scrubbers"] += list(list(
						"id_tag"		= id_tag,
						"long_name" 	= sanitize(long_name),
						"power"			= info["power"],
						"scrubbing"		= info["scrubbing"],
						"panic"			= info["panic"],
						"filter_co2"	= info["filter_co2"],
						"filter_toxins"	= info["filter_toxins"],
						"filter_n2o"	= info["filter_n2o"]
					))
		if(AALARM_SCREEN_MODE)
			data["mode"] = mode
			data["modes"] = list()
			data["modes"] += list(list("name" = "Filtering - Scrubs out contaminants", 				"mode" = AALARM_MODE_SCRUBBING,		"selected" = mode == AALARM_MODE_SCRUBBING, 	"danger" = 0))
			data["modes"] += list(list("name" = "Draught - Siphons out air while replacing",		"mode" = AALARM_MODE_VENTING,		"selected" = mode == AALARM_MODE_VENTING,		"danger" = 0))
			data["modes"] += list(list("name" = "Cycle - Siphons air before replacing", 			"mode" = AALARM_MODE_REPLACEMENT,	"selected" = mode == AALARM_MODE_REPLACEMENT, 	"danger" = 1))
			data["modes"] += list(list("name" = "Panic - Siphons air out of the room", 				"mode" = AALARM_MODE_PANIC,			"selected" = mode == AALARM_MODE_PANIC, 		"danger" = 1))
			data["modes"] += list(list("name" = "Off - Shuts off vents and scrubbers", 				"mode" = AALARM_MODE_OFF,			"selected" = mode == AALARM_MODE_OFF, 			"danger" = 0))
			if (src.emagged)
				data["modes"] += list(list("name" = "Flood - Shuts off scrubbers and opens vents",	"mode" = AALARM_MODE_FLOOD,		"selected" = mode == AALARM_MODE_FLOOD, 			"danger" = 1))
		if(AALARM_SCREEN_SENSORS)
			var/datum/tlv/selected
			var/list/thresholds = list()

			var/list/gas_names = list(
				"oxygen"         = "O2",
				"carbon dioxide" = "CO2",
				"plasma"         = "Toxin",
				"other"          = "Other")
			for (var/g in gas_names)
				thresholds += list(list("name" = gas_names[g], "settings" = list()))
				selected = TLV[g]
				thresholds[thresholds.len]["settings"] += list(list("env" = g, "val" = "min2", "selected" = selected.min2))
				thresholds[thresholds.len]["settings"] += list(list("env" = g, "val" = "min1", "selected" = selected.min1))
				thresholds[thresholds.len]["settings"] += list(list("env" = g, "val" = "max1", "selected" = selected.max1))
				thresholds[thresholds.len]["settings"] += list(list("env" = g, "val" = "max2", "selected" = selected.max2))

			selected = TLV["pressure"]
			thresholds += list(list("name" = "Pressure", "settings" = list()))
			thresholds[thresholds.len]["settings"] += list(list("env" = "pressure", "val" = "min2", "selected" = selected.min2))
			thresholds[thresholds.len]["settings"] += list(list("env" = "pressure", "val" = "min1", "selected" = selected.min1))
			thresholds[thresholds.len]["settings"] += list(list("env" = "pressure", "val" = "max1", "selected" = selected.max1))
			thresholds[thresholds.len]["settings"] += list(list("env" = "pressure", "val" = "max2", "selected" = selected.max2))

			selected = TLV["temperature"]
			thresholds += list(list("name" = "Temperature", "settings" = list()))
			thresholds[thresholds.len]["settings"] += list(list("env" = "temperature", "val" = "min2", "selected" = selected.min2))
			thresholds[thresholds.len]["settings"] += list(list("env" = "temperature", "val" = "min1", "selected" = selected.min1))
			thresholds[thresholds.len]["settings"] += list(list("env" = "temperature", "val" = "max1", "selected" = selected.max1))
			thresholds[thresholds.len]["settings"] += list(list("env" = "temperature", "val" = "max2", "selected" = selected.max2))

			data["thresholds"] = thresholds

/obj/machinery/alarm/Topic(href, href_list)
	if(..())
		return
	usr.set_machine(src)

	if (locked && !usr.has_unlimited_silicon_privilege)
		return

	if ( (get_dist(src, usr) > 1 ))
		if (!istype(usr, /mob/living/silicon))
			usr.unset_machine()
			usr << browse(null, "window=air_alarm")
			return

	if(href_list["toggleaccess"])
		if(usr.has_unlimited_silicon_privilege && !wires.IsIndexCut(AALARM_WIRE_IDSCAN))
			locked = !locked

	if(href_list["command"])
		var/device_id = href_list["id_tag"]
		switch(href_list["command"])
			if("set_external_pressure")
				var/input_pressure = input("What pressure you like the system to mantain?", "Pressure Controls") as num|null
				if(isnum(input_pressure))
					send_signal(device_id, list(href_list["command"] = input_pressure))
				return 1

			if("reset_external_pressure")
				send_signal(device_id, list(href_list["command"] = ONE_ATMOSPHERE))
				return 1
			if(
				"power",
				"adjust_external_pressure",
				"co2_scrub",
				"tox_scrub",
				"n2o_scrub",
				"panic_siphon",
				"scrubbing"
			)
				send_signal(device_id, list (href_list["command"] = text2num(href_list["val"])))
				spawn(3)
					src.updateUsrDialog()

			if ("excheck")
				send_signal(device_id, list ("checks" = text2num(href_list["val"])^1))

			if ("incheck")
				send_signal(device_id, list ("checks" = text2num(href_list["val"])^2))

			//if("adjust_threshold") //was a good idea but required very wide window
			if("set_threshold")
				var/env = href_list["env"]
				var/varname = href_list["var"]
				var/datum/tlv/tlv = TLV[env]
				var/newval = input("Enter [varname] for [env]", "Alarm triggers", tlv.vars[varname]) as num|null

				if (isnull(newval) || ..() || (locked && !(usr.has_unlimited_silicon_privilege)))
					return
				if (newval<0)
					tlv.vars[varname] = -1.0
				else if (env=="temperature" && newval>5000)
					tlv.vars[varname] = 5000
				else if (env=="pressure" && newval>50*ONE_ATMOSPHERE)
					tlv.vars[varname] = 50*ONE_ATMOSPHERE
				else if (env!="temperature" && env!="pressure" && newval>200)
					tlv.vars[varname] = 200
				else
					newval = round(newval,0.01)
					tlv.vars[varname] = newval
				spawn(1)
					src.updateUsrDialog()

	if(href_list["screen"])
		screen = text2num(href_list["screen"])
		spawn(1)
			src.updateUsrDialog()

	if(href_list["atmos_alarm"])
		if (alarm_area.atmosalert(2,src))
			post_alert(2)
		spawn(1)
			src.updateUsrDialog()
		update_icon()
	if(href_list["atmos_reset"])
		if (alarm_area.atmosalert(0,src))
			post_alert(0)
		spawn(1)
			src.updateUsrDialog()
		update_icon()

	if(href_list["mode"])
		mode = text2num(href_list["mode"])
		apply_mode()
		spawn(5)
			src.updateUsrDialog()

	return

/obj/machinery/alarm/proc/apply_mode()
	switch(mode)
		if(AALARM_MODE_SCRUBBING)
			for(var/device_id in alarm_area.air_scrub_names)
				send_signal(device_id, list(
					"power"= 1,
					"co2_scrub"= 1,
					"scrubbing"= 1,
					"panic_siphon"= 0,
				))
			for(var/device_id in alarm_area.air_vent_names)
				send_signal(device_id, list(
					"power"= 1,
					"checks"= 1,
					"set_external_pressure"= ONE_ATMOSPHERE
				))

		if(AALARM_MODE_VENTING)
			for(var/device_id in alarm_area.air_scrub_names)
				send_signal(device_id, list(
					"power"= 1,
					"panic_siphon"= 0,
					"scrubbing"= 0
				))
			for(var/device_id in alarm_area.air_vent_names)
				send_signal(device_id, list(
					"power"= 1,
					"checks"= 1,
					"set_external_pressure"= ONE_ATMOSPHERE
				))
		if(
			AALARM_MODE_PANIC,
			AALARM_MODE_REPLACEMENT
		)
			for(var/device_id in alarm_area.air_scrub_names)
				send_signal(device_id, list(
					"power"= 1,
					"panic_siphon"= 1
				))
			for(var/device_id in alarm_area.air_vent_names)
				send_signal(device_id, list(
					"power"= 0
				))
		if(AALARM_MODE_OFF)
			for(var/device_id in alarm_area.air_scrub_names)
				send_signal(device_id, list(
					"panic_siphon"= 0,
					"power"= 0
				))
			for(var/device_id in alarm_area.air_vent_names)
				send_signal(device_id, list(
					"power"= 0
				))
		if(AALARM_MODE_FLOOD)
			for(var/device_id in alarm_area.air_scrub_names)
				send_signal(device_id, list(
					"panic_siphon"= 0,
					"power"=0
				))
			for(var/device_id in alarm_area.air_vent_names)
				send_signal(device_id, list(
					"power"= 1,
					"checks"= 0,
				))

/obj/machinery/alarm/update_icon()
	if(panel_open)
		switch(buildstage)
			if(2)
				icon_state = "alarmx"
			if(1)
				icon_state = "alarm_b2"
			if(0)
				icon_state = "alarm_b1"
		return

	if((stat & (NOPOWER|BROKEN)) || shorted)
		icon_state = "alarmp"
		return
	switch(max(danger_level, alarm_area.atmosalm))
		if (0)
			src.icon_state = "alarm0"
		if (1)
			src.icon_state = "alarm2" //yes, alarm2 is yellow alarm
		if (2)
			src.icon_state = "alarm1"

/obj/machinery/alarm/process()
	if((stat & (NOPOWER|BROKEN)) || shorted)
		return

	var/turf/simulated/location = src.loc
	if (!istype(location))
		return 0

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

	var/datum/tlv/cur_tlv
	var/GET_PP = R_IDEAL_GAS_EQUATION*environment.temperature/environment.volume

	cur_tlv = TLV["pressure"]
	var/environment_pressure = environment.return_pressure()
	var/pressure_dangerlevel = cur_tlv.get_danger_level(environment_pressure)

	cur_tlv = TLV["oxygen"]
	var/oxygen_dangerlevel = cur_tlv.get_danger_level(environment.oxygen*GET_PP)

	cur_tlv = TLV["carbon dioxide"]
	var/co2_dangerlevel = cur_tlv.get_danger_level(environment.carbon_dioxide*GET_PP)

	cur_tlv = TLV["plasma"]
	var/plasma_dangerlevel = cur_tlv.get_danger_level(environment.toxins*GET_PP)

	cur_tlv = TLV["other"]
	var/other_moles = 0.0
	for(var/datum/gas/G in environment.trace_gases)
		other_moles+=G.moles
	var/other_dangerlevel = cur_tlv.get_danger_level(other_moles*GET_PP)

	cur_tlv = TLV["temperature"]
	var/temperature_dangerlevel = cur_tlv.get_danger_level(environment.temperature)

	var/old_danger_level = danger_level
	danger_level = max(
		pressure_dangerlevel,
		oxygen_dangerlevel,
		co2_dangerlevel,
		plasma_dangerlevel,
		other_dangerlevel,
		temperature_dangerlevel
	)
	if (old_danger_level!=danger_level)
		apply_danger_level()

	if (mode==AALARM_MODE_REPLACEMENT && environment_pressure<ONE_ATMOSPHERE*0.05)
		mode=AALARM_MODE_SCRUBBING
		apply_mode()

	//src.updateDialog()
	return

/obj/machinery/alarm/proc/post_alert(alert_level)

	var/datum/radio_frequency/frequency = radio_controller.return_frequency(alarm_frequency)

	if(!frequency) return

	var/datum/signal/alert_signal = new
	alert_signal.source = src
	alert_signal.transmission_method = 1
	alert_signal.data["zone"] = alarm_area.name
	alert_signal.data["type"] = "Atmospheric"

	if(alert_level==2)
		alert_signal.data["alert"] = "severe"
	else if (alert_level==1)
		alert_signal.data["alert"] = "minor"
	else if (alert_level==0)
		alert_signal.data["alert"] = "clear"

	frequency.post_signal(src, alert_signal,null,-1)

/obj/machinery/alarm/proc/apply_danger_level()
	var/new_area_danger_level = 0
	var/area/A = alarm_area
	for (var/obj/machinery/alarm/AA in A)
		if (!(AA.stat & (NOPOWER|BROKEN)) && !AA.shorted)
			new_area_danger_level = max(new_area_danger_level,AA.danger_level)
	if (alarm_area.atmosalert(new_area_danger_level,src)) //if area was in normal state or if area was in alert state
		post_alert(new_area_danger_level)
	update_icon()

/obj/machinery/alarm/attackby(obj/item/W as obj, mob/user as mob, params)
	if (!user.IsAdvancedToolUser())
		user << "You don't have the dexterity to do this!"
		return
	switch(buildstage)
		if(2)
			if(istype(W, /obj/item/weapon/wirecutters) && panel_open && (wires.wires_status == 27 || wires.wires_status == 31))   //this checks for all wires to be cut, except the syphon wire which is optional.
				playsound(src.loc, 'sound/items/Wirecutter.ogg', 50, 1)
				user << "You cut the final wires."
				var/obj/item/stack/cable_coil/cable = new /obj/item/stack/cable_coil( src.loc )
				cable.amount = 5
				buildstage = 1
				update_icon()
				return

			if(istype(W, /obj/item/weapon/screwdriver))  // Opening that Air Alarm up.
				playsound(src.loc, 'sound/items/Screwdriver.ogg', 50, 1)
				panel_open = !panel_open
				user << "The wires have been [panel_open ? "exposed" : "unexposed"]"
				update_icon()
				return

			if (panel_open && ((istype(W, /obj/item/device/multitool) || istype(W, /obj/item/weapon/wirecutters))))
				return src.attack_hand(user)
			else if (istype(W, /obj/item/weapon/card/id) || istype(W, /obj/item/device/pda))// trying to unlock the interface with an ID card
				if(stat & (NOPOWER|BROKEN))
					user << "It does nothing"
				else
					if(src.allowed(usr) && !wires.IsIndexCut(AALARM_WIRE_IDSCAN))
						locked = !locked
						user << "You [ locked ? "lock" : "unlock"] the Air Alarm interface."
						src.updateUsrDialog()
					else
						user << "Access denied."
				return
		if(1)
			if(istype(W, /obj/item/weapon/crowbar))
				user.visible_message("[user.name] removes the electronics from [src.name].",\
									"You start prying out the circuit.")
				playsound(src.loc, 'sound/items/Crowbar.ogg', 50, 1)
				if (do_after(user, 20))
					if (buildstage == 1)
						user <<"You remove the air alarm electronics."
						new /obj/item/weapon/airalarm_electronics( src.loc )
						playsound(src.loc, 'sound/items/Deconstruct.ogg', 50, 1)
						buildstage = 0
						update_icon()
				return

			if(istype(W, /obj/item/stack/cable_coil))
				var/obj/item/stack/cable_coil/cable = W
				if(cable.get_amount() < 5)
					user << "You need five lengths of cable to wire the fire alarm."
					return
				user.visible_message("[user.name] wires the air alarm.", \
									"You start wiring the air alarm.")
				if (do_after(user, 20))
					if (cable.get_amount() >= 5 && buildstage == 1)
						cable.use(5)
						user << "You wire the air alarm."
						wires.wires_status = 0
						aidisabled = 0
						locked = 1
						mode = 1
						shorted = 0
						post_alert(0)
						buildstage = 2
						update_icon()
				return
		if(0)
			if(istype(W, /obj/item/weapon/airalarm_electronics))
				user << "You insert the circuit!"
				buildstage = 1
				update_icon()
				user.drop_item()
				qdel(W)
				return

			if(istype(W, /obj/item/weapon/wrench))
				user << "You detach \the [src] from the wall!"
				playsound(src.loc, 'sound/items/Ratchet.ogg', 50, 1)
				new /obj/item/alarm_frame( user.loc )
				qdel(src)
				return

	return ..()

/obj/machinery/alarm/power_change()
	if(powered(power_channel))
		stat &= ~NOPOWER
	else
		stat |= NOPOWER
	spawn(rand(0,15))
		if(loc)
			update_icon()

/obj/machinery/alarm/emag_act(mob/user as mob)
	if(!emagged)
		src.emagged = 1
		user.visible_message("Sparks fly out of the [src]!", "You emag the [src], disabling its safeties.")
		playsound(src.loc, 'sound/effects/sparks4.ogg', 50, 1)
		return

/*
AIR ALARM CIRCUIT
Just a object used in constructing air alarms
*/
/obj/item/weapon/airalarm_electronics
	name = "air alarm electronics"
	icon = 'icons/obj/module.dmi'
	icon_state = "airalarm_electronics"
	desc = "Looks like a circuit. Probably is."
	w_class = 2.0
	m_amt = 50
	g_amt = 50

/*
AIR ALARM ITEM
Handheld air alarm frame, for placing on walls
Code shamelessly copied from apc_frame
*/
/obj/item/alarm_frame
	name = "air alarm frame"
	desc = "Used for building Air Alarms"
	icon = 'icons/obj/monitors.dmi'
	icon_state = "alarm_bitem"
	flags = CONDUCT

/obj/item/alarm_frame/attackby(obj/item/weapon/W as obj, mob/user as mob, params)
	if (istype(W, /obj/item/weapon/wrench))
		new /obj/item/stack/sheet/metal( get_turf(src.loc), 2 )
		qdel(src)
		return
	..()

/obj/item/alarm_frame/proc/try_build(turf/on_wall)
	if (get_dist(on_wall,usr)>1)
		return

	var/ndir = get_dir(on_wall,usr)
	if (!(ndir in cardinal))
		return

	var/turf/loc = get_turf(usr)
	var/area/A = loc.loc
	if (!istype(loc, /turf/simulated/floor))
		usr << "Air Alarm cannot be placed on this spot."
		return
	if (A.requires_power == 0 || A.name == "Space")
		usr << "Air Alarm cannot be placed in this area."
		return

	if(gotwallitem(loc, ndir))
		usr << "There's already an item on this wall!"
		return

	new /obj/machinery/alarm(loc, ndir, 1)

	qdel(src)

/*
FIRE ALARM
*/
/obj/machinery/firealarm
	name = "fire alarm"
	desc = "\"Pull this in case of emergency\". Thus, keep pulling it forever."
	icon = 'icons/obj/monitors.dmi'
	icon_state = "fire0"
	var/detecting = 1.0
	var/time = 10.0
	var/timing = 0.0
	var/lockdownbyai = 0
	anchored = 1.0
	use_power = 1
	idle_power_usage = 2
	active_power_usage = 6
	power_channel = ENVIRON
	var/last_process = 0
	var/buildstage = 2 // 2 = complete, 1 = no wires,  0 = circuit gone

/obj/machinery/firealarm/update_icon()

	src.overlays = list()

	var/area/A = src.loc
	A = A.loc

	if(panel_open)
		switch(buildstage)
			if(0)
				icon_state="fire_b0"
				return
			if(1)
				icon_state="fire_b1"
				return
			if(2)
				icon_state="fire_b2"

		if((stat & BROKEN) || (stat & NOPOWER))
			return

		overlays += "overlay_[security_level]"
		return

	if(stat & BROKEN)
		icon_state = "firex"
		return

	icon_state = "fire0"

	if(stat & NOPOWER)
		return

	overlays += "overlay_[security_level]"

	if(!src.detecting)
		overlays += "overlay_fire"
	else
		overlays += "overlay_[A.fire ? "fire" : "clear"]"

/obj/machinery/firealarm/emag_act(mob/user as mob)
	if(!emagged)
		src.emagged = 1
		user.visible_message("Sparks fly out of the [src]!", "You emag the [src], disabling its thermal sensors.")
		playsound(src.loc, 'sound/effects/sparks4.ogg', 50, 1)
		return

/obj/machinery/firealarm/temperature_expose(datum/gas_mixture/air, temperature, volume)
	if(src.detecting)
		if(temperature > T0C+200)
			if(!emagged) //Doesn't give off alarm when emagged
				src.alarm()			// added check of detector status here
	return

/obj/machinery/firealarm/attack_ai(mob/user as mob)
	return src.attack_hand(user)

/obj/machinery/firealarm/bullet_act(BLAH)
	return src.alarm()

/obj/machinery/firealarm/attack_paw(mob/user as mob)
	return src.attack_hand(user)

/obj/machinery/firealarm/emp_act(severity)
	if(prob(50/severity)) alarm()
	..()

/obj/machinery/firealarm/attackby(obj/item/W as obj, mob/user as mob, params)
	src.add_fingerprint(user)
	if (!user.IsAdvancedToolUser())
		user << "You don't have the dexterity to do this!"
		return

	if (istype(W, /obj/item/weapon/screwdriver) && buildstage == 2)
		playsound(src.loc, 'sound/items/Screwdriver.ogg', 50, 1)
		panel_open = !panel_open
		user << "The wires have been [panel_open ? "exposed" : "unexposed"]"
		update_icon()
		return

	if(panel_open)
		switch(buildstage)
			if(2)
				if (istype(W, /obj/item/device/multitool))
					src.detecting = !( src.detecting )
					if (src.detecting)
						user.visible_message("[user] has reconnected [src]'s detecting unit!", "You have reconnected [src]'s detecting unit.")
					else
						user.visible_message("[user] has disconnected [src]'s detecting unit!", "You have disconnected [src]'s detecting unit.")

				else if (istype(W, /obj/item/weapon/wirecutters))
					buildstage = 1
					playsound(src.loc, 'sound/items/Wirecutter.ogg', 50, 1)
					var/obj/item/stack/cable_coil/coil = new /obj/item/stack/cable_coil()
					coil.amount = 5
					coil.loc = user.loc
					user << "You cut the wires from \the [src]"
					update_icon()
			if(1)
				if(istype(W, /obj/item/stack/cable_coil))
					var/obj/item/stack/cable_coil/coil = W
					if(coil.get_amount() < 5)
						user << "You need more cable for this!"
						return

					coil.use(5)

					buildstage = 2
					user << "You wire \the [src]!"
					update_icon()

				else if(istype(W, /obj/item/weapon/crowbar))
					playsound(src.loc, 'sound/items/Crowbar.ogg', 50, 1)
					user.visible_message("[user.name] removes the electronics from [src.name].", \
										"You start prying out the circuit.")
					if(do_after(user, 20))
						if(buildstage == 1)
							if(stat & BROKEN)
								user << "You remove the destroyed circuit!"
							else
								user << "You pry out the circuit!"
								new /obj/item/weapon/firealarm_electronics(user.loc)
							buildstage = 0
							update_icon()
			if(0)
				if(istype(W, /obj/item/weapon/firealarm_electronics))
					user << "You insert the circuit!"
					qdel(W)
					buildstage = 1
					update_icon()

				else if(istype(W, /obj/item/weapon/wrench))
					user.visible_message("[user] removes the fire alarm assembly from the wall!", \
										 "You remove the fire alarm assembly from the wall!")
					var/obj/item/firealarm_frame/frame = new /obj/item/firealarm_frame()
					frame.loc = user.loc
					playsound(src.loc, 'sound/items/Ratchet.ogg', 50, 1)
					qdel(src)
		return

	return

/obj/machinery/firealarm/process()//Note: this processing was mostly phased out due to other code, and only runs when needed
	if(stat & (NOPOWER|BROKEN))
		return

	if(src.timing)
		if(src.time > 0)
			src.time = src.time - ((world.timeofday - last_process)/10)
		else
			src.alarm()
			src.time = 0
			src.timing = 0
			SSobj.processing.Remove(src)
		src.updateDialog()
	last_process = world.timeofday
	return

/obj/machinery/firealarm/power_change()
	if(powered(ENVIRON))
		stat &= ~NOPOWER
	else
		stat |= NOPOWER
	spawn(rand(0,15))
		if(loc)
			update_icon()

/obj/machinery/firealarm/attack_hand(mob/user as mob)
	if(user.stat || stat & (NOPOWER|BROKEN))
		return

	if (buildstage != 2)
		return

	user.set_machine(src)
	var/area/A = src.loc
	var/safety_warning
	var/d1
	var/d2
	var/dat = ""
	if (istype(user, /mob/living/carbon/human) || user.has_unlimited_silicon_privilege)
		A = A.loc
		if (src.emagged)
			safety_warning = text("NOTICE: Thermal sensors nonfunctional. Device will not report or recognize high temperatures.")
		else
			safety_warning = text("Safety measures functioning properly.")
		if (A.fire)
			d1 = text("Reset - Lockdown", src)
		else
			d1 = text("Alarm - Lockdown", src)
		if (src.timing)
			d2 = text("Stop Time Lock", src)
		else
			d2 = text("Initiate Time Lock", src)
		var/second = round(src.time) % 60
		var/minute = (round(src.time) - second) / 60
		dat = "[safety_warning]

[d1]
The current alert level is: [get_security_level()]

Timer System: [d2]
Time Left: - - [(minute ? "[minute]:" : null)][second] + +"
		//user << browse(dat, "window=firealarm")
		//onclose(user, "firealarm")
	else
		A = A.loc
		if (A.fire)
			d1 = text("[]", src, stars("Reset - Lockdown"))
		else
			d1 = text("[]", src, stars("Alarm - Lockdown"))
		if (src.timing)
			d2 = text("[]", src, stars("Stop Time Lock"))
		else
			d2 = text("[]", src, stars("Initiate Time Lock"))
		var/second = round(src.time) % 60
		var/minute = (round(src.time) - second) / 60
		dat = "[d1]
The current alert level is: [stars(get_security_level())]

Timer System: [d2]
Time Left: - - [(minute ? text("[]:", minute) : null)][second] + +"
		//user << browse(dat, "window=firealarm")
		//onclose(user, "firealarm")
	var/datum/browser/popup = new(user, "firealarm", "Fire Alarm")
	popup.set_content(dat)
	popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state))
	popup.open()
	return

/obj/machinery/firealarm/Topic(href, href_list)
	if(..())
		return

	if (buildstage != 2)
		return

	usr.set_machine(src)
	if (href_list["reset"])
		src.reset()
	else if (href_list["alarm"])
		src.alarm()
	else if (href_list["time"])
		src.timing = text2num(href_list["time"])
		last_process = world.timeofday
		SSobj.processing |= src
	else if (href_list["tp"])
		var/tp = text2num(href_list["tp"])
		src.time += tp
		src.time = min(max(round(src.time), 0), 120)

	src.updateUsrDialog()

/obj/machinery/firealarm/proc/reset()
	if (stat & (NOPOWER|BROKEN)) // can't reset alarm if it's unpowered or broken.
		return
	var/area/A = get_area(src)
	A.firereset(src)
	return

/obj/machinery/firealarm/proc/alarm()
	if (stat & (NOPOWER|BROKEN))  // can't activate alarm if it's unpowered or broken.
		return
	var/area/A = get_area(src)
	A.firealert(src)
	//playsound(src.loc, 'sound/ambience/signal.ogg', 75, 0)
	return

/obj/machinery/firealarm/New(loc, dir, building)
	..()

	if(loc)
		src.loc = loc

	if(dir)
		src.dir = dir

	if(building)
		buildstage = 0
		panel_open = 1
		pixel_x = (dir & 3)? 0 : (dir == 4 ? -24 : 24)
		pixel_y = (dir & 3)? (dir ==1 ? -24 : 24) : 0

	if(z == 1)
		if(security_level)
			src.overlays += image('icons/obj/monitors.dmi', "overlay_[get_security_level()]")
		else
			src.overlays += image('icons/obj/monitors.dmi', "overlay_green")

	update_icon()

/*
FIRE ALARM CIRCUIT
Just a object used in constructing fire alarms
*/
/obj/item/weapon/firealarm_electronics
	name = "fire alarm electronics"
	icon = 'icons/obj/doors/door_assembly.dmi'
	icon_state = "door_electronics"
	desc = "A circuit. It has a label on it, it says \"Can handle heat levels up to 40 degrees celsius!\""
	w_class = 2.0
	m_amt = 50
	g_amt = 50

/*
FIRE ALARM ITEM
Handheld fire alarm frame, for placing on walls
Code shamelessly copied from apc_frame
*/
/obj/item/firealarm_frame
	name = "fire alarm frame"
	desc = "Used for building Fire Alarms"
	icon = 'icons/obj/monitors.dmi'
	icon_state = "fire_bitem"
	flags = CONDUCT

/obj/item/firealarm_frame/attackby(obj/item/weapon/W as obj, mob/user as mob, params)
	if (istype(W, /obj/item/weapon/wrench))
		new /obj/item/stack/sheet/metal( get_turf(src.loc), 2 )
		qdel(src)
		return
	..()

/obj/item/firealarm_frame/proc/try_build(turf/on_wall)
	if (get_dist(on_wall,usr)>1)
		return

	var/ndir = get_dir(on_wall,usr)
	if (!(ndir in cardinal))
		return

	var/turf/loc = get_turf(usr)
	var/area/A = loc.loc
	if (!istype(loc, /turf/simulated/floor))
		usr << "Fire Alarm cannot be placed on this spot."
		return
	if (A.requires_power == 0 || A.name == "Space")
		usr << "Fire Alarm cannot be placed in this area."
		return

	if(gotwallitem(loc, ndir))
		usr << "There's already an item on this wall!"
		return

	new /obj/machinery/firealarm(loc, ndir, 1)

	qdel(src)

/*
 * Party button
 */

/obj/machinery/firealarm/partyalarm
	name = "\improper PARTY BUTTON"
	desc = "Cuban Pete is in the house!"

/obj/machinery/firealarm/partyalarm/attack_hand(mob/user as mob)
	if(user.stat || stat & (NOPOWER|BROKEN))
		return

	user.set_machine(src)
	var/area/A = src.loc
	var/d1
	var/dat
	if (istype(user, /mob/living/carbon/human) || user.has_unlimited_silicon_privilege)
		A = A.loc

		if (A.party)
			d1 = text("No Party :(", src)
		else
			d1 = text("PARTY!!!", src)
		dat = text("<TT>Party Button []", d1)

	else
		A = A.loc
		if (A.fire)
			d1 = text("[]", src, stars("No Party :("))
		else
			d1 = text("[]", src, stars("PARTY!!!"))
		dat = text("<TT>[] []", stars("Party Button"), d1)

	var/datum/browser/popup = new(user, "firealarm", "Party Alarm")
	popup.set_content(dat)
	popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state))
	popup.open()
	return

/obj/machinery/firealarm/partyalarm/reset()
	if (stat & (NOPOWER|BROKEN))
		return
	var/area/A = src.loc
	A = A.loc
	if (!( istype(A, /area) ))
		return
	A.partyreset()
	return

/obj/machinery/firealarm/partyalarm/alarm()
	if (stat & (NOPOWER|BROKEN))
		return
	var/area/A = src.loc
	A = A.loc
	if (!( istype(A, /area) ))
		return
	A.partyalert()
	return


code:
/////////////////////////////////////////////////////////////
// AIR SENSOR (found in gaz tanks)
/////////////////////////////////////////////////////////////

obj/machinery/air_sensor
	icon = 'icons/obj/stationobjs.dmi'
	icon_state = "gsensor1"
	name = "gas sensor"

	anchored = 1
	var/state = 0

	var/id_tag
	var/frequency = 1439

	var/on = 1
	var/output = 3
	//Flags:
	// 1 for pressure
	// 2 for temperature
	// Output >= 4 includes gas composition
	// 4 for oxygen concentration
	// 8 for toxins concentration
	// 16 for nitrogen concentration
	// 32 for carbon dioxide concentration

	var/datum/radio_frequency/radio_connection

obj/machinery/air_sensor/update_icon()
		icon_state = "gsensor[on]"

obj/machinery/air_sensor/process_atmos()
	if(on)
		var/datum/signal/signal = new
		signal.transmission_method = 1 //radio signal
		signal.data["tag"] = id_tag
		signal.data["timestamp"] = world.time

		var/datum/gas_mixture/air_sample = return_air()

		if(output&1)
			signal.data["pressure"] = num2text(round(air_sample.return_pressure(),0.1))
		if(output&2)
			signal.data["temperature"] = round(air_sample.temperature,0.1)

		if(output>4)
			var/total_moles = air_sample.total_moles()
			if(total_moles > 0)
				if(output&4)
					signal.data["oxygen"] = round(100*air_sample.oxygen/total_moles,0.1)
				if(output&8)
					signal.data["toxins"] = round(100*air_sample.toxins/total_moles,0.1)
				if(output&16)
					signal.data["nitrogen"] = round(100*air_sample.nitrogen/total_moles,0.1)
				if(output&32)
					signal.data["carbon_dioxide"] = round(100*air_sample.carbon_dioxide/total_moles,0.1)
			else
				signal.data["oxygen"] = 0
				signal.data["toxins"] = 0
				signal.data["nitrogen"] = 0
				signal.data["carbon_dioxide"] = 0
		signal.data["sigtype"]="status"
		radio_connection.post_signal(src, signal, filter = RADIO_ATMOSIA)

obj/machinery/air_sensor/proc/set_frequency(new_frequency)
	radio_controller.remove_object(src, frequency)
	frequency = new_frequency
	radio_connection = radio_controller.add_object(src, frequency, RADIO_ATMOSIA)

obj/machinery/air_sensor/initialize()
	set_frequency(frequency)

obj/machinery/air_sensor/New()
	..()
	SSair.atmos_machinery += src
	if(radio_controller)
		set_frequency(frequency)

obj/machinery/air_sensor/Destroy()
	SSair.atmos_machinery -= src
	if(radio_controller)
		radio_controller.remove_object(src,frequency)
	..()

/////////////////////////////////////////////////////////////
// GENERAL AIR CONTROL (a.k.a atmos computer)
/////////////////////////////////////////////////////////////

obj/machinery/computer/general_air_control
	icon = 'icons/obj/computer.dmi'
	icon_state = "tank"

	circuit = /obj/item/weapon/circuitboard/air_management
	name = "computer"

	var/frequency = 1439
	var/list/sensors = list()

	var/list/sensor_information = list()
	var/datum/radio_frequency/radio_connection

obj/machinery/computer/general_air_control/New()
	..()

	if(radio_controller)
		set_frequency(frequency)

obj/machinery/computer/general_air_control/attack_hand(mob/user)
	if(..(user))
		return
	interact(user) //UpdateDialog() is calling /interact each tick, not attack_hand()

obj/machinery/computer/general_air_control/interact(mob/user)
	var/datum/browser/popup = new(user, "computer", name, 480, 490) //update the content every tick
	popup.set_content(return_text())
	popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state))
	popup.open()

obj/machinery/computer/general_air_control/process()
	if(..())	//if the computer is not broken or unpowered
		src.updateDialog()

obj/machinery/computer/general_air_control/receive_signal(datum/signal/signal)
	if(!signal || signal.encryption) return

	var/id_tag = signal.data["tag"]
	if(!id_tag || !sensors.Find(id_tag)) return

	sensor_information[id_tag] = signal.data

obj/machinery/computer/general_air_control/proc/return_text()
	var/sensor_data
	var/count = 0
	if(sensors.len)
		sensor_data += "

" //begin the 3x2 table formatting

		for(var/id_tag in sensors)
			var/long_name = sensors[id_tag]
			var/list/data = sensor_information[id_tag]
			var/sensor_part = "
[long_name]
"

			if(data)
				if(data["pressure"])
					sensor_part += "   Pressure: [data["pressure"]] kPa
"
				else
					sensor_part += "   Pressure: No pressure detected
"
				if(data["temperature"])
					sensor_part += "   Temperature: [data["temperature"]] K
"
				if(data["oxygen"]||data["toxins"]||data["nitrogen"]||data["carbon_dioxide"])
					sensor_part += "   Gas Composition : "
					if(data["oxygen"])
						sensor_part += "[data["oxygen"]]% O2; "
					if(data["nitrogen"])
						sensor_part += "[data["nitrogen"]]% N; "
					if(data["carbon_dioxide"])
						sensor_part += "[data["carbon_dioxide"]]% CO2; "
					if(data["toxins"])
						sensor_part += "[data["toxins"]]% TX; "

			else
				sensor_part = "[long_name] can not be found!
"

			sensor_data += "[sensor_part]	"//add the data to the current table cell
			count++;
			if(count == 2) //if we've put two readings on a line...
				sensor_data +="

" //... start a new one
				count = 0
		sensor_data += "

" //end the table formatting
	else
		sensor_data = "No sensors connected.

" // there's nothing in the sensors list (new computer), so choose between the two atmos computers
		sensor_data += "Initialize as Distribution and Waste Monitor
"
		sensor_data += "Initialize as Tank Monitor
"

	var/output = {"

Sensor Data
[sensor_data]"}

	return output

obj/machinery/computer/general_air_control/Destroy()
	if(radio_controller)
		radio_controller.remove_object(src, frequency)
	..()

obj/machinery/computer/general_air_control/proc/set_frequency(new_frequency)
	radio_controller.remove_object(src, frequency)
	frequency = new_frequency
	radio_connection = radio_controller.add_object(src, frequency, RADIO_ATMOSIA)

obj/machinery/computer/general_air_control/initialize()
	set_frequency(frequency)

obj/machinery/computer/general_air_control/Topic(href, href_list)
	if(..())
		return

	if(href_list["dist_loop"])
		name = "Distribution and Waste Monitor"
		set_frequency(1443)
		sensors = list("mair_in_meter" = "Mixed Air In", "air_sensor" = "Mixed Air Supply Tank", "mair_out_meter" = "Mixed Air Out", "dloop_atm_meter" = "Distribution Loop", "wloop_atm_meter" = "Waste Loop")

	if(href_list["tank_mon"])
		name = "Tank Monitor"
		set_frequency(1441)
		sensors = list("n2_sensor" = "Nitrogen", "o2_sensor" = "Oxygen", "co2_sensor" = "Carbon Dioxide", "tox_sensor" = "Toxins", "n2o_sensor" = "Nitrous Oxide", "waste_sensor" = "Gas Mix Tank")

/////////////////////////////////////////////////////////////
// LARGE TANK CONTROL
/////////////////////////////////////////////////////////////

obj/machinery/computer/general_air_control/large_tank_control
	icon = 'icons/obj/computer.dmi'
	icon_state = "tank"

	var/input_tag
	var/output_tag
	frequency = 1441
	circuit = /obj/item/weapon/circuitboard/large_tank_control

	var/list/input_info
	var/list/output_info

	var/pressure_setting = ONE_ATMOSPHERE * 45

obj/machinery/computer/general_air_control/large_tank_control/proc/reconnect(mob/user)    //This hacky madness is the evidence of the fact that a lot of machines were never meant to be constructable, im so sorry you had to see this
	var/list/IO = list()
	var/datum/radio_frequency/air_freq = radio_controller.return_frequency(1443)
	var/datum/radio_frequency/gas_freq = radio_controller.return_frequency(1441)
	var/list/devices = air_freq.devices["_default"]
	devices |= gas_freq.devices["_default"]
	for(var/obj/machinery/atmospherics/unary/vent_pump/U in devices)
		var/list/text = text2list(U.id_tag, "_")
		IO |= text[1]
	for(var/obj/machinery/atmospherics/unary/outlet_injector/U in devices)
		var/list/text = text2list(U.id, "_")
		IO |= text[1]
	if(!IO.len)
		user << "No machinery detected."
	var/S = input("Select the device set: ", "Selection", IO[1]) as anything in IO
	if(src)
		src.input_tag = "[S]_in"
		src.output_tag = "[S]_out"
		name = "[uppertext(S)] Supply Control"
		var/list/new_devices = gas_freq.devices["4"]
		new_devices |= air_freq.devices["4"]
		for(var/obj/machinery/air_sensor/U in new_devices)
			var/list/text = text2list(U.id_tag, "_")
			if(text[1] == S)
				sensors = list("[S]_sensor" = "Tank")
				break

	if(S == "air")
		frequency = 1443
	else
		frequency = 1441

	set_frequency(frequency)

	for(var/obj/machinery/atmospherics/unary/outlet_injector/U in devices)
		U.broadcast_status()

	for(var/obj/machinery/atmospherics/unary/vent_pump/U in devices)
		U.broadcast_status()

obj/machinery/computer/general_air_control/large_tank_control/return_text()
	var/output = "Reconnect
"
	if(sensors.len) //if recieving signals from nearby sensors...
		output += ..() //... get the data.
	else
		output += "No sensors connected."

	output += "
Tank Control System
"
	if(input_info)
		var/power = (input_info["power"])
		var/volume_rate = input_info["volume_rate"]
		output += {"Input: [power?("Injecting"):("On Hold")] Refresh

Rate: [volume_rate] L/sec
"}
		output += "Command: Toggle Power
"

	else
		output += "ERROR: Can not find input port
"

	output += "
"

	if(output_info)
		var/power = (output_info["power"])
		var/output_pressure = output_info["internal"]
		output += {"Output: [power?("Open"):("On Hold")] Refresh

Max Output Pressure: [output_pressure] kPa
"}
		output += "Command: Toggle Power Set Pressure
"
		output += "Max Output Pressure Set: - - - - [pressure_setting] kPa + + + +
"

	else
		output += "ERROR: Can not find output port
"

	return output

obj/machinery/computer/general_air_control/large_tank_control/receive_signal(datum/signal/signal)
	if(!signal || signal.encryption) return

	var/id_tag = signal.data["tag"]

	if(input_tag == id_tag)
		input_info = signal.data
	else if(output_tag == id_tag)
		output_info = signal.data
	else
		..(signal)

obj/machinery/computer/general_air_control/large_tank_control/Topic(href, href_list)
	if(..())
		return

	if(href_list["adj_pressure"])
		var/change = text2num(href_list["adj_pressure"])
		pressure_setting = Clamp(pressure_setting + change, 0, 50*ONE_ATMOSPHERE)
		return

	if(!radio_connection)
		return 0
	var/datum/signal/signal = new
	signal.transmission_method = 1 //radio signal
	signal.source = src
	if(href_list["reconnect"])
		reconnect(usr)
	if(href_list["in_refresh_status"])
		input_info = null
		signal.data = list ("tag" = input_tag, "status")

	if(href_list["in_toggle_injector"])
		input_info = null
		signal.data = list ("tag" = input_tag, "power_toggle")

	if(href_list["out_refresh_status"])
		output_info = null
		signal.data = list ("tag" = output_tag, "status")

	if(href_list["out_toggle_power"])
		output_info = null
		signal.data = list ("tag" = output_tag, "power_toggle")

	if(href_list["out_set_pressure"])
		output_info = null
		signal.data = list ("tag" = output_tag, "set_internal_pressure" = "[pressure_setting]")

	signal.data["sigtype"]="command"
	radio_connection.post_signal(src, signal, filter = RADIO_ATMOSIA)

/////////////////////////////////////////////////////////////
// FUEL INJECTION
/////////////////////////////////////////////////////////////

obj/machinery/computer/general_air_control/fuel_injection
	icon = 'icons/obj/computer.dmi'
	icon_state = "atmos"

	var/device_tag
	var/list/device_info

	var/automation = 0

	var/cutoff_temperature = 2000
	var/on_temperature = 1200

obj/machinery/computer/general_air_control/fuel_injection/process()
	if(automation)
		if(!radio_connection)
			return 0

		var/injecting = 0
		for(var/id_tag in sensor_information)
			var/list/data = sensor_information[id_tag]
			if(data["temperature"])
				if(data["temperature"] >= cutoff_temperature)
					injecting = 0
					break
				if(data["temperature"] <= on_temperature)
					injecting = 1

		var/datum/signal/signal = new
		signal.transmission_method = 1 //radio signal
		signal.source = src

		signal.data = list(
			"tag" = device_tag,
			"power" = injecting,
			"sigtype"="command"
		)

		radio_connection.post_signal(src, signal, filter = RADIO_ATMOSIA)

	..()

obj/machinery/computer/general_air_control/fuel_injection/return_text()
	var/output = ..()

	output += "Fuel Injection System
"
	if(device_info)
		var/power = device_info["power"]
		var/volume_rate = device_info["volume_rate"]
		output += {"Status: [power?("Injecting"):("On Hold")] Refresh

Rate: [volume_rate] L/sec
"}

		if(automation)
			output += "Automated Fuel Injection: Engaged
"
			output += "Injector Controls Locked Out
"
		else
			output += "Automated Fuel Injection: Disengaged
"
			output += "Injector: Toggle Power Inject (1 Cycle)
"

	else
		output += "ERROR: Can not find device Search
"

	return output

obj/machinery/computer/general_air_control/fuel_injection/receive_signal(datum/signal/signal)
	if(!signal || signal.encryption) return

	var/id_tag = signal.data["tag"]

	if(device_tag == id_tag)
		device_info = signal.data
	else
		..(signal)

obj/machinery/computer/general_air_control/fuel_injection/Topic(href, href_list)
	if(..())
		return

	if(href_list["refresh_status"])
		device_info = null
		if(!radio_connection)
			return 0

		var/datum/signal/signal = new
		signal.transmission_method = 1 //radio signal
		signal.source = src
		signal.data = list(
			"tag" = device_tag,
			"status",
			"sigtype"="command"
		)
		radio_connection.post_signal(src, signal, filter = RADIO_ATMOSIA)

	if(href_list["toggle_automation"])
		automation = !automation

	if(href_list["toggle_injector"])
		device_info = null
		if(!radio_connection)
			return 0

		var/datum/signal/signal = new
		signal.transmission_method = 1 //radio signal
		signal.source = src
		signal.data = list(
			"tag" = device_tag,
			"power_toggle",
			"sigtype"="command"
		)

		radio_connection.post_signal(src, signal, filter = RADIO_ATMOSIA)

	if(href_list["injection"])
		if(!radio_connection)
			return 0

		var/datum/signal/signal = new
		signal.transmission_method = 1 //radio signal
		signal.source = src
		signal.data = list(
			"tag" = device_tag,
			"inject",
			"sigtype"="command"
		)

		radio_connection.post_signal(src, signal, filter = RADIO_ATMOSIA)

license: mit


code:
/obj/machinery/computer/area_atmos
	name = "area air control computer"
	desc = "A computer used to control the stationary scrubbers and pumps in the area."
	icon_state = "area_atmos"
	circuit = "/obj/item/weapon/circuitboard/area_atmos"

	var/list/connectedscrubbers = new()
	var/status = ""

	var/range = 25

	//Simple variable to prevent me from doing attack_hand in both this and the child computer
	var/zone = "This computer is working on a wireless range, the range is currently limited to 25 meters."

/obj/machinery/computer/area_atmos/New()
	..()
	//So the scrubbers have time to spawn
	spawn(10)
		scanscrubbers()

/obj/machinery/computer/area_atmos/attack_hand(var/mob/user as mob)
	if(..(user))
		return
	src.add_fingerprint(usr)
	var/dat = {"

Area Air Control

			[status]

			Scan

"}
	for(var/obj/machinery/portable_atmospherics/scrubber/huge/scrubber in connectedscrubbers)
		dat += {"

					[scrubber.name]
					Turn On Turn Off

"}

	dat += {"

			[zone]

	"}
	user << browse("[dat]", "window=miningshuttle;size=400x400")
	status = ""

/obj/machinery/computer/area_atmos/Topic(href, href_list)
	if(..())
		return
	usr.set_machine(src)
	src.add_fingerprint(usr)

	if(href_list["scan"])
		scanscrubbers()
	else if(href_list["toggle"])
		var/obj/machinery/portable_atmospherics/scrubber/huge/scrubber = locate(href_list["scrub"])

		if(!validscrubber(scrubber))
			spawn(20)
				status = "ERROR: Couldn't connect to scrubber! (timeout)"
				connectedscrubbers -= scrubber
				src.updateUsrDialog()
			return

		scrubber.on = text2num(href_list["toggle"])
		scrubber.update_icon()

/obj/machinery/computer/area_atmos/proc/validscrubber( var/obj/machinery/portable_atmospherics/scrubber/huge/scrubber as obj )
	if(!isobj(scrubber) || get_dist(scrubber.loc, src.loc) > src.range || scrubber.loc.z != src.loc.z)
		return 0

	return 1

/obj/machinery/computer/area_atmos/proc/scanscrubbers()
	connectedscrubbers = new()

	var/found = 0
	for(var/obj/machinery/portable_atmospherics/scrubber/huge/scrubber in range(range, src.loc))
		if(istype(scrubber))
			found = 1
			connectedscrubbers += scrubber

	if(!found)
		status = "ERROR: No scrubber found!"

	src.updateUsrDialog()

/obj/machinery/computer/area_atmos/area
	zone = "This computer is working in a wired network limited to this area."

	validscrubber( var/obj/machinery/portable_atmospherics/scrubber/huge/scrubber as obj )
		if(!isobj(scrubber))
			return 0

		/*
		wow this is stupid, someone help me
		*/
		var/turf/T_src = get_turf(src)
		if(!T_src.loc) return 0
		var/area/A_src = T_src.loc

		var/turf/T_scrub = get_turf(scrubber)
		if(!T_scrub.loc) return 0
		var/area/A_scrub = T_scrub.loc

		if(A_scrub != A_src)
			return 0

		return 1

/obj/machinery/computer/area_atmos/area/scanscrubbers()
	connectedscrubbers = new()

	var/found = 0

	var/turf/T = get_turf(src)
	if(!T.loc) return
	var/area/A = T.loc
	for(var/obj/machinery/portable_atmospherics/scrubber/huge/scrubber in world ) //fugg
		var/turf/T2 = get_turf(scrubber)
		if(T2 && T2.loc)
			var/area/A2 = T2.loc
			if(istype(A2) && A2 && A2 == A )
				connectedscrubbers += scrubber
				found = 1

	if(!found)
		status = "ERROR: No scrubber found!"

	src.updateUsrDialog()


code:
/obj/machinery/portable_atmospherics/canister
	name = "canister"
	icon = 'icons/obj/atmos.dmi'
	icon_state = "yellow"
	density = 1
	var/health = 100.0

	var/valve_open = 0
	var/release_pressure = ONE_ATMOSPHERE

	var/canister_color = "yellow"
	var/can_label = 1
	var/filled = 0.5
	pressure_resistance = 7*ONE_ATMOSPHERE
	var/temperature_resistance = 1000 + T0C
	volume = 1000
	use_power = 0
	var/release_log = ""
	var/update_flag = 0

/obj/machinery/portable_atmospherics/canister/sleeping_agent
	name = "canister: \[N2O\]"
	icon_state = "redws"
	canister_color = "redws"
	can_label = 0
/obj/machinery/portable_atmospherics/canister/nitrogen
	name = "canister: \[N2\]"
	icon_state = "red"
	canister_color = "red"
	can_label = 0
/obj/machinery/portable_atmospherics/canister/oxygen
	name = "canister: \[O2\]"
	icon_state = "blue"
	canister_color = "blue"
	can_label = 0
/obj/machinery/portable_atmospherics/canister/toxins
	name = "canister \[Toxin (Bio)\]"
	icon_state = "orange"
	canister_color = "orange"
	can_label = 0
/obj/machinery/portable_atmospherics/canister/carbon_dioxide
	name = "canister \[CO2\]"
	icon_state = "black"
	canister_color = "black"
	can_label = 0
/obj/machinery/portable_atmospherics/canister/air
	name = "canister \[Air\]"
	icon_state = "grey"
	canister_color = "grey"
	can_label = 0

/obj/machinery/portable_atmospherics/canister/proc/check_change()
	var/old_flag = update_flag
	update_flag = 0
	if(holding)
		update_flag |= 1
	if(connected_port)
		update_flag |= 2

	var/tank_pressure = air_contents.return_pressure()
	if(tank_pressure < 10)
		update_flag |= 4
	else if(tank_pressure < ONE_ATMOSPHERE)
		update_flag |= 8
	else if(tank_pressure < 15*ONE_ATMOSPHERE)
		update_flag |= 16
	else
		update_flag |= 32

	if(update_flag == old_flag)
		return 1
	else
		return 0

/obj/machinery/portable_atmospherics/canister/update_icon()
/*
update_flag
1 = holding
2 = connected_port
4 = tank_pressure < 10
8 = tank_pressure < ONE_ATMOS
16 = tank_pressure < 15*ONE_ATMOS
32 = tank_pressure go boom.
*/

	if (destroyed)
		overlays = 0
		icon_state = text("[]-1", canister_color)
		return

	if(icon_state != "[canister_color]")
		icon_state = "[canister_color]"

	if(check_change()) //Returns 1 if no change needed to icons.
		return

	src.overlays = 0

	if(update_flag & 1)
		overlays += "can-open"
	if(update_flag & 2)
		overlays += "can-connector"
	if(update_flag & 4)
		overlays += "can-o0"
	if(update_flag & 8)
		overlays += "can-o1"
	else if(update_flag & 16)
		overlays += "can-o2"
	else if(update_flag & 32)
		overlays += "can-o3"
	return

/obj/machinery/portable_atmospherics/canister/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume)
	if(exposed_temperature > temperature_resistance)
		health -= 5
		healthcheck()

/obj/machinery/portable_atmospherics/canister/proc/healthcheck()
	if(destroyed)
		return 1

	if (src.health <= 10)
		var/atom/location = src.loc
		location.assume_air(air_contents)
		air_update_turf()

		src.destroyed = 1
		playsound(src.loc, 'sound/effects/spray.ogg', 10, 1, -3)
		src.density = 0
		update_icon()
		investigate_log("was destroyed by heat/gunfire.", "atmos")

		if (src.holding)
			src.holding.loc = src.loc
			src.holding = null

		return 1
	else
		return 1

/obj/machinery/portable_atmospherics/canister/process_atmos()
	if (destroyed)
		return PROCESS_KILL

	..()

	if(valve_open)
		var/datum/gas_mixture/environment
		if(holding)
			environment = holding.air_contents
		else
			environment = loc.return_air()

		var/env_pressure = environment.return_pressure()
		var/pressure_delta = min(release_pressure - env_pressure, (air_contents.return_pressure() - env_pressure)/2)
		//Can not have a pressure delta that would cause environment pressure > tank pressure

		var/transfer_moles = 0
		if((air_contents.temperature > 0) && (pressure_delta > 0))
			transfer_moles = pressure_delta*environment.volume/(air_contents.temperature * R_IDEAL_GAS_EQUATION)

			//Actually transfer the gas
			var/datum/gas_mixture/removed = air_contents.remove(transfer_moles)

			if(holding)
				environment.merge(removed)
			else
				loc.assume_air(removed)
				air_update_turf()
			src.update_icon()

	if(!air_contents)
		return
	if(air_contents.return_pressure() < 1)
		can_label = 1
	else
		can_label = 0

/obj/machinery/portable_atmospherics/canister/process()
	src.updateDialog()
	return ..()

/obj/machinery/portable_atmospherics/canister/return_air()
	return air_contents

/obj/machinery/portable_atmospherics/canister/proc/return_temperature()
	var/datum/gas_mixture/GM = src.return_air()
	if(GM && GM.volume>0)
		return GM.temperature
	return 0

/obj/machinery/portable_atmospherics/canister/proc/return_pressure()
	var/datum/gas_mixture/GM = src.return_air()
	if(GM && GM.volume>0)
		return GM.return_pressure()
	return 0

/obj/machinery/portable_atmospherics/canister/blob_act()
	src.health -= 200
	healthcheck()
	return

/obj/machinery/portable_atmospherics/canister/bullet_act(var/obj/item/projectile/Proj)
	if((Proj.damage_type == BRUTE || Proj.damage_type == BURN))
		if(Proj.damage)
			src.health -= round(Proj.damage / 2)
			healthcheck()
	..()

/obj/machinery/portable_atmospherics/canister/ex_act(severity, target)
	switch(severity)
		if(1.0)
			if(destroyed || prob(30))
				qdel(src)
			else
				src.health = 0
				healthcheck()
			return
		if(2.0)
			if(destroyed)
				qdel(src)
			else
				src.health -= rand(40, 100)
				healthcheck()
			return
		if(3.0)
			src.health -= rand(15,40)
			healthcheck()
			return
	return

/obj/machinery/portable_atmospherics/canister/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob, params)
	if(!istype(W, /obj/item/weapon/wrench) && !istype(W, /obj/item/weapon/tank) && !istype(W, /obj/item/device/analyzer) && !istype(W, /obj/item/device/pda))
		visible_message("[user] hits \the [src] with a [W]!")
		investigate_log("was smacked with \a [W] by [key_name(user)]", "atmos")
		src.health -= W.force
		src.add_fingerprint(user)
		healthcheck()

	if(istype(user, /mob/living/silicon/robot) && istype(W, /obj/item/weapon/tank/jetpack))
		var/datum/gas_mixture/thejetpack = W:air_contents
		var/env_pressure = thejetpack.return_pressure()
		var/pressure_delta = min(10*ONE_ATMOSPHERE - env_pressure, (air_contents.return_pressure() - env_pressure)/2)
		//Can not have a pressure delta that would cause environment pressure > tank pressure
		var/transfer_moles = 0
		if((air_contents.temperature > 0) && (pressure_delta > 0))
			transfer_moles = pressure_delta*thejetpack.volume/(air_contents.temperature * R_IDEAL_GAS_EQUATION)//Actually transfer the gas
			var/datum/gas_mixture/removed = air_contents.remove(transfer_moles)
			thejetpack.merge(removed)
			user << "You pulse-pressurize your jetpack from the tank."
		return

	..()

/obj/machinery/portable_atmospherics/canister/attack_ai(var/mob/user as mob)
	return src.attack_hand(user)

/obj/machinery/portable_atmospherics/canister/attack_paw(var/mob/user as mob)
	return src.attack_hand(user)

/obj/machinery/portable_atmospherics/canister/attack_hand(var/mob/user as mob)
	return src.ui_interact(user)

/obj/machinery/portable_atmospherics/canister/interact(mob/user, ui_key = "main")
	if (src.destroyed || !user)
		return

	SSnano.try_update_ui(user, src, ui_key, null, src.get_ui_data())

/obj/machinery/portable_atmospherics/canister/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null)
	if (src.destroyed)
		return
	ui = SSnano.push_open_or_new_ui(user, src, ui_key, ui, "canister.tmpl", "Canister", 480, 400, 0)

/obj/machinery/portable_atmospherics/canister/get_ui_data()
	var/data = list()
	data["name"] = src.name
	data["canLabel"] = src.can_label ? 1 : 0
	data["portConnected"] = src.connected_port ? 1 : 0
	data["tankPressure"] = round(src.air_contents.return_pressure() ? src.air_contents.return_pressure() : 0)
	data["releasePressure"] = round(src.release_pressure ? src.release_pressure : 0)
	data["minReleasePressure"] = round(ONE_ATMOSPHERE/10)
	data["maxReleasePressure"] = round(10*ONE_ATMOSPHERE)
	data["valveOpen"] = src.valve_open ? 1 : 0

	data["hasHoldingTank"] = src.holding ? 1 : 0
	if (holding)
		data["holdingTank"] = list("name" = src.holding.name, "tankPressure" = round(src.holding.air_contents.return_pressure()))

	return data

/obj/machinery/portable_atmospherics/canister/Topic(href, href_list)

	//Do not use "if(..()) return" here, canisters will stop working in unpowered areas like space or on the derelict.
	if(!usr.canmove || usr.stat || usr.restrained() || !in_range(loc, usr))
		usr << browse(null, "window=canister")
		onclose(usr, "canister")
		return

	if (((get_dist(src, usr) <= 1) && istype(src.loc, /turf)))
		usr.set_machine(src)

		if(href_list["toggle"])
			var/logmsg
			if (valve_open)
				if (holding)
					logmsg = "Valve was closed by [key_name(usr)], stopping the transfer into the [holding]
"
				else
					logmsg = "Valve was closed by [key_name(usr)], stopping the transfer into the air
"
			else
				if (holding)
					logmsg = "Valve was opened by [key_name(usr)], starting the transfer into the [holding]
"
				else
					logmsg = "Valve was opened by [key_name(usr)], starting the transfer into the air
"
					if(air_contents.toxins > 0)
						message_admins("[key_name(usr)] (?) opened a canister that contains plasma! (JMP)")
						log_admin("[key_name(usr)] opened a canister that contains plasma at [x], [y], [z]")
					var/datum/gas/sleeping_agent = locate(/datum/gas/sleeping_agent) in air_contents.trace_gases
					if(sleeping_agent && (sleeping_agent.moles > 1))
						message_admins("[key_name(usr)] (?) opened a canister that contains N2O! (JMP)")
						log_admin("[key_name(usr)] opened a canister that contains N2O at [x], [y], [z]")
			investigate_log(logmsg, "atmos")
			release_log += logmsg
			valve_open = !valve_open

		if (href_list["remove_tank"])
			if(holding)
				holding.loc = loc
				holding = null

		if (href_list["pressure_adj"])
			var/diff = text2num(href_list["pressure_adj"])
			if(diff > 0)
				release_pressure = min(10*ONE_ATMOSPHERE, release_pressure+diff)
			else
				release_pressure = max(ONE_ATMOSPHERE/10, release_pressure+diff)

		if (href_list["relabel"])
			if (can_label)
				var/list/colors = list(\
					"\[N2O\]" = "redws", \
					"\[N2\]" = "red", \
					"\[O2\]" = "blue", \
					"\[Toxin (Bio)\]" = "orange", \
					"\[CO2\]" = "black", \
					"\[Air\]" = "grey", \
					"\[CAUTION\]" = "yellow", \
				)
				var/label = input("Choose canister label", "Gas canister") as null|anything in colors
				if (label)
					src.canister_color = colors[label]
					src.icon_state = colors[label]
					src.name = "canister: [label]"
		src.updateUsrDialog()
		src.add_fingerprint(usr)
		update_icon()
	else
		usr << browse(null, "window=canister")
		return
	return

/obj/machinery/portable_atmospherics/canister/toxins/New()

	..()

	src.air_contents.toxins = (src.maximum_pressure*filled)*air_contents.volume/(R_IDEAL_GAS_EQUATION*air_contents.temperature)

	src.update_icon()
	return 1

/obj/machinery/portable_atmospherics/canister/oxygen/New()

	..()

	src.air_contents.oxygen = (src.maximum_pressure*filled)*air_contents.volume/(R_IDEAL_GAS_EQUATION*air_contents.temperature)

	src.update_icon()
	return 1

/obj/machinery/portable_atmospherics/canister/sleeping_agent/New()

	..()

	var/datum/gas/sleeping_agent/trace_gas = new
	air_contents.trace_gases += trace_gas
	trace_gas.moles = (src.maximum_pressure*filled)*air_contents.volume/(R_IDEAL_GAS_EQUATION*air_contents.temperature)

	src.update_icon()
	return 1

//Dirty way to fill room with gas. However it is a bit easier to do than creating some floor/engine/n2o -rastaf0
/obj/machinery/portable_atmospherics/canister/sleeping_agent/roomfiller/New()
	..()
	var/datum/gas/sleeping_agent/trace_gas = air_contents.trace_gases[1]
	trace_gas.moles = 9*4000
	spawn(10)
		var/turf/simulated/location = src.loc
		if (istype(src.loc))
			while (!location.air)
				sleep(10)
			location.assume_air(air_contents)
			air_contents = new
	return 1

/obj/machinery/portable_atmospherics/canister/nitrogen/New()

	..()

	src.air_contents.nitrogen = (src.maximum_pressure*filled)*air_contents.volume/(R_IDEAL_GAS_EQUATION*air_contents.temperature)

	src.update_icon()
	return 1

/obj/machinery/portable_atmospherics/canister/carbon_dioxide/New()

	..()
	src.air_contents.carbon_dioxide = (src.maximum_pressure*filled)*air_contents.volume/(R_IDEAL_GAS_EQUATION*air_contents.temperature)

	src.update_icon()
	return 1

/obj/machinery/portable_atmospherics/canister/air/New()

	..()
	src.air_contents.oxygen = (O2STANDARD*src.maximum_pressure*filled)*air_contents.volume/(R_IDEAL_GAS_EQUATION*air_contents.temperature)
	src.air_contents.nitrogen = (N2STANDARD*src.maximum_pressure*filled)*air_contents.volume/(R_IDEAL_GAS_EQUATION*air_contents.temperature)

	src.update_icon()
	return 1


code:
/obj/machinery/meter
	name = "gas flow meter"
	desc = "It measures something."
	icon = 'icons/obj/meter.dmi'
	icon_state = "meterX"
	var/obj/machinery/atmospherics/pipe/target = null
	anchored = 1.0
	power_channel = ENVIRON
	var/frequency = 0
	var/id
	use_power = 1
	idle_power_usage = 2
	active_power_usage = 4

/obj/machinery/meter/New()
	..()
	SSair.atmos_machinery += src
	src.target = locate(/obj/machinery/atmospherics/pipe) in loc
	return 1

/obj/machinery/meter/Destroy()
	SSair.atmos_machinery -= src
	src.target = null
	..()

/obj/machinery/meter/initialize()
	if (!target)
		src.target = locate(/obj/machinery/atmospherics/pipe) in loc

/obj/machinery/meter/process_atmos()
	if(!target)
		icon_state = "meterX"
		return 0

	if(stat & (BROKEN|NOPOWER))
		icon_state = "meter0"
		return 0

	use_power(5)

	var/datum/gas_mixture/environment = target.return_air()
	if(!environment)
		icon_state = "meterX"
		return 0

	var/env_pressure = environment.return_pressure()
	if(env_pressure <= 0.15*ONE_ATMOSPHERE)
		icon_state = "meter0"
	else if(env_pressure <= 1.8*ONE_ATMOSPHERE)
		var/val = round(env_pressure/(ONE_ATMOSPHERE*0.3) + 0.5)
		icon_state = "meter1_[val]"
	else if(env_pressure <= 30*ONE_ATMOSPHERE)
		var/val = round(env_pressure/(ONE_ATMOSPHERE*5)-0.35) + 1
		icon_state = "meter2_[val]"
	else if(env_pressure <= 59*ONE_ATMOSPHERE)
		var/val = round(env_pressure/(ONE_ATMOSPHERE*5) - 6) + 1
		icon_state = "meter3_[val]"
	else
		icon_state = "meter4"

	if(frequency)
		var/datum/radio_frequency/radio_connection = radio_controller.return_frequency(frequency)

		if(!radio_connection) return

		var/datum/signal/signal = new
		signal.source = src
		signal.transmission_method = 1
		signal.data = list(
			"tag" = id,
			"device" = "AM",
			"pressure" = round(env_pressure),
			"sigtype" = "status"
		)
		radio_connection.post_signal(src, signal)

/obj/machinery/meter/proc/status()
	var/t = ""
	if (src.target)
		var/datum/gas_mixture/environment = target.return_air()
		if(environment)
			t += "The pressure gauge reads [round(environment.return_pressure(), 0.01)] kPa; [round(environment.temperature,0.01)] K ([round(environment.temperature-T0C,0.01)]°C)"
		else
			t += "The sensor error light is blinking."
	else
		t += "The connect error light is blinking."
	return t

/obj/machinery/meter/examine(mob/user)
	..()
	user << status()

/obj/machinery/meter/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob, params)
	if (istype(W, /obj/item/weapon/wrench))
		playsound(src.loc, 'sound/items/Ratchet.ogg', 50, 1)
		user << "You begin to unfasten \the [src]..."
		if (do_after(user, 40))
			user.visible_message( \
				"[user] unfastens \the [src].", \
				"You have unfastened \the [src].", \
				"You hear ratchet.")
			new /obj/item/pipe_meter(src.loc)
			qdel(src)
		return
	..()

/obj/machinery/meter/attack_ai(var/mob/user as mob)
	return src.attack_hand(user)

/obj/machinery/meter/attack_paw(var/mob/user as mob)
	return src.attack_hand(user)

/obj/machinery/meter/attack_hand(var/mob/user as mob)

	if(stat & (NOPOWER|BROKEN))
		return 1
	else
		usr << status()
		return 1

// TURF METER - REPORTS A TILE'S AIR CONTENTS
//	why are you yelling?

/obj/machinery/meter/turf/New()
	..()
	src.target = loc
	return 1

/obj/machinery/meter/turf/initialize()
	if (!target)
		src.target = loc


code:
/obj/machinery/portable_atmospherics
	name = "atmoalter"
	use_power = 0
	var/datum/gas_mixture/air_contents = new

	var/obj/machinery/atmospherics/unary/portables_connector/connected_port
	var/obj/item/weapon/tank/holding

	var/volume = 0
	var/destroyed = 0

	var/lastupdate = 0
	var/maximum_pressure = 90*ONE_ATMOSPHERE

/obj/machinery/portable_atmospherics/New()
	..()
	SSair.atmos_machinery += src
	air_contents.volume = volume
	air_contents.temperature = T20C

	return 1

/obj/machinery/portable_atmospherics/process_atmos()
	//if(!connected_port) //only react when pipe_network will not it do it for you
		//Allow for reactions
	if(air_contents)  //implying pipe_network
		air_contents.react()
	update_icon()

/obj/machinery/portable_atmospherics/process()
	return
/obj/machinery/portable_atmospherics/Destroy()
	qdel(air_contents)
	SSair.atmos_machinery -= src
	..()

/obj/machinery/portable_atmospherics/update_icon()
	return null

/obj/machinery/portable_atmospherics/proc/connect(obj/machinery/atmospherics/unary/portables_connector/new_port)
	//Make sure not already connected to something else
	if(connected_port || !new_port || new_port.connected_device)
		return 0

	//Make sure are close enough for a valid connection
	if(new_port.loc != loc)
		return 0

	//Perform the connection
	connected_port = new_port
	connected_port.connected_device = src
	if(connected_port.parent)
		connected_port.parent.reconcile_air()

	anchored = 1 //Prevent movement
	return 1

/obj/machinery/portable_atmospherics/proc/disconnect()
	if(!connected_port)
		return 0
	anchored = 0
	connected_port.connected_device = null
	connected_port = null
	return 1

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

/obj/machinery/portable_atmospherics/attackby(var/obj/item/weapon/W as obj, var/mob/user as mob, params)
	if ((istype(W, /obj/item/weapon/tank) && !( src.destroyed )))
		if (src.holding)
			return
		var/obj/item/weapon/tank/T = W
		user.drop_item()
		T.loc = src
		src.holding = T
		update_icon()
		return

	else if (istype(W, /obj/item/weapon/wrench))
		if(connected_port)
			disconnect()
			user << "You disconnect [name] from the port."
			update_icon()
			return
		else
			var/obj/machinery/atmospherics/unary/portables_connector/possible_port = locate(/obj/machinery/atmospherics/unary/portables_connector) in loc
			if(possible_port)
				if(connect(possible_port))
					user << "You connect [name] to the port."
					update_icon()
					return
				else
					user << "[name] failed to connect to the port."
					return
			else
				user << "Nothing happens."
				return

	else if ((istype(W, /obj/item/device/analyzer)) && get_dist(user, src) <= 1)
		atmosanalyzer_scan(air_contents, user)

	return


code:
/obj/machinery/portable_atmospherics/pump
	name = "portable air pump"

	icon = 'icons/obj/atmos.dmi'
	icon_state = "psiphon:0"
	density = 1

	var/on = 0
	var/direction_out = 0 //0 = siphoning, 1 = releasing
	var/target_pressure = 100

	volume = 1000

/obj/machinery/portable_atmospherics/pump/update_icon()
	src.overlays = 0

	if(on)
		icon_state = "psiphon:1"
	else
		icon_state = "psiphon:0"

	if(holding)
		overlays += "siphon-open"

	if(connected_port)
		overlays += "siphon-connector"

	return

/obj/machinery/portable_atmospherics/pump/emp_act(severity)
	if(stat & (BROKEN|NOPOWER))
		..(severity)
		return

	if(prob(50/severity))
		on = !on

	if(prob(100/severity))
		direction_out = !direction_out

	target_pressure = rand(0,1300)
	update_icon()

	..(severity)

/obj/machinery/portable_atmospherics/pump/process_atmos()
	..()
	if(on)
		var/datum/gas_mixture/environment
		if(holding)
			environment = holding.air_contents
		else
			environment = loc.return_air()
		if(direction_out)
			var/pressure_delta = target_pressure - environment.return_pressure()
			//Can not have a pressure delta that would cause environment pressure > tank pressure

			var/transfer_moles = 0
			if(air_contents.temperature > 0)
				transfer_moles = pressure_delta*environment.volume/(air_contents.temperature * R_IDEAL_GAS_EQUATION)

				//Actually transfer the gas
				var/datum/gas_mixture/removed = air_contents.remove(transfer_moles)

				if(holding)
					environment.merge(removed)
				else
					loc.assume_air(removed)
					air_update_turf()
		else
			var/pressure_delta = target_pressure - air_contents.return_pressure()
			//Can not have a pressure delta that would cause environment pressure > tank pressure

			var/transfer_moles = 0
			if(environment.temperature > 0)
				transfer_moles = pressure_delta*air_contents.volume/(environment.temperature * R_IDEAL_GAS_EQUATION)

				//Actually transfer the gas
				var/datum/gas_mixture/removed
				if(holding)
					removed = environment.remove(transfer_moles)
				else
					removed = loc.remove_air(transfer_moles)
					air_update_turf()

				air_contents.merge(removed)
		//src.update_icon()
/obj/machinery/portable_atmospherics/pump/process()
	..()
	src.updateDialog()
	return

/obj/machinery/portable_atmospherics/pump/return_air()
	return air_contents

/obj/machinery/portable_atmospherics/pump/attack_ai(var/mob/user as mob)
	return src.attack_hand(user)

/obj/machinery/portable_atmospherics/pump/attack_paw(var/mob/user as mob)
	return src.attack_hand(user)

/obj/machinery/portable_atmospherics/pump/attack_hand(var/mob/user as mob)

	user.set_machine(src)
	var/holding_text

	if(holding)
		holding_text = {"
Tank Pressure: [holding.air_contents.return_pressure()] KPa

Remove Tank

"}
	var/output_text = {"<TT>[name]

Pressure: [air_contents.return_pressure()] KPa

Port Status: [(connected_port)?("Connected"):("Disconnected")]
[holding_text]

Power Switch: [on?("On"):("Off")]

Pump Direction: [direction_out?("Out"):("In")]

Target Pressure: - - - - [target_pressure] + + + +

Close

"}

	user << browse(output_text, "window=pump;size=600x300")
	onclose(user, "pump")

	return

/obj/machinery/portable_atmospherics/pump/Topic(href, href_list)
	..()
	if (usr.stat || usr.restrained())
		return

	if (((get_dist(src, usr) <= 1) && istype(src.loc, /turf)))
		usr.set_machine(src)

		if(href_list["power"])
			on = !on

		if(href_list["direction"])
			direction_out = !direction_out

		if (href_list["remove_tank"])
			if(holding)
				holding.loc = loc
				holding = null

		if (href_list["pressure_adj"])
			var/diff = text2num(href_list["pressure_adj"])
			target_pressure = min(10*ONE_ATMOSPHERE, max(0, target_pressure+diff))

		src.updateUsrDialog()
		src.add_fingerprint(usr)
		update_icon()
	else
		usr << browse(null, "window=pump")
		return
	return


code:
/obj/machinery/portable_atmospherics/scrubber
	name = "portable air scrubber"

	icon = 'icons/obj/atmos.dmi'
	icon_state = "pscrubber:0"
	density = 1

	var/on = 0
	var/volume_rate = 800

	volume = 750

/obj/machinery/portable_atmospherics/scrubber/emp_act(severity)
	if(stat & (BROKEN|NOPOWER))
		..(severity)
		return

	if(prob(50/severity))
		on = !on
		update_icon()

	..(severity)

/obj/machinery/portable_atmospherics/scrubber/huge
	name = "huge air scrubber"
	icon_state = "scrubber:0"
	anchored = 1
	volume = 50000
	volume_rate = 5000

	var/global/gid = 1
	var/id = 0
	New()
		..()
		id = gid
		gid++

		name = "[name] (ID [id])"

	attack_hand(var/mob/user as mob)
		usr << "You can't directly interact with this machine. Use the area atmos computer."

	update_icon()
		src.overlays = 0

		if(on)
			icon_state = "scrubber:1"
		else
			icon_state = "scrubber:0"

	attackby(var/obj/item/weapon/W as obj, var/mob/user as mob)
		if(istype(W, /obj/item/weapon/wrench))
			if(on)
				user << "Turn it off first!"
				return

			anchored = !anchored
			playsound(src.loc, 'sound/items/Ratchet.ogg', 50, 1)
			user << "You [anchored ? "wrench" : "unwrench"] \the [src]."

			return

		..()

/obj/machinery/portable_atmospherics/scrubber/huge/stationary
	name = "stationary air scrubber"

	attackby(var/obj/item/weapon/W as obj, var/mob/user as mob)
		if(istype(W, /obj/item/weapon/wrench))
			user << "The bolts are too tight for you to unscrew!"
			return

		..()

/obj/machinery/portable_atmospherics/scrubber/update_icon()
	src.overlays = 0

	if(on)
		icon_state = "pscrubber:1"
	else
		icon_state = "pscrubber:0"

	if(holding)
		overlays += "scrubber-open"

	if(connected_port)
		overlays += "scrubber-connector"

	return

/obj/machinery/portable_atmospherics/scrubber/process_atmos()
	..()

	if(on)
		var/datum/gas_mixture/environment
		if(holding)
			environment = holding.air_contents
		else
			environment = loc.return_air()
		var/transfer_moles = min(1, volume_rate/environment.volume)*environment.total_moles()

		//Take a gas sample
		var/datum/gas_mixture/removed
		if(holding)
			removed = environment.remove(transfer_moles)
		else
			removed = loc.remove_air(transfer_moles)

		//Filter it
		if (removed)
			var/datum/gas_mixture/filtered_out = new

			filtered_out.temperature = removed.temperature

			filtered_out.toxins = removed.toxins
			removed.toxins = 0

			filtered_out.carbon_dioxide = removed.carbon_dioxide
			removed.carbon_dioxide = 0

			if(removed.trace_gases.len>0)
				for(var/datum/gas/trace_gas in removed.trace_gases)
					if(istype(trace_gas, /datum/gas/sleeping_agent))
						removed.trace_gases -= trace_gas
						filtered_out.trace_gases += trace_gas

			if(removed.trace_gases.len>0)
				for(var/datum/gas/trace_gas in removed.trace_gases)
					if(istype(trace_gas, /datum/gas/oxygen_agent_b))
						removed.trace_gases -= trace_gas
						filtered_out.trace_gases += trace_gas

		//Remix the resulting gases
			air_contents.merge(filtered_out)

			if(holding)
				environment.merge(removed)
			else
				loc.assume_air(removed)
		//src.update_icon()
/obj/machinery/portable_atmospherics/scrubber/process()
	..()
	src.updateDialog()
	return

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

/obj/machinery/portable_atmospherics/scrubber/attack_ai(var/mob/user as mob)
	return src.attack_hand(user)

/obj/machinery/portable_atmospherics/scrubber/attack_paw(var/mob/user as mob)
	return src.attack_hand(user)

/obj/machinery/portable_atmospherics/scrubber/attack_hand(var/mob/user as mob)

	user.set_machine(src)
	var/holding_text

	if(holding)
		holding_text = {"
Tank Pressure: [holding.air_contents.return_pressure()] KPa

Remove Tank

"}
	var/output_text = {"<TT>[name]

Pressure: [air_contents.return_pressure()] KPa

Port Status: [(connected_port)?("Connected"):("Disconnected")]
[holding_text]

Power Switch: [on?("On"):("Off")]

Power regulator: - - - - [volume_rate] + + + +

Close

"}

	user << browse(output_text, "window=scrubber;size=600x300")
	onclose(user, "scrubber")
	return

/obj/machinery/portable_atmospherics/scrubber/Topic(href, href_list)
	..()
	if (usr.stat || usr.restrained())
		return

	if (((get_dist(src, usr) <= 1) && istype(src.loc, /turf)))
		usr.set_machine(src)

		if(href_list["power"])
			on = !on

		if (href_list["remove_tank"])
			if(holding)
				holding.loc = loc
				holding = null

		if (href_list["volume_adj"])
			var/diff = text2num(href_list["volume_adj"])
			volume_rate = min(10*ONE_ATMOSPHERE, max(0, volume_rate+diff))

		src.updateUsrDialog()
		src.add_fingerprint(usr)
		update_icon()
	else
		usr << browse(null, "window=scrubber")
		return
	return


code:
/obj/machinery/zvent
	name = "interfloor air transfer system"

	icon = 'icons/obj/pipes.dmi'
	icon_state = "vent-db"
	density = 0
	anchored=1

	var/on = 0
	var/volume_rate = 800

/obj/machinery/zvent/New()
	..()
	SSair.atmos_machinery += src

/obj/machinery/zvent/Destroy()
	SSair.atmos_machinery -= src
	..()

/obj/machinery/zvent/process_atmos()

	//all this object does, is make its turf share air with the ones above and below it, if they have a vent too.
	if (istype(loc,/turf/simulated)) //if we're not on a valid turf, forget it
		for (var/new_z in list(-1,1))  //change this list if a fancier system of z-levels gets implemented
			var/turf/simulated/zturf_conn = locate(x,y,z+new_z)
			if (istype(zturf_conn))
				var/obj/machinery/zvent/zvent_conn= locate(/obj/machinery/zvent) in zturf_conn
				if (istype(zvent_conn))
					//both floors have simulated turfs, share()
					var/turf/simulated/myturf = loc
					var/datum/gas_mixture/conn_air = zturf_conn.air //TODO: pop culture reference
					var/datum/gas_mixture/my_air = myturf.air
					if (istype(conn_air) && istype(my_air))
						my_air.share(conn_air)
						air_update_turf()


code:
#define AUTOLATHE_MAIN_MENU       1
#define AUTOLATHE_CATEGORY_MENU   2
#define AUTOLATHE_SEARCH_MENU     3

/obj/machinery/autolathe
	name = "autolathe"
	desc = "It produces items using metal and glass."
	icon_state = "autolathe"
	density = 1

	var/m_amount = 0.0
	var/max_m_amount = 150000.0

	var/g_amount = 0.0
	var/max_g_amount = 75000.0

	var/operating = 0.0
	anchored = 1.0
	var/list/L = list()
	var/list/LL = list()
	var/hacked = 0
	var/disabled = 0
	var/shocked = 0
	var/hack_wire
	var/disable_wire
	var/shock_wire
	use_power = 1
	idle_power_usage = 10
	active_power_usage = 100
	var/busy = 0
	var/prod_coeff
	var/datum/wires/autolathe/wires = null

	var/datum/design/being_built
	var/datum/research/files
	var/list/datum/design/matching_designs
	var/selected_category
	var/screen = 1

	var/list/categories = list(
							"Tools",
							"Electronics",
							"Construction",
							"T-Comm",
							"Security",
							"Medical",
							"Misc"
							)

/obj/machinery/autolathe/New()
	..()
	component_parts = list()
	component_parts += new /obj/item/weapon/circuitboard/autolathe(null)
	component_parts += new /obj/item/weapon/stock_parts/matter_bin(null)
	component_parts += new /obj/item/weapon/stock_parts/matter_bin(null)
	component_parts += new /obj/item/weapon/stock_parts/matter_bin(null)
	component_parts += new /obj/item/weapon/stock_parts/manipulator(null)
	component_parts += new /obj/item/weapon/stock_parts/console_screen(null)
	RefreshParts()

	wires = new(src)
	files = new /datum/research/autolathe(src)
	matching_designs = list()

/obj/machinery/autolathe/interact(mob/user)
	if(!is_operational())
		return

	if(shocked && !(stat & NOPOWER))
		shock(user,50)

	var/dat

	if(panel_open)
		dat = wires.GetInteractWindow()

	else
		switch(screen)
			if(AUTOLATHE_MAIN_MENU)
				dat = main_win(user)
			if(AUTOLATHE_CATEGORY_MENU)
				dat = category_win(user,selected_category)
			if(AUTOLATHE_SEARCH_MENU)
				dat = search_win(user)

	var/datum/browser/popup = new(user, "autolathe", name, 400, 500)
	popup.set_content(dat)
	popup.open()

	return

/obj/machinery/autolathe/attackby(obj/item/O, mob/user, params)
	if (busy)
		user << "The autolathe is busy. Please wait for completion of previous operation."
		return 1

	if(default_deconstruction_screwdriver(user, "autolathe_t", "autolathe", O))
		updateUsrDialog()
		return

	if(exchange_parts(user, O))
		return

	if (panel_open)
		if(istype(O, /obj/item/weapon/crowbar))
			if(m_amount >= MINERAL_MATERIAL_AMOUNT)
				var/obj/item/stack/sheet/metal/G = new /obj/item/stack/sheet/metal(src.loc)
				G.amount = round(m_amount / MINERAL_MATERIAL_AMOUNT)
			if(g_amount >= MINERAL_MATERIAL_AMOUNT)
				var/obj/item/stack/sheet/glass/G = new /obj/item/stack/sheet/glass(src.loc)
				G.amount = round(g_amount / MINERAL_MATERIAL_AMOUNT)
			default_deconstruction_crowbar(O)
			return 1
		else
			attack_hand(user)
			return 1
	if (stat)
		return 1

	if (src.m_amount + O.m_amt > max_m_amount)
		user << "The autolathe is full. Please remove metal from the autolathe in order to insert more."
		return 1
	if (src.g_amount + O.g_amt > max_g_amount)
		user << "The autolathe is full. Please remove glass from the autolathe in order to insert more."
		return 1
	if (O.m_amt == 0 && O.g_amt == 0)
		user << "This object does not contain significant amounts of metal or glass, or cannot be accepted by the autolathe due to size or hazardous materials."
		return 1

	var/amount = 1
	var/obj/item/stack/stack
	var/m_amt = O.m_amt
	var/g_amt = O.g_amt
	if (istype(O, /obj/item/stack))
		stack = O
		amount = stack.amount
		if (m_amt)
			amount = min(amount, round((max_m_amount-src.m_amount)/m_amt))
			flick("autolathe_o",src)//plays metal insertion animation
		if (g_amt)
			amount = min(amount, round((max_g_amount-src.g_amount)/g_amt))
			flick("autolathe_r",src)//plays glass insertion animation
		stack.use(amount)
	else
		if(!user.unEquip(O))
			user << "[O] is stuck to your hand, you can't put it in \the [src]!"
			return
		O.loc = src
	icon_state = "autolathe"
	busy = 1
	use_power(max(1000, (m_amt+g_amt)*amount/10))
	src.m_amount += m_amt * amount
	src.g_amount += g_amt * amount
	user << "You insert [amount] sheet[amount>1 ? "s" : ""] to the autolathe."
	if (O && O.loc == src)
		qdel(O)
	busy = 0
	src.updateUsrDialog()

/obj/machinery/autolathe/attack_paw(mob/user)
	return attack_hand(user)

/obj/machinery/autolathe/attack_hand(mob/user)
	if(..(user, 0))
		return
	interact(user)

/obj/machinery/autolathe/Topic(href, href_list)
	if(..())
		return
	if (!busy)
		if(href_list["menu"])
			screen = text2num(href_list["menu"])

		if(href_list["category"])
			selected_category = href_list["category"]

		if(href_list["make"])

			var/turf/T = get_step(src.loc, get_dir(src,usr))

			/////////////////
			//href protection
			being_built = files.FindDesignByID(href_list["make"]) //check if it's a valid design
			if(!being_built)
				return

			//multiplier checks : only stacks can have one and its value is 1, 10 ,25 or max_multiplier
			var/multiplier = text2num(href_list["multiplier"])
			var/max_multiplier = min(50, being_built.materials["$metal"] ?round(m_amount/being_built.materials["$metal"]):INFINITY,being_built.materials["$glass"]?round(g_amount/being_built.materials["$glass"]):INFINITY)
			var/is_stack = ispath(being_built.build_path, /obj/item/stack)

			if(!is_stack && (multiplier > 1))
				return
			if (!(multiplier in list(1,10,25,max_multiplier))) //"enough materials ?" is checked further down
				return
			/////////////////

			var/coeff = (is_stack ? 1 : 2 ** prod_coeff) //stacks are unaffected by production coefficient
			var/metal_cost = being_built.materials["$metal"]
			var/glass_cost = being_built.materials["$glass"]

			var/power = max(2000, (metal_cost+glass_cost)*multiplier/5)

			if((m_amount >= metal_cost*multiplier/coeff) && (g_amount >= glass_cost*multiplier/coeff))
				busy = 1
				use_power(power)
				icon_state = "autolathe"
				flick("autolathe_n",src)
				spawn(32/coeff)
					use_power(power)
					if(is_stack)
						m_amount -= metal_cost*multiplier
						g_amount -= glass_cost*multiplier
						var/obj/item/stack/S = new being_built.build_path(T)
						S.amount = multiplier
					else
						m_amount -= metal_cost/coeff
						g_amount -= glass_cost/coeff
						var/obj/item/new_item = new being_built.build_path(T)
						new_item.m_amt /= coeff
						new_item.g_amt /= coeff
					if(m_amount < 0)
						m_amount = 0
					if(g_amount < 0)
						g_amount = 0
					busy = 0
					src.updateUsrDialog()

		if(href_list["search"])
			matching_designs.Cut()

			for(var/datum/design/D in files.known_designs)
				if(findtext(D.name,href_list["to_search"]))
					matching_designs.Add(D)
	else
		usr << "The autolathe is busy. Please wait for completion of previous operation."

	src.updateUsrDialog()

	return

/obj/machinery/autolathe/RefreshParts()
	var/tot_rating = 0
	prod_coeff = 0
	for(var/obj/item/weapon/stock_parts/matter_bin/MB in component_parts)
		tot_rating += MB.rating
	tot_rating *= 25000
	max_m_amount = tot_rating * 2
	max_g_amount = tot_rating
	for(var/obj/item/weapon/stock_parts/manipulator/M in component_parts)
		prod_coeff += M.rating - 1

/obj/machinery/autolathe/proc/main_win(mob/user)
	var/dat = "

Autolathe Menu:

"
	dat += "Metal amount: [src.m_amount] / [max_m_amount] cm3
"
	dat += "Glass amount: [src.g_amount] / [max_g_amount] cm3"

	dat += "<form name='search' action='?src=\ref[src]'> \
	<input type='hidden' name='src' value='\ref[src]'> \
	<input type='hidden' name='search' value='to_search'> \
	<input type='hidden' name='menu' value='[AUTOLATHE_SEARCH_MENU]'> \
	<input type='text' name='to_search'> \
	<input type='submit' value='Search'> \
	</form>
"

	var/line_length = 1
	dat += "

"

	for(var/C in categories)
		if(line_length > 2)
			dat += "

"
			line_length = 1

		dat += "[C]	"
		line_length++

	dat += "

"
	return dat

/obj/machinery/autolathe/proc/category_win(mob/user,var/selected_category)
	var/dat = "Return to main menu"
	dat += "

Browsing [selected_category]:

"
	dat += "Metal amount: [src.m_amount] / [max_m_amount] cm3
"
	dat += "Glass amount: [src.g_amount] / [max_g_amount] cm3
"

	for(var/datum/design/D in files.known_designs)
		if(!(selected_category in D.category))
			continue

		if(disabled || !can_build(D))
			dat += "[D.name]"
		else
			dat += "[D.name]"

		if(ispath(D.build_path, /obj/item/stack))
			var/max_multiplier = min(50, D.materials["$metal"] ?round(m_amount/D.materials["$metal"]):INFINITY,D.materials["$glass"]?round(g_amount/D.materials["$glass"]):INFINITY)
			if (max_multiplier>10 && !disabled)
				dat += " x10"
			if (max_multiplier>25 && !disabled)
				dat += " x25"
			if(max_multiplier > 0 && !disabled)
				dat += " x[max_multiplier]"

		dat += "[get_design_cost(D)]
"

	dat += "
"
	return dat

/obj/machinery/autolathe/proc/search_win(mob/user)
	var/dat = "Return to main menu"
	dat += "

Search results:

"
	dat += "Metal amount: [src.m_amount] / [max_m_amount] cm3
"
	dat += "Glass amount: [src.g_amount] / [max_g_amount] cm3
"

	for(var/datum/design/D in matching_designs)
		if(disabled || !can_build(D))
			dat += "[D.name]"
		else
			dat += "[D.name]"

		if(ispath(D.build_path, /obj/item/stack))
			var/max_multiplier = min(50, D.materials["$metal"] ?round(m_amount/D.materials["$metal"]):INFINITY,D.materials["$glass"]?round(g_amount/D.materials["$glass"]):INFINITY)
			if (max_multiplier>10 && !disabled)
				dat += " x10"
			if (max_multiplier>25 && !disabled)
				dat += " x25"
			if(max_multiplier > 0 && !disabled)
				dat += " x[max_multiplier]"

		dat += "[get_design_cost(D)]
"

	dat += "
"
	return dat

/obj/machinery/autolathe/proc/can_build(var/datum/design/D)
	var/coeff = (ispath(D.build_path,/obj/item/stack) ? 1 : 2 ** prod_coeff)

	if(D.materials["$metal"] && (m_amount < (D.materials["$metal"] / coeff)))
		return 0
	if(D.materials["$glass"] && (g_amount < (D.materials["$glass"] / coeff)))
		return 0
	return 1

/obj/machinery/autolathe/proc/get_design_cost(var/datum/design/D)
	var/coeff = (ispath(D.build_path,/obj/item/stack) ? 1 : 2 ** prod_coeff)
	var/dat
	if(D.materials["$metal"])
		dat += "[D.materials["$metal"] / coeff] metal "
	if(D.materials["$glass"])
		dat += "[D.materials["$glass"] / coeff] glass"
	return dat

/obj/machinery/autolathe/proc/shock(mob/user, prb)
	if(stat & (BROKEN|NOPOWER))		// unpowered, no shock
		return 0
	if(!prob(prb))
		return 0
	var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread
	s.set_up(5, 1, src)
	s.start()
	if (electrocute_mob(user, get_area(src), src, 0.7))
		return 1
	else
		return 0

/obj/machinery/autolathe/proc/adjust_hacked(var/hack)
	hacked = hack

	if(hack)
		for(var/datum/design/D in files.possible_designs)
			if((D.build_type & 4) && ("hacked" in D.category))
				files.known_designs += D
	else
		for(var/datum/design/D in files.known_designs)
			if("hacked" in D.category)
				files.known_designs -= D