mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2025-12-10 17:52:36 +00:00
* Adds explosion SFX to the blastcannon and explosive compressor - Extracts the explosion SFX and screenshake proc from the SSexplosions explosion handling proc and lets the explosive compressor and blastcannon use it. * Miscellaneous changes - Adds defines for the internal explosion arglist keys - Reverses the values of the explosion severity defines - Changes almost everything that uses `/proc/explosion` to use named arguments - Removes a whole bunch of argname = 0 in explosion calls. * Removes named callback arguments. * Changes the explosion signals to just use the arguments list Adds a simple framework to let objects respond to explosions occurring inside of them. Changes a whole bunch of explosions to use the object being exploded as the origin of the explosion rather than the turf the object is on. Makes the explosive compressor and blastcannon actually use the TTVs they are given. Adds support for things responding to internal explosions. Less snowflake code for the explosive compressor and blastcannon calculating bomb range.* Less confusing explosion severity defines. Less opaque explosion arguments *does not guarantee that the solution to letting them actually use the TTV is any less snowflake.
676 lines
25 KiB
Plaintext
676 lines
25 KiB
Plaintext
#define EXPLOSION_THROW_SPEED 4
|
|
GLOBAL_LIST_EMPTY(explosions)
|
|
|
|
SUBSYSTEM_DEF(explosions)
|
|
name = "Explosions"
|
|
init_order = INIT_ORDER_EXPLOSIONS
|
|
priority = FIRE_PRIORITY_EXPLOSIONS
|
|
wait = 1
|
|
flags = SS_TICKER|SS_NO_INIT
|
|
runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME
|
|
|
|
var/cost_lowturf = 0
|
|
var/cost_medturf = 0
|
|
var/cost_highturf = 0
|
|
var/cost_flameturf = 0
|
|
|
|
var/cost_throwturf = 0
|
|
|
|
var/cost_low_mov_atom = 0
|
|
var/cost_med_mov_atom = 0
|
|
var/cost_high_mov_atom = 0
|
|
|
|
var/list/lowturf = list()
|
|
var/list/medturf = list()
|
|
var/list/highturf = list()
|
|
var/list/flameturf = list()
|
|
|
|
var/list/throwturf = list()
|
|
|
|
var/list/low_mov_atom = list()
|
|
var/list/med_mov_atom = list()
|
|
var/list/high_mov_atom = list()
|
|
|
|
var/list/explosions = list()
|
|
|
|
var/currentpart = SSAIR_PIPENETS
|
|
|
|
|
|
/datum/controller/subsystem/explosions/stat_entry(msg)
|
|
msg += "C:{"
|
|
msg += "LT:[round(cost_lowturf,1)]|"
|
|
msg += "MT:[round(cost_medturf,1)]|"
|
|
msg += "HT:[round(cost_highturf,1)]|"
|
|
msg += "FT:[round(cost_flameturf,1)]||"
|
|
|
|
msg += "LO:[round(cost_low_mov_atom,1)]|"
|
|
msg += "MO:[round(cost_med_mov_atom,1)]|"
|
|
msg += "HO:[round(cost_high_mov_atom,1)]|"
|
|
|
|
msg += "TO:[round(cost_throwturf,1)]"
|
|
|
|
msg += "} "
|
|
|
|
msg += "AMT:{"
|
|
msg += "LT:[lowturf.len]|"
|
|
msg += "MT:[medturf.len]|"
|
|
msg += "HT:[highturf.len]|"
|
|
msg += "FT:[flameturf.len]||"
|
|
|
|
msg += "LO:[low_mov_atom.len]|"
|
|
msg += "MO:[med_mov_atom.len]|"
|
|
msg += "HO:[high_mov_atom.len]|"
|
|
|
|
msg += "TO:[throwturf.len]"
|
|
|
|
msg += "} "
|
|
return ..()
|
|
|
|
|
|
#define SSEX_TURF "turf"
|
|
#define SSEX_OBJ "obj"
|
|
|
|
/datum/controller/subsystem/explosions/proc/is_exploding()
|
|
return (lowturf.len || medturf.len || highturf.len || flameturf.len || throwturf.len || low_mov_atom.len || med_mov_atom.len || high_mov_atom.len)
|
|
|
|
/datum/controller/subsystem/explosions/proc/wipe_turf(turf/T)
|
|
lowturf -= T
|
|
medturf -= T
|
|
highturf -= T
|
|
flameturf -= T
|
|
throwturf -= T
|
|
|
|
/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/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("Devastation range (Tiles):") as num
|
|
heavy = input("Heavy impact range (Tiles):") as num
|
|
light = input("Light impact range (Tiles):") as num
|
|
|
|
var/max_range = max(dev, heavy, light)
|
|
var/x0 = epicenter.x
|
|
var/y0 = epicenter.y
|
|
var/list/wipe_colours = list()
|
|
for(var/turf/T in spiral_range_turfs(max_range, epicenter))
|
|
wipe_colours += T
|
|
var/dist = cheap_hypotenuse(T.x, T.y, x0, y0)
|
|
|
|
if(newmode == "Yes")
|
|
var/turf/TT = T
|
|
while(TT != epicenter)
|
|
TT = get_step_towards(TT,epicenter)
|
|
if(TT.density)
|
|
dist += TT.explosion_block
|
|
|
|
for(var/obj/O in T)
|
|
var/the_block = O.explosion_block
|
|
dist += the_block == EXPLOSION_BLOCK_PROC ? O.GetExplosionBlock() : the_block
|
|
|
|
if(dist < dev)
|
|
T.color = "red"
|
|
T.maptext = MAPTEXT("Dev")
|
|
else if (dist < heavy)
|
|
T.color = "yellow"
|
|
T.maptext = MAPTEXT("Heavy")
|
|
else if (dist < light)
|
|
T.color = "blue"
|
|
T.maptext = MAPTEXT("Light")
|
|
else
|
|
continue
|
|
|
|
addtimer(CALLBACK(GLOBAL_PROC, .proc/wipe_color_and_text, wipe_colours), 100)
|
|
|
|
/proc/wipe_color_and_text(list/atom/wiping)
|
|
for(var/i in wiping)
|
|
var/atom/A = i
|
|
A.color = null
|
|
A.maptext = ""
|
|
|
|
/proc/dyn_explosion(turf/epicenter, power, flame_range = 0, flash_range = null, adminlog = TRUE, ignorecap = TRUE, silent = FALSE, smoke = TRUE)
|
|
if(!power)
|
|
return
|
|
var/range = 0
|
|
range = round((2 * power)**GLOB.DYN_EX_SCALE)
|
|
explosion(epicenter, devastation_range = round(range * 0.25), heavy_impact_range = round(range * 0.5), light_impact_range = round(range), flame_range = flame_range*range, flash_range = flash_range*range, adminlog = adminlog, ignorecap = ignorecap, silent = silent, smoke = 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.
|
|
|
|
/**
|
|
* Makes a given atom explode.
|
|
*
|
|
* Arguments:
|
|
* - [origin][/atom]: The atom that's exploding.
|
|
* - devastation_range: The range at which the effects of the explosion are at their strongest.
|
|
* - heavy_impact_range: The range at which the effects of the explosion are relatively severe.
|
|
* - light_impact_range: The range at which the effects of the explosion are relatively weak.
|
|
* - flash_range: The range at which the explosion flashes people.
|
|
* - adminlog: Whether to log the explosion/report it to the administration.
|
|
* - ignorecap: Whether to ignore the relevant bombcap. Defaults to FALSE.
|
|
* - flame_range: The range at which the explosion should produce hotspots.
|
|
* - silent: Whether to generate/execute sound effects.
|
|
* - smoke: Whether to generate a smoke cloud provided the explosion is powerful enough to warrant it.
|
|
*/
|
|
/proc/explosion(atom/origin, devastation_range = 0, heavy_impact_range = 0, light_impact_range = 0, flame_range = 0, flash_range = 0, adminlog = TRUE, ignorecap = FALSE, silent = FALSE, smoke = FALSE)
|
|
. = SSexplosions.explode(arglist(args))
|
|
|
|
|
|
/**
|
|
* Makes a given atom explode. Now on the explosions subsystem!
|
|
*
|
|
* Arguments:
|
|
* - [origin][/atom]: The atom that's exploding.
|
|
* - devastation_range: The range at which the effects of the explosion are at their strongest.
|
|
* - heavy_impact_range: The range at which the effects of the explosion are relatively severe.
|
|
* - light_impact_range: The range at which the effects of the explosion are relatively weak.
|
|
* - flash_range: The range at which the explosion flashes people.
|
|
* - adminlog: Whether to log the explosion/report it to the administration.
|
|
* - ignorecap: Whether to ignore the relevant bombcap. Defaults to FALSE.
|
|
* - flame_range: The range at which the explosion should produce hotspots.
|
|
* - silent: Whether to generate/execute sound effects.
|
|
* - smoke: Whether to generate a smoke cloud provided the explosion is powerful enough to warrant it.
|
|
*/
|
|
/datum/controller/subsystem/explosions/proc/explode(atom/origin, devastation_range = 0, heavy_impact_range = 0, light_impact_range = 0, flame_range = 0, flash_range = 0, adminlog = TRUE, ignorecap = FALSE, silent = FALSE, smoke = FALSE)
|
|
var/list/arguments = list(EXARG_KEY_ORIGIN = origin, EXARG_KEY_DEV_RANGE = devastation_range, EXARG_KEY_HEAVY_RANGE = heavy_impact_range, EXARG_KEY_LIGHT_RANGE = light_impact_range, EXARG_KEY_FLAME_RANGE = flame_range, EXARG_KEY_FLASH_RANGE = flash_range, EXARG_KEY_ADMIN_LOG = adminlog, EXARG_KEY_IGNORE_CAP = ignorecap, EXARG_KEY_SILENT = silent, EXARG_KEY_SMOKE = smoke)
|
|
var/atom/location = isturf(origin) ? origin : origin.loc
|
|
if(SEND_SIGNAL(origin, COMSIG_ATOM_EXPLODE, arguments) & COMSIG_CANCEL_EXPLOSION)
|
|
return // Signals are incompatible with `arglist(...)` so we can't actually use that for these. Additionally,
|
|
|
|
while(location)
|
|
var/next_loc = location.loc
|
|
if(SEND_SIGNAL(location, COMSIG_ATOM_INTERNAL_EXPLOSION, arguments) & COMSIG_CANCEL_EXPLOSION)
|
|
return
|
|
if(isturf(location))
|
|
break
|
|
location = next_loc
|
|
|
|
if(!location)
|
|
return
|
|
|
|
var/area/epicenter_area = get_area(location)
|
|
if(SEND_SIGNAL(epicenter_area, COMSIG_AREA_INTERNAL_EXPLOSION, arguments) & COMSIG_CANCEL_EXPLOSION)
|
|
return
|
|
|
|
arguments -= EXARG_KEY_ORIGIN
|
|
propagate_blastwave(arglist(list(location) + arguments))
|
|
|
|
|
|
/**
|
|
* Handles the effects of an explosion originating from a given point.
|
|
*
|
|
* Primarily handles popagating the balstwave of the explosion to the relevant turfs.
|
|
* Also handles the fireball from the explosion.
|
|
* Also handles the smoke cloud from the explosion.
|
|
* Also handles sfx and screenshake.
|
|
*
|
|
* Arguments:
|
|
* - [epicenter][/atom]: The location of the explosion rounded to the nearest turf.
|
|
* - devastation_range: The range at which the effects of the explosion are at their strongest.
|
|
* - heavy_impact_range: The range at which the effects of the explosion are relatively severe.
|
|
* - light_impact_range: The range at which the effects of the explosion are relatively weak.
|
|
* - flash_range: The range at which the explosion flashes people.
|
|
* - adminlog: Whether to log the explosion/report it to the administration.
|
|
* - ignorecap: Whether to ignore the relevant bombcap. Defaults to TRUE for some mysterious reason.
|
|
* - flame_range: The range at which the explosion should produce hotspots.
|
|
* - silent: Whether to generate/execute sound effects.
|
|
* - smoke: Whether to generate a smoke cloud provided the explosion is powerful enough to warrant it.
|
|
*/
|
|
/datum/controller/subsystem/explosions/proc/propagate_blastwave(atom/epicenter, devastation_range, heavy_impact_range, light_impact_range, flame_range, flash_range, adminlog, ignorecap, silent, smoke)
|
|
epicenter = get_turf(epicenter)
|
|
if(!epicenter)
|
|
return
|
|
|
|
if(isnull(flame_range))
|
|
flame_range = light_impact_range
|
|
if(isnull(flash_range))
|
|
flash_range = devastation_range
|
|
|
|
// Archive the uncapped explosion for the doppler array
|
|
var/orig_dev_range = devastation_range
|
|
var/orig_heavy_range = heavy_impact_range
|
|
var/orig_light_range = light_impact_range
|
|
|
|
var/orig_max_distance = max(devastation_range, heavy_impact_range, light_impact_range, flame_range, flash_range)
|
|
|
|
//Zlevel specific bomb cap multiplier
|
|
var/cap_multiplier = SSmapping.level_trait(epicenter.z, ZTRAIT_BOMBCAP_MULTIPLIER)
|
|
if (isnull(cap_multiplier))
|
|
cap_multiplier = 1
|
|
|
|
if(!ignorecap)
|
|
devastation_range = min(GLOB.MAX_EX_DEVESTATION_RANGE * cap_multiplier, devastation_range)
|
|
heavy_impact_range = min(GLOB.MAX_EX_HEAVY_RANGE * cap_multiplier, heavy_impact_range)
|
|
light_impact_range = min(GLOB.MAX_EX_LIGHT_RANGE * cap_multiplier, light_impact_range)
|
|
flash_range = min(GLOB.MAX_EX_FLASH_RANGE * cap_multiplier, flash_range)
|
|
flame_range = min(GLOB.MAX_EX_FLAME_RANGE * cap_multiplier, flame_range)
|
|
|
|
var/max_range = max(devastation_range, heavy_impact_range, light_impact_range, flame_range)
|
|
var/started_at = REALTIMEOFDAY
|
|
if(adminlog)
|
|
message_admins("Explosion with size ([devastation_range], [heavy_impact_range], [light_impact_range], [flame_range]) in [ADMIN_VERBOSEJMP(epicenter)]")
|
|
log_game("Explosion with size ([devastation_range], [heavy_impact_range], [light_impact_range], [flame_range]) in [loc_name(epicenter)]")
|
|
|
|
var/x0 = epicenter.x
|
|
var/y0 = epicenter.y
|
|
var/z0 = epicenter.z
|
|
var/area/areatype = get_area(epicenter)
|
|
SSblackbox.record_feedback("associative", "explosion", 1, list("dev" = devastation_range, "heavy" = heavy_impact_range, "light" = light_impact_range, "flame" = flame_range, "flash" = flash_range, "orig_dev" = orig_dev_range, "orig_heavy" = orig_heavy_range, "orig_light" = orig_light_range, "x" = x0, "y" = y0, "z" = z0, "area" = areatype.type, "time" = time_stamp("YYYY-MM-DD hh:mm:ss", 1)))
|
|
|
|
// 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
|
|
|
|
var/far_dist = 0
|
|
far_dist += heavy_impact_range * 15
|
|
far_dist += devastation_range * 20
|
|
|
|
if(!silent)
|
|
shake_the_room(epicenter, orig_max_distance, far_dist, devastation_range, heavy_impact_range)
|
|
|
|
if(heavy_impact_range > 1)
|
|
var/datum/effect_system/explosion/E
|
|
if(smoke)
|
|
E = new /datum/effect_system/explosion/smoke
|
|
else
|
|
E = new
|
|
E.set_up(epicenter)
|
|
E.start()
|
|
|
|
//flash mobs
|
|
if(flash_range)
|
|
for(var/mob/living/L in viewers(flash_range, epicenter))
|
|
L.flash_act()
|
|
|
|
var/list/affected_turfs = GatherSpiralTurfs(max_range, epicenter)
|
|
|
|
var/reactionary = CONFIG_GET(flag/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
|
|
|
|
for(var/TI in affected_turfs)
|
|
var/turf/T = TI
|
|
var/init_dist = cheap_hypotenuse(T.x, T.y, x0, y0)
|
|
var/dist = init_dist
|
|
|
|
if(reactionary)
|
|
var/turf/Trajectory = T
|
|
while(Trajectory != epicenter)
|
|
Trajectory = get_step_towards(Trajectory, epicenter)
|
|
dist += cached_exp_block[Trajectory]
|
|
|
|
var/flame_dist = dist < flame_range
|
|
var/throw_dist = dist
|
|
|
|
if(dist < devastation_range)
|
|
dist = EXPLODE_DEVASTATE
|
|
else if(dist < heavy_impact_range)
|
|
dist = EXPLODE_HEAVY
|
|
else if(dist < light_impact_range)
|
|
dist = EXPLODE_LIGHT
|
|
else
|
|
dist = EXPLODE_NONE
|
|
|
|
if(T == epicenter) // Ensures explosives detonating from bags trigger other explosives in that bag
|
|
var/list/items = list()
|
|
for(var/I in T)
|
|
var/atom/A = I
|
|
if (length(A.contents) && !(A.flags_1 & PREVENT_CONTENTS_EXPLOSION_1)) //The atom/contents_explosion() proc returns null if the contents ex_acting has been handled by the atom, and TRUE if it hasn't.
|
|
items += A.GetAllContents()
|
|
for(var/thing in items)
|
|
var/atom/movable/movable_thing = thing
|
|
if(QDELETED(movable_thing))
|
|
continue
|
|
switch(dist)
|
|
if(EXPLODE_DEVASTATE)
|
|
SSexplosions.high_mov_atom += movable_thing
|
|
if(EXPLODE_HEAVY)
|
|
SSexplosions.med_mov_atom += movable_thing
|
|
if(EXPLODE_LIGHT)
|
|
SSexplosions.low_mov_atom += movable_thing
|
|
switch(dist)
|
|
if(EXPLODE_DEVASTATE)
|
|
SSexplosions.highturf += T
|
|
if(EXPLODE_HEAVY)
|
|
SSexplosions.medturf += T
|
|
if(EXPLODE_LIGHT)
|
|
SSexplosions.lowturf += T
|
|
|
|
|
|
if(flame_dist && prob(40) && !isspaceturf(T) && !T.density)
|
|
flameturf += T
|
|
|
|
//--- THROW ITEMS AROUND ---
|
|
var/throw_dir = get_dir(epicenter,T)
|
|
var/throw_range = max_range-throw_dist
|
|
var/list/throwingturf = T.explosion_throw_details
|
|
if (throwingturf)
|
|
if (throwingturf[1] < throw_range)
|
|
throwingturf[1] = throw_range
|
|
throwingturf[2] = throw_dir
|
|
throwingturf[3] = max_range
|
|
else
|
|
T.explosion_throw_details = list(throw_range, throw_dir, max_range)
|
|
throwturf += T
|
|
|
|
|
|
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.")
|
|
|
|
SEND_GLOBAL_SIGNAL(COMSIG_GLOB_EXPLOSION, epicenter, devastation_range, heavy_impact_range, light_impact_range, took, orig_dev_range, orig_heavy_range, orig_light_range)
|
|
|
|
// Explosion SFX defines...
|
|
/// The probability that a quaking explosion will make the station creak per unit. Maths!
|
|
#define QUAKE_CREAK_PROB 30
|
|
/// The probability that an echoing explosion will make the station creak per unit.
|
|
#define ECHO_CREAK_PROB 5
|
|
/// Time taken for the hull to begin to creak after an explosion, if applicable.
|
|
#define CREAK_DELAY (5 SECONDS)
|
|
/// Lower limit for far explosion SFX volume.
|
|
#define FAR_LOWER 40
|
|
/// Upper limit for far explosion SFX volume.
|
|
#define FAR_UPPER 60
|
|
/// The probability that a distant explosion SFX will be a far explosion sound rather than an echo. (0-100)
|
|
#define FAR_SOUND_PROB 75
|
|
/// The upper limit on screenshake amplitude for nearby explosions.
|
|
#define NEAR_SHAKE_CAP 10
|
|
/// The upper limit on screenshake amplifude for distant explosions.
|
|
#define FAR_SHAKE_CAP 2.5
|
|
/// The duration of the screenshake for nearby explosions.
|
|
#define NEAR_SHAKE_DURATION (2.5 SECONDS)
|
|
/// The duration of the screenshake for distant explosions.
|
|
#define FAR_SHAKE_DURATION (1 SECONDS)
|
|
/// The lower limit for the randomly selected hull creaking frequency.
|
|
#define FREQ_LOWER 25
|
|
/// The upper limit for the randomly selected hull creaking frequency.
|
|
#define FREQ_UPPER 40
|
|
|
|
/**
|
|
* Handles the sfx and screenshake caused by an explosion.
|
|
*
|
|
* Arguments:
|
|
* - [epicenter][/turf]: The location of the explosion.
|
|
* - near_distance: How close to the explosion you need to be to get the full effect of the explosion.
|
|
* - far_distance: How close to the explosion you need to be to hear more than echos.
|
|
* - quake_factor: Main scaling factor for screenshake.
|
|
* - echo_factor: Whether to make the explosion echo off of very distant parts of the station.
|
|
* - creaking: Whether to make the station creak. Autoset if null.
|
|
* - [near_sound][/sound]: The sound that plays if you are close to the explosion.
|
|
* - [far_sound][/sound]: The sound that plays if you are far from the explosion.
|
|
* - [echo_sound][/sound]: The sound that plays as echos for the explosion.
|
|
* - [creaking_sound][/sound]: The sound that plays when the station creaks during the explosion.
|
|
* - [hull_creaking_sound][/sound]: The sound that plays when the station creaks after the explosion.
|
|
*/
|
|
/datum/controller/subsystem/explosions/proc/shake_the_room(turf/epicenter, near_distance, far_distance, quake_factor, echo_factor, creaking, sound/near_sound = sound(get_sfx("explosion")), sound/far_sound = sound('sound/effects/explosionfar.ogg'), sound/echo_sound = sound('sound/effects/explosion_distant.ogg'), sound/creaking_sound = sound(get_sfx("explosion_creaking")), hull_creaking_sound = sound(get_sfx("hull_creaking")))
|
|
var/frequency = get_rand_frequency()
|
|
var/blast_z = epicenter.z
|
|
if(isnull(creaking)) // Autoset creaking.
|
|
var/on_station = SSmapping.level_trait(epicenter.z, ZTRAIT_STATION)
|
|
if(on_station && prob((quake_factor * QUAKE_CREAK_PROB) + (echo_factor * ECHO_CREAK_PROB))) // Huge explosions are near guaranteed to make the station creak and whine, smaller ones might.
|
|
creaking = TRUE // prob over 100 always returns true
|
|
else
|
|
creaking = FALSE
|
|
|
|
for(var/mob/listener as anything in GLOB.player_list)
|
|
var/turf/listener_turf = get_turf(listener)
|
|
if(!listener_turf || listener_turf.z != blast_z)
|
|
continue
|
|
|
|
var/distance = get_dist(epicenter, listener_turf)
|
|
var/base_shake_amount
|
|
if(near_distance > distance)
|
|
base_shake_amount = sqrt((near_distance - distance) / 10)
|
|
|
|
if(distance <= round(near_distance + world.view - 2, 1)) // If you are close enough to see the effects of the explosion first-hand (ignoring walls)
|
|
listener.playsound_local(epicenter, null, 100, TRUE, frequency, S = near_sound)
|
|
if(base_shake_amount > 0)
|
|
shake_camera(listener, NEAR_SHAKE_DURATION, clamp(base_shake_amount, 0, NEAR_SHAKE_CAP))
|
|
|
|
else if(distance < far_distance) // You can hear a far explosion if you are outside the blast radius. Small explosions shouldn't be heard throughout the station.
|
|
var/far_volume = clamp(far_distance / 2, FAR_LOWER, FAR_UPPER)
|
|
if(creaking)
|
|
listener.playsound_local(epicenter, null, far_volume, TRUE, frequency, S = creaking_sound, distance_multiplier = 0)
|
|
else if(prob(FAR_SOUND_PROB)) // Sound variety during meteor storm/tesloose/other bad event
|
|
listener.playsound_local(epicenter, null, far_volume, TRUE, frequency, S = far_sound, distance_multiplier = 0)
|
|
else
|
|
listener.playsound_local(epicenter, null, far_volume, TRUE, frequency, S = echo_sound, distance_multiplier = 0)
|
|
|
|
if(base_shake_amount || quake_factor)
|
|
if(!base_shake_amount) // Devastating explosions rock the station and ground
|
|
base_shake_amount = quake_factor * 3
|
|
shake_camera(listener, FAR_SHAKE_DURATION, clamp(base_shake_amount / 4, 0, FAR_SHAKE_CAP))
|
|
|
|
else if(!isspaceturf(listener_turf) && echo_factor) // Big enough explosions echo through the hull.
|
|
var/echo_volume
|
|
if(quake_factor)
|
|
base_shake_amount = quake_factor
|
|
echo_volume = 60
|
|
shake_camera(listener, FAR_SHAKE_DURATION, clamp(base_shake_amount / 4, 0, FAR_SHAKE_CAP))
|
|
else
|
|
echo_volume = 40
|
|
listener.playsound_local(epicenter, null, echo_volume, TRUE, frequency, S = echo_sound, distance_multiplier = 0)
|
|
|
|
if(creaking) // 5 seconds after the bang, the station begins to creak
|
|
addtimer(CALLBACK(listener, /mob/proc/playsound_local, epicenter, null, rand(FREQ_LOWER, FREQ_UPPER), TRUE, frequency, null, null, FALSE, hull_creaking_sound, 0), CREAK_DELAY)
|
|
|
|
#undef CREAK_DELAY
|
|
#undef QUAKE_CREAK_PROB
|
|
#undef ECHO_CREAK_PROB
|
|
#undef FAR_UPPER
|
|
#undef FAR_LOWER
|
|
#undef FAR_SOUND_PROB
|
|
#undef NEAR_SHAKE_CAP
|
|
#undef FAR_SHAKE_CAP
|
|
#undef NEAR_SHAKE_DURATION
|
|
#undef FAR_SHAKE_DURATION
|
|
#undef FREQ_UPPER
|
|
#undef FREQ_LOWER
|
|
|
|
/datum/controller/subsystem/explosions/proc/GatherSpiralTurfs(range, turf/epicenter)
|
|
var/list/outlist = list()
|
|
var/center = epicenter
|
|
var/dist = range
|
|
if(!dist)
|
|
outlist += center
|
|
return outlist
|
|
|
|
var/turf/t_center = get_turf(center)
|
|
if(!t_center)
|
|
return outlist
|
|
|
|
var/list/L = outlist
|
|
var/turf/T
|
|
var/y
|
|
var/x
|
|
var/c_dist = 1
|
|
L += t_center
|
|
|
|
while( c_dist <= dist )
|
|
y = t_center.y + c_dist
|
|
x = t_center.x - c_dist + 1
|
|
for(x in x to t_center.x+c_dist)
|
|
T = locate(x,y,t_center.z)
|
|
if(T)
|
|
L += T
|
|
|
|
y = t_center.y + c_dist - 1
|
|
x = t_center.x + c_dist
|
|
for(y in t_center.y-c_dist to y)
|
|
T = locate(x,y,t_center.z)
|
|
if(T)
|
|
L += T
|
|
|
|
y = t_center.y - c_dist
|
|
x = t_center.x + c_dist - 1
|
|
for(x in t_center.x-c_dist to x)
|
|
T = locate(x,y,t_center.z)
|
|
if(T)
|
|
L += T
|
|
|
|
y = t_center.y - c_dist + 1
|
|
x = t_center.x - c_dist
|
|
for(y in y to t_center.y+c_dist)
|
|
T = locate(x,y,t_center.z)
|
|
if(T)
|
|
L += T
|
|
c_dist++
|
|
. = L
|
|
|
|
/datum/controller/subsystem/explosions/proc/CaculateExplosionBlock(list/affected_turfs)
|
|
. = list()
|
|
var/I
|
|
for(I in 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/O in T)
|
|
var/the_block = O.explosion_block
|
|
current_exp_block += the_block == EXPLOSION_BLOCK_PROC ? O.GetExplosionBlock() : the_block
|
|
|
|
.[T] = current_exp_block
|
|
|
|
/datum/controller/subsystem/explosions/fire(resumed = 0)
|
|
if (!is_exploding())
|
|
return
|
|
var/timer
|
|
Master.current_ticklimit = TICK_LIMIT_RUNNING //force using the entire tick if we need it.
|
|
|
|
if(currentpart == SSEXPLOSIONS_TURFS)
|
|
currentpart = SSEXPLOSIONS_MOVABLES
|
|
|
|
timer = TICK_USAGE_REAL
|
|
var/list/low_turf = lowturf
|
|
lowturf = list()
|
|
for(var/thing in low_turf)
|
|
var/turf/turf_thing = thing
|
|
turf_thing.ex_act(EXPLODE_LIGHT)
|
|
cost_lowturf = MC_AVERAGE(cost_lowturf, TICK_DELTA_TO_MS(TICK_USAGE_REAL - timer))
|
|
|
|
timer = TICK_USAGE_REAL
|
|
var/list/med_turf = medturf
|
|
medturf = list()
|
|
for(var/thing in med_turf)
|
|
var/turf/turf_thing = thing
|
|
turf_thing.ex_act(EXPLODE_HEAVY)
|
|
cost_medturf = MC_AVERAGE(cost_medturf, TICK_DELTA_TO_MS(TICK_USAGE_REAL - timer))
|
|
|
|
timer = TICK_USAGE_REAL
|
|
var/list/high_turf = highturf
|
|
highturf = list()
|
|
for(var/thing in high_turf)
|
|
var/turf/turf_thing = thing
|
|
turf_thing.ex_act(EXPLODE_DEVASTATE)
|
|
cost_highturf = MC_AVERAGE(cost_highturf, TICK_DELTA_TO_MS(TICK_USAGE_REAL - timer))
|
|
|
|
timer = TICK_USAGE_REAL
|
|
var/list/flame_turf = flameturf
|
|
flameturf = list()
|
|
for(var/thing in flame_turf)
|
|
if(thing)
|
|
var/turf/T = thing
|
|
new /obj/effect/hotspot(T) //Mostly for ambience!
|
|
cost_flameturf = MC_AVERAGE(cost_flameturf, TICK_DELTA_TO_MS(TICK_USAGE_REAL - timer))
|
|
|
|
if (low_turf.len || med_turf.len || high_turf.len)
|
|
Master.laggy_byond_map_update_incoming()
|
|
|
|
if(currentpart == SSEXPLOSIONS_MOVABLES)
|
|
currentpart = SSEXPLOSIONS_THROWS
|
|
|
|
timer = TICK_USAGE_REAL
|
|
var/list/local_high_mov_atom = high_mov_atom
|
|
high_mov_atom = list()
|
|
for(var/thing in local_high_mov_atom)
|
|
var/atom/movable/movable_thing = thing
|
|
if(QDELETED(movable_thing))
|
|
continue
|
|
movable_thing.ex_act(EXPLODE_DEVASTATE)
|
|
cost_high_mov_atom = MC_AVERAGE(cost_high_mov_atom, TICK_DELTA_TO_MS(TICK_USAGE_REAL - timer))
|
|
|
|
timer = TICK_USAGE_REAL
|
|
var/list/local_med_mov_atom = med_mov_atom
|
|
med_mov_atom = list()
|
|
for(var/thing in local_med_mov_atom)
|
|
var/atom/movable/movable_thing = thing
|
|
if(QDELETED(movable_thing))
|
|
continue
|
|
movable_thing.ex_act(EXPLODE_HEAVY)
|
|
cost_med_mov_atom = MC_AVERAGE(cost_med_mov_atom, TICK_DELTA_TO_MS(TICK_USAGE_REAL - timer))
|
|
|
|
timer = TICK_USAGE_REAL
|
|
var/list/local_low_mov_atom = low_mov_atom
|
|
low_mov_atom = list()
|
|
for(var/thing in local_low_mov_atom)
|
|
var/atom/movable/movable_thing = thing
|
|
if(QDELETED(movable_thing))
|
|
continue
|
|
movable_thing.ex_act(EXPLODE_LIGHT)
|
|
cost_low_mov_atom = MC_AVERAGE(cost_low_mov_atom, TICK_DELTA_TO_MS(TICK_USAGE_REAL - timer))
|
|
|
|
|
|
if (currentpart == SSEXPLOSIONS_THROWS)
|
|
currentpart = SSEXPLOSIONS_TURFS
|
|
timer = TICK_USAGE_REAL
|
|
var/list/throw_turf = throwturf
|
|
throwturf = list()
|
|
for (var/thing in throw_turf)
|
|
if (!thing)
|
|
continue
|
|
var/turf/T = thing
|
|
var/list/L = T.explosion_throw_details
|
|
T.explosion_throw_details = null
|
|
if (length(L) != 3)
|
|
continue
|
|
var/throw_range = L[1]
|
|
var/throw_dir = L[2]
|
|
var/max_range = L[3]
|
|
for(var/atom/movable/A in T)
|
|
if(!A.anchored && A.move_resist != INFINITY)
|
|
var/atom_throw_range = rand(throw_range, max_range)
|
|
var/turf/throw_at = get_ranged_target_turf(A, throw_dir, atom_throw_range)
|
|
A.throw_at(throw_at, atom_throw_range, EXPLOSION_THROW_SPEED, quickstart = FALSE)
|
|
cost_throwturf = MC_AVERAGE(cost_throwturf, TICK_DELTA_TO_MS(TICK_USAGE_REAL - timer))
|
|
|
|
currentpart = SSEXPLOSIONS_TURFS
|