[MIRROR] Dna, Bodyrecord, Xenochi Revive Refactor (#11038)

Co-authored-by: Will <7099514+Willburd@users.noreply.github.com>
Co-authored-by: Cameron Lennox <killer65311@gmail.com>
This commit is contained in:
CHOMPStation2StaffMirrorBot
2025-06-10 18:41:08 -07:00
committed by GitHub
parent 48b3e8f772
commit 75e167a92f
43 changed files with 1082 additions and 919 deletions

View File

@@ -593,6 +593,8 @@
#define COMSIG_HUMAN_DISARM_HIT "human_disarm_hit" #define COMSIG_HUMAN_DISARM_HIT "human_disarm_hit"
///Whenever EquipRanked is called, called after job is set ///Whenever EquipRanked is called, called after job is set
#define COMSIG_JOB_RECEIVED "job_received" #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 // /datum/species signals

View File

@@ -103,8 +103,8 @@ var/SMALLSIZEBLOCK = 0
#define DNA_UI_GENDER 14 #define DNA_UI_GENDER 14
#define DNA_UI_BEARD_STYLE 15 #define DNA_UI_BEARD_STYLE 15
#define DNA_UI_HAIR_STYLE 16 #define DNA_UI_HAIR_STYLE 16
#define DNA_UI_EAR_STYLE 17 // VOREStation snippet. #define DNA_UI_EAR_STYLE 17
#define DNA_UI_EAR_SECONDARY_STYLE 18 // VOREStation snippet. #define DNA_UI_EAR_SECONDARY_STYLE 18
#define DNA_UI_TAIL_STYLE 19 #define DNA_UI_TAIL_STYLE 19
#define DNA_UI_PLAYERSCALE 20 #define DNA_UI_PLAYERSCALE 20
#define DNA_UI_TAIL_R 21 #define DNA_UI_TAIL_R 21
@@ -152,9 +152,14 @@ var/SMALLSIZEBLOCK = 0
#define DNA_UI_WING2_B 57 #define DNA_UI_WING2_B 57
#define DNA_UI_WING3_R 58 #define DNA_UI_WING3_R 58
#define DNA_UI_WING3_G 59 #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_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. #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. // This leaves room for future expansion. This can be arbitrarily raised without worry if genes start to get crowded.

View File

@@ -96,17 +96,18 @@
#define ROBOT_NOTIFICATION_AI_SHELL 5 #define ROBOT_NOTIFICATION_AI_SHELL 5
// Appearance change flags // Appearance change flags
#define APPEARANCE_UPDATE_DNA 0x1 #define APPEARANCE_RACE 0x1
#define APPEARANCE_RACE (0x2|APPEARANCE_UPDATE_DNA) #define APPEARANCE_GENDER 0x2
#define APPEARANCE_GENDER (0x4|APPEARANCE_UPDATE_DNA) #define APPEARANCE_SKIN 0x4
#define APPEARANCE_SKIN 0x8 #define APPEARANCE_HAIR 0x8
#define APPEARANCE_HAIR 0x10 #define APPEARANCE_HAIR_COLOR 0x10
#define APPEARANCE_HAIR_COLOR 0x20 #define APPEARANCE_FACIAL_HAIR 0x20
#define APPEARANCE_FACIAL_HAIR 0x40 #define APPEARANCE_FACIAL_HAIR_COLOR 0x40
#define APPEARANCE_FACIAL_HAIR_COLOR 0x80 #define APPEARANCE_EYE_COLOR 0x80
#define APPEARANCE_EYE_COLOR 0x100
#define APPEARANCE_ALL_HAIR (APPEARANCE_HAIR|APPEARANCE_HAIR_COLOR|APPEARANCE_FACIAL_HAIR|APPEARANCE_FACIAL_HAIR_COLOR) #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 // Click cooldown
#define DEFAULT_ATTACK_COOLDOWN 8 //Default timeout for aggressive actions #define DEFAULT_ATTACK_COOLDOWN 8 //Default timeout for aggressive actions

View File

@@ -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"

View File

@@ -308,7 +308,9 @@ GLOBAL_LIST_EMPTY(mannequins)
*/ */
// Custom species icon bases // 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 var/list/whitelisted_icons = list(SPECIES_FENNEC,SPECIES_XENOHYBRID,SPECIES_VOX,SPECIES_SHADEKIN) //CHOMPedit
for(var/species_name in GLOB.playable_species) for(var/species_name in GLOB.playable_species)
if(species_name in blacklisted_icons) if(species_name in blacklisted_icons)

View File

@@ -861,22 +861,7 @@ Turf and target are seperate in case you want to teleport some distance from a t
else else
O=new original.type(locate(0,0,0)) O=new original.type(locate(0,0,0))
var/static/list/blacklisted_var_names = list( var/static/list/blacklisted_var_names = list(BLACKLISTED_COPY_VARS)
"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
)
if(perfectcopy) if(perfectcopy)
if((O) && (original)) if((O) && (original))
for(var/V in original.vars) for(var/V in original.vars)

View File

@@ -1,31 +1,43 @@
/datum/component/xenochimera /datum/component/xenochimera
var/laststress = 0 VAR_PRIVATE/laststress = 0
var/mob/living/carbon/human/owner VAR_PRIVATE/mob/living/carbon/human/owner
var/feral = 0 var/feral = 0
var/revive_ready = REVIVING_READY var/revive_ready = REVIVING_READY
var/revive_finished = FALSE 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_1.ogg',
'sound/effects/mob_effects/xenochimera/regen_2.ogg', 'sound/effects/mob_effects/xenochimera/regen_2.ogg',
'sound/effects/mob_effects/xenochimera/regen_4.ogg', 'sound/effects/mob_effects/xenochimera/regen_4.ogg',
'sound/effects/mob_effects/xenochimera/regen_3.ogg', 'sound/effects/mob_effects/xenochimera/regen_3.ogg',
'sound/effects/mob_effects/xenochimera/regen_5.ogg' 'sound/effects/mob_effects/xenochimera/regen_5.ogg'
) )
VAR_PRIVATE/datum/transhuman/body_record/revival_record
/datum/component/xenochimera/Initialize() /datum/component/xenochimera/Initialize()
if(!ishuman(parent)) if(!ishuman(parent))
return COMPONENT_INCOMPATIBLE return COMPONENT_INCOMPATIBLE
owner = parent owner = parent
RegisterSignal(owner, COMSIG_XENOCHIMERA_COMPONENT, PROC_REF(handle_comp)) 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) add_verb(owner, /mob/living/carbon/human/proc/reconstitute_form)
/datum/component/xenochimera/Destroy(force) /datum/component/xenochimera/Destroy(force)
UnregisterSignal(owner, COMSIG_XENOCHIMERA_COMPONENT) UnregisterSignal(owner, COMSIG_XENOCHIMERA_COMPONENT)
UnregisterSignal(owner, COMSIG_HUMAN_DNA_FINALIZED)
remove_verb(owner, /mob/living/carbon/human/proc/reconstitute_form) remove_verb(owner, /mob/living/carbon/human/proc/reconstitute_form)
qdel_null(revival_record)
owner = null 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() /datum/component/xenochimera/proc/handle_comp()
SIGNAL_HANDLER
if(QDELETED(owner)) if(QDELETED(owner))
return return
handle_feralness() handle_feralness()
@@ -42,6 +54,19 @@
if(!owner.lying) if(!owner.lying)
owner.lay_down() 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() /datum/component/xenochimera/proc/handle_feralness()
//first, calculate how stressed the chimera is //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. /mob/living/carbon/human/proc/reconstitute_form() //Scree's race ability.in exchange for: No cloning.
set name = "Reconstitute Form" set name = "Reconstitute Form"
set category = "Abilities.Xenochimera" set category = "Abilities.Xenochimera"
var/datum/component/xenochimera/xc = get_xenochimera_component()
if(!xc)
return
if(is_incorporeal()) if(is_incorporeal())
to_chat(src, "You cannot regenerate while incorporeal.") to_chat(src, "You cannot regenerate while incorporeal.")
return return
// Sanity is mostly handled in chimera_regenerate() // Sanity is mostly handled in chimera_regenerate()
if(stat == DEAD) 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") if(confirm == "Yes")
chimera_regenerate() xc.chimera_regenerate()
else if (quickcheckuninjured()) 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")) 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") if(confirm == "Yes")
chimera_regenerate() xc.chimera_regenerate()
else 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") if(confirm == "Yes")
chimera_regenerate() xc.chimera_regenerate()
/mob/living/carbon/human/proc/chimera_regenerate() /datum/component/xenochimera/proc/chimera_regenerate()
var/datum/component/xenochimera/xc = get_xenochimera_component() if(!owner)
if(!xc)
return return
//If they're already regenerating //If they're already regenerating
switch(xc.revive_ready) switch(revive_ready)
if(REVIVING_NOW) 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 return
if(REVIVING_DONE) 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 return
if(xc.revive_ready > world.time) if(revive_ready > world.time)
to_chat(src, "You can't use that ability again so soon!") to_chat(owner, "You can't use that ability again so soon!")
return 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 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 (quickcheckuninjured()) //if you're completely uninjured, then you get a speedymode - check health first for quickness if (owner.quickcheckuninjured()) //if you're completely uninjured, then you get a speedymode - check health first for quickness
time = 30 time = 30
//Clicked regen while dead. //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 //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 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. //Scary spawnerization.
xc.revive_ready = REVIVING_NOW set_revival_delay(time)
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. owner.throw_alert("regen", /obj/screen/alert/xenochimera/reconstitution)
throw_alert("regen", /obj/screen/alert/xenochimera/reconstitution)
addtimer(CALLBACK(src, PROC_REF(chimera_regenerate_ready)), time SECONDS, TIMER_DELETE_ME) addtimer(CALLBACK(src, PROC_REF(chimera_regenerate_ready)), time SECONDS, TIMER_DELETE_ME)
//Clicked regen while NOT dead //Clicked regen while NOT dead
else 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 //Waiting for regen after being alive
xc.revive_ready = REVIVING_NOW set_revival_delay(time)
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. owner.throw_alert("regen", /obj/screen/alert/xenochimera/reconstitution)
throw_alert("regen", /obj/screen/alert/xenochimera/reconstitution)
addtimer(CALLBACK(src, PROC_REF(chimera_regenerate_nutrition)), time SECONDS, TIMER_DELETE_ME) 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() /datum/component/xenochimera/proc/chimera_regenerate_nutrition()
var/datum/component/xenochimera/xc = get_xenochimera_component() if(!owner)
if(!xc)
return return
//Slightly different flavour messages //Slightly different flavour messages
if(stat != DEAD || hasnutriment()) if(owner.stat != DEAD || owner.hasnutriment())
to_chat(src, span_notice("Consciousness begins to stir as your new body awakens, ready to hatch..")) to_chat(owner, span_notice("Consciousness begins to stir as your new body awakens, ready to hatch.."))
else else
to_chat(src, span_warning("Consciousness begins to stir as your battered body struggles to recover from its ordeal..")) to_chat(owner, 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) add_verb(owner, /mob/living/carbon/human/proc/hatch)
xc.revive_ready = REVIVING_DONE revive_ready = REVIVING_DONE
src << sound('sound/effects/mob_effects/xenochimera/hatch_notification.ogg',0,0,0,30) owner << sound('sound/effects/mob_effects/xenochimera/hatch_notification.ogg',0,0,0,30)
clear_alert("regen") owner.clear_alert("regen")
throw_alert("hatch", /obj/screen/alert/xenochimera/readytohatch) owner.throw_alert("hatch", /obj/screen/alert/xenochimera/readytohatch)
/mob/living/carbon/human/proc/chimera_regenerate_ready() /datum/component/xenochimera/proc/chimera_regenerate_ready()
var/datum/component/xenochimera/xc = get_xenochimera_component() if(!owner)
if(!xc)
return return
// check to see if they've been fixed by outside forces in the meantime such as defibbing // check to see if they've been fixed by outside forces in the meantime such as defibbing
if(stat != DEAD) if(owner.stat != DEAD)
to_chat(src, span_notice("Your body has recovered from its ordeal, ready to regenerate itself again.")) to_chat(owner, span_notice("Your body has recovered from its ordeal, ready to regenerate itself again."))
xc.revive_ready = REVIVING_READY //reset their cooldown revive_ready = REVIVING_READY //reset their cooldown
clear_alert("regen") owner.clear_alert("regen")
throw_alert("hatch", /obj/screen/alert/xenochimera/readytohatch) owner.throw_alert("hatch", /obj/screen/alert/xenochimera/readytohatch)
// Was dead, still dead. // Was dead, still dead.
else else
to_chat(src, span_notice("Consciousness begins to stir as your new body awakens, ready to hatch.")) to_chat(owner, span_notice("Consciousness begins to stir as your new body awakens, ready to hatch."))
add_verb(src, /mob/living/carbon/human/proc/hatch) add_verb(owner, /mob/living/carbon/human/proc/hatch)
xc.revive_ready = REVIVING_DONE revive_ready = REVIVING_DONE
src << sound('sound/effects/mob_effects/xenochimera/hatch_notification.ogg',0,0,0,30) owner << sound('sound/effects/mob_effects/xenochimera/hatch_notification.ogg',0,0,0,30)
clear_alert("regen") owner.clear_alert("regen")
throw_alert("hatch", /obj/screen/alert/xenochimera/readytohatch) owner.throw_alert("hatch", /obj/screen/alert/xenochimera/readytohatch)
/mob/living/carbon/human/proc/hatch() /mob/living/carbon/human/proc/hatch()
set name = "Hatch" set name = "Hatch"
@@ -359,71 +383,95 @@
if(!xc) if(!xc)
return return
if(xc.revive_ready != REVIVING_DONE) if(xc.revive_ready != REVIVING_DONE)
//Hwhat? return //Hwhat?
remove_verb(src, /mob/living/carbon/human/proc/hatch)
return
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") 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 //Dead when hatching
//var/sickness_duration = 10 MINUTES //CHOMPedit
var/has_braindamage = FALSE
if(stat == DEAD) 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 //Reviving from ded takes extra nutrition - if it isn't provided from outside sources, it comes from you
if(!hasnutriment()) if(!hasnutriment())
nutrition=nutrition * 0.75 nutrition=nutrition * 0.75
// sickness_duration = 20 MINUTES //CHOMPedit //sickness_duration = 20 MINUTES //CHOMPedit
chimera_hatch() has_braindamage = TRUE
// add_modifier(/datum/modifier/resleeving_sickness/chimera, sickness_duration) //CHOMPedit
// 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. adjustBrainLoss(5) // if they're reviving from dead, they come back with 5 brainloss on top of whatever's unhealed.
visible_message(span_warning("<p>" + span_huge("The former corpse staggers to its feet, all its former wounds having vanished...") + "</p>")) //Bloody hell...
clear_alert("hatch")
return
//Alive when hatching /datum/component/xenochimera/proc/chimera_hatch(var/from_save_slot)
else if(!owner)
chimera_hatch()
visible_message(span_warning("<p>" + span_huge("[src] rises to \his feet.") + "</p>")) //Bloody hell...
clear_alert("hatch")
/mob/living/carbon/human/proc/chimera_hatch()
var/datum/component/xenochimera/xc = get_xenochimera_component()
if(!xc)
return 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) //Modify and record values (half nutrition and braindamage)
var/old_nutrition = nutrition var/old_nutrition = owner.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/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=quickcheckuninjured() var/uninjured=owner.quickcheckuninjured()
//I did have special snowflake code, but this is easier. //It's also EXTREMELY BAD AND LETS THEM SAVEFILE HACK. trigger_revival(from_save_slot)
revive()
mutations.Remove(HUSK) owner.mutations.Remove(HUSK)
setBrainLoss(braindamage) owner.setBrainLoss(braindamage)
species.update_vore_belly_def_variant() owner.species.update_vore_belly_def_variant()
if(!uninjured) if(!uninjured)
nutrition = old_nutrition * 0.5 owner.nutrition = old_nutrition * 0.5
//Drop everything //Drop everything
for(var/obj/item/W in src) for(var/obj/item/W in owner)
drop_from_inventory(W) owner.drop_from_inventory(W)
//Visual effects //Visual effects
var/T = get_turf(src) var/T = get_turf(owner)
var/blood_color = species.blood_color var/blood_color = owner.species.blood_color
var/flesh_color = species.flesh_color var/flesh_color = owner.species.flesh_color
new /obj/effect/gibspawner/human/xenochimera(T, null, flesh_color, blood_color) new /obj/effect/gibspawner/human/xenochimera(T, null, flesh_color, blood_color)
visible_message(span_danger("<p>" + span_huge("The lifeless husk of [src] bursts open, revealing a new, intact copy in the pool of viscera.") + "</p>")) //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) playsound(T, 'sound/effects/mob_effects/xenochimera/hatch.ogg', 50)
else //lower cost for doing a quick cosmetic revive else //lower cost for doing a quick cosmetic revive
nutrition = old_nutrition * 0.9 owner.nutrition = old_nutrition * 0.9
//Unfreeze some things //Unfreeze some things
does_not_breathe = FALSE owner.does_not_breathe = FALSE
update_canmove() owner.update_canmove()
stunned = 2 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 /datum/modifier/resleeving_sickness/chimera //near identical to the regular version, just with different flavortexts
name = "imperfect regeneration" name = "imperfect regeneration"

