code:
/obj/item/wallframe
	materials = list(MAT_METAL=MINERAL_MATERIAL_AMOUNT*2)
	flags = CONDUCT
	origin_tech = "materials=1;engineering=1"
	item_state = "syringe_kit"
	w_class = 2
	var/result_path
	var/inverse = 0
	// For inverse dir frames like light fixtures.

/obj/item/wallframe/proc/try_build(turf/on_wall)
	if(get_dist(on_wall,usr)>1)
		return
	var/ndir = get_dir(on_wall, usr)
	if(!(ndir in cardinal))
		return
	var/turf/loc = get_turf(usr)
	var/area/A = loc.loc
	if(!istype(loc, /turf/open/floor))
		usr << "You cannot place [src] on this spot!"
		return
	if(A.requires_power == 0 || istype(A, /area/space))
		usr << "You cannot place [src] in this area!"
		return
	if(gotwallitem(loc, ndir, inverse*2))
		usr << "There's already an item on this wall!"
		return

	return 1

/obj/item/wallframe/proc/attach(turf/on_wall)
	if(result_path)
		playsound(src.loc, 'sound/machines/click.ogg', 75, 1)
		usr.visible_message("[usr.name] attaches [src] to the wall.",
			"You attach [src] to the wall.",
			"You hear clicking.")
		var/ndir = get_dir(on_wall,usr)
		if(inverse)
			ndir = turn(ndir, 180)

		var/obj/O = new result_path(get_turf(usr), ndir, 1)
		after_attach(O)

	qdel(src)

/obj/item/wallframe/proc/after_attach(var/obj/O)
	transfer_fingerprints_to(O)

/obj/item/wallframe/attackby(obj/item/weapon/W, mob/user, params)
	..()
	if(istype(W, /obj/item/weapon/screwdriver))
		// For camera-building borgs
		var/turf/T = get_step(get_turf(user), user.dir)
		if(istype(T, /turf/closed/wall))
			T.attackby(src, user, params)

	var/metal_amt = round(materials[MAT_METAL]/MINERAL_MATERIAL_AMOUNT)
	var/glass_amt = round(materials[MAT_GLASS]/MINERAL_MATERIAL_AMOUNT)

	if(istype(W, /obj/item/weapon/wrench) && (metal_amt || glass_amt))
		user << "You dismantle [src]."
		if(metal_amt)
			new /obj/item/stack/sheet/metal(get_turf(src), metal_amt)
		if(glass_amt)
			new /obj/item/stack/sheet/glass(get_turf(src), glass_amt)
		qdel(src)

// APC HULL
/obj/item/wallframe/apc
	name = "\improper APC frame"
	desc = "Used for repairing or building APCs"
	icon = 'icons/obj/apc_repair.dmi'
	icon_state = "apc_frame"
	result_path = /obj/machinery/power/apc
	inverse = 1

/obj/item/wallframe/apc/try_build(turf/on_wall)
	if(!..())
		return
	var/turf/loc = get_turf(usr)
	var/area/A = loc.loc
	if (A.get_apc())
		usr << "This area already has APC!"
		return //only one APC per area
	for(var/obj/machinery/power/terminal/T in loc)
		if (T.master)
			usr << "There is another network terminal here!"
			return
		else
			var/obj/item/stack/cable_coil/C = new /obj/item/stack/cable_coil(loc)
			C.amount = 10
			usr << "You cut the cables and disassemble the unused power terminal."
			qdel(T)
	return 1

/obj/item/weapon/electronics
	desc = "Looks like a circuit. Probably is."
	icon = 'icons/obj/module.dmi'
	icon_state = "door_electronics"
	item_state = "electronic"
	flags = CONDUCT
	w_class = 2
	origin_tech = "engineering=2;programming=1"
	materials = list(MAT_METAL=50, MAT_GLASS=50)


code:
/obj/item/areaeditor
	name = "area modification item"
	icon = 'icons/obj/items.dmi'
	icon_state = "blueprints"
	attack_verb = list("attacked", "bapped", "hit")
	var/fluffnotice = "Nobody's gonna read this stuff!"

	var/const/AREA_ERRNONE = 0
	var/const/AREA_STATION = 1
	var/const/AREA_SPACE =   2
	var/const/AREA_SPECIAL = 3

	var/const/BORDER_ERROR = 0
	var/const/BORDER_NONE = 1
	var/const/BORDER_BETWEEN =   2
	var/const/BORDER_2NDTILE = 3
	var/const/BORDER_SPACE = 4

	var/const/ROOM_ERR_LOLWAT = 0
	var/const/ROOM_ERR_SPACE = -1
	var/const/ROOM_ERR_TOOLARGE = -2

/obj/item/areaeditor/attack_self(mob/user)
	add_fingerprint(user)
	var/text = "<title>[src]</title> \

[station_name()] [src.name]
 \
				[fluffnotice]
"
	switch(get_area_type())
		if(AREA_SPACE)
			text += "
According to the [src.name], you are now in an unclaimed territory.
 \

Mark this place as new area.
"
		if(AREA_SPECIAL)
			text += "
This place is not noted on the [src.name].
"
	return text

/obj/item/areaeditor/Topic(href, href_list)
	if(..())
		return
	if(!usr.canUseTopic(src))
		usr << browse(null, "window=blueprints")
		return
	if(href_list["create_area"])
		if(get_area_type()==AREA_SPACE)
			create_area()
	updateUsrDialog()

//One-use area creation permits.
/obj/item/areaeditor/permit
	name = "construction permit"
	icon_state = "permit"
	desc = "This is a one-use permit that allows the user to offically declare a built room as new addition to the station."
	fluffnotice = "Nanotrasen Engineering requires all on-station construction projects to be approved by a head of staff, as detailed in Nanotrasen Company Regulation 512-C (Mid-Shift Modifications to Company Property). \
						By submitting this form, you accept any fines, fees, or personal injury/death that may occur during construction."
	w_class = 1

/obj/item/areaeditor/permit/attack_self(mob/user)
	. = ..()
	var/area/A = get_area()
	if(get_area_type() == AREA_STATION)
		. += "
According to \the [src], you are now in \"[html_encode(A.name)]\".
"
	var/datum/browser/popup = new(user, "blueprints", "[src]", 700, 500)
	popup.set_content(.)
	popup.open()
	onclose(usr, "blueprints")

/obj/item/areaeditor/permit/create_area()
	var/success = ..()
	if(success)
		qdel(src)

//Station blueprints!!!
/obj/item/areaeditor/blueprints
	name = "station blueprints"
	desc = "Blueprints of the station. There is a \"Classified\" stamp and several coffee stains on it."
	icon = 'icons/obj/items.dmi'
	icon_state = "blueprints"
	fluffnotice = "Property of Nanotrasen. For heads of staff only. Store in high-secure storage."

/obj/item/areaeditor/blueprints/attack_self(mob/user)
	. = ..()
	var/area/A = get_area()
	if(get_area_type() == AREA_STATION)
		. += "
According to \the [src], you are now in \"[html_encode(A.name)]\".
"
		. += "
You may make an amendment to the drawing.
"
	var/datum/browser/popup = new(user, "blueprints", "[src]", 700, 500)
	popup.set_content(.)
	popup.open()
	onclose(user, "blueprints")

/obj/item/areaeditor/blueprints/Topic(href, href_list)
	..()
	if(href_list["edit_area"])
		if(get_area_type()!=AREA_STATION)
			return
		edit_area()
	updateUsrDialog()

