code:
///?
/obj/structure/tank_holder
	name = "tank holder"
	desc = "A metallic frame that can hold tanks and extinguishers."
	icon = 'icons/obj/tank.dmi'
	icon_state = "holder"

	custom_materials = list(/datum/material/iron = 2000)

	density = FALSE
	anchored = FALSE
	pass_flags_self = LETPASSTHROW
	max_integrity = 20

	resistance_flags = FIRE_PROOF

	/// The stored tank. If this is a path, it gets created into contents at Initialize.
	var/obj/item/tank

/obj/structure/tank_holder/Initialize(mapload)
	. = ..()
	if(tank)
		var/obj/item/tank_ = new tank(null)
		tank = null
		SEND_SIGNAL(tank_, COMSIG_CONTAINER_TRY_ATTACH, src, null)

/obj/structure/tank_holder/Destroy()
	QDEL_NULL(tank)
	return ..()

/obj/structure/tank_holder/CanAllowThrough(atom/movable/mover, border_dir)
	. = ..()
	if(istype(mover) && mover.throwing)
		return TRUE

/obj/structure/tank_holder/examine(mob/user)
	. = ..()
	. += "It is [anchored ? "wrenched to the floor." : "The bolts on the bottom are unsecured."]
"
	if(tank)
		. += "It is holding one [tank]."
	else
		. += "It is empty."
	. += span_notice("It is held together by some screws.")

/obj/structure/tank_holder/attackby(obj/item/W, mob/living/user, params)
	//if(user.combat_mode) //SEPTIC EDIT REMOVAL
	//SEPTIC EDIT
	if(!IS_HELP_INTENT(user, params2list(params)))
	//SEPTIC EDIT END
		return ..()
	if(W.tool_behaviour == TOOL_WRENCH)
		to_chat(user, span_notice("You begin to [anchored ? "unwrench" : "wrench"] [src]."))
		if(W.use_tool(src, user, 20, volume=50))
			to_chat(user, span_notice("You successfully [anchored ? "unwrench" : "wrench"] [src]."))
			set_anchored(!anchored)
	else if(!SEND_SIGNAL(W, COMSIG_CONTAINER_TRY_ATTACH, src, user))
		to_chat(user, span_warning("[W] does not fit in [src]."))
	return

/obj/structure/tank_holder/screwdriver_act(mob/living/user, obj/item/I)
	if(..())
		return TRUE
	if(tank)
		return FALSE
	I.play_tool_sound(src)
	deconstruct(TRUE)
	return TRUE

/obj/structure/tank_holder/deconstruct(disassembled = TRUE)
	var/atom/Tsec = drop_location()
	new /obj/item/stack/rods(Tsec, 2)
	if(tank)
		tank.forceMove(Tsec)
		after_detach_tank()
	qdel(src)

/obj/structure/tank_holder/attack_paw(mob/user, list/modifiers)
	return attack_hand(user, modifiers)

/obj/structure/tank_holder/attack_hand(mob/user, list/modifiers)
	if(!tank)
		return ..()
	if(!Adjacent(user) || issilicon(user))
		return ..()
	to_chat(user, span_notice("You take [tank] from [src]."))
	add_fingerprint(user)
	tank.add_fingerprint(user)
	user.put_in_hands(tank)
	after_detach_tank()

/obj/structure/tank_holder/handle_atom_del(atom/A)
	if(A == tank)
		after_detach_tank()
	return ..()

/obj/structure/tank_holder/contents_explosion(severity, target)
	if(!tank)
		return

	switch(severity)
		if(EXPLODE_DEVASTATE)
			SSexplosions.high_mov_atom += tank
		if(EXPLODE_HEAVY)
			SSexplosions.med_mov_atom += tank
		if(EXPLODE_LIGHT)
			SSexplosions.low_mov_atom += tank

/// Call this after taking the tank from contents in order to update references, icon
/// and density.
/obj/structure/tank_holder/proc/after_detach_tank()
	tank = null
	set_density(FALSE)
	icon_state = "holder"

/obj/structure/tank_holder/oxygen
	icon_state = "holder_oxygen"
	tank = /obj/item/tank/internals/oxygen

/obj/structure/tank_holder/anesthetic
	icon_state = "holder_anesthetic"
	tank = /obj/item/tank/internals/anesthetic

/obj/structure/tank_holder/oxygen/yellow
	icon_state = "holder_oxygen_f"
	tank = /obj/item/tank/internals/oxygen/yellow

/obj/structure/tank_holder/oxygen/red
	icon_state = "holder_oxygen_fr"
	tank = /obj/item/tank/internals/oxygen/red

/obj/structure/tank_holder/emergency_oxygen
	icon_state = "holder_emergency"
	tank = /obj/item/tank/internals/emergency_oxygen

/obj/structure/tank_holder/emergency_oxygen/engi
	icon_state = "holder_emergency_engi"
	tank = /obj/item/tank/internals/emergency_oxygen/engi

/obj/structure/tank_holder/generic
	icon_state = "holder_generic"
	tank = /obj/item/tank/internals/generic

/obj/structure/tank_holder/extinguisher
	icon_state = "holder_extinguisher"
	tank = /obj/item/extinguisher

/obj/structure/tank_holder/extinguisher/advanced
	icon_state = "holder_foam_extinguisher"
	tank = /obj/item/extinguisher/advanced


code:
#define MIN_RANGE 1
#define MIN_SPEED 1
#define MAX_RANGE 7
#define MAX_SPEED 10
#define HITS_TO_KILL 9
#define MIN_ATTACK_DELAY 10
#define MAX_ATTACK_DELAY 15

/**
 * Machine that runs around wildly so people can practice clickin on things
 *
 * Can have a mob buckled on or a obj/item/target attached. Movement controlled by SSFastProcess,
 * movespeed controlled by cooldown macros. Can attach obj/item/target, obj/item/training_toolbox, and can buckle mobs to this.
 */
/obj/structure/training_machine
	name = "AURUMILL-Brand MkII. Personnel Training Machine"
	desc = "Used for combat training simulations. Accepts standard training targets. A pair of buckling straps are attached."
	icon = 'icons/obj/objects.dmi'
	icon_state = "training_machine"
	can_buckle = TRUE
	buckle_lying = 0
	max_integrity = 200
	///Is the machine moving? Setting this to FALSE will automatically call stop_moving()
	var/moving = FALSE
	///The distance the machine is allowed to roam from its starting point
	var/range = 1
	///A random spot within range that the trainer is trying to reach
	var/turf/target_position
	///The turf the machine was on when it was activated
	var/turf/starting_turf
	///Delay between process() calls. Cannot be higher than MAX_SPEED. Smaller value represents faster movement.
	var/move_speed = 1
	///Reference to a potentially attached object, either a target, trainer toolbox, or syndicate toolbox
	var/obj/attached_item
	///Helper for timing attacks when emagged
	COOLDOWN_DECLARE(attack_cooldown)
	///Cooldown macro to control how fast this will move. Used in process()
	COOLDOWN_DECLARE(move_cooldown)

/**
 * Called on qdel(), so we don't want a cool explosion to happen
 */
/obj/structure/training_machine/Destroy()
	remove_attached_item()
	return ..()

/**
 * Called on a normal destruction, so we have a cool explosion and toss whatever's attached
 */
/obj/structure/training_machine/atom_destruction(damage_flag)
	remove_attached_item(throwing = TRUE)
	explosion(src, light_impact_range = 1, flash_range = 2)
	return ..()

/obj/structure/training_machine/ui_state(mob/user)
	return GLOB.physical_state

/obj/structure/training_machine/ui_interact(mob/user, datum/tgui/ui)
	ui = SStgui.try_update_ui(user, src, ui)
	if(!ui)
		ui = new(user, src, "TrainingMachine", name)
		ui.open()

/**
 * Send data to the UI
 *
 * Include's the machine's movement range, speed, and whether or not it's active
 */
/obj/structure/training_machine/ui_data(mob/user)
	var/list/data = list()
	data["range"] = range
	data["movespeed"] = move_speed
	data["moving"] = moving
	return data

/**
 * Control the attached variables.
 *
 * Will not respond if moving and emagged, so once you set it to go it can't be stopped!
 */
/obj/structure/training_machine/ui_act(action, params)
	. = ..()
	if(.)
		return
	if (moving && obj_flags & EMAGGED)
		visible_message(span_warning("The [src]'s control panel fizzles slightly."))
		return
	switch(action)
		if("toggle")
			toggle()
			. = TRUE
		if("range")
			var/range_input = params["range"]
			range = clamp(range_input, MIN_RANGE, MAX_RANGE)
			. = TRUE
		if("movespeed")
			var/range_input = params["movespeed"]
			move_speed = clamp(range_input, MIN_SPEED, MAX_SPEED)
			. = TRUE

/obj/structure/training_machine/attack_hand(mob/user, list/modifiers)
	ui_interact(user)

/**
 * Called when the machien is attacked by something
 *
 * Meant for attaching an item to the machine, should only be a training toolbox or target. If emagged, the
 * machine will gain an auto-attached syndicate toolbox, so in that case we shouldn't be able to swap it out
 */
/obj/structure/training_machine/attackby(obj/item/target, mob/living/user)
	//if (user.combat_mode) //SEPTIC EDIT REMOVAL
	//SEPTIC EDIT
	if (!IS_HELP_INTENT(user, null))
	//SEPTIC EDIT END
		return ..()
	if (!istype(target, /obj/item/training_toolbox) && !istype(target, /obj/item/target))
		return ..()
	if (obj_flags & EMAGGED)
		to_chat(user, span_warning("The toolbox is somehow stuck on! It won't budge!"))
		return
	attach_item(target)
	to_chat(user, span_notice("You attach \the [attached_item] to the training device."))
	playsound(src, "rustle", 50, TRUE)

/**
 * Attach an item to the machine
 *
 * This proc technically works with any obj. Currently is only used for objects of type item/target and item/training_toolbox
 * Will make the attached item appear visually on the machine
 * Arguments
 * * target - The object to attach
 */
/obj/structure/training_machine/proc/attach_item(obj/target)
	remove_attached_item()
	attached_item = target
	attached_item.forceMove(src)
	attached_item.vis_flags |= VIS_INHERIT_ID
	vis_contents += attached_item
	RegisterSignal(attached_item, COMSIG_PARENT_QDELETING, .proc/on_attached_delete)
	handle_density()

/**
 * Called when the attached item is deleted.
 *
 * Cleans up behavior for when the attached item is deleted or removed.
 */
/obj/structure/training_machine/proc/on_attached_delete()
	SIGNAL_HANDLER
	UnregisterSignal(attached_item, COMSIG_PARENT_QDELETING)
	vis_contents -= attached_item
	attached_item = null
	handle_density()

/**
 * Remove the attached item from the machine
 *
 * Called when a user removes the item by hand or by swapping it out with another, when the machine breaks, or
 * when the machine is emagged.
 * Arguments
 * * user - The peson , if any, removing the attached item
 * * throwing - If we should make the item fly off the machine
 */
/obj/structure/training_machine/proc/remove_attached_item(mob/user, throwing = FALSE)
	if (!attached_item)
		return
	if (istype(attached_item, /obj/item/storage/toolbox/syndicate))
		UnregisterSignal(attached_item, COMSIG_PARENT_QDELETING)
		qdel(attached_item)
	else if (user)
		INVOKE_ASYNC(user, /mob/proc/put_in_hands, attached_item)
	else
		attached_item.forceMove(drop_location())
	if (throwing && !QDELETED(attached_item)) //Fun little thing where we throw out the old attached item when emagged
		//We do a QDELETED check here because we don't want to throw the syndi toolbox, if it exists
		var/destination = get_edge_target_turf(get_turf(src), pick(GLOB.alldirs))
		attached_item.throw_at(destination, 4, 1)
	on_attached_delete()

/obj/structure/training_machine/AltClick(mob/user)
	. = ..()
	if(!user.canUseTopic(src, BE_CLOSE, NO_DEXTERITY, NO_TK, FLOOR_OKAY))
		return
	if(has_buckled_mobs())
		user_unbuckle_mob(buckled_mobs[1], user)
		return
	if (!attached_item)
		return
	if (obj_flags & EMAGGED)
		to_chat(user, span_warning("The toolbox is somehow stuck on! It won't budge!"))
		return
	to_chat(user, span_notice("You remove \the [attached_item] from the training device."))
	remove_attached_item(user)
	playsound(src, "rustle", 50, TRUE)

/**
 * Toggle the machine's movement
 */
/obj/structure/training_machine/proc/toggle()
	if (moving)
		stop_moving()
	else
		start_moving()

/**
 * Stop the machine's movement
 *
 * Will call STOP_PROCESSING, play a sound, and say an appropriate message
 * Arguments
 * * Message - the message the machine says when stopping
 */