View File

@@ -63,10 +63,6 @@ GLOBAL_LIST_EMPTY_TYPED(dna_genes_bad, /datum/gene/trait)
var/gender_specific_species_sounds = FALSE var/gender_specific_species_sounds = FALSE
var/species_sounds_male = "None" var/species_sounds_male = "None"
var/species_sounds_female = "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_say
var/custom_ask var/custom_ask
var/custom_whisper 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_heat = list()
var/list/custom_cold = list() var/list/custom_cold = list()
var/digitigrade = 0 //0, Not FALSE, for future use as indicator for digitigrade types var/digitigrade = 0 //0, Not FALSE, for future use as indicator for digitigrade types
var/custom_footstep = FOOTSTEP_MOB_SHOE
// New stuff // New stuff
var/species = SPECIES_HUMAN var/species = SPECIES_HUMAN
var/list/body_markings = list() 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. 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. // Make a copy of this strand.
// USE THIS WHEN COPYING STUFF OR YOU'LL GET CORRUPTION! // 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() /datum/dna/proc/Clone()
var/datum/dna/new_dna = new() var/datum/dna/new_dna = new()
new_dna.unique_enzymes=unique_enzymes for(var/A in vars)
new_dna.b_type=b_type switch(A)
new_dna.real_name=real_name if(BLACKLISTED_COPY_VARS)
new_dna.species=species continue
new_dna.body_markings=body_markings.Copy() if("dirtyUI")
new_dna.base_species=base_species dirtyUI=1
new_dna.custom_species=custom_species continue
new_dna.species_traits=species_traits.Copy() if("dirtySE")
new_dna.blood_color=blood_color dirtySE=1
new_dna.blood_reagents=blood_reagents continue
new_dna.scale_appearance = scale_appearance if("body_markings")
new_dna.offset_override = offset_override var/list/body_markings_genetic = body_markings.Copy()
new_dna.synth_markings = synth_markings body_markings_genetic -= body_marking_nopersist_list
new_dna.custom_speech_bubble = custom_speech_bubble new_dna.vars[A] = body_markings_genetic
new_dna.species_sounds = species_sounds continue
new_dna.gender_specific_species_sounds = gender_specific_species_sounds if(islist(vars[A]))
new_dna.species_sounds_male = species_sounds_male var/list/L = vars[A]
new_dna.species_sounds_female = species_sounds_female new_dna.vars[A] = L.Copy()
new_dna.grad_style = grad_style continue
new_dna.r_grad = r_grad new_dna.vars[A] = vars[A]
new_dna.g_grad = g_grad // Finish up by updating enzymes/identity from our UI/SEs
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]
new_dna.UpdateUI() new_dna.UpdateUI()
new_dna.UpdateSE() new_dna.UpdateSE()
return new_dna return new_dna
/////////////////////////////////////// ///////////////////////////////////////
// UNIQUE IDENTITY // UNIQUE IDENTITY
/////////////////////////////////////// ///////////////////////////////////////
@@ -138,6 +122,11 @@ GLOBAL_LIST_EMPTY_TYPED(dna_genes_bad, /datum/gene/trait)
if(!defer) if(!defer)
UpdateUI() 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) /datum/dna/proc/ResetUIFrom(var/mob/living/carbon/human/character)
// INITIALIZE! // INITIALIZE!
ResetUI(1) ResetUI(1)
@@ -174,6 +163,11 @@ GLOBAL_LIST_EMPTY_TYPED(dna_genes_bad, /datum/gene/trait)
if(character.wing_style) if(character.wing_style)
wing_style = wing_styles_list.Find(character.wing_style.type) 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) // Playerscale (This assumes list is sorted big->small)
var/size_multiplier = GLOB.player_sizes_list.len // If fail to find, take smallest var/size_multiplier = GLOB.player_sizes_list.len // If fail to find, take smallest
for(var/N in GLOB.player_sizes_list) 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.gender_specific_species_sounds = character.species.gender_specific_species_sounds
src.species_sounds_male = character.species.species_sounds_male src.species_sounds_male = character.species.species_sounds_male
src.species_sounds_female = character.species.species_sounds_female 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.species_traits = character.species.traits.Copy()
src.custom_say = character.custom_say src.custom_say = character.custom_say
src.custom_ask = character.custom_ask 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_heat = character.custom_heat
src.custom_cold = character.custom_cold src.custom_cold = character.custom_cold
src.digitigrade = character.digitigrade src.digitigrade = character.digitigrade
src.custom_footstep = character.custom_footstep
// +1 to account for the none-of-the-above possibility // +1 to account for the none-of-the-above possibility
SetUIValueRange(DNA_UI_EAR_STYLE, ear_style + 1, ear_styles_list.len + 1, 1) 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_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_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_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_R, character.r_tail, 255, 1)
SetUIValueRange(DNA_UI_TAIL_G, character.g_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_EARS3_B, character.b_ears3, 255, 1)
SetUIValueRange(DNA_UI_EARS_ALPHA,character.a_ears, 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) for(var/channel in 1 to DNA_UI_EARS_SECONDARY_COLOR_CHANNEL_COUNT)
var/offset = DNA_UI_EARS_SECONDARY_START + (channel - 1) * 3 var/offset = DNA_UI_EARS_SECONDARY_START + (channel - 1) * 3
var/list/read_rgb = ReadRGB(LAZYACCESS(character.ear_secondary_colors, channel) || "#ffffff") 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() 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. // Set a DNA UI block's raw value.
/datum/dna/proc/SetUIValue(var/block,var/value,var/defer=0) /datum/dna/proc/SetUIValue(var/block,var/value,var/defer=0)
if (block<=0) return if (block<=0) return

View File

@@ -132,172 +132,29 @@
return output return output
// Use mob.UpdateAppearance() // 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. // Simpler. Don't specify UI in order for the mob to use its own.
/mob/proc/UpdateAppearance(var/list/UI=null) /mob/living/carbon/human/UpdateAppearance(var/list/UI=null)
if(ishuman(src)) // Rebuild off UI arg if not null
if(UI!=null) if(UI!=null)
src.dna.UI=UI src.dna.UI=UI
src.dna.UpdateUI() 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)
H.r_facial = dna.GetUIValueRange(DNA_UI_BEARD_R, 255) // Setup dna
H.g_facial = dna.GetUIValueRange(DNA_UI_BEARD_G, 255) dna.check_integrity()
H.b_facial = dna.GetUIValueRange(DNA_UI_BEARD_B, 255) dna.ApplyToMob(src)
H.r_skin = dna.GetUIValueRange(DNA_UI_SKIN_R, 255) // Apply dna changes to organ icons
H.g_skin = dna.GetUIValueRange(DNA_UI_SKIN_G, 255) force_update_organs()
H.b_skin = dna.GetUIValueRange(DNA_UI_SKIN_B, 255) force_update_limbs()
H.r_eyes = dna.GetUIValueRange(DNA_UI_EYES_R, 255) //H.update_body(0) //Done in force_update_limbs already
H.g_eyes = dna.GetUIValueRange(DNA_UI_EYES_G, 255) update_eyes()
H.b_eyes = dna.GetUIValueRange(DNA_UI_EYES_B, 255) update_hair()
H.update_eyes()
H.s_tone = 35 - dna.GetUIValueRange(DNA_UI_SKIN_TONE, 220) // Value can be negative. return TRUE
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
/mob/living/carbon/human/proc/force_update_organs() /mob/living/carbon/human/proc/force_update_organs()
for(var/obj/item/organ/O as anything in organs + internal_organs) for(var/obj/item/organ/O as anything in organs + internal_organs)

View File

