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)