code:
//Harvest; activated ly clicking the target, will try to drain their essence.
/mob/living/simple_animal/revenant/ClickOn(atom/A, params) //revenants can't interact with the world directly.
	A.examine(src)
	if(ishuman(A))
		if(A in drained_mobs)
			src << "[A]'s soul is dead and empty." //feedback at any range
		else if(in_range(src, A))
			Harvest(A)

/mob/living/simple_animal/revenant/proc/Harvest(mob/living/carbon/human/target)
	if(!castcheck(0))
		return
	if(draining)
		src << "You are already siphoning the essence of a soul!"
		return
	if(!target.stat)
		src << "This being's soul is too strong to harvest."
		if(prob(10))
			target << "You feel as if you are being watched."
		return
	draining = 1
	essence_drained += rand(15, 20)
	src << "You search for the soul of [target]."
	if(do_after(src, rand(10, 20), 0, target)) //did they get deleted in that second?
		if(target.ckey)
			src << "Their soul burns with intelligence."
			essence_drained += rand(20, 30)
		if(target.stat != DEAD)
			src << "Their soul blazes with life!"
			essence_drained += rand(40, 50)
		else
			src << "Their soul is weak and faltering."
		if(do_after(src, rand(15, 20), 0, target)) //did they get deleted NOW?
			switch(essence_drained)
				if(1 to 30)
					src << "[target] will not yield much essence. Still, every bit counts."
				if(30 to 70)
					src << "[target] will yield an average amount of essence."
				if(70 to 90)
					src << "Such a feast! [target] will yield much essence to you."
				if(90 to INFINITY)
					src << "Ah, the perfect soul. [target] will yield massive amounts of essence to you."
			if(do_after(src, rand(15, 25), 0, target)) //how about now
				if(!target.stat)
					src << "They are now powerful enough to fight off your draining."
					target << "You feel something tugging across your body before subsiding."
					draining = 0
					essence_drained = 0
					return //hey, wait a minute...
				src << "You begin siphoning essence from [target]'s soul."
				if(target.stat != DEAD)
					target << "You feel a horribly unpleasant draining sensation as your grip on life weakens..."
				reveal(46)
				stun(46)
				target.visible_message("[target] suddenly rises slightly into the air, their skin turning an ashy gray.")
				var/datum/beam/B = Beam(target,icon_state="drain_life",icon='icons/effects/effects.dmi',time=46)
				if(do_after(src, 46, 0, target)) //As one cannot prove the existance of ghosts, ghosts cannot prove the existance of the target they were draining.
					qdel(B)
					change_essence_amount(essence_drained, 0, target)
					if(essence_drained <= 90 && target.stat != DEAD)
						essence_regen_cap += 5
						src << "The absorption of [target]'s living soul has increased your maximum essence level. Your new maximum essence is [essence_regen_cap]."
					if(essence_drained > 90)
						essence_regen_cap += 15
						perfectsouls += 1
						src << "The perfection of [target]'s soul has increased your maximum essence level. Your new maximum essence is [essence_regen_cap]."
					src << "[target]'s soul has been considerably weakened and will yield no more essence for the time being."
					target.visible_message("[target] slumps onto the ground.", \
										   "Violets lights, dancing in your vision, getting clo--")
					drained_mobs.Add(target)
					target.death(0)
				else
					qdel(B)
					src << "[target ? "[target] has":"They have"] been drawn out of your grasp. The link has been broken."
					draining = 0
					essence_drained = 0
					if(target) //Wait, target is WHERE NOW?
						target.visible_message("[target] slumps onto the ground.", \
											   "Violets lights, dancing in your vision, receding--")
					return
			else
				src << "You are not close enough to siphon [target ? "[target]'s":"their"] soul. The link has been broken."
				draining = 0
				essence_drained = 0
				return
	draining = 0
	essence_drained = 0
	return

//Toggle night vision: lets the revenant toggle its night vision
/obj/effect/proc_holder/spell/targeted/night_vision/revenant
	charge_max = 0
	panel = "Revenant Abilities"
	message = "You toggle your night vision."
	action_icon_state = "r_nightvision"
	action_background_icon_state = "bg_revenant"

//Transmit: the revemant's only direct way to communicate. Sends a single message silently to a single mob
/obj/effect/proc_holder/spell/targeted/revenant_transmit
	name = "Transmit"
	desc = "Telepathically transmits a message to the target."
	panel = "Revenant Abilities"
	charge_max = 0
	clothes_req = 0
	range = 7
	include_user = 0
	action_icon_state = "r_transmit"
	action_background_icon_state = "bg_revenant"

/obj/effect/proc_holder/spell/targeted/revenant_transmit/cast(list/targets, mob/living/simple_animal/revenant/user = usr)
	for(var/mob/living/M in targets)
		spawn(0)
			var/msg = stripped_input(usr, "What do you wish to tell [M]?", null, "")
			if(!msg)
				charge_counter = charge_max
				return
			log_say("RevenantTransmit: [key_name(user)]->[key_name(M)] : [msg]")
			user << "You transmit to [M]: [msg]"
			M << "An alien voice resonates from all around... [msg]"

/obj/effect/proc_holder/spell/aoe_turf/revenant
	clothes_req = 0
	action_background_icon_state = "bg_revenant"
	panel = "Revenant Abilities (Locked)"
	name = "Report this to a coder"
	var/reveal = 80 //How long it reveals the revenant in deciseconds
	var/stun = 20 //How long it stuns the revenant in deciseconds
	var/locked = 1 //If it's locked and needs to be unlocked before use
	var/unlock_amount = 100 //How much essence it costs to unlock
	var/cast_amount = 50 //How much essence it costs to use

/obj/effect/proc_holder/spell/aoe_turf/revenant/New()
	..()
	if(locked)
		name = "[initial(name)] ([unlock_amount]E)"
	else
		name = "[initial(name)] ([cast_amount]E)"

/obj/effect/proc_holder/spell/aoe_turf/revenant/can_cast(mob/living/simple_animal/revenant/user = usr)
	if(!istype(user)) //Badmins, no. Badmins, don't do it.
		if(charge_counter < charge_max)
			return 0
		return 1
	if(user.inhibited)
		return 0
	if(charge_counter < charge_max)
		return 0
	if(locked)
		if(user.essence <= unlock_amount)
			return 0
	if(user.essence <= cast_amount)
		return 0
	return 1

/obj/effect/proc_holder/spell/aoe_turf/revenant/proc/attempt_cast(mob/living/simple_animal/revenant/user = usr)
	if(!istype(user)) //If you're not a revenant, it works. Please, please, please don't give this to a non-revenant.
		name = "[initial(name)]"
		if(locked)
			panel = "Revenant Abilities"
			locked = 0
		return 1
	if(locked)
		if(!user.castcheck(-unlock_amount))
			charge_counter = charge_max
			return 0
		name = "[initial(name)] ([cast_amount]E)"
		user << "You have unlocked [initial(name)]!"
		panel = "Revenant Abilities"
		locked = 0
		charge_counter = charge_max
		return 0
	if(!user.castcheck(-cast_amount))
		charge_counter = charge_max
		return 0
	name = "[initial(name)] ([cast_amount]E)"
	user.reveal(reveal)
	user.stun(stun)
	if(action)
		action.UpdateButtonIcon()
	return 1

//Overload Light: Breaks a light that's online and sends out lightning bolts to all nearby people.
/obj/effect/proc_holder/spell/aoe_turf/revenant/overload
	name = "Overload Lights"
	desc = "Directs a large amount of essence into nearby electrical lights, causing lights to shock those nearby."
	charge_max = 200
	range = 5
	stun = 30
	cast_amount = 40
	var/shock_range = 2
	var/shock_damage = 20
	action_icon_state = "overload_lights"

/obj/effect/proc_holder/spell/aoe_turf/revenant/overload/cast(list/targets, mob/living/simple_animal/revenant/user = usr)
	if(attempt_cast(user))
		for(var/turf/T in targets)
			spawn(0)
				for(var/obj/machinery/light/L in T.contents)
					spawn(0)
						if(!L.on)
							return
						L.visible_message("\The [L] suddenly flares brightly and begins to spark!")
						var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread
						s.set_up(4, 0, L)
						s.start()
						PoolOrNew(/obj/effect/overlay/temp/revenant, L.loc)
						sleep(20)
						if(!L.on) //wait, wait, don't shock me
							return
						flick("[L.base_state]2", L)
						for(var/mob/living/carbon/human/M in view(shock_range, L))
							if(M == user)
								continue
							L.Beam(M,icon_state="purple_lightning",icon='icons/effects/effects.dmi',time=5)
							M.electrocute_act(shock_damage, "[L.name]", safety=1)
							var/datum/effect_system/spark_spread/z = new /datum/effect_system/spark_spread
							z.set_up(4, 0, M)
							z.start()
							playsound(M, 'sound/machines/defib_zap.ogg', 50, 1, -1)

//Defile: Corrupts nearby stuff, unblesses floor tiles.
/obj/effect/proc_holder/spell/aoe_turf/revenant/defile
	name = "Defile"
	desc = "Twists and corrupts the nearby area as well as dispelling holy auras on floors."
	charge_max = 150
	range = 4
	stun = 10
	reveal = 40
	unlock_amount = 75
	cast_amount = 30
	action_icon_state = "defile"

/obj/effect/proc_holder/spell/aoe_turf/revenant/defile/cast(list/targets, mob/living/simple_animal/revenant/user = usr)
	if(attempt_cast(user))
		for(var/turf/T in targets)
			spawn(0)
				if(T.flags & NOJAUNT)
					T.flags -= NOJAUNT
					PoolOrNew(/obj/effect/overlay/temp/revenant, T)
				if(!istype(T, /turf/open/floor/plating) && !istype(T, /turf/open/floor/engine/cult) && istype(T, /turf/open/floor) && prob(15))
					var/turf/open/floor/floor = T
					if(floor.intact)
						floor.builtin_tile.loc = floor
					floor.broken = 0
					floor.burnt = 0
					floor.make_plating(1)
				if(T.type == /turf/closed/wall && prob(15))
					PoolOrNew(/obj/effect/overlay/temp/revenant, T)
					T.ChangeTurf(/turf/closed/wall/rust)
				if(T.type == /turf/closed/wall/r_wall && prob(10))
					PoolOrNew(/obj/effect/overlay/temp/revenant, T)
					T.ChangeTurf(/turf/closed/wall/r_wall/rust)
				for(var/obj/structure/closet/closet in T.contents)
					closet.open()
				for(var/obj/structure/bodycontainer/corpseholder in T.contents)
					if(corpseholder.connected.loc == corpseholder)
						corpseholder.open()
				for(var/obj/machinery/dna_scannernew/dna in T.contents)
					dna.open_machine()
				for(var/obj/structure/window/window in T.contents)
					window.hit(rand(30,80))
					if(window && window.fulltile)
						PoolOrNew(/obj/effect/overlay/temp/revenant/cracks, window.loc)
				for(var/obj/machinery/light/light in T.contents)
					light.flicker(20) //spooky

//Malfunction: Makes bad stuff happen to robots and machines.
/obj/effect/proc_holder/spell/aoe_turf/revenant/malfunction
	name = "Malfunction"
	desc = "Corrupts and damages nearby machines and mechanical objects."
	charge_max = 200
	range = 4
	cast_amount = 45
	unlock_amount = 150
	action_icon_state = "malfunction"

//A note to future coders: do not replace this with an EMP because it will wreck malf AIs and gang dominators and everyone will hate you.
/obj/effect/proc_holder/spell/aoe_turf/revenant/malfunction/cast(list/targets, mob/living/simple_animal/revenant/user = usr)
	if(attempt_cast(user))
		for(var/turf/T in targets)
			spawn(0)
				for(var/mob/living/simple_animal/bot/bot in T.contents)
					if(!bot.emagged)
						PoolOrNew(/obj/effect/overlay/temp/revenant, bot.loc)
						bot.locked = 0
						bot.open = 1
						bot.emag_act()
				for(var/mob/living/carbon/human/human in T.contents)
					if(human == user)
						continue
					human << "You feel [pick("your sense of direction flicker out", "a stabbing pain in your head", "your mind fill with static")]."
					PoolOrNew(/obj/effect/overlay/temp/revenant, human.loc)
					human.emp_act(1)
				for(var/obj/thing in T.contents)
					if(istype(thing, /obj/machinery/dominator) || istype(thing, /obj/machinery/power/apc) || istype(thing, /obj/machinery/power/smes)) //Doesn't work on dominators, SMES and APCs, to prevent kekkery
						continue
					if(prob(20))
						if(prob(50))
							PoolOrNew(/obj/effect/overlay/temp/revenant, thing.loc)
						thing.emag_act(null)
					else
						if(!istype(thing, /obj/machinery/clonepod)) //I hate everything but mostly the fact there's no better way to do this without just not affecting it at all
							thing.emp_act(1)
				for(var/mob/living/silicon/robot/S in T.contents) //Only works on cyborgs, not AI
					playsound(S, 'sound/machines/warning-buzzer.ogg', 50, 1)
					PoolOrNew(/obj/effect/overlay/temp/revenant, S.loc)
					S.spark_system.start()
					S.emp_act(1)

