code:
var/list/image/ghost_darkness_images = list() //this is a list of images for things ghosts should still be able to see when they toggle darkness, BUT NOT THE GHOSTS THEMSELVES!
var/list/image/ghost_images_full = list() //this is a list of full images of the ghosts themselves
var/list/image/ghost_images_default = list() //this is a list of the default (non-accessorized, non-dir) images of the ghosts themselves
var/list/image/ghost_images_simple = list() //this is a list of all ghost images as the simple white ghost
/mob/dead/observer
	name = "ghost"
	desc = "It's a g-g-g-g-ghooooost!" //jinkies!
	icon = 'icons/mob/mob.dmi'
	icon_state = "ghost"
	layer = MOB_LAYER + 1
	stat = DEAD
	density = 0
	canmove = 0
	anchored = 1	//  don't get pushed around
	sight = SEE_TURFS | SEE_MOBS | SEE_OBJS | SEE_SELF
	see_invisible = SEE_INVISIBLE_OBSERVER
	see_in_dark = 100
	invisibility = INVISIBILITY_OBSERVER
	languages = ALL
	var/can_reenter_corpse
	var/datum/hud/living/carbon/hud = null // hud
	var/bootime = 0
	var/started_as_observer //This variable is set to 1 when you enter the game as an observer.
							//If you died in the game and are a ghsot - this will remain as null.
							//Note that this is not a reliable way to determine if admins started as observers, since they change mobs a lot.
	var/atom/movable/following = null
	var/fun_verbs = 0
	var/image/ghostimage = null //this mobs ghost image, for deleting and stuff
	var/image/ghostimage_default = null //this mobs ghost image without accessories and dirs
	var/image/ghostimage_simple = null //this mob with the simple white ghost sprite
	var/ghostvision = 1 //is the ghost able to see things humans can't?
	var/seedarkness = 1
	var/ghost_hud_enabled = 1 //did this ghost disable the on-screen HUD?
	var/data_hud_seen = 0 //this should one of the defines in __DEFINES/hud.dm
	var/ghost_orbit = GHOST_ORBIT_CIRCLE

	//These variables store hair data if the ghost originates from a species with head and/or facial hair.
	var/hair_style
	var/hair_color
	var/image/hair_image
	var/facial_hair_style
	var/facial_hair_color
	var/image/facial_hair_image

	var/updatedir = 1						//Do we have to update our dir as the ghost moves around?
	var/lastsetting = null	//Stores the last setting that ghost_others was set to, for a little more efficiency when we update ghost images. Null means no update is necessary

	//We store copies of the ghost display preferences locally so they can be referred to even if no client is connected.
	//If there's a bug with changing your ghost settings, it's probably related to this.
	var/ghost_accs = GHOST_ACCS_DEFAULT_OPTION
	var/ghost_others = GHOST_OTHERS_DEFAULT_OPTION

/mob/dead/observer/New(mob/body)
	verbs += /mob/dead/observer/proc/dead_tele

	ghostimage = image(src.icon,src,src.icon_state)
	if(icon_state in ghost_forms_with_directions_list)
		ghostimage_default = image(src.icon,src,src.icon_state + "_nodir")
	else
		ghostimage_default = image(src.icon,src,src.icon_state)
	ghostimage_simple = image(src.icon,src,"ghost_nodir")
	ghost_images_full |= ghostimage
	ghost_images_default |= ghostimage_default
	ghost_images_simple |= ghostimage_simple
	updateallghostimages()

	var/turf/T
	if(ismob(body))
		T = get_turf(body)				//Where is the body located?
		attack_log = body.attack_log	//preserve our attack logs by copying them to our ghost

		gender = body.gender
		if(body.mind && body.mind.name)
			name = body.mind.name
		else
			if(body.real_name)
				name = body.real_name
			else
				name = random_unique_name(gender)

		mind = body.mind	//we don't transfer the mind but we keep a reference to it.
		if(ishuman(body))
			var/mob/living/carbon/human/body_human = body
			if(HAIR in body_human.dna.species.specflags)
				hair_style = body_human.hair_style
				hair_color = brighten_color(body_human.hair_color)
			if(FACEHAIR in body_human.dna.species.specflags)
				facial_hair_style = body_human.facial_hair_style
				facial_hair_color = brighten_color(body_human.facial_hair_color)

	update_icon()

	if(!T)
		T = pick(latejoin)			//Safety in case we cannot find the body's position
	loc = T

	if(!name)							//To prevent nameless ghosts
		name = random_unique_name(gender)
	real_name = name

	if(!fun_verbs)
		verbs -= /mob/dead/observer/verb/boo
		verbs -= /mob/dead/observer/verb/possess

	animate(src, pixel_y = 2, time = 10, loop = -1)
	..()

/mob/dead/observer/Destroy()
	if (ghostimage)
		ghost_darkness_images -= ghostimage
		qdel(ghostimage)
		ghostimage = null
		updateallghostimages()
	return ..()

/mob/dead/CanPass(atom/movable/mover, turf/target, height=0)
	return 1

/*
 * This proc will update the icon of the ghost itself, with hair overlays, as well as the ghost image.
 * Please call update_icon(icon_state) from now on when you want to update the icon_state of the ghost,
 * or you might end up with hair on a sprite that's not supposed to get it.
 * Hair will always update its dir, so if your sprite has no dirs the haircut will go all over the place.
 * |- Ricotez
 */
/mob/dead/observer/proc/update_icon(new_form)
	if(client) //We update our preferences in case they changed right before update_icon was called.
		ghost_accs = client.prefs.ghost_accs
		ghost_others = client.prefs.ghost_others

	if(hair_image)
		overlays -= hair_image
		ghostimage.overlays -= hair_image
		hair_image = null

	if(facial_hair_image)
		overlays -= facial_hair_image
		ghostimage.overlays -= facial_hair_image
		facial_hair_image = null

	if(new_form)
		icon_state = new_form
		if(icon_state in ghost_forms_with_directions_list)
			ghostimage_default.icon_state = new_form + "_nodir" //if this icon has dirs, the default ghostimage must use its nodir version or clients with the preference set to default sprites only will see the dirs
		else
			ghostimage_default.icon_state = new_form

	if(ghost_accs >= GHOST_ACCS_DIR && icon_state in ghost_forms_with_directions_list) //if this icon has dirs AND the client wants to show them, we make sure we update the dir on movement
		updatedir = 1
	else
		updatedir = 0	//stop updating the dir in case we want to show accessories with dirs on a ghost sprite without dirs
		dir = 2 		//reset the dir to its default so the sprites all properly align up

	if(ghost_accs == GHOST_ACCS_FULL && icon_state in ghost_forms_with_accessories_list) //check if this form supports accessories and if the client wants to show them
		var/datum/sprite_accessory/S
		if(facial_hair_style)
			S = facial_hair_styles_list[facial_hair_style]
			if(S)
				facial_hair_image = image("icon" = S.icon, "icon_state" = "[S.icon_state]_s", "layer" = -HAIR_LAYER)
				if(facial_hair_color)
					facial_hair_image.color = "#" + facial_hair_color
				facial_hair_image.alpha = 200
				overlays += facial_hair_image
				ghostimage.overlays += facial_hair_image
		if(hair_style)
			S = hair_styles_list[hair_style]
			if(S)
				hair_image = image("icon" = S.icon, "icon_state" = "[S.icon_state]_s", "layer" = -HAIR_LAYER)
				if(hair_color)
					hair_image.color = "#" + hair_color
				hair_image.alpha = 200
				overlays += hair_image
				ghostimage.overlays += hair_image

/*
 * Increase the brightness of a color by calculating the average distance between the R, G and B values,
 * and maximum brightness, then adding 30% of that average to R, G and B.
 *
 * I'll make this proc global and move it to its own file in a future update. |- Ricotez
 */
/mob/proc/brighten_color(input_color)
	var/r_val
	var/b_val
	var/g_val
	var/color_format = lentext(input_color)
	if(color_format == 3)
		r_val = hex2num(copytext(input_color, 1, 2))*16
		g_val = hex2num(copytext(input_color, 2, 3))*16
		b_val = hex2num(copytext(input_color, 3, 0))*16
	else if(color_format == 6)
		r_val = hex2num(copytext(input_color, 1, 3))
		g_val = hex2num(copytext(input_color, 3, 5))
		b_val = hex2num(copytext(input_color, 5, 0))
	else
		return 0 //If the color format is not 3 or 6, you're using an unexpected way to represent a color.

	r_val += (255 - r_val) * 0.4
	if(r_val > 255)
		r_val = 255
	g_val += (255 - g_val) * 0.4
	if(g_val > 255)
		g_val = 255
	b_val += (255 - b_val) * 0.4
	if(b_val > 255)
		b_val = 255

	return num2hex(r_val, 2) + num2hex(g_val, 2) + num2hex(b_val, 2)

/*
Transfer_mind is there to check if mob is being deleted/not going to have a body.
Works together with spawning an observer, noted above.
*/

/mob/proc/ghostize(can_reenter_corpse = 1)
	if(key)
		if(!cmptext(copytext(key,1,2),"@")) // Skip aghosts.
			var/mob/dead/observer/ghost = new(src)	// Transfer safety to observer spawning proc.
			SStgui.on_transfer(src, ghost) // Transfer NanoUIs.
			ghost.can_reenter_corpse = can_reenter_corpse
			ghost.key = key
			return ghost

/*
This is the proc mobs get to turn into a ghost. Forked from ghostize due to compatibility issues.
*/
/mob/living/verb/ghost()
	set category = "OOC"
	set name = "Ghost"
	set desc = "Relinquish your life and enter the land of the dead."

	if(stat != DEAD)
		succumb()
	if(stat == DEAD)
		ghostize(1)
	else
		var/response = alert(src, "Are you -sure- you want to ghost?\n(You are alive. If you ghost whilst still alive you may not play again this round! You can't change your mind so choose wisely!!)","Are you sure you want to ghost?","Ghost","Stay in body")
		if(response != "Ghost")
			return	//didn't want to ghost after-all
		ghostize(0)						//0 parameter is so we can never re-enter our body, "Charlie, you can never come baaaack~" :3
	return

/mob/dead/observer/Move(NewLoc, direct)
	if(updatedir)
		dir = direct //only update dir if we actually need it, so overlays won't spin on base sprites that don't have directions of their own
	if(NewLoc)
		loc = NewLoc
		for(var/obj/effect/step_trigger/S in NewLoc)
			S.Crossed(src)

		return
	loc = get_turf(src) //Get out of closets and such as a ghost
	if((direct & NORTH) && y < world.maxy)
		y++
	else if((direct & SOUTH) && y > 1)
		y--
	if((direct & EAST) && x < world.maxx)
		x++
	else if((direct & WEST) && x > 1)
		x--

	for(var/obj/effect/step_trigger/S in locate(x, y, z))	//<-- this is dumb
		S.Crossed(src)

/mob/dead/observer/is_active()
	return 0

/mob/dead/observer/Stat()
	..()
	if(statpanel("Status"))
		stat(null, "Station Time: [worldtime2text()]")
		if(ticker)
			if(ticker.mode)
				for(var/datum/gang/G in ticker.mode.gangs)
					if(isnum(G.dom_timer))
						stat(null, "[G.name] Gang Takeover: [max(G.dom_timer, 0)]")

/mob/dead/observer/verb/reenter_corpse()
	set category = "Ghost"
	set name = "Re-enter Corpse"
	if(!client)
		return
	if(!(mind && mind.current))
		src << "You have no body."
		return
	if(!can_reenter_corpse)
		src << "You cannot re-enter your body."
		return
	if(mind.current.key && copytext(mind.current.key,1,2)!="@")	//makes sure we don't accidentally kick any clients
		usr << "Another consciousness is in your body...It is resisting you."
		return
	SStgui.on_transfer(src, mind.current) // Transfer NanoUIs.
	mind.current.key = key
	return 1

/mob/dead/observer/proc/notify_cloning(var/message, var/sound, var/atom/source)
	if(message)
		src << "[message]"
		if(source)
			var/obj/screen/alert/A = throw_alert("\ref[source]_notify_cloning", /obj/screen/alert/notify_cloning)
			if(A)
				if(client && client.prefs && client.prefs.UI_style)
					A.icon = ui_style2icon(client.prefs.UI_style)
				A.desc = message
				var/old_layer = source.layer
				source.layer = FLOAT_LAYER
				A.overlays += source
				source.layer = old_layer
	src << "(Click to re-enter)"
	if(sound)
		src << sound(sound)

/mob/dead/observer/proc/dead_tele()
	set category = "Ghost"
	set name = "Teleport"
	set desc= "Teleport to a location"
	if(!istype(usr, /mob/dead/observer))
		usr << "Not when you're not dead!"
		return
	var/A
	A = input("Area to jump to", "BOOYEA", A) as null|anything in sortedAreas
	var/area/thearea = A
	if(!thearea)
		return

	var/list/L = list()
	for(var/turf/T in get_area_turfs(thearea.type))
		L+=T

	if(!L || !L.len)
		usr << "No area available."

	usr.loc = pick(L)

/mob/dead/observer/verb/follow()
	set category = "Ghost"
	set name = "Orbit" // "Haunt"
	set desc = "Follow and orbit a mob."

	var/list/mobs = getpois(skip_mindless=1)
	var/input = input("Please, select a mob!", "Haunt", null, null) as null|anything in mobs
	var/mob/target = mobs[input]
	ManualFollow(target)

