code:
#define COOLDOWN_STUN 120 SECONDS
#define COOLDOWN_DAMAGE 60 SECONDS
#define COOLDOWN_MEME 30 SECONDS
#define COOLDOWN_NONE 10 SECONDS

/// Used to stop listeners with silly or clown-esque (first) names such as "Honk" or "Flip" from screwing up certain commands.
GLOBAL_DATUM(all_voice_of_god_triggers, /regex)
/// List of all voice of god commands
GLOBAL_LIST_INIT(voice_of_god_commands, init_voice_of_god_commands())

/proc/init_voice_of_god_commands()
	. = list()
	var/all_triggers
	var/separator
	for(var/datum/voice_of_god_command/prototype as anything in subtypesof(/datum/voice_of_god_command))
		var/init_trigger = initial(prototype.trigger)
		if(!init_trigger)
			continue
		. += new prototype
		all_triggers += "[separator][init_trigger]"
		separator = "|" // Shouldn't be at the start or end of the regex, or it won't work.
	GLOB.all_voice_of_god_triggers = regex(all_triggers, "i")

//////////////////////////////////////
///////////VOICE OF GOD///////////////
//////////////////////////////////////

/*
 * The main proc for the voice of god power. it makes the user shout a message in an ominous way,
 * The first matching command (from a list of static datums) the listeners must obey,
 * and the return value of this proc the cooldown variable of the command dictates. (only relevant for things with cooldowns i guess)
 */
/proc/voice_of_god(message, mob/living/user, list/span_list, base_multiplier = 1, include_speaker = FALSE, forced = null)
	var/log_message = uppertext(message)
	var/is_cultie = IS_CULTIST(user)
	if(LAZYLEN(span_list) && is_cultie)
		span_list = list("narsiesmall")

	if(!user.say(message, spans = span_list, sanitize = FALSE))
		return

	message = lowertext(message)

	var/list/mob/living/listeners = list()
	//used to check if the speaker specified a name or a job to focus on
	var/list/specific_listeners = list()
	// string to remove at the end of the following of the following loop, so saying "Burn Mr. Hopkins" doesn't also burn the HoP later when we check jobs.
	var/to_remove_string
	var/list/candidates = get_hearers_in_view(8, user) - (include_speaker ? null : user)
	for(var/mob/living/candidate in candidates)
		if(candidate.stat != DEAD && candidate.can_hear() && !candidate.anti_magic_check(magic = FALSE, holy = TRUE))
			listeners += candidate

			//Let's ensure the listener's name is not matched within another word or command (and viceversa). e.g. "Saul" in "somersault"
			var/their_first_name = candidate.first_name()
			if(!GLOB.all_voice_of_god_triggers.Find(their_first_name) && findtext(message, regex("(\\L|^)[their_first_name](\\L|$)", "i")))
				specific_listeners += candidate //focus on those with the specified name
				to_remove_string += "[to_remove_string ? "|" : null][their_first_name]"
				continue
			var/their_last_name = candidate.last_name()
			if(their_last_name != their_first_name && !GLOB.all_voice_of_god_triggers.Find(their_last_name) && findtext(message, regex("(\\L|^)[their_last_name](\\L|$)", "i")))
				specific_listeners += candidate // Ditto
				to_remove_string += "[to_remove_string ? "|" : null][their_last_name]"

	if(!listeners.len)
		return
	if(to_remove_string)
		to_remove_string = "(\\L|^)([to_remove_string])(\\L|$)"
		message = replacetext(message, regex(to_remove_string, "i"), "")

	var/power_multiplier = base_multiplier * (user.mind?.assigned_role.voice_of_god_power || 1)

	//Cultists are closer to their gods and are more powerful, but they'll give themselves away
	if(is_cultie)
		power_multiplier *= 2

	//Now get the proper job titles and check for matches.
	var/job_message = get_full_job_name(message)
	for(var/mob/living/candidate in candidates)
		var/their_role = candidate.mind?.assigned_role.title
		if(their_role && findtext(job_message, their_role))
			specific_listeners |= candidate //focus on those with the specified job. "|=" instead "+=" so "Mrs. Capri the Captain" doesn't get affected twice.

	if(specific_listeners.len)
		listeners = specific_listeners
		power_multiplier *= (1 + (1/specific_listeners.len)) //2x on a single guy, 1.5x on two and so on

	for(var/datum/voice_of_god_command/command as anything in GLOB.voice_of_god_commands)
		if(findtext(message, command.trigger))
			. = command.execute(listeners, user, power_multiplier, message) || command.cooldown
			break

	if(!forced)
		message_admins("[ADMIN_LOOKUPFLW(user)] has said '[log_message]' with a Voice of God, affecting [english_list(listeners)], with a power multiplier of [power_multiplier].")
	log_game("[key_name(user)] has said '[log_message]' with a Voice of God[forced ? " forced by [forced]" : ""], affecting [english_list(listeners)], with a power multiplier of [power_multiplier].")
	SSblackbox.record_feedback("tally", "voice_of_god", 1, log_message)

/// Voice of god command datums that are used in [/proc/voice_of_god()]
/datum/voice_of_god_command
	///a text string or regex that triggers the command.
	var/trigger
	/// Is the trigger supposed to be a regex? If so, convert it to such on New()
	var/is_regex = TRUE
	/// cooldown variable which is normally returned to [proc/voice_of_god] and used as its return value.
	var/cooldown = COOLDOWN_MEME

/datum/voice_of_god_command/New()
	if(is_regex)
		trigger = regex(trigger)

/*
 * What happens when the command is triggered.
 * If a return value is set, it'll be used in place of the 'cooldown' var.
 * Args:
 * * listeners: the list of living mobs who are affected by the command.
 * * user: the one who casted Voice of God
 * * power_multiplier: multiplies the power of the command, most times.
 */
/datum/voice_of_god_command/proc/execute(list/listeners, mob/living/user, power_multiplier = 1, message)
	return

/// This command knocks the listeners down.
/datum/voice_of_god_command/knockdown
	trigger = "drop|fall|trip|knockdown"
	cooldown = COOLDOWN_STUN

/datum/voice_of_god_command/knockdown/execute(list/listeners, mob/living/user, power_multiplier = 1, message)
	for(var/mob/living/target as anything in listeners)
		target.Knockdown(4 SECONDS * power_multiplier)

/// This command stops the listeners from moving.
/datum/voice_of_god_command/immobilize
	trigger = "stop|wait|stand\\s*still|hold\\s*on|halt"
	cooldown = COOLDOWN_STUN

/datum/voice_of_god_command/immobilize/execute(list/listeners, mob/living/user, power_multiplier = 1, message)
	for(var/mob/living/target as anything in listeners)
		target.Immobilize(4 SECONDS * power_multiplier)

/// This command makes carbon listeners throw up like Mr. Creosote.
/datum/voice_of_god_command/vomit
	trigger = "vomit|throw\\s*up|sick"
	cooldown = COOLDOWN_STUN

/datum/voice_of_god_command/vomit/execute(list/listeners, mob/living/user, power_multiplier = 1, message)
	for(var/mob/living/carbon/target in listeners)
		target.vomit(10 * power_multiplier, distance = power_multiplier, stun = FALSE)

/// This command silences the listeners. Thrice as effective is the user is a mime or curator.
/datum/voice_of_god_command/silence
	trigger = "shut\\s*up|silence|be\\s*silent|ssh|quiet|hush"
	cooldown = COOLDOWN_STUN

/datum/voice_of_god_command/silence/execute(list/listeners, mob/living/user, power_multiplier = 1, message)
	power_multiplier *= user.mind?.assigned_role?.voice_of_god_silence_power || 1
	for(var/mob/living/carbon/target in listeners)
		target.silent += (10 * power_multiplier)

/// This command makes the listeners see others as corgis, carps, skellies etcetera etcetera.
/datum/voice_of_god_command/hallucinate
	trigger = "see\\s*the\\s*truth|hallucinate"

/datum/voice_of_god_command/hallucinate/execute(list/listeners, mob/living/user, power_multiplier = 1, message)
	for(var/mob/living/carbon/target in listeners)
		new /datum/hallucination/delusion(target, TRUE, null, 150 * power_multiplier, 0)

/// This command wakes up the listeners.
/datum/voice_of_god_command/wake_up
	trigger = "wake\\s*up|awaken"
	cooldown = COOLDOWN_DAMAGE

/datum/voice_of_god_command/wake_up/execute(list/listeners, mob/living/user, power_multiplier = 1, message)
	for(var/mob/living/target as anything in listeners)
		target.SetSleeping(0)

/// This command heals the listeners for 10 points of total damage.
/datum/voice_of_god_command/heal
	trigger = "live|heal|survive|mend|life|heroes\\s*never\\s*die"
	cooldown = COOLDOWN_DAMAGE

/datum/voice_of_god_command/heal/execute(list/listeners, mob/living/user, power_multiplier = 1, message)
	for(var/mob/living/target as anything in listeners)
		target.heal_overall_damage(10 * power_multiplier, 10 * power_multiplier)

/// This command applies 15 points of brute damage to the listeners. There's subtle theological irony in this being more powerful than healing.
/datum/voice_of_god_command/brute
	trigger = "die|suffer|hurt|pain|death"
	cooldown = COOLDOWN_DAMAGE

/datum/voice_of_god_command/brute/execute(list/listeners, mob/living/user, power_multiplier = 1, message)
	for(var/mob/living/target as anything in listeners)
		target.apply_damage(15 * power_multiplier, def_zone = BODY_ZONE_CHEST, wound_bonus = CANT_WOUND)

/// This command makes carbon listeners bleed from a random body part.
/datum/voice_of_god_command/bleed
	trigger = "bleed|there\\s*will\\s*be\\s*blood"
	cooldown = COOLDOWN_DAMAGE

/datum/voice_of_god_command/bleed/execute(list/listeners, mob/living/user, power_multiplier = 1, message)
	for(var/mob/living/carbon/human/target in listeners)
		var/obj/item/bodypart/chosen_part = pick(target.bodyparts)
		chosen_part.generic_bleedstacks += 5

/// This command sets the listeners ablaze.
/datum/voice_of_god_command/burn
	trigger = "burn|ignite"
	cooldown = COOLDOWN_DAMAGE

/datum/voice_of_god_command/burn/execute(list/listeners, mob/living/user, power_multiplier = 1, message)
	for(var/mob/living/target as anything in listeners)
		target.adjust_fire_stacks(1 * power_multiplier)
		target.IgniteMob()

/// This command heats the listeners up like boiling water.
/datum/voice_of_god_command/hot
	trigger = "heat|hot|hell"
	cooldown = COOLDOWN_DAMAGE

/datum/voice_of_god_command/hot/execute(list/listeners, mob/living/user, power_multiplier = 1, message)
	for(var/mob/living/target as anything in listeners)
		target.adjust_bodytemperature(50 * power_multiplier)

/// This command cools the listeners down like freezing water.
/datum/voice_of_god_command/cold
	trigger = "cold|chill|freeze"
	cooldown = COOLDOWN_DAMAGE

/datum/voice_of_god_command/cold/execute(list/listeners, mob/living/user, power_multiplier = 1, message)
	for(var/mob/living/target as anything in listeners)
		target.adjust_bodytemperature(-50 * power_multiplier)

/// This command throws the listeners away from the user.
/datum/voice_of_god_command/repulse
	trigger = "shoo|go\\s*away|leave\\s*me\\s*alone|begone|flee|fus\\s*ro\\s*dah|get\\s*away|repulse"
	cooldown = COOLDOWN_DAMAGE

/datum/voice_of_god_command/repulse/execute(list/listeners, mob/living/user, power_multiplier = 1, message)
	for(var/mob/living/target as anything in listeners)
		var/throwtarget = get_edge_target_turf(user, get_dir(user, get_step_away(target, user)))
		target.throw_at(throwtarget, 3 * power_multiplier, 1 * power_multiplier)

/// This command throws the listeners at the user.
/datum/voice_of_god_command/attract
	trigger = "come\\s*here|come\\s*to\\s*me|get\\s*over\\s*here|attract"
	cooldown = COOLDOWN_DAMAGE

/datum/voice_of_god_command/attract/execute(list/listeners, mob/living/user, power_multiplier = 1, message)
	for(var/mob/living/target as anything in listeners)
		target.throw_at(get_step_towards(user, target), 3 * power_multiplier, 1 * power_multiplier)

/// This command forces the listeners to say their true name (so masks and hoods won't help).
/datum/voice_of_god_command/who_are_you
	trigger = "who\\s*are\\s*you|say\\s*your\\s*name|state\\s*your\\s*name|identify"

/datum/voice_of_god_command/who_are_you/execute(list/listeners, mob/living/user, power_multiplier = 1, message)
	var/iteration = 1
	for(var/mob/living/target as anything in listeners)
		addtimer(CALLBACK(target, /atom/movable/proc/say, target.real_name), 0.5 SECONDS * iteration)
		iteration++

/// This command forces the listeners to say the user's name
/datum/voice_of_god_command/say_my_name
	trigger = "say\\s*my\\s*name|who\\s*am\\s*i"

/datum/voice_of_god_command/say_my_name/execute(list/listeners, mob/living/user, power_multiplier = 1, message)
	var/iteration = 1
	for(var/mob/living/target as anything in listeners)
		addtimer(CALLBACK(target, /atom/movable/proc/say, user.name), 0.5 SECONDS * iteration)
		iteration++

/// This command forces the listeners to say "Who's there?".
/datum/voice_of_god_command/knock_knock
	trigger = "knock\\s*knock"

