diff --git a/code/game/sound.dm b/code/game/sound.dm
index 12cee118b5..cceed31cfb 100644
--- a/code/game/sound.dm
+++ b/code/game/sound.dm
@@ -27,7 +27,10 @@
if(get_dist(M, turf_source) <= maxdistance)
M.playsound_local(turf_source, soundin, vol, vary, frequency, falloff, channel, pressure_affected, S, soundenvwet, soundenvdry)
-/mob/proc/playsound_local(turf/turf_source, soundin, vol as num, vary, frequency, falloff, channel = 0, pressure_affected = TRUE, sound/S, envwet = -10000, envdry = 0)
+/mob/proc/playsound_local(turf/turf_source, soundin, vol as num, vary, frequency, falloff, channel = 0, pressure_affected = TRUE, sound/S, envwet = -10000, envdry = 0, manual_x, manual_y)
+ if(audiovisual_redirect)
+ var/turf/T = get_turf(src)
+ audiovisual_redirect.playsound_local(turf_source, soundin, vol, vary, frequency, falloff, channel, pressure_affected, S, 0, -1000, turf_source.x - T.x, turf_source.y - T.y)
if(!client || !can_hear())
return
@@ -49,7 +52,9 @@
var/turf/T = get_turf(src)
//sound volume falloff with distance
- var/distance = get_dist(T, turf_source)
+ var/distance = 0
+ if(!manual_x && !manual_y)
+ distance = get_dist(T, turf_source)
S.volume -= max(distance - world.view, 0) * 2 //multiplicative falloff to add on top of natural audio falloff.
@@ -77,9 +82,17 @@
if(S.volume <= 0)
return //No sound
- var/dx = turf_source.x - T.x // Hearing from the right/left
+ var/dx = 0 // Hearing from the right/left
+ if(!manual_x)
+ dx = turf_source.x - T.x
+ else
+ dx = manual_x
S.x = dx
- var/dz = turf_source.y - T.y // Hearing from infront/behind
+ var/dz = 0 // Hearing from infront/behind
+ if(!manual_x)
+ dz = turf_source.y - T.y
+ else
+ dz = manual_y
S.z = dz
// The y value is for above your head, but there is no ceiling in 2d spessmens.
S.y = 1
diff --git a/code/modules/VR/vr_human.dm b/code/modules/VR/vr_human.dm
index 56f0986fef..53a4bbe540 100644
--- a/code/modules/VR/vr_human.dm
+++ b/code/modules/VR/vr_human.dm
@@ -35,6 +35,7 @@
/mob/living/carbon/human/virtual_reality/proc/revert_to_reality(deathchecks = TRUE)
if(real_mind && mind)
+ real_mind.current.audiovisual_redirect = null
real_mind.current.ckey = ckey
real_mind.current.stop_sound_channel(CHANNEL_HEARTBEAT)
if(deathchecks && vr_sleeper)
diff --git a/code/modules/VR/vr_sleeper.dm b/code/modules/VR/vr_sleeper.dm
index eed6f9b3ae..7169378179 100644
--- a/code/modules/VR/vr_sleeper.dm
+++ b/code/modules/VR/vr_sleeper.dm
@@ -93,6 +93,7 @@
to_chat(occupant, "Transferring to virtual reality...")
if(vr_human && vr_human.stat == CONSCIOUS && !vr_human.real_mind)
SStgui.close_user_uis(occupant, src)
+ human_occupant.audiovisual_redirect = vr_human
vr_human.real_mind = human_occupant.mind
vr_human.ckey = human_occupant.ckey
to_chat(vr_human, "Transfer successful! You are now playing as [vr_human] in VR!")
@@ -171,6 +172,7 @@
O.equip(vr_human)
if(transfer && H.mind)
SStgui.close_user_uis(H, src)
+ H.audiovisual_redirect = vr_human
vr_human.ckey = H.ckey
/obj/machinery/vr_sleeper/proc/cleanup_vr_human()
diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm
index c32b6ace78..ef91c6843d 100644
--- a/code/modules/mob/living/living.dm
+++ b/code/modules/mob/living/living.dm
@@ -1067,10 +1067,12 @@
update_canmove() //if the mob was asleep inside a container and then got forceMoved out we need to make them fall.
/mob/living/proc/update_z(new_z) // 1+ to register, null to unregister
+ if(isnull(new_z) && audiovisual_redirect)
+ return
if (registered_z != new_z)
if (registered_z)
SSmobs.clients_by_zlevel[registered_z] -= src
- if (client)
+ if (client || audiovisual_redirect)
if (new_z)
SSmobs.clients_by_zlevel[new_z] += src
for (var/I in length(SSidlenpcpool.idle_mobs_by_zlevel[new_z]) to 1 step -1) //Backwards loop because we're removing (guarantees optimal rather than worst-case performance), it's fine to use .len here but doesn't compile on 511
diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm
index 79215ff5f5..30e58ebb44 100644
--- a/code/modules/mob/mob.dm
+++ b/code/modules/mob/mob.dm
@@ -76,6 +76,8 @@
return "a ... thing?"
/mob/proc/show_message(msg, type, alt_msg, alt_type)//Message, type of message (1 or 2), alternative message, alt message type (1 or 2)
+ if(audiovisual_redirect)
+ audiovisual_redirect.show_message(msg ? "[msg]" : null, type, alt_msg ? "[alt_msg]" : null, alt_type)
if(!client)
return
diff --git a/code/modules/mob/mob_defines.dm b/code/modules/mob/mob_defines.dm
index 298fee46cd..a0126f5fdd 100644
--- a/code/modules/mob/mob_defines.dm
+++ b/code/modules/mob/mob_defines.dm
@@ -110,3 +110,5 @@
var/datum/click_intercept
var/registered_z
+
+ var/mob/audiovisual_redirect //Mob to redirect messages, speech, and sounds to