// This is the ghost's follow verb with an argument
/mob/dead/observer/proc/ManualFollow(atom/movable/target)
	if (!istype(target))
		return

	var/icon/I = icon(target.icon,target.icon_state,target.dir)

	var/orbitsize = (I.Width()+I.Height())*0.5
	orbitsize -= (orbitsize/world.icon_size)*(world.icon_size*0.25)

	if(orbiting != target)
		src << "Now orbiting [target]."

	var/rot_seg

	switch(ghost_orbit)
		if(GHOST_ORBIT_TRIANGLE)
			rot_seg = 3
		if(GHOST_ORBIT_SQUARE)
			rot_seg = 4
		if(GHOST_ORBIT_PENTAGON)
			rot_seg = 5
		if(GHOST_ORBIT_HEXAGON)
			rot_seg = 6
		else //Circular
			rot_seg = 36 //360/10 bby, smooth enough aproximation of a circle

	orbit(target,orbitsize, FALSE, 20, rot_seg)

/mob/dead/observer/orbit()
	dir = 2 //reset dir so the right directional sprites show up
	..()
	//restart our floating animation after orbit is done.
	sleep 2  //orbit sets up a 2ds animation when it finishes, so we wait for that to end
	if (!orbiting) //make sure another orbit hasn't started
		pixel_y = 0
		animate(src, pixel_y = 2, time = 10, loop = -1)

/mob/dead/observer/verb/jumptomob() //Moves the ghost instead of just changing the ghosts's eye -Nodrak
	set category = "Ghost"
	set name = "Jump to Mob"
	set desc = "Teleport to a mob"

	if(istype(usr, /mob/dead/observer)) //Make sure they're an observer!

		var/list/dest = list() //List of possible destinations (mobs)
		var/target = null	   //Chosen target.

		dest += getpois(mobs_only=1) //Fill list, prompt user with list
		target = input("Please, select a player!", "Jump to Mob", null, null) as null|anything in dest

		if (!target)//Make sure we actually have a target
			return
		else
			var/mob/M = dest[target] //Destination mob
			var/mob/A = src			 //Source mob
			var/turf/T = get_turf(M) //Turf of the destination mob

			if(T && isturf(T))	//Make sure the turf exists, then move the source to that destination.
				A.loc = T
			else
				A << "This mob is not located in the game world."

/mob/dead/observer/verb/boo()
	set category = "Ghost"
	set name = "Boo!"
	set desc= "Scare your crew members because of boredom!"

	if(bootime > world.time) return
	var/obj/machinery/light/L = locate(/obj/machinery/light) in view(1, src)
	if(L)
		L.flicker()
		bootime = world.time + 600
		return
	//Maybe in the future we can add more spooky code here!
	return

/mob/dead/observer/memory()
	set hidden = 1
	src << "You are dead! You have no mind to store memory!"

/mob/dead/observer/add_memory()
	set hidden = 1
	src << "You are dead! You have no mind to store memory!"

/mob/dead/observer/verb/toggle_ghostsee()
	set name = "Toggle Ghost Vision"
	set desc = "Toggles your ability to see things only ghosts can see, like other ghosts"
	set category = "Ghost"
	ghostvision = !(ghostvision)
	updateghostsight()
	usr << "You [(ghostvision?"now":"no longer")] have ghost vision."

/mob/dead/observer/verb/toggle_darkness()
	set name = "Toggle Darkness"
	set category = "Ghost"
	seedarkness = !(seedarkness)
	updateghostsight()

/mob/dead/observer/proc/updateghostsight()
	if(client)
		ghost_others = client.prefs.ghost_others //A quick update just in case this setting was changed right before calling the proc

	if (seedarkness)
		see_invisible = SEE_INVISIBLE_OBSERVER
		if (!ghostvision || ghost_others <= GHOST_OTHERS_DEFAULT_SPRITE)
			see_invisible = SEE_INVISIBLE_LIVING
	else
		see_invisible = SEE_INVISIBLE_NOLIGHTING

	updateghostimages()

/proc/updateallghostimages()
	for (var/mob/dead/observer/O in player_list)
		O.updateghostimages()

/mob/dead/observer/proc/updateghostimages()
	if (!client)
		return

	if(lastsetting)
		switch(lastsetting) //checks the setting we last came from, for a little efficiency so we don't try to delete images from the client that it doesn't have anyway
			if(GHOST_OTHERS_THEIR_SETTING)
				client.images -= ghost_images_full
			if(GHOST_OTHERS_DEFAULT_SPRITE)
				client.images -= ghost_images_default
			if(GHOST_OTHERS_SIMPLE)
				client.images -= ghost_images_simple

	if ((seedarkness || !ghostvision) && client.prefs.ghost_others == GHOST_OTHERS_THEIR_SETTING)
		client.images -= ghost_darkness_images
		lastsetting = null
	else if(ghostvision && (!seedarkness || client.prefs.ghost_others <= GHOST_OTHERS_DEFAULT_SPRITE))
		//add images for the 60inv things ghosts can normally see when darkness is enabled so they can see them now
		if(!lastsetting)
			client.images |= ghost_darkness_images
		switch(client.prefs.ghost_others)
			if(GHOST_OTHERS_THEIR_SETTING)
				client.images |= ghost_images_full
				if (ghostimage)
					client.images -= ghostimage //remove ourself
			if(GHOST_OTHERS_DEFAULT_SPRITE)
				client.images |= ghost_images_default
				if(ghostimage_default)
					client.images -= ghostimage_default
			if(GHOST_OTHERS_SIMPLE)
				client.images |= ghost_images_simple
				if(ghostimage_simple)
					client.images -= ghostimage_simple
		lastsetting = client.prefs.ghost_others

/mob/dead/observer/verb/possess()
	set category = "Ghost"
	set name = "Possess!"
	set desc= "Take over the body of a mindless creature!"

	var/list/possessible = list()
	for(var/mob/living/L in living_mob_list)
		if(!(L in player_list) && !L.mind)
			possessible += L

	var/mob/living/target = input("Your new life begins today!", "Possess Mob", null, null) as null|anything in possessible

	if(!target)
		return 0
	if(can_reenter_corpse || (mind && mind.current))
		if(alert(src, "Your soul is still tied to your former life as [mind.current.name], if you go foward there is no going back to that life. Are you sure you wish to continue?", "Move On", "Yes", "No") == "No")
			return 0
	if(target.key)
		src << "Someone has taken this body while you were choosing!"
		return 0

	target.key = key
	return 1

//this is a mob verb instead of atom for performance reasons
//see /mob/verb/examinate() in mob.dm for more info
//overriden here and in /mob/living for different point span classes and sanity checks
/mob/dead/observer/pointed(atom/A as mob|obj|turf in view())
	if(!..())
		return 0
	usr.visible_message("[src] points to [A].")
	return 1

/mob/dead/observer/verb/view_manifest()
	set name = "View Crew Manifest"
	set category = "Ghost"

	var/dat
	dat += "
Crew Manifest
"
	dat += data_core.get_manifest()

	src << browse(dat, "window=manifest;size=387x420;can_close=1")

//this is called when a ghost is drag clicked to something.
/mob/dead/observer/MouseDrop(atom/over)
	if(!usr || !over) return
	if (isobserver(usr) && usr.client.holder && isliving(over))
		if (usr.client.holder.cmd_ghost_drag(src,over))
			return

	return ..()

/mob/dead/observer/Topic(href, href_list)
	..()
	if(usr == src)
		if(href_list["follow"])
			var/atom/movable/target = locate(href_list["follow"])
			if(istype(target) && (target != src))
				ManualFollow(target)
		if(href_list["reenter"])
			reenter_corpse()

//We don't want to update the current var
//But we will still carry a mind.
/mob/dead/observer/mind_initialize()
	return

/mob/dead/observer/proc/show_me_the_hud(hud_index)
	var/datum/atom_hud/H = huds[hud_index]
	H.add_hud_to(src)
	data_hud_seen = hud_index

/mob/dead/observer/verb/toggle_ghost_med_sec_diag_hud()
	set name = "Toggle Sec/Med/Diag HUD"
	set desc = "Toggles whether you see medical/security/diagnostic HUDs"
	set category = "Ghost"

	if(data_hud_seen) //remove old huds
		var/datum/atom_hud/H = huds[data_hud_seen]
		H.remove_hud_from(src)

	switch(data_hud_seen) //give new huds
		if(0)
			show_me_the_hud(DATA_HUD_SECURITY_ADVANCED)
			src << "Security HUD set."
		if(DATA_HUD_SECURITY_ADVANCED)
			show_me_the_hud(DATA_HUD_MEDICAL_ADVANCED)
			src << "Medical HUD set."
		if(DATA_HUD_MEDICAL_ADVANCED)
			show_me_the_hud(DATA_HUD_DIAGNOSTIC)
			src << "Diagnostic HUD set."
		if(DATA_HUD_DIAGNOSTIC)
			data_hud_seen = 0
			src << "HUDs disabled."

/mob/dead/observer/canUseTopic()
	if(check_rights(R_ADMIN, 0))
		return 1
	return


code:
/mob/dead/observer/say(message)
	message = trim(copytext(sanitize(message), 1, MAX_MESSAGE_LEN))

	if (!message)
		return

	log_say("Ghost/[src.key] : [message]")

	if(jobban_isbanned(src, "OOC"))
		src << "You have been banned from deadchat."
		return

	if (src.client)
		if(src.client.prefs.muted & MUTE_DEADCHAT)
			src << "You cannot talk in deadchat (muted)."
			return

		if (src.client.handle_spam_prevention(message,MUTE_DEADCHAT))
			return

	. = src.say_dead(message)

/mob/dead/observer/Hear(message, atom/movable/speaker, message_langs, raw_message, radio_freq, list/spans)
	if(radio_freq)
		var/atom/movable/virtualspeaker/V = speaker

		if(istype(V.source, /mob/living/silicon/ai))
			var/mob/living/silicon/ai/S = V.source
			speaker = S.eyeobj
		else
			speaker = V.source
	src << "(F) [message]"

license: mit


code:
//This is the proc for gibbing a mob. Cannot gib ghosts.
//added different sort of gibs and animations. N
/mob/proc/gib()
	return

//This is the proc for turning a mob into ash. Mostly a copy of gib code (above).
//Originally created for wizard disintegrate. I've removed the virus code since it's irrelevant here.
//Dusting robots does not eject the MMI, so it's a bit more powerful than gib() /N
/mob/proc/dust()
	return

/mob/proc/death(gibbed)
	return


code:
/*
	NPC VAR EXPLANATIONS (for modules and other things)

		doing = their current action, INTERACTING, TRAVEL or FIGHTING
		interest = how interested the NPC is in the situation, if they are idle, this drops
		timeout = this is internal
		TARGET = their current target
		LAST_TARGET = their last target
		nearby = a list of nearby mobs
		best_force = the highest force object, used for checking when to swap items
		retal = this is internal
		retal_target = this is internal
		update_hands = this is a bool (1/0) to determine if the NPC should update what is in his hands

		MYID = their ID card
		MYPDA = their PDA
		main_hand = what is in their "main" hand (chosen from left > right)
		TRAITS = the traits assigned to this npc
		mymjob = the job assigned to the npc

		robustness = the chance for the npc to hit something
		smartness = the inverse chance for an npc to do stupid things
		attitude = the chance for an npc to do rude or mean things
		slyness = the chance for an npc to do naughty things ie thieving

		functions = the list of procs that the npc will use for modules

		graytide = shitmin var to make them go psycho
*/
#define NPC_SPEAK_FILE "npc_chatter.json"
/mob/living/carbon/human/interactive
	name = "interactive station member"
	var/doing = 0
	var/interest = 10
	var/maxInterest = 10
	var/timeout = 0
	var/inactivity_period = 0
	var/TARGET = null
	var/LAST_TARGET = null
	var/list/nearby = list()
	var/best_force = 0
	var/retal = 0
	var/mob/retal_target = null
	var/update_hands = 0
	var/list/blacklistItems = list() // items we should be ignoring
	var/maxStepsTick = 6 // step as many times as we can per frame
	//Job and mind data
	var/obj/item/weapon/card/id/MYID
	var/obj/item/weapon/card/id/RPID // the "real" idea they use
	var/obj/item/device/pda/MYPDA
	var/obj/item/main_hand
	var/obj/item/other_hand
	var/TRAITS = 0
	var/obj/item/weapon/card/id/Path_ID
	var/datum/job/myjob
	var/list/myPath = list()
	faction = list("synth")
	//trait vars
	var/robustness = 50
	var/smartness = 50
	var/attitude = 50
	var/slyness = 50
	var/graytide = 0
	var/list/favoured_types = list() // allow a mob to favour a type, and hold onto them
	var/chattyness = CHANCE_TALK
	var/targetInterestShift = 5 // how much a good action should "reward" the npc
	//modules
	var/list/functions = list("nearbyscan","combat","shitcurity","chatter")
	var/restrictedJob = 0
	var/shouldUseDynamicProc = 0 // switch to make the AI control it's own proccessing
	var/alternateProcessing = 0
	var/forceProcess = 0
	var/processTime = 10

	var/list/knownStrings = list()

	//snpc traitor variables

	var/isTraitor = 0
	var/traitorTarget
	var/traitorScale = 0 // our ability as a traitor
	var/traitorType = 0

/// SNPC voice handling

/mob/living/carbon/human/interactive/proc/loadVoice()
	var/savefile/S = new /savefile("data/npc_saves/snpc.sav")
	S["knownStrings"] >> knownStrings

	if(isnull(knownStrings))
		knownStrings = list()

