From c44843b738db839e1d11fbad8b045d89efa748ac Mon Sep 17 00:00:00 2001 From: Tastyfish Date: Thu, 12 Jan 2023 15:18:28 -0500 Subject: [PATCH] [MANUAL MIRROR] Abstract away stuff that acts on baseturfs directly into their own procs, and kills some dead code related to baseturfs + tests (#72117) (#18655) Abstract away stuff that acts on baseturfs directly into their own procs, and kills some dead code related to baseturfs + tests (#72117) Adds some new procs relating to baseturfs that replaces some code that reads and sets them directly. Moves them to their own file. **To reviewers: Any proc in baseturfs.dm that is snake_case is mine, anything else is just moved**. Adds tests for the existing procs of baseturfs. I'm going to be doing some optimizations to baseturfs that change the actual representation of baseturfs, and so I'm prepping these to be implementation agnostic. Co-authored-by: LemonInTheDark <58055496+LemonInTheDark@users.noreply.github.com> Co-authored-by: Mothblocks <35135081+Mothblocks@users.noreply.github.com> Co-authored-by: LemonInTheDark <58055496+LemonInTheDark@users.noreply.github.com> --- code/__DEFINES/is_helpers.dm | 2 +- code/__DEFINES/stat_tracking.dm | 4 + code/datums/shuttles.dm | 8 +- code/game/turfs/baseturfs.dm | 189 ++++++++++++++++++ code/game/turfs/change_turf.dm | 117 ----------- code/game/turfs/closed/minerals.dm | 1 - code/game/turfs/open/floor.dm | 4 +- .../game/turfs/open/floor/reinforced_floor.dm | 2 +- code/game/turfs/open/river.dm | 7 +- code/modules/holodeck/computer.dm | 7 +- code/modules/mapping/mapping_helpers.dm | 18 +- code/modules/shuttle/on_move.dm | 14 +- code/modules/shuttle/shuttle.dm | 10 +- code/modules/unit_tests/_unit_tests.dm | 1 + code/modules/unit_tests/baseturfs.dm | 69 +++++++ tgstation.dme | 1 + 16 files changed, 285 insertions(+), 169 deletions(-) create mode 100644 code/game/turfs/baseturfs.dm create mode 100644 code/modules/unit_tests/baseturfs.dm 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"