/datum/voice_of_god_command/knock_knock/execute(list/listeners, mob/living/user, power_multiplier = 1, message)
	var/iteration = 1
	for(var/mob/living/target as anything in listeners)
		addtimer(CALLBACK(target, /atom/movable/proc/say, "Who's there?"), 0.5 SECONDS * iteration)
		iteration++

/// This command forces silicon listeners to state all their laws.
/datum/voice_of_god_command/state_laws
	trigger = "state\\s*(your)?\\s*laws"

/datum/voice_of_god_command/state_laws/execute(list/listeners, mob/living/user, power_multiplier = 1, message)
	var/iteration = 0
	for(var/mob/living/silicon/target in listeners)
		addtimer(CALLBACK(target, /mob/living/silicon/proc/statelaws, TRUE), (3 SECONDS * iteration) + 0.5 SECONDS)
		iteration++

/// This command forces the listeners to take step in a direction chosen by the user, otherwise a random cardinal one.
/datum/voice_of_god_command/move
	trigger = "move|walk"
	var/static/up_words = regex("up|north|fore")
	var/static/down_words = regex("down|south|aft")
	var/static/left_words = regex("left|west|port")
	var/static/right_words = regex("right|east|starboard")

/datum/voice_of_god_command/move/execute(list/listeners, mob/living/user, power_multiplier = 1, message)
	var/iteration = 1
	var/direction
	if(findtext(message, up_words))
		direction = NORTH
	else if(findtext(message, down_words))
		direction = SOUTH
	else if(findtext(message, left_words))
		direction = WEST
	else if(findtext(message, right_words))
		direction = EAST
	for(var/mob/living/target as anything in listeners)
		addtimer(CALLBACK(GLOBAL_PROC, .proc/_step, target, direction || pick(GLOB.cardinals)), 1 SECONDS * (iteration - 1))
		iteration++

/// This command forces the listeners to switch to walk intent.
/datum/voice_of_god_command/walk
	trigger = "slow\\s*down"

/datum/voice_of_god_command/walk/execute(list/listeners, mob/living/user, power_multiplier = 1, message)
	for(var/mob/living/target as anything in listeners)
		if(target.m_intent != MOVE_INTENT_WALK)
			target.toggle_move_intent()

/// This command forces the listeners to switch to run intent.
/datum/voice_of_god_command/run
	trigger = "run"
	is_regex = FALSE

/datum/voice_of_god_command/walk/execute(list/listeners, mob/living/user, power_multiplier = 1, message)
	for(var/mob/living/target as anything in listeners)
		if(target.m_intent != MOVE_INTENT_RUN)
			target.toggle_move_intent()

/// This command turns the listeners' throw mode on.
/datum/voice_of_god_command/throw_catch
	trigger = "throw|catch"

/datum/voice_of_god_command/throw_catch/execute(list/listeners, mob/living/user, power_multiplier = 1, message)
	for(var/mob/living/carbon/target in listeners)
		target.throw_mode_on(THROW_MODE_TOGGLE)

/// This command forces the listeners to say a brain damage line.
/datum/voice_of_god_command/speak
	trigger = "speak|say\\s*something"

/datum/voice_of_god_command/speak/execute(list/listeners, mob/living/user, power_multiplier = 1, message)
	var/iteration = 1
	for(var/mob/living/target in listeners)
		addtimer(CALLBACK(target, /atom/movable/proc/say, pick_list_replacements(BRAIN_DAMAGE_FILE, "brain_damage")), 0.5 SECONDS * iteration)
		iteration++

/// This command forces the listeners to get the fuck up, resetting all stuns.
/datum/voice_of_god_command/getup
	trigger = "get\\s*up"
	cooldown = COOLDOWN_DAMAGE

/datum/voice_of_god_command/getup/execute(list/listeners, mob/living/user, power_multiplier = 1, message)
	for(var/mob/living/target as anything in listeners)
		target.set_resting(FALSE)
		target.SetAllImmobility(0)

/// This command forces each listener to buckle to a chair found on the same tile.
/datum/voice_of_god_command/sit
	trigger = "sit"
	is_regex = FALSE

/datum/voice_of_god_command/sit/execute(list/listeners, mob/living/user, power_multiplier = 1, message)
	for(var/mob/living/target as anything in listeners)
		var/obj/structure/chair/chair = locate(/obj/structure/chair) in get_turf(target)
		chair?.buckle_mob(target)

/// This command forces each listener to unbuckle from whatever they are buckled to.
/datum/voice_of_god_command/stand
	trigger = "stand"
	is_regex = FALSE

/datum/voice_of_god_command/stand/execute(list/listeners, mob/living/user, power_multiplier = 1, message)
	for(var/mob/living/target as anything in listeners)
		target.buckled?.unbuckle_mob(target)

/// This command forces the listener to do the jump emote 3/4 of the times or reply "HOW HIGH?!!".
/datum/voice_of_god_command/jump
	trigger = "jump"
	is_regex = FALSE

/datum/voice_of_god_command/jump/execute(list/listeners, mob/living/user, power_multiplier = 1, message)
	var/iteration = 1
	for(var/mob/living/target as anything in listeners)
		if(prob(25))
			addtimer(CALLBACK(target, /atom/movable/proc/say, "HOW HIGH?!!"), 0.5 SECONDS * iteration)
		else
			addtimer(CALLBACK(target, /mob/living/.proc/emote, "jump"), 0.5 SECONDS * iteration)
		iteration++

///This command plays a bikehorn sound after 2 seconds and a half have passed, and also slips listeners if the user is a clown.
/datum/voice_of_god_command/honk
	trigger = "ho+nk"

/datum/voice_of_god_command/honk/execute(list/listeners, mob/living/user, power_multiplier = 1, message)
	addtimer(CALLBACK(GLOBAL_PROC, .proc/playsound, get_turf(user), 'sound/items/bikehorn.ogg', 300, 1), 2.5 SECONDS)
	if(is_clown_job(user.mind?.assigned_role))
		. = COOLDOWN_STUN //it slips.
		for(var/mob/living/carbon/target in listeners)
			target.slip(14 SECONDS * power_multiplier)

///This command spins the listeners 1800° degrees clockwise.
/datum/voice_of_god_command/multispin
	trigger = "like\\s*a\\s*record\\s*baby|right\\s*round"

/datum/voice_of_god_command/multispin/execute(list/listeners, mob/living/user, power_multiplier = 1, message)
	for(var/mob/living/target as anything in listeners)
		target.SpinAnimation(speed = 10, loops = 5)

/// Supertype of all those commands that make people emote and nothing else. Fuck copypasta.
/datum/voice_of_god_command/emote
	/// The emote to run.
	var/emote_name = "dance"

/datum/voice_of_god_command/emote/execute(list/listeners, mob/living/user, power_multiplier = 1, message)
	var/iteration = 1
	for(var/mob/living/target as anything in listeners)
		addtimer(CALLBACK(target, /mob/living/.proc/emote, emote_name), 0.5 SECONDS * iteration)
		iteration++

/datum/voice_of_god_command/emote/flip
	trigger = "flip|rotate|revolve|roll|somersault"
	emote_name = "flip"

/datum/voice_of_god_command/emote/dance
	trigger = "dance"
	is_regex = FALSE

/datum/voice_of_god_command/emote/salute
	trigger = "salute"
	emote_name = "salute"
	is_regex = FALSE

/datum/voice_of_god_command/emote/play_dead
	trigger = "play\\s*dead"
	emote_name = "deathgasp"

/datum/voice_of_god_command/emote/clap
	trigger = "clap|applaud"
	emote_name = "clap"

#undef COOLDOWN_STUN
#undef COOLDOWN_DAMAGE
#undef COOLDOWN_MEME
#undef COOLDOWN_NONE


code:
/// Creates a weakref to the given input.
/// See /datum/weakref's documentation for more information.
/proc/WEAKREF(datum/input)
	if(istype(input) && !QDELETED(input))
		if(istype(input, /datum/weakref))
			return input

		if(!input.weak_reference)
			input.weak_reference = new /datum/weakref(input)
		return input.weak_reference

/datum/proc/create_weakref() //Forced creation for admin proccalls
	return WEAKREF(src)

/**
 * A weakref holds a non-owning reference to a datum.
 * The datum can be referenced again using `resolve()`.
 *
 * To figure out why this is important, you must understand how deletion in
 * BYOND works.
 *
 * Imagine a datum as a TV in a living room. When one person enters to watch
 * TV, they turn it on. Others can come into the room and watch the TV.
 * When the last person leaves the room, they turn off the TV because it's
 * no longer being used.
 *
 * A datum being deleted tells everyone who's watching the TV to stop.
 * If everyone leaves properly (AKA cleaning up their references), then the
 * last person will turn off the TV, and everything is well.
 * However, if someone is resistant (holds a hard reference after deletion),
 * then someone has to walk in, drag them away, and turn off the TV forecefully.
 * This process is very slow, and it's known as hard deletion.
 *
 * This is where weak references come in. Weak references don't count as someone
 * watching the TV. Thus, when what it's referencing is destroyed, it will
 * hopefully clean up properly, and limit hard deletions.
 *
 * A common use case for weak references is holding onto what created itself.
 * For example, if a machine wanted to know what its last user was, it might
 * create a `var/mob/living/last_user`. However, this is a storng reference to
 * the mob, and thus will force a hard deletion when that mob is deleted.
 * It is often better in this case to instead create a weakref to the user,
 * meaning this type definition becomes `var/datum/weakref/last_user`.
 *
 * A good rule of thumb is that you should hold strong references to things
 * that you *own*. For example, a dog holding a chew toy would be the owner
 * of that chew toy, and thus a `var/obj/item/chew_toy` reference is fine
 * (as long as it is cleaned up properly).
 * However, a chew toy does not own its dog, so a `var/mob/living/dog/owner`
 * might be inferior to a weakref.
 * This is also a good rule of thumb to avoid circular references, such as the
 * chew toy example. A circular reference that doesn't clean itself up properly
 * will always hard delete.
 */
/datum/weakref
	var/reference

/datum/weakref/New(datum/thing)
	reference = REF(thing)

/datum/weakref/Destroy(force)
	var/datum/target = resolve()
	qdel(target)

	if(!force)
		return QDEL_HINT_LETMELIVE //Let BYOND autoGC thiswhen nothing is using it anymore.
	target?.weak_reference = null
	return ..()

/**
 * Retrieves the datum that this weakref is referencing.
 *
 * This will return `null` if the datum was deleted. This MUST be respected.
 */
/datum/weakref/proc/resolve()
	var/datum/D = locate(reference)
	return (!QDELETED(D) && D.weak_reference == src) ? D : null

repo_name: AnalWerewolf/sundown

license: mit


code:
/**
 * Causes weather to occur on a z level in certain area types
 *
 * The effects of weather occur across an entire z-level. For instance, lavaland has periodic ash storms that scorch most unprotected creatures.
 * Weather always occurs on different z levels at different times, regardless of weather type.
 * Can have custom durations, targets, and can automatically protect indoor areas.
 *
 */

/datum/weather
	/// name of weather
	var/name = "space wind"
	/// description of weather
	var/desc = "Heavy gusts of wind blanket the area, periodically knocking down anyone caught in the open."
	/// The message displayed in chat to foreshadow the weather's beginning
	var/telegraph_message = "The wind begins to pick up."
	/// In deciseconds, how long from the beginning of the telegraph until the weather begins
	var/telegraph_duration = 300
	/// The sound file played to everyone on an affected z-level
	var/telegraph_sound
	/// The overlay applied to all tiles on the z-level
	var/telegraph_overlay

	/// Displayed in chat once the weather begins in earnest
	var/weather_message = "The wind begins to blow ferociously!"
	/// In deciseconds, how long the weather lasts once it begins
	var/weather_duration = 1200
	/// See above - this is the lowest possible duration
	var/weather_duration_lower = 1200
	/// See above - this is the highest possible duration
	var/weather_duration_upper = 1500
	/// Looping sound while weather is occuring
	var/weather_sound
	/// Area overlay while the weather is occuring
	var/weather_overlay
	/// Color to apply to the area while weather is occuring
	var/weather_color = null

	/// Displayed once the weather is over
	var/end_message = "The wind relents its assault."
	/// In deciseconds, how long the "wind-down" graphic will appear before vanishing entirely
	var/end_duration = 300
	/// Sound that plays while weather is ending
	var/end_sound
	/// Area overlay while weather is ending
	var/end_overlay

	/// Types of area to affect
	var/area_type = /area/space
	/// TRUE value protects areas with outdoors marked as false, regardless of area type
	var/protect_indoors = FALSE
	/// Areas to be affected by the weather, calculated when the weather begins
	var/list/impacted_areas = list()
	/// Areas that are protected and excluded from the affected areas.
	var/list/protected_areas = list()
	/// The list of z-levels that this weather is actively affecting
	var/impacted_z_levels

	/// Since it's above everything else, this is the layer used by default. TURF_LAYER is below mobs and walls if you need to use that.
	var/overlay_layer = AREA_LAYER
	/// Plane for the overlay
	var/overlay_plane = ABOVE_LIGHTING_PLANE
	/// If the weather has no purpose other than looks
	var/aesthetic = FALSE
	/// Used by mobs (or movables containing mobs, such as enviro bags) to prevent them from being affected by the weather.
	var/immunity_type

	/// The stage of the weather, from 1-4
	var/stage = END_STAGE

	/// Weight amongst other eligible weather. If zero, will never happen randomly.
	var/probability = 0
	/// The z-level trait to affect when run randomly or when not overridden.
	var/target_trait = ZTRAIT_STATION

	/// Whether a barometer can predict when the weather will happen
	var/barometer_predictable = FALSE
	/// For barometers to know when the next storm will hit
	var/next_hit_time = 0
	/// This causes the weather to only end if forced to
	var/perpetual = FALSE

