code:
/obj/machinery/computer/mechpad
	name = "orbital mech pad console"
	desc = "A computer designed to handle the calculations and routing required for sending and receiving mechs from orbit. Requires a link to a nearby Orbital Mech Pad to function."
	icon_screen = "mechpad"
	icon_keyboard = "teleport_key"
	circuit = /obj/item/circuitboard/computer/mechpad
	///ID of the mechpad, used for linking up
	var/id = "roboticsmining"
	///Selected mechpad in the console
	var/selected_id
	///Mechpads that it can send mechs through to other mechpads
	var/obj/machinery/mechpad/connected_mechpad
	///List of mechpads connected
	var/list/obj/machinery/mechpad/mechpads = list()
	///Maximum amount of pads connected at once
	var/maximum_pads = 3

/obj/machinery/computer/mechpad/Initialize(mapload)
	. = ..()
	if(mapload)
		connected_mechpad = connect_to_pad()
		connected_mechpad.connected_console = src
		connected_mechpad.id = id
		return INITIALIZE_HINT_LATELOAD
	else
		id ="handmade"

/obj/machinery/computer/mechpad/LateInitialize()
	for(var/obj/machinery/mechpad/pad in GLOB.mechpad_list)
		if(pad == connected_mechpad)
			continue
		if(pad.id != id)
			continue
		mechpads += pad
		LAZYADD(pad.consoles, src)
		if(mechpads.len > maximum_pads)
			break

/obj/machinery/computer/mechpad/Destroy()
	if(connected_mechpad)
		connected_mechpad.connected_console = null
		connected_mechpad = null
	for(var/obj/machinery/mechpad/mechpad in mechpads)
		LAZYREMOVE(mechpad.consoles, src)
	return ..()

///Tries to locate a pad in the cardinal directions, if it finds one it returns it
/obj/machinery/computer/mechpad/proc/connect_to_pad()
	if(connected_mechpad)
		return
	for(var/direction in GLOB.cardinals)
		connected_mechpad = locate(/obj/machinery/mechpad, get_step(src, direction))
		if(connected_mechpad)
			break
	return connected_mechpad

/obj/machinery/computer/mechpad/multitool_act(mob/living/user, obj/item/tool)
	if(!multitool_check_buffer(user, tool))
		return
	var/obj/item/multitool/multitool = tool
	if(istype(multitool.buffer, /obj/machinery/mechpad))
		var/obj/machinery/mechpad/buffered_console = multitool.buffer
		if(!(mechpads.len < maximum_pads))
			to_chat(user, span_warning("[src] cannot handle any more connections!"))
			return
		if(buffered_console == connected_mechpad)
			to_chat(user, span_warning("[src] cannot connect to its own mechpad!"))
		else if(!connected_mechpad && buffered_console == connect_to_pad())
			connected_mechpad = buffered_console
			connected_mechpad.connected_console = src
			connected_mechpad.id = id
			multitool.buffer = null
			to_chat(user, span_notice("You connect the console to the pad with data from the [multitool.name]'s buffer."))
		else
			mechpads += buffered_console
			LAZYADD(buffered_console.consoles, src)
			multitool.buffer = null
			to_chat(user, span_notice("You upload the data from the [multitool.name]'s buffer."))

/**
 * Tries to call the launch proc on the connected mechpad, returns if there is no connected mechpad or there is no mecha on the pad
 * Arguments:
 * * user - The user of the proc
 * * where - The mechpad that the connected mechpad will try to send a supply pod to
 */
/obj/machinery/computer/mechpad/proc/try_launch(mob/user, obj/machinery/mechpad/where)
	if(!connected_mechpad)
		to_chat(user, span_warning("[src] has no connected pad!"))
		return
	if(connected_mechpad.panel_open)
		to_chat(user, span_warning("[src]'s pad has its' panel open! It won't work!"))
		return
	if(!(locate(/obj/vehicle/sealed/mecha) in get_turf(connected_mechpad)))
		to_chat(user, span_warning("[src] detects no mecha on the pad!"))
		return
	connected_mechpad.launch(where)

///Checks if the pad of a certain number has been QDELETED, if yes returns FALSE, otherwise returns TRUE
/obj/machinery/computer/mechpad/proc/pad_exists(number)
	var/obj/machinery/mechpad/pad = mechpads[number]
	if(QDELETED(pad))
		return FALSE
	return TRUE

///Returns the pad of the value specified
/obj/machinery/computer/mechpad/proc/get_pad(number)
	var/obj/machinery/mechpad/pad = mechpads[number]
	return pad

/obj/machinery/computer/mechpad/ui_interact(mob/user, datum/tgui/ui)
	ui = SStgui.try_update_ui(user, src, ui)
	if(!ui)
		ui = new(user, src, "MechpadConsole", name)
		ui.open()

/obj/machinery/computer/mechpad/ui_data(mob/user)
	var/list/data = list()
	var/list/pad_list = list()
	for(var/i in 1 to LAZYLEN(mechpads))
		if(pad_exists(i))
			var/obj/machinery/mechpad/pad = get_pad(i)
			var/list/this_pad = list()
			this_pad["name"] = pad.display_name
			this_pad["id"] = i
			if(pad.machine_stat & NOPOWER)
				this_pad["inactive"] = TRUE
			pad_list += list(this_pad)
		else
			mechpads -= get_pad(i)
	data["mechpads"] = pad_list
	data["selected_id"] = selected_id
	data["connected_mechpad"] = !!connected_mechpad
	if(selected_id)
		var/obj/machinery/mechpad/current_pad = mechpads[selected_id]
		data["pad_name"] = current_pad.display_name
		data["selected_pad"] = current_pad
		if(QDELETED(current_pad) || (current_pad.machine_stat & NOPOWER))
			data["pad_active"] = FALSE
			return data
		data["pad_active"] = TRUE
	return data

/obj/machinery/computer/mechpad/ui_act(action, params)
	. = ..()
	if(.)
		return
	var/obj/machinery/mechpad/current_pad = mechpads[selected_id]
	switch(action)
		if("select_pad")
			selected_id = text2num(params["id"])
		if("rename")
			var/new_name = params["name"]
			if(!new_name)
				return
			current_pad.display_name = new_name
		if("remove")
			if(usr && tgui_alert(usr, "Are you sure?", "Unlink Orbital Pad", list("I'm Sure", "Abort")) == "I'm Sure")
				mechpads -= current_pad
				LAZYREMOVE(current_pad.consoles, src)
				selected_id = null
		if("launch")
			try_launch(usr, current_pad)
	. = TRUE


code:
/obj/machinery/computer/med_data//TODO:SANITY
	name = "medical records console"
	desc = "This can be used to check medical records."
	icon_screen = "medcomp"
	icon_keyboard = "med_key"
	req_one_access = list(ACCESS_MEDICAL, ACCESS_FORENSICS_LOCKERS)
	circuit = /obj/item/circuitboard/computer/med_data
	light_color = LIGHT_COLOR_BLUE
	var/rank = null
	var/screen = null
	var/datum/data/record/active1
	var/datum/data/record/active2
	var/temp = null
	var/printing = null
	//Sorting Variables
	var/sortBy = "name"
	var/order = 1 // -1 = Descending - 1 = Ascending

/obj/machinery/computer/med_data/syndie
	icon_keyboard = "syndie_key"