//Blight: Infects nearby humans and in general messes living stuff up.
/obj/effect/proc_holder/spell/aoe_turf/revenant/blight
	name = "Blight"
	desc = "Causes nearby living things to waste away."
	charge_max = 200
	range = 3
	reveal = 50
	cast_amount = 50
	unlock_amount = 200
	action_icon_state = "blight"

/obj/effect/proc_holder/spell/aoe_turf/revenant/blight/cast(list/targets, mob/living/simple_animal/revenant/user = usr)
	if(attempt_cast(user))
		for(var/turf/T in targets)
			spawn(0)
				for(var/mob/living/mob in T.contents)
					if(mob == user)
						continue
					PoolOrNew(/obj/effect/overlay/temp/revenant, mob.loc)
					if(iscarbon(mob))
						if(ishuman(mob))
							var/mob/living/carbon/human/H = mob
							if(H.dna && H.dna.species)
								H.dna.species.handle_mutant_bodyparts(H,"#1d2953")
								H.dna.species.handle_hair(H,"#1d2953")
								H.dna.species.update_color(H,"#1d2953")
								spawn(20)
									if(H && H.dna && H.dna.species)
										H.dna.species.handle_mutant_bodyparts(H)
										H.dna.species.handle_hair(H)
										H.dna.species.update_color(H)
							var/blightfound = 0
							for(var/datum/disease/revblight/blight in H.viruses)
								blightfound = 1
								if(blight.stage < 5)
									blight.stage++
							if(!blightfound)
								H.AddDisease(new /datum/disease/revblight)
								H << "You feel [pick("suddenly sick", "a surge of nausea", "like your skin is wrong")]."
						else
							if(mob.reagents)
								mob.reagents.add_reagent("plasma", 5)
					else
						mob.adjustToxLoss(5)
				for(var/obj/effect/spacevine/vine in T.contents) //Fucking with botanists, the ability.
					vine.color = "#823abb"
					PoolOrNew(/obj/effect/overlay/temp/revenant, vine.loc)
					spawn(10)
						if(vine)
							qdel(vine)
				for(var/obj/effect/glowshroom/shroom in T.contents)
					shroom.color = "#823abb"
					PoolOrNew(/obj/effect/overlay/temp/revenant, shroom.loc)
					spawn(10)
						if(shroom)
							qdel(shroom)
				for(var/obj/machinery/hydroponics/tray in T.contents)
					PoolOrNew(/obj/effect/overlay/temp/revenant, tray.loc)
					tray.pestlevel = rand(8, 10)
					tray.weedlevel = rand(8, 10)
					tray.toxic = rand(45, 55)


code:
/datum/disease/revblight
	name = "Unnatural Wasting"
	max_stages = 5
	stage_prob = 10
	spread_flags = NON_CONTAGIOUS
	cure_text = "Holy water or extensive rest."
	spread_text = "A burst of unholy energy"
	cures = list("holywater")
	cure_chance = 50 //higher chance to cure, because revenants are assholes
	agent = "Unholy Forces"
	viable_mobtypes = list(/mob/living/carbon/human)
	disease_flags = CURABLE
	permeability_mod = 1
	severity = BIOHAZARD
	var/stagedamage = 0 //Highest stage reached.
	var/finalstage = 0 //Because we're spawning off the cure in the final stage, we need to check if we've done the final stage's effects.

/datum/disease/revblight/cure()
	if(affected_mob)
		affected_mob << "You feel better."
		if(affected_mob.dna && affected_mob.dna.species)
			affected_mob.dna.species.handle_mutant_bodyparts(affected_mob)
			affected_mob.dna.species.handle_hair(affected_mob)
			affected_mob.dna.species.update_color(affected_mob)
	..()

/datum/disease/revblight/stage_act()
	if(!finalstage && affected_mob.lying && prob(stage*2))
		cure()
		return
	if(!finalstage && prob(stage*3))
		affected_mob << "You suddenly feel [pick("sick and tired", "disoriented", "tired and confused", "nauseated", "faint", "dizzy")]..."
		affected_mob.confused += 10
		affected_mob.adjustStaminaLoss(10)
		PoolOrNew(/obj/effect/overlay/temp/revenant, affected_mob.loc)
	if(!finalstage && stagedamage < stage)
		stagedamage++
		affected_mob.adjustToxLoss(stage*3) //should, normally, do about 45 toxin damage.
		PoolOrNew(/obj/effect/overlay/temp/revenant, affected_mob.loc)
	if(!finalstage && prob(45))
		affected_mob.adjustStaminaLoss(stage*2)
	..() //So we don't increase a stage before applying the stage damage.
	switch(stage)
		if(2)
			if(prob(5))
				affected_mob.emote("pale")
		if(3)
			if(prob(10))
				affected_mob.emote(pick("pale","shiver"))
		if(4)
			if(prob(15))
				affected_mob.emote(pick("pale","shiver","cries"))
		if(5)
			if(!finalstage)
				finalstage = 1
				affected_mob << "You feel like [pick("nothing's worth it anymore", "nobody ever needed your help", "nothing you did mattered", "everything you tried to do was worthless")]."
				affected_mob.adjustStaminaLoss(45)
				PoolOrNew(/obj/effect/overlay/temp/revenant, affected_mob.loc)
				if(affected_mob.dna && affected_mob.dna.species)
					affected_mob.dna.species.handle_mutant_bodyparts(affected_mob,"#1d2953")
					affected_mob.dna.species.handle_hair(affected_mob,"#1d2953")
					affected_mob.dna.species.update_color(affected_mob,"#1d2953")
					affected_mob.visible_message("[affected_mob] looks terrifyingly gaunt...", "You suddenly feel like your skin is wrong...")
					spawn(100)
						cure()
		else
			return

license: mit


code:
#define REVENANT_SPAWN_THRESHOLD 10

/datum/round_event_control/revenant
	name = "Spawn Revenant"
	typepath = /datum/round_event/revenant
	weight = 7
	max_occurrences = 1
	earliest_start = 6000 //Meant to mix things up early-game.
	min_players = 5

/datum/round_event/revenant
	var/key_of_revenant

/datum/round_event/revenant/proc/get_revenant(end_if_fail = 0)
	var/deadMobs = 0
	for(var/mob/M in dead_mob_list)
		deadMobs++
	if(deadMobs < REVENANT_SPAWN_THRESHOLD)
		message_admins("Random event attempted to spawn a revenant, but there were only [deadMobs]/[REVENANT_SPAWN_THRESHOLD] dead mobs.")
		return
	key_of_revenant = null
	if(!key_of_revenant)
		var/list/candidates = get_candidates(ROLE_REVENANT)
		if(!candidates.len)
			if(end_if_fail)
				return 0
			return find_revenant()
		var/client/C = pick(candidates)
		key_of_revenant = C.key
	if(!key_of_revenant)
		if(end_if_fail)
			return 0
		return find_revenant()
	var/datum/mind/player_mind = new /datum/mind(key_of_revenant)
	player_mind.active = 1
	var/list/spawn_locs = list()
	for(var/obj/effect/landmark/L in landmarks_list)
		if(isturf(L.loc))
			switch(L.name)
				if("revenantspawn")
					spawn_locs += L.loc
	if(!spawn_locs) //If we can't find any revenant spawns, try the carp spawns
		for(var/obj/effect/landmark/L in landmarks_list)
			if(isturf(L.loc))
				switch(L.name)
					if("carpspawn")
						spawn_locs += L.loc
	if(!spawn_locs) //If we can't find either, just spawn the revenant at the player's location
		spawn_locs += get_turf(player_mind.current)
	if(!spawn_locs) //If we can't find THAT, then just retry
		return find_revenant()
	var/mob/living/simple_animal/revenant/revvie = new /mob/living/simple_animal/revenant/(pick(spawn_locs))
	player_mind.transfer_to(revvie)
	player_mind.assigned_role = "revenant"
	player_mind.special_role = "Revenant"
	ticker.mode.traitors |= player_mind
	message_admins("[key_of_revenant] has been made into a revenant by an event.")
	log_game("[key_of_revenant] was spawned as a revenant by an event.")
	return 1

/datum/round_event/revenant/start()
	get_revenant()

/datum/round_event/revenant/proc/find_revenant()
	message_admins("An event failed to spawn a revenant. Retrying momentarily...")
	spawn(50)
		if(get_revenant(1))
			message_admins("[key_of_revenant] has been spawned as a revenant.")
			log_game("[key_of_revenant] was spawned as a revenant by an event.")
			return 0
		message_admins("No candidates were available for becoming a revenant.")
	return kill()


code:
//////////////////The Monster

/mob/living/simple_animal/slaughter
	name = "slaughter demon"
	real_name = "slaughter demon"
	desc = "A large, menacing creature covered in armored black scales."
	speak_emote = list("gurgles")
	emote_hear = list("wails","screeches")
	response_help  = "thinks better of touching"
	response_disarm = "flails at"
	response_harm   = "punches"
	icon = 'icons/mob/mob.dmi'
	icon_state = "daemon"
	icon_living = "daemon"
	speed = 1
	a_intent = "harm"
	stop_automated_movement = 1
	status_flags = CANPUSH
	attack_sound = 'sound/magic/demon_attack1.ogg'
	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
	faction = list("slaughter")
	attacktext = "wildly tears into"
	maxHealth = 200
	health = 200
	healable = 0
	environment_smash = 1
	melee_damage_lower = 30
	melee_damage_upper = 30
	see_in_dark = 8
	var/boost = 0
	bloodcrawl = BLOODCRAWL_EAT
	see_invisible = SEE_INVISIBLE_MINIMUM
	var/list/consumed_mobs = list()
	var/playstyle_string = "You are a slaughter demon, a terrible creature from another realm. You have a single desire: To kill.  \
							You may use the \"Blood Crawl\" ability near blood pools to travel through them, appearing and dissaapearing from the station at will. \
							Pulling a dead or unconscious mob while you enter a pool will pull them in with you, allowing you to feast and regain your health. \
							You move quickly upon leaving a pool of blood, but the material world will soon sap your strength and leave you sluggish. "

/mob/living/simple_animal/slaughter/New()
	..()
	var/obj/effect/proc_holder/spell/bloodcrawl/bloodspell = new
	AddSpell(bloodspell)
	if(istype(loc, /obj/effect/dummy/slaughter))
		bloodspell.phased = 1

/mob/living/simple_animal/slaughter/Life()
	..()
	if(boost<world.time)
		speed = 1
	else
		speed = 0

/mob/living/simple_animal/slaughter/death()
	..(1)
	new /obj/effect/decal/cleanable/blood (get_turf(src))
	var/obj/effect/decal/cleanable/blood/innards = new (get_turf(src))
	innards.icon = 'icons/obj/surgery.dmi'
	innards.icon_state = "innards"
	innards.name = "pile of viscera"
	innards.desc = "A repulsive pile of guts and gore."
	new /obj/item/organ/internal/heart/demon (src.loc)
	playsound(get_turf(src),'sound/magic/demon_dies.ogg', 200, 1)
	visible_message("[src] screams in anger as it collapses into a puddle of viscera, its most recent meals spilling out of it.")
	for(var/mob/living/M in consumed_mobs)
		M.loc = get_turf(src)
	ghostize()
	qdel(src)
	return

/mob/living/simple_animal/slaughter/phasein()
	. = ..()
	speed = 0
	boost = world.time + 60

//The loot from killing a slaughter demon - can be consumed to allow the user to blood crawl
/obj/item/organ/internal/heart/demon
	name = "demon heart"
	desc = "Still it beats furiously, emanating an aura of utter hate."
	icon = 'icons/obj/surgery.dmi'
	icon_state = "demon_heart"
	origin_tech = "combat=5;biotech=8"

/obj/item/organ/internal/heart/demon/update_icon()
	return //always beating visually

/obj/item/organ/internal/heart/demon/attack(mob/M, mob/living/carbon/user, obj/target)
	if(M != user)
		return ..()
	user.visible_message("[user] raises [src] to their mouth and tears into it with their teeth!", \
						 "An unnatural hunger consumes you. You raise [src] your mouth and devour it!")
	playsound(user, 'sound/magic/Demon_consume.ogg', 50, 1)
	for(var/obj/effect/proc_holder/spell/knownspell in user.mind.spell_list)
		if(knownspell.type == /obj/effect/proc_holder/spell/bloodcrawl)
			user <<"...and you don't feel any different."
			qdel(src)
			return
	user.visible_message("[user]'s eyes flare a deep crimson!", \
						 "You feel a strange power seep into your body... you have absorbed the demon's blood-travelling powers!")
	user.drop_item()
	src.Insert(user) //Consuming the heart literally replaces your heart with a demon heart. H A R D C O R E