/datum/weather/New(z_levels)
	..()
	impacted_z_levels = z_levels

/**
 * Telegraphs the beginning of the weather on the impacted z levels
 *
 * Sends sounds and details to mobs in the area
 * Calculates duration and hit areas, and makes a callback for the actual weather to start
 *
 */
/datum/weather/proc/telegraph()
	if(stage == STARTUP_STAGE)
		return
	SEND_GLOBAL_SIGNAL(COMSIG_WEATHER_TELEGRAPH(type))
	stage = STARTUP_STAGE
	var/list/affectareas = list()
	for(var/V in get_areas(area_type))
		affectareas += V
	for(var/V in protected_areas)
		affectareas -= get_areas(V)
	for(var/V in affectareas)
		var/area/A = V
		if(protect_indoors && !A.outdoors)
			continue
		if(A.z in impacted_z_levels)
			impacted_areas |= A
	weather_duration = rand(weather_duration_lower, weather_duration_upper)
	START_PROCESSING(SSweather, src)
	update_areas()
	for(var/z_level in impacted_z_levels)
		for(var/mob/player as anything in SSmobs.clients_by_zlevel[z_level])
			var/turf/mob_turf = get_turf(player)
			if(!mob_turf)
				continue
			if(telegraph_message)
				to_chat(player, telegraph_message)
			if(telegraph_sound)
				SEND_SOUND(player, sound(telegraph_sound))
	addtimer(CALLBACK(src, .proc/start), telegraph_duration)

/**
 * Starts the actual weather and effects from it
 *
 * Updates area overlays and sends sounds and messages to mobs to notify them
 * Begins dealing effects from weather to mobs in the area
 *
 */
/datum/weather/proc/start()
	if(stage >= MAIN_STAGE)
		return
	SEND_GLOBAL_SIGNAL(COMSIG_WEATHER_START(type))
	stage = MAIN_STAGE
	update_areas()
	for(var/z_level in impacted_z_levels)
		for(var/mob/player as anything in SSmobs.clients_by_zlevel[z_level])
			var/turf/mob_turf = get_turf(player)
			if(!mob_turf)
				continue
			if(weather_message)
				to_chat(player, weather_message)
			if(weather_sound)
				SEND_SOUND(player, sound(weather_sound))
	if(!perpetual)
		addtimer(CALLBACK(src, .proc/wind_down), weather_duration)

/**
 * Weather enters the winding down phase, stops effects
 *
 * Updates areas to be in the winding down phase
 * Sends sounds and messages to mobs to notify them
 *
 */
/datum/weather/proc/wind_down()
	if(stage >= WIND_DOWN_STAGE)
		return
	SEND_GLOBAL_SIGNAL(COMSIG_WEATHER_WINDDOWN(type))
	stage = WIND_DOWN_STAGE
	update_areas()
	for(var/z_level in impacted_z_levels)
		for(var/mob/player as anything in SSmobs.clients_by_zlevel[z_level])
			var/turf/mob_turf = get_turf(player)
			if(!mob_turf)
				continue
			if(end_message)
				to_chat(player, end_message)
			if(end_sound)
				SEND_SOUND(player, sound(end_sound))
	addtimer(CALLBACK(src, .proc/end), end_duration)

/**
 * Fully ends the weather
 *
 * Effects no longer occur and area overlays are removed
 * Removes weather from processing completely
 *
 */
/datum/weather/proc/end()
	if(stage == END_STAGE)
		return
	SEND_GLOBAL_SIGNAL(COMSIG_WEATHER_END(type))
	stage = END_STAGE
	STOP_PROCESSING(SSweather, src)
	update_areas()

/**
 * Returns TRUE if the living mob can be affected by the weather
 *
 */
/datum/weather/proc/can_weather_act(mob/living/mob_to_check)
	var/turf/mob_turf = get_turf(mob_to_check)

	if(!mob_turf)
		return

	if(!(mob_turf.z in impacted_z_levels))
		return

	if((immunity_type && HAS_TRAIT(mob_to_check, immunity_type)) || HAS_TRAIT(mob_to_check, TRAIT_WEATHER_IMMUNE))
		return

	var/atom/loc_to_check = mob_to_check.loc
	while(loc_to_check != mob_turf)
		if((immunity_type && HAS_TRAIT(loc_to_check, immunity_type)) || HAS_TRAIT(loc_to_check, TRAIT_WEATHER_IMMUNE))
			return
		loc_to_check = loc_to_check.loc

	if(!(get_area(mob_to_check) in impacted_areas))
		return

	return TRUE

/**
 * Affects the mob with whatever the weather does
 *
 */
/datum/weather/proc/weather_act(mob/living/L)
	return

/**
 * Updates the overlays on impacted areas
 *
 */
/datum/weather/proc/update_areas()
	for(var/V in impacted_areas)
		var/area/N = V
		N.layer = overlay_layer
		N.plane = overlay_plane
		N.icon = 'icons/effects/weather_effects.dmi'
		N.color = weather_color
		switch(stage)
			if(STARTUP_STAGE)
				N.icon_state = telegraph_overlay
			if(MAIN_STAGE)
				N.icon_state = weather_overlay
			if(WIND_DOWN_STAGE)
				N.icon_state = end_overlay
			if(END_STAGE)
				N.color = null
				N.icon_state = ""
				N.icon = 'icons/turf/areas.dmi'
				N.layer = initial(N.layer)
				N.plane = initial(N.plane)
				N.set_opacity(FALSE)


code:
//Ash storms happen frequently on lavaland. They heavily obscure vision, and cause high fire damage to anyone caught outside.
/datum/weather/ash_storm
	name = "ash storm"
	desc = "An intense atmospheric storm lifts ash off of the planet's surface and billows it down across the area, dealing intense fire damage to the unprotected."

	telegraph_message = "An eerie moan rises on the wind. Sheets of burning ash blacken the horizon. Seek shelter."
	telegraph_duration = 300
	telegraph_overlay = "light_ash"

	weather_message = "Smoldering clouds of scorching ash billow down around you! Get inside!"
	weather_duration_lower = 600
	weather_duration_upper = 1200
	weather_overlay = "ash_storm"

	end_message = "The shrieking wind whips away the last of the ash and falls to its usual murmur. It should be safe to go outside now."
	end_duration = 300
	end_overlay = "light_ash"

	area_type = /area
	protect_indoors = TRUE
	target_trait = ZTRAIT_ASHSTORM

	immunity_type = TRAIT_ASHSTORM_IMMUNE

	probability = 90

	barometer_predictable = TRUE
	var/list/weak_sounds = list()
	var/list/strong_sounds = list()

/datum/weather/ash_storm/telegraph()
	var/list/eligible_areas = list()
	for (var/z in impacted_z_levels)
		eligible_areas += SSmapping.areas_in_z["[z]"]
	for(var/i in 1 to eligible_areas.len)
		var/area/place = eligible_areas[i]
		if(place.outdoors)
			weak_sounds[place] = /datum/looping_sound/weak_outside_ashstorm
			strong_sounds[place] = /datum/looping_sound/active_outside_ashstorm
		else
			weak_sounds[place] = /datum/looping_sound/weak_inside_ashstorm
			strong_sounds[place] = /datum/looping_sound/active_inside_ashstorm
		CHECK_TICK

	//We modify this list instead of setting it to weak/stron sounds in order to preserve things that hold a reference to it
	//It's essentially a playlist for a bunch of components that chose what sound to loop based on the area a player is in
	GLOB.ash_storm_sounds += weak_sounds
	return ..()

/datum/weather/ash_storm/start()
	GLOB.ash_storm_sounds -= weak_sounds
	GLOB.ash_storm_sounds += strong_sounds
	return ..()

/datum/weather/ash_storm/wind_down()
	GLOB.ash_storm_sounds -= strong_sounds
	GLOB.ash_storm_sounds += weak_sounds
	return ..()

/datum/weather/ash_storm/end()
	GLOB.ash_storm_sounds -= weak_sounds
	return ..()

/datum/weather/ash_storm/can_weather_act(mob/living/mob_to_check)
	. = ..()
	if(!. || !ishuman(mob_to_check))
		return
	var/mob/living/carbon/human/human_to_check = mob_to_check
	if(human_to_check.get_thermal_protection() >= FIRE_IMMUNITY_MAX_TEMP_PROTECT)
		return FALSE

/datum/weather/ash_storm/weather_act(mob/living/victim)
	victim.adjustFireLoss(4)

//Emberfalls are the result of an ash storm passing by close to the playable area of lavaland. They have a 10% chance to trigger in place of an ash storm.
/datum/weather/ash_storm/emberfall
	name = "emberfall"
	desc = "A passing ash storm blankets the area in harmless embers."

	weather_message = "Gentle embers waft down around you like grotesque snow. The storm seems to have passed you by..."
	weather_overlay = "light_ash"

	end_message = "The emberfall slows, stops. Another layer of hardened soot to the basalt beneath your feet."
	end_sound = null

	aesthetic = TRUE

	probability = 10


code:
//Causes fire damage to anyone not standing on a dense object.
/datum/weather/floor_is_lava
	name = "the floor is lava"
	desc = "The ground turns into surprisingly cool lava, lightly damaging anything on the floor."

	telegraph_message = "You feel the ground beneath you getting hot. Waves of heat distort the air."
	telegraph_duration = 150

	weather_message = "The floor is lava! Get on top of something!"
	weather_duration_lower = 300
	weather_duration_upper = 600
	weather_overlay = "lava"

	end_message = "The ground cools and returns to its usual form."
	end_duration = 0

	area_type = /area
	protected_areas = list(/area/space)
	target_trait = ZTRAIT_STATION

	overlay_layer = ABOVE_OPEN_TURF_LAYER //Covers floors only
	overlay_plane = FLOOR_PLANE
	immunity_type = TRAIT_LAVA_IMMUNE

/datum/weather/floor_is_lava/can_weather_act(mob/living/mob_to_check)
	if(!mob_to_check.client) //Only sentient people are going along with it!
		return FALSE
	. = ..()
	if(!. || issilicon(mob_to_check) || istype(mob_to_check.buckled, /obj/structure/bed))
		return FALSE
	var/turf/mob_turf = get_turf(mob_to_check)
	if(mob_turf.density) //Walls are not floors.
		return FALSE
	for(var/obj/structure/structure_to_check in mob_turf)
		if(structure_to_check.density)
			return FALSE
	if(mob_to_check.movement_type & FLYING)
		return FALSE

/datum/weather/floor_is_lava/weather_act(mob/living/victim)
	victim.adjustFireLoss(3)


code:
//Radiation storms occur when the station passes through an irradiated area, and irradiate anyone not standing in protected areas (maintenance, emergency storage, etc.)
/datum/weather/rad_storm
	name = "radiation storm"
	desc = "A cloud of intense radiation passes through the area dealing rad damage to those who are unprotected."

	telegraph_duration = 400
	telegraph_message = "The air begins to grow warm."

	weather_message = "You feel waves of heat wash over you! Find shelter!"
	weather_overlay = "ash_storm"
	weather_duration_lower = 600
	weather_duration_upper = 1500
	weather_color = "green"
	weather_sound = 'sound/misc/bloblarm.ogg'

	end_duration = 100
	end_message = "The air seems to be cooling off again."

	area_type = /area
	protected_areas = list(/area/maintenance, /area/ai_monitored/turret_protected/ai_upload, /area/ai_monitored/turret_protected/ai_upload_foyer, /area/ai_monitored/turret_protected/aisat/maint, /area/ai_monitored/command/storage/satellite,
	/area/ai_monitored/turret_protected/ai, /area/commons/storage/emergency/starboard, /area/commons/storage/emergency/port, /area/shuttle, /area/security/prison/safe, /area/security/prison/toilet)
	target_trait = ZTRAIT_STATION

	immunity_type = TRAIT_RADSTORM_IMMUNE

/datum/weather/rad_storm/telegraph()
	..()
	status_alarm(TRUE)

/datum/weather/rad_storm/weather_act(mob/living/L)
	var/resist = L.getarmor(null, RAD)
	if(prob(40))
		if(ishuman(L))
			var/mob/living/carbon/human/H = L
			if(H.dna && !HAS_TRAIT(H, TRAIT_GENELESS))
				if(prob(max(0,100-resist)))
					H.random_mutate_unique_identity()
					H.random_mutate_unique_features()
					if(prob(50))
						if(prob(90))
							H.easy_random_mutate(NEGATIVE+MINOR_NEGATIVE)
						else
							H.easy_random_mutate(POSITIVE)
						H.domutcheck()
		L.rad_act(20)

/datum/weather/rad_storm/end()
	if(..())
		return
	priority_announce("The radiation threat has passed. Please return to your workplaces.", "Anomaly Alert")
	status_alarm(FALSE)

/datum/weather/rad_storm/proc/status_alarm(active) //Makes the status displays show the radiation warning for those who missed the announcement.
	var/datum/radio_frequency/frequency = SSradio.return_frequency(FREQ_STATUS_DISPLAYS)
	if(!frequency)
		return

	var/datum/signal/signal = new
	if (active)
		signal.data["command"] = "alert"
		signal.data["picture_state"] = "radiation"
	else
		signal.data["command"] = "shuttle"

	var/atom/movable/virtualspeaker/virt = new(null)
	frequency.post_signal(virt, signal)


