code:
//The advanced pea-green monochrome lcd of tomorrow.

var/global/list/obj/item/device/pda/PDAs = list()

/obj/item/device/pda
	name = "\improper PDA"
	desc = "A portable microcomputer by Thinktronic Systems, LTD. Functionality determined by a preprogrammed ROM cartridge."
	icon = 'icons/obj/pda.dmi'
	icon_state = "pda"
	item_state = "electronic"
	w_class = 1.0
	slot_flags = SLOT_ID | SLOT_BELT

	//Main variables
	var/owner = null // String name of owner
	var/default_cartridge = 0 // Access level defined by cartridge
	var/obj/item/weapon/cartridge/cartridge = null //current cartridge
	var/mode = 0 //Controls what menu the PDA will display. 0 is hub; the rest are either built in or based on cartridge.

	//Secondary variables
	var/scanmode = 0 //1 is medical scanner, 2 is forensics, 3 is reagent scanner.
	var/fon = 0 //Is the flashlight function on?
	var/f_lum = 3 //Luminosity for the flashlight function
	var/silent = 0 //To beep or not to beep, that is the question
	var/toff = 0 //If 1, messenger disabled
	var/tnote = null //Current Texts
	var/last_text //No text spamming
	var/last_honk //Also no honk spamming that's bad too
	var/ttone = "beep" //The ringtone!
	var/lock_code = "" // Lockcode to unlock uplink
	var/honkamt = 0 //How many honks left when infected with honk.exe
	var/mimeamt = 0 //How many silence left when infected with mime.exe
	var/note = "Congratulations, your station has chosen the Thinktronic 5230 Personal Data Assistant!" //Current note in the notepad function
	var/notehtml = ""
	var/notescanned = 0 // True if what is in the notekeeper was from a paper.
	var/cart = "" //A place to stick cartridge menu information
	var/detonate = 1 // Can the PDA be blown up?
	var/hidden = 0 // Is the PDA hidden from the PDA list?
	var/emped = 0

	var/obj/item/weapon/card/id/id = null //Making it possible to slot an ID card into the PDA so it can function as both.
	var/ownjob = null //related to above

	var/obj/item/device/paicard/pai = null	// A slot for a personal AI device

	var/chat_channel = "#ss13" //name of our current NTRC channel
	var/nick = "" //our NTRC nick
	var/list/ntrclog = list() //NTRC message log
	var/new_ntrc_msg = 0

	var/noreturn = 0 //whether the PDA can use the Return button, used for the aiPDA chatroom

/obj/item/device/pda/medical
	default_cartridge = /obj/item/weapon/cartridge/medical
	icon_state = "pda-medical"

/obj/item/device/pda/viro
	default_cartridge = /obj/item/weapon/cartridge/medical
	icon_state = "pda-virology"

/obj/item/device/pda/engineering
	default_cartridge = /obj/item/weapon/cartridge/engineering
	icon_state = "pda-engineer"

/obj/item/device/pda/security
	default_cartridge = /obj/item/weapon/cartridge/security
	icon_state = "pda-security"

/obj/item/device/pda/detective
	default_cartridge = /obj/item/weapon/cartridge/detective
	icon_state = "pda-detective"

/obj/item/device/pda/warden
	default_cartridge = /obj/item/weapon/cartridge/security
	icon_state = "pda-warden"

/obj/item/device/pda/janitor
	default_cartridge = /obj/item/weapon/cartridge/janitor
	icon_state = "pda-janitor"
	ttone = "slip"

/obj/item/device/pda/toxins
	default_cartridge = /obj/item/weapon/cartridge/signal/toxins
	icon_state = "pda-science"
	ttone = "boom"

/obj/item/device/pda/clown
	default_cartridge = /obj/item/weapon/cartridge/clown
	icon_state = "pda-clown"
	desc = "A portable microcomputer by Thinktronic Systems, LTD. The surface is coated with polytetrafluoroethylene and banana drippings."
	ttone = "honk"

/obj/item/device/pda/mime
	default_cartridge = /obj/item/weapon/cartridge/mime
	icon_state = "pda-mime"
	silent = 1
	ttone = "silence"

/obj/item/device/pda/heads
	default_cartridge = /obj/item/weapon/cartridge/head
	icon_state = "pda-hop"

/obj/item/device/pda/heads/hop
	default_cartridge = /obj/item/weapon/cartridge/hop
	icon_state = "pda-hop"

/obj/item/device/pda/heads/hos
	default_cartridge = /obj/item/weapon/cartridge/hos
	icon_state = "pda-hos"

/obj/item/device/pda/heads/ce
	default_cartridge = /obj/item/weapon/cartridge/ce
	icon_state = "pda-ce"

/obj/item/device/pda/heads/cmo
	default_cartridge = /obj/item/weapon/cartridge/cmo
	icon_state = "pda-cmo"

/obj/item/device/pda/heads/rd
	default_cartridge = /obj/item/weapon/cartridge/robotics/rd
	icon_state = "pda-rd"

/obj/item/device/pda/captain
	default_cartridge = /obj/item/weapon/cartridge/captain
	icon_state = "pda-captain"

/obj/item/device/pda/cargo
	default_cartridge = /obj/item/weapon/cartridge/quartermaster
	icon_state = "pda-cargo"

/obj/item/device/pda/quartermaster
	default_cartridge = /obj/item/weapon/cartridge/quartermaster
	icon_state = "pda-qm"

/obj/item/device/pda/shaftminer
	icon_state = "pda-miner"

/obj/item/device/pda/syndicate
	default_cartridge = /obj/item/weapon/cartridge/syndicate
	icon_state = "pda-syndi"
	name = "military PDA"
	owner = "John Doe"
	hidden = 1

/obj/item/device/pda/chaplain
	icon_state = "pda-chaplain"
	ttone = "holy"

/obj/item/device/pda/lawyer
	default_cartridge = /obj/item/weapon/cartridge/lawyer
	icon_state = "pda-lawyer"
	ttone = "objection"

/obj/item/device/pda/botanist
	//default_cartridge = /obj/item/weapon/cartridge/botanist
	icon_state = "pda-hydro"

/obj/item/device/pda/roboticist
	default_cartridge = /obj/item/weapon/cartridge/robotics
	icon_state = "pda-roboticist"

/obj/item/device/pda/librarian
	icon_state = "pda-library"
	default_cartridge = /obj/item/weapon/cartridge/librarian
	desc = "A portable microcomputer by Thinktronic Systems, LTD. This is model is a WGW-11 series e-reader."
	note = "Congratulations, your station has chosen the Thinktronic 5290 WGW-11 Series E-reader and Personal Data Assistant!"
	silent = 1 //Quiet in the library!

/obj/item/device/pda/clear
	icon_state = "pda-clear"
	desc = "A portable microcomputer by Thinktronic Systems, LTD. This is model is a special edition with a transparent case."
	note = "Congratulations, you have chosen the Thinktronic 5230 Personal Data Assistant Deluxe Special Max Turbo Limited Edition!"

/obj/item/device/pda/cook
	icon_state = "pda-cook"

/obj/item/device/pda/bar
	icon_state = "pda-bartender"

/obj/item/device/pda/atmos
	default_cartridge = /obj/item/weapon/cartridge/atmos
	icon_state = "pda-atmos"

/obj/item/device/pda/chemist
	default_cartridge = /obj/item/weapon/cartridge/chemistry
	icon_state = "pda-chemistry"

/obj/item/device/pda/geneticist
	default_cartridge = /obj/item/weapon/cartridge/medical
	icon_state = "pda-genetics"

// Special AI/pAI PDAs that cannot explode.
/obj/item/device/pda/ai
	icon_state = "NONE"
	ttone = "data"
	mode = 5
	noreturn = 1
	detonate = 0

/obj/item/device/pda/ai/attack_self(mob/user as mob)
	if ((honkamt > 0) && (prob(60)))//For clown virus.
		honkamt--
		playsound(loc, 'sound/items/bikehorn.ogg', 30, 1)
	..()

/obj/item/device/pda/ai/pai
	ttone = "assist"

/*
 *	The Actual PDA
 */
/obj/item/device/pda/New()
	..()
	PDAs += src
	if(default_cartridge)
		cartridge = new default_cartridge(src)
	new /obj/item/weapon/pen(src)

/obj/item/device/pda/proc/update_label()
	name = "PDA-[owner] ([ownjob])" //Name generalisation

/obj/item/device/pda/proc/can_use(mob/user)
	if(user && ismob(user))
		if(user.stat || user.restrained() || user.paralysis || user.stunned || user.weakened)
			return 0
		if(loc == user)
			return 1
	return 0

/obj/item/device/pda/GetAccess()
	if(id)
		return id.GetAccess()
	else
		return ..()

/obj/item/device/pda/GetID()
	return id

/obj/item/device/pda/MouseDrop(obj/over_object as obj, src_location, over_location)
	var/mob/M = usr
	if((!istype(over_object, /obj/screen)) && can_use(M))
		return attack_self(M)
	return

//NOTE: graphic resources are loaded on client login
/obj/item/device/pda/attack_self(mob/user as mob)

	user.set_machine(src)

	if(active_uplink_check(user))
		return

	setup_chatrooms()

	var/dat = "<title>Personal Data Assistant</title>"

	dat += " Close"

	if ((!isnull(cartridge)) && (mode == 0))
		dat += " |  Eject [cartridge]"
	if (mode && !noreturn)
		dat += " |  Return"
	dat += " |  Refresh"

	dat += "
"

	if (!owner)
		dat += "Warning: No owner information entered.  Please swipe card.

"
		dat += " Retry"
	else
		switch (mode)
			if (0)
				dat += "
PERSONAL DATA ASSISTANT v.1.2
"
				dat += "Owner: [owner], [ownjob]
