diff --git a/code/__DEFINES/components.dm b/code/__DEFINES/components.dm
index dbe8cfbb62..fe88832e8d 100644
--- a/code/__DEFINES/components.dm
+++ b/code/__DEFINES/components.dm
@@ -119,8 +119,13 @@
#define COMSIG_MOVABLE_HEAR "movable_hear" //from base of atom/movable/Hear(): (message, atom/movable/speaker, message_language, raw_message, radio_freq, list/spans, message_mode)
#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)
+
// /mob signals
#define COMSIG_MOB_DEATH "mob_death" //from base of mob/death(): (gibbed)
+#define COMSIG_MOB_GHOSTIZE "mob_ghostize" //from base of mob/Ghostize() (can_reenter_corpse)
+ #define COMPONENT_BLOCK_GHOSTING 1
#define COMSIG_MOB_ALLOWED "mob_allowed" //from base of obj/allowed(mob/M): (/obj) returns bool, if TRUE the mob has id access to the obj
#define COMSIG_MOB_RECEIVE_MAGIC "mob_receive_magic" //from base of mob/anti_magic_check(): (magic, holy, protection_sources)
#define COMPONENT_BLOCK_MAGIC 1
@@ -130,6 +135,7 @@
#define COMSIG_MOB_ITEM_AFTERATTACK "mob_item_afterattack" //from base of obj/item/afterattack(): (atom/target, mob/user, proximity_flag, click_parameters)
#define COMSIG_MOB_ATTACK_RANGED "mob_attack_ranged" //from base of mob/RangedAttack(): (atom/A, params)
#define COMSIG_MOB_THROW "mob_throw" //from base of /mob/throw_item(): (atom/target)
+#define COMSIG_MOB_KEY_CHANGE "mob_key_change" //from base of /mob/transfer_key()
// /mob/living signals
#define COMSIG_LIVING_RESIST "living_resist" //from base of mob/living/resist() (/mob/living)
@@ -146,6 +152,9 @@
#define COMSIG_OBJ_BREAK "obj_break" //from base of /obj/obj_break(): (damage_flag)
#define COMSIG_OBJ_SETANCHORED "obj_setanchored" //called in /obj/structure/setAnchored(): (value)
+// /machinery signals
+#define COMSIG_MACHINE_EJECT_OCCUPANT "eject_occupant" //from base of obj/machinery/dropContents() (occupant)
+
// /obj/item signals
#define COMSIG_ITEM_ATTACK "item_attack" //from base of obj/item/attack(): (/mob/living/target, /mob/living/user)
#define COMSIG_ITEM_ATTACK_SELF "item_attack_self" //from base of obj/item/attack_self(): (/mob)
diff --git a/code/__HELPERS/game.dm b/code/__HELPERS/game.dm
index 899ef16306..133d7eefc1 100644
--- a/code/__HELPERS/game.dm
+++ b/code/__HELPERS/game.dm
@@ -479,7 +479,7 @@
G_found.client.prefs.copy_to(new_character)
new_character.dna.update_dna_identity()
- new_character.key = G_found.key
+ G_found.transfer_key(new_character, FALSE)
return new_character
diff --git a/code/datums/brain_damage/imaginary_friend.dm b/code/datums/brain_damage/imaginary_friend.dm
index 1bc7d8e3be..37fbc243d2 100644
--- a/code/datums/brain_damage/imaginary_friend.dm
+++ b/code/datums/brain_damage/imaginary_friend.dm
@@ -46,7 +46,7 @@
var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as [owner]'s imaginary friend?", ROLE_PAI, null, null, 75, friend)
if(LAZYLEN(candidates))
var/mob/dead/observer/C = pick(candidates)
- friend.key = C.key
+ C.transfer_key(friend, FALSE)
friend_initialized = TRUE
else
qdel(src)
diff --git a/code/datums/brain_damage/split_personality.dm b/code/datums/brain_damage/split_personality.dm
index 9ce65717f1..6a2f8682a5 100644
--- a/code/datums/brain_damage/split_personality.dm
+++ b/code/datums/brain_damage/split_personality.dm
@@ -26,7 +26,7 @@
var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as [owner]'s split personality?", ROLE_PAI, null, null, 75, stranger_backseat)
if(LAZYLEN(candidates))
var/mob/dead/observer/C = pick(candidates)
- stranger_backseat.key = C.key
+ C.transfer_key(stranger_backseat, FALSE)
log_game("[key_name(stranger_backseat)] became [key_name(owner)]'s split personality.")
message_admins("[ADMIN_LOOKUPFLW(stranger_backseat)] became [ADMIN_LOOKUPFLW(owner)]'s split personality.")
else
@@ -184,7 +184,7 @@
var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as [owner]'s brainwashed mind?", null, null, null, 75, stranger_backseat)
if(LAZYLEN(candidates))
var/mob/dead/observer/C = pick(candidates)
- stranger_backseat.key = C.key
+ C.transfer_key(stranger_backseat, FALSE)
else
qdel(src)
diff --git a/code/datums/components/virtual_reality.dm b/code/datums/components/virtual_reality.dm
new file mode 100644
index 0000000000..0bc1f9d8ec
--- /dev/null
+++ b/code/datums/components/virtual_reality.dm
@@ -0,0 +1,88 @@
+/datum/component/virtual_reality
+ dupe_mode = COMPONENT_DUPE_UNIQUE
+ var/datum/mind/real_mind // 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))
+ return COMPONENT_INCOMPATIBLE
+
+ you_die_in_the_game_you_die_for_real = yolo
+ quit_action = new()
+ quit_action.Grant(parent)
+ 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)
+
+/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)
+
+/datum/component/virtual_reality/UnregisterFromParent()
+ UnregisterSignal(parent, list(COMSIG_MOB_DEATH, COMSIG_MOB_KEY_CHANGE, COMSIG_MOB_GHOSTIZE))
+ UnregisterSignal(quit_action, COMSIG_ACTION_TRIGGER)
+ UnregisterSignal(current_mind, COMSIG_MIND_TRANSFER)
+
+/datum/component/virtual_reality/proc/action_trigger(datum/signal_source, datum/action/source)
+ if(source != quit_action)
+ return COMPONENT_ACTION_BLOCK_TRIGGER
+ revert_to_reality(signal_source)
+
+/datum/component/virtual_reality/proc/you_only_live_once()
+ if(you_die_in_the_game_you_die_for_real)
+ return FALSE
+ you_die_in_the_game_you_die_for_real = TRUE
+ return TRUE
+
+/datum/component/virtual_reality/proc/pass_me_the_remote(datum/source, mob/new_mob)
+ if(new_mob == real_mind.current)
+ revert_to_reality(source)
+ new_mob.TakeComponent(src)
+ return TRUE
+
+/datum/component/virtual_reality/PostTransfer()
+ if(!ismob(parent))
+ return COMPONENT_INCOMPATIBLE
+
+/datum/component/virtual_reality/proc/revert_to_reality(datum/source)
+ quit_it(FALSE)
+
+/datum/component/virtual_reality/proc/game_over(datum/source)
+ quit_it(TRUE)
+
+/datum/component/virtual_reality/proc/be_a_quitter(datum/source)
+ quit_it(FALSE)
+ return COMPONENT_BLOCK_GHOSTING
+
+/datum/component/virtual_reality/proc/quit_it(deathcheck = FALSE)
+ 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...")
+ else
+ real_mind.current.audiovisual_redirect = null
+ real_mind.current.ckey = M.ckey
+ real_mind.current.stop_sound_channel(CHANNEL_HEARTBEAT)
+ if(deathcheck)
+ 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
+ qdel(src)
+
+/datum/component/virtual_reality/Destroy()
+ QDEL_NULL(quit_action)
+ return ..()
\ No newline at end of file
diff --git a/code/datums/diseases/transformation.dm b/code/datums/diseases/transformation.dm
index b295f5f10b..df6f2e34a1 100644
--- a/code/datums/diseases/transformation.dm
+++ b/code/datums/diseases/transformation.dm
@@ -63,11 +63,11 @@
if(istype(new_mob))
if(bantype && jobban_isbanned(affected_mob, bantype))
replace_banned_player(new_mob)
- new_mob.a_intent = INTENT_HARM
- if(affected_mob.mind)
- affected_mob.mind.transfer_to(new_mob)
- else
- new_mob.key = affected_mob.key
+ new_mob.a_intent = INTENT_HARM
+ if(affected_mob.mind)
+ affected_mob.mind.transfer_to(new_mob)
+ else
+ affected_mob.transfer_key(new_mob)
new_mob.name = affected_mob.real_name
new_mob.real_name = new_mob.name
@@ -82,7 +82,7 @@
to_chat(affected_mob, "Your mob has been taken over by a ghost! Appeal your job ban if you want to avoid this in the future!")
message_admins("[key_name_admin(C)] has taken control of ([key_name_admin(affected_mob)]) to replace a jobbaned player.")
affected_mob.ghostize(0)
- affected_mob.key = C.key
+ C.transfer_key(affected_mob)
else
to_chat(new_mob, "Your mob has been claimed by death! Appeal your job ban if you want to avoid this in the future!")
new_mob.death()
diff --git a/code/datums/mind.dm b/code/datums/mind.dm
index 90affe0228..2844a59d61 100644
--- a/code/datums/mind.dm
+++ b/code/datums/mind.dm
@@ -19,9 +19,9 @@
- IMPORTANT NOTE 2, if you want a player to become a ghost, use mob.ghostize() It does all the hard work for you.
- When creating a new mob which will be a new IC character (e.g. putting a shade in a construct or randomly selecting
- a ghost to become a xeno during an event). Simply assign the key or ckey like you've always done.
+ a ghost to become a xeno during an event), use this mob proc.
- new_mob.key = key
+ mob.transfer_key(new_mob)
The Login proc will handle making a new mind for that mobtype (including setting up stuff like mind.name). Simple!
However if you want that mind to have any special properties like being a traitor etc you will have to do that
@@ -121,6 +121,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)
//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/game/machinery/_machinery.dm b/code/game/machinery/_machinery.dm
index 9b5aa96b0b..ac975c2ffd 100644
--- a/code/game/machinery/_machinery.dm
+++ b/code/game/machinery/_machinery.dm
@@ -175,6 +175,7 @@ Class Procs:
if(isliving(A))
var/mob/living/L = A
L.update_canmove()
+ SEND_SIGNAL(src, COMSIG_MACHINE_EJECT_OCCUPANT, occupant)
occupant = null
/obj/machinery/proc/close_machine(atom/movable/target = null)
@@ -479,6 +480,7 @@ Class Procs:
/obj/machinery/Exited(atom/movable/AM, atom/newloc)
. = ..()
if (AM == occupant)
+ SEND_SIGNAL(src, COMSIG_MACHINE_EJECT_OCCUPANT, occupant)
occupant = null
/obj/machinery/proc/adjust_item_drop_location(atom/movable/AM) // Adjust item drop location to a 3x3 grid inside the tile, returns slot id from 0 to 8
diff --git a/code/game/machinery/exp_cloner.dm b/code/game/machinery/exp_cloner.dm
index 45ac999a6a..4d0ec9f1d9 100644
--- a/code/game/machinery/exp_cloner.dm
+++ b/code/game/machinery/exp_cloner.dm
@@ -52,7 +52,7 @@
var/list/candidates = pollCandidatesForMob("Do you want to play as [clonename]'s defective clone?", null, null, null, 100, H)
if(LAZYLEN(candidates))
var/mob/dead/observer/C = pick(candidates)
- H.key = C.key
+ C.transfer_key(H)
if(grab_ghost_when == CLONER_FRESH_CLONE)
H.grab_ghost()
diff --git a/code/modules/VR/vr_human.dm b/code/modules/VR/vr_human.dm
deleted file mode 100644
index 53a4bbe540..0000000000
--- a/code/modules/VR/vr_human.dm
+++ /dev/null
@@ -1,61 +0,0 @@
-/mob/living/carbon/human/virtual_reality
- var/datum/mind/real_mind // where is my mind t. pixies
- var/obj/machinery/vr_sleeper/vr_sleeper
- var/datum/action/quit_vr/quit_action
-
-/mob/living/carbon/human/virtual_reality/Initialize()
- . = ..()
- quit_action = new()
- quit_action.Grant(src)
-
-/mob/living/carbon/human/virtual_reality/death()
- revert_to_reality()
- ..()
-
-/mob/living/carbon/human/virtual_reality/Destroy()
- revert_to_reality()
- return ..()
-
-/mob/living/carbon/human/virtual_reality/Life()
- . = ..()
- if(real_mind)
- var/mob/living/real_me = real_mind.current
- if (real_me && real_me.stat == CONSCIOUS)
- return
- revert_to_reality(FALSE)
-
-/mob/living/carbon/human/virtual_reality/ghostize()
- stack_trace("Ghostize was called on a virtual reality mob")
-
-/mob/living/carbon/human/virtual_reality/ghost()
- set category = "OOC"
- set name = "Ghost"
- set desc = "Relinquish your life and enter the land of the dead."
- revert_to_reality(FALSE)
-
-/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)
- if(vr_sleeper.you_die_in_the_game_you_die_for_real)
- to_chat(real_mind, "You feel everything fading away...")
- real_mind.current.death(0)
- if(deathchecks && vr_sleeper)
- vr_sleeper.vr_human = null
- vr_sleeper = null
- real_mind = null
-
-/datum/action/quit_vr
- name = "Quit Virtual Reality"
- icon_icon = 'icons/mob/actions/actions_vr.dmi'
- button_icon_state = "logout"
-
-/datum/action/quit_vr/Trigger()
- if(..())
- if(istype(owner, /mob/living/carbon/human/virtual_reality))
- var/mob/living/carbon/human/virtual_reality/VR = owner
- VR.revert_to_reality(FALSE)
- else
- Remove(owner)
diff --git a/code/modules/VR/vr_mob.dm b/code/modules/VR/vr_mob.dm
new file mode 100644
index 0000000000..4f8a7daf34
--- /dev/null
+++ b/code/modules/VR/vr_mob.dm
@@ -0,0 +1,42 @@
+/mob/proc/build_virtual_character(mob/M)
+ mind_initialize()
+ if(!M)
+ return FALSE
+ name = M.name
+ real_name = M.real_name
+ return TRUE
+
+/mob/living/carbon/build_virtual_character(mob/M)
+ . = ..()
+ if(!.)
+ return
+ if(!iscarbon(M))
+ return FALSE
+ var/mob/living/carbon/C = M
+ C.dna?.transfer_identity(src)
+
+/mob/living/carbon/human/build_virtual_character(mob/M, datum/outfit/outfit)
+ . = ..()
+ if(!.)
+ return
+ if(ishuman(M))
+ var/mob/living/carbon/human/H = M
+ socks = H.socks
+ undershirt = H.undershirt
+ underwear = H.underwear
+ give_genitals(TRUE)
+ if(outfit)
+ var/datum/outfit/O = new outfit()
+ O.equip(src)
+ name = M.name
+ real_name = M.real_name
+
+/datum/action/quit_vr
+ name = "Quit Virtual Reality"
+ icon_icon = 'icons/mob/actions/actions_vr.dmi'
+ button_icon_state = "logout"
+
+/datum/action/quit_vr/Trigger() //this merely a trigger for /datum/component/virtual_reality
+ . = ..()
+ if(!.)
+ Remove(owner)
diff --git a/code/modules/VR/vr_sleeper.dm b/code/modules/VR/vr_sleeper.dm
index 4e342f6ced..afc8de6e21 100644
--- a/code/modules/VR/vr_sleeper.dm
+++ b/code/modules/VR/vr_sleeper.dm
@@ -1,5 +1,3 @@
-
-
//Glorified teleporter that puts you in a new human body.
// it's """VR"""
/obj/machinery/vr_sleeper
@@ -12,9 +10,9 @@
circuit = /obj/item/circuitboard/machine/vr_sleeper
var/you_die_in_the_game_you_die_for_real = FALSE
var/datum/effect_system/spark_spread/sparks
- var/mob/living/carbon/human/virtual_reality/vr_human
+ var/mob/living/vr_mob
var/vr_category = "default" //Specific category of spawn points to pick from
- var/allow_creating_vr_humans = TRUE //So you can have vr_sleepers that always spawn you as a specific person or 1 life/chance vr games
+ var/allow_creating_vr_mobs = TRUE //So you can have vr_sleepers that always spawn you as a specific person or 1 life/chance vr games
var/only_current_user_can_interact = FALSE
/obj/machinery/vr_sleeper/Initialize()
@@ -44,7 +42,7 @@
/obj/machinery/vr_sleeper/Destroy()
open_machine()
- cleanup_vr_human()
+ cleanup_vr_mob()
QDEL_NULL(sparks)
return ..()
@@ -57,6 +55,10 @@
return
/obj/machinery/vr_sleeper/emag_act(mob/user)
+ . = ..()
+ if(!(obj_flags & EMAGGED))
+ return
+ obj_flags |= EMAGGED
you_die_in_the_game_you_die_for_real = TRUE
sparks.start()
addtimer(CALLBACK(src, .proc/emagNotify), 150)
@@ -65,12 +67,11 @@
icon_state = "[initial(icon_state)][state_open ? "-open" : ""]"
/obj/machinery/vr_sleeper/open_machine()
- if(!state_open)
- if(vr_human)
- vr_human.revert_to_reality(FALSE)
- if(occupant)
- SStgui.close_user_uis(occupant, src)
- ..()
+ if(state_open)
+ return
+ if(occupant)
+ SStgui.close_user_uis(occupant, src)
+ return ..()
/obj/machinery/vr_sleeper/MouseDrop_T(mob/target, mob/user)
if(user.stat || user.lying || !Adjacent(user) || !user.Adjacent(target) || !iscarbon(target) || !user.IsAdvancedToolUser())
@@ -91,21 +92,21 @@
var/mob/living/carbon/human/human_occupant = occupant
if(human_occupant && human_occupant.mind && usr == occupant)
to_chat(occupant, "Transferring to virtual reality...")
- if(vr_human && vr_human.stat == CONSCIOUS && !vr_human.real_mind)
+ if(vr_mob && vr_mob.stat == CONSCIOUS && !vr_mob.GetComponent(/datum/component/virtual_reality))
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!")
+ 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
+ to_chat(vr_mob, "Transfer successful! You are now playing as [vr_mob] in VR!")
else
- if(allow_creating_vr_humans)
+ if(allow_creating_vr_mobs)
to_chat(occupant, "Virtual avatar not found, attempting to create one...")
var/obj/effect/landmark/vr_spawn/V = get_vr_spawnpoint()
var/turf/T = get_turf(V)
if(T)
SStgui.close_user_uis(occupant, src)
- build_virtual_human(occupant, T, V.vr_outfit)
- to_chat(vr_human, "Transfer successful! You are now playing as [vr_human] in VR!")
+ new_player(occupant, T, V.vr_outfit)
+ to_chat(vr_mob, "Transfer successful! You are now playing as [vr_mob] in VR!")
else
to_chat(occupant, "Virtual world misconfigured, aborting transfer")
else
@@ -113,8 +114,8 @@
. = TRUE
if("delete_avatar")
if(!occupant || usr == occupant)
- if(vr_human)
- cleanup_vr_human()
+ if(vr_mob)
+ cleanup_vr_mob()
else
to_chat(usr, "The VR Sleeper's safeties prevent you from doing that.")
. = TRUE
@@ -127,10 +128,10 @@
/obj/machinery/vr_sleeper/ui_data(mob/user)
var/list/data = list()
- if(vr_human && !QDELETED(vr_human))
+ if(vr_mob && !QDELETED(vr_mob))
data["can_delete_avatar"] = TRUE
var/status
- switch(vr_human.stat)
+ switch(vr_mob.stat)
if(CONSCIOUS)
status = "Conscious"
if(DEAD)
@@ -139,7 +140,7 @@
status = "Unconscious"
if(SOFT_CRIT)
status = "Barely Conscious"
- data["vr_avatar"] = list("name" = vr_human.name, "status" = status, "health" = vr_human.health, "maxhealth" = vr_human.maxHealth)
+ data["vr_avatar"] = list("name" = vr_mob.name, "status" = status, "health" = vr_mob.health, "maxhealth" = vr_mob.maxHealth)
data["toggle_open"] = state_open
data["emagged"] = you_die_in_the_game_you_die_for_real
data["isoccupant"] = (user == occupant)
@@ -153,37 +154,28 @@
for(var/obj/effect/landmark/vr_spawn/V in GLOB.landmarks_list)
GLOB.vr_spawnpoints[V.vr_category] = V
-/obj/machinery/vr_sleeper/proc/build_virtual_human(mob/living/carbon/human/H, location, var/datum/outfit/outfit, transfer = TRUE)
- if(H)
- cleanup_vr_human()
- vr_human = new /mob/living/carbon/human/virtual_reality(location)
- vr_human.mind_initialize()
- vr_human.vr_sleeper = src
- vr_human.real_mind = H.mind
- H.dna.transfer_identity(vr_human)
- vr_human.name = H.name
- vr_human.real_name = H.real_name
- vr_human.socks = H.socks
- vr_human.undershirt = H.undershirt
- vr_human.underwear = H.underwear
- vr_human.updateappearance(TRUE, TRUE, TRUE)
- vr_human.give_genitals(TRUE) //CITADEL ADD
- if(outfit)
- var/datum/outfit/O = new outfit()
- 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/new_player(mob/living/carbon/human/H, location, datum/outfit/outfit, transfer = TRUE)
+ if(!H)
+ return
+ cleanup_vr_mob()
+ vr_mob = new /mob/living/carbon/human(location)
+ if(vr_mob.build_virtual_character(H, outfit))
+ var/mob/living/carbon/human/vr_H = vr_mob
+ 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
-/obj/machinery/vr_sleeper/proc/cleanup_vr_human()
- if(vr_human)
- vr_human.vr_sleeper = null // Prevents race condition where a new human could get created out of order and set to null.
- QDEL_NULL(vr_human)
+/obj/machinery/vr_sleeper/proc/cleanup_vr_mob()
+ if(vr_mob)
+ QDEL_NULL(vr_mob)
/obj/machinery/vr_sleeper/proc/emagNotify()
- if(vr_human)
- vr_human.Dizzy(10)
+ if(vr_mob)
+ vr_mob.Dizzy(10)
/obj/effect/landmark/vr_spawn //places you can spawn in VR, auto selected by the vr_sleeper during get_vr_spawnpoint()
var/vr_category = "default" //So we can have specific sleepers, eg: "Basketball VR Sleeper", etc.
@@ -215,6 +207,7 @@
color = "#00FF00"
invisibility = INVISIBILITY_ABSTRACT
var/area/vr_area
+ var/list/corpse_party
/obj/effect/vr_clean_master/Initialize()
. = ..()
@@ -227,7 +220,8 @@
qdel(casing)
for(var/obj/effect/decal/cleanable/C in vr_area)
qdel(C)
- for (var/mob/living/carbon/human/virtual_reality/H in vr_area)
- if (H.stat == DEAD && !H.vr_sleeper && !H.real_mind)
- qdel(H)
+ for (var/A in corpse_party)
+ var/mob/M = A
+ if(get_area(M) == vr_area && M.stat == DEAD)
+ qdel(M)
addtimer(CALLBACK(src, .proc/clean_up), 3 MINUTES)
diff --git a/code/modules/admin/fun_balloon.dm b/code/modules/admin/fun_balloon.dm
index da811a1974..281fb49f05 100644
--- a/code/modules/admin/fun_balloon.dm
+++ b/code/modules/admin/fun_balloon.dm
@@ -61,7 +61,7 @@
to_chat(body, "Your mob has been taken over by a ghost!")
message_admins("[key_name_admin(C)] has taken control of ([key_name_admin(body)])")
body.ghostize(0)
- body.key = C.key
+ C.transfer_key(body)
new /obj/effect/temp_visual/gravpush(get_turf(body))
/obj/effect/fun_balloon/sentience/emergency_shuttle
diff --git a/code/modules/admin/secrets.dm b/code/modules/admin/secrets.dm
index 66358dc080..db6a5b9e88 100644
--- a/code/modules/admin/secrets.dm
+++ b/code/modules/admin/secrets.dm
@@ -744,7 +744,7 @@
var/mob/chosen = players[1]
if (chosen.client)
chosen.client.prefs.copy_to(spawnedMob)
- spawnedMob.key = chosen.key
+ chosen.transfer_key(spawnedMob)
players -= chosen
if (ishuman(spawnedMob) && ispath(humanoutfit, /datum/outfit))
var/mob/living/carbon/human/H = spawnedMob
diff --git a/code/modules/admin/verbs/one_click_antag.dm b/code/modules/admin/verbs/one_click_antag.dm
index d9732818bd..5409934abf 100644
--- a/code/modules/admin/verbs/one_click_antag.dm
+++ b/code/modules/admin/verbs/one_click_antag.dm
@@ -412,7 +412,7 @@
//Spawn the body
var/mob/living/carbon/human/ERTOperative = new ertemplate.mobtype(spawnloc)
chosen_candidate.client.prefs.copy_to(ERTOperative)
- ERTOperative.key = chosen_candidate.key
+ chosen_candidate.transfer_key(ERTOperative)
if(ertemplate.enforce_human || ERTOperative.dna.species.dangerous_existence) // Don't want any exploding plasmemes
ERTOperative.set_species(/datum/species/human)
diff --git a/code/modules/admin/verbs/randomverbs.dm b/code/modules/admin/verbs/randomverbs.dm
index f74b31760d..bbeb6a963e 100644
--- a/code/modules/admin/verbs/randomverbs.dm
+++ b/code/modules/admin/verbs/randomverbs.dm
@@ -384,7 +384,7 @@ Traitors and the like can also be revived with the previous role mostly intact.
//Now to give them their mind back.
G_found.mind.transfer_to(new_xeno) //be careful when doing stuff like this! I've already checked the mind isn't in use
- new_xeno.key = G_found.key
+ G_found.transfer_key(new_xeno, FALSE)
to_chat(new_xeno, "You have been fully respawned. Enjoy the game.")
var/msg = "[key_name_admin(usr)] has respawned [new_xeno.key] as a filthy xeno."
message_admins(msg)
@@ -397,7 +397,7 @@ Traitors and the like can also be revived with the previous role mostly intact.
var/mob/living/carbon/monkey/new_monkey = new
SSjob.SendToLateJoin(new_monkey)
G_found.mind.transfer_to(new_monkey) //be careful when doing stuff like this! I've already checked the mind isn't in use
- new_monkey.key = G_found.key
+ G_found.transfer_key(new_monkey, FALSE)
to_chat(new_monkey, "You have been fully respawned. Enjoy the game.")
var/msg = "[key_name_admin(usr)] has respawned [new_monkey.key] as a filthy xeno."
message_admins(msg)
@@ -437,7 +437,7 @@ Traitors and the like can also be revived with the previous role mostly intact.
if(!new_character.mind.assigned_role)
new_character.mind.assigned_role = "Assistant"//If they somehow got a null assigned role.
- new_character.key = G_found.key
+ G_found.transfer_key(new_character, FALSE)
/*
The code below functions with the assumption that the mob is already a traitor if they have a special role.
diff --git a/code/modules/antagonists/_common/antag_datum.dm b/code/modules/antagonists/_common/antag_datum.dm
index aa91af1dd8..8d7d5395a4 100644
--- a/code/modules/antagonists/_common/antag_datum.dm
+++ b/code/modules/antagonists/_common/antag_datum.dm
@@ -15,7 +15,7 @@ GLOBAL_LIST_EMPTY(antagonists)
var/antag_memory = ""//These will be removed with antag datum
var/antag_moodlet //typepath of moodlet that the mob will gain with their status
var/can_hijack = HIJACK_NEUTRAL //If these antags are alone on shuttle hijack happens.
-
+
//Antag panel properties
var/show_in_antagpanel = TRUE //This will hide adding this antag type in antag panel, use only for internal subtypes that shouldn't be added directly but still show if possessed by mind
var/antagpanel_category = "Uncategorized" //Antagpanel will display these together, REQUIRED
@@ -87,7 +87,7 @@ GLOBAL_LIST_EMPTY(antagonists)
to_chat(owner, "Your mob has been taken over by a ghost! Appeal your job ban if you want to avoid this in the future!")
message_admins("[key_name_admin(C)] has taken control of ([key_name_admin(owner.current)]) to replace a jobbaned player.")
owner.current.ghostize(0)
- owner.current.key = C.key
+ C.transfer_key(owner.current, FALSE)
/datum/antagonist/proc/on_removal()
remove_innate_effects()
diff --git a/code/modules/antagonists/blob/blob/powers.dm b/code/modules/antagonists/blob/blob/powers.dm
index 9e915ee0fa..b4affefb29 100644
--- a/code/modules/antagonists/blob/blob/powers.dm
+++ b/code/modules/antagonists/blob/blob/powers.dm
@@ -172,7 +172,7 @@
blobber.adjustHealth(blobber.maxHealth * 0.5)
blob_mobs += blobber
var/mob/dead/observer/C = pick(candidates)
- blobber.key = C.key
+ C.transfer_key(blobber)
SEND_SOUND(blobber, sound('sound/effects/blobattack.ogg'))
SEND_SOUND(blobber, sound('sound/effects/attackblob.ogg'))
to_chat(blobber, "You are a blobbernaut!")
diff --git a/code/modules/antagonists/clockcult/clock_effects/clock_sigils.dm b/code/modules/antagonists/clockcult/clock_effects/clock_sigils.dm
index 97d7f0c128..efcc8089ef 100644
--- a/code/modules/antagonists/clockcult/clock_effects/clock_sigils.dm
+++ b/code/modules/antagonists/clockcult/clock_effects/clock_sigils.dm
@@ -349,7 +349,7 @@
to_chat(L, "Your physical form has been taken over by another soul due to your inactivity! Ahelp if you wish to regain your form!")
message_admins("[key_name_admin(C)] has taken control of ([key_name_admin(L)]) to replace an inactive clock cultist.")
L.ghostize(0)
- L.key = C.key
+ C.transfer_key(L, FALSE)
var/obj/effect/temp_visual/ratvar/sigil/vitality/V = new /obj/effect/temp_visual/ratvar/sigil/vitality(get_turf(src))
animate(V, alpha = 0, transform = matrix()*2, time = 8)
playsound(L, 'sound/magic/staff_healing.ogg', 50, 1)
diff --git a/code/modules/antagonists/clockcult/clock_items/construct_chassis.dm b/code/modules/antagonists/clockcult/clock_items/construct_chassis.dm
index 30c84f5312..aac545c366 100644
--- a/code/modules/antagonists/clockcult/clock_items/construct_chassis.dm
+++ b/code/modules/antagonists/clockcult/clock_items/construct_chassis.dm
@@ -54,7 +54,7 @@
pre_spawn()
visible_message(creation_message)
var/mob/living/construct = new construct_type(get_turf(src))
- construct.key = user.key
+ user.transfer_key(construct, FALSE)
post_spawn(construct)
qdel(user)
qdel(src)
diff --git a/code/modules/antagonists/clockcult/clock_structures/eminence_spire.dm b/code/modules/antagonists/clockcult/clock_structures/eminence_spire.dm
index 05f3ca5917..bb17254931 100644
--- a/code/modules/antagonists/clockcult/clock_structures/eminence_spire.dm
+++ b/code/modules/antagonists/clockcult/clock_structures/eminence_spire.dm
@@ -64,7 +64,7 @@
message_admins("Admin [key_name_admin(user)] directly became the Eminence of the cult!")
log_admin("Admin [key_name(user)] made themselves the Eminence.")
var/mob/camera/eminence/eminence = new(get_turf(src))
- eminence.key = user.key
+ eminence.key = user.transfer_key(eminence, FALSE)
hierophant_message("Ratvar has directly assigned the Eminence!")
for(var/mob/M in servants_and_ghosts())
M.playsound_local(M, 'sound/machines/clockcult/eminence_selected.ogg', 50, FALSE)
@@ -138,7 +138,7 @@
playsound(src, 'sound/machines/clockcult/ark_damage.ogg', 50, FALSE)
var/mob/camera/eminence/eminence = new(get_turf(src))
eminence_nominee = pick(candidates)
- eminence.key = eminence_nominee.key
+ eminence_nominee.transfer_key(eminence)
hierophant_message("A ghost has ascended into the Eminence!")
for(var/mob/M in servants_and_ghosts())
M.playsound_local(M, 'sound/machines/clockcult/eminence_selected.ogg', 50, FALSE)
diff --git a/code/modules/antagonists/clockcult/clock_structures/ratvar_the_clockwork_justicar.dm b/code/modules/antagonists/clockcult/clock_structures/ratvar_the_clockwork_justicar.dm
index 9341a7ee6a..21ece44212 100644
--- a/code/modules/antagonists/clockcult/clock_structures/ratvar_the_clockwork_justicar.dm
+++ b/code/modules/antagonists/clockcult/clock_structures/ratvar_the_clockwork_justicar.dm
@@ -45,7 +45,7 @@
return FALSE
var/mob/living/simple_animal/drone/cogscarab/ratvar/R = new/mob/living/simple_animal/drone/cogscarab/ratvar(get_turf(src))
R.visible_message("[R] forms, and its eyes blink open, glowing bright red!")
- R.key = O.key
+ O.transfer_key(R, FALSE)
/obj/structure/destructible/clockwork/massive/ratvar/Bump(atom/A)
var/turf/T = get_turf(A)
diff --git a/code/modules/antagonists/cult/runes.dm b/code/modules/antagonists/cult/runes.dm
index 3c11351660..4be56efd0d 100644
--- a/code/modules/antagonists/cult/runes.dm
+++ b/code/modules/antagonists/cult/runes.dm
@@ -595,7 +595,7 @@ structure_check() searches for nearby cultist structures required for the invoca
to_chat(mob_to_revive.mind, "Your physical form has been taken over by another soul due to your inactivity! Ahelp if you wish to regain your form.")
message_admins("[key_name_admin(C)] has taken control of ([key_name_admin(mob_to_revive)]) to replace an AFK player.")
mob_to_revive.ghostize(0)
- mob_to_revive.key = C.key
+ C.transfer_key(mob_to_revive, FALSE)
else
fail_invoke()
return
@@ -890,7 +890,7 @@ structure_check() searches for nearby cultist structures required for the invoca
visible_message("A cloud of red mist forms above [src], and from within steps... a [new_human.gender == FEMALE ? "wo":""]man.")
to_chat(user, "Your blood begins flowing into [src]. You must remain in place and conscious to maintain the forms of those summoned. This will hurt you slowly but surely...")
var/obj/structure/emergency_shield/invoker/N = new(T)
- new_human.key = ghost_to_spawn.key
+ ghost_to_spawn.transfer_key(new_human, FALSE)
SSticker.mode.add_cultist(new_human.mind, 0)
to_chat(new_human, "You are a servant of the Geometer. You have been made semi-corporeal by the cult of Nar'Sie, and you are to serve them at all costs.")
diff --git a/code/modules/antagonists/devil/true_devil/_true_devil.dm b/code/modules/antagonists/devil/true_devil/_true_devil.dm
index 923a224b81..87925271ec 100644
--- a/code/modules/antagonists/devil/true_devil/_true_devil.dm
+++ b/code/modules/antagonists/devil/true_devil/_true_devil.dm
@@ -146,7 +146,7 @@
/mob/living/carbon/true_devil/attack_ghost(mob/dead/observer/user as mob)
if(ascended || user.mind.soulOwner == src.mind)
var/mob/living/simple_animal/imp/S = new(get_turf(loc))
- S.key = user.key
+ user.transfer_key(S, FALSE)
S.mind.assigned_role = "Imp"
S.mind.special_role = "Imp"
var/datum/objective/newobjective = new
diff --git a/code/modules/antagonists/disease/disease_event.dm b/code/modules/antagonists/disease/disease_event.dm
index 7183ed5455..da58787fe9 100644
--- a/code/modules/antagonists/disease/disease_event.dm
+++ b/code/modules/antagonists/disease/disease_event.dm
@@ -18,7 +18,7 @@
var/mob/dead/observer/selected = pick_n_take(candidates)
var/mob/camera/disease/virus = new /mob/camera/disease(SSmapping.get_station_center())
- virus.key = selected.key
+ selected.transfer_key(virus, FALSE)
INVOKE_ASYNC(virus, /mob/camera/disease/proc/pick_name)
message_admins("[ADMIN_LOOKUPFLW(virus)] has been made into a sentient disease by an event.")
log_game("[key_name(virus)] was spawned as a sentient disease by an event.")
diff --git a/code/modules/antagonists/revenant/revenant.dm b/code/modules/antagonists/revenant/revenant.dm
index 87794993a7..50b8851d3d 100644
--- a/code/modules/antagonists/revenant/revenant.dm
+++ b/code/modules/antagonists/revenant/revenant.dm
@@ -377,14 +377,15 @@
/obj/item/ectoplasm/revenant/proc/reform()
if(QDELETED(src) || QDELETED(revenant) || inert)
return
- var/key_of_revenant
+ var/key_of_revenant = FALSE
message_admins("Revenant ectoplasm was left undestroyed for 1 minute and is reforming into a new revenant.")
forceMove(drop_location()) //In case it's in a backpack or someone's hand
revenant.forceMove(loc)
if(old_key)
for(var/mob/M in GLOB.dead_mob_list)
if(M.client && M.client.key == old_key) //Only recreates the mob if the mob the client is in is dead
- key_of_revenant = old_key
+ M.transfer_key(revenant.key, FALSE)
+ key_of_revenant = TRUE
break
if(!key_of_revenant)
message_admins("The new revenant's old client either could not be found or is in a new, living mob - grabbing a random candidate instead...")
@@ -396,22 +397,21 @@
visible_message("[src] settles down and seems lifeless.")
return
var/mob/dead/observer/C = pick(candidates)
- key_of_revenant = C.key
- if(!key_of_revenant)
+ C.transfer_key(revenant.key, FALSE)
+ if(!revenant.key)
qdel(revenant)
message_admins("No ckey was found for the new revenant. Oh well!")
inert = TRUE
visible_message("[src] settles down and seems lifeless.")
return
- message_admins("[key_of_revenant] has been [old_key == key_of_revenant ? "re":""]made into a revenant by reforming ectoplasm.")
- log_game("[key_of_revenant] was [old_key == key_of_revenant ? "re":""]made as a revenant by reforming ectoplasm.")
+ message_admins("[key_of_revenant] has been [old_key == revenant.key ? "re":""]made into a revenant by reforming ectoplasm.")
+ log_game("[key_of_revenant] was [old_key == revenant.key ? "re":""]made as a revenant by reforming ectoplasm.")
visible_message("[src] suddenly rises into the air before fading away.")
revenant.essence = essence
revenant.essence_regen_cap = essence
revenant.death_reset()
- revenant.key = key_of_revenant
revenant = null
qdel(src)
diff --git a/code/modules/antagonists/revenant/revenant_spawn_event.dm b/code/modules/antagonists/revenant/revenant_spawn_event.dm
index c9a892cd64..4ab2bcb30a 100644
--- a/code/modules/antagonists/revenant/revenant_spawn_event.dm
+++ b/code/modules/antagonists/revenant/revenant_spawn_event.dm
@@ -52,7 +52,7 @@
return MAP_ERROR
var/mob/living/simple_animal/revenant/revvie = new(pick(spawn_locs))
- revvie.key = selected.key
+ selected.transfer_key(revvie, FALSE)
message_admins("[ADMIN_LOOKUPFLW(revvie)] has been made into a revenant by an event.")
log_game("[key_name(revvie)] was spawned as a revenant by an event.")
spawned_mobs += revvie
diff --git a/code/modules/antagonists/wizard/equipment/soulstone.dm b/code/modules/antagonists/wizard/equipment/soulstone.dm
index 40551ae2fc..3df561799e 100644
--- a/code/modules/antagonists/wizard/equipment/soulstone.dm
+++ b/code/modules/antagonists/wizard/equipment/soulstone.dm
@@ -218,7 +218,7 @@
newstruct.master = stoner
var/datum/action/innate/seek_master/SM = new()
SM.Grant(newstruct)
- newstruct.key = target.key
+ target.transfer_key(newstruct)
var/obj/screen/alert/bloodsense/BS
if(newstruct.mind && ((stoner && iscultist(stoner)) || cultoverride) && SSticker && SSticker.mode)
SSticker.mode.add_cultist(newstruct.mind, 0)
@@ -243,7 +243,7 @@
S.canmove = FALSE//Can't move out of the soul stone
S.name = "Shade of [T.real_name]"
S.real_name = "Shade of [T.real_name]"
- S.key = T.key
+ T.transfer_key(S)
S.language_holder = U.language_holder.copy(S)
if(U)
S.faction |= "[REF(U)]" //Add the master as a faction, allowing inter-mob cooperation
diff --git a/code/modules/awaymissions/mission_code/Academy.dm b/code/modules/awaymissions/mission_code/Academy.dm
index 57d8420fa8..310699576c 100644
--- a/code/modules/awaymissions/mission_code/Academy.dm
+++ b/code/modules/awaymissions/mission_code/Academy.dm
@@ -133,7 +133,7 @@
var/mob/dead/observer/C = pick(candidates)
message_admins("[ADMIN_LOOKUPFLW(C)] was spawned as Wizard Academy Defender")
current_wizard.ghostize() // on the off chance braindead defender gets back in
- current_wizard.key = C.key
+ C.transfer_key(current_wizard, FALSE)
/obj/structure/academy_wizard_spawner/proc/summon_wizard()
var/turf/T = src.loc
@@ -274,7 +274,7 @@
if(LAZYLEN(candidates))
var/mob/dead/observer/C = pick(candidates)
message_admins("[ADMIN_LOOKUPFLW(C)] was spawned as Dice Servant")
- H.key = C.key
+ C.transfer_key(H, FALSE)
var/obj/effect/proc_holder/spell/targeted/summonmob/S = new
S.target_mob = H
diff --git a/code/modules/events/holiday/xmas.dm b/code/modules/events/holiday/xmas.dm
index a76c75dd43..261d1fc774 100644
--- a/code/modules/events/holiday/xmas.dm
+++ b/code/modules/events/holiday/xmas.dm
@@ -77,7 +77,7 @@
if(LAZYLEN(candidates))
var/mob/dead/observer/C = pick(candidates)
santa = new /mob/living/carbon/human(pick(GLOB.blobstart))
- santa.key = C.key
+ C.transfer_key(santa, FALSE)
santa.equipOutfit(/datum/outfit/santa)
santa.update_icons()
diff --git a/code/modules/events/sentience.dm b/code/modules/events/sentience.dm
index 55d8ce8b14..49ca828d35 100644
--- a/code/modules/events/sentience.dm
+++ b/code/modules/events/sentience.dm
@@ -48,7 +48,7 @@
spawned_animals++
- SA.key = SG.key
+ SG.transfer_key(SA, FALSE)
SA.grant_all_languages(TRUE)
diff --git a/code/modules/events/wizard/imposter.dm b/code/modules/events/wizard/imposter.dm
index 1c8ef95baa..550cce0217 100644
--- a/code/modules/events/wizard/imposter.dm
+++ b/code/modules/events/wizard/imposter.dm
@@ -23,7 +23,7 @@
I.name = I.dna.real_name
I.updateappearance(mutcolor_update=1)
I.domutcheck()
- I.key = C.key
+ C.transfer_key(I, FALSE)
var/datum/antagonist/wizard/master = M.has_antag_datum(/datum/antagonist/wizard)
if(!master.wiz_team)
master.create_wiz_team()
diff --git a/code/modules/mob/dead/new_player/new_player.dm b/code/modules/mob/dead/new_player/new_player.dm
index 84be6438c0..bf768d8277 100644
--- a/code/modules/mob/dead/new_player/new_player.dm
+++ b/code/modules/mob/dead/new_player/new_player.dm
@@ -285,7 +285,7 @@
else
to_chat(src, "Teleporting failed. Ahelp an admin please")
stack_trace("There's no freaking observer landmark available on this map or you're making observers before the map is initialised")
- observer.key = key
+ transfer_key(observer, FALSE)
observer.client = client
observer.set_ghost_appearance()
if(observer.client && observer.client.prefs)
diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm
index 55b8891534..41760cb71a 100644
--- a/code/modules/mob/dead/observer/observer.dm
+++ b/code/modules/mob/dead/observer/observer.dm
@@ -260,16 +260,16 @@ 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 = 1)
- if(key)
- if(!cmptext(copytext(key,1,2),"@")) // Skip aghosts.
- 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.
- ghost.can_reenter_corpse = can_reenter_corpse
- ghost.can_reenter_round = (can_reenter_corpse && !suiciding)
- ghost.key = key
- return ghost
+/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.
+ 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.
+ ghost.can_reenter_corpse = can_reenter_corpse
+ ghost.can_reenter_round = (can_reenter_corpse && !suiciding)
+ transfer_key(ghost, FALSE)
+ return ghost
/*
This is the proc mobs get to turn into a ghost. Forked from ghostize due to compatibility issues.
@@ -348,7 +348,7 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
return
client.change_view(CONFIG_GET(string/default_view))
SStgui.on_transfer(src, mind.current) // Transfer NanoUIs.
- mind.current.key = key
+ transfer_key(mind.current, FALSE)
return 1
/mob/dead/observer/proc/notify_cloning(var/message, var/sound, var/atom/source, flashwindow = TRUE)
@@ -631,7 +631,7 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
to_chat(src, "Someone has taken this body while you were choosing!")
return 0
- target.key = key
+ transfer_key(target, FALSE)
target.faction = list("neutral")
return 1
diff --git a/code/modules/mob/living/brain/brain_item.dm b/code/modules/mob/living/brain/brain_item.dm
index 2f3ee10428..571b60317c 100644
--- a/code/modules/mob/living/brain/brain_item.dm
+++ b/code/modules/mob/living/brain/brain_item.dm
@@ -34,7 +34,7 @@
if(brainmob.mind)
brainmob.mind.transfer_to(C)
else
- C.key = brainmob.key
+ brainmob.transfer_key(C)
QDEL_NULL(brainmob)
@@ -64,7 +64,7 @@
name = "[L.name]'s brain"
if(brainmob)
return
-
+
if(!L.mind)
return
brainmob = new(src)
diff --git a/code/modules/mob/living/carbon/alien/special/alien_embryo.dm b/code/modules/mob/living/carbon/alien/special/alien_embryo.dm
index b4739f943e..2bdbf2515b 100644
--- a/code/modules/mob/living/carbon/alien/special/alien_embryo.dm
+++ b/code/modules/mob/living/carbon/alien/special/alien_embryo.dm
@@ -86,7 +86,7 @@
var/atom/xeno_loc = get_turf(owner)
var/mob/living/carbon/alien/larva/new_xeno = new(xeno_loc)
- new_xeno.key = ghost.key
+ ghost.transfer_key(new_xeno, FALSE)
SEND_SOUND(new_xeno, sound('sound/voice/hiss5.ogg',0,0,0,100)) //To get the player's attention
new_xeno.canmove = 0 //so we don't move during the bursting animation
new_xeno.notransform = 1
diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm
index b6717f2932..0e35dd24d0 100644
--- a/code/modules/mob/living/living.dm
+++ b/code/modules/mob/living/living.dm
@@ -878,7 +878,7 @@
if(mind)
mind.transfer_to(new_mob)
else
- new_mob.key = key
+ transfer_key(new_mob)
for(var/para in hasparasites())
var/mob/living/simple_animal/hostile/guardian/G = para
diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm
index b4524a54e6..3e72cbf875 100644
--- a/code/modules/mob/living/silicon/ai/ai.dm
+++ b/code/modules/mob/living/silicon/ai/ai.dm
@@ -86,7 +86,7 @@
var/datum/action/innate/deploy_last_shell/redeploy_action = new
var/chnotify = 0
-
+
var/multicam_on = FALSE
var/obj/screen/movable/pic_in_pic/ai/master_multicam
var/list/multicam_screens = list()
diff --git a/code/modules/mob/living/silicon/pai/pai.dm b/code/modules/mob/living/silicon/pai/pai.dm
index 896d8674be..6ea1e96a06 100644
--- a/code/modules/mob/living/silicon/pai/pai.dm
+++ b/code/modules/mob/living/silicon/pai/pai.dm
@@ -192,7 +192,7 @@
/mob/proc/makePAI(delold)
var/obj/item/paicard/card = new /obj/item/paicard(get_turf(src))
var/mob/living/silicon/pai/pai = new /mob/living/silicon/pai(card)
- pai.key = key
+ transfer_key(pai)
pai.name = name
card.setPersonality(pai)
if(delold)
diff --git a/code/modules/mob/living/simple_animal/bot/bot.dm b/code/modules/mob/living/simple_animal/bot/bot.dm
index e9978d1e62..5fbd92c11a 100644
--- a/code/modules/mob/living/simple_animal/bot/bot.dm
+++ b/code/modules/mob/living/simple_animal/bot/bot.dm
@@ -916,7 +916,7 @@ Pass a positive integer as an argument to override a bot's default speed.
if(mind && paicard.pai)
mind.transfer_to(paicard.pai)
else if(paicard.pai)
- paicard.pai.key = key
+ transfer_key(paicard.pai)
else
ghostize(0) // The pAI card that just got ejected was dead.
key = null
diff --git a/code/modules/mob/living/simple_animal/friendly/drone/_drone.dm b/code/modules/mob/living/simple_animal/friendly/drone/_drone.dm
index cf3742fcc5..71d4eddb27 100644
--- a/code/modules/mob/living/simple_animal/friendly/drone/_drone.dm
+++ b/code/modules/mob/living/simple_animal/friendly/drone/_drone.dm
@@ -159,7 +159,7 @@
if(mind)
mind.transfer_to(R, 1)
else
- R.key = key
+ transfer_key(R)
qdel(src)
diff --git a/code/modules/mob/living/simple_animal/friendly/drone/drones_as_items.dm b/code/modules/mob/living/simple_animal/friendly/drone/drones_as_items.dm
index a655bdf231..3717d6538e 100644
--- a/code/modules/mob/living/simple_animal/friendly/drone/drones_as_items.dm
+++ b/code/modules/mob/living/simple_animal/friendly/drone/drones_as_items.dm
@@ -61,5 +61,5 @@
var/obj/item/new_hat = new hat_type(D)
D.equip_to_slot_or_del(new_hat, SLOT_HEAD)
D.flags_1 |= (flags_1 & ADMIN_SPAWNED_1)
- D.key = user.key
+ user.transfer_key(D, FALSE)
qdel(src)
diff --git a/code/modules/mob/living/simple_animal/guardian/guardian.dm b/code/modules/mob/living/simple_animal/guardian/guardian.dm
index 701e244f89..8a5d06392f 100644
--- a/code/modules/mob/living/simple_animal/guardian/guardian.dm
+++ b/code/modules/mob/living/simple_animal/guardian/guardian.dm
@@ -426,9 +426,9 @@ 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(0)
+ G.ghostize(FALSE, FALSE)
G.setthemename(G.namedatum.theme) //give it a new color, to show it's a new person
- G.key = C.key
+ C.transfer_key(G)
G.reset = 1
switch(G.namedatum.theme)
if("tech")
diff --git a/code/modules/mob/living/simple_animal/hostile/giant_spider.dm b/code/modules/mob/living/simple_animal/hostile/giant_spider.dm
index cfdf302d6b..bc644ba9ec 100644
--- a/code/modules/mob/living/simple_animal/hostile/giant_spider.dm
+++ b/code/modules/mob/living/simple_animal/hostile/giant_spider.dm
@@ -90,7 +90,7 @@
if(key)
to_chat(user, "Someone else already took this spider.")
return 1
- key = user.key
+ user.transfer_key(src, FALSE)
return 1
//nursemaids - these create webs and eggs
diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm
index c491b3e78d..d626af96f6 100644
--- a/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm
+++ b/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm
@@ -585,7 +585,7 @@ Difficulty: Very Hard
var/be_helper = alert("Become a Lightgeist? (Warning, You can no longer be cloned!)",,"Yes","No")
if(be_helper == "Yes" && !QDELETED(src) && isobserver(user))
var/mob/living/simple_animal/hostile/lightgeist/W = new /mob/living/simple_animal/hostile/lightgeist(get_turf(loc))
- W.key = user.key
+ user.transfer_key(W, FALSE)
/obj/machinery/anomalous_crystal/helpers/Topic(href, href_list)
@@ -649,7 +649,7 @@ Difficulty: Very Hard
L.heal_overall_damage(heal_power, heal_power)
new /obj/effect/temp_visual/heal(get_turf(target), "#80F5FF")
-/mob/living/simple_animal/hostile/lightgeist/ghostize()
+/mob/living/simple_animal/hostile/lightgeist/ghostize(can_reenter_corpse = TRUE, send_the_signal = TRUE)
. = ..()
if(.)
death()
diff --git a/code/modules/mob/living/simple_animal/parrot.dm b/code/modules/mob/living/simple_animal/parrot.dm
index 4fdf431fa0..25b9314fb1 100644
--- a/code/modules/mob/living/simple_animal/parrot.dm
+++ b/code/modules/mob/living/simple_animal/parrot.dm
@@ -920,7 +920,7 @@
if(mind)
mind.transfer_to(G)
else
- G.key = key
+ transfer_key(G)
..(gibbed)
/mob/living/simple_animal/parrot/Poly/proc/Read_Memory()
diff --git a/code/modules/mob/living/simple_animal/slime/powers.dm b/code/modules/mob/living/simple_animal/slime/powers.dm
index 96e84a1754..48d9e65717 100644
--- a/code/modules/mob/living/simple_animal/slime/powers.dm
+++ b/code/modules/mob/living/simple_animal/slime/powers.dm
@@ -174,7 +174,7 @@
if(src.mind)
src.mind.transfer_to(new_slime)
else
- new_slime.key = src.key
+ transfer_key(new_slime)
qdel(src)
else
to_chat(src, "I am not ready to reproduce yet...")
diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm
index 9d662b1673..113f8a54c8 100644
--- a/code/modules/mob/mob.dm
+++ b/code/modules/mob/mob.dm
@@ -436,7 +436,10 @@
// M.Login() //wat
return
-
+/mob/proc/transfer_key(mob/new_mob, send_signal = TRUE)
+ if(send_signal)
+ SEND_SIGNAL(src, COMSIG_MOB_KEY_CHANGE, new_mob)
+ new_mob.key = key
/mob/verb/cancel_camera()
set name = "Cancel Camera View"
diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm
index 776bd04935..80a65f56c6 100644
--- a/code/modules/mob/mob_helpers.dm
+++ b/code/modules/mob/mob_helpers.dm
@@ -429,8 +429,8 @@ 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(0)
- M.key = C.key
+ M.ghostize(FALSE, FALSE)
+ C.transfer_key(M, FALSE)
return TRUE
else
to_chat(M, "There were no ghosts willing to take control.")
diff --git a/code/modules/mob/mob_transformation_simple.dm b/code/modules/mob/mob_transformation_simple.dm
index 673548ff48..11ff21b28a 100644
--- a/code/modules/mob/mob_transformation_simple.dm
+++ b/code/modules/mob/mob_transformation_simple.dm
@@ -53,7 +53,7 @@
if(mind && isliving(M))
mind.transfer_to(M, 1) // second argument to force key move to new mob
else
- M.key = key
+ transfer_key(M)
if(delete_old_mob)
QDEL_IN(src, 1)
diff --git a/code/modules/mob/transform_procs.dm b/code/modules/mob/transform_procs.dm
index 5703e2190b..ef25aa3fd4 100644
--- a/code/modules/mob/transform_procs.dm
+++ b/code/modules/mob/transform_procs.dm
@@ -382,7 +382,7 @@
mind.active = FALSE
mind.transfer_to(R)
else if(transfer_after)
- R.key = key
+ transfer_key(R)
R.apply_pref_name("cyborg")
@@ -425,7 +425,7 @@
new_xeno = new /mob/living/carbon/alien/humanoid/drone(loc)
new_xeno.a_intent = INTENT_HARM
- new_xeno.key = key
+ transfer_key(new_xeno)
to_chat(new_xeno, "You are now an alien.")
. = new_xeno
@@ -457,7 +457,7 @@
else
new_slime = new /mob/living/simple_animal/slime(loc)
new_slime.a_intent = INTENT_HARM
- new_slime.key = key
+ transfer_key(new_slime)
to_chat(new_slime, "You are now a slime. Skreee!")
. = new_slime
@@ -465,7 +465,7 @@
/mob/proc/become_overmind(starting_points = 60)
var/mob/camera/blob/B = new /mob/camera/blob(get_turf(src), starting_points)
- B.key = key
+ transfer_key(B)
. = B
qdel(src)
@@ -485,7 +485,7 @@
var/mob/living/simple_animal/pet/dog/corgi/new_corgi = new /mob/living/simple_animal/pet/dog/corgi (loc)
new_corgi.a_intent = INTENT_HARM
- new_corgi.key = key
+ transfer_key(new_corgi)
to_chat(new_corgi, "You are now a Corgi. Yap Yap!")
. = new_corgi
@@ -512,7 +512,7 @@
if(mind)
mind.transfer_to(new_gorilla)
else
- new_gorilla.key = key
+ transfer_key(new_gorilla)
to_chat(new_gorilla, "You are now a gorilla. Ooga ooga!")
. = new_gorilla
qdel(src)
@@ -542,7 +542,7 @@
var/mob/new_mob = new mobpath(src.loc)
- new_mob.key = key
+ transfer_key(new_mob)
new_mob.a_intent = INTENT_HARM
@@ -561,7 +561,7 @@
var/mob/new_mob = new mobpath(src.loc)
- new_mob.key = key
+ transfer_key(new_mob)
new_mob.a_intent = INTENT_HARM
to_chat(new_mob, "You feel more... animalistic")
diff --git a/code/modules/research/xenobiology/xenobiology.dm b/code/modules/research/xenobiology/xenobiology.dm
index ca12accbed..0af9cb9011 100644
--- a/code/modules/research/xenobiology/xenobiology.dm
+++ b/code/modules/research/xenobiology/xenobiology.dm
@@ -675,7 +675,7 @@
var/list/candidates = pollCandidatesForMob("Do you want to play as [SM.name]?", ROLE_SENTIENCE, null, ROLE_SENTIENCE, 50, SM, POLL_IGNORE_SENTIENCE_POTION) // see poll_ignore.dm
if(LAZYLEN(candidates))
var/mob/dead/observer/C = pick(candidates)
- SM.key = C.key
+ C.transfer_key(SM, FALSE)
SM.mind.enslave_mind_to_creator(user)
SM.sentience_act()
to_chat(SM, "All at once it makes sense: you know what you are and who you are! Self awareness is yours!")
diff --git a/code/modules/spells/spell_types/mind_transfer.dm b/code/modules/spells/spell_types/mind_transfer.dm
index ffdd270994..021f57e873 100644
--- a/code/modules/spells/spell_types/mind_transfer.dm
+++ b/code/modules/spells/spell_types/mind_transfer.dm
@@ -70,12 +70,12 @@ 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(0)
+ var/mob/dead/observer/ghost = victim.ghostize(FALSE, FALSE)
caster.mind.transfer_to(victim)
ghost.mind.transfer_to(caster)
if(ghost.key)
- caster.key = ghost.key //have to transfer the key since the mind was not active
+ ghost.transfer_key(caster) //have to transfer the key since the mind was not active
qdel(ghost)
//MIND TRANSFER END
diff --git a/modular_citadel/code/modules/mob/living/simple_animal/banana_spider.dm b/modular_citadel/code/modules/mob/living/simple_animal/banana_spider.dm
index d2006cd12c..6b15065095 100644
--- a/modular_citadel/code/modules/mob/living/simple_animal/banana_spider.dm
+++ b/modular_citadel/code/modules/mob/living/simple_animal/banana_spider.dm
@@ -38,7 +38,7 @@
return
to_chat(user, "You decide to wake up the banana spider...")
awakening = 1
-
+
spawn(30)
if(!QDELETED(src))
var/mob/living/simple_animal/banana_spider/S = new /mob/living/simple_animal/banana_spider(get_turf(src.loc))
@@ -98,7 +98,7 @@
if(be_spider == "No" || QDELETED(src) || !isobserver(user))
return
sentience_act()
- key = user.key
+ user.transfer_key(src, FALSE)
density = TRUE
/mob/living/simple_animal/banana_spider/ComponentInitialize()
diff --git a/tgstation.dme b/tgstation.dme
index 739176a457..e0acab0d6a 100755
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -373,6 +373,7 @@
#include "code\datums\components\swarming.dm"
#include "code\datums\components\thermite.dm"
#include "code\datums\components\uplink.dm"
+#include "code\datums\components\virtual_reality.dm"
#include "code\datums\components\wearertargeting.dm"
#include "code\datums\components\wet_floor.dm"
#include "code\datums\components\decals\blood.dm"
@@ -2798,7 +2799,7 @@
#include "code\modules\vending\toys.dm"
#include "code\modules\vending\wardrobes.dm"
#include "code\modules\vending\youtool.dm"
-#include "code\modules\VR\vr_human.dm"
+#include "code\modules\VR\vr_mob.dm"
#include "code\modules\VR\vr_sleeper.dm"
#include "code\modules\zombie\items.dm"
#include "code\modules\zombie\organs.dm"