code:
/datum/weather/snow_storm
	name = "snow storm"
	desc = "Harsh snowstorms roam the topside of this arctic planet, burying any area unfortunate enough to be in its path."
	probability = 90

	telegraph_message = "Drifting particles of snow begin to dust the surrounding area.."
	telegraph_duration = 300
	telegraph_overlay = "light_snow"

	weather_message = "Harsh winds pick up as dense snow begins to fall from the sky! Seek shelter!"
	weather_overlay = "snow_storm"
	weather_duration_lower = 600
	weather_duration_upper = 1500

	end_duration = 100
	end_message = "The snowfall dies down, it should be safe to go outside again."

	area_type = /area
	protect_indoors = TRUE
	target_trait = ZTRAIT_SNOWSTORM

	immunity_type = TRAIT_SNOWSTORM_IMMUNE

	barometer_predictable = TRUE

/datum/weather/snow_storm/weather_act(mob/living/L)
	L.adjust_bodytemperature(-rand(5,15))


code:
/datum/weather/void_storm
	name = "void storm"
	desc = "A rare and highly anomalous event often accompanied by unknown entities shredding spacetime continouum. We'd advise you to start running."

	telegraph_duration = 2 SECONDS
	telegraph_overlay = "light_snow"

	weather_message = "You feel air around you getting colder... and void's sweet embrace..."
	weather_overlay = "snow_storm"
	weather_color = COLOR_BLACK
	weather_duration_lower = 60 SECONDS
	weather_duration_upper = 120 SECONDS

	end_duration = 10 SECONDS

	area_type = /area
	protect_indoors = FALSE
	target_trait = ZTRAIT_VOIDSTORM

	immunity_type = TRAIT_VOIDSTORM_IMMUNE

	barometer_predictable = FALSE
	perpetual = TRUE

/datum/weather/void_storm/can_weather_act(mob/living/mob_to_check)
	. = ..()
	if(IS_HERETIC(mob_to_check) || IS_HERETIC_MONSTER(mob_to_check))
		return FALSE

/datum/weather/void_storm/weather_act(mob/living/victim)
	victim.adjustOxyLoss(rand(1,3))
	victim.adjustFireLoss(rand(1,3))
	victim.adjust_blurriness(rand(0,1))
	victim.adjust_bodytemperature(-rand(5,15))


code:
#define MAXIMUM_EMP_WIRES 3

/proc/is_wire_tool(obj/item/I)
	if(!I)
		return

	if(I.tool_behaviour == TOOL_WIRECUTTER || I.tool_behaviour == TOOL_MULTITOOL)
		return TRUE
	if(istype(I, /obj/item/assembly))
		var/obj/item/assembly/A = I
		if(A.attachable)
			return TRUE

/atom/proc/attempt_wire_interaction(mob/user)
	if(!wires)
		return WIRE_INTERACTION_FAIL
	if(!user.CanReach(src))
		return WIRE_INTERACTION_FAIL
	wires.interact(user)
	return WIRE_INTERACTION_BLOCK

/datum/wires
	/// The holder (atom that contains these wires).
	var/atom/holder = null
	/// The holder's typepath (used for sanity checks to make sure the holder is the appropriate type for these wire sets).
	var/holder_type = null
	/// Key that enables wire assignments to be common across different holders. If null, will use the holder_type as a key.
	var/dictionary_key = null
	/// The display name for the wire set shown in station blueprints. Not shown in blueprints if randomize is TRUE or it's an item NT wouldn't know about (Explosives/Nuke). Also used in the hacking interface.
	var/proper_name = "Unknown"

	/// List of all wires.
	var/list/wires = list()
	/// List of cut wires.
	var/list/cut_wires = list() // List of wires that have been cut.
	/// Dictionary of colours to wire.
	var/list/colors = list()
	/// List of attached assemblies.
	var/list/assemblies = list()

	/// If every instance of these wires should be random. Prevents wires from showing up in station blueprints.
	var/randomize = FALSE

/datum/wires/New(atom/holder)
	..()
	if(!istype(holder, holder_type))
		CRASH("Wire holder is not of the expected type!")

	src.holder = holder

	// If there is a dictionary key set, we'll want to use that. Otherwise, use the holder type.
	var/key = dictionary_key ? dictionary_key : holder_type

	RegisterSignal(holder, COMSIG_PARENT_QDELETING, .proc/on_holder_qdel)
	if(randomize)
		randomize()
	else
		if(!GLOB.wire_color_directory[key])
			randomize()
			GLOB.wire_color_directory[key] = colors
			GLOB.wire_name_directory[key] = proper_name
		else
			colors = GLOB.wire_color_directory[key]

/datum/wires/Destroy()
	holder = null
	assemblies = list()
	return ..()

/datum/wires/proc/add_duds(duds)
	while(duds)
		var/dud = WIRE_DUD_PREFIX + "[--duds]"
		if(dud in wires)
			continue
		wires += dud

///Called when holder is qdeleted for us to clean ourselves as not to leave any unlawful references.
/datum/wires/proc/on_holder_qdel(atom/source, force)
	SIGNAL_HANDLER

	qdel(src)

/datum/wires/proc/randomize()
	var/static/list/possible_colors = list(
	"blue",
	"brown",
	"crimson",
	"cyan",
	"gold",
	"grey",
	"green",
	"magenta",
	"orange",
	"pink",
	"purple",
	"red",
	"silver",
	"violet",
	"white",
	"yellow"
	)

	var/list/my_possible_colors = possible_colors.Copy()

	for(var/wire in shuffle(wires))
		colors[pick_n_take(my_possible_colors)] = wire

/datum/wires/proc/shuffle_wires()
	colors.Cut()
	randomize()

/datum/wires/proc/repair()
	cut_wires.Cut()

/datum/wires/proc/get_wire(color)
	return colors[color]

/datum/wires/proc/get_color_of_wire(wire_type)
	for(var/color in colors)
		var/other_type = colors[color]
		if(wire_type == other_type)
			return color

/datum/wires/proc/get_attached(color)
	if(assemblies[color])
		return assemblies[color]
	return null

/datum/wires/proc/is_attached(color)
	if(assemblies[color])
		return TRUE

/datum/wires/proc/is_cut(wire)
	return (wire in cut_wires)

/datum/wires/proc/is_color_cut(color)
	return is_cut(get_wire(color))

/datum/wires/proc/is_all_cut()
	if(cut_wires.len == wires.len)
		return TRUE

/datum/wires/proc/is_dud(wire)
	return findtext(wire, WIRE_DUD_PREFIX, 1, length(WIRE_DUD_PREFIX) + 1)

/datum/wires/proc/is_dud_color(color)
	return is_dud(get_wire(color))

/datum/wires/proc/cut(wire)
	if(is_cut(wire))
		cut_wires -= wire
		on_cut(wire, mend = TRUE)
	else
		cut_wires += wire
		on_cut(wire, mend = FALSE)

/datum/wires/proc/cut_color(color)
	cut(get_wire(color))

/datum/wires/proc/cut_random()
	cut(wires[rand(1, wires.len)])

/datum/wires/proc/cut_all()
	for(var/wire in wires)
		cut(wire)

/datum/wires/proc/pulse(wire, user, force=FALSE)
	if(!force && is_cut(wire))
		return
	on_pulse(wire, user)

/datum/wires/proc/pulse_color(color, mob/living/user, force=FALSE)
	pulse(get_wire(color), user, force)

/datum/wires/proc/pulse_assembly(obj/item/assembly/S)
	for(var/color in assemblies)
		if(S == assemblies[color])
			pulse_color(color, force=TRUE)
			return TRUE

/datum/wires/proc/attach_assembly(color, obj/item/assembly/S)
	if(S && istype(S) && S.attachable && !is_attached(color))
		assemblies[color] = S
		S.forceMove(holder)
		S.connected = src
		return S

/datum/wires/proc/detach_assembly(color)
	var/obj/item/assembly/S = get_attached(color)
	if(S && istype(S))
		assemblies -= color
		S.connected = null
		S.forceMove(holder.drop_location())
		return S

/// Called from [/atom/proc/emp_act]
/datum/wires/proc/emp_pulse()
	var/list/possible_wires = shuffle(wires)
	var/remaining_pulses = MAXIMUM_EMP_WIRES

	for(var/wire in possible_wires)
		if(prob(33))
			pulse(wire)
			remaining_pulses--
			if(!remaining_pulses)
				break

// Overridable Procs
/datum/wires/proc/interactable(mob/user)
	SHOULD_CALL_PARENT(TRUE)
	if((SEND_SIGNAL(user, COMSIG_TRY_WIRES_INTERACT, holder) & COMPONENT_CANT_INTERACT_WIRES))
		return FALSE
	return TRUE

/datum/wires/proc/get_status()
	return list()

/datum/wires/proc/on_cut(wire, mend = FALSE)
	return

/datum/wires/proc/on_pulse(wire, user)
	return
// End Overridable Procs

/datum/wires/proc/interact(mob/user)
	if(!interactable(user))
		return
	ui_interact(user)
	for(var/A in assemblies)
		var/obj/item/I = assemblies[A]
		if(istype(I) && I.on_found(user))
			return

/**
 * Checks whether wire assignments should be revealed.
 *
 * Returns TRUE if the wires should be revealed, FALSE otherwise.
 * Currently checks for admin ghost AI, abductor multitool and blueprints.
 * Arguments:
 * * user - The mob to check when deciding whether to reveal wires.
 */
/datum/wires/proc/can_reveal_wires(mob/user)
	// Admin ghost can see a purpose of each wire.
	if(isAdminGhostAI(user))
		return TRUE

	// Same for anyone with an abductor multitool.
	if(user.is_holding_item_of_type(/obj/item/multitool/abductor))
		return TRUE

	// Station blueprints do that too, but only if the wires are not randomized.
	if(user.is_holding_item_of_type(/obj/item/areaeditor/blueprints) && !randomize)
		return TRUE
	//SEPTIC EDIT START
	if(GET_MOB_ATTRIBUTE_VALUE(user, SKILL_ELECTRONICS) >= 5)
		return TRUE
	//SEPTIC EDIT END

	return FALSE

/**
 * Whether the given wire should always be revealed.
 *
 * Intended to be overridden. Allows for forcing a wire's assignmenmt to always be revealed
 * in the hacking interface.
 * Arguments:
 * * color - Color string of the wire to check.
 */
/datum/wires/proc/always_reveal_wire(color)
	return FALSE

/datum/wires/ui_host()
	return holder

/datum/wires/ui_status(mob/user)
	if(interactable(user))
		return ..()
	return UI_CLOSE

/datum/wires/ui_state(mob/user)
	return GLOB.physical_state

/datum/wires/ui_interact(mob/user, datum/tgui/ui)
	ui = SStgui.try_update_ui(user, src, ui)
	if (!ui)
		ui = new(user, src, "Wires", "[holder.name] Wires")
		ui.open()

/datum/wires/ui_data(mob/user)
	var/list/data = list()
	var/list/payload = list()
	var/reveal_wires = can_reveal_wires(user)

	for(var/color in colors)
		payload.Add(list(list(
			"color" = color,
			"wire" = (((reveal_wires || always_reveal_wire(color)) && !is_dud_color(color)) ? get_wire(color) : null),
			"cut" = is_color_cut(color),
			"attached" = is_attached(color)
		)))
	data["wires"] = payload
	data["status"] = get_status()
	data["proper_name"] = (proper_name != "Unknown") ? proper_name : null
	return data

/* SEPTIC EDIT REMOVAL - MOVED TO MODULAR
/datum/wires/ui_act(action, params)
	. = ..()
	if(. || !interactable(usr))
		return
	var/target_wire = params["wire"]
	var/mob/living/L = usr
	var/obj/item/I
	switch(action)
		if("cut")
			I = L.is_holding_tool_quality(TOOL_WIRECUTTER)
			if(I || isAdminGhostAI(usr))
				if(I && holder)
					I.play_tool_sound(holder, 20)
				cut_color(target_wire)
				. = TRUE
			else
				to_chat(L, span_warning("You need wirecutters!"))
		if("pulse")
			I = L.is_holding_tool_quality(TOOL_MULTITOOL)
			if(I || isAdminGhostAI(usr))
				if(I && holder)
					I.play_tool_sound(holder, 20)
				pulse_color(target_wire, L)
				. = TRUE
			else
				to_chat(L, span_warning("You need a multitool!"))
		if("attach")
			if(is_attached(target_wire))
				I = detach_assembly(target_wire)
				if(I)
					L.put_in_hands(I)
					. = TRUE
			else
				I = L.get_active_held_item()
				if(istype(I, /obj/item/assembly))
					var/obj/item/assembly/A = I
					if(A.attachable)
						if(!L.temporarilyRemoveItemFromInventory(A))
							return
						if(!attach_assembly(target_wire, A))
							A.forceMove(L.drop_location())
						. = TRUE
					else
						to_chat(L, span_warning("You need an attachable assembly!"))
*/
#undef MAXIMUM_EMP_WIRES


code:
/datum/wires/airalarm
	holder_type = /obj/machinery/airalarm
	proper_name = "Air Alarm"

/datum/wires/airalarm/New(atom/holder)
	wires = list(
		WIRE_POWER,
		WIRE_IDSCAN, WIRE_AI,
		WIRE_PANIC, WIRE_ALARM
	)
	add_duds(3)
	..()

/datum/wires/airalarm/interactable(mob/user)
	if(!..())
		return FALSE
	var/obj/machinery/airalarm/A = holder
	if(A.panel_open && A.buildstage == 2)
		return TRUE