/obj/item/areaeditor/proc/get_area()
	var/turf/T = get_turf(usr)
	var/area/A = T.loc
	A = A.master
	return A

/obj/item/areaeditor/proc/get_area_type(area/A = get_area())
	if(A.outdoors)
		return AREA_SPACE
	var/list/SPECIALS = list(
		/area/shuttle,
		/area/admin,
		/area/arrival,
		/area/centcom,
		/area/asteroid,
		/area/tdome,
		/area/wizard_station,
		/area/prison
	)
	for (var/type in SPECIALS)
		if ( istype(A,type) )
			return AREA_SPECIAL
	return AREA_STATION

/obj/item/areaeditor/proc/create_area()
	var/res = detect_room(get_turf(usr))
	if(!istype(res,/list))
		switch(res)
			if(ROOM_ERR_SPACE)
				usr << "The new area must be completely airtight."
				return
			if(ROOM_ERR_TOOLARGE)
				usr << "The new area is too large."
				return
			else
				usr << "Error! Please notify administration."
				return
	var/list/turfs = res
	var/str = trim(stripped_input(usr,"New area name:", "Blueprint Editing", "", MAX_NAME_LEN))
	if(!str || !length(str)) //cancel
		return
	if(length(str) > 50)
		usr << "The given name is too long.  The area remains undefined."
		return

	var/area/A
	for(var/key in turfs)
		if(key == str)
			A = turfs[key]
		if(turfs[key])
			turfs -= turfs[key]
			turfs -= key
	if(A)
		A.contents += turfs
		A.SetDynamicLighting()
	else
		A = new
		A.setup(str)
		A.contents += turfs
		A.SetDynamicLighting()
	interact()
	return 1

/obj/item/areaeditor/proc/edit_area()
	var/area/A = get_area()
	var/prevname = "[A.name]"
	var/str = trim(stripped_input(usr,"New area name:", "Blueprint Editing", "", MAX_NAME_LEN))
	if(!str || !length(str) || str==prevname) //cancel
		return
	if(length(str) > 50)
		usr << "The given name is too long.  The area's name is unchanged."
		return
	set_area_machinery_title(A,str,prevname)
	for(var/area/RA in A.related)
		RA.name = str
	usr << "You rename the '[prevname]' to '[str]'."
	interact()
	return 1

/obj/item/areaeditor/proc/set_area_machinery_title(area/A,title,oldtitle)
	if(!oldtitle) // or replacetext goes to infinite loop
		return
	for(var/area/RA in A.related)
		for(var/obj/machinery/airalarm/M in RA)
			M.name = replacetext(M.name,oldtitle,title)
		for(var/obj/machinery/power/apc/M in RA)
			M.name = replacetext(M.name,oldtitle,title)
		for(var/obj/machinery/atmospherics/components/unary/vent_scrubber/M in RA)
			M.name = replacetext(M.name,oldtitle,title)
		for(var/obj/machinery/atmospherics/components/unary/vent_pump/M in RA)
			M.name = replacetext(M.name,oldtitle,title)
		for(var/obj/machinery/door/M in RA)
			M.name = replacetext(M.name,oldtitle,title)
	//TODO: much much more. Unnamed airlocks, cameras, etc.

/obj/item/areaeditor/proc/check_tile_is_border(turf/T2,dir)
	if (istype(T2, /turf/open/space))
		return BORDER_SPACE //omg hull breach we all going to die here
	if (get_area_type(T2.loc)!=AREA_SPACE)
		return BORDER_BETWEEN
	if (istype(T2, /turf/closed/wall))
		return BORDER_2NDTILE
	if (!istype(T2, /turf))
		return BORDER_BETWEEN

	for (var/obj/structure/window/W in T2)
		if(turn(dir,180) == W.dir)
			return BORDER_BETWEEN
		if (W.dir in list(NORTHEAST,SOUTHEAST,NORTHWEST,SOUTHWEST))
			return BORDER_2NDTILE
	for(var/obj/machinery/door/window/D in T2)
		if(turn(dir,180) == D.dir)
			return BORDER_BETWEEN
	if (locate(/obj/machinery/door) in T2)
		return BORDER_2NDTILE
	if (locate(/obj/structure/falsewall) in T2)
		return BORDER_2NDTILE

	return BORDER_NONE

/obj/item/areaeditor/proc/detect_room(turf/first)
	var/list/turf/found = new
	var/list/turf/pending = list(first)
	var/list/border = list()
	while(pending.len)
		if (found.len+pending.len > 300)
			return ROOM_ERR_TOOLARGE
		var/turf/T = pending[1] //why byond havent list::pop()?
		pending -= T
		for (var/dir in cardinal)
			var/skip = 0
			for (var/obj/structure/window/W in T)
				if(dir == W.dir || (W.dir in list(NORTHEAST,SOUTHEAST,NORTHWEST,SOUTHWEST)))
					skip = 1
					break
			if (skip)
				continue
			for(var/obj/machinery/door/window/D in T)
				if(dir == D.dir)
					skip = 1
					break
			if (skip)
				continue

			var/turf/NT = get_step(T,dir)
			if (!isturf(NT) || (NT in found) || (NT in pending))
				continue

			switch(check_tile_is_border(NT,dir))
				if(BORDER_NONE)
					pending+=NT
				if(BORDER_BETWEEN)
					var/area/A = NT.loc
					if(!found[A.name])
						found[A.name] = NT.loc
				if(BORDER_2NDTILE)
					border[NT] += dir
				if(BORDER_SPACE)
					return ROOM_ERR_SPACE
		found+=T

	for(var/V in border) //lazy but works
		var/turf/F = V
		for(var/direction in cardinal)
			if(direction == border[F])
				continue //don't want to grab turfs from outside the border
			var/turf/U = get_step(F, direction)
			if((U in border) || (U in found))
				continue
			if(check_tile_is_border(U, direction) == BORDER_2NDTILE)
				found += U
		found |= F
	return found

license: mit


code:
/obj/item/organ/internal/body_egg
	name = "body egg"
	desc = "All slimy and yuck."
	icon_state = "innards"
	origin_tech = "biotech=5"
	zone = "chest"
	slot = "parasite_egg"

/obj/item/organ/internal/body_egg/on_find(mob/living/finder)
	..()
	finder << "You found an unknown alien organism in [owner]'s [zone]!"

/obj/item/organ/internal/body_egg/New(loc)
	if(iscarbon(loc))
		src.Insert(loc)
	return ..()

/obj/item/organ/internal/body_egg/Insert(var/mob/living/carbon/M, special = 0)
	..()
	owner.status_flags |= XENO_HOST
	SSobj.processing |= src
	owner.med_hud_set_status()
	spawn(0)
		AddInfectionImages(owner)

/obj/item/organ/internal/body_egg/Remove(var/mob/living/carbon/M, special = 0)
	SSobj.processing.Remove(src)
	if(owner)
		owner.status_flags &= ~(XENO_HOST)
		owner.med_hud_set_status()
		spawn(0)
			RemoveInfectionImages(owner)
	..()

/obj/item/organ/internal/body_egg/process()
	if(!owner)
		return
	if(!(src in owner.internal_organs))
		Remove(owner)
		return
	egg_process()

/obj/item/organ/internal/body_egg/proc/egg_process()
	return

/obj/item/organ/internal/body_egg/proc/RefreshInfectionImage()
	RemoveInfectionImages()
	AddInfectionImages()

/obj/item/organ/internal/body_egg/proc/AddInfectionImages()
	return

