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"