/obj/item/organ/internal/heart/demon/Insert(mob/living/carbon/M, special = 0)
	..()
	if(M.mind)
		M.mind.AddSpell(new /obj/effect/proc_holder/spell/bloodcrawl(null))

/obj/item/organ/internal/heart/demon/Remove(mob/living/carbon/M, special = 0)
	..()
	if(M.mind)
		M.mind.RemoveSpell(/obj/effect/proc_holder/spell/bloodcrawl)

/obj/item/organ/internal/heart/demon/Stop()
	return 0 // Always beating.


code:
/datum/round_event_control/slaughter
	name = "Spawn Slaughter Demon"
	typepath = /datum/round_event/slaughter
	weight = 1 //Very rare
	max_occurrences = 1
	earliest_start = 36000 //1 hour
	min_players = 20

/datum/round_event/slaughter
	var/key_of_slaughter

/datum/round_event/slaughter/proc/get_slaughter(end_if_fail = 0)
	key_of_slaughter = null
	if(!key_of_slaughter)
		var/list/candidates = get_candidates(ROLE_ALIEN)
		if(!candidates.len)
			if(end_if_fail)
				return 0
			return find_slaughter()
		var/client/C = pick(candidates)
		key_of_slaughter = C.key
	if(!key_of_slaughter)
		if(end_if_fail)
			return 0
		return find_slaughter()
	var/datum/mind/player_mind = new /datum/mind(key_of_slaughter)
	player_mind.active = 1
	var/list/spawn_locs = list()
	for(var/obj/effect/landmark/L in landmarks_list)
		if(isturf(L.loc))
			switch(L.name)
				if("carpspawn")
					spawn_locs += L.loc
	if(!spawn_locs)
		return find_slaughter()
	var /obj/effect/dummy/slaughter/holder = PoolOrNew(/obj/effect/dummy/slaughter,(pick(spawn_locs)))
	var/mob/living/simple_animal/slaughter/S = new /mob/living/simple_animal/slaughter/(holder)
	S.holder = holder
	player_mind.transfer_to(S)
	player_mind.assigned_role = "Slaughter Demon"
	player_mind.special_role = "Slaughter Demon"
	ticker.mode.traitors |= player_mind
	S << S.playstyle_string
	S << "<B>You are currently not currently in the same plane of existence as the station. Blood Crawl near a blood pool to manifest.</B>"
	S << 'sound/magic/demon_dies.ogg'
	message_admins("[key_of_slaughter] has been made into a slaughter demon by an event.")
	log_game("[key_of_slaughter] was spawned as a slaughter demon by an event.")
	return 1

/datum/round_event/slaughter/start()
	get_slaughter()

/datum/round_event/slaughter/proc/find_slaughter()
	message_admins("Attempted to spawn a slaughter demon but there was no players available. Will try again momentarily.")
	spawn(50)
		if(get_slaughter(1))
			message_admins("Situation has been resolved, [key_of_slaughter] has been spawned as a slaughter demon.")
			log_game("[key_of_slaughter] was spawned as a slaughter demon by an event.")
			return 0
		message_admins("Unfortunately, no candidates were available for becoming a slaughter demon. Shutting down.")
	return kill()


code:
/datum/game_mode
	var/list/datum/mind/syndicates = list()
	var/nukeops_lastname = ""

/datum/game_mode/nuclear
	name = "nuclear emergency"
	config_tag = "nuclear"
	required_players = 30 // 30 players - 5 players to be the nuke ops = 25 players remaining
	required_enemies = 5
	recommended_enemies = 5
	antag_flag = ROLE_OPERATIVE
	enemy_minimum_age = 14
	var/const/agents_possible = 5 //If we ever need more syndicate agents.

	var/nukes_left = 1 // Call 3714-PRAY right now and order more nukes! Limited offer!
	var/nuke_off_station = 0 //Used for tracking if the syndies actually haul the nuke to the station
	var/syndies_didnt_escape = 0 //Used for tracking if the syndies got the shuttle off of the z-level

/datum/game_mode/nuclear/announce()
	world << "The current game mode is - Nuclear Emergency!"
	world << "A [syndicate_name()] Strike Force is approaching [station_name()]!"
	world << "A nuclear explosive was being transported by Nanotrasen to a military base. The transport ship mysteriously lost contact with Space Traffic Control (STC). About that time a strange disk was discovered around [station_name()]. It was identified by Nanotrasen as a nuclear auth. disk and now Syndicate Operatives have arrived to retake the disk and detonate SS13! Also, most likely Syndicate star ships are in the vicinity so take care not to lose the disk!\nSyndicate: Reclaim the disk and detonate the nuclear bomb anywhere on SS13.\nPersonnel: Hold the disk and escape with the disk on the shuttle!"

/datum/game_mode/nuclear/pre_setup()
	var/agent_number = 0
	if(antag_candidates.len > agents_possible)
		agent_number = agents_possible
	else
		agent_number = antag_candidates.len

	var/n_players = num_players()
	if(agent_number > n_players)
		agent_number = n_players/2

	while(agent_number > 0)
		var/datum/mind/new_syndicate = pick(antag_candidates)
		syndicates += new_syndicate
		antag_candidates -= new_syndicate //So it doesn't pick the same guy each time.
		agent_number--

	for(var/datum/mind/synd_mind in syndicates)
		synd_mind.assigned_role = "Syndicate"
		synd_mind.special_role = "Syndicate"//So they actually have a special role/N
		log_game("[synd_mind.key] (ckey) has been selected as a nuclear operative")

	return 1

////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////
/datum/game_mode/proc/update_synd_icons_added(datum/mind/synd_mind)
	var/datum/atom_hud/antag/opshud = huds[ANTAG_HUD_OPS]
	opshud.join_hud(synd_mind.current)
	set_antag_hud(synd_mind.current, "synd")

/datum/game_mode/proc/update_synd_icons_removed(datum/mind/synd_mind)
	var/datum/atom_hud/antag/opshud = huds[ANTAG_HUD_OPS]
	opshud.leave_hud(synd_mind.current)
	set_antag_hud(synd_mind.current, null)

////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////

/datum/game_mode/nuclear/post_setup()

	var/list/turf/synd_spawn = list()

	for(var/obj/effect/landmark/A in landmarks_list)
		if(A.name == "Syndicate-Spawn")
			synd_spawn += get_turf(A)
			continue

	var/nuke_code = "[rand(10000, 99999)]"
	var/leader_selected = 0
	var/agent_number = 1
	var/spawnpos = 1

	for(var/datum/mind/synd_mind in syndicates)
		if(spawnpos > synd_spawn.len)
			spawnpos = 2
		synd_mind.current.loc = synd_spawn[spawnpos]

		forge_syndicate_objectives(synd_mind)
		greet_syndicate(synd_mind)
		equip_syndicate(synd_mind.current)

		if (nuke_code)
			synd_mind.store_memory("Syndicate Nuclear Bomb Code: [nuke_code]", 0, 0)
			synd_mind.current << "The nuclear authorization code is: [nuke_code]"

		if(!leader_selected)
			prepare_syndicate_leader(synd_mind, nuke_code)
			leader_selected = 1
		else
			synd_mind.current.real_name = "[syndicate_name()] Operative #[agent_number]"
			agent_number++
		spawnpos++
		update_synd_icons_added(synd_mind)
	var/obj/machinery/nuclearbomb/nuke = locate("syndienuke") in nuke_list
	if(nuke)
		nuke.r_code = nuke_code
	return ..()

/datum/game_mode/proc/prepare_syndicate_leader(datum/mind/synd_mind, nuke_code)
	var/leader_title = pick("Czar", "Boss", "Commander", "Chief", "Kingpin", "Director", "Overlord")
	spawn(1)
		nukeops_lastname = nukelastname(synd_mind.current)
		NukeNameAssign(nukeops_lastname,syndicates) //allows time for the rest of the syndies to be chosen
	synd_mind.current.real_name = "[syndicate_name()] [leader_title]"
	synd_mind.current << "You are the Syndicate [leader_title] for this mission. You are responsible for the distribution of telecrystals and your ID is the only one who can open the launch bay doors."
	synd_mind.current << "If you feel you are not up to this task, give your ID to another operative."
	synd_mind.current << "In your hand you will find a special item capable of triggering a greater challenge for your team. Examine it carefully and consult with your fellow operatives before activating it."

	var/obj/item/device/nuclear_challenge/challenge = new /obj/item/device/nuclear_challenge
	synd_mind.current.equip_to_slot_or_del(challenge, slot_r_hand)

	var/list/foundIDs = synd_mind.current.search_contents_for(/obj/item/weapon/card/id)
	if(foundIDs.len)
		for(var/obj/item/weapon/card/id/ID in foundIDs)
			ID.name = "lead agent card"
			ID.access += access_syndicate_leader
	else
		message_admins("Warning: Nuke Ops spawned without access to leave their spawn area!")

	var/obj/item/device/radio/headset/syndicate/alt/A = locate() in synd_mind.current
	if(A)
		A.command = TRUE

	if (nuke_code)
		var/obj/item/weapon/paper/P = new
		P.info = "The nuclear authorization code is: [nuke_code]"
		P.name = "nuclear bomb code"
		var/mob/living/carbon/human/H = synd_mind.current
		P.loc = H.loc
		H.equip_to_slot_or_del(P, slot_r_hand, 0)
		H.update_icons()
	else
		nuke_code = "code will be provided later"
	return

/datum/game_mode/proc/forge_syndicate_objectives(datum/mind/syndicate)
	var/datum/objective/nuclear/syndobj = new
	syndobj.owner = syndicate
	syndicate.objectives += syndobj

/datum/game_mode/proc/greet_syndicate(datum/mind/syndicate, you_are=1)
	if (you_are)
		syndicate.current << "You are a [syndicate_name()] agent!"
	var/obj_count = 1
	for(var/datum/objective/objective in syndicate.objectives)
		syndicate.current << "Objective #[obj_count]: [objective.explanation_text]"
		obj_count++
	return

/datum/game_mode/proc/equip_syndicate(mob/living/carbon/human/synd_mob, telecrystals = TRUE)
	synd_mob.set_species(/datum/species/human) //Plasamen burn up otherwise, and lizards are vulnerable to asimov AIs

	if(telecrystals)
		synd_mob.equipOutfit(/datum/outfit/syndicate)
	else
		synd_mob.equipOutfit(/datum/outfit/syndicate/no_crystals)
	return 1

/datum/game_mode/nuclear/check_win()
	if (nukes_left == 0)
		return 1
	return ..()

/datum/game_mode/proc/are_operatives_dead()
	for(var/datum/mind/operative_mind in syndicates)
		if (istype(operative_mind.current,/mob/living/carbon/human) && (operative_mind.current.stat!=2))
			return 0
	return 1

/datum/game_mode/nuclear/check_finished() //to be called by ticker
	if(replacementmode && round_converted == 2)
		return replacementmode.check_finished()
	if(SSshuttle.emergency.mode >= SHUTTLE_ENDGAME || station_was_nuked)
		return 1
	if(are_operatives_dead())
		if(bomb_set) //snaaaaaaaaaake! It's not over yet!
			return 0
	..()

