diff --git a/code/__DEFINES/components.dm b/code/__DEFINES/components.dm
index fe88832e8d..3841a086e9 100644
--- a/code/__DEFINES/components.dm
+++ b/code/__DEFINES/components.dm
@@ -120,7 +120,7 @@
#define COMSIG_MOVABLE_DISPOSING "movable_disposing" //called when the movable is added to a disposal holder object for disposal movement: (obj/structure/disposalholder/holder, obj/machinery/disposal/source)
// /mind signals
-#define COMSIG_MIND_TRANSFER "mind_transfer" //from base of mind/transfer_to(): (new_character)
+#define COMSIG_MIND_TRANSFER "mind_transfer" //from base of mind/transfer_to(): (new_character, old_character)
// /mob signals
#define COMSIG_MOB_DEATH "mob_death" //from base of mob/death(): (gibbed)
diff --git a/code/datums/components/virtual_reality.dm b/code/datums/components/virtual_reality.dm
index 0bc1f9d8ec..8421f18dbb 100644
--- a/code/datums/components/virtual_reality.dm
+++ b/code/datums/components/virtual_reality.dm
@@ -1,36 +1,54 @@
/datum/component/virtual_reality
- dupe_mode = COMPONENT_DUPE_UNIQUE
- var/datum/mind/real_mind // where is my mind t. pixies
+ dupe_mode = COMPONENT_DUPE_ALLOWED //mindswap memes, shouldn't stack up otherwise.
+ var/datum/mind/mastermind // where is my mind t. pixies
var/datum/mind/current_mind
var/obj/machinery/vr_sleeper/vr_sleeper
var/datum/action/quit_vr/quit_action
var/you_die_in_the_game_you_die_for_real = FALSE
-/datum/component/virtual_reality/Initialize(datum/mind/mastermind, obj/machinery/vr_sleeper/gaming_pod, yolo = FALSE, new_char = TRUE)
- if(!ismob(parent))
+/datum/component/virtual_reality/Initialize(mob/M, obj/machinery/vr_sleeper/gaming_pod, yolo = FALSE, new_char = TRUE)
+ if(!ismob(parent) || !istype(M))
return COMPONENT_INCOMPATIBLE
-
+ var/mob/vr_M
+ mastermind = M.mind
+ RegisterSignal(mastermind, COMSIG_MIND_TRANSFER, .proc/switch_player)
+ RegisterSignal(mastermind, COMSIG_MOB_KEY_CHANGE, .proc/switch_player)
you_die_in_the_game_you_die_for_real = yolo
quit_action = new()
- quit_action.Grant(parent)
+ RegisterSignal(quit_action, COMSIG_ACTION_TRIGGER, .proc/revert_to_reality)
if(gaming_pod)
vr_sleeper = gaming_pod
RegisterSignal(vr_sleeper, COMSIG_ATOM_EMAG_ACT, .proc/you_only_live_once)
- RegisterSignal(vr_sleeper, COMSIG_MACHINE_EJECT_OCCUPANT, .proc/revert_to_reality)
+ RegisterSignal(vr_sleeper, COMSIG_MACHINE_EJECT_OCCUPANT, .proc/virtual_reality_in_a_virtual_reality)
+ vr_M.ckey = M.ckey
+ SStgui.close_user_uis(M, src)
/datum/component/virtual_reality/RegisterWithParent()
var/mob/M = parent
current_mind = M.mind
- RegisterSignal(parent, COMSIG_MOB_DEATH, .proc/game_over)
- RegisterSignal(parent, COMSIG_MOB_KEY_CHANGE, .proc/pass_me_the_remote)
- RegisterSignal(current_mind, COMSIG_MIND_TRANSFER, .proc/pass_me_the_remote)
- RegisterSignal(parent, COMSIG_MOB_GHOSTIZE, .proc/be_a_quitter)
- RegisterSignal(quit_action, COMSIG_ACTION_TRIGGER, .proc/revert_to_reality)
+ quit_action.Grant(M)
+ RegisterSignal(M, list(COMSIG_MOB_DEATH, COMSIG_PARENT_QDELETED), .proc/game_over)
+ RegisterSignal(M, list(COMSIG_MOB_KEY_CHANGE, COMSIG_MIND_TRANSFER), .proc/pass_me_the_remote)
+ RegisterSignal(M, COMSIG_MOB_GHOSTIZE, .proc/be_a_quitter)
+ mastermind.current.audiovisual_redirect = M
/datum/component/virtual_reality/UnregisterFromParent()
- UnregisterSignal(parent, list(COMSIG_MOB_DEATH, COMSIG_MOB_KEY_CHANGE, COMSIG_MOB_GHOSTIZE))
+ quit_action.Remove(parent)
+ UnregisterSignal(parent, list(COMSIG_MOB_DEATH, COMSIG_PARENT_QDELETED, COMSIG_MOB_KEY_CHANGE, COMSIG_MOB_GHOSTIZE))
UnregisterSignal(quit_action, COMSIG_ACTION_TRIGGER)
UnregisterSignal(current_mind, COMSIG_MIND_TRANSFER)
+ current_mind = null
+ mastermind.current.audiovisual_redirect = null
+
+/datum/component/virtual_reality/proc/switch_player(datum/source, mob/new_mob, mob/old_mob)
+ if(vr_sleeper || !new_mob.mind)
+ // Machineries currently don't deal up with the occupant being polymorphed or the such. Or the admin dared to use outdated transformation procs.
+ virtual_reality_in_a_virtual_reality(FALSE, new_mob)
+ return
+ old_mob.audiovisual_redirect = null
+ old_mob.inception = null
+ new_mob.audiovisual_redirect = parent
+ new_mob.inception = src
/datum/component/virtual_reality/proc/action_trigger(datum/signal_source, datum/action/source)
if(source != quit_action)
@@ -44,7 +62,7 @@
return TRUE
/datum/component/virtual_reality/proc/pass_me_the_remote(datum/source, mob/new_mob)
- if(new_mob == real_mind.current)
+ if(new_mob == mastermind.current)
revert_to_reality(source)
new_mob.TakeComponent(src)
return TRUE
@@ -54,35 +72,47 @@
return COMPONENT_INCOMPATIBLE
/datum/component/virtual_reality/proc/revert_to_reality(datum/source)
- quit_it(FALSE)
+ quit_it()
/datum/component/virtual_reality/proc/game_over(datum/source)
- quit_it(TRUE)
+ quit_it(TRUE, TRUE)
-/datum/component/virtual_reality/proc/be_a_quitter(datum/source)
- quit_it(FALSE)
+/datum/component/virtual_reality/proc/be_a_quitter(datum/source, can_reenter_corpse)
+ quit_it()
return COMPONENT_BLOCK_GHOSTING
-/datum/component/virtual_reality/proc/quit_it(deathcheck = FALSE)
+/datum/component/virtual_reality/proc/virtual_reality_in_a_virtual_reality(killme = FALSE, mob_override)
var/mob/M = parent
- if(!real_mind)
- to_chat(M, "You feel as if something terrible happened, you try to wake up from this dream... but you can't...")
+ if(!QDELETED(M.inception) && M.inception.parent)
+ M.inception.virtual_reality_in_a_virtual_reality()
+ quit_it(FALSE, killme, mob_override)
+ if(killme)
+ M.death(FALSE)
+
+/datum/component/virtual_reality/proc/quit_it(deathcheck = FALSE, cleanup = FALSE, mob_override)
+ var/mob/M = parent
+ var/mob/dreamer = mob_override ? mob_override : mastermind.current
+ if(!mastermind)
+ to_chat(M, "You feel like something terrible happened. You try to wake up from this dream, but you can't...")
else
- real_mind.current.audiovisual_redirect = null
- real_mind.current.ckey = M.ckey
- real_mind.current.stop_sound_channel(CHANNEL_HEARTBEAT)
+ dreamer.ckey = M.ckey
+ dreamer.stop_sound_channel(CHANNEL_HEARTBEAT)
+ dreamer.inception = null
+ dreamer.audiovisual_redirect = null
if(deathcheck)
+ if(you_die_in_the_game_you_die_for_real)
+ to_chat(mastermind, "You feel everything fading away...")
+ dreamer.death(FALSE)
+ if(cleanup)
var/obj/effect/vr_clean_master/cleanbot = locate() in get_area(M)
if(cleanbot)
LAZYADD(cleanbot.corpse_party, M)
- if(you_die_in_the_game_you_die_for_real)
- to_chat(real_mind, "You feel everything fading away...")
- real_mind.current.death(0)
- if(vr_sleeper)
- vr_sleeper.vr_mob = null
- vr_sleeper = null
+ if(vr_sleeper)
+ vr_sleeper.vr_mob = null
+ vr_sleeper = null
qdel(src)
/datum/component/virtual_reality/Destroy()
- QDEL_NULL(quit_action)
- return ..()
\ No newline at end of file
+ var/datum/action/quit_vr/delet_me = quit_action
+ . = ..()
+ qdel(delet_me)
\ No newline at end of file
diff --git a/code/datums/mind.dm b/code/datums/mind.dm
index 2844a59d61..07414a3362 100644
--- a/code/datums/mind.dm
+++ b/code/datums/mind.dm
@@ -87,6 +87,7 @@
return language_holder
/datum/mind/proc/transfer_to(mob/new_character, var/force_key_move = 0)
+ var/old_character = current
if(current) // remove ourself from our old body's mind variable
current.mind = null
SStgui.on_transfer(current, new_character)
@@ -97,7 +98,7 @@
if(key)
if(new_character.key != key) //if we're transferring into a body with a key associated which is not ours
- new_character.ghostize(1) //we'll need to ghostize so that key isn't mobless.
+ new_character.ghostize(TRUE, TRUE) //we'll need to ghostize so that key isn't mobless.
else
key = new_character.key
@@ -121,7 +122,7 @@
transfer_martial_arts(new_character)
if(active || force_key_move)
new_character.key = key //now transfer the key to link the client to our new body
- SEND_SIGNAL(src, COMSIG_MIND_TRANSFER, new_character)
+ SEND_SIGNAL(src, COMSIG_MIND_TRANSFER, new_character, old_character)
//CIT CHANGE - makes arousal update when transfering bodies
if(isliving(new_character)) //New humans and such are by default enabled arousal. Let's always use the new mind's prefs.
diff --git a/code/modules/VR/vr_sleeper.dm b/code/modules/VR/vr_sleeper.dm
index afc8de6e21..86816ba3cb 100644
--- a/code/modules/VR/vr_sleeper.dm
+++ b/code/modules/VR/vr_sleeper.dm
@@ -93,10 +93,7 @@
if(human_occupant && human_occupant.mind && usr == occupant)
to_chat(occupant, "Transferring to virtual reality...")
if(vr_mob && vr_mob.stat == CONSCIOUS && !vr_mob.GetComponent(/datum/component/virtual_reality))
- SStgui.close_user_uis(occupant, src)
- vr_mob.AddComponent(/datum/component/virtual_reality, human_occupant.mind, src, you_die_in_the_game_you_die_for_real)
- human_occupant.audiovisual_redirect = vr_mob
- vr_mob.ckey = human_occupant.ckey
+ vr_mob.AddComponent(/datum/component/virtual_reality, human_occupant, src, you_die_in_the_game_you_die_for_real)
to_chat(vr_mob, "Transfer successful! You are now playing as [vr_mob] in VR!")
else
if(allow_creating_vr_mobs)
@@ -164,10 +161,7 @@
vr_H.updateappearance(TRUE, TRUE, TRUE)
if(!transfer || !H.mind)
return
- vr_mob.AddComponent(/datum/component/virtual_reality, H.mind, src, you_die_in_the_game_you_die_for_real)
- SStgui.close_user_uis(H, src)
- H.audiovisual_redirect = vr_mob
- vr_mob.ckey = H.ckey
+ vr_mob.AddComponent(/datum/component/virtual_reality, H, src, you_die_in_the_game_you_die_for_real)
/obj/machinery/vr_sleeper/proc/cleanup_vr_mob()
if(vr_mob)
diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm
index a018acb165..e074631640 100644
--- a/code/modules/mob/dead/observer/observer.dm
+++ b/code/modules/mob/dead/observer/observer.dm
@@ -260,9 +260,9 @@ Transfer_mind is there to check if mob is being deleted/not going to have a body
Works together with spawning an observer, noted above.
*/
-/mob/proc/ghostize(can_reenter_corpse = TRUE, send_the_signal = TRUE)
- if(!key || cmptext(copytext(key,1,2),"@") || (send_the_signal && SEND_SIGNAL(src, COMSIG_MOB_GHOSTIZE, can_reenter_corpse) & COMPONENT_BLOCK_GHOSTING))
- return //mob has no key, is an aghost or some component hijack.
+/mob/proc/ghostize(can_reenter_corpse = TRUE, special = FALSE)
+ if(!key || cmptext(copytext(key,1,2),"@") || (!special && SEND_SIGNAL(src, COMSIG_MOB_GHOSTIZE, can_reenter_corpse, special) & COMPONENT_BLOCK_GHOSTING))
+ return //mob has no key, is an aghost or some component hijacked.
stop_sound_channel(CHANNEL_HEARTBEAT) //Stop heartbeat sounds because You Are A Ghost Now
var/mob/dead/observer/ghost = new(src) // Transfer safety to observer spawning proc.
SStgui.on_transfer(src, ghost) // Transfer NanoUIs.
@@ -280,7 +280,7 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
set name = "Ghost"
set desc = "Relinquish your life and enter the land of the dead."
- if(SEND_SIGNAL(src, COMSIG_MOB_GHOSTIZE, (stat == DEAD) ? TRUE : FALSE) & COMPONENT_BLOCK_GHOSTING)
+ if(SEND_SIGNAL(src, COMSIG_MOB_GHOSTIZE, (stat == DEAD) ? TRUE : FALSE, FALSE) & COMPONENT_BLOCK_GHOSTING)
return
// CITADEL EDIT
@@ -309,7 +309,7 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
set name = "Ghost"
set desc = "Relinquish your life and enter the land of the dead."
- if(SEND_SIGNAL(src, COMSIG_MOB_GHOSTIZE, FALSE) & COMPONENT_BLOCK_GHOSTING)
+ if(SEND_SIGNAL(src, COMSIG_MOB_GHOSTIZE, FALSE, FALSE) & COMPONENT_BLOCK_GHOSTING)
return
var/response = alert(src, "Are you -sure- you want to ghost?\n(You are alive. If you ghost whilst still alive you won't be able to re-enter this round! You can't change your mind so choose wisely!!)","Are you sure you want to ghost?","Ghost","Stay in body")
diff --git a/code/modules/mob/death.dm b/code/modules/mob/death.dm
index 8c335a9e8c..f10d4baa20 100644
--- a/code/modules/mob/death.dm
+++ b/code/modules/mob/death.dm
@@ -11,4 +11,6 @@
/mob/proc/death(gibbed)
SEND_SIGNAL(src, COMSIG_MOB_DEATH, gibbed)
+ if(!QDELETED(inception) && inception.parent)
+ inception.virtual_reality_in_a_virtual_reality(TRUE)
return
\ No newline at end of file
diff --git a/code/modules/mob/living/simple_animal/guardian/guardian.dm b/code/modules/mob/living/simple_animal/guardian/guardian.dm
index 8a5d06392f..7ce2a11314 100644
--- a/code/modules/mob/living/simple_animal/guardian/guardian.dm
+++ b/code/modules/mob/living/simple_animal/guardian/guardian.dm
@@ -426,7 +426,7 @@ GLOBAL_LIST_EMPTY(parasites) //all currently existing/living guardians
to_chat(G, "Your user reset you, and your body was taken over by a ghost. Looks like they weren't happy with your performance.")
to_chat(src, "Your [G.real_name] has been successfully reset.")
message_admins("[key_name_admin(C)] has taken control of ([key_name_admin(G)])")
- G.ghostize(FALSE, FALSE)
+ G.ghostize(FALSE)
G.setthemename(G.namedatum.theme) //give it a new color, to show it's a new person
C.transfer_key(G)
G.reset = 1
diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm
index 113f8a54c8..d1f83e69de 100644
--- a/code/modules/mob/mob.dm
+++ b/code/modules/mob/mob.dm
@@ -12,6 +12,8 @@
var/mob/dead/observe = M
observe.reset_perspective(null)
qdel(hud_used)
+ if(!QDELETED(inception) && inception.parent)
+ inception.virtual_reality_in_a_virtual_reality(TRUE)
for(var/cc in client_colours)
qdel(cc)
client_colours = null
@@ -438,7 +440,7 @@
/mob/proc/transfer_key(mob/new_mob, send_signal = TRUE)
if(send_signal)
- SEND_SIGNAL(src, COMSIG_MOB_KEY_CHANGE, new_mob)
+ SEND_SIGNAL(src, COMSIG_MOB_KEY_CHANGE, new_mob, src)
new_mob.key = key
/mob/verb/cancel_camera()
diff --git a/code/modules/mob/mob_defines.dm b/code/modules/mob/mob_defines.dm
index a0126f5fdd..c016fac8b0 100644
--- a/code/modules/mob/mob_defines.dm
+++ b/code/modules/mob/mob_defines.dm
@@ -112,3 +112,5 @@
var/registered_z
var/mob/audiovisual_redirect //Mob to redirect messages, speech, and sounds to
+
+ var/datum/component/virtual_reality/inception
\ No newline at end of file
diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm
index 80a65f56c6..8b26660cbb 100644
--- a/code/modules/mob/mob_helpers.dm
+++ b/code/modules/mob/mob_helpers.dm
@@ -429,7 +429,7 @@ It's fairly easy to fix if dealing with single letters but not so much with comp
var/mob/dead/observer/C = pick(candidates)
to_chat(M, "Your mob has been taken over by a ghost!")
message_admins("[key_name_admin(C)] has taken control of ([key_name_admin(M)])")
- M.ghostize(FALSE, FALSE)
+ M.ghostize(FALSE, TRUE)
C.transfer_key(M, FALSE)
return TRUE
else
diff --git a/code/modules/spells/spell_types/mind_transfer.dm b/code/modules/spells/spell_types/mind_transfer.dm
index 021f57e873..a5e6ed202a 100644
--- a/code/modules/spells/spell_types/mind_transfer.dm
+++ b/code/modules/spells/spell_types/mind_transfer.dm
@@ -70,7 +70,7 @@ Also, you never added distance checking after target is selected. I've went ahea
var/mob/living/caster = user//The wizard/whomever doing the body transferring.
//MIND TRANSFER BEGIN
- var/mob/dead/observer/ghost = victim.ghostize(FALSE, FALSE)
+ var/mob/dead/observer/ghost = victim.ghostize(FALSE, TRUE)
caster.mind.transfer_to(victim)
ghost.mind.transfer_to(caster)