From c1000da8c6ca33cbe73cebe51fb205cdb001ef9f Mon Sep 17 00:00:00 2001 From: Willburd <7099514+Willburd@users.noreply.github.com> Date: Sun, 18 May 2025 18:51:59 -0400 Subject: [PATCH] replace xeno regen --- code/__defines/dcs/signals.dm | 2 + code/datums/components/species/xenochimera.dm | 13 +++- code/game/machinery/cloning.dm | 58 ++++----------- code/game/machinery/computer/cloning.dm | 43 ++++++----- .../preference_setup/vore/04_resleeving.dm | 31 ++++---- code/modules/mob/living/living.dm | 5 ++ code/modules/resleeving/infocore_records.dm | 72 +++++++++++++++---- code/modules/resleeving/machines.dm | 4 +- 8 files changed, 132 insertions(+), 96 deletions(-) diff --git a/code/__defines/dcs/signals.dm b/code/__defines/dcs/signals.dm index 7e1dc3c3640..03b4e09091e 100644 --- a/code/__defines/dcs/signals.dm +++ b/code/__defines/dcs/signals.dm @@ -593,6 +593,8 @@ #define COMSIG_HUMAN_DISARM_HIT "human_disarm_hit" ///Whenever EquipRanked is called, called after job is set #define COMSIG_JOB_RECEIVED "job_received" +///When the initial spawn's records are saved. Regardless of pref for it. +#define COMSIG_INITIAL_RECORDS "initial_record_save" // /datum/species signals diff --git a/code/datums/components/species/xenochimera.dm b/code/datums/components/species/xenochimera.dm index 505c9eb6c3d..abe1f854974 100644 --- a/code/datums/components/species/xenochimera.dm +++ b/code/datums/components/species/xenochimera.dm @@ -11,20 +11,29 @@ 'sound/effects/mob_effects/xenochimera/regen_3.ogg', 'sound/effects/mob_effects/xenochimera/regen_5.ogg' ) + var/datum/transhuman/body_record/revival_record /datum/component/xenochimera/Initialize() if(!ishuman(parent)) return COMPONENT_INCOMPATIBLE owner = parent RegisterSignal(owner, COMSIG_XENOCHIMERA_COMPONENT, PROC_REF(handle_comp)) + RegisterSignal(owner, COMSIG_INITIAL_RECORDS, PROC_REF(handle_record)) add_verb(owner, /mob/living/carbon/human/proc/reconstitute_form) /datum/component/xenochimera/Destroy(force) UnregisterSignal(owner, COMSIG_XENOCHIMERA_COMPONENT) + UnregisterSignal(owner, COMSIG_INITIAL_RECORDS) remove_verb(owner, /mob/living/carbon/human/proc/reconstitute_form) + qdel_null(revival_record) owner = null . = ..() +/datum/component/xenochimera/proc/handle_record() + if(QDELETED(owner)) + return + revival_record = new(owner) + /datum/component/xenochimera/proc/handle_comp() if(QDELETED(owner)) return @@ -397,8 +406,8 @@ var/old_nutrition = nutrition var/braindamage = min(5, max(0, (brainloss-1) * 0.5)) //brainloss is tricky to heal and might take a couple of goes to get rid of completely. var/uninjured=quickcheckuninjured() - //I did have special snowflake code, but this is easier. //It's also EXTREMELY BAD AND LETS THEM SAVEFILE HACK. - revive() + xc.revival_record.rebuild_human_mob(src,FALSE) + mutations.Remove(HUSK) setBrainLoss(braindamage) species.update_vore_belly_def_variant() diff --git a/code/game/machinery/cloning.dm b/code/game/machinery/cloning.dm index 9b6c3952d4a..57c3a19e0ff 100644 --- a/code/game/machinery/cloning.dm +++ b/code/game/machinery/cloning.dm @@ -68,27 +68,27 @@ return //Start growing a human clone in the pod! -/obj/machinery/clonepod/proc/growclone(var/datum/dna2/record/R) +/obj/machinery/clonepod/proc/growclone(var/datum/transhuman/body_record/BR) if(mess || attempting) return 0 - var/datum/mind/clonemind = locate(R.mind) + var/datum/mind/clonemind = locate(BR.mydna.mind) if(!istype(clonemind, /datum/mind)) //not a mind return 0 if(clonemind.current && clonemind.current.stat != DEAD) //mind is associated with a non-dead body return 0 if(clonemind.active) //somebody is using that mind - if(ckey(clonemind.key) != R.ckey) + if(ckey(clonemind.key) != BR.ckey) return 0 else for(var/mob/observer/dead/G in player_list) - if(G.ckey == R.ckey) + if(G.ckey == BR.ckey) if(G.can_reenter_corpse) break else return 0 - for(var/modifier_type in R.genetic_modifiers) //Can't be cloned, even if they had a previous scan + for(var/modifier_type in BR.genetic_modifiers) //Can't be cloned, even if they had a previous scan if(istype(modifier_type, /datum/modifier/no_clone)) return 0 @@ -102,24 +102,16 @@ spawn(30) eject_wait = 0 - var/mob/living/carbon/human/H = new /mob/living/carbon/human(src, R.dna.species) - occupant = H - - if(!R.dna.real_name) //to prevent null names - R.dna.real_name = "clone ([rand(0,999)])" - H.real_name = R.dna.real_name - H.gender = R.gender - H.descriptors = R.body_descriptors - - //Get the clone body ready + //Get the clone body ready, let's calculate their health so the pod doesn't immediately eject them!!! + var/mob/living/carbon/human/H = BR.produce_human_mob(src,FALSE, FALSE, "clone ([rand(0,999)])") H.adjustCloneLoss(150) // New damage var so you can't eject a clone early then stab them to abuse the current damage system --NeoFite H.Paralyse(4) - - //Here let's calculate their health so the pod doesn't immediately eject them!!! H.updatehealth() + H.set_cloned_appearance() + // Move mind to body along with key clonemind.transfer_to(H) - H.ckey = R.ckey + H.ckey = BR.ckey to_chat(H, span_warning(span_bold("Consciousness slowly creeps over you as your body regenerates.") + "
" + span_bold(span_large("Your recent memories are fuzzy, and it's hard to remember anything from today...")) + \ "
" + span_notice(span_italics("So this is what cloning feels like?")))) @@ -128,44 +120,24 @@ update_antag_icons(H.mind) // -- End mode specific stuff - if(!R.dna) - H.dna = new /datum/dna() - qdel_swap(H.dna, new /datum/dna()) - else - qdel_swap(H.dna, R.dna) - H.UpdateAppearance() - H.sync_dna_traits(FALSE) // Traitgenes Sync traits to genetics if needed - H.sync_organ_dna() - H.initialize_vessel() - - H.set_cloned_appearance() - update_icon() - // A modifier is added which makes the new clone be unrobust. + // Upgraded cloners can reduce the time of the modifier, up to 80% var/modifier_lower_bound = 25 MINUTES var/modifier_upper_bound = 40 MINUTES - // Upgraded cloners can reduce the time of the modifier, up to 80% var/clone_sickness_length = abs(((heal_level - 20) / 100 ) - 1) clone_sickness_length = between(0.2, clone_sickness_length, 1.0) // Caps it off just incase. modifier_lower_bound = round(modifier_lower_bound * clone_sickness_length, 1) modifier_upper_bound = round(modifier_upper_bound * clone_sickness_length, 1) H.add_modifier(H.species.cloning_modifier, rand(modifier_lower_bound, modifier_upper_bound)) - - // Modifier that doesn't do anything. H.add_modifier(/datum/modifier/cloned) - // This is really stupid. - for(var/modifier_type in R.genetic_modifiers) - H.add_modifier(modifier_type) - - for(var/datum/language/L in R.languages) - H.add_language(L.name) - - H.flavor_texts = R.flavor.Copy() - H.suiciding = 0 + // Finished! + update_icon() + occupant = H attempting = 0 + return 1 //Grow clones to maturity then kick them out. FREELOADERS diff --git a/code/game/machinery/computer/cloning.dm b/code/game/machinery/computer/cloning.dm index da4e264154e..3547b252029 100644 --- a/code/game/machinery/computer/cloning.dm +++ b/code/game/machinery/computer/cloning.dm @@ -14,7 +14,7 @@ var/list/scantemp = null var/menu = MENU_MAIN //Which menu screen to display var/list/records = null - var/datum/dna2/record/active_record = null + var/datum/transhuman/body_record/active_BR = null var/obj/item/disk/body_record/diskette = null // Traitgenes - Storing the entire body record var/loading = 0 // Nice loading text var/autoprocess = 0 @@ -206,15 +206,14 @@ . = TRUE switch(tgui_modal_act(src, action, params)) if(TGUI_MODAL_ANSWER) - if(params["id"] == "del_rec" && active_record) + if(params["id"] == "del_rec" && active_BR) var/obj/item/card/id/C = ui.user.get_active_hand() if(!istype(C) && !istype(C, /obj/item/pda)) set_temp("ID not in hand.", "danger") return if(check_access(C)) - records.Remove(active_record) - qdel(active_record.dna) - qdel(active_record) + records.Remove(active_BR) + qdel(active_BR) // Already deletes dna in destroy() set_temp("Record deleted.", "success") menu = MENU_RECORDS else @@ -245,28 +244,28 @@ var/ref = params["ref"] if(!length(ref)) return - active_record = locate(ref) - if(istype(active_record)) - if(isnull(active_record.ckey)) - qdel(active_record) + active_BR = locate(ref) + if(istype(active_BR)) + if(isnull(active_BR.ckey)) + qdel(active_BR) set_temp("Error: Record corrupt.", "danger") else var/obj/item/implant/health/H = null - if(active_record.implant) - H = locate(active_record.implant) + if(active_BR.mydna.implant) + H = locate(active_BR.mydna.implant) var/list/payload = list( - activerecord = "\ref[active_record]", + activerecord = "\ref[active_BR]", health = (H && istype(H)) ? H.sensehealth() : "", - realname = sanitize(active_record.dna.real_name), - unidentity = active_record.dna.uni_identity, - strucenzymes = active_record.dna.struc_enzymes, + realname = sanitize(active_BR.mydna.dna.real_name), + unidentity = active_BR.mydna.dna.uni_identity, + strucenzymes = active_BR.mydna.dna.struc_enzymes, ) tgui_modal_message(src, action, "", null, payload) else - active_record = null + active_BR = null set_temp("Error: Record missing.", "danger") if("del_rec") - if(!active_record) + if(!active_BR) return tgui_modal_boolean(src, action, "Please confirm that you want to delete the record by holding your ID and pressing Delete:", yes_text = "Delete", no_text = "Cancel") if("disk") // Disk management. @@ -277,20 +276,20 @@ if(isnull(diskette) || isnull(diskette.stored)) // Traitgenes Storing the entire body record set_temp("Error: The disk's data could not be read.", "danger") return - else if(isnull(active_record)) + else if(isnull(active_BR)) set_temp("Error: No active record was found.", "danger") menu = MENU_MAIN return - active_record = diskette.stored.mydna // Traitgenes Storing the entire body record + active_BR = new(diskette.stored) // Traitgenes Storing the entire body record set_temp("Successfully loaded from disk.", "success") if("save") - if(isnull(diskette) || isnull(active_record)) // Traitgenes Removed readonly + if(isnull(diskette) || isnull(active_BR)) // Traitgenes Removed readonly set_temp("Error: The data could not be saved.", "danger") return - diskette.stored.mydna = active_record // Traitgenes Storing the entire body record - diskette.name = "data disk - '[active_record.dna.real_name]'" + diskette.stored = new(active_BR) // Traitgenes Storing the entire body record + diskette.name = "data disk - '[active_BR.mydna.dna.real_name]'" set_temp("Successfully saved to disk.", "success") if("eject") if(!isnull(diskette)) diff --git a/code/modules/client/preference_setup/vore/04_resleeving.dm b/code/modules/client/preference_setup/vore/04_resleeving.dm index 67cf72792fc..8c9f608bec6 100644 --- a/code/modules/client/preference_setup/vore/04_resleeving.dm +++ b/code/modules/client/preference_setup/vore/04_resleeving.dm @@ -26,20 +26,23 @@ /datum/category_item/player_setup_item/vore/resleeve/copy_to_mob(var/mob/living/carbon/human/character) if(character && !istype(character,/mob/living/carbon/human/dummy)) - spawn(50) - if(QDELETED(character) || QDELETED(pref)) - return // They might have been deleted during the wait - if(!character.virtual_reality_mob && !(/mob/living/carbon/human/proc/perform_exit_vr in character.verbs)) //Janky fix to prevent resleeving VR avatars but beats refactoring transcore - if(pref.resleeve_scan) - var/datum/transhuman/body_record/BR = new() - BR.init_from_mob(character, pref.resleeve_scan, pref.resleeve_lock) - if(pref.mind_scan) - var/datum/transcore_db/our_db = SStranscore.db_by_key(null) - if(our_db) - our_db.m_backup(character.mind,character.nif,one_time = TRUE) - if(pref.resleeve_lock) - character.resleeve_lock = character.ckey - character.original_player = character.ckey + addtimer(CALLBACK(character, TYPE_PROC_REF(/mob/living/carbon/human,initial_record_sync), pref), 5 SECONDS, TIMER_DELETE_ME) + +/mob/living/carbon/human/proc/initial_record_sync(var/datum/preferences/pref) + if(QDELETED(src) || QDELETED(pref)) + return // They might have been deleted during the wait + if(!virtual_reality_mob && !(/mob/living/carbon/human/proc/perform_exit_vr in verbs)) //Janky fix to prevent resleeving VR avatars but beats refactoring transcore + if(pref.resleeve_scan) + var/datum/transhuman/body_record/BR = new() + BR.init_from_mob(src, pref.resleeve_scan, pref.resleeve_lock) + if(pref.mind_scan) + var/datum/transcore_db/our_db = SStranscore.db_by_key(null) + if(our_db) + our_db.m_backup(mind,nif,one_time = TRUE) + if(pref.resleeve_lock) + resleeve_lock = ckey + original_player = ckey + SEND_SIGNAL(src, COMSIG_INITIAL_RECORDS) /datum/category_item/player_setup_item/vore/resleeve/content(var/mob/user) . += "
" diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index 50900864966..e5ba442969b 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -679,7 +679,12 @@ return 1 return 0 +/// Revives a body using the client's preferences if human /mob/living/proc/revive() + revival_healing_action() + +/// Performs the actual healing of Aheal, seperate from revive() because it does not use client prefs. Will not heal everything, and expects to be called through revive() or with a bodyrecord doing a respawn/revive. +/mob/living/proc/revival_healing_action() rejuvenate() if(buckled) buckled.unbuckle_mob() diff --git a/code/modules/resleeving/infocore_records.dm b/code/modules/resleeving/infocore_records.dm index 8461d8bfe34..ef5832cebcb 100644 --- a/code/modules/resleeving/infocore_records.dm +++ b/code/modules/resleeving/infocore_records.dm @@ -246,24 +246,35 @@ */ /// The core of resleeving, creates a mob based on the current record -/datum/transhuman/body_record/proc/produce_human_mob(var/is_synthfab, var/force_unlock, var/backup_name) - var/mob/living/carbon/human/H = new /mob/living/carbon/human(src, mydna.dna.species) - - if(!mydna.dna.real_name) - mydna.dna.real_name = backup_name - H.real_name = mydna.dna.real_name - - // Setup body +/datum/transhuman/body_record/proc/produce_human_mob(var/location, var/is_synthfab, var/force_unlock, var/backup_name) + // These are broken up into steps, otherwise the proc gets massive and hard to read. + var/mob/living/carbon/human/H = internal_producebody(location,backup_name) internal_producebody_handlesleevelock(H,force_unlock) internal_producebody_updatelimbandorgans(H) internal_producebody_updatednastate(H,is_synthfab) internal_producebody_virgocharacterdata(H) - - // Finish off - H.suiciding = 0 - H.mind = null return H +/// Creates a human mob with the correct species, name, and a stable state. +/datum/transhuman/body_record/proc/internal_producebody(var/location,var/backup_name) + SHOULD_NOT_OVERRIDE(TRUE) + PRIVATE_PROC(TRUE) + + var/mob/living/carbon/human/H = new /mob/living/carbon/human(location, mydna.dna.species) + if(!mydna.dna.real_name) + mydna.dna.real_name = backup_name + H.real_name = mydna.dna.real_name + H.gender = mydna.gender + H.descriptors = mydna.body_descriptors + for(var/datum/language/L in mydna.languages) + H.add_language(L.name) + H.suiciding = 0 + H.losebreath = 0 + H.mind = null + + return H + +/// Sets the new body's sleevelock status, to prevent impersonation by transfering an incorrect mind. /datum/transhuman/body_record/proc/internal_producebody_handlesleevelock(var/mob/living/carbon/human/H,var/force_unlock) SHOULD_NOT_OVERRIDE(TRUE) PRIVATE_PROC(TRUE) @@ -274,6 +285,7 @@ // Ensure even body scans without an attached ckey respect locking H.resleeve_lock = "@badckey" +/// Either converts limbs to robotics or prosthetic states, or removes them entirely based off record. /datum/transhuman/body_record/proc/internal_producebody_updatelimbandorgans(var/mob/living/carbon/human/H,var/is_synthfab) SHOULD_NOT_OVERRIDE(TRUE) PRIVATE_PROC(TRUE) @@ -313,11 +325,14 @@ else if(status == 3) //Digital organ I.digitize() +/// Transfers dna data to mob, and reinits traits and appearance from it /datum/transhuman/body_record/proc/internal_producebody_updatednastate(var/mob/living/carbon/human/H,var/is_synthfab) SHOULD_NOT_OVERRIDE(TRUE) PRIVATE_PROC(TRUE) //Apply DNA from record + if(!mydna.dna) // This case should never happen, but copied from clone pod... Who knows with this codebase. + mydna.dna = new /datum/dna() qdel_swap(H.dna, mydna.dna.Clone()) H.original_player = ckey @@ -338,10 +353,10 @@ H.regenerate_icons() H.initialize_vessel() +/// Transfers VORE related information cached in the mob /datum/transhuman/body_record/proc/internal_producebody_virgocharacterdata(var/mob/living/carbon/human/H) SHOULD_NOT_OVERRIDE(TRUE) PRIVATE_PROC(TRUE) - //Basically all the VORE stuff H.ooc_notes = body_oocnotes H.ooc_notes_likes = body_ooclikes H.ooc_notes_dislikes = body_oocdislikes @@ -354,3 +369,34 @@ H.weight = weight if(speciesname) H.custom_species = speciesname + +/// Rebuild a body from a record, like aheal, but without save files. Primarily used for anything that heals like xenochimera. +/datum/transhuman/body_record/proc/rebuild_human_mob(var/mob/living/carbon/human/H,var/heal_robot_limbs) + // Boy this one is complex, but what do we expect when trying to heal damage and organ loss in this game! + if(!H || QDELETED(H)) // Someone, somewhere, will call this without any safety. I feel it in my bones cappin' + return + + // Reset our organs/limbs. + H.species.create_organs(H) + internal_producebody_updatelimbandorgans(H) + + //Don't boot out anyone already in the mob. + if(!H.client || !H.key) + for (var/obj/item/organ/internal/brain/CH in GLOB.all_brain_organs) + if(CH.brainmob) + if(CH.brainmob.real_name == H.real_name) + if(CH.brainmob.mind) + CH.brainmob.mind.transfer_to(H) + qdel(CH) + + // Traitgenes Disable all traits currently active, before prefs.copy_to() is applied, as it refreshes the traits list! + for(var/datum/gene/trait/gene in GLOB.dna_genes) + if(gene.name in H.active_genes) + gene.deactivate(H) + H.active_genes -= gene.name + internal_producebody_updatednastate(H,heal_robot_limbs) + + // Begin actual REVIVIAL. Do NOT use revive(). That uses client prefs and allows save hacking. + H.revival_healing_action() + + return H diff --git a/code/modules/resleeving/machines.dm b/code/modules/resleeving/machines.dm index 8b539e2d6d6..aa6cdd631a8 100644 --- a/code/modules/resleeving/machines.dm +++ b/code/modules/resleeving/machines.dm @@ -29,7 +29,7 @@ remove_biomass(CLONE_BIOMASS) //Get the DNA and generate a new mob - var/mob/living/carbon/human/H = current_project.produce_human_mob(FALSE,FALSE,"clone ([rand(0,999)])") + var/mob/living/carbon/human/H = current_project.produce_human_mob(src,FALSE,FALSE,"clone ([rand(0,999)])") //Give breathing equipment if needed if(current_project.breath_type != null && current_project.breath_type != GAS_O2) @@ -206,7 +206,7 @@ return //Get the DNA and generate a new mob - var/mob/living/carbon/human/H = current_project.produce_human_mob(TRUE,FALSE,"synth ([rand(0,999)])") + var/mob/living/carbon/human/H = current_project.produce_human_mob(src,TRUE,FALSE,"synth ([rand(0,999)])") //Apply damage H.adjustBruteLoss(brute_value)