/mob/living/carbon/human/interactive/proc/saveVoice()
	var/savefile/S = new /savefile("data/npc_saves/snpc.sav")
	S["knownStrings"] << knownStrings

//botPool funcs
/mob/living/carbon/human/interactive/proc/takeDelegate(mob/living/carbon/human/interactive/from,doReset=TRUE)
	eye_color = "red"
	if(from == src)
		return FALSE
	TARGET = from.TARGET
	LAST_TARGET = from.LAST_TARGET
	retal = from.retal
	retal_target = from.retal_target
	doing = from.doing
	//
	timeout = 0
	inactivity_period = 0
	interest = maxInterest
	//
	update_icons()
	if(doReset)
		from.TARGET = null
		from.LAST_TARGET = null
		from.retal = 0
		from.retal_target = null
		from.doing = 0
	return TRUE

//end pool funcs

/mob/living/carbon/human/interactive/proc/random()
	//this is here because this has no client/prefs/brain whatever.
	age = rand(AGE_MIN,AGE_MAX)
	//job handling
	myjob = new/datum/job/assistant()
	job = myjob.title
	myjob.equip(src)
	myjob.apply_fingerprints(src)

/mob/living/carbon/human/interactive/attacked_by(obj/item/I, mob/living/user, def_zone)
	..()
	retal = 1
	retal_target = user

/mob/living/carbon/human/interactive/bullet_act(var/obj/item/projectile/P)
	var/potentialAssault = locate(/mob/living) in view(2,P.starting)
	if(potentialAssault)
		retal = 1
		retal_target = potentialAssault
	..()

/client/proc/resetSNPC(var/mob/A in SSnpc.botPool_l)
	set name = "Reset SNPC"
	set desc = "Reset the SNPC"
	set category = "Debug"

	if(!holder)
		return

	if(A)
		if(!istype(A,/mob/living/carbon/human/interactive))
			return
		var/mob/living/carbon/human/interactive/T = A
		if(T)
			T.timeout = 100
			T.retal = 0
			T.doing = 0

/client/proc/toggleSNPC(var/mob/A in SSnpc.botPool_l)
	set name = "Toggle SNPC Proccessing Mode"
	set desc = "Toggle SNPC Proccessing Mode"
	set category = "Debug"

	if(!holder)
		return

	if(A)
		if(!istype(A,/mob/living/carbon/human/interactive))
			return
		var/mob/living/carbon/human/interactive/T = A
		if(T)
			T.alternateProcessing = !T.alternateProcessing
			T.forceProcess = 1
			usr << "[T]'s processing has been switched to [T.alternateProcessing ? "High Profile" : "Low Profile"]"

/client/proc/customiseSNPC(var/mob/A in SSnpc.botPool_l)
	set name = "Customize SNPC"
	set desc = "Customise the SNPC"
	set category = "Debug"

	if(!holder)
		return

	if(A)
		if(!istype(A,/mob/living/carbon/human/interactive))
			return
		var/mob/living/carbon/human/interactive/T = A
		var/cjob = input("Choose Job") as null|anything in SSjob.occupations

		if(cjob)
			T.myjob = cjob
			T.job = T.myjob.title
			for(var/obj/item/W in T)
				qdel(W)
			T.myjob.equip(T)
			T.myjob.apply_fingerprints(T)
			T.doSetup()

		var/shouldDoppel = input("Do you want the SNPC to disguise themself as a crewmember?") as null|anything in list("Yes","No")
		if(shouldDoppel)
			if(shouldDoppel == "Yes")
				var/list/validchoices = list()
				for(var/mob/living/carbon/human/M in mob_list)
					validchoices += M

				var/mob/living/carbon/human/chosen = input("Which crewmember?") as null|anything in validchoices

				if(chosen)
					var/datum/dna/toDoppel = chosen.dna

					T.real_name = toDoppel.real_name
					toDoppel.transfer_identity(T, transfer_SE=1)
					T.updateappearance(mutcolor_update=1)
					T.domutcheck()

		var/doTrait = input("Do you want the SNPC to be a traitor?") as null|anything in list("Yes","No")
		if(doTrait)
			if(doTrait == "Yes")
				var/list/tType = list("Brute" = SNPC_BRUTE, "Stealth" = SNPC_STEALTH, "Martyr" = SNPC_MARTYR, "Psycho" = SNPC_PSYCHO)
				var/cType = input("Choose the traitor personality.") as null|anything in tType
				if(cType)
					var/value = tType[cType]
					T.makeTraitor(value)

		var/doTele = input("Place the SNPC in their department?") as null|anything in list("Yes","No")
		if(doTele)
			if(doTele == "Yes")
				T.loc = pick(get_area_turfs(T.job2area(T.myjob)))

/mob/living/carbon/human/interactive/proc/doSetup()
	Path_ID = new /obj/item/weapon/card/id(src)

	var/datum/job/captain/C = new/datum/job/captain
	Path_ID.access = C.get_access()

	MYID = new(src)
	MYID.name = "[src.real_name]'s ID Card ([myjob.title])"
	MYID.assignment = "[myjob.title]"
	MYID.registered_name = src.real_name
	MYID.access = Path_ID.access // Automatons have strange powers... strange indeed

	RPID = new(src)
	RPID.name = "[src.real_name]'s ID Card ([myjob.title])"
	RPID.assignment = "[myjob.title]"
	RPID.registered_name = src.real_name
	RPID.access = myjob.get_access()

	equip_to_slot_or_del(MYID, slot_wear_id)
	MYPDA = new(src)
	MYPDA.owner = real_name
	MYPDA.ownjob = "Crew"
	MYPDA.name = "PDA-[real_name] ([myjob.title])"
	equip_to_slot_or_del(MYPDA, slot_belt)
	zone_selected = "chest"
	//arms
	if(prob((FUZZY_CHANCE_LOW+FUZZY_CHANCE_HIGH)/4))
		var/obj/item/organ/limb/r_arm/R = locate(/obj/item/organ/limb/r_arm) in organs
		qdel(R)
		organs += new /obj/item/organ/limb/robot/r_arm
	else
		var/obj/item/organ/limb/l_arm/L = locate(/obj/item/organ/limb/l_arm) in organs
		qdel(L)
		organs += new /obj/item/organ/limb/robot/l_arm
	//legs
	if(prob((FUZZY_CHANCE_LOW+FUZZY_CHANCE_HIGH)/4))
		var/obj/item/organ/limb/r_leg/R = locate(/obj/item/organ/limb/r_leg) in organs
		qdel(R)
		organs += new /obj/item/organ/limb/robot/r_leg
	else
		var/obj/item/organ/limb/l_leg/L = locate(/obj/item/organ/limb/l_leg) in organs
		qdel(L)
		organs += new /obj/item/organ/limb/robot/l_leg
	//chest and head
	if(prob((FUZZY_CHANCE_LOW+FUZZY_CHANCE_HIGH)/4))
		var/obj/item/organ/limb/chest/R = locate(/obj/item/organ/limb/chest) in organs
		qdel(R)
		organs += new /obj/item/organ/limb/robot/chest
	else
		var/obj/item/organ/limb/head/L = locate(/obj/item/organ/limb/head) in organs
		qdel(L)
		organs += new /obj/item/organ/limb/robot/head
	for(var/LIMB in organs)
		if(LIMB) // prevents a runtime
			var/obj/item/organ/limb/L = LIMB
			L.owner = src
	update_icons()
	update_damage_overlays(0)
	update_augments()

	hand = 0
	functions = list("nearbyscan","combat","shitcurity","chatter") // stop customize adding multiple copies of a function
	//job specific favours
	switch(myjob.title)
		if("Assistant")
			favoured_types = list(/obj/item/clothing, /obj/item/weapon)
		if("Captain","Head of Personnel")
			favoured_types = list(/obj/item/clothing, /obj/item/weapon/stamp/captain,/obj/item/weapon/disk/nuclear)
		if("Cook")
			favoured_types = list(/obj/item/weapon/reagent_containers/food, /obj/item/weapon/kitchen)
			functions += "souschef"
			restrictedJob = 1
		if("Bartender")
			favoured_types = list(/obj/item/weapon/reagent_containers/food, /obj/item/weapon/kitchen)
			functions += "bartend"
			restrictedJob = 1
		if("Station Engineer","Chief Engineer","Atmospheric Technician")
			favoured_types = list(/obj/item/stack, /obj/item/weapon, /obj/item/clothing)
		if("Chief Medical Officer","Medical Doctor","Chemist","Virologist","Geneticist")
			favoured_types = list(/obj/item/weapon/reagent_containers/glass/beaker, /obj/item/weapon/storage/firstaid, /obj/item/stack/medical, /obj/item/weapon/reagent_containers/syringe)
			functions += "healpeople"
		if("Research Director","Scientist","Roboticist")
			favoured_types = list(/obj/item/weapon/reagent_containers/glass/beaker, /obj/item/stack, /obj/item/weapon/reagent_containers)
		if("Head of Security","Warden","Security Officer","Detective")
			favoured_types = list(/obj/item/clothing, /obj/item/weapon, /obj/item/weapon/restraints)
		if("Janitor")
			favoured_types = list(/obj/item/weapon/mop, /obj/item/weapon/reagent_containers/glass/bucket, /obj/item/weapon/reagent_containers/spray/cleaner, /obj/effect/decal/cleanable)
			functions += "dojanitor"
		if("Clown")
			favoured_types = list(/obj/item/weapon/soap, /obj/item/weapon/reagent_containers/food/snacks/grown/banana,/obj/item/weapon/grown/bananapeel)
			functions += "clowning"
		if("Botanist")
			favoured_types = list(/obj/machinery/hydroponics,  /obj/item/weapon/reagent_containers, /obj/item/weapon)
			functions += "botany"
			restrictedJob = 1
		else
			favoured_types = list(/obj/item/clothing)

	if(TRAITS & TRAIT_ROBUST)
		robustness = 75
	else if(TRAITS & TRAIT_UNROBUST)
		robustness = 25

	//modifiers are prob chances, lower = smarter
	if(TRAITS & TRAIT_SMART)
		smartness = 75
	else if(TRAITS & TRAIT_DUMB)
		disabilities |= CLUMSY
		smartness = 25

	if(TRAITS & TRAIT_MEAN)
		attitude = 75
	else if(TRAITS & TRAIT_FRIENDLY)
		attitude = 1

	if(TRAITS & TRAIT_THIEVING)
		slyness = 75

/mob/living/carbon/human/interactive/proc/makeTraitor(var/inPers)
	isTraitor = 1
	traitorScale = (slyness + smartness) + rand(-10,10)
	traitorType = inPers

	switch(traitorType)
		if(SNPC_BRUTE) // SMASH KILL RAAARGH
			traitorTarget = pick(mob_list)
		if(SNPC_STEALTH) // Shhh we is sneekies
			var/A = pick(typesof(/datum/objective_item/steal) - /datum/objective_item/steal)
			var/datum/objective_item/steal/S = new A
			traitorTarget = locate(S.targetitem) in world
		if(SNPC_MARTYR) // MY LIFE FOR SPESZUL
			var/targetType = pick(/obj/structure/particle_accelerator,/obj/machinery/gravity_generator/main,/obj/machinery/power/smes)
			traitorTarget = locate(targetType) in world
		if(SNPC_PSYCHO) // YOU'RE LIKE A FLESH BICYLE AND I WANT TO DISMANTLE YOU
			traitorTarget = null

	functions += "traitor"
	faction -= "neutral"
	faction += "hostile"

/mob/living/carbon/human/interactive/New()
	..()

	src.set_species(/datum/species/synth)
	var/datum/species/synth/mSyn = dna.species
	mSyn.assume_disguise(new/datum/species/human,src)

	random()

	doSetup()

	SSnpc.insertBot(src)

	loadVoice()

	// a little bit of variation to make individuals more unique
	robustness += rand(-10,10)
	smartness += rand(-10,10)
	attitude += rand(-10,10)
	slyness += rand(-10,10)

	doProcess()

/mob/living/carbon/human/interactive/proc/retalTarget(var/target)
	var/mob/living/carbon/human/M = target
	if(target)
		if(health > 0)
			if(M.a_intent == "help")
				chatter()
			if(M.a_intent == "harm")
				retal = 1
				retal_target = target

//Retaliation clauses

/mob/living/carbon/human/interactive/hitby(atom/movable/AM, skipcatch, hitpush, blocked)
	..(AM,skipcatch,hitpush,blocked)
	var/mob/living/carbon/C = locate(/mob/living/carbon) in view(MIN_RANGE_FIND,src)
	if(C)
		retalTarget(C)

/mob/living/carbon/human/interactive/bullet_act(obj/item/projectile/P, def_zone)
	..(P,def_zone)
	retalTarget(P.firer)

/mob/living/carbon/human/interactive/attack_hand(mob/living/carbon/human/M)
	..(M)
	retalTarget(M)

/mob/living/carbon/human/interactive/show_inv(mob/user)
	..(user)
	retalTarget(user)

/mob/living/carbon/human/interactive/can_inject(mob/user, error_msg, target_zone, var/penetrate_thick = 0)
	..(user,error_msg,target_zone,penetrate_thick)
	retalTarget(user)

//THESE EXIST FOR DEBUGGING OF THE DOING/INTEREST SYSTEM EASILY
/mob/living/carbon/human/interactive/proc/doing2string(doin)
	var/toReturn = ""
	if(!doin)
		toReturn = "not doing anything"
	if(doin & INTERACTING)
		toReturn += "interacting with something, "
	if(doin & FIGHTING)
		toReturn += "engaging in combat, "
	if(doin & TRAVEL)
		toReturn += "and going somewhere"
	return toReturn

