code:
/obj/machinery/sleep_console
	name = "sleeper console"
	icon = 'icons/obj/Cryogenic2.dmi'
	icon_state = "console"
	density = FALSE
	anchored = TRUE

/obj/machinery/sleeper
	name = "sleeper"
	icon = 'icons/obj/Cryogenic2.dmi'
	icon_state = "sleeper-open"
	density = FALSE
	anchored = TRUE
	state_open = TRUE
	var/efficiency = 1
	var/min_health = -25
	var/list/available_chems
	var/list/possible_chems = list(
		list("epinephrine", "morphine", "salbutamol", "bicaridine", "kelotane"),
		list("oculine"),
		list("antitoxin", "mutadone", "mannitol", "pen_acid"),
		list("omnizine")
	)

/obj/machinery/sleeper/New()
	..()
	component_parts = list()
	component_parts += new /obj/item/weapon/stock_parts/matter_bin(null)
	component_parts += new /obj/item/weapon/stock_parts/manipulator(null)
	component_parts += new /obj/item/weapon/stock_parts/console_screen(null)
	component_parts += new /obj/item/weapon/stock_parts/console_screen(null)
	component_parts += new /obj/item/stack/cable_coil(null, 1)
	component_parts += new /obj/item/weapon/circuitboard/sleeper(null)
	RefreshParts()

/obj/machinery/sleeper/RefreshParts()
	var/E
	for(var/obj/item/weapon/stock_parts/matter_bin/B in component_parts)
		E += B.rating
	var/I
	for(var/obj/item/weapon/stock_parts/manipulator/M in component_parts)
		I += M.rating

	efficiency = initial(efficiency)* E
	min_health = initial(min_health) * E
	available_chems = list()
	for(var/i in 1 to I)
		available_chems |= possible_chems[i]

/obj/machinery/sleeper/update_icon()
	if(state_open)
		icon_state = "sleeper-open"
	else
		icon_state = "sleeper"

/obj/machinery/sleeper/container_resist()
	visible_message("[occupant] emerges from [src]!",
		"You climb out of [src]!")
	open_machine()

/obj/machinery/sleeper/relaymove(mob/user)
	container_resist()

/obj/machinery/sleeper/open_machine()
	if(!state_open && !panel_open)
		..()

/obj/machinery/sleeper/close_machine(mob/user)
	if((isnull(user) || istype(user)) && state_open && !panel_open)
		..(user)

/obj/machinery/sleeper/attack_animal(mob/living/simple_animal/M)
	if(M.environment_smash)
		M.do_attack_animation(src)
		visible_message("[M.name] smashes [src] apart!")
		qdel(src)

/obj/machinery/sleeper/emp_act(severity)
	if(is_operational() && occupant)
		open_machine()
	..(severity)

/obj/machinery/sleeper/blob_act()
	if(prob(75))
		var/turf/T = get_turf(src)
		for(var/atom/movable/A in src)
			A.forceMove(T)
			A.blob_act()
		qdel(src)

/obj/machinery/sleeper/MouseDrop_T(mob/target, mob/user)
	if(user.stat || user.lying || !Adjacent(user) || !user.Adjacent(target) || !iscarbon(target))
		return
	close_machine(target)

/obj/machinery/sleeper/attackby(obj/item/I, mob/user, params)
	if(!state_open && !occupant)
		if(default_deconstruction_screwdriver(user, "sleeper-o", "sleeper", I))
			return
	if(default_change_direction_wrench(user, I))
		return
	if(exchange_parts(user, I))
		return
	if(default_pry_open(I))
		return
	if(default_deconstruction_crowbar(I))
		return
/obj/machinery/sleeper/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = 0, \
									datum/tgui/master_ui = null, datum/ui_state/state = notcontained_state)
	ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
	if(!ui)
		ui = new(user, src, ui_key, "sleeper", name, 375, 550, master_ui, state)
		ui.open()

/obj/machinery/sleeper/ui_data()
	var/list/data = list()
	data["occupied"] = occupant ? 1 : 0
	data["open"] = state_open

	data["chems"] = list()
	for(var/chem in available_chems)
		var/datum/reagent/R = chemical_reagents_list[chem]
		data["chems"] += list(list("name" = R.name, "id" = R.id, "allowed" = chem_allowed(chem)))

	data["occupant"] = list()
	if(occupant)
		data["occupant"]["name"] = occupant.name
		data["occupant"]["stat"] = occupant.stat
		data["occupant"]["health"] = occupant.health
		data["occupant"]["maxHealth"] = occupant.maxHealth
		data["occupant"]["minHealth"] = config.health_threshold_dead
		data["occupant"]["bruteLoss"] = occupant.getBruteLoss()
		data["occupant"]["oxyLoss"] = occupant.getOxyLoss()
		data["occupant"]["toxLoss"] = occupant.getToxLoss()
		data["occupant"]["fireLoss"] = occupant.getFireLoss()
		data["occupant"]["cloneLoss"] = occupant.getCloneLoss()
		data["occupant"]["brainLoss"] = occupant.getBrainLoss()
		data["occupant"]["reagents"] = list()
		if(occupant.reagents.reagent_list.len)
			for(var/datum/reagent/R in occupant.reagents.reagent_list)
				data["occupant"]["reagents"] += list(list("name" = R.name, "volume" = R.volume))
	return data

/obj/machinery/sleeper/ui_act(action, params)
	if(..())
		return
	switch(action)
		if("door")
			if(state_open)
				close_machine()
			else
				open_machine()
			. = TRUE
		if("inject")
			var/chem = params["chem"]
			if(!is_operational() || !occupant)
				return
			if(occupant.health < min_health && chem != "epinephrine")
				return
			if(inject_chem(chem))
				. = TRUE

/obj/machinery/sleeper/proc/inject_chem(chem)
	if((chem in available_chems) && chem_allowed(chem))
		occupant.reagents.add_reagent(chem, 10)
		return TRUE

/obj/machinery/sleeper/proc/chem_allowed(chem)
	if(!occupant)
		return
	var/amount = occupant.reagents.get_reagent_amount(chem) + 10 <= 20 * efficiency
	var/health = occupant.health > min_health || chem == "epinephrine"
	return amount && health


code:
/obj/machinery/ai_slipper
	name = "\improper AI liquid dispenser"
	icon = 'icons/obj/device.dmi'
	icon_state = "motion3"
	layer = 3
	anchored = 1
	var/uses = 20
	var/disabled = 1
	var/lethal = 0
	var/locked = 1
	var/cooldown_time = 0
	var/cooldown_timeleft = 0
	var/cooldown_on = 0
	req_access = list(access_ai_upload)

/obj/machinery/ai_slipper/power_change()
	if(stat & BROKEN)
		return
	else
		if( powered() )
			stat &= ~NOPOWER
		else
			icon_state = "motion0"
			stat |= NOPOWER

/obj/machinery/ai_slipper/proc/setState(enabled, uses)
	src.disabled = disabled
	src.uses = uses
	src.power_change()

/obj/machinery/ai_slipper/attackby(obj/item/weapon/W, mob/user, params)
	if(stat & (NOPOWER|BROKEN))
		return
	if (istype(user, /mob/living/silicon))
		return src.attack_hand(user)
	else // trying to unlock the interface
		if (src.allowed(usr))
			locked = !locked
			user << "You [ locked ? "lock" : "unlock"] the device."
			if (locked)
				if (user.machine==src)
					user.unset_machine()
					user << browse(null, "window=ai_slipper")
			else
				if (user.machine==src)
					src.attack_hand(usr)
		else
			user << "Access denied."

/obj/machinery/ai_slipper/attack_ai(mob/user)
	return attack_hand(user)

/obj/machinery/ai_slipper/attack_hand(mob/user)
	if(stat & (NOPOWER|BROKEN))
		return
	if ( (get_dist(src, user) > 1 ))
		if (!(istype(user, /mob/living/silicon) || IsAdminGhost(user)))
			user << text("Too far away.")
			user.unset_machine()
			user << browse(null, "window=ai_slipper")
			return

	user.set_machine(src)
	var/loc = src.loc
	if (istype(loc, /turf))
		loc = loc:loc
	if (!istype(loc, /area))
		user << text("Turret badly positioned - loc.loc is [].", loc)
		return
	var/area/area = loc
	var/t = "<TT>AI Liquid Dispenser ([format_text(area.name)])
"

	if(src.locked && (!(istype(user, /mob/living/silicon) || IsAdminGhost(user))))
		t += "(Swipe ID card to unlock control panel.)
