From 3891c91799ea03e6f506a13117a70127f2a94b09 Mon Sep 17 00:00:00 2001
From: Ghommie <42542238+Ghommie@users.noreply.github.com>
Date: Sat, 14 Dec 2019 17:19:48 +0100
Subject: [PATCH 1/4] virtual reality component improvements, fixes and
documentation.
---
code/__DEFINES/components.dm | 11 +-
code/datums/components/_component.dm | 4 +-
code/datums/components/bane.dm | 2 +
code/datums/components/bouncy.dm | 2 +
code/datums/components/decal.dm | 2 +
code/datums/components/fantasy/_fantasy.dm | 2 +
code/datums/components/igniter.dm | 2 +
code/datums/components/knockback.dm | 2 +
code/datums/components/lifesteal.dm | 2 +
code/datums/components/nanites.dm | 2 +
code/datums/components/orbiter.dm | 2 +
code/datums/components/shrapnel.dm | 2 +
code/datums/components/summoning.dm | 2 +
code/datums/components/tactical.dm | 2 +
code/datums/components/virtual_reality.dm | 234 ++++++++++++++-------
code/datums/components/wet_floor.dm | 2 +
code/game/machinery/_machinery.dm | 5 +-
code/modules/VR/vr_mob.dm | 2 +-
code/modules/VR/vr_sleeper.dm | 64 +++---
code/modules/mob/dead/observer/observer.dm | 2 +-
code/modules/mob/living/death.dm | 11 +-
21 files changed, 237 insertions(+), 122 deletions(-)
diff --git a/code/__DEFINES/components.dm b/code/__DEFINES/components.dm
index 4f56b07fe7..aab8b340be 100644
--- a/code/__DEFINES/components.dm
+++ b/code/__DEFINES/components.dm
@@ -36,8 +36,10 @@
//////////////////////////////////////////////////////////////////
// /datum signals
-#define COMSIG_COMPONENT_ADDED "component_added" //when a component is added to a datum: (/datum/component)
-#define COMSIG_COMPONENT_REMOVING "component_removing" //before a component is removed from a datum because of RemoveComponent: (/datum/component)
+#define COMSIG_COMPONENT_ADDED "component_added" //sent to the new datum parent when a component is added to them: (/datum/component)
+#define COMSIG_COMPONENT_REMOVING "component_removing" //sent to the datum parent before a component is removed from them because of RemoveComponent: (/datum/component)
+#define COMSIG_COMPONENT_UNREGISTER_PARENT "component_unregister_parent" //sent to the component itself when unregistered from a parent
+#define COMSIG_COMPONENT_REGISTER_PARENT "component_register_parent" //sent to the component itself when registered to a parent
#define COMSIG_PARENT_PREQDELETED "parent_preqdeleted" //before a datum's Destroy() is called: (force), returning a nonzero value will cancel the qdel operation
#define COMSIG_PARENT_QDELETING "parent_qdeleting" //just before a datum's Destroy() is called: (force), at this point none of the other components chose to interrupt qdel and Destroy will be called
@@ -140,7 +142,7 @@
#define HEARING_RAW_MESSAGE 4
/* #define HEARING_RADIO_FREQ 5
#define HEARING_SPANS 6
- #define HEARING_MESSAGE_MODE 7
+ #define HEARING_MESSAGE_MODE 7
#define HEARING_SOURCE 8*/
#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)
#define COMSIG_MOVABLE_TELEPORTED "movable_teleported" //from base of do_teleport(): (channel, turf/origin, turf/destination)
@@ -152,7 +154,8 @@
#define COMSIG_MOB_EXAMINATE "mob_examinate" //from base of /mob/verb/examinate(): (atom/A)
#define COMPONENT_ALLOW_EXAMINE 1
#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_DEATH_BROADCAST 1 //stops the death from being broadcasted in deathchat.
+#define COMSIG_MOB_GHOSTIZE "mob_ghostize" //from base of mob/Ghostize(): (can_reenter_corpse, special, penalize)
#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(): (mob/user, magic, holy, tinfoil, chargecost, self, protection_sources)
diff --git a/code/datums/components/_component.dm b/code/datums/components/_component.dm
index f938a70308..72b9681f91 100644
--- a/code/datums/components/_component.dm
+++ b/code/datums/components/_component.dm
@@ -53,7 +53,7 @@
// If you want/expect to be moving the component around between parents, use this to register on the parent for signals
/datum/component/proc/RegisterWithParent()
- return
+ SEND_SIGNAL(src, COMSIG_COMPONENT_REGISTER_PARENT) //CITADEL EDIT
/datum/component/proc/Initialize(...)
return
@@ -85,7 +85,7 @@
UnregisterFromParent()
/datum/component/proc/UnregisterFromParent()
- return
+ SEND_SIGNAL(src, COMSIG_COMPONENT_UNREGISTER_PARENT) //CITADEL EDIT
/datum/proc/RegisterSignal(datum/target, sig_type_or_types, proctype, override = FALSE)
if(QDELETED(src) || QDELETED(target))
diff --git a/code/datums/components/bane.dm b/code/datums/components/bane.dm
index 84f8010270..bdfcfed517 100644
--- a/code/datums/components/bane.dm
+++ b/code/datums/components/bane.dm
@@ -19,12 +19,14 @@
src.damage_multiplier = damage_multiplier
/datum/component/bane/RegisterWithParent()
+ . = ..()
if(speciestype)
RegisterSignal(parent, COMSIG_ITEM_AFTERATTACK, .proc/speciesCheck)
else
RegisterSignal(parent, COMSIG_ITEM_AFTERATTACK, .proc/mobCheck)
/datum/component/bane/UnregisterFromParent()
+ . = ..()
UnregisterSignal(parent, COMSIG_ITEM_AFTERATTACK)
/datum/component/bane/proc/speciesCheck(obj/item/source, atom/target, mob/user, proximity_flag, click_parameters)
diff --git a/code/datums/components/bouncy.dm b/code/datums/components/bouncy.dm
index f6a2a89195..c7ca85455b 100644
--- a/code/datums/components/bouncy.dm
+++ b/code/datums/components/bouncy.dm
@@ -21,9 +21,11 @@
RegisterSignal(parent, bounce, .proc/bounce_up)
/datum/component/bouncy/RegisterWithParent()
+ . = ..()
RegisterSignal(parent, bounce_signals, .proc/bounce_up)
/datum/component/bouncy/UnregisterFromParent()
+ . = ..()
UnregisterSignal(parent, bounce_signals)
/datum/component/bouncy/proc/bounce_up(datum/source)
diff --git a/code/datums/components/decal.dm b/code/datums/components/decal.dm
index 641dbdb1cf..60317797a7 100644
--- a/code/datums/components/decal.dm
+++ b/code/datums/components/decal.dm
@@ -17,6 +17,7 @@
apply()
/datum/component/decal/RegisterWithParent()
+ . = ..()
if(first_dir)
RegisterSignal(parent, COMSIG_ATOM_DIR_CHANGE, .proc/rotate_react)
if(cleanable)
@@ -25,6 +26,7 @@
RegisterSignal(parent, COMSIG_PARENT_EXAMINE, .proc/examine)
/datum/component/decal/UnregisterFromParent()
+ . = ..()
UnregisterSignal(parent, list(COMSIG_ATOM_DIR_CHANGE, COMSIG_COMPONENT_CLEAN_ACT, COMSIG_PARENT_EXAMINE))
/datum/component/decal/Destroy()
diff --git a/code/datums/components/fantasy/_fantasy.dm b/code/datums/components/fantasy/_fantasy.dm
index 86e016784a..9e8493b6f4 100644
--- a/code/datums/components/fantasy/_fantasy.dm
+++ b/code/datums/components/fantasy/_fantasy.dm
@@ -30,11 +30,13 @@
return ..()
/datum/component/fantasy/RegisterWithParent()
+ . = ..()
var/obj/item/master = parent
originalName = master.name
modify()
/datum/component/fantasy/UnregisterFromParent()
+ . = ..()
unmodify()
/datum/component/fantasy/InheritComponent(datum/component/fantasy/newComp, original, list/arguments)
diff --git a/code/datums/components/igniter.dm b/code/datums/components/igniter.dm
index b40383e828..13944b1200 100644
--- a/code/datums/components/igniter.dm
+++ b/code/datums/components/igniter.dm
@@ -9,6 +9,7 @@
src.fire_stacks = fire_stacks
/datum/component/igniter/RegisterWithParent()
+ . = ..()
if(ismachinery(parent) || isstructure(parent) || isgun(parent)) // turrets, etc
RegisterSignal(parent, COMSIG_PROJECTILE_ON_HIT, .proc/projectile_hit)
else if(isitem(parent))
@@ -17,6 +18,7 @@
RegisterSignal(parent, COMSIG_HOSTILE_ATTACKINGTARGET, .proc/hostile_attackingtarget)
/datum/component/igniter/UnregisterFromParent()
+ . = ..()
UnregisterSignal(parent, list(COMSIG_ITEM_AFTERATTACK, COMSIG_HOSTILE_ATTACKINGTARGET, COMSIG_PROJECTILE_ON_HIT))
/datum/component/igniter/proc/item_afterattack(obj/item/source, atom/target, mob/user, proximity_flag, click_parameters)
diff --git a/code/datums/components/knockback.dm b/code/datums/components/knockback.dm
index b4fcaa2dd8..988a0e575e 100644
--- a/code/datums/components/knockback.dm
+++ b/code/datums/components/knockback.dm
@@ -10,6 +10,7 @@
src.throw_anchored = throw_anchored
/datum/component/knockback/RegisterWithParent()
+ . = ..()
if(ismachinery(parent) || isstructure(parent) || isgun(parent)) // turrets, etc
RegisterSignal(parent, COMSIG_PROJECTILE_ON_HIT, .proc/projectile_hit)
else if(isitem(parent))
@@ -18,6 +19,7 @@
RegisterSignal(parent, COMSIG_HOSTILE_ATTACKINGTARGET, .proc/hostile_attackingtarget)
/datum/component/knockback/UnregisterFromParent()
+ . = ..()
UnregisterSignal(parent, list(COMSIG_ITEM_AFTERATTACK, COMSIG_HOSTILE_ATTACKINGTARGET, COMSIG_PROJECTILE_ON_HIT))
/datum/component/knockback/proc/item_afterattack(obj/item/source, atom/target, mob/user, proximity_flag, click_parameters)
diff --git a/code/datums/components/lifesteal.dm b/code/datums/components/lifesteal.dm
index c7a78e10a3..9d62d32866 100644
--- a/code/datums/components/lifesteal.dm
+++ b/code/datums/components/lifesteal.dm
@@ -10,6 +10,7 @@
src.flat_heal = flat_heal
/datum/component/lifesteal/RegisterWithParent()
+ . = ..()
if(isgun(parent))
RegisterSignal(parent, COMSIG_PROJECTILE_ON_HIT, .proc/projectile_hit)
else if(isitem(parent))
@@ -18,6 +19,7 @@
RegisterSignal(parent, COMSIG_HOSTILE_ATTACKINGTARGET, .proc/hostile_attackingtarget)
/datum/component/lifesteal/UnregisterFromParent()
+ . = ..()
UnregisterSignal(parent, list(COMSIG_ITEM_AFTERATTACK, COMSIG_HOSTILE_ATTACKINGTARGET, COMSIG_PROJECTILE_ON_HIT))
/datum/component/lifesteal/proc/item_afterattack(obj/item/source, atom/target, mob/user, proximity_flag, click_parameters)
diff --git a/code/datums/components/nanites.dm b/code/datums/components/nanites.dm
index 362961a24f..0ef13b514b 100644
--- a/code/datums/components/nanites.dm
+++ b/code/datums/components/nanites.dm
@@ -34,6 +34,7 @@
cloud_sync()
/datum/component/nanites/RegisterWithParent()
+ . = ..()
RegisterSignal(parent, COMSIG_HAS_NANITES, .proc/confirm_nanites)
RegisterSignal(parent, COMSIG_NANITE_UI_DATA, .proc/nanite_ui_data)
RegisterSignal(parent, COMSIG_NANITE_GET_PROGRAMS, .proc/get_programs)
@@ -57,6 +58,7 @@
RegisterSignal(parent, COMSIG_NANITE_SIGNAL, .proc/receive_signal)
/datum/component/nanites/UnregisterFromParent()
+ . = ..()
UnregisterSignal(parent, list(COMSIG_HAS_NANITES,
COMSIG_NANITE_UI_DATA,
COMSIG_NANITE_GET_PROGRAMS,
diff --git a/code/datums/components/orbiter.dm b/code/datums/components/orbiter.dm
index efa0fd14d5..05174c196b 100644
--- a/code/datums/components/orbiter.dm
+++ b/code/datums/components/orbiter.dm
@@ -20,12 +20,14 @@
begin_orbit(orbiter, radius, clockwise, rotation_speed, rotation_segments, pre_rotation)
/datum/component/orbiter/RegisterWithParent()
+ . = ..()
var/atom/target = parent
while(ismovableatom(target))
RegisterSignal(target, COMSIG_MOVABLE_MOVED, .proc/move_react)
target = target.loc
/datum/component/orbiter/UnregisterFromParent()
+ . = ..()
var/atom/target = parent
while(ismovableatom(target))
UnregisterSignal(target, COMSIG_MOVABLE_MOVED)
diff --git a/code/datums/components/shrapnel.dm b/code/datums/components/shrapnel.dm
index a911221f26..4d1fe21b95 100644
--- a/code/datums/components/shrapnel.dm
+++ b/code/datums/components/shrapnel.dm
@@ -13,10 +13,12 @@
src.override_projectile_range = override_projectile_range
/datum/component/shrapnel/RegisterWithParent()
+ . = ..()
if(ismachinery(parent) || isstructure(parent) || isgun(parent)) // turrets, etc
RegisterSignal(parent, COMSIG_PROJECTILE_ON_HIT, .proc/projectile_hit)
/datum/component/shrapnel/UnregisterFromParent()
+ . = ..()
UnregisterSignal(parent, list(COMSIG_PROJECTILE_ON_HIT))
/datum/component/shrapnel/proc/projectile_hit(atom/fired_from, atom/movable/firer, atom/target, Angle)
diff --git a/code/datums/components/summoning.dm b/code/datums/components/summoning.dm
index 552959603d..61718301b3 100644
--- a/code/datums/components/summoning.dm
+++ b/code/datums/components/summoning.dm
@@ -24,6 +24,7 @@
src.faction = faction
/datum/component/summoning/RegisterWithParent()
+ . = ..()
if(ismachinery(parent) || isstructure(parent) || isgun(parent)) // turrets, etc
RegisterSignal(parent, COMSIG_PROJECTILE_ON_HIT, .proc/projectile_hit)
else if(isitem(parent))
@@ -32,6 +33,7 @@
RegisterSignal(parent, COMSIG_HOSTILE_ATTACKINGTARGET, .proc/hostile_attackingtarget)
/datum/component/summoning/UnregisterFromParent()
+ . = ..()
UnregisterSignal(parent, list(COMSIG_ITEM_AFTERATTACK, COMSIG_HOSTILE_ATTACKINGTARGET, COMSIG_PROJECTILE_ON_HIT))
/datum/component/summoning/proc/item_afterattack(obj/item/source, atom/target, mob/user, proximity_flag, click_parameters)
diff --git a/code/datums/components/tactical.dm b/code/datums/components/tactical.dm
index 5917fc3009..ba028e2fd5 100644
--- a/code/datums/components/tactical.dm
+++ b/code/datums/components/tactical.dm
@@ -9,10 +9,12 @@
src.allowed_slot = allowed_slot
/datum/component/tactical/RegisterWithParent()
+ . = ..()
RegisterSignal(parent, COMSIG_ITEM_EQUIPPED, .proc/modify)
RegisterSignal(parent, COMSIG_ITEM_DROPPED, .proc/unmodify)
/datum/component/tactical/UnregisterFromParent()
+ . = ..()
UnregisterSignal(parent, list(COMSIG_ITEM_EQUIPPED, COMSIG_ITEM_DROPPED))
unmodify()
diff --git a/code/datums/components/virtual_reality.dm b/code/datums/components/virtual_reality.dm
index 7bad836e47..bd48676541 100644
--- a/code/datums/components/virtual_reality.dm
+++ b/code/datums/components/virtual_reality.dm
@@ -1,128 +1,204 @@
+/**
+ * The virtual reality turned component.
+ * Originally created to overcome issues of mob polymorphing locking the player inside virtual reality
+ * and allow for a more "realistic" virtual reality in a virtual reality experience.
+ * (I was there when VR sleepers were first tested on /tg/station, it was whacky.)
+ * In short, a barebone not so hardcoded VR framework.
+ * If you plan to add more devices that make use of this component, remember to isolate their specific code outta here where possible.
+ */
/datum/component/virtual_reality
can_transfer = TRUE
- var/datum/mind/mastermind // where is my mind t. pixies
+ //the player's mind (not the parent's), should something happen to them or to their mob.
+ var/datum/mind/mastermind
+ //the current mob's mind, which we need to keep track for mind transfer.
var/datum/mind/current_mind
- var/obj/machinery/vr_sleeper/vr_sleeper
+ //the action datum used by the mob to quit the vr session.
var/datum/action/quit_vr/quit_action
+ //This one's name should be self explainatory, currently used for emags.
var/you_die_in_the_game_you_die_for_real = FALSE
- var/datum/component/virtual_reality/inception //The component works on a very fragile link betwixt mind, ckey and death.
+ //Used to allow people to play recursively playing vr while playing vr without many issues.
+ var/datum/component/virtual_reality/inception
+ //Used to stop the component from executing certain functions that'd cause us some issues otherwise.
+ //FALSE if there is a connected player, otherwise TRUE.
+ var/session_paused = TRUE
+ //Used to stop unwarranted behaviour from happening in cases where the master mind transference is unsupported. Set on Initialize().
+ var/allow_mastermind_transfer = FALSE
-/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
+/datum/component/virtual_reality/Initialize(yolo = FALSE, _allow_mastermind_transfer = FALSE)
var/mob/vr_M = parent
- mastermind = M.mind
- RegisterSignal(M, list(COMSIG_MOB_DEATH, COMSIG_PARENT_QDELETING), .proc/game_over)
- RegisterSignal(M, COMSIG_MOB_KEY_CHANGE, .proc/switch_player)
- RegisterSignal(mastermind, COMSIG_MIND_TRANSFER, .proc/switch_player)
+ if(!istype(vr_M) || !vr_M.mind)
+ return COMPONENT_INCOMPATIBLE
you_die_in_the_game_you_die_for_real = yolo
- quit_action = new()
- 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)
- vr_M.ckey = M.ckey
- var/datum/component/virtual_reality/clusterfk = M.GetComponent(/datum/component/virtual_reality)
- if(clusterfk && !clusterfk.inception)
- clusterfk.inception = src
- SStgui.close_user_uis(M, src)
+ allow_mastermind_transfer = _allow_mastermind_transfer
+ quit_action = new
+
+/datum/component/virtual_reality/Destroy()
+ QDEL_NULL(quit_action)
+ return ..()
/datum/component/virtual_reality/RegisterWithParent()
+ . = ..()
var/mob/M = parent
current_mind = M.mind
+ if(!quit_action)
+ quit_action = new
quit_action.Grant(M)
- RegisterSignal(quit_action, COMSIG_ACTION_TRIGGER, .proc/revert_to_reality)
+ RegisterSignal(quit_action, COMSIG_ACTION_TRIGGER, .proc/action_trigger)
RegisterSignal(M, list(COMSIG_MOB_DEATH, COMSIG_PARENT_QDELETING), .proc/game_over)
RegisterSignal(M, COMSIG_MOB_GHOSTIZE, .proc/be_a_quitter)
RegisterSignal(M, COMSIG_MOB_KEY_CHANGE, .proc/pass_me_the_remote)
RegisterSignal(current_mind, COMSIG_MIND_TRANSFER, .proc/pass_me_the_remote)
- mastermind.current.audiovisual_redirect = M
- if(vr_sleeper)
- vr_sleeper.vr_mob = M
+ if(mastermind)
+ mastermind.current.audiovisual_redirect = M
/datum/component/virtual_reality/UnregisterFromParent()
- quit_action.Remove(parent)
+ . = ..()
+ if(quit_action)
+ quit_action.Remove(parent)
+ UnregisterSignal(quit_action, COMSIG_ACTION_TRIGGER)
UnregisterSignal(parent, list(COMSIG_MOB_DEATH, COMSIG_PARENT_QDELETING, COMSIG_MOB_KEY_CHANGE, COMSIG_MOB_GHOSTIZE))
UnregisterSignal(current_mind, COMSIG_MIND_TRANSFER)
- UnregisterSignal(quit_action, COMSIG_ACTION_TRIGGER)
current_mind = null
- mastermind.current.audiovisual_redirect = null
+ if(mastermind)
+ mastermind.current.audiovisual_redirect = null
+/**
+ * Called when attempting to connect a mob to a virtual reality mob.
+ * This will return FALSE if the mob is without player or dead.
+ */
+/datum/component/virtual_reality/proc/connect(mob/M)
+ if(!M.mind || M.stat == DEAD)
+ return FALSE
+ RegisterSignal(M, list(COMSIG_MOB_DEATH, COMSIG_PARENT_QDELETING), .proc/game_over)
+ mastermind = M.mind
+ RegisterSignal(mastermind, COMSIG_MIND_TRANSFER, .proc/switch_player)
+ var/datum/component/virtual_reality/clusterfk = M.GetComponent(/datum/component/virtual_reality)
+ if(clusterfk)
+ clusterfk.inception = src
+ var/mob/vr_M = parent
+ SStgui.close_user_uis(M, src)
+ M.transfer_ckey(vr_M, FALSE)
+ session_paused = FALSE
+ return TRUE
+
+/**
+ * Called when the mastermind mind is transferred to another mob.
+ * This is pretty much going to simply quit the session until machineries support polymorphed occupants etcetera.
+ */
/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 et similar... Or did something fuck up?
- revert_to_reality()
+ if(!allow_mastermind_transfer)
+ quit()
return
old_mob.audiovisual_redirect = null
new_mob.audiovisual_redirect = parent
-/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)
-
+/**
+ * VR sleeper emag_act() hook.
+ */
/datum/component/virtual_reality/proc/you_only_live_once()
- if(you_die_in_the_game_you_die_for_real || vr_sleeper?.only_current_user_can_interact)
+ if(you_die_in_the_game_you_die_for_real)
return FALSE
you_die_in_the_game_you_die_for_real = TRUE
return TRUE
+/**
+ * Takes care of moving the component from a mob to another when their mind or ckey is transferred.
+ * The very reason this component even exists (else one would be stuck playing as a monky if monkyified)
+ * Should the new mob happen to be one of the virtual realities ultimately associated the player
+ * a 180° turn will be done and quit the session instead.
+ */
/datum/component/virtual_reality/proc/pass_me_the_remote(datum/source, mob/new_mob)
- if(new_mob == mastermind.current)
- revert_to_reality(source)
- return TRUE
+ if(mastermind && new_mob == mastermind.current)
+ quit()
+ return
+ var/datum/component/virtual_reality/VR = new_mob.GetComponent(/datum/component/virtual_reality)
+ if(VR.inception)
+ var/datum/component/virtual_reality/VR2 = VR.inception
+ var/emergency_quit = FALSE
+ while(VR2)
+ if(VR2 == src)
+ emergency_quit = TRUE
+ break
+ VR2 = VR2.inception
+ if(emergency_quit)
+ VR.inception.quit() //this will make the ckey revert back to the new mob.
+ return
new_mob.TakeComponent(src)
- return TRUE
+/**
+ * Required for the component to be transferable from mob to mob.
+ */
/datum/component/virtual_reality/PostTransfer()
if(!ismob(parent))
return COMPONENT_INCOMPATIBLE
+/**
+ *The following procs simply acts as hooks for quit(), since components do not use callbacks anymore
+ */
+/datum/component/virtual_reality/proc/action_trigger(datum/signal_source, datum/action/source)
+ quit()
+ return COMPONENT_ACTION_BLOCK_TRIGGER
+
/datum/component/virtual_reality/proc/revert_to_reality(datum/source)
- quit_it()
+ quit()
/datum/component/virtual_reality/proc/game_over(datum/source)
- quit_it(TRUE, TRUE)
+ quit(you_die_in_the_game_you_die_for_real, TRUE)
+ return COMPONENT_BLOCK_DEATH_BROADCAST
-/datum/component/virtual_reality/proc/be_a_quitter(datum/source, can_reenter_corpse)
- quit_it()
- return COMPONENT_BLOCK_GHOSTING
+/datum/component/virtual_reality/proc/be_a_quitter(datum/source, can_reenter_corpse, special = FALSE, penalize = FALSE)
+ if(!special)
+ quit()
+ return COMPONENT_BLOCK_GHOSTING
-/datum/component/virtual_reality/proc/virtual_reality_in_a_virtual_reality(mob/player, killme = FALSE, datum/component/virtual_reality/yo_dawg)
+/datum/component/virtual_reality/proc/machine_destroyed(datum/source)
+ quit(cleanup = TRUE)
+
+/**
+ * Takes care of deleting itself, moving the player back to the mastermind's current and queueing the parent for deletion.
+ * It supports nested virtual realities by recursively calling vr_in_a_vr(), which in turns calls quit(),
+ * up to the deepest level, where the ckey will be transferred back to our mastermind's mob instead.
+ * The above operation is skipped when session_paused is TRUE (ergo no player in control of the current mob).
+ * vars:
+ * * deathcheck is used to kill the master, you want this FALSE unless for stuff that doesn't involve emagging.
+ * * cleanup is used to queue the parent for the next vr_clean_master's run, where they'll be deleted should they be dead.
+ * * mob/override is used for the recursive virtual reality explained above and shouldn't be used outside of vr_in_a_vr().
+ */
+/datum/component/virtual_reality/proc/quit(deathcheck = FALSE, cleanup = FALSE, mob/override)
var/mob/M = parent
- quit_it(FALSE, killme, player, yo_dawg)
- yo_dawg.inception = null
- 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 = override ? override : mastermind.current
- if(!mastermind)
- to_chat(M, "You feel a dreadful sensation, something terrible happened. You try to wake up, but you find yourself unable to...")
- else
- var/key_transfer = FALSE
+ if(!session_paused)
+ var/mob/dreamer = override || mastermind?.current
+ if(!dreamer) //This should NEVER happen.
+ stack_trace("virtual reality component quit() called without a mob to transfer the parent key to.")
+ to_chat(M, "You feel a dreadful sensation, something terrible happened. You try to wake up, but you find yourself unable to...")
+ qdel(src)
+ return
if(inception?.parent)
- inception.virtual_reality_in_a_virtual_reality(dreamer, cleanup, src)
- else
- key_transfer = TRUE
- if(key_transfer)
+ inception.vr_in_a_vr(dreamer, deathcheck, cleanup, src)
+ else if(M.ckey)
M.transfer_ckey(dreamer, FALSE)
- dreamer.stop_sound_channel(CHANNEL_HEARTBEAT)
- dreamer.audiovisual_redirect = null
- if(deathcheck && 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(vr_sleeper)
- vr_sleeper.vr_mob = null
- vr_sleeper = null
- qdel(src)
+ if(deathcheck)
+ to_chat(dreamer, "You feel everything fading away...")
+ dreamer.death(FALSE)
+ dreamer.stop_sound_channel(CHANNEL_HEARTBEAT)
+ dreamer.audiovisual_redirect = null
+ if(cleanup)
+ var/obj/effect/vr_clean_master/cleanbot = locate() in get_area(M)
+ if(cleanbot)
+ LAZYOR(cleanbot.corpse_party, M)
+ qdel(src)
+ else if(mastermind)
+ UnregisterSignal(mastermind.current, list(COMSIG_MOB_DEATH, COMSIG_PARENT_QDELETING))
+ UnregisterSignal(mastermind, COMSIG_MIND_TRANSFER)
+ mastermind = null
+ session_paused = TRUE
-/datum/component/virtual_reality/Destroy()
- var/datum/action/quit_vr/delet_me = quit_action
- . = ..()
- qdel(delet_me)
\ No newline at end of file
+/**
+ * Used for recursive virtual realities shenanigeans and should be called only through the above proc.
+ */
+/datum/component/virtual_reality/proc/vr_in_a_vr(mob/player, deathcheck = FALSE, cleanup = FALSE, datum/component/virtual_reality/yo_dawg)
+ var/mob/M = parent
+ quit(deathcheck, cleanup, player, yo_dawg)
+ yo_dawg.inception = null
+ if(deathcheck && cleanup)
+ M.death(FALSE)
diff --git a/code/datums/components/wet_floor.dm b/code/datums/components/wet_floor.dm
index 38b17993d8..d6c5c0bf83 100644
--- a/code/datums/components/wet_floor.dm
+++ b/code/datums/components/wet_floor.dm
@@ -34,10 +34,12 @@
last_process = world.time
/datum/component/wet_floor/RegisterWithParent()
+ . = ..()
RegisterSignal(parent, COMSIG_TURF_IS_WET, .proc/is_wet)
RegisterSignal(parent, COMSIG_TURF_MAKE_DRY, .proc/dry)
/datum/component/wet_floor/UnregisterFromParent()
+ . = ..()
UnregisterSignal(parent, list(COMSIG_TURF_IS_WET, COMSIG_TURF_MAKE_DRY))
/datum/component/wet_floor/Destroy()
diff --git a/code/game/machinery/_machinery.dm b/code/game/machinery/_machinery.dm
index ff7383cd9e..5f44fccdac 100644
--- a/code/game/machinery/_machinery.dm
+++ b/code/game/machinery/_machinery.dm
@@ -181,8 +181,9 @@ Class Procs:
if(isliving(A))
var/mob/living/L = A
L.update_canmove()
- SEND_SIGNAL(src, COMSIG_MACHINE_EJECT_OCCUPANT, occupant)
- occupant = null
+ if(occupant)
+ SEND_SIGNAL(src, COMSIG_MACHINE_EJECT_OCCUPANT, occupant)
+ occupant = null
/obj/machinery/proc/can_be_occupant(atom/movable/am)
return occupant_typecache ? is_type_in_typecache(am, occupant_typecache) : isliving(am)
diff --git a/code/modules/VR/vr_mob.dm b/code/modules/VR/vr_mob.dm
index 5c0cea9f60..5f2caffc76 100644
--- a/code/modules/VR/vr_mob.dm
+++ b/code/modules/VR/vr_mob.dm
@@ -40,5 +40,5 @@
/datum/action/quit_vr/Trigger() //this merely a trigger for /datum/component/virtual_reality
. = ..()
- if(!.)
+ if(.) //The component was not there to block the trigger.
Remove(owner)
diff --git a/code/modules/VR/vr_sleeper.dm b/code/modules/VR/vr_sleeper.dm
index 72cbdc1409..b09084bcd7 100644
--- a/code/modules/VR/vr_sleeper.dm
+++ b/code/modules/VR/vr_sleeper.dm
@@ -52,18 +52,16 @@
flags_1 = NODECONSTRUCT_1
only_current_user_can_interact = TRUE
-/obj/machinery/vr_sleeper/hugbox/emag_act(mob/user)
- return SEND_SIGNAL(src, COMSIG_ATOM_EMAG_ACT)
-
/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)
- return TRUE
+ if(!only_current_user_can_interact)
+ obj_flags |= EMAGGED
+ you_die_in_the_game_you_die_for_real = TRUE
+ sparks.start()
+ addtimer(CALLBACK(src, .proc/emagNotify), 150)
+ return TRUE
/obj/machinery/vr_sleeper/update_icon()
icon_state = "[initial(icon_state)][state_open ? "-open" : ""]"
@@ -76,7 +74,7 @@
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())
+ if(user.lying || !iscarbon(target) || !Adjacent(target) || user.canUseTopic(src, BE_CLOSE, TRUE, NO_TK))
return
close_machine(target)
@@ -91,22 +89,19 @@
return
switch(action)
if("vr_connect")
- 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_mob && (!istype(vr_mob) || !vr_mob.InCritical()) && !vr_mob.GetComponent(/datum/component/virtual_reality))
- 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
+ var/mob/M = occupant
+ if(M?.mind && M == usr)
+ to_chat(M, "Transferring to virtual reality...")
+ var/datum/component/virtual_reality/VR
+ if(vr_mob)
+ VR = vr_mob.GetComponent(/datum/component/virtual_reality)
+ if(!(VR?.connect(M)))
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)
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
@@ -157,17 +152,29 @@
for(var/obj/effect/landmark/vr_spawn/V in GLOB.landmarks_list)
GLOB.vr_spawnpoints[V.vr_category] = V
-/obj/machinery/vr_sleeper/proc/new_player(mob/living/carbon/human/H, location, datum/outfit/outfit, transfer = TRUE)
- if(!H)
+/obj/machinery/vr_sleeper/proc/new_player(mob/M, location, datum/outfit/outfit, transfer = TRUE)
+ if(!M)
return
cleanup_vr_mob()
vr_mob = new virtual_mob_type(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, src, you_die_in_the_game_you_die_for_real)
+ if(vr_mob.build_virtual_character(M, outfit) && iscarbon(vr_mob))
+ var/mob/living/carbon/C = vr_mob
+ C.updateappearance(TRUE, TRUE, TRUE)
+ var/datum/component/virtual_reality/VR = vr_mob.AddComponent(/datum/component/virtual_reality, you_die_in_the_game_you_die_for_real)
+ if(VR.connect(M))
+ RegisterSignal(VR, COMSIG_COMPONENT_UNREGISTER_PARENT, .proc/unset_vr_mob)
+ RegisterSignal(VR, COMSIG_COMPONENT_REGISTER_PARENT, .proc/set_vr_mob)
+ if(!only_current_user_can_interact)
+ VR.RegisterSignal(src, COMSIG_ATOM_EMAG_ACT, /datum/component/virtual_reality.proc/you_only_live_once)
+ VR.RegisterSignal(src, COMSIG_MACHINE_EJECT_OCCUPANT, /datum/component/virtual_reality.proc/revert_to_reality)
+ VR.RegisterSignal(src, COMSIG_PARENT_QDELETING, /datum/component/virtual_reality.proc/machine_destroyed)
+ to_chat(vr_mob, "Transfer successful! You are now playing as [vr_mob] in VR!")
+
+/obj/machinery/vr_sleeper/proc/unset_vr_mob(datum/component/virtual_reality/VR)
+ vr_mob = null
+
+/obj/machinery/vr_sleeper/proc/set_vr_mob(datum/component/virtual_reality/VR)
+ vr_mob = VR.parent
/obj/machinery/vr_sleeper/proc/cleanup_vr_mob()
if(vr_mob)
@@ -222,6 +229,7 @@
qdel(C)
for (var/A in corpse_party)
var/mob/M = A
- if(get_area(M) == vr_area && M.stat == DEAD)
+ if(M && M.stat == DEAD && get_area(M) == vr_area)
qdel(M)
+ corpse_party -= M
addtimer(CALLBACK(src, .proc/clean_up), 3 MINUTES)
diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm
index fbbd43bbe1..5bdfd174b5 100644
--- a/code/modules/mob/dead/observer/observer.dm
+++ b/code/modules/mob/dead/observer/observer.dm
@@ -265,7 +265,7 @@ Works together with spawning an observer, noted above.
/mob/proc/ghostize(can_reenter_corpse = TRUE, special = FALSE, penalize = FALSE)
penalize = suiciding || penalize // suicide squad.
- if(!key || cmptext(copytext(key,1,2),"@") || (!special && SEND_SIGNAL(src, COMSIG_MOB_GHOSTIZE, can_reenter_corpse) & COMPONENT_BLOCK_GHOSTING))
+ if(!key || cmptext(copytext(key,1,2),"@") || (SEND_SIGNAL(src, COMSIG_MOB_GHOSTIZE, can_reenter_corpse, special, penalize) & 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.
diff --git a/code/modules/mob/living/death.dm b/code/modules/mob/living/death.dm
index 0ff418d628..8345ef916d 100644
--- a/code/modules/mob/living/death.dm
+++ b/code/modules/mob/living/death.dm
@@ -62,12 +62,8 @@
unset_machine()
timeofdeath = world.time
tod = STATION_TIME_TIMESTAMP("hh:mm:ss")
- var/turf/T = get_turf(src)
for(var/obj/item/I in contents)
I.on_mob_death(src, gibbed)
- if(mind && mind.name && mind.active && !istype(T.loc, /area/ctf))
- var/rendered = "[mind.name] has died at [get_area_name(T)]."
- deadchat_broadcast(rendered, follow_target = src, turf_target = T, message_type=DEADCHAT_DEATHRATTLE)
if(mind)
mind.store_memory("Time of death: [tod]", 0)
GLOB.alive_mob_list -= src
@@ -89,7 +85,12 @@
addtimer(CALLBACK(src, .proc/med_hud_set_status), (DEFIB_TIME_LIMIT * 10) + 1)
stop_pulling()
- SEND_SIGNAL(src, COMSIG_MOB_DEATH, gibbed)
+ var/signal = SEND_SIGNAL(src, COMSIG_MOB_DEATH, gibbed)
+
+ var/turf/T = get_turf(src)
+ if(mind && mind.name && mind.active && !istype(T.loc, /area/ctf) && !(signal & COMPONENT_BLOCK_DEATH_BROADCAST))
+ var/rendered = "[mind.name] has died at [get_area_name(T)]."
+ deadchat_broadcast(rendered, follow_target = src, turf_target = T, message_type=DEADCHAT_DEATHRATTLE)
if (client)
client.move_delay = initial(client.move_delay)
From e5d45faee8be48a4296af96d233dc9c8c2131c39 Mon Sep 17 00:00:00 2001
From: Ghommie <42542238+Ghommie@users.noreply.github.com>
Date: Sat, 14 Dec 2019 18:53:53 +0100
Subject: [PATCH 2/4] deadchat, not deathchat.
---
code/__DEFINES/components.dm | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/code/__DEFINES/components.dm b/code/__DEFINES/components.dm
index aab8b340be..107631f9cc 100644
--- a/code/__DEFINES/components.dm
+++ b/code/__DEFINES/components.dm
@@ -154,7 +154,7 @@
#define COMSIG_MOB_EXAMINATE "mob_examinate" //from base of /mob/verb/examinate(): (atom/A)
#define COMPONENT_ALLOW_EXAMINE 1
#define COMSIG_MOB_DEATH "mob_death" //from base of mob/death(): (gibbed)
- #define COMPONENT_BLOCK_DEATH_BROADCAST 1 //stops the death from being broadcasted in deathchat.
+ #define COMPONENT_BLOCK_DEATH_BROADCAST 1 //stops the death from being broadcasted in deadchat.
#define COMSIG_MOB_GHOSTIZE "mob_ghostize" //from base of mob/Ghostize(): (can_reenter_corpse, special, penalize)
#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
From 081d3df75043800c149ab580652e9bdfbde3dc92 Mon Sep 17 00:00:00 2001
From: Ghommie <42542238+Ghommie@users.noreply.github.com>
Date: Sat, 14 Dec 2019 22:52:40 +0100
Subject: [PATCH 3/4] This is one serious Pepe Silvia complex.
---
code/__DEFINES/components.dm | 4 +-
code/datums/components/virtual_reality.dm | 53 +++++++++++++----------
code/datums/mind.dm | 5 ++-
code/modules/VR/vr_sleeper.dm | 2 +-
4 files changed, 37 insertions(+), 27 deletions(-)
diff --git a/code/__DEFINES/components.dm b/code/__DEFINES/components.dm
index 107631f9cc..806680d8bf 100644
--- a/code/__DEFINES/components.dm
+++ b/code/__DEFINES/components.dm
@@ -148,7 +148,9 @@
#define COMSIG_MOVABLE_TELEPORTED "movable_teleported" //from base of do_teleport(): (channel, turf/origin, turf/destination)
// /mind signals
-#define COMSIG_MIND_TRANSFER "mind_transfer" //from base of mind/transfer_to(): (new_character, old_character)
+#define COMSIG_PRE_MIND_TRANSFER "pre_mind_transfer" //from base of mind/transfer_to(): (new_character, old_character)
+ #define COMPONENT_STOP_MIND_TRANSFER 1 //stops the mind transfer from happening.
+#define COMSIG_MIND_TRANSFER "mind_transfer" //from base of mind/transfer_to(): (new_character, old_character)
// /mob signals
#define COMSIG_MOB_EXAMINATE "mob_examinate" //from base of /mob/verb/examinate(): (atom/A)
diff --git a/code/datums/components/virtual_reality.dm b/code/datums/components/virtual_reality.dm
index bd48676541..7ed259d30a 100644
--- a/code/datums/components/virtual_reality.dm
+++ b/code/datums/components/virtual_reality.dm
@@ -1,8 +1,7 @@
/**
* The virtual reality turned component.
* Originally created to overcome issues of mob polymorphing locking the player inside virtual reality
- * and allow for a more "realistic" virtual reality in a virtual reality experience.
- * (I was there when VR sleepers were first tested on /tg/station, it was whacky.)
+ * and allow for a more "immersive" virtual reality in a virtual reality experience.
* In short, a barebone not so hardcoded VR framework.
* If you plan to add more devices that make use of this component, remember to isolate their specific code outta here where possible.
*/
@@ -46,9 +45,10 @@
RegisterSignal(quit_action, COMSIG_ACTION_TRIGGER, .proc/action_trigger)
RegisterSignal(M, list(COMSIG_MOB_DEATH, COMSIG_PARENT_QDELETING), .proc/game_over)
RegisterSignal(M, COMSIG_MOB_GHOSTIZE, .proc/be_a_quitter)
- RegisterSignal(M, COMSIG_MOB_KEY_CHANGE, .proc/pass_me_the_remote)
- RegisterSignal(current_mind, COMSIG_MIND_TRANSFER, .proc/pass_me_the_remote)
- if(mastermind)
+ RegisterSignal(M, COMSIG_MOB_KEY_CHANGE, .proc/on_player_transfer)
+ RegisterSignal(current_mind, COMSIG_MIND_TRANSFER, .proc/on_player_transfer)
+ RegisterSignal(current_mind, COMSIG_PRE_MIND_TRANSFER, .proc/pre_player_transfer)
+ if(mastermind?.current)
mastermind.current.audiovisual_redirect = M
/datum/component/virtual_reality/UnregisterFromParent()
@@ -57,24 +57,24 @@
quit_action.Remove(parent)
UnregisterSignal(quit_action, COMSIG_ACTION_TRIGGER)
UnregisterSignal(parent, list(COMSIG_MOB_DEATH, COMSIG_PARENT_QDELETING, COMSIG_MOB_KEY_CHANGE, COMSIG_MOB_GHOSTIZE))
- UnregisterSignal(current_mind, COMSIG_MIND_TRANSFER)
+ UnregisterSignal(current_mind, list(COMSIG_MIND_TRANSFER, COMSIG_PRE_MIND_TRANSFER))
current_mind = null
- if(mastermind)
+ if(mastermind?.current)
mastermind.current.audiovisual_redirect = null
/**
* Called when attempting to connect a mob to a virtual reality mob.
- * This will return FALSE if the mob is without player or dead.
+ * This will return FALSE if the mob is without player or dead. TRUE otherwise
*/
/datum/component/virtual_reality/proc/connect(mob/M)
if(!M.mind || M.stat == DEAD)
return FALSE
RegisterSignal(M, list(COMSIG_MOB_DEATH, COMSIG_PARENT_QDELETING), .proc/game_over)
mastermind = M.mind
- RegisterSignal(mastermind, COMSIG_MIND_TRANSFER, .proc/switch_player)
- var/datum/component/virtual_reality/clusterfk = M.GetComponent(/datum/component/virtual_reality)
- if(clusterfk)
- clusterfk.inception = src
+ RegisterSignal(mastermind, COMSIG_PRE_MIND_TRANSFER, .proc/switch_player)
+ var/datum/component/virtual_reality/VR = M.GetComponent(/datum/component/virtual_reality)
+ if(VR)
+ VR.inception = src
var/mob/vr_M = parent
SStgui.close_user_uis(M, src)
M.transfer_ckey(vr_M, FALSE)
@@ -93,7 +93,7 @@
new_mob.audiovisual_redirect = parent
/**
- * VR sleeper emag_act() hook.
+ * emag_act() hook. Makes the game deadlier, killing the mastermind mob too should the parent die.
*/
/datum/component/virtual_reality/proc/you_only_live_once()
if(you_die_in_the_game_you_die_for_real)
@@ -102,17 +102,16 @@
return TRUE
/**
- * Takes care of moving the component from a mob to another when their mind or ckey is transferred.
- * The very reason this component even exists (else one would be stuck playing as a monky if monkyified)
- * Should the new mob happen to be one of the virtual realities ultimately associated the player
- * a 180° turn will be done and quit the session instead.
+ * Called to stop the mind transfer should the new mob happen to be the mastermind's or in a damn mess associated with us.
+ * Since the target's mind.current is null'd in the mind transfer process,
+ * This has to be done in a different signal proc than on_player_transfer(), by then the mastermind.current will be null.
*/
-/datum/component/virtual_reality/proc/pass_me_the_remote(datum/source, mob/new_mob)
+/datum/component/virtual_reality/proc/pre_player_transfer(datum/source, mob/new_mob, mob/old_mob)
if(mastermind && new_mob == mastermind.current)
quit()
- return
+ return COMPONENT_STOP_MIND_TRANSFER
var/datum/component/virtual_reality/VR = new_mob.GetComponent(/datum/component/virtual_reality)
- if(VR.inception)
+ if(VR?.inception)
var/datum/component/virtual_reality/VR2 = VR.inception
var/emergency_quit = FALSE
while(VR2)
@@ -121,8 +120,14 @@
break
VR2 = VR2.inception
if(emergency_quit)
- VR.inception.quit() //this will make the ckey revert back to the new mob.
- return
+ VR.inception.quit() //this will revert the ckey back to new_mob.
+ return COMPONENT_STOP_MIND_TRANSFER
+
+/**
+ * Takes care of moving the component from a mob to another when their mind or ckey is transferred.
+ * The very reason this component even exists (else one would be stuck playing as a monky if monkyified)
+ */
+/datum/component/virtual_reality/proc/on_player_transfer(datum/source, mob/new_mob, mob/old_mob)
new_mob.TakeComponent(src)
/**
@@ -168,7 +173,7 @@
var/mob/M = parent
if(!session_paused)
var/mob/dreamer = override || mastermind?.current
- if(!dreamer) //This should NEVER happen.
+ if(!dreamer) //This shouldn't happen.
stack_trace("virtual reality component quit() called without a mob to transfer the parent key to.")
to_chat(M, "You feel a dreadful sensation, something terrible happened. You try to wake up, but you find yourself unable to...")
qdel(src)
@@ -189,7 +194,7 @@
qdel(src)
else if(mastermind)
UnregisterSignal(mastermind.current, list(COMSIG_MOB_DEATH, COMSIG_PARENT_QDELETING))
- UnregisterSignal(mastermind, COMSIG_MIND_TRANSFER)
+ UnregisterSignal(mastermind, COMSIG_PRE_MIND_TRANSFER)
mastermind = null
session_paused = TRUE
diff --git a/code/datums/mind.dm b/code/datums/mind.dm
index 0ded5f9912..a499cd56c0 100644
--- a/code/datums/mind.dm
+++ b/code/datums/mind.dm
@@ -88,6 +88,8 @@
/datum/mind/proc/transfer_to(mob/new_character, var/force_key_move = 0)
var/old_character = current
+ if(SEND_SIGNAL(src, COMSIG_PRE_MIND_TRANSFER, new_character, old_character) & COMPONENT_STOP_MIND_TRANSFER)
+ return
if(current) // remove ourself from our old body's mind variable
current.mind = null
SStgui.on_transfer(current, new_character)
@@ -125,7 +127,6 @@
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, 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.
@@ -134,6 +135,8 @@
L.canbearoused = L.client.prefs.arousable //Technically this should make taking over a character mean the body gain the new minds setting...
L.update_arousal_hud() //Removes the old icon
+ SEND_SIGNAL(src, COMSIG_MIND_TRANSFER, new_character, old_character)
+
/datum/mind/proc/store_memory(new_text)
if((length(memory) + length(new_text)) <= MAX_MESSAGE_LEN)
memory += "[new_text]
"
diff --git a/code/modules/VR/vr_sleeper.dm b/code/modules/VR/vr_sleeper.dm
index b09084bcd7..479a65dabb 100644
--- a/code/modules/VR/vr_sleeper.dm
+++ b/code/modules/VR/vr_sleeper.dm
@@ -74,7 +74,7 @@
return ..()
/obj/machinery/vr_sleeper/MouseDrop_T(mob/target, mob/user)
- if(user.lying || !iscarbon(target) || !Adjacent(target) || user.canUseTopic(src, BE_CLOSE, TRUE, NO_TK))
+ if(user.lying || !iscarbon(target) || !Adjacent(target) || !user.canUseTopic(src, BE_CLOSE, TRUE, NO_TK))
return
close_machine(target)
From 80f47872d76bed4a66b714e55c7bc281aad1a3c6 Mon Sep 17 00:00:00 2001
From: Ghommie <42542238+Ghommie@users.noreply.github.com>
Date: Sun, 15 Dec 2019 20:31:36 +0100
Subject: [PATCH 4/4] My head splodes.
---
code/__DEFINES/components.dm | 8 +-
code/datums/components/virtual_reality.dm | 140 ++++++++++++++--------
code/datums/mind.dm | 3 +-
code/modules/VR/vr_sleeper.dm | 6 +-
code/modules/mob/mob.dm | 3 +-
5 files changed, 102 insertions(+), 58 deletions(-)
diff --git a/code/__DEFINES/components.dm b/code/__DEFINES/components.dm
index 806680d8bf..f95ff93ac3 100644
--- a/code/__DEFINES/components.dm
+++ b/code/__DEFINES/components.dm
@@ -148,9 +148,9 @@
#define COMSIG_MOVABLE_TELEPORTED "movable_teleported" //from base of do_teleport(): (channel, turf/origin, turf/destination)
// /mind signals
-#define COMSIG_PRE_MIND_TRANSFER "pre_mind_transfer" //from base of mind/transfer_to(): (new_character, old_character)
+#define COMSIG_PRE_MIND_TRANSFER "pre_mind_transfer" //from base of mind/transfer_to() before it's done: (new_character, old_character)
#define COMPONENT_STOP_MIND_TRANSFER 1 //stops the mind transfer from happening.
-#define COMSIG_MIND_TRANSFER "mind_transfer" //from base of mind/transfer_to(): (new_character, old_character)
+#define COMSIG_MIND_TRANSFER "mind_transfer" //from base of mind/transfer_to() when it's done: (new_character, old_character)
// /mob signals
#define COMSIG_MOB_EXAMINATE "mob_examinate" //from base of /mob/verb/examinate(): (atom/A)
@@ -169,7 +169,9 @@
#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_ckey()
+#define COMSIG_MOB_KEY_CHANGE "mob_key_change" //from base of /mob/transfer_ckey(): (new_character, old_character)
+#define COMSIG_MOB_PRE_PLAYER_CHANGE "mob_pre_player_change" //sent to the target mob from base of /mob/transfer_ckey() and /mind/transfer_to(): (our_character, their_character)
+// #define COMPONENT_STOP_MIND_TRANSFER 1
#define COMSIG_MOB_UPDATE_SIGHT "mob_update_sight" //from base of /mob/update_sight(): ()
#define COMSIG_MOB_SAY "mob_say" // from /mob/living/say(): (proc args list)
#define COMPONENT_UPPERCASE_SPEECH 1
diff --git a/code/datums/components/virtual_reality.dm b/code/datums/components/virtual_reality.dm
index 7ed259d30a..2f0405af2e 100644
--- a/code/datums/components/virtual_reality.dm
+++ b/code/datums/components/virtual_reality.dm
@@ -2,8 +2,9 @@
* The virtual reality turned component.
* Originally created to overcome issues of mob polymorphing locking the player inside virtual reality
* and allow for a more "immersive" virtual reality in a virtual reality experience.
+ * It relies on comically complex order of logic, expect things to break if procs such as mind/transfer_to() are revamped.
* In short, a barebone not so hardcoded VR framework.
- * If you plan to add more devices that make use of this component, remember to isolate their specific code outta here where possible.
+ * If you plan to add more devices that make use of this component, remember to isolate their code outta here where possible.
*/
/datum/component/virtual_reality
can_transfer = TRUE
@@ -16,7 +17,8 @@
//This one's name should be self explainatory, currently used for emags.
var/you_die_in_the_game_you_die_for_real = FALSE
//Used to allow people to play recursively playing vr while playing vr without many issues.
- var/datum/component/virtual_reality/inception
+ var/datum/component/virtual_reality/level_below
+ var/datum/component/virtual_reality/level_above
//Used to stop the component from executing certain functions that'd cause us some issues otherwise.
//FALSE if there is a connected player, otherwise TRUE.
var/session_paused = TRUE
@@ -24,8 +26,8 @@
var/allow_mastermind_transfer = FALSE
/datum/component/virtual_reality/Initialize(yolo = FALSE, _allow_mastermind_transfer = FALSE)
- var/mob/vr_M = parent
- if(!istype(vr_M) || !vr_M.mind)
+ var/mob/M = parent
+ if(!istype(M) || !M.mind)
return COMPONENT_INCOMPATIBLE
you_die_in_the_game_you_die_for_real = yolo
allow_mastermind_transfer = _allow_mastermind_transfer
@@ -33,6 +35,12 @@
/datum/component/virtual_reality/Destroy()
QDEL_NULL(quit_action)
+ if(level_above)
+ level_above.level_below = null
+ level_above = null
+ if(level_below)
+ level_below.level_above = null
+ level_below = null
return ..()
/datum/component/virtual_reality/RegisterWithParent()
@@ -67,31 +75,23 @@
* This will return FALSE if the mob is without player or dead. TRUE otherwise
*/
/datum/component/virtual_reality/proc/connect(mob/M)
- if(!M.mind || M.stat == DEAD)
+ var/mob/vr_M = parent
+ if(!M.mind || M.stat == DEAD || !vr_M.mind || vr_M.stat == DEAD)
return FALSE
- RegisterSignal(M, list(COMSIG_MOB_DEATH, COMSIG_PARENT_QDELETING), .proc/game_over)
- mastermind = M.mind
- RegisterSignal(mastermind, COMSIG_PRE_MIND_TRANSFER, .proc/switch_player)
var/datum/component/virtual_reality/VR = M.GetComponent(/datum/component/virtual_reality)
if(VR)
- VR.inception = src
- var/mob/vr_M = parent
- SStgui.close_user_uis(M, src)
+ VR.level_below = src
+ level_above = VR
M.transfer_ckey(vr_M, FALSE)
+ mastermind = M.mind
+ mastermind.current.audiovisual_redirect = parent
+ RegisterSignal(mastermind, COMSIG_PRE_MIND_TRANSFER, .proc/switch_player)
+ RegisterSignal(M, list(COMSIG_MOB_DEATH, COMSIG_PARENT_QDELETING), .proc/game_over)
+ RegisterSignal(M, COMSIG_MOB_PRE_PLAYER_CHANGE, .proc/player_hijacked)
+ SStgui.close_user_uis(vr_M, src)
session_paused = FALSE
return TRUE
-/**
- * Called when the mastermind mind is transferred to another mob.
- * This is pretty much going to simply quit the session until machineries support polymorphed occupants etcetera.
- */
-/datum/component/virtual_reality/proc/switch_player(datum/source, mob/new_mob, mob/old_mob)
- if(!allow_mastermind_transfer)
- quit()
- return
- old_mob.audiovisual_redirect = null
- new_mob.audiovisual_redirect = parent
-
/**
* emag_act() hook. Makes the game deadlier, killing the mastermind mob too should the parent die.
*/
@@ -102,26 +102,60 @@
return TRUE
/**
- * Called to stop the mind transfer should the new mob happen to be the mastermind's or in a damn mess associated with us.
- * Since the target's mind.current is null'd in the mind transfer process,
+ * Called when the mastermind mind is transferred to another mob.
+ * This is pretty much just going to simply quit the session until machineries support polymorphed occupants etcetera.
+ */
+/datum/component/virtual_reality/proc/switch_player(datum/source, mob/new_mob, mob/old_mob)
+ if(session_paused)
+ return
+ if(!allow_mastermind_transfer)
+ quit()
+ return COMPONENT_STOP_MIND_TRANSFER
+ UnregisterSignal(old_mob, list(COMSIG_MOB_DEATH, COMSIG_PARENT_QDELETING, COMSIG_MOB_PRE_PLAYER_CHANGE))
+ RegisterSignal(new_mob, list(COMSIG_MOB_DEATH, COMSIG_PARENT_QDELETING), .proc/game_over)
+ RegisterSignal(new_mob, COMSIG_MOB_PRE_PLAYER_CHANGE, .proc/player_hijacked)
+ old_mob.audiovisual_redirect = null
+ new_mob.audiovisual_redirect = parent
+
+/**
+ * Called to stop the player mind from being transferred should the new mob happen to be one of our masterminds'.
+ * Since the target's mind.current is going to be null'd in the mind transfer process,
* This has to be done in a different signal proc than on_player_transfer(), by then the mastermind.current will be null.
*/
/datum/component/virtual_reality/proc/pre_player_transfer(datum/source, mob/new_mob, mob/old_mob)
- if(mastermind && new_mob == mastermind.current)
+ if(!mastermind || session_paused)
+ return
+ if(new_mob == mastermind.current)
quit()
return COMPONENT_STOP_MIND_TRANSFER
- var/datum/component/virtual_reality/VR = new_mob.GetComponent(/datum/component/virtual_reality)
- if(VR?.inception)
- var/datum/component/virtual_reality/VR2 = VR.inception
- var/emergency_quit = FALSE
- while(VR2)
- if(VR2 == src)
- emergency_quit = TRUE
- break
- VR2 = VR2.inception
- if(emergency_quit)
- VR.inception.quit() //this will revert the ckey back to new_mob.
+ if(!level_above)
+ return
+ var/datum/component/virtual_reality/VR = level_above
+ while(VR)
+ if(VR.mastermind.current == new_mob)
+ VR.quit() //this will revert the ckey back to new_mob.
return COMPONENT_STOP_MIND_TRANSFER
+ VR = VR.level_above
+
+/**
+ * Called when someone or something else is somewhat about to replace the mastermind's mob key somehow.
+ * And potentially lock the player in a broken virtual reality plot. Not really something to be proud of.
+ */
+/datum/component/virtual_reality/proc/player_hijacked(datum/source, mob/our_character, mob/their_character)
+ if(session_paused)
+ return
+ if(!their_character)
+ quit(cleanup = TRUE)
+ return
+ var/will_it_be_handled_in_their_pre_player_transfer = FALSE
+ var/datum/component/virtual_reality/VR = src
+ while(VR)
+ if(VR.parent == their_character)
+ will_it_be_handled_in_their_pre_player_transfer = TRUE
+ break
+ VR = VR.level_below
+ if(!will_it_be_handled_in_their_pre_player_transfer) //it's not the player playing shenanigeans, abandon all ships.
+ quit(cleanup = TRUE)
/**
* Takes care of moving the component from a mob to another when their mind or ckey is transferred.
@@ -172,38 +206,40 @@
/datum/component/virtual_reality/proc/quit(deathcheck = FALSE, cleanup = FALSE, mob/override)
var/mob/M = parent
if(!session_paused)
- var/mob/dreamer = override || mastermind?.current
+ session_paused = TRUE
+ var/mob/dreamer = override || mastermind.current
if(!dreamer) //This shouldn't happen.
- stack_trace("virtual reality component quit() called without a mob to transfer the parent key to.")
+ stack_trace("virtual reality component quit() called without a mob to transfer the parent ckey to.")
to_chat(M, "You feel a dreadful sensation, something terrible happened. You try to wake up, but you find yourself unable to...")
qdel(src)
return
- if(inception?.parent)
- inception.vr_in_a_vr(dreamer, deathcheck, cleanup, src)
- else if(M.ckey)
+ if(level_below?.parent)
+ level_below.vr_in_a_vr(dreamer, deathcheck, (deathcheck && cleanup))
+ else
M.transfer_ckey(dreamer, FALSE)
if(deathcheck)
to_chat(dreamer, "You feel everything fading away...")
dreamer.death(FALSE)
- dreamer.stop_sound_channel(CHANNEL_HEARTBEAT)
- dreamer.audiovisual_redirect = null
+ mastermind.current.audiovisual_redirect = null
+ if(!cleanup)
+ if(level_above)
+ level_above.level_below = null
+ level_above = null
+ UnregisterSignal(mastermind.current, list(COMSIG_MOB_DEATH, COMSIG_PARENT_QDELETING, COMSIG_MOB_PRE_PLAYER_CHANGE))
+ UnregisterSignal(mastermind, COMSIG_PRE_MIND_TRANSFER)
+ mastermind = null
if(cleanup)
var/obj/effect/vr_clean_master/cleanbot = locate() in get_area(M)
if(cleanbot)
LAZYOR(cleanbot.corpse_party, M)
qdel(src)
- else if(mastermind)
- UnregisterSignal(mastermind.current, list(COMSIG_MOB_DEATH, COMSIG_PARENT_QDELETING))
- UnregisterSignal(mastermind, COMSIG_PRE_MIND_TRANSFER)
- mastermind = null
- session_paused = TRUE
/**
* Used for recursive virtual realities shenanigeans and should be called only through the above proc.
*/
-/datum/component/virtual_reality/proc/vr_in_a_vr(mob/player, deathcheck = FALSE, cleanup = FALSE, datum/component/virtual_reality/yo_dawg)
+/datum/component/virtual_reality/proc/vr_in_a_vr(mob/player, deathcheck = FALSE, lethal_cleanup = FALSE)
var/mob/M = parent
- quit(deathcheck, cleanup, player, yo_dawg)
- yo_dawg.inception = null
- if(deathcheck && cleanup)
+ quit(deathcheck, lethal_cleanup, player)
+ M.audiovisual_redirect = null
+ if(lethal_cleanup)
M.death(FALSE)
diff --git a/code/datums/mind.dm b/code/datums/mind.dm
index a499cd56c0..d1b4e51a7d 100644
--- a/code/datums/mind.dm
+++ b/code/datums/mind.dm
@@ -88,7 +88,8 @@
/datum/mind/proc/transfer_to(mob/new_character, var/force_key_move = 0)
var/old_character = current
- if(SEND_SIGNAL(src, COMSIG_PRE_MIND_TRANSFER, new_character, old_character) & COMPONENT_STOP_MIND_TRANSFER)
+ var/signals = SEND_SIGNAL(new_character, COMSIG_MOB_PRE_PLAYER_CHANGE, new_character, old_character) | SEND_SIGNAL(src, COMSIG_PRE_MIND_TRANSFER, new_character, old_character)
+ if(signals & COMPONENT_STOP_MIND_TRANSFER)
return
if(current) // remove ourself from our old body's mind variable
current.mind = null
diff --git a/code/modules/VR/vr_sleeper.dm b/code/modules/VR/vr_sleeper.dm
index 479a65dabb..7cda24d98b 100644
--- a/code/modules/VR/vr_sleeper.dm
+++ b/code/modules/VR/vr_sleeper.dm
@@ -97,7 +97,7 @@
VR = vr_mob.GetComponent(/datum/component/virtual_reality)
if(!(VR?.connect(M)))
if(allow_creating_vr_mobs)
- to_chat(occupant, "Virtual avatar not found, attempting to create one...")
+ to_chat(occupant, "Virtual avatar [vr_mob ? "corrupted" : "missing"], attempting to create one...")
var/obj/effect/landmark/vr_spawn/V = get_vr_spawnpoint()
var/turf/T = get_turf(V)
if(T)
@@ -106,6 +106,8 @@
to_chat(occupant, "Virtual world misconfigured, aborting transfer")
else
to_chat(occupant, "The virtual world does not support the creation of new virtual avatars, aborting transfer")
+ else
+ to_chat(vr_mob, "Transfer successful! You are now playing as [vr_mob] in VR!")
. = TRUE
if("delete_avatar")
if(!occupant || usr == occupant)
@@ -169,6 +171,8 @@
VR.RegisterSignal(src, COMSIG_MACHINE_EJECT_OCCUPANT, /datum/component/virtual_reality.proc/revert_to_reality)
VR.RegisterSignal(src, COMSIG_PARENT_QDELETING, /datum/component/virtual_reality.proc/machine_destroyed)
to_chat(vr_mob, "Transfer successful! You are now playing as [vr_mob] in VR!")
+ else
+ to_chat(M, "Transfer failed! virtual reality data likely corrupted!")
/obj/machinery/vr_sleeper/proc/unset_vr_mob(datum/component/virtual_reality/VR)
vr_mob = null
diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm
index d4528fb91f..a47ea9d5a0 100644
--- a/code/modules/mob/mob.dm
+++ b/code/modules/mob/mob.dm
@@ -465,9 +465,10 @@ mob/visible_message(message, self_message, blind_message, vision_distance = DEFA
/mob/proc/transfer_ckey(mob/new_mob, send_signal = TRUE)
if(!ckey || !new_mob)
CRASH("transfer_ckey() called [ckey ? "" : "on a ckey-less mob[new_mob ? "" : " and "]"][new_mob ? "" : "without a valid mob target"]!")
+ SEND_SIGNAL(new_mob, COMSIG_MOB_PRE_PLAYER_CHANGE, new_mob, src)
+ new_mob.ckey = ckey
if(send_signal)
SEND_SIGNAL(src, COMSIG_MOB_KEY_CHANGE, new_mob, src)
- new_mob.ckey = ckey
return TRUE
/mob/verb/cancel_camera()