code:
/* Morgue stuff
 * Contains:
 *		Morgue
 *		Morgue tray
 *		Crematorium
 *		Crematorium tray
 *		Crematorium button
 */

/*
 * Bodycontainer
 * Parent class for morgue and crematorium
 * For overriding only
 */
/obj/structure/bodycontainer
	icon = 'icons/obj/stationobjs.dmi'
	icon_state = "morgue1"
	density = 1
	anchored = 1

	var/obj/structure/tray/connected = null
	var/locked = 0
	var/opendir = SOUTH

/obj/structure/bodycontainer/New()
	..()

/obj/structure/bodycontainer/Destroy()
	open()
	if(connected)
		qdel(connected)
		connected = null
	return ..()

/obj/structure/bodycontainer/on_log()
	update_icon()

/obj/structure/bodycontainer/update_icon()
	return

/obj/structure/bodycontainer/alter_health()
	return src.loc

/obj/structure/bodycontainer/relaymove(mob/user)
	if(user.stat || !isturf(loc))
		return
	open()

/obj/structure/bodycontainer/attack_paw(mob/user)
	return src.attack_hand(user)

/obj/structure/bodycontainer/attack_hand(mob/user)
	if(locked)
		user << "It's locked."
		return
	if(!connected)
		user << "That doesn't appear to have a tray."
		return
	if(connected.loc == src)
		open()
	else
		close()
	add_fingerprint(user)

/obj/structure/bodycontainer/attackby(obj/P, mob/user, params)
	if (istype(P, /obj/item/weapon/pen))
		var/t = stripped_input(user, "What would you like the label to be?", text("[]", name), null)
		if (user.get_active_hand() != P)
			return
		if ((!in_range(src, usr) && src.loc != user))
			return
		if (t)
			name = text("[]- '[]'", initial(name), t)
		else
			name = initial(name)
	add_fingerprint(user)

/obj/structure/bodycontainer/container_resist()
	open()

/obj/structure/bodycontainer/proc/open()
	playsound(src.loc, 'sound/items/Deconstruct.ogg', 50, 1)
	var/turf/T = get_step(src, opendir)
	for(var/atom/movable/AM in src)
		AM.forceMove(T)
	update_icon()

/obj/structure/bodycontainer/proc/close()
	playsound(src.loc, 'sound/items/Deconstruct.ogg', 50, 1)
	for(var/atom/movable/AM in connected.loc)
		if(!AM.anchored || AM == connected)
			AM.forceMove(src)
	update_icon()

/obj/structure/bodycontainer/get_remote_view_fullscreens(mob/user)
	if(user.stat == DEAD || !(user.sight & (SEEOBJS|SEEMOBS)))
		user.overlay_fullscreen("remote_view", /obj/screen/fullscreen/impaired, 2)
/*
 * Morgue
 */
/obj/structure/bodycontainer/morgue
	name = "morgue"
	desc = "Used to keep bodies in until someone fetches them."
	icon_state = "morgue1"
	opendir = EAST

/obj/structure/bodycontainer/morgue/New()
	connected = new/obj/structure/tray/m_tray(src)
	connected.connected = src
	..()

/obj/structure/bodycontainer/morgue/update_icon()
	if (!connected || connected.loc != src) // Open or tray is gone.
		icon_state = "morgue0"
	else
		if(contents.len == 1)  // Empty
			icon_state = "morgue1"
		else
			icon_state = "morgue2" // Dead, brainded mob.
			var/list/compiled = recursive_mob_check(src, 0, 0) // Search for mobs in all contents.
			if(!length(compiled)) // No mobs?
				icon_state = "morgue3"
				return
			for(var/mob/living/M in compiled)
				if(M.client)
					icon_state = "morgue4" // Cloneable
					break

/*
 * Crematorium
 */
var/global/list/crematoriums = new/list()
/obj/structure/bodycontainer/crematorium
	name = "crematorium"
	desc = "A human incinerator. Works well on barbeque nights."
	icon_state = "crema1"
	opendir = SOUTH
	var/id = 1

/obj/structure/bodycontainer/crematorium/Destroy()
	crematoriums.Remove(src)
	return ..()

/obj/structure/bodycontainer/crematorium/New()
	connected = new/obj/structure/tray/c_tray(src)
	connected.connected = src

	crematoriums.Add(src)
	..()

/obj/structure/bodycontainer/crematorium/update_icon()
	if(!connected || connected.loc != src)
		icon_state = "crema0"
	else

		if(src.contents.len > 1)
			src.icon_state = "crema2"
		else
			src.icon_state = "crema1"

		if(locked)
			src.icon_state = "crema_active"

	return

/obj/structure/bodycontainer/crematorium/proc/cremate(mob/user)
	if(locked)
		return //don't let you cremate something twice or w/e

	if(contents.len <= 1)
		audible_message("You hear a hollow crackle.")
		return

	else
		audible_message("You hear a roar as the crematorium activates.")

		locked = 1
		update_icon()

		for(var/mob/living/M in contents)
			if (M.stat != DEAD)
				M.emote("scream")
			if(user)
				user.attack_log +="\[[time_stamp()]\] Cremated [M]/[M.ckey]"
				log_attack("\[[time_stamp()]\] [user]/[user.ckey] cremated [M]/[M.ckey]")
			else
				log_attack("\[[time_stamp()]\] UNKNOWN cremated [M]/[M.ckey]")
			M.death(1)
			if(M) //some animals get automatically deleted on death.
				M.ghostize()
				qdel(M)

		for(var/obj/O in contents) //obj instead of obj/item so that bodybags and ashes get destroyed. We dont want tons and tons of ash piling up
			if(O != connected) //Creamtorium does not burn hot enough to destroy the tray
				qdel(O)

		new /obj/effect/decal/cleanable/ash(src)
		sleep(30)
		locked = 0
		update_icon()
		playsound(src.loc, 'sound/machines/ding.ogg', 50, 1) //you horrible people

/*
 * Generic Tray
 * Parent class for morguetray and crematoriumtray
 * For overriding only
 */
/obj/structure/tray
	icon = 'icons/obj/stationobjs.dmi'
	density = 1
	layer = 2.9
	var/obj/structure/bodycontainer/connected = null
	anchored = 1
	pass_flags = LETPASSTHROW

/obj/structure/tray/Destroy()
	if(connected)
		connected.connected = null
		connected.update_icon()
		connected = null
	return ..()

/obj/structure/tray/attack_paw(mob/user)
	return src.attack_hand(user)

/obj/structure/tray/attack_hand(mob/user)
	if (src.connected)
		connected.close()
		add_fingerprint(user)
	else
		user << "That's not connected to anything!"

/obj/structure/tray/MouseDrop_T(atom/movable/O as mob|obj, mob/user)
	if(!istype(O, /atom/movable) || O.anchored || !Adjacent(user) || !user.Adjacent(O) || O.loc == user)
		return
	if(!ismob(O))
		if(!istype(O, /obj/structure/closet/body_bag))
			return
	else
		var/mob/M = O
		if(M.buckled)
			return
	if(!ismob(user) || user.lying || user.incapacitated())
		return
	O.loc = src.loc
	if (user != O)
		visible_message("[user] stuffs [O] into [src].")
	return

/*
 * Crematorium tray
 */
/obj/structure/tray/c_tray
	name = "crematorium tray"
	desc = "Apply body before burning."
	icon_state = "cremat"

/*
 * Morgue tray
 */
/obj/structure/tray/m_tray
	name = "morgue tray"
	desc = "Apply corpse before closing."
	icon_state = "morguet"

/obj/structure/tray/m_tray/CanPass(atom/movable/mover, turf/target, height=0)
	if(height == 0)
		return 1

	if(istype(mover) && mover.checkpass(PASSTABLE))
		return 1
	if(locate(/obj/structure/table) in get_turf(mover))
		return 1
	else
		return 0

/obj/structure/tray/m_tray/CanAStarPass(ID, dir, caller)
	. = !density
	if(ismovableatom(caller))
		var/atom/movable/mover = caller
		. = . || mover.checkpass(PASSTABLE)


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

/datum/song
	var/name = "Untitled"
	var/list/lines = new()
	var/tempo = 5			// delay between notes

	var/playing = 0			// if we're playing
	var/help = 0			// if help is open
	var/edit = 1			// if we're in editing mode
	var/repeat = 0			// number of times remaining to repeat
	var/max_repeats = 10	// maximum times we can repeat

	var/instrumentDir = "piano"		// the folder with the sounds
	var/instrumentExt = "ogg"		// the file extension
	var/obj/instrumentObj = null	// the associated obj playing the sound

/datum/song/New(dir, obj)
	tempo = sanitize_tempo(tempo)
	instrumentDir = dir
	instrumentObj = obj

/datum/song/Destroy()
	instrumentObj = null
	return ..()

// note is a number from 1-7 for A-G
// acc is either "b", "n", or "#"
// oct is 1-8 (or 9 for C)
/datum/song/proc/playnote(note, acc as text, oct)
	// handle accidental -> B<>C of E<>F
	if(acc == "b" && (note == 3 || note == 6)) // C or F
		if(note == 3)
			oct--
		note--
		acc = "n"
	else if(acc == "#" && (note == 2 || note == 5)) // B or E
		if(note == 2)
			oct++
		note++
		acc = "n"
	else if(acc == "#" && (note == 7)) //G#
		note = 1
		acc = "b"
	else if(acc == "#") // mass convert all sharps to flats, octave jump already handled
		acc = "b"
		note++

	// check octave, C is allowed to go to 9
	if(oct < 1 || (note == 3 ? oct > 9 : oct > 8))
		return

	// now generate name
	var/soundfile = "sound/[instrumentDir]/[ascii2text(note+64)][acc][oct].[instrumentExt]"
	soundfile = file(soundfile)
	// make sure the note exists
	if(!fexists(soundfile))
		return
	// and play
	var/turf/source = get_turf(instrumentObj)
	for(var/mob/M in get_hearers_in_view(15, source))
		if(!M.client || !(M.client.prefs.toggles & SOUND_INSTRUMENTS))
			continue
		M.playsound_local(source, soundfile, 100, falloff = 5)

/datum/song/proc/updateDialog(mob/user)
	instrumentObj.updateDialog()		// assumes it's an object in world, override if otherwise

/datum/song/proc/shouldStopPlaying(mob/user)
	if(instrumentObj)
		if(!user.canUseTopic(instrumentObj))
			return 1
		return !instrumentObj.anchored		// add special cases to stop in subclasses
	else
		return 1

/datum/song/proc/playsong(mob/user)
	while(repeat >= 0)
		var/cur_oct[7]
		var/cur_acc[7]
		for(var/i = 1 to 7)
			cur_oct[i] = 3
			cur_acc[i] = "n"

		for(var/line in lines)
			//world << line
			for(var/beat in splittext(lowertext(line), ","))
				//world << "beat: [beat]"
				var/list/notes = splittext(beat, "/")
				for(var/note in splittext(notes[1], "-"))
					//world << "note: [note]"
					if(!playing || shouldStopPlaying(user))//If the instrument is playing, or special case
						playing = 0
						return
					if(lentext(note) == 0)
						continue
					//world << "Parse: [copytext(note,1,2)]"
					var/cur_note = text2ascii(note) - 96
					if(cur_note < 1 || cur_note > 7)
						continue
					for(var/i=2 to lentext(note))
						var/ni = copytext(note,i,i+1)
						if(!text2num(ni))
							if(ni == "#" || ni == "b" || ni == "n")
								cur_acc[cur_note] = ni
							else if(ni == "s")
								cur_acc[cur_note] = "#" // so shift is never required
						else
							cur_oct[cur_note] = text2num(ni)
					playnote(cur_note, cur_acc[cur_note], cur_oct[cur_note])
				if(notes.len >= 2 && text2num(notes[2]))
					sleep(sanitize_tempo(tempo / text2num(notes[2])))
				else
					sleep(tempo)
		repeat--
		if(repeat >= 0) // don't show the last -1 repeat
			updateDialog(user)
	playing = 0
	repeat = 0
	updateDialog(user)

/datum/song/proc/interact(mob/user)
	var/dat = ""

	if(lines.len > 0)
		dat += "
Playback
"
		if(!playing)
			dat += "Play Stop

"
			dat += "Repeat Song: "
			dat += repeat > 0 ? "--" : "--"
			dat += " [repeat] times "
			dat += repeat < max_repeats ? "++" : "++"
			dat += "
"
		else
			dat += "Play Stop
"
			dat += "Repeats left: [repeat]
"
	if(!edit)
		dat += "
Show Editor
"
	else
		dat += "
Editing
"
		dat += "Hide Editor"
		dat += " Start a New Song"
		dat += " Import a Song

"
		var/bpm = round(600 / tempo)
		dat += "Tempo: - [bpm] BPM +

"
		var/linecount = 0
		for(var/line in lines)
			linecount += 1
			dat += "Line [linecount]: Edit X [line]
"
		dat += "Add Line

"
		if(help)
			dat += "Hide Help
