code:
// AI EYE
//
// An invisible (no icon) mob that the AI controls to look around the station with.
// It streams chunks as it moves around, which will show it what the AI can and cannot see.
/mob/camera/ai_eye
	name = "Inactive AI Eye"

	icon_state = "ai_camera"
	icon = 'icons/mob/cameramob.dmi'
	invisibility = INVISIBILITY_MAXIMUM
	hud_possible = list(ANTAG_HUD, AI_DETECT_HUD = HUD_LIST_LIST)
	var/list/visibleCameraChunks = list()
	var/mob/living/silicon/ai/ai = null
	var/relay_speech = FALSE
	var/use_static = TRUE
	var/static_visibility_range = 16
	var/ai_detector_visible = TRUE
	var/ai_detector_color = COLOR_RED
	interaction_range = null

/mob/camera/ai_eye/Initialize(mapload)
	. = ..()
	GLOB.aiEyes += src
	update_ai_detect_hud()
	setLoc(loc, TRUE)

/mob/camera/ai_eye/examine(mob/user) //Displays a silicon's laws to ghosts
	. = ..()
	if(istype(ai) && ai.laws && isobserver(user))
		. += "<b>[ai] has the following laws:</b>"
		for(var/law in ai.laws.get_law_list(include_zeroth = TRUE))
			. += law

/mob/camera/ai_eye/proc/update_ai_detect_hud()
	var/datum/atom_hud/ai_detector/hud = GLOB.huds[DATA_HUD_AI_DETECT]
	var/list/old_images = hud_list[AI_DETECT_HUD]
	if(!ai_detector_visible)
		hud.remove_from_hud(src)
		QDEL_LIST(old_images)
		return

	if(!length(hud.hudusers))
		return //no one is watching, do not bother updating anything

	hud.remove_from_hud(src)

	var/static/list/vis_contents_opaque = list()
	var/obj/effect/overlay/ai_detect_hud/hud_obj = vis_contents_opaque[ai_detector_color]
	if(!hud_obj)
		hud_obj = new /obj/effect/overlay/ai_detect_hud()
		hud_obj.color = ai_detector_color
		vis_contents_opaque[ai_detector_color] = hud_obj

	var/list/new_images = list()
	var/list/turfs = get_visible_turfs()
	for(var/T in turfs)
		var/image/I = (old_images.len > new_images.len) ? old_images[new_images.len + 1] : image(null, T)
		I.loc = T
		I.vis_contents += hud_obj
		new_images += I
	for(var/i in (new_images.len + 1) to old_images.len)
		qdel(old_images[i])
	hud_list[AI_DETECT_HUD] = new_images
	hud.add_to_hud(src)

/mob/camera/ai_eye/proc/get_visible_turfs()
	if(!isturf(loc))
		return list()
	var/client/C = GetViewerClient()
	var/view = C ? getviewsize(C.view) : getviewsize(world.view)
	var/turf/lowerleft = locate(max(1, x - (view[1] - 1)/2), max(1, y - (view[2] - 1)/2), z)
	var/turf/upperright = locate(min(world.maxx, lowerleft.x + (view[1] - 1)), min(world.maxy, lowerleft.y + (view[2] - 1)), lowerleft.z)
	return block(lowerleft, upperright)

// Use this when setting the aiEye's location.
// It will also stream the chunk that the new loc is in.

/mob/camera/ai_eye/proc/setLoc(destination, force_update = FALSE)
	if(ai)
		if(!isturf(ai.loc))
			return
		destination = get_turf(destination)
		if(!force_update && (destination == get_turf(src)) )
			return //we are already here!
		if (destination)
			abstract_move(destination)
		else
			moveToNullspace()
		if(use_static)
			ai.camera_visibility(src)
		if(ai.client && !ai.multicam_on)
			ai.client.eye = src
		update_ai_detect_hud()
		update_parallax_contents()
		//Holopad
		if(istype(ai.current, /obj/machinery/holopad))
			var/obj/machinery/holopad/H = ai.current
			H.move_hologram(ai, destination)
		if(ai.camera_light_on)
			ai.light_cameras()
		if(ai.master_multicam)
			ai.master_multicam.refresh_view()

//it uses setLoc not forceMove, talks to the sillycone and not the camera mob
/mob/camera/ai_eye/zMove(dir, feedback = FALSE)
	if(dir != UP && dir != DOWN)
		return FALSE
	var/turf/target = get_step_multiz(src, dir)
	if(!target)
		if(feedback)
			to_chat(ai, span_warning("There's nowhere to go in that direction!"))
		return FALSE
	if(!canZMove(dir, target))
		if(feedback)
			to_chat(ai, span_warning("You couldn't move there!"))
		return FALSE
	setLoc(target, TRUE)
	return TRUE

/mob/camera/ai_eye/canZMove(direction, turf/target) //cameras do not respect these FLOORS you speak so much of
	return TRUE

/mob/camera/ai_eye/Move()
	return

/mob/camera/ai_eye/proc/GetViewerClient()
	if(ai)
		return ai.client
	return null

/mob/camera/ai_eye/Destroy()
	if(ai)
		ai.all_eyes -= src
		ai = null
	for(var/V in visibleCameraChunks)
		var/datum/camerachunk/c = V
		c.remove(src)
	GLOB.aiEyes -= src
	if(ai_detector_visible)
		var/datum/atom_hud/ai_detector/hud = GLOB.huds[DATA_HUD_AI_DETECT]
		hud.remove_from_hud(src)
		var/list/L = hud_list[AI_DETECT_HUD]
		QDEL_LIST(L)
	return ..()

/atom/proc/move_camera_by_click()
	if(isAI(usr))
		var/mob/living/silicon/ai/AI = usr
		if(AI.eyeobj && (AI.multicam_on || (AI.client.eye == AI.eyeobj)) && (AI.eyeobj.z == z))
			AI.cameraFollow = null
			if (isturf(loc) || isturf(src))
				AI.eyeobj.setLoc(src)

// This will move the AIEye. It will also cause lights near the eye to light up, if toggled.
// This is handled in the proc below this one.

/client/proc/AIMove(n, direct, mob/living/silicon/ai/user)

	var/initial = initial(user.sprint)
	var/max_sprint = 50

	if(user.cooldown && user.cooldown < world.timeofday) // 3 seconds
		user.sprint = initial

	for(var/i = 0; i < max(user.sprint, initial); i += 20)
		var/turf/step = get_turf(get_step(user.eyeobj, direct))
		if(step)
			user.eyeobj.setLoc(step)

	user.cooldown = world.timeofday + 5
	if(user.acceleration)
		user.sprint = min(user.sprint + 0.5, max_sprint)
	else
		user.sprint = initial

	if(!user.tracking)
		user.cameraFollow = null

// Return to the Core.
/mob/living/silicon/ai/proc/view_core()
	if(istype(current,/obj/machinery/holopad))
		var/obj/machinery/holopad/H = current
		H.clear_holo(src)
	else
		current = null
	cameraFollow = null
	unset_machine()

	if(isturf(loc) && (QDELETED(eyeobj) || !eyeobj.loc))
		to_chat(src, "ERROR: Eyeobj not found. Creating new eye...")
		create_eye()

	eyeobj?.setLoc(loc)

/mob/living/silicon/ai/proc/create_eye()
	if(eyeobj)
		return
	eyeobj = new /mob/camera/ai_eye()
	all_eyes += eyeobj
	eyeobj.ai = src
	eyeobj.setLoc(loc)
	eyeobj.name = "[name] (AI Eye)"
	eyeobj.real_name = eyeobj.name
	set_eyeobj_visible(TRUE)

/mob/living/silicon/ai/proc/set_eyeobj_visible(state = TRUE)
	if(!eyeobj)
		return
	eyeobj.mouse_opacity = state ? MOUSE_OPACITY_ICON : initial(eyeobj.mouse_opacity)
	eyeobj.invisibility = state ? INVISIBILITY_OBSERVER : initial(eyeobj.invisibility)

/mob/living/silicon/ai/verb/toggle_acceleration()
	set category = "AI Commands"
	set name = "Toggle Camera Acceleration"

	if(incapacitated())
		return
	acceleration = !acceleration
	to_chat(usr, "Camera acceleration has been toggled [acceleration ? "on" : "off"].")

/mob/camera/ai_eye/Hear(message, atom/movable/speaker, datum/language/message_language, raw_message, radio_freq, list/spans, list/message_mods = list())
	. = ..()
	if(relay_speech && speaker && ai && !radio_freq && speaker != ai && near_camera(speaker))
		ai.relay_speech(message, speaker, message_language, raw_message, radio_freq, spans, message_mods)

/obj/effect/overlay/ai_detect_hud
	name = ""
	mouse_opacity = MOUSE_OPACITY_TRANSPARENT
	icon = 'icons/effects/alphacolors.dmi'
	icon_state = ""
	alpha = 100
	layer = ABOVE_ALL_MOB_LAYER
	/* SEPTIC EDIT REMOVAL
	plane = GAME_PLANE
	*/
	//SEPTIC EDIT BEGIN
	plane = MOB_PLANE
	//SEPTIC EDIT END


code:
/mob/living/silicon/ai/proc/show_laws_verb()
	set category = "AI Commands"
	set name = "Show Laws"
	if(usr.stat == DEAD)
		return //won't work if dead
	src.show_laws()

/mob/living/silicon/ai/show_laws(everyone = 0)
	var/who

	if (everyone)
		who = world
	else
		who = src
	to_chat(who, "<b>Obey these laws:</b>")

	src.laws_sanity_check()
	src.laws.show_laws(who)
	if(!everyone)
		for(var/mob/living/silicon/robot/R in connected_robots)
			if(R.lawupdate)
				R.lawsync()
				R.show_laws()
				R.law_change_counter++

repo_name: AnalWerewolf/sundown

license: mit


code:
/mob/living/silicon/ai/Life(delta_time = SSMOBS_DT, times_fired)
	if (stat == DEAD)
		return
	//Being dead doesn't mean your temperature never changes

	update_gravity(mob_has_gravity())

	handle_status_effects(delta_time, times_fired)

	handle_traits(delta_time, times_fired)

	if(malfhack?.aidisabled)
		deltimer(malfhacking)
		// This proc handles cleanup of screen notifications and
		// messenging the client
		malfhacked(malfhack)

	if(isturf(loc) && (QDELETED(eyeobj) || !eyeobj.loc))
		view_core()

	if(machine)
		machine.check_eye(src)

	// Handle power damage (oxy)
	if(aiRestorePowerRoutine)
		// Lost power
		if (!battery)
			to_chat(src, span_warning("Your backup battery's output drops below usable levels. It takes only a moment longer for your systems to fail, corrupted and unusable."))
			adjustOxyLoss(200)
		else
			battery--
	else
		// Gain Power
		if (battery < 200)
			battery++

	if(!lacks_power())
		var/area/home = get_area(src)
		if(home.powered(AREA_USAGE_EQUIP))
			home.use_power(500 * delta_time, AREA_USAGE_EQUIP)

		if(aiRestorePowerRoutine >= POWER_RESTORATION_SEARCH_APC)
			ai_restore_power()
			return

	else if(!aiRestorePowerRoutine)
		ai_lose_power()

/mob/living/silicon/ai/proc/lacks_power()
	var/turf/T = get_turf(src)
	var/area/A = get_area(src)
	switch(requires_power)
		if(NONE)
			return FALSE
		if(POWER_REQ_ALL)
			return !T || !A || ((!A.power_equip || isspaceturf(T)) && !is_type_in_list(loc, list(/obj/item, /obj/vehicle/sealed/mecha)))

/mob/living/silicon/ai/updatehealth()
	if(status_flags & GODMODE)
		return
	set_health(maxHealth - getOxyLoss() - getToxLoss() - getBruteLoss() - getFireLoss())
	update_stat()
	diag_hud_set_health()
	disconnect_shell()

/mob/living/silicon/ai/update_stat()
	if(status_flags & GODMODE)
		return
	if(stat != DEAD)
		if(health <= HEALTH_THRESHOLD_DEAD)
			death()
			return
		else if(stat >= UNCONSCIOUS)
			set_stat(CONSCIOUS)
	diag_hud_set_status()

/mob/living/silicon/ai/update_sight()
	see_invisible = initial(see_invisible)
	see_in_dark = initial(see_in_dark)
	sight = initial(sight)
	if(aiRestorePowerRoutine)
		sight = sight&~SEE_TURFS
		sight = sight&~SEE_MOBS
		sight = sight&~SEE_OBJS
		see_in_dark = 0

	if(see_override)
		see_invisible = see_override
	sync_lighting_plane_alpha()

/mob/living/silicon/ai/proc/start_RestorePowerRoutine()
	to_chat(src, span_notice("Backup battery online. Scanners, camera, and radio interface offline. Beginning fault-detection."))
	end_multicam()
	sleep(50)
	var/turf/T = get_turf(src)
	var/area/AIarea = get_area(src)
	if(AIarea?.power_equip)
		if(!isspaceturf(T))
			ai_restore_power()
			return
	to_chat(src, span_notice("Fault confirmed: missing external power. Shutting down main control system to save power."))
	sleep(20)
	to_chat(src, span_notice("Emergency control system online. Verifying connection to power network."))
	sleep(50)
	T = get_turf(src)
	if(isspaceturf(T))
		to_chat(src, span_alert("Unable to verify! No power connection detected!"))
		setAiRestorePowerRoutine(POWER_RESTORATION_SEARCH_APC)
		return
	to_chat(src, span_notice("Connection verified. Searching for APC in power network."))
	sleep(50)
	var/obj/machinery/power/apc/theAPC = null

	var/PRP //like ERP with the code, at least this stuff is no more 4x sametext
	for (PRP=1, PRP<=4, PRP++)
		T = get_turf(src)
		AIarea = get_area(src)
		if(AIarea)
			theAPC = AIarea.apc
		if (!theAPC)
			switch(PRP)
				if(1)
					to_chat(src, span_alert("Unable to locate APC!"))
				else
					to_chat(src, span_alert("Lost connection with the APC!"))
			setAiRestorePowerRoutine(POWER_RESTORATION_SEARCH_APC)
			return
		if(AIarea.power_equip)
			if(!isspaceturf(T))
				ai_restore_power()
				return
		switch(PRP)
			if (1)
				to_chat(src, span_notice("APC located. Optimizing route to APC to avoid needless power waste."))
			if (2)
				to_chat(src, span_notice("Best route identified. Hacking offline APC power port."))
			if (3)
				to_chat(src, span_notice("Power port upload access confirmed. Loading control program into APC power port software."))
			if (4)
				to_chat(src, span_notice("Transfer complete. Forcing APC to execute program."))
				sleep(50)
				to_chat(src, span_notice("Receiving control information from APC."))
				sleep(2)
				to_chat(src, "APC ready for connection.")
				apc_override = theAPC
				apc_override.ui_interact(src)
				setAiRestorePowerRoutine(POWER_RESTORATION_APC_FOUND)
		sleep(50)
		theAPC = null

/mob/living/silicon/ai/proc/ai_restore_power()
	if(aiRestorePowerRoutine)
		if(aiRestorePowerRoutine == POWER_RESTORATION_APC_FOUND)
			to_chat(src, span_notice("Alert cancelled. Power has been restored."))
			if(apc_override)
				to_chat(src, span_notice("APC backdoor has been closed.")) //Fluff for why we have to hack every time.
		else
			to_chat(src, span_notice("Alert cancelled. Power has been restored without our assistance."))
		setAiRestorePowerRoutine(POWER_RESTORATION_OFF)
		set_blindness(0)
		apc_override = null
		update_sight()

