diff --git a/code/controllers/subsystem/events.dm b/code/controllers/subsystem/events.dm
index d144016d2b1..e61c3e2c730 100644
--- a/code/controllers/subsystem/events.dm
+++ b/code/controllers/subsystem/events.dm
@@ -3,6 +3,8 @@ SUBSYSTEM_DEF(events)
init_order = INIT_ORDER_EVENTS
runlevels = RUNLEVEL_GAME
+ var/manual_mode = FALSE //SKYRAT EDIT ADDITION: Is this subsystem being manually operated by the admins?
+
var/list/control = list() //list of all datum/round_event_control. Used for selecting events based on weight and occurrences.
var/list/running = list() //list of all existing /datum/round_event
var/list/currentrun = list()
@@ -60,7 +62,10 @@ SUBSYSTEM_DEF(events)
set waitfor = FALSE //for the admin prompt
if(!CONFIG_GET(flag/allow_random_events))
return
-
+//SKYRAT EDIT ADDITION
+ if(manual_mode)
+ return
+//SKYRAT EDIT END
var/gamemode = SSticker.mode.config_tag
var/players_amt = get_active_player_count(alive_check = 1, afk_check = 1, human_check = 1)
// Only alive, non-AFK human players count towards this.
diff --git a/code/game/objects/items/devices/anomaly_neutralizer.dm b/code/game/objects/items/devices/anomaly_neutralizer.dm
index decc6d255df..32a98db7019 100644
--- a/code/game/objects/items/devices/anomaly_neutralizer.dm
+++ b/code/game/objects/items/devices/anomaly_neutralizer.dm
@@ -11,7 +11,7 @@
slot_flags = ITEM_SLOT_BELT
item_flags = NOBLUDGEON
-/obj/item/anomaly_neutralizer/afterattack(atom/target, mob/user, proximity)
+/obj/item/anomaly_neutralizer/afterattack(atom/target, mob/living/user, proximity) //SKYRAT EDIT - MOB/LIVING
..()
if(!proximity || !target)
return
@@ -20,3 +20,14 @@
to_chat(user, "The circuitry of [src] fries from the strain of neutralizing [A]!")
A.anomalyNeutralize()
qdel(src)
+ //SKYRAT EDIT ADDITON START - CME
+ if(istype(target, /obj/effect/cme))
+ var/obj/effect/cme/C = target
+ to_chat(user, "The circuitry of [src] fries from the strain of neutralizing [C] causing you to absorb the shock!")
+ do_sparks(5, FALSE, src)
+ electrocute_mob(user, get_area(src), src, 1, TRUE)
+ user.adjust_fire_stacks(5)
+ user.IgniteMob()
+ C.anomalyNeutralize()
+ qdel(src)
+ //SKYRAT EDIT END
diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm
index 5c37c313171..9c63b48dd20 100644
--- a/code/modules/admin/admin_verbs.dm
+++ b/code/modules/admin/admin_verbs.dm
@@ -4,6 +4,7 @@ GLOBAL_LIST_INIT(admin_verbs_default, world.AVerbsDefault())
GLOBAL_PROTECT(admin_verbs_default)
/world/proc/AVerbsDefault()
return list(
+ /client/proc/manual_events_mode, //SKYRAT EDIT, ENABLES MANUAL EVENT MODE!
/client/proc/deadmin, /*destroys our own admin datum so we can play as a regular player*/
/client/proc/cmd_admin_say, /*admin-only ooc chat*/
/client/proc/cmd_loud_admin_say, /*ditto except its annoying - SKYRAT EDIT ADDITION - ADMIN*/
@@ -11,6 +12,7 @@ GLOBAL_PROTECT(admin_verbs_default)
/client/proc/debug_variables, /*allows us to -see- the variables of any instance in the game. +VAREDIT needed to modify*/
/client/proc/dsay, /*talk in deadchat using our ckey/fakekey*/
/client/proc/investigate_show, /*various admintools for investigation. Such as a singulo grief-log*/
+
/client/proc/secrets,
/client/proc/toggle_hear_radio, /*allows admins to hide all radio output*/
/client/proc/reload_admins,
diff --git a/modular_skyrat/modules/alerts/code/set_security_level.dm b/modular_skyrat/modules/alerts/code/set_security_level.dm
index bd459ecebcd..704f225f3b2 100644
--- a/modular_skyrat/modules/alerts/code/set_security_level.dm
+++ b/modular_skyrat/modules/alerts/code/set_security_level.dm
@@ -156,7 +156,8 @@ GLOBAL_VAR_INIT(gamma_looping, FALSE) //This is so we know if the gamma sound ef
if(SEC_LEVEL_DELTA)
alert_sound_to_playing('modular_skyrat/modules/alerts/sound/misc/deltaklaxon.ogg')
if(SEC_LEVEL_GAMMA)
- gamma_loop() //Gamma has a looping sound effect
+ if(!GLOB.gamma_looping)
+ gamma_loop() //Gamma has a looping sound effect
/proc/alert_sound_to_playing(soundin, volume = 100, vary = FALSE, frequency = 0, falloff = FALSE, channel = 0, pressure_affected = FALSE, sound/S)
if(!S)
@@ -169,12 +170,11 @@ GLOBAL_VAR_INIT(gamma_looping, FALSE) //This is so we know if the gamma sound ef
//ALERT ALERT ALERT SHITCODE
/proc/gamma_loop() //Loops gamma sound
- if(GLOB.gamma_looping)
+ if(GLOB.security_level != SEC_LEVEL_GAMMA)
+ GLOB.gamma_looping = FALSE
return
GLOB.gamma_looping = TRUE
- while(GLOB.security_level == SEC_LEVEL_GAMMA)
- alert_sound_to_playing('modular_skyrat/modules/alerts/sound/misc/gamma_alert.ogg')
- sleep(GAMMA_LOOP_LENGTH)
- GLOB.gamma_looping = FALSE
+ alert_sound_to_playing('modular_skyrat/modules/alerts/sound/misc/gamma_alert.ogg')
+ addtimer(CALLBACK(.proc/gamma_loop), GAMMA_LOOP_LENGTH)
#undef GAMMA_LOOP_LENGTH
diff --git a/modular_skyrat/modules/cme/code/_cme_defines.dm b/modular_skyrat/modules/cme/code/_cme_defines.dm
new file mode 100644
index 00000000000..3baba39f33e
--- /dev/null
+++ b/modular_skyrat/modules/cme/code/_cme_defines.dm
@@ -0,0 +1,55 @@
+////////////////////////
+//CME DEFINES FILE!
+///////////////////////
+
+#define CME_RANDOM "random"
+#define CME_MINIMAL "minimal"
+#define CME_MODERATE "moderate"
+#define CME_EXTREME "extreme"
+#define CME_ARMAGEDDON "armageddon"
+
+//Times are SECONDS, they're devided by 2 because that's how long the controller takes to process. 20 deciseconds = 2 seconds. I know, it's dumb as fuck.
+
+#define CME_MINIMAL_LIGHT_RANGE_LOWER 7 //The lowest range for the emp pulse light range.
+#define CME_MINIMAL_LIGHT_RANGE_UPPER 10 //The highest range for the emp pulse light range.
+#define CME_MINIMAL_HEAVY_RANGE_LOWER 5 //The lowest range for the emp pulse heavy range.
+#define CME_MINIMAL_HEAVY_RANGE_UPPER 7 //The highest range for the emp pulse heavy range.
+#define CME_MINIMAL_FREQUENCY_LOWER 25 / 2 //The lower time range for cme bubbles to appear.
+#define CME_MINIMAL_FREQUENCY_UPPER 30 / 2 //The higher time range for cme bubbles to appear.
+#define CME_MINIMAL_BUBBLE_BURST_TIME 40 SECONDS //The time taken for a cme bubble to pop.
+#define CME_MINIMAL_START_LOWER 180 / 2 //The lowest amount of time for the event to start from the announcement. - Prep time
+#define CME_MINIMAL_START_UPPER 240 / 2 //The highest amount of time for the event to start from the announcement. - Prep time
+#define CME_MINIMAL_END 240 / 2 //The amount of time starting from THE MINIMAL START TIME for the event to end. - How long it actually lasts.
+
+#define CME_MODERATE_LIGHT_RANGE_LOWER 10
+#define CME_MODERATE_LIGHT_RANGE_UPPER 15
+#define CME_MODERATE_HEAVY_RANGE_LOWER 7
+#define CME_MODERATE_HEAVY_RANGE_UPPER 10
+#define CME_MODERATE_FREQUENCY_LOWER 20 / 2
+#define CME_MODERATE_FREQUENCY_UPPER 25 / 2
+#define CME_MODERATE_BUBBLE_BURST_TIME 30 SECONDS
+#define CME_MODERATE_START_LOWER 120 / 2
+#define CME_MODERATE_START_UPPER 180 / 2
+#define CME_MODERATE_END 300 / 2
+
+#define CME_EXTREME_LIGHT_RANGE_LOWER 15
+#define CME_EXTREME_LIGHT_RANGE_UPPER 20
+#define CME_EXTREME_HEAVY_RANGE_LOWER 10
+#define CME_EXTREME_HEAVY_RANGE_UPPER 13
+#define CME_EXTREME_FREQUENCY_LOWER 15 / 2
+#define CME_EXTREME_FREQUENCY_UPPER 20 / 2
+#define CME_EXTREME_BUBBLE_BURST_TIME 20 SECONDS
+#define CME_EXTREME_START_LOWER 60 /2
+#define CME_EXTREME_START_UPPER 120 / 2
+#define CME_EXTREME_END 360 / 2
+
+#define CME_ARMAGEDDON_LIGHT_RANGE_LOWER 25
+#define CME_ARMAGEDDON_LIGHT_RANGE_UPPER 30
+#define CME_ARMAGEDDON_HEAVY_RANGE_LOWER 20
+#define CME_ARMAGEDDON_HEAVY_RANGE_UPPER 25
+#define CME_ARMAGEDDON_FREQUENCY_LOWER 5 / 2
+#define CME_ARMAGEDDON_FREQUENCY_UPPER 7 / 2
+#define CME_ARMAGEDDON_BUBBLE_BURST_TIME 10 SECONDS
+#define CME_ARMAGEDDON_START_LOWER 60 / 2
+#define CME_ARMAGEDDON_START_UPPER 70 / 2
+#define CME_ARMAGEDDON_END 1200 / 2
diff --git a/modular_skyrat/modules/cme/code/cme.dm b/modular_skyrat/modules/cme/code/cme.dm
new file mode 100644
index 00000000000..d6baf39fdbb
--- /dev/null
+++ b/modular_skyrat/modules/cme/code/cme.dm
@@ -0,0 +1,307 @@
+////////////////////////////////////
+//CME EVENT SYSTEM
+//SEE _CME_DEFINES.DM FOR SETTINGS.
+////////////////////////////////////
+
+/* Welcome to the CME control system.
+
+This controls the CME event, or coronal mass ejection event, which causes multiple EMP bubbles to form around the station
+depending on conditons and time. There are currently 4 settings of CME, all of which have settings defined in the
+cme defines DM file. See that for more info
+
+Armageddon is truly going to fuck the station, use it sparingly.
+*/
+
+/datum/round_event_control/cme
+ name = "Coronal Mass Ejection: Minimal"
+ typepath = /datum/round_event/cme
+ weight = 7
+ min_players = 15
+ max_occurrences = 2
+ earliest_start = 20 MINUTES
+
+/datum/round_event/cme
+ startWhen = 6
+ endWhen = 66
+ announceWhen = 10
+ var/cme_intensity = CME_MINIMAL
+ var/cme_frequency_lower
+ var/cme_frequency_upper
+ var/list/cme_start_locs = list()
+
+/datum/round_event_control/cme/random
+ name = "Coronal Mass Ejection: Random"
+ typepath = /datum/round_event/cme/random
+ weight = 10
+ min_players = 15
+ max_occurrences = 3
+ earliest_start = 20 MINUTES
+
+/datum/round_event/cme/random
+ cme_intensity = CME_RANDOM
+
+/datum/round_event_control/cme/moderate
+ name = "Coronal Mass Ejection: Moderate"
+ typepath = /datum/round_event/cme/moderate
+ weight = 7
+ min_players = 20
+ max_occurrences = 1
+ earliest_start = 35 MINUTES
+
+/datum/round_event/cme/moderate
+ cme_intensity = CME_MODERATE
+
+/datum/round_event_control/cme/extreme
+ name = "Coronal Mass Ejection: Extreme"
+ typepath = /datum/round_event/cme/extreme
+ weight = 4
+ min_players = 25
+ max_occurrences = 1
+ earliest_start = 45 MINUTES
+
+/datum/round_event/cme/extreme
+ cme_intensity = CME_EXTREME
+
+/datum/round_event_control/cme/armageddon
+ name = "Coronal Mass Ejection: Armageddon"
+ typepath = /datum/round_event/cme/armageddon
+ weight = 0
+ max_occurrences = 0
+
+/datum/round_event/cme/armageddon
+ cme_intensity = CME_ARMAGEDDON
+
+/datum/round_event/cme/setup()
+ if(!cme_intensity)
+ cme_intensity = pick(CME_MINIMAL, CME_MODERATE, CME_EXTREME)
+ switch(cme_intensity)
+ if(CME_RANDOM)
+ cme_frequency_lower = CME_MODERATE_FREQUENCY_LOWER
+ cme_frequency_upper = CME_MODERATE_FREQUENCY_UPPER
+ startWhen = rand(CME_MODERATE_START_LOWER, CME_MODERATE_START_UPPER)
+ endWhen = startWhen + rand(CME_MINIMAL_END, CME_EXTREME_END)
+ if(CME_MINIMAL)
+ cme_frequency_lower = CME_MINIMAL_FREQUENCY_LOWER
+ cme_frequency_upper = CME_MINIMAL_FREQUENCY_UPPER
+ startWhen = rand(CME_MINIMAL_START_LOWER, CME_MINIMAL_START_UPPER)
+ endWhen = startWhen + CME_MINIMAL_END
+ if(CME_MODERATE)
+ cme_frequency_lower = CME_MODERATE_FREQUENCY_LOWER
+ cme_frequency_upper = CME_MODERATE_FREQUENCY_UPPER
+ startWhen = rand(CME_MODERATE_START_LOWER, CME_MODERATE_START_UPPER)
+ endWhen = startWhen + CME_MODERATE_END
+ if(CME_EXTREME)
+ cme_frequency_lower = CME_EXTREME_FREQUENCY_LOWER
+ cme_frequency_upper = CME_EXTREME_FREQUENCY_UPPER
+ startWhen = rand(CME_EXTREME_START_LOWER, CME_EXTREME_START_UPPER)
+ endWhen = startWhen + CME_EXTREME_END
+ if(CME_ARMAGEDDON)
+ cme_frequency_lower = CME_ARMAGEDDON_FREQUENCY_LOWER
+ cme_frequency_upper = CME_ARMAGEDDON_FREQUENCY_UPPER
+ startWhen = rand(CME_ARMAGEDDON_START_LOWER, CME_ARMAGEDDON_START_UPPER)
+ endWhen = startWhen + CME_ARMAGEDDON_END
+ else
+ message_admins("CME setup failure, aborting.")
+ kill()
+
+ for(var/turf/open/floor/T in world)
+ if(is_station_level(T.z))
+ cme_start_locs += T
+
+/datum/round_event/cme/announce(fake)
+ if(fake)
+ priority_announce("Critical Coronal mass ejection detected! Expected intensity: [uppertext(cme_intensity)]. Impact in: [rand(200, 300)] seconds. \
+ All synthetic and non-organic lifeforms should seek shelter immediately! \
+ Ensure all sensitive equipment is shielded.", "Solar Event", sound('modular_skyrat/modules/cme/sound/cme_warning.ogg'))
+ else
+ switch(cme_intensity)
+ if(CME_RANDOM)
+ priority_announce("Coronal mass ejection detected! Expected intensity: UNKNOWN. Impact in: [round((startWhen * SSevents.wait) / 10, 0.1)] seconds. \
+ All synthetic and non-organic lifeforms should seek shelter immediately! \
+ Neutralize magnetic field bubbles at all costs.", "Solar Event", sound('modular_skyrat/modules/cme/sound/cme_warning.ogg'))
+ if(CME_MINIMAL)
+ priority_announce("Coronal mass ejection detected! Expected intensity: [uppertext(cme_intensity)]. Impact in: [round((startWhen * SSevents.wait) / 10, 0.1)] seconds. \
+ All synthetic and non-organic lifeforms should seek shelter immediately! \
+ Neutralize magnetic field bubbles at all costs.", "Solar Event", sound('modular_skyrat/modules/cme/sound/cme_warning.ogg'))
+ if(CME_MODERATE)
+ priority_announce("Coronal mass ejection detected! Expected intensity: [uppertext(cme_intensity)]. Impact in: [round((startWhen * SSevents.wait) / 10, 0.1)] seconds. \
+ All synthetic and non-organic lifeforms should seek shelter immediately! \
+ Neutralize magnetic field bubbles at all costs.", "Solar Event", sound('modular_skyrat/modules/cme/sound/cme_warning.ogg'))
+ if(CME_EXTREME)
+ set_security_level(SEC_LEVEL_RED)
+ priority_announce("Critical Coronal mass ejection detected! Expected intensity: [uppertext(cme_intensity)]. Impact in: [round((startWhen * SSevents.wait) / 10, 0.1)] seconds. \
+ All synthetic and non-organic lifeforms should seek shelter immediately! \
+ Neutralize magnetic field bubbles at all costs.", "Solar Event", sound('modular_skyrat/modules/cme/sound/cme_warning.ogg'))
+ if(CME_ARMAGEDDON)
+ set_security_level(SEC_LEVEL_GAMMA)
+ priority_announce("Neutron Mass Ejection Detected! Expected intensity: [uppertext(cme_intensity)]. Impact in: [round((startWhen * SSevents.wait) / 10, 0.1)] seconds. \
+ All personnel should proceed to their nearest warpgate for evacuation, the Solar Federation has issued this mandatory alert.", "Solar Event", sound('modular_skyrat/modules/cme/sound/cme_warning.ogg'))
+
+/datum/round_event/cme/tick()
+ if(ISMULTIPLE(activeFor, rand(cme_frequency_lower, cme_frequency_upper)))
+ var/turf/spawnpoint = pick(cme_start_locs)
+ spawn_cme(spawnpoint, cme_intensity)
+
+/datum/round_event/cme/proc/spawn_cme(spawnpoint, intensity)
+ if(intensity == CME_RANDOM)
+ intensity = pick(CME_MINIMAL, CME_MODERATE, CME_EXTREME)
+ var/area/loc_area_name = get_area(spawnpoint)
+ minor_announce("WARNING! [uppertext(intensity)] PULSE EXPECTED IN: [loc_area_name.name]", "Solar Flare Log:")
+ for(var/i in GLOB.mob_list)
+ var/mob/M = i
+ if(M.client)
+ SEND_SOUND(M, sound('modular_skyrat/modules/cme/sound/cme_warning.ogg'))
+ switch(intensity)
+ if(CME_MINIMAL)
+ var/obj/effect/cme/spawnedcme = new(spawnpoint)
+ announce_to_ghosts(spawnedcme)
+ if(CME_MODERATE)
+ var/obj/effect/cme/moderate/spawnedcme = new(spawnpoint)
+ announce_to_ghosts(spawnedcme)
+ if(CME_EXTREME)
+ var/obj/effect/cme/extreme/spawnedcme = new(spawnpoint)
+ announce_to_ghosts(spawnedcme)
+ if(CME_ARMAGEDDON)
+ var/obj/effect/cme/armageddon/spawnedcme = new(spawnpoint)
+ announce_to_ghosts(spawnedcme)
+
+
+/datum/round_event/cme/end()
+ minor_announce("The station has cleared the solar flare, please proceed to repair electronic failures.", "CME cleared:")
+
+
+////////////////////////
+//CME bubbles
+///////////////////////
+
+/obj/effect/cme
+ desc = "A solar ejection projection."
+ name = "MINIMAL SOLAR EJECTION"
+ icon = 'modular_skyrat/modules/cme/icons/cme_effect.dmi'
+ icon_state = "cme_effect"
+ color = COLOR_BLUE_LIGHT
+ light_range = 5
+ light_power = 2
+ light_color = COLOR_BLUE_LIGHT
+ pixel_x = -32
+ pixel_y = -32
+ anchored = TRUE
+ opacity = FALSE
+ density = FALSE
+ layer = LIGHTING_LAYER
+ CanAtmosPass = ATMOS_PASS_DENSITY
+ var/timeleft = CME_MINIMAL_BUBBLE_BURST_TIME
+ var/cme_light_range_lower = CME_MINIMAL_LIGHT_RANGE_LOWER
+ var/cme_light_range_upper = CME_MINIMAL_LIGHT_RANGE_UPPER
+ var/cme_heavy_range_lower = CME_MINIMAL_HEAVY_RANGE_LOWER
+ var/cme_heavy_range_upper = CME_MINIMAL_HEAVY_RANGE_UPPER
+ var/neutralized = FALSE
+
+/obj/effect/cme/moderate
+ name = "MODERATE SOLAR EJECTION"
+ color = COLOR_VIVID_YELLOW
+ light_color = COLOR_VIVID_YELLOW
+ timeleft = CME_MODERATE_BUBBLE_BURST_TIME
+ cme_light_range_lower = CME_MODERATE_LIGHT_RANGE_LOWER
+ cme_light_range_upper = CME_MODERATE_LIGHT_RANGE_UPPER
+ cme_heavy_range_lower = CME_MODERATE_HEAVY_RANGE_LOWER
+ cme_heavy_range_upper = CME_MODERATE_HEAVY_RANGE_UPPER
+
+/obj/effect/cme/extreme
+ name = "EXTREME SOLAR EJECTION"
+ color = COLOR_RED
+ light_color = COLOR_RED
+ timeleft = CME_EXTREME_BUBBLE_BURST_TIME
+ cme_light_range_lower = CME_EXTREME_LIGHT_RANGE_LOWER
+ cme_light_range_upper = CME_EXTREME_LIGHT_RANGE_UPPER
+ cme_heavy_range_lower = CME_EXTREME_HEAVY_RANGE_LOWER
+ cme_heavy_range_upper = CME_EXTREME_HEAVY_RANGE_UPPER
+
+/obj/effect/cme/armageddon
+ name = "ARMAGEDDON SOLAR EJECTION"
+ color = COLOR_VIOLET
+ light_color = COLOR_VIOLET
+ timeleft = CME_ARMAGEDDON_BUBBLE_BURST_TIME
+ cme_light_range_lower = CME_ARMAGEDDON_LIGHT_RANGE_LOWER
+ cme_light_range_upper = CME_ARMAGEDDON_LIGHT_RANGE_UPPER
+ cme_heavy_range_lower = CME_ARMAGEDDON_HEAVY_RANGE_LOWER
+ cme_heavy_range_upper = CME_ARMAGEDDON_HEAVY_RANGE_UPPER
+
+/obj/effect/cme/Initialize()
+ . = ..()
+ playsound(src,'sound/weapons/resonator_fire.ogg',75,TRUE)
+ var/turf/open/T = get_turf(src)
+ if(istype(T))
+ T.atmos_spawn_air("o2=15;plasma=15;TEMP=5778")
+ addtimer(CALLBACK(src, .proc/burst), timeleft)
+
+/obj/effect/cme/proc/burst()
+ if(neutralized)
+ visible_message("[src] fizzles out into nothingness.")
+ new /obj/effect/particle_effect/smoke/bad(loc)
+ qdel(src)
+ return
+ var/pulse_range_light = rand(cme_light_range_lower, cme_light_range_upper)
+ var/pulse_range_heavy = rand(cme_heavy_range_lower, cme_heavy_range_upper)
+ empulse(src, pulse_range_heavy, pulse_range_light)
+ playsound(src,'sound/weapons/resonator_blast.ogg',100,TRUE)
+ explosion(src, 0, 0, 2, flame_range = 3)
+ for(var/i in GLOB.mob_list)
+ var/mob/M = i
+ if(M.client)
+ SEND_SOUND(M, sound('modular_skyrat/modules/cme/sound/cme.ogg'))
+ shake_camera(M, 15, 1)
+ qdel(src)
+
+/obj/effect/cme/armageddon/burst()
+ if(neutralized)
+ visible_message("[src] fizzles out into nothingness.")
+ new /obj/effect/particle_effect/smoke/bad(loc)
+ qdel(src)
+ return
+ var/pulse_range_light = rand(cme_light_range_lower, cme_light_range_upper)
+ var/pulse_range_heavy = rand(cme_heavy_range_lower, cme_heavy_range_upper)
+ empulse(src, pulse_range_heavy, pulse_range_light)
+ explosion(src, 0, 3, 10, flame_range = 10)
+ playsound(src,'sound/weapons/resonator_blast.ogg',100,TRUE)
+ for(var/i in GLOB.mob_list)
+ var/mob/M = i
+ if(M.client)
+ SEND_SOUND(M, sound('modular_skyrat/modules/cme/sound/cme.ogg'))
+ shake_camera(M, 15, 1)
+ qdel(src)
+
+/obj/effect/cme/singularity_pull()
+ return
+
+/obj/effect/cme/proc/anomalyNeutralize()
+ playsound(src,'sound/weapons/resonator_blast.ogg',100,TRUE)
+ minor_announce("[src.name] NEUTRALIZED.", "Solar Flare Log:")
+ new /obj/effect/particle_effect/smoke/bad(loc)
+ color = COLOR_WHITE
+ light_color = COLOR_WHITE
+ neutralized = TRUE
+
+/obj/effect/cme/extreme/anomalyNeutralize()
+ playsound(src,'sound/weapons/resonator_blast.ogg',100,TRUE)
+ minor_announce("[src.name] NEUTRALIZED.", "Solar Flare Log:")
+ new /obj/effect/particle_effect/smoke/bad(loc)
+ var/turf/open/T = get_turf(src)
+ if(istype(T))
+ T.atmos_spawn_air("o2=15;plasma=15;TEMP=5778")
+ color = COLOR_WHITE
+ light_color = COLOR_WHITE
+ neutralized = TRUE
+
+/client/proc/manual_events_mode()
+ set name = "Manual Event Mode Toggle"
+ set category = "Admin"
+ set desc = "Toggle manual event mode."
+
+ if(!holder)
+ return
+
+ SSevents.manual_mode = !SSevents.manual_mode
+
+ log_admin("EVENT MODE SWITCHED.")
+ message_admins("EVENT SYSTEM NOW IN [SSevents.manual_mode ? "MANUAL OPERATION" : "AUTOMATIC OPERATION"]. TRIGGERED BY: [src]")
diff --git a/modular_skyrat/modules/cme/icons/cme_effect.dmi b/modular_skyrat/modules/cme/icons/cme_effect.dmi
new file mode 100644
index 00000000000..5c3e2cf0761
Binary files /dev/null and b/modular_skyrat/modules/cme/icons/cme_effect.dmi differ
diff --git a/modular_skyrat/modules/cme/readme.md b/modular_skyrat/modules/cme/readme.md
new file mode 100644
index 00000000000..6d2af7ef42d
--- /dev/null
+++ b/modular_skyrat/modules/cme/readme.md
@@ -0,0 +1,24 @@
+## Title: Coronal Mass Ejection Event
+
+MODULE ID: CME
+
+### Description:
+
+Adds in a new event called CME, an EMP based event that spawns random EMP's across the station.
+
+### TG Proc Changes:
+
+N/A
+
+### Defines:
+
+N/A
+
+### Included files:
+
+N/A
+
+### Credits:
+
+Gandalf2k15 - OG
+CME sound effect - Faster Than Light(FTL)
diff --git a/modular_skyrat/modules/cme/sound/cme.ogg b/modular_skyrat/modules/cme/sound/cme.ogg
new file mode 100644
index 00000000000..8c57ad37569
Binary files /dev/null and b/modular_skyrat/modules/cme/sound/cme.ogg differ
diff --git a/modular_skyrat/modules/cme/sound/cme_warning.ogg b/modular_skyrat/modules/cme/sound/cme_warning.ogg
new file mode 100644
index 00000000000..e912b3a277c
Binary files /dev/null and b/modular_skyrat/modules/cme/sound/cme_warning.ogg differ
diff --git a/tgstation.dme b/tgstation.dme
index ba9e1bd07bf..366522b406d 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -3580,6 +3580,8 @@
#include "modular_skyrat\modules\cloning\code\modules\research\designs\machine_designs.dm"
#include "modular_skyrat\modules\cloning\code\modules\research\techweb\all_nodes.dm"
#include "modular_skyrat\modules\cloning\code\modules\ruins\spaceruin_code\cloning_lab.dm"
+#include "modular_skyrat\modules\cme\code\_cme_defines.dm"
+#include "modular_skyrat\modules\cme\code\cme.dm"
#include "modular_skyrat\modules\combat_indicator\code\combat_indicator.dm"
#include "modular_skyrat\modules\cryosleep\code\ai.dm"
#include "modular_skyrat\modules\cryosleep\code\area.dm"