"
	else
		t += text("Dispenser [] - []?
\n", src.disabled?"deactivated":"activated", src, src.disabled?"Enable":"Disable")
		t += text("Uses Left: [uses]. Activate the dispenser?
\n")

	user << browse(t, "window=computer;size=575x450")
	onclose(user, "computer")
	return

/obj/machinery/ai_slipper/Topic(href, href_list)
	if(..())
		return
	if (src.locked)
		if (!(istype(usr, /mob/living/silicon)|| IsAdminGhost(usr)))
			usr << "Control panel is locked!"
			return
	if (href_list["toggleOn"])
		src.disabled = !src.disabled
		icon_state = src.disabled? "motion0":"motion3"
	if (href_list["toggleUse"])
		if(cooldown_on || disabled)
			return
		else
			PoolOrNew(/obj/effect/particle_effect/foam, loc)
			src.uses--
			cooldown_on = 1
			cooldown_time = world.timeofday + 100
			slip_process()
			return

	src.attack_hand(usr)
	return

/obj/machinery/ai_slipper/proc/slip_process()
	while(cooldown_time - world.timeofday > 0)
		var/ticksleft = cooldown_time - world.timeofday

		if(ticksleft > 1e5)
			cooldown_time = world.timeofday + 10	// midnight rollover

		cooldown_timeleft = (ticksleft / 10)
		sleep(5)
	if (uses <= 0)
		return
	if (uses >= 0)
		cooldown_on = 0
	src.power_change()
	return

license: mit


code:
#define AIRLOCK_CONTROL_RANGE 5

// This code allows for airlocks to be controlled externally by setting an id_tag and comm frequency (disables ID access)
/obj/machinery/door/airlock
	var/id_tag
	var/frequency
	var/shockedby = list()
	var/datum/radio_frequency/radio_connection

/obj/machinery/door/airlock/receive_signal(datum/signal/signal)
	if(!signal || signal.encryption) return

	if(id_tag != signal.data["tag"] || !signal.data["command"]) return

	switch(signal.data["command"])
		if("open")
			open(1)

		if("close")
			close(1)

		if("unlock")
			locked = 0
			update_icon()

		if("lock")
			locked = 1
			update_icon()

		if("secure_open")
			locked = 0
			update_icon()

			sleep(2)
			open(1)

			locked = 1
			update_icon()

		if("secure_close")
			locked = 0
			close(1)

			locked = 1
			sleep(2)
			update_icon()

	send_status()

/obj/machinery/door/airlock/proc/send_status()
	if(radio_connection)
		var/datum/signal/signal = new
		signal.transmission_method = 1 //radio signal
		signal.data["tag"] = id_tag
		signal.data["timestamp"] = world.time

		signal.data["door_status"] = density?("closed"):("open")
		signal.data["lock_status"] = locked?("locked"):("unlocked")

		radio_connection.post_signal(src, signal, range = AIRLOCK_CONTROL_RANGE, filter = RADIO_AIRLOCK)

/obj/machinery/door/airlock/open(surpress_send)
	. = ..()
	if(!surpress_send) send_status()

/obj/machinery/door/airlock/close(surpress_send)
	. = ..()
	if(!surpress_send) send_status()

/obj/machinery/door/airlock/proc/set_frequency(new_frequency)
	SSradio.remove_object(src, frequency)
	if(new_frequency)
		frequency = new_frequency
		radio_connection = SSradio.add_object(src, frequency, RADIO_AIRLOCK)

/obj/machinery/door/airlock/initialize()
	if(frequency)
		set_frequency(frequency)

	update_icon()

/obj/machinery/door/airlock/New()
	..()

	if(SSradio)
		set_frequency(frequency)

/obj/machinery/door/airlock/Destroy()
	if(frequency && SSradio)
		SSradio.remove_object(src,frequency)
	return ..()

/obj/machinery/airlock_sensor
	icon = 'icons/obj/airlock_machines.dmi'
	icon_state = "airlock_sensor_off"
	name = "airlock sensor"

	anchored = 1
	power_channel = ENVIRON

	var/id_tag
	var/master_tag
	var/frequency = 1449

	var/datum/radio_frequency/radio_connection

	var/on = 1
	var/alert = 0

/obj/machinery/airlock_sensor/update_icon()
	if(on)
		if(alert)
			icon_state = "airlock_sensor_alert"
		else
			icon_state = "airlock_sensor_standby"
	else
		icon_state = "airlock_sensor_off"

/obj/machinery/airlock_sensor/attack_hand(mob/user)
	var/datum/signal/signal = new
	signal.transmission_method = 1 //radio signal
	signal.data["tag"] = master_tag
	signal.data["command"] = "cycle"

	radio_connection.post_signal(src, signal, range = AIRLOCK_CONTROL_RANGE, filter = RADIO_AIRLOCK)
	flick("airlock_sensor_cycle", src)

/obj/machinery/airlock_sensor/process()
	if(on)
		var/datum/signal/signal = new
		signal.transmission_method = 1 //radio signal
		signal.data["tag"] = id_tag
		signal.data["timestamp"] = world.time

		var/datum/gas_mixture/air_sample = return_air()

		var/pressure = round(air_sample.return_pressure(),0.1)
		alert = (pressure < ONE_ATMOSPHERE*0.8)

		signal.data["pressure"] = num2text(pressure)

		radio_connection.post_signal(src, signal, range = AIRLOCK_CONTROL_RANGE, filter = RADIO_AIRLOCK)

	update_icon()

/obj/machinery/airlock_sensor/proc/set_frequency(new_frequency)
	SSradio.remove_object(src, frequency)
	frequency = new_frequency
	radio_connection = SSradio.add_object(src, frequency, RADIO_AIRLOCK)

/obj/machinery/airlock_sensor/initialize()
	set_frequency(frequency)

/obj/machinery/airlock_sensor/New()
	..()

	if(SSradio)
		set_frequency(frequency)

/obj/machinery/airlock_sensor/Destroy()
	if(SSradio)
		SSradio.remove_object(src,frequency)
	return ..()


code:
var/list/announcement_systems = list()

/obj/machinery/announcement_system
	density = 1
	anchored = 1
	name = "\improper Automated Announcement System"
	desc = "An automated announcement system that handles minor announcements over the radio."
	icon = 'icons/obj/machines/telecomms.dmi'
	icon_state = "AAS_On"
	var/obj/item/device/radio/headset/radio

	verb_say = "coldly states"
	verb_ask = "queries"
	verb_exclaim = "alarms"

	var/broken = 0

	idle_power_usage = 20
	active_power_usage = 50

	var/arrival = "%PERSON has signed up as %RANK"
	var/arrivalToggle = 1
	var/newhead = "%PERSON, %RANK, is the department head."
	var/newheadToggle = 1

	var/greenlight = "Light_Green"
	var/pinklight = "Light_Pink"
	var/errorlight = "Error_Red"

/obj/machinery/announcement_system/New()
	..()
	announcement_systems += src
	radio = new /obj/item/device/radio/headset/ai(src)

	component_parts = list()
	component_parts += new /obj/item/weapon/circuitboard/announcement_system(null)
	component_parts += new /obj/item/stack/cable_coil(null, 2)
	component_parts += new /obj/item/weapon/stock_parts/console_screen(null)
	RefreshParts()

	update_icon()

/obj/machinery/announcement_system/update_icon()
	if(is_operational())
		icon_state = (panel_open ? "AAS_On_Open" : "AAS_On")
	else
		icon_state = (panel_open ? "AAS_Off_Open" : "AAS_Off")

	overlays.Cut()
	if(arrivalToggle)
		overlays |= greenlight
	else
		overlays -= greenlight

	if(newheadToggle)
		overlays |= pinklight
	else
		overlays -= pinklight

	if(broken)
		overlays |= errorlight
	else
		overlays -= errorlight

/obj/machinery/announcement_system/Destroy()
	announcement_systems -= src //"OH GOD WHY ARE THERE 100,000 LISTED ANNOUNCEMENT SYSTEMS?!!"
	return ..()

/obj/machinery/announcement_system/power_change()
	..()
	update_icon()

/obj/machinery/announcement_system/attackby(obj/item/P, mob/user, params)
	if(istype(P, /obj/item/weapon/screwdriver))
		if(!panel_open)
			playsound(src.loc, 'sound/items/Screwdriver.ogg', 50, 1)
			user << "You open the maintenance hatch of [src]."
			panel_open = 1
		else
			playsound(src.loc, 'sound/items/Screwdriver.ogg', 50, 1)
			user << "You close the maintenance hatch of [src]."
			panel_open = 0
		update_icon()
		return

	if(panel_open)
		default_deconstruction_crowbar(P)
		if(istype(P, /obj/item/device/multitool) && broken)
			user << "You reset [src]'s firmware."
			broken = 0
			update_icon()
			return

	return ..()

/obj/machinery/announcement_system/proc/CompileText(str, user, rank) //replaces user-given variables with actual thingies.
	str = replacetext(str, "%PERSON", "[user]")
	str = replacetext(str, "%RANK", "[rank]")
	return str

/obj/machinery/announcement_system/proc/announce(message_type, user, rank, list/channels)
	if(!is_operational())
		return

	var/message

	if(message_type == "ARRIVAL" && arrivalToggle)
		message = CompileText(arrival, user, rank)

	else if(message_type == "NEWHEAD" && newheadToggle)
		message = CompileText(newhead, user, rank)

	if(channels.len == 0)
		radio.talk_into(src, message, null, list(SPAN_ROBOT))
	else
		for(var/channel in channels)
			radio.talk_into(src, message, channel, list(SPAN_ROBOT))

//config stuff

/obj/machinery/announcement_system/interact(mob/user)
	if(broken)
		visible_message("[src] buzzes.", "You hear a faint buzz.")
		playsound(src.loc, 'sound/machines/buzz-two.ogg', 50, 1)
		return

	var/contents = "Arrival Announcement:  ([(arrivalToggle ? "On" : "Off")])
\n[arrival]

\n"
	contents += "Departmental Head Announcement:  ([(newheadToggle ? "On" : "Off")])
\n[newhead]

\n"

	var/datum/browser/popup = new(user, "announcement_config", "Automated Announcement Configuration", 370, 220)
	popup.set_content(contents)
	popup.open()

/obj/machinery/announcement_system/Topic(href, href_list)
	if(broken)
		visible_message("[src] buzzes.", "You hear a faint buzz.")
		playsound(src.loc, 'sound/machines/buzz-two.ogg', 50, 1)
		return

	if(href_list["ArrivalTopic"])
		var/NewMessage = stripped_input(usr, "Enter in the arrivals announcement configuration.", "Arrivals Announcement Config", arrival)
		if(!in_range(src, usr) && src.loc != usr && !isAI(usr))
			return
		if(NewMessage)
			arrival = NewMessage
	else if(href_list["NewheadTopic"])
		var/NewMessage = stripped_input(usr, "Enter in the departmental head announcement configuration.", "Head Departmental Announcement Config", newhead)
		if(!in_range(src, usr) && src.loc != usr && !isAI(usr))
			return
		if(NewMessage)
			newhead = NewMessage

	else if(href_list["NewheadT-Topic"])
		newheadToggle = !newheadToggle
		update_icon()
	else if(href_list["ArrivalT-Topic"])
		arrivalToggle = !arrivalToggle
		update_icon()

	add_fingerprint(usr)
	interact(usr)
	return

/obj/machinery/announcement_system/attack_ai(mob/living/silicon/ai/user)
	if(!isAI(user))
		return
	if(broken)
		user << "[src]'s firmware appears to be malfunctioning!"
		return
	interact(user)

/obj/machinery/announcement_system/proc/act_up() //does funny breakage stuff
	broken = 1
	update_icon()

	arrival = pick("#!@%ERR-34%2 CANNOT LOCAT@# JO# F*LE!", "CRITICAL ERROR 99.", "ERR)#: DA#AB@#E NOT F(*ND!")
	newhead = pick("OV#RL()D: \[UNKNOWN??\] DET*#CT)D!", "ER)#R - B*@ TEXT F*O(ND!", "AAS.exe is not responding. NanoOS is searching for a solution to the problem.")

/obj/machinery/announcement_system/emp_act(severity)
	if(!(stat & (NOPOWER|BROKEN)))
		act_up()
	..(severity)

/obj/machinery/announcement_system/emag_act()
	if(!emagged)
		emagged = 1
		act_up()


code:
#define AUTOLATHE_MAIN_MENU       1
#define AUTOLATHE_CATEGORY_MENU   2
#define AUTOLATHE_SEARCH_MENU     3

/obj/machinery/autolathe
	name = "autolathe"
	desc = "It produces items using metal and glass."
	icon_state = "autolathe"
	density = 1

	var/operating = 0
	anchored = 1
	var/list/L = list()
	var/list/LL = list()
	var/hacked = 0
	var/disabled = 0
	var/shocked = 0
	var/hack_wire
	var/disable_wire
	var/shock_wire
	use_power = 1
	idle_power_usage = 10
	active_power_usage = 100
	var/busy = 0
	var/prod_coeff

	var/datum/design/being_built
	var/datum/research/files
	var/list/datum/design/matching_designs
	var/selected_category
	var/screen = 1

	var/datum/material_container/materials

	var/list/categories = list(
							"Tools",
							"Electronics",
							"Construction",
							"T-Comm",
							"Security",
							"Machinery",
							"Medical",
							"Misc",
							"Imported"
							)

/obj/machinery/autolathe/New()
	..()
	component_parts = list()
	component_parts += new /obj/item/weapon/circuitboard/autolathe(null)
	component_parts += new /obj/item/weapon/stock_parts/matter_bin(null)
	component_parts += new /obj/item/weapon/stock_parts/matter_bin(null)
	component_parts += new /obj/item/weapon/stock_parts/matter_bin(null)
	component_parts += new /obj/item/weapon/stock_parts/manipulator(null)
	component_parts += new /obj/item/weapon/stock_parts/console_screen(null)
	materials = new /datum/material_container(src, list(MAT_METAL=1, MAT_GLASS=1))
	RefreshParts()

	wires = new /datum/wires/autolathe(src)
	files = new /datum/research/autolathe(src)
	matching_designs = list()

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

/obj/machinery/autolathe/interact(mob/user)
	if(!is_operational())
		return

	if(shocked && !(stat & NOPOWER))
		shock(user,50)

	var/dat

	switch(screen)
		if(AUTOLATHE_MAIN_MENU)
			dat = main_win(user)
		if(AUTOLATHE_CATEGORY_MENU)
			dat = category_win(user,selected_category)
		if(AUTOLATHE_SEARCH_MENU)
			dat = search_win(user)

	var/datum/browser/popup = new(user, "autolathe", name, 400, 500)
	popup.set_content(dat)
	popup.open()

	return

/obj/machinery/autolathe/attackby(obj/item/O, mob/user, params)
	if (busy)
		user << "The autolathe is busy. Please wait for completion of previous operation."
		return 1

	if(default_deconstruction_screwdriver(user, "autolathe_t", "autolathe", O))
		updateUsrDialog()
		return

	if(exchange_parts(user, O))
		return

	if(panel_open)
		if(istype(O, /obj/item/weapon/crowbar))
			materials.retrieve_all()
			default_deconstruction_crowbar(O)
			return 1
		else if(is_wire_tool(O))
			wires.interact(user)
			return 1
	if(stat)
		return 1

	if(istype(O, /obj/item/weapon/disk/design_disk))
		user.visible_message("[user] begins to load \the [O] in \the [src]...",
			"You begin to load a design from \the [O]...",
			"You hear the chatter of a floppy drive.")
		busy = 1
		var/obj/item/weapon/disk/design_disk/D = O
		if(do_after(user, 14.4, target = src))
			files.AddDesign2Known(D.blueprint)

		busy = 0
		return

	var/material_amount = materials.get_item_material_amount(O)
	if(!material_amount)
		user << "This object does not contain sufficient amounts of metal or glass to be accepted by the autolathe."
		return 1
	if(!materials.has_space(material_amount))
		user << "The autolathe is full. Please remove metal or glass from the autolathe in order to insert more."
		return 1
	if(!user.unEquip(O))
		user << "\The [O] is stuck to you and cannot be placed into the autolathe."
		return 1

	busy = 1
	var/inserted = materials.insert_item(O)
	if(inserted)
		if(istype(O,/obj/item/stack))
			if (O.materials[MAT_METAL])
				flick("autolathe_o",src)//plays metal insertion animation
			if (O.materials[MAT_GLASS])
				flick("autolathe_r",src)//plays glass insertion animation
			user << "You insert [inserted] sheet[inserted>1 ? "s" : ""] to the autolathe."
			use_power(inserted*100)
		else
			user << "You insert a material total of [inserted] to the autolathe."
			use_power(max(500,inserted/10))
			qdel(O)
	busy = 0
	src.updateUsrDialog()

/obj/machinery/autolathe/Topic(href, href_list)
	if(..())
		return
	if (!busy)
		if(href_list["menu"])
			screen = text2num(href_list["menu"])

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

		if(href_list["make"])

			var/turf/T = loc

			/////////////////
			//href protection
			being_built = files.FindDesignByID(href_list["make"]) //check if it's a valid design
			if(!being_built)
				return

			//multiplier checks : only stacks can have one and its value is 1, 10 ,25 or max_multiplier
			var/multiplier = text2num(href_list["multiplier"])
			var/max_multiplier = min(being_built.maxstack, being_built.materials[MAT_METAL] ?round(materials.amount(MAT_METAL)/being_built.materials[MAT_METAL]):INFINITY,being_built.materials[MAT_GLASS]?round(materials.amount(MAT_GLASS)/being_built.materials[MAT_GLASS]):INFINITY)
			var/is_stack = ispath(being_built.build_path, /obj/item/stack)

			if(!is_stack && (multiplier > 1))
				return
			if (!(multiplier in list(1,10,25,max_multiplier))) //"enough materials ?" is checked further down
				return
			/////////////////

			var/coeff = (is_stack ? 1 : 2 ** prod_coeff) //stacks are unaffected by production coefficient
			var/metal_cost = being_built.materials[MAT_METAL]
			var/glass_cost = being_built.materials[MAT_GLASS]

			var/power = max(2000, (metal_cost+glass_cost)*multiplier/5)

			if((materials.amount(MAT_METAL) >= metal_cost*multiplier/coeff) && (materials.amount(MAT_GLASS) >= glass_cost*multiplier/coeff))
				busy = 1
				use_power(power)
				icon_state = "autolathe"
				flick("autolathe_n",src)
				spawn(32/coeff)
					use_power(power)
					if(is_stack)
						var/list/materials_used = list(MAT_METAL=metal_cost*multiplier, MAT_GLASS=glass_cost*multiplier)
						materials.use_amount(materials_used)

						var/obj/item/stack/N = new being_built.build_path(T, multiplier)
						N.update_icon()
						N.autolathe_crafted(src)

						for(var/obj/item/stack/S in T.contents - N)
							if(istype(S, N.merge_type))
								N.merge(S)
					else
						var/list/materials_used = list(MAT_METAL=metal_cost/coeff, MAT_GLASS=glass_cost/coeff)
						materials.use_amount(materials_used)
						var/obj/item/new_item = new being_built.build_path(T)
						new_item.materials = materials_used.Copy()
						new_item.autolathe_crafted(src)
					busy = 0
					src.updateUsrDialog()

		if(href_list["search"])
			matching_designs.Cut()

			for(var/v in files.known_designs)
				var/datum/design/D = files.known_designs[v]
				if(findtext(D.name,href_list["to_search"]))
					matching_designs.Add(D)
	else
		usr << "The autolathe is busy. Please wait for completion of previous operation."

	src.updateUsrDialog()

	return

/obj/machinery/autolathe/RefreshParts()
	var/tot_rating = 0
	prod_coeff = 0
	for(var/obj/item/weapon/stock_parts/matter_bin/MB in component_parts)
		tot_rating += MB.rating
	tot_rating *= 25000
	materials.max_amount = tot_rating * 3
	for(var/obj/item/weapon/stock_parts/manipulator/M in component_parts)
		prod_coeff += M.rating - 1

/obj/machinery/autolathe/proc/main_win(mob/user)
	var/dat = "

Autolathe Menu:

"
	dat += "Total amount: [materials.total_amount] / [materials.max_amount] cm3
"
	dat += "Metal amount: [materials.amount(MAT_METAL)] cm3
"
	dat += "Glass amount: [materials.amount(MAT_GLASS)] cm3
"

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

	var/line_length = 1
	dat += "

"

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

"
			line_length = 1

		dat += "[C]	"
		line_length++

	dat += "

"
	return dat

/obj/machinery/autolathe/proc/category_win(mob/user,selected_category)
	var/dat = "Return to main menu"
	dat += "

Browsing [selected_category]:

"
	dat += "Total amount: [materials.total_amount] / [materials.max_amount] cm3
"
	dat += "Metal amount: [materials.amount(MAT_METAL)] cm3
"
	dat += "Glass amount: [materials.amount(MAT_GLASS)] cm3
"

	for(var/v in files.known_designs)
		var/datum/design/D = files.known_designs[v]
		if(!(selected_category in D.category))
			continue

		if(disabled || !can_build(D))
			dat += "[D.name]"
		else
			dat += "[D.name]"

		if(ispath(D.build_path, /obj/item/stack))
			var/max_multiplier = min(D.maxstack, D.materials[MAT_METAL] ?round(materials.amount(MAT_METAL)/D.materials[MAT_METAL]):INFINITY,D.materials[MAT_GLASS]?round(materials.amount(MAT_GLASS)/D.materials[MAT_GLASS]):INFINITY)
			if (max_multiplier>10 && !disabled)
				dat += " x10"
			if (max_multiplier>25 && !disabled)
				dat += " x25"
			if(max_multiplier > 0 && !disabled)
				dat += " x[max_multiplier]"

		dat += "[get_design_cost(D)]
"

	dat += "
"
	return dat

/obj/machinery/autolathe/proc/search_win(mob/user)
	var/dat = "Return to main menu"
	dat += "

Search results:

"
	dat += "Total amount: [materials.total_amount] / [materials.max_amount] cm3
"
	dat += "Metal amount: [materials.amount(MAT_METAL)] cm3
"
	dat += "Glass amount: [materials.amount(MAT_GLASS)] cm3
"

	for(var/v in matching_designs)
		var/datum/design/D = v
		if(disabled || !can_build(D))
			dat += "[D.name]"
		else
			dat += "[D.name]"

		if(ispath(D.build_path, /obj/item/stack))
			var/max_multiplier = min(D.maxstack, D.materials[MAT_METAL] ?round(materials.amount(MAT_METAL)/D.materials[MAT_METAL]):INFINITY,D.materials[MAT_GLASS]?round(materials.amount(MAT_GLASS)/D.materials[MAT_GLASS]):INFINITY)
			if (max_multiplier>10 && !disabled)
				dat += " x10"
			if (max_multiplier>25 && !disabled)
				dat += " x25"
			if(max_multiplier > 0 && !disabled)
				dat += " x[max_multiplier]"

		dat += "[get_design_cost(D)]
"

	dat += "
"
	return dat

/obj/machinery/autolathe/proc/can_build(datum/design/D)
	var/coeff = (ispath(D.build_path,/obj/item/stack) ? 1 : 2 ** prod_coeff)

	if(D.materials[MAT_METAL] && (materials.amount(MAT_METAL) < (D.materials[MAT_METAL] / coeff)))
		return 0
	if(D.materials[MAT_GLASS] && (materials.amount(MAT_GLASS) < (D.materials[MAT_GLASS] / coeff)))
		return 0
	return 1

/obj/machinery/autolathe/proc/get_design_cost(datum/design/D)
	var/coeff = (ispath(D.build_path,/obj/item/stack) ? 1 : 2 ** prod_coeff)
	var/dat
	if(D.materials[MAT_METAL])
		dat += "[D.materials[MAT_METAL] / coeff] metal "
	if(D.materials[MAT_GLASS])
		dat += "[D.materials[MAT_GLASS] / coeff] glass"
	return dat

/obj/machinery/autolathe/proc/reset(wire)
	switch(wire)
		if(WIRE_HACK)
			if(!wires.is_cut(wire))
				adjust_hacked(FALSE)
		if(WIRE_SHOCK)
			if(!wires.is_cut(wire))
				shocked = FALSE
		if(WIRE_DISABLE)
			if(!wires.is_cut(wire))
				disabled = FALSE

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

/obj/machinery/autolathe/proc/adjust_hacked(state)
	hacked = state
	for(var/datum/design/D in files.possible_designs)
		if((D.build_type & AUTOLATHE) && ("hacked" in D.category))
			if(hacked)
				files.AddDesign2Known(D)
			else
				files.known_designs -= D.id

//Called when the object is constructed by an autolathe
//Has a reference to the autolathe so you can do !!FUN!! things with hacked lathes
/obj/item/proc/autolathe_crafted(obj/machinery/autolathe/A)
	return


code:
/obj/machinery/button
	name = "button"
	desc = "A remote control switch."
	icon = 'icons/obj/stationobjs.dmi'
	icon_state = "doorctrl"
	var/skin = "doorctrl"
	power_channel = ENVIRON
	var/obj/item/device/assembly/device
	var/obj/item/weapon/electronics/airlock/board
	var/device_type = null
	var/id = null
	var/initialized = 0

	anchored = 1
	use_power = 1
	idle_power_usage = 2

/obj/machinery/button/New(loc, ndir = 0, built = 0)
	..()
	if(built)
		dir = ndir
		pixel_x = (dir & 3)? 0 : (dir == 4 ? -24 : 24)
		pixel_y = (dir & 3)? (dir ==1 ? -24 : 24) : 0
		panel_open = 1
		update_icon()

	if(!built && !device && device_type)
		device = new device_type(src)

	src.check_access(null)

	if(req_access.len || req_one_access.len)
		board = new(src)
		if(req_access.len)
			board.accesses = req_access
		else
			board.one_access = 1
			board.accesses = req_one_access

/obj/machinery/button/update_icon()
	overlays.Cut()
	if(panel_open)
		icon_state = "button-open"
		if(device)
			overlays += "button-device"
		if(board)
			overlays += "button-board"

	else
		if(stat & (NOPOWER|BROKEN))
			icon_state = "[skin]-p"
		else
			icon_state = skin

/obj/machinery/button/attackby(obj/item/W, mob/user, params)
	if(istype(W, /obj/item/device/detective_scanner))
		return

	if(istype(W, /obj/item/weapon/screwdriver))
		if(panel_open || allowed(user))
			default_deconstruction_screwdriver(user, "button-open", "[skin]",W)
			update_icon()
		else
			user << "Maintenance Access Denied"
			flick("[skin]-denied", src)
		return

	if(panel_open)
		if(!device && istype(W, /obj/item/device/assembly))
			if(!user.unEquip(W))
				user << "\The [W] is stuck to you!"
				return
			W.loc = src
			device = W
			user << "You add [W] to the button."

		if(!board && istype(W, /obj/item/weapon/electronics/airlock))
			if(!user.unEquip(W))
				user << "\The [W] is stuck to you!"
				return
			W.loc = src
			board = W
			if(board.one_access)
				req_one_access = board.accesses
			else
				req_access = board.accesses
			user << "You add [W] to the button."

		if(!device && !board && istype(W, /obj/item/weapon/wrench))
			user << "You start unsecuring the button frame..."
			playsound(loc, 'sound/items/Ratchet.ogg', 50, 1)
			if(do_after(user, 40/W.toolspeed, target = src))
				user << "You unsecure the button frame."
				transfer_fingerprints_to(new /obj/item/wallframe/button(get_turf(src)))
				playsound(loc, 'sound/items/Deconstruct.ogg', 50, 1)
				qdel(src)

		update_icon()
		return

	return src.attack_hand(user)

/obj/machinery/button/emag_act(mob/user)
	req_access = list()
	req_one_access = list()
	playsound(src.loc, "sparks", 100, 1)

/obj/machinery/button/attack_ai(mob/user)
	if(!panel_open)
		return attack_hand(user)

/obj/machinery/button/proc/setup_device()
	if(id && istype(device, /obj/item/device/assembly/control))
		var/obj/item/device/assembly/control/A = device
		A.id = id
	initialized = 1

/obj/machinery/button/attack_hand(mob/user)
	if(!initialized)
		setup_device()
	src.add_fingerprint(user)
	if(panel_open)
		if(device || board)
			if(device)
				device.loc = get_turf(src)
				device = null
			if(board)
				board.loc = get_turf(src)
				req_access = list()
				req_one_access = list()
				board = null
			update_icon()
			user << "You remove electronics from the button frame."

		else
			if(skin == "doorctrl")
				skin = "launcher"
			else
				skin = "doorctrl"
			user << "You change the button frame's front panel."
		return

	if((stat & (NOPOWER|BROKEN)))
		return

	if(device && device.cooldown)
		return

	if(!allowed(user))
		user << "Access Denied"
		flick("[skin]-denied", src)
		return

	use_power(5)
	icon_state = "[skin]1"

	if(device)
		device.pulsed()

	spawn(15)
		update_icon()

/obj/machinery/button/power_change()
	..()
	update_icon()

/obj/machinery/button/door
	name = "door button"
	desc = "A door remote control switch."
	var/normaldoorcontrol = 0
	var/specialfunctions = OPEN // Bitflag, see assembly file

/obj/machinery/button/door/setup_device()
	if(!device)
		if(normaldoorcontrol)
			var/obj/item/device/assembly/control/airlock/A = new(src)
			device = A
			A.specialfunctions = specialfunctions
		else
			device = new /obj/item/device/assembly/control(src)
	..()

/obj/machinery/button/massdriver
	name = "mass driver button"
	desc = "A remote control switch for a mass driver."
	icon_state = "launcher"
	skin = "launcher"
	device_type = /obj/item/device/assembly/control/massdriver

/obj/machinery/button/ignition
	name = "ignition switch"
	desc = "A remote control switch for a mounted igniter."
	icon_state = "launcher"
	skin = "launcher"
	device_type = /obj/item/device/assembly/control/igniter

/obj/machinery/button/flasher
	name = "flasher button"
	desc = "A remote control switch for a mounted flasher."
	icon_state = "launcher"
	skin = "launcher"
	device_type = /obj/item/device/assembly/control/flasher

/obj/machinery/button/crematorium
	name = "crematorium igniter"
	desc = "Burn baby burn!"
	icon_state = "launcher"
	skin = "launcher"
	device_type = /obj/item/device/assembly/control/crematorium
	req_access = list()
	id = 1

/obj/item/wallframe/button
	name = "button frame"
	desc = "Used for building buttons."
	icon = 'icons/obj/apc_repair.dmi'
	icon_state = "button_frame"
	result_path = /obj/machinery/button
	materials = list(MAT_METAL=MINERAL_MATERIAL_AMOUNT)


code:
#define CAMERA_UPGRADE_XRAY 1
#define CAMERA_UPGRADE_EMP_PROOF 2
#define CAMERA_UPGRADE_MOTION 4

/obj/machinery/camera
	name = "security camera"
	desc = "It's used to monitor rooms."
	icon = 'icons/obj/monitors.dmi'
	icon_state = "camera"
	use_power = 2
	idle_power_usage = 5
	active_power_usage = 10
	layer = 5

	var/health = 50
	var/list/network = list("SS13")
	var/c_tag = null
	var/c_tag_order = 999
	var/status = 1
	anchored = 1
	var/start_active = 0 //If it ignores the random chance to start broken on round start
	var/invuln = null
	var/obj/item/device/camera_bug/bug = null
	var/obj/machinery/camera_assembly/assembly = null

	//OTHER

	var/view_range = 7
	var/short_range = 2

	var/alarm_on = 0
	var/busy = 0
	var/emped = 0  //Number of consecutive EMP's on this camera

	// Upgrades bitflag
	var/upgrades = 0

/obj/machinery/camera/New()
	..()
	assembly = new(src)
	assembly.state = 4
	cameranet.cameras += src
	cameranet.addCamera(src)
	add_to_proximity_list(src, 1) //1 was default of everything
	/* // Use this to look for cameras that have the same c_tag.
	for(var/obj/machinery/camera/C in cameranet.cameras)
		var/list/tempnetwork = C.network&src.network
		if(C != src && C.c_tag == src.c_tag && tempnetwork.len)
			world.log << "[src.c_tag] [src.x] [src.y] [src.z] conflicts with [C.c_tag] [C.x] [C.y] [C.z]"
	*/

/obj/machinery/camera/initialize()
	if(z == 1 && prob(3) && !start_active)
		toggle_cam()

/obj/machinery/camera/Move()
	remove_from_proximity_list(src, 1)
	return ..()

/obj/machinery/camera/Destroy()
	toggle_cam(null, 0) //kick anyone viewing out
	remove_from_proximity_list(src, 1)
	if(assembly)
		qdel(assembly)
		assembly = null
	if(istype(bug))
		bug.bugged_cameras -= src.c_tag
		if(bug.current == src)
			bug.current = null
		bug = null
	cameranet.removeCamera(src) //Will handle removal from the camera network and the chunks, so we don't need to worry about that
	cameranet.cameras -= src
	cameranet.removeCamera(src)
	return ..()

/obj/machinery/camera/emp_act(severity)
	if(!status)
		return
	if(!isEmpProof())
		if(prob(150/severity))
			icon_state = "[initial(icon_state)]emp"
			var/list/previous_network = network
			network = list()
			cameranet.removeCamera(src)
			stat |= EMPED
			SetLuminosity(0)
			emped = emped+1  //Increase the number of consecutive EMP's
			var/thisemp = emped //Take note of which EMP this proc is for
			spawn(900)
				if(loc) //qdel limbo
					triggerCameraAlarm() //camera alarm triggers even if multiple EMPs are in effect.
					if(emped == thisemp) //Only fix it if the camera hasn't been EMP'd again
						network = previous_network
						icon_state = initial(icon_state)
						stat &= ~EMPED
						if(can_use())
							cameranet.addCamera(src)
						emped = 0 //Resets the consecutive EMP count
						spawn(100)
							if(!qdeleted(src))
								cancelCameraAlarm()
			for(var/mob/O in mob_list)
				if (O.client && O.client.eye == src)
					O.unset_machine()
					O.reset_perspective(null)
					O << "The screen bursts into static."
			..()

/obj/machinery/camera/ex_act(severity, target)
	if(src.invuln)
		return
	else
		..()
	return

/obj/machinery/camera/proc/setViewRange(num = 7)
	src.view_range = num
	cameranet.updateVisibility(src, 0)

/obj/machinery/camera/proc/shock(mob/living/user)
	if(!istype(user))
		return
	user.electrocute_act(10, src)

/obj/machinery/camera/attack_paw(mob/living/carbon/alien/humanoid/user)
	if(!istype(user))
		return
	user.do_attack_animation(src)
	add_hiddenprint(user)
	visible_message("\The [user] slashes at [src]!")
	playsound(src.loc, 'sound/weapons/slash.ogg', 100, 1)
	health = max(0, health - 30)
	if(!health && status)
		toggle_cam(user, 0)

/obj/machinery/camera/attackby(obj/W, mob/living/user, params)
	var/msg = "You attach [W] into the assembly's inner circuits."
	var/msg2 = "[src] already has that upgrade!"

	// DECONSTRUCTION
	if(istype(W, /obj/item/weapon/screwdriver))
		panel_open = !panel_open
		user << "You screw the camera's panel [panel_open ? "open" : "closed"]."
		playsound(src.loc, 'sound/items/Screwdriver.ogg', 50, 1)
		return

	if(panel_open)
		if(istype(W, /obj/item/weapon/wirecutters)) //enable/disable the camera
			toggle_cam(user, 1)
			health = initial(health) //this is a pretty simplistic way to heal the camera, but there's no reason for this to be complex.

		else if(istype(W, /obj/item/device/multitool)) //change focus
			setViewRange((view_range == initial(view_range)) ? short_range : initial(view_range))
			user << "You [(view_range == initial(view_range)) ? "restore" : "mess up"] the camera's focus."

		else if(istype(W, /obj/item/weapon/weldingtool))
			if(weld(W, user))
				visible_message("[user] unwelds [src], leaving it as just a frame screwed to the wall.", "You unweld [src], leaving it as just a frame screwed to the wall")
				if(!assembly)
					assembly = new()
				assembly.loc = src.loc
				assembly.state = 1
				assembly.dir = src.dir
				assembly = null
				qdel(src)
				return

		else if(istype(W, /obj/item/device/analyzer))
			if(!isXRay())
				upgradeXRay()
				qdel(W)
				user << "[msg]"
			else
				user << "[msg2]"

		else if(istype(W, /obj/item/stack/sheet/mineral/plasma))
			if(!isEmpProof())
				upgradeEmpProof()
				user << "[msg]"
				qdel(W)
			else
				user << "[msg2]"
		else if(istype(W, /obj/item/device/assembly/prox_sensor))
			if(!isMotion())
				upgradeMotion()
				user << "[msg]"
				qdel(W)
			else
				user << "[msg2]"

	// OTHER
	if((istype(W, /obj/item/weapon/paper) || istype(W, /obj/item/device/pda)) && isliving(user))
		var/mob/living/U = user
		var/obj/item/weapon/paper/X = null
		var/obj/item/device/pda/P = null

		var/itemname = ""
		var/info = ""
		if(istype(W, /obj/item/weapon/paper))
			X = W
			itemname = X.name
			info = X.info
		else
			P = W
			itemname = P.name
			info = P.notehtml
		U << "You hold \the [itemname] up to the camera..."
		U.changeNext_move(CLICK_CD_MELEE)
		for(var/mob/O in player_list)
			if(istype(O, /mob/living/silicon/ai))
				var/mob/living/silicon/ai/AI = O
				if(AI.control_disabled || (AI.stat == DEAD))
					return
				if(U.name == "Unknown")
					AI << "[U] holds \a [itemname] up to one of your cameras ..."
				else
					AI << "[U] holds \a [itemname] up to one of your cameras ..."
				AI.last_paper_seen = "<TITLE>[itemname]</TITLE><TT>[info]</TT>"
			else if (O.client && O.client.eye == src)
				O << "[U] holds \a [itemname] up to one of the cameras ..."
				O << browse(text("<TITLE>[]</TITLE><TT>[]</TT>", itemname, info), text("window=[]", itemname))

	else if (istype(W, /obj/item/device/camera_bug))
		if (!src.can_use())
			user << "Camera non-functional."
			return
		if(istype(src.bug))
			user << "Camera bug removed."
			src.bug.bugged_cameras -= src.c_tag
			src.bug = null
		else
			user << "Camera bugged."
			src.bug = W
			src.bug.bugged_cameras[src.c_tag] = src

	else if(istype(W, /obj/item/device/laser_pointer))
		var/obj/item/device/laser_pointer/L = W
		L.laser_act(src, user)

	else
		if(W.force >= 10) //fairly simplistic, but will do for now.
			user.changeNext_move(CLICK_CD_MELEE)
			visible_message("[user] hits [src] with [W]!", "You hit [src] with [W]!")
			health = max(0, health - W.force)
			user.do_attack_animation(src)
			if(!health && status)
				triggerCameraAlarm()
				toggle_cam(user, 1)
	return

/obj/machinery/camera/proc/toggle_cam(mob/user, displaymessage = 1)
	status = !status
	if(can_use())
		cameranet.addCamera(src)
	else
		SetLuminosity(0)
		cameranet.removeCamera(src)
	cameranet.updateChunk(x, y, z)
	var/change_msg = "deactivates"
	if(!status)
		icon_state = "[initial(icon_state)]1"
	else
		icon_state = initial(icon_state)
		change_msg = "reactivates"
		triggerCameraAlarm()
		spawn(100)
			if(!qdeleted(src))
				cancelCameraAlarm()
	if(displaymessage)
		if(user)
			visible_message("[user] [change_msg] [src]!")
			add_hiddenprint(user)
		else
			visible_message("\The [src] [change_msg]!")

		playsound(src.loc, 'sound/items/Wirecutter.ogg', 100, 1)

	// now disconnect anyone using the camera
	//Apparently, this will disconnect anyone even if the camera was re-activated.
	//I guess that doesn't matter since they can't use it anyway?
	for(var/mob/O in player_list)
		if (O.client && O.client.eye == src)
			O.unset_machine()
			O.reset_perspective(null)
			O << "The screen bursts into static."

/obj/machinery/camera/proc/triggerCameraAlarm()
	alarm_on = 1
	for(var/mob/living/silicon/S in mob_list)
		S.triggerAlarm("Camera", get_area(src), list(src), src)

/obj/machinery/camera/proc/cancelCameraAlarm()
	alarm_on = 0
	for(var/mob/living/silicon/S in mob_list)
		S.cancelAlarm("Camera", get_area(src), src)

/obj/machinery/camera/proc/can_use()
	if(!status)
		return 0
	if(stat & EMPED)
		return 0
	return 1

/obj/machinery/camera/proc/can_see()
	var/list/see = null
	var/turf/pos = get_turf(src)
	if(isXRay())
		see = range(view_range, pos)
	else
		see = get_hear(view_range, pos)
	return see

/atom/proc/auto_turn()
	//Automatically turns based on nearby walls.
	var/turf/closed/wall/T = null
	for(var/i = 1, i <= 8; i += i)
		T = get_ranged_target_turf(src, i, 1)
		if(istype(T))
			//If someone knows a better way to do this, let me know. -Giacom
			switch(i)
				if(NORTH)
					src.dir = SOUTH
				if(SOUTH)
					src.dir = NORTH
				if(WEST)
					src.dir = EAST
				if(EAST)
					src.dir = WEST
			break

//Return a working camera that can see a given mob
//or null if none
/proc/seen_by_camera(var/mob/M)
	for(var/obj/machinery/camera/C in oview(4, M))
		if(C.can_use())	// check if camera disabled
			return C
			break
	return null

/proc/near_range_camera(var/mob/M)
	for(var/obj/machinery/camera/C in range(4, M))
		if(C.can_use())	// check if camera disabled
			return C
			break

	return null

/obj/machinery/camera/proc/weld(obj/item/weapon/weldingtool/WT, mob/living/user)
	if(busy)
		return 0
	if(!WT.remove_fuel(0, user))
		return 0

	user << "You start to weld [src]..."
	playsound(src.loc, 'sound/items/Welder.ogg', 50, 1)
	busy = 1
	if(do_after(user, 100, target = src))
		busy = 0
		if(!WT.isOn())
			return 0
		return 1
	busy = 0
	return 0

/obj/machinery/camera/proc/Togglelight(on=0)
	for(var/mob/living/silicon/ai/A in ai_list)
		for(var/obj/machinery/camera/cam in A.lit_cameras)
			if(cam == src)
				return
	if(on)
		src.SetLuminosity(AI_CAMERA_LUMINOSITY)
	else
		src.SetLuminosity(0)

/obj/machinery/camera/bullet_act(obj/item/projectile/proj)
	if(proj.damage_type == BRUTE)
		health = max(0, health - proj.damage)
		if(!health && status)
			triggerCameraAlarm()
			toggle_cam(null, 1)

/obj/machinery/camera/portable //Cameras which are placed inside of things, such as helmets.
	var/turf/prev_turf

/obj/machinery/camera/portable/New()
	..()
	assembly.state = 0 //These cameras are portable, and so shall be in the portable state if removed.
	assembly.anchored = 0
	assembly.update_icon()

/obj/machinery/camera/portable/process() //Updates whenever the camera is moved.
	if(cameranet && get_turf(src) != prev_turf)
		cameranet.updatePortableCamera(src)
		prev_turf = get_turf(src)

/obj/machinery/camera/get_remote_view_fullscreens(mob/user)
	if(view_range == short_range) //unfocused
		user.overlay_fullscreen("remote_view", /obj/screen/fullscreen/impaired, 2)

/obj/machinery/camera/update_remote_sight(mob/living/user)
	user.see_invisible = SEE_INVISIBLE_LIVING //can't see ghosts through cameras
	if(isXRay())
		user.sight |= (SEE_TURFS|SEE_MOBS|SEE_OBJS)
		user.see_in_dark = max(user.see_in_dark, 8)
	else
		user.sight = 0
		user.see_in_dark = 2
	return 1


code:
/obj/item/wallframe/camera
	name = "camera assembly"
	desc = "The basic construction for Nanotrasen-Always-Watching-You cameras."
	icon = 'icons/obj/monitors.dmi'
	icon_state = "cameracase"
	materials = list(MAT_METAL=400, MAT_GLASS=250)
	result_path = /obj/machinery/camera_assembly

/obj/machinery/camera_assembly
	name = "camera assembly"
	desc = "The basic construction for Nanotrasen-Always-Watching-You cameras."
	icon = 'icons/obj/monitors.dmi'
	icon_state = "camera1"
	//	Motion, EMP-Proof, X-Ray
	var/list/obj/item/possible_upgrades = list(/obj/item/device/assembly/prox_sensor, /obj/item/stack/sheet/mineral/plasma, /obj/item/device/analyzer)
	var/list/upgrades = list()
	var/state = 1
	var/busy = 0
	/*
			1 = Wrenched in place
			2 = Welded in place
			3 = Wires attached to it (you can now attach/dettach upgrades)
			4 = Screwdriver panel closed and is fully built (you cannot attach upgrades)
	*/

/obj/machinery/camera_assembly/New(loc, ndir, building)
	..()
	if(building)
		dir = ndir

/obj/machinery/camera_assembly/attackby(obj/item/W, mob/living/user, params)
	switch(state)
		if(1)
			// State 1
			if(istype(W, /obj/item/weapon/weldingtool))
				if(weld(W, user))
					user << "You weld the assembly securely into place."
					anchored = 1
					state = 2
				return

			else if(istype(W, /obj/item/weapon/wrench))
				playsound(src.loc, 'sound/items/Ratchet.ogg', 50, 1)
				user << "You unattach the assembly from its place."
				new /obj/item/wallframe/camera(get_turf(src))
				qdel(src)
				return

		if(2)
			// State 2
			if(istype(W, /obj/item/stack/cable_coil))
				var/obj/item/stack/cable_coil/C = W
				if(C.use(2))
					user << "You add wires to the assembly."
					state = 3
				else
					user << "You need two lengths of cable to wire a camera!"
					return
				return

			else if(istype(W, /obj/item/weapon/weldingtool))

				if(weld(W, user))
					user << "You unweld the assembly from its place."
					state = 1
					anchored = 1
				return

		if(3)
			// State 3
			if(istype(W, /obj/item/weapon/screwdriver))
				playsound(src.loc, 'sound/items/Screwdriver.ogg', 50, 1)

				var/input = stripped_input(usr, "Which networks would you like to connect this camera to? Seperate networks with a comma. No Spaces!\nFor example: SS13,Security,Secret ", "Set Network", "SS13")
				if(!input)
					usr << "No input found, please hang up and try your call again!"
					return

				var/list/tempnetwork = splittext(input, ",")
				if(tempnetwork.len < 1)
					usr << "No network found, please hang up and try your call again!"
					return

				state = 4
				var/obj/machinery/camera/C = new(src.loc)
				src.loc = C
				C.assembly = src
				C.dir = src.dir

				C.network = tempnetwork
				var/area/A = get_area_master(src)
				C.c_tag = "[A.name] ([rand(1, 999)])"

			else if(istype(W, /obj/item/weapon/wirecutters))
				new/obj/item/stack/cable_coil(get_turf(src), 2)
				playsound(src.loc, 'sound/items/Wirecutter.ogg', 50, 1)
				user << "You cut the wires from the circuits."
				state = 2
				return

	// Upgrades!
	if(is_type_in_list(W, possible_upgrades) && !is_type_in_list(W, upgrades)) // Is a possible upgrade and isn't in the camera already.
		if(!user.unEquip(W))
			return
		user << "You attach \the [W] into the assembly inner circuits."
		upgrades += W
		W.loc = src
		return

	// Taking out upgrades
	else if(istype(W, /obj/item/weapon/crowbar) && upgrades.len)
		var/obj/U = locate(/obj) in upgrades
		if(U)
			user << "You unattach an upgrade from the assembly."
			playsound(src.loc, 'sound/items/Crowbar.ogg', 50, 1)
			U.loc = get_turf(src)
			upgrades -= U
		return

	..()

/obj/machinery/camera_assembly/proc/weld(obj/item/weapon/weldingtool/WT, mob/living/user)
	if(busy)
		return 0
	if(!WT.remove_fuel(0, user))
		return 0

	user << "You start to weld \the [src]..."
	playsound(src.loc, 'sound/items/Welder.ogg', 50, 1)
	busy = 1
	if(do_after(user, 20, target = src))
		busy = 0
		if(!WT.isOn())
			return 0
		return 1
	busy = 0
	return 0


code:
/obj/machinery/camera

	var/list/motionTargets = list()
	var/detectTime = 0
	var/area/ai_monitored/area_motion = null
	var/alarm_delay = 30 // Don't forget, there's another 3 seconds in queueAlarm()

/obj/machinery/camera/process()
	// motion camera event loop
	if(!isMotion())
		. = PROCESS_KILL
		return
	if (detectTime > 0)
		var/elapsed = world.time - detectTime
		if (elapsed > alarm_delay)
			triggerAlarm()
	else if (detectTime == -1)
		for (var/mob/target in motionTargets)
			if (target.stat == 2) lostTarget(target)
			// If not detecting with motion camera...
			if (!area_motion)
				// See if the camera is still in range
				if(!in_range(src, target))
					// If they aren't in range, lose the target.
					lostTarget(target)

/obj/machinery/camera/proc/newTarget(mob/target)
	if (istype(target, /mob/living/silicon/ai)) return 0
	if (detectTime == 0)
		detectTime = world.time // start the clock
	if (!(target in motionTargets))
		motionTargets += target
	return 1

/obj/machinery/camera/proc/lostTarget(mob/target)
	if (target in motionTargets)
		motionTargets -= target
	if (motionTargets.len == 0)
		cancelAlarm()

/obj/machinery/camera/proc/cancelAlarm()
	if (detectTime == -1)
		for (var/mob/living/silicon/aiPlayer in player_list)
			if (status)
				aiPlayer.cancelAlarm("Motion", get_area(src), src)
	detectTime = 0
	return 1

/obj/machinery/camera/proc/triggerAlarm()
	if (!detectTime) return 0
	for (var/mob/living/silicon/aiPlayer in player_list)
		if (status)
			aiPlayer.triggerAlarm("Motion", get_area(src), list(src), src)
	detectTime = -1
	return 1

/obj/machinery/camera/HasProximity(atom/movable/AM as mob|obj)
	// Motion cameras outside of an "ai monitored" area will use this to detect stuff.
	if (!area_motion)
		if(isliving(AM))
			newTarget(AM)


code:
// PRESETS

// EMP
/obj/machinery/camera/emp_proof
	start_active = 1

/obj/machinery/camera/emp_proof/New()
	..()
	upgradeEmpProof()

// X-RAY

/obj/machinery/camera/xray
	start_active = 1
	icon_state = "xraycam" // Thanks to Krutchen for the icons.

/obj/machinery/camera/xray/New()
	..()
	upgradeXRay()

// MOTION
/obj/machinery/camera/motion
	start_active = 1
	name = "motion-sensitive security camera"

/obj/machinery/camera/motion/New()
	..()
	upgradeMotion()

// ALL UPGRADES
/obj/machinery/camera/all
	start_active = 1

/obj/machinery/camera/all/New()
	..()
	upgradeEmpProof()
	upgradeXRay()
	upgradeMotion()

// AUTONAME

/obj/machinery/camera/autoname
	var/number = 0 //camera number in area

//This camera type automatically sets it's name to whatever the area that it's in is called.
/obj/machinery/camera/autoname/New()
	..()
	spawn(10)
		number = 1
		var/area/A = get_area(src)
		if(A)
			for(var/obj/machinery/camera/autoname/C in machines)
				if(C == src) continue
				var/area/CA = get_area(C)
				if(CA.type == A.type)
					if(C.number)
						number = max(number, C.number+1)
			c_tag = "[A.name] #[number]"

// CHECKS

/obj/machinery/camera/proc/isEmpProof()
	return upgrades & CAMERA_UPGRADE_EMP_PROOF

/obj/machinery/camera/proc/isXRay()
	return upgrades & CAMERA_UPGRADE_XRAY

/obj/machinery/camera/proc/isMotion()
	return upgrades & CAMERA_UPGRADE_MOTION

// UPGRADE PROCS

/obj/machinery/camera/proc/upgradeEmpProof()
	assembly.upgrades.Add(new /obj/item/stack/sheet/mineral/plasma(assembly))
	upgrades |= CAMERA_UPGRADE_EMP_PROOF

/obj/machinery/camera/proc/upgradeXRay()
	assembly.upgrades.Add(new /obj/item/device/analyzer(assembly))
	upgrades |= CAMERA_UPGRADE_XRAY

// If you are upgrading Motion, and it isn't in the camera's New(), add it to the machines list.
/obj/machinery/camera/proc/upgradeMotion()
	assembly.upgrades.Add(new /obj/item/device/assembly/prox_sensor(assembly))
	upgrades |= CAMERA_UPGRADE_MOTION


code:
/mob/living/silicon/ai/proc/get_camera_list()

	track.cameras.Cut()

	if(src.stat == 2)
		return

	var/list/L = list()
	for (var/obj/machinery/camera/C in cameranet.cameras)
		L.Add(C)

	camera_sort(L)

	var/list/T = list()

	for (var/obj/machinery/camera/C in L)
		var/list/tempnetwork = C.network&src.network
		if (tempnetwork.len)
			T[text("[][]", C.c_tag, (C.can_use() ? null : " (Deactivated)"))] = C

	track.cameras = T
	return T

/mob/living/silicon/ai/proc/ai_camera_list(camera)
	if (!camera)
		return 0

	var/obj/machinery/camera/C = track.cameras[camera]
	src.eyeobj.setLoc(C)

	return

/datum/trackable
	var/list/names = list()
	var/list/namecounts = list()
	var/list/humans = list()
	var/list/others = list()
	var/list/cameras = list()

/mob/living/silicon/ai/proc/trackable_mobs()

	track.names.Cut()
	track.namecounts.Cut()
	track.humans.Cut()
	track.others.Cut()

	if(usr.stat == 2)
		return list()

	for(var/mob/living/M in mob_list)
		if(!M.can_track(usr))
			continue

		// Human check
		var/human = 0
		if(istype(M, /mob/living/carbon/human))
			human = 1

		var/name = M.name
		if (name in track.names)
			track.namecounts[name]++
			name = text("[] ([])", name, track.namecounts[name])
		else
			track.names.Add(name)
			track.namecounts[name] = 1
		if(human)
			track.humans[name] = M
		else
			track.others[name] = M

	var/list/targets = sortList(track.humans) + sortList(track.others)

	return targets

/mob/living/silicon/ai/verb/ai_camera_track(target_name in trackable_mobs())
	set name = "track"
	set hidden = 1 //Don't display it on the verb lists. This verb exists purely so you can type "track Oldman Robustin" and follow his ass

	if(!target_name)
		return

	var/mob/target = (isnull(track.humans[target_name]) ? track.others[target_name] : track.humans[target_name])

	ai_actual_track(target)

/mob/living/silicon/ai/proc/ai_actual_track(mob/living/target)
	if(!istype(target))
		return
	var/mob/living/silicon/ai/U = usr

	U.cameraFollow = target
	U.tracking = 1

	U << "Attempting to track [target.get_visible_name()]..."
	sleep(min(30, get_dist(target, U.eyeobj) / 4))
	spawn(15) //give the AI a grace period to stop moving.
		U.tracking = 0

	if(!target || !target.can_track(usr))
		U << "Target is not near any active cameras."
		U.cameraFollow = null
		return

	U << "Now tracking [target.get_visible_name()] on camera."

	var/cameraticks = 0
	spawn(0)
		while(U.cameraFollow == target)
			if(U.cameraFollow == null)
				return

			if(!target.can_track(usr))
				U.tracking = 1
				if(!cameraticks)
					U << "Target is not near any active cameras. Attempting to reacquire..."
				cameraticks++
				if(cameraticks > 9)
					U.cameraFollow = null
					U << "Unable to reacquire, cancelling track..."
					tracking = 0
					return
				else
					sleep(10)
					continue

			else
				cameraticks = 0
				U.tracking = 0

			if(U.eyeobj)
				U.eyeobj.setLoc(get_turf(target))

			else
				view_core()
				U.cameraFollow = null
				return

			sleep(10)

/proc/near_camera(mob/living/M)
	if (!isturf(M.loc))
		return 0
	if(isrobot(M))
		var/mob/living/silicon/robot/R = M
		if(!(R.camera && R.camera.can_use()) && !cameranet.checkCameraVis(M))
			return 0
	else if(!cameranet.checkCameraVis(M))
		return 0
	return 1

/obj/machinery/camera/attack_ai(mob/living/silicon/ai/user)
	if (!istype(user))
		return
	if (!src.can_use())
		return
	user.eyeobj.setLoc(get_turf(src))

/mob/living/silicon/ai/attack_ai(mob/user)
	ai_camera_list()

/proc/camera_sort(list/L)
	var/obj/machinery/camera/a
	var/obj/machinery/camera/b

	for (var/i = L.len, i > 0, i--)
		for (var/j = 1 to i - 1)
			a = L[j]
			b = L[j + 1]
			if (a.c_tag_order != b.c_tag_order)
				if (a.c_tag_order > b.c_tag_order)
					L.Swap(j, j + 1)
			else
				if (sorttext(a.c_tag, b.c_tag) < 0)
					L.Swap(j, j + 1)
	return L


code:
/obj/machinery/cell_charger
	name = "cell charger"
	desc = "It charges power cells."
	icon = 'icons/obj/power.dmi'
	icon_state = "ccharger0"
	anchored = 1
	use_power = 1
	idle_power_usage = 5
	active_power_usage = 60
	power_channel = EQUIP
	var/obj/item/weapon/stock_parts/cell/charging = null
	var/chargelevel = -1

/obj/machinery/cell_charger/proc/updateicon()
	icon_state = "ccharger[charging ? 1 : 0]"

	if(charging && !(stat & (BROKEN|NOPOWER)))
		var/newlevel = 	round(charging.percent() * 4 / 100)

		if(chargelevel != newlevel)
			chargelevel = newlevel

			overlays.Cut()
			overlays += "ccharger-o[newlevel]"

	else
		overlays.Cut()

/obj/machinery/cell_charger/examine(mob/user)
	..()
	user << "There's [charging ? "a" : "no"] cell in the charger."
	if(charging)
		user << "Current charge: [round(charging.percent(), 1)]%"

/obj/machinery/cell_charger/attackby(obj/item/weapon/W, mob/user, params)
	if(stat & BROKEN)
		return

	if(istype(W, /obj/item/weapon/stock_parts/cell) && anchored)
		if(charging)
			user << "There is already a cell in the charger!"
			return
		else
			var/area/a = loc.loc // Gets our locations location, like a dream within a dream
			if(!isarea(a))
				return
			if(a.power_equip == 0) // There's no APC in this area, don't try to cheat power!
				user << "The [name] blinks red as you try to insert the cell!"
				return
			if(!user.drop_item())
				return

			W.loc = src
			charging = W
			user.visible_message("[user] inserts a cell into the charger.", "You insert a cell into the charger.")
			chargelevel = -1
			updateicon()
	else if(istype(W, /obj/item/weapon/wrench))
		if(charging)
			user << "Remove the cell first!"
			return

		anchored = !anchored
		user << "You [anchored ? "attach" : "detach"] the cell charger [anchored ? "to" : "from"] the ground"
		playsound(src.loc, 'sound/items/Ratchet.ogg', 75, 1)

/obj/machinery/cell_charger/proc/removecell()
	charging.updateicon()
	charging = null
	chargelevel = -1
	updateicon()

/obj/machinery/cell_charger/attack_hand(mob/user)
	if(!charging)
		return

	user.put_in_hands(charging)
	charging.add_fingerprint(user)

	user.visible_message("[user] removes the cell from the charger.", "You remove the cell from the charger.")

	removecell()

/obj/machinery/cell_charger/attack_tk(mob/user)
	if(!charging)
		return

	charging.loc = loc
	user << "You telekinetically remove [charging] from [src]."

	removecell()

/obj/machinery/cell_charger/attack_ai(mob/user)
	return

/obj/machinery/cell_charger/emp_act(severity)
	if(stat & (BROKEN|NOPOWER))
		return

	if(charging)
		charging.emp_act(severity)

	..(severity)

/obj/machinery/cell_charger/process()
	if(!charging || !anchored || (stat & (BROKEN|NOPOWER)))
		return

	if(charging.percent() >= 100)
		return

	use_power(200)		//this used to use CELLRATE, but CELLRATE is fucking awful. feel free to fix this properly!
	charging.give(175)	//inefficiency.

	updateicon()


code:
//Cloning revival method.
//The pod handles the actual cloning while the computer manages the clone profiles

//Potential replacement for genetics revives or something I dunno (?)

#define CLONE_INITIAL_DAMAGE     190    //Clones in clonepods start with 190 cloneloss damage and 190 brainloss damage, thats just logical

/obj/machinery/clonepod
	anchored = 1
	name = "cloning pod"
	desc = "An electronically-lockable pod for growing organic tissue."
	density = 1
	icon = 'icons/obj/cloning.dmi'
	icon_state = "pod_0"
	req_access = list(access_genetics) //For premature unlocking.
	var/heal_level = 90 //The clone is released once its health reaches this level.
	var/locked = 0
	var/obj/machinery/computer/cloning/connected = null //So we remember the connected clone machine.
	var/mess = 0 //Need to clean out it if it's full of exploded clone.
	var/attempting = 0 //One clone attempt at a time thanks
	var/eject_wait = 0 //Don't eject them as soon as they are created fuckkk
	var/speed_coeff
	var/efficiency

/obj/machinery/clonepod/New()
	..()
	component_parts = list()
	component_parts += new /obj/item/weapon/circuitboard/clonepod(null)
	component_parts += new /obj/item/weapon/stock_parts/scanning_module(null)
	component_parts += new /obj/item/weapon/stock_parts/scanning_module(null)
	component_parts += new /obj/item/weapon/stock_parts/manipulator(null)
	component_parts += new /obj/item/weapon/stock_parts/manipulator(null)
	component_parts += new /obj/item/weapon/stock_parts/console_screen(null)
	component_parts += new /obj/item/stack/cable_coil(null, 1)
	component_parts += new /obj/item/stack/cable_coil(null, 1)
	RefreshParts()

/obj/machinery/clonepod/RefreshParts()
	speed_coeff = 0
	efficiency = 0
	for(var/obj/item/weapon/stock_parts/scanning_module/S in component_parts)
		efficiency += S.rating
	for(var/obj/item/weapon/stock_parts/manipulator/P in component_parts)
		speed_coeff += P.rating
	heal_level = (efficiency * 15) + 10
	if(heal_level > 100)
		heal_level = 100

//The return of data disks?? Just for transferring between genetics machine/cloning machine.
//TO-DO: Make the genetics machine accept them.
/obj/item/weapon/disk/data
	name = "cloning data disk"
	icon_state = "datadisk0" //Gosh I hope syndies don't mistake them for the nuke disk.
	var/list/fields = list()
	var/read_only = 0 //Well,it's still a floppy disk

//Disk stuff.
/obj/item/weapon/disk/data/New()
	..()
	icon_state = "datadisk[rand(0,6)]"
	overlays += "datadisk_gene"

/obj/item/weapon/disk/data/attack_self(mob/user)
	read_only = !read_only
	user << "You flip the write-protect tab to [src.read_only ? "protected" : "unprotected"]."

/obj/item/weapon/disk/data/examine(mob/user)
	..()
	user << "The write-protect tab is set to [src.read_only ? "protected" : "unprotected"]."

//Find a dead mob with a brain and client.
/proc/find_dead_player(find_key)
	if (isnull(find_key))
		return

	var/mob/selected = null
	for(var/mob/M in player_list)
		//Dead people only thanks!
		if ((M.stat != 2) || (!M.client))
			continue
		//They need a brain!
		if (ishuman(M) && !M.getorgan(/obj/item/organ/internal/brain))
			continue

		if (M.ckey == find_key)
			selected = M
			break
	return selected

//Health Tracker Implant

/obj/item/weapon/implant/health
	name = "health implant"
	activated = 0
	var/healthstring = ""

/obj/item/weapon/implant/health/proc/sensehealth()
	if (!src.implanted)
		return "ERROR"
	else
		if(isliving(src.implanted))
			var/mob/living/L = src.implanted
			src.healthstring = "Oxygen Deprivation Damage => [round(L.getOxyLoss())]
Fire Damage => [round(L.getFireLoss())]
Toxin Damage => [round(L.getToxLoss())]
Brute Force Damage => [round(L.getBruteLoss())]"
		if (!src.healthstring)
			src.healthstring = "ERROR"
		return src.healthstring

//Clonepod

/obj/machinery/clonepod/examine(mob/user)
	..()
	if (isnull(src.occupant) || !is_operational())
		return
	if ((!isnull(src.occupant)) && (src.occupant.stat != 2))
		var/completion = (100 * ((src.occupant.health + 100) / (src.heal_level + 100)))
		user << "Current clone cycle is [round(completion)]% complete."
	return

/obj/machinery/clonepod/attack_ai(mob/user)
	return examine(user)

//Start growing a human clone in the pod!
/obj/machinery/clonepod/proc/growclone(ckey, clonename, ui, se, mindref, datum/species/mrace, list/features, factions)
	if(panel_open)
		return 0
	if(mess || attempting)
		return 0
	var/datum/mind/clonemind = locate(mindref)
	if(!istype(clonemind))	//not a mind
		return 0
	if( clonemind.current && clonemind.current.stat != DEAD )	//mind is associated with a non-dead body
		return 0
	if(clonemind.active)	//somebody is using that mind
		if( ckey(clonemind.key)!=ckey )
			return 0
	else
		for(var/mob/M in player_list)
			if(M.ckey == ckey)
				if(istype(M, /mob/dead/observer))
					var/mob/dead/observer/G = M
					if(G.can_reenter_corpse)
						break
				return 0

	src.attempting = 1 //One at a time!!
	src.locked = 1

	src.eject_wait = 1
	spawn(30)
		src.eject_wait = 0

	var/mob/living/carbon/human/H = new /mob/living/carbon/human(src)

	if(efficiency > 2)
		for(var/A in bad_se_blocks)
			setblock(H.dna.struc_enzymes, A, construct_block(0,2))
	if(efficiency > 5 && prob(20))
		randmutg(H)
	if(efficiency < 3 && prob(50))
		var/mob/M = randmutb(H)
		if(ismob(M))
			H = M

	H.silent = 20 //Prevents an extreme edge case where clones could speak if they said something at exactly the right moment.
	occupant = H

	if(!clonename)	//to prevent null names
		clonename = "clone ([rand(0,999)])"
	H.real_name = clonename

	src.icon_state = "pod_1"
	//Get the clone body ready
	H.adjustCloneLoss(CLONE_INITIAL_DAMAGE)     //Yeah, clones start with very low health, not with random, because why would they start with random health
	H.adjustBrainLoss(CLONE_INITIAL_DAMAGE)
	H.Paralyse(4)

	clonemind.transfer_to(H)
	H.ckey = ckey
	H << "Consciousness slowly creeps over you as your body regenerates.
So this is what cloning feels like?"

	H.hardset_dna(ui, se, H.real_name, null, mrace, features)
	if(H)
		H.faction |= factions

		H.set_cloned_appearance()

		H.suiciding = 0
	src.attempting = 0
	return 1

//Grow clones to maturity then kick them out.  FREELOADERS
/obj/machinery/clonepod/process()

	if(!is_operational()) //Autoeject if power is lost
		if (src.occupant)
			src.locked = 0
			src.go_out()
		return

	if((src.occupant) && (src.occupant.loc == src))
		if((src.occupant.stat == DEAD) || (src.occupant.suiciding) || !occupant.key)  //Autoeject corpses and suiciding dudes.
			src.locked = 0
			src.go_out()
			src.connected_message("Clone Rejected: Deceased.")
			return

		else if(src.occupant.cloneloss > (100 - src.heal_level))
			src.occupant.Paralyse(4)

			 //Slowly get that clone healed and finished.
			src.occupant.adjustCloneLoss(-((speed_coeff/2)))

			//Premature clones may have brain damage.
			src.occupant.adjustBrainLoss(-((speed_coeff/2)))

			//So clones don't die of oxyloss in a running pod.
			if (src.occupant.reagents.get_reagent_amount("salbutamol") < 30)
				src.occupant.reagents.add_reagent("salbutamol", 60)

			use_power(7500) //This might need tweaking.
			return

		else if((src.occupant.cloneloss <= (100 - src.heal_level)) && (!src.eject_wait))
			src.connected_message("Cloning Process Complete.")
			src.locked = 0
			src.go_out()
			return

	else if ((!src.occupant) || (src.occupant.loc != src))
		src.occupant = null
		if (src.locked)
			src.locked = 0
		if (!src.mess && !panel_open)
			icon_state = "pod_0"
		use_power(200)
		return

	return

//Let's unlock this early I guess.  Might be too early, needs tweaking.
/obj/machinery/clonepod/attackby(obj/item/weapon/W, mob/user, params)
	if(!(occupant || mess || locked))
		if(default_deconstruction_screwdriver(user, "[icon_state]_maintenance", "[initial(icon_state)]",W))
			return

	if(exchange_parts(user, W))
		return

	default_deconstruction_crowbar(W)

	if (istype(W, /obj/item/weapon/card/id)||istype(W, /obj/item/device/pda))
		if (!src.check_access(W))
			user << "Access Denied."
			return
		if ((!src.locked) || (isnull(src.occupant)))
			return
		if ((src.occupant.health < -20) && (src.occupant.stat != 2))
			user << "Access Refused."
			return
		else
			src.locked = 0
			user << "System unlocked."
	else
		..()

/obj/machinery/clonepod/emag_act(mob/user)
	if (isnull(src.occupant))
		return
	user << "You force an emergency ejection."
	src.locked = 0
	src.go_out()

//Put messages in the connected computer's temp var for display.
/obj/machinery/clonepod/proc/connected_message(message)
	if ((isnull(src.connected)) || (!istype(src.connected, /obj/machinery/computer/cloning)))
		return 0
	if (!message)
		return 0

	src.connected.temp = message
	src.connected.updateUsrDialog()
	return 1

/obj/machinery/clonepod/verb/eject()
	set name = "Eject Cloner"
	set category = "Object"
	set src in oview(1)

	if(!usr)
		return
	if(usr.stat || !usr.canmove || usr.restrained())
		return
	src.go_out()
	add_fingerprint(usr)
	return

/obj/machinery/clonepod/proc/go_out()
	if (locked)
		return

	if (mess) //Clean that mess and dump those gibs!
		mess = 0
		gibs(loc)
		icon_state = "pod_0"
		return

	if (!occupant)
		return

	var/turf/T = get_turf(src)
	occupant.forceMove(T)
	icon_state = "pod_0"
	eject_wait = 0 //If it's still set somehow.
	occupant.domutcheck() //Waiting until they're out before possible monkeyizing.
	occupant = null
	return

/obj/machinery/clonepod/proc/malfunction()
	if(src.occupant)
		src.connected_message("Critical Error!")
		src.mess = 1
		src.icon_state = "pod_g"
		src.occupant.ghostize()
		spawn(5)
			qdel(src.occupant)
	return

/obj/machinery/clonepod/relaymove(mob/user)
	if (user.stat)
		return
	src.go_out()
	return

/obj/machinery/clonepod/emp_act(severity)
	if(prob(100/(severity*efficiency))) malfunction()
	..()

/obj/machinery/clonepod/ex_act(severity, target)
	..()
	if(!qdeleted(src))
		go_out()

/*
 *	Diskette Box
 */

/obj/item/weapon/storage/box/disks
	name = "diskette box"
	icon_state = "disk_kit"

/obj/item/weapon/storage/box/disks/New()
	..()
	for(var/i in 1 to 7)
		new /obj/item/weapon/disk/data(src)

/*
 *	Manual -- A big ol' manual.
 */

/obj/item/weapon/paper/Cloning
	name = "paper - 'H-87 Cloning Apparatus Manual"
	info = {"
Getting Started

	Congratulations, your station has purchased the H-87 industrial cloning device!

	Using the H-87 is almost as simple as brain surgery! Simply insert the target humanoid into the scanning chamber and select the scan option to create a new profile!

	That's all there is to it!

	Notice, cloning system cannot scan inorganic life or small primates.  Scan may fail if subject has suffered extreme brain damage.

Clone profiles may be viewed through the profiles menu. Scanning implants a complementary HEALTH MONITOR IMPLANT into the subject, which may be viewed from each profile.
	Profile Deletion has been restricted to \[Station Head\] level access.

Cloning from a profile

	Cloning is as simple as pressing the CLONE option at the bottom of the desired profile.

	Per your company's EMPLOYEE PRIVACY RIGHTS agreement, the H-87 has been blocked from cloning crewmembers while they are still alive.

The provided CLONEPOD SYSTEM will produce the desired clone.  Standard clone maturation times (With SPEEDCLONE technology) are roughly 90 seconds.
	The cloning pod may be unlocked early with any \[Medical Researcher\] ID after initial maturation is complete.

	Please note that resulting clones may have a small DEVELOPMENTAL DEFECT as a result of genetic drift.

Profile Management

The H-87 (as well as your station's standard genetics machine) can accept STANDARD DATA DISKETTES.
	These diskettes are used to transfer genetic information between machines and profiles.
	A load/save dialog will become available in each profile if a disk is inserted.

	A good diskette is a great way to counter aforementioned genetic drift!

	This technology produced under license from Thinktronic Systems, LTD."}

//SOME SCRAPS I GUESS
/* EMP grenade/spell effect
		if(istype(A, /obj/machinery/clonepod))
			A:malfunction()
*/

#undef CLONE_INITIAL_DAMAGE


code:
/obj/machinery/computer/operating
	name = "operating computer"
	desc = "Used to monitor the vitals of a patient during surgery."
	icon_screen = "crew"
	icon_keyboard = "med_key"
	circuit = /obj/item/weapon/circuitboard/operating
	var/mob/living/carbon/human/patient = null
	var/obj/structure/table/optable/table = null

/obj/machinery/computer/operating/New()
	..()
	if(ticker)
		find_table()

/obj/machinery/computer/operating/initialize()
	find_table()

/obj/machinery/computer/operating/proc/find_table()
	for(var/dir in cardinal)
		table = locate(/obj/structure/table/optable, get_step(src, dir))
		if(table)
			table.computer = src
			break

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

/obj/machinery/computer/operating/interact(mob/user)
	var/dat = ""
	if(table)
		dat += "Patient information:
"
		if(table.check_patient())
			patient = table.patient
			dat += get_patient_info()
		else
			patient = null
			dat += "No patient detected"
	else
		dat += "Operating table not found."

	var/datum/browser/popup = new(user, "op", "Operating Computer", 400, 500)
	popup.set_content(dat)
	popup.open()

/obj/machinery/computer/operating/proc/get_patient_info()
	var/dat = {"

Patient:
 [patient.stat ? "Non-Responsive" : "Stable"]

Blood Type:
 [patient.dna.blood_type]

Health:

[patient.health]%

\> Brute Damage:

[patient.getBruteLoss()]%

\> Resp. Damage:

[patient.getOxyLoss()]%

\> Toxin Content:

[patient.getToxLoss()]%

\> Burn Severity:

[patient.getFireLoss()]%

				"}
	if(patient.surgeries.len)
		dat += "

Initiated Procedures
"
		for(var/datum/surgery/procedure in patient.surgeries)
			dat += "[capitalize(procedure.name)]
"
			var/datum/surgery_step/surgery_step = procedure.get_surgery_step()
			dat += "Next step: [capitalize(surgery_step.name)]
"
		dat += "
"
	return dat


code:
/obj/machinery/computer/aifixer
	name = "\improper AI system integrity restorer"
	desc = "Used with intelliCards containing nonfunctioning AIs to restore them to working order."
	req_access = list(access_captain, access_robotics, access_heads)
	var/mob/living/silicon/ai/occupier = null
	var/active = 0
	circuit = /obj/item/weapon/circuitboard/aifixer
	icon_keyboard = "tech_key"
	icon_screen = "ai-fixer"

/obj/machinery/computer/aifixer/attackby(obj/I, mob/user, params)
	if(occupier && istype(I, /obj/item/weapon/screwdriver))
		if(stat & (NOPOWER|BROKEN))
			user << "The screws on [name]'s screen won't budge."
		else
			user << "The screws on [name]'s screen won't budge and it emits a warning beep."
		return
	else
		..()

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

/obj/machinery/computer/aifixer/interact(mob/user)

	var/dat = ""

	if (src.occupier)
		var/laws
		dat += "
Stored AI: [src.occupier.name]
"
		dat += "System integrity: [(src.occupier.health+100)/2]%
"

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

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

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

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

		dat += "Laws:
[laws]
"

		if (src.occupier.stat == 2)
			dat += "AI non-functional"
		else
			dat += "AI functional"
		if (!src.active)
			dat += {"

Begin Reconstruction"}
		else
			dat += "

Reconstruction in process, please wait.
"
	dat += {"
Close"}

	//user << browse(dat, "window=computer;size=400x500")
	//onclose(user, "computer")
	var/datum/browser/popup = new(user, "computer", "AI System Integrity Restorer", 400, 500)
	popup.set_content(dat)
	popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state))
	popup.open()
	return

/obj/machinery/computer/aifixer/process()
	if(..())
		src.updateDialog()
		return

/obj/machinery/computer/aifixer/Topic(href, href_list)
	if(..())
		return
	if(href_list["fix"])
		active = 1
		while (occupier.health < 100)
			occupier.adjustOxyLoss(-1, 0)
			occupier.adjustFireLoss(-1, 0)
			occupier.adjustToxLoss(-1, 0)
			occupier.adjustBruteLoss(-1, 0)
			occupier.updatehealth()
			if(occupier.health >= 0 && occupier.stat == DEAD)
				occupier.revive()
			updateUsrDialog()
			update_icon()
			sleep(10)
		active = 0
		add_fingerprint(usr)
	updateUsrDialog()
	update_icon()

/obj/machinery/computer/aifixer/update_icon()
	..()
	if(stat & (NOPOWER|BROKEN))
		return
	else
		if(active)
			overlays += "ai-fixer-on"
		if (occupier)
			switch (occupier.stat)
				if (0)
					overlays += "ai-fixer-full"
				if (2)
					overlays += "ai-fixer-404"
		else
			overlays += "ai-fixer-empty"

/obj/machinery/computer/aifixer/transfer_ai(interaction, mob/user, mob/living/silicon/ai/AI, obj/item/device/aicard/card)
	if(!..())
		return
	//Downloading AI from card to terminal.
	if(interaction == AI_TRANS_FROM_CARD)
		if(stat & (NOPOWER|BROKEN))
			user << "[src] is offline and cannot take an AI at this time!"
			return
		AI.forceMove(src)
		occupier = AI
		AI.control_disabled = 1
		AI.radio_enabled = 0
		AI << "You have been uploaded to a stationary terminal. Sadly, there is no remote access from here."
		user << "Transfer successful: [AI.name] ([rand(1000,9999)].exe) installed and executed successfully. Local copy has been removed."
		card.AI = null
		update_icon()

	else //Uploading AI from terminal to card
		if(occupier && !active)
			occupier << "You have been downloaded to a mobile storage device. Still no remote access."
			user << "Transfer successful: [occupier.name] ([rand(1000,9999)].exe) removed from host terminal and stored within local memory."
			occupier.loc = card
			card.AI = occupier
			occupier = null
			update_icon()
		else if (active)
			user << "ERROR: Reconstruction in progress."
		else if (!occupier)
			user << "ERROR: Unable to locate artificial intelligence."