"
			dat += {"
					Lines are a series of chords, separated by commas (,), each with notes seperated by hyphens (-).

					Every note in a chord will play together, with chord timed by the tempo.

					Notes are played by the names of the note, and optionally, the accidental, and/or the octave number.

					By default, every note is natural and in octave 3. Defining otherwise is remembered for each note.

					Example: C,D,E,F,G,A,B will play a C major scale.

					After a note has an accidental placed, it will be remembered: C,C4,C,C3 is C3,C4,C4,C3

					Chords can be played simply by seperating each note with a hyphon: A-C#,Cn-E,E-G#,Gn-B

					A pause may be denoted by an empty chord: C,E,,C,G

					To make a chord be a different time, end it with /x, where the chord length will be length

					defined by tempo / x: C,G/2,E/4

					Combined, an example is: E-E4/4,F#/2,G#/8,B/8,E3-E4/4

					Lines may be up to 50 characters.

					A song may only contain up to 50 lines.

					"}
		else
			dat += "Show Help
"

	var/datum/browser/popup = new(user, "instrument", instrumentObj.name, 700, 500)
	popup.set_content(dat)
	popup.set_title_image(user.browse_rsc_icon(instrumentObj.icon, instrumentObj.icon_state))
	popup.open()

/datum/song/Topic(href, href_list)
	if(!usr.canUseTopic(instrumentObj))
		usr << browse(null, "window=instrument")
		usr.unset_machine()
		return

	instrumentObj.add_fingerprint(usr)

	if(href_list["newsong"])
		lines = new()
		tempo = sanitize_tempo(5) // default 120 BPM
		name = ""

	else if(href_list["import"])
		var/t = ""
		do
			t = html_encode(input(usr, "Please paste the entire song, formatted:", text("[]", name), t)  as message)
			if(!in_range(instrumentObj, usr))
				return

			if(lentext(t) >= 3072)
				var/cont = input(usr, "Your message is too long! Would you like to continue editing it?", "", "yes") in list("yes", "no")
				if(cont == "no")
					break
		while(lentext(t) > 3072)

		//split into lines
		spawn()
			lines = splittext(t, "\n")
			if(copytext(lines[1],1,6) == "BPM: ")
				tempo = sanitize_tempo(600 / text2num(copytext(lines[1],6)))
				lines.Cut(1,2)
			else
				tempo = sanitize_tempo(5) // default 120 BPM
			if(lines.len > 50)
				usr << "Too many lines!"
				lines.Cut(51)
			var/linenum = 1
			for(var/l in lines)
				if(lentext(l) > 50)
					usr << "Line [linenum] too long!"
					lines.Remove(l)
				else
					linenum++
			updateDialog(usr)		// make sure updates when complete

	else if(href_list["help"])
		help = text2num(href_list["help"]) - 1

	else if(href_list["edit"])
		edit = text2num(href_list["edit"]) - 1

	if(href_list["repeat"]) //Changing this from a toggle to a number of repeats to avoid infinite loops.
		if(playing)
			return //So that people cant keep adding to repeat. If the do it intentionally, it could result in the server crashing.
		repeat += round(text2num(href_list["repeat"]))
		if(repeat < 0)
			repeat = 0
		if(repeat > max_repeats)
			repeat = max_repeats

	else if(href_list["tempo"])
		tempo = sanitize_tempo(tempo + text2num(href_list["tempo"]))

	else if(href_list["play"])
		playing = 1
		spawn()
			playsong(usr)

	else if(href_list["newline"])
		var/newline = html_encode(input("Enter your line: ", instrumentObj.name) as text|null)
		if(!newline || !in_range(instrumentObj, usr))
			return
		if(lines.len > 50)
			return
		if(lentext(newline) > 50)
			newline = copytext(newline, 1, 50)
		lines.Add(newline)

	else if(href_list["deleteline"])
		var/num = round(text2num(href_list["deleteline"]))
		if(num > lines.len || num < 1)
			return
		lines.Cut(num, num+1)

	else if(href_list["modifyline"])
		var/num = round(text2num(href_list["modifyline"]),1)
		var/content = html_encode(input("Enter your line: ", instrumentObj.name, lines[num]) as text|null)
		if(!content || !in_range(instrumentObj, usr))
			return
		if(lentext(content) > 50)
			content = copytext(content, 1, 50)
		if(num > lines.len || num < 1)
			return
		lines[num] = content

	else if(href_list["stop"])
		playing = 0

	updateDialog(usr)
	return

/datum/song/proc/sanitize_tempo(new_tempo)
	new_tempo = abs(new_tempo)
	return max(round(new_tempo, world.tick_lag), world.tick_lag)

// subclass for handheld instruments, like violin
/datum/song/handheld

/datum/song/handheld/updateDialog(mob/user)
	instrumentObj.interact(user)

/datum/song/handheld/shouldStopPlaying()
	if(instrumentObj)
		return !isliving(instrumentObj.loc)
	else
		return 1

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

/obj/structure/piano
	name = "space minimoog"
	icon = 'icons/obj/musician.dmi'
	icon_state = "minimoog"
	anchored = 1
	density = 1
	var/datum/song/song

/obj/structure/piano/New()
	song = new("piano", src)

	if(prob(50))
		name = "space minimoog"
		desc = "This is a minimoog, like a space piano, but more spacey!"
		icon_state = "minimoog"
	else
		name = "space piano"
		desc = "This is a space piano, like a regular piano, but always in tune! Even if the musician isn't."
		icon_state = "piano"

/obj/structure/piano/Destroy()
	qdel(song)
	song = null
	return ..()

/obj/structure/piano/initialize()
	song.tempo = song.sanitize_tempo(song.tempo) // tick_lag isn't set when the map is loaded
	..()

/obj/structure/piano/attack_hand(mob/user)
	if(!user.IsAdvancedToolUser())
		user << "You don't have the dexterity to do this!"
		return 1
	interact(user)

/obj/structure/piano/attack_paw(mob/user)
	return src.attack_hand(user)

/obj/structure/piano/interact(mob/user)
	if(!user || !anchored)
		return

	user.set_machine(src)
	song.interact(user)

/obj/structure/piano/attackby(obj/item/O, mob/user, params)
	if (istype(O, /obj/item/weapon/wrench))
		if (!anchored && !isinspace())
			playsound(src.loc, 'sound/items/Ratchet.ogg', 50, 1)
			user << " You begin to tighten \the [src] to the floor..."
			if (do_after(user, 20/O.toolspeed, target = src))
				user.visible_message( \
					"[user] tightens \the [src]'s casters.", \
					"You tighten \the [src]'s casters. Now it can be played again.", \
					"You hear ratchet.")
				anchored = 1
		else if(anchored)
			playsound(src.loc, 'sound/items/Ratchet.ogg', 50, 1)
			user << " You begin to loosen \the [src]'s casters..."
			if (do_after(user, 40/O.toolspeed, target = src))
				user.visible_message( \
					"[user] loosens \the [src]'s casters.", \
					"You loosen \the [src]. Now it can be pulled somewhere else.", \
					"You hear ratchet.")
				anchored = 0
	else
		..()

license: mit


code:
/obj/structure/noticeboard
	name = "notice board"
	desc = "A board for pinning important notices upon."
	icon = 'icons/obj/stationobjs.dmi'
	icon_state = "nboard00"
	density = 0
	anchored = 1
	var/notices = 0

/obj/structure/noticeboard/initialize()
	for(var/obj/item/I in loc)
		if(notices > 4) break
		if(istype(I, /obj/item/weapon/paper))
			I.loc = src
			notices++
	icon_state = "nboard0[notices]"

//attaching papers!!
/obj/structure/noticeboard/attackby(obj/item/weapon/O, mob/user, params)
	if(istype(O, /obj/item/weapon/paper))
		if(notices < 5)
			if(!user.drop_item())
				return
			O.add_fingerprint(user)
			add_fingerprint(user)
			O.loc = src
			notices++
			icon_state = "nboard0[notices]"	//update sprite
			user << "You pin the paper to the noticeboard."
		else
			user << "You reach to pin your paper to the board but hesitate. You are certain your paper will not be seen among the many others already attached."

/obj/structure/noticeboard/attack_hand(mob/user)
	var/dat = "Noticeboard
"
	for(var/obj/item/weapon/paper/P in src)
		dat += "[P.name] Write Remove
"
	user << browse("<TITLE>Notices</TITLE>[dat]","window=noticeboard")
	onclose(user, "noticeboard")

/obj/structure/noticeboard/Topic(href, href_list)
	..()
	usr.set_machine(src)
	if(href_list["remove"])
		if((usr.stat || usr.restrained()))	//For when a player is handcuffed while they have the notice window open
			return
		var/obj/item/P = locate(href_list["remove"])
		if((P && P.loc == src))
			P.loc = get_turf(src)	//dump paper on the floor because you're a clumsy fuck
			P.add_fingerprint(usr)
			add_fingerprint(usr)
			notices--
			icon_state = "nboard0[notices]"

	if(href_list["write"])
		if((usr.stat || usr.restrained())) //For when a player is handcuffed while they have the notice window open
			return
		var/obj/item/P = locate(href_list["write"])

		if((P && P.loc == src)) //ifthe paper's on the board
			if(istype(usr.r_hand, /obj/item/weapon/pen)) //and you're holding a pen
				add_fingerprint(usr)
				P.attackby(usr.r_hand, usr) //then do ittttt
			else
				if(istype(usr.l_hand, /obj/item/weapon/pen)) //check other hand for pen
					add_fingerprint(usr)
					P.attackby(usr.l_hand, usr)
				else
					usr << "You'll need something to write with!"

	if(href_list["read"])
		var/obj/item/weapon/paper/P = locate(href_list["read"])
		if((P && P.loc == src))
			if(!( istype(usr, /mob/living/carbon/human) ))
				usr << browse("<TITLE>[P.name]</TITLE><TT>[stars(P.info)]</TT>", "window=[P.name]")
				onclose(usr, "[P.name]")
			else
				usr << browse("<TITLE>[P.name]</TITLE><TT>[P.info]</TT>", "window=[P.name]")
				onclose(usr, "[P.name]")
	return


code:
/obj/structure/plasticflaps	//HOW DO YOU CALL THOSE THINGS ANYWAY
	name = "plastic flaps"
	desc = "Definitely can't get past those. No way."
	icon = 'icons/obj/stationobjs.dmi'	//Change this.
	icon_state = "plasticflaps"
	density = 0
	anchored = 1
	layer = 4

/obj/structure/plasticflaps/CanAStarPass(ID, to_dir, caller)
	if(istype(caller, /mob/living))
		if(istype(caller,/mob/living/simple_animal/bot/mulebot))
			return 1

		var/mob/living/M = caller
		if(!M.ventcrawler && M.mob_size != MOB_SIZE_TINY)
			return 0

	return 1 //diseases, stings, etc can pass

/obj/structure/plasticflaps/CanPass(atom/movable/A, turf/T)
	if(istype(A) && A.checkpass(PASSGLASS))
		return prob(60)

	var/obj/structure/bed/B = A
	if (istype(A, /obj/structure/bed) && (B.buckled_mobs.len || B.density))//if it's a bed/chair and is dense or someone is buckled, it will not pass
		return 0

	if (istype(A, /obj/structure/closet/cardboard))
		var/obj/structure/closet/cardboard/C = A
		if(C.move_delay)
			return 0

	else if(istype(A, /mob/living)) // You Shall Not Pass!
		var/mob/living/M = A
		if(istype(A,/mob/living/simple_animal/bot/mulebot)) //mulebots can pass
			return 1
		if(M.buckled && istype(M.buckled, /mob/living/simple_animal/bot/mulebot)) // mulebot passenger gets a free pass.
			return 1
		if(!M.lying && !M.ventcrawler && M.mob_size != MOB_SIZE_TINY)	//If your not laying down, or a ventcrawler or a small creature, no pass.
			return 0
	return ..()

/obj/structure/plasticflaps/attackby(obj/item/weapon/W, mob/user, params)
	user.changeNext_move(CLICK_CD_MELEE)
	..()

/obj/structure/plasticflaps/ex_act(severity)
	..()
	switch(severity)
		if (1)
			qdel(src)
		if (2)
			if (prob(50))
				qdel(src)
		if (3)
			if (prob(5))
				qdel(src)

/obj/structure/plasticflaps/mining //A specific type for mining that doesn't allow airflow because of them damn crates
	name = "airtight plastic flaps"
	desc = "Heavy duty, airtight, plastic flaps."

/obj/structure/plasticflaps/mining/New() //set the turf below the flaps to block air
	var/turf/T = get_turf(loc)
	if(T)
		T.blocks_air = 1
	..()

/obj/structure/plasticflaps/mining/Destroy() //lazy hack to set the turf to allow air to pass if it's a simulated floor //wow this is terrible
	var/turf/T = get_turf(loc)
	if(T)
		if(istype(T, /turf/open/floor))
			T.blocks_air = 0
	return ..()


code:
/obj/structure/reflector
	name = "reflector frame"
	icon = 'icons/obj/stock_parts.dmi'
	icon_state = "box_0"
	desc = "An angled mirror for reflecting lasers. This one does so at a 90 degree angle."
	anchored = 0
	density = 1
	layer = 2.9
	var/finished = 0
	var/admin = 0 //Can't be rotated or deconstructed
	var/framebuildstacktype = /obj/item/stack/sheet/metal
	var/framebuildstackamount = 5
	var/buildstacktype = /obj/item/stack/sheet/metal
	var/buildstackamount = 0

/obj/structure/reflector/bullet_act(obj/item/projectile/P)
	var/turf/reflector_turf = get_turf(src)
	var/turf/reflect_turf
	var/new_dir = get_reflection(src.dir,P.dir)
	if(!istype(P, /obj/item/projectile/beam))
		return..()
	if(new_dir)
		reflect_turf = get_step(reflect_turf, new_dir)
	else
		visible_message("[src] is hit by the [P]!")
		new_dir = 0
		return ..() //Hits as normal, explodes or emps or whatever

	reflect_turf = get_step(loc,new_dir)

	P.original = reflect_turf
	P.starting = reflector_turf
	P.current = reflector_turf
	P.yo = reflect_turf.y - reflector_turf.y
	P.xo = reflect_turf.x - reflector_turf.x
	P.range = initial(P.range) //Keep the projectile healthy as long as its bouncing off things
	new_dir = 0
	return - 1

/obj/structure/reflector/attackby(obj/item/weapon/W, mob/user, params)
	if(admin)
		return
	if(istype(W, /obj/item/weapon/wrench))
		if(anchored)
			user << "Unweld the [src] first!"
		if(do_after(user, 80/W.toolspeed, target = src))
			playsound(src.loc, 'sound/items/Ratchet.ogg', 50, 1)
			user << "You dismantle the [src]."
			new framebuildstacktype(loc, framebuildstackamount)
			new buildstacktype(loc, buildstackamount)
			qdel(src)
	if(istype(W, /obj/item/weapon/weldingtool))
		var/obj/item/weapon/weldingtool/WT = W
		switch(anchored)
			if(0)
				if (WT.remove_fuel(0,user))
					playsound(src.loc, 'sound/items/Welder2.ogg', 50, 1)
					user.visible_message("[user.name] starts to weld the [src.name] to the floor.", \
						"You start to weld \the [src] to the floor...", \
						"You hear welding.")
					if (do_after(user,20/W.toolspeed, target = src))
						if(!src || !WT.isOn())
							return
						anchored = 1
						user << "You weld \the [src] to the floor."
			if(1)
				if (WT.remove_fuel(0,user))
					playsound(src.loc, 'sound/items/Welder2.ogg', 50, 1)
					user.visible_message("[user.name] starts to cut the [src.name] free from the floor.", \
						"You start to cut \the [src] free from the floor...", \
						"You hear welding.")
					if (do_after(user,20/W.toolspeed, target = src))
						if(!src || !WT.isOn())
							return
						anchored  = 0
						user << "You cut \the [src] free from the floor."
	//Finishing the frame
	if(istype(W,/obj/item/stack/sheet))
		if(finished)
			return
		var/obj/item/stack/sheet/S = W
		if(istype(W, /obj/item/stack/sheet/glass))
			if(S.get_amount() < 5)
				user << "You need five sheets of glass to create a reflector!"
				return
			else
				S.use(5)
				new /obj/structure/reflector/single (src.loc)
				qdel (src)
		if(istype(W,/obj/item/stack/sheet/rglass))
			if(S.get_amount() < 10)
				user << "You need ten sheets of reinforced glass to create a double reflector!"
				return
			else
				S.use(10)
				new /obj/structure/reflector/double (src.loc)
				qdel(src)
		if(istype(W, /obj/item/stack/sheet/mineral/diamond))
			if(S.get_amount() >= 1)
				S.use(1)
				new /obj/structure/reflector/box (src.loc)
				qdel(src)

/obj/structure/reflector/proc/get_reflection(srcdir,pdir)
	return 0

/obj/structure/reflector/verb/rotate()
	set name = "Rotate"
	set category = "Object"
	set src in oview(1)

	if(usr.stat || !usr.canmove || usr.restrained())
		return
	if (src.anchored)
		usr << "It is fastened to the floor!"
		return 0
	src.dir = turn(src.dir, 270)
	return 1

/obj/structure/reflector/AltClick(mob/user)
	..()
	if(!user.canUseTopic(user))
		user << "You can't do that right now!"
		return
	if(!in_range(src, user))
		return
	else
		rotate()

//TYPES OF REFLECTORS, SINGLE, DOUBLE, BOX

//SINGLE

/obj/structure/reflector/single
	name = "reflector"
	icon = 'icons/obj/structures.dmi'
	icon_state = "reflector"
	desc = "A double sided angled mirror for reflecting lasers. This one does so at a 90 degree angle."
	finished = 1
	var/static/list/rotations = list("[NORTH]" = list("[SOUTH]" = WEST, "[EAST]" = NORTH),
"[EAST]" = list("[SOUTH]" = EAST, "[WEST]" = NORTH),
"[SOUTH]" = list("[NORTH]" = EAST, "[WEST]" = SOUTH),
"[WEST]" = list("[NORTH]" = WEST, "[EAST]" = SOUTH) )
	buildstacktype = /obj/item/stack/sheet/glass
	buildstackamount = 5

/obj/structure/reflector/single/get_reflection(srcdir,pdir)
	var/new_dir = rotations["[srcdir]"]["[pdir]"]
	return new_dir

/obj/structure/reflector/single/mapping
	admin = 1
	anchored = 1

//DOUBLE

/obj/structure/reflector/double
	name = "double sided reflector"
	icon = 'icons/obj/structures.dmi'
	icon_state = "reflector_double"
	desc = "A double sided angled mirror for reflecting lasers. This one does so at a 90 degree angle."
	finished = 1
	var/static/list/double_rotations = list("[NORTH]" = list("[NORTH]" = WEST, "[EAST]" = SOUTH, "[SOUTH]" = EAST, "[WEST]" = NORTH),
"[EAST]" = list("[NORTH]" = EAST, "[WEST]" = SOUTH, "[SOUTH]" = WEST, "[EAST]" = NORTH),
"[SOUTH]" = list("[NORTH]" = EAST, "[WEST]" = SOUTH, "[SOUTH]" = WEST, "[EAST]" = NORTH),
"[WEST]" = list("[NORTH]" = WEST, "[EAST]" = SOUTH, "[SOUTH]" = EAST, "[WEST]" = NORTH) )
	buildstacktype = /obj/item/stack/sheet/rglass
	buildstackamount = 10

/obj/structure/reflector/double/get_reflection(srcdir,pdir)
	var/new_dir = double_rotations["[srcdir]"]["[pdir]"]
	return new_dir

/obj/structure/reflector/double/mapping
	admin = 1
	anchored = 1

//BOX

/obj/structure/reflector/box
	name = "reflector box"
	icon = 'icons/obj/structures.dmi'
	icon_state = "reflector_box"
	desc = "A box with an internal set of mirrors that reflects all laser fire in a single direction."
	finished = 1
	var/static/list/box_rotations = list("[NORTH]" = list("[SOUTH]" = NORTH, "[EAST]" = NORTH, "[WEST]" = NORTH, "[NORTH]" = NORTH),
"[EAST]" = list("[SOUTH]" = EAST, "[EAST]" = EAST, "[WEST]" = EAST, "[NORTH]" = EAST),
"[SOUTH]" = list("[SOUTH]" = SOUTH, "[EAST]" = SOUTH, "[WEST]" = SOUTH, "[NORTH]" = SOUTH),
"[WEST]" = list("[SOUTH]" = WEST, "[EAST]" = WEST, "[WEST]" = WEST, "[NORTH]" = WEST) )
	buildstacktype = /obj/item/stack/sheet/mineral/diamond
	buildstackamount = 1

/obj/structure/reflector/box/get_reflection(srcdir,pdir)
	var/new_dir = box_rotations["[srcdir]"]["[pdir]"]
	return new_dir

/obj/structure/reflector/box/mapping
	admin = 1
	anchored = 1

/obj/structure/reflector/ex_act()
	if(admin)
		return
	else
		..()

/obj/structure/reflector/singularity_act()
	if(admin)
		return
	else
		..()


code:
/*
CONTAINS:
SAFES
FLOOR SAFES
*/

//SAFES
/obj/structure/safe
	name = "safe"
	desc = "A huge chunk of metal with a dial embedded in it. Fine print on the dial reads \"Scarborough Arms - 2 tumbler safe, guaranteed thermite resistant, explosion resistant, and assistant resistant.\""
	icon = 'icons/obj/structures.dmi'
	icon_state = "safe"
	anchored = 1
	density = 1
	var/open = 0		//is the safe open?
	var/tumbler_1_pos	//the tumbler position- from 0 to 72
	var/tumbler_1_open	//the tumbler position to open at- 0 to 72
	var/tumbler_2_pos
	var/tumbler_2_open
	var/dial = 0		//where is the dial pointing?
	var/space = 0		//the combined w_class of everything in the safe
	var/maxspace = 24	//the maximum combined w_class of stuff in the safe

/obj/structure/safe/New()
	tumbler_1_pos = rand(0, 71)
	tumbler_1_open = rand(0, 71)

	tumbler_2_pos = rand(0, 71)
	tumbler_2_open = rand(0, 71)

/obj/structure/safe/initialize()
	for(var/obj/item/I in loc)
		if(space >= maxspace)
			return
		if(I.w_class + space <= maxspace)
			space += I.w_class
			I.loc = src

/obj/structure/safe/proc/check_unlocked(mob/user, canhear)
	if(user && canhear)
		if(tumbler_1_pos == tumbler_1_open)
			user << "You hear a [pick("tonk", "krunk", "plunk")] from [src]."
		if(tumbler_2_pos == tumbler_2_open)
			user << "You hear a [pick("tink", "krink", "plink")] from [src]."
	if(tumbler_1_pos == tumbler_1_open && tumbler_2_pos == tumbler_2_open)
		if(user) visible_message("[pick("Spring", "Sprang", "Sproing", "Clunk", "Krunk")]!")
		return 1
	return 0

/obj/structure/safe/proc/decrement(num)
	num -= 1
	if(num < 0)
		num = 71
	return num

/obj/structure/safe/proc/increment(num)
	num += 1
	if(num > 71)
		num = 0
	return num

/obj/structure/safe/update_icon()
	if(open)
		icon_state = "[initial(icon_state)]-open"
	else
		icon_state = initial(icon_state)

/obj/structure/safe/attack_hand(mob/user)
	user.set_machine(src)
	var/dat = ""
	dat += "[open ? "Close" : "Open"] [src] | - [dial * 5] +"
	if(open)
		dat += "
"
		for(var/i = contents.len, i>=1, i--)
			var/obj/item/P = contents[i]
			dat += "
[P.name]
"
		dat += "
"
	user << browse("<title>[name]</title>[dat]", "window=safe;size=350x300")

/obj/structure/safe/Topic(href, href_list)
	if(!ishuman(usr))
		return
	var/mob/living/carbon/human/user = usr

	var/canhear = 0
	if(istype(user.l_hand, /obj/item/clothing/tie/stethoscope) || istype(user.r_hand, /obj/item/clothing/tie/stethoscope))
		canhear = 1

	if(href_list["open"])
		if(check_unlocked())
			user << "You [open ? "close" : "open"] [src]."
			open = !open
			update_icon()
			updateUsrDialog()
			return
		else
			user << "You can't [open ? "close" : "open"] [src], the lock is engaged!"
			return

	if(href_list["decrement"])
		dial = decrement(dial)
		if(dial == tumbler_1_pos + 1 || dial == tumbler_1_pos - 71)
			tumbler_1_pos = decrement(tumbler_1_pos)
			if(canhear)
				user << "You hear a [pick("clack", "scrape", "clank")] from [src]."
			if(tumbler_1_pos == tumbler_2_pos + 37 || tumbler_1_pos == tumbler_2_pos - 35)
				tumbler_2_pos = decrement(tumbler_2_pos)
				if(canhear)
					user << "You hear a [pick("click", "chink", "clink")] from [src]."
			check_unlocked(user, canhear)
		updateUsrDialog()
		return

	if(href_list["increment"])
		dial = increment(dial)
		if(dial == tumbler_1_pos - 1 || dial == tumbler_1_pos + 71)
			tumbler_1_pos = increment(tumbler_1_pos)
			if(canhear)
				user << "You hear a [pick("clack", "scrape", "clank")] from [src]."
			if(tumbler_1_pos == tumbler_2_pos - 37 || tumbler_1_pos == tumbler_2_pos + 35)
				tumbler_2_pos = increment(tumbler_2_pos)
				if(canhear)
					user << "You hear a [pick("click", "chink", "clink")] from [src]."
			check_unlocked(user, canhear)
		updateUsrDialog()
		return

	if(href_list["retrieve"])
		user << browse("", "window=safe") // Close the menu

		var/obj/item/P = locate(href_list["retrieve"]) in src
		if(open)
			if(P && in_range(src, user))
				user.put_in_hands(P)
				updateUsrDialog()

/obj/structure/safe/attackby(obj/item/I, mob/user, params)
	if(open)
		if(I.w_class + space <= maxspace)
			space += I.w_class
			if(!user.drop_item())
				user << "\The [I] is stuck to your hand, you cannot put it in the safe!"
				return
			I.loc = src
			user << "You put [I] in [src]."
			updateUsrDialog()
			return
		else
			user << "[I] won't fit in [src]."
			return
	else
		if(istype(I, /obj/item/clothing/tie/stethoscope))
			user << "Hold [I] in one of your hands while you manipulate the dial!"
			return

obj/structure/safe/blob_act()
	return

obj/structure/safe/ex_act(severity, target)
	return

//FLOOR SAFES
/obj/structure/safe/floor
	name = "floor safe"
	icon_state = "floorsafe"
	density = 0
	level = 1	//underfloor
	layer = 2.5

/obj/structure/safe/floor/initialize()
	..()
	var/turf/T = loc
	hide(T.intact)

/obj/structure/safe/floor/hide(var/intact)
	invisibility = intact ? 101 : 0


code:
/*Completely generic structures for use by mappers to create fake objects, i.e. display rooms*/
/obj/structure/showcase
	name = "showcase"
	icon = 'icons/obj/stationobjs.dmi'
	icon_state = "showcase_1"
	desc = "A stand with the empty body of a cyborg bolted to it."
	density = 1
	anchored = 1

/obj/structure/showcase/fakeid
	name = "\improper Centcom identification console"
	desc = "You can use this to change ID's."
	icon = 'icons/obj/computer.dmi'
	icon_state = "computer"

/obj/structure/showcase/fakeid/New()
	overlays += "id"
	overlays += "id_key"

/obj/structure/showcase/fakesec
	name = "\improper Centcom security records"
	desc = "Used to view and edit personnel's security records"
	icon = 'icons/obj/computer.dmi'
	icon_state = "computer"

/obj/structure/showcase/fakesec/New()
	overlays += "security"
	overlays += "security_key"


code:
/obj/structure/sign
	icon = 'icons/obj/decals.dmi'
	anchored = 1
	opacity = 0
	density = 0
	layer = 3.5

/obj/structure/sign/basic
	name = "blank sign"
	desc = "How can signs be real if our eyes aren't real?"
	icon_state = "backing"

/obj/structure/sign/ex_act(severity, target)
	qdel(src)

/obj/structure/sign/blob_act()
	qdel(src)
	return

/obj/structure/sign/attackby(obj/item/O, mob/user, params)
	if(istype(O, /obj/item/weapon/wrench))
		user.visible_message("[user] starts removing [src]...", \
							 "You start unfastening [src].")
		playsound(src, 'sound/items/Ratchet.ogg', 50, 1)
		if(!do_after(user, 30/O.toolspeed, target = src))
			return
		playsound(src, 'sound/items/Deconstruct.ogg', 50, 1)
		user.visible_message("[user] unfastens [src].", \
							 "You unfasten [src].")
		new /obj/item/sign_backing(get_turf(user))
		qdel(src)
	if(istype(O, /obj/item/weapon/pen))
		var/list/sign_types = list("Secure Area", "Biohazard", "High Voltage", "Radiation", "Hard Vacuum Ahead", "Disposal: Leads To Space", "Danger: Fire", "No Smoking", "Medbay", "Science", "Chemistry", \
		"Hydroponics", "Xenobiology")
		var/obj/structure/sign/sign_type
		switch(input(user, "Select a sign type.", "Sign Customization") as null|anything in sign_types)
			if("Blank")
				sign_type = /obj/structure/sign/basic
			if("Secure Area")
				sign_type = /obj/structure/sign/securearea
			if("Biohazard")
				sign_type = /obj/structure/sign/biohazard
			if("High Voltage")
				sign_type = /obj/structure/sign/electricshock
			if("Radiation")
				sign_type = /obj/structure/sign/radiation
			if("Hard Vacuum Ahead")
				sign_type = /obj/structure/sign/vacuum
			if("Disposal: Leads To Space")
				sign_type = /obj/structure/sign/deathsposal
			if("Danger: Fire")
				sign_type = /obj/structure/sign/fire
			if("No Smoking")
				sign_type = /obj/structure/sign/nosmoking_1
			if("Medbay")
				sign_type = /obj/structure/sign/bluecross_2
			if("Science")
				sign_type = /obj/structure/sign/science
			if("Chemistry")
				sign_type = /obj/structure/sign/chemistry
			if("Hydroponics")
				sign_type = /obj/structure/sign/botany
			if("Xenobiology")
				sign_type = /obj/structure/sign/xenobio

		//Make sure user is adjacent still
		if(!Adjacent(user))
			return

		if(!sign_type)
			return

		//It's import to clone the pixel layout information
		//Otherwise signs revert to being on the turf and
		//move jarringly
		var/obj/structure/sign/newsign = new sign_type(get_turf(src))
		newsign.pixel_x = pixel_x
		newsign.pixel_y = pixel_y
		qdel(src)

/obj/item/sign_backing
	name = "sign backing"
	desc = "A blank sign with adhesive backing."
	icon = 'icons/obj/decals.dmi'
	icon_state = "backing"
	w_class = 3
	burn_state = FLAMMABLE

/obj/item/sign_backing/afterattack(atom/target, mob/user, proximity)
	if(!isturf(target))
		return ..()
	if(!user.Adjacent(target))
		return ..()
	var/turf/T = target
	if(!T || !istype(T))
		return ..()
	user.visible_message("[user] fastens [src] to [T].", \
						 "You attach a blank sign to [T].")
	playsound(T, 'sound/items/Deconstruct.ogg', 50, 1)
	new /obj/structure/sign/basic(T)
	user.drop_item()
	qdel(src)
	return

/obj/structure/sign/map
	name = "station map"
	desc = "A framed picture of the station."

/obj/structure/sign/map/left
	icon_state = "map-left"

/obj/structure/sign/map/right
	icon_state = "map-right"

/obj/structure/sign/securearea
	name = "\improper SECURE AREA"
	desc = "A warning sign which reads 'SECURE AREA'."
	icon_state = "securearea"

/obj/structure/sign/biohazard
	name = "\improper BIOHAZARD"
	desc = "A warning sign which reads 'BIOHAZARD'"
	icon_state = "bio"

/obj/structure/sign/electricshock
	name = "\improper HIGH VOLTAGE"
	desc = "A warning sign which reads 'HIGH VOLTAGE'"
	icon_state = "shock"

/obj/structure/sign/examroom
	name = "\improper EXAM ROOM"
	desc = "A guidance sign which reads 'EXAM ROOM'"
	icon_state = "examroom"

/obj/structure/sign/vacuum
	name = "\improper HARD VACUUM AHEAD"
	desc = "A warning sign which reads 'HARD VACUUM AHEAD'"
	icon_state = "space"

/obj/structure/sign/deathsposal
	name = "\improper DISPOSAL: LEADS TO SPACE"
	desc = "A warning sign which reads 'DISPOSAL: LEADS TO SPACE'"
	icon_state = "deathsposal"

/obj/structure/sign/pods
	name = "\improper ESCAPE PODS"
	desc = "A warning sign which reads 'ESCAPE PODS'"
	icon_state = "pods"

/obj/structure/sign/fire
	name = "\improper DANGER: FIRE"
	desc = "A warning sign which reads 'DANGER: FIRE'"
	icon_state = "fire"

/obj/structure/sign/nosmoking_1
	name = "\improper NO SMOKING"
	desc = "A warning sign which reads 'NO SMOKING'"
	icon_state = "nosmoking"

/obj/structure/sign/nosmoking_2
	name = "\improper NO SMOKING"
	desc = "A warning sign which reads 'NO SMOKING'"
	icon_state = "nosmoking2"

/obj/structure/sign/radiation
	name = "HAZARDOUS RADIATION"
	desc = "A warning sign alerting the user of potential radiation hazards."
	icon_state = "radiation"

/obj/structure/sign/bluecross
	name = "medbay"
	desc = "The Intergalactic symbol of Medical institutions. You'll probably get help here."
	icon_state = "bluecross"

/obj/structure/sign/bluecross_2
	name = "medbay"
	desc = "The Intergalactic symbol of Medical institutions. You'll probably get help here."
	icon_state = "bluecross2"

/obj/structure/sign/goldenplaque
	name = "The Most Robust Men Award for Robustness"
	desc = "To be Robust is not an action or a way of life, but a mental state. Only those with the force of Will strong enough to act during a crisis, saving friend from foe, are truly Robust. Stay Robust my friends."
	icon_state = "goldenplaque"

/obj/structure/sign/kiddieplaque
	name = "AI developers plaque"
	desc = "Next to the extremely long list of names and job titles, there is a drawing of a little child. The child appears to be retarded. Beneath the image, someone has scratched the word \"PACKETS\""
	icon_state = "kiddieplaque"

/obj/structure/sign/atmosplaque
	name = "\improper FEA Atmospherics Division plaque"
	desc = "This plaque commemorates the fall of the Atmos FEA division. For all the charred, dizzy, and brittle men who have died in its hands."
	icon_state = "atmosplaque"

/obj/structure/sign/maltesefalcon	//The sign is 64x32, so it needs two tiles. ;3
	name = "The Maltese Falcon"
	desc = "The Maltese Falcon, Space Bar and Grill."

/obj/structure/sign/maltesefalcon/left
	icon_state = "maltesefalcon-left"

/obj/structure/sign/maltesefalcon/right
	icon_state = "maltesefalcon-right"

/obj/structure/sign/science			//These 3 have multiple types, just var-edit the icon_state to whatever one you want on the map
	name = "\improper SCIENCE"
	desc = "A sign labelling an area where research and science is performed."
	icon_state = "science1"

/obj/structure/sign/chemistry
	name = "\improper CHEMISTRY"
	desc = "A sign labelling an area containing chemical equipment."
	icon_state = "chemistry1"

/obj/structure/sign/botany
	name = "\improper HYDROPONICS"
	desc = "A sign labelling an area as a place where plants are grown."
	icon_state = "hydro1"

/obj/structure/sign/xenobio
	name = "\improper XENOBIOLOGY"
	desc = "A sign labelling an area as a place where xenobiological entites are researched."
	icon_state = "xenobio"

/obj/structure/sign/directions/science
	name = "science department"
	desc = "A direction sign, pointing out which way the Science department is."
	icon_state = "direction_sci"

/obj/structure/sign/directions/engineering
	name = "engineering department"
	desc = "A direction sign, pointing out which way the Engineering department is."
	icon_state = "direction_eng"

/obj/structure/sign/directions/security
	name = "security department"
	desc = "A direction sign, pointing out which way the Security department is."
	icon_state = "direction_sec"

/obj/structure/sign/directions/medical
	name = "medical bay"
	desc = "A direction sign, pointing out which way the Medical Bay is."
	icon_state = "direction_med"

/obj/structure/sign/directions/evac
	name = "escape arm"
	desc = "A direction sign, pointing out which way the escape shuttle dock is."
	icon_state = "direction_evac"


code:
/obj/structure/spirit_board
	name = "spirit board"
	desc = "A wooden board with letters etched into it, used in seances."
	icon = 'icons/obj/objects.dmi'
	icon_state = "spirit_board"
	density = 1
	anchored = 0
	var/virgin = 1
	var/cooldown = 0
	var/planchette = "A"
	var/lastuser = null

/obj/structure/spirit_board/examine()
	desc = "[initial(desc)] The planchette is sitting at \"[planchette]\"."
	..()

/obj/structure/spirit_board/attack_hand(mob/user)
	if(..())
		return
	spirit_board_pick_letter(user)

/obj/structure/spirit_board/attack_ghost(mob/dead/observer/user)
	spirit_board_pick_letter(user)

/obj/structure/spirit_board/proc/spirit_board_pick_letter(mob/M)
	if(!spirit_board_checks(M))
		return 0

	if(virgin)
		virgin = 0
		notify_ghosts("Someone has begun playing with a [src.name] in [get_area(src)]!", source = src)

	planchette = input("Choose the letter.", "Seance!") in list("A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z")
	add_logs(M, src, "picked a letter on", " which was \"[planchette]\".")
	cooldown = world.time
	lastuser = M.ckey

	var/turf/T = loc
	sleep(rand(20,30))
	if(T == loc)
		visible_message("The planchette slowly moves... and stops at the letter \"[planchette]\".")

/obj/structure/spirit_board/proc/spirit_board_checks(mob/M)
	//cooldown
	var/bonus = 0
	if(M.ckey == lastuser)
		bonus = 10 //Give some other people a chance, hog.

	if(cooldown > world.time - (30 + bonus))
		return 0 //No feedback here, hiding the cooldown a little makes it harder to tell who's really picking letters.

	//lighting check
	var/light_amount = 0
	var/turf/T = get_turf(src)
	light_amount = T.get_lumcount()

	if(light_amount > 2)
		M << "It's too bright here to use [src.name]!"
		return 0

	//mobs in range check
	var/users_in_range = 0
	for(var/mob/living/L in orange(1,src))
		if(L.ckey && L.client)
			if((world.time - L.client.inactivity) < (world.time - 300) || L.stat != CONSCIOUS || L.restrained())//no playing with braindeads or corpses or handcuffed dudes.
				M << "[L] doesn't seem to be paying attention..."
			else
				users_in_range++

	if(users_in_range < 2)
		M << "There aren't enough people to use the [src.name]!"
		return 0

	return 1


code:
/obj/structure/statue
	name = "Statue"
	desc = "Placeholder. Yell at Firecage if you SOMEHOW see this."
	icon = 'icons/obj/statue.dmi'
	icon_state = ""
	density = 1
	anchored = 0
	var/hardness = 1
	var/oreAmount = 7
	var/mineralType = "metal"

/obj/structure/statue/Destroy()
	density = 0
	return ..()

/obj/structure/statue/attackby(obj/item/weapon/W, mob/living/user, params)
	add_fingerprint(user)
	user.changeNext_move(CLICK_CD_MELEE)
	if(istype(W, /obj/item/weapon/wrench))
		if(anchored)
			playsound(src.loc, 'sound/items/Ratchet.ogg', 100, 1)
			user.visible_message("[user] is loosening the [name]'s bolts.", \
								 "You are loosening the [name]'s bolts...")
			if(do_after(user,40/W.toolspeed, target = src))
				if(!src.loc || !anchored)
					return
				user.visible_message("[user] loosened the [name]'s bolts!", \
									 "You loosen the [name]'s bolts!")
				anchored = 0
		else
			if (!istype(src.loc, /turf/open/floor))
				user.visible_message("A floor must be present to secure the [name]!")
				return
			playsound(src.loc, 'sound/items/Ratchet.ogg', 100, 1)
			user.visible_message("[user] is securing the [name]'s bolts...", \
								 "You are securing the [name]'s bolts...")
			if(do_after(user, 40/W.toolspeed, target = src))
				if(!src.loc || anchored)
					return
				user.visible_message("[user] has secured the [name]'s bolts.", \
									 "You have secured the [name]'s bolts.")
				anchored = 1

	else if(istype(W, /obj/item/weapon/gun/energy/plasmacutter))
		playsound(src, 'sound/items/Welder.ogg', 100, 1)
		user.visible_message("[user] is slicing apart the [name]...", \
							 "You are slicing apart the [name]...")
		if(do_after(user,30, target = src))
			if(!src.loc)
				return
			user.visible_message("[user] slices apart the [name].", \
								 "You slice apart the [name].")
			Dismantle(1)

	else if(istype(W, /obj/item/weapon/pickaxe/drill/jackhammer))
		var/obj/item/weapon/pickaxe/drill/jackhammer/D = W
		if(!src.loc)
			return
		user.visible_message("[user] destroys the [name]!", \
							 "You destroy the [name].")
		D.playDigSound()
		qdel(src)

	else if(istype(W, /obj/item/weapon/weldingtool) && !anchored)
		playsound(loc, 'sound/items/Welder.ogg', 40, 1)
		user.visible_message("[user] is slicing apart the [name].", \
							 "You are slicing apart the [name]...")
		if(do_after(user, 40/W.toolspeed, target = src))
			if(!src.loc)
				return
			playsound(loc, 'sound/items/Welder2.ogg', 50, 1)
			user.visible_message("[user] slices apart the [name].", \
								 "You slice apart the [name]!")
			Dismantle(1)

	else
		hardness -= W.force/100
		..()
		CheckHardness()

/obj/structure/statue/attack_hand(mob/living/user)
	user.changeNext_move(CLICK_CD_MELEE)
	add_fingerprint(user)
	user.visible_message("[user] rubs some dust off from the [name]'s surface.", \
						 "You rub some dust off from the [name]'s surface.")

/obj/structure/statue/CanAtmosPass()
	return !density

/obj/structure/statue/bullet_act(obj/item/projectile/Proj)
	hardness -= Proj.damage
	..()
	CheckHardness()
	return

/obj/structure/statue/proc/CheckHardness()
	if(hardness <= 0)
		Dismantle(1)

/obj/structure/statue/proc/Dismantle(devastated = 0)
	if(!devastated)
		if (mineralType == "metal")
			var/ore = /obj/item/stack/sheet/metal
			for(var/i = 1, i <= oreAmount, i++)
				new ore(get_turf(src))
		else
			var/ore = text2path("/obj/item/stack/sheet/mineral/[mineralType]")
			for(var/i = 1, i <= oreAmount, i++)
				new ore(get_turf(src))
	else
		if (mineralType == "metal")
			var/ore = /obj/item/stack/sheet/metal
			for(var/i = 3, i <= oreAmount, i++)
				new ore(get_turf(src))
		else
			var/ore = text2path("/obj/item/stack/sheet/mineral/[mineralType]")
			for(var/i = 3, i <= oreAmount, i++)
				new ore(get_turf(src))
	qdel(src)

/obj/structure/statue/ex_act(severity = 1)
	switch(severity)
		if(1)
			Dismantle(1)
		if(2)
			if(prob(20))
				Dismantle(1)
			else
				hardness--
				CheckHardness()
		if(3)
			hardness -= 0.1
			CheckHardness()
	return

//////////////////////////////////////STATUES/////////////////////////////////////////////////////////////
////////////////////////uranium///////////////////////////////////

/obj/structure/statue/uranium
	hardness = 3
	luminosity = 2
	mineralType = "uranium"
	var/last_event = 0
	var/active = null

/obj/structure/statue/uranium/nuke
	name = "Statue of a Nuclear Fission Explosive"
	desc = "This is a grand statue of a Nuclear Explosive. It has a sickening green colour."
	icon_state = "nuke"

/obj/structure/statue/uranium/eng
	name = "Statue of an engineer"
	desc = "This statue has a sickening green colour."
	icon_state = "eng"

/obj/structure/statue/uranium/attackby(obj/item/weapon/W, mob/user, params)
	radiate()
	..()

/obj/structure/statue/uranium/Bumped(atom/user)
	radiate()
	..()

/obj/structure/statue/uranium/attack_hand(mob/user)
	radiate()
	..()

/obj/structure/statue/uranium/attack_paw(mob/user)
	radiate()
	..()

/obj/structure/statue/uranium/proc/radiate()
	if(!active)
		if(world.time > last_event+15)
			active = 1
			radiation_pulse(get_turf(src), 3, 3, 12, 0)
			last_event = world.time
			active = null
			return
	return

////////////////////////////plasma///////////////////////////////////////////////////////////////////////

/obj/structure/statue/plasma
	hardness = 2
	mineralType = "plasma"
	desc = "This statue is suitably made from plasma."

/obj/structure/statue/plasma/scientist
	name = "Statue of a Scientist"
	icon_state = "sci"

/obj/structure/statue/plasma/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume)
	if(exposed_temperature > 300)
		PlasmaBurn(exposed_temperature)