@@ -38,16 +38,21 @@
/datum/dna2/record/proc/copy() /datum/dna2/record/proc/copy()
var/datum/dna2/record/newrecord = new /datum/dna2/record var/datum/dna2/record/newrecord = new /datum/dna2/record
qdel_swap(newrecord.dna, dna.Clone()) for(var/A in vars)
newrecord.types = types switch(A)
newrecord.name = name if(BLACKLISTED_COPY_VARS)
newrecord.mind = mind continue
newrecord.ckey = ckey if("id")
newrecord.languages = languages newrecord.id = copytext(md5(dna.real_name), 2, 6) // update this specially
newrecord.implant = implant continue
newrecord.flavor = flavor if("dna")
newrecord.gender = gender qdel_swap(newrecord.dna, dna.Clone())
newrecord.genetic_modifiers = genetic_modifiers.Copy() continue
if(islist(vars[A]))
var/list/L = vars[A]
newrecord.vars[A] = L.Copy()
continue
newrecord.vars[A] = vars[A]
return newrecord return newrecord
@@ -65,7 +70,7 @@
interact_offline = 1 interact_offline = 1
circuit = /obj/item/circuitboard/clonescanner circuit = /obj/item/circuitboard/clonescanner
var/locked = 0 var/locked = 0
var/datum/weakref/occupant = null VAR_PRIVATE/datum/weakref/weakref_occupant = null
var/obj/item/reagent_containers/glass/beaker = null var/obj/item/reagent_containers/glass/beaker = null
var/opened = 0 var/opened = 0
var/damage_coeff var/damage_coeff
@@ -81,6 +86,18 @@
eject_occupant() 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() /obj/machinery/dna_scannernew/RefreshParts()
scan_level = 0 scan_level = 0
damage_coeff = 0 damage_coeff = 0
@@ -112,7 +129,7 @@
return return
/obj/machinery/dna_scannernew/proc/eject_occupant() /obj/machinery/dna_scannernew/proc/eject_occupant()
var/mob/living/carbon/WC = occupant?.resolve() var/mob/living/carbon/WC = get_occupant()
go_out() go_out()
for(var/obj/O in src) 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))) 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)) 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 /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) if(user.stat || user.lying || !Adjacent(user) || !target.Adjacent(user)|| !ishuman(target) || WC)
return return
// Traitgenes Do not allow buckled or ridden mobs // 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 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.")) to_chat(usr, span_notice("Try as you might, you can not climb up into the scanner."))
return return
if(occupant) var/mob/living/carbon/WC = get_occupant()
if(WC)
to_chat(usr, span_warning("The scanner is already occupied!")) to_chat(usr, span_warning("The scanner is already occupied!"))
return return
if(usr.abiotic()) if(usr.abiotic())
to_chat(usr, span_warning("The subject cannot have abiotic items on.")) to_chat(usr, span_warning("The subject cannot have abiotic items on."))
return return
var/mob/living/carbon/WC = occupant?.resolve()
if(WC) if(WC)
to_chat(usr, span_warning("There is already something inside.")) to_chat(usr, span_warning("There is already something inside."))
return return
@@ -158,7 +175,7 @@
usr.client.perspective = EYE_PERSPECTIVE usr.client.perspective = EYE_PERSPECTIVE
usr.client.eye = src usr.client.eye = src
usr.forceMove(src) usr.forceMove(src)
occupant = WEAKREF(usr) set_occupant(usr)
icon_state = "scanner_1" icon_state = "scanner_1"
add_fingerprint(usr) add_fingerprint(usr)
SStgui.update_uis(src) SStgui.update_uis(src)
@@ -182,7 +199,7 @@
return return
else if(istype(item, /obj/item/organ/internal/brain)) else if(istype(item, /obj/item/organ/internal/brain))
if(occupant) if(get_occupant())
to_chat(user, span_warning("The scanner is already occupied!")) to_chat(user, span_warning("The scanner is already occupied!"))
return return
var/obj/item/organ/internal/brain/brain = item var/obj/item/organ/internal/brain/brain = item
@@ -202,7 +219,7 @@
var/obj/item/grab/G = item var/obj/item/grab/G = item
if(!ismob(G.affecting)) if(!ismob(G.affecting))
return return
if(occupant) if(get_occupant())
to_chat(user, span_warning("The scanner is already occupied!")) to_chat(user, span_warning("The scanner is already occupied!"))
return return
if(G.affecting.abiotic()) if(G.affecting.abiotic())
@@ -219,10 +236,10 @@
if(beaker) if(beaker)
beaker.forceMove(get_turf(src)) beaker.forceMove(get_turf(src))
beaker = null beaker = null
if(occupant) var/mob/living/carbon/WC = get_occupant()
var/mob/living/carbon/WC = occupant.resolve() if(WC)
WC.forceMove(get_turf(src)) WC.forceMove(get_turf(src))
occupant = null set_occupant(null)
// Disconnect from our terminal // Disconnect from our terminal
for(var/dirfind in GLOB.cardinal) for(var/dirfind in GLOB.cardinal)
var/obj/machinery/computer/scan_consolenew/console = locate(/obj/machinery/computer/scan_consolenew, get_step(src, dirfind)) 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.perspective = EYE_PERSPECTIVE
M.client.eye = src M.client.eye = src
M.forceMove(src) M.forceMove(src)
occupant = WEAKREF(M) set_occupant(M)
icon_state = "scanner_1" icon_state = "scanner_1"
// search for ghosts, if the corpse is empty and the scanner is connected to a cloner // search for ghosts, if the corpse is empty and the scanner is connected to a cloner
@@ -254,9 +271,9 @@
SStgui.update_uis(src) SStgui.update_uis(src)
/obj/machinery/dna_scannernew/proc/go_out() /obj/machinery/dna_scannernew/proc/go_out()
if((!(occupant) || locked)) var/mob/living/carbon/WC = get_occupant()
if((!WC || locked))
return return
var/mob/living/carbon/WC = occupant.resolve()
if(WC.client) if(WC.client)
WC.client.eye = WC.client.mob WC.client.eye = WC.client.mob
WC.client.perspective = MOB_PERSPECTIVE WC.client.perspective = MOB_PERSPECTIVE
@@ -268,7 +285,7 @@
break break
else else
WC.forceMove(loc) WC.forceMove(loc)
occupant = null set_occupant(null)
icon_state = "scanner_0" icon_state = "scanner_0"
SStgui.update_uis(src) SStgui.update_uis(src)
@@ -395,7 +412,7 @@
tgui_interact(user) tgui_interact(user)
/obj/machinery/computer/scan_consolenew/tgui_interact(mob/user, datum/tgui/ui) /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) if(!connected || user == WC || user.stat)
return return
ui = SStgui.try_update_ui(user, src, ui) ui = SStgui.try_update_ui(user, src, ui)
@@ -408,7 +425,7 @@
var/data[0] var/data[0]
data["selectedMenuKey"] = selected_menu_key data["selectedMenuKey"] = selected_menu_key
data["locked"] = src.connected.locked data["locked"] = src.connected.locked
data["hasOccupant"] = connected.occupant ? 1 : 0 data["hasOccupant"] = connected.get_occupant() ? 1 : 0
data["isInjectorReady"] = injector_ready data["isInjectorReady"] = injector_ready
@@ -448,7 +465,7 @@
data["selectedUITargetHex"] = selected_ui_target_hex data["selectedUITargetHex"] = selected_ui_target_hex
var/occupantData[0] var/occupantData[0]
var/mob/living/carbon/WC = connected?.occupant?.resolve() var/mob/living/carbon/WC = connected?.get_occupant()
if(!WC || !WC.dna) if(!WC || !WC.dna)
occupantData["name"] = null occupantData["name"] = null
occupantData["stat"] = null occupantData["stat"] = null
@@ -522,7 +539,7 @@
return TRUE return TRUE
if("toggleLock") if("toggleLock")
playsound(src, 'sound/machines/button.ogg', 30, 1, 0) playsound(src, 'sound/machines/button.ogg', 30, 1, 0)
if(connected && connected.occupant) if(connected && connected.get_occupant())
connected.locked = !(connected.locked) connected.locked = !(connected.locked)
return TRUE return TRUE
@@ -541,9 +558,9 @@
return TRUE return TRUE
if("injectRejuvenators") if("injectRejuvenators")
playsound(src, 'sound/machines/button.ogg', 30, 1, 0) playsound(src, 'sound/machines/button.ogg', 30, 1, 0)
if(!connected.occupant || !connected.beaker) if(!connected.get_occupant() || !connected.beaker)
return TRUE 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 var/inject_amount = clamp(round(text2num(params["amount"]), 5), 0, 50) // round to nearest 5 and clamp to 0-50
if(!inject_amount) if(!inject_amount)
return TRUE return TRUE
@@ -561,9 +578,9 @@
selected_se_subblock = clamp(select_subblock, 1, DNA_BLOCK_SIZE) selected_se_subblock = clamp(select_subblock, 1, DNA_BLOCK_SIZE)
return TRUE return TRUE
if("pulseSERadiation") if("pulseSERadiation")
if(!connected?.occupant) if(!connected?.get_occupant())
return TRUE return TRUE
var/mob/living/carbon/WC = connected?.occupant?.resolve() var/mob/living/carbon/WC = connected?.get_occupant()
playsound(src, "keyboard", 40) playsound(src, "keyboard", 40)
var/block = WC.dna.GetSESubBlock(selected_se_block,selected_se_subblock) var/block = WC.dna.GetSESubBlock(selected_se_block,selected_se_subblock)
//var/original_block=block //var/original_block=block
@@ -604,7 +621,7 @@
// Traitgenes Moved SE and UI saves to storing the entire body record // Traitgenes Moved SE and UI saves to storing the entire body record
if("saveDNA") if("saveDNA")
playsound(src, "keyboard", 40) // into console 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) if(WC && WC.dna)
// Traitgenes Properly clone records // Traitgenes Properly clone records
var/datum/transhuman/body_record/databuf = new /datum/transhuman/body_record() 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) 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 return TRUE
if("transfer") 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) if(!WC || (NOCLONE in WC.mutations) || !WC.dna)
return TRUE return TRUE
irradiating = 2 irradiating = 2
@@ -673,7 +690,7 @@
playsound(src, "keyboard", 40) playsound(src, "keyboard", 40)
var/datum/transhuman/body_record/buf = buffers[bufferId] var/datum/transhuman/body_record/buf = buffers[bufferId]
// Send printable record to first sleevepod in area // Send printable record to first sleevepod in area
print_sleeve(usr, buf) print_sleeve(ui.user, buf)
return TRUE return TRUE
if("wipeDisk") if("wipeDisk")
@@ -774,12 +791,12 @@
to_chat(user, span_danger( "Error: Cannot grow synthetic.")) to_chat(user, span_danger( "Error: Cannot grow synthetic."))
return return
//No pods //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) if(!pod)
to_chat(user, span_danger( "Error: No growpods detected.")) to_chat(user, span_danger( "Error: No growpods detected."))
return return
//Already doing someone. //Already doing someone.
if(pod.occupant) if(pod.get_occupant())
to_chat(user, span_danger( "Error: Growpod is currently occupied.")) to_chat(user, span_danger( "Error: Growpod is currently occupied."))
return return
//Not enough materials. //Not enough materials.
@@ -805,7 +822,7 @@
to_chat(user, span_notice( "Initiating growing cycle...")) to_chat(user, span_notice( "Initiating growing cycle..."))
/obj/machinery/computer/scan_consolenew/proc/do_irradiate(var/lock_state, var/block) /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 irradiating = 0
connected.locked = lock_state connected.locked = lock_state
if(!WC) if(!WC)
@@ -840,7 +857,7 @@
WC.regenerate_icons() WC.regenerate_icons()
/obj/machinery/computer/scan_consolenew/proc/do_pulse(var/lock_state) /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 irradiating = 0
connected.locked = lock_state connected.locked = lock_state
@@ -872,7 +889,7 @@
playsound(src, "keyboard", 40) playsound(src, "keyboard", 40)
var/mob/living/carbon/WC = connected?.occupant?.resolve() var/mob/living/carbon/WC = connected?.get_occupant()
if(!WC) if(!WC)
return TRUE return TRUE
var/datum/transhuman/body_record/buf = buffers[bufferId] // Traitgenes- Use bodyrecords var/datum/transhuman/body_record/buf = buffers[bufferId] // Traitgenes- Use bodyrecords

View File

@@ -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.")) 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) absorbDNA(newDNA)
if(T.mind && T.mind.changeling) if(T.mind && T.mind.changeling)

View File