/obj/item/organ/internal/body_egg/proc/RemoveInfectionImages()
	return


code:
//Also contains /obj/structure/closet/body_bag because I doubt anyone would think to look for bodybags in /object/structures

/obj/item/bodybag
	name = "body bag"
	desc = "A folded bag designed for the storage and transportation of cadavers."
	icon = 'icons/obj/bodybag.dmi'
	icon_state = "bodybag_folded"
	var/unfoldedbag_path = /obj/structure/closet/body_bag
	w_class = 2

/obj/item/bodybag/attack_self(mob/user)
		var/obj/structure/closet/body_bag/R = new unfoldedbag_path(user.loc)
		R.add_fingerprint(user)
		qdel(src)

/obj/item/weapon/storage/box/bodybags
	name = "body bags"
	desc = "The label indicates that it contains body bags."
	icon_state = "bodybags"

/obj/item/weapon/storage/box/bodybags/New()
	..()
	new /obj/item/bodybag(src)
	new /obj/item/bodybag(src)
	new /obj/item/bodybag(src)
	new /obj/item/bodybag(src)
	new /obj/item/bodybag(src)
	new /obj/item/bodybag(src)
	new /obj/item/bodybag(src)

/obj/structure/closet/body_bag
	name = "body bag"
	desc = "A plastic bag designed for the storage and transportation of cadavers."
	icon = 'icons/obj/bodybag.dmi'
	icon_state = "bodybag"
	var/foldedbag_path = /obj/item/bodybag
	var/tagged = 0 // so closet code knows to put the tag overlay back
	density = 0
	mob_storage_capacity = 2
	open_sound = 'sound/items/zip.ogg'
	close_sound = 'sound/items/zip.ogg'

/obj/structure/closet/body_bag/attackby(obj/item/I, mob/user, params)
	if (istype(I, /obj/item/weapon/pen) || istype(I, /obj/item/toy/crayon))
		var/t = stripped_input(user, "What would you like the label to be?", name, null, 53)
		if(user.get_active_hand() != I)
			return
		if(!in_range(src, user) && loc != user)
			return
		if(t)
			name = "body bag - [t]"
			tagged = 1
			update_icon()
		else
			name = "body bag"
		return
	else if(istype(I, /obj/item/weapon/wirecutters))
		user << "You cut the tag off [src]."
		name = "body bag"
		tagged = 0
		update_icon()

/obj/structure/closet/body_bag/update_icon()
	..()
	if (tagged)
		overlays += "bodybag_label"

/obj/structure/closet/body_bag/close()
	if(..())
		density = 0
		return 1
	return 0

/obj/structure/closet/body_bag/MouseDrop(over_object, src_location, over_location)
	..()
	if(over_object == usr && Adjacent(usr) && (in_range(src, usr) || usr.contents.Find(src)))
		if(!ishuman(usr))
			return 0
		if(opened)
			return 0
		if(contents.len)
			return 0
		visible_message("[usr] folds up [src].")
		var/obj/item/bodybag/B = new foldedbag_path(get_turf(src))
		usr.put_in_hands(B)
		qdel(src)

// Bluespace bodybag

/obj/item/bodybag/bluespace
	name = "bluespace body bag"
	desc = "A folded bluespace body bag designed for the storage and transportation of cadavers."
	icon = 'icons/obj/bodybag.dmi'
	icon_state = "bluebodybag_folded"
	unfoldedbag_path = /obj/structure/closet/body_bag/bluespace
	w_class = 2

/obj/structure/closet/body_bag/bluespace
	name = "bluespace body bag"
	desc = "A bluespace body bag designed for the storage and transportation of cadavers."
	icon = 'icons/obj/bodybag.dmi'
	icon_state = "bluebodybag"
	foldedbag_path = /obj/item/bodybag/bluespace
	density = 0
	mob_storage_capacity = 15
	max_mob_size = MOB_SIZE_LARGE


code:
#define CANDLE_LUMINOSITY	2
/obj/item/candle
	name = "red candle"
	desc = "a candle"
	icon = 'icons/obj/candle.dmi'
	icon_state = "candle1"
	item_state = "candle1"
	w_class = 1
	var/wax = 200
	var/lit = 0
	var/infinite = 0
	var/start_lit = 0
	heat = 1000

/obj/item/candle/New()
	..()
	if(start_lit)
		light()

/obj/item/candle/update_icon()
	var/i
	if(wax>150)
		i = 1
	else if(wax>80)
		i = 2
	else i = 3
	icon_state = "candle[i][lit ? "_lit" : ""]"

/obj/item/candle/attackby(obj/item/weapon/W, mob/user, params)
	..()
	if(istype(W, /obj/item/weapon/weldingtool))
		var/obj/item/weapon/weldingtool/WT = W
		if(WT.isOn()) //Badasses dont get blinded by lighting their candle with a welding tool
			light("[user] casually lights the [name] with [W], what a badass.")
	else if(istype(W, /obj/item/weapon/lighter))
		var/obj/item/weapon/lighter/L = W
		if(L.lit)
			light()
	else if(istype(W, /obj/item/weapon/match))
		var/obj/item/weapon/match/M = W
		if(M.lit)
			light()
	else if(istype(W, /obj/item/candle))
		var/obj/item/candle/C = W
		if(C.lit)
			light()
	else if(istype(W, /obj/item/clothing/mask/cigarette))
		var/obj/item/clothing/mask/cigarette/M = W
		if(M.lit)
			light()

/obj/item/candle/fire_act()
	if(!src.lit)
		light() //honk
	return

/obj/item/candle/proc/light(var/flavor_text = "[usr] lights the [name].")
	if(!src.lit)
		src.lit = 1
		//src.damtype = "fire"
		usr.visible_message(flavor_text)
		SetLuminosity(CANDLE_LUMINOSITY)
		if(!infinite)
			SSobj.processing |= src
		update_icon()

/obj/item/candle/process()
	if(!lit)
		return
	wax--
	if(!wax)
		new/obj/item/trash/candle(src.loc)
		if(istype(src.loc, /mob))
			var/mob/M = src.loc
			M.unEquip(src, 1) //src is being deleted anyway
		qdel(src)
	update_icon()
	if(istype(loc, /turf)) //start a fire if possible
		var/turf/T = loc
		T.hotspot_expose(700, 5)

/obj/item/candle/attack_self(mob/user)
	if(lit)
		lit = 0
		update_icon()
		SetLuminosity(0)
		user.AddLuminosity(-CANDLE_LUMINOSITY)

/obj/item/candle/pickup(mob/user)
	..()
	if(lit)
		SetLuminosity(0)
		user.AddLuminosity(CANDLE_LUMINOSITY)

/obj/item/candle/dropped(mob/user)
	..()
	if(lit)
		user.AddLuminosity(-CANDLE_LUMINOSITY)
		SetLuminosity(CANDLE_LUMINOSITY)

/obj/item/candle/is_hot()
	return lit * heat

/obj/item/candle/infinite
	infinite = 1
	start_lit = 1

#undef CANDLE_LUMINOSITY


code:
#define WAND_OPEN "Open Door"
#define WAND_BOLT "Toggle Bolts"
#define WAND_EMERGENCY "Toggle Emergency Access"

/obj/item/weapon/door_remote
	icon_state = "gangtool-white"
	item_state = "electronic"
	icon = 'icons/obj/device.dmi'
	name = "control wand"
	desc = "Remotely controls airlocks."
	w_class = 1
	var/mode = WAND_OPEN
	var/region_access = 1 //See access.dm
	var/obj/item/weapon/card/id/ID