/obj/structure/statue/plasma/bullet_act(obj/item/projectile/Proj)
	var/burn = FALSE
	if(istype(Proj,/obj/item/projectile/beam))
		PlasmaBurn(2500)
		burn = TRUE
	else if(istype(Proj,/obj/item/projectile/ion))
		PlasmaBurn(500)
		burn = TRUE
	if(burn)
		if(Proj.firer)
			message_admins("Plasma statue ignited by [key_name_admin(Proj.firer)](?) (FLW) in ([x],[y],[z] - JMP)",0,1)
			log_game("Plasma statue ignited by [key_name(Proj.firer)] in ([x],[y],[z])")
		else
			message_admins("Plasma statue ignited by [Proj]. No known firer.(?) (FLW) in ([x],[y],[z] - JMP)",0,1)
			log_game("Plasma statue ignited by [Proj] in ([x],[y],[z]). No known firer.")
	..()

/obj/structure/statue/plasma/attackby(obj/item/weapon/W, mob/user, params)
	if(W.is_hot() > 300)//If the temperature of the object is over 300, then ignite
		message_admins("Plasma statue ignited by [key_name_admin(user)](?) (FLW) in ([x],[y],[z] - JMP)",0,1)
		log_game("Plasma statue ignited by [key_name(user)] in ([x],[y],[z])")
		ignite(W.is_hot())
		return
	..()