/obj/machinery/computer/med_data/ui_interact(mob/user)
	. = ..()
	if(isliving(user))
		playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, FALSE)
	var/dat
	if(temp)
		dat = text("<TT>[temp]</TT>

Clear Screen")
	else
		if(authenticated)
			switch(screen)
				if(1)
					dat += {"
Search Records

List Records

Virus Database

Medbot Tracking

Record Maintenance

{Log Out}

"}
				if(2)
					dat += {"

Records:

Name
ID
Fingerprints (F) | DNA UE (D)
Blood Type
Physical Status
Mental Status

"}

					if(!isnull(GLOB.data_core.general))
						for(var/datum/data/record/R in sort_record(GLOB.data_core.general, sortBy, order))
							var/blood_type = ""
							var/b_dna = ""
							for(var/datum/data/record/E in GLOB.data_core.medical)
								if((E.fields["name"] == R.fields["name"] && E.fields["id"] == R.fields["id"]))
									blood_type = E.fields["blood_type"]
									b_dna = E.fields["b_dna"]
							var/background

							if(R.fields["m_stat"] == "*Insane*" || R.fields["p_stat"] == "*Deceased*")
								background = "'background-color:#990000;'"
							else if(R.fields["p_stat"] == "*Unconscious*" || R.fields["m_stat"] == "*Unstable*")
								background = "'background-color:#CD6500;'"
							else if(R.fields["p_stat"] == "Physically Unfit" || R.fields["m_stat"] == "*Watch*")
								background = "'background-color:#3BB9FF;'"
							else
								background = "'background-color:#4F7529;'"

							dat += text("
[]	", background, R.fields["id"], R.fields["name"])
							dat += text("[]	", R.fields["id"])
							dat += text("F: []
D: []	", R.fields["fingerprint"], b_dna)
							dat += text("[]	", blood_type)
							dat += text("[]	", R.fields["p_stat"])
							dat += text("[]
", R.fields["m_stat"])
					dat += "

"
					dat += "
Back"
				if(3)
					dat += "Records Maintenance
\nBackup To Disk
\nUpload From Disk
\nDelete All Records
\n
\nBack"
				if(4)

					dat += "

Medical Record
"
					if(active1 in GLOB.data_core.general)
						if(istype(active1.fields["photo_front"], /obj/item/photo))
							var/obj/item/photo/P1 = active1.fields["photo_front"]
							user << browse_rsc(P1.picture.picture_image, "photo_front")
						if(istype(active1.fields["photo_side"], /obj/item/photo))
							var/obj/item/photo/P2 = active1.fields["photo_side"]
							user << browse_rsc(P2.picture.picture_image, "photo_side")
						dat += "
Name:	[active1.fields["name"]]	"
						dat += "	"
						dat += "
"
						dat += "
ID:	[active1.fields["id"]]
"
						dat += "
Gender:	 [active1.fields["gender"]]
"
						dat += "
Age:	 [active1.fields["age"]]
"
						dat += "
Species:	 [active1.fields["species"]]
"
						dat += "
Fingerprint:	 [active1.fields["fingerprint"]]
"
						dat += "
Physical Status:	 [active1.fields["p_stat"]]
"
						dat += "
Mental Status:	 [active1.fields["m_stat"]]
"
					else
						dat += "
General Record Lost!
"

					dat += "

Medical Data
"
					if(active2 in GLOB.data_core.medical)
						dat += "
Blood Type:	 [active2.fields["blood_type"]]
"
						dat += "
DNA:	 [active2.fields["b_dna"]]
"
						dat += "

Minor Disabilities:
 [active2.fields["mi_dis"]]
"
						dat += "
Details:	 [active2.fields["mi_dis_d"]]
"
						dat += "

Major Disabilities:
 [active2.fields["ma_dis"]]
"
						dat += "
Details:	 [active2.fields["ma_dis_d"]]
"
						dat += "

Current Diseases:
 [active2.fields["cdi"]]
" //(per disease info placed in log/comment section)
						dat += "
Details:	 [active2.fields["cdi_d"]]
"
						dat += "

Important Notes:
 [active2.fields["notes"]]
"
						dat += "

Notes Cont'd:
 [active2.fields["notes_d"]]
"

						dat += "

Comments/Log
"
						var/counter = 1
						while(active2.fields[text("com_[]", counter)])
							dat += "
[active2.fields[text("com_[]", counter)]]

Delete Entry
"
							counter++
						dat += "
Add Entry
"

						dat += "

Delete Record (Medical Only)
"
					else
						dat += "
Medical Record Lost!
"
						dat += "

New Record
"
					dat += "
Print Record
"
					dat += "
Back
"
					dat += "
"
				if(5)
					dat += "Virus Database"
					for(var/Dt in typesof(/datum/disease/))
						var/datum/disease/Dis = new Dt(0)
						if(istype(Dis, /datum/disease/advance))
							continue // TODO (tm): Add advance diseases to the virus database which no one uses.
						if(!Dis.desc)
							continue
						dat += "
[Dis.name]"
					dat += "
Back"
				if(6)
					dat += "Medical Robot Monitor"
					dat += "Back"
					dat += "
Medical Robots:"
					var/bdat = null
					for(var/mob/living/simple_animal/bot/medbot/M in GLOB.alive_mob_list)
						if(M.z != z)
							continue //only find medibots on the same z-level as the computer
						var/turf/bl = get_turf(M)
						if(bl) //if it can't find a turf for the medibot, then it probably shouldn't be showing up
							bdat += "[M.name] - \[[bl.x],[bl.y]\] - [M.on ? "Online" : "Offline"]
"
					if(!bdat)
						dat += "
None detected"
					else
						dat += "
[bdat]"

				else
		else
			dat += "{Log In}"
	var/datum/browser/popup = new(user, "med_rec", "Medical Records Console", 600, 400)
	popup.set_content(dat)
	popup.open()

/obj/machinery/computer/med_data/Topic(href, href_list)
	. = ..()
	if(.)
		return .
	if(!(active1 in GLOB.data_core.general))
		active1 = null
	if(!(active2 in GLOB.data_core.medical))
		active2 = null

	if(usr.contents.Find(src) || (in_range(src, usr) && isturf(loc)) || issilicon(usr) || isAdminGhostAI(usr))
		usr.set_machine(src)
		if(href_list["temp"])
			temp = null
		else if(href_list["logout"])
			authenticated = null
			screen = null
			active1 = null
			active2 = null
			playsound(src, 'sound/machines/terminal_off.ogg', 50, FALSE)
		else if(href_list["choice"])
			// SORTING!
			if(href_list["choice"] == "Sorting")
				// Reverse the order if clicked twice
				if(sortBy == href_list["sort"])
					if(order == 1)
						order = -1
					else
						order = 1
				else
				// New sorting order!
					sortBy = href_list["sort"]
					order = initial(order)
		else if(href_list["login"])
			var/obj/item/card/id/I
			if(isliving(usr))
				var/mob/living/L = usr
				I = L.get_idcard(TRUE)
			if(issilicon(usr))
				active1 = null
				active2 = null
				authenticated = 1
				rank = "AI"
				screen = 1
			else if(isAdminGhostAI(usr))
				active1 = null
				active2 = null
				authenticated = 1
				rank = "Central Command"
				screen = 1
			else if(istype(I) && check_access(I))
				active1 = null
				active2 = null
				authenticated = I.registered_name
				rank = I.assignment
				screen = 1
			else
				to_chat(usr, span_danger("Unauthorized access."))
			playsound(src, 'sound/machines/terminal_on.ogg', 50, FALSE)
		if(authenticated)
			if(href_list["screen"])
				screen = text2num(href_list["screen"])
				if(screen < 1)
					screen = 1

				active1 = null
				active2 = null

			else if(href_list["vir"])
				var/type = href_list["vir"]
				var/datum/disease/Dis = new type(0)
				var/AfS = ""
				for(var/mob/M in Dis.viable_mobtypes)
					AfS += " [initial(M.name)];"
				temp = {"Name: [Dis.name]

Number of stages: [Dis.max_stages]

Spread: [Dis.spread_text] Transmission

Possible Cure: [(Dis.cure_text||"none")]

Affected Lifeforms:[AfS]

Notes: [Dis.desc]

Severity: [Dis.severity]"}

			else if(href_list["del_all"])
				temp = "Are you sure you wish to delete all records?
\n\tYes
\n\tNo
"

			else if(href_list["del_all2"])
				investigate_log("[key_name(usr)] has deleted all medical records.", INVESTIGATE_RECORDS)
				GLOB.data_core.medical.Cut()
				temp = "All records deleted."

			else if(href_list["field"])
				var/a1 = active1
				var/a2 = active2
				switch(href_list["field"])
					if("fingerprint")
						if(active1)
							var/t1 = stripped_input("Please input fingerprint hash:", "Med. records", active1.fields["fingerprint"], null)
							if(!canUseMedicalRecordsConsole(usr, t1, a1))
								return
							active1.fields["fingerprint"] = t1
					if("gender")
						if(active1)
							if(active1.fields["gender"] == "Male")
								active1.fields["gender"] = "Female"
							else if(active1.fields["gender"] == "Female")
								active1.fields["gender"] = "Other"
							else
								active1.fields["gender"] = "Male"
					if("age")
						if(active1)
							var/t1 = input("Please input age:", "Med. records", active1.fields["age"], null)  as num
							if(!canUseMedicalRecordsConsole(usr, t1, a1))
								return
							active1.fields["age"] = t1
					if("species")
						if(active1)
							var/t1 = stripped_input("Please input species name", "Med. records", active1.fields["species"], null)
							if(!canUseMedicalRecordsConsole(usr, t1, a1))
								return
							active1.fields["species"] = t1
					if("mi_dis")
						if(active2)
							var/t1 = stripped_input("Please input minor disabilities list:", "Med. records", active2.fields["mi_dis"], null)
							if(!canUseMedicalRecordsConsole(usr, t1, null, a2))
								return
							active2.fields["mi_dis"] = t1
					if("mi_dis_d")
						if(active2)
							var/t1 = stripped_input("Please summarize minor dis.:", "Med. records", active2.fields["mi_dis_d"], null)
							if(!canUseMedicalRecordsConsole(usr, t1, null, a2))
								return
							active2.fields["mi_dis_d"] = t1
					if("ma_dis")
						if(active2)
							var/t1 = stripped_input("Please input major disabilities list:", "Med. records", active2.fields["ma_dis"], null)
							if(!canUseMedicalRecordsConsole(usr, t1, null, a2))
								return
							active2.fields["ma_dis"] = t1
					if("ma_dis_d")
						if(active2)
							var/t1 = stripped_input("Please summarize major dis.:", "Med. records", active2.fields["ma_dis_d"], null)
							if(!canUseMedicalRecordsConsole(usr, t1, null, a2))
								return
							active2.fields["ma_dis_d"] = t1
					if("alg")
						if(active2)
							var/t1 = stripped_input("Please state allergies:", "Med. records", active2.fields["alg"], null)
							if(!canUseMedicalRecordsConsole(usr, t1, null, a2))
								return
							active2.fields["alg"] = t1
					if("alg_d")
						if(active2)
							var/t1 = stripped_input("Please summarize allergies:", "Med. records", active2.fields["alg_d"], null)
							if(!canUseMedicalRecordsConsole(usr, t1, null, a2))
								return
							active2.fields["alg_d"] = t1
					if("cdi")
						if(active2)
							var/t1 = stripped_input("Please state diseases:", "Med. records", active2.fields["cdi"], null)
							if(!canUseMedicalRecordsConsole(usr, t1, null, a2))
								return
							active2.fields["cdi"] = t1
					if("cdi_d")
						if(active2)
							var/t1 = stripped_input("Please summarize diseases:", "Med. records", active2.fields["cdi_d"], null)
							if(!canUseMedicalRecordsConsole(usr, t1, null, a2))
								return
							active2.fields["cdi_d"] = t1
					if("notes")
						if(active2)
							var/t1 = stripped_input("Please summarize notes:", "Med. records", active2.fields["notes"], null)
							if(!canUseMedicalRecordsConsole(usr, t1, null, a2))
								return
							active2.fields["notes"] = t1
					if("p_stat")
						if(active1)
							temp = "Physical Condition:
\n\t*Deceased*
\n\t*Unconscious*
\n\tActive
\n\tPhysically Unfit
"
					if("m_stat")
						if(active1)
							temp = "Mental Condition:
\n\t*Insane*
\n\t*Unstable*
\n\t*Watch*
\n\tStable
"
					if("blood_type")
						if(active2)
							temp = "Blood Type:
\n\tA- A+
\n\tB- B+
\n\tAB- AB+
\n\tO- O+
"
					if("b_dna")
						if(active2)
							var/t1 = stripped_input("Please input DNA hash:", "Med. records", active2.fields["b_dna"], null)
							if(!canUseMedicalRecordsConsole(usr, t1, null, a2))
								return
							active2.fields["b_dna"] = t1
					if("show_photo_front")
						if(active1)
							if(active1.fields["photo_front"])
								if(istype(active1.fields["photo_front"], /obj/item/photo))
									var/obj/item/photo/P = active1.fields["photo_front"]
									P.show(usr)
					if("show_photo_side")
						if(active1)
							if(active1.fields["photo_side"])
								if(istype(active1.fields["photo_side"], /obj/item/photo))
									var/obj/item/photo/P = active1.fields["photo_side"]
									P.show(usr)
					else

			else if(href_list["p_stat"])
				if(active1)
					switch(href_list["p_stat"])
						if("deceased")
							active1.fields["p_stat"] = "*Deceased*"
						if("unconscious")
							active1.fields["p_stat"] = "*Unconscious*"
						if("active")
							active1.fields["p_stat"] = "Active"
						if("unfit")
							active1.fields["p_stat"] = "Physically Unfit"

			else if(href_list["m_stat"])
				if(active1)
					switch(href_list["m_stat"])
						if("insane")
							active1.fields["m_stat"] = "*Insane*"
						if("unstable")
							active1.fields["m_stat"] = "*Unstable*"
						if("watch")
							active1.fields["m_stat"] = "*Watch*"
						if("stable")
							active1.fields["m_stat"] = "Stable"

			else if(href_list["blood_type"])
				if(active2)
					switch(href_list["blood_type"])
						if("an")
							active2.fields["blood_type"] = "A-"
						if("bn")
							active2.fields["blood_type"] = "B-"
						if("abn")
							active2.fields["blood_type"] = "AB-"
						if("on")
							active2.fields["blood_type"] = "O-"
						if("ap")
							active2.fields["blood_type"] = "A+"
						if("bp")
							active2.fields["blood_type"] = "B+"
						if("abp")
							active2.fields["blood_type"] = "AB+"
						if("op")
							active2.fields["blood_type"] = "O+"

			else if(href_list["del_r"])
				if(active2)
					temp = "Are you sure you wish to delete the record (Medical Portion Only)?
\n\tYes
\n\tNo
"

			else if(href_list["del_r2"])
				investigate_log("[key_name(usr)] has deleted the medical records for [active1.fields["name"]].", INVESTIGATE_RECORDS)
				if(active2)
					qdel(active2)
					active2 = null

			else if(href_list["d_rec"])
				active1 = find_record("id", href_list["d_rec"], GLOB.data_core.general)
				if(active1)
					active2 = find_record("id", href_list["d_rec"], GLOB.data_core.medical)
				if(!active2)
					active1 = null
				screen = 4

			else if(href_list["new"])
				if((istype(active1, /datum/data/record) && !( istype(active2, /datum/data/record) )))
					var/datum/data/record/R = new /datum/data/record(  )
					R.fields["name"] = active1.fields["name"]
					R.fields["id"] = active1.fields["id"]
					R.name = text("Medical Record #[]", R.fields["id"])
					R.fields["blood_type"] = "Unknown"
					R.fields["b_dna"] = "Unknown"
					R.fields["mi_dis"] = "None"
					R.fields["mi_dis_d"] = "No minor disabilities have been diagnosed."
					R.fields["ma_dis"] = "None"
					R.fields["ma_dis_d"] = "No major disabilities have been diagnosed."
					R.fields["alg"] = "None"
					R.fields["alg_d"] = "No allergies have been detected in this patient."
					R.fields["cdi"] = "None"
					R.fields["cdi_d"] = "No diseases have been diagnosed at the moment."
					R.fields["notes"] = "No notes."
					GLOB.data_core.medical += R
					active2 = R
					screen = 4

			else if(href_list["add_c"])
				if(!(active2 in GLOB.data_core.medical))
					return
				var/a2 = active2
				var/t1 = stripped_multiline_input("Add Comment:", "Med. records", null, null)
				if(!canUseMedicalRecordsConsole(usr, t1, null, a2))
					return
				var/counter = 1
				while(active2.fields[text("com_[]", counter)])
					counter++
				/* SEPTIC EDIT REMOVAL
				active2.fields[text("com_[]", counter)] = text("Made by [] ([]) on [] [], []
[]", authenticated, rank, station_time_timestamp(), time2text(world.realtime, "MMM DD"), GLOB.year_integer+540, t1)
				*/
				//SEPTIC EDIT BEGIN
				active2.fields[text("com_[]", counter)] = text("Made by [] ([]) on [] [], []
[]", authenticated, rank, station_time_timestamp(), time2text(world.realtime, "MMM DD"), GLOB.current_station_year, t1)
				//SEPTIC EDIT END
			else if(href_list["del_c"])
				if((istype(active2, /datum/data/record) && active2.fields[text("com_[]", href_list["del_c"])]))
					active2.fields[text("com_[]", href_list["del_c"])] = "Deleted"

			else if(href_list["search"])
				var/t1 = stripped_input(usr, "Search String: (Name, DNA, or ID)", "Med. records")
				if(!canUseMedicalRecordsConsole(usr, t1))
					return
				active1 = null
				active2 = null
				t1 = lowertext(t1)
				for(var/datum/data/record/R in GLOB.data_core.medical)
					if((lowertext(R.fields["name"]) == t1 || t1 == lowertext(R.fields["id"]) || t1 == lowertext(R.fields["b_dna"])))
						active2 = R
					else
						//Foreach continue //goto(3229)
				if(!( active2 ))
					temp = text("Could not locate record [].", sanitize(t1))
				else
					for(var/datum/data/record/E in GLOB.data_core.general)
						if((E.fields["name"] == active2.fields["name"] || E.fields["id"] == active2.fields["id"]))
							active1 = E
						else
							//Foreach continue //goto(3334)
					screen = 4

			else if(href_list["print_p"])
				if(!( printing ))
					printing = 1
					GLOB.data_core.medicalPrintCount++
					playsound(loc, 'sound/items/poster_being_created.ogg', 100, TRUE)
					sleep(30)
					var/obj/item/paper/P = new /obj/item/paper( loc )
					P.info = "Medical Record - (MR-[GLOB.data_core.medicalPrintCount])
"
					if(active1 in GLOB.data_core.general)
						P.info += text("Name: [] ID: []
\nGender: []
\nAge: []
", active1.fields["name"], active1.fields["id"], active1.fields["gender"], active1.fields["age"])
						P.info += "\nSpecies: [active1.fields["species"]]
"
						P.info += text("\nFingerprint: []
\nPhysical Status: []
\nMental Status: []
", active1.fields["fingerprint"], active1.fields["p_stat"], active1.fields["m_stat"])
					else
						P.info += "General Record Lost!
"
					if(active2 in GLOB.data_core.medical)
						P.info += text("
\nMedical Data
\nBlood Type: []
\nDNA: []
\n
\nMinor Disabilities: []
\nDetails: []
\n
\nMajor Disabilities: []
\nDetails: []
\n
\nAllergies: []
\nDetails: []
\n
\nCurrent Diseases: [] (per disease info placed in log/comment section)
\nDetails: []
\n
\nImportant Notes:
\n\t[]
\n
\nComments/Log
", active2.fields["blood_type"], active2.fields["b_dna"], active2.fields["mi_dis"], active2.fields["mi_dis_d"], active2.fields["ma_dis"], active2.fields["ma_dis_d"], active2.fields["alg"], active2.fields["alg_d"], active2.fields["cdi"], active2.fields["cdi_d"], active2.fields["notes"])
						var/counter = 1
						while(active2.fields[text("com_[]", counter)])
							P.info += text("[]
", active2.fields[text("com_[]", counter)])
							counter++
						P.name = text("MR-[] '[]'", GLOB.data_core.medicalPrintCount, active1.fields["name"])
					else
						P.info += "Medical Record Lost!
"
						P.name = text("MR-[] '[]'", GLOB.data_core.medicalPrintCount, "Record Lost")
					P.info += "</TT>"
					P.update_appearance()
					printing = null

	add_fingerprint(usr)
	updateUsrDialog()
	return

/obj/machinery/computer/med_data/emp_act(severity)
	. = ..()
	if(!(machine_stat & (BROKEN|NOPOWER)) && !(. & EMP_PROTECT_SELF))
		for(var/datum/data/record/R in GLOB.data_core.medical)
			if(prob(10/severity))
				switch(rand(1,6))
					if(1)
						if(prob(10))
							R.fields["name"] = random_unique_lizard_name(R.fields["gender"],1)
						else
							R.fields["name"] = random_unique_name(R.fields["gender"],1)
					if(2)
						R.fields["gender"] = pick("Male", "Female", "Other")
					if(3)
						R.fields["age"] = rand(AGE_MIN, AGE_MAX)
					if(4)
						R.fields["blood_type"] = random_blood_type()
					if(5)
						R.fields["p_stat"] = pick("*Unconscious*", "Active", "Physically Unfit")
					if(6)
						R.fields["m_stat"] = pick("*Insane*", "*Unstable*", "*Watch*", "Stable")
				continue

			else if(prob(1))
				qdel(R)
				continue

/obj/machinery/computer/med_data/proc/canUseMedicalRecordsConsole(mob/user, message = 1, record1, record2)
	if(user && message && authenticated)
		if(user.canUseTopic(src, !issilicon(user)))
			if(!record1 || record1 == active1)
				if(!record2 || record2 == active2)
					return TRUE
	return FALSE

/obj/machinery/computer/med_data/laptop
	name = "medical laptop"
	desc = "A cheap Nanotrasen medical laptop, it functions as a medical records computer. It's bolted to the table."
	icon_state = "laptop"
	icon_screen = "medlaptop"
	icon_keyboard = "laptop_key"
	pass_flags = PASSTABLE

repo_name: AnalWerewolf/sundown

license: mit


code:
/obj/machinery/computer/pod
	name = "mass driver launch control"
	desc = "A combined blastdoor and mass driver control unit."
	processing_flags = START_PROCESSING_MANUALLY
	/// Connected mass driver
	var/obj/machinery/mass_driver/connected = null
	/// ID of the launch control
	var/id = 1
	/// If the launch timer counts down
	var/timing = FALSE
	/// Time before auto launch
	var/time = 30
	/// Range in which we search for a mass drivers and poddoors nearby
	var/range = 4
	/// Countdown timer for the mass driver's delayed launch functionality.
	COOLDOWN_DECLARE(massdriver_countdown)

/obj/machinery/computer/pod/Initialize(mapload)
	. = ..()
	for(var/obj/machinery/mass_driver/M in range(range, src))
		if(M.id == id)
			connected = M
			break

/obj/machinery/computer/pod/process(delta_time)
	if(COOLDOWN_FINISHED(src, massdriver_countdown))
		timing = FALSE
		// alarm() sleeps, so we want to end processing first and can't rely on return PROCESS_KILL
		end_processing()
		alarm()

/**
 * Initiates launching sequence by checking if all components are functional, opening poddoors, firing mass drivers and then closing poddoors
 */
/obj/machinery/computer/pod/proc/alarm()
	if(machine_stat & (NOPOWER|BROKEN))
		return

	if(!connected)
		say("Cannot locate mass driver connector. Cancelling firing sequence!")
		return

	for(var/obj/machinery/door/poddoor/M in range(range, src))
		if(M.id == id)
			M.open()

	sleep(20)
	for(var/obj/machinery/mass_driver/M in range(range, src))
		if(M.id == id)
			M.power = connected.power
			M.drive()

	sleep(50)
	for(var/obj/machinery/door/poddoor/M in range(range, src))
		if(M.id == id)
			M.close()

/obj/machinery/computer/pod/ui_interact(mob/user, datum/tgui/ui)
	ui = SStgui.try_update_ui(user, src, ui)
	if(!ui)
		ui = new(user, src, "MassDriverControl", name)
		ui.open()

/obj/machinery/computer/pod/ui_data(mob/user)
	var/list/data = list()
	// If the cooldown has finished, just display the time. If the cooldown hasn't finished, display the cooldown.
	var/display_time = COOLDOWN_FINISHED(src, massdriver_countdown) ? time : COOLDOWN_TIMELEFT(src, massdriver_countdown) * 0.1
	data["connected"] = connected ? TRUE : FALSE
	data["seconds"] = round(display_time % 60)
	data["minutes"] = round((display_time - data["seconds"]) / 60)
	data["timing"] = timing
	data["power"] = connected ? connected.power : 0.25
	data["poddoor"] = FALSE
	for(var/obj/machinery/door/poddoor/door in range(range, src))
		if(door.id == id)
			data["poddoor"] = TRUE
			break
	return data

/obj/machinery/computer/pod/ui_act(action, list/params)
	. = ..()
	if(.)
		return
	if(!allowed(usr))
		to_chat(usr, span_warning("Access denied."))
		return

	switch(action)
		if("set_power")
			if(!connected)
				return
			var/value = text2num(params["power"])
			if(!value)
				return
			value = clamp(value, 0.25, 16)
			connected.power = value
			return TRUE
		if("launch")
			alarm()
			return TRUE
		if("time")
			timing = !timing
			if(timing)
				COOLDOWN_START(src, massdriver_countdown, time SECONDS)
				begin_processing()
			else
				time = COOLDOWN_TIMELEFT(src, massdriver_countdown) * 0.1
				COOLDOWN_RESET(src, massdriver_countdown)
				end_processing()
			return TRUE
		if("input")
			var/value = text2num(params["adjust"])
			if(!value)
				return
			value = round(time + value)
			time = clamp(value, 0, 120)
			return TRUE
		if("door")
			for(var/obj/machinery/door/poddoor/M in range(range, src))
				if(M.id == id)
					if(M.density)
						M.open()
					else
						M.close()
			return TRUE
		if("driver_test")
			for(var/obj/machinery/mass_driver/M in range(range, src))
				if(M.id == id)
					M.power = connected?.power
					M.drive()
			return TRUE

/obj/machinery/computer/pod/old
	name = "\improper DoorMex control console"
	icon_state = "oldcomp"
	icon_screen = "library"
	icon_keyboard = null

/obj/machinery/computer/pod/old/mass_driver_controller
	name = "\improper Mass Driver Controller"
	icon = 'icons/obj/airlock_machines.dmi'
	icon_state = "airlock_control_standby"
	icon_keyboard = null
	density = FALSE

/obj/machinery/computer/pod/old/mass_driver_controller/ordnancedriver
	id = MASSDRIVER_ORDNANCE

//for maps where pod doors are outside of the standard 4 tile controller detection range (ie Pubbystation)
/obj/machinery/computer/pod/old/mass_driver_controller/ordnancedriver/longrange
	range = 6

/obj/machinery/computer/pod/old/mass_driver_controller/chapelgun
	id = MASSDRIVER_CHAPEL

/obj/machinery/computer/pod/old/mass_driver_controller/trash
	id = MASSDRIVER_DISPOSALS

/obj/machinery/computer/pod/old/mass_driver_controller/shack
	id = MASSDRIVER_SHACK

/obj/machinery/computer/pod/old/syndicate
	name = "\improper ProComp Executive IIc"
	desc = "The Syndicate operate on a tight budget. Operates external airlocks."
	req_access = list(ACCESS_SYNDICATE)

/obj/machinery/computer/pod/old/swf
	name = "\improper Magix System IV"
	desc = "An arcane artifact that holds much magic. Running E-Knock 2.2: Sorcerer's Edition."


code:
/obj/machinery/computer/prisoner
	var/obj/item/card/id/advanced/prisoner/contained_id

/obj/machinery/computer/prisoner/Destroy()
	if(contained_id)
		contained_id.forceMove(get_turf(src))
	return ..()

/obj/machinery/computer/prisoner/examine(mob/user)
	. = ..()
	if(contained_id)
		. += span_notice("<b>Alt-click</b> to eject the ID card.")

/obj/machinery/computer/prisoner/AltClick(mob/user)
	id_eject(user)
	return ..()

/obj/machinery/computer/prisoner/proc/id_insert(mob/user, obj/item/card/id/advanced/prisoner/P)
	if(istype(P))
		if(contained_id)
			to_chat(user, span_warning("There's already an ID card in the console!"))
			return
		if(!user.transferItemToLoc(P, src))
			return
		contained_id = P
		user.visible_message(span_notice("[user] inserts an ID card into the console."), \
							span_notice("You insert the ID card into the console."))
		playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, FALSE)
		updateUsrDialog()

/obj/machinery/computer/prisoner/proc/id_eject(mob/user)
	if(!contained_id)
		to_chat(user, span_warning("There's no ID card in the console!"))
		return
	else
		contained_id.forceMove(drop_location())
		if(!issilicon(user) && Adjacent(user))
			user.put_in_hands(contained_id)
		contained_id = null
		user.visible_message(span_notice("[user] gets an ID card from the console."), \
							span_notice("You get the ID card from the console."))
		playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, FALSE)
		updateUsrDialog()

/obj/machinery/computer/prisoner/attackby(obj/item/I, mob/user)
	if(istype(I, /obj/item/card/id/advanced/prisoner))
		id_insert(user, I)
	else
		return ..()


code:
//computer that handle the points and teleports the prisoner
/obj/machinery/computer/prisoner/gulag_teleporter_computer
	name = "labor camp teleporter console"
	desc = "Used to send criminals to the Labor Camp."
	icon_screen = "explosive"
	icon_keyboard = "security_key"
	req_access = list(ACCESS_ARMORY)
	circuit = /obj/item/circuitboard/computer/gulag_teleporter_console
	light_color = COLOR_SOFT_RED

	var/default_goal = 200
	var/obj/machinery/gulag_teleporter/teleporter = null
	var/obj/structure/gulag_beacon/beacon = null
	var/mob/living/carbon/human/prisoner = null
	var/datum/data/record/temporary_record = null

/obj/machinery/computer/prisoner/gulag_teleporter_computer/Initialize(mapload)
	. = ..()
	scan_machinery()

/obj/machinery/computer/prisoner/gulag_teleporter_computer/ui_interact(mob/user, datum/tgui/ui)
	ui = SStgui.try_update_ui(user, src, ui)
	if(!ui)
		ui = new(user, src, "GulagTeleporterConsole", name)
		ui.open()

/obj/machinery/computer/prisoner/gulag_teleporter_computer/ui_data(mob/user)
	var/list/data = list()

	var/list/prisoner_list = list()
	var/can_teleport = FALSE

	if(teleporter && (teleporter.occupant && ishuman(teleporter.occupant)))
		prisoner = teleporter.occupant
		prisoner_list["name"] = prisoner.real_name
		if(contained_id)
			can_teleport = TRUE
		if(!isnull(GLOB.data_core.general))
			for(var/r in GLOB.data_core.security)
				var/datum/data/record/R = r
				if(R.fields["name"] == prisoner_list["name"])
					temporary_record = R
					prisoner_list["crimstat"] = temporary_record.fields["criminal"]

	data["prisoner"] = prisoner_list

	if(teleporter)
		data["teleporter"] = teleporter
		data["teleporter_location"] = "([teleporter.x], [teleporter.y], [teleporter.z])"
		data["teleporter_lock"] = teleporter.locked
		data["teleporter_state_open"] = teleporter.state_open
	else
		data["teleporter"] = null
	if(beacon)
		data["beacon"] = beacon
		data["beacon_location"] = "([beacon.x], [beacon.y], [beacon.z])"
	else
		data["beacon"] = null
	if(contained_id)
		data["id"] = contained_id
		data["id_name"] = contained_id.registered_name
		data["goal"] = contained_id.goal
	else
		data["id"] = null
	data["can_teleport"] = can_teleport

	return data

/obj/machinery/computer/prisoner/gulag_teleporter_computer/ui_act(action, list/params)
	. = ..()
	if(.)
		return
	if(isliving(usr))
		playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, FALSE)
	if(!allowed(usr))
		to_chat(usr, span_warning("Access denied."))
		return
	switch(action)
		if("scan_teleporter")
			teleporter = findteleporter()
			return TRUE
		if("scan_beacon")
			beacon = findbeacon()
			return TRUE
		if("handle_id")
			if(contained_id)
				id_eject(usr)
			else
				id_insert(usr)
			return TRUE
		if("set_goal")
			var/new_goal = text2num(params["value"])
			if(!isnum(new_goal))
				return
			if(!new_goal)
				new_goal = default_goal
			contained_id.goal = clamp(new_goal, 0, 1000) //maximum 1000 points
			return TRUE
		if("toggle_open")
			if(teleporter.locked)
				to_chat(usr, span_alert("The teleporter must be unlocked first."))
				return
			teleporter.toggle_open()
			return TRUE
		if("teleporter_lock")
			if(teleporter.state_open)
				to_chat(usr, span_alert("The teleporter must be closed first."))
				return
			teleporter.locked = !teleporter.locked
			return TRUE
		if("teleport")
			if(!teleporter || !beacon)
				return
			addtimer(CALLBACK(src, .proc/teleport, usr), 5)
			return TRUE

/obj/machinery/computer/prisoner/gulag_teleporter_computer/proc/scan_machinery()
	teleporter = findteleporter()
	beacon = findbeacon()

/obj/machinery/computer/prisoner/gulag_teleporter_computer/proc/findteleporter()
	var/obj/machinery/gulag_teleporter/teleporterf = null

	for(var/direction in GLOB.cardinals)
		teleporterf = locate(/obj/machinery/gulag_teleporter, get_step(src, direction))
		if(teleporterf?.is_operational)
			return teleporterf

/obj/machinery/computer/prisoner/gulag_teleporter_computer/proc/findbeacon()
	return locate(/obj/structure/gulag_beacon)

/obj/machinery/computer/prisoner/gulag_teleporter_computer/proc/teleport(mob/user)
	if(!contained_id) //incase the ID was removed after the transfer timer was set.
		say("Warning: Unable to transfer prisoner without a valid Prisoner ID inserted!")
		return
	var/id_goal_not_set
	if(!contained_id.goal)
		id_goal_not_set = TRUE
		contained_id.goal = default_goal
		say("[contained_id]'s ID card goal defaulting to [contained_id.goal] points.")
	log_game("[key_name(user)] teleported [key_name(prisoner)] to the Labor Camp [COORD(beacon)] for [id_goal_not_set ? "default goal of ":""][contained_id.goal] points.")
	teleporter.handle_prisoner(contained_id, temporary_record)
	playsound(src, 'sound/weapons/emitter.ogg', 50, TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
	prisoner.forceMove(get_turf(beacon))
	prisoner.Paralyze(40) // small travel dizziness
	to_chat(prisoner, span_warning("The teleportation makes you a little dizzy."))
	new /obj/effect/particle_effect/sparks(get_turf(prisoner))
	playsound(src, "sparks", 50, TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
	if(teleporter.locked)
		teleporter.locked = FALSE
	teleporter.toggle_open()
	contained_id = null
	temporary_record = null


code:
/obj/machinery/computer/prisoner/management
	name = "prisoner management console"
	desc = "Used to manage tracking implants placed inside criminals."
	icon_screen = "explosive"
	icon_keyboard = "security_key"
	req_access = list(ACCESS_BRIG)
	light_color = COLOR_SOFT_RED
	var/id = 0
	var/temp = null
	var/status = 0
	var/timeleft = 60
	var/stop = 0
	var/screen = 0 // 0 - No Access Denied, 1 - Access allowed
	circuit = /obj/item/circuitboard/computer/prisoner

/obj/machinery/computer/prisoner/management/ui_interact(mob/user)
	. = ..()
	if(isliving(user))
		playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, FALSE)
	var/dat = ""
	if(screen == 0)
		dat += "
{Log In}"
	else if(screen == 1)
		dat += "
Prisoner ID Management
"
		if(contained_id)
			dat += text("[contained_id]
")
			dat += text("Collected Points: [contained_id.points]. Reset.
")
			dat += text("Card goal: [contained_id.goal].  Set
")
			dat += text("Space Law recommends quotas of 100 points per minute they would normally serve in the brig.
")
		else
			dat += text("Insert Prisoner ID.
")
		dat += "
Prisoner Implant Management
"
		dat += "
Chemical Implants
"
		var/turf/Tr = null
		for(var/obj/item/implant/chem/C in GLOB.tracked_chem_implants)
			Tr = get_turf(C)
			if((Tr) && (Tr.z != src.z))
				continue//Out of range
			if(!C.imp_in)
				continue
			dat += "ID: [C.imp_in.name] | Remaining Units: [C.reagents.total_volume]
"
			dat += "| Inject: "
			dat += "((1))"
			dat += "((5))"
			dat += "((10))
"
			dat += "********************************
"
		dat += "
Tracking Implants
"
		for(var/obj/item/implant/tracking/T in GLOB.tracked_implants)
			if(!isliving(T.imp_in))
				continue
			Tr = get_turf(T)
			if((Tr) && (Tr.z != src.z))
				continue//Out of range

			var/loc_display = "Unknown"
			var/mob/living/M = T.imp_in
			if(is_station_level(Tr.z) && !isspaceturf(M.loc))
				var/turf/mob_loc = get_turf(M)
				loc_display = mob_loc.loc

			dat += "ID: [T.imp_in.name] | Location: [loc_display]
"
			dat += "(Message Holder) |
"
			dat += "********************************
"
		dat += "
{Log Out}"
	var/datum/browser/popup = new(user, "computer", "Prisoner Management Console", 400, 500)
	popup.set_content(dat)
	popup.open()
	return

/obj/machinery/computer/prisoner/management/attackby(obj/item/I, mob/user, params)
	if(istype(I, /obj/item/card/id))
		if(screen)
			id_insert(user)
		else
			to_chat(user, span_danger("Unauthorized access."))
	else
		return ..()

/obj/machinery/computer/prisoner/management/process()
	if(!..())
		src.updateDialog()
	return

/obj/machinery/computer/prisoner/management/Topic(href, href_list)
	if(..())
		return
	if(usr.contents.Find(src) || (in_range(src, usr) && isturf(loc)) || issilicon(usr))
		usr.set_machine(src)

		if(href_list["id"])
			if(href_list["id"] =="insert" && !contained_id)
				id_insert(usr)
			else if(contained_id)
				switch(href_list["id"])
					if("eject")
						id_eject(usr)
					if("reset")
						contained_id.points = 0
					if("setgoal")
						var/num = round(input(usr, "Choose prisoner's goal:", "Input an Integer", null) as num|null)
						if(num >= 0)
							num = min(num,1000) //Cap the quota to the equivilent of 10 minutes.
							contained_id.goal = num
		else if(href_list["inject1"])
			var/obj/item/implant/I = locate(href_list["inject1"]) in GLOB.tracked_chem_implants
			if(I && istype(I))
				I.activate(1)
		else if(href_list["inject5"])
			var/obj/item/implant/I = locate(href_list["inject5"]) in GLOB.tracked_chem_implants
			if(I && istype(I))
				I.activate(5)
		else if(href_list["inject10"])
			var/obj/item/implant/I = locate(href_list["inject10"]) in GLOB.tracked_chem_implants
			if(I && istype(I))
				I.activate(10)

		else if(href_list["lock"])
			if(allowed(usr))
				screen = !screen
				playsound(src, 'sound/machines/terminal_on.ogg', 50, FALSE)
			else
				to_chat(usr, span_danger("Unauthorized access."))

		else if(href_list["warn"])
			var/warning = stripped_input(usr, "Message:", "Enter your message here!", "", MAX_MESSAGE_LEN)
			if(!warning)
				return
			var/obj/item/implant/I = locate(href_list["warn"]) in GLOB.tracked_implants
			if(I && istype(I) && I.imp_in)
				var/mob/living/R = I.imp_in
				to_chat(R, span_hear("You hear a voice in your head saying: '[warning]'"))
				log_directed_talk(usr, R, warning, LOG_SAY, "implant message")

		src.add_fingerprint(usr)
	src.updateUsrDialog()
	return


code:
/obj/machinery/computer/robotics
	name = "robotics control console"
	desc = "Used to remotely lockdown or detonate linked Cyborgs and Drones."
	icon_screen = "robot"
	icon_keyboard = "rd_key"
	req_access = list(ACCESS_ROBOTICS)
	circuit = /obj/item/circuitboard/computer/robotics
	light_color = LIGHT_COLOR_PINK

/obj/machinery/computer/robotics/proc/can_control(mob/user, mob/living/silicon/robot/R)
	. = FALSE
	if(!istype(R))
		return
	if(isAI(user))
		if(R.connected_ai != user)
			return
	if(iscyborg(user))
		if(R != user)
			return
	if(R.scrambledcodes)
		return
	return TRUE

/obj/machinery/computer/robotics/ui_interact(mob/user, datum/tgui/ui)
	ui = SStgui.try_update_ui(user, src, ui)
	if(!ui)
		ui = new(user, src, "RoboticsControlConsole", name)
		ui.open()

/obj/machinery/computer/robotics/ui_data(mob/user)
	var/list/data = list()

	data["can_hack"] = FALSE
	if(issilicon(user))
		var/mob/living/silicon/S = user
		if(S.hack_software)
			data["can_hack"] = TRUE
	else if(isAdminGhostAI(user))
		data["can_hack"] = TRUE

	data["cyborgs"] = list()
	for(var/mob/living/silicon/robot/R in GLOB.silicon_mobs)
		if(!can_control(user, R))
			continue
		if(z != (get_turf(R)).z)
			continue
		var/list/cyborg_data = list(
			name = R.name,
			locked_down = R.lockcharge,
			status = R.stat,
			charge = R.cell ? round(R.cell.percent()) : null,
			module = R.model ? "[R.model.name] Model" : "No Model Detected",
			synchronization = R.connected_ai,
			emagged =  R.emagged,
			ref = REF(R)
		)
		data["cyborgs"] += list(cyborg_data)

	data["drones"] = list()
	for(var/mob/living/simple_animal/drone/D in GLOB.drones_list)
		if(D.hacked)
			continue
		if(z != (get_turf(D)).z)
			continue
		var/list/drone_data = list(
			name = D.name,
			status = D.stat,
			ref = REF(D)
		)
		data["drones"] += list(drone_data)

	return data

/obj/machinery/computer/robotics/ui_act(action, params)
	. = ..()
	if(.)
		return

	switch(action)
		if("killbot")
			if(allowed(usr))
				var/mob/living/silicon/robot/R = locate(params["ref"]) in GLOB.silicon_mobs
				if(can_control(usr, R) && !..())
					R.self_destruct(usr)
			else
				to_chat(usr, span_danger("Access Denied."))
		if("stopbot")
			if(allowed(usr))
				var/mob/living/silicon/robot/R = locate(params["ref"]) in GLOB.silicon_mobs
				if(can_control(usr, R) && !..())
					message_admins(span_notice("[ADMIN_LOOKUPFLW(usr)] [!R.lockcharge ? "locked down" : "released"] [ADMIN_LOOKUPFLW(R)]!"))
					log_game("[key_name(usr)] [!R.lockcharge ? "locked down" : "released"] [key_name(R)]!")
					log_combat(usr, R, "[!R.lockcharge ? "locked down" : "released"] cyborg")
					R.SetLockdown(!R.lockcharge)
					to_chat(R, !R.lockcharge ? span_notice("Your lockdown has been lifted!") : span_alert("You have been locked down!"))
					if(R.connected_ai)
						to_chat(R.connected_ai, "[!R.lockcharge ? span_notice("NOTICE - Cyborg lockdown lifted") : span_alert("ALERT - Cyborg lockdown detected")]: [R.name]
")
			else
				to_chat(usr, span_danger("Access Denied."))
		if("magbot")
			var/mob/living/silicon/S = usr
			if((istype(S) && S.hack_software) || isAdminGhostAI(usr))
				var/mob/living/silicon/robot/R = locate(params["ref"]) in GLOB.silicon_mobs
				if(istype(R) && !R.emagged && (R.connected_ai == usr || isAdminGhostAI(usr)) && !R.scrambledcodes && can_control(usr, R))
					log_game("[key_name(usr)] emagged [key_name(R)] using robotic console!")
					message_admins("[ADMIN_LOOKUPFLW(usr)] emagged cyborg [key_name_admin(R)] using robotic console!")
					R.SetEmagged(TRUE)
					R.logevent("WARN: root privleges granted to PID [num2hex(rand(1,65535), -1)][num2hex(rand(1,65535), -1)].") //random eight digit hex value. Two are used because rand(1,4294967295) throws an error
		if("killdrone")
			if(allowed(usr))
				var/mob/living/simple_animal/drone/D = locate(params["ref"]) in GLOB.mob_list
				if(D.hacked)
					to_chat(usr, span_danger("ERROR: [D] is not responding to external commands."))
				else
					var/turf/T = get_turf(D)
					message_admins("[ADMIN_LOOKUPFLW(usr)] detonated [key_name_admin(D)] at [ADMIN_VERBOSEJMP(T)]!")
					log_game("[key_name(usr)] detonated [key_name(D)]!")
					var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread
					s.set_up(3, TRUE, D)
					s.start()
					D.visible_message(span_danger("\the [D] self-destructs!"))
					D.gib()


code:
/obj/machinery/computer/secure_data//TODO:SANITY
	name = "security records console"
	desc = "Used to view and edit personnel's security records."
	icon_screen = "security"
	icon_keyboard = "security_key"
	req_one_access = list(ACCESS_SECURITY, ACCESS_FORENSICS_LOCKERS)
	circuit = /obj/item/circuitboard/computer/secure_data
	light_color = COLOR_SOFT_RED
	var/rank = null
	var/screen = null
	var/datum/data/record/active1 = null
	var/datum/data/record/active2 = null
	var/temp = null
	var/printing = null
	var/can_change_id = 0
	var/list/Perp
	var/tempname = null
	//Sorting Variables
	var/sortBy = "name"
	var/order = 1 // -1 = Descending - 1 = Ascending

/obj/machinery/computer/secure_data/Initialize(mapload, obj/item/circuitboard/C)
	. = ..()
	AddComponent(/datum/component/usb_port, list(
		/obj/item/circuit_component/arrest_console_data,
		/obj/item/circuit_component/arrest_console_arrest,
	))

#define COMP_STATE_ARREST "*Arrest*"
#define COMP_STATE_PRISONER "Incarcerated"
#define COMP_STATE_PAROL "Paroled"
#define COMP_STATE_DISCHARGED "Discharged"
#define COMP_STATE_NONE "None"
#define COMP_SECURITY_ARREST_AMOUNT_TO_FLAG 10

/obj/item/circuit_component/arrest_console_data
	display_name = "Security Records Data"
	desc = "Outputs the security records data, where it can then be filtered with a Select Query component"
	circuit_flags = CIRCUIT_FLAG_INPUT_SIGNAL|CIRCUIT_FLAG_OUTPUT_SIGNAL

	/// The records retrieved
	var/datum/port/output/records

	/// Sends a signal on failure
	var/datum/port/output/on_fail

	var/obj/machinery/computer/secure_data/attached_console

/obj/item/circuit_component/arrest_console_data/populate_ports()
	records = add_output_port("Security Records", PORT_TYPE_TABLE)
	on_fail = add_output_port("Failed", PORT_TYPE_SIGNAL)

/obj/item/circuit_component/arrest_console_data/register_usb_parent(atom/movable/shell)
	. = ..()
	if(istype(shell, /obj/machinery/computer/secure_data))
		attached_console = shell

/obj/item/circuit_component/arrest_console_data/unregister_usb_parent(atom/movable/shell)
	attached_console = null
	return ..()

/obj/item/circuit_component/arrest_console_data/get_ui_notices()
	. = ..()
	. += create_table_notices(list(
		"name",
		"id",
		"rank",
		"arrest_status",
		"gender",
		"age",
		"species",
		"fingerprint",
	))

/obj/item/circuit_component/arrest_console_data/input_received(datum/port/input/port)

	if(!attached_console || !attached_console.authenticated)
		on_fail.set_output(COMPONENT_SIGNAL)
		return

	if(isnull(GLOB.data_core.general))
		on_fail.set_output(COMPONENT_SIGNAL)
		return

	var/list/new_table = list()
	for(var/datum/data/record/player_record as anything in GLOB.data_core.general)
		var/list/entry = list()
		var/datum/data/record/player_security_record = find_record("id", player_record.fields["id"], GLOB.data_core.security)
		if(player_security_record)
			entry["arrest_status"] = player_security_record.fields["criminal"]
			entry["security_record"] = player_security_record
		entry["name"] = player_record.fields["name"]
		entry["id"] = player_record.fields["id"]
		entry["rank"] = player_record.fields["rank"]
		entry["gender"] = player_record.fields["gender"]
		entry["age"] = player_record.fields["age"]
		entry["species"] = player_record.fields["species"]
		entry["fingerprint"] = player_record.fields["fingerprint"]

		new_table += list(entry)

	records.set_output(new_table)

/obj/item/circuit_component/arrest_console_arrest
	display_name = "Security Records Set Status"
	desc = "Receives a table to use to set people's arrest status. Table should be from the security records data component. If New Status port isn't set, the status will be decided by the options."
	circuit_flags = CIRCUIT_FLAG_INPUT_SIGNAL|CIRCUIT_FLAG_OUTPUT_SIGNAL

	/// The targets to set the status of.
	var/datum/port/input/targets

	/// Sets the new status of the targets.
	var/datum/port/input/option/new_status

	/// Returns the new status set once the setting is complete. Good for locating errors.
	var/datum/port/output/new_status_set

	/// Sends a signal on failure
	var/datum/port/output/on_fail

	var/obj/machinery/computer/secure_data/attached_console

/obj/item/circuit_component/arrest_console_arrest/register_usb_parent(atom/movable/shell)
	. = ..()
	if(istype(shell, /obj/machinery/computer/secure_data))
		attached_console = shell

/obj/item/circuit_component/arrest_console_arrest/unregister_usb_parent(atom/movable/shell)
	attached_console = null
	return ..()

/obj/item/circuit_component/arrest_console_arrest/populate_options()
	var/static/list/component_options = list(
		COMP_STATE_ARREST,
		COMP_STATE_PRISONER,
		COMP_STATE_PAROL,
		COMP_STATE_DISCHARGED,
		COMP_STATE_NONE,
	)
	new_status = add_option_port("Arrest Options", component_options)

/obj/item/circuit_component/arrest_console_arrest/populate_ports()
	targets = add_input_port("Targets", PORT_TYPE_TABLE)
	new_status_set = add_output_port("Set Status", PORT_TYPE_STRING)
	on_fail = add_output_port("Failed", PORT_TYPE_SIGNAL)

/obj/item/circuit_component/arrest_console_arrest/input_received(datum/port/input/port)

	if(!attached_console || !attached_console.authenticated)
		on_fail.set_output(COMPONENT_SIGNAL)
		return

	var/status_to_set = new_status.value

	new_status_set.set_output(status_to_set)
	var/list/target_table = targets.value
	if(!target_table)
		on_fail.set_output(COMPONENT_SIGNAL)
		return

	var/successful_set = 0
	var/list/names_of_entries = list()
	for(var/list/target in target_table)
		var/datum/data/record/sec_record = target["security_record"]
		if(!sec_record)
			continue

		if(sec_record.fields["criminal"] != status_to_set)
			successful_set++
			names_of_entries += target["name"]
		sec_record.fields["criminal"] = status_to_set

	if(successful_set > 0)
		investigate_log("[names_of_entries.Join(", ")] have been set to [status_to_set] by [parent.get_creator()].", INVESTIGATE_RECORDS)
		if(successful_set > COMP_SECURITY_ARREST_AMOUNT_TO_FLAG)
			message_admins("[successful_set] security entries have been set to [status_to_set] by [parent.get_creator_admin()]. [ADMIN_COORDJMP(src)]")
		for(var/mob/living/carbon/human/human as anything in GLOB.human_list)
			human.sec_hud_set_security_status()

#undef COMP_STATE_ARREST
#undef COMP_STATE_PRISONER
#undef COMP_STATE_PAROL
#undef COMP_STATE_DISCHARGED
#undef COMP_STATE_NONE
#undef COMP_SECURITY_ARREST_AMOUNT_TO_FLAG

/obj/machinery/computer/secure_data/syndie
	icon_keyboard = "syndie_key"

/obj/machinery/computer/secure_data/laptop
	name = "security laptop"
	desc = "A cheap Nanotrasen security laptop, it functions as a security records console. It's bolted to the table."
	icon_state = "laptop"
	icon_screen = "seclaptop"
	icon_keyboard = "laptop_key"
	pass_flags = PASSTABLE

//Someone needs to break down the dat += into chunks instead of long ass lines.
/obj/machinery/computer/secure_data/ui_interact(mob/user)
	. = ..()
	if(isliving(user))
		playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, FALSE)
	if(src.z > 6)
		to_chat(user, "[span_boldannounce("Unable to establish a connection")]: \black You're too far away from the station!")
		return
	var/dat

	if(temp)
		dat = "<TT>[temp]</TT>

Clear Screen"
	else
		dat = ""
		if(authenticated)
			switch(screen)
				if(1)

					//body tag start + onload and onkeypress (onkeyup) javascript event calls
					dat += ""
					//search bar javascript
					dat += {"

	"}
					dat += {"

"}
					dat += "New Record
"
					//search bar
					dat += {"

									Search: <input type='text' id='filter' value='' style='width:300px;'>

					"}
					dat += {"

Records:

Name
ID
Rank
Fingerprints
Criminal Status

"}
					if(!isnull(GLOB.data_core.general))
						for(var/datum/data/record/R in sort_record(GLOB.data_core.general, sortBy, order))
							var/crimstat = ""
							for(var/datum/data/record/E in GLOB.data_core.security)
								if((E.fields["name"] == R.fields["name"]) && (E.fields["id"] == R.fields["id"]))
									crimstat = E.fields["criminal"]
							var/background
							switch(crimstat)
								if("*Arrest*")
									background = "'background-color:#990000;'"
								if("Incarcerated")
									background = "'background-color:#CD6500;'"
								if("Paroled")
									background = "'background-color:#CD6500;'"
								if("Discharged")
									background = "'background-color:#006699;'"
								if("None")
									background = "'background-color:#4F7529;'"
								if("")
									background = "''" //"'background-color:#FFFFFF;'"
									crimstat = "No Record."
							dat += "
"
							dat += text("<input type='hidden' value='[] [] [] []'></input>[]	", R.fields["name"], R.fields["id"], R.fields["rank"], R.fields["fingerprint"], R.fields["name"])
							dat += text("[]	", R.fields["id"])
							dat += text("[]	", R.fields["rank"])
							dat += text("[]	", R.fields["fingerprint"])
							dat += text("[]
", crimstat)
						dat += {"

"}
					dat += "Record Maintenance

"
					dat += "{Log Out}"
				if(2)
					dat += "Records Maintenance
"
					dat += "
Delete All Records

Back"
				if(3)
					dat += "Security Record
"
					if(istype(active1, /datum/data/record) && GLOB.data_core.general.Find(active1))
						if(istype(active1.fields["photo_front"], /obj/item/photo))
							var/obj/item/photo/P1 = active1.fields["photo_front"]
							user << browse_rsc(P1.picture.picture_image, "photo_front")
						if(istype(active1.fields["photo_side"], /obj/item/photo))
							var/obj/item/photo/P2 = active1.fields["photo_side"]
							user << browse_rsc(P2.picture.picture_image, "photo_side")
						dat += {"

Name:	 [active1.fields["name"]]

ID:	 [active1.fields["id"]]

Gender:	 [active1.fields["gender"]]

Age:	 [active1.fields["age"]]
"}
						dat += "
Species:	 [active1.fields["species"]]
"
						dat += {"
Rank:	 [active1.fields["rank"]]

Fingerprint:	 [active1.fields["fingerprint"]]

Physical Status:	 [active1.fields["p_stat"]]

Mental Status:	 [active1.fields["m_stat"]]

						Print photo

						Update front photo

						Print photo

						Update side photo

"}
					else
						dat += "
General Record Lost!
"
					if((istype(active2, /datum/data/record) && GLOB.data_core.security.Find(active2)))
						dat += "Security Data"
						dat += "
Criminal Status: [active2.fields["criminal"]]"
						dat += "

Citations: Add New"

						dat +={"

						Crime
						Fine
						Author
						Time Added
						Amount Due
						Del

"}
						for(var/datum/data/crime/c in active2.fields["citation"])
							var/owed = c.fine - c.paid
							dat += {"
[c.crimeName]
							[c.fine] cr	[c.author]
							[c.time]	"}
							if(owed > 0)
								dat += "[owed] cr \[Pay\]		"
							else
								dat += "All Paid Off	"
							dat += {"
							\[X\]

"}
						dat += "
"

						dat += "

Crimes: Add New"

						dat +={"

						Crime
						Details
						Author
						Time Added
						Del

"}
						for(var/datum/data/crime/c in active2.fields["crim"])
							dat += "
[c.crimeName]	"
							if(!c.crimeDetails)
								dat += "\[+\]	"
							else
								dat += "[c.crimeDetails]	"
							dat += "[c.author]	"
							dat += "[c.time]	"
							dat += "\[X\]	"
							dat += "
"
						dat += "
"

						dat += "
\nImportant Notes:
\n\t [active2.fields["notes"]] "
						dat += "

Comments/Log
"
						var/counter = 1
						while(active2.fields[text("com_[]", counter)])
							dat += (active2.fields[text("com_[]", counter)] + "
")
							if(active2.fields[text("com_[]", counter)] != "Deleted")
								dat += text("Delete Entry

", counter)
							counter++
						dat += "Add Entry

"
						dat += "Delete Record (Security Only)
"
					else
						dat += "Security Record Lost!
"
						dat += "New Security Record

"
					dat += "Delete Record (ALL)
Print Record
Print Wanted Poster
Print Missing Persons Poster
Back

"
					dat += "{Log Out}"
				else
		else
			dat += "{Log In}"
	var/datum/browser/popup = new(user, "secure_rec", "Security Records Console", 600, 400)
	popup.set_content(dat)
	popup.open()
	return

/*Revised /N
I can't be bothered to look more of the actual code outside of switch but that probably needs revising too.
What a mess.*/
/obj/machinery/computer/secure_data/Topic(href, href_list)
	. = ..()
	if(.)
		return .
	if(!( GLOB.data_core.general.Find(active1) ))
		active1 = null
	if(!( GLOB.data_core.security.Find(active2) ))
		active2 = null
	if(!authenticated && href_list["choice"] != "Log In") // logging in is the only action you can do if not logged in
		return
	if(usr.contents.Find(src) || (in_range(src, usr) && isturf(loc)) || issilicon(usr) || isAdminGhostAI(usr))
		usr.set_machine(src)
		switch(href_list["choice"])
// SORTING!
			if("Sorting")
				// Reverse the order if clicked twice
				if(sortBy == href_list["sort"])
					if(order == 1)
						order = -1
					else
						order = 1
				else
				// New sorting order!
					sortBy = href_list["sort"]
					order = initial(order)
//BASIC FUNCTIONS
			if("Clear Screen")
				temp = null

			if("Return")
				screen = 1
				active1 = null
				active2 = null

			if("Log Out")
				authenticated = null
				screen = null
				active1 = null
				active2 = null
				playsound(src, 'sound/machines/terminal_off.ogg', 50, FALSE)

			if("Log In")
				var/obj/item/card/id/I
				if(isliving(usr))
					var/mob/living/L = usr
					I = L.get_idcard(TRUE)
				if(issilicon(usr))
					active1 = null
					active2 = null
					authenticated = usr.name
					rank = "AI"
					screen = 1
				else if(isAdminGhostAI(usr))
					active1 = null
					active2 = null
					authenticated = usr.client.holder.admin_signature
					rank = "Central Command"
					screen = 1
				else if(I && check_access(I))
					active1 = null
					active2 = null
					authenticated = I.registered_name
					rank = I.assignment
					screen = 1
				else
					to_chat(usr, span_danger("Unauthorized Access."))
				playsound(src, 'sound/machines/terminal_on.ogg', 50, FALSE)

//RECORD FUNCTIONS
			if("Record Maintenance")
				screen = 2
				active1 = null
				active2 = null

			if("Browse Record")
				var/datum/data/record/R = locate(href_list["d_rec"]) in GLOB.data_core.general
				if(!R)
					temp = "Record Not Found!"
				else
					active1 = active2 = R
					for(var/datum/data/record/E in GLOB.data_core.security)
						if((E.fields["name"] == R.fields["name"] || E.fields["id"] == R.fields["id"]))
							active2 = E
					screen = 3

			if("Pay")
				for(var/datum/data/crime/p in active2.fields["citation"])
					if(p.dataId == text2num(href_list["cdataid"]))
						var/obj/item/holochip/C = usr.is_holding_item_of_type(/obj/item/holochip)
						if(C && istype(C))
							var/pay = C.get_item_credit_value()
							if(!pay)
								to_chat(usr, span_warning("[C] doesn't seem to be worth anything!"))
							else
								var/diff = p.fine - p.paid
								GLOB.data_core.payCitation(active2.fields["id"], text2num(href_list["cdataid"]), pay)
								to_chat(usr, span_notice("You have paid [pay] credit\s towards your fine."))
								if (pay == diff || pay > diff || pay >= diff)
									investigate_log("Citation Paid off: [p.crimeName] Fine: [p.fine] | Paid off by [key_name(usr)]", INVESTIGATE_RECORDS)
									to_chat(usr, span_notice("The fine has been paid in full."))
								SSblackbox.ReportCitation(text2num(href_list["cdataid"]),"","","","", 0, pay)
								qdel(C)
								playsound(src, "terminal_type", 25, FALSE)
						else
							to_chat(usr, span_warning("Fines can only be paid with holochips!"))

			if("Print Record")
				if(!( printing ))
					printing = 1
					GLOB.data_core.securityPrintCount++
					playsound(loc, 'sound/items/poster_being_created.ogg', 100, TRUE)
					sleep(30)
					var/obj/item/paper/P = new /obj/item/paper( loc )
					P.info = "Security Record - (SR-[GLOB.data_core.securityPrintCount])
"
					if((istype(active1, /datum/data/record) && GLOB.data_core.general.Find(active1)))
						P.info += text("Name: [] ID: []
\nGender: []
\nAge: []
", active1.fields["name"], active1.fields["id"], active1.fields["gender"], active1.fields["age"])
						P.info += "\nSpecies: [active1.fields["species"]]
"
						P.info += text("\nFingerprint: []
\nPhysical Status: []
\nMental Status: []
", active1.fields["fingerprint"], active1.fields["p_stat"], active1.fields["m_stat"])
					else
						P.info += "General Record Lost!
"
					if((istype(active2, /datum/data/record) && GLOB.data_core.security.Find(active2)))
						P.info += text("
\nSecurity Data
\nCriminal Status: []", active2.fields["criminal"])

						P.info += "
\n
\nCrimes:
\n"
						P.info +={"

Crime
Details
Author
Time Added

"}
						for(var/datum/data/crime/c in active2.fields["crim"])
							P.info += "
[c.crimeName]	"
							P.info += "[c.crimeDetails]	"
							P.info += "[c.author]	"
							P.info += "[c.time]	"
							P.info += "
"
						P.info += "
"

						P.info += text("
\nImportant Notes:
\n\t[]
\n
\nComments/Log
", active2.fields["notes"])
						var/counter = 1
						while(active2.fields[text("com_[]", counter)])
							P.info += text("[]
", active2.fields[text("com_[]", counter)])
							counter++
						P.name = text("SR-[] '[]'", GLOB.data_core.securityPrintCount, active1.fields["name"])
					else
						P.info += "Security Record Lost!
"
						P.name = text("SR-[] '[]'", GLOB.data_core.securityPrintCount, "Record Lost")
					P.info += "</TT>"
					P.update_appearance()
					printing = null
			if("Print Poster")
				if(!( printing ))
					var/wanted_name = stripped_input(usr, "Please enter an alias for the criminal:", "Print Wanted Poster", active1.fields["name"])
					if(wanted_name)
						var/default_description = "A poster declaring [wanted_name] to be a dangerous individual, wanted by Nanotrasen. Report any sightings to security immediately."
						var/list/crimes = active2.fields["crim"]
						if(crimes.len)
							default_description += "\n[wanted_name] is wanted for the following crimes:\n"
							for(var/datum/data/crime/c in active2.fields["crim"])
								default_description += "\n[c.crimeName]\n"
								default_description += "[c.crimeDetails]\n"

						var/headerText = stripped_input(usr, "Please enter Poster Heading (Max 7 Chars):", "Print Wanted Poster", "WANTED", 8)

						var/info = stripped_multiline_input(usr, "Please input a description for the poster:", "Print Wanted Poster", default_description, null)
						if(info)
							playsound(loc, 'sound/items/poster_being_created.ogg', 100, TRUE)
							printing = 1
							sleep(30)
							if((istype(active1, /datum/data/record) && GLOB.data_core.general.Find(active1)))//make sure the record still exists.
								var/obj/item/photo/photo = active1.fields["photo_front"]
								new /obj/item/poster/wanted(loc, photo.picture.picture_image, wanted_name, info, headerText)
							printing = 0
			if("Print Missing")
				if(!( printing ))
					var/missing_name = stripped_input(usr, "Please enter an alias for the missing person:", "Print Missing Persons Poster", active1.fields["name"])
					if(missing_name)
						var/default_description = "A poster declaring [missing_name] to be a missing individual, missed by Nanotrasen. Report any sightings to security immediately."

						var/headerText = stripped_input(usr, "Please enter Poster Heading (Max 7 Chars):", "Print Missing Persons Poster", "MISSING", 8)

						var/info = stripped_multiline_input(usr, "Please input a description for the poster:", "Print Missing Persons Poster", default_description, null)
						if(info)
							playsound(loc, 'sound/items/poster_being_created.ogg', 100, TRUE)
							printing = 1
							sleep(30)
							if((istype(active1, /datum/data/record) && GLOB.data_core.general.Find(active1)))//make sure the record still exists.
								var/obj/item/photo/photo = active1.fields["photo_front"]
								new /obj/item/poster/wanted/missing(loc, photo.picture.picture_image, missing_name, info, headerText)
							printing = 0

//RECORD DELETE
			if("Delete All Records")
				temp = ""
				temp += "Are you sure you wish to delete all Security records?
"
				temp += "Yes
"
				temp += "No"

			if("Purge All Records")
				investigate_log("[key_name(usr)] has purged all the security records.", INVESTIGATE_RECORDS)
				for(var/datum/data/record/R in GLOB.data_core.security)
					qdel(R)
				GLOB.data_core.security.Cut()
				temp = "All Security records deleted."

			if("Add Entry")
				if(!( istype(active2, /datum/data/record) ))
					return
				var/a2 = active2
				var/t1 = stripped_multiline_input("Add Comment:", "Secure. records", null, null)
				if(!canUseSecurityRecordsConsole(usr, t1, null, a2))
					return
				var/counter = 1
				while(active2.fields[text("com_[]", counter)])
					counter++
				/* SEPTIC EDIT REMOVAL
				active2.fields[text("com_[]", counter)] = text("Made by [] ([]) on [] [], []
[]", src.authenticated, src.rank, station_time_timestamp(), time2text(world.realtime, "MMM DD"), GLOB.year_integer+540, t1)
				*/
				//SEPTIC EDIT BEGIN
				active2.fields[text("com_[]", counter)] = text("Made by [] ([]) on [] [], []
[]", src.authenticated, src.rank, station_time_timestamp(), time2text(world.realtime, "MMM DD"), GLOB.current_station_year, t1)
				//SEPTIC EDIT END
			if("Delete Record (ALL)")
				if(active1)
					temp = "
Are you sure you wish to delete the record (ALL)?
"
					temp += "Yes
"
					temp += "No"

			if("Delete Record (Security)")
				if(active2)
					temp = "
Are you sure you wish to delete the record (Security Portion Only)?
"
					temp += "Yes
"
					temp += "No"

			if("Delete Entry")
				if((istype(active2, /datum/data/record) && active2.fields[text("com_[]", href_list["del_c"])]))
					active2.fields[text("com_[]", href_list["del_c"])] = "Deleted"
//RECORD CREATE
			if("New Record (Security)")
				if((istype(active1, /datum/data/record) && !( istype(active2, /datum/data/record) )))
					var/datum/data/record/R = new /datum/data/record()
					R.fields["name"] = active1.fields["name"]
					R.fields["id"] = active1.fields["id"]
					R.name = text("Security Record #[]", R.fields["id"])
					R.fields["criminal"] = "None"
					R.fields["crim"] = list()
					R.fields["notes"] = "No notes."
					GLOB.data_core.security += R
					active2 = R
					screen = 3

			if("New Record (General)")
				//General Record
				var/datum/data/record/G = new /datum/data/record()
				G.fields["name"] = "New Record"
				G.fields["id"] = "[num2hex(rand(1, 1.6777215E7), 6)]"
				G.fields["rank"] = "Unassigned"
				G.fields["gender"] = "Male"
				G.fields["age"] = "Unknown"
				G.fields["species"] = "Human"
				G.fields["photo_front"] = new /icon()
				G.fields["photo_side"] = new /icon()
				G.fields["fingerprint"] = "?????"
				G.fields["p_stat"] = "Active"
				G.fields["m_stat"] = "Stable"
				GLOB.data_core.general += G
				active1 = G

				//Security Record
				var/datum/data/record/R = new /datum/data/record()
				R.fields["name"] = active1.fields["name"]
				R.fields["id"] = active1.fields["id"]
				R.name = text("Security Record #[]", R.fields["id"])
				R.fields["criminal"] = "None"
				R.fields["crim"] = list()
				R.fields["notes"] = "No notes."
				GLOB.data_core.security += R
				active2 = R

				//Medical Record
				var/datum/data/record/M = new /datum/data/record()
				M.fields["id"] = active1.fields["id"]
				M.fields["name"] = active1.fields["name"]
				M.fields["blood_type"] = "?"
				M.fields["b_dna"] = "?????"
				M.fields["mi_dis"] = "None"
				M.fields["mi_dis_d"] = "No minor disabilities have been declared."
				M.fields["ma_dis"] = "None"
				M.fields["ma_dis_d"] = "No major disabilities have been diagnosed."
				M.fields["alg"] = "None"
				M.fields["alg_d"] = "No allergies have been detected in this patient."
				M.fields["cdi"] = "None"
				M.fields["cdi_d"] = "No diseases have been diagnosed at the moment."
				M.fields["notes"] = "No notes."
				GLOB.data_core.medical += M

//FIELD FUNCTIONS
			if("Edit Field")
				var/a1 = active1
				var/a2 = active2

				switch(href_list["field"])
					if("name")
						if(istype(active1, /datum/data/record) || istype(active2, /datum/data/record))
							var/t1 = stripped_input(usr, "Please input name:", "Secure. records", active1.fields["name"], MAX_MESSAGE_LEN)
							if(!canUseSecurityRecordsConsole(usr, t1, a1))
								return
							if(istype(active1, /datum/data/record))
								active1.fields["name"] = t1
							if(istype(active2, /datum/data/record))
								active2.fields["name"] = t1
					if("id")
						if(istype(active2, /datum/data/record) || istype(active1, /datum/data/record))
							var/t1 = stripped_input(usr, "Please input id:", "Secure. records", active1.fields["id"], null)
							if(!canUseSecurityRecordsConsole(usr, t1, a1))
								return
							if(istype(active1, /datum/data/record))
								active1.fields["id"] = t1
							if(istype(active2, /datum/data/record))
								active2.fields["id"] = t1
					if("fingerprint")
						if(istype(active1, /datum/data/record))
							var/t1 = stripped_input(usr, "Please input fingerprint hash:", "Secure. records", active1.fields["fingerprint"], null)
							if(!canUseSecurityRecordsConsole(usr, t1, a1))
								return
							active1.fields["fingerprint"] = t1
					if("gender")
						if(istype(active1, /datum/data/record))
							if(active1.fields["gender"] == "Male")
								active1.fields["gender"] = "Female"
							else if(active1.fields["gender"] == "Female")
								active1.fields["gender"] = "Other"
							else
								active1.fields["gender"] = "Male"
					if("age")
						if(istype(active1, /datum/data/record))
							var/t1 = input("Please input age:", "Secure. records", active1.fields["age"], null) as num|null

							if (!t1)
								return

							if(!canUseSecurityRecordsConsole(usr, "age", a1))
								return
							active1.fields["age"] = t1
					if("species")
						if(istype(active1, /datum/data/record))
							var/t1 = input("Select a species", "Species Selection") as null|anything in get_selectable_species()
							if(!canUseSecurityRecordsConsole(usr, t1, a1))
								return
							active1.fields["species"] = t1
					if("show_photo_front")
						if(active1.fields["photo_front"])
							if(istype(active1.fields["photo_front"], /obj/item/photo))
								var/obj/item/photo/P = active1.fields["photo_front"]
								P.show(usr)
					if("upd_photo_front")
						var/obj/item/photo/photo = get_photo(usr)
						if(photo)
							qdel(active1.fields["photo_front"])
							//Lets center it to a 32x32.
							var/icon/I = photo.picture.picture_image
							var/w = I.Width()
							var/h = I.Height()
							var/dw = w - 32
							var/dh = w - 32
							I.Crop(dw/2, dh/2, w - dw/2, h - dh/2)
							active1.fields["photo_front"] = photo
					if("print_photo_front")
						if(active1.fields["photo_front"])
							if(istype(active1.fields["photo_front"], /obj/item/photo))
								var/obj/item/photo/P = active1.fields["photo_front"]
								print_photo(P.picture.picture_image, active1.fields["name"])
					if("show_photo_side")
						if(active1.fields["photo_side"])
							if(istype(active1.fields["photo_side"], /obj/item/photo))
								var/obj/item/photo/P = active1.fields["photo_side"]
								P.show(usr)
					if("upd_photo_side")
						var/obj/item/photo/photo = get_photo(usr)
						if(photo)
							qdel(active1.fields["photo_side"])
							//Lets center it to a 32x32.
							var/icon/I = photo.picture.picture_image
							var/w = I.Width()
							var/h = I.Height()
							var/dw = w - 32
							var/dh = w - 32
							I.Crop(dw/2, dh/2, w - dw/2, h - dh/2)
							active1.fields["photo_side"] = photo
					if("print_photo_side")
						if(active1.fields["photo_side"])
							if(istype(active1.fields["photo_side"], /obj/item/photo))
								var/obj/item/photo/P = active1.fields["photo_side"]
								print_photo(P.picture.picture_image, active1.fields["name"])
					if("crim_add")
						if(istype(active1, /datum/data/record))
							var/t1 = stripped_input(usr, "Please input crime names:", "Secure. records", "", null)
							var/t2 = stripped_input(usr, "Please input crime details:", "Secure. records", "", null)
							if(!canUseSecurityRecordsConsole(usr, t1, null, a2))
								return
							var/crime = GLOB.data_core.createCrimeEntry(t1, t2, authenticated, station_time_timestamp())
							GLOB.data_core.addCrime(active1.fields["id"], crime)
							investigate_log("New Crime: [t1]: [t2] | Added to [active1.fields["name"]] by [key_name(usr)]", INVESTIGATE_RECORDS)
					if("crim_delete")
						if(istype(active1, /datum/data/record))
							if(href_list["cdataid"])
								if(!canUseSecurityRecordsConsole(usr, "delete", null, a2))
									return
								GLOB.data_core.removeCrime(active1.fields["id"],href_list["cdataid"])
					if("add_details")
						if(istype(active1, /datum/data/record))
							if(href_list["cdataid"])
								var/t1 = stripped_input(usr, "Please input crime details:", "Secure. records", "", null)
								if(!canUseSecurityRecordsConsole(usr, t1, null, a2))
									return
								GLOB.data_core.addCrimeDetails(active1.fields["id"], href_list["cdataid"], t1)
								investigate_log("New Crime details: [t1] | Added to [active1.fields["name"]] by [key_name(usr)]", INVESTIGATE_RECORDS)
					if("citation_add")
						if(istype(active1, /datum/data/record))
							var/maxFine = CONFIG_GET(number/maxfine)

							var/t1 = stripped_input(usr, "Please input citation crime:", "Secure. records", "", null)
							var/fine = FLOOR(input(usr, "Please input citation fine, up to [maxFine]:", "Secure. records", 50) as num|null, 1)

							if (isnull(fine))
								return
							fine = min(fine, maxFine)

							if(fine < 0)
								to_chat(usr, span_warning("You're pretty sure that's not how money works."))
								return

							if(!canUseSecurityRecordsConsole(usr, t1, null, a2))
								return

							var/datum/data/crime/crime = GLOB.data_core.createCrimeEntry(t1, "", authenticated, station_time_timestamp(), fine)
							for (var/obj/item/pda/P in GLOB.PDAs)
								if(P.owner == active1.fields["name"])
									var/message = "You have been fined [fine] credits for '[t1]'. Fines may be paid at security."
									var/datum/signal/subspace/messaging/pda/signal = new(src, list(
										"name" = "Security Citation",
										"job" = "Citation Server",
										"message" = message,
										"targets" = list("[P.owner] ([P.ownjob])"),
										"automated" = 1
									))
									signal.send_to_receivers()
									usr.log_message("(PDA: Citation Server) sent \"[message]\" to [signal.format_target()]", LOG_PDA)
							GLOB.data_core.addCitation(active1.fields["id"], crime)
							investigate_log("New Citation: [t1] Fine: [fine] | Added to [active1.fields["name"]] by [key_name(usr)]", INVESTIGATE_RECORDS)
							SSblackbox.ReportCitation(crime.dataId, usr.ckey, usr.real_name, active1.fields["name"], t1, fine)
					if("citation_delete")
						if(istype(active1, /datum/data/record))
							if(href_list["cdataid"])
								if(!canUseSecurityRecordsConsole(usr, "delete", null, a2))
									return
								GLOB.data_core.removeCitation(active1.fields["id"], href_list["cdataid"])
					if("notes")
						if(istype(active2, /datum/data/record))
							var/t1 = stripped_input(usr, "Please summarize notes:", "Secure. records", active2.fields["notes"], null)
							if(!canUseSecurityRecordsConsole(usr, t1, null, a2))
								return
							active2.fields["notes"] = t1
					if("criminal")
						if(istype(active2, /datum/data/record))
							temp = "
Criminal Status:
"
							temp += "
"
							temp += "
None
"
							temp += "
*Arrest*
"
							temp += "
Incarcerated
"
							temp += "
Paroled
"
							temp += "
Discharged
"
							temp += "
"
					if("rank")
						var/list/L = list( "Head of Personnel", "Captain", "AI", "Central Command" )
						//This was so silly before the change. Now it actually works without beating your head against the keyboard. /N
						if((istype(active1, /datum/data/record) && L.Find(rank)))
							temp = "
Rank:
"
							temp += "
"
							for(var/rank in SSjob.station_jobs)
								temp += "
[rank]
"
							temp += "
"
						else
							tgui_alert(usr, "You do not have the required rank to do this!")
//TEMPORARY MENU FUNCTIONS
			else//To properly clear as per clear screen.
				temp=null
				switch(href_list["choice"])
					if("Change Rank")
						if(active1)
							active1.fields["rank"] = strip_html(href_list["rank"])
							if(href_list["rank"] in SSjob.station_jobs)
								active1.fields["real_rank"] = href_list["real_rank"]

					if("Change Criminal Status")
						if(active2)
							var/old_field = active2.fields["criminal"]
							switch(href_list["criminal2"])
								if("none")
									active2.fields["criminal"] = "None"
								if("arrest")
									active2.fields["criminal"] = "*Arrest*"
								if("incarcerated")
									active2.fields["criminal"] = "Incarcerated"
								if("paroled")
									active2.fields["criminal"] = "Paroled"
								if("released")
									active2.fields["criminal"] = "Discharged"
							investigate_log("[active1.fields["name"]] has been set from [old_field] to [active2.fields["criminal"]] by [key_name(usr)].", INVESTIGATE_RECORDS)
							for(var/i in GLOB.human_list)
								var/mob/living/carbon/human/H = i
								H.sec_hud_set_security_status()
					if("Delete Record (Security) Execute")
						investigate_log("[key_name(usr)] has deleted the security records for [active1.fields["name"]].", INVESTIGATE_RECORDS)
						if(active2)
							qdel(active2)
							active2 = null

					if("Delete Record (ALL) Execute")
						if(active1)
							investigate_log("[key_name(usr)] has deleted all records for [active1.fields["name"]].", INVESTIGATE_RECORDS)
							for(var/datum/data/record/R in GLOB.data_core.medical)
								if((R.fields["name"] == active1.fields["name"] || R.fields["id"] == active1.fields["id"]))
									qdel(R)
									break
							qdel(active1)
							active1 = null

						if(active2)
							qdel(active2)
							active2 = null
					else
						temp = "This function does not appear to be working at the moment. Our apologies."

	add_fingerprint(usr)
	updateUsrDialog()
	return

/obj/machinery/computer/secure_data/proc/get_photo(mob/user)
	var/obj/item/photo/P = null
	if(issilicon(user))
		var/mob/living/silicon/tempAI = user
		var/datum/picture/selection = tempAI.GetPhoto(user)
		if(selection)
			P = new(null, selection)
	else if(istype(user.get_active_held_item(), /obj/item/photo))
		P = user.get_active_held_item()
	return P

/obj/machinery/computer/secure_data/proc/print_photo(icon/temp, person_name)
	if (printing)
		return
	printing = TRUE
	sleep(20)
	var/obj/item/photo/P = new/obj/item/photo(drop_location())
	var/datum/picture/toEmbed = new(name = person_name, desc = "The photo on file for [person_name].", image = temp)
	P.set_picture(toEmbed, TRUE, TRUE)
	P.pixel_x = rand(-10, 10)
	P.pixel_y = rand(-10, 10)
	printing = FALSE

/obj/machinery/computer/secure_data/emp_act(severity)
	. = ..()

	if(machine_stat & (BROKEN|NOPOWER) || . & EMP_PROTECT_SELF)
		return

	for(var/datum/data/record/R in GLOB.data_core.security)
		if(prob(10/severity))
			switch(rand(1,8))
				if(1)
					if(prob(10))
						R.fields["name"] = "[pick(lizard_name(MALE),lizard_name(FEMALE))]"
					else
						R.fields["name"] = "[pick(pick(GLOB.first_names_male), pick(GLOB.first_names_female))] [pick(GLOB.last_names)]"
				if(2)
					R.fields["gender"] = pick("Male", "Female", "Other")
				if(3)
					R.fields["age"] = rand(5, 85)
				if(4)
					R.fields["criminal"] = pick("None", "*Arrest*", "Incarcerated", "Paroled", "Discharged")
				if(5)
					R.fields["p_stat"] = pick("*Unconscious*", "Active", "Physically Unfit")
				if(6)
					R.fields["m_stat"] = pick("*Insane*", "*Unstable*", "*Watch*", "Stable")
				if(7)
					R.fields["species"] = pick(get_selectable_species())
				if(8)
					var/datum/data/record/G = pick(GLOB.data_core.general)
					R.fields["photo_front"] = G.fields["photo_front"]
					R.fields["photo_side"] = G.fields["photo_side"]
			continue

		else if(prob(1))
			qdel(R)
			continue

/obj/machinery/computer/secure_data/proc/canUseSecurityRecordsConsole(mob/user, message1 = 0, record1, record2)
	if(user && authenticated)
		if(user.canUseTopic(src, !issilicon(user)))
			if(!trim(message1))
				return FALSE
			if(!record1 || record1 == active1)
				if(!record2 || record2 == active2)
					return TRUE
	return FALSE


code:
/obj/machinery/computer/station_alert
	name = "station alert console"
	desc = "Used to access the station's automated alert system."
	icon_screen = "alert:0"
	icon_keyboard = "atmos_key"
	circuit = /obj/item/circuitboard/computer/stationalert
	light_color = LIGHT_COLOR_CYAN
	/// Station alert datum for showing alerts UI
	var/datum/station_alert/alert_control

/obj/machinery/computer/station_alert/Initialize(mapload)
	alert_control = new(src, list(ALARM_ATMOS, ALARM_FIRE, ALARM_POWER), list(z), title = name)
	RegisterSignal(alert_control.listener, list(COMSIG_ALARM_TRIGGERED, COMSIG_ALARM_CLEARED), .proc/update_alarm_display)
	return ..()

/obj/machinery/computer/station_alert/Destroy()
	QDEL_NULL(alert_control)
	return ..()

/obj/machinery/computer/station_alert/ui_interact(mob/user)
	alert_control.ui_interact(user)

/obj/machinery/computer/station_alert/on_set_machine_stat(old_value)
	if(machine_stat & BROKEN)
		alert_control.listener.prevent_alarm_changes()
	else
		alert_control.listener.allow_alarm_changes()

/obj/machinery/computer/station_alert/update_overlays()
	. = ..()
	if(machine_stat & (NOPOWER|BROKEN))
		return
	if(length(alert_control.listener.alarms))
		. += "alert:2"

/**
 * Signal handler for calling an icon update in case an alarm is added or cleared
 *
 * Arguments:
 * * source The datum source of the signal
 */
/obj/machinery/computer/station_alert/proc/update_alarm_display(datum/source)
	SIGNAL_HANDLER
	update_icon()


code:
/obj/machinery/computer/teleporter
	name = "teleporter control console"
	desc = "Used to control a linked teleportation Hub and Station."
	icon_screen = "teleport"
	icon_keyboard = "teleport_key"
	light_color = LIGHT_COLOR_BLUE
	circuit = /obj/item/circuitboard/computer/teleporter

	var/regime_set = "Teleporter"
	var/id
	var/obj/machinery/teleport/station/power_station
	var/calibrating
	///Weakref to the target atom we're pointed at currently
	var/datum/weakref/target_ref

/obj/machinery/computer/teleporter/Initialize(mapload)
	. = ..()
	id = "[rand(1000, 9999)]"
	link_power_station()

	AddComponent(/datum/component/usb_port, list(
		/obj/item/circuit_component/teleporter_control_console,
	))

/obj/machinery/computer/teleporter/Destroy()
	if (power_station)
		power_station.teleporter_console = null
		power_station = null
	return ..()

/obj/machinery/computer/teleporter/proc/link_power_station()
	if(power_station)
		return
	for(var/direction in GLOB.cardinals)
		power_station = locate(/obj/machinery/teleport/station, get_step(src, direction))
		if(power_station)
			power_station.link_console_and_hub()
			break
	return power_station

/obj/machinery/computer/teleporter/ui_interact(mob/user, datum/tgui/ui)
	ui = SStgui.try_update_ui(user, src, ui)
	if(!ui)
		ui = new(user, src, "Teleporter", name)
		ui.open()

/obj/machinery/computer/teleporter/ui_data(mob/user)
	var/atom/target
	if(target_ref)
		target = target_ref.resolve()
	if(!target)
		target_ref = null
	var/list/data = list()
	data["power_station"] = power_station ? TRUE : FALSE
	data["teleporter_hub"] = power_station?.teleporter_hub ? TRUE : FALSE
	data["regime_set"] = regime_set
	data["target"] = !target ? "None" : "[get_area(target)] [(regime_set != "Gate") ? "" : "Teleporter"]"
	data["calibrating"] = calibrating

	if(power_station?.teleporter_hub?.calibrated || power_station?.teleporter_hub?.accuracy >= 3)
		data["calibrated"] = TRUE
	else
		data["calibrated"] = FALSE

	return data

/obj/machinery/computer/teleporter/ui_act(action, params)
	. = ..()
	if(.)
		return

	if(!check_hub_connection())
		say("Error: Unable to detect hub.")
		return
	if(calibrating)
		say("Error: Calibration in progress. Stand by.")
		return

	switch(action)
		if("regimeset")
			power_station.engaged = FALSE
			power_station.teleporter_hub.update_appearance()
			power_station.teleporter_hub.calibrated = FALSE
			reset_regime()
			. = TRUE
		if("settarget")
			power_station.engaged = FALSE
			power_station.teleporter_hub.update_appearance()
			power_station.teleporter_hub.calibrated = FALSE
			set_target(usr)
			. = TRUE
		if("calibrate")
			if(!target_ref)
				say("Error: No target set to calibrate to.")
				return
			if(power_station.teleporter_hub.calibrated || power_station.teleporter_hub.accuracy >= 3)
				say("Hub is already calibrated!")
				return

			say("Processing hub calibration to target...")
			calibrating = TRUE
			power_station.update_appearance()
			addtimer(CALLBACK(src, .proc/finish_calibration), 50 * (3 - power_station.teleporter_hub.accuracy)) //Better parts mean faster calibration
			return TRUE

/obj/machinery/computer/teleporter/proc/set_teleport_target(new_target)
	var/datum/weakref/new_target_ref = WEAKREF(new_target)
	if (target_ref == new_target_ref)
		return
	SEND_SIGNAL(src, COMSIG_TELEPORTER_NEW_TARGET, new_target)
	target_ref = new_target_ref

/obj/machinery/computer/teleporter/proc/finish_calibration()
	calibrating = FALSE
	if(check_hub_connection())
		power_station.teleporter_hub.calibrated = TRUE
		say("Calibration complete.")
	else
		say("Error: Unable to detect hub.")
	power_station.update_appearance()

/obj/machinery/computer/teleporter/proc/check_hub_connection()
	if(!power_station)
		return FALSE
	if(!power_station.teleporter_hub)
		return FALSE
	return TRUE

/obj/machinery/computer/teleporter/proc/reset_regime()
	set_teleport_target(null)
	if(regime_set == "Teleporter")
		regime_set = "Gate"
	else
		regime_set = "Teleporter"

/// Gets a list of targets to teleport to.
/// List is an assoc list of descriptors to locations.
/obj/machinery/computer/teleporter/proc/get_targets()
	var/list/targets = list()
	var/list/area_index = list()

	if (regime_set == "Teleporter")
		for (var/obj/item/beacon/beacon as anything in GLOB.teleportbeacons)
			if (!is_eligible(beacon))
				continue

			if(beacon.renamed)
				targets[avoid_assoc_duplicate_keys("[beacon.name] ([get_area(beacon)])", area_index)] = beacon
			else
				var/area/area = get_area(beacon)
				targets[avoid_assoc_duplicate_keys(area.name, area_index)] = beacon

		for (var/obj/item/implant/tracking/tracking_implant in GLOB.tracked_implants)
			if (!tracking_implant.imp_in || !isliving(tracking_implant.loc) || !tracking_implant.allow_teleport)
				continue

			var/mob/living/implanted = tracking_implant.loc
			if (implanted.stat == DEAD && implanted.timeofdeath + tracking_implant.lifespan_postmortem < world.time)
				continue

			if (is_eligible(tracking_implant))
				targets[avoid_assoc_duplicate_keys("[implanted.real_name] ([get_area(implanted)])", area_index)] = tracking_implant
	else
		for (var/obj/machinery/teleport/station/station as anything in power_station.linked_stations)
			if (is_eligible(station) && station.teleporter_hub)
				var/area/area = get_area(station)
				targets[avoid_assoc_duplicate_keys(area.name, area_index)] = station

	return targets

/// Given a target station, will power and link it.
/obj/machinery/computer/teleporter/proc/lock_in_station(obj/machinery/teleport/station/target_station)
	target_station.linked_stations |= power_station
	target_station.set_machine_stat(target_station.machine_stat & ~NOPOWER)
	if(target_station.teleporter_hub)
		target_station.teleporter_hub.set_machine_stat(target_station.teleporter_hub.machine_stat & ~NOPOWER)
		target_station.teleporter_hub.update_appearance()
	if(target_station.teleporter_console)
		target_station.teleporter_console.set_machine_stat(target_station.teleporter_console.machine_stat & ~NOPOWER)
		target_station.teleporter_console.update_appearance()

/obj/machinery/computer/teleporter/proc/set_target(mob/user)
	var/list/targets = get_targets()

	if (regime_set == "Teleporter")
		var/desc = input("Please select a location to lock in.", "Locking Computer") as null|anything in sort_list(targets)
		set_teleport_target(targets[desc])
		var/turf/target_turf = get_turf(targets[desc])
		log_game("[key_name(user)] has set the teleporter target to [targets[desc]] at [AREACOORD(target_turf)]")
	else
		if (targets.len == 0)
			to_chat(user, span_alert("No active connected stations located."))
			return

		var/desc = input("Please select a station to lock in.", "Locking Computer") as null|anything in sort_list(targets)
		var/obj/machinery/teleport/station/target_station = targets[desc]
		if(!target_station || !target_station.teleporter_hub)
			return
		var/turf/target_station_turf = get_turf(target_station)
		log_game("[key_name(user)] has set the teleporter target to [target_station] at [AREACOORD(target_station_turf)]")
		set_teleport_target(target_station.teleporter_hub)
		lock_in_station(target_station)

/obj/machinery/computer/teleporter/proc/is_eligible(atom/movable/AM)
	var/turf/T = get_turf(AM)
	if(!T)
		return FALSE
	if(is_centcom_level(T.z) || is_away_level(T.z))
		return FALSE
	var/area/A = get_area(T)
	if(!A ||(A.area_flags & NOTELEPORT))
		return FALSE
	return TRUE

/obj/item/circuit_component/teleporter_control_console
	display_name = "Teleporter Control Console"
	desc = "Used to control a linked teleportation Hub and Station."
	circuit_flags = CIRCUIT_FLAG_OUTPUT_SIGNAL

	var/datum/port/input/new_target
	var/datum/port/input/set_target_trigger
	var/datum/port/input/update_trigger

	var/datum/port/output/current_target
	var/datum/port/output/possible_targets
	var/datum/port/output/on_fail

	var/obj/machinery/computer/teleporter/attached_console

/obj/item/circuit_component/teleporter_control_console/populate_ports()

	new_target = add_input_port("New Target", PORT_TYPE_STRING)
	set_target_trigger = add_input_port("Set Target", PORT_TYPE_SIGNAL)
	update_trigger = add_input_port("Update Targets", PORT_TYPE_SIGNAL)

	current_target = add_output_port("Current Target", PORT_TYPE_STRING)
	possible_targets = add_output_port("Possible Targets", PORT_TYPE_LIST(PORT_TYPE_ANY))
	on_fail = add_output_port("Failed", PORT_TYPE_SIGNAL)

/obj/item/circuit_component/teleporter_control_console/register_usb_parent(atom/movable/shell)
	. = ..()

	if (istype(shell, /obj/machinery/computer/teleporter))
		attached_console = shell

		RegisterSignal(attached_console, COMSIG_TELEPORTER_NEW_TARGET, .proc/on_teleporter_new_target)
		update_targets()

/obj/item/circuit_component/teleporter_control_console/unregister_usb_parent(atom/movable/shell)
	UnregisterSignal(attached_console, COMSIG_TELEPORTER_NEW_TARGET)
	attached_console = null
	return attached_console

/obj/item/circuit_component/teleporter_control_console/input_received(datum/port/input/port)
	var/list/targets = attached_console.get_targets()

	if (COMPONENT_TRIGGERED_BY(set_target_trigger, port))
		var/target = targets[new_target.value]
		if (!target)
			on_fail.set_output(COMPONENT_SIGNAL)
			return .

		attached_console.investigate_log("Teleport location set to [target] by circuit. [parent.get_creator()]")

		if (istype(target, /obj/machinery/teleport/station))
			var/obj/machinery/teleport/station/station = target
			attached_console.set_teleport_target(station.teleporter_hub)
			attached_console.lock_in_station(station)
		else
			attached_console.set_teleport_target(target)

		return .

	if (COMPONENT_TRIGGERED_BY(update_trigger, port))
		update_targets()

/obj/item/circuit_component/teleporter_control_console/proc/on_teleporter_new_target(datum/source, atom/new_target)
	SIGNAL_HANDLER

	if (isnull(new_target))
		current_target.set_output(null)
		return

	var/list/targets = attached_console.get_targets()
	for (var/target_name in targets)
		if (targets[target_name] == new_target)
			current_target.set_output(target_name)
			return

	// Last ditch scenario, we still need a string.
	current_target.set_output(new_target.name)

/obj/item/circuit_component/teleporter_control_console/proc/update_targets()
	var/list/target_names = list()
	for (var/target in attached_console.get_targets())
		target_names |= target

	possible_targets.set_output(target_names)