@@ -34,7 +34,7 @@
icon = 'icons/obj/cloning.dmi' icon = 'icons/obj/cloning.dmi'
icon_state = "pod_0" icon_state = "pod_0"
req_access = list(access_genetics) // For premature unlocking. 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_level = 20 // The clone is released once its health reaches this level.
var/heal_rate = 1 var/heal_rate = 1
var/locked = 0 var/locked = 0
@@ -54,12 +54,25 @@
default_apply_parts() default_apply_parts()
update_icon() 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) /obj/machinery/clonepod/attack_ai(mob/user as mob)
add_hiddenprint(user) add_hiddenprint(user)
return attack_hand(user) return attack_hand(user)
/obj/machinery/clonepod/attack_hand(mob/user as mob) /obj/machinery/clonepod/attack_hand(mob/user as mob)
var/mob/living/occupant = get_occupant()
if((isnull(occupant)) || (stat & NOPOWER)) if((isnull(occupant)) || (stat & NOPOWER))
return return
if((!isnull(occupant)) && (occupant.stat != 2)) if((!isnull(occupant)) && (occupant.stat != 2))
@@ -68,27 +81,27 @@
return return
//Start growing a human clone in the pod! //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) if(mess || attempting)
return 0 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 if(!istype(clonemind, /datum/mind)) //not a mind
return 0 return 0
if(clonemind.current && clonemind.current.stat != DEAD) //mind is associated with a non-dead body if(clonemind.current && clonemind.current.stat != DEAD) //mind is associated with a non-dead body
return 0 return 0
if(clonemind.active) //somebody is using that mind if(clonemind.active) //somebody is using that mind
if(ckey(clonemind.key) != R.ckey) if(ckey(clonemind.key) != BR.ckey)
return 0 return 0
else else
for(var/mob/observer/dead/G in player_list) for(var/mob/observer/dead/G in player_list)
if(G.ckey == R.ckey) if(G.ckey == BR.ckey)
if(G.can_reenter_corpse) if(G.can_reenter_corpse)
break break
else else
return 0 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)) if(istype(modifier_type, /datum/modifier/no_clone))
return 0 return 0
@@ -102,23 +115,20 @@
spawn(30) spawn(30)
eject_wait = 0 eject_wait = 0
var/mob/living/carbon/human/H = new /mob/living/carbon/human(src, R.dna.species) //Get the clone body ready, let's calculate their health so the pod doesn't immediately eject them!!!
occupant = H var/mob/living/carbon/human/H = BR.produce_human_mob(src,FALSE, FALSE, "clone ([rand(0,999)])")
SEND_SIGNAL(H, COMSIG_HUMAN_DNA_FINALIZED)
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 //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) H.Paralyse(4)
//Here let's calculate their health so the pod doesn't immediately eject them!!!
H.updatehealth() H.updatehealth()
H.set_cloned_appearance()
// Move mind to body along with key
clonemind.transfer_to(H) 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.") + "<br>" + span_bold(span_large("Your recent memories are fuzzy, and it's hard to remember anything from today...")) + \ to_chat(H, span_warning(span_bold("Consciousness slowly creeps over you as your body regenerates.") + "<br>" + span_bold(span_large("Your recent memories are fuzzy, and it's hard to remember anything from today...")) + \
"<br>" + span_notice(span_italics("So this is what cloning feels like?")))) "<br>" + span_notice(span_italics("So this is what cloning feels like?"))))
@@ -127,48 +137,29 @@
update_antag_icons(H.mind) update_antag_icons(H.mind)
// -- End mode specific stuff // -- 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. // 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_lower_bound = 25 MINUTES
var/modifier_upper_bound = 40 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) 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. 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_lower_bound = round(modifier_lower_bound * clone_sickness_length, 1)
modifier_upper_bound = round(modifier_upper_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)) 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) H.add_modifier(/datum/modifier/cloned)
// This is really stupid. // Finished!
for(var/modifier_type in R.genetic_modifiers) update_icon()
H.add_modifier(modifier_type) set_occupant(H)
for(var/datum/language/L in R.languages)
H.add_language(L.name)
H.flavor_texts = R.flavor.Copy()
H.suiciding = 0
attempting = 0 attempting = 0
return 1 return 1
//Grow clones to maturity then kick them out. FREELOADERS //Grow clones to maturity then kick them out. FREELOADERS
/obj/machinery/clonepod/process() /obj/machinery/clonepod/process()
var/mob/living/occupant = get_occupant()
if(stat & NOPOWER) //Autoeject if power is lost if(stat & NOPOWER) //Autoeject if power is lost
if(occupant) if(occupant)
locked = 0 locked = 0
@@ -210,7 +201,7 @@
return return
else if((!occupant) || (occupant.loc != src)) else if((!occupant) || (occupant.loc != src))
occupant = null set_occupant(null)
if(locked) if(locked)
locked = 0 locked = 0
return return
@@ -219,6 +210,7 @@
//Let's unlock this early I guess. Might be too early, needs tweaking. //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) /obj/machinery/clonepod/attackby(obj/item/W as obj, mob/user as mob)
var/mob/living/occupant = get_occupant()
if(isnull(occupant)) if(isnull(occupant))
if(default_deconstruction_screwdriver(user, W)) if(default_deconstruction_screwdriver(user, W))
return return
@@ -272,7 +264,7 @@
..() ..()
/obj/machinery/clonepod/emag_act(var/remaining_charges, var/mob/user) /obj/machinery/clonepod/emag_act(var/remaining_charges, var/mob/user)
if(isnull(occupant)) if(isnull(get_occupant()))
return return
to_chat(user, "You force an emergency ejection.") to_chat(user, "You force an emergency ejection.")
locked = 0 locked = 0
@@ -301,7 +293,7 @@
heal_level = max(min((efficiency * 15) + 10, 100), MINIMUM_HEAL_LEVEL) heal_level = max(min((efficiency * 15) + 10, 100), MINIMUM_HEAL_LEVEL)
/obj/machinery/clonepod/proc/get_completion() /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() /obj/machinery/clonepod/verb/eject()
set name = "Eject Cloner" set name = "Eject Cloner"
@@ -324,20 +316,21 @@
update_icon() update_icon()
return return
var/mob/living/occupant = get_occupant()
if(!(occupant)) if(!(occupant))
return return
if(occupant.client) if(occupant.client)
occupant.client.eye = occupant.client.mob occupant.client.eye = occupant.client.mob
occupant.client.perspective = MOB_PERSPECTIVE occupant.client.perspective = MOB_PERSPECTIVE
occupant.loc = src.loc occupant.forceMove(get_turf(src))
eject_wait = 0 //If it's still set somehow. eject_wait = 0 //If it's still set somehow.
if(ishuman(occupant)) //Need to be safe. if(ishuman(occupant)) //Need to be safe.
var/mob/living/carbon/human/patient = occupant 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. 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. domutcheck(occupant) //Waiting until they're out before possible transforming.
occupant.UpdateAppearance() occupant.UpdateAppearance()
occupant = null set_occupant(null)
update_icon() update_icon()
return return
@@ -400,6 +393,7 @@
return 0 return 0
/obj/machinery/clonepod/proc/malfunction() /obj/machinery/clonepod/proc/malfunction()
var/mob/living/occupant = get_occupant()
if(occupant) if(occupant)
connected_message("Critical Error!") connected_message("Critical Error!")
mess = 1 mess = 1
@@ -421,21 +415,21 @@
switch(severity) switch(severity)
if(1.0) if(1.0)
for(var/atom/movable/A as mob|obj in src) for(var/atom/movable/A as mob|obj in src)
A.loc = src.loc A.forceMove(get_turf(src))
ex_act(severity) ex_act(severity)
qdel(src) qdel(src)
return return
if(2.0) if(2.0)
if(prob(50)) if(prob(50))
for(var/atom/movable/A as mob|obj in src) for(var/atom/movable/A as mob|obj in src)
A.loc = src.loc A.forceMove(get_turf(src))
ex_act(severity) ex_act(severity)
qdel(src) qdel(src)
return return
if(3.0) if(3.0)
if(prob(25)) if(prob(25))
for(var/atom/movable/A as mob|obj in src) for(var/atom/movable/A as mob|obj in src)
A.loc = src.loc A.forceMove(get_turf(src))
ex_act(severity) ex_act(severity)
qdel(src) qdel(src)
return return
@@ -444,7 +438,7 @@
/obj/machinery/clonepod/update_icon() /obj/machinery/clonepod/update_icon()
..() ..()
icon_state = "pod_0" icon_state = "pod_0"
if(occupant && !(stat & NOPOWER)) if(get_occupant() && !(stat & NOPOWER))
icon_state = "pod_1" icon_state = "pod_1"
else if(mess) else if(mess)
icon_state = "pod_g" icon_state = "pod_g"

View File

@@ -14,7 +14,7 @@
var/list/scantemp = null var/list/scantemp = null
var/menu = MENU_MAIN //Which menu screen to display var/menu = MENU_MAIN //Which menu screen to display
var/list/records = null 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/obj/item/disk/body_record/diskette = null // Traitgenes - Storing the entire body record
var/loading = 0 // Nice loading text var/loading = 0 // Nice loading text
var/autoprocess = 0 var/autoprocess = 0
@@ -34,27 +34,26 @@
/obj/machinery/computer/cloning/Destroy() /obj/machinery/computer/cloning/Destroy()
releasecloner() releasecloner()
for(var/datum/dna2/record/R in records) for(var/datum/transhuman/body_record/BR in records)
qdel(R.dna) qdel(BR)
qdel(R)
return ..() return ..()
/obj/machinery/computer/cloning/process() /obj/machinery/computer/cloning/process()
if(!scanner || !pods.len || !autoprocess || stat & NOPOWER) if(!scanner || !pods.len || !autoprocess || stat & NOPOWER)
return return
if(scanner.occupant && can_autoprocess()) if(scanner.get_occupant() && can_autoprocess())
scan_mob(scanner.occupant) scan_mob(scanner.get_occupant())
if(!LAZYLEN(records)) if(!LAZYLEN(records))
return return
for(var/obj/machinery/clonepod/pod in pods) for(var/obj/machinery/clonepod/pod in pods)
if(!(pod.occupant || pod.mess) && (pod.efficiency > 5)) if(!(pod.get_occupant() || pod.mess) && (pod.efficiency > 5))
for(var/datum/dna2/record/R in records) for(var/datum/transhuman/body_record/BR in records)
if(!(pod.occupant || pod.mess)) if(!(pod.get_occupant() || pod.mess))
if(pod.growclone(R)) if(pod.growclone(BR))
records.Remove(R) records.Remove(BR)
/obj/machinery/computer/cloning/proc/updatemodules() /obj/machinery/computer/cloning/proc/updatemodules()
scanner = findscanner() scanner = findscanner()
@@ -67,8 +66,8 @@
var/obj/machinery/dna_scannernew/scannerf = null var/obj/machinery/dna_scannernew/scannerf = null
//Try to find scanner on adjacent tiles first //Try to find scanner on adjacent tiles first
for(dir in list(NORTH,EAST,SOUTH,WEST)) for(var/scan_dir in list(NORTH,EAST,SOUTH,WEST))
scannerf = locate(/obj/machinery/dna_scannernew, get_step(src, dir)) scannerf = locate(/obj/machinery/dna_scannernew, get_step(src, scan_dir))
if(scannerf) if(scannerf)
return scannerf return scannerf
@@ -97,7 +96,7 @@
if(istype(W, /obj/item/disk/body_record/)) //Traitgenes Storing the entire body record if(istype(W, /obj/item/disk/body_record/)) //Traitgenes Storing the entire body record
if(!diskette) if(!diskette)
user.drop_item() user.drop_item()
W.loc = src W.forceMove(src)
diskette = W diskette = W
to_chat(user, "You insert [W].") to_chat(user, "You insert [W].")
SStgui.update_uis(src) SStgui.update_uis(src)
@@ -154,17 +153,18 @@
if(pod.efficiency > 5) if(pod.efficiency > 5)
canpodautoprocess = 1 canpodautoprocess = 1
var/mob/living/occupant = pod.get_occupant()
var/status = "idle" var/status = "idle"
if(pod.mess) if(pod.mess)
status = "mess" status = "mess"
else if(pod.occupant && !(pod.stat & NOPOWER)) else if(occupant && !(pod.stat & NOPOWER))
status = "cloning" status = "cloning"
tempods.Add(list(list( tempods.Add(list(list(
"pod" = "\ref[pod]", "pod" = "\ref[pod]",
"name" = sanitize(capitalize(pod.name)), "name" = sanitize(capitalize(pod.name)),
"biomass" = pod.get_biomass(), "biomass" = pod.get_biomass(),
"status" = status, "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 data["pods"] = tempods
@@ -178,16 +178,16 @@
else else
data["autoallowed"] = 0 data["autoallowed"] = 0
if(scanner) if(scanner)
data["occupant"] = scanner.occupant data["occupant"] = scanner.get_occupant()
data["locked"] = scanner.locked data["locked"] = scanner.locked
data["temp"] = temp data["temp"] = temp
data["scantemp"] = scantemp data["scantemp"] = scantemp
data["disk"] = diskette data["disk"] = diskette
data["selected_pod"] = "\ref[selected_pod]" data["selected_pod"] = "\ref[selected_pod]"
var/list/temprecords[0] var/list/temprecords[0]
for(var/datum/dna2/record/R in records) for(var/datum/transhuman/body_record/BR in records)
var tempRealName = R.dna.real_name var tempRealName = BR.mydna.dna.real_name
temprecords.Add(list(list("record" = "\ref[R]", "realname" = sanitize(tempRealName)))) temprecords.Add(list(list("record" = "\ref[BR]", "realname" = sanitize(tempRealName))))
data["records"] = temprecords data["records"] = temprecords
if(selected_pod && (selected_pod in pods) && selected_pod.get_biomass() >= CLONE_BIOMASS) if(selected_pod && (selected_pod in pods) && selected_pod.get_biomass() >= CLONE_BIOMASS)
@@ -206,67 +206,68 @@
. = TRUE . = TRUE
switch(tgui_modal_act(src, action, params)) switch(tgui_modal_act(src, action, params))
if(TGUI_MODAL_ANSWER) 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() var/obj/item/card/id/C = ui.user.get_active_hand()
if(!istype(C) && !istype(C, /obj/item/pda)) if(!istype(C) && !istype(C, /obj/item/pda))
set_temp("ID not in hand.", "danger") set_temp("ID not in hand.", "danger")
return return
if(check_access(C)) if(check_access(C))
records.Remove(active_record) records.Remove(active_BR)
qdel(active_record.dna) qdel(active_BR) // Already deletes dna in destroy()
qdel(active_record)
set_temp("Record deleted.", "success") set_temp("Record deleted.", "success")
menu = MENU_RECORDS menu = MENU_RECORDS
else else
set_temp("Access denied.", "danger") set_temp("Access denied.", "danger")
return return
var/mob/living/carbon/human/scanner_occupant = scanner.get_occupant()
switch(action) switch(action)
if("scan") if("scan")
if(!scanner || !scanner.occupant || loading) if(!scanner || !scanner_occupant || loading)
return return
set_scan_temp("Scanner ready.", "good") set_scan_temp("Scanner ready.", "good")
loading = TRUE loading = TRUE
spawn(20) spawn(20)
if(can_brainscan() && scan_mode) if(can_brainscan() && scan_mode)
scan_mob(scanner.occupant, scan_brain = TRUE) scan_mob(scanner_occupant, scan_brain = TRUE)
else else
scan_mob(scanner.occupant) scan_mob(scanner_occupant)
loading = FALSE loading = FALSE
SStgui.update_uis(src) SStgui.update_uis(src)
if("autoprocess") if("autoprocess")
autoprocess = text2num(params["on"]) > 0 autoprocess = text2num(params["on"]) > 0
if("lock") if("lock")
if(isnull(scanner) || !scanner.occupant) //No locking an open scanner. if(isnull(scanner) || !scanner_occupant) //No locking an open scanner.
return return
scanner.locked = !scanner.locked scanner.locked = !scanner.locked
if("view_rec") if("view_rec")
var/ref = params["ref"] var/ref = params["ref"]
if(!length(ref)) if(!length(ref))
return return
active_record = locate(ref) active_BR = locate(ref)
if(istype(active_record)) if(istype(active_BR))
if(isnull(active_record.ckey)) if(isnull(active_BR.ckey))
qdel(active_record) qdel(active_BR)
set_temp("Error: Record corrupt.", "danger") set_temp("Error: Record corrupt.", "danger")
else else
var/obj/item/implant/health/H = null var/obj/item/implant/health/H = null
if(active_record.implant) if(active_BR.mydna.implant)
H = locate(active_record.implant) H = locate(active_BR.mydna.implant)
var/list/payload = list( var/list/payload = list(
activerecord = "\ref[active_record]", activerecord = "\ref[active_BR]",
health = (H && istype(H)) ? H.sensehealth() : "", health = (H && istype(H)) ? H.sensehealth() : "",
realname = sanitize(active_record.dna.real_name), realname = sanitize(active_BR.mydna.dna.real_name),
unidentity = active_record.dna.uni_identity, unidentity = active_BR.mydna.dna.uni_identity,
strucenzymes = active_record.dna.struc_enzymes, strucenzymes = active_BR.mydna.dna.struc_enzymes,
) )
tgui_modal_message(src, action, "", null, payload) tgui_modal_message(src, action, "", null, payload)
else else
active_record = null active_BR = null
set_temp("Error: Record missing.", "danger") set_temp("Error: Record missing.", "danger")
if("del_rec") if("del_rec")
if(!active_record) if(!active_BR)
return 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") 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. if("disk") // Disk management.
@@ -277,24 +278,24 @@
if(isnull(diskette) || isnull(diskette.stored)) // Traitgenes Storing the entire body record if(isnull(diskette) || isnull(diskette.stored)) // Traitgenes Storing the entire body record
set_temp("Error: The disk's data could not be read.", "danger") set_temp("Error: The disk's data could not be read.", "danger")
return return
else if(isnull(active_record)) else if(isnull(active_BR))
set_temp("Error: No active record was found.", "danger") set_temp("Error: No active record was found.", "danger")
menu = MENU_MAIN menu = MENU_MAIN
return 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") set_temp("Successfully loaded from disk.", "success")
if("save") 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") set_temp("Error: The data could not be saved.", "danger")
return return
diskette.stored.mydna = active_record // Traitgenes Storing the entire body record diskette.stored = new(active_BR) // Traitgenes Storing the entire body record
diskette.name = "data disk - '[active_record.dna.real_name]'" diskette.name = "data disk - '[active_BR.mydna.dna.real_name]'"
set_temp("Successfully saved to disk.", "success") set_temp("Successfully saved to disk.", "success")
if("eject") if("eject")
if(!isnull(diskette)) if(!isnull(diskette))
diskette.loc = loc diskette.forceMove(get_turf(src))
diskette = null diskette = null
if("refresh") if("refresh")
SStgui.update_uis(src) SStgui.update_uis(src)
@@ -309,7 +310,7 @@
var/ref = params["ref"] var/ref = params["ref"]
if(!length(ref)) if(!length(ref))
return return
var/datum/dna2/record/C = locate(ref) var/datum/transhuman/body_record/C = locate(ref)
//Look for that player! They better be dead! //Look for that player! They better be dead!
if(istype(C)) if(istype(C))
tgui_modal_clear(src) tgui_modal_clear(src)
@@ -321,7 +322,7 @@
var/cloneresult var/cloneresult
if(!selected_pod) if(!selected_pod)
set_temp("Error: No cloning pod selected.", "danger") 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") set_temp("Error: The cloning pod is currently occupied.", "danger")
else if(pod.get_biomass() < CLONE_BIOMASS) else if(pod.get_biomass() < CLONE_BIOMASS)
set_temp("Error: Not enough biomass.", "danger") set_temp("Error: Not enough biomass.", "danger")
@@ -335,7 +336,6 @@
set_temp("Initiating cloning cycle...", "success") set_temp("Initiating cloning cycle...", "success")
playsound(src, 'sound/machines/medbayscanner1.ogg', 100, 1) playsound(src, 'sound/machines/medbayscanner1.ogg', 100, 1)
records.Remove(C) records.Remove(C)
qdel(C.dna)
qdel(C) qdel(C)
menu = MENU_MAIN menu = MENU_MAIN
else else
@@ -407,49 +407,38 @@
return return
for(var/obj/machinery/clonepod/pod in pods) 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.") set_scan_temp("Subject already getting cloned.")
SStgui.update_uis(src) SStgui.update_uis(src)
return return
subject.dna.check_integrity() subject.dna.check_integrity()
var/datum/transhuman/body_record/BR = new(subject)
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)
//Add an implant if needed //Add an implant if needed
var/obj/item/implant/health/imp = locate(/obj/item/implant/health, subject) var/obj/item/implant/health/imp = locate(/obj/item/implant/health, subject)
if (isnull(imp)) if (isnull(imp))
imp = new /obj/item/implant/health(subject) imp = new /obj/item/implant/health(subject)
imp.implanted = subject imp.implanted = subject
R.implant = "\ref[imp]" BR.mydna.implant = "\ref[imp]"
//Update it if needed //Update it if needed
else else
R.implant = "\ref[imp]" BR.mydna.implant = "\ref[imp]"
if (!isnull(subject.mind)) //Save that mind so traitors can continue traitoring after cloning. 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") set_scan_temp("Subject successfully scanned.", "good")
SStgui.update_uis(src) SStgui.update_uis(src)
//Find a specific record by key. //Find a specific record by key.
/obj/machinery/computer/cloning/proc/find_record(var/find_key) /obj/machinery/computer/cloning/proc/find_record(var/find_key)
var/selected_record = null var/selected_record = null
for(var/datum/dna2/record/R in records) for(var/datum/transhuman/body_record/BR in records)
if(R.ckey == find_key) if(BR.mydna.ckey == find_key)
selected_record = R selected_record = BR
break break
return selected_record return selected_record

View File

@@ -271,6 +271,8 @@
if(def_lang) if(def_lang)
P.default_language = def_lang P.default_language = def_lang
SEND_SIGNAL(P, COMSIG_HUMAN_DNA_FINALIZED)
protean_brain.brainmob.mind.transfer_to(P) protean_brain.brainmob.mind.transfer_to(P)
protean_brain.loc = BR protean_brain.loc = BR
protean_refactory = null protean_refactory = null

View File

@@ -135,6 +135,8 @@
avatar.sync_organ_dna() avatar.sync_organ_dna()
avatar.initialize_vessel() 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) 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) if (newname)
avatar.real_name = newname avatar.real_name = newname

View File

@@ -314,6 +314,8 @@
avatar.sync_organ_dna() avatar.sync_organ_dna()
avatar.initialize_vessel() avatar.initialize_vessel()
SEND_SIGNAL(avatar, COMSIG_HUMAN_DNA_FINALIZED)
if(tf) 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. 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. if(isliving(new_form)) // Make sure the mob spawned properly.

View File

@@ -106,6 +106,8 @@
if(is_lang_whitelisted(usr,chosen_language) || (avatar.species && (chosen_language.name in avatar.species.secondary_langs))) if(is_lang_whitelisted(usr,chosen_language) || (avatar.species && (chosen_language.name in avatar.species.secondary_langs)))
avatar.add_language(lang) avatar.add_language(lang)
SEND_SIGNAL(avatar, COMSIG_HUMAN_DNA_FINALIZED)
avatar.regenerate_icons() avatar.regenerate_icons()
avatar.update_transform() avatar.update_transform()
job_master.EquipRank(avatar,JOB_VR, 1, FALSE) job_master.EquipRank(avatar,JOB_VR, 1, FALSE)

View File

@@ -257,6 +257,8 @@
if(is_lang_whitelisted(src,chosen_language) || (new_character.species && (chosen_language.name in new_character.species.secondary_langs))) if(is_lang_whitelisted(src,chosen_language) || (new_character.species && (chosen_language.name in new_character.species.secondary_langs)))
new_character.add_language(lang) new_character.add_language(lang)
SEND_SIGNAL(new_character, COMSIG_HUMAN_DNA_FINALIZED)
new_character.regenerate_icons() new_character.regenerate_icons()
new_character.update_transform() new_character.update_transform()

View File

@@ -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))) if(is_lang_whitelisted(src,chosen_language) || (new_character.species && (chosen_language.name in new_character.species.secondary_langs)))
new_character.add_language(lang) new_character.add_language(lang)
SEND_SIGNAL(new_character, COMSIG_HUMAN_DNA_FINALIZED)
//If desired, apply equipment. //If desired, apply equipment.
if(equipment) if(equipment)
if(charjob) if(charjob)

