code:
var/global/list/obj/machinery/message_server/message_servers = list()

/datum/data_pda_msg
	var/recipient = "Unspecified" //name of the person
	var/sender = "Unspecified" //name of the sender
	var/message = "Blank" //transferred message
	var/image/photo = null //Attached photo

/datum/data_pda_msg/New(var/param_rec = "",var/param_sender = "",var/param_message = "",var/param_photo=null)

	if(param_rec)
		recipient = param_rec
	if(param_sender)
		sender = param_sender
	if(param_message)
		message = param_message
	if(param_photo)
		photo = param_photo

/datum/data_pda_msg/proc/get_photo_ref()
	if(photo)
		return "(Photo)"
	return ""

/datum/data_pda_msg/Topic(href,href_list)
	..()
	if(href_list["photo"])
		var/mob/M = usr
		M << browse_rsc(photo, "pda_photo.png")
		M << browse("<title>PDA Photo</title>" \
		+ "" \
		+ "" \
		+ "", "window=book;size=192x192")
		onclose(M, "PDA Photo")

/datum/data_rc_msg
	var/rec_dpt = "Unspecified" //name of the person
	var/send_dpt = "Unspecified" //name of the sender
	var/message = "Blank" //transferred message
	var/stamp = "Unstamped"
	var/id_auth = "Unauthenticated"
	var/priority = "Normal"