/datum/game_mode/nuclear/declare_completion()
	var/disk_rescued = 1
	for(var/obj/item/weapon/disk/nuclear/D in poi_list)
		if(!D.onCentcom())
			disk_rescued = 0
			break
	var/crew_evacuated = (SSshuttle.emergency.mode >= SHUTTLE_ENDGAME)
	//var/operatives_are_dead = is_operatives_are_dead()

	//nukes_left
	//station_was_nuked
	//derp //Used for tracking if the syndies actually haul the nuke to the station	//no
	//herp //Used for tracking if the syndies got the shuttle off of the z-level	//NO, DON'T FUCKING NAME VARS LIKE THIS

	if      (!disk_rescued &&  station_was_nuked && !syndies_didnt_escape)
		feedback_set_details("round_end_result","win - syndicate nuke")
		world << "Syndicate Major Victory!"
		world << "[syndicate_name()] operatives have destroyed [station_name()]!"

	else if (!disk_rescued &&  station_was_nuked && syndies_didnt_escape)
		feedback_set_details("round_end_result","halfwin - syndicate nuke - did not evacuate in time")
		world << "Total Annihilation"
		world << "[syndicate_name()] operatives destroyed [station_name()] but did not leave the area in time and got caught in the explosion. Next time, don't lose the disk!"

	else if (!disk_rescued && !station_was_nuked && nuke_off_station && !syndies_didnt_escape)
		feedback_set_details("round_end_result","halfwin - blew wrong station")
		world << "Crew Minor Victory"
		world << "[syndicate_name()] operatives secured the authentication disk but blew up something that wasn't [station_name()]. Next time, don't lose the disk!"

	else if (!disk_rescued && !station_was_nuked && nuke_off_station && syndies_didnt_escape)
		feedback_set_details("round_end_result","halfwin - blew wrong station - did not evacuate in time")
		world << "[syndicate_name()] operatives have earned Darwin Award!"
		world << "[syndicate_name()] operatives blew up something that wasn't [station_name()] and got caught in the explosion. Next time, don't lose the disk!"

	else if ((disk_rescued || SSshuttle.emergency.mode < SHUTTLE_ENDGAME) && are_operatives_dead())
		feedback_set_details("round_end_result","loss - evacuation - disk secured - syndi team dead")
		world << "Crew Major Victory!"
		world << "The Research Staff has saved the disc and killed the [syndicate_name()] Operatives"

	else if (disk_rescued)
		feedback_set_details("round_end_result","loss - evacuation - disk secured")
		world << "Crew Major Victory"
		world << "The Research Staff has saved the disc and stopped the [syndicate_name()] Operatives!"

	else if (!disk_rescued && are_operatives_dead())
		feedback_set_details("round_end_result","loss - evacuation - disk not secured")
		world << "Syndicate Minor Victory!"
		world << "The Research Staff failed to secure the authentication disk but did manage to kill most of the [syndicate_name()] Operatives!"

	else if (!disk_rescued &&  crew_evacuated)
		feedback_set_details("round_end_result","halfwin - detonation averted")
		world << "Syndicate Minor Victory!"
		world << "[syndicate_name()] operatives recovered the abandoned authentication disk but detonation of [station_name()] was averted. Next time, don't lose the disk!"

	else if (!disk_rescued && !crew_evacuated)
		feedback_set_details("round_end_result","halfwin - interrupted")
		world << "Neutral Victory"
		world << "Round was mysteriously interrupted!"

	..()
	return

/datum/game_mode/proc/auto_declare_completion_nuclear()
	if( syndicates.len || (ticker && istype(ticker.mode,/datum/game_mode/nuclear)) )
		var/text = "
The syndicate operatives were:"
		var/purchases = ""
		var/TC_uses = 0
		for(var/datum/mind/syndicate in syndicates)
			text += printplayer(syndicate)
			for(var/obj/item/device/uplink/H in uplinks)
				if(H && H.owner && H.owner == syndicate.key)
					TC_uses += H.spent_telecrystals
					purchases += H.purchase_log
		text += "
"
		text += "(Syndicates used [TC_uses] TC) [purchases]"
		if(TC_uses == 0 && station_was_nuked && !are_operatives_dead())
			text += "<BIG></BIG>"
		world << text
	return 1

/proc/nukelastname(mob/M) //--All praise goes to NEO|Phyte, all blame goes to DH, and it was Cindi-Kate's idea. Also praise Urist for copypasta ho.
	var/randomname = pick(last_names)
	var/newname = copytext(sanitize(input(M,"You are the nuke operative [pick("Czar", "Boss", "Commander", "Chief", "Kingpin", "Director", "Overlord")]. Please choose a last name for your family.", "Name change",randomname)),1,MAX_NAME_LEN)

	if (!newname)
		newname = randomname

	else
		if (newname == "Unknown" || newname == "floor" || newname == "wall" || newname == "rwall" || newname == "_")
			M << "That name is reserved."
			return nukelastname(M)

	return capitalize(newname)

/proc/NukeNameAssign(lastname,list/syndicates)
	for(var/datum/mind/synd_mind in syndicates)
		var/mob/living/carbon/human/H = synd_mind.current
		synd_mind.name = H.dna.species.random_name(H.gender,0,lastname)
		synd_mind.current.real_name = synd_mind.name
	return

/datum/outfit/syndicate
	name = "Syndicate Operative - Basic"

	uniform = /obj/item/clothing/under/syndicate
	shoes = /obj/item/clothing/shoes/combat
	gloves = /obj/item/clothing/gloves/combat
	back = /obj/item/weapon/storage/backpack
	ears = /obj/item/device/radio/headset/syndicate/alt
	l_pocket = /obj/item/weapon/pinpointer/nukeop
	id = /obj/item/weapon/card/id/syndicate
	belt = /obj/item/weapon/gun/projectile/automatic/pistol
	backpack_contents = list(/obj/item/weapon/storage/box/engineer=1)

	var/tc = 25

/datum/outfit/syndicate/no_crystals
	tc = 0

/datum/outfit/syndicate/post_equip(mob/living/carbon/human/H)
	var/obj/item/device/radio/R = H.ears
	R.set_frequency(SYND_FREQ)
	R.freqlock = 1

	if(tc)
		var/obj/item/device/radio/uplink/nuclear/U = new(H)
		U.hidden_uplink.owner = "[H.key]"
		U.hidden_uplink.telecrystals = tc
		H.equip_to_slot_or_del(U, slot_in_backpack)

	var/obj/item/weapon/implant/weapons_auth/W = new/obj/item/weapon/implant/weapons_auth(H)
	W.implant(H)
	var/obj/item/weapon/implant/explosive/E = new/obj/item/weapon/implant/explosive(H)
	E.implant(H)
	H.faction |= "syndicate"
	H.update_icons()

/datum/outfit/syndicate/full
	name = "Syndicate Operative - Full Kit"

	glasses = /obj/item/clothing/glasses/night
	mask = /obj/item/clothing/mask/gas/syndicate
	suit = /obj/item/clothing/suit/space/hardsuit/syndi
	r_pocket = /obj/item/weapon/tank/internals/emergency_oxygen/engi
	belt = /obj/item/weapon/storage/belt/military
	r_hand = /obj/item/weapon/gun/projectile/automatic/shotgun/bulldog
	backpack_contents = list(/obj/item/weapon/storage/box/engineer=1,\
		/obj/item/weapon/tank/jetpack/oxygen/harness=1,\
		/obj/item/weapon/gun/projectile/automatic/pistol=1)

/datum/outfit/syndicate/full/post_equip(mob/living/carbon/human/H)
	..()

	var/obj/item/clothing/suit/space/hardsuit/syndi/suit = H.wear_suit
	suit.ToggleHelmet()
	var/obj/item/clothing/head/helmet/space/hardsuit/syndi/helmet = H.head
	helmet.attack_self(H)

	H.internal = H.r_store


code:
#define CHALLENGE_TELECRYSTALS 280
#define CHALLENGE_TIME_LIMIT 3000
#define CHALLENGE_MIN_PLAYERS 50
#define CHALLENGE_SHUTTLE_DELAY 15000 // 25 minutes, so the ops have at least 5 minutes before the shuttle is callable.

/obj/item/device/nuclear_challenge
	name = "Declaration of War (Challenge Mode)"
	icon_state = "gangtool-red"
	item_state = "walkietalkie"
	desc = "Use to send a declaration of hostilities to the target, delaying your shuttle departure for 20 minutes while they prepare for your assault.  \
			Such a brazen move will attract the attention of powerful benefactors within the Syndicate, who will supply your team with a massive amount of bonus telecrystals.  \
			Must be used within five minutes, or your benefactors will lose interest."
	var/declaring_war = 0

/obj/item/device/nuclear_challenge/attack_self(mob/living/user)
	if(declaring_war)
		return
	if(player_list.len < CHALLENGE_MIN_PLAYERS)
		user << "The enemy crew is too small to be worth declaring war on."
		return
	if(user.z != ZLEVEL_CENTCOM)
		user << "You have to be at your base to use this."
		return

	if(world.time > CHALLENGE_TIME_LIMIT)
		user << "It's too late to declare hostilities. Your benefactors are already busy with other schemes. You'll have to make  do with what you have on hand."
		return

	declaring_war = 1
	var/are_you_sure = alert(user, "Consult your team carefully before you declare war on [station_name()]]. Are you sure you want to alert the enemy crew?", "Declare war?", "Yes", "No")
	if(are_you_sure == "No")
		user << "On second thought, the element of surprise isn't so bad after all."
		declaring_war = 0
		return

	var/war_declaration = "[user.real_name] has declared his intent to utterly destroy [station_name()] with a nuclear device, and dares the crew to try and stop them."
	priority_announce(war_declaration, title = "Declaration of War", sound = 'sound/machines/Alarm.ogg')
	user << "You've attracted the attention of powerful forces within the syndicate. A bonus bundle of telecrystals has been granted to your team. Great things await you if you complete the mission."

	for(var/obj/machinery/computer/shuttle/syndicate/S in machines)
		S.challenge = TRUE

	var/obj/item/device/radio/uplink/nuclear/U = new(get_turf(user))
	U.hidden_uplink.owner = "[user.key]"
	U.hidden_uplink.telecrystals = CHALLENGE_TELECRYSTALS
	U.hidden_uplink.gamemode = /datum/game_mode/nuclear
	config.shuttle_refuel_delay = max(config.shuttle_refuel_delay, CHALLENGE_SHUTTLE_DELAY)
	qdel(src)

#undef CHALLENGE_TELECRYSTALS
#undef CHALLENGE_TIME_LIMIT
#undef CHALLENGE_MIN_PLAYERS
#undef CHALLENGE_SHUTTLE_DELAY


code:
#define NUKESTATE_INTACT		5
#define NUKESTATE_UNSCREWED		4
#define NUKESTATE_PANEL_REMOVED		3
#define NUKESTATE_WELDED		2
#define NUKESTATE_CORE_EXPOSED	1
#define NUKESTATE_CORE_REMOVED	0

#define NUKE_OFF_LOCKED		0
#define NUKE_OFF_UNLOCKED	1
#define NUKE_ON_TIMING		2
#define NUKE_ON_EXPLODING	3

var/bomb_set

/obj/machinery/nuclearbomb
	name = "nuclear fission explosive"
	desc = "You probably shouldn't stick around to see if this is armed."
	icon = 'icons/obj/machines/nuke.dmi'
	icon_state = "nuclearbomb_base"
	density = 1

	var/timeleft = 60
	var/timing = 0
	var/r_code = "ADMIN"
	var/code = ""
	var/yes_code = 0
	var/safety = 1
	var/obj/item/weapon/disk/nuclear/auth = null
	use_power = 0
	var/previous_level = ""
	var/lastentered = ""
	var/obj/item/nuke_core/core = null
	var/deconstruction_state = NUKESTATE_INTACT
	var/image/lights = null
	var/image/interior = null

/obj/machinery/nuclearbomb/New()
	..()
	nuke_list += src
	core = new /obj/item/nuke_core(src)
	SSobj.processing -= core
	update_icon()
	previous_level = get_security_level()

/obj/machinery/nuclearbomb/selfdestruct
	name = "station self-destruct terminal"
	desc = "For when it all gets too much to bear. Do not taunt."
	icon = 'icons/obj/machines/nuke_terminal.dmi'
	icon_state = "nuclearbomb_base"
	anchored = 1 //stops it being moved
	layer = 4

/obj/machinery/nuclearbomb/syndicate

/obj/machinery/nuclearbomb/syndicate/New()
	var/obj/machinery/nuclearbomb/existing = locate("syndienuke")
	if(existing)
		qdel(src)
		throw EXCEPTION("Attempted to spawn a syndicate nuke while one already exists at [existing.loc.x],[existing.loc.y],[existing.loc.z]")
		return 0
	tag = "syndienuke"
	return ..()