View File

@@ -533,7 +533,7 @@ var/list/preferences_datums = list()
else else
var/bodytype var/bodytype
var/datum/species/selected_species = GLOB.all_species[species] 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 bodytype = custom_base
else else
bodytype = selected_species.get_bodytype() bodytype = selected_species.get_bodytype()
@@ -544,7 +544,8 @@ var/list/preferences_datums = list()
for(var/N in character.organs_by_name) for(var/N in character.organs_by_name)
var/obj/item/organ/external/O = character.organs_by_name[N] var/obj/item/organ/external/O = character.organs_by_name[N]
O.markings.Cut() if(O)
O.markings.Cut()
var/priority = 0 var/priority = 0
for(var/M in body_markings) for(var/M in body_markings)
@@ -554,6 +555,7 @@ var/list/preferences_datums = list()
for(var/BP in mark_datum.body_parts) for(var/BP in mark_datum.body_parts)
var/obj/item/organ/external/O = character.organs_by_name[BP] var/obj/item/organ/external/O = character.organs_by_name[BP]
if(O) 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"]) 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 character.markings_len = priority
@@ -581,7 +583,6 @@ var/list/preferences_datums = list()
character.fuzzy = fuzzy character.fuzzy = fuzzy
character.offset_override = offset_override character.offset_override = offset_override
character.voice_freq = voice_freq character.voice_freq = voice_freq
character.size_multiplier = size_multiplier
character.resize(size_multiplier, animate = FALSE, ignore_prefs = TRUE) character.resize(size_multiplier, animate = FALSE, ignore_prefs = TRUE)
var/list/traits_to_copy = list(/datum/trait/neutral/tall, 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/datum/species/selected_species = GLOB.all_species[species]
var/bodytype_selected var/bodytype_selected
if(custom_base) if(selected_species.selects_bodytype && custom_base)
bodytype_selected = custom_base bodytype_selected = custom_base
else else
bodytype_selected = selected_species.get_bodytype(character) bodytype_selected = selected_species.get_bodytype(character)
character.dna.base_species = bodytype_selected character.dna.base_species = bodytype_selected
character.species.base_species = bodytype_selected character.species.base_species = bodytype_selected
character.species.icobase = character.species.get_icobase() character.species.icobase = character.species.get_icobase()
character.species.deform = character.species.get_icobase(get_deform = TRUE) character.species.deform = character.species.get_icobase(get_deform = TRUE)
character.species.vanity_base_fit = bodytype_selected 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 wrapped_species_by_ref["\ref[character]"] = bodytype_selected
character.custom_species = custom_species character.custom_species = custom_species

View File

@@ -801,11 +801,11 @@
//We REALLY don't need to go through every variable. Doing so makes this lag like hell on 515 //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) /datum/species/proc/copy_variables(var/datum/species/S, var/list/whitelist)
//List of variables to ignore, trying to copy type will runtime. //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. //Makes thorough copy of species datum.
for(var/i in whitelist) for(var/i in whitelist)
if(!(i in S.vars)) //Don't copy incompatible vars. //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 // continue
if(S.vars[i] != vars[i] && !islist(vars[i])) //If vars are same, no point in copying. if(S.vars[i] != vars[i] && !islist(vars[i])) //If vars are same, no point in copying.
S.vars[i] = vars[i] S.vars[i] = vars[i]

View File

@@ -109,16 +109,8 @@ var/datum/species/shapeshifter/promethean/prometheans
cold_discomfort_strings = list("You feel too cool.") cold_discomfort_strings = list("You feel too cool.")
inherent_verbs = list( inherent_verbs = list(
/mob/living/carbon/human/proc/shapeshifter_select_shape, /mob/living/carbon/human/proc/innate_shapeshifting,
/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/regenerate, /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/carbon/human/proc/prommie_blobform,
/mob/living/proc/set_size, /mob/living/proc/set_size,
/mob/living/carbon/human/proc/promethean_select_opaqueness, /mob/living/carbon/human/proc/promethean_select_opaqueness,
@@ -410,3 +402,9 @@ var/datum/species/shapeshifter/promethean/prometheans
return return
else else
prommie_intoblob() 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)

View File

@@ -679,7 +679,12 @@
return 1 return 1
return 0 return 0
/// Revives a body using the client's preferences if human
/mob/living/proc/revive() /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() rejuvenate()
if(buckled) if(buckled)
buckled.unbuckle_mob() 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. if(ai_holder) // AI gets told to sleep when killed. Since they're not dead anymore, wake it up.
ai_holder.go_wake() ai_holder.go_wake()
SEND_SIGNAL(src, COMSIG_HUMAN_DNA_FINALIZED)
/mob/living/proc/rejuvenate() /mob/living/proc/rejuvenate()
if(reagents) if(reagents)
reagents.clear_reagents() reagents.clear_reagents()

View File

@@ -447,15 +447,15 @@
var/datum/language/keylang = GLOB.all_languages[client.prefs.language_custom_keys[key]] var/datum/language/keylang = GLOB.all_languages[client.prefs.language_custom_keys[key]]
if(keylang) if(keylang)
new_character.language_keys[key] = keylang new_character.language_keys[key] = keylang
// VOREStation Add: Preferred Language Setting;
if(client.prefs.preferred_language) // Do we have a preferred language? if(client.prefs.preferred_language) // Do we have a preferred language?
var/datum/language/def_lang = GLOB.all_languages[client.prefs.preferred_language] var/datum/language/def_lang = GLOB.all_languages[client.prefs.preferred_language]
if(def_lang) if(def_lang)
new_character.default_language = def_lang new_character.default_language = def_lang
// VOREStation Add End
// And uncomment this, too. // And uncomment this, too.
//new_character.dna.UpdateSE() //new_character.dna.UpdateSE()
SEND_SIGNAL(new_character, COMSIG_HUMAN_DNA_FINALIZED)
// Do the initial caching of the player's body icons. // Do the initial caching of the player's body icons.
new_character.force_update_limbs() new_character.force_update_limbs()
new_character.update_icons_body() new_character.update_icons_body()

View File

@@ -37,7 +37,7 @@ GLOBAL_LIST_INIT(fancy_sprite_accessory_color_channel_names, list("Primary", "Se
var/gender = NEUTER var/gender = NEUTER
// Restrict some styles to specific species. Default to all species to avoid runtimes in character creator. // 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 // Whether or not the accessory can be affected by colouration
var/do_colouration = 1 var/do_colouration = 1

View File

@@ -15,7 +15,7 @@
var/extra_overlay2 var/extra_overlay2
var/desc = DEVELOPER_WARNING_NAME var/desc = DEVELOPER_WARNING_NAME
em_block = TRUE 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. * Gets the number of color channels we have.

View File

@@ -21,7 +21,7 @@
var/extra_overlay_w // Flapping state for extra overlay var/extra_overlay_w // Flapping state for extra overlay
var/extra_overlay2_w 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/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 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

View File

@@ -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) 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) if(new_speech_bubble)
custom_speech_bubble = new_speech_bubble custom_speech_bubble = new_speech_bubble
if(dna)
dna.custom_speech_bubble = new_speech_bubble

View File

@@ -216,6 +216,9 @@ GLOBAL_LIST_BOILERPLATE(all_brain_organs, /obj/item/organ/internal/brain)
..() ..()
/obj/item/organ/internal/brain/slime/proc/reviveBody() /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() var/datum/dna2/record/R = new /datum/dna2/record()
qdel_swap(R.dna, brainmob.dna.Clone()) qdel_swap(R.dna, brainmob.dna.Clone())
R.ckey = brainmob.ckey 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) for(var/datum/language/L in R.languages)
H.add_language(L.name) H.add_language(L.name)
H.flavor_texts = R.flavor.Copy() 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) qdel(src)
return 1 return 1

