code:
//Contains the target item datums for Steal objectives.

/datum/objective_item
	var/name = "A silly bike horn! Honk!"
	var/targetitem = /obj/item/weapon/bikehorn		//typepath of the objective item
	var/difficulty = 9001							//vaguely how hard it is to do this objective
	var/list/excludefromjob = list()				//If you don't want a job to get a certain objective (no captain stealing his own medal, etcetc)
	var/list/altitems = list()				//Items which can serve as an alternative to the objective (darn you blueprints)
	var/list/special_equipment = list()

/datum/objective_item/proc/check_special_completion() //for objectives with special checks (is that slime extract unused? does that intellicard have an ai in it? etcetc)
	return 1

/datum/objective_item/steal/caplaser
	name = "the captain's antique laser gun"
	targetitem = /obj/item/weapon/gun/energy/laser/captain
	difficulty = 5
	excludefromjob = list("Captain")

/datum/objective_item/steal/hoslaser
	name = "the head of security's personal laser gun"
	targetitem = /obj/item/weapon/gun/energy/gun/hos
	difficulty = 10
	excludefromjob = list("Head Of Security")

/datum/objective_item/steal/handtele
	name = "a hand teleporter"
	targetitem = /obj/item/weapon/hand_tele
	difficulty = 5
	excludefromjob = list("Captain")

/datum/objective_item/steal/jetpack
	name = "the Captain's jetpack"
	targetitem = /obj/item/weapon/tank/jetpack/oxygen/captain
	difficulty = 5
	excludefromjob = list("Captain")

/datum/objective_item/steal/magboots
	name = "the chief engineer's advanced magnetic boots"
	targetitem =  /obj/item/clothing/shoes/magboots/advance
	difficulty = 5
	excludefromjob = list("Chief Engineer")

/datum/objective_item/steal/capmedal
	name = "the medal of captaincy"
	targetitem = /obj/item/clothing/tie/medal/gold/captain
	difficulty = 5
	excludefromjob = list("Captain")

/datum/objective_item/steal/hypo
	name = "the hypospray"
	targetitem = /obj/item/weapon/reagent_containers/hypospray/CMO
	difficulty = 5
	excludefromjob = list("Chief Medical Officer")

/datum/objective_item/steal/nukedisc
	name = "the nuclear authentication disk"
	targetitem = /obj/item/weapon/disk/nuclear
	difficulty = 5
	excludefromjob = list("Captain")

/datum/objective_item/steal/reflector
	name = "a reflector vest"
	targetitem = /obj/item/clothing/suit/armor/laserproof
	difficulty = 3
	excludefromjob = list("Head of Security", "Warden")

/datum/objective_item/steal/reactive
	name = "the reactive teleport armor"
	targetitem = /obj/item/clothing/suit/armor/reactive
	difficulty = 5
	excludefromjob = list("Research Director")

/datum/objective_item/steal/documents
	name = "any set of secret documents of any organization"
	targetitem = /obj/item/documents //Any set of secret documents. Doesn't have to be NT's
	difficulty = 5

/datum/objective_item/steal/nuke_core
	name = "the heavily radioactive plutonium core from the onboard self-destruct. Take care to wear the proper safety equipment when extracting the core"
	targetitem = /obj/item/nuke_core
	difficulty = 15

/datum/objective_item/steal/nuke_core/New()
	special_equipment += /obj/item/weapon/storage/box/syndie_kit/nuke

//Items with special checks!
/datum/objective_item/steal/plasma
	name = "28 moles of plasma (full tank)"
	targetitem = /obj/item/weapon/tank
	difficulty = 3
	excludefromjob = list("Chief Engineer","Research Director","Station Engineer","Scientist","Atmospheric Technician")

/datum/objective_item/steal/plasma/check_special_completion(obj/item/weapon/tank/T)
	var/target_amount = text2num(name)
	var/found_amount = 0
	found_amount += T.air_contents.gases["plasma"] ? T.air_contents.gases["plasma"][MOLES] : 0
	return found_amount>=target_amount

/datum/objective_item/steal/functionalai
	name = "a functional AI"
	targetitem = /obj/item/device/aicard
	difficulty = 20 //beyond the impossible

/datum/objective_item/steal/functionalai/check_special_completion(obj/item/device/aicard/C)
	for(var/mob/living/silicon/ai/A in C)
		if(istype(A, /mob/living/silicon/ai) && A.stat != 2) //See if any AI's are alive inside that card.
			return 1
	return 0

/datum/objective_item/steal/blueprints
	name = "the station blueprints"
	targetitem = /obj/item/areaeditor/blueprints
	difficulty = 10
	excludefromjob = list("Chief Engineer")
	altitems = list(/obj/item/weapon/photo)

/datum/objective_item/steal/blueprints/check_special_completion(obj/item/I)
	if(istype(I, /obj/item/areaeditor/blueprints))
		return 1
	if(istype(I, /obj/item/weapon/photo))
		var/obj/item/weapon/photo/P = I
		if(P.blueprints)	//if the blueprints are in frame
			return 1
	return 0

/datum/objective_item/steal/slime
	name = "an unused sample of slime extract"
	targetitem = /obj/item/slime_extract
	difficulty = 3
	excludefromjob = list("Research Director","Scientist")

/datum/objective_item/steal/slime/check_special_completion(obj/item/slime_extract/E)
	if(E.Uses > 0)
		return 1
	return 0

//Unique Objectives
/datum/objective_item/unique/docs_red
	name = "the \"Red\" secret documents"
	targetitem = /obj/item/documents/syndicate/red
	difficulty = 10

/datum/objective_item/unique/docs_blue
	name = "the \"Blue\" secret documents"
	targetitem = /obj/item/documents/syndicate/blue
	difficulty = 10

//Old ninja objectives.
/datum/objective_item/special/pinpointer
	name = "the captain's pinpointer"
	targetitem = /obj/item/weapon/pinpointer
	difficulty = 10

/datum/objective_item/special/aegun
	name = "an advanced energy gun"
	targetitem = /obj/item/weapon/gun/energy/gun/nuclear
	difficulty = 10

/datum/objective_item/special/ddrill
	name = "a diamond drill"
	targetitem = /obj/item/weapon/pickaxe/drill/diamonddrill
	difficulty = 10

/datum/objective_item/special/boh
	name = "a bag of holding"
	targetitem = /obj/item/weapon/storage/backpack/holding
	difficulty = 10

/datum/objective_item/special/hypercell
	name = "a hyper-capacity cell"
	targetitem = /obj/item/weapon/stock_parts/cell/hyper
	difficulty = 5

/datum/objective_item/special/laserpointer
	name = "a laser pointer"
	targetitem = /obj/item/device/laser_pointer
	difficulty = 5

/datum/objective_item/special/corgimeat
	name = "a piece of corgi meat"
	targetitem = /obj/item/weapon/reagent_containers/food/snacks/meat/slab/corgi
	difficulty = 5

//Stack objectives get their own subtype
/datum/objective_item/stack
	name = "5 cardboards"
	targetitem = /obj/item/stack/sheet/cardboard
	difficulty = 9001

/datum/objective_item/stack/check_special_completion(obj/item/stack/S)
	var/target_amount = text2num(name)
	var/found_amount = 0

	if(istype(S, targetitem))
		found_amount = S.amount
	return found_amount>=target_amount

/datum/objective_item/stack/diamond
	name = "10 diamonds"
	targetitem = /obj/item/stack/sheet/mineral/diamond
	difficulty = 10

/datum/objective_item/stack/gold
	name = "50 gold bars"
	targetitem = /obj/item/stack/sheet/mineral/gold
	difficulty = 15

/datum/objective_item/stack/uranium
	name = "25 refined uranium bars"
	targetitem = /obj/item/stack/sheet/mineral/uranium
	difficulty = 10


code:
// To add a rev to the list of revolutionaries, make sure it's rev (with if(ticker.mode.name == "revolution)),
// then call ticker.mode:add_revolutionary(_THE_PLAYERS_MIND_)
// nothing else needs to be done, as that proc will check if they are a valid target.
// Just make sure the converter is a head before you call it!
// To remove a rev (from brainwashing or w/e), call ticker.mode:remove_revolutionary(_THE_PLAYERS_MIND_),
// this will also check they're not a head, so it can just be called freely
// If the game somtimes isn't registering a win properly, then ticker.mode.check_win() isn't being called somewhere.

/datum/game_mode
	var/list/datum/mind/head_revolutionaries = list()
	var/list/datum/mind/revolutionaries = list()

/datum/game_mode/revolution
	name = "revolution"
	config_tag = "revolution"
	antag_flag = ROLE_REV
	restricted_jobs = list("Security Officer", "Warden", "Detective", "AI", "Cyborg","Captain", "Head of Personnel", "Head of Security", "Chief Engineer", "Research Director", "Chief Medical Officer")
	required_players = 20
	required_enemies = 1
	recommended_enemies = 3
	enemy_minimum_age = 14

	var/finished = 0
	var/check_counter = 0
	var/max_headrevs = 3
	var/list/datum/mind/heads_to_kill = list()

///////////////////////////
//Announces the game type//
///////////////////////////
/datum/game_mode/revolution/announce()
	world << "The current game mode is - Revolution!"
	world << "Some crewmembers are attempting to start a revolution!
\nRevolutionaries - Kill the Captain, HoP, HoS, CE, RD and CMO. Convert other crewmembers (excluding the heads of staff, and security officers) to your cause by flashing them. Protect your leaders.
\nPersonnel - Protect the heads of staff. Kill the leaders of the revolution, and brainwash the other revolutionaries (by beating them in the head)."

///////////////////////////////////////////////////////////////////////////////
//Gets the round setup, cancelling if there's not enough players at the start//
///////////////////////////////////////////////////////////////////////////////
/datum/game_mode/revolution/pre_setup()

	if(config.protect_roles_from_antagonist)
		restricted_jobs += protected_jobs

	if(config.protect_assistant_from_antagonist)
		restricted_jobs += "Assistant"

	for (var/i=1 to max_headrevs)
		if (antag_candidates.len==0)
			break
		var/datum/mind/lenin = pick(antag_candidates)
		antag_candidates -= lenin
		head_revolutionaries += lenin
		lenin.restricted_roles = restricted_jobs

	if(head_revolutionaries.len < required_enemies)
		return 0

	return 1

/datum/game_mode/revolution/post_setup()
	var/list/heads = get_living_heads()
	var/list/sec = get_living_sec()
	var/weighted_score = min(max(round(heads.len - ((8 - sec.len) / 3)),1),max_headrevs)

	while(weighted_score < head_revolutionaries.len) //das vi danya
		var/datum/mind/trotsky = pick(head_revolutionaries)
		antag_candidates += trotsky
		head_revolutionaries -= trotsky
		update_rev_icons_removed(trotsky)

	for(var/datum/mind/rev_mind in head_revolutionaries)
		log_game("[rev_mind.key] (ckey) has been selected as a head rev")
		for(var/datum/mind/head_mind in heads)
			mark_for_death(rev_mind, head_mind)

		spawn(rand(10,100))
		//	equip_traitor(rev_mind.current, 1) //changing how revs get assigned their uplink so they can get PDA uplinks. --NEO
		//	Removing revolutionary uplinks.	-Pete
			equip_revolutionary(rev_mind.current)

	for(var/datum/mind/rev_mind in head_revolutionaries)
		greet_revolutionary(rev_mind)
	modePlayer += head_revolutionaries
	SSshuttle.emergencyNoEscape = 1
	..()

/datum/game_mode/revolution/process()
	check_counter++
	if(check_counter >= 5)
		if(!finished)
			check_heads()
			ticker.mode.check_win()
		check_counter = 0
	return 0

/datum/game_mode/proc/forge_revolutionary_objectives(datum/mind/rev_mind)
	var/list/heads = get_living_heads()
	for(var/datum/mind/head_mind in heads)
		var/datum/objective/mutiny/rev_obj = new
		rev_obj.owner = rev_mind
		rev_obj.target = head_mind
		rev_obj.explanation_text = "Assassinate or exile [head_mind.name], the [head_mind.assigned_role]."
		rev_mind.objectives += rev_obj

/datum/game_mode/proc/greet_revolutionary(datum/mind/rev_mind, you_are=1)
	var/obj_count = 1
	update_rev_icons_added(rev_mind)
	if (you_are)
		rev_mind.current << "You are a member of the revolutionaries' leadership!"
	for(var/datum/objective/objective in rev_mind.objectives)
		rev_mind.current << "Objective #[obj_count]: [objective.explanation_text]"
		rev_mind.special_role = "Head Revolutionary"
		obj_count++

/////////////////////////////////////////////////////////////////////////////////
//This are equips the rev heads with their gear, and makes the clown not clumsy//
/////////////////////////////////////////////////////////////////////////////////
/datum/game_mode/proc/equip_revolutionary(mob/living/carbon/human/mob)
	if(!istype(mob))
		return

	if (mob.mind)
		if (mob.mind.assigned_role == "Clown")
			mob << "Your training has allowed you to overcome your clownish nature, allowing you to wield weapons without harming yourself."
			mob.dna.remove_mutation(CLOWNMUT)

	var/obj/item/device/assembly/flash/T = new(mob)
	var/obj/item/toy/crayon/spraycan/R = new(mob)
	var/obj/item/clothing/glasses/hud/security/chameleon/C = new(mob)

	var/list/slots = list (
		"backpack" = slot_in_backpack,
		"left pocket" = slot_l_store,
		"right pocket" = slot_r_store,
		"left hand" = slot_l_hand,
		"right hand" = slot_r_hand,
	)
	var/where = mob.equip_in_one_of_slots(T, slots)
	var/where2 = mob.equip_in_one_of_slots(C, slots)
	mob.equip_in_one_of_slots(R,slots)

	mob.update_icons()

	if (!where2)
		mob << "The Syndicate were unfortunately unable to get you a chameleon security HUD."
	else
		mob << "The chameleon security HUD in your [where2] will help you keep track of who is loyalty-implanted, and unable to be recruited."

	if (!where)
		mob << "The Syndicate were unfortunately unable to get you a flash."
	else
		mob << "The flash in your [where] will help you to persuade the crew to join your cause."
		return 1