/obj/structure/statue/plasma/proc/PlasmaBurn()
	atmos_spawn_air("plasma=400;TEMP=1000")
	hardness = 0
	CheckHardness()

/obj/structure/statue/plasma/proc/ignite(exposed_temperature)
	if(exposed_temperature > 300)
		PlasmaBurn(exposed_temperature)

//////////////////////gold///////////////////////////////////////

/obj/structure/statue/gold
	hardness = 3
	mineralType = "gold"
	desc = "This is a highly valuable statue made from gold."

/obj/structure/statue/gold/hos
	name = "Statue of the Head of Security"
	icon_state = "hos"

/obj/structure/statue/gold/hop
	name = "Statue of the Head of Personnel"
	icon_state = "hop"

/obj/structure/statue/gold/cmo
	name = "Statue of the Chief Medical Officer"
	icon_state = "cmo"

/obj/structure/statue/gold/ce
	name = "Statue of the Chief Engineer"
	icon_state = "ce"

/obj/structure/statue/gold/rd
	name = "Statue of the Research Director"
	icon_state = "rd"

//////////////////////////silver///////////////////////////////////////

/obj/structure/statue/silver
	hardness = 3
	mineralType = "silver"
	desc = "This is a valuable statue made from silver."

/obj/structure/statue/silver/md
	name = "Statue of a Medical Officer"
	icon_state = "md"