/obj/structure/training_machine/proc/stop_moving(message = "Ending training simulation.")
	moving = FALSE
	starting_turf = null
	say(message)
	playsound(src,'sound/machines/synth_no.ogg',50,FALSE)
	STOP_PROCESSING(SSfastprocess, src)

/**
 * Start the machine's movement
 *
 * Says a message, plays a sound, then starts processing
 */
/obj/structure/training_machine/proc/start_moving()
	moving = TRUE
	starting_turf = get_turf(src)
	say("Beginning training simulation.")
	playsound(src,'sound/machines/triple_beep.ogg',50,FALSE)
	START_PROCESSING(SSfastprocess, src)

/**
 * Main movement method for the machine
 *
 * Handles movement using SSFastProcess. Moves randomly, point-to-point, in an area centered around wherever it started.
 * Will only move if the move_cooldown cooldown macro is finished.
 * If it can't find a place to go, it will stop moving.
 */
/obj/structure/training_machine/process()
	if(!COOLDOWN_FINISHED(src, move_cooldown))
		return
	var/turf/current_turf = get_turf(src)
	if (!moving || !starting_turf || isspaceturf(current_turf))
		stop_moving()
		return
	if (current_turf == target_position) //We've reached our target turf, now find a new one
		target_position = null
	if (!target_position)
		target_position = find_target_position()
		if (!target_position)
			stop_moving("ERROR! Cannot calculate suitable movement path.")
	var/turf/nextStep = get_step_towards(src, target_position)
	if (!Move(nextStep, get_dir(src, nextStep)))
		target_position = null //We couldn't move towards the target turf, so find a new target turf
	try_attack()
	COOLDOWN_START(src, move_cooldown, max(MAX_SPEED - move_speed, 1))

/**
 * Find a suitable turf to move towards
 */
/obj/structure/training_machine/proc/find_target_position()
	var/list/turfs = list()
	for(var/turf/potential_turf in view(range, starting_turf))
		if (potential_turf.is_blocked_turf() || potential_turf == target_position)
			continue
		turfs += potential_turf
	if (!length(turfs))
		return
	return pick(turfs)

/**
 * Try to attack a nearby mob
 *
 * Called whenever the machine moves, this will look for mobs adjacent to the machine to attack.
 * Will attack with either a training toolbox (if attached), or a much more deadly syndicate toolbox (if emagged).
 * A cooldown macro (attack_cooldown) ensures it doesn't attack too quickly
 */
/obj/structure/training_machine/proc/try_attack()
	if (!attached_item || istype(attached_item, /obj/item/target))
		return
	if (!COOLDOWN_FINISHED(src, attack_cooldown))
		return
	var/list/targets
	for(var/mob/living/carbon/target in oview(1, get_turf(src))) //Find adjacent target
		if (target.stat == CONSCIOUS && target.Adjacent(src))
			LAZYADD(targets, target)
	var/mob/living/carbon/target = pick(targets)
	if (!target)
		return
	do_attack_animation(target, null, attached_item)
	if (obj_flags & EMAGGED)
		target.apply_damage(attached_item.force, BRUTE, BODY_ZONE_CHEST)
	playsound(src, 'sound/weapons/smash.ogg', 15, TRUE)
	COOLDOWN_START(src, attack_cooldown, rand(MIN_ATTACK_DELAY, MAX_ATTACK_DELAY))

/**
 * Make sure the machine can't be walked through if something is attached
 */
/obj/structure/training_machine/proc/handle_density()
	if(length(buckled_mobs) || attached_item)
		set_density(TRUE)
	else
		set_density(FALSE)

/obj/structure/training_machine/buckle_mob(mob/living/M, force = FALSE, check_loc = TRUE)
	. = ..()
	if (istype(attached_item, /obj/item/target))
		return FALSE

/obj/structure/training_machine/post_buckle_mob()
	handle_density()
	return ..()

/obj/structure/training_machine/post_unbuckle_mob()
	handle_density()
	return ..()

/**
 * Emagging causes a deadly, unremovable syndicate toolbox to be attached to the machine
 */
/obj/structure/training_machine/emag_act(mob/user)
	. = ..()
	if (obj_flags & EMAGGED)
		return
	obj_flags |= EMAGGED
	remove_attached_item(throwing = TRUE) //Toss out the old attached item!
	attach_item(new /obj/item/storage/toolbox/syndicate(src))
	to_chat(user, span_warning("You override the training machine's safety protocols, and activate its realistic combat feature. A toolbox pops out of a slot on the top."))
	playsound(src, 'sound/machines/click.ogg', 50, TRUE)
	add_overlay("evil_trainer")

/obj/structure/training_machine/examine(mob/user)
	. = ..()
	var/has_buckled_mob = has_buckled_mobs()
	if(has_buckled_mob)
		. += span_notice("Alt-Click to unbuckle \the [buckled_mobs[1]]")
	if (obj_flags & EMAGGED)
		. += span_warning("It has a dangerous-looking toolbox attached to it, and the control panel is smoking sightly...")
	else if (!has_buckled_mob && attached_item) //Can't removed the syndicate toolbox!
		. += span_notice("Alt-Click to remove \the [attached_item]")
	. += span_notice("Click to open control interface.")

/**
 * Device that simply counts the number of times you've hit a mob or target with. Looks like a toolbox but isn't.
 *
 * Also has a 'Lap' function for keeping track of hits made at a certain point. Also, looks kinda like his grace for laughs and pranks.
 */
/obj/item/training_toolbox
	name = "Training Toolbox"
	desc = "AURUMILL-Brand Baby's First Training Toolbox. A digital display on the back keeps track of hits made by the user. Second toolbox sold seperately!"
	icon = 'icons/obj/storage.dmi'
	icon_state = "gold"
	inhand_icon_state = "toolbox_gold"
	lefthand_file = 'icons/mob/inhands/equipment/toolbox_lefthand.dmi'
	righthand_file = 'icons/mob/inhands/equipment/toolbox_righthand.dmi'
	flags_1 = CONDUCT_1
	force = 0
	throwforce = 0
	throw_speed = 2
	throw_range = 7
	w_class = WEIGHT_CLASS_BULKY
	///Total number of hits made against a valid target
	var/total_hits = 0
	///Number of hits made since the Lap button (alt-click) was last pushed
	var/lap_hits = 0

/obj/item/training_toolbox/afterattack(atom/target, mob/living/user, proximity)
	. = ..()
	//if (!proximity || target == user || !user.combat_mode) //SEPTIC EDIT REMOVAL
	//SEPTIC EDIT
	if (!proximity || target == user || IS_HELP_INTENT(user, null))
	//SEPTIC EDIT END
		return
	if (check_hit(target))
		user.changeNext_move(CLICK_CD_MELEE)

/**
 * Check if we should increment the hit counter
 *
 * Increments the 'hit' counter if the target we're attacking is a mob, target, or training machine with an attached item.
 * Will beep every 9 hits, as 9 hits usually signifies a KO with a normal toolbox
 * Arguments
 * * target - the atom we're hitting
 */
/obj/item/training_toolbox/proc/check_hit(atom/target)
	var/target_is_machine = istype(target, /obj/structure/training_machine)
	if (!ismob(target) && !istype(target, /obj/item/target) && !target_is_machine)
		return FALSE
	if (target_is_machine)
		var/obj/structure/training_machine/trainer = target
		if (!trainer.attached_item)
			return FALSE
	total_hits++
	lap_hits++
	playsound(src,'sound/weapons/smash.ogg',50,FALSE)
	if (lap_hits % HITS_TO_KILL == 0)
		playsound(src,'sound/machines/twobeep.ogg',25,FALSE)
	return TRUE

/obj/item/training_toolbox/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
	. = ..()
	if (!.)
		check_hit(hit_atom)

/obj/item/training_toolbox/AltClick(mob/user)
	. = ..()
	if(!can_interact(user))
		return
	to_chat(user, span_notice("You push the 'Lap' button on the toolbox's display."))
	lap_hits = initial(lap_hits)

/obj/item/training_toolbox/examine(mob/user)
	. = ..()
	if(!in_range(src, user) && !isobserver(user))
		. += span_notice("You can see a display on the back. You'll need to get closer to read it, though.")
		return
	. += span_notice("A display on the back reads:")
	. += span_notice("Total Hits: [total_hits]")
	if (lap_hits != total_hits)
		. += span_notice("Current Lap: [lap_hits]")
	. += span_notice("Alt-Click to 'Lap' the hit counter.")

#undef MIN_RANGE
#undef MIN_SPEED
#undef MAX_RANGE
#undef MAX_SPEED
#undef HITS_TO_KILL
#undef MIN_ATTACK_DELAY
#undef MAX_ATTACK_DELAY

repo_name: AnalWerewolf/sundown

license: mit


code:
#define OPEN_DURATION 6
#define CLOSE_DURATION 6

// 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_state = "closed_station0"
	desc = "The lynchpin of the transit system."
	exit_delay = 1
	enter_delay = 2
	tube_construction = /obj/structure/c_transit_tube/station
	var/open_status = STATION_TUBE_CLOSED
	var/pod_moving = FALSE
	var/cooldown_delay = 50
	var/launch_cooldown = 0
	var/reverse_launch = FALSE
	var/base_icon = "station0"
	var/boarding_dir //from which direction you can board the tube

/obj/structure/transit_tube/station/Initialize(mapload)
	. = ..()
	START_PROCESSING(SSobj, src)

/obj/structure/transit_tube/station/Destroy()
	STOP_PROCESSING(SSobj, src)
	return ..()

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

/obj/structure/transit_tube/station/Bumped(atom/movable/AM)
	if(!pod_moving && open_status == STATION_TUBE_OPEN && ismob(AM) && AM.dir == boarding_dir)
		for(var/obj/structure/transit_tube_pod/pod in loc)
			if(!pod.moving)
				AM.forceMove(pod)
				pod.update_appearance()
				return

//pod insertion
/obj/structure/transit_tube/station/MouseDrop_T(obj/structure/c_transit_tube_pod/R, mob/user)
	if(isliving(user))
		var/mob/living/L = user
		if(L.incapacitated())
			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/TP = new(loc)
	R.transfer_fingerprints_to(TP)
	TP.add_fingerprint(user)
	TP.setDir(turn(src.dir, -90))
	user.visible_message(span_notice("[user] inserts [R]."), span_notice("You insert [R]."))
	qdel(R)

