diff --git a/code/datums/map_elements.dm b/code/datums/map_elements.dm
index 1d79ccc7321..4b8ccf8204b 100644
--- a/code/datums/map_elements.dm
+++ b/code/datums/map_elements.dm
@@ -9,7 +9,7 @@ var/list/datum/map_element/map_elements = list()
var/file_path = "maps/randomvaults/new.dmm"
- var/turf/location //A random turf from the map element. Used for jumping to
+ var/turf/location //Lower left turf of the map element
var/width //Width of the map element, in turfs
var/height //Height of the map element, in turfs
@@ -24,11 +24,18 @@ var/list/datum/map_element/map_elements = list()
location = locate(/turf) in objects
/datum/map_element/proc/load(x, y, z)
- var/file = file(file_path)
- if(isfile(file))
- pre_load()
- var/list/L = maploader.load_map(file, z, x, y, src)
- initialize(L)
+ pre_load()
+
+ if(file_path)
+ var/file = file(file_path)
+ if(isfile(file))
+ var/list/L = maploader.load_map(file, z, x, y, src)
+ initialize(L)
+ return 1
+ else //No file specified - empty map element
+ //These variables are usually set by the map loader. Here we have to set them manually
+ location = locate(x+1, y+1, z) //Location is always lower left corner
+ initialize(list()) //Initialize with an empty list
return 1
return 0
\ No newline at end of file
diff --git a/code/game/objects/effects/step_triggers.dm b/code/game/objects/effects/step_triggers.dm
index dfb6172a44b..c40d8b777e9 100644
--- a/code/game/objects/effects/step_triggers.dm
+++ b/code/game/objects/effects/step_triggers.dm
@@ -118,3 +118,16 @@
A.y = rand(teleport_y, teleport_y_offset)
A.z = rand(teleport_z, teleport_z_offset)
+/obj/effect/step_trigger/teleporter/random/shuttle_transit
+ teleport_x = 25
+ teleport_y = 25
+ teleport_z = 6
+
+ //x and y offsets depend on the map size
+
+ teleport_z_offset = 6
+
+/obj/effect/step_trigger/teleporter/random/shuttle_transit/New()
+ ..()
+ teleport_x_offset = world.maxx - 25
+ teleport_y_offset = world.maxy - 25
diff --git a/code/game/objects/structures/docking_port.dm b/code/game/objects/structures/docking_port.dm
index 337eeb72f92..0e5e08ce33d 100644
--- a/code/game/objects/structures/docking_port.dm
+++ b/code/game/objects/structures/docking_port.dm
@@ -64,12 +64,17 @@ var/global/list/all_docking_ports = list()
docked_with = null
return 1
+/obj/docking_port/proc/docked(obj/docking_port/D)
+ return
+
/obj/docking_port/proc/dock(var/obj/docking_port/D)
undock()
D.docked_with = src
src.docked_with = D
+ D.docked(src)
+
/obj/docking_port/proc/get_docking_turf()
return get_step(get_turf(src),src.dir)
@@ -159,6 +164,34 @@ var/global/list/all_docking_ports = list()
/obj/docking_port/destination/shuttle_act() //These guys don't get destroyed
return 0
+/obj/docking_port/destination/transit
+ areaname = "transit area"
+ var/generate_borders = 0
+
+/obj/docking_port/destination/transit/docked(obj/docking_port/shuttle/D)
+ .=..()
+
+ if(!istype(D))
+ return //Only deal with shuttle docking ports
+
+ if(generate_borders)
+ //Generate teleport triggers around the shuttle that prevent players from simply walking out
+ //1) Go through every turf in the newly docked shuttle
+ //2) Check all adjacent turfs of every turf (maybe this sucks but I haven't thought of a better way to do it)
+ //3) Place teleporters as needed
+
+ var/teleporter_typepath = /obj/effect/step_trigger/teleporter/random/shuttle_transit
+
+ var/area/shuttle_area = D.linked_shuttle.linked_area
+ for(var/turf/T in shuttle_area)
+ for(var/dir in cardinal)
+ var/turf/check = get_step(T, dir)
+ if(check.loc != shuttle_area) //Turf doesn't belong to a shuttle
+ if(!locate(teleporter_typepath) in check)
+ new teleporter_typepath(check)
+
+ generate_borders = 0
+
//SILLY PROC
/proc/select_port_from_list(var/mob/user, var/message="Select a docking port", var/title="Admin abuse", var/list/list) //like input
if(!list || !user)
diff --git a/code/game/turfs/space/transit.dm b/code/game/turfs/space/transit.dm
index 0a400952675..696598351f9 100644
--- a/code/game/turfs/space/transit.dm
+++ b/code/game/turfs/space/transit.dm
@@ -6,6 +6,12 @@
if(loc)
var/area/A = loc
A.area_turfs += src
+
+ update_icon()
+
+/turf/space/transit/update_icon()
+ icon_state = ""
+
var/dira=""
var/i=0
switch(pushdirection)
diff --git a/code/modules/admin/admin.dm b/code/modules/admin/admin.dm
index f5e9babc6f1..fc761661174 100644
--- a/code/modules/admin/admin.dm
+++ b/code/modules/admin/admin.dm
@@ -861,6 +861,7 @@ var/global/floorIsLava = 0
Create a destination docking port
Add a destination docking port
Modify transit area
+ Generate new transit area
Get control console
Modify parameters[selected_shuttle.is_special() ? " and pre-defined areas" : ""]
diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm
index 5488f7e8d32..7211465a480 100644
--- a/code/modules/admin/topic.dm
+++ b/code/modules/admin/topic.dm
@@ -4285,6 +4285,34 @@
S.show_outline(usr)
+ if(href_list["shuttle_generate_transit"])
+ feedback_inc("admin_shuttle_magic_used",1)
+ feedback_add_details("admin_shuttle_magic_used","SO")
+
+ var/datum/shuttle/S = selected_shuttle
+ if(!istype(S))
+ return
+
+ var/transit_dir = NORTH
+ var/list/dirs = list("north"=NORTH, "west"=WEST, "east"=EAST, "south"=SOUTH)
+ var/choice = input(usr, "Select a direction for the transit area (this should be the direction in which the shuttle is currently facing)", "Transit") as null|anything in dirs
+
+ if(!choice)
+ return
+
+ transit_dir = dirs[choice]
+
+ var/obj/docking_port/destination/D = generate_transit_area(S, transit_dir)
+ if(!istype(D))
+ to_chat(usr, "Transit area generation failed!")
+ return
+
+ S.transit_port = D
+ to_chat(usr, "Transit area generated successfully.")
+ if(S.use_transit == NO_TRANSIT)
+ S.use_transit = TRANSIT_ACROSS_Z_LEVELS
+ to_chat(usr, "The [S.name] will now use the transit area when traveling across z-levels. Set its use_transit to 2 to make it always use transit, or 0 to disable transit.")
+
//------------------------------------------------------------------Shuttle stuff end---------------------------------
diff --git a/code/modules/admin/verbs/adminjump.dm b/code/modules/admin/verbs/adminjump.dm
index 9c1cab94b3a..2673c29d676 100644
--- a/code/modules/admin/verbs/adminjump.dm
+++ b/code/modules/admin/verbs/adminjump.dm
@@ -131,7 +131,7 @@
var/datum/map_element/V = vaults[selection]
if(!V.location)
- to_chat(src, "[V.file_path] doesn't have a location! Report this")
+ to_chat(src, "[V.file_path || V.name] doesn't have a location! Report this")
return
usr.forceMove(V.location)
diff --git a/code/modules/randomMaps/transit.dm b/code/modules/randomMaps/transit.dm
new file mode 100644
index 00000000000..ad846989cc9
--- /dev/null
+++ b/code/modules/randomMaps/transit.dm
@@ -0,0 +1,81 @@
+//Dynamically generated transit areas - welcome to the future
+
+//Object used for the dungeons system (see dungeons.dm)
+/datum/map_element/transit
+ type_abbreviation = "TR"
+
+ file_path = null //No map file is loaded
+
+ width = 0
+ height = 0
+
+
+//generate_transit_area proc
+//Arguments: shuttle (/datum/shuttle object), direction (of the transit turfs), create_borders = 1 (if 0, people are not teleported when stepping out)
+//Returns: docking port
+/proc/generate_transit_area(datum/shuttle/shuttle, direction, create_borders = 1)
+ //To do this, we need to find the shuttle's width and height
+ //Go through each turf in the shuttle, find the lowest x;y and the highest x;y, subtract them to get the size
+ var/low_x = 0
+ var/low_y = 0
+ var/top_x = world.maxx
+ var/top_y = world.maxy
+
+ for(var/turf/T in shuttle.linked_area)
+ if(T.x > low_x)
+ low_x = T.x
+ if(T.x < top_x)
+ top_x = T.x
+
+ if(T.y > low_y)
+ low_y = T.y
+ if(T.y < top_y)
+ top_y = T.y
+
+ var/shuttle_width = abs(top_x - low_x)
+ var/shuttle_height= abs(top_y - low_y)
+
+ //Extra space in every direction. WIthout this, you'd be able to see z2 out of your shuttle's window
+ var/buffer_space = world.view
+
+ var/datum/map_element/transit/new_transit = new()
+ new_transit.name = "[shuttle.name] - transit area"
+ new_transit.width = shuttle_width + 2*buffer_space
+ new_transit.height = shuttle_height + 2*buffer_space
+
+ //Find a suitable location for the map_element object (done automatically)
+ load_dungeon(new_transit)
+
+ //Start filling out the area
+ var/turf/t_loc = new_transit.location
+
+ if(!istype(t_loc))
+ message_admins("ERROR: Unable to generate transit area (area placement failed).")
+ return
+
+ for(var/turf/T in block(locate(t_loc.x, t_loc.y, t_loc.z), locate(t_loc.x+new_transit.width, t_loc.y+new_transit.height, t_loc.z)))
+ T.ChangeTurf(/turf/space/transit)
+ var/turf/space/transit/t_turf = T
+ t_turf.pushdirection = direction
+ t_turf.update_icon()
+
+ //Transit turfs placed - place the docking port!
+ //First, find the shuttle docking port's location relative to the shuttle's lower left corner
+ var/port_x = shuttle.linked_port.x - top_x
+ var/port_y = shuttle.linked_port.y - top_y
+
+ //Now calculate the location of the destination docking port
+ //Docking ports dock like this: [ ][->][<-][ ], so the resulting coordinates will have to be shifted 1 turf in the direction of the shuttle docking port
+ //Otherwise both arrows will be on the same turf
+ var/dest_x = t_loc.x + buffer_space + port_x
+ var/dest_y = t_loc.y + buffer_space + port_y
+ var/turf/destination_turf = get_step(locate(dest_x, dest_y, t_loc.z), shuttle.linked_port.dir)
+
+ var/obj/docking_port/destination/transit/result = new(destination_turf)
+ result.dir = turn(shuttle.linked_port.dir, 180)
+
+ if(create_borders)
+ result.generate_borders = TRUE
+ //Border generation is done by the docking port
+
+ return result
\ No newline at end of file
diff --git a/html/changelogs/unid-atr.yml b/html/changelogs/unid-atr.yml
new file mode 100644
index 00000000000..dac2e1eaf30
--- /dev/null
+++ b/html/changelogs/unid-atr.yml
@@ -0,0 +1,6 @@
+author: Unid
+
+delete-after: True
+
+changes:
+- experiment: Added a new admin button to shuttle magic - it generates a transit area for the selected shuttle.
diff --git a/vgstation13.dme b/vgstation13.dme
index 5c9a2a58b8f..e5aca9dbc24 100644
--- a/vgstation13.dme
+++ b/vgstation13.dme
@@ -1668,6 +1668,7 @@
#include "code\modules\projectiles\projectile\rocket.dm"
#include "code\modules\projectiles\projectile\special.dm"
#include "code\modules\randomMaps\dungeons.dm"
+#include "code\modules\randomMaps\transit.dm"
#include "code\modules\randomMaps\vault_definitions.dm"
#include "code\modules\randomMaps\vaults.dm"
#include "code\modules\RCD\engie.dm"