/////////////////////////////////
//Gives head revs their targets//
/////////////////////////////////
/datum/game_mode/revolution/proc/mark_for_death(datum/mind/rev_mind, datum/mind/head_mind)
	var/datum/objective/mutiny/rev_obj = new
	rev_obj.owner = rev_mind
	rev_obj.target = head_mind
	rev_obj.explanation_text = "Assassinate [head_mind.name], the [head_mind.assigned_role]."
	rev_mind.objectives += rev_obj
	heads_to_kill += head_mind

////////////////////////////////////////////
//Checks if new heads have joined midround//
////////////////////////////////////////////
/datum/game_mode/revolution/proc/check_heads()
	var/list/heads = get_all_heads()
	var/list/sec = get_all_sec()
	if(heads_to_kill.len < heads.len)
		var/list/new_heads = heads - heads_to_kill
		for(var/datum/mind/head_mind in new_heads)
			for(var/datum/mind/rev_mind in head_revolutionaries)
				mark_for_death(rev_mind, head_mind)

	if(head_revolutionaries.len < max_headrevs && head_revolutionaries.len < round(heads.len - ((8 - sec.len) / 3)))
		latejoin_headrev()

///////////////////////////////
//Adds a new headrev midround//
///////////////////////////////
/datum/game_mode/revolution/proc/latejoin_headrev()
	if(revolutionaries) //Head Revs are not in this list
		var/list/promotable_revs = list()
		for(var/datum/mind/khrushchev in revolutionaries)
			if(khrushchev.current && khrushchev.current.client && khrushchev.current.stat != DEAD)
				if(ROLE_REV in khrushchev.current.client.prefs.be_special)
					promotable_revs += khrushchev
		if(promotable_revs.len)
			var/datum/mind/stalin = pick(promotable_revs)
			revolutionaries -= stalin
			head_revolutionaries += stalin
			log_game("[stalin.key] (ckey) has been promoted to a head rev")
			equip_revolutionary(stalin.current)
			forge_revolutionary_objectives(stalin)
			greet_revolutionary(stalin)

//////////////////////////////////////
//Checks if the revs have won or not//
//////////////////////////////////////
/datum/game_mode/revolution/check_win()
	if(check_rev_victory())
		finished = 1
	else if(check_heads_victory())
		finished = 2
	return

///////////////////////////////
//Checks if the round is over//
///////////////////////////////
/datum/game_mode/revolution/check_finished()
	if(config.continuous["revolution"])
		if(finished != 0)
			SSshuttle.emergencyNoEscape = 0
			if(SSshuttle.emergency.mode == SHUTTLE_STRANDED)
				SSshuttle.emergency.mode = SHUTTLE_DOCKED
				SSshuttle.emergency.timer = world.time
				priority_announce("Hostile enviroment resolved. You have 3 minutes to board the Emergency Shuttle.", null, 'sound/AI/shuttledock.ogg', "Priority")
		return ..()
	if(finished != 0)
		return 1
	else
		return ..()

///////////////////////////////////////////////////
//Deals with converting players to the revolution//
///////////////////////////////////////////////////
/datum/game_mode/proc/add_revolutionary(datum/mind/rev_mind)
	if(rev_mind.assigned_role in command_positions)
		return 0
	var/mob/living/carbon/human/H = rev_mind.current//Check to see if the potential rev is implanted
	if(isloyal(H))
		return 0
	if((rev_mind in revolutionaries) || (rev_mind in head_revolutionaries))
		return 0
	revolutionaries += rev_mind
	if(iscarbon(rev_mind.current))
		var/mob/living/carbon/carbon_mob = rev_mind.current
		carbon_mob.silent = max(carbon_mob.silent, 5)
		carbon_mob.flash_eyes(1, 1)
	rev_mind.current.Stun(5)
	rev_mind.current << " You are now a revolutionary! Help your cause. Do not harm your fellow freedom fighters. You can identify your comrades by the red \"R\" icons, and your leaders by the blue \"R\" icons. Help them kill the heads to win the revolution!"
	rev_mind.current.attack_log += "\[[time_stamp()]\] Has been converted to the revolution!"
	rev_mind.special_role = "Revolutionary"
	update_rev_icons_added(rev_mind)
	if(jobban_isbanned(rev_mind.current, ROLE_REV))
		replace_jobbaned_player(rev_mind.current, ROLE_REV, ROLE_REV)
	return 1
//////////////////////////////////////////////////////////////////////////////
//Deals with players being converted from the revolution (Not a rev anymore)//  // Modified to handle borged MMIs.  Accepts another var if the target is being borged at the time  -- Polymorph.
//////////////////////////////////////////////////////////////////////////////
/datum/game_mode/proc/remove_revolutionary(datum/mind/rev_mind , beingborged)
	var/remove_head = 0
	if(beingborged && (rev_mind in head_revolutionaries))
		head_revolutionaries -= rev_mind
		remove_head = 1

	if((rev_mind in revolutionaries) || remove_head)
		revolutionaries -= rev_mind
		rev_mind.special_role = null
		rev_mind.current.attack_log += "\[[time_stamp()]\] Has renounced the revolution!"

		if(beingborged)
			rev_mind.current << "The frame's firmware detects and deletes your neural reprogramming! You remember nothing[remove_head ? "." : " but the name of the one who flashed you."]"
			message_admins("[key_name_admin(rev_mind.current)] ? (FLW) has been borged while being a [remove_head ? "leader" : " member"] of the revolution.")

		else
			rev_mind.current.Paralyse(5)
			rev_mind.current << "You have been brainwashed! You are no longer a revolutionary! Your memory is hazy from the time you were a rebel...the only thing you remember is the name of the one who brainwashed you..."

		update_rev_icons_removed(rev_mind)
		for(var/mob/living/M in view(rev_mind.current))
			if(beingborged)
				M << "The frame beeps contentedly, purging the hostile memory engram from the MMI before initalizing it."

			else
				M << "[rev_mind.current] looks like they just remembered their real allegiance!"

/////////////////////////////////////
//Adds the rev hud to a new convert//
/////////////////////////////////////
/datum/game_mode/proc/update_rev_icons_added(datum/mind/rev_mind)
	var/datum/atom_hud/antag/revhud = huds[ANTAG_HUD_REV]
	revhud.join_hud(rev_mind.current)
	set_antag_hud(rev_mind.current, ((rev_mind in head_revolutionaries) ? "rev_head" : "rev"))

/////////////////////////////////////////
//Removes the hud from deconverted revs//
/////////////////////////////////////////
/datum/game_mode/proc/update_rev_icons_removed(datum/mind/rev_mind)
	var/datum/atom_hud/antag/revhud = huds[ANTAG_HUD_REV]
	revhud.leave_hud(rev_mind.current)
	set_antag_hud(rev_mind.current, null)

//////////////////////////
//Checks for rev victory//
//////////////////////////
/datum/game_mode/revolution/proc/check_rev_victory()
	for(var/datum/mind/rev_mind in head_revolutionaries)
		for(var/datum/objective/mutiny/objective in rev_mind.objectives)
			if(!(objective.check_completion()))
				return 0

		return 1

/////////////////////////////
//Checks for a head victory//
/////////////////////////////
/datum/game_mode/revolution/proc/check_heads_victory()
	for(var/datum/mind/rev_mind in head_revolutionaries)
		var/turf/T = get_turf(rev_mind.current)
		if((rev_mind) && (rev_mind.current) && (rev_mind.current.stat != 2) && rev_mind.current.client && T && (T.z == ZLEVEL_STATION))
			if(ishuman(rev_mind.current))
				return 0
	return 1

//////////////////////////////////////////////////////////////////////
//Announces the end of the game with all relavent information stated//
//////////////////////////////////////////////////////////////////////
/datum/game_mode/revolution/declare_completion()
	if(finished == 1)
		feedback_set_details("round_end_result","win - heads killed")
		world << "The heads of staff were killed or exiled! The revolutionaries win!"
	else if(finished == 2)
		feedback_set_details("round_end_result","loss - rev heads killed")
		world << "The heads of staff managed to stop the revolution!"
	..()
	return 1

/datum/game_mode/proc/auto_declare_completion_revolution()
	var/list/targets = list()
	if(head_revolutionaries.len || istype(ticker.mode,/datum/game_mode/revolution))
		var/num_revs = 0
		var/num_survivors = 0
		for(var/mob/living/carbon/survivor in living_mob_list)
			if(survivor.ckey)
				num_survivors++
				if(survivor.mind)
					if((survivor.mind in head_revolutionaries) || (survivor.mind in revolutionaries))
						num_revs++
		if(num_survivors)
			world << "[TAB]Command's Approval Rating: [100 - round((num_revs/num_survivors)*100, 0.1)]%" // % of loyal crew
		var/text = "
The head revolutionaries were:"
		for(var/datum/mind/headrev in head_revolutionaries)
			text += printplayer(headrev, 1)
		text += "
"
		world << text

	if(revolutionaries.len || istype(ticker.mode,/datum/game_mode/revolution))
		var/text = "
The revolutionaries were:"
		for(var/datum/mind/rev in revolutionaries)
			text += printplayer(rev, 1)
		text += "
"
		world << text

	if( head_revolutionaries.len || revolutionaries.len || istype(ticker.mode,/datum/game_mode/revolution) )
		var/text = "
The heads of staff were:"
		var/list/heads = get_all_heads()
		for(var/datum/mind/head in heads)
			var/target = (head in targets)
			if(target)
				text += "Target"
			text += printplayer(head, 1)
		text += "
"
		world << text

license: mit


code:
/*
	This is for the sandbox for now,
	maybe useful later for an actual thing?
	-Sayu
*/

/obj/structure/door_assembly
	var/datum/airlock_maker/maker = null

/obj/structure/door_assembly/attack_hand()
	..()
	if(maker)
		maker.interact()

/datum/airlock_maker
	var/obj/structure/door_assembly/linked = null

	var/list/access_used = null
	var/require_all = 1

	var/paintjob = "none"
	var/glassdoor = 0

	var/doorname = "airlock"

/datum/airlock_maker/New(var/atom/target_loc)
	linked = new(target_loc)
	linked.maker = src
	linked.anchored = 0
	access_used = list()

	interact()

/datum/airlock_maker/proc/linkpretty(href,desc,active)
	if(!desc)
		var/static/list/defaults = list("No","Yes")
		desc = defaults[active+1]
	if(active)
		return "[desc]"
	return "[desc]"

/datum/airlock_maker/proc/interact()
	var/list/leftcolumn = list()
	var/list/rightcolumn = list()
	leftcolumn += "Required Access"
	for(var/access in get_all_accesses())
		leftcolumn += linkpretty("access=[access]",get_access_desc(access),access in access_used)
	leftcolumn += "Require all listed accesses: [linkpretty("reqall",null,require_all)]"

	rightcolumn += "Paintjob"
	for(var/option in list("none","engineering","atmos","security","command","medical","research","mining","maintenance","external","highsecurity"))
		rightcolumn += linkpretty("paint=[option]",option,option == paintjob)
	rightcolumn += "Glass door: " + linkpretty("glass",null,glassdoor) + "

"
	var/length = max(leftcolumn.len,rightcolumn.len)

	var/dat = "You may move the model airlock around.  A new airlock will be built in its space when you click done, below.

"
	dat += "Door name: \"[doorname]\""
	dat += "
"
	for(var/i=1; i<=length; i++)
		dat += "
"
		if(i<=leftcolumn.len)
			dat += leftcolumn[i]
		dat += "	"
		if(i<=rightcolumn.len)
			dat += rightcolumn[i]
		dat += "
"

	dat += "

Finalize Airlock Construction | Cancel and Destroy Airlock"
	usr << browse(dat,"window=airlockmaker")

/datum/airlock_maker/Topic(var/href,var/list/href_list)
	if(!usr) return
	if(!src || !linked || !linked.loc)
		usr << browse(null,"window=airlockmaker")
		return

	if("rename" in href_list)
		var/newname = stripped_input(usr,"New airlock name:","Name the airlock",doorname)
		if(newname)
			doorname = newname
	if("access" in href_list)
		var/value = text2num(href_list["access"])
		access_used ^= value
	if("reqall" in href_list)
		require_all = !require_all
	if("paint" in href_list)
		paintjob = href_list["paint"]
	if("glass" in href_list)
		glassdoor = !glassdoor

	if("cancel" in href_list)
		usr << browse(null,"window=airlockmaker")
		qdel(linked)
		qdel(src)
		return

	if("done" in href_list)
		usr << browse(null,"window=airlockmaker")
		var/turf/t_loc = linked.loc
		qdel(linked)
		if(!istype(t_loc))
			return

		var/target_type = "/obj/machinery/door/airlock"
		if(glassdoor)
			if(paintjob != "none")
				if(paintjob in list("external","highsecurity","maintenance")) // no glass version
					target_type += "/[paintjob]"
				else
					target_type += "/glass_[paintjob]"
			else
				target_type += "/glass"
		else if(paintjob != "none")
			target_type += "/[paintjob]"
		var/final = target_type
		target_type = text2path(final)
		if(!target_type)
			usr << "Didn't work, contact Sayu with this: [final]"
			usr << browse(null,"window=airlockmaker")
			return

		var/obj/machinery/door/D = new target_type(t_loc)

		D.name = doorname

		if(access_used.len == 0)
			D.req_access = null
			D.req_one_access = null
		else if(require_all)
			D.req_access = access_used.Copy()
			D.req_one_access = null
		else
			D.req_access = null
			D.req_one_access = access_used.Copy()

		return

	interact()


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