/datum/data_rc_msg/New(var/param_rec = "",var/param_sender = "",var/param_message = "",var/param_stamp = "",var/param_id_auth = "",var/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(1)
				priority = "Normal"
			if(2)
				priority = "High"
			if(3)
				priority = "Extreme"
			else
				priority = "Undetermined"

/obj/machinery/message_server
	icon = 'icons/obj/machines/research.dmi'
	icon_state = "server"
	name = "Messaging Server"
	density = 1
	anchored = 1
	use_power = 1
	idle_power_usage = 10
	active_power_usage = 100

	var/list/datum/data_pda_msg/pda_msgs = list()
	var/list/datum/data_rc_msg/rc_msgs = list()
	var/active = 1
	var/decryptkey = "password"

/obj/machinery/message_server/New()
	message_servers += src
	decryptkey = GenerateKey()
	send_pda_message("System Administrator", "system", "This is an automated message. The messaging system is functioning correctly.")
	..()
	return

/obj/machinery/message_server/Destroy()
	message_servers -= src
	return ..()

/obj/machinery/message_server/proc/GenerateKey()
	//Feel free to move to Helpers.
	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/message_server/process()
	//if(decryptkey == "password")
	//	decryptkey = generateKey()
	if(active && (stat & (BROKEN|NOPOWER)))
		active = 0
		return
	update_icon()
	return

/obj/machinery/message_server/proc/send_pda_message(recipient = "",sender = "",message = "",photo=null)
	. = new/datum/data_pda_msg(recipient,sender,message,photo)
	pda_msgs += .

/obj/machinery/message_server/proc/send_rc_message(recipient = "",sender = "",message = "",stamp = "", id_auth = "", priority = 1)
	rc_msgs += new/datum/data_rc_msg(recipient,sender,message,stamp,id_auth)

/obj/machinery/message_server/attack_hand(mob/user)
//	user << "\blue There seem to be some parts missing from this server. They should arrive on the station in a few days, give or take a few Centcom delays."
	user << "You toggle PDA message passing from [active ? "On" : "Off"] to [active ? "Off" : "On"]"
	active = !active
	update_icon()

	return

/obj/machinery/message_server/update_icon()
	if((stat & (BROKEN|NOPOWER)))
		icon_state = "server-nopower"
	else if (!active)
		icon_state = "server-off"
	else
		icon_state = "server-on"

	return

/datum/feedback_variable
	var/variable
	var/value
	var/details

/datum/feedback_variable/New(var/param_variable,var/param_value = 0)
	variable = param_variable
	value = param_value

/datum/feedback_variable/proc/inc(num = 1)
	if (isnum(value))
		value += num
	else
		value = text2num(value)
		if (isnum(value))
			value += num
		else
			value = num

/datum/feedback_variable/proc/dec(num = 1)
	if (isnum(value))
		value -= num
	else
		value = text2num(value)
		if (isnum(value))
			value -= num
		else
			value = -num

/datum/feedback_variable/proc/set_value(num)
	if (isnum(num))
		value = num

/datum/feedback_variable/proc/get_value()
	if (!isnum(value))
		return 0
	return value

/datum/feedback_variable/proc/get_variable()
	return variable

/datum/feedback_variable/proc/set_details(text)
	if (istext(text))
		details = text

/datum/feedback_variable/proc/add_details(text)
	if (istext(text))
		text = replacetext(text, " ", "_")
		if (!details)
			details = text
		else
			details += " [text]"

/datum/feedback_variable/proc/get_details()
	return details

/datum/feedback_variable/proc/get_parsed()
	return list(variable,value,details)

var/obj/machinery/blackbox_recorder/blackbox

/obj/machinery/blackbox_recorder
	icon = 'icons/obj/stationobjs.dmi'
	icon_state = "blackbox"
	name = "Blackbox Recorder"
	density = 1
	anchored = 1
	use_power = 1
	idle_power_usage = 10
	active_power_usage = 100
	var/list/messages = list()		//Stores messages of non-standard frequencies
	var/list/messages_admin = list()

	var/list/msg_common = list()
	var/list/msg_science = list()
	var/list/msg_command = list()
	var/list/msg_medical = list()
	var/list/msg_engineering = list()
	var/list/msg_security = list()
	var/list/msg_deathsquad = list()
	var/list/msg_syndicate = list()
	var/list/msg_service = list()
	var/list/msg_cargo = list()

	var/list/datum/feedback_variable/feedback = new()

	//Only one can exsist in the world!
/obj/machinery/blackbox_recorder/New()
	if (blackbox)
		if (istype(blackbox,/obj/machinery/blackbox_recorder))
			qdel(src)
	blackbox = src

/obj/machinery/blackbox_recorder/Destroy()
	var/turf/T = locate(1,1,2)
	if (T)
		blackbox = null
		var/obj/machinery/blackbox_recorder/BR = new/obj/machinery/blackbox_recorder(T)
		BR.msg_common = msg_common
		BR.msg_science = msg_science
		BR.msg_command = msg_command
		BR.msg_medical = msg_medical
		BR.msg_engineering = msg_engineering
		BR.msg_security = msg_security
		BR.msg_deathsquad = msg_deathsquad
		BR.msg_syndicate = msg_syndicate
		BR.msg_service = msg_service
		BR.msg_cargo = msg_cargo
		BR.feedback = feedback
		BR.messages = messages
		BR.messages_admin = messages_admin
		if(blackbox != BR)
			blackbox = BR
	return ..()

/obj/machinery/blackbox_recorder/proc/find_feedback_datum(variable)
	for (var/datum/feedback_variable/FV in feedback)
		if (FV.get_variable() == variable)
			return FV
	var/datum/feedback_variable/FV = new(variable)
	feedback += FV
	return FV

/obj/machinery/blackbox_recorder/proc/get_round_feedback()
	return feedback

/obj/machinery/blackbox_recorder/proc/round_end_data_gathering()

	var/pda_msg_amt = 0
	var/rc_msg_amt = 0

	for (var/obj/machinery/message_server/MS in message_servers)
		if (MS.pda_msgs.len > pda_msg_amt)
			pda_msg_amt = MS.pda_msgs.len
		if (MS.rc_msgs.len > rc_msg_amt)
			rc_msg_amt = MS.rc_msgs.len

	feedback_set_details("radio_usage","")

	feedback_add_details("radio_usage","COM-[msg_common.len]")
	feedback_add_details("radio_usage","SCI-[msg_science.len]")
	feedback_add_details("radio_usage","HEA-[msg_command.len]")
	feedback_add_details("radio_usage","MED-[msg_medical.len]")
	feedback_add_details("radio_usage","ENG-[msg_engineering.len]")
	feedback_add_details("radio_usage","SEC-[msg_security.len]")
	feedback_add_details("radio_usage","DTH-[msg_deathsquad.len]")
	feedback_add_details("radio_usage","SYN-[msg_syndicate.len]")
	feedback_add_details("radio_usage","SRV-[msg_service.len]")
	feedback_add_details("radio_usage","CAR-[msg_cargo.len]")
	feedback_add_details("radio_usage","OTH-[messages.len]")
	feedback_add_details("radio_usage","PDA-[pda_msg_amt]")
	feedback_add_details("radio_usage","RC-[rc_msg_amt]")

	feedback_set_details("round_end","[time2text(world.realtime)]") //This one MUST be the last one that gets set.

//This proc is only to be called at round end.
/obj/machinery/blackbox_recorder/proc/save_all_data_to_sql()
	if (!feedback) return

	round_end_data_gathering() //round_end time logging and some other data processing
	establish_db_connection()
	if (!dbcon.IsConnected()) return
	var/round_id

	var/DBQuery/query = dbcon.NewQuery("SELECT MAX(round_id) AS round_id FROM [format_table_name("feedback")]")
	query.Execute()
	while (query.NextRow())
		round_id = query.item[1]

	if (!isnum(round_id))
		round_id = text2num(round_id)
	round_id++

	var/sqlrowlist = ""

	for (var/datum/feedback_variable/FV in feedback)
		if (sqlrowlist != "")
			sqlrowlist += ", " //a comma (,) at the start of the first row to insert will trigger a SQL error

		sqlrowlist += "(null, Now(), [round_id], \"[sanitizeSQL(FV.get_variable())]\", [FV.get_value()], \"[sanitizeSQL(FV.get_details())]\")"

	if (sqlrowlist == "")
		return

	var/DBQuery/query_insert = dbcon.NewQuery("INSERT DELAYED IGNORE INTO [format_table_name("feedback")] VALUES " + sqlrowlist)
	query_insert.Execute()

/proc/feedback_set(variable,value)
	if (!blackbox) return

	var/datum/feedback_variable/FV = blackbox.find_feedback_datum(variable)

	if (!FV) return

	FV.set_value(value)

/proc/feedback_inc(variable,value)
	if (!blackbox) return

	var/datum/feedback_variable/FV = blackbox.find_feedback_datum(variable)

	if (!FV) return

	FV.inc(value)

/proc/feedback_dec(variable,value)
	if (!blackbox) return

	var/datum/feedback_variable/FV = blackbox.find_feedback_datum(variable)

	if (!FV) return

	FV.dec(value)

/proc/feedback_set_details(variable,details)
	if (!blackbox) return

	var/datum/feedback_variable/FV = blackbox.find_feedback_datum(variable)

	if(!FV) return

	FV.set_details(details)

/proc/feedback_add_details(variable,details)
	if (!blackbox) return

	var/datum/feedback_variable/FV = blackbox.find_feedback_datum(variable)

	if (!FV) return

	FV.add_details(details)


code:
/*
Protolathe

Similar to an autolathe, you load glass and metal sheets (but not other objects) into it to be used as raw materials for the stuff
it creates. All the menus and other manipulation commands are in the R&D console.

Note: Must be placed west/left of and R&D console to function.

*/
/obj/machinery/r_n_d/protolathe
	name = "Protolathe"
	desc = "Converts raw materials into useful objects."
	icon_state = "protolathe"
	flags = OPENCONTAINER

	var/datum/material_container/materials
	var/efficiency_coeff

	var/list/categories = list(
								"Power Designs",
								"Medical Designs",
								"Bluespace Designs",
								"Stock Parts",
								"Equipment",
								"Mining Designs",
								"Electronics",
								"Weapons",
								"Ammo",
								"Firing Pins"
								)

	reagents = new()

/obj/machinery/r_n_d/protolathe/New()
	..()
	component_parts = list()
	component_parts += new /obj/item/weapon/circuitboard/protolathe(src)
	component_parts += new /obj/item/weapon/stock_parts/matter_bin(src)
	component_parts += new /obj/item/weapon/stock_parts/matter_bin(src)
	component_parts += new /obj/item/weapon/stock_parts/manipulator(src)
	component_parts += new /obj/item/weapon/stock_parts/manipulator(src)
	component_parts += new /obj/item/weapon/reagent_containers/glass/beaker(src)
	component_parts += new /obj/item/weapon/reagent_containers/glass/beaker(src)
	materials = new(src, list(MAT_METAL=1, MAT_GLASS=1, MAT_SILVER=1, MAT_GOLD=1, MAT_DIAMOND=1, MAT_PLASMA=1, MAT_URANIUM=1, MAT_BANANIUM=1))
	RefreshParts()

	reagents.my_atom = src

/obj/machinery/r_n_d/protolathe/Destroy()
	qdel(materials)
	return ..()

/obj/machinery/r_n_d/protolathe/RefreshParts()
	var/T = 0
	for(var/obj/item/weapon/reagent_containers/glass/G in component_parts)
		G.reagents.trans_to(src, G.reagents.total_volume)
	for(var/obj/item/weapon/stock_parts/matter_bin/M in component_parts)
		T += M.rating
	materials.max_amount = T * 75000
	T = 0
	for(var/obj/item/weapon/stock_parts/manipulator/M in component_parts)
		T += (M.rating/3)
	efficiency_coeff = max(T, 1)

/obj/machinery/r_n_d/protolathe/proc/check_mat(datum/design/being_built, M)	// now returns how many times the item can be built with the material
	var/A = materials.amount(M)
	if(!A)
		A = reagents.get_reagent_amount(M)
		A = A / max(1, (being_built.reagents[M]))
	else
		A = A / max(1, (being_built.materials[M]))
	return A

/obj/machinery/r_n_d/protolathe/attackby(obj/item/O, mob/user, params)
	if (shocked)
		shock(user,50)
	if (default_deconstruction_screwdriver(user, "protolathe_t", "protolathe", O))
		if(linked_console)
			linked_console.linked_lathe = null
			linked_console = null
		return

	if(exchange_parts(user, O))
		return

	if (panel_open)
		if(istype(O, /obj/item/weapon/crowbar))
			for(var/obj/item/weapon/reagent_containers/glass/G in component_parts)
				reagents.trans_to(G, G.reagents.maximum_volume)
			materials.retrieve_all()
			default_deconstruction_crowbar(O)
			return 1
		else
			user << "You can't load the [src.name] while it's opened!"
			return 1
	if (disabled)
		return
	if (!linked_console)
		user << "The [src.name] must be linked to an R&D console first!"
		return 1
	if (busy)
		user << "The [src.name] is busy! Please wait for completion of previous operation."
		return 1
	if (O.is_open_container())
		return
	if (stat)
		return 1
	if(!istype(O,/obj/item/stack/sheet))
		return 1

	if(!materials.has_space( materials.get_item_material_amount(O) ))
		user << "The [src.name]'s material bin is full! Please remove material before adding more."
		return 1

	var/obj/item/stack/sheet/stack = O
	var/amount = round(input("How many sheets do you want to add?") as num)//No decimals
	if(!in_range(src, stack) || !user.Adjacent(src))
		return
	var/amount_inserted = materials.insert_stack(O,amount)
	if(!amount_inserted)
		return 1
	else
		var/stack_name = stack.name
		busy = 1
		use_power(max(1000, (MINERAL_MATERIAL_AMOUNT*amount_inserted/10)))
		user << "You add [amount_inserted] sheets to the [src.name]."
		overlays += "protolathe_[stack_name]"
		sleep(10)
		overlays -= "protolathe_[stack_name]"
		busy = 0
	updateUsrDialog()

license: mit


code:
/*
Research and Development System. (Designed specifically for the /tg/station 13 (Space Station 13) open source project)

///////////////Overview///////////////////
This system is a "tech tree" research and development system designed for SS13. It allows a "researcher" job (this document assumes
the "scientist" job is given this role) the tools necessiary to research new and better technologies. In general, the system works
by breaking existing technology and using what you learn from to advance your knowledge of SCIENCE! As your knowledge progresses,
you can build newer (and better?) devices (which you can also, eventually, deconstruct to advance your knowledge).

A brief overview is below. For more details, see the related files.

////////////Game Use/////////////
The major research and development is performed using a combination of four machines:
- R&D Console: A computer console that allows you to manipulate the other devices that are linked to it and view/manipulate the
technologies you have researched so far.
- Protolathe: Used to make new hand-held devices and parts for larger devices. All metals and reagents as raw materials.
- Destructive Analyzer: You can put hand-held objects into it and it'll analyze them for technological advancements but it destroys
them in the process. Destroyed items will send their raw materials to a linked Protolathe (if any)
- Circuit Imprinter: Similar to the Protolathe, it allows for the construction of circuit boards. Uses glass and acid as the raw
materials.

While researching you are dealing with two different types of information: Technology Paths and Device Designs. Technology Paths
are the "Tech Trees" of the game. You start out with a number of them at the game start and they are improved by using the
Destructive Analyzer. By themselves, they don't do a whole lot. However, they unlock Device Designs. This is the information used
by the circuit imprinter and the protolathe to produce objects. It also tracks the current reliability of that particular design.

//EXISTING TECH
Each tech path should have at LEAST one item at every level (levels 1 - 20). This is to allow for a more fluid progression of the
researching. Existing tech (ie, anything you can find on the station or get from the quartermaster) shouldn't go higher then
level 5 or 7. Everything past that should be stuff you research.

Below is a checklist to make sure every tree is filled. As new items get added to R&D, add them here if there is an empty slot.
When thinking about new stuff, check here to see if there are any slots unfilled.

//MATERIALS
1	|	Metal
2	|	Solid Plasma
3	|	Silver
4	|	Gold, Super Capacitor
5	|	Uranium, Nuclear Gun, SUPERPACMAN
6	|	Diamond, MRSPACMAN
7	|
8	|
9	|
10	|
11	|
12	|
13	|
14	|
15	|
16	|
17	|
18	|
19	|
20	|

//PLASMA TECH
1	|
2	|	Solid Plasma
3	|	Pacman Generator
4	|
5	|
6	|
7	|
8	|
9	|
10	|
11	|
12	|
13	|
14	|
15	|
16	|
17	|
18	|
19	|
20	|

//POWER TECH
1	|	Basic Capacitor, Basic Cell
2	|	High-Capacity Cell (10,000)
3	|	Super-Capacity Cell (20,000), Powersink, PACMAN
4	|	SUPERPACMAN
5	|	MRSPACMAN, Super Capacitor
6	|	Hyper-Capacity Cell (30,000)
7	|
8	|
9	|
10	|
11	|
12	|
13	|
14	|
15	|
16	|
17	|
18	|
19	|
20	|

//BLUE SPACE
1	|
2	|	Teleporter Console Board
3	|	Teleport Gun, Hand Tele
4	|	Teleportation Scroll
5	|
6	|
7	|
8	|
9	|
10	|
11	|
12	|
13	|
14	|
15	|
16	|
17	|
18	|
19	|
20	|

//BIOTECH
1	|	Bruise Pack, Scalple
2	|	PANDEMIC Board, Mass Spectrometer
3	|	AI Core, Brains (MMI)
4	|	MMI+Radio
5	|
6	|
7	|
8	|
9	|
10	|
11	|
12	|
13	|
14	|
15	|
16	|
17	|
18	|
19	|
20	|

//MAGNETS
1	|	Basic Sensor
2	|	Comm Console Board
3	|	Adv Sensor
4	|	Adv Mass Spectrometer, Chameleon Projector
5	|	Phasic Sensor
6	|
7	|
8	|
9	|
10	|
11	|
12	|
13	|
14	|
15	|
16	|
17	|
18	|
19	|
20	|

//PROGRAMMING
1	|	Arcade Board
2	|	Sec Camera
3	|	Cloning Machine Console Board
4	|	AI Core, Intellicard
5	|	Pico-Manipulator, Ultra-Micro-Laser
6	|
7	|
8	|
9	|
10	|
11	|
12	|
13	|
14	|
15	|
16	|
17	|
18	|
19	|
20	|

//SYNDICATE
1	|	Sleepypen
2	|	TYRANT Module, Emag
3	|	Power Sink
4	|
5	|
6	|
7	|
8	|
9	|
10	|
11	|
12	|
13	|
14	|
15	|
16	|
17	|
18	|
19	|
20	|

//COMBAT
1	|	Flashbang, Mousetrap, Nettle
2	|	Stun Baton
3	|	Power Axe, Death Nettle, Nuclear Gun
4	|
5	|
6	|
7	|
8	|
9	|
10	|
11	|
12	|
13	|
14	|
15	|
16	|
17	|
18	|
19	|
20	|

*/


code:
/*
Research and Development (R&D) Console

This is the main work horse of the R&D system. It contains the menus/controls for the Destructive Analyzer, Protolathe, and Circuit
imprinter. It also contains the /datum/research holder with all the known/possible technology paths and device designs.

Basic use: When it first is created, it will attempt to link up to related devices within 3 squares. It'll only link up if they
aren't already linked to another console. Any consoles it cannot link up with (either because all of a certain type are already
linked or there aren't any in range), you'll just not have access to that menu. In the settings menu, there are menu options that
allow a player to attempt to re-sync with nearby consoles. You can also force it to disconnect from a specific console.

The imprinting and construction menus do NOT require toxins access to access but all the other menus do. However, if you leave it
on a menu, nothing is to stop the person from using the options on that menu (although they won't be able to change to a different
one). You can also lock the console on the settings menu if you're feeling paranoid and you don't want anyone messing with it who
doesn't have toxins access.

When a R&D console is destroyed or even partially disassembled, you lose all research data on it. However, there are two ways around
this dire fate:
- The easiest way is to go to the settings menu and select "Sync Database with Network." That causes it to upload (but not download)
it's data to every other device in the game. Each console has a "disconnect from network" option that'll will cause data base sync
operations to skip that console. This is useful if you want to make a "public" R&D console or, for example, give the engineers
a circuit imprinter with certain designs on it and don't want it accidentally updating. The downside of this method is that you have
to have physical access to the other console to send data back. Note: An R&D console is on Centcom so if a random griffan happens to
cause a ton of data to be lost, an admin can go send it back.
- The second method is with Technology Disks and Design Disks. Each of these disks can hold a single technology or design datum in
it's entirety. You can then take the disk to any R&D console and upload it's data to it. This method is a lot more secure (since it
won't update every console in existence) but it's more of a hassle to do. Also, the disks can be stolen.

*/

/obj/machinery/computer/rdconsole
	name = "R&D Console"
	icon_screen = "rdcomp"
	icon_keyboard = "rd_key"
	circuit = /obj/item/weapon/circuitboard/rdconsole
	var/datum/research/files							//Stores all the collected research data.
	var/obj/item/weapon/disk/tech_disk/t_disk = null	//Stores the technology disk.
	var/obj/item/weapon/disk/design_disk/d_disk = null	//Stores the design disk.

	var/obj/machinery/r_n_d/destructive_analyzer/linked_destroy = null	//Linked Destructive Analyzer
	var/obj/machinery/r_n_d/protolathe/linked_lathe = null				//Linked Protolathe
	var/obj/machinery/r_n_d/circuit_imprinter/linked_imprinter = null	//Linked Circuit Imprinter

	var/screen = 1.0	//Which screen is currently showing.
	var/id = 0			//ID of the computer (for server restrictions).
	var/sync = 1		//If sync = 0, it doesn't show up on Server Control Console

	req_access = list(access_tox)	//Data and setting manipulation requires scientist access.

	var/selected_category
	var/list/datum/design/matching_designs = list() //for the search function

/proc/CallTechName(ID) //A simple helper proc to find the name of a tech with a given ID.
	for(var/T in subtypesof(/datum/tech))
		var/datum/tech/tt = T
		if(initial(tt.id) == ID)
			return initial(tt.name)

proc/CallMaterialName(ID)
	if (copytext(ID, 1, 2) == "$")
		var/return_name = copytext(ID, 2)
		switch(return_name)
			if("metal")
				return_name = "Metal"
			if("glass")
				return_name = "Glass"
			if("gold")
				return_name = "Gold"
			if("silver")
				return_name = "Silver"
			if("plasma")
				return_name = "Solid Plasma"
			if("uranium")
				return_name = "Uranium"
			if("diamond")
				return_name = "Diamond"
			if("clown")
				return_name = "Bananium"
		return return_name
	else
		for(var/R in subtypesof(/datum/reagent))
			var/datum/reagent/rr = R
			if(initial(rr.id) == ID)
				return initial(rr.name)

/obj/machinery/computer/rdconsole/proc/SyncRDevices() //Makes sure it is properly sync'ed up with the devices attached to it (if any).
	for(var/obj/machinery/r_n_d/D in oview(3,src))
		if(D.linked_console != null || D.disabled || D.panel_open)
			continue
		if(istype(D, /obj/machinery/r_n_d/destructive_analyzer))
			if(linked_destroy == null)
				linked_destroy = D
				D.linked_console = src
		else if(istype(D, /obj/machinery/r_n_d/protolathe))
			if(linked_lathe == null)
				linked_lathe = D
				D.linked_console = src
		else if(istype(D, /obj/machinery/r_n_d/circuit_imprinter))
			if(linked_imprinter == null)
				linked_imprinter = D
				D.linked_console = src
	return

//Have it automatically push research to the centcom server so wild griffins can't fuck up R&D's work --NEO
/obj/machinery/computer/rdconsole/proc/griefProtection()
	for(var/obj/machinery/r_n_d/server/centcom/C in machines)
		for(var/v in files.known_tech)
			var/datum/tech/T = files.known_tech[v]
			C.files.AddTech2Known(T)
		for(var/v in files.known_designs)
			var/datum/design/D = files.known_designs[v]
			C.files.AddDesign2Known(D)
		C.files.RefreshResearch()

/obj/machinery/computer/rdconsole/New()
	..()
	files = new /datum/research(src) //Setup the research data holder.
	matching_designs = list()
	if(!id)
		for(var/obj/machinery/r_n_d/server/centcom/S in machines)
			S.initialize()
			break

/obj/machinery/computer/rdconsole/initialize()
	SyncRDevices()

/*	Instead of calling this every tick, it is only being called when needed
/obj/machinery/computer/rdconsole/process()
	griefProtection()
*/

/obj/machinery/computer/rdconsole/attackby(obj/item/weapon/D, mob/user, params)

	//Loading a disk into it.
	if(istype(D, /obj/item/weapon/disk))
		if(t_disk || d_disk)
			user << "A disk is already loaded into the machine."
			return

		if(istype(D, /obj/item/weapon/disk/tech_disk)) t_disk = D
		else if (istype(D, /obj/item/weapon/disk/design_disk)) d_disk = D
		else
			user << "Machine cannot accept disks in that format."
			return
		if(!user.drop_item())
			return
		D.loc = src
		user << "You add the disk to the machine!"
	else if(!(linked_destroy && linked_destroy.busy) && !(linked_lathe && linked_lathe.busy) && !(linked_imprinter && linked_imprinter.busy))
		..()
	src.updateUsrDialog()
	return

/obj/machinery/computer/rdconsole/emag_act(mob/user)
	if(!emagged)
		playsound(src.loc, 'sound/effects/sparks4.ogg', 75, 1)
		emagged = 1
		user << "You disable the security protocols"

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

	add_fingerprint(usr)

	usr.set_machine(src)
	if(href_list["menu"]) //Switches menu screens. Converts a sent text string into a number. Saves a LOT of code.
		var/temp_screen = text2num(href_list["menu"])
		screen = temp_screen

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

	else if(href_list["updt_tech"]) //Update the research holder with information from the technology disk.
		screen = 0.0
		spawn(50)
			screen = 1.2
			files.AddTech2Known(t_disk.stored)
			updateUsrDialog()
			griefProtection() //Update centcom too

	else if(href_list["clear_tech"]) //Erase data on the technology disk.
		if(t_disk)
			t_disk.stored = null

	else if(href_list["eject_tech"]) //Eject the technology disk.
		if(t_disk)
			t_disk.loc = src.loc
			t_disk = null
		screen = 1.0

	else if(href_list["copy_tech"]) //Copy some technology data from the research holder to the disk.
		t_disk.stored = files.known_tech[href_list["copy_tech_ID"]]
		screen = 1.2

	else if(href_list["updt_design"]) //Updates the research holder with design data from the design disk.
		screen = 0.0
		spawn(50)
			screen = 1.4
			files.AddDesign2Known(d_disk.blueprint)
			updateUsrDialog()
			griefProtection() //Update centcom too

	else if(href_list["clear_design"]) //Erases data on the design disk.
		if(d_disk)
			d_disk.blueprint = null

	else if(href_list["eject_design"]) //Eject the design disk.
		if(d_disk)
			d_disk.loc = src.loc
			d_disk = null
		screen = 1.0

	else if(href_list["copy_design"]) //Copy design data from the research holder to the design disk.
		var/datum/design/D = files.known_designs[href_list["copy_design_ID"]]
		if(D)
			var/autolathe_friendly = 1
			for(var/x in D.materials)
				if( !(x in list(MAT_METAL, MAT_GLASS)))
					autolathe_friendly = 0
					D.category -= "Imported"
			if(D.build_type & (AUTOLATHE|PROTOLATHE|CRAFTLATHE)) // Specifically excludes circuit imprinter and mechfab
				D.build_type = autolathe_friendly ? (D.build_type | AUTOLATHE) : D.build_type
				D.category |= "Imported"
			d_disk.blueprint = D
		screen = 1.4

	else if(href_list["eject_item"]) //Eject the item inside the destructive analyzer.
		if(linked_destroy)
			if(linked_destroy.busy)
				usr << "The destructive analyzer is busy at the moment."

			else if(linked_destroy.loaded_item)
				linked_destroy.loaded_item.loc = linked_destroy.loc
				linked_destroy.loaded_item = null
				linked_destroy.icon_state = "d_analyzer"
				screen = 2.1

	else if(href_list["deconstruct"]) //Deconstruct the item in the destructive analyzer and update the research holder.
		if(linked_destroy)
			if(linked_destroy.busy)
				usr << "The destructive analyzer is busy at the moment."
			else
				var/choice = input("Proceeding will destroy loaded item.") in list("Proceed", "Cancel")
				if(choice == "Cancel" || !linked_destroy) return
				linked_destroy.busy = 1
				screen = 0.1
				updateUsrDialog()
				flick("d_analyzer_process", linked_destroy)
				spawn(24)
					if(linked_destroy)
						linked_destroy.busy = 0
						if(!linked_destroy.hacked)
							if(!linked_destroy.loaded_item)
								usr <<"The destructive analyzer appears to be empty."
								screen = 1.0
								return
							if((linked_destroy.loaded_item.reliability >= 99 - (linked_destroy.decon_mod * 3)) || linked_destroy.loaded_item.crit_fail)
								var/list/temp_tech = linked_destroy.ConvertReqString2List(linked_destroy.loaded_item.origin_tech)
								for(var/T in temp_tech)
									if(prob(linked_destroy.loaded_item.reliability))               //If deconstructed item is not reliable enough its just being wasted, else it is pocessed
										files.UpdateTech(T, temp_tech[T])                          //Check if deconstructed item has research levels higher/same/one less than current ones
								files.UpdateDesigns(linked_destroy.loaded_item, temp_tech, src)    //If if such reseach type found all the known designs are checked for having this research type in them
								screen = 1.0                                                       //If design have it it gains some reliability
							else                                                                   //Same design always gain quality
								screen = 2.3                                                       //Crit fail gives the same design a lot of reliability, like really a lot
							if(linked_lathe) //Also sends salvaged materials to a linked protolathe, if any.
								for(var/material in linked_destroy.loaded_item.materials)
									linked_lathe.materials.insert_amount(min((linked_lathe.materials.max_amount - linked_lathe.materials.total_amount), (linked_destroy.loaded_item.materials[material]*(linked_destroy.decon_mod/10))), material)
								feedback_add_details("item_deconstructed","[linked_destroy.loaded_item.type]")
							linked_destroy.loaded_item = null
						else
							screen = 1.0
						for(var/obj/I in linked_destroy.contents)
							for(var/mob/M in I.contents)
								M.death()
							if(istype(I,/obj/item/stack/sheet))//Only deconsturcts one sheet at a time instead of the entire stack
								var/obj/item/stack/sheet/S = I
								if(S.amount > 1)
									S.amount--
									linked_destroy.loaded_item = S
								else
									qdel(S)
									linked_destroy.icon_state = "d_analyzer"
							else
								if(!(I in linked_destroy.component_parts))
									qdel(I)
									linked_destroy.icon_state = "d_analyzer"
						use_power(250)
						updateUsrDialog()

	else if(href_list["lock"]) //Lock the console from use by anyone without tox access.
		if(src.allowed(usr))
			screen = text2num(href_list["lock"])
		else
			usr << "Unauthorized Access."

	else if(href_list["sync"]) //Sync the research holder with all the R&D consoles in the game that aren't sync protected.
		screen = 0.0
		if(!sync)
			usr << "You must connect to the network first!"
		else
			griefProtection() //Putting this here because I dont trust the sync process
			spawn(30)
				if(src)
					for(var/obj/machinery/r_n_d/server/S in machines)
						var/server_processed = 0
						if(S.disabled)
							continue
						if((id in S.id_with_upload) || istype(S, /obj/machinery/r_n_d/server/centcom))
							for(var/v in files.known_tech)
								var/datum/tech/T = files.known_tech[v]
								S.files.AddTech2Known(T)
							for(var/v in files.known_designs)
								var/datum/design/D = files.known_designs[v]
								S.files.AddDesign2Known(D)
							S.files.RefreshResearch()
							server_processed = 1
						if(((id in S.id_with_download) && !istype(S, /obj/machinery/r_n_d/server/centcom)) || S.hacked)
							for(var/v in S.files.known_tech)
								var/datum/tech/T = S.files.known_tech[v]
								files.AddTech2Known(T)
							for(var/v in S.files.known_designs)
								var/datum/design/D = S.files.known_designs[v]
								files.AddDesign2Known(D)
							files.RefreshResearch()
							server_processed = 1
						if(!istype(S, /obj/machinery/r_n_d/server/centcom) && server_processed)
							S.produce_heat(100)
					screen = 1.6
					updateUsrDialog()

	else if(href_list["togglesync"]) //Prevents the console from being synced by other consoles. Can still send data.
		sync = !sync

	else if(href_list["build"]) //Causes the Protolathe to build something.
		var/coeff

		if(linked_lathe)
			coeff = linked_lathe.efficiency_coeff
		else
			coeff = 1

		var/g2g = 1
		if(linked_lathe)
			var/datum/design/being_built = files.known_designs[href_list["build"]]
			if(being_built)
				var/power = 2000
				var/amount=text2num(href_list["amount"])
				var/old_screen = screen
				amount = max(1, min(10, amount))
				for(var/M in being_built.materials)
					power += round(being_built.materials[M] * amount / 5)
				power = max(2000, power)
				screen = 0.3
				if(linked_lathe.busy)
					g2g = 0
				var/key = usr.key	//so we don't lose the info during the spawn delay
				if (!(being_built.build_type & PROTOLATHE))
					g2g = 0
					message_admins("Protolathe exploit attempted by [key_name(usr, usr.client)]!")

				if (g2g) //If input is incorrect, nothing happens
					var/enough_materials = 1
					linked_lathe.busy = 1
					flick("protolathe_n",linked_lathe)
					use_power(power)

					var/list/efficient_mats = list()
					for(var/MAT in being_built.materials)
						efficient_mats[MAT] = being_built.materials[MAT]

					if(!linked_lathe.materials.has_materials(efficient_mats, amount))
						src.visible_message("The [src.name] beeps, \"Not enough materials to complete prototype.\"")
						enough_materials = 0
						g2g = 0
					else
						for(var/R in being_built.reagents)
							if(!linked_lathe.reagents.has_reagent(R, being_built.reagents[R]))
								src.visible_message("The [src.name] beeps, \"Not enough reagents to complete prototype.\"")
								enough_materials = 0
								g2g = 0

					if(enough_materials)
						linked_lathe.materials.use_amount(efficient_mats, amount)
						for(var/R in being_built.reagents)
							linked_lathe.reagents.remove_reagent(R, being_built.reagents[R])

					var/P = being_built.build_path //lets save these values before the spawn() just in case. Nobody likes runtimes.
					var/R = being_built.reliability
					spawn(32*amount/coeff)
						if(g2g) //And if we only fail the material requirements, we still spend time and power
							var/already_logged = 0
							for(var/i = 0, i<amount, i++)
								var/obj/item/new_item = new P(src)
								if( new_item.type == /obj/item/weapon/storage/backpack/holding )
									new_item.investigate_log("built by [key]","singulo")
								new_item.reliability = R
								if(!istype(new_item, /obj/item/stack/sheet)) // To avoid materials dupe glitches
									new_item.materials = efficient_mats.Copy()
								if(linked_lathe.hacked)
									R = max((new_item.reliability/2), 0)
								new_item.loc = linked_lathe.loc
								if(!already_logged)
									feedback_add_details("item_printed","[new_item.type]|[amount]")
									already_logged = 1
						linked_lathe.busy = 0
						screen = old_screen
						updateUsrDialog()

	else if(href_list["imprint"]) //Causes the Circuit Imprinter to build something.
		var/g2g = 1
		if(linked_imprinter)
			var/datum/design/being_built = files.known_designs[href_list["imprint"]]
			if(being_built)
				var/power = 2000
				var/old_screen = screen
				for(var/M in being_built.materials)
					power += round(being_built.materials[M] / 5)
				power = max(2000, power)
				screen = 0.4
				if (linked_imprinter.busy)
					g2g = 0
				if (!(being_built.build_type & IMPRINTER))
					g2g = 0
					message_admins("Circuit imprinter exploit attempted by [key_name(usr, usr.client)]!")

				if (g2g) //Again, if input is wrong, do nothing
					linked_imprinter.busy = 1
					flick("circuit_imprinter_ani",linked_imprinter)
					use_power(power)

					for(var/M in being_built.materials)
						if(!linked_imprinter.check_mat(being_built, M))
							src.visible_message("The [src.name] beeps, \"Not enough materials to complete prototype.\"")
							g2g = 0
							break
						switch(M)
							if(MAT_GLASS)
								linked_imprinter.g_amount = max(0, (linked_imprinter.g_amount-being_built.materials[M]))
							if(MAT_GOLD)
								linked_imprinter.gold_amount = max(0, (linked_imprinter.gold_amount-being_built.materials[M]))
							if(MAT_DIAMOND)
								linked_imprinter.diamond_amount = max(0, (linked_imprinter.diamond_amount-being_built.materials[M]))
							else
								linked_imprinter.reagents.remove_reagent(M, being_built.materials[M])

					var/P = being_built.build_path //lets save these values before the spawn() just in case. Nobody likes runtimes.
					var/R = being_built.reliability
					spawn(16)
						if(g2g)
							var/obj/item/new_item = new P(src)
							new_item.reliability = R
							new_item.loc = linked_imprinter.loc
							feedback_add_details("circuit_printed","[new_item.type]")
						linked_imprinter.busy = 0
						screen = old_screen
						updateUsrDialog()

	else if(href_list["disposeI"] && linked_imprinter)  //Causes the circuit imprinter to dispose of a single reagent (all of it)
		linked_imprinter.reagents.del_reagent(href_list["disposeI"])

	else if(href_list["disposeallI"] && linked_imprinter) //Causes the circuit imprinter to dispose of all it's reagents.
		linked_imprinter.reagents.clear_reagents()

	else if(href_list["disposeP"] && linked_lathe)  //Causes the protolathe to dispose of a single reagent (all of it)
		linked_lathe.reagents.del_reagent(href_list["disposeP"])

	else if(href_list["disposeallP"] && linked_lathe) //Causes the protolathe to dispose of all it's reagents.
		linked_lathe.reagents.clear_reagents()

	else if(href_list["lathe_ejectsheet"] && linked_lathe) //Causes the protolathe to eject a sheet of material
		var/desired_num_sheets = text2num(href_list["lathe_ejectsheet_amt"])
		var/MAT
		switch(href_list["lathe_ejectsheet"])
			if("metal")
				MAT = MAT_METAL
			if("glass")
				MAT = MAT_GLASS
			if("gold")
				MAT = MAT_GOLD
			if("silver")
				MAT = MAT_SILVER
			if("plasma")
				MAT = MAT_PLASMA
			if("uranium")
				MAT = MAT_URANIUM
			if("diamond")
				MAT = MAT_DIAMOND
			if("clown")
				MAT = MAT_BANANIUM
		linked_lathe.materials.retrieve_sheets(desired_num_sheets, MAT)

	else if(href_list["imprinter_ejectsheet"] && linked_imprinter) //Causes the protolathe to eject a sheet of material
		var/desired_num_sheets = text2num(href_list["imprinter_ejectsheet_amt"])
		var/res_amount, type
		switch(href_list["imprinter_ejectsheet"])
			if("glass")
				type = /obj/item/stack/sheet/glass
				res_amount = "g_amount"
			if("gold")
				type = /obj/item/stack/sheet/mineral/gold
				res_amount = "gold_amount"
			if("diamond")
				type = /obj/item/stack/sheet/mineral/diamond
				res_amount = "diamond_amount"
		if(ispath(type) && hasvar(linked_imprinter, res_amount))
			var/obj/item/stack/sheet/sheet = new type(linked_imprinter.loc)
			var/available_num_sheets = round(linked_imprinter.vars[res_amount]/sheet.perunit)
			if(available_num_sheets>0)
				sheet.amount = min(available_num_sheets, desired_num_sheets)
				linked_imprinter.vars[res_amount] = max(0, (linked_imprinter.vars[res_amount]-sheet.amount * sheet.perunit))
			else
				qdel(sheet)

	else if(href_list["find_device"]) //The R&D console looks for devices nearby to link up with.
		screen = 0.0
		spawn(20)
			SyncRDevices()
			screen = 1.7
			updateUsrDialog()

	else if(href_list["disconnect"]) //The R&D console disconnects with a specific device.
		switch(href_list["disconnect"])
			if("destroy")
				linked_destroy.linked_console = null
				linked_destroy = null
			if("lathe")
				linked_lathe.linked_console = null
				linked_lathe = null
			if("imprinter")
				linked_imprinter.linked_console = null
				linked_imprinter = null

	else if(href_list["reset"]) //Reset the R&D console's database.
		griefProtection()
		var/choice = alert("R&D Console Database Reset", "Are you sure you want to reset the R&D console's database? Data lost cannot be recovered.", "Continue", "Cancel")
		if(choice == "Continue")
			screen = 0.0
			qdel(files)
			files = new /datum/research(src)
			spawn(20)
				screen = 1.6
				updateUsrDialog()

	else if(href_list["search"]) //Search for designs with name matching pattern
		var/compare

		matching_designs.Cut()

		if(href_list["type"] == "proto")
			compare = PROTOLATHE
			screen = 3.17
		else
			compare = IMPRINTER
			screen = 4.17

		for(var/v in files.known_designs)
			var/datum/design/D = files.known_designs[v]
			if(!(D.build_type & compare))
				continue
			if(findtext(D.name,href_list["to_search"]))
				matching_designs.Add(D)

	updateUsrDialog()
	return

/obj/machinery/computer/rdconsole/attack_hand(mob/user)
	if(..())
		return
	interact(user)

/obj/machinery/computer/rdconsole/interact(mob/user)
	user.set_machine(src)
	var/dat = ""
	files.RefreshResearch()
	switch(screen) //A quick check to make sure you get the right screen when a device is disconnected.
		if(2 to 2.9)
			if(screen == 2.3)
				;
			else if(linked_destroy == null)
				screen = 2.0
			else if(linked_destroy.loaded_item == null)
				screen = 2.1
			else
				screen = 2.2
		if(3 to 3.9)
			if(linked_lathe == null)
				screen = 3.0
		if(4 to 4.9)
			if(linked_imprinter == null)
				screen = 4.0

	switch(screen)

		//////////////////////R&D CONSOLE SCREENS//////////////////
		if(0.0) dat += "
Updating Database....
"

		if(0.1) dat += "
Processing and Updating Database...
"

		if(0.2)
			dat += "
SYSTEM LOCKED
"
			dat += "Unlock"

		if(0.3)
			dat += "
Constructing Prototype. Please Wait...
"

		if(0.4)
			dat += "
Imprinting Circuit. Please Wait...
"

		if(1.0) //Main Menu
			dat += "
"
			dat += "
Main Menu:

"
			dat += "Current Research Levels
"
			if(t_disk)
				dat += "Disk Operations
"
			else if(d_disk)
				dat += "Disk Operations
"
			else
				dat += "Disk Operations
"
			if(linked_destroy)
				dat += "Destructive Analyzer Menu
"
			else
				dat += "Destructive Analyzer Menu
"
			if(linked_lathe)
				dat += "Protolathe Construction Menu
"
			else
				dat += "Protolathe Construction Menu
"
			if(linked_imprinter)
				dat += "Circuit Construction Menu
"
			else
				dat += "Circuit Construction Menu
"
			dat += "Settings"
			dat += "
"

		if(1.1) //Research viewer
			dat += "Main Menu"
			dat += "
Current Research Levels:

"
			for(var/v in files.known_tech)
				var/datum/tech/T = files.known_tech[v]
				if(T.level <= 0)
					continue
				dat += "[T.name]
"
				dat +=  "* Level: [T.level]
"
				dat +=  "* Summary: [T.desc]
"
			dat += "
"

		if(1.2) //Technology Disk Menu

			dat += "Main Menu
"
			dat += "
Technology Data Disk Contents:

"
			if(t_disk.stored == null)
				dat += "The disk has no data stored on it.
"
				dat += "Operations: "
				dat += "Load Tech to Disk"
			else
				dat += "Name: [t_disk.stored.name]
"
				dat += "Level: [t_disk.stored.level]
"
				dat += "Description: [t_disk.stored.desc]
"
				dat += "Operations: "
				dat += "Upload to Database"
				dat += "Clear Disk"
			dat += "Eject Disk"

		if(1.3) //Technology Disk submenu
			dat += "Main Menu"
			dat += "Return to Disk Operations
"
			dat += "
Load Technology to Disk:

"
			for(var/v in files.known_tech)
				var/datum/tech/T = files.known_tech[v]
				if(T.level <= 0)
					continue
				dat += "[T.name] "
				dat += "Copy to Disk
"
			dat += "
"

		if(1.4) //Design Disk menu.
			dat += "Main Menu
"
			if(d_disk.blueprint == null)
				dat += "The disk has no data stored on it.
"
				dat += "Operations: "
				dat += "Load Design to Disk"
			else
				dat += "Name: [d_disk.blueprint.name]
"
				dat += "Level: [d_disk.blueprint.reliability]
"
				var/b_type = d_disk.blueprint.build_type
				if(b_type)
					dat += "Lathe Types:
"
					if(b_type & IMPRINTER) dat += "Circuit Imprinter
"
					if(b_type & PROTOLATHE) dat += "Proto-lathe
"
					if(b_type & AUTOLATHE) dat += "Auto-lathe
"
					if(b_type & MECHFAB) dat += "Mech Fabricator
"
				dat += "Required Materials:
"
				for(var/M in d_disk.blueprint.materials)
					if(copytext(M, 1, 2) == "$") dat += "* [copytext(M, 2)] x [d_disk.blueprint.materials[M]]
"
					else dat += "* [M] x [d_disk.blueprint.materials[M]]
"
				dat += "
Operations: "
				dat += "Upload to Database"
				dat += "Clear Disk"
			dat += "Eject Disk"

		if(1.5) //Technology disk submenu
			dat += "Main Menu"
			dat += "Return to Disk Operations
"
			dat += "
Load Design to Disk:

"
			for(var/v in files.known_designs)
				var/datum/design/D = files.known_designs[v]
				dat += "[D.name] "
				dat += "Copy to Disk
"
			dat += "
"

		if(1.6) //R&D console settings
			dat += "Main Menu
"
			dat += "
R&D Console Setting:

"
			if(sync)
				dat += "Sync Database with Network
"
				dat += "Connect to Research Network
"
				dat += "Disconnect from Research Network
"
			else
				dat += "Sync Database with Network
"
				dat += "Connect to Research Network
"
				dat += "Disconnect from Research Network
"
			dat += "Device Linkage Menu
"
			dat += "Lock Console
"
			dat += "Reset R&D Database
"

		if(1.7) //R&D device linkage
			dat += "Main Menu"
			dat += "Settings Menu
"
			dat += "
R&D Console Device Linkage Menu:

"
			dat += "Re-sync with Nearby Devices

"
			dat += "
Linked Devices:

"
			if(linked_destroy)
				dat += "* Destructive Analyzer Disconnect
"
			else
				dat += "* No Destructive Analyzer Linked
"
			if(linked_lathe)
				dat += "* Protolathe Disconnect
"
			else
				dat += "* No Protolathe Linked
"
			if(linked_imprinter)
				dat += "* Circuit Imprinter Disconnect
"
			else
				dat += "* No Circuit Imprinter Linked
"
			dat += "
"

		////////////////////DESTRUCTIVE ANALYZER SCREENS////////////////////////////
		if(2.0)
			dat += "Main Menu"
			dat += "
NO DESTRUCTIVE ANALYZER LINKED TO CONSOLE
"

		if(2.1)
			dat += "Main Menu"
			dat += "
No Item Loaded. Standing-by...
"

		if(2.2)
			dat += "Main Menu
"
			dat += "
Deconstruction Menu

"
			dat += "Name: [linked_destroy.loaded_item.name]
"
			dat += "Reliability: [linked_destroy.loaded_item.reliability]
"
			dat += "Origin Tech:
"
			var/list/temp_tech = linked_destroy.ConvertReqString2List(linked_destroy.loaded_item.origin_tech)
			for(var/T in temp_tech)
				dat += "* [CallTechName(T)] [temp_tech[T]]"
				for(var/v in files.known_tech)
					var/datum/tech/F = files.known_tech[v]
					if(F.name == CallTechName(T))
						dat += " (Current: [F.level])"
						break
				dat += "
"
			dat += "
Options: "
			dat += "Deconstruct Item"
			dat += "Eject Item"
		if(2.3)
			dat += "Main Menu"
			dat += "
Item is neither reliable enough or broken enough to learn from.
"

		/////////////////////PROTOLATHE SCREENS/////////////////////////
		if(3.0)
			dat += "Main Menu
"
			dat += "
NO PROTOLATHE LINKED TO CONSOLE
"

		if(3.1)
			dat += "Main Menu "
			dat += "Material Storage"
			dat += "Chemical Storage
"
			dat += "
Protolathe Menu:

"
			dat += "Material Amount: [linked_lathe.materials.total_amount] / [linked_lathe.materials.max_amount]
"
			dat += "Chemical Volume: [linked_lathe.reagents.total_volume] / [linked_lathe.reagents.maximum_volume]
"

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

			dat += list_categories(linked_lathe.categories, 3.15)

		//Grouping designs by categories, to improve readability
		if(3.15)
			dat += "Main Menu"
			dat += "Protolathe Menu"
			dat += "

Browsing [selected_category]:

"
			dat += "Material Amount: [linked_lathe.materials.total_amount] / [linked_lathe.materials.max_amount]
"
			dat += "Chemical Volume: [linked_lathe.reagents.total_volume] / [linked_lathe.reagents.maximum_volume]
"

			for(var/v in files.known_designs)
				var/datum/design/D = files.known_designs[v]
				if(!(selected_category in D.category)|| !(D.build_type & PROTOLATHE))
					continue
				var/temp_material
				var/c = 50
				var/t
				for(var/M in D.materials)
					t = linked_lathe.check_mat(D, M)
					temp_material += " | "
					if (t < 1)
						temp_material += "[D.materials[M]] [CallMaterialName(M)]"
					else
						temp_material += " [D.materials[M]] [CallMaterialName(M)]"
					c = min(c,t)

				for(var/R in D.reagents)
					t = linked_lathe.check_mat(D, R)
					temp_material += " | "
					if (t < 1)
						temp_material += "[D.reagents[R]] [CallMaterialName(R)]"
					else
						temp_material += " [D.reagents[R]] [CallMaterialName(R)]"
					c = min(c,t)

				if (c >= 1)
					dat += "[D.name]"
					if(c >= 5)
						dat += "x5"
					if(c >= 10)
						dat += "x10"
					dat += "[temp_material]"
				else
					dat += "[D.name][temp_material]"
				dat += "
"
			dat += "
"

		if(3.17) //Display search result
			dat += "Main Menu"
			dat += "Protolathe Menu"
			dat += "

Search results:

"
			dat += "Material Amount: [linked_lathe.materials.total_amount] / [linked_lathe.materials.max_amount]
"
			dat += "Chemical Volume: [linked_lathe.reagents.total_volume] / [linked_lathe.reagents.maximum_volume]
"

			for(var/datum/design/D in matching_designs)
				var/temp_material
				var/c = 50
				var/t
				for(var/M in D.materials)
					t = linked_lathe.check_mat(D, M)
					temp_material += " | "
					if (t < 1)
						temp_material += "[D.materials[M]] [CallMaterialName(M)]"
					else
						temp_material += " [D.materials[M]] [CallMaterialName(M)]"
					c = min(c,t)

				if (c >= 1)
					dat += "[D.name]"
					if(c >= 5)
						dat += "x5"
					if(c >= 10)
						dat += "x10"
					dat += "[temp_material]"
				else
					dat += "[D.name][temp_material]"
				dat += "
"
			dat += "
"

		if(3.2) //Protolathe Material Storage Sub-menu
			dat += "Main Menu"
			dat += "Protolathe Menu
"
			dat += "
Material Storage:

"
			//Metal
			var/m_amount = linked_lathe.materials.amount(MAT_METAL)
			dat += "* [m_amount] of Metal: "
			if(m_amount >= MINERAL_MATERIAL_AMOUNT) dat += "Eject "
			if(m_amount >= MINERAL_MATERIAL_AMOUNT*5) dat += "5x "
			if(m_amount >= MINERAL_MATERIAL_AMOUNT) dat += "All"
			dat += "
"
			//Glass
			var/g_amount = linked_lathe.materials.amount(MAT_GLASS)
			dat += "* [g_amount] of Glass: "
			if(g_amount >= MINERAL_MATERIAL_AMOUNT) dat += "Eject "
			if(g_amount >= MINERAL_MATERIAL_AMOUNT*5) dat += "5x "
			if(g_amount >= MINERAL_MATERIAL_AMOUNT) dat += "All"
			dat += "
"
			//Gold
			var/gold_amount = linked_lathe.materials.amount(MAT_GOLD)
			dat += "* [gold_amount] of Gold: "
			if(gold_amount >= MINERAL_MATERIAL_AMOUNT) dat += "Eject "
			if(gold_amount >= MINERAL_MATERIAL_AMOUNT*5) dat += "5x "
			if(gold_amount >= MINERAL_MATERIAL_AMOUNT) dat += "All"
			dat += "
"
			//Silver
			var/silver_amount = linked_lathe.materials.amount(MAT_SILVER)
			dat += "* [silver_amount] of Silver: "
			if(silver_amount >= MINERAL_MATERIAL_AMOUNT) dat += "Eject "
			if(silver_amount >= MINERAL_MATERIAL_AMOUNT*5) dat += "5x "
			if(silver_amount >= MINERAL_MATERIAL_AMOUNT) dat += "All"
			dat += "
"
			//Plasma
			var/plasma_amount = linked_lathe.materials.amount(MAT_PLASMA)
			dat += "* [plasma_amount] of Solid Plasma: "
			if(plasma_amount >= MINERAL_MATERIAL_AMOUNT) dat += "Eject "
			if(plasma_amount >= MINERAL_MATERIAL_AMOUNT*5) dat += "5x "
			if(plasma_amount >= MINERAL_MATERIAL_AMOUNT) dat += "All"
			dat += "
"
			//Uranium
			var/uranium_amount = linked_lathe.materials.amount(MAT_URANIUM)
			dat += "* [uranium_amount] of Uranium: "
			if(uranium_amount >= MINERAL_MATERIAL_AMOUNT) dat += "Eject "
			if(uranium_amount >= MINERAL_MATERIAL_AMOUNT*5) dat += "5x "
			if(uranium_amount >= MINERAL_MATERIAL_AMOUNT) dat += "All"
			dat += "
"
			//Diamond
			var/diamond_amount = linked_lathe.materials.amount(MAT_DIAMOND)
			dat += "* [diamond_amount] of Diamond: "
			if(diamond_amount >= MINERAL_MATERIAL_AMOUNT) dat += "Eject "
			if(diamond_amount >= MINERAL_MATERIAL_AMOUNT*5) dat += "5x "
			if(diamond_amount >= MINERAL_MATERIAL_AMOUNT) dat += "All"
			dat += "
"
			//Bananium
			var/bananium_amount = linked_lathe.materials.amount(MAT_BANANIUM)
			dat += "* [bananium_amount] of Bananium: "
			if(bananium_amount >= MINERAL_MATERIAL_AMOUNT) dat += "Eject "
			if(bananium_amount >= MINERAL_MATERIAL_AMOUNT*5) dat += "5x "
			if(bananium_amount >= MINERAL_MATERIAL_AMOUNT) dat += "All"
			dat += "
"

		if(3.3)
			dat += "Main Menu"
			dat += "Protolathe Menu"
			dat += "Disposal All Chemicals in Storage
"
			dat += "
Chemical Storage:

"
			for(var/datum/reagent/R in linked_lathe.reagents.reagent_list)
				dat += "[R.name]: [R.volume]"
				dat += "Purge
"

		///////////////////CIRCUIT IMPRINTER SCREENS////////////////////
		if(4.0)
			dat += "Main Menu
"
			dat += "
NO CIRCUIT IMPRINTER LINKED TO CONSOLE
"

		if(4.1)
			dat += "Main Menu"
			dat += "Material Storage"
			dat += "Chemical Storage
"
			dat += "
Circuit Imprinter Menu:

"
			dat += "Material Amount: [linked_imprinter.TotalMaterials()]
"
			dat += "Chemical Volume: [linked_imprinter.reagents.total_volume]
"

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

			dat += list_categories(linked_imprinter.categories, 4.15)

		if(4.15)
			dat += "Main Menu"
			dat += "Circuit Imprinter Menu"
			dat += "

Browsing [selected_category]:

"
			dat += "Material Amount: [linked_imprinter.TotalMaterials()]
"
			dat += "Chemical Volume: [linked_imprinter.reagents.total_volume]
"

			for(var/v in files.known_designs)
				var/datum/design/D = files.known_designs[v]
				if(!(selected_category in D.category) || !(D.build_type & IMPRINTER))
					continue
				var/temp_materials
				var/check_materials = 1
				for(var/M in D.materials)
					temp_materials += " | "
					if (!linked_imprinter.check_mat(D, M))
						check_materials = 0
						temp_materials += " [D.materials[M]] [CallMaterialName(M)]"
					else
						temp_materials += " [D.materials[M]] [CallMaterialName(M)]"
				if (check_materials)
					dat += "[D.name][temp_materials]
"
				else
					dat += "[D.name][temp_materials]
"
			dat += "
"

		if(4.17)
			dat += "Main Menu"
			dat += "Circuit Imprinter Menu"
			dat += "

Search results:

"
			dat += "Material Amount: [linked_imprinter.TotalMaterials()]
"
			dat += "Chemical Volume: [linked_imprinter.reagents.total_volume]
"

			for(var/datum/design/D in matching_designs)
				var/temp_materials
				var/check_materials = 1
				for(var/M in D.materials)
					temp_materials += " | "
					if (!linked_imprinter.check_mat(D, M))
						check_materials = 0
						temp_materials += " [D.materials[M]] [CallMaterialName(M)]"
					else
						temp_materials += " [D.materials[M]] [CallMaterialName(M)]"
				if (check_materials)
					dat += "[D.name][temp_materials]
"
				else
					dat += "[D.name][temp_materials]
"
			dat += "
"

		if(4.2)
			dat += "Main Menu"
			dat += "Imprinter Menu"
			dat += "Disposal All Chemicals in Storage
"
			dat += "
Chemical Storage:

"
			for(var/datum/reagent/R in linked_imprinter.reagents.reagent_list)
				dat += "[R.name]: [R.volume]"
				dat += "Purge
"

		if(4.3)
			dat += "Main Menu"
			dat += "Circuit Imprinter Menu
"
			dat += "
Material Storage:

"
			//Glass
			dat += "* [linked_imprinter.g_amount]  glass: "
			if(linked_imprinter.g_amount >= MINERAL_MATERIAL_AMOUNT) dat += "Eject "
			if(linked_imprinter.g_amount >= MINERAL_MATERIAL_AMOUNT*5) dat += "5x "
			if(linked_imprinter.g_amount >= MINERAL_MATERIAL_AMOUNT) dat += "All"
			dat += "
"
			//Gold
			dat += "* [linked_imprinter.gold_amount] gold: "
			if(linked_imprinter.gold_amount >= MINERAL_MATERIAL_AMOUNT) dat += "Eject "
			if(linked_imprinter.gold_amount >= MINERAL_MATERIAL_AMOUNT*5) dat += "5x "
			if(linked_imprinter.gold_amount >= MINERAL_MATERIAL_AMOUNT) dat += "All"
			dat += "
"
			//Diamond
			dat += "* [linked_imprinter.diamond_amount] diamond: "
			if(linked_imprinter.diamond_amount >= MINERAL_MATERIAL_AMOUNT) dat += "Eject "
			if(linked_imprinter.diamond_amount >= MINERAL_MATERIAL_AMOUNT*5) dat += "5x "
			if(linked_imprinter.diamond_amount >= MINERAL_MATERIAL_AMOUNT) dat += "All"
			dat += "
"

	var/datum/browser/popup = new(user, "rndconsole", name, 460, 550)
	popup.set_content(dat)
	popup.open()
	return

//helper proc, which return a table containing categories
/obj/machinery/computer/rdconsole/proc/list_categories(list/categories, menu_num as num)
	if(!categories)
		return

	var/line_length = 1
	var/dat = "

"

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

"
			line_length = 1

		dat += "[C]	"
		line_length++

	dat += "

"
	return dat

/obj/machinery/computer/rdconsole/robotics
	name = "Robotics R&D Console"
	desc = "A console used to interface with R&D tools."
	id = 2
	req_access = null
	req_access_txt = "29"

/obj/machinery/computer/rdconsole/robotics/New()
	..()
	if(circuit)
		circuit.name = "circuit board (RD Console - Robotics)"
		circuit.build_path = /obj/machinery/computer/rdconsole/robotics

/obj/machinery/computer/rdconsole/core
	name = "Core R&D Console"
	desc = "A console used to interface with R&D tools."
	id = 1

/obj/machinery/computer/rdconsole/experiment
	name = "E.X.P.E.R.I-MENTOR R&D Console"
	desc = "A console used to interface with R&D tools."
	id = 3


code:
//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:33

//All devices that link into the R&D console fall into thise type for easy identification and some shared procs.

/obj/machinery/r_n_d
	name = "R&D Device"
	icon = 'icons/obj/machines/research.dmi'
	density = 1
	anchored = 1
	use_power = 1
	var/busy = 0
	var/hacked = 0
	var/disabled = 0
	var/shocked = 0
	var/obj/machinery/computer/rdconsole/linked_console

/obj/machinery/r_n_d/New()
	..()
	wires = new /datum/wires/r_n_d(src)

/obj/machinery/r_n_d/Destroy()
	qdel(wires)
	wires = null
	return ..()

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

/obj/machinery/r_n_d/attack_hand(mob/user)
	if(shocked)
		shock(user,50)
	if(panel_open)
		wires.interact(user)


code:
/*
General Explination:
The research datum is the "folder" where all the research information is stored in a R&D console. It's also a holder for all the
various procs used to manipulate it. It has four variables and seven procs:

Variables:
- possible_tech is a list of all the /datum/tech that can potentially be researched by the player. The RefreshResearch() proc
(explained later) only goes through those when refreshing what you know. Generally, possible_tech contains ALL of the existing tech
but it is possible to add tech to the game that DON'T start in it (example: Xeno tech). Generally speaking, you don't want to mess
with these since they should be the default version of the datums. They're actually stored in a list rather then using typesof to
refer to them since it makes it a bit easier to search through them for specific information.
- know_tech is the companion list to possible_tech. It's the tech you can actually research and improve. Until it's added to this
list, it can't be improved. All the tech in this list are visible to the player.
- possible_designs is functionally identical to possbile_tech except it's for /datum/design.
- known_designs is functionally identical to known_tech except it's for /datum/design

Procs:
- TechHasReqs: Used by other procs (specifically RefreshResearch) to see whether all of a tech's requirements are currently in
known_tech and at a high enough level.
- DesignHasReqs: Same as TechHasReqs but for /datum/design and known_design.
- AddTech2Known: Adds a /datum/tech to known_tech. It checks to see whether it already has that tech (if so, it just replaces it). If
it doesn't have it, it adds it. Note: It does NOT check possible_tech at all. So if you want to add something strange to it (like
a player made tech?) you can.
- AddDesign2Known: Same as AddTech2Known except for /datum/design and known_designs.
- RefreshResearch: This is the workhorse of the R&D system. It updates the /datum/research holder and adds any unlocked tech paths
and designs you have reached the requirements for. It only checks through possible_tech and possible_designs, however, so it won't
accidentally add "secret" tech to it.
- UpdateTech is used as part of the actual researching process. It takes an ID and finds techs with that same ID in known_tech. When
it finds it, it checks to see whether it can improve it at all. If the known_tech's level is less then or equal to
the inputted level, it increases the known tech's level to the inputted level -1 or know tech's level +1 (whichever is higher).

The tech datums are the actual "tech trees" that you improve through researching. Each one has five variables:
- Name:		Pretty obvious. This is often viewable to the players.
- Desc:		Pretty obvious. Also player viewable.
- ID:		This is the unique ID of the tech that is used by the various procs to find and/or maniuplate it.
- Level:	This is the current level of the tech. All techs start at 1 and have a max of 20. Devices and some techs require a certain
level in specific techs before you can produce them.
- Req_tech:	This is a list of the techs required to unlock this tech path. If left blank, it'll automatically be loaded into the
research holder datum.

*/
/***************************************************************
**						Master Types						  **
**	Includes all the helper procs and basic tech processing.  **
***************************************************************/

/datum/research								//Holder for all the existing, archived, and known tech. Individual to console.

											//Datum/tech go here.
	var/list/possible_tech = list()			//List of all tech in the game that players have access to (barring special events).
	var/list/known_tech = list()			//List of locally known tech.
	var/list/possible_designs = list()		//List of all designs (at base reliability).
	var/list/known_designs = list()			//List of available designs (at base reliability).

/datum/research/New()		//Insert techs into possible_tech here. Known_tech automatically updated.
	for(var/T in subtypesof(/datum/tech))
		possible_tech += new T(src)
	for(var/D in subtypesof(/datum/design))
		possible_designs += new D(src)
	RefreshResearch()

//Checks to see if tech has all the required pre-reqs.
//Input: datum/tech; Output: 0/1 (false/true)
/datum/research/proc/TechHasReqs(datum/tech/T)
	if(T.req_tech.len == 0)
		return TRUE
	for(var/req in T.req_tech)
		var/datum/tech/known = known_tech[req]
		if(!known || known.level < T.req_tech[req])
			return FALSE
	return TRUE

//Checks to see if design has all the required pre-reqs.
//Input: datum/design; Output: 0/1 (false/true)
/datum/research/proc/DesignHasReqs(datum/design/D)//Heavily optimized -Sieve
	if(D.req_tech.len == 0)
		return TRUE
	for(var/req in D.req_tech)
		var/datum/tech/known = known_tech[req]
		if(!known || known.level < D.req_tech[req])
			return FALSE
	return TRUE

//Adds a tech to known_tech list. Checks to make sure there aren't duplicates and updates existing tech's levels if needed.
//Input: datum/tech; Output: Null
/datum/research/proc/AddTech2Known(datum/tech/T)
	if(known_tech[T.id])
		var/datum/tech/known = known_tech[T.id]
		if(T.level > known.level)
			known.level = T.level
		return
	known_tech[T.id] = T

/datum/research/proc/AddDesign2Known(datum/design/D)
	if(known_designs[D.id])
		var/datum/design/known = known_designs[D.id]
		if(D.reliability > known.reliability)
			known.reliability = D.reliability
		return
	known_designs[D.id] = D

//Refreshes known_tech and known_designs list. Then updates the reliability vars of the designs in the known_designs list.
//Input/Output: n/a
/datum/research/proc/RefreshResearch()
	for(var/datum/tech/PT in possible_tech)
		if(TechHasReqs(PT))
			AddTech2Known(PT)

	for(var/datum/design/PD in possible_designs)
		if(DesignHasReqs(PD))
			AddDesign2Known(PD)

	for(var/v in known_tech)
		var/datum/tech/T = known_tech[v]
		T.level = Clamp(T.level, 0, 20)

	for(var/v in known_designs)
		var/datum/design/D = known_designs[v]
		D.CalcReliability(known_tech)
	return

//Refreshes the levels of a given tech.
//Input: Tech's ID and Level; Output: null
/datum/research/proc/UpdateTech(ID, level)
	var/datum/tech/KT = known_tech[ID]
	if(KT)
		if(KT.level <= level)
			KT.level = max((KT.level + 1), (level - 1))

/datum/research/proc/UpdateDesigns(obj/item/I, list/temp_tech)
	for(var/v in known_designs)
		var/datum/design/D = known_designs[v]
		var/shared_tech = length(temp_tech & D.req_tech)
		if(shared_tech)
			D.reliability = min(100, D.reliability + shared_tech)
			if(D.build_path == I.type)
				D.reliability = min(100, D.reliability + shared_tech * rand(1,3))
				if(I.crit_fail)
					D.reliability = min(100, D.reliability + shared_tech * rand(3, 5))

/datum/research/proc/FindDesignByID(id)
	return known_designs[id]

//Autolathe files
/datum/research/autolathe/New()
	for(var/T in (subtypesof(/datum/tech)))
		possible_tech += new T(src)
	for(var/path in subtypesof(/datum/design))
		var/datum/design/D = new path(src)
		possible_designs += D
		if((D.build_type & AUTOLATHE) && ("initial" in D.category))  //autolathe starts without hacked designs
			AddDesign2Known(D)

/datum/research/autolathe/AddDesign2Known(datum/design/D)
	if(!(D.build_type & AUTOLATHE))
		return
	..()

/***************************************************************
**						Technology Datums					  **
**	Includes all the various technoliges and what they make.  **
***************************************************************/

/datum/tech	//Datum of individual technologies.
	var/name = "name"					//Name of the technology.
	var/desc = "description"			//General description of what it does and what it makes.
	var/id = "id"						//An easily referenced ID. Must be alphanumeric, lower-case, and no symbols.
	var/level = 1						//A simple number scale of the research level. Level 0 = Secret tech.
	var/rare = 1						//How much CentCom wants to get that tech. Used in supply shuttle tech cost calculation.
	var/list/req_tech = list()			//List of ids associated values of techs required to research this tech. "id" = #

//Trunk Technologies (don't require any other techs and you start knowning them).

/datum/tech/materials
	name = "Materials Research"
	desc = "Development of new and improved materials."
	id = "materials"

/datum/tech/engineering
	name = "Engineering Research"
	desc = "Development of new and improved engineering parts and."
	id = "engineering"

/datum/tech/plasmatech
	name = "Plasma Research"
	desc = "Research into the mysterious substance colloqually known as 'plasma'."
	id = "plasmatech"
	rare = 3

/datum/tech/powerstorage
	name = "Power Manipulation Technology"
	desc = "The various technologies behind the storage and generation of electicity."
	id = "powerstorage"

/datum/tech/bluespace
	name = "'Blue-space' Research"
	desc = "Research into the sub-reality known as 'blue-space'"
	id = "bluespace"
	rare = 2

/datum/tech/biotech
	name = "Biological Technology"
	desc = "Research into the deeper mysteries of life and organic substances."
	id = "biotech"

/datum/tech/combat
	name = "Combat Systems Research"
	desc = "The development of offensive and defensive systems."
	id = "combat"

/datum/tech/magnets
	name = "Electromagnetic Spectrum Research"
	desc = "Research into the electromagnetic spectrum. No clue how they actually work, though."
	id = "magnets"

/datum/tech/programming
	name = "Data Theory Research"
	desc = "The development of new computer and artificial intelligence and data storage systems."
	id = "programming"

/datum/tech/syndicate
	name = "Illegal Technologies Research"
	desc = "The study of technologies that violate Nanotrassen regulations."
	id = "syndicate"
	rare = 4

/datum/tech/abductor
	name = "Alien Technologies Research"
	desc = "The study of technologies used by the advanced alien race known as Abductors."
	id = "abductor"
	rare = 5
	level = 0

/*
/datum/tech/arcane
	name = "Arcane Research"
	desc = "Research into the occult and arcane field for use in practical science"
	id = "arcane"
	level = 0 //It didn't become "secret" as advertised.

//Branch Techs
/datum/tech/explosives
	name = "Explosives Research"
	desc = "The creation and application of explosive materials."
	id = "explosives"
	req_tech = list("materials" = 3)

/datum/tech/generators
	name = "Power Generation Technology"
	desc = "Research into more powerful and more reliable sources."
	id = "generators"
	req_tech = list("powerstorage" = 2)

/datum/tech/robotics
	name = "Robotics Technology"
	desc = "The development of advanced automated, autonomous machines."
	id = "robotics"
	req_tech = list("materials" = 3, "programming" = 3)
*/

/datum/tech/proc/getCost(var/current_level = null)
	// Calculates tech disk's supply points sell cost
	if(!current_level)
		current_level = initial(level)

	if(current_level >= level)
		return 0

	var/cost = 0
	for(var/i=current_level+1, i<=level, i++)
		if(i == initial(level))
			continue
		cost += i*5*rare

	return cost

/obj/item/weapon/disk/tech_disk
	name = "Technology Disk"
	desc = "A disk for storing technology data for further research."
	icon_state = "datadisk0"
	materials = list(MAT_METAL=30, MAT_GLASS=10)
	var/datum/tech/stored

/obj/item/weapon/disk/tech_disk/New()
	src.pixel_x = rand(-5, 5)
	src.pixel_y = rand(-5, 5)


code:
/obj/machinery/r_n_d/server
	name = "R&D Server"
	icon = 'icons/obj/machines/research.dmi'
	icon_state = "server"
	var/datum/research/files
	var/health = 100
	var/list/id_with_upload = list()		//List of R&D consoles with upload to server access.
	var/list/id_with_download = list()	//List of R&D consoles with download from server access.
	var/id_with_upload_string = ""		//String versions for easy editing in map editor.
	var/id_with_download_string = ""
	var/server_id = 0
	var/heat_gen = 100
	var/heating_power = 40000
	var/delay = 10
	req_access = list(access_rd) //Only the R&D can change server settings.

/obj/machinery/r_n_d/server/New()
	..()
	component_parts = list()
	component_parts += new /obj/item/weapon/circuitboard/rdserver(null)
	component_parts += new /obj/item/weapon/stock_parts/scanning_module(null)
	component_parts += new /obj/item/stack/cable_coil(null, 1)
	component_parts += new /obj/item/stack/cable_coil(null, 1)
	RefreshParts()
	initialize(); //Agouri

/obj/machinery/r_n_d/server/Destroy()
	griefProtection()
	return ..()

/obj/machinery/r_n_d/server/RefreshParts()
	var/tot_rating = 0
	for(var/obj/item/weapon/stock_parts/SP in src)
		tot_rating += SP.rating
	heat_gen /= max(1, tot_rating)

/obj/machinery/r_n_d/server/initialize()
	if(!files) files = new /datum/research(src)
	var/list/temp_list
	if(!id_with_upload.len)
		temp_list = list()
		temp_list = splittext(id_with_upload_string, ";")
		for(var/N in temp_list)
			id_with_upload += text2num(N)
	if(!id_with_download.len)
		temp_list = list()
		temp_list = splittext(id_with_download_string, ";")
		for(var/N in temp_list)
			id_with_download += text2num(N)

/obj/machinery/r_n_d/server/process()
	var/datum/gas_mixture/environment = loc.return_air()
	switch(environment.temperature)
		if(0 to T0C)
			health = min(100, health + 1)
		if(T0C to (T20C + 20))
			health = Clamp(health, 0, 100)
		if((T20C + 20) to (T0C + 70))
			health = max(0, health - 1)
	if(health <= 0)
		/*griefProtection() This seems to get called twice before running any code that deletes/damages the server or it's files anwyay.
							refreshParts and the hasReq procs that get called by this are laggy and do not need to be called by every server on the map every tick */
		var/updateRD = 0
		files.known_designs = list()
		for(var/v in files.known_tech)
			var/datum/tech/T = files.known_tech[v]
			if(prob(1))
				updateRD++
				T.level--
		if(updateRD)
			files.RefreshResearch()
	if(delay)
		delay--
	else
		produce_heat(heat_gen)
		delay = initial(delay)

/obj/machinery/r_n_d/server/emp_act(severity)
	griefProtection()
	..()

/obj/machinery/r_n_d/server/ex_act(severity, target)
	griefProtection()
	..()

/obj/machinery/r_n_d/server/blob_act()
	griefProtection()
	..()

//Backup files to centcom to help admins recover data after greifer attacks
/obj/machinery/r_n_d/server/proc/griefProtection()
	for(var/obj/machinery/r_n_d/server/centcom/C in machines)
		for(var/v in files.known_tech)
			var/datum/tech/T = files.known_tech[v]
			C.files.AddTech2Known(T)
		for(var/v in files.known_designs)
			var/datum/design/D = files.known_designs[v]
			C.files.AddDesign2Known(D)
		C.files.RefreshResearch()

/obj/machinery/r_n_d/server/proc/produce_heat(heat_amt)
	if(!(stat & (NOPOWER|BROKEN))) //Blatently stolen from space heater.
		var/turf/L = loc
		if(istype(L))
			var/datum/gas_mixture/env = L.return_air()
			if(env.temperature < (heat_amt+T0C))

				var/transfer_moles = 0.25 * env.total_moles()

				var/datum/gas_mixture/removed = env.remove(transfer_moles)

				if(removed)

					var/heat_capacity = removed.heat_capacity()
					if(heat_capacity == 0 || heat_capacity == null)
						heat_capacity = 1
					removed.temperature = min((removed.temperature*heat_capacity + heating_power)/heat_capacity, 1000)

				env.merge(removed)
				air_update_turf()

/obj/machinery/r_n_d/server/attackby(obj/item/O, mob/user, params)
	if (disabled)
		return
	if (shocked)
		shock(user,50)
	if (default_deconstruction_screwdriver(user, "server_o", "server", O))
		return
	if(exchange_parts(user, O))
		return
	if (panel_open)
		if(istype(O, /obj/item/weapon/crowbar))
			griefProtection()
			default_deconstruction_crowbar(O)
			return 1

/obj/machinery/r_n_d/server/attack_hand(mob/user as mob) // I guess only exists to stop ninjas or hell does it even work I dunno.  See also ninja gloves.
	if (disabled)
		return
	if (shocked)
		shock(user,50)
	return

/obj/machinery/r_n_d/server/centcom
	name = "Centcom Central R&D Database"
	server_id = -1

/obj/machinery/r_n_d/server/centcom/initialize()
	..()
	var/list/no_id_servers = list()
	var/list/server_ids = list()
	for(var/obj/machinery/r_n_d/server/S in machines)
		switch(S.server_id)
			if(-1)
				continue
			if(0)
				no_id_servers += S
			else
				server_ids += S.server_id

	for(var/obj/machinery/r_n_d/server/S in no_id_servers)
		var/num = 1
		while(!S.server_id)
			if(num in server_ids)
				num++
			else
				S.server_id = num
				server_ids += num
		no_id_servers -= S

/obj/machinery/r_n_d/server/centcom/process()
	return PROCESS_KILL	//don't need process()

/obj/machinery/computer/rdservercontrol
	name = "R&D Server Controller"
	desc = "Used to manage access to research and manufacturing databases."
	icon_screen = "rdcomp"
	icon_keyboard = "rd_key"
	var/screen = 0
	var/obj/machinery/r_n_d/server/temp_server
	var/list/servers = list()
	var/list/consoles = list()
	var/badmin = 0
	circuit = /obj/item/weapon/circuitboard/rdservercontrol

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

	add_fingerprint(usr)
	usr.set_machine(src)
	if(!src.allowed(usr) && !emagged)
		usr << "You do not have the required access level."
		return

	if(href_list["main"])
		screen = 0

	else if(href_list["access"] || href_list["data"] || href_list["transfer"])
		temp_server = null
		consoles = list()
		servers = list()
		for(var/obj/machinery/r_n_d/server/S in machines)
			if(S.server_id == text2num(href_list["access"]) || S.server_id == text2num(href_list["data"]) || S.server_id == text2num(href_list["transfer"]))
				temp_server = S
				break
		if(href_list["access"])
			screen = 1
			for(var/obj/machinery/computer/rdconsole/C in machines)
				if(C.sync)
					consoles += C
		else if(href_list["data"])
			screen = 2
		else if(href_list["transfer"])
			screen = 3
			for(var/obj/machinery/r_n_d/server/S in machines)
				if(S == src)
					continue
				servers += S

	else if(href_list["upload_toggle"])
		var/num = text2num(href_list["upload_toggle"])
		if(num in temp_server.id_with_upload)
			temp_server.id_with_upload -= num
		else
			temp_server.id_with_upload += num

	else if(href_list["download_toggle"])
		var/num = text2num(href_list["download_toggle"])
		if(num in temp_server.id_with_download)
			temp_server.id_with_download -= num
		else
			temp_server.id_with_download += num

	else if(href_list["reset_tech"])
		var/choice = alert("Technology Data Reset", "Are you sure you want to reset this technology to its default data? Data lost cannot be recovered.", "Continue", "Cancel")
		if(choice == "Continue")
			var/datum/tech/T = temp_server.files.known_tech[href_list["reset_tech"]]
			if(T)
				T.level = 1
		temp_server.files.RefreshResearch()

	else if(href_list["reset_design"])
		var/choice = alert("Design Data Deletion", "Are you sure you want to delete this design? If you still have the prerequisites for the design, it'll reset to its base reliability. Data lost cannot be recovered.", "Continue", "Cancel")
		if(choice == "Continue")
			var/datum/design/D = temp_server.files.known_designs[href_list["reset_design"]]
			if(D)
				temp_server.files.known_designs -= D.id
		temp_server.files.RefreshResearch()

	updateUsrDialog()
	return

/obj/machinery/computer/rdservercontrol/attack_hand(mob/user)
	if(..())
		return
	user.set_machine(src)
	var/dat = ""

	switch(screen)
		if(0) //Main Menu
			dat += "Connected Servers:

"

			for(var/obj/machinery/r_n_d/server/S in machines)
				if(istype(S, /obj/machinery/r_n_d/server/centcom) && !badmin)
					continue
				dat += "[S.name] || "
				dat += "Access Rights | "
				dat += "Data Management"
				if(badmin) dat += " | Server-to-Server Transfer"
				dat += "
"

		if(1) //Access rights menu
			dat += "[temp_server.name] Access Rights

"
			dat += "Consoles with Upload Access
"
			for(var/obj/machinery/computer/rdconsole/C in consoles)
				var/turf/console_turf = get_turf(C)
				dat += "* [console_turf.loc]" //FYI, these are all numeric ids, eventually.
				if(C.id in temp_server.id_with_upload)
					dat += " (Remove)
"
				else
					dat += " (Add)
"
			dat += "Consoles with Download Access
"
			for(var/obj/machinery/computer/rdconsole/C in consoles)
				var/turf/console_turf = get_turf(C)
				dat += "* [console_turf.loc]"
				if(C.id in temp_server.id_with_download)
					dat += " (Remove)
"
				else
					dat += " (Add)
"
			dat += "
Main Menu"

		if(2) //Data Management menu
			dat += "[temp_server.name] Data ManagementP

"
			dat += "Known Technologies
"
			for(var/v in temp_server.files.known_tech)
				var/datum/tech/T = temp_server.files.known_tech[v]
				if(T.level <= 0)
					continue
				dat += "* [T.name] "
				dat += "(Reset)
" //FYI, these are all strings.
			dat += "Known Designs
"
			for(var/v in temp_server.files.known_designs)
				var/datum/design/D = temp_server.files.known_designs[v]
				dat += "* [D.name] "
				dat += "(Delete)
"
			dat += "
Main Menu"

		if(3) //Server Data Transfer
			dat += "[temp_server.name] Server to Server Transfer

"
			dat += "Send Data to what server?
"
			for(var/obj/machinery/r_n_d/server/S in servers)
				dat += "[S.name] (Transfer)
"
			dat += "
Main Menu"
	user << browse("<TITLE>R&D Server Control</TITLE>
[dat]", "window=server_control;size=575x400")
	onclose(user, "server_control")
	return

/obj/machinery/computer/rdservercontrol/attackby(obj/item/weapon/D, mob/user, params)
	..()
	src.updateUsrDialog()
	return

/obj/machinery/computer/rdservercontrol/emag_act(mob/user)
	if(!emagged)
		playsound(src.loc, 'sound/effects/sparks4.ogg', 75, 1)
		emagged = 1
		user << "You you disable the security protocols."

/obj/machinery/r_n_d/server/robotics
	name = "Robotics R&D Server"
	id_with_upload_string = "1;2"
	id_with_download_string = "1;2"
	server_id = 2

/obj/machinery/r_n_d/server/core
	name = "Core R&D Server"
	id_with_upload_string = "1"
	id_with_download_string = "1"
	server_id = 1


code:
/*Power cells are in code\modules\power\cell.dm*/
/obj/item/weapon/storage/part_replacer
	name = "rapid part exchange device"
	desc = "Special mechanical module made to store, sort, and apply standard machine parts."
	icon_state = "RPED"
	item_state = "RPED"
	w_class = 5
	can_hold = list(/obj/item/weapon/stock_parts)
	storage_slots = 50
	use_to_pickup = 1
	allow_quick_gather = 1
	allow_quick_empty = 1
	collection_mode = 1
	display_contents_with_number = 1
	max_w_class = 3
	max_combined_w_class = 100
	var/works_from_distance = 0
	var/pshoom_or_beepboopblorpzingshadashwoosh = 'sound/items/rped.ogg'
	var/alt_sound = null

/obj/item/weapon/storage/part_replacer/afterattack(obj/machinery/T, mob/living/carbon/human/user, flag, params)
	if(flag)
		return
	else
		if(works_from_distance)
			if(istype(T))
				if(T.component_parts)
					T.exchange_parts(user, src)
					user.Beam(T,icon_state="rped_upgrade",icon='icons/effects/effects.dmi',time=5)
	return

/obj/item/weapon/storage/part_replacer/bluespace
	name = "bluespace rapid part exchange device"
	desc = "A version of the RPED that allows for replacement of parts and scanning from a distance, along with higher capacity for parts."
	icon_state = "BS_RPED"
	w_class = 3
	storage_slots = 400
	max_w_class = 3
	max_combined_w_class = 800
	works_from_distance = 1
	pshoom_or_beepboopblorpzingshadashwoosh = 'sound/items/PSHOOM.ogg'
	alt_sound = 'sound/items/PSHOOM_2.ogg'

/obj/item/weapon/storage/part_replacer/bluespace/content_can_dump(atom/dest_object, mob/user)
	if(Adjacent(user))
		if(get_dist(user, dest_object) < 8)
			if(dest_object.storage_contents_dump_act(src, user))
				play_rped_sound()
				user.Beam(dest_object,icon_state="rped_upgrade",icon='icons/effects/effects.dmi',time=5)
				return 1
		user << "The [src.name] buzzes."
		playsound(src, 'sound/machines/buzz-sigh.ogg', 50, 0)
	return 0

/obj/item/weapon/storage/part_replacer/proc/play_rped_sound()
	//Plays the sound for RPED exhanging or installing parts.
	if(alt_sound && prob(1))
		playsound(src, alt_sound, 40, 1)
	else
		playsound(src, pshoom_or_beepboopblorpzingshadashwoosh, 40, 1)

//Sorts stock parts inside an RPED by their rating.
//Only use /obj/item/weapon/stock_parts/ with this sort proc!
/proc/cmp_rped_sort(obj/item/weapon/stock_parts/A, obj/item/weapon/stock_parts/B)
	return B.rating - A.rating

/obj/item/weapon/stock_parts
	name = "stock part"
	desc = "What?"
	icon = 'icons/obj/stock_parts.dmi'
	w_class = 2
	var/rating = 1

/obj/item/weapon/stock_parts/New()
	src.pixel_x = rand(-5, 5)
	src.pixel_y = rand(-5, 5)

//Rating 1

/obj/item/weapon/stock_parts/console_screen
	name = "console screen"
	desc = "Used in the construction of computers and other devices with a interactive console."
	icon_state = "screen"
	origin_tech = "materials=1"
	materials = list(MAT_GLASS=200)

/obj/item/weapon/stock_parts/capacitor
	name = "capacitor"
	desc = "A basic capacitor used in the construction of a variety of devices."
	icon_state = "capacitor"
	origin_tech = "powerstorage=1"
	materials = list(MAT_METAL=50, MAT_GLASS=50)

/obj/item/weapon/stock_parts/scanning_module
	name = "scanning module"
	desc = "A compact, high resolution scanning module used in the construction of certain devices."
	icon_state = "scan_module"
	origin_tech = "magnets=1"
	materials = list(MAT_METAL=50, MAT_GLASS=20)

/obj/item/weapon/stock_parts/manipulator
	name = "micro-manipulator"
	desc = "A tiny little manipulator used in the construction of certain devices."
	icon_state = "micro_mani"
	origin_tech = "materials=1;programming=1"
	materials = list(MAT_METAL=30)

/obj/item/weapon/stock_parts/micro_laser
	name = "micro-laser"
	desc = "A tiny laser used in certain devices."
	icon_state = "micro_laser"
	origin_tech = "magnets=1"
	materials = list(MAT_METAL=10, MAT_GLASS=20)

/obj/item/weapon/stock_parts/matter_bin
	name = "matter bin"
	desc = "A container designed to hold compressed matter awaiting reconstruction."
	icon_state = "matter_bin"
	origin_tech = "materials=1"
	materials = list(MAT_METAL=80)

//Rating 2

/obj/item/weapon/stock_parts/capacitor/adv
	name = "advanced capacitor"
	desc = "An advanced capacitor used in the construction of a variety of devices."
	icon_state = "adv_capacitor"
	origin_tech = "powerstorage=3"
	rating = 2
	materials = list(MAT_METAL=50, MAT_GLASS=50)

/obj/item/weapon/stock_parts/scanning_module/adv
	name = "advanced scanning module"
	desc = "A compact, high resolution scanning module used in the construction of certain devices."
	icon_state = "adv_scan_module"
	origin_tech = "magnets=3"
	rating = 2
	materials = list(MAT_METAL=50, MAT_GLASS=20)

/obj/item/weapon/stock_parts/manipulator/nano
	name = "nano-manipulator"
	desc = "A tiny little manipulator used in the construction of certain devices."
	icon_state = "nano_mani"
	origin_tech = "materials=3;programming=2"
	rating = 2
	materials = list(MAT_METAL=30)

/obj/item/weapon/stock_parts/micro_laser/high
	name = "high-power micro-laser"
	desc = "A tiny laser used in certain devices."
	icon_state = "high_micro_laser"
	origin_tech = "magnets=3"
	rating = 2
	materials = list(MAT_METAL=10, MAT_GLASS=20)

/obj/item/weapon/stock_parts/matter_bin/adv
	name = "advanced matter bin"
	desc = "A container designed to hold compressed matter awaiting reconstruction."
	icon_state = "advanced_matter_bin"
	origin_tech = "materials=3"
	rating = 2
	materials = list(MAT_METAL=80)

//Rating 3

/obj/item/weapon/stock_parts/capacitor/super
	name = "super capacitor"
	desc = "A super-high capacity capacitor used in the construction of a variety of devices."
	icon_state = "super_capacitor"
	origin_tech = "powerstorage=5;materials=4"
	rating = 3
	materials = list(MAT_METAL=50, MAT_GLASS=50)

/obj/item/weapon/stock_parts/scanning_module/phasic
	name = "phasic scanning module"
	desc = "A compact, high resolution phasic scanning module used in the construction of certain devices."
	icon_state = "super_scan_module"
	origin_tech = "magnets=5"
	rating = 3
	materials = list(MAT_METAL=50, MAT_GLASS=20)

/obj/item/weapon/stock_parts/manipulator/pico
	name = "pico-manipulator"
	desc = "A tiny little manipulator used in the construction of certain devices."
	icon_state = "pico_mani"
	origin_tech = "materials=5;programming=2"
	rating = 3
	materials = list(MAT_METAL=30)

/obj/item/weapon/stock_parts/micro_laser/ultra
	name = "ultra-high-power micro-laser"
	icon_state = "ultra_high_micro_laser"
	desc = "A tiny laser used in certain devices."
	origin_tech = "magnets=5"
	rating = 3
	materials = list(MAT_METAL=10, MAT_GLASS=20)

/obj/item/weapon/stock_parts/matter_bin/super
	name = "super matter bin"
	desc = "A container designed to hold compressed matter awaiting reconstruction."
	icon_state = "super_matter_bin"
	origin_tech = "materials=5"
	rating = 3
	materials = list(MAT_METAL=80)

//Rating 4

/obj/item/weapon/stock_parts/capacitor/quadratic
	name = "quadratic capacitor"
	desc = "An capacity capacitor used in the construction of a variety of devices."
	icon_state = "quadratic_capacitor"
	origin_tech = "powerstorage=6;materials=5"
	rating = 4
	materials = list(MAT_METAL=50, MAT_GLASS=50)

/obj/item/weapon/stock_parts/scanning_module/triphasic
	name = "triphasic scanning module"
	desc = "A compact, ultra resolution triphasic scanning module used in the construction of certain devices."
	icon_state = "triphasic_scan_module"
	origin_tech = "magnets=6"
	rating = 4
	materials = list(MAT_METAL=50, MAT_GLASS=20)

/obj/item/weapon/stock_parts/manipulator/femto
	name = "femto-manipulator"
	desc = "A tiny little manipulator used in the construction of certain devices."
	icon_state = "femto_mani"
	origin_tech = "materials=6;programming=3"
	rating = 4
	materials = list(MAT_METAL=30)

/obj/item/weapon/stock_parts/micro_laser/quadultra
	name = "quad-ultra micro-laser"
	icon_state = "quadultra_micro_laser"
	desc = "A tiny laser used in certain devices."
	origin_tech = "magnets=6"
	rating = 4
	materials = list(MAT_METAL=10, MAT_GLASS=20)

/obj/item/weapon/stock_parts/matter_bin/bluespace
	name = "bluespace matter bin"
	desc = "A container designed to hold compressed matter awaiting reconstruction."
	icon_state = "bluespace_matter_bin"
	origin_tech = "materials=6"
	rating = 4
	materials = list(MAT_METAL=80)

// Subspace stock parts

/obj/item/weapon/stock_parts/subspace/ansible
	name = "subspace ansible"
	icon_state = "subspace_ansible"
	desc = "A compact module capable of sensing extradimensional activity."
	origin_tech = "programming=2;magnets=3;materials=2;bluespace=1"
	materials = list(MAT_METAL=30, MAT_GLASS=10)

/obj/item/weapon/stock_parts/subspace/filter
	name = "hyperwave filter"
	icon_state = "hyperwave_filter"
	desc = "A tiny device capable of filtering and converting super-intense radiowaves."
	origin_tech = "programming=2;magnets=1"
	materials = list(MAT_METAL=30, MAT_GLASS=10)

/obj/item/weapon/stock_parts/subspace/amplifier
	name = "subspace amplifier"
	icon_state = "subspace_amplifier"
	desc = "A compact micro-machine capable of amplifying weak subspace transmissions."
	origin_tech = "programming=2;magnets=2;materials=2;bluespace=1"
	materials = list(MAT_METAL=30, MAT_GLASS=10)

/obj/item/weapon/stock_parts/subspace/treatment
	name = "subspace treatment disk"
	icon_state = "treatment_disk"
	desc = "A compact micro-machine capable of stretching out hyper-compressed radio waves."
	origin_tech = "programming=2;magnets=1;materials=3;bluespace=1"
	materials = list(MAT_METAL=30, MAT_GLASS=10)

/obj/item/weapon/stock_parts/subspace/analyzer
	name = "subspace wavelength analyzer"
	icon_state = "wavelength_analyzer"
	desc = "A sophisticated analyzer capable of analyzing cryptic subspace wavelengths."
	origin_tech = "programming=2;magnets=2;materials=2;bluespace=1"
	materials = list(MAT_METAL=30, MAT_GLASS=10)

/obj/item/weapon/stock_parts/subspace/crystal
	name = "ansible crystal"
	icon_state = "ansible_crystal"
	desc = "A crystal made from pure glass used to transmit laser databursts to subspace."
	origin_tech = "magnets=2;materials=2;bluespace=1"
	materials = list(MAT_GLASS=50)

/obj/item/weapon/stock_parts/subspace/transmitter
	name = "subspace transmitter"
	icon_state = "subspace_transmitter"
	desc = "A large piece of equipment used to open a window into the subspace dimension."
	origin_tech = "magnets=3;materials=3;bluespace=2"
	materials = list(MAT_METAL=50)

/obj/item/weapon/research//Makes testing much less of a pain -Sieve
	name = "research"
	icon = 'icons/obj/stock_parts.dmi'
	icon_state = "capacitor"
	desc = "A debug item for research."
	origin_tech = "materials=8;programming=8;magnets=8;powerstorage=8;bluespace=8;combat=8;biotech=8;syndicate=8;engineering=8;plasmatech=8"


code:
//Xenobio control console
/mob/camera/aiEye/remote/xenobio
	visible_icon = 1
	icon = 'icons/obj/abductor.dmi'
	icon_state = "camera_target"

/mob/camera/aiEye/remote/xenobio/setLoc(var/t)
	var/area/new_area = get_area(t)
	if(new_area && new_area.name == "Xenobiology Lab" || istype(new_area, /area/toxins/xenobiology ))
		return ..()
	else
		return

/obj/machinery/computer/camera_advanced/xenobio
	name = "Slime management console"
	desc = "A computer used for remotely handling slimes."
	networks = list("SS13")
	off_action = new/datum/action/innate/camera_off/xenobio
	var/datum/action/innate/slime_place/slime_place_action = new
	var/datum/action/innate/slime_pick_up/slime_up_action = new
	var/datum/action/innate/feed_slime/feed_slime_action = new
	var/datum/action/innate/monkey_recycle/monkey_recycle_action = new

	var/list/stored_slimes = list()
	var/max_slimes = 5
	var/monkeys = 0

	icon_screen = "slime_comp"
	icon_keyboard = "rd_key"

/obj/machinery/computer/camera_advanced/xenobio/CreateEye()
	eyeobj = new /mob/camera/aiEye/remote/xenobio()
	eyeobj.loc = get_turf(src)
	eyeobj.origin = src
	eyeobj.visible_icon = 1
	eyeobj.icon = 'icons/obj/abductor.dmi'
	eyeobj.icon_state = "camera_target"

/obj/machinery/computer/camera_advanced/xenobio/GrantActions(mob/living/carbon/user)
	off_action.target = user
	off_action.Grant(user)

	jump_action.target = user
	jump_action.Grant(user)

	slime_up_action.target = src
	slime_up_action.Grant(user)

	slime_place_action.target = src
	slime_place_action.Grant(user)

	feed_slime_action.target = src
	feed_slime_action.Grant(user)

	monkey_recycle_action.target = src
	monkey_recycle_action.Grant(user)

/obj/machinery/computer/camera_advanced/xenobio/attack_hand(mob/user)
	if(!ishuman(user)) //AIs using it might be weird
		return
	return ..()

/datum/action/innate/camera_off/xenobio/Activate()
	if(!target || !ishuman(target))
		return
	var/mob/living/carbon/C = target
	var/mob/camera/aiEye/remote/xenobio/remote_eye = C.remote_control
	var/obj/machinery/computer/camera_advanced/xenobio/origin = remote_eye.origin
	origin.current_user = null
	origin.jump_action.Remove(C)
	origin.slime_place_action.Remove(C)
	origin.slime_up_action.Remove(C)
	origin.feed_slime_action.Remove(C)
	origin.monkey_recycle_action.Remove(C)
	//All of this stuff below could probably be a proc for all advanced cameras, only the action removal needs to be camera specific
	remote_eye.eye_user = null
	C.reset_perspective(null)
	if(C.client)
		C.client.images -= remote_eye.user_image
		for(var/datum/camerachunk/chunk in remote_eye.visibleCameraChunks)
			C.client.images -= chunk.obscured
	C.remote_control = null
	C.unset_machine()
	src.Remove(C)

/datum/action/innate/slime_place
	name = "Place Slimes"
	button_icon_state = "slime_down"

/datum/action/innate/slime_place/Activate()
	if(!target || !ishuman(owner))
		return
	var/mob/living/carbon/human/C = owner
	var/mob/camera/aiEye/remote/xenobio/remote_eye = C.remote_control
	var/obj/machinery/computer/camera_advanced/xenobio/X = target

	if(cameranet.checkTurfVis(remote_eye.loc))
		for(var/mob/living/simple_animal/slime/S in X.stored_slimes)
			S.loc = remote_eye.loc
			S.visible_message("[S] warps in!")
			X.stored_slimes -= S

/datum/action/innate/slime_pick_up
	name = "Pick up Slime"
	button_icon_state = "slime_up"

/datum/action/innate/slime_pick_up/Activate()
	if(!target || !ishuman(owner))
		return
	var/mob/living/carbon/human/C = owner
	var/mob/camera/aiEye/remote/xenobio/remote_eye = C.remote_control
	var/obj/machinery/computer/camera_advanced/xenobio/X = target

	if(cameranet.checkTurfVis(remote_eye.loc))
		for(var/mob/living/simple_animal/slime/S in remote_eye.loc)
			if(X.stored_slimes.len >= X.max_slimes)
				break
			if(!S.ckey)
				if(S.buckled)
					S.Feedstop(silent=1)
				S.visible_message("[S] vanishes in a flash of light!")
				S.loc = X
				X.stored_slimes += S

/datum/action/innate/feed_slime
	name = "Feed Slimes"
	button_icon_state = "monkey_down"

/datum/action/innate/feed_slime/Activate()
	if(!target || !ishuman(owner))
		return
	var/mob/living/carbon/human/C = owner
	var/mob/camera/aiEye/remote/xenobio/remote_eye = C.remote_control
	var/obj/machinery/computer/camera_advanced/xenobio/X = target

	if(cameranet.checkTurfVis(remote_eye.loc))
		if(X.monkeys >= 1)
			var/mob/living/carbon/monkey/food = new /mob/living/carbon/monkey(remote_eye.loc)
			food.LAssailant = C
			X.monkeys --
			owner << "[X] now has [X.monkeys] monkeys left."

/datum/action/innate/monkey_recycle
	name = "Recycle Monkeys"
	button_icon_state = "monkey_up"

/datum/action/innate/monkey_recycle/Activate()
	if(!target || !ishuman(owner))
		return
	var/mob/living/carbon/human/C = owner
	var/mob/camera/aiEye/remote/xenobio/remote_eye = C.remote_control
	var/obj/machinery/computer/camera_advanced/xenobio/X = target

	if(cameranet.checkTurfVis(remote_eye.loc))
		for(var/mob/living/carbon/monkey/M in remote_eye.loc)
			if(M.stat)
				M.visible_message("[M] vanishes as they are reclaimed for recycling!")
				X.monkeys += 0.2
				qdel(M)