/obj/structure/statue/silver/janitor
	name = "Statue of a Janitor"
	icon_state = "jani"

/obj/structure/statue/silver/sec
	name = "Statue of a Security Officer"
	icon_state = "sec"

/obj/structure/statue/silver/secborg
	name = "Statue of a Security Cyborg"
	icon_state = "secborg"

/obj/structure/statue/silver/medborg
	name = "Statue of a Medical Cyborg"
	icon_state = "medborg"

/////////////////////////diamond/////////////////////////////////////////

/obj/structure/statue/diamond
	hardness = 10
	mineralType = "diamond"
	desc = "This is a very expensive diamond statue"

/obj/structure/statue/diamond/captain
	name = "Statue of THE Captain."
	icon_state = "cap"

/obj/structure/statue/diamond/ai1
	name = "Statue of the AI hologram."
	icon_state = "ai1"

/obj/structure/statue/diamond/ai2
	name = "Statue of the AI core."
	icon_state = "ai2"

////////////////////////bananium///////////////////////////////////////

/obj/structure/statue/bananium
	hardness = 3
	mineralType = "bananium"
	desc = "A bananium statue with a small engraving:'HOOOOOOONK'."
	var/spam_flag = 0

/obj/structure/statue/bananium/clown
	name = "Statue of a clown"
	icon_state = "clown"

/obj/structure/statue/bananium/Bumped(atom/user)
	honk()
	..()

/obj/structure/statue/bananium/attackby(obj/item/weapon/W, mob/user, params)
	honk()
	..()

/obj/structure/statue/bananium/attack_hand(mob/user)
	honk()
	..()

/obj/structure/statue/bananium/attack_paw(mob/user)
	honk()
	..()

/obj/structure/statue/bananium/proc/honk()
	if(!spam_flag)
		spam_flag = 1
		playsound(src.loc, 'sound/items/bikehorn.ogg', 50, 1)
		spawn(20)
			spam_flag = 0

/////////////////////sandstone/////////////////////////////////////////

/obj/structure/statue/sandstone
	hardness = 0.5
	mineralType = "sandstone"

/obj/structure/statue/sandstone/assistant
	name = "Statue of an assistant"
	desc = "A cheap statue of sandstone for a greyshirt."
	icon_state = "assist"

/////////////////////snow/////////////////////////////////////////

/obj/structure/statue/snow
	hardness = 0.5
	mineralType = "snow"

/obj/structure/statue/snow/snowman
	name = "snowman"
	desc = "Several lumps of snow put together to form a snowman."
	icon_state = "snowman"


code:
/* Table Frames
 * Contains:
 *		Frames
 *		Wooden Frames
 */

/*
 * Normal Frames
 */

/obj/structure/table_frame
	name = "table frame"
	desc = "Four metal legs with four framing rods for a table. You could easily pass through this."
	icon = 'icons/obj/structures.dmi'
	icon_state = "table_frame"
	density = 0
	anchored = 0
	layer = 2.8
	var/framestack = /obj/item/stack/rods
	var/framestackamount = 2

/obj/structure/table_frame/attackby(obj/item/I, mob/user, params)
	if(istype(I, /obj/item/weapon/wrench))
		user << "You start disassembling [src]..."
		playsound(src.loc, 'sound/items/Ratchet.ogg', 50, 1)
		if(do_after(user, 30/I.toolspeed, target = src))
			playsound(src.loc, 'sound/items/Deconstruct.ogg', 50, 1)
			for(var/i = 1, i <= framestackamount, i++)
				new framestack(get_turf(src))
			qdel(src)
			return
	if(istype(I, /obj/item/stack/sheet/plasteel))
		var/obj/item/stack/sheet/plasteel/P = I
		if(P.get_amount() < 1)
			user << "You need one plasteel sheet to do this!"
			return
		user << "You start adding [P] to [src]..."
		if(do_after(user, 50, target = src))
			P.use(1)
			new /obj/structure/table/reinforced(src.loc)
			qdel(src)
		return
	if(istype(I, /obj/item/stack/sheet/metal))
		var/obj/item/stack/sheet/metal/M = I
		if(M.get_amount() < 1)
			user << "You need one metal sheet to do this!"
			return
		user << "You start adding [M] to [src]..."
		if(do_after(user, 20, target = src))
			M.use(1)
			new /obj/structure/table(src.loc)
			qdel(src)
		return
	if(istype(I, /obj/item/stack/sheet/glass))
		var/obj/item/stack/sheet/glass/G = I
		if(G.get_amount() < 1)
			user << "You need one glass sheet to do this!"
			return
		user << "You start adding [G] to [src]..."
		if(do_after(user, 20, target = src))
			G.use(1)

			new /obj/structure/table/glass(src.loc)
			qdel(src)
		return
	if(istype(I, /obj/item/stack/sheet/mineral/silver))
		var/obj/item/stack/sheet/mineral/silver/S = I
		if(S.get_amount() < 1)
			user << "You need one silver sheet to do this!"
			return
		user << "You start adding [S] to [src]..."
		if(do_after(user, 20, target = src))
			S.use(1)
			new /obj/structure/table/optable(src.loc)
			qdel(src)
		return

/*
 * Wooden Frames
 */

/obj/structure/table_frame/wood
	name = "wooden table frame"
	desc = "Four wooden legs with four framing wooden rods for a wooden table. You could easily pass through this."
	icon_state = "wood_frame"
	framestack = /obj/item/stack/sheet/mineral/wood
	framestackamount = 2
	burn_state = FLAMMABLE

/obj/structure/table_frame/wood/attackby(obj/item/I, mob/user, params)
	if(istype(I, /obj/item/weapon/wrench))
		..()
	if(istype(I, /obj/item/stack/sheet/mineral/wood))
		var/obj/item/stack/sheet/mineral/wood/W = I
		if(W.get_amount() < 1)
			user << "You need one wood sheet to do this!"
			return
		user << "You start adding [W] to [src]..."
		if(do_after(user, 20, target = src))
			W.use(1)
			new /obj/structure/table/wood(src.loc)
			qdel(src)
		return
	if(istype(I, /obj/item/stack/tile/carpet))
		var/obj/item/stack/tile/carpet/C = I
		if(C.get_amount() < 1)
			user << "You need one carpet sheet to do this!"
			return
		user << "You start adding [C] to [src]..."
		if(do_after(user, 20, target = src))
			C.use(1)
			new /obj/structure/table/wood/poker(src.loc)
			qdel(src)
		return


code:
/* Tables and Racks
 * Contains:
 *		Tables
 *		Glass Tables
 *		Wooden Tables
 *		Reinforced Tables
 *		Racks
 *		Rack Parts
 */

/*
 * Tables
 */

/obj/structure/table
	name = "table"
	desc = "A square piece of metal standing on four metal legs. It can not move."
	icon = 'icons/obj/smooth_structures/table.dmi'
	icon_state = "table"
	density = 1
	anchored = 1
	layer = 2.8
	climbable = TRUE
	pass_flags = LETPASSTHROW //You can throw objects over this, despite it's density.")
	var/frame = /obj/structure/table_frame
	var/framestack = /obj/item/stack/rods
	var/buildstack = /obj/item/stack/sheet/metal
	var/busy = 0
	var/buildstackamount = 1
	var/framestackamount = 2
	var/deconstructable = 1
	smooth = SMOOTH_TRUE
	canSmoothWith = list(/obj/structure/table, /obj/structure/table/reinforced)

/obj/structure/table/New()
	..()
	for(var/obj/structure/table/T in src.loc)
		if(T != src)
			qdel(T)

/obj/structure/table/update_icon()
	if(smooth)
		queue_smooth(src)
		queue_smooth_neighbors(src)

/obj/structure/table/ex_act(severity, target)
	switch(severity)
		if(1)
			qdel(src)
		if(2)
			if(prob(50))
				table_destroy(1)
			else
				qdel(src)
		if(3)
			if(prob(25))
				table_destroy(1)

/obj/structure/table/blob_act()
	if(prob(75))
		table_destroy(1)
		return

