code:
/*
	The monitoring computer for the messaging server.
	Lets you read PDA and request console messages.
*/

#define LINKED_SERVER_NONRESPONSIVE  (!linkedServer || (linkedServer.machine_stat & (NOPOWER|BROKEN)))

#define MSG_MON_SCREEN_MAIN 0
#define MSG_MON_SCREEN_LOGS 1
#define MSG_MON_SCREEN_HACKED 2
#define MSG_MON_SCREEN_CUSTOM_MSG 3
#define MSG_MON_SCREEN_REQUEST_LOGS 4

// The monitor itself.
/obj/machinery/computer/message_monitor
	name = "message monitor console"
	desc = "Used to monitor the crew's PDA messages, as well as request console messages."
	icon_screen = "comm_logs"
	circuit = /obj/item/circuitboard/computer/message_monitor
	light_color = LIGHT_COLOR_GREEN
	//Server linked to.
	var/obj/machinery/telecomms/message_server/linkedServer = null
	//Sparks effect - For emag
	var/datum/effect_system/spark_spread/spark_system = new /datum/effect_system/spark_spread
	//Messages - Saves me time if I want to change something.
	var/noserver = "ALERT: No server detected."
	var/incorrectkey = "ALERT: Incorrect decryption key!"
	var/defaultmsg = "Welcome. Please select an option."
	var/rebootmsg = "%$&(£: Critical %$$@ Error // !RestArting! <lOadiNg backUp iNput ouTput> - ?pLeaSe wAit!"
	//Computer properties
	var/screen = MSG_MON_SCREEN_MAIN // 0 = Main menu, 1 = Message Logs, 2 = Hacked screen, 3 = Custom Message
	var/hacking = FALSE // Is it being hacked into by the AI/Cyborg
	var/message = "System bootup complete. Please select an option." // The message that shows on the main menu.
	var/auth = FALSE // Are they authenticated?
	var/optioncount = 7
	// Custom Message Properties
	var/customsender = "System Administrator"
	var/obj/item/pda/customrecepient = null
	var/customjob = "Admin"
	var/custommessage = "This is a test, please ignore."

/obj/machinery/computer/message_monitor/attackby(obj/item/O, mob/living/user, params)
	if(O.tool_behaviour == TOOL_SCREWDRIVER && (obj_flags & EMAGGED))
		//Stops people from just unscrewing the monitor and putting it back to get the console working again.
		to_chat(user, span_warning("It is too hot to mess with!"))
	else
		return ..()

/obj/machinery/computer/message_monitor/emag_act(mob/user)
	if(obj_flags & EMAGGED)
		return
	if(!isnull(linkedServer))
		obj_flags |= EMAGGED
		screen = MSG_MON_SCREEN_HACKED
		spark_system.set_up(5, 0, src)
		spark_system.start()
		var/obj/item/paper/monitorkey/MK = new(loc, linkedServer)
		// Will help make emagging the console not so easy to get away with.
		MK.info += "