var/hsboxspawn = 1

/mob
	var/datum/hSB/sandbox = null
/mob/proc/CanBuild()
	if(master_mode == "sandbox")
		sandbox = new/datum/hSB
		sandbox.owner = src.ckey
		if(src.client.holder)
			sandbox.admin = 1
		verbs += new/mob/proc/sandbox_panel
/mob/proc/sandbox_panel()
	set name = "Sandbox Panel"
	if(sandbox)
		sandbox.update()

/datum/hSB
	var/owner = null
	var/admin = 0

	var/clothinfo = null
	var/reaginfo = null
	var/objinfo = null
	var/canisterinfo = null
	var/hsbinfo = null
	//items that shouldn't spawn on the floor because they would bug or act weird
	var/global/list/spawn_forbidden = list(
		/obj/item/weapon/grab, /obj/item/tk_grab, /obj/item/weapon/implant, // not implanter, the actual thing that is inside you
		/obj/item/assembly,/obj/item/device/onetankbomb, /obj/item/radio, /obj/item/device/pda/ai,
		/obj/item/device/uplink, /obj/item/smallDelivery, /obj/item/missile,/obj/item/projectile,
		/obj/item/borg/sight,/obj/item/borg/stun,/obj/item/weapon/robot_module)

/datum/hSB/proc/update()
	var/global/list/hrefs = list(
			"Space Gear",
			"Suit Up (Space Travel Gear)"		= "hsbsuit",
			"Spawn Gas Mask"					= "hsbspawn&path=[/obj/item/clothing/mask/gas]",
			"Spawn Emergency Air Tank"			= "hsbspawn&path=[/obj/item/weapon/tank/internals/emergency_oxygen/double]",

			"Standard Tools",
			"Spawn Flashlight"					= "hsbspawn&path=[/obj/item/device/flashlight]",
			"Spawn Toolbox"						= "hsbspawn&path=[/obj/item/weapon/storage/toolbox/mechanical]",
			"Spawn Light Replacer"				= "hsbspawn&path=[/obj/item/device/lightreplacer]",
			"Spawn Medical Kit"					= "hsbspawn&path=[/obj/item/weapon/storage/firstaid/regular]",
			"Spawn All-Access ID"				= "hsbaaid",

			"Building Supplies",
			"Spawn 50 Wood"                     = "hsbwood",
			"Spawn 50 Metal"					= "hsbmetal",
			"Spawn 50 Plasteel"					= "hsbplasteel",
			"Spawn 50 Reinforced Glass"         = "hsbrglass",
			"Spawn 50 Glass"					= "hsbglass",
			"Spawn Full Cable Coil"				= "hsbspawn&path=[/obj/item/stack/cable_coil]",
			"Spawn Hyper Capacity Power Cell"	= "hsbspawn&path=[/obj/item/weapon/stock_parts/cell/hyper]",
			"Spawn Inf. Capacity Power Cell"	= "hsbspawn&path=[/obj/item/weapon/stock_parts/cell/infinite]",
			"Spawn Rapid Construction Device"	= "hsbrcd",
			"Spawn RCD Ammo"					= "hsb_safespawn&path=[/obj/item/weapon/rcd_ammo]",
			"Spawn Airlock"						= "hsbairlock",

			"Miscellaneous",
			"Spawn Air Scrubber"				= "hsbscrubber",
			"Spawn Welding Fuel Tank"			= "hsbspawn&path=[/obj/structure/reagent_dispensers/fueltank]",
			"Spawn Water Tank"					= "hsbspawn&path=[/obj/structure/reagent_dispensers/watertank]",

			"Bots",
			"Spawn Cleanbot"					= "hsbspawn&path=[/mob/living/simple_animal/bot/cleanbot]",
			"Spawn Floorbot"					= "hsbspawn&path=[/mob/living/simple_animal/bot/floorbot]",
			"Spawn Medbot"						= "hsbspawn&path=[/mob/living/simple_animal/bot/medbot]",

			"Canisters",
			"Spawn O2 Canister" 				= "hsbspawn&path=[/obj/machinery/portable_atmospherics/canister/oxygen]",
			"Spawn Air Canister"				= "hsbspawn&path=[/obj/machinery/portable_atmospherics/canister/air]")

	if(!hsbinfo)
		hsbinfo = "Sandbox Panel
"
		if(admin)
			hsbinfo += "Administration
"
			hsbinfo += "- Toggle Object Spawning
"
			hsbinfo += "- Toggle Item Spawn Panel Auto-close
"
			hsbinfo += "Canister Spawning
"
			hsbinfo += "- Spawn Plasma Canister
"
			hsbinfo += "- Spawn CO2 Canister
"
			hsbinfo += "- Spawn Nitrogen Canister
"
			hsbinfo += "- Spawn N2O Canister
"
		else
			hsbinfo += "Some item spawning may be disabled by the administrators.
"
			hsbinfo += "Only administrators may spawn dangerous canisters.
"
		for(var/T in hrefs)
			var/href = hrefs[T]
			if(href)
				hsbinfo += "- [T]
"
			else
				hsbinfo += "
[T]
"
		hsbinfo += "
"
		hsbinfo += "- Spawn Clothing...
"
		hsbinfo += "- Spawn Reagent Container...
"
		hsbinfo += "- Spawn Other Item...

"

	usr << browse(hsbinfo, "window=hsbpanel")

/datum/hSB/Topic(href, href_list)
	if(!usr || !src || !(src.owner == usr.ckey))
		if(usr)
			usr << browse(null,"window=sandbox")
		return

	if(href_list["hsb"])
		switch(href_list["hsb"])
			//
			// Admin: toggle spawning
			//
			if("hsbtobj")
				if(!admin) return
				if(hsboxspawn)
					world << "Sandbox: \black[usr.key] has disabled object spawning!"
					hsboxspawn = 0
					return
				else
					world << "Sandbox: \black[usr.key] has enabled object spawning!"
					hsboxspawn = 1
					return
			//
			// Admin: Toggle auto-close
			//
			if("hsbtac")
				if(!admin) return
				if(config.sandbox_autoclose)
					world << "Sandbox: \black [usr.key] has removed the object spawn limiter."
					config.sandbox_autoclose = 0
				else
					world << "Sandbox: \black [usr.key] has added a limiter to object spawning.  The window will now auto-close after use."
					config.sandbox_autoclose = 1
				return
			//
			// Spacesuit with full air jetpack set as internals
			//
			if("hsbsuit")
				var/mob/living/carbon/human/P = usr
				if(!istype(P)) return
				if(P.wear_suit)
					P.wear_suit.loc = P.loc
					P.wear_suit.layer = initial(P.wear_suit.layer)
					P.wear_suit = null
				P.wear_suit = new/obj/item/clothing/suit/space(P)
				P.wear_suit.layer = 20
				P.update_inv_wear_suit()
				if(P.head)
					P.head.loc = P.loc
					P.head.layer = initial(P.head.layer)
					P.head = null
				P.head = new/obj/item/clothing/head/helmet/space(P)
				P.head.layer = 20
				P.update_inv_head()
				if(P.wear_mask)
					P.wear_mask.loc = P.loc
					P.wear_mask.layer = initial(P.wear_mask.layer)
					P.wear_mask = null
				P.wear_mask = new/obj/item/clothing/mask/gas(P)
				P.wear_mask.layer = 20
				P.update_inv_wear_mask()
				if(P.back)
					P.back.loc = P.loc
					P.back.layer = initial(P.back.layer)
					P.back = null
				P.back = new/obj/item/weapon/tank/jetpack/oxygen(P)
				P.back.layer = 20
				P.update_inv_back()
				P.internal = P.back
				P.update_internals_hud_icon(1)

			if("hsbscrubber") // This is beyond its normal capability but this is sandbox and you spawned one, I assume you need it
				var/obj/hsb = new/obj/machinery/portable_atmospherics/scrubber{volume_rate=50*ONE_ATMOSPHERE;on=1}(usr.loc)
				hsb.update_icon() // hackish but it wasn't meant to be spawned I guess?

			//
			// Stacked Materials
			//

			if("hsbrglass")
				new/obj/item/stack/sheet/rglass{amount=50}(usr.loc)

			if("hsbmetal")
				new/obj/item/stack/sheet/metal{amount=50}(usr.loc)

			if("hsbplasteel")
				new/obj/item/stack/sheet/plasteel{amount=50}(usr.loc)

			if("hsbglass")
				new/obj/item/stack/sheet/glass{amount=50}(usr.loc)

			if("hsbwood")
				new/obj/item/stack/sheet/mineral/wood{amount=50}(usr.loc)

			//
			// All access ID
			//
			if("hsbaaid")
				var/obj/item/weapon/card/id/gold/ID = new(usr.loc)
				ID.registered_name = usr.real_name
				ID.assignment = "Sandbox"
				ID.access = get_all_accesses()
				ID.update_label()

			//
			// RCD - starts with full clip
			// Spawn check due to grief potential (destroying floors, walls, etc)
			//
			if("hsbrcd")
				if(!hsboxspawn) return

				new/obj/item/weapon/rcd/combat(usr.loc)

			//
			// New sandbox airlock maker
			//
			if("hsbairlock")
				new /datum/airlock_maker(usr.loc)

			//
			// Object spawn window
			//

			// Clothing
			if("hsbcloth")
				if(!hsboxspawn) return

				if(!clothinfo)
					clothinfo = "Clothing (Reagent Containers) (Other Items)

"
					var/list/all_items = subtypesof(/obj/item/clothing)
					for(var/typekey in spawn_forbidden)
						all_items -= typesof(typekey)
					for(var/O in reverseRange(all_items))
						clothinfo += "[O]
"

				usr << browse(clothinfo,"window=sandbox")

			// Reagent containers
			if("hsbreag")
				if(!hsboxspawn) return

				if(!reaginfo)
					reaginfo = "Reagent Containers (Clothing) (Other Items)

"
					var/list/all_items = subtypesof(/obj/item/weapon/reagent_containers)
					for(var/typekey in spawn_forbidden)
						all_items -= typesof(typekey)
					for(var/O in reverseRange(all_items))
						reaginfo += "[O]
"

				usr << browse(reaginfo,"window=sandbox")

			// Other items
			if("hsbobj")
				if(!hsboxspawn) return

				if(!objinfo)
					objinfo = "Other Items (Clothing) (Reagent Containers)

"
					var/list/all_items = subtypesof(/obj/item/) - typesof(/obj/item/clothing) - typesof(/obj/item/weapon/reagent_containers)
					for(var/typekey in spawn_forbidden)
						all_items -= typesof(typekey)

					for(var/O in reverseRange(all_items))
						objinfo += "[O]
"

				usr << browse(objinfo,"window=sandbox")

			//
			// Safespawn checks to see if spawning is disabled.
			//
			if("hsb_safespawn")
				if(!hsboxspawn)
					usr << browse(null,"window=sandbox")
					return

				var/typepath = text2path(href_list["path"])
				if(!typepath)
					usr << "Bad path: \"[href_list["path"]]\""
					return
				new typepath(usr.loc)

				if(config.sandbox_autoclose)
					usr << browse(null,"window=sandbox")
			//
			// For everything else in the href list
			//
			if("hsbspawn")
				var/typepath = text2path(href_list["path"])
				if(!typepath)
					usr << "Bad path: \"[href_list["path"]]\""
					return
				new typepath(usr.loc)

				if(config.sandbox_autoclose)
					usr << browse(null,"window=sandbox")


code:
/datum/game_mode/sandbox
	name = "sandbox"
	config_tag = "sandbox"
	required_players = 0

/datum/game_mode/sandbox/announce()
	world << "The current game mode is - Sandbox!"
	world << "Build your own station with the sandbox-panel command!"

/datum/game_mode/sandbox/pre_setup()
	for(var/mob/M in player_list)
		M.CanBuild()
	return 1

/datum/game_mode/sandbox/post_setup()
	..()
	SSshuttle.emergencyNoEscape = 1


code:
/datum/subsystem/objects/proc/setupGenetics()
	var/list/avnums = new /list(DNA_STRUC_ENZYMES_BLOCKS)
	for(var/i=1, i<=DNA_STRUC_ENZYMES_BLOCKS, i++)
		avnums[i] = i
		CHECK_TICK

	for(var/A in subtypesof(/datum/mutation/human))
		var/datum/mutation/human/B = new A()
		if(B.dna_block == NON_SCANNABLE)
			continue
		B.dna_block = pick_n_take(avnums)
		if(B.quality == POSITIVE)
			good_mutations |= B
		else if(B.quality == NEGATIVE)
			bad_mutations |= B
		else if(B.quality == MINOR_NEGATIVE)
			not_good_mutations |= B
		CHECK_TICK

/datum/subsystem/ticker/proc/setupFactions()
	// Populate the factions list:
	for(var/typepath in typesof(/datum/faction))
		var/datum/faction/F = new typepath()
		if(!F.name)
			qdel(F)
			continue
		else
			factions.Add(F)
			availablefactions.Add(F)
		CHECK_TICK

	// Populate the syndicate coalition:
	for(var/datum/faction/syndicate/S in factions)
		syndicate_coalition.Add(S)
		CHECK_TICK