/datum/wires/airalarm/get_status()
	var/obj/machinery/airalarm/A = holder
	var/list/status = list()
	status += "The interface light is [A.locked ? "red" : "green"]."
	status += "The short indicator is [A.shorted ? "lit" : "off"]."
	status += "The AI connection light is [!A.aidisabled ? "on" : "off"]."
	return status

/datum/wires/airalarm/on_pulse(wire)
	var/obj/machinery/airalarm/A = holder
	switch(wire)
		if(WIRE_POWER) // Short out for a long time.
			if(!A.shorted)
				A.shorted = TRUE
				A.update_appearance()
			addtimer(CALLBACK(A, /obj/machinery/airalarm.proc/reset, wire), 1200)
		if(WIRE_IDSCAN) // Toggle lock.
			A.locked = !A.locked
		if(WIRE_AI) // Disable AI control for a while.
			if(!A.aidisabled)
				A.aidisabled = TRUE
			addtimer(CALLBACK(A, /obj/machinery/airalarm.proc/reset, wire), 100)
		if(WIRE_PANIC) // Toggle panic siphon.
			if(!A.shorted)
				if(A.mode == 1) // AALARM_MODE_SCRUB
					A.mode = 3 // AALARM_MODE_PANIC
				else
					A.mode = 1 // AALARM_MODE_SCRUB
				A.apply_mode(usr)
		if(WIRE_ALARM) // Clear alarms.
			if(A.alarm_manager.clear_alarm(ALARM_ATMOS))
				A.post_alert(0)
			A.update_appearance()

/datum/wires/airalarm/on_cut(wire, mend)
	var/obj/machinery/airalarm/A = holder
	switch(wire)
		if(WIRE_POWER) // Short out forever.
			A.shock(usr, 50)
			A.shorted = !mend
			A.update_appearance()
		if(WIRE_IDSCAN)
			if(!mend)
				A.locked = TRUE
		if(WIRE_AI)
			A.aidisabled = mend // Enable/disable AI control.
		if(WIRE_PANIC) // Force panic syphon on.
			if(!mend && !A.shorted)
				A.mode = 3 // AALARM_MODE_PANIC
				A.apply_mode(usr)
		if(WIRE_ALARM) // Post alarm.
			if(A.alarm_manager.send_alarm(ALARM_ATMOS))
				A.post_alert(2)
			A.update_appearance()


code:
#define AI_WIRE_NORMAL 0
#define AI_WIRE_DISABLED 1
#define AI_WIRE_HACKED 2
#define AI_WIRE_DISABLED_HACKED -1

/datum/wires/airlock
	holder_type = /obj/machinery/door/airlock
	proper_name = "Generic Airlock"

/datum/wires/airlock/secure
	proper_name = "High Security Airlock"
	randomize = TRUE

/datum/wires/airlock/maint
	dictionary_key = /datum/wires/airlock/maint
	proper_name = "Maintenance Airlock"

/datum/wires/airlock/command
	dictionary_key = /datum/wires/airlock/command
	proper_name = "Command Airlock"

/datum/wires/airlock/service
	dictionary_key = /datum/wires/airlock/service
	proper_name = "Service Airlock"

/datum/wires/airlock/security
	dictionary_key = /datum/wires/airlock/security
	proper_name = "Security Airlock"

/datum/wires/airlock/engineering
	dictionary_key = /datum/wires/airlock/engineering
	proper_name = "Engineering Airlock"

/datum/wires/airlock/medbay
	dictionary_key = /datum/wires/airlock/medbay
	proper_name = "Medbay Airlock"

/datum/wires/airlock/science
	dictionary_key = /datum/wires/airlock/science
	proper_name = "Science Airlock"

/datum/wires/airlock/ai
	dictionary_key = /datum/wires/airlock/ai
	proper_name = "AI Airlock"

/datum/wires/airlock/New(atom/holder)
	wires = list(
		WIRE_POWER1, WIRE_POWER2,
		WIRE_BACKUP1, WIRE_BACKUP2,
		WIRE_OPEN, WIRE_BOLTS, WIRE_IDSCAN, WIRE_AI,
		WIRE_SHOCK, WIRE_SAFETY, WIRE_TIMING, WIRE_LIGHT,
		WIRE_ZAP1, WIRE_ZAP2
	)
	add_duds(2)
	..()

/datum/wires/airlock/interact(mob/user)
	var/obj/machinery/door/airlock/airlock_holder = holder
	if (!issilicon(user) && airlock_holder.isElectrified() && airlock_holder.shock(user, 100))
		return

	return ..()

/datum/wires/airlock/interactable(mob/user)
	if(!..())
		return FALSE
	var/obj/machinery/door/airlock/A = holder
	if(!issilicon(user) && A.isElectrified())
		var/mob/living/carbon/carbon_user = user
		if (!istype(carbon_user) || carbon_user.should_electrocute(src))
			return FALSE

	if(A.panel_open)
		return TRUE

/datum/wires/airlock/get_status()
	var/obj/machinery/door/airlock/A = holder
	var/list/status = list()
	status += "The door bolts [A.locked ? "have fallen!" : "look up."]"
	status += "The test light is [A.hasPower() ? "on" : "off"]."
	status += "The AI connection light is [A.aiControlDisabled || (A.obj_flags & EMAGGED) ? "off" : "on"]."
	status += "The check wiring light is [A.safe ? "off" : "on"]."
	status += "The timer is powered [A.autoclose ? "on" : "off"]."
	status += "The speed light is [A.normalspeed ? "on" : "off"]."
	status += "The emergency light is [A.emergency ? "on" : "off"]."
	return status

/datum/wires/airlock/on_pulse(wire)
	set waitfor = FALSE
	var/obj/machinery/door/airlock/A = holder
	switch(wire)
		if(WIRE_POWER1, WIRE_POWER2) // Pulse to loose power.
			A.loseMainPower()
		if(WIRE_BACKUP1, WIRE_BACKUP2) // Pulse to loose backup power.
			A.loseBackupPower()
		if(WIRE_OPEN) // Pulse to open door (only works not emagged and ID wire is cut or no access is required).
			if(A.obj_flags & EMAGGED)
				return
			if(!A.requiresID() || A.check_access(null))
				if(A.density)
					INVOKE_ASYNC(A, /obj/machinery/door/airlock.proc/open, 1)
				else
					INVOKE_ASYNC(A, /obj/machinery/door/airlock.proc/close, 1)
		if(WIRE_BOLTS) // Pulse to toggle bolts (but only raise if power is on).
			if(!A.locked)
				A.bolt()
			else
				if(A.hasPower())
					A.unbolt()
			A.update_appearance()
		if(WIRE_IDSCAN) // Pulse to disable emergency access and flash red lights.
			if(A.hasPower() && A.density)
				A.do_animate("deny")
				if(A.emergency)
					A.emergency = FALSE
					A.update_appearance()
		if(WIRE_AI) // Pulse to disable WIRE_AI control for 10 ticks (follows same rules as cutting).
			if(A.aiControlDisabled == AI_WIRE_NORMAL)
				A.aiControlDisabled = AI_WIRE_DISABLED
			else if(A.aiControlDisabled == AI_WIRE_DISABLED_HACKED)
				A.aiControlDisabled = AI_WIRE_HACKED
			addtimer(CALLBACK(A, /obj/machinery/door/airlock.proc/reset_ai_wire), 1 SECONDS)
		if(WIRE_SHOCK) // Pulse to shock the door for 10 ticks.
			if(!A.secondsElectrified)
				A.set_electrified(MACHINE_DEFAULT_ELECTRIFY_TIME, usr)
			A.shock(usr, 100)
		if(WIRE_SAFETY)
			A.safe = !A.safe
			if(!A.density)
				A.close()
		if(WIRE_TIMING)
			A.normalspeed = !A.normalspeed
		if(WIRE_LIGHT)
			A.lights = !A.lights
			A.update_appearance()

/obj/machinery/door/airlock/proc/reset_ai_wire()
	if(aiControlDisabled == AI_WIRE_DISABLED)
		aiControlDisabled = AI_WIRE_NORMAL
	else if(aiControlDisabled == AI_WIRE_HACKED)
		aiControlDisabled = AI_WIRE_DISABLED_HACKED

/datum/wires/airlock/on_cut(wire, mend)
	var/obj/machinery/door/airlock/A = holder
	switch(wire)
		if(WIRE_POWER1, WIRE_POWER2) // Cut to loose power, repair all to gain power.
			if(mend && !is_cut(WIRE_POWER1) && !is_cut(WIRE_POWER2))
				A.regainMainPower()
			else
				A.loseMainPower()
			if(isliving(usr))
				A.shock(usr, 50)
		if(WIRE_BACKUP1, WIRE_BACKUP2) // Cut to loose backup power, repair all to gain backup power.
			if(mend && !is_cut(WIRE_BACKUP1) && !is_cut(WIRE_BACKUP2))
				A.regainBackupPower()
			else
				A.loseBackupPower()
			if(isliving(usr))
				A.shock(usr, 50)
		if(WIRE_BOLTS) // Cut to drop bolts, mend does nothing.
			if(!mend)
				A.bolt()
		if(WIRE_AI) // Cut to disable WIRE_AI control, mend to re-enable.
			if(mend)
				if(A.aiControlDisabled == AI_WIRE_DISABLED) // 0 = normal, 1 = locked out, 2 = overridden by WIRE_AI, -1 = previously overridden by WIRE_AI
					A.aiControlDisabled = AI_WIRE_NORMAL
				else if(A.aiControlDisabled == AI_WIRE_HACKED)
					A.aiControlDisabled = AI_WIRE_DISABLED_HACKED
			else
				if(A.aiControlDisabled == AI_WIRE_NORMAL)
					A.aiControlDisabled = AI_WIRE_DISABLED
				else if(A.aiControlDisabled == AI_WIRE_DISABLED_HACKED)
					A.aiControlDisabled = AI_WIRE_HACKED
		if(WIRE_SHOCK) // Cut to shock the door, mend to unshock.
			if(mend)
				if(A.secondsElectrified)
					A.set_electrified(MACHINE_NOT_ELECTRIFIED, usr)
			else
				if(A.secondsElectrified != MACHINE_ELECTRIFIED_PERMANENT)
					A.set_electrified(MACHINE_ELECTRIFIED_PERMANENT, usr)
				A.shock(usr, 100)
		if(WIRE_SAFETY) // Cut to disable safeties, mend to re-enable.
			A.safe = mend
		if(WIRE_TIMING) // Cut to disable auto-close, mend to re-enable.
			A.autoclose = mend
			if(A.autoclose && !A.density)
				A.close()
		if(WIRE_LIGHT) // Cut to disable lights, mend to re-enable.
			A.lights = mend
			A.update_appearance()
		if(WIRE_ZAP1, WIRE_ZAP2) // Ouch.
			if(isliving(usr))
				A.shock(usr, 50)

/datum/wires/airlock/can_reveal_wires(mob/user)
	if(HAS_TRAIT(user, TRAIT_KNOW_ENGI_WIRES))
		return TRUE

	return ..()


code:
/datum/wires/apc
	holder_type = /obj/machinery/power/apc
	proper_name = "APC"

/datum/wires/apc/New(atom/holder)
	wires = list(
		WIRE_POWER1, WIRE_POWER2,
		WIRE_IDSCAN, WIRE_AI
	)
	add_duds(6)
	..()

/datum/wires/apc/interactable(mob/user)
	if(!..())
		return FALSE
	var/obj/machinery/power/apc/A = holder
	if(A.panel_open && !A.opened)
		return TRUE

/datum/wires/apc/get_status()
	var/obj/machinery/power/apc/A = holder
	var/list/status = list()
	status += "The interface light is [A.locked ? "red" : "green"]."
	status += "The short indicator is [A.shorted ? "lit" : "off"]."
	status += "The AI connection light is [!A.aidisabled ? "on" : "off"]."
	return status

/datum/wires/apc/on_pulse(wire)
	var/obj/machinery/power/apc/A = holder
	switch(wire)
		if(WIRE_POWER1, WIRE_POWER2) // Short for a long while.
			if(!A.shorted)
				A.shorted = TRUE
				addtimer(CALLBACK(A, /obj/machinery/power/apc.proc/reset, wire), 2 MINUTES)
		if(WIRE_IDSCAN) // Unlock for a little while.
			A.locked = FALSE
			addtimer(CALLBACK(A, /obj/machinery/power/apc.proc/reset, wire), 30 SECONDS)
		if(WIRE_AI) // Disable AI control for a very short time.
			if(!A.aidisabled)
				A.aidisabled = TRUE
				addtimer(CALLBACK(A, /obj/machinery/power/apc.proc/reset, wire), 1 SECONDS)

/datum/wires/apc/on_cut(index, mend)
	var/obj/machinery/power/apc/A = holder
	switch(index)
		if(WIRE_POWER1, WIRE_POWER2) // Short out.
			if(mend && !is_cut(WIRE_POWER1) && !is_cut(WIRE_POWER2))
				A.shorted = FALSE
			else
				A.shorted = TRUE
			A.shock(usr, 50)
		if(WIRE_AI) // Disable AI control.
			A.aidisabled = !mend

/datum/wires/apc/can_reveal_wires(mob/user)
	if(HAS_TRAIT(user, TRAIT_KNOW_ENGI_WIRES))
		return TRUE

	return ..()


code:
/datum/wires/autolathe
	holder_type = /obj/machinery/autolathe
	proper_name = "Autolathe"

/datum/wires/autolathe/New(atom/holder)
	wires = list(
		WIRE_HACK, WIRE_DISABLE,
		WIRE_SHOCK, WIRE_ZAP
	)
	add_duds(6)
	..()