/obj/structure/transit_tube/station/attack_hand(mob/user, list/modifiers)
	. = ..()
	if(.)
		return
	if(!pod_moving)
		if(user.pulling && isliving(user.pulling))
			if(open_status == STATION_TUBE_OPEN)
				var/mob/living/GM = user.pulling
				if(user.grab_state >= GRAB_AGGRESSIVE)
					if(GM.buckled || GM.has_buckled_mobs())
						to_chat(user, span_warning("[GM] is attached to something!"))
						return
					for(var/obj/structure/transit_tube_pod/pod in loc)
						pod.visible_message(span_warning("[user] starts putting [GM] into the [pod]!"))
						if(do_after(user, 15, target = src))
							if(open_status == STATION_TUBE_OPEN && GM && user.grab_state >= GRAB_AGGRESSIVE && user.pulling == GM && !GM.buckled && !GM.has_buckled_mobs())
								GM.Paralyze(100)
								src.Bumped(GM)
						break
		else
			for(var/obj/structure/transit_tube_pod/pod in loc)
				if(!pod.moving && (pod.dir in tube_dirs))
					if(open_status == STATION_TUBE_CLOSED)
						open_animation()

					else if(open_status == STATION_TUBE_OPEN)
						if(pod.contents.len && user.loc != pod)
							user.visible_message(span_notice("[user] starts emptying [pod]'s contents onto the floor."), span_notice("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 && pod.loc == loc)
									for(var/atom/movable/AM in pod)
										AM.forceMove(get_turf(user))

						else
							close_animation()
				break

/obj/structure/transit_tube/station/attackby(obj/item/W, mob/user, params)
	if(W.tool_behaviour == TOOL_CROWBAR)
		for(var/obj/structure/transit_tube_pod/P in loc)
			P.deconstruct(FALSE, user)
	else
		return ..()

/obj/structure/transit_tube/station/proc/open_animation()
	if(open_status == STATION_TUBE_CLOSED)
		icon_state = "opening_[base_icon]"
		open_status = STATION_TUBE_OPENING
		addtimer(CALLBACK(src, .proc/finish_animation), OPEN_DURATION)

/obj/structure/transit_tube/station/proc/finish_animation()
	switch(open_status)
		if(STATION_TUBE_OPENING)
			icon_state = "open_[base_icon]"
			open_status = STATION_TUBE_OPEN
			for(var/obj/structure/transit_tube_pod/pod in loc)
				for(var/thing in pod)
					if(ismob(thing))
						var/mob/mob_content = thing
						if(mob_content.client && mob_content.stat < UNCONSCIOUS)
							continue // Let the mobs with clients decide what they want to do themselves.
					var/atom/movable/movable_content = thing
					movable_content.forceMove(loc) //Everything else is moved out of.
		if(STATION_TUBE_CLOSING)
			icon_state = "closed_[base_icon]"
			open_status = STATION_TUBE_CLOSED

/obj/structure/transit_tube/station/proc/close_animation()
	if(open_status == STATION_TUBE_OPEN)
		icon_state = "closing_[base_icon]"
		open_status = STATION_TUBE_CLOSING
		addtimer(CALLBACK(src, .proc/finish_animation), CLOSE_DURATION)

/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)
			pod_moving = TRUE
			close_animation()
			sleep(CLOSE_DURATION + 2)
			if(open_status == STATION_TUBE_CLOSED && pod && pod.loc == loc)
				pod.follow_tube()
			pod_moving = FALSE
			return TRUE
	return FALSE

/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 = TRUE
	addtimer(CALLBACK(src, .proc/start_stopped, pod), 5)

/obj/structure/transit_tube/station/proc/start_stopped(obj/structure/transit_tube_pod/pod)
	if(QDELETED(pod))
		return
	if(reverse_launch)
		pod.setDir(tube_dirs[1]) //turning the pod around for next launch.
	launch_cooldown = world.time + cooldown_delay
	open_animation()
	addtimer(CALLBACK(src, .proc/finish_stopped, pod), OPEN_DURATION + 2)

/obj/structure/transit_tube/station/proc/finish_stopped(obj/structure/transit_tube_pod/pod)
	pod_moving = FALSE
	if(!QDELETED(pod))
		var/datum/gas_mixture/floor_mixture = loc.return_air()
		floor_mixture.archive()
		pod.air_contents.archive()
		pod.air_contents.share(floor_mixture, 1) //mix the pod's gas mixture with the tile it's on
		air_update_turf(FALSE, FALSE)

/obj/structure/transit_tube/station/init_tube_dirs()
	switch(dir)
		if(NORTH)
			tube_dirs = list(EAST, WEST)
		if(SOUTH)
			tube_dirs = list(EAST, WEST)
		if(EAST)
			tube_dirs = list(NORTH, SOUTH)
		if(WEST)
			tube_dirs = list(NORTH, SOUTH)
	boarding_dir = turn(dir, 180)

/obj/structure/transit_tube/station/flipped
	icon_state = "closed_station1"
	base_icon = "station1"
	tube_construction = /obj/structure/c_transit_tube/station/flipped

/obj/structure/transit_tube/station/flipped/init_tube_dirs()
	..()
	boarding_dir = dir

// 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 = TRUE
	icon_state = "closed_terminus0"
	base_icon = "terminus0"

/obj/structure/transit_tube/station/reverse/init_tube_dirs()
	switch(dir)
		if(NORTH)
			tube_dirs = list(EAST)
		if(SOUTH)
			tube_dirs = list(WEST)
		if(EAST)
			tube_dirs = list(SOUTH)
		if(WEST)
			tube_dirs = list(NORTH)
	boarding_dir = turn(dir, 180)

/obj/structure/transit_tube/station/reverse/flipped
	icon_state = "closed_terminus1"
	base_icon = "terminus1"
	tube_construction = /obj/structure/c_transit_tube/station/reverse/flipped

/obj/structure/transit_tube/station/reverse/flipped/init_tube_dirs()
	..()
	boarding_dir = dir

//special dispenser station, it creates a pod for you to enter when you bump into it.

/obj/structure/transit_tube/station/dispenser
	name = "station tube pod dispenser"
	icon_state = "open_dispenser0"
	desc = "The lynchpin of a GOOD transit system."
	enter_delay = 1
	tube_construction = /obj/structure/c_transit_tube/station/dispenser
	base_icon = "dispenser0"
	open_status = STATION_TUBE_OPEN

/obj/structure/transit_tube/station/dispenser/close_animation()
	return

/obj/structure/transit_tube/station/dispenser/launch_pod()
	for(var/obj/structure/transit_tube_pod/pod in loc)
		if(!pod.moving)
			pod_moving = TRUE
			pod.follow_tube()
			pod_moving = FALSE
			return TRUE
	return FALSE

/obj/structure/transit_tube/station/dispenser/examine(mob/user)
	. = ..()
	. += span_notice("This station will create a pod for you to ride, no need to wait for one.")

/obj/structure/transit_tube/station/dispenser/Bumped(atom/movable/AM)
	if(!(istype(AM) && AM.dir == boarding_dir))
		return
	var/obj/structure/transit_tube_pod/dispensed/pod = new(loc)
	AM.visible_message(span_notice("[pod] forms around [AM]."), span_notice("[pod] materializes around you."))
	playsound(src, 'sound/weapons/emitter2.ogg', 50, TRUE)
	pod.setDir(turn(src.dir, -90))
	AM.forceMove(pod)
	pod.update_appearance()
	launch_pod()

/obj/structure/transit_tube/station/dispenser/pod_stopped(obj/structure/transit_tube_pod/pod, from_dir)
	playsound(src, 'sound/machines/ding.ogg', 50, TRUE)
	qdel(pod)

/obj/structure/transit_tube/station/dispenser/flipped
	icon_state = "open_dispenser1"
	base_icon = "dispenser1"
	tube_construction = /obj/structure/c_transit_tube/station/dispenser/flipped

/obj/structure/transit_tube/station/dispenser/flipped/init_tube_dirs()
	..()
	boarding_dir = dir

/obj/structure/transit_tube/station/dispenser/reverse
	tube_construction = /obj/structure/c_transit_tube/station/dispenser/reverse
	reverse_launch = TRUE
	icon_state = "open_terminusdispenser0"
	base_icon = "terminusdispenser0"

/obj/structure/transit_tube/station/dispenser/reverse/init_tube_dirs()
	switch(dir)
		if(NORTH)
			tube_dirs = list(EAST)
		if(SOUTH)
			tube_dirs = list(WEST)
		if(EAST)
			tube_dirs = list(SOUTH)
		if(WEST)
			tube_dirs = list(NORTH)
	boarding_dir = turn(dir, 180)

/obj/structure/transit_tube/station/dispenser/reverse/flipped
	icon_state = "open_terminusdispenser1"
	base_icon = "terminusdispenser1"
	tube_construction = /obj/structure/c_transit_tube/station/dispenser/reverse/flipped

/obj/structure/transit_tube/station/dispenser/reverse/flipped/init_tube_dirs()
	..()
	boarding_dir = dir

#undef OPEN_DURATION
#undef CLOSE_DURATION


code:
/obj/structure/transit_tube
	name = "transit tube"
	icon = 'icons/obj/atmospherics/pipes/transit_tube.dmi'
	icon_state = "straight"
	desc = "A transit tube for moving things around."
	density = TRUE
	layer = LOW_ITEM_LAYER
	anchored = TRUE
	pass_flags_self = PASSGLASS
	var/tube_construction = /obj/structure/c_transit_tube
	var/list/tube_dirs //list of directions this tube section can connect to.
	var/exit_delay = 1
	var/enter_delay = 0

/obj/structure/transit_tube/Initialize(mapload, newdirection)
	. = ..()
	if(newdirection)
		setDir(newdirection)
	init_tube_dirs()
	generate_tube_overlays()
	AddElement(/datum/element/climbable)

/obj/structure/transit_tube/Destroy()
	for(var/obj/structure/transit_tube_pod/P in loc)
		P.deconstruct(FALSE)
	return ..()

/obj/structure/transit_tube/singularity_pull(S, current_size)
	..()
	if(current_size >= STAGE_FIVE)
		deconstruct(FALSE)

/obj/structure/transit_tube/attackby(obj/item/W, mob/user, params)
	if(W.tool_behaviour == TOOL_WRENCH)
		if(tube_construction)
			for(var/obj/structure/transit_tube_pod/pod in src.loc)
				to_chat(user, span_warning("Remove the pod first!"))
				return
			user.visible_message(span_notice("[user] starts to detach \the [src]."), span_notice("You start to detach the [name]..."))
			if(W.use_tool(src, user, 2 SECONDS, volume=50))
				to_chat(user, span_notice("You detach the [name]."))
				var/obj/structure/c_transit_tube/R = new tube_construction(loc)
				R.setDir(dir)
				transfer_fingerprints_to(R)
				R.add_fingerprint(user)
				qdel(src)
	else if(W.tool_behaviour == TOOL_CROWBAR)
		for(var/obj/structure/transit_tube_pod/pod in src.loc)
			pod.attackby(W, user)
	else
		return ..()

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

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

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

	for(var/direction in tube_dirs)
		if(direction == from_dir)
			return TRUE

	return FALSE

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

	return FALSE

// 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 tube_dirs)
		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

/obj/structure/transit_tube/proc/init_tube_dirs()
	switch(dir)
		if(NORTH)
			tube_dirs = list(NORTH, SOUTH)
		if(SOUTH)
			tube_dirs = list(NORTH, SOUTH)
		if(EAST)
			tube_dirs = list(EAST, WEST)
		if(WEST)
			tube_dirs = list(EAST, WEST)

/obj/structure/transit_tube/proc/generate_tube_overlays()
	for(var/direction in tube_dirs)
		if(ISDIAGONALDIR(direction))
			if(direction & NORTH)
				create_tube_overlay(direction ^ 3, NORTH)

				if(direction & EAST)
					create_tube_overlay(direction ^ 12, EAST)

				else
					create_tube_overlay(direction ^ 12, WEST)
		else
			create_tube_overlay(direction)

/obj/structure/transit_tube/proc/create_tube_overlay(direction, shift_dir)
	var/image/tube_overlay = new(dir = direction)
	if(shift_dir)
		tube_overlay.icon_state = "decorative_diag"
		switch(shift_dir)
			if(NORTH)
				tube_overlay.pixel_y = 32
			if(SOUTH)
				tube_overlay.pixel_y = -32
			if(EAST)
				tube_overlay.pixel_x = 32
			if(WEST)
				tube_overlay.pixel_x = -32
	else
		tube_overlay.icon_state = "decorative"
	add_overlay(tube_overlay)

//Some of these are mostly for mapping use
/obj/structure/transit_tube/horizontal
	dir = WEST

/obj/structure/transit_tube/diagonal
	icon_state = "diagonal"
	tube_construction = /obj/structure/c_transit_tube/diagonal

/obj/structure/transit_tube/diagonal/init_tube_dirs()
	switch(dir)
		if(NORTH)
			tube_dirs = list(NORTHEAST, SOUTHWEST)
		if(SOUTH)
			tube_dirs = list(NORTHEAST, SOUTHWEST)
		if(EAST)
			tube_dirs = list(NORTHWEST, SOUTHEAST)
		if(WEST)
			tube_dirs = list(NORTHWEST, SOUTHEAST)

//mostly for mapping use
/obj/structure/transit_tube/diagonal/topleft
	dir = WEST

/obj/structure/transit_tube/diagonal/crossing
	density = FALSE
	icon_state = "diagonal_crossing"
	tube_construction = /obj/structure/c_transit_tube/diagonal/crossing

//mostly for mapping use
/obj/structure/transit_tube/diagonal/crossing/topleft
	dir = WEST

/obj/structure/transit_tube/curved
	icon_state = "curved0"
	tube_construction = /obj/structure/c_transit_tube/curved

/obj/structure/transit_tube/curved/init_tube_dirs()
	switch(dir)
		if(NORTH)
			tube_dirs = list(NORTH, SOUTHWEST)
		if(SOUTH)
			tube_dirs = list(SOUTH, NORTHEAST)
		if(EAST)
			tube_dirs = list(EAST, NORTHWEST)
		if(WEST)
			tube_dirs = list(SOUTHEAST, WEST)

/obj/structure/transit_tube/curved/flipped
	icon_state = "curved1"
	tube_construction = /obj/structure/c_transit_tube/curved/flipped

/obj/structure/transit_tube/curved/flipped/init_tube_dirs()
	switch(dir)
		if(NORTH)
			tube_dirs = list(NORTH, SOUTHEAST)
		if(SOUTH)
			tube_dirs = list(SOUTH, NORTHWEST)
		if(EAST)
			tube_dirs = list(EAST, SOUTHWEST)
		if(WEST)
			tube_dirs = list(NORTHEAST, WEST)

/obj/structure/transit_tube/junction
	icon_state = "junction0"
	tube_construction = /obj/structure/c_transit_tube/junction

/obj/structure/transit_tube/junction/init_tube_dirs()
	switch(dir)
		if(NORTH)
			tube_dirs = list(NORTH, SOUTHEAST, SOUTHWEST)//ending with the prefered direction
		if(SOUTH)
			tube_dirs = list(SOUTH, NORTHWEST, NORTHEAST)
		if(EAST)
			tube_dirs = list(EAST, SOUTHWEST, NORTHWEST)
		if(WEST)
			tube_dirs = list(WEST, NORTHEAST, SOUTHEAST)

/obj/structure/transit_tube/junction/flipped
	icon_state = "junction1"
	tube_construction = /obj/structure/c_transit_tube/junction/flipped

/obj/structure/transit_tube/junction/flipped/init_tube_dirs()
	switch(dir)
		if(NORTH)
			tube_dirs = list(NORTH, SOUTHWEST, SOUTHEAST)//ending with the prefered direction
		if(SOUTH)
			tube_dirs = list(SOUTH, NORTHEAST, NORTHWEST)
		if(EAST)
			tube_dirs = list(EAST, NORTHWEST, SOUTHWEST)
		if(WEST)
			tube_dirs = list(WEST, SOUTHEAST, NORTHEAST)

/obj/structure/transit_tube/crossing
	icon_state = "crossing"
	tube_construction = /obj/structure/c_transit_tube/crossing
	density = FALSE

//mostly for mapping use
/obj/structure/transit_tube/crossing/horizontal
	dir = WEST


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 = "straight"
	desc = "An unattached segment of transit tube."
	density = FALSE
	layer = LOW_ITEM_LAYER //same as the built tube
	anchored = FALSE
	var/flipped = FALSE
	var/build_type = /obj/structure/transit_tube
	var/flipped_build_type
	var/base_icon

/obj/structure/c_transit_tube/proc/can_wrench_in_loc(mob/user)
	var/turf/source_turf = get_turf(loc)
	var/existing_tubes = 0
	for(var/obj/structure/transit_tube/tube in source_turf)
		existing_tubes +=1
		if(existing_tubes >= 2)
			to_chat(user, "[span_warning("You cannot wrench any more transit tubes!")] ")
			return FALSE
	return TRUE

/obj/structure/c_transit_tube/ComponentInitialize()
	. = ..()
	AddComponent(/datum/component/simple_rotation,ROTATION_ALTCLICK | ROTATION_CLOCKWISE | ROTATION_FLIP | ROTATION_VERBS,null,null,CALLBACK(src,.proc/after_rot))

/obj/structure/c_transit_tube/proc/after_rot(mob/user,rotation_type)
	if(flipped_build_type && rotation_type == ROTATION_FLIP)
		setDir(turn(dir,-180)) //Turn back we don't actually flip
		flipped = !flipped
		var/cur_flip = initial(flipped) ? !flipped : flipped
		if(cur_flip)
			build_type = flipped_build_type
		else
			build_type = initial(build_type)
		icon_state = "[base_icon][flipped]"

/obj/structure/c_transit_tube/wrench_act(mob/living/user, obj/item/I)
	..()
	if(!can_wrench_in_loc(user))
		return
	to_chat(user, span_notice("You start attaching the [name]..."))
	add_fingerprint(user)
	if(I.use_tool(src, user, 2 SECONDS, volume=50, extra_checks=CALLBACK(src, .proc/can_wrench_in_loc, user)))
		to_chat(user, span_notice("You attach the [name]."))
		var/obj/structure/transit_tube/R = new build_type(loc, dir)
		transfer_fingerprints_to(R)
		qdel(src)
	return TRUE

// transit tube station
/obj/structure/c_transit_tube/station
	name = "unattached through station"
	icon_state = "closed_station0"
	build_type = /obj/structure/transit_tube/station
	flipped_build_type = /obj/structure/transit_tube/station/flipped
	base_icon = "closed_station"

/obj/structure/c_transit_tube/station/flipped
	icon_state = "closed_station1"
	flipped = TRUE
	build_type = /obj/structure/transit_tube/station/flipped
	flipped_build_type = /obj/structure/transit_tube/station

// reverser station, used for the terminus
/obj/structure/c_transit_tube/station/reverse
	name = "unattached terminus station"
	icon_state = "closed_terminus0"
	build_type = /obj/structure/transit_tube/station/reverse
	flipped_build_type = /obj/structure/transit_tube/station/reverse/flipped
	base_icon = "closed_terminus"

/obj/structure/c_transit_tube/station/reverse/flipped
	icon_state = "closed_terminus1"
	flipped = TRUE
	build_type = /obj/structure/transit_tube/station/reverse/flipped
	flipped_build_type = /obj/structure/transit_tube/station/reverse

//all the dispenser stations

/obj/structure/c_transit_tube/station/dispenser
	icon_state = "open_dispenser0"
	name = "unattached dispenser station"
	build_type = /obj/structure/transit_tube/station/dispenser
	flipped_build_type = /obj/structure/transit_tube/station/dispenser/flipped
	base_icon = "open_dispenser"

/obj/structure/c_transit_tube/station/dispenser/flipped
	icon_state = "open_dispenser1"
	flipped = TRUE
	build_type = /obj/structure/transit_tube/station/dispenser/flipped
	flipped_build_type = /obj/structure/transit_tube/station/dispenser

//and the ones that reverse

/obj/structure/c_transit_tube/station/dispenser/reverse
	name = "unattached terminus dispenser station"
	icon_state = "open_terminusdispenser0"
	build_type = /obj/structure/transit_tube/station/dispenser/reverse
	flipped_build_type = /obj/structure/transit_tube/station/dispenser/reverse/flipped
	base_icon = "open_terminusdispenser"

/obj/structure/c_transit_tube/station/dispenser/reverse/flipped
	icon_state = "open_terminusdispenser1"
	flipped = TRUE
	build_type = /obj/structure/transit_tube/station/dispenser/reverse/flipped
	flipped_build_type = /obj/structure/transit_tube/station/dispenser/reverse

//onto some special tube types

/obj/structure/c_transit_tube/crossing
	icon_state = "crossing"
	build_type = /obj/structure/transit_tube/crossing

/obj/structure/c_transit_tube/diagonal
	icon_state = "diagonal"
	build_type = /obj/structure/transit_tube/diagonal

/obj/structure/c_transit_tube/diagonal/crossing
	icon_state = "diagonal_crossing"
	build_type = /obj/structure/transit_tube/diagonal/crossing

/obj/structure/c_transit_tube/curved
	icon_state = "curved0"
	build_type = /obj/structure/transit_tube/curved
	flipped_build_type = /obj/structure/transit_tube/curved/flipped
	base_icon = "curved"

/obj/structure/c_transit_tube/curved/flipped
	icon_state = "curved1"
	build_type = /obj/structure/transit_tube/curved/flipped
	flipped_build_type = /obj/structure/transit_tube/curved
	flipped = TRUE

/obj/structure/c_transit_tube/junction
	icon_state = "junction0"
	build_type = /obj/structure/transit_tube/junction
	flipped_build_type = /obj/structure/transit_tube/junction/flipped
	base_icon = "junction"

/obj/structure/c_transit_tube/junction/flipped
	icon_state = "junction1"
	flipped = TRUE
	build_type = /obj/structure/transit_tube/junction/flipped
	flipped_build_type = /obj/structure/transit_tube/junction

//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.dmi'
	icon_state = "pod"
	desc = "Could probably be <b>dragged</b> into an open Transit Tube."
	anchored = FALSE
	density = FALSE


code:
#define MOVE_ANIMATION_STAGE_ONE 1
#define MOVE_ANIMATION_STAGE_TWO 2

/obj/structure/transit_tube_pod
	icon = 'icons/obj/atmospherics/pipes/transit_tube.dmi'
	icon_state = "pod"
	animate_movement = FORWARD_STEPS
	anchored = TRUE
	density = TRUE
	var/moving = FALSE
	var/datum/gas_mixture/air_contents = new()
	var/occupied_icon_state = "pod_occupied"
	var/obj/structure/transit_tube/current_tube = null
	var/next_dir
	var/next_loc
	var/enter_delay = 0
	var/exit_delay
	var/moving_time = 0

/obj/structure/transit_tube_pod/Initialize(mapload)
	. = ..()
	air_contents.add_gases(/datum/gas/oxygen, /datum/gas/nitrogen)
	air_contents.gases[/datum/gas/oxygen][MOLES] = MOLES_O2STANDARD
	air_contents.gases[/datum/gas/nitrogen][MOLES] = MOLES_N2STANDARD
	air_contents.temperature = T20C

/obj/structure/transit_tube_pod/Destroy()
	empty_pod()
	return ..()

/obj/structure/transit_tube_pod/update_icon_state()
	icon_state = contents.len ? occupied_icon_state : initial(icon_state)
	return ..()

/obj/structure/transit_tube_pod/attackby(obj/item/I, mob/user, params)
	if(I.tool_behaviour == TOOL_CROWBAR)
		if(!moving)
			I.play_tool_sound(src)
			if(contents.len)
				user.visible_message(span_notice("[user] empties \the [src]."), span_notice("You empty \the [src]."))
				empty_pod()
			else
				deconstruct(TRUE, user)
	else
		return ..()

/obj/structure/transit_tube_pod/deconstruct(disassembled = TRUE, mob/user)
	if(!(flags_1 & NODECONSTRUCT_1))
		var/atom/location = get_turf(src)
		if(user)
			location = user.loc
			add_fingerprint(user)
			user.visible_message(span_notice("[user] removes [src]."), span_notice("You remove [src]."))
		var/obj/structure/c_transit_tube_pod/R = new/obj/structure/c_transit_tube_pod(location)
		transfer_fingerprints_to(R)
		R.setDir(dir)
		empty_pod(location)
	qdel(src)

/obj/structure/transit_tube_pod/ex_act(severity, target)
	. = ..()
	if(!QDELETED(src))
		empty_pod()

/obj/structure/transit_tube_pod/contents_explosion(severity, target)
	switch(severity)
		if(EXPLODE_DEVASTATE)
			SSexplosions.high_mov_atom += contents
		if(EXPLODE_HEAVY)
			SSexplosions.med_mov_atom += contents
		if(EXPLODE_LIGHT)
			SSexplosions.low_mov_atom += contents

/obj/structure/transit_tube_pod/singularity_pull(S, current_size)
	..()
	if(current_size >= STAGE_FIVE)
		deconstruct(FALSE)

/obj/structure/transit_tube_pod/container_resist_act(mob/living/user)
	if(!user.incapacitated())
		empty_pod()
		return
	if(!moving)
		user.changeNext_move(CLICK_CD_BREAKOUT)
		user.last_special = world.time + CLICK_CD_BREAKOUT
		to_chat(user, span_notice("You start trying to escape from the pod..."))
		if(do_after(user, 1 MINUTES, target = src))
			to_chat(user, span_notice("You manage to open the pod."))
			empty_pod()

/obj/structure/transit_tube_pod/proc/empty_pod(atom/location)
	if(!location)
		location = get_turf(src)
	for(var/atom/movable/M in contents)
		M.forceMove(location)
	update_appearance()

/obj/structure/transit_tube_pod/Process_Spacemove()
	if(moving) //No drifting while moving in the tubes
		return TRUE
	else
		return ..()

/obj/structure/transit_tube_pod/proc/follow_tube()
	set waitfor = FALSE
	if(moving)
		return

	moving = TRUE

	for(var/obj/structure/transit_tube/tube in loc)
		if(tube.has_exit(dir))
			current_tube = tube
			break

	move_animation(MOVE_ANIMATION_STAGE_ONE)

///timer loop that handles the pod moving from tube to tube
/obj/structure/transit_tube_pod/proc/move_animation(stage = MOVE_ANIMATION_STAGE_ONE)
	if(stage == MOVE_ANIMATION_STAGE_ONE)
		next_dir = current_tube.get_exit(dir)

		if(!next_dir)
			return

		exit_delay = current_tube.exit_delay(src, dir)
		next_loc = get_step(loc, next_dir)

		current_tube = null
		for(var/obj/structure/transit_tube/tube in next_loc)
			if(tube.has_entrance(next_dir))
				current_tube = tube
				break

		if(current_tube == null)
			setDir(next_dir)
			Move(get_step(loc, dir), dir, DELAY_TO_GLIDE_SIZE(exit_delay)) // Allow collisions when leaving the tubes.
			return

		enter_delay = current_tube.enter_delay(src, next_dir)
		if(enter_delay > 0)
			addtimer(CALLBACK(src, .proc/move_animation, MOVE_ANIMATION_STAGE_TWO), enter_delay)
			return
		else
			stage = MOVE_ANIMATION_STAGE_TWO
	if(stage == MOVE_ANIMATION_STAGE_TWO)
		setDir(next_dir)
		set_glide_size(DELAY_TO_GLIDE_SIZE(enter_delay + exit_delay))
		forceMove(next_loc) // When moving from one tube to another, skip collision and such.
		set_density(current_tube.density)

		if(current_tube?.should_stop_pod(src, next_dir))
			current_tube.pod_stopped(src, dir)
		else
			addtimer(CALLBACK(src, .proc/move_animation, MOVE_ANIMATION_STAGE_ONE), exit_delay)
			return
	set_density(TRUE)
	moving = FALSE

	var/obj/structure/transit_tube/TT = locate(/obj/structure/transit_tube) in loc
	if(!TT || (!(dir in TT.tube_dirs) && !(turn(dir,180) in TT.tube_dirs))) //landed on a turf without transit tube or not in our direction
		outside_tube()

/obj/structure/transit_tube_pod/proc/outside_tube()
	var/list/savedcontents = contents.Copy()
	var/saveddir = dir
	var/turf/destination = get_edge_target_turf(src,saveddir)
	visible_message(span_warning("[src] ejects its insides out!"))
	deconstruct(FALSE)//we automatically deconstruct the pod
	for(var/i in savedcontents)
		var/atom/movable/AM = i
		AM.throw_at(destination,rand(1,3),5)

/obj/structure/transit_tube_pod/return_air()
	return air_contents

/obj/structure/transit_tube_pod/return_analyzable_air()
	return air_contents

/obj/structure/transit_tube_pod/assume_air(datum/gas_mixture/giver)
	return air_contents.merge(giver)

/obj/structure/transit_tube_pod/remove_air(amount)
	return air_contents.remove(amount)

/obj/structure/transit_tube_pod/relaymove(mob/living/user, direction)
	if(!user.client || moving)
		return

	for(var/obj/structure/transit_tube/station/station in loc)
		if(station.pod_moving)
			return
		if(direction == turn(station.boarding_dir,180))
			if(station.open_status == STATION_TUBE_OPEN)
				user.forceMove(loc)
				update_appearance()
			else
				station.open_animation()
		else if(direction in station.tube_dirs)
			setDir(direction)
			station.launch_pod()
		return

	for(var/obj/structure/transit_tube/transit_tube in loc)
		if(!(dir in transit_tube.tube_dirs))
			continue
		if(!transit_tube.has_exit(direction))
			continue
		setDir(direction)
		return

/obj/structure/transit_tube_pod/return_temperature()
	return air_contents.temperature

//special pod made by the dispenser, it fizzles away when reaching a station.

/obj/structure/transit_tube_pod/dispensed
	name = "temporary transit tube pod"
	desc = "Hits the skrrrt (tube station), then hits the dirt (nonexistence). You know how it is."
	icon_state = "temppod"
	occupied_icon_state = "temppod_occupied"

/obj/structure/transit_tube_pod/dispensed/outside_tube()
	qdel(src)

#undef MOVE_ANIMATION_STAGE_ONE
#undef MOVE_ANIMATION_STAGE_TWO


code:
/obj/structure/trap
	name = "IT'S A TRAP"
	desc = "Stepping on me is a guaranteed bad day."
	icon = 'icons/obj/hand_of_god_structures.dmi'
	icon_state = "trap"
	density = FALSE
	anchored = TRUE
	alpha = 30 //initially quite hidden when not "recharging"
	var/flare_message = "the trap flares brightly!"
	var/last_trigger = 0
	var/time_between_triggers = 600 //takes a minute to recharge
	var/charges = INFINITY
	var/checks_antimagic = TRUE

	var/list/static/ignore_typecache
	var/list/mob/immune_minds = list()

	var/sparks = TRUE
	var/datum/effect_system/spark_spread/spark_system

/obj/structure/trap/Initialize(mapload)
	. = ..()
	flare_message = "[src] flares brightly!"
	spark_system = new
	spark_system.set_up(4,1,src)
	spark_system.attach(src)

	var/static/list/loc_connections = list(
		COMSIG_ATOM_ENTERED = .proc/on_entered
	)
	AddElement(/datum/element/connect_loc, loc_connections)

	if(!ignore_typecache)
		ignore_typecache = typecacheof(list(
			/obj/effect,
			/mob/dead))

/obj/structure/trap/Destroy()
	qdel(spark_system)
	spark_system = null
	. = ..()

/obj/structure/trap/examine(mob/user)
	. = ..()
	if(!isliving(user))
		return
	if(user.mind && (user.mind in immune_minds))
		return
	if(get_dist(user, src) <= 1)
		. += span_notice("You reveal [src]!")
		flare()

/obj/structure/trap/proc/flare()
	// Makes the trap visible, and starts the cooldown until it's
	// able to be triggered again.
	visible_message(flare_message)
	if(sparks)
		spark_system.start()
	alpha = 200
	last_trigger = world.time
	charges--
	if(charges <= 0)
		animate(src, alpha = 0, time = 10)
		QDEL_IN(src, 10)
	else
		animate(src, alpha = initial(alpha), time = time_between_triggers)

/obj/structure/trap/proc/on_entered(datum/source, atom/movable/AM)
	SIGNAL_HANDLER
	if(last_trigger + time_between_triggers > world.time)
		return
	// Don't want the traps triggered by sparks, ghosts or projectiles.
	if(is_type_in_typecache(AM, ignore_typecache))
		return
	if(ismob(AM))
		var/mob/M = AM
		if(M.mind in immune_minds)
			return
		if(checks_antimagic && M.anti_magic_check())
			flare()
			return
	if(charges <= 0)
		return
	flare()
	if(isliving(AM))
		trap_effect(AM)

/obj/structure/trap/proc/trap_effect(mob/living/L)
	return

/obj/structure/trap/stun
	name = "shock trap"
	desc = "A trap that will shock and render you immobile. You'd better avoid it."
	icon_state = "trap-shock"
	var/stun_time = 100

/obj/structure/trap/stun/trap_effect(mob/living/L)
	L.electrocute_act(30, src, flags = SHOCK_NOGLOVES) // electrocute act does a message.
	L.Paralyze(stun_time)

/obj/structure/trap/stun/hunter
	name = "bounty trap"
	desc = "A trap that only goes off when a fugitive steps on it, announcing the location and stunning the target. You'd better avoid it."
	icon = 'icons/obj/objects.dmi'
	icon_state = "bounty_trap_on"
	stun_time = 200
	sparks = FALSE //the item version gives them off to prevent runtimes (see Destroy())
	checks_antimagic  = FALSE
	var/obj/item/bountytrap/stored_item
	var/caught = FALSE

/obj/structure/trap/stun/hunter/Initialize(mapload)
	. = ..()
	time_between_triggers = 10
	flare_message = "[src] snaps shut!"

/obj/structure/trap/stun/hunter/Destroy()
	if(!QDELETED(stored_item))
		qdel(stored_item)
	stored_item = null
	return ..()

/obj/structure/trap/stun/hunter/on_entered(datum/source, atom/movable/AM)
	if(isliving(AM))
		var/mob/living/L = AM
		if(!L.mind?.has_antag_datum(/datum/antagonist/fugitive))
			return
	caught = TRUE
	. = ..()

/obj/structure/trap/stun/hunter/flare()
	..()
	var/turf/our_turf = get_turf(src)
	if(!our_turf)
		return
	stored_item.forceMove(get_turf(src))
	forceMove(stored_item)
	if(caught)
		stored_item.announce_fugitive()
		caught = FALSE

/obj/item/bountytrap
	name = "bounty trap"
	desc = "A trap that only goes off when a fugitive steps on it, announcing the location and stunning the target. It's currently inactive."
	icon = 'icons/obj/objects.dmi'
	icon_state = "bounty_trap_off"
	var/obj/structure/trap/stun/hunter/stored_trap
	var/obj/item/radio/radio
	var/datum/effect_system/spark_spread/spark_system

/obj/item/bountytrap/Initialize(mapload)
	. = ..()
	radio = new(src)
	radio.subspace_transmission = TRUE
	radio.canhear_range = 0
	radio.recalculateChannels()
	spark_system = new
	spark_system.set_up(4,1,src)
	spark_system.attach(src)
	name = "[name] #[rand(1, 999)]"
	stored_trap = new(src)
	stored_trap.name = name
	stored_trap.stored_item = src

/obj/item/bountytrap/proc/announce_fugitive()
	spark_system.start()
	playsound(src, 'sound/machines/ding.ogg', 50, TRUE)
	radio.talk_into(src, "Fugitive has triggered this trap in the [get_area_name(src)]!", RADIO_CHANNEL_COMMON)

/obj/item/bountytrap/attack_self(mob/living/user)
	var/turf/T = get_turf(src)
	if(!user || !user.transferItemToLoc(src, T))//visibly unequips
		return
	to_chat(user, "You set up [src]. Examine while close to disarm it.")
	stored_trap.forceMove(T)//moves trap to ground
	forceMove(stored_trap)//moves item into trap

/obj/item/bountytrap/Destroy()
	if(!QDELETED(stored_trap))
		qdel(stored_trap)
	stored_trap = null
	QDEL_NULL(radio)
	QDEL_NULL(spark_system)
	. = ..()

/obj/structure/trap/fire
	name = "flame trap"
	desc = "A trap that will set you ablaze. You'd better avoid it."
	icon_state = "trap-fire"

/obj/structure/trap/fire/trap_effect(mob/living/L)
	to_chat(L, span_danger("Spontaneous combustion!"))
	L.Paralyze(20)
	new /obj/effect/hotspot(get_turf(src))

/obj/structure/trap/chill
	name = "frost trap"
	desc = "A trap that will chill you to the bone. You'd better avoid it."
	icon_state = "trap-frost"

/obj/structure/trap/chill/trap_effect(mob/living/L)
	to_chat(L, span_danger("You're frozen solid!"))
	L.Paralyze(20)
	L.adjust_bodytemperature(-300)
	L.apply_status_effect(/datum/status_effect/freon)

/obj/structure/trap/damage
	name = "earth trap"
	desc = "A trap that will summon a small earthquake, just for you. You'd better avoid it."
	icon_state = "trap-earth"

/obj/structure/trap/damage/trap_effect(mob/living/L)
	to_chat(L, span_danger("The ground quakes beneath your feet!"))
	L.Paralyze(100)
	L.adjustBruteLoss(35)
	var/obj/structure/flora/rock/giant_rock = new(get_turf(src))
	QDEL_IN(giant_rock, 200)

/obj/structure/trap/ward
	name = "divine ward"
	desc = "A divine barrier, It looks like you could destroy it with enough effort, or wait for it to dissipate..."
	icon_state = "ward"
	density = TRUE
	time_between_triggers = 1200 //Exists for 2 minutes

/obj/structure/trap/ward/Initialize(mapload)
	. = ..()
	QDEL_IN(src, time_between_triggers)

/obj/structure/trap/cult
	name = "unholy trap"
	desc = "A trap that rings with unholy energy. You think you hear... chittering?"
	icon_state = "trap-cult"

/obj/structure/trap/cult/trap_effect(mob/living/L)
	to_chat(L, span_danger("With a crack, the hostile constructs come out of hiding, stunning you!"))
	L.electrocute_act(10, src, flags = SHOCK_NOGLOVES) // electrocute act does a message.
	L.Paralyze(20)
	new /mob/living/simple_animal/hostile/construct/proteon/hostile(loc)
	new /mob/living/simple_animal/hostile/construct/proteon/hostile(loc)
	QDEL_IN(src, 30)


code:
#define VOTE_TEXT_LIMIT 255
#define MAX_VOTES 255

/obj/structure/votebox
	name = "voting box"
	desc = "An automatic voting box."

	icon = 'icons/obj/votebox.dmi'
	icon_state = "votebox_maint"

	anchored = TRUE

	var/obj/item/card/id/owner //Slapping the box with this ID starts/ends the vote.

	var/voting_active = FALSE //Voting or Maintenance Mode
	var/id_auth = FALSE //One vote per ID.
	var/vote_description = ""

	var/list/voted //List of ID's that already voted.
	COOLDOWN_DECLARE(vote_print_cooldown)

/obj/structure/votebox/attackby(obj/item/I, mob/living/user, params)
	if(istype(I,/obj/item/card/id))
		if(!owner)
			register_owner(I,user)
			return
	if(istype(I,/obj/item/paper))
		if(voting_active)
			apply_vote(I,user)
		else
			to_chat(user,span_warning("[src] is in maintenance mode. Voting is not possible at the moment."))
		return
	return ..()

/obj/structure/votebox/interact(mob/user)
	..()
	ui_interact(user)

/obj/structure/votebox/ui_interact(mob/user)
	. = ..()

	var/list/dat = list()
	if(!owner)
		dat += "
 Unregistered. Swipe ID card to register as voting box operator
"
	dat += "
[vote_description]
"
	if(is_operator(user))
		dat += "Voting: [voting_active ? "Active" : "Maintenance Mode"]
"
		dat += "Set Description: Set Description
"
		dat += "One vote per ID: [id_auth ? "Yes" : "No"]
"
		dat += "Reset voted ID's: Reset
"
		dat += "Draw random vote: Raffle
"
		dat += "Shred votes: Shred
"
		dat += "Tally votes: Tally
"

	var/datum/browser/popup = new(user, "votebox", "Voting Box", 300, 300)
	popup.set_content(dat.Join())
	popup.open()

/obj/structure/votebox/Topic(href, href_list)
	if(..())
		return

	var/mob/user = usr
	if(!can_interact(user))
		return
	if(!is_operator(user))
		to_chat(user,span_warning("Voting box operator authorization required!"))
		return

	if(href_list["act"])
		switch(href_list["act"])
			if("toggle_vote")
				voting_active = !voting_active
				update_appearance()
			if("toggle_auth")
				id_auth = !id_auth
			if("reset_voted")
				if(voted)
					voted.Cut()
				to_chat(user,span_notice("You reset the voter buffer. Everyone can vote again."))
			if("raffle")
				raffle(user)
			if("shred")
				shred(user)
			if("tally")
				print_tally(user)
			if("set_desc")
				set_description(user)
		interact(user)

/obj/structure/votebox/proc/register_owner(obj/item/card/id/I,mob/living/user)
	owner = I
	to_chat(user,span_notice("You register [src] to your ID card."))
	ui_interact(user)

/obj/structure/votebox/proc/set_description(mob/user)
	var/new_description = stripped_multiline_input(user,"Enter new description","Vote Description",vote_description)
	if(new_description)
		vote_description = new_description

/obj/structure/votebox/proc/is_operator(mob/living/user)
	return (istype(user) && user?.get_idcard() == owner)

/obj/structure/votebox/proc/apply_vote(obj/item/paper/I,mob/living/user)
	var/obj/item/card/id/voter_card = user.get_idcard()
	if(id_auth)
		if(!voter_card)
			to_chat(user,span_warning("[src] requires a valid ID card to vote!"))
			return
		if(voted && (voter_card in voted))
			to_chat(user,span_warning("[src] allows only one vote per person."))
			return
	if(user.transferItemToLoc(I,src))
		if(!voted)
			voted = list()
		voted += voter_card
		to_chat(user,span_notice("You cast your vote."))

/obj/structure/votebox/proc/valid_vote(obj/item/paper/I)
	if(I.get_info_length() > VOTE_TEXT_LIMIT || findtext(I.info,"
Voting Results:

"))
		return FALSE
	return TRUE

/obj/structure/votebox/proc/shred(mob/user)
	for(var/obj/item/paper/P in contents)
		qdel(P)
	to_chat(user,span_notice("You shred the current votes."))

/obj/structure/votebox/wrench_act(mob/living/user, obj/item/I)
	. = ..()
	default_unfasten_wrench(user, I, 40)
	return TRUE

/obj/structure/votebox/crowbar_act(mob/living/user, obj/item/I)
	. = ..()
	if(voting_active)
		to_chat(user,span_warning("You can only retrieve votes if maintenance mode is active!"))
		return FALSE
	dump_contents()
	to_chat(user,span_notice("You open vote retrieval hatch and dump all the votes."))
	return TRUE

/obj/structure/votebox/dump_contents()
	var/atom/droppoint = drop_location()
	for(var/atom/movable/AM in contents)
		AM.forceMove(droppoint)

/obj/structure/votebox/deconstruct(disassembled)
	dump_contents()
	. = ..()

/obj/structure/votebox/proc/raffle(mob/user)
	var/list/options = list()
	for(var/obj/item/paper/P in contents)
		options += P
	if(!length(options))
		to_chat(user,"[src] is empty!")
	else
		var/obj/item/paper/P = pick(options)
		user.put_in_hands(P)
		to_chat(user,span_notice("[src] pops out random vote."))

/obj/structure/votebox/proc/print_tally(mob/user)
	var/list/results = list()
	var/i = 0
	for(var/obj/item/paper/P in contents)
		if(i++ > MAX_VOTES)
			break
		var/text = P.info
		if(!valid_vote(P))
			continue
		if(!results[text])
			results[text] = 1
		else
			results[text] += 1
	sortTim(results, cmp=/proc/cmp_numeric_dsc, associative = TRUE)
	if(!COOLDOWN_FINISHED(src, vote_print_cooldown))
		return
	COOLDOWN_START(src, vote_print_cooldown, 60 SECONDS)
	var/obj/item/paper/P = new(drop_location())
	var/list/tally = list()
	tally += {"

		"}

	tally += "
Voting Results:

"
	for(var/option in results)
		tally += "
\"
[option]
\" - [results[option]] Vote[results[option] > 1 ? "s" : ""].
"
	tally += "
"

	P.info = tally.Join()
	P.name = "Voting Results"
	P.update_appearance()
	user.put_in_hands(P)
	to_chat(user,span_notice("[src] prints out the voting tally."))

/obj/structure/votebox/update_icon_state()
	icon_state = "votebox_[voting_active ? "active" : "maint"]"
	return ..()

#undef VOTE_TEXT_LIMIT
#undef MAX_VOTES


code:
/obj/structure/toilet
	name = "toilet"
	desc = "The HT-451, a torque rotation-based, waste disposal unit for small matter. This one seems remarkably clean."
	icon = 'icons/obj/watercloset.dmi'
	icon_state = "toilet00"
	density = FALSE
	anchored = TRUE
	var/open = FALSE //if the lid is up
	var/cistern = 0 //if the cistern bit is open
	var/w_items = 0 //the combined w_class of all the items in the cistern
	var/mob/living/swirlie = null //the mob being given a swirlie
	var/buildstacktype = /obj/item/stack/sheet/iron //they're iron now, shut up
	var/buildstackamount = 1

/obj/structure/toilet/Initialize(mapload)
	. = ..()
	open = round(rand(0, 1))
	update_appearance()

/obj/structure/toilet/attack_hand(mob/living/user, list/modifiers)
	. = ..()
	if(.)
		return
	if(swirlie)
		user.changeNext_move(CLICK_CD_MELEE)
		playsound(src.loc, "swing_hit", 25, TRUE)
		swirlie.visible_message(span_danger("[user] slams the toilet seat onto [swirlie]'s head!"), span_userdanger("[user] slams the toilet seat onto your head!"), span_hear("You hear reverberating porcelain."))
		log_combat(user, swirlie, "swirlied (brute)")
		swirlie.adjustBruteLoss(5)

	else if(user.pulling && isliving(user.pulling))
		user.changeNext_move(CLICK_CD_MELEE)
		var/mob/living/GM = user.pulling
		if(user.grab_state >= GRAB_AGGRESSIVE)
			if(GM.loc != get_turf(src))
				to_chat(user, span_warning("[GM] needs to be on [src]!"))
				return
			if(!swirlie)
				if(open)
					GM.visible_message(span_danger("[user] starts to give [GM] a swirlie!"), span_userdanger("[user] starts to give you a swirlie..."))
					swirlie = GM
					var/was_alive = (swirlie.stat != DEAD)
					if(do_after(user, 3 SECONDS, target = src, timed_action_flags = IGNORE_HELD_ITEM))
						GM.visible_message(span_danger("[user] gives [GM] a swirlie!"), span_userdanger("[user] gives you a swirlie!"), span_hear("You hear a toilet flushing."))
						if(iscarbon(GM))
							var/mob/living/carbon/C = GM
							if(!C.internal)
								log_combat(user, C, "swirlied (oxy)")
								C.adjustOxyLoss(5)
						else
							log_combat(user, GM, "swirlied (oxy)")
							GM.adjustOxyLoss(5)
					if(was_alive && swirlie.stat == DEAD && swirlie.client)
						swirlie.client.give_award(/datum/award/achievement/misc/swirlie, swirlie) // just like space high school all over again!
					swirlie = null
				else
					playsound(src.loc, 'sound/effects/bang.ogg', 25, TRUE)
					GM.visible_message(span_danger("[user] slams [GM.name] into [src]!"), span_userdanger("[user] slams you into [src]!"))
					log_combat(user, GM, "toilet slammed")
					GM.adjustBruteLoss(5)
		else
			to_chat(user, span_warning("You need a tighter grip!"))

	else if(cistern && !open && user.CanReach(src))
		if(!contents.len)
			to_chat(user, span_notice("The cistern is empty."))
		else
			var/obj/item/I = pick(contents)
			if(ishuman(user))
				user.put_in_hands(I)
			else
				I.forceMove(drop_location())
			to_chat(user, span_notice("You find [I] in the cistern."))
			w_items -= I.w_class
	else
		open = !open
		update_appearance()

/obj/structure/toilet/update_icon_state()
	icon_state = "toilet[open][cistern]"
	return ..()

/obj/structure/toilet/deconstruct()
	if(!(flags_1 & NODECONSTRUCT_1))
		for(var/obj/toilet_item in contents)
			toilet_item.forceMove(drop_location())
		if(buildstacktype)
			new buildstacktype(loc,buildstackamount)
		else
			for(var/i in custom_materials)
				var/datum/material/M = i
				new M.sheet_type(loc, FLOOR(custom_materials[M] / MINERAL_MATERIAL_AMOUNT, 1))
	..()

/obj/structure/toilet/attackby(obj/item/I, mob/living/user, params)
	//SEPTIC EDIT BEGIN
	var/list/modifiers = params2list(params)
	//SEPTIC EDIT END
	add_fingerprint(user)
	if(I.tool_behaviour == TOOL_CROWBAR)
		to_chat(user, span_notice("You start to [cistern ? "replace the lid on the cistern" : "lift the lid off the cistern"]..."))
		playsound(loc, 'sound/effects/stonedoor_openclose.ogg', 50, TRUE)
		if(I.use_tool(src, user, 30))
			user.visible_message(span_notice("[user] [cistern ? "replaces the lid on the cistern" : "lifts the lid off the cistern"]!"), span_notice("You [cistern ? "replace the lid on the cistern" : "lift the lid off the cistern"]!"), span_hear("You hear grinding porcelain."))
			cistern = !cistern
			update_appearance()
		return COMPONENT_CANCEL_ATTACK_CHAIN
	else if(I.tool_behaviour == TOOL_WRENCH && !(flags_1&NODECONSTRUCT_1))
		I.play_tool_sound(src)
		deconstruct()
		return TRUE
	/* SEPTIC EDIT REMOVAL
	else if(cistern && !user.combat_mode)
	*/
	//SEPTIC EDIT BEGIN
	else if(cistern && IS_HELP_INTENT(user, modifiers))
	//SEPTIC EDIT END
		if(I.w_class > WEIGHT_CLASS_NORMAL)
			to_chat(user, span_warning("[I] does not fit!"))
			return
		if(w_items + I.w_class > WEIGHT_CLASS_HUGE)
			to_chat(user, span_warning("The cistern is full!"))
			return
		if(!user.transferItemToLoc(I, src))
			to_chat(user, span_warning("\The [I] is stuck to your hand, you cannot put it in the cistern!"))
			return
		w_items += I.w_class
		to_chat(user, span_notice("You carefully place [I] into the cistern."))

	//else if(istype(I, /obj/item/reagent_containers) && !user.combat_mode) //SEPTIC EDIT REMOVAL
	//SEPTIC EDIT
	else if(istype(I, /obj/item/reagent_containers) && IS_HELP_INTENT(user, params2list(params)))
	//SEPTIC EDIT END
		if (!open)
			return
		if(istype(I, /obj/item/food/monkeycube))
			var/obj/item/food/monkeycube/cube = I
			cube.Expand()
			return
		var/obj/item/reagent_containers/RG = I
		RG.reagents.add_reagent(/datum/reagent/water, min(RG.volume - RG.reagents.total_volume, RG.amount_per_transfer_from_this))
		to_chat(user, span_notice("You fill [RG] from [src]. Gross."))
	. = ..()

/obj/structure/toilet/secret
	var/obj/item/secret
	var/secret_type = null

/obj/structure/toilet/secret/Initialize(mapload)
	. = ..()
	if (secret_type)
		secret = new secret_type(src)
		secret.desc += " It's a secret!"
		w_items += secret.w_class
		contents += secret

/obj/structure/toilet/greyscale
	material_flags = MATERIAL_EFFECTS | MATERIAL_ADD_PREFIX | MATERIAL_COLOR | MATERIAL_AFFECT_STATISTICS
	buildstacktype = null

/obj/structure/urinal
	name = "urinal"
	desc = "The HU-452, an experimental urinal. Comes complete with experimental urinal cake."
	icon = 'icons/obj/watercloset.dmi'
	icon_state = "urinal"
	density = FALSE
	anchored = TRUE
	var/exposed = 0 // can you currently put an item inside
	var/obj/item/hiddenitem = null // what's in the urinal

/obj/structure/urinal/directional/north
	dir = SOUTH
	pixel_y = 32

/obj/structure/urinal/directional/south
	dir = NORTH
	pixel_y = -32

/obj/structure/urinal/directional/east
	dir = WEST
	pixel_x = 32

/obj/structure/urinal/directional/west
	dir = EAST
	pixel_x = -32

/obj/structure/urinal/Initialize(mapload)
	. = ..()
	hiddenitem = new /obj/item/food/urinalcake

/obj/structure/urinal/attack_hand(mob/living/user, list/modifiers)
	. = ..()
	if(.)
		return
	if(user.pulling && isliving(user.pulling))
		var/mob/living/GM = user.pulling
		if(user.grab_state >= GRAB_AGGRESSIVE)
			if(GM.loc != get_turf(src))
				to_chat(user, span_notice("[GM.name] needs to be on [src]."))
				return
			user.changeNext_move(CLICK_CD_MELEE)
			user.visible_message(span_danger("[user] slams [GM] into [src]!"), span_danger("You slam [GM] into [src]!"))
			GM.adjustBruteLoss(8)
		else
			to_chat(user, span_warning("You need a tighter grip!"))

	else if(exposed)
		if(!hiddenitem)
			to_chat(user, span_warning("There is nothing in the drain holder!"))
		else
			if(ishuman(user))
				user.put_in_hands(hiddenitem)
			else
				hiddenitem.forceMove(get_turf(src))
			to_chat(user, span_notice("You fish [hiddenitem] out of the drain enclosure."))
			hiddenitem = null
	else
		..()

/obj/structure/urinal/attackby(obj/item/I, mob/living/user, params)
	if(exposed)
		if (hiddenitem)
			to_chat(user, span_warning("There is already something in the drain enclosure!"))
			return
		if(I.w_class > 1)
			to_chat(user, span_warning("[I] is too large for the drain enclosure."))
			return
		if(!user.transferItemToLoc(I, src))
			to_chat(user, span_warning("[I] is stuck to your hand, you cannot put it in the drain enclosure!"))
			return
		hiddenitem = I
		to_chat(user, span_notice("You place [I] into the drain enclosure."))
	else
		return ..()

/obj/structure/urinal/screwdriver_act(mob/living/user, obj/item/I)
	if(..())
		return TRUE
	to_chat(user, span_notice("You start to [exposed ? "screw the cap back into place" : "unscrew the cap to the drain protector"]..."))
	playsound(loc, 'sound/effects/stonedoor_openclose.ogg', 50, TRUE)
	if(I.use_tool(src, user, 20))
		user.visible_message(span_notice("[user] [exposed ? "screws the cap back into place" : "unscrew the cap to the drain protector"]!"),
			span_notice("You [exposed ? "screw the cap back into place" : "unscrew the cap on the drain"]!"),
			span_hear("You hear metal and squishing noises."))
		exposed = !exposed
	return TRUE

/obj/item/food/urinalcake
	name = "urinal cake"
	desc = "The noble urinal cake, protecting the station's pipes from the station's pee. Do not eat."
	icon = 'icons/obj/items_and_weapons.dmi'
	icon_state = "urinalcake"
	w_class = WEIGHT_CLASS_TINY
	food_reagents = list(/datum/reagent/chlorine = 3, /datum/reagent/ammonia = 1)
	foodtypes = TOXIC | GROSS

/obj/item/food/urinalcake/attack_self(mob/living/user)
	user.visible_message(span_notice("[user] squishes [src]!"), span_notice("You squish [src]."), "<i>You hear a squish.</i>")
	icon_state = "urinalcake_squish"
	addtimer(VARSET_CALLBACK(src, icon_state, "urinalcake"), 8)

/obj/item/bikehorn/rubberducky/plasticducky
	name = "plastic ducky"
	desc = "It's a cheap plastic knockoff of a loveable bathtime toy."
	custom_materials = list(/datum/material/plastic = 1000)

/obj/item/bikehorn/rubberducky
	name = "rubber ducky"
	desc = "Rubber ducky you're so fine, you make bathtime lots of fuuun. Rubber ducky I'm awfully fooooond of yooooouuuu~" //thanks doohl
	icon = 'icons/obj/watercloset.dmi'
	icon_state = "rubberducky"
	inhand_icon_state = "rubberducky"
	worn_icon_state = "duck"

/obj/structure/sink
	name = "sink"
	icon = 'icons/obj/watercloset.dmi'
	icon_state = "sink"
	desc = "A sink used for washing one's hands and face. Passively reclaims water over time."
	anchored = TRUE
	///Something's being washed at the moment
	var/busy = FALSE
	///What kind of reagent is produced by this sink by default? (We now have actual plumbing, Arcane, August 2020)
	var/dispensedreagent = /datum/reagent/water
	///Material to drop when broken or deconstructed.
	var/buildstacktype = /obj/item/stack/sheet/iron
	///Number of sheets of material to drop when broken or deconstructed.
	var/buildstackamount = 1
	///Does the sink have a water recycler to recollect it's water supply?
	var/has_water_reclaimer = TRUE
	///Has the water reclamation begun?
	var/reclaiming = FALSE
	///Units of water to reclaim per second
	var/reclaim_rate = 0.5

/obj/structure/sink/Initialize(mapload, bolt)
	. = ..()
	if(has_water_reclaimer)
		create_reagents(100, NO_REACT)
		reagents.add_reagent(dispensedreagent, 100)
	AddComponent(/datum/component/plumbing/simple_demand, bolt)

/obj/structure/sink/examine(mob/user)
	. = ..()
	. += span_notice("[reagents.total_volume]/[reagents.maximum_volume] liquids remaining.")

/obj/structure/sink/attack_hand(mob/living/user, list/modifiers)
	. = ..()
	if(.)
		return
	if(!user || !istype(user))
		return
	if(!iscarbon(user))
		return
	if(!Adjacent(user))
		return
	if(reagents.total_volume < 5)
		to_chat(user, span_warning("The sink has no more contents left!"))
		return
	if(busy)
		to_chat(user, span_warning("Someone's already washing here!"))
		return
	var/selected_area = parse_zone(user.zone_selected)
	var/washing_face = 0
	/* SEPTIC EDIT REMOVAL
	if(selected_area in list(BODY_ZONE_HEAD, BODY_ZONE_PRECISE_MOUTH, BODY_ZONE_PRECISE_EYES))
		washing_face = 1
	*/
	//SEPTIC EDIT BEGIN
	if(selected_area in list(BODY_ZONE_HEAD, BODY_ZONE_PRECISE_FACE, BODY_ZONE_PRECISE_MOUTH, BODY_ZONE_PRECISE_L_EYE, BODY_ZONE_PRECISE_R_EYE))
		washing_face = 1
	//SEPTIC EDIT END
	user.visible_message(span_notice("[user] starts washing [user.p_their()] [washing_face ? "face" : "hands"]..."), \
						span_notice("You start washing your [washing_face ? "face" : "hands"]..."))
	busy = TRUE

	if(!do_after(user, 40, target = src))
		busy = FALSE
		return

	busy = FALSE
	reagents.remove_any(5)
	reagents.expose(user, TOUCH, 5 / max(reagents.total_volume, 5))
	begin_reclamation()
	if(washing_face)
		SEND_SIGNAL(user, COMSIG_COMPONENT_CLEAN_FACE_ACT, CLEAN_WASH)
		user.adjust_drowsyness(rand(-2, -3)) //Washing your face wakes you up if you're falling asleep
	else if(ishuman(user))
		var/mob/living/carbon/human/human_user = user
		if(!human_user.wash_hands(CLEAN_WASH))
			to_chat(user, span_warning("Your hands are covered by something!"))
			return
	else
		user.wash(CLEAN_WASH)

	user.visible_message(span_notice("[user] washes [user.p_their()] [washing_face ? "face" : "hands"] using [src]."), \
						span_notice("You wash your [washing_face ? "face" : "hands"] using [src]."))

/obj/structure/sink/attackby(obj/item/O, mob/living/user, params)
	if(busy)
		to_chat(user, span_warning("Someone's already washing here!"))
		return

	if(istype(O, /obj/item/reagent_containers))
		var/obj/item/reagent_containers/RG = O
		if(reagents.total_volume <= 0)
			to_chat(user, span_notice("\The [src] is dry."))
			return FALSE
		if(RG.is_refillable())
			if(!RG.reagents.holder_full())
				reagents.trans_to(RG, RG.amount_per_transfer_from_this, transfered_by = user)
				begin_reclamation()
				to_chat(user, span_notice("You fill [RG] from [src]."))
				return TRUE
			to_chat(user, span_notice("\The [RG] is full."))
			return FALSE

	if(istype(O, /obj/item/melee/baton/security))
		var/obj/item/melee/baton/security/baton = O
		if(baton.cell?.charge && baton.active)
			flick("baton_active", src)
			user.Paralyze(baton.knockdown_time)
			user.stuttering = baton.knockdown_time*0.5
			baton.cell.use(baton.cell_hit_cost)
			user.visible_message(span_warning("[user] shocks [user.p_them()]self while attempting to wash the active [baton.name]!"), \
								span_userdanger("You unwisely attempt to wash [baton] while it's still on."))
			playsound(src, baton.on_stun_sound, 50, TRUE)
			return

	if(istype(O, /obj/item/mop))
		if(reagents.total_volume <= 0)
			to_chat(user, span_notice("\The [src] is dry."))
			return FALSE
		reagents.trans_to(O, 5, transfered_by = user)
		begin_reclamation()
		to_chat(user, span_notice("You wet [O] in [src]."))
		playsound(loc, 'sound/effects/slosh.ogg', 25, TRUE)
		return

	if(O.tool_behaviour == TOOL_WRENCH && !(flags_1&NODECONSTRUCT_1))
		O.play_tool_sound(src)
		deconstruct()
		return

	if(istype(O, /obj/item/stack/medical/gauze))
		var/obj/item/stack/medical/gauze/G = O
		new /obj/item/reagent_containers/glass/rag(src.loc)
		to_chat(user, span_notice("You tear off a strip of gauze and make a rag."))
		G.use(1)
		return

	if(istype(O, /obj/item/stack/sheet/cloth))
		var/obj/item/stack/sheet/cloth/cloth = O
		new /obj/item/reagent_containers/glass/rag(loc)
		to_chat(user, span_notice("You tear off a strip of cloth and make a rag."))
		cloth.use(1)
		return

	if(istype(O, /obj/item/stack/ore/glass))
		new /obj/item/stack/sheet/sandblock(loc)
		to_chat(user, span_notice("You wet the sand in the sink and form it into a block."))
		O.use(1)
		return

	if(!istype(O))
		return
	if(O.item_flags & ABSTRACT) //Abstract items like grabs won't wash. No-drop items will though because it's still technically an item in your hand.
		return
	/* SEPTIC EDIT REMOVAL
	if(!user.combat_mode)
	*/
	//SEPTIC EDIT BEGIN
	var/list/modifiers = params2list(params)
	if(IS_HELP_INTENT(user, modifiers))
	//SEPTIC EDIT END
		to_chat(user, span_notice("You start washing [O]..."))
		busy = TRUE
		if(!do_after(user, 40, target = src))
			busy = FALSE
			return 1
		busy = FALSE
		O.wash(CLEAN_WASH)
		reagents.expose(O, TOUCH, 5 / max(reagents.total_volume, 5))
		user.visible_message(span_notice("[user] washes [O] using [src]."), \
							span_notice("You wash [O] using [src]."))
		return 1
	else
		return ..()

/obj/structure/sink/deconstruct()
	if(!(flags_1 & NODECONSTRUCT_1))
		drop_materials()
	..()

/obj/structure/sink/process(delta_time)
	if(has_water_reclaimer && reagents.total_volume < reagents.maximum_volume)
		reagents.add_reagent(dispensedreagent, reclaim_rate * delta_time)
	else
		reclaiming = FALSE
		return PROCESS_KILL

/obj/structure/sink/proc/drop_materials()
	if(buildstacktype)
		new buildstacktype(loc,buildstackamount)
	else
		for(var/i in custom_materials)
			var/datum/material/M = i
			new M.sheet_type(loc, FLOOR(custom_materials[M] / MINERAL_MATERIAL_AMOUNT, 1))

/obj/structure/sink/proc/begin_reclamation()
	if(!reclaiming)
		reclaiming = TRUE
		START_PROCESSING(SSfluids, src)

/obj/structure/sink/kitchen
	name = "kitchen sink"
	icon_state = "sink_alt"

/obj/structure/sink/greyscale
	icon_state = "sink_greyscale"
	material_flags = MATERIAL_EFFECTS | MATERIAL_ADD_PREFIX | MATERIAL_COLOR | MATERIAL_AFFECT_STATISTICS
	buildstacktype = null

/obj/structure/sinkframe
	name = "sink frame"
	icon = 'icons/obj/watercloset.dmi'
	icon_state = "sink_frame"
	desc = "A sink frame, that needs a water recycler to finish construction."
	anchored = FALSE
	material_flags = MATERIAL_EFFECTS | MATERIAL_ADD_PREFIX | MATERIAL_COLOR | MATERIAL_AFFECT_STATISTICS

/obj/structure/sinkframe/ComponentInitialize()
	. = ..()
	AddComponent(/datum/component/simple_rotation, ROTATION_ALTCLICK | ROTATION_CLOCKWISE | ROTATION_COUNTERCLOCKWISE | ROTATION_VERBS, null, CALLBACK(src, .proc/can_be_rotated))

/obj/structure/sinkframe/proc/can_be_rotated(mob/user, rotation_type)
	if(anchored)
		to_chat(user, span_warning("It is fastened to the floor!"))
	return !anchored

/obj/structure/sinkframe/attackby(obj/item/I, mob/living/user, params)
	if(istype(I, /obj/item/stock_parts/water_recycler))
		qdel(I)
		var/obj/structure/sink/greyscale/new_sink = new /obj/structure/sink/greyscale(loc)
		new_sink.has_water_reclaimer = TRUE
		new_sink.set_custom_materials(custom_materials)
		new_sink.setDir(dir)
		qdel(src)
		return
	return ..()

/obj/structure/sinkframe/wrench_act_secondary(mob/living/user, obj/item/tool)
	. = ..()
	tool.play_tool_sound(src)
	deconstruct()
	return TRUE

/obj/structure/sinkframe/deconstruct()
	if(!(flags_1 & NODECONSTRUCT_1))
		drop_materials()
	return ..()

/obj/structure/sinkframe/proc/drop_materials()
	for(var/datum/material/material as anything in custom_materials)
		new material.sheet_type(loc, FLOOR(custom_materials[material] / MINERAL_MATERIAL_AMOUNT, 1))
	return

//Water source, use the type water_source for unlimited water sources like classic sinks.
/obj/structure/water_source
	name = "Water Source"
	icon = 'icons/obj/watercloset.dmi'
	icon_state = "sink"
	desc = "A sink used for washing one's hands and face. This one seems to be infinite!"
	anchored = TRUE
	var/busy = FALSE //Something's being washed at the moment
	var/dispensedreagent = /datum/reagent/water // for whenever plumbing happens

/obj/structure/water_source/attack_hand(mob/living/user, list/modifiers)
	. = ..()
	if(.)
		return
	if(!iscarbon(user))
		return
	if(!Adjacent(user))
		return

	if(busy)
		to_chat(user, span_warning("Someone's already washing here!"))
		return
	var/selected_area = parse_zone(user.zone_selected)
	var/washing_face = FALSE
	/* SEPTIC EDIT REMOVAL
	if(selected_area in list(BODY_ZONE_HEAD, BODY_ZONE_PRECISE_MOUTH, BODY_ZONE_PRECISE_EYES))
		washing_face = TRUE
	*/
	//SEPTIC EDIT BEGIN
	if(selected_area in list(BODY_ZONE_HEAD, BODY_ZONE_PRECISE_FACE, BODY_ZONE_PRECISE_MOUTH, BODY_ZONE_PRECISE_L_EYE, BODY_ZONE_PRECISE_R_EYE))
		washing_face = TRUE
	//SEPTIC EDIT END
	user.visible_message(span_notice("[user] starts washing [user.p_their()] [washing_face ? "face" : "hands"]..."), \
						span_notice("You start washing your [washing_face ? "face" : "hands"]..."))
	busy = TRUE

	if(!do_after(user, 4 SECONDS, target = src))
		busy = FALSE
		return

	busy = FALSE

	if(washing_face)
		SEND_SIGNAL(user, COMSIG_COMPONENT_CLEAN_FACE_ACT, CLEAN_WASH)
		user.adjust_drowsyness(rand(-2, -3)) //Washing your face wakes you up if you're falling asleep
	else if(ishuman(user))
		var/mob/living/carbon/human/human_user = user
		if(!human_user.wash_hands(CLEAN_WASH))
			to_chat(user, span_warning("Your hands are covered by something!"))
			return
	else
		user.wash(CLEAN_WASH)

	user.visible_message(span_notice("[user] washes [user.p_their()] [washing_face ? "face" : "hands"] using [src]."), \
						span_notice("You wash your [washing_face ? "face" : "hands"] using [src]."))

/obj/structure/water_source/attackby(obj/item/O, mob/living/user, params)
	if(busy)
		to_chat(user, span_warning("Someone's already washing here!"))
		return

	if(istype(O, /obj/item/reagent_containers))
		var/obj/item/reagent_containers/container = O
		if(container.is_refillable())
			if(!container.reagents.holder_full())
				container.reagents.add_reagent(dispensedreagent, min(container.volume - container.reagents.total_volume, container.amount_per_transfer_from_this))
				to_chat(user, span_notice("You fill [container] from [src]."))
				return TRUE
			to_chat(user, span_notice("\The [container] is full."))
			return FALSE

	if(istype(O, /obj/item/melee/baton/security))
		var/obj/item/melee/baton/security/baton = O
		if(baton.cell?.charge && baton.active)
			flick("baton_active", src)
			user.Paralyze(baton.knockdown_time)
			user.stuttering = baton.knockdown_time*0.5
			baton.cell.use(baton.cell_hit_cost)
			user.visible_message(span_warning("[user] shocks [user.p_them()]self while attempting to wash the active [baton.name]!"), \
								span_userdanger("You unwisely attempt to wash [baton] while it's still on."))
			playsound(src, baton.on_stun_sound, 50, TRUE)
			return

	if(istype(O, /obj/item/mop))
		O.reagents.add_reagent(dispensedreagent, 5)
		to_chat(user, span_notice("You wet [O] in [src]."))
		playsound(loc, 'sound/effects/slosh.ogg', 25, TRUE)
		return

	if(istype(O, /obj/item/stack/medical/gauze))
		var/obj/item/stack/medical/gauze/G = O
		new /obj/item/reagent_containers/glass/rag(loc)
		to_chat(user, span_notice("You tear off a strip of gauze and make a rag."))
		G.use(1)
		return

	if(istype(O, /obj/item/stack/sheet/cloth))
		var/obj/item/stack/sheet/cloth/cloth = O
		new /obj/item/reagent_containers/glass/rag(loc)
		to_chat(user, span_notice("You tear off a strip of cloth and make a rag."))
		cloth.use(1)
		return

	if(istype(O, /obj/item/stack/ore/glass))
		new /obj/item/stack/sheet/sandblock(loc)
		to_chat(user, span_notice("You wet the sand and form it into a block."))
		O.use(1)
		return

	if(O.item_flags & ABSTRACT) //Abstract items like grabs won't wash. No-drop items will though because it's still technically an item in your hand.
		return
	/* SEPTIC EDIT REMOVAL
	if(!user.combat_mode)
	*/
	//SEPTIC EDIT
	var/list/modifiers = params2list(params)
	if(IS_HELP_INTENT(user, modifiers))
	//SEPTIC EDIT END
		to_chat(user, span_notice("You start washing [O]..."))
		busy = TRUE
		if(!do_after(user, 4 SECONDS, target = src))
			busy = FALSE
			return TRUE
		busy = FALSE
		O.wash(CLEAN_WASH)
		reagents.expose(O, TOUCH, 5 / max(reagents.total_volume, 5))
		user.visible_message(span_notice("[user] washes [O] using [src]."), \
							span_notice("You wash [O] using [src]."))
		return TRUE

	return ..()

/obj/structure/water_source/puddle //splishy splashy ^_^
	name = "puddle"
	desc = "A puddle used for washing one's hands and face."
	icon_state = "puddle"
	resistance_flags = UNACIDABLE

//ATTACK HAND IGNORING PARENT RETURN VALUE
/obj/structure/water_source/puddle/attack_hand(mob/user, list/modifiers)
	icon_state = "puddle-splash"
	. = ..()
	icon_state = "puddle"

/obj/structure/water_source/puddle/attackby(obj/item/O, mob/user, params)
	icon_state = "puddle-splash"
	. = ..()
	icon_state = "puddle"

/obj/structure/water_source/puddle/deconstruct(disassembled = TRUE)
	qdel(src)

//End legacy sink

//Shower Curtains//
//Defines used are pre-existing in layers.dm//

/obj/structure/curtain
	name = "curtain"
	desc = "Contains less than 1% mercury."
	icon = 'icons/obj/watercloset.dmi'
	icon_state = "bathroom-open"
	var/icon_type = "bathroom"//used in making the icon state
	color = "#ACD1E9" //Default color, didn't bother hardcoding other colors, mappers can and should easily change it.
	alpha = 200 //Mappers can also just set this to 255 if they want curtains that can't be seen through
	layer = SIGN_LAYER
	anchored = TRUE
	opacity = FALSE
	density = FALSE
	var/open = TRUE
	/// if it can be seen through when closed
	var/opaque_closed = FALSE

/obj/structure/curtain/proc/toggle()
	open = !open
	if(open)
		layer = SIGN_LAYER
		set_density(FALSE)
		set_opacity(FALSE)
	else
		layer = WALL_OBJ_LAYER
		set_density(TRUE)
		if(opaque_closed)
			set_opacity(TRUE)

	update_appearance()

/obj/structure/curtain/update_icon_state()
	icon_state = "[icon_type]-[open ? "open" : "closed"]"
	return ..()

/obj/structure/curtain/attackby(obj/item/W, mob/user)
	if (istype(W, /obj/item/toy/crayon))
		color = input(user,"","Choose Color",color) as color
	else
		return ..()

/obj/structure/curtain/wrench_act(mob/living/user, obj/item/I)
	..()
	default_unfasten_wrench(user, I, 50)
	return TRUE

/obj/structure/curtain/wirecutter_act(mob/living/user, obj/item/I)
	..()
	if(anchored)
		return TRUE

	user.visible_message(span_warning("[user] cuts apart [src]."),
		span_notice("You start to cut apart [src]."), span_hear("You hear cutting."))
	if(I.use_tool(src, user, 50, volume=100) && !anchored)
		to_chat(user, span_notice("You cut apart [src]."))
		deconstruct()

	return TRUE

/obj/structure/curtain/attack_hand(mob/user, list/modifiers)
	. = ..()
	if(.)
		return
	playsound(loc, 'sound/effects/curtain.ogg', 50, TRUE)
	toggle()

/obj/structure/curtain/deconstruct(disassembled = TRUE)
	new /obj/item/stack/sheet/cloth (loc, 2)
	new /obj/item/stack/sheet/plastic (loc, 2)
	new /obj/item/stack/rods (loc, 1)
	qdel(src)

/obj/structure/curtain/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0)
	switch(damage_type)
		if(BRUTE)
			if(damage_amount)
				playsound(src.loc, 'sound/weapons/slash.ogg', 80, TRUE)
			else
				playsound(loc, 'sound/weapons/tap.ogg', 50, TRUE)
		if(BURN)
			playsound(loc, 'sound/items/welder.ogg', 80, TRUE)

/obj/structure/curtain/bounty
	icon_type = "bounty"
	icon_state = "bounty-open"
	color = null
	alpha = 255
	opaque_closed = TRUE

/obj/structure/curtain/cloth
	color = null
	alpha = 255
	opaque_closed = TRUE

/obj/structure/curtain/cloth/deconstruct(disassembled = TRUE)
	new /obj/item/stack/sheet/cloth (loc, 4)
	new /obj/item/stack/rods (loc, 1)
	qdel(src)

/obj/structure/curtain/cloth/fancy
	icon_type = "cur_fancy"
	icon_state = "cur_fancy-open"

/obj/structure/curtain/cloth/fancy/mechanical
	var/id = null

/obj/structure/curtain/cloth/fancy/mechanical/Destroy()
	GLOB.curtains -= src
	return ..()

/obj/structure/curtain/cloth/fancy/mechanical/Initialize(mapload)
	. = ..()
	GLOB.curtains += src

/obj/structure/curtain/cloth/fancy/mechanical/connect_to_shuttle(obj/docking_port/mobile/port, obj/docking_port/stationary/dock)
	id = "[port.id]_[id]"

/obj/structure/curtain/cloth/fancy/mechanical/proc/open()
	icon_state = "[icon_type]-open"
	layer = SIGN_LAYER
	set_density(FALSE)
	open = TRUE
	set_opacity(FALSE)

/obj/structure/curtain/cloth/fancy/mechanical/proc/close()
	icon_state = "[icon_type]-closed"
	layer = WALL_OBJ_LAYER
	set_density(TRUE)
	open = FALSE
	if(opaque_closed)
		set_opacity(TRUE)

/obj/structure/curtain/cloth/fancy/mechanical/attack_hand(mob/user, list/modifiers)
		return