/mob/living/carbon/human/interactive/proc/interest2string(inter)
	var/toReturn = "Flatlined"
	if(inter >= 0 && inter <= 25)
		toReturn = "Very Bored"
	if(inter >= 26 && inter <= 50)
		toReturn = "Bored"
	if(inter >= 51 && inter <= 75)
		toReturn = "Content"
	if(inter >= 76)
		toReturn = "Excited"
	return toReturn
//END DEBUG

/mob/living/carbon/human/interactive/proc/IsDeadOrIncap(checkDead = TRUE)
	if(!canmove)
		return 1
	if(health <= 0 && checkDead)
		return 1
	if(restrained())
		return 1
	if(paralysis)
		return 1
	if(stunned)
		return 1
	if(stat)
		return 1
	if(inactivity_period > 0)
		return 1
	return 0

/mob/living/carbon/human/interactive/proc/enforce_hands()
	if(main_hand)
		if(main_hand.loc != src)
			main_hand = null
	if(other_hand)
		if(other_hand.loc != src)
			other_hand = null
	if(hand)
		if(!l_hand)
			main_hand = null
			if(r_hand)
				swap_hands()
	else
		if(!r_hand)
			main_hand = null
			if(l_hand)
				swap_hands()

/mob/living/carbon/human/interactive/proc/swap_hands()
	hand = !hand
	var/obj/item/T = other_hand
	main_hand = other_hand
	other_hand = T
	update_hands = 1

/mob/living/carbon/human/interactive/proc/take_to_slot(obj/item/G, var/hands=0)
	var/list/slots = list ("left pocket" = slot_l_store,"right pocket" = slot_r_store,"left hand" = slot_l_hand,"right hand" = slot_r_hand)
	if(hands)
		slots = list ("left hand" = slot_l_hand,"right hand" = slot_r_hand)
	G.loc = src
	if(G.force && G.force > best_force)
		best_force = G.force
	equip_in_one_of_slots(G, slots)
	update_hands = 1

/mob/living/carbon/human/interactive/proc/insert_into_backpack()
	var/list/slots = list ("left pocket" = slot_l_store,"right pocket" = slot_r_store,"left hand" = slot_l_hand,"right hand" = slot_r_hand)
	var/obj/item/I = get_item_by_slot(pick(slots))
	var/obj/item/weapon/storage/BP = get_item_by_slot(slot_back)
	if(back && BP && I)
		if(BP.can_be_inserted(I,0))
			BP.handle_item_insertion(I,0)
	else
		unEquip(I,TRUE)
	update_hands = 1

/mob/living/carbon/human/interactive/proc/targetRange(towhere)
	return get_dist(get_turf(towhere), get_turf(src))

/mob/living/carbon/human/interactive/Life()
	..()
	if(ticker.current_state == GAME_STATE_FINISHED)
		saveVoice()
	if(!alternateProcessing || forceProcess)
		doProcess()

/mob/living/carbon/human/interactive/death()
	saveVoice()
	..()

/mob/living/carbon/human/interactive/Hear(message, atom/movable/speaker, message_langs, raw_message, radio_freq, list/spans)
	if(speaker != src)
		knownStrings |= html_decode(raw_message)
	..()

/mob/living/carbon/human/interactive/proc/doProcess()
	forceProcess = 0

	if(shouldUseDynamicProc)
		var/isSeen = 0
		for(var/mob/living/carbon/human/A in orange(12,src))
			if(A.client)
				isSeen = 1
		alternateProcessing = isSeen
		if(alternateProcessing)
			forceProcess = 1

	if(IsDeadOrIncap())
		walk(src,0)
		return
	//---------------------------
	//---- interest flow control
	if(interest < 0 || inactivity_period < 0)
		if(interest < 0)
			interest = 0
		if(inactivity_period < 0)
			inactivity_period = 0
	if(interest > maxInterest)
		interest = maxInterest
	//---------------------------
	//VIEW FUNCTIONS

	//doorscan is now integrated into life and runs before all other procs
	spawn(0)
		for(var/dir in alldirs)
			var/turf/T = get_step(src,dir)
			if(T)
				for(var/obj/machinery/door/D in T.contents)
					if(!istype(D,/obj/machinery/door/poddoor) && D.density)
						spawn(0)
							if(istype(D,/obj/machinery/door/airlock))
								var/obj/machinery/door/airlock/AL = D
								if(!AL.CanAStarPass(RPID)) // only crack open doors we can't get through
									AL.p_open = 1
									AL.update_icon()
									AL.shock(src,(100 - smartness)/2)
									sleep(5)
									AL.unbolt()
									if(!AL.wires.is_cut(WIRE_BOLTS))
										AL.wires.cut(WIRE_BOLTS)
									if(!AL.wires.is_cut(WIRE_POWER1))
										AL.wires.cut(WIRE_POWER1)
									if(!AL.wires.is_cut(WIRE_POWER2))
										AL.wires.cut(WIRE_POWER2)
									sleep(5)
									AL.p_open = 0
									AL.update_icon()
							D.open()

	if(update_hands)
		if(l_hand || r_hand)
			if(l_hand)
				hand = 1
				main_hand = l_hand
				if(r_hand)
					other_hand = r_hand
			else if(r_hand)
				hand = 0
				main_hand = r_hand
				if(l_hand) //this technically shouldnt occur, but its a redundancy
					other_hand = l_hand
			update_icons()
		update_hands = 0

	if(grabbed_by.len > 0)
		for(var/obj/item/weapon/grab/G in grabbed_by)
			if(Adjacent(G))
				a_intent = "disarm"
				G.assailant.attack_hand(src)
				inactivity_period = 10

	if(buckled)
		resist()
		inactivity_period = 10

	//proc functions
	for(var/Proc in functions)
		if(!IsDeadOrIncap())
			callfunction(Proc)

	//target interaction stays hardcoded

	if(TARGET) // don't use blacklisted items
		if(TARGET in blacklistItems)
			TARGET = null

	if((TARGET && Adjacent(TARGET)))
		doing |= INTERACTING
		//--------DOORS
		if(istype(TARGET, /obj/machinery/door))
			var/obj/machinery/door/D = TARGET
			if(D.check_access(MYID) && !istype(D,/obj/machinery/door/poddoor))
				D.open()
				sleep(15)
				var/turf/T = get_step(get_step(D.loc,dir),dir) //recursion yo
				tryWalk(T)
		//THIEVING SKILLS
		if(!TARGET in blacklistItems)
			insert_into_backpack() // dump random item into backpack to make space
			//---------ITEMS
			if(istype(TARGET, /obj/item))
				if(istype(TARGET, /obj/item/weapon))
					var/obj/item/weapon/W = TARGET
					if(W.force >= best_force || prob((FUZZY_CHANCE_LOW+FUZZY_CHANCE_HIGH)/2))
						if(!l_hand || !r_hand)
							put_in_hands(W)
						else
							insert_into_backpack()
				else
					if(!l_hand || !r_hand)
						put_in_hands(TARGET)
					else
						insert_into_backpack()
			//---------FASHION
			if(istype(TARGET,/obj/item/clothing))
				var/obj/item/clothing/C = TARGET
				drop_item()
				spawn(5)
					take_to_slot(C,1)
					if(!equip_to_appropriate_slot(C))
						var/obj/item/I = get_item_by_slot(C)
						unEquip(I)
						spawn(5)
							equip_to_appropriate_slot(C)
				update_hands = 1
				if(MYPDA in src.loc || MYID in src.loc)
					if(MYPDA in src.loc)
						equip_to_appropriate_slot(MYPDA)
					if(MYID in src.loc)
						equip_to_appropriate_slot(MYID)
		//THIEVING SKILLS END
		//-------------TOUCH ME
		if(istype(TARGET,/obj/structure))
			var/obj/structure/STR = TARGET
			if(main_hand)
				var/obj/item/weapon/W = main_hand
				STR.attackby(W, src)
			else
				STR.attack_hand(src)
		interest += targetInterestShift
		doing = doing & ~INTERACTING
		timeout = 0
		TARGET = null
	else
		if(TARGET)
			tryWalk(TARGET)
			timeout++

	if(doing == 0)
		interest--
	else
		interest++

	if(inactivity_period > 0)
		inactivity_period--

	if(interest <= 0 || timeout >= 10) // facilitate boredom functions
		TARGET = null
		doing = 0
		timeout = 0
		myPath = list()

	//this is boring, lets move
	if(!doing && !IsDeadOrIncap() && !TARGET)
		doing |= TRAVEL
		if(nearby.len > 4)
			//i'm crowded, time to leave
			TARGET = pick(target_filter(urange(MAX_RANGE_FIND,src,1)))
		else
			var/choice = rand(1,50)
			switch(choice)
				if(1 to 30)
					//chance to chase an item
					TARGET = locate(/obj/item) in favouredObjIn(oview(MIN_RANGE_FIND,src))
				if(31 to 40)
					TARGET = safepick(get_area_turfs(job2area(myjob)))
				if(41 to 45)
					TARGET = pick(target_filter(favouredObjIn(urange(MAX_RANGE_FIND,src,1))))
				if(46 to 50)
					TARGET = pick(target_filter(oview(MIN_RANGE_FIND,src)))
		tryWalk(TARGET)
	LAST_TARGET = TARGET
	if(alternateProcessing)
		spawn(processTime)
			doProcess()

/mob/living/carbon/human/interactive/proc/favouredObjIn(var/list/inList)
	var/list/outList = list()
	for(var/i in inList)
		for(var/path in favoured_types)
			if(ispath(i,path))
				outList += i
	if(outList.len <= 0)
		outList = inList
	return outList

/mob/living/carbon/human/interactive/proc/callfunction(Proc)
	set waitfor = 0
	spawn(0)
		call(src,Proc)(src)

/mob/living/carbon/human/interactive/proc/tryWalk(turf/inTarget)
	if(restrictedJob) // we're a job that has to stay in our home
		if(!(get_turf(inTarget) in get_area_turfs(job2area(myjob))))
			TARGET = null
			return

	if(!IsDeadOrIncap())
		if(!walk2derpless(inTarget))
			timeout++
	else
		timeout++

/mob/living/carbon/human/interactive/proc/getGoodPath(target,var/maxtries=512)
	set background = 1
	var/turf/end = get_turf(target)

	var/turf/current = get_turf(src)

	var/list/path = list()
	var/tries = 0
	while(current != end && tries < maxtries)
		var/turf/shortest = current
		for(var/turf/T in view(current,1))
			var/foundDense = 0
			for(var/atom/A in T)
				if(A.density)
					foundDense = 1
			if(T.density == 0 && !foundDense)
				if(get_dist(T, target) < get_dist(shortest,target))
					shortest = T
				else
					tries++
			else
				tries++
		current = shortest
		path += shortest
	return path

/mob/living/carbon/human/interactive/proc/walk2derpless(target)
	set background = 1
	if(!target)
		return 0

	if(myPath.len <= 0)
		myPath = get_path_to(src, get_turf(target), /turf/proc/Distance, MAX_RANGE_FIND + 1, 250,1, id=Path_ID)

	if(myPath)
		if(myPath.len > 0)
			doing = doing & ~TRAVEL
			for(var/i = 0; i < maxStepsTick; ++i)
				if(!IsDeadOrIncap())
					if(myPath.len >= 1)
						walk_to(src,myPath[1],0,5)
						myPath -= myPath[1]
			return 1
	return 0

/mob/living/carbon/human/interactive/proc/job2area(target)
	var/datum/job/T = target
	if(T.title == "Assistant")
		return /area/hallway/primary
	if(T.title == "Captain" || T.title == "Head of Personnel")
		return /area/bridge
	if(T.title == "Bartender")
		return /area/crew_quarters/bar
	if(T.title == "Cook")
		return /area/crew_quarters/kitchen
	if(T.title == "Station Engineer" || T.title == "Chief Engineer" || T.title == "Atmospheric Technician")
		return /area/engine
	if(T.title == "Chief Medical Officer" || T.title == "Medical Doctor" || T.title == "Chemist" || T.title == "Virologist" || T.title == "Geneticist")
		return /area/medical
	if(T.title == "Research Director" || T.title == "Scientist" || T.title == "Roboticist")
		return /area/toxins
	if(T.title == "Head of Security" || T.title == "Warden" || T.title == "Security Officer" || T.title == "Detective")
		return /area/security
	if(T.title == "Botanist")
		return /area/hydroponics
	else
		return pick(/area/hallway,/area/crew_quarters)

/mob/living/carbon/human/interactive/proc/target_filter(target)
	var/list/filtered_targets = list(/area, /turf, /obj/machinery/door, /atom/movable/light, /obj/structure/cable, /obj/machinery/atmospherics)
	var/list/L = target
	for(var/atom/A in target) // added a bunch of "junk" that clogs up the general find procs
		if(is_type_in_list(A,filtered_targets))
			L -= A
	return L

/mob/living/carbon/human/interactive/proc/shouldModulePass() // returns 1 if the npc is in anything "primary"
	if(doing & FIGHTING)
		return 1
	if(retal)
		return 1
	return 0