code:
/mob/living/simple_animal/ascendant_shadowling
	name = "ascendant shadowling"
	desc = "HOLY SHIT RUN THE FUCK AWAY"
	icon = 'icons/mob/mob.dmi'
	icon_state = "shadowling_ascended"
	icon_living = "shadowling_ascended"
	verb_say = "telepathically thunders"
	verb_ask = "telepathically thunders"
	verb_exclaim = "telepathically thunders"
	verb_yell = "telepathically thunders"
	force_threshold = INFINITY //Can't die by normal means
	health = 100000
	maxHealth = 100000
	speed = 0
	see_in_dark = 8
	see_invisible = SEE_INVISIBLE_MINIMUM

	response_help   = "pokes"
	response_disarm = "flails at"
	response_harm   = "flails at"

	harm_intent_damage = 0
	melee_damage_lower = 60 //Was 35, buffed
	melee_damage_upper = 60
	attacktext = "rends"
	attack_sound = 'sound/weapons/slash.ogg'

	minbodytemp = 0
	maxbodytemp = INFINITY
	environment_smash = 3

	faction = list("faithless")

/mob/living/simple_animal/ascendant_shadowling/Process_Spacemove(movement_dir = 0)
	return 1 //copypasta from carp code

/mob/living/simple_animal/ascendant_shadowling/get_spans()
	return ..() | list(SPAN_REALLYBIG, SPAN_YELL) //MAKES THEM SHOUT WHEN THEY TALK

/mob/living/simple_animal/ascendant_shadowling/ex_act(severity)
	return 0 //You think an ascendant can be hurt by bombs? HA

/mob/living/simple_animal/ascendant_shadowling/singularity_act()
	return 0 //Well hi, fellow god! How are you today?


code:
#define LIGHT_DAM_THRESHOLD 4
#define LIGHT_HEAL_THRESHOLD 2
#define LIGHT_DAMAGE_TAKEN 7

/*

SHADOWLING: A gamemode based on previously-run events

Aliens called shadowlings are on the station.
These shadowlings can 'enthrall' crew members and enslave them.
They also burn in the light but heal rapidly whilst in the dark.
The game will end under two conditions:
	1. The shadowlings die
	2. The emergency shuttle docks at CentCom

Shadowling strengths:
	- The dark
	- Hard vacuum (They are not affected by it, but are affected by starlight!)
	- Their thralls who are not harmed by the light
	- Stealth

Shadowling weaknesses:
	- The light
	- Fire
	- Enemy numbers
	- Burn-based weapons and items (flashbangs, lasers, etc.)

Shadowlings start off disguised as normal crew members, and they only have two abilities: Hatch and Enthrall.
They can still enthrall and perhaps complete their objectives in this form.
Hatch will, after a short time, cast off the human disguise and assume the shadowling's true identity.
They will then assume the normal shadowling form and gain their abilities.

The shadowling will seem OP, and that's because it kinda is. Being restricted to the dark while being alone most of the time is extremely difficult and as such the shadowling needs powerful abilities.
Made by Xhuis

*/

/*
	GAMEMODE
*/

/datum/game_mode
	var/list/datum/mind/shadows = list()
	var/list/datum/mind/thralls = list()
	var/list/shadow_objectives = list()
	var/required_thralls = 15 //How many thralls are needed (hardcoded for now)
	var/shadowling_ascended = 0 //If at least one shadowling has ascended
	var/shadowling_dead = 0 //is shadowling kill
	var/objective_explanation

/proc/is_thrall(var/mob/living/M)
	return istype(M) && M.mind && ticker && ticker.mode && (M.mind in ticker.mode.thralls)

/proc/is_shadow_or_thrall(var/mob/living/M)
	return istype(M) && M.mind && ticker && ticker.mode && ((M.mind in ticker.mode.thralls) || (M.mind in ticker.mode.shadows))

/proc/is_shadow(var/mob/living/M)
	return istype(M) && M.mind && ticker && ticker.mode && (M.mind in ticker.mode.shadows)

/datum/game_mode/shadowling
	name = "shadowling"
	config_tag = "shadowling"
	antag_flag = ROLE_SHADOWLING
	required_players = 30
	required_enemies = 2
	recommended_enemies = 2
	restricted_jobs = list("AI", "Cyborg")
	protected_jobs = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain")

/datum/game_mode/shadowling/announce()
	world << "The current game mode is - Shadowling!"
	world << "There are alien shadowlings on the station. Crew: Kill the shadowlings before they can enthrall the crew. Shadowlings: Enthrall the crew while remaining in hiding."

/datum/game_mode/shadowling/pre_setup()
	if(config.protect_roles_from_antagonist)
		restricted_jobs += protected_jobs

	if(config.protect_assistant_from_antagonist)
		restricted_jobs += "Assistant"

	var/shadowlings = max(3, round(num_players()/14))

	while(shadowlings)
		var/datum/mind/shadow = pick(antag_candidates)
		shadows += shadow
		antag_candidates -= shadow
		modePlayer += shadow
		shadow.special_role = "Shadowling"
		shadow.restricted_roles = restricted_jobs
		shadowlings--
	return 1

/datum/game_mode/shadowling/post_setup()
	for(var/datum/mind/shadow in shadows)
		log_game("[shadow.key] (ckey) has been selected as a Shadowling.")
		sleep(10)
		shadow.current << "
"
		shadow.current << "You are a shadowling!"
		greet_shadow(shadow)
		finalize_shadowling(shadow)
		process_shadow_objectives(shadow)
		//give_shadowling_abilities(shadow)
	..()
	return

/datum/game_mode/proc/greet_shadow(datum/mind/shadow)
	shadow.current << "Currently, you are disguised as an employee aboard [station_name()]]."
	shadow.current << "In your limited state, you have three abilities: Enthrall, Hatch, and Hivemind Commune."
	shadow.current << "Any other shadowlings are your allies. You must assist them as they shall assist you."
	shadow.current << "If you are new to shadowling, or want to read about abilities, check the wiki page at https://tgstation13.org/wiki/Shadowling
"

/datum/game_mode/proc/process_shadow_objectives(datum/mind/shadow_mind)
	var/objective = "enthrall" //may be devour later, but for now it seems murderbone-y

	if(objective == "enthrall")
		objective_explanation = "Ascend to your true form by use of the Ascendance ability. This may only be used with [required_thralls] collective thralls, while hatched, and is unlocked with the Collective Mind ability."
		shadow_objectives += "enthrall"
		shadow_mind.memory += "Objective #1: [objective_explanation]"
		shadow_mind.current << "Objective #1: [objective_explanation]
"

/datum/game_mode/proc/finalize_shadowling(datum/mind/shadow_mind)
	var/mob/living/carbon/human/S = shadow_mind.current
	shadow_mind.AddSpell(new /obj/effect/proc_holder/spell/self/shadowling_hatch(null))
	shadow_mind.AddSpell(new /obj/effect/proc_holder/spell/self/shadowling_hivemind(null))
	spawn(0)
		update_shadow_icons_added(shadow_mind)
		if(shadow_mind.assigned_role == "Clown")
			S << "Your alien nature has allowed you to overcome your clownishness."
			S.dna.remove_mutation(CLOWNMUT)

/datum/game_mode/proc/add_thrall(datum/mind/new_thrall_mind)
	if(!istype(new_thrall_mind))
		return 0
	if(!(new_thrall_mind in thralls))
		update_shadow_icons_added(new_thrall_mind)
		thralls += new_thrall_mind
		new_thrall_mind.special_role = "thrall"
		new_thrall_mind.current.attack_log += "\[[time_stamp()]\] Became a thrall"
		new_thrall_mind.AddSpell(new /obj/effect/proc_holder/spell/self/lesser_shadowling_hivemind(null))
		new_thrall_mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/lesser_glare(null))
		new_thrall_mind.AddSpell(new /obj/effect/proc_holder/spell/self/lesser_shadow_walk(null))
		new_thrall_mind.AddSpell(new /obj/effect/proc_holder/spell/self/thrall_vision(null))
		new_thrall_mind.current << "You see the truth. Reality has been torn away and you realize what a fool you've been."
		new_thrall_mind.current << "The shadowlings are your masters. Serve them above all else and ensure they complete their goals."
		new_thrall_mind.current << "You may not harm other thralls or the shadowlings. However, you do not need to obey other thralls."
		new_thrall_mind.current << "Your body has been irreversibly altered. The attentive can see this - you may conceal it by wearing a mask."
		new_thrall_mind.current << "Though not nearly as powerful as your masters, you possess some weak powers. These can be found in the Thrall Abilities tab."
		new_thrall_mind.current << "You may communicate with your allies by using the Lesser Commune ability."
		if(jobban_isbanned(new_thrall_mind.current, ROLE_SHADOWLING))
			replace_jobbaned_player(new_thrall_mind.current, ROLE_SHADOWLING, ROLE_SHADOWLING)
		return 1

/datum/game_mode/proc/remove_thrall(datum/mind/thrall_mind, var/kill = 0)
	if(!istype(thrall_mind) || !(thrall_mind in thralls) || !isliving(thrall_mind.current)) return 0 //If there is no mind, the mind isn't a thrall, or the mind's mob isn't alive, return
	update_shadow_icons_removed(thrall_mind)
	thralls.Remove(thrall_mind)
	thrall_mind.current.attack_log += "\[[time_stamp()]\] Dethralled"
	thrall_mind.special_role = null
	for(var/obj/effect/proc_holder/spell/S in thrall_mind.spell_list)
		thrall_mind.RemoveSpell(S)
	if(kill && ishuman(thrall_mind.current)) //If dethrallization surgery fails, kill the mob as well as dethralling them
		var/mob/living/carbon/human/H = thrall_mind.current
		H.visible_message("[H] jerks violently and falls still.", \
						  "A piercing white light floods your mind, banishing your memories as a thrall and--")
		H.death()
		return 1
	var/mob/living/M = thrall_mind.current
	if(issilicon(M))
		M.audible_message("[M] lets out a short blip.", \
						  "You have been turned into a robot! You are no longer a thrall! Though you try, you cannot remember anything about your servitude...")
	else
		M.visible_message("[M] looks like their mind is their own again!", \
						  "A piercing white light floods your eyes. Your mind is your own again! Though you try, you cannot remember anything about the shadowlings or your time \
						  under their command...")
	return 1

/datum/game_mode/proc/remove_shadowling(datum/mind/ling_mind)
	if(!istype(ling_mind) || !(ling_mind in shadows)) return 0
	update_shadow_icons_removed(ling_mind)
	shadows.Remove(ling_mind)
	ling_mind.current.attack_log += "\[[time_stamp()]\] Deshadowlinged"
	ling_mind.special_role = null
	for(var/obj/effect/proc_holder/spell/S in ling_mind.spell_list)
		ling_mind.RemoveSpell(S)
	var/mob/living/M = ling_mind.current
	if(issilicon(M))
		M.audible_message("[M] lets out a short blip.", \
						  "You have been turned into a robot! You are no longer a shadowling! Though you try, you cannot remember anything about your time as one...")
	else
		M.visible_message("[M] screams and contorts!", \
						  "THE LIGHT-- YOUR MIND-- BURNS--")
		spawn(30)
			if(!M || qdeleted(M))
				return
			M.visible_message("[M] suddenly bloats and explodes!", \
							  "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA----")
			playsound(M, 'sound/magic/Disintegrate.ogg', 100, 1)
			M.gib()

/datum/game_mode/shadowling/proc/check_shadow_victory()
	var/success = 0 //Did they win?
	if(shadow_objectives.Find("enthrall"))
		success = shadowling_ascended
	return success

/datum/game_mode/shadowling/declare_completion()
	if(check_shadow_victory() && SSshuttle.emergency.mode >= SHUTTLE_ESCAPE) //Doesn't end instantly - this is hacky and I don't know of a better way ~X
		world << "The shadowlings have ascended and taken over the station!"
	else if(shadowling_dead && !check_shadow_victory()) //If the shadowlings have ascended, they can not lose the round
		world << "The shadowlings have been killed by the crew!"
	else if(!check_shadow_victory() && SSshuttle.emergency.mode >= SHUTTLE_ESCAPE)
		world << "The crew escaped the station before the shadowlings could ascend!"
	else
		world << "The shadowlings have failed!"
	..()
	return 1

/datum/game_mode/proc/auto_declare_completion_shadowling()
	var/text = ""
	if(shadows.len)
		text += "
The shadowlings were:"
		for(var/datum/mind/shadow in shadows)
			text += printplayer(shadow)
		text += "
"
		if(thralls.len)
			text += "
The thralls were:"
			for(var/datum/mind/thrall in thralls)
				text += printplayer(thrall)
	text += "
"
	world << text

/*
	MISCELLANEOUS
*/

/datum/species/shadow/ling
	//Normal shadowpeople but with enhanced effects
	name = "Shadowling"
	id = "shadowling"
	say_mod = "chitters"
	specflags = list(NOBREATH,NOBLOOD,RADIMMUNE,NOGUNS) //Can't use guns due to muzzle flash
	burnmod = 1.5 //1.5x burn damage, 2x is excessive
	heatmod = 1.5