/obj/structure/table/narsie_act()
	if(prob(20))
		new /obj/structure/table/wood(src.loc)

/obj/structure/table/mech_melee_attack(obj/mecha/M)
	visible_message("[M.name] smashes [src] apart!")
	playsound(src.loc, 'sound/weapons/punch4.ogg', 50, 1)
	table_destroy(1)

/obj/structure/table/attack_alien(mob/living/user)
	user.do_attack_animation(src)
	playsound(src.loc, 'sound/weapons/bladeslice.ogg', 50, 1)
	visible_message("[user] slices [src] apart!")
	table_destroy(1)

/obj/structure/table/attack_animal(mob/living/simple_animal/user)
	if(user.environment_smash)
		user.do_attack_animation(src)
		playsound(src.loc, 'sound/weapons/Genhit.ogg', 50, 1)
		visible_message("[user] smashes [src] apart!")
		table_destroy(1)

/obj/structure/table/attack_paw(mob/user)
	attack_hand(user)

/obj/structure/table/attack_hulk(mob/living/carbon/human/user)
	..(user, 1)
	visible_message("[user] smashes [src] apart!")
	playsound(src.loc, 'sound/effects/bang.ogg', 50, 1)
	user.say(pick(";RAAAAAAAARGH!", ";HNNNNNNNNNGGGGGGH!", ";GWAAAAAAAARRRHHH!", "NNNNNNNNGGGGGGGGHH!", ";AAAAAAARRRGH!" ))
	table_destroy(1)
	return 1

/obj/structure/table/attack_tk() // no telehulk sorry
	return

/obj/structure/table/CanPass(atom/movable/mover, turf/target, height=0)
	if(height==0)
		return 1
	if(istype(mover) && mover.checkpass(PASSTABLE))
		return 1
	if(locate(/obj/structure/table) in get_turf(mover))
		return 1
	else
		return !density

/obj/structure/table/CanAStarPass(ID, dir, caller)
	. = !density
	if(ismovableatom(caller))
		var/atom/movable/mover = caller
		. = . || mover.checkpass(PASSTABLE)

/obj/structure/table/proc/tablepush(obj/item/I, mob/user)
	if(get_dist(src, user) < 2)
		var/obj/item/weapon/grab/G = I
		if(G.affecting.buckled)
			user << "[G.affecting] is buckled to [G.affecting.buckled]!"
			return 0
		if(G.state < GRAB_AGGRESSIVE)
			user << "You need a better grip to do that!"
			return 0
		if(!G.confirm())
			return 0
		G.affecting.loc = src.loc
		if(istype(src, /obj/structure/table/optable))
			var/obj/structure/table/optable/OT = src
			G.affecting.resting = 1
			G.affecting.update_canmove()
			visible_message("[G.assailant] has laid [G.affecting] on [src].")
			OT.patient = G.affecting
			OT.check_patient()
			qdel(I)
			return 1
		G.affecting.Weaken(2)
		G.affecting.visible_message("[G.assailant] pushes [G.affecting] onto [src].", \
									"[G.assailant] pushes [G.affecting] onto [src].")
		add_logs(G.assailant, G.affecting, "pushed")
		qdel(I)
		return 1
	qdel(I)

/obj/structure/table/attackby(obj/item/I, mob/user, params)
	if (istype(I, /obj/item/weapon/grab))
		tablepush(I, user)
		return
	if(!(flags&NODECONSTRUCT))
		if (istype(I, /obj/item/weapon/screwdriver))
			if(istype(src, /obj/structure/table/reinforced))
				var/obj/structure/table/reinforced/RT = src
				if(RT.status == 1)
					table_destroy(2, user)
					return
			else
				table_destroy(2, user)
				return

		if (istype(I, /obj/item/weapon/wrench))
			if(istype(src, /obj/structure/table/reinforced))
				var/obj/structure/table/reinforced/RT = src
				if(RT.status == 1)
					table_destroy(3, user)
					return
			else
				table_destroy(3, user)
				return

	if (istype(I, /obj/item/weapon/storage/bag/tray))
		var/obj/item/weapon/storage/bag/tray/T = I
		if(T.contents.len > 0) // If the tray isn't empty
			var/list/obj/item/oldContents = T.contents.Copy()
			T.quick_empty()

			for(var/obj/item/C in oldContents)
				C.loc = src.loc

			user.visible_message("[user] empties [I] on [src].")
			return
		// If the tray IS empty, continue on (tray will be placed on the table like other items)

	if(isrobot(user))
		return

	if(!(I.flags & ABSTRACT)) //rip more parems rip in peace ;_;
		if(user.drop_item())
			I.Move(loc)
			var/list/click_params = params2list(params)
			//Center the icon where the user clicked.
			if(!click_params || !click_params["icon-x"] || !click_params["icon-y"])
				return
			//Clamp it so that the icon never moves more than 16 pixels in either direction (thus leaving the table turf)
			I.pixel_x = Clamp(text2num(click_params["icon-x"]) - 16, -(world.icon_size/2), world.icon_size/2)
			I.pixel_y = Clamp(text2num(click_params["icon-y"]) - 16, -(world.icon_size/2), world.icon_size/2)

/*
 * TABLE DESTRUCTION/DECONSTRUCTION
 */

#define TBL_DESTROY 1
#define TBL_DISASSEMBLE 2
#define TBL_DECONSTRUCT 3

/obj/structure/table/proc/table_destroy(destroy_type, mob/user)
	if(!deconstructable || (flags&NODECONSTRUCT))
		return

	if(destroy_type == TBL_DESTROY)
		for(var/i = 1, i <= framestackamount, i++)
			new framestack(get_turf(src))
		for(var/i = 1, i <= buildstackamount, i++)
			new buildstack(get_turf(src))
		qdel(src)
		return

	if(destroy_type == TBL_DISASSEMBLE)
		user << "You start disassembling [src]..."
		playsound(src.loc, 'sound/items/Screwdriver.ogg', 50, 1)
		if(do_after(user, 20, target = src))
			new frame(src.loc)
			for(var/i = 1, i <= buildstackamount, i++)
				new buildstack(get_turf(src))
			qdel(src)
			return

	if(destroy_type == TBL_DECONSTRUCT)
		user << "You start deconstructing [src]..."
		playsound(src.loc, 'sound/items/Ratchet.ogg', 50, 1)
		if(do_after(user, 40, target = src))
			for(var/i = 1, i <= framestackamount, i++)
				new framestack(get_turf(src))
			for(var/i = 1, i <= buildstackamount, i++)
				new buildstack(get_turf(src))
			playsound(src.loc, 'sound/items/Deconstruct.ogg', 50, 1)
			qdel(src)
			return

/*
 * Glass tables
 */
/obj/structure/table/glass
	name = "glass table"
	desc = "What did I say about leaning on the glass tables? Now you need surgery."
	icon = 'icons/obj/smooth_structures/glass_table.dmi'
	icon_state = "glass_table"
	buildstack = /obj/item/stack/sheet/glass
	canSmoothWith = null

/obj/structure/table/glass/tablepush(obj/item/I, mob/user)
	if(..())
		visible_message("[src] breaks!")
		playsound(src.loc, "shatter", 50, 1)
		new frame(src.loc)
		new /obj/item/weapon/shard(src.loc)
		qdel(src)

/obj/structure/table/glass/climb_structure(mob/user)
	if(..())
		visible_message("[src] breaks!")
		playsound(src.loc, "shatter", 50, 1)
		new frame(src.loc)
		new /obj/item/weapon/shard(src.loc)
		qdel(src)
		user.Weaken(5)

/*
 * Wooden tables
 */

/obj/structure/table/wood
	name = "wooden table"
	desc = "Do not apply fire to this. Rumour says it burns easily."
	icon = 'icons/obj/smooth_structures/wood_table.dmi'
	icon_state = "wood_table"
	frame = /obj/structure/table_frame/wood
	framestack = /obj/item/stack/sheet/mineral/wood
	buildstack = /obj/item/stack/sheet/mineral/wood
	burn_state = FLAMMABLE
	burntime = 20
	canSmoothWith = list(/obj/structure/table/wood, /obj/structure/table/wood/poker)

/obj/structure/table/wood/narsie_act()
	return

/obj/structure/table/wood/poker //No specialties, Just a mapping object.
	name = "gambling table"
	desc = "A seedy table for seedy dealings in seedy places."
	icon = 'icons/obj/smooth_structures/poker_table.dmi'
	icon_state = "poker_table"
	buildstack = /obj/item/stack/tile/carpet
	canSmoothWith = list(/obj/structure/table/wood/poker, /obj/structure/table/wood)

/obj/structure/table/wood/poker/narsie_act()
	new /obj/structure/table/wood(src.loc)

/*
 * Reinforced tables
 */
/obj/structure/table/reinforced
	name = "reinforced table"
	desc = "A reinforced version of the four legged table, much harder to simply deconstruct."
	icon = 'icons/obj/smooth_structures/reinforced_table.dmi'
	icon_state = "r_table"
	var/status = 2
	buildstack = /obj/item/stack/sheet/plasteel
	canSmoothWith = list(/obj/structure/table/reinforced, /obj/structure/table)

/obj/structure/table/reinforced/attackby(obj/item/weapon/W, mob/user, params)
	if (istype(W, /obj/item/weapon/weldingtool))
		var/obj/item/weapon/weldingtool/WT = W
		if(WT.remove_fuel(0, user))
			if(src.status == 2)
				user << "You start weakening the reinforced table..."
				playsound(src.loc, 'sound/items/Welder.ogg', 50, 1)
				if (do_after(user, 50/W.toolspeed, target = src))
					if(!src || !WT.isOn()) return
					user << "You weaken the table."
					src.status = 1
			else
				user << "You start strengthening the reinforced table..."
				playsound(src.loc, 'sound/items/Welder.ogg', 50, 1)
				if (do_after(user, 50/W.toolspeed, target = src))
					if(!src || !WT.isOn()) return
					user << "You strengthen the table."
					src.status = 2
			return
	..()

/obj/structure/table/reinforced/attack_paw(mob/user)
	attack_hand(user)

/obj/structure/table/reinforced/attack_hulk(mob/living/carbon/human/user)
	..(user, 1)
	if(prob(75))
		playsound(src, 'sound/effects/meteorimpact.ogg', 100, 1)
		user << text("You kick [src] into pieces.")
		user.say(pick(";RAAAAAAAARGH!", ";HNNNNNNNNNGGGGGGH!", ";GWAAAAAAAARRRHHH!", "NNNNNNNNGGGGGGGGHH!", ";AAAAAAARRRGH!" ))
		table_destroy(1)
	else
		playsound(src, 'sound/effects/bang.ogg', 50, 1)
		user << text("You kick [src].")
	return 1

/*
 * Surgery Tables
 */

/obj/structure/table/optable
	name = "operating table"
	desc = "Used for advanced medical procedures."
	icon = 'icons/obj/surgery.dmi'
	icon_state = "optable"
	buildstack = /obj/item/stack/sheet/mineral/silver
	smooth = SMOOTH_FALSE
	can_buckle = 1
	buckle_lying = 1
	buckle_requires_restraints = 1
	var/mob/living/carbon/human/patient = null
	var/obj/machinery/computer/operating/computer = null

/obj/structure/table/optable/New()
	..()
	for(var/dir in cardinal)
		computer = locate(/obj/machinery/computer/operating, get_step(src, dir))
		if(computer)
			computer.table = src
			break

/obj/structure/table/optable/proc/check_patient()
	var/mob/M = locate(/mob/living/carbon/human, loc)
	if(M)
		if(M.resting)
			patient = M
			return 1
	else
		patient = null
		return 0

/*
 * Racks
 */
/obj/structure/rack
	name = "rack"
	desc = "Different from the Middle Ages version."
	icon = 'icons/obj/objects.dmi'
	icon_state = "rack"
	density = 1
	anchored = 1
	pass_flags = LETPASSTHROW //You can throw objects over this, despite it's density.
	var/health = 5

/obj/structure/rack/ex_act(severity, target)
	switch(severity)
		if(1)
			qdel(src)
		if(2)
			if(prob(50))
				rack_destroy()
			else
				qdel(src)
		if(3)
			if(prob(25))
				rack_destroy()

/obj/structure/rack/blob_act()
	if(prob(75))
		qdel(src)
		return
	else if(prob(50))
		rack_destroy()
		return

/obj/structure/rack/mech_melee_attack(obj/mecha/M)
	visible_message("[M.name] smashes [src] apart!")
	rack_destroy(1)

/obj/structure/rack/CanPass(atom/movable/mover, turf/target, height=0)
	if(height==0) return 1
	if(src.density == 0) //Because broken racks -Agouri |TODO: SPRITE!|
		return 1
	if(istype(mover) && mover.checkpass(PASSTABLE))
		return 1
	else
		return 0

/obj/structure/rack/CanAStarPass(ID, dir, caller)
	. = !density
	if(ismovableatom(caller))
		var/atom/movable/mover = caller
		. = . || mover.checkpass(PASSTABLE)

/obj/structure/rack/MouseDrop_T(obj/O, mob/user)
	if ((!( istype(O, /obj/item/weapon) ) || user.get_active_hand() != O))
		return
	if(isrobot(user))
		return
	if(!user.drop_item())
		user << "\The [O] is stuck to your hand, you cannot put it in the rack!"
		return
	if (O.loc != src.loc)
		step(O, get_dir(O, src))
	return

/obj/structure/rack/attackby(obj/item/weapon/W, mob/user, params)
	if (istype(W, /obj/item/weapon/wrench) && !(flags&NODECONSTRUCT))
		playsound(src.loc, 'sound/items/Ratchet.ogg', 50, 1)
		rack_destroy()
		return

	if(isrobot(user))
		return
	if(!user.drop_item())
		user << "\The [W] is stuck to your hand, you cannot put it in the rack!"
		return
	W.Move(loc)
	return 1

/obj/structure/rack/attack_paw(mob/living/user)
	attack_hand(user)

