mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-08-25 14:08:31 +01:00
[READY] Basically, Instant Explosions 2: The Search For More Money: Eternal: A Tail Of Two Kitties: 33 1/3 (#50594)
About The Pull Request Extools maptick stuff is in the game. Stolen from BeeStation/BeeStation-Hornet#1119, improves performance. Requires ex-tools on the server, though. Explosions have been refactored to do the actual exploding in a subsystem. Credit to goon. Here's some videos! Why It's Good For The Game Basically instant max-caps now. We can now give more of a tick over to the sending of map updates Changelog cl Goonstation Coders, Beestation, Extools devs refactor: Explosions have been heavily optimized. /cl
This commit is contained in:
@@ -35,6 +35,9 @@ GLOBAL_REAL(Master, /datum/controller/master) = new
|
||||
var/tickdrift = 0
|
||||
|
||||
var/sleep_delta = 1
|
||||
|
||||
///Only run ticker subsystems for the next n ticks.
|
||||
var/skip_ticks = 0
|
||||
|
||||
var/make_runtime = 0
|
||||
|
||||
@@ -335,7 +338,7 @@ GLOBAL_REAL(Master, /datum/controller/master) = new
|
||||
new/datum/controller/failsafe() // (re)Start the failsafe.
|
||||
|
||||
//now do the actual stuff
|
||||
if (!queue_head || !(iteration % 3))
|
||||
if (!skip_ticks)
|
||||
var/checking_runlevel = current_runlevel
|
||||
if(cached_runlevel != checking_runlevel)
|
||||
//resechedule subsystems
|
||||
@@ -381,6 +384,8 @@ GLOBAL_REAL(Master, /datum/controller/master) = new
|
||||
|
||||
iteration++
|
||||
last_run = world.time
|
||||
if (skip_ticks)
|
||||
skip_ticks--
|
||||
src.sleep_delta = MC_AVERAGE_FAST(src.sleep_delta, sleep_delta)
|
||||
current_ticklimit = TICK_LIMIT_RUNNING
|
||||
if (processing * sleep_delta <= world.tick_lag)
|
||||
@@ -444,10 +449,12 @@ GLOBAL_REAL(Master, /datum/controller/master) = new
|
||||
while (queue_node)
|
||||
if (ran && TICK_USAGE > TICK_LIMIT_RUNNING)
|
||||
break
|
||||
|
||||
queue_node_flags = queue_node.flags
|
||||
queue_node_priority = queue_node.queued_priority
|
||||
|
||||
|
||||
if (!(queue_node_flags & SS_TICKER) && skip_ticks)
|
||||
queue_node = queue_node.queue_next
|
||||
continue
|
||||
//super special case, subsystems where we can't make them pause mid way through
|
||||
//if we can't run them this tick (without going over a tick)
|
||||
//we bump up their priority and attempt to run them next tick
|
||||
@@ -584,14 +591,19 @@ GLOBAL_REAL(Master, /datum/controller/master) = new
|
||||
log_world("MC: SoftReset: Finished.")
|
||||
. = 1
|
||||
|
||||
|
||||
/// Warns us that the end of tick byond map_update will be laggier then normal, so that we can just skip running subsystems this tick.
|
||||
/datum/controller/master/proc/laggy_byond_map_update_incoming()
|
||||
if (!skip_ticks)
|
||||
skip_ticks = 1
|
||||
|
||||
|
||||
/datum/controller/master/stat_entry()
|
||||
if(!statclick)
|
||||
statclick = new/obj/effect/statclick/debug(null, "Initializing...", src)
|
||||
|
||||
stat("Byond:", "(FPS:[world.fps]) (TickCount:[world.time/world.tick_lag]) (TickDrift:[round(Master.tickdrift,1)]([round((Master.tickdrift/(world.time/world.tick_lag))*100,0.1)]%))")
|
||||
stat("Master Controller:", statclick.update("(TickRate:[Master.processing]) (Iteration:[Master.iteration])"))
|
||||
stat("Byond:", "(FPS:[world.fps]) (TickCount:[world.time/world.tick_lag]) (TickDrift:[round(Master.tickdrift,1)]([round((Master.tickdrift/(world.time/world.tick_lag))*100,0.1)]%)) (Internal Tick Usage: [round(MAPTICK_LAST_INTERNAL_TICK_USAGE,0.1)]%)")
|
||||
stat("Master Controller:", statclick.update("(TickRate:[Master.processing]) (Iteration:[Master.iteration]) (TickLimit: [round(Master.current_ticklimit, 0.1)])"))
|
||||
|
||||
|
||||
/datum/controller/master/StartLoadingMap()
|
||||
//disallow more than one map to load at once, multithreading it will just cause race conditions
|
||||
|
||||
@@ -1,88 +1,91 @@
|
||||
/**
|
||||
* # Subsystem base class
|
||||
*
|
||||
*
|
||||
* Defines a subsystem to be managed by the [Master Controller][/datum/controller/master]
|
||||
*
|
||||
* Simply define a child of this subsystem, using the [SUBSYSTEM_DEF] macro, and the MC will handle registration.
|
||||
* Simply define a child of this subsystem, using the [SUBSYSTEM_DEF] macro, and the MC will handle registration.
|
||||
* Changing the name is required
|
||||
**/
|
||||
|
||||
|
||||
/datum/controller/subsystem
|
||||
// Metadata; you should define these.
|
||||
|
||||
|
||||
/// Name of the subsystem - you must change this
|
||||
name = "fire coderbus"
|
||||
|
||||
name = "fire coderbus"
|
||||
|
||||
/// Order of initialization. Higher numbers are initialized first, lower numbers later. Use or create defines such as [INIT_ORDER_DEFAULT] so we can see the order in one file.
|
||||
var/init_order = INIT_ORDER_DEFAULT
|
||||
|
||||
var/init_order = INIT_ORDER_DEFAULT
|
||||
|
||||
/// Time to wait (in deciseconds) between each call to fire(). Must be a positive integer.
|
||||
var/wait = 20
|
||||
|
||||
var/wait = 20
|
||||
|
||||
/// Priority Weight: When mutiple subsystems need to run in the same tick, higher priority subsystems will be given a higher share of the tick before MC_TICK_CHECK triggers a sleep, higher priority subsystems also run before lower priority subsystems
|
||||
var/priority = FIRE_PRIORITY_DEFAULT
|
||||
|
||||
var/priority = FIRE_PRIORITY_DEFAULT
|
||||
|
||||
/// [Subsystem Flags][SS_NO_INIT] to control binary behavior. Flags must be set at compile time or before preinit finishes to take full effect. (You can also restart the mc to force them to process again)
|
||||
var/flags = 0
|
||||
|
||||
var/flags = 0
|
||||
|
||||
/// This var is set to TRUE after the subsystem has been initialized.
|
||||
var/initialized = FALSE
|
||||
|
||||
/// Set to 0 to prevent fire() calls, mostly for admin use or subsystems that may be resumed later
|
||||
/// use the [SS_NO_FIRE] flag instead for systems that never fire to keep it from even being added to list that is checked every tick
|
||||
var/can_fire = TRUE
|
||||
|
||||
|
||||
///Bitmap of what game states can this subsystem fire at. See [RUNLEVELS_DEFAULT] for more details.
|
||||
var/runlevels = RUNLEVELS_DEFAULT //points of the game at which the SS can fire
|
||||
|
||||
/*
|
||||
* The following variables are managed by the MC and should not be modified directly.
|
||||
*/
|
||||
|
||||
|
||||
/// Last world.time the subsystem completed a run (as in wasn't paused by [MC_TICK_CHECK])
|
||||
var/last_fire = 0
|
||||
|
||||
|
||||
/// Scheduled world.time for next fire()
|
||||
var/next_fire = 0
|
||||
|
||||
|
||||
/// Running average of the amount of milliseconds it takes the subsystem to complete a run (including all resumes but not the time spent paused)
|
||||
var/cost = 0
|
||||
|
||||
var/cost = 0
|
||||
|
||||
/// Running average of the amount of tick usage in percents of a tick it takes the subsystem to complete a run
|
||||
var/tick_usage = 0
|
||||
|
||||
var/tick_usage = 0
|
||||
|
||||
/// Running average of the amount of tick usage (in percents of a game tick) the subsystem has spent past its allocated time without pausing
|
||||
var/tick_overrun = 0
|
||||
|
||||
|
||||
/// Tracks the current execution state of the subsystem. Used to handle subsystems that sleep in fire so the mc doesn't run them again while they are sleeping
|
||||
var/state = SS_IDLE
|
||||
|
||||
|
||||
/// Tracks how many fires the subsystem has consecutively paused on in the current run
|
||||
var/paused_ticks = 0
|
||||
|
||||
|
||||
/// Tracks how much of a tick the subsystem has consumed in the current run
|
||||
var/paused_tick_usage
|
||||
|
||||
|
||||
/// Tracks how many fires the subsystem takes to complete a run on average.
|
||||
var/ticks = 1
|
||||
|
||||
|
||||
/// Tracks the amount of completed runs for the subsystem
|
||||
var/times_fired = 0
|
||||
|
||||
|
||||
/// Time the subsystem entered the queue, (for timing and priority reasons)
|
||||
var/queued_time = 0
|
||||
|
||||
var/queued_time = 0
|
||||
|
||||
/// Priority at the time the subsystem entered the queue. Needed to avoid changes in priority (by admins and the like) from breaking things.
|
||||
var/queued_priority
|
||||
|
||||
var/queued_priority
|
||||
|
||||
/// How many times we suspect a subsystem type has crashed the MC, 3 strikes and you're out!
|
||||
var/static/list/failure_strikes
|
||||
|
||||
var/static/list/failure_strikes
|
||||
|
||||
/// Next subsystem in the queue of subsystems to run this tick
|
||||
var/datum/controller/subsystem/queue_next
|
||||
/// Previous subsystem in the queue of subsystems to run this tick
|
||||
var/datum/controller/subsystem/queue_prev
|
||||
|
||||
//Do not blindly add vars here to the bottom, put it where it goes above
|
||||
//If your var only has two values, put it in as a flag.
|
||||
|
||||
|
||||
//Do not override
|
||||
///datum/controller/subsystem/New()
|
||||
|
||||
@@ -12,12 +12,14 @@ SUBSYSTEM_DEF(air)
|
||||
var/cost_hotspots = 0
|
||||
var/cost_superconductivity = 0
|
||||
var/cost_pipenets = 0
|
||||
var/cost_rebuilds = 0
|
||||
var/cost_atmos_machinery = 0
|
||||
|
||||
var/list/excited_groups = list()
|
||||
var/list/active_turfs = list()
|
||||
var/list/hotspots = list()
|
||||
var/list/networks = list()
|
||||
var/list/pipenets_needing_rebuilt = list()
|
||||
var/list/obj/machinery/atmos_machinery = list()
|
||||
var/list/pipe_init_dirs_cache = list()
|
||||
|
||||
@@ -31,7 +33,7 @@ SUBSYSTEM_DEF(air)
|
||||
|
||||
|
||||
var/list/currentrun = list()
|
||||
var/currentpart = SSAIR_PIPENETS
|
||||
var/currentpart = SSAIR_REBUILD_PIPENETS
|
||||
|
||||
var/map_loading = TRUE
|
||||
var/list/queued_for_activation
|
||||
@@ -44,6 +46,7 @@ SUBSYSTEM_DEF(air)
|
||||
msg += "HS:[round(cost_hotspots,1)]|"
|
||||
msg += "SC:[round(cost_superconductivity,1)]|"
|
||||
msg += "PN:[round(cost_pipenets,1)]|"
|
||||
msg += "RB:[round(cost_rebuilds,1)]|"
|
||||
msg += "AM:[round(cost_atmos_machinery,1)]"
|
||||
msg += "} "
|
||||
msg += "AT:[active_turfs.len]|"
|
||||
@@ -68,6 +71,18 @@ SUBSYSTEM_DEF(air)
|
||||
/datum/controller/subsystem/air/fire(resumed = 0)
|
||||
var/timer = TICK_USAGE_REAL
|
||||
|
||||
if(currentpart == SSAIR_REBUILD_PIPENETS)
|
||||
var/list/pipenet_rebuilds = pipenets_needing_rebuilt
|
||||
for(var/thing in pipenet_rebuilds)
|
||||
var/obj/machinery/atmospherics/AT = thing
|
||||
AT.build_network()
|
||||
cost_rebuilds = MC_AVERAGE(cost_rebuilds, TICK_DELTA_TO_MS(TICK_USAGE_REAL - timer))
|
||||
pipenets_needing_rebuilt.Cut()
|
||||
if(state != SS_RUNNING)
|
||||
return
|
||||
resumed = FALSE
|
||||
currentpart = SSAIR_PIPENETS
|
||||
|
||||
if(currentpart == SSAIR_PIPENETS || !resumed)
|
||||
process_pipenets(resumed)
|
||||
cost_pipenets = MC_AVERAGE(cost_pipenets, TICK_DELTA_TO_MS(TICK_USAGE_REAL - timer))
|
||||
@@ -128,7 +143,7 @@ SUBSYSTEM_DEF(air)
|
||||
if(state != SS_RUNNING)
|
||||
return
|
||||
resumed = 0
|
||||
currentpart = SSAIR_PIPENETS
|
||||
currentpart = SSAIR_REBUILD_PIPENETS
|
||||
|
||||
|
||||
|
||||
@@ -147,6 +162,9 @@ SUBSYSTEM_DEF(air)
|
||||
if(MC_TICK_CHECK)
|
||||
return
|
||||
|
||||
/datum/controller/subsystem/air/proc/add_to_rebuild_queue(atmos_machine)
|
||||
if(istype(atmos_machine, /obj/machinery/atmospherics))
|
||||
pipenets_needing_rebuilt += atmos_machine
|
||||
|
||||
/datum/controller/subsystem/air/proc/process_atmos_machinery(resumed = 0)
|
||||
var/seconds = wait * 0.1
|
||||
|
||||
@@ -0,0 +1,514 @@
|
||||
#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_lowobj = 0
|
||||
var/cost_medobj = 0
|
||||
var/cost_highobj = 0
|
||||
|
||||
|
||||
var/list/lowturf = list()
|
||||
var/list/medturf = list()
|
||||
var/list/highturf = list()
|
||||
var/list/flameturf = list()
|
||||
|
||||
var/list/throwturf = list()
|
||||
|
||||
var/list/lowobj = list()
|
||||
var/list/medobj = list()
|
||||
var/list/highobj = 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_lowobj,1)]|"
|
||||
msg += "MO:[round(cost_medobj,1)]|"
|
||||
msg += "HO:[round(cost_highobj,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:[lowobj.len]|"
|
||||
msg += "MO:[medobj.len]|"
|
||||
msg += "HO:[highobj.len]|"
|
||||
|
||||
msg += "TO:[throwturf.len]"
|
||||
|
||||
msg += "} "
|
||||
..(msg)
|
||||
|
||||
|
||||
#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 || lowobj.len || medobj.len || highobj.len)
|
||||
|
||||
|
||||
/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 = "Dev"
|
||||
else if (dist < heavy)
|
||||
T.color = "yellow"
|
||||
T.maptext = "Heavy"
|
||||
else if (dist < light)
|
||||
T.color = "blue"
|
||||
T.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, flash_range, adminlog = TRUE, ignorecap = TRUE, flame_range = 0, silent = FALSE, smoke = TRUE)
|
||||
if(!power)
|
||||
return
|
||||
var/range = 0
|
||||
range = round((2 * power)**GLOB.DYN_EX_SCALE)
|
||||
explosion(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.
|
||||
|
||||
/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)
|
||||
. = SSexplosions.explode(arglist(args))
|
||||
|
||||
/datum/controller/subsystem/explosions/proc/explode(atom/epicenter, devastation_range, heavy_impact_range, light_impact_range, flash_range, adminlog, ignorecap, flame_range, 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, flash_range, flame_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, "flash" = flash_range, "flame" = flame_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 * 5
|
||||
far_dist += devastation_range * 20
|
||||
|
||||
if(!silent)
|
||||
var/frequency = get_rand_frequency()
|
||||
var/sound/explosion_sound = sound(get_sfx("explosion"))
|
||||
var/sound/far_explosion_sound = sound('sound/effects/explosionfar.ogg')
|
||||
|
||||
for(var/MN in GLOB.player_list)
|
||||
var/mob/M = MN
|
||||
// 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)
|
||||
var/baseshakeamount
|
||||
if(orig_max_distance - dist > 0)
|
||||
baseshakeamount = sqrt((orig_max_distance - dist)*0.1)
|
||||
// If inside the blast radius + world.view - 2
|
||||
if(dist <= round(max_range + world.view - 2, 1))
|
||||
M.playsound_local(epicenter, null, 100, 1, frequency, falloff = 5, S = explosion_sound)
|
||||
if(baseshakeamount > 0)
|
||||
shake_camera(M, 25, clamp(baseshakeamount, 0, 10))
|
||||
// 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, null, far_volume, 1, frequency, falloff = 5, S = far_explosion_sound)
|
||||
if(baseshakeamount > 0)
|
||||
shake_camera(M, 10, clamp(baseshakeamount*0.25, 0, 2.5))
|
||||
|
||||
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/O in items)
|
||||
var/atom/A = O
|
||||
if(!QDELETED(A))
|
||||
switch(dist)
|
||||
if(EXPLODE_DEVASTATE)
|
||||
SSexplosions.highobj += A
|
||||
if(EXPLODE_HEAVY)
|
||||
SSexplosions.medobj += A
|
||||
if(EXPLODE_LIGHT)
|
||||
SSexplosions.lowobj += A
|
||||
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)
|
||||
|
||||
|
||||
/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)
|
||||
if(thing)
|
||||
var/turf/T = thing
|
||||
T.explosion_level = max(T.explosion_level, EXPLODE_LIGHT)
|
||||
T.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)
|
||||
if(thing)
|
||||
var/turf/T = thing
|
||||
T.explosion_level = max(T.explosion_level, EXPLODE_HEAVY)
|
||||
T.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)
|
||||
if(thing)
|
||||
var/turf/T = thing
|
||||
T.explosion_level = max(T.explosion_level, EXPLODE_DEVASTATE)
|
||||
T.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/high_obj = highobj
|
||||
highobj = list()
|
||||
for(var/thing in high_obj)
|
||||
if(thing)
|
||||
var/obj/O = thing
|
||||
O.ex_act(EXPLODE_DEVASTATE)
|
||||
cost_highobj = MC_AVERAGE(cost_highobj, TICK_DELTA_TO_MS(TICK_USAGE_REAL - timer))
|
||||
|
||||
timer = TICK_USAGE_REAL
|
||||
var/list/med_obj = medobj
|
||||
medobj = list()
|
||||
for(var/thing in med_obj)
|
||||
if(thing)
|
||||
var/obj/O = thing
|
||||
O.ex_act(EXPLODE_HEAVY)
|
||||
cost_medobj = MC_AVERAGE(cost_medobj, TICK_DELTA_TO_MS(TICK_USAGE_REAL - timer))
|
||||
|
||||
timer = TICK_USAGE_REAL
|
||||
var/list/low_obj = lowobj
|
||||
lowobj = list()
|
||||
for(var/thing in low_obj)
|
||||
if(thing)
|
||||
var/obj/O = thing
|
||||
O.ex_act(EXPLODE_LIGHT)
|
||||
cost_lowobj = MC_AVERAGE(cost_lowobj, 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
|
||||
@@ -26,7 +26,7 @@ SUBSYSTEM_DEF(input)
|
||||
"T" = "say",
|
||||
"M" = "me",
|
||||
"Back" = "\".winset \\\"input.text=\\\"\\\"\\\"\"",
|
||||
"Tab" = "\".winset \\\"input.focus=true?map.focus=true input.background-color=[COLOR_INPUT_DISABLED]:input.focus=true input.background-color=[COLOR_INPUT_ENABLED]\\\"\"",
|
||||
"Tab" = "\".winset \\\"input.focus=true?map.focus=true input.background-color=[COLOR_INPUT_DISABLED]:input.focus=true input.background-color=[COLOR_INPUT_ENABLED]\\\"\"",
|
||||
"Escape" = "\".winset \\\"input.text=\\\"\\\"\\\"\"")
|
||||
|
||||
// Badmins just wanna have fun ♪
|
||||
|
||||
@@ -35,7 +35,7 @@ SUBSYSTEM_DEF(nightshift)
|
||||
if(!emergency)
|
||||
announce("Restoring night lighting configuration to normal operation.")
|
||||
else
|
||||
announce("Disabling night lighting: Station is in a state of emergency.")
|
||||
announce("Disabling night lighting: Station is in a state of emergency.")
|
||||
if(emergency)
|
||||
night_time = FALSE
|
||||
if(nightshift_active != night_time)
|
||||
|
||||
@@ -339,10 +339,12 @@ SUBSYSTEM_DEF(ticker)
|
||||
|
||||
/datum/controller/subsystem/ticker/proc/station_explosion_detonation(atom/bomb)
|
||||
if(bomb) //BOOM
|
||||
var/turf/epi = bomb.loc
|
||||
qdel(bomb)
|
||||
if(epi)
|
||||
explosion(epi, 0, 256, 512, 0, TRUE, TRUE, 0, TRUE)
|
||||
for(var/T in GLOB.station_turfs)
|
||||
if(prob(33))
|
||||
SSexplosions.highturf += T
|
||||
else
|
||||
SSexplosions.medturf += T
|
||||
|
||||
/datum/controller/subsystem/ticker/proc/create_characters()
|
||||
for(var/i in GLOB.new_player_list)
|
||||
|
||||
Reference in New Issue
Block a user