/obj/item/weapon/door_remote/New()
	..()
	ID = new /obj/item/weapon/card/id
	ID.access = get_region_accesses(region_access)

/obj/item/weapon/door_remote/attack_self(mob/user)
	switch(mode)
		if(WAND_OPEN)
			mode = WAND_BOLT
		if(WAND_BOLT)
			mode = WAND_EMERGENCY
		if(WAND_EMERGENCY)
			mode = WAND_OPEN
	user << "Now in mode: [mode]."

/obj/item/weapon/door_remote/afterattack(obj/machinery/door/airlock/D, mob/user)
	if(!istype(D))
		return
	if(!(D.hasPower()))
		user << "[D] has no power!"
		return
	if(!D.requiresID())
		user << "[D]'s ID scan is disabled!"
		return
	if(D.check_access(src.ID))
		switch(mode)
			if(WAND_OPEN)
				if(D.density)
					D.open()
				else
					D.close()
			if(WAND_BOLT)
				if(D.locked)
					D.unbolt()
				else
					D.bolt()
			if(WAND_EMERGENCY)
				if(D.emergency)
					D.emergency = 0
				else
					D.emergency = 1
				D.update_icon()
	else
		user << "[src] does not have access to this door."

/obj/item/weapon/door_remote/omni
	name = "omni door remote"
	desc = "This control wand can access any door on the station."
	icon_state = "gangtool-yellow"
	region_access = 0

/obj/item/weapon/door_remote/captain
	name = "command door remote"
	icon_state = "gangtool-yellow"
	region_access = 7

/obj/item/weapon/door_remote/chief_engineer
	name = "engineering door remote"
	icon_state = "gangtool-orange"
	region_access = 5

/obj/item/weapon/door_remote/research_director
	name = "research door remote"
	icon_state = "gangtool-purple"
	region_access = 4

/obj/item/weapon/door_remote/head_of_security
	name = "security door remote"
	icon_state = "gangtool-red"
	region_access = 2

/obj/item/weapon/door_remote/quartermaster
	name = "supply door remote"
	icon_state = "gangtool-green"
	region_access = 6

/obj/item/weapon/door_remote/chief_medical_officer
	name = "medical door remote"
	icon_state = "gangtool-blue"
	region_access = 3

/obj/item/weapon/door_remote/civillian
	name = "civillian door remote"
	icon_state = "gangtool-white"
	region_access = 1

#undef WAND_OPEN
#undef WAND_BOLT
#undef WAND_EMERGENCY


code:
/obj/item/toy/crayon/red
	icon_state = "crayonred"
	paint_color = "#DA0000"
	colourName = "red"

/obj/item/toy/crayon/orange
	icon_state = "crayonorange"
	paint_color = "#FF9300"
	colourName = "orange"

/obj/item/toy/crayon/yellow
	icon_state = "crayonyellow"
	paint_color = "#FFF200"
	colourName = "yellow"

/obj/item/toy/crayon/green
	icon_state = "crayongreen"
	paint_color = "#A8E61D"
	colourName = "green"

/obj/item/toy/crayon/blue
	icon_state = "crayonblue"
	paint_color = "#00B7EF"
	colourName = "blue"

/obj/item/toy/crayon/purple
	icon_state = "crayonpurple"
	paint_color = "#DA00FF"
	colourName = "purple"

/obj/item/toy/crayon/white
	icon_state = "crayonwhite"
	paint_color = "#FFFFFF"
	colourName = "white"

/obj/item/toy/crayon/mime
	icon_state = "crayonmime"
	desc = "A very sad-looking crayon."
	paint_color = "#FFFFFF"
	colourName = "mime"
	uses = -1

/obj/item/toy/crayon/mime/attack_self(mob/living/user)
	update_window(user)

/obj/item/toy/crayon/mime/update_window(mob/living/user)
	dat += "   Change color"
	..()

/obj/item/toy/crayon/mime/Topic(href,href_list)
	if ((usr.restrained() || usr.stat || usr.get_active_hand() != src))
		return
	if(href_list["color"])
		if(paint_color != "#FFFFFF")
			paint_color = "#FFFFFF"
		else
			paint_color = "#000000"
		update_window(usr)
	else
		..()

/obj/item/toy/crayon/rainbow
	icon_state = "crayonrainbow"
	paint_color = "#FFF000"
	colourName = "rainbow"
	uses = -1

/obj/item/toy/crayon/rainbow/attack_self(mob/living/user)
	update_window(user)

/obj/item/toy/crayon/rainbow/update_window(mob/living/user)
	dat += "   Change color"
	..()

/obj/item/toy/crayon/rainbow/Topic(href,href_list[])

	if(href_list["color"])
		var/temp = input(usr, "Please select colour.", "Crayon colour") as color
		if ((usr.restrained() || usr.stat || usr.get_active_hand() != src))
			return
		paint_color = temp
		update_window(usr)
	else
		..()

/*
 * Crayon Box
 */
/obj/item/weapon/storage/crayons
	name = "box of crayons"
	desc = "A box of crayons for all your rune drawing needs."
	icon = 'icons/obj/crayons.dmi'
	icon_state = "crayonbox"
	w_class = 2
	storage_slots = 6
	can_hold = list(
		/obj/item/toy/crayon
	)

/obj/item/weapon/storage/crayons/New()
	..()
	new /obj/item/toy/crayon/red(src)
	new /obj/item/toy/crayon/orange(src)
	new /obj/item/toy/crayon/yellow(src)
	new /obj/item/toy/crayon/green(src)
	new /obj/item/toy/crayon/blue(src)
	new /obj/item/toy/crayon/purple(src)
	update_icon()

/obj/item/weapon/storage/crayons/update_icon()
	overlays.Cut()
	for(var/obj/item/toy/crayon/crayon in contents)
		overlays += image('icons/obj/crayons.dmi',crayon.colourName)

/obj/item/weapon/storage/crayons/attackby(obj/item/W, mob/user, params)
	if(istype(W,/obj/item/toy/crayon))
		switch(W:colourName)
			if("mime")
				usr << "This crayon is too sad to be contained in this box."
				return
			if("rainbow")
				usr << "This crayon is too powerful to be contained in this box."
				return
	..()

//Spraycan stuff

/obj/item/toy/crayon/spraycan
	icon_state = "spraycan_cap"
	item_state = "spraycan"
	desc = "A metallic container containing tasty paint."
	var/capped = 1
	instant = 1
	edible = 0
	validSurfaces = list(/turf/open/floor,/turf/closed/wall)

/obj/item/toy/crayon/spraycan/suicide_act(mob/user)
	var/mob/living/carbon/human/H = user
	if(capped)
		user.visible_message("[user] shakes up the [src] with a rattle and lifts it to their mouth, but nothing happens! Maybe they should have uncapped it first! Nonetheless--")
		user.say("MEDIOCRE!!")
	else
		user.visible_message("[user] shakes up the [src] with a rattle and lifts it to their mouth, spraying silver paint across their teeth!")
		user.say("WITNESS ME!!")
		playsound(loc, 'sound/effects/spray.ogg', 5, 1, 5)
		paint_color = "#C0C0C0"
		update_icon()
		H.lip_style = "spray_face"
		H.lip_color = paint_color
		H.update_body()
		uses = max(0, uses - 10)
	return (OXYLOSS)

