Shuttle refactor (#2171)

Rewrites the area movement code used by shuttles & elevators in an effort to make it faster, more extensible, and generally easier to read. Also fixes some bugs relating to lighting & moving areas, such as lighting overlays suddenly being teleported into space for absolutely no reason.

Fixes #2161.
Fixes #2166.
This commit is contained in:
Lohikar
2017-05-05 10:16:53 +03:00
committed by skull132
parent a14654b197
commit 52a4f3a4e3
14 changed files with 204 additions and 292 deletions
+1
View File
@@ -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"
+125
View File
@@ -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)
-258
View File
@@ -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)
+1 -1
View File
@@ -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.
+5
View File
@@ -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
+38
View File
@@ -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()
+8 -8
View File
@@ -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
+12 -2
View File
@@ -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)
+3 -3
View File
@@ -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."
+4
View File
@@ -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)
+1 -1
View File
@@ -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)
+4
View File
@@ -190,6 +190,10 @@
. += C
return .
/obj/machinery/power/shuttle_move(turf/loc)
..()
SSmachinery.powernet_update_queued = TRUE
///////////////////////////////////////////
// GLOBAL PROCS for powernets handling
//////////////////////////////////////////
+2 -16
View File
@@ -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)
-3
View File
@@ -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.")