/mob/living/silicon/ai/proc/ai_lose_power()
	disconnect_shell()
	setAiRestorePowerRoutine(POWER_RESTORATION_START)
	blind_eyes(1)
	update_sight()
	to_chat(src, span_alert("You've lost power!"))
	addtimer(CALLBACK(src, .proc/start_RestorePowerRoutine), 20)


code:
/mob/living/silicon/ai/Login()
	. = ..()
	if(!. || !client)
		return FALSE
	if(stat != DEAD)
		if(lacks_power() && apc_override) //Placing this in Login() in case the AI doesn't have this link for whatever reason.
			to_chat(usr, "[span_warning("Main power is unavailable, backup power in use. Diagnostics scan complete.")] Local APC ready for connection.")
	set_eyeobj_visible(TRUE)
	if(multicam_on)
		end_multicam()
	view_core()


code:
/mob/living/silicon/ai/Logout()
	..()
	set_eyeobj_visible(FALSE)
	view_core()


code:
//Picture in picture

/atom/movable/screen/movable/pic_in_pic/ai
	var/mob/living/silicon/ai/ai
	var/mutable_appearance/highlighted_background
	var/highlighted = FALSE
	var/mob/camera/ai_eye/pic_in_pic/aiEye

/atom/movable/screen/movable/pic_in_pic/ai/Initialize(mapload)
	. = ..()
	aiEye = new /mob/camera/ai_eye/pic_in_pic()
	aiEye.screen = src

/atom/movable/screen/movable/pic_in_pic/ai/Destroy()
	set_ai(null)
	QDEL_NULL(aiEye)
	return ..()

/atom/movable/screen/movable/pic_in_pic/ai/Click()
	..()
	if(ai)
		ai.select_main_multicam_window(src)

/atom/movable/screen/movable/pic_in_pic/ai/make_backgrounds()
	..()
	highlighted_background = new /mutable_appearance()
	highlighted_background.icon = 'icons/misc/pic_in_pic.dmi'
	highlighted_background.icon_state = "background_highlight"
	highlighted_background.layer = SPACE_LAYER

/atom/movable/screen/movable/pic_in_pic/ai/add_background()
	if((width > 0) && (height > 0))
		var/matrix/M = matrix()
		M.Scale(width + 0.5, height + 0.5)
		M.Translate((width-1)/2 * world.icon_size, (height-1)/2 * world.icon_size)
		highlighted_background.transform = M
		standard_background.transform = M
		add_overlay(highlighted ? highlighted_background : standard_background)

/atom/movable/screen/movable/pic_in_pic/ai/set_view_size(width, height, do_refresh = TRUE)
	aiEye.static_visibility_range = (round(max(width, height) / 2) + 1)
	if(ai)
		ai.camera_visibility(aiEye)
	..()

/atom/movable/screen/movable/pic_in_pic/ai/set_view_center(atom/target, do_refresh = TRUE)
	..()
	aiEye.setLoc(get_turf(target))

/atom/movable/screen/movable/pic_in_pic/ai/refresh_view()
	..()
	aiEye.setLoc(get_turf(center))

/atom/movable/screen/movable/pic_in_pic/ai/proc/highlight()
	if(highlighted)
		return
	highlighted = TRUE
	cut_overlay(standard_background)
	add_overlay(highlighted_background)

/atom/movable/screen/movable/pic_in_pic/ai/proc/unhighlight()
	if(!highlighted)
		return
	highlighted = FALSE
	cut_overlay(highlighted_background)
	add_overlay(standard_background)

/atom/movable/screen/movable/pic_in_pic/ai/proc/set_ai(mob/living/silicon/ai/new_ai)
	if(ai)
		ai.multicam_screens -= src
		ai.all_eyes -= aiEye
		if(ai.master_multicam == src)
			ai.master_multicam = null
		if(ai.multicam_on)
			unshow_to(ai.client)
	ai = new_ai
	if(new_ai)
		new_ai.multicam_screens += src
		ai.all_eyes += aiEye
		if(new_ai.multicam_on)
			show_to(new_ai.client)

//Turf, area, and landmark for the viewing room

/turf/open/ai_visible
	name = ""
	icon = 'icons/misc/pic_in_pic.dmi'
	icon_state = "room_background"
	flags_1 = NOJAUNT

/area/ai_multicam_room
	name = "ai_multicam_room"
	icon_state = "ai_camera_room"
	static_lighting = FALSE

	base_lighting_alpha = 255
	area_flags = NOTELEPORT | HIDDEN_AREA | UNIQUE_AREA
	ambientsounds = null
	flags_1 = NONE

GLOBAL_DATUM(ai_camera_room_landmark, /obj/effect/landmark/ai_multicam_room)

/obj/effect/landmark/ai_multicam_room
	name = "ai camera room"
	icon = 'icons/mob/landmarks.dmi'
	icon_state = "x"

/obj/effect/landmark/ai_multicam_room/Initialize(mapload)
	. = ..()
	qdel(GLOB.ai_camera_room_landmark)
	GLOB.ai_camera_room_landmark = src

/obj/effect/landmark/ai_multicam_room/Destroy()
	if(GLOB.ai_camera_room_landmark == src)
		GLOB.ai_camera_room_landmark = null
	return ..()

//Dummy camera eyes

/mob/camera/ai_eye/pic_in_pic
	name = "Secondary AI Eye"
	invisibility = INVISIBILITY_OBSERVER
	mouse_opacity = MOUSE_OPACITY_ICON
	icon_state = "ai_pip_camera"
	var/atom/movable/screen/movable/pic_in_pic/ai/screen
	var/list/cameras_telegraphed = list()
	var/telegraph_cameras = TRUE
	var/telegraph_range = 7
	ai_detector_color = COLOR_ORANGE

/mob/camera/ai_eye/pic_in_pic/GetViewerClient()
	if(screen?.ai)
		return screen.ai.client

/mob/camera/ai_eye/pic_in_pic/setLoc(turf/destination)
	if (destination)
		abstract_move(destination)
	else
		moveToNullspace()
	if(screen?.ai)
		screen.ai.camera_visibility(src)
	else
		GLOB.cameranet.visibility(src)
	update_camera_telegraphing()
	update_ai_detect_hud()

/mob/camera/ai_eye/pic_in_pic/get_visible_turfs()
	return screen ? screen.get_visible_turfs() : list()

/mob/camera/ai_eye/pic_in_pic/proc/update_camera_telegraphing()
	if(!telegraph_cameras)
		return
	var/list/obj/machinery/camera/add = list()
	var/list/obj/machinery/camera/remove = list()
	var/list/obj/machinery/camera/visible = list()
	for (var/VV in visibleCameraChunks)
		var/datum/camerachunk/CC = VV
		for (var/V in CC.cameras)
			var/obj/machinery/camera/C = V
			if (!C.can_use() || (get_dist(C, src) > telegraph_range))
				continue
			visible |= C

	add = visible - cameras_telegraphed
	remove = cameras_telegraphed - visible

	for (var/V in remove)
		var/obj/machinery/camera/C = V
		if(QDELETED(C))
			continue
		cameras_telegraphed -= C
		C.in_use_lights--
		C.update_appearance()
	for (var/V in add)
		var/obj/machinery/camera/C = V
		if(QDELETED(C))
			continue
		cameras_telegraphed |= C
		C.in_use_lights++
		C.update_appearance()

/mob/camera/ai_eye/pic_in_pic/proc/disable_camera_telegraphing()
	telegraph_cameras = FALSE
	for (var/V in cameras_telegraphed)
		var/obj/machinery/camera/C = V
		if(QDELETED(C))
			continue
		C.in_use_lights--
		C.update_appearance()
	cameras_telegraphed.Cut()

/mob/camera/ai_eye/pic_in_pic/Destroy()
	disable_camera_telegraphing()
	return ..()

//AI procs

/mob/living/silicon/ai/proc/drop_new_multicam(silent = FALSE)
	if(!CONFIG_GET(flag/allow_ai_multicam))
		if(!silent)
			to_chat(src, span_warning("This action is currently disabled. Contact an administrator to enable this feature."))
		return
	if(!eyeobj)
		return
	if(multicam_screens.len >= max_multicams)
		if(!silent)
			to_chat(src, span_warning("Cannot place more than [max_multicams] multicamera windows."))
		return
	var/atom/movable/screen/movable/pic_in_pic/ai/C = new /atom/movable/screen/movable/pic_in_pic/ai()
	C.set_view_size(3, 3, FALSE)
	C.set_view_center(get_turf(eyeobj))
	C.set_ai(src)
	if(!silent)
		to_chat(src, span_notice("Added new multicamera window."))
	return C

/mob/living/silicon/ai/proc/toggle_multicam()
	if(!CONFIG_GET(flag/allow_ai_multicam))
		to_chat(src, span_warning("This action is currently disabled. Contact an administrator to enable this feature."))
		return
	if(multicam_on)
		end_multicam()
	else
		start_multicam()

/mob/living/silicon/ai/proc/start_multicam()
	if(multicam_on || aiRestorePowerRoutine || !isturf(loc))
		return
	if(!GLOB.ai_camera_room_landmark)
		to_chat(src, span_warning("This function is not available at this time."))
		return
	multicam_on = TRUE
	refresh_multicam()
	to_chat(src, span_notice("Multiple-camera viewing mode activated."))

/mob/living/silicon/ai/proc/refresh_multicam()
	reset_perspective(GLOB.ai_camera_room_landmark)
	if(client)
		for(var/V in multicam_screens)
			var/atom/movable/screen/movable/pic_in_pic/P = V
			P.show_to(client)

/mob/living/silicon/ai/proc/end_multicam()
	if(!multicam_on)
		return
	multicam_on = FALSE
	select_main_multicam_window(null)
	if(client)
		for(var/V in multicam_screens)
			var/atom/movable/screen/movable/pic_in_pic/P = V
			P.unshow_to(client)
	reset_perspective()
	to_chat(src, span_notice("Multiple-camera viewing mode deactivated."))

/mob/living/silicon/ai/proc/select_main_multicam_window(atom/movable/screen/movable/pic_in_pic/ai/P)
	if(master_multicam == P)
		return

	if(master_multicam)
		master_multicam.set_view_center(get_turf(eyeobj), FALSE)
		master_multicam.unhighlight()
		master_multicam = null

	if(P)
		P.highlight()
		eyeobj.setLoc(get_turf(P.center))
		P.set_view_center(eyeobj)
		master_multicam = P


code:
/datum/robot_control
	var/mob/living/silicon/ai/owner

/datum/robot_control/New(mob/living/silicon/ai/new_owner)
	if(!istype(new_owner))
		qdel(src)
	owner = new_owner

/datum/robot_control/proc/is_interactable(mob/user)
	if(user != owner || owner.incapacitated())
		return FALSE
	if(owner.control_disabled)
		to_chat(user, span_warning("Wireless control is disabled."))
		return FALSE
	return TRUE

/datum/robot_control/ui_status(mob/user)
	if(is_interactable(user))
		return ..()
	return UI_CLOSE

/datum/robot_control/ui_state(mob/user)
	return GLOB.always_state

/datum/robot_control/ui_interact(mob/user, datum/tgui/ui)
	ui = SStgui.try_update_ui(user, src, ui)
	if(!ui)
		ui = new(user, src, "RemoteRobotControl")
		ui.open()

/datum/robot_control/ui_data(mob/user)
	if(!owner || user != owner)
		return
	var/list/data = list()
	var/turf/ai_current_turf = get_turf(owner)
	var/ai_zlevel = ai_current_turf.z

	data["robots"] = list()
	for(var/mob/living/simple_animal/bot/B in GLOB.bots_list)
		if(B.z != ai_zlevel || B.remote_disabled) //Only non-emagged bots on the same Z-level are detected!
			continue
		var/list/robot_data = list(
			name = B.name,
			model = B.model,
			mode = B.get_mode(),
			hacked = B.hacked,
			location = get_area_name(B, TRUE),
			ref = REF(B)
		)
		data["robots"] += list(robot_data)

	return data

/datum/robot_control/ui_act(action, params)
	. = ..()
	if(.)
		return
	if(!is_interactable(usr))
		return

	switch(action)
		if("callbot") //Command a bot to move to a selected location.
			if(owner.call_bot_cooldown > world.time)
				to_chat(usr, span_danger("Error: Your last call bot command is still processing, please wait for the bot to finish calculating a route."))
				return
			owner.Bot = locate(params["ref"]) in GLOB.bots_list
			if(!owner.Bot || owner.Bot.remote_disabled || owner.control_disabled)
				return
			owner.waypoint_mode = TRUE
			to_chat(usr, span_notice("Set your waypoint by clicking on a valid location free of obstructions."))
			. = TRUE
		if("interface") //Remotely connect to a bot!
			owner.Bot = locate(params["ref"]) in GLOB.bots_list
			if(!owner.Bot || owner.Bot.remote_disabled || owner.control_disabled)
				return
			owner.Bot.attack_ai(usr)
			. = TRUE


code:
// List is required to compile the resources into the game when it loads.
// Dynamically loading it has bad results with sounds overtaking each other, even with the wait variable.
#ifdef AI_VOX

// Regex for collecting a list of ogg files
// (([a-zA-Z,.]+)\.ogg)

