diff --git a/code/__defines/misc.dm b/code/__defines/misc.dm index d83ea898f7a..e9603198906 100644 --- a/code/__defines/misc.dm +++ b/code/__defines/misc.dm @@ -457,3 +457,8 @@ Define for getting a bitfield of adjacent turfs that meet a condition. } #define DEVICE_NO_CELL "no_cell" + +#define FALLOFF_SOUNDS 0.5 +#define SOUND_Z_FACTOR 100 +// Maximum number of Zs away you can be from a sound before it stops being audible. +#define MAX_SOUND_Z_TRAVERSAL 2 diff --git a/code/__defines/subsystem-defines.dm b/code/__defines/subsystem-defines.dm index 3f7ade85b33..5b8e7af6baa 100644 --- a/code/__defines/subsystem-defines.dm +++ b/code/__defines/subsystem-defines.dm @@ -87,6 +87,8 @@ // Connection prefixes for player-editable fields #define WP_ELECTRONICS "elec_" +// -- SSatlas -- +#define ARE_Z_CONNECTED(ZA, ZB) ((ZA == ZB) || ((SSatlas.connected_z_cache.len >= ZA && SSatlas.connected_z_cache[ZA]) ? SSatlas.connected_z_cache[ZA][ZB] : AreConnectedZLevels(ZA, ZB))) // -- SSicon_cache -- #define LIGHT_FIXTURE_CACHE(icon,state,color) SSicon_cache.light_fixture_cache["[icon]_[state]_[color]"] || (SSicon_cache.light_fixture_cache["[icon]_[state]_[color]"] = SSicon_cache.generate_color_variant(icon,state,color)) diff --git a/code/controllers/configuration.dm b/code/controllers/configuration.dm index b9c202d09e6..b22300d1eab 100644 --- a/code/controllers/configuration.dm +++ b/code/controllers/configuration.dm @@ -167,7 +167,7 @@ var/list/gamemode_cache = list() var/simultaneous_pm_warning_timeout = 100 - var/use_recursive_explosions = 0 //Defines whether the server uses recursive or circular explosions. + var/use_spreading_explosions = 0 //Defines whether the server uses iterative or circular explosions. var/assistant_maint = 0 //Do assistants get maint access? var/gateway_delay = 18000 //How long the gateway takes before it activates. Default is half an hour. @@ -272,6 +272,9 @@ var/list/gamemode_cache = list() var/openturf_starlight_permitted = FALSE + var/iterative_explosives_z_threshold = 10 + var/iterative_explosives_z_multiplier = 0.75 + /datum/configuration/New() var/list/L = typesof(/datum/game_mode) - /datum/game_mode for (var/T in L) @@ -338,8 +341,8 @@ var/list/gamemode_cache = list() if ("jobs_have_minimal_access") config.jobs_have_minimal_access = 1 - if ("use_recursive_explosions") - use_recursive_explosions = 1 + if ("use_spreading_explosions") + use_spreading_explosions = 1 if ("log_ooc") config.log_ooc = 1 @@ -835,6 +838,12 @@ var/list/gamemode_cache = list() if("force_map") override_map = value + if ("explosion_z_threshold") + iterative_explosives_z_threshold = text2num(value) + + if ("explosion_z_mult") + iterative_explosives_z_multiplier = text2num(value) + if("show_game_type_odd") config.show_game_type_odd = 1 else diff --git a/code/controllers/subsystems/explosives.dm b/code/controllers/subsystems/explosives.dm index e87c1c4779e..3264c7741d7 100644 --- a/code/controllers/subsystems/explosives.dm +++ b/code/controllers/subsystems/explosives.dm @@ -1,16 +1,21 @@ +#define EXPLFX_BOTH 3 +#define EXPLFX_SOUND 2 +#define EXPLFX_SHAKE 1 +#define EXPLFX_NONE 0 + var/datum/controller/subsystem/explosives/SSexplosives // yes, let's move the laggiest part of the game to a process // nothing could go wrong -- Lohikar /datum/controller/subsystem/explosives name = "Explosives" - wait = 5 + wait = 1 flags = SS_NO_INIT | SS_BACKGROUND | SS_POST_FIRE_TIMING priority = SS_PRIORITY_EXPLOSIVES suspended = TRUE // Start disabled, explosions will wake us if need be. - var/list/work_queue + var/list/work_queue = list() var/ticks_without_work = 0 var/list/explosion_turfs var/explosion_in_progress @@ -20,7 +25,6 @@ var/datum/controller/subsystem/explosives/SSexplosives /datum/controller/subsystem/explosives/New() NEW_SS_GLOBAL(SSexplosives) - LAZYINITLIST(work_queue) /datum/controller/subsystem/explosives/Recover() work_queue = SSexplosives.work_queue @@ -53,8 +57,8 @@ var/datum/controller/subsystem/explosives/SSexplosives for (var/A in work_queue) var/datum/explosiondata/data = A - if (data.is_rec) - explosion_rec(data.epicenter, data.rec_pow) + if (data.spreading) + explosion_iter(data.epicenter, data.rec_pow, data.z_transfer) else explosion(data) @@ -71,8 +75,8 @@ var/datum/controller/subsystem/explosives/SSexplosives var/z_transfer = data.z_transfer var/power = data.rec_pow - if(data.is_rec) - explosion_rec(epicenter, power) + if(data.spreading) + explosion_iter(epicenter, power, z_transfer) return var/start = world.timeofday @@ -82,9 +86,9 @@ var/datum/controller/subsystem/explosives/SSexplosives // Handles recursive propagation of explosions. if(devastation_range > 2 || heavy_impact_range > 2) if(HasAbove(epicenter.z) && z_transfer & UP) - global.explosion(GetAbove(epicenter), max(0, devastation_range - 2), max(0, heavy_impact_range - 2), max(0, light_impact_range - 2), max(0, flash_range - 2), 0, UP, is_rec = FALSE) + global.explosion(GetAbove(epicenter), max(0, devastation_range - 2), max(0, heavy_impact_range - 2), max(0, light_impact_range - 2), max(0, flash_range - 2), 0, UP, spreading = FALSE) if(HasBelow(epicenter.z) && z_transfer & DOWN) - global.explosion(GetAbove(epicenter), max(0, devastation_range - 2), max(0, heavy_impact_range - 2), max(0, light_impact_range - 2), max(0, flash_range - 2), 0, DOWN, is_rec = FALSE) + global.explosion(GetAbove(epicenter), max(0, devastation_range - 2), max(0, heavy_impact_range - 2), max(0, light_impact_range - 2), max(0, flash_range - 2), 0, DOWN, spreading = FALSE) var/max_range = max(devastation_range, heavy_impact_range, light_impact_range, flash_range) @@ -212,78 +216,189 @@ var/datum/controller/subsystem/explosives/SSexplosives if(Array) Array.sense_explosion(x0,y0,z0,devastation_range,heavy_impact_range,light_impact_range,took) -// Handle a recursive explosion. -/datum/controller/subsystem/explosives/proc/explosion_rec(turf/epicenter, power) - if(power <= 0) return +// All the vars used on the turf should be on unsimulated turfs too, we just don't care about those generally. +#define SEARCH_DIR(dir) \ + search_direction = dir;\ + search_turf = get_step(current_turf, search_direction);\ + if (istype(search_turf, /turf/simulated)) {\ + turf_queue += search_turf;\ + dir_queue += search_direction;\ + power_queue += current_power;\ + } + +// Handle an iterative explosion. +/datum/controller/subsystem/explosives/proc/explosion_iter(turf/epicenter, power, z_transfer) + if(power <= 0) + return + epicenter = get_turf(epicenter) - if(!epicenter) return + if(!epicenter) + return message_admins("Explosion with size ([power]) in area [epicenter.loc.name] ([epicenter.x],[epicenter.y],[epicenter.z])") log_game("Explosion with size ([power]) in area [epicenter.loc.name] ") - playsound(epicenter, 'sound/effects/explosionfar.ogg', 100, 1, round(power*2,1) ) - playsound(epicenter, "explosion", 100, 1, round(power,1) ) + log_debug("iexpl: Beginning discovery phase.") + var/time = world.time - explosion_in_progress = 1 - explosion_turfs = list() + explosion_in_progress = TRUE + var/list/act_turfs = list() + act_turfs[epicenter] = power - explosion_turfs[epicenter] = power + power -= epicenter.explosion_resistance + for (var/obj/O in epicenter) + if (O.explosion_resistance) + power -= O.explosion_resistance - //This steap handles the gathering of turfs which will be ex_act() -ed in the next step. It also ensures each turf gets the maximum possible amount of power dealt to it. - for(var/direction in cardinal) - var/turf/T = get_step(epicenter, direction) - if (T) - explosion_spread(T, power - epicenter.explosion_resistance, direction) + if (power >= config.iterative_explosives_z_threshold) + if ((z_transfer & UP) && HasAbove(epicenter.z)) + var/datum/explosiondata/data = new + data.epicenter = GetAbove(epicenter) + data.rec_pow = power * config.iterative_explosives_z_multiplier + data.z_transfer = UP + data.spreading = TRUE + queue(data) + + if ((z_transfer & DOWN) && HasBelow(epicenter.z)) + var/datum/explosiondata/data = new + data.epicenter = GetBelow(epicenter) + data.rec_pow = power * config.iterative_explosives_z_multiplier + data.z_transfer = DOWN + data.spreading = TRUE + queue(data) + + // These three lists must always be the same length. + var/list/turf_queue = list(epicenter, epicenter, epicenter, epicenter) + var/list/dir_queue = list(NORTH, SOUTH, EAST, WEST) + var/list/power_queue = list(power, power, power, power) + + var/turf/simulated/current_turf + var/turf/search_turf + var/origin_direction + var/search_direction + var/current_power + var/index = 1 + while (index <= turf_queue.len) + current_turf = turf_queue[index] + origin_direction = dir_queue[index] + current_power = power_queue[index] + ++index + + if (!istype(current_turf) || current_power <= 0) CHECK_TICK + continue - //This step applies the ex_act effects for the explosion, as planned in the previous step. - for(var/turf/T in explosion_turfs) - if(!T || explosion_turfs[T] <= 0) + if (act_turfs[current_turf] >= current_power && current_turf != epicenter) + CHECK_TICK + continue + + act_turfs[current_turf] = current_power + current_power -= current_turf.explosion_resistance + + // Attempt to shortcut on empty tiles: if a turf only has a LO on it, we don't need to check object resistance. Some turfs might not have LOs, so we need to check it actually has one. + if (current_turf.contents.len > !!current_turf.lighting_overlay) + for (var/thing in current_turf) + var/atom/movable/AM = thing + if (AM.simulated && AM.explosion_resistance) + current_power -= AM.explosion_resistance + + if (current_power <= 0) + CHECK_TICK + continue + + SEARCH_DIR(origin_direction) + SEARCH_DIR(turn(origin_direction, 90)) + SEARCH_DIR(turn(origin_direction, -90)) + + CHECK_TICK + + log_debug("iexpl: Discovery completed in [(world.time-time)/10] seconds.") + log_debug("iexpl: Beginning SFX phase.") + time = world.time + + var/volume = 10 + (power * 20) + + var/frequency = get_rand_frequency() + var/close_dist = round(power + world.view - 2, 1) + + var/sound/explosion_sound = sound(get_sfx("explosion")) + + for (var/thing in player_list) + var/mob/M = thing + var/reception = EXPLFX_BOTH + + var/turf/T = isturf(M.loc) ? M.loc : get_turf(M) + + if (!T) + CHECK_TICK + continue + + if (!ARE_Z_CONNECTED(T.z, epicenter.z)) + CHECK_TICK + continue + + if (T.type == /turf/space) // Equality is faster than istype. + reception = EXPLFX_NONE + + for (var/turf/simulated/THING in RANGE_TURFS(1, M)) + reception |= EXPLFX_SHAKE + break + + if (!reception) + CHECK_TICK + continue + + var/dist = get_dist(M, epicenter) || 1 + if ((reception & EXPLFX_SOUND) && M.ear_deaf <= 0) + if (dist <= close_dist) + M.playsound_local(epicenter, explosion_sound, min(100, volume), 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 + volume = M.playsound_local(epicenter, 'sound/effects/explosionfar.ogg', volume, 1, frequency, usepressure = 0, falloff = 1000) + + if ((reception & EXPLFX_SHAKE) && volume > 0) + shake_camera(M, min(30, max(2,(power*2) / dist)), min(3.5, ((power/3) / dist)),0.05) + //Maximum duration is 3 seconds, and max strength is 3.5 + //Becuse values higher than those just get really silly + + CHECK_TICK + + log_debug("iexpl: SFX phase completed in [(world.time-time)/10] seconds.") + log_debug("iexpl: Beginning application phase.") + time = world.time + + var/turf_tally = 0 + var/movable_tally = 0 + for (var/thing in act_turfs) + var/turf/T = thing + if (act_turfs[T] <= 0) CHECK_TICK continue //Wow severity looks confusing to calculate... Fret not, I didn't leave you with any additional instructions or help. (just kidding, see the line under the calculation) - var/severity = 4 - round(max(min( 3, ((explosion_turfs[T] - T.explosion_resistance) / (max(3,(power/3)))) ) ,1), 1) //sanity effective power on tile divided by either 3 or one third the total explosion power - // One third because there are three power levels and I - // want each one to take up a third of the crater - var/x = T.x - var/y = T.y - var/z = T.z - T.ex_act(severity) - if(!T) - T = locate(x,y,z) - for(var/atom/A in T) - A.ex_act(severity) + var/severity = 4 - round(max(min( 3, ((act_turfs[T] - T.explosion_resistance) / (max(3,(power/3)))) ) ,1), 1) + //sanity effective power on tile divided by either 3 or one third the total explosion power + // One third because there are three power levels and I + // want each one to take up a third of the crater + + if (T.simulated) + T.ex_act(severity) + if (T.contents.len > !!T.lighting_overlay) + for (var/subthing in T) + var/atom/movable/AM = subthing + if (AM.simulated) + AM.ex_act(severity) + movable_tally++ + CHECK_TICK + else CHECK_TICK - explosion_in_progress = 0 + turf_tally++ -// A proc used by recursive explosions. (The actually recursive bit.) -/datum/controller/subsystem/explosives/proc/explosion_spread(turf/s, power, direction) - CHECK_TICK - if (istype(s, /turf/unsimulated)) - return - if(power <= 0) - return + explosion_in_progress = FALSE + log_debug("iexpl: Application completed in [(world.time-time)/10] seconds; processed [turf_tally] turfs and [movable_tally] movables.") - if(explosion_turfs[s] >= power) - return //The turf already sustained and spread a power greated than what we are dealing with. No point spreading again. - explosion_turfs[s] = power - - var/spread_power = power - s.explosion_resistance //This is the amount of power that will be spread to the tile in the direction of the blast - for(var/obj/O in s) - if(O.explosion_resistance) - spread_power -= O.explosion_resistance - - var/turf/T = get_step(s, direction) - if (T) - explosion_spread(T, spread_power, direction) - T = get_step(s, turn(direction,90)) - if (T) - explosion_spread(T, spread_power, turn(direction,90)) - T = get_step(s, turn(direction,-90)) - if (T) - explosion_spread(T, spread_power, turn(direction,90)) +#undef SEARCH_DIR // Add an explosion to the queue for processing. /datum/controller/subsystem/explosives/proc/queue(var/datum/explosiondata/data) @@ -308,5 +423,10 @@ var/datum/controller/subsystem/explosives/SSexplosives var/flash_range var/adminlog var/z_transfer - var/is_rec + var/spreading var/rec_pow + +#undef EXPLFX_BOTH +#undef EXPLFX_SOUND +#undef EXPLFX_SHAKE +#undef EXPLFX_NONE diff --git a/code/controllers/subsystems/initialization/atlas.dm b/code/controllers/subsystems/initialization/atlas.dm index 57e18b14c90..f6df5563bcd 100644 --- a/code/controllers/subsystems/initialization/atlas.dm +++ b/code/controllers/subsystems/initialization/atlas.dm @@ -17,6 +17,8 @@ var/datum/controller/subsystem/atlas/SSatlas var/map_override // If set, SSatlas will forcibly load this map. If the map does not exist, mapload will fail and SSatlas will panic. var/list/spawn_locations = list() + var/list/connected_z_cache = list() + /datum/controller/subsystem/atlas/New() NEW_SS_GLOBAL(SSatlas) @@ -103,6 +105,8 @@ var/datum/controller/subsystem/atlas/SSatlas var/obj/effect/landmark/map_data/marker = thing marker.setup() + connected_z_cache.Cut() + /datum/controller/subsystem/atlas/proc/get_selected_map() if (config.override_map) if (known_maps[config.override_map]) diff --git a/code/game/objects/explosion.dm b/code/game/objects/explosion.dm index 2f5cf8ff901..f3a0528d3f4 100644 --- a/code/game/objects/explosion.dm +++ b/code/game/objects/explosion.dm @@ -1,6 +1,6 @@ // explosion logic is in code/controllers/Processes/explosives.dm now -/proc/explosion(turf/epicenter, devastation_range, heavy_impact_range, light_impact_range, flash_range, adminlog = 1, z_transfer = UP|DOWN, is_rec = config.use_recursive_explosions) +/proc/explosion(turf/epicenter, devastation_range, heavy_impact_range, light_impact_range, flash_range, adminlog = 1, z_transfer = UP|DOWN, spreading = config.use_spreading_explosions) src = null //so we don't abort once src is deleted var/datum/explosiondata/data = new data.epicenter = epicenter @@ -10,7 +10,7 @@ data.flash_range = flash_range data.adminlog = adminlog data.z_transfer = z_transfer - data.is_rec = is_rec + data.spreading = spreading data.rec_pow = max(0,devastation_range) * 2 + max(0,heavy_impact_range) + max(0,light_impact_range) // queue work @@ -22,20 +22,20 @@ var/power = input(src, "power?", "power?") as num var/turf/T = get_turf(src.mob) var/datum/explosiondata/d = new - d.is_rec = 1 + d.spreading = TRUE d.epicenter = T d.rec_pow = power SSexplosives.queue(d) -/obj - var/explosion_resistance - -/turf - var/explosion_resistance +/atom + var/explosion_resistance /turf/space explosion_resistance = 3 +/turf/simulated/open + explosion_resistance = 3 + /turf/simulated/floor explosion_resistance = 1 diff --git a/code/game/objects/items/weapons/explosives.dm b/code/game/objects/items/weapons/explosives.dm index c406c9e801e..5326475cd04 100644 --- a/code/game/objects/items/weapons/explosives.dm +++ b/code/game/objects/items/weapons/explosives.dm @@ -76,7 +76,7 @@ if(!target) target = src if(location) - explosion(location, -1, -1, 2, 3, is_rec = 0) + explosion(location, -1, -1, 2, 3, spreading = 0) if(target) if (istype(target, /turf/simulated/wall)) diff --git a/code/game/sound.dm b/code/game/sound.dm index 17438051cc5..59464354eb5 100644 --- a/code/game/sound.dm +++ b/code/game/sound.dm @@ -155,15 +155,14 @@ var/list/footstepfx = list("defaultstep","concretestep","grassstep","dirtstep"," /proc/playsound(atom/source, soundin, vol as num, vary, extrarange as num, falloff, is_global, usepressure = 1, environment = -1, is_ambience = FALSE, is_footstep = FALSE) if (istext(soundin)) soundin = get_sfx(soundin) // same sound for everyone - - if(isarea(source)) + else if (isarea(source)) error("[source] is an area and is trying to make the sound: [soundin]") return var/frequency = get_rand_frequency() // Same frequency for everybody var/turf/turf_source = get_turf(source) // cache this so we don't create a new one for everybody - var/sound/S = sound(get_sfx(soundin)) + var/sound/S = sound(soundin) // Looping through the player list has the added bonus of working for mobs inside containers for (var/P in player_list) var/mob/M = P @@ -173,10 +172,19 @@ var/list/footstepfx = list("defaultstep","concretestep","grassstep","dirtstep"," var/distance = get_dist(M, turf_source) if(distance <= (world.view + extrarange) * 3) var/turf/T = get_turf(M) - if(T && T.z == turf_source.z && (!is_ambience || M.client.prefs.toggles & SOUND_AMBIENCE) && (!is_footstep || M.client.prefs.asfx_togs & ASFX_FOOTSTEPS)) - M.playsound_local(turf_source, soundin, vol, vary, frequency, falloff, is_global, usepressure, environment, S) -var/const/FALLOFF_SOUNDS = 0.5 + // These checks are split into multiple ifs for readability reasons. + + if (!T || T.z != turf_source.z) + continue + + if (is_ambience && !(M.client.prefs.toggles & SOUND_AMBIENCE)) + continue + + if (is_footstep && !(M.client.prefs.asfx_togs & ASFX_FOOTSTEPS)) + continue + + M.playsound_local(turf_source, soundin, vol, vary, frequency, falloff, is_global, usepressure, environment, S) /mob/proc/playsound_local(turf/turf_source, soundin, vol as num, vary, frequency, falloff, is_global, usepressure = 1, environment = -1, sound/S) if(!client || ear_deaf > 0) @@ -237,8 +245,8 @@ var/const/FALLOFF_SOUNDS = 0.5 S.x = dx var/dz = turf_source.y - T.y // Hearing from infront/behind S.z = dz - // The y value is for above your head, but there is no ceiling in 2d spessmens. - S.y = 1 + // 3D sound, truly this is the future. + S.y = (turf_source.z - T.z) * SOUND_Z_FACTOR S.falloff = (falloff ? falloff : FALLOFF_SOUNDS) if(!is_global && environment != 0) diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index b993add9039..31752806ebd 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -1026,15 +1026,15 @@ var/list/admin_verbs_cciaa = list( if (!check_rights(R_SERVER|R_DEBUG)) return - var/ans = alert(src, "This will force explosions to run in the [config.use_recursive_explosions ? "old manner (non-recursive)" : "new, realistic manner (recursive)"]. Do you want to proceed?", "Switch explosion type", "Yes", "Cancel") + var/ans = alert(src, "This will force explosions to run in the [config.use_spreading_explosions ? "old manner (circular)" : "new, realistic manner (spreading)"]. Do you want to proceed?", "Switch explosion type", "Yes", "Cancel") if (!ans || ans == "Cancel") src << "Cancelled." return - config.use_recursive_explosions = !config.use_recursive_explosions + config.use_spreading_explosions = !config.use_spreading_explosions - log_and_message_admins("has toggled explosions to be [config.use_recursive_explosions ? "recursive" : "non-recursive"].") + log_and_message_admins("has toggled explosions to be [config.use_spreading_explosions ? "iterative/spreading" : "simple/circular"].") feedback_add_details("admin_verb", "TRE") /client/proc/wipe_ai() diff --git a/code/modules/multiz/basic.dm b/code/modules/multiz/basic.dm index dbd6e6276b8..5615e69bb54 100644 --- a/code/modules/multiz/basic.dm +++ b/code/modules/multiz/basic.dm @@ -44,12 +44,28 @@ var/z_levels = 0 // Each bit represents a connection between adjacent levels. S /proc/GetConnectedZlevels(z) . = list(z) for(var/level = z, HasBelow(level), level--) - . |= level-1 + . += level-1 for(var/level = z, HasAbove(level), level++) - . |= level+1 + . += level+1 -proc/AreConnectedZLevels(var/zA, var/zB) - return zA == zB || (zB in GetConnectedZlevels(zA)) +/proc/AreConnectedZLevels(var/zA, var/zB) + if (zA == zB) + return TRUE + + if (SSatlas.connected_z_cache.len >= zA && SSatlas.connected_z_cache[zA]) + return SSatlas.connected_z_cache[zA][zB] + + var/list/levels = GetConnectedZlevels(zA) + var/list/new_entry = new(max(levels)) + for (var/entry in levels) + new_entry[entry] = TRUE + + if (SSatlas.connected_z_cache.len < zA) + SSatlas.connected_z_cache.len = zA + + SSatlas.connected_z_cache[zA] = new_entry + + return new_entry[zB] /proc/get_zstep(ref, dir) if(dir == UP) diff --git a/config/example/config.txt b/config/example/config.txt index 0eee64f11d0..e702a751c0f 100644 --- a/config/example/config.txt +++ b/config/example/config.txt @@ -33,8 +33,14 @@ JOBS_HAVE_MINIMAL_ACCESS ## Unhash to enable loading of age restrictions from age_restrictions.txt. #LOAD_AGE_RESTRICTIONS_FROM_FILE -## Unhash this to use recursive explosions, keep it hashed to use circle explosions. Recursive explosions react to walls, airlocks and blast doors, making them look a lot cooler than the boring old circular explosions. They require more CPU and are (as of january 2013) experimental -#USE_RECURSIVE_EXPLOSIONS +## Unhash this to use iterative explosions, keep it hashed to use circle explosions. Iterative explosions react to walls, airlocks and blast doors, making them look a lot cooler than the boring old circular explosions. They require more CPU, but are generally better to use. +#USE_SPREADING_EXPLOSIONS + +# The power of explosion required for it to cross Z-levels. +#EXPLOSION_Z_THRESHOLD 10 + +# What to multiply power by when crossing Z-levels. +#EXPLOSION_Z_MULT 0.75 ## log OOC channel LOG_OOC diff --git a/html/changelogs/lohikar-iterativeexpl.yml b/html/changelogs/lohikar-iterativeexpl.yml new file mode 100644 index 00000000000..1128bb3b99f --- /dev/null +++ b/html/changelogs/lohikar-iterativeexpl.yml @@ -0,0 +1,6 @@ +author: Lohikar +delete-after: True +changes: + - experiment: "Spreading-style explosions have been rewritten: they should be faster and more reliable." + - rscadd: "Spreading-style explosions now have simple explosions' directional explosion sounds." + - rscadd: "Spreading-style explosions now cross Z-levels."