/datum/species/shadow/ling/spec_life(mob/living/carbon/human/H)
	if(!H.weakeyes) H.weakeyes = 1 //Makes them more vulnerable to flashes and flashbangs
	var/light_amount = 0
	H.nutrition = NUTRITION_LEVEL_WELL_FED //i aint never get hongry
	if(isturf(H.loc))
		var/turf/T = H.loc
		light_amount = T.get_lumcount()
		if(light_amount > LIGHT_DAM_THRESHOLD && !H.incorporeal_move) //Can survive in very small light levels. Also doesn't take damage while incorporeal, for shadow walk purposes
			H.take_overall_damage(0, LIGHT_DAMAGE_TAKEN)
			if(H.stat != DEAD)
				H << "The light burns you!" //Message spam to say "GET THE FUCK OUT"
				H << 'sound/weapons/sear.ogg'
		else if (light_amount < LIGHT_HEAL_THRESHOLD)
			H.heal_overall_damage(5,5)
			H.adjustToxLoss(-5)
			H.adjustBrainLoss(-25) //Shad O. Ling gibbers, "CAN U BE MY THRALL?!!"
			H.adjustCloneLoss(-1)
			H.SetWeakened(0)
			H.SetStunned(0)

/datum/species/shadow/ling/lesser //Empowered thralls. Obvious, but powerful
	name = "Lesser Shadowling"
	id = "l_shadowling"
	say_mod = "chitters"
	specflags = list(NOBREATH,NOBLOOD,RADIMMUNE)
	burnmod = 1.1
	heatmod = 1.1

/datum/species/shadow/ling/lesser/spec_life(mob/living/carbon/human/H)
	if(!H.weakeyes) H.weakeyes = 1
	var/light_amount = 0
	H.nutrition = NUTRITION_LEVEL_WELL_FED //i aint never get hongry
	if(isturf(H.loc))
		var/turf/T = H.loc
		light_amount = T.get_lumcount()
		if(light_amount > LIGHT_DAM_THRESHOLD && !H.incorporeal_move)
			H.take_overall_damage(0, LIGHT_DAMAGE_TAKEN/2)
		else if (light_amount < LIGHT_HEAL_THRESHOLD)
			H.heal_overall_damage(2,2)
			H.adjustToxLoss(-5)
			H.adjustBrainLoss(-25)
			H.adjustCloneLoss(-1)

/datum/game_mode/proc/update_shadow_icons_added(datum/mind/shadow_mind)
	var/datum/atom_hud/antag/shadow_hud = huds[ANTAG_HUD_SHADOW]
	shadow_hud.join_hud(shadow_mind.current)
	set_antag_hud(shadow_mind.current, ((shadow_mind in shadows) ? "shadowling" : "thrall"))

/datum/game_mode/proc/update_shadow_icons_removed(datum/mind/shadow_mind)
	var/datum/atom_hud/antag/shadow_hud = huds[ANTAG_HUD_SHADOW]
	shadow_hud.leave_hud(shadow_mind.current)
	set_antag_hud(shadow_mind.current, null)


code:
#define EMPOWERED_THRALL_LIMIT 5

/obj/effect/proc_holder/spell/proc/shadowling_check(var/mob/living/carbon/human/H)
	if(!H || !istype(H)) return
	if(H.dna.species.id == "shadowling" && is_shadow(H)) return 1
	if(H.dna.species.id == "l_shadowling" && is_thrall(H)) return 1
	if(!is_shadow_or_thrall(usr)) usr << "You can't wrap your head around how to do this."
	else if(is_thrall(usr)) usr << "You aren't powerful enough to do this."
	else if(is_shadow(usr)) usr << "Your telepathic ability is suppressed. Hatch or use Rapid Re-Hatch first."
	return 0

/obj/effect/proc_holder/spell/targeted/glare //Stuns and mutes a human target for 10 seconds
	name = "Glare"
	desc = "Stuns and mutes a target for a decent duration."
	panel = "Shadowling Abilities"
	charge_max = 300
	human_req = 1
	clothes_req = 0
	action_icon_state = "glare"

/obj/effect/proc_holder/spell/targeted/glare/cast(list/targets,mob/user = usr)
	for(var/mob/living/target in targets)
		if(!ishuman(target))
			user << "You may only glare at humans!"
			revert_cast()
			return
		if(!shadowling_check(user))
			revert_cast()
			return
		if(target.stat)
			user << "[target] must be conscious!"
			revert_cast()
			return
		if(is_shadow_or_thrall(target))
			user << "You cannot glare at allies!"
			revert_cast()
			return
		var/mob/living/L = user
		if(L.incorporeal_move) //Other abilities can still be used, but glare needed balancing
			user << "You cannot glare while shadow walking!"
			revert_cast()
			return
		var/mob/living/carbon/human/M = target
		user.visible_message("[user]'s eyes flash a blinding red!")
		target.visible_message("[target] freezes in place, their eyes glazing over...")
		if(in_range(target, user))
			target << "Your gaze is forcibly drawn into [user]'s eyes, and you are mesmerized by the heavenly lights..."
		else //Only alludes to the shadowling if the target is close by
			target << "Red lights suddenly dance in your vision, and you are mesmerized by their heavenly beauty..."
		target.Stun(10)
		M.silent += 10

/obj/effect/proc_holder/spell/aoe_turf/veil //Puts out most nearby lights except for flares and yellow slime cores
	name = "Veil"
	desc = "Extinguishes most nearby light sources."
	panel = "Shadowling Abilities"
	charge_max = 150 //Short cooldown because people can just turn the lights back on
	human_req = 1
	clothes_req = 0
	range = 5
	action_icon_state = "veil"
	var/blacklisted_lights = list(/obj/item/device/flashlight/flare, /obj/item/device/flashlight/slime)
	var/admin_override = 0 //Requested by Shadowlight213. Allows anyone to cast the spell, not just shadowlings.

/obj/effect/proc_holder/spell/aoe_turf/veil/proc/extinguishItem(obj/item/I) //Does not darken items held by mobs due to mobs having separate luminosity, use extinguishMob() or write your own proc.
	if(istype(I, /obj/item/device/flashlight))
		var/obj/item/device/flashlight/F = I
		if(F.on)
			if(is_type_in_list(I, blacklisted_lights))
				I.visible_message("[I] dims slightly before scattering the shadows around it.")
				return F.brightness_on //Necessary because flashlights become 0-luminosity when held.  I don't make the rules of lightcode.
			F.on = 0
			F.update_brightness()
	else if(istype(I, /obj/item/device/pda))
		var/obj/item/device/pda/P = I
		P.fon = 0
	I.SetLuminosity(0)
	return I.luminosity

/obj/effect/proc_holder/spell/aoe_turf/veil/proc/extinguishMob(mob/living/H)
	var/blacklistLuminosity = 0
	for(var/obj/item/F in H)
		blacklistLuminosity += extinguishItem(F)
	H.SetLuminosity(blacklistLuminosity) //I hate lightcode for making me do it this way

/obj/effect/proc_holder/spell/aoe_turf/veil/cast(list/targets,mob/user = usr)
	if(!shadowling_check(user) && !admin_override)
		revert_cast()
		return
	user << "You silently disable all nearby lights."
	for(var/turf/T in targets)
		for(var/obj/item/F in T.contents)
			extinguishItem(F)
		for(var/obj/machinery/light/L in T.contents)
			L.on = 0
			L.visible_message("[L] flickers and falls dark.")
			L.update(0)
		for(var/obj/machinery/computer/C in T.contents)
			C.SetLuminosity(0)
			C.visible_message("[C] grows dim, its screen barely readable.")
		for(var/obj/effect/glowshroom/G in orange(7, user)) //High radius because glowshroom spam wrecks shadowlings
			G.visible_message("[G] withers away!")
			qdel(G)
		for(var/mob/living/H in T.contents)
			extinguishMob(H)
		for(var/mob/living/silicon/robot/borgie in T.contents)
			borgie.update_headlamp(1)

/obj/effect/proc_holder/spell/self/shadow_walk //Grants the shadowling invisibility and phasing for 4 seconds
	name = "Shadow Walk"
	desc = "Phases you into the space between worlds for a short time, allowing movement through walls and invisbility."
	panel = "Shadowling Abilities"
	charge_max = 300 //Used to be twice this, buffed
	human_req = 1
	clothes_req = 0
	action_icon_state = "shadow_walk"
	sound = 'sound/effects/bamf.ogg'

/obj/effect/proc_holder/spell/self/shadow_walk/cast(mob/living/carbon/human/user)
	if(!shadowling_check(user))
		revert_cast()
		return
	user.visible_message("[user] vanishes in a puff of black mist!", "You enter the space between worlds as a tunnel.")
	user.SetStunned(0)
	user.SetWeakened(0)
	user.incorporeal_move = 1
	user.alpha = 0
	user.ExtinguishMob()
	var/turf/T = get_turf(user)
	user.forceMove(T) //to properly move the mob out of a potential container
	if(user.buckled)
		user.buckled.unbuckle_mob(user,force=1)
	if(user.pulledby)
		user.pulledby.stop_pulling()
	user.stop_pulling()
	if(user.buckled_mobs.len)
		user.unbuckle_all_mobs(force=1)
	sleep(40) //4 seconds
	if(!qdeleted(user))
		user.visible_message("[user] suddenly manifests!", "The rift's pressure forces you back to corporeality.")
		user.incorporeal_move = 0
		user.alpha = 255
		user.forceMove(user.loc)

/obj/effect/proc_holder/spell/aoe_turf/flashfreeze //Stuns and freezes nearby people - a bit more effective than a changeling's cryosting
	name = "Icy Veins"
	desc = "Instantly freezes the blood of nearby people, stunning them and causing burn damage."
	panel = "Shadowling Abilities"
	range = 5
	charge_max = 250
	human_req = 1
	clothes_req = 0
	action_icon_state = "icy_veins"
	sound = 'sound/effects/ghost2.ogg'

/obj/effect/proc_holder/spell/aoe_turf/flashfreeze/cast(list/targets,mob/user = usr)
	if(!shadowling_check(user))
		revert_cast()
		return
	user << "You freeze the nearby air."
	for(var/turf/T in targets)
		for(var/mob/living/carbon/M in T.contents)
			if(is_shadow_or_thrall(M))
				if(M == user) //No message for the user, of course
					continue
				else
					M << "You feel a blast of paralyzingly cold air wrap around you and flow past, but you are unaffected!"
					continue
			M << "A wave of shockingly cold air engulfs you!"
			M.Stun(2)
			M.apply_damage(10, BURN)
			if(M.bodytemperature)
				M.bodytemperature -= 200 //Extreme amount of initial cold
			if(M.reagents)
				M.reagents.add_reagent("frostoil", 15) //Half of a cryosting

/obj/effect/proc_holder/spell/targeted/enthrall //Turns a target into the shadowling's slave. This overrides all previous loyalties
	name = "Enthrall"
	desc = "Allows you to enslave a conscious, non-braindead, non-catatonic human to your will. This takes some time to cast."
	panel = "Shadowling Abilities"
	charge_max = 0
	human_req = 1
	clothes_req = 0
	range = 1 //Adjacent to user
	action_icon_state = "enthrall"
	var/enthralling = 0

/obj/effect/proc_holder/spell/targeted/enthrall/cast(list/targets,mob/living/carbon/human/user = usr)
	listclearnulls(ticker.mode.thralls)
	if(!(user.mind in ticker.mode.shadows)) return
	if(user.dna.species.id != "shadowling")
		if(ticker.mode.thralls.len >= 5)
			revert_cast()
			return
	for(var/mob/living/carbon/human/target in targets)
		if(!in_range(user, target))
			user << "You need to be closer to enthrall [target]!"
			revert_cast()
			return
		if(!target.key || !target.mind)
			user << "The target has no mind!"
			revert_cast()
			return
		if(target.stat)
			user << "The target must be conscious!"
			revert_cast()
			return
		if(is_shadow_or_thrall(target))
			user << "You can not enthrall allies!"
			revert_cast()
			return
		if(!ishuman(target))
			user << "You can only enthrall humans!"
			revert_cast()
			return
		if(enthralling)
			user << "You are already enthralling!"
			revert_cast()
			return
		if(!target.client)
			user << "[target]'s mind is vacant of activity."
		enthralling = 1
		user << "This target is valid. You begin the enthralling..."
		target << "[user] stares at you. You feel your head begin to pulse."

		for(var/progress = 0, progress <= 3, progress++)
			switch(progress)
				if(1)
					user << "You place your hands to [target]'s head..."
					user.visible_message("[user] places their hands onto the sides of [target]'s head!")
				if(2)
					user << "You begin preparing [target]'s mind as a blank slate..."
					user.visible_message("[user]'s palms flare a bright red against [target]'s temples!")
					target << "A terrible red light floods your mind. You collapse as conscious thought is wiped away."
					target.Weaken(12)
					sleep(20)
					if(isloyal(target))
						user << "They are enslaved by Nanotrasen. You begin to shut down the nanobot implant - this will take some time."
						user.visible_message("[user] pauses, then dips their head in concentration!")
						target << "You feel your loyalties begin to weaken!"
						sleep(100) //10 seconds - not spawn() so the enthralling takes longer
						user << "The nanobots composing the loyalty implant have been rendered inert. Now to continue."
						user.visible_message("[user] relaxes again.")
						for(var/obj/item/weapon/implant/loyalty/L in target)
							if(L && L.implanted)
								qdel(L)
						target << "Your unwavering loyalty to Nanotrasen unexpectedly falters, dims, dies."
				if(3)
					user << "You begin planting the tumor that will control the new thrall..."
					user.visible_message("A strange energy passes from [user]'s hands into [target]'s head!")
					target << "You feel your memories twisting, morphing. A sense of horror dominates your mind."
			if(!do_mob(user, target, 70)) //around 21 seconds total for enthralling, 31 for someone with a loyalty implant
				user << "The enthralling has been interrupted - your target's mind returns to its previous state."
				target << "You wrest yourself away from [user]'s hands and compose yourself"
				enthralling = 0
				return

		enthralling = 0
		user << "You have enthralled [target.real_name]!"
		target.visible_message("[target] looks to have experienced a revelation!", \
							   "False faces all dark not real not real not--")
		target.setOxyLoss(0) //In case the shadowling was choking them out
		target.mind.special_role = "thrall"
		ticker.mode.add_thrall(target.mind)