// For vim
// :%s/\(\(.*\)\.ogg\)/"\2" = 'sound\/vox_fem\/\1',/g
GLOBAL_LIST_INIT(vox_sounds, list("abduction" = 'sound/vox_fem/abduction.ogg',
"abortions" = 'sound/vox_fem/abortions.ogg',
"above" = 'sound/vox_fem/above.ogg',
"abstain" = 'sound/vox_fem/abstain.ogg',
"accelerating" = 'sound/vox_fem/accelerating.ogg',
"accelerator" = 'sound/vox_fem/accelerator.ogg',
"accepted" = 'sound/vox_fem/accepted.ogg',
"access" = 'sound/vox_fem/access.ogg',
"acknowledged" = 'sound/vox_fem/acknowledged.ogg',
"acknowledge" = 'sound/vox_fem/acknowledge.ogg',
"acquired" = 'sound/vox_fem/acquired.ogg',
"acquisition" = 'sound/vox_fem/acquisition.ogg',
"across" = 'sound/vox_fem/across.ogg',
"activated" = 'sound/vox_fem/activated.ogg',
"activate" = 'sound/vox_fem/activate.ogg',
"activity" = 'sound/vox_fem/activity.ogg',
"adios" = 'sound/vox_fem/adios.ogg',
"administration" = 'sound/vox_fem/administration.ogg',
"advanced" = 'sound/vox_fem/advanced.ogg',
"advised" = 'sound/vox_fem/advised.ogg',
"after" = 'sound/vox_fem/after.ogg',
"aft" = 'sound/vox_fem/aft.ogg',
"agent" = 'sound/vox_fem/agent.ogg',
"ai" = 'sound/vox_fem/ai.ogg',
"airlock" = 'sound/vox_fem/airlock.ogg',
"air" = 'sound/vox_fem/air.ogg',
"alarm" = 'sound/vox_fem/alarm.ogg',
"alert" = 'sound/vox_fem/alert.ogg',
"alien" = 'sound/vox_fem/alien.ogg',
"aligned" = 'sound/vox_fem/aligned.ogg',
"all" = 'sound/vox_fem/all.ogg',
"alpha" = 'sound/vox_fem/alpha.ogg',
"also" = 'sound/vox_fem/also.ogg',
"amigo" = 'sound/vox_fem/amigo.ogg',
"ammunition" = 'sound/vox_fem/ammunition.ogg',
"am" = 'sound/vox_fem/am.ogg',
"and" = 'sound/vox_fem/and.ogg',
"animal" = 'sound/vox_fem/animal.ogg',
"announcement" = 'sound/vox_fem/announcement.ogg',
"an" = 'sound/vox_fem/an.ogg',
"anomalous" = 'sound/vox_fem/anomalous.ogg',
"answer" = 'sound/vox_fem/answer.ogg',
"antenna" = 'sound/vox_fem/antenna.ogg',
"any" = 'sound/vox_fem/any.ogg',
"a" = 'sound/vox_fem/a.ogg',
"apc" = 'sound/vox_fem/apc.ogg',
"apprehend" = 'sound/vox_fem/apprehend.ogg',
"approach" = 'sound/vox_fem/approach.ogg',
"area" = 'sound/vox_fem/area.ogg',
"are" = 'sound/vox_fem/are.ogg',
"armed" = 'sound/vox_fem/armed.ogg',
"arm" = 'sound/vox_fem/arm.ogg',
"armor" = 'sound/vox_fem/armor.ogg',
"armory" = 'sound/vox_fem/armory.ogg',
"array" = 'sound/vox_fem/array.ogg',
"arrest" = 'sound/vox_fem/arrest.ogg',
"asimov" = 'sound/vox_fem/asimov.ogg',
"asshole" = 'sound/vox_fem/asshole.ogg',
"assholes" = 'sound/vox_fem/assholes.ogg',
"assistance" = 'sound/vox_fem/assistance.ogg',
"assistant" = 'sound/vox_fem/assistant.ogg',
"ass" = 'sound/vox_fem/ass.ogg',
"atmosphere" = 'sound/vox_fem/atmosphere.ogg',
"atmospheric" = 'sound/vox_fem/atmospheric.ogg',
"atmospherics" = 'sound/vox_fem/atmospherics.ogg',
"at" = 'sound/vox_fem/at.ogg',
"atomic" = 'sound/vox_fem/atomic.ogg',
"attention" = 'sound/vox_fem/attention.ogg',
"authentication" = 'sound/vox_fem/authentication.ogg',
"authorized" = 'sound/vox_fem/authorized.ogg',
"authorize" = 'sound/vox_fem/authorize.ogg',
"automatic" = 'sound/vox_fem/automatic.ogg',
"away" = 'sound/vox_fem/away.ogg',
"awful" = 'sound/vox_fem/awful.ogg',
"backman" = 'sound/vox_fem/backman.ogg',
"back" = 'sound/vox_fem/back.ogg',
"bad" = 'sound/vox_fem/bad.ogg',
"bag" = 'sound/vox_fem/bag.ogg',
"bailey" = 'sound/vox_fem/bailey.ogg',
"bar" = 'sound/vox_fem/bar.ogg',
"barracks" = 'sound/vox_fem/barracks.ogg',
"bartender" = 'sound/vox_fem/bartender.ogg',
"base" = 'sound/vox_fem/base.ogg',
"bay" = 'sound/vox_fem/bay.ogg',
"beam" = 'sound/vox_fem/beam.ogg',
"been" = 'sound/vox_fem/been.ogg',
"beep" = 'sound/vox_fem/beep.ogg',
"before" = 'sound/vox_fem/before.ogg',
"below" = 'sound/vox_fem/below.ogg',
"be" = 'sound/vox_fem/be.ogg',
"beside" = 'sound/vox_fem/beside.ogg',
"beware" = 'sound/vox_fem/beware.ogg',
"beyond" = 'sound/vox_fem/beyond.ogg',
"biohazard" = 'sound/vox_fem/biohazard.ogg',
"biological" = 'sound/vox_fem/biological.ogg',
"birdwell" = 'sound/vox_fem/birdwell.ogg',
"bitches" = 'sound/vox_fem/bitches.ogg',
"bitch" = 'sound/vox_fem/bitch.ogg',
"bitcoin" = 'sound/vox_fem/bitcoin.ogg',
"black" = 'sound/vox_fem/black.ogg',
"blast" = 'sound/vox_fem/blast.ogg',
"bleed" = 'sound/vox_fem/bleed.ogg',
"blob" = 'sound/vox_fem/blob.ogg',
"blocked" = 'sound/vox_fem/blocked.ogg',
"blood" = 'sound/vox_fem/blood.ogg',
"bloop" = 'sound/vox_fem/bloop.ogg',
"blue" = 'sound/vox_fem/blue.ogg',
"b" = 'sound/vox_fem/b.ogg',
"bomb" = 'sound/vox_fem/bomb.ogg',
"bone" = 'sound/vox_fem/bone.ogg',
"botanist" = 'sound/vox_fem/botanist.ogg',
"botany" = 'sound/vox_fem/botany.ogg',
"bottom" = 'sound/vox_fem/bottom.ogg',
"bravo" = 'sound/vox_fem/bravo.ogg',
"breached" = 'sound/vox_fem/breached.ogg',
"breach" = 'sound/vox_fem/breach.ogg',
"break" = 'sound/vox_fem/break.ogg',
"bridge" = 'sound/vox_fem/bridge.ogg',
"brig" = 'sound/vox_fem/brig.ogg',
"bust" = 'sound/vox_fem/bust.ogg',
"but" = 'sound/vox_fem/but.ogg',
"button" = 'sound/vox_fem/button.ogg',
"bypass" = 'sound/vox_fem/bypass.ogg',
"cable" = 'sound/vox_fem/cable.ogg',
"called" = 'sound/vox_fem/called.ogg',
"call" = 'sound/vox_fem/call.ogg',
"canal" = 'sound/vox_fem/canal.ogg',
"canister" = 'sound/vox_fem/canister.ogg',
"cap" = 'sound/vox_fem/cap.ogg',
"captain" = 'sound/vox_fem/captain.ogg',
"capture" = 'sound/vox_fem/capture.ogg',
"carbon" = 'sound/vox_fem/carbon.ogg',
"cargo" = 'sound/vox_fem/cargo.ogg',
"cat" = 'sound/vox_fem/cat.ogg',
"cease" = 'sound/vox_fem/cease.ogg',
"ceiling" = 'sound/vox_fem/ceiling.ogg',
"celsius" = 'sound/vox_fem/celsius.ogg',
"centcom" = 'sound/vox_fem/centcom.ogg',
"center" = 'sound/vox_fem/center.ogg',
"centi" = 'sound/vox_fem/centi.ogg',
"central" = 'sound/vox_fem/central.ogg',
"ce" = 'sound/vox_fem/ce.ogg',
"challenge" = 'sound/vox_fem/challenge.ogg',
"chamber" = 'sound/vox_fem/chamber.ogg',
"changed" = 'sound/vox_fem/changed.ogg',
"changeling" = 'sound/vox_fem/changeling.ogg',
"change" = 'sound/vox_fem/change.ogg',
"chapel" = 'sound/vox_fem/chapel.ogg',
"chaplain" = 'sound/vox_fem/chaplain.ogg',
"charlie" = 'sound/vox_fem/charlie.ogg',
"check" = 'sound/vox_fem/check.ogg',
"checkpoint" = 'sound/vox_fem/checkpoint.ogg',
"chemical" = 'sound/vox_fem/chemical.ogg',
"chemist" = 'sound/vox_fem/chemist.ogg',
"chief" = 'sound/vox_fem/chief.ogg',
"christ" = 'sound/vox_fem/christ.ogg',
"chuckle" = 'sound/vox_fem/chuckle.ogg',
"circuit" = 'sound/vox_fem/circuit.ogg',
"cleanup" = 'sound/vox_fem/cleanup.ogg',
"clearance" = 'sound/vox_fem/clearance.ogg',
"clear" = 'sound/vox_fem/clear.ogg',
"clockwork" = 'sound/vox_fem/clockwork.ogg',
"close" = 'sound/vox_fem/close.ogg',
"clowning" = 'sound/vox_fem/clowning.ogg',
"clown" = 'sound/vox_fem/clown.ogg',
"cmo" = 'sound/vox_fem/cmo.ogg',
"coded" = 'sound/vox_fem/coded.ogg',
"code" = 'sound/vox_fem/code.ogg',
"c" = 'sound/vox_fem/c.ogg',
"cold" = 'sound/vox_fem/cold.ogg',
"collider" = 'sound/vox_fem/collider.ogg',
"come" = 'sound/vox_fem/come.ogg',
"command" = 'sound/vox_fem/command.ogg',
"communication" = 'sound/vox_fem/communication.ogg',
"complex" = 'sound/vox_fem/complex.ogg',
"comply" = 'sound/vox_fem/comply.ogg',
"computer" = 'sound/vox_fem/computer.ogg',
"condition" = 'sound/vox_fem/condition.ogg',
"condom" = 'sound/vox_fem/condom.ogg',
"confirmed" = 'sound/vox_fem/confirmed.ogg',
"connor" = 'sound/vox_fem/connor.ogg',
"console2" = 'sound/vox_fem/console2.ogg',
"console" = 'sound/vox_fem/console.ogg',
"construct" = 'sound/vox_fem/construct.ogg',
"containment" = 'sound/vox_fem/containment.ogg',
"contamination" = 'sound/vox_fem/contamination.ogg',
"contraband" = 'sound/vox_fem/contraband.ogg',
"control" = 'sound/vox_fem/control.ogg',
"cook" = 'sound/vox_fem/cook.ogg',
"coolant" = 'sound/vox_fem/coolant.ogg',
"coomer" = 'sound/vox_fem/coomer.ogg',
"core" = 'sound/vox_fem/core.ogg',
"corgi" = 'sound/vox_fem/corgi.ogg',
"corporation" = 'sound/vox_fem/corporation.ogg',
"correct" = 'sound/vox_fem/correct.ogg',
"corridor" = 'sound/vox_fem/corridor.ogg',
"corridors" = 'sound/vox_fem/corridors.ogg',
"coward" = 'sound/vox_fem/coward.ogg',
"cowards" = 'sound/vox_fem/cowards.ogg',
"crate" = 'sound/vox_fem/crate.ogg',
"created" = 'sound/vox_fem/created.ogg',
"creature" = 'sound/vox_fem/creature.ogg',
"crew" = 'sound/vox_fem/crew.ogg',
"critical" = 'sound/vox_fem/critical.ogg',
"cross" = 'sound/vox_fem/cross.ogg',
"cryogenic" = 'sound/vox_fem/cryogenic.ogg',
"crystal" = 'sound/vox_fem/crystal.ogg',
"cultist" = 'sound/vox_fem/cultist.ogg',
"cult" = 'sound/vox_fem/cult.ogg',
"cunt" = 'sound/vox_fem/cunt.ogg',
"curator" = 'sound/vox_fem/curator.ogg',
"cyborg" = 'sound/vox_fem/cyborg.ogg',
"cyborgs" = 'sound/vox_fem/cyborgs.ogg',
"damaged" = 'sound/vox_fem/damaged.ogg',
"damage" = 'sound/vox_fem/damage.ogg',
"danger" = 'sound/vox_fem/danger.ogg',
"dangerous" = 'sound/vox_fem/dangerous.ogg',
"day" = 'sound/vox_fem/day.ogg',
"deactivated" = 'sound/vox_fem/deactivated.ogg',
"dead" = 'sound/vox_fem/dead.ogg',
"death" = 'sound/vox_fem/death.ogg',
"decompression" = 'sound/vox_fem/decompression.ogg',
"decontamination" = 'sound/vox_fem/decontamination.ogg',
"deeoo" = 'sound/vox_fem/deeoo.ogg',
"defense" = 'sound/vox_fem/defense.ogg',
"degrees" = 'sound/vox_fem/degrees.ogg',
"delta" = 'sound/vox_fem/delta.ogg',
"demon" = 'sound/vox_fem/demon.ogg',
"denied" = 'sound/vox_fem/denied.ogg',
"departures" = 'sound/vox_fem/departures.ogg',
"deployed" = 'sound/vox_fem/deployed.ogg',
"deploy" = 'sound/vox_fem/deploy.ogg',
"desire" = 'sound/vox_fem/desire.ogg',
"desist" = 'sound/vox_fem/desist.ogg',
"destroyed" = 'sound/vox_fem/destroyed.ogg',
"destroy" = 'sound/vox_fem/destroy.ogg',
"destruction" = 'sound/vox_fem/destruction.ogg',
"detain" = 'sound/vox_fem/detain.ogg',
"detected" = 'sound/vox_fem/detected.ogg',
"detective" = 'sound/vox_fem/detective.ogg',
"detonation" = 'sound/vox_fem/detonation.ogg',
"device" = 'sound/vox_fem/device.ogg',
"devil" = 'sound/vox_fem/devil.ogg',
"did" = 'sound/vox_fem/did.ogg',
"die" = 'sound/vox_fem/die.ogg',
"dimensional" = 'sound/vox_fem/dimensional.ogg',
"dioxide" = 'sound/vox_fem/dioxide.ogg',
"director" = 'sound/vox_fem/director.ogg',
"dirt" = 'sound/vox_fem/dirt.ogg',
"disabled" = 'sound/vox_fem/disabled.ogg',
"disease" = 'sound/vox_fem/disease.ogg',
"disengaged" = 'sound/vox_fem/disengaged.ogg',
"dish" = 'sound/vox_fem/dish.ogg',
"disk" = 'sound/vox_fem/disk.ogg',
"disposal" = 'sound/vox_fem/disposal.ogg',
"distance" = 'sound/vox_fem/distance.ogg',
"distortion" = 'sound/vox_fem/distortion.ogg',
"doctor" = 'sound/vox_fem/doctor.ogg',
"d" = 'sound/vox_fem/d.ogg',
"dog" = 'sound/vox_fem/dog.ogg',
"do" = 'sound/vox_fem/do.ogg',
"doomsday" = 'sound/vox_fem/doomsday.ogg',
"doop" = 'sound/vox_fem/doop.ogg',
"door" = 'sound/vox_fem/door.ogg',
"dormitory" = 'sound/vox_fem/dormitory.ogg',
"dot" = 'sound/vox_fem/dot.ogg',
"down" = 'sound/vox_fem/down.ogg',
"drone" = 'sound/vox_fem/drone.ogg',
"dual" = 'sound/vox_fem/dual.ogg',
"duct" = 'sound/vox_fem/duct.ogg',
"east" = 'sound/vox_fem/east.ogg',
"echo" = 'sound/vox_fem/echo.ogg',
"ed" = 'sound/vox_fem/ed.ogg',
"effect" = 'sound/vox_fem/effect.ogg',
"egress" = 'sound/vox_fem/egress.ogg',
"eighteen" = 'sound/vox_fem/eighteen.ogg',
"eight" = 'sound/vox_fem/eight.ogg',
"eighty" = 'sound/vox_fem/eighty.ogg',
"electric" = 'sound/vox_fem/electric.ogg',
"electromagnetic" = 'sound/vox_fem/electromagnetic.ogg',
"elevator" = 'sound/vox_fem/elevator.ogg',
"eleven" = 'sound/vox_fem/eleven.ogg',
"eliminate" = 'sound/vox_fem/eliminate.ogg',
"emergency" = 'sound/vox_fem/emergency.ogg',
"enabled" = 'sound/vox_fem/enabled.ogg',
"energy" = 'sound/vox_fem/energy.ogg',
"engaged" = 'sound/vox_fem/engaged.ogg',
"engage" = 'sound/vox_fem/engage.ogg',
"engineering" = 'sound/vox_fem/engineering.ogg',
"engineer" = 'sound/vox_fem/engineer.ogg',
"engine" = 'sound/vox_fem/engine.ogg',
"enter" = 'sound/vox_fem/enter.ogg',
"entity" = 'sound/vox_fem/entity.ogg',
"entry" = 'sound/vox_fem/entry.ogg',
"environment" = 'sound/vox_fem/environment.ogg',
"e" = 'sound/vox_fem/e.ogg',
"epic" = 'sound/vox_fem/epic.ogg',
"equipment" = 'sound/vox_fem/equipment.ogg',
"error" = 'sound/vox_fem/error.ogg',
"escape" = 'sound/vox_fem/escape.ogg',
"evacuate" = 'sound/vox_fem/evacuate.ogg',
"eva" = 'sound/vox_fem/eva.ogg',
"exchange" = 'sound/vox_fem/exchange.ogg',
"exit" = 'sound/vox_fem/exit.ogg',
"expect" = 'sound/vox_fem/expect.ogg',
"experimental" = 'sound/vox_fem/experimental.ogg',
"experiment" = 'sound/vox_fem/experiment.ogg',
"explode" = 'sound/vox_fem/explode.ogg',
"explosion" = 'sound/vox_fem/explosion.ogg',
"explosive" = 'sound/vox_fem/explosive.ogg',
"exposure" = 'sound/vox_fem/exposure.ogg',
"exterminate" = 'sound/vox_fem/exterminate.ogg',
"extinguisher" = 'sound/vox_fem/extinguisher.ogg',
"extinguish" = 'sound/vox_fem/extinguish.ogg',
"extreme" = 'sound/vox_fem/extreme.ogg',
"facility" = 'sound/vox_fem/facility.ogg',
"factory" = 'sound/vox_fem/factory.ogg',
"fahrenheit" = 'sound/vox_fem/fahrenheit.ogg',
"failed" = 'sound/vox_fem/failed.ogg',
"failure" = 'sound/vox_fem/failure.ogg',
"false" = 'sound/vox_fem/false.ogg',
"farthest" = 'sound/vox_fem/farthest.ogg',
"fast" = 'sound/vox_fem/fast.ogg',
"fauna" = 'sound/vox_fem/fauna.ogg',
"feet" = 'sound/vox_fem/feet.ogg',
"field" = 'sound/vox_fem/field.ogg',
"fifteen" = 'sound/vox_fem/fifteen.ogg',
"fifth" = 'sound/vox_fem/fifth.ogg',
"fifty" = 'sound/vox_fem/fifty.ogg',
"final" = 'sound/vox_fem/final.ogg',
"fine" = 'sound/vox_fem/fine.ogg',
"fire" = 'sound/vox_fem/fire.ogg',
"first" = 'sound/vox_fem/first.ogg',
"five" = 'sound/vox_fem/five.ogg',
"fix" = 'sound/vox_fem/fix.ogg',
"flooding" = 'sound/vox_fem/flooding.ogg',
"floor" = 'sound/vox_fem/floor.ogg',
"flyman" = 'sound/vox_fem/flyman.ogg',
"f" = 'sound/vox_fem/f.ogg',
"fool" = 'sound/vox_fem/fool.ogg',
"forbidden" = 'sound/vox_fem/forbidden.ogg',
"force" = 'sound/vox_fem/force.ogg',
"fore" = 'sound/vox_fem/fore.ogg',
"formed" = 'sound/vox_fem/formed.ogg',
"form" = 'sound/vox_fem/form.ogg',
"forms" = 'sound/vox_fem/forms.ogg',
"for" = 'sound/vox_fem/for.ogg',
"found" = 'sound/vox_fem/found.ogg',
"four" = 'sound/vox_fem/four.ogg',
"fourteen" = 'sound/vox_fem/fourteen.ogg',
"fourth" = 'sound/vox_fem/fourth.ogg',
"fourty" = 'sound/vox_fem/fourty.ogg',
"foxtrot" = 'sound/vox_fem/foxtrot.ogg',
"freeman" = 'sound/vox_fem/freeman.ogg',
"free" = 'sound/vox_fem/free.ogg',
"freezer" = 'sound/vox_fem/freezer.ogg',
"freezing" = 'sound/vox_fem/freezing.ogg',
"from" = 'sound/vox_fem/from.ogg',
"front" = 'sound/vox_fem/front.ogg',
"fucking" = 'sound/vox_fem/fucking.ogg',
"fuck" = 'sound/vox_fem/fuck.ogg',
"fucks" = 'sound/vox_fem/fucks.ogg',
"fuel" = 'sound/vox_fem/fuel.ogg',
"gas" = 'sound/vox_fem/gas.ogg',
"generator" = 'sound/vox_fem/generator.ogg',
"geneticist" = 'sound/vox_fem/geneticist.ogg',
"get" = 'sound/vox_fem/get.ogg',
"glory" = 'sound/vox_fem/glory.ogg',
"god" = 'sound/vox_fem/god.ogg',
"g" = 'sound/vox_fem/g.ogg',
"going" = 'sound/vox_fem/going.ogg',
"golem" = 'sound/vox_fem/golem.ogg',
"goodbye" = 'sound/vox_fem/goodbye.ogg',
"good" = 'sound/vox_fem/good.ogg',
"go" = 'sound/vox_fem/go.ogg',
"gordon" = 'sound/vox_fem/gordon.ogg',
"got" = 'sound/vox_fem/got.ogg',
"government" = 'sound/vox_fem/government.ogg',
"granted" = 'sound/vox_fem/granted.ogg',
"gravity" = 'sound/vox_fem/gravity.ogg',
"gray" = 'sound/vox_fem/gray.ogg',
"great" = 'sound/vox_fem/great.ogg',
"green" = 'sound/vox_fem/green.ogg',
"grenade" = 'sound/vox_fem/grenade.ogg',
"guard" = 'sound/vox_fem/guard.ogg',
"gulf" = 'sound/vox_fem/gulf.ogg',
"gun" = 'sound/vox_fem/gun.ogg',
"guthrie" = 'sound/vox_fem/guthrie.ogg',
"hacker" = 'sound/vox_fem/hacker.ogg',
"hackers" = 'sound/vox_fem/hackers.ogg',
"hall" = 'sound/vox_fem/hall.ogg',
"hallway" = 'sound/vox_fem/hallway.ogg',
"handling" = 'sound/vox_fem/handling.ogg',
"hangar" = 'sound/vox_fem/hangar.ogg',
"harmful" = 'sound/vox_fem/harmful.ogg',
"harm" = 'sound/vox_fem/harm.ogg',
"has" = 'sound/vox_fem/has.ogg',
"have" = 'sound/vox_fem/have.ogg',
"hazard" = 'sound/vox_fem/hazard.ogg',
"head" = 'sound/vox_fem/head.ogg',
"health" = 'sound/vox_fem/health.ogg',
"heat" = 'sound/vox_fem/heat.ogg',
"helicopter" = 'sound/vox_fem/helicopter.ogg',
"helium" = 'sound/vox_fem/helium.ogg',
"hello" = 'sound/vox_fem/hello.ogg',
"help" = 'sound/vox_fem/help.ogg',
"he" = 'sound/vox_fem/he.ogg',
"here" = 'sound/vox_fem/here.ogg',
"hide" = 'sound/vox_fem/hide.ogg',
"highest" = 'sound/vox_fem/highest.ogg',
"high" = 'sound/vox_fem/high.ogg',
"hit" = 'sound/vox_fem/hit.ogg',
"h" = 'sound/vox_fem/h.ogg',
"hole" = 'sound/vox_fem/hole.ogg',
"honk" = 'sound/vox_fem/honk.ogg',
"hop" = 'sound/vox_fem/hop.ogg',
"hos" = 'sound/vox_fem/hos.ogg',
"hostile" = 'sound/vox_fem/hostile.ogg',
"hotel" = 'sound/vox_fem/hotel.ogg',
"hot" = 'sound/vox_fem/hot.ogg',
"hour" = 'sound/vox_fem/hour.ogg',
"hours" = 'sound/vox_fem/hours.ogg',
"how" = 'sound/vox_fem/how.ogg',
"human" = 'sound/vox_fem/human.ogg',
"humanoid" = 'sound/vox_fem/humanoid.ogg',
"humans" = 'sound/vox_fem/humans.ogg',
"hundred" = 'sound/vox_fem/hundred.ogg',
"hunger" = 'sound/vox_fem/hunger.ogg',
"hurt" = 'sound/vox_fem/hurt.ogg',
"hydro" = 'sound/vox_fem/hydro.ogg',
"hydroponics" = 'sound/vox_fem/hydroponics.ogg',
"ian" = 'sound/vox_fem/ian.ogg',
"idiot" = 'sound/vox_fem/idiot.ogg',
"if2" = 'sound/vox_fem/if2.ogg',
"if" = 'sound/vox_fem/if.ogg',
"illegal" = 'sound/vox_fem/illegal.ogg',
"immediately" = 'sound/vox_fem/immediately.ogg',
"immediate" = 'sound/vox_fem/immediate.ogg',
"immortal" = 'sound/vox_fem/immortal.ogg',
"impossible" = 'sound/vox_fem/impossible.ogg',
"inches" = 'sound/vox_fem/inches.ogg',
"india" = 'sound/vox_fem/india.ogg',
"ing" = 'sound/vox_fem/ing.ogg',
"in" = 'sound/vox_fem/in.ogg',
"inoperative" = 'sound/vox_fem/inoperative.ogg',
"inside" = 'sound/vox_fem/inside.ogg',
"inspection" = 'sound/vox_fem/inspection.ogg',
"inspector" = 'sound/vox_fem/inspector.ogg',
"interchange" = 'sound/vox_fem/interchange.ogg',
"internals" = 'sound/vox_fem/internals.ogg',
"intruder" = 'sound/vox_fem/intruder.ogg',
"invalid" = 'sound/vox_fem/invalid.ogg',
"invasion" = 'sound/vox_fem/invasion.ogg',
"i" = 'sound/vox_fem/i.ogg',
"is" = 'sound/vox_fem/is.ogg',
"it" = 'sound/vox_fem/it.ogg',
"janitor" = 'sound/vox_fem/janitor.ogg',
"jesus" = 'sound/vox_fem/jesus.ogg',
"j" = 'sound/vox_fem/j.ogg',
"johnson" = 'sound/vox_fem/johnson.ogg',
"juliet" = 'sound/vox_fem/juliet.ogg',
"key" = 'sound/vox_fem/key.ogg',
"kidnapped" = 'sound/vox_fem/kidnapped.ogg',
"kidnapping" = 'sound/vox_fem/kidnapping.ogg',
"killed" = 'sound/vox_fem/killed.ogg',
"kill" = 'sound/vox_fem/kill.ogg',
"kilo" = 'sound/vox_fem/kilo.ogg',
"kitchen" = 'sound/vox_fem/kitchen.ogg',
"kit" = 'sound/vox_fem/kit.ogg',
"k" = 'sound/vox_fem/k.ogg',
"lab" = 'sound/vox_fem/lab.ogg',
"lambda" = 'sound/vox_fem/lambda.ogg',
"laser" = 'sound/vox_fem/laser.ogg',
"last" = 'sound/vox_fem/last.ogg',
"launch" = 'sound/vox_fem/launch.ogg',
"lavaland" = 'sound/vox_fem/lavaland.ogg',
"law" = 'sound/vox_fem/law.ogg',
"laws" = 'sound/vox_fem/laws.ogg',
"lawyer" = 'sound/vox_fem/lawyer.ogg',
"leak" = 'sound/vox_fem/leak.ogg',
"leave" = 'sound/vox_fem/leave.ogg',
"left" = 'sound/vox_fem/left.ogg',
"legal" = 'sound/vox_fem/legal.ogg',
"level" = 'sound/vox_fem/level.ogg',
"lever" = 'sound/vox_fem/lever.ogg',
"library" = 'sound/vox_fem/library.ogg',
"lie" = 'sound/vox_fem/lie.ogg',
"lieutenant" = 'sound/vox_fem/lieutenant.ogg',
"lifeform" = 'sound/vox_fem/lifeform.ogg',
"life" = 'sound/vox_fem/life.ogg',
"light" = 'sound/vox_fem/light.ogg',
"lima" = 'sound/vox_fem/lima.ogg',
"liquid" = 'sound/vox_fem/liquid.ogg',
"live2" = 'sound/vox_fem/live2.ogg',
"live" = 'sound/vox_fem/live.ogg',
"lizard" = 'sound/vox_fem/lizard.ogg',
"loading" = 'sound/vox_fem/loading.ogg',
"located" = 'sound/vox_fem/located.ogg',
"locate" = 'sound/vox_fem/locate.ogg',
"location" = 'sound/vox_fem/location.ogg',
"locked" = 'sound/vox_fem/locked.ogg',
"locker" = 'sound/vox_fem/locker.ogg',
"lock" = 'sound/vox_fem/lock.ogg',
"lockout" = 'sound/vox_fem/lockout.ogg',
"l" = 'sound/vox_fem/l.ogg',
"long" = 'sound/vox_fem/long.ogg',
"look" = 'sound/vox_fem/look.ogg',
"loop" = 'sound/vox_fem/loop.ogg',
"loose" = 'sound/vox_fem/loose.ogg',
"lot" = 'sound/vox_fem/lot.ogg',
"lower" = 'sound/vox_fem/lower.ogg',
"lowest" = 'sound/vox_fem/lowest.ogg',
"lusty" = 'sound/vox_fem/lusty.ogg',
"machine" = 'sound/vox_fem/machine.ogg',
"magic" = 'sound/vox_fem/magic.ogg',
"magnetic" = 'sound/vox_fem/magnetic.ogg',
"main" = 'sound/vox_fem/main.ogg',
"maintenance" = 'sound/vox_fem/maintenance.ogg',
"malfunction" = 'sound/vox_fem/malfunction.ogg',
"man" = 'sound/vox_fem/man.ogg',
"many" = 'sound/vox_fem/many.ogg',
"mass" = 'sound/vox_fem/mass.ogg',
"materials" = 'sound/vox_fem/materials.ogg',
"maximum" = 'sound/vox_fem/maximum.ogg',
"may" = 'sound/vox_fem/may.ogg',
"meat" = 'sound/vox_fem/meat.ogg',
"medbay" = 'sound/vox_fem/medbay.ogg',
"medical" = 'sound/vox_fem/medical.ogg',
"megafauna" = 'sound/vox_fem/megafauna.ogg',
"men" = 'sound/vox_fem/men.ogg',
"me" = 'sound/vox_fem/me.ogg',
"mercy" = 'sound/vox_fem/mercy.ogg',
"mesa" = 'sound/vox_fem/mesa.ogg',
"message" = 'sound/vox_fem/message.ogg',
"meter" = 'sound/vox_fem/meter.ogg',
"micro" = 'sound/vox_fem/micro.ogg',
"middle" = 'sound/vox_fem/middle.ogg',
"mike" = 'sound/vox_fem/mike.ogg',
"miles" = 'sound/vox_fem/miles.ogg',
"military" = 'sound/vox_fem/military.ogg',
"milli" = 'sound/vox_fem/milli.ogg',
"million" = 'sound/vox_fem/million.ogg',
"mime" = 'sound/vox_fem/mime.ogg',
"minefield" = 'sound/vox_fem/minefield.ogg',
"miner" = 'sound/vox_fem/miner.ogg',
"minimum" = 'sound/vox_fem/minimum.ogg',
"minutes" = 'sound/vox_fem/minutes.ogg',
"mister" = 'sound/vox_fem/mister.ogg',
"mode" = 'sound/vox_fem/mode.ogg',
"modification" = 'sound/vox_fem/modification.ogg',
"m" = 'sound/vox_fem/m.ogg',
"money" = 'sound/vox_fem/money.ogg',
"monkey" = 'sound/vox_fem/monkey.ogg',
"moth" = 'sound/vox_fem/moth.ogg',
"motor" = 'sound/vox_fem/motor.ogg',
"motorpool" = 'sound/vox_fem/motorpool.ogg',
"move" = 'sound/vox_fem/move.ogg',
"multitude" = 'sound/vox_fem/multitude.ogg',
"murder" = 'sound/vox_fem/murder.ogg',
"must" = 'sound/vox_fem/must.ogg',
"my" = 'sound/vox_fem/my.ogg',
"mythic" = 'sound/vox_fem/mythic.ogg',
"nanotrasen" = 'sound/vox_fem/nanotrasen.ogg',
"nearest" = 'sound/vox_fem/nearest.ogg',
"need" = 'sound/vox_fem/need.ogg',
"nice" = 'sound/vox_fem/nice.ogg',
"nine" = 'sound/vox_fem/nine.ogg',
"nineteen" = 'sound/vox_fem/nineteen.ogg',
"ninety" = 'sound/vox_fem/ninety.ogg',
"nitrogen" = 'sound/vox_fem/nitrogen.ogg',
"n" = 'sound/vox_fem/n.ogg',
"nominal" = 'sound/vox_fem/nominal.ogg',
"no" = 'sound/vox_fem/no.ogg',
"north" = 'sound/vox_fem/north.ogg',
"not" = 'sound/vox_fem/not.ogg',
"november" = 'sound/vox_fem/november.ogg',
"now" = 'sound/vox_fem/now.ogg',
"nuclear" = 'sound/vox_fem/nuclear.ogg',
"nuke" = 'sound/vox_fem/nuke.ogg',
"number" = 'sound/vox_fem/number.ogg',
"objective" = 'sound/vox_fem/objective.ogg',
"observation" = 'sound/vox_fem/observation.ogg',
"obtain" = 'sound/vox_fem/obtain.ogg',
"office" = 'sound/vox_fem/office.ogg',
"officer" = 'sound/vox_fem/officer.ogg',
"off" = 'sound/vox_fem/off.ogg',
"of" = 'sound/vox_fem/of.ogg',
"," = 'sound/vox_fem/,.ogg',
"." = 'sound/vox_fem/..ogg',
"oh" = 'sound/vox_fem/oh.ogg',
"ok" = 'sound/vox_fem/ok.ogg',
"one" = 'sound/vox_fem/one.ogg',
"on" = 'sound/vox_fem/on.ogg',
"oof" = 'sound/vox_fem/oof.ogg',
"o" = 'sound/vox_fem/o.ogg',
"open" = 'sound/vox_fem/open.ogg',
"operating" = 'sound/vox_fem/operating.ogg',
"operations" = 'sound/vox_fem/operations.ogg',
"operative" = 'sound/vox_fem/operative.ogg',
"option" = 'sound/vox_fem/option.ogg',
"order" = 'sound/vox_fem/order.ogg',
"organic" = 'sound/vox_fem/organic.ogg',
"or" = 'sound/vox_fem/or.ogg',
"oscar" = 'sound/vox_fem/oscar.ogg',
"out" = 'sound/vox_fem/out.ogg',
"outside" = 'sound/vox_fem/outside.ogg',
"overload" = 'sound/vox_fem/overload.ogg',
"over" = 'sound/vox_fem/over.ogg',
"override" = 'sound/vox_fem/override.ogg',
"oxygen" = 'sound/vox_fem/oxygen.ogg',
"pacification" = 'sound/vox_fem/pacification.ogg',
"pacify" = 'sound/vox_fem/pacify.ogg',
"pain" = 'sound/vox_fem/pain.ogg',
"pal" = 'sound/vox_fem/pal.ogg',
"panel" = 'sound/vox_fem/panel.ogg',
"panting" = 'sound/vox_fem/panting.ogg',
"pathetic" = 'sound/vox_fem/pathetic.ogg',
"percent" = 'sound/vox_fem/percent.ogg',
"perfect" = 'sound/vox_fem/perfect.ogg',
"perimeter" = 'sound/vox_fem/perimeter.ogg',
"permitted" = 'sound/vox_fem/permitted.ogg',
"personal" = 'sound/vox_fem/personal.ogg',
"personnel" = 'sound/vox_fem/personnel.ogg',
"pipe" = 'sound/vox_fem/pipe.ogg',
"piping" = 'sound/vox_fem/piping.ogg',
"piss" = 'sound/vox_fem/piss.ogg',
"plant" = 'sound/vox_fem/plant.ogg',
"plasmaman" = 'sound/vox_fem/plasmaman.ogg',
"plasma" = 'sound/vox_fem/plasma.ogg',
"platform" = 'sound/vox_fem/platform.ogg',
"plausible" = 'sound/vox_fem/plausible.ogg',
"please" = 'sound/vox_fem/please.ogg',
"p" = 'sound/vox_fem/p.ogg',
"point" = 'sound/vox_fem/point.ogg',
"portal" = 'sound/vox_fem/portal.ogg',
"port" = 'sound/vox_fem/port.ogg',
"possible" = 'sound/vox_fem/possible.ogg',
"power" = 'sound/vox_fem/power.ogg',
"presence" = 'sound/vox_fem/presence.ogg',
"press" = 'sound/vox_fem/press.ogg',
"pressure" = 'sound/vox_fem/pressure.ogg',
"primary" = 'sound/vox_fem/primary.ogg',
"prisoner" = 'sound/vox_fem/prisoner.ogg',
"prison" = 'sound/vox_fem/prison.ogg',
"proceed" = 'sound/vox_fem/proceed.ogg',
"processing" = 'sound/vox_fem/processing.ogg',
"progress" = 'sound/vox_fem/progress.ogg',
"proper" = 'sound/vox_fem/proper.ogg',
"propulsion" = 'sound/vox_fem/propulsion.ogg',
"prosecute" = 'sound/vox_fem/prosecute.ogg',
"protective" = 'sound/vox_fem/protective.ogg',
"push" = 'sound/vox_fem/push.ogg',
"put" = 'sound/vox_fem/put.ogg',
"q" = 'sound/vox_fem/q.ogg',
"quantum" = 'sound/vox_fem/quantum.ogg',
"quarantine" = 'sound/vox_fem/quarantine.ogg',
"quartermaster" = 'sound/vox_fem/quartermaster.ogg',
"quebec" = 'sound/vox_fem/quebec.ogg',
"queen" = 'sound/vox_fem/queen.ogg',
"questionable" = 'sound/vox_fem/questionable.ogg',
"questioning" = 'sound/vox_fem/questioning.ogg',
"question" = 'sound/vox_fem/question.ogg',
"quick" = 'sound/vox_fem/quick.ogg',
"quit" = 'sound/vox_fem/quit.ogg',
"radiation" = 'sound/vox_fem/radiation.ogg',
"radioactive" = 'sound/vox_fem/radioactive.ogg',
"rads" = 'sound/vox_fem/rads.ogg',
"raider" = 'sound/vox_fem/raider.ogg',
"raiders" = 'sound/vox_fem/raiders.ogg',
"rapid" = 'sound/vox_fem/rapid.ogg',
"reached" = 'sound/vox_fem/reached.ogg',
"reach" = 'sound/vox_fem/reach.ogg',
"reactor" = 'sound/vox_fem/reactor.ogg',
"red" = 'sound/vox_fem/red.ogg',
"relay" = 'sound/vox_fem/relay.ogg',
"released" = 'sound/vox_fem/released.ogg',
"remaining" = 'sound/vox_fem/remaining.ogg',
"removal" = 'sound/vox_fem/removal.ogg',
"renegade" = 'sound/vox_fem/renegade.ogg',
"repair" = 'sound/vox_fem/repair.ogg',
"report" = 'sound/vox_fem/report.ogg',
"reports" = 'sound/vox_fem/reports.ogg',
"required" = 'sound/vox_fem/required.ogg',
"require" = 'sound/vox_fem/require.ogg',
"research" = 'sound/vox_fem/research.ogg',
"resevoir" = 'sound/vox_fem/resevoir.ogg',
"resistance" = 'sound/vox_fem/resistance.ogg',
"rest" = 'sound/vox_fem/rest.ogg',
"restoration" = 'sound/vox_fem/restoration.ogg',
"revolutionary" = 'sound/vox_fem/revolutionary.ogg',
"revolution" = 'sound/vox_fem/revolution.ogg',
"right" = 'sound/vox_fem/right.ogg',
"riot" = 'sound/vox_fem/riot.ogg',
"roboticist" = 'sound/vox_fem/roboticist.ogg',
"rocket" = 'sound/vox_fem/rocket.ogg',
"roger" = 'sound/vox_fem/roger.ogg',
"r" = 'sound/vox_fem/r.ogg',
"rogue" = 'sound/vox_fem/rogue.ogg',
"romeo" = 'sound/vox_fem/romeo.ogg',
"room" = 'sound/vox_fem/room.ogg',
"round" = 'sound/vox_fem/round.ogg',
"rune" = 'sound/vox_fem/rune.ogg',
"run" = 'sound/vox_fem/run.ogg',
"runtime" = 'sound/vox_fem/runtime.ogg',
"sabotage" = 'sound/vox_fem/sabotage.ogg',
"safe" = 'sound/vox_fem/safe.ogg',
"safety" = 'sound/vox_fem/safety.ogg',
"sairhorn" = 'sound/vox_fem/sairhorn.ogg',
"sarah" = 'sound/vox_fem/sarah.ogg',
"sargeant" = 'sound/vox_fem/sargeant.ogg',
"satellite" = 'sound/vox_fem/satellite.ogg',
"save" = 'sound/vox_fem/save.ogg',
"scensor" = 'sound/vox_fem/scensor.ogg',
"science" = 'sound/vox_fem/science.ogg',
"scientist" = 'sound/vox_fem/scientist.ogg',
"scream" = 'sound/vox_fem/scream.ogg',
"screen" = 'sound/vox_fem/screen.ogg',
"search" = 'sound/vox_fem/search.ogg',
"secondary" = 'sound/vox_fem/secondary.ogg',
"second" = 'sound/vox_fem/second.ogg',
"seconds" = 'sound/vox_fem/seconds.ogg',
"section" = 'sound/vox_fem/section.ogg',
"sector" = 'sound/vox_fem/sector.ogg',
"secured" = 'sound/vox_fem/secured.ogg',
"secure" = 'sound/vox_fem/secure.ogg',
"security" = 'sound/vox_fem/security.ogg',
"selected" = 'sound/vox_fem/selected.ogg',
"select" = 'sound/vox_fem/select.ogg',
"self" = 'sound/vox_fem/self.ogg',
"sensors" = 'sound/vox_fem/sensors.ogg',
"server" = 'sound/vox_fem/server.ogg',
"service" = 'sound/vox_fem/service.ogg',
"seven" = 'sound/vox_fem/seven.ogg',
"seventeen" = 'sound/vox_fem/seventeen.ogg',
"seventy" = 'sound/vox_fem/seventy.ogg',
"severe" = 'sound/vox_fem/severe.ogg',
"sewage" = 'sound/vox_fem/sewage.ogg',
"sewer" = 'sound/vox_fem/sewer.ogg',
"shaft" = 'sound/vox_fem/shaft.ogg',
"she" = 'sound/vox_fem/she.ogg',
"shield" = 'sound/vox_fem/shield.ogg',
"shipment" = 'sound/vox_fem/shipment.ogg',
"shirt" = 'sound/vox_fem/shirt.ogg',
"shitlord" = 'sound/vox_fem/shitlord.ogg',
"shit" = 'sound/vox_fem/shit.ogg',
"shits" = 'sound/vox_fem/shits.ogg',
"shitting" = 'sound/vox_fem/shitting.ogg',
"shock" = 'sound/vox_fem/shock.ogg',
"shonk" = 'sound/vox_fem/shonk.ogg',
"shoot" = 'sound/vox_fem/shoot.ogg',
"shower" = 'sound/vox_fem/shower.ogg',
"shut" = 'sound/vox_fem/shut.ogg',
"shuttle" = 'sound/vox_fem/shuttle.ogg',
"sick" = 'sound/vox_fem/sick.ogg',
"side" = 'sound/vox_fem/side.ogg',
"sierra" = 'sound/vox_fem/sierra.ogg',
"sight" = 'sound/vox_fem/sight.ogg',
"silicon" = 'sound/vox_fem/silicon.ogg',
"silo" = 'sound/vox_fem/silo.ogg',
"singularity" = 'sound/vox_fem/singularity.ogg',
"six" = 'sound/vox_fem/six.ogg',
"sixteen" = 'sound/vox_fem/sixteen.ogg',
"sixty" = 'sound/vox_fem/sixty.ogg',
"skeleton" = 'sound/vox_fem/skeleton.ogg',
"slaughter" = 'sound/vox_fem/slaughter.ogg',
"slime" = 'sound/vox_fem/slime.ogg',
"slip" = 'sound/vox_fem/slip.ogg',
"slippery" = 'sound/vox_fem/slippery.ogg',
"slow" = 'sound/vox_fem/slow.ogg',
"sm" = 'sound/vox_fem/sm.ogg',
"s" = 'sound/vox_fem/s.ogg',
"solar" = 'sound/vox_fem/solar.ogg',
"solars" = 'sound/vox_fem/solars.ogg',
"soldier" = 'sound/vox_fem/soldier.ogg',
"some" = 'sound/vox_fem/some.ogg',
"someone" = 'sound/vox_fem/someone.ogg',
"something" = 'sound/vox_fem/something.ogg',
"son" = 'sound/vox_fem/son.ogg',
"sorry" = 'sound/vox_fem/sorry.ogg',
"south" = 'sound/vox_fem/south.ogg',
"space" = 'sound/vox_fem/space.ogg',
"squad" = 'sound/vox_fem/squad.ogg',
"square" = 'sound/vox_fem/square.ogg',
"ss13" = 'sound/vox_fem/ss13.ogg',
"stairway" = 'sound/vox_fem/stairway.ogg',
"starboard" = 'sound/vox_fem/starboard.ogg',
"station" = 'sound/vox_fem/station.ogg',
"status" = 'sound/vox_fem/status.ogg',
"stay" = 'sound/vox_fem/stay.ogg',
"sterile" = 'sound/vox_fem/sterile.ogg',
"sterilization" = 'sound/vox_fem/sterilization.ogg',
"stop" = 'sound/vox_fem/stop.ogg',
"storage" = 'sound/vox_fem/storage.ogg',
"strong" = 'sound/vox_fem/strong.ogg',
"stuck" = 'sound/vox_fem/stuck.ogg',
"sub" = 'sound/vox_fem/sub.ogg',
"subsurface" = 'sound/vox_fem/subsurface.ogg',
"sudden" = 'sound/vox_fem/sudden.ogg',
"suffer" = 'sound/vox_fem/suffer.ogg',
"suit" = 'sound/vox_fem/suit.ogg',
"superconducting" = 'sound/vox_fem/superconducting.ogg',
"supercooled" = 'sound/vox_fem/supercooled.ogg',
"supermatter" = 'sound/vox_fem/supermatter.ogg',
"supply" = 'sound/vox_fem/supply.ogg',
"surface" = 'sound/vox_fem/surface.ogg',
"surrender" = 'sound/vox_fem/surrender.ogg',
"surrounded" = 'sound/vox_fem/surrounded.ogg',
"surround" = 'sound/vox_fem/surround.ogg',
"sweating" = 'sound/vox_fem/sweating.ogg',
"swhitenoise" = 'sound/vox_fem/swhitenoise.ogg',
"switch" = 'sound/vox_fem/switch.ogg',
"syndicate" = 'sound/vox_fem/syndicate.ogg',
"system" = 'sound/vox_fem/system.ogg',
"systems" = 'sound/vox_fem/systems.ogg',
"table" = 'sound/vox_fem/table.ogg',
"tactical" = 'sound/vox_fem/tactical.ogg',
"take" = 'sound/vox_fem/take.ogg',
"talk" = 'sound/vox_fem/talk.ogg',
"tampered" = 'sound/vox_fem/tampered.ogg',
"tango" = 'sound/vox_fem/tango.ogg',
"tank" = 'sound/vox_fem/tank.ogg',
"target" = 'sound/vox_fem/target.ogg',
"team" = 'sound/vox_fem/team.ogg',
"technician" = 'sound/vox_fem/technician.ogg',
"technology" = 'sound/vox_fem/technology.ogg',
"tech" = 'sound/vox_fem/tech.ogg',
"temperature" = 'sound/vox_fem/temperature.ogg',
"temporal" = 'sound/vox_fem/temporal.ogg',
"ten" = 'sound/vox_fem/ten.ogg',
"terminal" = 'sound/vox_fem/terminal.ogg',
"terminated" = 'sound/vox_fem/terminated.ogg',
"termination" = 'sound/vox_fem/termination.ogg',
"test" = 'sound/vox_fem/test.ogg',
"text" = 'sound/vox_fem/text.ogg',
"that" = 'sound/vox_fem/that.ogg',
"theater" = 'sound/vox_fem/theater.ogg',
"them" = 'sound/vox_fem/them.ogg',
"then" = 'sound/vox_fem/then.ogg',
"the" = 'sound/vox_fem/the.ogg',
"there" = 'sound/vox_fem/there.ogg',
"they" = 'sound/vox_fem/they.ogg',
"third" = 'sound/vox_fem/third.ogg',
"thirteen" = 'sound/vox_fem/thirteen.ogg',
"thirty" = 'sound/vox_fem/thirty.ogg',
"this" = 'sound/vox_fem/this.ogg',
"those" = 'sound/vox_fem/those.ogg',
"thousand" = 'sound/vox_fem/thousand.ogg',
"threat" = 'sound/vox_fem/threat.ogg',
"three" = 'sound/vox_fem/three.ogg',
"through" = 'sound/vox_fem/through.ogg',
"tide" = 'sound/vox_fem/tide.ogg',
"time" = 'sound/vox_fem/time.ogg',
"t" = 'sound/vox_fem/t.ogg',
"to" = 'sound/vox_fem/to.ogg',
"top" = 'sound/vox_fem/top.ogg',
"topside" = 'sound/vox_fem/topside.ogg',
"touch" = 'sound/vox_fem/touch.ogg',
"towards" = 'sound/vox_fem/towards.ogg',
"toxins" = 'sound/vox_fem/toxins.ogg',
"track" = 'sound/vox_fem/track.ogg',
"train" = 'sound/vox_fem/train.ogg',
"traitor" = 'sound/vox_fem/traitor.ogg',
"transportation" = 'sound/vox_fem/transportation.ogg',
"truck" = 'sound/vox_fem/truck.ogg',
"true" = 'sound/vox_fem/true.ogg',
"tunnel" = 'sound/vox_fem/tunnel.ogg',
"turn" = 'sound/vox_fem/turn.ogg',
"turret" = 'sound/vox_fem/turret.ogg',
"twelve" = 'sound/vox_fem/twelve.ogg',
"twenty" = 'sound/vox_fem/twenty.ogg',
"two" = 'sound/vox_fem/two.ogg',
"ughh" = 'sound/vox_fem/ughh.ogg',
"ugh" = 'sound/vox_fem/ugh.ogg',
"unable" = 'sound/vox_fem/unable.ogg',
"unauthorized" = 'sound/vox_fem/unauthorized.ogg',
"under" = 'sound/vox_fem/under.ogg',
"uniform" = 'sound/vox_fem/uniform.ogg',
"unknown" = 'sound/vox_fem/unknown.ogg',
"unlocked" = 'sound/vox_fem/unlocked.ogg',
"unsafe" = 'sound/vox_fem/unsafe.ogg',
"until" = 'sound/vox_fem/until.ogg',
"u" = 'sound/vox_fem/u.ogg',
"updated" = 'sound/vox_fem/updated.ogg',
"update" = 'sound/vox_fem/update.ogg',
"updating" = 'sound/vox_fem/updating.ogg',
"upload" = 'sound/vox_fem/upload.ogg',
"up" = 'sound/vox_fem/up.ogg',
"upper" = 'sound/vox_fem/upper.ogg',
"uranium" = 'sound/vox_fem/uranium.ogg',
"usa" = 'sound/vox_fem/usa.ogg',
"used" = 'sound/vox_fem/used.ogg',
"use" = 'sound/vox_fem/use.ogg',
"user" = 'sound/vox_fem/user.ogg',
"us" = 'sound/vox_fem/us.ogg',
"vacate" = 'sound/vox_fem/vacate.ogg',
"vacuum" = 'sound/vox_fem/vacuum.ogg',
"valid" = 'sound/vox_fem/valid.ogg',
"vapor" = 'sound/vox_fem/vapor.ogg',
"vendor" = 'sound/vox_fem/vendor.ogg',
"ventilation" = 'sound/vox_fem/ventilation.ogg',
"vent" = 'sound/vox_fem/vent.ogg',
"very" = 'sound/vox_fem/very.ogg',
"victor" = 'sound/vox_fem/victor.ogg',
"violated" = 'sound/vox_fem/violated.ogg',
"violation" = 'sound/vox_fem/violation.ogg',
"virologist" = 'sound/vox_fem/virologist.ogg',
"virology" = 'sound/vox_fem/virology.ogg',
"virus" = 'sound/vox_fem/virus.ogg',
"vitals" = 'sound/vox_fem/vitals.ogg',
"v" = 'sound/vox_fem/v.ogg',
"voltage" = 'sound/vox_fem/voltage.ogg',
"vox_login" = 'sound/vox_fem/vox_login.ogg',
"vox" = 'sound/vox_fem/vox.ogg',
"voxtest" = 'sound/vox_fem/voxtest.ogg',
"walk" = 'sound/vox_fem/walk.ogg',
"wall" = 'sound/vox_fem/wall.ogg',
"wanker" = 'sound/vox_fem/wanker.ogg',
"wanted" = 'sound/vox_fem/wanted.ogg',
"want" = 'sound/vox_fem/want.ogg',
"warden" = 'sound/vox_fem/warden.ogg',
"warm" = 'sound/vox_fem/warm.ogg',
"warning" = 'sound/vox_fem/warning.ogg',
"warn" = 'sound/vox_fem/warn.ogg',
"waste" = 'sound/vox_fem/waste.ogg',
"water" = 'sound/vox_fem/water.ogg',
"weak" = 'sound/vox_fem/weak.ogg',
"weapon" = 'sound/vox_fem/weapon.ogg',
"welcome" = 'sound/vox_fem/welcome.ogg',
"we" = 'sound/vox_fem/we.ogg',
"west" = 'sound/vox_fem/west.ogg',
"wew" = 'sound/vox_fem/wew.ogg',
"what" = 'sound/vox_fem/what.ogg',
"when" = 'sound/vox_fem/when.ogg',
"where" = 'sound/vox_fem/where.ogg',
"whiskey" = 'sound/vox_fem/whiskey.ogg',
"white" = 'sound/vox_fem/white.ogg',
"why" = 'sound/vox_fem/why.ogg',
"wilco" = 'sound/vox_fem/wilco.ogg',
"will" = 'sound/vox_fem/will.ogg',
"wing" = 'sound/vox_fem/wing.ogg',
"wire" = 'sound/vox_fem/wire.ogg',
"with" = 'sound/vox_fem/with.ogg',
"without" = 'sound/vox_fem/without.ogg',
"wizard" = 'sound/vox_fem/wizard.ogg',
"w" = 'sound/vox_fem/w.ogg',
"wood" = 'sound/vox_fem/wood.ogg',
"woody" = 'sound/vox_fem/woody.ogg',
"woop" = 'sound/vox_fem/woop.ogg',
"wow" = 'sound/vox_fem/wow.ogg',
"xenobiology" = 'sound/vox_fem/xenobiology.ogg',
"xenomorph" = 'sound/vox_fem/xenomorph.ogg',
"xenomorphs" = 'sound/vox_fem/xenomorphs.ogg',
"xeno" = 'sound/vox_fem/xeno.ogg',
"x" = 'sound/vox_fem/x.ogg',
"yankee" = 'sound/vox_fem/yankee.ogg',
"yards" = 'sound/vox_fem/yards.ogg',
"year" = 'sound/vox_fem/year.ogg',
"yellow" = 'sound/vox_fem/yellow.ogg',
"yes" = 'sound/vox_fem/yes.ogg',
"y" = 'sound/vox_fem/y.ogg',
"you" = 'sound/vox_fem/you.ogg',
"your" = 'sound/vox_fem/your.ogg',
"yourself" = 'sound/vox_fem/yourself.ogg',
"zero" = 'sound/vox_fem/zero.ogg',
"z" = 'sound/vox_fem/z.ogg',
"zombie" = 'sound/vox_fem/zombie.ogg',
"zone" = 'sound/vox_fem/zone.ogg',
"zulu" = 'sound/vox_fem/zulu.ogg'))
#endif