/obj/item/toy/crayon/spraycan/New()
	..()
	name = "spray can"
	paint_color = pick("#DA0000","#FF9300","#FFF200","#A8E61D","#00B7EF","#DA00FF")
	update_icon()

/obj/item/toy/crayon/spraycan/examine(mob/user)
	..()
	if(uses)
		user << "It has [uses] uses left."
	else
		user << "It is empty."

/obj/item/toy/crayon/spraycan/attack_self(mob/living/user)
	var/choice = input(user,"Spraycan options") as null|anything in list("Toggle Cap","Change Drawing","Change Color")
	switch(choice)
		if("Toggle Cap")
			user << "You [capped ? "Remove" : "Replace"] the cap of the [src]"
			capped = capped ? 0 : 1
			icon_state = "spraycan[capped ? "_cap" : ""]"
			update_icon()
		if("Change Drawing")
			..()
		if("Change Color")
			paint_color = input(user,"Choose Color") as color
			update_icon()

/obj/item/toy/crayon/spraycan/afterattack(atom/target, mob/user, proximity)
	if(!proximity)
		return
	if(capped)
		user << "Take the cap off first!"
		return
	else
		if(iscarbon(target))
			if(uses)
				playsound(user.loc, 'sound/effects/spray.ogg', 5, 1, 5)
				var/mob/living/carbon/C = target
				user.visible_message("[user] sprays [src] into the face of [target]!")
				target << "[user] sprays [src] into your face!"
				if(C.client)
					C.blur_eyes(3)
					C.blind_eyes(1)
					if(C.check_eye_prot() <= 0) // no eye protection? ARGH IT BURNS.
						C.confused = max(C.confused, 3)
						C.Weaken(3)
				if(ishuman(C))
					var/mob/living/carbon/human/H = C
					H.lip_style = "spray_face"
					H.lip_color = paint_color
					H.update_body()
				uses = max(0,uses-10)
		if(istype(target, /obj/structure/window))
			if(uses)
				target.color = paint_color
				if(color_hex2num(paint_color) < 255)
					target.SetOpacity(255)
				else
					target.SetOpacity(initial(target.opacity))
				uses = max(0, uses-2)
				playsound(user.loc, 'sound/effects/spray.ogg', 5, 1, 5)
				return
		..()

/obj/item/toy/crayon/spraycan/update_icon()
	overlays.Cut()
	var/image/I = image('icons/obj/crayons.dmi',icon_state = "[capped ? "spraycan_cap_colors" : "spraycan_colors"]")
	I.color = paint_color
	overlays += I

/obj/item/toy/crayon/spraycan/gang
	desc = "A modified container containing suspicious paint."
	gang = 1
	uses = 20
	instant = -1

/obj/item/toy/crayon/spraycan/gang/New(loc, datum/gang/G)
	..()
	if(G)
		paint_color = G.color_hex
		update_icon()


code:
/*
 *	Dehydrated Carp
 *	Instant carp, just add water
 */

//Child of carpplushie because this should do everything the toy does and more
/obj/item/toy/carpplushie/dehy_carp
	var/mob/owner = null	//Carp doesn't attack owner, set when using in hand
	var/owned = 0	//Boolean, no owner to begin with

//Attack self
/obj/item/toy/carpplushie/dehy_carp/attack_self(mob/user)
	src.add_fingerprint(user)	//Anyone can add their fingerprints to it with this
	if(!owned)
		user << "You pet [src]. You swear it looks up at you."
		owner = user
		owned = 1
	return ..()

/obj/item/toy/carpplushie/dehy_carp/afterattack(obj/O, mob/user,proximity)
	if(!proximity) return
	if(istype(O,/obj/structure/sink))
		user.drop_item()
		loc = get_turf(O)
		return Swell()
	..()

/obj/item/toy/carpplushie/dehy_carp/proc/Swell()
	desc = "It's growing!"
	visible_message("[src] swells up!")

	//Animation
	icon = 'icons/mob/animal.dmi'
	flick("carp_swell", src)
	//Wait for animation to end
	sleep(6)
	//Make space carp
	var/mob/living/simple_animal/hostile/carp/C = new /mob/living/simple_animal/hostile/carp(get_turf(src))
	//Make carp non-hostile to user, and their allies
	if(owner)
		var/list/factions = owner.faction
		for(var/F in factions)
			if(F == "neutral")
				factions -= F
		C.faction = factions
	if (!owner || owner.faction != C.faction)
		visible_message("You have a bad feeling about this.") //welcome to the hostile carp enjoy your die
	else
		visible_message("The newly grown carp looks up at you with friendly eyes.")
	qdel(src)


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
	slot_flags = SLOT_ID | SLOT_BELT
	origin_tech = "programming=2"

	//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.
	var/icon_alert = "pda-r" //Icon to be overlayed for message alerts. Taken from the pda icon file.

	//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_noise //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/image/photo = null //Scanned photo

/obj/item/device/pda/pickup(mob/user)
	..()
	if(fon)
		SetLuminosity(0)
		user.AddLuminosity(f_lum)

/obj/item/device/pda/dropped(mob/user)
	..()
	if(fon)
		user.AddLuminosity(-f_lum)
		SetLuminosity(f_lum)

/obj/item/device/pda/New()
	..()
	if(fon)
		if(!isturf(loc))
			loc.AddLuminosity(f_lum)
			SetLuminosity(0)
		else
			SetLuminosity(f_lum)
	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/GetAccess()
	if(id)
		return id.GetAccess()
	else
		return ..()

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

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

/obj/item/device/pda/attack_self(mob/user)
	var/datum/asset/assets = get_asset_datum(/datum/asset/simple/pda)
	assets.send(user)

	user.set_machine(src)

	if(hidden_uplink && hidden_uplink.active)
		hidden_uplink.interact(user)
		return

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

	dat += " Refresh"

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

	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
"

				if (cartridge)
					if (cartridge.access_clown)
						dat += "
 Honk Synthesizer
"
						dat += "
 Sad Trombone
"
					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
"
						dat += "
"
					if (cartridge.access_medical)
						dat += "
Medical Functions
"
						dat += "
"
						dat += "
 Medical Records
"
						dat += "
 [scanmode == 1 ? "Disable" : "Enable"] Medical Scanner
"
						dat += "
"
					if (cartridge.access_security)
						dat += "
Security Functions
"
						dat += "
"
						dat += "
 Security Records
"
						dat += "
"
					if(cartridge.access_quartermaster)
						dat += "
Quartermaster Functions:
"
						dat += "
"
						dat += "
 Supply Records
"
						dat += "
"
				dat += "
"

				dat += "
Utilities
"
				dat += "
"
				if (cartridge)
					if(cartridge.bot_access_flags)
						dat += "
 Bots Access
"
					if (cartridge.access_janitor)
						dat += "
 Custodial Locator
"
					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.
"
				else if(cartridge && cartridge.spam_enabled)
					dat += "Send To All"

			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/list/env_gases = environment.gases

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

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

					if (total_moles)
						for(var/id in env_gases)
							var/gas_level = env_gases[id][MOLES]/total_moles
							if(id in hardcoded_gases || gas_level > 0.001)
								dat += "[env_gases[id][GAS_META][META_GAS_NAME]]: [round(gas_level*100, 0.01)]%
"

					dat += "Temperature: [round(environment.temperature-T0C)]°C
"
				dat += "
