diff --git a/code/__defines/dcs/signals.dm b/code/__defines/dcs/signals.dm
index 54194176d6..c4290a9456 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 mob's dna and species have been fully applied
+#define COMSIG_HUMAN_DNA_FINALIZED "human_dna_finished"
// /datum/species signals
diff --git a/code/__defines/dna.dm b/code/__defines/dna.dm
index c7ee3eceec..1c0862f48c 100644
--- a/code/__defines/dna.dm
+++ b/code/__defines/dna.dm
@@ -103,8 +103,8 @@ var/SMALLSIZEBLOCK = 0
#define DNA_UI_GENDER 14
#define DNA_UI_BEARD_STYLE 15
#define DNA_UI_HAIR_STYLE 16
-#define DNA_UI_EAR_STYLE 17 // VOREStation snippet.
-#define DNA_UI_EAR_SECONDARY_STYLE 18 // VOREStation snippet.
+#define DNA_UI_EAR_STYLE 17
+#define DNA_UI_EAR_SECONDARY_STYLE 18
#define DNA_UI_TAIL_STYLE 19
#define DNA_UI_PLAYERSCALE 20
#define DNA_UI_TAIL_R 21
@@ -152,9 +152,14 @@ var/SMALLSIZEBLOCK = 0
#define DNA_UI_WING2_B 57
#define DNA_UI_WING3_R 58
#define DNA_UI_WING3_G 59
-#define DNA_UI_WING3_B 60 // VOREStation snippet end.
+#define DNA_UI_WING3_B 60
#define DNA_UI_WING_ALPHA 61
-#define DNA_UI_LENGTH 61 // VOREStation Edit - Needs to match the highest number above.
+#define DNA_UI_GRAD_STYLE 62
+#define DNA_UI_GRAD_R 63
+#define DNA_UI_GRAD_G 64
+#define DNA_UI_GRAD_B 65
+// Needs to match the highest number above.
+#define DNA_UI_LENGTH 65
#define DNA_SE_LENGTH 90 // Traitgenes (Expanded from 49 to 84, there have been a considerable expansion of genes.
// This leaves room for future expansion. This can be arbitrarily raised without worry if genes start to get crowded.
diff --git a/code/__defines/mobs.dm b/code/__defines/mobs.dm
index 23e7349dbf..e8222a5735 100644
--- a/code/__defines/mobs.dm
+++ b/code/__defines/mobs.dm
@@ -96,17 +96,18 @@
#define ROBOT_NOTIFICATION_AI_SHELL 5
// Appearance change flags
-#define APPEARANCE_UPDATE_DNA 0x1
-#define APPEARANCE_RACE (0x2|APPEARANCE_UPDATE_DNA)
-#define APPEARANCE_GENDER (0x4|APPEARANCE_UPDATE_DNA)
-#define APPEARANCE_SKIN 0x8
-#define APPEARANCE_HAIR 0x10
-#define APPEARANCE_HAIR_COLOR 0x20
-#define APPEARANCE_FACIAL_HAIR 0x40
-#define APPEARANCE_FACIAL_HAIR_COLOR 0x80
-#define APPEARANCE_EYE_COLOR 0x100
+#define APPEARANCE_RACE 0x1
+#define APPEARANCE_GENDER 0x2
+#define APPEARANCE_SKIN 0x4
+#define APPEARANCE_HAIR 0x8
+#define APPEARANCE_HAIR_COLOR 0x10
+#define APPEARANCE_FACIAL_HAIR 0x20
+#define APPEARANCE_FACIAL_HAIR_COLOR 0x40
+#define APPEARANCE_EYE_COLOR 0x80
#define APPEARANCE_ALL_HAIR (APPEARANCE_HAIR|APPEARANCE_HAIR_COLOR|APPEARANCE_FACIAL_HAIR|APPEARANCE_FACIAL_HAIR_COLOR)
-#define APPEARANCE_ALL 0xFFFF
+#define APPEARANCE_MISC 0x100
+#define APPEARANCE_ALL_COSMETIC (APPEARANCE_GENDER|APPEARANCE_SKIN|APPEARANCE_ALL_HAIR|APPEARANCE_EYE_COLOR|APPEARANCE_MISC)
+#define APPEARANCE_ALL 0xFFFF
// Click cooldown
#define DEFAULT_ATTACK_COOLDOWN 8 //Default timeout for aggressive actions
diff --git a/code/__defines/var_copy.dm b/code/__defines/var_copy.dm
new file mode 100644
index 0000000000..ca75b966c3
--- /dev/null
+++ b/code/__defines/var_copy.dm
@@ -0,0 +1,2 @@
+/// Use this when copying vars[] to skip some built in byond ones that get really unhappy when you access them like that. Used like if(BLACKLISTED_COPY_VARS) in switch or list(BLACKLISTED_COPY_VARS)
+#define BLACKLISTED_COPY_VARS "ATOM_TOPIC_EXAMINE","type","loc","locs","vars","parent","parent_type","verbs","ckey","key","_active_timers", "_datum_components", "_listen_lookup", "_signal_procs"
diff --git a/code/_helpers/global_lists.dm b/code/_helpers/global_lists.dm
index 6cae6f4ba3..6306f11f1e 100644
--- a/code/_helpers/global_lists.dm
+++ b/code/_helpers/global_lists.dm
@@ -308,7 +308,9 @@ GLOBAL_LIST_EMPTY(mannequins)
*/
// Custom species icon bases
- var/list/blacklisted_icons = list(SPECIES_CUSTOM,SPECIES_PROMETHEAN) //VOREStation Edit
+ ///These are icons that you DO NOT want to be selectable!
+ var/list/blacklisted_icons = list(SPECIES_CUSTOM,SPECIES_PROMETHEAN)
+ ///These are icons that you WANT to be selectable, even if they're a whitelist species!
var/list/whitelisted_icons = list(SPECIES_FENNEC,SPECIES_XENOHYBRID,SPECIES_VOX,SPECIES_SHADEKIN) //CHOMPedit
for(var/species_name in GLOB.playable_species)
if(species_name in blacklisted_icons)
diff --git a/code/_helpers/unsorted.dm b/code/_helpers/unsorted.dm
index b0d400619d..0d127ed172 100644
--- a/code/_helpers/unsorted.dm
+++ b/code/_helpers/unsorted.dm
@@ -861,22 +861,7 @@ Turf and target are seperate in case you want to teleport some distance from a t
else
O=new original.type(locate(0,0,0))
- var/static/list/blacklisted_var_names = list(
- "ATOM_TOPIC_EXAMINE",
- "type",
- "loc",
- "locs",
- "vars",
- "parent",
- "parent_type",
- "verbs",
- "ckey",
- "key",
- "_active_timers", // ChompEDIT - blacklist timers
- "_datum_components", // ChompEDIT - blacklist DCS
- "_listen_lookup", // ChompEDIT - blacklist signal listeners
- "_signal_procs" // ChompEDIT - blacklist signal procs
- )
+ var/static/list/blacklisted_var_names = list(BLACKLISTED_COPY_VARS)
if(perfectcopy)
if((O) && (original))
for(var/V in original.vars)
diff --git a/code/datums/components/species/xenochimera.dm b/code/datums/components/species/xenochimera.dm
index 8351ace06e..c830264ed6 100644
--- a/code/datums/components/species/xenochimera.dm
+++ b/code/datums/components/species/xenochimera.dm
@@ -1,31 +1,43 @@
/datum/component/xenochimera
- var/laststress = 0
- var/mob/living/carbon/human/owner
+ VAR_PRIVATE/laststress = 0
+ VAR_PRIVATE/mob/living/carbon/human/owner
var/feral = 0
var/revive_ready = REVIVING_READY
var/revive_finished = FALSE
- var/regen_sounds = list(
+ VAR_PRIVATE/regen_sounds = list(
'sound/effects/mob_effects/xenochimera/regen_1.ogg',
'sound/effects/mob_effects/xenochimera/regen_2.ogg',
'sound/effects/mob_effects/xenochimera/regen_4.ogg',
'sound/effects/mob_effects/xenochimera/regen_3.ogg',
'sound/effects/mob_effects/xenochimera/regen_5.ogg'
)
+ VAR_PRIVATE/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_HUMAN_DNA_FINALIZED, 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_HUMAN_DNA_FINALIZED)
remove_verb(owner, /mob/living/carbon/human/proc/reconstitute_form)
+ qdel_null(revival_record)
owner = null
. = ..()
+/datum/component/xenochimera/proc/handle_record()
+ SIGNAL_HANDLER
+ if(QDELETED(owner))
+ return
+ qdel_null(revival_record)
+ revival_record = new(owner)
+
/datum/component/xenochimera/proc/handle_comp()
+ SIGNAL_HANDLER
if(QDELETED(owner))
return
handle_feralness()
@@ -42,6 +54,19 @@
if(!owner.lying)
owner.lay_down()
+/datum/component/xenochimera/proc/set_revival_delay(var/time)
+ revive_ready = REVIVING_NOW
+ revive_finished = (world.time + time SECONDS) // When do we finish reviving? Allows us to find out when we're done, called by the alert currently.
+
+/datum/component/xenochimera/proc/trigger_revival(var/from_save_slot)
+ ASSERT(revival_record)
+ if(owner.isSynthetic())
+ revival_record.revive_xenochimera(owner,TRUE,from_save_slot)
+ else
+ revival_record.revive_xenochimera(owner,FALSE,from_save_slot)
+ if(from_save_slot)
+ handle_record() // Update record
+
/datum/component/xenochimera/proc/handle_feralness()
//first, calculate how stressed the chimera is
@@ -254,103 +279,102 @@
/mob/living/carbon/human/proc/reconstitute_form() //Scree's race ability.in exchange for: No cloning.
set name = "Reconstitute Form"
set category = "Abilities.Xenochimera"
-
+ var/datum/component/xenochimera/xc = get_xenochimera_component()
+ if(!xc)
+ return
if(is_incorporeal())
to_chat(src, "You cannot regenerate while incorporeal.")
return
// Sanity is mostly handled in chimera_regenerate()
if(stat == DEAD)
- var/confirm = tgui_alert(src, "Are you sure you want to regenerate your corpse? This process can take up to thirty minutes.", "Confirm Regeneration", list("Yes", "No"))
+ var/confirm = tgui_alert(src, "Are you sure you want to regenerate your corpse? This process can take up to thirty minutes. Additionally, you may regenerate your appearance based on your current form or the appearance of the currently loaded slot.", "Confirm Regeneration", list("Yes", "No"))
if(confirm == "Yes")
- chimera_regenerate()
- else if (quickcheckuninjured())
- var/confirm = tgui_alert(src, "Are you sure you want to regenerate? As you are uninjured this will only take 30 seconds and match your appearance to your character slot.", "Confirm Regeneration", list("Yes", "No"))
+ xc.chimera_regenerate()
+ else if(quickcheckuninjured())
+ var/confirm = tgui_alert(src, "Are you sure you want to regenerate? As you are uninjured this will only take 30 seconds. Additionally, you may regenerate your appearance based on your current form or the appearance of the currently loaded slot.", "Confirm Regeneration", list("Yes", "No"))
if(confirm == "Yes")
- chimera_regenerate()
+ xc.chimera_regenerate()
else
- var/confirm = tgui_alert(src, "Are you sure you want to completely reconstruct your form? This process can take up to fifteen minutes, depending on how hungry you are, and you will be unable to move.", "Confirm Regeneration", list("Yes", "No"))
+ var/confirm = tgui_alert(src, "Are you sure you want to completely reconstruct your form? This process can take up to fifteen minutes, depending on how hungry you are, and you will be unable to move. Additionally, you may regenerate your appearance based on your current form or the appearance of the currently loaded slot", "Confirm Regeneration", list("Yes", "No"))
if(confirm == "Yes")
- chimera_regenerate()
+ xc.chimera_regenerate()
-/mob/living/carbon/human/proc/chimera_regenerate()
- var/datum/component/xenochimera/xc = get_xenochimera_component()
- if(!xc)
+/datum/component/xenochimera/proc/chimera_regenerate()
+ if(!owner)
return
//If they're already regenerating
- switch(xc.revive_ready)
+ switch(revive_ready)
if(REVIVING_NOW)
- to_chat(src, "You are already reconstructing, just wait for the reconstruction to finish!")
+ to_chat(owner, "You are already reconstructing, just wait for the reconstruction to finish!")
return
if(REVIVING_DONE)
- to_chat(src, "Your reconstruction is done, but you need to hatch now.")
+ to_chat(owner, "Your reconstruction is done, but you need to hatch now.")
return
- if(xc.revive_ready > world.time)
- to_chat(src, "You can't use that ability again so soon!")
+ if(revive_ready > world.time)
+ to_chat(owner, "You can't use that ability again so soon!")
return
- var/time = min(900, (120+780/(1 + nutrition/100))) //capped at 15 mins, roughly 6 minutes at 250 (yellow) nutrition, 4.1 minutes at 500 (grey), cannot be below 2 mins
- if (quickcheckuninjured()) //if you're completely uninjured, then you get a speedymode - check health first for quickness
+ var/time = min(900, (120+780/(1 + owner.nutrition/100))) //capped at 15 mins, roughly 6 minutes at 250 (yellow) nutrition, 4.1 minutes at 500 (grey), cannot be below 2 mins
+ if (owner.quickcheckuninjured()) //if you're completely uninjured, then you get a speedymode - check health first for quickness
time = 30
//Clicked regen while dead.
- if(stat == DEAD)
+ if(owner.stat == DEAD)
//reviving from dead takes extra nutriment to be provided from outside OR takes twice as long and consumes extra at the end
- if(!hasnutriment())
+ if(!owner.hasnutriment())
time = time*2
- to_chat(src, "You begin to reconstruct your form. You will not be able to move during this time. It should take aproximately [round(time)] seconds.")
+ to_chat(owner, "You begin to reconstruct your form. You will not be able to move during this time. It should take aproximately [round(time)] seconds.")
//Scary spawnerization.
- xc.revive_ready = REVIVING_NOW
- xc.revive_finished = (world.time + time SECONDS) // When do we finish reviving? Allows us to find out when we're done, called by the alert currently.
- throw_alert("regen", /obj/screen/alert/xenochimera/reconstitution)
+ set_revival_delay(time)
+ owner.throw_alert("regen", /obj/screen/alert/xenochimera/reconstitution)
addtimer(CALLBACK(src, PROC_REF(chimera_regenerate_ready)), time SECONDS, TIMER_DELETE_ME)
//Clicked regen while NOT dead
else
- to_chat(src, "You begin to reconstruct your form. You will not be able to move during this time. It should take aproximately [round(time)] seconds.")
+ to_chat(owner, "You begin to reconstruct your form. You will not be able to move during this time. It should take aproximately [round(time)] seconds.")
//Waiting for regen after being alive
- xc.revive_ready = REVIVING_NOW
- xc.revive_finished = (world.time + time SECONDS) // When do we finish reviving? Allows us to find out when we're done, called by the alert currently.
- throw_alert("regen", /obj/screen/alert/xenochimera/reconstitution)
+ set_revival_delay(time)
+ owner.throw_alert("regen", /obj/screen/alert/xenochimera/reconstitution)
addtimer(CALLBACK(src, PROC_REF(chimera_regenerate_nutrition)), time SECONDS, TIMER_DELETE_ME)
+ owner.lying = TRUE
+ // open_appearance_editor()
-/mob/living/carbon/human/proc/chimera_regenerate_nutrition()
- var/datum/component/xenochimera/xc = get_xenochimera_component()
- if(!xc)
+/datum/component/xenochimera/proc/chimera_regenerate_nutrition()
+ if(!owner)
return
//Slightly different flavour messages
- if(stat != DEAD || hasnutriment())
- to_chat(src, span_notice("Consciousness begins to stir as your new body awakens, ready to hatch.."))
+ if(owner.stat != DEAD || owner.hasnutriment())
+ to_chat(owner, span_notice("Consciousness begins to stir as your new body awakens, ready to hatch.."))
else
- to_chat(src, span_warning("Consciousness begins to stir as your battered body struggles to recover from its ordeal.."))
- add_verb(src, /mob/living/carbon/human/proc/hatch)
- xc.revive_ready = REVIVING_DONE
- src << sound('sound/effects/mob_effects/xenochimera/hatch_notification.ogg',0,0,0,30)
- clear_alert("regen")
- throw_alert("hatch", /obj/screen/alert/xenochimera/readytohatch)
+ to_chat(owner, span_warning("Consciousness begins to stir as your battered body struggles to recover from its ordeal.."))
+ add_verb(owner, /mob/living/carbon/human/proc/hatch)
+ revive_ready = REVIVING_DONE
+ owner << sound('sound/effects/mob_effects/xenochimera/hatch_notification.ogg',0,0,0,30)
+ owner.clear_alert("regen")
+ owner.throw_alert("hatch", /obj/screen/alert/xenochimera/readytohatch)
-/mob/living/carbon/human/proc/chimera_regenerate_ready()
- var/datum/component/xenochimera/xc = get_xenochimera_component()
- if(!xc)
+/datum/component/xenochimera/proc/chimera_regenerate_ready()
+ if(!owner)
return
// check to see if they've been fixed by outside forces in the meantime such as defibbing
- if(stat != DEAD)
- to_chat(src, span_notice("Your body has recovered from its ordeal, ready to regenerate itself again."))
- xc.revive_ready = REVIVING_READY //reset their cooldown
- clear_alert("regen")
- throw_alert("hatch", /obj/screen/alert/xenochimera/readytohatch)
+ if(owner.stat != DEAD)
+ to_chat(owner, span_notice("Your body has recovered from its ordeal, ready to regenerate itself again."))
+ revive_ready = REVIVING_READY //reset their cooldown
+ owner.clear_alert("regen")
+ owner.throw_alert("hatch", /obj/screen/alert/xenochimera/readytohatch)
// Was dead, still dead.
else
- to_chat(src, span_notice("Consciousness begins to stir as your new body awakens, ready to hatch."))
- add_verb(src, /mob/living/carbon/human/proc/hatch)
- xc.revive_ready = REVIVING_DONE
- src << sound('sound/effects/mob_effects/xenochimera/hatch_notification.ogg',0,0,0,30)
- clear_alert("regen")
- throw_alert("hatch", /obj/screen/alert/xenochimera/readytohatch)
+ to_chat(owner, span_notice("Consciousness begins to stir as your new body awakens, ready to hatch."))
+ add_verb(owner, /mob/living/carbon/human/proc/hatch)
+ revive_ready = REVIVING_DONE
+ owner << sound('sound/effects/mob_effects/xenochimera/hatch_notification.ogg',0,0,0,30)
+ owner.clear_alert("regen")
+ owner.throw_alert("hatch", /obj/screen/alert/xenochimera/readytohatch)
/mob/living/carbon/human/proc/hatch()
set name = "Hatch"
@@ -359,71 +383,95 @@
if(!xc)
return
if(xc.revive_ready != REVIVING_DONE)
- //Hwhat?
- remove_verb(src, /mob/living/carbon/human/proc/hatch)
- return
+ return //Hwhat?
- var/confirm = tgui_alert(src, "Are you sure you want to hatch right now? This will be very obvious to anyone in view.", "Confirm Regeneration", list("Yes", "No"))
+ // Default is use internal record, even if closes menu
+ var/reload_slot = tgui_alert(src, "Regenerate from your current form, or from the appearance of your current character slot(This will not change your current species or traits.)", "Regenerate Form", list("Current Form", "From Slot"))
+
+ // Check if valid to load from this slot
+ var/from_slot = ""
+ from_slot = "You'll hatch using your current appearance"
+ if(reload_slot == "From Slot" && client)
+ if(client.prefs.species == SPECIES_PROTEAN) // Exploit protection
+ to_chat(src,span_warning("You cannot copy nanoform prosthetic limbs from this species. Please try another character."))
+ return
+ var/slot_is_synth = ((O_BRAIN in client.prefs.organ_data) && client.prefs.organ_data[O_BRAIN])
+ if(slot_is_synth && !isSynthetic()) // Prevents some pretty weird situations
+ to_chat(src,span_warning("Cannot apply character appearance. [slot_is_synth ? "The slot's character is synthetic." : "The slot's character is organic."] Slot must match the current body's synthetic state. Please try another character."))
+ return
+ from_slot = "You'll hatch using [client.prefs.real_name]'s appearance"
+
+ var/confirm = tgui_alert(src, "Are you sure you want to hatch right now? This will be very obvious to anyone in view. [from_slot]! Are you sure?", "Confirm Regeneration", list("Yes", "No"))
if(confirm == "Yes")
+ ///This makes xenochimera shoot out their robotic limbs if they're not a FBP.
+ if(!isSynthetic()) //If we aren't repairing robotic limbs (FBP) we reject any robot limbs we have and kick them out!
+ for(var/O in organs_by_name)
+ var/obj/item/organ/external/organ = organs_by_name[O]
+ if(!istype(organ, /obj/item/organ/external))
+ continue
+ if(!organ.robotic)
+ continue
+ else
+ organ.removed()
+ ///End of xenochimera limb rejection code.
+
//Dead when hatching
+ //var/sickness_duration = 10 MINUTES //CHOMPedit
+ var/has_braindamage = FALSE
if(stat == DEAD)
- // var/sickness_duration = 10 MINUTES //CHOMPedit
//Reviving from ded takes extra nutrition - if it isn't provided from outside sources, it comes from you
if(!hasnutriment())
nutrition=nutrition * 0.75
- // sickness_duration = 20 MINUTES //CHOMPedit
- chimera_hatch()
- // add_modifier(/datum/modifier/resleeving_sickness/chimera, sickness_duration) //CHOMPedit
+ //sickness_duration = 20 MINUTES //CHOMPedit
+ has_braindamage = TRUE
+
+ // Finalize!
+ remove_verb(src, /mob/living/carbon/human/proc/hatch)
+ clear_alert("hatch")
+ xc.chimera_hatch((reload_slot == "From Slot" && client))
+ visible_message(span_warning(span_huge("[src] rises to \his feet."))) //Bloody hell...
+ if(has_braindamage)
+ //add_modifier(/datum/modifier/resleeving_sickness/chimera, sickness_duration) //CHOMPedit
adjustBrainLoss(5) // if they're reviving from dead, they come back with 5 brainloss on top of whatever's unhealed.
- visible_message(span_warning("
" + span_huge("The former corpse staggers to its feet, all its former wounds having vanished...") + "
")) //Bloody hell...
- clear_alert("hatch")
-
-/mob/living/carbon/human/proc/chimera_hatch()
- var/datum/component/xenochimera/xc = get_xenochimera_component()
- if(!xc)
+/datum/component/xenochimera/proc/chimera_hatch(var/from_save_slot)
+ if(!owner)
return
- remove_verb(src, /mob/living/carbon/human/proc/hatch)
- to_chat(src, span_notice("Your new body awakens, bursting free from your old skin."))
+
+ remove_verb(owner, /mob/living/carbon/human/proc/hatch)
+ to_chat(owner, span_notice("Your new body awakens, bursting free from your old skin."))
//Modify and record values (half nutrition and braindamage)
- 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()
- mutations.Remove(HUSK)
- setBrainLoss(braindamage)
- species.update_vore_belly_def_variant()
+ var/old_nutrition = owner.nutrition
+ var/braindamage = min(5, max(0, (owner.brainloss-1) * 0.5)) //brainloss is tricky to heal and might take a couple of goes to get rid of completely.
+ var/uninjured=owner.quickcheckuninjured()
+ trigger_revival(from_save_slot)
+
+ owner.mutations.Remove(HUSK)
+ owner.setBrainLoss(braindamage)
+ owner.species.update_vore_belly_def_variant()
if(!uninjured)
- nutrition = old_nutrition * 0.5
+ owner.nutrition = old_nutrition * 0.5
//Drop everything
- for(var/obj/item/W in src)
- drop_from_inventory(W)
+ for(var/obj/item/W in owner)
+ owner.drop_from_inventory(W)
//Visual effects
- var/T = get_turf(src)
- var/blood_color = species.blood_color
- var/flesh_color = species.flesh_color
+ var/T = get_turf(owner)
+ var/blood_color = owner.species.blood_color
+ var/flesh_color = owner.species.flesh_color
new /obj/effect/gibspawner/human/xenochimera(T, null, flesh_color, blood_color)
- visible_message(span_danger("
" + span_huge("The lifeless husk of [src] bursts open, revealing a new, intact copy in the pool of viscera.") + "
")) //Bloody hell...
+ owner.visible_message(span_danger(span_huge("The lifeless husk of [owner] bursts open, revealing a new, intact copy in the pool of viscera."))) //Bloody hell...
playsound(T, 'sound/effects/mob_effects/xenochimera/hatch.ogg', 50)
else //lower cost for doing a quick cosmetic revive
- nutrition = old_nutrition * 0.9
+ owner.nutrition = old_nutrition * 0.9
//Unfreeze some things
- does_not_breathe = FALSE
- update_canmove()
- stunned = 2
+ owner.does_not_breathe = FALSE
+ owner.update_canmove()
+ owner.AdjustStunned(2)
- xc.revive_ready = world.time + 10 MINUTES //set the cooldown, Reduced this to 10 minutes, you're playing with fire if you're reviving that often.
+ revive_ready = world.time + 10 MINUTES //set the cooldown, Reduced this to 10 minutes, you're playing with fire if you're reviving that often.
/datum/modifier/resleeving_sickness/chimera //near identical to the regular version, just with different flavortexts
name = "imperfect regeneration"
diff --git a/code/game/dna/dna2.dm b/code/game/dna/dna2.dm
index 4a80c02f0d..1f2c4059ad 100644
--- a/code/game/dna/dna2.dm
+++ b/code/game/dna/dna2.dm
@@ -63,10 +63,6 @@ GLOBAL_LIST_EMPTY_TYPED(dna_genes_bad, /datum/gene/trait)
var/gender_specific_species_sounds = FALSE
var/species_sounds_male = "None"
var/species_sounds_female = "None"
- var/grad_style = 0
- var/r_grad = 0
- var/g_grad = 0
- var/b_grad = 0
var/custom_say
var/custom_ask
var/custom_whisper
@@ -74,55 +70,43 @@ GLOBAL_LIST_EMPTY_TYPED(dna_genes_bad, /datum/gene/trait)
var/list/custom_heat = list()
var/list/custom_cold = list()
var/digitigrade = 0 //0, Not FALSE, for future use as indicator for digitigrade types
+ var/custom_footstep = FOOTSTEP_MOB_SHOE
// New stuff
var/species = SPECIES_HUMAN
var/list/body_markings = list()
- var/list/body_markings_genetic = list()
var/list/genetic_modifiers = list() // Modifiers with the MODIFIER_GENETIC flag are saved. Note that only the type is saved, not an instance.
// Make a copy of this strand.
// USE THIS WHEN COPYING STUFF OR YOU'LL GET CORRUPTION!
+// Can you imagine, this used to be done manually, var by var?
/datum/dna/proc/Clone()
var/datum/dna/new_dna = new()
- new_dna.unique_enzymes=unique_enzymes
- new_dna.b_type=b_type
- new_dna.real_name=real_name
- new_dna.species=species
- new_dna.body_markings=body_markings.Copy()
- new_dna.base_species=base_species
- new_dna.custom_species=custom_species
- new_dna.species_traits=species_traits.Copy()
- new_dna.blood_color=blood_color
- new_dna.blood_reagents=blood_reagents
- new_dna.scale_appearance = scale_appearance
- new_dna.offset_override = offset_override
- new_dna.synth_markings = synth_markings
- new_dna.custom_speech_bubble = custom_speech_bubble
- new_dna.species_sounds = species_sounds
- new_dna.gender_specific_species_sounds = gender_specific_species_sounds
- new_dna.species_sounds_male = species_sounds_male
- new_dna.species_sounds_female = species_sounds_female
- new_dna.grad_style = grad_style
- new_dna.r_grad = r_grad
- new_dna.g_grad = g_grad
- new_dna.b_grad = b_grad
- new_dna.custom_say=custom_say
- new_dna.custom_ask=custom_ask
- new_dna.custom_whisper=custom_whisper
- new_dna.custom_exclaim=custom_exclaim
- new_dna.custom_heat=custom_heat
- new_dna.custom_cold=custom_cold
- new_dna.digitigrade=src.digitigrade
- var/list/body_markings_genetic = (body_markings - body_marking_nopersist_list)
- new_dna.body_markings=body_markings_genetic.Copy()
- for(var/b=1;b<=DNA_SE_LENGTH;b++)
- new_dna.SE[b]=SE[b]
- if(b<=DNA_UI_LENGTH)
- new_dna.UI[b]=UI[b]
+ for(var/A in vars)
+ switch(A)
+ if(BLACKLISTED_COPY_VARS)
+ continue
+ if("dirtyUI")
+ dirtyUI=1
+ continue
+ if("dirtySE")
+ dirtySE=1
+ continue
+ if("body_markings")
+ var/list/body_markings_genetic = body_markings.Copy()
+ body_markings_genetic -= body_marking_nopersist_list
+ new_dna.vars[A] = body_markings_genetic
+ continue
+ if(islist(vars[A]))
+ var/list/L = vars[A]
+ new_dna.vars[A] = L.Copy()
+ continue
+ new_dna.vars[A] = vars[A]
+ // Finish up by updating enzymes/identity from our UI/SEs
new_dna.UpdateUI()
new_dna.UpdateSE()
return new_dna
+
///////////////////////////////////////
// UNIQUE IDENTITY
///////////////////////////////////////
@@ -138,6 +122,11 @@ GLOBAL_LIST_EMPTY_TYPED(dna_genes_bad, /datum/gene/trait)
if(!defer)
UpdateUI()
+/**
+ * Beginning of mob to dna, and dna to mob transfer procs.
+ * Ensure that ResetUIFrom() and ApplyToMob() mirror each other. All vars should be read FROM the mob, and written back to it!
+ * ALL dna logic for reading from the mob, storing the dna data, and writing that dna data back to the mob should be here, and ONLY here.
+ */
/datum/dna/proc/ResetUIFrom(var/mob/living/carbon/human/character)
// INITIALIZE!
ResetUI(1)
@@ -174,6 +163,11 @@ GLOBAL_LIST_EMPTY_TYPED(dna_genes_bad, /datum/gene/trait)
if(character.wing_style)
wing_style = wing_styles_list.Find(character.wing_style.type)
+ // Hairgrad
+ var/grad_style = 0
+ if(character.grad_style)
+ grad_style = GLOB.hair_gradients.Find(character.grad_style)
+
// Playerscale (This assumes list is sorted big->small)
var/size_multiplier = GLOB.player_sizes_list.len // If fail to find, take smallest
for(var/N in GLOB.player_sizes_list)
@@ -194,10 +188,6 @@ GLOBAL_LIST_EMPTY_TYPED(dna_genes_bad, /datum/gene/trait)
src.gender_specific_species_sounds = character.species.gender_specific_species_sounds
src.species_sounds_male = character.species.species_sounds_male
src.species_sounds_female = character.species.species_sounds_female
- src.grad_style = character.grad_style
- src.r_grad = character.r_grad
- src.g_grad = character.g_grad
- src.b_grad = character.b_grad
src.species_traits = character.species.traits.Copy()
src.custom_say = character.custom_say
src.custom_ask = character.custom_ask
@@ -206,6 +196,7 @@ GLOBAL_LIST_EMPTY_TYPED(dna_genes_bad, /datum/gene/trait)
src.custom_heat = character.custom_heat
src.custom_cold = character.custom_cold
src.digitigrade = character.digitigrade
+ src.custom_footstep = character.custom_footstep
// +1 to account for the none-of-the-above possibility
SetUIValueRange(DNA_UI_EAR_STYLE, ear_style + 1, ear_styles_list.len + 1, 1)
@@ -213,6 +204,7 @@ GLOBAL_LIST_EMPTY_TYPED(dna_genes_bad, /datum/gene/trait)
SetUIValueRange(DNA_UI_TAIL_STYLE, tail_style + 1, tail_styles_list.len + 1, 1)
SetUIValueRange(DNA_UI_PLAYERSCALE, size_multiplier, GLOB.player_sizes_list.len, 1)
SetUIValueRange(DNA_UI_WING_STYLE, wing_style + 1, wing_styles_list.len + 1, 1)
+ SetUIValueRange(DNA_UI_GRAD_STYLE, grad_style, GLOB.hair_gradients.len, 1)
SetUIValueRange(DNA_UI_TAIL_R, character.r_tail, 255, 1)
SetUIValueRange(DNA_UI_TAIL_G, character.g_tail, 255, 1)
@@ -253,6 +245,10 @@ GLOBAL_LIST_EMPTY_TYPED(dna_genes_bad, /datum/gene/trait)
SetUIValueRange(DNA_UI_EARS3_B, character.b_ears3, 255, 1)
SetUIValueRange(DNA_UI_EARS_ALPHA,character.a_ears, 255, 1)
+ SetUIValueRange(DNA_UI_GRAD_R, character.r_grad, 255, 1)
+ SetUIValueRange(DNA_UI_GRAD_G, character.g_grad, 255, 1)
+ SetUIValueRange(DNA_UI_GRAD_B, character.b_grad, 255, 1)
+
for(var/channel in 1 to DNA_UI_EARS_SECONDARY_COLOR_CHANNEL_COUNT)
var/offset = DNA_UI_EARS_SECONDARY_START + (channel - 1) * 3
var/list/read_rgb = ReadRGB(LAZYACCESS(character.ear_secondary_colors, channel) || "#ffffff")
@@ -294,6 +290,180 @@ GLOBAL_LIST_EMPTY_TYPED(dna_genes_bad, /datum/gene/trait)
UpdateUI()
+/datum/dna/proc/ApplyToMob(var/mob/living/carbon/human/H)
+ ////////////////////////////////////////////////////////////////////////////////
+ // Apply UIs to character
+ //Hair color
+ H.r_hair = GetUIValueRange(DNA_UI_HAIR_R, 255)
+ H.g_hair = GetUIValueRange(DNA_UI_HAIR_G, 255)
+ H.b_hair = GetUIValueRange(DNA_UI_HAIR_B, 255)
+
+ //Facial hair color
+ H.r_facial = GetUIValueRange(DNA_UI_BEARD_R, 255)
+ H.g_facial = GetUIValueRange(DNA_UI_BEARD_G, 255)
+ H.b_facial = GetUIValueRange(DNA_UI_BEARD_B, 255)
+
+ //Skin color (Tone for humans is seperate)
+ H.r_skin = GetUIValueRange(DNA_UI_SKIN_R, 255)
+ H.g_skin = GetUIValueRange(DNA_UI_SKIN_G, 255)
+ H.b_skin = GetUIValueRange(DNA_UI_SKIN_B, 255)
+
+ H.s_tone = 35 - GetUIValueRange(DNA_UI_SKIN_TONE, 220) // Value can be negative.
+
+ //Eye color
+ H.r_eyes = GetUIValueRange(DNA_UI_EYES_R, 255)
+ H.g_eyes = GetUIValueRange(DNA_UI_EYES_G, 255)
+ H.b_eyes = GetUIValueRange(DNA_UI_EYES_B, 255)
+ H.update_eyes()
+
+ //Hair gradient color
+ H.r_grad = GetUIValueRange(DNA_UI_GRAD_R, 255)
+ H.g_grad = GetUIValueRange(DNA_UI_GRAD_G, 255)
+ H.b_grad = GetUIValueRange(DNA_UI_GRAD_B, 255)
+
+ //Sex... Needs future support for properly handling things other then just male/female. UIs have the capability to do so!
+ if(H.gender != NEUTER)
+ if (GetUIState(DNA_UI_GENDER))
+ H.gender = FEMALE
+ else
+ H.gender = MALE
+
+ //Body markings
+ for(var/tag in body_markings)
+ var/obj/item/organ/external/E = H.organs_by_name[tag]
+ if(E)
+ var/list/marklist = body_markings[tag]
+ E.markings = marklist.Copy()
+
+ //Hair style
+ var/hair = GetUIValueRange(DNA_UI_HAIR_STYLE,hair_styles_list.len)
+ if((0 < hair) && (hair <= hair_styles_list.len))
+ H.h_style = hair_styles_list[hair]
+
+ //Facial Hair
+ var/beard = GetUIValueRange(DNA_UI_BEARD_STYLE,facial_hair_styles_list.len)
+ if((0 < beard) && (beard <= facial_hair_styles_list.len))
+ H.f_style = facial_hair_styles_list[beard]
+
+ // Ears
+ var/ears = GetUIValueRange(DNA_UI_EAR_STYLE, ear_styles_list.len + 1) - 1
+ if(ears < 1)
+ H.ear_style = null
+ else if((0 < ears) && (ears <= ear_styles_list.len))
+ H.ear_style = ear_styles_list[ear_styles_list[ears]]
+ var/ears_secondary = GetUIValueRange(DNA_UI_EAR_SECONDARY_STYLE, ear_styles_list.len + 1) - 1
+ if(ears_secondary < 1)
+ H.ear_secondary_style = null
+ else if((0 < ears_secondary) && (ears_secondary <= ear_styles_list.len))
+ H.ear_secondary_style = ear_styles_list[ear_styles_list[ears_secondary]]
+
+ // Ear Color
+ H.r_ears = GetUIValueRange(DNA_UI_EARS_R, 255)
+ H.g_ears = GetUIValueRange(DNA_UI_EARS_G, 255)
+ H.b_ears = GetUIValueRange(DNA_UI_EARS_B, 255)
+ H.r_ears2 = GetUIValueRange(DNA_UI_EARS2_R, 255)
+ H.g_ears2 = GetUIValueRange(DNA_UI_EARS2_G, 255)
+ H.b_ears2 = GetUIValueRange(DNA_UI_EARS2_B, 255)
+ H.r_ears3 = GetUIValueRange(DNA_UI_EARS3_R, 255)
+ H.g_ears3 = GetUIValueRange(DNA_UI_EARS3_G, 255)
+ H.b_ears3 = GetUIValueRange(DNA_UI_EARS3_B, 255)
+ H.a_ears = GetUIValueRange(DNA_UI_EARS_ALPHA, 255)
+ H.a_ears2 = GetUIValueRange(DNA_UI_EARS_SECONDARY_ALPHA, 255)
+
+ LAZYINITLIST(H.ear_secondary_colors)
+ H.ear_secondary_colors.len = max(length(H.ear_secondary_colors), DNA_UI_EARS_SECONDARY_COLOR_CHANNEL_COUNT)
+ for(var/channel in 1 to DNA_UI_EARS_SECONDARY_COLOR_CHANNEL_COUNT)
+ var/offset = DNA_UI_EARS_SECONDARY_START + (channel - 1) * 3
+ H.ear_secondary_colors[channel] = rgb(
+ GetUIValueRange(offset, 255),
+ GetUIValueRange(offset + 1, 255),
+ GetUIValueRange(offset + 2, 255),
+ )
+
+ //Tail
+ var/tail = GetUIValueRange(DNA_UI_TAIL_STYLE, tail_styles_list.len + 1) - 1
+ if(tail < 1)
+ H.tail_style = null
+ else if((0 < tail) && (tail <= tail_styles_list.len))
+ H.tail_style = tail_styles_list[tail_styles_list[tail]]
+
+ //Wing
+ var/wing = GetUIValueRange(DNA_UI_WING_STYLE, wing_styles_list.len + 1) - 1
+ if(wing < 1)
+ H.wing_style = null
+ else if((0 < wing) && (wing <= wing_styles_list.len))
+ H.wing_style = wing_styles_list[wing_styles_list[wing]]
+
+ //Wing Color
+ H.r_wing = GetUIValueRange(DNA_UI_WING_R, 255)
+ H.g_wing = GetUIValueRange(DNA_UI_WING_G, 255)
+ H.b_wing = GetUIValueRange(DNA_UI_WING_B, 255)
+ H.r_wing2 = GetUIValueRange(DNA_UI_WING2_R, 255)
+ H.g_wing2 = GetUIValueRange(DNA_UI_WING2_G, 255)
+ H.b_wing2 = GetUIValueRange(DNA_UI_WING2_B, 255)
+ H.r_wing3 = GetUIValueRange(DNA_UI_WING3_R, 255)
+ H.g_wing3 = GetUIValueRange(DNA_UI_WING3_G, 255)
+ H.b_wing3 = GetUIValueRange(DNA_UI_WING3_B, 255)
+ H.a_wing = GetUIValueRange(DNA_UI_WING_ALPHA, 255)
+
+ // Playerscale
+ var/size = GetUIValueRange(DNA_UI_PLAYERSCALE, GLOB.player_sizes_list.len)
+ if((0 < size) && (size <= GLOB.player_sizes_list.len))
+ H.resize(GLOB.player_sizes_list[GLOB.player_sizes_list[size]], TRUE, ignore_prefs = TRUE)
+
+ // Tail/Taur Color
+ H.r_tail = GetUIValueRange(DNA_UI_TAIL_R, 255)
+ H.g_tail = GetUIValueRange(DNA_UI_TAIL_G, 255)
+ H.b_tail = GetUIValueRange(DNA_UI_TAIL_B, 255)
+ H.r_tail2 = GetUIValueRange(DNA_UI_TAIL2_R, 255)
+ H.g_tail2 = GetUIValueRange(DNA_UI_TAIL2_G, 255)
+ H.b_tail2 = GetUIValueRange(DNA_UI_TAIL2_B, 255)
+ H.r_tail3 = GetUIValueRange(DNA_UI_TAIL3_R, 255)
+ H.g_tail3 = GetUIValueRange(DNA_UI_TAIL3_G, 255)
+ H.b_tail3 = GetUIValueRange(DNA_UI_TAIL3_B, 255)
+ H.a_tail = GetUIValueRange(DNA_UI_TAIL_ALPHA, 255)
+
+ // Hair gradiant
+ var/grad = GetUIValueRange(DNA_UI_GRAD_STYLE,GLOB.hair_gradients.len)
+ if((0 < grad) && (grad <= GLOB.hair_gradients.len))
+ H.grad_style = GLOB.hair_gradients[grad]
+
+ ////////////////////////////////////////////////////////////////////////////////
+ // Custom species and other cosmetic vars
+ H.custom_species = custom_species
+ H.custom_say = custom_say
+ H.custom_ask = custom_ask
+ H.custom_whisper = custom_whisper
+ H.custom_exclaim = custom_exclaim
+ H.custom_speech_bubble = custom_speech_bubble
+ H.custom_heat = custom_heat
+ H.custom_cold = custom_cold
+ H.custom_footstep = custom_footstep
+ H.digitigrade = digitigrade
+
+ // If synths have character markings
+ H.synth_markings = synth_markings
+
+ // Scaling style
+ H.fuzzy = scale_appearance
+ H.offset_override = offset_override
+
+ ////////////////////////////////////////////////////////////////////////////////
+ // Get a copy of the species datum to edit for ourselves
+ // anything that sets stuff in species MUST be done beyond here!
+ H.species.produceCopy(species_traits, H, base_species, FALSE) // Traitgenes edit - reset_dna flag required, or genes get reset on resleeve
+
+ // Update species blood with our blood color from dna!
+ H.species.blood_reagents = blood_reagents
+ H.species.blood_color = blood_color
+ H.species.species_sounds = species_sounds
+ H.species.gender_specific_species_sounds = gender_specific_species_sounds
+ H.species.species_sounds_male = species_sounds_male
+ H.species.species_sounds_female = species_sounds_female
+/**
+ * End of mob to dna, and dna to mob transfer procs.
+ */
+
// Set a DNA UI block's raw value.
/datum/dna/proc/SetUIValue(var/block,var/value,var/defer=0)
if (block<=0) return
diff --git a/code/game/dna/dna2_helpers.dm b/code/game/dna/dna2_helpers.dm
index 96d4162c85..950808873d 100644
--- a/code/game/dna/dna2_helpers.dm
+++ b/code/game/dna/dna2_helpers.dm
@@ -132,172 +132,29 @@
return output
// Use mob.UpdateAppearance()
+/mob/proc/UpdateAppearance(var/list/UI=null)
+ return FALSE
// Simpler. Don't specify UI in order for the mob to use its own.
-/mob/proc/UpdateAppearance(var/list/UI=null)
- if(ishuman(src))
- if(UI!=null)
- src.dna.UI=UI
- src.dna.UpdateUI()
- dna.check_integrity()
- var/mob/living/carbon/human/H = src
- H.r_hair = dna.GetUIValueRange(DNA_UI_HAIR_R, 255)
- H.g_hair = dna.GetUIValueRange(DNA_UI_HAIR_G, 255)
- H.b_hair = dna.GetUIValueRange(DNA_UI_HAIR_B, 255)
+/mob/living/carbon/human/UpdateAppearance(var/list/UI=null)
+ // Rebuild off UI arg if not null
+ if(UI!=null)
+ src.dna.UI=UI
+ src.dna.UpdateUI()
- H.r_facial = dna.GetUIValueRange(DNA_UI_BEARD_R, 255)
- H.g_facial = dna.GetUIValueRange(DNA_UI_BEARD_G, 255)
- H.b_facial = dna.GetUIValueRange(DNA_UI_BEARD_B, 255)
+ // Setup dna
+ dna.check_integrity()
+ dna.ApplyToMob(src)
- H.r_skin = dna.GetUIValueRange(DNA_UI_SKIN_R, 255)
- H.g_skin = dna.GetUIValueRange(DNA_UI_SKIN_G, 255)
- H.b_skin = dna.GetUIValueRange(DNA_UI_SKIN_B, 255)
+ // Apply dna changes to organ icons
+ force_update_organs()
+ force_update_limbs()
- H.r_eyes = dna.GetUIValueRange(DNA_UI_EYES_R, 255)
- H.g_eyes = dna.GetUIValueRange(DNA_UI_EYES_G, 255)
- H.b_eyes = dna.GetUIValueRange(DNA_UI_EYES_B, 255)
- H.update_eyes()
+ //H.update_body(0) //Done in force_update_limbs already
+ update_eyes()
+ update_hair()
- H.s_tone = 35 - dna.GetUIValueRange(DNA_UI_SKIN_TONE, 220) // Value can be negative.
-
- if(H.gender != NEUTER)
- if (dna.GetUIState(DNA_UI_GENDER))
- H.gender = FEMALE
- else
- H.gender = MALE
-
- //Body markings
- for(var/tag in dna.body_markings)
- var/obj/item/organ/external/E = H.organs_by_name[tag]
- if(E)
- var/list/marklist = dna.body_markings[tag]
- E.markings = marklist.Copy()
-
- //Hair
- var/hair = dna.GetUIValueRange(DNA_UI_HAIR_STYLE,hair_styles_list.len)
- if((0 < hair) && (hair <= hair_styles_list.len))
- H.h_style = hair_styles_list[hair]
-
- //Facial Hair
- var/beard = dna.GetUIValueRange(DNA_UI_BEARD_STYLE,facial_hair_styles_list.len)
- if((0 < beard) && (beard <= facial_hair_styles_list.len))
- H.f_style = facial_hair_styles_list[beard]
-
- // Ears
- var/ears = dna.GetUIValueRange(DNA_UI_EAR_STYLE, ear_styles_list.len + 1) - 1
- if(ears < 1)
- H.ear_style = null
- else if((0 < ears) && (ears <= ear_styles_list.len))
- H.ear_style = ear_styles_list[ear_styles_list[ears]]
- var/ears_secondary = dna.GetUIValueRange(DNA_UI_EAR_SECONDARY_STYLE, ear_styles_list.len + 1) - 1
- if(ears_secondary < 1)
- H.ear_secondary_style = null
- else if((0 < ears_secondary) && (ears_secondary <= ear_styles_list.len))
- H.ear_secondary_style = ear_styles_list[ear_styles_list[ears_secondary]]
-
- // Ear Color
- H.r_ears = dna.GetUIValueRange(DNA_UI_EARS_R, 255)
- H.g_ears = dna.GetUIValueRange(DNA_UI_EARS_G, 255)
- H.b_ears = dna.GetUIValueRange(DNA_UI_EARS_B, 255)
- H.r_ears2 = dna.GetUIValueRange(DNA_UI_EARS2_R, 255)
- H.g_ears2 = dna.GetUIValueRange(DNA_UI_EARS2_G, 255)
- H.b_ears2 = dna.GetUIValueRange(DNA_UI_EARS2_B, 255)
- H.r_ears3 = dna.GetUIValueRange(DNA_UI_EARS3_R, 255)
- H.g_ears3 = dna.GetUIValueRange(DNA_UI_EARS3_G, 255)
- H.b_ears3 = dna.GetUIValueRange(DNA_UI_EARS3_B, 255)
- H.a_ears = dna.GetUIValueRange(DNA_UI_EARS_ALPHA, 255)
- H.a_ears2 = dna.GetUIValueRange(DNA_UI_EARS_SECONDARY_ALPHA, 255)
-
- LAZYINITLIST(H.ear_secondary_colors)
- H.ear_secondary_colors.len = max(length(H.ear_secondary_colors), DNA_UI_EARS_SECONDARY_COLOR_CHANNEL_COUNT)
- for(var/channel in 1 to DNA_UI_EARS_SECONDARY_COLOR_CHANNEL_COUNT)
- var/offset = DNA_UI_EARS_SECONDARY_START + (channel - 1) * 3
- H.ear_secondary_colors[channel] = rgb(
- dna.GetUIValueRange(offset, 255),
- dna.GetUIValueRange(offset + 1, 255),
- dna.GetUIValueRange(offset + 2, 255),
- )
-
- //Tail
- var/tail = dna.GetUIValueRange(DNA_UI_TAIL_STYLE, tail_styles_list.len + 1) - 1
- if(tail < 1)
- H.tail_style = null
- else if((0 < tail) && (tail <= tail_styles_list.len))
- H.tail_style = tail_styles_list[tail_styles_list[tail]]
-
- //Wing
- var/wing = dna.GetUIValueRange(DNA_UI_WING_STYLE, wing_styles_list.len + 1) - 1
- if(wing < 1)
- H.wing_style = null
- else if((0 < wing) && (wing <= wing_styles_list.len))
- H.wing_style = wing_styles_list[wing_styles_list[wing]]
-
- //Wing Color
- H.r_wing = dna.GetUIValueRange(DNA_UI_WING_R, 255)
- H.g_wing = dna.GetUIValueRange(DNA_UI_WING_G, 255)
- H.b_wing = dna.GetUIValueRange(DNA_UI_WING_B, 255)
- H.r_wing2 = dna.GetUIValueRange(DNA_UI_WING2_R, 255)
- H.g_wing2 = dna.GetUIValueRange(DNA_UI_WING2_G, 255)
- H.b_wing2 = dna.GetUIValueRange(DNA_UI_WING2_B, 255)
- H.r_wing3 = dna.GetUIValueRange(DNA_UI_WING3_R, 255)
- H.g_wing3 = dna.GetUIValueRange(DNA_UI_WING3_G, 255)
- H.b_wing3 = dna.GetUIValueRange(DNA_UI_WING3_B, 255)
- H.a_wing = dna.GetUIValueRange(DNA_UI_WING_ALPHA, 255)
-
- // Playerscale
- var/size = dna.GetUIValueRange(DNA_UI_PLAYERSCALE, GLOB.player_sizes_list.len)
- if((0 < size) && (size <= GLOB.player_sizes_list.len))
- H.resize(GLOB.player_sizes_list[GLOB.player_sizes_list[size]], TRUE, ignore_prefs = TRUE)
-
- // Tail/Taur Color
- H.r_tail = dna.GetUIValueRange(DNA_UI_TAIL_R, 255)
- H.g_tail = dna.GetUIValueRange(DNA_UI_TAIL_G, 255)
- H.b_tail = dna.GetUIValueRange(DNA_UI_TAIL_B, 255)
- H.r_tail2 = dna.GetUIValueRange(DNA_UI_TAIL2_R, 255)
- H.g_tail2 = dna.GetUIValueRange(DNA_UI_TAIL2_G, 255)
- H.b_tail2 = dna.GetUIValueRange(DNA_UI_TAIL2_B, 255)
- H.r_tail3 = dna.GetUIValueRange(DNA_UI_TAIL3_R, 255)
- H.g_tail3 = dna.GetUIValueRange(DNA_UI_TAIL3_G, 255)
- H.b_tail3 = dna.GetUIValueRange(DNA_UI_TAIL3_B, 255)
- H.a_tail = dna.GetUIValueRange(DNA_UI_TAIL_ALPHA, 255)
-
- // Technically custom_species is not part of the UI, but this place avoids merge problems.
- H.custom_species = dna.custom_species
- H.custom_say = dna.custom_say
- H.custom_ask = dna.custom_ask
- H.custom_whisper = dna.custom_whisper
- H.custom_exclaim = dna.custom_exclaim
- H.species.blood_color = dna.blood_color
- H.fuzzy = dna.scale_appearance
- H.offset_override = dna.offset_override
- H.synth_markings = dna.synth_markings
- H.custom_speech_bubble = dna.custom_speech_bubble
- H.grad_style = dna.grad_style
- H.r_grad = dna.r_grad
- H.g_grad = dna.g_grad
- H.b_grad = dna.b_grad
- H.custom_heat = dna.custom_heat
- H.custom_cold = dna.custom_cold
- var/datum/species/S = H.species
- S.produceCopy(dna.species_traits, H, dna.base_species, FALSE) // Traitgenes edit - reset_dna flag required, or genes get reset on resleeve
- H.dna.blood_reagents = dna.blood_reagents
- H.dna.blood_color = dna.blood_color
- H.species.blood_reagents = H.dna.blood_reagents
- H.species.blood_color = H.dna.blood_color
- H.species.species_sounds = dna.species_sounds
- H.species.gender_specific_species_sounds = dna.gender_specific_species_sounds
- H.species.species_sounds_male = dna.species_sounds_male
- H.species.species_sounds_female = dna.species_sounds_female
-
- H.force_update_organs() //VOREStation Add - Gotta do this too
- H.force_update_limbs()
- //H.update_body(0) //VOREStation Edit - Done in force_update_limbs already
- H.update_eyes()
- H.update_hair()
-
- return 1
- else
- return 0
+ return TRUE
/mob/living/carbon/human/proc/force_update_organs()
for(var/obj/item/organ/O as anything in organs + internal_organs)
diff --git a/code/game/dna/dna_modifier.dm b/code/game/dna/dna_modifier.dm
index 4ff878e536..fd88c04978 100644
--- a/code/game/dna/dna_modifier.dm
+++ b/code/game/dna/dna_modifier.dm
@@ -38,16 +38,21 @@
/datum/dna2/record/proc/copy()
var/datum/dna2/record/newrecord = new /datum/dna2/record
- qdel_swap(newrecord.dna, dna.Clone())
- newrecord.types = types
- newrecord.name = name
- newrecord.mind = mind
- newrecord.ckey = ckey
- newrecord.languages = languages
- newrecord.implant = implant
- newrecord.flavor = flavor
- newrecord.gender = gender
- newrecord.genetic_modifiers = genetic_modifiers.Copy()
+ for(var/A in vars)
+ switch(A)
+ if(BLACKLISTED_COPY_VARS)
+ continue
+ if("id")
+ newrecord.id = copytext(md5(dna.real_name), 2, 6) // update this specially
+ continue
+ if("dna")
+ qdel_swap(newrecord.dna, dna.Clone())
+ continue
+ if(islist(vars[A]))
+ var/list/L = vars[A]
+ newrecord.vars[A] = L.Copy()
+ continue
+ newrecord.vars[A] = vars[A]
return newrecord
@@ -65,7 +70,7 @@
interact_offline = 1
circuit = /obj/item/circuitboard/clonescanner
var/locked = 0
- var/datum/weakref/occupant = null
+ VAR_PRIVATE/datum/weakref/weakref_occupant = null
var/obj/item/reagent_containers/glass/beaker = null
var/opened = 0
var/damage_coeff
@@ -81,6 +86,18 @@
eject_occupant()
. = ..()
+/obj/machinery/dna_scannernew/proc/set_occupant(var/mob/living/L)
+ SHOULD_NOT_OVERRIDE(TRUE)
+ if(!L)
+ weakref_occupant = null
+ return
+ weakref_occupant = WEAKREF(L)
+
+/obj/machinery/dna_scannernew/proc/get_occupant()
+ RETURN_TYPE(/mob/living)
+ SHOULD_NOT_OVERRIDE(TRUE)
+ return weakref_occupant?.resolve()
+
/obj/machinery/dna_scannernew/RefreshParts()
scan_level = 0
damage_coeff = 0
@@ -112,7 +129,7 @@
return
/obj/machinery/dna_scannernew/proc/eject_occupant()
- var/mob/living/carbon/WC = occupant?.resolve()
+ var/mob/living/carbon/WC = get_occupant()
go_out()
for(var/obj/O in src)
if((!istype(O,/obj/item/reagent_containers)) && (!istype(O,/obj/item/circuitboard/clonescanner)) && (!istype(O,/obj/item/stock_parts)) && (!istype(O,/obj/item/stack/cable_coil)))
@@ -122,7 +139,7 @@
M.forceMove(get_turf(src))
/obj/machinery/dna_scannernew/MouseDrop_T(var/mob/target, var/mob/user) //Allows borgs to clone people without external assistance
- var/mob/living/carbon/WC = occupant?.resolve()
+ var/mob/living/carbon/WC = get_occupant()
if(user.stat || user.lying || !Adjacent(user) || !target.Adjacent(user)|| !ishuman(target) || WC)
return
// Traitgenes Do not allow buckled or ridden mobs
@@ -144,13 +161,13 @@
if(!ishuman(usr) && !issmall(usr)) //Make sure they're a mob that has dna
to_chat(usr, span_notice("Try as you might, you can not climb up into the scanner."))
return
- if(occupant)
+ var/mob/living/carbon/WC = get_occupant()
+ if(WC)
to_chat(usr, span_warning("The scanner is already occupied!"))
return
if(usr.abiotic())
to_chat(usr, span_warning("The subject cannot have abiotic items on."))
return
- var/mob/living/carbon/WC = occupant?.resolve()
if(WC)
to_chat(usr, span_warning("There is already something inside."))
return
@@ -158,7 +175,7 @@
usr.client.perspective = EYE_PERSPECTIVE
usr.client.eye = src
usr.forceMove(src)
- occupant = WEAKREF(usr)
+ set_occupant(usr)
icon_state = "scanner_1"
add_fingerprint(usr)
SStgui.update_uis(src)
@@ -182,7 +199,7 @@
return
else if(istype(item, /obj/item/organ/internal/brain))
- if(occupant)
+ if(get_occupant())
to_chat(user, span_warning("The scanner is already occupied!"))
return
var/obj/item/organ/internal/brain/brain = item
@@ -202,7 +219,7 @@
var/obj/item/grab/G = item
if(!ismob(G.affecting))
return
- if(occupant)
+ if(get_occupant())
to_chat(user, span_warning("The scanner is already occupied!"))
return
if(G.affecting.abiotic())
@@ -219,10 +236,10 @@
if(beaker)
beaker.forceMove(get_turf(src))
beaker = null
- if(occupant)
- var/mob/living/carbon/WC = occupant.resolve()
+ var/mob/living/carbon/WC = get_occupant()
+ if(WC)
WC.forceMove(get_turf(src))
- occupant = null
+ set_occupant(null)
// Disconnect from our terminal
for(var/dirfind in GLOB.cardinal)
var/obj/machinery/computer/scan_consolenew/console = locate(/obj/machinery/computer/scan_consolenew, get_step(src, dirfind))
@@ -237,7 +254,7 @@
M.client.perspective = EYE_PERSPECTIVE
M.client.eye = src
M.forceMove(src)
- occupant = WEAKREF(M)
+ set_occupant(M)
icon_state = "scanner_1"
// search for ghosts, if the corpse is empty and the scanner is connected to a cloner
@@ -254,9 +271,9 @@
SStgui.update_uis(src)
/obj/machinery/dna_scannernew/proc/go_out()
- if((!(occupant) || locked))
+ var/mob/living/carbon/WC = get_occupant()
+ if((!WC || locked))
return
- var/mob/living/carbon/WC = occupant.resolve()
if(WC.client)
WC.client.eye = WC.client.mob
WC.client.perspective = MOB_PERSPECTIVE
@@ -268,7 +285,7 @@
break
else
WC.forceMove(loc)
- occupant = null
+ set_occupant(null)
icon_state = "scanner_0"
SStgui.update_uis(src)
@@ -395,7 +412,7 @@
tgui_interact(user)
/obj/machinery/computer/scan_consolenew/tgui_interact(mob/user, datum/tgui/ui)
- var/mob/living/carbon/WC = connected?.occupant?.resolve()
+ var/mob/living/carbon/WC = connected?.get_occupant()
if(!connected || user == WC || user.stat)
return
ui = SStgui.try_update_ui(user, src, ui)
@@ -408,7 +425,7 @@
var/data[0]
data["selectedMenuKey"] = selected_menu_key
data["locked"] = src.connected.locked
- data["hasOccupant"] = connected.occupant ? 1 : 0
+ data["hasOccupant"] = connected.get_occupant() ? 1 : 0
data["isInjectorReady"] = injector_ready
@@ -448,7 +465,7 @@
data["selectedUITargetHex"] = selected_ui_target_hex
var/occupantData[0]
- var/mob/living/carbon/WC = connected?.occupant?.resolve()
+ var/mob/living/carbon/WC = connected?.get_occupant()
if(!WC || !WC.dna)
occupantData["name"] = null
occupantData["stat"] = null
@@ -522,7 +539,7 @@
return TRUE
if("toggleLock")
playsound(src, 'sound/machines/button.ogg', 30, 1, 0)
- if(connected && connected.occupant)
+ if(connected && connected.get_occupant())
connected.locked = !(connected.locked)
return TRUE
@@ -541,9 +558,9 @@
return TRUE
if("injectRejuvenators")
playsound(src, 'sound/machines/button.ogg', 30, 1, 0)
- if(!connected.occupant || !connected.beaker)
+ if(!connected.get_occupant() || !connected.beaker)
return TRUE
- var/mob/living/carbon/WC = connected?.occupant?.resolve()
+ var/mob/living/carbon/WC = connected?.get_occupant()
var/inject_amount = clamp(round(text2num(params["amount"]), 5), 0, 50) // round to nearest 5 and clamp to 0-50
if(!inject_amount)
return TRUE
@@ -561,9 +578,9 @@
selected_se_subblock = clamp(select_subblock, 1, DNA_BLOCK_SIZE)
return TRUE
if("pulseSERadiation")
- if(!connected?.occupant)
+ if(!connected?.get_occupant())
return TRUE
- var/mob/living/carbon/WC = connected?.occupant?.resolve()
+ var/mob/living/carbon/WC = connected?.get_occupant()
playsound(src, "keyboard", 40)
var/block = WC.dna.GetSESubBlock(selected_se_block,selected_se_subblock)
//var/original_block=block
@@ -604,7 +621,7 @@
// Traitgenes Moved SE and UI saves to storing the entire body record
if("saveDNA")
playsound(src, "keyboard", 40) // into console
- var/mob/living/carbon/WC = connected?.occupant?.resolve()
+ var/mob/living/carbon/WC = connected?.get_occupant()
if(WC && WC.dna)
// Traitgenes Properly clone records
var/datum/transhuman/body_record/databuf = new /datum/transhuman/body_record()
@@ -631,7 +648,7 @@
tgui_modal_input(src, "changeBufferLabel", "Please enter the new buffer label:", null, list("id" = bufferId), buffer.mydna.name, TGUI_MODAL_INPUT_MAX_LENGTH_NAME)
return TRUE
if("transfer")
- var/mob/living/carbon/WC = connected?.occupant?.resolve()
+ var/mob/living/carbon/WC = connected?.get_occupant()
if(!WC || (NOCLONE in WC.mutations) || !WC.dna)
return TRUE
irradiating = 2
@@ -673,7 +690,7 @@
playsound(src, "keyboard", 40)
var/datum/transhuman/body_record/buf = buffers[bufferId]
// Send printable record to first sleevepod in area
- print_sleeve(usr, buf)
+ print_sleeve(ui.user, buf)
return TRUE
if("wipeDisk")
@@ -774,12 +791,12 @@
to_chat(user, span_danger( "Error: Cannot grow synthetic."))
return
//No pods
- var/obj/machinery/clonepod/transhuman/pod = locate() in get_area(src)
+ var/obj/machinery/clonepod/pod = locate() in get_area(src)
if(!pod)
to_chat(user, span_danger( "Error: No growpods detected."))
return
//Already doing someone.
- if(pod.occupant)
+ if(pod.get_occupant())
to_chat(user, span_danger( "Error: Growpod is currently occupied."))
return
//Not enough materials.
@@ -805,7 +822,7 @@
to_chat(user, span_notice( "Initiating growing cycle..."))
/obj/machinery/computer/scan_consolenew/proc/do_irradiate(var/lock_state, var/block)
- var/mob/living/carbon/WC = connected?.occupant?.resolve()
+ var/mob/living/carbon/WC = connected?.get_occupant()
irradiating = 0
connected.locked = lock_state
if(!WC)
@@ -840,7 +857,7 @@
WC.regenerate_icons()
/obj/machinery/computer/scan_consolenew/proc/do_pulse(var/lock_state)
- var/mob/living/carbon/WC = connected?.occupant?.resolve()
+ var/mob/living/carbon/WC = connected?.get_occupant()
irradiating = 0
connected.locked = lock_state
@@ -872,7 +889,7 @@
playsound(src, "keyboard", 40)
- var/mob/living/carbon/WC = connected?.occupant?.resolve()
+ var/mob/living/carbon/WC = connected?.get_occupant()
if(!WC)
return TRUE
var/datum/transhuman/body_record/buf = buffers[bufferId] // Traitgenes- Use bodyrecords
diff --git a/code/game/gamemodes/changeling/powers/absorb.dm b/code/game/gamemodes/changeling/powers/absorb.dm
index 8efc8f293f..f5ca7e53fb 100644
--- a/code/game/gamemodes/changeling/powers/absorb.dm
+++ b/code/game/gamemodes/changeling/powers/absorb.dm
@@ -77,7 +77,8 @@
to_chat(src, span_notice("We can now re-adapt, reverting our evolution so that we may start anew, if needed."))
- var/datum/absorbed_dna/newDNA = new(T.real_name, T.dna, T.species.name, T.languages, T.identifying_gender, T.flavor_texts, T.modifiers)
+ var/saved_dna = T.dna.Clone() /// Prevent transforming bugginess.
+ var/datum/absorbed_dna/newDNA = new(T.real_name, saved_dna, T.species.name, T.languages, T.identifying_gender, T.flavor_texts, T.modifiers)
absorbDNA(newDNA)
if(T.mind && T.mind.changeling)
diff --git a/code/game/machinery/cloning.dm b/code/game/machinery/cloning.dm
index 83bb1e11a5..68fddae556 100644
--- a/code/game/machinery/cloning.dm
+++ b/code/game/machinery/cloning.dm
@@ -34,7 +34,7 @@
icon = 'icons/obj/cloning.dmi'
icon_state = "pod_0"
req_access = list(access_genetics) // For premature unlocking.
- var/mob/living/occupant
+ VAR_PRIVATE/datum/weakref/weakref_occupant = null
var/heal_level = 20 // The clone is released once its health reaches this level.
var/heal_rate = 1
var/locked = 0
@@ -54,12 +54,25 @@
default_apply_parts()
update_icon()
+/obj/machinery/clonepod/proc/set_occupant(var/mob/living/L)
+ SHOULD_NOT_OVERRIDE(TRUE)
+ if(!L)
+ weakref_occupant = null
+ return
+ weakref_occupant = WEAKREF(L)
+
+/obj/machinery/clonepod/proc/get_occupant()
+ RETURN_TYPE(/mob/living)
+ SHOULD_NOT_OVERRIDE(TRUE)
+ return weakref_occupant?.resolve()
+
/obj/machinery/clonepod/attack_ai(mob/user as mob)
add_hiddenprint(user)
return attack_hand(user)
/obj/machinery/clonepod/attack_hand(mob/user as mob)
+ var/mob/living/occupant = get_occupant()
if((isnull(occupant)) || (stat & NOPOWER))
return
if((!isnull(occupant)) && (occupant.stat != 2))
@@ -68,27 +81,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,23 +115,20 @@
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
+ //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)])")
+ SEND_SIGNAL(H, COMSIG_HUMAN_DNA_FINALIZED)
//Get the clone body ready
- H.adjustCloneLoss(150) // New damage var so you can't eject a clone early then stab them to abuse the current damage system --NeoFite
+ var/damage_to_deal = H.getMaxHealth() * 1.5 //If you have 100, you get 150. Have 200? Get 300. 25hp? get 37.5
+ H.adjustCloneLoss(damage_to_deal) // 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?"))))
@@ -127,48 +137,29 @@
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()
+ set_occupant(H)
attempting = 0
+
return 1
//Grow clones to maturity then kick them out. FREELOADERS
/obj/machinery/clonepod/process()
+ var/mob/living/occupant = get_occupant()
if(stat & NOPOWER) //Autoeject if power is lost
if(occupant)
locked = 0
@@ -210,7 +201,7 @@
return
else if((!occupant) || (occupant.loc != src))
- occupant = null
+ set_occupant(null)
if(locked)
locked = 0
return
@@ -219,6 +210,7 @@
//Let's unlock this early I guess. Might be too early, needs tweaking.
/obj/machinery/clonepod/attackby(obj/item/W as obj, mob/user as mob)
+ var/mob/living/occupant = get_occupant()
if(isnull(occupant))
if(default_deconstruction_screwdriver(user, W))
return
@@ -272,7 +264,7 @@
..()
/obj/machinery/clonepod/emag_act(var/remaining_charges, var/mob/user)
- if(isnull(occupant))
+ if(isnull(get_occupant()))
return
to_chat(user, "You force an emergency ejection.")
locked = 0
@@ -301,7 +293,7 @@
heal_level = max(min((efficiency * 15) + 10, 100), MINIMUM_HEAL_LEVEL)
/obj/machinery/clonepod/proc/get_completion()
- . = (100 * ((occupant.health + 100) / (heal_level + 100)))
+ . = (100 * ((get_occupant().health + 100) / (heal_level + 100)))
/obj/machinery/clonepod/verb/eject()
set name = "Eject Cloner"
@@ -324,20 +316,21 @@
update_icon()
return
+ var/mob/living/occupant = get_occupant()
if(!(occupant))
return
if(occupant.client)
occupant.client.eye = occupant.client.mob
occupant.client.perspective = MOB_PERSPECTIVE
- occupant.loc = src.loc
+ occupant.forceMove(get_turf(src))
eject_wait = 0 //If it's still set somehow.
if(ishuman(occupant)) //Need to be safe.
var/mob/living/carbon/human/patient = occupant
if(!(patient.species.flags & NO_DNA)) //If, for some reason, someone makes a genetically-unalterable clone, let's not make them permanently disabled.
domutcheck(occupant) //Waiting until they're out before possible transforming.
occupant.UpdateAppearance()
- occupant = null
+ set_occupant(null)
update_icon()
return
@@ -400,6 +393,7 @@
return 0
/obj/machinery/clonepod/proc/malfunction()
+ var/mob/living/occupant = get_occupant()
if(occupant)
connected_message("Critical Error!")
mess = 1
@@ -421,21 +415,21 @@
switch(severity)
if(1.0)
for(var/atom/movable/A as mob|obj in src)
- A.loc = src.loc
+ A.forceMove(get_turf(src))
ex_act(severity)
qdel(src)
return
if(2.0)
if(prob(50))
for(var/atom/movable/A as mob|obj in src)
- A.loc = src.loc
+ A.forceMove(get_turf(src))
ex_act(severity)
qdel(src)
return
if(3.0)
if(prob(25))
for(var/atom/movable/A as mob|obj in src)
- A.loc = src.loc
+ A.forceMove(get_turf(src))
ex_act(severity)
qdel(src)
return
@@ -444,7 +438,7 @@
/obj/machinery/clonepod/update_icon()
..()
icon_state = "pod_0"
- if(occupant && !(stat & NOPOWER))
+ if(get_occupant() && !(stat & NOPOWER))
icon_state = "pod_1"
else if(mess)
icon_state = "pod_g"
diff --git a/code/game/machinery/computer/cloning.dm b/code/game/machinery/computer/cloning.dm
index 5bb3dd67a6..f962921d1a 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
@@ -34,27 +34,26 @@
/obj/machinery/computer/cloning/Destroy()
releasecloner()
- for(var/datum/dna2/record/R in records)
- qdel(R.dna)
- qdel(R)
+ for(var/datum/transhuman/body_record/BR in records)
+ qdel(BR)
return ..()
/obj/machinery/computer/cloning/process()
if(!scanner || !pods.len || !autoprocess || stat & NOPOWER)
return
- if(scanner.occupant && can_autoprocess())
- scan_mob(scanner.occupant)
+ if(scanner.get_occupant() && can_autoprocess())
+ scan_mob(scanner.get_occupant())
if(!LAZYLEN(records))
return
for(var/obj/machinery/clonepod/pod in pods)
- if(!(pod.occupant || pod.mess) && (pod.efficiency > 5))
- for(var/datum/dna2/record/R in records)
- if(!(pod.occupant || pod.mess))
- if(pod.growclone(R))
- records.Remove(R)
+ if(!(pod.get_occupant() || pod.mess) && (pod.efficiency > 5))
+ for(var/datum/transhuman/body_record/BR in records)
+ if(!(pod.get_occupant() || pod.mess))
+ if(pod.growclone(BR))
+ records.Remove(BR)
/obj/machinery/computer/cloning/proc/updatemodules()
scanner = findscanner()
@@ -67,8 +66,8 @@
var/obj/machinery/dna_scannernew/scannerf = null
//Try to find scanner on adjacent tiles first
- for(dir in list(NORTH,EAST,SOUTH,WEST))
- scannerf = locate(/obj/machinery/dna_scannernew, get_step(src, dir))
+ for(var/scan_dir in list(NORTH,EAST,SOUTH,WEST))
+ scannerf = locate(/obj/machinery/dna_scannernew, get_step(src, scan_dir))
if(scannerf)
return scannerf
@@ -97,7 +96,7 @@
if(istype(W, /obj/item/disk/body_record/)) //Traitgenes Storing the entire body record
if(!diskette)
user.drop_item()
- W.loc = src
+ W.forceMove(src)
diskette = W
to_chat(user, "You insert [W].")
SStgui.update_uis(src)
@@ -154,17 +153,18 @@
if(pod.efficiency > 5)
canpodautoprocess = 1
+ var/mob/living/occupant = pod.get_occupant()
var/status = "idle"
if(pod.mess)
status = "mess"
- else if(pod.occupant && !(pod.stat & NOPOWER))
+ else if(occupant && !(pod.stat & NOPOWER))
status = "cloning"
tempods.Add(list(list(
"pod" = "\ref[pod]",
"name" = sanitize(capitalize(pod.name)),
"biomass" = pod.get_biomass(),
"status" = status,
- "progress" = (pod.occupant && pod.occupant.stat != DEAD) ? pod.get_completion() : 0
+ "progress" = (occupant && occupant.stat != DEAD) ? pod.get_completion() : 0
)))
data["pods"] = tempods
@@ -178,16 +178,16 @@
else
data["autoallowed"] = 0
if(scanner)
- data["occupant"] = scanner.occupant
+ data["occupant"] = scanner.get_occupant()
data["locked"] = scanner.locked
data["temp"] = temp
data["scantemp"] = scantemp
data["disk"] = diskette
data["selected_pod"] = "\ref[selected_pod]"
var/list/temprecords[0]
- for(var/datum/dna2/record/R in records)
- var tempRealName = R.dna.real_name
- temprecords.Add(list(list("record" = "\ref[R]", "realname" = sanitize(tempRealName))))
+ for(var/datum/transhuman/body_record/BR in records)
+ var tempRealName = BR.mydna.dna.real_name
+ temprecords.Add(list(list("record" = "\ref[BR]", "realname" = sanitize(tempRealName))))
data["records"] = temprecords
if(selected_pod && (selected_pod in pods) && selected_pod.get_biomass() >= CLONE_BIOMASS)
@@ -206,67 +206,68 @@
. = 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
set_temp("Access denied.", "danger")
return
+ var/mob/living/carbon/human/scanner_occupant = scanner.get_occupant()
+
switch(action)
if("scan")
- if(!scanner || !scanner.occupant || loading)
+ if(!scanner || !scanner_occupant || loading)
return
set_scan_temp("Scanner ready.", "good")
loading = TRUE
spawn(20)
if(can_brainscan() && scan_mode)
- scan_mob(scanner.occupant, scan_brain = TRUE)
+ scan_mob(scanner_occupant, scan_brain = TRUE)
else
- scan_mob(scanner.occupant)
+ scan_mob(scanner_occupant)
loading = FALSE
SStgui.update_uis(src)
if("autoprocess")
autoprocess = text2num(params["on"]) > 0
if("lock")
- if(isnull(scanner) || !scanner.occupant) //No locking an open scanner.
+ if(isnull(scanner) || !scanner_occupant) //No locking an open scanner.
return
scanner.locked = !scanner.locked
if("view_rec")
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,24 +278,24 @@
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))
- diskette.loc = loc
+ diskette.forceMove(get_turf(src))
diskette = null
if("refresh")
SStgui.update_uis(src)
@@ -309,7 +310,7 @@
var/ref = params["ref"]
if(!length(ref))
return
- var/datum/dna2/record/C = locate(ref)
+ var/datum/transhuman/body_record/C = locate(ref)
//Look for that player! They better be dead!
if(istype(C))
tgui_modal_clear(src)
@@ -321,7 +322,7 @@
var/cloneresult
if(!selected_pod)
set_temp("Error: No cloning pod selected.", "danger")
- else if(pod.occupant)
+ else if(pod.get_occupant())
set_temp("Error: The cloning pod is currently occupied.", "danger")
else if(pod.get_biomass() < CLONE_BIOMASS)
set_temp("Error: Not enough biomass.", "danger")
@@ -335,7 +336,6 @@
set_temp("Initiating cloning cycle...", "success")
playsound(src, 'sound/machines/medbayscanner1.ogg', 100, 1)
records.Remove(C)
- qdel(C.dna)
qdel(C)
menu = MENU_MAIN
else
@@ -407,49 +407,38 @@
return
for(var/obj/machinery/clonepod/pod in pods)
- if(pod.occupant && pod.occupant.mind == subject.mind)
+ var/mob/living/occupant = pod.get_occupant()
+ if(occupant && occupant.mind == subject.mind)
set_scan_temp("Subject already getting cloned.")
SStgui.update_uis(src)
return
subject.dna.check_integrity()
-
- var/datum/dna2/record/R = new /datum/dna2/record()
- qdel_swap(R.dna, subject.dna)
- R.ckey = subject.ckey
- R.id = copytext(md5(subject.real_name), 2, 6)
- R.name = R.dna.real_name
- R.types = DNA2_BUF_UI|DNA2_BUF_UE|DNA2_BUF_SE
- R.languages = subject.languages
- R.gender = subject.gender
- R.flavor = subject.flavor_texts.Copy()
- for(var/datum/modifier/mod in subject.modifiers)
- if(mod.flags & MODIFIER_GENETIC)
- R.genetic_modifiers.Add(mod.type)
+ var/datum/transhuman/body_record/BR = new(subject)
//Add an implant if needed
var/obj/item/implant/health/imp = locate(/obj/item/implant/health, subject)
if (isnull(imp))
imp = new /obj/item/implant/health(subject)
imp.implanted = subject
- R.implant = "\ref[imp]"
+ BR.mydna.implant = "\ref[imp]"
//Update it if needed
else
- R.implant = "\ref[imp]"
+ BR.mydna.implant = "\ref[imp]"
if (!isnull(subject.mind)) //Save that mind so traitors can continue traitoring after cloning.
- R.mind = "\ref[subject.mind]"
+ BR.mydna.mind = "\ref[subject.mind]"
- records += R
+ records += BR
set_scan_temp("Subject successfully scanned.", "good")
SStgui.update_uis(src)
//Find a specific record by key.
/obj/machinery/computer/cloning/proc/find_record(var/find_key)
var/selected_record = null
- for(var/datum/dna2/record/R in records)
- if(R.ckey == find_key)
- selected_record = R
+ for(var/datum/transhuman/body_record/BR in records)
+ if(BR.mydna.ckey == find_key)
+ selected_record = BR
break
return selected_record
diff --git a/code/game/machinery/protean_reconstitutor.dm b/code/game/machinery/protean_reconstitutor.dm
index 5fef0e960c..26090eb29f 100644
--- a/code/game/machinery/protean_reconstitutor.dm
+++ b/code/game/machinery/protean_reconstitutor.dm
@@ -271,6 +271,8 @@
if(def_lang)
P.default_language = def_lang
+ SEND_SIGNAL(P, COMSIG_HUMAN_DNA_FINALIZED)
+
protean_brain.brainmob.mind.transfer_to(P)
protean_brain.loc = BR
protean_refactory = null
diff --git a/code/game/machinery/virtual_reality/ar_console.dm b/code/game/machinery/virtual_reality/ar_console.dm
index a0893aa9d0..c3ac5e35c2 100644
--- a/code/game/machinery/virtual_reality/ar_console.dm
+++ b/code/game/machinery/virtual_reality/ar_console.dm
@@ -135,6 +135,8 @@
avatar.sync_organ_dna()
avatar.initialize_vessel()
+ SEND_SIGNAL(avatar, COMSIG_HUMAN_DNA_FINALIZED)
+
var/newname = sanitize(tgui_input_text(avatar, "Your mind feels foggy. You're certain your name is [occupant.real_name], but it could also be [avatar.name]. Would you like to change it to something else?", "Name change", null, MAX_NAME_LEN), MAX_NAME_LEN)
if (newname)
avatar.real_name = newname
diff --git a/code/game/machinery/virtual_reality/vr_console.dm b/code/game/machinery/virtual_reality/vr_console.dm
index a248c2ab32..b190c28f70 100644
--- a/code/game/machinery/virtual_reality/vr_console.dm
+++ b/code/game/machinery/virtual_reality/vr_console.dm
@@ -314,6 +314,8 @@
avatar.sync_organ_dna()
avatar.initialize_vessel()
+ SEND_SIGNAL(avatar, COMSIG_HUMAN_DNA_FINALIZED)
+
if(tf)
var/mob/living/new_form = avatar.transform_into_mob(tf, TRUE) // No need to check prefs when the occupant already chose to transform.
if(isliving(new_form)) // Make sure the mob spawned properly.
diff --git a/code/game/machinery/virtual_reality/vr_procs.dm b/code/game/machinery/virtual_reality/vr_procs.dm
index 0afd9e562e..40f0a2dd68 100644
--- a/code/game/machinery/virtual_reality/vr_procs.dm
+++ b/code/game/machinery/virtual_reality/vr_procs.dm
@@ -106,6 +106,8 @@
if(is_lang_whitelisted(usr,chosen_language) || (avatar.species && (chosen_language.name in avatar.species.secondary_langs)))
avatar.add_language(lang)
+ SEND_SIGNAL(avatar, COMSIG_HUMAN_DNA_FINALIZED)
+
avatar.regenerate_icons()
avatar.update_transform()
job_master.EquipRank(avatar,JOB_VR, 1, FALSE)
diff --git a/code/game/objects/structures/ghost_pods/event_vr.dm b/code/game/objects/structures/ghost_pods/event_vr.dm
index d1d6ae07fe..6ad09905b1 100644
--- a/code/game/objects/structures/ghost_pods/event_vr.dm
+++ b/code/game/objects/structures/ghost_pods/event_vr.dm
@@ -257,6 +257,8 @@
if(is_lang_whitelisted(src,chosen_language) || (new_character.species && (chosen_language.name in new_character.species.secondary_langs)))
new_character.add_language(lang)
+ SEND_SIGNAL(new_character, COMSIG_HUMAN_DNA_FINALIZED)
+
new_character.regenerate_icons()
new_character.update_transform()
diff --git a/code/modules/admin/verbs/randomverbs.dm b/code/modules/admin/verbs/randomverbs.dm
index 9e54293f82..7d57cd453d 100644
--- a/code/modules/admin/verbs/randomverbs.dm
+++ b/code/modules/admin/verbs/randomverbs.dm
@@ -561,6 +561,8 @@ Traitors and the like can also be revived with the previous role mostly intact.
if(is_lang_whitelisted(src,chosen_language) || (new_character.species && (chosen_language.name in new_character.species.secondary_langs)))
new_character.add_language(lang)
+ SEND_SIGNAL(new_character, COMSIG_HUMAN_DNA_FINALIZED)
+
//If desired, apply equipment.
if(equipment)
if(charjob)
diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm
index 147e216d85..f55911f9a3 100644
--- a/code/modules/client/preferences.dm
+++ b/code/modules/client/preferences.dm
@@ -533,7 +533,7 @@ var/list/preferences_datums = list()
else
var/bodytype
var/datum/species/selected_species = GLOB.all_species[species]
- if(custom_base)
+ if(selected_species.selects_bodytype && custom_base) //Everyone technically has custom_base set to HUMAN, but only some species actually select it.
bodytype = custom_base
else
bodytype = selected_species.get_bodytype()
@@ -544,7 +544,8 @@ var/list/preferences_datums = list()
for(var/N in character.organs_by_name)
var/obj/item/organ/external/O = character.organs_by_name[N]
- O.markings.Cut()
+ if(O)
+ O.markings.Cut()
var/priority = 0
for(var/M in body_markings)
@@ -554,6 +555,7 @@ var/list/preferences_datums = list()
for(var/BP in mark_datum.body_parts)
var/obj/item/organ/external/O = character.organs_by_name[BP]
if(O)
+ if(!islist(body_markings[M][BP])) continue
O.markings[M] = list("color" = body_markings[M][BP]["color"], "datum" = mark_datum, "priority" = priority, "on" = body_markings[M][BP]["on"])
character.markings_len = priority
@@ -581,7 +583,6 @@ var/list/preferences_datums = list()
character.fuzzy = fuzzy
character.offset_override = offset_override
character.voice_freq = voice_freq
- character.size_multiplier = size_multiplier
character.resize(size_multiplier, animate = FALSE, ignore_prefs = TRUE)
var/list/traits_to_copy = list(/datum/trait/neutral/tall,
@@ -616,17 +617,16 @@ var/list/preferences_datums = list()
var/datum/species/selected_species = GLOB.all_species[species]
var/bodytype_selected
- if(custom_base)
+ if(selected_species.selects_bodytype && custom_base)
bodytype_selected = custom_base
else
bodytype_selected = selected_species.get_bodytype(character)
-
character.dna.base_species = bodytype_selected
character.species.base_species = bodytype_selected
character.species.icobase = character.species.get_icobase()
character.species.deform = character.species.get_icobase(get_deform = TRUE)
character.species.vanity_base_fit = bodytype_selected
- if (istype(character.species, /datum/species/shapeshifter))
+ if(istype(character.species, /datum/species/shapeshifter))
wrapped_species_by_ref["\ref[character]"] = bodytype_selected
character.custom_species = custom_species
diff --git a/code/modules/mob/living/carbon/human/species/species.dm b/code/modules/mob/living/carbon/human/species/species.dm
index 77e5f3f7ed..061315b9f4 100644
--- a/code/modules/mob/living/carbon/human/species/species.dm
+++ b/code/modules/mob/living/carbon/human/species/species.dm
@@ -801,11 +801,11 @@
//We REALLY don't need to go through every variable. Doing so makes this lag like hell on 515
/datum/species/proc/copy_variables(var/datum/species/S, var/list/whitelist)
//List of variables to ignore, trying to copy type will runtime.
- //var/list/blacklist = list("type", "loc", "client", "ckey")
+ //var/list/blacklist = list(BLACKLISTED_COPY_VARS)
//Makes thorough copy of species datum.
for(var/i in whitelist)
- if(!(i in S.vars)) //Don't copy incompatible vars.
- continue
+ //if(!(i in S.vars)) // This check SOUNDS like a good idea, until you realize it loops over every var in base datum + species datum + byond builtin vars for EACH var in the whitelist. All the vars in whitelist are in the base species datum anyway, so this is unneeded.
+ // continue
if(S.vars[i] != vars[i] && !islist(vars[i])) //If vars are same, no point in copying.
S.vars[i] = vars[i]
diff --git a/code/modules/mob/living/carbon/human/species/station/prometheans.dm b/code/modules/mob/living/carbon/human/species/station/prometheans.dm
index 9aa6085f5d..5dad808f13 100644
--- a/code/modules/mob/living/carbon/human/species/station/prometheans.dm
+++ b/code/modules/mob/living/carbon/human/species/station/prometheans.dm
@@ -109,16 +109,8 @@ var/datum/species/shapeshifter/promethean/prometheans
cold_discomfort_strings = list("You feel too cool.")
inherent_verbs = list(
- /mob/living/carbon/human/proc/shapeshifter_select_shape,
- /mob/living/carbon/human/proc/shapeshifter_select_colour,
- /mob/living/carbon/human/proc/shapeshifter_select_hair,
- /mob/living/carbon/human/proc/shapeshifter_select_hair_colors,
- /mob/living/carbon/human/proc/shapeshifter_select_gender,
+ /mob/living/carbon/human/proc/innate_shapeshifting,
/mob/living/carbon/human/proc/regenerate,
- /mob/living/carbon/human/proc/shapeshifter_select_wings,
- /mob/living/carbon/human/proc/shapeshifter_select_tail,
- /mob/living/carbon/human/proc/shapeshifter_select_ears,
- /mob/living/carbon/human/proc/shapeshifter_select_secondary_ears,
/mob/living/carbon/human/proc/prommie_blobform,
/mob/living/proc/set_size,
/mob/living/carbon/human/proc/promethean_select_opaqueness,
@@ -410,3 +402,9 @@ var/datum/species/shapeshifter/promethean/prometheans
return
else
prommie_intoblob()
+
+/mob/living/carbon/human/proc/innate_shapeshifting()
+ set name = "Transform Appearance"
+ set category = "Abilities.Superpower"
+ var/datum/tgui_module/appearance_changer/innate/I = new(src, src)
+ I.tgui_interact(src)
diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm
index c49300dae3..9aa66cc739 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()
@@ -701,6 +706,8 @@
if(ai_holder) // AI gets told to sleep when killed. Since they're not dead anymore, wake it up.
ai_holder.go_wake()
+ SEND_SIGNAL(src, COMSIG_HUMAN_DNA_FINALIZED)
+
/mob/living/proc/rejuvenate()
if(reagents)
reagents.clear_reagents()
diff --git a/code/modules/mob/new_player/new_player.dm b/code/modules/mob/new_player/new_player.dm
index 38524fcb44..2d14ec4d01 100644
--- a/code/modules/mob/new_player/new_player.dm
+++ b/code/modules/mob/new_player/new_player.dm
@@ -447,15 +447,15 @@
var/datum/language/keylang = GLOB.all_languages[client.prefs.language_custom_keys[key]]
if(keylang)
new_character.language_keys[key] = keylang
- // VOREStation Add: Preferred Language Setting;
if(client.prefs.preferred_language) // Do we have a preferred language?
var/datum/language/def_lang = GLOB.all_languages[client.prefs.preferred_language]
if(def_lang)
new_character.default_language = def_lang
- // VOREStation Add End
// And uncomment this, too.
//new_character.dna.UpdateSE()
+ SEND_SIGNAL(new_character, COMSIG_HUMAN_DNA_FINALIZED)
+
// Do the initial caching of the player's body icons.
new_character.force_update_limbs()
new_character.update_icons_body()
diff --git a/code/modules/mob/new_player/sprite_accessories.dm b/code/modules/mob/new_player/sprite_accessories.dm
index fbbff18767..f1610ac5b5 100644
--- a/code/modules/mob/new_player/sprite_accessories.dm
+++ b/code/modules/mob/new_player/sprite_accessories.dm
@@ -37,7 +37,7 @@ GLOBAL_LIST_INIT(fancy_sprite_accessory_color_channel_names, list("Primary", "Se
var/gender = NEUTER
// Restrict some styles to specific species. Default to all species to avoid runtimes in character creator.
- var/list/species_allowed = list(SPECIES_HUMAN, SPECIES_SKRELL, SPECIES_UNATHI, SPECIES_TAJARAN, SPECIES_TESHARI, SPECIES_NEVREAN, SPECIES_AKULA, SPECIES_SERGAL, SPECIES_FENNEC, SPECIES_ZORREN_HIGH, SPECIES_VULPKANIN, SPECIES_XENOCHIMERA, SPECIES_XENOHYBRID, SPECIES_VASILISSAN, SPECIES_RAPALA, SPECIES_PROTEAN, SPECIES_ALRAUNE, SPECIES_WEREBEAST, SPECIES_SHADEKIN, SPECIES_SHADEKIN_CREW, SPECIES_ALTEVIAN, SPECIES_LLEILL, SPECIES_HANNER, SPECIES_ZADDAT, SPECIES_SPARKLE)
+ var/list/species_allowed = list(SPECIES_HUMAN, SPECIES_SKRELL, SPECIES_UNATHI, SPECIES_TAJARAN, SPECIES_TESHARI, SPECIES_NEVREAN, SPECIES_AKULA, SPECIES_SERGAL, SPECIES_FENNEC, SPECIES_ZORREN_HIGH, SPECIES_VULPKANIN, SPECIES_XENOCHIMERA, SPECIES_XENOHYBRID, SPECIES_VASILISSAN, SPECIES_RAPALA, SPECIES_PROTEAN, SPECIES_ALRAUNE, SPECIES_WEREBEAST, SPECIES_SHADEKIN, SPECIES_SHADEKIN_CREW, SPECIES_ALTEVIAN, SPECIES_LLEILL, SPECIES_HANNER, SPECIES_ZADDAT, SPECIES_SPARKLE, SPECIES_PROMETHEAN)
// Whether or not the accessory can be affected by colouration
var/do_colouration = 1
diff --git a/code/modules/mob/new_player/sprite_accessories_ear.dm b/code/modules/mob/new_player/sprite_accessories_ear.dm
index e25e6cd28a..ccf20f9046 100644
--- a/code/modules/mob/new_player/sprite_accessories_ear.dm
+++ b/code/modules/mob/new_player/sprite_accessories_ear.dm
@@ -15,7 +15,7 @@
var/extra_overlay2
var/desc = DEVELOPER_WARNING_NAME
em_block = TRUE
- species_allowed = list(SPECIES_HUMAN, SPECIES_SKRELL, SPECIES_UNATHI, SPECIES_TAJARAN, SPECIES_TESHARI, SPECIES_NEVREAN, SPECIES_AKULA, SPECIES_SERGAL, SPECIES_FENNEC, SPECIES_ZORREN_HIGH, SPECIES_VULPKANIN, SPECIES_XENOCHIMERA, SPECIES_XENOHYBRID, SPECIES_VASILISSAN, SPECIES_RAPALA, SPECIES_PROTEAN, SPECIES_ALRAUNE, SPECIES_WEREBEAST, SPECIES_SHADEKIN, SPECIES_SHADEKIN_CREW, SPECIES_ALTEVIAN, SPECIES_LLEILL, SPECIES_HANNER, SPECIES_SPARKLE)
+ species_allowed = list(SPECIES_HUMAN, SPECIES_SKRELL, SPECIES_UNATHI, SPECIES_TAJARAN, SPECIES_TESHARI, SPECIES_NEVREAN, SPECIES_AKULA, SPECIES_SERGAL, SPECIES_FENNEC, SPECIES_ZORREN_HIGH, SPECIES_VULPKANIN, SPECIES_XENOCHIMERA, SPECIES_XENOHYBRID, SPECIES_VASILISSAN, SPECIES_RAPALA, SPECIES_PROTEAN, SPECIES_ALRAUNE, SPECIES_WEREBEAST, SPECIES_SHADEKIN, SPECIES_SHADEKIN_CREW, SPECIES_ALTEVIAN, SPECIES_LLEILL, SPECIES_HANNER, SPECIES_SPARKLE, SPECIES_PROMETHEAN)
/**
* Gets the number of color channels we have.
diff --git a/code/modules/mob/new_player/sprite_accessories_wing.dm b/code/modules/mob/new_player/sprite_accessories_wing.dm
index 5210bc4fb3..b374f77826 100644
--- a/code/modules/mob/new_player/sprite_accessories_wing.dm
+++ b/code/modules/mob/new_player/sprite_accessories_wing.dm
@@ -21,7 +21,7 @@
var/extra_overlay_w // Flapping state for extra overlay
var/extra_overlay2_w
- species_allowed = list(SPECIES_HUMAN, SPECIES_SKRELL, SPECIES_UNATHI, SPECIES_TAJARAN, SPECIES_TESHARI, SPECIES_NEVREAN, SPECIES_AKULA, SPECIES_SERGAL, SPECIES_FENNEC, SPECIES_ZORREN_HIGH, SPECIES_VULPKANIN, SPECIES_XENOCHIMERA, SPECIES_XENOHYBRID, SPECIES_VASILISSAN, SPECIES_RAPALA, SPECIES_PROTEAN, SPECIES_ALRAUNE, SPECIES_WEREBEAST, SPECIES_SHADEKIN, SPECIES_SHADEKIN_CREW, SPECIES_ALTEVIAN, SPECIES_LLEILL, SPECIES_HANNER, SPECIES_SPARKLE)
+ species_allowed = list(SPECIES_HUMAN, SPECIES_SKRELL, SPECIES_UNATHI, SPECIES_TAJARAN, SPECIES_TESHARI, SPECIES_NEVREAN, SPECIES_AKULA, SPECIES_SERGAL, SPECIES_FENNEC, SPECIES_ZORREN_HIGH, SPECIES_VULPKANIN, SPECIES_XENOCHIMERA, SPECIES_XENOHYBRID, SPECIES_VASILISSAN, SPECIES_RAPALA, SPECIES_PROTEAN, SPECIES_ALRAUNE, SPECIES_WEREBEAST, SPECIES_SHADEKIN, SPECIES_SHADEKIN_CREW, SPECIES_ALTEVIAN, SPECIES_LLEILL, SPECIES_HANNER, SPECIES_SPARKLE, SPECIES_PROMETHEAN)
var/wing_offset = 0
var/multi_dir = FALSE // Does it use different sprites at different layers? _front will be added for sprites on low layer, _back to high layer
diff --git a/code/modules/mob/say_vr.dm b/code/modules/mob/say_vr.dm
index 4b33061d11..6cc54fbf57 100644
--- a/code/modules/mob/say_vr.dm
+++ b/code/modules/mob/say_vr.dm
@@ -514,3 +514,5 @@
var/new_speech_bubble = tgui_input_list(src, "Pick new voice (default for automatic selection)", "Character Preference", GLOB.selectable_speech_bubbles)
if(new_speech_bubble)
custom_speech_bubble = new_speech_bubble
+ if(dna)
+ dna.custom_speech_bubble = new_speech_bubble
diff --git a/code/modules/organs/internal/brain.dm b/code/modules/organs/internal/brain.dm
index f97a3f0927..421bdeb036 100644
--- a/code/modules/organs/internal/brain.dm
+++ b/code/modules/organs/internal/brain.dm
@@ -216,6 +216,9 @@ GLOBAL_LIST_BOILERPLATE(all_brain_organs, /obj/item/organ/internal/brain)
..()
/obj/item/organ/internal/brain/slime/proc/reviveBody()
+ // TODO - Reference how xenochimera component handles revival from bodyrecord in the future.
+ // This requires a promie/protean component for transformation and regeneration.
+ // This shouldn't use a brain mob for caching dna. That's what BRs are for.
var/datum/dna2/record/R = new /datum/dna2/record()
qdel_swap(R.dna, brainmob.dna.Clone())
R.ckey = brainmob.ckey
@@ -293,8 +296,10 @@ GLOBAL_LIST_BOILERPLATE(all_brain_organs, /obj/item/organ/internal/brain)
for(var/datum/language/L in R.languages)
H.add_language(L.name)
H.flavor_texts = R.flavor.Copy()
- qdel(R.dna)
- qdel(R)
+
+ SEND_SIGNAL(H, COMSIG_HUMAN_DNA_FINALIZED)
+
+ qdel(R) // Record already deletes dna
qdel(src)
return 1
diff --git a/code/modules/reagents/reactions/instant/instant_ch.dm b/code/modules/reagents/reactions/instant/instant_ch.dm
index 9b07554e7a..2c0f7fca11 100644
--- a/code/modules/reagents/reactions/instant/instant_ch.dm
+++ b/code/modules/reagents/reactions/instant/instant_ch.dm
@@ -151,9 +151,9 @@
if(comp.revive_ready >= 1) // if it's not reviving, start doing so
comp.revive_ready = REVIVING_READY // overrides the normal cooldown
H.visible_message(span_info("[H] shudders briefly, then relaxes, faint movements stirring within."))
- H.chimera_regenerate()
+ comp.chimera_regenerate()
else if(comp.revive_ready == REVIVING_DONE)// already reviving, check if they're ready to hatch
- H.chimera_hatch()
+ comp.chimera_hatch()
H.visible_message(span_danger(span_huge("[H] violently convulses and then bursts open, revealing a new, intact copy in the pool of viscera."))) // Hope you were wearing waterproofs, doc...
H.adjustBrainLoss(10) // they're reviving from dead, so take 10 brainloss
else //they're already reviving but haven't hatched. Give a little message to tell them to wait.
diff --git a/code/modules/resleeving/autoresleever.dm b/code/modules/resleeving/autoresleever.dm
index 92d3aa3eb5..49f904b3d5 100644
--- a/code/modules/resleeving/autoresleever.dm
+++ b/code/modules/resleeving/autoresleever.dm
@@ -188,6 +188,8 @@
if(def_lang)
new_character.default_language = def_lang
+ SEND_SIGNAL(new_character, COMSIG_HUMAN_DNA_FINALIZED)
+
//If desired, apply equipment.
if(equip_body)
if(charjob)
diff --git a/code/modules/resleeving/computers.dm b/code/modules/resleeving/computers.dm
index 0ec0973fdb..5626c87fd6 100644
--- a/code/modules/resleeving/computers.dm
+++ b/code/modules/resleeving/computers.dm
@@ -148,16 +148,17 @@
var/list/clonepods = list()
for(var/obj/machinery/clonepod/transhuman/pod in pods)
var/status = "idle"
+ var/mob/living/occupant = pod.get_occupant()
if(pod.mess)
status = "mess"
- else if(pod.occupant && !(pod.stat & NOPOWER))
+ else if(occupant && !(pod.stat & NOPOWER))
status = "cloning"
clonepods += list(list(
"pod" = REF(pod),
"name" = sanitize(capitalize(pod.name)),
"biomass" = pod.get_biomass(),
"status" = status,
- "progress" = (pod.occupant && pod.occupant.stat != DEAD) ? pod.get_completion() : 0
+ "progress" = (occupant && occupant.stat != DEAD) ? pod.get_completion() : 0
))
data["pods"] = clonepods
@@ -177,8 +178,8 @@
resleevers += list(list(
"sleever" = REF(resleever),
"name" = sanitize(capitalize(resleever.name)),
- "occupied" = !!resleever.occupant,
- "occupant" = resleever.occupant ? resleever.occupant.real_name : "None"
+ "occupied" = !!resleever.get_occupant(),
+ "occupant" = resleever.get_occupant() ? resleever.get_occupant().real_name : "None"
))
data["sleevers"] = resleevers
@@ -322,7 +323,7 @@
return
//Already doing someone.
- if(pod.occupant)
+ if(pod.get_occupant())
set_temp("Error: Growpod is currently occupied.", "danger")
active_br = null
return
@@ -375,26 +376,26 @@
switch(mode)
if(1) //Body resleeving
//No body to sleeve into.
- if(!sleever.occupant)
+ if(!sleever.get_occupant())
set_temp("Error: Resleeving pod is not occupied.", "danger")
active_mr = null
return
//OOC body lock thing.
- if(sleever.occupant.resleeve_lock && active_mr.ckey != sleever.occupant.resleeve_lock)
+ if(sleever.get_occupant().resleeve_lock && active_mr.ckey != sleever.get_occupant().resleeve_lock)
set_temp("Error: Mind incompatible with body.", "danger")
active_mr = null
return
var/list/subtargets = list()
- for(var/mob/living/carbon/human/H in sleever.occupant)
+ for(var/mob/living/carbon/human/H in sleever.get_occupant())
if(H.resleeve_lock && active_mr.ckey != H.resleeve_lock)
continue
subtargets += H
if(subtargets.len)
- var/oc_sanity = sleever.occupant
+ var/oc_sanity = sleever.get_occupant()
override = tgui_input_list(ui.user,"Multiple bodies detected. Select target for resleeving of [active_mr.mindname] manually. Sleeving of primary body is unsafe with sub-contents, and is not listed.", "Resleeving Target", subtargets)
- if(!override || oc_sanity != sleever.occupant || !(override in sleever.occupant))
+ if(!override || oc_sanity != sleever.get_occupant() || !(override in sleever.get_occupant()))
set_temp("Error: Target selection aborted.", "danger")
active_mr = null
return
@@ -558,7 +559,7 @@
if(!selected_sleever)
can_sleeve_active = FALSE
set_temp("Error: Cannot sleeve due to no selected sleever.", "danger")
- if(selected_sleever && !selected_sleever.occupant)
+ if(selected_sleever && !selected_sleever.get_occupant())
can_sleeve_active = FALSE
set_temp("Error: Cannot sleeve due to lack of sleever occupant.", "danger")
else
diff --git a/code/modules/resleeving/designer.dm b/code/modules/resleeving/designer.dm
index 4736e68952..96b39af2a3 100644
--- a/code/modules/resleeving/designer.dm
+++ b/code/modules/resleeving/designer.dm
@@ -59,6 +59,7 @@
if(!designer_gui)
designer_gui = new(src, null)
designer_gui.linked_body_design_console = WEAKREF(src)
+ CallAsync(designer_gui, TYPE_PROC_REF(/datum/tgui_module/appearance_changer,jiggle_map))
if(!designer_gui.owner)
designer_gui.make_fake_owner()
selected_record = FALSE
diff --git a/code/modules/resleeving/infocore_records.dm b/code/modules/resleeving/infocore_records.dm
index 30b905fd76..44778f995a 100644
--- a/code/modules/resleeving/infocore_records.dm
+++ b/code/modules/resleeving/infocore_records.dm
@@ -134,6 +134,11 @@
speciesname = M.custom_species ? M.custom_species : null
bodygender = M.gender
body_oocnotes = M.ooc_notes
+ body_ooclikes = M.ooc_notes_likes
+ body_oocdislikes = M.ooc_notes_dislikes
+ body_oocfavs = M.ooc_notes_favs
+ body_oocmaybes = M.ooc_notes_maybes
+ body_oocstyle = M.ooc_notes_style
sizemult = M.size_multiplier
weight = M.weight
aflags = M.appearance_flags
@@ -204,7 +209,6 @@
if(add_to_db)
SStranscore.add_body(src, database_key = database_key)
-
/**
* Make a deep copy of this record so it can be saved on a disk without modifications
* to the original affecting the copy.
@@ -214,28 +218,195 @@
/datum/transhuman/body_record/proc/init_from_br(var/datum/transhuman/body_record/orig)
ASSERT(!QDELETED(orig))
ASSERT(istype(orig))
- src.mydna = new ()
- qdel_swap(src.mydna.dna, orig.mydna.dna.Clone())
- src.mydna.ckey = orig.mydna.ckey
- src.mydna.id = orig.mydna.id
- src.mydna.name = orig.mydna.name
- src.mydna.types = orig.mydna.types
- src.mydna.flavor = orig.mydna.flavor.Copy()
- src.ckey = orig.ckey
- src.locked = orig.locked
- src.client_ref = orig.client_ref
- src.mind_ref = orig.mind_ref
- src.synthetic = orig.synthetic
- src.speciesname = orig.speciesname
- src.bodygender = orig.bodygender
- src.body_oocnotes = orig.body_oocnotes
- src.body_ooclikes = orig.body_ooclikes
- src.body_oocdislikes = orig.body_oocdislikes
- src.limb_data = orig.limb_data.Copy()
- src.organ_data = orig.organ_data.Copy()
- src.genetic_modifiers = orig.genetic_modifiers.Copy()
- src.toocomplex = orig.toocomplex
- src.sizemult = orig.sizemult
- src.aflags = orig.aflags
- src.breath_type = orig.breath_type
- src.weight = orig.weight
+ for(var/A in vars)
+ switch(A)
+ if(BLACKLISTED_COPY_VARS)
+ continue
+ if("mydna")
+ mydna = orig.mydna.copy()
+ continue
+ if(islist(vars[A]))
+ var/list/L = orig.vars[A]
+ vars[A] = L.Copy()
+ continue
+ vars[A] = orig.vars[A]
+
+/**
+ * Spawning a body was once left entirely up to the machine doing it, but bodies are massivley complex
+ * objects, and doing it this way lead to huge amounts of copypasted code to do the same thing.
+ * If you want to spawn a body from a BR, please use these...
+ */
+
+/// The core of resleeving, creates a mob based on the current record
+/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_virgoOOC(H)
+ internal_producebody_misc(H)
+ 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.name = H.real_name
+ 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)
+ if(locked && !force_unlock)
+ if(ckey)
+ H.resleeve_lock = ckey
+ else
+ // 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)
+
+ //Fix the external organs
+ for(var/part in limb_data)
+ var/status = limb_data[part]
+ if(status == null) continue //Species doesn't have limb? Child of amputated limb?
+
+ var/obj/item/organ/external/O = H.organs_by_name[part]
+ if(!O) continue //Not an organ. Perhaps another amputation removed it already.
+
+ if(status == 1) //Normal limbs
+ continue
+ else if(status == 0) //Missing limbs
+ O.remove_rejuv()
+ else if(status) //Anything else is a manufacturer
+ if(!is_synthfab)
+ O.remove_rejuv() //Don't robotize them, leave them removed so robotics can attach a part.
+ else
+ O.robotize(status)
+
+ //Then the internal organs
+ for(var/part in organ_data)
+ var/status = organ_data[part]
+ if(status == null) continue //Species doesn't have organ? Child of missing part?
+
+ var/obj/item/organ/I = H.internal_organs_by_name[part]
+ if(!I) continue//Not an organ. Perhaps external conversion changed it already?
+
+ if(status == 0) //Normal organ
+ continue
+ else if(status == 1) //Assisted organ
+ I.mechassist()
+ else if(status == 2) //Mechanical organ
+ I.robotize()
+ 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
+
+ //Apply genetic modifiers, synths don't use these
+ if(!is_synthfab)
+ for(var/modifier_type in mydna.genetic_modifiers)
+ H.add_modifier(modifier_type)
+
+ //Update appearance, remake icons
+ H.UpdateAppearance()
+ H.sync_dna_traits(FALSE) // Traitgenes Sync traits to genetics if needed
+ H.sync_organ_dna()
+ H.regenerate_icons()
+ H.initialize_vessel()
+
+/// Transfers VORE related information cached in the mob
+/datum/transhuman/body_record/proc/internal_producebody_virgoOOC(var/mob/living/carbon/human/H)
+ SHOULD_NOT_OVERRIDE(TRUE)
+ PRIVATE_PROC(TRUE)
+ H.ooc_notes = body_oocnotes
+ H.ooc_notes_likes = body_ooclikes
+ H.ooc_notes_dislikes = body_oocdislikes
+ H.ooc_notes_favs = body_oocfavs
+ H.ooc_notes_maybes = body_oocmaybes
+ H.ooc_notes_style = body_oocstyle
+
+/datum/transhuman/body_record/proc/internal_producebody_misc(var/mob/living/carbon/human/H)
+ SHOULD_NOT_OVERRIDE(TRUE)
+ PRIVATE_PROC(TRUE)
+ H.flavor_texts = mydna.flavor.Copy()
+ H.resize(sizemult, FALSE)
+ H.appearance_flags = aflags
+ H.weight = weight
+ if(speciesname)
+ H.custom_species = speciesname
+
+/**
+ * Specialty revival procs. Uses the BR for data, but needs to handle some weird logic for xenochi/slimes
+ */
+/datum/transhuman/body_record/proc/revive_xenochimera(var/mob/living/carbon/human/H,var/heal_robot_limbs,var/from_save_slot)
+ // 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
+
+ // Don't unlock unwilling xenochi!
+ internal_producebody_handlesleevelock(H,FALSE)
+
+ // Reset our organs/limbs.
+ H.species.create_organs(H)
+ internal_producebody_updatelimbandorgans(H, heal_robot_limbs)
+
+ //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 species.produceCopy() applies them during updatednastate(). Relevant here as genetraits may not match prior dna!
+ 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,FALSE)
+ internal_producebody_virgoOOC(H) // Is this needed?
+ internal_producebody_misc(H)
+
+ // Begin actual REVIVIAL. Do NOT use revive(). That uses client prefs and allows save hacking.
+ H.revival_healing_action()
+
+ // Update record from vanity copy of slot if needed
+ if(from_save_slot)
+ H.client.prefs.vanity_copy_to(H,FALSE,TRUE,TRUE,FALSE)
+ for(var/category in H.all_underwear) // No undies
+ H.hide_underwear[category] = TRUE
+ H.update_underwear()
+
+ return H
+
+/datum/transhuman/body_record/proc/revive_promethean(var/mob/living/carbon/human/H)
+ // TODO - See note in code\modules\organs\internal\brain.dm for slime brains
+ return
diff --git a/code/modules/resleeving/machines.dm b/code/modules/resleeving/machines.dm
index d1c9021270..4291f02287 100644
--- a/code/modules/resleeving/machines.dm
+++ b/code/modules/resleeving/machines.dm
@@ -29,86 +29,9 @@
remove_biomass(CLONE_BIOMASS)
//Get the DNA and generate a new mob
- var/datum/dna2/record/R = current_project.mydna
- var/mob/living/carbon/human/H = new /mob/living/carbon/human(src, R.dna.species)
- if(current_project.locked)
- H.resleeve_lock = current_project.ckey //CHOMPAdd, keep the lock
- /*CHOMPRemove Start
- if(current_project.ckey)
- H.resleeve_lock = current_project.ckey
- else
- // Ensure even body scans without an attached ckey respect locking
- H.resleeve_lock = "@badckey"
- *///CHOMPRemove End
+ var/mob/living/carbon/human/H = current_project.produce_human_mob(src,FALSE,FALSE,"clone ([rand(0,999)])")
+ SEND_SIGNAL(H, COMSIG_HUMAN_DNA_FINALIZED)
- //Fix the external organs
- for(var/part in current_project.limb_data)
-
- var/status = current_project.limb_data[part]
- if(status == null) continue //Species doesn't have limb? Child of amputated limb?
-
- var/obj/item/organ/external/O = H.organs_by_name[part]
- if(!O) continue //Not an organ. Perhaps another amputation removed it already.
-
- if(status == 1) //Normal limbs
- continue
- else if(status == 0) //Missing limbs
- O.remove_rejuv()
- else if(status) //Anything else is a manufacturer
- O.remove_rejuv() //Don't robotize them, leave them removed so robotics can attach a part.
-
- //Look, this machine can do this because [reasons] okay?!
- for(var/part in current_project.organ_data)
-
- var/status = current_project.organ_data[part]
- if(status == null) continue //Species doesn't have organ? Child of missing part?
-
- var/obj/item/organ/I = H.internal_organs_by_name[part]
- if(!I) continue//Not an organ. Perhaps external conversion changed it already?
-
- if(status == 0) //Normal organ
- continue
- else if(status == 1) //Assisted organ
- I.mechassist()
- else if(status == 2) //Mechanical organ
- I.robotize()
- else if(status == 3) //Digital organ
- I.digitize()
-
-
- occupant = H
-
- //Set the name or generate one
- if(!R.dna.real_name)
- R.dna.real_name = "clone ([rand(0,999)])"
- H.real_name = R.dna.real_name
-
- //Apply DNA
- qdel_swap(H.dna, R.dna.Clone())
- H.original_player = current_project.ckey
-
- //Apply genetic modifiers
- for(var/modifier_type in R.genetic_modifiers)
- H.add_modifier(modifier_type)
-
- //Apply legs
- H.digitigrade = R.dna.digitigrade // ensure clone mob has digitigrade var set appropriately
- if(H.dna.digitigrade <> R.dna.digitigrade)
- H.dna.digitigrade = R.dna.digitigrade // ensure cloned DNA is set appropriately from record??? for some reason it doesn't get set right despite the override to datum/dna/Clone()
-
- //Apply damage
- H.adjustCloneLoss((H.getMaxHealth() - (H.getMaxHealth()))*-0.75)
- H.Paralyse(4)
- H.updatehealth()
-
- //Update appearance, remake icons
- H.UpdateAppearance()
- H.sync_dna_traits(FALSE) // Traitgenes Sync traits to genetics if needed
- H.sync_organ_dna()
- H.regenerate_icons()
- H.initialize_vessel()
-
- // Traitgenes Moved breathing equipment to AFTER the genes set it
//Give breathing equipment if needed
if(current_project.breath_type != null && current_project.breath_type != GAS_O2)
H.equip_to_slot_or_del(new /obj/item/clothing/mask/breath(H), slot_wear_mask)
@@ -124,25 +47,11 @@
if(istype(H.internal,/obj/item/tank) && H.internals)
H.internals.icon_state = "internal1"
- //Basically all the VORE stuff
- H.ooc_notes = current_project.body_oocnotes
- H.ooc_notes_likes = current_project.body_ooclikes
- H.ooc_notes_dislikes = current_project.body_oocdislikes
- H.ooc_notes_favs = current_project.body_oocfavs
- H.ooc_notes_maybes = current_project.body_oocmaybes
- H.ooc_notes_style = current_project.body_oocstyle
- H.flavor_texts = current_project.mydna.flavor.Copy()
- H.resize(current_project.sizemult, FALSE)
- H.appearance_flags = current_project.aflags
- H.weight = current_project.weight
- if(current_project.speciesname)
- H.custom_species = current_project.speciesname
-
- //Suiciding var
- H.suiciding = 0
-
- //Making double-sure this is not set
- H.mind = null
+ //Apply damage
+ set_occupant(H)
+ H.adjustCloneLoss((H.getMaxHealth() - (H.getMaxHealth()))*-0.75)
+ H.Paralyse(4)
+ H.updatehealth()
//Machine specific stuff at the end
update_icon()
@@ -150,6 +59,7 @@
return 1
/obj/machinery/clonepod/transhuman/process()
+ var/mob/living/occupant = get_occupant()
if(stat & NOPOWER)
if(occupant)
locked = 0
@@ -190,7 +100,7 @@
return
else if((!occupant) || (occupant.loc != src))
- occupant = null
+ set_occupant(null)
if(locked)
locked = 0
update_icon()
@@ -199,13 +109,14 @@
return
/obj/machinery/clonepod/transhuman/get_completion()
+ var/mob/living/occupant = get_occupant()
if(occupant)
return 100 * ((occupant.health + (occupant.getMaxHealth()))) / (occupant.getMaxHealth() + abs(occupant.getMaxHealth()))
return 0
/obj/machinery/clonepod/transhuman/examine(mob/user, infix, suffix)
. = ..()
- if(occupant)
+ if(get_occupant())
var/completion = get_completion()
. += "Progress: [round(completion)]% [chat_progress_bar(round(completion), TRUE)]"
@@ -298,103 +209,16 @@
return
//Get the DNA and generate a new mob
- var/datum/dna2/record/R = current_project.mydna
- var/mob/living/carbon/human/H = new /mob/living/carbon/human(src, R.dna.species)
- if(current_project.locked)
- H.resleeve_lock = current_project.ckey //CHOMPAdd, keep the lock
- /*CHOMPRemove Start
- if(current_project.ckey)
- H.resleeve_lock = current_project.ckey
- else
- // Ensure even body scans without an attached ckey respect locking
- H.resleeve_lock = "@badckey"
- *///CHOMPRemove End
-
- //Fix the external organs
- for(var/part in current_project.limb_data)
-
- var/status = current_project.limb_data[part]
- if(status == null) continue //Species doesn't have limb? Child of amputated limb?
-
- var/obj/item/organ/external/O = H.organs_by_name[part]
- if(!O) continue //Not an organ. Perhaps another amputation removed it already.
-
- if(status == 1) //Normal limbs
- continue
- else if(status == 0) //Missing limbs
- O.remove_rejuv()
- else if(status) //Anything else is a manufacturer
- O.robotize(status)
-
- //Then the internal organs
- for(var/part in current_project.organ_data)
-
- var/status = current_project.organ_data[part]
- if(status == null) continue //Species doesn't have organ? Child of missing part?
-
- var/obj/item/organ/I = H.internal_organs_by_name[part]
- if(!I) continue//Not an organ. Perhaps external conversion changed it already?
-
- if(status == 0) //Normal organ
- continue
- else if(status == 1) //Assisted organ
- I.mechassist()
- else if(status == 2) //Mechanical organ
- I.robotize()
- else if(status == 3) //Digital organ
- I.digitize()
-
- //Set the name or generate one
- if(!R.dna.real_name)
- R.dna.real_name = "synth ([rand(0,999)])"
- H.real_name = R.dna.real_name
-
- //Apply DNA
- qdel_swap(H.dna, R.dna.Clone())
- H.original_player = current_project.ckey
-
- //Apply legs
- H.digitigrade = R.dna.digitigrade // ensure clone mob has digitigrade var set appropriately
- if(H.dna.digitigrade <> R.dna.digitigrade)
- H.dna.digitigrade = R.dna.digitigrade // ensure cloned DNA is set appropriately from record??? for some reason it doesn't get set right despite the override to datum/dna/Clone()
+ var/mob/living/carbon/human/H = current_project.produce_human_mob(src,TRUE,FALSE,"synth ([rand(0,999)])")
+ SEND_SIGNAL(H, COMSIG_HUMAN_DNA_FINALIZED)
//Apply damage
H.adjustBruteLoss(brute_value)
H.adjustFireLoss(burn_value)
H.updatehealth()
- //Update appearance, remake icons
- H.UpdateAppearance()
- H.sync_dna_traits(FALSE) // Traitgenes Sync traits to genetics if needed
- H.sync_organ_dna()
- H.regenerate_icons()
- H.initialize_vessel()
-
- //Basically all the VORE stuff
- H.ooc_notes = current_project.body_oocnotes
- H.ooc_notes_likes = current_project.body_ooclikes
- H.ooc_notes_dislikes = current_project.body_oocdislikes
- //CHOMPEdit Start
- H.ooc_notes_favs = current_project.body_oocfavs
- H.ooc_notes_maybes = current_project.body_oocmaybes
- H.ooc_notes_style = current_project.body_oocstyle
- //CHOMPEdit End
- H.flavor_texts = current_project.mydna.flavor.Copy()
- H.resize(current_project.sizemult)
- H.appearance_flags = current_project.aflags
- H.weight = current_project.weight
- if(current_project.speciesname)
- H.custom_species = current_project.speciesname
-
- //Suiciding var
- H.suiciding = 0
-
- //Making double-sure this is not set
- H.mind = null
-
//Plonk them here.
- H.regenerate_icons()
- H.loc = get_turf(src)
+ H.forceMove(get_turf(src))
//Machine specific stuff at the end
stored_material[MAT_STEEL] -= body_cost
@@ -470,7 +294,7 @@
var/blur_amount
var/confuse_amount
- var/mob/living/carbon/human/occupant = null
+ VAR_PRIVATE/datum/weakref/weakref_occupant = null
var/connected = null
var/sleevecards = 2
@@ -487,6 +311,18 @@
RefreshParts()
update_icon()
+/obj/machinery/transhuman/resleever/proc/set_occupant(var/mob/living/carbon/human/H)
+ SHOULD_NOT_OVERRIDE(TRUE)
+ if(!H)
+ weakref_occupant = null
+ return
+ weakref_occupant = WEAKREF(H)
+
+/obj/machinery/transhuman/resleever/proc/get_occupant()
+ RETURN_TYPE(/mob/living/carbon/human)
+ SHOULD_NOT_OVERRIDE(TRUE)
+ return weakref_occupant?.resolve()
+
/obj/machinery/transhuman/resleever/RefreshParts()
var/scan_rating = 0
for(var/obj/item/stock_parts/scanning_module/SM in component_parts)
@@ -513,14 +349,15 @@
/obj/machinery/transhuman/resleever/tgui_data(mob/user)
var/list/data = list()
- data["occupied"] = !!occupant
- if(occupant)
- data["name"] = occupant.name
- data["health"] = occupant.health
- data["maxHealth"] = occupant.getMaxHealth()
- data["stat"] = occupant.stat
- data["mindStatus"] = !!occupant.mind
- data["mindName"] = occupant.mind?.name
+ var/mob/living/carbon/human/H = get_occupant()
+ data["occupied"] = !!H
+ if(H)
+ data["name"] = H.name
+ data["health"] = H.health
+ data["maxHealth"] = H.getMaxHealth()
+ data["stat"] = H.stat
+ data["mindStatus"] = !!H.mind
+ data["mindName"] = H.mind?.name
return data
/obj/machinery/transhuman/resleever/attackby(obj/item/W, mob/user)
@@ -583,6 +420,7 @@
add_fingerprint(user)
/obj/machinery/transhuman/resleever/proc/putmind(var/datum/transhuman/mind_record/MR, mode = 1, var/mob/living/carbon/human/override = null, var/db_key)
+ var/mob/living/carbon/human/occupant = get_occupant()
if((!occupant || !istype(occupant) || occupant.stat >= DEAD) && mode == 1)
return 0
@@ -665,13 +503,14 @@
return 1
/obj/machinery/transhuman/resleever/proc/go_out(var/mob/M)
- if(!( src.occupant ))
+ var/mob/living/carbon/human/occupant = get_occupant()
+ if(!occupant)
return
- if (src.occupant.client)
- src.occupant.client.eye = src.occupant.client.mob
- src.occupant.client.perspective = MOB_PERSPECTIVE
- src.occupant.loc = src.loc
- src.occupant = null
+ if (occupant.client)
+ occupant.client.eye = occupant.client.mob
+ occupant.client.perspective = MOB_PERSPECTIVE
+ occupant.forceMove(get_turf(src))
+ set_occupant(null)
icon_state = "implantchair"
return
@@ -679,7 +518,7 @@
if(!ishuman(M))
to_chat(usr, span_warning("\The [src] cannot hold this!"))
return
- if(src.occupant)
+ if(get_occupant())
to_chat(usr, span_warning("\The [src] is already occupied!"))
return
if(M.client)
@@ -687,7 +526,7 @@
M.client.eye = src
M.stop_pulling()
M.loc = src
- src.occupant = M
+ set_occupant(M)
src.add_fingerprint(usr)
icon_state = "implantchair_on"
return 1
diff --git a/code/modules/tgui/modules/appearance_changer.dm b/code/modules/tgui/modules/appearance_changer.dm
index 521a995252..3f528fc416 100644
--- a/code/modules/tgui/modules/appearance_changer.dm
+++ b/code/modules/tgui/modules/appearance_changer.dm
@@ -44,6 +44,7 @@
var/list/valid_wingstyles = list()
var/list/valid_gradstyles = list()
var/list/markings = null
+ var/cooldown //Anti-spam. If spammed, this can be REALLY laggy.
/datum/tgui_module/appearance_changer/New(
host,
@@ -60,7 +61,7 @@
cam_screen.name = "screen"
cam_screen.assigned_map = map_name
cam_screen.del_on_map_removal = FALSE
- cam_screen.screen_loc = "[map_name]:1,1"
+ cam_screen.screen_loc = "[map_name]:3:-32,3:-48"
cam_plane_masters = get_tgui_plane_masters()
@@ -83,11 +84,19 @@
whitelist = species_whitelist
blacklist = species_blacklist
+/datum/tgui_module/appearance_changer/proc/jiggle_map()
+ // Fix for weird byond bug, jiggles the map around a little
+ sleep(0.1 SECONDS)
+ cam_screen.screen_loc = "[map_name]:1,1"
+ sleep(0.1 SECONDS)
+ cam_screen.screen_loc = "[map_name]:3:-32,3:-48" // Align for larger icons and scales
+
/datum/tgui_module/appearance_changer/tgui_close(mob/user)
. = ..()
if(owner == user || !customize_usr)
close_ui()
UnregisterSignal(owner, COMSIG_OBSERVER_MOVED)
+ SEND_SIGNAL(owner, COMSIG_HUMAN_DNA_FINALIZED) // Update any components using our saved appearance
owner = null
last_camera_turf = null
cut_data()
@@ -101,6 +110,11 @@
/datum/tgui_module/appearance_changer/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state)
if(..())
return TRUE
+ if(cooldown > world.time)
+ to_chat(ui.user, span_warning("You are changing appearance too fast!"))
+ return FALSE
+ else
+ cooldown = world.time + 0.5 SECONDS
var/obj/machinery/computer/transhuman/designer/DC = null
var/datum/tgui_module/appearance_changer/body_designer/BD = null
@@ -435,9 +449,8 @@
if (owner.change_marking_color(mark_datum, marking_color))
return TRUE
if("rotate_view")
- if(can_change(owner, APPEARANCE_RACE))
- owner.set_dir(turn(owner.dir, 90))
- return TRUE
+ owner.set_dir(turn(owner.dir, 90))
+ return TRUE
if("rename")
if(owner)
var/raw_name = tgui_input_text(ui.user, "Choose the a name:", "Sleeve Name")
@@ -465,35 +478,30 @@
owner.custom_species = new_name
return TRUE
if("base_icon")
- if(owner.species.selects_bodytype == SELECTS_BODYTYPE_FALSE)
- var/datum/species/S = GLOB.all_species[owner.species.name]
- owner.species.base_species = S.base_species // Return to original form
- generate_data(ui.user, owner)
- changed_hook(APPEARANCECHANGER_CHANGED_RACE)
- return TRUE
- var/list/choices
- var/datum/species/S = GLOB.all_species[owner.species.name]
- if(S.selects_bodytype == SELECTS_BODYTYPE_SHAPESHIFTER)
- choices = S.get_valid_shapeshifter_forms()
- else if(S.selects_bodytype == SELECTS_BODYTYPE_CUSTOM)
- choices = GLOB.custom_species_bases
- var/new_species = tgui_input_list(ui.user, "Please select basic shape.", "Body Shape", choices)
- if(new_species && can_change(owner, APPEARANCE_RACE))
- owner.species.base_species = new_species
- owner.regenerate_icons()
- generate_data(ui.user, owner)
- changed_hook(APPEARANCECHANGER_CHANGED_RACE)
- return TRUE
- if("blood_reagent")
- var/new_blood_reagents = tgui_input_list(ui.user, "Please select blood restoration reagent:", "Character Preference", valid_bloodreagents)
- if(new_blood_reagents && can_change(owner, APPEARANCE_RACE))
- owner.dna.blood_reagents = new_blood_reagents
- changed_hook(APPEARANCECHANGER_CHANGED_RACE)
- return TRUE
+ if(can_change(owner, APPEARANCE_MISC))
+ var/new_species = tgui_input_list(ui.user, "Please select basic shape.", "Body Shape", GLOB.custom_species_bases)
+ if(new_species)
+ owner.species.base_species = new_species
+ owner.species.icobase = owner.species.get_icobase()
+ owner.species.deform = owner.species.get_icobase(get_deform = TRUE)
+ owner.species.vanity_base_fit = new_species
+ if(istype(owner.species, /datum/species/shapeshifter)) //TODO: See if this is still needed.
+ wrapped_species_by_ref["\ref[owner]"] = new_species
+ owner.regenerate_icons()
+ generate_data(ui.user, owner)
+ changed_hook(APPEARANCECHANGER_CHANGED_RACE)
+ return TRUE
+ if("blood_reagent") //you know, this feels REALLY odd to be able to change at will but WHATEVER, WE BALL.
+ if(can_change(owner, APPEARANCE_MISC))
+ var/new_blood_reagents = tgui_input_list(ui.user, "Please select blood restoration reagent:", "Character Preference", valid_bloodreagents)
+ if(new_blood_reagents)
+ owner.dna.blood_reagents = new_blood_reagents
+ changed_hook(APPEARANCECHANGER_CHANGED_RACE)
+ return TRUE
if("blood_color")
var/current = owner.species.blood_color ? owner.species.blood_color : "#A10808"
- var/blood_col = tgui_color_picker(ui.user, "Please select marking color", "Marking color", current)
- if(blood_col && can_change(owner, APPEARANCE_RACE))
+ var/blood_col = tgui_color_picker(ui.user, "Please select blood color", "Blood color", current)
+ if(blood_col && can_change(owner, APPEARANCE_MISC))
owner.dna.blood_color = blood_col
changed_hook(APPEARANCECHANGER_CHANGED_RACE)
return TRUE
@@ -501,9 +509,9 @@
var/new_weight = tgui_input_number(ui.user, "Choose tbe character's relative body weight.\n\
This measurement should be set relative to a normal 5'10'' person's body and not the actual size of the character.\n\
([WEIGHT_MIN]-[WEIGHT_MAX])", "Character Preference", null, WEIGHT_MAX, WEIGHT_MIN, round_value=FALSE)
- if(new_weight && can_change(owner, APPEARANCE_RACE))
+ if(new_weight && can_change(owner, APPEARANCE_MISC))
var/unit_of_measurement = tgui_alert(ui.user, "Is that number in pounds (lb) or kilograms (kg)?", "Confirmation", list("Pounds", "Kilograms"))
- if(unit_of_measurement && can_change(owner, APPEARANCE_RACE))
+ if(unit_of_measurement)
if(unit_of_measurement == "Pounds")
new_weight = round(text2num(new_weight),4)
if(unit_of_measurement == "Kilograms")
@@ -513,7 +521,7 @@
return TRUE
if("size_scale")
var/new_size = tgui_input_number(ui.user, "Choose size, ranging from [RESIZE_MINIMUM * 100]% to [RESIZE_MAXIMUM * 100]%", "Set Size", null, RESIZE_MAXIMUM * 100, RESIZE_MINIMUM * 100)
- if(new_size && ISINRANGE(new_size,RESIZE_MINIMUM * 100,RESIZE_MAXIMUM * 100) && can_change(owner, APPEARANCE_RACE))
+ if(new_size && ISINRANGE(new_size,RESIZE_MINIMUM * 100,RESIZE_MAXIMUM * 100) && can_change(owner, APPEARANCE_MISC))
owner.size_multiplier = new_size / 100
owner.update_transform(TRUE)
owner.regenerate_icons()
@@ -521,21 +529,21 @@
changed_hook(APPEARANCECHANGER_CHANGED_RACE)
return TRUE
if("scale_appearance")
- if(can_change(owner, APPEARANCE_RACE))
+ if(can_change(owner, APPEARANCE_MISC))
owner.dna.scale_appearance = !owner.dna.scale_appearance
owner.fuzzy = owner.dna.scale_appearance
owner.regenerate_icons()
owner.set_dir(owner.dir) // Causes a visual update for fuzzy/offset
return TRUE
if("offset_override")
- if(can_change(owner, APPEARANCE_RACE))
+ if(can_change(owner, APPEARANCE_MISC))
owner.dna.offset_override = !owner.dna.offset_override
owner.offset_override = owner.dna.offset_override
owner.regenerate_icons()
owner.set_dir(owner.dir) // Causes a visual update for fuzzy/offset
return TRUE
if("digitigrade")
- if(can_change(owner, APPEARANCE_RACE))
+ if(can_change(owner, APPEARANCE_MISC))
owner.dna.digitigrade = !owner.dna.digitigrade
owner.digitigrade = owner.dna.digitigrade
owner.regenerate_icons()
@@ -545,28 +553,37 @@
if("species_sound")
var/list/possible_species_sound_types = species_sound_map
var/choice = tgui_input_list(ui.user, "Which set of sounds would you like to use? (Cough, Sneeze, Scream, Pain, Gasp, Death)", "Species Sounds", possible_species_sound_types)
- if(choice && can_change(owner, APPEARANCE_RACE))
+ if(choice && can_change(owner, APPEARANCE_MISC))
owner.species.species_sounds = choice
return TRUE
if("flavor_text")
var/select_key = params["target"]
- if(select_key && can_change(owner, APPEARANCE_RACE))
+ if(select_key && can_change(owner, APPEARANCE_MISC))
if(select_key in owner.flavor_texts)
switch(select_key)
if("general")
var/msg = strip_html_simple(tgui_input_text(ui.user,"Give a general description of the character. This will be shown regardless of clothings. Put in \"!clear\" to make blank.","Flavor Text",html_decode(owner.flavor_texts[select_key]), multiline = TRUE, prevent_enter = TRUE))
- if(can_change(owner, APPEARANCE_RACE)) // allows empty to wipe flavor
+ if(can_change(owner, APPEARANCE_MISC)) // allows empty to wipe flavor
if(msg == "!clear")
msg = ""
owner.flavor_texts[select_key] = msg
return TRUE
else
var/msg = strip_html_simple(tgui_input_text(ui.user,"Set the flavor text for their [select_key]. Put in \"!clear\" to make blank.","Flavor Text",html_decode(owner.flavor_texts[select_key]), multiline = TRUE, prevent_enter = TRUE))
- if(can_change(owner, APPEARANCE_RACE)) // allows empty to wipe flavor
+ if(can_change(owner, APPEARANCE_MISC)) // allows empty to wipe flavor
if(msg == "!clear")
msg = ""
owner.flavor_texts[select_key] = msg
return TRUE
+ if("load_saveslot") //saveslot_load
+ if(can_change(owner, APPEARANCE_ALL_COSMETIC))
+ if(tgui_alert(owner, "Are you certain you wish to load the currently selected savefile?", "Load Savefile", list("No","Yes")) == "Yes")
+ if(owner && owner.client) //sanity
+ owner.client.prefs.vanity_copy_to(owner, FALSE, TRUE, FALSE, FALSE)
+ return TRUE
+ return TRUE
+ else
+ return TRUE
// ***********************************
// Body designer UI
// ***********************************
@@ -618,6 +635,7 @@
if(DC.disk.stored)
qdel_null(DC.disk.stored)
to_chat(ui.user,span_notice("\The [owner]'s bodyrecord was saved to the disk."))
+ owner.update_dna()
DC.disk.stored = new /datum/transhuman/body_record(owner, FALSE, FALSE) // Saves a COPY!
DC.disk.stored.locked = FALSE // remove lock
DC.disk.name = "[initial(DC.disk.name)] ([owner.real_name])"
@@ -660,6 +678,7 @@
// Open UI
ui = new(user, src, tgui_id, name)
ui.open()
+ CallAsync(src, PROC_REF(jiggle_map))
if(custom_state)
ui.set_state(custom_state)
update_active_camera_screen()
@@ -747,6 +766,7 @@
stock_bodyrecords_list_ui += N
data["stock_records"] = stock_bodyrecords_list_ui
data["change_race"] = can_change(owner, APPEARANCE_RACE)
+ data["change_misc"] = can_change(owner, APPEARANCE_MISC)
data["gender_id"] = can_change(owner, APPEARANCE_GENDER)
data["change_gender"] = can_change(owner, APPEARANCE_GENDER)
data["change_hair"] = can_change(owner, APPEARANCE_HAIR)
@@ -790,6 +810,8 @@
data["gender"] = owner.gender
data["gender_id"] = owner.identifying_gender //This is saved to your MIND.
data["change_race"] = can_change(owner, APPEARANCE_RACE)
+ data["saveslot_load"] = can_change(owner, APPEARANCE_ALL_COSMETIC)
+ data["change_misc"] = can_change(owner, APPEARANCE_MISC)
data["change_gender"] = can_change(owner, APPEARANCE_GENDER)
if(data["change_gender"])
@@ -873,7 +895,7 @@
local_skybox.cut_overlays()
/datum/tgui_module/appearance_changer/proc/update_dna(mob/living/carbon/human/target)
- if(target && (flags & APPEARANCE_UPDATE_DNA))
+ if(target)
target.update_dna()
/datum/tgui_module/appearance_changer/proc/can_change(mob/living/carbon/human/target, var/flag)
@@ -1038,7 +1060,7 @@
// *******************************************************
/datum/tgui_module/appearance_changer/cocoon
name ="Appearance Editor (Cocoon)"
- flags = APPEARANCE_ALL_HAIR | APPEARANCE_EYE_COLOR | APPEARANCE_SKIN
+ flags = APPEARANCE_ALL_COSMETIC
customize_usr = TRUE
/datum/tgui_module/appearance_changer/cocoon/tgui_status(mob/user, datum/tgui_state/state)
@@ -1051,7 +1073,7 @@
// *******************************************************
/datum/tgui_module/appearance_changer/superpower
name ="Appearance Editor (Superpower)"
- flags = APPEARANCE_ALL_HAIR | APPEARANCE_EYE_COLOR | APPEARANCE_SKIN
+ flags = APPEARANCE_ALL_COSMETIC
customize_usr = TRUE
/datum/tgui_module/appearance_changer/superpower/tgui_status(mob/user, datum/tgui_state/state)
@@ -1060,6 +1082,19 @@
return STATUS_CLOSE
return ..()
+// *******************************************************
+// Innate Species Transformation.
+// *******************************************************
+/datum/tgui_module/appearance_changer/innate
+ name ="Appearance Editor (Innate)"
+ flags = APPEARANCE_ALL_COSMETIC
+ customize_usr = TRUE
+
+/datum/tgui_module/appearance_changer/innate/tgui_status(mob/user, datum/tgui_state/state)
+ if(owner.stat != CONSCIOUS)
+ return STATUS_CLOSE
+ return ..()
+
// *******************************************************
// Body design console
// *******************************************************
@@ -1097,52 +1132,10 @@
if(owner)
UnregisterSignal(owner, COMSIG_OBSERVER_MOVED)
qdel_null(owner)
- //Get the DNA and generate a new mob
- var/datum/dna2/record/R = current_project.mydna
- owner = new /mob/living/carbon/human(src, R.dna.species)
- //Fix the external organs
- for(var/part in current_project.limb_data)
- var/status = current_project.limb_data[part]
- if(status == null) continue //Species doesn't have limb? Child of amputated limb?
- var/obj/item/organ/external/O = owner.organs_by_name[part]
- if(!O) continue //Not an organ. Perhaps another amputation removed it already.
- if(status == 1) //Normal limbs
- continue
- else if(status == 0) //Missing limbs
- O.remove_rejuv()
- else if(status) //Anything else is a manufacturer
- O.remove_rejuv() //Don't robotize them, leave them removed so robotics can attach a part.
- for(var/part in current_project.organ_data)
- var/status = current_project.organ_data[part]
- if(status == null) continue //Species doesn't have organ? Child of missing part?
- var/obj/item/organ/I = owner.internal_organs_by_name[part]
- if(!I) continue//Not an organ. Perhaps external conversion changed it already?
- if(status == 0) //Normal organ
- continue
- else if(status == 1) //Assisted organ
- I.mechassist()
- else if(status == 2) //Mechanical organ
- I.robotize()
- else if(status == 3) //Digital organ
- I.digitize()
- //Set the name or generate one
- owner.real_name = R.dna.real_name
- owner.name = owner.real_name
- //Apply DNA
- owner.dna = R.dna.Clone()
- owner.original_player = current_project.ckey
- //Apply legs
- owner.digitigrade = R.dna.digitigrade // ensure clone mob has digitigrade var set appropriately
- if(owner.dna.digitigrade <> R.dna.digitigrade)
- owner.dna.digitigrade = R.dna.digitigrade // ensure cloned DNA is set appropriately from record??? for some reason it doesn't get set right despite the override to datum/dna/Clone()
- //Update appearance, remake icons
- owner.UpdateAppearance()
- owner.sync_dna_traits(FALSE)
- owner.sync_organ_dna()
- owner.initialize_vessel()
- owner.dna.blood_reagents = R.dna.blood_reagents
- owner.dna.blood_color = R.dna.blood_color
- owner.regenerate_icons()
+ owner = current_project.produce_human_mob(src,FALSE,FALSE,"Designer [rand(999)]")
+ // Update some specifics from the current record
+ owner.dna.blood_reagents = current_project.mydna.dna.blood_reagents
+ owner.dna.blood_color = current_project.mydna.dna.blood_color
owner.flavor_texts = current_project.mydna.flavor.Copy()
owner.resize(current_project.sizemult, FALSE)
owner.appearance_flags = current_project.aflags
diff --git a/code/modules/vore/eating/inbelly_spawn.dm b/code/modules/vore/eating/inbelly_spawn.dm
index f7e6ac273b..b4a674c1c3 100644
--- a/code/modules/vore/eating/inbelly_spawn.dm
+++ b/code/modules/vore/eating/inbelly_spawn.dm
@@ -169,12 +169,12 @@ Please do not abuse this ability.
var/datum/language/keylang = GLOB.all_languages[prey.prefs.language_custom_keys[key]]
if(keylang)
new_character.language_keys[key] = keylang
- // VOREStation Add: Preferred Language Setting;
if(prey.prefs.preferred_language) // Do we have a preferred language?
var/datum/language/def_lang = GLOB.all_languages[prey.prefs.preferred_language]
if(def_lang)
new_character.default_language = def_lang
- // VOREStation Add End
+
+ SEND_SIGNAL(new_character, COMSIG_HUMAN_DNA_FINALIZED)
new_character.regenerate_icons()
diff --git a/tgui/packages/tgui/interfaces/AppearanceChanger/AppearanceChangerBody.tsx b/tgui/packages/tgui/interfaces/AppearanceChanger/AppearanceChangerBody.tsx
index 16ed991fc6..88e763f363 100644
--- a/tgui/packages/tgui/interfaces/AppearanceChanger/AppearanceChangerBody.tsx
+++ b/tgui/packages/tgui/interfaces/AppearanceChanger/AppearanceChangerBody.tsx
@@ -1,5 +1,5 @@
import { useBackend } from 'tgui/backend';
-import { Button, ColorBox, LabeledList, Section } from 'tgui-core/components';
+import { Button, LabeledList, Section } from 'tgui-core/components';
import type { Data, species } from './types';
@@ -35,65 +35,8 @@ export const AppearanceChangerSpecies = (props) => {
) : (
''
)}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
);
};
diff --git a/tgui/packages/tgui/interfaces/AppearanceChanger/AppearanceChangerMisc.tsx b/tgui/packages/tgui/interfaces/AppearanceChanger/AppearanceChangerMisc.tsx
new file mode 100644
index 0000000000..36e485017a
--- /dev/null
+++ b/tgui/packages/tgui/interfaces/AppearanceChanger/AppearanceChangerMisc.tsx
@@ -0,0 +1,97 @@
+import { useBackend } from 'tgui/backend';
+import {
+ Button,
+ ColorBox,
+ LabeledList,
+ Section,
+ Stack,
+} from 'tgui-core/components';
+
+import type { Data } from './types';
+
+export const AppearanceChangerMisc = (props) => {
+ const { act, data } = useBackend();
+ const { specimen } = data;
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
diff --git a/tgui/packages/tgui/interfaces/AppearanceChanger/constants.ts b/tgui/packages/tgui/interfaces/AppearanceChanger/constants.ts
index 86d040fa31..eac1806a8b 100644
--- a/tgui/packages/tgui/interfaces/AppearanceChanger/constants.ts
+++ b/tgui/packages/tgui/interfaces/AppearanceChanger/constants.ts
@@ -16,4 +16,5 @@ export const TAB_EARS2 = 7;
export const TAB_TAIL = 8;
export const TAB_WINGS = 9;
export const TAB_MARKINGS = 10;
+export const TAB_MISC = 11;
export const MARKINGS_PER_PAGE = 30;
diff --git a/tgui/packages/tgui/interfaces/AppearanceChanger/index.tsx b/tgui/packages/tgui/interfaces/AppearanceChanger/index.tsx
index 78b581621e..443187be8d 100644
--- a/tgui/packages/tgui/interfaces/AppearanceChanger/index.tsx
+++ b/tgui/packages/tgui/interfaces/AppearanceChanger/index.tsx
@@ -21,6 +21,7 @@ import { AppearanceChangerColors } from './AppearanceChangerColors';
import { AppearanceChangerFlavor } from './AppearanceChangerFlavor';
import { AppearanceChangerHeader } from './AppearanceChangerHeader';
import { AppearanceChangerMarkings } from './AppearanceChangerMarkings';
+import { AppearanceChangerMisc } from './AppearanceChangerMisc';
import {
AppearanceChangerHair,
AppearanceChangerParts,
@@ -34,6 +35,7 @@ import {
TAB_GENDER,
TAB_HAIR,
TAB_MARKINGS,
+ TAB_MISC,
TAB_RACE,
TAB_TAIL,
TAB_WINGS,
@@ -60,6 +62,7 @@ export const AppearanceChanger = (props) => {
wing_style,
wing_styles,
change_race,
+ change_misc,
change_gender,
change_eye_color,
change_skin_tone,
@@ -90,7 +93,7 @@ export const AppearanceChanger = (props) => {
) : (
);
- tab[TAB_FLAVOR] = change_race ? (
+ tab[TAB_FLAVOR] = change_misc ? (
) : (
@@ -180,6 +183,11 @@ export const AppearanceChanger = (props) => {
) : (
);
+ tab[TAB_MISC] = change_misc ? (
+
+ ) : (
+
+ );
let firstAccesibleTab = -1;
if (change_race) {
@@ -378,6 +386,14 @@ export const AppearanceChanger = (props) => {
>
) : null}
+ {change_misc ? (
+ setTabIndex(TAB_MISC)}
+ >
+ Misc
+
+ ) : null}
{tab[tabIndex]}
diff --git a/tgui/packages/tgui/interfaces/AppearanceChanger/types.ts b/tgui/packages/tgui/interfaces/AppearanceChanger/types.ts
index 70a536d51a..8e3373b944 100644
--- a/tgui/packages/tgui/interfaces/AppearanceChanger/types.ts
+++ b/tgui/packages/tgui/interfaces/AppearanceChanger/types.ts
@@ -17,6 +17,7 @@ export type Data = {
tail_styles: styles[];
markings: { marking_name: string; marking_color: string }[];
change_race: BooleanLike;
+ change_misc: BooleanLike;
change_gender: BooleanLike;
genders: genders;
id_genders: genders;
diff --git a/vorestation.dme b/vorestation.dme
index b2bcbe518c..ebad5cab0c 100644
--- a/vorestation.dme
+++ b/vorestation.dme
@@ -179,6 +179,7 @@
#include "code\__defines\typeids.dm"
#include "code\__defines\unit_tests.dm"
#include "code\__defines\update_icons.dm"
+#include "code\__defines\var_copy.dm"
#include "code\__defines\vchatlog.dm"
#include "code\__defines\verb_manager.dm"
#include "code\__defines\visualnet.dm"