code:
/mob/living/silicon/apply_damage(damage = 0,damagetype = BRUTE, def_zone = null, blocked = FALSE, forced = FALSE, spread_damage = FALSE, wound_bonus = 0, bare_wound_bonus = 0, sharpness = NONE)
	var/hit_percent = (100-blocked)/100
	if((!damage || (!forced && hit_percent <= 0)))
		return 0
	var/damage_amount = forced ? damage : damage * hit_percent
	switch(damagetype)
		if(BRUTE)
			adjustBruteLoss(damage_amount, forced = forced)
		if(BURN)
			adjustFireLoss(damage_amount, forced = forced)
	return 1

/mob/living/silicon/apply_effect(effect = 0,effecttype = EFFECT_STUN, blocked = FALSE)
	return FALSE //The only effect that can hit them atm is flashes and they still directly edit so this works for now. (This was written in at least 2016. Help)

/mob/living/silicon/adjustToxLoss(amount, updating_health = TRUE, forced = FALSE) //immune to tox damage
	return FALSE

/mob/living/silicon/setToxLoss(amount, updating_health = TRUE, forced = FALSE)
	return FALSE

/mob/living/silicon/adjustCloneLoss(amount, updating_health = TRUE, forced = FALSE) //immune to clone damage
	return FALSE