/obj/machinery/nuclearbomb/attackby(obj/item/I, mob/user, params)
	if (istype(I, /obj/item/weapon/disk/nuclear))
		if(!user.drop_item())
			return
		I.loc = src
		auth = I
		add_fingerprint(user)
		return

	switch(deconstruction_state)
		if(NUKESTATE_INTACT)
			if(istype(I, /obj/item/weapon/screwdriver/nuke))
				playsound(loc, 'sound/items/Screwdriver.ogg', 100, 1)
				user << "You start removing [src]'s front panel's screws..."
				if(do_after(user, 60/I.toolspeed,target=src))
					deconstruction_state = NUKESTATE_UNSCREWED
					user << "You remove the screws from [src]'s front panel."
					update_icon()
				return
		if(NUKESTATE_UNSCREWED)
			if(istype(I, /obj/item/weapon/crowbar))
				user << "You start removing [src]'s front panel..."
				playsound(loc, 'sound/items/Crowbar.ogg', 100, 1)
				if(do_after(user,30/I.toolspeed,target=src))
					user << "You remove [src]'s front panel."
					deconstruction_state = NUKESTATE_PANEL_REMOVED
					update_icon()
				return
		if(NUKESTATE_PANEL_REMOVED)
			if(istype(I, /obj/item/weapon/weldingtool))
				var/obj/item/weapon/weldingtool/welder = I
				playsound(loc, 'sound/items/Welder.ogg', 100, 1)
				user << "You start cutting [src]'s inner plate..."
				if(welder.remove_fuel(1,user))
					if(do_after(user,80/I.toolspeed,target=src))
						user << "You cut [src]'s inner plate."
						deconstruction_state = NUKESTATE_WELDED
						update_icon()
				return
		if(NUKESTATE_WELDED)
			if(istype(I, /obj/item/weapon/crowbar))
				user << "You start prying off [src]'s inner plate..."
				playsound(loc, 'sound/items/Crowbar.ogg', 100, 1)
				if(do_after(user,50/I.toolspeed,target=src))
					user << "You pry off [src]'s inner plate. You can see the core's green glow!"
					deconstruction_state = NUKESTATE_CORE_EXPOSED
					update_icon()
					SSobj.processing += core
		if(NUKESTATE_CORE_EXPOSED)
			if(istype(I, /obj/item/nuke_core_container))
				var/obj/item/nuke_core_container/core_box = I
				user << "You start loading the plutonium core into [core_box]..."
				if(do_after(user,50,target=src))
					if(core_box.load(core, user))
						user << "You load the plutonium core into [core_box]."
						deconstruction_state = NUKESTATE_CORE_REMOVED
						update_icon()
						core = null
					else
						user << "You fail to load the plutonium core into [core_box]. [core_box] has already been used!"
				return
			if(istype(I, /obj/item/stack/sheet/metal))
				var/obj/item/stack/sheet/metal/M = I
				if(M.amount >= 20)
					user << "You begin repairing [src]'s inner metal plate..."
					if(do_after(user, 100, target=src))
						if(M.use(20))
							user << "You repair [src]'s inner metal plate. The radiation is contained."
							deconstruction_state = NUKESTATE_PANEL_REMOVED
							SSobj.processing -= core
							update_icon()
						else
							user << "You need more metal to do that!"
				else
					user << "You need more metal to do that!"
				return
		else
			..()

/obj/machinery/nuclearbomb/proc/get_nuke_state()
	if(timing < 0)
		return NUKE_ON_EXPLODING
	if(timing > 0)
		return NUKE_ON_TIMING
	if(safety)
		return NUKE_OFF_LOCKED
	else
		return NUKE_OFF_UNLOCKED

/obj/machinery/nuclearbomb/update_icon()
	if(deconstruction_state == NUKESTATE_INTACT)
		switch(get_nuke_state())
			if(NUKE_OFF_LOCKED, NUKE_OFF_UNLOCKED)
				icon_state = "nuclearbomb_base"
				update_icon_interior()
				update_icon_lights()
			if(NUKE_ON_TIMING)
				overlays.Cut()
				icon_state = "nuclearbomb_timing"
			if(NUKE_ON_EXPLODING)
				overlays.Cut()
				icon_state = "nuclearbomb_exploding"
	else
		icon_state = "nuclearbomb_base"
		update_icon_interior()
		update_icon_lights()

/obj/machinery/nuclearbomb/proc/update_icon_interior()
	overlays -= interior
	switch(deconstruction_state)
		if(NUKESTATE_UNSCREWED)
			interior = image(icon,"panel-unscrewed")
		if(NUKESTATE_PANEL_REMOVED)
			interior = image(icon,"panel-removed")
		if(NUKESTATE_WELDED)
			interior = image(icon,"plate-welded")
		if(NUKESTATE_CORE_EXPOSED)
			interior = image(icon,"plate-removed")
		if(NUKESTATE_CORE_REMOVED)
			interior = image(icon,"core-removed")
		if(NUKESTATE_INTACT)
			interior = null
	overlays += interior

/obj/machinery/nuclearbomb/proc/update_icon_lights()
	overlays -= lights
	switch(get_nuke_state())
		if(NUKE_OFF_LOCKED)
			lights = null
		if(NUKE_OFF_UNLOCKED)
			lights = image(icon,"lights-safety")
		if(NUKE_ON_TIMING)
			lights = image(icon,"lights-timing")
		if(NUKE_ON_EXPLODING)
			lights = image(icon,"lights-exploding")
	overlays += lights

/obj/machinery/nuclearbomb/process()
	if (timing > 0)
		bomb_set = 1 //So long as there is one nuke timing, it means one nuke is armed.
		timeleft--
		if (timeleft <= 0)
			explode()
		else
			var/volume = (timeleft <= 20 ? 30 : 5)
			playsound(loc, 'sound/items/timer.ogg', volume, 0)
		for(var/mob/M in viewers(1, src))
			if ((M.client && M.machine == src))
				attack_hand(M)
	return

/obj/machinery/nuclearbomb/attack_paw(mob/user)
	return attack_hand(user)

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

