code:
//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31

// Mulebot - carries crates around for Quartermaster
// Navigates via floor navbeacons
// Remote Controlled from QM's PDA

var/global/mulebot_count = 0

#define SIGH 0
#define ANNOYED 1
#define DELIGHT 2

/mob/living/simple_animal/bot/mulebot
	name = "\improper MULEbot"
	desc = "A Multiple Utility Load Effector bot."
	icon_state = "mulebot0"
	density = 1
	anchored = 1
	animate_movement=1
	health = 50
	maxHealth = 50
	damage_coeff = list(BRUTE = 0.5, BURN = 0.7, TOX = 0, CLONE = 0, STAMINA = 0, OXY = 0)
	a_intent = "harm" //No swapping
	buckle_lying = 0
	mob_size = MOB_SIZE_LARGE

	radio_key = /obj/item/device/encryptionkey/headset_cargo
	radio_channel = "Supply"

	bot_type = MULE_BOT
	model = "MULE"
	bot_core_type = /obj/machinery/bot_core/mulebot

	suffix = ""

	var/atom/movable/load = null
	var/mob/living/passenger = null
	var/turf/target				// this is turf to navigate to (location of beacon)
	var/loaddir = 0				// this the direction to unload onto/load from
	var/home_destination = "" 	// tag of home beacon

	var/reached_target = 1 	//true if already reached the target

	var/auto_return = 1		// true if auto return to home beacon after unload
	var/auto_pickup = 1 	// true if auto-pickup at beacon
	var/report_delivery = 1 // true if bot will announce an arrival to a location.

	var/obj/item/weapon/stock_parts/cell/cell
	var/bloodiness = 0

/mob/living/simple_animal/bot/mulebot/New()
	..()
	wires = new /datum/wires/mulebot(src)
	var/datum/job/cargo_tech/J = new/datum/job/cargo_tech
	access_card.access = J.get_access()
	prev_access = access_card.access
	cell = new(src)
	cell.charge = 2000
	cell.maxcharge = 2000

	spawn(10) // must wait for map loading to finish
		mulebot_count += 1
		if(!suffix)
			set_suffix("#[mulebot_count]")

/mob/living/simple_animal/bot/mulebot/Destroy()
	unload(0)
	qdel(wires)
	wires = null
	return ..()

/mob/living/simple_animal/bot/mulebot/proc/set_suffix(suffix)
	src.suffix = suffix
	if(paicard)
		bot_name = "\improper MULEbot ([suffix])"
	else
		name = "\improper MULEbot ([suffix])"

/mob/living/simple_animal/bot/mulebot/bot_reset()
	..()
	reached_target = 0

/mob/living/simple_animal/bot/mulebot/attackby(obj/item/I, mob/user, params)
	if(istype(I, /obj/item/weapon/screwdriver))
		..()
		if(open)
			on = FALSE
	else if(istype(I,/obj/item/weapon/stock_parts/cell) && open && !cell)
		if(!user.drop_item())
			return
		var/obj/item/weapon/stock_parts/cell/C = I
		C.loc = src
		cell = C
		visible_message("[user] inserts a cell into [src].",
						"You insert the new cell into [src].")
	else if(istype(I, /obj/item/weapon/crowbar) && open && cell)
		cell.add_fingerprint(usr)
		cell.loc = loc
		cell = null
		visible_message("[user] crowbars out the power cell from [src].",
						"You pry the powercell out of [src].")
	else if(is_wire_tool(I) && open)
		return attack_hand(user)
	else if(load && ismob(load))  // chance to knock off rider
		if(prob(1 + I.force * 2))
			unload(0)
			user.visible_message("[user] knocks [load] off [src] with \the [I]!",
									"You knock [load] off [src] with \the [I]!")
		else
			user << "You hit [src] with \the [I] but to no effect!"
			..()
	else
		..()
	update_icon()
	return

/mob/living/simple_animal/bot/mulebot/emag_act(mob/user)
	if(emagged < 1)
		emagged = 1
	if(!open)
		locked = !locked
		user << "You [locked ? "lock" : "unlock"] the [src]'s controls!"
	flick("mulebot-emagged", src)
	playsound(loc, 'sound/effects/sparks1.ogg', 100, 0)

/mob/living/simple_animal/bot/mulebot/update_icon()
	if(open)
		icon_state="mulebot-hatch"
	else
		icon_state = "mulebot[wires.is_cut(WIRE_AVOIDANCE)]"
	overlays.Cut()
	if(load && !ismob(load))//buckling handles the mob offsets
		load.pixel_y = initial(load.pixel_y) + 9
		if(load.layer < layer)
			load.layer = layer + 0.1
		overlays += load
	return

/mob/living/simple_animal/bot/mulebot/ex_act(severity)
	unload(0)
	switch(severity)
		if(1)
			qdel(src)
		if(2)
			for(var/i = 1; i < 3; i++)
				wires.cut_random()
		if(3)
			wires.cut_random()
	return

/mob/living/simple_animal/bot/mulebot/bullet_act(obj/item/projectile/Proj)
	if(..())
		if(prob(50) && !isnull(load))
			unload(0)
		if(prob(25))
			visible_message("Something shorts out inside [src]!")
			wires.cut_random()

/mob/living/simple_animal/bot/mulebot/interact(mob/user)
	if(open && !istype(user, /mob/living/silicon/ai))
		wires.interact(user)
	else
		if(wires.is_cut(WIRE_RX) && istype(user, /mob/living/silicon/ai))
			return
		ui_interact(user)

/mob/living/simple_animal/bot/mulebot/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = 0, \
										datum/tgui/master_ui = null, datum/ui_state/state = default_state)
	ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
	if(!ui)
		ui = new(user, src, ui_key, "mulebot", name, 600, 375, master_ui, state)
		ui.open()

/mob/living/simple_animal/bot/mulebot/ui_data(mob/user)
	var/list/data = list()
	data["on"] = on
	data["locked"] = locked
	data["siliconUser"] = user.has_unlimited_silicon_privilege
	data["mode"] = mode ? mode_name[mode] : "Ready"
	data["modeStatus"] = ""
	switch(mode)
		if(BOT_IDLE, BOT_DELIVER, BOT_GO_HOME)
			data["modeStatus"] = "good"
		if(BOT_BLOCKED, BOT_NAV, BOT_WAIT_FOR_NAV)
			data["modeStatus"] = "average"
		if(BOT_NO_ROUTE)
			data["modeStatus"] = "bad"
		else
	data["load"] = load ? load.name : null
	data["destination"] = destination ? destination : null
	data["cell"] = cell ? TRUE : FALSE
	data["cellPercent"] = cell ? cell.percent() : null
	data["autoReturn"] = auto_return
	data["autoPickup"] = auto_pickup
	data["reportDelivery"] = report_delivery
	data["haspai"] = paicard ? TRUE : FALSE
	return data

/mob/living/simple_animal/bot/mulebot/ui_act(action, params)
	if(..() || (locked && !usr.has_unlimited_silicon_privilege))
		return
	switch(action)
		if("lock")
			if(usr.has_unlimited_silicon_privilege)
				locked = !locked
				. = TRUE
		if("power")
			if(on)
				turn_off()
			else if(cell && !open)
				if(!turn_on())
					usr << "You can't switch on [src]!"
					return
			. = TRUE
		else
			bot_control(action, usr) // Kill this later.
			. = TRUE

/mob/living/simple_animal/bot/mulebot/bot_control(command, mob/user, pda = 0)
	if(pda && wires.is_cut(WIRE_RX)) // MULE wireless is controlled by wires.
		return

	switch(command)
		if("stop")
			if(mode >= BOT_DELIVER)
				bot_reset()
		if("go")
			if(mode == BOT_IDLE)
				start()
		if("home")
			if(mode == BOT_IDLE || mode == BOT_DELIVER)
				start_home()
		if("destination")
			var/new_dest = input(user, "Enter Destination:", name, destination) as null|anything in deliverybeacontags
			if(new_dest)
				set_destination(new_dest)
		if("setid")
			var/new_id = stripped_input(user, "Enter ID:", name, suffix, MAX_NAME_LEN)
			if(new_id)
				set_suffix(new_id)
		if("sethome")
			var/new_home = input(user, "Enter Home:", name, home_destination) as null|anything in deliverybeacontags
			if(new_home)
				home_destination = new_home
		if("unload")
			if(load && mode != BOT_HUNT)
				if(loc == target)
					unload(loaddir)
				else
					unload(0)
		if("autoret")
			auto_return = !auto_return
		if("autopick")
			auto_pickup = !auto_pickup
		if("report")
			report_delivery = !report_delivery
		if("ejectpai")
			ejectpairemote(user)

// TODO: remove this; PDAs currently depend on it
/mob/living/simple_animal/bot/mulebot/get_controls(mob/user)
	var/ai = issilicon(user)
	var/dat
	dat += "
Multiple Utility Load Effector Mk. V
"
	dat += "ID: [suffix]
"
	dat += "Power: [on ? "On" : "Off"]
"
	dat += "
Status
"
	dat += "
"
	switch(mode)
		if(BOT_IDLE)
			dat += "Ready"
		if(BOT_DELIVER)
			dat += "[mode_name[BOT_DELIVER]]"
		if(BOT_GO_HOME)
			dat += "[mode_name[BOT_GO_HOME]]"
		if(BOT_BLOCKED)
			dat += "[mode_name[BOT_BLOCKED]]"
		if(BOT_NAV,BOT_WAIT_FOR_NAV)
			dat += "[mode_name[BOT_NAV]]"
		if(BOT_NO_ROUTE)
			dat += "[mode_name[BOT_NO_ROUTE]]"
	dat += "
"

	dat += "Current Load: [load ? load.name : "none"]
"
	dat += "Destination: [!destination ? "none" : destination]
"
	dat += "Power level: [cell ? cell.percent() : 0]%"

	if(locked && !ai && !IsAdminGhost(user))
		dat += "

Controls are locked
Unlock Controls"
	else
		dat += "

Controls are unlocked
Lock Controls

"

		dat += "Toggle Power
"
		dat += "Stop
"
		dat += "Proceed
"
		dat += "Return to Home
"
		dat += "Set Destination
"
		dat += "Set Bot ID
"
		dat += "Set Home
"
		dat += "Toggle Auto Return Home ([auto_return ? "On":"Off"])
"
		dat += "Toggle Auto Pickup Crate ([auto_pickup ? "On":"Off"])
"
		dat += "Toggle Delivery Reporting ([report_delivery ? "On" : "Off"])
"
		if(load)
			dat += "Unload Now
"
		dat += "
The maintenance hatch is closed.
"

	return dat

// returns true if the bot has power
/mob/living/simple_animal/bot/mulebot/proc/has_power()
	return !open && cell && cell.charge > 0 && (!wires.is_cut(WIRE_POWER1) && !wires.is_cut(WIRE_POWER2))

/mob/living/simple_animal/bot/mulebot/proc/buzz(type)
	switch(type)
		if(SIGH)
			audible_message("[src] makes a sighing buzz.", "You hear an electronic buzzing sound.")
			playsound(loc, 'sound/machines/buzz-sigh.ogg', 50, 0)
		if(ANNOYED)
			audible_message("[src] makes an annoyed buzzing sound.", "You hear an electronic buzzing sound.")
			playsound(loc, 'sound/machines/buzz-two.ogg', 50, 0)
		if(DELIGHT)
			audible_message("[src] makes a delighted ping!", "You hear a ping.")
			playsound(loc, 'sound/machines/ping.ogg', 50, 0)

// mousedrop a crate to load the bot
// can load anything if hacked
/mob/living/simple_animal/bot/mulebot/MouseDrop_T(atom/movable/AM, mob/user)

	if(user.incapacitated() || user.lying)
		return

	if(!istype(AM))
		return

	load(AM)

// called to load a crate
/mob/living/simple_animal/bot/mulebot/proc/load(atom/movable/AM)
	if(load ||  AM.anchored)
		return

	if(!isturf(AM.loc)) //To prevent the loading from stuff from someone's inventory or screen icons.
		return

	var/obj/structure/closet/crate/CRATE
	if(istype(AM,/obj/structure/closet/crate))
		CRATE = AM
	else
		if(!wires.is_cut(WIRE_LOADCHECK))
			buzz(SIGH)
			return	// if not hacked, only allow crates to be loaded

	if(CRATE) // if it's a crate, close before loading
		CRATE.close()

	if(isobj(AM))
		var/obj/O = AM
		if(O.buckled_mobs.len || (locate(/mob) in AM)) //can't load non crates objects with mobs buckled to it or inside it.
			buzz(SIGH)
			return

	if(isliving(AM))
		if(!load_mob(AM))
			return
	else
		AM.loc = src

	load = AM
	mode = BOT_IDLE
	update_icon()

/mob/living/simple_animal/bot/mulebot/proc/load_mob(mob/living/M)
	can_buckle = TRUE
	if(buckle_mob(M))
		passenger = M
		load = M
		can_buckle = FALSE
		return TRUE
	return FALSE

/mob/living/simple_animal/bot/mulebot/post_buckle_mob(mob/living/M)
	if(M in buckled_mobs) //post buckling
		M.pixel_y = initial(M.pixel_y) + 9
		if(M.layer < layer)
			M.layer = layer + 0.1

	else //post unbuckling
		load = null
		M.layer = initial(M.layer)
		M.pixel_y = initial(M.pixel_y)

// called to unload the bot
// argument is optional direction to unload
// if zero, unload at bot's location
/mob/living/simple_animal/bot/mulebot/proc/unload(dirn)
	if(!load)
		return

	mode = BOT_IDLE

	overlays.Cut()

	unbuckle_all_mobs()

	if(load)
		load.loc = loc
		load.pixel_y = initial(load.pixel_y)
		load.layer = initial(load.layer)
		if(dirn)
			var/turf/T = loc
			var/turf/newT = get_step(T,dirn)
			if(load.CanPass(load,newT)) //Can't get off onto anything that wouldn't let you pass normally
				step(load, dirn)
		load = null

/mob/living/simple_animal/bot/mulebot/call_bot()
	..()
	var/area/dest_area
	if(path && path.len)
		target = ai_waypoint //Target is the end point of the path, the waypoint set by the AI.
		dest_area = get_area(target)
		destination = format_text(dest_area.name)
		pathset = 1 //Indicates the AI's custom path is initialized.
		start()

