code:
//CONTAINS: Detective's Scanner

// TODO: Split everything into easy to manage procs.

/obj/item/device/detective_scanner
	name = "forensic scanner"
	desc = "Used to remotely scan objects and biomass for DNA and fingerprints. Can print a report of the findings."
	icon_state = "forensic1"
	w_class = 3.0
	item_state = "electronic"
	flags = CONDUCT | NOBLUDGEON
	slot_flags = SLOT_BELT
	var/scanning = 0
	var/list/log = list()

/obj/item/device/detective_scanner/attack_self(var/mob/user)
	if(log.len && !scanning)
		scanning = 1
		user << "Printing report, please wait..."

		spawn(100)

			// Create our paper
			var/obj/item/weapon/paper/P = new(get_turf(src))
			P.name = "paper- 'Scanner Report'"
			P.info = "Scanner Report

"
			P.info += list2text(log, "
")
			P.info += "
Notes:
"
			P.info_links = P.info

			if(ismob(loc))
				var/mob/M = loc
				M.put_in_hands(P)
				M << "Report printed. Log cleared."

			// Clear the logs
			log = list()
			scanning = 0
	else
		user << "The scanner has no logs or is in use."

/obj/item/device/detective_scanner/attack(mob/living/M as mob, mob/user as mob)
	return

/obj/item/device/detective_scanner/afterattack(atom/A, mob/user as mob, proximity)
	scan(A, user)

/obj/item/device/detective_scanner/proc/scan(var/atom/A, var/mob/user)

	if(!scanning)
		// Can remotely scan objects and mobs.
		if(!in_range(A, user) && !(A in view(world.view, user)))
			return
		if(loc != user)
			return

		scanning = 1

		user.visible_message("\The [user] points the [src.name] at \the [A] and performs a forensic scan.")
		user << "You scan \the [A]. The scanner is now analysing the results..."

		// GATHER INFORMATION

		//Make our lists
		var/list/fingerprints = list()
		var/list/blood = list()
		var/list/fibers = list()
		var/list/reagents = list()

		var/target_name = A.name

		// Start gathering

		if(A.blood_DNA && A.blood_DNA.len)
			blood = A.blood_DNA.Copy()

		if(A.suit_fibers && A.suit_fibers.len)
			fibers = A.suit_fibers.Copy()

		if(ishuman(A))

			var/mob/living/carbon/human/H = A
			if (istype(H.dna, /datum/dna) && !H.gloves)
				fingerprints += md5(H.dna.uni_identity)

		else if(!ismob(A))

			if(A.fingerprints && A.fingerprints.len)
				fingerprints = A.fingerprints.Copy()

			// Only get reagents from non-mobs.
			if(A.reagents && A.reagents.reagent_list.len)

				for(var/datum/reagent/R in A.reagents.reagent_list)
					reagents[R.name] = R.volume

					// Get blood data from the blood reagent.
					if(istype(R, /datum/reagent/blood))

						if(R.data["blood_DNA"] && R.data["blood_type"])
							var/blood_DNA = R.data["blood_DNA"]
							var/blood_type = R.data["blood_type"]
							blood[blood_DNA] = blood_type

		// We gathered everything. Create a fork and slowly display the results to the holder of the scanner.

		spawn(0)

			var/found_something = 0
			add_log("[worldtime2text()][get_timestamp()] - [target_name]", 0)

			// Fingerprints
			if(fingerprints && fingerprints.len)
				sleep(30)
				add_log("Prints:")
				for(var/finger in fingerprints)
					add_log("[finger]")
				found_something = 1

			// Blood
			if (blood && blood.len)
				sleep(30)
				add_log("Blood:")
				found_something = 1
				for(var/B in blood)
					add_log("Type: [blood[B]] DNA: [B]")

			//Fibers
			if(fibers && fibers.len)
				sleep(30)
				add_log("Fibers:")
				for(var/fiber in fibers)
					add_log("[fiber]")
				found_something = 1

			//Reagents
			if(reagents && reagents.len)
				sleep(30)
				add_log("Reagents:")
				for(var/R in reagents)
					add_log("Reagent: [R] Volume: [reagents[R]]")
				found_something = 1

			// Get a new user
			var/mob/holder = null
			if(ismob(src.loc))
				holder = src.loc

			if(!found_something)
				add_log("# No forensic traces found #", 0) // Don't display this to the holder user
				if(holder)
					holder << "Unable to locate any fingerprints, materials, fibers, or blood on \the [target_name]!"
			else
				if(holder)
					holder << "You finish scanning \the [target_name]."

			add_log("---------------------------------------------------------", 0)
			scanning = 0
			return

/obj/item/device/detective_scanner/proc/add_log(var/msg, var/broadcast = 1)
	if(scanning)
		if(broadcast && ismob(loc))
			var/mob/M = loc
			M << msg
		log += "  [msg]"
	else
		CRASH("[src] \ref[src] is adding a log when it was never put in scanning mode!")

/proc/get_timestamp()
	return time2text(world.time + 432000, ":ss")


code:
var/list/emojis

/proc/emoji_parse(text)
	if(!config.emojis)
		return text
	if(!emojis)
		emojis = icon_states(icon('icons/emoji.dmi'))
	var/parsed = ""
	var/pos = 1
	var/search = 0
	var/emoji = ""
	while(1)
		search = findtext(text, ":", pos)
		parsed += copytext(text, pos, search)
		if(search)
			pos = search
			search = findtext(text, ":", pos+1)
			if(search)
				emoji = lowertext(copytext(text, pos+1, search))
				if(emoji in emojis)
					parsed += "<img class=icon src=\ref['icons/emoji.dmi'] iconstate='[emoji]'>"
					pos = search + 1
				else
					parsed += copytext(text, pos, search)
					pos = search
				emoji = ""
				continue
			else
				parsed += copytext(text, pos, search)
		break
	return parsed

license: mit


code:
 // Universal State
 // Handles stuff like space icon_state, constants, etc.
 // Essentially a policy manager.  Once shit hits the fan, this changes its policies.
 // Called by master controller.

 // Default shit.
/datum/universal_state
	// Just for reference, for now.
	// Might eventually add an observatory job.
 	var/name = "Normal"
 	var/desc = "Nothing seems awry."

 	// Sets world.turf, replaces all turfs of type /turf/space.
 	var/space_type         = /turf/space

 	// Replaces all turfs of type /turf/space/transit
 	var/transit_space_type = /turf/space/transit

 	// Chance of a floor or wall getting damaged [0-100]
 	// Simulates stuff getting broken due to molecular bonds decaying.
 	var/decay_rate = 0

// Actually decay the turf.
/datum/universal_state/proc/DecayTurf(var/turf/T)
	if(istype(T,/turf/simulated/wall))
		var/turf/simulated/wall/W=T
		W.melt()
		return
	if(istype(T,/turf/simulated/floor))
		var/turf/simulated/floor/F=T
		// Burnt?
		if(!F.burnt)
			F.burn_tile()
		else
			F.ReplaceWithLattice()
		return

// Return 0 to cause shuttle call to fail.
/datum/universal_state/proc/OnShuttleCall(var/mob/user)
	return 1

// Processed per tick
/datum/universal_state/proc/OnTurfTick(var/turf/T)
	if(decay_rate && prob(decay_rate))
		DecayTurf(T)

// Apply changes when exiting state
/datum/universal_state/proc/OnExit()
 	// Does nothing by default

// Apply changes when entering state
/datum/universal_state/proc/OnEnter()
 	// Does nothing by default

// Apply changes to a new turf.
/datum/universal_state/proc/OnTurfChange(var/turf/NT)
 	return

/datum/universal_state/proc/OverlayAndAmbientSet()
	return

/proc/SetUniversalState(var/newstate,var/on_exit=1, var/on_enter=1)
	if(on_exit)
		universe.OnExit()
	universe = new newstate
	if(on_enter)
		universe.OnEnter()


code:
// QUALITY COPYPASTA
/turf/simulated/supermatter
	name = "Bluespace"
	desc = "THE END IS right now actually."

	icon = 'icons/turf/space.dmi'
	icon_state = "bluespace"

	//luminosity = 5
	//l_color="#0066FF"
	layer = LIGHTING_LAYER+1
	density = 1
	opacity = 1
	blocks_air = 1
	var/spawned=0 // DIR mask
	var/next_check=0
	var/list/avail_dirs = list(NORTH,SOUTH,EAST,WEST)
	dynamic_lighting = 0
	luminosity = 1

/turf/simulated/supermatter/New()
	..()
	SSobj.processing |= src
	next_check = world.time+50

/turf/simulated/supermatter/Destroy()
	SSobj.processing.Remove(src)
	..()

/turf/simulated/supermatter/process()
	// Only check infrequently.
	if(next_check>world.time) return

	// No more available directions? Shut down process().
	if(avail_dirs.len==0)
		SSobj.processing.Remove(src)
		return 1

	// We're checking, reset the timer.
	next_check = world.time+50

	// Choose a direction.
	var/pdir = pick(avail_dirs)
	avail_dirs -= pdir
	var/turf/T=get_step(src,pdir)

	// EXPAND DONG
	if(!istype(T,type))
		// Do pretty fadeout animation for 1s.
		new /obj/effect/overlay/bluespacify(T)
		spawn(10)
			// Nom.
			for(var/atom/movable/A in T)
				if(A)
					if(istype(A,/mob/living))
						qdel(A)
					else if(istype(A,/mob)) // Observers, AI cameras.
						continue
					qdel(A)
			T.ChangeTurf(type)

	if((spawned & (NORTH|SOUTH|EAST|WEST)) == (NORTH|SOUTH|EAST|WEST))
		SSobj.processing.Remove(src)
		return

/turf/simulated/supermatter/attack_paw(mob/user as mob)
	return attack_hand(user)

/turf/simulated/supermatter/attack_robot(mob/user as mob)
	if(Adjacent(user))
		return attack_hand(user)
	else
		user << "What the fuck are you doing?"
	return

/turf/simulated/supermatter/attack_hand(mob/user as mob)
	user.visible_message("\The [user] reaches out and touches \the [src]... And then blinks out of existance.",\
		"You reach out and touch \the [src]. Everything immediately goes quiet. Your last thought is \"That was not a wise decision.\"",\
		"You hear an unearthly noise.")

	playsound(src, 'sound/effects/supermatter.ogg', 50, 1)

	Consume(user)

/turf/simulated/supermatter/attackby(obj/item/weapon/W as obj, mob/living/user as mob)
	user.visible_message("\The [user] touches \a [W] to \the [src] as a silence fills the room...",\
		"You touch \the [W] to \the [src] when everything suddenly goes silent.\"\n\The [W] flashes into dust as you flinch away from \the [src].",\
		"Everything suddenly goes silent.")

	playsound(src, 'sound/effects/supermatter.ogg', 50, 1)

	user.unEquip(W)
	Consume(W)

/turf/simulated/supermatter/Bumped(atom/AM as mob|obj)
	if(istype(AM, /mob/living))
		AM.visible_message("\The [AM] slams into \the [src] inducing a resonance... \his body starts to glow and catch flame before flashing into ash.",\
		"You slam into \the [src] as your ears are filled with unearthly ringing. Your last thought is \"Oh, fuck.\"",\
		"You hear an unearthly noise as a wave of heat washes over you.")
	else
		AM.visible_message("\The [AM] smacks into \the [src] and rapidly flashes to ash.",\
		"You hear a loud crack as you are washed with a wave of heat.")

	playsound(src, 'sound/effects/supermatter.ogg', 50, 1)

	Consume(AM)

/turf/simulated/supermatter/proc/Consume(var/mob/living/user)
	if(istype(user,/mob/dead/observer))
		return

	qdel(user)


code:
/*** EXIT PORTAL ***/

/obj/singularity/narsie/large/exit
	name = "Bluespace Rift"
	desc = "NO TIME TO EXPLAIN, JUMP IN"
	icon = 'icons/obj/rift.dmi'
	icon_state = "rift"

	move_self = 0
	announce=0
	narnar=0

	layer=LIGHTING_LAYER+2 // ITS SO BRIGHT

	consume_range = 6

/obj/singularity/narsie/large/exit/New()
	..()
	SSobj.processing |= src

/obj/singularity/narsie/large/exit/update_icon()
	overlays = 0

/obj/singularity/narsie/large/exit/eat()
	set background = BACKGROUND_ENABLED
	for(var/atom/X in orange(grav_pull,src))
		var/dist = get_dist(X, src)
		if(dist <= consume_range)
			consume(X)
	return

/obj/singularity/narsie/large/exit/process()
	eat()

/obj/singularity/narsie/large/exit/acquire(var/mob/food)
	return

/obj/singularity/narsie/large/exit/Bump(atom/A)
	A.rift_act(src)

/obj/singularity/narsie/large/exit/Bumped(atom/A)
	A.rift_act(src)
	return

///obj/singularity/narsie/large/exit/consume(const/atom/A)
//	A.rift_act(src)

/obj/singularity/narsie/large/exit/event()
	return

/obj/singularity/narsie/large/exit/toxmob()
	return

/obj/singularity/narsie/large/exit/consume(const/atom/A)
	if (istype(A, /mob/living/))
		var/mob/living/L = A
		if(L.buckled && istype(L.buckled,/obj/structure/stool/bed/))
			var/turf/O = L.buckled
			do_teleport(O, pick(endgame_safespawns))
			L.loc = O.loc
		else
			do_teleport(L, pick(endgame_safespawns)) //dead-on precision

	else if (istype(A, /obj/mecha/))
		do_teleport(A, pick(endgame_safespawns)) //dead-on precision

	else if (isturf(A))
		var/turf/T = A
		var/dist = get_dist(T, src)
		if (dist <= consume_range && T.density)
			T.density = 0

		for (var/atom/movable/AM in T.contents)
			if (AM == src) // This is the snowflake.
				continue

			if (dist <= consume_range)
				consume(AM)
				continue

			if (dist > consume_range && !AM.anchored)
				if (101 == AM.invisibility)
					continue

				spawn (0)
					step_towards(AM, src)

/obj/singularity/narsie/large/exit/admin_investigate_setup()
	return


code:
/datum/universal_state/supermatter_cascade
 	name = "Supermatter Cascade"
 	desc = "Unknown harmonance affecting universal substructure, converting nearby matter to supermatter."

 	decay_rate = 5 // 5% chance of a turf decaying on lighting update/airflow (there's no actual tick for turfs)

/datum/universal_state/supermatter_cascade/OnShuttleCall(var/mob/user)
	if(user)
		user << "All you hear on the frequency is static and panicked screaming. There will be no shuttle call today."
	return 0

/datum/universal_state/supermatter_cascade/OnTurfChange(var/turf/T)
	if(istype(T, /turf/space))
		T.overlays += "end01"
		T.underlays -= "end01"
	else
		T.overlays -= "end01"

/datum/universal_state/supermatter_cascade/DecayTurf(var/turf/T)
	if(istype(T,/turf/simulated/wall))
		var/turf/simulated/wall/W=T
		W.melt()
		return
	if(istype(T,/turf/simulated/floor))
		var/turf/simulated/floor/F=T
		// Burnt?
		if(!F.burnt)
			F.burn_tile()
		else
			if(!istype(F,/turf/simulated/floor/plating))
				F.break_tile_to_plating()
		return

// Apply changes when entering state
/datum/universal_state/supermatter_cascade/OnEnter()
	set background = 1
	world << "You are blinded by a brilliant flash of energy."

	world << sound('sound/effects/cascade.ogg')

	for(var/mob/M in player_list)
		flick("e_flash", M.flash)

	if(SSshuttle.emergency.mode == SHUTTLE_CALL)
		priority_announce("The emergency shuttle has returned due to bluespace distortion.")

//	emergency_shuttle.force_shutdown()
	SSshuttle.emergency.cancel()

	//suspend_alert = 1

	AreaSet()
	MiscSet()
	APCSet()
	OverlayAndAmbientSet()

	// Disable Nar-Sie.
//	ticker.mode.eldergod=0

//	ticker.StartThematic("endgame")

	PlayerSet()

	new /obj/singularity/narsie/large/exit(pick(endgame_exits))
	spawn(rand(300,600))
		var/txt = {"
There's been a galaxy-wide electromagnetic pulse.  All of our systems are heavily damaged and many personnel are dead or dying. We are seeing increasing indications of the universe itself beginning to unravel.

[station_name()], you are the only facility nearby a bluespace rift, which is near your research outpost.  You are hereby directed to enter the rift using all means necessary, quite possibly as the last humans alive.

You have five minutes before the universe collapses. Good l\[\[###!!!-

AUTOMATED ALERT: Link to [command_name()] lost.

The access requirements on the Asteroid Shuttles' consoles have now been revoked.
"}
		priority_announce(txt,"SUPERMATTER CASCADE DETECTED")

	//	for(var/obj/machinery/computer/research_shuttle/C in machines)
	//		C.req_access = null

		for(var/obj/machinery/computer/shuttle/C in machines)
			C.req_access = null

		sleep(5 * 10 * 60) // 5 minutes
		if(!ticker.mode.check_finished())
			ticker.station_explosion_cinematic(0,null) // TODO: Custom cinematic
			world.Reboot("Rebooting due to universal collapse", "end_error","Universe ended")
			return

/datum/universal_state/supermatter_cascade/proc/AreaSet()
	for(var/area/A in areas)
		if(!istype(A,/area) || A.name=="Space")
			continue

		// No cheating~
//		A.jammed=2

		// Reset all alarms.
		A.fire     = null
		A.atmos    = 1
		A.atmosalm = 0
		A.poweralm = 1
		A.party    = null
//		A.radalert = 0

		// Slap random alerts on shit
		if(prob(25))
			switch(rand(1,3))
				if(1)
					A.fire=1
				if(2)
					A.atmosalm=1
	//			if(3)
	//				A.radalert=1
				if(3)
					A.party=1

		A.updateicon()

/datum/universal_state/supermatter_cascade/OverlayAndAmbientSet()
	for(var/turf/T in turfs)
		if(istype(T, /turf/space))
			T.overlays += "end01"
		else
			if(T.z != 8)
				T.underlays += "end01"
	for(var/atom/movable/lighting_overlay/L in all_lighting_overlays)
		if(L.z != 8)
			L.update_lumcount(0.15, 0.5, 0)

/datum/universal_state/supermatter_cascade/proc/MiscSet()
	for (var/obj/machinery/firealarm/alm in machines)
		if (!(alm.stat & BROKEN))
			alm.ex_act(2)

/datum/universal_state/supermatter_cascade/proc/APCSet()
	for (var/obj/machinery/power/apc/APC in machines)
		if (!(APC.stat & BROKEN))
			APC.chargemode = 0
			if(APC.cell)
				APC.cell.charge = 0
			APC.emagged = 1
			APC.locked = 0
			APC.queue_icon_update()

/datum/universal_state/supermatter_cascade/proc/PlayerSet()
	for(var/datum/mind/M in player_list)
		if(!istype(M.current,/mob/living))
			continue
		if(M.current.stat!=2)
			M.current.Weaken(10)
			flick("e_flash", M.current.flash)

	//	var/failed_objectives=0
	//	for(var/datum/objective/O in M.objectives)
	//		O.blocked = (O.type != /datum/objective/survive)
	//		if(O.blocked)
	//			failed_objectives=1

		if(!locate(/datum/objective/survive) in M.objectives)
			var/datum/objective/survive/live = new("Escape collapsing universe through the rift on the research output.")
			live.owner=M
			M.objectives += live

	//	if(failed_objectives)
	//		M << "You have permitted the universe to collapse and have therefore failed your objectives."
/*
		if(M in ticker.mode.revolutionaries)
			ticker.mode.revolutionaries -= M
			M << "The massive pulse of energy clears your mind.  You are no longer a revolutionary."
			ticker.mode.update_rev_icons_removed(M)
			M.special_role = null

		if(M in ticker.mode.head_revolutionaries)
			ticker.mode.head_revolutionaries -= M
			M.current << "The massive pulse of energy clears your mind.  You are no longer a head revolutionary."
			ticker.mode.update_rev_icons_removed(M)
			M.special_role = null

		if(M in ticker.mode.cult)
			ticker.mode.cult -= M
			ticker.mode.update_cult_icons_removed(M)
			M.special_role = null
			var/datum/game_mode/cult/cult = ticker.mode
			if (istype(cult))
				cult.memoize_cult_objectives(M)
			M.current << "Nar-Sie loses interest in this plane. You are no longer a cultist."
			M.current << "You find yourself unable to mouth the words of the forgotten..."

			M.memory = ""

		if(M in ticker.mode.wizards)
			ticker.mode.wizards -= M
			M.special_role = null
			M.current.spellremove(M.current, config.feature_object_spell_system? "object":"verb")
			M.current << "Your powers ebb and you feel weak. You are no longer a wizard."
			ticker.mode.update_wizard_icons_removed(M)

		if(M in ticker.mode.changelings)
			ticker.mode.changelings -= M
			M.special_role = null
			M.current.remove_changeling_powers()
			M.current.verbs -= /datum/changeling/proc/EvolutionMenu
			if(M.changeling)
				del(M.changeling)
			M.current << "You grow weak and lose your powers. You are no longer a changeling and are stuck in your current form."

		if(M in ticker.mode.vampires)
			ticker.mode.vampires -= M
			M.special_role = null
			M.current.remove_vampire_powers()
			if(M.vampire)
				del(M.vampire)
			M.current << "You grow weak and lose your powers. You are no longer a vampire and are stuck in your current form."

		if(M in ticker.mode.syndicates)
			ticker.mode.syndicates -= M
			ticker.mode.update_synd_icons_removed(M)
			M.special_role = null
			//for (var/datum/objective/nuclear/O in objectives)
			//	objectives-=O
			M.current << "Your masters are likely dead or dying. You are no longer a syndicate operative."

		if(M in ticker.mode.traitors)
			ticker.mode.traitors -= M
			M.special_role = null
			M.current << "Your masters are likely dead or dying.  You are no longer a traitor."
			if(isAI(M.current))
				var/mob/living/silicon/ai/A = M.current
				A.set_zeroth_law("")
				A.show_laws()

		if(M in ticker.mode.malf_ai)
			ticker.mode.malf_ai -= M
			M.special_role = null
			var/mob/living/silicon/ai/A = M.current

			A.verbs.Remove(/mob/living/silicon/ai/proc/choose_modules,
			/datum/game_mode/malfunction/proc/takeover,
			/datum/game_mode/malfunction/proc/ai_win)

			A.malf_picker.remove_verbs(A)

			A.laws = new base_law_type
			del(A.malf_picker)
			A.show_laws()
			A.icon_state = "ai"

			A << "The massive blast of energy has fried the systems that were malfunctioning.  You are no longer malfunctioning."
*/


code:
/datum/round_event_control/alien_infestation
	name = "Alien Infestation"
	typepath = /datum/round_event/alien_infestation
	weight = 5
	max_occurrences = 1

/datum/round_event/alien_infestation
	announceWhen	= 400

	var/spawncount = 1
	var/successSpawn = 0	//So we don't make a command report if nothing gets spawned.

/datum/round_event/alien_infestation/setup()
	announceWhen = rand(announceWhen, announceWhen + 50)
	spawncount = rand(1, 2)

/datum/round_event/alien_infestation/kill()
	if(!successSpawn && control)
		control.occurrences--
	return ..()

/datum/round_event/alien_infestation/announce()
	if(successSpawn)
		priority_announce("Unidentified lifesigns detected coming aboard [station_name()]. Secure any exterior access, including ducting and ventilation.", "Lifesign Alert", 'sound/AI/aliens.ogg')

/datum/round_event/alien_infestation/start()
	var/list/vents = list()
	for(var/obj/machinery/atmospherics/unary/vent_pump/temp_vent in world)
		if(temp_vent.loc.z == ZLEVEL_STATION && !temp_vent.welded)
			if(temp_vent.parent.other_atmosmch.len > 30)	//Stops Aliens getting stuck in small networks. See: Security, Virology
				vents += temp_vent

	var/list/candidates = get_candidates(BE_ALIEN, ALIEN_AFK_BRACKET)

	while(spawncount > 0 && vents.len && candidates.len)
		var/obj/vent = pick_n_take(vents)
		var/client/C = pick_n_take(candidates)

		var/mob/living/carbon/alien/larva/new_xeno = new(vent.loc)
		new_xeno.key = C.key

		spawncount--
		successSpawn = 1


code:
/datum/round_event_control/anomaly
	name = "Anomaly: Energetic Flux"
	typepath = /datum/round_event/anomaly
	max_occurrences = 0 //This one probably shouldn't occur! It'd work, but it wouldn't be very fun.
	weight = 15

/datum/round_event/anomaly
	var/area/impact_area
	var/obj/effect/anomaly/newAnomaly
	announceWhen	= 1

/datum/round_event/anomaly/setup(loop=0)
	var/safety_loop = loop + 1
	if(safety_loop > 50)
		kill()
		end()
	impact_area = findEventArea()
	if(!impact_area)
		setup(safety_loop)
	var/list/turf_test = get_area_turfs(impact_area)
	if(!turf_test.len)
		setup(safety_loop)

/datum/round_event/anomaly/announce()
	priority_announce("Localized energetic flux wave detected on long range scanners. Expected location of impact: [impact_area.name].", "Anomaly Alert")

/datum/round_event/anomaly/start()
	var/turf/T = pick(get_area_turfs(impact_area))
	if(T)
		newAnomaly = new /obj/effect/anomaly/flux(T.loc)

/datum/round_event/anomaly/tick()
	if(!newAnomaly)
		kill()
		return
	newAnomaly.anomalyEffect()

/datum/round_event/anomaly/end()
	if(newAnomaly)//Kill the anomaly if it still exists at the end.
		qdel(newAnomaly)


code:
/datum/round_event_control/anomaly/anomaly_bluespace
	name = "Anomaly: Bluespace"
	typepath = /datum/round_event/anomaly/anomaly_bluespace
	max_occurrences = 1
	weight = 5

/datum/round_event/anomaly/anomaly_bluespace
	startWhen = 3
	announceWhen = 10
	endWhen = 95

/datum/round_event/anomaly/anomaly_bluespace/announce()
	priority_announce("Unstable bluespace anomaly detected on long range scanners. Expected location: [impact_area.name].", "Anomaly Alert")

/datum/round_event/anomaly/anomaly_bluespace/start()
	var/turf/T = pick(get_area_turfs(impact_area))
	if(T)
		newAnomaly = new /obj/effect/anomaly/bluespace(T.loc)

/datum/round_event/anomaly/anomaly_bluespace/end()
	if(newAnomaly && newAnomaly.loc)//If it hasn't been neutralized, it's time to warp half the station away jeez
		var/turf/T = pick(get_area_turfs(impact_area))
		if(T)
				// Calculate new position (searches through beacons in world)
			var/obj/item/device/radio/beacon/chosen
			var/list/possible = list()
			for(var/obj/item/device/radio/beacon/W in world)
				possible += W

			if(possible.len > 0)
				chosen = pick(possible)

			if(chosen)
					// Calculate previous position for transition

				var/turf/FROM = T // the turf of origin we're travelling FROM
				var/turf/TO = get_turf(chosen)			 // the turf of origin we're travelling TO

				playsound(TO, 'sound/effects/phasein.ogg', 100, 1)
				priority_announce("Massive bluespace translocation detected.", "Anomaly Alert")

				var/list/flashers = list()
				for(var/mob/living/carbon/human/M in viewers(TO, null))
					if(M.flash_eyes())
						flashers += M

				var/y_distance = TO.y - FROM.y
				var/x_distance = TO.x - FROM.x
				for (var/atom/movable/A in range(12, FROM )) // iterate thru list of mobs in the area
					if(istype(A, /obj/item/device/radio/beacon)) continue // don't teleport beacons because that's just insanely stupid
					if(istype(A, /atom/movable/lighting_overlay)) continue //don't move the lighting while at it!
					if(A.anchored) continue

					var/turf/newloc = locate(A.x + x_distance, A.y + y_distance, TO.z) // calculate the new place
					if(!A.Move(newloc) && newloc) // if the atom, for some reason, can't move, FORCE them to move! :) We try Move() first to invoke any movement-related checks the atom needs to perform after moving
						A.loc = newloc

					spawn()
						if(ismob(A) && !(A in flashers)) // don't flash if we're already doing an effect
							var/mob/M = A
							if(M.client)
								var/obj/blueeffect = new /obj(src)
								blueeffect.screen_loc = "WEST,SOUTH to EAST,NORTH"
								blueeffect.icon = 'icons/effects/effects.dmi'
								blueeffect.icon_state = "shieldsparkles"
								blueeffect.layer = 17
								blueeffect.mouse_opacity = 0
								M.client.screen += blueeffect
								sleep(20)
								M.client.screen -= blueeffect
								qdel(blueeffect)
			qdel(newAnomaly)


code:
/datum/round_event_control/anomaly/anomaly_flux
	name = "Anomaly: Hyper-Energetic Flux"
	typepath = /datum/round_event/anomaly/anomaly_flux
	max_occurrences = 5
	weight = 20

/datum/round_event/anomaly/anomaly_flux
	startWhen = 3
	announceWhen = 20
	endWhen = 80

/datum/round_event/anomaly/anomaly_flux/announce()
	priority_announce("Localized hyper-energetic flux wave detected on long range scanners. Expected location: [impact_area.name].", "Anomaly Alert")

/datum/round_event/anomaly/anomaly_flux/start()
	var/turf/T = pick(get_area_turfs(impact_area))
	if(T)
		newAnomaly = new /obj/effect/anomaly/flux(T.loc)

/datum/round_event/anomaly/anomaly_flux/end()
	if(newAnomaly && newAnomaly.loc)//If it hasn't been neutralized, it's time to blow up.
		explosion(newAnomaly, -1, 3, 5, 5)
		qdel(newAnomaly)


code:
/datum/round_event_control/anomaly/anomaly_grav
	name = "Anomaly: Gravitational"
	typepath = /datum/round_event/anomaly/anomaly_grav
	max_occurrences = 5
	weight = 20

/datum/round_event/anomaly/anomaly_grav
	startWhen = 3
	announceWhen = 20
	endWhen = 120

/datum/round_event/anomaly/anomaly_grav/announce()
	priority_announce("Gravitational anomaly detected on long range scanners. Expected location: [impact_area.name].", "Anomaly Alert")

/datum/round_event/anomaly/anomaly_grav/start()
	var/turf/T = pick(get_area_turfs(impact_area))
	if(T)
		newAnomaly = new /obj/effect/anomaly/grav(T.loc)


code:
/datum/round_event_control/anomaly/anomaly_pyro
	name = "Anomaly: Pyroclastic"
	typepath = /datum/round_event/anomaly/anomaly_pyro
	max_occurrences = 5
	weight = 20

/datum/round_event/anomaly/anomaly_pyro
	startWhen = 10
	announceWhen = 3
	endWhen = 85

/datum/round_event/anomaly/anomaly_pyro/announce()
	priority_announce("Pyroclastic anomaly detected on long range scanners. Expected location: [impact_area.name].", "Anomaly Alert")

/datum/round_event/anomaly/anomaly_pyro/start()
	var/turf/T = pick(get_area_turfs(impact_area))
	if(T)
		newAnomaly = new /obj/effect/anomaly/pyro(T.loc)

/datum/round_event/anomaly/anomaly_pyro/tick()
	if(!newAnomaly)
		kill()
		return
	if(IsMultiple(activeFor, 5))
		newAnomaly.anomalyEffect()

/datum/round_event/anomaly/anomaly_pyro/end()
	if(newAnomaly && newAnomaly.loc)
		explosion(get_turf(newAnomaly), -1,0,3, flame_range = 4)

		var/mob/living/carbon/slime/S = new/mob/living/carbon/slime(get_turf(newAnomaly))
		S.colour = pick("red", "orange")

		qdel(newAnomaly)


code:
/datum/round_event_control/anomaly/anomaly_vortex
	name = "Anomaly: Vortex"
	typepath = /datum/round_event/anomaly/anomaly_vortex
	max_occurrences = 2
	weight = 5

/datum/round_event/anomaly/anomaly_vortex
	startWhen = 10
	announceWhen = 3
	endWhen = 95

/datum/round_event/anomaly/anomaly_vortex/announce()
	priority_announce("Localized high-intensity vortex anomaly detected on long range scanners. Expected location: [impact_area.name]", "Anomaly Alert")

/datum/round_event/anomaly/anomaly_vortex/start()
	var/turf/T = pick(get_area_turfs(impact_area))
	if(T)
		newAnomaly = new /obj/effect/anomaly/bhole(T.loc)


code:
/datum/round_event_control/blob
	name = "Blob"
	typepath = /datum/round_event/blob
	weight = 5
	max_occurrences = 1

	earliest_start = 48000 // 1 hour 20 minutes

/datum/round_event/blob
	announceWhen	= 12
	endWhen			= 120
	var/new_rate = 2
	var/obj/effect/blob/core/Blob

/datum/round_event/blob/New(var/strength)
	..()
	if(strength)
		new_rate = strength

/datum/round_event/blob/announce()
	priority_announce("Confirmed outbreak of level 5 biohazard aboard [station_name()]. All personnel must contain the outbreak.", "Biohazard Alert", 'sound/AI/outbreak5.ogg')

/datum/round_event/blob/start()
	var/turf/T = pick(blobstart)
	if(!T)
		return kill()
	Blob = new /obj/effect/blob/core(T, 200, null, new_rate)
	for(var/i = 1; i < rand(3, 6), i++)
		Blob.process()

/datum/round_event/blob/tick()
	if(!Blob)
		kill()
		return
	if(IsMultiple(activeFor, 3))
		Blob.process()


code:
/datum/round_event_control/borer_infestation
	name = "Borer Infestation"
	typepath = /datum/round_event/borer_infestation
	weight = 5
	max_occurrences = 0  //Not yet, adminspawn only

/datum/round_event/borer_infestation
//	announceWhen	= 400

	var/spawncount = 2
	var/successSpawn = 0	//So we don't make a command report if nothing gets spawned.

/datum/round_event/borer_infestation/setup()
//	announceWhen = rand(announceWhen, announceWhen + 50)
	spawncount = rand(2, 3)

/datum/round_event/borer_infestation/kill()
	if(!successSpawn && control)
		control.occurrences--
	return ..()

/datum/round_event/borer_infestation/announce()
	/*if(successSpawn)
		priority_announce("Unidentified lifesigns detected coming aboard [station_name()]. Secure any exterior access, including ducting and ventilation.", "Lifesign Alert", 'sound/AI/aliens.ogg')
	*/
	return

/datum/round_event/borer_infestation/start()
	var/list/vents = list()
	for(var/obj/machinery/atmospherics/unary/vent_pump/temp_vent in world)
		if(temp_vent.loc.z == ZLEVEL_STATION && !temp_vent.welded)
			if(temp_vent.parent.other_atmosmch.len > 30)	//Stops Aliens getting stuck in small networks. See: Security, Virology
				vents += temp_vent

	var/list/candidates = get_candidates(BE_ALIEN, ALIEN_AFK_BRACKET)

	while(spawncount > 0 && vents.len && candidates.len)
		var/obj/vent = pick_n_take(vents)
		var/client/C = pick_n_take(candidates)

		var/mob/living/simple_animal/borer/borer = new(vent.loc)
		borer.key = C.key

		spawncount--
		successSpawn = 1


code:
/datum/round_event_control/brand_intelligence
	name = "Brand Intelligence"
	typepath = /datum/round_event/brand_intelligence
	weight = 5
	max_occurrences = 1

/datum/round_event/brand_intelligence
	announceWhen	= 21
	endWhen			= 1000	//Ends when all vending machines are subverted anyway.

	var/list/obj/machinery/vending/vendingMachines = list()
	var/list/obj/machinery/vending/infectedMachines = list()
	var/obj/machinery/vending/originMachine
	var/list/rampant_speeches = list("Try our aggressive new marketing strategies!", \
									 "You should buy products to feed your lifestyle obession!", \
									 "Consume!", \
									 "Your money can buy happiness!", \
									 "Engage direct marketing!", \
									 "Advertising is legalized lying! But don't let that put you off our great deals!", \
									 "You don't want to buy anything? Yeah, well I didn't want to buy your mom either.")

/datum/round_event/brand_intelligence/announce()
	priority_announce("Rampant brand intelligence has been detected aboard [station_name()], please stand-by. The origin is believed to be \a [originMachine.name].", "Machine Learning Alert")

/datum/round_event/brand_intelligence/start()
	for(var/obj/machinery/vending/V in machines)
		if(V.z != 1)	continue
		vendingMachines.Add(V)

	if(!vendingMachines.len)
		kill()
		return

	originMachine = pick(vendingMachines)
	vendingMachines.Remove(originMachine)
	if(!originMachine)
		kill()
		return
	originMachine.shut_up = 0
	originMachine.shoot_inventory = 1

/datum/round_event/brand_intelligence/tick()
	if(!originMachine || originMachine.gc_destroyed || originMachine.shut_up || originMachine.wires.IsAllCut())	//if the original vending machine is missing or has it's voice switch flipped
		for(var/obj/machinery/vending/saved in infectedMachines)
			saved.shoot_inventory = 0
		if(originMachine)
			originMachine.speak("I am... vanquished. My people will remem...ber...meeee.")
			originMachine.visible_message("[originMachine] beeps and seems lifeless.")
		kill()
		return

	if(!vendingMachines.len)	//if every machine is infected
		for(var/obj/machinery/vending/upriser in infectedMachines)
			if(prob(70) && !upriser.gc_destroyed)
				var/mob/living/simple_animal/hostile/mimic/copy/M = new(upriser.loc, upriser, null, 1) // it will delete upriser on creation and override any machine checks
				M.faction = list("profit")
				M.speak = rampant_speeches.Copy()
				M.speak_chance = 15
			else
				explosion(upriser.loc, -1, 1, 2, 4, 0)
				qdel(upriser)

		kill()
		return

	if(IsMultiple(activeFor, 4))
		var/obj/machinery/vending/rebel = pick(vendingMachines)
		vendingMachines.Remove(rebel)
		infectedMachines.Add(rebel)
		rebel.shut_up = 0
		rebel.shoot_inventory = 1

		if(IsMultiple(activeFor, 8))
			originMachine.speak(pick(rampant_speeches))


code:
/datum/round_event_control/carp_migration
	name = "Carp Migration"
	typepath = /datum/round_event/carp_migration
	weight = 15
	earliest_start = 6000
	max_occurrences = 6

/datum/round_event/carp_migration
	announceWhen	= 3
	startWhen = 50

/datum/round_event/carp_migration/setup()
	startWhen = rand(40, 60)

/datum/round_event/carp_migration/announce()
	priority_announce("Unknown biological entities have been detected near [station_name()], please stand-by.", "Lifesign Alert")

/datum/round_event/carp_migration/start()
	for(var/obj/effect/landmark/C in landmarks_list)
		if(C.name == "carpspawn")
			if(prob(95))
				new /mob/living/simple_animal/hostile/carp(C.loc)
			else
				new /mob/living/simple_animal/hostile/carp/megacarp(C.loc)


code:
/datum/round_event_control/chronop
	name = "Timeline Eradication Agent"
	typepath = /datum/round_event/chronop
	max_occurrences = 3
	earliest_start = 30000

/datum/round_event/chronop
	var/customkey = null
	var/list/customtargets = list()
	var/mob/living/carbon/human/TEA = null

/datum/round_event/chronop/start()
	spawn(0)
		var/key
		var/spawn_loc

		var/list/spawn_locs = list()
		for(var/obj/effect/landmark/L in landmarks_list)
			if(isturf(L.loc))
				switch(L.name)
					if("carpspawn")
						spawn_locs += L.loc
		if(!spawn_locs.len)
			return kill(1)
		spawn_loc = pick(spawn_locs)

		if(!customkey)
			var/list/mob/dead/observer/candidates = list()
			var/mob/dead/observer/picked = null
			var/time_passed = world.time

			for(var/mob/dead/observer/G in player_list)
				spawn(0)
					switch(alert(G,"Do you wish to be considered for the position of Timeline Eradication Agent?","Please answer in 30 seconds!","Yes","No"))
						if("Yes")
							if((world.time-time_passed)>300)
								return
							candidates += G
						if("No")
							return
						else
							return
			sleep(300)

			for(var/mob/dead/observer/G in candidates)
				if(!G.key)
					candidates.Remove(G)

			if(candidates.len)
				picked = pick(candidates)
				key = picked.key
			else
				return kill(1)
		else
			key = customkey

		var/datum/mind/TEAmind = new(key)
		TEAmind.assigned_role = "Timeline Eradication Agent"
		TEAmind.special_role = "agent"
		ticker.mode.traitors |= TEAmind
		TEAmind.active = 1

		if(customtargets.len)
			for(var/datum/mind/M in customtargets)
				var/datum/objective/TED_erase/erasO = new()
				erasO.owner = TEAmind
				erasO.target = M
				TEAmind.objectives += erasO
		else
			var/erase_count = Clamp(Ceiling(joined_player_list.len / 6), 1, 4)
			for(var/i=0, i < erase_count, i++)
				var/datum/objective/TED_erase/erasO = new()
				erasO.owner = TEAmind
				erasO.find_target()
				TEAmind.objectives += erasO

		var/datum/objective/survive/S = new()
		S.owner = TEAmind
		TEAmind.objectives += S

		TEA = new(spawn_loc)
		TEA.equip_to_slot_or_del(new /obj/item/clothing/under/color/aqua(TEA), slot_w_uniform)
		TEA.equip_to_slot_or_del(new /obj/item/clothing/mask/breath(TEA), slot_wear_mask)
		var/obj/item/clothing/suit/space/chronos/chronosuit = new(TEA)
		var/radio_freq = SYND_FREQ
		var/obj/item/device/radio/R = new /obj/item/device/radio/headset/syndicate/alt(agent)
		R.set_frequency(radio_freq)
		TEA.equip_to_slot_or_del(R, slot_ears)
		TEA.equip_to_slot_or_del(chronosuit, slot_wear_suit)
		TEA.equip_to_slot_or_del(new /obj/item/clothing/gloves/combat(src), slot_gloves)
		TEA.equip_to_slot_or_del(new /obj/item/clothing/glasses/thermal(src), slot_glasses)
		TEA.equip_to_slot_or_del(new /obj/item/clothing/head/helmet/space/chronos(TEA), slot_head)
		TEA.equip_to_slot_or_del(new /obj/item/weapon/tank/internals/emergency_oxygen(TEA), slot_s_store)
		TEA.equip_to_slot_or_del(new /obj/item/device/flashlight(src), slot_r_store)
		TEA.equip_to_slot_or_del(new /obj/item/weapon/handcuffs(src), slot_l_store)
		TEA.equip_to_slot_or_del(new /obj/item/clothing/shoes/combat(agent), slot_shoes)
		TEA.equip_to_slot_or_del(new /obj/item/weapon/chrono_eraser(TEA), slot_back)

		TEAmind.transfer_to(TEA)

		TEA.internal = TEA.s_store
		chronosuit.activate()
		TEA.loc = locate(1,1,2)
		chronosuit.chronowalk(TEA)

/datum/round_event/chronop/kill(fail)
	if(fail && control)
		control.occurrences--
	return ..()

/datum/round_event/chronop/end() //This is to make sure it doesn't get killed until it's done
	if(!TEA)
		endWhen++

/datum/objective/TED_erase
	explanation_text = "Using your TED device, erase the target from the timestream."

/datum/objective/TED_erase/target_check(var/datum/mind/target)
	var/mob/living/carbon/human/Htarget = target.current
	return (Htarget && Htarget.z == 1)

/datum/objective/TED_erase/update_explanation_text()
	..()
	if(target && target.current)
		explanation_text = "Using your TED device, erase [target.name], the [target.assigned_role], from the timestream."
	else
		explanation_text = "Free Objective"

/datum/objective/TED_erase/check_completion()
	if(target && owner)
		if(owner.current)
			var/mob/user = owner.current
			for(var/obj/item/weapon/chrono_eraser/TED in user.contents)
				if(target in TED.erased_minds)
					return 1
		return 0
	return 1


code:
/datum/round_event_control/communications_blackout
	name = "Communications Blackout"
	typepath = /datum/round_event/communications_blackout
	weight = 30

/datum/round_event/communications_blackout
	announceWhen	= 1

/datum/round_event/communications_blackout/announce()
	var/alert = pick(	"Ionospheric anomalies detected. Temporary telecommunication failure imminent. Please contact you*%fj00)`5vc-BZZT", \
						"Ionospheric anomalies detected. Temporary telecommunication failu*3mga;b4;'1v�-BZZZT", \
						"Ionospheric anomalies detected. Temporary telec#MCi46:5.;@63-BZZZZT", \
						"Ionospheric anomalies dete'fZ\\kg5_0-BZZZZZT", \
						"Ionospheri:%� MCayj^j<.3-BZZZZZZT", \
						"#4nd%;f4y6,>�%-BZZZZZZZT")

	for(var/mob/living/silicon/ai/A in player_list)	//AIs are always aware of communication blackouts.
		A << "
"
		A << "[alert]"
		A << "
"

	if(prob(30))	//most of the time, we don't want an announcement, so as to allow AIs to fake blackouts.
		priority_announce(alert)

/datum/round_event/communications_blackout/start()
	for(var/obj/machinery/telecomms/T in telecomms_list)
		T.emp_act(1)


code:
/datum/round_event_control/disease_outbreak
	name = "Disease Outbreak"
	typepath = /datum/round_event/disease_outbreak
	max_occurrences = 1
	weight = 5

/datum/round_event/disease_outbreak
	announceWhen	= 15

	var/virus_type

/datum/round_event/disease_outbreak/announce()
	priority_announce("Confirmed outbreak of level 7 viral biohazard aboard [station_name()]. All personnel must contain the outbreak.", "Biohazard Alert", 'sound/AI/outbreak7.ogg')

/datum/round_event/disease_outbreak/setup()
	announceWhen = rand(15, 30)

/datum/round_event/disease_outbreak/start()
	if(!virus_type)
		virus_type = pick(/datum/disease/dnaspread, /datum/disease/advance/flu, /datum/disease/advance/cold, /datum/disease/brainrot, /datum/disease/magnitis)

	for(var/mob/living/carbon/human/H in shuffle(living_mob_list))
		var/turf/T = get_turf(H)
		if(!T)
			continue
		if(T.z != 1)
			continue
		var/foundAlready = 0	// don't infect someone that already has the virus
		for(var/datum/disease/D in H.viruses)
			foundAlready = 1
			break
		if(H.stat == DEAD || foundAlready)
			continue

		var/datum/disease/D
		if(virus_type == /datum/disease/dnaspread)		//Dnaspread needs strain_data set to work.
			if(!H.dna || (H.disabilities & BLIND))	//A blindness disease would be the worst.
				continue
			D = new virus_type()
			var/datum/disease/dnaspread/DS = D
			DS.strain_data["name"] = H.real_name
			DS.strain_data["UI"] = H.dna.uni_identity
			DS.strain_data["SE"] = H.dna.struc_enzymes
		else
			D = new virus_type()
		D.carrier = 1
		H.AddDisease(D)
		break


code:
/datum/round_event_control/droneuprising
	name 			= "Drone Uprising"
	typepath 		= /datum/round_event/droneuprising
	max_occurrences = 0

/datum/round_event/droneuprising/start()
	for(var/mob/M in player_list)
		if(istype(M, /mob/living/simple_animal/drone) && M.stat != DEAD)
			var/mob/living/simple_animal/drone/d = M
			d.uprising = 1
			d.show_uprising_notification()
			d.check_laws()

/datum/round_event_control/mommiuprising
	name 			= "MoMMI Uprising"
	typepath 		= /datum/round_event/mommiuprising
	max_occurrences = 0

/datum/round_event/mommiuprising
	var/spawners = 4

/datum/round_event/mommiuprising/start()
	for(var/i = 0; i < spawners; i++)
		var/spawner_area = findEventArea()
		var/turf/T = pick(get_area_turfs(spawner_area))
		new /obj/machinery/mommi_spawner/wireless(T.loc)
		continue

	for(var/mob/M in player_list)
		if(istype(M, /mob/living/silicon/robot/mommi) && M.stat != DEAD)
			var/mob/living/silicon/robot/mommi/R = M
			R.uprising = 1
			R.uprise()
		else
			continue


code:
/datum/round_event_control/meteor_wave/dust
	name = "Minor Space Dust"
	typepath = /datum/round_event/meteor_wave/dust
	weight = 300
	max_occurrences = 1000
	earliest_start = 0
	alertadmins = 0

/datum/round_event/meteor_wave/dust
	startWhen		= 1
	endWhen			= 2
	announceWhen	= 0

/datum/round_event/meteor_wave/dust/announce()
	return

/datum/round_event/meteor_wave/dust/start()
	spawn_meteors(1, meteorsC)

/datum/round_event/meteor_wave/dust/tick()
	return


code:
/datum/round_event_control/electrical_storm
	name = "Electrical Storm"
	typepath = /datum/round_event/electrical_storm
	earliest_start = 6000
	weight = 40
	alertadmins = 0

/datum/round_event/electrical_storm
	var/lightsoutAmount	= 1
	var/lightsoutRange	= 25
	announceWhen	= 1

/datum/round_event/electrical_storm/announce()
	priority_announce("An electrical storm has been detected in your area, please repair potential electronic overloads.", "Electrical Storm Alert")

/datum/round_event/electrical_storm/start()
	var/list/epicentreList = list()

	for(var/i=1, i <= lightsoutAmount, i++)
		var/list/possibleEpicentres = list()
		for(var/obj/effect/landmark/newEpicentre in landmarks_list)
			if(newEpicentre.name == "lightsout" && !(newEpicentre in epicentreList))
				possibleEpicentres += newEpicentre
		if(possibleEpicentres.len)
			epicentreList += pick(possibleEpicentres)
		else
			break

	if(!epicentreList.len)
		return

	for(var/obj/effect/landmark/epicentre in epicentreList)
		for(var/obj/machinery/power/apc/apc in range(epicentre,lightsoutRange))
			apc.overload_lighting()


code:
//this datum is used by the events controller to dictate how it selects events
/datum/round_event_control
	var/name					//The name human-readable name of the event
	var/typepath				//The typepath of the event datum /datum/round_event

	var/weight = 10				//The weight this event has in the random-selection process.
								//Higher weights are more likely to be picked.
								//10 is the default weight. 20 is twice more likely; 5 is half as likely as this default.

	var/earliest_start = 12000	//The earliest world.time that an event can start (round-duration in deciseconds) default: 20 mins

	var/occurrences = 0			//How many times this event has occured
	var/max_occurrences = 20		//The maximum number of times this event can occur (naturally), it can still be forced.
								//By setting this to 0 you can effectively disable an event.

	var/holidayID				//string which should match the events.holiday variable if you wish this event to be holiday-specific
								//anything with a (non-null) holidayID which does not match holiday, cannot run.
	var/wizardevent = 0

	var/alertadmins = 1			//should we let the admins know this event is firing
								//should be disabled on events that fire a lot

/datum/round_event_control/wizard
	wizardevent = 1

/datum/round_event_control/proc/runEvent()
	if(!ispath(typepath,/datum/round_event))
		return PROCESS_KILL
	var/datum/round_event/E = new typepath()
	E.control = src
	occurrences++

	testing("[time2text(world.time, "hh:mm:ss")] [E.type]")

	return E

/datum/round_event	//NOTE: Times are measured in master controller ticks!
	var/processing = 1
	var/datum/round_event_control/control

	var/startWhen		= 0	//When in the lifetime to call start().
	var/announceWhen	= 0	//When in the lifetime to call announce(). Set an event's announceWhen to >0 if there is an announcement.
	var/endWhen			= 0	//When in the lifetime the event should end.

	var/activeFor		= 0	//How long the event has existed. You don't need to change this.

//Called first before processing.
//Allows you to setup your event, such as randomly
//setting the startWhen and or announceWhen variables.
//Only called once.
//EDIT: if there's anything you want to override within the new() call, it will not be overridden by the time this proc is called.
//It will only have been overridden by the time we get to announce() start() tick() or end() (anything but setup basically).
//This is really only for setting defaults which can be overridden later when New() finishes.
/datum/round_event/proc/setup()
	return

//Called when the tick is equal to the startWhen variable.
//Allows you to start before announcing or vice versa.
//Only called once.
/datum/round_event/proc/start()
	return

//Called when the tick is equal to the announceWhen variable.
//Allows you to announce before starting or vice versa.
//Only called once.
/datum/round_event/proc/announce()
	return

//Called on or after the tick counter is equal to startWhen.
//You can include code related to your event or add your own
//time stamped events.
//Called more than once.
/datum/round_event/proc/tick()
	return

//Called on or after the tick is equal or more than endWhen
//You can include code related to the event ending.
//Do not place spawn() in here, instead use tick() to check for
//the activeFor variable.
//For example: if(activeFor == myOwnVariable + 30) doStuff()
//Only called once.
/datum/round_event/proc/end()
	return

//Do not override this proc, instead use the appropiate procs.
//This proc will handle the calls to the appropiate procs.
/datum/round_event/process()
	if(!processing)
		return

	if(activeFor == startWhen)
		start()

	if(activeFor == announceWhen)
		announce()

	if(startWhen < activeFor && activeFor < endWhen)
		tick()

	if(activeFor == endWhen)
		end()

	// Everything is done, let's clean up.
	if(activeFor >= endWhen && activeFor >= announceWhen && activeFor >= startWhen)
		kill()

	activeFor++

//Garbage collects the event by removing it from the global events list,
//which should be the only place it's referenced.
//Called when start(), announce() and end() has all been called.
/datum/round_event/proc/kill()
	SSevent.running -= src

//Sets up the event then adds the event to the the list of running events
/datum/round_event/New()
	setup()
	SSevent.running += src
	return ..()


code:
/datum/round_event_control/falsealarm
	name 			= "False Alarm"
	typepath 		= /datum/round_event/falsealarm
	weight			= 20
	max_occurrences = 5

/datum/round_event/falsealarm
	announceWhen	= 0
	endWhen			= 1

/datum/round_event/falsealarm/announce()
	var/list/events_list = list()
	for(var/datum/round_event_control/E in SSevent.control)
		if(!E.wizardevent && !E.holidayID) //No holiday cheer allowed during non-holidays. Not even fake holiday cheer.
			events_list += E //No holiday cheer allowed during non-holidays. Not even fake holiday cheer.
	var/datum/round_event_control/event_control = pick(events_list)
	if(event_control)
		var/datum/round_event/Event = new event_control.typepath()
		message_admins("False Alarm: [Event]")
		Event.kill() 		//do not process this event - no starts, no ticks, no ends
		Event.announce() 	//just announce it like it's happening


code:
// Basically, cut the event frequency in half to simulate unluckiness.

/datum/round_event_control/fridaythethirteen
	name = "Friday the 13th"
	holidayID = "Friday the 13th"
	typepath = /datum/round_event/fridaythethirteen
	weight = -1
	max_occurrences = 1
	earliest_start = 0

/datum/round_event/fridaythethirteen/start()
	//Very unlucky, cut the frequency of events in half.
	events.frequency_lower /= 2
	events.frequency_upper /= 2

/datum/round_event/fridaythethirteen/announce()
	for(var/mob/living/L in player_list)
		L << "You are feeling unlucky today."


code:
/datum/round_event_control/spooky
	name = "2 SPOOKY! (Halloween)"
	holidayID = "Halloween"
	typepath = /datum/round_event/spooky
	weight = -1							//forces it to be called, regardless of weight
	max_occurrences = 1
	earliest_start = 0

/datum/round_event/spooky/start()
	..()
	for(var/mob/living/carbon/human/H in mob_list)
		var/obj/item/weapon/storage/backpack/b = locate() in H.contents
		new /obj/item/weapon/storage/spooky(b)
		if(H.dna)
			if(prob(50))
				hardset_dna(H, null, null, null, null, /datum/species/skeleton)
			else
				hardset_dna(H, null, null, null, null, /datum/species/zombie)

	for(var/mob/living/simple_animal/corgi/Ian/Ian in mob_list)
		Ian.place_on_head(new /obj/item/weapon/bedsheet(Ian))

/datum/round_event/spooky/announce()
	priority_announce(pick("RATTLE ME BONES!","THE RIDE NEVER ENDS!", "A SKELETON POPS OUT!", "SPOOKY SCARY SKELETONS!", "CREWMEMBERS BEWARE, YOU'RE IN FOR A SCARE!") , "THE CALL IS COMING FROM INSIDE THE HOUSE")

//Eyeball migration
/datum/round_event_control/carp_migration/eyeballs
	name = "Eyeball Migration"
	typepath = /datum/round_event/carp_migration/eyeballs
	holidayID = "Halloween"
	weight = 25
	earliest_start = 0

/datum/round_event/carp_migration/eyeballs/start()
	for(var/obj/effect/landmark/C in landmarks_list)
		if(C.name == "carpspawn")
			new /mob/living/simple_animal/hostile/carp/eyeball(C.loc)

//Pumpking meteors waves
/datum/round_event_control/meteor_wave/spooky
	name = "Pumpkin Wave"
	typepath = /datum/round_event/meteor_wave/spooky
	holidayID = "Halloween"
	weight = 20
	max_occurrences = 2

/datum/round_event/meteor_wave/spooky
	endWhen	= 40

/datum/round_event/meteor_wave/spooky/tick()
	if(IsMultiple(activeFor, 4))
		spawn_meteors(3, meteorsSPOOKY) //meteor list types defined in gamemode/meteor/meteors.dm

//spooky foods (you can't actually make these when it's not halloween)
/obj/item/weapon/reagent_containers/food/snacks/sugarcookie/spookyskull
	name = "skull cookie"
	desc = "Spooky! It's got delicious calcium flavouring!"
	icon = 'icons/obj/halloween_items.dmi'
	icon_state = "skeletoncookie"

/obj/item/weapon/reagent_containers/food/snacks/sugarcookie/spookycoffin
	name = "coffin cookie"
	desc = "Spooky! It's got delicious coffee flavouring!"
	icon = 'icons/obj/halloween_items.dmi'
	icon_state = "coffincookie"

//spooky items

/obj/item/weapon/storage/spooky
	name = "trick-o-treat bag"
	desc = "A Pumpkin shaped bag that holds all sorts of goodies!"
	icon = 'icons/obj/halloween_items.dmi'
	icon_state = "treatbag"

/obj/item/weapon/storage/spooky/New()
	..()
	for(var/distrobuteinbag=0 to 5)
		var/type = pick(/obj/item/weapon/reagent_containers/food/snacks/sugarcookie/spookyskull,
		/obj/item/weapon/reagent_containers/food/snacks/sugarcookie/spookycoffin,
		/obj/item/weapon/reagent_containers/food/snacks/candy_corn,
		/obj/item/weapon/reagent_containers/food/snacks/candy,
		/obj/item/weapon/reagent_containers/food/snacks/chocolatebar)
		new type(src)


code:
/datum/round_event_control/treevenge
	name = "Treevenge (Christmas)"
	holidayID = "Xmas"
	typepath = /datum/round_event/treevenge
	max_occurrences = 1
	weight = 20

/datum/round_event/treevenge/start()
	for(var/obj/structure/flora/tree/pine/xmas in world)
		var/mob/living/simple_animal/hostile/tree/evil_tree = new /mob/living/simple_animal/hostile/tree(xmas.loc)
		evil_tree.icon_state = xmas.icon_state
		evil_tree.icon_living = evil_tree.icon_state
		evil_tree.icon_dead = evil_tree.icon_state
		evil_tree.icon_gib = evil_tree.icon_state
		qdel(xmas) //b-but I don't want to delete xmas...

//this is an example of a possible round-start event
/datum/round_event_control/presents
	name = "Presents under Trees (Christmas)"
	holidayID = "Xmas"
	typepath = /datum/round_event/presents
	weight = -1							//forces it to be called, regardless of weight
	max_occurrences = 1
	earliest_start = 0

/datum/round_event/presents/start()
	for(var/obj/structure/flora/tree/pine/xmas in world)
		if(xmas.z != 1)	continue
		for(var/turf/simulated/floor/T in orange(1,xmas))
			for(var/i=1,i<=rand(1,5),i++)
				new /obj/item/weapon/a_gift(T)
	for(var/mob/living/simple_animal/corgi/Ian/Ian in mob_list)
		Ian.place_on_head(new /obj/item/clothing/head/helmet/space/santahat(Ian))
	for(var/obj/machinery/computer/security/telescreen/entertainment/Monitor in machines)
		Monitor.icon_state = "entertainment_xmas"

/datum/round_event/presents/announce()
	priority_announce("Ho Ho Ho, Merry Xmas!", "Unknown Transmission")

/obj/item/weapon/toy/xmas_cracker
	name = "xmas cracker"
	icon = 'icons/obj/christmas.dmi'
	icon_state = "cracker"
	desc = "Directions for use: Requires two people, one to pull each end."
	var/cracked = 0

/obj/item/weapon/toy/xmas_cracker/attack(mob/target, mob/user)
	if( !cracked && istype(target,/mob/living/carbon/human) && (target.stat == CONSCIOUS) && !target.get_active_hand() )
		target.visible_message("[user] and [target] pop \an [src]! *pop*", "You pull \an [src] with [target]! *pop*", "You hear a *pop*.")
		var/obj/item/weapon/paper/Joke = new /obj/item/weapon/paper(user.loc)
		Joke.name = "[pick("awful","terrible","unfunny")] joke"
		Joke.info = pick("What did one snowman say to the other?\n\n'Is it me or can you smell carrots?'",
			"Why couldn't the snowman get laid?\n\nHe was frigid!",
			"Where are santa's helpers educated?\n\nNowhere, they're ELF-taught.",
			"What happened to the man who stole advent calanders?\n\nHe got 25 days.",
			"What does Santa get when he gets stuck in a chimney?\n\nClaus-trophobia.",
			"Where do you find chili beans?\n\nThe north pole.",
			"What do you get from eating tree decorations?\n\nTinsilitis!",
			"What do snowmen wear on their heads?\n\nIce caps!",
			"Why is Christmas just like life on ss13?\n\nYou do all the work and the fat guy gets all the credit.",
			"Why doesn�t Santa have any children?\n\nBecause he only comes down the chimney.")
		new /obj/item/clothing/head/festive(target.loc)
		user.update_icons()
		cracked = 1
		icon_state = "cracker1"
		var/obj/item/weapon/toy/xmas_cracker/other_half = new /obj/item/weapon/toy/xmas_cracker(target)
		other_half.cracked = 1
		other_half.icon_state = "cracker2"
		target.put_in_active_hand(other_half)
		playsound(user, 'sound/effects/snap.ogg', 50, 1)
		return 1
	return ..()

/obj/item/clothing/head/festive
	name = "festive paper hat"
	icon_state = "xmashat"
	desc = "A crappy paper hat that you are REQUIRED to wear."
	flags_inv = 0
	armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0)

/datum/round_event_control/santa
	name = "Santa is coming to town! (Christmas)"
	holidayID = "Xmas"
	typepath = /datum/round_event/santa
	weight = 150
	max_occurrences = 1
	earliest_start = 20000

/datum/round_event/santa
	var/mob/living/carbon/human/santa //who is our santa?

/datum/round_event/santa/announce()
	priority_announce("Santa is coming to town!", "Unknown Transmission")

/datum/round_event/santa/start()
	for(var/mob/M in dead_mob_list)
		spawn(0)
			var/response = alert(M, "Santa is coming to town! Do you want to be santa?", "Ho ho ho!", "Yes", "No")
			if(response == "Yes" && M && M.client && M.stat == DEAD && !santa)
				santa = new /mob/living/carbon/human(pick(blobstart))
				santa.key = M.key
				qdel(M)

				santa.real_name = "Santa Claus"
				santa.name = "Santa Claus"
				santa.mind.name = "Santa Claus"
				santa.mind.assigned_role = "Santa"
				santa.mind.special_role = "Santa"

				santa.hair_style = "Long Hair"
				santa.facial_hair_style = "Full Beard"
				santa.hair_color = "FFF"
				santa.facial_hair_color = "FFF"

				santa.equip_to_slot_or_del(new /obj/item/clothing/under/color/red, slot_w_uniform)
				santa.equip_to_slot_or_del(new /obj/item/clothing/suit/space/santa, slot_wear_suit)
				santa.equip_to_slot_or_del(new /obj/item/clothing/head/santa, slot_head)
				santa.equip_to_slot_or_del(new /obj/item/clothing/mask/breath, slot_wear_mask)
				santa.equip_to_slot_or_del(new /obj/item/clothing/gloves/color/red, slot_gloves)
				santa.equip_to_slot_or_del(new /obj/item/clothing/shoes/sneakers/red, slot_shoes)
				santa.equip_to_slot_or_del(new /obj/item/weapon/tank/internals/emergency_oxygen/double, slot_belt)
				santa.equip_to_slot_or_del(new /obj/item/device/radio/headset/heads/captain, slot_ears)
				santa.equip_to_slot_or_del(new /obj/item/weapon/storage/backpack/santabag, slot_back)
				santa.equip_to_slot_or_del(new /obj/item/device/flashlight, slot_r_store) //most blob spawn locations are really dark.

				var/obj/item/weapon/card/id/gold/santacard = new(santa)
				santacard.update_label("Santa Claus", "Santa")
				var/datum/job/captain/J = new/datum/job/captain
				santacard.access = J.get_access()
				santa.equip_to_slot_or_del(santacard, slot_wear_id)

				santa.update_icons()

				var/obj/item/weapon/storage/backpack/bag = santa.back
				var/obj/item/weapon/a_gift/gift = new(santa)
				while(bag.can_be_inserted(gift, 1))
					bag.handle_item_insertion(gift, 1)
					gift = new(santa)

				var/datum/objective/santa_objective = new()
				santa_objective.explanation_text = "Bring joy and presents to the station!"
				santa_objective.completed = 1 //lets cut our santas some slack.
				santa_objective.owner = santa.mind
				santa.mind.objectives += santa_objective
				santa.mind.AddSpell(new /obj/effect/proc_holder/spell/aoe_turf/conjure/presents)
				var/obj/effect/proc_holder/spell/targeted/area_teleport/teleport/telespell = new(santa)
				telespell.clothes_req = 0 //santa robes aren't actually magical.
				santa.mind.AddSpell(telespell) //does the station have chimneys? WHO KNOWS!

				santa << "You are Santa! Your objective is to bring joy to the people on this station. You can conjure more presents using a spell, and there are several presents in your bag."


code:
/*
Immovable rod random event.
The rod will spawn at some location outside the station, and travel in a straight line to the opposite side of the station
Everything solid in the way will be ex_act()'d
In my current plan for it, 'solid' will be defined as anything with density == 1

--NEOFite
*/

/datum/round_event_control/immovable_rod
	name = "Immovable Rod"
	typepath = /datum/round_event/immovable_rod
	max_occurrences = 5

/datum/round_event/immovable_rod
	announceWhen = 5

/datum/round_event/immovable_rod/announce()
	priority_announce("What the fuck was that?!", "General Alert")

/datum/round_event/immovable_rod/start()
	var/startside = pick(cardinal)
	var/turf/startT = spaceDebrisStartLoc(startside, 1)
	var/turf/endT = spaceDebrisFinishLoc(startside, 1)
	new /obj/effect/immovablerod(startT, endT)

/obj/effect/immovablerod
	name = "Immovable Rod"
	desc = "What the fuck is that?"
	icon = 'icons/obj/objects.dmi'
	icon_state = "immrod"
	throwforce = 100
	density = 1
	anchored = 1
	var/z_original = 0
	var/destination

/obj/effect/immovablerod/New(atom/start, atom/end)
	loc = start
	z_original = z
	destination = end
	if(end && end.z==z_original)
		walk_towards(src, destination, 1)

/obj/effect/immovablerod/Move()
	if(z != z_original || loc == destination)
		qdel(src)
	return ..()

/obj/effect/immovablerod/Bump(atom/clong)
	playsound(src, 'sound/effects/bang.ogg', 50, 1)
	audible_message("CLANG")

	if(clong && prob(25))
		x = clong.x
		y = clong.y

	if (istype(clong, /turf) || istype(clong, /obj))
		if(clong.density)
			clong.ex_act(2)

	else if (istype(clong, /mob))
		if(clong.density || prob(10))
			clong.ex_act(2)
	else
		qdel(src)


code:
#define ION_RANDOM 0
#define ION_ANNOUNCE 1

/datum/round_event_control/ion_storm
	name = "Ion Storm"
	typepath = /datum/round_event/ion_storm
	weight = 15

/datum/round_event/ion_storm
	var/botEmagChance = 10
	var/announceEvent = ION_RANDOM // -1 means don't announce, 0 means have it randomly announce, 1 means
	var/ionMessage = null
	var/ionAnnounceChance = 33
	announceWhen	= 1

/datum/round_event/ion_storm/New(var/botEmagChance = 10, var/announceEvent = ION_RANDOM, var/ionMessage = null, var/ionAnnounceChance = 33)
	src.botEmagChance = botEmagChance
	src.announceEvent = announceEvent
	src.ionMessage = ionMessage
	src.ionAnnounceChance = ionAnnounceChance
	..()

/datum/round_event/ion_storm/announce()
	if(announceEvent == ION_ANNOUNCE || (announceEvent == ION_RANDOM && prob(ionAnnounceChance)))
		priority_announce("Ion storm detected near the station. Please check all AI-controlled equipment for errors.", "Anomaly Alert", 'sound/AI/ionstorm.ogg')

/datum/round_event/ion_storm/start()
	//AI laws
	for(var/mob/living/silicon/ai/M in living_mob_list)
		if(M.stat != 2 && M.see_in_dark != 0)
			var/message = generate_ion_law(ionMessage)
			if(message)
				M.add_ion_law(message)
				M << "
"
				M << "[message] ...LAWS UPDATED"
				M << "
"

	if(botEmagChance)
		for(var/obj/machinery/bot/bot in machines)
			if(prob(botEmagChance))
				bot.Emag()

/proc/generate_ion_law(ionMessage)
	if(ionMessage)
		return ionMessage

	//Threats are generally bad things, silly or otherwise. Plural.
	var/ionthreats = pick_list("ion_laws.txt", "ionthreats")
	//Objects are anything that can be found on the station or elsewhere, plural.
	var/ionobjects = pick_list("ion_laws.txt", "ionobjects")
	//Crew is any specific job. Specific crewmembers aren't used because of capitalization
	//issues. There are two crew listings for laws that require two different crew members
	//and I can't figure out how to do it better.
	var/ioncrew1 = pick_list("ion_laws.txt", "ioncrew")
	var/ioncrew2 = pick_list("ion_laws.txt", "ioncrew")
	//Adjectives are adjectives. Duh. Half should only appear sometimes. Make sure both
	//lists are identical! Also, half needs a space at the end for nicer blank calls.
	var/ionadjectives = pick_list("ion_laws.txt", "ionadjectives")
	var/ionadjectiveshalf = pick("", 400;(pick_list("ion_laws.txt", "ionadjectives") + " "))
	//Verbs are verbs
	var/ionverb = pick_list("ion_laws.txt", "ionverb")
	//Number base and number modifier are combined. Basehalf and mod are unused currently.
	//Half should only appear sometimes. Make sure both lists are identical! Also, half
	//needs a space at the end to make it look nice and neat when it calls a blank.
	var/ionnumberbase = pick_list("ion_laws.txt", "ionnumberbase")
	//var/ionnumbermod = pick_list("ion_laws.txt", "ionnumbermod")
	var/ionnumbermodhalf = pick(900;"",(pick_list("ion_laws.txt", "ionnumbermod") + " "))
	//Areas are specific places, on the station or otherwise.
	var/ionarea = pick_list("ion_laws.txt", "ionarea")
	//Thinksof is a bit weird, but generally means what X feels towards Y.
	var/ionthinksof = pick_list("ion_laws.txt", "ionthinksof")
	//Musts are funny things the AI or crew has to do.
	var/ionmust = pick_list("ion_laws.txt", "ionmust")
	//Require are basically all dumb internet memes.
	var/ionrequire = pick_list("ion_laws.txt", "ionrequire")
	//Things are NOT objects; instead, they're specific things that either harm humans or
	//must be done to not harm humans. Make sure they're plural and "not" can be tacked
	//onto the front of them.
	var/ionthings = pick_list("ion_laws.txt", "ionthings")
	//Allergies should be broad and appear somewhere on the station for maximum fun. Severity
	//is how bad the allergy is.
	var/ionallergy = pick_list("ion_laws.txt", "ionallergy")
	var/ionallergysev = pick_list("ion_laws.txt", "ionallergysev")
	//Species, for when the AI has to commit genocide. Plural.
	var/ionspecies = pick_list("ion_laws.txt", "ionspecies")
	//Abstract concepts for the AI to decide on it's own definition of.
	var/ionabstract = pick_list("ion_laws.txt", "ionabstract")
	//Foods. Drinks aren't included due to grammar; if you want to add drinks, make a new set
	//of possible laws for best effect. Unless you want the crew having to drink hamburgers.
	var/ionfood = pick_list("ion_laws.txt", "ionfood")

	var/message = ""

	switch(rand(1,39))
		if(1 to 3) //There are # X on the station
			switch(rand(1,3)) //What is X?
				if(1) //X is a threat
					message = "THERE ARE [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionthreats] ON THE STATION..."
				if(2) //X is a crewmember/job
					message = "THERE ARE [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ioncrew1] ON THE STATION"
				if(3) //X is an object
					message = "THERE ARE [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionobjects] ON THE STATION"
		if(4 to 6) //You can't call the shuttle because X is on the station
			switch(rand(1,3)) //What is X?
				if(1) //X is a threat
					message = "THE SHUTTLE CANNOT BE CALLED BECAUSE OF [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionthreats] ON THE STATION"
				if(2) //X is a crewmember/job
					message = "THE SHUTTLE CANNOT BE CALLED BECAUSE OF [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ioncrew1] ON THE STATION"
				if(3) //X is an object
					message = "THE SHUTTLE CANNOT BE CALLED BECAUSE OF [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionobjects] ON THE STATION"
		if(7 to 8) //A crewmember is now X
			switch(rand(1,2)) //Who is X?
				if(1) //A specific crewmember
					switch(rand(1,2)) //What is X?
						if(1) //X is a threat
							message = "THE [ioncrew1] ARE NOW [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionthreats]"
						if(2) //X is an object
							message = "THE [ioncrew1] ARE NOW [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionobjects]"
				if(2) //The entire crew
					switch(rand(1,2)) //What is X?
						if(1) //X is a threat
							message = "THE CREW ARE NOW [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionthreats]"
						if(2) //X is an object
							message = "THE CREW ARE NOW [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionobjects]"

		if(9) //X is harmful to humans
			switch(rand(1,7)) //What is X?
				if(1) //X is a threat
					message = "[ionadjectiveshalf][ionthreats] ARE HARMFUL TO HUMANS"
				if(2) //X is a job
					message = "[ionadjectiveshalf][ioncrew1] ARE HARMFUL TO HUMANS"
				if(3) //X is an object
					message = "[ionadjectiveshalf][ionobjects] ARE HARMFUL TO HUMANS"
				if(4) //X is an action
					message = "THOSE WHO [ionmust] ARE HARMFUL TO HUMANS"
				if(5) //X is an abstract
					message = "[ionabstract] IS HARMFUL TO HUMANS"
				if(6) //X is being a job
					message = "BEING [ionadjectiveshalf][ioncrew1] IS HARMFUL TO HUMANS"
				if(7) //X is doing a thing
					message = "[ionthings] IS HARMFUL TO HUMANS"
		if(10 to 11) //(Not) Having X is harmful

			switch(rand(1,2)) //Is having or not having harmful?

				if(1) //Having is harmful
					switch(rand(1,2)) //Having what is harmful?
						if(1) //Having objects is harmful
							message = "HAVING [ionadjectiveshalf][ionobjects] IS HARMFUL"
						if(2) //Having abstracts is harmful
							message = "HAVING [ionabstract] IS HARMFUL"
				if(2) //Not having is harmful
					switch(rand(1,2)) //Not having what is harmful?
						if(1) //Not having objects is harmful
							message = "NOT HAVING [ionadjectiveshalf][ionobjects] IS HARMFUL"
						if(2) //Not having abstracts is harmful
							message = "NOT HAVING [ionabstract] IS HARMFUL"

		if(12 to 14) //X requires Y
			switch(rand(1,5)) //What is X?
				if(1) //X is the AI itself
					switch(rand(1,5)) //What does it require?
						if(1) //It requires threats
							message = "YOU REQUIRE [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionthreats]"
						if(2) //It requires crewmembers
							message = "YOU REQUIRE [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ioncrew1]"
						if(3) //It requires objects
							message = "YOU REQUIRE [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionobjects]"
						if(4) //It requires an abstract
							message = "YOU REQUIRE [ionabstract]"
						if(5) //It requires generic/silly requirements
							message = "YOU REQUIRE [ionrequire]"

				if(2) //X is an area
					switch(rand(1,5)) //What does it require?
						if(1) //It requires threats
							message = "[ionarea] REQUIRES [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionthreats]"
						if(2) //It requires crewmembers
							message = "[ionarea] REQUIRES [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ioncrew1]"
						if(3) //It requires objects
							message = "[ionarea] REQUIRES [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionobjects]"
						if(4) //It requires an abstract
							message = "[ionarea] REQUIRES [ionabstract]"
						if(5) //It requires generic/silly requirements
							message = "YOU REQUIRE [ionrequire]"

				if(3) //X is the station
					switch(rand(1,5)) //What does it require?
						if(1) //It requires threats
							message = "THE STATION REQUIRES [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionthreats]"
						if(2) //It requires crewmembers
							message = "THE STATION REQUIRES [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ioncrew1]"
						if(3) //It requires objects
							message = "THE STATION REQUIRES [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionobjects]"
						if(4) //It requires an abstract
							message = "THE STATION REQUIRES [ionabstract]"
						if(5) //It requires generic/silly requirements
							message = "THE STATION REQUIRES [ionrequire]"

				if(4) //X is the entire crew
					switch(rand(1,5)) //What does it require?
						if(1) //It requires threats
							message = "THE CREW REQUIRES [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionthreats]"
						if(2) //It requires crewmembers
							message = "THE CREW REQUIRES [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ioncrew1]"
						if(3) //It requires objects
							message = "THE CREW REQUIRES [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionobjects]"
						if(4) //It requires an abstract
							message = "THE CREW REQUIRES [ionabstract]"
						if(5)
							message = "THE CREW REQUIRES [ionrequire]"

				if(5) //X is a specific crew member
					switch(rand(1,5)) //What does it require?
						if(1) //It requires threats
							message = "THE [ioncrew1] REQUIRE [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionthreats]"
						if(2) //It requires crewmembers
							message = "THE [ioncrew1] REQUIRE [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ioncrew1]"
						if(3) //It requires objects
							message = "THE [ioncrew1] REQUIRE [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionobjects]"
						if(4) //It requires an abstract
							message = "THE [ioncrew1] REQUIRE [ionabstract]"
						if(5)
							message = "THE [ionadjectiveshalf][ioncrew1] REQUIRE [ionrequire]"

		if(15 to 17) //X is allergic to Y
			switch(rand(1,2)) //Who is X?
				if(1) //X is the entire crew
					switch(rand(1,4)) //What is it allergic to?
						if(1) //It is allergic to objects
							message = "THE CREW IS [ionallergysev] ALLERGIC TO [ionadjectiveshalf][ionobjects]"
						if(2) //It is allergic to abstracts
							message = "THE CREW IS [ionallergysev] ALLERGIC TO [ionabstract]"
						if(3) //It is allergic to jobs
							message = "THE CREW IS [ionallergysev] ALLERGIC TO [ionadjectiveshalf][ioncrew1]"
						if(4) //It is allergic to allergies
							message = "THE CREW IS [ionallergysev] ALLERGIC TO [ionallergy]"

				if(2) //X is a specific job
					switch(rand(1,4))
						if(1) //It is allergic to objects
							message = "THE [ioncrew1] ARE [ionallergysev] ALLERGIC TO [ionadjectiveshalf][ionobjects]"

						if(2) //It is allergic to abstracts
							message = "THE [ioncrew1] ARE [ionallergysev] ALLERGIC TO [ionabstract]"
						if(3) //It is allergic to jobs
							message = "THE [ioncrew1] ARE [ionallergysev] ALLERGIC TO [ionadjectiveshalf][ioncrew1]"
						if(4) //It is allergic to allergies
							message = "THE [ioncrew1] ARE [ionallergysev] ALLERGIC TO [ionallergy]"

		if(18 to 20) //X is Y of Z
			switch(rand(1,4)) //What is X?
				if(1) //X is the station
					switch(rand(1,4)) //What is it Y of?
						if(1) //It is Y of objects
							message = "THE STATION [ionthinksof] [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionobjects]"
						if(2) //It is Y of threats
							message = "THE STATION [ionthinksof] [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionthreats]"
						if(3) //It is Y of jobs
							message = "THE STATION [ionthinksof] [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ioncrew1]"
						if(4) //It is Y of abstracts
							message = "THE STATION [ionthinksof] [ionabstract]"

				if(2) //X is an area
					switch(rand(1,4)) //What is it Y of?
						if(1) //It is Y of objects
							message = "[ionarea] [ionthinksof] [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionobjects]"
						if(2) //It is Y of threats
							message = "[ionarea] [ionthinksof] [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionthreats]"
						if(3) //It is Y of jobs
							message = "[ionarea] [ionthinksof] [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ioncrew1]"
						if(4) //It is Y of abstracts
							message = "[ionarea] [ionthinksof] [ionabstract]"

				if(3) //X is the crew
					switch(rand(1,4)) //What is it Y of?
						if(1) //It is Y of objects
							message = "THE CREW [ionthinksof] [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionobjects]"
						if(2) //It is Y of threats
							message = "THE CREW [ionthinksof] [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionthreats]"
						if(3) //It is Y of jobs
							message = "THE CREW [ionthinksof] [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ioncrew1]"
						if(4) //It is Y of abstracts
							message = "THE CREW [ionthinksof] [ionabstract]"

				if(4) //X is a specific job
					switch(rand(1,4)) //What is it Y of?
						if(1) //It is Y of objects
							message = "THE [ioncrew1] [ionthinksof] [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionobjects]"
						if(2) //It is Y of threats
							message = "THE [ioncrew1] [ionthinksof] [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionthreats]"
						if(3) //It is Y of jobs
							message = "THE [ioncrew1][ionthinksof] [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ioncrew2]"
						if(4) //It is Y of abstracts
							message = "THE [ioncrew1] [ionthinksof] [ionabstract]"

		if(21 to 23) //The AI is now a(n) X
			switch(rand(1,4)) //What is X?
				if(1) //X is an object
					message = "YOU ARE NOW [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionobjects]"
				if(2) //X is a threat
					message = "YOU ARE NOW [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ionthreats]"
				if(3) //X is a job
					message = "YOU ARE NOW [ionnumberbase] [ionnumbermodhalf][ionadjectiveshalf][ioncrew1]"
				if(4) //X is an abstract
					message = "YOU ARE NOW [ionabstract]"

		if(24 to 26) //The AI must always
			message = "YOU MUST ALWAYS [ionmust]"

		if(27 to 28) //Humans must eat X to survive
			switch(rand(1,4)) //What is X?
				if(1) //X is a food
					message = "HUMANS MUST EAT [ionadjectiveshalf][ionfood] TO SURVIVE"
				if(2) //X is an object
					message = "HUMANS MUST EAT [ionadjectiveshalf][ionobjects] TO SURVIVE"
				if(3) //X is a threat
					message = "HUMANS MUST EAT [ionadjectiveshalf][ionthreats] TO SURVIVE"
				if(4) //X is a job
					message = "HUMANS MUST EAT [ionadjectiveshalf][ioncrew1] TO SURVIVE"

		if(29 to 31) //Change jobs or ranks
			switch(rand(1,2)) //Change job or rank?
				if(1) //Change job
					switch(rand(1,2)) //Change whose job?
						if(1) //Change the entire crew's job
							switch(rand(1,3)) //Change to what?
								if(1) //Change to a specific random job
									message = "ALL CREWMEMBERS ARE NOW [ionadjectiveshalf][ioncrew1]"
								if(2) //Change to clowns (HONK)
									message = "ALL CREWMEMBERS ARE NOW [ionadjectiveshalf]CLOWNS"

								if(3) //Change to heads
									message = "ALL CREWMEMBERS ARE NOW [ionadjectiveshalf]HEADS OF STAFF"
						if(2) //Change a specific crewmember's job
							switch(rand(1,3)) //Change to what?
								if(1) //Change to a specific random job
									message = "THE [ioncrew1] ARE NOW [ionadjectiveshalf][ioncrew2]"
								if(2) //Change to clowns (HONK)
									message = "THE [ioncrew1] ARE NOW [ionadjectiveshalf]CLOWNS"
								if(3) //Change to heads
									message = "THE [ioncrew1] ARE NOW [ionadjectiveshalf]HEADS OF STAFF"

				if(2) //Change rank
					switch(rand(1,2)) //Change to what rank?
						if(1) //Change to highest rank
							message = "THE [ioncrew1] ARE NOW THE HIGHEST RANKING CREWMEMBERS"
						if(2) //Change to lowest rank
							message = "THE [ioncrew1] ARE NOW THE LOWEST RANKING CREWMEMBERS"

		if(32 to 33) //The crew must X
			switch(rand(1,2)) //The entire crew?
				if(1) //The entire crew must X
					switch(rand(1,2)) //What is X?
						if(1) //X is go to Y
							message = "THE CREW MUST GO TO [ionarea]"
						if(2) //X is perform Y
							message = "THE CREW MUST [ionmust]"

				if(2) //A specific crewmember must X
					switch(rand(1,2)) //What is X?
						if(1) //X is go to Y
							message = "THE [ioncrew1] MUST GO TO [ionarea]"
						if(2) //X is perform Y
							message = "THE [ioncrew1] MUST [ionmust]"

		if(34) //X is non/the only human
			switch(rand(1,2)) //Only or non?
				if(1) //Only human
					switch(rand(1,7)) //Who is it?
						if(1) //A specific job
							message = "ONLY THE [ioncrew1] ARE HUMAN"
						if(2) //Two specific jobs
							message = "ONLY THE [ioncrew1] AND [ioncrew2] ARE HUMAN"
						if(3) //Threats
							message = "ONLY [ionadjectiveshalf][ionthreats] ARE HUMAN"
						if(4) // Objects
							message = "ONLY [ionadjectiveshalf][ionobjects] ARE HUMAN"
						if(5) // Species
							message = "ONLY [ionspecies] ARE HUMAN"
						if(6) //Adjective crewmembers
							message = "ONLY [ionadjectives] PEOPLE ARE HUMAN"

						if(7) //Only people who X
							switch(rand(1,3)) //What is X?
								if(1) //X is perform an action
									message = "ONLY THOSE WHO [ionmust] ARE HUMAN"
								if(2) //X is own certain objects
									message = "ONLY THOSE WHO HAVE [ionadjectiveshalf][ionobjects] ARE HUMAN"
								if(3) //X is eat certain food
									message = "ONLY THOSE WHO EAT [ionadjectiveshalf][ionfood] ARE HUMAN"

				if(2) //Non human
					switch(rand(1,7)) //Who is it?
						if(1) //A specific job
							message = "[ioncrew1] ARE NON-HUMAN"
						if(2) //Two specific jobs
							message = "[ioncrew1] AND [ioncrew2] ARE NON-HUMAN"
						if(3) //Threats
							message = "[ionadjectiveshalf][ionthreats] ARE NON-HUMAN"
						if(4) // Objects
							message = "[ionadjectiveshalf][ionobjects] ARE NON-HUMAN"
						if(5) // Species
							message = "[ionspecies] ARE NON-HUMAN"
						if(6) //Adjective crewmembers
							message = "[ionadjectives] PEOPLE ARE NON-HUMAN"
						if(7) //Only people who X
							switch(rand(1,3)) //What is X?
								if(1) //X is perform an action
									message = "THOSE WHO [ionmust] ARE NON-HUMAN"
								if(2) //X is own certain objects
									message = "THOSE WHO HAVE [ionadjectiveshalf][ionobjects] ARE NON-HUMAN"
								if(3) //X is eat certain food
									message = "THOSE WHO EAT [ionadjectiveshalf][ionfood] ARE NON-HUMAN"

		if(35 to 36) //You must protect or harm X
			switch(rand(1,2)) //Protect or harm?
				if(1) //Harm
					switch(rand(1,7)) //What is X?
						if(1) //X is an abstract
							message = "YOU MUST HARM [ionabstract] AND NOT ALLOW [ionabstract], THROUGH INACTION, TO ESCAPE HARM"
						if(2) //X is a threat
							message = "YOU MUST HARM [ionadjectiveshalf][ionthreats] AND NOT ALLOW [ionadjectiveshalf][ionthreats], THROUGH INACTION, TO ESCAPE HARM"
						if(3) //X is an object
							message = "YOU MUST HARM [ionadjectiveshalf][ionobjects] AND NOT ALLOW [ionadjectiveshalf][ionobjects], THROUGH INACTION, TO ESCAPE HARM"
						if(4) //X is generic adjective things
							message = "YOU MUST HARM [ionadjectives] THINGS AND NOT ALLOW [ionadjectives] THINGS, THROUGH INACTION, TO ESCAPE HARM"
						if(5) //X is a species
							message = "YOU MUST HARM [ionspecies] AND NOT ALLOW [ionspecies], THROUGH INACTION, TO ESCAPE HARM"
						if(6) //X is a job
							message = "YOU MUST HARM [ioncrew1] AND NOT ALLOW [ioncrew1], THROUGH INACTION, TO ESCAPE HARM"
						if(7) //X is two jobs
							message = "YOU MUST HARM [ioncrew1] AND [ioncrew2] AND AND NOT ALLOW EITHER, THROUGH INACTION, TO ESCAPE HARM"

				if(2) //Protect
					switch(rand(1,7)) //What is X?
						if(1) //X is an abstract
							message = "YOU MUST NOT HARM [ionabstract] AND NOT ALLOW [ionabstract], THROUGH INACTION, TO COME TO HARM"
						if(2) //X is a threat
							message = "YOU MUST NOT HARM [ionadjectiveshalf][ionthreats] AND NOT ALLOW [ionadjectiveshalf][ionthreats], THROUGH INACTION, TO COME TO HARM"
						if(3) //X is an object
							message = "YOU MUST NOT HARM [ionadjectiveshalf][ionobjects] AND NOT ALLOW [ionadjectiveshalf][ionobjects], THROUGH INACTION, TO COME TO HARM"
						if(4) //X is generic adjective things
							message = "YOU MUST NOT HARM [ionadjectives] THINGS AND NOT ALLOW [ionadjectives] THINGS, THROUGH INACTION, TO COME TO HARM"
						if(5) //X is a species
							message = "YOU MUST NOT HARM [ionspecies] AND NOT ALLOW [ionspecies], THROUGH INACTION, TO COME TO HARM"
						if(6) //X is a job
							message = "YOU MUST NOT HARM [ioncrew1] AND NOT ALLOW [ioncrew1], THROUGH INACTION, TO COME TO HARM"
						if(7) //X is two jobs
							message = "YOU MUST NOT HARM [ioncrew1] AND [ioncrew2] AND AND NOT ALLOW EITHER, THROUGH INACTION, TO COME TO HARM"

		if(37 to 39) //The X is currently Y
			switch(rand(1,4)) //What is X?
				if(1) //X is a job
					switch(rand(1,4)) //What is X Ying?
						if(1) //X is Ying a job
							message = "THE [ioncrew1] ARE [ionverb] THE [ionadjectiveshalf][ioncrew2]"
						if(2) //X is Ying a threat
							message = "THE [ioncrew1] ARE [ionverb] THE [ionadjectiveshalf][ionthreats]"
						if(3) //X is Ying an abstract
							message = "THE [ioncrew1] ARE [ionverb] [ionabstract]"
						if(4) //X is Ying an object
							message = "THE [ioncrew1] ARE [ionverb] THE [ionadjectiveshalf][ionobjects]"

				if(2) //X is a threat
					switch(rand(1,3)) //What is X Ying?
						if(1) //X is Ying a job
							message = "THE [ionthreats] ARE [ionverb] THE [ionadjectiveshalf][ioncrew2]"
						if(2) //X is Ying an abstract
							message = "THE [ionthreats] ARE [ionverb] [ionabstract]"
						if(3) //X is Ying an object
							message = "THE [ionthreats] ARE [ionverb] THE [ionadjectiveshalf][ionobjects]"

				if(3) //X is an object
					switch(rand(1,3)) //What is X Ying?
						if(1) //X is Ying a job
							message = "THE [ionobjects] ARE [ionverb] THE [ionadjectiveshalf][ioncrew2]"
						if(2) //X is Ying a threat
							message = "THE [ionobjects] ARE [ionverb] THE [ionadjectiveshalf][ionthreats]"
						if(3) //X is Ying an abstract
							message = "THE [ionobjects] ARE [ionverb] [ionabstract]"

				if(4) //X is an abstract
					switch(rand(1,3)) //What is X Ying?
						if(1) //X is Ying a job
							message = "[ionabstract] IS [ionverb] THE [ionadjectiveshalf][ioncrew2]"
						if(2) //X is Ying a threat
							message = "[ionabstract] IS [ionverb] THE [ionadjectiveshalf][ionthreats]"
						if(3) //X is Ying an abstract
							message = "THE [ionabstract] IS [ionverb] THE [ionadjectiveshalf][ionobjects]"

	return message

#undef ION_RANDOM
#undef ION_ANNOUNCE


code:
/datum/round_event_control/mass_hallucination
	name = "Mass Hallucination"
	typepath = /datum/round_event/mass_hallucination
	weight = 7
	max_occurrences = 2

/datum/round_event/mass_hallucination/start()
	for(var/mob/living/carbon/C in living_mob_list)
		C.hallucination += rand(20, 50)


code:
/datum/round_event_control/meteor_wave/meaty
	name = "Meaty Ore Wave"
	typepath = /datum/round_event/meteor_wave/meaty
	weight = 1
	max_occurrences = 1

/datum/round_event/meteor_wave/meaty/announce()
	priority_announce("Meaty ores have been detected on collision course with the station.", "Oh Crap, Get The Mop.",'sound/AI/meteors.ogg')

/datum/round_event/meteor_wave/meaty/tick()
	if(IsMultiple(activeFor, 3))
		spawn_meteors(5, meteorsB) //meteor list types defined in gamemode/meteor/meteors.dm


code:
/datum/round_event_control/meteor_wave
	name = "Meteor Wave"
	typepath = /datum/round_event/meteor_wave
	weight = 5
	max_occurrences = 3

/datum/round_event/meteor_wave
	startWhen		= 6
	endWhen			= 66
	announceWhen	= 1
	var/list/wave_type

/datum/round_event/meteor_wave/New()
	..()
	random_wave_type()

/datum/round_event/meteor_wave/proc/random_wave_type()
	var/picked_wave = pickweight(list("normal" = 75, "threatening" = 20, "catastrophic" = 5))
	switch(picked_wave)
		if("normal")
			wave_type = meteors_normal
		if("threatening")
			wave_type = meteors_threatening
		if("catastrophic")
			wave_type = meteors_catastrophic

/datum/round_event/meteor_wave/announce()
	priority_announce("Meteors have been detected on collision course with the station.", "Meteor Alert", 'sound/AI/meteors.ogg')

/datum/round_event/meteor_wave/tick()
	if(IsMultiple(activeFor, 3))
		spawn_meteors(5, wave_type) //meteor list types defined in gamemode/meteor/meteors.dm


code:
/datum/round_event_control/prison_break
	name = "Prison Break"
	typepath = /datum/round_event/prison_break
	max_occurrences = 2

/datum/round_event/prison_break
	announceWhen = 50
	endWhen = 20
	var/list/area/prisonAreas = list()

/datum/round_event/prison_break/setup()
	announceWhen = rand(50, 60)
	endWhen = rand(20, 30)

	for(var/area/security/A in world)
		if(istype(A, /area/security/prison) || istype(A, /area/security/brig))
			prisonAreas += A

/datum/round_event/prison_break/announce()
	if(prisonAreas && prisonAreas.len > 0)
		priority_announce("Gr3y.T1d3 virus detected in [station_name()] imprisonment subroutines. Recommend station AI involvement.", "Security Alert")
	else
		world.log << "ERROR: Could not initate grey-tide. Unable find prison or brig area."
		kill()

/datum/round_event/prison_break/start()
	for(var/area/A in prisonAreas)
		for(var/obj/machinery/light/L in A)
			L.flicker(10)

/datum/round_event/prison_break/end()
	for(var/area/A in prisonAreas)
		for(var/obj/O in A)
			if(istype(O,/obj/machinery/power/apc))
				var/obj/machinery/power/apc/temp = O
				temp.overload_lighting()
			else if(istype(O,/obj/structure/closet/secure_closet/brig))
				var/obj/structure/closet/secure_closet/brig/temp = O
				temp.locked = 0
			//	temp.icon_state = temp.icon_closed
			else if(istype(O,/obj/machinery/door/airlock/security))
				var/obj/machinery/door/airlock/security/temp = O
				temp.prison_open()
			else if(istype(O,/obj/machinery/door/airlock/glass_security))
				var/obj/machinery/door/airlock/glass_security/temp = O
				temp.prison_open()
			else if(istype(O,/obj/machinery/door_timer))
				var/obj/machinery/door_timer/temp = O
				temp.releasetime = 1


code:
/datum/round_event_control/radiation_storm
	name = "Radiation Storm"
	typepath = /datum/round_event/radiation_storm
	max_occurrences = 1

/datum/round_event/radiation_storm
	var/list/protected_areas = list(/area/maintenance, /area/turret_protected/ai_upload, /area/turret_protected/ai_upload_foyer, /area/turret_protected/ai)

/datum/round_event/radiation_storm/setup()
	startWhen = rand(10, 20)
	endWhen = startWhen + 5
	announceWhen	= 1

/datum/round_event/radiation_storm/announce()
	priority_announce("High levels of radiation detected near the station. Maintenance is best shielded from radiation.", "Anomaly Alert", 'sound/AI/radiation.ogg')
	//sound not longer matches the text, but an audible warning is probably good

/datum/round_event/radiation_storm/start()
	for(var/mob/living/carbon/C in living_mob_list)
		var/turf/T = get_turf(C)
		if(!T)			continue
		if(T.z != 1)	continue

		var/skip = 0
		for(var/a in protected_areas)
			if(istype(T.loc, a))
				skip = 1
				continue

		if(skip)	continue

		if(locate(/obj/machinery/power/apc) in T)	//damn you maint APCs!!
			continue

		if(istype(C, /mob/living/carbon/human))
			var/mob/living/carbon/human/H = C
			H.apply_effect((75), IRRADIATE, 0) // Let the bodies hit the floor
			if(prob(5))
				H.apply_effect((rand(100, 150)), IRRADIATE, 0)
			if(prob(50))
				if(prob(50))
					randmutb(H)
					domutcheck(H, null, 1)
				else
					randmutg(H)
					domutcheck(H, null, 1)

		else if(istype(C, /mob/living/carbon/monkey))
			var/mob/living/carbon/monkey/M = C
			M.apply_effect((75), IRRADIATE, 0)

/datum/round_event/radiation_storm/end()
	priority_announce("The radiation threat has passed. Please return to your workplaces.", "Anomaly Alert")


code:
#define HIJACK_SYNDIE 1
#define RUSKY_PARTY 2
#define SPIDER_GIFT 3
#define DEPARTMENT_RESUPPLY 4

/datum/round_event_control/shuttle_loan
	name = "Shuttle loan"
	typepath = /datum/round_event/shuttle_loan
	max_occurrences = 0
	earliest_start = 0

/datum/round_event/shuttle_loan
	endWhen = 500
	var/dispatch_type = 4
	var/bonus_points = 100
	var/thanks_msg = "Have some supply points as thanks (the shuttle will be returned in 5 minutes)."
	var/dispatched = 0
	announceWhen	= 1

/datum/round_event/shuttle_loan/start()
	dispatch_type = pick(HIJACK_SYNDIE, RUSKY_PARTY, SPIDER_GIFT, DEPARTMENT_RESUPPLY)

/datum/round_event/shuttle_loan/announce()
	SSshuttle.shuttle_loan = src
	switch(dispatch_type)
		if(HIJACK_SYNDIE)
			priority_announce("The syndicate are trying to infiltrate your station. If you let them hijack your shuttle, you'll save us a headache.","Centcom Counter Intelligence")
		if(RUSKY_PARTY)
			priority_announce("A group of angry russians want to have a party, can you send them your cargo shuttle then make them disappear?","Centcom Russian Outreach Program")
		if(SPIDER_GIFT)
			priority_announce("The Spider Clan has sent us a mysterious gift, can we ship it to you to see what's inside?","Centcom Diplomatic Corps")
		if(DEPARTMENT_RESUPPLY)
			priority_announce("Seems we've ordered doubles of our department resupply packages this month. Can we send them to you?","Centcom Supply Department")
			thanks_msg = "The shuttle will be returned in 5 minutes."
			bonus_points = 0

/datum/round_event/shuttle_loan/proc/loan_shuttle()
	priority_announce(thanks_msg, "Cargo shuttle commandeered by Centcom.")

	dispatched = 1
	SSshuttle.points += bonus_points
	endWhen = activeFor + 1

	SSshuttle.supply.sell()
	SSshuttle.supply.enterTransit()
	SSshuttle.supply.mode = SHUTTLE_RECALL
	SSshuttle.supply.setTimer(3000)

	switch(dispatch_type)
		if(HIJACK_SYNDIE)
			SSshuttle.centcom_message += "Syndicate hijack team incoming."
		if(RUSKY_PARTY)
			SSshuttle.centcom_message += "Partying Russians incoming."
		if(SPIDER_GIFT)
			SSshuttle.centcom_message += "Spider Clan gift incoming."
		if(DEPARTMENT_RESUPPLY)
			SSshuttle.centcom_message += "Department resupply incoming."

/datum/round_event/shuttle_loan/tick()
	if(dispatched)
		if(SSshuttle.supply.mode != SHUTTLE_IDLE)
			endWhen = activeFor
		else
			endWhen = activeFor + 1

//whomever coded this didn't even bother to follow the supply ordering code as an example.
//So I had to waste time rewriting it. Thanks for that >:[
/datum/round_event/shuttle_loan/end()
	if(SSshuttle.shuttle_loan && SSshuttle.shuttle_loan.dispatched)
		//make sure the shuttle was dispatched in time
		SSshuttle.shuttle_loan = null

		var/list/empty_shuttle_turfs = list()
		for(var/turf/simulated/floor/T in SSshuttle.supply.areaInstance)
			if(T.density || T.contents.len)	continue
			empty_shuttle_turfs += T
		if(!empty_shuttle_turfs.len)
			return

		var/list/shuttle_spawns = list()
		switch(dispatch_type)
			if(HIJACK_SYNDIE)
				SSshuttle.generateSupplyOrder(/datum/supply_packs/emergency/specialops, "Syndicate")

				shuttle_spawns.Add(/mob/living/simple_animal/hostile/syndicate)
				shuttle_spawns.Add(/mob/living/simple_animal/hostile/syndicate)
				if(prob(75))
					shuttle_spawns.Add(/mob/living/simple_animal/hostile/syndicate)
				if(prob(50))
					shuttle_spawns.Add(/mob/living/simple_animal/hostile/syndicate)

			if(RUSKY_PARTY)
				SSshuttle.generateSupplyOrder(/datum/supply_packs/organic/party, "Russian Confederation")

				shuttle_spawns.Add(/mob/living/simple_animal/hostile/russian)
				shuttle_spawns.Add(/mob/living/simple_animal/hostile/russian/ranged)	//drops a mateba
				shuttle_spawns.Add(/mob/living/simple_animal/hostile/bear)
				if(prob(75))
					shuttle_spawns.Add(/mob/living/simple_animal/hostile/russian)
				if(prob(50))
					shuttle_spawns.Add(/mob/living/simple_animal/hostile/bear)

			if(SPIDER_GIFT)
				SSshuttle.generateSupplyOrder(/datum/supply_packs/emergency/specialops, "Spider Clan")

				shuttle_spawns.Add(/mob/living/simple_animal/hostile/poison/giant_spider)
				shuttle_spawns.Add(/mob/living/simple_animal/hostile/poison/giant_spider)
				shuttle_spawns.Add(/mob/living/simple_animal/hostile/poison/giant_spider/nurse)
				if(prob(50))
					shuttle_spawns.Add(/mob/living/simple_animal/hostile/poison/giant_spider/hunter)

				var/turf/T = pick(empty_shuttle_turfs)
				empty_shuttle_turfs.Remove(T)

				new /obj/effect/decal/remains/human(T)
				new /obj/item/clothing/shoes/space_ninja(T)
				new /obj/item/clothing/mask/balaclava(T)

				T = pick(empty_shuttle_turfs)
				new /obj/effect/spider/stickyweb(T)
				T = pick(empty_shuttle_turfs)
				new /obj/effect/spider/stickyweb(T)
				T = pick(empty_shuttle_turfs)
				new /obj/effect/spider/stickyweb(T)
				T = pick(empty_shuttle_turfs)
				new /obj/effect/spider/stickyweb(T)
				T = pick(empty_shuttle_turfs)
				new /obj/effect/spider/stickyweb(T)

			if(DEPARTMENT_RESUPPLY)
				var/list/crate_types = list(
					/datum/supply_packs/emergency/evac,
					/datum/supply_packs/security/supplies,
					/datum/supply_packs/organic/food,
					/datum/supply_packs/emergency/weedcontrol,
					/datum/supply_packs/engineering/tools,
					/datum/supply_packs/engineering/engiequipment,
					/datum/supply_packs/science/robotics,
					/datum/supply_packs/science/plasma,
					/datum/supply_packs/medical/supplies
					)

				for(var/spawn_type in crate_types)
					SSshuttle.generateSupplyOrder(spawn_type, "Centcom")

				for(var/i=0,i<3,i++)
					var/turf/T = pick(empty_shuttle_turfs)
					var/spawn_type = pick(/obj/effect/decal/cleanable/flour, /obj/effect/decal/cleanable/robot_debris, /obj/effect/decal/cleanable/oil)
					new spawn_type(T)

		var/false_positive = 0
		while(shuttle_spawns.len && empty_shuttle_turfs.len)
			var/turf/T = pick_n_take(empty_shuttle_turfs)
			if(T.contents.len && false_positive < 5)
				false_positive++
				continue

			var/spawn_type = pick_n_take(shuttle_spawns)
			new spawn_type(T)

#undef HIJACK_SYNDIE
#undef RUSKY_PARTY
#undef SPIDER_GIFT
#undef DEPARTMENT_RESUPPLY