/obj/structure/rack/attack_hulk(mob/living/carbon/human/user)
	..(user, 1)
	rack_destroy()
	return 1

/obj/structure/rack/attack_hand(mob/living/user)
	user.changeNext_move(CLICK_CD_MELEE)
	user.do_attack_animation(src)
	playsound(loc, 'sound/items/dodgeball.ogg', 80, 1)
	user.visible_message("[user] kicks [src].", \
						 "You kick [src].")
	health -= rand(1,2)
	healthcheck()

/obj/structure/rack/attack_alien(mob/living/user)
	user.do_attack_animation(src)
	visible_message("[user] slices [src] apart.")
	rack_destroy()

/obj/structure/rack/attack_animal(mob/living/simple_animal/user)
	if(user.environment_smash)
		user.do_attack_animation(src)
		visible_message("[user] smashes [src] apart.")
		rack_destroy()
/obj/structure/rack/attack_tk() // no telehulk sorry
	return

/obj/structure/rack/proc/healthcheck()
	if(health <= 0)
		rack_destroy()
	return

/*
 * Rack destruction
 */

/obj/structure/rack/proc/rack_destroy()
	if(!(flags&NODECONSTRUCT))
		density = 0
		var/obj/item/weapon/rack_parts/newparts = new(loc)
		transfer_fingerprints_to(newparts)
	qdel(src)

/*
 * Rack Parts
 */

/obj/item/weapon/rack_parts
	name = "rack parts"
	desc = "Parts of a rack."
	icon = 'icons/obj/items.dmi'
	icon_state = "rack_parts"
	flags = CONDUCT
	materials = list(MAT_METAL=2000)

/obj/item/weapon/rack_parts/attackby(obj/item/weapon/W, mob/user, params)
	..()
	if (istype(W, /obj/item/weapon/wrench))
		new /obj/item/stack/sheet/metal( user.loc )
		qdel(src)
		return
	return

/obj/item/weapon/rack_parts/attack_self(mob/user)
	user << "You start constructing rack..."
	if (do_after(user, 50, target = src))
		if(!user.drop_item())
			return
		var/obj/structure/rack/R = new /obj/structure/rack( user.loc )
		R.add_fingerprint(user)
		qdel(src)
		return


code:
#define TANK_DISPENSER_CAPACITY 10

/obj/structure/tank_dispenser
	name = "tank dispenser"
	desc = "A simple yet bulky storage device for gas tanks. Holds up to 10 oxygen tanks and 10 plasma tanks."
	icon = 'icons/obj/objects.dmi'
	icon_state = "dispenser"
	density = 1
	anchored = 1
	var/oxygentanks = TANK_DISPENSER_CAPACITY
	var/plasmatanks = TANK_DISPENSER_CAPACITY

/obj/structure/tank_dispenser/oxygen
	plasmatanks = 0

/obj/structure/tank_dispenser/plasma
	oxygentanks = 0

/obj/structure/tank_dispenser/New()
	for(var/i in 1 to oxygentanks)
		new /obj/item/weapon/tank/internals/oxygen(src)
	for(var/i in 1 to plasmatanks)
		new /obj/item/weapon/tank/internals/plasma(src)
	update_icon()

/obj/structure/tank_dispenser/update_icon()
	overlays.Cut()
	switch(oxygentanks)
		if(1 to 3)
			overlays += "oxygen-[oxygentanks]"
		if(4 to TANK_DISPENSER_CAPACITY)
			overlays += "oxygen-4"
	switch(plasmatanks)
		if(1 to 4)
			overlays += "plasma-[plasmatanks]"
		if(5 to TANK_DISPENSER_CAPACITY)
			overlays += "plasma-5"

/obj/structure/tank_dispenser/attackby(obj/item/I, mob/user, params)
	var/full
	if(istype(I, /obj/item/weapon/tank/internals/plasma))
		if(plasmatanks < TANK_DISPENSER_CAPACITY)
			plasmatanks++
		else
			full = TRUE
	else if(istype(I, /obj/item/weapon/tank/internals/oxygen))
		if(oxygentanks < TANK_DISPENSER_CAPACITY)
			oxygentanks++
		else
			full = TRUE
	else
		user << "[I] does not fit into [src]."
		return
	if(full)
		user << "[src] can't hold anymore of [I]."
		return

	if(!user.drop_item())
		return
	I.loc = src
	user << "You put [I] in [src]."
	update_icon()

/obj/structure/tank_dispenser/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = 0, \
										datum/tgui/master_ui = null, datum/ui_state/state = physical_state)
	ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
	if(!ui)
		ui = new(user, src, ui_key, "tank_dispenser", name, 275, 100, master_ui, state)
		ui.open()

/obj/structure/tank_dispenser/ui_data(mob/user)
	var/list/data = list()
	data["oxygen"] = oxygentanks
	data["plasma"] = plasmatanks

	return data

/obj/structure/tank_dispenser/ui_act(action, params)
	if(..())
		return
	switch(action)
		if("plasma")
			var/obj/item/weapon/tank/internals/plasma/tank = locate() in src
			if(tank)
				usr.put_in_hands(tank)
				plasmatanks--
			. = TRUE
		if("oxygen")
			var/obj/item/weapon/tank/internals/oxygen/tank = locate() in src
			if(tank)
				usr.put_in_hands(tank)
				oxygentanks--
			. = TRUE
	update_icon()

#undef TANK_DISPENSER_CAPACITY


code:
/obj/structure/target_stake
	name = "target stake"
	desc = "A thin platform with negatively-magnetized wheels."
	icon = 'icons/obj/objects.dmi'
	icon_state = "target_stake"
	density = 1
	flags = CONDUCT
	var/obj/item/target/pinned_target

/obj/structure/target_stake/Destroy()
	if(pinned_target)
		pinned_target.nullPinnedLoc()
	return ..()

/obj/structure/target_stake/proc/nullPinnedTarget()
	pinned_target = null

/obj/structure/target_stake/Move()
	..()
	if(pinned_target)
		pinned_target.loc = loc

/obj/structure/target_stake/attackby(obj/item/target/T, mob/user)
	if(pinned_target)
		return
	if(istype(T) && user.drop_item())
		pinned_target = T
		T.pinnedLoc = src
		T.density = 1
		T.layer = OBJ_LAYER + 0.1
		T.loc = loc
		user << "You slide the target into the stake."

/obj/structure/target_stake/attack_hand(mob/user)
	if(pinned_target)
		removeTarget(user)

/obj/structure/target_stake/proc/removeTarget(mob/user)
	pinned_target.layer = OBJ_LAYER
	pinned_target.loc = user.loc
	pinned_target.nullPinnedLoc()
	nullPinnedTarget()
	if(ishuman(user))
		if(!user.get_active_hand())
			user.put_in_hands(pinned_target)
			user << "You take the target out of the stake."
	else
		pinned_target.loc = get_turf(user)
		user << "You take the target out of the stake."

/obj/structure/target_stake/bullet_act(obj/item/projectile/P)
	if(pinned_target)
		pinned_target.bullet_act(P)
	else
		..()


code:
// A place where tube pods stop, and people can get in or out.
// Mappers: use "Generate Instances from Directions" for this
//  one.
/obj/structure/transit_tube/station
	name = "station tube station"
	icon = 'icons/obj/atmospherics/pipes/transit_tube_station.dmi'
	icon_state = "closed"
	exit_delay = 1
	enter_delay = 2
	tube_construction = /obj/structure/c_transit_tube/station
	var/pod_moving = 0
	var/cooldown_delay = 50
	var/launch_cooldown = 0
	var/reverse_launch = 0

	var/const/OPEN_DURATION = 6
	var/const/CLOSE_DURATION = 6

/obj/structure/transit_tube/station/New()
	..()
	SSobj.processing += src

/obj/structure/transit_tube/station/Destroy()
	SSobj.processing -= src
	return ..()

// Stations which will send the tube in the opposite direction after their stop.
/obj/structure/transit_tube/station/reverse
	tube_construction = /obj/structure/c_transit_tube/station/reverse
	reverse_launch = 1

/obj/structure/transit_tube/station/should_stop_pod(pod, from_dir)
	return 1

/obj/structure/transit_tube/station/Bumped(mob/AM as mob|obj)
	if(!pod_moving && icon_state == "open" && istype(AM, /mob))
		for(var/obj/structure/transit_tube_pod/pod in loc)
			if(!pod.moving && pod.dir in directions())
				AM.loc = pod
				return

//pod insertion
/obj/structure/transit_tube/station/MouseDrop_T(obj/structure/c_transit_tube_pod/R, mob/user)
	if(!user.canmove || user.stat || user.restrained())
		return
	if (!istype(R) || get_dist(user, src) > 1 || get_dist(src,R) > 1)
		return
	for(var/obj/structure/transit_tube_pod/pod in loc)
		return //no fun allowed
	var/obj/structure/transit_tube_pod/T = new/obj/structure/transit_tube_pod(src)
	R.transfer_fingerprints_to(T)
	T.add_fingerprint(user)
	T.loc = src.loc
	T.dir = turn(src.dir, -90)
	user.visible_message("[user] inserts the [R].", "You insert the [R].")
	qdel(R)

/obj/structure/transit_tube/station/attack_hand(mob/user)
	if(!pod_moving)
		for(var/obj/structure/transit_tube_pod/pod in loc)
			if(!pod.moving && pod.dir in directions())
				if(icon_state == "closed")
					open_animation()

				else if(icon_state == "open")
					if(pod.contents.len && user.loc != pod)
						user.visible_message("[user] starts emptying [pod]'s contents onto the floor.", "You start emptying [pod]'s contents onto the floor...")
						if(do_after(user, 10, target = src)) //So it doesn't default to close_animation() on fail
							if(pod.loc == loc)
								for(var/atom/movable/AM in pod)
									AM.loc = get_turf(user)
									if(ismob(AM))
										var/mob/M = AM
										M.Weaken(5)

					else
						close_animation()
			break

/obj/structure/transit_tube/station/attackby(obj/item/W, mob/user, params)
	if(istype(W, /obj/item/weapon/grab) && icon_state == "open")
		var/obj/item/weapon/grab/G = W
		if(ismob(G.affecting) && G.state >= GRAB_AGGRESSIVE)
			var/mob/GM = G.affecting
			for(var/obj/structure/transit_tube_pod/pod in loc)
				pod.visible_message("[user] starts putting [GM] into the [pod]!")
				if(do_after(user, 15, target = src) && GM && G && G.affecting == GM)
					GM.Weaken(5)
					src.Bumped(GM)
					qdel(G)
				break
	if(istype(W, /obj/item/weapon/crowbar))
		for(var/obj/structure/transit_tube_pod/pod in loc)
			if(pod.contents)
				user << "Empty the pod first!"
				return
			user.visible_message("[user] removes the [pod].", "You remove the [pod].")
			var/obj/structure/c_transit_tube_pod/R = new/obj/structure/c_transit_tube_pod(src.loc)
			pod.transfer_fingerprints_to(R)
			R.add_fingerprint(user)
			qdel(pod)
	..(W, user)

/obj/structure/transit_tube/station/proc/open_animation()
	if(icon_state == "closed")
		icon_state = "opening"
		spawn(OPEN_DURATION)
			if(icon_state == "opening")
				icon_state = "open"

/obj/structure/transit_tube/station/proc/close_animation()
	if(icon_state == "open")
		icon_state = "closing"
		spawn(CLOSE_DURATION)
			if(icon_state == "closing")
				icon_state = "closed"

/obj/structure/transit_tube/station/proc/launch_pod()
	if(launch_cooldown >= world.time)
		return
	for(var/obj/structure/transit_tube_pod/pod in loc)
		if(!pod.moving && turn(pod.dir, (reverse_launch ? 180 : 0)) in directions())
			pod_moving = 1
			close_animation()
			sleep(CLOSE_DURATION + 2)
			if(icon_state == "closed" && pod)
				pod.follow_tube(reverse_launch)
			pod_moving = 0
			return 1
	return 0

/obj/structure/transit_tube/station/process()
	if(!pod_moving)
		launch_pod()

/obj/structure/transit_tube/station/pod_stopped(obj/structure/transit_tube_pod/pod, from_dir)
	pod_moving = 1
	spawn(5)
		launch_cooldown = world.time + cooldown_delay
		open_animation()
		sleep(OPEN_DURATION + 2)
		pod_moving = 0
		pod.mix_air()

// Tube station directions are simply 90 to either side of
//  the exit.
/obj/structure/transit_tube/station/init_dirs()
	tube_dirs = list(turn(dir, 90), turn(dir, -90))


code:
// Basic transit tubes. Straight pieces, curved sections,
//  and basic splits/joins (no routing logic).
// Mappers: you can use "Generate Instances from Icon-states"
//  to get the different pieces.
/obj/structure/transit_tube
	icon = 'icons/obj/atmospherics/pipes/transit_tube.dmi'
	icon_state = "E-W"
	density = 1
	layer = 3.1
	anchored = 1
	var/tube_construction = /obj/structure/c_transit_tube
	var/list/tube_dirs = null
	var/exit_delay = 1
	var/enter_delay = 0

	// alldirs in global.dm is the same list of directions, but since
	//  the specific order matters to get a usable icon_state, it is
	//  copied here so that, in the unlikely case that alldirs is changed,
	//  this continues to work.
	var/global/list/tube_dir_list = list(NORTH, SOUTH, EAST, WEST, NORTHEAST, NORTHWEST, SOUTHEAST, SOUTHWEST)

/obj/structure/transit_tube/CanPass(atom/movable/mover, turf/target)
	if(istype(mover) && mover.checkpass(PASSGLASS))
		return 1
	return !density

// When destroyed by explosions, properly handle contents.
obj/structure/transit_tube/ex_act(severity, target)
	if(3 - severity >= 0)
		var/oldloc = loc
		..(severity + 1)
		for(var/atom/movable/AM in contents)
			AM.loc = oldloc

