diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm
index 559d690c1b2..21c44e8ea25 100644
--- a/code/__DEFINES/traits.dm
+++ b/code/__DEFINES/traits.dm
@@ -652,6 +652,9 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
#define TRAIT_CHASM_STOPPED "chasm_stopped"
///The effects of the immerse element will be halted while this trait is present.
#define TRAIT_IMMERSE_STOPPED "immerse_stopped"
+/// The effects of hyperspace drift are blocked when the tile has this trait
+#define TRAIT_HYPERSPACE_STOPPED "hyperspace_stopped"
+
///Turf slowdown will be ignored when this trait is added to a turf.
#define TRAIT_TURF_IGNORE_SLOWDOWN "turf_ignore_slowdown"
///Mobs won't slip on a wet turf while it has this trait
diff --git a/code/datums/components/shuttle_cling.dm b/code/datums/components/shuttle_cling.dm
index 63e11947b6e..f045555c98a 100644
--- a/code/datums/components/shuttle_cling.dm
+++ b/code/datums/components/shuttle_cling.dm
@@ -121,7 +121,7 @@
///Are we on a hyperspace tile? There's some special bullshit with lattices so we just wrap this check
/datum/component/shuttle_cling/proc/is_on_hyperspace(atom/movable/clinger)
- if(istype(clinger.loc, hyperspace_type) && !(locate(/obj/structure/lattice) in clinger.loc))
+ if(istype(clinger.loc, hyperspace_type) && !HAS_TRAIT(clinger.loc, TRAIT_HYPERSPACE_STOPPED))
return TRUE
return FALSE
diff --git a/code/game/objects/structures/lattice.dm b/code/game/objects/structures/lattice.dm
index fc9821aedd7..40119ca5606 100644
--- a/code/game/objects/structures/lattice.dm
+++ b/code/game/objects/structures/lattice.dm
@@ -16,7 +16,7 @@
canSmoothWith = SMOOTH_GROUP_LATTICE + SMOOTH_GROUP_WALLS + SMOOTH_GROUP_OPEN_FLOOR
var/number_of_mats = 1
var/build_material = /obj/item/stack/rods
- var/list/give_turf_traits = list(TRAIT_CHASM_STOPPED)
+ var/list/give_turf_traits = list(TRAIT_CHASM_STOPPED, TRAIT_HYPERSPACE_STOPPED)
/obj/structure/lattice/Initialize(mapload)
. = ..()
@@ -99,7 +99,7 @@
smoothing_groups = SMOOTH_GROUP_CATWALK + SMOOTH_GROUP_LATTICE + SMOOTH_GROUP_OPEN_FLOOR
canSmoothWith = SMOOTH_GROUP_CATWALK
obj_flags = CAN_BE_HIT | BLOCK_Z_OUT_DOWN | BLOCK_Z_IN_UP
- give_turf_traits = list(TRAIT_TURF_IGNORE_SLOWDOWN, TRAIT_LAVA_STOPPED, TRAIT_CHASM_STOPPED, TRAIT_IMMERSE_STOPPED)
+ give_turf_traits = list(TRAIT_TURF_IGNORE_SLOWDOWN, TRAIT_LAVA_STOPPED, TRAIT_CHASM_STOPPED, TRAIT_IMMERSE_STOPPED, TRAIT_HYPERSPACE_STOPPED)
/obj/structure/lattice/catwalk/Initialize(mapload)
. = ..()
@@ -154,7 +154,7 @@
canSmoothWith = SMOOTH_GROUP_LATTICE
obj_flags = CAN_BE_HIT | BLOCK_Z_OUT_DOWN | BLOCK_Z_IN_UP
resistance_flags = FIRE_PROOF | LAVA_PROOF
- give_turf_traits = list(TRAIT_LAVA_STOPPED, TRAIT_CHASM_STOPPED, TRAIT_IMMERSE_STOPPED)
+ give_turf_traits = list(TRAIT_LAVA_STOPPED, TRAIT_CHASM_STOPPED, TRAIT_IMMERSE_STOPPED, TRAIT_HYPERSPACE_STOPPED)
/obj/structure/lattice/lava/deconstruction_hints(mob/user)
return span_notice("The rods look like they could be cut, but the heat treatment will shatter off. There's space for a tile.")
diff --git a/code/game/turfs/open/space/transit.dm b/code/game/turfs/open/space/transit.dm
index 21ecbcb45b2..3e3f7b0c55e 100644
--- a/code/game/turfs/open/space/transit.dm
+++ b/code/game/turfs/open/space/transit.dm
@@ -36,13 +36,13 @@
/turf/open/space/transit/proc/initialize_drifting(atom/entered, atom/movable/enterer)
SIGNAL_HANDLER
- if(enterer && !HAS_TRAIT(enterer, TRAIT_HYPERSPACED))
+ if(enterer && !HAS_TRAIT(enterer, TRAIT_HYPERSPACED) && !HAS_TRAIT(src, TRAIT_HYPERSPACE_STOPPED))
enterer.AddComponent(/datum/component/shuttle_cling, REVERSE_DIR(dir))
/turf/open/space/transit/proc/initialize_drifting_but_from_initialize(atom/movable/location, atom/movable/enterer, mapload)
SIGNAL_HANDLER
- if(!mapload && !istype(enterer, /obj/docking_port) && !enterer.anchored)
+ if(!mapload && !enterer.anchored)
INVOKE_ASYNC(src, PROC_REF(initialize_drifting), src, enterer)
/turf/open/space/transit/Exited(atom/movable/gone, direction)
diff --git a/code/modules/shuttle/shuttle_events/_shuttle_events.dm b/code/modules/shuttle/shuttle_events/_shuttle_events.dm
index d73fba2282b..6736428c02a 100644
--- a/code/modules/shuttle/shuttle_events/_shuttle_events.dm
+++ b/code/modules/shuttle/shuttle_events/_shuttle_events.dm
@@ -106,10 +106,12 @@
. = ..()
if(!.)
- return FALSE
+ return
- if(!LAZYLEN(spawning_list) && self_destruct_when_empty)
- return SHUTTLE_EVENT_CLEAR
+ if(!LAZYLEN(spawning_list))
+ if(self_destruct_when_empty)
+ return SHUTTLE_EVENT_CLEAR
+ return
if(prob(spawn_probability_per_process))
for(var/i in 1 to spawns_per_spawn)
diff --git a/code/modules/shuttle/shuttle_events/carp.dm b/code/modules/shuttle/shuttle_events/carp.dm
index ac040bd66e6..18529f1c028 100644
--- a/code/modules/shuttle/shuttle_events/carp.dm
+++ b/code/modules/shuttle/shuttle_events/carp.dm
@@ -9,6 +9,7 @@
spawn_probability_per_process = 20
remove_from_list_when_spawned = TRUE
+ self_destruct_when_empty = TRUE
/datum/shuttle_event/simple_spawner/carp/post_spawn(mob/living/basic/carp/carpee)
. = ..()
@@ -26,6 +27,7 @@
spawn_probability_per_process = 20
remove_from_list_when_spawned = TRUE
+ self_destruct_when_empty = TRUE
///Spawn a bunch of friendly carp to view from inside the shuttle! May occassionally pass through and nibble some windows, but are otherwise pretty harmless
/datum/shuttle_event/simple_spawner/carp/friendly
diff --git a/code/modules/shuttle/shuttle_events/misc.dm b/code/modules/shuttle/shuttle_events/misc.dm
index c0644f6e491..9f6db855a76 100644
--- a/code/modules/shuttle/shuttle_events/misc.dm
+++ b/code/modules/shuttle/shuttle_events/misc.dm
@@ -20,6 +20,7 @@
spawn_probability_per_process = 5
remove_from_list_when_spawned = TRUE
+ self_destruct_when_empty = TRUE
///Just spawn random maint garbage
/datum/shuttle_event/simple_spawner/maintenance
diff --git a/code/modules/shuttle/shuttle_events/player_controlled.dm b/code/modules/shuttle/shuttle_events/player_controlled.dm
index ef7e2c33170..40bc29f8017 100644
--- a/code/modules/shuttle/shuttle_events/player_controlled.dm
+++ b/code/modules/shuttle/shuttle_events/player_controlled.dm
@@ -15,13 +15,18 @@
else
..()
+/// Attempt to grant control of a mob to ghosts before spawning it in. if spawn_anyway_if_no_player = TRUE, we spawn the mob even if there's no ghosts
/datum/shuttle_event/simple_spawner/player_controlled/proc/try_grant_ghost_control(spawn_type)
var/list/candidates = poll_ghost_candidates(ghost_alert_string + " (Warning: you will not be able to return to your body!)", role_type, FALSE, 10 SECONDS)
- var/mob/dead/observer/candidate = pick(candidates)
- if(candidate || spawn_anyway_if_no_player)
- var/mob/living/new_mob = new spawn_type (get_turf(get_spawn_turf()))
- if(candidate)
- new_mob.ckey = candidate.ckey
+
+ if(!candidates.len && !spawn_anyway_if_no_player)
+ return
+
+ var/mob/living/new_mob = new spawn_type (get_turf(get_spawn_turf()))
+
+ if(candidates.len)
+ var/mob/dead/observer/candidate = pick(candidates)
+ new_mob.ckey = candidate.ckey
post_spawn(new_mob)
///BACK FOR REVENGE!!!
@@ -37,6 +42,7 @@
spawn_anyway_if_no_player = FALSE
ghost_alert_string = "Would you like to be an alien queen shot at the shuttle?"
remove_from_list_when_spawned = TRUE
+ self_destruct_when_empty = TRUE
role_type = ROLE_ALIEN
@@ -53,6 +59,7 @@
spawn_anyway_if_no_player = TRUE
ghost_alert_string = "Would you like to be a space carp to pester the emergency shuttle?"
remove_from_list_when_spawned = TRUE
+ self_destruct_when_empty = TRUE
role_type = ROLE_SENTIENCE