From 0da5609e9abd3ef0c259035e207aecbf7d670d2a Mon Sep 17 00:00:00 2001 From: Toast Date: Fri, 29 Nov 2019 01:14:04 -0500 Subject: [PATCH] This is painful --- code/__DEFINES/logging.dm | 1 + code/__DEFINES/machines.dm | 4 + code/__HELPERS/_logging.dm | 4 + code/_globalvars/logging.dm | 2 + .../configuration/entries/general.dm | 2 + code/datums/dna.dm | 2 +- code/game/machinery/cloning.dm | 82 ++++++++----- code/game/machinery/computer/cloning.dm | 113 +++++++++++++----- code/game/machinery/exp_cloner.dm | 6 +- code/game/world.dm | 1 + config/config.txt | 3 + 11 files changed, 157 insertions(+), 63 deletions(-) diff --git a/code/__DEFINES/logging.dm b/code/__DEFINES/logging.dm index 2ba50123..c99db65c 100644 --- a/code/__DEFINES/logging.dm +++ b/code/__DEFINES/logging.dm @@ -35,6 +35,7 @@ #define LOG_GAME (1 << 12) #define LOG_ADMIN_PRIVATE (1 << 13) #define LOG_ASAY (1 << 14) +#define LOG_CLONING (1 << 15) //Individual logging panel pages #define INDIVIDUAL_ATTACK_LOG (LOG_ATTACK) diff --git a/code/__DEFINES/machines.dm b/code/__DEFINES/machines.dm index 86245940..c2959d40 100644 --- a/code/__DEFINES/machines.dm +++ b/code/__DEFINES/machines.dm @@ -100,6 +100,10 @@ #define NUKE_ON_TIMING 2 #define NUKE_ON_EXPLODING 3 +//cloning defines. These are flags. +#define CLONING_SUCCESS (1<<0) +#define CLONING_DELETE_RECORD (1<<1) + //these flags are used to tell the DNA modifier if a plant gene cannot be extracted or modified. #define PLANT_GENE_REMOVABLE (1<<0) #define PLANT_GENE_EXTRACTABLE (1<<1) diff --git a/code/__HELPERS/_logging.dm b/code/__HELPERS/_logging.dm index ac17df57..864057ed 100644 --- a/code/__HELPERS/_logging.dm +++ b/code/__HELPERS/_logging.dm @@ -58,6 +58,10 @@ if (CONFIG_GET(flag/log_game)) WRITE_LOG(GLOB.world_game_log, "GAME: [text]") +/proc/log_cloning(text, mob/initiator) + if(CONFIG_GET(flag/log_cloning)) + WRITE_LOG(GLOB.world_cloning_log, "CLONING: [text]") + /proc/log_access(text) if (CONFIG_GET(flag/log_access)) WRITE_LOG(GLOB.world_game_log, "ACCESS: [text]") diff --git a/code/_globalvars/logging.dm b/code/_globalvars/logging.dm index deffd25b..bf4ff55f 100644 --- a/code/_globalvars/logging.dm +++ b/code/_globalvars/logging.dm @@ -26,6 +26,8 @@ GLOBAL_VAR(query_debug_log) GLOBAL_PROTECT(query_debug_log) GLOBAL_VAR(world_job_debug_log) GLOBAL_PROTECT(world_job_debug_log) +GLOBAL_VAR(world_cloning_log) +GLOBAL_PROTECT(world_cloning_log) GLOBAL_LIST_EMPTY(bombers) GLOBAL_PROTECT(bombers) diff --git a/code/controllers/configuration/entries/general.dm b/code/controllers/configuration/entries/general.dm index 1f6e4623..a3d17ee0 100644 --- a/code/controllers/configuration/entries/general.dm +++ b/code/controllers/configuration/entries/general.dm @@ -39,6 +39,8 @@ /datum/config_entry/flag/log_game // log game events +/datum/config_entry/flag/log_cloning // log cloning actions. + /datum/config_entry/flag/log_vote // log voting /datum/config_entry/flag/log_whisper // log client whisper diff --git a/code/datums/dna.dm b/code/datums/dna.dm index e6f33680..b2e447ba 100644 --- a/code/datums/dna.dm +++ b/code/datums/dna.dm @@ -371,7 +371,7 @@ updateappearance(icon_update=0) if(LAZYLEN(mutation_index)) - dna.mutation_index = mutation_index + dna.mutation_index = mutation_index.Copy() domutcheck() if(mrace || newfeatures || ui) diff --git a/code/game/machinery/cloning.dm b/code/game/machinery/cloning.dm index 5b7eff2b..761eb39e 100644 --- a/code/game/machinery/cloning.dm +++ b/code/game/machinery/cloning.dm @@ -74,6 +74,23 @@ if(heal_level > 100) heal_level = 100 +/obj/machinery/clonepod/attack_hand(mob/user) + . = ..() + if(.) + return + user.examinate(src) + +/obj/machinery/clonepod/attack_ai(mob/user) + return attack_hand(user) + +/obj/machinery/clonepod/examine(mob/user) + . = ..() + . += "The linking device can be scanned with a multitool." + if(in_range(user, src) || isobserver(user)) + . += "The status display reads: Cloning speed at [speed_coeff*50]%.
Predicted amount of cellular damage: [100-heal_level]%.
" + if(efficiency > 5) + . += "Pod has been upgraded to support autoprocessing and apply beneficial mutations." + //The return of data disks?? Just for transferring between genetics machine/cloning machine. //TO-DO: Make the genetics machine accept them. /obj/item/disk/data @@ -129,41 +146,42 @@ return examine(user) //Start growing a human clone in the pod! -/obj/machinery/clonepod/proc/growclone(ckey, clonename, ui, mutation_index, mindref, datum/species/mrace, list/features, factions, list/quirks) +/obj/machinery/clonepod/proc/growclone(clonename, ui, mutation_index, mindref, last_death, blood_type, datum/species/mrace, list/features, factions, list/quirks, datum/bank_account/insurance, list/traumas, empty) if(panel_open) return FALSE if(mess || attempting) return FALSE - clonemind = locate(mindref) in SSticker.minds - if(!istype(clonemind)) //not a mind - return FALSE - if(!QDELETED(clonemind.current)) - if(clonemind.current.stat != DEAD) //mind is associated with a non-dead body + if(!empty) //Doesn't matter if we're just making a copy + clonemind = locate(mindref) in SSticker.minds + if(!istype(clonemind)) //not a mind return FALSE - if(clonemind.current.suiciding) // Mind is associated with a body that is suiciding. +// if(clonemind.last_death != last_death) //The soul has advanced, the record has not. +// return FALSE + if(!QDELETED(clonemind.current)) + if(clonemind.current.stat != DEAD) //mind is associated with a non-dead body + return NONE + if(clonemind.current.suiciding) // Mind is associated with a body that is suiciding. + return NONE + if(!clonemind.active) + // get_ghost() will fail if they're unable to reenter their body + var/mob/dead/observer/G = clonemind.get_ghost() + if(!G) + return NONE + if(G.suiciding) // The ghost came from a body that is suiciding. + return NONE + if(clonemind.damnation_type) //Can't clone the damned. + INVOKE_ASYNC(src, .proc/horrifyingsound) + mess = TRUE + icon_state = "pod_g" + update_icon() return FALSE - if(clonemind.active) //somebody is using that mind - if( ckey(clonemind.key)!=ckey ) - return FALSE - else - // get_ghost() will fail if they're unable to reenter their body - var/mob/dead/observer/G = clonemind.get_ghost() - if(!G) - return FALSE - if(G.suiciding) // The ghost came from a body that is suiciding. - return FALSE - if(clonemind.damnation_type) //Can't clone the damned. - INVOKE_ASYNC(src, .proc/horrifyingsound) - mess = TRUE - update_icon() - return FALSE attempting = TRUE //One at a time!! countdown.start() var/mob/living/carbon/human/H = new /mob/living/carbon/human(src) - H.hardset_dna(ui, mutation_index, H.real_name, null, mrace, features) + H.hardset_dna(ui, mutation_index, H.real_name, blood_type, mrace, features) if(prob(50 - efficiency*10)) //Chance to give a bad mutation. H.easy_randmut(NEGATIVE+MINOR_NEGATIVE) //100% bad mutation. Can be cured with mutadone. @@ -184,15 +202,16 @@ ADD_TRAIT(H, TRAIT_NOCRITDAMAGE, "cloning") H.Unconscious(80) - clonemind.transfer_to(H) + if(!empty) + clonemind.transfer_to(H) - if(grab_ghost_when == CLONER_FRESH_CLONE) - H.grab_ghost() - to_chat(H, "Consciousness slowly creeps over you as your body regenerates.
So this is what cloning feels like?
") + if(grab_ghost_when == CLONER_FRESH_CLONE) + H.grab_ghost() + to_chat(H, "Consciousness slowly creeps over you as your body regenerates.
So this is what cloning feels like?
") - if(grab_ghost_when == CLONER_MATURE_CLONE) - H.ghostize(TRUE) //Only does anything if they were still in their old body and not already a ghost - to_chat(H.get_ghost(TRUE), "Your body is beginning to regenerate in a cloning pod. You will become conscious when it is complete.") + if(grab_ghost_when == CLONER_MATURE_CLONE) + H.ghostize(TRUE) //Only does anything if they were still in their old body and not already a ghost + to_chat(H.get_ghost(TRUE), "Your body is beginning to regenerate in a cloning pod. You will become conscious when it is complete.") if(H) H.faction |= factions @@ -381,6 +400,7 @@ unattached_flesh.Cut() occupant = null + clonemind = null /obj/machinery/clonepod/proc/malfunction() var/mob/living/mob_occupant = occupant @@ -391,7 +411,7 @@ mess = TRUE maim_clone(mob_occupant) //Remove every bit that's grown back so far to drop later, also destroys bits that haven't grown yet update_icon() - if(mob_occupant.mind != clonemind) + if(clonemind && mob_occupant.mind != clonemind) clonemind.transfer_to(mob_occupant) mob_occupant.grab_ghost() // We really just want to make you suffer. flash_color(mob_occupant, flash_color="#960000", flash_time=100) diff --git a/code/game/machinery/computer/cloning.dm b/code/game/machinery/computer/cloning.dm index eceb6042..2dfc5603 100644 --- a/code/game/machinery/computer/cloning.dm +++ b/code/game/machinery/computer/cloning.dm @@ -39,7 +39,7 @@ if(pods) for(var/P in pods) var/obj/machinery/clonepod/pod = P - if(pod.occupant && pod.clonemind == mind) + if(pod.occupant && mind && pod.clonemind == mind) return null if(pod.is_operational() && !(pod.occupant || pod.mess)) return pod @@ -60,6 +60,9 @@ else if(!. && pod.is_operational() && !(pod.occupant || pod.mess) && pod.efficiency > 5) . = pod +/proc/grow_clone_from_record(obj/machinery/clonepod/pod, datum/data/record/R, empty) + return pod.growclone(R.fields["name"], R.fields["UI"], R.fields["SE"], R.fields["mindref"], R.fields["last_death"], R.fields["blood_type"], R.fields["mrace"], R.fields["features"], R.fields["factions"], R.fields["quirks"], R.fields["bank_account"], R.fields["traumas"], empty) + /obj/machinery/computer/cloning/process() if(!(scanner && LAZYLEN(pods) && autoprocess)) return @@ -76,8 +79,11 @@ if(pod.occupant) continue //how though? - if(pod.growclone(R.fields["ckey"], R.fields["name"], R.fields["UI"], R.fields["SE"], R.fields["mind"], R.fields["mrace"], R.fields["features"], R.fields["factions"], R.fields["quirks"])) + var/result = grow_clone_from_record(pod, R) + if(result & CLONING_SUCCESS) temp = "[R.fields["name"]] => Cloning cycle in progress..." + log_cloning("Cloning of [key_name(R.fields["mindref"])] automatically started via autoprocess - [src] at [AREACOORD(src)]. Pod: [pod] at [AREACOORD(pod)].") + if(result & CLONING_DELETE_RECORD) records -= R /obj/machinery/computer/cloning/proc/updatemodules(findfirstcloner) @@ -201,6 +207,7 @@ if(scanner_occupant) dat += "Start Scan" + dat += "Body-Only Scan" dat += "
[src.scanner.locked ? "Unlock Scanner" : "Lock Scanner"]" else dat += "Start Scan" @@ -228,8 +235,11 @@ if (!src.active_record) dat += "Record not found." else - dat += "