View File

@@ -151,9 +151,9 @@
if(comp.revive_ready >= 1) // if it's not reviving, start doing so if(comp.revive_ready >= 1) // if it's not reviving, start doing so
comp.revive_ready = REVIVING_READY // overrides the normal cooldown comp.revive_ready = REVIVING_READY // overrides the normal cooldown
H.visible_message(span_info("[H] shudders briefly, then relaxes, faint movements stirring within.")) 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 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.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 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. else //they're already reviving but haven't hatched. Give a little message to tell them to wait.

View File

@@ -188,6 +188,8 @@
if(def_lang) if(def_lang)
new_character.default_language = def_lang new_character.default_language = def_lang
SEND_SIGNAL(new_character, COMSIG_HUMAN_DNA_FINALIZED)
//If desired, apply equipment. //If desired, apply equipment.
if(equip_body) if(equip_body)
if(charjob) if(charjob)

View File

@@ -148,16 +148,17 @@
var/list/clonepods = list() var/list/clonepods = list()
for(var/obj/machinery/clonepod/transhuman/pod in pods) for(var/obj/machinery/clonepod/transhuman/pod in pods)
var/status = "idle" var/status = "idle"
var/mob/living/occupant = pod.get_occupant()
if(pod.mess) if(pod.mess)
status = "mess" status = "mess"
else if(pod.occupant && !(pod.stat & NOPOWER)) else if(occupant && !(pod.stat & NOPOWER))
status = "cloning" status = "cloning"
clonepods += list(list( clonepods += list(list(
"pod" = REF(pod), "pod" = REF(pod),
"name" = sanitize(capitalize(pod.name)), "name" = sanitize(capitalize(pod.name)),
"biomass" = pod.get_biomass(), "biomass" = pod.get_biomass(),
"status" = status, "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 data["pods"] = clonepods
@@ -177,8 +178,8 @@
resleevers += list(list( resleevers += list(list(
"sleever" = REF(resleever), "sleever" = REF(resleever),
"name" = sanitize(capitalize(resleever.name)), "name" = sanitize(capitalize(resleever.name)),
"occupied" = !!resleever.occupant, "occupied" = !!resleever.get_occupant(),
"occupant" = resleever.occupant ? resleever.occupant.real_name : "None" "occupant" = resleever.get_occupant() ? resleever.get_occupant().real_name : "None"
)) ))
data["sleevers"] = resleevers data["sleevers"] = resleevers
@@ -322,7 +323,7 @@
return return
//Already doing someone. //Already doing someone.
if(pod.occupant) if(pod.get_occupant())
set_temp("Error: Growpod is currently occupied.", "danger") set_temp("Error: Growpod is currently occupied.", "danger")
active_br = null active_br = null
return return
@@ -375,26 +376,26 @@
switch(mode) switch(mode)
if(1) //Body resleeving if(1) //Body resleeving
//No body to sleeve into. //No body to sleeve into.
if(!sleever.occupant) if(!sleever.get_occupant())
set_temp("Error: Resleeving pod is not occupied.", "danger") set_temp("Error: Resleeving pod is not occupied.", "danger")
active_mr = null active_mr = null
return return
//OOC body lock thing. //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") set_temp("Error: Mind incompatible with body.", "danger")
active_mr = null active_mr = null
return return
var/list/subtargets = list() 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) if(H.resleeve_lock && active_mr.ckey != H.resleeve_lock)
continue continue
subtargets += H subtargets += H
if(subtargets.len) 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) 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") set_temp("Error: Target selection aborted.", "danger")
active_mr = null active_mr = null
return return
@@ -558,7 +559,7 @@
if(!selected_sleever) if(!selected_sleever)
can_sleeve_active = FALSE can_sleeve_active = FALSE
set_temp("Error: Cannot sleeve due to no selected sleever.", "danger") 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 can_sleeve_active = FALSE
set_temp("Error: Cannot sleeve due to lack of sleever occupant.", "danger") set_temp("Error: Cannot sleeve due to lack of sleever occupant.", "danger")
else else

View File

@@ -59,6 +59,7 @@
if(!designer_gui) if(!designer_gui)
designer_gui = new(src, null) designer_gui = new(src, null)
designer_gui.linked_body_design_console = WEAKREF(src) 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) if(!designer_gui.owner)
designer_gui.make_fake_owner() designer_gui.make_fake_owner()
selected_record = FALSE selected_record = FALSE

View File

@@ -134,6 +134,11 @@
speciesname = M.custom_species ? M.custom_species : null speciesname = M.custom_species ? M.custom_species : null
bodygender = M.gender bodygender = M.gender
body_oocnotes = M.ooc_notes 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 sizemult = M.size_multiplier
weight = M.weight weight = M.weight
aflags = M.appearance_flags aflags = M.appearance_flags
@@ -204,7 +209,6 @@
if(add_to_db) if(add_to_db)
SStranscore.add_body(src, database_key = database_key) 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 * Make a deep copy of this record so it can be saved on a disk without modifications
* to the original affecting the copy. * to the original affecting the copy.
@@ -214,28 +218,195 @@
/datum/transhuman/body_record/proc/init_from_br(var/datum/transhuman/body_record/orig) /datum/transhuman/body_record/proc/init_from_br(var/datum/transhuman/body_record/orig)
ASSERT(!QDELETED(orig)) ASSERT(!QDELETED(orig))
ASSERT(istype(orig)) ASSERT(istype(orig))
src.mydna = new () for(var/A in vars)
qdel_swap(src.mydna.dna, orig.mydna.dna.Clone()) switch(A)
src.mydna.ckey = orig.mydna.ckey if(BLACKLISTED_COPY_VARS)
src.mydna.id = orig.mydna.id continue
src.mydna.name = orig.mydna.name if("mydna")
src.mydna.types = orig.mydna.types mydna = orig.mydna.copy()
src.mydna.flavor = orig.mydna.flavor.Copy() continue
src.ckey = orig.ckey if(islist(vars[A]))
src.locked = orig.locked var/list/L = orig.vars[A]
src.client_ref = orig.client_ref vars[A] = L.Copy()
src.mind_ref = orig.mind_ref continue
src.synthetic = orig.synthetic vars[A] = orig.vars[A]
src.speciesname = orig.speciesname
src.bodygender = orig.bodygender /**
src.body_oocnotes = orig.body_oocnotes * Spawning a body was once left entirely up to the machine doing it, but bodies are massivley complex
src.body_ooclikes = orig.body_ooclikes * objects, and doing it this way lead to huge amounts of copypasted code to do the same thing.
src.body_oocdislikes = orig.body_oocdislikes * If you want to spawn a body from a BR, please use these...
src.limb_data = orig.limb_data.Copy() */
src.organ_data = orig.organ_data.Copy()
src.genetic_modifiers = orig.genetic_modifiers.Copy() /// The core of resleeving, creates a mob based on the current record
src.toocomplex = orig.toocomplex /datum/transhuman/body_record/proc/produce_human_mob(var/location, var/is_synthfab, var/force_unlock, var/backup_name)
src.sizemult = orig.sizemult // These are broken up into steps, otherwise the proc gets massive and hard to read.
src.aflags = orig.aflags var/mob/living/carbon/human/H = internal_producebody(location,backup_name)
src.breath_type = orig.breath_type internal_producebody_handlesleevelock(H,force_unlock)
src.weight = orig.weight 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

View File

@@ -29,86 +29,9 @@
remove_biomass(CLONE_BIOMASS) remove_biomass(CLONE_BIOMASS)
//Get the DNA and generate a new mob //Get the DNA and generate a new mob
var/datum/dna2/record/R = current_project.mydna var/mob/living/carbon/human/H = current_project.produce_human_mob(src,FALSE,FALSE,"clone ([rand(0,999)])")
var/mob/living/carbon/human/H = new /mob/living/carbon/human(src, R.dna.species) SEND_SIGNAL(H, COMSIG_HUMAN_DNA_FINALIZED)
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.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 //Give breathing equipment if needed
if(current_project.breath_type != null && current_project.breath_type != GAS_O2) 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) 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) if(istype(H.internal,/obj/item/tank) && H.internals)
H.internals.icon_state = "internal1" H.internals.icon_state = "internal1"
//Basically all the VORE stuff //Apply damage
H.ooc_notes = current_project.body_oocnotes set_occupant(H)
H.ooc_notes_likes = current_project.body_ooclikes H.adjustCloneLoss((H.getMaxHealth() - (H.getMaxHealth()))*-0.75)
H.ooc_notes_dislikes = current_project.body_oocdislikes H.Paralyse(4)
H.ooc_notes_favs = current_project.body_oocfavs H.updatehealth()
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
//Machine specific stuff at the end //Machine specific stuff at the end
update_icon() update_icon()
@@ -150,6 +59,7 @@
return 1 return 1
/obj/machinery/clonepod/transhuman/process() /obj/machinery/clonepod/transhuman/process()
var/mob/living/occupant = get_occupant()
if(stat & NOPOWER) if(stat & NOPOWER)
if(occupant) if(occupant)
locked = 0 locked = 0
@@ -190,7 +100,7 @@
return return
else if((!occupant) || (occupant.loc != src)) else if((!occupant) || (occupant.loc != src))
occupant = null set_occupant(null)
if(locked) if(locked)
locked = 0 locked = 0
update_icon() update_icon()
@@ -199,13 +109,14 @@
return return
/obj/machinery/clonepod/transhuman/get_completion() /obj/machinery/clonepod/transhuman/get_completion()
var/mob/living/occupant = get_occupant()
if(occupant) if(occupant)
return 100 * ((occupant.health + (occupant.getMaxHealth()))) / (occupant.getMaxHealth() + abs(occupant.getMaxHealth())) return 100 * ((occupant.health + (occupant.getMaxHealth()))) / (occupant.getMaxHealth() + abs(occupant.getMaxHealth()))
return 0 return 0
/obj/machinery/clonepod/transhuman/examine(mob/user, infix, suffix) /obj/machinery/clonepod/transhuman/examine(mob/user, infix, suffix)
. = ..() . = ..()
if(occupant) if(get_occupant())
var/completion = get_completion() var/completion = get_completion()
. += "Progress: [round(completion)]% [chat_progress_bar(round(completion), TRUE)]" . += "Progress: [round(completion)]% [chat_progress_bar(round(completion), TRUE)]"
@@ -298,103 +209,16 @@
return return
//Get the DNA and generate a new mob //Get the DNA and generate a new mob
var/datum/dna2/record/R = current_project.mydna var/mob/living/carbon/human/H = current_project.produce_human_mob(src,TRUE,FALSE,"synth ([rand(0,999)])")
var/mob/living/carbon/human/H = new /mob/living/carbon/human(src, R.dna.species) SEND_SIGNAL(H, COMSIG_HUMAN_DNA_FINALIZED)
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()
//Apply damage //Apply damage
H.adjustBruteLoss(brute_value) H.adjustBruteLoss(brute_value)
H.adjustFireLoss(burn_value) H.adjustFireLoss(burn_value)
H.updatehealth() 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. //Plonk them here.
H.regenerate_icons() H.forceMove(get_turf(src))
H.loc = get_turf(src)
//Machine specific stuff at the end //Machine specific stuff at the end
stored_material[MAT_STEEL] -= body_cost stored_material[MAT_STEEL] -= body_cost
@@ -470,7 +294,7 @@
var/blur_amount var/blur_amount
var/confuse_amount var/confuse_amount
var/mob/living/carbon/human/occupant = null VAR_PRIVATE/datum/weakref/weakref_occupant = null
var/connected = null var/connected = null
var/sleevecards = 2 var/sleevecards = 2
@@ -487,6 +311,18 @@
RefreshParts() RefreshParts()
update_icon() 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() /obj/machinery/transhuman/resleever/RefreshParts()
var/scan_rating = 0 var/scan_rating = 0
for(var/obj/item/stock_parts/scanning_module/SM in component_parts) for(var/obj/item/stock_parts/scanning_module/SM in component_parts)
@@ -513,14 +349,15 @@
/obj/machinery/transhuman/resleever/tgui_data(mob/user) /obj/machinery/transhuman/resleever/tgui_data(mob/user)
var/list/data = list() var/list/data = list()
data["occupied"] = !!occupant var/mob/living/carbon/human/H = get_occupant()
if(occupant) data["occupied"] = !!H
data["name"] = occupant.name if(H)
data["health"] = occupant.health data["name"] = H.name
data["maxHealth"] = occupant.getMaxHealth() data["health"] = H.health
data["stat"] = occupant.stat data["maxHealth"] = H.getMaxHealth()
data["mindStatus"] = !!occupant.mind data["stat"] = H.stat
data["mindName"] = occupant.mind?.name data["mindStatus"] = !!H.mind
data["mindName"] = H.mind?.name
return data return data
/obj/machinery/transhuman/resleever/attackby(obj/item/W, mob/user) /obj/machinery/transhuman/resleever/attackby(obj/item/W, mob/user)
@@ -583,6 +420,7 @@
add_fingerprint(user) 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) /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) if((!occupant || !istype(occupant) || occupant.stat >= DEAD) && mode == 1)
return 0 return 0
@@ -665,13 +503,14 @@
return 1 return 1
/obj/machinery/transhuman/resleever/proc/go_out(var/mob/M) /obj/machinery/transhuman/resleever/proc/go_out(var/mob/M)
if(!( src.occupant )) var/mob/living/carbon/human/occupant = get_occupant()
if(!occupant)
return return
if (src.occupant.client) if (occupant.client)
src.occupant.client.eye = src.occupant.client.mob occupant.client.eye = occupant.client.mob
src.occupant.client.perspective = MOB_PERSPECTIVE occupant.client.perspective = MOB_PERSPECTIVE
src.occupant.loc = src.loc occupant.forceMove(get_turf(src))
src.occupant = null set_occupant(null)
icon_state = "implantchair" icon_state = "implantchair"
return return
@@ -679,7 +518,7 @@
if(!ishuman(M)) if(!ishuman(M))
to_chat(usr, span_warning("\The [src] cannot hold this!")) to_chat(usr, span_warning("\The [src] cannot hold this!"))
return return
if(src.occupant) if(get_occupant())
to_chat(usr, span_warning("\The [src] is already occupied!")) to_chat(usr, span_warning("\The [src] is already occupied!"))
return return
if(M.client) if(M.client)
@@ -687,7 +526,7 @@
M.client.eye = src M.client.eye = src
M.stop_pulling() M.stop_pulling()
M.loc = src M.loc = src
src.occupant = M set_occupant(M)
src.add_fingerprint(usr) src.add_fingerprint(usr)
icon_state = "implantchair_on" icon_state = "implantchair_on"
return 1 return 1