£%@%(*$%&(£&?*(%&£/{}"
		var/time = 100 * length(linkedServer.decryptkey)
		addtimer(CALLBACK(src, .proc/UnmagConsole), time)
		message = rebootmsg
	else
		to_chat(user, span_notice("A no server error appears on the screen."))

/obj/machinery/computer/message_monitor/Initialize(mapload)
	..()
	GLOB.telecomms_list += src
	return INITIALIZE_HINT_LATELOAD

/obj/machinery/computer/message_monitor/LateInitialize()
	//Is the server isn't linked to a server, and there's a server available, default it to the first one in the list.
	if(!linkedServer)
		for(var/obj/machinery/telecomms/message_server/S in GLOB.telecomms_list)
			linkedServer = S
			break

/obj/machinery/computer/message_monitor/Destroy()
	GLOB.telecomms_list -= src
	return ..()

/obj/machinery/computer/message_monitor/ui_interact(mob/living/user)
	. = ..()
	//If the computer is being hacked or is emagged, display the reboot message.
	if(hacking || (obj_flags & EMAGGED))
		message = rebootmsg
	var/dat = "<font color='blue'[message]"

	if(auth)
		dat += "

	\[Authenticated\]	/"
		dat += " Server Power: [linkedServer?.on ? "\[On\]":"\[Off\]"]
"
	else
		dat += "

	\[Unauthenticated\]	/"
		dat += " Server Power: [linkedServer?.on ? "\[On\]":"\[Off\]"]
"

	if(hacking || (obj_flags & EMAGGED))
		screen = MSG_MON_SCREEN_HACKED
	else if(!auth || LINKED_SERVER_NONRESPONSIVE)
		if(LINKED_SERVER_NONRESPONSIVE)
			message = noserver
		screen = MSG_MON_SCREEN_MAIN

	switch(screen)
		//Main menu
		if(MSG_MON_SCREEN_MAIN)
			//	 = TAB
			var/i = 0
			dat += "
	[++i]. Link To A Server
"
			if(auth)
				if(LINKED_SERVER_NONRESPONSIVE)
					dat += "
	ERROR: Server not found!

"
				else
					dat += "
	[++i]. View Message Logs

"
					dat += "
	[++i]. View Request Console Logs

"
					dat += "
	[++i]. Clear Message Logs

"
					dat += "
	[++i]. Clear Request Console Logs

"
					dat += "
	[++i]. Set Custom Key

"
					dat += "
	[++i]. Send Admin Message

"
			else
				for(var/n = ++i; n <= optioncount; n++)
					dat += "
	[n]. ---------------

"
			var/mob/living/silicon/S = usr
			if(istype(S) && S.hack_software)
				//Malf/Traitor AIs can bruteforce into the system to gain the Key.
				dat += "
*&@#. Bruteforce Key

"
			else
				dat += "
"

			//Bottom message
			if(!auth)
				dat += "

[span_notice("Please authenticate with the server in order to show additional options.")]"
			else
				dat += "

[span_warning("Reg, #514 forbids sending messages to a Head of Staff containing Erotic Rendering Properties.")]"

		//Message Logs
		if(MSG_MON_SCREEN_LOGS)
			var/index = 0
			dat += "Back - Refresh
"
			dat += "

X	Sender	Recipient	Message
"
			for(var/datum/data_pda_msg/pda in linkedServer.pda_msgs)
				index++
				if(index > 3000)
					break
				// Del - Sender   - Recepient - Message
				// X   - Al Green - Your Mom  - WHAT UP!?
				dat += "
X	[pda.sender]	[pda.recipient]	[pda.message][pda.picture ? " (Photo)":""]
"
			dat += "
"
		//Hacking screen.
		if(MSG_MON_SCREEN_HACKED)
			if(isAI(user) || iscyborg(user))
				dat += "Brute-forcing for server key.
 It will take 20 seconds for every character that the password has."
				dat += "In the meantime, this console can reveal your true intentions if you let someone access it. Make sure no humans enter the room during that time."
			else
				//It's the same message as the one above but in binary. Because robots understand binary and humans don't... well I thought it was clever.
				dat += {"01000010011100100111010101110100011001010010110

				10110011001101111011100100110001101101001011011100110011

				10010000001100110011011110111001000100000011100110110010

				10111001001110110011001010111001000100000011010110110010

				10111100100101110001000000100100101110100001000000111011

				10110100101101100011011000010000001110100011000010110101

				10110010100100000001100100011000000100000011100110110010

				10110001101101111011011100110010001110011001000000110011

				00110111101110010001000000110010101110110011001010111001

				00111100100100000011000110110100001100001011100100110000

				10110001101110100011001010111001000100000011101000110100

				00110000101110100001000000111010001101000011001010010000

				00111000001100001011100110111001101110111011011110111001

				00110010000100000011010000110000101110011001011100010000

				00100100101101110001000000111010001101000011001010010000

				00110110101100101011000010110111001110100011010010110110

				10110010100101100001000000111010001101000011010010111001

				10010000001100011011011110110111001110011011011110110110

				00110010100100000011000110110000101101110001000000111001

				00110010101110110011001010110000101101100001000000111100

				10110111101110101011100100010000001110100011100100111010

				10110010100100000011010010110111001110100011001010110111

				00111010001101001011011110110111001110011001000000110100

				10110011000100000011110010110111101110101001000000110110

				00110010101110100001000000111001101101111011011010110010

				10110111101101110011001010010000001100001011000110110001

				10110010101110011011100110010000001101001011101000010111

				00010000001001101011000010110101101100101001000000111001

				10111010101110010011001010010000001101110011011110010000

				00110100001110101011011010110000101101110011100110010000

				00110010101101110011101000110010101110010001000000111010

				00110100001100101001000000111001001101111011011110110110

				10010000001100100011101010111001001101001011011100110011

				10010000001110100011010000110000101110100001000000111010

				001101001011011010110010100101110"}

		//Fake messages
		if(MSG_MON_SCREEN_CUSTOM_MSG)
			dat += "Back - Reset
"

			dat += {"

Sender
					Sender's Job
					Recipient
					Message
"}
				//Sender  - Sender's Job  - Recepient - Message
				//Al Green- Your Dad   - Your Mom  - WHAT UP!?

			dat += {"
[customsender]
			[customjob]
			[customrecepient ? customrecepient.owner : "NONE"]
			[custommessage]
"}
			dat += "

Send"

		//Request Console Logs
		if(MSG_MON_SCREEN_REQUEST_LOGS)

			var/index = 0
			/* data_rc_msg
				X  - 5%
				var/rec_dpt = "Unspecified" //name of the person - 15%
				var/send_dpt = "Unspecified" //name of the sender- 15%
				var/message = "Blank" //transferred message  - 300px
				var/stamp = "Unstamped"  - 15%
				var/id_auth = "Unauthenticated"  - 15%
				var/priority = "Normal"  - 10%
			*/
			dat += "Back - Refresh
"
			dat += {"

X	Sending Dep.	Receiving Dep.
			Message	Stamp	ID Auth.	Priority.
"}
			for(var/datum/data_rc_msg/rc in linkedServer.rc_msgs)
				index++
				if(index > 3000)
					break
				// Del - Sender   - Recepient - Message
				// X   - Al Green - Your Mom  - WHAT UP!?
				dat += {"
X	[rc.send_dpt]
				[rc.rec_dpt]	[rc.message]	[rc.stamp]	[rc.id_auth]	[rc.priority]
"}
			dat += "
"

	message = defaultmsg
	var/datum/browser/popup = new(user, "hologram_console", name, 700, 700)
	popup.set_content(dat)
	popup.open()

/obj/machinery/computer/message_monitor/proc/BruteForce(mob/user)
	if(isnull(linkedServer))
		to_chat(user, span_warning("Could not complete brute-force: Linked Server Disconnected!"))
	else
		var/currentKey = linkedServer.decryptkey
		to_chat(user, span_warning("Brute-force completed! The key is '[currentKey]'."))
	hacking = FALSE
	screen = MSG_MON_SCREEN_MAIN // Return the screen back to normal

/obj/machinery/computer/message_monitor/proc/UnmagConsole()
	obj_flags &= ~EMAGGED

/obj/machinery/computer/message_monitor/proc/ResetMessage()
	customsender = "System Administrator"
	customrecepient = null
	custommessage = "This is a test, please ignore."
	customjob = "Admin"

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

	if(usr.contents.Find(src) || (in_range(src, usr) && isturf(loc)) || issilicon(usr))
		//Authenticate
		if (href_list["auth"])
			if(LINKED_SERVER_NONRESPONSIVE)
				message = noserver
			else if(auth)
				auth = FALSE
				screen = MSG_MON_SCREEN_MAIN
			else
				var/dkey = trim(input(usr, "Please enter the decryption key.") as text|null)
				if(dkey && dkey != "")
					if(linkedServer.decryptkey == dkey)
						auth = TRUE
					else
						message = incorrectkey

		//Turn the server on/off.
		if (href_list["active"])
			if(LINKED_SERVER_NONRESPONSIVE)
				message = noserver
			else if(auth)
				linkedServer.toggled = !linkedServer.toggled
		//Find a server
		if (href_list["find"])
			var/list/message_servers = list()
			for (var/obj/machinery/telecomms/message_server/M in GLOB.telecomms_list)
				message_servers += M

			if(message_servers.len > 1)
				linkedServer = input(usr, "Please select a server.", "Select a server.", null) as null|anything in message_servers
				message = span_alert("NOTICE: Server selected.")
			else if(message_servers.len > 0)
				linkedServer = message_servers[1]
				message =  span_notice("NOTICE: Only Single Server Detected - Server selected.")
			else
				message = noserver

		//View the logs - KEY REQUIRED
		if (href_list["view_logs"])
			if(LINKED_SERVER_NONRESPONSIVE)
				message = noserver
			else if(auth)
				screen = MSG_MON_SCREEN_LOGS

		//Clears the logs - KEY REQUIRED
		if (href_list["clear_logs"])
			if(LINKED_SERVER_NONRESPONSIVE)
				message = noserver
			else if(auth)
				linkedServer.pda_msgs = list()
				message = span_notice("NOTICE: Logs cleared.")
		//Clears the request console logs - KEY REQUIRED
		if (href_list["clear_requests"])
			if(LINKED_SERVER_NONRESPONSIVE)
				message = noserver
			else if(auth)
				linkedServer.rc_msgs = list()
				message = span_notice("NOTICE: Logs cleared.")
		//Change the password - KEY REQUIRED
		if (href_list["pass"])
			if(LINKED_SERVER_NONRESPONSIVE)
				message = noserver
			else if(auth)
				var/dkey = stripped_input(usr, "Please enter the decryption key.")
				if(dkey && dkey != "")
					if(linkedServer.decryptkey == dkey)
						var/newkey = stripped_input(usr,"Please enter the new key (3 - 16 characters max):")
						if(length(newkey) <= 3)
							message = span_notice("NOTICE: Decryption key too short!")
						else if(length(newkey) > 16)
							message = span_notice("NOTICE: Decryption key too long!")
						else if(newkey && newkey != "")
							linkedServer.decryptkey = newkey
						message = span_notice("NOTICE: Decryption key set.")
					else
						message = incorrectkey

		//Hack the Console to get the password
		if (href_list["hack"])
			var/mob/living/silicon/S = usr
			if(istype(S) && S.hack_software)
				hacking = TRUE
				screen = MSG_MON_SCREEN_HACKED
				//Time it takes to bruteforce is dependant on the password length.
				addtimer(CALLBACK(src, .proc/finish_bruteforce, usr), 100*length(linkedServer.decryptkey))

		//Delete the log.
		if (href_list["delete_logs"])
			//Are they on the view logs screen?
			if(screen == MSG_MON_SCREEN_LOGS)
				if(LINKED_SERVER_NONRESPONSIVE)
					message = noserver
				else //if(istype(href_list["delete_logs"], /datum/data_pda_msg))
					linkedServer.pda_msgs -= locate(href_list["delete_logs"]) in linkedServer.pda_msgs
					message = span_notice("NOTICE: Log Deleted!")
		//Delete the request console log.
		if (href_list["delete_requests"])
			//Are they on the view logs screen?
			if(screen == MSG_MON_SCREEN_REQUEST_LOGS)
				if(LINKED_SERVER_NONRESPONSIVE)
					message = noserver
				else //if(istype(href_list["delete_logs"], /datum/data_pda_msg))
					linkedServer.rc_msgs -= locate(href_list["delete_requests"]) in linkedServer.rc_msgs
					message = span_notice("NOTICE: Log Deleted!")
		//Create a custom message
		if (href_list["msg"])
			if(LINKED_SERVER_NONRESPONSIVE)
				message = noserver
			else if(auth)
				screen = MSG_MON_SCREEN_CUSTOM_MSG
		//Fake messaging selection - KEY REQUIRED
		if (href_list["select"])
			if(LINKED_SERVER_NONRESPONSIVE)
				message = noserver
				screen = MSG_MON_SCREEN_MAIN
			else
				switch(href_list["select"])

					//Reset
					if("Reset")
						ResetMessage()

					//Select Your Name
					if("Sender")
						customsender = stripped_input(usr, "Please enter the sender's name.") || customsender

					//Select Receiver
					if("Recepient")
						//Get out list of viable PDAs
						var/list/obj/item/pda/sendPDAs = get_viewable_pdas()
						if(GLOB.PDAs && GLOB.PDAs.len > 0)
							customrecepient = input(usr, "Select a PDA from the list.") as null|anything in sendPDAs
						else
							customrecepient = null

					//Enter custom job
					if("RecJob")
						customjob = stripped_input(usr, "Please enter the sender's job.") || customjob

					//Enter message
					if("Message")
						custommessage = stripped_input(usr, "Please enter your message.") || custommessage

					//Send message
					if("Send")
						if(isnull(customsender) || customsender == "")
							customsender = "UNKNOWN"

						if(isnull(customrecepient))
							message = span_notice("NOTICE: No recepient selected!")
							return attack_hand(usr)

						if(isnull(custommessage) || custommessage == "")
							message = span_notice("NOTICE: No message entered!")
							return attack_hand(usr)

						var/datum/signal/subspace/messaging/pda/signal = new(src, list(
							"name" = "[customsender]",
							"job" = "[customjob]",
							"message" = custommessage,
							"targets" = list("[customrecepient.owner] ([customrecepient.ownjob])")
						))
						// this will log the signal and transmit it to the target
						linkedServer.receive_information(signal, null)
						usr.log_message("(PDA: [name] | [usr.real_name]) sent \"[custommessage]\" to [signal.format_target()]", LOG_PDA)

		//Request Console Logs - KEY REQUIRED
		if(href_list["view_requests"])
			if(LINKED_SERVER_NONRESPONSIVE)
				message = noserver
			else if(auth)
				screen = MSG_MON_SCREEN_REQUEST_LOGS

		if (href_list["back"])
			screen = MSG_MON_SCREEN_MAIN

	return attack_hand(usr)

/obj/machinery/computer/message_monitor/proc/finish_bruteforce(mob/user)
	if(!QDELETED(user))
		BruteForce(user)
		return
	hacking = FALSE
	screen = MSG_MON_SCREEN_MAIN

#undef MSG_MON_SCREEN_MAIN
#undef MSG_MON_SCREEN_LOGS
#undef MSG_MON_SCREEN_HACKED
#undef MSG_MON_SCREEN_CUSTOM_MSG
#undef MSG_MON_SCREEN_REQUEST_LOGS

#undef LINKED_SERVER_NONRESPONSIVE

/obj/item/paper/monitorkey
	name = "monitor decryption key"

/obj/item/paper/monitorkey/Initialize(mapload, obj/machinery/telecomms/message_server/server)
	..()
	if (server)
		print(server)
		return INITIALIZE_HINT_NORMAL
	else
		return INITIALIZE_HINT_LATELOAD

/obj/item/paper/monitorkey/proc/print(obj/machinery/telecomms/message_server/server)
	info = "
Daily Key Reset

The new message monitor key is '[server.decryptkey]'.
Please keep this a secret and away from the clown.
If necessary, change the password to a more secure one."
	add_overlay("paper_words")

/obj/item/paper/monitorkey/LateInitialize()
	for (var/obj/machinery/telecomms/message_server/preset/server in GLOB.telecomms_list)
		if (server.decryptkey)
			print(server)
			break


code:
/*
	Telecomms monitor tracks the overall trafficing of a telecommunications network
	and displays a heirarchy of linked machines.
*/

/obj/machinery/computer/telecomms/monitor
	name = "telecommunications monitoring console"
	icon_screen = "comm_monitor"
	desc = "Monitors the details of the telecommunications network it's synced with."

	var/screen = 0 // the screen number:
	var/list/machinelist = list() // the machines located by the computer
	var/obj/machinery/telecomms/SelectedMachine

	var/network = "NULL" // the network to probe

	var/temp = "" // temporary feedback messages
	circuit = /obj/item/circuitboard/computer/comm_monitor

/obj/machinery/computer/telecomms/monitor/ui_interact(mob/user)
	. = ..()
	var/dat = "<TITLE>Telecommunications Monitor</TITLE>Telecommunications Monitor"

	switch(screen)

	  // --- Main Menu ---

		if(0)
			dat += "
[temp]

"
			dat += "
Current Network: [network]
"
			if(machinelist.len)
				dat += "
Detected Network Entities:
"
				for(var/obj/machinery/telecomms/T in machinelist)
					dat += "
[REF(T)] [T.name] ([T.id])
"
				dat += "
"
				dat += "
\[Flush Buffer\]"
			else
				dat += "\[Probe Network\]"

	  // --- Viewing Machine ---

		if(1)
			dat += "
[temp]
"
			dat += "\[Main Menu\]"
			dat += "
Current Network: [network]
"
			dat += "Selected Network Entity: [SelectedMachine.name] ([SelectedMachine.id])
"
			dat += "Linked Entities:
"
			for(var/obj/machinery/telecomms/T in SelectedMachine.links)
				if(!T.hide)
					dat += "
[REF(T.id)] [T.name] ([T.id])
"
			dat += "
"

	user << browse(dat, "window=comm_monitor;size=575x400")
	onclose(user, "server_control")

	temp = ""
	return

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

	add_fingerprint(usr)
	usr.set_machine(src)

	if(href_list["viewmachine"])
		screen = 1
		for(var/obj/machinery/telecomms/T in machinelist)
			if(T.id == href_list["viewmachine"])
				SelectedMachine = T
				break

	if(href_list["operation"])
		switch(href_list["operation"])

			if("release")
				machinelist = list()
				screen = 0

			if("mainmenu")
				screen = 0

			if("probe")
				if(machinelist.len > 0)
					temp = "- FAILED: CANNOT PROBE WHEN BUFFER FULL -"

				else
					for(var/obj/machinery/telecomms/T in urange(25, src))
						if(T.network == network)
							machinelist.Add(T)

					if(!machinelist.len)
						temp = "- FAILED: UNABLE TO LOCATE NETWORK ENTITIES IN \[[network]\] -"
					else
						temp = "- [machinelist.len] ENTITIES LOCATED & BUFFERED -"

					screen = 0

	if(href_list["network"])

		var/newnet = stripped_input(usr, "Which network do you want to view?", "Comm Monitor", network)
		if(newnet && ((usr in range(1, src)) || issilicon(usr)))
			if(length(newnet) > 15)
				temp = "- FAILED: NETWORK TAG STRING TOO LENGHTLY -"

			else
				network = newnet
				screen = 0
				machinelist = list()
				temp = "- NEW NETWORK TAG SET IN ADDRESS \[[network]\] -"

	updateUsrDialog()
	return

/obj/machinery/computer/telecomms/monitor/attackby()
	. = ..()
	updateUsrDialog()

repo_name: AnalWerewolf/sundown

license: mit


code:
/*

	All telecommunications interactions:

*/

/obj/machinery/telecomms
	var/temp = "" // output message
	var/tempfreq = FREQ_COMMON
	var/mob/living/operator

/obj/machinery/telecomms/attackby(obj/item/P, mob/user, params)

	var/icon_closed = initial(icon_state)
	var/icon_open = "[initial(icon_state)]_o"
	if(!on)
		icon_closed = "[initial(icon_state)]_off"
		icon_open = "[initial(icon_state)]_o_off"

	if(default_deconstruction_screwdriver(user, icon_open, icon_closed, P))
		return
	// Using a multitool lets you access the receiver's interface
	else if(P.tool_behaviour == TOOL_MULTITOOL)
		attack_hand(user)

	else if(default_deconstruction_crowbar(P))
		return
	else
		return ..()

/obj/machinery/telecomms/ui_interact(mob/user, datum/tgui/ui)
	operator = user
	ui = SStgui.try_update_ui(user, src, ui)
	if(!ui)
		ui = new(user, src, "Telecomms")
		ui.open()

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

	data += add_option()

	data["minfreq"] = MIN_FREE_FREQ
	data["maxfreq"] = MAX_FREE_FREQ
	data["frequency"] = tempfreq

	var/obj/item/multitool/heldmultitool = get_multitool(user)
	data["multitool"] = heldmultitool

	if(heldmultitool)
		data["multibuff"] = heldmultitool.buffer

	data["toggled"] = toggled
	data["id"] = id
	data["network"] = network
	data["prefab"] = autolinkers.len ? TRUE : FALSE

	var/list/linked = list()
	var/i = 0
	data["linked"] = list()
	for(var/obj/machinery/telecomms/machine in links)
		i++
		if(machine.hide && !hide)
			continue
		var/list/entry = list()
		entry["index"] = i
		entry["name"] = machine.name
		entry["id"] = machine.id
		linked += list(entry)
	data["linked"] = linked

	var/list/frequencies = list()
	data["frequencies"] = list()
	for(var/x in freq_listening)
		frequencies += list(x)
	data["frequencies"] = frequencies

	return data

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

	if(!issilicon(usr))
		if(!istype(usr.get_active_held_item(), /obj/item/multitool))
			return

	var/obj/item/multitool/heldmultitool = get_multitool(operator)

	switch(action)
		if("toggle")
			toggled = !toggled
			update_power()
			update_appearance()
			log_game("[key_name(operator)] toggled [toggled ? "On" : "Off"] [src] at [AREACOORD(src)].")
			. = TRUE
		if("id")
			if(params["value"])
				if(length(params["value"]) > 32)
					to_chat(operator, span_warning("Error: Machine ID too long!"))
					playsound(src, 'sound/machines/buzz-sigh.ogg', 50, TRUE)
					return
				else
					id = params["value"]
					log_game("[key_name(operator)] has changed the ID for [src] at [AREACOORD(src)] to [id].")
					. = TRUE
		if("network")
			if(params["value"])
				if(length(params["value"]) > 15)
					to_chat(operator, span_warning("Error: Network name too long!"))
					playsound(src, 'sound/machines/buzz-sigh.ogg', 50, TRUE)
					return
				else
					for(var/obj/machinery/telecomms/T in links)
						T.links.Remove(src)
					network = params["value"]
					links = list()
					log_game("[key_name(operator)] has changed the network for [src] at [AREACOORD(src)] to [network].")
					. = TRUE
		if("tempfreq")
			if(params["value"])
				tempfreq = text2num(params["value"]) * 10
		if("freq")
			if(tempfreq == FREQ_SYNDICATE)
				to_chat(operator, span_warning("Error: Interference preventing filtering frequency: \"[tempfreq / 10] kHz\""))
				playsound(src, 'sound/machines/buzz-sigh.ogg', 50, TRUE)
			else
				if(!(tempfreq in freq_listening))
					freq_listening.Add(tempfreq)
					log_game("[key_name(operator)] added frequency [tempfreq] for [src] at [AREACOORD(src)].")
					. = TRUE
		if("delete")
			freq_listening.Remove(params["value"])
			log_game("[key_name(operator)] added removed frequency [params["value"]] for [src] at [AREACOORD(src)].")
			. = TRUE
		if("unlink")
			var/obj/machinery/telecomms/T = links[text2num(params["value"])]
			if(T)
				// Remove link entries from both T and src.
				if(T.links)
					T.links.Remove(src)
				links.Remove(T)
				log_game("[key_name(operator)] unlinked [src] and [T] at [AREACOORD(src)].")
				. = TRUE
		if("link")
			if(heldmultitool)
				var/obj/machinery/telecomms/T = heldmultitool.buffer
				if(istype(T) && T != src)
					if(!(src in T.links))
						T.links += src
					if(!(T in links))
						links += T
						log_game("[key_name(operator)] linked [src] for [T] at [AREACOORD(src)].")
						. = TRUE
		if("buffer")
			heldmultitool.buffer = src
			. = TRUE
		if("flush")
			heldmultitool.buffer = null
			. = TRUE

	add_act(action, params)
	. = TRUE

/obj/machinery/telecomms/proc/add_option()
	return

/obj/machinery/telecomms/bus/add_option()
	var/list/data = list()
	data["type"] = "bus"
	data["changefrequency"] = change_frequency
	return data

/obj/machinery/telecomms/relay/add_option()
	var/list/data = list()
	data["type"] = "relay"
	data["broadcasting"] = broadcasting
	data["receiving"] = receiving
	return data

/obj/machinery/telecomms/proc/add_act(action, params)

/obj/machinery/telecomms/relay/add_act(action, params)
	switch(action)
		if("broadcast")
			broadcasting = !broadcasting
			. = TRUE
		if("receive")
			receiving = !receiving
			. = TRUE

/obj/machinery/telecomms/bus/add_act(action, params)
	switch(action)
		if("change_freq")
			var/newfreq = text2num(params["value"]) * 10
			if(newfreq)
				if(newfreq < 10000)
					change_frequency = newfreq
					. = TRUE
				else
					change_frequency = 0

// Returns a multitool from a user depending on their mobtype.

/obj/machinery/telecomms/proc/get_multitool(mob/user)
	var/obj/item/multitool/P = null
	// Let's double check
	if(!issilicon(user) && istype(user.get_active_held_item(), /obj/item/multitool))
		P = user.get_active_held_item()
	else if(isAI(user))
		var/mob/living/silicon/ai/U = user
		P = U.aiMulti
	else if(iscyborg(user) && in_range(user, src))
		if(istype(user.get_active_held_item(), /obj/item/multitool))
			P = user.get_active_held_item()
	return P

/obj/machinery/telecomms/proc/canAccess(mob/user)
	if(issilicon(user) || in_range(user, src))
		return TRUE
	return FALSE


code:
/*
	Basically just an empty shell for receiving and broadcasting radio messages. Not
	very flexible, but it gets the job done.
*/

/obj/machinery/telecomms/allinone
	name = "telecommunications mainframe"
	icon_state = "comm_server"
	desc = "A compact machine used for portable subspace telecommunications processing."
	density = TRUE
	use_power = NO_POWER_USE
	idle_power_usage = 0
	var/intercept = FALSE  // If true, only works on the Syndicate frequency.

/obj/machinery/telecomms/allinone/indestructable
	resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
	flags_1 = NODECONSTRUCT_1

/obj/machinery/telecomms/allinone/Initialize(mapload)
	. = ..()
	if (intercept)
		freq_listening = list(FREQ_SYNDICATE)

/obj/machinery/telecomms/allinone/receive_signal(datum/signal/subspace/signal)
	if(!istype(signal) || signal.transmission_method != TRANSMISSION_SUBSPACE)  // receives on subspace only
		return
	if(!on || !is_freq_listening(signal))  // has to be on to receive messages
		return
	if (!intercept && !(z in signal.levels) && !(0 in signal.levels))  // has to be syndicate or on the right level
		return

	// Decompress the signal and mark it done
	if (intercept)
		signal.levels += 0  // Signal is broadcast to agents anywhere

	signal.data["compression"] = 0
	signal.mark_done()
	if(signal.data["slow"] > 0)
		sleep(signal.data["slow"]) // simulate the network lag if necessary
	signal.broadcast()

/obj/machinery/telecomms/allinone/attackby(obj/item/P, mob/user, params)
	if(P.tool_behaviour == TOOL_MULTITOOL)
		return attack_hand(user)


code:
/*
	The broadcaster sends processed messages to all radio devices in the game. They
	do not have to be headsets; intercoms and station-bounced radios suffice.

	They receive their message from a server after the message has been logged.
*/

GLOBAL_LIST_EMPTY(recentmessages) // global list of recent messages broadcasted : used to circumvent massive radio spam
GLOBAL_VAR_INIT(message_delay, 0) // To make sure restarting the recentmessages list is kept in sync

/obj/machinery/telecomms/broadcaster
	name = "subspace broadcaster"
	icon_state = "broadcaster"
	desc = "A dish-shaped machine used to broadcast processed subspace signals."
	density = TRUE
	use_power = IDLE_POWER_USE
	idle_power_usage = 25
	circuit = /obj/item/circuitboard/machine/telecomms/broadcaster

/obj/machinery/telecomms/broadcaster/receive_information(datum/signal/subspace/signal, obj/machinery/telecomms/machine_from)
	// Don't broadcast rejected signals
	if(!istype(signal))
		return
	if(signal.data["reject"])
		return
	if(!signal.data["message"])
		return

	// Prevents massive radio spam
	signal.mark_done()
	var/datum/signal/subspace/original = signal.original
	if(original && ("compression" in signal.data))
		original.data["compression"] = signal.data["compression"]

	var/turf/T = get_turf(src)
	if (T)
		signal.levels |= T.z

	var/signal_message = "[signal.frequency]:[signal.data["message"]]:[signal.data["name"]]"
	if(signal_message in GLOB.recentmessages)
		return
	GLOB.recentmessages.Add(signal_message)

	if(signal.data["slow"] > 0)
		sleep(signal.data["slow"]) // simulate the network lag if necessary

	signal.broadcast()

	if(!GLOB.message_delay)
		GLOB.message_delay = TRUE
		addtimer(CALLBACK(GLOBAL_PROC, .proc/end_message_delay), 1 SECONDS)

	/* --- Do a snazzy animation! --- */
	flick("broadcaster_send", src)

/proc/end_message_delay()
	GLOB.message_delay = FALSE
	GLOB.recentmessages = list()

/obj/machinery/telecomms/broadcaster/Destroy()
	// In case message_delay is left on 1, otherwise it won't reset the list and people can't say the same thing twice anymore.
	if(GLOB.message_delay)
		GLOB.message_delay = 0
	return ..()

//Preset Broadcasters

//--PRESET LEFT--//

/obj/machinery/telecomms/broadcaster/preset_left
	id = "Broadcaster A"
	network = "tcommsat"
	autolinkers = list("broadcasterA")

//--PRESET RIGHT--//

/obj/machinery/telecomms/broadcaster/preset_right
	id = "Broadcaster B"
	network = "tcommsat"
	autolinkers = list("broadcasterB")

/obj/machinery/telecomms/broadcaster/preset_left/birdstation
	name = "Broadcaster"


code:
/*
	The bus mainframe idles and waits for hubs to relay them signals. They act
	as junctions for the network.

	They transfer uncompressed subspace packets to processor units, and then take
	the processed packet to a server for logging.

	Link to a subspace hub if it can't send to a server.
*/

/obj/machinery/telecomms/bus
	name = "bus mainframe"
	icon_state = "bus"
	desc = "A mighty piece of hardware used to send massive amounts of data quickly."
	density = TRUE
	use_power = IDLE_POWER_USE
	idle_power_usage = 50
	netspeed = 40
	circuit = /obj/item/circuitboard/machine/telecomms/bus
	var/change_frequency = 0

/obj/machinery/telecomms/bus/receive_information(datum/signal/subspace/signal, obj/machinery/telecomms/machine_from)
	if(!istype(signal) || !is_freq_listening(signal))
		return

	if(change_frequency && signal.frequency != FREQ_SYNDICATE)
		signal.frequency = change_frequency

	if(!istype(machine_from, /obj/machinery/telecomms/processor) && machine_from != src) // Signal must be ready (stupid assuming machine), let's send it
		// send to one linked processor unit
		if(relay_information(signal, /obj/machinery/telecomms/processor))
			return

		// failed to send to a processor, relay information anyway
		signal.data["slow"] += rand(1, 5) // slow the signal down only slightly

	// Try sending it!
	var/list/try_send = list(signal.server_type, /obj/machinery/telecomms/hub, /obj/machinery/telecomms/broadcaster)

	var/i = 0
	for(var/send in try_send)
		if(i)
			signal.data["slow"] += rand(0, 1) // slow the signal down only slightly
		i++
		if(relay_information(signal, send))
			break

//Preset Buses

/obj/machinery/telecomms/bus/preset_one
	id = "Bus 1"
	network = "tcommsat"
	freq_listening = list(FREQ_SCIENCE, FREQ_MEDICAL)
	autolinkers = list("processor1", "science", "medical")

/obj/machinery/telecomms/bus/preset_two
	id = "Bus 2"
	network = "tcommsat"
	freq_listening = list(FREQ_SUPPLY, FREQ_SERVICE)
	autolinkers = list("processor2", "supply", "service")

/obj/machinery/telecomms/bus/preset_three
	id = "Bus 3"
	network = "tcommsat"
	freq_listening = list(FREQ_SECURITY, FREQ_COMMAND)
	autolinkers = list("processor3", "security", "command")

/obj/machinery/telecomms/bus/preset_four
	id = "Bus 4"
	network = "tcommsat"
	freq_listening = list(FREQ_ENGINEERING)
	autolinkers = list("processor4", "engineering", "common", "messaging")

/obj/machinery/telecomms/bus/preset_four/Initialize(mapload)
	. = ..()
	for(var/i = MIN_FREQ, i <= MAX_FREQ, i += 2)
		freq_listening |= i

/obj/machinery/telecomms/bus/preset_one/birdstation
	name = "Bus"
	autolinkers = list("processor1", "common", "messaging")
	freq_listening = list()


code:
/*
	The HUB idles until it receives information. It then passes on that information
	depending on where it came from.

	This is the heart of the Telecommunications Network, sending information where it
	is needed. It mainly receives information from long-distance Relays and then sends
	that information to be processed. Afterwards it gets the uncompressed information
	from Servers/Buses and sends that back to the relay, to then be broadcasted.
*/

/obj/machinery/telecomms/hub
	name = "telecommunication hub"
	icon_state = "hub"
	desc = "A mighty piece of hardware used to send/receive massive amounts of data."
	density = TRUE
	use_power = IDLE_POWER_USE
	idle_power_usage = 80
	long_range_link = TRUE
	netspeed = 40
	circuit = /obj/item/circuitboard/machine/telecomms/hub

/obj/machinery/telecomms/hub/receive_information(datum/signal/signal, obj/machinery/telecomms/machine_from)
	if(!is_freq_listening(signal))
		return

	if(istype(machine_from, /obj/machinery/telecomms/receiver))
		// It's probably compressed so send it to the bus.
		relay_information(signal, /obj/machinery/telecomms/bus, TRUE)
	else
		// Send it to each relay so their levels get added...
		relay_information(signal, /obj/machinery/telecomms/relay)
		// Then broadcast that signal to
		relay_information(signal, /obj/machinery/telecomms/broadcaster)

//Preset HUB

/obj/machinery/telecomms/hub/preset
	id = "Hub"
	network = "tcommsat"
	autolinkers = list("hub", "relay", "s_relay", "m_relay", "r_relay", "h_relay", "science", "medical",
	"supply", "service", "common", "command", "engineering", "security",
	"receiverA", "receiverB", "broadcasterA", "broadcasterB", "autorelay", "messaging")


code:
/*
	The equivalent of the server, for PDA and request console messages.
	Without it, PDA and request console messages cannot be transmitted.
	PDAs require the rest of the telecomms setup, but request consoles only
	require the message server.
*/

// A decorational representation of SSblackbox, usually placed alongside the message server. Also contains a traitor theft item.
/obj/machinery/blackbox_recorder
	icon = 'icons/obj/stationobjs.dmi'
	icon_state = "blackbox"
	name = "Blackbox Recorder"
	density = TRUE
	use_power = IDLE_POWER_USE
	idle_power_usage = 10
	active_power_usage = 100
	armor = list(MELEE = 25, BULLET = 10, LASER = 10, ENERGY = 0, BOMB = 0, BIO = 0, RAD = 0, FIRE = 50, ACID = 70)
	var/obj/item/stored

/obj/machinery/blackbox_recorder/Initialize(mapload)
	. = ..()
	stored = new /obj/item/blackbox(src)

/obj/machinery/blackbox_recorder/attack_hand(mob/living/user, list/modifiers)
	. = ..()
	if(stored)
		stored.forceMove(drop_location())
		if(Adjacent(user))
			user.put_in_hands(stored)
		stored = null
		to_chat(user, span_notice("You remove the blackbox from [src]. The tapes stop spinning."))
		update_appearance()
		return
	else
		to_chat(user, span_warning("It seems that the blackbox is missing..."))
		return

/obj/machinery/blackbox_recorder/attackby(obj/item/I, mob/living/user, params)
	if(istype(I, /obj/item/blackbox))
		if(HAS_TRAIT(I, TRAIT_NODROP) || !user.transferItemToLoc(I, src))
			to_chat(user, span_warning("[I] is stuck to your hand!"))
			return
		user.visible_message(span_notice("[user] clicks [I] into [src]!"), \
		span_notice("You press the device into [src], and it clicks into place. The tapes begin spinning again."))
		playsound(src, 'sound/machines/click.ogg', 50, TRUE)
		stored = I
		update_appearance()
		return
	return ..()

/obj/machinery/blackbox_recorder/Destroy()
	if(stored)
		stored.forceMove(loc)
		new /obj/effect/decal/cleanable/oil(loc)
	return ..()

/obj/machinery/blackbox_recorder/update_icon_state()
	icon_state = "blackbox[stored ? null : "_b"]"
	return ..()

/obj/item/blackbox
	name = "\proper the blackbox"
	desc = "A strange relic, capable of recording data on extradimensional vertices. It lives inside the blackbox recorder for safe keeping."
	icon = 'icons/obj/stationobjs.dmi'
	icon_state = "blackcube"
	lefthand_file = 'icons/mob/inhands/items_lefthand.dmi'
	righthand_file = 'icons/mob/inhands/items_righthand.dmi'
	w_class = WEIGHT_CLASS_BULKY
	resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | ACID_PROOF

#define MESSAGE_SERVER_FUNCTIONING_MESSAGE "This is an automated message. The messaging system is functioning correctly."

// The message server itself.
/obj/machinery/telecomms/message_server
	icon_state = "message_server"
	name = "Messaging Server"
	desc = "A machine that processes and routes PDA and request console messages."
	density = TRUE
	use_power = IDLE_POWER_USE
	idle_power_usage = 10
	active_power_usage = 100
	circuit = /obj/item/circuitboard/machine/telecomms/message_server

	var/list/datum/data_pda_msg/pda_msgs = list()
	var/list/datum/data_rc_msg/rc_msgs = list()
	var/decryptkey = "password"
	var/calibrating = 15 MINUTES //Init reads this and adds world.time, then becomes 0 when that time has passed and the machine works

/obj/machinery/telecomms/message_server/Initialize(mapload)
	. = ..()
	if (!decryptkey)
		decryptkey = GenerateKey()

	if (calibrating)
		calibrating += world.time
		say("Calibrating... Estimated wait time: [rand(3, 9)] minutes.")
		pda_msgs += new /datum/data_pda_msg("System Administrator", "system", "This is an automated message. System calibration started at [station_time_timestamp()]")
	else
		pda_msgs += new /datum/data_pda_msg("System Administrator", "system", MESSAGE_SERVER_FUNCTIONING_MESSAGE)

/obj/machinery/telecomms/message_server/Destroy()
	for(var/obj/machinery/computer/message_monitor/monitor in GLOB.telecomms_list)
		if(monitor.linkedServer && monitor.linkedServer == src)
			monitor.linkedServer = null
	. = ..()

/obj/machinery/telecomms/message_server/examine(mob/user)
	. = ..()
	if(calibrating)
		. += span_warning("It's still calibrating.")

/obj/machinery/telecomms/message_server/proc/GenerateKey()
	var/newKey
	newKey += pick("the", "if", "of", "as", "in", "a", "you", "from", "to", "an", "too", "little", "snow", "dead", "drunk", "rosebud", "duck", "al", "le")
	newKey += pick("diamond", "beer", "mushroom", "assistant", "clown", "captain", "twinkie", "security", "nuke", "small", "big", "escape", "yellow", "gloves", "monkey", "engine", "nuclear", "ai")
	newKey += pick("1", "2", "3", "4", "5", "6", "7", "8", "9", "0")
	return newKey

/obj/machinery/telecomms/message_server/process()
	. = ..()
	if(calibrating && calibrating <= world.time)
		calibrating = 0
		pda_msgs += new /datum/data_pda_msg("System Administrator", "system", MESSAGE_SERVER_FUNCTIONING_MESSAGE)

/obj/machinery/telecomms/message_server/receive_information(datum/signal/subspace/messaging/signal, obj/machinery/telecomms/machine_from)
	// can't log non-message signals
	if(!istype(signal) || !signal.data["message"] || !on || calibrating)
		return

	// log the signal
	if(istype(signal, /datum/signal/subspace/messaging/pda))
		var/datum/signal/subspace/messaging/pda/PDAsignal = signal
		var/datum/data_pda_msg/M = new(PDAsignal.format_target(), "[PDAsignal.data["name"]] ([PDAsignal.data["job"]])", PDAsignal.data["message"], PDAsignal.data["photo"])
		pda_msgs += M
		signal.logged = M
	else if(istype(signal, /datum/signal/subspace/messaging/rc))
		var/datum/data_rc_msg/M = new(signal.data["rec_dpt"], signal.data["send_dpt"], signal.data["message"], signal.data["stamped"], signal.data["verified"], signal.data["priority"])
		signal.logged = M
		if(signal.data["send_dpt"]) // don't log messages not from a department but allow them to work
			rc_msgs += M
	signal.data["reject"] = FALSE

	// pass it along to either the hub or the broadcaster
	if(!relay_information(signal, /obj/machinery/telecomms/hub))
		relay_information(signal, /obj/machinery/telecomms/broadcaster)

/obj/machinery/telecomms/message_server/update_overlays()
	. = ..()

	if(calibrating)
		. += "message_server_calibrate"

// Root messaging signal datum
/datum/signal/subspace/messaging
	frequency = FREQ_COMMON
	server_type = /obj/machinery/telecomms/message_server
	var/datum/logged

/datum/signal/subspace/messaging/New(init_source, init_data)
	source = init_source
	data = init_data
	var/turf/T = get_turf(source)
	levels = list(T.z)
	if(!("reject" in data))
		data["reject"] = TRUE

/datum/signal/subspace/messaging/copy()
	var/datum/signal/subspace/messaging/copy = new type(source, data.Copy())
	copy.original = src
	copy.levels = levels
	return copy

// PDA signal datum
/datum/signal/subspace/messaging/pda/proc/format_target()
	if (length(data["targets"]) > 1)
		return "Everyone"
	return data["targets"][1]

/datum/signal/subspace/messaging/pda/proc/format_message()
	if (logged && data["photo"])
		return "\"[data["message"]]\" (Photo)"
	return "\"[data["message"]]\""

/datum/signal/subspace/messaging/pda/broadcast()
	if (!logged)  // Can only go through if a message server logs it
		return
	for (var/obj/item/pda/P in GLOB.PDAs)
		if ("[P.owner] ([P.ownjob])" in data["targets"])
			P.receive_message(src)

// Request Console signal datum
/datum/signal/subspace/messaging/rc/broadcast()
	if (!logged)  // Like /pda, only if logged
		return
	var/rec_dpt = ckey(data["rec_dpt"])
	for (var/obj/machinery/requests_console/Console in GLOB.allConsoles)
		if(ckey(Console.department) == rec_dpt || (data["ore_update"] && Console.receive_ore_updates))
			Console.createmessage(data["sender"], data["send_dpt"], data["message"], data["verified"], data["stamped"], data["priority"], data["notify_freq"])

// Log datums stored by the message server.
/datum/data_pda_msg
	var/sender = "Unspecified"
	var/recipient = "Unspecified"
	var/message = "Blank"  // transferred message
	var/datum/picture/picture  // attached photo
	var/automated = 0 //automated message

/datum/data_pda_msg/New(param_rec, param_sender, param_message, param_photo)
	if(param_rec)
		recipient = param_rec
	if(param_sender)
		sender = param_sender
	if(param_message)
		message = param_message
	if(param_photo)
		picture = param_photo

/datum/data_pda_msg/Topic(href,href_list)
	..()
	if(href_list["photo"])
		var/mob/M = usr
		M << browse_rsc(picture.picture_image, "pda_photo.png")
		M << browse("<meta http-equiv='Content-Type' content='text/html; charset=UTF-8'><title>PDA Photo</title>" \
		+ "" \
		+ "" \
		+ "", "window=pdaphoto;size=[picture.psize_x]x[picture.psize_y];can-close=true")
		onclose(M, "pdaphoto")

/datum/data_rc_msg
	var/rec_dpt = "Unspecified"  // receiving department
	var/send_dpt = "Unspecified"  // sending department
	var/message = "Blank"
	var/stamp = "Unstamped"
	var/id_auth = "Unauthenticated"
	var/priority = "Normal"

/datum/data_rc_msg/New(param_rec, param_sender, param_message, param_stamp, param_id_auth, param_priority)
	if(param_rec)
		rec_dpt = param_rec
	if(param_sender)
		send_dpt = param_sender
	if(param_message)
		message = param_message
	if(param_stamp)
		stamp = param_stamp
	if(param_id_auth)
		id_auth = param_id_auth
	if(param_priority)
		switch(param_priority)
			if(REQ_NORMAL_MESSAGE_PRIORITY)
				priority = "Normal"
			if(REQ_HIGH_MESSAGE_PRIORITY)
				priority = "High"
			if(REQ_EXTREME_MESSAGE_PRIORITY)
				priority = "Extreme"
			else
				priority = "Undetermined"

#undef MESSAGE_SERVER_FUNCTIONING_MESSAGE

/obj/machinery/telecomms/message_server/preset
	id = "Messaging Server"
	network = "tcommsat"
	autolinkers = list("messaging")
	decryptkey = null //random
	calibrating = 0


code:
/*
	The processor is a very simple machine that decompresses subspace signals and
	transfers them back to the original bus. It is essential in producing audible
	data.

	Link to servers if bus is not present
*/

/obj/machinery/telecomms/processor
	name = "processor unit"
	icon_state = "processor"
	desc = "This machine is used to process large quantities of information."
	density = TRUE
	use_power = IDLE_POWER_USE
	idle_power_usage = 30
	circuit = /obj/item/circuitboard/machine/telecomms/processor
	var/process_mode = 1 // 1 = Uncompress Signals, 0 = Compress Signals

/obj/machinery/telecomms/processor/receive_information(datum/signal/subspace/signal, obj/machinery/telecomms/machine_from)
	if(!is_freq_listening(signal))
		return

	if (!process_mode)
		signal.data["compression"] = 100 // even more compressed signal
	else if (signal.data["compression"])
		signal.data["compression"] = 0 // uncompress subspace signal

	if(istype(machine_from, /obj/machinery/telecomms/bus))
		relay_direct_information(signal, machine_from) // send the signal back to the machine
	else // no bus detected - send the signal to servers instead
		signal.data["slow"] += rand(5, 10) // slow the signal down
		relay_information(signal, signal.server_type)

//Preset Processors

/obj/machinery/telecomms/processor/preset_one
	id = "Processor 1"
	network = "tcommsat"
	autolinkers = list("processor1") // processors are sort of isolated; they don't need backward links

/obj/machinery/telecomms/processor/preset_two
	id = "Processor 2"
	network = "tcommsat"
	autolinkers = list("processor2")

/obj/machinery/telecomms/processor/preset_three
	id = "Processor 3"
	network = "tcommsat"
	autolinkers = list("processor3")

/obj/machinery/telecomms/processor/preset_four
	id = "Processor 4"
	network = "tcommsat"
	autolinkers = list("processor4")

/obj/machinery/telecomms/processor/preset_one/birdstation
	name = "Processor"


code:
/*
	The receiver idles and receives messages from subspace-compatible radio equipment;
	primarily headsets. Then they just relay this information to all linked devices,
	which would probably be network hubs.

	Link to Processor Units in case receiver can't send to bus units.
*/

/obj/machinery/telecomms/receiver
	name = "subspace receiver"
	icon_state = "broadcast receiver"
	desc = "This machine has a dish-like shape and green lights. It is designed to detect and process subspace radio activity."
	density = TRUE
	use_power = IDLE_POWER_USE
	idle_power_usage = 30
	circuit = /obj/item/circuitboard/machine/telecomms/receiver

/obj/machinery/telecomms/receiver/receive_signal(datum/signal/subspace/signal)
	if(!on || !istype(signal) || !check_receive_level(signal) || signal.transmission_method != TRANSMISSION_SUBSPACE)
		return
	if(!is_freq_listening(signal))
		return

	signal.levels = list()

	// send the signal to the hub if possible, or a bus otherwise
	if(!relay_information(signal, /obj/machinery/telecomms/hub))
		relay_information(signal, /obj/machinery/telecomms/bus)

/obj/machinery/telecomms/receiver/proc/check_receive_level(datum/signal/subspace/signal)
	if (z in signal.levels)
		return TRUE

	for(var/obj/machinery/telecomms/hub/H in links)
		for(var/obj/machinery/telecomms/relay/R in H.links)
			if(R.can_receive(signal) && (R.z in signal.levels))
				return TRUE

	return FALSE

//Preset Receivers

//--PRESET LEFT--//

/obj/machinery/telecomms/receiver/preset_left
	id = "Receiver A"
	network = "tcommsat"
	autolinkers = list("receiverA") // link to relay
	freq_listening = list(FREQ_SCIENCE, FREQ_MEDICAL, FREQ_SUPPLY, FREQ_SERVICE)

//--PRESET RIGHT--//

/obj/machinery/telecomms/receiver/preset_right
	id = "Receiver B"
	network = "tcommsat"
	autolinkers = list("receiverB") // link to relay
	freq_listening = list(FREQ_COMMAND, FREQ_ENGINEERING, FREQ_SECURITY)

	//Common and other radio frequencies for people to freely use
/obj/machinery/telecomms/receiver/preset_right/Initialize(mapload)
	. = ..()
	for(var/i = MIN_FREQ, i <= MAX_FREQ, i += 2)
		freq_listening |= i

/obj/machinery/telecomms/receiver/preset_left/birdstation
	name = "Receiver"
	freq_listening = list()


code:
/*
	The relay idles until it receives information. It then passes on that information
	depending on where it came from.

	The relay is needed in order to send information pass Z levels. It must be linked
	with a HUB, the only other machine that can send/receive pass Z levels.
*/

/obj/machinery/telecomms/relay
	name = "telecommunication relay"
	icon_state = "relay"
	desc = "A mighty piece of hardware used to send massive amounts of data far away."
	density = TRUE
	use_power = IDLE_POWER_USE
	idle_power_usage = 30
	netspeed = 5
	long_range_link = 1
	circuit = /obj/item/circuitboard/machine/telecomms/relay
	var/broadcasting = 1
	var/receiving = 1

/obj/machinery/telecomms/relay/receive_information(datum/signal/subspace/signal, obj/machinery/telecomms/machine_from)
	// Add our level and send it back
	var/turf/T = get_turf(src)
	if(can_send(signal) && T)
		signal.levels |= T.z

// Checks to see if it can send/receive.

/obj/machinery/telecomms/relay/proc/can(datum/signal/signal)
	if(!on)
		return FALSE
	if(!is_freq_listening(signal))
		return FALSE
	return TRUE

/obj/machinery/telecomms/relay/proc/can_send(datum/signal/signal)
	if(!can(signal))
		return FALSE
	return broadcasting

/obj/machinery/telecomms/relay/proc/can_receive(datum/signal/signal)
	if(!can(signal))
		return FALSE
	return receiving

//Preset Relay

/obj/machinery/telecomms/relay/preset
	network = "tcommsat"

/obj/machinery/telecomms/relay/Initialize(mapload)
	. = ..()
	if(autolinkers.len) //We want lateloaded presets to autolink (lateloaded aways/ruins/shuttles)
		return INITIALIZE_HINT_LATELOAD

/obj/machinery/telecomms/relay/preset/station
	id = "Station Relay"
	autolinkers = list("s_relay")

/obj/machinery/telecomms/relay/preset/telecomms
	id = "Telecomms Relay"
	autolinkers = list("relay")

/obj/machinery/telecomms/relay/preset/mining
	id = "Mining Relay"
	autolinkers = list("m_relay")

/obj/machinery/telecomms/relay/preset/ruskie
	id = "Ruskie Relay"
	hide = 1
	toggled = FALSE
	autolinkers = list("r_relay")

//Generic preset relay
/obj/machinery/telecomms/relay/preset/auto
	hide = TRUE
	autolinkers = list("autorelay")


code:
/*
	The server logs all traffic and signal data. Once it records the signal, it sends
	it to the subspace broadcaster.

	Store a maximum of 100 logs and then deletes them.
*/

/obj/machinery/telecomms/server
	name = "telecommunication server"
	icon_state = "comm_server"
	desc = "A machine used to store data and network statistics."
	density = TRUE
	use_power = IDLE_POWER_USE
	idle_power_usage = 15
	circuit = /obj/item/circuitboard/machine/telecomms/server
	var/list/log_entries = list()
	var/totaltraffic = 0 // gigabytes (if > 1024, divide by 1024 -> terrabytes)

/obj/machinery/telecomms/server/receive_information(datum/signal/subspace/vocal/signal, obj/machinery/telecomms/machine_from)
	// can't log non-vocal signals
	if(!istype(signal) || !signal.data["message"] || !is_freq_listening(signal))
		return

	if(traffic > 0)
		totaltraffic += traffic // add current traffic to total traffic

	// Delete particularly old logs
	if (log_entries.len >= 400)
		log_entries.Cut(1, 2)

	var/datum/comm_log_entry/log = new
	log.parameters["mobtype"] = signal.virt.source.type
	log.parameters["name"] = signal.data["name"]
	log.parameters["job"] = signal.data["job"]
	log.parameters["message"] = signal.data["message"]
	log.parameters["language"] = signal.language

	// If the signal is still compressed, make the log entry gibberish
	var/compression = signal.data["compression"]
	if(compression > 0)
		log.input_type = "Corrupt File"
		var/replace_characters = compression >= 20 ? TRUE : FALSE
		log.parameters["name"] = Gibberish(signal.data["name"], replace_characters)
		log.parameters["job"] = Gibberish(signal.data["job"], replace_characters)
		log.parameters["message"] = Gibberish(signal.data["message"], replace_characters)

	// Give the log a name and store it
	var/identifier = num2text( rand(-1000,1000) + world.time )
	log.name = "data packet ([md5(identifier)])"
	log_entries.Add(log)

	var/can_send = relay_information(signal, /obj/machinery/telecomms/hub)
	if(!can_send)
		relay_information(signal, /obj/machinery/telecomms/broadcaster)

// Simple log entry datum
/datum/comm_log_entry
	var/input_type = "Speech File"
	var/name = "data packet (#)"
	var/parameters = list()  // copied from signal.data above

// Preset Servers
/obj/machinery/telecomms/server/presets
	network = "tcommsat"

/obj/machinery/telecomms/server/presets/Initialize(mapload)
	. = ..()
	name = id

/obj/machinery/telecomms/server/presets/science
	id = "Science Server"
	freq_listening = list(FREQ_SCIENCE)
	autolinkers = list("science")

/obj/machinery/telecomms/server/presets/medical
	id = "Medical Server"
	freq_listening = list(FREQ_MEDICAL)
	autolinkers = list("medical")

/obj/machinery/telecomms/server/presets/supply
	id = "Supply Server"
	freq_listening = list(FREQ_SUPPLY)
	autolinkers = list("supply")

/obj/machinery/telecomms/server/presets/service
	id = "Service Server"
	freq_listening = list(FREQ_SERVICE)
	autolinkers = list("service")

/obj/machinery/telecomms/server/presets/common
	id = "Common Server"
	freq_listening = list()
	autolinkers = list("common")

//Common and other radio frequencies for people to freely use
/obj/machinery/telecomms/server/presets/common/Initialize(mapload)
	. = ..()
	for(var/i = MIN_FREQ, i <= MAX_FREQ, i += 2)
		freq_listening |= i

/obj/machinery/telecomms/server/presets/command
	id = "Command Server"
	freq_listening = list(FREQ_COMMAND)
	autolinkers = list("command")

/obj/machinery/telecomms/server/presets/engineering
	id = "Engineering Server"
	freq_listening = list(FREQ_ENGINEERING)
	autolinkers = list("engineering")

/obj/machinery/telecomms/server/presets/security
	id = "Security Server"
	freq_listening = list(FREQ_SECURITY)
	autolinkers = list("security")

/obj/machinery/telecomms/server/presets/common/birdstation/Initialize(mapload)
	. = ..()
	freq_listening = list()


code:
/*
	Hello, friends, this is Doohl from sexylands. You may be wondering what this
	monstrous code file is. Sit down, boys and girls, while I tell you the tale.

	The telecom machines were designed to be compatible with any radio
	signals, provided they use subspace transmission. Currently they are only used for
	headsets, but they can eventually be outfitted for real COMPUTER networks. This
	is just a skeleton, ladies and gentlemen.

	Look at radio.dm for the prequel to this code.
*/

GLOBAL_LIST_EMPTY(telecomms_list)

/obj/machinery/telecomms
	icon = 'icons/obj/machines/telecomms.dmi'
	critical_machine = TRUE
	var/list/links = list() // list of machines this machine is linked to
	var/traffic = 0 // value increases as traffic increases
	var/netspeed = 2.5 // how much traffic to lose per second (50 gigabytes/second * netspeed)
	var/list/autolinkers = list() // list of text/number values to link with
	var/id = "NULL" // identification string
	var/network = "NULL" // the network of the machinery

	var/list/freq_listening = list() // list of frequencies to tune into: if none, will listen to all

	var/on = TRUE
	var/toggled = TRUE // Is it toggled on
	var/long_range_link = FALSE  // Can you link it across Z levels or on the otherside of the map? (Relay & Hub)
	var/hide = FALSE  // Is it a hidden machine?

	///Looping sounds for any servers
	var/datum/looping_sound/server/soundloop

/obj/machinery/telecomms/proc/relay_information(datum/signal/subspace/signal, filter, copysig, amount = 20)
	// relay signal to all linked machinery that are of type [filter]. If signal has been sent [amount] times, stop sending

	if(!on)
		return
	var/send_count = 0

	// Apply some lag based on traffic rates
	var/netlag = round(traffic / 50)
	if(netlag > signal.data["slow"])
		signal.data["slow"] = netlag

	// Loop through all linked machines and send the signal or copy.
	for(var/obj/machinery/telecomms/machine in links)
		if(filter && !istype( machine, filter ))
			continue
		if(!machine.on)
			continue
		if(amount && send_count >= amount)
			break
		if(z != machine.loc.z && !long_range_link && !machine.long_range_link)
			continue

		send_count++
		if(machine.is_freq_listening(signal))
			machine.traffic++

		if(copysig)
			machine.receive_information(signal.copy(), src)
		else
			machine.receive_information(signal, src)

	if(send_count > 0 && is_freq_listening(signal))
		traffic++

	return send_count

/obj/machinery/telecomms/proc/relay_direct_information(datum/signal/signal, obj/machinery/telecomms/machine)
	// send signal directly to a machine
	machine.receive_information(signal, src)

/obj/machinery/telecomms/proc/receive_information(datum/signal/signal, obj/machinery/telecomms/machine_from)
	// receive information from linked machinery

/obj/machinery/telecomms/proc/is_freq_listening(datum/signal/signal)
	// return TRUE if found, FALSE if not found
	return signal && (!freq_listening.len || (signal.frequency in freq_listening))

/obj/machinery/telecomms/Initialize(mapload)
	. = ..()
	soundloop = new(src, on)
	GLOB.telecomms_list += src
	if(mapload && autolinkers.len)
		return INITIALIZE_HINT_LATELOAD

/obj/machinery/telecomms/LateInitialize()
	..()
	for(var/obj/machinery/telecomms/T in (long_range_link ? GLOB.telecomms_list : urange(20, src, 1)))
		add_link(T)

/obj/machinery/telecomms/Destroy()
	GLOB.telecomms_list -= src
	QDEL_NULL(soundloop)
	for(var/obj/machinery/telecomms/comm in GLOB.telecomms_list)
		comm.links -= src
	links = list()
	return ..()

// Used in auto linking
/obj/machinery/telecomms/proc/add_link(obj/machinery/telecomms/T)
	var/turf/position = get_turf(src)
	var/turf/T_position = get_turf(T)
	if((position.z == T_position.z) || (long_range_link && T.long_range_link))
		if(src != T)
			for(var/x in autolinkers)
				if(x in T.autolinkers)
					links |= T
					T.links |= src

/obj/machinery/telecomms/update_icon_state()
	icon_state = "[initial(icon_state)][panel_open ? "_o" : null][on ? null : "_off"]"
	return ..()

/obj/machinery/telecomms/proc/update_power()

	if(toggled)
		if(machine_stat & (BROKEN|NOPOWER|EMPED)) // if powered, on. if not powered, off. if too damaged, off
			on = FALSE
			soundloop.stop()
		else
			on = TRUE
			soundloop.start()
	else
		on = FALSE
		soundloop.stop()

/obj/machinery/telecomms/process(delta_time)
	update_power()

	// Update the icon
	update_appearance()

	if(traffic > 0)
		traffic -= netspeed * delta_time

/obj/machinery/telecomms/emp_act(severity)
	. = ..()
	if(. & EMP_PROTECT_SELF)
		return
	if(prob(100/severity) && !(machine_stat & EMPED))
		set_machine_stat(machine_stat | EMPED)
		var/duration = (300 * 10)/severity
		addtimer(CALLBACK(src, .proc/de_emp), rand(duration - 20, duration + 20))

/obj/machinery/telecomms/proc/de_emp()
	set_machine_stat(machine_stat & ~EMPED)


code:
/obj/machinery/teleport
	name = "teleport"
	icon = 'icons/obj/machines/teleporter.dmi'
	density = TRUE

/obj/machinery/teleport/hub
	name = "teleporter hub"
	desc = "It's the hub of a teleporting machine."
	icon_state = "tele0"
	base_icon_state = "tele"
	use_power = IDLE_POWER_USE
	idle_power_usage = 10
	active_power_usage = 2000
	circuit = /obj/item/circuitboard/machine/teleporter_hub
	var/accuracy = 0
	var/obj/machinery/teleport/station/power_station
	var/calibrated = FALSE//Calibration prevents mutation

/obj/machinery/teleport/hub/Initialize(mapload)
	. = ..()
	link_power_station()

/obj/machinery/teleport/hub/Destroy()
	if (power_station)
		power_station.teleporter_hub = null
		power_station = null
	return ..()

/obj/machinery/teleport/hub/RefreshParts()
	var/A = 0
	for(var/obj/item/stock_parts/matter_bin/M in component_parts)
		A += M.rating
	accuracy = A

/obj/machinery/teleport/hub/examine(mob/user)
	. = ..()
	if(in_range(user, src) || isobserver(user))
		. += span_notice("The status display reads: Probability of malfunction decreased by [(accuracy*25)-25]%.")

/obj/machinery/teleport/hub/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/teleport/hub/Bumped(atom/movable/AM)
	if(is_centcom_level(z))
		to_chat(AM, span_warning("You can't use this here!"))
		return
	if(is_ready())
		teleport(AM)

/obj/machinery/teleport/hub/attackby(obj/item/W, mob/user, params)
	if(default_deconstruction_screwdriver(user, "tele-o", "tele0", W))
		if(power_station?.engaged)
			power_station.engaged = 0 //hub with panel open is off, so the station must be informed.
			update_appearance()
		return
	if(default_deconstruction_crowbar(W))
		return
	return ..()

/obj/machinery/teleport/hub/proc/teleport(atom/movable/M as mob|obj, turf/T)
	var/obj/machinery/computer/teleporter/com = power_station.teleporter_console
	if (QDELETED(com))
		return
	var/atom/target
	if(com.target_ref)
		target = com.target_ref.resolve()
	if (!target)
		com.target_ref = null
		visible_message(span_alert("Cannot authenticate locked on coordinates. Please reinstate coordinate matrix."))
		return
	if (ismovable(M))
		if(do_teleport(M, target, channel = TELEPORT_CHANNEL_BLUESPACE))
			use_power(5000)
			if(!calibrated && prob(30 - ((accuracy) * 10))) //oh dear a problem
				if(ishuman(M))//don't remove people from the round randomly you jerks
					var/mob/living/carbon/human/human = M
					if(!(human.mob_biotypes & (MOB_ROBOTIC|MOB_MINERAL|MOB_UNDEAD|MOB_SPIRIT)))
						var/datum/species/species_to_transform = /datum/species/fly
						if(SSevents.holidays && SSevents.holidays[MOTH_WEEK])
							species_to_transform = /datum/species/moth
						if(human.dna && human.dna.species.id != initial(species_to_transform.id))
							to_chat(M, span_hear("You hear a buzzing in your ears."))
							human.set_species(species_to_transform)
							log_game("[human] ([key_name(human)]) was turned into a [initial(species_to_transform.name)] through [src].")

					human.apply_effect((rand(120 - accuracy * 40, 180 - accuracy * 60)), EFFECT_IRRADIATE, 0)
			calibrated = FALSE
	return

/obj/machinery/teleport/hub/update_icon_state()
	icon_state = "[base_icon_state][panel_open ? "-o" : (is_ready() ? 1 : 0)]"
	return ..()

/obj/machinery/teleport/hub/proc/is_ready()
	. = !panel_open && !(machine_stat & (BROKEN|NOPOWER)) && power_station && power_station.engaged && !(power_station.machine_stat & (BROKEN|NOPOWER))

/obj/machinery/teleport/hub/syndicate/Initialize(mapload)
	. = ..()
	var/obj/item/stock_parts/matter_bin/super/super_bin = new(src)
	LAZYADD(component_parts, super_bin)
	RefreshParts()

/obj/machinery/teleport/station
	name = "teleporter station"
	desc = "The power control station for a bluespace teleporter. Used for toggling power, and can activate a test-fire to prevent malfunctions."
	icon_state = "controller"
	base_icon_state = "controller"
	use_power = IDLE_POWER_USE
	idle_power_usage = 10
	active_power_usage = 2000
	circuit = /obj/item/circuitboard/machine/teleporter_station
	var/engaged = FALSE
	var/obj/machinery/computer/teleporter/teleporter_console
	var/obj/machinery/teleport/hub/teleporter_hub
	var/list/linked_stations = list()
	var/efficiency = 0

/obj/machinery/teleport/station/Initialize(mapload)
	. = ..()
	link_console_and_hub()

/obj/machinery/teleport/station/RefreshParts()
	var/E
	for(var/obj/item/stock_parts/capacitor/C in component_parts)
		E += C.rating
	efficiency = E - 1

/obj/machinery/teleport/station/examine(mob/user)
	. = ..()
	if(!panel_open)
		. += span_notice("The panel is screwed in, obstructing the linking device and wiring panel.")
	else
		. += span_notice("The linking device is now able to be scanned with a multitool.")
	if(in_range(user, src) || isobserver(user))
		. += span_notice("The status display reads: This station can be linked to [efficiency] other station(s).")

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

/obj/machinery/teleport/station/Destroy()
	if(teleporter_hub)
		teleporter_hub.power_station = null
		teleporter_hub.update_appearance()
		teleporter_hub = null
	if (teleporter_console)
		teleporter_console.power_station = null
		teleporter_console = null
	return ..()

/obj/machinery/teleport/station/attackby(obj/item/W, mob/user, params)
	if(W.tool_behaviour == TOOL_MULTITOOL)
		if(!multitool_check_buffer(user, W))
			return
		var/obj/item/multitool/M = W
		if(panel_open)
			M.buffer = src
			to_chat(user, span_notice("You download the data to the [W.name]'s buffer."))
		else
			if(M.buffer && istype(M.buffer, /obj/machinery/teleport/station) && M.buffer != src)
				if(linked_stations.len < efficiency)
					linked_stations.Add(M.buffer)
					M.buffer = null
					to_chat(user, span_notice("You upload the data from the [W.name]'s buffer."))
				else
					to_chat(user, span_alert("This station can't hold more information, try to use better parts."))
		return
	else if(default_deconstruction_screwdriver(user, "controller-o", "controller", W))
		update_appearance()
		return

	else if(default_deconstruction_crowbar(W))
		return
	else
		return ..()

/obj/machinery/teleport/station/interact(mob/user)
	toggle(user)

/obj/machinery/teleport/station/proc/toggle(mob/user)
	if(machine_stat & (BROKEN|NOPOWER) || !teleporter_hub || !teleporter_console )
		return
	if (teleporter_console.target_ref?.resolve())
		if(teleporter_hub.panel_open || teleporter_hub.machine_stat & (BROKEN|NOPOWER))
			to_chat(user, span_alert("The teleporter hub isn't responding."))
		else
			engaged = !engaged
			use_power(5000)
			to_chat(user, span_notice("Teleporter [engaged ? "" : "dis"]engaged!"))
	else
		teleporter_console.target_ref = null
		to_chat(user, span_alert("No target detected."))
		engaged = FALSE
	teleporter_hub.update_appearance()
	add_fingerprint(user)

/obj/machinery/teleport/station/power_change()
	. = ..()
	if(teleporter_hub)
		teleporter_hub.update_appearance()

/obj/machinery/teleport/station/update_icon_state()
	if(panel_open)
		icon_state = "[base_icon_state]-o"
		return ..()
	if(machine_stat & (BROKEN|NOPOWER))
		icon_state = "[base_icon_state]-p"
		return ..()
	if(teleporter_console?.calibrating)
		icon_state = "[base_icon_state]-c"
		return ..()
	icon_state = base_icon_state
	return ..()


code:
/obj/machinery/transformer
	name = "\improper Automatic Robotic Factory 5000"
	desc = "A large metallic machine with an entrance and an exit. A sign on \
		the side reads, 'human go in, robot come out'. The human must be \
		lying down and alive. Has to cooldown between each use."
	icon = 'icons/obj/recycling.dmi'
	icon_state = "separator-AO1"
	layer = ABOVE_ALL_MOB_LAYER // Overhead
	density = FALSE
	var/transform_dead = 0
	var/transform_standing = 0
	var/cooldown_duration = 600 // 1 minute
	var/cooldown = 0
	var/cooldown_timer
	var/robot_cell_charge = 5000
	var/obj/effect/countdown/transformer/countdown
	var/mob/living/silicon/ai/masterAI

/obj/machinery/transformer/Initialize(mapload)
	// On us
	. = ..()
	new /obj/machinery/conveyor/auto(locate(x - 1, y, z), WEST)
	new /obj/machinery/conveyor/auto(loc, WEST)
	new /obj/machinery/conveyor/auto(locate(x + 1, y, z), WEST)
	countdown = new(src)
	countdown.start()

/obj/machinery/transformer/examine(mob/user)
	. = ..()
	if(cooldown && (issilicon(user) || isobserver(user)))
		. += "It will be ready in [DisplayTimeText(cooldown_timer - world.time)]."

/obj/machinery/transformer/Destroy()
	QDEL_NULL(countdown)
	. = ..()

/obj/machinery/transformer/update_icon_state()
	if(machine_stat & (BROKEN|NOPOWER) || cooldown == 1)
		icon_state = "separator-AO0"
	else
		icon_state = initial(icon_state)
	return ..()

/obj/machinery/transformer/Bumped(atom/movable/AM)
	if(cooldown == 1)
		return

	// Crossed didn't like people lying down.
	if(ishuman(AM))
		// Only humans can enter from the west side, while lying down.
		var/move_dir = get_dir(loc, AM.loc)
		var/mob/living/carbon/human/H = AM
		if((transform_standing || H.body_position == LYING_DOWN) && move_dir == EAST)// || move_dir == WEST)
			AM.forceMove(drop_location())
			do_transform(AM)

/obj/machinery/transformer/CanAllowThrough(atom/movable/mover, border_dir)
	. = ..()
	// Allows items to go through,
	// to stop them from blocking the conveyor belt.
	if(!ishuman(mover))
		if(get_dir(src, mover) == EAST)
			return
	return FALSE

/obj/machinery/transformer/process()
	if(cooldown && (cooldown_timer <= world.time))
		cooldown = FALSE
		update_appearance()

/obj/machinery/transformer/proc/do_transform(mob/living/carbon/human/H)
	if(machine_stat & (BROKEN|NOPOWER))
		return
	if(cooldown == 1)
		return

	if(!transform_dead && H.stat == DEAD)
		playsound(src.loc, 'sound/machines/buzz-sigh.ogg', 50, FALSE)
		return

	// Activate the cooldown
	cooldown = 1
	cooldown_timer = world.time + cooldown_duration
	update_appearance()

	playsound(src.loc, 'sound/items/welder.ogg', 50, TRUE)
	H.emote("scream") // It is painful
	H.adjustBruteLoss(max(0, 80 - H.getBruteLoss())) // Hurt the human, don't try to kill them though.

	// Sleep for a couple of ticks to allow the human to see the pain
	sleep(5)

	use_power(5000) // Use a lot of power.
	var/mob/living/silicon/robot/R = H.Robotize()
	R.cell = new /obj/item/stock_parts/cell/upgraded/plus(R, robot_cell_charge)

	// So he can't jump out the gate right away.
	R.SetLockdown()
	if(masterAI)
		R.set_connected_ai(masterAI)
		R.lawsync()
		R.lawupdate = TRUE
	addtimer(CALLBACK(src, .proc/unlock_new_robot, R), 50)

/obj/machinery/transformer/proc/unlock_new_robot(mob/living/silicon/robot/R)
	playsound(src.loc, 'sound/machines/ping.ogg', 50, FALSE)
	sleep(30)
	if(R)
		R.SetLockdown(FALSE)
		R.notify_ai(NEW_BORG)


code:
//dye registry, add dye colors and their resulting output here if you want the sprite to change instead of just the color.
GLOBAL_LIST_INIT(dye_registry, list(
	DYE_REGISTRY_UNDER = list(
		DYE_RED = /obj/item/clothing/under/color/red,
		DYE_ORANGE = /obj/item/clothing/under/color/orange,
		DYE_YELLOW = /obj/item/clothing/under/color/yellow,
		DYE_GREEN = /obj/item/clothing/under/color/green,
		DYE_BLUE = /obj/item/clothing/under/color/blue,
		DYE_PURPLE = /obj/item/clothing/under/color/lightpurple,
		DYE_BLACK = /obj/item/clothing/under/color/black,
		DYE_WHITE = /obj/item/clothing/under/color/white,
		DYE_RAINBOW = /obj/item/clothing/under/color/rainbow,
		DYE_MIME = /obj/item/clothing/under/rank/civilian/mime,
		DYE_CLOWN = /obj/item/clothing/under/rank/civilian/clown,
		DYE_CHAP = /obj/item/clothing/under/rank/civilian/chaplain,
		DYE_QM = /obj/item/clothing/under/rank/cargo/qm,
		DYE_LAW = /obj/item/clothing/under/suit/black,
		DYE_CAPTAIN = /obj/item/clothing/under/rank/captain,
		DYE_HOP = /obj/item/clothing/under/rank/civilian/head_of_personnel,
		DYE_HOS = /obj/item/clothing/under/rank/security/head_of_security,
		DYE_CE = /obj/item/clothing/under/rank/engineering/chief_engineer,
		DYE_RD = /obj/item/clothing/under/rank/rnd/research_director,
		DYE_CMO = /obj/item/clothing/under/rank/medical/chief_medical_officer,
		DYE_REDCOAT = /obj/item/clothing/under/costume/redcoat,
		DYE_PRISONER = /obj/item/clothing/under/rank/prisoner,
		DYE_SYNDICATE = /obj/item/clothing/under/syndicate,
		DYE_CENTCOM = /obj/item/clothing/under/rank/centcom/commander
	),
	DYE_REGISTRY_JUMPSKIRT = list(
		DYE_RED = /obj/item/clothing/under/color/jumpskirt/red,
		DYE_ORANGE = /obj/item/clothing/under/color/jumpskirt/orange,
		DYE_YELLOW = /obj/item/clothing/under/color/jumpskirt/yellow,
		DYE_GREEN = /obj/item/clothing/under/color/jumpskirt/green,
		DYE_BLUE = /obj/item/clothing/under/color/jumpskirt/blue,
		DYE_PURPLE = /obj/item/clothing/under/color/jumpskirt/lightpurple,
		DYE_BLACK = /obj/item/clothing/under/color/jumpskirt/black,
		DYE_WHITE = /obj/item/clothing/under/color/jumpskirt/white,
		DYE_RAINBOW = /obj/item/clothing/under/color/jumpskirt/rainbow,
		DYE_MIME = /obj/item/clothing/under/rank/civilian/mime/skirt,
		DYE_CHAP = /obj/item/clothing/under/rank/civilian/chaplain/skirt,
		DYE_QM = /obj/item/clothing/under/rank/cargo/qm/skirt,
		DYE_CAPTAIN = /obj/item/clothing/under/rank/captain/skirt,
		DYE_HOP = /obj/item/clothing/under/rank/civilian/head_of_personnel/skirt,
		DYE_HOS = /obj/item/clothing/under/rank/security/head_of_security/skirt,
		DYE_CE = /obj/item/clothing/under/rank/engineering/chief_engineer/skirt,
		DYE_RD = /obj/item/clothing/under/rank/rnd/research_director/skirt,
		DYE_CMO = /obj/item/clothing/under/rank/medical/chief_medical_officer/skirt,
		DYE_PRISONER = /obj/item/clothing/under/rank/prisoner/skirt,
	),
	DYE_REGISTRY_GLOVES = list(
		DYE_RED = /obj/item/clothing/gloves/color/red,
		DYE_ORANGE = /obj/item/clothing/gloves/color/orange,
		DYE_YELLOW = /obj/item/clothing/gloves/color/yellow,
		DYE_GREEN = /obj/item/clothing/gloves/color/green,
		DYE_BLUE = /obj/item/clothing/gloves/color/blue,
		DYE_PURPLE = /obj/item/clothing/gloves/color/purple,
		DYE_BLACK = /obj/item/clothing/gloves/color/black,
		DYE_WHITE = /obj/item/clothing/gloves/color/white,
		DYE_RAINBOW = /obj/item/clothing/gloves/color/rainbow,
		DYE_MIME = /obj/item/clothing/gloves/color/white,
		DYE_CLOWN = /obj/item/clothing/gloves/color/rainbow,
		DYE_QM = /obj/item/clothing/gloves/color/brown,
		DYE_CAPTAIN = /obj/item/clothing/gloves/color/captain,
		DYE_HOP = /obj/item/clothing/gloves/color/grey,
		DYE_HOS = /obj/item/clothing/gloves/color/black,
		DYE_CE = /obj/item/clothing/gloves/color/chief_engineer,
		DYE_RD = /obj/item/clothing/gloves/color/grey,
		DYE_CMO = /obj/item/clothing/gloves/color/latex/nitrile,
		DYE_REDCOAT = /obj/item/clothing/gloves/color/white,
		DYE_SYNDICATE = /obj/item/clothing/gloves/combat,
		DYE_CENTCOM = /obj/item/clothing/gloves/combat
	),
	DYE_REGISTRY_SNEAKERS = list(
		DYE_RED = /obj/item/clothing/shoes/sneakers/red,
		DYE_ORANGE = /obj/item/clothing/shoes/sneakers/orange,
		DYE_YELLOW = /obj/item/clothing/shoes/sneakers/yellow,
		DYE_GREEN = /obj/item/clothing/shoes/sneakers/green,
		DYE_BLUE = /obj/item/clothing/shoes/sneakers/blue,
		DYE_PURPLE = /obj/item/clothing/shoes/sneakers/purple,
		DYE_BLACK = /obj/item/clothing/shoes/sneakers/black,
		DYE_WHITE = /obj/item/clothing/shoes/sneakers/white,
		DYE_RAINBOW = /obj/item/clothing/shoes/sneakers/rainbow,
		DYE_MIME = /obj/item/clothing/shoes/sneakers/black,
		DYE_CLOWN = /obj/item/clothing/shoes/sneakers/rainbow,
		DYE_QM = /obj/item/clothing/shoes/sneakers/brown,
		DYE_CAPTAIN = /obj/item/clothing/shoes/sneakers/brown,
		DYE_HOP = /obj/item/clothing/shoes/sneakers/brown,
		DYE_CE = /obj/item/clothing/shoes/sneakers/brown,
		DYE_RD = /obj/item/clothing/shoes/sneakers/brown,
		DYE_CMO = /obj/item/clothing/shoes/sneakers/brown,
		DYE_SYNDICATE = /obj/item/clothing/shoes/combat,
		DYE_CENTCOM = /obj/item/clothing/shoes/combat
	),
	DYE_REGISTRY_FANNYPACK = list(
		DYE_RED = /obj/item/storage/belt/fannypack/red,
		DYE_ORANGE = /obj/item/storage/belt/fannypack/orange,
		DYE_YELLOW = /obj/item/storage/belt/fannypack/yellow,
		DYE_GREEN = /obj/item/storage/belt/fannypack/green,
		DYE_BLUE = /obj/item/storage/belt/fannypack/blue,
		DYE_PURPLE = /obj/item/storage/belt/fannypack/purple,
		DYE_BLACK = /obj/item/storage/belt/fannypack/black,
		DYE_WHITE = /obj/item/storage/belt/fannypack/white,
		DYE_SYNDICATE = /obj/item/storage/belt/military
	),
	DYE_REGISTRY_BEDSHEET = list(
		DYE_RED = /obj/item/bedsheet/red,
		DYE_ORANGE = /obj/item/bedsheet/orange,
		DYE_YELLOW = /obj/item/bedsheet/yellow,
		DYE_GREEN = /obj/item/bedsheet/green,
		DYE_BLUE = /obj/item/bedsheet/blue,
		DYE_PURPLE = /obj/item/bedsheet/purple,
		DYE_BLACK = /obj/item/bedsheet/black,
		DYE_WHITE = /obj/item/bedsheet,
		DYE_RAINBOW = /obj/item/bedsheet/rainbow,
		DYE_MIME = /obj/item/bedsheet/mime,
		DYE_CLOWN = /obj/item/bedsheet/clown,
		DYE_CHAP = /obj/item/bedsheet/chaplain,
		DYE_QM = /obj/item/bedsheet/qm,
		DYE_LAW = /obj/item/bedsheet/black,
		DYE_CAPTAIN = /obj/item/bedsheet/captain,
		DYE_HOP = /obj/item/bedsheet/hop,
		DYE_HOS = /obj/item/bedsheet/hos,
		DYE_CE = /obj/item/bedsheet/ce,
		DYE_RD = /obj/item/bedsheet/rd,
		DYE_CMO = /obj/item/bedsheet/cmo,
		DYE_COSMIC = /obj/item/bedsheet/cosmos,
		DYE_SYNDICATE = /obj/item/bedsheet/syndie,
		DYE_CENTCOM = /obj/item/bedsheet/centcom
	),
		DYE_REGISTRY_DOUBLE_BEDSHEET = list(
		DYE_RED = /obj/item/bedsheet/red/double,
		DYE_ORANGE = /obj/item/bedsheet/orange/double,
		DYE_YELLOW = /obj/item/bedsheet/yellow/double,
		DYE_GREEN = /obj/item/bedsheet/green/double,
		DYE_BLUE = /obj/item/bedsheet/blue/double,
		DYE_PURPLE = /obj/item/bedsheet/purple/double,
		DYE_BLACK = /obj/item/bedsheet/black/double,
		DYE_WHITE = /obj/item/bedsheet/double,
		DYE_RAINBOW = /obj/item/bedsheet/rainbow/double,
		DYE_MIME = /obj/item/bedsheet/mime/double,
		DYE_CLOWN = /obj/item/bedsheet/clown/double,
		DYE_CHAP = /obj/item/bedsheet/chaplain/double,
		DYE_QM = /obj/item/bedsheet/qm/double,
		DYE_LAW = /obj/item/bedsheet/black/double,
		DYE_CAPTAIN = /obj/item/bedsheet/captain/double,
		DYE_HOP = /obj/item/bedsheet/hop/double,
		DYE_HOS = /obj/item/bedsheet/hos/double,
		DYE_CE = /obj/item/bedsheet/ce/double,
		DYE_RD = /obj/item/bedsheet/rd/double,
		DYE_CMO = /obj/item/bedsheet/cmo/double,
		DYE_COSMIC = /obj/item/bedsheet/cosmos/double,
		DYE_SYNDICATE = /obj/item/bedsheet/syndie/double,
		DYE_CENTCOM = /obj/item/bedsheet/centcom/double
	),
	DYE_LAWYER_SPECIAL = list(
		DYE_COSMIC = /obj/item/clothing/under/rank/civilian/lawyer/galaxy,
		DYE_SYNDICATE = /obj/item/clothing/under/rank/civilian/lawyer/galaxy/red
	)
))

/obj/machinery/washing_machine
	name = "washing machine"
	desc = "Gets rid of those pesky bloodstains, or your money back!"
	icon = 'icons/obj/machines/washing_machine.dmi'
	icon_state = "wm_1_0"
	density = TRUE
	state_open = TRUE
	var/busy = FALSE
	var/bloody_mess = FALSE
	var/obj/item/color_source
	var/max_wash_capacity = 5

/obj/machinery/washing_machine/examine(mob/user)
	. = ..()
	if(!busy)
		. += span_notice("<b>Right-click</b> with an empty hand to start a wash cycle.")

/obj/machinery/washing_machine/process(delta_time)
	if(!busy)
		animate(src, transform=matrix(), time=2)
		return PROCESS_KILL
	if(anchored)
		if(DT_PROB(2.5, delta_time))
			var/matrix/M = new
			M.Translate(rand(-1, 1), rand(0, 1))
			animate(src, transform=M, time=1)
			animate(transform=matrix(), time=1)
	else
		if(DT_PROB(0.5, delta_time))
			step(src, pick(GLOB.cardinals))
		var/matrix/M = new
		M.Translate(rand(-3, 3), rand(-1, 3))
		animate(src, transform=M, time=2)

/obj/machinery/washing_machine/wash(clean_types)
	. = ..()
	if(!busy && bloody_mess && (clean_types & CLEAN_TYPE_BLOOD))
		bloody_mess = FALSE
		update_appearance()
		. = TRUE

/obj/machinery/washing_machine/proc/wash_cycle()
	for(var/X in contents)
		var/atom/movable/AM = X
		AM.wash(CLEAN_WASH)
		AM.machine_wash(src)

	//if we had the ability to brainwash, remove that now
	REMOVE_TRAIT(src, TRAIT_BRAINWASHING, SKILLCHIP_TRAIT)
	busy = FALSE
	if(color_source)
		qdel(color_source)
		color_source = null
	update_appearance()

/obj/item/proc/dye_item(dye_color, dye_key_override)
	var/dye_key_selector = dye_key_override ? dye_key_override : dying_key
	if(undyeable)
		return FALSE
	if(!dye_key_selector)
		return FALSE
	if(!GLOB.dye_registry[dye_key_selector])
		log_runtime("Item just tried to be dyed with an invalid registry key: [dye_key_selector]")
		return FALSE
	var/obj/item/target_type = GLOB.dye_registry[dye_key_selector][dye_color]
	if(!target_type)
		return FALSE
	if(initial(target_type.greyscale_config) && initial(target_type.greyscale_colors))
		set_greyscale(
			colors=initial(target_type.greyscale_colors),
			new_config=initial(target_type.greyscale_config),
			new_worn_config=initial(target_type.greyscale_config_worn),
			new_inhand_left=initial(target_type.greyscale_config_inhand_left),
			new_inhand_right=initial(target_type.greyscale_config_inhand_right)
		)
	else
		icon = initial(target_type.icon)
		lefthand_file = initial(target_type.lefthand_file)
		righthand_file = initial(target_type.righthand_file)
		worn_icon = initial(target_type.worn_icon)

	icon_state = initial(target_type.icon_state)
	inhand_icon_state = initial(target_type.inhand_icon_state)
	worn_icon_state = initial(target_type.worn_icon_state)
	inhand_x_dimension = initial(target_type.inhand_x_dimension)
	inhand_y_dimension = initial(target_type.inhand_y_dimension)
	name = initial(target_type.name)
	desc = "[initial(target_type.desc)] The colors look a little dodgy."
	return target_type //successfully "appearance copy" dyed something; returns the target type as a hacky way of extending

//what happens to this object when washed inside a washing machine
/atom/movable/proc/machine_wash(obj/machinery/washing_machine/WM)
	return

/obj/item/stack/sheet/hairlesshide/machine_wash(obj/machinery/washing_machine/WM)
	new /obj/item/stack/sheet/wethide(drop_location(), amount)
	qdel(src)

/obj/item/clothing/suit/hooded/ian_costume/machine_wash(obj/machinery/washing_machine/WM)
	new /obj/item/food/meat/slab/corgi(loc)
	qdel(src)

/mob/living/simple_animal/pet/machine_wash(obj/machinery/washing_machine/WM)
	WM.bloody_mess = TRUE
	gib()

/obj/item/machine_wash(obj/machinery/washing_machine/WM)
	if(WM.color_source)
		dye_item(WM.color_source.dye_color)

/obj/item/clothing/under/dye_item(dye_color, dye_key)
	. = ..()
	if(.)
		var/obj/item/clothing/under/U = .
		can_adjust = initial(U.can_adjust)
		if(!can_adjust && adjusted) //we deadjust the uniform if it's now unadjustable
			toggle_jumpsuit_adjust()

/obj/item/clothing/under/machine_wash(obj/machinery/washing_machine/WM)
	freshly_laundered = TRUE
	addtimer(VARSET_CALLBACK(src, freshly_laundered, FALSE), 5 MINUTES, TIMER_UNIQUE | TIMER_OVERRIDE)
	..()

/obj/item/clothing/head/mob_holder/machine_wash(obj/machinery/washing_machine/WM)
	..()
	held_mob.machine_wash(WM)

/obj/item/clothing/shoes/sneakers/machine_wash(obj/machinery/washing_machine/WM)
	if(chained)
		chained = FALSE
		slowdown = SHOES_SLOWDOWN
		new /obj/item/restraints/handcuffs(loc)
	..()

/obj/machinery/washing_machine/relaymove(mob/living/user, direction)
	container_resist_act(user)

/obj/machinery/washing_machine/container_resist_act(mob/living/user)
	if(!busy)
		add_fingerprint(user)
		open_machine()

/obj/machinery/washing_machine/update_icon_state()
	if(busy)
		icon_state = "wm_running_[bloody_mess]"
		return ..()
	if(bloody_mess)
		icon_state = "wm_[state_open]_blood"
		return ..()

	var/full = contents.len ? 1 : 0
	icon_state = "wm_[state_open]_[full]"
	return ..()

/obj/machinery/washing_machine/update_overlays()
	. = ..()
	if(panel_open)
		. += "wm_panel"

/obj/machinery/washing_machine/attackby(obj/item/W, mob/living/user, params)
	if(panel_open && !busy && default_unfasten_wrench(user, W))
		return

	if(default_deconstruction_screwdriver(user, null, null, W))
		update_appearance()
		return

	//else if(!user.combat_mode) //SEPTIC EDIT REMOVAL
	//SEPTIC EDIT BEGIN
	else if(!IS_HELP_INTENT(user, params2list(params)))
	//SEPTIC EDIT END
		if (!state_open)
			to_chat(user, span_warning("Open the door first!"))
			return TRUE

		if(bloody_mess)
			to_chat(user, span_warning("[src] must be cleaned up first!"))
			return TRUE

		if(contents.len >= max_wash_capacity)
			to_chat(user, span_warning("The washing machine is full!"))
			return TRUE

		if(!user.transferItemToLoc(W, src))
			to_chat(user, span_warning("\The [W] is stuck to your hand, you cannot put it in the washing machine!"))
			return TRUE
		if(W.dye_color)
			color_source = W
		update_appearance()

	else
		return ..()

/obj/machinery/washing_machine/attack_hand(mob/living/user, list/modifiers)
	. = ..()
	if(.)
		return
	if(busy)
		to_chat(user, span_warning("[src] is busy!"))
		return

	if(user.pulling && isliving(user.pulling))
		var/mob/living/L = user.pulling
		if(L.buckled || L.has_buckled_mobs())
			return
		if(state_open)
			if(istype(L, /mob/living/simple_animal/pet))
				L.forceMove(src)
				update_appearance()
		return

	if(!state_open)
		open_machine()
	else
		state_open = FALSE //close the door
		update_appearance()

/obj/machinery/washing_machine/attack_hand_secondary(mob/user, modifiers)
	. = ..()
	if(. == SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN)
		return

	if(!user.canUseTopic(src, !issilicon(user)))
		return SECONDARY_ATTACK_CONTINUE_CHAIN
	if(busy)
		to_chat(user, span_warning("[src] is busy!"))
		return SECONDARY_ATTACK_CONTINUE_CHAIN
	if(state_open)
		to_chat(user, span_warning("Close the door first!"))
		return SECONDARY_ATTACK_CONTINUE_CHAIN
	if(bloody_mess)
		to_chat(user, span_warning("[src] must be cleaned up first!"))
		return SECONDARY_ATTACK_CONTINUE_CHAIN
	busy = TRUE
	if(HAS_TRAIT(user, TRAIT_BRAINWASHING))
		ADD_TRAIT(src, TRAIT_BRAINWASHING, SKILLCHIP_TRAIT)
	update_appearance()
	addtimer(CALLBACK(src, .proc/wash_cycle), 20 SECONDS)
	START_PROCESSING(SSfastprocess, src)
	return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN

/obj/machinery/washing_machine/attack_ai_secondary(mob/user, modifiers)
	return attack_hand_secondary(user, modifiers)

/obj/machinery/washing_machine/deconstruct(disassembled = TRUE)
	if (!(flags_1 & NODECONSTRUCT_1))
		new /obj/item/stack/sheet/iron(drop_location(), 2)
	qdel(src)

/obj/machinery/washing_machine/open_machine(drop = 1)
	..()
	set_density(TRUE) //because machinery/open_machine() sets it to FALSE
	color_source = null


code:
/obj/machinery/wish_granter
	name = "wish granter"
	desc = "You're not so sure about this, anymore..."
	icon = 'icons/obj/device.dmi'
	icon_state = "syndbeacon"

	use_power = NO_POWER_USE
	density = TRUE

	var/charges = 1
	var/insisting = 0

/obj/machinery/wish_granter/attack_hand(mob/living/carbon/user, list/modifiers)
	. = ..()
	if(.)
		return
	if(charges <= 0)
		to_chat(user, span_boldnotice("The Wish Granter lies silent."))
		return

	else if(!ishuman(user))
		to_chat(user, span_boldnotice("You feel a dark stirring inside of the Wish Granter, something you want nothing of. Your instincts are better than any man's."))
		return

	else if(is_special_character(user))
		to_chat(user, span_boldnotice("Even to a heart as dark as yours, you know nothing good will come of this. Something instinctual makes you pull away."))

	else if (!insisting)
		to_chat(user, span_boldnotice("Your first touch makes the Wish Granter stir, listening to you. Are you really sure you want to do this?"))
		insisting++

	else
		to_chat(user, span_boldnotice("You speak. [pick("I want the station to disappear","Humanity is corrupt, mankind must be destroyed","I want to be rich", "I want to rule the world","I want immortality.")]. The Wish Granter answers."))
		to_chat(user, span_boldnotice("Your head pounds for a moment, before your vision clears. You are the avatar of the Wish Granter, and your power is LIMITLESS! And it's all yours. You need to make sure no one can take it from you. No one can know, first."))

		charges--
		insisting = 0

		user.mind.add_antag_datum(/datum/antagonist/wishgranter)

		to_chat(user, span_warning("You have a very bad feeling about this."))

	return


code:
///Keeps track of the sources of dynamic luminosity and updates our visibility with the highest.
/atom/movable/proc/update_dynamic_luminosity()
	var/highest = 0
	for(var/i in affected_dynamic_lights)
		if(affected_dynamic_lights[i] <= highest)
			continue
		highest = affected_dynamic_lights[i]
	if(highest == affecting_dynamic_lumi)
		return
	luminosity -= affecting_dynamic_lumi
	affecting_dynamic_lumi = highest
	luminosity += affecting_dynamic_lumi

///Helper to change several lighting overlay settings.
/atom/movable/proc/set_light_range_power_color(range, power, color)
	set_light_range(range)
	set_light_power(power)
	set_light_color(color)