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()