"

			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=400x450;border=1;can_resize=1;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(usr.canUseTopic(src) && !href_list["close"])
		add_fingerprint(U)
		U.set_machine(src)

		switch(href_list["choice"])

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

			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

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

			if("Light")
				if(fon)
					fon = 0
					if(src in U.contents)
						U.AddLuminosity(-f_lum)
					else
						SetLuminosity(0)
				else
					fon = 1
					if(src in U.contents)
						U.AddLuminosity(f_lum)
					else
						SetLuminosity(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_noise && world.time < last_noise + 20) )
					playsound(loc, 'sound/items/bikehorn.ogg', 50, 1)
					last_noise = world.time
			if("Trombone")
				if ( !(last_noise && world.time < last_noise + 20) )
					playsound(loc, 'sound/misc/sadtrombone.ogg', 50, 1)
					last_noise = 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(hidden_uplink && (trim(lowertext(t)) == trim(lowertext(lock_code))))
							hidden_uplink.interact(U)
							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("MessageAll")
				src.send_to_all(U)

			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

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

			if("Toggle Door")
				if(cartridge && cartridge.access_remote_door)
					for(var/obj/machinery/door/poddoor/M in machines)
						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(prob(difficulty * 15) || (P.hidden_uplink))
								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(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(!U.canUseTopic(src))
		return
	if(emped)
		t = Gibberish(t, 100)
	return t

/obj/item/device/pda/proc/send_message(mob/living/user = usr,list/obj/item/device/pda/targets)
	var/message = msg_input(user)

	if(!message || !targets.len)
		return

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

	var/multiple = targets.len > 1

	var/datum/data_pda_msg/last_sucessful_msg
	for(var/obj/item/device/pda/P in targets)
		if(P == src)
			continue
		var/obj/machinery/message_server/MS = null
		MS = can_send(P)
		if(MS)
			var/datum/data_pda_msg/msg = MS.send_pda_message("[P.owner]","[owner]","[message]",photo)
			if(msg)
				last_sucessful_msg = msg
			if(!multiple)
				show_to_sender(msg)
			P.show_recieved_message(msg,src)
			if(!multiple)
				show_to_ghosts(user,msg)
				log_pda("[user] (PDA: [src.name]) sent \"[message]\" to [P.name]")
		else
			if(!multiple)
				user << "ERROR: Server isn't responding."
				return
	photo = null

	if(multiple)
		show_to_sender(last_sucessful_msg,1)
		show_to_ghosts(user,last_sucessful_msg,1)
		log_pda("[user] (PDA: [src.name]) sent \"[message]\" to Everyone")

/obj/item/device/pda/proc/show_to_sender(datum/data_pda_msg/msg,multiple = 0)
	tnote += "→ To [multiple ? "Everyone" : msg.recipient]:
[msg.message][msg.get_photo_ref()]
"

/obj/item/device/pda/proc/show_recieved_message(datum/data_pda_msg/msg,obj/item/device/pda/source)
	tnote += "← From [source.owner] ([source.ownjob]):
[msg.message][msg.get_photo_ref()]
"

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

	if(L && L.stat != UNCONSCIOUS)
		L << "\icon[src] Message from [source.owner] ([source.ownjob]), \"[msg.message]\"[msg.get_photo_ref()] (Reply)"

	overlays.Cut()
	overlays += image(icon, icon_alert)

/obj/item/device/pda/proc/show_to_ghosts(mob/living/user, datum/data_pda_msg/msg,multiple = 0)
	for(var/mob/M in player_list)
		if(isobserver(M) && M.client && (M.client.prefs.chat_toggles & CHAT_GHOSTPDA))
			M << "(F) [msg.sender] PDA Message --> [multiple ? "Everyone" : msg.recipient]: [msg.message][msg.get_photo_ref()]"

/obj/item/device/pda/proc/can_send(obj/item/device/pda/P)
	if(!P || qdeleted(P) || P.toff)
		return null

	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
				break

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

	if(!P || qdeleted(P) || P.toff) //in case the PDA or mob gets destroyed during telecomms_process()
		return null

	var/useTC = 0
	if(signal)
		if(signal.data["done"])
			useTC = 1
			var/turf/pos = get_turf(P)
			if(pos.z in signal.data["level"])
				useTC = 2

	if(useTC == 2)
		return useMS
	else
		return null

/obj/item/device/pda/proc/send_to_all(mob/living/U = usr)
	send_message(U,get_viewable_pdas())

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

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

	if(issilicon(usr))
		return

	if(usr.canUseTopic(src))
		if(id)
			remove_id()
		else
			usr << "This PDA does not have an ID in it!"

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

	if(issilicon(usr))
		return

	if (usr.canUseTopic(src))
		if(id)
			remove_id()
		else
			usr << "This PDA does not have an ID in it!"

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

	if(issilicon(usr))
		return

	if (usr.canUseTopic(src))
		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!"

/obj/item/device/pda/proc/id_check(mob/user, 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.unEquip(I))
					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)
			if(!user.unEquip(I))
				return 0
			var/obj/old_id = id
			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, mob/user, params)
	..()
	if(istype(C, /obj/item/weapon/cartridge) && !cartridge)
		cartridge = C
		if(!user.unEquip(C))
			return
		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(!id_check(user, 2))
					return
				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.unEquip(C))
			return
		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.unEquip(C))
				return
			C.loc = src
			user << "You slide \the [C] into \the [src]."
	else if(istype(C, /obj/item/weapon/photo))
		var/obj/item/weapon/photo/P = C
		photo = P.img
		user << "You scan \the [C]."
	return

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

			if(1)
				C.visible_message("[user] has analyzed [C]'s vitals!")
				healthscan(user, C, 1)
				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, 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 = html_encode(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(!detonate) return
	var/turf/T = get_turf(src)

	if (ismob(loc))
		var/mob/M = loc
		M.show_message("Your [src] explodes!", 1)
	else
		visible_message("[src] explodes!", "You hear a loud *pop*!")

	if(T)
		T.hotspot_expose(700,125)
		if(istype(cartridge, /obj/item/weapon/cartridge/syndicate))
			explosion(T, -1, 1, 3, 4)
		else
			explosion(T, -1, -1, 2, 3)
	qdel(src)
	return

/obj/item/device/pda/Destroy()
	PDAs -= src
	return ..()

//AI verb and proc for sending PDA messages.

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

	if(user.stat == 2)
		return //won't work if dead

	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]

	if(aicamera.aipictures.len>0)
		var/add_photo = input(user,"Do you want to attach a photo?","Photo","No") as null|anything in list("Yes","No")
		if(add_photo=="Yes")
			var/datum/picture/Pic = aicamera.selectpicture(aicamera)
			src.aiPDA.photo = Pic.fields["img"]
	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)
		return //won't work if dead
	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)
		return //won't work if dead
	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/proc/cmd_show_message_log(mob/user)
	if(user.stat == 2)
		return //won't work if dead
	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

/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:
//Clown PDA is slippery.
/obj/item/device/pda/clown
	name = "clown PDA"
	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/clown/Crossed(AM as mob|obj)
	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++

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

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

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

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

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

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

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

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

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

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

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

/obj/item/device/pda/mime
	name = "mime PDA"
	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
	name = "head of personnel PDA"
	default_cartridge = /obj/item/weapon/cartridge/hop
	icon_state = "pda-hop"

/obj/item/device/pda/heads/hos
	name = "head of security PDA"
	default_cartridge = /obj/item/weapon/cartridge/hos
	icon_state = "pda-hos"

/obj/item/device/pda/heads/ce
	name = "chief engineer PDA"
	default_cartridge = /obj/item/weapon/cartridge/ce
	icon_state = "pda-ce"