View File

@@ -44,6 +44,7 @@
var/list/valid_wingstyles = list() var/list/valid_wingstyles = list()
var/list/valid_gradstyles = list() var/list/valid_gradstyles = list()
var/list/markings = null var/list/markings = null
var/cooldown //Anti-spam. If spammed, this can be REALLY laggy.
/datum/tgui_module/appearance_changer/New( /datum/tgui_module/appearance_changer/New(
host, host,
@@ -60,7 +61,7 @@
cam_screen.name = "screen" cam_screen.name = "screen"
cam_screen.assigned_map = map_name cam_screen.assigned_map = map_name
cam_screen.del_on_map_removal = FALSE 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() cam_plane_masters = get_tgui_plane_masters()
@@ -83,11 +84,19 @@
whitelist = species_whitelist whitelist = species_whitelist
blacklist = species_blacklist 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) /datum/tgui_module/appearance_changer/tgui_close(mob/user)
. = ..() . = ..()
if(owner == user || !customize_usr) if(owner == user || !customize_usr)
close_ui() close_ui()
UnregisterSignal(owner, COMSIG_OBSERVER_MOVED) UnregisterSignal(owner, COMSIG_OBSERVER_MOVED)
SEND_SIGNAL(owner, COMSIG_HUMAN_DNA_FINALIZED) // Update any components using our saved appearance
owner = null owner = null
last_camera_turf = null last_camera_turf = null
cut_data() cut_data()
@@ -101,6 +110,11 @@
/datum/tgui_module/appearance_changer/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) /datum/tgui_module/appearance_changer/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state)
if(..()) if(..())
return TRUE 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/obj/machinery/computer/transhuman/designer/DC = null
var/datum/tgui_module/appearance_changer/body_designer/BD = null var/datum/tgui_module/appearance_changer/body_designer/BD = null
@@ -435,9 +449,8 @@
if (owner.change_marking_color(mark_datum, marking_color)) if (owner.change_marking_color(mark_datum, marking_color))
return TRUE return TRUE
if("rotate_view") if("rotate_view")
if(can_change(owner, APPEARANCE_RACE)) owner.set_dir(turn(owner.dir, 90))
owner.set_dir(turn(owner.dir, 90)) return TRUE
return TRUE
if("rename") if("rename")
if(owner) if(owner)
var/raw_name = tgui_input_text(ui.user, "Choose the a name:", "Sleeve Name") var/raw_name = tgui_input_text(ui.user, "Choose the a name:", "Sleeve Name")
@@ -465,35 +478,30 @@
owner.custom_species = new_name owner.custom_species = new_name
return TRUE return TRUE
if("base_icon") if("base_icon")
if(owner.species.selects_bodytype == SELECTS_BODYTYPE_FALSE) if(can_change(owner, APPEARANCE_MISC))
var/datum/species/S = GLOB.all_species[owner.species.name] var/new_species = tgui_input_list(ui.user, "Please select basic shape.", "Body Shape", GLOB.custom_species_bases)
owner.species.base_species = S.base_species // Return to original form if(new_species)
generate_data(ui.user, owner) owner.species.base_species = new_species
changed_hook(APPEARANCECHANGER_CHANGED_RACE) owner.species.icobase = owner.species.get_icobase()
return TRUE owner.species.deform = owner.species.get_icobase(get_deform = TRUE)
var/list/choices owner.species.vanity_base_fit = new_species
var/datum/species/S = GLOB.all_species[owner.species.name] if(istype(owner.species, /datum/species/shapeshifter)) //TODO: See if this is still needed.
if(S.selects_bodytype == SELECTS_BODYTYPE_SHAPESHIFTER) wrapped_species_by_ref["\ref[owner]"] = new_species
choices = S.get_valid_shapeshifter_forms() owner.regenerate_icons()
else if(S.selects_bodytype == SELECTS_BODYTYPE_CUSTOM) generate_data(ui.user, owner)
choices = GLOB.custom_species_bases changed_hook(APPEARANCECHANGER_CHANGED_RACE)
var/new_species = tgui_input_list(ui.user, "Please select basic shape.", "Body Shape", choices) return TRUE
if(new_species && can_change(owner, APPEARANCE_RACE)) if("blood_reagent") //you know, this feels REALLY odd to be able to change at will but WHATEVER, WE BALL.
owner.species.base_species = new_species if(can_change(owner, APPEARANCE_MISC))
owner.regenerate_icons() var/new_blood_reagents = tgui_input_list(ui.user, "Please select blood restoration reagent:", "Character Preference", valid_bloodreagents)
generate_data(ui.user, owner) if(new_blood_reagents)
changed_hook(APPEARANCECHANGER_CHANGED_RACE) owner.dna.blood_reagents = new_blood_reagents
return TRUE changed_hook(APPEARANCECHANGER_CHANGED_RACE)
if("blood_reagent") return TRUE
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("blood_color") if("blood_color")
var/current = owner.species.blood_color ? owner.species.blood_color : "#A10808" 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) var/blood_col = tgui_color_picker(ui.user, "Please select blood color", "Blood color", current)
if(blood_col && can_change(owner, APPEARANCE_RACE)) if(blood_col && can_change(owner, APPEARANCE_MISC))
owner.dna.blood_color = blood_col owner.dna.blood_color = blood_col
changed_hook(APPEARANCECHANGER_CHANGED_RACE) changed_hook(APPEARANCECHANGER_CHANGED_RACE)
return TRUE return TRUE
@@ -501,9 +509,9 @@
var/new_weight = tgui_input_number(ui.user, "Choose tbe character's relative body weight.\n\ 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\ 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) ([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")) 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") if(unit_of_measurement == "Pounds")
new_weight = round(text2num(new_weight),4) new_weight = round(text2num(new_weight),4)
if(unit_of_measurement == "Kilograms") if(unit_of_measurement == "Kilograms")
@@ -513,7 +521,7 @@
return TRUE return TRUE
if("size_scale") 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) 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.size_multiplier = new_size / 100
owner.update_transform(TRUE) owner.update_transform(TRUE)
owner.regenerate_icons() owner.regenerate_icons()
@@ -521,21 +529,21 @@
changed_hook(APPEARANCECHANGER_CHANGED_RACE) changed_hook(APPEARANCECHANGER_CHANGED_RACE)
return TRUE return TRUE
if("scale_appearance") if("scale_appearance")
if(can_change(owner, APPEARANCE_RACE)) if(can_change(owner, APPEARANCE_MISC))
owner.dna.scale_appearance = !owner.dna.scale_appearance owner.dna.scale_appearance = !owner.dna.scale_appearance
owner.fuzzy = owner.dna.scale_appearance owner.fuzzy = owner.dna.scale_appearance
owner.regenerate_icons() owner.regenerate_icons()
owner.set_dir(owner.dir) // Causes a visual update for fuzzy/offset owner.set_dir(owner.dir) // Causes a visual update for fuzzy/offset
return TRUE return TRUE
if("offset_override") if("offset_override")
if(can_change(owner, APPEARANCE_RACE)) if(can_change(owner, APPEARANCE_MISC))
owner.dna.offset_override = !owner.dna.offset_override owner.dna.offset_override = !owner.dna.offset_override
owner.offset_override = owner.dna.offset_override owner.offset_override = owner.dna.offset_override
owner.regenerate_icons() owner.regenerate_icons()
owner.set_dir(owner.dir) // Causes a visual update for fuzzy/offset owner.set_dir(owner.dir) // Causes a visual update for fuzzy/offset
return TRUE return TRUE
if("digitigrade") if("digitigrade")
if(can_change(owner, APPEARANCE_RACE)) if(can_change(owner, APPEARANCE_MISC))
owner.dna.digitigrade = !owner.dna.digitigrade owner.dna.digitigrade = !owner.dna.digitigrade
owner.digitigrade = owner.dna.digitigrade owner.digitigrade = owner.dna.digitigrade
owner.regenerate_icons() owner.regenerate_icons()
@@ -545,28 +553,37 @@
if("species_sound") if("species_sound")
var/list/possible_species_sound_types = species_sound_map 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) 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 owner.species.species_sounds = choice
return TRUE return TRUE
if("flavor_text") if("flavor_text")
var/select_key = params["target"] 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) if(select_key in owner.flavor_texts)
switch(select_key) switch(select_key)
if("general") 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)) 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") if(msg == "!clear")
msg = "" msg = ""
owner.flavor_texts[select_key] = msg owner.flavor_texts[select_key] = msg
return TRUE return TRUE
else 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)) 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") if(msg == "!clear")
msg = "" msg = ""
owner.flavor_texts[select_key] = msg owner.flavor_texts[select_key] = msg
return TRUE 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 // Body designer UI
// *********************************** // ***********************************
@@ -618,6 +635,7 @@
if(DC.disk.stored) if(DC.disk.stored)
qdel_null(DC.disk.stored) qdel_null(DC.disk.stored)
to_chat(ui.user,span_notice("\The [owner]'s bodyrecord was saved to the disk.")) 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 = new /datum/transhuman/body_record(owner, FALSE, FALSE) // Saves a COPY!
DC.disk.stored.locked = FALSE // remove lock DC.disk.stored.locked = FALSE // remove lock
DC.disk.name = "[initial(DC.disk.name)] ([owner.real_name])" DC.disk.name = "[initial(DC.disk.name)] ([owner.real_name])"
@@ -660,6 +678,7 @@
// Open UI // Open UI
ui = new(user, src, tgui_id, name) ui = new(user, src, tgui_id, name)
ui.open() ui.open()
CallAsync(src, PROC_REF(jiggle_map))
if(custom_state) if(custom_state)
ui.set_state(custom_state) ui.set_state(custom_state)
update_active_camera_screen() update_active_camera_screen()
@@ -747,6 +766,7 @@
stock_bodyrecords_list_ui += N stock_bodyrecords_list_ui += N
data["stock_records"] = stock_bodyrecords_list_ui data["stock_records"] = stock_bodyrecords_list_ui
data["change_race"] = can_change(owner, APPEARANCE_RACE) 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["gender_id"] = can_change(owner, APPEARANCE_GENDER)
data["change_gender"] = can_change(owner, APPEARANCE_GENDER) data["change_gender"] = can_change(owner, APPEARANCE_GENDER)
data["change_hair"] = can_change(owner, APPEARANCE_HAIR) data["change_hair"] = can_change(owner, APPEARANCE_HAIR)
@@ -790,6 +810,8 @@
data["gender"] = owner.gender data["gender"] = owner.gender
data["gender_id"] = owner.identifying_gender //This is saved to your MIND. data["gender_id"] = owner.identifying_gender //This is saved to your MIND.
data["change_race"] = can_change(owner, APPEARANCE_RACE) 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) data["change_gender"] = can_change(owner, APPEARANCE_GENDER)
if(data["change_gender"]) if(data["change_gender"])
@@ -873,7 +895,7 @@
local_skybox.cut_overlays() local_skybox.cut_overlays()
/datum/tgui_module/appearance_changer/proc/update_dna(mob/living/carbon/human/target) /datum/tgui_module/appearance_changer/proc/update_dna(mob/living/carbon/human/target)
if(target && (flags & APPEARANCE_UPDATE_DNA)) if(target)
target.update_dna() target.update_dna()
/datum/tgui_module/appearance_changer/proc/can_change(mob/living/carbon/human/target, var/flag) /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 /datum/tgui_module/appearance_changer/cocoon
name ="Appearance Editor (Cocoon)" name ="Appearance Editor (Cocoon)"
flags = APPEARANCE_ALL_HAIR | APPEARANCE_EYE_COLOR | APPEARANCE_SKIN flags = APPEARANCE_ALL_COSMETIC
customize_usr = TRUE customize_usr = TRUE
/datum/tgui_module/appearance_changer/cocoon/tgui_status(mob/user, datum/tgui_state/state) /datum/tgui_module/appearance_changer/cocoon/tgui_status(mob/user, datum/tgui_state/state)
@@ -1051,7 +1073,7 @@
// ******************************************************* // *******************************************************
/datum/tgui_module/appearance_changer/superpower /datum/tgui_module/appearance_changer/superpower
name ="Appearance Editor (Superpower)" name ="Appearance Editor (Superpower)"
flags = APPEARANCE_ALL_HAIR | APPEARANCE_EYE_COLOR | APPEARANCE_SKIN flags = APPEARANCE_ALL_COSMETIC
customize_usr = TRUE customize_usr = TRUE
/datum/tgui_module/appearance_changer/superpower/tgui_status(mob/user, datum/tgui_state/state) /datum/tgui_module/appearance_changer/superpower/tgui_status(mob/user, datum/tgui_state/state)
@@ -1060,6 +1082,19 @@
return STATUS_CLOSE return STATUS_CLOSE
return ..() 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 // Body design console
// ******************************************************* // *******************************************************
@@ -1097,52 +1132,10 @@
if(owner) if(owner)
UnregisterSignal(owner, COMSIG_OBSERVER_MOVED) UnregisterSignal(owner, COMSIG_OBSERVER_MOVED)
qdel_null(owner) qdel_null(owner)
//Get the DNA and generate a new mob owner = current_project.produce_human_mob(src,FALSE,FALSE,"Designer [rand(999)]")
var/datum/dna2/record/R = current_project.mydna // Update some specifics from the current record
owner = new /mob/living/carbon/human(src, R.dna.species) owner.dna.blood_reagents = current_project.mydna.dna.blood_reagents
//Fix the external organs owner.dna.blood_color = current_project.mydna.dna.blood_color
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.flavor_texts = current_project.mydna.flavor.Copy() owner.flavor_texts = current_project.mydna.flavor.Copy()
owner.resize(current_project.sizemult, FALSE) owner.resize(current_project.sizemult, FALSE)
owner.appearance_flags = current_project.aflags owner.appearance_flags = current_project.aflags