[src.active_record.fields["name"]]

" - dat += "Scan ID [src.active_record.fields["id"]] Clone
" + var/body_only = active_record.fields["body_only"] + dat += "

[active_record.fields["name"]][body_only ? " - BODY-ONLY" : ""]

" + dat += "Scan ID [active_record.fields["id"]] \ + [!body_only ? "Clone" : "" ]\ + Empty Clone
" var/obj/item/implant/health/H = locate(src.active_record.fields["imp"]) @@ -297,13 +307,14 @@ else if ((href_list["scan"]) && !isnull(scanner) && scanner.is_operational()) scantemp = "" + var/body_only = href_list["body_only"] loading = 1 src.updateUsrDialog() playsound(src, 'sound/machines/terminal_prompt.ogg', 50, 0) say("Initiating scan...") spawn(20) - src.scan_occupant(scanner.occupant) + src.scan_occupant(scanner.occupant, usr, body_only) loading = 0 src.updateUsrDialog() @@ -336,9 +347,23 @@ if ((!src.active_record) || (src.menu < 3)) return if (src.menu == 3) //If we are viewing a record, confirm deletion - src.temp = "Delete record?" - src.menu = 4 - playsound(src, 'sound/machines/terminal_prompt.ogg', 50, 0) + var/has_access = FALSE + if(ishuman(usr)) + var/mob/living/carbon/human/user = usr + var/obj/item/card/id/C = user.get_idcard(TRUE) + if(C) + if(check_access(C)) + has_access = TRUE + if(active_record.fields["body_only"]) //Body-only scans are not as important and can be deleted freely + has_access = TRUE + if(has_access) + temp = "Delete record?" + menu = 4 + playsound(src, 'sound/machines/terminal_prompt.ogg', 50, 0) + else + temp = "Access Denied" + menu = 2 + playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0) else if (src.menu == 4) var/obj/item/card/id/C = usr.get_active_held_item() @@ -400,9 +425,14 @@ else if (href_list["clone"]) var/datum/data/record/C = find_record("id", href_list["clone"], records) + var/empty = href_list["empty"] //Look for that player! They better be dead! if(C) + if(C.fields["body_only"] && !empty) + temp = "Cannot initiate regular cloning with body-only scans." + playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0) var/obj/machinery/clonepod/pod = GetAvailablePod() + var/success = FALSE //Can't clone without someone to clone. Or a pod. Or if the pod is busy. Or full of gibs. if(!LAZYLEN(pods)) temp = "No Clonepods detected." @@ -410,20 +440,31 @@ else if(!pod) temp = "No Clonepods available." playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0) - else if(!CONFIG_GET(flag/revival_cloning)) + else if(!CONFIG_GET(flag/revival_cloning) && !empty) temp = "Unable to initiate cloning cycle." playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0) else if(pod.occupant) temp = "Cloning cycle already in progress." playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0) - else if(pod.growclone(C.fields["ckey"], C.fields["name"], C.fields["UI"], C.fields["SE"], C.fields["mind"], C.fields["mrace"], C.fields["features"], C.fields["factions"], C.fields["quirks"])) - temp = "[C.fields["name"]] => Cloning cycle in progress..." - playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, 0) - records.Remove(C) - if(active_record == C) - active_record = null - menu = 1 else + var/result = grow_clone_from_record(pod, C, empty) + if(result & CLONING_SUCCESS) + temp = "[C.fields["name"]] => Cloning cycle in progress..." + playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, 0) + records.Remove(C) + if(active_record == C) + active_record = null + menu = 1 + success = TRUE + if(!empty) + log_cloning("[key_name(usr)] initiated cloning of [key_name(C.fields["mindref"])] via [src] at [AREACOORD(src)]. Pod: [pod] at [AREACOORD(pod)].") + else + log_cloning("[key_name(usr)] initiated EMPTY cloning of [key_name(C.fields["mindref"])] via [src] at [AREACOORD(src)]. Pod: [pod] at [AREACOORD(pod)].") + if(result & CLONING_DELETE_RECORD) + if(active_record == C) + active_record = null + + if(!success) temp = "[C.fields["name"]] => Initialisation failure." playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0) @@ -439,9 +480,11 @@ src.updateUsrDialog() return -/obj/machinery/computer/cloning/proc/scan_occupant(occupant) +/obj/machinery/computer/cloning/proc/scan_occupant(occupant, mob/M, body_only) var/mob/living/mob_occupant = get_mob_or_brainmob(occupant) var/datum/dna/dna + + // Do not use unless you know what they are. if(ishuman(mob_occupant)) var/mob/living/carbon/C = mob_occupant dna = C.has_dna() @@ -453,7 +496,7 @@ scantemp = "Unable to locate valid genetic data." playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0) return - if(mob_occupant.suiciding || mob_occupant.hellbound) + if(!body_only && (mob_occupant.suiciding || mob_occupant.hellbound)) scantemp = "Subject's brain is not responding to scanning stimuli." playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0) return @@ -461,7 +504,7 @@ scantemp = "Subject no longer contains the fundamental materials required to create a living clone." playsound(src, 'sound/machines/terminal_alert.ogg', 50, 0) return - if ((!mob_occupant.ckey) || (!mob_occupant.client)) + if (!body_only && isnull(mob_occupant.mind)) scantemp = "Mental interface failure." playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0) return @@ -490,6 +533,7 @@ R.fields["blood_type"] = dna.blood_type R.fields["features"] = dna.features R.fields["factions"] = mob_occupant.faction + R.fields["body_only"] = body_only R.fields["quirks"] = list() for(var/V in mob_occupant.roundstart_quirks) var/datum/quirk/T = V @@ -498,17 +542,30 @@ if (!isnull(mob_occupant.mind)) //Save that mind so traitors can continue traitoring after cloning. R.fields["mind"] = "[REF(mob_occupant.mind)]" - //Add an implant if needed - var/obj/item/implant/health/imp - for(var/obj/item/implant/health/HI in mob_occupant.implants) - imp = HI - break - if(!imp) - imp = new /obj/item/implant/health(mob_occupant) - imp.implant(mob_occupant) - R.fields["imp"] = "[REF(imp)]" + if(!body_only) + //Add an implant if needed + var/obj/item/implant/health/imp + for(var/obj/item/implant/health/HI in mob_occupant.implants) + imp = HI + break + if(!imp) + imp = new /obj/item/implant/health(mob_occupant) + imp.implant(mob_occupant) + R.fields["imp"] = "[REF(imp)]" + var/datum/data/record/old_record = find_record("mindref", REF(mob_occupant.mind), records) + if(body_only) + old_record = find_record("UE", dna.unique_enzymes, records) //Body-only records cannot be identified by mind, so we use the DNA + if(old_record && ((old_record.fields["UI"] != dna.uni_identity) || (!old_record.fields["body_only"]))) //Never overwrite a mind-and-body record if it exists + old_record = null + if(old_record) + records -= old_record + scantemp = "Record updated." + else + scantemp = "Subject successfully scanned." src.records += R + log_cloning("[M ? key_name(M) : "Autoprocess"] added the [body_only ? "body-only " : ""]record of [key_name(mob_occupant)] to [src] at [AREACOORD(src)].") + var/obj/item/circuitboard/computer/cloning/board = circuit board.records = records scantemp = "Subject successfully scanned." diff --git a/code/game/machinery/exp_cloner.dm b/code/game/machinery/exp_cloner.dm index ffe66e68..7a3115ad 100644 --- a/code/game/machinery/exp_cloner.dm +++ b/code/game/machinery/exp_cloner.dm @@ -9,7 +9,7 @@ internal_radio = FALSE //Start growing a human clone in the pod! -/obj/machinery/clonepod/experimental/growclone(ckey, clonename, ui, se, datum/species/mrace, list/features, factions) +/obj/machinery/clonepod/experimental/growclone(clonename, ui, mutation_index, mindref, last_death, blood_type, datum/species/mrace, list/features, factions, list/quirks, datum/bank_account/insurance) if(panel_open) return FALSE if(mess || attempting) @@ -20,7 +20,7 @@ var/mob/living/carbon/human/H = new /mob/living/carbon/human(src) - H.hardset_dna(ui, se, H.real_name, null, mrace, features) + H.hardset_dna(ui, mutation_index, H.real_name, blood_type, mrace, features) if(efficiency > 2) var/list/unclean_mutations = (GLOB.not_good_mutations|GLOB.bad_mutations) @@ -292,6 +292,6 @@ temp = "Cloning cycle already in progress." playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0) else - pod.growclone(null, mob_occupant.real_name, dna.uni_identity, dna.mutation_index, clone_species, dna.features, mob_occupant.faction) + pod.growclone(mob_occupant.real_name, dna.uni_identity, dna.mutation_index, null, null, dna.blood_type, clone_species, dna.features, mob_occupant.faction) temp = "[mob_occupant.real_name] => Cloning data sent to pod." playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, 0) diff --git a/code/game/world.dm b/code/game/world.dm index d3491903..a534feb8 100644 --- a/code/game/world.dm +++ b/code/game/world.dm @@ -100,6 +100,7 @@ GLOBAL_VAR(restart_counter) GLOB.picture_log_directory = "data/picture_logs/[override_dir]" GLOB.world_game_log = "[GLOB.log_directory]/game.log" + GLOB.world_cloning_log = "[GLOB.log_directory]/cloning.log" GLOB.world_attack_log = "[GLOB.log_directory]/attack.log" GLOB.world_pda_log = "[GLOB.log_directory]/pda.log" GLOB.world_telecomms_log = "[GLOB.log_directory]/telecomms.log" diff --git a/config/config.txt b/config/config.txt index 5dda144c..819bc01a 100644 --- a/config/config.txt +++ b/config/config.txt @@ -146,6 +146,9 @@ LOG_MANIFEST ## Enable logging pictures # LOG_PICTURES +## log cloning actions +LOG_CLONING + ##Log camera pictures - Must have picture logging enabled PICTURE_LOGGING_CAMERA