/datum/wires/autolathe/interactable(mob/user)
	if(!..())
		return FALSE
	var/obj/machinery/autolathe/A = holder
	if(A.panel_open)
		return TRUE

/datum/wires/autolathe/get_status()
	var/obj/machinery/autolathe/A = holder
	var/list/status = list()
	status += "The red light is [A.disabled ? "on" : "off"]."
	status += "The blue light is [A.hacked ? "on" : "off"]."
	return status

/datum/wires/autolathe/on_pulse(wire)
	var/obj/machinery/autolathe/A = holder
	switch(wire)
		if(WIRE_HACK)
			A.adjust_hacked(!A.hacked)
			addtimer(CALLBACK(A, /obj/machinery/autolathe.proc/reset, wire), 60)
		if(WIRE_SHOCK)
			A.shocked = !A.shocked
			addtimer(CALLBACK(A, /obj/machinery/autolathe.proc/reset, wire), 60)
		if(WIRE_DISABLE)
			A.disabled = !A.disabled
			addtimer(CALLBACK(A, /obj/machinery/autolathe.proc/reset, wire), 60)

/datum/wires/autolathe/on_cut(wire, mend)
	var/obj/machinery/autolathe/A = holder
	switch(wire)
		if(WIRE_HACK)
			A.adjust_hacked(!mend)
		if(WIRE_SHOCK)
			A.shocked = !mend
		if(WIRE_DISABLE)
			A.disabled = !mend
		if(WIRE_ZAP)
			A.shock(usr, 50)


code:
/datum/wires/conveyor
	holder_type = /obj/machinery/conveyor_switch
	proper_name = "Conveyor"
	/// var holder that logs who put the assembly inside and gets transfered to the switch on pulse
	var/mob/fingerman

/datum/wires/conveyor/New(atom/holder)
	add_duds(1)
	..()

/datum/wires/conveyor/on_pulse(wire)
	var/obj/machinery/conveyor_switch/C = holder
	C.interact(fingerman)

/datum/wires/conveyor/interactable(mob/user)
	if(!..())
		return FALSE
	fingerman = user
	return TRUE


code:
/datum/wires/ecto_sniffer
	proper_name = "Ectoscopic Sniffer"
	randomize = TRUE //Only one wire don't need blueprints
	holder_type = /obj/machinery/ecto_sniffer

/datum/wires/ecto_sniffer/New(atom/holder)
	wires = list(WIRE_ACTIVATE)
	..()

/datum/wires/ecto_sniffer/on_pulse(wire)
	var/obj/machinery/ecto_sniffer/our_sniffer = holder
	our_sniffer.activate()
	..()

/datum/wires/ecto_sniffer/on_cut(wire, mend)
	var/obj/machinery/ecto_sniffer/our_sniffer = holder
	our_sniffer.sensor_enabled = mend


code:
/datum/wires/emitter
	holder_type = /obj/machinery/power/emitter
	proper_name = "Emitter"

/datum/wires/emitter/New(atom/holder)
	wires = list(WIRE_ZAP,WIRE_HACK)
	..()

/datum/wires/emitter/on_pulse(wire)
	var/obj/machinery/power/emitter/E = holder
	switch(wire)
		if(WIRE_ZAP)
			E.fire_beam_pulse()
		if(WIRE_HACK)
			E.mode = !E.mode
			E.set_projectile()
	..()


code:
/datum/wires/explosive
	var/duds_number = 2 // All "dud" wires cause an explosion when cut or pulsed
	proper_name = "Explosive Device"
	randomize = TRUE // Prevents wires from showing up on blueprints

/datum/wires/explosive/New(atom/holder)
	add_duds(duds_number) // Duds also explode here.
	..()

/datum/wires/explosive/on_pulse(index)
	explode()

/datum/wires/explosive/on_cut(index, mend)
	explode()

/datum/wires/explosive/proc/explode()
	return

/datum/wires/explosive/chem_grenade
	duds_number = 1
	holder_type = /obj/item/grenade/chem_grenade
	var/fingerprint

/datum/wires/explosive/chem_grenade/interactable(mob/user)
	if(!..())
		return FALSE
	var/obj/item/grenade/chem_grenade/G = holder
	if(G.stage == GRENADE_WIRED)
		return TRUE

/datum/wires/explosive/chem_grenade/on_pulse(index)
	var/obj/item/grenade/chem_grenade/grenade = holder
	if(grenade.stage != GRENADE_READY)
		return
	. = ..()

/datum/wires/explosive/chem_grenade/on_cut(index, mend)
	var/obj/item/grenade/chem_grenade/grenade = holder
	if(grenade.stage != GRENADE_READY)
		return
	. = ..()

/datum/wires/explosive/chem_grenade/attach_assembly(color, obj/item/assembly/S)
	if(istype(S,/obj/item/assembly/timer))
		var/obj/item/grenade/chem_grenade/G = holder
		var/obj/item/assembly/timer/T = S
		G.det_time = T.saved_time*10
	else if(istype(S,/obj/item/assembly/prox_sensor))
		var/obj/item/grenade/chem_grenade/G = holder
		G.landminemode = S
		S.proximity_monitor.wire = TRUE
	fingerprint = S.fingerprintslast
	return ..()

/datum/wires/explosive/chem_grenade/explode()
	var/obj/item/grenade/chem_grenade/G = holder
	var/obj/item/assembly/assembly = get_attached(get_wire(1))
	message_admins("\An [assembly] has pulsed a grenade, which was installed by [fingerprint].")
	log_game("\An [assembly] has pulsed a grenade, which was installed by [fingerprint].")
	var/mob/M = get_mob_by_ckey(fingerprint)
	G.log_grenade(M) //Used in arm_grenade() too but this one conveys where the mob who triggered the bomb is
	if(G.landminemode)
		G.detonate() ///already armed
	else
		G.arm_grenade() //The one here conveys where the bomb was when it went boom

/datum/wires/explosive/chem_grenade/detach_assembly(color)
	var/obj/item/assembly/S = get_attached(color)
	if(S && istype(S))
		assemblies -= color
		S.connected = null
		S.forceMove(holder.drop_location())
		var/obj/item/grenade/chem_grenade/G = holder
		G.landminemode = null
		return S

/datum/wires/explosive/c4 // Also includes X4
	holder_type = /obj/item/grenade/c4

/datum/wires/explosive/c4/explode()
	var/obj/item/grenade/c4/P = holder
	P.detonate()

/datum/wires/explosive/pizza
	holder_type = /obj/item/pizzabox

/datum/wires/explosive/pizza/New(atom/holder)
	wires = list(
		WIRE_DISARM
	)
	add_duds(3) // Duds also explode here.
	..()

/datum/wires/explosive/pizza/interactable(mob/user)
	if(!..())
		return FALSE
	var/obj/item/pizzabox/P = holder
	if(P.open && P.bomb)
		return TRUE

/datum/wires/explosive/pizza/get_status()
	var/obj/item/pizzabox/P = holder
	var/list/status = list()
	status += "The red light is [P.bomb_active ? "on" : "off"]."
	status += "The green light is [P.bomb_defused ? "on": "off"]."
	return status

/datum/wires/explosive/pizza/on_pulse(wire)
	var/obj/item/pizzabox/P = holder
	switch(wire)
		if(WIRE_DISARM) // Pulse to toggle
			P.bomb_defused = !P.bomb_defused
		else // Boom
			explode()

/datum/wires/explosive/pizza/on_cut(wire, mend)
	var/obj/item/pizzabox/P = holder
	switch(wire)
		if(WIRE_DISARM) // Disarm and untrap the box.
			if(!mend)
				P.bomb_defused = TRUE
		else
			if(!mend && !P.bomb_defused)
				explode()

/datum/wires/explosive/pizza/explode()
	var/obj/item/pizzabox/P = holder
	P.bomb.detonate()

/datum/wires/explosive/gibtonite
	holder_type = /obj/item/gibtonite

/datum/wires/explosive/gibtonite/explode()
	var/obj/item/gibtonite/P = holder
	P.GibtoniteReaction(null, 2)


code:
/datum/wires/microwave
	holder_type = /obj/machinery/microwave
	proper_name = "Microwave"

/datum/wires/microwave/New(atom/holder)
	wires = list(
		WIRE_ACTIVATE
	)
	..()

/datum/wires/microwave/interactable(mob/user)
	if(!..())
		return FALSE
	. = FALSE
	var/obj/machinery/microwave/M = holder
	if(M.panel_open)
		. = TRUE

/datum/wires/microwave/on_pulse(wire)
	var/obj/machinery/microwave/M = holder
	switch(wire)
		if(WIRE_ACTIVATE)
			M.cook()

/datum/wires/microwave/on_cut(wire, mend)
	var/obj/machinery/microwave/M = holder
	switch(wire)
		if(WIRE_ACTIVATE)
			M.wire_disabled = !mend


code:
#define FAST_MOTOR_SPEED 1
#define AVERAGE_MOTOR_SPEED 2
#define SLOW_MOTOR_SPEED 3

/datum/wires/mulebot
	holder_type = /mob/living/simple_animal/bot/mulebot
	proper_name = "Mulebot"
	randomize = TRUE

/datum/wires/mulebot/New(atom/holder)
	wires = list(
		WIRE_POWER1, WIRE_POWER2,
		WIRE_AVOIDANCE, WIRE_LOADCHECK,
		WIRE_MOTOR1, WIRE_MOTOR2,
		WIRE_RX, WIRE_TX, WIRE_BEACON
	)
	..()

/datum/wires/mulebot/interactable(mob/user)
	if(!..())
		return FALSE
	var/mob/living/simple_animal/bot/mulebot/mule = holder
	if(mule.open)
		return TRUE

/datum/wires/mulebot/on_cut(wire, mend)
	var/mob/living/simple_animal/bot/mulebot/mule = holder
	switch(wire)
		if(WIRE_MOTOR1, WIRE_MOTOR2)
			if(is_cut(WIRE_MOTOR1) && is_cut(WIRE_MOTOR2))
				ADD_TRAIT(mule, TRAIT_IMMOBILIZED, MOTOR_LACK_TRAIT)
			else
				REMOVE_TRAIT(mule, TRAIT_IMMOBILIZED, MOTOR_LACK_TRAIT)

			if(is_cut(WIRE_MOTOR1))
				mule.set_varspeed(FAST_MOTOR_SPEED)
			else if(is_cut(WIRE_MOTOR2))
				mule.set_varspeed(AVERAGE_MOTOR_SPEED)
			else
				mule.set_varspeed(SLOW_MOTOR_SPEED)

/datum/wires/mulebot/on_pulse(wire)
	var/mob/living/simple_animal/bot/mulebot/mule = holder
	if(!mule.has_power(TRUE))
		return //logically mulebots can't flash and beep if they don't have power.
	switch(wire)
		if(WIRE_POWER1, WIRE_POWER2)
			holder.visible_message(span_notice("[icon2html(mule, viewers(holder))] The charge light flickers."))
		if(WIRE_AVOIDANCE)
			holder.visible_message(span_notice("[icon2html(mule, viewers(holder))] The external warning lights flash briefly."))
			flick("[mule.base_icon]1", mule)
		if(WIRE_LOADCHECK)
			holder.visible_message(span_notice("[icon2html(mule, viewers(holder))] The load platform clunks."))
		if(WIRE_MOTOR1, WIRE_MOTOR2)
			holder.visible_message(span_notice("[icon2html(mule, viewers(holder))] The drive motor whines briefly."))
		else
			holder.visible_message(span_notice("[icon2html(mule, viewers(holder))] You hear a radio crackle."))

#undef FAST_MOTOR_SPEED
#undef AVERAGE_MOTOR_SPEED
#undef SLOW_MOTOR_SPEED


code:
/datum/wires/rnd
	holder_type = /obj/machinery/rnd
	proper_name = "R&D Machinery"
	randomize = TRUE

/datum/wires/rnd/New(atom/holder)
	wires = list(
		WIRE_HACK, WIRE_DISABLE,
		WIRE_SHOCK
	)
	add_duds(5)
	..()

/datum/wires/rnd/interactable(mob/user)
	if(!..())
		return FALSE
	var/obj/machinery/rnd/R = holder
	if(R.panel_open)
		return TRUE

/datum/wires/rnd/get_status()
	var/obj/machinery/rnd/R = holder
	var/list/status = list()
	status += "The red light is [R.disabled ? "off" : "on"]."
	status += "The blue light is [R.hacked ? "off" : "on"]."
	return status

/datum/wires/rnd/on_pulse(wire)
	set waitfor = FALSE
	var/obj/machinery/rnd/R = holder
	switch(wire)
		if(WIRE_HACK)
			R.hacked = !R.hacked
		if(WIRE_DISABLE)
			R.disabled = !R.disabled
/datum/wires/rnd/on_cut(wire, mend)
	var/obj/machinery/rnd/R = holder
	switch(wire)
		if(WIRE_HACK)
			R.hacked = !mend
		if(WIRE_DISABLE)
			R.disabled = !mend


code:
/datum/wires/radio
	holder_type = /obj/item/radio
	proper_name = "Radio"

/datum/wires/radio/New(atom/holder)
	wires = list(
		WIRE_SIGNAL,
		WIRE_RX, WIRE_TX
	)
	..()

/datum/wires/radio/interactable(mob/user)
	if(!..())
		return FALSE
	var/obj/item/radio/R = holder
	return R.unscrewed

/datum/wires/radio/on_pulse(index)
	var/obj/item/radio/R = holder
	switch(index)
		if(WIRE_SIGNAL)
			R.listening = !R.listening
			R.broadcasting = R.listening
		if(WIRE_RX)
			R.listening = !R.listening
		if(WIRE_TX)
			R.broadcasting = !R.broadcasting