/obj/item/device/pda/heads/cmo
	name = "chief medical officer PDA"
	default_cartridge = /obj/item/weapon/cartridge/cmo
	icon_state = "pda-cmo"

/obj/item/device/pda/heads/rd
	name = "research director PDA"
	default_cartridge = /obj/item/weapon/cartridge/rd
	icon_state = "pda-rd"

/obj/item/device/pda/captain
	name = "captain PDA"
	default_cartridge = /obj/item/weapon/cartridge/captain
	icon_state = "pda-captain"
	detonate = 0

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

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

/obj/item/device/pda/shaftminer
	name = "shaft miner PDA"
	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
	name = "chaplain PDA"
	icon_state = "pda-chaplain"
	ttone = "holy"

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

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

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

/obj/item/device/pda/librarian
	name = "librarian PDA"
	icon_state = "pda-library"
	icon_alert = "pda-r-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
	name = "clear PDA"
	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
	name = "cook PDA"
	icon_state = "pda-cook"

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

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

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

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


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/bot_access_flags = 0 //Bit flags. Selection: SEC_BOT|MULE_BOT|FLOOR_BOT|CLEAN_BOT|MED_BOT
	var/spam_enabled = 0 //Enables "Send to All" Option

	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

	var/mob/living/simple_animal/bot/active_bot
	var/list/botlist = list()

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

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

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

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

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

/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
	bot_access_flags = SEC_BOT

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

/obj/item/weapon/cartridge/lawyer
	name = "\improper P.R.O.V.E. cartridge"
	icon_state = "cart-s"
	access_security = 1
	spam_enabled = 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/roboticist
	name = "\improper B.O.O.P. Remote Control cartridge"
	desc = "Packed with heavy duty triple-bot interlink!"
	bot_access_flags = FLOOR_BOT|CLEAN_BOT|MED_BOT

/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
	bot_access_flags = MULE_BOT

/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
	bot_access_flags = MULE_BOT|CLEAN_BOT
	access_janitor = 1
	access_security = 1
	access_newscaster = 1
	access_quartermaster = 1

/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
	bot_access_flags = SEC_BOT

/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
	bot_access_flags = FLOOR_BOT

/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
	bot_access_flags = MED_BOT

/obj/item/weapon/cartridge/rd
	name = "\improper Signal Ace DELUXE cartridge"
	icon_state = "cart-rd"
	access_manifest = 1
	access_status_display = 1
	access_reagent_scanner = 1
	access_atmos = 1
	bot_access_flags = FLOOR_BOT|CLEAN_BOT|MED_BOT

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

/obj/item/weapon/cartridge/captain
	name = "\improper Value-PAK cartridge"
	desc = "Now with 350% more value!" //Give the Captain...EVERYTHING! (Except Mime and Clown)
	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
	access_newscaster = 1
	access_quartermaster = 1
	access_janitor = 1
	bot_access_flags = SEC_BOT|MULE_BOT|FLOOR_BOT|CLEAN_BOT|MED_BOT
	spam_enabled = 1

/obj/item/weapon/cartridge/captain/New()
	..()
	radio = new /obj/item/radio/integrated/signal(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/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(text)
	if (!istype(loc, /obj/item/device/pda))
		return
	var/obj/item/device/pda/P = loc
	P.cart = text

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

	return

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

	var/datum/radio_frequency/frequency = SSradio.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/generate_menu(mob/user)
	switch(mode)
		if(40) //signaller
			var/obj/item/radio/integrated/signal/S = radio
			menu = "
 Remote Signaling System
"

			menu += {"
Send Signal

Frequency:
-
-
[format_frequency(S.frequency)]
+
+

Code:
-
-
[S.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 machines)
				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.attached.powernet.nodes)
					if(istype(term.master, /obj/machinery/power/apc))
						var/obj/machinery/power/apc/A = term.master
						L += A

				menu += "
Total power: [powmonitor.attached.powernet.viewavail] W
Total load:  [num2text(powmonitor.attached.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 (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.id] - [SO.pack.name] approved by [SO.orderer] [SO.reason ? "([SO.reason])":""]
"
			menu += "
"

			menu += "Current requests:

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

		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/mob/living/simple_animal/bot/cleanbot/B in living_mob_list)
					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 (53) // Newscaster
			menu = "
 Newscaster Access
"
			menu += "
 Current Newsfeed: [current_channel ? current_channel : "None"]
"
			var/datum/newscaster/feed_channel/current
			for(var/datum/newscaster/feed_channel/chan in news_network.network_channels)
				if (chan.channel_name == current_channel)
					current = chan
			if(!current)
				menu += "
 ERROR : NO CHANNEL FOUND
"
				return
			var/i = 1
			for(var/datum/newscaster/feed_message/msg in current.messages)
				menu +="-[msg.returnBody(-1)]
\[Story by [msg.returnAuthor(-1)]\]
"
				menu +="[msg.comments.len] comment[msg.comments.len > 1 ? "s" : ""]
"
				if(msg.img)
					user << browse_rsc(msg.img, "tmp_photo[i].png")
					menu +="
"
				i++
				for(var/datum/newscaster/feed_comment/comment in msg.comments)
					menu +="[comment.body]
[comment.author] [comment.time_stamp]
"
			menu += "
 Post Message"

		if (54) // Beepsky, Medibot, Floorbot, and Cleanbot access
			menu = "
 Bots Interlink
"
			bot_control()

/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

	var/obj/item/device/pda/pda = loc

	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)
			pda.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)
			pda.mode = 451
			mode = 451
			if(!active3)
				active1 = null

		if("Send Signal")
			spawn( 0 )
				var/obj/item/radio/integrated/signal/S = radio
				S.send_signal("ACTIVATE")
				return

		if("Signal Frequency")
			var/obj/item/radio/integrated/signal/S = radio
			var/new_frequency = sanitize_frequency(S.frequency + text2num(href_list["sfreq"]))
			S.set_frequency(new_frequency)

		if("Signal Code")
			var/obj/item/radio/integrated/signal/S = radio
			S.code += text2num(href_list["scode"])
			S.code = round(S.code)
			S.code = min(100, S.code)
			S.code = max(1, S.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]
			pda.mode = 433
			mode = 433

		if("Supply Orders")
			pda.mode =47
			mode = 47

		if("Newscaster Access")
			mode = 53

		if("Newscaster Message")
			var/pda_owner_name = pda.id ? "[pda.id.registered_name] ([pda.id.assignment])" : "Unknown"
			var/message = pda.msg_input()
			var/datum/newscaster/feed_channel/current
			for(var/datum/newscaster/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")
			current_channel = pda.msg_input()
			pda.Topic(null,list("choice"=num2text(mode)))
			return

	//Bot control section! Viciously ripped from radios for being laggy and terrible.
	if(href_list["op"])
		switch(href_list["op"])

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

			if("botlist")
				active_bot = null
			if("summon") //Args are in the correct order, they are stated here just as an easy reminder.
				active_bot.bot_control(command= "summon", user_turf= get_turf(usr), user_access= pda.GetAccess())
			else //Forward all other bot commands to the bot itself!
				active_bot.bot_control(command= href_list["op"], user= usr)

	if(href_list["mule"]) //MULEbots are special snowflakes, and need different args due to how they work.

		active_bot.bot_control(command= href_list["mule"], user= usr, pda= 1)

	generate_menu(usr)
	print_to_host(menu)

/obj/item/weapon/cartridge/proc/bot_control()

	var/mob/living/simple_animal/bot/Bot

//	if(!SC)
//		menu = "Interlink Error - Please reinsert cartridge."
//		return
	if(active_bot)
		menu += "[active_bot]
 Status: (refresh)
"
		menu += "Model: [active_bot.model]
"
		menu += "Location: [get_area(active_bot)]
"
		menu += "Mode: [active_bot.get_mode()]"
		if(active_bot.allow_pai)
			menu += "
pAI: "
			if(active_bot.paicard && active_bot.paicard.pai)
				menu += "[active_bot.paicard.pai.name]"
				if(active_bot.bot_core.allowed(usr))
					menu += " (eject)"
			else
				menu += "none"

		//MULEs!
		if(active_bot.bot_type == MULE_BOT)
			var/mob/living/simple_animal/bot/mulebot/MULE = active_bot
			var/atom/Load = MULE.load
			menu += "
Current Load: [ !Load ? "none" : "[Load.name] (unload)" ]
"
			menu += "Destination: [MULE.destination ? MULE.destination : "None"] (set)
"
			menu += "Set ID: [MULE.suffix]  Modify
"
			menu += "Power: [MULE.cell ? MULE.cell.percent() : 0]%
"
			menu += "Home: [!MULE.home_destination ? "none" : MULE.home_destination ]
"
			menu += "Delivery Reporting: [MULE.report_delivery ? "(On)": "(Off)"]
"
			menu += "Auto Return Home: [MULE.auto_return ? "(On)": "(Off)"]
"
			menu += "Auto Pickup Crate: [MULE.auto_pickup ? "(On)": "(Off)"]

" //Hue.

			menu += "\[Stop\] "
			menu += "\[Proceed\] "
			menu += "\[Return Home\]
"

		else
			menu += "
\[Stop Patrol\] "	//patrolon
			menu += "\[Start Patrol\] "	//patroloff
			menu += "\[Summon Bot\]
"		//summon
			menu += "Keep an ID inserted to upload access codes upon summoning."

		menu += "
Return to bot list"
	else
		menu += "
Scan for active bots

"
		var/turf/current_turf = get_turf(src)
		var/zlevel = current_turf.z
		var/botcount = 0
		for(Bot in living_mob_list) //Git da botz
			if(!Bot.on || Bot.z != zlevel || Bot.remote_disabled || !(bot_access_flags & Bot.bot_type)) //Only non-emagged bots on the same Z-level are detected!
				continue //Also, the PDA must have access to the bot type.
			menu += "[Bot.name] ([Bot.get_mode()])
"
			botcount++
		if(!botcount) //No bots at all? Lame.
			menu += "No bots found.
"
			return

	return menu


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/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

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

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

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

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

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

	set_frequency(frequency)

/obj/item/radio/integrated/signal/proc/set_frequency(new_frequency)
	SSradio.remove_object(src, frequency)
	frequency = new_frequency
	radio_connection = SSradio.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
	slot_flags = SLOT_BELT
	flags = NOBLUDGEON
	var/flush = FALSE
	var/mob/living/silicon/ai/AI
	origin_tech = "programming=4;materials=4"

/obj/item/device/aicard/afterattack(atom/target, mob/user, proximity)
	..()
	if(!proximity || !target)
		return
	if(AI) //AI is on the card, implies user wants to upload it.
		target.transfer_ai(AI_TRANS_FROM_CARD, user, AI, src)
		add_logs(user, AI, "carded", src)
	else //No AI on the card, therefore the user wants to download one.
		target.transfer_ai(AI_TRANS_TO_CARD, user, null, src)
	update_icon() //Whatever happened, update the card's state (icon, name) to match.

/obj/item/device/aicard/update_icon()
	if(AI)
		name = "[initial(name)]- [AI.name]"
		if(AI.stat == DEAD)
			icon_state = "aicard-404"
		else
			icon_state = "aicard-full"
		if(!AI.control_disabled)
			overlays += image('icons/obj/aicards.dmi', "aicard-on")
		AI.cancel_camera()
	else
		name = initial(name)
		icon_state = initial(icon_state)
		overlays.Cut()

/obj/item/device/aicard/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = 0, \
									datum/tgui/master_ui = null, datum/ui_state/state = hands_state)
	ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
	if(!ui)
		ui = new(user, src, ui_key, "intellicard", name, 500, 500, master_ui, state)
		ui.open()

