From ccebdd43499a30427d118aaebd7fde13ee02338d Mon Sep 17 00:00:00 2001 From: Atermonera Date: Mon, 30 Oct 2017 22:28:23 -0700 Subject: [PATCH 1/3] Stash pop did something --- code/game/machinery/vr_console.dm | 222 ++++++++++++++++++ .../human/species/virtual_reality/avatar.dm | 32 +++ polaris.dme | 2 + 3 files changed, 256 insertions(+) create mode 100644 code/game/machinery/vr_console.dm create mode 100644 code/modules/mob/living/carbon/human/species/virtual_reality/avatar.dm diff --git a/code/game/machinery/vr_console.dm b/code/game/machinery/vr_console.dm new file mode 100644 index 0000000000..7dabf72ecf --- /dev/null +++ b/code/game/machinery/vr_console.dm @@ -0,0 +1,222 @@ +/obj/machinery/vr_sleeper + name = "VR Sleeper" + desc = "A fancy bed with built-in sensory I/O ports and connectors to interface users' minds with their bodies in virtual reality." + icon = 'icons/obj/Cryogenic2.dmi' + icon_state = "syndipod_0" + density = 1 + anchored = 1 + circuit = /obj/item/weapon/circuitboard/vr_sleeper + var/mob/living/carbon/human/occupant = null + var/mob/living/carbon/human/avatar = null + var/datum/mind/vr_mind = null + + use_power = 1 + idle_power_usage = 15 + active_power_usage = 200 + light_color = "#FF0000" + +/obj/machinery/vr_sleeper/New() + ..() + component_parts = list() + component_parts += new /obj/item/weapon/stock_parts/scanning_module(src) + component_parts += new /obj/item/stack/material/glass/reinforced(src, 2) + + RefreshParts() + +/obj/machinery/vr_sleeper/initialize() + update_icon() + +/obj/machinery/vr_sleeper/process() + if(stat & (NOPOWER|BROKEN)) + return + +/obj/machinery/vr_sleeper/update_icon() + icon_state = "syndipod_[occupant ? "1" : "0"]" + +/obj/machinery/vr_sleeper/Topic(href, href_list) + if(..()) + return 1 + + if(usr == occupant) + to_chat(usr, "You can't reach the controls from the inside.") + return + + add_fingerprint(usr) + + if(href_list["eject"]) + go_out() + + return 1 + +/obj/machinery/vr_sleeper/attackby(var/obj/item/I, var/mob/user) + add_fingerprint(user) + if(default_deconstruction_screwdriver(user, I)) + return + else if(default_deconstruction_crowbar(user, I)) + return + + +/obj/machinery/vr_sleeper/MouseDrop_T(var/mob/target, var/mob/user) + if(user.stat || user.lying || !Adjacent(user) || !target.Adjacent(user)|| !isliving(target)) + return + go_in(target, user) + + + +/obj/machinery/sleeper/relaymove(var/mob/user) + ..() + if(usr.incapacitated()) + return + go_out() + + + +/obj/machinery/vr_sleeper/emp_act(var/severity) + if(stat & (BROKEN|NOPOWER)) + ..(severity) + return + + if(occupant) + // This will eject the user from VR + // ### Fry the brain? + go_out() + + ..(severity) + +/obj/machinery/vr_sleeper/verb/eject() + set src in oview(1) + set category = "Object" + set name = "Eject VR Capsule" + + if(usr.incapacitated()) + return + + if(usr != occupant && avatar && alert(avatar, "Someone wants to remove you from virtual reality. Do you want to leave?", "Leave VR?", "Yes", "No") == "No") + return + + // The player in VR is fine with leaving, kick them out and reset avatar + avatar.exit_vr() + avatar = null + go_out() + add_fingerprint(usr) + +/obj/machinery/vr_sleeper/verb/climb_in() + set src in oview(1) + set category = "Object" + set name = "Enter VR Capsule" + + if(usr.incapacitated()) + return + go_in(usr, usr) + add_fingerprint(usr) + +/obj/machinery/vr_sleeper/relaymove(mob/user as mob) + if(user.incapacitated()) + return 0 //maybe they should be able to get out with cuffs, but whatever + go_out() + +/obj/machinery/vr_sleeper/proc/go_in(var/mob/M, var/mob/user) + if(!M) + return + if(stat & (BROKEN|NOPOWER)) + return + if(!ishuman(M)) + user << "\The [src] rejects [M] with a sharp beep." + if(occupant) + user << "\The [src] is already occupied." + return + + if(M == user) + visible_message("\The [user] starts climbing into \the [src].") + else + visible_message("\The [user] starts putting [M] into \the [src].") + + if(do_after(user, 20)) + if(occupant) + to_chat(user, "\The [src] is already occupied.") + return + M.stop_pulling() + if(M.client) + M.client.perspective = EYE_PERSPECTIVE + M.client.eye = src + M.loc = src + update_use_power(2) + occupant = M + + update_icon() + + enter_vr() + return + +/obj/machinery/vr_sleeper/proc/go_out() + if(!occupant) + return + + if(occupant.client) + occupant.client.eye = occupant.client.mob + occupant.client.perspective = MOB_PERSPECTIVE + occupant.loc = src.loc + occupant = null + for(var/atom/movable/A in src) // In case an object was dropped inside or something + if(A == circuit) + continue + if(A in component_parts) + continue + A.loc = src.loc + update_use_power(1) + update_icon() + +/obj/machinery/vr_sleeper/proc/enter_vr() + + // No mob to transfer a mind from + if(!occupant) + return + + // No mind to transfer + if(!occupant.mind) + return + + // Mob doesn't have an active consciousness to send/receive from + if(occupant.stat != CONSCIOUS) + return + + avatar = occupant.vr_link + // If they've already enterred VR, and are reconnecting, prompt if they want a new body + if(avatar && alert(occupant, "You already have a Virtual Reality avatar. Would you like to use it?", "New avatar", "Yes", "No") == "No") + // Delink the mob + occupant.vr_link = null + avatar = null + + if(!avatar) + // Get the desired spawn location to put the body + var/S = null + var/list/vr_landmarks = list() + for(var/obj/effect/landmark/virtual_reality/sloc in landmarks_list) + vr_landmarks += sloc.name + + S = input(occupant, "Please select a location to spawn your avatar at:", "Spawn location") as null|anything in vr_landmarks + if(!S) + return 0 + + for(var/obj/effect/landmark/virtual_reality/i in landmarks_list) + if(i.name == S) + S = i + break + + avatar = new(S, "Virtual Reality Avatar") + // If the user has a non-default (Human) bodyshape, make it match theirs. + if(occupant.species.name != "Promethean" && occupant.species.name != "Human") + avatar.shapeshifter_change_shape(occupant.species.name) + avatar.forceMove(get_turf(S)) // Put the mob on the landmark, instead of inside it + avatar.Sleeping(1) + + occupant.enter_vr(avatar) + + // Prompt for username after they've enterred the body. + var/newname = sanitize(input(avatar, "You are enterring virtual reality. Your username is currently [src.name]. Would you like to change it to something else?", "Name change") as null|text, MAX_NAME_LEN) + if (newname) + avatar.real_name = newname + + else + occupant.enter_vr(avatar) + diff --git a/code/modules/mob/living/carbon/human/species/virtual_reality/avatar.dm b/code/modules/mob/living/carbon/human/species/virtual_reality/avatar.dm new file mode 100644 index 0000000000..8fbf47022d --- /dev/null +++ b/code/modules/mob/living/carbon/human/species/virtual_reality/avatar.dm @@ -0,0 +1,32 @@ +// ### Wooo, inheritance. Basically copying everything I don't need to edit from prometheans, because they mostly work already. +// ### Any and all of this is open to change for balance or whatever. +// ### +// ### +// Species definition follows. +/datum/species/shapeshifter/promethean/avatar + + name = "Virtual Reality Avatar" + name_plural = "Virtual Reality Avatars" + blurb = "A 3-dimensional representation of some sort of animate object used to display the presence and actions of some-one or -thing using a virtual reality program." + show_ssd = "eerily still" + death_message = "flickers briefly, their gear falling in a heap on the floor around their motionless body." + knockout_message = "has been knocked unconscious!" + + spawn_flags = SPECIES_IS_RESTRICTED + + speech_bubble_appearance = "cyber" + + male_cough_sounds = list('sound/effects/mob_effects/m_cougha.ogg','sound/effects/mob_effects/m_coughb.ogg', 'sound/effects/mob_effects/m_coughc.ogg') + female_cough_sounds = list('sound/effects/mob_effects/f_cougha.ogg','sound/effects/mob_effects/f_coughb.ogg') + male_sneeze_sound = 'sound/effects/mob_effects/sneeze.ogg' + female_sneeze_sound = 'sound/effects/mob_effects/f_sneeze.ogg' + + unarmed_types = list(/datum/unarmed_attack/stomp, /datum/unarmed_attack/kick, /datum/unarmed_attack/punch, /datum/unarmed_attack/bite) + has_organ = list(O_BRAIN = /obj/item/organ/internal/brain/slime, O_EYES = /obj/item/organ/internal/eyes) // Slime core. + heal_rate = 0 // Avatars don't naturally heal like prometheans, at least not for now + +/datum/species/shapeshifter/promethean/avatar/handle_death(var/mob/living/carbon/human/H) + return + +/datum/species/shapeshifter/promethean/avatar/handle_environment_special(var/mob/living/carbon/human/H) + return \ No newline at end of file diff --git a/polaris.dme b/polaris.dme index 66e1fedb0e..5b490ce1f2 100644 --- a/polaris.dme +++ b/polaris.dme @@ -613,6 +613,7 @@ #include "code\game\machinery\teleporter.dm" #include "code\game\machinery\turret_control.dm" #include "code\game\machinery\vending.dm" +#include "code\game\machinery\vr_console.dm" #include "code\game\machinery\wall_frames.dm" #include "code\game\machinery\washing_machine.dm" #include "code\game\machinery\wishgranter.dm" @@ -1721,6 +1722,7 @@ #include "code\modules\mob\living\carbon\human\species\station\prometheans.dm" #include "code\modules\mob\living\carbon\human\species\station\seromi.dm" #include "code\modules\mob\living\carbon\human\species\station\station.dm" +#include "code\modules\mob\living\carbon\human\species\virtual_reality\avatar.dm" #include "code\modules\mob\living\carbon\human\species\xenomorphs\alien_powers.dm" #include "code\modules\mob\living\carbon\human\species\xenomorphs\alien_species.dm" #include "code\modules\mob\living\carbon\human\species\xenomorphs\xenomorphs.dm" From 64942bd04c93de46d9adeb04a93c1560276deae8 Mon Sep 17 00:00:00 2001 From: Atermonera Date: Mon, 30 Oct 2017 22:28:23 -0700 Subject: [PATCH 2/3] VR Core --- code/game/machinery/vr_console.dm | 222 ++++++++++++++++++ code/game/objects/effects/landmarks.dm | 12 + .../items/weapons/circuitboards/frame.dm | 9 + code/modules/mob/dead/observer/observer.dm | 12 +- code/modules/mob/living/carbon/human/death.dm | 26 +- code/modules/mob/living/carbon/human/human.dm | 53 ++++- .../mob/living/carbon/human/human_defines.dm | 5 + .../human/species/species_shapeshift.dm | 9 +- .../human/species/virtual_reality/avatar.dm | 32 +++ polaris.dme | 2 + 10 files changed, 376 insertions(+), 6 deletions(-) create mode 100644 code/game/machinery/vr_console.dm create mode 100644 code/modules/mob/living/carbon/human/species/virtual_reality/avatar.dm diff --git a/code/game/machinery/vr_console.dm b/code/game/machinery/vr_console.dm new file mode 100644 index 0000000000..7dabf72ecf --- /dev/null +++ b/code/game/machinery/vr_console.dm @@ -0,0 +1,222 @@ +/obj/machinery/vr_sleeper + name = "VR Sleeper" + desc = "A fancy bed with built-in sensory I/O ports and connectors to interface users' minds with their bodies in virtual reality." + icon = 'icons/obj/Cryogenic2.dmi' + icon_state = "syndipod_0" + density = 1 + anchored = 1 + circuit = /obj/item/weapon/circuitboard/vr_sleeper + var/mob/living/carbon/human/occupant = null + var/mob/living/carbon/human/avatar = null + var/datum/mind/vr_mind = null + + use_power = 1 + idle_power_usage = 15 + active_power_usage = 200 + light_color = "#FF0000" + +/obj/machinery/vr_sleeper/New() + ..() + component_parts = list() + component_parts += new /obj/item/weapon/stock_parts/scanning_module(src) + component_parts += new /obj/item/stack/material/glass/reinforced(src, 2) + + RefreshParts() + +/obj/machinery/vr_sleeper/initialize() + update_icon() + +/obj/machinery/vr_sleeper/process() + if(stat & (NOPOWER|BROKEN)) + return + +/obj/machinery/vr_sleeper/update_icon() + icon_state = "syndipod_[occupant ? "1" : "0"]" + +/obj/machinery/vr_sleeper/Topic(href, href_list) + if(..()) + return 1 + + if(usr == occupant) + to_chat(usr, "You can't reach the controls from the inside.") + return + + add_fingerprint(usr) + + if(href_list["eject"]) + go_out() + + return 1 + +/obj/machinery/vr_sleeper/attackby(var/obj/item/I, var/mob/user) + add_fingerprint(user) + if(default_deconstruction_screwdriver(user, I)) + return + else if(default_deconstruction_crowbar(user, I)) + return + + +/obj/machinery/vr_sleeper/MouseDrop_T(var/mob/target, var/mob/user) + if(user.stat || user.lying || !Adjacent(user) || !target.Adjacent(user)|| !isliving(target)) + return + go_in(target, user) + + + +/obj/machinery/sleeper/relaymove(var/mob/user) + ..() + if(usr.incapacitated()) + return + go_out() + + + +/obj/machinery/vr_sleeper/emp_act(var/severity) + if(stat & (BROKEN|NOPOWER)) + ..(severity) + return + + if(occupant) + // This will eject the user from VR + // ### Fry the brain? + go_out() + + ..(severity) + +/obj/machinery/vr_sleeper/verb/eject() + set src in oview(1) + set category = "Object" + set name = "Eject VR Capsule" + + if(usr.incapacitated()) + return + + if(usr != occupant && avatar && alert(avatar, "Someone wants to remove you from virtual reality. Do you want to leave?", "Leave VR?", "Yes", "No") == "No") + return + + // The player in VR is fine with leaving, kick them out and reset avatar + avatar.exit_vr() + avatar = null + go_out() + add_fingerprint(usr) + +/obj/machinery/vr_sleeper/verb/climb_in() + set src in oview(1) + set category = "Object" + set name = "Enter VR Capsule" + + if(usr.incapacitated()) + return + go_in(usr, usr) + add_fingerprint(usr) + +/obj/machinery/vr_sleeper/relaymove(mob/user as mob) + if(user.incapacitated()) + return 0 //maybe they should be able to get out with cuffs, but whatever + go_out() + +/obj/machinery/vr_sleeper/proc/go_in(var/mob/M, var/mob/user) + if(!M) + return + if(stat & (BROKEN|NOPOWER)) + return + if(!ishuman(M)) + user << "\The [src] rejects [M] with a sharp beep." + if(occupant) + user << "\The [src] is already occupied." + return + + if(M == user) + visible_message("\The [user] starts climbing into \the [src].") + else + visible_message("\The [user] starts putting [M] into \the [src].") + + if(do_after(user, 20)) + if(occupant) + to_chat(user, "\The [src] is already occupied.") + return + M.stop_pulling() + if(M.client) + M.client.perspective = EYE_PERSPECTIVE + M.client.eye = src + M.loc = src + update_use_power(2) + occupant = M + + update_icon() + + enter_vr() + return + +/obj/machinery/vr_sleeper/proc/go_out() + if(!occupant) + return + + if(occupant.client) + occupant.client.eye = occupant.client.mob + occupant.client.perspective = MOB_PERSPECTIVE + occupant.loc = src.loc + occupant = null + for(var/atom/movable/A in src) // In case an object was dropped inside or something + if(A == circuit) + continue + if(A in component_parts) + continue + A.loc = src.loc + update_use_power(1) + update_icon() + +/obj/machinery/vr_sleeper/proc/enter_vr() + + // No mob to transfer a mind from + if(!occupant) + return + + // No mind to transfer + if(!occupant.mind) + return + + // Mob doesn't have an active consciousness to send/receive from + if(occupant.stat != CONSCIOUS) + return + + avatar = occupant.vr_link + // If they've already enterred VR, and are reconnecting, prompt if they want a new body + if(avatar && alert(occupant, "You already have a Virtual Reality avatar. Would you like to use it?", "New avatar", "Yes", "No") == "No") + // Delink the mob + occupant.vr_link = null + avatar = null + + if(!avatar) + // Get the desired spawn location to put the body + var/S = null + var/list/vr_landmarks = list() + for(var/obj/effect/landmark/virtual_reality/sloc in landmarks_list) + vr_landmarks += sloc.name + + S = input(occupant, "Please select a location to spawn your avatar at:", "Spawn location") as null|anything in vr_landmarks + if(!S) + return 0 + + for(var/obj/effect/landmark/virtual_reality/i in landmarks_list) + if(i.name == S) + S = i + break + + avatar = new(S, "Virtual Reality Avatar") + // If the user has a non-default (Human) bodyshape, make it match theirs. + if(occupant.species.name != "Promethean" && occupant.species.name != "Human") + avatar.shapeshifter_change_shape(occupant.species.name) + avatar.forceMove(get_turf(S)) // Put the mob on the landmark, instead of inside it + avatar.Sleeping(1) + + occupant.enter_vr(avatar) + + // Prompt for username after they've enterred the body. + var/newname = sanitize(input(avatar, "You are enterring virtual reality. Your username is currently [src.name]. Would you like to change it to something else?", "Name change") as null|text, MAX_NAME_LEN) + if (newname) + avatar.real_name = newname + + else + occupant.enter_vr(avatar) + diff --git a/code/game/objects/effects/landmarks.dm b/code/game/objects/effects/landmarks.dm index 547406aa1d..b8cb8006d2 100644 --- a/code/game/objects/effects/landmarks.dm +++ b/code/game/objects/effects/landmarks.dm @@ -107,6 +107,18 @@ return 1 +/obj/effect/landmark/virtual_reality + name = "virtual_reality" + icon = 'icons/mob/screen1.dmi' + icon_state = "x" + anchored = 1.0 + +/obj/effect/landmark/virtual_reality/New() + ..() + tag = "virtual_reality*[name]" + invisibility = 101 + return 1 + //Costume spawner landmarks /obj/effect/landmark/costume/New() //costume spawner, selects a random subclass and disappears diff --git a/code/game/objects/items/weapons/circuitboards/frame.dm b/code/game/objects/items/weapons/circuitboards/frame.dm index fa48be65bb..3cbc54e855 100644 --- a/code/game/objects/items/weapons/circuitboards/frame.dm +++ b/code/game/objects/items/weapons/circuitboards/frame.dm @@ -202,6 +202,15 @@ /obj/item/weapon/reagent_containers/syringe = 3, /obj/item/stack/material/glass/reinforced = 2) +/obj/item/weapon/circuitboard/vr_sleeper + name = T_BOARD("VR sleeper") + build_path = /obj/machinery/vr_sleeper + board_type = new /datum/frame/frame_types/medical_pod + origin_tech = list(TECH_MAGNET = 2, TECH_BIO = 2) + req_components = list( + /obj/item/weapon/stock_parts/scanning_module = 1, + /obj/item/stack/material/glass/reinforced = 2) + /obj/item/weapon/circuitboard/dna_analyzer name = T_BOARD("dna analyzer") build_path = /obj/machinery/dnaforensics diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm index 64893a973b..7f8d69c614 100644 --- a/code/modules/mob/dead/observer/observer.dm +++ b/code/modules/mob/dead/observer/observer.dm @@ -201,6 +201,11 @@ Works together with spawning an observer, noted above. /mob/proc/ghostize(var/can_reenter_corpse = 1) if(key) + if(ishuman(src)) + var/mob/living/carbon/human/H = src + if(H.vr_holder && !can_reenter_corpse) + H.exit_vr() + return 0 var/mob/observer/dead/ghost = new(src) //Transfer safety to observer spawning proc. ghost.can_reenter_corpse = can_reenter_corpse ghost.timeofdeath = src.timeofdeath //BS12 EDIT @@ -243,9 +248,10 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp var/turf/location = get_turf(src) message_admins("[key_name_admin(usr)] has ghosted. (JMP)") log_game("[key_name_admin(usr)] has ghosted.") - var/mob/observer/dead/ghost = ghostize(0) //0 parameter is so we can never re-enter our body, "Charlie, you can never come baaaack~" :3 - ghost.timeofdeath = world.time // Because the living mob won't have a time of death and we want the respawn timer to work properly. - announce_ghost_joinleave(ghost) + var/mob/observer/dead/ghost = ghostize(0) // 0 parameter is so we can never re-enter our body, "Charlie, you can never come baaaack~" :3 + if(ghost) + ghost.timeofdeath = world.time // Because the living mob won't have a time of death and we want the respawn timer to work properly. + announce_ghost_joinleave(ghost) /mob/observer/dead/can_use_hands() return 0 /mob/observer/dead/is_active() return 0 diff --git a/code/modules/mob/living/carbon/human/death.dm b/code/modules/mob/living/carbon/human/death.dm index ffac791a1f..8df0bd1605 100644 --- a/code/modules/mob/living/carbon/human/death.dm +++ b/code/modules/mob/living/carbon/human/death.dm @@ -1,5 +1,15 @@ /mob/living/carbon/human/gib() + if(vr_holder) + exit_vr() + // Delete the link, because this mob won't be around much longer + vr_holder.vr_link = null + + if(vr_link) + vr_link.exit_vr() + vr_link.vr_holder = null + vr_link = null + for(var/obj/item/organ/I in internal_organs) I.removed() if(istype(loc,/turf)) @@ -80,6 +90,20 @@ if(wearing_rig) wearing_rig.notify_ai("Warning: user death event. Mobility control passed to integrated intelligence system.") + // If the body is in VR, move the mind back to the real world + if(vr_holder) + src.exit_vr() + src.vr_holder.vr_link = null + for(var/obj/item/W in src) + src.drop_from_inventory(W) + + // If our mind is in VR, bring it back to the real world so it can die with its body + if(vr_link) + vr_link.exit_vr() + vr_link.vr_holder = null + vr_link = null + to_chat(src, "Everything abruptly stops.") + return ..(gibbed,species.get_death_message(src)) /mob/living/carbon/human/proc/ChangeToHusk() @@ -113,4 +137,4 @@ mutations.Add(SKELETON) status_flags |= DISFIGURED update_body(1) - return + return \ No newline at end of file diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index 3cd3d0c96e..2862af69ab 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -1525,4 +1525,55 @@ var/turf/T = get_turf(src) var/obj/item/clothing/accessory/permit/drone/permit = new(T) permit.set_name(real_name) - equip_to_appropriate_slot(permit) // If for some reason it can't find room, it'll still be on the floor. \ No newline at end of file + equip_to_appropriate_slot(permit) // If for some reason it can't find room, it'll still be on the floor. + +// enter_vr is called on the original mob, and puts the mind into the supplied vr mob +/mob/living/carbon/human/proc/enter_vr(var/mob/living/carbon/human/avatar) // Avatar is currently a human, because we have preexisting setup code for appearance manipulation, etc. + if(!istype(avatar)) + return + + // Link the two mobs for client transfer + avatar.vr_holder = src + src.teleop = avatar + src.vr_link = avatar // Can't reuse vr_holder so that death can automatically eject users from VR + + // Move the mind + avatar.Sleeping(1) + src.mind.transfer_to(avatar) + to_chat(avatar, "You have enterred Virtual Reality!\nAll normal gameplay rules still apply.\nWounds you suffer here won't persist when you leave VR, but some of the pain will.\nYou can leave VR at any time by using the \"Exit Virtual Reality\" verb in the Abilities tab, or by ghosting.\nYou can modify your appearance by using various \"Change \[X\]\" verbs in the Abilities tab.") + to_chat(avatar, " You black out for a moment, and wake to find yourself in a new body in virtual reality.") // So this is what VR feels like? + +// exit_vr is called on the vr mob, and puts the mind back into the original mob +/mob/living/carbon/human/verb/exit_vr() + set name = "Exit Virtual Reality" + set category = "Abilities" + + if(!vr_holder) + return + if(!mind) + return + + var/total_damage + // Tally human damage + if(ishuman(src)) + var/mob/living/carbon/human/H = src + total_damage = H.getBruteLoss() + H.getFireLoss() + H.getOxyLoss() + H.getToxLoss() + + // Move the mind back to the original mob +// vr_holder.Sleeping(1) + src.mind.transfer_to(vr_holder) + to_chat(vr_holder, "You black out for a moment, and wake to find yourself back in your own body.") + // Two-thirds damage is transferred as agony for /humans + // Getting hurt in VR doesn't damage the physical body, but you still got hurt. + if(ishuman(vr_holder) && total_damage) + var/mob/living/carbon/human/V = vr_holder + V.stun_effect_act(0, total_damage*2/3, null) // 200 damage leaves the user in paincrit for several seconds, agony reaches 0 after around 2m. + to_chat(vr_holder, "Pain from your time in VR lingers.") // 250 damage leaves the user unconscious for several seconds in addition to paincrit + + // Maintain a link with the mob, but don't use teleop + vr_holder.vr_link = src + vr_holder.teleop = null + + if(istype(vr_holder.loc, /obj/machinery/vr_sleeper)) + var/obj/machinery/vr_sleeper/V = vr_holder.loc + V.go_out() \ No newline at end of file diff --git a/code/modules/mob/living/carbon/human/human_defines.dm b/code/modules/mob/living/carbon/human/human_defines.dm index a4c5efb1fe..0001797dcc 100644 --- a/code/modules/mob/living/carbon/human/human_defines.dm +++ b/code/modules/mob/living/carbon/human/human_defines.dm @@ -97,3 +97,8 @@ var/step_count = 0 // Track how many footsteps have been taken to know when to play footstep sounds can_be_antagged = TRUE + +// Used by mobs in virtual reality to point back to the "real" mob the client belongs to. + var/mob/living/carbon/human/vr_holder = null + // Used by "real" mobs after they leave a VR session + var/mob/living/carbon/human/vr_link = null \ No newline at end of file diff --git a/code/modules/mob/living/carbon/human/species/species_shapeshift.dm b/code/modules/mob/living/carbon/human/species/species_shapeshift.dm index 1fb7b6c300..11a50c1316 100644 --- a/code/modules/mob/living/carbon/human/species/species_shapeshift.dm +++ b/code/modules/mob/living/carbon/human/species/species_shapeshift.dm @@ -148,9 +148,16 @@ var/list/wrapped_species_by_ref = list() last_special = world.time + 50 - var/new_species = input("Please select a species to emulate.", "Shapeshifter Body") as null|anything in species.get_valid_shapeshifter_forms(src) + var/new_species = null + new_species = input("Please select a species to emulate.", "Shapeshifter Body") as null|anything in species.get_valid_shapeshifter_forms(src) + if(!new_species || !all_species[new_species] || wrapped_species_by_ref["\ref[src]"] == new_species) return + shapeshifter_change_shape(new_species) + +/mob/living/carbon/human/proc/shapeshifter_change_shape(var/new_species = null) + if(!new_species) + return wrapped_species_by_ref["\ref[src]"] = new_species visible_message("\The [src] shifts and contorts, taking the form of \a [new_species]!") diff --git a/code/modules/mob/living/carbon/human/species/virtual_reality/avatar.dm b/code/modules/mob/living/carbon/human/species/virtual_reality/avatar.dm new file mode 100644 index 0000000000..8fbf47022d --- /dev/null +++ b/code/modules/mob/living/carbon/human/species/virtual_reality/avatar.dm @@ -0,0 +1,32 @@ +// ### Wooo, inheritance. Basically copying everything I don't need to edit from prometheans, because they mostly work already. +// ### Any and all of this is open to change for balance or whatever. +// ### +// ### +// Species definition follows. +/datum/species/shapeshifter/promethean/avatar + + name = "Virtual Reality Avatar" + name_plural = "Virtual Reality Avatars" + blurb = "A 3-dimensional representation of some sort of animate object used to display the presence and actions of some-one or -thing using a virtual reality program." + show_ssd = "eerily still" + death_message = "flickers briefly, their gear falling in a heap on the floor around their motionless body." + knockout_message = "has been knocked unconscious!" + + spawn_flags = SPECIES_IS_RESTRICTED + + speech_bubble_appearance = "cyber" + + male_cough_sounds = list('sound/effects/mob_effects/m_cougha.ogg','sound/effects/mob_effects/m_coughb.ogg', 'sound/effects/mob_effects/m_coughc.ogg') + female_cough_sounds = list('sound/effects/mob_effects/f_cougha.ogg','sound/effects/mob_effects/f_coughb.ogg') + male_sneeze_sound = 'sound/effects/mob_effects/sneeze.ogg' + female_sneeze_sound = 'sound/effects/mob_effects/f_sneeze.ogg' + + unarmed_types = list(/datum/unarmed_attack/stomp, /datum/unarmed_attack/kick, /datum/unarmed_attack/punch, /datum/unarmed_attack/bite) + has_organ = list(O_BRAIN = /obj/item/organ/internal/brain/slime, O_EYES = /obj/item/organ/internal/eyes) // Slime core. + heal_rate = 0 // Avatars don't naturally heal like prometheans, at least not for now + +/datum/species/shapeshifter/promethean/avatar/handle_death(var/mob/living/carbon/human/H) + return + +/datum/species/shapeshifter/promethean/avatar/handle_environment_special(var/mob/living/carbon/human/H) + return \ No newline at end of file diff --git a/polaris.dme b/polaris.dme index 66e1fedb0e..5b490ce1f2 100644 --- a/polaris.dme +++ b/polaris.dme @@ -613,6 +613,7 @@ #include "code\game\machinery\teleporter.dm" #include "code\game\machinery\turret_control.dm" #include "code\game\machinery\vending.dm" +#include "code\game\machinery\vr_console.dm" #include "code\game\machinery\wall_frames.dm" #include "code\game\machinery\washing_machine.dm" #include "code\game\machinery\wishgranter.dm" @@ -1721,6 +1722,7 @@ #include "code\modules\mob\living\carbon\human\species\station\prometheans.dm" #include "code\modules\mob\living\carbon\human\species\station\seromi.dm" #include "code\modules\mob\living\carbon\human\species\station\station.dm" +#include "code\modules\mob\living\carbon\human\species\virtual_reality\avatar.dm" #include "code\modules\mob\living\carbon\human\species\xenomorphs\alien_powers.dm" #include "code\modules\mob\living\carbon\human\species\xenomorphs\alien_species.dm" #include "code\modules\mob\living\carbon\human\species\xenomorphs\xenomorphs.dm" From 4e191a2bd71c661c35f98d66e3e34f80019103a6 Mon Sep 17 00:00:00 2001 From: Atermonera Date: Tue, 31 Oct 2017 09:41:02 -0700 Subject: [PATCH 3/3] improper sleeper --- code/game/machinery/vr_console.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/game/machinery/vr_console.dm b/code/game/machinery/vr_console.dm index 7dabf72ecf..e21aa27c96 100644 --- a/code/game/machinery/vr_console.dm +++ b/code/game/machinery/vr_console.dm @@ -1,5 +1,5 @@ /obj/machinery/vr_sleeper - name = "VR Sleeper" + name = "VR sleeper" desc = "A fancy bed with built-in sensory I/O ports and connectors to interface users' minds with their bodies in virtual reality." icon = 'icons/obj/Cryogenic2.dmi' icon_state = "syndipod_0"