/obj/effect/proc_holder/spell/self/shadowling_hivemind //Lets a shadowling talk to its allies
	name = "Hivemind Commune"
	desc = "Allows you to silently communicate with all other shadowlings and thralls."
	panel = "Shadowling Abilities"
	charge_max = 0
	human_req = 1
	clothes_req = 0
	action_icon_state = "commune"

/obj/effect/proc_holder/spell/self/shadowling_hivemind/cast(mob/living/user,mob/user = usr)
	if(!is_shadow(user))
		user << "You must be a shadowling to do that!"
		return
	var/text = stripped_input(user, "What do you want to say your thralls and fellow shadowlings?.", "Hive Chat", "")
	if(!text)
		return
	var/my_message = "\[Shadowling\] [user.real_name]: [text]"
	for(var/mob/M in mob_list)
		if(is_shadow_or_thrall(M))
			M << my_message
		if(M in dead_mob_list)
			M << "(F) [my_message]"

/obj/effect/proc_holder/spell/self/shadowling_regenarmor //Resets a shadowling's species to normal, removes genetic defects, and re-equips their armor
	name = "Rapid Re-Hatch"
	desc = "Re-forms protective chitin that may be lost during cloning or similar processes."
	panel = "Shadowling Abilities"
	charge_max = 600
	human_req = 1
	clothes_req = 0
	action_icon_state = "regen_armor"

/obj/effect/proc_holder/spell/self/shadowling_regenarmor/cast(mob/living/carbon/human/user)
	if(!is_shadow(user))
		user << "You must be a shadowling to do this!"
		revert_cast()
		return
	user.visible_message("[user]'s skin suddenly bubbles and shifts around their body!", \
						 "You regenerate your protective armor and cleanse your form of defects.")
	user.adjustCloneLoss(user.getCloneLoss())
	user.equip_to_slot_or_del(new /obj/item/clothing/under/shadowling(user), slot_w_uniform)
	user.equip_to_slot_or_del(new /obj/item/clothing/shoes/shadowling(user), slot_shoes)
	user.equip_to_slot_or_del(new /obj/item/clothing/suit/space/shadowling(user), slot_wear_suit)
	user.equip_to_slot_or_del(new /obj/item/clothing/head/shadowling(user), slot_head)
	user.equip_to_slot_or_del(new /obj/item/clothing/gloves/shadowling(user), slot_gloves)
	user.equip_to_slot_or_del(new /obj/item/clothing/mask/gas/shadowling(user), slot_wear_mask)
	user.equip_to_slot_or_del(new /obj/item/clothing/glasses/night/shadowling(user), slot_glasses)
	user.set_species(/datum/species/shadow/ling)

/obj/effect/proc_holder/spell/self/collective_mind //Lets a shadowling bring together their thralls' strength, granting new abilities and a headcount
	name = "Collective Hivemind"
	desc = "Gathers the power of all of your thralls and compares it to what is needed for ascendance. Also gains you new abilities."
	panel = "Shadowling Abilities"
	charge_max = 300 //30 second cooldown to prevent spam
	human_req = 1
	clothes_req = 0
	action_icon_state = "collective_mind"
	var/blind_smoke_acquired
	var/screech_acquired
	var/drainLifeAcquired
	var/reviveThrallAcquired

/obj/effect/proc_holder/spell/self/collective_mind/cast(mob/living/carbon/human/user)
	if(!shadowling_check(user))
		revert_cast()
		return
	var/thralls = 0
	var/victory_threshold = 15
	var/mob/M

	user << "You focus your telepathic energies abound, harnessing and drawing together the strength of your thralls."

	for(M in living_mob_list)
		if(is_thrall(M))
			thralls++
			M << "You feel hooks sink into your mind and pull."

	if(!do_after(user, 30, target = user))
		user << "Your concentration has been broken. The mental hooks you have sent out now retract into your mind."
		return

	if(thralls >= 3 && !screech_acquired)
		screech_acquired = 1
		user << "The power of your thralls has granted you the Sonic Screech ability. This ability will shatter nearby windows and deafen enemies, plus stunning silicon lifeforms."
		user.mind.AddSpell(new /obj/effect/proc_holder/spell/aoe_turf/unearthly_screech(null))

	if(thralls >= 5 && !blind_smoke_acquired)
		blind_smoke_acquired = 1
		user << "The power of your thralls has granted you the Blinding Smoke ability. It will create a choking cloud that will blind any non-thralls who enter. \
			"
		user.mind.AddSpell(new /obj/effect/proc_holder/spell/self/blindness_smoke(null))

	if(thralls >= 7 && !drainLifeAcquired)
		drainLifeAcquired = 1
		user << "The power of your thralls has granted you the Drain Life ability. You can now drain the health of nearby humans to heal yourself."
		user.mind.AddSpell(new /obj/effect/proc_holder/spell/aoe_turf/drain_life(null))

	if(thralls >= 9 && !reviveThrallAcquired)
		reviveThrallAcquired = 1
		user << "The power of your thralls has granted you the Black Recuperation ability. This will, after a short time, bring a dead thrall completely back to life \
		with no bodily defects."
		user.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/revive_thrall(null))

	if(thralls < victory_threshold)
		user << "You do not have the power to ascend. You require [victory_threshold] thralls, but only [thralls] living thralls are present."

	else if(thralls >= victory_threshold)
		user << "You are now powerful enough to ascend. Use the Ascendance ability when you are ready. This will kill all of your thralls."
		user << "You may find Ascendance in the Shadowling Evolution tab."
		for(M in living_mob_list)
			if(is_shadow(M))
				var/obj/effect/proc_holder/spell/self/collective_mind/CM
				if(CM in M.mind.spell_list)
					M.mind.spell_list -= CM
					qdel(CM)
				M.mind.RemoveSpell(/obj/effect/proc_holder/spell/self/shadowling_hatch)
				M.mind.AddSpell(new /obj/effect/proc_holder/spell/self/shadowling_ascend(null))
				if(M == user)
					M << "You project this power to the rest of the shadowlings."
				else
					M << "[user.real_name] has coalesced the strength of the thralls. You can draw upon it at any time to ascend. (Shadowling Evolution Tab)" //Tells all the other shadowlings

/obj/effect/proc_holder/spell/self/blindness_smoke //Spawns a cloud of smoke that blinds non-thralls/shadows and grants slight healing to shadowlings and their allies
	name = "Blindness Smoke"
	desc = "Spews a cloud of smoke which will blind enemies."
	panel = "Shadowling Abilities"
	charge_max = 600
	human_req = 1
	clothes_req = 0
	action_icon_state = "black_smoke"
	sound = 'sound/effects/bamf.ogg'

/obj/effect/proc_holder/spell/self/blindness_smoke/cast(mob/living/carbon/human/user) //Extremely hacky
	if(!shadowling_check(user))
		revert_cast()
		return
	user.visible_message("[user] bends over and coughs out a cloud of black smoke!")
	user << "You regurgitate a vast cloud of blinding smoke."
	var/obj/item/weapon/reagent_containers/glass/beaker/large/B = new /obj/item/weapon/reagent_containers/glass/beaker/large(user.loc) //hacky
	B.reagents.clear_reagents() //Just in case!
	B.invisibility = INFINITY //This ought to do the trick
	B.reagents.add_reagent("blindness_smoke", 10)
	var/datum/effect_system/smoke_spread/chem/S = new
	S.attach(B)
	if(S)
		S.set_up(B.reagents, 4, 0, B.loc)
		S.start()
	qdel(B)

/obj/effect/proc_holder/spell/aoe_turf/unearthly_screech //Damages nearby windows, confuses nearby carbons, and outright stuns silly cones
	name = "Sonic Screech"
	desc = "Deafens, stuns, and confuses nearby people. Also shatters windows."
	panel = "Shadowling Abilities"
	range = 7
	charge_max = 300
	human_req = 1
	clothes_req = 0
	action_icon_state = "screech"
	sound = 'sound/effects/screech.ogg'

/obj/effect/proc_holder/spell/aoe_turf/unearthly_screech/cast(list/targets,mob/user = usr)
	if(!shadowling_check(user))
		revert_cast()
		return
	user.audible_message("[user] lets out a horrible scream!")
	for(var/turf/T in targets)
		for(var/mob/target in T.contents)
			if(is_shadow_or_thrall(target))
				if(target == user) //No message for the user, of course
					continue
				else
					continue
			if(iscarbon(target))
				var/mob/living/carbon/M = target
				M << "A spike of pain drives into your head and scrambles your thoughts!"
				M.confused += 10
				M.setEarDamage(M.ear_damage + 3)
			else if(issilicon(target))
				var/mob/living/silicon/S = target
				S << "ERROR $!(@ ERROR )#^! SENSORY OVERLOAD \[$(!@#"
				S << 'sound/misc/interference.ogg'
				playsound(S, 'sound/machines/warning-buzzer.ogg', 50, 1)
				var/datum/effect_system/spark_spread/sp = new /datum/effect_system/spark_spread
				sp.set_up(5, 1, S)
				sp.start()
				S.Weaken(6)
		for(var/obj/structure/window/W in T.contents)
			W.hit(rand(80, 100))

/obj/effect/proc_holder/spell/aoe_turf/drain_life //Deals stamina and oxygen damage to nearby humans and heals the shadowling. On a short cooldown because of the small range and situational usefulness
	name = "Drain Life"
	desc = "Damages nearby humans, draining their life and healing your own wounds."
	panel = "Shadowling Abilities"
	range = 3
	charge_max = 100
	human_req = 1
	clothes_req = 0
	action_icon_state = "drain_life"
	var/targetsDrained
	var/list/nearbyTargets

/obj/effect/proc_holder/spell/aoe_turf/drain_life/cast(list/targets, mob/living/carbon/human/user = usr)
	if(!shadowling_check(user))
		revert_cast()
		return
	targetsDrained = 0
	nearbyTargets = list()
	for(var/turf/T in targets)
		for(var/mob/living/carbon/M in T.contents)
			if(M == user) continue
			targetsDrained++
			nearbyTargets.Add(M)
	if(!targetsDrained)
		revert_cast()
		user << "There were no nearby humans for you to drain."
		return
	for(var/mob/living/carbon/M in nearbyTargets)
		user.heal_organ_damage(10, 10)
		user.adjustToxLoss(-10)
		user.adjustOxyLoss(-10)
		user.adjustStaminaLoss(-20)
		user.AdjustWeakened(-1)
		user.AdjustStunned(-1)
		M.adjustOxyLoss(20)
		M.adjustStaminaLoss(20)
		M << "You feel a wave of exhaustion and a curious draining sensation directed towards [usr]!"
	user << "You draw life from those around you to heal your wounds."

/obj/effect/proc_holder/spell/targeted/revive_thrall //Completely revives a dead thrall
	name = "Black Recuperation"
	desc = "Revives or empowers a thrall."
	panel = "Shadowling Abilities"
	range = 1
	charge_max = 600
	human_req = 1
	clothes_req = 0
	include_user = 0
	action_icon_state = "revive_thrall"