///BUILT IN MODULES
/mob/living/carbon/human/interactive/proc/chatter(obj)
	var/verbs_use = pick_list(NPC_SPEAK_FILE,"verbs_use")
	var/verbs_touch = pick_list(NPC_SPEAK_FILE,"verbs_touch")
	var/verbs_move = pick_list(NPC_SPEAK_FILE,"verbs_move")
	var/nouns_insult = pick_list(NPC_SPEAK_FILE,"nouns_insult")
	var/nouns_generic = pick_list(NPC_SPEAK_FILE,"nouns_generic")
	var/nouns_objects = pick_list(NPC_SPEAK_FILE,"nouns_objects")
	var/nouns_body = pick_list(NPC_SPEAK_FILE,"nouns_body")
	var/adjective_insult = pick_list(NPC_SPEAK_FILE,"adjective_insult")
	var/adjective_objects = pick_list(NPC_SPEAK_FILE,"adjective_objects")
	var/adjective_generic = pick_list(NPC_SPEAK_FILE,"adjective_generic")
	var/curse_words = pick_list(NPC_SPEAK_FILE,"curse_words")

	var/chatmsg = ""

	if(prob(10)) // 10% chance to broadcast it over the radio
		chatmsg = ";"

	if(prob(35) || knownStrings.len < 10) // say a generic phrase, otherwise draw from our strings.
		if(doing & INTERACTING)
			if(prob(chattyness))
				chatmsg += pick("This [nouns_objects] is a little [adjective_objects].",
				"Well [verbs_use] my [nouns_body], this [nouns_insult] is pretty [adjective_insult].",
				"[capitalize(curse_words)], what am I meant to do with this [adjective_insult] [nouns_objects].")
		else if(doing & TRAVEL)
			if(prob(chattyness))
				chatmsg += pick("Oh [curse_words], [verbs_move]!",
				"Time to get my [adjective_generic] [adjective_insult] [nouns_body] elsewhere.",
				"I wonder if there is anything to [verbs_use] and [verbs_touch] somewhere else..")
		else if(doing & FIGHTING)
			if(prob(chattyness))
				chatmsg += pick("I'm going to [verbs_use] you, you [adjective_insult] [nouns_insult]!",
				"Rend and [verbs_touch], Rend and [verbs_use]!",
				"You [nouns_insult], I'm going to [verbs_use] you right in the [nouns_body]. JUST YOU WAIT!")
		if(prob(chattyness/2))
			chatmsg = ";"
			var/what = pick(1,2,3,4,5)
			switch(what)
				if(1)
					chatmsg += "Well [curse_words], this is a [adjective_generic] situation."
				if(2)
					chatmsg += "Oh [curse_words], that [nouns_insult] was one hell of an [adjective_insult] [nouns_body]."
				if(3)
					chatmsg += "I want to [verbs_use] that [nouns_insult] when I find them."
				if(4)
					chatmsg += "[pick("Innocent","Guilty","Traitorous","Honk")] until proven [adjective_generic]!"
				if(5)
					var/toSay = ""
					for(var/i = 0; i < 5; i++)
						curse_words = pick_list(NPC_SPEAK_FILE,"curse_words")
						toSay += "[curse_words] "
					chatmsg += "Hey [nouns_generic], why dont you go [toSay], you [nouns_insult]!"
	else
		chatmsg += pick(knownStrings)
		if(prob(25)) // cut out some phrases now and then to make sure we're fresh and new
			knownStrings -= pick(chatmsg)

	if(chatmsg != ";" && chatmsg != "")
		src.say(chatmsg)

/mob/living/carbon/human/interactive/proc/getAllContents()
	var/list/allContents = list()
	for(var/atom/A in contents)
		allContents += A
		if(A.contents.len > 0)
			for(var/atom/B in A)
				allContents += B
	return allContents

/mob/living/carbon/human/interactive/proc/enforceHome()
	var/list/validHome = get_area_turfs(job2area(myjob))

	if(TARGET)
		var/atom/tcheck = TARGET
		if(tcheck)
			if(!(get_turf(tcheck) in validHome))
				TARGET = null
				return 1

	if(!(get_turf(src) in validHome))
		tryWalk(pick(get_area_turfs(job2area(myjob))))
		return 1
	return 0

/mob/living/carbon/human/interactive/proc/npcDrop(var/obj/item/A,var/blacklist = 0)
	if(blacklist)
		blacklistItems += A
	A.loc = get_turf(src) // drop item works inconsistently
	enforce_hands()
	update_icons()

/mob/living/carbon/human/interactive/proc/traitor(obj)

	if(traitorType == SNPC_PSYCHO)
		traitorTarget = pick(nearby)

	if(prob(traitorScale))
		if(!Adjacent(traitorTarget) && !(traitorType == SNPC_PSYCHO))
			tryWalk(get_turf(traitorTarget))
		else
			switch(traitorType)
				if(SNPC_BRUTE)
					retal = 1
					retal_target = traitorTarget
				if(SNPC_STEALTH)
					if(istype(traitorTarget,/mob)) // it's inside something, lets kick their shit in
						var/mob/M = traitorTarget
						if(!M.stat)
							retal = 1
							retal_target = traitorTarget
						else
							var/obj/item/I = traitorTarget
							I.loc = get_turf(traitorTarget) // pull it outta them
					else
						take_to_slot(traitorTarget)
				if(SNPC_MARTYR)
					customEmote("[src]'s chest opens up, revealing a large mass of explosives and tangled wires!")
					if(inactivity_period <= 0)
						inactivity_period = 9999 // technically infinite
						if(do_after(src,60,target=traitorTarget))
							customEmote("A fire bursts from [src]'s eyes, igniting white hot and consuming their body in a flaming explosion!")
							explosion(src, 6, 6, 6)
						else
							inactivity_period = 0
							customEmote("[src]'s chest closes, hiding their insides.")
				if(SNPC_PSYCHO)
					var/choice = pick(typesof(/obj/item/weapon/grenade/chem_grenade) - /obj/item/weapon/grenade/chem_grenade)

					new choice(src)

					retal = 1
					retal_target = traitorTarget

/mob/living/carbon/human/interactive/proc/botany(obj)
	if(shouldModulePass())
		return

	if(enforceHome())
		return

	var/list/allContents = getAllContents()

	var/obj/item/weapon/reagent_containers/glass/beaker/bluespace/internalBeaker = locate(/obj/item/weapon/reagent_containers/glass/beaker/bluespace) in allContents
	var/obj/item/weapon/storage/bag/plants/internalBag = locate(/obj/item/weapon/storage/bag/plants) in allContents

	if(!internalBag)
		internalBag = new/obj/item/weapon/storage/bag/plants(src)
	if(!internalBeaker)
		internalBeaker = new/obj/item/weapon/reagent_containers/glass/beaker/bluespace(src)
		internalBeaker.name = "Grow-U-All Super Spray"

	if(internalBeaker && internalBag)
		var/obj/machinery/hydroponics/HP

		//consider the appropriate target
		var/list/considered = list()

		for(var/obj/machinery/hydroponics/tester in view(12,src))
			considered[tester] = 1

			if(!tester.myseed)
				considered[tester] += 50
			if(tester.weedlevel > 0)
				considered[tester] += 5
			if(tester.pestlevel > 0)
				considered[tester] += 5
			if(tester.nutrilevel <  tester.maxnutri)
				considered[tester] += 15
			if(tester.waterlevel < tester.maxwater)
				considered[tester] += 15
			if(tester.harvest || tester.dead)
				considered[tester] += 100
			considered[tester] = max(1,considered[tester] - get_dist(src,tester))

		var/index = 0
		for(var/A in considered)
			++index
			if(considered[A] > considered[HP] || !HP)
				HP = considered[index]

		if(HP)
			TARGET = HP
			if(!Adjacent(HP))
				tryWalk(get_turf(HP))
			else
				if(HP.harvest || HP.dead)
					HP.attack_hand(src)
				else if(!HP.myseed)
					var/seedType = pick(typesof(/obj/item/seeds) - /obj/item/seeds)
					var/obj/item/seeds/SEED = new seedType(src)
					customEmote("[src] [pick("gibbers","drools","slobbers","claps wildly","spits")] towards [TARGET], producing a [SEED]!")
					HP.attackby(SEED,src)
				else
					var/change = 0
					if(HP.weedlevel > 0)
						change = 1
						if(!internalBeaker.reagents.has_reagent("weedkiller", 10))
							internalBeaker.reagents.add_reagent("weedkiller",10)
					if(HP.pestlevel > 0)
						change = 1
						if(!internalBeaker.reagents.has_reagent("pestkiller", 10))
							internalBeaker.reagents.add_reagent("pestkiller",10)
					if(HP.nutrilevel <  HP.maxnutri)
						change = 1
						if(!internalBeaker.reagents.has_reagent("eznutriment", 15))
							internalBeaker.reagents.add_reagent("eznutriment",15)
						if(!internalBeaker.reagents.has_reagent("diethylamine", 15))
							internalBeaker.reagents.add_reagent("diethylamine",15)
					if(HP.waterlevel < HP.maxwater)
						change = 1
						if(!internalBeaker.reagents.has_reagent("holywater", 50))
							internalBeaker.reagents.add_reagent("holywater",50)
					if(change)
						HP.attackby(internalBeaker,src)

	var/obj/item/weapon/reagent_containers/food/snacks/grown/GF = locate(/obj/item/weapon/reagent_containers/food/snacks/grown) in view(12,src)
	if(GF)
		if(!Adjacent(GF))
			tryWalk(get_turf(GF))
		else
			GF.attackby(internalBag,src)

	if(internalBag.contents.len > 0)
		var/obj/machinery/smartfridge/SF = locate(/obj/machinery/smartfridge) in range(12,src)
		if(!Adjacent(SF))
			tryWalk(get_turf(SF))
		else
			customEmote("[src] [pick("gibbers","drools","slobbers","claps wildly","spits")], upending the [internalBag]'s contents all over the [SF]!")
			//smartfridges call updateUsrDialog when you call attackby, so we're going to have to cheese-magic-space this
			for(var/obj/toLoad in internalBag.contents)
				if(contents.len >= SF.max_n_of_items)
					break
				if(SF.accept_check(toLoad))
					SF.load(toLoad)
				else
					qdel(toLoad) // destroy everything we dont need

/mob/living/carbon/human/interactive/proc/bartend(obj)
	if(shouldModulePass())
		return

	if(enforceHome())
		return

	var/list/rangeCheck = oview(6,src)
	var/obj/structure/table/RT

	var/mob/living/carbon/human/serveTarget

	for(var/mob/living/carbon/human/H in rangeCheck)
		if(!locate(/obj/item/weapon/reagent_containers/food/drinks) in orange(1,H))
			serveTarget = H

	if(serveTarget)
		RT = locate(/obj/structure/table) in orange(1,serveTarget)

	if(RT && serveTarget)
		if(!Adjacent(RT))
			tryWalk(get_turf(RT))
		else
			var/drinkChoice = pick(typesof(/obj/item/weapon/reagent_containers/food/drinks) - /obj/item/weapon/reagent_containers/food/drinks)
			if(drinkChoice)
				var/obj/item/weapon/reagent_containers/food/drinks/D = new drinkChoice(get_turf(src))
				RT.attackby(D,src)
				src.say("[pick("Something to wet your whistle!","Down the hatch, a tasty beverage!","One drink, coming right up!","Tasty liquid for your oral intake!","Enjoy!")]")
				customEmote("[src] [pick("gibbers","drools","slobbers","claps wildly","spits")], serving up a [D]!")

/mob/living/carbon/human/interactive/proc/shitcurity(obj)
	var/list/allContents = getAllContents()

	if(retal && TARGET)
		for(var/obj/item/I in allContents)
			if(istype(I,/obj/item/weapon/restraints))
				I.attack(TARGET,src) // go go bluespace restraint launcher!
				sleep(25)

/mob/living/carbon/human/interactive/proc/clowning(obj)
	if(shouldModulePass())
		return
	var/list/allContents = getAllContents()
	var/list/rangeCheck = orange(12,src)

	var/mob/living/carbon/human/clownTarget
	var/list/clownPriority = list()

	var/obj/item/weapon/reagent_containers/spray/S
	if(!locate(/obj/item/weapon/reagent_containers/spray) in allContents)
		new/obj/item/weapon/reagent_containers/spray(src)
	else
		S = locate(/obj/item/weapon/reagent_containers/spray) in allContents

	for(var/mob/living/carbon/human/C in rangeCheck)
		var/pranksNearby = 100
		for(var/turf/open/T in orange(1,C))
			for(var/obj/item/A in T)
				if(istype(A,/obj/item/weapon/soap) || istype(A,/obj/item/weapon/reagent_containers/food/snacks/grown/banana) || istype(A,/obj/item/weapon/grown/bananapeel))
					pranksNearby--
			if(T.wet)
				pranksNearby -= 10
		clownPriority[C] = pranksNearby

	for(var/A in clownPriority)
		if(!clownTarget)
			clownTarget = A
		else
			if(clownPriority[A] > clownPriority[clownTarget])
				clownTarget = clownPriority[A]

	if(clownTarget)
		if(!Adjacent(clownTarget))
			tryWalk(clownTarget)
		else
			var/hasPranked = 0
			for(var/A in allContents)
				if(prob(smartness/2) && !hasPranked)
					if(istype(A,/obj/item/weapon/soap))
						npcDrop(A)
						hasPranked = 1
					if(istype(A,/obj/item/weapon/reagent_containers/food/snacks/grown/banana))
						var/obj/item/weapon/reagent_containers/food/snacks/B = A
						B.attack(src, src)
					if(istype(A,/obj/item/weapon/grown/bananapeel))
						npcDrop(A)
						hasPranked = 1
			if(!hasPranked)
				if(S.reagents.total_volume <= 5)
					S.reagents.add_reagent("water", 25)
				S.afterattack(get_turf(pick(orange(1,clownTarget))),src)

