From 9e51ba2fa37e4d0ca9a15f362e12eb7c7c0210d2 Mon Sep 17 00:00:00 2001 From: Arokha Sieyes Date: Fri, 9 Jun 2017 19:02:16 -0400 Subject: [PATCH] Soulcatcher improvements - Multiple minds at once, gotta catch 'em all, they can all talk to each other, etc - A setting to enable mind backups for all minds in there like an implant - Notifications to the already-in-there minds when one comes or goes - If YOU get put in YOUR soulcatcher, it does ONE backup right then and stops, so medical will be notified --- .../mob/living/carbon/human/emote_vr.dm | 12 +- .../nifsoft/software/13_soulcatcher.dm | 189 ++++++++++++------ code/modules/resleeving/infocore.dm | 24 +-- 3 files changed, 143 insertions(+), 82 deletions(-) diff --git a/code/modules/mob/living/carbon/human/emote_vr.dm b/code/modules/mob/living/carbon/human/emote_vr.dm index fb62e0ca22..0deb824388 100644 --- a/code/modules/mob/living/carbon/human/emote_vr.dm +++ b/code/modules/mob/living/carbon/human/emote_vr.dm @@ -21,14 +21,12 @@ if(!SC) to_chat(src,"You need the Soulcatcher software to use *nme.") return 1 - if(!SC.brainmob.mind) + if(!SC.brainmobs.len) to_chat(src,"You need a loaded mind to use *nme.") return 1 var/nifmessage = sanitize(input("Type a message to say.","Speak into NIF") as text|null) if(nifmessage) - nifmessage = "\[\icon[nif.big_icon]NIF\] [src] speaks, \"[nifmessage]\"" - to_chat(SC.brainmob,nifmessage) - to_chat(src,nifmessage) + SC.say_into(nifmessage,src.real_name) return 1 if ("nme") @@ -39,14 +37,12 @@ if(!SC) to_chat(src,"You need the Soulcatcher software to use *nme.") return 1 - if(!SC.brainmob.mind) + if(!SC.brainmobs.len) to_chat(src,"You need a loaded mind to use *nme.") return 1 var/nifmessage = sanitize(input("Type an action to perform.","Emote into NIF") as text|null) if(nifmessage) - nifmessage = "\[\icon[nif.big_icon]NIF\] [src] [nifmessage]" - to_chat(SC.brainmob,nifmessage) - to_chat(src,nifmessage) + SC.emote_into(nifmessage,src.real_name) return 1 if ("flip") diff --git a/code/modules/nifsoft/software/13_soulcatcher.dm b/code/modules/nifsoft/software/13_soulcatcher.dm index 583e7c5ec7..52fbe74f6a 100644 --- a/code/modules/nifsoft/software/13_soulcatcher.dm +++ b/code/modules/nifsoft/software/13_soulcatcher.dm @@ -4,36 +4,31 @@ //These are purely local setings flags, without global representation. #define NIF_SC_ALLOW_EARS 0x4 #define NIF_SC_ALLOW_EYES 0x8 +#define NIF_SC_BACKUPS 0x10 /////////// // Soulcatcher - Like a posibrain, sorta! /datum/nifsoft/soulcatcher name = "Soulcatcher" - desc = "A mind storage and processing system capable of capturing and supporting a single human-intelligence mind on body death, into a small VR space." + desc = "A mind storage and processing system capable of capturing and supporting human-level minds in a small VR space." list_pos = NIF_SOULCATCHER cost = 1265 wear = 1 p_drain = 0.01 - var/setting_flags = (NIF_SC_CATCHING_ME|NIF_SC_CATCHING_OTHERS|NIF_SC_ALLOW_EARS|NIF_SC_ALLOW_EYES) - var/mob/living/carbon/brain/caught_soul/brainmob + var/setting_flags = (NIF_SC_CATCHING_ME|NIF_SC_CATCHING_OTHERS|NIF_SC_ALLOW_EARS|NIF_SC_ALLOW_EYES|NIF_SC_BACKUPS) + var/list/brainmobs = list() var/inside_flavor = "A small completely white room with a couch, and a window to what seems to be the outside world. A small sign in the corner says 'Configure Me'." New() ..() - brainmob = new(nif) - brainmob.nif = nif - brainmob.container = src - brainmob.stat = 0 - brainmob.silent = FALSE - dead_mob_list -= src.brainmob - brainmob.add_language(LANGUAGE_GALCOM) load_settings() Destroy() - if(brainmob) - brainmob.container = null - qdel(brainmob) + for(var/brain in brainmobs) + var/mob/living/carbon/brain/caught_soul/CS = brain + qdel(CS) + brainmobs.Cut() ..() install() @@ -57,7 +52,7 @@ return TRUE stat_text() - return "Change Settings[brainmob.mind ? " (Mind Stored)" : ""]" + return "Change Settings ([brainmobs.len] minds)" proc/save_settings() nif.save_data["[list_pos]"] = inside_flavor @@ -69,17 +64,32 @@ inside_flavor = load return TRUE + proc/say_into(var/message, var/sender) + to_chat(nif.human,"\[\icon[nif.big_icon]NIF\] [sender] speaks, \"[message]\"") + for(var/brainmob in brainmobs) + var/mob/living/carbon/brain/caught_soul/CS = brainmob + to_chat(CS,"\[\icon[nif.big_icon]NIF\] [sender] speaks, \"[message]\"") + + proc/emote_into(var/message, var/sender) + to_chat(nif.human,"\[\icon[nif.big_icon]NIF\] [sender] [message]") + for(var/brainmob in brainmobs) + var/mob/living/carbon/brain/caught_soul/CS = brainmob + to_chat(CS,"\[\icon[nif.big_icon]NIF\] [sender] [message]") + proc/show_settings(var/mob/living/carbon/human/H) + set waitfor = FALSE var/settings_list = list( "Catching You \[[setting_flags & NIF_SC_CATCHING_ME ? "Enabled" : "Disabled"]\]" = NIF_SC_CATCHING_ME, - "Catching Others \[[setting_flags & NIF_SC_CATCHING_OTHERS ? "Enabled" : "Disabled"]\]" = NIF_SC_CATCHING_OTHERS, + "Catching Prey \[[setting_flags & NIF_SC_CATCHING_OTHERS ? "Enabled" : "Disabled"]\]" = NIF_SC_CATCHING_OTHERS, "Ext. Hearing \[[setting_flags & NIF_SC_ALLOW_EARS ? "Enabled" : "Disabled"]\]" = NIF_SC_ALLOW_EARS, "Ext. Vision \[[setting_flags & NIF_SC_ALLOW_EYES ? "Enabled" : "Disabled"]\]" = NIF_SC_ALLOW_EYES, + "Mind Backups \[[setting_flags & NIF_SC_BACKUPS ? "Enabled" : "Disabled"]\]" = NIF_SC_BACKUPS, "Design Inside", "Erase Contents") var/choice = input(nif.human,"Select a setting to modify:","Soulcatcher NIFSoft") as null|anything in settings_list if(choice in settings_list) switch(choice) + if("Design Inside") var/new_flavor = input(nif.human, "Type what the prey sees after being 'caught'. This will be \ printed after an intro ending with: \"Around you, you see...\" to the prey. If you already \ @@ -88,22 +98,20 @@ new_flavor = sanitize(new_flavor) inside_flavor = new_flavor nif.notify("Updating VR environment...") - to_chat(brainmob,"Your surroundings change to...\n[inside_flavor]") + for(var/brain in brainmobs) + var/mob/living/carbon/brain/caught_soul/CS = brain + to_chat(CS,"Your surroundings change to...\n[inside_flavor]") save_settings() return TRUE if("Erase Contents") - if(brainmob && brainmob.mind) - var/warning = alert(nif.human,"Are you SURE you want to erase [brainmob.mind.name]?","Erase Mind","CANCEL","DELETE","CANCEL") - if(warning == "DELETE") - brainmob.ghostize() - qdel(brainmob) - brainmob = new(nif) - nif.notify("Mind deleted!",TRUE) - return TRUE - else - nif.notify("No mind to delete!",TRUE) - return TRUE + var/mob/living/carbon/brain/caught_soul/brainpick = input(nif.human,"Select a mind to delete:","Erase Mind") as null|anything in brainmobs + if(!brainpick) return + + var/warning = alert(nif.human,"Are you SURE you want to erase [brainpick.name]?","Erase Mind","CANCEL","DELETE","CANCEL") + if(warning == "DELETE") + qdel(brainpick) + return TRUE //Must just be a flag without special handling then. else @@ -115,6 +123,12 @@ //Special treatment switch(flag) + if(NIF_SC_BACKUPS) + if(setting_flags & NIF_SC_BACKUPS) + emote_into(" - Mind backup system enabled.","SOULCATCHER") + else + emote_into(" - Mind backup system disabled.","SOULCATCHER") + if(NIF_SC_CATCHING_ME) if(setting_flags & NIF_SC_CATCHING_ME) nif.set_flag(NIF_O_SCMYSELF,NIF_FLAGS_OTHER) @@ -127,42 +141,74 @@ nif.clear_flag(NIF_O_SCOTHERS,NIF_FLAGS_OTHER) if(NIF_SC_ALLOW_EARS) if(setting_flags & NIF_SC_ALLOW_EARS) - brainmob.ext_deaf = FALSE - to_chat(brainmob,"\[\icon[nif.big_icon]NIF\] Your access to your host's auditory sense has been unrestricted.") + for(var/brain in brainmobs) + var/mob/living/carbon/brain/caught_soul/brainmob = brain + brainmob.ext_deaf = FALSE + emote_into(" - External audio input enabled.","SOULCATCHER") else - brainmob.ext_deaf = TRUE - to_chat(brainmob,"\[\icon[nif.big_icon]NIF\] Your access to your host's auditory sense has been restricted.") + for(var/brain in brainmobs) + var/mob/living/carbon/brain/caught_soul/brainmob = brain + brainmob.ext_deaf = TRUE + emote_into(" - External audio input disabled.","SOULCATCHER") if(NIF_SC_ALLOW_EYES) if(setting_flags & NIF_SC_ALLOW_EYES) - brainmob.ext_blind = FALSE - to_chat(brainmob,"\[\icon[nif.big_icon]NIF\] Your access to your host's visual sense has been unrestricted.") + for(var/brain in brainmobs) + var/mob/living/carbon/brain/caught_soul/brainmob = brain + brainmob.ext_blind = FALSE + emote_into(" - External video input enabled.","SOULCATCHER") else - brainmob.ext_blind = TRUE - to_chat(brainmob,"\[\icon[nif.big_icon]NIF\] Your access to your host's visual sense has been restricted.") + for(var/brain in brainmobs) + var/mob/living/carbon/brain/caught_soul/brainmob = brain + brainmob.ext_blind = TRUE + emote_into(" - External video input disabled.","SOULCATCHER") return TRUE proc/catch_mob(var/mob/living/carbon/human/H) - if(istype(H)) - if(H == nif.human) //Reset our permissions to be permissive to the owner. - brainmob.ext_deaf = FALSE - brainmob.ext_blind = FALSE - brainmob.parent_mob = TRUE + //Create a new brain mob + var/mob/living/carbon/brain/caught_soul/brainmob = new(nif) + brainmob = new(nif) + brainmob.nif = nif + brainmob.soulcatcher = src + brainmob.container = src + brainmob.stat = 0 + brainmob.silent = FALSE + dead_mob_list -= brainmob + brainmob.add_language(LANGUAGE_GALCOM) + brainmobs |= brainmob - brainmob.dna = H.dna - brainmob.timeofhostdeath = H.timeofdeath - brainmob.stat = 0 - if(H.mind) - H.mind.transfer_to(brainmob) - brainmob.name = brainmob.mind.name - var/message = "Your vision fades in a haze of static, before returning.\nAround you, you see...\n[inside_flavor]" - to_chat(brainmob,message) - nif.notify("Mind loaded into VR space: [brainmob.name]") + //If we caught our owner, special settings. + if(H == nif.human) + brainmob.ext_deaf = FALSE + brainmob.ext_blind = FALSE + brainmob.parent_mob = TRUE + transcore.m_backup(H.mind,H) //ONE backup. Won't be called in life due to avoidance of parent_mob backups. + + //Set some basics on the mob. + brainmob.dna = H.dna + brainmob.timeofhostdeath = H.timeofdeath + brainmob.stat = 0 + + //Put the mind and player into the mob + H.mind.transfer_to(brainmob) + brainmob.name = brainmob.mind.name + transcore.m_backup(brainmob.mind,0) //It does ONE, so medical will hear about it. + + //Give them a flavortext message + var/message = "Your vision fades in a haze of static, before returning.\n\ + Around you, you see...\n\ + [inside_flavor]" + + to_chat(brainmob,message) + + //Reminder on how this works to host + if(brainmobs.len == 1) //Only spam this on the first one to_chat(nif.human,"Your occupant's messages/actions can only be seen by you, and you can \ send messages that only they can hear/see by 'say'ing either '*nsay' or '*nme'.") - return TRUE - return FALSE + //Announce to host and other minds + emote_into(" - New mind loaded: [brainmob.name]","SOULCATCHER") + return TRUE //////////////// //The caught mob @@ -174,16 +220,33 @@ var/ext_blind = FALSE //Forbidden from 'eye' access on host var/parent_mob = FALSE //If we've captured our owner var/obj/item/device/nif/nif + var/datum/nifsoft/soulcatcher/soulcatcher /mob/living/carbon/brain/caught_soul/Destroy() - nif = null + if(soulcatcher) + soulcatcher.emote_into(" - Mind unloaded: [name]","SOULCATCHER") + soulcatcher.brainmobs -= src + soulcatcher = null + if(nif) + nif = null + if(mind && ckey) + src.ghostize() + container = null ..() /mob/living/carbon/brain/caught_soul/Life() - if(!client) return + if(!mind) + qdel(src) + return . = ..() + if(!parent_mob && (life_tick % 150 == 0) && soulcatcher.setting_flags & NIF_SC_BACKUPS) + transcore.m_backup(mind,0) //Passed 0 means "Don't touch the nif fields on the mind record" + + life_tick++ + + if(!client) return if(parent_mob) return //If they're blinded if(ext_blind) @@ -214,8 +277,7 @@ /mob/living/carbon/brain/caught_soul/say(var/message) if(parent_mob) return ..() if(silent) return FALSE - to_chat(nif.human,"\[\icon[nif.big_icon]NIF\] [name] speaks, \"[message]\"") - to_chat(src,"\[\icon[nif.big_icon]NIF\] [name] speaks, \"[message]\"") + soulcatcher.say_into(message,src.name) /mob/living/carbon/brain/caught_soul/emote(var/act,var/m_type=1,var/message = null) if(parent_mob) return ..() @@ -237,23 +299,26 @@ /mob/living/carbon/brain/caught_soul/custom_emote(var/m_type, var/message) if(silent) return FALSE - to_chat(nif.human,"\[\icon[nif.big_icon]NIF\] [name] [message]") - to_chat(src,"\[\icon[nif.big_icon]NIF\] [name] [message]") + soulcatcher.emote_into(message,src.name) + +/mob/living/carbon/brain/caught_soul/resist() + set name = "Resist" + set category = "IC" + + to_chat(src,"There's no way out! You're stuck in VR.") /////////////////// //The catching hook /hook/death/proc/nif_soulcatcher(var/mob/living/carbon/human/H) - if(!istype(H)) return TRUE //Hooks must return TRUE + if(!istype(H) || !H.mind) return TRUE //Hooks must return TRUE if(H.nif && H.nif.flag_check(NIF_O_SCMYSELF,NIF_FLAGS_OTHER)) //They are caught in their own NIF var/datum/nifsoft/soulcatcher/SC = H.nif.imp_check(NIF_SOULCATCHER) - if(!SC.brainmob.mind) //As long as they don't have one - SC.catch_mob(H,TRUE) + SC.catch_mob(H,TRUE) else if(ishuman(H.loc)) //Died in someone var/mob/living/carbon/human/HP = H.loc if(HP.nif && HP.nif.flag_check(NIF_O_SCOTHERS,NIF_FLAGS_OTHER)) var/datum/nifsoft/soulcatcher/SC = HP.nif.imp_check(NIF_SOULCATCHER) - if(!SC.brainmob.mind) //As long as they don't have one - SC.catch_mob(H,FALSE) + SC.catch_mob(H,FALSE) return TRUE \ No newline at end of file diff --git a/code/modules/resleeving/infocore.dm b/code/modules/resleeving/infocore.dm index dc0b06d8bd..6ed0730cff 100644 --- a/code/modules/resleeving/infocore.dm +++ b/code/modules/resleeving/infocore.dm @@ -50,6 +50,8 @@ var/datum/transhuman/infocore/transcore = new/datum/transhuman/infocore if(mind.name in backed_up) MR = backed_up[mind.name] MR.last_update = world.time + + //Pass a 0 to not change NIF status (because the elseif is checking for null) if(nif) MR.nif_path = nif.type MR.nif_durability = nif.durability @@ -59,7 +61,7 @@ var/datum/transhuman/infocore/transcore = new/datum/transhuman/infocore var/datum/nifsoft/nifsoft = N nifsofts += nifsoft.type MR.nif_software = nifsofts - else + else if(isnull(nif)) //Didn't pass anything, so no NIF MR.nif_path = null MR.nif_durability = null MR.nif_software = null @@ -122,21 +124,18 @@ var/datum/transhuman/infocore/transcore = new/datum/transhuman/infocore //Backend var/ckey = "" - var/id_gender + var/id_gender = MALE var/datum/mind/mind_ref - var/cryo_at - var/languages - var/mind_oocnotes + var/cryo_at = 0 + var/languages = list() + var/mind_oocnotes = "" var/nif_path var/nif_durability var/list/nif_software /datum/transhuman/mind_record/New(var/datum/mind/mind,var/mob/living/carbon/human/M,var/obj/item/weapon/implant/backup/imp,var/add_to_db = 1) - ASSERT(mind && M && imp) - - if(!istype(M)) - return //Only works with humanoids. + ASSERT(mind) //The mind! mind_ref = mind @@ -146,9 +145,10 @@ var/datum/transhuman/infocore/transcore = new/datum/transhuman/infocore cryo_at = 0 //Mental stuff the game doesn't keep mentally - id_gender = M.identifying_gender - languages = M.languages.Copy() - mind_oocnotes = M.ooc_notes + if(istype(M)) + id_gender = M.identifying_gender + languages = M.languages.Copy() + mind_oocnotes = M.ooc_notes last_update = world.time