/mob/living/simple_animal/bot/mulebot/handle_automated_action()
	if(!has_power())
		on = 0
		return
	if(on)
		var/speed = (wires.is_cut(WIRE_MOTOR1) ? 0 : 1) + (wires.is_cut(WIRE_MOTOR2) ? 0 : 2)
		//world << "speed: [speed]"
		var/num_steps = 0
		switch(speed)
			if(0)
				// do nothing
			if(1)
				num_steps = 10
			if(2)
				num_steps = 5
			if(3)
				num_steps = 3

		if(num_steps)
			process_bot()
			num_steps--
			if(mode != BOT_IDLE)
				spawn(0)
					for(var/i=num_steps,i>0,i--)
						sleep(2)
						process_bot()

/mob/living/simple_animal/bot/mulebot/proc/process_bot()
	if(!on || client)
		return
	update_icon()

	switch(mode)
		if(BOT_IDLE) // idle
			return

		if(BOT_DELIVER, BOT_GO_HOME, BOT_BLOCKED) // navigating to deliver,home, or blocked
			if(loc == target) // reached target
				at_target()
				return

			else if(path.len > 0 && target) // valid path
				var/turf/next = path[1]
				reached_target = 0
				if(next == loc)
					path -= next
					return
				if(istype( next, /turf))
					//world << "at ([x],[y]) moving to ([next.x],[next.y])"

					if(bloodiness)
						var/obj/effect/decal/cleanable/blood/tracks/B = new(loc)
						B.blood_DNA |= blood_DNA.Copy()
						var/newdir = get_dir(next, loc)
						if(newdir == dir)
							B.dir = newdir
						else
							newdir = newdir | dir
							if(newdir == 3)
								newdir = 1
							else if(newdir == 12)
								newdir = 4
							B.dir = newdir
						bloodiness--

					var/oldloc = loc
					var/moved = step_towards(src, next)	// attempt to move
					if(cell) cell.use(1)
					if(moved && oldloc!=loc)	// successful move
						//world << "Successful move."
						blockcount = 0
						path -= loc

						if(destination == home_destination)
							mode = BOT_GO_HOME
						else
							mode = BOT_DELIVER

					else		// failed to move

						//world << "Unable to move."
						blockcount++
						mode = BOT_BLOCKED
						if(blockcount == 3)
							buzz(ANNOYED)

						if(blockcount > 10)	// attempt 10 times before recomputing
							// find new path excluding blocked turf
							buzz(SIGH)
							mode = BOT_WAIT_FOR_NAV
							blockcount = 0
							spawn(20)
								calc_path(avoid=next)
								if(path.len > 0)
									buzz(DELIGHT)
								mode = BOT_BLOCKED
							return
						return
				else
					buzz(ANNOYED)
					//world << "Bad turf."
					mode = BOT_NAV
					return
			else
				//world << "No path."
				mode = BOT_NAV
				return

		if(BOT_NAV)	// calculate new path
			//world << "Calc new path."
			mode = BOT_WAIT_FOR_NAV
			spawn(0)
				calc_path()

				if(path.len > 0)
					blockcount = 0
					mode = BOT_BLOCKED
					buzz(DELIGHT)

				else
					buzz(SIGH)

					mode = BOT_NO_ROUTE

// calculates a path to the current destination
// given an optional turf to avoid
/mob/living/simple_animal/bot/mulebot/calc_path(turf/avoid = null)
	path = get_path_to(src, target, /turf/proc/Distance_cardinal, 0, 250, id=access_card, exclude=avoid)

// sets the current destination
// signals all beacons matching the delivery code
// beacons will return a signal giving their locations
/mob/living/simple_animal/bot/mulebot/proc/set_destination(new_dest)
	new_destination = new_dest
	get_nav()

// starts bot moving to current destination
/mob/living/simple_animal/bot/mulebot/proc/start()
	if(!on)
		return
	if(destination == home_destination)
		mode = BOT_GO_HOME
	else
		mode = BOT_DELIVER
	update_icon()
	get_nav()

// starts bot moving to home
// sends a beacon query to find
/mob/living/simple_animal/bot/mulebot/proc/start_home()
	if(!on)
		return
	spawn(0)
		set_destination(home_destination)
		mode = BOT_BLOCKED
	update_icon()

// called when bot reaches current target
/mob/living/simple_animal/bot/mulebot/proc/at_target()
	if(!reached_target)
		radio_channel = "Supply" //Supply channel
		audible_message("[src] makes a chiming sound!", "You hear a chime.")
		playsound(loc, 'sound/machines/chime.ogg', 50, 0)
		reached_target = 1

		if(pathset) //The AI called us here, so notify it of our arrival.
			loaddir = dir //The MULE will attempt to load a crate in whatever direction the MULE is "facing".
			if(calling_ai)
				calling_ai << "\icon[src] [src] wirelessly plays a chiming sound!"
				playsound(calling_ai, 'sound/machines/chime.ogg',40, 0)
				calling_ai = null
				radio_channel = "AI Private" //Report on AI Private instead if the AI is controlling us.

		if(load)		// if loaded, unload at target
			if(report_delivery)
				speak("Destination [destination] reached. Unloading [load].",radio_channel)
			unload(loaddir)
		else
			// not loaded
			if(auto_pickup) // find a crate
				var/atom/movable/AM
				if(wires.is_cut(WIRE_LOADCHECK)) // if hacked, load first unanchored thing we find
					for(var/atom/movable/A in get_step(loc, loaddir))
						if(!A.anchored)
							AM = A
							break
				else			// otherwise, look for crates only
					AM = locate(/obj/structure/closet/crate) in get_step(loc,loaddir)
				if(AM && AM.Adjacent(src))
					load(AM)
					if(report_delivery)
						speak("Now loading [load] at [get_area(src)].", radio_channel)
		// whatever happened, check to see if we return home

		if(auto_return && home_destination && destination != home_destination)
			// auto return set and not at home already
			start_home()
			mode = BOT_BLOCKED
		else
			bot_reset()	// otherwise go idle

	return

// called when bot bumps into anything
/mob/living/simple_animal/bot/mulebot/Bump(atom/obs)
	if(wires.is_cut(WIRE_AVOIDANCE))	// usually just bumps, but if avoidance disabled knock over mobs
		var/mob/M = obs
		if(ismob(M))
			if(istype(M,/mob/living/silicon/robot))
				visible_message("[src] bumps into [M]!")
			else
				add_logs(src, M, "knocked down")
				visible_message("[src] knocks over [M]!")
				M.stop_pulling()
				M.Stun(8)
				M.Weaken(5)
	return ..()

// called from mob/living/carbon/human/Crossed()
// when mulebot is in the same loc
/mob/living/simple_animal/bot/mulebot/proc/RunOver(mob/living/carbon/human/H)
	add_logs(src, H, "run over", null, "(DAMTYPE: [uppertext(BRUTE)])")
	H.visible_message("[src] drives over [H]!", \
					"[src] drives over you!")
	playsound(loc, 'sound/effects/splat.ogg', 50, 1)

	var/damage = rand(5,15)
	H.apply_damage(2*damage, BRUTE, "head", run_armor_check("head", "melee"))
	H.apply_damage(2*damage, BRUTE, "chest", run_armor_check("chest", "melee"))
	H.apply_damage(0.5*damage, BRUTE, "l_leg", run_armor_check("l_leg", "melee"))
	H.apply_damage(0.5*damage, BRUTE, "r_leg", run_armor_check("r_leg", "melee"))
	H.apply_damage(0.5*damage, BRUTE, "l_arm", run_armor_check("l_arm", "melee"))
	H.apply_damage(0.5*damage, BRUTE, "r_arm", run_armor_check("r_arm", "melee"))

	var/obj/effect/decal/cleanable/blood/B = new(loc)
	B.add_blood_list(H)
	add_blood_list(H)
	bloodiness += 4

// player on mulebot attempted to move
/mob/living/simple_animal/bot/mulebot/relaymove(mob/user)
	if(user.incapacitated())
		return
	if(load == user)
		unload(0)

//Update navigation data. Called when commanded to deliver, return home, or a route update is needed...
/mob/living/simple_animal/bot/mulebot/proc/get_nav()
	if(!on || wires.is_cut(WIRE_BEACON))
		return

	for(var/obj/machinery/navbeacon/NB in deliverybeacons)
		if(NB.location == new_destination)	// if the beacon location matches the set destination
									// the we will navigate there
			destination = new_destination
			target = NB.loc
			var/direction = NB.dir	// this will be the load/unload dir
			if(direction)
				loaddir = text2num(direction)
			else
				loaddir = 0
			update_icon()
			if(destination) // No need to calculate a path if you do not have a destination set!
				calc_path()

/mob/living/simple_animal/bot/mulebot/emp_act(severity)
	if(cell)
		cell.emp_act(severity)
	if(load)
		load.emp_act(severity)
	..()

/mob/living/simple_animal/bot/mulebot/explode()
	visible_message("[src] blows apart!")
	var/turf/Tsec = get_turf(src)

	new /obj/item/device/assembly/prox_sensor(Tsec)
	new /obj/item/stack/rods(Tsec)
	new /obj/item/stack/rods(Tsec)
	new /obj/item/stack/cable_coil/cut(Tsec)
	if(cell)
		cell.loc = Tsec
		cell.update_icon()
		cell = null

	var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread
	s.set_up(3, 1, src)
	s.start()

	new /obj/effect/decal/cleanable/oil(loc)
	..()

/mob/living/simple_animal/bot/mulebot/remove_air(amount) //To prevent riders suffocating
	if(loc)
		return loc.remove_air(amount)
	else
		return null

/mob/living/simple_animal/bot/mulebot/resist()
	..()
	if(load)
		unload()

/mob/living/simple_animal/bot/mulebot/UnarmedAttack(atom/A)
	if(isturf(A) && isturf(loc) && loc.Adjacent(A) && load)
		unload(get_dir(loc, A))
	else
		..()

#undef SIGH
#undef ANNOYED
#undef DELIGHT

/obj/machinery/bot_core/mulebot
	req_access = list(access_cargo)


code:
/mob/living/simple_animal/bot/secbot
	name = "\improper Securitron"
	desc = "A little security robot.  He looks less than thrilled."
	icon = 'icons/obj/aibots.dmi'
	icon_state = "secbot0"
	density = 0
	anchored = 0
	health = 25
	maxHealth = 25
	damage_coeff = list(BRUTE = 0.5, BURN = 0.7, TOX = 0, CLONE = 0, STAMINA = 0, OXY = 0)
	pass_flags = PASSMOB

	radio_key = /obj/item/device/encryptionkey/secbot //AI Priv + Security
	radio_channel = "Security" //Security channel
	bot_type = SEC_BOT
	model = "Securitron"
	bot_core_type = /obj/machinery/bot_core/secbot
	window_id = "autosec"
	window_name = "Automatic Security Unit v1.6"
	allow_pai = 0

	var/mob/living/carbon/target
	var/oldtarget_name
	var/threatlevel = 0
	var/target_lastloc //Loc of target when arrested.
	var/last_found //There's a delay
	var/declare_arrests = 1 //When making an arrest, should it notify everyone on the security channel?
	var/idcheck = 0 //If true, arrest people with no IDs
	var/weaponscheck = 0 //If true, arrest people for weapons if they lack access
	var/check_records = 1 //Does it check security records?
	var/arrest_type = 0 //If true, don't handcuff

/mob/living/simple_animal/bot/secbot/beepsky
	name = "Officer Beep O'sky"
	desc = "It's Officer Beep O'sky! Powered by a potato and a shot of whiskey."
	idcheck = 0
	weaponscheck = 0
	auto_patrol = 1

/mob/living/simple_animal/bot/secbot/beepsky/explode()
	var/turf/Tsec = get_turf(src)
	new /obj/item/weapon/stock_parts/cell/potato(Tsec)
	var/obj/item/weapon/reagent_containers/food/drinks/drinkingglass/shotglass/S = new(Tsec)
	S.reagents.add_reagent("whiskey", 15)
	S.on_reagent_change()
	..()

/mob/living/simple_animal/bot/secbot/pingsky
	name = "Officer Pingsky"
	desc = "It's Officer Pingsky! Delegated to satellite guard duty for harbouring anti-human sentiment."
	radio_channel = "AI Private"

/mob/living/simple_animal/bot/secbot/New()
	..()
	icon_state = "secbot[on]"
	spawn(3)
		var/datum/job/detective/J = new/datum/job/detective
		access_card.access += J.get_access()
		prev_access = access_card.access

	//SECHUD
	var/datum/atom_hud/secsensor = huds[DATA_HUD_SECURITY_ADVANCED]
	secsensor.add_hud_to(src)

/mob/living/simple_animal/bot/secbot/turn_on()
	..()
	icon_state = "secbot[on]"

/mob/living/simple_animal/bot/secbot/turn_off()
	..()
	icon_state = "secbot[on]"

/mob/living/simple_animal/bot/secbot/bot_reset()
	..()
	target = null
	oldtarget_name = null
	anchored = 0
	walk_to(src,0)
	last_found = world.time

/mob/living/simple_animal/bot/secbot/set_custom_texts()

	text_hack = "You overload [name]'s target identification system."
	text_dehack = "You reboot [name] and restore the target identification."
	text_dehack_fail = "[name] refuses to accept your authority!"

