From 1180f9ddd4c22c6310a504d76f80da88ad4b7905 Mon Sep 17 00:00:00 2001 From: Crazylemon64 Date: Sat, 6 Aug 2016 22:52:41 -0700 Subject: [PATCH] Makes space level removal and adding smooth --- code/game/turfs/space/space.dm | 24 +++ code/modules/space_management/space_level.dm | 73 ++++++++- .../space_management/space_transition.dm | 155 ++++++++++-------- .../space_management/zlevel_manager.dm | 3 + 4 files changed, 178 insertions(+), 77 deletions(-) diff --git a/code/game/turfs/space/space.dm b/code/game/turfs/space/space.dm index ec473c3eadc..483da4266fc 100644 --- a/code/game/turfs/space/space.dm +++ b/code/game/turfs/space/space.dm @@ -32,6 +32,7 @@ ..() var/datum/space_level/S = space_manager.get_zlev(z) S.add_to_transit(src) + S.apply_transition(src) /turf/space/proc/update_starlight() if(!config.starlight) @@ -209,3 +210,26 @@ /turf/space/can_have_cabling() return 0 + +/turf/space/proc/set_transition_north(dest_z) + destination_x = x + destination_y = TRANSITIONEDGE + 2 + destination_z = dest_z + +/turf/space/proc/set_transition_south(dest_z) + destination_x = x + destination_y = world.maxy - TRANSITIONEDGE - 2 + destination_z = dest_z + +/turf/space/proc/set_transition_east(dest_z) + destination_x = TRANSITIONEDGE + 2 + destination_y = y + destination_z = dest_z + +/turf/space/proc/set_transition_west(dest_z) + destination_x = world.maxx - TRANSITIONEDGE - 2 + destination_y = y + destination_z = dest_z + +/turf/space/proc/remove_transitions() + destination_z = initial(destination_z) diff --git a/code/modules/space_management/space_level.dm b/code/modules/space_management/space_level.dm index 1082a1bf29c..d7b2ef367df 100644 --- a/code/modules/space_management/space_level.dm +++ b/code/modules/space_management/space_level.dm @@ -6,7 +6,9 @@ // Map transition stuff var/list/neighbors = list() // # How this level connects with others. See __MAP_DEFINES.dm for defines - var/linkage = SELFLOOPING + // It's UNAFFECTED by default because none of the space turfs are normally linked up + // so we don't need to rebuild transitions if an UNAFFECTED level is requested + var/linkage = UNAFFECTED // # imaginary placements on the grid - these reflect the point it is linked to var/xi var/yi @@ -21,15 +23,35 @@ /datum/space_level/New(z, name, transition_type = SELFLOOPING) zpos = z - set_linkage(transition_type) build_space_destination_arrays() + set_linkage(transition_type) + +/datum/space_level/Destroy() + if(linkage == CROSSLINKED) + if(space_manager.linkage_map) + remove_from_space_network(space_manager.linkage_map) + + space_manager.unbuilt_space_transitions -= src + space_manager.z_list -= "[zpos]" + return ..() /datum/space_level/proc/build_space_destination_arrays() - var/timer = start_watch() - log_debug("Starting to build space destination arrays for z level '[zpos]'...") - for(var/turf/space/S in get_turfs()) - add_to_transit(S) - log_debug("Building space destination arrays complete, took [stop_watch(timer)]s.") + // We skip `add_to_transit` here because we want to skip the checks in order to save time + // Bottom border + for(var/turf/space/S in block(locate(1,1,zpos),locate(world.maxx,TRANSITIONEDGE+1,zpos))) + transit_south |= S + + // Top border + for(var/turf/space/S in block(locate(1,world.maxy,zpos),locate(world.maxx,world.maxy - TRANSITIONEDGE - 1,zpos))) + transit_north |= S + + // Left border + for(var/turf/space/S in block(locate(1,TRANSITIONEDGE+1,zpos),locate(TRANSITIONEDGE+1,world.maxy - TRANSITIONEDGE - 2,zpos))) + transit_west |= S + + // Right border + for(var/turf/space/S in block(locate(world.maxx - TRANSITIONEDGE - 1,TRANSITIONEDGE+1,zpos),locate(world.maxx,world.maxy - TRANSITIONEDGE - 2,zpos))) + transit_east |= S /datum/space_level/proc/add_to_transit(turf/space/S) if(S.y <= TRANSITIONEDGE) @@ -69,13 +91,46 @@ if(S.x >= (world.maxx - TRANSITIONEDGE - 1)) transit_east -= S +/datum/space_level/proc/apply_transition(turf/space/S) + if(src in space_manager.unbuilt_space_transitions) + return // Let the space manager handle this one + switch(linkage) + if(UNAFFECTED) + S.remove_transitions() + if(SELFLOOPING,CROSSLINKED) + var/datum/space_level/E = get_connection() + if(S in transit_north) + E = get_connection("[NORTH]") + S.set_transition_north(E.zpos) + if(S in transit_south) + E = get_connection("[SOUTH]") + S.set_transition_south(E.zpos) + if(S in transit_east) + E = get_connection("[EAST]") + S.set_transition_east(E.zpos) + if(S in transit_west) + E = get_connection("[WEST]") + S.set_transition_west(E.zpos) + + /datum/space_level/proc/get_turfs() return block(locate(1, 1, zpos), locate(world.maxx, world.maxy, zpos)) /datum/space_level/proc/set_linkage(transition_type) + if(linkage == transition_type) + return + // Remove ourselves from the linkage map if we were cross-linked + if(linkage == CROSSLINKED) + if(space_manager.linkage_map) + remove_from_space_network(space_manager.linkage_map) + + space_manager.unbuilt_space_transitions |= src linkage = transition_type - if(transition_type == SELFLOOPING) - link_to_self() // `link_to_self` is defined in space_transitions.dm + switch(transition_type) + if(UNAFFECTED) + reset_connections() + if(SELFLOOPING) + link_to_self() // `link_to_self` is defined in space_transitions.dm /datum/space_level/proc/resume_init() diff --git a/code/modules/space_management/space_transition.dm b/code/modules/space_management/space_transition.dm index 469365e4023..7e7e019a44d 100644 --- a/code/modules/space_management/space_transition.dm +++ b/code/modules/space_management/space_transition.dm @@ -16,8 +16,10 @@ if(Z_LEVEL_WEST) return Z_LEVEL_EAST -/datum/space_level - var/list/direction_cache = list() + +// Do this before setting up new connections, or the old ones will haunt you +/datum/space_level/proc/reset_connections() + neighbors.Cut() /datum/space_level/proc/link_to_self() neighbors = list() @@ -25,6 +27,24 @@ for(var/A in L) neighbors[A] = src +// Only call this when the `linkage_map` is already built +/datum/space_level/proc/add_to_space_network(datum/spacewalk_grid/SW) + // Make sure we don't bring any noise data into the network + reset_connections() + xi = initial(xi) + yi = initial(yi) + var/datum/point/P = SW.get_empty_node() + P.set_space_level(src) + +/datum/space_level/proc/remove_from_space_network(datum/spacewalk_grid/SW) + var/datum/point/P = SW.get(xi,yi) + SW.release_node(P) + // Only do this when we're done, or we'll trample vars needed for releasing + // the level + xi = initial(xi) + yi = initial(yi) + reset_connections() + // This proc takes another space level, and establishes a connection between the // two depending on how the `xi` and the `yi` values compare /datum/space_level/proc/link_levels(datum/space_level/S) @@ -41,26 +61,18 @@ else // yell about evil wizards, this shouldn't happen log_debug("Two z levels attempted to link, but were not adjacent! Our z:([xi],[yi]). Other z:([S.xi],[S.yi])") -// Do this before setting up new connections, or the old ones will haunt you -/datum/space_level/proc/reset_connections() - neighbors.Cut() - direction_cache.Cut() - // `direction` here is the direction from `src` to `S` /datum/space_level/proc/add_connection(datum/space_level/S, direction) var/oppose = get_opposite_direction(direction) neighbors[direction] = S S.neighbors[oppose] = src + space_manager.unbuilt_space_transitions |= src + space_manager.unbuilt_space_transitions |= S -// The "direction cache" will need updating if a /datum/space_level/proc/get_connection(direction) if(direction in neighbors) return neighbors[direction] - var/use_direction_cache = 0 - if(use_direction_cache) - if(direction in direction_cache) - return direction_cache[direction] // It's in a direction that loops - so we step as far in the opposite direction to get where to wrap to var/datum/space_level/S = src @@ -70,9 +82,6 @@ if(S.neighbors[oppose] == src) // we've got a tesseract, boys CRASH("Tesseract formed when routing connections between z levels. Culprit: z level '[S.zpos]' to '[src.zpos]', direction [oppose]") S = S.neighbors[oppose] - - if(use_direction_cache) - direction_cache[direction] = S return S @@ -145,8 +154,6 @@ // This looks around itself to see if it has any active nodes within the cardinal directions /datum/point/proc/has_no_neighbors(datum/spacewalk_grid/SW) var/result = 1 - if(spl) - return 1 if(!isnull(SW.get(x+1,y))) result = 0 if(!isnull(SW.get(x-1,y))) @@ -165,6 +172,7 @@ var/datum/space_level/S = spl.neighbors[direction] var/oppose = get_opposite_direction(direction) S.neighbors.Remove(oppose) + space_manager.unbuilt_space_transitions |= S spl.reset_connections() spl = initial(spl) @@ -191,6 +199,14 @@ var/datum/point/P = new(0,0) add_available_node(P) +/datum/spacewalk_grid/Destroy() + for(var/datum/point/P in filled_nodes) + release_node(P) + if(available_nodes.len > 1) + log_debug("Multiple nodes left behind after SW grid qdel: [available_nodes.len]") + for(var/datum/point/P in available_nodes) + log_debug("([P.x],[P.y])") + /datum/spacewalk_grid/proc/add_available_node(datum/point/P) var/hash = P.hash() if(hash in all_nodes) @@ -225,9 +241,12 @@ for(var/datum/point/P2 in P.neighbors) var/isolated = P2.has_no_neighbors(src) if(isolated) - available_nodes -= P2 - all_nodes -= P2.hash() - qdel(P) + if(!P2.spl) + available_nodes -= P2 + all_nodes -= P2.hash() + qdel(P) + else + log_debug("Isolated z level at ([P2.x],[P2.y]): [P2.spl.zpos]") P.deactivate() P.neighbors.Cut() @@ -307,75 +326,75 @@ // `grid` is a flat list of these same above points // Each point represents a possible z level position - var/datum/spacewalk_grid/point_grid = new - // We do this so we can display the way the levels connect later - linkage_map = point_grid - var/datum/point/P + if(linkage_map) + qdel(linkage_map) + linkage_map = new // Now, we pop entries in a random order from our list of space levels // and assign its connections based on the grid while(crosslinks.len) D = pick(crosslinks) crosslinks.Remove(D) - // We now choose a point in our imaginary grid adjacent to our current location - P = point_grid.get_empty_node() - // Let our z level know where in the imaginary grid it is - // This will also handle establishing neighborship with other z levels - P.set_space_level(D) + // Add it to our space grid + D.add_to_space_network(linkage_map) -// A heavy proc - loops through all space turfs and sets its destination -// based on its space level's linkage -// Takes 0.6 seconds per call on my machine - could have each z level -// have its own space turf cache, but I don't want to complicate this more -// than is necessary -/datum/zlev_manager/proc/setup_space_destinations() +// Used to loop through turfs in world, now just goes through each level's +// transit turf cache +/datum/zlev_manager/proc/setup_space_destinations(force_all_rebuilds = FALSE) var/timer = start_watch() log_debug("Assigning space turf destinations...") var/datum/space_level/D var/datum/space_level/E var/turf/space/S - for(var/A in z_list) //Define the transistions of the z levels - D = z_list[A] - if(!D.neighbors.len) - continue - // Left border - for(var/B in D.transit_west) - S = B - E = D.get_connection(Z_LEVEL_WEST) - S.destination_z = E.zpos - S.destination_x = world.maxx - TRANSITIONEDGE - 2 - S.destination_y = S.y + var/list/levels_to_rebuild = unbuilt_space_transitions - // Right border - for(var/B in D.transit_east) - S = B - E = D.get_connection(Z_LEVEL_EAST) - S.destination_x = TRANSITIONEDGE + 2 - S.destination_y = S.y - S.destination_z = E.zpos + if(force_all_rebuilds) + // Assuming we don't have like 9000 zlevels this shouldn't hurt + levels_to_rebuild = list() + for(var/A in z_list) + levels_to_rebuild.Add(z_list[A]) - // Bottom border - for(var/B in D.transit_south) - S = B - E = D.get_connection(Z_LEVEL_SOUTH) - S.destination_x = S.x - S.destination_y = world.maxy - TRANSITIONEDGE - 2 - S.destination_z = E.zpos - // Top border - for(var/B in D.transit_north) - S = B - E = D.get_connection(Z_LEVEL_NORTH) - S.destination_x = S.x - S.destination_y = TRANSITIONEDGE + 2 - S.destination_z = E.zpos + for(var/foo in levels_to_rebuild) //Define the transitions of the z levels + D = foo + log_debug("Z level [D.zpos]") + switch(D.linkage) + if(UNAFFECTED) + for(var/B in D.transit_west | D.transit_east | D.transit_south | D.transit_north) + S = B + S.remove_transitions() + if(SELFLOOPING,CROSSLINKED) + // Left border + for(var/B in D.transit_west) + S = B + E = D.get_connection(Z_LEVEL_WEST) + S.set_transition_west(E.zpos) + + // Right border + for(var/B in D.transit_east) + S = B + E = D.get_connection(Z_LEVEL_EAST) + S.set_transition_east(E.zpos) + + // Bottom border + for(var/B in D.transit_south) + S = B + E = D.get_connection(Z_LEVEL_SOUTH) + S.set_transition_south(E.zpos) + + // Top border + for(var/B in D.transit_north) + S = B + E = D.get_connection(Z_LEVEL_NORTH) + S.set_transition_north(E.zpos) + unbuilt_space_transitions -= D log_debug("Assigning space turf destinations complete. Took [stop_watch(timer)]s.") // Nothing fancy, just does it all at once /datum/zlev_manager/proc/do_transition_setup() route_linkage() - setup_space_destinations() + setup_space_destinations(force_all_rebuilds = TRUE) // A debugging proc that expresses the map's shape as a bunch of turfs /datum/zlev_manager/proc/map_as_turfs(turf/center) diff --git a/code/modules/space_management/zlevel_manager.dm b/code/modules/space_management/zlevel_manager.dm index 8f83c0ab5e4..8a6efe91407 100644 --- a/code/modules/space_management/zlevel_manager.dm +++ b/code/modules/space_management/zlevel_manager.dm @@ -5,6 +5,9 @@ var/global/datum/zlev_manager/space_manager = new var/list/z_list = list() var/list/heaps = list() + // Levels that need their transitions rebuilt + var/list/unbuilt_space_transitions = list() + var/datum/spacewalk_grid/linkage_map // Populate our space level list