[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"
///Whenever EquipRanked is called, called after job is set
#define COMSIG_JOB_RECEIVED "job_received"
///When the mob's dna and species have been fully applied
#define COMSIG_HUMAN_DNA_FINALIZED "human_dna_finished"
// /datum/species signals

View File

@@ -103,8 +103,8 @@ var/SMALLSIZEBLOCK = 0
#define DNA_UI_GENDER 14
#define DNA_UI_BEARD_STYLE 15
#define DNA_UI_HAIR_STYLE 16
#define DNA_UI_EAR_STYLE 17 // VOREStation snippet.
#define DNA_UI_EAR_SECONDARY_STYLE 18 // VOREStation snippet.
#define DNA_UI_EAR_STYLE 17
#define DNA_UI_EAR_SECONDARY_STYLE 18
#define DNA_UI_TAIL_STYLE 19
#define DNA_UI_PLAYERSCALE 20
#define DNA_UI_TAIL_R 21
@@ -152,9 +152,14 @@ var/SMALLSIZEBLOCK = 0
#define DNA_UI_WING2_B 57
#define DNA_UI_WING3_R 58
#define DNA_UI_WING3_G 59
#define DNA_UI_WING3_B 60 // VOREStation snippet end.
#define DNA_UI_WING3_B 60
#define DNA_UI_WING_ALPHA 61
#define DNA_UI_LENGTH 61 // VOREStation Edit - Needs to match the highest number above.
#define DNA_UI_GRAD_STYLE 62
#define DNA_UI_GRAD_R 63
#define DNA_UI_GRAD_G 64
#define DNA_UI_GRAD_B 65
// Needs to match the highest number above.
#define DNA_UI_LENGTH 65
#define DNA_SE_LENGTH 90 // Traitgenes (Expanded from 49 to 84, there have been a considerable expansion of genes.
// This leaves room for future expansion. This can be arbitrarily raised without worry if genes start to get crowded.

View File

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

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
var/list/blacklisted_icons = list(SPECIES_CUSTOM,SPECIES_PROMETHEAN) //VOREStation Edit
///These are icons that you DO NOT want to be selectable!
var/list/blacklisted_icons = list(SPECIES_CUSTOM,SPECIES_PROMETHEAN)
///These are icons that you WANT to be selectable, even if they're a whitelist species!
var/list/whitelisted_icons = list(SPECIES_FENNEC,SPECIES_XENOHYBRID,SPECIES_VOX,SPECIES_SHADEKIN) //CHOMPedit
for(var/species_name in GLOB.playable_species)
if(species_name in blacklisted_icons)

View File

@@ -861,22 +861,7 @@ Turf and target are seperate in case you want to teleport some distance from a t
else
O=new original.type(locate(0,0,0))
var/static/list/blacklisted_var_names = list(
"ATOM_TOPIC_EXAMINE",
"type",
"loc",
"locs",
"vars",
"parent",
"parent_type",
"verbs",
"ckey",
"key",
"_active_timers", // ChompEDIT - blacklist timers
"_datum_components", // ChompEDIT - blacklist DCS
"_listen_lookup", // ChompEDIT - blacklist signal listeners
"_signal_procs" // ChompEDIT - blacklist signal procs
)
var/static/list/blacklisted_var_names = list(BLACKLISTED_COPY_VARS)
if(perfectcopy)
if((O) && (original))
for(var/V in original.vars)

View File

@@ -1,31 +1,43 @@
/datum/component/xenochimera
var/laststress = 0
var/mob/living/carbon/human/owner
VAR_PRIVATE/laststress = 0
VAR_PRIVATE/mob/living/carbon/human/owner
var/feral = 0
var/revive_ready = REVIVING_READY
var/revive_finished = FALSE
var/regen_sounds = list(
VAR_PRIVATE/regen_sounds = list(
'sound/effects/mob_effects/xenochimera/regen_1.ogg',
'sound/effects/mob_effects/xenochimera/regen_2.ogg',
'sound/effects/mob_effects/xenochimera/regen_4.ogg',
'sound/effects/mob_effects/xenochimera/regen_3.ogg',
'sound/effects/mob_effects/xenochimera/regen_5.ogg'
)
VAR_PRIVATE/datum/transhuman/body_record/revival_record
/datum/component/xenochimera/Initialize()
if(!ishuman(parent))
return COMPONENT_INCOMPATIBLE
owner = parent
RegisterSignal(owner, COMSIG_XENOCHIMERA_COMPONENT, PROC_REF(handle_comp))
RegisterSignal(owner, COMSIG_HUMAN_DNA_FINALIZED, PROC_REF(handle_record))
add_verb(owner, /mob/living/carbon/human/proc/reconstitute_form)
/datum/component/xenochimera/Destroy(force)
UnregisterSignal(owner, COMSIG_XENOCHIMERA_COMPONENT)
UnregisterSignal(owner, COMSIG_HUMAN_DNA_FINALIZED)
remove_verb(owner, /mob/living/carbon/human/proc/reconstitute_form)
qdel_null(revival_record)
owner = null
. = ..()
/datum/component/xenochimera/proc/handle_record()
SIGNAL_HANDLER
if(QDELETED(owner))
return
qdel_null(revival_record)
revival_record = new(owner)
/datum/component/xenochimera/proc/handle_comp()
SIGNAL_HANDLER
if(QDELETED(owner))
return
handle_feralness()
@@ -42,6 +54,19 @@
if(!owner.lying)
owner.lay_down()
/datum/component/xenochimera/proc/set_revival_delay(var/time)
revive_ready = REVIVING_NOW
revive_finished = (world.time + time SECONDS) // When do we finish reviving? Allows us to find out when we're done, called by the alert currently.
/datum/component/xenochimera/proc/trigger_revival(var/from_save_slot)
ASSERT(revival_record)
if(owner.isSynthetic())
revival_record.revive_xenochimera(owner,TRUE,from_save_slot)
else
revival_record.revive_xenochimera(owner,FALSE,from_save_slot)
if(from_save_slot)
handle_record() // Update record
/datum/component/xenochimera/proc/handle_feralness()
//first, calculate how stressed the chimera is
@@ -254,103 +279,102 @@
/mob/living/carbon/human/proc/reconstitute_form() //Scree's race ability.in exchange for: No cloning.
set name = "Reconstitute Form"
set category = "Abilities.Xenochimera"
var/datum/component/xenochimera/xc = get_xenochimera_component()
if(!xc)
return
if(is_incorporeal())
to_chat(src, "You cannot regenerate while incorporeal.")
return
// Sanity is mostly handled in chimera_regenerate()
if(stat == DEAD)
var/confirm = tgui_alert(src, "Are you sure you want to regenerate your corpse? This process can take up to thirty minutes.", "Confirm Regeneration", list("Yes", "No"))
var/confirm = tgui_alert(src, "Are you sure you want to regenerate your corpse? This process can take up to thirty minutes. Additionally, you may regenerate your appearance based on your current form or the appearance of the currently loaded slot.", "Confirm Regeneration", list("Yes", "No"))
if(confirm == "Yes")
chimera_regenerate()
else if (quickcheckuninjured())
var/confirm = tgui_alert(src, "Are you sure you want to regenerate? As you are uninjured this will only take 30 seconds and match your appearance to your character slot.", "Confirm Regeneration", list("Yes", "No"))
xc.chimera_regenerate()
else if(quickcheckuninjured())
var/confirm = tgui_alert(src, "Are you sure you want to regenerate? As you are uninjured this will only take 30 seconds. Additionally, you may regenerate your appearance based on your current form or the appearance of the currently loaded slot.", "Confirm Regeneration", list("Yes", "No"))
if(confirm == "Yes")
chimera_regenerate()
xc.chimera_regenerate()
else
var/confirm = tgui_alert(src, "Are you sure you want to completely reconstruct your form? This process can take up to fifteen minutes, depending on how hungry you are, and you will be unable to move.", "Confirm Regeneration", list("Yes", "No"))
var/confirm = tgui_alert(src, "Are you sure you want to completely reconstruct your form? This process can take up to fifteen minutes, depending on how hungry you are, and you will be unable to move. Additionally, you may regenerate your appearance based on your current form or the appearance of the currently loaded slot", "Confirm Regeneration", list("Yes", "No"))
if(confirm == "Yes")
chimera_regenerate()
xc.chimera_regenerate()
/mob/living/carbon/human/proc/chimera_regenerate()
var/datum/component/xenochimera/xc = get_xenochimera_component()
if(!xc)
/datum/component/xenochimera/proc/chimera_regenerate()
if(!owner)
return
//If they're already regenerating
switch(xc.revive_ready)
switch(revive_ready)
if(REVIVING_NOW)
to_chat(src, "You are already reconstructing, just wait for the reconstruction to finish!")
to_chat(owner, "You are already reconstructing, just wait for the reconstruction to finish!")
return
if(REVIVING_DONE)
to_chat(src, "Your reconstruction is done, but you need to hatch now.")
to_chat(owner, "Your reconstruction is done, but you need to hatch now.")
return
if(xc.revive_ready > world.time)
to_chat(src, "You can't use that ability again so soon!")
if(revive_ready > world.time)
to_chat(owner, "You can't use that ability again so soon!")
return
var/time = min(900, (120+780/(1 + nutrition/100))) //capped at 15 mins, roughly 6 minutes at 250 (yellow) nutrition, 4.1 minutes at 500 (grey), cannot be below 2 mins
if (quickcheckuninjured()) //if you're completely uninjured, then you get a speedymode - check health first for quickness
var/time = min(900, (120+780/(1 + owner.nutrition/100))) //capped at 15 mins, roughly 6 minutes at 250 (yellow) nutrition, 4.1 minutes at 500 (grey), cannot be below 2 mins
if (owner.quickcheckuninjured()) //if you're completely uninjured, then you get a speedymode - check health first for quickness
time = 30
//Clicked regen while dead.
if(stat == DEAD)
if(owner.stat == DEAD)
//reviving from dead takes extra nutriment to be provided from outside OR takes twice as long and consumes extra at the end
if(!hasnutriment())
if(!owner.hasnutriment())
time = time*2
to_chat(src, "You begin to reconstruct your form. You will not be able to move during this time. It should take aproximately [round(time)] seconds.")
to_chat(owner, "You begin to reconstruct your form. You will not be able to move during this time. It should take aproximately [round(time)] seconds.")
//Scary spawnerization.
xc.revive_ready = REVIVING_NOW
xc.revive_finished = (world.time + time SECONDS) // When do we finish reviving? Allows us to find out when we're done, called by the alert currently.
throw_alert("regen", /obj/screen/alert/xenochimera/reconstitution)
set_revival_delay(time)
owner.throw_alert("regen", /obj/screen/alert/xenochimera/reconstitution)
addtimer(CALLBACK(src, PROC_REF(chimera_regenerate_ready)), time SECONDS, TIMER_DELETE_ME)
//Clicked regen while NOT dead
else
to_chat(src, "You begin to reconstruct your form. You will not be able to move during this time. It should take aproximately [round(time)] seconds.")
to_chat(owner, "You begin to reconstruct your form. You will not be able to move during this time. It should take aproximately [round(time)] seconds.")
//Waiting for regen after being alive
xc.revive_ready = REVIVING_NOW
xc.revive_finished = (world.time + time SECONDS) // When do we finish reviving? Allows us to find out when we're done, called by the alert currently.
throw_alert("regen", /obj/screen/alert/xenochimera/reconstitution)
set_revival_delay(time)
owner.throw_alert("regen", /obj/screen/alert/xenochimera/reconstitution)
addtimer(CALLBACK(src, PROC_REF(chimera_regenerate_nutrition)), time SECONDS, TIMER_DELETE_ME)
owner.lying = TRUE
// open_appearance_editor()
/mob/living/carbon/human/proc/chimera_regenerate_nutrition()
var/datum/component/xenochimera/xc = get_xenochimera_component()
if(!xc)
/datum/component/xenochimera/proc/chimera_regenerate_nutrition()
if(!owner)
return
//Slightly different flavour messages
if(stat != DEAD || hasnutriment())
to_chat(src, span_notice("Consciousness begins to stir as your new body awakens, ready to hatch.."))
if(owner.stat != DEAD || owner.hasnutriment())
to_chat(owner, span_notice("Consciousness begins to stir as your new body awakens, ready to hatch.."))
else
to_chat(src, span_warning("Consciousness begins to stir as your battered body struggles to recover from its ordeal.."))
add_verb(src, /mob/living/carbon/human/proc/hatch)
xc.revive_ready = REVIVING_DONE
src << sound('sound/effects/mob_effects/xenochimera/hatch_notification.ogg',0,0,0,30)
clear_alert("regen")
throw_alert("hatch", /obj/screen/alert/xenochimera/readytohatch)
to_chat(owner, span_warning("Consciousness begins to stir as your battered body struggles to recover from its ordeal.."))
add_verb(owner, /mob/living/carbon/human/proc/hatch)
revive_ready = REVIVING_DONE
owner << sound('sound/effects/mob_effects/xenochimera/hatch_notification.ogg',0,0,0,30)
owner.clear_alert("regen")
owner.throw_alert("hatch", /obj/screen/alert/xenochimera/readytohatch)
/mob/living/carbon/human/proc/chimera_regenerate_ready()
var/datum/component/xenochimera/xc = get_xenochimera_component()
if(!xc)
/datum/component/xenochimera/proc/chimera_regenerate_ready()
if(!owner)
return
// check to see if they've been fixed by outside forces in the meantime such as defibbing
if(stat != DEAD)
to_chat(src, span_notice("Your body has recovered from its ordeal, ready to regenerate itself again."))
xc.revive_ready = REVIVING_READY //reset their cooldown
clear_alert("regen")
throw_alert("hatch", /obj/screen/alert/xenochimera/readytohatch)
if(owner.stat != DEAD)
to_chat(owner, span_notice("Your body has recovered from its ordeal, ready to regenerate itself again."))
revive_ready = REVIVING_READY //reset their cooldown
owner.clear_alert("regen")
owner.throw_alert("hatch", /obj/screen/alert/xenochimera/readytohatch)
// Was dead, still dead.
else
to_chat(src, span_notice("Consciousness begins to stir as your new body awakens, ready to hatch."))
add_verb(src, /mob/living/carbon/human/proc/hatch)
xc.revive_ready = REVIVING_DONE
src << sound('sound/effects/mob_effects/xenochimera/hatch_notification.ogg',0,0,0,30)
clear_alert("regen")
throw_alert("hatch", /obj/screen/alert/xenochimera/readytohatch)
to_chat(owner, span_notice("Consciousness begins to stir as your new body awakens, ready to hatch."))
add_verb(owner, /mob/living/carbon/human/proc/hatch)
revive_ready = REVIVING_DONE
owner << sound('sound/effects/mob_effects/xenochimera/hatch_notification.ogg',0,0,0,30)
owner.clear_alert("regen")
owner.throw_alert("hatch", /obj/screen/alert/xenochimera/readytohatch)
/mob/living/carbon/human/proc/hatch()
set name = "Hatch"
@@ -359,71 +383,95 @@
if(!xc)
return
if(xc.revive_ready != REVIVING_DONE)
//Hwhat?
remove_verb(src, /mob/living/carbon/human/proc/hatch)
return
return //Hwhat?
var/confirm = tgui_alert(src, "Are you sure you want to hatch right now? This will be very obvious to anyone in view.", "Confirm Regeneration", list("Yes", "No"))
// Default is use internal record, even if closes menu
var/reload_slot = tgui_alert(src, "Regenerate from your current form, or from the appearance of your current character slot(This will not change your current species or traits.)", "Regenerate Form", list("Current Form", "From Slot"))
// Check if valid to load from this slot
var/from_slot = ""
from_slot = "You'll hatch using your current appearance"
if(reload_slot == "From Slot" && client)
if(client.prefs.species == SPECIES_PROTEAN) // Exploit protection
to_chat(src,span_warning("You cannot copy nanoform prosthetic limbs from this species. Please try another character."))
return
var/slot_is_synth = ((O_BRAIN in client.prefs.organ_data) && client.prefs.organ_data[O_BRAIN])
if(slot_is_synth && !isSynthetic()) // Prevents some pretty weird situations
to_chat(src,span_warning("Cannot apply character appearance. [slot_is_synth ? "The slot's character is synthetic." : "The slot's character is organic."] Slot must match the current body's synthetic state. Please try another character."))
return
from_slot = "You'll hatch using [client.prefs.real_name]'s appearance"
var/confirm = tgui_alert(src, "Are you sure you want to hatch right now? This will be very obvious to anyone in view. [from_slot]! Are you sure?", "Confirm Regeneration", list("Yes", "No"))
if(confirm == "Yes")
///This makes xenochimera shoot out their robotic limbs if they're not a FBP.
if(!isSynthetic()) //If we aren't repairing robotic limbs (FBP) we reject any robot limbs we have and kick them out!
for(var/O in organs_by_name)
var/obj/item/organ/external/organ = organs_by_name[O]
if(!istype(organ, /obj/item/organ/external))
continue
if(!organ.robotic)
continue
else
organ.removed()
///End of xenochimera limb rejection code.
//Dead when hatching
//var/sickness_duration = 10 MINUTES //CHOMPedit
var/has_braindamage = FALSE
if(stat == DEAD)
// var/sickness_duration = 10 MINUTES //CHOMPedit
//Reviving from ded takes extra nutrition - if it isn't provided from outside sources, it comes from you
if(!hasnutriment())
nutrition=nutrition * 0.75
// sickness_duration = 20 MINUTES //CHOMPedit
chimera_hatch()
// add_modifier(/datum/modifier/resleeving_sickness/chimera, sickness_duration) //CHOMPedit
//sickness_duration = 20 MINUTES //CHOMPedit
has_braindamage = TRUE
// Finalize!
remove_verb(src, /mob/living/carbon/human/proc/hatch)
clear_alert("hatch")
xc.chimera_hatch((reload_slot == "From Slot" && client))
visible_message(span_warning(span_huge("[src] rises to \his feet."))) //Bloody hell...
if(has_braindamage)
//add_modifier(/datum/modifier/resleeving_sickness/chimera, sickness_duration) //CHOMPedit
adjustBrainLoss(5) // if they're reviving from dead, they come back with 5 brainloss on top of whatever's unhealed.
visible_message(span_warning("<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
else
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)
/datum/component/xenochimera/proc/chimera_hatch(var/from_save_slot)
if(!owner)
return
remove_verb(src, /mob/living/carbon/human/proc/hatch)
to_chat(src, span_notice("Your new body awakens, bursting free from your old skin."))
remove_verb(owner, /mob/living/carbon/human/proc/hatch)
to_chat(owner, span_notice("Your new body awakens, bursting free from your old skin."))
//Modify and record values (half nutrition and braindamage)
var/old_nutrition = nutrition
var/braindamage = min(5, max(0, (brainloss-1) * 0.5)) //brainloss is tricky to heal and might take a couple of goes to get rid of completely.
var/uninjured=quickcheckuninjured()
//I did have special snowflake code, but this is easier. //It's also EXTREMELY BAD AND LETS THEM SAVEFILE HACK.
revive()
mutations.Remove(HUSK)
setBrainLoss(braindamage)
species.update_vore_belly_def_variant()
var/old_nutrition = owner.nutrition
var/braindamage = min(5, max(0, (owner.brainloss-1) * 0.5)) //brainloss is tricky to heal and might take a couple of goes to get rid of completely.
var/uninjured=owner.quickcheckuninjured()
trigger_revival(from_save_slot)
owner.mutations.Remove(HUSK)
owner.setBrainLoss(braindamage)
owner.species.update_vore_belly_def_variant()
if(!uninjured)
nutrition = old_nutrition * 0.5
owner.nutrition = old_nutrition * 0.5
//Drop everything
for(var/obj/item/W in src)
drop_from_inventory(W)
for(var/obj/item/W in owner)
owner.drop_from_inventory(W)
//Visual effects
var/T = get_turf(src)
var/blood_color = species.blood_color
var/flesh_color = species.flesh_color
var/T = get_turf(owner)
var/blood_color = owner.species.blood_color
var/flesh_color = owner.species.flesh_color
new /obj/effect/gibspawner/human/xenochimera(T, null, flesh_color, blood_color)
visible_message(span_danger("<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)
else //lower cost for doing a quick cosmetic revive
nutrition = old_nutrition * 0.9
owner.nutrition = old_nutrition * 0.9
//Unfreeze some things
does_not_breathe = FALSE
update_canmove()
stunned = 2
owner.does_not_breathe = FALSE
owner.update_canmove()
owner.AdjustStunned(2)
xc.revive_ready = world.time + 10 MINUTES //set the cooldown, Reduced this to 10 minutes, you're playing with fire if you're reviving that often.
revive_ready = world.time + 10 MINUTES //set the cooldown, Reduced this to 10 minutes, you're playing with fire if you're reviving that often.
/datum/modifier/resleeving_sickness/chimera //near identical to the regular version, just with different flavortexts
name = "imperfect regeneration"

View File

@@ -63,10 +63,6 @@ GLOBAL_LIST_EMPTY_TYPED(dna_genes_bad, /datum/gene/trait)
var/gender_specific_species_sounds = FALSE
var/species_sounds_male = "None"
var/species_sounds_female = "None"
var/grad_style = 0
var/r_grad = 0
var/g_grad = 0
var/b_grad = 0
var/custom_say
var/custom_ask
var/custom_whisper
@@ -74,55 +70,43 @@ GLOBAL_LIST_EMPTY_TYPED(dna_genes_bad, /datum/gene/trait)
var/list/custom_heat = list()
var/list/custom_cold = list()
var/digitigrade = 0 //0, Not FALSE, for future use as indicator for digitigrade types
var/custom_footstep = FOOTSTEP_MOB_SHOE
// New stuff
var/species = SPECIES_HUMAN
var/list/body_markings = list()
var/list/body_markings_genetic = list()
var/list/genetic_modifiers = list() // Modifiers with the MODIFIER_GENETIC flag are saved. Note that only the type is saved, not an instance.
// Make a copy of this strand.
// USE THIS WHEN COPYING STUFF OR YOU'LL GET CORRUPTION!
// Can you imagine, this used to be done manually, var by var?
/datum/dna/proc/Clone()
var/datum/dna/new_dna = new()
new_dna.unique_enzymes=unique_enzymes
new_dna.b_type=b_type
new_dna.real_name=real_name
new_dna.species=species
new_dna.body_markings=body_markings.Copy()
new_dna.base_species=base_species
new_dna.custom_species=custom_species
new_dna.species_traits=species_traits.Copy()
new_dna.blood_color=blood_color
new_dna.blood_reagents=blood_reagents
new_dna.scale_appearance = scale_appearance
new_dna.offset_override = offset_override
new_dna.synth_markings = synth_markings
new_dna.custom_speech_bubble = custom_speech_bubble
new_dna.species_sounds = species_sounds
new_dna.gender_specific_species_sounds = gender_specific_species_sounds
new_dna.species_sounds_male = species_sounds_male
new_dna.species_sounds_female = species_sounds_female
new_dna.grad_style = grad_style
new_dna.r_grad = r_grad
new_dna.g_grad = g_grad
new_dna.b_grad = b_grad
new_dna.custom_say=custom_say
new_dna.custom_ask=custom_ask
new_dna.custom_whisper=custom_whisper
new_dna.custom_exclaim=custom_exclaim
new_dna.custom_heat=custom_heat
new_dna.custom_cold=custom_cold
new_dna.digitigrade=src.digitigrade
var/list/body_markings_genetic = (body_markings - body_marking_nopersist_list)
new_dna.body_markings=body_markings_genetic.Copy()
for(var/b=1;b<=DNA_SE_LENGTH;b++)
new_dna.SE[b]=SE[b]
if(b<=DNA_UI_LENGTH)
new_dna.UI[b]=UI[b]
for(var/A in vars)
switch(A)
if(BLACKLISTED_COPY_VARS)
continue
if("dirtyUI")
dirtyUI=1
continue
if("dirtySE")
dirtySE=1
continue
if("body_markings")
var/list/body_markings_genetic = body_markings.Copy()
body_markings_genetic -= body_marking_nopersist_list
new_dna.vars[A] = body_markings_genetic
continue
if(islist(vars[A]))
var/list/L = vars[A]
new_dna.vars[A] = L.Copy()
continue
new_dna.vars[A] = vars[A]
// Finish up by updating enzymes/identity from our UI/SEs
new_dna.UpdateUI()
new_dna.UpdateSE()
return new_dna
///////////////////////////////////////
// UNIQUE IDENTITY
///////////////////////////////////////
@@ -138,6 +122,11 @@ GLOBAL_LIST_EMPTY_TYPED(dna_genes_bad, /datum/gene/trait)
if(!defer)
UpdateUI()
/**
* Beginning of mob to dna, and dna to mob transfer procs.
* Ensure that ResetUIFrom() and ApplyToMob() mirror each other. All vars should be read FROM the mob, and written back to it!
* ALL dna logic for reading from the mob, storing the dna data, and writing that dna data back to the mob should be here, and ONLY here.
*/
/datum/dna/proc/ResetUIFrom(var/mob/living/carbon/human/character)
// INITIALIZE!
ResetUI(1)
@@ -174,6 +163,11 @@ GLOBAL_LIST_EMPTY_TYPED(dna_genes_bad, /datum/gene/trait)
if(character.wing_style)
wing_style = wing_styles_list.Find(character.wing_style.type)
// Hairgrad
var/grad_style = 0
if(character.grad_style)
grad_style = GLOB.hair_gradients.Find(character.grad_style)
// Playerscale (This assumes list is sorted big->small)
var/size_multiplier = GLOB.player_sizes_list.len // If fail to find, take smallest
for(var/N in GLOB.player_sizes_list)
@@ -194,10 +188,6 @@ GLOBAL_LIST_EMPTY_TYPED(dna_genes_bad, /datum/gene/trait)
src.gender_specific_species_sounds = character.species.gender_specific_species_sounds
src.species_sounds_male = character.species.species_sounds_male
src.species_sounds_female = character.species.species_sounds_female
src.grad_style = character.grad_style
src.r_grad = character.r_grad
src.g_grad = character.g_grad
src.b_grad = character.b_grad
src.species_traits = character.species.traits.Copy()
src.custom_say = character.custom_say
src.custom_ask = character.custom_ask
@@ -206,6 +196,7 @@ GLOBAL_LIST_EMPTY_TYPED(dna_genes_bad, /datum/gene/trait)
src.custom_heat = character.custom_heat
src.custom_cold = character.custom_cold
src.digitigrade = character.digitigrade
src.custom_footstep = character.custom_footstep
// +1 to account for the none-of-the-above possibility
SetUIValueRange(DNA_UI_EAR_STYLE, ear_style + 1, ear_styles_list.len + 1, 1)
@@ -213,6 +204,7 @@ GLOBAL_LIST_EMPTY_TYPED(dna_genes_bad, /datum/gene/trait)
SetUIValueRange(DNA_UI_TAIL_STYLE, tail_style + 1, tail_styles_list.len + 1, 1)
SetUIValueRange(DNA_UI_PLAYERSCALE, size_multiplier, GLOB.player_sizes_list.len, 1)
SetUIValueRange(DNA_UI_WING_STYLE, wing_style + 1, wing_styles_list.len + 1, 1)
SetUIValueRange(DNA_UI_GRAD_STYLE, grad_style, GLOB.hair_gradients.len, 1)
SetUIValueRange(DNA_UI_TAIL_R, character.r_tail, 255, 1)
SetUIValueRange(DNA_UI_TAIL_G, character.g_tail, 255, 1)
@@ -253,6 +245,10 @@ GLOBAL_LIST_EMPTY_TYPED(dna_genes_bad, /datum/gene/trait)
SetUIValueRange(DNA_UI_EARS3_B, character.b_ears3, 255, 1)
SetUIValueRange(DNA_UI_EARS_ALPHA,character.a_ears, 255, 1)
SetUIValueRange(DNA_UI_GRAD_R, character.r_grad, 255, 1)
SetUIValueRange(DNA_UI_GRAD_G, character.g_grad, 255, 1)
SetUIValueRange(DNA_UI_GRAD_B, character.b_grad, 255, 1)
for(var/channel in 1 to DNA_UI_EARS_SECONDARY_COLOR_CHANNEL_COUNT)
var/offset = DNA_UI_EARS_SECONDARY_START + (channel - 1) * 3
var/list/read_rgb = ReadRGB(LAZYACCESS(character.ear_secondary_colors, channel) || "#ffffff")
@@ -294,6 +290,180 @@ GLOBAL_LIST_EMPTY_TYPED(dna_genes_bad, /datum/gene/trait)
UpdateUI()
/datum/dna/proc/ApplyToMob(var/mob/living/carbon/human/H)
////////////////////////////////////////////////////////////////////////////////
// Apply UIs to character
//Hair color
H.r_hair = GetUIValueRange(DNA_UI_HAIR_R, 255)
H.g_hair = GetUIValueRange(DNA_UI_HAIR_G, 255)
H.b_hair = GetUIValueRange(DNA_UI_HAIR_B, 255)
//Facial hair color
H.r_facial = GetUIValueRange(DNA_UI_BEARD_R, 255)
H.g_facial = GetUIValueRange(DNA_UI_BEARD_G, 255)
H.b_facial = GetUIValueRange(DNA_UI_BEARD_B, 255)
//Skin color (Tone for humans is seperate)
H.r_skin = GetUIValueRange(DNA_UI_SKIN_R, 255)
H.g_skin = GetUIValueRange(DNA_UI_SKIN_G, 255)
H.b_skin = GetUIValueRange(DNA_UI_SKIN_B, 255)
H.s_tone = 35 - GetUIValueRange(DNA_UI_SKIN_TONE, 220) // Value can be negative.
//Eye color
H.r_eyes = GetUIValueRange(DNA_UI_EYES_R, 255)
H.g_eyes = GetUIValueRange(DNA_UI_EYES_G, 255)
H.b_eyes = GetUIValueRange(DNA_UI_EYES_B, 255)
H.update_eyes()
//Hair gradient color
H.r_grad = GetUIValueRange(DNA_UI_GRAD_R, 255)
H.g_grad = GetUIValueRange(DNA_UI_GRAD_G, 255)
H.b_grad = GetUIValueRange(DNA_UI_GRAD_B, 255)
//Sex... Needs future support for properly handling things other then just male/female. UIs have the capability to do so!
if(H.gender != NEUTER)
if (GetUIState(DNA_UI_GENDER))
H.gender = FEMALE
else
H.gender = MALE
//Body markings
for(var/tag in body_markings)
var/obj/item/organ/external/E = H.organs_by_name[tag]
if(E)
var/list/marklist = body_markings[tag]
E.markings = marklist.Copy()
//Hair style
var/hair = GetUIValueRange(DNA_UI_HAIR_STYLE,hair_styles_list.len)
if((0 < hair) && (hair <= hair_styles_list.len))
H.h_style = hair_styles_list[hair]
//Facial Hair
var/beard = GetUIValueRange(DNA_UI_BEARD_STYLE,facial_hair_styles_list.len)
if((0 < beard) && (beard <= facial_hair_styles_list.len))
H.f_style = facial_hair_styles_list[beard]
// Ears
var/ears = GetUIValueRange(DNA_UI_EAR_STYLE, ear_styles_list.len + 1) - 1
if(ears < 1)
H.ear_style = null
else if((0 < ears) && (ears <= ear_styles_list.len))
H.ear_style = ear_styles_list[ear_styles_list[ears]]
var/ears_secondary = GetUIValueRange(DNA_UI_EAR_SECONDARY_STYLE, ear_styles_list.len + 1) - 1
if(ears_secondary < 1)
H.ear_secondary_style = null
else if((0 < ears_secondary) && (ears_secondary <= ear_styles_list.len))
H.ear_secondary_style = ear_styles_list[ear_styles_list[ears_secondary]]
// Ear Color
H.r_ears = GetUIValueRange(DNA_UI_EARS_R, 255)
H.g_ears = GetUIValueRange(DNA_UI_EARS_G, 255)
H.b_ears = GetUIValueRange(DNA_UI_EARS_B, 255)
H.r_ears2 = GetUIValueRange(DNA_UI_EARS2_R, 255)
H.g_ears2 = GetUIValueRange(DNA_UI_EARS2_G, 255)
H.b_ears2 = GetUIValueRange(DNA_UI_EARS2_B, 255)
H.r_ears3 = GetUIValueRange(DNA_UI_EARS3_R, 255)
H.g_ears3 = GetUIValueRange(DNA_UI_EARS3_G, 255)
H.b_ears3 = GetUIValueRange(DNA_UI_EARS3_B, 255)
H.a_ears = GetUIValueRange(DNA_UI_EARS_ALPHA, 255)
H.a_ears2 = GetUIValueRange(DNA_UI_EARS_SECONDARY_ALPHA, 255)
LAZYINITLIST(H.ear_secondary_colors)
H.ear_secondary_colors.len = max(length(H.ear_secondary_colors), DNA_UI_EARS_SECONDARY_COLOR_CHANNEL_COUNT)
for(var/channel in 1 to DNA_UI_EARS_SECONDARY_COLOR_CHANNEL_COUNT)
var/offset = DNA_UI_EARS_SECONDARY_START + (channel - 1) * 3
H.ear_secondary_colors[channel] = rgb(
GetUIValueRange(offset, 255),
GetUIValueRange(offset + 1, 255),
GetUIValueRange(offset + 2, 255),
)
//Tail
var/tail = GetUIValueRange(DNA_UI_TAIL_STYLE, tail_styles_list.len + 1) - 1
if(tail < 1)
H.tail_style = null
else if((0 < tail) && (tail <= tail_styles_list.len))
H.tail_style = tail_styles_list[tail_styles_list[tail]]
//Wing
var/wing = GetUIValueRange(DNA_UI_WING_STYLE, wing_styles_list.len + 1) - 1
if(wing < 1)
H.wing_style = null
else if((0 < wing) && (wing <= wing_styles_list.len))
H.wing_style = wing_styles_list[wing_styles_list[wing]]
//Wing Color
H.r_wing = GetUIValueRange(DNA_UI_WING_R, 255)
H.g_wing = GetUIValueRange(DNA_UI_WING_G, 255)
H.b_wing = GetUIValueRange(DNA_UI_WING_B, 255)
H.r_wing2 = GetUIValueRange(DNA_UI_WING2_R, 255)
H.g_wing2 = GetUIValueRange(DNA_UI_WING2_G, 255)
H.b_wing2 = GetUIValueRange(DNA_UI_WING2_B, 255)
H.r_wing3 = GetUIValueRange(DNA_UI_WING3_R, 255)
H.g_wing3 = GetUIValueRange(DNA_UI_WING3_G, 255)
H.b_wing3 = GetUIValueRange(DNA_UI_WING3_B, 255)
H.a_wing = GetUIValueRange(DNA_UI_WING_ALPHA, 255)
// Playerscale
var/size = GetUIValueRange(DNA_UI_PLAYERSCALE, GLOB.player_sizes_list.len)
if((0 < size) && (size <= GLOB.player_sizes_list.len))
H.resize(GLOB.player_sizes_list[GLOB.player_sizes_list[size]], TRUE, ignore_prefs = TRUE)
// Tail/Taur Color
H.r_tail = GetUIValueRange(DNA_UI_TAIL_R, 255)
H.g_tail = GetUIValueRange(DNA_UI_TAIL_G, 255)
H.b_tail = GetUIValueRange(DNA_UI_TAIL_B, 255)
H.r_tail2 = GetUIValueRange(DNA_UI_TAIL2_R, 255)
H.g_tail2 = GetUIValueRange(DNA_UI_TAIL2_G, 255)
H.b_tail2 = GetUIValueRange(DNA_UI_TAIL2_B, 255)
H.r_tail3 = GetUIValueRange(DNA_UI_TAIL3_R, 255)
H.g_tail3 = GetUIValueRange(DNA_UI_TAIL3_G, 255)
H.b_tail3 = GetUIValueRange(DNA_UI_TAIL3_B, 255)
H.a_tail = GetUIValueRange(DNA_UI_TAIL_ALPHA, 255)
// Hair gradiant
var/grad = GetUIValueRange(DNA_UI_GRAD_STYLE,GLOB.hair_gradients.len)
if((0 < grad) && (grad <= GLOB.hair_gradients.len))
H.grad_style = GLOB.hair_gradients[grad]
////////////////////////////////////////////////////////////////////////////////
// Custom species and other cosmetic vars
H.custom_species = custom_species
H.custom_say = custom_say
H.custom_ask = custom_ask
H.custom_whisper = custom_whisper
H.custom_exclaim = custom_exclaim
H.custom_speech_bubble = custom_speech_bubble
H.custom_heat = custom_heat
H.custom_cold = custom_cold
H.custom_footstep = custom_footstep
H.digitigrade = digitigrade
// If synths have character markings
H.synth_markings = synth_markings
// Scaling style
H.fuzzy = scale_appearance
H.offset_override = offset_override
////////////////////////////////////////////////////////////////////////////////
// Get a copy of the species datum to edit for ourselves
// anything that sets stuff in species MUST be done beyond here!
H.species.produceCopy(species_traits, H, base_species, FALSE) // Traitgenes edit - reset_dna flag required, or genes get reset on resleeve
// Update species blood with our blood color from dna!
H.species.blood_reagents = blood_reagents
H.species.blood_color = blood_color
H.species.species_sounds = species_sounds
H.species.gender_specific_species_sounds = gender_specific_species_sounds
H.species.species_sounds_male = species_sounds_male
H.species.species_sounds_female = species_sounds_female
/**
* End of mob to dna, and dna to mob transfer procs.
*/
// Set a DNA UI block's raw value.
/datum/dna/proc/SetUIValue(var/block,var/value,var/defer=0)
if (block<=0) return

View File

@@ -132,172 +132,29 @@
return output
// Use mob.UpdateAppearance()
/mob/proc/UpdateAppearance(var/list/UI=null)
return FALSE
// Simpler. Don't specify UI in order for the mob to use its own.
/mob/proc/UpdateAppearance(var/list/UI=null)
if(ishuman(src))
if(UI!=null)
src.dna.UI=UI
src.dna.UpdateUI()
dna.check_integrity()
var/mob/living/carbon/human/H = src
H.r_hair = dna.GetUIValueRange(DNA_UI_HAIR_R, 255)
H.g_hair = dna.GetUIValueRange(DNA_UI_HAIR_G, 255)
H.b_hair = dna.GetUIValueRange(DNA_UI_HAIR_B, 255)
/mob/living/carbon/human/UpdateAppearance(var/list/UI=null)
// Rebuild off UI arg if not null
if(UI!=null)
src.dna.UI=UI
src.dna.UpdateUI()
H.r_facial = dna.GetUIValueRange(DNA_UI_BEARD_R, 255)
H.g_facial = dna.GetUIValueRange(DNA_UI_BEARD_G, 255)
H.b_facial = dna.GetUIValueRange(DNA_UI_BEARD_B, 255)
// Setup dna
dna.check_integrity()
dna.ApplyToMob(src)
H.r_skin = dna.GetUIValueRange(DNA_UI_SKIN_R, 255)
H.g_skin = dna.GetUIValueRange(DNA_UI_SKIN_G, 255)
H.b_skin = dna.GetUIValueRange(DNA_UI_SKIN_B, 255)
// Apply dna changes to organ icons
force_update_organs()
force_update_limbs()
H.r_eyes = dna.GetUIValueRange(DNA_UI_EYES_R, 255)
H.g_eyes = dna.GetUIValueRange(DNA_UI_EYES_G, 255)
H.b_eyes = dna.GetUIValueRange(DNA_UI_EYES_B, 255)
H.update_eyes()
//H.update_body(0) //Done in force_update_limbs already
update_eyes()
update_hair()
H.s_tone = 35 - dna.GetUIValueRange(DNA_UI_SKIN_TONE, 220) // Value can be negative.
if(H.gender != NEUTER)
if (dna.GetUIState(DNA_UI_GENDER))
H.gender = FEMALE
else
H.gender = MALE
//Body markings
for(var/tag in dna.body_markings)
var/obj/item/organ/external/E = H.organs_by_name[tag]
if(E)
var/list/marklist = dna.body_markings[tag]
E.markings = marklist.Copy()
//Hair
var/hair = dna.GetUIValueRange(DNA_UI_HAIR_STYLE,hair_styles_list.len)
if((0 < hair) && (hair <= hair_styles_list.len))
H.h_style = hair_styles_list[hair]
//Facial Hair
var/beard = dna.GetUIValueRange(DNA_UI_BEARD_STYLE,facial_hair_styles_list.len)
if((0 < beard) && (beard <= facial_hair_styles_list.len))
H.f_style = facial_hair_styles_list[beard]
// Ears
var/ears = dna.GetUIValueRange(DNA_UI_EAR_STYLE, ear_styles_list.len + 1) - 1
if(ears < 1)
H.ear_style = null
else if((0 < ears) && (ears <= ear_styles_list.len))
H.ear_style = ear_styles_list[ear_styles_list[ears]]
var/ears_secondary = dna.GetUIValueRange(DNA_UI_EAR_SECONDARY_STYLE, ear_styles_list.len + 1) - 1
if(ears_secondary < 1)
H.ear_secondary_style = null
else if((0 < ears_secondary) && (ears_secondary <= ear_styles_list.len))
H.ear_secondary_style = ear_styles_list[ear_styles_list[ears_secondary]]
// Ear Color
H.r_ears = dna.GetUIValueRange(DNA_UI_EARS_R, 255)
H.g_ears = dna.GetUIValueRange(DNA_UI_EARS_G, 255)
H.b_ears = dna.GetUIValueRange(DNA_UI_EARS_B, 255)
H.r_ears2 = dna.GetUIValueRange(DNA_UI_EARS2_R, 255)
H.g_ears2 = dna.GetUIValueRange(DNA_UI_EARS2_G, 255)
H.b_ears2 = dna.GetUIValueRange(DNA_UI_EARS2_B, 255)
H.r_ears3 = dna.GetUIValueRange(DNA_UI_EARS3_R, 255)
H.g_ears3 = dna.GetUIValueRange(DNA_UI_EARS3_G, 255)
H.b_ears3 = dna.GetUIValueRange(DNA_UI_EARS3_B, 255)
H.a_ears = dna.GetUIValueRange(DNA_UI_EARS_ALPHA, 255)
H.a_ears2 = dna.GetUIValueRange(DNA_UI_EARS_SECONDARY_ALPHA, 255)
LAZYINITLIST(H.ear_secondary_colors)
H.ear_secondary_colors.len = max(length(H.ear_secondary_colors), DNA_UI_EARS_SECONDARY_COLOR_CHANNEL_COUNT)
for(var/channel in 1 to DNA_UI_EARS_SECONDARY_COLOR_CHANNEL_COUNT)
var/offset = DNA_UI_EARS_SECONDARY_START + (channel - 1) * 3
H.ear_secondary_colors[channel] = rgb(
dna.GetUIValueRange(offset, 255),
dna.GetUIValueRange(offset + 1, 255),
dna.GetUIValueRange(offset + 2, 255),
)
//Tail
var/tail = dna.GetUIValueRange(DNA_UI_TAIL_STYLE, tail_styles_list.len + 1) - 1
if(tail < 1)
H.tail_style = null
else if((0 < tail) && (tail <= tail_styles_list.len))
H.tail_style = tail_styles_list[tail_styles_list[tail]]
//Wing
var/wing = dna.GetUIValueRange(DNA_UI_WING_STYLE, wing_styles_list.len + 1) - 1
if(wing < 1)
H.wing_style = null
else if((0 < wing) && (wing <= wing_styles_list.len))
H.wing_style = wing_styles_list[wing_styles_list[wing]]
//Wing Color
H.r_wing = dna.GetUIValueRange(DNA_UI_WING_R, 255)
H.g_wing = dna.GetUIValueRange(DNA_UI_WING_G, 255)
H.b_wing = dna.GetUIValueRange(DNA_UI_WING_B, 255)
H.r_wing2 = dna.GetUIValueRange(DNA_UI_WING2_R, 255)
H.g_wing2 = dna.GetUIValueRange(DNA_UI_WING2_G, 255)
H.b_wing2 = dna.GetUIValueRange(DNA_UI_WING2_B, 255)
H.r_wing3 = dna.GetUIValueRange(DNA_UI_WING3_R, 255)
H.g_wing3 = dna.GetUIValueRange(DNA_UI_WING3_G, 255)
H.b_wing3 = dna.GetUIValueRange(DNA_UI_WING3_B, 255)
H.a_wing = dna.GetUIValueRange(DNA_UI_WING_ALPHA, 255)
// Playerscale
var/size = dna.GetUIValueRange(DNA_UI_PLAYERSCALE, GLOB.player_sizes_list.len)
if((0 < size) && (size <= GLOB.player_sizes_list.len))
H.resize(GLOB.player_sizes_list[GLOB.player_sizes_list[size]], TRUE, ignore_prefs = TRUE)
// Tail/Taur Color
H.r_tail = dna.GetUIValueRange(DNA_UI_TAIL_R, 255)
H.g_tail = dna.GetUIValueRange(DNA_UI_TAIL_G, 255)
H.b_tail = dna.GetUIValueRange(DNA_UI_TAIL_B, 255)
H.r_tail2 = dna.GetUIValueRange(DNA_UI_TAIL2_R, 255)
H.g_tail2 = dna.GetUIValueRange(DNA_UI_TAIL2_G, 255)
H.b_tail2 = dna.GetUIValueRange(DNA_UI_TAIL2_B, 255)
H.r_tail3 = dna.GetUIValueRange(DNA_UI_TAIL3_R, 255)
H.g_tail3 = dna.GetUIValueRange(DNA_UI_TAIL3_G, 255)
H.b_tail3 = dna.GetUIValueRange(DNA_UI_TAIL3_B, 255)
H.a_tail = dna.GetUIValueRange(DNA_UI_TAIL_ALPHA, 255)
// Technically custom_species is not part of the UI, but this place avoids merge problems.
H.custom_species = dna.custom_species
H.custom_say = dna.custom_say
H.custom_ask = dna.custom_ask
H.custom_whisper = dna.custom_whisper
H.custom_exclaim = dna.custom_exclaim
H.species.blood_color = dna.blood_color
H.fuzzy = dna.scale_appearance
H.offset_override = dna.offset_override
H.synth_markings = dna.synth_markings
H.custom_speech_bubble = dna.custom_speech_bubble
H.grad_style = dna.grad_style
H.r_grad = dna.r_grad
H.g_grad = dna.g_grad
H.b_grad = dna.b_grad
H.custom_heat = dna.custom_heat
H.custom_cold = dna.custom_cold
var/datum/species/S = H.species
S.produceCopy(dna.species_traits, H, dna.base_species, FALSE) // Traitgenes edit - reset_dna flag required, or genes get reset on resleeve
H.dna.blood_reagents = dna.blood_reagents
H.dna.blood_color = dna.blood_color
H.species.blood_reagents = H.dna.blood_reagents
H.species.blood_color = H.dna.blood_color
H.species.species_sounds = dna.species_sounds
H.species.gender_specific_species_sounds = dna.gender_specific_species_sounds
H.species.species_sounds_male = dna.species_sounds_male
H.species.species_sounds_female = dna.species_sounds_female
H.force_update_organs() //VOREStation Add - Gotta do this too
H.force_update_limbs()
//H.update_body(0) //VOREStation Edit - Done in force_update_limbs already
H.update_eyes()
H.update_hair()
return 1
else
return 0
return TRUE
/mob/living/carbon/human/proc/force_update_organs()
for(var/obj/item/organ/O as anything in organs + internal_organs)

View File

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

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."))
var/datum/absorbed_dna/newDNA = new(T.real_name, T.dna, T.species.name, T.languages, T.identifying_gender, T.flavor_texts, T.modifiers)
var/saved_dna = T.dna.Clone() /// Prevent transforming bugginess.
var/datum/absorbed_dna/newDNA = new(T.real_name, saved_dna, T.species.name, T.languages, T.identifying_gender, T.flavor_texts, T.modifiers)
absorbDNA(newDNA)
if(T.mind && T.mind.changeling)

View File

@@ -34,7 +34,7 @@
icon = 'icons/obj/cloning.dmi'
icon_state = "pod_0"
req_access = list(access_genetics) // For premature unlocking.
var/mob/living/occupant
VAR_PRIVATE/datum/weakref/weakref_occupant = null
var/heal_level = 20 // The clone is released once its health reaches this level.
var/heal_rate = 1
var/locked = 0
@@ -54,12 +54,25 @@
default_apply_parts()
update_icon()
/obj/machinery/clonepod/proc/set_occupant(var/mob/living/L)
SHOULD_NOT_OVERRIDE(TRUE)
if(!L)
weakref_occupant = null
return
weakref_occupant = WEAKREF(L)
/obj/machinery/clonepod/proc/get_occupant()
RETURN_TYPE(/mob/living)
SHOULD_NOT_OVERRIDE(TRUE)
return weakref_occupant?.resolve()
/obj/machinery/clonepod/attack_ai(mob/user as mob)
add_hiddenprint(user)
return attack_hand(user)
/obj/machinery/clonepod/attack_hand(mob/user as mob)
var/mob/living/occupant = get_occupant()
if((isnull(occupant)) || (stat & NOPOWER))
return
if((!isnull(occupant)) && (occupant.stat != 2))
@@ -68,27 +81,27 @@
return
//Start growing a human clone in the pod!
/obj/machinery/clonepod/proc/growclone(var/datum/dna2/record/R)
/obj/machinery/clonepod/proc/growclone(var/datum/transhuman/body_record/BR)
if(mess || attempting)
return 0
var/datum/mind/clonemind = locate(R.mind)
var/datum/mind/clonemind = locate(BR.mydna.mind)
if(!istype(clonemind, /datum/mind)) //not a mind
return 0
if(clonemind.current && clonemind.current.stat != DEAD) //mind is associated with a non-dead body
return 0
if(clonemind.active) //somebody is using that mind
if(ckey(clonemind.key) != R.ckey)
if(ckey(clonemind.key) != BR.ckey)
return 0
else
for(var/mob/observer/dead/G in player_list)
if(G.ckey == R.ckey)
if(G.ckey == BR.ckey)
if(G.can_reenter_corpse)
break
else
return 0
for(var/modifier_type in R.genetic_modifiers) //Can't be cloned, even if they had a previous scan
for(var/modifier_type in BR.genetic_modifiers) //Can't be cloned, even if they had a previous scan
if(istype(modifier_type, /datum/modifier/no_clone))
return 0
@@ -102,23 +115,20 @@
spawn(30)
eject_wait = 0
var/mob/living/carbon/human/H = new /mob/living/carbon/human(src, R.dna.species)
occupant = H
if(!R.dna.real_name) //to prevent null names
R.dna.real_name = "clone ([rand(0,999)])"
H.real_name = R.dna.real_name
H.gender = R.gender
//Get the clone body ready, let's calculate their health so the pod doesn't immediately eject them!!!
var/mob/living/carbon/human/H = BR.produce_human_mob(src,FALSE, FALSE, "clone ([rand(0,999)])")
SEND_SIGNAL(H, COMSIG_HUMAN_DNA_FINALIZED)
//Get the clone body ready
H.adjustCloneLoss(150) // New damage var so you can't eject a clone early then stab them to abuse the current damage system --NeoFite
var/damage_to_deal = H.getMaxHealth() * 1.5 //If you have 100, you get 150. Have 200? Get 300. 25hp? get 37.5
H.adjustCloneLoss(damage_to_deal) // New damage var so you can't eject a clone early then stab them to abuse the current damage system --NeoFite
H.Paralyse(4)
//Here let's calculate their health so the pod doesn't immediately eject them!!!
H.updatehealth()
H.set_cloned_appearance()
// Move mind to body along with key
clonemind.transfer_to(H)
H.ckey = R.ckey
H.ckey = BR.ckey
to_chat(H, span_warning(span_bold("Consciousness slowly creeps over you as your body regenerates.") + "<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?"))))
@@ -127,48 +137,29 @@
update_antag_icons(H.mind)
// -- End mode specific stuff
if(!R.dna)
H.dna = new /datum/dna()
qdel_swap(H.dna, new /datum/dna())
else
qdel_swap(H.dna, R.dna)
H.UpdateAppearance()
H.sync_dna_traits(FALSE) // Traitgenes Sync traits to genetics if needed
H.sync_organ_dna()
H.initialize_vessel()
H.set_cloned_appearance()
update_icon()
// A modifier is added which makes the new clone be unrobust.
// Upgraded cloners can reduce the time of the modifier, up to 80%
var/modifier_lower_bound = 25 MINUTES
var/modifier_upper_bound = 40 MINUTES
// Upgraded cloners can reduce the time of the modifier, up to 80%
var/clone_sickness_length = abs(((heal_level - 20) / 100 ) - 1)
clone_sickness_length = between(0.2, clone_sickness_length, 1.0) // Caps it off just incase.
modifier_lower_bound = round(modifier_lower_bound * clone_sickness_length, 1)
modifier_upper_bound = round(modifier_upper_bound * clone_sickness_length, 1)
H.add_modifier(H.species.cloning_modifier, rand(modifier_lower_bound, modifier_upper_bound))
// Modifier that doesn't do anything.
H.add_modifier(/datum/modifier/cloned)
// This is really stupid.
for(var/modifier_type in R.genetic_modifiers)
H.add_modifier(modifier_type)
for(var/datum/language/L in R.languages)
H.add_language(L.name)
H.flavor_texts = R.flavor.Copy()
H.suiciding = 0
// Finished!
update_icon()
set_occupant(H)
attempting = 0
return 1
//Grow clones to maturity then kick them out. FREELOADERS
/obj/machinery/clonepod/process()
var/mob/living/occupant = get_occupant()
if(stat & NOPOWER) //Autoeject if power is lost
if(occupant)
locked = 0
@@ -210,7 +201,7 @@
return
else if((!occupant) || (occupant.loc != src))
occupant = null
set_occupant(null)
if(locked)
locked = 0
return
@@ -219,6 +210,7 @@
//Let's unlock this early I guess. Might be too early, needs tweaking.
/obj/machinery/clonepod/attackby(obj/item/W as obj, mob/user as mob)
var/mob/living/occupant = get_occupant()
if(isnull(occupant))
if(default_deconstruction_screwdriver(user, W))
return
@@ -272,7 +264,7 @@
..()
/obj/machinery/clonepod/emag_act(var/remaining_charges, var/mob/user)
if(isnull(occupant))
if(isnull(get_occupant()))
return
to_chat(user, "You force an emergency ejection.")
locked = 0
@@ -301,7 +293,7 @@
heal_level = max(min((efficiency * 15) + 10, 100), MINIMUM_HEAL_LEVEL)
/obj/machinery/clonepod/proc/get_completion()
. = (100 * ((occupant.health + 100) / (heal_level + 100)))
. = (100 * ((get_occupant().health + 100) / (heal_level + 100)))
/obj/machinery/clonepod/verb/eject()
set name = "Eject Cloner"
@@ -324,20 +316,21 @@
update_icon()
return
var/mob/living/occupant = get_occupant()
if(!(occupant))
return
if(occupant.client)
occupant.client.eye = occupant.client.mob
occupant.client.perspective = MOB_PERSPECTIVE
occupant.loc = src.loc
occupant.forceMove(get_turf(src))
eject_wait = 0 //If it's still set somehow.
if(ishuman(occupant)) //Need to be safe.
var/mob/living/carbon/human/patient = occupant
if(!(patient.species.flags & NO_DNA)) //If, for some reason, someone makes a genetically-unalterable clone, let's not make them permanently disabled.
domutcheck(occupant) //Waiting until they're out before possible transforming.
occupant.UpdateAppearance()
occupant = null
set_occupant(null)
update_icon()
return
@@ -400,6 +393,7 @@
return 0
/obj/machinery/clonepod/proc/malfunction()
var/mob/living/occupant = get_occupant()
if(occupant)
connected_message("Critical Error!")
mess = 1
@@ -421,21 +415,21 @@
switch(severity)
if(1.0)
for(var/atom/movable/A as mob|obj in src)
A.loc = src.loc
A.forceMove(get_turf(src))
ex_act(severity)
qdel(src)
return
if(2.0)
if(prob(50))
for(var/atom/movable/A as mob|obj in src)
A.loc = src.loc
A.forceMove(get_turf(src))
ex_act(severity)
qdel(src)
return
if(3.0)
if(prob(25))
for(var/atom/movable/A as mob|obj in src)
A.loc = src.loc
A.forceMove(get_turf(src))
ex_act(severity)
qdel(src)
return
@@ -444,7 +438,7 @@
/obj/machinery/clonepod/update_icon()
..()
icon_state = "pod_0"
if(occupant && !(stat & NOPOWER))
if(get_occupant() && !(stat & NOPOWER))
icon_state = "pod_1"
else if(mess)
icon_state = "pod_g"

View File

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

View File

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

View File

@@ -135,6 +135,8 @@
avatar.sync_organ_dna()
avatar.initialize_vessel()
SEND_SIGNAL(avatar, COMSIG_HUMAN_DNA_FINALIZED)
var/newname = sanitize(tgui_input_text(avatar, "Your mind feels foggy. You're certain your name is [occupant.real_name], but it could also be [avatar.name]. Would you like to change it to something else?", "Name change", null, MAX_NAME_LEN), MAX_NAME_LEN)
if (newname)
avatar.real_name = newname

View File

@@ -314,6 +314,8 @@
avatar.sync_organ_dna()
avatar.initialize_vessel()
SEND_SIGNAL(avatar, COMSIG_HUMAN_DNA_FINALIZED)
if(tf)
var/mob/living/new_form = avatar.transform_into_mob(tf, TRUE) // No need to check prefs when the occupant already chose to transform.
if(isliving(new_form)) // Make sure the mob spawned properly.

View File

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

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)))
new_character.add_language(lang)
SEND_SIGNAL(new_character, COMSIG_HUMAN_DNA_FINALIZED)
new_character.regenerate_icons()
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)))
new_character.add_language(lang)
SEND_SIGNAL(new_character, COMSIG_HUMAN_DNA_FINALIZED)
//If desired, apply equipment.
if(equipment)
if(charjob)

