Merge pull request #4188 from Atermonera/VR_2

Virtual Reality
This commit is contained in:
Anewbe
2017-11-03 15:10:53 -05:00
committed by GitHub
10 changed files with 376 additions and 6 deletions
+222
View File
@@ -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, "<span class='warning'>You can't reach the controls from the inside.</span>")
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 << "<span class='warning'>\The [src] rejects [M] with a sharp beep.</span>"
if(occupant)
user << "<span class='warning'>\The [src] is already occupied.</span>"
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, "<span class='warning'>\The [src] is already occupied.</span>")
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)
+12
View File
@@ -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
@@ -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
+9 -3
View File
@@ -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. (<A HREF='?_src_=holder;adminplayerobservecoodjump=1;X=[location.x];Y=[location.y];Z=[location.z]'>JMP</a>)")
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
+25 -1
View File
@@ -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("<span class='danger'>Warning: user death event. Mobility control passed to integrated intelligence system.</span>")
// 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, "<span class='danger'>Everything abruptly stops.</span>")
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
+52 -1
View File
@@ -1523,4 +1523,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.
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, "<b>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.</b>")
to_chat(avatar, "<span class='notice'> You black out for a moment, and wake to find yourself in a new body in virtual reality.</span>") // 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, "<span class='notice'>You black out for a moment, and wake to find yourself back in your own body.</span>")
// 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, "<span class='warning'>Pain from your time in VR lingers.</span>") // 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()
@@ -103,3 +103,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
@@ -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("<span class='notice'>\The [src] shifts and contorts, taking the form of \a [new_species]!</span>")
@@ -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
+2
View File
@@ -616,6 +616,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"
@@ -1731,6 +1732,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"