code:
/datum/wires/robot
	holder_type = /mob/living/silicon/robot
	proper_name = "Cyborg"
	randomize = TRUE

/datum/wires/robot/New(atom/holder)
	wires = list(
		WIRE_AI, WIRE_CAMERA,
		WIRE_LAWSYNC, WIRE_LOCKDOWN,
		WIRE_RESET_MODEL
	)
	add_duds(2)
	..()

/datum/wires/robot/interactable(mob/user)
	if(!..())
		return FALSE
	var/mob/living/silicon/robot/R = holder
	if(R.wiresexposed)
		return TRUE

/datum/wires/robot/get_status()
	var/mob/living/silicon/robot/R = holder
	var/list/status = list()
	status += "The law sync module is [R.lawupdate ? "on" : "off"]."
	status += "The intelligence link display shows [R.connected_ai ? R.connected_ai.name : "NULL"]."
	status += "The camera light is [!isnull(R.builtInCamera) && R.builtInCamera.status ? "on" : "off"]."
	status += "The lockdown indicator is [R.lockcharge ? "on" : "off"]."
	status += "There is a star symbol above the [get_color_of_wire(WIRE_RESET_MODEL)] wire."
	return status

/datum/wires/robot/on_pulse(wire, user)
	var/mob/living/silicon/robot/R = holder
	switch(wire)
		if(WIRE_AI) // Pulse to pick a new AI.
			if(!R.emagged)
				var/new_ai
				if(user)
					new_ai = select_active_ai(user, R.z)
				else
					new_ai = select_active_ai(R, R.z)
				R.notify_ai(DISCONNECT)
				if(new_ai && (new_ai != R.connected_ai))
					R.set_connected_ai(new_ai)
					log_combat(usr, R, "synced cyborg [R.connected_ai ? "from [ADMIN_LOOKUP(R.connected_ai)]": ""] to [ADMIN_LOOKUP(new_ai)]")
					if(R.shell)
						R.undeploy() //If this borg is an AI shell, disconnect the controlling AI and assign ti to a new AI
						R.notify_ai(AI_SHELL)
					else
						R.notify_ai(TRUE)
		if(WIRE_CAMERA) // Pulse to disable the camera.
			if(!QDELETED(R.builtInCamera) && !R.scrambledcodes)
				R.builtInCamera.toggle_cam(usr, FALSE)
				R.visible_message(span_notice("[R]'s camera lens focuses loudly."), span_notice("Your camera lens focuses loudly."))
				log_combat(usr, R, "toggled cyborg camera to [R.builtInCamera.status ? "on" : "off"] via pulse")
		if(WIRE_LAWSYNC) // Forces a law update if possible.
			if(R.lawupdate)
				R.visible_message(span_notice("[R] gently chimes."), span_notice("LawSync protocol engaged."))
				log_combat(usr, R, "forcibly synced cyborg laws via pulse")
				// TODO, log the laws they gained here
				R.lawsync()
				R.show_laws()
		if(WIRE_LOCKDOWN)
			R.SetLockdown(!R.lockcharge) // Toggle
			log_combat(usr, R, "[!R.lockcharge ? "locked down" : "released"] via pulse")

		if(WIRE_RESET_MODEL)
			if(R.has_model())
				R.visible_message(span_notice("[R]'s model servos twitch."), span_notice("Your model display flickers."))

/datum/wires/robot/on_cut(wire, mend)
	var/mob/living/silicon/robot/R = holder
	switch(wire)
		if(WIRE_AI) // Cut the AI wire to reset AI control.
			if(!mend)
				R.notify_ai(DISCONNECT)
				log_combat(usr, R, "cut AI wire on cyborg[R.connected_ai ? " and disconnected from [ADMIN_LOOKUP(R.connected_ai)]": ""]")
				if(R.shell)
					R.undeploy()
				R.set_connected_ai(null)
			R.logevent("AI connection fault [mend?"cleared":"detected"]")
		if(WIRE_LAWSYNC) // Cut the law wire, and the borg will no longer receive law updates from its AI. Repair and it will re-sync.
			if(mend)
				if(!R.emagged)
					R.lawupdate = TRUE
					log_combat(usr, R, "enabled lawsync via wire")
			else if(!R.deployed) //AI shells must always have the same laws as the AI
				R.lawupdate = FALSE
				log_combat(usr, R, "disabled lawsync via wire")
			R.logevent("Lawsync Module fault [mend?"cleared":"detected"]")
		if (WIRE_CAMERA) // Disable the camera.
			if(!QDELETED(R.builtInCamera) && !R.scrambledcodes)
				R.builtInCamera.status = mend
				R.builtInCamera.toggle_cam(usr, 0)
				R.visible_message(span_notice("[R]'s camera lens focuses loudly."), span_notice("Your camera lens focuses loudly."))
				R.logevent("Camera Module fault [mend?"cleared":"detected"]")
				log_combat(usr, R, "[mend ? "enabled" : "disabled"] cyborg camera via wire")
		if(WIRE_LOCKDOWN) // Simple lockdown.
			R.SetLockdown(!mend)
			R.logevent("Motor Controller fault [mend?"cleared":"detected"]")
			log_combat(usr, R, "[!R.lockcharge ? "locked down" : "released"] via wire")
		if(WIRE_RESET_MODEL)
			if(R.has_model() && !mend)
				R.ResetModel()
				log_combat(usr, R, "reset the cyborg module via wire")

/datum/wires/robot/can_reveal_wires(mob/user)
	if(HAS_TRAIT(user, TRAIT_KNOW_CYBORG_WIRES))
		return TRUE

	return ..()

/datum/wires/robot/always_reveal_wire(color)
	// Always reveal the reset model wire.
	if(color == get_color_of_wire(WIRE_RESET_MODEL))
		return TRUE

	return ..()


code:
/datum/wires/roulette
	holder_type = /obj/machinery/roulette
	proper_name = "Roulette Table"
	randomize = TRUE

/datum/wires/roulette/New(atom/holder)
	wires = list(
		WIRE_RESETOWNER,
		WIRE_PRIZEVEND,
		WIRE_SHOCK,
		WIRE_BOLTS
	)
	..()

/datum/wires/roulette/interactable(mob/user)
	if(!..())
		return FALSE
	. = FALSE
	var/obj/machinery/roulette/R = holder
	if(R.machine_stat & MAINT)
		. = TRUE

/datum/wires/roulette/get_status()
	var/obj/machinery/roulette/R = holder
	var/list/status = list()
	status += "The machines bolts [R.anchored ? "have fallen!" : "look up."]"
	status += "The main circuit is [R.on ? "on" : "off"]."
	status += "The main system lock appears to be [R.locked ? "on" : "off"]."
	status += "The account balance system appears to be [R.my_card ? "connected to [R.my_card.registered_account.account_holder]" : "disconnected"]."
	return status

/datum/wires/roulette/on_pulse(wire)
	var/obj/machinery/roulette/R = holder
	switch(wire)
		if(WIRE_SHOCK)
			if(isliving(usr))
				R.shock(usr, 50)
		if(WIRE_BOLTS) // Pulse to toggle bolts (but only raise if power is on).
			if(!R.on)
				return
			R.set_anchored(!R.anchored)
		if(WIRE_RESETOWNER)
			R.my_card = null
			R.audible_message(span_warning("Owner reset!"))
			R.locked = FALSE
		if(WIRE_PRIZEVEND)
			if(isliving(usr))
				R.shock(usr, 70)
			if(R.locked)
				return
			R.audible_message(span_warning("Unauthorized prize vend detected! Locking down machine!"))
			R.prize_theft(0.20)

/datum/wires/roulette/on_cut(wire, mend)
	var/obj/machinery/roulette/R = holder
	switch(wire)
		if(WIRE_SHOCK)
			if(isliving(usr))
				R.shock(usr, 60)
			if(mend)
				R.on = TRUE
			else
				R.on = FALSE
		if(WIRE_BOLTS) // Always drop
			if(!R.on)
				return
			R.set_anchored(TRUE)
		if(WIRE_RESETOWNER)
			if(isliving(usr))
				R.shock(usr, 70)
		if(WIRE_PRIZEVEND)
			if(isliving(usr))
				R.shock(usr, 75)
			if(R.locked)
				return
			R.audible_message(span_warning("Unauthorized prize vend detected! Locking down machine!"))
			R.prize_theft(0.10)


code:
/datum/wires/scanner_gate
	holder_type = /obj/machinery/scanner_gate
	proper_name = "Scanner Gate"
	wires = list(WIRE_ACCEPT, WIRE_DENY, WIRE_DISABLE)

/datum/wires/scanner_gate/on_pulse(wire, user)
	. = ..()
	var/obj/machinery/scanner_gate/scan_gate = holder
	switch(wire)
		if(WIRE_ACCEPT)
			scan_gate.light_pass = !scan_gate.light_pass
		if(WIRE_DENY)
			scan_gate.light_fail = !scan_gate.light_fail
		if(WIRE_DISABLE)
			scan_gate.ignore_signals = !scan_gate.ignore_signals

/datum/wires/scanner_gate/get_status()
	var/obj/machinery/scanner_gate/scanner = holder
	. = list()
	. += "The Green light is [scanner.light_pass ? "on" : "off"]."
	. += "The Red light is [scanner.light_fail ? "on" : "off"]."
	. += "The Purple light is [scanner.ignore_signals ? "on" : "off"]."


code:
/datum/wires/suit_storage_unit
	holder_type = /obj/machinery/suit_storage_unit
	proper_name = "Suit Storage Unit"

/datum/wires/suit_storage_unit/New(atom/holder)
	wires = list(
		WIRE_HACK, WIRE_SAFETY,
		WIRE_ZAP
	)
	add_duds(2)
	..()

/datum/wires/suit_storage_unit/interactable(mob/user)
	if(!..())
		return FALSE
	var/obj/machinery/suit_storage_unit/SSU = holder
	if(SSU.panel_open)
		return TRUE

/datum/wires/suit_storage_unit/get_status()
	var/obj/machinery/suit_storage_unit/SSU = holder
	var/list/status = list()
	status += "The UV bulb is [SSU.uv_super ? "glowing" : "dim"]."
	status += "The service light is [SSU.safeties ? "off" : "on"]."
	return status

/datum/wires/suit_storage_unit/on_pulse(wire)
	var/obj/machinery/suit_storage_unit/SSU = holder
	switch(wire)
		if(WIRE_HACK)
			SSU.uv_super = !SSU.uv_super
		if(WIRE_SAFETY)
			SSU.safeties = !SSU.safeties
		if(WIRE_ZAP)
			if(usr)
				SSU.shock(usr)

/datum/wires/suit_storage_unit/on_cut(wire, mend)
	var/obj/machinery/suit_storage_unit/SSU = holder
	switch(wire)
		if(WIRE_HACK)
			SSU.uv_super = !mend
		if(WIRE_SAFETY)
			SSU.safeties = mend
		if(WIRE_ZAP)
			if(usr)
				SSU.shock(usr)


code:
/datum/wires/syndicatebomb
	holder_type = /obj/machinery/syndicatebomb
	proper_name = "Syndicate Explosive Device"
	randomize = TRUE

/datum/wires/syndicatebomb/New(atom/holder)
	wires = list(
		WIRE_BOOM, WIRE_UNBOLT,
		WIRE_ACTIVATE, WIRE_DELAY, WIRE_PROCEED
	)
	..()

/datum/wires/syndicatebomb/interactable(mob/user)
	if(!..())
		return FALSE
	var/obj/machinery/syndicatebomb/P = holder
	if(P.open_panel)
		return TRUE

/datum/wires/syndicatebomb/on_pulse(wire)
	var/obj/machinery/syndicatebomb/B = holder
	switch(wire)
		if(WIRE_BOOM)
			if(B.active)
				holder.visible_message(span_danger("[icon2html(B, viewers(holder))] An alarm sounds! It's go-"))
				B.explode_now = TRUE
				tell_admins(B)
			else
				holder.visible_message(span_notice("[icon2html(B, viewers(holder))] Nothing happens."))
		if(WIRE_UNBOLT)
			holder.visible_message(span_notice("[icon2html(B, viewers(holder))] The bolts spin in place for a moment."))
		if(WIRE_DELAY)
			if(B.delayedbig)
				holder.visible_message(span_notice("[icon2html(B, viewers(holder))] Nothing happens."))
			else
				holder.visible_message(span_notice("[icon2html(B, viewers(holder))] The bomb chirps."))
				playsound(B, 'sound/machines/chime.ogg', 30, TRUE)
				B.detonation_timer += 300
				if(B.active)
					B.delayedbig = TRUE
		if(WIRE_PROCEED)
			holder.visible_message(span_danger("[icon2html(B, viewers(holder))] The bomb buzzes ominously!"))
			playsound(B, 'sound/machines/buzz-sigh.ogg', 30, TRUE)
			var/seconds = B.seconds_remaining()
			if(seconds >= 61) // Long fuse bombs can suddenly become more dangerous if you tinker with them.
				B.detonation_timer = world.time + 600
			else if(seconds >= 21)
				B.detonation_timer -= 100
			else if(seconds >= 11) // Both to prevent negative timers and to have a little mercy.
				B.detonation_timer = world.time + 100
		if(WIRE_ACTIVATE)
			if(!B.active)
				holder.visible_message(span_danger("[icon2html(B, viewers(holder))] You hear the bomb start ticking!"))
				B.activate()
				B.update_appearance()
			else if(B.delayedlittle)
				holder.visible_message(span_notice("[icon2html(B, viewers(holder))] Nothing happens."))
			else
				holder.visible_message(span_notice("[icon2html(B, viewers(holder))] The bomb seems to hesitate for a moment."))
				B.detonation_timer += 100
				B.delayedlittle = TRUE