/obj/effect/proc_holder/spell/targeted/revive_thrall/cast(list/targets,mob/user = usr)
	if(!shadowling_check(user))
		revert_cast()
		return
	for(var/mob/living/carbon/human/thrallToRevive in targets)
		var/choice = alert(user,"Empower a living thrall or revive a dead one?",,"Empower","Revive","Cancel")
		switch(choice)
			if("Empower")
				if(!is_thrall(thrallToRevive))
					user << "[thrallToRevive] is not a thrall."
					revert_cast()
					return
				if(thrallToRevive.stat != CONSCIOUS)
					user << "[thrallToRevive] must be conscious to become empowered."
					revert_cast()
					return
				if(thrallToRevive.dna.species.id == "l_shadowling")
					user << "[thrallToRevive] is already empowered."
					revert_cast()
					return
				var/empowered_thralls = 0
				for(var/datum/mind/M in ticker.mode.thralls)
					if(!ishuman(M.current))
						return
					var/mob/living/carbon/human/H = M.current
					if(H.dna.species.id == "l_shadowling")
						empowered_thralls++
				if(empowered_thralls >= EMPOWERED_THRALL_LIMIT)
					user << "You cannot spare this much energy. There are too many empowered thralls."
					revert_cast()
					return
				user.visible_message("[user] places their hands over [thrallToRevive]'s face, red light shining from beneath.", \
									"You place your hands on [thrallToRevive]'s face and begin gathering energy...")
				thrallToRevive << "[user] places their hands over your face. You feel energy gathering. Stand still..."
				if(!do_mob(user, thrallToRevive, 80))
					user << "Your concentration snaps. The flow of energy ebbs."
					revert_cast()
					return
				user << "You release a massive surge of power into [thrallToRevive]!"
				user.visible_message("Red lightning surges into [thrallToRevive]'s face!")
				playsound(thrallToRevive, 'sound/weapons/Egloves.ogg', 50, 1)
				playsound(thrallToRevive, 'sound/machines/defib_zap.ogg', 50, 1)
				user.Beam(thrallToRevive,icon_state="red_lightning",icon='icons/effects/effects.dmi',time=1)
				thrallToRevive.Weaken(5)
				thrallToRevive.visible_message("[thrallToRevive] collapses, their skin and face distorting!", \
											   "AAAAAAAAAAAAAAAAAAAGH-")
				sleep(20)
				thrallToRevive.visible_message("[thrallToRevive] slowly rises, no longer recognizable as human.", \
											   "You feel new power flow into you. You have been gifted by your masters. You now closely resemble them. You are empowered in \
											    darkness but wither slowly in light. In addition, Lesser Glare and Guise have been upgraded into their true forms.")
				thrallToRevive.set_species(/datum/species/shadow/ling/lesser)
				thrallToRevive.mind.RemoveSpell(/obj/effect/proc_holder/spell/targeted/lesser_glare)
				thrallToRevive.mind.RemoveSpell(/obj/effect/proc_holder/spell/self/lesser_shadow_walk)
				thrallToRevive.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/glare(null))
				thrallToRevive.mind.AddSpell(new /obj/effect/proc_holder/spell/self/shadow_walk(null))
			if("Revive")
				if(!is_thrall(thrallToRevive))
					user << "[thrallToRevive] is not a thrall."
					revert_cast()
					return
				if(thrallToRevive.stat != DEAD)
					user << "[thrallToRevive] is not dead."
					revert_cast()
					return
				user.visible_message("[user] kneels over [thrallToRevive], placing their hands on \his chest.", \
									"You crouch over the body of your thrall and begin gathering energy...")
				thrallToRevive.notify_ghost_cloning("Your masters are resuscitating you! Re-enter your corpse if you wish to be brought to life.", source = thrallToRevive)
				if(!do_mob(user, thrallToRevive, 30))
					user << "Your concentration snaps. The flow of energy ebbs."
					revert_cast()
					return
				user << "You release a massive surge of power into [thrallToRevive]!"
				user.visible_message("Red lightning surges from [user]'s hands into [thrallToRevive]'s chest!")
				playsound(thrallToRevive, 'sound/weapons/Egloves.ogg', 50, 1)
				playsound(thrallToRevive, 'sound/machines/defib_zap.ogg', 50, 1)
				user.Beam(thrallToRevive,icon_state="red_lightning",icon='icons/effects/effects.dmi',time=1)
				sleep(10)
				if(thrallToRevive.revive(full_heal = 1))
					thrallToRevive.visible_message("[thrallToRevive] heaves in breath, dim red light shining in their eyes.", \
											   "You have returned. One of your masters has brought you from the darkness beyond.")
					thrallToRevive.Weaken(4)
					thrallToRevive.emote("gasp")
					playsound(thrallToRevive, "bodyfall", 50, 1)
			else
				revert_cast()
				return

/obj/effect/proc_holder/spell/targeted/shadowling_extend_shuttle
	name = "Destroy Engines"
	desc = "Extends the time of the emergency shuttle's arrival by fifteen minutes. This can only be used once."
	panel = "Shadowling Abilities"
	range = 1
	human_req = 1
	clothes_req = 0
	charge_max = 600
	action_icon_state = "extend_shuttle"

/obj/effect/proc_holder/spell/targeted/shadowling_extend_shuttle/cast(list/targets, mob/living/carbon/human/user = usr)
	if(!shadowling_check(user))
		revert_cast()
		return
	for(var/mob/living/carbon/human/target in targets)
		if(target.stat)
			revert_cast()
			return
		if(!is_thrall(target))
			user << "[target] must be a thrall."
			revert_cast()
			return
		if(SSshuttle.emergency.mode != SHUTTLE_CALL)
			user << "The shuttle must be inbound only to the station."
			revert_cast()
			return
		var/mob/living/carbon/human/M = target
		user.visible_message("[user]'s eyes flash a bright red!", \
						  "You begin to draw [M]'s life force.")
		M.visible_message("[M]'s face falls slack, their jaw slightly distending.", \
						  "You are suddenly transported... far, far away...")
		if(!do_after(user, 50, target = M))
			M << "You are snapped back to reality, your haze dissipating!"
			user << "You have been interrupted. The draw has failed."
			return
		user << "You project [M]'s life force toward the approaching shuttle, extending its arrival duration!"
		M.visible_message("[M]'s eyes suddenly flare red. They proceed to collapse on the floor, not breathing.", \
						  "...speeding by... ...pretty blue glow... ...touch it... ...no glow now... ...no light... ...nothing at all...")
		M.death()
		if(SSshuttle.emergency.mode == SHUTTLE_CALL)
			var/more_minutes = 9000
			var/timer = SSshuttle.emergency.timeLeft()
			timer += more_minutes
			priority_announce("Major system failure aboard the emergency shuttle. This will extend its arrival time by approximately 15 minutes..", "System Failure", 'sound/misc/notice1.ogg')
			SSshuttle.emergency.setTimer(timer)
		user.mind.spell_list.Remove(src) //Can only be used once!
		qdel(src)

// THRALL ABILITIES BEYOND THIS POINT //

/obj/effect/proc_holder/spell/targeted/lesser_glare //Thrall version of Glare - same effects but for 5 seconds
	name = "Lesser Glare"
	desc = "Stuns and mutes a target for a short duration."
	panel = "Thrall Abilities"
	charge_max = 450
	human_req = 1
	clothes_req = 0
	action_icon_state = "glare"

/obj/effect/proc_holder/spell/targeted/lesser_glare/cast(list/targets,mob/user = usr)
	for(var/mob/living/target in targets)
		if(!ishuman(target) || !target)
			user << "You nay only glare at humans!"
			revert_cast()
			return
		if(target.stat)
			user << "[target] must be conscious!"
			revert_cast()
			return
		if(is_shadow_or_thrall(target))
			user << "You cannot glare at allies!"
			revert_cast()
			return
		var/mob/living/carbon/human/M = target
		user.visible_message("[user]'s eyes flash a bright red!")
		target.visible_message("[target] freezes in place, their eyes clouding...")
		if(in_range(target, user))
			target << "Your gaze is forcibly drawn into [user]'s eyes, and you are starstruck by the heavenly lights..."
		else //Only alludes to the shadowling if the target is close by
			target << "Red lights suddenly dance in your vision, and you are starstruck by their heavenly beauty..."
		target.Stun(5) //Roughly 50% as long as the normal one
		M.silent += 5

/obj/effect/proc_holder/spell/self/lesser_shadow_walk //Thrall version of Shadow Walk, only works in darkness, doesn't grant phasing, but gives near-invisibility
	name = "Guise"
	desc = "Wraps your form in shadows, making you harder to see."
	panel = "Thrall Abilities"
	charge_max = 1200
	human_req = 1
	clothes_req = 0
	action_icon_state = "shadow_walk"

/obj/effect/proc_holder/spell/self/lesser_shadow_walk/cast(mob/living/carbon/human/user)
	user.visible_message("[user] suddenly fades away!", "You veil yourself in darkness, making you harder to see.")
	user.alpha = 10
	sleep(40)
	user.visible_message("[user] appears from nowhere!", "Your shadowy guise slips away.")
	user.alpha = initial(user.alpha)

/obj/effect/proc_holder/spell/self/thrall_vision //Toggleable night vision for thralls
	name = "Darksight"
	desc = "Gives you night vision."
	panel = "Thrall Abilities"
	charge_max = 0
	human_req = 1
	clothes_req = 0
	action_icon_state = "darksight"
	var/active = 0

/obj/effect/proc_holder/spell/self/thrall_vision/cast(mob/living/carbon/human/user)
	active = !active
	if(active)
		user << "You shift the nerves in your eyes, allowing you to see in the dark."
		user.dna.species.darksight = 8
		user.dna.species.invis_sight = SEE_INVISIBLE_MINIMUM
	else
		user << "You return your vision to normal."
		user.dna.species.darksight = 0
		user.dna.species.invis_sight = initial(user.dna.species.invis_sight)
	user.update_sight()

/obj/effect/proc_holder/spell/self/lesser_shadowling_hivemind //Lets a thrall talk with their allies
	name = "Lesser Commune"
	desc = "Allows you to silently communicate with all other shadowlings and thralls."
	panel = "Thrall Abilities"
	charge_max = 50
	human_req = 1
	clothes_req = 0
	action_icon_state = "commune"

/obj/effect/proc_holder/spell/self/lesser_shadowling_hivemind/cast(mob/living/carbon/human/user)
	if(!is_shadow_or_thrall(user))
		user << "As you attempt to commune with the others, an agonizing spike of pain drives itself into your head!"
		user.apply_damage(10, BRUTE, "head")
		return
	var/text = stripped_input(user, "What do you want to say your masters and fellow thralls?.", "Lesser Commune", "")
	if(!text)
		return
	text = "\[Thrall\] [user.real_name]: [text]"
	for(var/mob/M in mob_list)
		if(is_shadow_or_thrall(M))
			M << text
		if(isobserver(M))
			M << "(F) [text]"
	log_say("[user.real_name]/[user.key] : [text]")

// ASCENDANT ABILITIES BEYOND THIS POINT //

/obj/effect/proc_holder/spell/targeted/annihilate //Gibs someone instantly.
	name = "Annihilate"
	desc = "Gibs someone instantly."
	panel = "Ascendant"
	range = 7
	charge_max = 0
	clothes_req = 0
	action_icon_state = "annihilate"
	sound = 'sound/magic/Staff_Chaos.ogg'

/obj/effect/proc_holder/spell/targeted/annihilate/cast(list/targets,mob/living/simple_animal/ascendant_shadowling/user = usr)
	if(user.incorporeal_move)
		user << "You are not in the same plane of existence. Unphase first."
		revert_cast()
		return
	for(var/mob/living/boom in targets)
		if(is_shadow(boom)) //Used to not work on thralls. Now it does so you can PUNISH THEM LIKE THE WRATHFUL GOD YOU ARE.
			user << "Making an ally explode seems unwise."
			revert_cast()
			return
		user.visible_message("[user]'s markings flare as they gesture at [boom]!", \
							"You direct a lance of telekinetic energy into [boom].")
		sleep(4)
		if(iscarbon(boom))
			playsound(boom, 'sound/magic/Disintegrate.ogg', 100, 1)
		boom.visible_message("[boom] explodes!")
		boom.gib()

/obj/effect/proc_holder/spell/targeted/hypnosis //Enthralls someone instantly. Nonlethal alternative to Annihilate
	name = "Hypnosis"
	desc = "Instantly enthralls a human."
	panel = "Ascendant"
	range = 7
	charge_max = 0
	clothes_req = 0
	action_icon_state = "enthrall"

/obj/effect/proc_holder/spell/targeted/hypnosis/cast(list/targets,mob/living/simple_animal/ascendant_shadowling/user = usr)
	if(user.incorporeal_move)
		revert_cast()
		user << "You are not in the same plane of existence. Unphase first."
		return

	for(var/mob/living/carbon/human/target in targets)
		if(is_shadow_or_thrall(target))
			user << "You cannot enthrall an ally."
			revert_cast()
			return
		if(!target.ckey || !target.mind)
			user << "The target has no mind."
			revert_cast()
			return
		if(target.stat)
			user << "The target must be conscious."
			revert_cast()
			return
		if(!ishuman(target))
			user << "You can only enthrall humans."
			revert_cast()
			return

		user << "You instantly rearrange [target]'s memories, hyptonitizing them into a thrall."
		target << "An agonizing spike of pain drives into your mind, and--"
		target.mind.special_role = "thrall"
		ticker.mode.add_thrall(target.mind)

/obj/effect/proc_holder/spell/self/shadowling_phase_shift //Permanent version of shadow walk with no drawback. Toggleable.
	name = "Phase Shift"
	desc = "Phases you into the space between worlds at will, allowing you to move through walls and become invisible."
	panel = "Ascendant"
	charge_max = 15
	clothes_req = 0
	action_icon_state = "shadow_walk"

/obj/effect/proc_holder/spell/self/shadowling_phase_shift/cast(mob/living/simple_animal/ascendant_shadowling/user)
	user.incorporeal_move = !user.incorporeal_move
	if(user.incorporeal_move)
		user.visible_message("[user] suddenly vanishes!", \
		"You begin phasing through planes of existence. Use the ability again to return.")
		user.density = 0
		user.alpha = 0
	else
		user.visible_message("[user] suddenly appears from nowhere!", \
		"You return from the space between worlds.")
		user.density = 1
		user.alpha = 255

/obj/effect/proc_holder/spell/aoe_turf/ascendant_storm //Releases bolts of lightning to everyone nearby
	name = "Lightning Storm"
	desc = "Shocks everyone nearby."
	panel = "Ascendant"
	range = 6
	charge_max = 100
	clothes_req = 0
	action_icon_state = "lightning_storm"
	sound = 'sound/magic/lightningbolt.ogg'

/obj/effect/proc_holder/spell/aoe_turf/ascendant_storm/cast(list/targets,mob/living/simple_animal/ascendant_shadowling/user = usr)
	if(user.incorporeal_move)
		user << "You are not in the same plane of existence. Unphase first."
		revert_cast()
		return
	user.visible_message("A massive ball of lightning appears in [user]'s hands and flares out!", \
						"You conjure a ball of lightning and release it.")

	for(var/turf/T in targets)
		for(var/mob/living/carbon/human/target in T.contents)
			if(is_shadow_or_thrall(target))
				continue
			target << "You are struck by a bolt of lightning!"
			playsound(target, 'sound/magic/LightningShock.ogg', 50, 1)
			target.Weaken(8)
			target.take_organ_damage(0,50)
			user.Beam(target,icon_state="red_lightning",icon='icons/effects/effects.dmi',time=1)