/obj/item/device/aicard/ui_data()
	var/list/data = list()
	if(AI)
		data["name"] = AI.name
		data["laws"] = AI.laws.get_law_list(include_zeroth = 1)
		data["health"] = (AI.health + 100) / 2
		data["wireless"] = !AI.control_disabled //todo disabled->enabled
		data["radio"] = AI.radio_enabled
		data["isDead"] = AI.stat == DEAD
		data["isBraindead"] = AI.client ? TRUE : FALSE
	data["wiping"] = flush
	return data

/obj/item/device/aicard/ui_act(action,params)
	if(..())
		return
	switch(action)
		if("wipe")
			var/confirm = alert("Are you sure you want to wipe this card's memory? This cannot be undone once started.", name, "Yes", "No")
			if(confirm == "Yes" && !..())
				flush = TRUE
				if(AI && AI.loc == src)
					AI.suiciding = TRUE
					AI << "Your core files are being wiped!"
					while(AI.stat != DEAD)
						AI.adjustOxyLoss(2)
						AI.updatehealth()
						sleep(10)
					flush = FALSE
			. = TRUE
		if("wireless")
			AI.control_disabled = !AI.control_disabled
			AI << "[src]'s wireless port has been [AI.control_disabled ? "disabled" : "enabled"]!"
			. = TRUE
		if("radio")
			AI.radio_enabled = !AI.radio_enabled
			AI << "Your Subspace Transceiver has been [AI.radio_enabled ? "enabled" : "disabled"]!"
			. = TRUE
	update_icon()


code:
#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
	item_state	= "camera_bug"
	throw_speed	= 4
	throw_range	= 20
	origin_tech = "syndicate=3;engineering=1"

	var/obj/machinery/camera/current = null

	var/last_net_update = 0
	var/list/bugged_cameras = list()

	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()
	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
	return ..()

/obj/item/device/camera_bug/interact(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)
	user.set_machine(src)
	interact(user)

/obj/item/device/camera_bug/check_eye(mob/user)
	if ( loc != user || user.incapacitated() || user.eye_blind || !current )
		user.unset_machine()
		return
	var/turf/T = get_turf(user.loc)
	if(T.z != current.z || !current.can_use())
		user << "[src] has lost the signal."
		current = null
		user.unset_machine()

/obj/item/device/camera_bug/on_unset_machine(mob/user)
	user.reset_perspective(null)

/obj/item/device/camera_bug/proc/get_cameras()
	if( world.time > (last_net_update + 100))
		bugged_cameras = list()
		for(var/obj/machinery/camera/camera in cameranet.cameras)
			if(camera.stat || !camera.can_use())
				continue
			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(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 = ""
				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)
"
					if( C && (C.bug == src)) //Checks to see if the camera has a bug
						html += "\[Disable\]"

				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(href,list/href_list)
	if(usr != loc)
		usr.unset_machine()
		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_perspective(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.unset_machine()
		current = null
		return
	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_perspective(C)
					interact()
				else
					usr.unset_machine()
					usr << browse(null, "window=camerabug")
			return
		else
			usr.unset_machine()

	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()

#undef BUGMODE_LIST
#undef BUGMODE_MONITOR
#undef BUGMODE_TRACK