/mob/living/carbon/human/interactive/proc/healpeople(obj)
	var/shouldTryHeal = 0
	var/obj/item/stack/medical/M

	var/list/allContents = getAllContents()

	for(var/A in allContents)
		if(istype(A,/obj/item/stack/medical))
			shouldTryHeal = 1
			M = A

	var/obj/item/weapon/reagent_containers/hypospray/HPS

	if(!locate(/obj/item/weapon/reagent_containers/hypospray) in allContents)
		new/obj/item/weapon/reagent_containers/hypospray(src)
	else
		HPS = locate(/obj/item/weapon/reagent_containers/hypospray) in allContents
		if(!shouldTryHeal)
			shouldTryHeal = 2 // we have no stackables to use, lets use our internal hypospray instead

	if(shouldTryHeal == 1)
		for(var/mob/living/carbon/human/C in nearby)
			if(istype(C,/mob/living/carbon/human)) //I haven't the foggiest clue why this is turning up non-carbons but sure here whatever
				if(C.health <= 75)
					if(get_dist(src,C) <= 2)
						src.say("Wait, [C], let me heal you!")
						M.attack(C,src)
						sleep(25)
					else
						tryWalk(get_turf(C))
	else if(shouldTryHeal == 2)
		if(HPS)
			if(HPS.reagents.total_volume <= 0)
				HPS.reagents.add_reagent("tricordrazine",30)
			for(var/mob/living/carbon/human/C in nearby)
				if(istype(C,/mob/living/carbon/human))
					if(C.health <= 75 && C.reagents.get_reagent_amount("tricordrazine") <= 0) // make sure they wont be overdosing
						if(get_dist(src,C) <= 2)
							src.say("Wait, [C], let me heal you!")
							HPS.attack(C,src)
							sleep(25)
						else
							tryWalk(get_turf(C))

/mob/living/carbon/human/interactive/proc/dojanitor(obj)
	if(shouldModulePass())
		return
	var/list/allContents = getAllContents()
	//now with bluespace magic!
	var/obj/item/weapon/reagent_containers/spray/S
	if(!locate(/obj/item/weapon/reagent_containers/spray) in allContents)
		new/obj/item/weapon/reagent_containers/spray(src)
	else
		S = locate(/obj/item/weapon/reagent_containers/spray) in allContents

	if(S)
		if(S.reagents.total_volume <= 5)
			S.reagents.add_reagent("cleaner", 25) // bluespess water delivery for AI

		var/obj/effect/decal/cleanable/TC
		TC = locate(/obj/effect/decal/cleanable) in range(MAX_RANGE_FIND,src)

		if(TC)
			if(!Adjacent(TC))
				tryWalk(TC)
			else
				S.afterattack(TC,src)
				sleep(25)

/mob/living/carbon/human/interactive/proc/customEmote(var/text)
	for(var/mob/living/carbon/M in view(src))
		M.show_message("<span class='notice'>[text]</span>", 2)

// START COOKING MODULE
/mob/living/carbon/human/interactive/proc/cookingwithmagic(var/obj/item/weapon/reagent_containers/food/snacks/target)
	if(Adjacent(target))
		customEmote("[src] [pick("gibbers","drools","slobbers","claps wildly","spits")] towards [target], and with a bang, it's instantly cooked!")
		var/obj/item/weapon/reagent_containers/food/snacks/S = new target.cooked_type (get_turf(src))
		target.initialize_cooked_food(S, 100)
		if(target) // cleaning up old food seems inconsistent, so this will clean up stragglers
			qdel(target)
	else
		tryWalk(target)

/mob/living/carbon/human/interactive/proc/souschef(obj)
	if(shouldModulePass())
		return

	if(enforceHome())
		return

	var/list/allContents = getAllContents()

	//Bluespace in some inbuilt tools
	var/obj/item/weapon/kitchen/rollingpin/RP = locate(/obj/item/weapon/kitchen/rollingpin) in allContents
	if(!RP)
		new/obj/item/weapon/kitchen/rollingpin(src)

	var/obj/item/weapon/kitchen/knife/KK = locate(/obj/item/weapon/kitchen/knife) in allContents
	if(!KK)
		new/obj/item/weapon/kitchen/knife(src)

	var/foundCookable = 0

	if(RP && KK) // Ready to cook!

		var/list/rangeCheck = view(6,src)

		//Make some basic custom food
		var/list/customableTypes = list(/obj/item/weapon/reagent_containers/food/snacks/customizable,/obj/item/weapon/reagent_containers/food/snacks/store/bread/plain,/obj/item/weapon/reagent_containers/food/snacks/pizzabread,/obj/item/weapon/reagent_containers/food/snacks/bun,/obj/item/weapon/reagent_containers/food/snacks/store/cake/plain,/obj/item/weapon/reagent_containers/food/snacks/pie/plain,/obj/item/weapon/reagent_containers/food/snacks/pastrybase)

		var/foundCustom

		for(var/obj/item/weapon/reagent_containers/food/snacks/donkpocket/DP in rangeCheck) // donkpockets are hitler to chef SNPCs
			if(prob(50))
				customEmote("[src] points at the [DP], emitting a loud [pick("bellow","screech","yell","scream")], and it bursts into flame.")
				qdel(DP)

		for(var/customType in customableTypes)
			var/A = locate(customType) in rangeCheck
			if(A)
				foundCustom = A // this will eventually wittle down to 0

		var/obj/machinery/smartfridge/SF = locate(/obj/machinery/smartfridge) in rangeCheck
		if(SF)
			if(SF.contents.len > 0)
				if(!Adjacent(SF))
					tryWalk(get_turf(SF))
				else
					customEmote("[src] [pick("gibbers","drools","slobbers","claps wildly","spits")], grabbing various foodstuffs from [SF] and sticking them in it's mouth!")
					for(var/obj/item/A in SF.contents)
						if(prob(smartness/2))
							A.loc = src

		if(foundCustom)
			var/obj/item/weapon/reagent_containers/food/snacks/FC = foundCustom
			for(var/obj/item/weapon/reagent_containers/food/snacks/toMake in allContents)
				if(prob(smartness))
					if(FC.reagents)
						FC.attackby(toMake,src)
					else
						qdel(FC) // this food is usless, toss it

		//Process dough into various states
		var/obj/item/weapon/reagent_containers/food/snacks/dough/D = locate(/obj/item/weapon/reagent_containers/food/snacks/dough) in rangeCheck
		var/obj/item/weapon/reagent_containers/food/snacks/flatdough/FD = locate(/obj/item/weapon/reagent_containers/food/snacks/flatdough) in rangeCheck
		var/obj/item/weapon/reagent_containers/food/snacks/cakebatter/CB = locate(/obj/item/weapon/reagent_containers/food/snacks/cakebatter) in rangeCheck
		var/obj/item/weapon/reagent_containers/food/snacks/piedough/PD = locate(/obj/item/weapon/reagent_containers/food/snacks/piedough) in rangeCheck

		if(D)
			TARGET = D
			var/choice = pick(1,2)
			if(choice == 1)
				tryWalk(get_turf(D))
				sleep(get_dist(src,D))
				D.attackby(RP,src)
			else
				cookingwithmagic(D)
			foundCookable = 1
		else if(FD)
			TARGET = FD
			var/choice = pick(1,2)
			if(choice == 1)
				tryWalk(get_turf(D))
				sleep(get_dist(src,D))
				FD.attackby(KK,src)
			else
				cookingwithmagic(FD)
			foundCookable = 1
		else if(CB)
			TARGET = CB
			var/choice = pick(1,2)
			if(choice == 1)
				tryWalk(get_turf(D))
				sleep(get_dist(src,D))
				CB.attackby(RP,src)
			else
				cookingwithmagic(CB)
			foundCookable = 1
		else if(PD)
			TARGET = PD
			var/choice = pick(1,2)
			if(choice == 1)
				tryWalk(get_turf(D))
				sleep(get_dist(src,D))
				PD.attackby(KK,src)
			else
				cookingwithmagic(PD)
			foundCookable = 1

		//Cook various regular foods into processed versions
		var/obj/item/weapon/reagent_containers/food/snacks/toCook = locate(/obj/item/weapon/reagent_containers/food/snacks) in rangeCheck
		if(toCook)
			if(toCook.cooked_type)
				TARGET = toCook
				foundCookable = 1
				if(Adjacent(toCook))
					cookingwithmagic(toCook)
				else
					tryWalk(get_turf(toCook))

		var/list/finishedList = list()
		for(var/obj/item/weapon/reagent_containers/food/snacks/toDisplay in allContents)
			if(!toDisplay.cooked_type && !istype(toDisplay,/obj/item/weapon/reagent_containers/food/snacks/grown)) // dont display our ingredients
				finishedList += toDisplay

		for(var/obj/item/weapon/reagent_containers/food/snacks/toGrab in rangeCheck)
			if(!(locate(/obj/structure/table/reinforced) in get_turf(toGrab))) //it's not being displayed
				foundCookable = 1
				if(!Adjacent(toGrab))
					tryWalk(toGrab)
				else
					toGrab.loc = src

		if(finishedList.len > 0)
			var/obj/structure/table/reinforced/RT

			for(var/obj/structure/table/reinforced/toCheck in rangeCheck)
				var/counted = 0
				for(var/obj/item/weapon/reagent_containers/food/snacks/S in get_turf(toCheck))
					++counted
				if(counted < 12) // make sure theres not too much food here
					RT = toCheck
					break

			if(RT)
				foundCookable = 1
				if(!Adjacent(RT))
					tryWalk(get_turf(RT))
				else
					for(var/obj/item/weapon/reagent_containers/food/snacks/toPlop in allContents)
						RT.attackby(toPlop,src)

		if(!foundCookable)
			var/list/allTypes = list(/obj/item/weapon/reagent_containers/food/snacks/piedough,/obj/item/weapon/reagent_containers/food/snacks/cakebatter,/obj/item/weapon/reagent_containers/food/snacks/dough,/obj/item/weapon/reagent_containers/food/snacks/flatdough)

			for(var/A in typesof(/obj/item/weapon/reagent_containers/food/snacks))
				var/obj/item/weapon/reagent_containers/food/snacks/O = A
				if(initial(O.cooked_type))
					allTypes += A

			var/chosenType = pick(allTypes)

			var/obj/item/weapon/reagent_containers/food/snacks/newSnack = new chosenType(get_turf(src))
			TARGET = newSnack
			newSnack.reagents.remove_any((newSnack.reagents.total_volume/2)-1)
			newSnack.name = "Synthetic [newSnack.name]"
			customEmote("[src] [pick("gibbers","drools","slobbers","claps wildly","spits")] as they vomit [newSnack] from their mouth!")
// END COOKING MODULE

/mob/living/carbon/human/interactive/proc/compareFaction(var/list/targetFactions)
	var/hasSame = 0

	for(var/A in targetFactions)
		if(A in faction)
			hasSame = 1

	return hasSame

/mob/living/carbon/human/interactive/proc/combat(obj)
	set background = 1
	enforce_hands()
	if(canmove)
		if((graytide || (TRAITS & TRAIT_MEAN)) || retal)
			interest += targetInterestShift
			a_intent = "harm"
			zone_selected = pick("chest","r_leg","l_leg","r_arm","l_arm","head")
			doing |= FIGHTING
			if(retal)
				TARGET = retal_target
			else
				var/mob/living/M = locate(/mob/living) in oview(7,src)
				if(M != src && !compareFaction(M.faction))
					TARGET = M
				if(!M)
					doing = doing & ~FIGHTING

	//no infighting
	if(retal)
		if(retal_target)
			if(compareFaction(retal_target.faction))
				retal = 0
				retal_target = null
				TARGET = null
				doing = 0

	//ensure we're using the best object possible

	var/obj/item/weapon/best
	var/foundFav = 0
	var/list/allContents = getAllContents()
	for(var/test in allContents)
		for(var/a in favoured_types)
			if(ispath(test,a) && !(doing & FIGHTING)) // if we're not in combat and we find our favourite things, use them (for people like janitor and doctors)
				best = test
				foundFav = 1
				return
		if(!foundFav)
			if(istype(test,/obj/item/weapon))
				var/obj/item/weapon/R = test
				if(R.force > 2) // make sure we don't equip any non-weaponlike items, ie bags and stuff
					if(!best)
						best = R
					else
						if(best.force < R.force)
							best = R
					if(istype(R,/obj/item/weapon/gun))
						var/obj/item/weapon/gun/G = R
						if(G.can_shoot())
							best = R
							break // gun with ammo? screw the rest
	if(best)
		take_to_slot(best,1)

	if((TARGET && (doing & FIGHTING))) // this is a redundancy check
		var/mob/living/M = TARGET
		if(istype(M,/mob/living))
			if(M.health > 1)
				//THROWING OBJECTS
				for(var/A in allContents)
					if(istype(A,/obj/item/weapon/gun))	// guns are for shooting, not throwing.
						continue
					if(prob(robustness))
						if(istype(A,/obj/item/weapon))
							var/obj/item/weapon/W = A
							if(W.throwforce > 19) // Only throw worthwile stuff, no more lobbing wrenches at wenches
								npcDrop(W,1)
								throw_item(TARGET)
						if(istype(A,/obj/item/weapon/grenade)) // Allahu ackbar! ALLAHU ACKBARR!!
							var/obj/item/weapon/grenade/G = A
							G.attack_self(src)
							if(prob(smartness))
								npcDrop(G,1)
								sleep(15)
								throw_item(TARGET)

				if(!main_hand && other_hand)
					swap_hands()
				if(main_hand)
					if(main_hand.force != 0)
						if(istype(main_hand,/obj/item/weapon/gun))
							var/obj/item/weapon/gun/G = main_hand
							if(G.can_trigger_gun(src))
								if(istype(main_hand,/obj/item/weapon/gun/projectile))
									var/obj/item/weapon/gun/projectile/P = main_hand
									if(!P.chambered)
										P.chamber_round()
										P.update_icon()
									else if(P.get_ammo(1) == 0)
										P.update_icon()
										npcDrop(P,1)
									else
										P.afterattack(TARGET, src)
								else if(istype(main_hand,/obj/item/weapon/gun/energy))
									var/obj/item/weapon/gun/energy/P = main_hand
									var/stunning = 0
									for(var/A in P.ammo_type)
										if(ispath(A,/obj/item/ammo_casing/energy/electrode))
											stunning = 1
									var/shouldFire = 1
									var/mob/stunCheck = TARGET
									if(stunning && stunCheck.stunned)
										shouldFire = 0
									if(shouldFire)
										if(P.power_supply.charge <= 10) // can shoot seems to bug out for tasers, using this hacky method instead
											P.update_icon()
											npcDrop(P,1)
										else
											P.afterattack(TARGET, src)
								else
									if(get_dist(src,TARGET) > 6)
										if(!walk2derpless(TARGET))
											timeout++
									else
										var/obj/item/weapon/W = main_hand
										W.attack(TARGET,src)
							else
								G.loc = get_turf(src) // drop item works inconsistently
								enforce_hands()
								update_icons()
				else
					if(targetRange(TARGET) > 2)
						tryWalk(TARGET)
					else
						if(Adjacent(TARGET))
							a_intent = pick("disarm","harm")
							M.attack_hand(src)
			timeout++
		else if(timeout >= 10 || !(targetRange(M) > 14))
			doing = doing & ~FIGHTING
			timeout = 0
			TARGET = null
			retal = 0
			retal_target = null
		else
			timeout++