/obj/machinery/nuclearbomb/attack_hand(mob/user)
	user.set_machine(src)
	var/dat = text("<TT>\nAuth. Disk: []
", src, (auth ? "++++++++++" : "----------"))
	if (auth)
		if (yes_code)
			dat += text("\nStatus: []-[]
\nTimer: []
\n
\nTimer: [] Toggle
\nTime: - - [] + +
\n
\nSafety: [] Toggle
\nAnchor: [] Toggle
\n", (timing ? "Func/Set" : "Functional"), (safety ? "Safe" : "Engaged"), timeleft, (timing ? "On" : "Off"), src, src, src, timeleft, src, src, (safety ? "On" : "Off"), src, (anchored ? "Engaged" : "Off"), src)
		else
			dat += text("\nStatus: Auth. S2-[]
\nTimer: []
\n
\nTimer: [] Toggle
\nTime: - - [] + +
\n
\n[] Safety: Toggle
\nAnchor: [] Toggle
\n", (safety ? "Safe" : "Engaged"), timeleft, (timing ? "On" : "Off"), timeleft, (safety ? "On" : "Off"), (anchored ? "Engaged" : "Off"))
	else
		if (timing)
			dat += text("\nStatus: Set-[]
\nTimer: []
\n
\nTimer: [] Toggle
\nTime: - - [] + +
\n
\nSafety: [] Toggle
\nAnchor: [] Toggle
\n", (safety ? "Safe" : "Engaged"), timeleft, (timing ? "On" : "Off"), timeleft, (safety ? "On" : "Off"), (anchored ? "Engaged" : "Off"))
		else
			dat += text("\nStatus: Auth. S1-[]
\nTimer: []
\n
\nTimer: [] Toggle
\nTime: - - [] + +
\n
\nSafety: [] Toggle
\nAnchor: [] Toggle
\n", (safety ? "Safe" : "Engaged"), timeleft, (timing ? "On" : "Off"), timeleft, (safety ? "On" : "Off"), (anchored ? "Engaged" : "Off"))
	var/message = "AUTH"
	if (auth)
		message = text("[]", code)
		if (yes_code)
			message = "*****"
	dat += text("
\n>[]
\n123
\n456
\n789
\nR0E
\n</TT>", message, src, src, src, src, src, src, src, src, src, src, src, src)
	var/datum/browser/popup = new(user, "nuclearbomb", name, 300, 400)
	popup.set_content(dat)
	popup.open()
	return

/obj/machinery/nuclearbomb/Topic(href, href_list)
	if(..())
		return
	usr.set_machine(src)
	if (href_list["auth"])
		if (auth)
			auth.loc = loc
			yes_code = 0
			auth = null
		else
			var/obj/item/I = usr.get_active_hand()
			if (istype(I, /obj/item/weapon/disk/nuclear))
				usr.drop_item()
				I.loc = src
				auth = I
	if (auth)
		if (href_list["type"])
			if (href_list["type"] == "E")
				if (code == r_code)
					yes_code = 1
					code = null
				else
					code = "ERROR"
			else
				if (href_list["type"] == "R")
					yes_code = 0
					code = null
				else
					lastentered = text("[]", href_list["type"])
					if (text2num(lastentered) == null)
						var/turf/LOC = get_turf(usr)
						message_admins("[key_name_admin(usr)] (FLW) tried to exploit a nuclear bomb by entering non-numerical codes: [lastentered] ! ([LOC ? "JMP" : "null"])", 0)
						log_admin("EXPLOIT : [key_name(usr)] tried to exploit a nuclear bomb by entering non-numerical codes: [lastentered] !")
					else
						code += lastentered
						if (length(code) > 5)
							code = "ERROR"
		if (yes_code)
			if (href_list["time"])
				var/time = text2num(href_list["time"])
				timeleft += time
				timeleft = min(max(round(timeleft), 60), 600)
			if (href_list["timer"])
				set_active()
			if (href_list["safety"])
				set_safety()
			if (href_list["anchor"])
				set_anchor()
	add_fingerprint(usr)
	for(var/mob/M in viewers(1, src))
		if ((M.client && M.machine == src))
			attack_hand(M)

/obj/machinery/nuclearbomb/proc/set_anchor()
	if(!isinspace())
		anchored = !anchored
	else
		usr << "There is nothing to anchor to!"

/obj/machinery/nuclearbomb/proc/set_safety()
	safety = !safety
	if(safety)
		timing = 0
		bomb_set = 0
		set_security_level(previous_level)
	update_icon()

/obj/machinery/nuclearbomb/proc/set_active()
	if(safety)
		usr << "The safety is still on."
		return
	timing = !timing
	previous_level = get_security_level()
	if(timing)
		bomb_set = 1
		set_security_level("delta")
	else
		bomb_set = 0
		set_security_level(previous_level)
	update_icon()

/obj/machinery/nuclearbomb/ex_act(severity, target)
	return

/obj/machinery/nuclearbomb/blob_act()
	if (timing == -1)
		return
	else
		return ..()
	return

#define NUKERANGE 80
/obj/machinery/nuclearbomb/proc/explode()
	if (safety)
		timing = 0
		return

	timing = -1
	yes_code = 0
	safety = 1
	update_icon()
	for(var/mob/M in player_list)
		M << 'sound/machines/Alarm.ogg'
	if (ticker && ticker.mode)
		ticker.mode.explosion_in_progress = 1
	sleep(100)

	if(!core)
		ticker.station_explosion_cinematic(3,"no_core")
		ticker.mode.explosion_in_progress = 0
		return

	enter_allowed = 0

	var/off_station = 0
	var/turf/bomb_location = get_turf(src)
	if( bomb_location && (bomb_location.z == ZLEVEL_STATION) )
		if( (bomb_location.x < (128-NUKERANGE)) || (bomb_location.x > (128+NUKERANGE)) || (bomb_location.y < (128-NUKERANGE)) || (bomb_location.y > (128+NUKERANGE)) )
			off_station = 1
	else
		off_station = 2

	if(ticker.mode && ticker.mode.name == "nuclear emergency")
		var/obj/docking_port/mobile/Shuttle = SSshuttle.getShuttle("syndicate")
		ticker.mode:syndies_didnt_escape = (Shuttle && Shuttle.z == ZLEVEL_CENTCOM) ? 0 : 1
		ticker.mode:nuke_off_station = off_station
	ticker.station_explosion_cinematic(off_station,null)
	if(ticker.mode)
		ticker.mode.explosion_in_progress = 0
		if(ticker.mode.name == "nuclear emergency")
			ticker.mode:nukes_left --
		else
			world << "The station was destoyed by the nuclear blast!"
		ticker.mode.station_was_nuked = (off_station<2)	//offstation==1 is a draw. the station becomes irradiated and needs to be evacuated.
														//kinda shit but I couldn't  get permission to do what I wanted to do.
		if(!ticker.mode.check_finished())//If the mode does not deal with the nuke going off so just reboot because everyone is stuck as is
			spawn()
				world.Reboot("Station destroyed by Nuclear Device.", "end_error", "nuke - unhandled ending")
			return
	return

/*
This is here to make the tiles around the station mininuke change when it's armed.
*/

/obj/machinery/nuclearbomb/selfdestruct/proc/SetTurfs()
	if(loc == initial(loc))
		for(var/N in nuke_tiles)
			var/turf/open/floor/T = N
			T.icon_state = (timing ? "rcircuitanim" : T.icon_regular_floor)

/obj/machinery/nuclearbomb/selfdestruct/set_anchor()
	return

/obj/machinery/nuclearbomb/selfdestruct/set_active()
	..()
	SetTurfs()

/obj/machinery/nuclearbomb/selfdestruct/set_safety()
	..()
	SetTurfs()

//==========DAT FUKKEN DISK===============
/obj/item/weapon/disk
	icon = 'icons/obj/module.dmi'
	w_class = 1
	item_state = "card-id"
	icon_state = "datadisk0"

/obj/item/weapon/disk/nuclear
	name = "nuclear authentication disk"
	desc = "Better keep this safe."
	icon_state = "nucleardisk"

/obj/item/weapon/disk/nuclear/New()
	..()
	poi_list |= src
	SSobj.processing |= src

/obj/item/weapon/disk/nuclear/process()
	var/turf/disk_loc = get_turf(src)
	if(disk_loc.z > ZLEVEL_CENTCOM)
		get(src, /mob) << "You can't help but feel that you just lost something back there..."
		qdel(src)

/obj/item/weapon/disk/nuclear/Destroy()
	if(blobstart.len > 0)
		var/turf/targetturf = get_turf(pick(blobstart))
		var/turf/diskturf = get_turf(src)
		if(ismob(loc))
			var/mob/M = loc
			M.remove_from_mob(src)
		if(istype(loc, /obj/item/weapon/storage))
			var/obj/item/weapon/storage/S = loc
			S.remove_from_storage(src, diskturf)
		forceMove(targetturf) //move the disc, so ghosts remain orbitting it even if it's "destroyed"
		message_admins("[src] has been destroyed in ([diskturf.x], [diskturf.y] ,[diskturf.z] - JMP). Moving it to ([targetturf.x], [targetturf.y], [targetturf.z] - JMP).")
		log_game("[src] has been destroyed in ([diskturf.x], [diskturf.y] ,[diskturf.z]). Moving it to ([targetturf.x], [targetturf.y], [targetturf.z]).")
	else
		throw EXCEPTION("Unable to find a blobstart landmark")
	return QDEL_HINT_LETMELIVE //Cancel destruction regardless of success


code:
/obj/item/weapon/pinpointer
	name = "pinpointer"
	icon = 'icons/obj/device.dmi'
	icon_state = "pinoff"
	flags = CONDUCT
	slot_flags = SLOT_BELT
	w_class = 2
	item_state = "electronic"
	throw_speed = 3
	throw_range = 7
	materials = list(MAT_METAL=500)
	var/obj/item/weapon/disk/nuclear/the_disk = null
	var/active = 0

/obj/item/weapon/pinpointer/New()
	..()
	pinpointer_list += src

/obj/item/weapon/pinpointer/Destroy()
	active = 0
	pinpointer_list -= src
	return ..()

/obj/item/weapon/pinpointer/attack_self()
	if(!active)
		active = 1
		workdisk()
		usr << "You activate the pinpointer."
	else
		active = 0
		icon_state = "pinoff"
		usr << "You deactivate the pinpointer."

/obj/item/weapon/pinpointer/proc/scandisk()
	if(!the_disk)
		the_disk = locate()

/obj/item/weapon/pinpointer/proc/point_at(atom/target, spawnself = 1)
	if(!active)
		return
	if(!target)
		icon_state = "pinonnull"
		return

	var/turf/T = get_turf(target)
	var/turf/L = get_turf(src)

	if(T.z != L.z)
		icon_state = "pinonnull"
	else
		dir = get_dir(L, T)
		switch(get_dist(L, T))
			if(-1)
				icon_state = "pinondirect"
			if(1 to 8)
				icon_state = "pinonclose"
			if(9 to 16)
				icon_state = "pinonmedium"
			if(16 to INFINITY)
				icon_state = "pinonfar"
	if(spawnself)
		spawn(5)
			.()

/obj/item/weapon/pinpointer/proc/workdisk()
	scandisk()
	point_at(the_disk, 0)
	spawn(5)
		.()

/obj/item/weapon/pinpointer/examine(mob/user)
	..()
	for(var/obj/machinery/nuclearbomb/bomb in machines)
		if(bomb.timing)
			user << "Extreme danger.  Arming signal detected.   Time remaining: [bomb.timeleft]"

/obj/item/weapon/pinpointer/advpinpointer
	name = "advanced pinpointer"
	icon = 'icons/obj/device.dmi'
	desc = "A larger version of the normal pinpointer, this unit features a helpful quantum entanglement detection system to locate various objects that do not broadcast a locator signal."
	var/mode = 0  // Mode 0 locates disk, mode 1 locates coordinates.
	var/turf/location = null
	var/obj/target = null

/obj/item/weapon/pinpointer/advpinpointer/attack_self()
	if(!active)
		active = 1
		if(mode == 0)
			workdisk()
		if(mode == 1)
			point_at(location)
		if(mode == 2)
			point_at(target)
		usr << "You activate the pinpointer."
	else
		active = 0
		icon_state = "pinoff"
		usr << "You deactivate the pinpointer."

/obj/item/weapon/pinpointer/advpinpointer/verb/toggle_mode()
	set category = "Object"
	set name = "Toggle Pinpointer Mode"
	set src in view(1)

	if(usr.stat || usr.restrained() || !usr.canmove)
		return

	active = 0
	icon_state = "pinoff"
	target=null
	location = null

	switch(alert("Please select the mode you want to put the pinpointer in.", "Pinpointer Mode Select", "Location", "Disk Recovery", "Other Signature"))
		if("Location")
			mode = 1

			var/locationx = input(usr, "Please input the x coordinate to search for.", "Location?" , "") as num
			if(!locationx || !(usr in view(1,src)))
				return
			var/locationy = input(usr, "Please input the y coordinate to search for.", "Location?" , "") as num
			if(!locationy || !(usr in view(1,src)))
				return

			var/turf/Z = get_turf(src)

			location = locate(locationx,locationy,Z.z)

			usr << "You set the pinpointer to locate [locationx],[locationy]"

			return attack_self()

		if("Disk Recovery")
			mode = 0
			return attack_self()

		if("Other Signature")
			mode = 2
			switch(alert("Search for item signature or DNA fragment?" , "Signature Mode Select" , "" , "Item" , "DNA"))
				if("Item")
					var/targetitem = input("Select item to search for.", "Item Mode Select","") as null|anything in possible_items
					if(!targetitem)
						return
					target=locate(possible_items[targetitem])
					if(!target)
						usr << "Failed to locate [targetitem]!"
						return
					usr << "You set the pinpointer to locate [targetitem]."
				if("DNA")
					var/DNAstring = input("Input DNA string to search for." , "Please Enter String." , "")
					if(!DNAstring)
						return
					for(var/mob/living/carbon/C in mob_list)
						if(!C.dna)
							continue
						if(C.dna.unique_enzymes == DNAstring)
							target = C
							break

			return attack_self()

///////////////////////
//nuke op pinpointers//
///////////////////////

/obj/item/weapon/pinpointer/nukeop
	var/mode = 0	//Mode 0 locates disk, mode 1 locates the shuttle
	var/obj/docking_port/mobile/home

/obj/item/weapon/pinpointer/nukeop/attack_self(mob/user)
	if(!active)
		active = 1
		var/mode_text = "Authentication Disk Locator mode"
		if(!mode)
			workdisk()
		else
			mode_text = "Shuttle Locator mode"
			worklocation()
		user << "You activate the pinpointer([mode_text])."
	else
		active = 0
		icon_state = "pinoff"
		user << "You deactivate the pinpointer."

/obj/item/weapon/pinpointer/nukeop/workdisk()
	if(!active) return
	if(mode)		//Check in case the mode changes while operating
		worklocation()
		return
	if(bomb_set)	//If the bomb is set, lead to the shuttle
		mode = 1	//Ensures worklocation() continues to work
		worklocation()
		playsound(loc, 'sound/machines/twobeep.ogg', 50, 1)	//Plays a beep
		visible_message("Shuttle Locator mode actived.")			//Lets the mob holding it know that the mode has changed
		return		//Get outta here
	scandisk()
	if(!the_disk)
		icon_state = "pinonnull"
		return
	dir = get_dir(src, the_disk)
	switch(get_dist(src, the_disk))
		if(0)
			icon_state = "pinondirect"
		if(1 to 8)
			icon_state = "pinonclose"
		if(9 to 16)
			icon_state = "pinonmedium"
		if(16 to INFINITY)
			icon_state = "pinonfar"

	spawn(5) .()

/obj/item/weapon/pinpointer/nukeop/proc/worklocation()
	if(!active)
		return
	if(!mode)
		workdisk()
		return
	if(!bomb_set)
		mode = 0
		workdisk()
		playsound(loc, 'sound/machines/twobeep.ogg', 50, 1)
		visible_message("Authentication Disk Locator mode actived.")
		return
	if(!home)
		home = SSshuttle.getShuttle("syndicate")
		if(!home)
			icon_state = "pinonnull"
			return
	if(loc.z != home.z)	//If you are on a different z-level from the shuttle
		icon_state = "pinonnull"
	else
		dir = get_dir(src, home)
		switch(get_dist(src, home))
			if(0)
				icon_state = "pinondirect"
			if(1 to 8)
				icon_state = "pinonclose"
			if(9 to 16)
				icon_state = "pinonmedium"
			if(16 to INFINITY)
				icon_state = "pinonfar"

	spawn(5)
		.()

/obj/item/weapon/pinpointer/operative
	name = "operative pinpointer"
	icon = 'icons/obj/device.dmi'
	desc = "A pinpointer that leads to the first Syndicate operative detected."
	var/mob/living/carbon/nearest_op = null

/obj/item/weapon/pinpointer/operative/attack_self()
	if(!usr.mind || !(usr.mind in ticker.mode.syndicates))
		usr << "AUTHENTICATION FAILURE. ACCESS DENIED."
		return 0
	if(!active)
		active = 1
		workop()
		usr << "You activate the pinpointer."
	else
		active = 0
		icon_state = "pinoff"
		usr << "You deactivate the pinpointer."

/obj/item/weapon/pinpointer/operative/proc/scan_for_ops()
	if(active)
		nearest_op = null //Resets nearest_op every time it scans
		var/closest_distance = 1000
		for(var/mob/living/carbon/M in mob_list)
			if(M.mind && (M.mind in ticker.mode.syndicates))
				if(get_dist(M, get_turf(src)) < closest_distance) //Actually points toward the nearest op, instead of a random one like it used to
					nearest_op = M

/obj/item/weapon/pinpointer/operative/proc/workop()
	if(active)
		scan_for_ops()
		point_at(nearest_op, 0)
		spawn(5)
			.()
	else
		return 0

/obj/item/weapon/pinpointer/operative/examine(mob/user)
	..()
	if(active)
		if(nearest_op)
			user << "Nearest operative detected is [nearest_op.real_name]."
		else
			user << "No operatives detected within scanning range."


code:
/datum/objective
	var/datum/mind/owner = null			//Who owns the objective.
	var/explanation_text = "Nothing"	//What that person is supposed to do.
	var/datum/mind/target = null		//If they are focused on a particular person.
	var/target_amount = 0				//If they are focused on a particular number. Steal objectives have their own counter.
	var/completed = 0					//currently only used for custom objectives.
	var/dangerrating = 0				//How hard the objective is, essentially. Used for dishing out objectives and checking overall victory.
	var/martyr_compatible = 0			//If the objective is compatible with martyr objective, i.e. if you can still do it while dead.

/datum/objective/New(var/text)
	if(text)
		explanation_text = text

/datum/objective/proc/check_completion()
	return completed

/datum/objective/proc/is_unique_objective(possible_target)
	for(var/datum/objective/O in owner.objectives)
		if(istype(O, type) && O.get_target() == possible_target)
			return 0
	return 1

/datum/objective/proc/get_target()
	return target

/datum/objective/proc/find_target()
	var/list/possible_targets = list()
	for(var/datum/mind/possible_target in ticker.minds)
		if(possible_target != owner && ishuman(possible_target.current) && (possible_target.current.stat != 2) && is_unique_objective(possible_target))
			possible_targets += possible_target
	if(possible_targets.len > 0)
		target = pick(possible_targets)
	update_explanation_text()
	return target

/datum/objective/proc/find_target_by_role(role, role_type=0, invert=0)//Option sets either to check assigned role or special role. Default to assigned., invert inverts the check, eg: "Don't choose a Ling"
	for(var/datum/mind/possible_target in ticker.minds)
		if((possible_target != owner) && ishuman(possible_target.current))
			var/is_role = 0
			if(role_type)
				if(possible_target.special_role == role)
					is_role++
			else
				if(possible_target.assigned_role == role)
					is_role++

			if(invert)
				if(is_role)
					continue
				target = possible_target
				break
			else if(is_role)
				target = possible_target
				break

	update_explanation_text()

/datum/objective/proc/update_explanation_text()
	//Default does nothing, override where needed

/datum/objective/proc/give_special_equipment()

/datum/objective/assassinate
	var/target_role_type=0
	dangerrating = 10
	martyr_compatible = 1

/datum/objective/assassinate/find_target_by_role(role, role_type=0, invert=0)
	if(!invert)
		target_role_type = role_type
	..()
	return target

/datum/objective/assassinate/check_completion()
	if(target && target.current)
		if(target.current.stat == DEAD || issilicon(target.current) || isbrain(target.current) || target.current.z > 6 || !target.current.ckey) //Borgs/brains/AIs count as dead for traitor objectives. --NeoFite
			return 1
		return 0
	return 1

/datum/objective/assassinate/update_explanation_text()
	..()
	if(target && target.current)
		explanation_text = "Assassinate [target.name], the [!target_role_type ? target.assigned_role : target.special_role]."
	else
		explanation_text = "Free Objective"

/datum/objective/mutiny
	var/target_role_type=0
	martyr_compatible = 1

/datum/objective/mutiny/find_target_by_role(role, role_type=0,invert=0)
	if(!invert)
		target_role_type = role_type
	..()
	return target

/datum/objective/mutiny/check_completion()
	if(target && target.current)
		if(target.current.stat == DEAD || !ishuman(target.current) || !target.current.ckey || !target.current.client)
			return 1
		var/turf/T = get_turf(target.current)
		if(T && (T.z > ZLEVEL_STATION) || target.current.client.is_afk())			//If they leave the station or go afk they count as dead for this
			return 2
		return 0
	return 1

/datum/objective/mutiny/update_explanation_text()
	..()
	if(target && target.current)
		explanation_text = "Assassinate or exile [target.name], the [!target_role_type ? target.assigned_role : target.special_role]."
	else
		explanation_text = "Free Objective"

/datum/objective/maroon
	var/target_role_type=0
	dangerrating = 5
	martyr_compatible = 1

/datum/objective/maroon/find_target_by_role(role, role_type=0, invert=0)
	if(!invert)
		target_role_type = role_type
	..()
	return target

/datum/objective/maroon/check_completion()
	if(target && target.current)
		if(target.current.stat == DEAD || issilicon(target.current) || isbrain(target.current) || target.current.z > 6 || !target.current.ckey) //Borgs/brains/AIs count as dead for traitor objectives. --NeoFite
			return 1
		if(target.current.onCentcom() || target.current.onSyndieBase())
			return 0
	return 1

/datum/objective/maroon/update_explanation_text()
	if(target && target.current)
		explanation_text = "Prevent [target.name], the [!target_role_type ? target.assigned_role : target.special_role], from escaping alive."
	else
		explanation_text = "Free Objective"

/datum/objective/debrain//I want braaaainssss
	var/target_role_type=0
	dangerrating = 20

/datum/objective/debrain/find_target_by_role(role, role_type=0, invert=0)
	if(!invert)
		target_role_type = role_type
	..()
	return target

/datum/objective/debrain/check_completion()
	if(!target)//If it's a free objective.
		return 1
	if( !owner.current || owner.current.stat==DEAD )//If you're otherwise dead.
		return 0
	if( !target.current || !isbrain(target.current) )
		return 0
	var/atom/A = target.current
	while(A.loc)			//check to see if the brainmob is on our person
		A = A.loc
		if(A == owner.current)
			return 1
	return 0

/datum/objective/debrain/update_explanation_text()
	..()
	if(target && target.current)
		explanation_text = "Steal the brain of [target.name], the [!target_role_type ? target.assigned_role : target.special_role]."
	else
		explanation_text = "Free Objective"

/datum/objective/protect//The opposite of killing a dude.
	var/target_role_type=0
	dangerrating = 10
	martyr_compatible = 1

/datum/objective/protect/find_target_by_role(role, role_type=0, invert=0)
	if(!invert)
		target_role_type = role_type
	..()
	return target

/datum/objective/protect/check_completion()
	if(!target)			//If it's a free objective.
		return 1
	if(target.current)
		if(target.current.stat == DEAD || issilicon(target.current) || isbrain(target.current))
			return 0
		return 1
	return 0

/datum/objective/protect/update_explanation_text()
	..()
	if(target && target.current)
		explanation_text = "Protect [target.name], the [!target_role_type ? target.assigned_role : target.special_role]."
	else
		explanation_text = "Free Objective"

/datum/objective/hijack
	explanation_text = "Hijack the shuttle to ensure no loyalist Nanotrasen crew escape alive and out of custody."
	dangerrating = 25
	martyr_compatible = 0 //Technically you won't get both anyway.

/datum/objective/hijack/check_completion()
	if(!owner.current || owner.current.stat)
		return 0
	if(SSshuttle.emergency.mode < SHUTTLE_ENDGAME)
		return 0
	if(issilicon(owner.current))
		return 0

	var/area/A = get_area(owner.current)
	if(SSshuttle.emergency.areaInstance != A)
		return 0

	for(var/mob/living/player in player_list)
		if(player.mind && player.mind != owner)
			if(player.stat != DEAD)
				if(istype(player, /mob/living/silicon)) //Borgs are technically dead anyways
					continue
				if(get_area(player) == A)
					if(!player.mind.special_role && !istype(get_turf(player.mind.current), /turf/open/floor/plasteel/shuttle/red))
						return 0
	return 1

/datum/objective/hijackclone
	explanation_text = "Hijack the emergency shuttle by ensuring only you (or your copies) escape."
	dangerrating = 25
	martyr_compatible = 0

/datum/objective/hijackclone/check_completion()
	if(!owner.current)
		return 0
	if(SSshuttle.emergency.mode < SHUTTLE_ENDGAME)
		return 0

	var/area/A = SSshuttle.emergency.areaInstance

	for(var/mob/living/player in player_list) //Make sure nobody else is onboard
		if(player.mind && player.mind != owner)
			if(player.stat != DEAD)
				if(istype(player, /mob/living/silicon))
					continue
				if(get_area(player) == A)
					if(player.real_name != owner.current.real_name && !istype(get_turf(player.mind.current), /turf/open/floor/plasteel/shuttle/red))
						return 0

	for(var/mob/living/player in player_list) //Make sure at least one of you is onboard
		if(player.mind && player.mind != owner)
			if(player.stat != DEAD)
				if(istype(player, /mob/living/silicon))
					continue
				if(get_area(player) == A)
					if(player.real_name == owner.current.real_name && !istype(get_turf(player.mind.current), /turf/open/floor/plasteel/shuttle/red))
						return 1
	return 0

/datum/objective/block
	explanation_text = "Do not allow any organic lifeforms to escape on the shuttle alive."
	dangerrating = 25
	martyr_compatible = 1

/datum/objective/block/check_completion()
	if(!istype(owner.current, /mob/living/silicon))
		return 0
	if(SSshuttle.emergency.mode < SHUTTLE_ENDGAME)
		return 1

	var/area/A = SSshuttle.emergency.areaInstance

	for(var/mob/living/player in player_list)
		if(istype(player, /mob/living/silicon))
			continue
		if(player.mind)
			if(player.stat != DEAD)
				if(get_area(player) == A)
					return 0

	return 1

/datum/objective/purge
	explanation_text = "Ensure no mutant humanoid species are present aboard the escape shuttle."
	dangerrating = 25
	martyr_compatible = 1

/datum/objective/purge/check_completion()
	if(SSshuttle.emergency.mode < SHUTTLE_ENDGAME)
		return 1

	var/area/A = SSshuttle.emergency.areaInstance

	for(var/mob/living/player in player_list)
		if(get_area(player) == A && player.mind && player.stat != DEAD && istype(player, /mob/living/carbon/human))
			var/mob/living/carbon/human/H = player
			if(H.dna.species.id != "human")
				return 0

	return 1

/datum/objective/robot_army
	explanation_text = "Have at least eight active cyborgs synced to you."
	dangerrating = 25
	martyr_compatible = 0

/datum/objective/robot_army/check_completion()
	if(!istype(owner.current, /mob/living/silicon/ai))
		return 0
	var/mob/living/silicon/ai/A = owner.current

	var/counter = 0

	for(var/mob/living/silicon/robot/R in A.connected_robots)
		if(R.stat != DEAD)
			counter++

	if(counter < 8)
		return 0
	return 1

/datum/objective/escape
	explanation_text = "Escape on the shuttle or an escape pod alive and without being in custody."
	dangerrating = 5

/datum/objective/escape/check_completion()
	if(issilicon(owner.current))
		return 0
	if(isbrain(owner.current))
		return 0
	if(!owner.current || owner.current.stat == DEAD)
		return 0
	if(ticker.force_ending) //This one isn't their fault, so lets just assume good faith
		return 1
	if(ticker.mode.station_was_nuked) //If they escaped the blast somehow, let them win
		return 1
	if(SSshuttle.emergency.mode < SHUTTLE_ENDGAME)
		return 0
	var/turf/location = get_turf(owner.current)
	if(!location)
		return 0

	if(istype(location, /turf/open/floor/plasteel/shuttle/red)) // Fails traitors if they are in the shuttle brig -- Polymorph
		return 0

	if(location.onCentcom() || location.onSyndieBase())
		return 1

	return 0

/datum/objective/escape/escape_with_identity
	dangerrating = 10
	var/target_real_name // Has to be stored because the target's real_name can change over the course of the round
	var/target_missing_id

/datum/objective/escape/escape_with_identity/find_target()
	target = ..()
	update_explanation_text()

/datum/objective/escape/escape_with_identity/update_explanation_text()
	if(target && target.current)
		target_real_name = target.current.real_name
		explanation_text = "Escape on the shuttle or an escape pod with the identity of [target_real_name], the [target.assigned_role]"
		var/mob/living/carbon/human/H
		if(ishuman(target.current))
			H = target.current
		if(H && H.get_id_name() != target_real_name)
			target_missing_id = 1
		else
			explanation_text += " while wearing their identification card"
		explanation_text += "." //Proper punctuation is important!

	else
		explanation_text = "Free Objective."

/datum/objective/escape/escape_with_identity/check_completion()
	if(!target_real_name)
		return 1
	if(!ishuman(owner.current))
		return 0
	var/mob/living/carbon/human/H = owner.current
	if(..())
		if(H.dna.real_name == target_real_name)
			if(H.get_id_name()== target_real_name || target_missing_id)
				return 1
	return 0

/datum/objective/survive
	explanation_text = "Stay alive until the end."
	dangerrating = 3

/datum/objective/survive/check_completion()
	if(!owner.current || owner.current.stat == DEAD || isbrain(owner.current))
		return 0		//Brains no longer win survive objectives. --NEO
	if(!is_special_character(owner.current)) //This fails borg'd traitors
		return 0
	return 1

/datum/objective/martyr
	explanation_text = "Die a glorious death."
	dangerrating = 1

/datum/objective/martyr/check_completion()
	if(!owner.current) //Gibbed, etc.
		return 1
	if(owner.current && owner.current.stat == DEAD) //You're dead! Yay!
		return 1
	return 0

/datum/objective/nuclear
	explanation_text = "Destroy the station with a nuclear device."
	martyr_compatible = 1

/datum/objective/nuclear/check_completion()
	if(ticker && ticker.mode && ticker.mode.station_was_nuked)
		return 1
	return 0

var/global/list/possible_items = list()
/datum/objective/steal
	var/datum/objective_item/targetinfo = null //Save the chosen item datum so we can access it later.
	var/obj/item/steal_target = null //Needed for custom objectives (they're just items, not datums).
	dangerrating = 5 //Overridden by the individual item's difficulty, but defaults to 5 for custom objectives.
	martyr_compatible = 0

/datum/objective/steal/get_target()
	return steal_target

/datum/objective/steal/New()
	..()
	if(!possible_items.len)//Only need to fill the list when it's needed.
		init_subtypes(/datum/objective_item/steal,possible_items)

/datum/objective/steal/find_target()
	var/approved_targets = list()
	for(var/datum/objective_item/possible_item in possible_items)
		if(is_unique_objective(possible_item.targetitem) && !(owner.current.mind.assigned_role in possible_item.excludefromjob))
			approved_targets += possible_item
	return set_target(safepick(approved_targets))

/datum/objective/steal/proc/set_target(datum/objective_item/item)
	if(item)
		targetinfo = item

		steal_target = targetinfo.targetitem
		explanation_text = "Steal [targetinfo.name]."
		dangerrating = targetinfo.difficulty
		give_special_equipment()
		return steal_target
	else
		explanation_text = "Free objective"
		return

/datum/objective/steal/proc/select_target() //For admins setting objectives manually.
	var/list/possible_items_all = possible_items+"custom"
	var/new_target = input("Select target:", "Objective target", steal_target) as null|anything in possible_items_all
	if (!new_target) return

	if (new_target == "custom") //Can set custom items.
		var/obj/item/custom_target = input("Select type:","Type") as null|anything in typesof(/obj/item)
		if (!custom_target) return
		var/tmp_obj = new custom_target
		var/custom_name = tmp_obj:name
		qdel(tmp_obj)
		custom_name = stripped_input("Enter target name:", "Objective target", custom_name)
		if (!custom_name) return
		steal_target = custom_target
		explanation_text = "Steal [custom_name]."

	else
		set_target(new_target)
	return steal_target

/datum/objective/steal/check_completion()
	if(!steal_target)
		return 1
	if(!isliving(owner.current))
		return 0
	var/list/all_items = owner.current.GetAllContents()	//this should get things in cheesewheels, books, etc.

	for(var/obj/I in all_items) //Check for items
		if(istype(I, steal_target))
			if(!targetinfo) //If there's no targetinfo, then that means it was a custom objective. At this point, we know you have the item, so return 1.
				return 1
			else if(targetinfo.check_special_completion(I))//Returns 1 by default. Items with special checks will return 1 if the conditions are fulfilled.
				return 1

		if(targetinfo && I.type in targetinfo.altitems) //Ok, so you don't have the item. Do you have an alternative, at least?
			if(targetinfo.check_special_completion(I))//Yeah, we do! Don't return 0 if we don't though - then you could fail if you had 1 item that didn't pass and got checked first!
				return 1
	return 0

/datum/objective/steal/give_special_equipment()
	if(owner && owner.current && targetinfo)
		if(istype(owner.current, /mob/living/carbon/human))
			var/mob/living/carbon/human/H = owner.current
			var/list/slots = list ("backpack" = slot_in_backpack)
			for(var/eq_path in targetinfo.special_equipment)
				var/obj/O = new eq_path
				H.equip_in_one_of_slots(O, slots)
				H.update_icons()

var/global/list/possible_items_special = list()
/datum/objective/steal/special //ninjas are so special they get their own subtype good for them

/datum/objective/steal/special/New()
	..()
	if(!possible_items_special.len)
		init_subtypes(/datum/objective_item/special,possible_items)
		init_subtypes(/datum/objective_item/stack,possible_items)

/datum/objective/steal/special/find_target()
	return set_target(pick(possible_items_special))

/datum/objective/steal/exchange
	dangerrating = 10
	martyr_compatible = 0

/datum/objective/steal/exchange/proc/set_faction(faction,otheragent)
	target = otheragent
	if(faction == "red")
		targetinfo = new/datum/objective_item/unique/docs_blue
	else if(faction == "blue")
		targetinfo = new/datum/objective_item/unique/docs_red
	explanation_text = "Acquire [targetinfo.name] held by [target.current.real_name], the [target.assigned_role] and syndicate agent"
	steal_target = targetinfo.targetitem

/datum/objective/steal/exchange/update_explanation_text()
	..()
	if(target && target.current)
		explanation_text = "Acquire [targetinfo.name] held by [target.name], the [target.assigned_role] and syndicate agent"
	else
		explanation_text = "Free Objective"

/datum/objective/steal/exchange/backstab
	dangerrating = 3

/datum/objective/steal/exchange/backstab/set_faction(faction)
	if(faction == "red")
		targetinfo = new/datum/objective_item/unique/docs_red
	else if(faction == "blue")
		targetinfo = new/datum/objective_item/unique/docs_blue
	explanation_text = "Do not give up or lose [targetinfo.name]."
	steal_target = targetinfo.targetitem

/datum/objective/download
	dangerrating = 10

/datum/objective/download/proc/gen_amount_goal()
	target_amount = rand(10,20)
	explanation_text = "Download [target_amount] research level\s."
	return target_amount

/datum/objective/download/check_completion()//NINJACODE
	if(!ishuman(owner.current))
		return 0

	var/mob/living/carbon/human/H = owner.current
	if(!H || H.stat == DEAD)
		return 0

	if(!istype(H.wear_suit, /obj/item/clothing/suit/space/space_ninja))
		return 0

	var/obj/item/clothing/suit/space/space_ninja/SN = H.wear_suit
	if(!SN.s_initialized)
		return 0

	var/current_amount
	if(!SN.stored_research.len)
		return 0
	else
		for(var/datum/tech/current_data in SN.stored_research)
			if(current_data.level)
				current_amount += (current_data.level-1)
	if(current_amount<target_amount)
		return 0
	return 1

/datum/objective/capture
	dangerrating = 10

/datum/objective/capture/proc/gen_amount_goal()
		target_amount = rand(5,10)
		explanation_text = "Capture [target_amount] lifeform\s with an energy net. Live, rare specimens are worth more."
		return target_amount

/datum/objective/capture/check_completion()//Basically runs through all the mobs in the area to determine how much they are worth.
	var/captured_amount = 0
	var/area/centcom/holding/A = locate()
	for(var/mob/living/carbon/human/M in A)//Humans.
		if(M.stat==2)//Dead folks are worth less.
			captured_amount+=0.5
			continue
		captured_amount+=1
	for(var/mob/living/carbon/monkey/M in A)//Monkeys are almost worthless, you failure.
		captured_amount+=0.1
	for(var/mob/living/carbon/alien/larva/M in A)//Larva are important for research.
		if(M.stat==2)
			captured_amount+=0.5
			continue
		captured_amount+=1
	for(var/mob/living/carbon/alien/humanoid/M in A)//Aliens are worth twice as much as humans.
		if(istype(M, /mob/living/carbon/alien/humanoid/royal/queen))//Queens are worth three times as much as humans.
			if(M.stat==2)
				captured_amount+=1.5
			else
				captured_amount+=3
			continue
		if(M.stat==2)
			captured_amount+=1
			continue
		captured_amount+=2
	if(captured_amount<target_amount)
		return 0
	return 1

/datum/objective/absorb
	dangerrating = 10

/datum/objective/absorb/proc/gen_amount_goal(lowbound = 4, highbound = 6)
	target_amount = rand (lowbound,highbound)
	if (ticker)
		var/n_p = 1 //autowin
		if (ticker.current_state == GAME_STATE_SETTING_UP)
			for(var/mob/new_player/P in player_list)
				if(P.client && P.ready && P.mind!=owner)
					n_p ++
		else if (ticker.current_state == GAME_STATE_PLAYING)
			for(var/mob/living/carbon/human/P in player_list)
				if(P.client && !(P.mind in ticker.mode.changelings) && P.mind!=owner)
					n_p ++
		target_amount = min(target_amount, n_p)

	explanation_text = "Extract [target_amount] compatible genome\s."
	return target_amount

/datum/objective/absorb/check_completion()
	if(owner && owner.changeling && owner.changeling.stored_profiles && (owner.changeling.absorbedcount >= target_amount))
		return 1
	else
		return 0

/datum/objective/destroy
	dangerrating = 10
	martyr_compatible = 1

/datum/objective/destroy/find_target()
	var/list/possible_targets = active_ais(1)
	var/mob/living/silicon/ai/target_ai = pick(possible_targets)
	target = target_ai.mind
	update_explanation_text()
	return target

/datum/objective/destroy/check_completion()
	if(target && target.current)
		if(target.current.stat == DEAD || target.current.z > 6 || !target.current.ckey) //Borgs/brains/AIs count as dead for traitor objectives. --NeoFite
			return 1
		return 0
	return 1

/datum/objective/destroy/update_explanation_text()
	..()
	if(target && target.current)
		explanation_text = "Destroy [target.name], the experimental AI."
	else
		explanation_text = "Free Objective"

/datum/objective/summon_guns
	explanation_text = "Steal at least five guns!"

/datum/objective/summon_guns/check_completion()
	if(!isliving(owner.current))
		return 0
	var/guncount = 0
	var/list/all_items = owner.current.GetAllContents()	//this should get things in cheesewheels, books, etc.
	for(var/obj/I in all_items) //Check for guns
		if(istype(I, /obj/item/weapon/gun))
			guncount++
	if(guncount >= 5)
		return 1
	else
		return 0
	return 0

////////////////////////////////
// Changeling team objectives //
////////////////////////////////

/datum/objective/changeling_team_objective //Abstract type
	martyr_compatible = 0	//Suicide is not teamwork!
	explanation_text = "Changeling Friendship!"
	var/min_lings = 3 //Minimum amount of lings for this team objective to be possible
	var/escape_objective_compatible = FALSE

//Impersonate department
//Picks as many people as it can from a department (Security,Engineer,Medical,Science)
//and tasks the lings with killing and replacing them
/datum/objective/changeling_team_objective/impersonate_department
	explanation_text = "Ensure X derpartment are killed, impersonated, and replaced by Changelings"
	var/command_staff_only = FALSE //if this is true, it picks command staff instead
	var/list/department_minds = list()
	var/list/department_real_names = list()
	var/department_string = ""

/datum/objective/changeling_team_objective/impersonate_department/proc/get_department_staff()
	department_minds = list()
	department_real_names = list()

	var/list/departments = list("Head of Security","Research Director","Chief Engineer","Chief Medical Officer")
	var/department_head = pick(departments)
	switch(department_head)
		if("Head of Security")
			department_string = "security"
		if("Research Director")
			department_string = "science"
		if("Chief Engineer")
			department_string = "engineering"
		if("Chief Medical Officer")
			department_string = "medical"

	var/ling_count = ticker.mode.changelings

	for(var/datum/mind/M in ticker.minds)
		if(M in ticker.mode.changelings)
			continue
		if(department_head in get_department_heads(M.assigned_role))
			if(ling_count)
				ling_count--
				department_minds += M
				department_real_names += M.current.real_name
			else
				break

	if(!department_minds.len)
		log_game("[type] has failed to find department staff, and has removed itself. the round will continue normally")
		owner.objectives -= src
		qdel(src)
		return

/datum/objective/changeling_team_objective/impersonate_department/proc/get_heads()
	department_minds = list()
	department_real_names = list()

	//Needed heads is between min_lings and the maximum possible amount of command roles
	//So at the time of writing, rand(3,6), it's also capped by the amount of lings there are
	//Because you can't fill 6 head roles with 3 lings

	var/needed_heads = rand(min_lings,command_positions.len)
	needed_heads = min(ticker.mode.changelings.len,needed_heads)

	var/list/heads = ticker.mode.get_living_heads()
	for(var/datum/mind/head in heads)
		if(head in ticker.mode.changelings) //Looking at you HoP.
			continue
		if(needed_heads)
			department_minds += head
			department_real_names += head.current.real_name
			needed_heads--
		else
			break

	if(!department_minds.len)
		log_game("[type] has failed to find department heads, and has removed itself. the round will continue normally")
		owner.objectives -= src
		qdel(src)
		return

/datum/objective/changeling_team_objective/impersonate_department/New(var/text)
	..()
	if(command_staff_only)
		get_heads()
	else
		get_department_staff()

	update_explanation_text()

/datum/objective/changeling_team_objective/impersonate_department/update_explanation_text()
	..()
	if(!department_real_names.len || !department_minds.len)
		explanation_text = "Free Objective"
		return  //Something fucked up, give them a win

	if(command_staff_only)
		explanation_text = "Ensure changelings impersonate and escape as the following heads of staff: "
	else
		explanation_text = "Ensure changelings impersonate and escape as the following members of \the [department_string] department: "

	var/first = 1
	for(var/datum/mind/M in department_minds)
		var/string = "[M.name] the [M.assigned_role]"
		if(!first)
			string = ", [M.name] the [M.assigned_role]"
		else
			first--
		explanation_text += string

	if(command_staff_only)
		explanation_text += ", while the real heads are dead. This is a team objective."
	else
		explanation_text += ", while the real members are dead. This is a team objective."

/datum/objective/changeling_team_objective/impersonate_department/check_completion()
	if(!department_real_names.len || !department_minds.len)
		return 1 //Something fucked up, give them a win

	var/list/check_names = department_real_names.Copy()

	//Check each department member's mind to see if any of them made it to centcomm alive, if they did it's an automatic fail
	for(var/datum/mind/M in department_minds)
		if(M in ticker.mode.changelings) //Lings aren't picked for this, but let's be safe
			continue

		if(M.current)
			var/turf/mloc = get_turf(M.current)
			if(mloc.onCentcom() && (M.current.stat != DEAD))
				return 0 //A Non-ling living target got to centcomm, fail

	//Check each staff member has been replaced, by cross referencing changeling minds, changeling current dna, the staff minds and their original DNA names
	var/success = 0
	changelings:
		for(var/datum/mind/changeling in ticker.mode.changelings)
			if(success >= department_minds.len) //We did it, stop here!
				return 1
			if(ishuman(changeling.current))
				var/mob/living/carbon/human/H = changeling.current
				var/turf/cloc = get_turf(changeling.current)
				if(cloc && cloc.onCentcom() && (changeling.current.stat != DEAD)) //Living changeling on centcomm....
					for(var/name in check_names) //Is he (disguised as) one of the staff?
						if(H.dna.real_name == name)
							check_names -= name //This staff member is accounted for, remove them, so the team don't succeed by escape as 7 of the same engineer
							success++ //A living changeling staff member made it to centcomm
							continue changelings

	if(success >= department_minds.len)
		return 1
	return 0

//A subtype of impersonate_derpartment
//This subtype always picks as many command staff as it can (HoS,HoP,Cap,CE,CMO,RD)
//and tasks the lings with killing and replacing them
/datum/objective/changeling_team_objective/impersonate_department/impersonate_heads
	explanation_text = "Have X or more heads of staff escape on the shuttle disguised as heads, while the real heads are dead"
	command_staff_only = TRUE