"
				dat += text("ID: [id ? "[id.registered_name], [id.assignment]" : "----------"]")
				dat += text("
[id ? "Update PDA Info" : ""]

")

				dat += "[worldtime2text()]
" //:[world.time / 100 % 6][world.time / 100 % 10]"
				dat += "[time2text(world.realtime, "MMM DD")] [year_integer+540]"

				dat += "

"

				dat += "
General Functions
"
				dat += "
"
				dat += "
 Notekeeper
"
				dat += "
 Messenger
"
				dat += "
 Nanotrasen Relay Chat ([new_ntrc_msg] unread)
"

				if (cartridge)
					if (cartridge.access_clown)
						dat += "
 Honk Synthesizer
"
					if (cartridge.access_manifest)
						dat += "
 View Crew Manifest
"
					if(cartridge.access_status_display)
						dat += "
 Set Status Display
"
					dat += "
"
					if (cartridge.access_engine)
						dat += "
Engineering Functions
"
						dat += "
"
						dat += "
 Power Monitor
"
						if(istype(cartridge.radio, /obj/item/radio/integrated/floorbot))
							dat += "
 Floorbot Access
"
						dat += "
"
					if (cartridge.access_medical)
						dat += "
Medical Functions
"
						dat += "
"
						dat += "
 Medical Records
"
						dat += "
 [scanmode == 1 ? "Disable" : "Enable"] Medical Scanner
"
					if(istype(cartridge.radio, /obj/item/radio/integrated/medbot))
						dat += "
 Medibot Access
"
						dat += "
"
					else
						dat += "
"
					if (cartridge.access_security)
						dat += "
Security Functions
"
						dat += "
"
						dat += "
 Security Records
"
					if(istype(cartridge.radio, /obj/item/radio/integrated/beepsky))
						dat += "
 Security Bot Access
"
						dat += "
"
					else	dat += "
"
					if(cartridge.access_quartermaster)
						dat += "
Quartermaster Functions:
"
						dat += "
"
						dat += "
 Supply Records
"
						dat += "
 Delivery Bot Control
"
						dat += "
"
					if (cartridge.access_robotics)
						dat += "
Robotics Functions:
"
						dat += "
"
						dat += "
 Cyborg specifications
"
						dat += "
 Exosuit tracking
"
						if(cartridge.access_botcontrol)	//Can't check for the cartridge.radioclean, a bit hackish solution
							dat += "
 Cleanbot Access
"
						if(cartridge.access_botcontrol) //Ditto
							dat += "
 Floorbot Access
"
						if(cartridge.access_botcontrol)	//Ditto
							dat += "
 Medibot Access
"
						dat += "
"
				dat += "
"

				dat += "
Utilities
"
				dat += "
"
				if (cartridge)
					if (cartridge.access_janitor)
						dat += "
 Custodial Locator
"
						if(istype(cartridge.radio, /obj/item/radio/integrated/cleanbot))
							dat += "
 Cleanbot Access
"
					if (istype(cartridge.radio, /obj/item/radio/integrated/signal))
						dat += "
 Signaler System
"
					if (cartridge.access_newscaster)
						dat += "
 Newscaster Access
"
					if (cartridge.access_reagent_scanner)
						dat += "
 [scanmode == 3 ? "Disable" : "Enable"] Reagent Scanner
"
					if (cartridge.access_engine)
						dat += "
 [scanmode == 4 ? "Disable" : "Enable"] Halogen Counter
"
					if (cartridge.access_atmos)
						dat += "
 [scanmode == 5 ? "Disable" : "Enable"] Gas Scanner
"
					if (cartridge.access_remote_door)
						dat += "
 Toggle Remote Door
"
				dat += "
 Atmospheric Scan
"
				dat += "
 [fon ? "Disable" : "Enable"] Flashlight
"
				if (pai)
					if(pai.loc != src)
						pai = null
					else
						dat += "
pAI Device Configuration
"
						dat += "
Eject pAI Device
"
				dat += "
"

			if (1)
				dat += "
 Notekeeper V2.2
"
				dat += "Edit
"
				if(notescanned)
					dat += "(This is a scanned image, editing it may cause some text formatting to change.)
"
				dat += "
[(!notehtml ? note : notehtml)]"

			if (2)
				dat += "
 SpaceMessenger V3.9.6
"
				dat += " Ringer: [silent == 1 ? "Off" : "On"] | "
				dat += " Send / Receive: [toff == 1 ? "Off" : "On"] | "
				dat += " Set Ringtone | "
				dat += " Messages
"

				if (istype(cartridge, /obj/item/weapon/cartridge/syndicate))
					dat += "[cartridge:shock_charges] detonation charges left.
"
				if (istype(cartridge, /obj/item/weapon/cartridge/clown))
					dat += "[cartridge:honk_charges] viral files left.
"
				if (istype(cartridge, /obj/item/weapon/cartridge/mime))
					dat += "[cartridge:mime_charges] viral files left.
"

				dat += "
 Detected PDAs
"

				dat += "
"

				var/count = 0

				if (!toff)
					for (var/obj/item/device/pda/P in sortNames(get_viewable_pdas()))
						if (P == src)	continue
						dat += "
[P]"
						if (istype(cartridge, /obj/item/weapon/cartridge/syndicate) && P.detonate)
							dat += " (*Detonate*)"
						if (istype(cartridge, /obj/item/weapon/cartridge/clown))
							dat += " (*Send Virus*)"
						if (istype(cartridge, /obj/item/weapon/cartridge/mime))
							dat += " (*Send Virus*)"
						dat += "
"
						count++
				dat += "
"
				if (count == 0)
					dat += "None detected.
"

			if(21)
				dat += "
 SpaceMessenger V3.9.6
"
				dat += " Clear Messages"

				dat += "
 Messages
"

				dat += tnote
				dat += "
"

			if (3)
				dat += "
 Atmospheric Readings
"

				var/turf/T = get_turf(user.loc)
				if (isnull(T))
					dat += "Unable to obtain a reading.
"
				else
					var/datum/gas_mixture/environment = T.return_air()

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

					dat += "Air Pressure: [round(pressure,0.1)] kPa
"

					if (total_moles)
						var/o2_level = environment.oxygen/total_moles
						var/n2_level = environment.nitrogen/total_moles
						var/co2_level = environment.carbon_dioxide/total_moles
						var/plasma_level = environment.toxins/total_moles
						var/unknown_level =  1-(o2_level+n2_level+co2_level+plasma_level)
						dat += "Nitrogen: [round(n2_level*100,0.1)]%
"
						dat += "Oxygen: [round(o2_level*100,0.1)]%
"
						dat += "Carbon Dioxide: [round(co2_level*100,0.1)]%
"
						dat += "Plasma: [round(plasma_level*100,0.1)]%
"
						if(unknown_level > 0.01)
							dat += "OTHER: [round(unknown_level,0.1)]%
"
					dat += "Temperature: [round(environment.temperature-T0C,0.1)]°C
"
				dat += "
"

			if (5)
				new_ntrc_msg = 0
				dat += "
 Nanotrasen Relay Chat Network V1.2
"

				dat += "[nick] | "
				dat += "[chat_channel] | "
				dat += "Write message | "
				dat += "Help

"
				if(chat_channel)
					dat += ntrclog[chat_channel]

			else//Else it links to the cart menu proc. Although, it really uses menu hub 4--menu 4 doesn't really exist as it simply redirects to hub.
				dat += cart

	dat += ""
	user << browse(dat, "window=pda;size=400x444;border=1;can_resize=1;can_close=0;can_minimize=0")
	onclose(user, "pda", src)

/obj/item/device/pda/Topic(href, href_list)
	..()
	var/mob/living/U = usr
	//Looking for master was kind of pointless since PDAs don't appear to have one.
	//if ((src in U.contents) || ( istype(loc, /turf) && in_range(src, U) ) )

	if(can_use(U)) //Why reinvent the wheel? There's a proc that does exactly that.
		add_fingerprint(U)
		U.set_machine(src)

		switch(href_list["choice"])

//BASIC FUNCTIONS===================================

			if("Close")//Self explanatory
				U.unset_machine()
				U << browse(null, "window=pda")
				return
			if("Refresh")//Refresh, goes to the end of the proc.
			if("Return")//Return
				if(mode<=9)
					mode = 0
				else
					mode = round(mode/10)
					if(mode==4 || mode == 5)//Fix for cartridges. Redirects to hub.
						mode = 0
					else if(mode >= 40 && mode <= 59)//Fix for cartridges. Redirects to refresh the menu.
						cartridge.mode = mode
						cartridge.unlock()
			if ("Authenticate")//Checks for ID
				id_check(U, 1)
			if("UpdateInfo")
				ownjob = id.assignment
				update_label()
			if("Eject")//Ejects the cart, only done from hub.
				if (!isnull(cartridge))
					var/turf/T = loc
					if(ismob(T))
						T = T.loc
					cartridge.loc = T
					scanmode = 0
					if (cartridge.radio)
						cartridge.radio.hostpda = null
					cartridge = null

//MENU FUNCTIONS===================================

			if("0")//Hub
				mode = 0
			if("1")//Notes
				mode = 1
			if("2")//Messenger
				mode = 2
			if("21")//Read messeges
				mode = 21
			if("3")//Atmos scan
				mode = 3
			if("4")//Redirects to hub
				mode = 0
			if("5")//Chatroom
				mode = 5

//MAIN FUNCTIONS===================================

			if("Light")
				if(fon)
					fon = 0
					set_light(0)
				else
					fon = 1
					set_light(f_lum)
			if("Medical Scan")
				if(scanmode == 1)
					scanmode = 0
				else if((!isnull(cartridge)) && (cartridge.access_medical))
					scanmode = 1
			if("Reagent Scan")
				if(scanmode == 3)
					scanmode = 0
				else if((!isnull(cartridge)) && (cartridge.access_reagent_scanner))
					scanmode = 3
			if("Halogen Counter")
				if(scanmode == 4)
					scanmode = 0
				else if((!isnull(cartridge)) && (cartridge.access_engine))
					scanmode = 4
			if("Honk")
				if ( !(last_honk && world.time < last_honk + 20) )
					playsound(loc, 'sound/items/bikehorn.ogg', 50, 1)
					last_honk = world.time
			if("Gas Scan")
				if(scanmode == 5)
					scanmode = 0
				else if((!isnull(cartridge)) && (cartridge.access_atmos))
					scanmode = 5

//NOTEKEEPER FUNCTIONS===================================

			if ("Edit")
				var/n = stripped_multiline_input(U, "Please enter message", name, note)
				if (in_range(src, U) && loc == U)
					if (mode == 1 && n)
						note = n
						notehtml = parsepencode(n, U, SIGNFONT)
						notescanned = 0
				else
					U << browse(null, "window=pda")
					return

//MESSENGER FUNCTIONS===================================

			if("Toggle Messenger")
				toff = !toff
			if("Toggle Ringer")//If viewing texts then erase them, if not then toggle silent status
				silent = !silent
			if("Clear")//Clears messages
				tnote = null
			if("Ringtone")
				var/t = input(U, "Please enter new ringtone", name, ttone) as text
				if (in_range(src, U) && loc == U)
					if (t)
						if(src.hidden_uplink && hidden_uplink.check_trigger(U, trim(lowertext(t)), trim(lowertext(lock_code))))
							U << "The PDA softly beeps."
							U << browse(null, "window=pda")
							src.mode = 0
						else
							t = copytext(sanitize(t), 1, 20)
							ttone = t
				else
					U << browse(null, "window=pda")
					return
			if("Message")
				var/obj/item/device/pda/P = locate(href_list["target"])
				src.create_message(U, P)

			if("Send Honk")//Honk virus
				if(istype(cartridge, /obj/item/weapon/cartridge/clown))//Cartridge checks are kind of unnecessary since everything is done through switch.
					var/obj/item/device/pda/P = locate(href_list["target"])//Leaving it alone in case it may do something useful, I guess.
					if(!isnull(P))
						if (!P.toff && cartridge:honk_charges > 0)
							cartridge:honk_charges--
							U.show_message("Virus sent!", 1)
							P.honkamt = (rand(15,20))
					else
						U << "PDA not found."
				else
					U << browse(null, "window=pda")
					return
			if("Send Silence")//Silent virus
				if(istype(cartridge, /obj/item/weapon/cartridge/mime))
					var/obj/item/device/pda/P = locate(href_list["target"])
					if(!isnull(P))
						if (!P.toff && cartridge:mime_charges > 0)
							cartridge:mime_charges--
							U.show_message("Virus sent!", 1)
							P.silent = 1
							P.ttone = "silence"
					else
						U << "PDA not found."
				else
					U << browse(null, "window=pda")
					return

//CHATROOM FUNCTIONS====================================

			if("Set Nick")
				var/n = trim(stripped_input(U, "Please enter nickname", name, nick, 9))
				if(n)
					nick = n

			if("Set Channel")
				var/t = replacetext(trim(stripped_input(U, "Please enter channel", name, chat_channel, 15)), " ", "_")
				if(t)
					var/datum/chatroom/C = chatchannels[chat_channel]
					var/ret = C.parse_msg(src, nick, "/join [t]")
					if((ret in chatchannels) && (ret != chat_channel))
						ntrclog[chat_channel] = "
" + ntrclog[chat_channel]
						chat_channel = ret

			if("NTRC Message")
				var/t = msg_input(U)
				if(t)
					var/datum/chatroom/C = chatchannels[chat_channel]
					if(C)
						var/ret = C.parse_msg(src, nick, t)
						if(findtextEx(ret, "BAD_", 1, 5))
							ntrclog[chat_channel] = "[ret]
" + ntrclog[chat_channel]
						else if(ret in chatchannels)
							chat_channel = ret

			if("NTRC Help")
				var/helptext = "NTRC Commands:

"
				helptext += "/join \[#\](channel name)
/register
/log (amount of lines)

"
				usr << browse(helptext, "window=ntrchelp;size=200x200;border=1;can_resize=1;can_close=1;can_minimize=1")

//SYNDICATE FUNCTIONS===================================

			if("Toggle Door")
				if(cartridge && cartridge.access_remote_door)
					for(var/obj/machinery/door/poddoor/M in world)
						if(M.id == cartridge.remote_door_id)
							if(M.density)
								M.open()
							else
								M.close()

			if("Detonate")//Detonate PDA
				if(istype(cartridge, /obj/item/weapon/cartridge/syndicate))
					var/obj/item/device/pda/P = locate(href_list["target"])
					if(!isnull(P))
						if (!P.toff && cartridge:shock_charges > 0)
							cartridge:shock_charges--
/*
							var/difficulty = 0

							if(P.cartridge)
								difficulty += P.cartridge.access_medical
								difficulty += P.cartridge.access_security
								difficulty += P.cartridge.access_engine
								difficulty += P.cartridge.access_clown
								difficulty += P.cartridge.access_janitor
								difficulty += P.cartridge.access_manifest * 2
							else
								difficulty += 2
*/
							if(P.hidden_uplink) //technically this could be used to confirm who is a fellow tator, but it SHOULDN'T matter
								U.show_message("An error flashes on your [src].", 1)
							else
								U.show_message("Success!", 1)
								P.explode()
					else
						U << "PDA not found."
				else
					U.unset_machine()
					U << browse(null, "window=pda")
					return

//pAI FUNCTIONS===================================
			if("pai")
				switch(href_list["option"])
					if("1")		// Configure pAI device
						pai.attack_self(U)
					if("2")		// Eject pAI device
						var/turf/T = get_turf(src.loc)
						if(T)
							pai.loc = T

//LINK FUNCTIONS===================================

			else//Cartridge menu linking
				mode = text2num(href_list["choice"])
				if(cartridge)
					cartridge.mode = mode
					cartridge.unlock()
	else//If not in range, can't interact or not using the pda.
		U.unset_machine()
		U << browse(null, "window=pda")
		return

//EXTRA FUNCTIONS===================================

	if (mode == 2||mode == 21)//To clear message overlays.
		overlays.Cut()

	if ((honkamt > 0) && (prob(60)))//For clown virus.
		honkamt--
		playsound(loc, 'sound/items/bikehorn.ogg', 30, 1)

	if(U.machine == src && href_list["skiprefresh"]!="1")//Final safety.
		attack_self(U)//It auto-closes the menu prior if the user is not in range and so on.
	else
		U.unset_machine()
		U << browse(null, "window=pda")
	return

/obj/item/device/pda/proc/remove_id()
	if (id)
		if (ismob(loc))
			var/mob/M = loc
			M.put_in_hands(id)
			usr << "You remove the ID from the [name]."
		else
			id.loc = get_turf(src)
		id = null

/obj/item/device/pda/proc/msg_input(var/mob/living/U = usr)
	var/t = stripped_input(U, "Please enter message", name, null, MAX_MESSAGE_LEN)
	if (!t || toff)
		return
	if (!in_range(src, U) && loc != U)
		return
	if(!can_use(U))
		return
	if(emped)
		t = Gibberish(t, 100)
	return t

/obj/item/device/pda/proc/create_message(var/mob/living/U = usr, var/obj/item/device/pda/P)

	var/t = msg_input(U)

	if (!t)
		return

	if (last_text && world.time < last_text + 5)
		return

	if (isnull(P) || P.toff || !istype(P))
		return

	last_text = world.time
	var/obj/machinery/message_server/useMS = null
	if(message_servers)
		for (var/obj/machinery/message_server/MS in message_servers)
		//PDAs are now dependant on the Message Server.
			if(MS.active)
				useMS = MS

	var/datum/signal/signal = src.telecomms_process()

	var/useTC = 0
	if(signal)
		if(signal.data["done"])
			useTC = 1
			var/turf/pos = get_turf(P)
			if(!pos)
				return
			if(pos.z in signal.data["level"])
				useTC = 2
				//Let's make this barely readable
				if(signal.data["compression"] > 0)
					t = Gibberish(t, signal.data["compression"] + 50)

	if(useMS && useTC) // only send the message if it's stable
		if(useTC != 2) // Does our recipient have a broadcaster on their level?
			U << "ERROR: Cannot reach recipient."
			return
		useMS.send_pda_message("[P.owner]","[owner]","[t]")

		tnote += "→ To [P.owner]:
[t]
"
		P.tnote += "← From [owner] ([ownjob]):
[t]
"
		for(var/mob/M in player_list)
			if(isobserver(M) && M.client && (M.client.prefs.chat_toggles & CHAT_GHOSTPDA))
				M.show_message("PDA Message - [owner] -> [P.owner]: [t]")

		if (!P.silent)
			playsound(P.loc, 'sound/machines/twobeep.ogg', 50, 1)
			P.audible_message("\icon[P] *[P.ttone]*", null, 3)
		//Search for holder of the PDA.
		var/mob/living/L = null
		if(P.loc && isliving(P.loc))
			L = P.loc
		//Maybe they are a pAI!
		else
			L = get(P, /mob/living/silicon)

		if(L)
			L << "\icon[P] Message from [src.owner] ([ownjob]), \"[t]\" (Reply)"

		log_pda("[usr] (PDA: [src.name]) sent \"[t]\" to [P.name]")
		P.overlays.Cut()
		P.overlays += image('icons/obj/pda.dmi', "pda-r")
	else
		U << "ERROR: Server isn't responding."

/obj/item/device/pda/AltClick()
	..()

	if(issilicon(usr))
		return

	if(can_use(usr))
		if(id)
			remove_id()
		else
			usr << "This PDA does not have an ID in it."
	else
		usr << "You cannot do this while restrained."

/obj/item/device/pda/verb/verb_remove_id()
	set category = "Object"
	set name = "Eject ID"
	set src in usr

	if(issilicon(usr))
		return

	if ( can_use(usr) )
		if(id)
			remove_id()
		else
			usr << "This PDA does not have an ID in it."
	else
		usr << "You cannot do this while restrained."

/obj/item/device/pda/verb/verb_remove_pen()
	set category = "Object"
	set name = "Remove Pen"
	set src in usr

	if(issilicon(usr))
		return

	if ( can_use(usr) )
		var/obj/item/weapon/pen/O = locate() in src
		if(O)
			if (istype(loc, /mob))
				var/mob/M = loc
				if(M.get_active_hand() == null)
					M.put_in_hands(O)
					usr << "You remove \the [O] from \the [src]."
					return
			O.loc = get_turf(src)
		else
			usr << "This PDA does not have a pen in it."
	else
		usr << "You cannot do this while restrained."

/obj/item/device/pda/proc/id_check(mob/user as mob, choice as num)//To check for IDs; 1 for in-pda use, 2 for out of pda use.
	if(choice == 1)
		if (id)
			remove_id()
		else
			var/obj/item/I = user.get_active_hand()
			if (istype(I, /obj/item/weapon/card/id))
				if(!user.drop_item())
					return 0
				I.loc = src
				id = I
	else
		var/obj/item/weapon/card/I = user.get_active_hand()
		if (istype(I, /obj/item/weapon/card/id) && I:registered_name)
			var/obj/old_id = id
			if(!user.drop_item())
				return 0
			I.loc = src
			id = I
			user.put_in_hands(old_id)
	return 1

// access to status display signals
/obj/item/device/pda/attackby(obj/item/C as obj, mob/user as mob, params)
	..()
	if(istype(C, /obj/item/weapon/cartridge) && !cartridge)
		cartridge = C
		if(!user.drop_item())
			return 0
		cartridge.loc = src
		user << "You insert [cartridge] into [src]."
		if(cartridge.radio)
			cartridge.radio.hostpda = src

	else if(istype(C, /obj/item/weapon/card/id))
		var/obj/item/weapon/card/id/idcard = C
		if(!idcard.registered_name)
			user << "\The [src] rejects the ID."
			return
		if(!owner)
			owner = idcard.registered_name
			ownjob = idcard.assignment
			update_label()
			user << "Card scanned."
		else
			//Basic safety check. If either both objects are held by user or PDA is on ground and card is in hand.
			if(((src in user.contents) && (C in user.contents)) || (istype(loc, /turf) && in_range(src, user) && (C in user.contents)) )
				if( can_use(user) )//If they can still act.
					if(!id_check(user, 2))
						return 0
					user << "You put the ID into \the [src]'s slot."
					updateSelfDialog()//Update self dialog on success.
			return	//Return in case of failed check or when successful.
		updateSelfDialog()//For the non-input related code.
	else if(istype(C, /obj/item/device/paicard) && !src.pai)
		if(!user.drop_item())
			return 0
		C.loc = src
		pai = C
		user << "You slot \the [C] into [src]."
		updateUsrDialog()
	else if(istype(C, /obj/item/weapon/pen))
		var/obj/item/weapon/pen/O = locate() in src
		if(O)
			user << "There is already a pen in \the [src]."
		else
			if(!user.drop_item())
				return 0
			C.loc = src
			user << "You slide \the [C] into \the [src]."
	return 1

/obj/item/device/pda/attack(mob/living/carbon/C, mob/living/user as mob)
	if(istype(C))
		switch(scanmode)

			if(1)
				user.visible_message(text("[] has analyzed []'s vitals!", user, C))
				healthscan(user, C, 1)
				src.add_fingerprint(user)

			if(2)
				// Unused

			if(4)
				C.visible_message("[user] has analyzed [C]'s radiation levels!")

				user.show_message("Analyzing Results for [C]:")
				if(C.radiation)
					user.show_message("\green Radiation Level: \black [C.radiation]")
				else
					user.show_message("No radiation detected.")

/obj/item/device/pda/afterattack(atom/A as mob|obj|turf|area, mob/user as mob, proximity)
	if(!proximity) return
	switch(scanmode)

		if(3)
			if(!isnull(A.reagents))
				if(A.reagents.reagent_list.len > 0)
					var/reagents_length = A.reagents.reagent_list.len
					user << "[reagents_length] chemical agent[reagents_length > 1 ? "s" : ""] found."
					for (var/re in A.reagents.reagent_list)
						user << "\t [re]"
				else
					user << "No active chemical agents found in [A]."
			else
				user << "No significant chemical agents found in [A]."

		if(5)
			if (istype(A, /obj/item/weapon/tank))
				var/obj/item/weapon/tank/T = A
				atmosanalyzer_scan(T.air_contents, user, T)
			else if (istype(A, /obj/machinery/portable_atmospherics))
				var/obj/machinery/portable_atmospherics/T = A
				atmosanalyzer_scan(T.air_contents, user, T)
			else if (istype(A, /obj/machinery/atmospherics/pipe))
				var/obj/machinery/atmospherics/pipe/T = A
				atmosanalyzer_scan(T.parent.air, user, T)
			else if (istype(A, /obj/machinery/power/rad_collector))
				var/obj/machinery/power/rad_collector/T = A
				if(T.P) atmosanalyzer_scan(T.P.air_contents, user, T)
			else if (istype(A, /obj/item/weapon/flamethrower))
				var/obj/item/weapon/flamethrower/T = A
				if(T.ptank) atmosanalyzer_scan(T.ptank.air_contents, user, T)

	if (!scanmode && istype(A, /obj/item/weapon/paper) && owner)
		if (!A:info)
			user << "Unable to scan. Paper is blank."
			return
		notehtml = A:info
		note = replacetext(notehtml, "
", "\[br\]")
		note = replacetext(note, "
", "\[*\]")
		note = replacetext(note, "
", "\[list\]")
		note = replacetext(note, "
", "\[/list\]")
		note = strip_html_properly(note)
		notescanned = 1
		user << "Paper scanned. Saved to PDA's notekeeper." //concept of scanning paper copyright brainoblivion 2009

/obj/item/device/pda/proc/explode() //This needs tuning.
	if(!src.detonate) return
	var/turf/T = get_turf(src.loc)

	if (ismob(loc))
		var/mob/M = loc
		M.show_message("Your [src] explodes!", 1)

	if(T)
		T.hotspot_expose(700,125)

		explosion(T, -1, -1, 2, 3)

	qdel(src)
	return

/obj/item/device/pda/Destroy()
	PDAs -= src
	if (src.id)
		src.id.loc = get_turf(src.loc)
	..()

/obj/item/device/pda/clown/Crossed(AM as mob|obj) //Clown PDA is slippery.
	if (istype(AM, /mob/living/carbon))
		var/mob/living/carbon/M = AM
		if(M.slip(8, 5, src, NO_SLIP_WHEN_WALKING))
			if (ishuman(M) && (M.real_name != src.owner))
				if (istype(src.cartridge, /obj/item/weapon/cartridge/clown))
					var/obj/item/weapon/cartridge/clown/cart = src.cartridge
					if(cart.honk_charges < 5)
						cart.honk_charges++

//AI verb and proc for sending PDA messages.

/mob/living/silicon/ai/proc/cmd_send_pdamesg(mob/user as mob)
	var/list/names = list()
	var/list/plist = list()
	var/list/namecounts = list()

	if(user.stat == 2)
		user << "You can't send PDA messages because you are dead!"
		return

	if(src.aiPDA.toff)
		user << "Turn on your receiver in order to send messages."
		return

	for (var/obj/item/device/pda/P in get_viewable_pdas())
		if (P == src)
			continue
		else if (P == src.aiPDA)
			continue

		var/name = P.owner
		if (name in names)
			namecounts[name]++
			name = text("[name] ([namecounts[name]])")
		else
			names.Add(name)
			namecounts[name] = 1

		plist[text("[name]")] = P

	var/c = input(user, "Please select a PDA") as null|anything in sortList(plist)

	if (!c)
		return

	var/selected = plist[c]
	src.aiPDA.create_message(src, selected)

/mob/living/silicon/ai/verb/cmd_toggle_pda_receiver()
	set category = "AI Commands"
	set name = "PDA - Toggle Sender/Receiver"
	if(usr.stat == 2)
		usr << "You can't do that because you are dead!"
		return
	if(!isnull(aiPDA))
		aiPDA.toff = !aiPDA.toff
		usr << "PDA sender/receiver toggled [(aiPDA.toff ? "Off" : "On")]!"
	else
		usr << "You do not have a PDA. You should make an issue report about this."

/mob/living/silicon/ai/verb/cmd_toggle_pda_silent()
	set category = "AI Commands"
	set name = "PDA - Toggle Ringer"
	if(usr.stat == 2)
		usr << "You can't do that because you are dead!"
		return
	if(!isnull(aiPDA))
		//0
		aiPDA.silent = !aiPDA.silent
		usr << "PDA ringer toggled [(aiPDA.silent ? "Off" : "On")]!"
	else
		usr << "You do not have a PDA. You should make an issue report about this."

/mob/living/silicon/ai/verb/cmd_use_chatroom()
	set category = "AI Commands"
	set name = "PDA - Chatrooms"
	if(usr.stat == 2)
		usr << "You can't do that because you are dead!"
		return
	if(!isnull(aiPDA))
		aiPDA.mode = 5
		aiPDA.attack_self(src)
	else
		usr << "You do not have a PDA. You should make an issue report about this."

/mob/living/silicon/ai/proc/cmd_show_message_log(mob/user as mob)
	if(user.stat == 2)
		user << "You can't do that because you are dead!"
		return
	if(!isnull(aiPDA))
		var/HTML = "<title>AI PDA Message Log</title>[aiPDA.tnote]"
		user << browse(HTML, "window=log;size=400x444;border=1;can_resize=1;can_close=1;can_minimize=0")
	else
		user << "You do not have a PDA. You should make an issue report about this."

//Some spare PDAs in a box
/obj/item/weapon/storage/box/PDAs
	name = "spare PDAs"
	desc = "A box of spare PDA microcomputers."
	icon = 'icons/obj/storage.dmi'
	icon_state = "pda"

/obj/item/weapon/storage/box/PDAs/New()
	..()
	new /obj/item/device/pda(src)
	new /obj/item/device/pda(src)
	new /obj/item/device/pda(src)
	new /obj/item/device/pda(src)
	new /obj/item/weapon/cartridge/head(src)

	var/newcart = pick(	/obj/item/weapon/cartridge/engineering,
						/obj/item/weapon/cartridge/security,
						/obj/item/weapon/cartridge/medical,
						/obj/item/weapon/cartridge/signal/toxins,
						/obj/item/weapon/cartridge/quartermaster)
	new newcart(src)

// Pass along the pulse to atoms in contents, largely added so pAIs are vulnerable to EMP
/obj/item/device/pda/emp_act(severity)
	for(var/atom/A in src)
		A.emp_act(severity)
	emped += 1
	spawn(200 * severity)
		emped -= 1

//ntrc handler proc
/obj/item/device/pda/proc/msg_chat(channel as text, sender as text, message as text)
	var/msg = "[strip_html_properly(sender)]| [strip_html_properly(message)]
"
	if(!channel)
		for(var/C in ntrclog)
			ntrclog[C] = msg + ntrclog[C]
	else
		ntrclog[channel] = msg + ntrclog[channel]
	if (findtext(message, nick) && !silent)
		audible_message("\icon[src] *[ttone]*", null, 3)
	new_ntrc_msg++

/obj/item/device/pda/proc/setup_chatrooms() //this can't be done on New() because the messaging server needs to be instanced first
	if(!nick) //first time using the PDA
		//join the default chat channel
		nick = copytext(sanitize(owner), 1, 9)
		var/datum/chatroom/C = chatchannels[chat_channel]
		C.parse_msg(src, nick, "/join [chat_channel]")

/proc/get_viewable_pdas()
	. = list()
	// Returns a list of PDAs which can be viewed from another PDA/message monitor.
	for(var/obj/item/device/pda/P in PDAs)
		if(!P.owner || P.toff || P.hidden) continue
		. += P
	return .


code:
/obj/item/weapon/cartridge
	name = "generic cartridge"
	desc = "A data cartridge for portable microcomputers."
	icon = 'icons/obj/pda.dmi'
	icon_state = "cart"
	item_state = "electronic"
	w_class = 1

	var/obj/item/radio/integrated/radio = null
	var/access_security = 0
	var/access_engine = 0
	var/access_atmos = 0
	var/access_medical = 0
	var/access_manifest = 0
	var/access_clown = 0
	var/access_mime = 0
	var/access_janitor = 0
//	var/access_flora = 0
	var/access_reagent_scanner = 0
	var/access_newscaster = 0
	var/access_remote_door = 0 //Control some blast doors remotely!!
	var/remote_door_id = ""
	var/access_status_display = 0
	var/access_quartermaster = 0
	var/access_hydroponics = 0

	var/access_robotics = 0
	var/access_botcontrol = 0

	var/mode = null
	var/menu
	var/datum/data/record/active1 = null //General
	var/datum/data/record/active2 = null //Medical
	var/datum/data/record/active3 = null //Security
	var/obj/machinery/computer/monitor/powmonitor = null // Power Monitor
	var/list/powermonitors = list()
	var/message1	// used for status_displays
	var/message2
	var/list/stored_data = list()
	var/current_channel

/obj/item/weapon/cartridge/engineering
	name = "\improper Power-ON cartridge"
	icon_state = "cart-e"
	access_engine = 1

	/obj/item/weapon/cartridge/engineering/New()
		..()
		radio = new /obj/item/radio/integrated/floorbot(src)

/obj/item/weapon/cartridge/atmos
	name = "\improper BreatheDeep cartridge"
	icon_state = "cart-a"
	access_atmos = 1

	/obj/item/weapon/cartridge/atmos/New()
		..()
		radio = new /obj/item/radio/integrated/floorbot(src)

/obj/item/weapon/cartridge/medical
	name = "\improper Med-U cartridge"
	icon_state = "cart-m"
	access_medical = 1

	/obj/item/weapon/cartridge/medical/New()
		..()
		radio = new /obj/item/radio/integrated/medbot(src)

/obj/item/weapon/cartridge/chemistry
	name = "\improper ChemWhiz cartridge"
	icon_state = "cart-chem"
	access_reagent_scanner = 1

	/obj/item/weapon/cartridge/chemistry/New()
		..()
		radio = new /obj/item/radio/integrated/medbot(src)

/obj/item/weapon/cartridge/security
	name = "\improper R.O.B.U.S.T. cartridge"
	icon_state = "cart-s"
	access_security = 1

/obj/item/weapon/cartridge/security/New()
	..()
	radio = new /obj/item/radio/integrated/beepsky(src)

/obj/item/weapon/cartridge/detective
	name = "\improper D.E.T.E.C.T. cartridge"
	icon_state = "cart-s"
	access_security = 1
	access_medical = 1
	access_manifest = 1

/obj/item/weapon/cartridge/detective/New()
	..()
	radio = new /obj/item/radio/integrated/beepsky(src)

/obj/item/weapon/cartridge/janitor
	name = "\improper CustodiPRO cartridge"
	desc = "The ultimate in clean-room design."
	icon_state = "cart-j"
	access_janitor = 1

/obj/item/weapon/cartridge/janitor/New()
	..()
	radio = new /obj/item/radio/integrated/cleanbot(src)

/obj/item/weapon/cartridge/lawyer
	name = "\improper P.R.O.V.E. cartridge"
	icon_state = "cart-s"
	access_security = 1

/obj/item/weapon/cartridge/clown
	name = "\improper Honkworks 5.0 cartridge"
	icon_state = "cart-clown"
	access_clown = 1
	var/honk_charges = 5

/obj/item/weapon/cartridge/mime
	name = "\improper Gestur-O 1000 cartridge"
	icon_state = "cart-mi"
	access_mime = 1
	var/mime_charges = 5

/obj/item/weapon/cartridge/librarian
	name = "\improper Lib-Tweet cartridge"
	icon_state = "cart-s"
	access_newscaster = 1

/*
/obj/item/weapon/cartridge/botanist
	name = "\improper Green Thumb v4.20 cartridge"
	icon_state = "cart-b"
	access_flora = 1
*/

/obj/item/weapon/cartridge/signal
	name = "generic signaler cartridge"
	desc = "A data cartridge with an integrated radio signaler module."

/obj/item/weapon/cartridge/signal/toxins
	name = "\improper Signal Ace 2 cartridge"
	desc = "Complete with integrated radio signaler!"
	icon_state = "cart-tox"
	access_reagent_scanner = 1
	access_atmos = 1

/obj/item/weapon/cartridge/signal/New()
	..()
	radio = new /obj/item/radio/integrated/signal(src)

/obj/item/weapon/cartridge/quartermaster
	name = "space parts & space vendors cartridge"
	desc = "Perfect for the Quartermaster on the go!"
	icon_state = "cart-q"
	access_quartermaster = 1

/obj/item/weapon/cartridge/quartermaster/New()
	..()
	radio = new /obj/item/radio/integrated/mule(src)

/obj/item/weapon/cartridge/head
	name = "\improper Easy-Record DELUXE cartridge"
	icon_state = "cart-h"
	access_manifest = 1
	access_status_display = 1

/obj/item/weapon/cartridge/hop
	name = "\improper HumanResources9001 cartridge"
	icon_state = "cart-h"
	access_manifest = 1
	access_status_display = 1
	access_quartermaster = 1
	access_janitor = 1
	access_security = 1

/obj/item/weapon/cartridge/hop/New()
	..()
	radio = new /obj/item/radio/integrated/mule(src)

/obj/item/weapon/cartridge/hos
	name = "\improper R.O.B.U.S.T. DELUXE cartridge"
	icon_state = "cart-hos"
	access_manifest = 1
	access_status_display = 1
	access_security = 1

/obj/item/weapon/cartridge/hos/New()
	..()
	radio = new /obj/item/radio/integrated/beepsky(src)

/obj/item/weapon/cartridge/ce
	name = "\improper Power-On DELUXE cartridge"
	icon_state = "cart-ce"
	access_manifest = 1
	access_status_display = 1
	access_engine = 1
	access_atmos = 1

/obj/item/weapon/cartridge/ce/New()
	..()
	radio = new /obj/item/radio/integrated/floorbot(src)

/obj/item/weapon/cartridge/cmo
	name = "\improper Med-U DELUXE cartridge"
	icon_state = "cart-cmo"
	access_manifest = 1
	access_status_display = 1
	access_reagent_scanner = 1
	access_medical = 1

/obj/item/weapon/cartridge/cmo/New()
	..()
	radio = new /obj/item/radio/integrated/medbot(src)

/obj/item/weapon/cartridge/captain
	name = "\improper Value-PAK cartridge"
	desc = "Now with 200% more value!"
	icon_state = "cart-c"
	access_manifest = 1
	access_engine = 1
	access_security = 1
	access_medical = 1
	access_reagent_scanner = 1
	access_status_display = 1
	access_atmos = 1

/obj/item/weapon/cartridge/captain/New()
	..()
	radio = new /obj/item/radio/integrated/beepsky(src)

/obj/item/weapon/cartridge/syndicate
	name = "\improper Detomatix cartridge"
	icon_state = "cart"
	access_remote_door = 1
	remote_door_id = "smindicate" //Make sure this matches the syndicate shuttle's shield/door id!!	//don't ask about the name, testing.
	var/shock_charges = 4

/obj/item/weapon/cartridge/robotics
	name = "\improper Botmaster cartridge"
	icon_state = "cart-tox"
	access_robotics = 1
	var/obj/item/radio/integrated/radioclean	//Cleanbot radio
	var/obj/item/radio/integrated/radiofloor	//Floorbot radio
	var/obj/item/radio/integrated/radiomed		//Medbot radio

/obj/item/weapon/cartridge/robotics/New()
	..()
	radioclean = new /obj/item/radio/integrated/cleanbot(src)
	radiofloor = new /obj/item/radio/integrated/floorbot(src)
	radiomed = new /obj/item/radio/integrated/medbot(src)
	if(radioclean && radiofloor && radiomed)
		access_botcontrol = 1	//Sanity check

/obj/item/weapon/cartridge/robotics/rd	//Child of cartridge/robotics now
	name = "\improper Signal Ace DELUXE cartridge"
	icon_state = "cart-rd"
	access_manifest = 1
	access_status_display = 1
	access_reagent_scanner = 1
	access_atmos = 1
	access_robotics = 1	//Redundant since inherited from cartridge/robotics

/obj/item/weapon/cartridge/robotics/rd/New()
	..()
	radio = new /obj/item/radio/integrated/signal(src)

/obj/item/weapon/cartridge/proc/unlock()
	if (!istype(loc, /obj/item/device/pda))
		return

	generate_menu()
	print_to_host(menu)
	return

/obj/item/weapon/cartridge/proc/print_to_host(var/text)
	if (!istype(loc, /obj/item/device/pda))
		return
	loc:cart = text

	for (var/mob/M in viewers(1, loc.loc))
		if (M.client && M.machine == loc)
			loc:attack_self(M)

	return

/obj/item/weapon/cartridge/proc/post_status(var/command, var/data1, var/data2)

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

	if(!frequency) return

	var/datum/signal/status_signal = new
	status_signal.source = src
	status_signal.transmission_method = 1
	status_signal.data["command"] = command

	switch(command)
		if("message")
			status_signal.data["msg1"] = data1
			status_signal.data["msg2"] = data2
		if("alert")
			status_signal.data["picture_state"] = data1

	frequency.post_signal(src, status_signal)

/obj/item/weapon/cartridge/proc/bot_control(var/obj/item/radio/integrated/SC)

	if(!SC)
		menu = "Interlink Error - Please reinsert cartridge."
		return

	if(!SC.active)
		// list of bots
		if(!SC.botlist || (SC.botlist && SC.botlist.len==0))
			menu += "No bots found.
"

		else
			for(var/obj/machinery/bot/B in SC.botlist)
				menu += "[B] at [get_area(B)]
"

		menu += "
 Scan for active bots
"

	else	// bot selected, control it

		menu += "[SC.active]
 Status: (refresh)
"

		if(!SC.botstatus)
			menu += "Waiting for response...
"
		else

			menu += "Location: [SC.botstatus["loca"] ]
"
			menu += "Mode: "

			switch(SC.botstatus["mode"])
				if(BOT_IDLE)
					menu += "Ready"
				if(BOT_HUNT)
					menu += "Apprehending target"
				if(BOT_PREP_ARREST,BOT_ARREST)
					menu += "Arresting target"
				if(BOT_START_PATROL)
					menu += "Starting patrol"
				if(BOT_PATROL)
					menu += "On patrol"
				if(BOT_SUMMON)
					menu += "Responding to summons"
				if(BOT_CLEANING)
					menu += "Cleaning"
				if(BOT_MOVING)
					menu += "Proceeding to work site"
				if(BOT_REPAIRING)
					menu += "Performing repairs"
				if(BOT_HEALING)
					menu += "Medicating patient"
				if(BOT_RESPONDING)
					menu += "Proceeding to AI waypoint"

			menu += "
\[Stop Patrol\] "
			menu += "\[Start Patrol\] "
			menu += "\[Summon Bot\]
"
			menu += "
Return to bot list
"
			menu += "Keep an ID inserted to upload access codes upon summoning."
	return menu

/obj/item/weapon/cartridge/proc/generate_menu()
	switch(mode)
		if(40) //signaller
			menu = "
 Remote Signaling System
"

			menu += {"
Send Signal

Frequency:
-
-
[format_frequency(radio:frequency)]
+
+

Code:
-
-
[radio:code]
+
+
"}
		if (41) //crew manifest

			menu = "
 Crew Manifest
"
			menu += "Entries cannot be modified from this terminal.

"
			if(data_core.general)
				for (var/datum/data/record/t in sortRecord(data_core.general))
					menu += "[t.fields["name"]] - [t.fields["rank"]]
"
			menu += "
"

		if (42) //status displays
			menu = "
 Station Status Display Interlink
"

			menu += "\[ Clear \]
"
			menu += "\[ Shuttle ETA \]
"
			menu += "\[ Message \]"
			menu += "

 Line 1: [ message1 ? message1 : "(none)"]"
			menu += "
 Line 2: [ message2 ? message2 : "(none)"]

"
			menu += "\[ Alert: None |"
			menu += " Red Alert |"
			menu += " Lockdown |"
			menu += " Biohazard \]
"

		if (43)
			menu = "
 Power Monitors - Please select one

"
			powmonitor = null
			powermonitors = list()
			var/powercount = 0

			for(var/obj/machinery/computer/monitor/pMon in world)
				if(!(pMon.stat & (NOPOWER|BROKEN)) )
					powercount++
					powermonitors += pMon

			if(!powercount)
				menu += "No connection
"
			else

				menu += ""
				var/count = 0
				for(var/obj/machinery/computer/monitor/pMon in powermonitors)
					count++
					menu += " [pMon]
"

				menu += ""

		if (433)
			menu = "
 Power Monitor

"
			if(!powmonitor)
				menu += "No connection
"
			else
				var/list/L = list()
				for(var/obj/machinery/power/terminal/term in powmonitor.powernet.nodes)
					if(istype(term.master, /obj/machinery/power/apc))
						var/obj/machinery/power/apc/A = term.master
						L += A

				menu += "
Total power: [powmonitor.powernet.avail] W
Total load:  [num2text(powmonitor.powernet.viewload,10)] W
"

				menu += ""

				if(L.len > 0)
					menu += "Area                           Eqp./Lgt./Env.  Load   Cell
"

					var/list/S = list(" Off","AOff","  On", " AOn")
					var/list/chg = list("N","C","F")

					for(var/obj/machinery/power/apc/A in L)
						menu += copytext(add_tspace(A.area.name, 30), 1, 30)
						menu += " [S[A.equipment+1]] [S[A.lighting+1]] [S[A.environ+1]] [add_lspace(A.lastused_total, 6)]  [A.cell ? "[add_lspace(round(A.cell.percent()), 3)]% [chg[A.charging+1]]" : "  N/C"]
"

				menu += "
"

		if (44) //medical records //This thing only displays a single screen so it's hard to really get the sub-menu stuff working.
			menu = "
 Medical Record List
"
			if(data_core.general)
				for(var/datum/data/record/R in sortRecord(data_core.general))
					menu += "[R.fields["id"]]: [R.fields["name"]]
"
			menu += "
"
		if(441)
			menu = "
 Medical Record
"

			if(active1 in data_core.general)
				menu += "Name: [active1.fields["name"]] ID: [active1.fields["id"]]
"
				menu += "Sex: [active1.fields["sex"]]
"
				menu += "Age: [active1.fields["age"]]
"
				menu += "Rank: [active1.fields["rank"]]
"
				menu += "Fingerprint: [active1.fields["fingerprint"]]
"
				menu += "Physical Status: [active1.fields["p_stat"]]
"
				menu += "Mental Status: [active1.fields["m_stat"]]
"
			else
				menu += "Record Lost!
"

			menu += "
"

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

"

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

"

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

"

				menu += "Allergies: [active2.fields["alg"]]
"
				menu += "Details: [active2.fields["alg_d"]]

"

				menu += "Current Diseases: [active2.fields["cdi"]]
"
				menu += "Details: [active2.fields["cdi_d"]]

"

				menu += "Important Notes: [active2.fields["notes"]]
"
			else
				menu += "Record Lost!
"

			menu += "
"
		if (45) //security records
			menu = "
 Security Record List
"
			if(data_core.general)
				for (var/datum/data/record/R in sortRecord(data_core.general))
					menu += "[R.fields["id"]]: [R.fields["name"]]
"

			menu += "
"
		if(451)
			menu = "
 Security Record
"

			if(active1 in data_core.general)
				menu += "Name: [active1.fields["name"]] ID: [active1.fields["id"]]
"
				menu += "Sex: [active1.fields["sex"]]
"
				menu += "Age: [active1.fields["age"]]
"
				menu += "Rank: [active1.fields["rank"]]
"
				menu += "Fingerprint: [active1.fields["fingerprint"]]
"
				menu += "Physical Status: [active1.fields["p_stat"]]
"
				menu += "Mental Status: [active1.fields["m_stat"]]
"
			else
				menu += "Record Lost!
"

			menu += "
"

			menu += "
 Security Data
"
			if(active3 in data_core.security)
				menu += "Criminal Status: [active3.fields["criminal"]]
"

				menu += text("
\nMinor Crimes:")

				menu +={"

Crime
Details
Author
Time Added

"}
				for(var/datum/data/crime/c in active3.fields["mi_crim"])
					menu += "
[c.crimeName]	"
					menu += "[c.crimeDetails]	"
					menu += "[c.author]	"
					menu += "[c.time]	"
					menu += "
"
				menu += "
"

				menu += text("
\nMajor Crimes:")

				menu +={"

Crime
Details
Author
Time Added

"}
				for(var/datum/data/crime/c in active3.fields["ma_crim"])
					menu += "
[c.crimeName]	"
					menu += "[c.crimeDetails]	"
					menu += "[c.author]	"
					menu += "[c.time]	"
					menu += "
"
				menu += "
"

				menu += "
\nImportant Notes:
"
				menu += "[active3.fields["notes"]]"
			else
				menu += "Record Lost!
"

			menu += "
"
		if (46) //beepsky control
			var/obj/item/radio/integrated/beepsky/SC = radio
			menu = "
 Securitron Interlink
"
			bot_control(SC)

		if (47) //quartermaster order records
			menu = "
 Supply Record Interlink
"

			menu += "
Supply shuttle
"
			menu += "Location: "
			switch(SSshuttle.supply.mode)
				if(SHUTTLE_CALL)
					menu += "Moving to "
					if(SSshuttle.supply.z != ZLEVEL_STATION)
						menu += "station"
					else
						menu += "centcomm"
					menu += " ([SSshuttle.supply.timeLeft(600)] Mins)"
				else
					menu += "At "
					if(SSshuttle.supply.z != ZLEVEL_STATION)
						menu += "centcomm"
					else
						menu += "station"
			menu += "
Current approved orders:

"
			for(var/S in SSshuttle.shoppinglist)
				var/datum/supply_order/SO = S
				menu += "
#[SO.ordernum] - [SO.object.name] approved by [SO.orderedby] [SO.comment ? "([SO.comment])":""]
"
			menu += "
"

			menu += "Current requests:

"
			for(var/S in SSshuttle.requestlist)
				var/datum/supply_order/SO = S
				menu += "
#[SO.ordernum] - [SO.object.name] requested by [SO.orderedby]
"
			menu += "
Upgrade NOW to Space Parts & Space Vendors PLUS for full remote order control and inventory management."

		if (48) //mulebot control
			var/obj/item/radio/integrated/mule/QC = radio
			if(!QC)
				menu = "Interlink Error - Please reinsert cartridge."
				return
			menu = "
 M.U.L.E. bot Interlink V0.8
"

			if(!QC.active)
				// list of bots
				if(!QC.botlist || (QC.botlist && QC.botlist.len==0))
					menu += "No bots found.
"

				else
					for(var/obj/machinery/bot/mulebot/B in QC.botlist)
						menu += "[B] at [get_area(B)]
"

				menu += "
 Scan for active bots
"

			else	// bot selected, control it

				menu += "[QC.active]
 Status: (refresh)
"

				if(!QC.botstatus)
					menu += "Waiting for response...
"
				else

					menu += "Location: [QC.botstatus["loca"] ]
"
					menu += "Mode: "

					switch(QC.botstatus["mode"])
						if(BOT_IDLE)
							menu += "Ready"
						if(BOT_LOADING)
							menu += "Loading/Unloading"
						if(BOT_DELIVER)
							menu += "Navigating to Delivery Location"
						if(BOT_GO_HOME)
							menu += "Navigating to Home"
						if(BOT_BLOCKED)
							menu += "Waiting for clear path"
						if(BOT_NAV,BOT_WAIT_FOR_NAV)
							menu += "Calculating navigation path"
						if(BOT_NO_ROUTE)
							menu += "Unable to locate destination"
					var/obj/structure/closet/crate/C = QC.botstatus["load"]
					menu += "
Current Load: [ !C ? "none" : "[C.name] (unload)" ]
"
					menu += "Destination: [!QC.botstatus["dest"] ? "none" : QC.botstatus["dest"] ] (set)
"
					menu += "Power: [QC.botstatus["powr"]]%
"
					menu += "Home: [!QC.botstatus["home"] ? "none" : QC.botstatus["home"] ]
"
					menu += "Auto Return Home: [QC.botstatus["retn"] ? "On Off" : "(On) Off"]
"
					menu += "Auto Pickup Crate: [QC.botstatus["pick"] ? "On Off" : "(On) Off"]

"

					menu += "\[Stop\] "
					menu += "\[Proceed\] "
					menu += "\[Return Home\]
"
					menu += "
Return to bot list"

		if (49) //janitorial locator
			menu = "
 Persistent Custodial Object Locator
"

			var/turf/cl = get_turf(src)
			if (cl)
				menu += "Current Orbital Location: \[[cl.x],[cl.y]\]"

				menu += "
Located Mops:
"

				var/ldat
				for (var/obj/item/weapon/mop/M in world)
					var/turf/ml = get_turf(M)

					if(ml)
						if (ml.z != cl.z)
							continue
						var/direction = get_dir(src, M)
						ldat += "Mop - \[[ml.x],[ml.y] ([uppertext(dir2text(direction))])\] - [M.reagents.total_volume ? "Wet" : "Dry"]
"

				if (!ldat)
					menu += "None"
				else
					menu += "[ldat]"

				menu += "
Located Janitorial Cart:
"

				ldat = null
				for (var/obj/structure/janitorialcart/B in world)
					var/turf/bl = get_turf(B)

					if(bl)
						if (bl.z != cl.z)
							continue
						var/direction = get_dir(src, B)
						ldat += "Cart - \[[bl.x],[bl.y] ([uppertext(dir2text(direction))])\] - Water level: [B.reagents.total_volume]/100
"

				if (!ldat)
					menu += "None"
				else
					menu += "[ldat]"

				menu += "
Located Cleanbots:
"

				ldat = null
				for (var/obj/machinery/bot/cleanbot/B in world)
					var/turf/bl = get_turf(B)

					if(bl)
						if (bl.z != cl.z)
							continue
						var/direction = get_dir(src, B)
						ldat += "Cleanbot - \[[bl.x],[bl.y] ([uppertext(dir2text(direction))])\] - [B.on ? "Online" : "Offline"]
"

				if (!ldat)
					menu += "None"
				else
					menu += "[ldat]"

			else
				menu += "ERROR: Unable to determine current location."
			menu += "

Refresh GPS Locator"

		if (50) //Cleanbot control
			menu = "

 Cleanbot Interlink
"
			var/obj/item/radio/integrated/cleanbot/SC = radio
			bot_control(SC)

		if (51) //floorbot control
			menu = "
 Floorbot Interlink
"
			var/obj/item/radio/integrated/floorbot/SC = radio
			bot_control(SC)

		if (52) //Medibot control
			menu = "
 Medibot Interlink
"
			var/obj/item/radio/integrated/medbot/SC = radio
			bot_control(SC)

		if (53) // Newscaster
			menu = "
 Newscaster Access
"
			menu += "
 Current Newsfeed: [current_channel ? current_channel : "None"]
"
			var/datum/feed_channel/current
			for(var/datum/feed_channel/chan in news_network.network_channels)
				if (chan.channel_name == current_channel)
					current = chan
			if(!current)
				menu += "
 ERROR : NO CHANNEL FOUND
"
				return
			for(var/datum/feed_message/msg in current.messages)
				menu +="-[msg.body]
\[Story by [msg.author]\]
"
			menu += "
 Post Message"

		if (54) // Borg specs
			menu = "
 Cyborg specifications
"
			menu += "
"
			var/robots = 0
			for(var/mob/living/silicon/robot/R in mob_list)
				if(!istype(R))
					continue
				else if(R.scrambledcodes)
					continue
				robots++
				menu += "[R.name] |"
				if(R.stat)
					menu += " Not Responding
"
				else if (!R.canmove)
					menu += " Locked Down
"
				else
					menu += " Operating Normally
"
				if (!R.canmove)
				else if(R.cell)
					menu += "Battery Installed ([R.cell.charge]/[R.cell.maxcharge])
"
				else
					menu += "No Cell Installed
"
				if(R.module)
					menu += "Module Installed ([R.module.name])
"
				else
					menu += "No Module Installed
"
				if(R.connected_ai)
					menu += " Slaved to [R.connected_ai.name]"
				else
					menu += " Independent from AI"
				menu += "
"

			if(!robots)
				menu += "No Cyborg Units detected within access parameters."

		if (55) // Mech tracking
			menu = "
 Tracking beacons data
"
			for(var/obj/item/mecha_parts/mecha_tracking/TR in world)
				var/answer = TR.get_mecha_info()
				if(answer)
					menu += "
[answer]
"

/obj/item/weapon/cartridge/robotics/generate_menu()
	..()

	switch(mode)
		if (56) //Robotics cleanbot control
			if(radioclean)
				menu = "

 Cleanbot Interlink
"
				var/obj/item/radio/integrated/cleanbot/SC = radioclean
				bot_control(SC)
			else
				menu = "Cleanbot interlink error
"
		if (57) //Robotics floorbot control
			if(radiofloor)
				menu = "
 Floorbot Interlink
"
				var/obj/item/radio/integrated/floorbot/SC = radiofloor
				bot_control(SC)
			else
				menu = "Floorbot interlink error
"
		if (58) //Robotics medibot control
			if(radiomed)
				menu = "
 Medibot Interlink
"
				var/obj/item/radio/integrated/medbot/SC = radiomed
				bot_control(SC)
			else
				menu = "Medibot interlink error
"

/obj/item/weapon/cartridge/Topic(href, href_list)
	..()

	if (!usr.canmove || usr.stat || usr.restrained() || !in_range(loc, usr))
		usr.unset_machine()
		usr << browse(null, "window=pda")
		return

	switch(href_list["choice"])
		if("Medical Records")
			active1 = find_record("id", href_list["target"], data_core.general)
			if(active1)
				active2 = find_record("id", href_list["target"], data_core.medical)
			loc:mode = 441
			mode = 441
			if(!active2)
				active1 = null

		if("Security Records")
			active1 = find_record("id", href_list["target"], data_core.general)
			if(active1)
				active3 = find_record("id", href_list["target"], data_core.security)
			loc:mode = 451
			mode = 451
			if(!active3)
				active1 = null

		if("Send Signal")
			spawn( 0 )
				radio:send_signal("ACTIVATE")
				return

		if("Signal Frequency")
			var/new_frequency = sanitize_frequency(radio:frequency + text2num(href_list["sfreq"]))
			radio:set_frequency(new_frequency)

		if("Signal Code")
			radio:code += text2num(href_list["scode"])
			radio:code = round(radio:code)
			radio:code = min(100, radio:code)
			radio:code = max(1, radio:code)

		if("Status")
			switch(href_list["statdisp"])
				if("message")
					post_status("message", message1, message2)
				if("alert")
					post_status("alert", href_list["alert"])
				if("setmsg1")
					message1 = reject_bad_text(input("Line 1", "Enter Message Text", message1) as text|null, 40)
					updateSelfDialog()
				if("setmsg2")
					message2 = reject_bad_text(input("Line 2", "Enter Message Text", message2) as text|null, 40)
					updateSelfDialog()
				else
					post_status(href_list["statdisp"])
		if("Power Select")
			var/pnum = text2num(href_list["target"])
			powmonitor = powermonitors[pnum]
			loc:mode = 433
			mode = 433

		if("Newscaster Access")
			mode = 53

		if("Newscaster Message")
			var/obj/item/device/pda/pda = loc
			var/pda_owner_name = pda.id ? "[pda.id.registered_name] ([pda.id.assignment])" : "Unknown"
			var/message = pda.msg_input()
			var/datum/feed_channel/current
			for(var/datum/feed_channel/chan in news_network.network_channels)
				if (chan.channel_name == current_channel)
					current = chan
			if(current.locked && current.author != pda_owner_name)
				pda.cart += "
 ERROR : NOT AUTHORIZED [pda.id ? "" : "- ID SLOT EMPTY"]
"
				pda.Topic(null,list("choice"="Refresh"))
				return
			news_network.SubmitArticle(message,pda.owner,current_channel)
			pda.Topic(null,list("choice"=num2text(mode)))
			return

		if("Newscaster Switch Channel")
			var/obj/item/device/pda/pda = loc
			current_channel = pda.msg_input()
			pda.Topic(null,list("choice"=num2text(mode)))
			return

	generate_menu()
	print_to_host(menu)

license: mit


code:
var/datum/chatroom/default_ntrc_chatroom = new()
var/list/chatchannels = list(default_ntrc_chatroom.name = default_ntrc_chatroom)

//procs that can be used directly:
//channel.parse_msg

/datum/chatroom
	var/name = "#ss13"
	var/list/logs = list() // chat logs
	var/list/auth = list() // authenticated clients
	var/list/authed = list() //authenticated users
	var/datum/events/events = new ()
	var/list/datum/event/evlist = list()

//parse_msg() is the exposed interface chat
//clients should interact with parse_msg() and parse_msg() only

/datum/chatroom/proc/parse_msg(client,nick,message)
	var/obj/machinery/message_server/MS = check_server(client)
	if(!MS)
		return "BAD_TCOM"

	if(!get_auth(client,nick) || !nick || length(nick) > 8)
		return "BAD_NICK"

	MS.send_chat_message(nick,name,message)

	if(findtext(message,"/",1,2))
		var/list/cmd=text2list(message, " ")
		switch(cmd[1])
			if("/register")
				if(cmd.len == 1)
					return register_auth(client,nick)
				else
					return "BAD_COMMAND_ARGUMENTS"
			if("/join")
				if(cmd.len == 2 && cmd[2])
					if((findtext(cmd[2],"#",1,2) && length(cmd[2]) > 1) || (!findtext(cmd[2],"#",1,2) && length(cmd[2]) > 0))
						return channel_join(client,nick,copytext(cmd[2], 1, 15))
					else // Happens only if "" or "#" is entered as a channel name.
						return "BAD_CHANNEL"
				else
					return "BAD_COMMAND_ARGUMENTS"
			if("/log")
				if(cmd.len == 2 && cmd[2])
					return get_log(client,nick,cmd[2])
				else
					return "BAD_COMMAND_ARGUMENTS"
			else
				return "BAD_COMMAND"

	return send_message(client,nick,message)

//the following are helper procs, FOR INTERNAL USE ONLY

/datum/chatroom/proc/check_server(client)
	var/atom/C = client
	var/turf/CT = get_turf(C)
	if(message_servers)
		for (var/obj/machinery/message_server/MS in message_servers)
			var/turf/T = get_turf(MS)
			if(MS.active && (T.z == CT.z))
				return MS
	return null

/datum/chatroom/proc/send_message(client,nick,message) //standard message
	if(!message)
		return 0
	logs.Insert(1,"[strip_html_properly(nick)]> [strip_html_properly(message)]")
	log_chat("[usr]/([usr.ckey]) as [nick] sent to [name]: [message]")
	events.fireEvent("msg_chat",name,nick,message)
	return 1

/datum/chatroom/proc/get_auth(client,nick) //check auth
	if((!(nick in authed)) || (auth[client] == nick))
		return 1
	return 0

//chat commands go here, FOR INTERNAL USE ONLY
/datum/chatroom/proc/register_auth(client,nick) //register
	if(!get_auth(client,nick))
		return "BAD_REGS"
	auth[client] = nick
	authed += nick
	return 1

/datum/chatroom/proc/channel_join(client,nick,channel) //join
	if(!findtext(channel,"#",1,2))
		channel = "#" + channel
	if(channel == name) //join this channel
		if(!(client in evlist))
			evlist[client] = events.addEvent("msg_chat",client,"msg_chat")
		return name

	else //leave this channel, join another one
		if(client in evlist)
			events.clearEvent("msg_chat",evlist[client])
			evlist -= client
		if(!(channel in chatchannels))
			var/datum/chatroom/NC = new /datum/chatroom()
			NC.name = channel
			chatchannels[channel] = NC
		var/datum/chatroom/C = chatchannels[channel]
		return C.parse_msg(client,nick,"/join [channel]")

/datum/chatroom/proc/get_log(client,nick,lines) //log
	lines = text2num(lines)
	lines = min(lines, logs.len)
	for(var/i=0;i<lines;i++)
		call(client,"msg_chat")(name,"NTbot","LOG[i]: [logs[(logs.len)-i]]")
	return 1


code:
/obj/item/radio/integrated
	name = "\improper PDA radio module"
	desc = "An electronic radio system of nanotrasen origin."
	icon = 'icons/obj/module.dmi'
	icon_state = "power_mod"
	var/obj/item/device/pda/hostpda = null
	var/list/botlist = null		// list of bots
	var/obj/machinery/bot/active 	// the active bot; if null, show bot list
	var/list/botstatus			// the status signal sent by the bot
	var/bot_type				//The type of bot it is.
	var/bot_filter				//Determines which radio filter to use.

	var/control_freq = 1447

	var/on = 0 //Are we currently active??
	var/menu_message = ""

/obj/item/radio/integrated/New()
	..()
	if (istype(loc.loc, /obj/item/device/pda))
		hostpda = loc.loc
	if (bot_filter)
		spawn(5)
			add_to_radio(bot_filter)

/obj/item/radio/integrated/Destroy()
	if(radio_controller)
		radio_controller.remove_object(src, control_freq)
	..()

/obj/item/radio/integrated/proc/post_signal(var/freq, var/key, var/value, var/key2, var/value2, var/key3, var/value3,var/key4, var/value4, s_filter)

	//world << "Post: [freq]: [key]=[value], [key2]=[value2]"
	var/datum/radio_frequency/frequency = radio_controller.return_frequency(freq)

	if(!frequency)
		return

	var/datum/signal/signal = new()
	signal.source = src
	signal.transmission_method = 1
	signal.data[key] = value
	if(key2)
		signal.data[key2] = value2
	if(key3)
		signal.data[key3] = value3
	if(key4)
		signal.data[key4] = value4

	frequency.post_signal(src, signal, filter = s_filter)

/obj/item/radio/integrated/proc/print_to_host(var/text)
	if (isnull(src.hostpda))
		return
	src.hostpda.cart = text

	for (var/mob/M in viewers(1, src.hostpda.loc))
		if (M.client && M.machine == src.hostpda)
			src.hostpda.cartridge.unlock()

	return

/obj/item/radio/integrated/receive_signal(datum/signal/signal)
	/*var/obj/item/device/pda/P = src.loc

	world << "recvd:[P] : [signal.source]"
	for(var/d in signal.data)
		world << "- [d] = [signal.data[d]]"
		*/

	if (signal.data["type"] == bot_type)
		if(!botlist)
			botlist = new()

		if(!(signal.source in botlist))
			botlist += signal.source

		if(active == signal.source)
			var/list/b = signal.data
			botstatus = b.Copy()

/obj/item/radio/integrated/Topic(href, href_list)
	..()
	var/obj/item/device/pda/PDA = src.hostpda

	switch(href_list["op"])

		if("control")
			active = locate(href_list["bot"])
			post_signal(control_freq, "command", "bot_status", "active", active, s_filter = bot_filter)

		if("scanbots")		// find all bots
			botlist = null
			post_signal(control_freq, "command", "bot_status", s_filter = bot_filter)

		if("botlist")
			active = null

		if("stop", "go")
			post_signal(control_freq, "command", href_list["op"], "active", active, s_filter = bot_filter)
			post_signal(control_freq, "command", "bot_status", "active", active, s_filter = bot_filter)

		if("summon")
			post_signal(control_freq, "command", "summon", "active", active, "target", get_turf(PDA) , "useraccess", PDA.GetAccess(), s_filter = bot_filter)
			post_signal(control_freq, "command", "bot_status", "active", active, s_filter = bot_filter)
	PDA.cartridge.unlock()

/obj/item/radio/integrated/proc/add_to_radio(bot_filter) //Master filter control for bots. Must be placed in the bot's local New() to support map spawned bots.
	if(radio_controller)
		radio_controller.add_object(src, control_freq, filter = bot_filter)

/obj/item/radio/integrated/beepsky
	bot_filter = RADIO_SECBOT
	bot_type = SEC_BOT

/obj/item/radio/integrated/medbot
	bot_filter = RADIO_MEDBOT
	bot_type = MED_BOT

/obj/item/radio/integrated/floorbot
	bot_filter = RADIO_FLOORBOT
	bot_type = FLOOR_BOT

/obj/item/radio/integrated/cleanbot
	bot_filter = RADIO_CLEANBOT
	bot_type = CLEAN_BOT

/obj/item/radio/integrated/mule
	//var/list/botlist = null		// list of bots
	//var/obj/machinery/bot/mulebot/active 	// the active bot; if null, show bot list
	//var/list/botstatus			// the status signal sent by the bot
	var/list/beacons

	var/beacon_freq = 1400
	control_freq = 1447

// create a new QM cartridge, and register to receive bot control & beacon message
/obj/item/radio/integrated/mule/New()
	..()
	spawn(5)
		if(radio_controller)
			radio_controller.add_object(src, control_freq, filter = RADIO_MULEBOT)
			radio_controller.add_object(src, beacon_freq, filter = RADIO_NAVBEACONS)
			spawn(10)
				post_signal(beacon_freq, "findbeacon", "delivery", s_filter = RADIO_NAVBEACONS)

/obj/item/radio/integrated/mule/Destroy()
	if(radio_controller)
		radio_controller.remove_object(src,beacon_freq)
	..()

// receive radio signals
// can detect bot status signals
// and beacon locations
// create/populate lists as they are recvd

/obj/item/radio/integrated/mule/receive_signal(datum/signal/signal)
//	var/obj/item/device/pda/P = src.loc

	/*
	world << "recvd:[P] : [signal.source]"
	for(var/d in signal.data)
		world << "- [d] = [signal.data[d]]"
	*/
	if(signal.data["type"] == MULE_BOT)
		if(!botlist)
			botlist = new()

		if(!(signal.source in botlist))
			botlist += signal.source

		if(active == signal.source)
			var/list/b = signal.data
			botstatus = b.Copy()

	else if(signal.data["beacon"])
		if(!beacons)
			beacons = new()

		beacons[signal.data["beacon"] ] = signal.source

//		if(istype(P)) P.updateSelfDialog()

/obj/item/radio/integrated/mule/Topic(href, href_list)
	//..()
	var/obj/item/device/pda/PDA = src.hostpda
	var/cmd = "command"
	if(active)
		cmd = "command [active.suffix]"

	switch(href_list["op"])

		if("control")
			active = locate(href_list["bot"])

		if("scanbots")		// find all bots
			botlist = null

		if("botlist")
			active = null

		if("unload")
			post_signal(control_freq, cmd, "unload", s_filter = RADIO_MULEBOT)
		if("setdest")
			if(beacons)
				var/dest = input("Select Bot Destination", "Mulebot [active.suffix] Interlink", active.destination) as null|anything in beacons
				if(dest)
					post_signal(control_freq, cmd, "target", "destination", dest, s_filter = RADIO_MULEBOT)

		if("retoff")
			post_signal(control_freq, cmd, "autoret", "value", 0, s_filter = RADIO_MULEBOT)
		if("reton")
			post_signal(control_freq, cmd, "autoret", "value", 1, s_filter = RADIO_MULEBOT)

		if("pickoff")
			post_signal(control_freq, cmd, "autopick", "value", 0, s_filter = RADIO_MULEBOT)

		if("pickon")
			post_signal(control_freq, cmd, "autopick", "value", 1, s_filter = RADIO_MULEBOT)

		if("stop", "go", "home")
			post_signal(control_freq, cmd, href_list["op"], s_filter = RADIO_MULEBOT)

	post_signal(control_freq, cmd, "bot_status", s_filter = RADIO_MULEBOT)
	PDA.cartridge.unlock()

/*
 *	Radio Cartridge, essentially a signaler.
 */

/obj/item/radio/integrated/signal
	var/frequency = 1457
	var/code = 30.0
	var/last_transmission
	var/datum/radio_frequency/radio_connection

/obj/item/radio/integrated/signal/New()
	..()
	if(radio_controller)
		initialize()

/obj/item/radio/integrated/signal/Destroy()
	if(radio_controller)
		radio_controller.remove_object(src, frequency)
	..()

/obj/item/radio/integrated/signal/initialize()
	if (src.frequency < 1441 || src.frequency > 1489)
		src.frequency = sanitize_frequency(src.frequency)

	set_frequency(frequency)

/obj/item/radio/integrated/signal/proc/set_frequency(new_frequency)
	radio_controller.remove_object(src, frequency)
	frequency = new_frequency
	radio_connection = radio_controller.add_object(src, frequency)

/obj/item/radio/integrated/signal/proc/send_signal(message="ACTIVATE")

	if(last_transmission && world.time < (last_transmission + 5))
		return
	last_transmission = world.time

	var/time = time2text(world.realtime,"hh:mm:ss")
	var/turf/T = get_turf(src)
	lastsignalers.Add("[time] : [usr.key] used [src] @ location ([T.x],[T.y],[T.z]) : [format_frequency(frequency)]/[code]")

	var/datum/signal/signal = new
	signal.source = src
	signal.encryption = code
	signal.data["message"] = message

	radio_connection.post_signal(src, signal)

	return


code:
/obj/item/device/aicard
	name = "intelliCard"
	desc = "A storage device for AIs. Patent pending."
	icon = 'icons/obj/aicards.dmi'
	icon_state = "aicard" // aicard-full
	item_state = "electronic"
	w_class = 2.0
	slot_flags = SLOT_BELT
	var/flush = null
	origin_tech = "programming=4;materials=4"

/obj/item/device/aicard/attack(mob/living/silicon/ai/M as mob, mob/user as mob)
	if(!istype(M, /mob/living/silicon/ai))//If target is not an AI.
		return ..()

	add_logs(user, M, "carded", object="[src.name]")

	transfer_ai("AICORE", "AICARD", M, user)
	return

/obj/item/device/aicard/attack_self(mob/user)
	if (!in_range(src, user))
		return
	user.set_machine(src)
	var/dat = "<TT>Intellicard
"
	var/laws
	for(var/mob/living/silicon/ai/A in src)
		dat += "Stored AI: [A.name]
System integrity: [(A.health+100)/2]%
"

		if (A.laws.zeroth)
			laws += "0: [A.laws.zeroth]
"

		for (var/index = 1, index <= A.laws.ion.len, index++)
			var/law = A.laws.ion[index]
			if (length(law) > 0)
				var/num = ionnum()
				laws += "[num]. [law]
"

		var/number = 1
		for (var/index = 1, index <= A.laws.inherent.len, index++)
			var/law = A.laws.inherent[index]
			if (length(law) > 0)
				laws += "[number]: [law]
"
				number++

		for (var/index = 1, index <= A.laws.supplied.len, index++)
			var/law = A.laws.supplied[index]
			if (length(law) > 0)
				laws += "[number]: [law]
"
				number++

		dat += "Laws:
[laws]
"

		if (A.stat == 2)
			dat += "AI nonfunctional"
		else
			if (!src.flush)
				dat += {"Wipe AI"}
			else
				dat += "Wipe in progress"
			dat += "
"
			dat += {"[A.control_disabled ? "Enable" : "Disable"] Wireless Activity"}
			dat += "
"
			dat += {"[A.radio_enabled ? "Disable" : "Enable"] Subspace Radio"}
			dat += "
"
			dat += {" Close"}
	user << browse(dat, "window=aicard")
	onclose(user, "aicard")
	return

/obj/item/device/aicard/Topic(href, href_list)
	var/mob/U = usr
	if (!in_range(src, U)||U.machine!=src)//If they are not in range of 1 or less or their machine is not the card (ie, clicked on something else).
		U << browse(null, "window=aicard")
		U.unset_machine()
		return

	add_fingerprint(U)
	U.set_machine(src)

	switch(href_list["choice"])//Now we switch based on choice.
		if ("Close")
			U << browse(null, "window=aicard")
			U.unset_machine()
			return

		if ("Wipe")
			var/confirm = alert("Are you sure you want to wipe this card's memory? This cannot be undone once started.", "Confirm Wipe", "Yes", "No")
			if(confirm == "Yes")
				if(isnull(src)||!in_range(src, U)||U.machine!=src)
					U << browse(null, "window=aicard")
					U.unset_machine()
					return
				else
					flush = 1
					for(var/mob/living/silicon/ai/A in src)
						A.suiciding = 1
						A << "Your core files are being wiped!"
						while (A.stat != 2)
							A.adjustOxyLoss(2)
							A.updatehealth()
							sleep(10)
						flush = 0

		if ("Wireless")
			for(var/mob/living/silicon/ai/A in src)
				A.control_disabled = !A.control_disabled
				A << "The intellicard's wireless port has been [A.control_disabled ? "disabled" : "enabled"]!"
				if (A.control_disabled)
					overlays -= image('icons/obj/aicards.dmi', "aicard-on")
				else
					overlays += image('icons/obj/aicards.dmi', "aicard-on")

		if ("Radio")
			for(var/mob/living/silicon/ai/A in src)
				A.radio_enabled = !A.radio_enabled
				A << "Your Subspace Transceiver has been [A.radio_enabled ? "enabled" : "disabled"]!"
	attack_self(U)


code:
#define VANILLA_BUG		0
#define UNIVERSAL_BUG	1
#define NETWORK_BUG		2
#define SABOTAGE_BUG	3
#define ADVANCED_BUG	4
#define ADMIN_BUG		5

#define BUGMODE_LIST	0
#define BUGMODE_MONITOR	1
#define BUGMODE_TRACK	2

/obj/item/device/camera_bug
	name = "camera bug"
	desc = "For illicit snooping through the camera network."
	icon = 'icons/obj/device.dmi'
	icon_state	= "camera_bug"
	w_class		= 1.0
	item_state	= "camera_bug"
	throw_speed	= 4
	throw_range	= 20

	var/obj/machinery/camera/current = null
	var/obj/item/expansion = null
	var/bugtype = VANILLA_BUG

	var/last_net_update = 0
	var/last_bugtype = VANILLA_BUG
	var/list/bugged_cameras = list()
	var/skip_bugcheck = 0

	var/track_mode = BUGMODE_LIST
	var/last_tracked = 0
	var/refresh_interval = 50

	var/tracked_name = null
	var/atom/tracking = null

	var/last_found = null
	var/last_seen = null

/obj/item/device/camera_bug/New()
	..()
	SSobj.processing += src

/obj/item/device/camera_bug/Destroy()
	if(expansion)
		qdel(expansion)
		expansion = null
	del(src)
// Easier to just call del() than this nonsense
// ya no, del() takes 0.8ds to run on avg. this takes less than 0.01
	get_cameras()
	for(var/cam_tag in bugged_cameras)
		var/obj/machinery/camera/camera = bugged_cameras[cam_tag]
		if(camera.bug == src)
			camera.bug = null
	bugged_cameras = list()
	if(tracking)
		tracking = null
	..()

/obj/item/device/camera_bug/interact(var/mob/user = usr)
	var/datum/browser/popup = new(user, "camerabug","Camera Bug",nref=src)
	popup.set_content(menu(get_cameras()))
	popup.open()

/obj/item/device/camera_bug/attack_self(mob/user as mob)
	user.set_machine(src)
	interact(user)

/obj/item/device/camera_bug/check_eye(var/mob/user as mob)
	if (user.stat || loc != user || !user.canmove || user.eye_blind || !current)
		user.reset_view(null)
		user.unset_machine()
		return null

	var/turf/T = get_turf(user.loc)
	if(T.z != current.z || (!skip_bugcheck && current.bug != src) || !current.can_use())
		user << "[src] has lost the signal."
		current = null
		user.reset_view(null)
		user.unset_machine()
		return null

	return 1

/obj/item/device/camera_bug/proc/get_cameras()
	if(bugtype != last_bugtype || ( (bugtype in list(UNIVERSAL_BUG,NETWORK_BUG,ADMIN_BUG)) && world.time > (last_net_update + 100)))
		bugged_cameras = list()
		last_bugtype = bugtype
		for(var/obj/machinery/camera/camera in cameranet.cameras)
			if(camera.stat || !camera.can_use())
				continue
			switch(bugtype)
				if(VANILLA_BUG,SABOTAGE_BUG,ADVANCED_BUG)
					if(camera.bug == src)
						bugged_cameras[camera.c_tag] = camera
				if(UNIVERSAL_BUG)
					if(camera.bug)
						bugged_cameras[camera.c_tag] = camera
				if(NETWORK_BUG,ADMIN_BUG)
					if(length(list("SS13","MINE")&camera.network))
						bugged_cameras[camera.c_tag] = camera
	sortList(bugged_cameras)
	return bugged_cameras

/obj/item/device/camera_bug/proc/menu(var/list/cameras)
	if(!cameras || !cameras.len)
		return "No bugged cameras found."

	var/html
	switch(track_mode)
		if(BUGMODE_LIST)
			html = "
Select a camera:
 \[Cancel camera view\]

"
			for(var/entry in cameras)
				var/obj/machinery/camera/C = cameras[entry]
				var/functions = ""
				switch(bugtype)
					if(SABOTAGE_BUG)
						functions = " - \[Disable\]"
					if(ADVANCED_BUG)
						functions = " - \[Monitor\]"
					if(ADMIN_BUG)
						if(C.bug == src)
							functions = " - \[Monitor\] \[Disable\]"
						else
							functions = " - \[Monitor\]"
				html += "
[entry]	[functions]
"

		if(BUGMODE_MONITOR)
			if(current)
				html = "Analyzing Camera '[current.c_tag]' \[Select Camera\]
"
				html += camera_report()
			else
				track_mode = BUGMODE_LIST
				return .(cameras)
		if(BUGMODE_TRACK)
			if(tracking)
				html = "Tracking '[tracked_name]'  \[Cancel Tracking\]  \[Cancel camera view\]
"
				if(last_found)
					var/time_diff = round((world.time - last_seen) / 150)
					var/obj/machinery/camera/C = bugged_cameras[last_found]
					var/outstring
					if(C)
						outstring = "[last_found]"
					else
						outstring = last_found
					if(!time_diff)
						html += "Last seen near [outstring] (now)
"
					else
						// 15 second intervals ~ 1/4 minute
						var/m = round(time_diff/4)
						var/s = (time_diff - 4*m) * 15
						if(!s) s = "00"
						html += "Last seen near [outstring] ([m]:[s] minute\s ago)
"
				else
					html += "Not yet seen."
			else
				track_mode = BUGMODE_LIST
				return .(cameras)
	return html

/obj/item/device/camera_bug/proc/camera_report()
	// this should only be called if current exists
	var/dat = ""
	if(current && current.can_use())
		var/list/seen = current.can_see()
		var/list/names = list()
		for(var/obj/singularity/S in seen) // god help you if you see more than one
			if(S.name in names)
				names[S.name]++
				dat += "[S.name] ([names[S.name]])"
			else
				names[S.name] = 1
				dat += "[S.name]"
			var/stage = round(S.current_size / 2)+1
			dat += " (Stage [stage])"
			dat += " \[Track\]
"

		for(var/obj/mecha/M in seen)
			if(M.name in names)
				names[M.name]++
				dat += "[M.name] ([names[M.name]])"
			else
				names[M.name] = 1
				dat += "[M.name]"
			dat += " \[Track\]
"

		for(var/mob/living/M in seen)
			if(M.name in names)
				names[M.name]++
				dat += "[M.name] ([names[M.name]])"
			else
				names[M.name] = 1
				dat += "[M.name]"
			if(M.buckled && !M.lying)
				dat += " (Sitting)"
			if(M.lying)
				dat += " (Laying down)"
			dat += " \[Track\]
"
		if(length(dat) == 0)
			dat += "No motion detected."
		return dat
	else
		return "Camera Offline
"

/obj/item/device/camera_bug/Topic(var/href,var/list/href_list)
	if(usr != loc)
		usr.unset_machine()
		usr.reset_view(null)
		usr << browse(null, "window=camerabug")
		return
	usr.set_machine(src)
	if("mode" in href_list)
		track_mode = text2num(href_list["mode"])
	if("monitor" in href_list)
		var/obj/machinery/camera/C = locate(href_list["monitor"])
		if(C)
			track_mode = BUGMODE_MONITOR
			current = C
			usr.reset_view(null)
			interact()
	if("track" in href_list)
		var/atom/A = locate(href_list["track"])
		if(A)
			tracking = A
			tracked_name = A.name
			last_found = current.c_tag
			last_seen = world.time
			track_mode = BUGMODE_TRACK
	if("emp" in href_list)
		var/obj/machinery/camera/C = locate(href_list["emp"])
		if(istype(C) && C.bug == src)
			C.emp_act(1)
			C.bug = null
			bugged_cameras -= C.c_tag
		interact()
		return
	if("close" in href_list)
		usr.reset_view(null)
		usr.unset_machine()
		current = null
		return // I do not <- I do not remember what I was going to write in this comment -Sayu, sometime later
	if("view" in href_list)
		var/obj/machinery/camera/C = locate(href_list["view"])
		if(istype(C))
			if(!C.can_use())
				usr << "Something's wrong with that camera.  You can't get a feed."
				return
			var/turf/T = get_turf(loc)
			if(!T || C.z != T.z)
				usr << "You can't get a signal."
				return
			current = C
			spawn(6)
				if(src.check_eye(usr))
					usr.reset_view(C)
					interact()
				else
					usr.unset_machine()
					usr.reset_view(null)
					usr << browse(null, "window=camerabug")
			return
		else
			usr.unset_machine()
			usr.reset_view(null)

	interact()

/obj/item/device/camera_bug/process()
	if(track_mode == BUGMODE_LIST || (world.time < (last_tracked + refresh_interval)))
		return
	last_tracked = world.time
	if(track_mode == BUGMODE_TRACK ) // search for user
		// Note that it will be tricked if your name appears to change.
		// This is not optimal but it is better than tracking you relentlessly despite everything.
		if(!tracking)
			src.updateSelfDialog()
			return

		if(tracking.name != tracked_name) // Hiding their identity, tricksy
			var/mob/M = tracking
			if(istype(M))
				if(!(tracked_name == "Unknown" && findtext(tracking.name,"Unknown"))) // we saw then disguised before
					if(!(tracked_name == M.real_name && findtext(tracking.name,M.real_name))) // or they're still ID'd
						src.updateSelfDialog()//But if it's neither of those cases
						return // you won't find em on the cameras
			else
				src.updateSelfDialog()
				return

		var/list/tracking_cams = list()
		var/list/b_cams = get_cameras()
		for(var/entry in b_cams)
			tracking_cams += b_cams[entry]
		var/list/target_region = view(tracking)

		for(var/obj/machinery/camera/C in (target_region & tracking_cams))
			if(!can_see(C,tracking)) // target may have xray, that doesn't make them visible to cameras
				continue
			if(C.can_use())
				last_found = C.c_tag
				last_seen = world.time
				break
	src.updateSelfDialog()

/obj/item/device/camera_bug/attackby(var/obj/item/W as obj,var/mob/living/user as mob, params)
	if(istype(W,/obj/item/weapon/screwdriver) && expansion)
		expansion.loc = get_turf(loc)
		user << "You unscrew [expansion]."
		user.put_in_inactive_hand(expansion)
		expansion = null
		bugtype = VANILLA_BUG
		skip_bugcheck = 0
		track_mode = BUGMODE_LIST
		tracking = null
		return

	if(expansion || !W)
		return ..(W,user)

	// I am not sure that this list is or should be final
	// really I do not know what to do here.
	var/static/list/expandables = list(
		/obj/item/weapon/research = ADMIN_BUG, // could have been anything spawn-only

		// these are all so hackish I am sorry

		/obj/item/device/analyzer = UNIVERSAL_BUG,
		/obj/item/weapon/stock_parts/subspace/analyzer = UNIVERSAL_BUG,

		/obj/item/device/assembly/igniter = SABOTAGE_BUG,
		/obj/item/device/assembly/infra = SABOTAGE_BUG, // ir blaster to disable camera
		/obj/item/weapon/stock_parts/subspace/amplifier = SABOTAGE_BUG,

		/obj/item/device/radio = NETWORK_BUG,
		/obj/item/device/assembly/signaler = NETWORK_BUG,
		/obj/item/weapon/stock_parts/subspace/transmitter = NETWORK_BUG,

		/obj/item/device/detective_scanner = ADVANCED_BUG,
		/obj/item/device/paicard = ADVANCED_BUG,
		/obj/item/weapon/stock_parts/scanning_module = ADVANCED_BUG
		)

	for(var/entry in expandables)
		if(istype(W,entry))
			bugtype = expandables[entry]
			user.drop_item()
			W.loc = src
			expansion = W
			user << "You add [W] to [src]."
			get_cameras() // the tracking code will want to know the new camera list
			if(bugtype in list(UNIVERSAL_BUG,NETWORK_BUG,ADMIN_BUG))
				skip_bugcheck = 1
			return

#undef VANILLA_BUG
#undef UNIVERSAL_BUG
#undef NETWORK_BUG
#undef SABOTAGE_BUG
#undef ADVANCED_BUG
#undef ADMIN_BUG

#undef BUGMODE_LIST
#undef BUGMODE_MONITOR
#undef BUGMODE_TRACK


code:
/obj/item/device/chameleon
	name = "chameleon-projector"
	icon_state = "shield0"
	flags = CONDUCT
	slot_flags = SLOT_BELT
	item_state = "electronic"
	throwforce = 5.0
	throw_speed = 3
	throw_range = 5
	w_class = 2.0
	origin_tech = "syndicate=4;magnets=4"
	var/can_use = 1
	var/obj/effect/dummy/chameleon/active_dummy = null
	var/saved_item = /obj/item/weapon/cigbutt
	var/saved_icon = 'icons/obj/clothing/masks.dmi'
	var/saved_icon_state = "cigbutt"
	var/saved_overlays = null
	var/saved_underlays = null

/obj/item/device/chameleon/dropped()
	disrupt()

/obj/item/device/chameleon/equipped()
	disrupt()

/obj/item/device/chameleon/attack_self()
	toggle()

/obj/item/device/chameleon/afterattack(atom/target, mob/user , proximity)
	if(!proximity) return
	if(!active_dummy)
		if(istype(target,/obj/item) && !istype(target, /obj/item/weapon/disk/nuclear))
			playsound(get_turf(src), 'sound/weapons/flash.ogg', 100, 1, -6)
			user << "Scanned [target]."
			saved_item = target.type
			saved_icon = target.icon
			saved_icon_state = target.icon_state
			saved_overlays = target.overlays
			saved_underlays = target.underlays

/obj/item/device/chameleon/proc/toggle()
	if(!can_use || !saved_item) return
	if(active_dummy)
		eject_all()
		playsound(get_turf(src), 'sound/effects/pop.ogg', 100, 1, -6)
		qdel(active_dummy)
		active_dummy = null
		usr << "You deactivate \the [src]."
		var/obj/effect/overlay/T = new/obj/effect/overlay(get_turf(src))
		T.icon = 'icons/effects/effects.dmi'
		flick("emppulse",T)
		spawn(8)
			qdel(T)
	else
		playsound(get_turf(src), 'sound/effects/pop.ogg', 100, 1, -6)
		var/obj/O = new saved_item(src)
		if(!O) return
		var/obj/effect/dummy/chameleon/C = new/obj/effect/dummy/chameleon(usr.loc)
		C.activate(O, usr, saved_icon, saved_icon_state, saved_overlays, saved_underlays, src)
		qdel(O)
		usr << "You activate \the [src]."
		var/obj/effect/overlay/T = new/obj/effect/overlay(get_turf(src))
		T.icon = 'icons/effects/effects.dmi'
		flick("emppulse",T)
		spawn(8)
			qdel(T)

/obj/item/device/chameleon/proc/disrupt(var/delete_dummy = 1)
	if(active_dummy)
		var/datum/effect/effect/system/spark_spread/spark_system = new /datum/effect/effect/system/spark_spread
		spark_system.set_up(5, 0, src)
		spark_system.attach(src)
		spark_system.start()
		eject_all()
		if(delete_dummy)
			qdel(active_dummy)
		active_dummy = null
		can_use = 0
		spawn(50) can_use = 1

/obj/item/device/chameleon/proc/eject_all()
	for(var/atom/movable/A in active_dummy)
		A.loc = active_dummy.loc
		if(ismob(A))
			var/mob/M = A
			M.reset_view(null)

/obj/effect/dummy/chameleon
	name = ""
	desc = ""
	density = 0
	anchored = 1
	var/can_move = 1
	var/obj/item/device/chameleon/master = null

/obj/effect/dummy/chameleon/proc/activate(var/obj/O, var/mob/M, new_icon, new_iconstate, new_overlays, new_underlays, var/obj/item/device/chameleon/C)
	name = O.name
	desc = O.desc
	icon = new_icon
	icon_state = new_iconstate
	overlays = new_overlays
	underlays = new_underlays
	dir = O.dir
	M.loc = src
	master = C
	master.active_dummy = src

/obj/effect/dummy/chameleon/attackby()
	for(var/mob/M in src)
		M << "Your chameleon-projector deactivates."
	master.disrupt()

/obj/effect/dummy/chameleon/attack_hand()
	for(var/mob/M in src)
		M << "Your chameleon-projector deactivates."
	master.disrupt()

/obj/effect/dummy/chameleon/ex_act() //ok now THATS some serious protection against explosions right here
	for(var/mob/M in src)
		M << "Your chameleon-projector deactivates."
	master.disrupt()

/obj/effect/dummy/chameleon/bullet_act()
	for(var/mob/M in src)
		M << "Your chameleon-projector deactivates."
	..()
	master.disrupt()

/obj/effect/dummy/chameleon/relaymove(var/mob/user, direction)
	if(istype(loc, /turf/space) || !direction)
		return //No magical space movement!

	if(can_move)
		can_move = 0
		switch(user.bodytemperature)
			if(300 to INFINITY)
				spawn(10) can_move = 1
			if(295 to 300)
				spawn(13) can_move = 1
			if(280 to 295)
				spawn(16) can_move = 1
			if(260 to 280)
				spawn(20) can_move = 1
			else
				spawn(25) can_move = 1
		step(src, direction)
	return

/obj/effect/dummy/chameleon/Destroy()
	master.disrupt(0)
	..()


code:
/obj/item/device/flash
	name = "flash"
	desc = "A powerful and versatile flashbulb device, with applications ranging from disorienting attackers to acting as visual receptors in robot production."
	icon_state = "flash"
	item_state = "flashtool"
	throwforce = 0
	w_class = 1
	throw_speed = 3
	throw_range = 7
	flags = CONDUCT
	origin_tech = "magnets=2;combat=1"

	var/times_used = 0 //Number of times it's been used.
	var/broken = 0     //Is the flash burnt out?
	var/last_used = 0 //last world.time it was used.

/obj/item/device/flash/proc/clown_check(mob/living/carbon/human/user)
	if(user.disabilities & CLUMSY && prob(50))
		flash_carbon(user, user, 15, 0)
		return 0
	return 1

/obj/item/device/flash/proc/burn_out() //Made so you can override it if you want to have an invincible flash from R&D or something.
	broken = 1
	icon_state = "[initial(icon_state)]burnt"
	visible_message("The [src.name] burns out!")

/obj/item/device/flash/proc/flash_recharge(var/mob/user)
	if(prob(times_used * 2))	//if you use it 5 times in a minute it has a 10% chance to break!
		burn_out()
		return 0

	var/deciseconds_passed = world.time - last_used
	for(var/seconds = deciseconds_passed/10, seconds>=10, seconds-=10) //get 1 charge every 10 seconds
		times_used--

	last_used = world.time
	times_used = max(0, times_used) //sanity

/obj/item/device/flash/proc/try_use_flash(var/mob/user = null)
	flash_recharge(user)

	if(broken)
		return 0

	playsound(src.loc, 'sound/weapons/flash.ogg', 100, 1)
	flick("[initial(icon_state)]2", src)
	times_used++

	if(user && !clown_check(user))
		return 0

	return 1

/obj/item/device/flash/proc/flash_carbon(var/mob/living/carbon/M, var/mob/user = null, var/power = 5, targeted = 1)
	add_logs(user, M, "flashed", object="[src.name]")
	if(user && targeted)
		if(M.flash_eyes(1, 1))
			M.confused += power
			terrible_conversion_proc(M, user)
			M.Stun(1)
			user.visible_message("[user] blinds [M] with the flash!")
		else
			user.visible_message("[user] fails to blind [M] with the flash!")
	else
		if(M.flash_eyes())
			M.confused += power

/obj/item/device/flash/attack(mob/living/M, mob/user)
	if(!try_use_flash(user))
		return 0

	if(iscarbon(M))
		flash_carbon(M, user, 5, 1)
		return 1

	else if(issilicon(M))
		add_logs(user, M, "flashed", object="[src.name]")
		flick("e_flash", M.flash)
		M.Weaken(rand(5,10))
		user.visible_message("[user] overloads [M]'s sensors with the flash!")
		return 1

	user.visible_message("[user] fails to blind [M] with the flash!")

/obj/item/device/flash/attack_self(mob/living/carbon/user, flag = 0, emp = 0)
	if(!try_use_flash(user))
		return 0
	user.visible_message("[user]'s flash emits a blinding light!")
	for(var/mob/living/carbon/M in oviewers(3, null))
		flash_carbon(M, user, 3, 0)

/obj/item/device/flash/emp_act(severity)
	if(!try_use_flash())
		return 0
	for(var/mob/living/carbon/M in viewers(3, null))
		flash_carbon(M, null, 10, 0)
	burn_out()
	..()

/obj/item/device/flash/proc/terrible_conversion_proc(var/mob/M, var/mob/user)
	if(ishuman(M) && ishuman(user) && M.stat != DEAD)
		if(user.mind && ((user.mind in ticker.mode.head_revolutionaries) || (user.mind in ticker.mode.A_bosses) || (user.mind in ticker.mode.B_bosses)))
			if(M.client)
				if(M.stat == CONSCIOUS)
					M.mind_initialize() //give them a mind datum if they don't have one.
					var/resisted
					if(!isloyal(M))
						if(user.mind in ticker.mode.head_revolutionaries)
							if(!ticker.mode.add_revolutionary(M.mind))
								resisted = 1
						if(user.mind in ticker.mode.A_bosses)
							if(!ticker.mode.add_gangster(M.mind,"A"))
								resisted = 1
						if(user.mind in ticker.mode.B_bosses)
							if(!ticker.mode.add_gangster(M.mind,"B"))
								resisted = 1
					else
						resisted = 1

					if(resisted)
						user << "This mind seems resistant to the flash!"
				else
					user << "They must be conscious before you can convert them!"
			else
				user << "This mind is so vacant that it is not susceptible to influence!"

/obj/item/device/flash/cyborg
	origin_tech = null

/obj/item/device/flash/cyborg/attack(mob/living/M, mob/user)
	..()
	cyborg_flash_animation(user)

/obj/item/device/flash/cyborg/attack_self(mob/user)
	..()
	cyborg_flash_animation(user)

/obj/item/device/flash/cyborg/proc/cyborg_flash_animation(var/mob/living/user)
	var/atom/movable/overlay/animation = new(user.loc)
	animation.layer = user.layer + 1
	animation.icon_state = "blank"
	animation.icon = 'icons/mob/mob.dmi'
	animation.master = user
	flick("blspell", animation)
	sleep(5)
	qdel(animation)

/obj/item/device/flash/memorizer
	name = "memorizer"
	desc = "If you see this, you're not likely to remember it any time soon."
	icon_state = "memorizer"
	item_state = "nullrod"

/obj/item/device/flash/handheld //this is now the regular pocket flashes