/mob/living/carbon/human/interactive/proc/nearbyscan(obj)
	nearby = list()
	for(var/mob/living/M in view(4,src))
		if(M != src)
			nearby += M

//END OF MODULES
/mob/living/carbon/human/interactive/angry/New()
	TRAITS |= TRAIT_ROBUST
	TRAITS |= TRAIT_MEAN
	faction += "bot_angry"
	..()

/mob/living/carbon/human/interactive/friendly/New()
	TRAITS |= TRAIT_FRIENDLY
	TRAITS |= TRAIT_UNROBUST
	faction += "bot_friendly"
	faction += "neutral"
	functions -= "combat"
	..()

/mob/living/carbon/human/interactive/greytide/New()
	TRAITS |= TRAIT_ROBUST
	TRAITS |= TRAIT_MEAN
	TRAITS |= TRAIT_THIEVING
	TRAITS |= TRAIT_DUMB
	maxInterest = 5 // really short attention span
	targetInterestShift = 2 // likewise
	faction += "bot_grey"
	graytide = 1
	..()


code:
//These procs handle putting s tuff in your hand. It's probably best to use these rather than setting l_hand = ...etc
//as they handle all relevant stuff like adding it to the player's screen and updating their overlays.

//Returns the thing in our active hand
/mob/proc/get_active_hand()
	if(hand)
		return l_hand
	else
		return r_hand

//Returns the thing in our inactive hand
/mob/proc/get_inactive_hand()
	if(hand)
		return r_hand
	else
		return l_hand

//Returns if a certain item can be equipped to a certain slot.
// Currently invalid for two-handed items - call obj/item/mob_can_equip() instead.
/mob/proc/can_equip(obj/item/I, slot, disable_warning = 0)
	return 0

//Puts the item into your l_hand if possible and calls all necessary triggers/updates. returns 1 on success.
/mob/proc/put_in_l_hand(obj/item/W)
	if(!put_in_hand_check(W))
		return 0
	if(!l_hand)
		W.loc = src		//TODO: move to equipped?
		l_hand = W
		W.layer = 20	//TODO: move to equipped?
		W.equipped(src,slot_l_hand)
		if(pulling == W)
			stop_pulling()
		update_inv_l_hand()
		W.pixel_x = initial(W.pixel_x)
		W.pixel_y = initial(W.pixel_y)
		return 1
	return 0

//Puts the item into your r_hand if possible and calls all necessary triggers/updates. returns 1 on success.
/mob/proc/put_in_r_hand(obj/item/W)
	if(!put_in_hand_check(W))
		return 0
	if(!r_hand)
		W.loc = src
		r_hand = W
		W.layer = 20
		W.equipped(src,slot_r_hand)
		if(pulling == W)
			stop_pulling()
		update_inv_r_hand()
		W.pixel_x = initial(W.pixel_x)
		W.pixel_y = initial(W.pixel_y)
		return 1
	return 0

/mob/proc/put_in_hand_check(obj/item/W)
	if(lying && !(W.flags&ABSTRACT))
		return 0
	if(!istype(W))
		return 0
	return 1

//Puts the item into our active hand if possible. returns 1 on success.
/mob/proc/put_in_active_hand(obj/item/W)
	if(hand)
		return put_in_l_hand(W)
	else
		return put_in_r_hand(W)

//Puts the item into our inactive hand if possible. returns 1 on success.
/mob/proc/put_in_inactive_hand(obj/item/W)
	if(hand)
		return put_in_r_hand(W)
	else
		return put_in_l_hand(W)

//Puts the item our active hand if possible. Failing that it tries our inactive hand. Returns 1 on success.
//If both fail it drops it on the floor and returns 0.
//This is probably the main one you need to know :)
/mob/proc/put_in_hands(obj/item/W)
	if(!W)
		return 0
	if(put_in_active_hand(W))
		return 1
	else if(put_in_inactive_hand(W))
		return 1
	else
		W.loc = get_turf(src)
		W.layer = initial(W.layer)
		W.dropped(src)
		return 0

/mob/proc/drop_item_v()		//this is dumb.
	if(stat == CONSCIOUS && isturf(loc))
		return drop_item()
	return 0

//Drops the item in our left hand
/mob/proc/drop_l_hand()
	if(!loc.allow_drop())
		return
	return unEquip(l_hand) //All needed checks are in unEquip

//Drops the item in our right hand
/mob/proc/drop_r_hand()
	if(!loc.allow_drop())
		return
	return unEquip(r_hand)

//Drops the item in our active hand.
/mob/proc/drop_item()
	if(hand)
		return drop_l_hand()
	else
		return drop_r_hand()

//Here lie drop_from_inventory and before_item_take, already forgotten and not missed.

/mob/proc/canUnEquip(obj/item/I, force)
	if(!I)
		return 1
	if((I.flags & NODROP) && !force)
		return 0
	return 1

/mob/proc/unEquip(obj/item/I, force) //Force overrides NODROP for things like wizarditis and admin undress.
	if(!I) //If there's nothing to drop, the drop is automatically succesfull. If(unEquip) should generally be used to check for NODROP.
		return 1

	if((I.flags & NODROP) && !force)
		return 0

	if(I == r_hand)
		r_hand = null
		update_inv_r_hand()
	else if(I == l_hand)
		l_hand = null
		update_inv_l_hand()

	if(I)
		if(client)
			client.screen -= I
		I.loc = loc
		I.dropped(src)
		if(I)
			I.layer = initial(I.layer)
	return 1

//Attemps to remove an object on a mob.  Will not move it to another area or such, just removes from the mob.
/mob/proc/remove_from_mob(var/obj/O)
	unEquip(O)
	O.screen_loc = null
	return 1

//Outdated but still in use apparently. This should at least be a human proc.
/mob/proc/get_equipped_items()
	var/list/items = new/list()

	if(hasvar(src,"back"))
		if(src:back)
			items += src:back
	if(hasvar(src,"belt"))
		if(src:belt)
			items += src:belt
	if(hasvar(src,"ears"))
		if(src:ears)
			items += src:ears
	if(hasvar(src,"glasses"))
		if(src:glasses)
			items += src:glasses
	if(hasvar(src,"gloves"))
		if(src:gloves)
			items += src:gloves
	if(hasvar(src,"head"))
		if(src:head)
			items += src:head
	if(hasvar(src,"shoes"))
		if(src:shoes)
			items += src:shoes
	if(hasvar(src,"wear_id"))
		if(src:wear_id)
			items += src:wear_id
	if(hasvar(src,"wear_mask"))
		if(src:wear_mask)
			items += src:wear_mask
	if(hasvar(src,"wear_suit"))
		if(src:wear_suit)
			items += src:wear_suit
/*	if(hasvar(src,"w_radio"))
		if(src:w_radio)
			items += src:w_radio  commenting this out since headsets go on your ears now PLEASE DON'T BE MAD KEELIN */
	if(hasvar(src,"w_uniform"))
		if(src:w_uniform)
			items += src:w_uniform

/*	if(hasvar(src,"l_hand"))
		if(src:l_hand)
			items += src:l_hand
	if(hasvar(src,"r_hand"))
		if(src:r_hand)
			items += src:r_hand*/

	return items

/obj/item/proc/equip_to_best_slot(var/mob/M)
	if(src != M.get_active_hand())
		M << "You are not holding anything to equip!"
		return 0

	if(M.equip_to_appropriate_slot(src))
		if(M.hand)
			M.update_inv_l_hand()
		else
			M.update_inv_r_hand()
		return 1

	if(M.s_active && M.s_active.can_be_inserted(src,1))	//if storage active insert there
		M.s_active.handle_item_insertion(src)
		return 1

	var/obj/item/weapon/storage/S = M.get_inactive_hand()
	if(istype(S) && S.can_be_inserted(src,1))	//see if we have box in other hand
		S.handle_item_insertion(src)
		return 1

	S = M.get_item_by_slot(slot_belt)
	if(istype(S) && S.can_be_inserted(src,1))		//else we put in belt
		S.handle_item_insertion(src)
		return 1

	S = M.get_item_by_slot(slot_drone_storage)	//else we put in whatever is in drone storage
	if(istype(S) && S.can_be_inserted(src,1))
		S.handle_item_insertion(src)

	S = M.get_item_by_slot(slot_back)	//else we put in backpack
	if(istype(S) && S.can_be_inserted(src,1))
		S.handle_item_insertion(src)
		playsound(src.loc, "rustle", 50, 1, -5)
		return 1

	M << "You are unable to equip that!"
	return 0

/mob/verb/quick_equip()
	set name = "quick-equip"
	set hidden = 1

	var/obj/item/I = get_active_hand()
	if (I)
		I.equip_to_best_slot(src)

//used in code for items usable by both carbon and drones, this gives the proper back slot for each mob.(defibrillator, backpack watertank, ...)
/mob/proc/getBackSlot()
	return slot_back

/mob/proc/getBeltSlot()
	return slot_belt


code:
/obj/effect/dummy/slaughter //Can't use the wizard one, blocked by jaunt/slow
	name = "water"
	icon = 'icons/effects/effects.dmi'
	icon_state = "nothing"
	var/canmove = 1
	density = 0
	anchored = 1
	invisibility = 60
	burn_state = LAVA_PROOF

/obj/effect/dummy/slaughter/relaymove(mob/user, direction)
	forceMove(get_step(src,direction))

/obj/effect/dummy/slaughter/ex_act()
	return
/obj/effect/dummy/slaughter/bullet_act()
	return

/obj/effect/dummy/slaughter/singularity_act()
	return

/obj/effect/dummy/slaughter/Destroy()
	..()
	return QDEL_HINT_PUTINPOOL

/mob/living/proc/phaseout(obj/effect/decal/cleanable/B)
	if(iscarbon(src))
		var/mob/living/carbon/C = src
		if(C.l_hand || C.r_hand)
			C << "You may not hold items while blood crawling!"
			return 0
		var/obj/item/weapon/bloodcrawl/B1 = new(C)
		var/obj/item/weapon/bloodcrawl/B2 = new(C)
		B1.icon_state = "bloodhand_left"
		B2.icon_state = "bloodhand_right"
		C.put_in_hands(B1)
		C.put_in_hands(B2)
		C.regenerate_icons()
	var/mob/living/kidnapped = null
	var/turf/mobloc = get_turf(src.loc)
	src.notransform = TRUE
	spawn(0)
		src.visible_message("[src] sinks into the pool of blood!")
		playsound(get_turf(src), 'sound/magic/enter_blood.ogg', 100, 1, -1)
		var/obj/effect/dummy/slaughter/holder = PoolOrNew(/obj/effect/dummy/slaughter,mobloc)
		src.ExtinguishMob()
		if(buckled)
			buckled.unbuckle_mob(src,force=1)
		if(buckled_mobs.len)
			unbuckle_all_mobs(force=1)
		if(pulledby)
			pulledby.stop_pulling()
		if(src.pulling && src.bloodcrawl == BLOODCRAWL_EAT)
			if(istype(src.pulling, /mob/living))
				var/mob/living/victim = src.pulling
				if(victim.stat == CONSCIOUS)
					src.visible_message("[victim] kicks free of the blood pool just before entering it!")
				else if(victim.reagents && victim.reagents.has_reagent("demonsblood"))
					visible_message("Something prevents [victim] from entering the pool!", "A strange force is blocking [victim] from entering!")
				else
					victim.loc = holder
					victim.emote("scream")
					src.visible_message("[src] drags [victim] into the pool of blood!")
					kidnapped = victim
		src.loc = holder
		src.holder = holder
		if(kidnapped)
			src << "You begin to feast on [kidnapped]. You can not move while you are doing this."
			for(var/i = 3; i > 0; i--)
				playsound(get_turf(src),'sound/magic/Demon_consume.ogg', 100, 1)
				sleep(30)
			if(kidnapped)
				if(kidnapped.reagents && kidnapped.reagents.has_reagent("devilskiss"))
					src << "AAH! THEIR FLESH! IT BURNS!"
					adjustBruteLoss(25) //I can't use adjustHealth() here because bloodcrawl affects /mob/living and adjustHealth() only affects simple mobs
					kidnapped.loc = get_turf(B)
					kidnapped.visible_message("[B] violently expels [kidnapped]!")
				else
					src << "You devour [kidnapped]. Your health is fully restored."
					src.adjustBruteLoss(-1000)
					src.adjustFireLoss(-1000)
					src.adjustOxyLoss(-1000)
					src.adjustToxLoss(-1000)
					kidnapped.ghostize()
					qdel(kidnapped)
			else
				src << "You happily devour... nothing? Your meal vanished at some point!"
		src.notransform = 0
	return 1