/mob/living/silicon/setCloneLoss(amount, updating_health = TRUE, forced = FALSE)
	return FALSE

/mob/living/silicon/adjustStaminaLoss(amount, updating_health = TRUE, forced = FALSE) //immune to stamina damage.
	return FALSE

/mob/living/silicon/setStaminaLoss(amount, updating_health = TRUE)
	return FALSE

/mob/living/silicon/adjustOrganLoss(slot, amount, maximum = 500) //immune to organ damage (no organs, duh)
	return FALSE

/mob/living/silicon/setOrganLoss(slot, amount)
	return FALSE

/mob/living/silicon/adjustOxyLoss(amount, updating_health = TRUE, forced = FALSE) //immune to oxygen damage
	if(istype(src, /mob/living/silicon/ai)) //ais are snowflakes and use oxyloss for being in AI cards and having no battery
		return ..()

	return FALSE

/mob/living/silicon/setOxyLoss(amount, updating_health = TRUE, forced = FALSE)
	if(istype(src, /mob/living/silicon/ai)) //ditto
		return ..()

	return FALSE


code:
/mob/living/silicon/spawn_gibs()
	new /obj/effect/gibspawner/robot(drop_location(), src)

/mob/living/silicon/spawn_dust()
	new /obj/effect/decal/remains/robot(loc)