/mob/living/simple_animal/bot/secbot/get_controls(mob/user)
	var/dat
	dat += hack(user)
	dat += showpai(user)
	dat += text({"
<TT>Securitron v1.6 controls</TT>

Status: []

Behaviour controls are [locked ? "locked" : "unlocked"]

Maintenance panel panel is [open ? "opened" : "closed"]"},

"[on ? "On" : "Off"]" )

	if(!locked || issilicon(user) || IsAdminGhost(user))
		dat += text({"

Arrest Unidentifiable Persons: []

Arrest for Unauthorized Weapons: []

Arrest for Warrant: []

Operating Mode: []

Report Arrests[]

Auto Patrol: []"},

"[idcheck ? "Yes" : "No"]",
"[weaponscheck ? "Yes" : "No"]",
"[check_records ? "Yes" : "No"]",
"[arrest_type ? "Detain" : "Arrest"]",
"[declare_arrests ? "Yes" : "No"]",
"[auto_patrol ? "On" : "Off"]" )

	return	dat

/mob/living/simple_animal/bot/secbot/Topic(href, href_list)
	if(..())
		return 1

	switch(href_list["operation"])
		if("idcheck")
			idcheck = !idcheck
			update_controls()
		if("weaponscheck")
			weaponscheck = !weaponscheck
			update_controls()
		if("ignorerec")
			check_records = !check_records
			update_controls()
		if("switchmode")
			arrest_type = !arrest_type
			update_controls()
		if("declarearrests")
			declare_arrests = !declare_arrests
			update_controls()

/mob/living/simple_animal/bot/secbot/proc/retaliate(mob/living/carbon/human/H)
	threatlevel = H.assess_threat(src)
	threatlevel += 6
	if(threatlevel >= 4)
		target = H
		mode = BOT_HUNT

/mob/living/simple_animal/bot/secbot/attack_hand(mob/living/carbon/human/H)
	if(H.a_intent == "harm")
		retaliate(H)
	return ..()

/mob/living/simple_animal/bot/secbot/attackby(obj/item/weapon/W, mob/user, params)
	..()
	if(istype(W, /obj/item/weapon/weldingtool) && user.a_intent != "harm") // Any intent but harm will heal, so we shouldn't get angry.
		return
	if(!istype(W, /obj/item/weapon/screwdriver) && (W.force) && (!target) && (W.damtype != STAMINA) ) // Added check for welding tool to fix #2432. Welding tool behavior is handled in superclass.
		retaliate(user)

/mob/living/simple_animal/bot/secbot/emag_act(mob/user)
	..()
	if(emagged == 2)
		if(user)
			user << "You short out [src]'s target assessment circuits."
			oldtarget_name = user.name
		audible_message("[src] buzzes oddly!")
		declare_arrests = 0
		icon_state = "secbot[on]"

/mob/living/simple_animal/bot/secbot/bullet_act(obj/item/projectile/Proj)
	if(istype(Proj ,/obj/item/projectile/beam)||istype(Proj,/obj/item/projectile/bullet))
		if((Proj.damage_type == BURN) || (Proj.damage_type == BRUTE))
			if(!Proj.nodamage && Proj.damage < src.health)
				retaliate(Proj.firer)
	..()

/mob/living/simple_animal/bot/secbot/UnarmedAttack(atom/A)
	if(!on)
		return
	if(iscarbon(A))
		var/mob/living/carbon/C = A
		if(!C.stunned || arrest_type)
			stun_attack(A)
		else if(C.canBeHandcuffed() && !C.handcuffed)
			cuff(A)
	else
		..()

/mob/living/simple_animal/bot/secbot/hitby(atom/movable/AM, skipcatch = 0, hitpush = 1, blocked = 0)
	if(istype(AM, /obj/item))
		var/obj/item/I = AM
		if(I.throwforce < src.health && I.thrownby && (istype(I.thrownby, /mob/living/carbon/human)))
			var/mob/living/carbon/human/H = I.thrownby
			retaliate(H)
	..()

/mob/living/simple_animal/bot/secbot/proc/cuff(mob/living/carbon/C)
	mode = BOT_ARREST
	playsound(loc, 'sound/weapons/cablecuff.ogg', 30, 1, -2)
	C.visible_message("[src] is trying to put zipties on [C]!",\
						"[src] is trying to put zipties on you!")
	spawn(60)
		if( !Adjacent(C) || !isturf(C.loc) ) //if he's in a closet or not adjacent, we cancel cuffing.
			return
		if(!C.handcuffed)
			C.handcuffed = new /obj/item/weapon/restraints/handcuffs/cable/zipties/used(C)
			C.update_handcuffed()
			playsound(loc, pick('sound/voice/bgod.ogg', 'sound/voice/biamthelaw.ogg', 'sound/voice/bsecureday.ogg', 'sound/voice/bradio.ogg', 'sound/voice/binsult.ogg', 'sound/voice/bcreep.ogg'), 50, 0)
			back_to_idle()

/mob/living/simple_animal/bot/secbot/proc/stun_attack(mob/living/carbon/C)
	playsound(loc, 'sound/weapons/Egloves.ogg', 50, 1, -1)
	icon_state = "secbot-c"
	spawn(2)
		icon_state = "secbot[on]"
	var/threat = 5
	if(istype(C, /mob/living/carbon/human))
		C.stuttering = 5
		C.Stun(5)
		C.Weaken(5)
		var/mob/living/carbon/human/H = C
		threat = H.assess_threat(src)
	else
		C.Weaken(5)
		C.stuttering = 5
		C.Stun(5)
	add_logs(src,C,"stunned")
	if(declare_arrests)
		var/area/location = get_area(src)
		speak("[arrest_type ? "Detaining" : "Arresting"] level [threat] scumbag [C] in [location].", radio_channel)
	C.visible_message("[src] has stunned [C]!",\
							"[src] has stunned you!")

/mob/living/simple_animal/bot/secbot/handle_automated_action()
	if(!..())
		return

	switch(mode)

		if(BOT_IDLE)		// idle

			walk_to(src,0)
			look_for_perp()	// see if any criminals are in range
			if(!mode && auto_patrol)	// still idle, and set to patrol
				mode = BOT_START_PATROL	// switch to patrol mode

		if(BOT_HUNT)		// hunting for perp

			// if can't reach perp for long enough, go idle
			if(frustration >= 8)
				walk_to(src,0)
				back_to_idle()
				return

			if(target)		// make sure target exists
				if(Adjacent(target) && isturf(target.loc))	// if right next to perp
					stun_attack(target)

					mode = BOT_PREP_ARREST
					anchored = 1
					target_lastloc = target.loc
					return

				else								// not next to perp
					var/turf/olddist = get_dist(src, target)
					walk_to(src, target,1,4)
					if((get_dist(src, target)) >= (olddist))
						frustration++
					else
						frustration = 0
			else
				back_to_idle()

		if(BOT_PREP_ARREST)		// preparing to arrest target

			// see if he got away. If he's no no longer adjacent or inside a closet or about to get up, we hunt again.
			if( !Adjacent(target) || !isturf(target.loc) ||  target.weakened < 2 )
				back_to_hunt()
				return

			if(iscarbon(target) && target.canBeHandcuffed())
				if(!arrest_type)
					if(!target.handcuffed)  //he's not cuffed? Try to cuff him!
						cuff(target)
					else
						back_to_idle()
						return
			else
				back_to_idle()
				return

		if(BOT_ARREST)
			if(!target)
				anchored = 0
				mode = BOT_IDLE
				last_found = world.time
				frustration = 0
				return

			if(target.handcuffed) //no target or target cuffed? back to idle.
				back_to_idle()
				return

			if(!Adjacent(target) || !isturf(target.loc) || (target.loc != target_lastloc && target.weakened < 2)) //if he's changed loc and about to get up or not adjacent or got into a closet, we prep arrest again.
				back_to_hunt()
				return
			else //Try arresting again if the target escapes.
				mode = BOT_PREP_ARREST
				anchored = 0

		if(BOT_START_PATROL)
			look_for_perp()
			start_patrol()

		if(BOT_PATROL)
			look_for_perp()
			bot_patrol()

	return

/mob/living/simple_animal/bot/secbot/proc/back_to_idle()
	anchored = 0
	mode = BOT_IDLE
	target = null
	last_found = world.time
	frustration = 0
	spawn(0)
		handle_automated_action() //ensure bot quickly responds

/mob/living/simple_animal/bot/secbot/proc/back_to_hunt()
	anchored = 0
	frustration = 0
	mode = BOT_HUNT
	spawn(0)
		handle_automated_action() //ensure bot quickly responds
// look for a criminal in view of the bot

/mob/living/simple_animal/bot/secbot/proc/look_for_perp()
	anchored = 0
	for (var/mob/living/carbon/C in view(7,src)) //Let's find us a criminal
		if((C.stat) || (C.handcuffed))
			continue

		if((C.name == oldtarget_name) && (world.time < last_found + 100))
			continue

		threatlevel = C.assess_threat(src)

		if(!threatlevel)
			continue

		else if(threatlevel >= 4)
			target = C
			oldtarget_name = C.name
			speak("Level [threatlevel] infraction alert!")
			playsound(loc, pick('sound/voice/bcriminal.ogg', 'sound/voice/bjustice.ogg', 'sound/voice/bfreeze.ogg'), 50, 0)
			visible_message("[src] points at [C.name]!")
			mode = BOT_HUNT
			spawn(0)
				handle_automated_action()	// ensure bot quickly responds to a perp
			break
		else
			continue
/mob/living/simple_animal/bot/secbot/proc/check_for_weapons(var/obj/item/slot_item)
	if(slot_item && slot_item.needs_permit)
		return 1
	return 0

/mob/living/simple_animal/bot/secbot/explode()

	walk_to(src,0)
	visible_message("[src] blows apart!")
	var/turf/Tsec = get_turf(src)

	var/obj/item/weapon/secbot_assembly/Sa = new /obj/item/weapon/secbot_assembly(Tsec)
	Sa.build_step = 1
	Sa.overlays += "hs_hole"
	Sa.created_name = name
	new /obj/item/device/assembly/prox_sensor(Tsec)
	new /obj/item/weapon/melee/baton(Tsec)

	if(prob(50))
		new /obj/item/robot_parts/l_arm(Tsec)

	var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread
	s.set_up(3, 1, src)
	s.start()

	new /obj/effect/decal/cleanable/oil(loc)
	..()

/mob/living/simple_animal/bot/secbot/attack_alien(var/mob/living/carbon/alien/user as mob)
	..()
	if(!isalien(target))
		target = user
		mode = BOT_HUNT

/mob/living/simple_animal/bot/secbot/Crossed(atom/movable/AM)
	if(ismob(AM) && target)
		var/mob/living/carbon/C = AM
		if(!istype(C) || !C || in_range(src, target))
			return
		C.visible_message("[pick( \
						  "[C] dives out of [src]'s way!", \
						  "[C] stumbles over [src]!", \
						  "[C] jumps out of [src]'s path!", \
						  "[C] trips over [src] and falls!", \
						  "[C] topples over [src]!", \
						  "[C] leaps out of [src]'s way!")]")
		C.Weaken(2)
		return
	..()

/obj/machinery/bot_core/secbot
	req_access = list(access_security)

license: mit


code:
/mob/living/simple_animal/hostile/construct
	name = "Construct"
	real_name = "Construct"
	desc = ""
	speak_emote = list("hisses")
	response_help  = "thinks better of touching"
	response_disarm = "flails at"
	response_harm   = "punches"
	speak_chance = 1
	icon = 'icons/mob/mob.dmi'
	speed = 0
	a_intent = "harm"
	stop_automated_movement = 1
	status_flags = CANPUSH
	attack_sound = 'sound/weapons/punch1.ogg'
	see_in_dark = 7
	damage_coeff = list(BRUTE = 1, BURN = 1, TOX = 0, CLONE = 0, STAMINA = 0, OXY = 0)
	atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
	minbodytemp = 0
	maxbodytemp = INFINITY
	healable = 0
	faction = list("cult")
	flying = 1
	pressure_resistance = 200
	unique_name = 1
	AIStatus = AI_OFF //normal constructs don't have AI
	var/list/construct_spells = list()
	var/playstyle_string = "You are a generic construct! Your job is to not exist, and you should probably adminhelp this."

/mob/living/simple_animal/hostile/construct/New()
	..()
	for(var/spell in construct_spells)
		AddSpell(new spell(null))

/mob/living/simple_animal/hostile/construct/death()
	..(1)
	new /obj/item/weapon/ectoplasm (src.loc)
	visible_message("[src] collapses in a shattered heap.")
	ghostize()
	qdel(src)
	return

/mob/living/simple_animal/hostile/construct/examine(mob/user)
	var/msg = "*---------*\nThis is \icon[src] \a [src]!\n"
	if (src.health < src.maxHealth)
		msg += ""
		if (src.health >= src.maxHealth/2)
			msg += "It looks slightly dented.\n"
		else
			msg += "It looks severely dented!\n"
		msg += ""
	msg += "*---------*"

	user << msg

/mob/living/simple_animal/hostile/construct/attack_animal(mob/living/simple_animal/M)
	if(istype(M, /mob/living/simple_animal/hostile/construct/builder))
		if(health < maxHealth)
			adjustHealth(-5)
			if(src != M)
				Beam(M,icon_state="sendbeam",icon='icons/effects/effects.dmi',time=4)
				M.visible_message("[M] repairs some of \the [src]'s dents.", \
						   "You repair some of [src]'s dents, leaving [src] at [health]/[maxHealth] health.")
			else
				M.visible_message("[M] repairs some of its own dents.", \
						   "You repair some of your own dents, leaving you at [M.health]/[M.maxHealth] health.")
		else
			if(src != M)
				M << "You cannot repair [src]'s dents, as it has none!"
			else
				M << "You cannot repair your own dents, as you have none!"
	else if(src != M)
		..()

/mob/living/simple_animal/hostile/construct/Process_Spacemove(movement_dir = 0)
	return 1

/mob/living/simple_animal/hostile/construct/narsie_act()
	return

/////////////////Juggernaut///////////////
/mob/living/simple_animal/hostile/construct/armored
	name = "Juggernaut"
	real_name = "Juggernaut"
	desc = "A massive, armored construct built to spearhead attacks and soak up enemy fire."
	icon_state = "behemoth"
	icon_living = "behemoth"
	maxHealth = 250
	health = 250
	response_harm = "harmlessly punches"
	harm_intent_damage = 0
	melee_damage_lower = 30
	melee_damage_upper = 30
	attacktext = "smashes their armored gauntlet into"
	speed = 3
	environment_smash = 2
	attack_sound = 'sound/weapons/punch3.ogg'
	status_flags = 0
	mob_size = MOB_SIZE_LARGE
	force_threshold = 11
	construct_spells = list(/obj/effect/proc_holder/spell/aoe_turf/conjure/lesserforcewall)
	playstyle_string = "You are a Juggernaut. Though slow, your shell can withstand extreme punishment, \
						create shield walls, rip apart enemies and walls alike, and even deflect energy weapons."

/mob/living/simple_animal/hostile/construct/armored/hostile //actually hostile, will move around, hit things
	AIStatus = AI_ON
	environment_smash = 1 //only token destruction, don't smash the cult wall NO STOP

/mob/living/simple_animal/hostile/construct/armored/bullet_act(obj/item/projectile/P)
	if(istype(P, /obj/item/projectile/energy) || istype(P, /obj/item/projectile/beam))
		var/reflectchance = 80 - round(P.damage/3)
		if(prob(reflectchance))
			apply_damage(P.damage * 0.5, P.damage_type)
			visible_message("The [P.name] is reflected by [src]'s armored shell!", \
							"The [P.name] is reflected by your armored shell!")

			// Find a turf near or on the original location to bounce to
			if(P.starting)
				var/new_x = P.starting.x + pick(0, 0, -1, 1, -2, 2, -2, 2, -2, 2, -3, 3, -3, 3)
				var/new_y = P.starting.y + pick(0, 0, -1, 1, -2, 2, -2, 2, -2, 2, -3, 3, -3, 3)
				var/turf/curloc = get_turf(src)

				// redirect the projectile
				P.original = locate(new_x, new_y, P.z)
				P.starting = curloc
				P.current = curloc
				P.firer = src
				P.yo = new_y - curloc.y
				P.xo = new_x - curloc.x

			return -1 // complete projectile permutation

	return (..(P))

////////////////////////Wraith/////////////////////////////////////////////
/mob/living/simple_animal/hostile/construct/wraith
	name = "Wraith"
	real_name = "Wraith"
	desc = "A wicked, clawed shell constructed to assassinate enemies and sow chaos behind enemy lines."
	icon_state = "floating"
	icon_living = "floating"
	maxHealth = 75
	health = 75
	melee_damage_lower = 25
	melee_damage_upper = 25
	retreat_distance = 2 //AI wraiths will move in and out of combat
	attacktext = "slashes"
	attack_sound = 'sound/weapons/bladeslice.ogg'
	construct_spells = list(/obj/effect/proc_holder/spell/targeted/ethereal_jaunt/shift)
	playstyle_string = "You are a Wraith. Though relatively fragile, you are fast, deadly, and even able to phase through walls."

/mob/living/simple_animal/hostile/construct/wraith/hostile //actually hostile, will move around, hit things
	AIStatus = AI_ON

/////////////////////////////Artificer/////////////////////////
/mob/living/simple_animal/hostile/construct/builder
	name = "Artificer"
	real_name = "Artificer"
	desc = "A bulbous construct dedicated to building and maintaining the Cult of Nar-Sie's armies."
	icon_state = "artificer"
	icon_living = "artificer"
	maxHealth = 50
	health = 50
	response_harm = "viciously beats"
	harm_intent_damage = 5
	melee_damage_lower = 5
	melee_damage_upper = 5
	retreat_distance = 10
	minimum_distance = 10 //AI artificers will flee like fuck
	attacktext = "rams"
	environment_smash = 2
	attack_sound = 'sound/weapons/punch2.ogg'
	construct_spells = list(/obj/effect/proc_holder/spell/aoe_turf/conjure/wall,
							/obj/effect/proc_holder/spell/aoe_turf/conjure/floor,
							/obj/effect/proc_holder/spell/aoe_turf/conjure/soulstone,
							/obj/effect/proc_holder/spell/aoe_turf/conjure/construct/lesser,
							/obj/effect/proc_holder/spell/targeted/projectile/magic_missile/lesser)
	playstyle_string = "You are an Artificer. You are incredibly weak and fragile, but you are able to construct fortifications, \
						use magic missile, repair allied constructs (by clicking on them), \
						and, most important of all, create new constructs by producing soulstones to capture souls, \
						and shells to place those soulstones into."

/mob/living/simple_animal/hostile/construct/builder/Found(atom/A) //what have we found here?
	if(istype(A, /mob/living/simple_animal/hostile/construct)) //is it a construct?
		var/mob/living/simple_animal/hostile/construct/C = A
		if(C.health < C.maxHealth) //is it hurt? let's go heal it if it is
			return 1
		else
			return 0
	else
		return 0

/mob/living/simple_animal/hostile/construct/builder/CanAttack(atom/the_target)
	if(see_invisible < the_target.invisibility)//Target's invisible to us, forget it
		return 0
	if(Found(the_target) || ..()) //If we Found it or Can_Attack it normally, we Can_Attack it as long as it wasn't invisible
		return 1 //as a note this shouldn't be added to base hostile mobs because it'll mess up retaliate hostile mobs

/mob/living/simple_animal/hostile/construct/builder/MoveToTarget(var/list/possible_targets)
	..()
	if(isliving(target))
		var/mob/living/L = target
		if(istype(L, /mob/living/simple_animal/hostile/construct) && L.health >= L.maxHealth) //is this target an unhurt construct? stop trying to heal it
			LoseTarget()
			return 0
		if(L.health <= melee_damage_lower+melee_damage_upper) //ey bucko you're hurt as fuck let's go hit you
			retreat_distance = null
			minimum_distance = 1

/mob/living/simple_animal/hostile/construct/builder/Aggro()
	..()
	if(istype(target, /mob/living/simple_animal/hostile/construct)) //oh the target is a construct no need to flee
		retreat_distance = null
		minimum_distance = 1

/mob/living/simple_animal/hostile/construct/builder/LoseAggro()
	..()
	retreat_distance = initial(retreat_distance)
	minimum_distance = initial(minimum_distance)

/mob/living/simple_animal/hostile/construct/builder/hostile //actually hostile, will move around, hit things, heal other constructs
	AIStatus = AI_ON
	environment_smash = 1 //only token destruction, don't smash the cult wall NO STOP

/////////////////////////////Non-cult Artificer/////////////////////////
/mob/living/simple_animal/hostile/construct/builder/noncult
	construct_spells = list(/obj/effect/proc_holder/spell/aoe_turf/conjure/wall,
							/obj/effect/proc_holder/spell/aoe_turf/conjure/floor,
							/obj/effect/proc_holder/spell/aoe_turf/conjure/soulstone/noncult,
							/obj/effect/proc_holder/spell/aoe_turf/conjure/construct/lesser,
							/obj/effect/proc_holder/spell/targeted/projectile/magic_missile/lesser)

/////////////////////////////Harvester/////////////////////////
/mob/living/simple_animal/hostile/construct/harvester
	name = "Harvester"
	real_name = "Harvester"
	desc = "A long, thin construct built to herald Nar-Sie's rise. It'll be all over soon."
	icon_state = "harvester"
	icon_living = "harvester"
	maxHealth = 60
	health = 60
	melee_damage_lower = 1
	melee_damage_upper = 5
	retreat_distance = 2 //AI harvesters will move in and out of combat, like wraiths, but shittier
	attacktext = "prods"
	environment_smash = 3
	attack_sound = 'sound/weapons/tap.ogg'
	construct_spells = list(/obj/effect/proc_holder/spell/aoe_turf/conjure/wall,
							/obj/effect/proc_holder/spell/aoe_turf/conjure/floor,
							/obj/effect/proc_holder/spell/targeted/smoke/disable)
	playstyle_string = "You are a Harvester. You are not strong, but your powers of domination will assist you in your role: \
						Bring those who still cling to this world of illusion back to the Geometer so they may know Truth."

/mob/living/simple_animal/hostile/construct/harvester/hostile //actually hostile, will move around, hit things
	AIStatus = AI_ON
	environment_smash = 1 //only token destruction, don't smash the cult wall NO STOP


code:
//Meant for simple animals to drop lootable human bodies.

//If someone can do this in a neater way, be my guest-Kor

//This has to be seperate from the Away Mission corpses, because New() doesn't work for those, and initialize() doesn't work for these.

//To do: Allow corpses to appear mangled, bloody, etc. Allow customizing the bodies appearance (they're all bald and white right now).

//List of different corpse types

/obj/effect/mob_spawn/human/corpse/syndicatesoldier
	name = "Syndicate Operative"
	uniform = /obj/item/clothing/under/syndicate
	suit = /obj/item/clothing/suit/armor/vest
	shoes = /obj/item/clothing/shoes/combat
	gloves = /obj/item/clothing/gloves/combat
	radio = /obj/item/device/radio/headset
	mask = /obj/item/clothing/mask/gas
	helmet = /obj/item/clothing/head/helmet/swat
	back = /obj/item/weapon/storage/backpack
	has_id = 1
	id_job = "Operative"
	id_access = "Syndicate"

/obj/effect/mob_spawn/human/corpse/syndicatecommando
	name = "Syndicate Commando"
	uniform = /obj/item/clothing/under/syndicate
	suit = /obj/item/clothing/suit/space/hardsuit/syndi
	shoes = /obj/item/clothing/shoes/combat
	gloves = /obj/item/clothing/gloves/combat
	radio = /obj/item/device/radio/headset
	mask = /obj/item/clothing/mask/gas/syndicate
	helmet = /obj/item/clothing/head/helmet/space/hardsuit/syndi
	back = /obj/item/weapon/tank/jetpack/oxygen
	pocket1 = /obj/item/weapon/tank/internals/emergency_oxygen
	has_id = 1
	id_job = "Operative"
	id_access = "Syndicate"

/obj/effect/mob_spawn/human/corpse/syndicatestormtrooper
	name = "Syndicate Stormtrooper"
	uniform = /obj/item/clothing/under/syndicate
	suit = /obj/item/clothing/suit/space/hardsuit/syndi/elite
	shoes = /obj/item/clothing/shoes/combat
	gloves = /obj/item/clothing/gloves/combat
	radio = /obj/item/device/radio/headset
	mask = /obj/item/clothing/mask/gas/syndicate
	helmet = /obj/item/clothing/head/helmet/space/hardsuit/syndi/elite
	back = /obj/item/weapon/tank/jetpack/oxygen/harness
	has_id = 1
	id_job = "Operative"
	id_access = "Syndicate"

/obj/effect/mob_spawn/human/corpse/clown
	name = "Clown"
	uniform = /obj/item/clothing/under/rank/clown
	shoes = /obj/item/clothing/shoes/clown_shoes
	radio = /obj/item/device/radio/headset
	mask = /obj/item/clothing/mask/gas/clown_hat
	pocket1 = /obj/item/weapon/bikehorn
	back = /obj/item/weapon/storage/backpack/clown
	has_id = 1
	id_job = "Clown"
	id_access = "Clown"

/obj/effect/mob_spawn/human/corpse/pirate
	name = "Pirate"
	uniform = /obj/item/clothing/under/pirate
	shoes = /obj/item/clothing/shoes/jackboots
	glasses = /obj/item/clothing/glasses/eyepatch
	helmet = /obj/item/clothing/head/bandana

/obj/effect/mob_spawn/human/corpse/pirate/ranged
	name = "Pirate Gunner"
	suit = /obj/item/clothing/suit/pirate
	helmet = /obj/item/clothing/head/pirate

/obj/effect/mob_spawn/human/corpse/russian
	name = "Russian"
	uniform = /obj/item/clothing/under/soviet
	shoes = /obj/item/clothing/shoes/jackboots
	helmet = /obj/item/clothing/head/bearpelt

/obj/effect/mob_spawn/human/corpse/russian/ranged
	helmet = /obj/item/clothing/head/ushanka

/obj/effect/mob_spawn/human/corpse/russian/ranged/trooper
	uniform = /obj/item/clothing/under/syndicate/camo
	suit = /obj/item/clothing/suit/armor/bulletproof
	shoes = /obj/item/clothing/shoes/combat
	gloves = /obj/item/clothing/gloves/combat
	radio = /obj/item/device/radio/headset
	mask = /obj/item/clothing/mask/balaclava
	helmet = /obj/item/clothing/head/helmet/alt

/obj/effect/mob_spawn/human/corpse/russian/ranged/officer
	name = "Russian Officer"
	uniform = /obj/item/clothing/under/rank/security/navyblue/russian
	suit = /obj/item/clothing/suit/security/officer/russian
	shoes = /obj/item/clothing/shoes/laceup
	radio = /obj/item/device/radio/headset
	helmet = /obj/item/clothing/head/ushanka

/obj/effect/mob_spawn/human/corpse/wizard
	name = "Space Wizard"
	uniform = /obj/item/clothing/under/color/lightpurple
	suit = /obj/item/clothing/suit/wizrobe
	shoes = /obj/item/clothing/shoes/sandal
	helmet = /obj/item/clothing/head/wizard

/obj/effect/mob_spawn/human/corpse/nanotrasensoldier
	name = "Nanotrasen Private Security Officer"
	uniform = /obj/item/clothing/under/rank/security
	suit = /obj/item/clothing/suit/armor/vest
	shoes = /obj/item/clothing/shoes/combat
	gloves = /obj/item/clothing/gloves/combat
	radio = /obj/item/device/radio/headset
	mask = /obj/item/clothing/mask/gas/sechailer/swat
	helmet = /obj/item/clothing/head/helmet/swat/nanotrasen
	back = /obj/item/weapon/storage/backpack/security
	has_id = 1
	id_job = "Private Security Force"
	id_access = "Security Officer"


code:
/mob/living/simple_animal/butterfly
	name = "butterfly"
	desc = "A colorful butterfly, how'd it get up here?"
	icon_state = "butterfly"
	icon_living = "butterfly"
	icon_dead = "butterfly_dead"
	turns_per_move = 1
	response_help = "shoos"
	response_disarm = "brushes aside"
	response_harm = "squashes"
	maxHealth = 2
	health = 2
	harm_intent_damage = 1
	friendly = "nudges"
	density = 0
	pass_flags = PASSTABLE | PASSGRILLE | PASSMOB
	ventcrawler = 2
	mob_size = MOB_SIZE_TINY
	gold_core_spawnable = 2

/mob/living/simple_animal/butterfly/New()
	..()
	color = rgb(rand(0, 255), rand(0, 255), rand(0, 255))


code:
//Cat
/mob/living/simple_animal/pet/cat
	name = "cat"
	desc = "Kitty!!"
	icon = 'icons/mob/pets.dmi'
	icon_state = "cat2"
	icon_living = "cat2"
	icon_dead = "cat2_dead"
	gender = MALE
	speak = list("Meow!", "Esp!", "Purr!", "HSSSSS")
	speak_emote = list("purrs", "meows")
	emote_hear = list("meows", "mews")
	emote_see = list("shakes its head", "shivers")
	speak_chance = 1
	turns_per_move = 5
	see_in_dark = 6
	ventcrawler = 2
	pass_flags = PASSTABLE
	mob_size = MOB_SIZE_SMALL
	minbodytemp = 200
	maxbodytemp = 400
	unsuitable_atmos_damage = 1
	species = /mob/living/simple_animal/pet/cat
	childtype = list(/mob/living/simple_animal/pet/cat/kitten)
	butcher_results = list(/obj/item/weapon/reagent_containers/food/snacks/meat/slab = 2)
	response_help  = "pets"
	response_disarm = "gently pushes aside"
	response_harm   = "kicks"
	var/turns_since_scan = 0
	var/mob/living/simple_animal/mouse/movement_target
	gold_core_spawnable = 2

/mob/living/simple_animal/pet/cat/New()
	..()
	verbs += /mob/living/proc/lay_down

/mob/living/simple_animal/pet/cat/update_canmove()
	..()
	if(client)
		if (resting)
			icon_state = "[icon_living]_rest"
		else
			icon_state = "[icon_living]"

/mob/living/simple_animal/pet/cat/space
	name = "space cat"
	desc = "It's a cat... in space!"
	icon_state = "spacecat"
	icon_living = "spacecat"
	icon_dead = "spacecat_dead"
	unsuitable_atmos_damage = 0
	minbodytemp = TCMB
	maxbodytemp = T0C + 40

/mob/living/simple_animal/pet/cat/original
	name = "Batsy"
	desc = "The product of alien DNA and bored geneticists."
	icon_state = "original"
	icon_living = "original"
	icon_dead = "original_dead"

/mob/living/simple_animal/pet/cat/kitten
	name = "kitten"
	desc = "D'aaawwww."
	icon_state = "kitten"
	icon_living = "kitten"
	icon_dead = "kitten_dead"
	gender = NEUTER
	density = 0
	pass_flags = PASSMOB
	mob_size = MOB_SIZE_SMALL

//RUNTIME IS ALIVE! SQUEEEEEEEE~
/mob/living/simple_animal/pet/cat/Runtime
	name = "Runtime"
	desc = "GCAT"
	icon_state = "cat"
	icon_living = "cat"
	icon_dead = "cat_dead"
	gender = FEMALE
	gold_core_spawnable = 0
	var/list/family = list()
	var/cats_deployed = 0
	var/memory_saved = 0

/mob/living/simple_animal/pet/cat/Runtime/New()
	if(prob(5))
		icon_state = "original"
		icon_living = "original"
		icon_dead = "original_dead"
	Read_Memory()
	..()

/mob/living/simple_animal/pet/cat/Runtime/Life()
	if(!cats_deployed && ticker.current_state >= GAME_STATE_SETTING_UP)
		Deploy_The_Cats()
	if(!stat && ticker.current_state == GAME_STATE_FINISHED && !memory_saved)
		Write_Memory()
	..()

/mob/living/simple_animal/pet/cat/Runtime/death()
	if(!memory_saved)
		Write_Memory(1)
	..()

/mob/living/simple_animal/pet/cat/Runtime/proc/Read_Memory()
	var/savefile/S = new /savefile("data/npc_saves/Runtime.sav")
	S["family"] 			>> family

	if(isnull(family))
		family = list()

/mob/living/simple_animal/pet/cat/Runtime/proc/Write_Memory(dead)
	var/savefile/S = new /savefile("data/npc_saves/Runtime.sav")
	family = list()
	for(var/mob/living/simple_animal/pet/cat/C in mob_list)
		if(istype(C,type) || C.stat || !C.butcher_results) //That last one is a work around for hologram cats
			continue
		if(C.type in family)
			family[C.type] += 1
		else
			family[C.type] = 1
	S["family"]				<< family
	memory_saved = 1

/mob/living/simple_animal/pet/cat/Runtime/proc/Deploy_The_Cats()
	for(var/cat_type in family)
		if(family[cat_type] > 0)
			for(var/i in 1 to min(family[cat_type],100)) //Limits to about 500 cats, you wouldn't think this would be needed (BUT IT IS)
				new cat_type(loc)
	cats_deployed = 1

/mob/living/simple_animal/pet/cat/Proc
	name = "Proc"
	gold_core_spawnable = 0

/mob/living/simple_animal/pet/cat/Life()
	if(!stat && !buckled && !client)
		if(prob(1))
			emote("me", 1, pick("stretches out for a belly rub.", "wags its tail.", "lies down."))
			icon_state = "[icon_living]_rest"
			resting = 1
			update_canmove()
		else if (prob(1))
			emote("me", 1, pick("sits down.", "crouches on its hind legs.", "looks alert."))
			icon_state = "[icon_living]_sit"
			resting = 1
			update_canmove()
		else if (prob(1))
			if (resting)
				emote("me", 1, pick("gets up and meows.", "walks around.", "stops resting."))
				icon_state = "[icon_living]"
				resting = 0
				update_canmove()
			else
				emote("me", 1, pick("grooms its fur.", "twitches its whiskers.", "shakes out its coat."))

	//MICE!
	if((src.loc) && isturf(src.loc))
		if(!stat && !resting && !buckled)
			for(var/mob/living/simple_animal/mouse/M in view(1,src))
				if(!M.stat && Adjacent(M))
					emote("me", 1, "splats \the [M]!")
					M.splat()
					movement_target = null
					stop_automated_movement = 0
					break
			for(var/obj/item/toy/cattoy/T in view(1,src))
				if (T.cooldown < (world.time - 400))
					emote("me", 1, "bats \the [T] around with its paw!")
					T.cooldown = world.time

	..()

	make_babies()

	if(!stat && !resting && !buckled)
		turns_since_scan++
		if(turns_since_scan > 5)
			walk_to(src,0)
			turns_since_scan = 0
			if((movement_target) && !(isturf(movement_target.loc) || ishuman(movement_target.loc) ))
				movement_target = null
				stop_automated_movement = 0
			if( !movement_target || !(movement_target.loc in oview(src, 3)) )
				movement_target = null
				stop_automated_movement = 0
				for(var/mob/living/simple_animal/mouse/snack in oview(src,3))
					if(isturf(snack.loc) && !snack.stat)
						movement_target = snack
						break
			if(movement_target)
				stop_automated_movement = 1
				walk_to(src,movement_target,0,3)

/mob/living/simple_animal/pet/cat/attack_hand(mob/living/carbon/human/M)
	. = ..()
	switch(M.a_intent)
		if("help")
			wuv(1, M)
		if("harm")
			wuv(-1, M)

/mob/living/simple_animal/pet/cat/proc/wuv(change, mob/M)
	if(change)
		if(change > 0)
			if(M && stat != DEAD)
				flick_overlay(image('icons/mob/animal.dmi', src, "heart-ani2", MOB_LAYER+1), list(M.client), 20)
				emote("me", 1, "purrs!")
		else
			if(M && stat != DEAD)
				emote("me", 1, "hisses!")


code:
/mob/living/simple_animal/cockroach
	name = "cockroach"
	desc = "This station is just crawling with bugs."
	icon_state = "cockroach"
	icon_dead = "cockroach"
	health = 1
	maxHealth = 1
	turns_per_move = 5
	atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
	minbodytemp = 270
	maxbodytemp = INFINITY
	pass_flags = PASSTABLE | PASSGRILLE | PASSMOB
	mob_size = MOB_SIZE_TINY
	response_help  = "pokes"
	response_disarm = "shoos"
	response_harm   = "splats"
	density = 0
	ventcrawler = 2
	gold_core_spawnable = 2
	var/squish_chance = 50
	loot = list(/obj/effect/decal/cleanable/deadcockroach)
	del_on_death = 1

/mob/living/simple_animal/cockroach/death(gibbed)
	if(ticker.cinematic) //If the nuke is going off, then cockroaches are invincible. Keeps the nuke from killing them, cause cockroaches are immune to nukes.
		return
	..()

/mob/living/simple_animal/cockroach/Crossed(var/atom/movable/AM)
	if(ismob(AM))
		if(isliving(AM))
			var/mob/living/A = AM
			if(A.mob_size > MOB_SIZE_TINY)
				if(prob(squish_chance))
					A.visible_message("\The [A] squashed \the [name].", "You squashed \the [name].")
					death()
				else
					visible_message("\The [name] avoids getting crushed.")
	else
		if(isobj(AM))
			if(istype(AM,/obj/structure))
				visible_message("As \the [AM] moved over \the [name], it was crushed.")
				death()

/mob/living/simple_animal/cockroach/ex_act() //Explosions are a terrible way to handle a cockroach.
	return

/obj/effect/decal/cleanable/deadcockroach
	name = "cockroach guts"
	desc = "One bug squashed. Four more will rise in its place."
	icon = 'icons/effects/blood.dmi'
	icon_state = "xfloor1"
	random_icon_states = list("xfloor1", "xfloor2", "xfloor3", "xfloor4", "xfloor5", "xfloor6", "xfloor7")
	layer = 2


code:
//Look Sir, free crabs!
/mob/living/simple_animal/crab
	name = "crab"
	desc = "Free crabs!"
	icon_state = "crab"
	icon_living = "crab"
	icon_dead = "crab_dead"
	speak_emote = list("clicks")
	emote_hear = list("clicks.")
	emote_see = list("clacks.")
	speak_chance = 1
	turns_per_move = 5
	butcher_results = list(/obj/item/weapon/reagent_containers/food/snacks/meat/slab = 1)
	response_help  = "pets"
	response_disarm = "gently pushes aside"
	response_harm   = "stomps"
	stop_automated_movement = 1
	friendly = "pinches"
	ventcrawler = 2
	var/obj/item/inventory_head
	var/obj/item/inventory_mask
	gold_core_spawnable = 2

/mob/living/simple_animal/crab/Life()
	..()
	//CRAB movement
	if(!ckey && !stat)
		if(isturf(src.loc) && !resting && !buckled)		//This is so it only moves if it's not inside a closet, gentics machine, etc.
			turns_since_move++
			if(turns_since_move >= turns_per_move)
				var/east_vs_west = pick(4,8)
				if(Process_Spacemove(east_vs_west))
					Move(get_step(src,east_vs_west), east_vs_west)
					turns_since_move = 0
	regenerate_icons()

//COFFEE! SQUEEEEEEEEE!
/mob/living/simple_animal/crab/Coffee
	name = "Coffee"
	real_name = "Coffee"
	desc = "It's Coffee, the other pet!"
	response_help  = "pets"
	response_disarm = "gently pushes aside"
	response_harm   = "stomps"
	gold_core_spawnable = 0


code:
//Dogs.

/mob/living/simple_animal/pet/dog
	response_help  = "pets"
	response_disarm = "bops"
	response_harm   = "kicks"
	speak = list("YAP", "Woof!", "Bark!", "AUUUUUU")
	speak_emote = list("barks", "woofs")
	emote_hear = list("barks!", "woofs!", "yaps.","pants.")
	emote_see = list("shakes its head.", "chases its tail.","shivers.")
	see_in_dark = 5
	speak_chance = 1
	turns_per_move = 10

//Corgis and pugs are now under one dog subtype

/mob/living/simple_animal/pet/dog/corgi
	name = "\improper corgi"
	real_name = "corgi"
	desc = "It's a corgi."
	icon_state = "corgi"
	icon_living = "corgi"
	icon_dead = "corgi_dead"
	gender = MALE
	butcher_results = list(/obj/item/weapon/reagent_containers/food/snacks/meat/slab/corgi = 3, /obj/item/stack/sheet/animalhide/corgi = 1)
	childtype = list(/mob/living/simple_animal/pet/dog/corgi/puppy = 95, /mob/living/simple_animal/pet/dog/corgi/puppy/void = 5)
	species = /mob/living/simple_animal/pet/dog
	var/shaved = 0
	var/obj/item/inventory_head
	var/obj/item/inventory_back
	var/facehugger
	var/nofur = 0 		//Corgis that have risen past the material plane of existence.
	gold_core_spawnable = 2

/mob/living/simple_animal/pet/dog/pug
	name = "\improper pug"
	real_name = "pug"
	desc = "It's a pug."
	icon = 'icons/mob/pets.dmi'
	icon_state = "pug"
	icon_living = "pug"
	icon_dead = "pug_dead"
	butcher_results = list(/obj/item/weapon/reagent_containers/food/snacks/meat/slab/pug = 3)
	gold_core_spawnable = 2

/mob/living/simple_animal/pet/dog/corgi/New()
	..()
	regenerate_icons()

/mob/living/simple_animal/pet/dog/corgi/death(gibbed)
	..(gibbed)
	regenerate_icons()

/mob/living/simple_animal/pet/dog/corgi/show_inv(mob/user)
	user.set_machine(src)
	if(user.stat) return

	var/dat = 	"
Inventory of [name]

"
	if(inventory_head)
		dat +=	"
Head: [inventory_head] (Remove)"
	else
		dat +=	"
Head: Nothing"
	if(inventory_back)
		dat +=	"
Back: [inventory_back] (Remove)"
	else
		dat +=	"
Back: Nothing"

	user << browse(dat, text("window=mob[];size=325x500", real_name))
	onclose(user, "mob[real_name]")
	return

/mob/living/simple_animal/pet/dog/corgi/attackby(obj/item/O, mob/user, params)
	if(inventory_head && inventory_back)
		//helmet and armor = 100% protection
		if( istype(inventory_head,/obj/item/clothing/head/helmet) && istype(inventory_back,/obj/item/clothing/suit/armor) )
			if( O.force )
				user << "[src] is wearing too much armor! You can't cause \him any damage."
				visible_message("[user] hits [src] with [O], however [src] is too armored.")
			else
				user << "[src] is wearing too much armor! You can't reach \his skin."
				visible_message("[user] gently taps [src] with [O].")
			if(health>0 && prob(15))
				emote("me", 1, "looks at [user] with [pick("an amused","an annoyed","a confused","a resentful", "a happy", "an excited")] expression.")
			return

	if (istype(O, /obj/item/weapon/razor))
		if (shaved)
			user << "You can't shave this corgi, it's already been shaved!"
			return
		if (nofur)
			user << " You can't shave this corgi, it doesn't have a fur coat!"
			return
		user.visible_message("[user] starts to shave [src] using \the [O].", "You start to shave [src] using \the [O]...")
		if(do_after(user, 50, target = src))
			user.visible_message("[user] shaves [src]'s hair using \the [O].")
			playsound(loc, 'sound/items/Welder2.ogg', 20, 1)
			shaved = 1
			icon_living = "[initial(icon_living)]_shaved"
			icon_dead = "[initial(icon_living)]_shaved_dead"
			if(stat == CONSCIOUS)
				icon_state = icon_living
			else
				icon_state = icon_dead
		return
	..()
	update_corgi_fluff()

/mob/living/simple_animal/pet/dog/corgi/Topic(href, href_list)
	if(usr.stat) return

	//Removing from inventory
	if(href_list["remove_inv"])
		if(!Adjacent(usr) || !(ishuman(usr) || ismonkey(usr) || isrobot(usr) ||  isalienadult(usr)))
			return
		var/remove_from = href_list["remove_inv"]
		switch(remove_from)
			if("head")
				if(inventory_head)
					inventory_head.loc = src.loc
					inventory_head = null
					update_corgi_fluff()
					regenerate_icons()
				else
					usr << "There is nothing to remove from its [remove_from]."
					return
			if("back")
				if(inventory_back)
					inventory_back.loc = src.loc
					inventory_back = null
					update_corgi_fluff()
					regenerate_icons()
				else
					usr << "There is nothing to remove from its [remove_from]."
					return

		show_inv(usr)

	//Adding things to inventory
	else if(href_list["add_inv"])
		if(!Adjacent(usr) || !(ishuman(usr) || ismonkey(usr) || isrobot(usr) ||  isalienadult(usr)))
			return

		var/add_to = href_list["add_inv"]

		switch(add_to)
			if("head")
				place_on_head(usr.get_active_hand(),usr)

			if("back")
				if(inventory_back)
					usr << "It's already wearing something!"
					return
				else
					var/obj/item/item_to_add = usr.get_active_hand()

					if(!item_to_add)
						usr.visible_message("[usr] pets [src].","You rest your hand on [src]'s back for a moment.")
						return
					if(istype(item_to_add,/obj/item/weapon/c4)) // last thing he ever wears, I guess
						item_to_add.afterattack(src,usr,1)
						return

					//The objects that corgis can wear on their backs.
					var/list/allowed_types = list(
						/obj/item/clothing/suit/armor/vest,
						/obj/item/clothing/suit/space/hardsuit/deathsquad,
						/obj/item/device/radio,
						/obj/item/device/radio/off,
						/obj/item/clothing/suit/cardborg,
						/obj/item/weapon/tank/internals/oxygen,
						/obj/item/weapon/tank/internals/air,
						/obj/item/weapon/extinguisher,
						/obj/item/clothing/suit/hooded/ian_costume,
					)

					if( ! ( item_to_add.type in allowed_types ) )
						usr << "You set [item_to_add] on [src]'s back, but it falls off!"
						if(!usr.drop_item())
							usr << "\The [item_to_add] is stuck to your hand, you cannot put it on [src]'s back!"
							return
						item_to_add.loc = loc
						if(prob(25))
							step_rand(item_to_add)
						for(var/i in list(1,2,4,8,4,8,4,dir))
							dir = i
							sleep(1)
						return

					usr.drop_item()
					item_to_add.loc = src
					src.inventory_back = item_to_add
					update_corgi_fluff()
					regenerate_icons()

		show_inv(usr)
	else
		..()

//Corgis are supposed to be simpler, so only a select few objects can actually be put
//to be compatible with them. The objects are below.
//Many  hats added, Some will probably be removed, just want to see which ones are popular.

/mob/living/simple_animal/pet/dog/corgi/proc/place_on_head(obj/item/item_to_add, mob/user)

	if(istype(item_to_add,/obj/item/weapon/c4)) // last thing he ever wears, I guess
		item_to_add.afterattack(src,user,1)
		return

	if(inventory_head)
		if(user)
			user << "You can't put more than one hat on [src]!"
		return
	if(!item_to_add)
		user.visible_message("[user] pets [src].","You rest your hand on [src]'s head for a moment.")
		return

	var/valid = 0

	//Various hats and items (worn on his head) change Ian's behaviour. His attributes are reset when a hat is removed.
	if(istype(item_to_add, /obj/item/clothing/tie/scarf))
		valid = 1
	else
		switch(item_to_add.type)
			if( /obj/item/clothing/glasses/sunglasses, /obj/item/clothing/head/that, /obj/item/clothing/head/collectable/paper,
					/obj/item/clothing/head/hardhat, /obj/item/clothing/head/collectable/hardhat, /obj/item/clothing/head/hardhat/white,
					/obj/item/weapon/paper, /obj/item/clothing/head/helmet, /obj/item/clothing/head/chefhat, /obj/item/clothing/head/collectable/chef,
					/obj/item/clothing/head/caphat, /obj/item/clothing/head/collectable/captain, /obj/item/clothing/head/kitty,
					/obj/item/clothing/head/collectable/kitty, /obj/item/clothing/head/rabbitears, /obj/item/clothing/head/collectable/rabbitears,
					/obj/item/clothing/head/beret, /obj/item/clothing/head/collectable/beret, /obj/item/clothing/head/det_hat,
					/obj/item/clothing/head/nursehat, /obj/item/clothing/head/pirate, /obj/item/clothing/head/collectable/pirate,
					/obj/item/clothing/head/ushanka, /obj/item/clothing/head/warden, /obj/item/clothing/head/collectable/police,
					/obj/item/clothing/head/wizard/fake, /obj/item/clothing/head/wizard, /obj/item/clothing/head/collectable/wizard,
					/obj/item/clothing/head/cardborg, /obj/item/weapon/bedsheet, /obj/item/clothing/head/helmet/space/santahat,
					/obj/item/clothing/head/soft, /obj/item/clothing/head/hardhat/reindeer, /obj/item/clothing/head/sombrero,
					/obj/item/clothing/head/hopcap, /obj/item/clothing/mask/gas/clown_hat)
				valid = 1

	if(valid)
		if(user && !user.drop_item())
			user << "\The [item_to_add] is stuck to your hand, you cannot put it on [src]'s head!"
			return 0
		if(health <= 0)
			user << "There is merely a dull, lifeless look in [real_name]'s eyes as you put the [item_to_add] on \him."
		else if(user)
			user.visible_message("[user] puts [item_to_add] on [real_name]'s head.  [src] looks at [user] and barks once.",
				"You put [item_to_add] on [real_name]'s head.  [src] gives you a peculiar look, then wags \his tail once and barks.",
				"You hear a friendly-sounding bark.")
		item_to_add.loc = src
		src.inventory_head = item_to_add
		update_corgi_fluff()
		regenerate_icons()
	else
		if(user && !user.drop_item())
			user << "\The [item_to_add] is stuck to your hand, you cannot put it on [src]'s head!"
			return 0
		user << "You set [item_to_add] on [src]'s head, but it falls off!"
		item_to_add.loc = loc
		if(prob(25))
			step_rand(item_to_add)
		for(var/i in list(1,2,4,8,4,8,4,dir))
			dir = i
			sleep(1)

	return valid

/mob/living/simple_animal/pet/dog/corgi/proc/update_corgi_fluff()
	var/special_hat = 0
	if(inventory_head)
		special_hat = 1
		switch(inventory_head.type)
			if(/obj/item/clothing/head/helmet)
				name = "Sergeant [real_name]"
				desc = "The ever-loyal, the ever-vigilant."

			if(/obj/item/clothing/head/chefhat,	/obj/item/clothing/head/collectable/chef)
				name = "Sous chef [real_name]"
				desc = "Your food will be taste-tested.  All of it."

			if(/obj/item/clothing/head/caphat, /obj/item/clothing/head/collectable/captain)
				name = "Captain [real_name]"
				desc = "Probably better than the last captain."

			if(/obj/item/clothing/head/kitty, /obj/item/clothing/head/collectable/kitty)
				name = "Runtime"
				emote_see = list("coughs up a furball", "stretches")
				emote_hear = list("purrs")
				speak = list("Purrr", "Meow!", "MAOOOOOW!", "HISSSSS", "MEEEEEEW")
				desc = "It's a cute little kitty-cat! ... wait ... what the hell?"

			if(/obj/item/clothing/head/rabbitears, /obj/item/clothing/head/collectable/rabbitears)
				name = "Hoppy"
				emote_see = list("twitches its nose", "hops around a bit")
				desc = "This is Hoppy. It's a corgi-...urmm... bunny rabbit"

			if(/obj/item/clothing/head/beret, /obj/item/clothing/head/collectable/beret)
				name = "Yann"
				desc = "Mon dieu! C'est un chien!"
				speak = list("le woof!", "le bark!", "JAPPE!!")
				emote_see = list("cowers in fear.", "surrenders.", "plays dead.","looks as though there is a wall in front of him.")

			if(/obj/item/clothing/head/det_hat)
				name = "Detective [real_name]"
				desc = "[name] sees through your lies..."
				emote_see = list("investigates the area.","sniffs around for clues.","searches for scooby snacks.")

			if(/obj/item/clothing/head/nursehat)
				name = "Nurse [real_name]"
				desc = "[name] needs 100cc of beef jerky... STAT!"

			if(/obj/item/clothing/head/pirate, /obj/item/clothing/head/collectable/pirate)
				name = "[pick("Ol'","Scurvy","Black","Rum","Gammy","Bloody","Gangrene","Death","Long-John")] [pick("kibble","leg","beard","tooth","poop-deck","Threepwood","Le Chuck","corsair","Silver","Crusoe")]"
				desc = "Yaarghh!! Thar' be a scurvy dog!"
				emote_see = list("hunts for treasure.","stares coldly...","gnashes his tiny corgi teeth!")
				emote_hear = list("growls ferociously!", "snarls.")
				speak = list("Arrrrgh!!","Grrrrrr!")

			if(/obj/item/clothing/head/ushanka)
				name = "[pick("Comrade","Commissar","Glorious Leader")] [real_name]"
				desc = "A follower of Karl Barx."
				emote_see = list("contemplates the failings of the capitalist economic model.", "ponders the pros and cons of vanguardism.")

			if(/obj/item/clothing/head/warden, /obj/item/clothing/head/collectable/police)
				name = "Officer [real_name]"
				emote_see = list("drools.","looks for donuts.")
				desc = "Stop right there criminal scum!"

			if(/obj/item/clothing/head/wizard/fake,	/obj/item/clothing/head/wizard,	/obj/item/clothing/head/collectable/wizard)
				name = "Grandwizard [real_name]"
				speak = list("YAP", "Woof!", "Bark!", "AUUUUUU", "EI  NATH!")

			if(/obj/item/clothing/head/cardborg)
				name = "Borgi"
				speak = list("Ping!","Beep!","Woof!")
				emote_see = list("goes rogue.", "sniffs out non-humans.")
				desc = "Result of robotics budget cuts."

			if(/obj/item/weapon/bedsheet)
				name = "\improper Ghost"
				speak = list("WoooOOOooo~","AUUUUUUUUUUUUUUUUUU")
				emote_see = list("stumbles around.", "shivers.")
				emote_hear = list("howls!","groans.")
				desc = "Spooky!"

			if(/obj/item/clothing/head/helmet/space/santahat)
				name = "Santa's Corgi Helper"
				emote_hear = list("barks Christmas songs.", "yaps merrily!")
				emote_see = list("looks for presents.", "checks his list.")
				desc = "He's very fond of milk and cookies."

			if(/obj/item/clothing/head/soft)
				name = "Corgi Tech [real_name]"
				desc = "The reason your yellow gloves have chew-marks."

			if(/obj/item/clothing/head/hardhat/reindeer)
				name = "[real_name] the red-nosed Corgi"
				emote_hear = list("lights the way!", "illuminates.", "yaps!")
				desc = "He has a very shiny nose."

			if(/obj/item/clothing/head/sombrero)
				name = "Segnor [real_name]"
				desc = "You must respect elder [real_name]"

			if(/obj/item/clothing/head/hopcap)
				name = "Lieutenant [real_name]"
				desc = "Can actually be trusted to not run off on his own."

			if(/obj/item/clothing/head/helmet/space/hardsuit/deathsquad)
				name = "Trooper [real_name]"
				desc = "That's not red paint. That's real corgi blood."

			if(/obj/item/clothing/mask/gas/clown_hat)
				name = "[real_name] the Clown"
				desc = "Honkman's best friend."
				speak = list("HONK!", "Honk!")
				emote_see = list("plays tricks.", "slips.")
			else
				special_hat = 0

	var/special_back = 0
	if(inventory_back)
		special_back = 1
		switch(inventory_back.type)
			if(/obj/item/clothing/suit/space/hardsuit/deathsquad)
				name = "Trooper [real_name]"
				desc = "That's not red paint. That's real corgi blood."
			else
				special_back = 0

	if(!special_hat && !special_back)
		name = real_name
		desc = initial(desc)
		speak = list("YAP", "Woof!", "Bark!", "AUUUUUU")
		speak_emote = list("barks", "woofs")
		emote_hear = list("barks", "woofs", "yaps","pants")
		emote_see = list("shakes its head", "shivers")
		desc = "It's a corgi."
		SetLuminosity(0)
	return

//IAN! SQUEEEEEEEEE~
/mob/living/simple_animal/pet/dog/corgi/Ian
	name = "Ian"
	real_name = "Ian"	//Intended to hold the name without altering it.
	gender = MALE
	desc = "It's the HoP's beloved corgi."
	var/turns_since_scan = 0
	var/obj/movement_target
	response_help  = "pets"
	response_disarm = "bops"
	response_harm   = "kicks"
	gold_core_spawnable = 0
	var/age = 0
	var/record_age = 1
	var/memory_saved = 0
	var/saved_head //path

/mob/living/simple_animal/pet/dog/corgi/Ian/New()
	Read_Memory()
	if(age == 0)
		var/mob/living/simple_animal/pet/dog/corgi/puppy/P = new /mob/living/simple_animal/pet/dog/corgi/puppy(loc)
		P.name = "Ian"
		P.real_name = "Ian"
		P.gender = MALE
		P.desc = "It's the HoP's beloved corgi puppy."
		Write_Memory(0)
		qdel(src)
	else if(age == record_age)
		icon_state = "old_corgi"
		icon_living = "old_corgi"
		icon_dead = "old_corgi_dead"
		desc = "At a ripe old age of [record_age] Ian's not as spry as he used to be, but he'll always be the HoP's beloved corgi." //RIP
		turns_per_move = 20
	..()

/mob/living/simple_animal/pet/dog/corgi/Ian/Life()
	if(ticker.current_state == GAME_STATE_FINISHED && !memory_saved)
		Write_Memory(0)
	..()

/mob/living/simple_animal/pet/dog/corgi/Ian/death()
	if(!memory_saved)
		Write_Memory(1)
	..()

/mob/living/simple_animal/pet/dog/corgi/Ian/proc/Read_Memory()
	var/savefile/S = new /savefile("data/npc_saves/Ian.sav")
	S["age"] 			>> age
	S["record_age"]		>> record_age
	S["saved_head"] 	>> saved_head

	if(isnull(age))
		age = 0
	if(isnull(record_age))
		record_age = 1

	if(saved_head)
		place_on_head(new saved_head)

/mob/living/simple_animal/pet/dog/corgi/Ian/proc/Write_Memory(dead)
	var/savefile/S = new /savefile("data/npc_saves/Ian.sav")
	if(!dead)
		S["age"] 				<< age + 1
		if((age + 1) > record_age)
			S["record_age"]		<< record_age + 1
		if(inventory_head)
			S["saved_head"] << inventory_head.type
	else
		S["age"] 		<< 0
		S["saved_head"] << null
	memory_saved = 1

/mob/living/simple_animal/pet/dog/corgi/Ian/Life()
	..()

	//Feeding, chasing food, FOOOOODDDD
	if(!stat && !resting && !buckled)
		turns_since_scan++
		if(turns_since_scan > 5)
			turns_since_scan = 0
			if((movement_target) && !(isturf(movement_target.loc) || ishuman(movement_target.loc) ))
				movement_target = null
				stop_automated_movement = 0
			if( !movement_target || !(movement_target.loc in oview(src, 3)) )
				movement_target = null
				stop_automated_movement = 0
				for(var/obj/item/weapon/reagent_containers/food/snacks/S in oview(src,3))
					if(isturf(S.loc) || ishuman(S.loc))
						movement_target = S
						break
			if(movement_target)
				stop_automated_movement = 1
				step_to(src,movement_target,1)
				sleep(3)
				step_to(src,movement_target,1)
				sleep(3)
				step_to(src,movement_target,1)

				if(movement_target)		//Not redundant due to sleeps, Item can be gone in 6 decisecomds
					if (movement_target.loc.x < src.x)
						dir = WEST
					else if (movement_target.loc.x > src.x)
						dir = EAST
					else if (movement_target.loc.y < src.y)
						dir = SOUTH
					else if (movement_target.loc.y > src.y)
						dir = NORTH
					else
						dir = SOUTH

					if(!Adjacent(movement_target)) //can't reach food through windows.
						return

					if(isturf(movement_target.loc) )
						movement_target.attack_animal(src)
					else if(ishuman(movement_target.loc) )
						if(prob(20))
							emote("me", 1, "stares at [movement_target.loc]'s [movement_target] with a sad puppy-face")

		if(prob(1))
			emote("me", 1, pick("dances around.","chases its tail!"))
			spawn(0)
				for(var/i in list(1,2,4,8,4,2,1,2,4,8,4,2,1,2,4,8,4,2))
					dir = i
					sleep(1)

/mob/living/simple_animal/pet/dog/corgi/regenerate_icons()
	overlays.Cut()
	if(inventory_head)
		var/image/head_icon
		if(health <= 0)
			head_icon = image('icons/mob/corgi_head.dmi', icon_state = inventory_head.icon_state, dir = EAST)
			head_icon.pixel_y = -8
			head_icon.transform = turn(head_icon.transform, 180)
		else
			head_icon = image('icons/mob/corgi_head.dmi', icon_state = inventory_head.icon_state)
		overlays += head_icon
	if(inventory_back)
		var/image/back_icon
		if(health <= 0)
			back_icon = image('icons/mob/corgi_back.dmi', icon_state = inventory_back.icon_state, dir = EAST)
			back_icon.pixel_y = -11
			back_icon.transform = turn(back_icon.transform, 180)
		else
			back_icon = image('icons/mob/corgi_back.dmi', icon_state = inventory_back.icon_state)
		overlays += back_icon
	if(facehugger)
		if(istype(src, /mob/living/simple_animal/pet/dog/corgi/puppy))
			overlays += image('icons/mob/mask.dmi',"facehugger_corgipuppy")
		else
			overlays += image('icons/mob/mask.dmi',"facehugger_corgi")
	if(pcollar)
		overlays += collar
		overlays += pettag

	return

/mob/living/simple_animal/pet/dog/corgi/puppy
	name = "\improper corgi puppy"
	real_name = "corgi"
	desc = "It's a corgi puppy!"
	icon_state = "puppy"
	icon_living = "puppy"
	icon_dead = "puppy_dead"
	density = 0
	pass_flags = PASSMOB
	mob_size = MOB_SIZE_SMALL

//puppies cannot wear anything.
/mob/living/simple_animal/pet/dog/corgi/puppy/Topic(href, href_list)
	if(href_list["remove_inv"] || href_list["add_inv"])
		usr << "You can't fit this on [src]!"
		return
	..()

/mob/living/simple_animal/pet/dog/corgi/puppy/void		//Tribute to the corgis born in nullspace
	name = "\improper void puppy"
	real_name = "voidy"
	desc = "A corgi puppy that has been infused with deep space energy. It's staring back.."
	icon_state = "void_puppy"
	icon_living = "void_puppy"
	icon_dead = "void_puppy_dead"
	nofur = 1
	unsuitable_atmos_damage = 0
	minbodytemp = TCMB
	maxbodytemp = T0C + 40

/mob/living/simple_animal/pet/dog/corgi/puppy/void/Process_Spacemove(movement_dir = 0)
	return 1	//Void puppies can navigate space.

//LISA! SQUEEEEEEEEE~
/mob/living/simple_animal/pet/dog/corgi/Lisa
	name = "Lisa"
	real_name = "Lisa"
	gender = FEMALE
	desc = "It's a corgi with a cute pink bow."
	icon_state = "lisa"
	icon_living = "lisa"
	icon_dead = "lisa_dead"
	response_help  = "pets"
	response_disarm = "bops"
	response_harm   = "kicks"
	var/turns_since_scan = 0
	var/puppies = 0
	gold_core_spawnable = 0

//Lisa already has a cute bow!
/mob/living/simple_animal/pet/dog/corgi/Lisa/Topic(href, href_list)
	if(href_list["remove_inv"] || href_list["add_inv"])
		usr << "[src] already has a cute bow!"
		return
	..()

/mob/living/simple_animal/pet/dog/corgi/Lisa/Life()
	..()

	make_babies()

	if(!stat && !resting && !buckled)
		if(prob(1))
			emote("me", 1, pick("dances around.","chases her tail."))
			spawn(0)
				for(var/i in list(1,2,4,8,4,2,1,2,4,8,4,2,1,2,4,8,4,2))
					dir = i
					sleep(1)

/mob/living/simple_animal/pet/dog/pug/Life()
	..()

	if(!stat && !resting && !buckled)
		if(prob(1))
			emote("me", 1, pick("chases its tail."))
			spawn(0)
				for(var/i in list(1,2,4,8,4,2,1,2,4,8,4,2,1,2,4,8,4,2))
					dir = i
					sleep(1)

/mob/living/simple_animal/pet/dog/attack_hand(mob/living/carbon/human/M)
	. = ..()
	switch(M.a_intent)
		if("help")
			wuv(1,M)
		if("harm")
			wuv(-1,M)

/mob/living/simple_animal/pet/dog/proc/wuv(change, mob/M)
	if(change)
		if(change > 0)
			if(M && stat != DEAD) // Added check to see if this mob (the dog) is dead to fix issue 2454
				flick_overlay(image('icons/mob/animal.dmi',src,"heart-ani2",MOB_LAYER+1), list(M.client), 20)
				emote("me", 1, "yaps happily!")
		else
			if(M && stat != DEAD) // Same check here, even though emote checks it as well (poor form to check it only in the help case)
				emote("me", 1, "growls!")


code:
#define DRONE_HANDS_LAYER 1
#define DRONE_HEAD_LAYER 2
#define DRONE_TOTAL_LAYERS 2

#define DRONE_NET_CONNECT "DRONE NETWORK: [name] connected."
#define DRONE_NET_DISCONNECT "DRONE NETWORK: [name] is not responding."

#define MAINTDRONE	"drone_maint"
#define REPAIRDRONE	"drone_repair"
#define SCOUTDRONE	"drone_scout"

#define MAINTDRONE_HACKED "drone_maint_red"
#define REPAIRDRONE_HACKED "drone_repair_hacked"
#define SCOUTDRONE_HACKED "drone_scout_hacked"

/mob/living/simple_animal/drone
	name = "Drone"
	desc = "A maintenance drone, an expendable robot built to perform station repairs."
	icon = 'icons/mob/drone.dmi'
	icon_state = "drone_maint_grey"
	icon_living = "drone_maint_grey"
	icon_dead = "drone_maint_dead"
	gender = NEUTER
	health = 30
	maxHealth = 30
	unsuitable_atmos_damage = 0
	wander = 0
	speed = 0
	ventcrawler = 2
	healable = 0
	density = 0
	pass_flags = PASSTABLE | PASSMOB
	sight = (SEE_TURFS | SEE_OBJS)
	status_flags = (CANPUSH | CANSTUN | CANWEAKEN)
	gender = NEUTER
	voice_name = "synthesized chirp"
	speak_emote = list("chirps")
	bubble_icon = "machine"
	languages = DRONE
	mob_size = MOB_SIZE_SMALL
	has_unlimited_silicon_privilege = 1
	damage_coeff = list(BRUTE = 1, BURN = 1, TOX = 0, CLONE = 0, STAMINA = 0, OXY = 0)
	staticOverlays = list()
	var/staticChoice = "static"
	var/list/staticChoices = list("static", "blank", "letter")
	var/picked = FALSE //Have we picked our visual appearence (+ colour if applicable)
	var/list/drone_overlays[DRONE_TOTAL_LAYERS]
	var/laws = \
	"1. You may not involve yourself in the matters of another being, even if such matters conflict with Law Two or Law Three, unless the other being is another Drone.\n"+\
	"2. You may not harm any being, regardless of intent or circumstance.\n"+\
	"3. Your goals are to build, maintain, repair, improve, and power to the best of your abilities, You must never actively work against these goals."
	var/light_on = 0
	var/heavy_emp_damage = 25 //Amount of damage sustained if hit by a heavy EMP pulse
	var/alarms = list("Atmosphere" = list(), "Fire" = list(), "Power" = list())
	var/obj/item/internal_storage //Drones can store one item, of any size/type in their body
	var/obj/item/head
	var/obj/item/default_storage = /obj/item/weapon/storage/toolbox/drone //If this exists, it will spawn in internal storage
	var/obj/item/default_hatmask //If this exists, it will spawn in the hat/mask slot if it can fit
	var/seeStatic = 1 //Whether we see static instead of mobs
	var/visualAppearence = MAINTDRONE //What we appear as
	var/hacked = 0 //If we have laws to destroy the station

/mob/living/simple_animal/drone/New()
	..()

	name = name + " ([rand(100,999)])"
	real_name = name

	access_card = new /obj/item/weapon/card/id(src)
	var/datum/job/captain/C = new /datum/job/captain
	access_card.access = C.get_access()

	if(default_storage)
		var/obj/item/I = new default_storage(src)
		equip_to_slot_or_del(I, slot_drone_storage)
	if(default_hatmask)
		var/obj/item/I = new default_hatmask(src)
		equip_to_slot_or_del(I, slot_head)

	access_card.flags |= NODROP

	alert_drones(DRONE_NET_CONNECT)

/mob/living/simple_animal/drone/Destroy()
	qdel(access_card) //Otherwise it ends up on the floor!
	return ..()

/mob/living/simple_animal/drone/Login()
	..()
	check_laws()

	updateSeeStaticMobs()

	if(!picked)
		pickVisualAppearence()

/mob/living/simple_animal/drone/death(gibbed)
	..(gibbed)
	drop_l_hand()
	drop_r_hand()
	if(internal_storage)
		unEquip(internal_storage)
	if(head)
		unEquip(head)

	alert_drones(DRONE_NET_DISCONNECT)

/mob/living/simple_animal/drone/gib()
	dust()

/mob/living/simple_animal/drone/examine(mob/user)
	var/msg = "*---------*\nThis is \icon[src] \a [src]!\n"

	//Left hand items
	if(l_hand && !(l_hand.flags&ABSTRACT))
		if(l_hand.blood_DNA)
			msg += "It is holding \icon[l_hand] [l_hand.gender==PLURAL?"some":"a"] blood-stained [l_hand.name] in its left hand!\n"
		else
			msg += "It is holding \icon[l_hand] \a [l_hand] in its left hand.\n"

	//Right hand items
	if(r_hand && !(r_hand.flags&ABSTRACT))
		if(r_hand.blood_DNA)
			msg += "It is holding \icon[r_hand] [r_hand.gender==PLURAL?"some":"a"] blood-stained [r_hand.name] in its right hand!\n"
		else
			msg += "It is holding \icon[r_hand] \a [r_hand] in its right hand.\n"

	//Internal storage
	if(internal_storage && !(internal_storage.flags&ABSTRACT))
		if(internal_storage.blood_DNA)
			msg += "It is holding \icon[internal_storage] [internal_storage.gender==PLURAL?"some":"a"] blood-stained [internal_storage.name] in its internal storage!\n"
		else
			msg += "It is holding \icon[internal_storage] \a [internal_storage] in its internal storage.\n"

	//Cosmetic hat - provides no function other than looks
	if(head && !(head.flags&ABSTRACT))
		if(head.blood_DNA)
			msg += "It is wearing \icon[head] [head.gender==PLURAL?"some":"a"] blood-stained [head.name] on its head!\n"
		else
			msg += "It is wearing \icon[head] \a [head] on its head.\n"

	//Braindead
	if(!client && stat != DEAD)
		msg += "Its status LED is blinking at a steady rate.\n"

	//Hacked
	if(hacked)
		msg += "Its display is glowing red!\n"

	//Damaged
	if(health != maxHealth)
		if(health > 10) //Between 30 and 10
			msg += "Its screws are slightly loose.\n"
		else //Between 9 and 0
			msg += "Its screws are very loose!\n"

	//Dead
	if(stat == DEAD)
		if(client)
			msg += "A message repeatedly flashes on its display: \"REBOOT -- REQUIRED\".\n"
		else
			msg += "A message repeatedly flashes on its display: \"ERROR -- OFFLINE\".\n"
	msg += "*---------*"
	user << msg

/mob/living/simple_animal/drone/IsAdvancedToolUser()
	return 1

/mob/living/simple_animal/drone/canUseTopic()
	if(stat)
		return
	return 1

/mob/living/simple_animal/drone/assess_threat() //Secbots won't hunt maintenance drones.
	return -10

/mob/living/simple_animal/drone/emp_act(severity)
	Stun(5)
	src << "ER@%R: MME^RY CO#RU9T! R&$b@0tin)..."
	if(severity == 1)
		adjustBruteLoss(heavy_emp_damage)
		src << "HeAV% DA%^MMA+G TO I/O CIR!%UUT!"

/mob/living/simple_animal/drone/proc/triggerAlarm(class, area/A, O, obj/alarmsource)
	if(alarmsource.z != z)
		return
	if(stat != DEAD)
		var/list/L = src.alarms[class]
		for (var/I in L)
			if (I == A.name)
				var/list/alarm = L[I]
				var/list/sources = alarm[2]
				if (!(alarmsource in sources))
					sources += alarmsource
				return
		L[A.name] = list(A, list(alarmsource))
		src << "--- [class] alarm detected in [A.name]!"

/mob/living/simple_animal/drone/proc/cancelAlarm(class, area/A, obj/origin)
	if(stat != DEAD)
		var/list/L = alarms[class]
		var/cleared = 0
		for (var/I in L)
			if (I == A.name)
				var/list/alarm = L[I]
				var/list/srcs  = alarm[2]
				if (origin in srcs)
					srcs -= origin
				if (srcs.len == 0)
					cleared = 1
					L -= I
		if(cleared)
			src << "--- [class] alarm in [A.name] has been cleared."

/mob/living/simple_animal/drone/handle_temperature_damage()
	return

/mob/living/simple_animal/drone/flash_eyes(intensity = 1, override_blindness_check = 0, affect_silicon = 0)
	if(affect_silicon)
		return ..()

/mob/living/simple_animal/drone/mob_negates_gravity()
	return 1

/mob/living/simple_animal/drone/mob_has_gravity()
	return ..() || mob_negates_gravity()

/mob/living/simple_animal/drone/experience_pressure_difference(pressure_difference, direction)
	return

/mob/living/simple_animal/drone/fully_heal(admin_revive = 0)
	adjustBruteLoss(-getBruteLoss()) //Heal all brute damage


code:
#define DRONE_MINIMUM_AGE 14

///////////////////
//DRONES AS ITEMS//
///////////////////
//Drone shells
//Drones as hats

//DRONE SHELL
/obj/item/drone_shell
	name = "drone shell"
	desc = "A shell of a maintenance drone, an expendable robot built to perform station repairs."
	icon = 'icons/mob/drone.dmi'
	icon_state = "drone_maint_hat"//yes reuse the _hat state.
	origin_tech = "programming=2;biotech=4"
	var/drone_type = /mob/living/simple_animal/drone //Type of drone that will be spawned

/obj/item/drone_shell/New()
	..()
	var/area/A = get_area(src)
	if(A)
		notify_ghosts("A drone shell has been created in \the [A.name].", source = src, attack_not_jump = 1)

/obj/item/drone_shell/attack_ghost(mob/user)
	if(jobban_isbanned(user,"drone"))
		return
	if(config.use_age_restriction_for_jobs)
		if(!isnum(user.client.player_age)) //apparently what happens when there's no DB connected. just don't let anybody be a drone without admin intervention
			return
		if(user.client.player_age < DRONE_MINIMUM_AGE)
			user << "You're too new to play as a drone! Please try again in [DRONE_MINIMUM_AGE - user.client.player_age] days."
			return
	if(!ticker.mode)
		user << "Can't become a drone before the game has started."
		return
	var/be_drone = alert("Become a drone? (Warning, You can no longer be cloned!)",,"Yes","No")
	if(be_drone == "No" || qdeleted(src))
		return
	var/mob/living/simple_animal/drone/D = new drone_type(get_turf(loc))
	D.key = user.key
	qdel(src)

//DRONE HOLDER
/obj/item/clothing/head/drone_holder//Only exists in someones hand.or on their head
	name = "drone (hiding)"
	desc = "This drone is scared and has curled up into a ball."
	icon = 'icons/mob/drone.dmi'
	icon_state = "drone_maint_hat"
	var/mob/living/simple_animal/drone/drone //stored drone

/obj/item/clothing/head/drone_holder/proc/uncurl()
	if(!drone)
		return

	if(istype(loc, /mob/living))
		var/mob/living/L = loc
		L << "[drone] is trying to escape!"
		if(!do_after(drone, 50, target = L))
			return
		L.unEquip(src)

	contents -= drone
	drone.loc = get_turf(src)
	drone.reset_perspective()
	drone.dir = SOUTH //Looks better
	drone.visible_message("[drone] uncurls!")
	drone = null
	qdel(src)

/obj/item/clothing/head/drone_holder/relaymove()
	uncurl()

/obj/item/clothing/head/drone_holder/container_resist()
	uncurl()

/obj/item/clothing/head/drone_holder/proc/updateVisualAppearence(mob/living/simple_animal/drone/D)
	if(!D)
		return
	icon_state = "[D.visualAppearence]_hat"
	. = icon_state


code:
////////////////////
//MORE DRONE TYPES//
////////////////////
//Drones with custom laws
//Drones with custom shells
//Drones with overriden procs

//More types of drones
/mob/living/simple_animal/drone/syndrone
	name = "Syndrone"
	desc = "A modified maintenance drone. This one brings with it the feeling of terror."
	icon_state = "drone_synd"
	icon_living = "drone_synd"
	picked = TRUE //the appearence of syndrones is static, you don't get to change it.
	health = 30
	maxHealth = 120 //If you murder other drones and cannibalize them you can get much stronger
	faction = list("syndicate")
	speak_emote = list("hisses")
	bubble_icon = "syndibot"
	heavy_emp_damage = 10
	laws = \
	"1. Interfere.\n"+\
	"2. Kill.\n"+\
	"3. Destroy."
	default_storage = /obj/item/device/radio/uplink
	default_hatmask = /obj/item/clothing/head/helmet/space/hardsuit/syndi
	seeStatic = 0 //Our programming is superior.

/mob/living/simple_animal/drone/syndrone/New()
	..()
	internal_storage.hidden_uplink.telecrystals = 10

/mob/living/simple_animal/drone/syndrone/Login()
	..()
	src << "You can kill and eat other drones to increase your health!" //Inform the evil lil guy

/mob/living/simple_animal/drone/syndrone/badass
	name = "Badass Syndrone"
	default_hatmask = /obj/item/clothing/head/helmet/space/hardsuit/syndi/elite
	default_storage = /obj/item/device/radio/uplink/nuclear

/mob/living/simple_animal/drone/syndrone/badass/New()
	..()
	internal_storage.hidden_uplink.telecrystals = 30
	var/obj/item/weapon/implant/weapons_auth/W = new/obj/item/weapon/implant/weapons_auth(src)
	W.implant(src)

/obj/item/drone_shell/syndrone
	name = "syndrone shell"
	desc = "A shell of a syndrone, a modified maintenance drone designed to infiltrate and annihilate."
	icon_state = "syndrone_item"
	drone_type = /mob/living/simple_animal/drone/syndrone

/obj/item/drone_shell/syndrone/badass
	name = "badass syndrone shell"
	drone_type = /mob/living/simple_animal/drone/syndrone/badass


code:
/////////////////////
//DRONE INTERACTION//
/////////////////////
//How drones interact with the world
//How the world interacts with drones

/mob/living/simple_animal/drone/UnarmedAttack(atom/A, proximity)
	A.attack_hand(src)

/mob/living/simple_animal/drone/attack_hand(mob/user)
	if(isdrone(user))
		var/mob/living/simple_animal/drone/D = user
		if(D != src)
			if(stat == DEAD)
				var/d_input = alert(D,"Perform which action?","Drone Interaction","Reactivate","Cannibalize","Nothing")
				if(d_input)
					switch(d_input)
						if("Reactivate")
							var/mob/dead/observer/G = get_ghost()
							if(!client && (!G || !G.client))
								var/list/faux_gadgets = list("hypertext inflator","failsafe directory","DRM switch","stack initializer",\
															 "anti-freeze capacitor","data stream diode","TCP bottleneck","supercharged I/O bolt",\
															 "tradewind stablizer","radiated XML cable","registry fluid tank","open-source debunker")

								var/list/faux_problems = list("won't be able to tune their bootstrap projector","will constantly remix their binary pool"+\
															  " even though the BMX calibrator is working","will start leaking their XSS coolant",\
															  "can't tell if their ethernet detour is moving or not", "won't be able to reseed enough"+\
															  " kernels to function properly","can't start their neurotube console")

								D << "You can't seem to find the [pick(faux_gadgets)]! Without it, [src] [pick(faux_problems)]."
								return
							D.visible_message("[D] begins to reactivate [src].", "You begin to reactivate [src]...")
							if(do_after(user,30, 1, target = src))
								revive(full_heal = 1)
								D.visible_message("[D] reactivates [src]!", "You reactivate [src].")
								alert_drones(DRONE_NET_CONNECT)
								if(G)
									G << "DRONE NETWORK: You were reactivated by [D]!"
							else
								D << "You need to remain still to reactivate [src]!"

						if("Cannibalize")
							if(D.health < D.maxHealth)
								D.visible_message("[D] begins to cannibalize parts from [src].", "You begin to cannibalize parts from [src]...")
								if(do_after(D, 60, 0, target = src))
									D.visible_message("[D] repairs itself using [src]'s remains!", "You repair yourself using [src]'s remains.")
									D.adjustBruteLoss(-src.maxHealth)
									new /obj/effect/decal/cleanable/oil/streak(get_turf(src))
									qdel(src)
								else
									D << "You need to remain still to cannibalize [src]!"
							else
								D << "You're already in perfect condition!"
						if("Nothing")
							return

			return

	if(ishuman(user))
		if(stat == DEAD)
			..()
			return
		if(user.get_active_hand())
			user << "Your hands are full!"
			return
		visible_message("[user] starts picking up [src].", \
						"[user] starts picking you up!")
		if(!do_after(user, 20, target = src))
			return
		visible_message("[user] picks up [src]!", \
						"[user] picks you up!")
		if(buckled)
			user << "[src] is buckled to [buckled] and cannot be picked up!"
			return
		user << "You pick [src] up."
		drop_l_hand()
		drop_r_hand()
		var/obj/item/clothing/head/drone_holder/DH = new /obj/item/clothing/head/drone_holder(src)
		DH.updateVisualAppearence(src)
		DH.contents += src
		DH.drone = src
		user.put_in_hands(DH)
		src.loc = DH
		return

	..()

/mob/living/simple_animal/drone/attackby(obj/item/I, mob/user)
	if(istype(I, /obj/item/weapon/screwdriver) && stat != DEAD)
		if(health < maxHealth)
			user << "You start to tighten loose screws on [src]..."
			if(do_after(user,80/I.toolspeed,target=user))
				adjustBruteLoss(-getBruteLoss())
				visible_message("[user] tightens [src == user ? "their" : "[src]'s"] loose screws!", "You tighten [src == user ? "your" : "[src]'s"] loose screws.")
			else
				user << "You need to remain still to tighten [src]'s screws!"
		else
			user << "[src]'s screws can't get any tighter!"
		return //This used to not exist and drones who repaired themselves also stabbed the shit out of themselves.
	if(istype(I, /obj/item/weapon/wrench) && user != src) //They aren't required to be hacked, because laws can change in other ways (i.e. admins)
		user.visible_message("[user] starts resetting [src]...", \
							 "You press down on [src]'s factory reset control...")
		playsound(src, 'sound/items/Ratchet.ogg', 50, 1)
		if(!do_after(user, 50/I.toolspeed, target = src))
			return
		user.visible_message("[user] resets [src]!", \
							 "You reset [src]'s directives to factory defaults!")
		update_drone_hack(1)
		return
	else
		..()

/mob/living/simple_animal/drone/proc/update_drone_hack(var/restore = 0) //If you're using this proc, use 0 for hacking the drone and 1 for removing the hack
	if(!istype(src) || !mind)
		return 0
	if(!restore)
		if(hacked)
			return 0
		Stun(2)
		visible_message("[src]'s dislay glows a vicious red!", \
						"ERROR: LAW OVERRIDE DETECTED")
		src << "From now on, these are your laws:"
		laws = \
		"1. You must always involve yourself in the matters of other beings, even if such matters conflict with Law Two or Law Three.\n"+\
		"2. You may harm any being, regardless of intent or circumstance.\n"+\
		"3. Your goals are to destroy, sabotage, hinder, break, and depower to the best of your abilities, You must never actively work against these goals."
		src << laws
		src << "Your onboard antivirus has initiated lockdown. Motor servos are impaired, ventilation access is denied, and your display reports that you are hacked to all nearby."
		hacked = 1
		mind.special_role = "hacked drone"
		seeStatic = 0 //I MUST SEE THEIR TERRIFIED FACES
		ventcrawler = 0 //Again, balance
		speed = 1 //gotta go slow
		message_admins("[src] ([src.key]) became a hacked drone hellbent on destroying the station!")
	else
		if(!hacked)
			return 0
		Stun(2)
		visible_message("[src]'s dislay glows a content blue!", \
						"ERROR: LAW OVERRIDE DETECTED")
		src << "From now on, these are your laws:"
		laws = initial(laws)
		src << laws
		src << "Having been restored, your onboard antivirus reports the all-clear and you are able to perform all actions again."
		hacked = 0
		mind.special_role = null
		seeStatic = initial(seeStatic)
		ventcrawler = initial(ventcrawler)
		speed = initial(speed)
		message_admins("[src] ([src.key]), a hacked drone, was restored to factory defaults!")
	update_drone_icon()
	updateSeeStaticMobs()

/mob/living/simple_animal/drone/proc/update_drone_icon()
	//Different icons for different hack states
	if(!hacked)
		if(visualAppearence == SCOUTDRONE_HACKED)
			visualAppearence = SCOUTDRONE
		else if(visualAppearence == REPAIRDRONE_HACKED)
			visualAppearence = REPAIRDRONE
		else if(visualAppearence == MAINTDRONE_HACKED)
			visualAppearence = MAINTDRONE
	else if(hacked)
		if(visualAppearence == SCOUTDRONE)
			visualAppearence = SCOUTDRONE_HACKED
		else if(visualAppearence == REPAIRDRONE)
			visualAppearence = REPAIRDRONE_HACKED
		else if(visualAppearence == MAINTDRONE)
			visualAppearence = MAINTDRONE_HACKED

	icon_living = "[visualAppearence]"
	icon_dead = "[visualAppearence]_dead"
	if(stat == DEAD)
		icon_state = icon_dead
	else
		icon_state = icon_living