diff --git a/baystation12.dme b/baystation12.dme index 4665b2cf95d..93b4793c851 100644 --- a/baystation12.dme +++ b/baystation12.dme @@ -46,6 +46,7 @@ #include "code\__defines\targeting.dm" #include "code\__defines\turfs.dm" #include "code\__defines\ZAS.dm" +#include "code\_helpers\area_movement.dm" #include "code\_helpers\areas.dm" #include "code\_helpers\atmospherics.dm" #include "code\_helpers\datum_pool.dm" diff --git a/code/_helpers/area_movement.dm b/code/_helpers/area_movement.dm new file mode 100644 index 00000000000..b903acb93cc --- /dev/null +++ b/code/_helpers/area_movement.dm @@ -0,0 +1,125 @@ +// Builds a list of turfs belonging to an area in a predictable order. +// Two areas of the same size should have directly comparable ordered turf lists. +// If ignore_type has a value, that turf will be excluded from the list. +// Excluded turfs are represented by null values in the list to maintain order. +/area/proc/build_ordered_turf_list(ignore_type) + . = list() + + // Find the maximums and minimums of the area. + var/xmax = -1 + var/ymax = -1 + var/xmin = INFINITY + var/ymin = INFINITY + var/z = -1 + for (var/turf/T in src) + if (z == -1) + z = T.z + + if (T.x > xmax) + xmax = T.x + + if (T.x < xmin) + xmin = T.x + + if (T.y > ymax) + ymax = T.y + + if (T.y < ymin) + ymin = T.y + + //log_debug("build_ordered_turf_list([DEBUG_REF(src)]): xmax=[xmax],xmin=[xmin],ymax=[ymax],ymin=[ymin],z=[z]") + + ASSERT(xmax > xmin) + ASSERT(ymax > ymin) + ASSERT(z != -1) + + // Now use our information to build an *ordered* list of turfs. + for (var/x = xmin; x <= xmax; x++) + for (var/y = ymin; y <= ymax; y++) + var/turf/T = locate(x, y, z) + if (T.loc != src || T.type == ignore_type) + // Not ours or ignored type, we don't give a crap. + // Add a null to keep the list a predictable size. + . += null + else + // Turf matches, add it. + . += T + +// Moves the contents of this area to A. If turf_to_leave is defined, that type will be excluded from the area. +/area/proc/move_contents_to(area/A, turf_to_leave = null) + var/list/source_turfs = src.build_ordered_turf_list(turf_to_leave) + var/list/target_turfs = A.build_ordered_turf_list() + + //log_debug("move_contents_to: source_turfs.len=[source_turfs.len],target_turfs.len=[target_turfs.len]") + + ASSERT(source_turfs.len == target_turfs.len) + + var/list/simulated_turfs = list() + + for (var/i = 1; i <= source_turfs.len; i++) + var/turf/ST = source_turfs[i] + if (!ST) // Excluded turfs are null to keep the list ordered. + continue + + var/turf/TT = ST.copy_turf(target_turfs[i]) + + for (var/thing in ST) + var/atom/movable/AM = thing + AM.shuttle_move(TT) + + ST.ChangeTurf(get_base_turf_by_area(ST)) + + if (istype(TT, /turf/simulated)) + simulated_turfs += TT + + for (var/thing in simulated_turfs) + var/turf/simulated/T = thing + + T.update_icon() + if (istype(T.above)) + T.above.queue_icon_update() + +// Called when a movable area wants to move this object. +/atom/movable/proc/shuttle_move(turf/loc) + forceMove(loc) + +// In theory, this copies the contents of the area to another, and returns a list containing every new object it created. +// It's not tested because the holodeck doesn't work yet. +/area/proc/copy_contents_to(area/A, plating_required = FALSE) + var/list/source_turfs = src.build_ordered_turf_list() + var/list/target_turfs = A.build_ordered_turf_list() + + . = list() + + log_debug("copy_contents_to: source_turfs.len=[source_turfs.len],target_turfs.len=[target_turfs.len]") + + ASSERT(source_turfs.len == target_turfs.len) + + var/baseturf + if (plating_required) + baseturf = A.base_turf + if (!baseturf) + var/turf/T + for (var/idex = 1; T == null; idex++) + if (idex > target_turfs.len) + CRASH("Empty target_turfs list!") + + T = target_turfs[idex] + + baseturf = get_base_turf(T.z) + + for (var/i = 1; i <= source_turfs.len; i++) + var/turf/ST = source_turfs[i] + var/turf/TTi = target_turfs[i] + if (!ST || (plating_required && TTi.type != baseturf)) // Excluded turfs are null to keep the list ordered. + continue + + var/turf/TT = ST.copy_turf(TTi, ignore_air = TRUE) + + for (var/thing in ST) + var/atom/movable/AM = thing + var/atom/movable/copy = DuplicateObject(AM, 1) + copy.forceMove(TT) + . += copy + + SSair.mark_for_update(TT) diff --git a/code/_helpers/unsorted.dm b/code/_helpers/unsorted.dm index f6a5c6b5f73..fb420e19874 100644 --- a/code/_helpers/unsorted.dm +++ b/code/_helpers/unsorted.dm @@ -798,143 +798,6 @@ proc/GaussRandRound(var/sigma,var/roundto) atoms += A return atoms -/datum/coords //Simple datum for storing coordinates. - var/x_pos = null - var/y_pos = null - var/z_pos = null - -/area/proc/move_contents_to(var/area/A, var/turftoleave=null, var/direction = null) - //Takes: Area. Optional: turf type to leave behind. - //Returns: Nothing. - //Notes: Attempts to move the contents of one area to another area. - // Movement based on lower left corner. Tiles that do not fit - // into the new area will not be moved. - - if(!A || !src) return 0 - - var/list/turfs_src = get_area_turfs(src.type) - var/list/turfs_trg = get_area_turfs(A.type) - - var/src_min_x = 0 - var/src_min_y = 0 - for (var/turf/T in turfs_src) - if(T.x < src_min_x || !src_min_x) src_min_x = T.x - if(T.y < src_min_y || !src_min_y) src_min_y = T.y - - var/trg_min_x = 0 - var/trg_min_y = 0 - for (var/turf/T in turfs_trg) - if(T.x < trg_min_x || !trg_min_x) trg_min_x = T.x - if(T.y < trg_min_y || !trg_min_y) trg_min_y = T.y - - var/list/refined_src = new/list() - for(var/turf/T in turfs_src) - refined_src += T - refined_src[T] = new/datum/coords - var/datum/coords/C = refined_src[T] - C.x_pos = (T.x - src_min_x) - C.y_pos = (T.y - src_min_y) - - var/list/refined_trg = new/list() - for(var/turf/T in turfs_trg) - refined_trg += T - refined_trg[T] = new/datum/coords - var/datum/coords/C = refined_trg[T] - C.x_pos = (T.x - trg_min_x) - C.y_pos = (T.y - trg_min_y) - - var/list/fromupdate = new/list() - var/list/toupdate = new/list() - - moving: - for (var/turf/T in refined_src) - var/datum/coords/C_src = refined_src[T] - for (var/turf/B in refined_trg) - var/datum/coords/C_trg = refined_trg[B] - if(C_src.x_pos == C_trg.x_pos && C_src.y_pos == C_trg.y_pos) - - var/old_dir1 = T.dir - var/old_icon_state1 = T.icon_state - var/old_icon1 = T.icon - var/old_underlays = T.underlays.Copy() - - // SSoverlays makes this a bit more complex. - var/old_our_overlays = T.our_overlays - T.our_overlays = null - var/old_priority_overlays = T.priority_overlays - T.priority_overlays = null - - var/turf/X = B.ChangeTurf(T.type) - X.set_dir(old_dir1) - X.icon_state = old_icon_state1 - X.icon = old_icon1 //Shuttle floors are in shuttle.dmi while the defaults are floors.dmi - X.underlays = old_underlays - - X.our_overlays = old_our_overlays - X.priority_overlays = old_priority_overlays - X.compile_overlays() - - var/turf/simulated/ST = T - if(istype(ST) && ST.zone) - var/turf/simulated/SX = X - if(!SX.air) - SX.make_air() - SX.air.copy_from(ST.zone.air) - ST.zone.remove(ST) - - /* Quick visual fix for some weird shuttle corner artefacts when on transit space tiles */ - if(direction && findtext(X.icon_state, "swall_s")) - - // Spawn a new shuttle corner object - var/obj/corner = new() - corner.loc = X - corner.density = 1 - corner.anchored = 1 - corner.icon = X.icon - corner.icon_state = replacetext(X.icon_state, "_s", "_f") - corner.tag = "delete me" - corner.name = "wall" - - // Find a new turf to take on the property of - var/turf/nextturf = get_step(corner, direction) - if(!nextturf || !istype(nextturf, /turf/space)) - nextturf = get_step(corner, turn(direction, 180)) - - - // Take on the icon of a neighboring scrolling space icon - X.icon = nextturf.icon - X.icon_state = nextturf.icon_state - - for (var/thing in T) - if (isobj(thing)) - var/obj/O = thing - - // Reset the shuttle corners - if(O.tag == "delete me") - X.icon = 'icons/turf/shuttle.dmi' - X.icon_state = replacetext(O.icon_state, "_f", "_s") // revert the turf to the old icon_state - X.name = "wall" - qdel(O) // prevents multiple shuttle corners from stacking - continue - - O.forceMove(X) - - else if (ismob(thing) && !istype(thing, /mob/eye) && !istype(thing, /mob/dview)) - var/mob/M = thing - M.forceMove(X) - - toupdate += X - - if(turftoleave) - fromupdate += T.ChangeTurf(turftoleave) - else - T.ChangeTurf(get_base_turf_by_area(T)) - - refined_src -= T - refined_trg -= B - continue moving - - proc/DuplicateObject(obj/original, var/perfectcopy = 0 , var/sameloc = 0) if(!original) return null @@ -953,127 +816,6 @@ proc/DuplicateObject(obj/original, var/perfectcopy = 0 , var/sameloc = 0) O.vars[V] = original.vars[V] return O - -/area/proc/copy_contents_to(var/area/A , var/platingRequired = 0 ) - //Takes: Area. Optional: If it should copy to areas that don't have plating - //Returns: Nothing. - //Notes: Attempts to move the contents of one area to another area. - // Movement based on lower left corner. Tiles that do not fit - // into the new area will not be moved. - - // Does *not* affect gases etc; copied turfs will be changed via ChangeTurf, and the dir, icon, and icon_state copied. All other vars will remain default. - - if(!A || !src) return 0 - - var/list/turfs_src = get_area_turfs(src.type) - var/list/turfs_trg = get_area_turfs(A.type) - - var/src_min_x = 0 - var/src_min_y = 0 - for (var/turf/T in turfs_src) - if(T.x < src_min_x || !src_min_x) src_min_x = T.x - if(T.y < src_min_y || !src_min_y) src_min_y = T.y - - var/trg_min_x = 0 - var/trg_min_y = 0 - for (var/turf/T in turfs_trg) - if(T.x < trg_min_x || !trg_min_x) trg_min_x = T.x - if(T.y < trg_min_y || !trg_min_y) trg_min_y = T.y - - var/list/refined_src = new/list() - for(var/turf/T in turfs_src) - refined_src += T - refined_src[T] = new/datum/coords - var/datum/coords/C = refined_src[T] - C.x_pos = (T.x - src_min_x) - C.y_pos = (T.y - src_min_y) - - var/list/refined_trg = new/list() - for(var/turf/T in turfs_trg) - refined_trg += T - refined_trg[T] = new/datum/coords - var/datum/coords/C = refined_trg[T] - C.x_pos = (T.x - trg_min_x) - C.y_pos = (T.y - trg_min_y) - - var/list/toupdate = new/list() - - var/copiedobjs = list() - - - moving: - for (var/turf/T in refined_src) - var/datum/coords/C_src = refined_src[T] - for (var/turf/B in refined_trg) - var/datum/coords/C_trg = refined_trg[B] - if(C_src.x_pos == C_trg.x_pos && C_src.y_pos == C_trg.y_pos) - - var/old_dir1 = T.dir - var/old_icon_state1 = T.icon_state - var/old_icon1 = T.icon - var/old_overlays = T.overlays.Copy() - var/old_underlays = T.underlays.Copy() - - if(platingRequired) - if(istype(B, get_base_turf_by_area(B))) - continue moving - - var/turf/X = B - X.ChangeTurf(T.type) - X.set_dir(old_dir1) - X.icon_state = old_icon_state1 - X.icon = old_icon1 //Shuttle floors are in shuttle.dmi while the defaults are floors.dmi - X.overlays = old_overlays - X.underlays = old_underlays - - var/list/objs = new/list() - var/list/newobjs = new/list() - var/list/mobs = new/list() - var/list/newmobs = new/list() - - for(var/obj/O in T) - - if(!istype(O,/obj)) - continue - - objs += O - - - for(var/obj/O in objs) - newobjs += DuplicateObject(O , 1) - - - for(var/obj/O in newobjs) - O.loc = X - - for(var/mob/M in T) - - if(!istype(M,/mob) || istype(M, /mob/eye)) continue // If we need to check for more mobs, I'll add a variable - mobs += M - - for(var/mob/M in mobs) - newmobs += DuplicateObject(M , 1) - - for(var/mob/M in newmobs) - M.loc = X - - copiedobjs += newobjs - copiedobjs += newmobs - - toupdate += X - - refined_src -= T - refined_trg -= B - continue moving - - if(toupdate.len) - for(var/turf/simulated/T1 in toupdate) - SSair.mark_for_update(T1) - - return copiedobjs - - - proc/get_cardinal_dir(atom/A, atom/B) var/dx = abs(B.x - A.x) var/dy = abs(B.y - A.y) diff --git a/code/controllers/subsystems/explosives.dm b/code/controllers/subsystems/explosives.dm index b496311f8ad..b18d7a8b092 100644 --- a/code/controllers/subsystems/explosives.dm +++ b/code/controllers/subsystems/explosives.dm @@ -32,7 +32,7 @@ var/datum/controller/subsystem/explosives/SSexplosives if (!(work_queue.len)) ticks_without_work++ if (powernet_update_pending && ticks_without_work > 5) - makepowernets() + SSmachinery.powernet_update_queued = TRUE powernet_update_pending = 0 // All explosions handled, powernet rebuilt. diff --git a/code/controllers/subsystems/machinery.dm b/code/controllers/subsystems/machinery.dm index 01b1219ff99..f7d0c9d062c 100644 --- a/code/controllers/subsystems/machinery.dm +++ b/code/controllers/subsystems/machinery.dm @@ -23,6 +23,7 @@ var/list/breaker_boxes = list() var/rcon_update_queued = FALSE + var/powernet_update_queued = FALSE var/list/slept_in_process = list() @@ -69,6 +70,10 @@ if (rcon_update_queued) build_rcon_lists() + if (powernet_update_queued) + makepowernets() + powernet_update_queued = FALSE + var/list/curr_machinery = src.processing_machinery var/list/curr_powersinks = src.processing_powersinks diff --git a/code/game/turfs/turf_changing.dm b/code/game/turfs/turf_changing.dm index 9c533083b40..342676d7540 100644 --- a/code/game/turfs/turf_changing.dm +++ b/code/game/turfs/turf_changing.dm @@ -91,3 +91,41 @@ src.air.copy_from(other.zone.air) other.zone.remove(other) return 1 + +// Copies this turf to other, overwriting it. +// Returns a ref to the other turf post-change. +/turf/proc/copy_turf(turf/other) + if (other.type != type) + . = other.ChangeTurf(type) + else + . = other + + if (dir != other.dir) + other.set_dir(dir) + + other.icon = icon + other.icon_state = icon_state + other.underlays = underlays.Copy() + + if (our_overlays) + other.our_overlays = our_overlays + + if (priority_overlays) + other.priority_overlays = priority_overlays + + other.overlays = overlays.Copy() + +/turf/simulated/copy_turf(turf/simulated/other, ignore_air = FALSE) + . = ..() + + if (ignore_air || !zone || !istype(other)) + return + + if (!other.air) + other.make_air() + + other.air.copy_from(zone.air) + + SSair.mark_for_update(other) + + other.update_icon() diff --git a/code/modules/lighting/lighting_overlay.dm b/code/modules/lighting/lighting_overlay.dm index 20a5aa8bc04..612c87232f8 100644 --- a/code/modules/lighting/lighting_overlay.dm +++ b/code/modules/lighting/lighting_overlay.dm @@ -30,7 +30,10 @@ update_overlay() -/atom/movable/lighting_overlay/Destroy() +/atom/movable/lighting_overlay/Destroy(force = FALSE) + if (!force) + return QDEL_HINT_LETMELIVE // STOP DELETING ME + L_PROF(loc, "overlay_destroy") SSlighting.lighting_overlays -= src SSlighting.overlay_queue -= src @@ -54,13 +57,7 @@ else warning("A lighting overlay realised it was in nullspace in update_overlay() and got deleted!") - qdel(src) - return - - if (istype(T, /turf/space)) - // I mean, this happens often and doesn't do any harm. Might as well silence the warning. - //warning("A lighting overlay realised it was attached to a space tile and got pooled!") - qdel(src) + qdel(src, TRUE) return // To the future coder who sees this and thinks @@ -154,3 +151,6 @@ color = LIGHTING_BASE_MATRIX return ..("color") + +/atom/movable/lighting_overlay/shuttle_move(turf/loc) + return diff --git a/code/modules/lighting/lighting_turf.dm b/code/modules/lighting/lighting_turf.dm index 3ae58356979..8c22ec0187b 100644 --- a/code/modules/lighting/lighting_turf.dm +++ b/code/modules/lighting/lighting_turf.dm @@ -23,7 +23,12 @@ /turf/proc/lighting_clear_overlay() if (lighting_overlay) - qdel(lighting_overlay) + if (lighting_overlay.loc != src) + var/turf/badT = lighting_overlay.loc + crash_with("Lighting overlay variable on turf [DEBUG_REF(src)] is insane, lighting overlay actually located on [DEBUG_REF(lighting_overlay.loc)] at ([badT.x],[badT.y],[badT.z])!") + + qdel(lighting_overlay, TRUE) + lighting_overlay = null L_PROF(src, "turf_clear_overlay") @@ -170,9 +175,14 @@ recalc_atom_opacity() lighting_overlay = old_lighting_overlay + if (lighting_overlay && lighting_overlay.loc != src) + // This is a hack, but I can't figure out why the fuck they're not on the correct turf in the first place. + lighting_overlay.forceMove(src, harderforce = TRUE) + affecting_lights = old_affecting_lights corners = old_corners - if ((old_opacity != opacity) || (dynamic_lighting != old_dynamic_lighting)) + + if ((old_opacity != opacity) || (dynamic_lighting != old_dynamic_lighting) || force_lighting_update) reconsider_lights() if (dynamic_lighting != old_dynamic_lighting) diff --git a/code/modules/lighting/lighting_verbs.dm b/code/modules/lighting/lighting_verbs.dm index ce14b59d8eb..2cae09c5fbd 100644 --- a/code/modules/lighting/lighting_verbs.dm +++ b/code/modules/lighting/lighting_verbs.dm @@ -42,7 +42,7 @@ var/list/admin_verbs_lighting = list( SSlighting.corner_queue = list() SSlighting.overlay_queue = list() -/client/proc/lighting_reconsider_target(turf/T in world) +/client/proc/lighting_reconsider_target(turf/T in turfs) set category = "Lighting" set name = "Reconsider Visibility" set desc = "Triggers a visibility update for a turf." @@ -57,7 +57,7 @@ var/list/admin_verbs_lighting = list( T.reconsider_lights() -/client/proc/lighting_build_overlay(turf/T in world) +/client/proc/lighting_build_overlay(turf/T in turfs) set category = "Lighting" set name = "Build Overlay" set desc = "Builds a lighting overlay for a turf if it does not have one." @@ -72,7 +72,7 @@ var/list/admin_verbs_lighting = list( T.lighting_build_overlay() -/client/proc/lighting_clear_overlay(turf/T in world) +/client/proc/lighting_clear_overlay(turf/T in turfs) set category = "Lighting" set name = "Clear Overlay" set desc = "Clears a lighting overlay for a turf if it has one." diff --git a/code/modules/power/cable.dm b/code/modules/power/cable.dm index 5182aac20f3..77e2e9ce987 100644 --- a/code/modules/power/cable.dm +++ b/code/modules/power/cable.dm @@ -201,6 +201,10 @@ var/list/possible_cable_coil_colours = list( return 1 return 0 +/obj/structure/cable/shuttle_move(turf/loc) + ..() + SSmachinery.powernet_update_queued = TRUE + //explosion handling /obj/structure/cable/ex_act(severity) switch(severity) diff --git a/code/modules/power/pacman2.dm b/code/modules/power/pacman2.dm index 592fc97f2d2..f98388f62a3 100644 --- a/code/modules/power/pacman2.dm +++ b/code/modules/power/pacman2.dm @@ -84,7 +84,7 @@ user << "\blue You secure the generator to the floor." else user << "\blue You unsecure the generator from the floor." - makepowernets() + SSmachinery.powernet_update_queued = TRUE else if(istype(O, /obj/item/weapon/screwdriver)) open = !open playsound(src.loc, 'sound/items/Screwdriver.ogg', 50, 1) diff --git a/code/modules/power/power.dm b/code/modules/power/power.dm index b9d135840f7..5e5d2821c97 100644 --- a/code/modules/power/power.dm +++ b/code/modules/power/power.dm @@ -190,6 +190,10 @@ . += C return . +/obj/machinery/power/shuttle_move(turf/loc) + ..() + SSmachinery.powernet_update_queued = TRUE + /////////////////////////////////////////// // GLOBAL PROCS for powernets handling ////////////////////////////////////////// diff --git a/code/modules/shuttles/shuttle.dm b/code/modules/shuttles/shuttle.dm index fb0724de611..00aed5cb866 100644 --- a/code/modules/shuttles/shuttle.dm +++ b/code/modules/shuttles/shuttle.dm @@ -80,7 +80,7 @@ //just moves the shuttle from A to B, if it can be moved //A note to anyone overriding move in a subtype. move() must absolutely not, under any circumstances, fail to move the shuttle. //If you want to conditionally cancel shuttle launches, that logic must go in short_jump() or long_jump() -/datum/shuttle/proc/move(var/area/origin, var/area/destination, var/direction=null) +/datum/shuttle/proc/move(var/area/origin, var/area/destination) //world << "move_shuttle() called for [shuttle_tag] leaving [origin] en route to [destination]." @@ -115,7 +115,7 @@ for(var/mob/living/simple_animal/pest in destination) pest.gib() - origin.move_contents_to(destination, direction=direction) + origin.move_contents_to(destination) for(var/mob/M in destination) if(M.client) @@ -130,20 +130,6 @@ if(!M.buckled) M.Weaken(3) - // Power-related checks. If shuttle contains power related machinery, update powernets. - var/update_power = 0 - for(var/obj/machinery/power/P in destination) - update_power = 1 - break - - for(var/obj/structure/cable/C in destination) - update_power = 1 - break - - if(update_power) - makepowernets() - return - //returns 1 if the shuttle has a valid arrive time /datum/shuttle/proc/has_arrive_time() return (moving_status == SHUTTLE_INTRANSIT) diff --git a/code/modules/turbolift/turbolift.dm b/code/modules/turbolift/turbolift.dm index 140b92a5bd0..cceccfff1d0 100644 --- a/code/modules/turbolift/turbolift.dm +++ b/code/modules/turbolift/turbolift.dm @@ -101,9 +101,6 @@ var/global/list/turbolifts = list() origin.move_contents_to(destination) - if((locate(/obj/machinery/power) in destination) || (locate(/obj/structure/cable) in destination)) - makepowernets() - current_floor = next_floor control_panel_interior.visible_message("The elevator [moving_upwards ? "rises" : "descends"] smoothly.")