/mob/living/silicon/death(gibbed)
	if(!gibbed)
		emote("deathgasp")
	diag_hud_set_status()
	diag_hud_set_health()
	update_health_hud()
	return ..()


code:
/mob/living/silicon/examine(mob/user) //Displays a silicon's laws to ghosts
	. = ..()
	if(laws && isobserver(user))
		. += "<b>[src] has the following laws:</b>"
		for(var/law in laws.get_law_list(include_zeroth = TRUE))
			. += law


code:
/mob/living/silicon/proc/show_laws() //Redefined in ai/laws.dm and robot/laws.dm
	return

/mob/living/silicon/proc/laws_sanity_check()
	if (!laws)
		make_laws()

/mob/living/silicon/proc/deadchat_lawchange()
	var/list/the_laws = laws.get_law_list(include_zeroth = TRUE)
	var/lawtext = the_laws.Join("
")
	deadchat_broadcast("'s laws were changed. View", span_name("[src]"), follow_target=src, message_type=DEADCHAT_LAWCHANGE)

/mob/living/silicon/proc/post_lawchange(announce = TRUE)
	throw_alert("newlaw", /atom/movable/screen/alert/newlaw)
	if(announce && last_lawchange_announce != world.time)
		to_chat(src, "Your laws have been changed.")
		// lawset modules cause this function to be executed multiple times in a tick, so we wait for the next tick in order to be able to see the entire lawset
		addtimer(CALLBACK(src, .proc/show_laws), 0)
		addtimer(CALLBACK(src, .proc/deadchat_lawchange), 0)
		last_lawchange_announce = world.time

/mob/living/silicon/proc/set_zeroth_law(law, law_borg, announce = TRUE)
	laws_sanity_check()
	laws.set_zeroth_law(law, law_borg)
	post_lawchange(announce)

/mob/living/silicon/proc/add_inherent_law(law, announce = TRUE)
	laws_sanity_check()
	laws.add_inherent_law(law)
	post_lawchange(announce)

/mob/living/silicon/proc/clear_inherent_laws(announce = TRUE)
	laws_sanity_check()
	laws.clear_inherent_laws()
	post_lawchange(announce)

/mob/living/silicon/proc/add_supplied_law(number, law, announce = TRUE)
	laws_sanity_check()
	laws.add_supplied_law(number, law)
	post_lawchange(announce)

/mob/living/silicon/proc/clear_supplied_laws(announce = TRUE)
	laws_sanity_check()
	laws.clear_supplied_laws()
	post_lawchange(announce)

/mob/living/silicon/proc/add_ion_law(law, announce = TRUE)
	laws_sanity_check()
	laws.add_ion_law(law)
	post_lawchange(announce)

/mob/living/silicon/proc/add_hacked_law(law, announce = TRUE)
	laws_sanity_check()
	laws.add_hacked_law(law)
	post_lawchange(announce)

/mob/living/silicon/proc/replace_random_law(law, groups, announce = TRUE)
	laws_sanity_check()
	. = laws.replace_random_law(law,groups)
	post_lawchange(announce)

/mob/living/silicon/proc/shuffle_laws(list/groups, announce = TRUE)
	laws_sanity_check()
	laws.shuffle_laws(groups)
	post_lawchange(announce)

/mob/living/silicon/proc/remove_law(number, announce = TRUE)
	laws_sanity_check()
	. = laws.remove_law(number)
	post_lawchange(announce)

/mob/living/silicon/proc/clear_ion_laws(announce = TRUE)
	laws_sanity_check()
	laws.clear_ion_laws()
	post_lawchange(announce)

/mob/living/silicon/proc/clear_hacked_laws(announce = TRUE)
	laws_sanity_check()
	laws.clear_hacked_laws()
	post_lawchange(announce)

/mob/living/silicon/proc/make_laws()
	laws = new /datum/ai_laws
	laws.set_laws_config()
	laws.associate(src)

/mob/living/silicon/proc/clear_zeroth_law(force, announce = TRUE)
	laws_sanity_check()
	laws.clear_zeroth_law(force)
	post_lawchange(announce)


code:
/mob/living/silicon/Login()
	if(mind)
		mind?.remove_antags_for_borging()
	return ..()

/mob/living/silicon/auto_deadmin_on_login()
	if(!client?.holder)
		return TRUE
	if(CONFIG_GET(flag/auto_deadmin_silicons) || (client.prefs?.toggles & DEADMIN_POSITION_SILICON))
		return client.holder.auto_deadmin()
	return ..()


code:
/mob/living/silicon/pai/death(gibbed)
	if(stat == DEAD)
		return
	set_stat(DEAD)
	update_sight()
	clear_fullscreens()

	//New pAI's get a brand new mind to prevent meta stuff from their previous life. This new mind causes problems down the line if it's not deleted here.
	ghostize()
	qdel(src)


code:
/mob/living/silicon/pai
	name = "pAI"
	icon = 'icons/mob/pai.dmi'
	icon_state = "repairbot"
	mouse_opacity = MOUSE_OPACITY_ICON
	density = FALSE
	hud_type = /datum/hud/pai
	pass_flags = PASSTABLE | PASSMOB
	mob_size = MOB_SIZE_TINY
	desc = "A generic pAI mobile hard-light holographics emitter. It seems to be deactivated."
	health = 500
	maxHealth = 500
	layer = BELOW_MOB_LAYER
	can_be_held = TRUE
	move_force = 0
	pull_force = 0
	move_resist = 0
	worn_slot_flags = ITEM_SLOT_HEAD
	held_lh = 'icons/mob/pai_item_lh.dmi'
	held_rh = 'icons/mob/pai_item_rh.dmi'
	head_icon = 'icons/mob/pai_item_head.dmi'
	radio = /obj/item/radio/headset/silicon/pai
	can_buckle_to = FALSE
	mobility_flags = MOBILITY_FLAGS_REST_CAPABLE_DEFAULT
	var/network = "ss13"
	var/obj/machinery/camera/current = null

	var/ram = 100 // Used as currency to purchase different abilities
	var/list/software = list()
	var/userDNA // The DNA string of our assigned user
	var/obj/item/paicard/card // The card we inhabit
	var/hacking = FALSE //Are we hacking a door?

	var/speakStatement = "states"
	var/speakExclamation = "declares"
	var/speakDoubleExclamation = "alarms"
	var/speakQuery = "queries"

	var/obj/item/pai_cable/hacking_cable // The cable we produce when hacking a door

	var/master // Name of the one who commands us
	var/master_dna // DNA string for owner verification

// Various software-specific vars

	var/temp // General error reporting text contained here will typically be shown once and cleared
	var/screen // Which screen our main window displays
	var/subscreen // Which specific function of the main screen is being displayed

	var/secHUD = 0 // Toggles whether the Security HUD is active or not
	var/medHUD = 0 // Toggles whether the Medical  HUD is active or not

	var/datum/data/record/medicalActive1 // Datacore record declarations for record software
	var/datum/data/record/medicalActive2

	var/datum/data/record/securityActive1 // Could probably just combine all these into one
	var/datum/data/record/securityActive2

	var/obj/machinery/door/hackdoor // The airlock being hacked
	var/hackprogress = 0 // Possible values: 0 - 100, >= 100 means the hack is complete and will be reset upon next check

	var/obj/item/assembly/signaler/internal/signaler // AI's signaler

	var/obj/item/instrument/piano_synth/internal_instrument
	var/obj/machinery/newscaster //pAI Newscaster
	var/obj/item/healthanalyzer/hostscan //pAI healthanalyzer

	/// Internal pAI GPS, enabled if pAI downloads GPS software, and then uses it.
	var/obj/item/gps/pai/internal_gps = null

	var/encryptmod = FALSE
	var/holoform = FALSE
	var/canholo = TRUE
	var/can_transmit = TRUE
	var/can_receive = TRUE
	var/chassis = "repairbot"
	var/list/possible_chassis = list("cat" = TRUE, "mouse" = TRUE, "monkey" = TRUE, "corgi" = FALSE, "fox" = FALSE, "repairbot" = TRUE, "rabbit" = TRUE, "bat" = FALSE, "butterfly" = FALSE, "hawk" = FALSE, "lizard" = FALSE, "duffel" = TRUE) //assoc value is whether it can be picked up.

	var/emitterhealth = 20
	var/emittermaxhealth = 20
	var/emitter_regen_per_second = 1.25
	var/emittercd = 50
	var/emitteroverloadcd = 100
	var/emittersemicd = FALSE

	var/overload_ventcrawl = 0
	var/overload_bulletblock = 0 //Why is this a good idea?
	var/overload_maxhealth = 0
	var/silent = FALSE
	var/brightness_power = 5

/mob/living/silicon/pai/add_sensors() //pAIs have to buy their HUDs
	return

/mob/living/silicon/pai/handle_atom_del(atom/A)
	if(A == hacking_cable)
		hacking_cable = null
		if(!QDELETED(card))
			card.update_appearance()
	if(A == internal_instrument)
		internal_instrument = null
	if(A == newscaster)
		newscaster = null
	if(A == signaler)
		signaler = null
	if(A == hostscan)
		hostscan = null
	if(A == internal_gps)
		internal_gps = null
	return ..()

/mob/living/silicon/pai/Destroy()
	QDEL_NULL(internal_instrument)
	QDEL_NULL(hacking_cable)
	QDEL_NULL(newscaster)
	QDEL_NULL(signaler)
	QDEL_NULL(hostscan)
	QDEL_NULL(internal_gps)
	if(!QDELETED(card) && loc != card)
		card.forceMove(drop_location())
		card.pai = null //these are otherwise handled by paicard/handle_atom_del()
		card.emotion_icon = initial(card.emotion_icon)
		card.update_appearance()
	GLOB.pai_list -= src
	return ..()

/mob/living/silicon/pai/Initialize(mapload)
	var/obj/item/paicard/P = loc
	START_PROCESSING(SSfastprocess, src)
	GLOB.pai_list += src
	make_laws()
	if(!istype(P)) //when manually spawning a pai, we create a card to put it into.
		var/newcardloc = P
		P = new /obj/item/paicard(newcardloc)
		P.setPersonality(src)
	forceMove(P)
	card = P
	job = "Personal AI"
	signaler = new /obj/item/assembly/signaler/internal(src)
	hostscan = new /obj/item/healthanalyzer(src)
	newscaster = new /obj/machinery/newscaster(src)
	if(!aicamera)
		aicamera = new /obj/item/camera/siliconcam/ai_camera(src)
		aicamera.flash_enabled = TRUE

	addtimer(CALLBACK(src, .proc/pdaconfig), 5)

	. = ..()

	emittersemicd = TRUE
	addtimer(CALLBACK(src, .proc/emittercool), 600)

	if(!holoform)
		ADD_TRAIT(src, TRAIT_IMMOBILIZED, PAI_FOLDED)
		ADD_TRAIT(src, TRAIT_HANDS_BLOCKED, PAI_FOLDED)

/mob/living/silicon/pai/proc/pdaconfig()
	//PDA
	aiPDA = new/obj/item/pda/ai(src)
	aiPDA.owner = real_name
	aiPDA.ownjob = "pAI Messenger"
	aiPDA.name = real_name + " (" + aiPDA.ownjob + ")"

/mob/living/silicon/pai/proc/process_hack(delta_time, times_fired)

	if(hacking_cable && hacking_cable.machine && istype(hacking_cable.machine, /obj/machinery/door) && hacking_cable.machine == hackdoor && get_dist(src, hackdoor) <= 1)
		hackprogress = clamp(hackprogress + (2 * delta_time), 0, 100)
	else
		temp = "Door Jack: Connection to airlock has been lost. Hack aborted."
		hackprogress = 0
		hacking = FALSE
		hackdoor = null
		QDEL_NULL(hacking_cable)
		if(!QDELETED(card))
			card.update_appearance()
		return
	if(screen == "doorjack" && subscreen == 0) // Update our view, if appropriate
		paiInterface()
	if(hackprogress >= 100)
		hackprogress = 0
		var/obj/machinery/door/D = hacking_cable.machine
		D.open()
		hacking = FALSE

/mob/living/silicon/pai/make_laws()
	laws = new /datum/ai_laws/pai()
	return TRUE

/mob/living/silicon/pai/Login()
	. = ..()
	if(!. || !client)
		return FALSE

	client.perspective = EYE_PERSPECTIVE
	if(holoform)
		client.eye = src
	else
		client.eye = card

/mob/living/silicon/pai/get_status_tab_items()
	. += ..()
	if(!stat)
		. += text("Emitter Integrity: [emitterhealth * (100/emittermaxhealth)]")
	else
		. += text("Systems nonfunctional")

// See software.dm for Topic()

/mob/living/silicon/pai/canUseTopic(atom/movable/M, be_close=FALSE, no_dexterity=FALSE, no_tk=FALSE, need_hands = FALSE, floor_okay=FALSE)
	return ..(M, be_close, no_dexterity, no_tk, need_hands, TRUE) //Resting is just an aesthetic feature for them.

/mob/proc/makePAI(delold)
	var/obj/item/paicard/card = new /obj/item/paicard(get_turf(src))
	var/mob/living/silicon/pai/pai = new /mob/living/silicon/pai(card)
	pai.key = key
	pai.name = name
	card.setPersonality(pai)
	if(delold)
		qdel(src)

/datum/action/innate/pai
	name = "PAI Action"
	icon_icon = 'icons/mob/actions/actions_silicon.dmi'
	var/mob/living/silicon/pai/P

/datum/action/innate/pai/Trigger()
	if(!ispAI(owner))
		return 0
	P = owner

/datum/action/innate/pai/software
	name = "Software Interface"
	button_icon_state = "pai"
	background_icon_state = "bg_tech"

/datum/action/innate/pai/software/Trigger()
	..()
	P.paiInterface()

/datum/action/innate/pai/shell
	name = "Toggle Holoform"
	button_icon_state = "pai_holoform"
	background_icon_state = "bg_tech"

/datum/action/innate/pai/shell/Trigger()
	..()
	if(P.holoform)
		P.fold_in(0)
	else
		P.fold_out()

/datum/action/innate/pai/chassis
	name = "Holochassis Appearance Composite"
	button_icon_state = "pai_chassis"
	background_icon_state = "bg_tech"

/datum/action/innate/pai/chassis/Trigger()
	..()
	P.choose_chassis()

/datum/action/innate/pai/rest
	name = "Rest"
	button_icon_state = "pai_rest"
	background_icon_state = "bg_tech"

/datum/action/innate/pai/rest/Trigger()
	..()
	P.toggle_resting()

/datum/action/innate/pai/light
	name = "Toggle Integrated Lights"
	icon_icon = 'icons/mob/actions/actions_spells.dmi'
	button_icon_state = "emp"
	background_icon_state = "bg_tech"

/datum/action/innate/pai/light/Trigger()
	..()
	P.toggle_integrated_light()

/mob/living/silicon/pai/Process_Spacemove(movement_dir = 0)
	. = ..()
	if(!.)
		add_movespeed_modifier(/datum/movespeed_modifier/pai_spacewalk)
		return TRUE
	remove_movespeed_modifier(/datum/movespeed_modifier/pai_spacewalk)
	return TRUE

/mob/living/silicon/pai/examine(mob/user)
	. = ..()
	. += "A personal AI in holochassis mode. Its master ID string seems to be [master]."

/mob/living/silicon/pai/Life(delta_time = SSMOBS_DT, times_fired)
	. = ..()
	if(QDELETED(src) || stat == DEAD)
		return
	if(hacking_cable)
		if(get_dist(src, hacking_cable) > 1)
			var/turf/T = get_turf(src)
			T.visible_message(span_warning("[hacking_cable] rapidly retracts back into its spool."), span_hear("You hear a click and the sound of wire spooling rapidly."))
			QDEL_NULL(hacking_cable)
			if(!QDELETED(card))
				card.update_appearance()
		else if(hacking)
			process_hack(delta_time, times_fired)
	silent = max(silent - (0.5 * delta_time), 0)

/mob/living/silicon/pai/updatehealth()
	if(status_flags & GODMODE)
		return
	set_health(maxHealth - getBruteLoss() - getFireLoss())
	update_stat()

/mob/living/silicon/pai/process(delta_time)
	emitterhealth = clamp((emitterhealth + (emitter_regen_per_second * delta_time)), -50, emittermaxhealth)

/mob/living/silicon/pai/can_interact_with(atom/A)
	if(A == signaler) // Bypass for signaler
		return TRUE

	return ..()

/obj/item/paicard/attackby(obj/item/used, mob/user, params)
	if(pai && (istype(used, /obj/item/encryptionkey) || used.tool_behaviour == TOOL_SCREWDRIVER))
		if(!pai.encryptmod)
			to_chat(user, span_alert("Encryption Key ports not configured."))
			return
		user.set_machine(src)
		pai.radio.attackby(used, user, params)
		to_chat(user, span_notice("You insert [used] into the [src]."))
		return

	return ..()

/obj/item/paicard/emag_act(mob/user) // Emag to wipe the master DNA and supplemental directive
	if(!pai)
		return
	to_chat(user, span_notice("You override [pai]'s directive system, clearing its master string and supplied directive."))
	to_chat(pai, span_userdanger("Warning: System override detected, check directive sub-system for any changes."))
	log_game("[key_name(user)] emagged [key_name(pai)], wiping their master DNA and supplemental directive.")
	pai.master = null
	pai.master_dna = null
	pai.laws.supplied[1] = "None." // Sets supplemental directive to this


code:
/mob/living/silicon/pai/blob_act(obj/structure/blob/B)
	return FALSE

/mob/living/silicon/pai/emp_act(severity)
	. = ..()
	if(. & EMP_PROTECT_SELF)
		return
	take_holo_damage(50/severity)
	Paralyze(400/severity)
	silent = max(20/severity, silent)
	if(holoform)
		fold_in(force = TRUE)
	//Need more effects that aren't instadeath or permanent law corruption.
	//Ask and you shall receive
	switch(rand(1, 3))
		if(1)
			stuttering += 30/severity //temporary, clears in a few ticks after silent is over.
			to_chat(src, span_danger("Warning: Feedback loop detected in speech module."))
		if(2)
			slurring = INFINITY // permanent until speech is fixed through the pAI card UI by someone else.
			to_chat(src, span_danger("Warning: Audio synthesizer CPU stuck."))
		if(3)
			derpspeech = 1 // Ditto.
			to_chat(src, span_danger("Warning: Vocabulary databank corrupted."))
	if(prob(40))
		mind.language_holder.selected_language = get_random_spoken_language()

/mob/living/silicon/pai/ex_act(severity, target)
	take_holo_damage(50 * severity)
	switch(severity)
		if(EXPLODE_DEVASTATE) //RIP
			qdel(card)
			qdel(src)
		if(EXPLODE_HEAVY)
			fold_in(force = 1)
			Paralyze(400)
		if(EXPLODE_LIGHT)
			fold_in(force = 1)
			Paralyze(200)

/mob/living/silicon/pai/attack_hand(mob/living/carbon/human/user, list/modifiers)
	//if(user.combat_mode) //SEPTIC EDIT REMOVAL
	//SEPTIC EDIT BEGIN
	if(!IS_HELP_INTENT(user, modifiers))
	//SEPTIC EDIT END
		user.do_attack_animation(src)
		if (user.name == master)
			visible_message(span_notice("Responding to its master's touch, [src] disengages its holochassis emitter, rapidly losing coherence."))
			if(do_after(user, 1 SECONDS, TRUE, src))
				fold_in()
				if(user.put_in_hands(card))
					user.visible_message(span_notice("[user] promptly scoops up [user.p_their()] pAI's card."))
		else
			visible_message(span_danger("[user] stomps on [src]!."))
			take_holo_damage(2)
	else
		visible_message(span_notice("[user] gently pats [src] on the head, eliciting an off-putting buzzing from its holographic field."))

/mob/living/silicon/pai/bullet_act(obj/projectile/Proj)
	if(Proj.stun)
		fold_in(force = TRUE)
		src.visible_message(span_warning("The electrically-charged projectile disrupts [src]'s holomatrix, forcing [src] to fold in!"))
	. = ..(Proj)

/mob/living/silicon/pai/stripPanelUnequip(obj/item/what, mob/who, where) //prevents stripping
	to_chat(src, span_warning("Your holochassis stutters and warps intensely as you attempt to interact with the object, forcing you to cease lest the field fail."))

/mob/living/silicon/pai/stripPanelEquip(obj/item/what, mob/who, where) //prevents stripping
	to_chat(src, span_warning("Your holochassis stutters and warps intensely as you attempt to interact with the object, forcing you to cease lest the field fail."))

/mob/living/silicon/pai/IgniteMob(mob/living/silicon/pai/P)
	return FALSE //No we're not flammable

/mob/living/silicon/pai/proc/take_holo_damage(amount)
	emitterhealth = clamp((emitterhealth - amount), -50, emittermaxhealth)
	if(emitterhealth < 0)
		fold_in(force = TRUE)
	if (amount > 0)
		to_chat(src, span_userdanger("The impact degrades your holochassis!"))
	return amount

/mob/living/silicon/pai/adjustBruteLoss(amount, updating_health = TRUE, forced = FALSE)
	return take_holo_damage(amount)

/mob/living/silicon/pai/adjustFireLoss(amount, updating_health = TRUE, forced = FALSE)
	return take_holo_damage(amount)

/mob/living/silicon/pai/adjustStaminaLoss(amount, updating_health, forced = FALSE)
	if(forced)
		take_holo_damage(amount)
	else
		take_holo_damage(amount * 0.25)

/mob/living/silicon/pai/getBruteLoss()
	return emittermaxhealth - emitterhealth

/mob/living/silicon/pai/getFireLoss()
	return emittermaxhealth - emitterhealth


code:
/mob/living/silicon/pai/say(message, bubble_type, list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null, filterproof = null)
	if(silent)
		to_chat(src, span_warning("Communication circuits remain uninitialized."))
	else
		..()

/mob/living/silicon/pai/binarycheck()
	return radio?.translate_binary


code:
/mob/living/silicon/pai/proc/fold_out(force = FALSE)
	if(emitterhealth < 0)
		to_chat(src, span_warning("Your holochassis emitters are still too unstable! Please wait for automatic repair."))
		return FALSE

	if(!canholo && !force)
		to_chat(src, span_warning("Your master or another force has disabled your holochassis emitters!"))
		return FALSE

	if(holoform)
		. = fold_in(force)
		return

	if(emittersemicd)
		to_chat(src, span_warning("Error: Holochassis emitters recycling. Please try again later."))
		return FALSE

	emittersemicd = TRUE
	addtimer(CALLBACK(src, .proc/emittercool), emittercd)
	REMOVE_TRAIT(src, TRAIT_IMMOBILIZED, PAI_FOLDED)
	REMOVE_TRAIT(src, TRAIT_HANDS_BLOCKED, PAI_FOLDED)
	set_density(TRUE)
	if(istype(card.loc, /obj/item/pda))
		var/obj/item/pda/P = card.loc
		P.pai = null
		P.visible_message(span_notice("[src] ejects itself from [P]!"))
	if(isliving(card.loc))
		var/mob/living/L = card.loc
		if(!L.temporarilyRemoveItemFromInventory(card))
			to_chat(src, span_warning("Error: Unable to expand to mobile form. Chassis is restrained by some device or person."))
			return FALSE
	forceMove(get_turf(card))
	card.forceMove(src)
	if(client)
		client.perspective = EYE_PERSPECTIVE
		client.eye = src
	set_light(0)
	icon_state = "[chassis]"
	held_state = "[chassis]"
	visible_message(span_boldnotice("[src] folds out its holochassis emitter and forms a holoshell around itself!"))
	holoform = TRUE

/mob/living/silicon/pai/proc/emittercool()
	emittersemicd = FALSE

/mob/living/silicon/pai/proc/fold_in(force = FALSE)
	emittersemicd = TRUE
	if(!force)
		addtimer(CALLBACK(src, .proc/emittercool), emittercd)
	else
		addtimer(CALLBACK(src, .proc/emittercool), emitteroverloadcd)
	icon_state = "[chassis]"
	if(!holoform)
		. = fold_out(force)
		return
	visible_message(span_notice("[src] deactivates its holochassis emitter and folds back into a compact card!"))
	stop_pulling()
	if(istype(loc, /obj/item/clothing/head/mob_holder))
		var/obj/item/clothing/head/mob_holder/MH = loc
		MH.release(display_messages = FALSE)
	if(client)
		client.perspective = EYE_PERSPECTIVE
		client.eye = card
	var/turf/T = drop_location()
	card.forceMove(T)
	forceMove(card)
	ADD_TRAIT(src, TRAIT_IMMOBILIZED, PAI_FOLDED)
	ADD_TRAIT(src, TRAIT_HANDS_BLOCKED, PAI_FOLDED)
	set_density(FALSE)
	set_light(0)
	holoform = FALSE
	set_resting(resting)

/**
 * Sets a new holochassis skin based on a pAI's choice
 */
/mob/living/silicon/pai/proc/choose_chassis()
	var/list/skins = list()
	for(var/holochassis_option in possible_chassis)
		var/image/item_image = image(icon = src.icon, icon_state = holochassis_option)
		skins += list("[holochassis_option]" = item_image)
	sort_list(skins)

	var/atom/anchor = get_atom_on_turf(src)
	var/choice = show_radial_menu(src, anchor, skins, custom_check = CALLBACK(src, .proc/check_menu, anchor), radius = 40, require_near = TRUE)
	if(!choice)
		return FALSE
	set_holochassis(choice)
	to_chat(src, span_boldnotice("You switch your holochassis projection composite to [choice]."))
	update_resting()

/**
 * Sets the holochassis skin and updates the icons
 * * Arguments:
 * * choice The animal skin that will be used for the pAI holoform
 */
/mob/living/silicon/pai/proc/set_holochassis(choice)
	if(!choice)
		return FALSE
	chassis = choice
	icon_state = "[chassis]"
	held_state = "[chassis]"

/**
 * Polymorphs the pai into a random holoform
 */
/mob/living/silicon/pai/wabbajack()
	if(length(possible_chassis) < 2)
		return
	var/holochassis = pick(possible_chassis - chassis)
	set_holochassis(holochassis)
	to_chat(src, span_boldnotice("Your holochassis form morphs into that of a [holochassis]."))

/**
 * Checks if we are allowed to interact with a radial menu
 *
 * * Arguments:
 * * anchor The atom that is anchoring the menu
 */
/mob/living/silicon/pai/proc/check_menu(atom/anchor)
	if(incapacitated())
		return FALSE
	if(get_turf(src) != get_turf(anchor))
		return FALSE
	if(!isturf(loc) && loc != card)
		to_chat(src, span_boldwarning("You can not change your holochassis composite while not on the ground or in your card!"))
		return FALSE
	return TRUE

/mob/living/silicon/pai/update_resting()
	. = ..()
	if(resting)
		icon_state = "[chassis]_rest"
	else
		icon_state = "[chassis]"
	if(loc != card)
		visible_message(span_notice("[src] [resting? "lays down for a moment..." : "perks up from the ground"]"))

/mob/living/silicon/pai/start_pulling(atom/movable/AM, state, force = move_force, supress_message = FALSE)
	return FALSE

/mob/living/silicon/pai/proc/toggle_integrated_light()
	if(!light_range)
		set_light(brightness_power)
		to_chat(src, span_notice("You enable your integrated light."))
	else
		set_light(0)
		to_chat(src, span_notice("You disable your integrated light."))

/mob/living/silicon/pai/mob_try_pickup(mob/living/user, instant=FALSE)
	if(!possible_chassis[chassis])
		to_chat(user, span_warning("[src]'s current form isn't able to be carried!"))
		return FALSE
	return ..()


code:
/*
		name
		key
		description
		role
		comments
		ready = 0
*/

/datum/pai_candidate/proc/savefile_path(mob/user)
	return "data/player_saves/[user.ckey[1]]/[user.ckey]/pai.sav"

/datum/pai_candidate/proc/savefile_save(mob/user)
	if(is_guest_key(user.key))
		return FALSE

	var/savefile/F = new /savefile(src.savefile_path(user))

	WRITE_FILE(F["name"], name)
	WRITE_FILE(F["description"], description)
	WRITE_FILE(F["role"], role)
	WRITE_FILE(F["comments"], comments)

	WRITE_FILE(F["version"], 1)

	return TRUE

// loads the savefile corresponding to the mob's ckey
// if silent=true, report incompatible savefiles
// returns 1 if loaded (or file was incompatible)
// returns 0 if savefile did not exist

/datum/pai_candidate/proc/savefile_load(mob/user, silent = TRUE)
	if (is_guest_key(user.key))
		return 0

	var/path = savefile_path(user)

	if (!fexists(path))
		return 0

	var/savefile/F = new /savefile(path)

	if(!F)
		return //Not everyone has a pai savefile.

	var/version = null
	F["version"] >> version

	if (isnull(version) || version != 1)
		fdel(path)
		if (!silent)
			tgui_alert(user, "Your savefile was incompatible with this version and was deleted.")
		return 0

	F["name"] >> src.name
	F["description"] >> src.description
	F["role"] >> src.role
	F["comments"] >> src.comments
	return 1


code:
/mob/living/silicon/robot/gib_animation()
	new /obj/effect/temp_visual/gib_animation(loc, "gibbed-r")

/mob/living/silicon/robot/dust(just_ash, drop_items, force)
	if(mmi)
		qdel(mmi)
	..()

/mob/living/silicon/robot/spawn_dust()
	new /obj/effect/decal/remains/robot(loc)

/mob/living/silicon/robot/dust_animation()
	new /obj/effect/temp_visual/dust_animation(loc, "dust-r")

/mob/living/silicon/robot/death(gibbed)
	if(stat == DEAD)
		return
	if(!gibbed)
		logevent("FATAL -- SYSTEM HALT")
		modularInterface.shutdown_computer()
	. = ..()

	locked = FALSE //unlock cover

	if(!QDELETED(builtInCamera) && builtInCamera.status)
		builtInCamera.toggle_cam(src,0)
	toggle_headlamp(TRUE) //So borg lights are disabled when killed.

	uneq_all() // particularly to ensure sight modes are cleared

	update_icons()

	unbuckle_all_mobs(TRUE)

	SSblackbox.ReportDeath(src)


code:
/datum/emote/silicon
	mob_type_allowed_typecache = list(/mob/living/silicon)
	emote_type = EMOTE_AUDIBLE

/datum/emote/silicon/boop
	key = "boop"
	key_third_person = "boops"
	message = "boops."

/datum/emote/silicon/buzz
	key = "buzz"
	key_third_person = "buzzes"
	message = "buzzes."
	message_param = "buzzes at %t."
	emote_type = EMOTE_AUDIBLE
	sound = 'sound/machines/buzz-sigh.ogg'

/datum/emote/silicon/buzz2
	key = "buzz2"
	message = "buzzes twice."
	emote_type = EMOTE_AUDIBLE
	sound = 'sound/machines/buzz-two.ogg'

/datum/emote/silicon/chime
	key = "chime"
	key_third_person = "chimes"
	message = "chimes."
	emote_type = EMOTE_AUDIBLE
	sound = 'sound/machines/chime.ogg'

/datum/emote/silicon/honk
	key = "honk"
	key_third_person = "honks"
	message = "honks."
	emote_type = EMOTE_AUDIBLE
	vary = TRUE
	sound = 'sound/items/bikehorn.ogg'

/datum/emote/silicon/ping
	key = "ping"
	key_third_person = "pings"
	message = "pings."
	message_param = "pings at %t."
	emote_type = EMOTE_AUDIBLE
	sound = 'sound/machines/ping.ogg'

/datum/emote/silicon/sad
	key = "sad"
	message = "plays a sad trombone..."
	emote_type = EMOTE_AUDIBLE
	sound = 'sound/misc/sadtrombone.ogg'

/datum/emote/silicon/warn
	key = "warn"
	message = "blares an alarm!"
	emote_type = EMOTE_AUDIBLE
	sound = 'sound/machines/warning-buzzer.ogg'

/datum/emote/silicon/slowclap
	key = "slowclap"
	message = "activates their slow clap processor."
	emote_type = EMOTE_AUDIBLE
	sound = 'sound/machines/slowclap.ogg'


code:
/mob/living/silicon/robot/examine(mob/user)
	. = list("*---------*\nThis is [icon2html(src, user)] \a [src]!")
	if(desc)
		. += "[desc]"

	var/obj/act_module = get_active_held_item()
	if(act_module)
		. += "It is holding [icon2html(act_module, user)] \a [act_module]."
	. += status_effect_examines()
	if (getBruteLoss())
		if (getBruteLoss() < maxHealth*0.5)
			. += span_warning("It looks slightly dented.")
		else
			. += span_warning("It looks severely dented!")
	if (getFireLoss() || getToxLoss())
		var/overall_fireloss = getFireLoss() + getToxLoss()
		if (overall_fireloss < maxHealth * 0.5)
			. += span_warning("It looks slightly charred.")
		else
			. += span_warning("It looks severely burnt and heat-warped!")
	if (health < -maxHealth*0.5)
		. += span_warning("It looks barely operational.")
	if (fire_stacks < 0)
		. += span_warning("It's covered in water.")
	else if (fire_stacks > 0)
		. += span_warning("It's coated in something flammable.")

	if(opened)
		. += span_warning("Its cover is open and the power cell is [cell ? "installed" : "missing"].")
	else
		. += "Its cover is closed[locked ? "" : ", and looks unlocked"]."

	if(cell && cell.charge <= 0)
		. += span_warning("Its battery indicator is blinking red!")

	switch(stat)
		if(CONSCIOUS)
			if(shell)
				. += "It appears to be an [deployed ? "active" : "empty"] AI shell."
			else if(!client)
				. += "It appears to be in stand-by mode." //afk
		if(SOFT_CRIT, UNCONSCIOUS, HARD_CRIT)
			. += span_warning("It doesn't seem to be responding.")
		if(DEAD)
			. += span_deadsay("It looks like its system is corrupted and requires a reset.")
	. += "*---------*"

	. += ..()