View File

@@ -169,12 +169,12 @@ Please do not abuse this ability.
var/datum/language/keylang = GLOB.all_languages[prey.prefs.language_custom_keys[key]] var/datum/language/keylang = GLOB.all_languages[prey.prefs.language_custom_keys[key]]
if(keylang) if(keylang)
new_character.language_keys[key] = keylang new_character.language_keys[key] = keylang
// VOREStation Add: Preferred Language Setting;
if(prey.prefs.preferred_language) // Do we have a preferred language? if(prey.prefs.preferred_language) // Do we have a preferred language?
var/datum/language/def_lang = GLOB.all_languages[prey.prefs.preferred_language] var/datum/language/def_lang = GLOB.all_languages[prey.prefs.preferred_language]
if(def_lang) if(def_lang)
new_character.default_language = def_lang new_character.default_language = def_lang
// VOREStation Add End
SEND_SIGNAL(new_character, COMSIG_HUMAN_DNA_FINALIZED)
new_character.regenerate_icons() new_character.regenerate_icons()

View File

@@ -1,5 +1,5 @@
import { useBackend } from 'tgui/backend'; 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'; import type { Data, species } from './types';
@@ -35,65 +35,8 @@ export const AppearanceChangerSpecies = (props) => {
) : ( ) : (
'' ''
)} )}
<LabeledList.Item label="Custom Species Name">
<Button icon="pen" onClick={() => act('race_name')}>
{data.species_name ? data.species_name : specimen}
</Button>
</LabeledList.Item>
<LabeledList.Item label="Species Appearance">
<Button
icon="pen"
disabled={!data.use_custom_icon}
onClick={() => act('base_icon')}
>
{data.base_icon ? data.base_icon : specimen}
</Button>
</LabeledList.Item>
<LabeledList.Item label="Blood Reagent">
<Button icon="pen" onClick={() => act('blood_reagent')}>
{data.blood_reagent}
</Button>
</LabeledList.Item>
<LabeledList.Item label="Blood Color">
<Button icon="pen" onClick={() => act('blood_color')}>
{data.blood_color}
</Button>
<ColorBox color={data.blood_color} mr={1} />
</LabeledList.Item>
<LabeledList.Item label="Digitigrade">
<Button icon="pen" onClick={() => act('digitigrade')}>
{data.digitigrade ? 'Yes' : 'No'}
</Button>
</LabeledList.Item>
<LabeledList.Item label="Species Sound">
<Button icon="pen" onClick={() => act('species_sound')}>
{data.species_sound}
</Button>
</LabeledList.Item>
</LabeledList> </LabeledList>
</Section> </Section>
<Section title="Sizing">
<LabeledList.Item label="Scale">
<Button icon="pen" onClick={() => act('size_scale')}>
{data.size_scale}
</Button>
</LabeledList.Item>
<LabeledList.Item label="Scale Appearance">
<Button icon="pen" onClick={() => act('scale_appearance')}>
{data.scale_appearance}
</Button>
</LabeledList.Item>
<LabeledList.Item label="Scale Offset">
<Button icon="pen" onClick={() => act('offset_override')}>
{data.offset_override}
</Button>
</LabeledList.Item>
<LabeledList.Item label="Weight">
<Button icon="pen" onClick={() => act('weight')}>
{data.weight}
</Button>
</LabeledList.Item>
</Section>
</Section> </Section>
); );
}; };

View File

@@ -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<Data>();
const { specimen } = data;
return (
<Section title="Misc" fill scrollable>
<Stack vertical fill>
<Section title="Load Currently Selected Savefile">
<LabeledList.Item label="Load Savefile">
<Button icon="pen" onClick={() => act('load_saveslot')}>
{'Load'}
</Button>
</LabeledList.Item>
</Section>
<Section title="Appearance">
<LabeledList.Item label="Species Appearance">
<Button
icon="pen"
disabled={!data.use_custom_icon}
onClick={() => act('base_icon')}
>
{data.base_icon ? data.base_icon : specimen}
</Button>
</LabeledList.Item>
<LabeledList.Item label="Digitigrade">
<Button icon="pen" onClick={() => act('digitigrade')}>
{data.digitigrade ? 'Yes' : 'No'}
</Button>
</LabeledList.Item>
<LabeledList.Item label="Custom Species Name">
<Button icon="pen" onClick={() => act('race_name')}>
{data.species_name ? data.species_name : specimen}
</Button>
</LabeledList.Item>
<LabeledList.Item label="Species Sound">
<Button icon="pen" onClick={() => act('species_sound')}>
{data.species_sound}
</Button>
</LabeledList.Item>
</Section>
<Section title="Scaling">
<LabeledList.Item label="Scale">
<Button icon="pen" onClick={() => act('size_scale')}>
{data.size_scale}
</Button>
</LabeledList.Item>
<LabeledList.Item label="Scale Offset">
<Button icon="pen" onClick={() => act('offset_override')}>
{data.offset_override}
</Button>
</LabeledList.Item>
<LabeledList.Item label="Scale Appearance">
<Button icon="pen" onClick={() => act('scale_appearance')}>
{data.scale_appearance}
</Button>
</LabeledList.Item>
</Section>
<Section title="Sizing">
<LabeledList.Item label="Weight">
<Button icon="pen" onClick={() => act('weight')}>
{data.weight}
</Button>
</LabeledList.Item>
</Section>
<Section title="Blood">
<LabeledList.Item label="Blood Reagent">
<Button icon="pen" onClick={() => act('blood_reagent')}>
{data.blood_reagent}
</Button>
</LabeledList.Item>
<LabeledList.Item label="Blood Color">
<Stack align="center">
<Stack.Item>
<Button icon="pen" onClick={() => act('blood_color')}>
{data.blood_color}
</Button>
</Stack.Item>
<Stack.Item>
<ColorBox color={data.blood_color} mr={1} />
</Stack.Item>
</Stack>
</LabeledList.Item>
</Section>
</Stack>
</Section>
);
};

View File

@@ -16,4 +16,5 @@ export const TAB_EARS2 = 7;
export const TAB_TAIL = 8; export const TAB_TAIL = 8;
export const TAB_WINGS = 9; export const TAB_WINGS = 9;
export const TAB_MARKINGS = 10; export const TAB_MARKINGS = 10;
export const TAB_MISC = 11;
export const MARKINGS_PER_PAGE = 30; export const MARKINGS_PER_PAGE = 30;

View File

@@ -21,6 +21,7 @@ import { AppearanceChangerColors } from './AppearanceChangerColors';
import { AppearanceChangerFlavor } from './AppearanceChangerFlavor'; import { AppearanceChangerFlavor } from './AppearanceChangerFlavor';
import { AppearanceChangerHeader } from './AppearanceChangerHeader'; import { AppearanceChangerHeader } from './AppearanceChangerHeader';
import { AppearanceChangerMarkings } from './AppearanceChangerMarkings'; import { AppearanceChangerMarkings } from './AppearanceChangerMarkings';
import { AppearanceChangerMisc } from './AppearanceChangerMisc';
import { import {
AppearanceChangerHair, AppearanceChangerHair,
AppearanceChangerParts, AppearanceChangerParts,
@@ -34,6 +35,7 @@ import {
TAB_GENDER, TAB_GENDER,
TAB_HAIR, TAB_HAIR,
TAB_MARKINGS, TAB_MARKINGS,
TAB_MISC,
TAB_RACE, TAB_RACE,
TAB_TAIL, TAB_TAIL,
TAB_WINGS, TAB_WINGS,
@@ -60,6 +62,7 @@ export const AppearanceChanger = (props) => {
wing_style, wing_style,
wing_styles, wing_styles,
change_race, change_race,
change_misc,
change_gender, change_gender,
change_eye_color, change_eye_color,
change_skin_tone, change_skin_tone,
@@ -90,7 +93,7 @@ export const AppearanceChanger = (props) => {
) : ( ) : (
<AppearanceChangerDefaultError /> <AppearanceChangerDefaultError />
); );
tab[TAB_FLAVOR] = change_race ? ( tab[TAB_FLAVOR] = change_misc ? (
<AppearanceChangerFlavor /> <AppearanceChangerFlavor />
) : ( ) : (
<AppearanceChangerDefaultError /> <AppearanceChangerDefaultError />
@@ -180,6 +183,11 @@ export const AppearanceChanger = (props) => {
) : ( ) : (
<AppearanceChangerDefaultError /> <AppearanceChangerDefaultError />
); );
tab[TAB_MISC] = change_misc ? (
<AppearanceChangerMisc />
) : (
<AppearanceChangerDefaultError />
);
let firstAccesibleTab = -1; let firstAccesibleTab = -1;
if (change_race) { if (change_race) {
@@ -378,6 +386,14 @@ export const AppearanceChanger = (props) => {
</Tabs.Tab> </Tabs.Tab>
</> </>
) : null} ) : null}
{change_misc ? (
<Tabs.Tab
selected={tabIndex === TAB_MISC}
onClick={() => setTabIndex(TAB_MISC)}
>
Misc
</Tabs.Tab>
) : null}
</Tabs> </Tabs>
</Stack.Item> </Stack.Item>
<Stack.Item grow>{tab[tabIndex]}</Stack.Item> <Stack.Item grow>{tab[tabIndex]}</Stack.Item>

View File

@@ -17,6 +17,7 @@ export type Data = {
tail_styles: styles[]; tail_styles: styles[];
markings: { marking_name: string; marking_color: string }[]; markings: { marking_name: string; marking_color: string }[];
change_race: BooleanLike; change_race: BooleanLike;
change_misc: BooleanLike;
change_gender: BooleanLike; change_gender: BooleanLike;
genders: genders; genders: genders;
id_genders: genders; id_genders: genders;

View File

@@ -179,6 +179,7 @@
#include "code\__defines\typeids.dm" #include "code\__defines\typeids.dm"
#include "code\__defines\unit_tests.dm" #include "code\__defines\unit_tests.dm"
#include "code\__defines\update_icons.dm" #include "code\__defines\update_icons.dm"
#include "code\__defines\var_copy.dm"
#include "code\__defines\vchatlog.dm" #include "code\__defines\vchatlog.dm"
#include "code\__defines\verb_manager.dm" #include "code\__defines\verb_manager.dm"
#include "code\__defines\visualnet.dm" #include "code\__defines\visualnet.dm"