diff --git a/code/__HELPERS/unsorted.dm b/code/__HELPERS/unsorted.dm
index ea3811085b..28a0dc7889 100644
--- a/code/__HELPERS/unsorted.dm
+++ b/code/__HELPERS/unsorted.dm
@@ -1079,18 +1079,17 @@ B --><-- A
return L
//similar function to RANGE_TURFS(), but will search spiralling outwards from the center (like the above, but only turfs)
-/proc/spiral_range_turfs(dist=0, center=usr, orange=0)
+/proc/spiral_range_turfs(dist=0, center=usr, orange=0, list/outlist = list(), tick_checked)
+ outlist.Cut()
if(!dist)
- if(!orange)
- return list(center)
- else
- return list()
+ outlist += center
+ return outlist
var/turf/t_center = get_turf(center)
if(!t_center)
- return list()
+ return outlist
- var/list/L = list()
+ var/list/L = outlist
var/turf/T
var/y
var/x
@@ -1128,6 +1127,8 @@ B --><-- A
if(T)
L += T
c_dist++
+ if(tick_checked)
+ CHECK_TICK
return L
diff --git a/code/_globalvars/configuration.dm b/code/_globalvars/configuration.dm
index 23bfdd3987..b001b84255 100644
--- a/code/_globalvars/configuration.dm
+++ b/code/_globalvars/configuration.dm
@@ -22,12 +22,12 @@ GLOBAL_VAR_INIT(tinted_weldhelh, TRUE)
// Debug2 is used in conjunction with a lot of admin verbs and therefore is actually legit.
GLOBAL_VAR_INIT(Debug, FALSE) // global debug switch
GLOBAL_VAR_INIT(Debug2, FALSE)
-
-//This was a define, but I changed it to a variable so it can be changed in-game.(kept the all-caps definition because... code...) -Errorage
-GLOBAL_VAR_INIT(MAX_EX_DEVESTATION_RANGE, 3)
-GLOBAL_VAR_INIT(MAX_EX_HEAVY_RANGE, 7)
-GLOBAL_VAR_INIT(MAX_EX_LIGHT_RANGE, 14)
-GLOBAL_VAR_INIT(MAX_EX_FLASH_RANGE, 14)
-GLOBAL_VAR_INIT(MAX_EX_FLAME_RANGE, 14)
-GLOBAL_VAR_INIT(DYN_EX_SCALE, 0.5)
-
+
+//This was a define, but I changed it to a variable so it can be changed in-game.(kept the all-caps definition because... code...) -Errorage
+GLOBAL_VAR_INIT(MAX_EX_DEVESTATION_RANGE, 3)
+GLOBAL_VAR_INIT(MAX_EX_HEAVY_RANGE, 7)
+GLOBAL_VAR_INIT(MAX_EX_LIGHT_RANGE, 14)
+GLOBAL_VAR_INIT(MAX_EX_FLASH_RANGE, 14)
+GLOBAL_VAR_INIT(MAX_EX_FLAME_RANGE, 14)
+GLOBAL_VAR_INIT(DYN_EX_SCALE, 0.5)
+
diff --git a/code/controllers/subsystem/explosion.dm b/code/controllers/subsystem/explosion.dm
new file mode 100644
index 0000000000..fe6657644f
--- /dev/null
+++ b/code/controllers/subsystem/explosion.dm
@@ -0,0 +1,510 @@
+SUBSYSTEM_DEF(explosion)
+ priority = 99
+ wait = 1
+ flags = SS_TICKER|SS_NO_INIT
+
+ var/list/explosions
+
+ var/rebuild_tick_split_count = FALSE
+ var/tick_portions_required = 0
+
+ var/list/logs
+
+ var/list/zlevels_that_ignore_bombcap
+ var/list/doppler_arrays
+
+ //legacy caps, set by config
+ var/devastation_cap = 3
+ var/heavy_cap = 7
+ var/light_cap = 14
+ var/flash_cap = 14
+ var/flame_cap = 14
+ var/dyn_ex_scale = 0.5
+
+ var/id_counter = 0
+
+/datum/controller/subsystem/explosion/PreInit()
+ doppler_arrays = list()
+ logs = list()
+ explosions = list()
+ zlevels_that_ignore_bombcap = list("[ZLEVEL_MINING]")
+
+/datum/controller/subsystem/explosion/Shutdown()
+ QDEL_LIST(explosions)
+ QDEL_LIST(logs)
+ zlevels_that_ignore_bombcap.Cut()
+
+/datum/controller/subsystem/explosion/Recover()
+ explosions = SSexplosion.explosions
+ logs = SSexplosion.logs
+ id_counter = SSexplosion.id_counter
+ rebuild_tick_split_count = TRUE
+ zlevels_that_ignore_bombcap = SSexplosion.zlevels_that_ignore_bombcap
+ doppler_arrays = SSexplosion.doppler_arrays
+
+ devastation_cap = SSexplosion.devastation_cap
+ heavy_cap = SSexplosion.heavy_cap
+ light_cap = SSexplosion.light_cap
+ flash_cap = SSexplosion.flash_cap
+ flame_cap = SSexplosion.flame_cap
+ dyn_ex_scale = SSexplosion.dyn_ex_scale
+
+/datum/controller/subsystem/explosion/fire()
+ var/list/cached_explosions = explosions
+ var/num_explosions = cached_explosions.len
+ if(!num_explosions)
+ return
+
+ //figure exactly how many tick splits are required
+ var/num_splits
+ if(rebuild_tick_split_count)
+ var/reactionary = config.reactionary_explosions
+ num_splits = num_explosions
+ for(var/I in cached_explosions)
+ var/datum/explosion/E = I
+ if(!E.turfs_processed)
+ ++num_splits
+ if(reactionary && !E.densities_processed)
+ ++num_splits
+ tick_portions_required = num_splits
+ else
+ num_splits = tick_portions_required
+
+ MC_SPLIT_TICK_INIT(num_splits)
+
+ for(var/I in cached_explosions)
+ var/datum/explosion/E = I
+
+ var/etp = E.turfs_processed
+ if(!etp)
+ if(GatherTurfs(E))
+ --tick_portions_required
+ etp = TRUE
+ MC_SPLIT_TICK
+
+ var/edp = E.densities_processed
+ if(!edp)
+ if(DensityCalculate(E, etp))
+ --tick_portions_required
+ edp = TRUE
+ MC_SPLIT_TICK
+
+ if(ProcessExplosion(E, edp)) //splits the tick
+ --tick_portions_required
+ explosions -= E
+ logs += E
+ NotifyDopplers(E)
+ MC_SPLIT_TICK
+
+/datum/controller/subsystem/explosion/proc/NotifyDopplers(datum/explosion/E)
+ for(var/array in doppler_arrays)
+ var/obj/machinery/doppler_array/A = array
+ A.sense_explosion(E.epicenter, E.devastation, E.heavy, E.light, E.finished_at - E.started_at, E.orig_dev_range, E.orig_heavy_range, E.orig_light_range)
+
+/datum/controller/subsystem/explosion/proc/Create(atom/epicenter, devastation_range, heavy_impact_range, light_impact_range, flash_range, adminlog = TRUE, ignorecap = FALSE, flame_range = 0 , silent = FALSE, smoke = FALSE)
+ epicenter = get_turf(epicenter)
+ if(!epicenter)
+ return
+
+ if(adminlog)
+ message_admins("Explosion with size ([devastation_range], [heavy_impact_range], [light_impact_range], [flame_range]) in area: [get_area(epicenter)] [ADMIN_COORDJMP(epicenter)]")
+ log_game("Explosion with size ([devastation_range], [heavy_impact_range], [light_impact_range], [flame_range]) in area [epicenter.loc.name] ([epicenter.x],[epicenter.y],[epicenter.z])")
+
+ var/datum/explosion/E = new(++id_counter, epicenter, devastation_range, heavy_impact_range, light_impact_range, flash_range, flame_range, silent, smoke, ignorecap)
+
+ if(heavy_impact_range > 1)
+ var/datum/effect_system/explosion/Eff
+ if(smoke)
+ Eff = new /datum/effect_system/explosion/smoke
+ else
+ Eff = new
+ Eff.set_up(epicenter)
+ Eff.start()
+
+ //flash mobs
+ if(flash_range)
+ for(var/mob/living/L in viewers(flash_range, epicenter))
+ L.flash_act()
+
+ if(!silent)
+ ExplosionSound(epicenter, devastation_range, heavy_impact_range, E.extent)
+
+ //add to SS
+ if(E.extent)
+ tick_portions_required += 2 + (config.reactionary_explosions ? 1 : 0)
+ explosions += E
+ else
+ logs += E //Already done processing
+
+/datum/controller/subsystem/explosion/proc/CreateDynamic(atom/epicenter, power, flash_range, adminlog = TRUE, ignorecap = TRUE, flame_range = 0 , silent = FALSE, smoke = TRUE)
+ if(!power)
+ return
+ var/range = round((2 * power) ** dyn_ex_scale)
+ Create(epicenter, round(range * 0.25), round(range * 0.5), round(range), flash_range*range, adminlog, ignorecap, flame_range*range, silent, smoke)
+
+// Using default dyn_ex scale:
+// 100 explosion power is a (5, 10, 20) explosion.
+// 75 explosion power is a (4, 8, 17) explosion.
+// 50 explosion power is a (3, 7, 14) explosion.
+// 25 explosion power is a (2, 5, 10) explosion.
+// 10 explosion power is a (1, 3, 6) explosion.
+// 5 explosion power is a (0, 1, 3) explosion.
+// 1 explosion power is a (0, 0, 1) explosion.
+
+/datum/explosion
+ var/explosion_id
+ var/turf/epicenter
+
+ var/started_at
+ var/finished_at
+ var/tick_started
+ var/tick_finished
+
+ var/turfs_processed = FALSE
+ var/densities_processed = FALSE
+
+ var/orig_dev_range
+ var/orig_heavy_range
+ var/orig_light_range
+ var/orig_flash_range
+ var/orig_flame_range
+
+ var/devastation
+ var/heavy
+ var/light
+ var/extent
+
+ var/flash
+ var/flame
+
+ var/gather_dist = 0
+
+ var/list/gathered_turfs
+ var/list/calculated_turfs
+
+ var/list/unsafe_turfs
+
+/datum/explosion/New(id, turf/epi, devastation_range, heavy_impact_range, light_impact_range, flash_range, flame_range, silent, smoke, ignorecap)
+ explosion_id = id
+ epicenter = epi
+
+ densities_processed = !config.reactionary_explosions
+
+ orig_dev_range = devastation_range
+ orig_heavy_range = heavy_impact_range
+ orig_light_range = light_impact_range
+ orig_flash_range = flash_range
+ orig_flame_range = flame_range
+
+ if(!ignorecap && !("[epicenter.z]" in SSexplosion.zlevels_that_ignore_bombcap))
+ //Clamp all values
+ devastation_range = min(SSexplosion.devastation_cap, devastation_range)
+ heavy_impact_range = min(SSexplosion.heavy_cap, heavy_impact_range)
+ light_impact_range = min(SSexplosion.light_cap, light_impact_range)
+ flash_range = min(SSexplosion.flash_cap, flash_range)
+ flame_range = min(SSexplosion.flame_cap, flame_range)
+
+ //store this
+ devastation = devastation_range
+ heavy = heavy_impact_range
+ light = light_impact_range
+
+ extent = max(devastation_range, heavy_impact_range, light_impact_range, flame_range)
+
+ flash = flash_range
+ flame = flame_range
+
+ started_at = REALTIMEOFDAY
+ tick_started = world.time
+
+ gathered_turfs = list()
+ calculated_turfs = list()
+ unsafe_turfs = list()
+
+// Play sounds; we want sounds to be different depending on distance so we will manually do it ourselves.
+// Stereo users will also hear the direction of the explosion!
+
+// Calculate far explosion sound range. Only allow the sound effect for heavy/devastating explosions.
+// 3/7/14 will calculate to 80 + 35
+/proc/ExplosionSound(turf/epicenter, devastation_range, heavy_impact_range, extent)
+ var/far_dist = 0
+ far_dist += heavy_impact_range * 5
+ far_dist += devastation_range * 20
+
+ var/z0 = epicenter.z
+
+ var/frequency = get_rand_frequency()
+ var/ex_sound = get_sfx("explosion")
+ for(var/mob/M in GLOB.player_list)
+ // Double check for client
+ var/turf/M_turf = get_turf(M)
+ if(M_turf && M_turf.z == z0)
+ var/dist = get_dist(M_turf, epicenter)
+ // If inside the blast radius + world.view - 2
+ if(dist <= round(extent + world.view - 2, 1))
+ M.playsound_local(epicenter, ex_sound, 100, 1, frequency, falloff = 5)
+ // You hear a far explosion if you're outside the blast radius. Small bombs shouldn't be heard all over the station.
+ else if(dist <= far_dist)
+ var/far_volume = Clamp(far_dist, 30, 50) // Volume is based on explosion size and dist
+ far_volume += (dist <= far_dist * 0.5 ? 50 : 0) // add 50 volume if the mob is pretty close to the explosion
+ M.playsound_local(epicenter, 'sound/effects/explosionfar.ogg', far_volume, 1, frequency, falloff = 5)
+
+/datum/explosion/Destroy()
+ SSexplosion.explosions -= src
+ SSexplosion.logs -= src
+ LAZYCLEARLIST(gathered_turfs)
+ LAZYCLEARLIST(calculated_turfs)
+ LAZYCLEARLIST(unsafe_turfs)
+ return ..()
+
+/datum/controller/subsystem/explosion/proc/GatherTurfs(datum/explosion/E)
+ var/turf/epicenter = E.epicenter
+
+ var/x0 = epicenter.x
+ var/y0 = epicenter.y
+ var/z0 = epicenter.z
+
+ var/c_dist = E.gather_dist
+ var/dist = E.extent
+
+ var/list/L = E.gathered_turfs
+
+ if(!c_dist)
+ L += epicenter
+ ++c_dist
+
+ while( c_dist <= dist )
+ var/y = y0 + c_dist
+ var/x = x0 - c_dist + 1
+ for(x in x to x0 + c_dist)
+ var/turf/T = locate(x, y, z0)
+ if(T)
+ L += T
+
+ y = y0 + c_dist - 1
+ x = x0 + c_dist
+ for(y in y0 - c_dist to y)
+ var/turf/T = locate(x, y, z0)
+ if(T)
+ L += T
+
+ y = y0 - c_dist
+ x = x0 + c_dist - 1
+ for(x in x0 - c_dist to x)
+ var/turf/T = locate(x, y, z0)
+ if(T)
+ L += T
+
+ y = y0 - c_dist + 1
+ x = x0 - c_dist
+ for(y in y to y0 + c_dist)
+ var/turf/T = locate(x, y, z0)
+ if(T)
+ L += T
+ ++c_dist
+
+ if(MC_TICK_CHECK)
+ break
+
+ if(c_dist > dist)
+ E.turfs_processed = TRUE
+ return TRUE
+ else
+ E.gather_dist = c_dist
+ return FALSE
+
+/datum/controller/subsystem/explosion/proc/DensityCalculate(datum/explosion/E, done_gathering_turfs)
+ var/list/L = E.calculated_turfs
+ var/cut_to = 1
+ for(var/I in E.gathered_turfs) // we cache the explosion block rating of every turf in the explosion area
+ var/turf/T = I
+ ++cut_to
+
+ var/current_exp_block = T.density ? T.explosion_block : 0
+
+ for(var/obj/machinery/door/D in T)
+ if(D.density)
+ current_exp_block += D.explosion_block
+
+ for(var/obj/structure/window/W in T)
+ if(W.reinf && W.fulltile)
+ current_exp_block += W.explosion_block
+
+ for(var/obj/structure/blob/B in T)
+ current_exp_block += B.explosion_block
+
+ L[T] = current_exp_block
+
+ if(MC_TICK_CHECK)
+ E.gathered_turfs.Cut(1, cut_to)
+ return FALSE
+
+ E.gathered_turfs.Cut()
+ return done_gathering_turfs
+
+/datum/controller/subsystem/explosion/proc/ProcessExplosion(datum/explosion/E, done_calculating_turfs)
+ //cache shit for speed
+ var/id = E.explosion_id
+
+ var/list/cached_unsafe = E.unsafe_turfs
+ var/list/cached_exp_block = E.calculated_turfs
+ var/list/affected_turfs = cached_exp_block ? cached_exp_block : E.gathered_turfs
+
+ var/devastation_range = E.devastation
+ var/heavy_impact_range = E.heavy
+ var/light_impact_range = E.light
+
+ var/flame_range = E.flame
+ var/throw_range_max = E.extent
+
+ var/turf/epi = E.epicenter
+
+ var/x0 = epi.x
+ var/y0 = epi.y
+
+ var/cut_to = 1
+ for(var/TI in affected_turfs)
+ var/turf/T = TI
+ ++cut_to
+
+ var/init_dist = cheap_hypotenuse(T.x, T.y, x0, y0)
+ var/dist = init_dist
+
+ if(cached_exp_block)
+ var/turf/Trajectory = T
+ while(Trajectory != epi)
+ Trajectory = get_step_towards(Trajectory, epi)
+ dist += cached_exp_block[Trajectory]
+
+ var/flame_dist = dist < flame_range
+ var/throw_dist = dist
+
+ if(dist < devastation_range)
+ dist = 1
+ else if(dist < heavy_impact_range)
+ dist = 2
+ else if(dist < light_impact_range)
+ dist = 3
+ else
+ dist = 0
+
+ //------- EX_ACT AND TURF FIRES -------
+
+ if(flame_dist && prob(40) && !isspaceturf(T) && !T.density)
+ new /obj/effect/hotspot(T) //Mostly for ambience!
+
+ if(dist > 0)
+ T.explosion_level = max(T.explosion_level, dist) //let the bigger one have it
+ T.explosion_id = id
+ T.ex_act(dist)
+ cached_unsafe += T
+
+ //--- THROW ITEMS AROUND ---
+
+ var/throw_dir = get_dir(epi, T)
+ for(var/obj/item/I in T)
+ if(!I.anchored)
+ var/throw_range = rand(throw_dist, throw_range_max)
+ var/turf/throw_at = get_ranged_target_turf(I, throw_dir, throw_range)
+ I.throw_speed = 4 //Temporarily change their throw_speed for embedding purposes (Resets when it finishes throwing, regardless of hitting anything)
+ I.throw_at(throw_at, throw_range, 4)
+
+ if(MC_TICK_CHECK)
+ var/circumference = (PI * (init_dist + 4) * 2) //+4 to radius to prevent shit gaps
+ if(cached_unsafe.len > circumference) //only do this every revolution
+ for(var/Unexplode in cached_unsafe)
+ var/turf/UnexplodeT = Unexplode
+ UnexplodeT.explosion_level = 0
+ cached_unsafe.Cut()
+ done_calculating_turfs = FALSE
+ break
+
+ affected_turfs.Cut(1, cut_to)
+
+ if(!done_calculating_turfs)
+ return FALSE
+
+ //unfuck the shit
+ for(var/Unexplode in cached_unsafe)
+ var/turf/UnexplodeT = Unexplode
+ UnexplodeT.explosion_level = 0
+ cached_unsafe.Cut()
+
+ E.finished_at = REALTIMEOFDAY
+ E.tick_finished = world.time
+
+ return TRUE
+
+/client/proc/check_bomb_impacts()
+ set name = "Check Bomb Impact"
+ set category = "Debug"
+
+ var/newmode = alert("Use reactionary explosions?","Check Bomb Impact", "Yes", "No")
+ var/turf/epicenter = get_turf(mob)
+ if(!epicenter)
+ return
+
+ var/x0 = epicenter.x
+ var/y0 = epicenter.y
+
+ var/dev = 0
+ var/heavy = 0
+ var/light = 0
+ var/list/choices = list("Small Bomb","Medium Bomb","Big Bomb","Custom Bomb")
+ var/choice = input("Bomb Size?") in choices
+ switch(choice)
+ if(null)
+ return 0
+ if("Small Bomb")
+ dev = 1
+ heavy = 2
+ light = 3
+ if("Medium Bomb")
+ dev = 2
+ heavy = 3
+ light = 4
+ if("Big Bomb")
+ dev = 3
+ heavy = 5
+ light = 7
+ if("Custom Bomb")
+ dev = input("Devestation range (Tiles):") as num
+ heavy = input("Heavy impact range (Tiles):") as num
+ light = input("Light impact range (Tiles):") as num
+ else
+ return
+
+ var/datum/explosion/E = new(null, epicenter, dev, heavy, light, ignorecap = TRUE)
+
+ while(!SSexplosion.GatherTurfs(E))
+ stoplag()
+ var/list/turfs
+ if(newmode)
+ while(!SSexplosion.DensityCalculate(E, TRUE))
+ stoplag()
+ turfs = E.calculated_turfs.Copy()
+ else
+ turfs = E.gathered_turfs.Copy()
+
+ qdel(E)
+
+ for(var/I in turfs)
+ var/turf/T = I
+ var/dist = cheap_hypotenuse(T.x, T.y, x0, y0) + turfs[T]
+
+ if(dist < dev)
+ T.color = "red"
+ T.maptext = "Dev"
+ else if (dist < heavy)
+ T.color = "yellow"
+ T.maptext = "Heavy"
+ else if (dist < light)
+ T.color = "blue"
+ T.maptext = "Light"
+ CHECK_TICK
+
+ sleep(100)
+ for(var/I in turfs)
+ var/turf/T = I
+ T.color = null
+ T.maptext = null
diff --git a/code/game/objects/explosion.dm b/code/datums/explosion.dm
similarity index 52%
rename from code/game/objects/explosion.dm
rename to code/datums/explosion.dm
index 11079bf847..3f8d45448f 100644
--- a/code/game/objects/explosion.dm
+++ b/code/datums/explosion.dm
@@ -1,14 +1,48 @@
-/proc/explosion(turf/epicenter, devastation_range, heavy_impact_range, light_impact_range, flash_range, adminlog = 1, ignorecap = 0, flame_range, silent = 0, smoke = 1)
- set waitfor = 0
- src = null //so we don't abort once src is deleted
+#define EXPLOSION_THROW_SPEED 4
+
+GLOBAL_LIST_EMPTY(explosions)
+//Against my better judgement, I will return the explosion datum
+//If I see any GC errors for it I will find you
+//and I will gib you
+/proc/explosion(atom/epicenter, devastation_range, heavy_impact_range, light_impact_range, flash_range, adminlog = TRUE, ignorecap = FALSE, flame_range = 0 , silent = FALSE, smoke = FALSE)
+ return new /datum/explosion(epicenter, devastation_range, heavy_impact_range, light_impact_range, flash_range, adminlog, ignorecap, flame_range, silent, smoke)
+
+//This datum creates 3 async tasks
+//1 GatherSpiralTurfsProc runs spiral_range_turfs(tick_checked = TRUE) to populate the affected_turfs list
+//2 CaculateExplosionBlock adds the blockings to the cached_exp_block list
+//3 The main thread explodes the prepared turfs
+
+/datum/explosion
+ var/explosion_id
+ var/started_at
+ var/running = TRUE
+ var/stopped = 0 //This is the number of threads stopped !DOESN'T COUNT THREAD 2!
+ var/static/id_counter = 0
+
+#define EX_PREPROCESS_EXIT_CHECK \
+ if(!running) {\
+ stopped = 2;\
+ qdel(src);\
+ return;\
+ }
+
+#define EX_PREPROCESS_CHECK_TICK \
+ if(TICK_CHECK) {\
+ stoplag();\
+ EX_PREPROCESS_EXIT_CHECK\
+ }
+
+/datum/explosion/New(atom/epicenter, devastation_range, heavy_impact_range, light_impact_range, flash_range, adminlog, ignorecap, flame_range, silent, smoke)
+ set waitfor = FALSE
+
+ var/id = ++id_counter
+ explosion_id = id
+
epicenter = get_turf(epicenter)
if(!epicenter)
return
- if(isnull(flame_range))
- flame_range = light_impact_range
- if(isnull(flash_range))
- flash_range = devastation_range
+ GLOB.explosions += src
// Archive the uncapped explosion for the doppler array
var/orig_dev_range = devastation_range
@@ -30,14 +64,13 @@
//I would make this not ex_act the thing that triggered the explosion,
//but everything that explodes gives us their loc or a get_turf()
//and somethings expect us to ex_act them so they can qdel()
- sleep(1) //tldr, let the calling proc call qdel(src) before we explode
+ stoplag() //tldr, let the calling proc call qdel(src) before we explode
- var/static/explosionid = 1
- var/id = explosionid++
- var/start = world.timeofday
+ EX_PREPROCESS_EXIT_CHECK
+
+ started_at = REALTIMEOFDAY
var/max_range = max(devastation_range, heavy_impact_range, light_impact_range, flame_range)
- var/list/cached_exp_block = list()
if(adminlog)
message_admins("Explosion with size ([devastation_range], [heavy_impact_range], [light_impact_range], [flame_range]) in area: [get_area(epicenter)] [ADMIN_COORDJMP(epicenter)]")
@@ -53,23 +86,27 @@
far_dist += heavy_impact_range * 5
far_dist += devastation_range * 20
+ var/x0 = epicenter.x
+ var/y0 = epicenter.y
+ var/z0 = epicenter.z
+
if(!silent)
var/frequency = get_rand_frequency()
var/ex_sound = get_sfx("explosion")
for(var/mob/M in GLOB.player_list)
// Double check for client
- if(M && M.client)
- var/turf/M_turf = get_turf(M)
- if(M_turf && M_turf.z == epicenter.z)
- var/dist = get_dist(M_turf, epicenter)
- // If inside the blast radius + world.view - 2
- if(dist <= round(max_range + world.view - 2, 1))
- M.playsound_local(epicenter, ex_sound, 100, 1, frequency, falloff = 5)
- // You hear a far explosion if you're outside the blast radius. Small bombs shouldn't be heard all over the station.
- else if(dist <= far_dist)
- var/far_volume = Clamp(far_dist, 30, 50) // Volume is based on explosion size and dist
- far_volume += (dist <= far_dist * 0.5 ? 50 : 0) // add 50 volume if the mob is pretty close to the explosion
- M.playsound_local(epicenter, 'sound/effects/explosionfar.ogg', far_volume, 1, frequency, falloff = 5)
+ var/turf/M_turf = get_turf(M)
+ if(M_turf && M_turf.z == z0)
+ var/dist = get_dist(M_turf, epicenter)
+ // If inside the blast radius + world.view - 2
+ if(dist <= round(max_range + world.view - 2, 1))
+ M.playsound_local(epicenter, ex_sound, 100, 1, frequency, falloff = 5)
+ // You hear a far explosion if you're outside the blast radius. Small bombs shouldn't be heard all over the station.
+ else if(dist <= far_dist)
+ var/far_volume = Clamp(far_dist, 30, 50) // Volume is based on explosion size and dist
+ far_volume += (dist <= far_dist * 0.5 ? 50 : 0) // add 50 volume if the mob is pretty close to the explosion
+ M.playsound_local(epicenter, 'sound/effects/explosionfar.ogg', far_volume, 1, frequency, falloff = 5)
+ EX_PREPROCESS_CHECK_TICK
//postpone processing for a bit
var/postponeCycles = max(round(devastation_range/8),1)
@@ -77,66 +114,52 @@
SSmachines.postpone(postponeCycles)
if(heavy_impact_range > 1)
+ var/datum/effect_system/explosion/E
if(smoke)
- var/datum/effect_system/explosion/smoke/E = new/datum/effect_system/explosion/smoke()
- E.set_up(epicenter)
- E.start()
+ E = new /datum/effect_system/explosion/smoke
else
- var/datum/effect_system/explosion/E = new/datum/effect_system/explosion()
- E.set_up(epicenter)
- E.start()
-
- var/x0 = epicenter.x
- var/y0 = epicenter.y
- var/z0 = epicenter.z
-
- var/list/affected_turfs = spiral_range_turfs(max_range, epicenter)
-
- if(config.reactionary_explosions)
- for(var/turf/T in affected_turfs) // we cache the explosion block rating of every turf in the explosion area
- cached_exp_block[T] = 0
- if(T.density && T.explosion_block)
- cached_exp_block[T] += T.explosion_block
-
- for(var/obj/machinery/door/D in T)
- if(D.density && D.explosion_block)
- cached_exp_block[T] += D.explosion_block
-
- for(var/obj/structure/window/W in T)
- if(W.reinf && W.fulltile)
- cached_exp_block[T] += W.explosion_block
-
- for(var/obj/structure/blob/B in T)
- cached_exp_block[T] += B.explosion_block
- CHECK_TICK
+ E = new
+ E.set_up(epicenter)
+ E.start()
+
+ EX_PREPROCESS_CHECK_TICK
//flash mobs
if(flash_range)
for(var/mob/living/L in viewers(flash_range, epicenter))
L.flash_act()
- CHECK_TICK
+ EX_PREPROCESS_CHECK_TICK
var/list/exploded_this_tick = list() //open turfs that need to be blocked off while we sleep
- for(var/turf/T in affected_turfs)
+ var/list/affected_turfs = GatherSpiralTurfs(max_range, epicenter)
- if (!T)
- continue
+ var/reactionary = config.reactionary_explosions
+ var/list/cached_exp_block
+
+ if(reactionary)
+ cached_exp_block = CaculateExplosionBlock(affected_turfs)
+
+ //lists are guaranteed to contain at least 1 turf at this point
+
+ var/iteration = 0
+ var/affTurfLen = affected_turfs.len
+ var/expBlockLen = cached_exp_block.len
+ for(var/TI in affected_turfs)
+ var/turf/T = TI
+ ++iteration
var/init_dist = cheap_hypotenuse(T.x, T.y, x0, y0)
var/dist = init_dist
- if(config.reactionary_explosions)
+ if(reactionary)
var/turf/Trajectory = T
while(Trajectory != epicenter)
Trajectory = get_step_towards(Trajectory, epicenter)
dist += cached_exp_block[Trajectory]
- var/flame_dist = 0
+ var/flame_dist = dist < flame_range
var/throw_dist = dist
- if(dist < flame_range)
- flame_dist = 1
-
if(dist < devastation_range)
dist = 1
else if(dist < heavy_impact_range)
@@ -148,27 +171,61 @@
//------- EX_ACT AND TURF FIRES -------
- if(T)
- if(flame_dist && prob(40) && !isspaceturf(T) && !T.density)
- new /obj/effect/hotspot(T) //Mostly for ambience!
- if(dist > 0)
- T.explosion_level = max(T.explosion_level, dist) //let the bigger one have it
- T.explosion_id = id
- T.ex_act(dist)
- exploded_this_tick += T
+ if(flame_dist && prob(40) && !isspaceturf(T) && !T.density)
+ new /obj/effect/hotspot(T) //Mostly for ambience!
+
+ if(dist > 0)
+ T.explosion_level = max(T.explosion_level, dist) //let the bigger one have it
+ T.explosion_id = id
+ T.ex_act(dist)
+ exploded_this_tick += T
//--- THROW ITEMS AROUND ---
var/throw_dir = get_dir(epicenter,T)
for(var/obj/item/I in T)
- if(I && !I.anchored)
+ if(!I.anchored)
var/throw_range = rand(throw_dist, max_range)
var/turf/throw_at = get_ranged_target_turf(I, throw_dir, throw_range)
- I.throw_speed = 4 //Temporarily change their throw_speed for embedding purposes (Reset when it finishes throwing, regardless of hitting anything)
- I.throw_at(throw_at, throw_range, I.throw_speed)
+ I.throw_speed = EXPLOSION_THROW_SPEED //Temporarily change their throw_speed for embedding purposes (Reset when it finishes throwing, regardless of hitting anything)
+ I.throw_at(throw_at, throw_range, EXPLOSION_THROW_SPEED)
- if(TICK_CHECK)
+ //wait for the lists to repop
+ var/break_condition
+ if(reactionary)
+ //If we've caught up to the density checker thread and there are no more turfs to process
+ break_condition = iteration == expBlockLen && iteration < affTurfLen
+ else
+ //If we've caught up to the turf gathering thread and it's still running
+ break_condition = iteration == affTurfLen && !stopped
+
+ if(break_condition || TICK_CHECK)
stoplag()
+
+ if(!running)
+ break
+
+ //update the trackers
+ affTurfLen = affected_turfs.len
+ expBlockLen = cached_exp_block.len
+
+ if(break_condition)
+ if(reactionary)
+ //until there are more block checked turfs than what we are currently at
+ //or the explosion has stopped
+ UNTIL(iteration < affTurfLen || !running)
+ else
+ //until there are more gathered turfs than what we are currently at
+ //or there are no more turfs to gather/the explosion has stopped
+ UNTIL(iteration < expBlockLen || stopped)
+
+ if(!running)
+ break
+
+ //update the trackers
+ affTurfLen = affected_turfs.len
+ expBlockLen = cached_exp_block.len
+
var/circumference = (PI * (init_dist + 4) * 2) //+4 to radius to prevent shit gaps
if(exploded_this_tick.len > circumference) //only do this every revolution
for(var/Unexplode in exploded_this_tick)
@@ -182,24 +239,67 @@
UnexplodeT.explosion_level = 0
exploded_this_tick.Cut()
- var/took = (world.timeofday-start)/10
- //You need to press the DebugGame verb to see these now....they were getting annoying and we've collected a fair bit of data. Just -test- changes to explosion code using this please so we can compare
+ var/took = (REALTIMEOFDAY - started_at) / 10
+
+ //You need to press the DebugGame verb to see these now....they were getting annoying and we've collected a fair bit of data. Just -test- changes to explosion code using this please so we can compare
if(GLOB.Debug2)
log_world("## DEBUG: Explosion([x0],[y0],[z0])(d[devastation_range],h[heavy_impact_range],l[light_impact_range]): Took [took] seconds.")
- //Machines which report explosions.
- for(var/array in GLOB.doppler_arrays)
- var/obj/machinery/doppler_array/A = array
- A.sense_explosion(epicenter,devastation_range,heavy_impact_range,light_impact_range,took,orig_dev_range,orig_heavy_range,orig_light_range)
+ if(!stopped) //if we aren't in a hurry
+ //Machines which report explosions.
+ for(var/array in GLOB.doppler_arrays)
+ var/obj/machinery/doppler_array/A = array
+ A.sense_explosion(epicenter, devastation_range, heavy_impact_range, light_impact_range, took,orig_dev_range, orig_heavy_range, orig_light_range)
- return 1
+ ++stopped
+ qdel(src)
+#undef EX_PREPROCESS_EXIT_CHECK
+#undef EX_PREPROCESS_CHECK_TICK
+//asyncly populate the affected_turfs list
+/datum/explosion/proc/GatherSpiralTurfs(range, turf/epicenter)
+ set waitfor = FALSE
+ . = list()
+ spiral_range_turfs(range, epicenter, outlist = ., tick_checked = TRUE)
+ ++stopped
-/proc/secondaryexplosion(turf/epicenter, range)
- for(var/turf/tile in spiral_range_turfs(range, epicenter))
- tile.ex_act(2)
+/datum/explosion/proc/CaculateExplosionBlock(list/affected_turfs)
+ set waitfor = FALSE
+ . = list()
+ var/processed = 0
+ while(!stopped && running)
+ var/I
+ for(I in (processed + 1) to affected_turfs.len) // we cache the explosion block rating of every turf in the explosion area
+ var/turf/T = affected_turfs[I]
+ var/current_exp_block = T.density ? T.explosion_block : 0
+
+ for(var/obj/machinery/door/D in T)
+ if(D.density)
+ current_exp_block += D.explosion_block
+
+ for(var/obj/structure/window/W in T)
+ if(W.reinf && W.fulltile)
+ current_exp_block += W.explosion_block
+
+ for(var/obj/structure/blob/B in T)
+ current_exp_block += B.explosion_block
+
+ .[T] = current_exp_block
+
+ if(TICK_CHECK)
+ break
+
+ processed = I
+ stoplag()
+
+/datum/explosion/Destroy()
+ running = FALSE
+ if(stopped < 2) //wait for main thread and spiral_range thread
+ return QDEL_HINT_IWILLGC
+ GLOB.explosions -= src
+ return ..()
/client/proc/check_bomb_impacts()
set name = "Check Bomb Impact"
diff --git a/code/datums/explosion.dm.rej b/code/datums/explosion.dm.rej
new file mode 100644
index 0000000000..ce5c5ddcfc
--- /dev/null
+++ b/code/datums/explosion.dm.rej
@@ -0,0 +1,53 @@
+diff a/code/datums/explosion.dm b/code/datums/explosion.dm (rejected hunks)
+@@ -1,10 +1,48 @@
+-/proc/explosion(turf/epicenter, devastation_range, heavy_impact_range, light_impact_range, flash_range, adminlog = 1, ignorecap = 0, flame_range = 0 ,silent = 0, smoke = 1)
+- set waitfor = 0
+- src = null //so we don't abort once src is deleted
++GLOBAL_LIST_EMPTY(explosions)
++//Against my better judgement, I will return the explosion datum
++//If I see any GC errors for it I will find you
++//and I will gib you
++// --Cyberboss
++/proc/explosion(atom/epicenter, devastation_range, heavy_impact_range, light_impact_range, flash_range, adminlog = TRUE, ignorecap = FALSE, flame_range = 0 , silent = FALSE, smoke = FALSE)
++ return new /datum/explosion(epicenter, devastation_range, heavy_impact_range, light_impact_range, flash_range, adminlog, ignorecap, flame_range, silent, smoke)
++
++//This datum creates 3 threads
++//1 GatherSpiralTurfsProc runs spiral_range_turfs(tick_checked = TRUE) to populate the affected_turfs list
++//2 CaculateExplosionBlock adds the blockings to the cached_exp_block list
++//3 The main thread explodes the prepared turfs
++
++/datum/explosion
++ var/explosion_id
++ var/started_at
++ var/running = TRUE
++ var/stopped = 0 //This is the number of threads stopped !DOESN'T COUNT THREAD 2!
++ var/static/id_counter = 0
++
++#define EX_PREPROCESS_EXIT_CHECK \
++ if(!running) {\
++ stopped = 2;\
++ qdel(src);\
++ return;\
++ }
++
++#define EX_PREPROCESS_CHECK_TICK \
++ if(TICK_CHECK) {\
++ stoplag();\
++ EX_PREPROCESS_EXIT_CHECK\
++ }
++
++/datum/explosion/New(atom/epicenter, devastation_range, heavy_impact_range, light_impact_range, flash_range, adminlog, ignorecap, flame_range, silent, smoke)
++ set waitfor = FALSE
++
++ var/id = ++id_counter
++ explosion_id = id
++
+ epicenter = get_turf(epicenter)
+ if(!epicenter)
+ return
+
++ GLOB.explosions += src
++
+ // Archive the uncapped explosion for the doppler array
+ var/orig_dev_range = devastation_range
+ var/orig_heavy_range = heavy_impact_range
diff --git a/code/game/atoms.dm b/code/game/atoms.dm
index 4a0f05602b..cc9ea86682 100644
--- a/code/game/atoms.dm
+++ b/code/game/atoms.dm
@@ -274,7 +274,6 @@
return
/atom/proc/ex_act(severity, target)
- set waitfor = FALSE
contents_explosion(severity, target)
/atom/proc/blob_act(obj/structure/blob/B)
diff --git a/code/game/atoms.dm.rej b/code/game/atoms.dm.rej
new file mode 100644
index 0000000000..d90cdba73e
--- /dev/null
+++ b/code/game/atoms.dm.rej
@@ -0,0 +1,9 @@
+diff a/code/game/atoms.dm b/code/game/atoms.dm (rejected hunks)
+@@ -267,6 +267,7 @@
+ return
+
+ /atom/proc/ex_act(severity, target)
++ set waitfor = FALSE
+ contents_explosion(severity, target)
+
+ /atom/proc/blob_act(obj/structure/blob/B)
diff --git a/code/game/machinery/doppler_array.dm b/code/game/machinery/doppler_array.dm
index 48dc35da4b..4053e9755c 100644
--- a/code/game/machinery/doppler_array.dm
+++ b/code/game/machinery/doppler_array.dm
@@ -1,5 +1,5 @@
-GLOBAL_LIST_EMPTY(doppler_arrays)
-
+GLOBAL_LIST_EMPTY(doppler_arrays)
+
/obj/machinery/doppler_array
name = "tachyon-doppler array"
desc = "A highly precise directional sensor array which measures the release of quants from decaying tachyons. The doppler shifting of the mirror-image formed by these quants can reveal the size, location and temporal affects of energetic disturbances within a large radius ahead of the array.\nAlt-click to rotate it clockwise."
@@ -13,10 +13,10 @@ GLOBAL_LIST_EMPTY(doppler_arrays)
/obj/machinery/doppler_array/New()
..()
- GLOB.doppler_arrays += src
+ GLOB.doppler_arrays += src
/obj/machinery/doppler_array/Destroy()
- GLOB.doppler_arrays -= src
+ GLOB.doppler_arrays -= src
return ..()
/obj/machinery/doppler_array/process()
diff --git a/code/game/mecha/mecha.dm b/code/game/mecha/mecha.dm
index 66cc6be38a..1e6fd57b1d 100644
--- a/code/game/mecha/mecha.dm
+++ b/code/game/mecha/mecha.dm
@@ -154,7 +154,7 @@
M.forceMove(loc)
if(prob(30))
- explosion(get_turf(loc), 0, 0, 1, 3)
+ explosion(get_turf(loc), 0, 0, 1, 3)
if(wreckage)
var/obj/structure/mecha_wreckage/WR = new wreckage(loc, AI)
diff --git a/code/game/objects/effects/mines.dm b/code/game/objects/effects/mines.dm
index 46c2306a79..74459cc85d 100644
--- a/code/game/objects/effects/mines.dm
+++ b/code/game/objects/effects/mines.dm
@@ -39,7 +39,7 @@
var/range_flash = 3
/obj/effect/mine/explosive/mineEffect(mob/victim)
- explosion(loc, range_devastation, range_heavy, range_light, range_flash)
+ explosion(loc, range_devastation, range_heavy, range_light, range_flash)
/obj/effect/mine/stun
diff --git a/code/game/objects/items/devices/PDA/PDA.dm b/code/game/objects/items/devices/PDA/PDA.dm
index 4a64cf5c0d..d9f4426fa1 100644
--- a/code/game/objects/items/devices/PDA/PDA.dm
+++ b/code/game/objects/items/devices/PDA/PDA.dm
@@ -890,9 +890,9 @@ GLOBAL_LIST_EMPTY(PDAs)
if(T)
T.hotspot_expose(700,125)
if(istype(cartridge, /obj/item/weapon/cartridge/syndicate))
- explosion(T, -1, 1, 3, 4)
+ explosion(T, -1, 1, 3, 4)
else
- explosion(T, -1, -1, 2, 3)
+ explosion(T, -1, -1, 2, 3)
qdel(src)
return
diff --git a/code/game/objects/items/devices/powersink.dm b/code/game/objects/items/devices/powersink.dm
index e03c85c09a..a4e1a45814 100644
--- a/code/game/objects/items/devices/powersink.dm
+++ b/code/game/objects/items/devices/powersink.dm
@@ -141,5 +141,5 @@
if(power_drained >= max_power)
STOP_PROCESSING(SSobj, src)
- explosion(src.loc, 4,8,16,32)
+ explosion(src.loc, 4,8,16,32)
qdel(src)
diff --git a/code/game/objects/items/weapons/grenades/ghettobomb.dm b/code/game/objects/items/weapons/grenades/ghettobomb.dm
index f4d573e3ba..76f3c8ac73 100644
--- a/code/game/objects/items/weapons/grenades/ghettobomb.dm
+++ b/code/game/objects/items/weapons/grenades/ghettobomb.dm
@@ -59,7 +59,7 @@
/obj/item/weapon/grenade/iedcasing/prime() //Blowing that can up
update_mob()
- explosion(src.loc,-1,-1,2, flame_range = 4) // small explosion, plus a very large fireball.
+ explosion(src.loc,-1,-1,2, flame_range = 4) // small explosion, plus a very large fireball.
qdel(src)
/obj/item/weapon/grenade/iedcasing/examine(mob/user)
diff --git a/code/game/objects/items/weapons/grenades/syndieminibomb.dm b/code/game/objects/items/weapons/grenades/syndieminibomb.dm
index 67b0dcddeb..6b260d366a 100644
--- a/code/game/objects/items/weapons/grenades/syndieminibomb.dm
+++ b/code/game/objects/items/weapons/grenades/syndieminibomb.dm
@@ -9,7 +9,7 @@
/obj/item/weapon/grenade/syndieminibomb/prime()
update_mob()
- explosion(src.loc,1,2,4,flame_range = 2)
+ explosion(src.loc,1,2,4,flame_range = 2)
qdel(src)
/obj/item/weapon/grenade/syndieminibomb/concussion
@@ -20,7 +20,7 @@
/obj/item/weapon/grenade/syndieminibomb/concussion/prime()
update_mob()
- explosion(src.loc,0,2,3,flame_range = 3)
+ explosion(src.loc,0,2,3,flame_range = 3)
qdel(src)
/obj/item/weapon/grenade/syndieminibomb/concussion/frag
diff --git a/code/game/objects/items/weapons/implants/implant_explosive.dm.rej b/code/game/objects/items/weapons/implants/implant_explosive.dm.rej
new file mode 100644
index 0000000000..20a7e3ef42
--- /dev/null
+++ b/code/game/objects/items/weapons/implants/implant_explosive.dm.rej
@@ -0,0 +1,10 @@
+diff a/code/game/objects/items/weapons/implants/implant_explosive.dm b/code/game/objects/items/weapons/implants/implant_explosive.dm (rejected hunks)
+@@ -47,7 +47,7 @@
+ message_admins("[key_name_admin(imp_in)]? (FLW) has activated their [name] at [A.name] (JMP).")
+ //If the delay is short, just blow up already jeez
+ if(delay <= 7)
+- SSexplosion.Create(src,heavy,medium,weak,weak, flame_range = weak)
++ explosion(src,heavy,medium,weak,weak, flame_range = weak)
+ if(imp_in)
+ imp_in.gib(1)
+ qdel(src)
diff --git a/code/game/objects/items/weapons/storage/backpack.dm b/code/game/objects/items/weapons/storage/backpack.dm
index 0fd70e7ac3..ed502e17cd 100644
--- a/code/game/objects/items/weapons/storage/backpack.dm
+++ b/code/game/objects/items/weapons/storage/backpack.dm
@@ -83,7 +83,7 @@
/obj/item/weapon/storage/backpack/holding/singularity_act(current_size)
var/dist = max((current_size - 2),1)
- explosion(src.loc,(dist),(dist*2),(dist*4))
+ explosion(src.loc,(dist),(dist*2),(dist*4))
return
diff --git a/code/game/objects/structures/crates_lockers/crates/secure.dm b/code/game/objects/structures/crates_lockers/crates/secure.dm
index 603dce4358..75096209f5 100644
--- a/code/game/objects/structures/crates_lockers/crates/secure.dm
+++ b/code/game/objects/structures/crates_lockers/crates/secure.dm
@@ -38,7 +38,7 @@
log_game("[key_name(user)] has detonated [src.name].")
for(var/atom/movable/AM in src)
qdel(AM)
- explosion(get_turf(src), 0, 1, 5, 5)
+ explosion(get_turf(src), 0, 1, 5, 5)
qdel(src)
/obj/structure/closet/crate/secure/weapon
diff --git a/code/modules/admin/admin_verbs.dm.rej b/code/modules/admin/admin_verbs.dm.rej
new file mode 100644
index 0000000000..0f2208d091
--- /dev/null
+++ b/code/modules/admin/admin_verbs.dm.rej
@@ -0,0 +1,24 @@
+diff a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm (rejected hunks)
+@@ -515,11 +515,11 @@ GLOBAL_LIST_INIT(admin_verbs_hideable, AVerbsHideable())
+ var/flash_range = input("Flash range (in tiles):") as null|num
+ if(flash_range == null)
+ return
+- if(devastation_range > SSexplosion.devastation_cap || heavy_impact_range > SSexplosion.heavy_cap || light_impact_range > SSexplosion.light_cap || flash_range > SSexplosion.flash_cap)
++ if(devastation_range > GLOB.MAX_EX_DEVESTATION_RANGE || heavy_impact_range > GLOB.MAX_EX_HEAVY_RANGE || light_impact_range > GLOB.MAX_EX_LIGHT_RANGE || flash_range > GLOB.MAX_EX_FLASH_RANGE)
+ if(alert("Bomb is bigger than the maxcap. Continue?",,"Yes","No") != "Yes")
+ return
+ epicenter = mob.loc //We need to reupdate as they may have moved again
+- SSexplosion.Create(epicenter, devastation_range, heavy_impact_range, light_impact_range, flash_range, TRUE, TRUE)
++ explosion(epicenter, devastation_range, heavy_impact_range, light_impact_range, flash_range, TRUE, TRUE)
+ message_admins("[ADMIN_LOOKUPFLW(usr)] creating an admin explosion at [epicenter.loc].")
+ log_admin("[key_name(usr)] created an admin explosion at [epicenter.loc].")
+ feedback_add_details("admin_verb","Drop Bomb") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
+@@ -532,7 +532,7 @@ GLOBAL_LIST_INIT(admin_verbs_hideable, AVerbsHideable())
+ var/ex_power = input("Explosive Power:") as null|num
+ var/turf/epicenter = mob.loc
+ if(ex_power && epicenter)
+- SSexplosion.CreateDynamic(epicenter, ex_power)
++ dyn_explosion(epicenter, ex_power)
+ message_admins("[ADMIN_LOOKUPFLW(usr)] creating an admin explosion at [epicenter.loc].")
+ log_admin("[key_name(usr)] created an admin explosion at [epicenter.loc].")
+ feedback_add_details("admin_verb","Drop Dynamic Bomb") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
diff --git a/code/modules/admin/secrets.dm.rej b/code/modules/admin/secrets.dm.rej
new file mode 100644
index 0000000000..de958c8ab0
--- /dev/null
+++ b/code/modules/admin/secrets.dm.rej
@@ -0,0 +1,29 @@
+diff a/code/modules/admin/secrets.dm b/code/modules/admin/secrets.dm (rejected hunks)
+@@ -386,19 +386,19 @@
+ feedback_inc("admin_secrets_fun_used",1)
+ feedback_add_details("admin_secrets_fun_used","BC")
+
+- var/newBombCap = input(usr,"What would you like the new bomb cap to be. (entered as the light damage range (the 3rd number in common (1,2,3) notation)) Must be above 4)", "New Bomb Cap", SSexplosion.light_cap) as num|null
++ var/newBombCap = input(usr,"What would you like the new bomb cap to be. (entered as the light damage range (the 3rd number in common (1,2,3) notation)) Must be above 4)", "New Bomb Cap", GLOB.MAX_EX_LIGHT_RANGE) as num|null
+ if (newBombCap < 4)
+ return
+
+- SSexplosion.devastation_cap = round(newBombCap/4)
+- SSexplosion.heavy_cap = round(newBombCap/2)
+- SSexplosion.light_cap = newBombCap
++ GLOB.MAX_EX_DEVESTATION_RANGE = round(newBombCap/4)
++ GLOB.MAX_EX_HEAVY_RANGE = round(newBombCap/2)
++ GLOB.MAX_EX_LIGHT_RANGE = newBombCap
+ //I don't know why these are their own variables, but fuck it, they are.
+- SSexplosion.flash_cap = newBombCap
+- SSexplosion.flame_cap = newBombCap
++ GLOB.MAX_EX_FLASH_RANGE = newBombCap
++ GLOB.MAX_EX_FLAME_RANGE = newBombCap
+
+- message_admins("[key_name_admin(usr)] changed the bomb cap to [SSexplosion.devastation_cap], [SSexplosion.heavy_cap], [SSexplosion.light_cap]")
+- log_admin("[key_name(usr)] changed the bomb cap to [SSexplosion.devastation_cap], [SSexplosion.heavy_cap], [SSexplosion.light_cap]")
++ message_admins("[key_name_admin(usr)] changed the bomb cap to [GLOB.MAX_EX_DEVESTATION_RANGE], [GLOB.MAX_EX_HEAVY_RANGE], [GLOB.MAX_EX_LIGHT_RANGE]")
++ log_admin("[key_name(usr)] changed the bomb cap to [GLOB.MAX_EX_DEVESTATION_RANGE], [GLOB.MAX_EX_HEAVY_RANGE], [GLOB.MAX_EX_LIGHT_RANGE]")
+
+
+ if("lightsout")
diff --git a/code/modules/admin/verbs/randomverbs.dm.rej b/code/modules/admin/verbs/randomverbs.dm.rej
new file mode 100644
index 0000000000..c1defb8299
--- /dev/null
+++ b/code/modules/admin/verbs/randomverbs.dm.rej
@@ -0,0 +1,10 @@
+diff a/code/modules/admin/verbs/randomverbs.dm b/code/modules/admin/verbs/randomverbs.dm (rejected hunks)
+@@ -557,7 +557,7 @@ Traitors and the like can also be revived with the previous role mostly intact.
+ if (alert(src, "Are you sure you want to do this? It will laaag.", "Confirmation", "Yes", "No") == "No")
+ return
+
+- SSexplosion.Create(O, devastation, heavy, light, flash, null, null,flames)
++ explosion(O, devastation, heavy, light, flash, null, null,flames)
+ log_admin("[key_name(usr)] created an explosion ([devastation],[heavy],[light],[flames]) at ([O.x],[O.y],[O.z])")
+ message_admins("[key_name_admin(usr)] created an explosion ([devastation],[heavy],[light],[flames]) at ([O.x],[O.y],[O.z])")
+ feedback_add_details("admin_verb","Explosion") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
diff --git a/code/modules/awaymissions/bluespaceartillery.dm b/code/modules/awaymissions/bluespaceartillery.dm
index e3aeec62cd..2e7ce98510 100644
--- a/code/modules/awaymissions/bluespaceartillery.dm
+++ b/code/modules/awaymissions/bluespaceartillery.dm
@@ -53,7 +53,7 @@
for(var/turf/T in get_area_turfs(thearea.type))
L+=T
var/loc = pick(L)
- explosion(loc,explosiondev,explosionmed,explosionlight)
+ explosion(loc,explosiondev,explosionmed,explosionlight)
reload = 0
/*/mob/proc/openfire()
diff --git a/code/modules/awaymissions/mission_code/wildwest.dm b/code/modules/awaymissions/mission_code/wildwest.dm
index 5847b8de8e..395344ce4c 100644
--- a/code/modules/awaymissions/mission_code/wildwest.dm
+++ b/code/modules/awaymissions/mission_code/wildwest.dm
@@ -107,7 +107,7 @@
var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread
s.set_up(3, 1, src)
s.start()
- explosion(M, 1, 0, 0, 0)
+ explosion(M, 1, 0, 0, 0)
qdel(src)
/////For the Wishgranter///////////
diff --git a/code/modules/events/brand_intelligence.dm b/code/modules/events/brand_intelligence.dm
index b42ea9d554..db2689b250 100644
--- a/code/modules/events/brand_intelligence.dm
+++ b/code/modules/events/brand_intelligence.dm
@@ -57,7 +57,7 @@
M.speak = rampant_speeches.Copy()
M.speak_chance = 7
else
- explosion(upriser.loc, -1, 1, 2, 4, 0)
+ explosion(upriser.loc, -1, 1, 2, 4, 0)
qdel(upriser)
kill()
diff --git a/code/modules/events/processor_overload.dm b/code/modules/events/processor_overload.dm
index e0673cb741..329e887379 100644
--- a/code/modules/events/processor_overload.dm
+++ b/code/modules/events/processor_overload.dm
@@ -32,7 +32,7 @@
var/obj/machinery/telecomms/processor/P = T
if(prob(10))
// Damage the surrounding area to indicate that it popped
- explosion(get_turf(P), 0, 0, 2)
+ explosion(get_turf(P), 0, 0, 2)
// Only a level 1 explosion actually damages the machine
// at all
P.ex_act(1)
diff --git a/code/modules/mining/ores_coins.dm b/code/modules/mining/ores_coins.dm
index b1ba6680c2..9a9fefac03 100644
--- a/code/modules/mining/ores_coins.dm
+++ b/code/modules/mining/ores_coins.dm
@@ -263,11 +263,11 @@
spawn(det_time)
if(primed)
if(quality == 3)
- explosion(src.loc,2,4,9,adminlog = notify_admins)
+ explosion(src.loc,2,4,9,adminlog = notify_admins)
if(quality == 2)
- explosion(src.loc,1,2,5,adminlog = notify_admins)
+ explosion(src.loc,1,2,5,adminlog = notify_admins)
if(quality == 1)
- explosion(src.loc,-1,1,3,adminlog = notify_admins)
+ explosion(src.loc,-1,1,3,adminlog = notify_admins)
qdel(src)
/obj/item/weapon/ore/Initialize()
diff --git a/code/modules/mob/living/carbon/human/species_types/golems.dm.rej b/code/modules/mob/living/carbon/human/species_types/golems.dm.rej
new file mode 100644
index 0000000000..daa843bd29
--- /dev/null
+++ b/code/modules/mob/living/carbon/human/species_types/golems.dm.rej
@@ -0,0 +1,10 @@
+diff a/code/modules/mob/living/carbon/human/species_types/golems.dm b/code/modules/mob/living/carbon/human/species_types/golems.dm (rejected hunks)
+@@ -72,7 +72,7 @@
+ special_names = list("Flood","Fire","Bar","Man")
+
+ /datum/species/golem/plasma/spec_death(gibbed, mob/living/carbon/human/H)
+- SSexplosion.Create(get_turf(H),0,1,2,flame_range = 5)
++ explosion(get_turf(H),0,1,2,flame_range = 5)
+ if(H)
+ H.gib()
+
diff --git a/code/modules/mob/living/silicon/ai/death.dm b/code/modules/mob/living/silicon/ai/death.dm
index c78084335f..d21f0a221c 100644
--- a/code/modules/mob/living/silicon/ai/death.dm
+++ b/code/modules/mob/living/silicon/ai/death.dm
@@ -23,7 +23,7 @@
if(explosive)
spawn(10)
- explosion(src.loc, 3, 6, 12, 15)
+ explosion(src.loc, 3, 6, 12, 15)
for(var/obj/machinery/ai_status_display/O in world) //change status
if(src.key)
diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/dragon.dm.rej b/code/modules/mob/living/simple_animal/hostile/megafauna/dragon.dm.rej
new file mode 100644
index 0000000000..e3e4d69862
--- /dev/null
+++ b/code/modules/mob/living/simple_animal/hostile/megafauna/dragon.dm.rej
@@ -0,0 +1,10 @@
+diff a/code/modules/mob/living/simple_animal/hostile/megafauna/dragon.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/dragon.dm (rejected hunks)
+@@ -130,7 +130,7 @@ Difficulty: Medium
+ playsound(T,'sound/magic/Fireball.ogg', 200, 1)
+ new /obj/effect/overlay/temp/fireball(T)
+ sleep(12)
+- SSexplosion.Create(T, 0, 0, 1, 0, 0, 0, 1)
++ explosion(T, 0, 0, 1, 0, 0, 0, 1)
+
+ /mob/living/simple_animal/hostile/megafauna/dragon/OpenFire()
+ anger_modifier = Clamp(((maxHealth - health)/50),0,20)
diff --git a/code/modules/power/antimatter/containment_jar.dm b/code/modules/power/antimatter/containment_jar.dm
index 0d2be23114..73fd2af580 100644
--- a/code/modules/power/antimatter/containment_jar.dm
+++ b/code/modules/power/antimatter/containment_jar.dm
@@ -18,12 +18,12 @@
/obj/item/weapon/am_containment/ex_act(severity, target)
switch(severity)
if(1)
- explosion(get_turf(src), 1, 2, 3, 5)//Should likely be larger but this works fine for now I guess
+ explosion(get_turf(src), 1, 2, 3, 5)//Should likely be larger but this works fine for now I guess
if(src)
qdel(src)
if(2)
if(prob((fuel/10)-stability))
- explosion(get_turf(src), 1, 2, 3, 5)
+ explosion(get_turf(src), 1, 2, 3, 5)
if(src)
qdel(src)
return
diff --git a/code/modules/power/antimatter/control.dm b/code/modules/power/antimatter/control.dm
index 647f838311..a1e448d282 100644
--- a/code/modules/power/antimatter/control.dm
+++ b/code/modules/power/antimatter/control.dm
@@ -46,7 +46,7 @@
/obj/machinery/power/am_control_unit/process()
if(exploding)
- explosion(get_turf(src),8,12,18,12)
+ explosion(get_turf(src),8,12,18,12)
if(src)
qdel(src)
diff --git a/code/modules/power/cell.dm b/code/modules/power/cell.dm
index 64d4077a12..a4b994e0cf 100644
--- a/code/modules/power/cell.dm
+++ b/code/modules/power/cell.dm
@@ -123,7 +123,7 @@
corrupt()
return
//explosion(T, 0, 1, 2, 2)
- explosion(T, devastation_range, heavy_impact_range, light_impact_range, flash_range)
+ explosion(T, devastation_range, heavy_impact_range, light_impact_range, flash_range)
qdel(src)
/obj/item/weapon/stock_parts/cell/proc/corrupt()
diff --git a/code/modules/power/port_gen.dm b/code/modules/power/port_gen.dm
index c02c13caf1..f1f251338e 100644
--- a/code/modules/power/port_gen.dm
+++ b/code/modules/power/port_gen.dm
@@ -167,7 +167,7 @@
src.updateDialog()
/obj/machinery/power/port_gen/pacman/proc/overheat()
- explosion(src.loc, 2, 5, 2, -1)
+ explosion(src.loc, 2, 5, 2, -1)
/obj/machinery/power/port_gen/pacman/attackby(obj/item/O, mob/user, params)
if(istype(O, sheet_path))
@@ -294,7 +294,7 @@
board_path = /obj/item/weapon/circuitboard/machine/pacman/super
/obj/machinery/power/port_gen/pacman/super/overheat()
- explosion(src.loc, 3, 3, 3, -1)
+ explosion(src.loc, 3, 3, 3, -1)
/obj/machinery/power/port_gen/pacman/mrs
name = "\improper M.R.S.P.A.C.M.A.N.-type portable generator"
@@ -305,4 +305,4 @@
board_path = /obj/item/weapon/circuitboard/machine/pacman/mrs
/obj/machinery/power/port_gen/pacman/mrs/overheat()
- explosion(src.loc, 4, 4, 4, -1)
+ explosion(src.loc, 4, 4, 4, -1)
diff --git a/code/modules/power/supermatter/supermatter.dm.rej b/code/modules/power/supermatter/supermatter.dm.rej
new file mode 100644
index 0000000000..498697c4d7
--- /dev/null
+++ b/code/modules/power/supermatter/supermatter.dm.rej
@@ -0,0 +1,10 @@
+diff a/code/modules/power/supermatter/supermatter.dm b/code/modules/power/supermatter/supermatter.dm (rejected hunks)
+@@ -174,7 +174,7 @@
+ S.consume(src)
+ else
+ investigate_log("has exploded.", "supermatter")
+- SSexplosion.Create(get_turf(T), explosion_power * max(gasmix_power_ratio, 0.205) * 0.5 , explosion_power * max(gasmix_power_ratio, 0.205) + 2, explosion_power * max(gasmix_power_ratio, 0.205) + 4 , explosion_power * max(gasmix_power_ratio, 0.205) + 6, 1, 1)
++ explosion(get_turf(T), explosion_power * max(gasmix_power_ratio, 0.205) * 0.5 , explosion_power * max(gasmix_power_ratio, 0.205) + 2, explosion_power * max(gasmix_power_ratio, 0.205) + 4 , explosion_power * max(gasmix_power_ratio, 0.205) + 6, 1, 1)
+ if(power > CRITICAL_POWER_PENALTY_THRESHOLD)
+ investigate_log("has spawned additional energy balls.", "supermatter")
+ var/obj/singularity/energy_ball/E = new(T)
diff --git a/code/modules/projectiles/guns/magic/wand.dm b/code/modules/projectiles/guns/magic/wand.dm
index fe93a58838..b9f6e3d1fe 100644
--- a/code/modules/projectiles/guns/magic/wand.dm
+++ b/code/modules/projectiles/guns/magic/wand.dm
@@ -166,5 +166,5 @@
/obj/item/weapon/gun/magic/wand/fireball/zap_self(mob/living/user)
..()
- explosion(user.loc, -1, 0, 2, 3, 0, flame_range = 2)
+ explosion(user.loc, -1, 0, 2, 3, 0, flame_range = 2)
charges--
\ No newline at end of file
diff --git a/code/modules/projectiles/projectile/bullets.dm b/code/modules/projectiles/projectile/bullets.dm
index 0594c17469..5ffebf33b3 100644
--- a/code/modules/projectiles/projectile/bullets.dm
+++ b/code/modules/projectiles/projectile/bullets.dm
@@ -61,10 +61,10 @@
/obj/item/projectile/bullet/pellet/overload/on_hit(atom/target, blocked = 0)
..()
- explosion(target, 0, 0, 2)
+ explosion(target, 0, 0, 2)
/obj/item/projectile/bullet/pellet/overload/on_range()
- explosion(src, 0, 0, 2)
+ explosion(src, 0, 0, 2)
do_sparks(3, TRUE, src)
..()
diff --git a/code/modules/reagents/reagent_dispenser.dm b/code/modules/reagents/reagent_dispenser.dm
index 1844dc53ab..be13516d3c 100644
--- a/code/modules/reagents/reagent_dispenser.dm
+++ b/code/modules/reagents/reagent_dispenser.dm
@@ -66,7 +66,7 @@
reagent_id = "welding_fuel"
/obj/structure/reagent_dispensers/fueltank/boom()
- explosion(get_turf(src), 0, 1, 5, flame_range = 5)
+ explosion(get_turf(src), 0, 1, 5, flame_range = 5)
qdel(src)
/obj/structure/reagent_dispensers/fueltank/blob_act(obj/structure/blob/B)
@@ -165,7 +165,7 @@
reagent_id = "beer"
/obj/structure/reagent_dispensers/beerkeg/blob_act(obj/structure/blob/B)
- explosion(src.loc,0,3,5,7,10)
+ explosion(src.loc,0,3,5,7,10)
if(!QDELETED(src))
qdel(src)
diff --git a/code/modules/research/experimentor.dm b/code/modules/research/experimentor.dm
index 8db05a0c4f..fa58de0964 100644
--- a/code/modules/research/experimentor.dm
+++ b/code/modules/research/experimentor.dm
@@ -683,7 +683,7 @@
spawn(rand(35,100))
if(src.loc == user)
visible_message("The [src]'s top opens, releasing a powerful blast!")
- explosion(user.loc, -1, rand(1,5), rand(1,5), rand(1,5), rand(1,5), flame_range = 2)
+ explosion(user.loc, -1, rand(1,5), rand(1,5), rand(1,5), rand(1,5), flame_range = 2)
warn_admins(user, "Explosion")
qdel(src) //Comment this line to produce a light grenade (the bomb that keeps on exploding when used)!!
diff --git a/code/modules/research/experimentor.dm.rej b/code/modules/research/experimentor.dm.rej
new file mode 100644
index 0000000000..9912e8e956
--- /dev/null
+++ b/code/modules/research/experimentor.dm.rej
@@ -0,0 +1,10 @@
+diff a/code/modules/research/experimentor.dm b/code/modules/research/experimentor.dm (rejected hunks)
+@@ -352,7 +352,7 @@
+ FB.fire()
+ else if(prob(EFFECT_PROB_LOW-badThingCoeff))
+ visible_message("[src] malfunctions, melting [exp_on] and releasing a burst of flame!")
+- SSexplosion.Create(src.loc, -1, 0, 0, 0, 0, flame_range = 2)
++ explosion(src.loc, -1, 0, 0, 0, 0, flame_range = 2)
+ investigate_log("Experimentor started a fire.", "experimentor")
+ ejectItem(TRUE)
+ else if(prob(EFFECT_PROB_MEDIUM-badThingCoeff))
diff --git a/code/modules/spells/spell_types/explosion.dm b/code/modules/spells/spell_types/explosion.dm
index a19a9582ba..443546d12f 100644
--- a/code/modules/spells/spell_types/explosion.dm
+++ b/code/modules/spells/spell_types/explosion.dm
@@ -10,6 +10,6 @@
/obj/effect/proc_holder/spell/targeted/explosion/cast(list/targets,mob/user = usr)
for(var/mob/living/target in targets)
- explosion(target.loc,ex_severe,ex_heavy,ex_light,ex_flash)
+ explosion(target.loc,ex_severe,ex_heavy,ex_light,ex_flash)
return
\ No newline at end of file
diff --git a/tgstation.dme b/tgstation.dme
index f61256862c..b4d861adfb 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -687,7 +687,6 @@
#include "code\game\mecha\working\working.dm"
#include "code\game\objects\buckling.dm"
#include "code\game\objects\empulse.dm"
-#include "code\game\objects\explosion.dm"
#include "code\game\objects\items.dm"
#include "code\game\objects\obj_defense.dm"
#include "code\game\objects\objs.dm"
diff --git a/tgstation.dme.rej b/tgstation.dme.rej
new file mode 100644
index 0000000000..1c776c32bd
--- /dev/null
+++ b/tgstation.dme.rej
@@ -0,0 +1,9 @@
+diff a/tgstation.dme b/tgstation.dme (rejected hunks)
+@@ -212,6 +211,7 @@
+ #include "code\datums\dna.dm"
+ #include "code\datums\dog_fashion.dm"
+ #include "code\datums\emotes.dm"
++#include "code\datums\explosion.dm"
+ #include "code\datums\forced_movement.dm"
+ #include "code\datums\hud.dm"
+ #include "code\datums\map_config.dm"