From 858da9f19a91d558a42c80bdebab01fe44c8c433 Mon Sep 17 00:00:00 2001 From: LemonInTheDark <58055496+LemonInTheDark@users.noreply.github.com> Date: Mon, 9 Jan 2023 22:29:03 -0800 Subject: [PATCH] Optimizes explosions (very slightly) (#71763) ## About The Pull Request We do two major things here. First, instead of having every turf need to ask all the turfs in "front" of it for its blast resistance, we have blast resistance carry back in a cache, so they only need to ask the one directly in front of them. Much faster, such wow The other thing we do is totally remove the idea of "building" a turf's explosion resistance. Instead, a turf's full resistance is stored on it at all times. We use an element to manage objects that want to block explosives, and that's it simple as. As an optimization, turfs handle block differently, using a system where we imply that a turf's own block is just the initial of their explosive_resistance, unless proven otherwise This also saves a significant amount of time To be honest with you, I did this mostly cause I wanted to well make explosions faster This doesn't really fufil that. They are faster, but not by much The bulk of explosion cost comes from actually exploding things, rather then figuring out what/how to delete. This code is much faster for larger blastwave sizes, because calculating protection is constant. We save maybe 60% of propogate_blastwave, but unfortunately propogate_blastwave for one maxcap on the top left of icebox's chapel is only 28ms, so while 11ms is good, it's not everything I could want when the cost of explosion/fire is 555ms. I'm happy about it still tho, because doing things like this means I can expand on how explosive blocking works without needing to make things seriously expensive in here. Also it's faster for meme admin explosions and mega burgers ## Why It's Good For The Game Speeds up explosives slightly, opens the door to better blast resistance projection --- .../signals_atom/signals_atom_explosion.dm | 2 + code/__DEFINES/explosions.dm | 3 - code/__DEFINES/is_helpers.dm | 1 + code/__DEFINES/maths.dm | 4 + code/__DEFINES/traits.dm | 2 + code/__HELPERS/game.dm | 6 - code/controllers/subsystem/explosions.dm | 246 ++++++++---------- code/datums/elements/blocks_explosives.dm | 56 ++++ code/game/atoms.dm | 8 +- code/game/atoms_movable.dm | 19 +- code/game/machinery/doors/door.dm | 21 +- code/game/objects/items/RCD.dm | 2 +- code/game/objects/items/puzzle_pieces.dm | 1 + code/game/objects/structures/window.dm | 10 +- code/game/turfs/change_turf.dm | 5 + code/game/turfs/closed/_closed.dm | 6 +- code/game/turfs/closed/wall/mineral_walls.dm | 16 +- code/game/turfs/closed/wall/reinf_walls.dm | 4 +- code/game/turfs/closed/walls.dm | 4 +- code/game/turfs/open/space/transit.dm | 2 +- code/game/turfs/turf.dm | 32 +++ .../antagonists/blob/structures/core.dm | 1 + .../antagonists/blob/structures/shield.dm | 4 + code/modules/awaymissions/cordon.dm | 2 +- code/modules/clothing/suits/jobs.dm | 1 - code/modules/industrial_lift/tram_walls.dm | 1 + .../ruins/spaceruin_code/hilbertshotel.dm | 6 +- .../projectiles/guns/special/blastcannon.dm | 8 +- tgstation.dme | 1 + 29 files changed, 284 insertions(+), 190 deletions(-) create mode 100644 code/datums/elements/blocks_explosives.dm diff --git a/code/__DEFINES/dcs/signals/signals_atom/signals_atom_explosion.dm b/code/__DEFINES/dcs/signals/signals_atom/signals_atom_explosion.dm index d2328f5e500..7d7ba861398 100644 --- a/code/__DEFINES/dcs/signals/signals_atom/signals_atom_explosion.dm +++ b/code/__DEFINES/dcs/signals/signals_atom/signals_atom_explosion.dm @@ -11,3 +11,5 @@ /// When returned on a signal hooked to [COMSIG_ATOM_EXPLODE], [COMSIG_ATOM_INTERNAL_EXPLOSION], or [COMSIG_AREA_INTERNAL_EXPLOSION] it prevents the explosion from being propagated further. #define COMSIG_CANCEL_EXPLOSION (1<<0) +/// from [/atom/movable/proc/set_explosion_resistance] : (old_block, new_block) +#define COMSIG_MOVABLE_EXPLOSION_BLOCK_CHANGED "explosion_block_changed" diff --git a/code/__DEFINES/explosions.dm b/code/__DEFINES/explosions.dm index cd30f89f1b0..f867ee9b813 100644 --- a/code/__DEFINES/explosions.dm +++ b/code/__DEFINES/explosions.dm @@ -18,9 +18,6 @@ /// Gibtonite will now explode #define GIBTONITE_DETONATE 3 -/// For object explosion block calculation -#define EXPLOSION_BLOCK_PROC -1 - /// A wrapper for [/atom/proc/ex_act] to ensure that the explosion propagation and attendant signal are always handled. #define EX_ACT(target, args...)\ if(!(target.flags_1 & PREVENT_CONTENTS_EXPLOSION_1)) { \ diff --git a/code/__DEFINES/is_helpers.dm b/code/__DEFINES/is_helpers.dm index 0ffa7235fe4..16aaf019de9 100644 --- a/code/__DEFINES/is_helpers.dm +++ b/code/__DEFINES/is_helpers.dm @@ -281,3 +281,4 @@ GLOBAL_LIST_INIT(book_types, typecacheof(list( #define is_unassigned_job(job_type) (istype(job_type, /datum/job/unassigned)) #define isprojectilespell(thing) (istype(thing, /datum/action/cooldown/spell/pointed/projectile)) +#define is_multi_tile_object(atom) (atom.bound_width > world.icon_size || atom.bound_height > world.icon_size) diff --git a/code/__DEFINES/maths.dm b/code/__DEFINES/maths.dm index db411b6285d..7117d7b4645 100644 --- a/code/__DEFINES/maths.dm +++ b/code/__DEFINES/maths.dm @@ -237,5 +237,9 @@ #define GET_TRUE_DIST(a, b) (a == null || b == null) ? -1 : max(abs(a.x -b.x), abs(a.y-b.y), abs(a.z-b.z)) +//We used to use linear regression to approximate the answer, but Mloc realized this was actually faster. +//And lo and behold, it is, and it's more accurate to boot. +#define CHEAP_HYPOTENUSE(Ax, Ay, Bx, By) (sqrt(abs(Ax - Bx) ** 2 + abs(Ay - By) ** 2)) //A squared + B squared = C squared + /// The number of cells in a taxicab circle (rasterized diamond) of radius X. #define DIAMOND_AREA(X) (1 + 2*(X)*((X)+1)) diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm index ae87eabb5fb..846d2b96dec 100644 --- a/code/__DEFINES/traits.dm +++ b/code/__DEFINES/traits.dm @@ -517,6 +517,8 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai #define TRAIT_PARALYSIS "paralysis" /// Used for limbs. #define TRAIT_DISABLED_BY_WOUND "disabled-by-wound" +/// This movable atom has the explosive block element +#define TRAIT_BLOCKING_EXPLOSIVES "blocking_explosives" /// Mobs with this trait can't send the mining shuttle console when used outside the station itself #define TRAIT_FORBID_MINING_SHUTTLE_CONSOLE_OUTSIDE_STATION "forbid_mining_shuttle_console_outside_station" diff --git a/code/__HELPERS/game.dm b/code/__HELPERS/game.dm index 8cf6be1b04f..ccca11387dd 100644 --- a/code/__HELPERS/game.dm +++ b/code/__HELPERS/game.dm @@ -11,12 +11,6 @@ return null return format_text ? format_text(checked_area.name) : checked_area.name -//We used to use linear regression to approximate the answer, but Mloc realized this was actually faster. -//And lo and behold, it is, and it's more accurate to boot. -///Calculate the hypotenuse cheaply (this should be in maths.dm) -/proc/cheap_hypotenuse(Ax, Ay, Bx, By) - return sqrt(abs(Ax - Bx) ** 2 + abs(Ay - By) ** 2) //A squared + B squared = C squared - /** toggle_organ_decay * inputs: first_object (object to start with) * outputs: diff --git a/code/controllers/subsystem/explosions.dm b/code/controllers/subsystem/explosions.dm index 177beb45cae..a7becb28706 100644 --- a/code/controllers/subsystem/explosions.dm +++ b/code/controllers/subsystem/explosions.dm @@ -119,30 +119,31 @@ SUBSYSTEM_DEF(explosions) 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) + var/list/cached_exp_block = list() + for(var/turf/explode in prepare_explosion_turfs(max_range, epicenter)) + wipe_colours += explode + var/our_x = explode.x + var/our_y = explode.y + var/dist = CHEAP_HYPOTENUSE(our_x, our_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(explode != epicenter) + var/our_block = cached_exp_block[get_step_towards(explode, epicenter)] + dist += our_block + cached_exp_block[explode] = our_block + explode.explosive_resistance + else + cached_exp_block[explode] = explode.explosive_resistance + dist = round(dist, 0.01) if(dist < dev) - T.color = "red" - T.maptext = MAPTEXT("Dev") + explode.color = "red" + explode.maptext = MAPTEXT("[dist]") else if (dist < heavy) - T.color = "yellow" - T.maptext = MAPTEXT("Heavy") + explode.color = "yellow" + explode.maptext = MAPTEXT("[dist]") else if (dist < light) - T.color = "blue" - T.maptext = MAPTEXT("Light") + explode.color = "blue" + explode.maptext = MAPTEXT("[dist]") else continue @@ -379,82 +380,77 @@ SUBSYSTEM_DEF(explosions) for(var/mob/living/L in viewers(flash_range, epicenter)) L.flash_act() - var/list/affected_turfs = GatherSpiralTurfs(max_range, epicenter) + var/list/affected_turfs = prepare_explosion_turfs(max_range, epicenter) var/reactionary = CONFIG_GET(flag/reactionary_explosions) - var/list/cached_exp_block - - if(reactionary) - cached_exp_block = CaculateExplosionBlock(affected_turfs) + // this list is setup in the form position -> block for that position + // we assert that turfs will be processed closed to farthest, so we can build this as we go along + // This is gonna be an array, index'd by turfs + var/list/cached_exp_block = list() //lists are guaranteed to contain at least 1 turf at this point + //we presuppose that we'll be iterating away from the epicenter + for(var/turf/explode as anything in affected_turfs) + var/our_x = explode.x + var/our_y = explode.y + var/dist = CHEAP_HYPOTENUSE(our_x, our_y, x0, y0) - 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 - + // Using this pattern, block will flow out from blocking turfs, essentially caching the recursion + // This is safe because if get_step_towards is ever anything but caridnally off, it'll do a diagonal move + // So we always sample from a "loop" closer + // It's kind of behaviorly unimpressive that that's a problem for the future if(reactionary) - var/turf/Trajectory = T - while(Trajectory != epicenter) - Trajectory = get_step_towards(Trajectory, epicenter) - dist += cached_exp_block[Trajectory] + if(explode == epicenter) + cached_exp_block[explode] = explode.explosive_resistance + else + var/our_block = cached_exp_block[get_step_towards(explode, epicenter)] + dist += our_block + cached_exp_block[explode] = our_block + explode.explosive_resistance - var/flame_dist = dist < flame_range - var/throw_dist = dist + var/severity = EXPLODE_NONE if(dist < devastation_range) - dist = EXPLODE_DEVASTATE + severity = EXPLODE_DEVASTATE else if(dist < heavy_impact_range) - dist = EXPLODE_HEAVY + severity = EXPLODE_HEAVY else if(dist < light_impact_range) - dist = EXPLODE_LIGHT - else - dist = EXPLODE_NONE + severity = EXPLODE_LIGHT - if(T == epicenter) // Ensures explosives detonating from bags trigger other explosives in that bag + if(explode == 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.get_all_contents(ignore_flag_1 = PREVENT_CONTENTS_EXPLOSION_1) - if(isliving(A)) - items -= A //Stops mobs from taking double damage from explosions originating from them/their turf, such as from projectiles - 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) + for(var/atom/holder as anything in explode) + if (length(holder.contents) && !(holder.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 += holder.get_all_contents(ignore_flag_1 = PREVENT_CONTENTS_EXPLOSION_1) + if(isliving(holder)) + items -= holder //Stops mobs from taking double damage from explosions originating from them/their turf, such as from projectiles + switch(severity) + if(EXPLODE_DEVASTATE) + SSexplosions.high_mov_atom += items + if(EXPLODE_HEAVY) + SSexplosions.med_mov_atom += items + if(EXPLODE_LIGHT) + SSexplosions.low_mov_atom += items + switch(severity) if(EXPLODE_DEVASTATE) - SSexplosions.highturf += T + SSexplosions.highturf += explode if(EXPLODE_HEAVY) - SSexplosions.medturf += T + SSexplosions.medturf += explode if(EXPLODE_LIGHT) - SSexplosions.lowturf += T + SSexplosions.lowturf += explode - - if(flame_dist && prob(40) && !isspaceturf(T) && !T.density) - flameturf += T + if(prob(40) && dist < flame_range && !isspaceturf(explode) && !explode.density) + flameturf += explode //--- 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 + if (explode.explosion_throw_details) + var/list/throwingturf = explode.explosion_throw_details + if (throwingturf[1] < max_range - dist) + throwingturf[1] = max_range - dist + throwingturf[2] = get_dir(epicenter, explode) throwingturf[3] = max_range else - T.explosion_throw_details = list(throw_range, throw_dir, max_range) - throwturf += T + explode.explosion_throw_details = list(max_range - dist, get_dir(epicenter, explode), max_range) + throwturf += explode var/took = (REALTIMEOFDAY - started_at) / 10 @@ -572,68 +568,40 @@ SUBSYSTEM_DEF(explosions) #undef FREQ_UPPER #undef FREQ_LOWER -/datum/controller/subsystem/explosions/proc/GatherSpiralTurfs(range, turf/epicenter) +/// Returns a list of turfs in X range from the epicenter +/// Returns in a unique order, spiraling outwards +/// This is done to ensure our progressive cache of blast resistance is always valid +/// This is quite fast +/proc/prepare_explosion_turfs(range, turf/epicenter) var/list/outlist = list() - var/center = epicenter - var/dist = range - if(!dist) - outlist += center - return outlist + // Add in the center + outlist += epicenter - var/turf/t_center = get_turf(center) - if(!t_center) - return outlist + var/our_x = epicenter.x + var/our_y = epicenter.y + var/our_z = epicenter.z - var/list/L = outlist - var/turf/T - var/y - var/x - var/c_dist = 1 - L += t_center + var/max_x = world.maxx + var/max_y = world.maxy + for(var/i in 1 to range) + var/lowest_x = our_x - i + var/lowest_y = our_y - i + var/highest_x = our_x + i + var/highest_y = our_y + i + // top left to one before top right + if(highest_y <= max_y) + outlist += block(locate(max(lowest_x, 1), highest_y, our_z), locate(min(highest_x - 1, max_x), highest_y, our_z)) + // top right to one before bottom right + if(highest_x <= max_x) + outlist += block(locate(highest_x, min(highest_y, max_y), our_z), locate(highest_x, max(lowest_y + 1, 1), our_z)) + // bottom right to one before bottom left + if(lowest_y >= 1) + outlist += block(locate(min(highest_x, max_x), lowest_y, our_z), locate(max(lowest_x + 1, 1), lowest_y, our_z)) + // bottom left to one before top left + if(lowest_x >= 1) + outlist += block(locate(lowest_x, max(lowest_y, 1), our_z), locate(lowest_x, min(highest_y - 1, max_y), our_z)) - 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 + return outlist /datum/controller/subsystem/explosions/fire(resumed = 0) if (!is_exploding()) @@ -722,15 +690,15 @@ SUBSYSTEM_DEF(explosions) 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) + var/turf/explode = thing + var/list/details = explode.explosion_throw_details + explode.explosion_throw_details = null + if (length(details) != 3) continue - var/throw_range = L[1] - var/throw_dir = L[2] - var/max_range = L[3] - for(var/atom/movable/A in T) + var/throw_range = details[1] + var/throw_dir = details[2] + var/max_range = details[3] + for(var/atom/movable/A in explode) if(QDELETED(A)) continue if(!A.anchored && A.move_resist != INFINITY) diff --git a/code/datums/elements/blocks_explosives.dm b/code/datums/elements/blocks_explosives.dm new file mode 100644 index 00000000000..1e867a81c1e --- /dev/null +++ b/code/datums/elements/blocks_explosives.dm @@ -0,0 +1,56 @@ +/// Apply this element to a movable atom when you want it to block explosions +/// It will mirror the blocking down to that movable's turf, keeping explosion work cheap +/datum/element/blocks_explosives + +/datum/element/blocks_explosives/Attach(datum/target) + if(!ismovable(target)) + return + . = ..() + ADD_TRAIT(target, TRAIT_BLOCKING_EXPLOSIVES, TRAIT_GENERIC) + var/atom/movable/moving_target = target + RegisterSignal(moving_target, COMSIG_MOVABLE_MOVED, PROC_REF(blocker_moved)) + RegisterSignal(moving_target, COMSIG_MOVABLE_EXPLOSION_BLOCK_CHANGED, PROC_REF(blocking_changed)) + moving_target.explosive_resistance = moving_target.explosion_block + + if(length(moving_target.locs) > 1) + for(var/atom/location as anything in moving_target.locs) + block_loc(location, moving_target.explosion_block) + else if(moving_target.loc) + block_loc(moving_target.loc, moving_target.explosion_block) + +/datum/element/blocks_explosives/Detach(datum/source) + . = ..() + REMOVE_TRAIT(source, TRAIT_BLOCKING_EXPLOSIVES, TRAIT_GENERIC) + +/// Call this when our blocking well, changes. we'll update our turf(s) with the details +/datum/element/blocks_explosives/proc/blocking_changed(atom/movable/target, old_block, new_block) + if(length(target.locs) > 1) + for(var/atom/location as anything in target.locs) + unblock_loc(location, old_block) + block_loc(location, new_block) + else if(target.loc) + unblock_loc(target.loc, old_block) + block_loc(target.loc, new_block) + +/// Applies a block amount to a turf. proc for convenince +/datum/element/blocks_explosives/proc/block_loc(atom/location, block_amount) + location.explosive_resistance += block_amount + +/// Removes a block amount from a turf. proc for convenince +/datum/element/blocks_explosives/proc/unblock_loc(atom/location, block_amount) + location.explosive_resistance -= block_amount + +/// Essentially just blocking_changed except we remove from the old loc, and add to the new one +/datum/element/blocks_explosives/proc/blocker_moved(atom/movable/target, atom/old_loc, dir, forced, list/old_locs) + if(length(old_locs) > 1) + for(var/atom/location as anything in old_locs) + unblock_loc(location, target.explosion_block) + else if(old_loc) + unblock_loc(old_loc, target.explosion_block) + + if(length(target.locs) > 1) + for(var/atom/location as anything in target.locs) + block_loc(location, target.explosion_block) + else if(target.loc) + block_loc(target.loc, target.explosion_block) + diff --git a/code/game/atoms.dm b/code/game/atoms.dm index 6b758061a04..696be4274d9 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -39,8 +39,8 @@ ///HUD images that this atom can provide. var/list/hud_possible - ///Value used to increment ex_act() if reactionary_explosions is on - var/explosion_block = 0 + ///How much this atom resists explosions by, in the end + var/explosive_resistance = 0 /** * used to store the different colors on an atom @@ -1850,6 +1850,10 @@ pixel_y = pixel_y + base_pixel_y - . +// Not a valid operation, turfs and movables handle block differently +/atom/proc/set_explosion_block(explosion_block) + return + /** * Returns true if this atom has gravity for the passed in turf * diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm index 6a97e1c370a..d67695d0093 100644 --- a/code/game/atoms_movable.dm +++ b/code/game/atoms_movable.dm @@ -94,6 +94,11 @@ /// The degree of pressure protection that mobs in list/contents have from the external environment, between 0 and 1 var/contents_pressure_protection = 0 + /// Value used to increment ex_act() if reactionary_explosions is on + /// How much we as a source block explosions by + /// Will not automatically apply to the turf below you, you need to apply /datum/element/block_explosives in conjunction with this + var/explosion_block = 0 + /mutable_appearance/emissive_blocker /mutable_appearance/emissive_blocker/New() @@ -104,6 +109,11 @@ /atom/movable/Initialize(mapload) . = ..() +#ifdef UNIT_TESTS + if(explosion_block && !HAS_TRAIT(src, TRAIT_BLOCKING_EXPLOSIVES)) + stack_trace("[type] blocks explosives, but does not have the managing element applied") +#endif + switch(blocks_emissive) if(EMISSIVE_BLOCK_GENERIC) var/static/mutable_appearance/emissive_blocker/blocker = new() @@ -515,7 +525,7 @@ if(set_dir_on_move && dir != direction && update_dir) setDir(direction) - var/is_multi_tile_object = bound_width > 32 || bound_height > 32 + var/is_multi_tile_object = is_multi_tile_object(src) var/list/old_locs if(is_multi_tile_object && isturf(loc)) @@ -1129,6 +1139,13 @@ return TRUE +/atom/movable/set_explosion_block(explosion_block) + var/old_block = src.explosion_block + explosive_resistance -= old_block + src.explosion_block = explosion_block + explosive_resistance += explosion_block + SEND_SIGNAL(src, COMSIG_MOVABLE_EXPLOSION_BLOCK_CHANGED, old_block, explosion_block) + /atom/movable/proc/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum) set waitfor = FALSE var/hitpush = TRUE diff --git a/code/game/machinery/doors/door.dm b/code/game/machinery/doors/door.dm index 18d33711fd8..b81d87a6cac 100644 --- a/code/game/machinery/doors/door.dm +++ b/code/game/machinery/doors/door.dm @@ -57,6 +57,7 @@ acid = 70 /obj/machinery/door/Initialize(mapload) + AddElement(/datum/element/blocks_explosives) . = ..() set_init_door_layer() update_freelook_sight() @@ -72,7 +73,7 @@ //doors only block while dense though so we have to use the proc real_explosion_block = explosion_block - explosion_block = EXPLOSION_BLOCK_PROC + update_explosive_block() RegisterSignal(SSsecurity_level, COMSIG_SECURITY_LEVEL_CHANGED, PROC_REF(check_security_level)) var/static/list/loc_connections = list( @@ -483,9 +484,6 @@ //if it blows up a wall it should blow up a door return ..(severity ? min(EXPLODE_DEVASTATE, severity + 1) : EXPLODE_NONE, target) -/obj/machinery/door/GetExplosionBlock() - return density ? real_explosion_block : 0 - /obj/machinery/door/power_change() . = ..() if(. && !(machine_stat & NOPOWER)) @@ -501,4 +499,19 @@ INVOKE_ASYNC(src, PROC_REF(open)) +/obj/machinery/door/set_density(new_value) + . = ..() + update_explosive_block() + +/obj/machinery/door/proc/update_explosive_block() + set_explosion_block(real_explosion_block) + +// Kinda roundabout, essentially if we're dense, we respect real_explosion_block +// Otherwise, we block nothing +/obj/machinery/door/set_explosion_block(explosion_block) + real_explosion_block = explosion_block + if(density) + return ..() + return ..(0) + #undef DOOR_CLOSE_WAIT diff --git a/code/game/objects/items/RCD.dm b/code/game/objects/items/RCD.dm index d5710ba8c3b..bb64fc89072 100644 --- a/code/game/objects/items/RCD.dm +++ b/code/game/objects/items/RCD.dm @@ -1008,7 +1008,7 @@ GLOBAL_VAR_INIT(icon_holographic_window, init_holographic_window()) if(istype(O)) var/x0 = O.x var/y0 = O.y - var/contender = cheap_hypotenuse(start.x, start.y, x0, y0) + var/contender = CHEAP_HYPOTENUSE(start.x, start.y, x0, y0) if(!winner) winner = O winning_dist = contender diff --git a/code/game/objects/items/puzzle_pieces.dm b/code/game/objects/items/puzzle_pieces.dm index 93321819ce4..b176d2b6823 100644 --- a/code/game/objects/items/puzzle_pieces.dm +++ b/code/game/objects/items/puzzle_pieces.dm @@ -210,6 +210,7 @@ acid = 100 /obj/structure/light_puzzle/Initialize(mapload) + AddElement(/datum/element/blocks_explosives) . = ..() var/generated_board = -1 while(generated_board in banned_combinations) diff --git a/code/game/objects/structures/window.dm b/code/game/objects/structures/window.dm index 61247b7163a..38988209284 100644 --- a/code/game/objects/structures/window.dm +++ b/code/game/objects/structures/window.dm @@ -40,6 +40,7 @@ acid = 100 /obj/structure/window/Initialize(mapload, direct) + AddElement(/datum/element/blocks_explosives) . = ..() if(direct) setDir(direct) @@ -55,9 +56,9 @@ setDir() AddElement(/datum/element/can_barricade) - //windows only block while reinforced and fulltile, so we'll use the proc - real_explosion_block = explosion_block - explosion_block = EXPLOSION_BLOCK_PROC + //windows only block while reinforced and fulltile + if(!reinf || !fulltile) + set_explosion_block(0) flags_1 |= ALLOW_DARK_PAINTS_1 RegisterSignal(src, COMSIG_OBJ_PAINTED, PROC_REF(on_painted)) @@ -424,9 +425,6 @@ return TRUE -/obj/structure/window/GetExplosionBlock() - return reinf && fulltile ? real_explosion_block : 0 - /obj/structure/window/spawner/east dir = EAST diff --git a/code/game/turfs/change_turf.dm b/code/game/turfs/change_turf.dm index 4c7fbda0e4a..0d04fcab2c4 100644 --- a/code/game/turfs/change_turf.dm +++ b/code/game/turfs/change_turf.dm @@ -78,6 +78,9 @@ GLOBAL_LIST_INIT(blacklisted_automated_baseturfs, typecacheof(list( var/old_dynamic_lumcount = dynamic_lumcount var/old_rcd_memory = rcd_memory var/old_always_lit = always_lit + var/old_explosion_throw_details = explosion_throw_details + // We get just the bits of explosive_resistance that aren't the turf + var/old_explosive_resistance = explosive_resistance - get_explosive_block() var/old_lattice_underneath = lattice_underneath var/old_bp = blueprint_data @@ -117,6 +120,8 @@ GLOBAL_LIST_INIT(blacklisted_automated_baseturfs, typecacheof(list( new_turf.blueprint_data = old_bp new_turf.rcd_memory = old_rcd_memory + new_turf.explosion_throw_details = old_explosion_throw_details + new_turf.explosive_resistance += old_explosive_resistance lighting_corner_NE = old_lighting_corner_NE lighting_corner_SE = old_lighting_corner_SE diff --git a/code/game/turfs/closed/_closed.dm b/code/game/turfs/closed/_closed.dm index 58af2771652..10d2a353b2e 100644 --- a/code/game/turfs/closed/_closed.dm +++ b/code/game/turfs/closed/_closed.dm @@ -20,7 +20,7 @@ name = "wall" desc = "Effectively impervious to conventional methods of destruction." icon = 'icons/turf/walls.dmi' - explosion_block = 50 + explosive_resistance = 50 /turf/closed/indestructible/rust_heretic_act() return @@ -280,7 +280,7 @@ INITIALIZE_IMMEDIATE(/turf/closed/indestructible/splashscreen) desc = "A seemingly impenetrable wall." icon = 'icons/turf/walls.dmi' icon_state = "necro" - explosion_block = 50 + explosive_resistance = 50 baseturfs = /turf/closed/indestructible/necropolis /turf/closed/indestructible/necropolis/get_smooth_underlay_icon(mutable_appearance/underlay_appearance, turf/asking_turf, adjacency_dir) @@ -308,7 +308,7 @@ INITIALIZE_IMMEDIATE(/turf/closed/indestructible/splashscreen) smoothing_flags = SMOOTH_BITMASK smoothing_groups = SMOOTH_GROUP_CLOSED_TURFS + SMOOTH_GROUP_BOSS_WALLS canSmoothWith = SMOOTH_GROUP_BOSS_WALLS - explosion_block = 50 + explosive_resistance = 50 baseturfs = /turf/closed/indestructible/riveted/boss /turf/closed/indestructible/riveted/boss/see_through diff --git a/code/game/turfs/closed/wall/mineral_walls.dm b/code/game/turfs/closed/wall/mineral_walls.dm index 9bcd1b4061f..6e88d4640a3 100644 --- a/code/game/turfs/closed/wall/mineral_walls.dm +++ b/code/game/turfs/closed/wall/mineral_walls.dm @@ -15,7 +15,7 @@ base_icon_state = "gold_wall" sheet_type = /obj/item/stack/sheet/mineral/gold hardness = 65 //gold is soft - explosion_block = 0 //gold is a soft metal you dingus. + explosive_resistance = 0 //gold is a soft metal you dingus. smoothing_groups = SMOOTH_GROUP_GOLD_WALLS + SMOOTH_GROUP_WALLS + SMOOTH_GROUP_CLOSED_TURFS canSmoothWith = SMOOTH_GROUP_GOLD_WALLS custom_materials = list(/datum/material/gold = 4000) @@ -42,7 +42,7 @@ sheet_type = /obj/item/stack/sheet/mineral/diamond hardness = 5 //diamond is very hard slicing_duration = 200 //diamond wall takes twice as much time to slice - explosion_block = 3 + explosive_resistance = 3 smoothing_flags = SMOOTH_BITMASK smoothing_groups = SMOOTH_GROUP_DIAMOND_WALLS + SMOOTH_GROUP_WALLS + SMOOTH_GROUP_CLOSED_TURFS canSmoothWith = SMOOTH_GROUP_DIAMOND_WALLS @@ -72,7 +72,7 @@ base_icon_state = "sandstone_wall" sheet_type = /obj/item/stack/sheet/mineral/sandstone hardness = 50 //moh says this is apparently 6-7 on it's scale - explosion_block = 0 + explosive_resistance = 0 smoothing_flags = SMOOTH_BITMASK smoothing_groups = SMOOTH_GROUP_SANDSTONE_WALLS + SMOOTH_GROUP_WALLS + SMOOTH_GROUP_CLOSED_TURFS canSmoothWith = SMOOTH_GROUP_SANDSTONE_WALLS @@ -158,7 +158,7 @@ sheet_type = /obj/item/stack/sheet/mineral/wood hardness = 80 turf_flags = IS_SOLID - explosion_block = 0 + explosive_resistance = 0 smoothing_flags = SMOOTH_BITMASK smoothing_groups = SMOOTH_GROUP_WOOD_WALLS + SMOOTH_GROUP_WALLS + SMOOTH_GROUP_CLOSED_TURFS canSmoothWith = SMOOTH_GROUP_WOOD_WALLS @@ -215,7 +215,7 @@ base_icon_state = "snow_wall" smoothing_flags = SMOOTH_BITMASK hardness = 80 - explosion_block = 0 + explosive_resistance = 0 slicing_duration = 30 sheet_type = /obj/item/stack/sheet/mineral/snow canSmoothWith = null @@ -236,7 +236,7 @@ sheet_type = /obj/item/stack/sheet/mineral/abductor hardness = 10 slicing_duration = 200 //alien wall takes twice as much time to slice - explosion_block = 3 + explosive_resistance = 3 smoothing_flags = SMOOTH_BITMASK | SMOOTH_DIAGONAL_CORNERS smoothing_groups = SMOOTH_GROUP_ABDUCTOR_WALLS + SMOOTH_GROUP_WALLS + SMOOTH_GROUP_CLOSED_TURFS canSmoothWith = SMOOTH_GROUP_ABDUCTOR_WALLS @@ -250,7 +250,7 @@ icon = 'icons/turf/walls/shuttle_wall.dmi' icon_state = "shuttle_wall-0" base_icon_state = "shuttle_wall" - explosion_block = 3 + explosive_resistance = 3 flags_1 = CAN_BE_DIRTY_1 flags_ricochet = RICOCHET_SHINY | RICOCHET_HARD sheet_type = /obj/item/stack/sheet/mineral/titanium @@ -314,7 +314,7 @@ icon = 'icons/turf/walls/plastitanium_wall.dmi' icon_state = "plastitanium_wall-0" base_icon_state = "plastitanium_wall" - explosion_block = 4 + explosive_resistance = 4 sheet_type = /obj/item/stack/sheet/mineral/plastitanium hardness = 25 //upgrade on titanium smoothing_flags = SMOOTH_BITMASK | SMOOTH_DIAGONAL_CORNERS diff --git a/code/game/turfs/closed/wall/reinf_walls.dm b/code/game/turfs/closed/wall/reinf_walls.dm index f41e21be848..d496c7f438b 100644 --- a/code/game/turfs/closed/wall/reinf_walls.dm +++ b/code/game/turfs/closed/wall/reinf_walls.dm @@ -12,7 +12,7 @@ sheet_type = /obj/item/stack/sheet/plasteel sheet_amount = 1 girder_type = /obj/structure/girder/reinforced - explosion_block = 2 + explosive_resistance = 2 rad_insulation = RAD_HEAVY_INSULATION heat_capacity = 312500 //a little over 5 cm thick , 312500 for 1 m by 2.5 m by 0.25 m plasteel wall. also indicates the temperature at wich the wall will melt (currently only able to melt with H/E pipes) ///Dismantled state, related to deconstruction. @@ -233,7 +233,7 @@ icon = 'icons/turf/walls/plastitanium_wall.dmi' icon_state = "plastitanium_wall-0" base_icon_state = "plastitanium_wall" - explosion_block = 20 + explosive_resistance = 20 sheet_type = /obj/item/stack/sheet/mineral/plastitanium hardness = 25 //plastitanium turf_flags = IS_SOLID diff --git a/code/game/turfs/closed/walls.dm b/code/game/turfs/closed/walls.dm index 4528c21db05..c9363912add 100644 --- a/code/game/turfs/closed/walls.dm +++ b/code/game/turfs/closed/walls.dm @@ -6,7 +6,7 @@ icon = 'icons/turf/walls/wall.dmi' icon_state = "wall-0" base_icon_state = "wall" - explosion_block = 1 + explosive_resistance = 1 thermal_conductivity = WALL_HEAT_TRANSFER_COEFFICIENT heat_capacity = 62500 //a little over 5 cm thick , 62500 for 1 m by 2.5 m by 0.25 m iron wall. also indicates the temperature at wich the wall will melt (currently only able to melt with H/E pipes) @@ -266,7 +266,7 @@ return null /turf/closed/wall/acid_act(acidpwr, acid_volume) - if(explosion_block >= 2) + if(get_explosive_block() >= 2) acidpwr = min(acidpwr, 50) //we reduce the power so strong walls never get melted. return ..() diff --git a/code/game/turfs/open/space/transit.dm b/code/game/turfs/open/space/transit.dm index e0a9981a83d..74ba0e6fb0a 100644 --- a/code/game/turfs/open/space/transit.dm +++ b/code/game/turfs/open/space/transit.dm @@ -5,7 +5,7 @@ dir = SOUTH baseturfs = /turf/open/space/transit flags_1 = NOJAUNT //This line goes out to every wizard that ever managed to escape the den. I'm sorry. - explosion_block = INFINITY + explosive_resistance = INFINITY /turf/open/space/transit/Initialize(mapload) . = ..() diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm index 2da6be00cab..3ecadaed44a 100755 --- a/code/game/turfs/turf.dm +++ b/code/game/turfs/turf.dm @@ -92,6 +92,12 @@ GLOBAL_LIST_EMPTY(station_turfs) /// Sorry for the mess var/area/in_contents_of #endif + /// How much explosive resistance this turf is providing to itself + /// Defaults to -1, interpreted as initial(explosive_resistance) + /// This is an optimization to prevent turfs from needing to set these on init + /// This would either be expensive, or impossible to manage. Let's just avoid it yes? + /// Never directly access this, use get_explosive_block() instead + var/inherent_explosive_resistance = -1 /turf/vv_edit_var(var_name, new_value) var/static/list/banned_edits = list(NAMEOF_STATIC(src, x), NAMEOF_STATIC(src, y), NAMEOF_STATIC(src, z)) @@ -673,6 +679,26 @@ GLOBAL_LIST_EMPTY(station_turfs) continue movable_content.wash(clean_types) +/turf/set_density(new_value) + var/old_density = density + . = ..() + if(old_density == density) + return + + if(old_density) + explosive_resistance -= get_explosive_block() + if(density) + explosive_resistance += get_explosive_block() + +/// Wrapper around inherent_explosive_resistance +/// We assume this proc is cold, so we can move the "what is our block" into it +/turf/proc/get_explosive_block() + if(inherent_explosive_resistance != -1) + return inherent_explosive_resistance + if(explosive_resistance) + return initial(explosive_resistance) + return 0 + /** * Returns adjacent turfs to this turf that are reachable, in all cardinal directions * @@ -702,3 +728,9 @@ GLOBAL_LIST_EMPTY(station_turfs) /turf/proc/TakeTemperature(temp) temperature += temp + +// I'm sorry, this is the only way that both makes sense and is cheap +/turf/set_explosion_block(explosion_block) + explosive_resistance -= get_explosive_block() + inherent_explosive_resistance = explosion_block + explosive_resistance += get_explosive_block() diff --git a/code/modules/antagonists/blob/structures/core.dm b/code/modules/antagonists/blob/structures/core.dm index dd92eb6ff8b..e6f27779fbf 100644 --- a/code/modules/antagonists/blob/structures/core.dm +++ b/code/modules/antagonists/blob/structures/core.dm @@ -32,6 +32,7 @@ overmind.blobstrain.on_gain() update_appearance() AddComponent(/datum/component/stationloving, FALSE, TRUE) + AddElement(/datum/element/blocks_explosives) return ..() /obj/structure/blob/special/core/Destroy() diff --git a/code/modules/antagonists/blob/structures/shield.dm b/code/modules/antagonists/blob/structures/shield.dm index 479c40395d4..0699d70f3f2 100644 --- a/code/modules/antagonists/blob/structures/shield.dm +++ b/code/modules/antagonists/blob/structures/shield.dm @@ -16,6 +16,10 @@ fire = 90 acid = 90 +/obj/structure/blob/shield/Initialize(mapload, owner_overmind) + AddElement(/datum/element/blocks_explosives) + return ..() + /obj/structure/blob/shield/scannerreport() if(atmosblock) return "Will prevent the spread of atmospheric changes." diff --git a/code/modules/awaymissions/cordon.dm b/code/modules/awaymissions/cordon.dm index 9062aa05123..eaebbaca5a9 100644 --- a/code/modules/awaymissions/cordon.dm +++ b/code/modules/awaymissions/cordon.dm @@ -5,7 +5,7 @@ icon_state = "cordon" invisibility = INVISIBILITY_ABSTRACT mouse_opacity = MOUSE_OPACITY_TRANSPARENT - explosion_block = INFINITY + explosive_resistance = INFINITY rad_insulation = RAD_FULL_INSULATION opacity = TRUE density = TRUE diff --git a/code/modules/clothing/suits/jobs.dm b/code/modules/clothing/suits/jobs.dm index 3bdaac0aa5d..26a96193364 100644 --- a/code/modules/clothing/suits/jobs.dm +++ b/code/modules/clothing/suits/jobs.dm @@ -165,7 +165,6 @@ resistance_flags = NONE species_exception = list(/datum/species/golem) -// Lemom todo: and here /obj/item/clothing/suit/hazardvest/worn_overlays(mutable_appearance/standing, isinhands, icon_file) . = ..() if(!isinhands) diff --git a/code/modules/industrial_lift/tram_walls.dm b/code/modules/industrial_lift/tram_walls.dm index 85213b3364b..87188955533 100644 --- a/code/modules/industrial_lift/tram_walls.dm +++ b/code/modules/industrial_lift/tram_walls.dm @@ -26,6 +26,7 @@ var/slicing_duration = 100 /obj/structure/tramwall/Initialize(mapload) + AddElement(/datum/element/blocks_explosives) . = ..() var/obj/item/stack/initialized_mineral = new mineral set_custom_materials(initialized_mineral.mats_per_unit, mineral_amount) diff --git a/code/modules/mapfluff/ruins/spaceruin_code/hilbertshotel.dm b/code/modules/mapfluff/ruins/spaceruin_code/hilbertshotel.dm index a6ea1743485..3835431a331 100644 --- a/code/modules/mapfluff/ruins/spaceruin_code/hilbertshotel.dm +++ b/code/modules/mapfluff/ruins/spaceruin_code/hilbertshotel.dm @@ -230,7 +230,7 @@ GLOBAL_VAR_INIT(hhMysteryRoomNumber, rand(1, 999999)) icon_state = "hotelwall" smoothing_groups = SMOOTH_GROUP_CLOSED_TURFS + SMOOTH_GROUP_HOTEL_WALLS canSmoothWith = SMOOTH_GROUP_HOTEL_WALLS - explosion_block = INFINITY + explosive_resistance = INFINITY /turf/open/indestructible/hotelwood desc = "Stylish dark wood with extra reinforcement. Secured firmly to the floor to prevent tampering." @@ -250,7 +250,7 @@ GLOBAL_VAR_INIT(hhMysteryRoomNumber, rand(1, 999999)) base_icon_state = "bluespace" baseturfs = /turf/open/space/bluespace flags_1 = NOJAUNT - explosion_block = INFINITY + explosive_resistance = INFINITY var/obj/item/hilbertshotel/parentSphere /turf/open/space/bluespace/Initialize(mapload) @@ -269,7 +269,7 @@ GLOBAL_VAR_INIT(hhMysteryRoomNumber, rand(1, 999999)) /turf/closed/indestructible/hoteldoor name = "Hotel Door" icon_state = "hoteldoor" - explosion_block = INFINITY + explosive_resistance = INFINITY var/obj/item/hilbertshotel/parentSphere /turf/closed/indestructible/hoteldoor/proc/promptExit(mob/living/user) diff --git a/code/modules/projectiles/guns/special/blastcannon.dm b/code/modules/projectiles/guns/special/blastcannon.dm index a255a2dfabf..6fda91369a4 100644 --- a/code/modules/projectiles/guns/special/blastcannon.dm +++ b/code/modules/projectiles/guns/special/blastcannon.dm @@ -322,13 +322,7 @@ var/decrement = 1 var/atom/location = loc if (reactionary) - if(location.density || !isturf(location)) - decrement += location.explosion_block - for(var/obj/thing in location) - if (thing == src) - continue - var/the_block = thing.explosion_block - decrement += the_block == EXPLOSION_BLOCK_PROC ? thing.GetExplosionBlock() : the_block + decrement += location.explosive_resistance range = max(range - decrement + 1, 0) // Already decremented by 1 in the parent. Exists so that if we pass through something with negative block it extends the range. heavy_ex_range = max(heavy_ex_range - decrement, 0) diff --git a/tgstation.dme b/tgstation.dme index 5ad9e201cc6..36102027bf1 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -1079,6 +1079,7 @@ #include "code\datums\elements\beauty.dm" #include "code\datums\elements\bed_tucking.dm" #include "code\datums\elements\befriend_petting.dm" +#include "code\datums\elements\blocks_explosives.dm" #include "code\datums\elements\bsa_blocker.dm" #include "code\datums\elements\bump_click.dm" #include "code\datums\elements\can_barricade.dm"