Merge pull request #1168 from Citadel-Station-13/upstream-merge-26361

[MIRROR]  Refactors explosions
This commit is contained in:
LetterJay
2017-05-30 15:15:51 -05:00
committed by GitHub
30 changed files with 780 additions and 166 deletions
+8 -7
View File
@@ -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
+9 -9
View File
@@ -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)
+510
View File
@@ -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
@@ -1,10 +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
GLOB.explosions += src
if(isnull(flame_range))
flame_range = light_impact_range
if(isnull(flash_range))
@@ -30,14 +68,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 +90,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 +118,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()
E = new
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
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 +175,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 +243,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"
+32 -33
View File
@@ -28,8 +28,8 @@
var/list/our_overlays //our local copy of (non-priority) overlays without byond magic. Use procs in SSoverlays to manipulate
var/list/priority_overlays //overlays that should remain on top and not normally removed when using cut_overlay functions, like c4.
var/datum/proximity_monitor/proximity_monitor
var/datum/proximity_monitor/proximity_monitor
/atom/New(loc, ...)
//atom creation method that preloads variables at creation
if(GLOB.use_preloader && (src.type == GLOB._preloader.target_path))//in case the instanciated atom is creating other atoms in New()
@@ -39,21 +39,21 @@
var/do_initialize = SSatoms.initialized
if(do_initialize > INITIALIZATION_INSSATOMS)
args[1] = do_initialize == INITIALIZATION_INNEW_MAPLOAD
if(SSatoms.InitAtom(src, args))
//we were deleted
return
var/list/created = SSatoms.created_atoms
if(created)
created += src
args[1] = do_initialize == INITIALIZATION_INNEW_MAPLOAD
if(SSatoms.InitAtom(src, args))
//we were deleted
return
var/list/created = SSatoms.created_atoms
if(created)
created += src
//Called after New if the map is being loaded. mapload = TRUE
//Called from base of New if the map is being loaded. mapload = FALSE
//This base must be called or derivatives must set initialized to TRUE
//must not sleep
//This base must be called or derivatives must set initialized to TRUE
//must not sleep
//Other parameters are passed from New (excluding loc), this does not happen if mapload is TRUE
//Must return an Initialize hint. Defined in __DEFINES/subsystems.dm
//Must return an Initialize hint. Defined in __DEFINES/subsystems.dm
//Note: the following functions don't call the base for optimization and must copypasta:
// /turf/Initialize
@@ -76,23 +76,23 @@
if (opacity && isturf(loc))
var/turf/T = loc
T.has_opaque_atom = TRUE // No need to recalculate it in this case, it's guaranteed to be on afterwards anyways.
return INITIALIZE_HINT_NORMAL
return INITIALIZE_HINT_NORMAL
//called if Initialize returns INITIALIZE_HINT_LATELOAD
//This version shouldn't be called
/atom/proc/LateInitialize()
var/static/list/warned_types = list()
if(!warned_types[type])
WARNING("Old style LateInitialize behaviour detected in [type]!")
warned_types[type] = TRUE
Initialize(FALSE)
//called if Initialize returns INITIALIZE_HINT_LATELOAD
//This version shouldn't be called
/atom/proc/LateInitialize()
var/static/list/warned_types = list()
if(!warned_types[type])
WARNING("Old style LateInitialize behaviour detected in [type]!")
warned_types[type] = TRUE
Initialize(FALSE)
/atom/Destroy()
if(alternate_appearances)
for(var/K in alternate_appearances)
var/datum/atom_hud/alternate_appearance/AA = alternate_appearances[K]
AA.remove_from_hud(src)
for(var/K in alternate_appearances)
var/datum/atom_hud/alternate_appearance/AA = alternate_appearances[K]
AA.remove_from_hud(src)
if(reagents)
qdel(reagents)
@@ -100,8 +100,8 @@
LAZYCLEARLIST(priority_overlays)
//SSoverlays.processing -= src //we COULD do this, but it's better to just let it fall out of the processing queue
QDEL_NULL(light)
QDEL_NULL(light)
return ..()
/atom/proc/handle_ricochet(obj/item/projectile/P)
@@ -277,7 +277,6 @@
return
/atom/proc/ex_act(severity, target)
set waitfor = FALSE
contents_explosion(severity, target)
/atom/proc/blob_act(obj/structure/blob/B)
@@ -509,10 +508,10 @@ GLOBAL_LIST_EMPTY(blood_splatter_icons)
/atom/proc/mech_melee_attack(obj/mecha/M)
return
//If a mob logouts/logins in side of an object you can use this proc
/atom/proc/on_log(login)
if(loc)
loc.on_log(login)
//If a mob logouts/logins in side of an object you can use this proc
/atom/proc/on_log(login)
if(loc)
loc.on_log(login)
/*
+4 -4
View File
@@ -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.\n<span class='notice'>Alt-click to rotate it clockwise.</span>"
@@ -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()
+1 -1
View File
@@ -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)
+1 -1
View File
@@ -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
+2 -2
View File
@@ -897,9 +897,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
+1 -1
View File
@@ -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)
@@ -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)
@@ -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
@@ -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
@@ -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
@@ -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()
@@ -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///////////
+1 -1
View File
@@ -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()
+1 -1
View File
@@ -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)
+3 -3
View File
@@ -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()
+1 -1
View File
@@ -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)
@@ -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
+1 -1
View File
@@ -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)
+1 -1
View File
@@ -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()
+3 -3
View File
@@ -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)
+1 -1
View File
@@ -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--
@@ -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)
..()
+2 -2
View File
@@ -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)
+1 -1
View File
@@ -683,7 +683,7 @@
spawn(rand(35,100))
if(src.loc == user)
visible_message("<span class='notice'>The [src]'s top opens, releasing a powerful blast!</span>")
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)!!
+1 -1
View File
@@ -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
+1 -1
View File
@@ -245,6 +245,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\holocall.dm"
#include "code\datums\hud.dm"
@@ -686,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"