/datum/wires/syndicatebomb/on_cut(wire, mend)
	var/obj/machinery/syndicatebomb/B = holder
	switch(wire)
		if(WIRE_BOOM)
			if(!mend && B.active)
				holder.visible_message(span_danger("[icon2html(B, viewers(holder))] An alarm sounds! It's go-"))
				B.explode_now = TRUE
				tell_admins(B)
		if(WIRE_UNBOLT)
			if(!mend && B.anchored)
				holder.visible_message(span_notice("[icon2html(B, viewers(holder))] The bolts lift out of the ground!"))
				playsound(B, 'sound/effects/stealthoff.ogg', 30, TRUE)
				B.set_anchored(FALSE)
		if(WIRE_PROCEED)
			if(!mend && B.active)
				holder.visible_message(span_danger("[icon2html(B, viewers(holder))] An alarm sounds! It's go-"))
				B.explode_now = TRUE
				tell_admins(B)
		if(WIRE_ACTIVATE)
			if(!mend && B.active)
				holder.visible_message(span_notice("[icon2html(B, viewers(holder))] The timer stops! The bomb has been defused!"))
				B.active = FALSE
				B.delayedlittle = FALSE
				B.delayedbig = FALSE
				B.update_appearance()

/datum/wires/syndicatebomb/proc/tell_admins(obj/machinery/syndicatebomb/B)
	if(istype(B, /obj/machinery/syndicatebomb/training))
		return
	var/turf/T = get_turf(B)
	log_game("\A [B] was detonated via boom wire at [AREACOORD(T)].")
	message_admins("A [B.name] was detonated via boom wire at [ADMIN_VERBOSEJMP(T)].")


code:
/datum/wires/tesla_coil
	proper_name = "Tesla Coil"
	randomize = TRUE //Only one wire don't need blueprints
	holder_type = /obj/machinery/power/energy_accumulator/tesla_coil

/datum/wires/tesla_coil/New(atom/holder)
	wires = list(WIRE_ZAP)
	..()

/datum/wires/tesla_coil/on_pulse(wire)
	var/obj/machinery/power/energy_accumulator/tesla_coil/C = holder
	C.zap()
	..()


code:
/datum/wires/vending
	holder_type = /obj/machinery/vending
	proper_name = "Vending Unit"

/datum/wires/vending/New(atom/holder)
	wires = list(
		WIRE_THROW, WIRE_SHOCK, WIRE_SPEAKER,
		WIRE_CONTRABAND, WIRE_IDSCAN
	)
	add_duds(1)
	..()

/datum/wires/vending/interactable(mob/user)
	if(!..())
		return FALSE
	var/obj/machinery/vending/V = holder
	if(!issilicon(user) && V.seconds_electrified && V.shock(user, 100))
		return FALSE
	if(V.panel_open)
		return TRUE

/datum/wires/vending/get_status()
	var/obj/machinery/vending/V = holder
	var/list/status = list()
	status += "The orange light is [V.seconds_electrified ? "on" : "off"]."
	status += "The red light is [V.shoot_inventory ? "off" : "blinking"]."
	status += "The green light is [V.extended_inventory ? "on" : "off"]."
	status += "A [V.scan_id ? "purple" : "yellow"] light is on."
	status += "A white light is [V.age_restrictions ? "on" : "off"]."
	status += "The speaker light is [V.shut_up ? "off" : "on"]."
	return status

/datum/wires/vending/on_pulse(wire)
	var/obj/machinery/vending/V = holder
	switch(wire)
		if(WIRE_THROW)
			V.shoot_inventory = !V.shoot_inventory
		if(WIRE_CONTRABAND)
			V.extended_inventory = !V.extended_inventory
		if(WIRE_SHOCK)
			V.seconds_electrified = MACHINE_DEFAULT_ELECTRIFY_TIME
		if(WIRE_IDSCAN)
			V.scan_id = !V.scan_id
		if(WIRE_SPEAKER)
			V.shut_up = !V.shut_up
		if(WIRE_AGELIMIT)
			V.age_restrictions = !V.age_restrictions

/datum/wires/vending/on_cut(wire, mend)
	var/obj/machinery/vending/V = holder
	switch(wire)
		if(WIRE_THROW)
			V.shoot_inventory = !mend
		if(WIRE_CONTRABAND)
			V.extended_inventory = FALSE
		if(WIRE_SHOCK)
			if(mend)
				V.seconds_electrified = MACHINE_NOT_ELECTRIFIED
			else
				V.seconds_electrified = MACHINE_ELECTRIFIED_PERMANENT
		if(WIRE_IDSCAN)
			V.scan_id = mend
		if(WIRE_SPEAKER)
			V.shut_up = mend


code:
// SETUP

/proc/TopicHandlers()
	. = list()
	var/list/all_handlers = subtypesof(/datum/world_topic)
	for(var/I in all_handlers)
		var/datum/world_topic/WT = I
		var/keyword = initial(WT.keyword)
		if(!keyword)
			warning("[WT] has no keyword! Ignoring...")
			continue
		var/existing_path = .[keyword]
		if(existing_path)
			warning("[existing_path] and [WT] have the same keyword! Ignoring [WT]...")
		else if(keyword == "key")
			warning("[WT] has keyword 'key'! Ignoring...")
		else
			.[keyword] = WT

// DATUM

/datum/world_topic
	var/keyword
	var/log = TRUE
	var/key_valid
	var/require_comms_key = FALSE

/datum/world_topic/proc/TryRun(list/input)
	key_valid = config && (CONFIG_GET(string/comms_key) == input["key"])
	input -= "key"
	if(require_comms_key && !key_valid)
		. = "Bad Key"
		if (input["format"] == "json")
			. = list("error" = .)
	else
		. = Run(input)
	if (input["format"] == "json")
		. = json_encode(.)
	else if(islist(.))
		. = list2params(.)

/datum/world_topic/proc/Run(list/input)
	CRASH("Run() not implemented for [type]!")

// TOPICS

/datum/world_topic/ping
	keyword = "ping"
	log = FALSE

/datum/world_topic/ping/Run(list/input)
	. = 0
	for (var/client/C in GLOB.clients)
		++.

/datum/world_topic/playing
	keyword = "playing"
	log = FALSE

/datum/world_topic/playing/Run(list/input)
	return GLOB.player_list.len

/datum/world_topic/pr_announce
	keyword = "announce"
	require_comms_key = TRUE
	var/static/list/PRcounts = list() //PR id -> number of times announced this round

/datum/world_topic/pr_announce/Run(list/input)
	var/list/payload = json_decode(input["payload"])
	var/id = "[payload["pull_request"]["id"]]"
	if(!PRcounts[id])
		PRcounts[id] = 1
	else
		++PRcounts[id]
		if(PRcounts[id] > PR_ANNOUNCEMENTS_PER_ROUND)
			return

	var/final_composed = span_announce("PR: [input[keyword]]")
	for(var/client/C in GLOB.clients)
		C.AnnouncePR(final_composed)

/datum/world_topic/ahelp_relay
	keyword = "Ahelp"
	require_comms_key = TRUE

/datum/world_topic/ahelp_relay/Run(list/input)
	relay_msg_admins(span_adminnotice("HELP:  [input["source"]] [input["message_sender"]]: [input["message"]]"))

/datum/world_topic/comms_console
	keyword = "Comms_Console"
	require_comms_key = TRUE

	var/list/timers

/datum/world_topic/comms_console/Run(list/input)
	// Reject comms messages from other servers that are not on our configured network,
	// if this has been configured. (See CROSS_COMMS_NETWORK in comms.txt)
	var/configured_network = CONFIG_GET(string/cross_comms_network)
	if (configured_network && configured_network != input["network"])
		return

	// We can't add the timer without the timer ID, but we can't get the timer ID without the timer!
	// To solve this, we just use a list that we mutate later.
	var/list/data = list("input" = input)
	var/timer_id = addtimer(CALLBACK(src, .proc/receive_cross_comms_message, data), CROSS_SECTOR_CANCEL_TIME, TIMER_STOPPABLE)
	data["timer_id"] = timer_id

	LAZYADD(timers, timer_id)

	to_chat(
		GLOB.admins,
		span_adminnotice( \
			"CROSS-SECTOR MESSAGE (INCOMING): [input["sender_ckey"]] (from [input["source"]]) is about to send \
			the following message (will autoapprove in [DisplayTimeText(CROSS_SECTOR_CANCEL_TIME)]): \
			REJECT
 \
			[html_encode(input["message"])]" \
		)
	)

/datum/world_topic/comms_console/Topic(href, list/href_list)
	. = ..()
	if (.)
		return

	if (href_list["reject_cross_comms_message"])
		if (!usr.client?.holder)
			log_game("[key_name(usr)] tried to reject an incoming cross-comms message without being an admin.")
			message_admins("[key_name(usr)] tried to reject an incoming cross-comms message without being an admin.")
			return

		var/timer_id = href_list["reject_cross_comms_message"]
		if (!(timer_id in timers))
			to_chat(usr, span_warning("It's too late!"))
			return

		deltimer(timer_id)
		LAZYREMOVE(timers, timer_id)

		log_admin("[key_name(usr)] has cancelled the incoming cross-comms message.")
		message_admins("[key_name(usr)] has cancelled the incoming cross-comms message.")

		return TRUE

/datum/world_topic/comms_console/proc/receive_cross_comms_message(list/data)
	var/list/input = data["input"]
	var/timer_id = data["timer_id"]

	LAZYREMOVE(timers, timer_id)

	minor_announce(input["message"], "Incoming message from [input["message_sender"]]")
	message_admins("Receiving a message from [input["sender_ckey"]] at [input["source"]]")
	for(var/obj/machinery/computer/communications/communications_console in GLOB.machines)
		communications_console.override_cooldown()

/datum/world_topic/news_report
	keyword = "News_Report"
	require_comms_key = TRUE

/datum/world_topic/news_report/Run(list/input)
	minor_announce(input["message"], "Breaking Update From [input["message_sender"]]")

/datum/world_topic/adminmsg
	keyword = "adminmsg"
	require_comms_key = TRUE

/datum/world_topic/adminmsg/Run(list/input)
	return TgsPm(input[keyword], input["msg"], input["sender"])

/datum/world_topic/namecheck
	keyword = "namecheck"
	require_comms_key = TRUE

/datum/world_topic/namecheck/Run(list/input)
	//Oh this is a hack, someone refactor the functionality out of the chat command PLS
	var/datum/tgs_chat_command/namecheck/NC = new
	var/datum/tgs_chat_user/user = new
	user.friendly_name = input["sender"]
	user.mention = user.friendly_name
	return NC.Run(user, input["namecheck"])

/datum/world_topic/adminwho
	keyword = "adminwho"
	require_comms_key = TRUE

/datum/world_topic/adminwho/Run(list/input)
	return tgsadminwho()

/datum/world_topic/status
	keyword = "status"

/datum/world_topic/status/Run(list/input)
	. = list()
	.["version"] = GLOB.game_version
	.["respawn"] = config ? !CONFIG_GET(flag/norespawn) : FALSE
	.["enter"] = !LAZYACCESS(SSlag_switch.measures, DISABLE_NON_OBSJOBS)
	.["ai"] = CONFIG_GET(flag/allow_ai)
	.["host"] = world.host ? world.host : null
	.["round_id"] = GLOB.round_id
	.["players"] = GLOB.clients.len
	.["revision"] = GLOB.revdata.commit
	.["revision_date"] = GLOB.revdata.date
	.["hub"] = GLOB.hub_visibility

	var/list/adm = get_admin_counts()
	var/list/presentmins = adm["present"]
	var/list/afkmins = adm["afk"]
	.["admins"] = presentmins.len + afkmins.len //equivalent to the info gotten from adminwho
	.["gamestate"] = SSticker.current_state

	.["map_name"] = SSmapping.config?.map_name || "Loading..."

	if(key_valid)
		.["active_players"] = get_active_player_count()

	.["security_level"] = get_security_level()
	.["round_duration"] = SSticker ? round((world.time-SSticker.round_start_time)/10) : 0
	// Amount of world's ticks in seconds, useful for calculating round duration

	//Time dilation stats.
	.["time_dilation_current"] = SStime_track.time_dilation_current
	.["time_dilation_avg"] = SStime_track.time_dilation_avg
	.["time_dilation_avg_slow"] = SStime_track.time_dilation_avg_slow
	.["time_dilation_avg_fast"] = SStime_track.time_dilation_avg_fast

	//pop cap stats
	.["soft_popcap"] = CONFIG_GET(number/soft_popcap) || 0
	.["hard_popcap"] = CONFIG_GET(number/hard_popcap) || 0
	.["extreme_popcap"] = CONFIG_GET(number/extreme_popcap) || 0
	.["popcap"] = max(CONFIG_GET(number/soft_popcap), CONFIG_GET(number/hard_popcap), CONFIG_GET(number/extreme_popcap)) //generalized field for this concept for use across ss13 codebases
	.["bunkered"] = CONFIG_GET(flag/panic_bunker) || FALSE
	.["interviews"] = CONFIG_GET(flag/panic_bunker_interview) || FALSE
	if(SSshuttle?.emergency)
		.["shuttle_mode"] = SSshuttle.emergency.mode
		// Shuttle status, see /__DEFINES/stat.dm
		.["shuttle_timer"] = SSshuttle.emergency.timeLeft()
		// Shuttle timer, in seconds