View File

@@ -533,7 +533,7 @@ var/list/preferences_datums = list()
else
var/bodytype
var/datum/species/selected_species = GLOB.all_species[species]
if(custom_base)
if(selected_species.selects_bodytype && custom_base) //Everyone technically has custom_base set to HUMAN, but only some species actually select it.
bodytype = custom_base
else
bodytype = selected_species.get_bodytype()
@@ -544,7 +544,8 @@ var/list/preferences_datums = list()
for(var/N in character.organs_by_name)
var/obj/item/organ/external/O = character.organs_by_name[N]
O.markings.Cut()
if(O)
O.markings.Cut()
var/priority = 0
for(var/M in body_markings)
@@ -554,6 +555,7 @@ var/list/preferences_datums = list()
for(var/BP in mark_datum.body_parts)
var/obj/item/organ/external/O = character.organs_by_name[BP]
if(O)
if(!islist(body_markings[M][BP])) continue
O.markings[M] = list("color" = body_markings[M][BP]["color"], "datum" = mark_datum, "priority" = priority, "on" = body_markings[M][BP]["on"])
character.markings_len = priority
@@ -581,7 +583,6 @@ var/list/preferences_datums = list()
character.fuzzy = fuzzy
character.offset_override = offset_override
character.voice_freq = voice_freq
character.size_multiplier = size_multiplier
character.resize(size_multiplier, animate = FALSE, ignore_prefs = TRUE)
var/list/traits_to_copy = list(/datum/trait/neutral/tall,
@@ -616,17 +617,16 @@ var/list/preferences_datums = list()
var/datum/species/selected_species = GLOB.all_species[species]
var/bodytype_selected
if(custom_base)
if(selected_species.selects_bodytype && custom_base)
bodytype_selected = custom_base
else
bodytype_selected = selected_species.get_bodytype(character)
character.dna.base_species = bodytype_selected
character.species.base_species = bodytype_selected
character.species.icobase = character.species.get_icobase()
character.species.deform = character.species.get_icobase(get_deform = TRUE)
character.species.vanity_base_fit = bodytype_selected
if (istype(character.species, /datum/species/shapeshifter))
if(istype(character.species, /datum/species/shapeshifter))
wrapped_species_by_ref["\ref[character]"] = bodytype_selected
character.custom_species = custom_species

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
/datum/species/proc/copy_variables(var/datum/species/S, var/list/whitelist)
//List of variables to ignore, trying to copy type will runtime.
//var/list/blacklist = list("type", "loc", "client", "ckey")
//var/list/blacklist = list(BLACKLISTED_COPY_VARS)
//Makes thorough copy of species datum.
for(var/i in whitelist)
if(!(i in S.vars)) //Don't copy incompatible vars.
continue
//if(!(i in S.vars)) // This check SOUNDS like a good idea, until you realize it loops over every var in base datum + species datum + byond builtin vars for EACH var in the whitelist. All the vars in whitelist are in the base species datum anyway, so this is unneeded.
// continue
if(S.vars[i] != vars[i] && !islist(vars[i])) //If vars are same, no point in copying.
S.vars[i] = vars[i]

View File

@@ -109,16 +109,8 @@ var/datum/species/shapeshifter/promethean/prometheans
cold_discomfort_strings = list("You feel too cool.")
inherent_verbs = list(
/mob/living/carbon/human/proc/shapeshifter_select_shape,
/mob/living/carbon/human/proc/shapeshifter_select_colour,
/mob/living/carbon/human/proc/shapeshifter_select_hair,
/mob/living/carbon/human/proc/shapeshifter_select_hair_colors,
/mob/living/carbon/human/proc/shapeshifter_select_gender,
/mob/living/carbon/human/proc/innate_shapeshifting,
/mob/living/carbon/human/proc/regenerate,
/mob/living/carbon/human/proc/shapeshifter_select_wings,
/mob/living/carbon/human/proc/shapeshifter_select_tail,
/mob/living/carbon/human/proc/shapeshifter_select_ears,
/mob/living/carbon/human/proc/shapeshifter_select_secondary_ears,
/mob/living/carbon/human/proc/prommie_blobform,
/mob/living/proc/set_size,
/mob/living/carbon/human/proc/promethean_select_opaqueness,
@@ -410,3 +402,9 @@ var/datum/species/shapeshifter/promethean/prometheans
return
else
prommie_intoblob()
/mob/living/carbon/human/proc/innate_shapeshifting()
set name = "Transform Appearance"
set category = "Abilities.Superpower"
var/datum/tgui_module/appearance_changer/innate/I = new(src, src)
I.tgui_interact(src)

View File

@@ -679,7 +679,12 @@
return 1
return 0
/// Revives a body using the client's preferences if human
/mob/living/proc/revive()
revival_healing_action()
/// Performs the actual healing of Aheal, seperate from revive() because it does not use client prefs. Will not heal everything, and expects to be called through revive() or with a bodyrecord doing a respawn/revive.
/mob/living/proc/revival_healing_action()
rejuvenate()
if(buckled)
buckled.unbuckle_mob()
@@ -701,6 +706,8 @@
if(ai_holder) // AI gets told to sleep when killed. Since they're not dead anymore, wake it up.
ai_holder.go_wake()
SEND_SIGNAL(src, COMSIG_HUMAN_DNA_FINALIZED)
/mob/living/proc/rejuvenate()
if(reagents)
reagents.clear_reagents()

View File

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

View File

@@ -37,7 +37,7 @@ GLOBAL_LIST_INIT(fancy_sprite_accessory_color_channel_names, list("Primary", "Se
var/gender = NEUTER
// Restrict some styles to specific species. Default to all species to avoid runtimes in character creator.
var/list/species_allowed = list(SPECIES_HUMAN, SPECIES_SKRELL, SPECIES_UNATHI, SPECIES_TAJARAN, SPECIES_TESHARI, SPECIES_NEVREAN, SPECIES_AKULA, SPECIES_SERGAL, SPECIES_FENNEC, SPECIES_ZORREN_HIGH, SPECIES_VULPKANIN, SPECIES_XENOCHIMERA, SPECIES_XENOHYBRID, SPECIES_VASILISSAN, SPECIES_RAPALA, SPECIES_PROTEAN, SPECIES_ALRAUNE, SPECIES_WEREBEAST, SPECIES_SHADEKIN, SPECIES_SHADEKIN_CREW, SPECIES_ALTEVIAN, SPECIES_LLEILL, SPECIES_HANNER, SPECIES_ZADDAT, SPECIES_SPARKLE)
var/list/species_allowed = list(SPECIES_HUMAN, SPECIES_SKRELL, SPECIES_UNATHI, SPECIES_TAJARAN, SPECIES_TESHARI, SPECIES_NEVREAN, SPECIES_AKULA, SPECIES_SERGAL, SPECIES_FENNEC, SPECIES_ZORREN_HIGH, SPECIES_VULPKANIN, SPECIES_XENOCHIMERA, SPECIES_XENOHYBRID, SPECIES_VASILISSAN, SPECIES_RAPALA, SPECIES_PROTEAN, SPECIES_ALRAUNE, SPECIES_WEREBEAST, SPECIES_SHADEKIN, SPECIES_SHADEKIN_CREW, SPECIES_ALTEVIAN, SPECIES_LLEILL, SPECIES_HANNER, SPECIES_ZADDAT, SPECIES_SPARKLE, SPECIES_PROMETHEAN)
// Whether or not the accessory can be affected by colouration
var/do_colouration = 1

View File

@@ -15,7 +15,7 @@
var/extra_overlay2
var/desc = DEVELOPER_WARNING_NAME
em_block = TRUE
species_allowed = list(SPECIES_HUMAN, SPECIES_SKRELL, SPECIES_UNATHI, SPECIES_TAJARAN, SPECIES_TESHARI, SPECIES_NEVREAN, SPECIES_AKULA, SPECIES_SERGAL, SPECIES_FENNEC, SPECIES_ZORREN_HIGH, SPECIES_VULPKANIN, SPECIES_XENOCHIMERA, SPECIES_XENOHYBRID, SPECIES_VASILISSAN, SPECIES_RAPALA, SPECIES_PROTEAN, SPECIES_ALRAUNE, SPECIES_WEREBEAST, SPECIES_SHADEKIN, SPECIES_SHADEKIN_CREW, SPECIES_ALTEVIAN, SPECIES_LLEILL, SPECIES_HANNER, SPECIES_SPARKLE)
species_allowed = list(SPECIES_HUMAN, SPECIES_SKRELL, SPECIES_UNATHI, SPECIES_TAJARAN, SPECIES_TESHARI, SPECIES_NEVREAN, SPECIES_AKULA, SPECIES_SERGAL, SPECIES_FENNEC, SPECIES_ZORREN_HIGH, SPECIES_VULPKANIN, SPECIES_XENOCHIMERA, SPECIES_XENOHYBRID, SPECIES_VASILISSAN, SPECIES_RAPALA, SPECIES_PROTEAN, SPECIES_ALRAUNE, SPECIES_WEREBEAST, SPECIES_SHADEKIN, SPECIES_SHADEKIN_CREW, SPECIES_ALTEVIAN, SPECIES_LLEILL, SPECIES_HANNER, SPECIES_SPARKLE, SPECIES_PROMETHEAN)
/**
* Gets the number of color channels we have.

View File

@@ -21,7 +21,7 @@
var/extra_overlay_w // Flapping state for extra overlay
var/extra_overlay2_w
species_allowed = list(SPECIES_HUMAN, SPECIES_SKRELL, SPECIES_UNATHI, SPECIES_TAJARAN, SPECIES_TESHARI, SPECIES_NEVREAN, SPECIES_AKULA, SPECIES_SERGAL, SPECIES_FENNEC, SPECIES_ZORREN_HIGH, SPECIES_VULPKANIN, SPECIES_XENOCHIMERA, SPECIES_XENOHYBRID, SPECIES_VASILISSAN, SPECIES_RAPALA, SPECIES_PROTEAN, SPECIES_ALRAUNE, SPECIES_WEREBEAST, SPECIES_SHADEKIN, SPECIES_SHADEKIN_CREW, SPECIES_ALTEVIAN, SPECIES_LLEILL, SPECIES_HANNER, SPECIES_SPARKLE)
species_allowed = list(SPECIES_HUMAN, SPECIES_SKRELL, SPECIES_UNATHI, SPECIES_TAJARAN, SPECIES_TESHARI, SPECIES_NEVREAN, SPECIES_AKULA, SPECIES_SERGAL, SPECIES_FENNEC, SPECIES_ZORREN_HIGH, SPECIES_VULPKANIN, SPECIES_XENOCHIMERA, SPECIES_XENOHYBRID, SPECIES_VASILISSAN, SPECIES_RAPALA, SPECIES_PROTEAN, SPECIES_ALRAUNE, SPECIES_WEREBEAST, SPECIES_SHADEKIN, SPECIES_SHADEKIN_CREW, SPECIES_ALTEVIAN, SPECIES_LLEILL, SPECIES_HANNER, SPECIES_SPARKLE, SPECIES_PROMETHEAN)
var/wing_offset = 0
var/multi_dir = FALSE // Does it use different sprites at different layers? _front will be added for sprites on low layer, _back to high layer

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)
if(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()
// TODO - Reference how xenochimera component handles revival from bodyrecord in the future.
// This requires a promie/protean component for transformation and regeneration.
// This shouldn't use a brain mob for caching dna. That's what BRs are for.
var/datum/dna2/record/R = new /datum/dna2/record()
qdel_swap(R.dna, brainmob.dna.Clone())
R.ckey = brainmob.ckey
@@ -293,8 +296,10 @@ GLOBAL_LIST_BOILERPLATE(all_brain_organs, /obj/item/organ/internal/brain)
for(var/datum/language/L in R.languages)
H.add_language(L.name)
H.flavor_texts = R.flavor.Copy()
qdel(R.dna)
qdel(R)
SEND_SIGNAL(H, COMSIG_HUMAN_DNA_FINALIZED)
qdel(R) // Record already deletes dna
qdel(src)
return 1

View File

@@ -151,9 +151,9 @@
if(comp.revive_ready >= 1) // if it's not reviving, start doing so
comp.revive_ready = REVIVING_READY // overrides the normal cooldown
H.visible_message(span_info("[H] shudders briefly, then relaxes, faint movements stirring within."))
H.chimera_regenerate()
comp.chimera_regenerate()
else if(comp.revive_ready == REVIVING_DONE)// already reviving, check if they're ready to hatch
H.chimera_hatch()
comp.chimera_hatch()
H.visible_message(span_danger(span_huge("[H] violently convulses and then bursts open, revealing a new, intact copy in the pool of viscera."))) // Hope you were wearing waterproofs, doc...
H.adjustBrainLoss(10) // they're reviving from dead, so take 10 brainloss
else //they're already reviving but haven't hatched. Give a little message to tell them to wait.

View File

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

View File

@@ -148,16 +148,17 @@
var/list/clonepods = list()
for(var/obj/machinery/clonepod/transhuman/pod in pods)
var/status = "idle"
var/mob/living/occupant = pod.get_occupant()
if(pod.mess)
status = "mess"
else if(pod.occupant && !(pod.stat & NOPOWER))
else if(occupant && !(pod.stat & NOPOWER))
status = "cloning"
clonepods += list(list(
"pod" = REF(pod),
"name" = sanitize(capitalize(pod.name)),
"biomass" = pod.get_biomass(),
"status" = status,
"progress" = (pod.occupant && pod.occupant.stat != DEAD) ? pod.get_completion() : 0
"progress" = (occupant && occupant.stat != DEAD) ? pod.get_completion() : 0
))
data["pods"] = clonepods
@@ -177,8 +178,8 @@
resleevers += list(list(
"sleever" = REF(resleever),
"name" = sanitize(capitalize(resleever.name)),
"occupied" = !!resleever.occupant,
"occupant" = resleever.occupant ? resleever.occupant.real_name : "None"
"occupied" = !!resleever.get_occupant(),
"occupant" = resleever.get_occupant() ? resleever.get_occupant().real_name : "None"
))
data["sleevers"] = resleevers
@@ -322,7 +323,7 @@
return
//Already doing someone.
if(pod.occupant)
if(pod.get_occupant())
set_temp("Error: Growpod is currently occupied.", "danger")
active_br = null
return
@@ -375,26 +376,26 @@
switch(mode)
if(1) //Body resleeving
//No body to sleeve into.
if(!sleever.occupant)
if(!sleever.get_occupant())
set_temp("Error: Resleeving pod is not occupied.", "danger")
active_mr = null
return
//OOC body lock thing.
if(sleever.occupant.resleeve_lock && active_mr.ckey != sleever.occupant.resleeve_lock)
if(sleever.get_occupant().resleeve_lock && active_mr.ckey != sleever.get_occupant().resleeve_lock)
set_temp("Error: Mind incompatible with body.", "danger")
active_mr = null
return
var/list/subtargets = list()
for(var/mob/living/carbon/human/H in sleever.occupant)
for(var/mob/living/carbon/human/H in sleever.get_occupant())
if(H.resleeve_lock && active_mr.ckey != H.resleeve_lock)
continue
subtargets += H
if(subtargets.len)
var/oc_sanity = sleever.occupant
var/oc_sanity = sleever.get_occupant()
override = tgui_input_list(ui.user,"Multiple bodies detected. Select target for resleeving of [active_mr.mindname] manually. Sleeving of primary body is unsafe with sub-contents, and is not listed.", "Resleeving Target", subtargets)
if(!override || oc_sanity != sleever.occupant || !(override in sleever.occupant))
if(!override || oc_sanity != sleever.get_occupant() || !(override in sleever.get_occupant()))
set_temp("Error: Target selection aborted.", "danger")
active_mr = null
return
@@ -558,7 +559,7 @@
if(!selected_sleever)
can_sleeve_active = FALSE
set_temp("Error: Cannot sleeve due to no selected sleever.", "danger")
if(selected_sleever && !selected_sleever.occupant)
if(selected_sleever && !selected_sleever.get_occupant())
can_sleeve_active = FALSE
set_temp("Error: Cannot sleeve due to lack of sleever occupant.", "danger")
else

View File

@@ -59,6 +59,7 @@
if(!designer_gui)
designer_gui = new(src, null)
designer_gui.linked_body_design_console = WEAKREF(src)
CallAsync(designer_gui, TYPE_PROC_REF(/datum/tgui_module/appearance_changer,jiggle_map))
if(!designer_gui.owner)
designer_gui.make_fake_owner()
selected_record = FALSE

View File

@@ -134,6 +134,11 @@
speciesname = M.custom_species ? M.custom_species : null
bodygender = M.gender
body_oocnotes = M.ooc_notes
body_ooclikes = M.ooc_notes_likes
body_oocdislikes = M.ooc_notes_dislikes
body_oocfavs = M.ooc_notes_favs
body_oocmaybes = M.ooc_notes_maybes
body_oocstyle = M.ooc_notes_style
sizemult = M.size_multiplier
weight = M.weight
aflags = M.appearance_flags
@@ -204,7 +209,6 @@
if(add_to_db)
SStranscore.add_body(src, database_key = database_key)
/**
* Make a deep copy of this record so it can be saved on a disk without modifications
* to the original affecting the copy.
@@ -214,28 +218,195 @@
/datum/transhuman/body_record/proc/init_from_br(var/datum/transhuman/body_record/orig)
ASSERT(!QDELETED(orig))
ASSERT(istype(orig))
src.mydna = new ()
qdel_swap(src.mydna.dna, orig.mydna.dna.Clone())
src.mydna.ckey = orig.mydna.ckey
src.mydna.id = orig.mydna.id
src.mydna.name = orig.mydna.name
src.mydna.types = orig.mydna.types
src.mydna.flavor = orig.mydna.flavor.Copy()
src.ckey = orig.ckey
src.locked = orig.locked
src.client_ref = orig.client_ref
src.mind_ref = orig.mind_ref
src.synthetic = orig.synthetic
src.speciesname = orig.speciesname
src.bodygender = orig.bodygender
src.body_oocnotes = orig.body_oocnotes
src.body_ooclikes = orig.body_ooclikes
src.body_oocdislikes = orig.body_oocdislikes
src.limb_data = orig.limb_data.Copy()
src.organ_data = orig.organ_data.Copy()
src.genetic_modifiers = orig.genetic_modifiers.Copy()
src.toocomplex = orig.toocomplex
src.sizemult = orig.sizemult
src.aflags = orig.aflags
src.breath_type = orig.breath_type
src.weight = orig.weight
for(var/A in vars)
switch(A)
if(BLACKLISTED_COPY_VARS)
continue
if("mydna")
mydna = orig.mydna.copy()
continue
if(islist(vars[A]))
var/list/L = orig.vars[A]
vars[A] = L.Copy()
continue
vars[A] = orig.vars[A]
/**
* Spawning a body was once left entirely up to the machine doing it, but bodies are massivley complex
* objects, and doing it this way lead to huge amounts of copypasted code to do the same thing.
* If you want to spawn a body from a BR, please use these...
*/
/// The core of resleeving, creates a mob based on the current record
/datum/transhuman/body_record/proc/produce_human_mob(var/location, var/is_synthfab, var/force_unlock, var/backup_name)
// These are broken up into steps, otherwise the proc gets massive and hard to read.
var/mob/living/carbon/human/H = internal_producebody(location,backup_name)
internal_producebody_handlesleevelock(H,force_unlock)
internal_producebody_updatelimbandorgans(H)
internal_producebody_updatednastate(H,is_synthfab)
internal_producebody_virgoOOC(H)
internal_producebody_misc(H)
return H
/// Creates a human mob with the correct species, name, and a stable state.
/datum/transhuman/body_record/proc/internal_producebody(var/location,var/backup_name)
SHOULD_NOT_OVERRIDE(TRUE)
PRIVATE_PROC(TRUE)
var/mob/living/carbon/human/H = new /mob/living/carbon/human(location, mydna.dna.species)
if(!mydna.dna.real_name)
mydna.dna.real_name = backup_name
H.real_name = mydna.dna.real_name
H.name = H.real_name
for(var/datum/language/L in mydna.languages)
H.add_language(L.name)
H.suiciding = 0
H.losebreath = 0
H.mind = null
return H
/// Sets the new body's sleevelock status, to prevent impersonation by transfering an incorrect mind.
/datum/transhuman/body_record/proc/internal_producebody_handlesleevelock(var/mob/living/carbon/human/H,var/force_unlock)
SHOULD_NOT_OVERRIDE(TRUE)
PRIVATE_PROC(TRUE)
if(locked && !force_unlock)
if(ckey)
H.resleeve_lock = ckey
else
// Ensure even body scans without an attached ckey respect locking
H.resleeve_lock = "@badckey"
/// Either converts limbs to robotics or prosthetic states, or removes them entirely based off record.
/datum/transhuman/body_record/proc/internal_producebody_updatelimbandorgans(var/mob/living/carbon/human/H,var/is_synthfab)
SHOULD_NOT_OVERRIDE(TRUE)
PRIVATE_PROC(TRUE)
//Fix the external organs
for(var/part in limb_data)
var/status = limb_data[part]
if(status == null) continue //Species doesn't have limb? Child of amputated limb?
var/obj/item/organ/external/O = H.organs_by_name[part]
if(!O) continue //Not an organ. Perhaps another amputation removed it already.
if(status == 1) //Normal limbs
continue
else if(status == 0) //Missing limbs
O.remove_rejuv()
else if(status) //Anything else is a manufacturer
if(!is_synthfab)
O.remove_rejuv() //Don't robotize them, leave them removed so robotics can attach a part.
else
O.robotize(status)
//Then the internal organs
for(var/part in organ_data)
var/status = organ_data[part]
if(status == null) continue //Species doesn't have organ? Child of missing part?
var/obj/item/organ/I = H.internal_organs_by_name[part]
if(!I) continue//Not an organ. Perhaps external conversion changed it already?
if(status == 0) //Normal organ
continue
else if(status == 1) //Assisted organ
I.mechassist()
else if(status == 2) //Mechanical organ
I.robotize()
else if(status == 3) //Digital organ
I.digitize()
/// Transfers dna data to mob, and reinits traits and appearance from it
/datum/transhuman/body_record/proc/internal_producebody_updatednastate(var/mob/living/carbon/human/H,var/is_synthfab)
SHOULD_NOT_OVERRIDE(TRUE)
PRIVATE_PROC(TRUE)
//Apply DNA from record
if(!mydna.dna) // This case should never happen, but copied from clone pod... Who knows with this codebase.
mydna.dna = new /datum/dna()
qdel_swap(H.dna, mydna.dna.Clone())
H.original_player = ckey
//Apply genetic modifiers, synths don't use these
if(!is_synthfab)
for(var/modifier_type in mydna.genetic_modifiers)
H.add_modifier(modifier_type)
//Update appearance, remake icons
H.UpdateAppearance()
H.sync_dna_traits(FALSE) // Traitgenes Sync traits to genetics if needed
H.sync_organ_dna()
H.regenerate_icons()
H.initialize_vessel()
/// Transfers VORE related information cached in the mob
/datum/transhuman/body_record/proc/internal_producebody_virgoOOC(var/mob/living/carbon/human/H)
SHOULD_NOT_OVERRIDE(TRUE)
PRIVATE_PROC(TRUE)
H.ooc_notes = body_oocnotes
H.ooc_notes_likes = body_ooclikes
H.ooc_notes_dislikes = body_oocdislikes
H.ooc_notes_favs = body_oocfavs
H.ooc_notes_maybes = body_oocmaybes
H.ooc_notes_style = body_oocstyle
/datum/transhuman/body_record/proc/internal_producebody_misc(var/mob/living/carbon/human/H)
SHOULD_NOT_OVERRIDE(TRUE)
PRIVATE_PROC(TRUE)
H.flavor_texts = mydna.flavor.Copy()
H.resize(sizemult, FALSE)
H.appearance_flags = aflags
H.weight = weight
if(speciesname)
H.custom_species = speciesname
/**
* Specialty revival procs. Uses the BR for data, but needs to handle some weird logic for xenochi/slimes
*/
/datum/transhuman/body_record/proc/revive_xenochimera(var/mob/living/carbon/human/H,var/heal_robot_limbs,var/from_save_slot)
// Boy this one is complex, but what do we expect when trying to heal damage and organ loss in this game!
if(!H || QDELETED(H)) // Someone, somewhere, will call this without any safety. I feel it in my bones cappin'
return
// Don't unlock unwilling xenochi!
internal_producebody_handlesleevelock(H,FALSE)
// Reset our organs/limbs.
H.species.create_organs(H)
internal_producebody_updatelimbandorgans(H, heal_robot_limbs)
//Don't boot out anyone already in the mob.
if(!H.client || !H.key)
for (var/obj/item/organ/internal/brain/CH in GLOB.all_brain_organs)
if(CH.brainmob)
if(CH.brainmob.real_name == H.real_name)
if(CH.brainmob.mind)
CH.brainmob.mind.transfer_to(H)
qdel(CH)
// Traitgenes Disable all traits currently active, before species.produceCopy() applies them during updatednastate(). Relevant here as genetraits may not match prior dna!
for(var/datum/gene/trait/gene in GLOB.dna_genes)
if(gene.name in H.active_genes)
gene.deactivate(H)
H.active_genes -= gene.name
internal_producebody_updatednastate(H,FALSE)
internal_producebody_virgoOOC(H) // Is this needed?
internal_producebody_misc(H)
// Begin actual REVIVIAL. Do NOT use revive(). That uses client prefs and allows save hacking.
H.revival_healing_action()
// Update record from vanity copy of slot if needed
if(from_save_slot)
H.client.prefs.vanity_copy_to(H,FALSE,TRUE,TRUE,FALSE)
for(var/category in H.all_underwear) // No undies
H.hide_underwear[category] = TRUE
H.update_underwear()
return H
/datum/transhuman/body_record/proc/revive_promethean(var/mob/living/carbon/human/H)
// TODO - See note in code\modules\organs\internal\brain.dm for slime brains
return

View File

@@ -29,86 +29,9 @@
remove_biomass(CLONE_BIOMASS)
//Get the DNA and generate a new mob
var/datum/dna2/record/R = current_project.mydna
var/mob/living/carbon/human/H = new /mob/living/carbon/human(src, R.dna.species)
if(current_project.locked)
H.resleeve_lock = current_project.ckey //CHOMPAdd, keep the lock
/*CHOMPRemove Start
if(current_project.ckey)
H.resleeve_lock = current_project.ckey
else
// Ensure even body scans without an attached ckey respect locking
H.resleeve_lock = "@badckey"
*///CHOMPRemove End
var/mob/living/carbon/human/H = current_project.produce_human_mob(src,FALSE,FALSE,"clone ([rand(0,999)])")
SEND_SIGNAL(H, COMSIG_HUMAN_DNA_FINALIZED)
//Fix the external organs
for(var/part in current_project.limb_data)
var/status = current_project.limb_data[part]
if(status == null) continue //Species doesn't have limb? Child of amputated limb?
var/obj/item/organ/external/O = H.organs_by_name[part]
if(!O) continue //Not an organ. Perhaps another amputation removed it already.
if(status == 1) //Normal limbs
continue
else if(status == 0) //Missing limbs
O.remove_rejuv()
else if(status) //Anything else is a manufacturer
O.remove_rejuv() //Don't robotize them, leave them removed so robotics can attach a part.
//Look, this machine can do this because [reasons] okay?!
for(var/part in current_project.organ_data)
var/status = current_project.organ_data[part]
if(status == null) continue //Species doesn't have organ? Child of missing part?
var/obj/item/organ/I = H.internal_organs_by_name[part]
if(!I) continue//Not an organ. Perhaps external conversion changed it already?
if(status == 0) //Normal organ
continue
else if(status == 1) //Assisted organ
I.mechassist()
else if(status == 2) //Mechanical organ
I.robotize()
else if(status == 3) //Digital organ
I.digitize()
occupant = H
//Set the name or generate one
if(!R.dna.real_name)
R.dna.real_name = "clone ([rand(0,999)])"
H.real_name = R.dna.real_name
//Apply DNA
qdel_swap(H.dna, R.dna.Clone())
H.original_player = current_project.ckey
//Apply genetic modifiers
for(var/modifier_type in R.genetic_modifiers)
H.add_modifier(modifier_type)
//Apply legs
H.digitigrade = R.dna.digitigrade // ensure clone mob has digitigrade var set appropriately
if(H.dna.digitigrade <> R.dna.digitigrade)
H.dna.digitigrade = R.dna.digitigrade // ensure cloned DNA is set appropriately from record??? for some reason it doesn't get set right despite the override to datum/dna/Clone()
//Apply damage
H.adjustCloneLoss((H.getMaxHealth() - (H.getMaxHealth()))*-0.75)
H.Paralyse(4)
H.updatehealth()
//Update appearance, remake icons
H.UpdateAppearance()
H.sync_dna_traits(FALSE) // Traitgenes Sync traits to genetics if needed
H.sync_organ_dna()
H.regenerate_icons()
H.initialize_vessel()
// Traitgenes Moved breathing equipment to AFTER the genes set it
//Give breathing equipment if needed
if(current_project.breath_type != null && current_project.breath_type != GAS_O2)
H.equip_to_slot_or_del(new /obj/item/clothing/mask/breath(H), slot_wear_mask)
@@ -124,25 +47,11 @@
if(istype(H.internal,/obj/item/tank) && H.internals)
H.internals.icon_state = "internal1"
//Basically all the VORE stuff
H.ooc_notes = current_project.body_oocnotes
H.ooc_notes_likes = current_project.body_ooclikes
H.ooc_notes_dislikes = current_project.body_oocdislikes
H.ooc_notes_favs = current_project.body_oocfavs
H.ooc_notes_maybes = current_project.body_oocmaybes
H.ooc_notes_style = current_project.body_oocstyle
H.flavor_texts = current_project.mydna.flavor.Copy()
H.resize(current_project.sizemult, FALSE)
H.appearance_flags = current_project.aflags
H.weight = current_project.weight
if(current_project.speciesname)
H.custom_species = current_project.speciesname
//Suiciding var
H.suiciding = 0
//Making double-sure this is not set
H.mind = null
//Apply damage
set_occupant(H)
H.adjustCloneLoss((H.getMaxHealth() - (H.getMaxHealth()))*-0.75)
H.Paralyse(4)
H.updatehealth()
//Machine specific stuff at the end
update_icon()
@@ -150,6 +59,7 @@
return 1
/obj/machinery/clonepod/transhuman/process()
var/mob/living/occupant = get_occupant()
if(stat & NOPOWER)
if(occupant)
locked = 0
@@ -190,7 +100,7 @@
return
else if((!occupant) || (occupant.loc != src))
occupant = null
set_occupant(null)
if(locked)
locked = 0
update_icon()
@@ -199,13 +109,14 @@
return
/obj/machinery/clonepod/transhuman/get_completion()
var/mob/living/occupant = get_occupant()
if(occupant)
return 100 * ((occupant.health + (occupant.getMaxHealth()))) / (occupant.getMaxHealth() + abs(occupant.getMaxHealth()))
return 0
/obj/machinery/clonepod/transhuman/examine(mob/user, infix, suffix)
. = ..()
if(occupant)
if(get_occupant())
var/completion = get_completion()
. += "Progress: [round(completion)]% [chat_progress_bar(round(completion), TRUE)]"
@@ -298,103 +209,16 @@
return
//Get the DNA and generate a new mob
var/datum/dna2/record/R = current_project.mydna
var/mob/living/carbon/human/H = new /mob/living/carbon/human(src, R.dna.species)
if(current_project.locked)
H.resleeve_lock = current_project.ckey //CHOMPAdd, keep the lock
/*CHOMPRemove Start
if(current_project.ckey)
H.resleeve_lock = current_project.ckey
else
// Ensure even body scans without an attached ckey respect locking
H.resleeve_lock = "@badckey"
*///CHOMPRemove End
//Fix the external organs
for(var/part in current_project.limb_data)
var/status = current_project.limb_data[part]
if(status == null) continue //Species doesn't have limb? Child of amputated limb?
var/obj/item/organ/external/O = H.organs_by_name[part]
if(!O) continue //Not an organ. Perhaps another amputation removed it already.
if(status == 1) //Normal limbs
continue
else if(status == 0) //Missing limbs
O.remove_rejuv()
else if(status) //Anything else is a manufacturer
O.robotize(status)
//Then the internal organs
for(var/part in current_project.organ_data)
var/status = current_project.organ_data[part]
if(status == null) continue //Species doesn't have organ? Child of missing part?
var/obj/item/organ/I = H.internal_organs_by_name[part]
if(!I) continue//Not an organ. Perhaps external conversion changed it already?
if(status == 0) //Normal organ
continue
else if(status == 1) //Assisted organ
I.mechassist()
else if(status == 2) //Mechanical organ
I.robotize()
else if(status == 3) //Digital organ
I.digitize()
//Set the name or generate one
if(!R.dna.real_name)
R.dna.real_name = "synth ([rand(0,999)])"
H.real_name = R.dna.real_name
//Apply DNA
qdel_swap(H.dna, R.dna.Clone())
H.original_player = current_project.ckey
//Apply legs
H.digitigrade = R.dna.digitigrade // ensure clone mob has digitigrade var set appropriately
if(H.dna.digitigrade <> R.dna.digitigrade)
H.dna.digitigrade = R.dna.digitigrade // ensure cloned DNA is set appropriately from record??? for some reason it doesn't get set right despite the override to datum/dna/Clone()
var/mob/living/carbon/human/H = current_project.produce_human_mob(src,TRUE,FALSE,"synth ([rand(0,999)])")
SEND_SIGNAL(H, COMSIG_HUMAN_DNA_FINALIZED)
//Apply damage
H.adjustBruteLoss(brute_value)
H.adjustFireLoss(burn_value)
H.updatehealth()
//Update appearance, remake icons
H.UpdateAppearance()
H.sync_dna_traits(FALSE) // Traitgenes Sync traits to genetics if needed
H.sync_organ_dna()
H.regenerate_icons()
H.initialize_vessel()
//Basically all the VORE stuff
H.ooc_notes = current_project.body_oocnotes
H.ooc_notes_likes = current_project.body_ooclikes
H.ooc_notes_dislikes = current_project.body_oocdislikes
//CHOMPEdit Start
H.ooc_notes_favs = current_project.body_oocfavs
H.ooc_notes_maybes = current_project.body_oocmaybes
H.ooc_notes_style = current_project.body_oocstyle
//CHOMPEdit End
H.flavor_texts = current_project.mydna.flavor.Copy()
H.resize(current_project.sizemult)
H.appearance_flags = current_project.aflags
H.weight = current_project.weight
if(current_project.speciesname)
H.custom_species = current_project.speciesname
//Suiciding var
H.suiciding = 0
//Making double-sure this is not set
H.mind = null
//Plonk them here.
H.regenerate_icons()
H.loc = get_turf(src)
H.forceMove(get_turf(src))
//Machine specific stuff at the end
stored_material[MAT_STEEL] -= body_cost
@@ -470,7 +294,7 @@
var/blur_amount
var/confuse_amount
var/mob/living/carbon/human/occupant = null
VAR_PRIVATE/datum/weakref/weakref_occupant = null
var/connected = null
var/sleevecards = 2
@@ -487,6 +311,18 @@
RefreshParts()
update_icon()
/obj/machinery/transhuman/resleever/proc/set_occupant(var/mob/living/carbon/human/H)
SHOULD_NOT_OVERRIDE(TRUE)
if(!H)
weakref_occupant = null
return
weakref_occupant = WEAKREF(H)
/obj/machinery/transhuman/resleever/proc/get_occupant()
RETURN_TYPE(/mob/living/carbon/human)
SHOULD_NOT_OVERRIDE(TRUE)
return weakref_occupant?.resolve()
/obj/machinery/transhuman/resleever/RefreshParts()
var/scan_rating = 0
for(var/obj/item/stock_parts/scanning_module/SM in component_parts)
@@ -513,14 +349,15 @@
/obj/machinery/transhuman/resleever/tgui_data(mob/user)
var/list/data = list()
data["occupied"] = !!occupant
if(occupant)
data["name"] = occupant.name
data["health"] = occupant.health
data["maxHealth"] = occupant.getMaxHealth()
data["stat"] = occupant.stat
data["mindStatus"] = !!occupant.mind
data["mindName"] = occupant.mind?.name
var/mob/living/carbon/human/H = get_occupant()
data["occupied"] = !!H
if(H)
data["name"] = H.name
data["health"] = H.health
data["maxHealth"] = H.getMaxHealth()
data["stat"] = H.stat
data["mindStatus"] = !!H.mind
data["mindName"] = H.mind?.name
return data
/obj/machinery/transhuman/resleever/attackby(obj/item/W, mob/user)
@@ -583,6 +420,7 @@
add_fingerprint(user)
/obj/machinery/transhuman/resleever/proc/putmind(var/datum/transhuman/mind_record/MR, mode = 1, var/mob/living/carbon/human/override = null, var/db_key)
var/mob/living/carbon/human/occupant = get_occupant()
if((!occupant || !istype(occupant) || occupant.stat >= DEAD) && mode == 1)
return 0
@@ -665,13 +503,14 @@
return 1
/obj/machinery/transhuman/resleever/proc/go_out(var/mob/M)
if(!( src.occupant ))
var/mob/living/carbon/human/occupant = get_occupant()
if(!occupant)
return
if (src.occupant.client)
src.occupant.client.eye = src.occupant.client.mob
src.occupant.client.perspective = MOB_PERSPECTIVE
src.occupant.loc = src.loc
src.occupant = null
if (occupant.client)
occupant.client.eye = occupant.client.mob
occupant.client.perspective = MOB_PERSPECTIVE
occupant.forceMove(get_turf(src))
set_occupant(null)
icon_state = "implantchair"
return
@@ -679,7 +518,7 @@
if(!ishuman(M))
to_chat(usr, span_warning("\The [src] cannot hold this!"))
return
if(src.occupant)
if(get_occupant())
to_chat(usr, span_warning("\The [src] is already occupied!"))
return
if(M.client)
@@ -687,7 +526,7 @@
M.client.eye = src
M.stop_pulling()
M.loc = src
src.occupant = M
set_occupant(M)
src.add_fingerprint(usr)
icon_state = "implantchair_on"
return 1

View File

@@ -44,6 +44,7 @@
var/list/valid_wingstyles = list()
var/list/valid_gradstyles = list()
var/list/markings = null
var/cooldown //Anti-spam. If spammed, this can be REALLY laggy.
/datum/tgui_module/appearance_changer/New(
host,
@@ -60,7 +61,7 @@
cam_screen.name = "screen"
cam_screen.assigned_map = map_name
cam_screen.del_on_map_removal = FALSE
cam_screen.screen_loc = "[map_name]:1,1"
cam_screen.screen_loc = "[map_name]:3:-32,3:-48"
cam_plane_masters = get_tgui_plane_masters()
@@ -83,11 +84,19 @@
whitelist = species_whitelist
blacklist = species_blacklist
/datum/tgui_module/appearance_changer/proc/jiggle_map()
// Fix for weird byond bug, jiggles the map around a little
sleep(0.1 SECONDS)
cam_screen.screen_loc = "[map_name]:1,1"
sleep(0.1 SECONDS)
cam_screen.screen_loc = "[map_name]:3:-32,3:-48" // Align for larger icons and scales
/datum/tgui_module/appearance_changer/tgui_close(mob/user)
. = ..()
if(owner == user || !customize_usr)
close_ui()
UnregisterSignal(owner, COMSIG_OBSERVER_MOVED)
SEND_SIGNAL(owner, COMSIG_HUMAN_DNA_FINALIZED) // Update any components using our saved appearance
owner = null
last_camera_turf = null
cut_data()
@@ -101,6 +110,11 @@
/datum/tgui_module/appearance_changer/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state)
if(..())
return TRUE
if(cooldown > world.time)
to_chat(ui.user, span_warning("You are changing appearance too fast!"))
return FALSE
else
cooldown = world.time + 0.5 SECONDS
var/obj/machinery/computer/transhuman/designer/DC = null
var/datum/tgui_module/appearance_changer/body_designer/BD = null
@@ -435,9 +449,8 @@
if (owner.change_marking_color(mark_datum, marking_color))
return TRUE
if("rotate_view")
if(can_change(owner, APPEARANCE_RACE))
owner.set_dir(turn(owner.dir, 90))
return TRUE
owner.set_dir(turn(owner.dir, 90))
return TRUE
if("rename")
if(owner)
var/raw_name = tgui_input_text(ui.user, "Choose the a name:", "Sleeve Name")
@@ -465,35 +478,30 @@
owner.custom_species = new_name
return TRUE
if("base_icon")
if(owner.species.selects_bodytype == SELECTS_BODYTYPE_FALSE)
var/datum/species/S = GLOB.all_species[owner.species.name]
owner.species.base_species = S.base_species // Return to original form
generate_data(ui.user, owner)
changed_hook(APPEARANCECHANGER_CHANGED_RACE)
return TRUE
var/list/choices
var/datum/species/S = GLOB.all_species[owner.species.name]
if(S.selects_bodytype == SELECTS_BODYTYPE_SHAPESHIFTER)
choices = S.get_valid_shapeshifter_forms()
else if(S.selects_bodytype == SELECTS_BODYTYPE_CUSTOM)
choices = GLOB.custom_species_bases
var/new_species = tgui_input_list(ui.user, "Please select basic shape.", "Body Shape", choices)
if(new_species && can_change(owner, APPEARANCE_RACE))
owner.species.base_species = new_species
owner.regenerate_icons()
generate_data(ui.user, owner)
changed_hook(APPEARANCECHANGER_CHANGED_RACE)
return TRUE
if("blood_reagent")
var/new_blood_reagents = tgui_input_list(ui.user, "Please select blood restoration reagent:", "Character Preference", valid_bloodreagents)
if(new_blood_reagents && can_change(owner, APPEARANCE_RACE))
owner.dna.blood_reagents = new_blood_reagents
changed_hook(APPEARANCECHANGER_CHANGED_RACE)
return TRUE
if(can_change(owner, APPEARANCE_MISC))
var/new_species = tgui_input_list(ui.user, "Please select basic shape.", "Body Shape", GLOB.custom_species_bases)
if(new_species)
owner.species.base_species = new_species
owner.species.icobase = owner.species.get_icobase()
owner.species.deform = owner.species.get_icobase(get_deform = TRUE)
owner.species.vanity_base_fit = new_species
if(istype(owner.species, /datum/species/shapeshifter)) //TODO: See if this is still needed.
wrapped_species_by_ref["\ref[owner]"] = new_species
owner.regenerate_icons()
generate_data(ui.user, owner)
changed_hook(APPEARANCECHANGER_CHANGED_RACE)
return TRUE
if("blood_reagent") //you know, this feels REALLY odd to be able to change at will but WHATEVER, WE BALL.
if(can_change(owner, APPEARANCE_MISC))
var/new_blood_reagents = tgui_input_list(ui.user, "Please select blood restoration reagent:", "Character Preference", valid_bloodreagents)
if(new_blood_reagents)
owner.dna.blood_reagents = new_blood_reagents
changed_hook(APPEARANCECHANGER_CHANGED_RACE)
return TRUE
if("blood_color")
var/current = owner.species.blood_color ? owner.species.blood_color : "#A10808"
var/blood_col = tgui_color_picker(ui.user, "Please select marking color", "Marking color", current)
if(blood_col && can_change(owner, APPEARANCE_RACE))
var/blood_col = tgui_color_picker(ui.user, "Please select blood color", "Blood color", current)
if(blood_col && can_change(owner, APPEARANCE_MISC))
owner.dna.blood_color = blood_col
changed_hook(APPEARANCECHANGER_CHANGED_RACE)
return TRUE
@@ -501,9 +509,9 @@
var/new_weight = tgui_input_number(ui.user, "Choose tbe character's relative body weight.\n\
This measurement should be set relative to a normal 5'10'' person's body and not the actual size of the character.\n\
([WEIGHT_MIN]-[WEIGHT_MAX])", "Character Preference", null, WEIGHT_MAX, WEIGHT_MIN, round_value=FALSE)
if(new_weight && can_change(owner, APPEARANCE_RACE))
if(new_weight && can_change(owner, APPEARANCE_MISC))
var/unit_of_measurement = tgui_alert(ui.user, "Is that number in pounds (lb) or kilograms (kg)?", "Confirmation", list("Pounds", "Kilograms"))
if(unit_of_measurement && can_change(owner, APPEARANCE_RACE))
if(unit_of_measurement)
if(unit_of_measurement == "Pounds")
new_weight = round(text2num(new_weight),4)
if(unit_of_measurement == "Kilograms")
@@ -513,7 +521,7 @@
return TRUE
if("size_scale")
var/new_size = tgui_input_number(ui.user, "Choose size, ranging from [RESIZE_MINIMUM * 100]% to [RESIZE_MAXIMUM * 100]%", "Set Size", null, RESIZE_MAXIMUM * 100, RESIZE_MINIMUM * 100)
if(new_size && ISINRANGE(new_size,RESIZE_MINIMUM * 100,RESIZE_MAXIMUM * 100) && can_change(owner, APPEARANCE_RACE))
if(new_size && ISINRANGE(new_size,RESIZE_MINIMUM * 100,RESIZE_MAXIMUM * 100) && can_change(owner, APPEARANCE_MISC))
owner.size_multiplier = new_size / 100
owner.update_transform(TRUE)
owner.regenerate_icons()
@@ -521,21 +529,21 @@
changed_hook(APPEARANCECHANGER_CHANGED_RACE)
return TRUE
if("scale_appearance")
if(can_change(owner, APPEARANCE_RACE))
if(can_change(owner, APPEARANCE_MISC))
owner.dna.scale_appearance = !owner.dna.scale_appearance
owner.fuzzy = owner.dna.scale_appearance
owner.regenerate_icons()
owner.set_dir(owner.dir) // Causes a visual update for fuzzy/offset
return TRUE
if("offset_override")
if(can_change(owner, APPEARANCE_RACE))
if(can_change(owner, APPEARANCE_MISC))
owner.dna.offset_override = !owner.dna.offset_override
owner.offset_override = owner.dna.offset_override
owner.regenerate_icons()
owner.set_dir(owner.dir) // Causes a visual update for fuzzy/offset
return TRUE
if("digitigrade")
if(can_change(owner, APPEARANCE_RACE))
if(can_change(owner, APPEARANCE_MISC))
owner.dna.digitigrade = !owner.dna.digitigrade
owner.digitigrade = owner.dna.digitigrade
owner.regenerate_icons()
@@ -545,28 +553,37 @@
if("species_sound")
var/list/possible_species_sound_types = species_sound_map
var/choice = tgui_input_list(ui.user, "Which set of sounds would you like to use? (Cough, Sneeze, Scream, Pain, Gasp, Death)", "Species Sounds", possible_species_sound_types)
if(choice && can_change(owner, APPEARANCE_RACE))
if(choice && can_change(owner, APPEARANCE_MISC))
owner.species.species_sounds = choice
return TRUE
if("flavor_text")
var/select_key = params["target"]
if(select_key && can_change(owner, APPEARANCE_RACE))
if(select_key && can_change(owner, APPEARANCE_MISC))
if(select_key in owner.flavor_texts)
switch(select_key)
if("general")
var/msg = strip_html_simple(tgui_input_text(ui.user,"Give a general description of the character. This will be shown regardless of clothings. Put in \"!clear\" to make blank.","Flavor Text",html_decode(owner.flavor_texts[select_key]), multiline = TRUE, prevent_enter = TRUE))
if(can_change(owner, APPEARANCE_RACE)) // allows empty to wipe flavor
if(can_change(owner, APPEARANCE_MISC)) // allows empty to wipe flavor
if(msg == "!clear")
msg = ""
owner.flavor_texts[select_key] = msg
return TRUE
else
var/msg = strip_html_simple(tgui_input_text(ui.user,"Set the flavor text for their [select_key]. Put in \"!clear\" to make blank.","Flavor Text",html_decode(owner.flavor_texts[select_key]), multiline = TRUE, prevent_enter = TRUE))
if(can_change(owner, APPEARANCE_RACE)) // allows empty to wipe flavor
if(can_change(owner, APPEARANCE_MISC)) // allows empty to wipe flavor
if(msg == "!clear")
msg = ""
owner.flavor_texts[select_key] = msg
return TRUE
if("load_saveslot") //saveslot_load
if(can_change(owner, APPEARANCE_ALL_COSMETIC))
if(tgui_alert(owner, "Are you certain you wish to load the currently selected savefile?", "Load Savefile", list("No","Yes")) == "Yes")
if(owner && owner.client) //sanity
owner.client.prefs.vanity_copy_to(owner, FALSE, TRUE, FALSE, FALSE)
return TRUE
return TRUE
else
return TRUE
// ***********************************
// Body designer UI
// ***********************************
@@ -618,6 +635,7 @@
if(DC.disk.stored)
qdel_null(DC.disk.stored)
to_chat(ui.user,span_notice("\The [owner]'s bodyrecord was saved to the disk."))
owner.update_dna()
DC.disk.stored = new /datum/transhuman/body_record(owner, FALSE, FALSE) // Saves a COPY!
DC.disk.stored.locked = FALSE // remove lock
DC.disk.name = "[initial(DC.disk.name)] ([owner.real_name])"
@@ -660,6 +678,7 @@
// Open UI
ui = new(user, src, tgui_id, name)
ui.open()
CallAsync(src, PROC_REF(jiggle_map))
if(custom_state)
ui.set_state(custom_state)
update_active_camera_screen()
@@ -747,6 +766,7 @@
stock_bodyrecords_list_ui += N
data["stock_records"] = stock_bodyrecords_list_ui
data["change_race"] = can_change(owner, APPEARANCE_RACE)
data["change_misc"] = can_change(owner, APPEARANCE_MISC)
data["gender_id"] = can_change(owner, APPEARANCE_GENDER)
data["change_gender"] = can_change(owner, APPEARANCE_GENDER)
data["change_hair"] = can_change(owner, APPEARANCE_HAIR)
@@ -790,6 +810,8 @@
data["gender"] = owner.gender
data["gender_id"] = owner.identifying_gender //This is saved to your MIND.
data["change_race"] = can_change(owner, APPEARANCE_RACE)
data["saveslot_load"] = can_change(owner, APPEARANCE_ALL_COSMETIC)
data["change_misc"] = can_change(owner, APPEARANCE_MISC)
data["change_gender"] = can_change(owner, APPEARANCE_GENDER)
if(data["change_gender"])
@@ -873,7 +895,7 @@
local_skybox.cut_overlays()
/datum/tgui_module/appearance_changer/proc/update_dna(mob/living/carbon/human/target)
if(target && (flags & APPEARANCE_UPDATE_DNA))
if(target)
target.update_dna()
/datum/tgui_module/appearance_changer/proc/can_change(mob/living/carbon/human/target, var/flag)
@@ -1038,7 +1060,7 @@
// *******************************************************
/datum/tgui_module/appearance_changer/cocoon
name ="Appearance Editor (Cocoon)"
flags = APPEARANCE_ALL_HAIR | APPEARANCE_EYE_COLOR | APPEARANCE_SKIN
flags = APPEARANCE_ALL_COSMETIC
customize_usr = TRUE
/datum/tgui_module/appearance_changer/cocoon/tgui_status(mob/user, datum/tgui_state/state)
@@ -1051,7 +1073,7 @@
// *******************************************************
/datum/tgui_module/appearance_changer/superpower
name ="Appearance Editor (Superpower)"
flags = APPEARANCE_ALL_HAIR | APPEARANCE_EYE_COLOR | APPEARANCE_SKIN
flags = APPEARANCE_ALL_COSMETIC
customize_usr = TRUE
/datum/tgui_module/appearance_changer/superpower/tgui_status(mob/user, datum/tgui_state/state)
@@ -1060,6 +1082,19 @@
return STATUS_CLOSE
return ..()
// *******************************************************
// Innate Species Transformation.
// *******************************************************
/datum/tgui_module/appearance_changer/innate
name ="Appearance Editor (Innate)"
flags = APPEARANCE_ALL_COSMETIC
customize_usr = TRUE
/datum/tgui_module/appearance_changer/innate/tgui_status(mob/user, datum/tgui_state/state)
if(owner.stat != CONSCIOUS)
return STATUS_CLOSE
return ..()
// *******************************************************
// Body design console
// *******************************************************
@@ -1097,52 +1132,10 @@
if(owner)
UnregisterSignal(owner, COMSIG_OBSERVER_MOVED)
qdel_null(owner)
//Get the DNA and generate a new mob
var/datum/dna2/record/R = current_project.mydna
owner = new /mob/living/carbon/human(src, R.dna.species)
//Fix the external organs
for(var/part in current_project.limb_data)
var/status = current_project.limb_data[part]
if(status == null) continue //Species doesn't have limb? Child of amputated limb?
var/obj/item/organ/external/O = owner.organs_by_name[part]
if(!O) continue //Not an organ. Perhaps another amputation removed it already.
if(status == 1) //Normal limbs
continue
else if(status == 0) //Missing limbs
O.remove_rejuv()
else if(status) //Anything else is a manufacturer
O.remove_rejuv() //Don't robotize them, leave them removed so robotics can attach a part.
for(var/part in current_project.organ_data)
var/status = current_project.organ_data[part]
if(status == null) continue //Species doesn't have organ? Child of missing part?
var/obj/item/organ/I = owner.internal_organs_by_name[part]
if(!I) continue//Not an organ. Perhaps external conversion changed it already?
if(status == 0) //Normal organ
continue
else if(status == 1) //Assisted organ
I.mechassist()
else if(status == 2) //Mechanical organ
I.robotize()
else if(status == 3) //Digital organ
I.digitize()
//Set the name or generate one
owner.real_name = R.dna.real_name
owner.name = owner.real_name
//Apply DNA
owner.dna = R.dna.Clone()
owner.original_player = current_project.ckey
//Apply legs
owner.digitigrade = R.dna.digitigrade // ensure clone mob has digitigrade var set appropriately
if(owner.dna.digitigrade <> R.dna.digitigrade)
owner.dna.digitigrade = R.dna.digitigrade // ensure cloned DNA is set appropriately from record??? for some reason it doesn't get set right despite the override to datum/dna/Clone()
//Update appearance, remake icons
owner.UpdateAppearance()
owner.sync_dna_traits(FALSE)
owner.sync_organ_dna()
owner.initialize_vessel()
owner.dna.blood_reagents = R.dna.blood_reagents
owner.dna.blood_color = R.dna.blood_color
owner.regenerate_icons()
owner = current_project.produce_human_mob(src,FALSE,FALSE,"Designer [rand(999)]")
// Update some specifics from the current record
owner.dna.blood_reagents = current_project.mydna.dna.blood_reagents
owner.dna.blood_color = current_project.mydna.dna.blood_color
owner.flavor_texts = current_project.mydna.flavor.Copy()
owner.resize(current_project.sizemult, FALSE)
owner.appearance_flags = current_project.aflags

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]]
if(keylang)
new_character.language_keys[key] = keylang
// VOREStation Add: Preferred Language Setting;
if(prey.prefs.preferred_language) // Do we have a preferred language?
var/datum/language/def_lang = GLOB.all_languages[prey.prefs.preferred_language]
if(def_lang)
new_character.default_language = def_lang
// VOREStation Add End
SEND_SIGNAL(new_character, COMSIG_HUMAN_DNA_FINALIZED)
new_character.regenerate_icons()

View File

@@ -1,5 +1,5 @@
import { useBackend } from 'tgui/backend';
import { Button, ColorBox, LabeledList, Section } from 'tgui-core/components';
import { Button, LabeledList, Section } from 'tgui-core/components';
import type { Data, species } from './types';
@@ -35,65 +35,8 @@ export const AppearanceChangerSpecies = (props) => {
) : (
''
)}
<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>
</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>
);
};

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_WINGS = 9;
export const TAB_MARKINGS = 10;
export const TAB_MISC = 11;
export const MARKINGS_PER_PAGE = 30;

View File

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

View File

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

View File

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