diff --git a/code/__DEFINES/layers.dm b/code/__DEFINES/layers.dm
index c568e9fe7bc..fb3b61cd80f 100644
--- a/code/__DEFINES/layers.dm
+++ b/code/__DEFINES/layers.dm
@@ -39,6 +39,7 @@
#define SPACEVINE_LAYER 4.8
#define SPACEVINE_MOB_LAYER 4.9
//#define FLY_LAYER 5 //For easy recordkeeping; this is a byond define
+#define RIPPLE_LAYER 5.1
#define GHOST_LAYER 6
#define AREA_LAYER 10
diff --git a/code/__DEFINES/shuttles.dm b/code/__DEFINES/shuttles.dm
index 9fbd05691e1..7fbaf6d8c9c 100644
--- a/code/__DEFINES/shuttles.dm
+++ b/code/__DEFINES/shuttles.dm
@@ -25,3 +25,7 @@
#define UNLAUNCHED 0
#define ENDGAME_LAUNCHED 1
#define EARLY_LAUNCHED 2
+
+// Ripples, effects that signal a shuttle's arrival
+#define SHUTTLE_RIPPLE_TIME 100
+#define SHUTTLE_RIPPLE_FADEIN 50
diff --git a/code/modules/shuttle/emergency.dm b/code/modules/shuttle/emergency.dm
index ff9439ecfba..9c93968aced 100644
--- a/code/modules/shuttle/emergency.dm
+++ b/code/modules/shuttle/emergency.dm
@@ -259,8 +259,18 @@
/obj/docking_port/mobile/emergency/check()
if(!timer)
return
-
var/time_left = timeLeft(1)
+
+ // The emergency shuttle doesn't work like others so this
+ // ripple check is slightly different
+ if(!ripples.len && (time_left <= SHUTTLE_RIPPLE_TIME) && ((mode == SHUTTLE_CALL) || (mode == SHUTTLE_ESCAPE)))
+ var/destination
+ if(mode == SHUTTLE_CALL)
+ destination = SSshuttle.getDock("emergency_home")
+ else if(mode == SHUTTLE_ESCAPE)
+ destination = SSshuttle.getDock("emergency_away")
+ create_ripples(destination)
+
switch(mode)
if(SHUTTLE_RECALL)
if(time_left <= 0)
diff --git a/code/modules/shuttle/ripple.dm b/code/modules/shuttle/ripple.dm
new file mode 100644
index 00000000000..a2a0947d5c3
--- /dev/null
+++ b/code/modules/shuttle/ripple.dm
@@ -0,0 +1,20 @@
+/obj/effect/ripple
+ name = "hyperspace ripple"
+ desc = "Something is coming through hyperspace, you can see the \
+ visual disturbances. It's probably best not to be on top of these \
+ when whatever is tunneling comes through."
+ icon = 'icons/effects/effects.dmi'
+ icon_state = "medi_holo"
+ anchored = TRUE
+ density = FALSE
+ layer = RIPPLE_LAYER
+ alpha = 0
+
+/obj/effect/ripple/New()
+ . = ..()
+ animate(src, alpha=255, time=SHUTTLE_RIPPLE_TIME)
+ // In case something goes wrong, delete us in a bit
+ addtimer(src, "delself", 3 * SHUTTLE_RIPPLE_TIME, FALSE)
+
+/obj/effect/ripple/proc/delself()
+ qdel(src)
diff --git a/code/modules/shuttle/shuttle.dm b/code/modules/shuttle/shuttle.dm
index 147c9f37444..5caccb742d3 100644
--- a/code/modules/shuttle/shuttle.dm
+++ b/code/modules/shuttle/shuttle.dm
@@ -189,6 +189,8 @@
// All shuttle templates are timid
var/timid = FALSE
+ var/list/ripples = list()
+
/obj/docking_port/mobile/New()
..()
if(!timid)
@@ -296,6 +298,10 @@
/obj/docking_port/mobile/proc/cancel()
if(mode != SHUTTLE_CALL)
return
+ if(ripples.len)
+ for(var/i in ripples)
+ qdel(i)
+ ripples.Cut()
timer = world.time - timeLeft(1)
mode = SHUTTLE_RECALL
@@ -365,6 +371,28 @@
qdel(src, force=TRUE)
+/obj/docking_port/mobile/proc/create_ripples(obj/docking_port/stationary/S1)
+ var/list/turfs = ripple_area(S1)
+ for(var/i in turfs)
+ ripples += new /obj/effect/ripple(i)
+
+/obj/docking_port/mobile/proc/ripple_area(obj/docking_port/stationary/S1)
+ var/list/L0 = return_ordered_turfs(x, y, z, dir, areaInstance)
+ var/list/L1 = return_ordered_turfs(S1.x, S1.y, S1.z, S1.dir)
+
+ var/list/ripple_turfs = list()
+
+ for(var/i in 1 to L0.len)
+ var/turf/T0 = L0[i]
+ if(!T0)
+ continue
+ var/turf/T1 = L1[i]
+ if(!T1)
+ continue
+ if(T0.type != T0.baseturf)
+ ripple_turfs += T1
+
+ return ripple_turfs
//this is the main proc. It instantly moves our mobile port to stationary port S1
//it handles all the generic behaviour, such as sanity checks, closing doors on the shuttle, stunning mobs, etc
/obj/docking_port/mobile/proc/dock(obj/docking_port/stationary/S1, force=FALSE)
@@ -374,11 +402,9 @@
if(status == SHUTTLE_ALREADY_DOCKED)
return status
else if(status)
- spawn(0)
- var/msg = "dock(): shuttle [src] cannot dock at [S1], \
- error: [status]"
- message_admins(msg)
- throw EXCEPTION(msg)
+ var/msg = "dock(): shuttle [src] cannot dock at [S1], \
+ error: [status]"
+ message_admins(msg)
return status
if(canMove())
@@ -416,9 +442,14 @@
A0.contents += T0
//move or squish anything in the way ship at destination
- roadkill(L1, S1.dir)
+ roadkill(L0, L1, S1.dir)
- for(var/i=1, i<=L0.len, ++i)
+ // Removes ripples
+ for(var/i in ripples)
+ qdel(i)
+ ripples.Cut()
+
+ for(var/i in 1 to L0.len)
var/turf/T0 = L0[i]
if(!T0)
continue
@@ -475,8 +506,7 @@
. = ..()
if(!.)
return
- spawn(0)
- close()
+ addtimer(src, "close", 0)
/mob/onShuttleMove()
if(!move_on_shuttle)
@@ -545,13 +575,20 @@
if(T)
var/obj/machinery/door/Door = locate() in T
if(Door)
- spawn(0)
- Door.close()
+ addtimer(Door, "close", 0)
-/obj/docking_port/mobile/proc/roadkill(list/L, dir, x, y)
+/obj/docking_port/mobile/proc/roadkill(list/L0, list/L1, dir)
var/list/hurt_mobs = list()
- for(var/turf/T in L)
- for(var/atom/movable/AM in T)
+ for(var/i in 1 to L0.len)
+ var/turf/T0 = L0[i]
+ var/turf/T1 = L1[i]
+ if(!T0 || !T1)
+ continue
+ if(T0.type == T0.baseturf)
+ continue
+ // The corresponding tile will not be changed, so no roadkill
+
+ for(var/atom/movable/AM in T1)
if(isliving(AM) && (!(AM in hurt_mobs)))
hurt_mobs |= AM
var/mob/living/M = AM
@@ -561,7 +598,7 @@
M.pulledby.stop_pulling()
M.stop_pulling()
M.visible_message("[M] is hit by \
- a bluespace ripple[M.anchored ? "":" and is thrown clear"]!",
+ a hyperspace ripple[M.anchored ? "":" and is thrown clear"]!",
"You feel an immense \
crushing pressure as the space around you ripples.")
if(M.anchored)
@@ -597,6 +634,9 @@
//used by shuttle subsystem to check timers
/obj/docking_port/mobile/proc/check()
var/timeLeft = timeLeft(1)
+ if(!ripples.len && (timeLeft <= SHUTTLE_RIPPLE_TIME) && ((mode == SHUTTLE_CALL) || (mode == SHUTTLE_RECALL)))
+ create_ripples(destination)
+
if(timeLeft <= 0)
switch(mode)
if(SHUTTLE_CALL)
diff --git a/tgstation.dme b/tgstation.dme
index 37d6a5bf53f..05db1566206 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -1699,6 +1699,7 @@
#include "code\modules\shuttle\emergency.dm"
#include "code\modules\shuttle\ferry.dm"
#include "code\modules\shuttle\manipulator.dm"
+#include "code\modules\shuttle\ripple.dm"
#include "code\modules\shuttle\shuttle.dm"
#include "code\modules\shuttle\special.dm"
#include "code\modules\shuttle\supply.dm"