/obj/item/weapon/bloodcrawl
	name = "blood crawl"
	desc = "You are unable to hold anything while in this form."
	icon = 'icons/effects/blood.dmi'
	flags = NODROP|ABSTRACT

/mob/living/proc/phasein(obj/effect/decal/cleanable/B)
	if(src.notransform)
		src << "Finish eating first!"
		return 0
	B.visible_message("[B] starts to bubble...")
	if(!do_after(src, 20, target = B))
		return
	if(!B)
		return
	src.loc = B.loc
	src.client.eye = src
	src.visible_message("[src] rises out of the pool of blood!")
	playsound(get_turf(src), 'sound/magic/exit_blood.ogg', 100, 1, -1)
	if(iscarbon(src))
		var/mob/living/carbon/C = src
		for(var/obj/item/weapon/bloodcrawl/BC in C)
			BC.flags = null
			C.unEquip(BC)
			qdel(BC)
	var/oldcolor = src.color
	if(istype(B, /obj/effect/decal/cleanable/xenoblood)) //Makes the mob have the color of the blood pool it came out of for a few seconds
		src.color = rgb(43, 186, 0)
	else
		src.color = rgb(149, 10, 10)
	qdel(src.holder)
	src.holder = null
	spawn(30)
		src.color = oldcolor
	return 1


code:
#define HEAT_DAMAGE_LEVEL_1 2 //Amount of damage applied when your body temperature just passes the 360.15k safety point
#define HEAT_DAMAGE_LEVEL_2 3 //Amount of damage applied when your body temperature passes the 400K point
#define HEAT_DAMAGE_LEVEL_3 8 //Amount of damage applied when your body temperature passes the 460K point and you are on fire

/mob/living/carbon/alien
	name = "alien"
	voice_name = "alien"
	icon = 'icons/mob/alien.dmi'
	gender = NEUTER
	dna = null
	faction = list("alien")
	ventcrawler = 2
	languages = ALIEN
	sight = SEE_MOBS
	see_in_dark = 4
	verb_say = "hisses"
	bubble_icon = "alien"
	type_of_meat = /obj/item/weapon/reagent_containers/food/snacks/meat/slab/xeno
	var/nightvision = 1

	var/obj/item/weapon/card/id/wear_id = null // Fix for station bounced radios -- Skie
	var/has_fine_manipulation = 0
	var/move_delay_add = 0 // movement delay to add

	status_flags = CANPARALYSE|CANPUSH

	var/heat_protection = 0.5
	var/leaping = 0
	gib_type = /obj/effect/decal/cleanable/xenoblood/xgibs
	unique_name = 1

/mob/living/carbon/alien/New()
	verbs += /mob/living/proc/mob_sleep
	verbs += /mob/living/proc/lay_down

	internal_organs += new /obj/item/organ/internal/brain/alien
	internal_organs += new /obj/item/organ/internal/alien/hivenode
	for(var/obj/item/organ/internal/I in internal_organs)
		I.Insert(src)

	AddAbility(new/obj/effect/proc_holder/alien/nightvisiontoggle(null))
	..()

/mob/living/carbon/alien/assess_threat() // beepsky won't hunt aliums
	return -10

/mob/living/carbon/alien/adjustToxLoss(amount)
	return

/mob/living/carbon/alien/adjustFireLoss(amount) // Weak to Fire
	if(amount > 0)
		..(amount * 2)
	else
		..(amount)
	return

/mob/living/carbon/alien/check_eye_prot()
	return ..() + 2

/mob/living/carbon/alien/getToxLoss()
	return 0

/mob/living/carbon/alien/handle_environment(datum/gas_mixture/environment)
	if(!environment)
		return

	var/loc_temp = get_temperature(environment)

	// Aliens are now weak to fire.

	//After then, it reacts to the surrounding atmosphere based on your thermal protection
	if(!on_fire) // If you're on fire, ignore local air temperature
		if(loc_temp > bodytemperature)
			//Place is hotter than we are
			var/thermal_protection = heat_protection //This returns a 0 - 1 value, which corresponds to the percentage of heat protection.
			if(thermal_protection < 1)
				bodytemperature += (1-thermal_protection) * ((loc_temp - bodytemperature) / BODYTEMP_HEAT_DIVISOR)
		else
			bodytemperature += 1 * ((loc_temp - bodytemperature) / BODYTEMP_HEAT_DIVISOR)

	if(bodytemperature > 360.15)
		//Body temperature is too hot.
		throw_alert("alien_fire", /obj/screen/alert/alien_fire)
		switch(bodytemperature)
			if(360 to 400)
				apply_damage(HEAT_DAMAGE_LEVEL_1, BURN)
			if(400 to 460)
				apply_damage(HEAT_DAMAGE_LEVEL_2, BURN)
			if(460 to INFINITY)
				if(on_fire)
					apply_damage(HEAT_DAMAGE_LEVEL_3, BURN)
				else
					apply_damage(HEAT_DAMAGE_LEVEL_2, BURN)
	else
		clear_alert("alien_fire")

/mob/living/carbon/alien/ex_act(severity, target)
	..()

	switch (severity)
		if (1)
			gib()
			return

		if (2)
			adjustBruteLoss(60)
			adjustFireLoss(60)
			adjustEarDamage(30,120)

		if(3)
			adjustBruteLoss(30)
			if (prob(50))
				Paralyse(1)
			adjustEarDamage(15,60)

	updatehealth()

/mob/living/carbon/alien/handle_fire()//Aliens on fire code
	if(..())
		return
	bodytemperature += BODYTEMP_HEATING_MAX //If you're on fire, you heat up!
	return

/mob/living/carbon/alien/reagent_check(datum/reagent/R) //can metabolize all reagents
	return 0

/mob/living/carbon/alien/IsAdvancedToolUser()
	return has_fine_manipulation

/mob/living/carbon/alien/Stat()
	..()

	if(statpanel("Status"))
		stat(null, "Intent: [a_intent]")

/mob/living/carbon/alien/Stun(amount)
	if(status_flags & CANSTUN)
		stunned = max(max(stunned,amount),0) //can't go below 0, getting a low amount of stun doesn't lower your current stun
	else
		// add some movement delay
		move_delay_add = min(move_delay_add + round(amount / 2), 10) // a maximum delay of 10
	return

/mob/living/carbon/alien/getTrail()
	if(getBruteLoss() < 200)
		return pick (list("xltrails_1", "xltrails2"))
	else
		return pick (list("xttrails_1", "xttrails2"))
/*----------------------------------------
Proc: AddInfectionImages()
Des: Gives the client of the alien an image on each infected mob.
----------------------------------------*/
/mob/living/carbon/alien/proc/AddInfectionImages()
	if (client)
		for (var/mob/living/C in mob_list)
			if(C.status_flags & XENO_HOST)
				var/obj/item/organ/internal/body_egg/alien_embryo/A = C.getorgan(/obj/item/organ/internal/body_egg/alien_embryo)
				if(A)
					var/I = image('icons/mob/alien.dmi', loc = C, icon_state = "infected[A.stage]")
					client.images += I
	return

/*----------------------------------------
Proc: RemoveInfectionImages()
Des: Removes all infected images from the alien.
----------------------------------------*/
/mob/living/carbon/alien/proc/RemoveInfectionImages()
	if (client)
		for(var/image/I in client.images)
			if(dd_hasprefix_case(I.icon_state, "infected"))
				qdel(I)
	return

/mob/living/carbon/alien/canBeHandcuffed()
	return 1

/mob/living/carbon/alien/get_standard_pixel_y_offset(lying = 0)
	return initial(pixel_y)

/mob/living/carbon/alien/proc/alien_evolve(mob/living/carbon/alien/new_xeno)
	src << "You begin to evolve!"
	visible_message("[src] begins to twist and contort!")
	if(mind)
		mind.transfer_to(new_xeno)
	qdel(src)

#undef HEAT_DAMAGE_LEVEL_1
#undef HEAT_DAMAGE_LEVEL_2
#undef HEAT_DAMAGE_LEVEL_3

/mob/living/carbon/alien/update_sight()
	if(!client)
		return
	if(stat == DEAD)
		sight |= SEE_TURFS
		sight |= SEE_MOBS
		sight |= SEE_OBJS
		see_in_dark = 8
		see_invisible = SEE_INVISIBLE_OBSERVER
		return

	sight = SEE_MOBS
	if(nightvision)
		see_in_dark = 8
		see_invisible = SEE_INVISIBLE_MINIMUM
	else
		see_in_dark = 4
		see_invisible = SEE_INVISIBLE_LIVING

	if(client.eye != src)
		var/atom/A = client.eye
		if(A.update_remote_sight(src)) //returns 1 if we override all other sight updates.
			return

	for(var/obj/item/organ/internal/cyberimp/eyes/E in internal_organs)
		sight |= E.sight_flags
		if(E.dark_view)
			see_in_dark = max(see_in_dark, E.dark_view)
		if(E.see_invisible)
			see_invisible = min(see_invisible, E.see_invisible)

	if(see_override)
		see_invisible = see_override


code:
/mob/living/carbon/alien/hitby(atom/movable/AM, skipcatch, hitpush)
	..(AM, skipcatch = 1, hitpush = 0)

/*Code for aliens attacking aliens. Because aliens act on a hivemind, I don't see them as very aggressive with each other.
As such, they can either help or harm other aliens. Help works like the human help command while harm is a simple nibble.
In all, this is a lot like the monkey code. /N
*/
/mob/living/carbon/alien/attack_alien(mob/living/carbon/alien/M)
	if(!ticker || !ticker.mode)
		M << "You cannot attack people before the game has started."
		return

	if (istype(loc, /turf) && istype(loc.loc, /area/start))
		M << "No attacking people at spawn, you jackass."
		return

	switch(M.a_intent)

		if ("help")
			AdjustSleeping(-5)
			resting = 0
			AdjustParalysis(-3)
			AdjustStunned(-3)
			AdjustWeakened(-3)
			visible_message("[M.name] nuzzles [src] trying to wake it up!")

		if ("grab")
			grabbedby(M)

		else
			if (health > 0)
				M.do_attack_animation(src)
				playsound(loc, 'sound/weapons/bite.ogg', 50, 1, -1)
				var/damage = 1
				visible_message("[M.name] bites [src]!", \
						"[M.name] bites [src]!")
				adjustBruteLoss(damage)
				add_logs(M, src, "attacked")
				updatehealth()
			else
				M << "[name] is too injured for that."
	return

/mob/living/carbon/alien/attack_larva(mob/living/carbon/alien/larva/L)
	return attack_alien(L)

/mob/living/carbon/alien/attack_hand(mob/living/carbon/human/M)
	if(..())	//to allow surgery to return properly.
		return 0

	switch(M.a_intent)
		if("help")
			help_shake_act(M)
		if("grab")
			grabbedby(M)
		if ("harm", "disarm")
			M.do_attack_animation(src)
			return 1
	return 0

/mob/living/carbon/alien/attack_paw(mob/living/carbon/monkey/M)
	if(..())
		if (stat != DEAD)
			adjustBruteLoss(rand(1, 3))
			updatehealth()
	return

/mob/living/carbon/alien/attack_animal(mob/living/simple_animal/M)
	if(..())
		var/damage = rand(M.melee_damage_lower, M.melee_damage_upper)
		switch(M.melee_damage_type)
			if(BRUTE)
				adjustBruteLoss(damage)
			if(BURN)
				adjustFireLoss(damage)
			if(TOX)
				adjustToxLoss(damage)
			if(OXY)
				adjustOxyLoss(damage)
			if(CLONE)
				adjustCloneLoss(damage)
			if(STAMINA)
				adjustStaminaLoss(damage)
		updatehealth()

/mob/living/carbon/alien/attack_slime(mob/living/simple_animal/slime/M)
	if(..()) //successful slime attack
		var/damage = rand(5, 35)
		if(M.is_adult)
			damage = rand(10, 40)
		adjustBruteLoss(damage)
		add_logs(M, src, "attacked")
		updatehealth()


code:
/mob/living/carbon/alien/spawn_gibs()
	xgibs(loc, viruses)

/mob/living/carbon/alien/gib_animation(animate)
	..(animate, "gibbed-a")

/mob/living/carbon/alien/spawn_dust()
	new /obj/effect/decal/remains/xeno(loc)

/mob/living/carbon/alien/spawn_dust()
	new /obj/effect/decal/remains/xeno(loc)

/mob/living/carbon/alien/dust_animation(animate)
	..(animate, "dust-a")

/mob/living/carbon/alien/dust(var/animation = 1)
	..()