diff --git a/code/__DEFINES/is_helpers.dm b/code/__DEFINES/is_helpers.dm index 896d7f0a0a4..12c50ab9b15 100644 --- a/code/__DEFINES/is_helpers.dm +++ b/code/__DEFINES/is_helpers.dm @@ -255,7 +255,7 @@ GLOBAL_LIST_INIT(glass_sheet_types, typecacheof(list( #define isblobmonster(O) (istype(O, /mob/living/simple_animal/hostile/blob)) -#define isshuttleturf(T) (length(T.baseturfs) && (/turf/baseturf_skipover/shuttle in T.baseturfs)) +#define isshuttleturf(T) (!isnull(T.depth_to_find_baseturf(/turf/baseturf_skipover/shuttle))) #define isProbablyWallMounted(O) (O.pixel_x > 20 || O.pixel_x < -20 || O.pixel_y > 20 || O.pixel_y < -20) #define isbook(O) (is_type_in_typecache(O, GLOB.book_types)) diff --git a/code/__DEFINES/stat_tracking.dm b/code/__DEFINES/stat_tracking.dm index 25eb910a9d1..810a313ac31 100644 --- a/code/__DEFINES/stat_tracking.dm +++ b/code/__DEFINES/stat_tracking.dm @@ -44,6 +44,10 @@ #define SET_COST_LINE(...) SET_COST("[__LINE__]") +/// A quick helper for running the code as a statement and profiling its cost. +/// For example, `SET_COST_STMT(var/x = do_work())` +#define SET_COST_STMT(code...) ##code; SET_COST("[__LINE__] - [#code]") + #define EXPORT_STATS_TO_JSON_LATER(filename, costs, counts) EXPORT_STATS_TO_FILE_LATER(filename, costs, counts, stat_tracking_export_to_json_later) #define EXPORT_STATS_TO_CSV_LATER(filename, costs, counts) EXPORT_STATS_TO_FILE_LATER(filename, costs, counts, stat_tracking_export_to_csv_later) diff --git a/code/datums/shuttles.dm b/code/datums/shuttles.dm index fb866cf9baf..7ad85086289 100644 --- a/code/datums/shuttles.dm +++ b/code/datums/shuttles.dm @@ -60,11 +60,11 @@ var/turf/place = turfs[i] if(isspaceturf(place)) // This assumes all shuttles are loaded in a single spot then moved to their real destination. continue - if(length(place.baseturfs) < 2) // Some snowflake shuttle shit + + if (place.count_baseturfs() < 2) // Some snowflake shuttle shit continue - var/list/sanity = place.baseturfs.Copy() - sanity.Insert(3, /turf/baseturf_skipover/shuttle) - place.baseturfs = baseturfs_string_list(sanity, place) + + place.insert_baseturf(3, /turf/baseturf_skipover/shuttle) for(var/obj/docking_port/mobile/port in place) port.calculate_docking_port_information(src) diff --git a/code/game/turfs/baseturfs.dm b/code/game/turfs/baseturfs.dm new file mode 100644 index 00000000000..b7e63c7d23d --- /dev/null +++ b/code/game/turfs/baseturfs.dm @@ -0,0 +1,189 @@ +/// Take off the top layer turf and replace it with the next baseturf down +/turf/proc/ScrapeAway(amount=1, flags) + if(!amount) + return + if(length(baseturfs)) + var/list/new_baseturfs = baseturfs.Copy() + var/turf_type = new_baseturfs[max(1, new_baseturfs.len - amount + 1)] + while(ispath(turf_type, /turf/baseturf_skipover)) + amount++ + if(amount > new_baseturfs.len) + CRASH("The bottommost baseturf of a turf is a skipover [src]([type])") + turf_type = new_baseturfs[max(1, new_baseturfs.len - amount + 1)] + new_baseturfs.len -= min(amount, new_baseturfs.len - 1) // No removing the very bottom + if(new_baseturfs.len == 1) + new_baseturfs = new_baseturfs[1] + return ChangeTurf(turf_type, new_baseturfs, flags) + + if(baseturfs == type) + return src + + return ChangeTurf(baseturfs, baseturfs, flags) // The bottom baseturf will never go away + +// Take the input as baseturfs and put it underneath the current baseturfs +// If fake_turf_type is provided and new_baseturfs is not the baseturfs list will be created identical to the turf type's +// If both or just new_baseturfs is provided they will be inserted below the existing baseturfs +/turf/proc/PlaceOnBottom(list/new_baseturfs, turf/fake_turf_type) + if(fake_turf_type) + if(!new_baseturfs) + if(!length(baseturfs)) + baseturfs = list(baseturfs) + var/list/old_baseturfs = baseturfs.Copy() + assemble_baseturfs(fake_turf_type) + if(!length(baseturfs)) + baseturfs = list(baseturfs) + baseturfs = baseturfs_string_list((baseturfs - (baseturfs & GLOB.blacklisted_automated_baseturfs)) + old_baseturfs, src) + return + else if(!length(new_baseturfs)) + new_baseturfs = list(new_baseturfs, fake_turf_type) + else + new_baseturfs += fake_turf_type + if(!length(baseturfs)) + baseturfs = list(baseturfs) + baseturfs = baseturfs_string_list(new_baseturfs + baseturfs, src) + +// Make a new turf and put it on top +// The args behave identical to PlaceOnBottom except they go on top +// Things placed on top of closed turfs will ignore the topmost closed turf +// Returns the new turf +/turf/proc/PlaceOnTop(list/new_baseturfs, turf/fake_turf_type, flags) + var/area/turf_area = loc + if(new_baseturfs && !length(new_baseturfs)) + new_baseturfs = list(new_baseturfs) + flags = turf_area.PlaceOnTopReact(new_baseturfs, fake_turf_type, flags) // A hook so areas can modify the incoming args + + var/turf/newT + if(flags & CHANGETURF_SKIP) // We haven't been initialized + if(flags_1 & INITIALIZED_1) + stack_trace("CHANGETURF_SKIP was used in a PlaceOnTop call for a turf that's initialized. This is a mistake. [src]([type])") + assemble_baseturfs() + if(fake_turf_type) + if(!new_baseturfs) // If no baseturfs list then we want to create one from the turf type + if(!length(baseturfs)) + baseturfs = list(baseturfs) + var/list/old_baseturfs = baseturfs.Copy() + if(!isclosedturf(src)) + old_baseturfs += type + newT = ChangeTurf(fake_turf_type, null, flags) + newT.assemble_baseturfs(initial(fake_turf_type.baseturfs)) // The baseturfs list is created like roundstart + if(!length(newT.baseturfs)) + newT.baseturfs = list(baseturfs) + // The old baseturfs are put underneath, and we sort out the unwanted ones + newT.baseturfs = baseturfs_string_list(old_baseturfs + (newT.baseturfs - GLOB.blacklisted_automated_baseturfs), newT) + return newT + if(!length(baseturfs)) + baseturfs = list(baseturfs) + if(!isclosedturf(src)) + new_baseturfs = list(type) + new_baseturfs + baseturfs = baseturfs_string_list(baseturfs + new_baseturfs, src) + return ChangeTurf(fake_turf_type, null, flags) + if(!length(baseturfs)) + baseturfs = list(baseturfs) + if(!isclosedturf(src)) + baseturfs = baseturfs_string_list(baseturfs + type, src) + var/turf/change_type + if(length(new_baseturfs)) + change_type = new_baseturfs[new_baseturfs.len] + new_baseturfs.len-- + if(new_baseturfs.len) + baseturfs = baseturfs_string_list(baseturfs + new_baseturfs, src) + else + change_type = new_baseturfs + return ChangeTurf(change_type, null, flags) + +// Copy an existing turf and put it on top +// Returns the new turf +/turf/proc/CopyOnTop(turf/copytarget, ignore_bottom=1, depth=INFINITY, copy_air = FALSE) + var/list/new_baseturfs = list() + new_baseturfs += baseturfs + new_baseturfs += type + + if(depth) + var/list/target_baseturfs + if(length(copytarget.baseturfs)) + // with default inputs this would be Copy(clamp(2, -INFINITY, baseturfs.len)) + // Don't forget a lower index is lower in the baseturfs stack, the bottom is baseturfs[1] + target_baseturfs = copytarget.baseturfs.Copy(clamp(1 + ignore_bottom, 1 + copytarget.baseturfs.len - depth, copytarget.baseturfs.len)) + else if(!ignore_bottom) + target_baseturfs = list(copytarget.baseturfs) + if(target_baseturfs) + target_baseturfs -= new_baseturfs & GLOB.blacklisted_automated_baseturfs + new_baseturfs += target_baseturfs + + var/turf/newT = copytarget.copyTurf(src, copy_air) + newT.baseturfs = baseturfs_string_list(new_baseturfs, newT) + return newT + +/// Tries to find the given type in baseturfs. +/// If found, returns how deep it is for use in other baseturf procs, or null if it cannot be found. +/// For example, this number can be passed into ScrapeAway to scrape everything until that point. +/turf/proc/depth_to_find_baseturf(baseturf_type) + var/index = baseturfs.Find(baseturf_type) + if (index == 0) + return null + return baseturfs.len - index + 1 + +/// Returns the baseturf at the given depth. +/// For example, baseturf_at_depth(1) will give the baseturf that would show up when scraping once. +/turf/proc/baseturf_at_depth(index) + TEST_ONLY_ASSERT(isnum(index), "baseturf_at_depth must be given a number, received [index]") + if (islist(baseturfs)) + return LAZYACCESS(baseturfs, baseturfs.len - index + 1) + else if (index == 1) + return baseturfs + else + return null + +/// Replaces all instances of needle_type in baseturfs with replacement_type +/turf/proc/replace_baseturf(needle_type, replacement_type) + if (islist(baseturfs)) + var/list/new_baseturfs + + while (TRUE) + var/found_index = baseturfs.Find(needle_type) + if (found_index == 0) + break + + new_baseturfs ||= baseturfs.Copy() + new_baseturfs[found_index] = replacement_type + + if (!isnull(new_baseturfs)) + baseturfs = baseturfs_string_list(new_baseturfs, src) + else if (baseturfs == needle_type) + baseturfs = replacement_type + +/// Removes all baseturfs that are found in the given typecache. +/turf/proc/remove_baseturfs_from_typecache(list/typecache) + if (islist(baseturfs)) + var/list/new_baseturfs + + for (var/baseturf in baseturfs) + if (!typecache[baseturf]) + continue + + new_baseturfs ||= baseturfs.Copy() + new_baseturfs -= baseturf + + if (!isnull(new_baseturfs)) + baseturfs = baseturfs_string_list(new_baseturfs, src) + else if (typecache[baseturfs]) + baseturfs = /turf/baseturf_bottom + +/// Returns the total number of baseturfs +/turf/proc/count_baseturfs() + return islist(baseturfs) ? length(baseturfs) : 1 + +/// Inserts a baseturf at the given level. +/// "Level" here doesn't mean depth. +/// For example, `insert_baseturf(2, /turf/open/floor/plating)` will make it so +/// the 2nd to last turf in the list is plating. +/// This is different from *depth*, since depth is the level from the top. +/turf/proc/insert_baseturf(level, turf_type) + if (!islist(baseturfs)) + assemble_baseturfs() + if(!islist(baseturfs)) + baseturfs = list(baseturfs) + + var/list/baseturfs_copy = baseturfs.Copy() + baseturfs_copy.Insert(level, turf_type) + baseturfs = baseturfs_string_list(baseturfs_copy, src) diff --git a/code/game/turfs/change_turf.dm b/code/game/turfs/change_turf.dm index 30a651b7e05..59c94fe6a19 100644 --- a/code/game/turfs/change_turf.dm +++ b/code/game/turfs/change_turf.dm @@ -228,123 +228,6 @@ GLOBAL_LIST_INIT(blacklisted_automated_baseturfs, typecacheof(list( flags |= CHANGETURF_RECALC_ADJACENT return ..() -/// Take off the top layer turf and replace it with the next baseturf down -/turf/proc/ScrapeAway(amount=1, flags) - if(!amount) - return - if(length(baseturfs)) - var/list/new_baseturfs = baseturfs.Copy() - var/turf_type = new_baseturfs[max(1, new_baseturfs.len - amount + 1)] - while(ispath(turf_type, /turf/baseturf_skipover)) - amount++ - if(amount > new_baseturfs.len) - CRASH("The bottommost baseturf of a turf is a skipover [src]([type])") - turf_type = new_baseturfs[max(1, new_baseturfs.len - amount + 1)] - new_baseturfs.len -= min(amount, new_baseturfs.len - 1) // No removing the very bottom - if(new_baseturfs.len == 1) - new_baseturfs = new_baseturfs[1] - return ChangeTurf(turf_type, new_baseturfs, flags) - - if(baseturfs == type) - return src - - return ChangeTurf(baseturfs, baseturfs, flags) // The bottom baseturf will never go away - -// Take the input as baseturfs and put it underneath the current baseturfs -// If fake_turf_type is provided and new_baseturfs is not the baseturfs list will be created identical to the turf type's -// If both or just new_baseturfs is provided they will be inserted below the existing baseturfs -/turf/proc/PlaceOnBottom(list/new_baseturfs, turf/fake_turf_type) - if(fake_turf_type) - if(!new_baseturfs) - if(!length(baseturfs)) - baseturfs = list(baseturfs) - var/list/old_baseturfs = baseturfs.Copy() - assemble_baseturfs(fake_turf_type) - if(!length(baseturfs)) - baseturfs = list(baseturfs) - baseturfs = baseturfs_string_list((baseturfs - (baseturfs & GLOB.blacklisted_automated_baseturfs)) + old_baseturfs, src) - return - else if(!length(new_baseturfs)) - new_baseturfs = list(new_baseturfs, fake_turf_type) - else - new_baseturfs += fake_turf_type - if(!length(baseturfs)) - baseturfs = list(baseturfs) - baseturfs = baseturfs_string_list(new_baseturfs + baseturfs, src) - -// Make a new turf and put it on top -// The args behave identical to PlaceOnBottom except they go on top -// Things placed on top of closed turfs will ignore the topmost closed turf -// Returns the new turf -/turf/proc/PlaceOnTop(list/new_baseturfs, turf/fake_turf_type, flags) - var/area/turf_area = loc - if(new_baseturfs && !length(new_baseturfs)) - new_baseturfs = list(new_baseturfs) - flags = turf_area.PlaceOnTopReact(new_baseturfs, fake_turf_type, flags) // A hook so areas can modify the incoming args - - var/turf/new_turf - if(flags & CHANGETURF_SKIP) // We haven't been initialized - if(flags_1 & INITIALIZED_1) - stack_trace("CHANGETURF_SKIP was used in a PlaceOnTop call for a turf that's initialized. This is a mistake. [src]([type])") - assemble_baseturfs() - if(fake_turf_type) - if(!new_baseturfs) // If no baseturfs list then we want to create one from the turf type - if(!length(baseturfs)) - baseturfs = list(baseturfs) - var/list/old_baseturfs = baseturfs.Copy() - if(!isclosedturf(src)) - old_baseturfs += type - new_turf = ChangeTurf(fake_turf_type, null, flags) - new_turf.assemble_baseturfs(initial(fake_turf_type.baseturfs)) // The baseturfs list is created like roundstart - if(!length(new_turf.baseturfs)) - new_turf.baseturfs = list(baseturfs) - // The old baseturfs are put underneath, and we sort out the unwanted ones - new_turf.baseturfs = baseturfs_string_list(old_baseturfs + (new_turf.baseturfs - GLOB.blacklisted_automated_baseturfs), new_turf) - return new_turf - if(!length(baseturfs)) - baseturfs = list(baseturfs) - if(!isclosedturf(src)) - new_baseturfs = list(type) + new_baseturfs - baseturfs = baseturfs_string_list(baseturfs + new_baseturfs, src) - return ChangeTurf(fake_turf_type, null, flags) - if(!length(baseturfs)) - baseturfs = list(baseturfs) - if(!isclosedturf(src)) - baseturfs = baseturfs_string_list(baseturfs + type, src) - var/turf/change_type - if(length(new_baseturfs)) - change_type = new_baseturfs[new_baseturfs.len] - new_baseturfs.len-- - if(new_baseturfs.len) - baseturfs = baseturfs_string_list(baseturfs + new_baseturfs, src) - else - change_type = new_baseturfs - return ChangeTurf(change_type, null, flags) - -// Copy an existing turf and put it on top -// Returns the new turf -/turf/proc/CopyOnTop(turf/copytarget, ignore_bottom=1, depth=INFINITY, copy_air = FALSE) - var/list/new_baseturfs = list() - new_baseturfs += baseturfs - new_baseturfs += type - - if(depth) - var/list/target_baseturfs - if(length(copytarget.baseturfs)) - // with default inputs this would be Copy(clamp(2, -INFINITY, baseturfs.len)) - // Don't forget a lower index is lower in the baseturfs stack, the bottom is baseturfs[1] - target_baseturfs = copytarget.baseturfs.Copy(clamp(1 + ignore_bottom, 1 + copytarget.baseturfs.len - depth, copytarget.baseturfs.len)) - else if(!ignore_bottom) - target_baseturfs = list(copytarget.baseturfs) - if(target_baseturfs) - target_baseturfs -= new_baseturfs & GLOB.blacklisted_automated_baseturfs - new_baseturfs += target_baseturfs - - var/turf/new_turf = copytarget.copyTurf(src, copy_air) - new_turf.baseturfs = baseturfs_string_list(new_baseturfs, new_turf) - return new_turf - - //If you modify this function, ensure it works correctly with lateloaded map templates. /turf/proc/AfterChange(flags, oldType) //called after a turf has been replaced in ChangeTurf() levelupdate() diff --git a/code/game/turfs/closed/minerals.dm b/code/game/turfs/closed/minerals.dm index 180516e6770..bdec8907d63 100644 --- a/code/game/turfs/closed/minerals.dm +++ b/code/game/turfs/closed/minerals.dm @@ -230,7 +230,6 @@ var/turf/T = ChangeTurf(path,null,CHANGETURF_IGNORE_AIR) T.flags_1 |= stored_flags - T.baseturfs = src.baseturfs T.color = stored_color //SKYRAT EDIT ADDITION if(ismineralturf(T)) var/turf/closed/mineral/M = T diff --git a/code/game/turfs/open/floor.dm b/code/game/turfs/open/floor.dm index 9b1d260af7a..be53db1da6b 100644 --- a/code/game/turfs/open/floor.dm +++ b/code/game/turfs/open/floor.dm @@ -84,7 +84,7 @@ if(EXPLODE_HEAVY) switch(rand(1, 3)) if(1) - if(!length(baseturfs) || !ispath(baseturfs[baseturfs.len-1], /turf/open/floor)) + if (!ispath(baseturf_at_depth(2), /turf/open/floor)) attempt_lattice_replacement() else ScrapeAway(2, flags = CHANGETURF_INHERIT_AIR) @@ -237,8 +237,6 @@ if(STAGE_FIVE to INFINITY) if(prob(70)) sheer = TRUE - else if(prob(50) && (/turf/open/space in baseturfs)) - attempt_lattice_replacement() if(sheer) if(has_tile()) remove_tile(null, TRUE, TRUE, TRUE) diff --git a/code/game/turfs/open/floor/reinforced_floor.dm b/code/game/turfs/open/floor/reinforced_floor.dm index 8f823a900cb..3d70823a3a1 100644 --- a/code/game/turfs/open/floor/reinforced_floor.dm +++ b/code/game/turfs/open/floor/reinforced_floor.dm @@ -64,7 +64,7 @@ switch(severity) if(EXPLODE_DEVASTATE) if(prob(80)) - if(!length(baseturfs) || !ispath(baseturfs[baseturfs.len-1], /turf/open/floor)) + if (!ispath(baseturf_at_depth(2), /turf/open/floor)) attempt_lattice_replacement() else ScrapeAway(2, flags = CHANGETURF_INHERIT_AIR) diff --git a/code/game/turfs/open/river.dm b/code/game/turfs/open/river.dm index 185f9c91eff..5171d840821 100644 --- a/code/game/turfs/open/river.dm +++ b/code/game/turfs/open/river.dm @@ -4,11 +4,10 @@ #define RANDOM_LOWER_X 50 #define RANDOM_LOWER_Y 50 -/proc/spawn_rivers(target_z, nodes = 4, turf_type = /turf/open/lava/smooth/lava_land_surface, whitelist_area = /area/lavaland/surface/outdoors/unexplored, min_x = RANDOM_LOWER_X, min_y = RANDOM_LOWER_Y, max_x = RANDOM_UPPER_X, max_y = RANDOM_UPPER_Y, new_baseturfs) +/proc/spawn_rivers(target_z, nodes = 4, turf_type = /turf/open/lava/smooth/lava_land_surface, whitelist_area = /area/lavaland/surface/outdoors/unexplored, min_x = RANDOM_LOWER_X, min_y = RANDOM_LOWER_Y, max_x = RANDOM_UPPER_X, max_y = RANDOM_UPPER_Y) var/list/river_nodes = list() var/num_spawned = 0 var/list/possible_locs = block(locate(min_x, min_y, target_z), locate(max_x, max_y, target_z)) - new_baseturfs = baseturfs_string_list(new_baseturfs, pick(possible_locs)) while(num_spawned < nodes && possible_locs.len) var/turf/T = pick(possible_locs) var/area/A = get_area(T) @@ -27,8 +26,6 @@ // Workaround around ChangeTurf that's safe because of when this proc is called var/turf/cur_turf = get_turf(W) cur_turf = new turf_type(cur_turf) - if(new_baseturfs) - cur_turf.baseturfs = new_baseturfs var/turf/target_turf = get_turf(pick(river_nodes - W)) if(!target_turf) break @@ -59,8 +56,6 @@ else // Workaround around ChangeTurf that's safe because of when this proc is called var/turf/river_turf = new turf_type(cur_turf) - if(new_baseturfs) - river_turf.baseturfs = new_baseturfs river_turf.Spread(25, 11, whitelist_area) for(var/WP in river_nodes) diff --git a/code/modules/holodeck/computer.dm b/code/modules/holodeck/computer.dm index bd3e57a879d..2f6b1d1053f 100644 --- a/code/modules/holodeck/computer.dm +++ b/code/modules/holodeck/computer.dm @@ -261,12 +261,7 @@ GLOBAL_LIST_INIT(typecache_holodeck_linked_floorcheck_ok, typecacheof(list(/turf //makes sure that any time a holoturf is inside a baseturf list (e.g. if someone put a wall over it) its set to the OFFLINE turf //so that you cant bring turfs from previous programs into other ones (like putting the plasma burn turf into lounge for example) for(var/turf/closed/holo_turf in linked) - for(var/baseturf in holo_turf.baseturfs) - if(ispath(baseturf, /turf/open/floor/holofloor)) - var/list/copy = holo_turf.baseturfs.Copy() - copy -= baseturf - copy += /turf/open/floor/holofloor/plating - holo_turf.baseturfs = baseturfs_string_list(copy, holo_turf) + holo_turf.replace_baseturf(/turf/open/floor/holofloor, /turf/open/floor/holofloor/plating) ///finalizes objects in the spawned list /obj/machinery/computer/holodeck/proc/finish_spawn() diff --git a/code/modules/mapping/mapping_helpers.dm b/code/modules/mapping/mapping_helpers.dm index b2f40ba68fc..60bd0c9761e 100644 --- a/code/modules/mapping/mapping_helpers.dm +++ b/code/modules/mapping/mapping_helpers.dm @@ -34,22 +34,8 @@ qdel(src) /obj/effect/baseturf_helper/proc/replace_baseturf(turf/thing) - if(length(thing.baseturfs)) - var/list/baseturf_cache = thing.baseturfs.Copy() - for(var/i in baseturf_cache) - if(baseturf_to_replace[i]) - baseturf_cache -= i - thing.baseturfs = baseturfs_string_list(baseturf_cache, thing) - if(!baseturf_cache.len) - thing.assemble_baseturfs(baseturf) - else - thing.PlaceOnBottom(null, baseturf) - else if(baseturf_to_replace[thing.baseturfs]) - thing.assemble_baseturfs(baseturf) - else - thing.PlaceOnBottom(null, baseturf) - - + thing.remove_baseturfs_from_typecache(baseturf_to_replace) + thing.PlaceOnBottom(fake_turf_type = baseturf) /obj/effect/baseturf_helper/space name = "space baseturf editor" diff --git a/code/modules/shuttle/on_move.dm b/code/modules/shuttle/on_move.dm index 246385971f1..15bebcf98cd 100644 --- a/code/modules/shuttle/on_move.dm +++ b/code/modules/shuttle/on_move.dm @@ -52,11 +52,10 @@ All ShuttleMove procs go here return // Destination turf changes. // Baseturfs is definitely a list or this proc wouldnt be called. - var/shuttle_boundary = baseturfs.Find(/turf/baseturf_skipover/shuttle) + var/shuttle_depth = depth_to_find_baseturf(/turf/baseturf_skipover/shuttle) - if(!shuttle_boundary) + if(!shuttle_depth) CRASH("A turf queued to move via shuttle somehow had no skipover in baseturfs. [src]([type]):[loc]") - var/depth = baseturfs.len - shuttle_boundary + 1 //SKYRAT EDIT ADDITION if(newT.lgroup) @@ -73,7 +72,8 @@ All ShuttleMove procs go here liquids.ChangeToNewTurf(newT) newT.reasses_liquids() //SKYRAT EDIT END - newT.CopyOnTop(src, 1, depth, TRUE) + + newT.CopyOnTop(src, 1, shuttle_depth, TRUE) newT.blocks_air = TRUE newT.air_update_turf(TRUE, FALSE) blocks_air = TRUE @@ -91,10 +91,10 @@ All ShuttleMove procs go here oldT.TransferComponents(src) SSexplosions.wipe_turf(src) - var/shuttle_boundary = baseturfs.Find(/turf/baseturf_skipover/shuttle) + var/shuttle_depth = depth_to_find_baseturf(/turf/baseturf_skipover/shuttle) - if(shuttle_boundary) - oldT.ScrapeAway(baseturfs.len - shuttle_boundary + 1) + if(shuttle_depth) + oldT.ScrapeAway(shuttle_depth) if(rotation) shuttleRotate(rotation) //see shuttle_rotate.dm diff --git a/code/modules/shuttle/shuttle.dm b/code/modules/shuttle/shuttle.dm index c94c5996c0a..529e896ca39 100644 --- a/code/modules/shuttle/shuttle.dm +++ b/code/modules/shuttle/shuttle.dm @@ -766,11 +766,9 @@ oldT.empty(FALSE) // Here we locate the bottommost shuttle boundary and remove all turfs above it - var/list/baseturf_cache = oldT.baseturfs - for(var/k in 1 to length(baseturf_cache)) - if(ispath(baseturf_cache[k], /turf/baseturf_skipover/shuttle)) - oldT.ScrapeAway(baseturf_cache.len - k + 1) - break + var/shuttle_tile_depth = oldT.depth_to_find_baseturf(/turf/baseturf_skipover/shuttle) + if (!isnull(shuttle_tile_depth)) + oldT.ScrapeAway(shuttle_tile_depth) qdel(src, force=TRUE) @@ -812,8 +810,6 @@ var/turf/T1 = L1[i] if(!T0 || !T1) continue // out of bounds - if(T0.type == T0.baseturfs) - continue // indestructible if(!istype(T0.loc, area_type) || istype(T0.loc, /area/shuttle/transit)) continue // not part of the shuttle ripple_turfs += T1 diff --git a/code/modules/unit_tests/_unit_tests.dm b/code/modules/unit_tests/_unit_tests.dm index 5760a886e4a..c845423351b 100644 --- a/code/modules/unit_tests/_unit_tests.dm +++ b/code/modules/unit_tests/_unit_tests.dm @@ -85,6 +85,7 @@ #include "area_contents.dm" #include "autowiki.dm" #include "barsigns.dm" +#include "baseturfs.dm" #include "bespoke_id.dm" #include "binary_insert.dm" #include "bloody_footprints.dm" diff --git a/code/modules/unit_tests/baseturfs.dm b/code/modules/unit_tests/baseturfs.dm new file mode 100644 index 00000000000..99ad3de6c9d --- /dev/null +++ b/code/modules/unit_tests/baseturfs.dm @@ -0,0 +1,69 @@ +#define EXPECTED_FLOOR_TYPE /turf/open/floor/iron + +/// Validates that unmodified baseturfs tear down properly +/datum/unit_test/baseturfs_unmodified_scrape + +/datum/unit_test/baseturfs_unmodified_scrape/Run() + // What this is specifically doesn't matter, just as long as the test is built for it + TEST_ASSERT_EQUAL(run_loc_floor_bottom_left.type, EXPECTED_FLOOR_TYPE, "run_loc_floor_bottom_left should be an iron floor") + + // Do this instead of ChangeTurf to guarantee that baseturfs is completely default on-init behavior + new EXPECTED_FLOOR_TYPE(run_loc_floor_bottom_left) + + run_loc_floor_bottom_left.ScrapeAway() + TEST_ASSERT_EQUAL(run_loc_floor_bottom_left.type, /turf/open/floor/plating, "Iron floors should scrape away to plating") + + run_loc_floor_bottom_left.ScrapeAway() + TEST_ASSERT_EQUAL(run_loc_floor_bottom_left.type, /turf/open/space, "Plating should scrape away to space") + + run_loc_floor_bottom_left.ScrapeAway() + TEST_ASSERT_EQUAL(run_loc_floor_bottom_left.type, /turf/open/space, "Space should scrape away to space") + +/datum/unit_test/baseturfs_unmodified_scrape/Destroy() + new EXPECTED_FLOOR_TYPE(run_loc_floor_bottom_left) + return ..() + +/// Validates that specially placed baseturfs tear down properly +/datum/unit_test/baseturfs_placed_on_top + +/datum/unit_test/baseturfs_placed_on_top/Run() + TEST_ASSERT_EQUAL(run_loc_floor_bottom_left.type, EXPECTED_FLOOR_TYPE, "run_loc_floor_bottom_left should be an iron floor") + + // Do this instead of ChangeTurf to guarantee that baseturfs is completely default on-init behavior + new EXPECTED_FLOOR_TYPE(run_loc_floor_bottom_left) + + run_loc_floor_bottom_left.PlaceOnTop(/turf/closed/wall/rock) + TEST_ASSERT_EQUAL(run_loc_floor_bottom_left.type, /turf/closed/wall/rock, "Rock wall should've been placed on top") + + run_loc_floor_bottom_left.ScrapeAway() + TEST_ASSERT_EQUAL(run_loc_floor_bottom_left.type, EXPECTED_FLOOR_TYPE, "Rock wall should've been scraped off, back into the expected type") + +/// Validates that specially placed baseturfs BELOW tear down properly +/datum/unit_test/baseturfs_placed_on_bottom + +/datum/unit_test/baseturfs_placed_on_bottom/Run() + TEST_ASSERT_EQUAL(run_loc_floor_bottom_left.type, EXPECTED_FLOOR_TYPE, "run_loc_floor_bottom_left should be an iron floor") + + // Do this instead of ChangeTurf to guarantee that baseturfs is completely default on-init behavior + new EXPECTED_FLOOR_TYPE(run_loc_floor_bottom_left) + + run_loc_floor_bottom_left.PlaceOnBottom(fake_turf_type = /turf/closed/wall/rock) + TEST_ASSERT_EQUAL(run_loc_floor_bottom_left.type, EXPECTED_FLOOR_TYPE, "PlaceOnBottom shouldn't have changed turf") + + run_loc_floor_bottom_left.ScrapeAway() + TEST_ASSERT_EQUAL(run_loc_floor_bottom_left.type, /turf/open/floor/plating, "Iron floors should scrape away to plating") + + run_loc_floor_bottom_left.ScrapeAway() + TEST_ASSERT_EQUAL(run_loc_floor_bottom_left.type, /turf/open/space, "Plating should've scraped off to space") + + run_loc_floor_bottom_left.ScrapeAway() + TEST_ASSERT_EQUAL(run_loc_floor_bottom_left.type, /turf/closed/wall/rock, "Space should've scraped down to a rock wall") + + run_loc_floor_bottom_left.ScrapeAway() + TEST_ASSERT_EQUAL(run_loc_floor_bottom_left.type, /turf/open/floor/plating, "Rock wall should've scraped down back to plating (because it's a wall)") + +/datum/unit_test/baseturfs_placed_on_bottom/Destroy() + new EXPECTED_FLOOR_TYPE(run_loc_floor_bottom_left) + return ..() + +#undef EXPECTED_FLOOR_TYPE diff --git a/tgstation.dme b/tgstation.dme index 8b462461698..c4d6f0b0bd6 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -2175,6 +2175,7 @@ #include "code\game\objects\structures\transit_tubes\transit_tube_construction.dm" #include "code\game\objects\structures\transit_tubes\transit_tube_pod.dm" #include "code\game\turfs\baseturf_skipover.dm" +#include "code\game\turfs\baseturfs.dm" #include "code\game\turfs\change_turf.dm" #include "code\game\turfs\turf.dm" #include "code\game\turfs\closed\_closed.dm"