/obj/effect/proc_holder/spell/self/shadowling_hivemind_ascendant //Large, all-caps text in shadowling chat
	name = "Ascendant Commune"
	desc = "Allows you to LOUDLY communicate with all other shadowlings and thralls."
	panel = "Ascendant"
	charge_max = 0
	clothes_req = 0
	action_icon_state = "commune"

/obj/effect/proc_holder/spell/self/shadowling_hivemind_ascendant/cast(mob/living/carbon/human/user)
	var/text = stripped_input(user, "What do you want to say to fellow thralls and shadowlings?.", "Hive Chat", "")
	if(!text)
		return
	text = "\[Ascendant\] [user.real_name]: [text]"
	for(var/mob/M in mob_list)
		if(is_shadow_or_thrall(M))
			M << text
		if(isobserver(M))
			M << "(F) [text]"
	log_say("[user.real_name]/[user.key] : [text]")


code:
/obj/item/clothing/under/shadowling
	name = "blackened flesh"
	desc = "Black, chitinous skin."
	item_state = null
	origin_tech = null
	icon_state = "shadowling"
	has_sensor = 0
	unacidable = 1
	flags = ABSTRACT | NODROP

/obj/item/clothing/suit/space/shadowling
	name = "chitin shell"
	desc = "A dark, semi-transparent shell. Protects against vacuum, but not against the light of the stars." //Still takes damage from spacewalking but is immune to space itself
	icon_state = "shadowling"
	item_state = null
	body_parts_covered = FULL_BODY //Shadowlings are immune to space
	cold_protection = FULL_BODY
	min_cold_protection_temperature = SPACE_SUIT_MIN_TEMP_PROTECT
	flags_inv = HIDEGLOVES | HIDESHOES | HIDEJUMPSUIT
	slowdown = 0
	unacidable = 1
	heat_protection = null //You didn't expect a light-sensitive creature to have heat resistance, did you?
	max_heat_protection_temperature = null
	armor = list(melee = 25, bullet = 0, laser = 0, energy = 0, bomb = 25, bio = 100, rad = 100)
	flags = ABSTRACT | NODROP | THICKMATERIAL | STOPSPRESSUREDMAGE

/obj/item/clothing/shoes/shadowling
	name = "chitin feet"
	desc = "Charred-looking feet. They have minature hooks that latch onto flooring."
	icon_state = "shadowling"
	item_state = null
	unacidable = 1
	flags = NOSLIP | ABSTRACT | NODROP

/obj/item/clothing/mask/gas/shadowling
	name = "chitin mask"
	desc = "A mask-like formation with slots for facial features. A red film covers the eyes."
	icon_state = "shadowling"
	item_state = null
	origin_tech = null
	siemens_coefficient = 0
	unacidable = 1
	flags = ABSTRACT | NODROP

/obj/item/clothing/gloves/shadowling
	name = "chitin hands"
	desc = "An electricity-resistant covering of the hands."
	icon_state = "shadowling"
	item_state = null
	origin_tech = null
	siemens_coefficient = 0
	unacidable = 1
	flags = ABSTRACT | NODROP

/obj/item/clothing/head/shadowling
	name = "chitin helm"
	desc = "A helmet-like enclosure of the head."
	icon_state = "shadowling"
	item_state = null
	cold_protection = HEAD
	min_cold_protection_temperature = SPACE_HELM_MIN_TEMP_PROTECT
	heat_protection = HEAD
	max_heat_protection_temperature = SPACE_HELM_MAX_TEMP_PROTECT
	origin_tech = null
	unacidable = 1
	flags = ABSTRACT | NODROP | STOPSPRESSUREDMAGE

/obj/item/clothing/glasses/night/shadowling
	name = "crimson eyes"
	desc = "A shadowling's eyes. Very light-sensitive and can detect body heat through walls."
	icon = 'icons/obj/surgery.dmi'
	icon_state = "ling_thermal"
	item_state = null
	origin_tech = null
	vision_flags = SEE_MOBS
	darkness_view = 1
	invis_view = 2
	flash_protect = -1
	unacidable = 1
	actions_types = list(/datum/action/item_action/hands_free/shift_nerves)
	var/max_darkness_view = 8
	var/min_darkness_view = 0
	flags = ABSTRACT | NODROP

/obj/item/clothing/glasses/night/shadowling/attack_self(mob/user)
	if(!ishuman(user))
		return
	var/mob/living/carbon/human/H = user
	if(H.dna.species.id != "shadowling")
		user << "You aren't sure how to do this..."
		return
	H.dna.species.darksight = 0 //so our species' vision in the dark doesn't interfere.
	var/new_dark_view
	new_dark_view = (input(user, "Enter the radius of tiles to see with night vision.", "Night Vision", "[new_dark_view]") as num)
	new_dark_view = Clamp(new_dark_view,min_darkness_view,max_darkness_view)
	switch(new_dark_view)
		if(0)
			user << "Your night vision capabilities fade away for the time being."
		else
			user << "You shift your night vision capabilities to see [new_dark_view] tiles away."
	darkness_view = new_dark_view
	user.update_sight()


code:
//In here: Hatch and Ascendance
var/list/possibleShadowlingNames = list("U'ruan", "Y`shej", "Nex", "Hel-uae", "Noaey'gief", "Mii`mahza", "Amerziox", "Gyrg-mylin", "Kanet'pruunance", "Vigistaezian") //Unpronouncable 2: electric boogalo
/obj/effect/proc_holder/spell/self/shadowling_hatch
	name = "Hatch"
	desc = "Casts off your disguise."
	panel = "Shadowling Evolution"
	charge_max = 3000
	human_req = 1
	clothes_req = 0
	action_icon_state = "hatch"

/obj/effect/proc_holder/spell/self/shadowling_hatch/cast(list/targets,mob/user = usr)
	if(user.stat || !ishuman(user) || !user || !is_shadow(user || isinspace(user)))
		return
	var/mob/living/carbon/human/H = user
	var/hatch_or_no = alert(H,"Are you sure you want to hatch? You cannot undo this!",,"Yes","No")
	switch(hatch_or_no)
		if("No")
			H << "You decide against hatching for now."
			charge_counter = charge_max
			return
		if("Yes")
			H.stunned = INFINITY //This is bad but hulks can't be stunned with the actual procs. I'm sorry.
			H.visible_message("[H]'s things suddenly slip off. They hunch over and vomit up a copious amount of purple goo which begins to shape around them!", \
							"You remove any equipment which would hinder your hatching and begin regurgitating the resin which will protect you.")

			var/temp_flags = H.status_flags
			H.status_flags |= GODMODE //Can't die while hatching

			for(var/obj/item/I in H) //drops all items
				H.unEquip(I)

			sleep(50)
			var/turf/open/floor/F
			var/turf/shadowturf = get_turf(user)
			for(F in orange(1, user))
				new /obj/structure/alien/resin/wall/shadowling(F)
			for(var/obj/structure/alien/resin/wall/shadowling/R in shadowturf) //extremely hacky
				qdel(R)
				new /obj/structure/alien/weeds/node(shadowturf) //Dim lighting in the chrysalis -- removes itself afterwards

			H.visible_message("A chrysalis forms around [H], sealing them inside.", \
							"You create your chrysalis and begin to contort within.")

			sleep(100)
			H.visible_message("The skin on [H]'s back begins to split apart. Black spines slowly emerge from the divide.", \
							"Spines pierce your back. Your claws break apart your fingers. You feel excruciating pain as your true form begins its exit.")

			sleep(90)
			H.visible_message("[H], skin shifting, begins tearing at the walls around them.", \
							"Your false skin slips away. You begin tearing at the fragile membrane protecting you.")

			sleep(80)
			playsound(H.loc, 'sound/weapons/slash.ogg', 25, 1)
			H << "You rip and slice."
			sleep(10)
			playsound(H.loc, 'sound/weapons/slashmiss.ogg', 25, 1)
			H << "The chrysalis falls like water before you."
			sleep(10)
			playsound(H.loc, 'sound/weapons/slice.ogg', 25, 1)
			H << "You are free!"
			H.status_flags = temp_flags
			sleep(10)
			playsound(H.loc, 'sound/effects/ghost.ogg', 100, 1)
			var/newNameId = pick(possibleShadowlingNames)
			possibleShadowlingNames.Remove(newNameId)
			H.real_name = newNameId
			H.name = user.real_name
			H.stunned = 0 //Same as above. Due to hulks.
			H << "YOU LIVE!!!"

			for(var/obj/structure/alien/resin/wall/shadowling/W in orange(1, H))
				playsound(W, 'sound/effects/splat.ogg', 50, 1)
				qdel(W)
			for(var/obj/structure/alien/weeds/node/N in shadowturf)
				qdel(N)
			H.visible_message("The chrysalis explodes in a shower of purple flesh and fluid!")
			H.underwear = "Nude"
			H.undershirt = "Nude"
			H.socks = "Nude"
			H.faction |= "faithless"

			H.equip_to_slot_or_del(new /obj/item/clothing/under/shadowling(H), slot_w_uniform)
			H.equip_to_slot_or_del(new /obj/item/clothing/shoes/shadowling(H), slot_shoes)
			H.equip_to_slot_or_del(new /obj/item/clothing/suit/space/shadowling(H), slot_wear_suit)
			H.equip_to_slot_or_del(new /obj/item/clothing/head/shadowling(H), slot_head)
			H.equip_to_slot_or_del(new /obj/item/clothing/gloves/shadowling(H), slot_gloves)
			H.equip_to_slot_or_del(new /obj/item/clothing/mask/gas/shadowling(H), slot_wear_mask)
			H.equip_to_slot_or_del(new /obj/item/clothing/glasses/night/shadowling(H), slot_glasses)
			H.set_species(/datum/species/shadow/ling) //can't be a shadowling without being a shadowling

			H.mind.RemoveSpell(src)

			sleep(10)
			H << "Your powers are awoken. You may now live to your fullest extent. Remember your goal. Cooperate with your thralls and allies."
			H.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/enthrall(null))
			H.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/glare(null))
			H.mind.AddSpell(new /obj/effect/proc_holder/spell/aoe_turf/veil(null))
			H.mind.AddSpell(new /obj/effect/proc_holder/spell/self/shadow_walk(null))
			H.mind.AddSpell(new /obj/effect/proc_holder/spell/aoe_turf/flashfreeze(null))
			H.mind.AddSpell(new /obj/effect/proc_holder/spell/self/collective_mind(null))
			H.mind.AddSpell(new /obj/effect/proc_holder/spell/self/shadowling_regenarmor(null))
			H.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/shadowling_extend_shuttle(null))

/obj/effect/proc_holder/spell/self/shadowling_ascend
	name = "Ascend"
	desc = "Enters your true form."
	panel = "Shadowling Evolution"
	charge_max = 3000
	clothes_req = 0
	action_icon_state = "ascend"

/obj/effect/proc_holder/spell/self/shadowling_ascend/cast(list/targets,mob/user = usr)
	var/mob/living/carbon/human/H = user
	if(!shadowling_check(H))
		return
	var/hatch_or_no = alert(H,"It is time to ascend. Are you sure about this?",,"Yes","No")
	switch(hatch_or_no)
		if("No")
			H << "You decide against ascending for now."
			charge_counter = charge_max
			return
		if("Yes")
			H.notransform = 1
			H.visible_message("[H]'s things suddenly slip off. They gently rise into the air, red light glowing in their eyes.", \
							"You rise into the air and get ready for your transformation.")

			for(var/obj/item/I in H) //drops all items
				H.unEquip(I)

				sleep(50)

				H.visible_message("[H]'s skin begins to crack and harden.", \
								"Your flesh begins creating a shield around yourself.")

				sleep(100)
				H.visible_message("The small horns on [H]'s head slowly grow and elongate.", \
								  "Your body continues to mutate. Your telepathic abilities grow.") //y-your horns are so big, senpai...!~

				sleep(90)
				H.visible_message("[H]'s body begins to violently stretch and contort.", \
								  "You begin to rend apart the final barriers to godhood.")

				sleep(40)
				H << "Yes!"
				sleep(10)
				H << "YES!!"
				sleep(10)
				H << "YE--"
				sleep(1)
				for(var/mob/living/M in orange(7, H))
					M.Weaken(10)
					M << "An immense pressure slams you onto the ground!"
				world << "\"VYSHA NERADA YEKHEZET U'RUU!!\""
				world << 'sound/hallucinations/veryfar_noise.ogg'
				for(var/obj/machinery/power/apc/A in apcs_list)
					A.overload_lighting()
				var/mob/A = new /mob/living/simple_animal/ascendant_shadowling(H.loc)
				for(var/obj/effect/proc_holder/spell/S in H.mind.spell_list)
					if(S == src) continue
					H.mind.RemoveSpell(S)
				H.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/annihilate(null))
				H.mind.AddSpell(new /obj/effect/proc_holder/spell/targeted/hypnosis(null))
				H.mind.AddSpell(new /obj/effect/proc_holder/spell/self/shadowling_phase_shift(null))
				H.mind.AddSpell(new /obj/effect/proc_holder/spell/aoe_turf/ascendant_storm(null))
				H.mind.AddSpell(new /obj/effect/proc_holder/spell/self/shadowling_hivemind_ascendant(null))
				H.mind.transfer_to(A)
				A.name = H.real_name
				if(A.real_name)
					A.real_name = H.real_name
				H.invisibility = 60 //This is pretty bad, but is also necessary for the shuttle call to function properly
				H.loc = A
				sleep(50)
				if(!ticker.mode.shadowling_ascended)
					SSshuttle.emergency.request(null, 0.3)
				ticker.mode.shadowling_ascended = 1
				A.mind.RemoveSpell(src)
				qdel(H)