/obj/structure/transit_tube/New(loc)
	..(loc)

	if(tube_dirs == null)
		init_dirs()

/obj/structure/transit_tube/attackby(obj/item/W, mob/user, params)
	if(istype(W, /obj/item/weapon/wrench))
		if(copytext(icon_state, 1, 3) != "D-") //decorative diagonals cannot be unwrenched directly
			for(var/obj/structure/transit_tube_pod/pod in src.loc)
				user << "Remove the pod first!"
				return
			user.visible_message("[user] starts to deattach \the [src].", "You start to deattach the [name]...")
			playsound(src.loc, 'sound/items/Ratchet.ogg', 50, 1)
			if(do_after(user, 35/W.toolspeed, target = src))
				user << "You deattach the [name]."
				var/obj/structure/R = new tube_construction(src.loc)
				R.icon_state = src.icon_state
				src.transfer_fingerprints_to(R)
				R.add_fingerprint(user)
				src.destroy_diagonals()
				qdel(src)
	if(istype(W, /obj/item/weapon/crowbar))
		for(var/obj/structure/transit_tube_pod/pod in src.loc)
			pod.attackby(W, user)

//destroys disconnected decorative diagonals
/obj/structure/transit_tube/proc/destroy_diagonals()
	for(var/obj/structure/transit_tube/D in orange(1, src))
		if(copytext(D.icon_state, 1, 3) == "D-") //is diagonal
			var/my_dir = text2dir_extended(copytext(D.icon_state, 3, 5))
			var/is_connecting = 0
			for(var/obj/structure/transit_tube/N in orange(1,D))
				if( (( get_dir(D,N) == turn(my_dir, -45) && D.has_exit(turn(my_dir, 90)) ) || \
					( get_dir(D,N) == turn(my_dir, 45) && D.has_exit(turn(my_dir, -90))) ) && \
					D != src )
					is_connecting = 1
					break
			if(!is_connecting)
				qdel(D)

// Called to check if a pod should stop upon entering this tube.
/obj/structure/transit_tube/proc/should_stop_pod(pod, from_dir)
	return 0

// Called when a pod stops in this tube section.
/obj/structure/transit_tube/proc/pod_stopped(pod, from_dir)
	return

// Returns a /list of directions this tube section can connect to.
//  Tubes that have some sort of logic or changing direction might
//  override it with additional logic.
/obj/structure/transit_tube/proc/directions()
	return tube_dirs

/obj/structure/transit_tube/proc/has_entrance(from_dir)
	from_dir = turn(from_dir, 180)

	for(var/direction in directions())
		if(direction == from_dir)
			return 1

	return 0

/obj/structure/transit_tube/proc/has_exit(in_dir)
	for(var/direction in directions())
		if(direction == in_dir)
			return 1

	return 0

// Searches for an exit direction within 45 degrees of the
//  specified dir. Returns that direction, or 0 if none match.
/obj/structure/transit_tube/proc/get_exit(in_dir)
	var/near_dir = 0
	var/in_dir_cw = turn(in_dir, -45)
	var/in_dir_ccw = turn(in_dir, 45)

	for(var/direction in directions())
		if(direction == in_dir)
			return direction

		else if(direction == in_dir_cw)
			near_dir = direction

		else if(direction == in_dir_ccw)
			near_dir = direction

	return near_dir

// Return how many BYOND ticks to wait before entering/exiting
//  the tube section. Default action is to return the value of
//  a var, which wouldn't need a proc, but it makes it possible
//  for later tube types to interact in more interesting ways
//  such as being very fast in one direction, but slow in others
/obj/structure/transit_tube/proc/exit_delay(pod, to_dir)
	return exit_delay

/obj/structure/transit_tube/proc/enter_delay(pod, to_dir)
	return enter_delay

// Parse the icon_state into a list of directions.
// This means that mappers can use Dream Maker's built in
//  "Generate Instances from Icon-states" option to get all
//  variations. Additionally, as a separate proc, sub-types
//  can handle it more intelligently.
/obj/structure/transit_tube/proc/init_dirs()
	if(icon_state == "auto")
		// Additional delay, for map loading.
		spawn(1)
			init_dirs_automatic()

	else
		tube_dirs = parse_dirs(icon_state)

		if(copytext(icon_state, 1, 3) == "D-" || findtextEx(icon_state, "Pass"))
			density = 0

// Initialize dirs by searching for tubes that do/might connect
//  on nearby turfs. Create corner pieces if nessecary.
// Pick two directions, preferring tubes that already connect
//  to loc, or other auto tubes if there aren't enough connections.
/obj/structure/transit_tube/proc/init_dirs_automatic()
	var/list/connected = list()
	var/list/connected_auto = list()

	for(var/direction in tube_dir_list)
		var/location = get_step(loc, direction)
		for(var/obj/structure/transit_tube/tube in location)
			if(tube.directions() == null && tube.icon_state == "auto")
				connected_auto += direction
				break

			else if(turn(direction, 180) in tube.directions())
				connected += direction
				break

	connected += connected_auto

	tube_dirs = select_automatic_dirs(connected)

	if(length(tube_dirs) == 2 && tube_dir_list.Find(tube_dirs[1]) > tube_dir_list.Find(tube_dirs[2]))
		tube_dirs.Swap(1, 2)

	generate_automatic_corners(tube_dirs)
	select_automatic_icon_state(tube_dirs)

// Given a list of directions, look a pair that forms a 180 or
//  135 degree angle, and return a list containing the pair.
//  If none exist, return list(connected[1], turn(connected[1], 180)
/obj/structure/transit_tube/proc/select_automatic_dirs(connected)
	if(length(connected) < 1)
		return list()

	for(var/i = 1, i <= length(connected), i++)
		for(var/j = i + 1, j <= length(connected), j++)
			var/d1 = connected[i]
			var/d2 = connected[j]

			if(d1 == turn(d2, 135) || d1 == turn(d2, 180) || d1 == turn(d2, 225))
				return list(d1, d2)

	return list(connected[1], turn(connected[1], 180))

/obj/structure/transit_tube/proc/select_automatic_icon_state(directions)
	if(length(directions) == 2)
		icon_state = "[dir2text_short(directions[1])]-[dir2text_short(directions[2])]"

// Look for diagonal directions, generate the decorative corners in each.
/obj/structure/transit_tube/proc/generate_automatic_corners(directions)
	for(var/direction in directions)
		if(direction == 5 || direction == 6 || direction == 9 || direction == 10)
			if(direction & NORTH)
				create_automatic_decorative_corner(get_step(loc, NORTH), direction ^ 3)

			else
				create_automatic_decorative_corner(get_step(loc, SOUTH), direction ^ 3)

			if(direction & EAST)
				create_automatic_decorative_corner(get_step(loc, EAST), direction ^ 12)

			else
				create_automatic_decorative_corner(get_step(loc, WEST), direction ^ 12)

// Generate a corner, if one doesn't exist for the direction on the turf.
/obj/structure/transit_tube/proc/create_automatic_decorative_corner(location, direction)
	var/state = "D-[dir2text_short(direction)]"

	for(var/obj/structure/transit_tube/tube in location)
		if(tube.icon_state == state)
			return

	var/obj/structure/transit_tube/tube = new(location)
	tube.icon_state = state
	tube.init_dirs()

// Uses a list() to cache return values. Since they should
//  never be edited directly, all tubes with a certain
//  icon_state can just reference the same list. In theory,
//  reduces memory usage, and improves CPU cache usage.
//  In reality, I don't know if that is quite how BYOND works,
//  but it is probably safer to assume the existence of, and
//  rely on, a sufficiently smart compiler/optimizer.
/obj/structure/transit_tube/proc/parse_dirs(text)
	var/global/list/direction_table = list()

	if(text in direction_table)
		return direction_table[text]

	var/list/split_text = splittext(text, "-")

	// If the first token is D, the icon_state represents
	//  a purely decorative tube, and doesn't actually
	//  connect to anything.
	if(split_text[1] == "D")
		direction_table[text] = list()
		return null

	var/list/directions = list()

	for(var/text_part in split_text)
		var/direction = text2dir_extended(text_part)

		if(direction > 0)
			directions += direction

	direction_table[text] = directions
	return directions


code:
// transit tube construction

// normal transit tubes
/obj/structure/c_transit_tube
	name = "unattached transit tube"
	icon = 'icons/obj/atmospherics/pipes/transit_tube.dmi'
	icon_state = "E-W" //icon_state decides which tube will be built
	density = 0
	layer = 3.1 //same as the built tube
	anchored = 0

/obj/structure/c_transit_tube/examine(mob/user)
	..()
	user << "Alt-click to rotate it clockwise."

//wrapper for turn that changes the transit tube formatted icon_state instead of the dir
/obj/structure/c_transit_tube/proc/tube_turn(angle)
	var/list/badtubes = list("W-E", "W-E-Pass", "S-N", "S-N-Pass", "SW-NE", "SE-NW")
	var/list/split_text = splittext(icon_state, "-")
	for(var/i=1; i<=split_text.len; i++)
		var/curdir = text2dir_extended(split_text[i]) //0 if not a valid direction (e.g. Pass, Block)
		if(curdir)
			split_text[i] = dir2text_short(turn(curdir, angle))
	var/newdir = jointext(split_text, "-")
	if(badtubes.Find(newdir))
		split_text.Swap(1,2)
		newdir = jointext(split_text, "-")
	icon_state = newdir

/obj/structure/c_transit_tube/proc/tube_flip()
	var/list/split_text = splittext(icon_state, "-")
	//skip straight pipes
	if(length(split_text[2]) < 2)
		return
	//for junctions, just swap the diagonals with each other
	if(split_text.len == 3 && split_text[3] != "Pass")
		split_text.Swap(2,3)
	else if(length(split_text[1]) == 2 && length(split_text[2]) == 2) //diagonals
		split_text[1] = copytext(split_text[1],1,2) + copytext(split_text[2],2,3)
		split_text[2] = copytext(split_text[2],1,2) + ((copytext(split_text[2],2,3) == "E") ? "W" : "E")
	//for curves, swap the diagonal direction that is not in the same axis as the cardinal direction
	else
		if(split_text[1] == "N" || split_text[1] == "S")
			split_text[2] = copytext(split_text[2],1,2) + ((copytext(split_text[2],2,3) == "E") ? "W" : "E")
		else
			split_text[2] = ((copytext(split_text[2],1,2) == "N") ? "S" : "N") + copytext(split_text[2],2,3)
	icon_state = jointext(split_text, "-")

// disposals-style flip and rotate verbs
/obj/structure/c_transit_tube/verb/rotate()
	set name = "Rotate Tube CW"
	set category = "Object"
	set src in view(1)

	if(usr.incapacitated())
		return

	tube_turn(-90)

/obj/structure/c_transit_tube/AltClick(mob/user)
	..()
	if(user.incapacitated())
		user << "You can't do that right now!"
		return
	if(!in_range(src, user))
		return
	else
		rotate()

/obj/structure/c_transit_tube/verb/rotate_ccw()
	set name = "Rotate Tube CCW"
	set category = "Object"
	set src in view(1)

	if(usr.incapacitated())
		return

	tube_turn(90)

/obj/structure/c_transit_tube/verb/flip()
	set name = "Flip"
	set category = "Object"
	set src in view(1)

	if(usr.incapacitated())
		return

	tube_flip()

/obj/structure/c_transit_tube/proc/buildtube()
	var/obj/structure/transit_tube/R = new/obj/structure/transit_tube(src.loc)
	R.icon_state = src.icon_state
	R.init_dirs()
	R.generate_automatic_corners(R.tube_dirs)
	return R

/obj/structure/c_transit_tube/attackby(obj/item/I, mob/user, params)
	if(istype(I, /obj/item/weapon/wrench))
		user << "You start attaching the [name]..."
		src.add_fingerprint(user)
		if(do_after(user, 40/I.toolspeed, target = src))
			if(!src) return
			user << "You attach the [name]."
			var/obj/structure/transit_tube/R = src.buildtube()
			src.transfer_fingerprints_to(R)
			qdel(src)
			return

// transit tube station
/obj/structure/c_transit_tube/station
	name = "unattached through station"
	icon = 'icons/obj/atmospherics/pipes/transit_tube_station.dmi'
	icon_state = "closed"

/obj/structure/c_transit_tube/station/tube_turn(var/angle)
	src.dir = turn(src.dir, angle)

/obj/structure/c_transit_tube/station/tube_flip()
	src.tube_turn(180)

/obj/structure/c_transit_tube/station/buildtube()
	var/obj/structure/transit_tube/station/R = new/obj/structure/transit_tube/station(src.loc)
	R.dir = src.dir
	R.init_dirs()
	return R

// reverser station, used for the terminus
/obj/structure/c_transit_tube/station/reverse
	name = "unattached terminus station"

/obj/structure/c_transit_tube/station/reverse/buildtube()
	var/obj/structure/transit_tube/station/reverse/R = new/obj/structure/transit_tube/station/reverse(src.loc)
	R.dir = src.dir
	R.init_dirs()
	return R

// block, used after the terminus of a transit tube station, decorative only
// code-wise they're normal tubes but they're ~speshul~ because they care about the dir instead of the icon_state
// in that sense they're the same as stations and can reuse their flip and rotate verbs
/obj/structure/c_transit_tube/station/block
	name = "unattached tube blocker"
	icon = 'icons/obj/atmospherics/pipes/transit_tube.dmi'
	icon_state = "Block"

/obj/structure/c_transit_tube/station/block/buildtube()
	var/obj/structure/transit_tube/R = new/obj/structure/transit_tube(src.loc)
	R.icon_state = src.icon_state
	R.dir = src.dir
	R.init_dirs()
	return R

//transit tube pod
//see station.dm for the logic
/obj/structure/c_transit_tube_pod
	name = "unattached transit tube pod"
	icon = 'icons/obj/atmospherics/pipes/transit_tube_pod.dmi'
	icon_state = "pod"
	anchored = 0
	density = 0